Merge Android 24Q1 Release (ab/11220357)

Bug: 319669529
Merged-In: If3540f2e20a31c43516adaab8851f2ad215208f8
Change-Id: Ida0d65fce858e031a7d60750262be5fad616aebc
diff --git a/.clang-format b/.clang-format
index 2fb833a..30ed2de 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,2 +1,12 @@
+# Defines the Chromium style for automatic reformatting.
 # http://clang.llvm.org/docs/ClangFormatStyleOptions.html
 BasedOnStyle: Chromium
+# This defaults to 'Auto'. Explicitly set it for a while, so that
+# 'vector<vector<int> >' in existing files gets formatted to
+# 'vector<vector<int>>'. ('Auto' means that clang-format will only use
+# 'int>>' if the file already contains at least one such instance.)
+Standard: Cpp11
+
+# TODO(crbug.com/1392808): Remove when InsertBraces has been upstreamed into
+# the Chromium style (is implied by BasedOnStyle: Chromium).
+InsertBraces: true
diff --git a/.gitattributes b/.gitattributes
index c772c32..953c317 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -3,6 +3,7 @@
 *.c text eol=lf
 *.cc text eol=lf
 *.cpp text eol=lf
+*.evt text eol=lf
 *.gn text eol=lf
 *.gni text eol=lf
 *.h text eol=lf
diff --git a/.gn b/.gn
index c974357..e39d6f7 100644
--- a/.gn
+++ b/.gn
@@ -1,35 +1,36 @@
-# Copyright 2016 PDFium Authors. All rights reserved.
+# Copyright 2016 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-buildconfig = "//build/config/BUILDCONFIG.gn"
+# TODO(crbug.com/pdfium/1932): Switch back to //build/config/BUILDCONFIG.gn if
+# appropriate.
+buildconfig = "//build_overrides/BUILDCONFIG.gn"
+
+# The python interpreter to use by default. On Windows, this will look
+# for python3.exe and python3.bat.
+script_executable = "python3"
 
 default_args = {
-  v8_extra_library_files = []
-  v8_experimental_extra_library_files = []
+  # Required for the Fuchsia SDK
+  fuchsia_target_api_level = 11
+
+  # PDFs only need to run JavaScript.
+  v8_enable_webassembly = false
 
   # Turns on compiler optimizations in V8 in Debug build.
   v8_optimized_debug = true
+
+  # PDFium is currently incompatible with the V8 Sandbox.
+  # See https://crbug.com/v8/13014 for details.
+  v8_enable_sandbox = false
 }
 
-check_targets = [
-  ":pdfium",
-  ":pdfium_embeddertests",
-  ":pdfium_unittests",
-  "//constants/*",
-  "//core/*",
-  "//fpdfsdk/*",
-  "//fxbarcode/*",
-  "//fxjs/*",
-  "//samples/*",
-  "//skia/*",
-  "//testing/:*",
-  "//testing/fuzzers/*",
-  "//testing/image_diff/*",
-  "//third_party:bigint",
-  "//third_party:fx_agg",
-  "//third_party:fx_freetype",
-  "//third_party:pdfium_base",
-  "//third_party:skia_shared",
-  "//xfa/*",
+no_check_targets = [
+  # See https://crbug.com/v8/7330 and/or check if these entries exist in
+  # Chromium's //.gn file.
+  "//v8:cppgc_base",
+  "//v8:v8_internal_headers",
+  "//v8/src/inspector:inspector",
+  "//v8/test/cctest:cctest_sources",
+  "//v8/test/unittests:inspector_unittests_sources",
 ]
diff --git a/.style.yapf b/.style.yapf
new file mode 100644
index 0000000..fdd0723
--- /dev/null
+++ b/.style.yapf
@@ -0,0 +1,2 @@
+[style]
+based_on_style = yapf
diff --git a/.vpython b/.vpython
deleted file mode 100644
index efe51d4..0000000
--- a/.vpython
+++ /dev/null
@@ -1,38 +0,0 @@
-# This is a vpython "spec" file.
-#
-# It describes patterns for python wheel dependencies of the python scripts in
-# the chromium repo, particularly for dependencies that have compiled components
-# (since pure-python dependencies can be easily vendored into third_party).
-#
-# When vpython is invoked, it finds this file and builds a python VirtualEnv,
-# containing all of the dependencies described in this file, fetching them from
-# CIPD (the "Chrome Infrastructure Package Deployer" service). Unlike `pip`,
-# this never requires the end-user machine to have a working python extension
-# compilation environment. All of these packages are built using:
-#   https://chromium.googlesource.com/infra/infra/+/master/infra/tools/dockerbuild/
-#
-# All python scripts in the repo share this same spec, to avoid dependency
-# fragmentation.
-#
-# If you have depot_tools installed in your $PATH, you can invoke python scripts
-# in this repo by running them as you normally would run them, except
-# substituting `vpython` instead of `python` on the command line, e.g.:
-#   vpython path/to/script.py some --arguments
-#
-# Read more about `vpython` and how to modify this file here:
-#   https://chromium.googlesource.com/infra/infra/+/master/doc/users/vpython.md
-
-python_version: "2.7"
-
-# Used by build/toolchain/win/tool_wrapper.py
-wheel: <
-  name: "infra/python/wheels/pypiwin32/${vpython_platform}"
-  version: "version:219"
-  match_tag: <
-    platform: "win32"
-  >
-  match_tag: <
-    platform: "win_amd64"
-  >
->
-
diff --git a/.vpython3 b/.vpython3
new file mode 100644
index 0000000..52840c6
--- /dev/null
+++ b/.vpython3
@@ -0,0 +1,54 @@
+# This is a vpython "spec" file.
+#
+# It describes patterns for python wheel dependencies of the python scripts in
+# the chromium repo, particularly for dependencies that have compiled components
+# (since pure-python dependencies can be easily vendored into third_party).
+#
+# When vpython is invoked, it finds this file and builds a python VirtualEnv,
+# containing all of the dependencies described in this file, fetching them from
+# CIPD (the "Chrome Infrastructure Package Deployer" service). Unlike `pip`,
+# this never requires the end-user machine to have a working python extension
+# compilation environment. All of these packages are built using:
+#   https://chromium.googlesource.com/infra/infra/+/main/infra/tools/dockerbuild/
+#
+# All python scripts in the repo share this same spec, to avoid dependency
+# fragmentation.
+#
+# If you have depot_tools installed in your $PATH, you can invoke python scripts
+# in this repo by running them as you normally would run them, except
+# substituting `vpython` instead of `python` on the command line, e.g.:
+#   vpython path/to/script.py some --arguments
+#
+# Read more about `vpython` and how to modify this file here:
+#   https://chromium.googlesource.com/infra/infra/+/main/doc/users/vpython.md
+
+python_version: "3.8"
+
+# Used by build/skia_gold_common/output_managerless_skia_gold_session.py
+# Used by tools/code_coverage/coverage.py
+wheel: <
+  name: "infra/python/wheels/six-py2_py3"
+  version: "version:1.15.0"
+>
+
+# Used by build/util/lib/results/result_sink.py
+wheel: <
+  name: "infra/python/wheels/certifi-py2_py3"
+  version: "version:2021.5.30"
+>
+wheel: <
+  name: "infra/python/wheels/charset_normalizer-py3"
+  version: "version:2.0.4"
+>
+wheel: <
+  name: "infra/python/wheels/idna-py2_py3"
+  version: "version:2.10"
+>
+wheel: <
+  name: "infra/python/wheels/requests-py3"
+  version: "version:2.31.0"
+>
+wheel: <
+  name: "infra/python/wheels/urllib3-py2_py3"
+  version: "version:1.26.6"
+>
diff --git a/AUTHORS b/AUTHORS
index 3c97237..5b98287 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -7,53 +7,49 @@
 #   Organization <fnmatch pattern>
 #
 # See python fnmatch module documentation for more information.
+#
+# Please keep the list sorted.
 
-Andrey Khalyavin <halyavin@chromium.org>
+# BEGIN individuals section.
+Abdelkarim Sellamna <abdelkarim.se@gmail.com>
+Aleksei Skotnikov <fineaskotnikov@gmail.com>
 Antonio Gomes <tonikitoo@igalia.com>
-Brett Wilson <brettw@chromium.org>
-Bruce Dawson <brucedawson@chromium.org>
 Chery Cherian <cherycherian@gmail.com>
 Claudio DeSouza <claudiomdsjr@gmail.com>
-Chris Palmer <palmer@chromium.org>
-Dan Sinclair <dsinclair@chromium.org>
+Dan Ilan <danilan@gmail.com>
+Dorian Rudolph <dorianrudo97@gmail.com>
 Felix Kauselmann <licorn@gmail.com>
-Finnur Thorarinsson <finnur@chromium.org>
 GiWan Go <gogil@stealien.com>
-Henrique Nakashima <hnakashima@chromium.org>
 Huy Ngo <huyna89@gmail.com>
 Jiang Jiang <jiangj@opera.com>
-Jochen Eisinger <jochen@chromium.org>
-John Abd-El-Malek <jam@chromium.org>
-Julien Tinnes <jln@chromium.org>
+Justin Pierce <brkfstmnchr@gmail.com>
 Ke Liu <stackexploit@gmail.com>
-Kostya Serebryany <kcc@chromium.org>
-Lei Zhang <thestig@chromium.org>
-Lucas Nihlen <luken@chromium.org>
 Luật Nguyễn <manhluat93.php@gmail.com>
-Matt Giuca <mgiuca@chromium.org>
+Manuel Geißer <geisserml@gmail.com>
 Michael Doppler <m.doppler@gmail.com>
 Miklos Vajna <vmiklos@vmiklos.hu>
 Minh Trần <myoki.crystal@gmail.com>
-Nico Weber <thakis@chromium.org>
-Nicolás Peña <npm@chromium.org>
-Peter Kasting <pkasting@chromium.org>
+Peter Varga <pvarga@inf.u-szeged.hu>
 Ralf Sippl <ralf.sippl@gmail.com>
-Raymes Khoury <raymes@chromium.org>
-Reid Kleckner <rnk@chromium.org>
-Ryan Harrison <rharrison@chromium.org>
+Robert Collyer <rcollyer99@gmail.com>
 Ryan Wiley <wileyrr@gmail.com>
-Robert Sesek <rsesek@chromium.org>
-Sam Clegg <sbc@chromium.org>
-Thomas Sepez <tsepez@chromium.org>
+Stephan Hartmann <stha09@googlemail.com>
+Tibor Dusnoki <tdusnoki@inf.u-szeged.hu>
 Wang Qing <wangqing-hf@loongson.cn>
 Zhuo Qingliang <zhuo.dev@gmail.com>
+# END individuals section.
 
-Collabora Ltd. <*@collabora.co.uk>
+# BEGIN organizations section.
+Ada Logics Ltd. <*@adalogics.com>
+Collabora Ltd. <*@collabora.com>
 DocsCorp Pty Ltd. <*@docscorp.com>
 Dropbox <*@dropbox.com>
 Foxit Software Inc <*@foxitsoftware.com>
 Google Inc. <*@google.com>
+Igalia S.L. <*@igalia.com>
 LG Electronics, Inc. <*@lge.com>
 Loongson Technology Corporation Limited. <*@loongson.cn>
 Microsoft <*@microsoft.com>
 PSPDFKit GmbH <*@pspdfkit.com>
+The Chromium Authors <*@chromium.org>
+# END organizations section.
diff --git a/Android.bp b/Android.bp
index 4af2504..85f449b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -109,10 +109,6 @@
     name: "libpdfium",
     defaults: ["pdfium-core"],
 
-    header_libs: [
-        "libpdfium-constants",
-    ],
-
     whole_static_libs: [
         "libpdfium-fpdfsdk",
     ],
@@ -122,6 +118,7 @@
     static_libs: [
         "libpdfium-agg",
         "libpdfium-cmaps",
+        "libpdfium-constants",
         "libpdfium-edit",
         "libpdfium-fdrm",
         "libpdfium-font",
diff --git a/BUILD.gn b/BUILD.gn
index 62f09e5..07a5b93 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1,10 +1,20 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# Copyright 2016 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/clang/clang.gni")
+import("//build/config/gclient_args.gni")
 import("//testing/test.gni")
 import("pdfium.gni")
 
+group("default") {
+  testonly = true
+  deps = [ ":pdfium" ]
+  if (pdf_is_standalone) {
+    deps += [ ":pdfium_all" ]
+  }
+}
+
 group("freetype_common") {
   public_deps = []
   if (pdf_bundle_freetype) {
@@ -16,12 +26,10 @@
 
 config("pdfium_common_config") {
   cflags = []
+  cflags_cc = []
   ldflags = []
   include_dirs = [ "." ]
-  defines = [
-    "PNG_PREFIX",
-    "PNG_USE_READ_MACROS",
-  ]
+  defines = []
 
   if (!use_system_libopenjpeg2) {
     defines += [ "OPJ_STATIC" ]
@@ -35,13 +43,189 @@
     defines += [ "_SKIA_SUPPORT_" ]
   }
 
-  if (pdf_use_skia_paths) {
-    defines += [ "_SKIA_SUPPORT_PATHS_" ]
+  if (pdf_use_partition_alloc) {
+    defines += [ "PDF_USE_PARTITION_ALLOC" ]
   }
 
   if (is_win) {
     # Assume UTF-8 by default to avoid code page dependencies.
     cflags += [ "/utf-8" ]
+
+    if (!is_clang) {
+      cflags += [
+        # Warnings permanently disabled:
+
+        # C4091: 'typedef ': ignored on left of 'X' when no variable is
+        #                    declared.
+        # This happens in a number of Windows headers. Dumb.
+        "/wd4091",
+
+        # C4127: conditional expression is constant
+        # This warning can in theory catch dead code and other problems, but
+        # triggers in far too many desirable cases where the conditional
+        # expression is either set by macros or corresponds some legitimate
+        # compile-time constant expression (due to constant template args,
+        # conditionals comparing the sizes of different types, etc.).  Some of
+        # these can be worked around, but it's not worth it.
+        "/wd4127",
+
+        # C4251: 'identifier' : class 'type' needs to have dll-interface to be
+        #        used by clients of class 'type2'
+        # This is necessary for the shared library build.
+        "/wd4251",
+
+        # C4275:  non dll-interface class used as base for dll-interface class
+        # This points out a potential (but rare) problem with referencing static
+        # fields of a non-exported base, through the base's non-exported inline
+        # functions, or directly. The warning is subtle enough that people just
+        # suppressed it when they saw it, so it's not worth it.
+        "/wd4275",
+
+        # C4312 is a VS 2015 64-bit warning for integer to larger pointer.
+        # TODO(brucedawson): fix warnings, crbug.com/554200
+        "/wd4312",
+
+        # C4324 warns when padding is added to fulfill alignas requirements,
+        # but can trigger in benign cases that are difficult to individually
+        # suppress.
+        "/wd4324",
+
+        # C4351: new behavior: elements of array 'array' will be default
+        #        initialized
+        # This is a silly "warning" that basically just alerts you that the
+        # compiler is going to actually follow the language spec like it's
+        # supposed to, instead of not following it like old buggy versions did.
+        # There's absolutely no reason to turn this on.
+        "/wd4351",
+
+        # C4355: 'this': used in base member initializer list
+        # It's commonly useful to pass |this| to objects in a class' initializer
+        # list.  While this warning can catch real bugs, most of the time the
+        # constructors in question don't attempt to call methods on the passed-in
+        # pointer (until later), and annotating every legit usage of this is
+        # simply more hassle than the warning is worth.
+        "/wd4355",
+
+        # C4503: 'identifier': decorated name length exceeded, name was
+        #        truncated
+        # This only means that some long error messages might have truncated
+        # identifiers in the presence of lots of templates.  It has no effect on
+        # program correctness and there's no real reason to waste time trying to
+        # prevent it.
+        "/wd4503",
+
+        # Warning C4589 says: "Constructor of abstract class ignores
+        # initializer for virtual base class." Disable this warning because it
+        # is flaky in VS 2015 RTM. It triggers on compiler generated
+        # copy-constructors in some cases.
+        "/wd4589",
+
+        # C4611: interaction between 'function' and C++ object destruction is
+        #        non-portable
+        # This warning is unavoidable when using e.g. setjmp/longjmp.  MSDN
+        # suggests using exceptions instead of setjmp/longjmp for C++, but
+        # Chromium code compiles without exception support.  We therefore have to
+        # use setjmp/longjmp for e.g. JPEG decode error handling, which means we
+        # have to turn off this warning (and be careful about how object
+        # destruction happens in such cases).
+        "/wd4611",
+
+        # Warnings to evaluate and possibly fix/reenable later:
+
+        "/wd4100",  # Unreferenced formal function parameter.
+        "/wd4121",  # Alignment of a member was sensitive to packing.
+        "/wd4244",  # Conversion: possible loss of data.
+        "/wd4505",  # Unreferenced local function has been removed.
+        "/wd4510",  # Default constructor could not be generated.
+        "/wd4512",  # Assignment operator could not be generated.
+        "/wd4610",  # Class can never be instantiated, constructor required.
+        "/wd4838",  # Narrowing conversion. Doesn't seem to be very useful.
+        "/wd4995",  # 'X': name was marked as #pragma deprecated
+        "/wd4996",  # Deprecated function warning.
+
+        # These are variable shadowing warnings that are new in VS2015. We
+        # should work through these at some point -- they may be removed from
+        # the RTM release in the /W4 set.
+        "/wd4456",
+        "/wd4457",
+        "/wd4458",
+        "/wd4459",
+
+        # All of our compilers support the extensions below.
+        "/wd4200",  # nonstandard extension used: zero-sized array in
+                    # struct/union
+        "/wd4201",  # nonstandard extension used: nameless struct/union
+        "/wd4204",  # nonstandard extension used : non-constant aggregate
+                    # initializer
+
+        "/wd4221",  # nonstandard extension used : 'identifier' : cannot be
+                    # initialized using address of automatic variable
+
+        # http://crbug.com/588506 - Conversion suppressions waiting on Clang
+        # -Wconversion.
+        "/wd4245",  # 'conversion' : conversion from 'type1' to 'type2',
+                    # signed/unsigned mismatch
+
+        "/wd4267",  # 'var' : conversion from 'size_t' to 'type', possible loss
+                    # of data
+
+        "/wd4305",  # 'identifier' : truncation from 'type1' to 'type2'
+        "/wd4389",  # 'operator' : signed/unsigned mismatch
+
+        "/wd4702",  # unreachable code
+
+        # http://crbug.com/848979 - MSVC is more conservative than Clang with
+        # regards to variables initialized and consumed in different branches.
+        "/wd4701",  # Potentially uninitialized local variable 'name' used
+        "/wd4703",  # Potentially uninitialized local pointer variable 'name'
+                    # used
+
+        # http://crbug.com/848979 - Remaining Clang permitted warnings.
+        "/wd4661",  # 'identifier' : no suitable definition provided for
+                    # explicit
+                    # template instantiation request
+
+        "/wd4706",  # assignment within conditional expression
+                    # MSVC is stricter and requires a boolean expression.
+
+        "/wd4715",  # 'function' : not all control paths return a value'
+                    # MSVC does not analyze switch (enum) for completeness.
+      ]
+
+      cflags_cc += [
+        # Allow "noexcept" annotations even though we compile with exceptions
+        # disabled.
+        "/wd4577",
+      ]
+
+      if (current_cpu == "x86") {
+        if (msvc_use_sse2) {
+          cflags += [ "/arch:SSE2" ]
+        }
+      }
+    }
+  }
+
+  if (is_clang) {
+    # Override -Wno-c++11-narrowing.
+    cflags += [ "-Wc++11-narrowing" ]
+
+    # TODO(crbug.com/1213098): Remove once this is in //build.
+    cflags += [ "-Wdeprecated-copy" ]
+
+    # May flag some issues when converting int to size_t.
+    cflags += [ "-Wtautological-unsigned-zero-compare" ]
+  }
+
+  if (!is_win && !is_clang) {
+    cflags += [
+      # Override -Wno-narrowing for GCC.
+      "-Wnarrowing",
+
+      # GCC assumes that control can get past an exhaustive switch and then
+      # warns if there's no return there.
+      "-Wno-return-type",
+    ]
   }
 }
 
@@ -72,10 +256,6 @@
       }
     }
   }
-
-  if (pdf_use_win32_gdi) {
-    defines += [ "PDFIUM_PRINT_TEXT_WITH_GDI" ]
-  }
 }
 
 config("pdfium_core_config") {
@@ -86,21 +266,75 @@
     "//build/config/compiler:noshadowing",
   ]
   defines = []
-  if (is_linux) {
-    if (current_cpu == "x64") {
-      defines += [ "_FX_CPU_=_FX_X64_" ]
-      cflags += [ "-fPIC" ]
-    } else if (current_cpu == "x86") {
-      defines += [ "_FX_CPU_=_FX_X86_" ]
-    }
-  }
   if (is_win) {
     cflags += [
       "/wd4324",
       "/wd4577",
     ]
   }
-  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+  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",
+  ]
+}
+
+config("pdfium_noshorten_config") {
+  cflags = []
+  if (is_clang) {
+    cflags += [ "-Wshorten-64-to-32" ]
+  }
 }
 
 source_set("pdfium_public_headers_impl") {
@@ -122,6 +356,7 @@
     "public/fpdf_progressive.h",
     "public/fpdf_save.h",
     "public/fpdf_searchex.h",
+    "public/fpdf_signature.h",
     "public/fpdf_structtree.h",
     "public/fpdf_sysfontinfo.h",
     "public/fpdf_text.h",
@@ -139,8 +374,9 @@
 }
 
 component("pdfium") {
+  output_name = "pdfium"
   libs = []
-  configs += [ ":pdfium_core_config" ]
+  configs += [ ":pdfium_strict_config" ]
   public_configs = [ ":pdfium_public_config" ]
 
   deps = [
@@ -155,7 +391,6 @@
     "fpdfsdk/formfiller",
     "fxjs",
     "third_party:pdfium_base",
-    "third_party:skia_shared",
   ]
 
   public_deps = [
@@ -180,7 +415,7 @@
   }
 
   if (is_mac) {
-    libs += [
+    frameworks = [
       "AppKit.framework",
       "CoreFoundation.framework",
     ]
@@ -191,24 +426,16 @@
     complete_static_lib = true
     configs -= [ "//build/config/compiler:thin_archive" ]
   }
-
-  if (is_component_build) {
-    deps += [ "testing/fuzzers:fuzzer_impls" ]
-  }
 }
 
-# Targets below this are only visible within this file (and to the
-# top-level gn_visibility target used to help gn_all build everything).
-visibility = [
-  ":*",
-  "//:gn_visibility",
-]
+# Targets below this are only visible within this file.
+visibility = [ ":*" ]
 
 group("pdfium_unittest_deps") {
   testonly = true
   public_deps = [
     "core/fxcrt",
-    "testing:test_support",
+    "testing:unit_test_support",
     "//testing/gmock",
     "//testing/gtest",
   ]
@@ -238,35 +465,33 @@
     "core/fxcrt:unittests",
     "core/fxge:unittests",
     "fpdfsdk:unittests",
-    "testing:test_support",
     "testing:unit_test_support",
     "//testing/gmock",
     "//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 += [
-      "fxbarcode:unittests",
-      "xfa/fde:unittests",
-      "xfa/fgas:unittests",
-      "xfa/fgas/layout:unittests",
-      "xfa/fxfa:unittests",
-      "xfa/fxfa/fm2js:unittests",
-      "xfa/fxfa/parser:unittests",
-    ]
+    if (pdf_enable_xfa) {
+      deps += [
+        "core/fxcrt/css:unittests",
+        "fxbarcode:unittests",
+        "xfa/fde:unittests",
+        "xfa/fgas/crt:unittests",
+        "xfa/fgas/font:unittests",
+        "xfa/fgas/layout:unittests",
+        "xfa/fxfa:unittests",
+        "xfa/fxfa/formcalc:unittests",
+        "xfa/fxfa/parser:unittests",
+      ]
+    }
   }
 }
 
@@ -276,7 +501,6 @@
     ":pdfium_public_headers",
     "core/fxcrt",
     "testing:embedder_test_support",
-    "testing:test_support",
     "third_party:pdfium_base_test_support",
     "//testing/gmock",
     "//testing/gtest",
@@ -293,6 +517,7 @@
   testonly = true
   sources = [ "testing/embedder_test_main.cpp" ]
   deps = [
+    ":pdfium_embeddertest_deps",
     "core/fpdfapi/edit:embeddertests",
     "core/fpdfapi/parser:embeddertests",
     "core/fpdfapi/render:embeddertests",
@@ -300,8 +525,8 @@
     "core/fxcrt",
     "core/fxge:embeddertests",
     "fpdfsdk:embeddertests",
+    "fpdfsdk/formfiller:embeddertests",
     "fpdfsdk/pwl:embeddertests",
-    "testing:test_support",
     "testing/image_diff",
     "//testing/gmock",
     "//testing/gtest",
@@ -333,29 +558,16 @@
 }
 
 executable("pdfium_diff") {
+  visibility += [ "testing/tools:test_runner_py" ]
   testonly = true
   sources = [ "testing/image_diff/image_diff.cpp" ]
   deps = [
-    ":pdfium",
     "core/fxcrt",
+    "testing:path_service",
     "testing/image_diff",
     "//build/win:default_exe_manifest",
   ]
-  configs -= [ "//build/config/compiler:chromium_code" ]
-  configs += [ "//build/config/compiler:no_chromium_code" ]
-  configs += [ ":pdfium_core_config" ]
-}
-
-if (pdf_is_standalone) {
-  source_set("samples") {
-    testonly = true
-    deps = [ "//samples" ]
-  }
-
-  group("fuzzers") {
-    testonly = true
-    deps = [ "//testing/fuzzers" ]
-  }
+  configs += [ ":pdfium_strict_config" ]
 }
 
 group("pdfium_all") {
@@ -364,11 +576,24 @@
     ":pdfium_diff",
     ":pdfium_embeddertests",
     ":pdfium_unittests",
+    "samples",
+    "testing/fuzzers",
   ]
+
   if (pdf_is_standalone) {
-    deps += [
-      ":fuzzers",
-      ":samples",
-    ]
+    deps += [ "testing/tools:test_runner_py" ]
+  }
+}
+
+# Makes additional targets reachable only for "gn check". These are not always
+# built by the "all" Ninja target, which uses the "default" group, which in turn
+# depends on the "pdfium_all" group.
+group("gn_check") {
+  deps = []
+
+  # TODO(crbug.com/pdfium/1832): Remove !is_android when //third_party/expat is
+  # available.
+  if (defined(checkout_skia) && checkout_skia && !is_android) {
+    deps += [ "//skia" ]
   }
 }
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..f23251b
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,205 @@
+# CONTRIBUTING
+In general, we follow the
+[Chromium Contributing](https://chromium.googlesource.com/chromium/src/+/main/docs/contributing.md)
+guidelines in PDFium. The code review process, and the build tools are all very
+similar to Chromium. The PDFium
+[README](https://pdfium.googlesource.com/pdfium/+/refs/heads/main/README.md)
+outlines specific build and test information for PDFium.
+
+This document focuses on how the PDFium project operates and how we’d like it
+to operate in the future. This is a living document, please file bugs if you
+think there are changes/updates which could be put in place to make it easier
+to contribute to PDFium.
+
+## Communication
+When writing a new feature or fixing an existing bug, get a second opinion
+before investing effort in coding. Coordinating up front makes it much easier
+to avoid frustration later on.
+
+If it‘s a new feature, or updating existing code, first propose it to the
+[mailing list](https://groups.google.com/forum/#!forum/pdfium).
+
+ * If a change needs further context outside the CL, it should be tracked in
+   the [bug system](https://bugs.chromium.org/p/pdfium). Bugs are the right
+   place for long histories, discussion and debate, attaching screenshots, and
+   linking to other associated bugs. Bugs are unnecessary for changes isolated
+   enough to not need any of these.
+ * If the work being implemented is especially complex or large a design
+   document may be warranted. The document should be linked to the filled bug
+   and be set to publicly viewable.
+ * If there isn't a bug and there should be one, please file a new bug.
+ * Just because there is a bug in the bug system doesn't necessarily mean that
+   a patch will be accepted.
+
+## Public APIs
+The public API of PDFium has grown over time. There are multiple mechanisms in
+place to support this growth from the stability requirements to the versioning
+fields. Along with those there are several other factors to be considered when
+adding public APIs.
+
+ * _Consistency_. We try to keep the APIs consistent with each other, this
+   includes things like naming, parameter ordering and how parameters are
+   handled.
+ * _Generality_. PDFium is used in several places outside the browser. This
+   could be server side, or in user applications. APIs should be designed to
+   work in the general case, or such that they can be expanded to the general
+   case if possible.
+ * _Documentation_. All public APIs should be documented to include information
+   on ownership of passed parameters, valid values being provided, error
+   conditions and return values.
+ * _Differentiate error conditions_. If at all possible, it should be possible
+   to tell the difference between a valid failure and an error.
+ * _Avoid global state_. APIs should receive the objects to be operated on
+   instead of assuming they exist in a global context.
+
+### Stability
+There are a lot of consumers of PDFium outside of Chromium. These include
+LibreOffice, Android and offline conversion tooling. As such, a lot of care is
+taken around the code in the
+[public](https://pdfium.googlesource.com/pdfium/+/refs/heads/main/public/)
+folder. When planning on changing the public API, the change should be preceded
+by a bug being created and an email to the mailing list to gather feedback from
+other PDFium embedders.
+
+The only stability guarantees that PDFium provides are around the APIs in the
+public folder. Any other interface in the system can be changed without notice.
+If there are features needed which are not exposed through the public headers
+you'll need to file a bug to get it added to the public APIs.
+
+#### Experimental
+All APIs start as Experimental. The experimental status is a documentation tag
+which is added to the API, the first line of the API documentation should be
+`// Experimental API.`
+
+Experimental APIs may be changed or removed entirely without formal notice to
+the community.
+
+#### Stable
+APIs eventually graduate to stable. This is done by removing the
+`// Experimental API.` marker in the documentation. We endeavor to not change
+stable APIs without notice to the community.
+
+NOTE, the process of migrating from experimental to stable isn’t well defined
+at this point. We have experimental APIs which have been that way for multiple
+years. We should work to better define how this transition happens.
+
+#### Deprecated
+If the API is retired, it is marked as deprecated and will eventually be removed.
+API deprecation should, ideally, come with a better replacement API and have a
+6-12 months deprecation period.  The pending removal should be recorded in the
+documentation comment for the API and should also be recorded in the README with
+the target removal timeframe. All deprecations should have an associated bug
+attached to them.
+
+### Versioning
+In order to allow the public API to expand there are `version` fields in some
+structures. When the versioned structures are expanded those version fields
+need to be incremented to cover the new additions. The code then needs to guard
+against the structure being received having the required version number in
+order to validate the new additions are available.
+
+## Trybot Access
+Changes must pass the try bots before they are merged into the repo. For your
+first few CLs the try bots will need to be triggered by a committer. After
+you've submitted 2-3 CLs you can request try bot access by emailing one of the
+OWNERS and requesting try bot access. This will allow you to trigger the bots
+on your own changes without needing a committer.
+
+## Committers
+All changes committed to PDFium must be reviewed by a committer. Committers
+have done significant work in the PDFium code base and have a good overall
+understanding of the system.
+
+Contributors can become committers as they exhibit a strong understanding
+of the code base. There is a general requirement for ~10 non-trivial CLs to be
+written by the contributor before being considered for committership. The
+contributor is then nominated by an existing committer and if the nomination is
+accepted by two other committers they receive committer status.
+
+## OWNERS
+The OWNERS files list long time committers to the project and have a broad
+understanding of the code base and how the various pieces interact. In the
+event of a code review stalling with a committer, the OWNERS are the first line
+of escalation. The OWNERS files inherit up the tree, so an OWNER in a top-level
+folder has OWNERS in the folders subdirectories.
+
+There are a limited number of OWNERS files in PDFium at this time due to the
+inherent interconnectedness of the code. We are hoping to expand the number of
+OWNERS files to make them more targeted as the code quality progresses.
+
+Committers can be added to OWNERS files when they exhibit a strong
+understanding of the PDFium code base. This typically involves a combination of
+significant CLs, code review on other contributor CLs, and working with the
+other OWNERs to work through design and development considerations for the code.
+An OWNER must be committed to upholding the principles for the long term health
+of the project, take on a responsibility for reviewing future work, and
+mentor new contributors. Once you are a committer, you should feel free to reach
+out to the OWNERS who have reviewed your patches to ask what else they’d like to
+see from you to be comfortable nominating you as an OWNER. Once nominated,
+OWNERS are added or removed by rough consensus of the existing OWNERS.
+
+## Escalations
+There are times when reviews stall due to differences between reviewers,
+developers and OWNERS. If this happens, please escalate the situation to one of
+the people in the top-level OWNERS file (or another of the owners if already
+discussing with a top-level owner). If the disagreement has moved up through
+all the OWNERS files in the PDFium repo, the escalation should then proceed to
+the Chromium
+[ATL_OWNERS](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/ATL_OWNERS)
+as the final deciders.
+
+The
+[Standard of Code Review](https://google.github.io/eng-practices/review/reviewer/standard.html)
+document has some good guidance on resolving conflicts during code review.
+
+## CLA
+All contributors must complete the Google contributor license agreement. For
+individual contributors, please complete the
+[Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual?csw=1)
+online. Corporate contributors must fill out the
+[Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate?csw=1)
+and send it to us as described on that page.
+
+Your first CL should add yourself to the
+[AUTHORS](https://pdfium.googlesource.com/pdfium/+/refs/heads/main/AUTHORS)
+file (unless you’re covered by one of the blanket entries).
+
+### External contributor checklist for reviewers
+Before LGTMing a change, ensure that the contribution can be accepted:
+ * Definition: The "author" is the email address that owns the code review
+   request on
+   [https://pdfium-review.googlesource.com](https://pdfium-review.googlesource.com/)
+ * Ensure the author is already listed in
+   [AUTHORS](https://pdfium.googlesource.com/pdfium/+/refs/heads/main/AUTHORS).
+   In some cases, the author's company might have a wildcard rule
+   (e.g. \*@google.com).
+ * If the author or their company is not listed, the CL should include a new
+   AUTHORS entry.
+   * Ensure the new entry is reviewed by a reviewer who works for Google.
+   * Contributor License Agreement can be verified by Googlers at
+     [http://go/cla](http://go/cla)
+   * If there is a corporate CLA for the author‘s company, it must list the
+     person explicitly (or the list of authorized contributors must say
+     something like "All employees"). If the author is not on their company’s
+     roster, do not accept the change.
+
+## Legacy Code
+The PDFium code base has been around in one form or another for a long time. As
+such, there is a lot of legacy hidden in the existing code. There are surprising
+interactions and untested corners of the code. We are actively working on
+increasing code coverage on the existing code, and especially welcome additions
+which move the coverage upwards. All new code should come with tests (either
+unit tests or integration tests depending on the feature).
+
+As part of this legacy nature, there is a good chance the code you’re working
+with wasn’t designed to do what you need it to do. There are often refactorings
+and bug fixes that end up happening along with feature development. Those
+fixes/refactorings should be pulled out to their own changes with the
+appropriate tests. This will make reviews a lot easier as, currently, it can be
+hard to tell if there are far reaching effects of a given change.
+
+There is a lot of existing technical debt that is being paid down in PDFium,
+anything we can do here to make future development easier is a great benefit to
+the project. This debt means means code reviews can take a bit longer if
+research is needed to determine how a feature change will interact with the
+rest of the system.
diff --git a/DEPS b/DEPS
index a939282..5a55c16 100644
--- a/DEPS
+++ b/DEPS
@@ -1,132 +1,475 @@
 use_relative_paths = True
 
+gclient_gn_args_file = 'build/config/gclient_args.gni'
+gclient_gn_args = [
+  'checkout_skia',
+]
+
 vars = {
   # By default, we should check out everything needed to run on the main
-  # chromium waterfalls. This var can be also be set to "small", in order
-  # to skip things are not strictly needed to build chromium for development
-  # purposes.
+  # pdfium waterfalls. This var can be also be set to 'small', in order to skip
+  # things are not strictly needed to build pdfium for development purposes,
+  # by adding the following line to the .gclient file inside a solutions entry:
+  #      "custom_vars": { "checkout_configuration": "small" },
+  # Similarly, this var can be set to 'minimal' to also skip the Skia and V8
+  # checkouts for the smallest possible checkout, where some features will not
+  # work.
   'checkout_configuration': 'default',
 
-  'checkout_instrumented_libraries': 'checkout_linux and checkout_configuration != "small"',
+  'checkout_instrumented_libraries': 'checkout_linux and checkout_configuration != "small" and checkout_configuration != "minimal"',
+
+  'checkout_skia': 'checkout_configuration != "minimal"',
+
+  'checkout_testing_corpus': 'checkout_configuration != "small" and checkout_configuration != "minimal"',
+
+  'checkout_v8': 'checkout_configuration != "minimal"',
+
+  # By default, download the fuchsia sdk from the public sdk directory.
+  'fuchsia_sdk_cipd_prefix': 'fuchsia/sdk/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',
+  'skia_git': 'https://skia.googlesource.com',
 
-  'android_ndk_revision': '27c0a8d090c666a50e40fceb4ee5b40b1a2d3f87',
-  'binutils_revision': '01aa7745b0bab64ae22600f09fd6483c60f22ebf',
-  'build_revision': '1bee638a8c4a9481ea06df4982d69488d0a5626d',
-  'buildtools_revision': '1f38b432e5630619f3aba0a22b9b63d606aee35a',
-  'catapult_revision': 'f7d73bb520283d2a06b8fde8a1b02aa33414fcd0',
-  'clang_revision': '42fbdfef1ce265b09dc6bda2ed90d83324c97481',
-  'code_coverage_revision': 'c7a868bacaccf4f52848e04564fb7de0671e0727',
-  'depot_tools_revision': 'e9730d75a00548a22e4392567243969d85c02dd4',
-  'freetype_revision': 'e5038be70414cf66da6c4d5ce4e30375884c30d8',
-  'gtest_revision': '5395345ca4f0c596110188688ed990e0de5a181c',
-  'icu_revision': 'dbd3825b31041d782c5b504c59dcfb5ac7dda08c',
-  'instrumented_lib_revision': '4dca59c6a614b08b394ed6154a8fcded9298b07e',
-  'jinja2_revision': 'b41863e42637544c2941b574c7877d3e1f663e25',
-  'jpeg_turbo_revision': 'ce0e57e8e636f5132fe6f0590a4dba91f92fd935',
-  'markupsafe_revision': '8f45f5cfa0009d2a70589bcda0349b8cb2b72783',
-  'pdfium_tests_revision': '02dd653ec62649b6f1aa4e4526071cc32d903f54',
-  'skia_revision': 'd50cc95872a8a832faea0154f7ea1fd56cebc775',
-  'tools_memory_revision': 'f7b00daf4df7f6c469f5fbc68d7f40f6bd15d6e6',
-  'trace_event_revision': '81c050f857a0e3c960cfd87f37e3d30d2ef78718',
-  'v8_revision': 'cd34145326def51cb6dcf87aed7d0caf9f62bb4f',
-  'yasm_source_revision': '720b70524a4424b15fc57e82263568c8ba0496ad',
-  'zlib_revision': '814da1f383b625955149c3845db62af3f29a4ffe',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling abseil
+  # and whatever else without interference from each other.
+  'abseil_revision': '2288062eef9624e8b48070ed2447139d3fd4a1c5',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling android_toolchain
+  # and whatever else without interference from each other.
+  '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': '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': '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': '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': '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': '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': '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': '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': '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: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: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.
+  'gtest_revision': 'af29db7ec28d6df1c7f0f745186884091e602e07',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling icu
+  # and whatever else without interference from each other.
+  'icu_revision': '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': '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': '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': '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': '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': '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.
+  'libpng_revision': '805df541c44099bb20d425ac47c666e29b1f7a80',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling libunwind
+  # and whatever else without interference from each other.
+  'libunwind_revision': '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': '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.
+  'nasm_source_revision': '7fc833e889d1afda72c06220e5bed8fb43b2e5ce',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling Ninja CIPD package version
+  # and whatever else without interference from each other.
+  'ninja_version': 'version:2@1.11.1.chromium.6',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling partition_allocator
+  # and whatever else without interference from each other.
+  'partition_allocator_revision': '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': '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.
+  'resultdb_version': 'git_revision:ebc74d10fa0d64057daa6f128e89f3672eeeec95',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling skia
+  # and whatever else without interference from each other.
+  'skia_revision': '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.
+  'test_fonts_revision': '7f51783942943e965cd56facf786544ccfc07713',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling tools_memory
+  # and whatever else without interference from each other.
+  'tools_memory_revision': '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.
+  'trace_event_revision': '147f65333c38ddd1ebf554e89965c243c8ce50b3',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling v8
+  # and whatever else without interference from each other.
+  'v8_revision': '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': '526382e41c9c5275dc329db4328b54e4f344a204',
 }
 
+# Only these hosts are allowed for dependencies in this DEPS file.
+# If you need to add a new host, and the new host is not in Chromium's DEPS
+# file's allowed_hosts list, contact Chrome infrastructure team.
+allowed_hosts = [
+  'chromium.googlesource.com',
+  'pdfium.googlesource.com',
+  'skia.googlesource.com',
+]
+
 deps = {
-  "base/trace_event/common":
-    Var('chromium_git') + "/chromium/src/base/trace_event/common.git@" +
+  'base/allocator/partition_allocator':
+    Var('chromium_git') +
+        '/chromium/src/base/allocator/partition_allocator.git@' +
+        Var('partition_allocator_revision'),
+
+  'base/trace_event/common':
+    Var('chromium_git') + '/chromium/src/base/trace_event/common.git@' +
         Var('trace_event_revision'),
 
-  "build":
-    Var('chromium_git') + "/chromium/src/build.git@" + Var('build_revision'),
+  'build':
+    Var('chromium_git') + '/chromium/src/build.git@' + Var('build_revision'),
 
-  "buildtools":
-    Var('chromium_git') + "/chromium/src/buildtools.git@" +
+  'buildtools':
+    Var('chromium_git') + '/chromium/src/buildtools.git@' +
         Var('buildtools_revision'),
 
-  "testing/corpus":
-    Var('pdfium_git') + "/pdfium_tests@" + Var('pdfium_tests_revision'),
+  'buildtools/clang_format/script':
+    Var('chromium_git') +
+        '/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@' +
+        Var('clang_format_revision'),
 
-  "third_party/android_ndk": {
-    'url': Var('chromium_git') + "/android_ndk.git@" + Var('android_ndk_revision'),
+  'buildtools/linux64': {
+    'packages': [
+      {
+        'package': 'gn/gn/linux-amd64',
+        'version': Var('gn_version'),
+      }
+    ],
+    'dep_type': 'cipd',
+    'condition': 'host_os == "linux"',
+  },
+
+  'buildtools/mac': {
+    'packages': [
+      {
+        'package': 'gn/gn/mac-${{arch}}',
+        'version': Var('gn_version'),
+      }
+    ],
+    'dep_type': 'cipd',
+    'condition': 'host_os == "mac"',
+  },
+
+  'buildtools/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@' +
+        Var('libcxx_revision'),
+
+  'buildtools/third_party/libc++abi/trunk':
+    Var('chromium_git') +
+        '/external/github.com/llvm/llvm-project/libcxxabi.git@' +
+        Var('libcxxabi_revision'),
+
+  'buildtools/third_party/libunwind/trunk':
+    Var('chromium_git') +
+        '/external/github.com/llvm/llvm-project/libunwind.git@' +
+        Var('libunwind_revision'),
+
+  'buildtools/win': {
+    'packages': [
+      {
+        'package': 'gn/gn/windows-amd64',
+        'version': Var('gn_version'),
+      }
+    ],
+    'dep_type': 'cipd',
+    'condition': 'host_os == "win"',
+  },
+
+  'testing/corpus': {
+    'url': Var('pdfium_git') + '/pdfium_tests@' + Var('pdfium_tests_revision'),
+    'condition': 'checkout_testing_corpus',
+  },
+
+  'third_party/abseil-cpp':
+    Var('chromium_git') + '/chromium/src/third_party/abseil-cpp.git@' +
+        Var('abseil_revision'),
+
+  'third_party/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': {
+    'url': Var('chromium_git') + '/catapult.git@' + Var('catapult_revision'),
     'condition': 'checkout_android',
   },
 
-  "third_party/binutils":
-    Var('chromium_git') + "/chromium/src/third_party/binutils.git@" +
-        Var('binutils_revision'),
-
-  "third_party/catapult": {
-    'url': Var('chromium_git') + '/catapult.git' + '@' + Var('catapult_revision'),
+  '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('chromium_git') + '/chromium/tools/depot_tools.git@' +
         Var('depot_tools_revision'),
 
-  "third_party/freetype/src":
+  'third_party/freetype/src':
     Var('chromium_git') + '/chromium/src/third_party/freetype2.git@' +
         Var('freetype_revision'),
 
-  "third_party/googletest/src":
-    Var('chromium_git') + '/external/github.com/google/googletest.git' + '@' +
+  'third_party/fuchsia-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':
+    Var('chromium_git') + '/external/github.com/google/googletest.git@' +
         Var('gtest_revision'),
 
-  "third_party/icu":
-    Var('chromium_git') + "/chromium/deps/icu.git@" + Var('icu_revision'),
+  'third_party/icu':
+    Var('chromium_git') + '/chromium/deps/icu.git@' + Var('icu_revision'),
 
-  "third_party/instrumented_libraries":
+  'third_party/instrumented_libraries':
     Var('chromium_git') +
-        "/chromium/src/third_party/instrumented_libraries.git@" +
+        '/chromium/src/third_party/instrumented_libraries.git@' +
         Var('instrumented_lib_revision'),
 
-  "third_party/jinja2":
-    Var('chromium_git') + "/chromium/src/third_party/jinja2.git@" +
+  'third_party/jinja2':
+    Var('chromium_git') + '/chromium/src/third_party/jinja2.git@' +
         Var('jinja2_revision'),
 
-  "third_party/markupsafe":
-    Var('chromium_git') + "/chromium/src/third_party/markupsafe.git@" +
-        Var('markupsafe_revision'),
+  'third_party/libc++/src':
+    Var('chromium_git') +
+        '/external/github.com/llvm/llvm-project/libcxx.git@' +
+        Var('libcxx_revision'),
 
-  "third_party/libjpeg_turbo":
-    Var('chromium_git') + "/chromium/deps/libjpeg_turbo.git@" +
+  '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'),
 
-  "third_party/skia":
-    Var('chromium_git') + '/skia.git@' +  Var('skia_revision'),
+  'third_party/libpng':
+    Var('chromium_git') + '/chromium/src/third_party/libpng.git@' +
+        Var('libpng_revision'),
 
-  "third_party/zlib":
-    Var('chromium_git') + "/chromium/src/third_party/zlib.git@" +
+  'third_party/markupsafe':
+    Var('chromium_git') + '/chromium/src/third_party/markupsafe.git@' +
+        Var('markupsafe_revision'),
+
+  'third_party/nasm':
+    Var('chromium_git') + '/chromium/deps/nasm.git@' +
+        Var('nasm_source_revision'),
+
+  'third_party/ninja': {
+    'packages': [
+      {
+        # https://chrome-infra-packages.appspot.com/p/infra/3pp/tools/ninja
+        'package': 'infra/3pp/tools/ninja/${{platform}}',
+        'version': Var('ninja_version'),
+      }
+    ],
+    'dep_type': 'cipd',
+  },
+
+  'third_party/skia': {
+    'url': Var('skia_git') + '/skia.git@' + Var('skia_revision'),
+    'condition': 'checkout_skia',
+  },
+
+  'third_party/test_fonts':
+    Var('chromium_git') + '/chromium/src/third_party/test_fonts.git@' +
+        Var('test_fonts_revision'),
+
+  'third_party/zlib':
+    Var('chromium_git') + '/chromium/src/third_party/zlib.git@' +
         Var('zlib_revision'),
 
-  'third_party/yasm/source/patched-yasm':
-    Var('chromium_git') + '/chromium/deps/yasm/patched-yasm.git@' +
-        Var('yasm_source_revision'),
+  'tools/clang':
+    Var('chromium_git') + '/chromium/src/tools/clang@' + Var('clang_revision'),
 
-  "tools/clang":
-    Var('chromium_git') + "/chromium/src/tools/clang@" +  Var('clang_revision'),
-
-  "tools/code_coverage":
-    Var('chromium_git') + "/chromium/src/tools/code_coverage.git@" +
+  'tools/code_coverage':
+    Var('chromium_git') + '/chromium/src/tools/code_coverage.git@' +
         Var('code_coverage_revision'),
 
-  "tools/memory":
-    Var('chromium_git') + "/chromium/src/tools/memory@" +
+  'tools/memory':
+    Var('chromium_git') + '/chromium/src/tools/memory@' +
         Var('tools_memory_revision'),
 
-  "v8":
-    Var('chromium_git') + "/v8/v8.git@" + Var('v8_revision'),
+  'tools/resultdb': {
+    'packages': [
+      {
+        'package': 'infra/tools/result_adapter/${{platform}}',
+        'version': Var('resultdb_version'),
+      },
+    ],
+    'dep_type': 'cipd',
+  },
+
+  # TODO(crbug.com/pdfium/1650): Set up autorollers for goldctl.
+  'tools/skia_goldctl/linux': {
+    'packages': [
+      {
+        'package': 'skia/tools/goldctl/linux-amd64',
+        'version': 'eZ3k373CYgRxlu4JKph6e-_7xkP02swy_jePFFMiyIQC',
+      }
+    ],
+    'dep_type': 'cipd',
+    'condition': 'checkout_linux',
+  },
+
+  'tools/skia_goldctl/mac_amd64': {
+    'packages': [
+      {
+        'package': 'skia/tools/goldctl/mac-amd64',
+        'version': 'nHUjLIViYsLxRjv-zDdmzqT8p1R3VoyHq5gdGkKeMYwC',
+      }
+    ],
+    'dep_type': 'cipd',
+    'condition': 'checkout_mac',
+  },
+
+  'tools/skia_goldctl/mac_arm64': {
+    'packages': [
+      {
+        'package': 'skia/tools/goldctl/mac-arm64',
+        'version': '-mc865SGfJAqreLZM6fkn8tgCJ7u5QLk5zm7r-ZRJ9gC',
+      }
+    ],
+    'dep_type': 'cipd',
+    'condition': 'checkout_mac',
+  },
+
+  'tools/skia_goldctl/win': {
+    'packages': [
+      {
+        'package': 'skia/tools/goldctl/windows-amd64',
+        'version': 'iEqqRADI7znrc6pG-MVnc5pBZwD25koILREPC6x2AFAC',
+      }
+    ],
+    'dep_type': 'cipd',
+    'condition': 'checkout_win',
+  },
+
+  'v8': {
+    'url': Var('chromium_git') + '/v8/v8.git@' + Var('v8_revision'),
+    'condition': 'checkout_v8',
+  },
+
 }
 
-recursedeps = [
-  # buildtools provides clang_format, libc++, and libc++abi
-  'buildtools',
-]
+recursedeps = []
 
 include_rules = [
   # Basic stuff that everyone can use.
@@ -135,27 +478,45 @@
   '+constants',
   '+testing',
   '+third_party/base',
+
+  # Abseil features must be allowlisted explicitly for now. See Chromium's
+  # //styleguide/c++/c++11.html. Allowed features' headers will be listed
+  # explicitly here.
+  '-absl',
+  '-third_party/abseil-cpp',
+  '+third_party/abseil-cpp/absl/types/optional.h',
+  '+third_party/abseil-cpp/absl/types/variant.h',
 ]
 
 specific_include_rules = {
   # Allow embedder tests to use public APIs.
-  "(.*embeddertest\.cpp)": [
-      "+public",
+  '(.*embeddertest\.cpp)': [
+    '+public',
   ]
 }
 
 hooks = [
   {
+    # Ensure that the DEPS'd "depot_tools" has its self-update capability
+    # disabled.
+    'name': 'disable_depot_tools_selfupdate',
+    'pattern': '.',
+    'action': [ 'python3',
+                'third_party/depot_tools/update_depot_tools_toggle.py',
+                '--disable',
+    ],
+  },
+  {
     # Case-insensitivity for the Win SDK. Must run before win_toolchain below.
     'name': 'ciopfs_linux',
     'pattern': '.',
     'condition': 'checkout_win and host_os == "linux"',
-    'action': [ 'python',
-                'pdfium/third_party/depot_tools/download_from_google_storage.py',
+    'action': [ 'python3',
+                'third_party/depot_tools/download_from_google_storage.py',
                 '--no_resume',
                 '--no_auth',
                 '--bucket', 'chromium-browser-clang/ciopfs',
-                '-s', 'pdfium/build/ciopfs.sha1',
+                '-s', 'build/ciopfs.sha1',
     ]
   },
   {
@@ -163,129 +524,205 @@
     'name': 'win_toolchain',
     'pattern': '.',
     'condition': 'checkout_win',
-    'action': ['python', 'pdfium/build/vs_toolchain.py', 'update', '--force'],
+    'action': ['python3', 'build/vs_toolchain.py', 'update', '--force'],
   },
   {
     # Update the Mac toolchain if necessary.
     'name': 'mac_toolchain',
     'pattern': '.',
-    'action': ['python', 'pdfium/build/mac_toolchain.py'],
+    'condition': 'checkout_mac',
+    'action': ['python3', 'build/mac_toolchain.py'],
   },
+  # Pull dsymutil binaries using checked-in hashes.
   {
-    # Pull clang-format binaries using checked-in hashes.
-    'name': 'clang_format_win',
+    'name': 'dsymutil_mac_arm64',
     'pattern': '.',
-    'action': [ 'download_from_google_storage',
+    'condition': 'host_os == "mac" and host_cpu == "arm64"',
+    'action': [ 'python3',
+                'third_party/depot_tools/download_from_google_storage.py',
                 '--no_resume',
-                '--platform=win32',
                 '--no_auth',
-                '--bucket', 'chromium-clang-format',
-                '-s', 'pdfium/buildtools/win/clang-format.exe.sha1',
+                '--bucket', 'chromium-browser-clang',
+                '-s', 'tools/clang/dsymutil/bin/dsymutil.arm64.sha1',
+                '-o', 'tools/clang/dsymutil/bin/dsymutil',
     ],
   },
   {
-    'name': 'clang_format_mac',
+    'name': 'dsymutil_mac_x64',
     'pattern': '.',
-    'action': [ 'download_from_google_storage',
+    'condition': 'host_os == "mac" and host_cpu == "x64"',
+    'action': [ 'python3',
+                'third_party/depot_tools/download_from_google_storage.py',
                 '--no_resume',
-                '--platform=darwin',
+                '--no_auth',
+                '--bucket', 'chromium-browser-clang',
+                '-s', 'tools/clang/dsymutil/bin/dsymutil.x64.sha1',
+                '-o', 'tools/clang/dsymutil/bin/dsymutil',
+    ],
+  },
+  # Pull clang-format binaries using checked-in hashes.
+  {
+    'name': 'clang_format_win',
+    'pattern': '.',
+    'condition': 'host_os == "win"',
+    'action': [ 'python3',
+                'third_party/depot_tools/download_from_google_storage.py',
+                '--no_resume',
                 '--no_auth',
                 '--bucket', 'chromium-clang-format',
-                '-s', 'pdfium/buildtools/mac/clang-format.sha1',
+                '-s', 'buildtools/win/clang-format.exe.sha1',
+    ],
+  },
+  {
+    'name': 'clang_format_mac_x64',
+    'pattern': '.',
+    'condition': 'host_os == "mac" and host_cpu == "x64"',
+    'action': [ 'python3',
+                'third_party/depot_tools/download_from_google_storage.py',
+                '--no_resume',
+                '--no_auth',
+                '--bucket', 'chromium-clang-format',
+                '-s', 'buildtools/mac/clang-format.x64.sha1',
+                '-o', 'buildtools/mac/clang-format',
+    ],
+  },
+  {
+    'name': 'clang_format_mac_arm64',
+    'pattern': '.',
+    'condition': 'host_os == "mac" and host_cpu == "arm64"',
+    'action': [ 'python3',
+                'third_party/depot_tools/download_from_google_storage.py',
+                '--no_resume',
+                '--no_auth',
+                '--bucket', 'chromium-clang-format',
+                '-s', 'buildtools/mac/clang-format.arm64.sha1',
+                '-o', 'buildtools/mac/clang-format',
     ],
   },
   {
     'name': 'clang_format_linux',
     'pattern': '.',
-    'action': [ 'download_from_google_storage',
+    'condition': 'host_os == "linux"',
+    'action': [ 'python3',
+                'third_party/depot_tools/download_from_google_storage.py',
                 '--no_resume',
-                '--platform=linux*',
                 '--no_auth',
                 '--bucket', 'chromium-clang-format',
-                '-s', 'pdfium/buildtools/linux64/clang-format.sha1',
+                '-s', 'buildtools/linux64/clang-format.sha1',
     ],
   },
   {
     # Note: On Win, this should run after win_toolchain, as it may use it.
     'name': 'clang',
     'pattern': '.',
-    'action': ['python',
-               'pdfium/tools/clang/scripts/update.py'
-    ],
-  },
-  {
-    'name': 'binutils',
-    'pattern': 'src/third_party/binutils',
-    'condition': 'host_os == "linux"',
-    'action': [
-        'python',
-        'pdfium/third_party/binutils/download.py',
+    'action': ['python3',
+               'tools/clang/scripts/update.py'
     ],
   },
   {
     'name': 'sysroot_arm',
     'pattern': '.',
     'condition': 'checkout_linux and checkout_arm',
-    'action': ['python', 'pdfium/build/linux/sysroot_scripts/install-sysroot.py',
+    'action': ['python3', 'build/linux/sysroot_scripts/install-sysroot.py',
                '--arch=arm'],
   },
   {
     'name': 'sysroot_arm64',
     'pattern': '.',
     'condition': 'checkout_linux and checkout_arm64',
-    'action': ['python', 'pdfium/build/linux/sysroot_scripts/install-sysroot.py',
+    'action': ['python3', 'build/linux/sysroot_scripts/install-sysroot.py',
                '--arch=arm64'],
   },
   {
     'name': 'sysroot_x86',
     'pattern': '.',
     'condition': 'checkout_linux and (checkout_x86 or checkout_x64)',
-    'action': ['python', 'pdfium/build/linux/sysroot_scripts/install-sysroot.py',
+    'action': ['python3', 'build/linux/sysroot_scripts/install-sysroot.py',
                '--arch=x86'],
   },
   {
     'name': 'sysroot_mips',
     'pattern': '.',
     'condition': 'checkout_linux and checkout_mips',
-    'action': ['python', 'pdfium/build/linux/sysroot_scripts/install-sysroot.py',
+    'action': ['python3', 'build/linux/sysroot_scripts/install-sysroot.py',
                '--arch=mips'],
   },
   {
     'name': 'sysroot_x64',
     'pattern': '.',
     'condition': 'checkout_linux and checkout_x64',
-    'action': ['python', 'pdfium/build/linux/sysroot_scripts/install-sysroot.py',
+    'action': ['python3', 'build/linux/sysroot_scripts/install-sysroot.py',
                '--arch=x64'],
   },
   {
-    'name': 'msan_chained_origins',
+    'name': 'test_fonts',
+    'pattern': '.',
+    'action': [ 'python3',
+                'third_party/depot_tools/download_from_google_storage.py',
+                '--no_resume',
+                '--extract',
+                '--no_auth',
+                '--bucket', 'chromium-fonts',
+                '-s', 'third_party/test_fonts/test_fonts.tar.gz.sha1',
+    ],
+  },
+  {
+    'name': 'msan_chained_origins_focal',
     'pattern': '.',
     'condition': 'checkout_instrumented_libraries',
-    'action': [ 'python',
-                'pdfium/third_party/depot_tools/download_from_google_storage.py',
-                "--no_resume",
-                "--no_auth",
-                "--bucket", "chromium-instrumented-libraries",
-                "-s", "pdfium/third_party/instrumented_libraries/binaries/msan-chained-origins-trusty.tgz.sha1",
+    'action': [ 'python3',
+                'third_party/depot_tools/download_from_google_storage.py',
+                '--no_resume',
+                '--no_auth',
+                '--bucket', 'chromium-instrumented-libraries',
+                '-s', 'third_party/instrumented_libraries/binaries/msan-chained-origins-focal.tgz.sha1',
               ],
   },
   {
-    'name': 'msan_no_origins',
+    'name': 'msan_no_origins_focal',
     'pattern': '.',
     'condition': 'checkout_instrumented_libraries',
-    'action': [ 'python',
-                'pdfium/third_party/depot_tools/download_from_google_storage.py',
-                "--no_resume",
-                "--no_auth",
-                "--bucket", "chromium-instrumented-libraries",
-                "-s", "pdfium/third_party/instrumented_libraries/binaries/msan-no-origins-trusty.tgz.sha1",
+    'action': [ 'python3',
+                'third_party/depot_tools/download_from_google_storage.py',
+                '--no_resume',
+                '--no_auth',
+                '--bucket', 'chromium-instrumented-libraries',
+                '-s', 'third_party/instrumented_libraries/binaries/msan-no-origins-focal.tgz.sha1',
               ],
   },
   {
     # Update LASTCHANGE.
     'name': 'lastchange',
     'pattern': '.',
-    'action': ['python', 'pdfium/build/util/lastchange.py',
-               '-o', 'pdfium/build/util/LASTCHANGE'],
+    'action': ['python3', 'build/util/lastchange.py',
+               '-o', 'build/util/LASTCHANGE'],
+  },
+  {
+    '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/DIR_METADATA b/DIR_METADATA
new file mode 100644
index 0000000..507a6ca
--- /dev/null
+++ b/DIR_METADATA
@@ -0,0 +1,3 @@
+monorail {
+  component: "Internals>Plugins>PDF"
+}
diff --git a/LICENSE b/LICENSE
index bb158cb..161ca16 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 //
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
diff --git a/METADATA b/METADATA
deleted file mode 100644
index d97975c..0000000
--- a/METADATA
+++ /dev/null
@@ -1,3 +0,0 @@
-third_party {
-  license_type: NOTICE
-}
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 9ad4abe..14c0506 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -1,4 +1,4 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
+# Copyright 2015 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -8,6 +8,10 @@
 for more details about the presubmit API built into depot_tools.
 """
 
+PRESUBMIT_VERSION = '2.0.0'
+
+USE_PYTHON3 = True
+
 LINT_FILTERS = [
   # Rvalue ref checks are unreliable.
   '-build/c++11',
@@ -34,6 +38,115 @@
     'cppguide.html#Names_and_Order_of_Includes')
 
 
+# Bypass the AUTHORS check for these accounts.
+_KNOWN_ROBOTS = set() | set(
+    '%s@skia-public.iam.gserviceaccount.com' % s for s in ('pdfium-autoroll',))
+
+_THIRD_PARTY = 'third_party/'
+
+# Format: Sequence of tuples containing:
+# * String pattern or, if starting with a slash, a regular expression.
+# * Sequence of strings to show when the pattern matches.
+# * Error flag. True if a match is a presubmit error, otherwise it's a warning.
+# * Sequence of paths to *not* check (regexps).
+_BANNED_CPP_FUNCTIONS = (
+    (
+        r'/\busing namespace ',
+        (
+            'Using directives ("using namespace x") are banned by the Google',
+            'Style Guide (',
+            'https://google.github.io/styleguide/cppguide.html#Namespaces ).',
+            'Explicitly qualify symbols or use using declarations ("using',
+            'x::foo").',
+        ),
+        True,
+        [_THIRD_PARTY],
+    ),
+    (
+        r'/v8::Isolate::(?:|Try)GetCurrent()',
+        (
+            'v8::Isolate::GetCurrent() and v8::Isolate::TryGetCurrent() are',
+            'banned. Hold a pointer to the v8::Isolate that was entered. Use',
+            'v8::Isolate::IsCurrent() to check whether a given v8::Isolate is',
+            'entered.',
+        ),
+        True,
+        (),
+    ),
+)
+
+
+def _CheckNoBannedFunctions(input_api, output_api):
+  """Makes sure that banned functions are not used."""
+  warnings = []
+  errors = []
+
+  def _GetMessageForMatchingType(input_api, affected_file, line_number, line,
+                                 type_name, message):
+    """Returns an string composed of the name of the file, the line number where
+    the match has been found and the additional text passed as `message` in case
+    the target type name matches the text inside the line passed as parameter.
+    """
+    result = []
+
+    if input_api.re.search(r"^ *//",
+                           line):  # Ignore comments about banned types.
+      return result
+    if line.endswith(
+        " nocheck"):  # A // nocheck comment will bypass this error.
+      return result
+
+    matched = False
+    if type_name[0:1] == '/':
+      regex = type_name[1:]
+      if input_api.re.search(regex, line):
+        matched = True
+    elif type_name in line:
+      matched = True
+
+    if matched:
+      result.append('    %s:%d:' % (affected_file.LocalPath(), line_number))
+      for message_line in message:
+        result.append('      %s' % message_line)
+
+    return result
+
+  def IsExcludedFile(affected_file, excluded_paths):
+    local_path = affected_file.LocalPath()
+    for item in excluded_paths:
+      if input_api.re.match(item, local_path):
+        return True
+    return False
+
+  def CheckForMatch(affected_file, line_num, line, func_name, message, error):
+    problems = _GetMessageForMatchingType(input_api, f, line_num, line,
+                                          func_name, message)
+    if problems:
+      if error:
+        errors.extend(problems)
+      else:
+        warnings.extend(problems)
+
+  file_filter = lambda f: f.LocalPath().endswith(('.cc', '.cpp', '.h'))
+  for f in input_api.AffectedFiles(file_filter=file_filter):
+    for line_num, line in f.ChangedContents():
+      for func_name, message, error, excluded_paths in _BANNED_CPP_FUNCTIONS:
+        if IsExcludedFile(f, excluded_paths):
+          continue
+        CheckForMatch(f, line_num, line, func_name, message, error)
+
+  result = []
+  if (warnings):
+    result.append(
+        output_api.PresubmitPromptWarning('Banned functions were used.\n' +
+                                          '\n'.join(warnings)))
+  if (errors):
+    result.append(
+        output_api.PresubmitError('Banned functions were used.\n' +
+                                  '\n'.join(errors)))
+  return result
+
+
 def _CheckUnwantedDependencies(input_api, output_api):
   """Runs checkdeps on #include statements added in this
   change. Breaking - rules is an error, breaking ! rules is a
@@ -246,12 +359,8 @@
   Each region separated by #if, #elif, #else, #endif, #define and #undef follows
   these rules separately.
   """
-  def FileFilterIncludeOrder(affected_file):
-    black_list = (input_api.DEFAULT_BLACK_LIST)
-    return input_api.FilterSourceFile(affected_file, black_list=black_list)
-
   warnings = []
-  for f in input_api.AffectedFiles(file_filter=FileFilterIncludeOrder):
+  for f in input_api.AffectedFiles(file_filter=input_api.FilterSourceFile):
     if f.LocalPath().endswith(('.cc', '.cpp', '.h', '.mm')):
       changed_linenums = set(line_num for line_num, _ in f.ChangedContents())
       warnings.extend(_CheckIncludeOrderInFile(input_api, f, changed_linenums))
@@ -262,6 +371,29 @@
                                                       warnings))
   return results
 
+
+def _CheckLibcxxRevision(input_api, output_api):
+  """Makes sure that libcxx_revision is set correctly."""
+  if 'DEPS' not in [f.LocalPath() for f in input_api.AffectedFiles()]:
+    return []
+
+  script_path = input_api.os_path.join('testing', 'tools', 'libcxx_check.py')
+  buildtools_deps_path = input_api.os_path.join('buildtools',
+                                                'deps_revisions.gni')
+
+  try:
+    errors = input_api.subprocess.check_output(
+        [script_path, 'DEPS', buildtools_deps_path])
+  except input_api.subprocess.CalledProcessError as error:
+    msg = 'libcxx_check.py failed:'
+    long_text = error.output.decode('utf-8', 'ignore')
+    return [output_api.PresubmitError(msg, long_text=long_text)]
+
+  if errors:
+    return [output_api.PresubmitError(errors)]
+  return []
+
+
 def _CheckTestDuplicates(input_api, output_api):
   """Checks that pixel and javascript tests don't contain duplicates.
   We use .in and .pdf files, having both can cause race conditions on the bots,
@@ -291,33 +423,136 @@
       tests_added.append(path)
   return results
 
-def _CheckPNGFormat(input_api, output_api):
-  """Checks that .png files have a format that will be considered valid by our
-  test runners. If a file ends with .png, then it must be of the form
-  NAME_expected(_(win|mac|linux))?.pdf.#.png"""
+
+def _CheckPngNames(input_api, output_api):
+  """Checks that .png files have the right file name format, which must be in
+  the form:
+
+  NAME_expected(_(agg|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(_(win|mac|linux))?\.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'):
       continue
     if expected_pattern.match(f.LocalPath()):
       continue
-    results.append(output_api.PresubmitError(
-        'PNG file %s does not have the correct format' % f.LocalPath()))
+    results.append(
+        output_api.PresubmitError(
+            'PNG file %s does not have the correct format' % f.LocalPath()))
   return results
 
-def CheckChangeOnUpload(input_api, output_api):
-  cpp_source_filter = lambda x: input_api.FilterSourceFile(
-      x, white_list=(r'\.(?:c|cc|cpp|h)$',))
 
+def _CheckUselessForwardDeclarations(input_api, output_api):
+  """Checks that added or removed lines in non third party affected
+     header files do not lead to new useless class or struct forward
+     declaration.
+  """
   results = []
-  results += _CheckUnwantedDependencies(input_api, output_api)
-  results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api)
-  results += input_api.canned_checks.CheckChangeLintsClean(
-      input_api, output_api, cpp_source_filter, LINT_FILTERS)
-  results += _CheckIncludeOrder(input_api, output_api)
-  results += _CheckTestDuplicates(input_api, output_api)
-  results += _CheckPNGFormat(input_api, output_api)
+  class_pattern = input_api.re.compile(r'^class\s+(\w+);$',
+                                       input_api.re.MULTILINE)
+  struct_pattern = input_api.re.compile(r'^struct\s+(\w+);$',
+                                        input_api.re.MULTILINE)
+  for f in input_api.AffectedFiles(include_deletes=False):
+    if f.LocalPath().startswith('third_party'):
+      continue
+
+    if not f.LocalPath().endswith('.h'):
+      continue
+
+    contents = input_api.ReadFile(f)
+    fwd_decls = input_api.re.findall(class_pattern, contents)
+    fwd_decls.extend(input_api.re.findall(struct_pattern, contents))
+
+    useless_fwd_decls = []
+    for decl in fwd_decls:
+      count = sum(
+          1
+          for _ in input_api.re.finditer(r'\b%s\b' %
+                                         input_api.re.escape(decl), contents))
+      if count == 1:
+        useless_fwd_decls.append(decl)
+
+    if not useless_fwd_decls:
+      continue
+
+    for line in f.GenerateScmDiff().splitlines():
+      if (line.startswith('-') and not line.startswith('--') or
+          line.startswith('+') and not line.startswith('++')):
+        for decl in useless_fwd_decls:
+          if input_api.re.search(r'\b%s\b' % decl, line[1:]):
+            results.append(
+                output_api.PresubmitPromptWarning(
+                    '%s: %s forward declaration is no longer needed' %
+                    (f.LocalPath(), decl)))
+            useless_fwd_decls.remove(decl)
+
+  return results
+
+
+def ChecksCommon(input_api, output_api):
+  results = []
+
+  results.extend(
+      input_api.canned_checks.PanProjectChecks(
+          input_api, output_api, project_name='PDFium'))
+
+  # PanProjectChecks() doesn't consider .gn/.gni files, so check those, too.
+  files_to_check = (
+      r'.*\.gn$',
+      r'.*\.gni$',
+  )
+  results.extend(
+      input_api.canned_checks.CheckLicense(
+          input_api,
+          output_api,
+          project_name='PDFium',
+          source_file_filter=lambda x: input_api.FilterSourceFile(
+              x, files_to_check=files_to_check)))
+
+  return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  results = []
+  results.extend(_CheckNoBannedFunctions(input_api, output_api))
+  results.extend(_CheckUnwantedDependencies(input_api, output_api))
+  results.extend(
+      input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
+  results.extend(
+      input_api.canned_checks.CheckChangeLintsClean(
+          input_api, output_api, lint_filters=LINT_FILTERS))
+  results.extend(_CheckIncludeOrder(input_api, output_api))
+  results.extend(_CheckLibcxxRevision(input_api, output_api))
+  results.extend(_CheckTestDuplicates(input_api, output_api))
+  results.extend(_CheckPngNames(input_api, output_api))
+  results.extend(_CheckUselessForwardDeclarations(input_api, output_api))
+
+  author = input_api.change.author_email
+  if author and author not in _KNOWN_ROBOTS:
+    results.extend(
+        input_api.canned_checks.CheckAuthorizedAuthor(input_api, output_api))
+
+  for f in input_api.AffectedFiles():
+    path, name = input_api.os_path.split(f.LocalPath())
+    if name == 'PRESUBMIT.py':
+      full_path = input_api.os_path.join(input_api.PresubmitLocalPath(), path)
+      test_file = input_api.os_path.join(path, 'PRESUBMIT_test.py')
+      if f.Action() != 'D' and input_api.os_path.exists(test_file):
+        # The PRESUBMIT.py file (and the directory containing it) might
+        # have been affected by being moved or removed, so only try to
+        # run the tests if they still exist.
+        results.extend(
+            input_api.canned_checks.RunUnitTestsInDirectory(
+                input_api,
+                output_api,
+                full_path,
+                files_to_check=[r'^PRESUBMIT_test\.py$'],
+                run_on_python2=not USE_PYTHON3,
+                run_on_python3=USE_PYTHON3,
+                skip_shebang_check=True))
 
   return results
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py
new file mode 100755
index 0000000..c5f9650
--- /dev/null
+++ b/PRESUBMIT_test.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+# Copyright 2020 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+import PRESUBMIT
+from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi, MockFile
+
+
+class BannedTypeCheckTest(unittest.TestCase):
+
+  def testBannedCppFunctions(self):
+    input_api = MockInputApi()
+    input_api.files = [
+        MockFile('some/cpp/problematic/file.cc', ['using namespace std;']),
+        MockFile('third_party/some/cpp/problematic/file.cc',
+                 ['using namespace std;']),
+        MockFile('some/cpp/ok/file.cc', ['using std::string;']),
+        MockFile('some/cpp/nocheck/file.cc',
+                 ['using namespace std;  // nocheck']),
+        MockFile('some/cpp/comment/file.cc',
+                 ['  // A comment about `using namespace std;`']),
+        MockFile('some/cpp/v8/get-current.cc', ['v8::Isolate::GetCurrent()']),
+        MockFile('some/cpp/v8/try-get-current.cc',
+                 ['v8::Isolate::TryGetCurrent()']),
+    ]
+
+    results = PRESUBMIT._CheckNoBannedFunctions(input_api, MockOutputApi())
+
+    # There are no warnings to test, so add an empty warning to keep the test
+    # extendable for the future. This block can be removed once warnings are
+    # added.
+    self.assertEqual(1, len(results))
+    results.insert(0, MockOutputApi().PresubmitPromptWarning(''))
+
+    # warnings are results[0], errors are results[1]
+    self.assertEqual(2, len(results))
+    self.assertTrue('some/cpp/problematic/file.cc' in results[1].message)
+    self.assertFalse(
+        'third_party/some/cpp/problematic/file.cc' in results[1].message)
+    self.assertFalse('some/cpp/ok/file.cc' in results[1].message)
+    self.assertFalse('some/cpp/nocheck/file.cc' in results[0].message)
+    self.assertFalse('some/cpp/nocheck/file.cc' in results[1].message)
+    self.assertFalse('some/cpp/comment/file.cc' in results[0].message)
+    self.assertFalse('some/cpp/comment/file.cc' in results[1].message)
+    self.assertTrue('some/cpp/v8/get-current.cc' in results[1].message)
+    self.assertTrue('some/cpp/v8/try-get-current.cc' in results[1].message)
+
+
+class CheckChangeOnUploadTest(unittest.TestCase):
+
+  def testCheckPngNames(self):
+    correct_paths = [
+        'test_expected.pdf.0.png',
+        'test_expected_win.pdf.1.png',
+        'test_expected_agg.pdf.3.png',
+        'test_expected_agg_linux.pdf.3.png',
+        'test_expected_skia.pdf.2.png',
+        'test_expected_skia_mac.pdf.4.png',
+        'notpng.cc',  # Check will be skipped for non-PNG files
+    ]
+    wrong_paths = [
+        'expected.pdf.0.png',  # Missing '_expected'
+        'test1_expected.0.png',  # Missing '.pdf'
+        'test2_expected.pdf.png',  # Missing page number
+        'test3_expected.pdf.x.png',  # Wrong character for page number
+        'test4_expected_linux_agg.pdf.0.png',  # Wrong order of keywords
+        'test4_expected_mac_skia.pdf.0.png',  # Wrong order of keywords
+        'test5_expected_useskia.pdf.0.png',  # Wrong keyword
+    ]
+    mock_input_api = MockInputApi()
+    mock_output_api = MockOutputApi()
+    mock_input_api.files = map(MockFile, correct_paths + wrong_paths)
+    errors = list(
+        map(str, PRESUBMIT._CheckPngNames(mock_input_api, mock_output_api)))
+
+    self.assertEqual(len(wrong_paths), len(errors))
+    self.assertFalse('notpng.cc' in errors[0])
+    for path, error in zip(wrong_paths, errors):
+      self.assertIn(path, error)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/PRESUBMIT_test_mocks.py b/PRESUBMIT_test_mocks.py
new file mode 100644
index 0000000..23e3946
--- /dev/null
+++ b/PRESUBMIT_test_mocks.py
@@ -0,0 +1,78 @@
+# Copyright 2020 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import re
+
+
+class MockInputApi(object):
+  """Mock class for the InputApi class.
+
+  This class can be used for unittests for presubmit by initializing the files
+  attribute as the list of changed files.
+  """
+
+  def __init__(self):
+    self.files = []
+    self.re = re
+
+  def AffectedFiles(self, file_filter=None, include_deletes=False):
+    # pylint: disable=unused-argument
+    return self.files
+
+
+class MockOutputApi(object):
+  """Mock class for the OutputApi class.
+
+  An instance of this class can be passed to presubmit unittests for outputting
+  various types of results.
+  """
+
+  class PresubmitResult(object):
+
+    def __init__(self, message, items=None, long_text=''):
+      self.message = message
+      self.items = items
+      self.long_text = long_text
+
+    def __repr__(self):
+      return self.message
+
+  class PresubmitError(PresubmitResult):
+
+    def __init__(self, message, items=None, long_text=''):
+      MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
+      self.type = 'error'
+
+  class PresubmitPromptWarning(PresubmitResult):
+
+    def __init__(self, message, items=None, long_text=''):
+      MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
+      self.type = 'warning'
+
+
+class MockFile(object):
+  """Mock class for the File class.
+
+  This class can be used to form the mock list of changed files in
+  MockInputApi for presubmit unittests.
+  """
+
+  def __init__(self,
+               local_path,
+               new_contents=None,
+               old_contents=None,
+               action='A'):
+    self._local_path = local_path
+    if new_contents is None:
+      new_contents = []
+    self._new_contents = new_contents
+    self._changed_contents = [(i + 1, l) for i, l in enumerate(new_contents)]
+    self._action = action
+    self._old_contents = old_contents
+
+  def ChangedContents(self):
+    return self._changed_contents
+
+  def LocalPath(self):
+    return self._local_path
diff --git a/README.md b/README.md
index a79e18d..d867e4c 100644
--- a/README.md
+++ b/README.md
@@ -2,27 +2,13 @@
 
 ## Prerequisites
 
-Get the Chromium depot\_tools via the
-[instructions](https://www.chromium.org/developers/how-tos/install-depot-tools).
-This provides the gclient utility needed below and many other tools needed for
-PDFium development.
+PDFium uses the same build tooling as Chromium. See the platform-specific
+Chromium build instructions to get started, but replace Chromium's
+"Get the code" instructions with [PDFium's](#get-the-code).
 
-Also install Python, Subversion, and Git and make sure they're in your path.
-
-
-### Windows development
-
-PDFium uses the same build tool as Chromium:
-
-#### Open source contributors
-Please refer to
-[Chromium's Visual Studio set up](https://chromium.googlesource.com/chromium/src/+/master/docs/windows_build_instructions.md#visual-studio)
-for requirements and instructions on build environment configuration.
-
-Run `set DEPOT_TOOLS_WIN_TOOLCHAIN=0`, or set that variable in your global
-environment.
-
-Compilation is done through Ninja, **not** Visual Studio.
+*   [Chromium Linux build instructions](https://chromium.googlesource.com/chromium/src/+/main/docs/linux/build_instructions.md)
+*   [Chromium Mac build instructions](https://chromium.googlesource.com/chromium/src/+/main/docs/mac_build_instructions.md)
+*   [Chromium Windows build instructions](https://chromium.googlesource.com/chromium/src/+/main/docs/windows_build_instructions.md)
 
 ### CPU Architectures supported
 
@@ -42,7 +28,7 @@
 @google.com credentials**. Enter "0" if asked for a project-id.
 
 Once you've done this, the toolchain will be installed automatically for
-you in the [Generate the build files](#GenBuild) step below.
+you in the [Generate the build files](#generate-the-build-files) step below.
 
 The toolchain will be in `depot_tools\win_toolchain\vs_files\<hash>`, and
 windbg can be found in
@@ -53,9 +39,10 @@
 
 ## Get the code
 
-The name of the top-level directory does not matter. In our examples, we use
-"repo". This directory must not have been used before by `gclient config` as
-each directory can only house a single gclient configuration.
+The name of the top-level directory does not matter. In the following example,
+the directory name is "repo". This directory must not have been used before by
+`gclient config` as each directory can only house a single gclient
+configuration.
 
 ```
 mkdir repo
@@ -65,8 +52,8 @@
 cd pdfium
 ```
 
-Additional build dependencies need to be installed by running the following from
-the `pdfium` directory.
+On Linux, additional build dependencies need to be installed by running the
+following from the `pdfium` directory.
 
 ```
 ./build/install-build-deps.sh
@@ -74,7 +61,7 @@
 
 ## Generate the build files
 
-We use GN to generate the build files and [Ninja](https://ninja-build.org/)
+PDFium uses GN to generate the build files and [Ninja](https://ninja-build.org/)
 to execute the build files.  Both of these are included with the
 depot\_tools checkout.
 
@@ -91,35 +78,27 @@
 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.
 pdf_use_skia = false
-# Set true to enable experimental Skia backend (paths only).
-pdf_use_skia_paths = false
 
 pdf_enable_xfa = true  # Set false to remove XFA support (implies JS support).
 pdf_enable_v8 = true  # Set false to remove Javascript support.
 pdf_is_standalone = true  # Set for a non-embedded build.
 is_component_build = false # Disable component build (Though it should work)
-
-clang_use_chrome_plugins = false  # Currently must be false.
 ```
 
 For sample applications like `pdfium_test` to build, one must set
 `pdf_is_standalone = true`.
 
-By default, the entire project builds with C++14, because features like V8
-support, XFA support, and the Skia backend all have dependencies on libraries
-that require C++14. If one does not need any of those features, and need to fall
-back to building in C++11 mode, then set `use_cxx11 = true`. This fallback is
-temporary and will go away in the future when PDFium fully transitions to C++14.
-See [this bug](https://crbug.com/pdfium/1407) for details.
+By default, the entire project builds with C++17.
 
-When building with the experimental Skia backend, Skia itself it built with
-C++17. There is no configuration for this. One just has to use a build toolchain
-that supports C++17.
+By default, 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.
@@ -190,7 +169,7 @@
 This saves space and also allows an easy way to reduce the test case to the
 essentials as you can simply remove everything that is not necessary.
 
-A simple example can be found [here](https://pdfium.googlesource.com/pdfium/+/refs/heads/master/testing/resources/rectangles.in).
+A simple example can be found [here](https://pdfium.googlesource.com/pdfium/+/refs/heads/main/testing/resources/rectangles.in).
 
 To transform this into a PDF, you can use the `fixup_pdf_template.py` tool:
 
@@ -208,7 +187,8 @@
 ## Embedding PDFium in your own projects
 
 The public/ directory contains header files for the APIs available for use by
-embedders of PDFium. We endeavor to keep these as stable as possible.
+embedders of PDFium. The PDFium project endeavors to keep these as stable as
+possible.
 
 Outside of the public/ directory, code may change at any time, and embedders
 should not directly call these routines.
@@ -223,13 +203,6 @@
 `third_party/pdfium` in Chromium's source code.
 This includes code coverage from PDFium's fuzzers.
 
-## Profiling
-
-Valgrind and other profiling tools do not work correctly with the standard build
-setup that PDFium uses. You will need to add
-`ro_segment_workaround_for_valgrind=true` to `args.gn` to get symbols to
-correctly appear.
-
 ## Waterfall
 
 The current health of the source tree can be found
@@ -247,26 +220,12 @@
 
 ## Bugs
 
- We use this
-[bug tracker](https://bugs.chromium.org/p/pdfium/issues/list), but for security
-bugs, please use
+PDFium uses this [bug tracker](https://bugs.chromium.org/p/pdfium/issues/list),
+but for security bugs, please use
 [Chromium's security bug template](https://bugs.chromium.org/p/chromium/issues/entry?template=Security%20Bug)
 and add the "Cr-Internals-Plugins-PDF" label.
 
 ## Contributing code
 
-For contributing code, we will follow
-[Chromium's process](https://chromium.googlesource.com/chromium/src/+/master/docs/contributing.md)
-as much as possible. The main exceptions are:
-
-1. Code has to conform to the existing style and not Chromium/Google style.
-2. PDFium uses a different Gerrit instance for code reviews, and credentials for
-this Gerrit instance need to be generated before uploading changes.
-3. PDFium is transitioning to C++14, but still supports C++11 compatibility
-for the duration of the transition period. Prefer to use only C++11 features,
-though technically C++14 is allowed in code that is only built when V8, XFA, or
-Skia is turned on.
-
-Before submitting a fix for a bug, it can help if you create an issue in the
-bug tracker. This allows easier discussion about the problem and also helps
-with statistics tracking.
+See the [CONTRIBUTING](CONTRIBUTING.md) document for more information on
+contributing to the PDFium project.
diff --git a/TEST_MAPPING b/TEST_MAPPING
deleted file mode 100644
index 561cbbf..0000000
--- a/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-    "presubmit": [
-        {
-            "name": "CtsPdfTestCases"
-        }
-    ],
-    "hwasan-postsubmit": [
-        {
-            "name": "CtsPdfTestCases"
-        }
-    ]
-}
diff --git a/build/build_config.h b/build/build_config.h
index 8165eee..478c4cf 100644
--- a/build/build_config.h
+++ b/build/build_config.h
@@ -1,7 +1,390 @@
-#define OS_POSIX
-#define OS_ANDROID
+// Copyright 2012 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file doesn't belong to any GN target by design for faster build and
+// less developer overhead.
+
+// This file adds build flags about the OS we're currently building on. They are
+// defined directly in this file instead of via a `buildflag_header` target in a
+// GN file for faster build. They are defined using the corresponding OS defines
+// (e.g. OS_WIN) which are also defined in this file (except for OS_CHROMEOS,
+// which is set by the build system). These defines are deprecated and should
+// NOT be used directly. For example:
+//    Please Use: #if BUILDFLAG(IS_WIN)
+//    Deprecated: #if defined(OS_WIN)
+//
+//  Operating System:
+//    IS_AIX / IS_ANDROID / IS_ASMJS / IS_CHROMEOS / IS_FREEBSD / IS_FUCHSIA /
+//    IS_IOS / IS_IOS_MACCATALYST / IS_LINUX / IS_MAC / IS_NACL / IS_NETBSD /
+//    IS_OPENBSD / IS_QNX / IS_SOLARIS / IS_WIN
+//  Operating System family:
+//    IS_APPLE: IOS or MAC or IOS_MACCATALYST
+//    IS_BSD: FREEBSD or NETBSD or OPENBSD
+//    IS_POSIX: AIX or ANDROID or ASMJS or CHROMEOS or FREEBSD or IOS or LINUX
+//              or MAC or NACL or NETBSD or OPENBSD or QNX or SOLARIS
+
+// This file also adds defines specific to the platform, architecture etc.
+//
+//  Platform:
+//    IS_OZONE
+//
+//  Compiler:
+//    COMPILER_MSVC / COMPILER_GCC
+//
+//  Processor:
+//    ARCH_CPU_ARM64 / ARCH_CPU_ARMEL / ARCH_CPU_LOONGARCH32 /
+//    ARCH_CPU_LOONGARCH64 / ARCH_CPU_MIPS / ARCH_CPU_MIPS64 /
+//    ARCH_CPU_MIPS64EL / ARCH_CPU_MIPSEL / ARCH_CPU_PPC64 / ARCH_CPU_S390 /
+//    ARCH_CPU_S390X / ARCH_CPU_X86 / ARCH_CPU_X86_64 / ARCH_CPU_RISCV64
+//  Processor family:
+//    ARCH_CPU_ARM_FAMILY: ARMEL or ARM64
+//    ARCH_CPU_LOONGARCH_FAMILY: LOONGARCH32 or LOONGARCH64
+//    ARCH_CPU_MIPS_FAMILY: MIPS64EL or MIPSEL or MIPS64 or MIPS
+//    ARCH_CPU_PPC64_FAMILY: PPC64
+//    ARCH_CPU_S390_FAMILY: S390 or S390X
+//    ARCH_CPU_X86_FAMILY: X86 or X86_64
+//    ARCH_CPU_RISCV_FAMILY: Riscv64
+//  Processor features:
+//    ARCH_CPU_31_BITS / ARCH_CPU_32_BITS / ARCH_CPU_64_BITS
+//    ARCH_CPU_BIG_ENDIAN / ARCH_CPU_LITTLE_ENDIAN
+
+#ifndef BUILD_BUILD_CONFIG_H_
+#define BUILD_BUILD_CONFIG_H_
+
+#include "build/buildflag.h"  // IWYU pragma: export
+
+// A set of macros to use for platform detection.
+#if defined(__native_client__)
+// __native_client__ must be first, so that other OS_ defines are not set.
+#define OS_NACL 1
+#elif defined(ANDROID)
+#define OS_ANDROID 1
+#elif defined(__APPLE__)
+// Only include TargetConditionals after testing ANDROID as some Android builds
+// on the Mac have this header available and it's not needed unless the target
+// is really an Apple platform.
+#include <TargetConditionals.h>
+#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+#define OS_IOS 1
+// Catalyst is the technology that allows running iOS apps on macOS. These
+// builds are both OS_IOS and OS_IOS_MACCATALYST.
+#if defined(TARGET_OS_MACCATALYST) && TARGET_OS_MACCATALYST
+#define OS_IOS_MACCATALYST
+#endif  // defined(TARGET_OS_MACCATALYST) && TARGET_OS_MACCATALYST
+#else
+#define OS_MAC 1
+#endif  // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+#elif defined(__linux__)
+#if !defined(OS_CHROMEOS)
+// Do not define OS_LINUX on Chrome OS build.
+// The OS_CHROMEOS macro is defined in GN.
+#define OS_LINUX 1
+#endif  // !defined(OS_CHROMEOS)
+// Include a system header to pull in features.h for glibc/uclibc macros.
+#include <assert.h>
+#if defined(__GLIBC__) && !defined(__UCLIBC__)
+// We really are using glibc, not uClibc pretending to be glibc.
+#define LIBC_GLIBC 1
+#endif
+#elif defined(_WIN32)
+#define OS_WIN 1
+#elif defined(__Fuchsia__)
+#define OS_FUCHSIA 1
+#elif defined(__FreeBSD__)
+#define OS_FREEBSD 1
+#elif defined(__NetBSD__)
+#define OS_NETBSD 1
+#elif defined(__OpenBSD__)
+#define OS_OPENBSD 1
+#elif defined(__sun)
+#define OS_SOLARIS 1
+#elif defined(__QNXNTO__)
+#define OS_QNX 1
+#elif defined(_AIX)
+#define OS_AIX 1
+#elif defined(__asmjs__) || defined(__wasm__)
+#define OS_ASMJS 1
+#elif defined(__MVS__)
+#define OS_ZOS 1
+#else
+#error Please add support for your platform in build/build_config.h
+#endif
+// NOTE: Adding a new port? Please follow
+// https://chromium.googlesource.com/chromium/src/+/main/docs/new_port_policy.md
+
+#if defined(OS_MAC) || defined(OS_IOS)
+#define OS_APPLE 1
+#endif
+
+// For access to standard BSD features, use OS_BSD instead of a
+// more specific macro.
+#if defined(OS_FREEBSD) || defined(OS_NETBSD) || defined(OS_OPENBSD)
+#define OS_BSD 1
+#endif
+
+// For access to standard POSIXish features, use OS_POSIX instead of a
+// more specific macro.
+#if defined(OS_AIX) || defined(OS_ANDROID) || defined(OS_ASMJS) ||  \
+    defined(OS_FREEBSD) || defined(OS_IOS) || defined(OS_LINUX) ||  \
+    defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_NACL) ||  \
+    defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_QNX) || \
+    defined(OS_SOLARIS) || defined(OS_ZOS)
+#define OS_POSIX 1
+#endif
+
+// OS build flags
+#if defined(OS_AIX)
+#define BUILDFLAG_INTERNAL_IS_AIX() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_AIX() (0)
+#endif
+
+#if defined(OS_ANDROID)
+#define BUILDFLAG_INTERNAL_IS_ANDROID() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_ANDROID() (0)
+#endif
+
+#if defined(OS_APPLE)
+#define BUILDFLAG_INTERNAL_IS_APPLE() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_APPLE() (0)
+#endif
+
+#if defined(OS_ASMJS)
+#define BUILDFLAG_INTERNAL_IS_ASMJS() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_ASMJS() (0)
+#endif
+
+#if defined(OS_BSD)
+#define BUILDFLAG_INTERNAL_IS_BSD() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_BSD() (0)
+#endif
+
+#if defined(OS_CHROMEOS)
+#define BUILDFLAG_INTERNAL_IS_CHROMEOS() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_CHROMEOS() (0)
+#endif
+
+#if defined(OS_FREEBSD)
+#define BUILDFLAG_INTERNAL_IS_FREEBSD() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_FREEBSD() (0)
+#endif
+
+#if defined(OS_FUCHSIA)
+#define BUILDFLAG_INTERNAL_IS_FUCHSIA() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_FUCHSIA() (0)
+#endif
+
+#if defined(OS_IOS)
+#define BUILDFLAG_INTERNAL_IS_IOS() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_IOS() (0)
+#endif
+
+#if defined(OS_IOS_MACCATALYST)
+#define BUILDFLAG_INTERNAL_IS_IOS_MACCATALYST() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_IOS_MACCATALYST() (0)
+#endif
+
+#if defined(OS_LINUX)
+#define BUILDFLAG_INTERNAL_IS_LINUX() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_LINUX() (0)
+#endif
+
+#if defined(OS_MAC)
+#define BUILDFLAG_INTERNAL_IS_MAC() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_MAC() (0)
+#endif
+
+#if defined(OS_NACL)
+#define BUILDFLAG_INTERNAL_IS_NACL() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_NACL() (0)
+#endif
+
+#if defined(OS_NETBSD)
+#define BUILDFLAG_INTERNAL_IS_NETBSD() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_NETBSD() (0)
+#endif
+
+#if defined(OS_OPENBSD)
+#define BUILDFLAG_INTERNAL_IS_OPENBSD() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_OPENBSD() (0)
+#endif
+
+#if defined(OS_POSIX)
+#define BUILDFLAG_INTERNAL_IS_POSIX() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_POSIX() (0)
+#endif
+
+#if defined(OS_QNX)
+#define BUILDFLAG_INTERNAL_IS_QNX() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_QNX() (0)
+#endif
+
+#if defined(OS_SOLARIS)
+#define BUILDFLAG_INTERNAL_IS_SOLARIS() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_SOLARIS() (0)
+#endif
+
+#if defined(OS_WIN)
+#define BUILDFLAG_INTERNAL_IS_WIN() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_WIN() (0)
+#endif
+
+#if defined(USE_OZONE)
+#define BUILDFLAG_INTERNAL_IS_OZONE() (1)
+#else
+#define BUILDFLAG_INTERNAL_IS_OZONE() (0)
+#endif
+
+// Compiler detection. Note: clang masquerades as GCC on POSIX and as MSVC on
+// Windows.
+#if defined(__GNUC__)
+#define COMPILER_GCC 1
+#elif defined(_MSC_VER)
+#define COMPILER_MSVC 1
+#else
+#error Please add support for your compiler in build/build_config.h
+#endif
+
+// Processor architecture detection.  For more info on what's defined, see:
+//   http://msdn.microsoft.com/en-us/library/b0084kay.aspx
+//   http://www.agner.org/optimize/calling_conventions.pdf
+//   or with gcc, run: "echo | gcc -E -dM -"
+#if defined(_M_X64) || defined(__x86_64__)
+#define ARCH_CPU_X86_FAMILY 1
+#define ARCH_CPU_X86_64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(_M_IX86) || defined(__i386__)
+#define ARCH_CPU_X86_FAMILY 1
+#define ARCH_CPU_X86 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__s390x__)
+#define ARCH_CPU_S390_FAMILY 1
+#define ARCH_CPU_S390X 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_BIG_ENDIAN 1
+#elif defined(__s390__)
+#define ARCH_CPU_S390_FAMILY 1
+#define ARCH_CPU_S390 1
+#define ARCH_CPU_31_BITS 1
+#define ARCH_CPU_BIG_ENDIAN 1
+#elif (defined(__PPC64__) || defined(__PPC__)) && defined(__BIG_ENDIAN__)
+#define ARCH_CPU_PPC64_FAMILY 1
+#define ARCH_CPU_PPC64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_BIG_ENDIAN 1
+#elif defined(__PPC64__)
+#define ARCH_CPU_PPC64_FAMILY 1
+#define ARCH_CPU_PPC64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__ARMEL__)
+#define ARCH_CPU_ARM_FAMILY 1
+#define ARCH_CPU_ARMEL 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__aarch64__) || defined(_M_ARM64)
+#define ARCH_CPU_ARM_FAMILY 1
+#define ARCH_CPU_ARM64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__pnacl__) || defined(__asmjs__) || defined(__wasm__)
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__MIPSEL__)
+#if defined(__LP64__)
+#define ARCH_CPU_MIPS_FAMILY 1
+#define ARCH_CPU_MIPS64EL 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#else
+#define ARCH_CPU_MIPS_FAMILY 1
+#define ARCH_CPU_MIPSEL 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#endif
+#elif defined(__MIPSEB__)
+#if defined(__LP64__)
+#define ARCH_CPU_MIPS_FAMILY 1
+#define ARCH_CPU_MIPS64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_BIG_ENDIAN 1
+#else
+#define ARCH_CPU_MIPS_FAMILY 1
+#define ARCH_CPU_MIPS 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_BIG_ENDIAN 1
+#endif
+#elif defined(__loongarch__)
+#define ARCH_CPU_LOONGARCH_FAMILY 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#if __loongarch_grlen == 64
+#define ARCH_CPU_LOONGARCH64 1
+#define ARCH_CPU_64_BITS 1
+#else
+#define ARCH_CPU_LOONGARCH32 1
+#define ARCH_CPU_32_BITS 1
+#endif
+#elif defined(__riscv) && (__riscv_xlen == 64)
+#define ARCH_CPU_RISCV_FAMILY 1
+#define ARCH_CPU_RISCV64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#else
+#error Please add support for your architecture in build/build_config.h
+#endif
+
+// Type detection for wchar_t.
+#if defined(OS_WIN)
+#define WCHAR_T_IS_UTF16
+#elif defined(OS_FUCHSIA)
+#define WCHAR_T_IS_UTF32
+#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
+#define WCHAR_T_IS_UTF32
+#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff)
+// On Posix, we'll detect short wchar_t, but projects aren't guaranteed to
+// compile in this mode (in particular, Chrome doesn't). This is intended for
+// other projects using base who manage their own dependencies and make sure
+// short wchar works for them.
+#define WCHAR_T_IS_UTF16
+#else
+#error Please add support for your compiler in build/build_config.h
+#endif
+
+#if defined(OS_ANDROID)
+// The compiler thinks std::string::const_iterator and "const char*" are
+// equivalent types.
+#define STD_STRING_ITERATOR_IS_CHAR_POINTER
+// The compiler thinks std::u16string::const_iterator and "char16*" are
+// equivalent types.
+#define BASE_STRING16_ITERATOR_IS_CHAR16_POINTER
+#endif
+
 #define USE_SYSTEM_ICUUC
 #define USE_SYSTEM_LIBJPEG
 #define USE_SYSTEM_ZLIB
-// Makes base/logging.h and base/bits.h work.
-#define COMPILER_GCC
+
+#endif  // BUILD_BUILD_CONFIG_H_
+
diff --git a/build/buildflag.h b/build/buildflag.h
new file mode 100644
index 0000000..d87a220
--- /dev/null
+++ b/build/buildflag.h
@@ -0,0 +1,48 @@
+// Copyright 2015 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BUILD_BUILDFLAG_H_
+#define BUILD_BUILDFLAG_H_
+
+// These macros un-mangle the names of the build flags in a way that looks
+// natural, and gives errors if the flag is not defined. Normally in the
+// preprocessor it's easy to make mistakes that interpret "you haven't done
+// the setup to know what the flag is" as "flag is off". Normally you would
+// include the generated header rather than include this file directly.
+//
+// This is for use with generated headers. See build/buildflag_header.gni.
+
+// This dance of two macros does a concatenation of two preprocessor args using
+// ## doubly indirectly because using ## directly prevents macros in that
+// parameter from being expanded.
+#define BUILDFLAG_CAT_INDIRECT(a, b) a ## b
+#define BUILDFLAG_CAT(a, b) BUILDFLAG_CAT_INDIRECT(a, b)
+
+// Accessor for build flags.
+//
+// To test for a value, if the build file specifies:
+//
+//   ENABLE_FOO=true
+//
+// Then you would check at build-time in source code with:
+//
+//   #include "foo_flags.h"  // The header the build file specified.
+//
+//   #if BUILDFLAG(ENABLE_FOO)
+//     ...
+//   #endif
+//
+// There will no #define called ENABLE_FOO so if you accidentally test for
+// whether that is defined, it will always be negative. You can also use
+// the value in expressions:
+//
+//   const char kSpamServerName[] = BUILDFLAG(SPAM_SERVER_NAME);
+//
+// Because the flag is accessed as a preprocessor macro with (), an error
+// will be thrown if the proper header defining the internal flag value has
+// not been included.
+#define BUILDFLAG(flag) (BUILDFLAG_CAT(BUILDFLAG_INTERNAL_, flag)())
+
+#endif  // BUILD_BUILDFLAG_H_
+
diff --git a/build_overrides/BUILDCONFIG.gn b/build_overrides/BUILDCONFIG.gn
new file mode 100644
index 0000000..d5ced94
--- /dev/null
+++ b/build_overrides/BUILDCONFIG.gn
@@ -0,0 +1,631 @@
+# Copyright 2022 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# =============================================================================
+# WHAT IS THIS FILE?
+# =============================================================================
+#
+# This is a copy of //build/config/BUILDCONFIG.gn. The difference is it adds an
+# extra default_compiler_configs to use PDFium's desired default compiler
+# config. See "PDFIUM MODIFICATIONS" below.
+#
+# This is the main GN build configuration. This file is loaded after the
+# build args (args.gn) for the build directory and after the toplevel ".gn"
+# file (which points to this file as the build configuration).
+#
+# This file will be executed and the resulting context will be used to execute
+# every other file in the build. So variables declared here (that don't start
+# with an underscore) will be implicitly global.
+
+# =============================================================================
+# PLATFORM SELECTION
+# =============================================================================
+#
+# There are two main things to set: "os" and "cpu". The "toolchain" is the name
+# of the GN thing that encodes combinations of these things.
+#
+# Users typically only set the variables "target_os" and "target_cpu" in "gn
+# args", the rest are set up by our build and internal to GN.
+#
+# There are three different types of each of these things: The "host"
+# represents the computer doing the compile and never changes. The "target"
+# represents the main thing we're trying to build. The "current" represents
+# which configuration is currently being defined, which can be either the
+# host, the target, or something completely different (like nacl). GN will
+# run the same build file multiple times for the different required
+# configuration in the same build.
+#
+# This gives the following variables:
+#  - host_os, host_cpu, host_toolchain
+#  - target_os, target_cpu, default_toolchain
+#  - current_os, current_cpu, current_toolchain.
+#
+# Note the default_toolchain isn't symmetrical (you would expect
+# target_toolchain). This is because the "default" toolchain is a GN built-in
+# concept, and "target" is something our build sets up that's symmetrical with
+# its GYP counterpart. Potentially the built-in default_toolchain variable
+# could be renamed in the future.
+#
+# When writing build files, to do something only for the host:
+#   if (current_toolchain == host_toolchain) { ...
+
+if (target_os == "") {
+  target_os = host_os
+}
+
+if (target_cpu == "") {
+  if (target_os == "android") {
+    # If we're building for Android, we should assume that we want to
+    # build for ARM by default, not the host_cpu (which is likely x64).
+    # This allows us to not have to specify both target_os and target_cpu
+    # on the command line.
+    target_cpu = "arm"
+  } else {
+    target_cpu = host_cpu
+  }
+}
+
+if (current_cpu == "") {
+  current_cpu = target_cpu
+}
+if (current_os == "") {
+  current_os = target_os
+}
+
+# =============================================================================
+# BUILD FLAGS
+# =============================================================================
+#
+# This block lists input arguments to the build, along with their default
+# values.
+#
+# If a value is specified on the command line, it will overwrite the defaults
+# given in a declare_args block, otherwise the default will be used.
+#
+# YOU SHOULD ALMOST NEVER NEED TO ADD FLAGS TO THIS FILE. GN allows any file in
+# the build to declare build flags. If you need a flag for a single component,
+# you can just declare it in the corresponding BUILD.gn file.
+#
+# - If your feature is a single target, say //components/foo, you can put
+#   a declare_args() block in //components/foo/BUILD.gn and use it there.
+#   Nobody else in the build needs to see the flag.
+#
+# - Defines based on build variables should be implemented via the generated
+#   build flag header system. See //build/buildflag_header.gni. You can put
+#   the buildflag_header target in the same file as the build flag itself. You
+#   should almost never set "defines" directly.
+#
+# - If your flag toggles a target on and off or toggles between different
+#   versions of similar things, write a "group" target that forwards to the
+#   right target (or no target) depending on the value of the build flag. This
+#   group can be in the same BUILD.gn file as the build flag, and targets can
+#   depend unconditionally on the group rather than duplicating flag checks
+#   across many targets.
+#
+# - If a semi-random set of build files REALLY needs to know about a define and
+#   the above pattern for isolating the build logic in a forwarding group
+#   doesn't work, you can put the argument in a .gni file. This should be put
+#   in the lowest level of the build that knows about this feature (which should
+#   almost always be outside of the //build directory!).
+#
+# Other flag advice:
+#
+# - Use boolean values when possible. If you need a default value that expands
+#   to some complex thing in the default case (like the location of the
+#   compiler which would be computed by a script), use a default value of -1 or
+#   the empty string. Outside of the declare_args block, conditionally expand
+#   the default value as necessary.
+#
+# - Use a name like "use_foo" or "is_foo" (whatever is more appropriate for
+#   your feature) rather than just "foo".
+#
+# - Write good comments directly above the declaration with no blank line.
+#   These comments will appear as documentation in "gn args --list".
+#
+# - Don't call exec_script inside declare_args. This will execute the script
+#   even if the value is overridden, which is wasteful. See first bullet.
+
+declare_args() {
+  # Set to enable the official build level of optimization. This has nothing
+  # to do with branding, but enables an additional level of optimization above
+  # release (!is_debug). This might be better expressed as a tri-state
+  # (debug, release, official) but for historical reasons there are two
+  # separate flags.
+  #
+  # IMPORTANT NOTE: (!is_debug) is *not* sufficient to get satisfying
+  # performance. In particular, DCHECK()s are still enabled for release builds,
+  # which can halve overall performance, and do increase memory usage. Always
+  # set "is_official_build" to true for any build intended to ship to end-users.
+  is_official_build = false
+
+  # Set to true when compiling with the Clang compiler.
+  is_clang = current_os != "linux" ||
+             (current_cpu != "s390x" && current_cpu != "s390" &&
+              current_cpu != "ppc64" && current_cpu != "ppc" &&
+              current_cpu != "mips" && current_cpu != "mips64" &&
+              current_cpu != "riscv64")
+
+  # Allows the path to a custom target toolchain to be injected as a single
+  # argument, and set as the default toolchain.
+  custom_toolchain = ""
+
+  # This should not normally be set as a build argument.  It's here so that
+  # every toolchain can pass through the "global" value via toolchain_args().
+  host_toolchain = ""
+
+  # Do not set this directly.
+  # It should be set only by //build/toolchains/android:robolectric_x64.
+  # True when compiling native code for use with robolectric_binary().
+  is_robolectric = false
+
+  # DON'T ADD MORE FLAGS HERE. Read the comment above.
+}
+
+declare_args() {
+  # Debug build. Enabling official builds automatically sets is_debug to false.
+  is_debug = !is_official_build
+}
+
+declare_args() {
+  # Component build. Setting to true compiles targets declared as "components"
+  # as shared libraries loaded dynamically. This speeds up development time.
+  # When false, components will be linked statically.
+  #
+  # For more information see
+  # https://chromium.googlesource.com/chromium/src/+/main/docs/component_build.md
+  is_component_build = is_debug && current_os != "ios"
+}
+
+assert(!(is_debug && is_official_build), "Can't do official debug builds")
+assert(!(current_os == "ios" && is_component_build),
+       "Can't use component build on iOS")
+
+# ==============================================================================
+# TOOLCHAIN SETUP
+# ==============================================================================
+#
+# Here we set the default toolchain, as well as the variable host_toolchain
+# which will identify the toolchain corresponding to the local system when
+# doing cross-compiles. When not cross-compiling, this will be the same as the
+# default toolchain.
+#
+# We do this before anything else to make sure we complain about any
+# unsupported os/cpu combinations as early as possible.
+
+if (host_toolchain == "") {
+  # This should only happen in the top-level context.
+  # In a specific toolchain context, the toolchain_args()
+  # block should have propagated a value down.
+  # TODO(dpranke): Add some sort of assert here that verifies that
+  # no toolchain omitted host_toolchain from its toolchain_args().
+
+  if (host_os == "linux") {
+    if (target_os != "linux") {
+      host_toolchain = "//build/toolchain/linux:clang_$host_cpu"
+    } else if (is_clang) {
+      host_toolchain = "//build/toolchain/linux:clang_$host_cpu"
+    } else {
+      host_toolchain = "//build/toolchain/linux:$host_cpu"
+    }
+  } else if (host_os == "mac") {
+    host_toolchain = "//build/toolchain/mac:clang_$host_cpu"
+  } else if (host_os == "win") {
+    # On Windows always use the target CPU for host builds for x86/x64. On the
+    # configurations we support this will always work and it saves build steps.
+    # Windows ARM64 targets require an x64 host for cross build.
+    if (target_cpu == "x86" || target_cpu == "x64") {
+      if (is_clang) {
+        host_toolchain = "//build/toolchain/win:win_clang_$target_cpu"
+      } else {
+        host_toolchain = "//build/toolchain/win:$target_cpu"
+      }
+    } else if (is_clang) {
+      host_toolchain = "//build/toolchain/win:win_clang_$host_cpu"
+    } else {
+      host_toolchain = "//build/toolchain/win:$host_cpu"
+    }
+  } else if (host_os == "aix") {
+    host_toolchain = "//build/toolchain/aix:$host_cpu"
+  } else if (host_os == "zos") {
+    host_toolchain = "//build/toolchain/zos:$host_cpu"
+  } else {
+    assert(false, "Unsupported host_os: $host_os")
+  }
+}
+
+_default_toolchain = ""
+
+if (target_os == "android") {
+  assert(host_os == "linux", "Android builds are only supported on Linux.")
+  _default_toolchain = "//build/toolchain/android:android_clang_$target_cpu"
+} else if (target_os == "chromeos" || target_os == "linux") {
+  # See comments in build/toolchain/cros/BUILD.gn about board compiles.
+  if (is_clang) {
+    _default_toolchain = "//build/toolchain/linux:clang_$target_cpu"
+  } else {
+    _default_toolchain = "//build/toolchain/linux:$target_cpu"
+  }
+} else if (target_os == "fuchsia") {
+  _default_toolchain = "//build/toolchain/fuchsia:$target_cpu"
+} else if (target_os == "ios") {
+  _default_toolchain = "//build/toolchain/ios:ios_clang_$target_cpu"
+} else if (target_os == "mac") {
+  assert(host_os == "mac" || host_os == "linux",
+         "Mac cross-compiles are unsupported.")
+  _default_toolchain = "//build/toolchain/mac:clang_$target_cpu"
+} else if (target_os == "win") {
+  # On Windows, we use the same toolchain for host and target by default.
+  # Beware, win cross builds have some caveats, see docs/win_cross.md
+  if (is_clang) {
+    _default_toolchain = "//build/toolchain/win:win_clang_$target_cpu"
+  } else {
+    _default_toolchain = "//build/toolchain/win:$target_cpu"
+  }
+} else if (target_os == "winuwp") {
+  # Only target WinUWP on for a Windows store application and only
+  # x86, x64 and arm are supported target CPUs.
+  assert(target_cpu == "x86" || target_cpu == "x64" || target_cpu == "arm" ||
+         target_cpu == "arm64")
+  _default_toolchain = "//build/toolchain/win:uwp_$target_cpu"
+} else if (target_os == "aix") {
+  _default_toolchain = "//build/toolchain/aix:$target_cpu"
+} else if (target_os == "zos") {
+  _default_toolchain = "//build/toolchain/zos:$target_cpu"
+} else {
+  assert(false, "Unsupported target_os: $target_os")
+}
+
+# If a custom toolchain has been set in the args, set it as default. Otherwise,
+# set the default toolchain for the platform (if any).
+if (custom_toolchain != "") {
+  set_default_toolchain(custom_toolchain)
+} else if (_default_toolchain != "") {
+  set_default_toolchain(_default_toolchain)
+}
+
+# =============================================================================
+# OS DEFINITIONS
+# =============================================================================
+#
+# We set these various is_FOO booleans for convenience in writing OS-based
+# conditions.
+#
+# - is_android, is_chromeos, is_ios, and is_win should be obvious.
+# - is_mac is set only for desktop Mac. It is not set on iOS.
+# - is_posix is true for mac and any Unix-like system (basically everything
+#   except Fuchsia and Windows).
+# - is_linux is true for desktop Linux, but not for ChromeOS nor Android (which
+#   is generally too different despite being based on the Linux kernel).
+#
+# Do not add more is_* variants here for random lesser-used Unix systems like
+# aix or one of the BSDs. If you need to check these, just check the
+# current_os value directly.
+
+is_android = current_os == "android"
+is_chromeos = current_os == "chromeos"
+is_fuchsia = current_os == "fuchsia"
+is_ios = current_os == "ios"
+is_linux = current_os == "linux"
+is_mac = current_os == "mac"
+is_nacl = current_os == "nacl"
+is_win = current_os == "win" || current_os == "winuwp"
+
+is_apple = is_ios || is_mac
+is_posix = !is_win && !is_fuchsia
+
+# =============================================================================
+# TARGET DEFAULTS
+# =============================================================================
+#
+# Set up the default configuration for every build target of the given type.
+# The values configured here will be automatically set on the scope of the
+# corresponding target. Target definitions can add or remove to the settings
+# here as needed.
+#
+# WHAT GOES HERE?
+#
+# Other than the main compiler and linker configs, the only reason for a config
+# to be in this list is if some targets need to explicitly override that config
+# by removing it. This is how targets opt-out of flags. If you don't have that
+# requirement and just need to add a config everywhere, reference it as a
+# sub-config of an existing one, most commonly the main "compiler" one.
+
+# Holds all configs used for running the compiler.
+default_compiler_configs = [
+  "//build/config:feature_flags",
+  "//build/config/compiler:afdo",
+  "//build/config/compiler:afdo_optimize_size",
+  "//build/config/compiler:cet_shadow_stack",
+  "//build/config/compiler:chromium_code",
+  "//build/config/compiler:compiler",
+  "//build/config/compiler:compiler_arm_fpu",
+  "//build/config/compiler:compiler_arm_thumb",
+  "//build/config/compiler:default_include_dirs",
+  "//build/config/compiler:default_init_stack_vars",
+  "//build/config/compiler:default_optimization",
+  "//build/config/compiler:default_stack_frames",
+  "//build/config/compiler:default_symbols",
+  "//build/config/compiler:export_dynamic",
+  "//build/config/compiler:no_exceptions",
+  "//build/config/compiler:no_rtti",
+  "//build/config/compiler:no_unresolved_symbols",
+  "//build/config/compiler:runtime_library",
+  "//build/config/compiler:thin_archive",
+  "//build/config/compiler:thinlto_optimize_default",
+  "//build/config/compiler/pgo:default_pgo_flags",
+  "//build/config/coverage:default_coverage",
+  "//build/config/sanitizers:default_sanitizer_flags",
+]
+
+if (is_win) {
+  default_compiler_configs += [
+    "//build/config/win:default_cfg_compiler",
+    "//build/config/win:default_crt",
+    "//build/config/win:lean_and_mean",
+    "//build/config/win:nominmax",
+    "//build/config/win:unicode",
+    "//build/config/win:winver",
+  ]
+}
+
+if (is_apple) {
+  default_compiler_configs += [ "//build/config/compiler:enable_arc2" ]
+}
+
+if (is_posix) {
+  if (current_os != "aix") {
+    default_compiler_configs +=
+        [ "//build/config/gcc:symbol_visibility_hidden" ]
+  }
+}
+
+if (is_fuchsia) {
+  default_compiler_configs += [ "//build/config/gcc:symbol_visibility_hidden" ]
+}
+
+if (is_android) {
+  default_compiler_configs +=
+      [ "//build/config/android:default_orderfile_instrumentation" ]
+}
+
+if (is_clang && !is_nacl) {
+  default_compiler_configs += [
+    "//build/config/clang:find_bad_constructs",
+    "//build/config/clang:extra_warnings",
+  ]
+}
+
+# Debug/release-related defines.
+if (is_debug) {
+  default_compiler_configs += [ "//build/config:debug" ]
+} else {
+  default_compiler_configs += [ "//build/config:release" ]
+}
+
+# =============================================================================
+# Begin PDFIUM MODIFICATIONS
+# =============================================================================
+import("//pdfium.gni")
+if (!pdf_use_cxx20) {
+  if (is_win && !is_clang) {
+    msvc_use_cxx17 = true
+  } else {
+    default_compiler_configs += [ "//build_overrides/compiler:force_cxx17" ]
+  }
+}
+
+# =============================================================================
+# End PDFIUM MODIFICATIONS
+# =============================================================================
+
+# Static libraries and source sets use only the compiler ones.
+set_defaults("static_library") {
+  configs = default_compiler_configs
+}
+set_defaults("source_set") {
+  configs = default_compiler_configs
+}
+set_defaults("rust_library") {
+  configs = default_compiler_configs
+}
+set_defaults("rust_proc_macro") {
+  configs = default_compiler_configs
+}
+
+# Compute the set of configs common to all linked targets (shared libraries,
+# loadable modules, executables) to avoid duplication below.
+if (is_win) {
+  # Many targets remove these configs, so they are not contained within
+  # //build/config:executable_config for easy removal.
+  _linker_configs = [
+    "//build/config/win:default_incremental_linking",
+
+    # Default to console-mode apps. Most of our targets are tests and such
+    # that shouldn't use the windows subsystem.
+    "//build/config/win:console",
+  ]
+} else if (is_mac) {
+  _linker_configs = [ "//build/config/apple:strip_all" ]
+} else {
+  _linker_configs = []
+}
+
+# Executable defaults.
+default_executable_configs = default_compiler_configs + [
+                               "//build/config:default_libs",
+                               "//build/config:executable_config",
+                             ] + _linker_configs
+
+if (is_win) {
+  # Turn on linker CFI for executables, and position it so it can be removed
+  # if needed.
+  default_executable_configs += [ "//build/config/win:cfi_linker" ]
+}
+
+set_defaults("executable") {
+  configs = default_executable_configs
+}
+
+# Shared library and loadable module defaults (also for components in component
+# mode).
+default_shared_library_configs = default_compiler_configs + [
+                                   "//build/config:default_libs",
+                                   "//build/config:shared_library_config",
+                                 ] + _linker_configs
+if (is_win) {
+  # Turn on linker CFI for DLLs, and position it so it can be removed if needed.
+  default_shared_library_configs += [ "//build/config/win:cfi_linker" ]
+}
+
+if (is_android) {
+  # Strip native JNI exports from shared libraries by default. Binaries that
+  # want this can remove this config.
+  default_shared_library_configs +=
+      [ "//build/config/android:hide_all_but_jni_onload" ]
+}
+set_defaults("shared_library") {
+  configs = default_shared_library_configs
+}
+set_defaults("loadable_module") {
+  configs = default_shared_library_configs
+
+  # loadable_modules are generally used by other libs, not just via JNI.
+  if (is_android) {
+    configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
+  }
+}
+
+# A helper for forwarding testonly and visibility.
+# Forwarding "*" does not include variables from outer scopes (to avoid copying
+# all globals into each template invocation), so it will not pick up
+# file-scoped or outer-template-scoped variables. Normally this behavior is
+# desired, but "visibility" and "testonly" are commonly defined in outer scopes.
+# Explicitly forwarding them in forward_variables_from() works around this
+# nuance. See //build/docs/writing_gn_templates.md#using-forward_variables_from
+TESTONLY_AND_VISIBILITY = [
+  "testonly",
+  "visibility",
+]
+
+# Sets default dependencies for executable and shared_library targets.
+#
+# Variables
+#   no_default_deps: If true, no standard dependencies will be added.
+#       Targets that set this usually also want to remove
+#       "//build/config/compiler:runtime_library" from configs (to remove
+#       its subconfig "//build/config/c++:runtime_library").
+foreach(_target_type,
+        [
+          "executable",
+          "loadable_module",
+          "shared_library",
+        ]) {
+  template(_target_type) {
+    # Alias "target_name" because it is clobbered by forward_variables_from().
+    _target_name = target_name
+    target(_target_type, _target_name) {
+      forward_variables_from(invoker,
+                             "*",
+                             TESTONLY_AND_VISIBILITY + [ "no_default_deps" ])
+      forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+      if (!defined(deps)) {
+        deps = []
+      }
+      if (!defined(invoker.no_default_deps) || !invoker.no_default_deps) {
+        # This pulls in one of:
+        # //build/config:executable_deps
+        # //build/config:loadable_module_deps
+        # //build/config:shared_library_deps
+        # (This explicit list is so that grepping for these configs finds where
+        # they are used.)
+        deps += [ "//build/config:${_target_type}_deps" ]
+      }
+
+      # On Android, write shared library output file to metadata. We will use
+      # this information to, for instance, collect all shared libraries that
+      # should be packaged into an APK.
+      if (!defined(invoker.metadata) && (is_android || is_robolectric) &&
+          (_target_type == "shared_library" ||
+           _target_type == "loadable_module")) {
+        _output_name = _target_name
+        if (defined(invoker.output_name)) {
+          _output_name = invoker.output_name
+        }
+
+        # Remove 'lib' prefix from output name if it exists.
+        _magic_prefix = "$0x01$0x01"
+        _output_name = string_replace("${_magic_prefix}${_output_name}",
+                                      "${_magic_prefix}lib",
+                                      _magic_prefix,
+                                      1)
+        _output_name = string_replace(_output_name, _magic_prefix, "", 1)
+
+        if (defined(output_extension)) {
+          _shlib_extension = ".$output_extension"
+        } else if (is_component_build && _target_type != "loadable_module") {
+          _shlib_extension = ".cr.so"
+        } else {
+          _shlib_extension = ".so"
+        }
+
+        metadata = {
+          shared_libraries =
+              [ "$root_out_dir/lib${_output_name}${_shlib_extension}" ]
+        }
+      }
+    }
+  }
+}
+
+# ==============================================================================
+# COMPONENT SETUP
+# ==============================================================================
+
+# Defines a component, which equates to a shared_library when
+# is_component_build == true and a static_library otherwise.
+#
+# Use static libraries for the static build rather than source sets because
+# many of of our test binaries link many large dependencies but often don't
+# use large portions of them. The static libraries are much more efficient to
+# link in this situation since only the necessary object files are linked.
+#
+# The invoker can override the type of the target in the non-component-build
+# case by setting static_component_type to either "source_set" or
+# "static_library". If unset, the default will be used.
+template("component") {
+  if (is_component_build) {
+    _component_mode = "shared_library"
+  } else if (defined(invoker.static_component_type)) {
+    assert(invoker.static_component_type == "static_library" ||
+           invoker.static_component_type == "source_set")
+    _component_mode = invoker.static_component_type
+  } else if (!defined(invoker.sources) || invoker.sources == []) {
+    # When there are no sources defined, use a source set to avoid creating
+    # an empty static library (which generally don't work).
+    _component_mode = "source_set"
+  } else {
+    _component_mode = "static_library"
+  }
+  target(_component_mode, target_name) {
+    forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+    forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)
+  }
+}
+
+# Component defaults
+# Set a variable since we also want to make this available
+# to mixed_component.gni
+if (is_component_build) {
+  default_component_configs = default_shared_library_configs
+  if (is_android) {
+    default_component_configs -=
+        [ "//build/config/android:hide_all_but_jni_onload" ]
+  }
+} else {
+  default_component_configs = default_compiler_configs
+}
+
+set_defaults("component") {
+  configs = default_component_configs
+}
diff --git a/build_overrides/build.gni b/build_overrides/build.gni
index bb15253..b10e681 100644
--- a/build_overrides/build.gni
+++ b/build_overrides/build.gni
@@ -1,35 +1,22 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# Copyright 2016 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# See https://bugs.chromium.org/p/webrtc/issues/detail?id=5453.
-# Some WebRTC targets require the 10.7 deployment version of the Mac SDK
-# and a 10.11 min SDK, but those targets are only used in non-Chromium
-# builds. We can remove this when Chromium drops 10.6 support and also
-# requires 10.7.
-mac_sdk_min_build_override = "10.10"
-mac_deployment_target_build_override = "10.7"
-
 # Variable that can be used to support multiple build scenarios, like having
 # Chromium specific targets in a client project's GN file etc.
 build_with_chromium = false
 
-# 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
 
+# Enables assertions on safety checks in libc++.
+enable_safe_libcxx = true
+
 # Whether to use the neon FPU instruction set or not.
 if (current_cpu == "arm") {
   arm_use_neon = true
 }
 
-# PDFium builds don't use Chromium's third_party/binutils.
-linux_use_bundled_binutils_override = false
-
 # PDFium just uses the Chromium suppression files for now.
 asan_suppressions_file = "//build/sanitizers/asan_suppressions.cc"
 lsan_suppressions_file = "//build/sanitizers/lsan_suppressions.cc"
@@ -47,6 +34,10 @@
   # obtained with gclient sync after setting the environment variable
   # FORCE_MAC_TOOLCHAIN].
   use_system_xcode = ""
+
+  # Allows googletest to pretty-print various absl types.
+  # Assumes //third_party/abseil-cpp is an available dependency for googletest.
+  gtest_enable_absl_printers = true
 }
 
 if (use_system_xcode == "") {
diff --git a/build_overrides/compiler/BUILD.gn b/build_overrides/compiler/BUILD.gn
new file mode 100644
index 0000000..b6cc641
--- /dev/null
+++ b/build_overrides/compiler/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2022 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# A simplified config to consistently use C++17 and override the config
+# in //build/config/compiler, which is using C++20 by default on many platforms.
+
+assert(!is_nacl)
+
+config("force_cxx17") {
+  cflags_cc = []
+
+  if (is_linux || is_chromeos || is_android || current_os == "aix") {
+    if (is_clang) {
+      standard_prefix = "c"
+    } else {
+      standard_prefix = "gnu"
+    }
+    cflags_cc += [ "-std=${standard_prefix}++17" ]
+  } else if (is_win) {
+    cflags_cc += [ "/std:c++17" ]
+  } else {
+    cflags_cc += [ "-std=c++17" ]
+  }
+}
diff --git a/build_overrides/gtest.gni b/build_overrides/gtest.gni
index bf30fb1..6afd879 100644
--- a/build_overrides/gtest.gni
+++ b/build_overrides/gtest.gni
@@ -1,4 +1,4 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# Copyright 2016 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -8,7 +8,7 @@
 # Exclude support for platform-specific operations across unit tests.
 gtest_include_platform_test = false
 
-# Exclude support for testing Objective C code on OS X and iOS.
+# Exclude support for testing Objective C code on macOS and iOS.
 gtest_include_objc_support = false
 
 # Exclude support for flushing coverage files on iOS.
diff --git a/build_overrides/partition_alloc.gni b/build_overrides/partition_alloc.gni
new file mode 100644
index 0000000..56f913c
--- /dev/null
+++ b/build_overrides/partition_alloc.gni
@@ -0,0 +1,21 @@
+# Copyright 2022 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+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_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
+put_ref_count_in_previous_slot_default = false
+enable_backup_ref_ptr_slow_checks_default = false
+enable_dangling_raw_ptr_checks_default = false
diff --git a/build_overrides/pdfium.gni b/build_overrides/pdfium.gni
index ab89cd9..b7dc9e0 100644
--- a/build_overrides/pdfium.gni
+++ b/build_overrides/pdfium.gni
@@ -1,4 +1,4 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# Copyright 2016 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -17,16 +17,13 @@
 # Default: Without XFA support.
 pdf_enable_xfa_override = false
 
-# Build PDFium against skia (experimental) rather than agg, replacing all PDFium
-# graphics.
-# Default: Use agg.
+# Build PDFium with PartitionAlloc support, directing `fxcrt` to use
+# it as its memory allocator in lieu of `malloc()`.
+# Default: Use PartitionAlloc when building with Clang.
+pdf_use_partition_alloc_override = is_clang
+
+# Build PDFium to use Skia (experimental) for all PDFium graphics.
+# If enabled, coexists in build with AGG graphics and the default
+# renderer is selectable at runtime.
+# The default is to use AGG only when `pdf_use_skia_override` is false.
 pdf_use_skia_override = false
-
-# Build PDFium against skia (experimental) rather than agg, adding only path
-# support.
-# Default: Use agg.
-pdf_use_skia_paths_override = false
-
-# Build PDFium either with or without experimental win32 GDI APIs.
-# Default: Without experimental win32 GDI APIs.
-pdf_use_win32_gdi_override = false
diff --git a/codereview.settings b/codereview.settings
index 77265c9..a23bae5 100644
--- a/codereview.settings
+++ b/codereview.settings
@@ -1,6 +1,6 @@
 # This file is used by git cl to get repository specific information.
+BUG_PREFIX: pdfium:
 CC_LIST: pdfium-reviews@googlegroups.com
-CODE_REVIEW_SERVER: codereview.chromium.org
 GERRIT_HOST: True
 PROJECT: pdfium
 STATUS: http://pdfium-status.appspot.com/status
diff --git a/constants/Android.bp b/constants/Android.bp
index caa06a7..9acfe51 100644
--- a/constants/Android.bp
+++ b/constants/Android.bp
@@ -7,8 +7,12 @@
     default_applicable_licenses: ["external_pdfium_license"],
 }
 
-cc_library_headers {
+cc_library_static {
     name: "libpdfium-constants",
+    defaults: ["pdfium-core"],
     export_include_dirs: ["."],
     visibility: ["//external/pdfium:__subpackages__"],
+    srcs: [
+        "*.cpp",
+    ],
 }
diff --git a/constants/BUILD.gn b/constants/BUILD.gn
index 4c10fed..3424743 100644
--- a/constants/BUILD.gn
+++ b/constants/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -6,12 +6,27 @@
 
 source_set("constants") {
   sources = [
+    "access_permissions.h",
+    "annotation_common.cpp",
     "annotation_common.h",
     "annotation_flags.h",
+    "appearance.cpp",
+    "appearance.h",
+    "ascii.h",
+    "font_encodings.cpp",
+    "font_encodings.h",
+    "form_fields.cpp",
     "form_fields.h",
     "form_flags.h",
+    "page_object.cpp",
     "page_object.h",
+    "stream_dict_common.cpp",
     "stream_dict_common.h",
+    "transparency.cpp",
     "transparency.h",
   ]
+  configs += [
+    "../:pdfium_strict_config",
+    "../:pdfium_noshorten_config",
+  ]
 }
diff --git a/constants/access_permissions.h b/constants/access_permissions.h
new file mode 100644
index 0000000..b95f7e9
--- /dev/null
+++ b/constants/access_permissions.h
@@ -0,0 +1,21 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONSTANTS_ACCESS_PERMISSIONS_H_
+#define CONSTANTS_ACCESS_PERMISSIONS_H_
+
+namespace pdfium {
+namespace access_permissions {
+
+// PDF 1.7 spec, table 3.20.
+// User access permissions.
+constexpr uint32_t kModifyContent = 1 << 3;
+constexpr uint32_t kModifyAnnotation = 1 << 5;
+constexpr uint32_t kFillForm = 1 << 8;
+constexpr uint32_t kExtractForAccessibility = 1 << 9;
+
+}  // namespace access_permissions
+}  // namespace pdfium
+
+#endif  // CONSTANTS_ACCESS_PERMISSIONS_H_
diff --git a/constants/annotation_common.cpp b/constants/annotation_common.cpp
new file mode 100644
index 0000000..6110e38
--- /dev/null
+++ b/constants/annotation_common.cpp
@@ -0,0 +1,37 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "constants/annotation_common.h"
+
+namespace pdfium {
+namespace annotation {
+
+// PDF 1.7 spec, table 8.15.
+// Entries common to all annotation dictionaries.
+const char kType[] = "Type";
+const char kSubtype[] = "Subtype";
+const char kRect[] = "Rect";
+const char kContents[] = "Contents";
+const char kP[] = "P";
+const char kNM[] = "NM";
+const char kM[] = "M";
+const char kF[] = "F";
+const char kAP[] = "AP";
+const char kAS[] = "AS";
+const char kBorder[] = "Border";
+const char kC[] = "C";
+const char kStructParent[] = "StructParent";
+const char kOC[] = "OC";
+
+// Entries for polygon and polyline annotations.
+const char kVertices[] = "Vertices";
+
+// Entries for ink annotations
+const char kInkList[] = "InkList";
+
+// Entries for line annotations
+const char kL[] = "L";
+
+}  // namespace annotation
+}  // namespace pdfium
diff --git a/constants/annotation_common.h b/constants/annotation_common.h
index 471d244..baf0677 100644
--- a/constants/annotation_common.h
+++ b/constants/annotation_common.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,23 +8,26 @@
 namespace pdfium {
 namespace annotation {
 
-// PDF 1.7 spec, table 8.15.
-// Entries common to all annotation dictionaries.
+extern const char kType[];
+extern const char kSubtype[];
+extern const char kRect[];
+extern const char kContents[];
+extern const char kP[];
+extern const char kNM[];
+extern const char kM[];
+extern const char kF[];
+extern const char kAP[];
+extern const char kAS[];
+extern const char kBorder[];
+extern const char kC[];
+extern const char kStructParent[];
+extern const char kOC[];
 
-constexpr char kType[] = "Type";
-constexpr char kSubtype[] = "Subtype";
-constexpr char kRect[] = "Rect";
-constexpr char kContents[] = "Contents";
-constexpr char kP[] = "P";
-constexpr char kNM[] = "NM";
-constexpr char kM[] = "M";
-constexpr char kF[] = "F";
-constexpr char kAP[] = "AP";
-constexpr char kAS[] = "AS";
-constexpr char kBorder[] = "Border";
-constexpr char kC[] = "C";
-constexpr char kStructParent[] = "StructParent";
-constexpr char kOC[] = "OC";
+extern const char kVertices[];
+
+extern const char kInkList[];
+
+extern const char kL[];
 
 }  // namespace annotation
 }  // namespace pdfium
diff --git a/constants/annotation_flags.h b/constants/annotation_flags.h
index d2731da..d44a0c1 100644
--- a/constants/annotation_flags.h
+++ b/constants/annotation_flags.h
@@ -1,10 +1,12 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef CONSTANTS_ANNOTATION_FLAGS_H_
 #define CONSTANTS_ANNOTATION_FLAGS_H_
 
+#include <stdint.h>
+
 namespace pdfium {
 namespace annotation_flags {
 
diff --git a/constants/appearance.cpp b/constants/appearance.cpp
new file mode 100644
index 0000000..3ccdddd
--- /dev/null
+++ b/constants/appearance.cpp
@@ -0,0 +1,23 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "constants/appearance.h"
+
+namespace pdfium {
+namespace appearance {
+
+// ISO 32000-1:2008 spec, table 189.
+// Entries in an appearance characteristics dictionary.
+const char kR[] = "R";
+const char kBC[] = "BC";
+const char kBG[] = "BG";
+const char kCA[] = "CA";
+const char kRC[] = "RC";
+const char kAC[] = "AC";
+const char kI[] = "I";
+const char kRI[] = "RI";
+const char kIX[] = "IX";
+
+}  // namespace appearance
+}  // namespace pdfium
diff --git a/constants/appearance.h b/constants/appearance.h
new file mode 100644
index 0000000..2a5b752
--- /dev/null
+++ b/constants/appearance.h
@@ -0,0 +1,24 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONSTANTS_APPEARANCE_H_
+#define CONSTANTS_APPEARANCE_H_
+
+namespace pdfium {
+namespace appearance {
+
+extern const char kR[];
+extern const char kBC[];
+extern const char kBG[];
+extern const char kCA[];
+extern const char kRC[];
+extern const char kAC[];
+extern const char kI[];
+extern const char kRI[];
+extern const char kIX[];
+
+}  // namespace appearance
+}  // namespace pdfium
+
+#endif  // CONSTANTS_APPEARANCE_H_
diff --git a/constants/ascii.h b/constants/ascii.h
new file mode 100644
index 0000000..2f64419
--- /dev/null
+++ b/constants/ascii.h
@@ -0,0 +1,30 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONSTANTS_ASCII_H_
+#define CONSTANTS_ASCII_H_
+
+#include <stdint.h>
+
+namespace pdfium {
+namespace ascii {
+
+constexpr uint8_t kNul = 0x00;
+constexpr uint8_t kControlA = 0x01;
+constexpr uint8_t kControlB = 0x02;
+constexpr uint8_t kControlC = 0x03;
+constexpr uint8_t kBackspace = 0x08;
+constexpr uint8_t kTab = 0x09;
+constexpr uint8_t kNewline = 0x0a;
+constexpr uint8_t kReturn = 0x0d;
+constexpr uint8_t kControlV = 0x16;
+constexpr uint8_t kControlX = 0x18;
+constexpr uint8_t kControlZ = 0x1a;
+constexpr uint8_t kEscape = 0x1b;
+constexpr uint8_t kSpace = 0x20;
+
+}  // namespace ascii
+}  // namespace pdfium
+
+#endif  // CONSTANTS_ASCII_H_
diff --git a/constants/font_encodings.cpp b/constants/font_encodings.cpp
new file mode 100644
index 0000000..4359701
--- /dev/null
+++ b/constants/font_encodings.cpp
@@ -0,0 +1,17 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "constants/font_encodings.h"
+
+namespace pdfium {
+namespace font_encodings {
+
+// ISO 32000-1:2008 spec, table D1.
+const char kMacRomanEncoding[] = "MacRomanEncoding";
+const char kWinAnsiEncoding[] = "WinAnsiEncoding";
+const char kPDFDocEncoding[] = "PDFDocEncoding";
+const char kMacExpertEncoding[] = "MacExpertEncoding";
+
+}  // namespace font_encodings
+}  // namespace pdfium
diff --git a/constants/font_encodings.h b/constants/font_encodings.h
new file mode 100644
index 0000000..aefd9f1
--- /dev/null
+++ b/constants/font_encodings.h
@@ -0,0 +1,19 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONSTANTS_FONT_ENCODINGS_H_
+#define CONSTANTS_FONT_ENCODINGS_H_
+
+namespace pdfium {
+namespace font_encodings {
+
+extern const char kMacRomanEncoding[];
+extern const char kWinAnsiEncoding[];
+extern const char kPDFDocEncoding[];
+extern const char kMacExpertEncoding[];
+
+}  // namespace font_encodings
+}  // namespace pdfium
+
+#endif  // CONSTANTS_FONT_ENCODINGS_H_
diff --git a/constants/form_fields.cpp b/constants/form_fields.cpp
new file mode 100644
index 0000000..32ef84c
--- /dev/null
+++ b/constants/form_fields.cpp
@@ -0,0 +1,38 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "constants/form_fields.h"
+
+namespace pdfium {
+namespace form_fields {
+
+// ISO 32000-1:2008 table 220.
+// Entries common to all field dictionaries.
+const char kFT[] = "FT";
+const char kParent[] = "Parent";
+const char kKids[] = "Kids";
+const char kT[] = "T";
+const char kTU[] = "TU";
+const char kTM[] = "TM";
+const char kFf[] = "Ff";
+const char kV[] = "V";
+const char kDV[] = "DV";
+const char kAA[] = "AA";
+
+// ISO 32000-1:2008 table 220.
+// Values for FT keyword.
+const char kBtn[] = "Btn";
+const char kTx[] = "Tx";
+const char kCh[] = "Ch";
+const char kSig[] = "Sig";
+
+// ISO 32000-1:2008 table 222.
+// Entries common to fields containing variable text.
+const char kDA[] = "DA";
+const char kQ[] = "Q";
+const char kDS[] = "DS";
+const char kRV[] = "RV";
+
+}  // namespace form_fields
+}  // namespace pdfium
diff --git a/constants/form_fields.h b/constants/form_fields.h
index 5b7c169..129bbd3 100644
--- a/constants/form_fields.h
+++ b/constants/form_fields.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,24 +8,26 @@
 namespace pdfium {
 namespace form_fields {
 
-// PDF 1.7 spec, table 8.69.
-// Entries common to all field dictionaries.
-constexpr char kFT[] = "FT";
-constexpr char kParent[] = "Parent";
-constexpr char kKids[] = "Kids";
-constexpr char kT[] = "T";
-constexpr char kTU[] = "TU";
-constexpr char kTM[] = "TM";
-constexpr char kFf[] = "Ff";
-constexpr char kV[] = "V";
-constexpr char kDV[] = "DV";
-constexpr char kAA[] = "AA";
+extern const char kFT[];
+extern const char kParent[];
+extern const char kKids[];
+extern const char kT[];
+extern const char kTU[];
+extern const char kTM[];
+extern const char kFf[];
+extern const char kV[];
+extern const char kDV[];
+extern const char kAA[];
 
-// FT values from PDF 1.7 spec, table 8.69.
-constexpr char kBtn[] = "Btn";
-constexpr char kTx[] = "Tx";
-constexpr char kCh[] = "Ch";
-constexpr char kSig[] = "Sig";
+extern const char kBtn[];
+extern const char kTx[];
+extern const char kCh[];
+extern const char kSig[];
+
+extern const char kDA[];
+extern const char kQ[];
+extern const char kDS[];
+extern const char kRV[];
 
 }  // namespace form_fields
 }  // namespace pdfium
diff --git a/constants/form_flags.h b/constants/form_flags.h
index 148bb4c..602a164 100644
--- a/constants/form_flags.h
+++ b/constants/form_flags.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/constants/page_object.cpp b/constants/page_object.cpp
new file mode 100644
index 0000000..0a3e068
--- /dev/null
+++ b/constants/page_object.cpp
@@ -0,0 +1,24 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "constants/page_object.h"
+
+namespace pdfium {
+namespace page_object {
+
+// PDF 1.7 spec, table 3.27.
+// Entries in a page object.
+const char kType[] = "Type";
+const char kParent[] = "Parent";
+const char kResources[] = "Resources";
+const char kMediaBox[] = "MediaBox";
+const char kCropBox[] = "CropBox";
+const char kBleedBox[] = "BleedBox";
+const char kTrimBox[] = "TrimBox";
+const char kArtBox[] = "ArtBox";
+const char kContents[] = "Contents";
+const char kRotate[] = "Rotate";
+
+}  // namespace page_object
+}  // namespace pdfium
diff --git a/constants/page_object.h b/constants/page_object.h
index 8a41b8c..6fb7d68 100644
--- a/constants/page_object.h
+++ b/constants/page_object.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,19 +8,16 @@
 namespace pdfium {
 namespace page_object {
 
-// PDF 1.7 spec, table 3.27.
-// Entries in a page object.
-
-constexpr char kType[] = "Type";
-constexpr char kParent[] = "Parent";
-constexpr char kResources[] = "Resources";
-constexpr char kMediaBox[] = "MediaBox";
-constexpr char kCropBox[] = "CropBox";
-constexpr char kBleedBox[] = "BleedBox";
-constexpr char kTrimBox[] = "TrimBox";
-constexpr char kArtBox[] = "ArtBox";
-constexpr char kContents[] = "Contents";
-constexpr char kRotate[] = "Rotate";
+extern const char kType[];
+extern const char kParent[];
+extern const char kResources[];
+extern const char kMediaBox[];
+extern const char kCropBox[];
+extern const char kBleedBox[];
+extern const char kTrimBox[];
+extern const char kArtBox[];
+extern const char kContents[];
+extern const char kRotate[];
 
 }  // namespace page_object
 }  // namespace pdfium
diff --git a/constants/stream_dict_common.cpp b/constants/stream_dict_common.cpp
new file mode 100644
index 0000000..5d294c4
--- /dev/null
+++ b/constants/stream_dict_common.cpp
@@ -0,0 +1,22 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "constants/stream_dict_common.h"
+
+namespace pdfium {
+namespace stream {
+
+// PDF 1.7 spec, table 3.4.
+// Entries common to all stream dictionaries.
+//
+// TODO(https://crbug.com/pdfium/1049): Examine all usages of "Length",
+// "Filter", and "F".
+const char kLength[] = "Length";
+const char kFilter[] = "Filter";
+const char kDecodeParms[] = "DecodeParms";
+const char kF[] = "F";
+const char kDL[] = "DL";
+
+}  // namespace stream
+}  // namespace pdfium
diff --git a/constants/stream_dict_common.h b/constants/stream_dict_common.h
index fc12622..feb887a 100644
--- a/constants/stream_dict_common.h
+++ b/constants/stream_dict_common.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,18 +8,11 @@
 namespace pdfium {
 namespace stream {
 
-// PDF 1.7 spec, table 3.4.
-// Entries common to all stream dictionaries.
-
-// TODO(https://crbug.com/pdfium/1049): Examine all usages of "Length",
-// "Filter", and "F".
-constexpr char kLength[] = "Length";
-constexpr char kFilter[] = "Filter";
-constexpr char kDecodeParms[] = "DecodeParms";
-constexpr char kF[] = "F";
-// constexpr char kFFilter[] = "FFilter";
-// constexpr char kFDecodeParms[] = "FDecodeParms";
-constexpr char kDL[] = "DL";
+extern const char kLength[];
+extern const char kFilter[];
+extern const char kDecodeParms[];
+extern const char kF[];
+extern const char kDL[];
 
 }  // namespace stream
 }  // namespace pdfium
diff --git a/constants/transparency.cpp b/constants/transparency.cpp
new file mode 100644
index 0000000..11b8703
--- /dev/null
+++ b/constants/transparency.cpp
@@ -0,0 +1,48 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "constants/transparency.h"
+
+namespace pdfium {
+namespace transparency {
+
+// PDF 1.7 spec, table 7.2.
+// Standard separable blend modes.
+const char kNormal[] = "Normal";
+const char kMultiply[] = "Multiply";
+const char kScreen[] = "Screen";
+const char kOverlay[] = "Overlay";
+const char kDarken[] = "Darken";
+const char kLighten[] = "Lighten";
+const char kColorDodge[] = "ColorDodge";
+const char kColorBurn[] = "ColorBurn";
+const char kHardLight[] = "HardLight";
+const char kSoftLight[] = "SoftLight";
+const char kDifference[] = "Difference";
+const char kExclusion[] = "Exclusion";
+
+// PDF 1.7 spec, table 7.3.
+// Standard nonseparable blend modes.
+const char kHue[] = "Hue";
+const char kSaturation[] = "Saturation";
+const char kColor[] = "Color";
+const char kLuminosity[] = "Luminosity";
+
+// PDF 1.7 spec, table 7.10.
+// Entries in a soft-mask dictionary.
+const char kSoftMaskSubType[] = "S";
+const char kAlpha[] = "Alpha";
+const char kG[] = "G";
+const char kBC[] = "BC";
+const char kTR[] = "TR";
+
+// PDF 1.7 spec, table 7.13.
+// Additional entries specific to a transparency group attributes dictionary.
+const char kGroupSubType[] = "S";
+const char kTransparency[] = "Transparency";
+const char kCS[] = "CS";
+const char kI[] = "I";
+
+}  // namespace transparency
+}  // namespace pdfium
diff --git a/constants/transparency.h b/constants/transparency.h
index 6532868..21b22c4 100644
--- a/constants/transparency.h
+++ b/constants/transparency.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,46 +8,34 @@
 namespace pdfium {
 namespace transparency {
 
-// PDF 1.7 spec, table 7.2.
-// Standard separable blend modes.
+extern const char kNormal[];
+extern const char kMultiply[];
+extern const char kScreen[];
+extern const char kOverlay[];
+extern const char kDarken[];
+extern const char kLighten[];
+extern const char kColorDodge[];
+extern const char kColorBurn[];
+extern const char kHardLight[];
+extern const char kSoftLight[];
+extern const char kDifference[];
+extern const char kExclusion[];
 
-constexpr char kNormal[] = "Normal";
-constexpr char kMultiply[] = "Multiply";
-constexpr char kScreen[] = "Screen";
-constexpr char kOverlay[] = "Overlay";
-constexpr char kDarken[] = "Darken";
-constexpr char kLighten[] = "Lighten";
-constexpr char kColorDodge[] = "ColorDodge";
-constexpr char kColorBurn[] = "ColorBurn";
-constexpr char kHardLight[] = "HardLight";
-constexpr char kSoftLight[] = "SoftLight";
-constexpr char kDifference[] = "Difference";
-constexpr char kExclusion[] = "Exclusion";
+extern const char kHue[];
+extern const char kSaturation[];
+extern const char kColor[];
+extern const char kLuminosity[];
 
-// PDF 1.7 spec, table 7.3.
-// Standard nonseparable blend modes.
+extern const char kSoftMaskSubType[];
+extern const char kAlpha[];
+extern const char kG[];
+extern const char kBC[];
+extern const char kTR[];
 
-constexpr char kHue[] = "Hue";
-constexpr char kSaturation[] = "Saturation";
-constexpr char kColor[] = "Color";
-constexpr char kLuminosity[] = "Luminosity";
-
-// PDF 1.7 spec, table 7.10.
-// Entries in a soft-mask dictionary.
-
-constexpr char kSoftMaskSubType[] = "S";
-constexpr char kAlpha[] = "Alpha";
-constexpr char kG[] = "G";
-constexpr char kBC[] = "BC";
-constexpr char kTR[] = "TR";
-
-// PDF 1.7 spec, table 7.13.
-// Additional entries specific to a transparency group attributes dictionary.
-
-constexpr char kGroupSubType[] = "S";
-constexpr char kTransparency[] = "Transparency";
-constexpr char kCS[] = "CS";
-constexpr char kI[] = "I";
+extern const char kGroupSubType[];
+extern const char kTransparency[];
+extern const char kCS[];
+extern const char kI[];
 
 }  // namespace transparency
 }  // namespace pdfium
diff --git a/core/fdrm/BUILD.gn b/core/fdrm/BUILD.gn
index 888bb92..1ee3162 100644
--- a/core/fdrm/BUILD.gn
+++ b/core/fdrm/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -12,7 +12,10 @@
     "fx_crypt_aes.cpp",
     "fx_crypt_sha.cpp",
   ]
-  configs += [ "../../:pdfium_core_config" ]
+  configs += [
+    "../../:pdfium_strict_config",
+    "../../:pdfium_noshorten_config",
+  ]
   deps = [ "../fxcrt" ]
   visibility = [ "../../*" ]
 }
diff --git a/core/fdrm/fx_crypt.cpp b/core/fdrm/fx_crypt.cpp
index 7cc0bc0..c702f92 100644
--- a/core/fdrm/fx_crypt.cpp
+++ b/core/fdrm/fx_crypt.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,6 +8,8 @@
 
 #include <utility>
 
+#include "core/fxcrt/span_util.h"
+
 #define GET_UINT32(n, b, i)                            \
   {                                                    \
     (n) = (uint32_t)((uint8_t*)b)[(i)] |               \
@@ -31,7 +33,7 @@
     0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 
 void md5_process(CRYPT_md5_context* ctx, const uint8_t data[64]) {
-  uint32_t A, B, C, D, X[16];
+  uint32_t X[16];
   GET_UINT32(X[0], data, 0);
   GET_UINT32(X[1], data, 4);
   GET_UINT32(X[2], data, 8);
@@ -48,16 +50,16 @@
   GET_UINT32(X[13], data, 52);
   GET_UINT32(X[14], data, 56);
   GET_UINT32(X[15], data, 60);
+  uint32_t A = ctx->state[0];
+  uint32_t B = ctx->state[1];
+  uint32_t C = ctx->state[2];
+  uint32_t D = ctx->state[3];
 #define S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
 #define P(a, b, c, d, k, s, t)  \
   {                             \
     a += F(b, c, d) + X[k] + t; \
     a = S(a, s) + b;            \
   }
-  A = ctx->state[0];
-  B = ctx->state[1];
-  C = ctx->state[2];
-  D = ctx->state[3];
 #define F(x, y, z) (z ^ (x & (y ^ z)))
   P(A, B, C, D, 0, 7, 0xD76AA478);
   P(D, A, B, C, 1, 12, 0xE8C7B756);
@@ -142,11 +144,11 @@
                         pdfium::span<const uint8_t> key) {
   context->x = 0;
   context->y = 0;
-  for (int i = 0; i < kRC4ContextPermutationLength; ++i)
+  for (int i = 0; i < CRYPT_rc4_context::kPermutationLength; ++i)
     context->m[i] = i;
 
   int j = 0;
-  for (int i = 0; i < kRC4ContextPermutationLength; ++i) {
+  for (int i = 0; i < CRYPT_rc4_context::kPermutationLength; ++i) {
     size_t size = key.size();
     j = (j + context->m[i] + (size ? key[i % size] : 0)) & 0xFF;
     std::swap(context->m[i], context->m[j]);
@@ -193,21 +195,20 @@
   context->total[1] += data.size() >> 29;
   context->total[0] &= 0xFFFFFFFF;
   context->total[1] += context->total[0] < data.size() << 3;
+
+  const pdfium::span<uint8_t> buffer_span = pdfium::make_span(context->buffer);
   if (left && data.size() >= fill) {
-    auto next_data = data.subspan(fill);
-    memcpy(context->buffer + left, data.data(), fill);
+    fxcrt::spancpy(buffer_span.subspan(left), data.first(fill));
     md5_process(context, context->buffer);
+    data = data.subspan(fill);
     left = 0;
-    data = next_data;
   }
   while (data.size() >= 64) {
-    auto next_data = data.subspan(64);
     md5_process(context, data.data());
-    data = next_data;
+    data = data.subspan(64);
   }
-  size_t remaining = data.size();
-  if (remaining)
-    memcpy(context->buffer + left, data.data(), remaining);
+  if (!data.empty())
+    fxcrt::spancpy(buffer_span.subspan(left), data);
 }
 
 void CRYPT_MD5Finish(CRYPT_md5_context* context, uint8_t digest[16]) {
@@ -216,7 +217,7 @@
   PUT_UINT32(context->total[1], msglen, 4);
   uint32_t last = (context->total[0] >> 3) & 0x3F;
   uint32_t padn = (last < 56) ? (56 - last) : (120 - last);
-  CRYPT_MD5Update(context, {md5_padding, padn});
+  CRYPT_MD5Update(context, pdfium::make_span(md5_padding).first(padn));
   CRYPT_MD5Update(context, msglen);
   PUT_UINT32(context->state[0], digest, 0);
   PUT_UINT32(context->state[1], digest, 4);
diff --git a/core/fdrm/fx_crypt.h b/core/fdrm/fx_crypt.h
index f3a91f4..511972f 100644
--- a/core/fdrm/fx_crypt.h
+++ b/core/fdrm/fx_crypt.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,24 +7,28 @@
 #ifndef CORE_FDRM_FX_CRYPT_H_
 #define CORE_FDRM_FX_CRYPT_H_
 
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/span.h"
+#include <stdint.h>
 
-constexpr int32_t kRC4ContextPermutationLength = 256;
+#include "third_party/base/containers/span.h"
+
 struct CRYPT_rc4_context {
+  static constexpr int32_t kPermutationLength = 256;
+
   int32_t x;
   int32_t y;
-  int32_t m[kRC4ContextPermutationLength];
+  int32_t m[kPermutationLength];
 };
 
-#define MAX_NR 14
-#define MAX_NB 8
 struct CRYPT_aes_context {
+  static constexpr int kMaxNb = 8;
+  static constexpr int kMaxNr = 14;
+  static constexpr int kSchedSize = (kMaxNr + 1) * kMaxNb;
+
   int Nb;
   int Nr;
-  unsigned int keysched[(MAX_NR + 1) * MAX_NB];
-  unsigned int invkeysched[(MAX_NR + 1) * MAX_NB];
-  unsigned int iv[MAX_NB];
+  unsigned int keysched[kSchedSize];
+  unsigned int invkeysched[kSchedSize];
+  unsigned int iv[kMaxNb];
 };
 
 struct CRYPT_md5_context {
@@ -54,8 +58,7 @@
 
 void CRYPT_AESSetKey(CRYPT_aes_context* context,
                      const uint8_t* key,
-                     uint32_t keylen,
-                     bool bEncrypt);
+                     uint32_t keylen);
 void CRYPT_AESSetIV(CRYPT_aes_context* context, const uint8_t* iv);
 void CRYPT_AESDecrypt(CRYPT_aes_context* context,
                       uint8_t* dest,
diff --git a/core/fdrm/fx_crypt_aes.cpp b/core/fdrm/fx_crypt_aes.cpp
index d4e446f..3fab02f 100644
--- a/core/fdrm/fx_crypt_aes.cpp
+++ b/core/fdrm/fx_crypt_aes.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,12 +6,13 @@
 
 #include "core/fdrm/fx_crypt.h"
 
+#include <string.h>
+
+#include "core/fxcrt/fx_system.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+
 #define mulby2(x) (((x & 0x7F) << 1) ^ (x & 0x80 ? 0x1B : 0))
-#define GET_32BIT_MSB_FIRST(cp)                    \
-  (((unsigned long)(unsigned char)(cp)[3]) |       \
-   ((unsigned long)(unsigned char)(cp)[2] << 8) |  \
-   ((unsigned long)(unsigned char)(cp)[1] << 16) | \
-   ((unsigned long)(unsigned char)(cp)[0] << 24))
 #define PUT_32BIT_MSB_FIRST(cp, value) \
   do {                                 \
     (cp)[3] = (value);                 \
@@ -428,12 +429,11 @@
     0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064,
     0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0,
 };
-#define ADD_ROUND_KEY_4                                                       \
+#define ADD_ROUND_KEY_4()                                                     \
   (block[0] ^= *keysched++, block[1] ^= *keysched++, block[2] ^= *keysched++, \
    block[3] ^= *keysched++)
 #define MOVEWORD(i) (block[i] = newstate[i])
-#undef MAKEWORD
-#define MAKEWORD(i)                                         \
+#define FMAKEWORD(i)                                        \
   (newstate[i] = (E0[(block[i] >> 24) & 0xFF] ^             \
                   E1[(block[(i + C1) % Nb] >> 16) & 0xFF] ^ \
                   E2[(block[(i + C2) % Nb] >> 8) & 0xFF] ^  \
@@ -446,21 +446,24 @@
 
 void aes_encrypt_nb_4(CRYPT_aes_context* ctx, unsigned int* block) {
   int i;
-  const int C1 = 1, C2 = 2, C3 = 3, Nb = 4;
+  const int C1 = 1;
+  const int C2 = 2;
+  const int C3 = 3;
+  const int Nb = 4;
   unsigned int* keysched = ctx->keysched;
   unsigned int newstate[4];
   for (i = 0; i < ctx->Nr - 1; i++) {
-    ADD_ROUND_KEY_4;
-    MAKEWORD(0);
-    MAKEWORD(1);
-    MAKEWORD(2);
-    MAKEWORD(3);
+    ADD_ROUND_KEY_4();
+    FMAKEWORD(0);
+    FMAKEWORD(1);
+    FMAKEWORD(2);
+    FMAKEWORD(3);
     MOVEWORD(0);
     MOVEWORD(1);
     MOVEWORD(2);
     MOVEWORD(3);
   }
-  ADD_ROUND_KEY_4;
+  ADD_ROUND_KEY_4();
   LASTWORD(0);
   LASTWORD(1);
   LASTWORD(2);
@@ -469,12 +472,12 @@
   MOVEWORD(1);
   MOVEWORD(2);
   MOVEWORD(3);
-  ADD_ROUND_KEY_4;
+  ADD_ROUND_KEY_4();
 }
-#undef MAKEWORD
+#undef FMAKEWORD
 #undef LASTWORD
 
-#define MAKEWORD(i)                                         \
+#define FMAKEWORD(i)                                        \
   (newstate[i] = (D0[(block[i] >> 24) & 0xFF] ^             \
                   D1[(block[(i + C1) % Nb] >> 16) & 0xFF] ^ \
                   D2[(block[(i + C2) % Nb] >> 8) & 0xFF] ^  \
@@ -487,21 +490,24 @@
 
 void aes_decrypt_nb_4(CRYPT_aes_context* ctx, unsigned int* block) {
   int i;
-  const int C1 = 4 - 1, C2 = 4 - 2, C3 = 4 - 3, Nb = 4;
+  const int C1 = 4 - 1;
+  const int C2 = 4 - 2;
+  const int C3 = 4 - 3;
+  const int Nb = 4;
   unsigned int* keysched = ctx->invkeysched;
   unsigned int newstate[4];
   for (i = 0; i < ctx->Nr - 1; i++) {
-    ADD_ROUND_KEY_4;
-    MAKEWORD(0);
-    MAKEWORD(1);
-    MAKEWORD(2);
-    MAKEWORD(3);
+    ADD_ROUND_KEY_4();
+    FMAKEWORD(0);
+    FMAKEWORD(1);
+    FMAKEWORD(2);
+    FMAKEWORD(3);
     MOVEWORD(0);
     MOVEWORD(1);
     MOVEWORD(2);
     MOVEWORD(3);
   }
-  ADD_ROUND_KEY_4;
+  ADD_ROUND_KEY_4();
   LASTWORD(0);
   LASTWORD(1);
   LASTWORD(2);
@@ -510,39 +516,37 @@
   MOVEWORD(1);
   MOVEWORD(2);
   MOVEWORD(3);
-  ADD_ROUND_KEY_4;
+  ADD_ROUND_KEY_4();
 }
-#undef MAKEWORD
+#undef FMAKEWORD
 #undef LASTWORD
 
 void aes_setup(CRYPT_aes_context* ctx, const unsigned char* key, int keylen) {
-  ASSERT(keylen == 16 || keylen == 24 || keylen == 32);
+  DCHECK(keylen == 16 || keylen == 24 || keylen == 32);
   int Nk = keylen / 4;
   ctx->Nb = 4;
   ctx->Nr = 6 + (ctx->Nb > Nk ? ctx->Nb : Nk);
   int rconst = 1;
   for (int i = 0; i < (ctx->Nr + 1) * ctx->Nb; i++) {
     if (i < Nk) {
-      ctx->keysched[i] = GET_32BIT_MSB_FIRST(key + 4 * i);
+      ctx->keysched[i] = FXSYS_UINT32_GET_MSBFIRST(key + 4 * i);
     } else {
       unsigned int temp = ctx->keysched[i - 1];
       if (i % Nk == 0) {
-        int a, b, c, d;
-        a = (temp >> 16) & 0xFF;
-        b = (temp >> 8) & 0xFF;
-        c = (temp >> 0) & 0xFF;
-        d = (temp >> 24) & 0xFF;
+        int a = (temp >> 16) & 0xFF;
+        int b = (temp >> 8) & 0xFF;
+        int c = (temp >> 0) & 0xFF;
+        int d = (temp >> 24) & 0xFF;
         temp = Sbox[a] ^ rconst;
         temp = (temp << 8) | Sbox[b];
         temp = (temp << 8) | Sbox[c];
         temp = (temp << 8) | Sbox[d];
         rconst = mulby2(rconst);
       } else if (i % Nk == 4 && Nk > 6) {
-        int a, b, c, d;
-        a = (temp >> 24) & 0xFF;
-        b = (temp >> 16) & 0xFF;
-        c = (temp >> 8) & 0xFF;
-        d = (temp >> 0) & 0xFF;
+        int a = (temp >> 24) & 0xFF;
+        int b = (temp >> 16) & 0xFF;
+        int c = (temp >> 8) & 0xFF;
+        int d = (temp >> 0) & 0xFF;
         temp = Sbox[a];
         temp = (temp << 8) | Sbox[b];
         temp = (temp << 8) | Sbox[c];
@@ -556,11 +560,10 @@
       unsigned int temp;
       temp = ctx->keysched[(ctx->Nr - i) * ctx->Nb + j];
       if (i != 0 && i != ctx->Nr) {
-        int a, b, c, d;
-        a = (temp >> 24) & 0xFF;
-        b = (temp >> 16) & 0xFF;
-        c = (temp >> 8) & 0xFF;
-        d = (temp >> 0) & 0xFF;
+        int a = (temp >> 24) & 0xFF;
+        int b = (temp >> 16) & 0xFF;
+        int c = (temp >> 8) & 0xFF;
+        int d = (temp >> 0) & 0xFF;
         temp = D0[Sbox[a]];
         temp ^= D1[Sbox[b]];
         temp ^= D2[Sbox[c]];
@@ -579,13 +582,15 @@
                      const unsigned char* src,
                      int len,
                      CRYPT_aes_context* ctx) {
-  unsigned int iv[4], x[4], ct[4];
+  unsigned int iv[4];
+  unsigned int x[4];
+  unsigned int ct[4];
   int i;
-  ASSERT((len & 15) == 0);
+  DCHECK_EQ((len & 15), 0);
   memcpy(iv, ctx->iv, sizeof(iv));
   while (len > 0) {
     for (i = 0; i < 4; i++) {
-      x[i] = ct[i] = GET_32BIT_MSB_FIRST(src + 4 * i);
+      x[i] = ct[i] = FXSYS_UINT32_GET_MSBFIRST(src + 4 * i);
     }
     aes_decrypt(ctx, x);
     for (i = 0; i < 4; i++) {
@@ -609,11 +614,11 @@
                      CRYPT_aes_context* ctx) {
   unsigned int iv[4];
   int i;
-  ASSERT((len & 15) == 0);
+  DCHECK_EQ((len & 15), 0);
   memcpy(iv, ctx->iv, sizeof(iv));
   while (len > 0) {
     for (i = 0; i < 4; i++) {
-      iv[i] ^= GET_32BIT_MSB_FIRST(src + 4 * i);
+      iv[i] ^= FXSYS_UINT32_GET_MSBFIRST(src + 4 * i);
     }
     aes_encrypt(ctx, iv);
     for (i = 0; i < 4; i++) {
@@ -630,14 +635,13 @@
 
 void CRYPT_AESSetKey(CRYPT_aes_context* context,
                      const uint8_t* key,
-                     uint32_t keylen,
-                     bool bEncrypt) {
+                     uint32_t keylen) {
   aes_setup(context, key, keylen);
 }
 
 void CRYPT_AESSetIV(CRYPT_aes_context* context, const uint8_t* iv) {
   for (int i = 0; i < context->Nb; i++)
-    context->iv[i] = GET_32BIT_MSB_FIRST(iv + 4 * i);
+    context->iv[i] = FXSYS_UINT32_GET_MSBFIRST(iv + 4 * i);
 }
 
 void CRYPT_AESDecrypt(CRYPT_aes_context* context,
diff --git a/core/fdrm/fx_crypt_sha.cpp b/core/fdrm/fx_crypt_sha.cpp
index 0371685..b0dc8b5 100644
--- a/core/fdrm/fx_crypt_sha.cpp
+++ b/core/fdrm/fx_crypt_sha.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include "core/fdrm/fx_crypt.h"
 
+#include <string.h>
+
 #define SHA_GET_UINT32(n, b, i)                                         \
   {                                                                     \
     (n) = ((uint32_t)(b)[(i)] << 24) | ((uint32_t)(b)[(i) + 1] << 16) | \
@@ -48,12 +50,12 @@
   (SHA384_ROTR(x, 28) ^ SHA384_ROTR(x, 34) ^ SHA384_ROTR(x, 39))
 #define SHA384_S3(x) \
   (SHA384_ROTR(x, 14) ^ SHA384_ROTR(x, 18) ^ SHA384_ROTR(x, 41))
-#define SHA384_P(a, b, c, d, e, f, g, h, x, K)             \
-  {                                                        \
-    temp1 = h + SHA384_S3(e) + SHA384_F1(e, f, g) + K + x; \
-    temp2 = SHA384_S2(a) + SHA384_F0(a, b, c);             \
-    d += temp1;                                            \
-    h = temp1 + temp2;                                     \
+#define SHA384_P(a, b, c, d, e, f, g, h, x, K)                      \
+  {                                                                 \
+    uint64_t temp1 = h + SHA384_S3(e) + SHA384_F1(e, f, g) + K + x; \
+    uint64_t temp2 = SHA384_S2(a) + SHA384_F0(a, b, c);             \
+    d += temp1;                                                     \
+    h = temp1 + temp2;                                              \
   }
 #define SHA384_R(t) \
   (W[t] = SHA384_S1(W[t - 2]) + W[t - 7] + SHA384_S0(W[t - 15]) + W[t - 16])
@@ -68,12 +70,12 @@
 #define F0(x, y, z) ((x & y) | (z & (x | y)))
 #define F1(x, y, z) (z ^ (x & (y ^ z)))
 #define R(t) (W[t] = S1(W[t - 2]) + W[t - 7] + S0(W[t - 15]) + W[t - 16])
-#define PS(a, b, c, d, e, f, g, h, x, K)     \
-  {                                          \
-    temp1 = h + S3(e) + F1(e, f, g) + K + x; \
-    temp2 = S2(a) + F0(a, b, c);             \
-    d += temp1;                              \
-    h = temp1 + temp2;                       \
+#define PS(a, b, c, d, e, f, g, h, x, K)              \
+  {                                                   \
+    uint32_t temp1 = h + S3(e) + F1(e, f, g) + K + x; \
+    uint32_t temp2 = S2(a) + F0(a, b, c);             \
+    d += temp1;                                       \
+    h = temp1 + temp2;                                \
   }
 
 namespace {
@@ -88,7 +90,6 @@
 
 void SHATransform(unsigned int* digest, unsigned int* block) {
   unsigned int w[80];
-  unsigned int a, b, c, d, e;
   int t;
   for (t = 0; t < 16; t++) {
     w[t] = block[t];
@@ -97,11 +98,11 @@
     unsigned int tmp = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16];
     w[t] = rol(tmp, 1);
   }
-  a = digest[0];
-  b = digest[1];
-  c = digest[2];
-  d = digest[3];
-  e = digest[4];
+  unsigned int a = digest[0];
+  unsigned int b = digest[1];
+  unsigned int c = digest[2];
+  unsigned int d = digest[3];
+  unsigned int e = digest[4];
   for (t = 0; t < 20; t++) {
     unsigned int tmp = rol(a, 5) + ((b & c) | (d & ~b)) + e + w[t] + 0x5a827999;
     e = d;
@@ -161,16 +162,14 @@
   SHA_GET_UINT32(W[14], data, 56);
   SHA_GET_UINT32(W[15], data, 60);
 
-  uint32_t temp1;
-  uint32_t temp2;
-  uint32_t A = ctx->state[0];
-  uint32_t B = ctx->state[1];
-  uint32_t C = ctx->state[2];
-  uint32_t D = ctx->state[3];
-  uint32_t E = ctx->state[4];
-  uint32_t F = ctx->state[5];
-  uint32_t G = ctx->state[6];
-  uint32_t H = ctx->state[7];
+  uint32_t A = static_cast<uint32_t>(ctx->state[0]);
+  uint32_t B = static_cast<uint32_t>(ctx->state[1]);
+  uint32_t C = static_cast<uint32_t>(ctx->state[2]);
+  uint32_t D = static_cast<uint32_t>(ctx->state[3]);
+  uint32_t E = static_cast<uint32_t>(ctx->state[4]);
+  uint32_t F = static_cast<uint32_t>(ctx->state[5]);
+  uint32_t G = static_cast<uint32_t>(ctx->state[6]);
+  uint32_t H = static_cast<uint32_t>(ctx->state[7]);
   PS(A, B, C, D, E, F, G, H, W[0], 0x428A2F98);
   PS(H, A, B, C, D, E, F, G, W[1], 0x71374491);
   PS(G, H, A, B, C, D, E, F, W[2], 0xB5C0FBCF);
@@ -290,8 +289,6 @@
 };
 
 void sha384_process(CRYPT_sha2_context* ctx, const uint8_t data[128]) {
-  uint64_t temp1, temp2;
-  uint64_t A, B, C, D, E, F, G, H;
   uint64_t W[80];
   SHA_GET_UINT64(W[0], data, 0);
   SHA_GET_UINT64(W[1], data, 8);
@@ -309,14 +306,14 @@
   SHA_GET_UINT64(W[13], data, 104);
   SHA_GET_UINT64(W[14], data, 112);
   SHA_GET_UINT64(W[15], data, 120);
-  A = ctx->state[0];
-  B = ctx->state[1];
-  C = ctx->state[2];
-  D = ctx->state[3];
-  E = ctx->state[4];
-  F = ctx->state[5];
-  G = ctx->state[6];
-  H = ctx->state[7];
+  uint64_t A = ctx->state[0];
+  uint64_t B = ctx->state[1];
+  uint64_t C = ctx->state[2];
+  uint64_t D = ctx->state[3];
+  uint64_t E = ctx->state[4];
+  uint64_t F = ctx->state[5];
+  uint64_t G = ctx->state[6];
+  uint64_t H = ctx->state[7];
   for (int i = 0; i < 10; ++i) {
     uint64_t temp[8];
     if (i < 2) {
@@ -552,7 +549,7 @@
 
 void CRYPT_SHA384Generate(const uint8_t* data,
                           uint32_t size,
-                          uint8_t digest[64]) {
+                          uint8_t digest[48]) {
   CRYPT_sha2_context context;
   CRYPT_SHA384Start(&context);
   CRYPT_SHA384Update(&context, data, size);
diff --git a/core/fdrm/fx_crypt_unittest.cpp b/core/fdrm/fx_crypt_unittest.cpp
index de76cd0..f9dd66d 100644
--- a/core/fdrm/fx_crypt_unittest.cpp
+++ b/core/fdrm/fx_crypt_unittest.cpp
@@ -1,20 +1,21 @@
-// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fdrm/fx_crypt.h"
 
-#include <memory>
+#include <algorithm>
 #include <string>
+#include <vector>
 
-#include "core/fxcrt/fx_memory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/utils/hash.h"
 
 namespace {
 
 std::string CRYPT_MD5String(const char* str) {
-  return GenerateMD5Base16(reinterpret_cast<const uint8_t*>(str), strlen(str));
+  return GenerateMD5Base16(
+      {reinterpret_cast<const uint8_t*>(str), strlen(str)});
 }
 
 void CheckArcFourContext(const CRYPT_rc4_context& context,
@@ -23,7 +24,7 @@
                          const uint8_t* expected_permutation) {
   EXPECT_EQ(expected_x, context.x);
   EXPECT_EQ(expected_y, context.y);
-  for (int32_t i = 0; i < kRC4ContextPermutationLength; ++i)
+  for (int32_t i = 0; i < CRYPT_rc4_context::kPermutationLength; ++i)
     EXPECT_EQ(expected_permutation[i], context.m[i]) << i;
 }
 
@@ -199,7 +200,7 @@
   CRYPT_SHA1Generate(reinterpret_cast<const uint8_t*>(kInput), strlen(kInput),
                      actual);
 
-  for (size_t i = 0; i < FX_ArraySize(kExpected); i++)
+  for (size_t i = 0; i < std::size(kExpected); i++)
     EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
 }
 
@@ -214,7 +215,7 @@
   CRYPT_SHA1Generate(reinterpret_cast<const uint8_t*>(kInput), strlen(kInput),
                      actual);
 
-  for (size_t i = 0; i < FX_ArraySize(kExpected); i++)
+  for (size_t i = 0; i < std::size(kExpected); i++)
     EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
 }
 
@@ -230,7 +231,7 @@
   CRYPT_SHA1Generate(reinterpret_cast<const uint8_t*>(kInput), strlen(kInput),
                      actual);
 
-  for (size_t i = 0; i < FX_ArraySize(kExpected); i++)
+  for (size_t i = 0; i < std::size(kExpected); i++)
     EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
 }
 
@@ -243,7 +244,7 @@
   uint8_t actual[32];
   CRYPT_SHA256Generate(reinterpret_cast<const uint8_t*>(kInput), strlen(kInput),
                        actual);
-  for (size_t i = 0; i < FX_ArraySize(kExpected); ++i)
+  for (size_t i = 0; i < std::size(kExpected); ++i)
     EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
 }
 
@@ -257,7 +258,7 @@
   uint8_t actual[32];
   CRYPT_SHA256Generate(reinterpret_cast<const uint8_t*>(kInput), strlen(kInput),
                        actual);
-  for (size_t i = 0; i < FX_ArraySize(kExpected); ++i)
+  for (size_t i = 0; i < std::size(kExpected); ++i)
     EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
 }
 
@@ -272,60 +273,64 @@
   uint8_t actual[32];
   CRYPT_SHA256Generate(reinterpret_cast<const uint8_t*>(kInput), strlen(kInput),
                        actual);
-  for (size_t i = 0; i < FX_ArraySize(kExpected); ++i)
+  for (size_t i = 0; i < std::size(kExpected); ++i)
     EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
 }
 
 TEST(FXCRYPT, CRYPT_ArcFourSetup) {
   {
-    static const uint8_t kNullPermutation[kRC4ContextPermutationLength] = {
-        0,   35,  3,   43,  9,   11,  65,  229, 32,  36,  134, 98,  59,  34,
-        173, 153, 214, 200, 64,  161, 191, 62,  6,   25,  56,  234, 49,  246,
-        69,  133, 203, 194, 10,  42,  228, 198, 195, 245, 236, 91,  206, 23,
-        235, 27,  138, 18,  143, 250, 244, 76,  123, 217, 132, 249, 72,  127,
-        94,  151, 33,  60,  248, 85,  177, 210, 142, 83,  110, 140, 41,  135,
-        196, 238, 156, 242, 141, 67,  5,   185, 131, 63,  137, 37,  172, 121,
-        70,  144, 237, 130, 17,  44,  253, 166, 78,  201, 12,  119, 215, 7,
-        126, 114, 97,  192, 53,  4,   254, 45,  102, 122, 230, 88,  193, 129,
-        160, 124, 84,  108, 239, 189, 152, 120, 115, 207, 50,  176, 86,  157,
-        164, 187, 71,  1,   15,  58,  29,  21,  46,  145, 247, 162, 95,  183,
-        13,  226, 159, 175, 221, 100, 96,  202, 101, 178, 154, 47,  205, 106,
-        148, 104, 93,  112, 26,  165, 128, 186, 146, 218, 66,  211, 171, 90,
-        252, 19,  40,  99,  223, 174, 255, 51,  77,  227, 48,  220, 168, 118,
-        224, 103, 75,  105, 125, 199, 73,  82,  57,  181, 81,  149, 68,  52,
-        232, 22,  2,   216, 113, 30,  109, 163, 92,  61,  14,  8,   38,  225,
-        79,  231, 170, 240, 20,  219, 204, 150, 180, 188, 116, 190, 241, 197,
-        179, 87,  74,  147, 80,  54,  212, 16,  167, 222, 136, 213, 55,  182,
-        139, 24,  209, 251, 208, 28,  111, 89,  158, 155, 243, 107, 233, 169,
-        117, 184, 31,  39};
+    static const uint8_t
+        kNullPermutation[CRYPT_rc4_context::kPermutationLength] = {
+            0,   35,  3,   43,  9,   11,  65,  229, 32,  36,  134, 98,  59,
+            34,  173, 153, 214, 200, 64,  161, 191, 62,  6,   25,  56,  234,
+            49,  246, 69,  133, 203, 194, 10,  42,  228, 198, 195, 245, 236,
+            91,  206, 23,  235, 27,  138, 18,  143, 250, 244, 76,  123, 217,
+            132, 249, 72,  127, 94,  151, 33,  60,  248, 85,  177, 210, 142,
+            83,  110, 140, 41,  135, 196, 238, 156, 242, 141, 67,  5,   185,
+            131, 63,  137, 37,  172, 121, 70,  144, 237, 130, 17,  44,  253,
+            166, 78,  201, 12,  119, 215, 7,   126, 114, 97,  192, 53,  4,
+            254, 45,  102, 122, 230, 88,  193, 129, 160, 124, 84,  108, 239,
+            189, 152, 120, 115, 207, 50,  176, 86,  157, 164, 187, 71,  1,
+            15,  58,  29,  21,  46,  145, 247, 162, 95,  183, 13,  226, 159,
+            175, 221, 100, 96,  202, 101, 178, 154, 47,  205, 106, 148, 104,
+            93,  112, 26,  165, 128, 186, 146, 218, 66,  211, 171, 90,  252,
+            19,  40,  99,  223, 174, 255, 51,  77,  227, 48,  220, 168, 118,
+            224, 103, 75,  105, 125, 199, 73,  82,  57,  181, 81,  149, 68,
+            52,  232, 22,  2,   216, 113, 30,  109, 163, 92,  61,  14,  8,
+            38,  225, 79,  231, 170, 240, 20,  219, 204, 150, 180, 188, 116,
+            190, 241, 197, 179, 87,  74,  147, 80,  54,  212, 16,  167, 222,
+            136, 213, 55,  182, 139, 24,  209, 251, 208, 28,  111, 89,  158,
+            155, 243, 107, 233, 169, 117, 184, 31,  39};
     CRYPT_rc4_context context;
     CRYPT_ArcFourSetup(&context, {});
     CheckArcFourContext(context, 0, 0, kNullPermutation);
   }
   {
-    static const uint8_t kFoobarPermutation[kRC4ContextPermutationLength] = {
-        102, 214, 39,  49,  17,  132, 244, 106, 114, 76,  183, 212, 116, 73,
-        42,  103, 128, 246, 139, 199, 31,  234, 25,  109, 48,  19,  121, 4,
-        20,  54,  134, 77,  163, 38,  61,  101, 145, 78,  215, 96,  92,  80,
-        224, 168, 243, 210, 82,  252, 113, 56,  217, 62,  218, 129, 125, 33,
-        99,  9,   153, 59,  43,  13,  206, 124, 131, 18,  213, 118, 173, 122,
-        193, 172, 177, 105, 148, 207, 186, 5,   85,  32,  68,  220, 79,  84,
-        169, 209, 150, 7,   133, 63,  147, 93,  26,  130, 60,  117, 250, 57,
-        24,  247, 200, 127, 136, 66,  112, 107, 140, 154, 70,  170, 185, 138,
-        248, 236, 88,  86,  44,  216, 241, 35,  100, 151, 156, 74,  119, 55,
-        245, 46,  227, 208, 229, 16,  249, 149, 53,  157, 201, 75,  58,  28,
-        142, 238, 182, 180, 179, 144, 12,  6,   176, 10,  90,  239, 104, 40,
-        181, 194, 137, 69,  221, 205, 165, 188, 191, 87,  1,   91,  2,   171,
-        232, 34,  162, 166, 160, 126, 225, 167, 123, 197, 223, 195, 22,  203,
-        189, 237, 37,  27,  222, 175, 23,  143, 152, 192, 21,  231, 228, 141,
-        30,  204, 158, 240, 120, 98,  89,  83,  135, 251, 81,  196, 161, 3,
-        8,   230, 52,  219, 41,  242, 36,  97,  15,  155, 65,  187, 254, 64,
-        159, 67,  211, 108, 178, 146, 202, 11,  164, 226, 184, 50,  190, 174,
-        71,  233, 235, 198, 95,  51,  110, 255, 253, 72,  115, 0,   47,  94,
-        29,  45,  14,  111};
+    static const uint8_t
+        kFoobarPermutation[CRYPT_rc4_context::kPermutationLength] = {
+            102, 214, 39,  49,  17,  132, 244, 106, 114, 76,  183, 212, 116,
+            73,  42,  103, 128, 246, 139, 199, 31,  234, 25,  109, 48,  19,
+            121, 4,   20,  54,  134, 77,  163, 38,  61,  101, 145, 78,  215,
+            96,  92,  80,  224, 168, 243, 210, 82,  252, 113, 56,  217, 62,
+            218, 129, 125, 33,  99,  9,   153, 59,  43,  13,  206, 124, 131,
+            18,  213, 118, 173, 122, 193, 172, 177, 105, 148, 207, 186, 5,
+            85,  32,  68,  220, 79,  84,  169, 209, 150, 7,   133, 63,  147,
+            93,  26,  130, 60,  117, 250, 57,  24,  247, 200, 127, 136, 66,
+            112, 107, 140, 154, 70,  170, 185, 138, 248, 236, 88,  86,  44,
+            216, 241, 35,  100, 151, 156, 74,  119, 55,  245, 46,  227, 208,
+            229, 16,  249, 149, 53,  157, 201, 75,  58,  28,  142, 238, 182,
+            180, 179, 144, 12,  6,   176, 10,  90,  239, 104, 40,  181, 194,
+            137, 69,  221, 205, 165, 188, 191, 87,  1,   91,  2,   171, 232,
+            34,  162, 166, 160, 126, 225, 167, 123, 197, 223, 195, 22,  203,
+            189, 237, 37,  27,  222, 175, 23,  143, 152, 192, 21,  231, 228,
+            141, 30,  204, 158, 240, 120, 98,  89,  83,  135, 251, 81,  196,
+            161, 3,   8,   230, 52,  219, 41,  242, 36,  97,  15,  155, 65,
+            187, 254, 64,  159, 67,  211, 108, 178, 146, 202, 11,  164, 226,
+            184, 50,  190, 174, 71,  233, 235, 198, 95,  51,  110, 255, 253,
+            72,  115, 0,   47,  94,  29,  45,  14,  111};
     CRYPT_rc4_context context;
     static const uint8_t kFooBar[] = "foobar";
-    CRYPT_ArcFourSetup(&context, {kFooBar, FX_ArraySize(kFooBar) - 1});
+    CRYPT_ArcFourSetup(&context, {kFooBar, std::size(kFooBar) - 1});
     CheckArcFourContext(context, 0, 0, kFoobarPermutation);
   }
 }
@@ -343,21 +348,21 @@
     CRYPT_rc4_context context;
     CRYPT_ArcFourSetup(&context, {});
 
-    uint8_t data_short[FX_ArraySize(kDataShort)];
-    memcpy(data_short, kDataShort, FX_ArraySize(kDataShort));
+    uint8_t data_short[std::size(kDataShort)];
+    memcpy(data_short, kDataShort, std::size(kDataShort));
     static const uint8_t kExpectedEncryptedDataShort[] = {
         138, 112, 236, 97,  242, 66,  52,  89,  225, 38,  88,  8,
         47,  78,  216, 24,  170, 106, 26,  199, 208, 131, 157, 242,
         55,  11,  25,  90,  66,  182, 19,  255, 210, 181, 85,  69,
         31,  240, 206, 171, 97,  62,  202, 172, 30,  252};
     static_assert(
-        FX_ArraySize(kExpectedEncryptedDataShort) == FX_ArraySize(data_short),
+        std::size(kExpectedEncryptedDataShort) == std::size(data_short),
         "data_short mismatch");
     CRYPT_ArcFourCrypt(&context, data_short);
-    for (size_t i = 0; i < FX_ArraySize(data_short); ++i)
+    for (size_t i = 0; i < std::size(data_short); ++i)
       EXPECT_EQ(kExpectedEncryptedDataShort[i], data_short[i]) << i;
 
-    static const uint8_t kPermutation[kRC4ContextPermutationLength] = {
+    static const uint8_t kPermutation[CRYPT_rc4_context::kPermutationLength] = {
         0,   198, 10,  37,  253, 192, 171, 183, 99,  8,   144, 103, 208, 191,
         149, 9,   228, 243, 94,  150, 169, 151, 210, 206, 221, 235, 32,  186,
         212, 122, 72,  200, 236, 138, 244, 217, 158, 213, 139, 242, 17,  143,
@@ -383,8 +388,8 @@
     CRYPT_rc4_context context;
     CRYPT_ArcFourSetup(&context, {});
 
-    uint8_t data_long[FX_ArraySize(kDataLong)];
-    memcpy(data_long, kDataLong, FX_ArraySize(kDataLong));
+    uint8_t data_long[std::size(kDataLong)];
+    memcpy(data_long, kDataLong, std::size(kDataLong));
     static const uint8_t kExpectedEncryptedDataLong[] = {
         138, 112, 236, 97,  242, 66,  52,  89,  225, 38,  88,  8,   47,  78,
         216, 24,  170, 106, 26,  199, 208, 131, 157, 242, 55,  11,  25,  90,
@@ -406,15 +411,14 @@
         208, 161, 105, 226, 164, 114, 80,  137, 58,  107, 109, 42,  110, 100,
         202, 170, 224, 89,  28,  5,   138, 19,  253, 105, 220, 105, 24,  187,
         109, 89,  205, 89,  202};
-    static_assert(
-        FX_ArraySize(kExpectedEncryptedDataLong) == FX_ArraySize(data_long),
-        "data_long mismatch");
-    static_assert(FX_ArraySize(data_long) > 256, "too short");
+    static_assert(std::size(kExpectedEncryptedDataLong) == std::size(data_long),
+                  "data_long mismatch");
+    static_assert(std::size(data_long) > 256, "too short");
     CRYPT_ArcFourCrypt(&context, data_long);
-    for (size_t i = 0; i < FX_ArraySize(data_long); ++i)
+    for (size_t i = 0; i < std::size(data_long); ++i)
       EXPECT_EQ(kExpectedEncryptedDataLong[i], data_long[i]) << i;
 
-    static const uint8_t kPermutation[kRC4ContextPermutationLength] = {
+    static const uint8_t kPermutation[CRYPT_rc4_context::kPermutationLength] = {
         172, 59,  196, 72,  101, 21,  215, 210, 212, 52,  243, 73,  47,  213,
         211, 50,  228, 144, 66,  93,  169, 31,  237, 206, 221, 235, 222, 250,
         97,  87,  174, 164, 190, 111, 27,  217, 173, 189, 65,  11,  115, 171,
@@ -439,23 +443,23 @@
   {
     CRYPT_rc4_context context;
     static const uint8_t kFooBar[] = "foobar";
-    CRYPT_ArcFourSetup(&context, {kFooBar, FX_ArraySize(kFooBar) - 1});
+    CRYPT_ArcFourSetup(&context, {kFooBar, std::size(kFooBar) - 1});
 
-    uint8_t data_short[FX_ArraySize(kDataShort)];
-    memcpy(data_short, kDataShort, FX_ArraySize(kDataShort));
+    uint8_t data_short[std::size(kDataShort)];
+    memcpy(data_short, kDataShort, std::size(kDataShort));
     static const uint8_t kExpectedEncryptedDataShort[] = {
         59,  193, 117, 206, 167, 54,  218, 7,   229, 214, 188, 55,
         90,  205, 196, 25,  36,  114, 199, 218, 161, 107, 122, 119,
         106, 167, 44,  175, 240, 123, 192, 102, 174, 167, 105, 187,
         202, 70,  121, 81,  17,  30,  5,   138, 116, 166};
     static_assert(
-        FX_ArraySize(kExpectedEncryptedDataShort) == FX_ArraySize(data_short),
+        std::size(kExpectedEncryptedDataShort) == std::size(data_short),
         "data_short mismatch");
     CRYPT_ArcFourCrypt(&context, data_short);
-    for (size_t i = 0; i < FX_ArraySize(data_short); ++i)
+    for (size_t i = 0; i < std::size(data_short); ++i)
       EXPECT_EQ(kExpectedEncryptedDataShort[i], data_short[i]) << i;
 
-    static const uint8_t kPermutation[kRC4ContextPermutationLength] = {
+    static const uint8_t kPermutation[CRYPT_rc4_context::kPermutationLength] = {
         102, 41,  45,  82,  124, 141, 237, 38,  6,   64,  90,  140, 254, 96,
         220, 109, 99,  49,  27,  227, 205, 75,  191, 37,  17,  54,  83,  196,
         108, 79,  31,  190, 180, 0,   125, 194, 243, 156, 224, 246, 253, 193,
@@ -480,10 +484,10 @@
   {
     CRYPT_rc4_context context;
     static const uint8_t kFooBar[] = "foobar";
-    CRYPT_ArcFourSetup(&context, {kFooBar, FX_ArraySize(kFooBar) - 1});
+    CRYPT_ArcFourSetup(&context, {kFooBar, std::size(kFooBar) - 1});
 
-    uint8_t data_long[FX_ArraySize(kDataLong)];
-    memcpy(data_long, kDataLong, FX_ArraySize(kDataLong));
+    uint8_t data_long[std::size(kDataLong)];
+    memcpy(data_long, kDataLong, std::size(kDataLong));
     static const uint8_t kExpectedEncryptedDataLong[] = {
         59,  193, 117, 206, 167, 54,  218, 7,   229, 214, 188, 55,  90,  205,
         196, 25,  36,  114, 199, 218, 161, 107, 122, 119, 106, 167, 44,  175,
@@ -505,15 +509,14 @@
         22,  110, 43,  56,  94,  127, 48,  96,  47,  172, 3,   31,  130, 249,
         243, 73,  206, 89,  9,   93,  156, 167, 205, 166, 75,  227, 36,  34,
         81,  124, 195, 246, 152};
-    static_assert(
-        FX_ArraySize(kExpectedEncryptedDataLong) == FX_ArraySize(data_long),
-        "data_long mismatch");
-    static_assert(FX_ArraySize(data_long) > 256, "too short");
+    static_assert(std::size(kExpectedEncryptedDataLong) == std::size(data_long),
+                  "data_long mismatch");
+    static_assert(std::size(data_long) > 256, "too short");
     CRYPT_ArcFourCrypt(&context, data_long);
-    for (size_t i = 0; i < FX_ArraySize(data_long); ++i)
+    for (size_t i = 0; i < std::size(data_long); ++i)
       EXPECT_EQ(kExpectedEncryptedDataLong[i], data_long[i]) << i;
 
-    static const uint8_t kPermutation[kRC4ContextPermutationLength] = {
+    static const uint8_t kPermutation[CRYPT_rc4_context::kPermutationLength] = {
         188, 12,  81,  130, 228, 58,  124, 218, 72,  210, 50,  70,  166, 38,
         110, 111, 73,  49,  27,  227, 249, 21,  1,   226, 17,  54,  53,  16,
         108, 51,  31,  123, 221, 23,  125, 148, 5,   200, 208, 246, 253, 193,
@@ -547,7 +550,7 @@
   uint8_t actual[48];
   CRYPT_SHA384Generate(reinterpret_cast<const uint8_t*>(kInput), strlen(kInput),
                        actual);
-  for (size_t i = 0; i < FX_ArraySize(kExpected); ++i)
+  for (size_t i = 0; i < std::size(kExpected); ++i)
     EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
 }
 
@@ -563,7 +566,7 @@
   uint8_t actual[48];
   CRYPT_SHA384Generate(reinterpret_cast<const uint8_t*>(kInput), strlen(kInput),
                        actual);
-  for (size_t i = 0; i < FX_ArraySize(kExpected); ++i)
+  for (size_t i = 0; i < std::size(kExpected); ++i)
     EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
 }
 
@@ -581,7 +584,7 @@
   EXPECT_EQ(112u, strlen(kInput));
   CRYPT_SHA384Generate(reinterpret_cast<const uint8_t*>(kInput), strlen(kInput),
                        actual);
-  for (size_t i = 0; i < FX_ArraySize(kExpected); ++i)
+  for (size_t i = 0; i < std::size(kExpected); ++i)
     EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
 }
 
@@ -597,7 +600,7 @@
   uint8_t actual[64];
   CRYPT_SHA512Generate(reinterpret_cast<const uint8_t*>(kInput), strlen(kInput),
                        actual);
-  for (size_t i = 0; i < FX_ArraySize(kExpected); ++i)
+  for (size_t i = 0; i < std::size(kExpected); ++i)
     EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
 }
 
@@ -615,7 +618,7 @@
   uint8_t actual[64];
   CRYPT_SHA512Generate(reinterpret_cast<const uint8_t*>(kInput), strlen(kInput),
                        actual);
-  for (size_t i = 0; i < FX_ArraySize(kExpected); ++i)
+  for (size_t i = 0; i < std::size(kExpected); ++i)
     EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
 }
 
@@ -635,6 +638,6 @@
   EXPECT_EQ(112u, strlen(kInput));
   CRYPT_SHA512Generate(reinterpret_cast<const uint8_t*>(kInput), strlen(kInput),
                        actual);
-  for (size_t i = 0; i < FX_ArraySize(kExpected); ++i)
+  for (size_t i = 0; i < std::size(kExpected); ++i)
     EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
 }
diff --git a/core/fpdfapi/cmaps/BUILD.gn b/core/fpdfapi/cmaps/BUILD.gn
index e7fddbf..23079c8 100644
--- a/core/fpdfapi/cmaps/BUILD.gn
+++ b/core/fpdfapi/cmaps/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -70,7 +70,10 @@
     "fpdf_cmaps.cpp",
     "fpdf_cmaps.h",
   ]
-  configs += [ "../../../:pdfium_core_config" ]
+  configs += [
+    "../../../:pdfium_strict_config",
+    "../../../:pdfium_noshorten_config",
+  ]
   deps = [ "../../fxcrt" ]
   visibility = [ "../../../*" ]
 }
diff --git a/core/fpdfapi/cmaps/CNS1/Adobe-CNS1-UCS2_5.cpp b/core/fpdfapi/cmaps/CNS1/Adobe-CNS1-UCS2_5.cpp
index c904239..154233b 100644
--- a/core/fpdfapi/cmaps/CNS1/Adobe-CNS1-UCS2_5.cpp
+++ b/core/fpdfapi/cmaps/CNS1/Adobe-CNS1-UCS2_5.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 
-const uint16_t g_FXCMAP_CNS1CID2Unicode_5[19088] = {
+namespace fxcmap {
+
+const uint16_t kCNS1CID2Unicode_5[19088] = {
     0xFFFD, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
     0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030,
     0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039,
@@ -2129,3 +2131,5 @@
     0x0000, 0x0000, 0x456D, 0x38D4, 0x0000, 0x4561, 0x451B, 0x4D89, 0x4C7B,
     0x4D76, 0x45EA, 0x3FC8, 0x0000, 0x3661, 0x44DE, 0x44BD, 0x41ED,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/CNS1/B5pc-H_0.cpp b/core/fpdfapi/cmaps/CNS1/B5pc-H_0.cpp
index 0c96065..d24620c 100644
--- a/core/fpdfapi/cmaps/CNS1/B5pc-H_0.cpp
+++ b/core/fpdfapi/cmaps/CNS1/B5pc-H_0.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 
-const uint16_t g_FXCMAP_B5pc_H_0[247 * 3] = {
+namespace fxcmap {
+
+const uint16_t kB5pc_H_0[247 * 3] = {
     0x0020, 0x007E, 0x0001, 0x0080, 0x0080, 0x003D, 0x00FD, 0x00FF, 0x0060,
     0xA140, 0xA158, 0x0063, 0xA159, 0xA15C, 0x35AF, 0xA15D, 0xA17E, 0x0080,
     0xA1A1, 0xA1F5, 0x00A2, 0xA1F6, 0xA1F6, 0x00F8, 0xA1F7, 0xA1F7, 0x00F7,
@@ -91,3 +93,5 @@
     0xF9C5, 0xF9C5, 0x353D, 0xF9C6, 0xF9C6, 0x3549, 0xF9C7, 0xF9D1, 0x353E,
     0xF9D2, 0xF9D5, 0x354A,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/CNS1/B5pc-V_0.cpp b/core/fpdfapi/cmaps/CNS1/B5pc-V_0.cpp
index 88768d6..b7a17a5 100644
--- a/core/fpdfapi/cmaps/CNS1/B5pc-V_0.cpp
+++ b/core/fpdfapi/cmaps/CNS1/B5pc-V_0.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,13 @@
 
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 
-const uint16_t g_FXCMAP_B5pc_V_0[12 * 3] = {
+namespace fxcmap {
+
+const uint16_t kB5pc_V_0[12 * 3] = {
     0xA14B, 0xA14B, 0x354E, 0xA15A, 0xA15A, 0x35AF, 0xA15C, 0xA15C, 0x35B1,
     0xA15D, 0xA15E, 0x0082, 0xA161, 0xA162, 0x0086, 0xA165, 0xA166, 0x008A,
     0xA169, 0xA16A, 0x008E, 0xA16D, 0xA16E, 0x0092, 0xA171, 0xA172, 0x0096,
     0xA175, 0xA176, 0x009A, 0xA179, 0xA17A, 0x009E, 0xA1E3, 0xA1E3, 0x354F,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/CNS1/CNS-EUC-H_0.cpp b/core/fpdfapi/cmaps/CNS1/CNS-EUC-H_0.cpp
index 9d0cee8..cfa4eac 100644
--- a/core/fpdfapi/cmaps/CNS1/CNS-EUC-H_0.cpp
+++ b/core/fpdfapi/cmaps/CNS1/CNS-EUC-H_0.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 
-const uint16_t g_FXCMAP_CNS_EUC_H_0[157 * 3] = {
+namespace fxcmap {
+
+const uint16_t kCNS_EUC_H_0[157 * 3] = {
     0x0020, 0x007E, 0x3550, 0xA1A1, 0xA1FE, 0x0063, 0xA2A1, 0xA2FE, 0x00C1,
     0xA3A1, 0xA3CE, 0x011F, 0xA4A1, 0xA4FE, 0x014D, 0xA5A1, 0xA5EC, 0x01AB,
     0xA5EE, 0xA5F0, 0x01F7, 0xA6A1, 0xA6BE, 0x01FA, 0xA7A1, 0xA7A1, 0x0253,
@@ -62,7 +64,7 @@
     0xFDA1, 0xFDCB, 0x1741,
 };
 
-const FXCMAP_DWordCIDMap g_FXCMAP_CNS_EUC_H_0_DWord[238] = {
+const DWordCIDMap kCNS_EUC_H_0_DWord[238] = {
     {0x8EA1, 0xA1A1, 0xA1FE, 0x0063}, {0x8EA1, 0xA2A1, 0xA2FE, 0x00C1},
     {0x8EA1, 0xA3A1, 0xA3CE, 0x011F}, {0x8EA1, 0xA4A1, 0xA4FE, 0x014D},
     {0x8EA1, 0xA5A1, 0xA5EC, 0x01AB}, {0x8EA1, 0xA5EE, 0xA5F0, 0x01F7},
@@ -183,3 +185,5 @@
     {0x8EA2, 0xEFA1, 0xEFFE, 0x3410}, {0x8EA2, 0xF0A1, 0xF0FE, 0x346E},
     {0x8EA2, 0xF1A1, 0xF1FE, 0x34CC}, {0x8EA2, 0xF2A1, 0xF2C4, 0x352A},
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/CNS1/CNS-EUC-V_0.cpp b/core/fpdfapi/cmaps/CNS1/CNS-EUC-V_0.cpp
index 1698106..97834d9 100644
--- a/core/fpdfapi/cmaps/CNS1/CNS-EUC-V_0.cpp
+++ b/core/fpdfapi/cmaps/CNS1/CNS-EUC-V_0.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 
-const uint16_t g_FXCMAP_CNS_EUC_V_0[180 * 3] = {
+namespace fxcmap {
+
+const uint16_t kCNS_EUC_V_0[180 * 3] = {
     0x0020, 0x007E, 0x3550, 0xA1A1, 0xA1AB, 0x0063, 0xA1AC, 0xA1AC, 0x354E,
     0xA1AD, 0xA1BA, 0x006F, 0xA1BB, 0xA1BB, 0x007C, 0xA1BC, 0xA1BC, 0x007E,
     0xA1BD, 0xA1BD, 0x007E, 0xA1BE, 0xA1BF, 0x0082, 0xA1C0, 0xA1C1, 0x0082,
@@ -69,7 +71,7 @@
     0xFBA1, 0xFBFE, 0x1685, 0xFCA1, 0xFCFE, 0x16E3, 0xFDA1, 0xFDCB, 0x1741,
 };
 
-const FXCMAP_DWordCIDMap g_FXCMAP_CNS_EUC_V_0_DWord[261] = {
+const DWordCIDMap kCNS_EUC_V_0_DWord[261] = {
     {0x8EA1, 0xA1A1, 0xA1AB, 0x0063}, {0x8EA1, 0xA1AC, 0xA1AC, 0x354E},
     {0x8EA1, 0xA1AD, 0xA1BA, 0x006F}, {0x8EA1, 0xA1BB, 0xA1BB, 0x007C},
     {0x8EA1, 0xA1BC, 0xA1BC, 0x007E}, {0x8EA1, 0xA1BD, 0xA1BD, 0x007E},
@@ -202,3 +204,5 @@
     {0x8EA2, 0xF0A1, 0xF0FE, 0x346E}, {0x8EA2, 0xF1A1, 0xF1FE, 0x34CC},
     {0x8EA2, 0xF2A1, 0xF2C4, 0x352A},
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/CNS1/ETen-B5-H_0.cpp b/core/fpdfapi/cmaps/CNS1/ETen-B5-H_0.cpp
index 3138ae9..44687c1 100644
--- a/core/fpdfapi/cmaps/CNS1/ETen-B5-H_0.cpp
+++ b/core/fpdfapi/cmaps/CNS1/ETen-B5-H_0.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 
-const uint16_t g_FXCMAP_ETen_B5_H_0[254 * 3] = {
+namespace fxcmap {
+
+const uint16_t kETen_B5_H_0[254 * 3] = {
     0x0020, 0x007E, 0x3550, 0xA140, 0xA158, 0x0063, 0xA159, 0xA15C, 0x35AF,
     0xA15D, 0xA17E, 0x0080, 0xA1A1, 0xA1F5, 0x00A2, 0xA1F6, 0xA1F6, 0x00F8,
     0xA1F7, 0xA1F7, 0x00F7, 0xA1F8, 0xA1FE, 0x00F9, 0xA240, 0xA27E, 0x0100,
@@ -93,3 +95,5 @@
     0xF9C5, 0xF9C5, 0x353D, 0xF9C6, 0xF9C6, 0x3549, 0xF9C7, 0xF9D1, 0x353E,
     0xF9D2, 0xF9D5, 0x354A, 0xF9D6, 0xF9FE, 0x36E8,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/CNS1/ETen-B5-V_0.cpp b/core/fpdfapi/cmaps/CNS1/ETen-B5-V_0.cpp
index 622f2ff..38359e8 100644
--- a/core/fpdfapi/cmaps/CNS1/ETen-B5-V_0.cpp
+++ b/core/fpdfapi/cmaps/CNS1/ETen-B5-V_0.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,14 @@
 
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 
-const uint16_t g_FXCMAP_ETen_B5_V_0[13 * 3] = {
+namespace fxcmap {
+
+const uint16_t kETen_B5_V_0[13 * 3] = {
     0xA14B, 0xA14B, 0x354E, 0xA15A, 0xA15A, 0x35AF, 0xA15C, 0xA15C,
     0x35B1, 0xA15D, 0xA15E, 0x0082, 0xA161, 0xA162, 0x0086, 0xA165,
     0xA166, 0x008A, 0xA169, 0xA16A, 0x008E, 0xA16D, 0xA16E, 0x0092,
     0xA171, 0xA172, 0x0096, 0xA175, 0xA176, 0x009A, 0xA179, 0xA17A,
     0x009E, 0xA1E3, 0xA1E3, 0x354F, 0xC6E4, 0xC6E5, 0x3711,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/CNS1/ETenms-B5-H_0.cpp b/core/fpdfapi/cmaps/CNS1/ETenms-B5-H_0.cpp
index 3cc5573..0f44566 100644
--- a/core/fpdfapi/cmaps/CNS1/ETenms-B5-H_0.cpp
+++ b/core/fpdfapi/cmaps/CNS1/ETenms-B5-H_0.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,12 @@
 
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 
-const uint16_t g_FXCMAP_ETenms_B5_H_0[1 * 3] = {
+namespace fxcmap {
+
+const uint16_t kETenms_B5_H_0[1 * 3] = {
     0x0020,
     0x007E,
     0x0001,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/CNS1/ETenms-B5-V_0.cpp b/core/fpdfapi/cmaps/CNS1/ETenms-B5-V_0.cpp
index de92249..5aafd16 100644
--- a/core/fpdfapi/cmaps/CNS1/ETenms-B5-V_0.cpp
+++ b/core/fpdfapi/cmaps/CNS1/ETenms-B5-V_0.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 
-const uint16_t g_FXCMAP_ETenms_B5_V_0[18 * 3] = {
+namespace fxcmap {
+
+const uint16_t kETenms_B5_V_0[18 * 3] = {
     0xA14B, 0xA14B, 0x354E, 0xA14C, 0xA14C, 0x006D, 0xA156, 0xA156, 0x0138,
     0xA158, 0xA158, 0x007A, 0xA15A, 0xA15A, 0x35AF, 0xA15C, 0xA15C, 0x35B1,
     0xA15D, 0xA15E, 0x0082, 0xA161, 0xA162, 0x0086, 0xA165, 0xA166, 0x008A,
@@ -14,3 +16,5 @@
     0xA175, 0xA176, 0x009A, 0xA179, 0xA17A, 0x009E, 0xA17D, 0xA17E, 0x0082,
     0xA1A1, 0xA1A2, 0x0086, 0xA1A3, 0xA1A4, 0x008A, 0xC6E4, 0xC6E5, 0x3711,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/CNS1/HKscs-B5-H_5.cpp b/core/fpdfapi/cmaps/CNS1/HKscs-B5-H_5.cpp
index 47f7d6a..e073f3e 100644
--- a/core/fpdfapi/cmaps/CNS1/HKscs-B5-H_5.cpp
+++ b/core/fpdfapi/cmaps/CNS1/HKscs-B5-H_5.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 
-const uint16_t g_FXCMAP_HKscs_B5_H_5[1210 * 3] = {
+namespace fxcmap {
+
+const uint16_t kHKscs_B5_H_5[1210 * 3] = {
     0x0020, 0x007E, 0x0001, 0x8740, 0x8765, 0x4A15, 0x8767, 0x8779, 0x4A3B,
     0x8840, 0x8855, 0x44C9, 0x8856, 0x887E, 0x4961, 0x88A1, 0x88A8, 0x498A,
     0x88A9, 0x88AA, 0x499C, 0x8940, 0x8941, 0x4534, 0x8943, 0x8943, 0x4536,
@@ -412,3 +414,5 @@
     0xFEDE, 0xFEDF, 0x495D, 0xFEE0, 0xFEEC, 0x42EB, 0xFEED, 0xFEEE, 0x495F,
     0xFEEF, 0xFEFE, 0x42F8,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/CNS1/HKscs-B5-V_5.cpp b/core/fpdfapi/cmaps/CNS1/HKscs-B5-V_5.cpp
index a833497..ab6418c 100644
--- a/core/fpdfapi/cmaps/CNS1/HKscs-B5-V_5.cpp
+++ b/core/fpdfapi/cmaps/CNS1/HKscs-B5-V_5.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,14 @@
 
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 
-const uint16_t g_FXCMAP_HKscs_B5_V_5[13 * 3] = {
+namespace fxcmap {
+
+const uint16_t kHKscs_B5_V_5[13 * 3] = {
     0xA14B, 0xA14B, 0x354E, 0xA15A, 0xA15A, 0x35AF, 0xA15C, 0xA15C,
     0x35B1, 0xA15D, 0xA15E, 0x0082, 0xA161, 0xA162, 0x0086, 0xA165,
     0xA166, 0x008A, 0xA169, 0xA16A, 0x008E, 0xA16D, 0xA16E, 0x0092,
     0xA171, 0xA172, 0x0096, 0xA175, 0xA176, 0x009A, 0xA179, 0xA17A,
     0x009E, 0xA1E3, 0xA1E3, 0x354F, 0xC6E4, 0xC6E5, 0x3711,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-H_3.cpp b/core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-H_3.cpp
index bfe6a34..fb13a37 100644
--- a/core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-H_3.cpp
+++ b/core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-H_3.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 
-const uint16_t g_FXCMAP_UniCNS_UCS2_H_3[16418 * 3] = {
+namespace fxcmap {
+
+const uint16_t kUniCNS_UCS2_H_3[16418 * 3] = {
     0x0020, 0x007E, 0x0001, 0x00A2, 0x00A3, 0x0106, 0x00A5, 0x00A5, 0x0104,
     0x00A7, 0x00A7, 0x00B2, 0x00A8, 0x00A8, 0x35B3, 0x00AC, 0x00AC, 0x36E1,
     0x00B0, 0x00B0, 0x0118, 0x00B1, 0x00B1, 0x00D4, 0x00B7, 0x00B7, 0x0073,
@@ -5481,3 +5483,5 @@
     0xFF5C, 0xFF5C, 0x0078, 0xFF5D, 0xFF5D, 0x0085, 0xFF64, 0xFF64, 0x0071,
     0xFFE2, 0xFFE2, 0x36E1, 0xFFE4, 0xFFE4, 0x36E2,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-V_3.cpp b/core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-V_3.cpp
index dfa7b87..36cb2a6 100644
--- a/core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-V_3.cpp
+++ b/core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-V_3.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,14 @@
 
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 
-const uint16_t g_FXCMAP_UniCNS_UCS2_V_3[13 * 3] = {
+namespace fxcmap {
+
+const uint16_t kUniCNS_UCS2_V_3[13 * 3] = {
     0x2013, 0x2013, 0x0078, 0x2014, 0x2014, 0x007A, 0x2025, 0x2025,
     0x006D, 0x3008, 0x3009, 0x0096, 0x300A, 0x300B, 0x0092, 0x300C,
     0x300D, 0x009A, 0x300E, 0x300F, 0x009E, 0x3010, 0x3011, 0x008E,
     0x3014, 0x3015, 0x008A, 0xFE4F, 0xFE4F, 0x35B1, 0xFF08, 0xFF09,
     0x0082, 0xFF5B, 0xFF5B, 0x0086, 0xFF5D, 0xFF5D, 0x0087,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/CNS1/UniCNS-UTF16-H_0.cpp b/core/fpdfapi/cmaps/CNS1/UniCNS-UTF16-H_0.cpp
index a033c13..05fc715 100644
--- a/core/fpdfapi/cmaps/CNS1/UniCNS-UTF16-H_0.cpp
+++ b/core/fpdfapi/cmaps/CNS1/UniCNS-UTF16-H_0.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 
-const uint16_t g_FXCMAP_UniCNS_UTF16_H_0[14557 * 2] = {
+namespace fxcmap {
+
+const uint16_t kUniCNS_UTF16_H_0[14557 * 2] = {
     0x0020, 0x0001, 0x0021, 0x0002, 0x0022, 0x0003, 0x0023, 0x0004, 0x0024,
     0x0005, 0x0025, 0x0006, 0x0026, 0x0007, 0x0027, 0x0008, 0x0028, 0x0009,
     0x0029, 0x000A, 0x002A, 0x000B, 0x002B, 0x000C, 0x002C, 0x000D, 0x002D,
@@ -3243,3 +3245,5 @@
     0x0085, 0xFF5E, 0x00E4, 0xFF64, 0x0071, 0xFFE0, 0x0106, 0xFFE1, 0x0107,
     0xFFE2, 0x36E1, 0xFFE3, 0x00C4, 0xFFE4, 0x36E2, 0xFFE5, 0x0104,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/CNS1/cmaps_cns1.cpp b/core/fpdfapi/cmaps/CNS1/cmaps_cns1.cpp
index 810104e..18a1970 100644
--- a/core/fpdfapi/cmaps/CNS1/cmaps_cns1.cpp
+++ b/core/fpdfapi/cmaps/CNS1/cmaps_cns1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,33 +6,32 @@
 
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 
-#include "core/fxcrt/fx_memory.h"
+#include <iterator>
 
-const FXCMAP_CMap g_FXCMAP_CNS1_cmaps[] = {
-    {"B5pc-H", g_FXCMAP_B5pc_H_0, nullptr, 247, 0, FXCMAP_CMap::Range, 0},
-    {"B5pc-V", g_FXCMAP_B5pc_V_0, nullptr, 12, 0, FXCMAP_CMap::Range, -1},
-    {"HKscs-B5-H", g_FXCMAP_HKscs_B5_H_5, nullptr, 1210, 0, FXCMAP_CMap::Range,
+namespace fxcmap {
+
+const CMap kCNS1_cmaps[] = {
+    {"B5pc-H", kB5pc_H_0, nullptr, 247, 0, CMap::Type::kRange, 0},
+    {"B5pc-V", kB5pc_V_0, nullptr, 12, 0, CMap::Type::kRange, -1},
+    {"HKscs-B5-H", kHKscs_B5_H_5, nullptr, 1210, 0, CMap::Type::kRange, 0},
+    {"HKscs-B5-V", kHKscs_B5_V_5, nullptr, 13, 0, CMap::Type::kRange, -1},
+    {"ETen-B5-H", kETen_B5_H_0, nullptr, 254, 0, CMap::Type::kRange, 0},
+    {"ETen-B5-V", kETen_B5_V_0, nullptr, 13, 0, CMap::Type::kRange, -1},
+    {"ETenms-B5-H", kETenms_B5_H_0, nullptr, 1, 0, CMap::Type::kRange, -2},
+    {"ETenms-B5-V", kETenms_B5_V_0, nullptr, 18, 0, CMap::Type::kRange, -1},
+    {"CNS-EUC-H", kCNS_EUC_H_0, kCNS_EUC_H_0_DWord, 157, 238,
+     CMap::Type::kRange, 0},
+    {"CNS-EUC-V", kCNS_EUC_V_0, kCNS_EUC_V_0_DWord, 180, 261,
+     CMap::Type::kRange, 0},
+    {"UniCNS-UCS2-H", kUniCNS_UCS2_H_3, nullptr, 16418, 0, CMap::Type::kRange,
      0},
-    {"HKscs-B5-V", g_FXCMAP_HKscs_B5_V_5, nullptr, 13, 0, FXCMAP_CMap::Range,
+    {"UniCNS-UCS2-V", kUniCNS_UCS2_V_3, nullptr, 13, 0, CMap::Type::kRange, -1},
+    {"UniCNS-UTF16-H", kUniCNS_UTF16_H_0, nullptr, 14557, 0,
+     CMap::Type::kSingle, 0},
+    {"UniCNS-UTF16-V", kUniCNS_UCS2_V_3, nullptr, 13, 0, CMap::Type::kRange,
      -1},
-    {"ETen-B5-H", g_FXCMAP_ETen_B5_H_0, nullptr, 254, 0, FXCMAP_CMap::Range, 0},
-    {"ETen-B5-V", g_FXCMAP_ETen_B5_V_0, nullptr, 13, 0, FXCMAP_CMap::Range, -1},
-    {"ETenms-B5-H", g_FXCMAP_ETenms_B5_H_0, nullptr, 1, 0, FXCMAP_CMap::Range,
-     -2},
-    {"ETenms-B5-V", g_FXCMAP_ETenms_B5_V_0, nullptr, 18, 0, FXCMAP_CMap::Range,
-     -1},
-    {"CNS-EUC-H", g_FXCMAP_CNS_EUC_H_0, g_FXCMAP_CNS_EUC_H_0_DWord, 157, 238,
-     FXCMAP_CMap::Range, 0},
-    {"CNS-EUC-V", g_FXCMAP_CNS_EUC_V_0, g_FXCMAP_CNS_EUC_V_0_DWord, 180, 261,
-     FXCMAP_CMap::Range, 0},
-    {"UniCNS-UCS2-H", g_FXCMAP_UniCNS_UCS2_H_3, nullptr, 16418, 0,
-     FXCMAP_CMap::Range, 0},
-    {"UniCNS-UCS2-V", g_FXCMAP_UniCNS_UCS2_V_3, nullptr, 13, 0,
-     FXCMAP_CMap::Range, -1},
-    {"UniCNS-UTF16-H", g_FXCMAP_UniCNS_UTF16_H_0, nullptr, 14557, 0,
-     FXCMAP_CMap::Single, 0},
-    {"UniCNS-UTF16-V", g_FXCMAP_UniCNS_UCS2_V_3, nullptr, 13, 0,
-     FXCMAP_CMap::Range, -1},
 };
 
-const size_t g_FXCMAP_CNS1_cmaps_size = FX_ArraySize(g_FXCMAP_CNS1_cmaps);
+const size_t kCNS1_cmaps_size = std::size(kCNS1_cmaps);
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/CNS1/cmaps_cns1.h b/core/fpdfapi/cmaps/CNS1/cmaps_cns1.h
index 64f6b8b..75b31cb 100644
--- a/core/fpdfapi/cmaps/CNS1/cmaps_cns1.h
+++ b/core/fpdfapi/cmaps/CNS1/cmaps_cns1.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,25 +7,32 @@
 #ifndef CORE_FPDFAPI_CMAPS_CNS1_CMAPS_CNS1_H_
 #define CORE_FPDFAPI_CMAPS_CNS1_CMAPS_CNS1_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include "core/fpdfapi/cmaps/fpdf_cmaps.h"
 
-extern const uint16_t g_FXCMAP_B5pc_H_0[];
-extern const uint16_t g_FXCMAP_B5pc_V_0[];
-extern const uint16_t g_FXCMAP_HKscs_B5_H_5[];
-extern const uint16_t g_FXCMAP_HKscs_B5_V_5[];
-extern const uint16_t g_FXCMAP_ETen_B5_H_0[];
-extern const uint16_t g_FXCMAP_ETen_B5_V_0[];
-extern const uint16_t g_FXCMAP_ETenms_B5_H_0[];
-extern const uint16_t g_FXCMAP_ETenms_B5_V_0[];
-extern const uint16_t g_FXCMAP_CNS_EUC_H_0[];
-extern const FXCMAP_DWordCIDMap g_FXCMAP_CNS_EUC_H_0_DWord[];
-extern const uint16_t g_FXCMAP_CNS_EUC_V_0[];
-extern const FXCMAP_DWordCIDMap g_FXCMAP_CNS_EUC_V_0_DWord[];
-extern const uint16_t g_FXCMAP_UniCNS_UCS2_H_3[];
-extern const uint16_t g_FXCMAP_UniCNS_UCS2_V_3[];
-extern const uint16_t g_FXCMAP_UniCNS_UTF16_H_0[];
-extern const uint16_t g_FXCMAP_CNS1CID2Unicode_5[19088];
-extern const FXCMAP_CMap g_FXCMAP_CNS1_cmaps[];
-extern const size_t g_FXCMAP_CNS1_cmaps_size;
+namespace fxcmap {
+
+extern const uint16_t kB5pc_H_0[];
+extern const uint16_t kB5pc_V_0[];
+extern const uint16_t kHKscs_B5_H_5[];
+extern const uint16_t kHKscs_B5_V_5[];
+extern const uint16_t kETen_B5_H_0[];
+extern const uint16_t kETen_B5_V_0[];
+extern const uint16_t kETenms_B5_H_0[];
+extern const uint16_t kETenms_B5_V_0[];
+extern const uint16_t kCNS_EUC_H_0[];
+extern const DWordCIDMap kCNS_EUC_H_0_DWord[];
+extern const uint16_t kCNS_EUC_V_0[];
+extern const DWordCIDMap kCNS_EUC_V_0_DWord[];
+extern const uint16_t kUniCNS_UCS2_H_3[];
+extern const uint16_t kUniCNS_UCS2_V_3[];
+extern const uint16_t kUniCNS_UTF16_H_0[];
+extern const uint16_t kCNS1CID2Unicode_5[19088];
+extern const CMap kCNS1_cmaps[];
+extern const size_t kCNS1_cmaps_size;
+
+}  // namespace fxcmap
 
 #endif  // CORE_FPDFAPI_CMAPS_CNS1_CMAPS_CNS1_H_
diff --git a/core/fpdfapi/cmaps/GB1/Adobe-GB1-UCS2_5.cpp b/core/fpdfapi/cmaps/GB1/Adobe-GB1-UCS2_5.cpp
index 5a5ae04..b987a24 100644
--- a/core/fpdfapi/cmaps/GB1/Adobe-GB1-UCS2_5.cpp
+++ b/core/fpdfapi/cmaps/GB1/Adobe-GB1-UCS2_5.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h"
 
-const uint16_t g_FXCMAP_GB1CID2Unicode_5[30284] = {
+namespace fxcmap {
+
+const uint16_t kGB1CID2Unicode_5[30284] = {
     0xFFFD, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
     0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030,
     0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039,
@@ -3373,3 +3375,5 @@
     0xA4B6, 0xA4B7, 0xA4B8, 0xA4B9, 0xA4BA, 0xA4BB, 0xA4BC, 0xA4BD, 0xA4BE,
     0xA4BF, 0xA4C0, 0xA4C1, 0xA4C2, 0xA4C3, 0xA4C4, 0xA4C5, 0xA4C6,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/GB1/GB-EUC-H_0.cpp b/core/fpdfapi/cmaps/GB1/GB-EUC-H_0.cpp
index 9bbfe32..6abfa33 100644
--- a/core/fpdfapi/cmaps/GB1/GB-EUC-H_0.cpp
+++ b/core/fpdfapi/cmaps/GB1/GB-EUC-H_0.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h"
 
-const uint16_t g_FXCMAP_GB_EUC_H_0[90 * 3] = {
+namespace fxcmap {
+
+const uint16_t kGB_EUC_H_0[90 * 3] = {
     0x0020, 0x0020, 0x1E24, 0x0021, 0x007E, 0x032E, 0xA1A1, 0xA1FE, 0x0060,
     0xA2B1, 0xA2E2, 0x00BE, 0xA2E5, 0xA2EE, 0x00F0, 0xA2F1, 0xA2FC, 0x00FA,
     0xA3A1, 0xA3FE, 0x0106, 0xA4A1, 0xA4F3, 0x0164, 0xA5A1, 0xA5F6, 0x01B7,
@@ -38,3 +40,5 @@
     0xF2A1, 0xF2FE, 0x1BE3, 0xF3A1, 0xF3FE, 0x1C41, 0xF4A1, 0xF4FE, 0x1C9F,
     0xF5A1, 0xF5FE, 0x1CFD, 0xF6A1, 0xF6FE, 0x1D5B, 0xF7A1, 0xF7FE, 0x1DB9,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/GB1/GB-EUC-V_0.cpp b/core/fpdfapi/cmaps/GB1/GB-EUC-V_0.cpp
index 19bd418..0f3d136 100644
--- a/core/fpdfapi/cmaps/GB1/GB-EUC-V_0.cpp
+++ b/core/fpdfapi/cmaps/GB1/GB-EUC-V_0.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h"
 
-const uint16_t g_FXCMAP_GB_EUC_V_0[20 * 3] = {
+namespace fxcmap {
+
+const uint16_t kGB_EUC_V_0[20 * 3] = {
     0xA1A2, 0xA1A2, 0x023F, 0xA1A3, 0xA1A3, 0x023E, 0xA1AA, 0xA1AA, 0x0256,
     0xA1AB, 0xA1AC, 0x1E18, 0xA1AD, 0xA1AD, 0x0257, 0xA1B2, 0xA1BF, 0x0246,
     0xA1FE, 0xA1FE, 0x1E1A, 0xA3A1, 0xA3A1, 0x0242, 0xA3A8, 0xA3A9, 0x0244,
@@ -15,3 +17,5 @@
     0xA3DD, 0xA3DD, 0x1E1E, 0xA3DF, 0xA3DF, 0x0258, 0xA3FB, 0xA3FB, 0x0254,
     0xA3FD, 0xA3FD, 0x0255, 0xA3FE, 0xA3FE, 0x1E1F,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/GB1/GBK-EUC-H_2.cpp b/core/fpdfapi/cmaps/GB1/GBK-EUC-H_2.cpp
index 617f020..5cfc25f 100644
--- a/core/fpdfapi/cmaps/GB1/GBK-EUC-H_2.cpp
+++ b/core/fpdfapi/cmaps/GB1/GBK-EUC-H_2.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h"
 
-const uint16_t g_FXCMAP_GBK_EUC_H_2[4071 * 3] = {
+namespace fxcmap {
+
+const uint16_t kGBK_EUC_H_2[4071 * 3] = {
     0x0020, 0x0020, 0x1E24, 0x0021, 0x007E, 0x032E, 0x8140, 0x8178, 0x2758,
     0x8179, 0x8179, 0x2059, 0x817A, 0x817E, 0x2791, 0x8180, 0x8185, 0x2796,
     0x8186, 0x8186, 0x21F1, 0x8187, 0x81EC, 0x279C, 0x81ED, 0x81ED, 0x1FF2,
@@ -1365,3 +1367,5 @@
     0xFD9E, 0xFD9E, 0x40D3, 0xFD9F, 0xFD9F, 0x200C, 0xFDA0, 0xFDA0, 0x5083,
     0xFE40, 0xFE40, 0x1259, 0xFE41, 0xFE7E, 0x5610, 0xFE80, 0xFEA0, 0x564E,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/GB1/GBK-EUC-V_2.cpp b/core/fpdfapi/cmaps/GB1/GBK-EUC-V_2.cpp
index 87e53a7..ea59b41 100644
--- a/core/fpdfapi/cmaps/GB1/GBK-EUC-V_2.cpp
+++ b/core/fpdfapi/cmaps/GB1/GBK-EUC-V_2.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h"
 
-const uint16_t g_FXCMAP_GBK_EUC_V_2[20 * 3] = {
+namespace fxcmap {
+
+const uint16_t kGBK_EUC_V_2[20 * 3] = {
     0xA1A2, 0xA1A2, 0x023F, 0xA1A3, 0xA1A3, 0x023E, 0xA1AA, 0xA1AA, 0x0256,
     0xA1AB, 0xA1AC, 0x1E18, 0xA1AD, 0xA1AD, 0x0257, 0xA1B2, 0xA1BF, 0x0246,
     0xA1FE, 0xA1FE, 0x1E1A, 0xA3A1, 0xA3A1, 0x0242, 0xA3A8, 0xA3A9, 0x0244,
@@ -15,3 +17,5 @@
     0xA3DD, 0xA3DD, 0x1E1E, 0xA3DF, 0xA3DF, 0x0258, 0xA3FB, 0xA3FB, 0x0254,
     0xA3FD, 0xA3FD, 0x0255, 0xA3FE, 0xA3FE, 0x1E1F,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/GB1/GBK2K-H_5.cpp b/core/fpdfapi/cmaps/GB1/GBK2K-H_5.cpp
index 7d7b48a..4ac5804 100644
--- a/core/fpdfapi/cmaps/GB1/GBK2K-H_5.cpp
+++ b/core/fpdfapi/cmaps/GB1/GBK2K-H_5.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h"
 
-const uint16_t g_FXCMAP_GBK2K_H_5[4071 * 3] = {
+namespace fxcmap {
+
+const uint16_t kGBK2K_H_5[4071 * 3] = {
     0x0020, 0x007E, 0x0001, 0x8140, 0x8178, 0x2758, 0x8179, 0x8179, 0x2059,
     0x817A, 0x817E, 0x2791, 0x8180, 0x8185, 0x2796, 0x8186, 0x8186, 0x21F1,
     0x8187, 0x81EC, 0x279C, 0x81ED, 0x81ED, 0x1FF2, 0x81EE, 0x81F5, 0x2802,
@@ -1366,7 +1368,7 @@
     0xFE40, 0xFE40, 0x1259, 0xFE41, 0xFE7E, 0x5610, 0xFE80, 0xFEA0, 0x564E,
 };
 
-const FXCMAP_DWordCIDMap g_FXCMAP_GBK2K_H_5_DWord[1017] = {
+const DWordCIDMap kGBK2K_H_5_DWord[1017] = {
     {0x8130, 0x8436, 0x8436, 0x5752}, {0x8138, 0xFD38, 0xFD39, 0x579C},
     {0x8138, 0xFE30, 0xFE39, 0x579E}, {0x8139, 0x8130, 0x8137, 0x57A8},
     {0x8139, 0x8139, 0x8139, 0x57B0}, {0x8139, 0x8230, 0x8239, 0x57B1},
@@ -1877,3 +1879,5 @@
     {0x8236, 0x9230, 0x9239, 0x7632}, {0x8236, 0x9330, 0x9339, 0x763C},
     {0x8236, 0x9430, 0x9435, 0x7646},
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/GB1/GBK2K-V_5.cpp b/core/fpdfapi/cmaps/GB1/GBK2K-V_5.cpp
index 8efedd0..904253f 100644
--- a/core/fpdfapi/cmaps/GB1/GBK2K-V_5.cpp
+++ b/core/fpdfapi/cmaps/GB1/GBK2K-V_5.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h"
 
-const uint16_t g_FXCMAP_GBK2K_V_5[41 * 3] = {
+namespace fxcmap {
+
+const uint16_t kGBK2K_V_5[41 * 3] = {
     0xA1A2, 0xA1A2, 0x023F, 0xA1A3, 0xA1A3, 0x023E, 0xA1AA, 0xA1AA, 0x0256,
     0xA1AB, 0xA1AC, 0x1E18, 0xA1AD, 0xA1AD, 0x0257, 0xA1B2, 0xA1BF, 0x0246,
     0xA1FE, 0xA1FE, 0x1E1A, 0xA3A1, 0xA3A1, 0x0242, 0xA3A8, 0xA3A9, 0x0244,
@@ -22,3 +24,5 @@
     0xA5E3, 0xA5E3, 0x5773, 0xA5E5, 0xA5E5, 0x5775, 0xA5E7, 0xA5E7, 0x5774,
     0xA5EE, 0xA5EE, 0x5772, 0xA960, 0xA960, 0x577A,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/GB1/GBKp-EUC-H_2.cpp b/core/fpdfapi/cmaps/GB1/GBKp-EUC-H_2.cpp
index 3145980..2de5aea 100644
--- a/core/fpdfapi/cmaps/GB1/GBKp-EUC-H_2.cpp
+++ b/core/fpdfapi/cmaps/GB1/GBKp-EUC-H_2.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h"
 
-const uint16_t g_FXCMAP_GBKp_EUC_H_2[4070 * 3] = {
+namespace fxcmap {
+
+const uint16_t kGBKp_EUC_H_2[4070 * 3] = {
     0x0020, 0x007E, 0x0001, 0x8140, 0x8178, 0x2758, 0x8179, 0x8179, 0x2059,
     0x817A, 0x817E, 0x2791, 0x8180, 0x8185, 0x2796, 0x8186, 0x8186, 0x21F1,
     0x8187, 0x81EC, 0x279C, 0x81ED, 0x81ED, 0x1FF2, 0x81EE, 0x81F5, 0x2802,
@@ -1365,3 +1367,5 @@
     0xFD9F, 0xFD9F, 0x200C, 0xFDA0, 0xFDA0, 0x5083, 0xFE40, 0xFE40, 0x1259,
     0xFE41, 0xFE7E, 0x5610, 0xFE80, 0xFEA0, 0x564E,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/GB1/GBKp-EUC-V_2.cpp b/core/fpdfapi/cmaps/GB1/GBKp-EUC-V_2.cpp
index 014ae70..aa55cf8 100644
--- a/core/fpdfapi/cmaps/GB1/GBKp-EUC-V_2.cpp
+++ b/core/fpdfapi/cmaps/GB1/GBKp-EUC-V_2.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h"
 
-const uint16_t g_FXCMAP_GBKp_EUC_V_2[20 * 3] = {
+namespace fxcmap {
+
+const uint16_t kGBKp_EUC_V_2[20 * 3] = {
     0xA1A2, 0xA1A2, 0x023F, 0xA1A3, 0xA1A3, 0x023E, 0xA1AA, 0xA1AA, 0x0256,
     0xA1AB, 0xA1AC, 0x1E18, 0xA1AD, 0xA1AD, 0x0257, 0xA1B2, 0xA1BF, 0x0246,
     0xA1FE, 0xA1FE, 0x1E1A, 0xA3A1, 0xA3A1, 0x0242, 0xA3A8, 0xA3A9, 0x0244,
@@ -15,3 +17,5 @@
     0xA3DD, 0xA3DD, 0x1E1E, 0xA3DF, 0xA3DF, 0x0258, 0xA3FB, 0xA3FB, 0x0254,
     0xA3FD, 0xA3FD, 0x0255, 0xA3FE, 0xA3FE, 0x1E1F,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/GB1/GBpc-EUC-H_0.cpp b/core/fpdfapi/cmaps/GB1/GBpc-EUC-H_0.cpp
index ff437d7..f1f6dd3 100644
--- a/core/fpdfapi/cmaps/GB1/GBpc-EUC-H_0.cpp
+++ b/core/fpdfapi/cmaps/GB1/GBpc-EUC-H_0.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h"
 
-const uint16_t g_FXCMAP_GBpc_EUC_H_0[91 * 3] = {
+namespace fxcmap {
+
+const uint16_t kGBpc_EUC_H_0[91 * 3] = {
     0x0020, 0x007E, 0x0001, 0x0080, 0x0080, 0x1E20, 0x00FD, 0x00FF, 0x1E21,
     0xA1A1, 0xA1FE, 0x0060, 0xA2B1, 0xA2E2, 0x00BE, 0xA2E5, 0xA2EE, 0x00F0,
     0xA2F1, 0xA2FC, 0x00FA, 0xA3A1, 0xA3FE, 0x0106, 0xA4A1, 0xA4F3, 0x0164,
@@ -39,3 +41,5 @@
     0xF4A1, 0xF4FE, 0x1C9F, 0xF5A1, 0xF5FE, 0x1CFD, 0xF6A1, 0xF6FE, 0x1D5B,
     0xF7A1, 0xF7FE, 0x1DB9,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/GB1/GBpc-EUC-V_0.cpp b/core/fpdfapi/cmaps/GB1/GBpc-EUC-V_0.cpp
index a35858f..3b53117 100644
--- a/core/fpdfapi/cmaps/GB1/GBpc-EUC-V_0.cpp
+++ b/core/fpdfapi/cmaps/GB1/GBpc-EUC-V_0.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h"
 
-const uint16_t g_FXCMAP_GBpc_EUC_V_0[20 * 3] = {
+namespace fxcmap {
+
+const uint16_t kGBpc_EUC_V_0[20 * 3] = {
     0xA1A2, 0xA1A2, 0x023F, 0xA1A3, 0xA1A3, 0x023E, 0xA1AA, 0xA1AA, 0x0256,
     0xA1AB, 0xA1AC, 0x1E18, 0xA1AD, 0xA1AD, 0x0257, 0xA1B2, 0xA1BF, 0x0246,
     0xA1FE, 0xA1FE, 0x1E1A, 0xA3A1, 0xA3A1, 0x0242, 0xA3A8, 0xA3A9, 0x0244,
@@ -15,3 +17,5 @@
     0xA3DD, 0xA3DD, 0x1E1E, 0xA3DF, 0xA3DF, 0x0258, 0xA3FB, 0xA3FB, 0x0254,
     0xA3FD, 0xA3FD, 0x0255, 0xA3FE, 0xA3FE, 0x1E1F,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/GB1/UniGB-UCS2-H_4.cpp b/core/fpdfapi/cmaps/GB1/UniGB-UCS2-H_4.cpp
index e135f40..a787a34 100644
--- a/core/fpdfapi/cmaps/GB1/UniGB-UCS2-H_4.cpp
+++ b/core/fpdfapi/cmaps/GB1/UniGB-UCS2-H_4.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h"
 
-const uint16_t g_FXCMAP_UniGB_UCS2_H_4[13825 * 3] = {
+namespace fxcmap {
+
+const uint16_t kUniGB_UCS2_H_4[13825 * 3] = {
     0x0020, 0x007E, 0x0001, 0x00A4, 0x00A4, 0x00A7, 0x00A5, 0x00A5, 0x5752,
     0x00A7, 0x00A7, 0x00AB, 0x00A8, 0x00A8, 0x0066, 0x00B0, 0x00B0, 0x00A2,
     0x00B1, 0x00B1, 0x007F, 0x00D7, 0x00D7, 0x0080, 0x00E0, 0x00E0, 0x029F,
@@ -4617,3 +4619,5 @@
     0xFFE2, 0xFFE2, 0x271E, 0xFFE3, 0xFFE3, 0x0163, 0xFFE4, 0xFFE4, 0x271F,
     0xFFE5, 0xFFE5, 0x0109,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/GB1/UniGB-UCS2-V_4.cpp b/core/fpdfapi/cmaps/GB1/UniGB-UCS2-V_4.cpp
index 4ec5405..34ab6e7 100644
--- a/core/fpdfapi/cmaps/GB1/UniGB-UCS2-V_4.cpp
+++ b/core/fpdfapi/cmaps/GB1/UniGB-UCS2-V_4.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h"
 
-const uint16_t g_FXCMAP_UniGB_UCS2_V_4[24 * 3] = {
+namespace fxcmap {
+
+const uint16_t kUniGB_UCS2_V_4[24 * 3] = {
     0x2014, 0x2014, 0x0256, 0x2026, 0x2026, 0x0257, 0x2225, 0x2225, 0x1E1C,
     0x3001, 0x3001, 0x023F, 0x3002, 0x3002, 0x023E, 0x3008, 0x300F, 0x0248,
     0x3010, 0x3011, 0x0252, 0x3013, 0x3013, 0x1E1A, 0x3014, 0x3015, 0x0246,
@@ -16,3 +18,5 @@
     0xFF3D, 0xFF3D, 0x1E1E, 0xFF3F, 0xFF3F, 0x0258, 0xFF5B, 0xFF5B, 0x0254,
     0xFF5D, 0xFF5D, 0x0255, 0xFF5E, 0xFF5E, 0x1E18, 0xFFE3, 0xFFE3, 0x1E1F,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/GB1/cmaps_gb1.cpp b/core/fpdfapi/cmaps/GB1/cmaps_gb1.cpp
index a598091..facf123 100644
--- a/core/fpdfapi/cmaps/GB1/cmaps_gb1.cpp
+++ b/core/fpdfapi/cmaps/GB1/cmaps_gb1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,33 +6,29 @@
 
 #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h"
 
-#include "core/fxcrt/fx_memory.h"
+#include <iterator>
 
-const FXCMAP_CMap g_FXCMAP_GB1_cmaps[] = {
-    {"GB-EUC-H", g_FXCMAP_GB_EUC_H_0, nullptr, 90, 0, FXCMAP_CMap::Range, 0},
-    {"GB-EUC-V", g_FXCMAP_GB_EUC_V_0, nullptr, 20, 0, FXCMAP_CMap::Range, -1},
-    {"GBpc-EUC-H", g_FXCMAP_GBpc_EUC_H_0, nullptr, 91, 0, FXCMAP_CMap::Range,
+namespace fxcmap {
+
+const CMap kGB1_cmaps[] = {
+    {"GB-EUC-H", kGB_EUC_H_0, nullptr, 90, 0, CMap::Type::kRange, 0},
+    {"GB-EUC-V", kGB_EUC_V_0, nullptr, 20, 0, CMap::Type::kRange, -1},
+    {"GBpc-EUC-H", kGBpc_EUC_H_0, nullptr, 91, 0, CMap::Type::kRange, 0},
+    {"GBpc-EUC-V", kGBpc_EUC_V_0, nullptr, 20, 0, CMap::Type::kRange, -1},
+    {"GBK-EUC-H", kGBK_EUC_H_2, nullptr, 4071, 0, CMap::Type::kRange, 0},
+    {"GBK-EUC-V", kGBK_EUC_V_2, nullptr, 20, 0, CMap::Type::kRange, -1},
+    {"GBKp-EUC-H", kGBKp_EUC_H_2, nullptr, 4070, 0, CMap::Type::kRange, -2},
+    {"GBKp-EUC-V", kGBKp_EUC_V_2, nullptr, 20, 0, CMap::Type::kRange, -1},
+    {"GBK2K-H", kGBK2K_H_5, kGBK2K_H_5_DWord, 4071, 1017, CMap::Type::kRange,
+     -4},
+    {"GBK2K-V", kGBK2K_V_5, nullptr, 41, 0, CMap::Type::kRange, -1},
+    {"UniGB-UCS2-H", kUniGB_UCS2_H_4, nullptr, 13825, 0, CMap::Type::kRange, 0},
+    {"UniGB-UCS2-V", kUniGB_UCS2_V_4, nullptr, 24, 0, CMap::Type::kRange, -1},
+    {"UniGB-UTF16-H", kUniGB_UCS2_H_4, nullptr, 13825, 0, CMap::Type::kRange,
      0},
-    {"GBpc-EUC-V", g_FXCMAP_GBpc_EUC_V_0, nullptr, 20, 0, FXCMAP_CMap::Range,
-     -1},
-    {"GBK-EUC-H", g_FXCMAP_GBK_EUC_H_2, nullptr, 4071, 0, FXCMAP_CMap::Range,
-     0},
-    {"GBK-EUC-V", g_FXCMAP_GBK_EUC_V_2, nullptr, 20, 0, FXCMAP_CMap::Range, -1},
-    {"GBKp-EUC-H", g_FXCMAP_GBKp_EUC_H_2, nullptr, 4070, 0, FXCMAP_CMap::Range,
-     -2},
-    {"GBKp-EUC-V", g_FXCMAP_GBKp_EUC_V_2, nullptr, 20, 0, FXCMAP_CMap::Range,
-     -1},
-    {"GBK2K-H", g_FXCMAP_GBK2K_H_5, g_FXCMAP_GBK2K_H_5_DWord, 4071, 1017,
-     FXCMAP_CMap::Range, -4},
-    {"GBK2K-V", g_FXCMAP_GBK2K_V_5, nullptr, 41, 0, FXCMAP_CMap::Range, -1},
-    {"UniGB-UCS2-H", g_FXCMAP_UniGB_UCS2_H_4, nullptr, 13825, 0,
-     FXCMAP_CMap::Range, 0},
-    {"UniGB-UCS2-V", g_FXCMAP_UniGB_UCS2_V_4, nullptr, 24, 0,
-     FXCMAP_CMap::Range, -1},
-    {"UniGB-UTF16-H", g_FXCMAP_UniGB_UCS2_H_4, nullptr, 13825, 0,
-     FXCMAP_CMap::Range, 0},
-    {"UniGB-UTF16-V", g_FXCMAP_UniGB_UCS2_V_4, nullptr, 24, 0,
-     FXCMAP_CMap::Range, -1},
+    {"UniGB-UTF16-V", kUniGB_UCS2_V_4, nullptr, 24, 0, CMap::Type::kRange, -1},
 };
 
-const size_t g_FXCMAP_GB1_cmaps_size = FX_ArraySize(g_FXCMAP_GB1_cmaps);
+const size_t kGB1_cmaps_size = std::size(kGB1_cmaps);
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/GB1/cmaps_gb1.h b/core/fpdfapi/cmaps/GB1/cmaps_gb1.h
index 7873f64..a226266 100644
--- a/core/fpdfapi/cmaps/GB1/cmaps_gb1.h
+++ b/core/fpdfapi/cmaps/GB1/cmaps_gb1.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,23 +7,30 @@
 #ifndef CORE_FPDFAPI_CMAPS_GB1_CMAPS_GB1_H_
 #define CORE_FPDFAPI_CMAPS_GB1_CMAPS_GB1_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include "core/fpdfapi/cmaps/fpdf_cmaps.h"
 
-extern const uint16_t g_FXCMAP_GB_EUC_H_0[];
-extern const uint16_t g_FXCMAP_GB_EUC_V_0[];
-extern const uint16_t g_FXCMAP_GBpc_EUC_H_0[];
-extern const uint16_t g_FXCMAP_GBpc_EUC_V_0[];
-extern const uint16_t g_FXCMAP_GBK_EUC_H_2[];
-extern const uint16_t g_FXCMAP_GBK_EUC_V_2[];
-extern const uint16_t g_FXCMAP_GBKp_EUC_H_2[];
-extern const uint16_t g_FXCMAP_GBKp_EUC_V_2[];
-extern const uint16_t g_FXCMAP_GBK2K_H_5[];
-extern const FXCMAP_DWordCIDMap g_FXCMAP_GBK2K_H_5_DWord[];
-extern const uint16_t g_FXCMAP_GBK2K_V_5[];
-extern const uint16_t g_FXCMAP_UniGB_UCS2_H_4[];
-extern const uint16_t g_FXCMAP_UniGB_UCS2_V_4[];
-extern const uint16_t g_FXCMAP_GB1CID2Unicode_5[30284];
-extern const FXCMAP_CMap g_FXCMAP_GB1_cmaps[];
-extern const size_t g_FXCMAP_GB1_cmaps_size;
+namespace fxcmap {
+
+extern const uint16_t kGB_EUC_H_0[];
+extern const uint16_t kGB_EUC_V_0[];
+extern const uint16_t kGBpc_EUC_H_0[];
+extern const uint16_t kGBpc_EUC_V_0[];
+extern const uint16_t kGBK_EUC_H_2[];
+extern const uint16_t kGBK_EUC_V_2[];
+extern const uint16_t kGBKp_EUC_H_2[];
+extern const uint16_t kGBKp_EUC_V_2[];
+extern const uint16_t kGBK2K_H_5[];
+extern const DWordCIDMap kGBK2K_H_5_DWord[];
+extern const uint16_t kGBK2K_V_5[];
+extern const uint16_t kUniGB_UCS2_H_4[];
+extern const uint16_t kUniGB_UCS2_V_4[];
+extern const uint16_t kGB1CID2Unicode_5[30284];
+extern const CMap kGB1_cmaps[];
+extern const size_t kGB1_cmaps_size;
+
+}  // namespace fxcmap
 
 #endif  // CORE_FPDFAPI_CMAPS_GB1_CMAPS_GB1_H_
diff --git a/core/fpdfapi/cmaps/Japan1/83pv-RKSJ-H_1.cpp b/core/fpdfapi/cmaps/Japan1/83pv-RKSJ-H_1.cpp
index b3447af..51b49e2 100644
--- a/core/fpdfapi/cmaps/Japan1/83pv-RKSJ-H_1.cpp
+++ b/core/fpdfapi/cmaps/Japan1/83pv-RKSJ-H_1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_83pv_RKSJ_H_1[222 * 3] = {
+namespace fxcmap {
+
+const uint16_t k83pv_RKSJ_H_1[222 * 3] = {
     0x0020, 0x007E, 0x0001, 0x0080, 0x0080, 0x0061, 0x00A0, 0x00DF, 0x0146,
     0x00FD, 0x00FD, 0x0098, 0x00FE, 0x00FE, 0x00E4, 0x00FF, 0x00FF, 0x007C,
     0x8140, 0x817E, 0x0279, 0x8180, 0x81AC, 0x02B8, 0x81B8, 0x81BF, 0x02E5,
@@ -82,3 +84,5 @@
     0xEE90, 0xEE90, 0x02FA, 0xEE91, 0xEE91, 0x02F9, 0xEE92, 0xEE92, 0x0301,
     0xEE93, 0xEE99, 0x1DC8, 0xEE9A, 0xEE9A, 0x0300, 0xEE9B, 0xEE9C, 0x1DCF,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/90ms-RKSJ-H_2.cpp b/core/fpdfapi/cmaps/Japan1/90ms-RKSJ-H_2.cpp
index d5d29d6..212f809 100644
--- a/core/fpdfapi/cmaps/Japan1/90ms-RKSJ-H_2.cpp
+++ b/core/fpdfapi/cmaps/Japan1/90ms-RKSJ-H_2.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_90ms_RKSJ_H_2[171 * 3] = {
+namespace fxcmap {
+
+const uint16_t k90ms_RKSJ_H_2[171 * 3] = {
     0x0020, 0x007D, 0x00E7, 0x007E, 0x007E, 0x0277, 0x00A0, 0x00DF, 0x0146,
     0x8140, 0x817E, 0x0279, 0x8180, 0x81AC, 0x02B8, 0x81B8, 0x81BF, 0x02E5,
     0x81C8, 0x81CE, 0x02ED, 0x81DA, 0x81E8, 0x02F4, 0x81F0, 0x81F7, 0x0303,
@@ -65,3 +67,5 @@
     0xFA80, 0xFACF, 0x20CA, 0xFAD0, 0xFAD0, 0x07C9, 0xFAD1, 0xFAFC, 0x211A,
     0xFB40, 0xFB7E, 0x2146, 0xFB80, 0xFBFC, 0x2185, 0xFC40, 0xFC4B, 0x2202,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/90ms-RKSJ-V_2.cpp b/core/fpdfapi/cmaps/Japan1/90ms-RKSJ-V_2.cpp
index 1fb0c33..5303c72 100644
--- a/core/fpdfapi/cmaps/Japan1/90ms-RKSJ-V_2.cpp
+++ b/core/fpdfapi/cmaps/Japan1/90ms-RKSJ-V_2.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_90ms_RKSJ_V_2[78 * 3] = {
+namespace fxcmap {
+
+const uint16_t k90ms_RKSJ_V_2[78 * 3] = {
     0x8141, 0x8142, 0x1ECF, 0x8143, 0x8143, 0x204C, 0x8144, 0x8144, 0x2052,
     0x8150, 0x8151, 0x1ED1, 0x815B, 0x815D, 0x1ED3, 0x8160, 0x8164, 0x1ED6,
     0x8169, 0x817A, 0x1EDB, 0x8181, 0x8181, 0x1EED, 0x81A8, 0x81A8, 0x02E3,
@@ -34,3 +36,5 @@
     0x8768, 0x8768, 0x2098, 0x8769, 0x876A, 0x1F0E, 0x876B, 0x876B, 0x209C,
     0x876C, 0x876D, 0x1F11, 0x876E, 0x876E, 0x209D, 0x8780, 0x8781, 0x1F14,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/90msp-RKSJ-H_2.cpp b/core/fpdfapi/cmaps/Japan1/90msp-RKSJ-H_2.cpp
index 65081c8..a77d0e1 100644
--- a/core/fpdfapi/cmaps/Japan1/90msp-RKSJ-H_2.cpp
+++ b/core/fpdfapi/cmaps/Japan1/90msp-RKSJ-H_2.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_90msp_RKSJ_H_2[170 * 3] = {
+namespace fxcmap {
+
+const uint16_t k90msp_RKSJ_H_2[170 * 3] = {
     0x0020, 0x007E, 0x0001, 0x00A0, 0x00DF, 0x0146, 0x8140, 0x817E, 0x0279,
     0x8180, 0x81AC, 0x02B8, 0x81B8, 0x81BF, 0x02E5, 0x81C8, 0x81CE, 0x02ED,
     0x81DA, 0x81E8, 0x02F4, 0x81F0, 0x81F7, 0x0303, 0x81FC, 0x81FC, 0x030B,
@@ -65,3 +67,5 @@
     0xFAD0, 0xFAD0, 0x07C9, 0xFAD1, 0xFAFC, 0x211A, 0xFB40, 0xFB7E, 0x2146,
     0xFB80, 0xFBFC, 0x2185, 0xFC40, 0xFC4B, 0x2202,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/90msp-RKSJ-V_2.cpp b/core/fpdfapi/cmaps/Japan1/90msp-RKSJ-V_2.cpp
index 5a5e457..ee2f799 100644
--- a/core/fpdfapi/cmaps/Japan1/90msp-RKSJ-V_2.cpp
+++ b/core/fpdfapi/cmaps/Japan1/90msp-RKSJ-V_2.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_90msp_RKSJ_V_2[78 * 3] = {
+namespace fxcmap {
+
+const uint16_t k90msp_RKSJ_V_2[78 * 3] = {
     0x8141, 0x8142, 0x1ECF, 0x8143, 0x8143, 0x204C, 0x8144, 0x8144, 0x2052,
     0x8150, 0x8151, 0x1ED1, 0x815B, 0x815D, 0x1ED3, 0x8160, 0x8164, 0x1ED6,
     0x8169, 0x817A, 0x1EDB, 0x8181, 0x8181, 0x1EED, 0x81A8, 0x81A8, 0x02E3,
@@ -34,3 +36,5 @@
     0x8768, 0x8768, 0x2098, 0x8769, 0x876A, 0x1F0E, 0x876B, 0x876B, 0x209C,
     0x876C, 0x876D, 0x1F11, 0x876E, 0x876E, 0x209D, 0x8780, 0x8781, 0x1F14,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/90pv-RKSJ-H_1.cpp b/core/fpdfapi/cmaps/Japan1/90pv-RKSJ-H_1.cpp
index e41b839..0bf3f2f 100644
--- a/core/fpdfapi/cmaps/Japan1/90pv-RKSJ-H_1.cpp
+++ b/core/fpdfapi/cmaps/Japan1/90pv-RKSJ-H_1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_90pv_RKSJ_H_1[263 * 3] = {
+namespace fxcmap {
+
+const uint16_t k90pv_RKSJ_H_1[263 * 3] = {
     0x0020, 0x007E, 0x0001, 0x0080, 0x0080, 0x0061, 0x00A0, 0x00DF, 0x0146,
     0x00FD, 0x00FD, 0x0098, 0x00FE, 0x00FE, 0x00E4, 0x00FF, 0x00FF, 0x007C,
     0x8140, 0x817E, 0x0279, 0x8180, 0x81AC, 0x02B8, 0x81B8, 0x81BF, 0x02E5,
@@ -96,3 +98,5 @@
     0xED83, 0xED83, 0x1EFE, 0xED85, 0xED85, 0x1EFF, 0xED87, 0xED87, 0x1F00,
     0xED8E, 0xED8E, 0x1F01, 0xED95, 0xED96, 0x1F02,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/Add-RKSJ-H_1.cpp b/core/fpdfapi/cmaps/Japan1/Add-RKSJ-H_1.cpp
index 53dc754..69af127 100644
--- a/core/fpdfapi/cmaps/Japan1/Add-RKSJ-H_1.cpp
+++ b/core/fpdfapi/cmaps/Japan1/Add-RKSJ-H_1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_Add_RKSJ_H_1[635 * 3] = {
+namespace fxcmap {
+
+const uint16_t kAdd_RKSJ_H_1[635 * 3] = {
     0x0020, 0x007E, 0x00E7, 0x00A0, 0x00DF, 0x0146, 0x8140, 0x817E, 0x0279,
     0x8180, 0x81AC, 0x02B8, 0x81B8, 0x81BF, 0x02E5, 0x81C8, 0x81CE, 0x02ED,
     0x81DA, 0x81E8, 0x02F4, 0x81F0, 0x81F7, 0x0303, 0x81FC, 0x81FC, 0x030B,
@@ -220,3 +222,5 @@
     0xEF52, 0xEF63, 0x1EDB, 0xEF64, 0xEF79, 0x1EEE, 0xEF7A, 0xEF7B, 0x2048,
     0xEF8D, 0xEF90, 0x02E0, 0xEF91, 0xEF94, 0x1FF6,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/Add-RKSJ-V_1.cpp b/core/fpdfapi/cmaps/Japan1/Add-RKSJ-V_1.cpp
index 47be931..ce351ff 100644
--- a/core/fpdfapi/cmaps/Japan1/Add-RKSJ-V_1.cpp
+++ b/core/fpdfapi/cmaps/Japan1/Add-RKSJ-V_1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_Add_RKSJ_V_1[57 * 3] = {
+namespace fxcmap {
+
+const uint16_t kAdd_RKSJ_V_1[57 * 3] = {
     0x8141, 0x8142, 0x1ECF, 0x8143, 0x8143, 0x204C, 0x8144, 0x8144, 0x2052,
     0x8150, 0x8151, 0x1ED1, 0x815B, 0x815D, 0x1ED3, 0x8160, 0x8164, 0x1ED6,
     0x8165, 0x8165, 0x205A, 0x8166, 0x8166, 0x2053, 0x8167, 0x8167, 0x2058,
@@ -27,3 +29,5 @@
     0xEC8D, 0xEC8D, 0x20A6, 0xEC8E, 0xEC8E, 0x20A5, 0xEC8F, 0xEC8F, 0x20A1,
     0xEC90, 0xEC90, 0x20A4, 0xEC95, 0xEC95, 0x2084, 0xEF92, 0xEF92, 0x208D,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/Adobe-Japan1-UCS2_4.cpp b/core/fpdfapi/cmaps/Japan1/Adobe-Japan1-UCS2_4.cpp
index fbef3f1..758bfeb 100644
--- a/core/fpdfapi/cmaps/Japan1/Adobe-Japan1-UCS2_4.cpp
+++ b/core/fpdfapi/cmaps/Japan1/Adobe-Japan1-UCS2_4.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_Japan1CID2Unicode_4[15444] = {
+namespace fxcmap {
+
+const uint16_t kJapan1CID2Unicode_4[15444] = {
     0xFFFD, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
     0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030,
     0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039,
@@ -1724,3 +1726,5 @@
     0x440C, 0x3E8A, 0xFFFD, 0xFFFD, 0x4BE8, 0xFFFD, 0x3EDA, 0x3B22, 0xFFFD,
     0x457A, 0x4093, 0xFFFD, 0x4665, 0x4103, 0x4293, 0x46AE, 0x3488, 0xFFFD,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/EUC-H_1.cpp b/core/fpdfapi/cmaps/Japan1/EUC-H_1.cpp
index ddb1bcf..c4675be 100644
--- a/core/fpdfapi/cmaps/Japan1/EUC-H_1.cpp
+++ b/core/fpdfapi/cmaps/Japan1/EUC-H_1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_EUC_H_1[120 * 3] = {
+namespace fxcmap {
+
+const uint16_t kEUC_H_1[120 * 3] = {
     0x0020, 0x007E, 0x00E7, 0x8EA0, 0x8EDF, 0x0146, 0xA1A1, 0xA1FE, 0x0279,
     0xA2A1, 0xA2AE, 0x02D7, 0xA2BA, 0xA2C1, 0x02E5, 0xA2CA, 0xA2D0, 0x02ED,
     0xA2DC, 0xA2EA, 0x02F4, 0xA2F2, 0xA2F9, 0x0303, 0xA2FE, 0xA2FE, 0x030B,
@@ -48,3 +50,5 @@
     0xF0A1, 0xF0FE, 0x1BBA, 0xF1A1, 0xF1FE, 0x1C18, 0xF2A1, 0xF2FE, 0x1C76,
     0xF3A1, 0xF3FE, 0x1CD4, 0xF4A1, 0xF4A4, 0x1D32, 0xF4A5, 0xF4A6, 0x205C,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/EUC-V_1.cpp b/core/fpdfapi/cmaps/Japan1/EUC-V_1.cpp
index d5a70c6..c507656 100644
--- a/core/fpdfapi/cmaps/Japan1/EUC-V_1.cpp
+++ b/core/fpdfapi/cmaps/Japan1/EUC-V_1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_EUC_V_1[27 * 3] = {
+namespace fxcmap {
+
+const uint16_t kEUC_V_1[27 * 3] = {
     0xA1A2, 0xA1A3, 0x1ECF, 0xA1B1, 0xA1B2, 0x1ED1, 0xA1BC, 0xA1BE, 0x1ED3,
     0xA1C1, 0xA1C5, 0x1ED6, 0xA1CA, 0xA1DB, 0x1EDB, 0xA1E1, 0xA1E1, 0x1EED,
     0xA4A1, 0xA4A1, 0x1EEE, 0xA4A3, 0xA4A3, 0x1EEF, 0xA4A5, 0xA4A5, 0x1EF0,
@@ -17,3 +19,5 @@
     0xA5C3, 0xA5C3, 0x1EFD, 0xA5E3, 0xA5E3, 0x1EFE, 0xA5E5, 0xA5E5, 0x1EFF,
     0xA5E7, 0xA5E7, 0x1F00, 0xA5EE, 0xA5EE, 0x1F01, 0xA5F5, 0xA5F6, 0x1F02,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/Ext-RKSJ-H_2.cpp b/core/fpdfapi/cmaps/Japan1/Ext-RKSJ-H_2.cpp
index b251c5d..80c0b48 100644
--- a/core/fpdfapi/cmaps/Japan1/Ext-RKSJ-H_2.cpp
+++ b/core/fpdfapi/cmaps/Japan1/Ext-RKSJ-H_2.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_Ext_RKSJ_H_2[665 * 3] = {
+namespace fxcmap {
+
+const uint16_t kExt_RKSJ_H_2[665 * 3] = {
     0x0020, 0x007E, 0x00E7, 0x00A0, 0x00DF, 0x0146, 0x8140, 0x817E, 0x0279,
     0x8180, 0x8188, 0x02B8, 0x8189, 0x8189, 0x1D36, 0x818A, 0x81AC, 0x02C2,
     0x824F, 0x8258, 0x030C, 0x8260, 0x8279, 0x0316, 0x8281, 0x829A, 0x0330,
@@ -230,3 +232,5 @@
     0xEE40, 0xEE7E, 0x2162, 0xEE80, 0xEEEC, 0x21A1, 0xEEEF, 0xEEF8, 0x1F9C,
     0xEEF9, 0xEEF9, 0x02EF, 0xEEFA, 0xEEFC, 0x1F45,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/Ext-RKSJ-V_2.cpp b/core/fpdfapi/cmaps/Japan1/Ext-RKSJ-V_2.cpp
index b62e5a5..f3833bc 100644
--- a/core/fpdfapi/cmaps/Japan1/Ext-RKSJ-V_2.cpp
+++ b/core/fpdfapi/cmaps/Japan1/Ext-RKSJ-V_2.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_Ext_RKSJ_V_2[39 * 3] = {
+namespace fxcmap {
+
+const uint16_t kExt_RKSJ_V_2[39 * 3] = {
     0x8141, 0x8142, 0x1ECF, 0x8143, 0x8143, 0x204C, 0x8144, 0x8144, 0x2052,
     0x814A, 0x814A, 0x2050, 0x814B, 0x814B, 0x204F, 0x815B, 0x815D, 0x1ED3,
     0x8160, 0x8164, 0x1ED6, 0x8165, 0x8165, 0x2059, 0x8166, 0x8166, 0x2054,
@@ -21,3 +23,5 @@
     0x8385, 0x8385, 0x1EFF, 0x8387, 0x8387, 0x1F00, 0x838E, 0x838E, 0x1F01,
     0x8395, 0x8396, 0x1F02, 0x875F, 0x876E, 0x1F04, 0x8780, 0x8781, 0x1F14,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/H_1.cpp b/core/fpdfapi/cmaps/Japan1/H_1.cpp
index be32b5c..be3694d 100644
--- a/core/fpdfapi/cmaps/Japan1/H_1.cpp
+++ b/core/fpdfapi/cmaps/Japan1/H_1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_H_1[118 * 3] = {
+namespace fxcmap {
+
+const uint16_t kH_1[118 * 3] = {
     0x2121, 0x217E, 0x0279, 0x2221, 0x222E, 0x02D7, 0x223A, 0x2241, 0x02E5,
     0x224A, 0x2250, 0x02ED, 0x225C, 0x226A, 0x02F4, 0x2272, 0x2279, 0x0303,
     0x227E, 0x227E, 0x030B, 0x2330, 0x2339, 0x030C, 0x2341, 0x235A, 0x0316,
@@ -48,3 +50,5 @@
     0x7221, 0x727E, 0x1C76, 0x7321, 0x737E, 0x1CD4, 0x7421, 0x7424, 0x1D32,
     0x7425, 0x7426, 0x205C,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-H_4.cpp b/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-H_4.cpp
index e03c139..aa8565d 100644
--- a/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-H_4.cpp
+++ b/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-H_4.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,11 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_UniJIS_UCS2_HW_H_4[4 * 3] = {
+namespace fxcmap {
+
+const uint16_t kUniJIS_UCS2_HW_H_4[4 * 3] = {
     0x0020, 0x005B, 0x00E7, 0x005C, 0x005C, 0x220F,
     0x005D, 0x007E, 0x0124, 0x00A5, 0x00A5, 0x0123,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-V_4.cpp b/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-V_4.cpp
index 3f3a2a4..aed1f63 100644
--- a/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-V_4.cpp
+++ b/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-V_4.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_UniJIS_UCS2_HW_V_4[199 * 3] = {
+namespace fxcmap {
+
+const uint16_t kUniJIS_UCS2_HW_V_4[199 * 3] = {
     0x0020, 0x005B, 0x00E7, 0x005C, 0x005C, 0x220F, 0x005D, 0x007E, 0x0124,
     0x00A5, 0x00A5, 0x0123, 0x00B0, 0x00B0, 0x204D, 0x2010, 0x2010, 0x1ED5,
     0x2015, 0x2015, 0x1ED4, 0x2016, 0x2016, 0x1ED7, 0x2018, 0x2019, 0x2059,
@@ -75,3 +77,5 @@
     0xFF5C, 0xFF5C, 0x1ED8, 0xFF5D, 0xFF5D, 0x1EE2, 0xFF5E, 0xFF5E, 0x1ED6,
     0xFFE3, 0xFFE3, 0x1ED1,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-H_4.cpp b/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-H_4.cpp
index a8c8461..5496706 100644
--- a/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-H_4.cpp
+++ b/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-H_4.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_UniJIS_UCS2_H_4[9772 * 2] = {
+namespace fxcmap {
+
+const uint16_t kUniJIS_UCS2_H_4[9772 * 2] = {
     0x0020, 0x0001, 0x0021, 0x0002, 0x0022, 0x0003, 0x0023, 0x0004, 0x0024,
     0x0005, 0x0025, 0x0006, 0x0026, 0x0007, 0x0027, 0x0008, 0x0028, 0x0009,
     0x0029, 0x000A, 0x002A, 0x000B, 0x002B, 0x000C, 0x002C, 0x000D, 0x002D,
@@ -2180,3 +2182,5 @@
     0xFFE0, 0x02C9, 0xFFE1, 0x02CA, 0xFFE2, 0x02EF, 0xFFE3, 0x0289, 0xFFE4,
     0x1F45, 0xFFE5, 0x02C7, 0xFFE8, 0x0143,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-V_4.cpp b/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-V_4.cpp
index 0e6b215..8c2cc89 100644
--- a/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-V_4.cpp
+++ b/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-V_4.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_UniJIS_UCS2_V_4[251 * 2] = {
+namespace fxcmap {
+
+const uint16_t kUniJIS_UCS2_V_4[251 * 2] = {
     0x00B0, 0x204D, 0x2010, 0x1ED5, 0x2015, 0x1ED4, 0x2016, 0x1ED7, 0x2018,
     0x2059, 0x2019, 0x205A, 0x201C, 0x2057, 0x201D, 0x2058, 0x2025, 0x1EDA,
     0x2026, 0x1ED9, 0x2032, 0x2051, 0x2033, 0x205B, 0x2190, 0x02E2, 0x2191,
@@ -64,3 +66,5 @@
     0xFF3B, 0x1EDF, 0xFF3D, 0x1EE0, 0xFF3F, 0x1ED2, 0xFF5B, 0x1EE1, 0xFF5C,
     0x1ED8, 0xFF5D, 0x1EE2, 0xFF5E, 0x1ED6, 0xFFE3, 0x1ED1,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/V_1.cpp b/core/fpdfapi/cmaps/Japan1/V_1.cpp
index 645e1ef..8fcc59f 100644
--- a/core/fpdfapi/cmaps/Japan1/V_1.cpp
+++ b/core/fpdfapi/cmaps/Japan1/V_1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-const uint16_t g_FXCMAP_V_1[27 * 3] = {
+namespace fxcmap {
+
+const uint16_t kV_1[27 * 3] = {
     0x2122, 0x2123, 0x1ECF, 0x2131, 0x2132, 0x1ED1, 0x213C, 0x213E, 0x1ED3,
     0x2141, 0x2145, 0x1ED6, 0x214A, 0x215B, 0x1EDB, 0x2161, 0x2161, 0x1EED,
     0x2421, 0x2421, 0x1EEE, 0x2423, 0x2423, 0x1EEF, 0x2425, 0x2425, 0x1EF0,
@@ -17,3 +19,5 @@
     0x2543, 0x2543, 0x1EFD, 0x2563, 0x2563, 0x1EFE, 0x2565, 0x2565, 0x1EFF,
     0x2567, 0x2567, 0x1F00, 0x256E, 0x256E, 0x1F01, 0x2575, 0x2576, 0x1F02,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/cmaps_japan1.cpp b/core/fpdfapi/cmaps/Japan1/cmaps_japan1.cpp
index 5a48c77..9475854 100644
--- a/core/fpdfapi/cmaps/Japan1/cmaps_japan1.cpp
+++ b/core/fpdfapi/cmaps/Japan1/cmaps_japan1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,45 +6,39 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-#include "core/fxcrt/fx_memory.h"
+#include <iterator>
 
-const FXCMAP_CMap g_FXCMAP_Japan1_cmaps[] = {
-    {"83pv-RKSJ-H", g_FXCMAP_83pv_RKSJ_H_1, nullptr, 222, 0, FXCMAP_CMap::Range,
+namespace fxcmap {
+
+const CMap kJapan1_cmaps[] = {
+    {"83pv-RKSJ-H", k83pv_RKSJ_H_1, nullptr, 222, 0, CMap::Type::kRange, 0},
+    {"90ms-RKSJ-H", k90ms_RKSJ_H_2, nullptr, 171, 0, CMap::Type::kRange, 0},
+    {"90ms-RKSJ-V", k90ms_RKSJ_V_2, nullptr, 78, 0, CMap::Type::kRange, -1},
+    {"90msp-RKSJ-H", k90msp_RKSJ_H_2, nullptr, 170, 0, CMap::Type::kRange, -2},
+    {"90msp-RKSJ-V", k90msp_RKSJ_V_2, nullptr, 78, 0, CMap::Type::kRange, -1},
+    {"90pv-RKSJ-H", k90pv_RKSJ_H_1, nullptr, 263, 0, CMap::Type::kRange, 0},
+    {"Add-RKSJ-H", kAdd_RKSJ_H_1, nullptr, 635, 0, CMap::Type::kRange, 0},
+    {"Add-RKSJ-V", kAdd_RKSJ_V_1, nullptr, 57, 0, CMap::Type::kRange, -1},
+    {"EUC-H", kEUC_H_1, nullptr, 120, 0, CMap::Type::kRange, 0},
+    {"EUC-V", kEUC_V_1, nullptr, 27, 0, CMap::Type::kRange, -1},
+    {"Ext-RKSJ-H", kExt_RKSJ_H_2, nullptr, 665, 0, CMap::Type::kRange, -4},
+    {"Ext-RKSJ-V", kExt_RKSJ_V_2, nullptr, 39, 0, CMap::Type::kRange, -1},
+    {"H", kH_1, nullptr, 118, 0, CMap::Type::kRange, 0},
+    {"V", kV_1, nullptr, 27, 0, CMap::Type::kRange, -1},
+    {"UniJIS-UCS2-H", kUniJIS_UCS2_H_4, nullptr, 9772, 0, CMap::Type::kSingle,
      0},
-    {"90ms-RKSJ-H", g_FXCMAP_90ms_RKSJ_H_2, nullptr, 171, 0, FXCMAP_CMap::Range,
-     0},
-    {"90ms-RKSJ-V", g_FXCMAP_90ms_RKSJ_V_2, nullptr, 78, 0, FXCMAP_CMap::Range,
+    {"UniJIS-UCS2-V", kUniJIS_UCS2_V_4, nullptr, 251, 0, CMap::Type::kSingle,
      -1},
-    {"90msp-RKSJ-H", g_FXCMAP_90msp_RKSJ_H_2, nullptr, 170, 0,
-     FXCMAP_CMap::Range, -2},
-    {"90msp-RKSJ-V", g_FXCMAP_90msp_RKSJ_V_2, nullptr, 78, 0,
-     FXCMAP_CMap::Range, -1},
-    {"90pv-RKSJ-H", g_FXCMAP_90pv_RKSJ_H_1, nullptr, 263, 0, FXCMAP_CMap::Range,
+    {"UniJIS-UCS2-HW-H", kUniJIS_UCS2_HW_H_4, nullptr, 4, 0, CMap::Type::kRange,
+     -2},
+    {"UniJIS-UCS2-HW-V", kUniJIS_UCS2_HW_V_4, nullptr, 199, 0,
+     CMap::Type::kRange, -1},
+    {"UniJIS-UTF16-H", kUniJIS_UCS2_H_4, nullptr, 9772, 0, CMap::Type::kSingle,
      0},
-    {"Add-RKSJ-H", g_FXCMAP_Add_RKSJ_H_1, nullptr, 635, 0, FXCMAP_CMap::Range,
-     0},
-    {"Add-RKSJ-V", g_FXCMAP_Add_RKSJ_V_1, nullptr, 57, 0, FXCMAP_CMap::Range,
+    {"UniJIS-UTF16-V", kUniJIS_UCS2_V_4, nullptr, 251, 0, CMap::Type::kSingle,
      -1},
-    {"EUC-H", g_FXCMAP_EUC_H_1, nullptr, 120, 0, FXCMAP_CMap::Range, 0},
-    {"EUC-V", g_FXCMAP_EUC_V_1, nullptr, 27, 0, FXCMAP_CMap::Range, -1},
-    {"Ext-RKSJ-H", g_FXCMAP_Ext_RKSJ_H_2, nullptr, 665, 0, FXCMAP_CMap::Range,
-     -4},
-    {"Ext-RKSJ-V", g_FXCMAP_Ext_RKSJ_V_2, nullptr, 39, 0, FXCMAP_CMap::Range,
-     -1},
-    {"H", g_FXCMAP_H_1, nullptr, 118, 0, FXCMAP_CMap::Range, 0},
-    {"V", g_FXCMAP_V_1, nullptr, 27, 0, FXCMAP_CMap::Range, -1},
-    {"UniJIS-UCS2-H", g_FXCMAP_UniJIS_UCS2_H_4, nullptr, 9772, 0,
-     FXCMAP_CMap::Single, 0},
-    {"UniJIS-UCS2-V", g_FXCMAP_UniJIS_UCS2_V_4, nullptr, 251, 0,
-     FXCMAP_CMap::Single, -1},
-    {"UniJIS-UCS2-HW-H", g_FXCMAP_UniJIS_UCS2_HW_H_4, nullptr, 4, 0,
-     FXCMAP_CMap::Range, -2},
-    {"UniJIS-UCS2-HW-V", g_FXCMAP_UniJIS_UCS2_HW_V_4, nullptr, 199, 0,
-     FXCMAP_CMap::Range, -1},
-    {"UniJIS-UTF16-H", g_FXCMAP_UniJIS_UCS2_H_4, nullptr, 9772, 0,
-     FXCMAP_CMap::Single, 0},
-    {"UniJIS-UTF16-V", g_FXCMAP_UniJIS_UCS2_V_4, nullptr, 251, 0,
-     FXCMAP_CMap::Single, -1},
 };
 
-const size_t g_FXCMAP_Japan1_cmaps_size = FX_ArraySize(g_FXCMAP_Japan1_cmaps);
+const size_t kJapan1_cmaps_size = std::size(kJapan1_cmaps);
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Japan1/cmaps_japan1.h b/core/fpdfapi/cmaps/Japan1/cmaps_japan1.h
index 267a9fc..45df22d 100644
--- a/core/fpdfapi/cmaps/Japan1/cmaps_japan1.h
+++ b/core/fpdfapi/cmaps/Japan1/cmaps_japan1.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,31 +7,38 @@
 #ifndef CORE_FPDFAPI_CMAPS_JAPAN1_CMAPS_JAPAN1_H_
 #define CORE_FPDFAPI_CMAPS_JAPAN1_CMAPS_JAPAN1_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include "core/fpdfapi/cmaps/fpdf_cmaps.h"
 
-extern const uint16_t g_FXCMAP_83pv_RKSJ_H_1[];
-extern const uint16_t g_FXCMAP_90ms_RKSJ_H_2[];
-extern const uint16_t g_FXCMAP_90ms_RKSJ_V_2[];
-extern const uint16_t g_FXCMAP_90msp_RKSJ_H_2[];
-extern const uint16_t g_FXCMAP_90msp_RKSJ_V_2[];
-extern const uint16_t g_FXCMAP_90pv_RKSJ_H_1[];
-extern const uint16_t g_FXCMAP_Add_RKSJ_H_1[];
-extern const uint16_t g_FXCMAP_Add_RKSJ_V_1[];
-extern const uint16_t g_FXCMAP_EUC_H_1[];
-extern const uint16_t g_FXCMAP_EUC_V_1[];
-extern const uint16_t g_FXCMAP_Ext_RKSJ_H_2[];
-extern const uint16_t g_FXCMAP_Ext_RKSJ_V_2[];
-extern const uint16_t g_FXCMAP_H_1[];
-extern const uint16_t g_FXCMAP_V_1[];
-extern const uint16_t g_FXCMAP_UniJIS_UCS2_H_4[];
-extern const uint16_t g_FXCMAP_UniJIS_UCS2_V_4[];
-extern const uint16_t g_FXCMAP_UniJIS_UCS2_HW_H_4[];
-extern const uint16_t g_FXCMAP_UniJIS_UCS2_HW_V_4[];
-extern const uint16_t g_FXCMAP_UniJIS_UTF16_H_0[];
-extern const uint16_t g_FXCMAP_UniJIS_UTF16_H_0_DWord[];
-extern const uint16_t g_FXCMAP_UniJIS_UTF16_V_0[];
-extern const uint16_t g_FXCMAP_Japan1CID2Unicode_4[15444];
-extern const FXCMAP_CMap g_FXCMAP_Japan1_cmaps[];
-extern const size_t g_FXCMAP_Japan1_cmaps_size;
+namespace fxcmap {
+
+extern const uint16_t k83pv_RKSJ_H_1[];
+extern const uint16_t k90ms_RKSJ_H_2[];
+extern const uint16_t k90ms_RKSJ_V_2[];
+extern const uint16_t k90msp_RKSJ_H_2[];
+extern const uint16_t k90msp_RKSJ_V_2[];
+extern const uint16_t k90pv_RKSJ_H_1[];
+extern const uint16_t kAdd_RKSJ_H_1[];
+extern const uint16_t kAdd_RKSJ_V_1[];
+extern const uint16_t kEUC_H_1[];
+extern const uint16_t kEUC_V_1[];
+extern const uint16_t kExt_RKSJ_H_2[];
+extern const uint16_t kExt_RKSJ_V_2[];
+extern const uint16_t kH_1[];
+extern const uint16_t kV_1[];
+extern const uint16_t kUniJIS_UCS2_H_4[];
+extern const uint16_t kUniJIS_UCS2_V_4[];
+extern const uint16_t kUniJIS_UCS2_HW_H_4[];
+extern const uint16_t kUniJIS_UCS2_HW_V_4[];
+extern const uint16_t kUniJIS_UTF16_H_0[];
+extern const uint16_t kUniJIS_UTF16_H_0_DWord[];
+extern const uint16_t kUniJIS_UTF16_V_0[];
+extern const uint16_t kJapan1CID2Unicode_4[15444];
+extern const CMap kJapan1_cmaps[];
+extern const size_t kJapan1_cmaps_size;
+
+}  // namespace fxcmap
 
 #endif  // CORE_FPDFAPI_CMAPS_JAPAN1_CMAPS_JAPAN1_H_
diff --git a/core/fpdfapi/cmaps/Korea1/Adobe-Korea1-UCS2_2.cpp b/core/fpdfapi/cmaps/Korea1/Adobe-Korea1-UCS2_2.cpp
index 3f81ff9..e7657cf 100644
--- a/core/fpdfapi/cmaps/Korea1/Adobe-Korea1-UCS2_2.cpp
+++ b/core/fpdfapi/cmaps/Korea1/Adobe-Korea1-UCS2_2.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h"
 
-const uint16_t g_FXCMAP_Korea1CID2Unicode_2[18352] = {
+namespace fxcmap {
+
+const uint16_t kKorea1CID2Unicode_2[18352] = {
     0xFFFD, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
     0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030,
     0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039,
@@ -2048,3 +2050,5 @@
     0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x203E, 0x007E,
     0x005C,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Korea1/KSC-EUC-H_0.cpp b/core/fpdfapi/cmaps/Korea1/KSC-EUC-H_0.cpp
index cce7899..2ba31b4 100644
--- a/core/fpdfapi/cmaps/Korea1/KSC-EUC-H_0.cpp
+++ b/core/fpdfapi/cmaps/Korea1/KSC-EUC-H_0.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h"
 
-const uint16_t g_FXCMAP_KSC_EUC_H_0[467 * 3] = {
+namespace fxcmap {
+
+const uint16_t kKSC_EUC_H_0[467 * 3] = {
     0x0020, 0x007E, 0x1F9E, 0xA1A1, 0xA1FE, 0x0065, 0xA2A1, 0xA2E5, 0x00C3,
     0xA3A1, 0xA3FE, 0x0108, 0xA4A1, 0xA4D3, 0x0166, 0xA4D5, 0xA4FE, 0x0199,
     0xA5A1, 0xA5AA, 0x01C3, 0xA5B0, 0xA5B9, 0x01CD, 0xA5C1, 0xA5D8, 0x01D7,
@@ -164,3 +166,5 @@
     0xFBA1, 0xFBFE, 0x1E5F, 0xFCA1, 0xFCA8, 0x1EBD, 0xFCA9, 0xFCA9, 0x0EE7,
     0xFCAA, 0xFCFE, 0x1EC5, 0xFDA1, 0xFDFE, 0x1F1A,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Korea1/KSC-EUC-V_0.cpp b/core/fpdfapi/cmaps/Korea1/KSC-EUC-V_0.cpp
index 7408f9e..b166075 100644
--- a/core/fpdfapi/cmaps/Korea1/KSC-EUC-V_0.cpp
+++ b/core/fpdfapi/cmaps/Korea1/KSC-EUC-V_0.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h"
 
-const uint16_t g_FXCMAP_KSC_EUC_V_0[16 * 3] = {
+namespace fxcmap {
+
+const uint16_t kKSC_EUC_V_0[16 * 3] = {
     0xA1A2, 0xA1A3, 0x1F78, 0xA1A5, 0xA1A5, 0x1F7A, 0xA1A6, 0xA1A6,
     0x2080, 0xA1A9, 0xA1AB, 0x1F7B, 0xA1AD, 0xA1AD, 0x1F7E, 0xA1B2,
     0xA1BD, 0x1F7F, 0xA1EB, 0xA1EB, 0x1F8B, 0xA3A1, 0xA3A1, 0x1F8C,
@@ -14,3 +16,5 @@
     0x1F90, 0xA3BA, 0xA3BF, 0x1F91, 0xA3DB, 0xA3DB, 0x1F97, 0xA3DD,
     0xA3DD, 0x1F98, 0xA3DF, 0xA3DF, 0x1F99, 0xA3FB, 0xA3FE, 0x1F9A,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-H_1.cpp b/core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-H_1.cpp
index fbbdee8..161198d 100644
--- a/core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-H_1.cpp
+++ b/core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-H_1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h"
 
-const uint16_t g_FXCMAP_KSCms_UHC_HW_H_1[675 * 3] = {
+namespace fxcmap {
+
+const uint16_t kKSCms_UHC_HW_H_1[675 * 3] = {
     0x0020, 0x007E, 0x1F9E, 0x8141, 0x815A, 0x2475, 0x8161, 0x817A, 0x248F,
     0x8181, 0x81FE, 0x24A9, 0x8241, 0x825A, 0x2527, 0x8261, 0x827A, 0x2541,
     0x8281, 0x82FE, 0x255B, 0x8341, 0x835A, 0x25D9, 0x8361, 0x837A, 0x25F3,
@@ -233,3 +235,5 @@
     0xFAE7, 0xFAFE, 0x1E47, 0xFBA1, 0xFBFE, 0x1E5F, 0xFCA1, 0xFCA8, 0x1EBD,
     0xFCA9, 0xFCA9, 0x0EE7, 0xFCAA, 0xFCFE, 0x1EC5, 0xFDA1, 0xFDFE, 0x1F1A,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-V_1.cpp b/core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-V_1.cpp
index 16ddafa..575a27d 100644
--- a/core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-V_1.cpp
+++ b/core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-V_1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h"
 
-const uint16_t g_FXCMAP_KSCms_UHC_HW_V_1[16 * 3] = {
+namespace fxcmap {
+
+const uint16_t kKSCms_UHC_HW_V_1[16 * 3] = {
     0xA1A2, 0xA1A3, 0x1F78, 0xA1A5, 0xA1A5, 0x1F7A, 0xA1A6, 0xA1A6,
     0x2080, 0xA1A9, 0xA1AB, 0x1F7B, 0xA1AD, 0xA1AD, 0x1F7E, 0xA1B2,
     0xA1BD, 0x1F7F, 0xA1EB, 0xA1EB, 0x1F8B, 0xA3A1, 0xA3A1, 0x1F8C,
@@ -14,3 +16,5 @@
     0x1F90, 0xA3BA, 0xA3BF, 0x1F91, 0xA3DB, 0xA3DB, 0x1F97, 0xA3DD,
     0xA3DD, 0x1F98, 0xA3DF, 0xA3DF, 0x1F99, 0xA3FB, 0xA3FE, 0x1F9A,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Korea1/KSCms-UHC-H_1.cpp b/core/fpdfapi/cmaps/Korea1/KSCms-UHC-H_1.cpp
index 986c879..7d855c6 100644
--- a/core/fpdfapi/cmaps/Korea1/KSCms-UHC-H_1.cpp
+++ b/core/fpdfapi/cmaps/Korea1/KSCms-UHC-H_1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h"
 
-const uint16_t g_FXCMAP_KSCms_UHC_H_1[675 * 3] = {
+namespace fxcmap {
+
+const uint16_t kKSCms_UHC_H_1[675 * 3] = {
     0x0020, 0x007E, 0x0001, 0x8141, 0x815A, 0x2475, 0x8161, 0x817A, 0x248F,
     0x8181, 0x81FE, 0x24A9, 0x8241, 0x825A, 0x2527, 0x8261, 0x827A, 0x2541,
     0x8281, 0x82FE, 0x255B, 0x8341, 0x835A, 0x25D9, 0x8361, 0x837A, 0x25F3,
@@ -233,3 +235,5 @@
     0xFAE7, 0xFAFE, 0x1E47, 0xFBA1, 0xFBFE, 0x1E5F, 0xFCA1, 0xFCA8, 0x1EBD,
     0xFCA9, 0xFCA9, 0x0EE7, 0xFCAA, 0xFCFE, 0x1EC5, 0xFDA1, 0xFDFE, 0x1F1A,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Korea1/KSCms-UHC-V_1.cpp b/core/fpdfapi/cmaps/Korea1/KSCms-UHC-V_1.cpp
index e040444..7b18417 100644
--- a/core/fpdfapi/cmaps/Korea1/KSCms-UHC-V_1.cpp
+++ b/core/fpdfapi/cmaps/Korea1/KSCms-UHC-V_1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h"
 
-const uint16_t g_FXCMAP_KSCms_UHC_V_1[16 * 3] = {
+namespace fxcmap {
+
+const uint16_t kKSCms_UHC_V_1[16 * 3] = {
     0xA1A2, 0xA1A3, 0x1F78, 0xA1A5, 0xA1A5, 0x1F7A, 0xA1A6, 0xA1A6,
     0x2080, 0xA1A9, 0xA1AB, 0x1F7B, 0xA1AD, 0xA1AD, 0x1F7E, 0xA1B2,
     0xA1BD, 0x1F7F, 0xA1EB, 0xA1EB, 0x1F8B, 0xA3A1, 0xA3A1, 0x1F8C,
@@ -14,3 +16,5 @@
     0x1F90, 0xA3BA, 0xA3BF, 0x1F91, 0xA3DB, 0xA3DB, 0x1F97, 0xA3DD,
     0xA3DD, 0x1F98, 0xA3DF, 0xA3DF, 0x1F99, 0xA3FB, 0xA3FE, 0x1F9A,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Korea1/KSCpc-EUC-H_0.cpp b/core/fpdfapi/cmaps/Korea1/KSCpc-EUC-H_0.cpp
index 12fa16d..87e8aa5 100644
--- a/core/fpdfapi/cmaps/Korea1/KSCpc-EUC-H_0.cpp
+++ b/core/fpdfapi/cmaps/Korea1/KSCpc-EUC-H_0.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h"
 
-const uint16_t g_FXCMAP_KSCpc_EUC_H_0[509 * 3] = {
+namespace fxcmap {
+
+const uint16_t kKSCpc_EUC_H_0[509 * 3] = {
     0x0020, 0x007E, 0x0001, 0x0081, 0x0083, 0x0060, 0x00FE, 0x00FF, 0x0063,
     0xA141, 0xA17D, 0x1FFF, 0xA181, 0xA19A, 0x203C, 0xA19C, 0xA1A0, 0x2056,
     0xA1A1, 0xA1A1, 0x0065, 0xA1A2, 0xA1A3, 0x205B, 0xA1A4, 0xA1FE, 0x0068,
@@ -178,3 +180,5 @@
     0xFBA1, 0xFBFE, 0x1E5F, 0xFCA1, 0xFCA8, 0x1EBD, 0xFCA9, 0xFCA9, 0x0EE7,
     0xFCAA, 0xFCFE, 0x1EC5, 0xFDA1, 0xFDFE, 0x1F1A,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Korea1/UniKS-UCS2-H_1.cpp b/core/fpdfapi/cmaps/Korea1/UniKS-UCS2-H_1.cpp
index 8ed9fbe..0d8d3fc 100644
--- a/core/fpdfapi/cmaps/Korea1/UniKS-UCS2-H_1.cpp
+++ b/core/fpdfapi/cmaps/Korea1/UniKS-UCS2-H_1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h"
 
-const uint16_t g_FXCMAP_UniKS_UCS2_H_1[8394 * 3] = {
+namespace fxcmap {
+
+const uint16_t kUniKS_UCS2_H_1[8394 * 3] = {
     0x0020, 0x007E, 0x0001, 0x00A1, 0x00A1, 0x00D0, 0x00A4, 0x00A4, 0x00D6,
     0x00A7, 0x00A7, 0x009B, 0x00A8, 0x00A8, 0x006B, 0x00AA, 0x00AA, 0x029C,
     0x00AB, 0x00AB, 0x00B0, 0x00B0, 0x00B0, 0x008A, 0x00B1, 0x00B1, 0x0082,
@@ -2806,3 +2808,5 @@
     0xFF5E, 0xFF5E, 0x0071, 0xFFE0, 0xFFE1, 0x008F, 0xFFE2, 0xFFE2, 0x00C2,
     0xFFE3, 0xFFE3, 0x0165, 0xFFE5, 0xFFE5, 0x0091, 0xFFE6, 0xFFE6, 0x0143,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Korea1/UniKS-UCS2-V_1.cpp b/core/fpdfapi/cmaps/Korea1/UniKS-UCS2-V_1.cpp
index 46759c5..a550b17 100644
--- a/core/fpdfapi/cmaps/Korea1/UniKS-UCS2-V_1.cpp
+++ b/core/fpdfapi/cmaps/Korea1/UniKS-UCS2-V_1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h"
 
-const uint16_t g_FXCMAP_UniKS_UCS2_V_1[18 * 3] = {
+namespace fxcmap {
+
+const uint16_t kUniKS_UCS2_V_1[18 * 3] = {
     0x2013, 0x2014, 0x1F7B, 0x2016, 0x2016, 0x1F7D, 0x2025, 0x2025, 0x1F7A,
     0x3001, 0x3002, 0x1F78, 0x3008, 0x3011, 0x1F81, 0x3013, 0x3013, 0x1F8B,
     0x3014, 0x3015, 0x1F7F, 0xFF01, 0xFF01, 0x1F8C, 0xFF08, 0xFF09, 0x1F8D,
@@ -14,3 +16,5 @@
     0xFF3B, 0xFF3B, 0x1F97, 0xFF3D, 0xFF3D, 0x1F98, 0xFF3F, 0xFF3F, 0x1F99,
     0xFF5B, 0xFF5D, 0x1F9A, 0xFF5E, 0xFF5E, 0x1F7E, 0xFFE3, 0xFFE3, 0x1F9D,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Korea1/UniKS-UTF16-H_0.cpp b/core/fpdfapi/cmaps/Korea1/UniKS-UTF16-H_0.cpp
index 2101805..2d42617 100644
--- a/core/fpdfapi/cmaps/Korea1/UniKS-UTF16-H_0.cpp
+++ b/core/fpdfapi/cmaps/Korea1/UniKS-UTF16-H_0.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h"
 
-const uint16_t g_FXCMAP_UniKS_UTF16_H_0[158 * 2] = {
+namespace fxcmap {
+
+const uint16_t kUniKS_UTF16_H_0[158 * 2] = {
     0x00A9, 0x0062, 0x2010, 0x0061, 0x20A9, 0x0060, 0x2F00, 0x193C, 0x2F04,
     0x18EC, 0x2F06, 0x190D, 0x2F08, 0x192B, 0x2F0A, 0x194D, 0x2F0B, 0x1D4B,
     0x2F11, 0x10AE, 0x2F12, 0x116A, 0x2F14, 0x143F, 0x2F17, 0x168C, 0x2F18,
@@ -44,3 +46,5 @@
     0x2FD0, 0x1466, 0x2FD1, 0x1A7D, 0x2FD2, 0x1CBF, 0x2FD3, 0x11D3, 0x2FD4,
     0x0F6A,
 };
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Korea1/cmaps_korea1.cpp b/core/fpdfapi/cmaps/Korea1/cmaps_korea1.cpp
index 7362ff8..2109e04 100644
--- a/core/fpdfapi/cmaps/Korea1/cmaps_korea1.cpp
+++ b/core/fpdfapi/cmaps/Korea1/cmaps_korea1.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,29 +6,27 @@
 
 #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h"
 
-#include "core/fxcrt/fx_memory.h"
+#include <iterator>
 
-const FXCMAP_CMap g_FXCMAP_Korea1_cmaps[] = {
-    {"KSC-EUC-H", g_FXCMAP_KSC_EUC_H_0, nullptr, 467, 0, FXCMAP_CMap::Range, 0},
-    {"KSC-EUC-V", g_FXCMAP_KSC_EUC_V_0, nullptr, 16, 0, FXCMAP_CMap::Range, -1},
-    {"KSCms-UHC-H", g_FXCMAP_KSCms_UHC_H_1, nullptr, 675, 0, FXCMAP_CMap::Range,
-     -2},
-    {"KSCms-UHC-V", g_FXCMAP_KSCms_UHC_V_1, nullptr, 16, 0, FXCMAP_CMap::Range,
+namespace fxcmap {
+
+const CMap kKorea1_cmaps[] = {
+    {"KSC-EUC-H", kKSC_EUC_H_0, nullptr, 467, 0, CMap::Type::kRange, 0},
+    {"KSC-EUC-V", kKSC_EUC_V_0, nullptr, 16, 0, CMap::Type::kRange, -1},
+    {"KSCms-UHC-H", kKSCms_UHC_H_1, nullptr, 675, 0, CMap::Type::kRange, -2},
+    {"KSCms-UHC-V", kKSCms_UHC_V_1, nullptr, 16, 0, CMap::Type::kRange, -1},
+    {"KSCms-UHC-HW-H", kKSCms_UHC_HW_H_1, nullptr, 675, 0, CMap::Type::kRange,
+     0},
+    {"KSCms-UHC-HW-V", kKSCms_UHC_HW_V_1, nullptr, 16, 0, CMap::Type::kRange,
      -1},
-    {"KSCms-UHC-HW-H", g_FXCMAP_KSCms_UHC_HW_H_1, nullptr, 675, 0,
-     FXCMAP_CMap::Range, 0},
-    {"KSCms-UHC-HW-V", g_FXCMAP_KSCms_UHC_HW_V_1, nullptr, 16, 0,
-     FXCMAP_CMap::Range, -1},
-    {"KSCpc-EUC-H", g_FXCMAP_KSCpc_EUC_H_0, nullptr, 509, 0, FXCMAP_CMap::Range,
-     -6},
-    {"UniKS-UCS2-H", g_FXCMAP_UniKS_UCS2_H_1, nullptr, 8394, 0,
-     FXCMAP_CMap::Range, 0},
-    {"UniKS-UCS2-V", g_FXCMAP_UniKS_UCS2_V_1, nullptr, 18, 0,
-     FXCMAP_CMap::Range, -1},
-    {"UniKS-UTF16-H", g_FXCMAP_UniKS_UTF16_H_0, nullptr, 158, 0,
-     FXCMAP_CMap::Single, -2},
-    {"UniKS-UTF16-V", g_FXCMAP_UniKS_UCS2_V_1, nullptr, 18, 0,
-     FXCMAP_CMap::Range, -1},
+    {"KSCpc-EUC-H", kKSCpc_EUC_H_0, nullptr, 509, 0, CMap::Type::kRange, -6},
+    {"UniKS-UCS2-H", kUniKS_UCS2_H_1, nullptr, 8394, 0, CMap::Type::kRange, 0},
+    {"UniKS-UCS2-V", kUniKS_UCS2_V_1, nullptr, 18, 0, CMap::Type::kRange, -1},
+    {"UniKS-UTF16-H", kUniKS_UTF16_H_0, nullptr, 158, 0, CMap::Type::kSingle,
+     -2},
+    {"UniKS-UTF16-V", kUniKS_UCS2_V_1, nullptr, 18, 0, CMap::Type::kRange, -1},
 };
 
-const size_t g_FXCMAP_Korea1_cmaps_size = FX_ArraySize(g_FXCMAP_Korea1_cmaps);
+const size_t kKorea1_cmaps_size = std::size(kKorea1_cmaps);
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/Korea1/cmaps_korea1.h b/core/fpdfapi/cmaps/Korea1/cmaps_korea1.h
index d54d156..f75ded9 100644
--- a/core/fpdfapi/cmaps/Korea1/cmaps_korea1.h
+++ b/core/fpdfapi/cmaps/Korea1/cmaps_korea1.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,20 +7,27 @@
 #ifndef CORE_FPDFAPI_CMAPS_KOREA1_CMAPS_KOREA1_H_
 #define CORE_FPDFAPI_CMAPS_KOREA1_CMAPS_KOREA1_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include "core/fpdfapi/cmaps/fpdf_cmaps.h"
 
-extern const uint16_t g_FXCMAP_KSC_EUC_H_0[];
-extern const uint16_t g_FXCMAP_KSC_EUC_V_0[];
-extern const uint16_t g_FXCMAP_KSCms_UHC_H_1[];
-extern const uint16_t g_FXCMAP_KSCms_UHC_V_1[];
-extern const uint16_t g_FXCMAP_KSCms_UHC_HW_H_1[];
-extern const uint16_t g_FXCMAP_KSCms_UHC_HW_V_1[];
-extern const uint16_t g_FXCMAP_KSCpc_EUC_H_0[];
-extern const uint16_t g_FXCMAP_UniKS_UCS2_H_1[];
-extern const uint16_t g_FXCMAP_UniKS_UCS2_V_1[];
-extern const uint16_t g_FXCMAP_UniKS_UTF16_H_0[];
-extern const uint16_t g_FXCMAP_Korea1CID2Unicode_2[18352];
-extern const FXCMAP_CMap g_FXCMAP_Korea1_cmaps[];
-extern const size_t g_FXCMAP_Korea1_cmaps_size;
+namespace fxcmap {
+
+extern const uint16_t kKSC_EUC_H_0[];
+extern const uint16_t kKSC_EUC_V_0[];
+extern const uint16_t kKSCms_UHC_H_1[];
+extern const uint16_t kKSCms_UHC_V_1[];
+extern const uint16_t kKSCms_UHC_HW_H_1[];
+extern const uint16_t kKSCms_UHC_HW_V_1[];
+extern const uint16_t kKSCpc_EUC_H_0[];
+extern const uint16_t kUniKS_UCS2_H_1[];
+extern const uint16_t kUniKS_UCS2_V_1[];
+extern const uint16_t kUniKS_UTF16_H_0[];
+extern const uint16_t kKorea1CID2Unicode_2[18352];
+extern const CMap kKorea1_cmaps[];
+extern const size_t kKorea1_cmaps_size;
+
+}  // namespace fxcmap
 
 #endif  // CORE_FPDFAPI_CMAPS_KOREA1_CMAPS_KOREA1_H_
diff --git a/core/fpdfapi/cmaps/fpdf_cmaps.cpp b/core/fpdfapi/cmaps/fpdf_cmaps.cpp
index 5c89faf..98187a1 100644
--- a/core/fpdfapi/cmaps/fpdf_cmaps.cpp
+++ b/core/fpdfapi/cmaps/fpdf_cmaps.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,6 +8,10 @@
 
 #include <algorithm>
 
+#include "third_party/base/check.h"
+
+namespace fxcmap {
+
 namespace {
 
 struct SingleCmap {
@@ -21,32 +25,23 @@
   uint16_t cid;
 };
 
-const FXCMAP_CMap* FindNextCMap(const FXCMAP_CMap* pMap) {
+const CMap* FindNextCMap(const CMap* pMap) {
   return pMap->m_UseOffset ? pMap + pMap->m_UseOffset : nullptr;
 }
 
 }  // namespace
 
-const FXCMAP_CMap* FindEmbeddedCMap(pdfium::span<const FXCMAP_CMap> pCMaps,
-                                    ByteStringView bsName) {
-  for (size_t i = 0; i < pCMaps.size(); i++) {
-    if (bsName == pCMaps[i].m_Name)
-      return &pCMaps[i];
-  }
-  return nullptr;
-}
-
-uint16_t CIDFromCharCode(const FXCMAP_CMap* pMap, uint32_t charcode) {
-  ASSERT(pMap);
+uint16_t CIDFromCharCode(const CMap* pMap, uint32_t charcode) {
+  DCHECK(pMap);
   const uint16_t loword = static_cast<uint16_t>(charcode);
   if (charcode >> 16) {
     while (pMap) {
       if (pMap->m_pDWordMap) {
-        const FXCMAP_DWordCIDMap* begin = pMap->m_pDWordMap;
+        const DWordCIDMap* begin = pMap->m_pDWordMap;
         const auto* end = begin + pMap->m_DWordCount;
         const auto* found = std::lower_bound(
             begin, end, charcode,
-            [](const FXCMAP_DWordCIDMap& element, uint32_t charcode) {
+            [](const DWordCIDMap& element, uint32_t charcode) {
               uint16_t hiword = static_cast<uint16_t>(charcode >> 16);
               if (element.m_HiWord != hiword)
                 return element.m_HiWord < hiword;
@@ -64,7 +59,7 @@
 
   while (pMap && pMap->m_pWordMap) {
     switch (pMap->m_WordMapType) {
-      case FXCMAP_CMap::Single: {
+      case CMap::Type::kSingle: {
         const auto* begin =
             reinterpret_cast<const SingleCmap*>(pMap->m_pWordMap);
         const auto* end = begin + pMap->m_WordCount;
@@ -76,7 +71,7 @@
           return found->cid;
         break;
       }
-      case FXCMAP_CMap::Range: {
+      case CMap::Type::kRange: {
         const auto* begin =
             reinterpret_cast<const RangeCmap*>(pMap->m_pWordMap);
         const auto* end = begin + pMap->m_WordCount;
@@ -88,10 +83,6 @@
           return found->cid + loword - found->low;
         break;
       }
-      default: {
-        NOTREACHED();
-        break;
-      }
     }
     pMap = FindNextCMap(pMap);
   }
@@ -99,16 +90,16 @@
   return 0;
 }
 
-uint32_t CharCodeFromCID(const FXCMAP_CMap* pMap, uint16_t cid) {
+uint32_t CharCodeFromCID(const CMap* pMap, uint16_t cid) {
   // TODO(dsinclair): This should be checking both pMap->m_WordMap and
   // pMap->m_DWordMap. There was a second while() but it was never reached as
   // the first always returns. Investigate and determine how this should
   // really be working. (https://codereview.chromium.org/2235743003 removed the
   // second while loop.)
-  ASSERT(pMap);
+  DCHECK(pMap);
   while (pMap) {
     switch (pMap->m_WordMapType) {
-      case FXCMAP_CMap::Single: {
+      case CMap::Type::kSingle: {
         const auto* pCur =
             reinterpret_cast<const SingleCmap*>(pMap->m_pWordMap);
         const auto* pEnd = pCur + pMap->m_WordCount;
@@ -119,7 +110,7 @@
         }
         break;
       }
-      case FXCMAP_CMap::Range: {
+      case CMap::Type::kRange: {
         const auto* pCur = reinterpret_cast<const RangeCmap*>(pMap->m_pWordMap);
         const auto* pEnd = pCur + pMap->m_WordCount;
         while (pCur < pEnd) {
@@ -129,12 +120,10 @@
         }
         break;
       }
-      default: {
-        NOTREACHED();
-        break;
-      }
     }
     pMap = FindNextCMap(pMap);
   }
   return 0;
 }
+
+}  // namespace fxcmap
diff --git a/core/fpdfapi/cmaps/fpdf_cmaps.h b/core/fpdfapi/cmaps/fpdf_cmaps.h
index 2c7548a..795427f 100644
--- a/core/fpdfapi/cmaps/fpdf_cmaps.h
+++ b/core/fpdfapi/cmaps/fpdf_cmaps.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,31 +9,32 @@
 
 #include <stdint.h>
 
-#include "core/fxcrt/fx_string.h"
-#include "third_party/base/span.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 
-struct FXCMAP_DWordCIDMap {
+namespace fxcmap {
+
+struct DWordCIDMap {
   uint16_t m_HiWord;
   uint16_t m_LoWordLow;
   uint16_t m_LoWordHigh;
   uint16_t m_CID;
 };
 
-struct FXCMAP_CMap {
-  enum MapType : uint8_t { Single, Range };
+struct CMap {
+  enum class Type : bool { kSingle, kRange };
 
-  const char* m_Name;                     // Raw, POD struct.
-  const uint16_t* m_pWordMap;             // Raw, POD struct.
-  const FXCMAP_DWordCIDMap* m_pDWordMap;  // Raw, POD struct.
+  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;
-  MapType m_WordMapType;
+  Type m_WordMapType;
   int8_t m_UseOffset;
 };
 
-const FXCMAP_CMap* FindEmbeddedCMap(pdfium::span<const FXCMAP_CMap> pCMaps,
-                                    ByteStringView name);
-uint16_t CIDFromCharCode(const FXCMAP_CMap* pMap, uint32_t charcode);
-uint32_t CharCodeFromCID(const FXCMAP_CMap* pMap, uint16_t cid);
+uint16_t CIDFromCharCode(const CMap* pMap, uint32_t charcode);
+uint32_t CharCodeFromCID(const CMap* pMap, uint16_t cid);
+
+}  // namespace fxcmap
 
 #endif  // CORE_FPDFAPI_CMAPS_FPDF_CMAPS_H_
diff --git a/core/fpdfapi/edit/Android.bp b/core/fpdfapi/edit/Android.bp
index 1822b06..6b70ca8 100644
--- a/core/fpdfapi/edit/Android.bp
+++ b/core/fpdfapi/edit/Android.bp
@@ -13,11 +13,8 @@
 
     visibility: ["//external/pdfium:__subpackages__"],
 
-    header_libs: [
-        "libpdfium-constants",
-    ],
-
     static_libs: [
+        "libpdfium-constants",
         "libpdfium-fxcrt",
         "libpdfium-font",
         "libpdfium-page",
diff --git a/core/fpdfapi/edit/BUILD.gn b/core/fpdfapi/edit/BUILD.gn
index cda86e63..f6af621 100644
--- a/core/fpdfapi/edit/BUILD.gn
+++ b/core/fpdfapi/edit/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -18,10 +18,12 @@
     "cpdf_stringarchivestream.cpp",
     "cpdf_stringarchivestream.h",
   ]
-  configs += [ "../../../:pdfium_core_config" ]
+  configs += [
+    "../../../:pdfium_strict_config",
+    "../../../:pdfium_noshorten_config",
+  ]
   deps = [
     "../../../constants",
-    "../../../third_party:skia_shared",
     "../../fxcrt",
     "../font",
     "../page",
@@ -37,7 +39,9 @@
     "../../fxge",
     "../font",
     "../page",
+    "../page:unit_test_support",
     "../parser",
+    "../parser:unit_test_support",
     "../render",
   ]
   pdfium_root_dir = "../../../"
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 28165b1..d1fede9 100644
--- a/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp
+++ b/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp
@@ -1,30 +1,247 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
 
-#include "third_party/skia_shared/SkFloatToDecimal.h"
+#include <cassert>
+#include <cfloat>
+#include <climits>
+#include <cmath>
+#include <ostream>
+
+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;
 }
 
-std::ostream& operator<<(std::ostream& ar, const CFX_Matrix& matrix) {
-  WriteFloat(ar, matrix.a) << " ";
-  WriteFloat(ar, matrix.b) << " ";
-  WriteFloat(ar, matrix.c) << " ";
-  WriteFloat(ar, matrix.d) << " ";
-  WriteFloat(ar, matrix.e) << " ";
-  WriteFloat(ar, matrix.f);
-  return ar;
+std::ostream& WriteMatrix(std::ostream& stream, const CFX_Matrix& matrix) {
+  WriteFloat(stream, matrix.a) << " ";
+  WriteFloat(stream, matrix.b) << " ";
+  WriteFloat(stream, matrix.c) << " ";
+  WriteFloat(stream, matrix.d) << " ";
+  WriteFloat(stream, matrix.e) << " ";
+  WriteFloat(stream, matrix.f);
+  return stream;
 }
 
-std::ostream& operator<<(std::ostream& ar, const CFX_PointF& point) {
-  WriteFloat(ar, point.x) << " ";
-  WriteFloat(ar, point.y);
-  return ar;
+std::ostream& WritePoint(std::ostream& stream, const CFX_PointF& point) {
+  WriteFloat(stream, point.x) << " ";
+  WriteFloat(stream, point.y);
+  return stream;
+}
+
+std::ostream& WriteRect(std::ostream& stream, const CFX_FloatRect& rect) {
+  WriteFloat(stream, rect.left) << " ";
+  WriteFloat(stream, rect.bottom) << " ";
+  WriteFloat(stream, rect.Width()) << " ";
+  WriteFloat(stream, rect.Height());
+  return stream;
 }
diff --git a/core/fpdfapi/edit/cpdf_contentstream_write_utils.h b/core/fpdfapi/edit/cpdf_contentstream_write_utils.h
index 3e14c9f..2440d92 100644
--- a/core/fpdfapi/edit/cpdf_contentstream_write_utils.h
+++ b/core/fpdfapi/edit/cpdf_contentstream_write_utils.h
@@ -1,16 +1,17 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef CORE_FPDFAPI_EDIT_CPDF_CONTENTSTREAM_WRITE_UTILS_H_
 #define CORE_FPDFAPI_EDIT_CPDF_CONTENTSTREAM_WRITE_UTILS_H_
 
-#include <ostream>
+#include <iosfwd>
 
 #include "core/fxcrt/fx_coordinates.h"
 
 std::ostream& WriteFloat(std::ostream& stream, float value);
-std::ostream& operator<<(std::ostream& ar, const CFX_Matrix& matrix);
-std::ostream& operator<<(std::ostream& ar, const CFX_PointF& point);
+std::ostream& WriteMatrix(std::ostream& stream, const CFX_Matrix& matrix);
+std::ostream& WritePoint(std::ostream& stream, const CFX_PointF& point);
+std::ostream& WriteRect(std::ostream& stream, const CFX_FloatRect& rect);
 
 #endif  // CORE_FPDFAPI_EDIT_CPDF_CONTENTSTREAM_WRITE_UTILS_H_
diff --git a/core/fpdfapi/edit/cpdf_creator.cpp b/core/fpdfapi/edit/cpdf_creator.cpp
index 65ddc45..9a2b88b 100644
--- a/core/fpdfapi/edit/cpdf_creator.cpp
+++ b/core/fpdfapi/edit/cpdf_creator.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,11 @@
 
 #include "core/fpdfapi/edit/cpdf_creator.h"
 
+#include <stdint.h>
+
 #include <algorithm>
+#include <set>
+#include <utility>
 
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_crypto_handler.h"
@@ -18,14 +22,16 @@
 #include "core/fpdfapi/parser/cpdf_parser.h"
 #include "core/fpdfapi/parser/cpdf_security_handler.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfapi/parser/cpdf_syntax_parser.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fpdfapi/parser/object_tree_traversal_util.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_random.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
 
 namespace {
 
@@ -33,33 +39,27 @@
 
 class CFX_FileBufferArchive final : public IFX_ArchiveStream {
  public:
-  explicit CFX_FileBufferArchive(
-      const RetainPtr<IFX_RetainableWriteStream>& file);
+  explicit CFX_FileBufferArchive(RetainPtr<IFX_RetainableWriteStream> file);
   ~CFX_FileBufferArchive() override;
 
-  bool WriteBlock(const void* pBuf, size_t size) override;
-  bool WriteByte(uint8_t byte) override;
-  bool WriteDWord(uint32_t i) override;
-  bool WriteString(ByteStringView str) override;
-
+  bool WriteBlock(pdfium::span<const uint8_t> buffer) override;
   FX_FILESIZE CurrentOffset() const override { return offset_; }
 
  private:
   bool Flush();
 
-  FX_FILESIZE offset_;
-  size_t current_length_;
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> buffer_;
-  RetainPtr<IFX_RetainableWriteStream> backing_file_;
+  FX_FILESIZE offset_ = 0;
+  DataVector<uint8_t> buffer_;
+  pdfium::span<uint8_t> available_;
+  RetainPtr<IFX_RetainableWriteStream> const backing_file_;
 };
 
 CFX_FileBufferArchive::CFX_FileBufferArchive(
-    const RetainPtr<IFX_RetainableWriteStream>& file)
-    : offset_(0),
-      current_length_(0),
-      buffer_(kArchiveBufferSize),
-      backing_file_(file) {
-  ASSERT(file);
+    RetainPtr<IFX_RetainableWriteStream> file)
+    : buffer_(kArchiveBufferSize),
+      available_(buffer_),
+      backing_file_(std::move(file)) {
+  DCHECK(backing_file_);
 }
 
 CFX_FileBufferArchive::~CFX_FileBufferArchive() {
@@ -67,35 +67,29 @@
 }
 
 bool CFX_FileBufferArchive::Flush() {
-  size_t nRemaining = current_length_;
-  current_length_ = 0;
-  if (!backing_file_)
-    return false;
-  if (!nRemaining)
+  size_t nUsed = buffer_.size() - available_.size();
+  available_ = pdfium::make_span(buffer_);
+  if (!nUsed)
     return true;
-  return backing_file_->WriteBlock(buffer_.data(), nRemaining);
+  return backing_file_->WriteBlock(available_.first(nUsed));
 }
 
-bool CFX_FileBufferArchive::WriteBlock(const void* pBuf, size_t size) {
-  ASSERT(pBuf);
-  ASSERT(size > 0);
+bool CFX_FileBufferArchive::WriteBlock(pdfium::span<const uint8_t> buffer) {
+  if (buffer.empty())
+    return true;
 
-  const uint8_t* buffer = reinterpret_cast<const uint8_t*>(pBuf);
-  size_t temp_size = size;
-  while (temp_size) {
-    size_t buf_size = std::min(kArchiveBufferSize - current_length_, temp_size);
-    memcpy(buffer_.data() + current_length_, buffer, buf_size);
-
-    current_length_ += buf_size;
-    if (current_length_ == kArchiveBufferSize && !Flush())
+  pdfium::span<const uint8_t> src_span = buffer;
+  while (!src_span.empty()) {
+    size_t copy_size = std::min(available_.size(), src_span.size());
+    fxcrt::spancpy(available_, src_span.first(copy_size));
+    src_span = src_span.subspan(copy_size);
+    available_ = available_.subspan(copy_size);
+    if (available_.empty() && !Flush())
       return false;
-
-    temp_size -= buf_size;
-    buffer += buf_size;
   }
 
   FX_SAFE_FILESIZE safe_offset = offset_;
-  safe_offset += size;
+  safe_offset += buffer.size();
   if (!safe_offset.IsValid())
     return false;
 
@@ -103,20 +97,6 @@
   return true;
 }
 
-bool CFX_FileBufferArchive::WriteByte(uint8_t byte) {
-  return WriteBlock(&byte, 1);
-}
-
-bool CFX_FileBufferArchive::WriteDWord(uint32_t i) {
-  char buf[32];
-  FXSYS_itoa(i, buf, 10);
-  return WriteBlock(buf, strlen(buf));
-}
-
-bool CFX_FileBufferArchive::WriteString(ByteStringView str) {
-  return WriteBlock(str.raw_str(), str.GetLength());
-}
-
 ByteString GenerateFileID(uint32_t dwSeed1, uint32_t dwSeed2) {
   uint32_t buffer[4];
   void* pContext1 = FX_Random_MT_Start(dwSeed1);
@@ -141,15 +121,15 @@
 }  // namespace
 
 CPDF_Creator::CPDF_Creator(CPDF_Document* pDoc,
-                           const RetainPtr<IFX_RetainableWriteStream>& archive)
+                           RetainPtr<IFX_RetainableWriteStream> archive)
     : m_pDocument(pDoc),
       m_pParser(pDoc->GetParser()),
       m_pEncryptDict(m_pParser ? m_pParser->GetEncryptDict() : nullptr),
       m_pSecurityHandler(m_pParser ? m_pParser->GetSecurityHandler() : nullptr),
       m_dwLastObjNum(m_pDocument->GetLastObjNum()),
-      m_Archive(pdfium::MakeUnique<CFX_FileBufferArchive>(archive)) {}
+      m_Archive(std::make_unique<CFX_FileBufferArchive>(std::move(archive))) {}
 
-CPDF_Creator::~CPDF_Creator() {}
+CPDF_Creator::~CPDF_Creator() = default;
 
 bool CPDF_Creator::WriteIndirectObj(uint32_t objnum, const CPDF_Object* pObj) {
   if (!m_Archive->WriteDWord(objnum) || !m_Archive->WriteString(" 0 obj\r\n"))
@@ -157,7 +137,7 @@
 
   std::unique_ptr<CPDF_Encryptor> encryptor;
   if (GetCryptoHandler() && pObj != m_pEncryptDict)
-    encryptor = pdfium::MakeUnique<CPDF_Encryptor>(GetCryptoHandler(), objnum);
+    encryptor = std::make_unique<CPDF_Encryptor>(GetCryptoHandler(), objnum);
 
   if (!pObj->WriteTo(m_Archive.get(), encryptor.get()))
     return false;
@@ -172,12 +152,12 @@
   m_ObjectOffsets[objnum] = m_Archive->CurrentOffset();
 
   bool bExistInMap = !!m_pDocument->GetIndirectObject(objnum);
-  CPDF_Object* pObj = m_pDocument->GetOrParseIndirectObject(objnum);
+  RetainPtr<CPDF_Object> pObj = m_pDocument->GetOrParseIndirectObject(objnum);
   if (!pObj) {
     m_ObjectOffsets.erase(objnum);
     return true;
   }
-  if (!WriteIndirectObj(pObj->GetObjNum(), pObj))
+  if (!WriteIndirectObj(pObj->GetObjNum(), pObj.Get()))
     return false;
   if (!bExistInMap)
     m_pDocument->DeleteIndirectObject(objnum);
@@ -185,13 +165,30 @@
 }
 
 bool CPDF_Creator::WriteOldObjs() {
-  uint32_t nLastObjNum = m_pParser->GetLastObjNum();
-  if (!m_pParser->IsValidObjectNumber(nLastObjNum))
+  const uint32_t nLastObjNum = m_pParser->GetLastObjNum();
+  if (!m_pParser->IsValidObjectNumber(nLastObjNum)) {
     return true;
+  }
+  if (m_CurObjNum > nLastObjNum) {
+    return true;
+  }
 
+  const std::set<uint32_t> objects_with_refs =
+      GetObjectsWithReferences(m_pDocument);
+  uint32_t last_object_number_written = 0;
   for (uint32_t objnum = m_CurObjNum; objnum <= nLastObjNum; ++objnum) {
-    if (!WriteOldIndirectObject(objnum))
+    if (!pdfium::Contains(objects_with_refs, objnum)) {
+      continue;
+    }
+    if (!WriteOldIndirectObject(objnum)) {
       return false;
+    }
+    last_object_number_written = objnum;
+  }
+  // If there are no new objects to write, then adjust `m_dwLastObjNum` if
+  // needed to reflect the actual last object number.
+  if (m_NewObjNumArray.empty()) {
+    m_dwLastObjNum = last_object_number_written;
   }
   return true;
 }
@@ -199,12 +196,12 @@
 bool CPDF_Creator::WriteNewObjs() {
   for (size_t i = m_CurObjNum; i < m_NewObjNumArray.size(); ++i) {
     uint32_t objnum = m_NewObjNumArray[i];
-    CPDF_Object* pObj = m_pDocument->GetIndirectObject(objnum);
+    RetainPtr<const CPDF_Object> pObj = m_pDocument->GetIndirectObject(objnum);
     if (!pObj)
       continue;
 
     m_ObjectOffsets[objnum] = m_Archive->CurrentOffset();
-    if (!WriteIndirectObj(pObj->GetObjNum(), pObj))
+    if (!WriteIndirectObj(pObj->GetObjNum(), pObj.Get()))
       return false;
   }
   return true;
@@ -228,13 +225,11 @@
 }
 
 CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage1() {
-  ASSERT(m_iStage > Stage::kInvalid || m_iStage < Stage::kInitWriteObjs20);
+  DCHECK(m_iStage > Stage::kInvalid || m_iStage < Stage::kInitWriteObjs20);
   if (m_iStage == Stage::kInit0) {
     if (!m_pParser || (m_bSecurityChanged && m_IsOriginal))
       m_IsIncremental = false;
 
-    const CPDF_Dictionary* pDict = m_pDocument->GetRoot();
-    m_pMetadata.Reset(pDict ? pDict->GetDirectObjectFor("Metadata") : nullptr);
     m_iStage = Stage::kWriteHeader10;
   }
   if (m_iStage == Stage::kWriteHeader10) {
@@ -254,26 +249,14 @@
       }
       m_iStage = Stage::kInitWriteObjs20;
     } else {
-      m_SavedOffset = m_pParser->GetSyntax()->GetDocumentSize();
+      m_SavedOffset = m_pParser->GetDocumentSize();
       m_iStage = Stage::kWriteIncremental15;
     }
   }
   if (m_iStage == Stage::kWriteIncremental15) {
     if (m_IsOriginal && m_SavedOffset > 0) {
-      static constexpr FX_FILESIZE kBufferSize = 4096;
-      std::vector<uint8_t, FxAllocAllocator<uint8_t>> buffer(kBufferSize);
-      FX_FILESIZE src_size = m_SavedOffset;
-      m_pParser->GetSyntax()->SetPos(0);
-      while (src_size) {
-        const FX_FILESIZE block_size = std::min(kBufferSize, src_size);
-        if (!m_pParser->GetSyntax()->ReadBlock(buffer.data(), block_size)) {
-          return Stage::kInvalid;
-        }
-        if (!m_Archive->WriteBlock(buffer.data(), block_size))
-          return Stage::kInvalid;
-
-        src_size -= block_size;
-      }
+      if (!m_pParser->WriteToArchive(m_Archive.get(), m_SavedOffset))
+        return Stage::kInvalid;
     }
     if (m_IsOriginal && m_pParser->GetLastXRefOffset() == 0) {
       for (uint32_t num = 0; num <= m_pParser->GetLastObjNum(); ++num) {
@@ -290,7 +273,7 @@
 }
 
 CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage2() {
-  ASSERT(m_iStage >= Stage::kInitWriteObjs20 ||
+  DCHECK(m_iStage >= Stage::kInitWriteObjs20 ||
          m_iStage < Stage::kInitWriteXRefs80);
   if (m_iStage == Stage::kInitWriteObjs20) {
     if (!m_IsIncremental && m_pParser) {
@@ -333,7 +316,7 @@
 }
 
 CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage3() {
-  ASSERT(m_iStage >= Stage::kInitWriteXRefs80 ||
+  DCHECK(m_iStage >= Stage::kInitWriteXRefs80 ||
          m_iStage < Stage::kWriteTrailerAndFinish90);
 
   uint32_t dwLastObjNum = m_dwLastObjNum;
@@ -342,7 +325,7 @@
     if (!m_IsIncremental || !m_pParser->IsXRefStream()) {
       if (!m_IsIncremental || m_pParser->GetLastXRefOffset() == 0) {
         ByteString str;
-        str = pdfium::ContainsKey(m_ObjectOffsets, 1)
+        str = pdfium::Contains(m_ObjectOffsets, 1)
                   ? "xref\r\n"
                   : "xref\r\n0 1\r\n0000000000 65535 f\r\n";
         if (!m_Archive->WriteString(str.AsStringView()))
@@ -366,14 +349,14 @@
     uint32_t i = m_CurObjNum;
     uint32_t j;
     while (i <= dwLastObjNum) {
-      while (i <= dwLastObjNum && !pdfium::ContainsKey(m_ObjectOffsets, i))
+      while (i <= dwLastObjNum && !pdfium::Contains(m_ObjectOffsets, i))
         i++;
 
       if (i > dwLastObjNum)
         break;
 
       j = i;
-      while (j <= dwLastObjNum && pdfium::ContainsKey(m_ObjectOffsets, j))
+      while (j <= dwLastObjNum && pdfium::Contains(m_ObjectOffsets, j))
         j++;
 
       if (i == 1)
@@ -396,7 +379,7 @@
   }
   if (m_iStage == Stage::kWriteXrefsIncremental82) {
     ByteString str;
-    uint32_t iCount = pdfium::CollectionSize<uint32_t>(m_NewObjNumArray);
+    uint32_t iCount = fxcrt::CollectionSize<uint32_t>(m_NewObjNumArray);
     uint32_t i = m_CurObjNum;
     while (i < iCount) {
       size_t j = i;
@@ -431,7 +414,7 @@
 }
 
 CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage4() {
-  ASSERT(m_iStage >= Stage::kWriteTrailerAndFinish90);
+  DCHECK(m_iStage >= Stage::kWriteTrailerAndFinish90);
 
   bool bXRefStream = m_IsIncremental && m_pParser->IsXRefStream();
   if (!bXRefStream) {
@@ -445,11 +428,10 @@
   }
 
   if (m_pParser) {
-    RetainPtr<CPDF_Dictionary> p = m_pParser->GetCombinedTrailer();
-    CPDF_DictionaryLocker locker(p.Get());
+    CPDF_DictionaryLocker locker(m_pParser->GetCombinedTrailer());
     for (const auto& it : locker) {
       const ByteString& key = it.first;
-      CPDF_Object* pValue = it.second.Get();
+      const RetainPtr<CPDF_Object>& pValue = it.second;
       if (key == "Encrypt" || key == "Size" || key == "Filter" ||
           key == "Index" || key == "Length" || key == "Prev" || key == "W" ||
           key == "XRefStm" || key == "ID" || key == "DecodeParms" ||
@@ -497,13 +479,7 @@
   if (m_IsIncremental) {
     FX_FILESIZE prev = m_pParser->GetLastXRefOffset();
     if (prev) {
-      if (!m_Archive->WriteString("/Prev "))
-        return Stage::kInvalid;
-
-      char offset_buf[20];
-      memset(offset_buf, 0, sizeof(offset_buf));
-      FXSYS_i64toa(prev, offset_buf, 10);
-      if (!m_Archive->WriteBlock(offset_buf, strlen(offset_buf)))
+      if (!m_Archive->WriteString("/Prev ") || !m_Archive->WriteFilesize(prev))
         return Stage::kInvalid;
     }
   }
@@ -522,7 +498,7 @@
     if (m_IsIncremental && m_pParser && m_pParser->GetLastXRefOffset() == 0) {
       uint32_t i = 0;
       for (i = 0; i < m_dwLastObjNum; i++) {
-        if (!pdfium::ContainsKey(m_ObjectOffsets, i))
+        if (!pdfium::Contains(m_ObjectOffsets, i))
           continue;
         if (!m_Archive->WriteDWord(i) || !m_Archive->WriteString(" 1 "))
           return Stage::kInvalid;
@@ -540,8 +516,8 @@
           return Stage::kInvalid;
       }
     } else {
-      size_t count = m_NewObjNumArray.size();
-      size_t i = 0;
+      int count = fxcrt::CollectionSize<int>(m_NewObjNumArray);
+      int i = 0;
       for (i = 0; i < count; i++) {
         if (!m_Archive->WriteDWord(m_NewObjNumArray[i]) ||
             !m_Archive->WriteString(" 1 ")) {
@@ -562,13 +538,8 @@
       return Stage::kInvalid;
   }
 
-  if (!m_Archive->WriteString("\r\nstartxref\r\n"))
-    return Stage::kInvalid;
-
-  char offset_buf[20];
-  memset(offset_buf, 0, sizeof(offset_buf));
-  FXSYS_i64toa(m_XrefStart, offset_buf, 10);
-  if (!m_Archive->WriteBlock(offset_buf, strlen(offset_buf)) ||
+  if (!m_Archive->WriteString("\r\nstartxref\r\n") ||
+      !m_Archive->WriteFilesize(m_XrefStart) ||
       !m_Archive->WriteString("\r\n%%EOF\r\n")) {
     return Stage::kInvalid;
   }
@@ -591,37 +562,39 @@
 }
 
 void CPDF_Creator::InitID() {
-  ASSERT(!m_pIDArray);
+  DCHECK(!m_pIDArray);
 
   m_pIDArray = pdfium::MakeRetain<CPDF_Array>();
-  const CPDF_Array* pOldIDArray = m_pParser ? m_pParser->GetIDArray() : nullptr;
-  const CPDF_Object* pID1 = pOldIDArray ? pOldIDArray->GetObjectAt(0) : nullptr;
+  RetainPtr<const CPDF_Array> pOldIDArray =
+      m_pParser ? m_pParser->GetIDArray() : nullptr;
+  RetainPtr<const CPDF_Object> pID1 =
+      pOldIDArray ? pOldIDArray->GetObjectAt(0) : nullptr;
   if (pID1) {
-    m_pIDArray->Add(pID1->Clone());
+    m_pIDArray->Append(pID1->Clone());
   } else {
     ByteString bsBuffer =
         GenerateFileID((uint32_t)(uintptr_t)this, m_dwLastObjNum);
-    m_pIDArray->AddNew<CPDF_String>(bsBuffer, true);
+    m_pIDArray->AppendNew<CPDF_String>(bsBuffer, true);
   }
 
   if (pOldIDArray) {
-    const CPDF_Object* pID2 = pOldIDArray->GetObjectAt(1);
+    RetainPtr<const CPDF_Object> pID2 = pOldIDArray->GetObjectAt(1);
     if (m_IsIncremental && m_pEncryptDict && pID2) {
-      m_pIDArray->Add(pID2->Clone());
+      m_pIDArray->Append(pID2->Clone());
       return;
     }
     ByteString bsBuffer =
         GenerateFileID((uint32_t)(uintptr_t)this, m_dwLastObjNum);
-    m_pIDArray->AddNew<CPDF_String>(bsBuffer, true);
+    m_pIDArray->AppendNew<CPDF_String>(bsBuffer, true);
     return;
   }
 
-  m_pIDArray->Add(m_pIDArray->GetObjectAt(0)->Clone());
+  m_pIDArray->Append(m_pIDArray->GetObjectAt(0)->Clone());
   if (m_pEncryptDict) {
-    ASSERT(m_pParser);
+    DCHECK(m_pParser);
     int revision = m_pEncryptDict->GetIntegerFor("R");
     if ((revision == 2 || revision == 3) &&
-        m_pEncryptDict->GetStringFor("Filter") == "Standard") {
+        m_pEncryptDict->GetByteStringFor("Filter") == "Standard") {
       m_pNewEncryptDict = ToDictionary(m_pEncryptDict->Clone());
       m_pEncryptDict = m_pNewEncryptDict;
       m_pSecurityHandler = pdfium::MakeRetain<CPDF_SecurityHandler>();
diff --git a/core/fpdfapi/edit/cpdf_creator.h b/core/fpdfapi/edit/cpdf_creator.h
index d85d8cf..b97dc29 100644
--- a/core/fpdfapi/edit/cpdf_creator.h
+++ b/core/fpdfapi/edit/cpdf_creator.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -29,7 +29,7 @@
 class CPDF_Creator {
  public:
   CPDF_Creator(CPDF_Document* pDoc,
-               const RetainPtr<IFX_RetainableWriteStream>& archive);
+               RetainPtr<IFX_RetainableWriteStream> archive);
   ~CPDF_Creator();
 
   void RemoveSecurity();
@@ -73,11 +73,10 @@
   CPDF_CryptoHandler* GetCryptoHandler();
 
   UnownedPtr<CPDF_Document> const m_pDocument;
-  UnownedPtr<const CPDF_Parser> const m_pParser;
+  UnownedPtr<CPDF_Parser> const m_pParser;
   RetainPtr<const CPDF_Dictionary> m_pEncryptDict;
   RetainPtr<CPDF_Dictionary> m_pNewEncryptDict;
   RetainPtr<CPDF_SecurityHandler> m_pSecurityHandler;
-  RetainPtr<const CPDF_Object> m_pMetadata;
   uint32_t m_dwLastObjNum;
   std::unique_ptr<IFX_ArchiveStream> m_Archive;
   FX_FILESIZE m_SavedOffset = 0;
diff --git a/core/fpdfapi/edit/cpdf_creator_embeddertest.cpp b/core/fpdfapi/edit/cpdf_creator_embeddertest.cpp
index 9d849c1..8dbad0c 100644
--- a/core/fpdfapi/edit/cpdf_creator_embeddertest.cpp
+++ b/core/fpdfapi/edit/cpdf_creator_embeddertest.cpp
@@ -1,13 +1,11 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <cstring>
-#include <memory>
-#include <string>
-#include <vector>
+#include <string.h>
 
-#include "core/fxcrt/fx_system.h"
+#include <string>
+
 #include "public/cpp/fpdf_scopers.h"
 #include "public/fpdf_annot.h"
 #include "public/fpdf_edit.h"
@@ -48,7 +46,7 @@
 }
 
 TEST_F(CPDF_CreatorEmbedderTest, BUG_873) {
-  EXPECT_TRUE(OpenDocument("embedded_attachments.pdf"));
+  ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
 
   // Cannot match second part of the ID since it is randomly generated.
@@ -69,24 +67,24 @@
   FileAccessForTesting file_acc("linearized.pdf");
   FakeFileAccess fake_acc(&file_acc);
 
-  avail_ = FPDFAvail_Create(fake_acc.GetFileAvail(), fake_acc.GetFileAccess());
+  CreateAvail(fake_acc.GetFileAvail(), fake_acc.GetFileAccess());
   while (PDF_DATA_AVAIL !=
-         FPDFAvail_IsDocAvail(avail_, fake_acc.GetDownloadHints())) {
+         FPDFAvail_IsDocAvail(avail(), fake_acc.GetDownloadHints())) {
     fake_acc.SetRequestedDataAvailable();
   }
 
-  document_ = FPDFAvail_GetDocument(avail_, nullptr);
-  ASSERT_TRUE(document_);
+  SetDocumentFromAvail();
+  ASSERT_TRUE(document());
 
   // Load second page, to parse additional crossref sections.
   while (PDF_DATA_AVAIL !=
-         FPDFAvail_IsPageAvail(avail_, 1, fake_acc.GetDownloadHints())) {
+         FPDFAvail_IsPageAvail(avail(), 1, fake_acc.GetDownloadHints())) {
     fake_acc.SetRequestedDataAvailable();
   }
   // Simulate downloading of whole file.
   fake_acc.SetWholeFileAvailable();
   // Save document.
-  EXPECT_TRUE(FPDF_SaveAsCopy(document_, this, 0));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   const std::string saved_doc = GetString();
 
   EXPECT_THAT(saved_doc, ::testing::HasSubstr("/Info"));
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
index 77d23a2..be87afa 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,9 +9,11 @@
 #include <map>
 #include <memory>
 #include <set>
+#include <sstream>
 #include <tuple>
 #include <utility>
 
+#include "constants/page_object.h"
 #include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
 #include "core/fpdfapi/edit/cpdf_pagecontentmanager.h"
 #include "core/fpdfapi/edit/cpdf_stringarchivestream.h"
@@ -19,6 +21,8 @@
 #include "core/fpdfapi/font/cpdf_type1font.h"
 #include "core/fpdfapi/page/cpdf_contentmarks.h"
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
+#include "core/fpdfapi/page/cpdf_form.h"
+#include "core/fpdfapi/page/cpdf_formobject.h"
 #include "core/fpdfapi/page/cpdf_image.h"
 #include "core/fpdfapi/page/cpdf_imageobject.h"
 #include "core/fpdfapi/page/cpdf_page.h"
@@ -34,11 +38,19 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fpdfapi/parser/object_tree_traversal_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/containers/span.h"
+#include "third_party/base/notreached.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
 
+// Key: The resource type.
+// Value: The resource names of a given type.
+using ResourcesMap = std::map<ByteString, std::set<ByteString>>;
+
 bool GetColor(const CPDF_Color* pColor, float* rgb) {
   int intRGB[3];
   if (!pColor || !pColor->IsColorSpaceRGB() ||
@@ -51,6 +63,67 @@
   return true;
 }
 
+void RecordPageObjectResourceUsage(const CPDF_PageObject* page_object,
+                                   ResourcesMap& seen_resources) {
+  const ByteString& resource_name = page_object->GetResourceName();
+  if (!resource_name.IsEmpty()) {
+    switch (page_object->GetType()) {
+      case CPDF_PageObject::Type::kText:
+        seen_resources["Font"].insert(resource_name);
+        break;
+      case CPDF_PageObject::Type::kImage:
+      case CPDF_PageObject::Type::kForm:
+        seen_resources["XObject"].insert(resource_name);
+        break;
+      case CPDF_PageObject::Type::kPath:
+        break;
+      case CPDF_PageObject::Type::kShading:
+        break;
+    }
+  }
+  for (const auto& name : page_object->GetGraphicsResourceNames()) {
+    CHECK(!name.IsEmpty());
+    seen_resources["ExtGState"].insert(name);
+  }
+}
+
+void RemoveUnusedResources(RetainPtr<CPDF_Dictionary> resources_dict,
+                           const ResourcesMap& resources_in_use) {
+  // TODO(thestig): Remove other unused resource types:
+  // - ColorSpace
+  // - Pattern
+  // - Shading
+  static constexpr const char* kResourceKeys[] = {"ExtGState", "Font",
+                                                  "XObject"};
+  for (const char* resource_key : kResourceKeys) {
+    RetainPtr<CPDF_Dictionary> resource_dict =
+        resources_dict->GetMutableDictFor(resource_key);
+    if (!resource_dict) {
+      continue;
+    }
+
+    std::vector<ByteString> keys;
+    {
+      CPDF_DictionaryLocker resource_dict_locker(resource_dict);
+      for (auto& it : resource_dict_locker) {
+        keys.push_back(it.first);
+      }
+    }
+
+    auto it = resources_in_use.find(resource_key);
+    const std::set<ByteString>* resource_in_use_of_current_type =
+        it != resources_in_use.end() ? &it->second : nullptr;
+    for (const ByteString& key : keys) {
+      if (resource_in_use_of_current_type &&
+          pdfium::Contains(*resource_in_use_of_current_type, key)) {
+        continue;
+      }
+
+      resource_dict->RemoveFor(key.AsStringView());
+    }
+  }
+}
+
 }  // namespace
 
 CPDF_PageContentGenerator::CPDF_PageContentGenerator(
@@ -62,22 +135,23 @@
   }
 }
 
-CPDF_PageContentGenerator::~CPDF_PageContentGenerator() {}
+CPDF_PageContentGenerator::~CPDF_PageContentGenerator() = default;
 
 void CPDF_PageContentGenerator::GenerateContent() {
-  ASSERT(m_pObjHolder->IsPage());
-
-  std::map<int32_t, std::unique_ptr<std::ostringstream>> stream =
+  DCHECK(m_pObjHolder->IsPage());
+  std::map<int32_t, fxcrt::ostringstream> new_stream_data =
       GenerateModifiedStreams();
+  // If no streams were regenerated or removed, nothing to do here.
+  if (new_stream_data.empty()) {
+    return;
+  }
 
-  UpdateContentStreams(&stream);
+  UpdateContentStreams(std::move(new_stream_data));
+  UpdateResourcesDict();
 }
 
-std::map<int32_t, std::unique_ptr<std::ostringstream>>
+std::map<int32_t, fxcrt::ostringstream>
 CPDF_PageContentGenerator::GenerateModifiedStreams() {
-  // Make sure default graphics are created.
-  GetOrCreateDefaultGraphics();
-
   // Figure out which streams are dirty.
   std::set<int32_t> all_dirty_streams;
   for (auto& pPageObj : m_pageObjects) {
@@ -89,23 +163,21 @@
                            marked_dirty_streams.end());
 
   // Start regenerating dirty streams.
-  std::map<int32_t, std::unique_ptr<std::ostringstream>> streams;
+  std::map<int32_t, fxcrt::ostringstream> streams;
   std::set<int32_t> empty_streams;
   std::unique_ptr<const CPDF_ContentMarks> empty_content_marks =
-      pdfium::MakeUnique<CPDF_ContentMarks>();
+      std::make_unique<CPDF_ContentMarks>();
   std::map<int32_t, const CPDF_ContentMarks*> current_content_marks;
 
   for (int32_t dirty_stream : all_dirty_streams) {
-    std::unique_ptr<std::ostringstream> buf =
-        pdfium::MakeUnique<std::ostringstream>();
+    fxcrt::ostringstream buf;
 
     // Set the default graphic state values
-    *buf << "q\n";
+    buf << "q\n";
     if (!m_pObjHolder->GetLastCTM().IsIdentity())
-      *buf << m_pObjHolder->GetLastCTM().GetInverse() << " cm\n";
+      WriteMatrix(buf, m_pObjHolder->GetLastCTM().GetInverse()) << " cm\n";
 
-    ProcessDefaultGraphics(buf.get());
-
+    ProcessDefaultGraphics(&buf);
     streams[dirty_stream] = std::move(buf);
     empty_streams.insert(dirty_stream);
     current_content_marks[dirty_stream] = empty_content_marks.get();
@@ -118,17 +190,17 @@
     if (it == streams.end())
       continue;
 
-    std::ostringstream* buf = it->second.get();
+    fxcrt::ostringstream* buf = &it->second;
     empty_streams.erase(stream_index);
-    current_content_marks[stream_index] = ProcessContentMarks(
-        buf, pPageObj.Get(), current_content_marks[stream_index]);
-    ProcessPageObject(buf, pPageObj.Get());
+    current_content_marks[stream_index] =
+        ProcessContentMarks(buf, pPageObj, current_content_marks[stream_index]);
+    ProcessPageObject(buf, pPageObj);
   }
 
   // Finish dirty streams.
   for (int32_t dirty_stream : all_dirty_streams) {
-    std::ostringstream* buf = streams[dirty_stream].get();
-    if (pdfium::ContainsKey(empty_streams, dirty_stream)) {
+    fxcrt::ostringstream* buf = &streams[dirty_stream];
+    if (pdfium::Contains(empty_streams, dirty_stream)) {
       // Clear to show that this stream needs to be deleted.
       buf->str("");
     } else {
@@ -143,70 +215,91 @@
 }
 
 void CPDF_PageContentGenerator::UpdateContentStreams(
-    std::map<int32_t, std::unique_ptr<std::ostringstream>>* new_stream_data) {
-  // If no streams were regenerated or removed, nothing to do here.
-  if (new_stream_data->empty())
-    return;
+    std::map<int32_t, fxcrt::ostringstream>&& new_stream_data) {
+  CHECK(!new_stream_data.empty());
 
-  CPDF_PageContentManager page_content_manager(m_pObjHolder.Get());
+  // Make sure default graphics are created.
+  m_DefaultGraphicsName = GetOrCreateDefaultGraphics();
 
-  for (auto& pair : *new_stream_data) {
+  CPDF_PageContentManager page_content_manager(m_pObjHolder, m_pDocument);
+  for (auto& pair : new_stream_data) {
     int32_t stream_index = pair.first;
-    std::ostringstream* buf = pair.second.get();
+    fxcrt::ostringstream* buf = &pair.second;
 
     if (stream_index == CPDF_PageObject::kNoContentStream) {
-      int new_stream_index = page_content_manager.AddStream(buf);
+      int new_stream_index =
+          pdfium::base::checked_cast<int>(page_content_manager.AddStream(buf));
       UpdateStreamlessPageObjects(new_stream_index);
       continue;
     }
 
-    CPDF_Stream* old_stream =
-        page_content_manager.GetStreamByIndex(stream_index);
-    ASSERT(old_stream);
+    page_content_manager.UpdateStream(stream_index, buf);
+  }
+}
 
-    // If buf is now empty, remove the stream instead of setting the data.
-    if (buf->tellp() <= 0)
-      page_content_manager.ScheduleRemoveStreamByIndex(stream_index);
-    else
-      old_stream->SetDataFromStringstreamAndRemoveFilter(buf);
+void CPDF_PageContentGenerator::UpdateResourcesDict() {
+  RetainPtr<CPDF_Dictionary> resources = m_pObjHolder->GetMutableResources();
+  if (!resources) {
+    return;
   }
 
-  page_content_manager.ExecuteScheduledRemovals();
+  const uint32_t resources_object_number = resources->GetObjNum();
+  if (resources_object_number) {
+    // If `resources` is not an inline object, then do not modify it directly if
+    // it has multiple references.
+    if (pdfium::Contains(GetObjectsWithMultipleReferences(m_pDocument),
+                         resources_object_number)) {
+      resources = pdfium::WrapRetain(resources->Clone()->AsMutableDictionary());
+      const uint32_t clone_object_number =
+          m_pDocument->AddIndirectObject(resources);
+      m_pObjHolder->SetResources(resources);
+      m_pObjHolder->GetMutableDict()->SetNewFor<CPDF_Reference>(
+          pdfium::page_object::kResources, m_pDocument, clone_object_number);
+    }
+  }
+
+  ResourcesMap seen_resources;
+  for (auto& page_object : m_pageObjects) {
+    RecordPageObjectResourceUsage(page_object, seen_resources);
+  }
+  if (!m_DefaultGraphicsName.IsEmpty()) {
+    seen_resources["ExtGState"].insert(m_DefaultGraphicsName);
+  }
+
+  RemoveUnusedResources(std::move(resources), seen_resources);
 }
 
 ByteString CPDF_PageContentGenerator::RealizeResource(
     const CPDF_Object* pResource,
     const ByteString& bsType) const {
-  ASSERT(pResource);
-  if (!m_pObjHolder->m_pResources) {
-    m_pObjHolder->m_pResources.Reset(
-        m_pDocument->NewIndirect<CPDF_Dictionary>());
-    m_pObjHolder->GetDict()->SetNewFor<CPDF_Reference>(
-        "Resources", m_pDocument.Get(),
-        m_pObjHolder->m_pResources->GetObjNum());
+  DCHECK(pResource);
+  if (!m_pObjHolder->GetResources()) {
+    m_pObjHolder->SetResources(m_pDocument->NewIndirect<CPDF_Dictionary>());
+    m_pObjHolder->GetMutableDict()->SetNewFor<CPDF_Reference>(
+        pdfium::page_object::kResources, m_pDocument,
+        m_pObjHolder->GetResources()->GetObjNum());
   }
-  CPDF_Dictionary* pResList = m_pObjHolder->m_pResources->GetDictFor(bsType);
-  if (!pResList)
-    pResList = m_pObjHolder->m_pResources->SetNewFor<CPDF_Dictionary>(bsType);
 
+  RetainPtr<CPDF_Dictionary> pResList =
+      m_pObjHolder->GetMutableResources()->GetOrCreateDictFor(bsType);
   ByteString name;
   int idnum = 1;
-  while (1) {
+  while (true) {
     name = ByteString::Format("FX%c%d", bsType[0], idnum);
     if (!pResList->KeyExist(name))
       break;
 
     idnum++;
   }
-  pResList->SetNewFor<CPDF_Reference>(name, m_pDocument.Get(),
+  pResList->SetNewFor<CPDF_Reference>(name, m_pDocument,
                                       pResource->GetObjNum());
   return name;
 }
 
-bool CPDF_PageContentGenerator::ProcessPageObjects(std::ostringstream* buf) {
+bool CPDF_PageContentGenerator::ProcessPageObjects(fxcrt::ostringstream* buf) {
   bool bDirty = false;
   std::unique_ptr<const CPDF_ContentMarks> empty_content_marks =
-      pdfium::MakeUnique<CPDF_ContentMarks>();
+      std::make_unique<CPDF_ContentMarks>();
   const CPDF_ContentMarks* content_marks = empty_content_marks.get();
 
   for (auto& pPageObj : m_pageObjects) {
@@ -214,8 +307,8 @@
       continue;
 
     bDirty = true;
-    content_marks = ProcessContentMarks(buf, pPageObj.Get(), content_marks);
-    ProcessPageObject(buf, pPageObj.Get());
+    content_marks = ProcessContentMarks(buf, pPageObj, content_marks);
+    ProcessPageObject(buf, pPageObj);
   }
   FinishMarks(buf, content_marks);
   return bDirty;
@@ -230,12 +323,11 @@
 }
 
 const CPDF_ContentMarks* CPDF_PageContentGenerator::ProcessContentMarks(
-    std::ostringstream* buf,
+    fxcrt::ostringstream* buf,
     const CPDF_PageObject* pPageObj,
     const CPDF_ContentMarks* pPrev) {
-  const CPDF_ContentMarks* pNext = &pPageObj->m_ContentMarks;
-
-  size_t first_different = pPrev->FindFirstDifference(pNext);
+  const CPDF_ContentMarks* pNext = pPageObj->GetContentMarks();
+  const size_t first_different = pPrev->FindFirstDifference(pNext);
 
   // Close all marks that are in prev but not in next.
   // Technically we should iterate backwards to close from the top to the
@@ -269,9 +361,8 @@
         *buf << "/" << item->GetPropertyName() << " ";
         break;
       }
-      default:
-        NOTREACHED();
-        break;
+      case CPDF_ContentMarkItem::kNone:
+        NOTREACHED_NORETURN();
     }
 
     // Write BDC (begin dictionary content) operator.
@@ -282,7 +373,7 @@
 }
 
 void CPDF_PageContentGenerator::FinishMarks(
-    std::ostringstream* buf,
+    fxcrt::ostringstream* buf,
     const CPDF_ContentMarks* pContentMarks) {
   // Technically we should iterate backwards to close from the top to the
   // bottom, but since the EMC operators do not identify which mark they are
@@ -291,10 +382,12 @@
     *buf << "EMC\n";
 }
 
-void CPDF_PageContentGenerator::ProcessPageObject(std::ostringstream* buf,
+void CPDF_PageContentGenerator::ProcessPageObject(fxcrt::ostringstream* buf,
                                                   CPDF_PageObject* pPageObj) {
   if (CPDF_ImageObject* pImageObject = pPageObj->AsImage())
     ProcessImage(buf, pImageObject);
+  else if (CPDF_FormObject* pFormObj = pPageObj->AsForm())
+    ProcessForm(buf, pFormObj);
   else if (CPDF_PathObject* pPathObj = pPageObj->AsPath())
     ProcessPath(buf, pPathObj);
   else if (CPDF_TextObject* pTextObj = pPageObj->AsText())
@@ -302,36 +395,59 @@
   pPageObj->SetDirty(false);
 }
 
-void CPDF_PageContentGenerator::ProcessImage(std::ostringstream* buf,
+void CPDF_PageContentGenerator::ProcessImage(fxcrt::ostringstream* buf,
                                              CPDF_ImageObject* pImageObj) {
   if ((pImageObj->matrix().a == 0 && pImageObj->matrix().b == 0) ||
       (pImageObj->matrix().c == 0 && pImageObj->matrix().d == 0)) {
     return;
   }
-  *buf << "q " << pImageObj->matrix() << " cm ";
 
   RetainPtr<CPDF_Image> pImage = pImageObj->GetImage();
   if (pImage->IsInline())
     return;
 
-  CPDF_Stream* pStream = pImage->GetStream();
+  RetainPtr<const CPDF_Stream> pStream = pImage->GetStream();
   if (!pStream)
     return;
 
+  *buf << "q ";
+  WriteMatrix(*buf, pImageObj->matrix()) << " cm ";
+
   bool bWasInline = pStream->IsInline();
   if (bWasInline)
     pImage->ConvertStreamToIndirectObject();
 
   ByteString name = RealizeResource(pStream, "XObject");
+  pImageObj->SetResourceName(name);
+
   if (bWasInline) {
-    auto* pPageData = CPDF_DocPageData::FromDocument(m_pDocument.Get());
+    auto* pPageData = CPDF_DocPageData::FromDocument(m_pDocument);
     pImageObj->SetImage(pPageData->GetImage(pStream->GetObjNum()));
   }
 
   *buf << "/" << PDF_NameEncode(name) << " Do Q\n";
 }
 
-// Processing path with operators from Tables 4.9 and 4.10 of PDF spec 1.7:
+void CPDF_PageContentGenerator::ProcessForm(fxcrt::ostringstream* buf,
+                                            CPDF_FormObject* pFormObj) {
+  if ((pFormObj->form_matrix().a == 0 && pFormObj->form_matrix().b == 0) ||
+      (pFormObj->form_matrix().c == 0 && pFormObj->form_matrix().d == 0)) {
+    return;
+  }
+
+  RetainPtr<const CPDF_Stream> pStream = pFormObj->form()->GetStream();
+  if (!pStream)
+    return;
+
+  ByteString name = RealizeResource(pStream.Get(), "XObject");
+  pFormObj->SetResourceName(name);
+
+  *buf << "q\n";
+  WriteMatrix(*buf, pFormObj->form_matrix()) << " cm ";
+  *buf << "/" << PDF_NameEncode(name) << " Do Q\n";
+}
+
+// Processing path construction with operators from Table 4.9 of PDF spec 1.7:
 // "re" appends a rectangle (here, used only if the whole path is a rectangle)
 // "m" moves current point to the given coordinates
 // "l" creates a line from current point to the new point
@@ -339,49 +455,56 @@
 // points as the Bezier control points
 // Note: "l", "c" change the current point
 // "h" closes the subpath (appends a line from current to starting point)
+void CPDF_PageContentGenerator::ProcessPathPoints(fxcrt::ostringstream* buf,
+                                                  CPDF_Path* pPath) {
+  pdfium::span<const CFX_Path::Point> points = pPath->GetPoints();
+  if (pPath->IsRect()) {
+    CFX_PointF diff = points[2].m_Point - points[0].m_Point;
+    WritePoint(*buf, points[0].m_Point) << " ";
+    WritePoint(*buf, diff) << " re";
+    return;
+  }
+  for (size_t i = 0; i < points.size(); ++i) {
+    if (i > 0)
+      *buf << " ";
+
+    WritePoint(*buf, points[i].m_Point);
+
+    CFX_Path::Point::Type point_type = points[i].m_Type;
+    if (point_type == CFX_Path::Point::Type::kMove) {
+      *buf << " m";
+    } else if (point_type == CFX_Path::Point::Type::kLine) {
+      *buf << " l";
+    } else if (point_type == CFX_Path::Point::Type::kBezier) {
+      if (i + 2 >= points.size() ||
+          !points[i].IsTypeAndOpen(CFX_Path::Point::Type::kBezier) ||
+          !points[i + 1].IsTypeAndOpen(CFX_Path::Point::Type::kBezier) ||
+          points[i + 2].m_Type != CFX_Path::Point::Type::kBezier) {
+        // If format is not supported, close the path and paint
+        *buf << " h";
+        break;
+      }
+      *buf << " ";
+      WritePoint(*buf, points[i + 1].m_Point) << " ";
+      WritePoint(*buf, points[i + 2].m_Point) << " c";
+      i += 2;
+    }
+    if (points[i].m_CloseFigure)
+      *buf << " h";
+  }
+}
+
+// Processing path painting with operators from Table 4.10 of PDF spec 1.7:
 // Path painting operators: "S", "n", "B", "f", "B*", "f*", depending on
 // the filling mode and whether we want stroking the path or not.
 // "Q" restores the graphics state imposed by the ProcessGraphics method.
-void CPDF_PageContentGenerator::ProcessPath(std::ostringstream* buf,
+void CPDF_PageContentGenerator::ProcessPath(fxcrt::ostringstream* buf,
                                             CPDF_PathObject* pPathObj) {
   ProcessGraphics(buf, pPathObj);
 
-  *buf << pPathObj->matrix() << " cm ";
+  WriteMatrix(*buf, pPathObj->matrix()) << " cm ";
+  ProcessPathPoints(buf, &pPathObj->path());
 
-  const auto& pPoints = pPathObj->path().GetPoints();
-  if (pPathObj->path().IsRect()) {
-    CFX_PointF diff = pPoints[2].m_Point - pPoints[0].m_Point;
-    *buf << pPoints[0].m_Point << " " << diff << " re";
-  } else {
-    for (size_t i = 0; i < pPoints.size(); i++) {
-      if (i > 0)
-        *buf << " ";
-
-      *buf << pPoints[i].m_Point;
-
-      FXPT_TYPE pointType = pPoints[i].m_Type;
-      if (pointType == FXPT_TYPE::MoveTo) {
-        *buf << " m";
-      } else if (pointType == FXPT_TYPE::LineTo) {
-        *buf << " l";
-      } else if (pointType == FXPT_TYPE::BezierTo) {
-        if (i + 2 >= pPoints.size() ||
-            !pPoints[i].IsTypeAndOpen(FXPT_TYPE::BezierTo) ||
-            !pPoints[i + 1].IsTypeAndOpen(FXPT_TYPE::BezierTo) ||
-            pPoints[i + 2].m_Type != FXPT_TYPE::BezierTo) {
-          // If format is not supported, close the path and paint
-          *buf << " h";
-          break;
-        }
-        *buf << " ";
-        *buf << pPoints[i + 1].m_Point << " ";
-        *buf << pPoints[i + 2].m_Point << " c";
-        i += 2;
-      }
-      if (pPoints[i].m_CloseFigure)
-        *buf << " h";
-    }
-  }
   if (pPathObj->has_no_filltype())
     *buf << (pPathObj->stroke() ? " S" : " n");
   else if (pPathObj->has_winding_filltype())
@@ -398,8 +521,10 @@
 // "rg" sets the fill color, "RG" sets the stroke color (using DefaultRGB)
 // "w" sets the stroke line width.
 // "ca" sets the fill alpha, "CA" sets the stroke alpha.
+// "W" and "W*" modify the clipping path using the nonzero winding rule and
+// even-odd rules, respectively.
 // "q" saves the graphics state, so that the settings can later be reversed
-void CPDF_PageContentGenerator::ProcessGraphics(std::ostringstream* buf,
+void CPDF_PageContentGenerator::ProcessGraphics(fxcrt::ostringstream* buf,
                                                 CPDF_PageObject* pPageObj) {
   *buf << "q ";
   float fillColor[3];
@@ -416,11 +541,45 @@
   if (lineWidth != 1.0f)
     WriteFloat(*buf, lineWidth) << " w ";
   CFX_GraphStateData::LineCap lineCap = pPageObj->m_GraphState.GetLineCap();
-  if (lineCap != CFX_GraphStateData::LineCapButt)
+  if (lineCap != CFX_GraphStateData::LineCap::kButt)
     *buf << static_cast<int>(lineCap) << " J ";
   CFX_GraphStateData::LineJoin lineJoin = pPageObj->m_GraphState.GetLineJoin();
-  if (lineJoin != CFX_GraphStateData::LineJoinMiter)
+  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()) {
+    for (size_t i = 0; i < clip_path.GetPathCount(); ++i) {
+      CPDF_Path path = clip_path.GetPath(i);
+      ProcessPathPoints(buf, &path);
+      switch (clip_path.GetClipType(i)) {
+        case CFX_FillRenderOptions::FillType::kWinding:
+          *buf << " W ";
+          break;
+        case CFX_FillRenderOptions::FillType::kEvenOdd:
+          *buf << " W* ";
+          break;
+        case CFX_FillRenderOptions::FillType::kNoFill:
+          NOTREACHED_NORETURN();
+      }
+
+      // Use a no-op path-painting operator to terminate the path without
+      // causing any marks to be placed on the page.
+      *buf << "n ";
+    }
+  }
 
   GraphicsData graphD;
   graphD.fillAlpha = pPageObj->m_GeneralState.GetFillAlpha();
@@ -432,9 +591,10 @@
   }
 
   ByteString name;
-  auto it = m_pObjHolder->m_GraphicsMap.find(graphD);
-  if (it != m_pObjHolder->m_GraphicsMap.end()) {
-    name = it->second;
+  absl::optional<ByteString> maybe_name =
+      m_pObjHolder->GraphicsMapSearch(graphD);
+  if (maybe_name.has_value()) {
+    name = std::move(maybe_name.value());
   } else {
     auto gsDict = pdfium::MakeRetain<CPDF_Dictionary>();
     if (graphD.fillAlpha != 1.0f)
@@ -447,20 +607,21 @@
       gsDict->SetNewFor<CPDF_Name>("BM",
                                    pPageObj->m_GeneralState.GetBlendMode());
     }
-    CPDF_Object* pDict = m_pDocument->AddIndirectObject(gsDict);
-    name = RealizeResource(pDict, "ExtGState");
-    m_pObjHolder->m_GraphicsMap[graphD] = name;
+    m_pDocument->AddIndirectObject(gsDict);
+    name = RealizeResource(std::move(gsDict), "ExtGState");
+    pPageObj->SetGraphicsResourceNames({name});
+    m_pObjHolder->GraphicsMapInsert(graphD, name);
   }
   *buf << "/" << PDF_NameEncode(name) << " gs ";
 }
 
 void CPDF_PageContentGenerator::ProcessDefaultGraphics(
-    std::ostringstream* buf) {
+    fxcrt::ostringstream* buf) {
   *buf << "0 0 0 RG 0 0 0 rg 1 w "
-       << static_cast<int>(CFX_GraphStateData::LineCapButt) << " J "
-       << static_cast<int>(CFX_GraphStateData::LineJoinMiter) << " j\n";
-  ByteString name = GetOrCreateDefaultGraphics();
-  *buf << "/" << PDF_NameEncode(name) << " gs ";
+       << static_cast<int>(CFX_GraphStateData::LineCap::kButt) << " J "
+       << static_cast<int>(CFX_GraphStateData::LineJoin::kMiter) << " j\n";
+  m_DefaultGraphicsName = GetOrCreateDefaultGraphics();
+  *buf << "/" << PDF_NameEncode(m_DefaultGraphicsName) << " gs ";
 }
 
 ByteString CPDF_PageContentGenerator::GetOrCreateDefaultGraphics() const {
@@ -468,34 +629,35 @@
   defaultGraphics.fillAlpha = 1.0f;
   defaultGraphics.strokeAlpha = 1.0f;
   defaultGraphics.blendType = BlendMode::kNormal;
-  auto it = m_pObjHolder->m_GraphicsMap.find(defaultGraphics);
 
-  // If default graphics already exists, return it.
-  if (it != m_pObjHolder->m_GraphicsMap.end())
-    return it->second;
+  absl::optional<ByteString> maybe_name =
+      m_pObjHolder->GraphicsMapSearch(defaultGraphics);
+  if (maybe_name.has_value())
+    return maybe_name.value();
 
-  // Otherwise, create them.
   auto gsDict = pdfium::MakeRetain<CPDF_Dictionary>();
   gsDict->SetNewFor<CPDF_Number>("ca", defaultGraphics.fillAlpha);
   gsDict->SetNewFor<CPDF_Number>("CA", defaultGraphics.strokeAlpha);
   gsDict->SetNewFor<CPDF_Name>("BM", "Normal");
-  CPDF_Object* pDict = m_pDocument->AddIndirectObject(gsDict);
-  ByteString name = RealizeResource(pDict, "ExtGState");
-  m_pObjHolder->m_GraphicsMap[defaultGraphics] = name;
+  m_pDocument->AddIndirectObject(gsDict);
+  ByteString name = RealizeResource(std::move(gsDict), "ExtGState");
+  m_pObjHolder->GraphicsMapInsert(defaultGraphics, name);
   return name;
 }
 
 // This method adds text to the buffer, BT begins the text object, ET ends it.
 // Tm sets the text matrix (allows positioning and transforming text).
 // Tf sets the font name (from Font in Resources) and font size.
+// Tr sets the text rendering mode.
 // Tj sets the actual text, <####...> is used when specifying charcodes.
-void CPDF_PageContentGenerator::ProcessText(std::ostringstream* buf,
+void CPDF_PageContentGenerator::ProcessText(fxcrt::ostringstream* buf,
                                             CPDF_TextObject* pTextObj) {
   ProcessGraphics(buf, pTextObj);
-  *buf << "BT " << pTextObj->GetTextMatrix() << " Tm ";
+  *buf << "BT ";
+  WriteMatrix(*buf, pTextObj->GetTextMatrix()) << " Tm ";
   RetainPtr<CPDF_Font> pFont(pTextObj->GetFont());
   if (!pFont)
-    pFont = CPDF_Font::GetStockFont(m_pDocument.Get(), "Helvetica");
+    pFont = CPDF_Font::GetStockFont(m_pDocument, "Helvetica");
 
   FontData data;
   const CPDF_FontEncoding* pEncoding = nullptr;
@@ -511,12 +673,13 @@
     return;
   }
   data.baseFont = pFont->GetBaseFontName();
-  auto it = m_pObjHolder->m_FontsMap.find(data);
-  ByteString dictName;
-  if (it != m_pObjHolder->m_FontsMap.end()) {
-    dictName = it->second;
+
+  ByteString dict_name;
+  absl::optional<ByteString> maybe_name = m_pObjHolder->FontsMapSearch(data);
+  if (maybe_name.has_value()) {
+    dict_name = std::move(maybe_name.value());
   } else {
-    CPDF_Object* pIndirectFont = pFont->GetFontDict();
+    RetainPtr<const CPDF_Object> pIndirectFont = pFont->GetFontDict();
     if (pIndirectFont->IsInline()) {
       // In this case we assume it must be a standard font
       auto pFontDict = pdfium::MakeRetain<CPDF_Dictionary>();
@@ -527,18 +690,22 @@
         pFontDict->SetFor("Encoding",
                           pEncoding->Realize(m_pDocument->GetByteStringPool()));
       }
-      pIndirectFont = m_pDocument->AddIndirectObject(pFontDict);
+      m_pDocument->AddIndirectObject(pFontDict);
+      pIndirectFont = std::move(pFontDict);
     }
-    dictName = RealizeResource(pIndirectFont, "Font");
-    m_pObjHolder->m_FontsMap[data] = dictName;
+    dict_name = RealizeResource(std::move(pIndirectFont), "Font");
+    m_pObjHolder->FontsMapInsert(data, dict_name);
   }
-  *buf << "/" << PDF_NameEncode(dictName) << " ";
+  pTextObj->SetResourceName(dict_name);
+
+  *buf << "/" << PDF_NameEncode(dict_name) << " ";
   WriteFloat(*buf, pTextObj->GetFontSize()) << " Tf ";
+  *buf << static_cast<int>(pTextObj->GetTextRenderMode()) << " Tr ";
   ByteString text;
   for (uint32_t charcode : pTextObj->GetCharCodes()) {
     if (charcode != CPDF_Font::kInvalidCharCode)
       pFont->AppendChar(&text, charcode);
   }
-  *buf << PDF_EncodeString(text, true) << " Tj ET";
+  *buf << PDF_HexEncodeString(text.AsStringView()) << " Tj ET";
   *buf << " Q\n";
 }
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.h b/core/fpdfapi/edit/cpdf_pagecontentgenerator.h
index 40d19ae..06bb239 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.h
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,21 +7,23 @@
 #ifndef CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTGENERATOR_H_
 #define CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTGENERATOR_H_
 
+#include <stdint.h>
+
 #include <map>
-#include <memory>
-#include <sstream>
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/fx_string_wrappers.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_ContentMarks;
 class CPDF_Document;
+class CPDF_FormObject;
 class CPDF_ImageObject;
 class CPDF_Object;
 class CPDF_PageObject;
 class CPDF_PageObjectHolder;
+class CPDF_Path;
 class CPDF_PathObject;
 class CPDF_TextObject;
 
@@ -31,43 +33,50 @@
   ~CPDF_PageContentGenerator();
 
   void GenerateContent();
-  bool ProcessPageObjects(std::ostringstream* buf);
+  bool ProcessPageObjects(fxcrt::ostringstream* buf);
 
  private:
   friend class CPDF_PageContentGeneratorTest;
 
-  void ProcessPageObject(std::ostringstream* buf, CPDF_PageObject* pPageObj);
-  void ProcessPath(std::ostringstream* buf, CPDF_PathObject* pPathObj);
-  void ProcessImage(std::ostringstream* buf, CPDF_ImageObject* pImageObj);
-  void ProcessGraphics(std::ostringstream* buf, CPDF_PageObject* pPageObj);
-  void ProcessDefaultGraphics(std::ostringstream* buf);
-  void ProcessText(std::ostringstream* buf, CPDF_TextObject* pTextObj);
+  void ProcessPageObject(fxcrt::ostringstream* buf, CPDF_PageObject* pPageObj);
+  void ProcessPathPoints(fxcrt::ostringstream* buf, CPDF_Path* pPath);
+  void ProcessPath(fxcrt::ostringstream* buf, CPDF_PathObject* pPathObj);
+  void ProcessForm(fxcrt::ostringstream* buf, CPDF_FormObject* pFormObj);
+  void ProcessImage(fxcrt::ostringstream* buf, CPDF_ImageObject* pImageObj);
+  void ProcessGraphics(fxcrt::ostringstream* buf, CPDF_PageObject* pPageObj);
+  void ProcessDefaultGraphics(fxcrt::ostringstream* buf);
+  void ProcessText(fxcrt::ostringstream* buf, CPDF_TextObject* pTextObj);
   ByteString GetOrCreateDefaultGraphics() const;
   ByteString RealizeResource(const CPDF_Object* pResource,
                              const ByteString& bsType) const;
-  const CPDF_ContentMarks* ProcessContentMarks(std::ostringstream* buf,
+  const CPDF_ContentMarks* ProcessContentMarks(fxcrt::ostringstream* buf,
                                                const CPDF_PageObject* pPageObj,
                                                const CPDF_ContentMarks* pPrev);
-  void FinishMarks(std::ostringstream* buf,
+  void FinishMarks(fxcrt::ostringstream* buf,
                    const CPDF_ContentMarks* pContentMarks);
 
   // Returns a map from content stream index to new stream data. Unmodified
   // streams are not touched.
-  std::map<int32_t, std::unique_ptr<std::ostringstream>>
-  GenerateModifiedStreams();
+  std::map<int32_t, fxcrt::ostringstream> GenerateModifiedStreams();
 
-  // Add buffer as a stream in page's 'Contents'
+  // For each entry in `new_stream_data`, adds the string buffer to the page's
+  // content stream.
   void UpdateContentStreams(
-      std::map<int32_t, std::unique_ptr<std::ostringstream>>* new_stream_data);
+      std::map<int32_t, fxcrt::ostringstream>&& new_stream_data);
 
-  // Set the stream index of all page objects with stream index ==
+  // Sets the stream index of all page objects with stream index ==
   // |CPDF_PageObject::kNoContentStream|. These are new objects that had not
   // been parsed from or written to any content stream yet.
   void UpdateStreamlessPageObjects(int new_content_stream_index);
 
+  // Updates the resource dictionary for `m_pObjHolder` to account for all the
+  // changes.
+  void UpdateResourcesDict();
+
   UnownedPtr<CPDF_PageObjectHolder> const m_pObjHolder;
   UnownedPtr<CPDF_Document> const m_pDocument;
   std::vector<UnownedPtr<CPDF_PageObject>> m_pageObjects;
+  ByteString m_DefaultGraphicsName;
 };
 
 #endif  // CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTGENERATOR_H_
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
index 62c3df5..8d6aa1f 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
@@ -1,9 +1,10 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
 
+#include <iterator>
 #include <memory>
 #include <utility>
 
@@ -12,65 +13,66 @@
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
 #include "core/fpdfapi/page/cpdf_form.h"
 #include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
 #include "core/fpdfapi/page/cpdf_pathobject.h"
 #include "core/fpdfapi/page/cpdf_textobject.h"
+#include "core/fpdfapi/page/cpdf_textstate.h"
+#include "core/fpdfapi/page/test_with_page_module.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_parser.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fpdfapi/render/cpdf_docrenderdata.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxge/render_defines.h"
+#include "core/fpdfapi/parser/cpdf_test_document.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
-class CPDF_PageContentGeneratorTest : public testing::Test {
+class CPDF_PageContentGeneratorTest : public TestWithPageModule {
  protected:
-  void SetUp() override { CPDF_PageModule::Create(); }
-  void TearDown() override { CPDF_PageModule::Destroy(); }
-
   void TestProcessPath(CPDF_PageContentGenerator* pGen,
-                       std::ostringstream* buf,
+                       fxcrt::ostringstream* buf,
                        CPDF_PathObject* pPathObj) {
     pGen->ProcessPath(buf, pPathObj);
   }
 
-  CPDF_Dictionary* TestGetResource(CPDF_PageContentGenerator* pGen,
-                                   const ByteString& type,
-                                   const ByteString& name) {
-    return pGen->m_pObjHolder->m_pResources->GetDictFor(type)->GetDictFor(name);
+  RetainPtr<const CPDF_Dictionary> TestGetResource(
+      CPDF_PageContentGenerator* pGen,
+      const ByteString& type,
+      const ByteString& name) {
+    RetainPtr<const CPDF_Dictionary> pResources =
+        pGen->m_pObjHolder->GetResources();
+    return pResources->GetDictFor(type)->GetDictFor(name);
   }
 
   void TestProcessText(CPDF_PageContentGenerator* pGen,
-                       std::ostringstream* buf,
+                       fxcrt::ostringstream* buf,
                        CPDF_TextObject* pTextObj) {
     pGen->ProcessText(buf, pTextObj);
   }
 };
 
 TEST_F(CPDF_PageContentGeneratorTest, ProcessRect) {
-  auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
+  auto pPathObj = std::make_unique<CPDF_PathObject>();
   pPathObj->set_stroke(true);
-  pPathObj->set_filltype(FXFILL_ALTERNATE);
+  pPathObj->set_filltype(CFX_FillRenderOptions::FillType::kEvenOdd);
   pPathObj->path().AppendRect(10, 5, 13, 30);
 
   auto dummy_page_dict = pdfium::MakeRetain<CPDF_Dictionary>();
-  auto pTestPage =
-      pdfium::MakeRetain<CPDF_Page>(nullptr, dummy_page_dict.Get());
+  auto pTestPage = pdfium::MakeRetain<CPDF_Page>(nullptr, dummy_page_dict);
   CPDF_PageContentGenerator generator(pTestPage.Get());
-  std::ostringstream buf;
+  fxcrt::ostringstream buf;
   TestProcessPath(&generator, &buf, pPathObj.get());
   EXPECT_EQ("q 1 0 0 1 0 0 cm 10 5 3 25 re B* Q\n", ByteString(buf));
 
-  pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
-  pPathObj->path().AppendPoint(CFX_PointF(0, 0), FXPT_TYPE::MoveTo, false);
-  pPathObj->path().AppendPoint(CFX_PointF(5.2f, 0), FXPT_TYPE::LineTo, false);
-  pPathObj->path().AppendPoint(CFX_PointF(5.2f, 3.78f), FXPT_TYPE::LineTo,
-                               false);
-  pPathObj->path().AppendPoint(CFX_PointF(0, 3.78f), FXPT_TYPE::LineTo, true);
+  pPathObj = std::make_unique<CPDF_PathObject>();
+  pPathObj->path().AppendPoint(CFX_PointF(0, 0), CFX_Path::Point::Type::kMove);
+  pPathObj->path().AppendPoint(CFX_PointF(5.2f, 0),
+                               CFX_Path::Point::Type::kLine);
+  pPathObj->path().AppendPoint(CFX_PointF(5.2f, 3.78f),
+                               CFX_Path::Point::Type::kLine);
+  pPathObj->path().AppendPointAndClose(CFX_PointF(0, 3.78f),
+                                       CFX_Path::Point::Type::kLine);
   buf.str("");
   TestProcessPath(&generator, &buf, pPathObj.get());
   EXPECT_EQ("q 1 0 0 1 0 0 cm 0 0 5.1999998 3.78 re n Q\n", ByteString(buf));
@@ -78,10 +80,11 @@
 
 TEST_F(CPDF_PageContentGeneratorTest, BUG_937) {
   static const std::vector<float> rgb = {0.000000000000000000001f, 0.7f, 0.35f};
-  RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
+  RetainPtr<CPDF_ColorSpace> pCS =
+      CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB);
   {
-    auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
-    pPathObj->set_filltype(FXFILL_WINDING);
+    auto pPathObj = std::make_unique<CPDF_PathObject>();
+    pPathObj->set_filltype(CFX_FillRenderOptions::FillType::kWinding);
 
     // Test code in ProcessPath that generates re operator
     pPathObj->path().AppendRect(0.000000000000000000001,
@@ -94,10 +97,9 @@
                                    200000000000000.000002));
 
     auto dummy_page_dict = pdfium::MakeRetain<CPDF_Dictionary>();
-    auto pTestPage =
-        pdfium::MakeRetain<CPDF_Page>(nullptr, dummy_page_dict.Get());
+    auto pTestPage = pdfium::MakeRetain<CPDF_Page>(nullptr, dummy_page_dict);
     CPDF_PageContentGenerator generator(pTestPage.Get());
-    std::ostringstream buf;
+    fxcrt::ostringstream buf;
     TestProcessPath(&generator, &buf, pPathObj.get());
     EXPECT_EQ(
         "q 0 0.701961 0.34902 rg 0 0.701961 0.34902 RG 200000000000000000000 w"
@@ -108,30 +110,29 @@
 
   {
     // Test code in ProcessPath that handles bezier operator
-    auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
+    auto pPathObj = std::make_unique<CPDF_PathObject>();
     pPathObj->m_ColorState.SetFillColor(pCS, rgb);
     pPathObj->m_ColorState.SetStrokeColor(pCS, rgb);
     pPathObj->m_GraphState.SetLineWidth(2.000000000000000000001);
     pPathObj->Transform(CFX_Matrix(1, 0, 0, 1, 432, 500000000000000.000002));
 
-    pPathObj->set_filltype(FXFILL_WINDING);
+    pPathObj->set_filltype(CFX_FillRenderOptions::FillType::kWinding);
     pPathObj->path().AppendPoint(CFX_PointF(0.000000000000000000001f, 4.67f),
-                                 FXPT_TYPE::MoveTo, false);
+                                 CFX_Path::Point::Type::kMove);
     pPathObj->path().AppendPoint(
         CFX_PointF(0.000000000000000000001, 100000000000000.000002),
-        FXPT_TYPE::LineTo, false);
+        CFX_Path::Point::Type::kLine);
     pPathObj->path().AppendPoint(CFX_PointF(0.0000000000001f, 3.15f),
-                                 FXPT_TYPE::BezierTo, false);
-    pPathObj->path().AppendPoint(CFX_PointF(3.57f, 2.98f), FXPT_TYPE::BezierTo,
-                                 false);
-    pPathObj->path().AppendPoint(
+                                 CFX_Path::Point::Type::kBezier);
+    pPathObj->path().AppendPoint(CFX_PointF(3.57f, 2.98f),
+                                 CFX_Path::Point::Type::kBezier);
+    pPathObj->path().AppendPointAndClose(
         CFX_PointF(53.4f, 5000000000000000000.00000000000000004),
-        FXPT_TYPE::BezierTo, true);
+        CFX_Path::Point::Type::kBezier);
     auto dummy_page_dict = pdfium::MakeRetain<CPDF_Dictionary>();
-    auto pTestPage =
-        pdfium::MakeRetain<CPDF_Page>(nullptr, dummy_page_dict.Get());
+    auto pTestPage = pdfium::MakeRetain<CPDF_Page>(nullptr, dummy_page_dict);
     CPDF_PageContentGenerator generator(pTestPage.Get());
-    std::ostringstream buf;
+    fxcrt::ostringstream buf;
 
     TestProcessPath(&generator, &buf, pPathObj.get());
     EXPECT_EQ(
@@ -144,33 +145,33 @@
 }
 
 TEST_F(CPDF_PageContentGeneratorTest, ProcessPath) {
-  auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
-  pPathObj->set_filltype(FXFILL_WINDING);
-  pPathObj->path().AppendPoint(CFX_PointF(3.102f, 4.67f), FXPT_TYPE::MoveTo,
-                               false);
-  pPathObj->path().AppendPoint(CFX_PointF(5.45f, 0.29f), FXPT_TYPE::LineTo,
-                               false);
-  pPathObj->path().AppendPoint(CFX_PointF(4.24f, 3.15f), FXPT_TYPE::BezierTo,
-                               false);
-  pPathObj->path().AppendPoint(CFX_PointF(4.65f, 2.98f), FXPT_TYPE::BezierTo,
-                               false);
-  pPathObj->path().AppendPoint(CFX_PointF(3.456f, 0.24f), FXPT_TYPE::BezierTo,
-                               false);
-  pPathObj->path().AppendPoint(CFX_PointF(10.6f, 11.15f), FXPT_TYPE::LineTo,
-                               false);
-  pPathObj->path().AppendPoint(CFX_PointF(11, 12.5f), FXPT_TYPE::LineTo, false);
-  pPathObj->path().AppendPoint(CFX_PointF(11.46f, 12.67f), FXPT_TYPE::BezierTo,
-                               false);
-  pPathObj->path().AppendPoint(CFX_PointF(11.84f, 12.96f), FXPT_TYPE::BezierTo,
-                               false);
-  pPathObj->path().AppendPoint(CFX_PointF(12, 13.64f), FXPT_TYPE::BezierTo,
-                               true);
+  auto pPathObj = std::make_unique<CPDF_PathObject>();
+  pPathObj->set_filltype(CFX_FillRenderOptions::FillType::kWinding);
+  pPathObj->path().AppendPoint(CFX_PointF(3.102f, 4.67f),
+                               CFX_Path::Point::Type::kMove);
+  pPathObj->path().AppendPoint(CFX_PointF(5.45f, 0.29f),
+                               CFX_Path::Point::Type::kLine);
+  pPathObj->path().AppendPoint(CFX_PointF(4.24f, 3.15f),
+                               CFX_Path::Point::Type::kBezier);
+  pPathObj->path().AppendPoint(CFX_PointF(4.65f, 2.98f),
+                               CFX_Path::Point::Type::kBezier);
+  pPathObj->path().AppendPoint(CFX_PointF(3.456f, 0.24f),
+                               CFX_Path::Point::Type::kBezier);
+  pPathObj->path().AppendPoint(CFX_PointF(10.6f, 11.15f),
+                               CFX_Path::Point::Type::kLine);
+  pPathObj->path().AppendPoint(CFX_PointF(11, 12.5f),
+                               CFX_Path::Point::Type::kLine);
+  pPathObj->path().AppendPoint(CFX_PointF(11.46f, 12.67f),
+                               CFX_Path::Point::Type::kBezier);
+  pPathObj->path().AppendPoint(CFX_PointF(11.84f, 12.96f),
+                               CFX_Path::Point::Type::kBezier);
+  pPathObj->path().AppendPointAndClose(CFX_PointF(12, 13.64f),
+                                       CFX_Path::Point::Type::kBezier);
 
   auto dummy_page_dict = pdfium::MakeRetain<CPDF_Dictionary>();
-  auto pTestPage =
-      pdfium::MakeRetain<CPDF_Page>(nullptr, dummy_page_dict.Get());
+  auto pTestPage = pdfium::MakeRetain<CPDF_Page>(nullptr, dummy_page_dict);
   CPDF_PageContentGenerator generator(pTestPage.Get());
-  std::ostringstream buf;
+  fxcrt::ostringstream buf;
   TestProcessPath(&generator, &buf, pPathObj.get());
   EXPECT_EQ(
       "q 1 0 0 1 0 0 cm 3.102 4.6700001 m 5.4499998 .28999999 l 4.2399998 "
@@ -181,15 +182,17 @@
 }
 
 TEST_F(CPDF_PageContentGeneratorTest, ProcessGraphics) {
-  auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
+  auto pPathObj = std::make_unique<CPDF_PathObject>();
   pPathObj->set_stroke(true);
-  pPathObj->set_filltype(FXFILL_WINDING);
-  pPathObj->path().AppendPoint(CFX_PointF(1, 2), FXPT_TYPE::MoveTo, false);
-  pPathObj->path().AppendPoint(CFX_PointF(3, 4), FXPT_TYPE::LineTo, false);
-  pPathObj->path().AppendPoint(CFX_PointF(5, 6), FXPT_TYPE::LineTo, true);
+  pPathObj->set_filltype(CFX_FillRenderOptions::FillType::kWinding);
+  pPathObj->path().AppendPoint(CFX_PointF(1, 2), CFX_Path::Point::Type::kMove);
+  pPathObj->path().AppendPoint(CFX_PointF(3, 4), CFX_Path::Point::Type::kLine);
+  pPathObj->path().AppendPointAndClose(CFX_PointF(5, 6),
+                                       CFX_Path::Point::Type::kLine);
 
   static const std::vector<float> rgb = {0.5f, 0.7f, 0.35f};
-  RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
+  RetainPtr<CPDF_ColorSpace> pCS =
+      CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB);
   pPathObj->m_ColorState.SetFillColor(pCS, rgb);
 
   static const std::vector<float> rgb2 = {1, 0.9f, 0};
@@ -197,15 +200,13 @@
   pPathObj->m_GeneralState.SetFillAlpha(0.5f);
   pPathObj->m_GeneralState.SetStrokeAlpha(0.8f);
 
-  auto pDoc = pdfium::MakeUnique<CPDF_Document>(
-      pdfium::MakeUnique<CPDF_DocRenderData>(),
-      pdfium::MakeUnique<CPDF_DocPageData>());
-
+  auto pDoc = std::make_unique<CPDF_TestDocument>();
   pDoc->CreateNewDoc();
-  CPDF_Dictionary* pPageDict = pDoc->CreateNewPage(0);
+
+  RetainPtr<CPDF_Dictionary> pPageDict(pDoc->CreateNewPage(0));
   auto pTestPage = pdfium::MakeRetain<CPDF_Page>(pDoc.get(), pPageDict);
   CPDF_PageContentGenerator generator(pTestPage.Get());
-  std::ostringstream buf;
+  fxcrt::ostringstream buf;
   TestProcessPath(&generator, &buf, pPathObj.get());
   ByteString pathString(buf);
 
@@ -215,12 +216,12 @@
   EXPECT_EQ(" gs 1 0 0 1 0 0 cm 1 2 m 3 4 l 5 6 l h B Q\n",
             pathString.Last(43));
   ASSERT_GT(pathString.GetLength(), 91U);
-  CPDF_Dictionary* externalGS =
+  RetainPtr<const CPDF_Dictionary> externalGS =
       TestGetResource(&generator, "ExtGState",
                       pathString.Substr(48, pathString.GetLength() - 91));
   ASSERT_TRUE(externalGS);
-  EXPECT_EQ(0.5f, externalGS->GetNumberFor("ca"));
-  EXPECT_EQ(0.8f, externalGS->GetNumberFor("CA"));
+  EXPECT_EQ(0.5f, externalGS->GetFloatFor("ca"));
+  EXPECT_EQ(0.8f, externalGS->GetFloatFor("CA"));
 
   // Same path, now with a stroke.
   pPathObj->m_GraphState.SetLineWidth(10.5f);
@@ -240,22 +241,20 @@
 
 TEST_F(CPDF_PageContentGeneratorTest, ProcessStandardText) {
   // Checking font whose font dictionary is not yet indirect object.
-  auto pDoc = pdfium::MakeUnique<CPDF_Document>(
-      pdfium::MakeUnique<CPDF_DocRenderData>(),
-      pdfium::MakeUnique<CPDF_DocPageData>());
-
+  auto pDoc = std::make_unique<CPDF_TestDocument>();
   pDoc->CreateNewDoc();
-  CPDF_Dictionary* pPageDict = pDoc->CreateNewPage(0);
+
+  RetainPtr<CPDF_Dictionary> pPageDict(pDoc->CreateNewPage(0));
   auto pTestPage = pdfium::MakeRetain<CPDF_Page>(pDoc.get(), pPageDict);
   CPDF_PageContentGenerator generator(pTestPage.Get());
-  auto pTextObj = pdfium::MakeUnique<CPDF_TextObject>();
-  RetainPtr<CPDF_Font> pFont =
-      CPDF_Font::GetStockFont(pDoc.get(), "Times-Roman");
-  pTextObj->m_TextState.SetFont(pFont);
+  auto pTextObj = std::make_unique<CPDF_TextObject>();
+  pTextObj->m_TextState.SetFont(
+      CPDF_Font::GetStockFont(pDoc.get(), "Times-Roman"));
   pTextObj->m_TextState.SetFontSize(10.0f);
 
   static const std::vector<float> rgb = {0.5f, 0.7f, 0.35f};
-  RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
+  RetainPtr<CPDF_ColorSpace> pCS =
+      CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB);
   pTextObj->m_ColorState.SetFillColor(pCS, rgb);
 
   static const std::vector<float> rgb2 = {1, 0.9f, 0};
@@ -264,7 +263,7 @@
   pTextObj->m_GeneralState.SetStrokeAlpha(0.8f);
   pTextObj->Transform(CFX_Matrix(1, 0, 0, 1, 100, 100));
   pTextObj->SetText("Hello World");
-  std::ostringstream buf;
+  fxcrt::ostringstream buf;
   TestProcessText(&generator, &buf, pTextObj.get());
   ByteString textString(buf);
   auto firstResourceAt = textString.Find('/');
@@ -284,61 +283,70 @@
       "q 0.501961 0.701961 0.34902 rg 1 0.901961 0 RG /";
   // Color RGB values used are integers divided by 255.
   ByteString compareString2 = " gs BT 1 0 0 1 100 100 Tm /";
-  ByteString compareString3 = " 10 Tf <48656C6C6F20576F726C64> Tj ET Q\n";
+  ByteString compareString3 = " 10 Tf 0 Tr <48656C6C6F20576F726C64> Tj ET Q\n";
   EXPECT_LT(compareString1.GetLength() + compareString2.GetLength() +
                 compareString3.GetLength(),
             textString.GetLength());
   EXPECT_EQ(compareString1, firstString.First(compareString1.GetLength()));
   EXPECT_EQ(compareString2, midString.Last(compareString2.GetLength()));
   EXPECT_EQ(compareString3, lastString.Last(compareString3.GetLength()));
-  CPDF_Dictionary* externalGS = TestGetResource(
+  RetainPtr<const CPDF_Dictionary> externalGS = TestGetResource(
       &generator, "ExtGState",
       midString.First(midString.GetLength() - compareString2.GetLength()));
   ASSERT_TRUE(externalGS);
-  EXPECT_EQ(0.5f, externalGS->GetNumberFor("ca"));
-  EXPECT_EQ(0.8f, externalGS->GetNumberFor("CA"));
-  CPDF_Dictionary* fontDict = TestGetResource(
+  EXPECT_EQ(0.5f, externalGS->GetFloatFor("ca"));
+  EXPECT_EQ(0.8f, externalGS->GetFloatFor("CA"));
+  RetainPtr<const CPDF_Dictionary> fontDict = TestGetResource(
       &generator, "Font",
       lastString.First(lastString.GetLength() - compareString3.GetLength()));
   ASSERT_TRUE(fontDict);
-  EXPECT_EQ("Font", fontDict->GetStringFor("Type"));
-  EXPECT_EQ("Type1", fontDict->GetStringFor("Subtype"));
-  EXPECT_EQ("Times-Roman", fontDict->GetStringFor("BaseFont"));
+  EXPECT_EQ("Font", fontDict->GetNameFor("Type"));
+  EXPECT_EQ("Type1", fontDict->GetNameFor("Subtype"));
+  EXPECT_EQ("Times-Roman", fontDict->GetNameFor("BaseFont"));
 }
 
 TEST_F(CPDF_PageContentGeneratorTest, ProcessText) {
   // Checking font whose font dictionary is already an indirect object.
-  auto pDoc = pdfium::MakeUnique<CPDF_Document>(
-      pdfium::MakeUnique<CPDF_DocRenderData>(),
-      pdfium::MakeUnique<CPDF_DocPageData>());
+  auto pDoc = std::make_unique<CPDF_TestDocument>();
   pDoc->CreateNewDoc();
 
-  CPDF_Dictionary* pPageDict = pDoc->CreateNewPage(0);
+  RetainPtr<CPDF_Dictionary> pPageDict(pDoc->CreateNewPage(0));
   auto pTestPage = pdfium::MakeRetain<CPDF_Page>(pDoc.get(), pPageDict);
   CPDF_PageContentGenerator generator(pTestPage.Get());
 
-  std::ostringstream buf;
+  fxcrt::ostringstream buf;
   {
     // Set the text object font and text
-    auto pTextObj = pdfium::MakeUnique<CPDF_TextObject>();
-    CPDF_Dictionary* pDict = pDoc->NewIndirect<CPDF_Dictionary>();
+    auto pTextObj = std::make_unique<CPDF_TextObject>();
+    auto pDict = pDoc->NewIndirect<CPDF_Dictionary>();
     pDict->SetNewFor<CPDF_Name>("Type", "Font");
     pDict->SetNewFor<CPDF_Name>("Subtype", "TrueType");
 
     RetainPtr<CPDF_Font> pFont = CPDF_Font::GetStockFont(pDoc.get(), "Arial");
     pDict->SetNewFor<CPDF_Name>("BaseFont", pFont->GetBaseFontName());
 
-    CPDF_Dictionary* pDesc = pDoc->NewIndirect<CPDF_Dictionary>();
+    auto pDesc = pDoc->NewIndirect<CPDF_Dictionary>();
     pDesc->SetNewFor<CPDF_Name>("Type", "FontDescriptor");
     pDesc->SetNewFor<CPDF_Name>("FontName", pFont->GetBaseFontName());
     pDict->SetNewFor<CPDF_Reference>("FontDescriptor", pDoc.get(),
                                      pDesc->GetObjNum());
 
-    RetainPtr<CPDF_Font> pLoadedFont =
-        CPDF_DocPageData::FromDocument(pDoc.get())->GetFont(pDict);
-    pTextObj->m_TextState.SetFont(pLoadedFont);
+    pTextObj->m_TextState.SetFont(
+        CPDF_DocPageData::FromDocument(pDoc.get())->GetFont(pDict));
     pTextObj->m_TextState.SetFontSize(15.5f);
     pTextObj->SetText("I am indirect");
+    pTextObj->SetTextRenderMode(TextRenderingMode::MODE_FILL_CLIP);
+
+    // Add a clipping path.
+    auto pPath = std::make_unique<CPDF_Path>();
+    pPath->AppendPoint(CFX_PointF(0, 0), CFX_Path::Point::Type::kMove);
+    pPath->AppendPoint(CFX_PointF(5, 0), CFX_Path::Point::Type::kLine);
+    pPath->AppendPoint(CFX_PointF(5, 4), CFX_Path::Point::Type::kLine);
+    pPath->AppendPointAndClose(CFX_PointF(0, 4), CFX_Path::Point::Type::kLine);
+    pTextObj->m_ClipPath.Emplace();
+    pTextObj->m_ClipPath.AppendPath(*pPath,
+                                    CFX_FillRenderOptions::FillType::kEvenOdd);
+
     TestProcessText(&generator, &buf, pTextObj.get());
   }
 
@@ -350,75 +358,68 @@
   ByteString lastString =
       textString.Last(textString.GetLength() - firstResourceAt.value());
   // q and Q must be outside the BT .. ET operations
-  ByteString compareString1 = "q BT 1 0 0 1 0 0 Tm /";
-  ByteString compareString2 = " 15.5 Tf <4920616D20696E646972656374> Tj ET Q\n";
+  ByteString compareString1 = "q 0 0 5 4 re W* n BT 1 0 0 1 0 0 Tm /";
+  ByteString compareString2 =
+      " 15.5 Tf 4 Tr <4920616D20696E646972656374> Tj ET Q\n";
   EXPECT_LT(compareString1.GetLength() + compareString2.GetLength(),
             textString.GetLength());
   EXPECT_EQ(compareString1, textString.First(compareString1.GetLength()));
   EXPECT_EQ(compareString2, textString.Last(compareString2.GetLength()));
-  CPDF_Dictionary* fontDict = TestGetResource(
+  RetainPtr<const CPDF_Dictionary> fontDict = TestGetResource(
       &generator, "Font",
       textString.Substr(compareString1.GetLength(),
                         textString.GetLength() - compareString1.GetLength() -
                             compareString2.GetLength()));
   ASSERT_TRUE(fontDict);
   EXPECT_TRUE(fontDict->GetObjNum());
-  EXPECT_EQ("Font", fontDict->GetStringFor("Type"));
-  EXPECT_EQ("TrueType", fontDict->GetStringFor("Subtype"));
-  EXPECT_EQ("Helvetica", fontDict->GetStringFor("BaseFont"));
-  CPDF_Dictionary* fontDesc = fontDict->GetDictFor("FontDescriptor");
+  EXPECT_EQ("Font", fontDict->GetNameFor("Type"));
+  EXPECT_EQ("TrueType", fontDict->GetNameFor("Subtype"));
+  EXPECT_EQ("Helvetica", fontDict->GetNameFor("BaseFont"));
+  RetainPtr<const CPDF_Dictionary> fontDesc =
+      fontDict->GetDictFor("FontDescriptor");
   ASSERT_TRUE(fontDesc);
   EXPECT_TRUE(fontDesc->GetObjNum());
-  EXPECT_EQ("FontDescriptor", fontDesc->GetStringFor("Type"));
-  EXPECT_EQ("Helvetica", fontDesc->GetStringFor("FontName"));
+  EXPECT_EQ("FontDescriptor", fontDesc->GetNameFor("Type"));
+  EXPECT_EQ("Helvetica", fontDesc->GetNameFor("FontName"));
 }
 
 TEST_F(CPDF_PageContentGeneratorTest, ProcessEmptyForm) {
-  auto pDoc = pdfium::MakeUnique<CPDF_Document>(
-      pdfium::MakeUnique<CPDF_DocRenderData>(),
-      pdfium::MakeUnique<CPDF_DocPageData>());
+  auto pDoc = std::make_unique<CPDF_TestDocument>();
   pDoc->CreateNewDoc();
-  auto pDict = pdfium::MakeRetain<CPDF_Dictionary>();
-  auto pStream = pdfium::MakeRetain<CPDF_Stream>(nullptr, 0, std::move(pDict));
+  auto pStream =
+      pdfium::MakeRetain<CPDF_Stream>(pdfium::MakeRetain<CPDF_Dictionary>());
 
   // Create an empty form.
-  auto pTestForm =
-      pdfium::MakeUnique<CPDF_Form>(pDoc.get(), nullptr, pStream.Get());
+  auto pTestForm = std::make_unique<CPDF_Form>(pDoc.get(), nullptr, pStream);
   pTestForm->ParseContent();
   ASSERT_EQ(CPDF_PageObjectHolder::ParseState::kParsed,
             pTestForm->GetParseState());
 
   // The generated stream for the empty form should be an empty string.
   CPDF_PageContentGenerator generator(pTestForm.get());
-  std::ostringstream buf;
+  fxcrt::ostringstream buf;
   generator.ProcessPageObjects(&buf);
   EXPECT_EQ("", ByteString(buf));
 }
 
 TEST_F(CPDF_PageContentGeneratorTest, ProcessFormWithPath) {
-  auto pDoc = pdfium::MakeUnique<CPDF_Document>(
-      pdfium::MakeUnique<CPDF_DocRenderData>(),
-      pdfium::MakeUnique<CPDF_DocPageData>());
+  auto pDoc = std::make_unique<CPDF_TestDocument>();
   pDoc->CreateNewDoc();
-  auto pDict = pdfium::MakeRetain<CPDF_Dictionary>();
-  const char content[] =
+  static constexpr uint8_t kContents[] =
       "q 1 0 0 1 0 0 cm 3.102 4.6700001 m 5.4500012 .28999999 "
       "l 4.2399998 3.1499999 4.65 2.98 3.456 0.24 c 3.102 4.6700001 l h f Q\n";
-  size_t buf_len = FX_ArraySize(content);
-  std::unique_ptr<uint8_t, FxFreeDeleter> buf(FX_Alloc(uint8_t, buf_len));
-  memcpy(buf.get(), content, buf_len);
-  auto pStream = pdfium::MakeRetain<CPDF_Stream>(std::move(buf), buf_len,
-                                                 std::move(pDict));
+  auto pStream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(std::begin(kContents), std::end(kContents)),
+      pdfium::MakeRetain<CPDF_Dictionary>());
 
   // Create a form with a non-empty stream.
-  auto pTestForm =
-      pdfium::MakeUnique<CPDF_Form>(pDoc.get(), nullptr, pStream.Get());
+  auto pTestForm = std::make_unique<CPDF_Form>(pDoc.get(), nullptr, pStream);
   pTestForm->ParseContent();
   ASSERT_EQ(CPDF_PageObjectHolder::ParseState::kParsed,
             pTestForm->GetParseState());
 
   CPDF_PageContentGenerator generator(pTestForm.get());
-  std::ostringstream process_buf;
+  fxcrt::ostringstream process_buf;
   generator.ProcessPageObjects(&process_buf);
   EXPECT_STREQ(
       "q 1 0 0 1 0 0 cm 3.102 4.6700001 m 5.4500012 .28999999 l 4.2399998 3.14"
diff --git a/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp b/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp
index 35a8acd..4be22e6 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp
@@ -1,11 +1,16 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/edit/cpdf_pagecontentmanager.h"
 
+#include <stdint.h>
+
 #include <map>
 #include <numeric>
+#include <set>
+#include <sstream>
+#include <utility>
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_pageobject.h"
@@ -15,87 +20,155 @@
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fpdfapi/parser/object_tree_traversal_util.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/adapters.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 CPDF_PageContentManager::CPDF_PageContentManager(
-    const CPDF_PageObjectHolder* obj_holder)
-    : obj_holder_(obj_holder), doc_(obj_holder_->GetDocument()) {
-  CPDF_Dictionary* page_dict = obj_holder_->GetDict();
-  CPDF_Object* contents_obj = page_dict->GetObjectFor("Contents");
-  CPDF_Array* contents_array = ToArray(contents_obj);
+    CPDF_PageObjectHolder* page_obj_holder,
+    CPDF_Document* document)
+    : page_obj_holder_(page_obj_holder),
+      document_(document),
+      objects_with_multi_refs_(GetObjectsWithMultipleReferences(document_)) {
+  RetainPtr<CPDF_Dictionary> page_dict = page_obj_holder_->GetMutableDict();
+  RetainPtr<CPDF_Object> contents_obj =
+      page_dict->GetMutableObjectFor("Contents");
+  RetainPtr<CPDF_Array> contents_array = ToArray(contents_obj);
   if (contents_array) {
-    contents_array_.Reset(contents_array);
+    CHECK(contents_array->IsInline());
+    contents_ = std::move(contents_array);
     return;
   }
 
-  CPDF_Reference* contents_reference = ToReference(contents_obj);
+  RetainPtr<CPDF_Reference> contents_reference = ToReference(contents_obj);
   if (contents_reference) {
-    CPDF_Object* indirect_obj = contents_reference->GetDirect();
+    RetainPtr<CPDF_Object> indirect_obj =
+        contents_reference->GetMutableDirect();
     if (!indirect_obj)
       return;
 
-    contents_array = indirect_obj->AsArray();
-    if (contents_array)
-      contents_array_.Reset(contents_array);
-    else if (indirect_obj->IsStream())
-      contents_stream_.Reset(indirect_obj->AsStream());
+    contents_array.Reset(indirect_obj->AsMutableArray());
+    if (contents_array) {
+      if (pdfium::Contains(objects_with_multi_refs_,
+                           contents_array->GetObjNum())) {
+        RetainPtr<CPDF_Array> cloned_contents_array =
+            pdfium::WrapRetain(contents_array->Clone()->AsMutableArray());
+        page_dict->SetFor("Contents", cloned_contents_array);
+        contents_ = std::move(cloned_contents_array);
+      } else {
+        contents_ = std::move(contents_array);
+      }
+    } else if (indirect_obj->IsStream()) {
+      contents_ = pdfium::WrapRetain(indirect_obj->AsMutableStream());
+    }
   }
 }
 
-CPDF_PageContentManager::~CPDF_PageContentManager() = default;
-
-CPDF_Stream* CPDF_PageContentManager::GetStreamByIndex(size_t stream_index) {
-  if (contents_stream_)
-    return stream_index == 0 ? contents_stream_.Get() : nullptr;
-
-  if (contents_array_) {
-    CPDF_Reference* stream_reference =
-        ToReference(contents_array_->GetObjectAt(stream_index));
-    if (!stream_reference)
-      return nullptr;
-
-    return stream_reference->GetDirect()->AsStream();
-  }
-
-  return nullptr;
+CPDF_PageContentManager::~CPDF_PageContentManager() {
+  ExecuteScheduledRemovals();
 }
 
-size_t CPDF_PageContentManager::AddStream(std::ostringstream* buf) {
-  CPDF_Stream* new_stream = doc_->NewIndirect<CPDF_Stream>();
+RetainPtr<CPDF_Stream> CPDF_PageContentManager::GetStreamByIndex(
+    size_t stream_index) {
+  RetainPtr<CPDF_Stream> contents_stream = GetContentsStream();
+  if (contents_stream) {
+    return stream_index == 0 ? contents_stream : nullptr;
+  }
+
+  RetainPtr<CPDF_Array> contents_array = GetContentsArray();
+  if (!contents_array) {
+    return nullptr;
+  }
+
+  RetainPtr<CPDF_Reference> stream_reference =
+      ToReference(contents_array->GetMutableObjectAt(stream_index));
+  if (!stream_reference)
+    return nullptr;
+
+  return ToStream(stream_reference->GetMutableDirect());
+}
+
+size_t CPDF_PageContentManager::AddStream(fxcrt::ostringstream* buf) {
+  auto new_stream = document_->NewIndirect<CPDF_Stream>();
   new_stream->SetDataFromStringstream(buf);
 
   // If there is one Content stream (not in an array), now there will be two, so
   // create an array with the old and the new one. The new one's index is 1.
-  if (contents_stream_) {
-    CPDF_Array* new_contents_array = doc_->NewIndirect<CPDF_Array>();
-    new_contents_array->AddNew<CPDF_Reference>(doc_.Get(),
-                                               contents_stream_->GetObjNum());
-    new_contents_array->AddNew<CPDF_Reference>(doc_.Get(),
-                                               new_stream->GetObjNum());
+  RetainPtr<CPDF_Stream> contents_stream = GetContentsStream();
+  if (contents_stream) {
+    auto new_contents_array = document_->NewIndirect<CPDF_Array>();
+    new_contents_array->AppendNew<CPDF_Reference>(document_,
+                                                  contents_stream->GetObjNum());
+    new_contents_array->AppendNew<CPDF_Reference>(document_,
+                                                  new_stream->GetObjNum());
 
-    CPDF_Dictionary* page_dict = obj_holder_->GetDict();
-    page_dict->SetNewFor<CPDF_Reference>("Contents", doc_.Get(),
+    RetainPtr<CPDF_Dictionary> page_dict = page_obj_holder_->GetMutableDict();
+    page_dict->SetNewFor<CPDF_Reference>("Contents", document_,
                                          new_contents_array->GetObjNum());
-    contents_array_.Reset(new_contents_array);
-    contents_stream_ = nullptr;
+    contents_ = std::move(new_contents_array);
     return 1;
   }
 
   // If there is an array, just add the new stream to it, at the last position.
-  if (contents_array_) {
-    contents_array_->AddNew<CPDF_Reference>(doc_.Get(),
-                                            new_stream->GetObjNum());
-    return contents_array_->size() - 1;
+  RetainPtr<CPDF_Array> contents_array = GetContentsArray();
+  if (contents_array) {
+    contents_array->AppendNew<CPDF_Reference>(document_,
+                                              new_stream->GetObjNum());
+    return contents_array->size() - 1;
   }
 
   // There were no Contents, so add the new stream as the single Content stream.
   // Its index is 0.
-  CPDF_Dictionary* page_dict = obj_holder_->GetDict();
-  page_dict->SetNewFor<CPDF_Reference>("Contents", doc_.Get(),
+  RetainPtr<CPDF_Dictionary> page_dict = page_obj_holder_->GetMutableDict();
+  page_dict->SetNewFor<CPDF_Reference>("Contents", document_,
                                        new_stream->GetObjNum());
-  contents_stream_.Reset(new_stream);
+  contents_ = std::move(new_stream);
   return 0;
 }
 
+void CPDF_PageContentManager::UpdateStream(size_t stream_index,
+                                           fxcrt::ostringstream* buf) {
+  // If `buf` is now empty, remove the stream instead of setting the data.
+  if (buf->tellp() <= 0) {
+    ScheduleRemoveStreamByIndex(stream_index);
+    return;
+  }
+
+  RetainPtr<CPDF_Stream> existing_stream = GetStreamByIndex(stream_index);
+  CHECK(existing_stream);
+  if (!pdfium::Contains(objects_with_multi_refs_,
+                        existing_stream->GetObjNum())) {
+    existing_stream->SetDataFromStringstreamAndRemoveFilter(buf);
+    return;
+  }
+
+  if (GetContentsStream()) {
+    auto new_stream = document_->NewIndirect<CPDF_Stream>();
+    new_stream->SetDataFromStringstream(buf);
+    RetainPtr<CPDF_Dictionary> page_dict = page_obj_holder_->GetMutableDict();
+    page_dict->SetNewFor<CPDF_Reference>("Contents", document_,
+                                         new_stream->GetObjNum());
+  }
+
+  RetainPtr<CPDF_Array> contents_array = GetContentsArray();
+  if (!contents_array) {
+    return;
+  }
+
+  RetainPtr<CPDF_Reference> stream_reference =
+      ToReference(contents_array->GetMutableObjectAt(stream_index));
+  if (!stream_reference) {
+    return;
+  }
+
+  auto new_stream = document_->NewIndirect<CPDF_Stream>();
+  new_stream->SetDataFromStringstream(buf);
+  stream_reference->SetRef(document_, new_stream->GetObjNum());
+}
+
 void CPDF_PageContentManager::ScheduleRemoveStreamByIndex(size_t stream_index) {
   streams_to_remove_.insert(stream_index);
 }
@@ -104,49 +177,72 @@
   // This method assumes there are no dirty streams in the
   // CPDF_PageObjectHolder. If there were any, their indexes would need to be
   // updated.
-  // Since this is only called by CPDF_PageContentGenerator::GenerateContent(),
-  // which cleans up the dirty streams first, this should always be true.
-  ASSERT(!obj_holder_->HasDirtyStreams());
+  // Since CPDF_PageContentManager is only instantiated in
+  // CPDF_PageContentGenerator::GenerateContent(), which cleans up the dirty
+  // streams first, this should always be true.
+  DCHECK(!page_obj_holder_->HasDirtyStreams());
 
-  if (contents_stream_) {
-    // Only stream that can be removed is 0.
-    if (streams_to_remove_.find(0) != streams_to_remove_.end()) {
-      CPDF_Dictionary* page_dict = obj_holder_->GetDict();
-      page_dict->RemoveFor("Contents");
-      contents_stream_ = nullptr;
-    }
-  } else if (contents_array_) {
-    // Initialize a vector with the old stream indexes. This will be used to
-    // build a map from the old to the new indexes.
-    std::vector<size_t> streams_left(contents_array_->size());
-    std::iota(streams_left.begin(), streams_left.end(), 0);
-
-    // In reverse order so as to not change the indexes in the middle of the
-    // loop, remove the streams.
-    for (auto it = streams_to_remove_.rbegin(); it != streams_to_remove_.rend();
-         ++it) {
-      size_t stream_index = *it;
-      contents_array_->RemoveAt(stream_index);
-      streams_left.erase(streams_left.begin() + stream_index);
-    }
-
-    // Create a mapping from the old to the new stream indexes, shifted due to
-    // the deletion of the |streams_to_remove_|.
-    std::map<int32_t, size_t> stream_index_mapping;
-    for (size_t i = 0; i < streams_left.size(); ++i)
-      stream_index_mapping[streams_left[i]] = i;
-
-    // Update the page objects' content stream indexes.
-    for (const auto& obj : *obj_holder_) {
-      int32_t old_stream_index = obj->GetContentStream();
-      size_t new_stream_index = stream_index_mapping[old_stream_index];
-      obj->SetContentStream(new_stream_index);
-    }
-
-    // Even if there is a single content stream now, keep the array with a
-    // single element. It's valid, a second stream might be added soon, and the
-    // complexity of removing it is not worth it.
+  if (streams_to_remove_.empty()) {
+    return;
   }
 
-  streams_to_remove_.clear();
+  RetainPtr<CPDF_Stream> contents_stream = GetContentsStream();
+  if (contents_stream) {
+    // Only stream that can be removed is 0.
+    if (streams_to_remove_.find(0) != streams_to_remove_.end()) {
+      RetainPtr<CPDF_Dictionary> page_dict = page_obj_holder_->GetMutableDict();
+      page_dict->RemoveFor("Contents");
+    }
+    return;
+  }
+
+  RetainPtr<CPDF_Array> contents_array = GetContentsArray();
+  if (!contents_array) {
+    return;
+  }
+
+  // Initialize a vector with the old stream indexes. This will be used to build
+  // a map from the old to the new indexes.
+  std::vector<size_t> streams_left(contents_array->size());
+  std::iota(streams_left.begin(), streams_left.end(), 0);
+
+  // In reverse order so as to not change the indexes in the middle of the loop,
+  // remove the streams.
+  for (size_t stream_index : pdfium::base::Reversed(streams_to_remove_)) {
+    contents_array->RemoveAt(stream_index);
+    streams_left.erase(streams_left.begin() + stream_index);
+  }
+
+  // Create a mapping from the old to the new stream indexes, shifted due to the
+  // deletion of the |streams_to_remove_|.
+  std::map<size_t, size_t> stream_index_mapping;
+  for (size_t i = 0; i < streams_left.size(); ++i) {
+    stream_index_mapping[streams_left[i]] = i;
+  }
+
+  // Update the page objects' content stream indexes.
+  for (const auto& obj : *page_obj_holder_) {
+    int32_t old_stream_index = obj->GetContentStream();
+    int32_t new_stream_index = pdfium::base::checked_cast<int32_t>(
+        stream_index_mapping[old_stream_index]);
+    obj->SetContentStream(new_stream_index);
+  }
+
+  // Even if there is a single content stream now, keep the array with a single
+  // element. It's valid, a second stream might be added in the near future, and
+  // the complexity of removing it is not worth it.
+}
+
+RetainPtr<CPDF_Stream> CPDF_PageContentManager::GetContentsStream() {
+  if (absl::holds_alternative<RetainPtr<CPDF_Stream>>(contents_)) {
+    return absl::get<RetainPtr<CPDF_Stream>>(contents_);
+  }
+  return nullptr;
+}
+
+RetainPtr<CPDF_Array> CPDF_PageContentManager::GetContentsArray() {
+  if (absl::holds_alternative<RetainPtr<CPDF_Array>>(contents_)) {
+    return absl::get<RetainPtr<CPDF_Array>>(contents_);
+  }
+  return nullptr;
 }
diff --git a/core/fpdfapi/edit/cpdf_pagecontentmanager.h b/core/fpdfapi/edit/cpdf_pagecontentmanager.h
index 2e2b225..5785fc3 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentmanager.h
+++ b/core/fpdfapi/edit/cpdf_pagecontentmanager.h
@@ -1,49 +1,60 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTMANAGER_H_
 #define CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTMANAGER_H_
 
-#include <set>
-#include <sstream>
+#include <stdint.h>
 
+#include <set>
+
+#include "core/fxcrt/fx_string_wrappers.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
 
 class CPDF_Array;
 class CPDF_Document;
-class CPDF_Object;
-class CPDF_Stream;
 class CPDF_PageObjectHolder;
+class CPDF_Stream;
 
 class CPDF_PageContentManager {
  public:
-  explicit CPDF_PageContentManager(const CPDF_PageObjectHolder* obj_holder);
+  CPDF_PageContentManager(CPDF_PageObjectHolder* page_obj_holder,
+                          CPDF_Document* document);
   ~CPDF_PageContentManager();
 
-  // Gets the Content stream at a given index. If Contents is a single stream
-  // rather than an array, it is considered to be at index 0.
-  CPDF_Stream* GetStreamByIndex(size_t stream_index);
-
   // Adds a new Content stream. Its index in the array will be returned, or 0
   // if Contents is not an array, but only a single stream.
-  size_t AddStream(std::ostringstream* buf);
+  size_t AddStream(fxcrt::ostringstream* buf);
 
-  // Schedule the removal of the Content stream at a given index. It will be
-  // removed when ExecuteScheduledRemovals() is called.
+  // Changes the stream at `stream_index` to contain the data in `buf`. If `buf`
+  // is empty, then schedule the removal of the stream instead.
+  void UpdateStream(size_t stream_index, fxcrt::ostringstream* buf);
+
+ private:
+  // Gets the Content stream at a given index. If Contents is a single stream
+  // rather than an array, it is retrievable at index 0.
+  RetainPtr<CPDF_Stream> GetStreamByIndex(size_t stream_index);
+
+  // Schedules the removal of the Content stream at a given index. It will be
+  // removed upon CPDF_PageContentManager destruction.
   void ScheduleRemoveStreamByIndex(size_t stream_index);
 
-  // Remove all Content streams for which ScheduleRemoveStreamByIndex() was
+  // Removes all Content streams for which ScheduleRemoveStreamByIndex() was
   // called. Update the content stream of all page objects with the shifted
   // indexes.
   void ExecuteScheduledRemovals();
 
- private:
-  UnownedPtr<const CPDF_PageObjectHolder> const obj_holder_;
-  UnownedPtr<CPDF_Document> const doc_;
-  RetainPtr<CPDF_Array> contents_array_;
-  RetainPtr<CPDF_Stream> contents_stream_;
+  RetainPtr<CPDF_Stream> GetContentsStream();
+  RetainPtr<CPDF_Array> GetContentsArray();
+
+  UnownedPtr<CPDF_PageObjectHolder> const page_obj_holder_;
+  UnownedPtr<CPDF_Document> const document_;
+  const std::set<uint32_t> objects_with_multi_refs_;
+  // When holding a CPDF_Stream, the pointer may be null.
+  absl::variant<RetainPtr<CPDF_Stream>, RetainPtr<CPDF_Array>> contents_;
   std::set<size_t> streams_to_remove_;
 };
 
diff --git a/core/fpdfapi/edit/cpdf_stringarchivestream.cpp b/core/fpdfapi/edit/cpdf_stringarchivestream.cpp
index 4840db8..48033b0 100644
--- a/core/fpdfapi/edit/cpdf_stringarchivestream.cpp
+++ b/core/fpdfapi/edit/cpdf_stringarchivestream.cpp
@@ -1,35 +1,23 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/edit/cpdf_stringarchivestream.h"
 
-CPDF_StringArchiveStream::CPDF_StringArchiveStream(std::ostringstream* stream)
+#include <sstream>
+
+#include "third_party/base/notreached.h"
+
+CPDF_StringArchiveStream::CPDF_StringArchiveStream(fxcrt::ostringstream* stream)
     : stream_(stream) {}
 
-CPDF_StringArchiveStream::~CPDF_StringArchiveStream() {}
-
-bool CPDF_StringArchiveStream::WriteByte(uint8_t byte) {
-  NOTREACHED();
-  return false;
-}
-
-bool CPDF_StringArchiveStream::WriteDWord(uint32_t i) {
-  NOTREACHED();
-  return false;
-}
+CPDF_StringArchiveStream::~CPDF_StringArchiveStream() = default;
 
 FX_FILESIZE CPDF_StringArchiveStream::CurrentOffset() const {
-  NOTREACHED();
-  return false;
+  NOTREACHED_NORETURN();
 }
 
-bool CPDF_StringArchiveStream::WriteBlock(const void* pData, size_t size) {
-  stream_->write(static_cast<const char*>(pData), size);
-  return true;
-}
-
-bool CPDF_StringArchiveStream::WriteString(ByteStringView str) {
-  stream_->write(str.unterminated_c_str(), str.GetLength());
+bool CPDF_StringArchiveStream::WriteBlock(pdfium::span<const uint8_t> buffer) {
+  stream_->write(reinterpret_cast<const char*>(buffer.data()), buffer.size());
   return true;
 }
diff --git a/core/fpdfapi/edit/cpdf_stringarchivestream.h b/core/fpdfapi/edit/cpdf_stringarchivestream.h
index 59d168f..3907e2c 100644
--- a/core/fpdfapi/edit/cpdf_stringarchivestream.h
+++ b/core/fpdfapi/edit/cpdf_stringarchivestream.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,21 +6,20 @@
 #define CORE_FPDFAPI_EDIT_CPDF_STRINGARCHIVESTREAM_H_
 
 #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:
-  explicit CPDF_StringArchiveStream(std::ostringstream* stream);
+  explicit CPDF_StringArchiveStream(fxcrt::ostringstream* stream);
   ~CPDF_StringArchiveStream() override;
 
-  // IFX_ArchiveStream
-  bool WriteByte(uint8_t byte) override;
-  bool WriteDWord(uint32_t i) override;
+  // IFX_ArchiveStream:
+  bool WriteBlock(pdfium::span<const uint8_t> buffer) override;
   FX_FILESIZE CurrentOffset() const override;
-  bool WriteBlock(const void* pData, size_t size) override;
-  bool WriteString(ByteStringView str) override;
 
  private:
-  std::ostringstream* stream_;
+  UnownedPtr<fxcrt::ostringstream> stream_;
 };
 
 #endif  // CORE_FPDFAPI_EDIT_CPDF_STRINGARCHIVESTREAM_H_
diff --git a/core/fpdfapi/font/BUILD.gn b/core/fpdfapi/font/BUILD.gn
index c30269a..ce89433 100644
--- a/core/fpdfapi/font/BUILD.gn
+++ b/core/fpdfapi/font/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -17,8 +17,6 @@
     "cpdf_cidfont.h",
     "cpdf_cmap.cpp",
     "cpdf_cmap.h",
-    "cpdf_cmapmanager.cpp",
-    "cpdf_cmapmanager.h",
     "cpdf_cmapparser.cpp",
     "cpdf_cmapparser.h",
     "cpdf_font.cpp",
@@ -40,17 +38,21 @@
     "cpdf_type3font.cpp",
     "cpdf_type3font.h",
   ]
-  configs += [ "../../../:pdfium_core_config" ]
+  configs += [
+    "../../../:pdfium_strict_config",
+    "../../../:pdfium_noshorten_config",
+  ]
   deps = [
+    "../../../constants",
     "../../fxcrt",
     "../../fxge",
     "../cmaps",
     "../parser",
   ]
-  if (is_mac) {
-    libs = [ "CoreFoundation.framework" ]
-  }
   visibility = [ "../../../*" ]
+  if (is_mac) {
+    frameworks = [ "CoreFoundation.framework" ]
+  }
 }
 
 pdfium_unittest_source_set("unittests") {
@@ -61,8 +63,9 @@
   ]
   deps = [
     ":font",
-    "../page",
+    "../page:unit_test_support",
     "../parser",
+    "../parser:unit_test_support",
     "../render",
   ]
   pdfium_root_dir = "../../../"
diff --git a/core/fpdfapi/font/cfx_cttgsubtable.cpp b/core/fpdfapi/font/cfx_cttgsubtable.cpp
index 49f93dc..c19649e 100644
--- a/core/fpdfapi/font/cfx_cttgsubtable.cpp
+++ b/core/fpdfapi/font/cfx_cttgsubtable.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,11 +6,14 @@
 
 #include "core/fpdfapi/font/cfx_cttgsubtable.h"
 
+#include <stdint.h>
+
 #include <utility>
 
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/stl_util.h"
 #include "core/fxge/cfx_fontmapper.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
 
 namespace {
 
@@ -24,341 +27,316 @@
 
 }  // 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) {
-  if ((gsub[0] << 24u | gsub[1] << 16u | gsub[2] << 8u | gsub[3]) != 0x00010000)
+bool CFX_CTTGSUBTable::LoadGSUBTable(pdfium::span<const uint8_t> gsub) {
+  if (FXSYS_UINT32_GET_MSBFIRST(gsub) != 0x00010000)
     return false;
 
-  return Parse(&gsub[gsub[4] << 8 | gsub[5]], &gsub[gsub[6] << 8 | gsub[7]],
-               &gsub[gsub[8] << 8 | gsub[9]]);
+  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 {
-  uint32_t vglyphnum = 0;
-  for (uint32_t item : m_featureSet) {
-    if (GetVerticalGlyphSub(FeatureList[item], glyphnum, &vglyphnum))
-      break;
+  for (uint32_t item : feature_set_) {
+    absl::optional<uint32_t> result =
+        GetVerticalGlyphSub(feature_list_[item], glyphnum);
+    if (result.has_value())
+      return result.value();
   }
-  return vglyphnum;
+  return 0;
 }
 
-bool CFX_CTTGSUBTable::GetVerticalGlyphSub(const TFeatureRecord& feature,
-                                           uint32_t glyphnum,
-                                           uint32_t* vglyphnum) const {
-  for (int index : feature.LookupListIndices) {
-    if (!pdfium::IndexInBounds(LookupList, index))
+absl::optional<uint32_t> CFX_CTTGSUBTable::GetVerticalGlyphSub(
+    const FeatureRecord& feature,
+    uint32_t glyphnum) const {
+  for (int index : feature.lookup_list_indices) {
+    if (!fxcrt::IndexInBounds(lookup_list_, index)) {
       continue;
-    if (LookupList[index].LookupType == 1 &&
-        GetVerticalGlyphSub2(LookupList[index], glyphnum, vglyphnum)) {
-      return true;
     }
+    if (lookup_list_[index].lookup_type != 1) {
+      continue;
+    }
+    absl::optional<uint32_t> result =
+        GetVerticalGlyphSub2(lookup_list_[index], glyphnum);
+    if (result.has_value())
+      return result.value();
   }
-  return false;
+  return absl::nullopt;
 }
 
-bool CFX_CTTGSUBTable::GetVerticalGlyphSub2(const TLookup& lookup,
-                                            uint32_t glyphnum,
-                                            uint32_t* vglyphnum) const {
-  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) {
-          *vglyphnum = glyphnum + tbl1->DeltaGlyphID;
-          return true;
-        }
-        break;
+absl::optional<uint32_t> CFX_CTTGSUBTable::GetVerticalGlyphSub2(
+    const Lookup& lookup,
+    uint32_t glyphnum) const {
+  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 (pdfium::IndexInBounds(tbl2->Substitutes, index)) {
-          *vglyphnum = tbl2->Substitutes[index];
-          return true;
-        }
-        break;
+    } else {
+      const auto& substitutes =
+          absl::get<DataVector<uint16_t>>(sub_table.table_data);
+      if (fxcrt::IndexInBounds(substitutes, index)) {
+        return substitutes[index];
       }
     }
   }
-  return false;
+  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 {
-  uint16_t ret = p[0] << 8 | p[1];
+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 ret = p[0] << 8 | p[1];
+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 {
-  uint32_t ret = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+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 ret = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+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 = std::vector<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 = std::vector<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)
-    ParseSingleSubst(&raw[GetUInt16(sp)], &subTable);
+  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 && format != 2) {
+    return absl::monostate();
+  }
+
   if (format == 1) {
-    auto rec = pdfium::MakeUnique<TCoverageFormat1>();
-    ParseCoverageFormat1(raw, rec.get());
-    return std::move(rec);
+    DataVector<uint16_t> glyph_array(GetUInt16(sp));
+    for (auto& glyph : glyph_array) {
+      glyph = GetUInt16(sp);
+    }
+    return glyph_array;
   }
-  if (format == 2) {
-    auto rec = pdfium::MakeUnique<TCoverageFormat2>();
-    ParseCoverageFormat2(raw, rec.get());
-    return std::move(rec);
+
+  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 nullptr;
+  return range_records;
 }
 
-void CFX_CTTGSUBTable::ParseCoverageFormat1(FT_Bytes raw,
-                                            TCoverageFormat1* rec) {
-  FT_Bytes sp = raw;
-  (void)GetUInt16(sp);
-  rec->GlyphArray = std::vector<uint16_t>(GetUInt16(sp));
-  for (auto& glyph : rec->GlyphArray)
-    glyph = GetUInt16(sp);
-}
-
-void CFX_CTTGSUBTable::ParseCoverageFormat2(FT_Bytes raw,
-                                            TCoverageFormat2* rec) {
-  FT_Bytes sp = raw;
-  (void)GetUInt16(sp);
-  rec->RangeRecords = std::vector<TRangeRecord>(GetUInt16(sp));
-  for (auto& rangeRec : rec->RangeRecords) {
-    rangeRec.Start = GetUInt16(sp);
-    rangeRec.End = GetUInt16(sp);
-    rangeRec.StartCoverageIndex = GetUInt16(sp);
+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;
   }
-}
 
-void CFX_CTTGSUBTable::ParseSingleSubst(FT_Bytes raw,
-                                        std::unique_ptr<TSubTableBase>* rec) {
-  FT_Bytes sp = raw;
-  uint16_t Format = GetUInt16(sp);
-  switch (Format) {
-    case 1:
-      *rec = pdfium::MakeUnique<TSubTable1>();
-      ParseSingleSubstFormat1(raw, static_cast<TSubTable1*>(rec->get()));
-      break;
-    case 2:
-      *rec = pdfium::MakeUnique<TSubTable2>();
-      ParseSingleSubstFormat2(raw, static_cast<TSubTable2*>(rec->get()));
-      break;
-  }
-}
-
-void CFX_CTTGSUBTable::ParseSingleSubstFormat1(FT_Bytes raw, TSubTable1* rec) {
-  FT_Bytes sp = raw;
-  GetUInt16(sp);
   uint16_t offset = GetUInt16(sp);
-  rec->Coverage = ParseCoverage(&raw[offset]);
-  rec->DeltaGlyphID = GetInt16(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;
 }
 
-void CFX_CTTGSUBTable::ParseSingleSubstFormat2(FT_Bytes raw, TSubTable2* rec) {
-  FT_Bytes sp = raw;
-  (void)GetUInt16(sp);
-  uint16_t offset = GetUInt16(sp);
-  rec->Coverage = ParseCoverage(&raw[offset]);
-  rec->Substitutes = std::vector<uint16_t>(GetUInt16(sp));
-  for (auto& substitute : rec->Substitutes)
-    substitute = GetUInt16(sp);
-}
+CFX_CTTGSUBTable::FeatureRecord::FeatureRecord() = default;
 
-CFX_CTTGSUBTable::TLangSysRecord::TLangSysRecord()
-    : LangSysTag(0), LookupOrder(0), ReqFeatureIndex(0) {}
+CFX_CTTGSUBTable::FeatureRecord::~FeatureRecord() = default;
 
-CFX_CTTGSUBTable::TLangSysRecord::~TLangSysRecord() {}
+CFX_CTTGSUBTable::RangeRecord::RangeRecord() = default;
 
-CFX_CTTGSUBTable::TScriptRecord::TScriptRecord()
-    : ScriptTag(0), DefaultLangSys(0) {}
+CFX_CTTGSUBTable::SubTable::SubTable() = default;
 
-CFX_CTTGSUBTable::TScriptRecord::~TScriptRecord() {}
+CFX_CTTGSUBTable::SubTable::SubTable(SubTable&& that) noexcept = default;
 
-CFX_CTTGSUBTable::TFeatureRecord::TFeatureRecord()
-    : FeatureTag(0), FeatureParams(0) {}
+CFX_CTTGSUBTable::SubTable& CFX_CTTGSUBTable::SubTable::operator=(
+    SubTable&& that) noexcept = default;
 
-CFX_CTTGSUBTable::TFeatureRecord::~TFeatureRecord() {}
+CFX_CTTGSUBTable::SubTable::~SubTable() = default;
 
-CFX_CTTGSUBTable::TRangeRecord::TRangeRecord()
-    : Start(0), End(0), StartCoverageIndex(0) {}
+CFX_CTTGSUBTable::Lookup::Lookup() = default;
 
-CFX_CTTGSUBTable::TCoverageFormat1::TCoverageFormat1() {
-  CoverageFormat = 1;
-}
+CFX_CTTGSUBTable::Lookup::Lookup(Lookup&& that) noexcept = default;
 
-CFX_CTTGSUBTable::TCoverageFormat1::~TCoverageFormat1() {}
+CFX_CTTGSUBTable::Lookup& CFX_CTTGSUBTable::Lookup::operator=(
+    Lookup&& that) noexcept = default;
 
-CFX_CTTGSUBTable::TCoverageFormat2::TCoverageFormat2() {
-  CoverageFormat = 2;
-}
-
-CFX_CTTGSUBTable::TCoverageFormat2::~TCoverageFormat2() {}
-
-CFX_CTTGSUBTable::TSubTableBase::TSubTableBase() {}
-
-CFX_CTTGSUBTable::TSubTableBase::~TSubTableBase() {}
-
-CFX_CTTGSUBTable::TSubTable1::TSubTable1() {
-  SubstFormat = 1;
-}
-
-CFX_CTTGSUBTable::TSubTable1::~TSubTable1() {}
-
-CFX_CTTGSUBTable::TSubTable2::TSubTable2() {
-  SubstFormat = 2;
-}
-
-CFX_CTTGSUBTable::TSubTable2::~TSubTable2() {}
-
-CFX_CTTGSUBTable::TLookup::TLookup() : LookupType(0), LookupFlag(0) {}
-
-CFX_CTTGSUBTable::TLookup::~TLookup() {}
+CFX_CTTGSUBTable::Lookup::~Lookup() = default;
diff --git a/core/fpdfapi/font/cfx_cttgsubtable.h b/core/fpdfapi/font/cfx_cttgsubtable.h
index 54e16b4..2a93cb9 100644
--- a/core/fpdfapi/font/cfx_cttgsubtable.h
+++ b/core/fpdfapi/font/cfx_cttgsubtable.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,148 +9,105 @@
 
 #include <stdint.h>
 
-#include <memory>
 #include <set>
 #include <vector>
 
-#include "core/fxge/fx_freetype.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxge/freetype/fx_freetype.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#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;
-    uint16_t LookupOrder;
-    uint16_t ReqFeatureIndex;
-    std::vector<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;
-    uint16_t DefaultLangSys;
-    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;
-    uint16_t FeatureParams;
-    std::vector<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;
-    uint16_t End;
-    uint16_t StartCoverageIndex;
+    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 {
-    virtual ~TCoverageFormatBase() = default;
-    uint16_t CoverageFormat;
-  };
+  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);
 
-  struct TCoverageFormat1 final : public TCoverageFormatBase {
-    TCoverageFormat1();
-    ~TCoverageFormat1() override;
+  absl::optional<uint32_t> GetVerticalGlyphSub(const FeatureRecord& feature,
+                                               uint32_t glyphnum) const;
+  absl::optional<uint32_t> GetVerticalGlyphSub2(const Lookup& lookup,
+                                                uint32_t glyphnum) const;
+  int GetCoverageIndex(const CoverageFormat& coverage, uint32_t g) const;
 
-    std::vector<uint16_t> GlyphArray;
-  };
+  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;
 
-  struct TCoverageFormat2 final : public TCoverageFormatBase {
-    TCoverageFormat2();
-    ~TCoverageFormat2() override;
-
-    std::vector<TRangeRecord> RangeRecords;
-  };
-
-  struct TDevice {
-    TDevice() : StartSize(0), EndSize(0), DeltaFormat(0) {}
-
-    uint16_t StartSize;
-    uint16_t EndSize;
-    uint16_t DeltaFormat;
-  };
-
-  struct TSubTableBase {
-    TSubTableBase();
-    virtual ~TSubTableBase();
-
-    std::unique_ptr<TCoverageFormatBase> Coverage;
-    uint16_t SubstFormat;
-  };
-
-  struct TSubTable1 final : public TSubTableBase {
-    TSubTable1();
-    ~TSubTable1() override;
-
-    int16_t DeltaGlyphID;
-  };
-
-  struct TSubTable2 final : public TSubTableBase {
-    TSubTable2();
-    ~TSubTable2() override;
-
-    std::vector<uint16_t> Substitutes;
-  };
-
-  struct TLookup {
-    TLookup();
-    ~TLookup();
-
-    uint16_t LookupType;
-    uint16_t LookupFlag;
-    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);
-  void ParseCoverageFormat1(FT_Bytes raw, TCoverageFormat1* rec);
-  void ParseCoverageFormat2(FT_Bytes raw, TCoverageFormat2* rec);
-  void ParseSingleSubst(FT_Bytes raw, std::unique_ptr<TSubTableBase>* rec);
-  void ParseSingleSubstFormat1(FT_Bytes raw, TSubTable1* rec);
-  void ParseSingleSubstFormat2(FT_Bytes raw, TSubTable2* rec);
-
-  bool GetVerticalGlyphSub(const TFeatureRecord& feature,
-                           uint32_t glyphnum,
-                           uint32_t* vglyphnum) const;
-  bool GetVerticalGlyphSub2(const TLookup& lookup,
-                            uint32_t glyphnum,
-                            uint32_t* vglyphnum) const;
-  int GetCoverageIndex(TCoverageFormatBase* 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;
-
-  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 a8d8597..3142525 100644
--- a/core/fpdfapi/font/cfx_stockfontarray.cpp
+++ b/core/fpdfapi/font/cfx_stockfontarray.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,19 +6,22 @@
 
 #include "core/fpdfapi/font/cfx_stockfontarray.h"
 
-#include <memory>
+#include <iterator>
 #include <utility>
 
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fxcrt/fx_memory.h"
+#include "third_party/base/check_op.h"
 
 CFX_StockFontArray::CFX_StockFontArray() = default;
 
 CFX_StockFontArray::~CFX_StockFontArray() {
-  for (size_t i = 0; i < FX_ArraySize(m_StockFonts); ++i) {
+  for (size_t i = 0; i < std::size(m_StockFonts); ++i) {
     if (m_StockFonts[i]) {
-      RetainPtr<CPDF_Dictionary> destroy(m_StockFonts[i]->GetFontDict());
+      // Ensure m_StockFonts[i]'s dict is cleared before releasing what
+      // may be the last reference to it.
+      RetainPtr<CPDF_Dictionary> destroy =
+          m_StockFonts[i]->GetMutableFontDict();
       m_StockFonts[i]->ClearFontDict();
     }
   }
@@ -26,14 +29,12 @@
 
 RetainPtr<CPDF_Font> CFX_StockFontArray::GetFont(
     CFX_FontMapper::StandardFont index) const {
-  if (index < FX_ArraySize(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,
-                                 const RetainPtr<CPDF_Font>& pFont) {
-  if (index < FX_ArraySize(m_StockFonts))
-    m_StockFonts[index] = pFont;
+                                 RetainPtr<CPDF_Font> pFont) {
+  if (index < std::size(m_StockFonts))
+    m_StockFonts[index] = std::move(pFont);
 }
diff --git a/core/fpdfapi/font/cfx_stockfontarray.h b/core/fpdfapi/font/cfx_stockfontarray.h
index 5e54704..30989d6 100644
--- a/core/fpdfapi/font/cfx_stockfontarray.h
+++ b/core/fpdfapi/font/cfx_stockfontarray.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,6 @@
 #ifndef CORE_FPDFAPI_FONT_CFX_STOCKFONTARRAY_H_
 #define CORE_FPDFAPI_FONT_CFX_STOCKFONTARRAY_H_
 
-#include <memory>
-
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/cfx_fontmapper.h"
 
@@ -20,8 +18,7 @@
   ~CFX_StockFontArray();
 
   RetainPtr<CPDF_Font> GetFont(CFX_FontMapper::StandardFont index) const;
-  void SetFont(CFX_FontMapper::StandardFont index,
-               const RetainPtr<CPDF_Font>& pFont);
+  void SetFont(CFX_FontMapper::StandardFont index, RetainPtr<CPDF_Font> pFont);
 
  private:
   RetainPtr<CPDF_Font> m_StockFonts[14];
diff --git a/core/fpdfapi/font/cpdf_cid2unicodemap.cpp b/core/fpdfapi/font/cpdf_cid2unicodemap.cpp
index abb23ac..fa14fe9 100644
--- a/core/fpdfapi/font/cpdf_cid2unicodemap.cpp
+++ b/core/fpdfapi/font/cpdf_cid2unicodemap.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fpdfapi/font/cpdf_cid2unicodemap.h b/core/fpdfapi/font/cpdf_cid2unicodemap.h
index e556917..6bc5c12 100644
--- a/core/fpdfapi/font/cpdf_cid2unicodemap.h
+++ b/core/fpdfapi/font/cpdf_cid2unicodemap.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -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 0c02b9e..7d2d549 100644
--- a/core/fpdfapi/font/cpdf_cidfont.cpp
+++ b/core/fpdfapi/font/cpdf_cidfont.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,6 +8,7 @@
 
 #include <algorithm>
 #include <limits>
+#include <utility>
 #include <vector>
 
 #include "build/build_config.h"
@@ -20,18 +21,30 @@
 #include "core/fpdfapi/font/cpdf_fontglobals.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "core/fxcrt/fixed_uninit_data_vector.h"
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_unicode.h"
+#include "core/fxcrt/stl_util.h"
 #include "core/fxge/fx_font.h"
-#include "third_party/base/numerics/safe_math.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/span.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/span.h"
 
 namespace {
 
-const uint16_t g_CharsetCPs[CIDSET_NUM_SETS] = {0, 936, 950, 932, 949, 1200};
+constexpr FX_CodePage kCharsetCodePages[CIDSET_NUM_SETS] = {
+    FX_CodePage::kDefANSI,
+    FX_CodePage::kChineseSimplified,
+    FX_CodePage::kChineseTraditional,
+    FX_CodePage::kShiftJIS,
+    FX_CodePage::kHangul,
+    FX_CodePage::kUTF16LE};
 
-const struct CIDTransform {
+struct CIDTransform {
   uint16_t cid;
   uint8_t a;
   uint8_t b;
@@ -39,7 +52,9 @@
   uint8_t d;
   uint8_t e;
   uint8_t f;
-} g_Japan1_VertCIDs[] = {
+};
+
+constexpr CIDTransform kJapan1VerticalCIDs[] = {
     {97, 129, 0, 0, 127, 55, 0},     {7887, 127, 0, 0, 127, 76, 89},
     {7888, 127, 0, 0, 127, 79, 94},  {7889, 0, 129, 127, 0, 17, 127},
     {7890, 0, 129, 127, 0, 17, 127}, {7891, 0, 129, 127, 0, 17, 127},
@@ -119,14 +134,17 @@
     {8818, 0, 129, 127, 0, 19, 114}, {8819, 0, 129, 127, 0, 218, 108},
 };
 
-// Boundary values to avoid integer overflow when multiplied by 1000.
-constexpr long kMinCBox = -2147483;
-constexpr long kMaxCBox = 2147483;
-
 // Boundary value to avoid integer overflow when adding 1/64th of the value.
 constexpr int kMaxRectTop = 2114445437;
 
-#if !defined(OS_WIN)
+int FTPosToCBoxInt(FT_Pos pos) {
+  // Boundary values to avoid integer overflow when multiplied by 1000.
+  constexpr FT_Pos kMinCBox = -2147483;
+  constexpr FT_Pos kMaxCBox = 2147483;
+  return static_cast<int>(std::clamp(pos, kMinCBox, kMaxCBox));
+}
+
+#if !BUILDFLAG(IS_WIN)
 
 bool IsValidEmbeddedCharcodeFromUnicodeCharset(CIDSet charset) {
   switch (charset) {
@@ -141,13 +159,13 @@
   }
 }
 
-wchar_t EmbeddedUnicodeFromCharcode(const FXCMAP_CMap* pEmbedMap,
+wchar_t EmbeddedUnicodeFromCharcode(const fxcmap::CMap* pEmbedMap,
                                     CIDSet charset,
                                     uint32_t charcode) {
   if (!IsValidEmbeddedCharcodeFromUnicodeCharset(charset))
     return 0;
 
-  uint16_t cid = CIDFromCharCode(pEmbedMap, charcode);
+  uint16_t cid = fxcmap::CIDFromCharCode(pEmbedMap, charcode);
   if (!cid)
     return 0;
 
@@ -156,7 +174,7 @@
   return cid < map.size() ? map[cid] : 0;
 }
 
-uint32_t EmbeddedCharcodeFromUnicode(const FXCMAP_CMap* pEmbedMap,
+uint32_t EmbeddedCharcodeFromUnicode(const fxcmap::CMap* pEmbedMap,
                                      CIDSet charset,
                                      wchar_t unicode) {
   if (!IsValidEmbeddedCharcodeFromUnicodeCharset(charset))
@@ -166,7 +184,7 @@
       CPDF_FontGlobals::GetInstance()->GetEmbeddedToUnicode(charset);
   for (uint32_t i = 0; i < map.size(); ++i) {
     if (map[i] == unicode) {
-      uint32_t charCode = CharCodeFromCID(pEmbedMap, i);
+      uint32_t charCode = fxcmap::CharCodeFromCID(pEmbedMap, i);
       if (charCode)
         return charCode;
     }
@@ -174,21 +192,21 @@
   return 0;
 }
 
-#endif  // !defined(OS_WIN)
+#endif  // !BUILDFLAG(IS_WIN)
 
-void FT_UseCIDCharmap(FXFT_FaceRec* face, int coding) {
+void FT_UseCIDCharmap(FXFT_FaceRec* face, CIDCoding coding) {
   int encoding;
   switch (coding) {
-    case CIDCODING_GB:
+    case CIDCoding::kGB:
       encoding = FT_ENCODING_GB2312;
       break;
-    case CIDCODING_BIG5:
+    case CIDCoding::kBIG5:
       encoding = FT_ENCODING_BIG5;
       break;
-    case CIDCODING_JIS:
+    case CIDCoding::kJIS:
       encoding = FT_ENCODING_SJIS;
       break;
-    case CIDCODING_KOREA:
+    case CIDCoding::kKOREA:
       encoding = FT_ENCODING_JOHAB;
       break;
     default:
@@ -197,586 +215,32 @@
   int err = FXFT_Select_Charmap(face, encoding);
   if (err)
     err = FXFT_Select_Charmap(face, FT_ENCODING_UNICODE);
-  if (err && FXFT_Get_Face_Charmaps(face))
-    FT_Set_Charmap(face, *FXFT_Get_Face_Charmaps(face));
+  if (err && face->charmaps)
+    FT_Set_Charmap(face, face->charmaps[0]);
 }
 
-bool IsMetricForCID(const uint32_t* pEntry, uint16_t CID) {
-  return pEntry[0] <= CID && pEntry[1] >= CID;
+bool IsMetricForCID(const int* pEntry, uint16_t cid) {
+  return pEntry[0] <= cid && pEntry[1] >= cid;
 }
 
-}  // namespace
-
-CPDF_CIDFont::CPDF_CIDFont(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict)
-    : CPDF_Font(pDocument, pFontDict) {
-  for (size_t i = 0; i < FX_ArraySize(m_CharBBox); ++i)
-    m_CharBBox[i] = FX_RECT(-1, -1, -1, -1);
-}
-
-CPDF_CIDFont::~CPDF_CIDFont() {}
-
-bool CPDF_CIDFont::IsCIDFont() const {
-  return true;
-}
-
-const CPDF_CIDFont* CPDF_CIDFont::AsCIDFont() const {
-  return this;
-}
-
-CPDF_CIDFont* CPDF_CIDFont::AsCIDFont() {
-  return this;
-}
-
-uint16_t CPDF_CIDFont::CIDFromCharCode(uint32_t charcode) const {
-  return m_pCMap ? m_pCMap->CIDFromCharCode(charcode)
-                 : static_cast<uint16_t>(charcode);
-}
-
-bool CPDF_CIDFont::IsVertWriting() const {
-  return m_pCMap && m_pCMap->IsVertWriting();
-}
-
-WideString CPDF_CIDFont::UnicodeFromCharCode(uint32_t charcode) const {
-  WideString str = CPDF_Font::UnicodeFromCharCode(charcode);
-  if (!str.IsEmpty())
-    return str;
-  wchar_t ret = GetUnicodeFromCharCode(charcode);
-  return ret ? ret : WideString();
-}
-
-wchar_t CPDF_CIDFont::GetUnicodeFromCharCode(uint32_t charcode) const {
-  switch (m_pCMap->GetCoding()) {
-    case CIDCODING_UCS2:
-    case CIDCODING_UTF16:
-      return static_cast<wchar_t>(charcode);
-    case CIDCODING_CID:
-      if (!m_pCID2UnicodeMap || !m_pCID2UnicodeMap->IsLoaded())
-        return 0;
-      return m_pCID2UnicodeMap->UnicodeFromCID(static_cast<uint16_t>(charcode));
-  }
-  if (m_pCID2UnicodeMap && m_pCID2UnicodeMap->IsLoaded() && m_pCMap->IsLoaded())
-    return m_pCID2UnicodeMap->UnicodeFromCID(CIDFromCharCode(charcode));
-
-#if defined(OS_WIN)
-  wchar_t unicode;
-  int charsize = 1;
-  if (charcode > 255) {
-    charcode = (charcode % 256) * 256 + (charcode / 256);
-    charsize = 2;
-  }
-  int ret = FXSYS_MultiByteToWideChar(g_CharsetCPs[m_pCMap->GetCoding()], 0,
-                                      reinterpret_cast<const char*>(&charcode),
-                                      charsize, &unicode, 1);
-  return ret == 1 ? unicode : 0;
-#else
-  if (!m_pCMap->GetEmbedMap())
-    return 0;
-  return EmbeddedUnicodeFromCharcode(m_pCMap->GetEmbedMap(),
-                                     m_pCMap->GetCharset(), charcode);
-#endif
-}
-
-uint32_t CPDF_CIDFont::CharCodeFromUnicode(wchar_t unicode) const {
-  uint32_t charcode = CPDF_Font::CharCodeFromUnicode(unicode);
-  if (charcode)
-    return charcode;
-  switch (m_pCMap->GetCoding()) {
-    case CIDCODING_UNKNOWN:
-      return 0;
-    case CIDCODING_UCS2:
-    case CIDCODING_UTF16:
-      return unicode;
-    case CIDCODING_CID: {
-      if (!m_pCID2UnicodeMap || !m_pCID2UnicodeMap->IsLoaded())
-        return 0;
-      uint32_t CID = 0;
-      while (CID < 65536) {
-        wchar_t this_unicode =
-            m_pCID2UnicodeMap->UnicodeFromCID(static_cast<uint16_t>(CID));
-        if (this_unicode == unicode)
-          return CID;
-        CID++;
-      }
-      break;
-    }
-  }
-
-  if (unicode < 0x80)
-    return static_cast<uint32_t>(unicode);
-  if (m_pCMap->GetCoding() == CIDCODING_CID)
-    return 0;
-#if defined(OS_WIN)
-  uint8_t buffer[32];
-  int ret = FXSYS_WideCharToMultiByte(
-      g_CharsetCPs[m_pCMap->GetCoding()], 0, &unicode, 1,
-      reinterpret_cast<char*>(buffer), 4, nullptr, nullptr);
-  if (ret == 1)
-    return buffer[0];
-  if (ret == 2)
-    return buffer[0] * 256 + buffer[1];
-#else
-  if (m_pCMap->GetEmbedMap()) {
-    return EmbeddedCharcodeFromUnicode(m_pCMap->GetEmbedMap(),
-                                       m_pCMap->GetCharset(), unicode);
-  }
-#endif
-  return 0;
-}
-
-bool CPDF_CIDFont::Load() {
-  if (m_pFontDict->GetStringFor("Subtype") == "TrueType") {
-    LoadGB2312();
-    return true;
-  }
-
-  const CPDF_Array* pFonts = m_pFontDict->GetArrayFor("DescendantFonts");
-  if (!pFonts || pFonts->size() != 1)
-    return false;
-
-  const CPDF_Dictionary* pCIDFontDict = pFonts->GetDictAt(0);
-  if (!pCIDFontDict)
-    return false;
-
-  m_BaseFontName = pCIDFontDict->GetStringFor("BaseFont");
-  if ((m_BaseFontName.Compare("CourierStd") == 0 ||
-       m_BaseFontName.Compare("CourierStd-Bold") == 0 ||
-       m_BaseFontName.Compare("CourierStd-BoldOblique") == 0 ||
-       m_BaseFontName.Compare("CourierStd-Oblique") == 0) &&
-      !IsEmbedded()) {
-    m_bAdobeCourierStd = true;
-  }
-
-  CPDF_Object* pEncoding = m_pFontDict->GetDirectObjectFor("Encoding");
-  if (!pEncoding)
-    return false;
-
-  ByteString subtype = pCIDFontDict->GetStringFor("Subtype");
-  m_bType1 = (subtype == "CIDFontType0");
-
-  CPDF_CMapManager* manager = CPDF_FontGlobals::GetInstance()->GetCMapManager();
-  if (pEncoding->IsName()) {
-    ByteString cmap = pEncoding->GetString();
-    m_pCMap = manager->GetPredefinedCMap(cmap);
-  } else if (CPDF_Stream* pStream = pEncoding->AsStream()) {
-    auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
-    pAcc->LoadAllDataFiltered();
-    pdfium::span<const uint8_t> span = pAcc->GetSpan();
-    m_pCMap = pdfium::MakeRetain<CPDF_CMap>(span);
-  } else {
-    return false;
-  }
-
-  const CPDF_Dictionary* pFontDesc = pCIDFontDict->GetDictFor("FontDescriptor");
-  if (pFontDesc)
-    LoadFontDescriptor(pFontDesc);
-
-  m_Charset = m_pCMap->GetCharset();
-  if (m_Charset == CIDSET_UNKNOWN) {
-    const CPDF_Dictionary* pCIDInfo = pCIDFontDict->GetDictFor("CIDSystemInfo");
-    if (pCIDInfo) {
-      m_Charset = CPDF_CMapParser::CharsetFromOrdering(
-          pCIDInfo->GetStringFor("Ordering").AsStringView());
-    }
-  }
-  if (m_Charset != CIDSET_UNKNOWN) {
-    m_pCID2UnicodeMap = manager->GetCID2UnicodeMap(m_Charset);
-  }
-  if (m_Font.GetFaceRec()) {
-    if (m_bType1)
-      FXFT_Select_Charmap(m_Font.GetFaceRec(), FT_ENCODING_UNICODE);
-    else
-      FT_UseCIDCharmap(m_Font.GetFaceRec(), m_pCMap->GetCoding());
-  }
-  m_DefaultWidth = pCIDFontDict->GetIntegerFor("DW", 1000);
-  const CPDF_Array* pWidthArray = pCIDFontDict->GetArrayFor("W");
-  if (pWidthArray)
-    LoadMetricsArray(pWidthArray, &m_WidthList, 1);
-  if (!IsEmbedded())
-    LoadSubstFont();
-
-  const CPDF_Object* pmap = pCIDFontDict->GetDirectObjectFor("CIDToGIDMap");
-  if (pmap) {
-    if (const CPDF_Stream* pStream = pmap->AsStream()) {
-      m_pStreamAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
-      m_pStreamAcc->LoadAllDataFiltered();
-    } else if (m_pFontFile && pmap->GetString() == "Identity") {
-      m_bCIDIsGID = true;
-    }
-  }
-
-  CheckFontMetrics();
-  if (IsVertWriting()) {
-    pWidthArray = pCIDFontDict->GetArrayFor("W2");
-    if (pWidthArray)
-      LoadMetricsArray(pWidthArray, &m_VertMetrics, 3);
-    const CPDF_Array* pDefaultArray = pCIDFontDict->GetArrayFor("DW2");
-    if (pDefaultArray) {
-      m_DefaultVY = pDefaultArray->GetIntegerAt(0);
-      m_DefaultW1 = pDefaultArray->GetIntegerAt(1);
-    }
-  }
-  return true;
-}
-
-FX_RECT CPDF_CIDFont::GetCharBBox(uint32_t charcode) {
-  if (charcode < 256 && m_CharBBox[charcode].right != -1)
-    return m_CharBBox[charcode];
-
-  FX_RECT rect;
-  bool bVert = false;
-  int glyph_index = GlyphFromCharCode(charcode, &bVert);
-  FXFT_FaceRec* face = m_Font.GetFaceRec();
-  if (face) {
-    if (FXFT_Is_Face_Tricky(face)) {
-      int err =
-          FT_Load_Glyph(face, glyph_index, FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
-      if (!err) {
-        FT_Glyph glyph;
-        err = FT_Get_Glyph(face->glyph, &glyph);
-        if (!err) {
-          FT_BBox cbox;
-          FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &cbox);
-          cbox.xMin = pdfium::clamp(cbox.xMin, kMinCBox, kMaxCBox);
-          cbox.xMax = pdfium::clamp(cbox.xMax, kMinCBox, kMaxCBox);
-          cbox.yMin = pdfium::clamp(cbox.yMin, kMinCBox, kMaxCBox);
-          cbox.yMax = pdfium::clamp(cbox.yMax, kMinCBox, kMaxCBox);
-          int pixel_size_x = face->size->metrics.x_ppem;
-          int pixel_size_y = face->size->metrics.y_ppem;
-          if (pixel_size_x == 0 || pixel_size_y == 0) {
-            rect = FX_RECT(cbox.xMin, cbox.yMax, cbox.xMax, cbox.yMin);
-          } else {
-            rect = FX_RECT(cbox.xMin * 1000 / pixel_size_x,
-                           cbox.yMax * 1000 / pixel_size_y,
-                           cbox.xMax * 1000 / pixel_size_x,
-                           cbox.yMin * 1000 / pixel_size_y);
-          }
-          rect.top = std::min(rect.top,
-                              static_cast<int>(FXFT_Get_Face_Ascender(face)));
-          rect.bottom = std::max(
-              rect.bottom, static_cast<int>(FXFT_Get_Face_Descender(face)));
-          FT_Done_Glyph(glyph);
-        }
-      }
-    } else {
-      int err = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE);
-      if (err == 0) {
-        rect = FX_RECT(TT2PDF(FXFT_Get_Glyph_HoriBearingX(face), face),
-                       TT2PDF(FXFT_Get_Glyph_HoriBearingY(face), face),
-                       TT2PDF(FXFT_Get_Glyph_HoriBearingX(face) +
-                                  FXFT_Get_Glyph_Width(face),
-                              face),
-                       TT2PDF(FXFT_Get_Glyph_HoriBearingY(face) -
-                                  FXFT_Get_Glyph_Height(face),
-                              face));
-        if (rect.top <= kMaxRectTop)
-          rect.top += rect.top / 64;
-        else
-          rect.top = std::numeric_limits<int>::max();
-      }
-    }
-  }
-  if (!m_pFontFile && m_Charset == CIDSET_JAPAN1) {
-    uint16_t CID = CIDFromCharCode(charcode);
-    const uint8_t* pTransform = GetCIDTransform(CID);
-    if (pTransform && !bVert) {
-      CFX_Matrix matrix(CIDTransformToFloat(pTransform[0]),
-                        CIDTransformToFloat(pTransform[1]),
-                        CIDTransformToFloat(pTransform[2]),
-                        CIDTransformToFloat(pTransform[3]),
-                        CIDTransformToFloat(pTransform[4]) * 1000,
-                        CIDTransformToFloat(pTransform[5]) * 1000);
-      rect = matrix.TransformRect(CFX_FloatRect(rect)).GetOuterRect();
-    }
-  }
-  if (charcode < 256)
-    m_CharBBox[charcode] = rect;
-
-  return rect;
-}
-
-uint32_t CPDF_CIDFont::GetCharWidthF(uint32_t charcode) {
-  if (charcode < 0x80 && m_bAnsiWidthsFixed)
-    return (charcode >= 32 && charcode < 127) ? 500 : 0;
-
-  uint16_t cid = CIDFromCharCode(charcode);
-  size_t size = m_WidthList.size();
-  const uint32_t* pList = m_WidthList.data();
-  for (size_t i = 0; i < size; i += 3) {
-    const uint32_t* pEntry = pList + i;
-    if (IsMetricForCID(pEntry, cid))
-      return pEntry[2];
-  }
-  return m_DefaultWidth;
-}
-
-short CPDF_CIDFont::GetVertWidth(uint16_t CID) const {
-  size_t vertsize = m_VertMetrics.size() / 5;
-  if (vertsize) {
-    const uint32_t* pTable = m_VertMetrics.data();
-    for (size_t i = 0; i < vertsize; i++) {
-      const uint32_t* pEntry = pTable + (i * 5);
-      if (IsMetricForCID(pEntry, CID))
-        return static_cast<short>(pEntry[2]);
-    }
-  }
-  return m_DefaultW1;
-}
-
-void CPDF_CIDFont::GetVertOrigin(uint16_t CID, short& vx, short& vy) const {
-  size_t vertsize = m_VertMetrics.size() / 5;
-  if (vertsize) {
-    const uint32_t* pTable = m_VertMetrics.data();
-    for (size_t i = 0; i < vertsize; i++) {
-      const uint32_t* pEntry = pTable + (i * 5);
-      if (IsMetricForCID(pEntry, CID)) {
-        vx = static_cast<short>(pEntry[3]);
-        vy = static_cast<short>(pEntry[4]);
-        return;
-      }
-    }
-  }
-  uint32_t dwWidth = m_DefaultWidth;
-  size_t size = m_WidthList.size();
-  const uint32_t* pList = m_WidthList.data();
-  for (size_t i = 0; i < size; i += 3) {
-    const uint32_t* pEntry = pList + i;
-    if (IsMetricForCID(pEntry, CID)) {
-      dwWidth = pEntry[2];
-      break;
-    }
-  }
-  vx = static_cast<short>(dwWidth) / 2;
-  vy = m_DefaultVY;
-}
-
-int CPDF_CIDFont::GetGlyphIndex(uint32_t unicode, bool* pVertGlyph) {
-  if (pVertGlyph)
-    *pVertGlyph = false;
-
-  FXFT_FaceRec* face = m_Font.GetFaceRec();
-  int index = FT_Get_Char_Index(face, unicode);
-  if (unicode == 0x2502)
-    return index;
-
-  if (!index || !IsVertWriting())
-    return index;
-
-  if (m_pTTGSUBTable)
-    return GetVerticalGlyph(index, pVertGlyph);
-
-  if (!m_Font.GetSubData()) {
-    unsigned long length = 0;
-    int error = FT_Load_Sfnt_Table(face, FT_MAKE_TAG('G', 'S', 'U', 'B'), 0,
-                                   nullptr, &length);
-    if (!error)
-      m_Font.SetSubData(FX_Alloc(uint8_t, length));
-  }
-  int error = FT_Load_Sfnt_Table(face, FT_MAKE_TAG('G', 'S', 'U', 'B'), 0,
-                                 m_Font.GetSubData(), nullptr);
-  if (error || !m_Font.GetSubData())
-    return index;
-
-  m_pTTGSUBTable = pdfium::MakeUnique<CFX_CTTGSUBTable>(m_Font.GetSubData());
-  return GetVerticalGlyph(index, pVertGlyph);
-}
-
-int CPDF_CIDFont::GetVerticalGlyph(int index, bool* pVertGlyph) {
-  uint32_t vindex = m_pTTGSUBTable->GetVerticalGlyph(index);
-  if (!vindex)
-    return index;
-
-  index = vindex;
-  if (pVertGlyph)
-    *pVertGlyph = true;
-  return index;
-}
-
-int CPDF_CIDFont::GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) {
-  if (pVertGlyph)
-    *pVertGlyph = false;
-
-  if (!m_pFontFile && (!m_pStreamAcc || m_pCID2UnicodeMap)) {
-    uint16_t cid = CIDFromCharCode(charcode);
-    wchar_t unicode = 0;
-    if (m_bCIDIsGID) {
-#if defined(OS_MACOSX)
-      if (FontStyleIsSymbolic(m_Flags))
-        return cid;
-
-      WideString uni_str = UnicodeFromCharCode(charcode);
-      if (uni_str.IsEmpty())
-        return cid;
-
-      unicode = uni_str[0];
-#else
-      return cid;
-#endif
-    } else {
-      if (cid && m_pCID2UnicodeMap && m_pCID2UnicodeMap->IsLoaded())
-        unicode = m_pCID2UnicodeMap->UnicodeFromCID(cid);
-      if (unicode == 0)
-        unicode = GetUnicodeFromCharCode(charcode);
-      if (unicode == 0) {
-        WideString unicode_str = UnicodeFromCharCode(charcode);
-        if (!unicode_str.IsEmpty())
-          unicode = unicode_str[0];
-      }
-    }
-    FXFT_FaceRec* face = m_Font.GetFaceRec();
-    if (unicode == 0) {
-      if (!m_bAdobeCourierStd)
-        return charcode ? static_cast<int>(charcode) : -1;
-
-      charcode += 31;
-      bool bMSUnicode = FT_UseTTCharmap(face, 3, 1);
-      bool bMacRoman = !bMSUnicode && FT_UseTTCharmap(face, 1, 0);
-      int iBaseEncoding = PDFFONT_ENCODING_STANDARD;
-      if (bMSUnicode)
-        iBaseEncoding = PDFFONT_ENCODING_WINANSI;
-      else if (bMacRoman)
-        iBaseEncoding = PDFFONT_ENCODING_MACROMAN;
-      const char* name =
-          GetAdobeCharName(iBaseEncoding, std::vector<ByteString>(), charcode);
-      if (!name)
-        return charcode ? static_cast<int>(charcode) : -1;
-
-      int index = 0;
-      uint16_t name_unicode = PDF_UnicodeFromAdobeName(name);
-      if (!name_unicode)
-        return charcode ? static_cast<int>(charcode) : -1;
-
-      if (iBaseEncoding == PDFFONT_ENCODING_STANDARD)
-        return FT_Get_Char_Index(face, name_unicode);
-
-      if (iBaseEncoding == PDFFONT_ENCODING_WINANSI) {
-        index = FT_Get_Char_Index(face, name_unicode);
-      } else {
-        ASSERT(iBaseEncoding == PDFFONT_ENCODING_MACROMAN);
-        uint32_t maccode =
-            FT_CharCodeFromUnicode(FT_ENCODING_APPLE_ROMAN, name_unicode);
-        index = maccode ? FT_Get_Char_Index(face, maccode)
-                        : FXFT_Get_Name_Index(face, name);
-      }
-      if (index == 0 || index == 0xffff)
-        return charcode ? static_cast<int>(charcode) : -1;
-      return index;
-    }
-    if (m_Charset == CIDSET_JAPAN1) {
-      if (unicode == '\\') {
-        unicode = '/';
-#if !defined(OS_MACOSX)
-      } else if (unicode == 0xa5) {
-        unicode = 0x5c;
-#endif
-      }
-    }
-    if (!face)
-      return unicode;
-
-    int err = FXFT_Select_Charmap(face, FT_ENCODING_UNICODE);
-    if (err) {
-      int i;
-      for (i = 0; i < FXFT_Get_Face_CharmapCount(face); i++) {
-        uint32_t ret = FT_CharCodeFromUnicode(
-            FXFT_Get_Charmap_Encoding(FXFT_Get_Face_Charmaps(face)[i]),
-            static_cast<wchar_t>(charcode));
-        if (ret == 0)
-          continue;
-        FT_Set_Charmap(face, FXFT_Get_Face_Charmaps(face)[i]);
-        unicode = static_cast<wchar_t>(ret);
-        break;
-      }
-      if (i == FXFT_Get_Face_CharmapCount(face) && i) {
-        FT_Set_Charmap(face, FXFT_Get_Face_Charmaps(face)[0]);
-        unicode = static_cast<wchar_t>(charcode);
-      }
-    }
-    if (FXFT_Get_Face_Charmap(face)) {
-      int index = GetGlyphIndex(unicode, pVertGlyph);
-      return index != 0 ? index : -1;
-    }
-    return unicode;
-  }
-
-  if (!m_Font.GetFaceRec())
-    return -1;
-
-  uint16_t cid = CIDFromCharCode(charcode);
-  if (!m_pStreamAcc) {
-    if (m_bType1)
-      return cid;
-    if (m_pFontFile && m_pCMap->IsDirectCharcodeToCIDTableIsEmpty())
-      return cid;
-    if (m_pCMap->GetCoding() == CIDCODING_UNKNOWN ||
-        !FXFT_Get_Face_Charmap(m_Font.GetFaceRec())) {
-      return cid;
-    }
-    if (FXFT_Get_Charmap_Encoding(FXFT_Get_Face_Charmap(m_Font.GetFaceRec())) ==
-        FT_ENCODING_UNICODE) {
-      WideString unicode_str = UnicodeFromCharCode(charcode);
-      if (unicode_str.IsEmpty())
-        return -1;
-
-      charcode = unicode_str[0];
-    }
-    return GetGlyphIndex(charcode, pVertGlyph);
-  }
-  uint32_t byte_pos = cid * 2;
-  if (byte_pos + 2 > m_pStreamAcc->GetSize())
-    return -1;
-
-  const uint8_t* pdata = m_pStreamAcc->GetData() + byte_pos;
-  return pdata[0] * 256 + pdata[1];
-}
-
-uint32_t CPDF_CIDFont::GetNextChar(ByteStringView pString,
-                                   size_t* pOffset) const {
-  return m_pCMap->GetNextChar(pString, pOffset);
-}
-
-int CPDF_CIDFont::GetCharSize(uint32_t charcode) const {
-  return m_pCMap->GetCharSize(charcode);
-}
-
-size_t CPDF_CIDFont::CountChar(ByteStringView pString) const {
-  return m_pCMap->CountChar(pString);
-}
-
-int CPDF_CIDFont::AppendChar(char* str, uint32_t charcode) const {
-  return m_pCMap->AppendChar(str, charcode);
-}
-
-bool CPDF_CIDFont::IsUnicodeCompatible() const {
-  if (m_pCID2UnicodeMap && m_pCID2UnicodeMap->IsLoaded() && m_pCMap->IsLoaded())
-    return true;
-  return m_pCMap->GetCoding() != CIDCODING_UNKNOWN;
-}
-
-void CPDF_CIDFont::LoadSubstFont() {
-  pdfium::base::CheckedNumeric<int> safeStemV(m_StemV);
-  safeStemV *= 5;
-  m_Font.LoadSubst(m_BaseFontName, !m_bType1, m_Flags,
-                   safeStemV.ValueOrDefault(FXFONT_FW_NORMAL), m_ItalicAngle,
-                   g_CharsetCPs[m_Charset], IsVertWriting());
-}
-
-void CPDF_CIDFont::LoadMetricsArray(const CPDF_Array* pArray,
-                                    std::vector<uint32_t>* result,
-                                    int nElements) {
+void LoadMetricsArray(RetainPtr<const CPDF_Array> pArray,
+                      std::vector<int>* result,
+                      int nElements) {
   int width_status = 0;
   int iCurElement = 0;
-  uint32_t first_code = 0;
-  uint32_t last_code = 0;
+  int first_code = 0;
+  int last_code = 0;
   for (size_t i = 0; i < pArray->size(); i++) {
-    const CPDF_Object* pObj = pArray->GetDirectObjectAt(i);
+    RetainPtr<const CPDF_Object> pObj = pArray->GetDirectObjectAt(i);
     if (!pObj)
       continue;
 
-    if (const CPDF_Array* pObjArray = pObj->AsArray()) {
+    const CPDF_Array* pObjArray = pObj->AsArray();
+    if (pObjArray) {
       if (width_status != 1)
         return;
-      if (first_code >
-          std::numeric_limits<uint32_t>::max() - pObjArray->size()) {
+      if (first_code > std::numeric_limits<int>::max() -
+                           fxcrt::CollectionSize<int>(*pObjArray)) {
         width_status = 0;
         continue;
       }
@@ -811,21 +275,607 @@
   }
 }
 
+}  // namespace
+
+CPDF_CIDFont::CPDF_CIDFont(CPDF_Document* pDocument,
+                           RetainPtr<CPDF_Dictionary> pFontDict)
+    : CPDF_Font(pDocument, std::move(pFontDict)) {
+  for (size_t i = 0; i < std::size(m_CharBBox); ++i)
+    m_CharBBox[i] = FX_RECT(-1, -1, -1, -1);
+}
+
+CPDF_CIDFont::~CPDF_CIDFont() = default;
+
+bool CPDF_CIDFont::IsCIDFont() const {
+  return true;
+}
+
+const CPDF_CIDFont* CPDF_CIDFont::AsCIDFont() const {
+  return this;
+}
+
+CPDF_CIDFont* CPDF_CIDFont::AsCIDFont() {
+  return this;
+}
+
+uint16_t CPDF_CIDFont::CIDFromCharCode(uint32_t charcode) const {
+  return m_pCMap ? m_pCMap->CIDFromCharCode(charcode)
+                 : static_cast<uint16_t>(charcode);
+}
+
+bool CPDF_CIDFont::IsVertWriting() const {
+  return m_pCMap && m_pCMap->IsVertWriting();
+}
+
+WideString CPDF_CIDFont::UnicodeFromCharCode(uint32_t charcode) const {
+  WideString str = CPDF_Font::UnicodeFromCharCode(charcode);
+  if (!str.IsEmpty())
+    return str;
+  wchar_t ret = GetUnicodeFromCharCode(charcode);
+  return ret ? WideString(ret) : WideString();
+}
+
+wchar_t CPDF_CIDFont::GetUnicodeFromCharCode(uint32_t charcode) const {
+  switch (m_pCMap->GetCoding()) {
+    case CIDCoding::kUCS2:
+    case CIDCoding::kUTF16:
+      return static_cast<wchar_t>(charcode);
+    case CIDCoding::kCID:
+      if (!m_pCID2UnicodeMap || !m_pCID2UnicodeMap->IsLoaded())
+        return 0;
+      return m_pCID2UnicodeMap->UnicodeFromCID(static_cast<uint16_t>(charcode));
+    default:
+      break;
+  }
+  if (m_pCID2UnicodeMap && m_pCID2UnicodeMap->IsLoaded() && m_pCMap->IsLoaded())
+    return m_pCID2UnicodeMap->UnicodeFromCID(CIDFromCharCode(charcode));
+
+#if BUILDFLAG(IS_WIN)
+  wchar_t unicode;
+  int charsize = 1;
+  if (charcode > 255) {
+    charcode = (charcode % 256) * 256 + (charcode / 256);
+    charsize = 2;
+  }
+  size_t ret = FX_MultiByteToWideChar(
+      kCharsetCodePages[static_cast<size_t>(m_pCMap->GetCoding())],
+      ByteStringView(reinterpret_cast<const char*>(&charcode), charsize),
+      pdfium::make_span(&unicode, 1));
+  return ret == 1 ? unicode : 0;
+#else
+  if (!m_pCMap->GetEmbedMap())
+    return 0;
+  return EmbeddedUnicodeFromCharcode(m_pCMap->GetEmbedMap(),
+                                     m_pCMap->GetCharset(), charcode);
+#endif
+}
+
+uint32_t CPDF_CIDFont::CharCodeFromUnicode(wchar_t unicode) const {
+  uint32_t charcode = CPDF_Font::CharCodeFromUnicode(unicode);
+  if (charcode)
+    return charcode;
+
+  switch (m_pCMap->GetCoding()) {
+    case CIDCoding::kUNKNOWN:
+      return 0;
+    case CIDCoding::kUCS2:
+    case CIDCoding::kUTF16:
+      return unicode;
+    case CIDCoding::kCID: {
+      if (!m_pCID2UnicodeMap || !m_pCID2UnicodeMap->IsLoaded())
+        return 0;
+      uint32_t cid = 0;
+      while (cid < 65536) {
+        wchar_t this_unicode =
+            m_pCID2UnicodeMap->UnicodeFromCID(static_cast<uint16_t>(cid));
+        if (this_unicode == unicode)
+          return cid;
+        cid++;
+      }
+      break;
+    }
+    default:
+      break;
+  }
+
+  if (unicode < 0x80)
+    return static_cast<uint32_t>(unicode);
+  if (m_pCMap->GetCoding() == CIDCoding::kCID)
+    return 0;
+#if BUILDFLAG(IS_WIN)
+  uint8_t buffer[32];
+  size_t ret = FX_WideCharToMultiByte(
+      kCharsetCodePages[static_cast<size_t>(m_pCMap->GetCoding())],
+      WideStringView(&unicode, 1),
+      pdfium::make_span(reinterpret_cast<char*>(buffer), 4));
+  if (ret == 1)
+    return buffer[0];
+  if (ret == 2)
+    return buffer[0] * 256 + buffer[1];
+#else
+  if (m_pCMap->GetEmbedMap()) {
+    return EmbeddedCharcodeFromUnicode(m_pCMap->GetEmbedMap(),
+                                       m_pCMap->GetCharset(), unicode);
+  }
+#endif
+  return 0;
+}
+
+bool CPDF_CIDFont::Load() {
+  if (m_pFontDict->GetByteStringFor("Subtype") == "TrueType") {
+    LoadGB2312();
+    return true;
+  }
+
+  RetainPtr<const CPDF_Array> pFonts =
+      m_pFontDict->GetArrayFor("DescendantFonts");
+  if (!pFonts || pFonts->size() != 1)
+    return false;
+
+  RetainPtr<const CPDF_Dictionary> pCIDFontDict = pFonts->GetDictAt(0);
+  if (!pCIDFontDict)
+    return false;
+
+  m_BaseFontName = pCIDFontDict->GetByteStringFor("BaseFont");
+  if ((m_BaseFontName == "CourierStd" || m_BaseFontName == "CourierStd-Bold" ||
+       m_BaseFontName == "CourierStd-BoldOblique" ||
+       m_BaseFontName == "CourierStd-Oblique") &&
+      !IsEmbedded()) {
+    m_bAdobeCourierStd = true;
+  }
+
+  RetainPtr<const CPDF_Object> pEncoding =
+      m_pFontDict->GetDirectObjectFor("Encoding");
+  if (!pEncoding)
+    return false;
+
+  ByteString subtype = pCIDFontDict->GetByteStringFor("Subtype");
+  m_FontType =
+      subtype == "CIDFontType0" ? CIDFontType::kType1 : CIDFontType::kTrueType;
+
+  if (!pEncoding->IsName() && !pEncoding->IsStream())
+    return false;
+
+  auto* pFontGlobals = CPDF_FontGlobals::GetInstance();
+  const CPDF_Stream* pEncodingStream = pEncoding->AsStream();
+  if (pEncodingStream) {
+    auto pAcc =
+        pdfium::MakeRetain<CPDF_StreamAcc>(pdfium::WrapRetain(pEncodingStream));
+    pAcc->LoadAllDataFiltered();
+    pdfium::span<const uint8_t> span = pAcc->GetSpan();
+    m_pCMap = pdfium::MakeRetain<CPDF_CMap>(span);
+  } else {
+    DCHECK(pEncoding->IsName());
+    ByteString cmap = pEncoding->GetString();
+    m_pCMap = pFontGlobals->GetPredefinedCMap(cmap);
+  }
+
+  RetainPtr<const CPDF_Dictionary> pFontDesc =
+      pCIDFontDict->GetDictFor("FontDescriptor");
+  if (pFontDesc)
+    LoadFontDescriptor(pFontDesc.Get());
+
+  m_Charset = m_pCMap->GetCharset();
+  if (m_Charset == CIDSET_UNKNOWN) {
+    RetainPtr<const CPDF_Dictionary> pCIDInfo =
+        pCIDFontDict->GetDictFor("CIDSystemInfo");
+    if (pCIDInfo) {
+      m_Charset = CPDF_CMapParser::CharsetFromOrdering(
+          pCIDInfo->GetByteStringFor("Ordering").AsStringView());
+    }
+  }
+  if (m_Charset != CIDSET_UNKNOWN) {
+    m_pCID2UnicodeMap = pFontGlobals->GetCID2UnicodeMap(m_Charset);
+  }
+  if (m_Font.GetFaceRec()) {
+    if (m_FontType == CIDFontType::kType1)
+      FXFT_Select_Charmap(m_Font.GetFaceRec(), FT_ENCODING_UNICODE);
+    else
+      FT_UseCIDCharmap(m_Font.GetFaceRec(), m_pCMap->GetCoding());
+  }
+  m_DefaultWidth = pCIDFontDict->GetIntegerFor("DW", 1000);
+  RetainPtr<const CPDF_Array> pWidthArray = pCIDFontDict->GetArrayFor("W");
+  if (pWidthArray)
+    LoadMetricsArray(std::move(pWidthArray), &m_WidthList, 1);
+
+  if (!IsEmbedded())
+    LoadSubstFont();
+
+  RetainPtr<const CPDF_Object> pmap =
+      pCIDFontDict->GetDirectObjectFor("CIDToGIDMap");
+  if (pmap) {
+    RetainPtr<const CPDF_Stream> pMapStream(pmap->AsStream());
+    if (pMapStream) {
+      m_pStreamAcc = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(pMapStream));
+      m_pStreamAcc->LoadAllDataFiltered();
+    } else if (m_pFontFile && pmap->IsName() &&
+               pmap->GetString() == "Identity") {
+      m_bCIDIsGID = true;
+    }
+  }
+
+  CheckFontMetrics();
+  if (IsVertWriting()) {
+    RetainPtr<const CPDF_Array> pWidth2Array = pCIDFontDict->GetArrayFor("W2");
+    if (pWidth2Array)
+      LoadMetricsArray(std::move(pWidth2Array), &m_VertMetrics, 3);
+
+    RetainPtr<const CPDF_Array> pDefaultArray =
+        pCIDFontDict->GetArrayFor("DW2");
+    if (pDefaultArray) {
+      m_DefaultVY = pDefaultArray->GetIntegerAt(0);
+      m_DefaultW1 = pDefaultArray->GetIntegerAt(1);
+    }
+  }
+
+  // TODO(thestig): Better identify font types and identify more font types.
+  if (m_FontType == CIDFontType::kTrueType && IsEmbedded())
+    m_Font.SetFontType(CFX_Font::FontType::kCIDTrueType);
+
+  return true;
+}
+
+FX_RECT CPDF_CIDFont::GetCharBBox(uint32_t charcode) {
+  if (charcode < 256 && m_CharBBox[charcode].right != -1)
+    return m_CharBBox[charcode];
+
+  FX_RECT rect;
+  bool bVert = false;
+  int glyph_index = GlyphFromCharCode(charcode, &bVert);
+  FXFT_FaceRec* face = m_Font.GetFaceRec();
+  if (face) {
+    if (FXFT_Is_Face_Tricky(face)) {
+      int err =
+          FT_Load_Glyph(face, glyph_index, FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
+      if (!err) {
+        FT_Glyph glyph;
+        err = FT_Get_Glyph(face->glyph, &glyph);
+        if (!err) {
+          FT_BBox cbox;
+          FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &cbox);
+          const int xMin = FTPosToCBoxInt(cbox.xMin);
+          const int xMax = FTPosToCBoxInt(cbox.xMax);
+          const int yMin = FTPosToCBoxInt(cbox.yMin);
+          const int yMax = FTPosToCBoxInt(cbox.yMax);
+          const int pixel_size_x = face->size->metrics.x_ppem;
+          const int pixel_size_y = face->size->metrics.y_ppem;
+          if (pixel_size_x == 0 || pixel_size_y == 0) {
+            rect = FX_RECT(xMin, yMax, xMax, yMin);
+          } else {
+            rect =
+                FX_RECT(xMin * 1000 / pixel_size_x, yMax * 1000 / pixel_size_y,
+                        xMax * 1000 / pixel_size_x, yMin * 1000 / pixel_size_y);
+          }
+          rect.top = std::min(rect.top,
+                              static_cast<int>(FXFT_Get_Face_Ascender(face)));
+          rect.bottom = std::max(
+              rect.bottom, static_cast<int>(FXFT_Get_Face_Descender(face)));
+          FT_Done_Glyph(glyph);
+        }
+      }
+    } else {
+      int err = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE);
+      if (err == 0) {
+        rect = FX_RECT(TT2PDF(FXFT_Get_Glyph_HoriBearingX(face), face),
+                       TT2PDF(FXFT_Get_Glyph_HoriBearingY(face), face),
+                       TT2PDF(FXFT_Get_Glyph_HoriBearingX(face) +
+                                  FXFT_Get_Glyph_Width(face),
+                              face),
+                       TT2PDF(FXFT_Get_Glyph_HoriBearingY(face) -
+                                  FXFT_Get_Glyph_Height(face),
+                              face));
+        if (rect.top <= kMaxRectTop)
+          rect.top += rect.top / 64;
+        else
+          rect.top = std::numeric_limits<int>::max();
+      }
+    }
+  }
+  if (!m_pFontFile && m_Charset == CIDSET_JAPAN1) {
+    uint16_t cid = CIDFromCharCode(charcode);
+    const uint8_t* pTransform = GetCIDTransform(cid);
+    if (pTransform && !bVert) {
+      CFX_Matrix matrix(CIDTransformToFloat(pTransform[0]),
+                        CIDTransformToFloat(pTransform[1]),
+                        CIDTransformToFloat(pTransform[2]),
+                        CIDTransformToFloat(pTransform[3]),
+                        CIDTransformToFloat(pTransform[4]) * 1000,
+                        CIDTransformToFloat(pTransform[5]) * 1000);
+      rect = matrix.TransformRect(CFX_FloatRect(rect)).GetOuterRect();
+    }
+  }
+  if (charcode < 256)
+    m_CharBBox[charcode] = rect;
+
+  return rect;
+}
+
+int CPDF_CIDFont::GetCharWidthF(uint32_t charcode) {
+  if (charcode < 0x80 && m_bAnsiWidthsFixed)
+    return (charcode >= 32 && charcode < 127) ? 500 : 0;
+
+  uint16_t cid = CIDFromCharCode(charcode);
+  size_t size = m_WidthList.size();
+  const int* pList = m_WidthList.data();
+  for (size_t i = 0; i < size; i += 3) {
+    const int* pEntry = pList + i;
+    if (IsMetricForCID(pEntry, cid))
+      return pEntry[2];
+  }
+  return m_DefaultWidth;
+}
+
+int16_t CPDF_CIDFont::GetVertWidth(uint16_t cid) const {
+  size_t vertsize = m_VertMetrics.size() / 5;
+  if (vertsize) {
+    const int* pTable = m_VertMetrics.data();
+    for (size_t i = 0; i < vertsize; i++) {
+      const int* pEntry = pTable + (i * 5);
+      if (IsMetricForCID(pEntry, cid))
+        return static_cast<int16_t>(pEntry[2]);
+    }
+  }
+  return m_DefaultW1;
+}
+
+CFX_Point16 CPDF_CIDFont::GetVertOrigin(uint16_t cid) const {
+  size_t vertsize = m_VertMetrics.size() / 5;
+  if (vertsize) {
+    const int* pTable = m_VertMetrics.data();
+    for (size_t i = 0; i < vertsize; i++) {
+      const int* pEntry = pTable + (i * 5);
+      if (IsMetricForCID(pEntry, cid)) {
+        return {static_cast<int16_t>(pEntry[3]),
+                static_cast<int16_t>(pEntry[4])};
+      }
+    }
+  }
+  int width = m_DefaultWidth;
+  size_t size = m_WidthList.size();
+  const int* pList = m_WidthList.data();
+  for (size_t i = 0; i < size; i += 3) {
+    const int* pEntry = pList + i;
+    if (IsMetricForCID(pEntry, cid)) {
+      width = pEntry[2];
+      break;
+    }
+  }
+  return {static_cast<int16_t>(width / 2), m_DefaultVY};
+}
+
+int CPDF_CIDFont::GetGlyphIndex(uint32_t unicode, bool* pVertGlyph) {
+  if (pVertGlyph)
+    *pVertGlyph = false;
+
+  FXFT_FaceRec* face = m_Font.GetFaceRec();
+  int index = FT_Get_Char_Index(face, unicode);
+  if (unicode == pdfium::unicode::kBoxDrawingsLightVerical)
+    return index;
+
+  if (!index || !IsVertWriting())
+    return index;
+
+  if (m_pTTGSUBTable)
+    return GetVerticalGlyph(index, pVertGlyph);
+
+  static constexpr uint32_t kGsubTag =
+      CFX_FontMapper::MakeTag('G', 'S', 'U', 'B');
+  unsigned long length = 0;
+  int error = FT_Load_Sfnt_Table(face, kGsubTag, 0, nullptr, &length);
+  if (error || !length) {
+    return index;
+  }
+
+  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);
+}
+
+int CPDF_CIDFont::GetVerticalGlyph(int index, bool* pVertGlyph) {
+  uint32_t vindex = m_pTTGSUBTable->GetVerticalGlyph(index);
+  if (!vindex)
+    return index;
+
+  index = vindex;
+  if (pVertGlyph)
+    *pVertGlyph = true;
+  return index;
+}
+
+int CPDF_CIDFont::GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) {
+  if (pVertGlyph)
+    *pVertGlyph = false;
+
+  if (!m_pFontFile && (!m_pStreamAcc || m_pCID2UnicodeMap)) {
+    uint16_t cid = CIDFromCharCode(charcode);
+    wchar_t unicode = 0;
+    if (m_bCIDIsGID) {
+#if BUILDFLAG(IS_APPLE)
+      if (FontStyleIsSymbolic(m_Flags))
+        return cid;
+
+      WideString uni_str = UnicodeFromCharCode(charcode);
+      if (uni_str.IsEmpty())
+        return cid;
+
+      unicode = uni_str[0];
+#else
+      return cid;
+#endif
+    } else {
+      if (cid && m_pCID2UnicodeMap && m_pCID2UnicodeMap->IsLoaded())
+        unicode = m_pCID2UnicodeMap->UnicodeFromCID(cid);
+      if (unicode == 0)
+        unicode = GetUnicodeFromCharCode(charcode);
+      if (unicode == 0) {
+        WideString unicode_str = UnicodeFromCharCode(charcode);
+        if (!unicode_str.IsEmpty())
+          unicode = unicode_str[0];
+      }
+    }
+    FXFT_FaceRec* face = m_Font.GetFaceRec();
+    if (unicode == 0) {
+      if (!m_bAdobeCourierStd)
+        return charcode ? static_cast<int>(charcode) : -1;
+
+      charcode += 31;
+      bool bMSUnicode = UseTTCharmapMSUnicode(face);
+      bool bMacRoman = !bMSUnicode && UseTTCharmapMacRoman(face);
+      FontEncoding base_encoding = FontEncoding::kStandard;
+      if (bMSUnicode)
+        base_encoding = FontEncoding::kWinAnsi;
+      else if (bMacRoman)
+        base_encoding = FontEncoding::kMacRoman;
+      const char* name =
+          GetAdobeCharName(base_encoding, std::vector<ByteString>(), charcode);
+      if (!name)
+        return charcode ? static_cast<int>(charcode) : -1;
+
+      int index = 0;
+      uint16_t name_unicode = UnicodeFromAdobeName(name);
+      if (!name_unicode)
+        return charcode ? static_cast<int>(charcode) : -1;
+
+      if (base_encoding == FontEncoding::kStandard)
+        return FT_Get_Char_Index(face, name_unicode);
+
+      if (base_encoding == FontEncoding::kWinAnsi) {
+        index = FT_Get_Char_Index(face, name_unicode);
+      } else {
+        DCHECK_EQ(base_encoding, FontEncoding::kMacRoman);
+        uint32_t maccode = CharCodeFromUnicodeForFreetypeEncoding(
+            FT_ENCODING_APPLE_ROMAN, name_unicode);
+        index = maccode ? FT_Get_Char_Index(face, maccode)
+                        : FT_Get_Name_Index(face, name);
+      }
+      if (index == 0 || index == 0xffff)
+        return charcode ? static_cast<int>(charcode) : -1;
+      return index;
+    }
+    if (m_Charset == CIDSET_JAPAN1) {
+      if (unicode == '\\') {
+        unicode = '/';
+#if !BUILDFLAG(IS_APPLE)
+      } else if (unicode == 0xa5) {
+        unicode = 0x5c;
+#endif
+      }
+    }
+    if (!face)
+      return unicode;
+
+    int err = FXFT_Select_Charmap(face, FT_ENCODING_UNICODE);
+    if (err) {
+      int i;
+      for (i = 0; i < face->num_charmaps; i++) {
+        uint32_t ret = CharCodeFromUnicodeForFreetypeEncoding(
+            FXFT_Get_Charmap_Encoding(face->charmaps[i]),
+            static_cast<wchar_t>(charcode));
+        if (ret == 0)
+          continue;
+        FT_Set_Charmap(face, face->charmaps[i]);
+        unicode = static_cast<wchar_t>(ret);
+        break;
+      }
+      if (i == face->num_charmaps && i) {
+        FT_Set_Charmap(face, face->charmaps[0]);
+        unicode = static_cast<wchar_t>(charcode);
+      }
+    }
+    if (face->charmap) {
+      int index = GetGlyphIndex(unicode, pVertGlyph);
+      return index != 0 ? index : -1;
+    }
+    return unicode;
+  }
+
+  if (!m_Font.GetFaceRec())
+    return -1;
+
+  uint16_t cid = CIDFromCharCode(charcode);
+  if (!m_pStreamAcc) {
+    if (m_FontType == CIDFontType::kType1)
+      return cid;
+    if (m_pFontFile && m_pCMap->IsDirectCharcodeToCIDTableIsEmpty())
+      return cid;
+
+    FT_CharMap charmap = m_Font.GetFaceRec()->charmap;
+    if (!charmap || m_pCMap->GetCoding() == CIDCoding::kUNKNOWN)
+      return cid;
+
+    if (FXFT_Get_Charmap_Encoding(charmap) == FT_ENCODING_UNICODE) {
+      WideString unicode_str = UnicodeFromCharCode(charcode);
+      if (unicode_str.IsEmpty())
+        return -1;
+
+      charcode = unicode_str[0];
+    }
+    return GetGlyphIndex(charcode, pVertGlyph);
+  }
+  uint32_t byte_pos = cid * 2;
+  if (byte_pos + 2 > m_pStreamAcc->GetSize())
+    return -1;
+
+  pdfium::span<const uint8_t> span = m_pStreamAcc->GetSpan().subspan(byte_pos);
+  return span[0] * 256 + span[1];
+}
+
+uint32_t CPDF_CIDFont::GetNextChar(ByteStringView pString,
+                                   size_t* pOffset) const {
+  return m_pCMap->GetNextChar(pString, pOffset);
+}
+
+int CPDF_CIDFont::GetCharSize(uint32_t charcode) const {
+  return m_pCMap->GetCharSize(charcode);
+}
+
+size_t CPDF_CIDFont::CountChar(ByteStringView pString) const {
+  return m_pCMap->CountChar(pString);
+}
+
+int CPDF_CIDFont::AppendChar(char* str, uint32_t charcode) const {
+  return m_pCMap->AppendChar(str, charcode);
+}
+
+bool CPDF_CIDFont::IsUnicodeCompatible() const {
+  if (m_pCID2UnicodeMap && m_pCID2UnicodeMap->IsLoaded() && m_pCMap->IsLoaded())
+    return true;
+  return m_pCMap->GetCoding() != CIDCoding::kUNKNOWN;
+}
+
+void CPDF_CIDFont::LoadSubstFont() {
+  FX_SAFE_INT32 safeStemV(m_StemV);
+  safeStemV *= 5;
+  m_Font.LoadSubst(m_BaseFontName, m_FontType == CIDFontType::kTrueType,
+                   m_Flags, safeStemV.ValueOrDefault(FXFONT_FW_NORMAL),
+                   m_ItalicAngle, kCharsetCodePages[m_Charset],
+                   IsVertWriting());
+}
+
 // static
 float CPDF_CIDFont::CIDTransformToFloat(uint8_t ch) {
   return (ch < 128 ? ch : ch - 255) * (1.0f / 127);
 }
 
 void CPDF_CIDFont::LoadGB2312() {
-  m_BaseFontName = m_pFontDict->GetStringFor("BaseFont");
+  m_BaseFontName = m_pFontDict->GetByteStringFor("BaseFont");
   m_Charset = CIDSET_GB1;
 
-  CPDF_CMapManager* manager = CPDF_FontGlobals::GetInstance()->GetCMapManager();
-  m_pCMap = manager->GetPredefinedCMap("GBK-EUC-H");
-  m_pCID2UnicodeMap = manager->GetCID2UnicodeMap(m_Charset);
-  const CPDF_Dictionary* pFontDesc = m_pFontDict->GetDictFor("FontDescriptor");
+  auto* pFontGlobals = CPDF_FontGlobals::GetInstance();
+  m_pCMap = pFontGlobals->GetPredefinedCMap("GBK-EUC-H");
+  m_pCID2UnicodeMap = pFontGlobals->GetCID2UnicodeMap(m_Charset);
+  RetainPtr<const CPDF_Dictionary> pFontDesc =
+      m_pFontDict->GetDictFor("FontDescriptor");
   if (pFontDesc)
-    LoadFontDescriptor(pFontDesc);
+    LoadFontDescriptor(pFontDesc.Get());
 
   if (!IsEmbedded())
     LoadSubstFont();
@@ -833,14 +883,16 @@
   m_bAnsiWidthsFixed = true;
 }
 
-const uint8_t* CPDF_CIDFont::GetCIDTransform(uint16_t CID) const {
+const uint8_t* CPDF_CIDFont::GetCIDTransform(uint16_t cid) const {
   if (m_Charset != CIDSET_JAPAN1 || m_pFontFile)
     return nullptr;
 
-  const auto* pEnd = g_Japan1_VertCIDs + FX_ArraySize(g_Japan1_VertCIDs);
+  const auto* pBegin = std::begin(kJapan1VerticalCIDs);
+  const auto* pEnd = std::end(kJapan1VerticalCIDs);
   const auto* pTransform = std::lower_bound(
-      g_Japan1_VertCIDs, pEnd, CID,
+      pBegin, pEnd, cid,
       [](const CIDTransform& entry, uint16_t cid) { return entry.cid < cid; });
-  return (pTransform < pEnd && CID == pTransform->cid) ? &pTransform->a
+
+  return (pTransform < pEnd && cid == pTransform->cid) ? &pTransform->a
                                                        : nullptr;
 }
diff --git a/core/fpdfapi/font/cpdf_cidfont.h b/core/fpdfapi/font/cpdf_cidfont.h
index ce00e1d..fe270a2 100644
--- a/core/fpdfapi/font/cpdf_cidfont.h
+++ b/core/fpdfapi/font/cpdf_cidfont.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,14 @@
 #ifndef CORE_FPDFAPI_FONT_CPDF_CIDFONT_H_
 #define CORE_FPDFAPI_FONT_CPDF_CIDFONT_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
 #include "core/fpdfapi/font/cpdf_font.h"
+#include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
@@ -27,16 +29,13 @@
 };
 
 class CFX_CTTGSUBTable;
-class CPDF_Array;
 class CPDF_CID2UnicodeMap;
 class CPDF_CMap;
 class CPDF_StreamAcc;
 
 class CPDF_CIDFont final : public CPDF_Font {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
+  CONSTRUCT_VIA_MAKE_RETAIN;
   ~CPDF_CIDFont() override;
 
   static float CIDTransformToFloat(uint8_t ch);
@@ -46,7 +45,7 @@
   const CPDF_CIDFont* AsCIDFont() const override;
   CPDF_CIDFont* AsCIDFont() override;
   int GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) override;
-  uint32_t GetCharWidthF(uint32_t charcode) override;
+  int GetCharWidthF(uint32_t charcode) override;
   FX_RECT GetCharBBox(uint32_t charcode) override;
   uint32_t GetNextChar(ByteStringView pString, size_t* pOffset) const override;
   size_t CountChar(ByteStringView pString) const override;
@@ -58,20 +57,22 @@
   uint32_t CharCodeFromUnicode(wchar_t Unicode) const override;
 
   uint16_t CIDFromCharCode(uint32_t charcode) const;
-  const uint8_t* GetCIDTransform(uint16_t CID) const;
-  short GetVertWidth(uint16_t CID) const;
-  void GetVertOrigin(uint16_t CID, short& vx, short& vy) const;
+  const uint8_t* GetCIDTransform(uint16_t cid) const;
+  int16_t GetVertWidth(uint16_t cid) const;
+  CFX_Point16 GetVertOrigin(uint16_t cid) const;
   int GetCharSize(uint32_t charcode) const;
 
  private:
-  CPDF_CIDFont(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict);
+  enum class CIDFontType : bool {
+    kType1,    // CIDFontType0
+    kTrueType  // CIDFontType2
+  };
+
+  CPDF_CIDFont(CPDF_Document* pDocument, RetainPtr<CPDF_Dictionary> pFontDict);
 
   void LoadGB2312();
   int GetGlyphIndex(uint32_t unicodeb, bool* pVertGlyph);
   int GetVerticalGlyph(int index, bool* pVertGlyph);
-  void LoadMetricsArray(const CPDF_Array* pArray,
-                        std::vector<uint32_t>* result,
-                        int nElements);
   void LoadSubstFont();
   wchar_t GetUnicodeFromCharCode(uint32_t charcode) const;
 
@@ -79,16 +80,16 @@
   UnownedPtr<const CPDF_CID2UnicodeMap> m_pCID2UnicodeMap;
   RetainPtr<CPDF_StreamAcc> m_pStreamAcc;
   std::unique_ptr<CFX_CTTGSUBTable> m_pTTGSUBTable;
-  bool m_bType1 = false;
+  CIDFontType m_FontType = CIDFontType::kTrueType;
   bool m_bCIDIsGID = false;
   bool m_bAnsiWidthsFixed = false;
   bool m_bAdobeCourierStd = false;
   CIDSet m_Charset = CIDSET_UNKNOWN;
-  uint16_t m_DefaultWidth = 1000;
-  short m_DefaultVY = 880;
-  short m_DefaultW1 = -1000;
-  std::vector<uint32_t> m_WidthList;
-  std::vector<uint32_t> m_VertMetrics;
+  int16_t m_DefaultWidth = 1000;
+  int16_t m_DefaultVY = 880;
+  int16_t m_DefaultW1 = -1000;
+  std::vector<int> m_WidthList;
+  std::vector<int> m_VertMetrics;
   FX_RECT m_CharBBox[256];
 };
 
diff --git a/core/fpdfapi/font/cpdf_cidfont_unittest.cpp b/core/fpdfapi/font/cpdf_cidfont_unittest.cpp
index 225ea3a..4bc7426 100644
--- a/core/fpdfapi/font/cpdf_cidfont_unittest.cpp
+++ b/core/fpdfapi/font/cpdf_cidfont_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,24 +6,18 @@
 
 #include <utility>
 
-#include "core/fpdfapi/page/cpdf_docpagedata.h"
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fpdfapi/page/test_with_page_module.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
-#include "core/fpdfapi/render/cpdf_docrenderdata.h"
+#include "core/fpdfapi/parser/cpdf_test_document.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-class CPDF_CIDFontTest : public testing::Test {
- protected:
-  void SetUp() override { CPDF_PageModule::Create(); }
-  void TearDown() override { CPDF_PageModule::Destroy(); }
-};
+using CPDF_CIDFontTest = TestWithPageModule;
 
 TEST_F(CPDF_CIDFontTest, BUG_920636) {
-  CPDF_Document doc(pdfium::MakeUnique<CPDF_DocRenderData>(),
-                    pdfium::MakeUnique<CPDF_DocPageData>());
+  CPDF_TestDocument doc;
   auto font_dict = pdfium::MakeRetain<CPDF_Dictionary>();
   font_dict->SetNewFor<CPDF_Name>("Encoding", "Identity−H");
 
@@ -32,12 +26,12 @@
     {
       auto descendant_font = pdfium::MakeRetain<CPDF_Dictionary>();
       descendant_font->SetNewFor<CPDF_Name>("BaseFont", "CourierStd");
-      descendant_fonts->Add(std::move(descendant_font));
+      descendant_fonts->Append(std::move(descendant_font));
     }
     font_dict->SetFor("DescendantFonts", std::move(descendant_fonts));
   }
 
-  auto font = pdfium::MakeRetain<CPDF_CIDFont>(&doc, font_dict.Get());
+  auto font = pdfium::MakeRetain<CPDF_CIDFont>(&doc, std::move(font_dict));
   ASSERT_TRUE(font->Load());
 
   // It would be nice if we can test more values here. However, the glyph
diff --git a/core/fpdfapi/font/cpdf_cmap.cpp b/core/fpdfapi/font/cpdf_cmap.cpp
index 844bc5f..d5fa61b 100644
--- a/core/fpdfapi/font/cpdf_cmap.cpp
+++ b/core/fpdfapi/font/cpdf_cmap.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,6 @@
 
 #include "core/fpdfapi/font/cpdf_cmap.h"
 
-#include <memory>
 #include <utility>
 #include <vector>
 
@@ -14,6 +13,7 @@
 #include "core/fpdfapi/font/cpdf_cmapparser.h"
 #include "core/fpdfapi/font/cpdf_fontglobals.h"
 #include "core/fpdfapi/parser/cpdf_simple_parser.h"
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -34,151 +34,176 @@
 constexpr PredefinedCMap kPredefinedCMaps[] = {
     {"GB-EUC",
      CIDSET_GB1,
-     CIDCODING_GB,
+     CIDCoding::kGB,
      CPDF_CMap::MixedTwoBytes,
      1,
      {{0xa1, 0xfe}}},
     {"GBpc-EUC",
      CIDSET_GB1,
-     CIDCODING_GB,
+     CIDCoding::kGB,
      CPDF_CMap::MixedTwoBytes,
      1,
      {{0xa1, 0xfc}}},
     {"GBK-EUC",
      CIDSET_GB1,
-     CIDCODING_GB,
+     CIDCoding::kGB,
      CPDF_CMap::MixedTwoBytes,
      1,
      {{0x81, 0xfe}}},
     {"GBKp-EUC",
      CIDSET_GB1,
-     CIDCODING_GB,
+     CIDCoding::kGB,
      CPDF_CMap::MixedTwoBytes,
      1,
      {{0x81, 0xfe}}},
     {"GBK2K-EUC",
      CIDSET_GB1,
-     CIDCODING_GB,
+     CIDCoding::kGB,
      CPDF_CMap::MixedTwoBytes,
      1,
      {{0x81, 0xfe}}},
     {"GBK2K",
      CIDSET_GB1,
-     CIDCODING_GB,
+     CIDCoding::kGB,
      CPDF_CMap::MixedTwoBytes,
      1,
      {{0x81, 0xfe}}},
-    {"UniGB-UCS2", CIDSET_GB1, CIDCODING_UCS2, CPDF_CMap::TwoBytes, 0, {}},
-    {"UniGB-UTF16", CIDSET_GB1, CIDCODING_UTF16, CPDF_CMap::TwoBytes, 0, {}},
+    {"UniGB-UCS2", CIDSET_GB1, CIDCoding::kUCS2, CPDF_CMap::TwoBytes, 0, {}},
+    {"UniGB-UTF16", CIDSET_GB1, CIDCoding::kUTF16, CPDF_CMap::TwoBytes, 0, {}},
     {"B5pc",
      CIDSET_CNS1,
-     CIDCODING_BIG5,
+     CIDCoding::kBIG5,
      CPDF_CMap::MixedTwoBytes,
      1,
      {{0xa1, 0xfc}}},
     {"HKscs-B5",
      CIDSET_CNS1,
-     CIDCODING_BIG5,
+     CIDCoding::kBIG5,
      CPDF_CMap::MixedTwoBytes,
      1,
      {{0x88, 0xfe}}},
     {"ETen-B5",
      CIDSET_CNS1,
-     CIDCODING_BIG5,
+     CIDCoding::kBIG5,
      CPDF_CMap::MixedTwoBytes,
      1,
      {{0xa1, 0xfe}}},
     {"ETenms-B5",
      CIDSET_CNS1,
-     CIDCODING_BIG5,
+     CIDCoding::kBIG5,
      CPDF_CMap::MixedTwoBytes,
      1,
      {{0xa1, 0xfe}}},
-    {"UniCNS-UCS2", CIDSET_CNS1, CIDCODING_UCS2, CPDF_CMap::TwoBytes, 0, {}},
-    {"UniCNS-UTF16", CIDSET_CNS1, CIDCODING_UTF16, CPDF_CMap::TwoBytes, 0, {}},
+    {"UniCNS-UCS2", CIDSET_CNS1, CIDCoding::kUCS2, CPDF_CMap::TwoBytes, 0, {}},
+    {"UniCNS-UTF16",
+     CIDSET_CNS1,
+     CIDCoding::kUTF16,
+     CPDF_CMap::TwoBytes,
+     0,
+     {}},
     {"83pv-RKSJ",
      CIDSET_JAPAN1,
-     CIDCODING_JIS,
+     CIDCoding::kJIS,
      CPDF_CMap::MixedTwoBytes,
      2,
      {{0x81, 0x9f}, {0xe0, 0xfc}}},
     {"90ms-RKSJ",
      CIDSET_JAPAN1,
-     CIDCODING_JIS,
+     CIDCoding::kJIS,
      CPDF_CMap::MixedTwoBytes,
      2,
      {{0x81, 0x9f}, {0xe0, 0xfc}}},
     {"90msp-RKSJ",
      CIDSET_JAPAN1,
-     CIDCODING_JIS,
+     CIDCoding::kJIS,
      CPDF_CMap::MixedTwoBytes,
      2,
      {{0x81, 0x9f}, {0xe0, 0xfc}}},
     {"90pv-RKSJ",
      CIDSET_JAPAN1,
-     CIDCODING_JIS,
+     CIDCoding::kJIS,
      CPDF_CMap::MixedTwoBytes,
      2,
      {{0x81, 0x9f}, {0xe0, 0xfc}}},
     {"Add-RKSJ",
      CIDSET_JAPAN1,
-     CIDCODING_JIS,
+     CIDCoding::kJIS,
      CPDF_CMap::MixedTwoBytes,
      2,
      {{0x81, 0x9f}, {0xe0, 0xfc}}},
     {"EUC",
      CIDSET_JAPAN1,
-     CIDCODING_JIS,
+     CIDCoding::kJIS,
      CPDF_CMap::MixedTwoBytes,
      2,
      {{0x8e, 0x8e}, {0xa1, 0xfe}}},
-    {"H", CIDSET_JAPAN1, CIDCODING_JIS, CPDF_CMap::TwoBytes, 1, {{0x21, 0x7e}}},
-    {"V", CIDSET_JAPAN1, CIDCODING_JIS, CPDF_CMap::TwoBytes, 1, {{0x21, 0x7e}}},
+    {"H",
+     CIDSET_JAPAN1,
+     CIDCoding::kJIS,
+     CPDF_CMap::TwoBytes,
+     1,
+     {{0x21, 0x7e}}},
+    {"V",
+     CIDSET_JAPAN1,
+     CIDCoding::kJIS,
+     CPDF_CMap::TwoBytes,
+     1,
+     {{0x21, 0x7e}}},
     {"Ext-RKSJ",
      CIDSET_JAPAN1,
-     CIDCODING_JIS,
+     CIDCoding::kJIS,
      CPDF_CMap::MixedTwoBytes,
      2,
      {{0x81, 0x9f}, {0xe0, 0xfc}}},
-    {"UniJIS-UCS2", CIDSET_JAPAN1, CIDCODING_UCS2, CPDF_CMap::TwoBytes, 0, {}},
+    {"UniJIS-UCS2",
+     CIDSET_JAPAN1,
+     CIDCoding::kUCS2,
+     CPDF_CMap::TwoBytes,
+     0,
+     {}},
     {"UniJIS-UCS2-HW",
      CIDSET_JAPAN1,
-     CIDCODING_UCS2,
+     CIDCoding::kUCS2,
      CPDF_CMap::TwoBytes,
      0,
      {}},
     {"UniJIS-UTF16",
      CIDSET_JAPAN1,
-     CIDCODING_UTF16,
+     CIDCoding::kUTF16,
      CPDF_CMap::TwoBytes,
      0,
      {}},
     {"KSC-EUC",
      CIDSET_KOREA1,
-     CIDCODING_KOREA,
+     CIDCoding::kKOREA,
      CPDF_CMap::MixedTwoBytes,
      1,
      {{0xa1, 0xfe}}},
     {"KSCms-UHC",
      CIDSET_KOREA1,
-     CIDCODING_KOREA,
+     CIDCoding::kKOREA,
      CPDF_CMap::MixedTwoBytes,
      1,
      {{0x81, 0xfe}}},
     {"KSCms-UHC-HW",
      CIDSET_KOREA1,
-     CIDCODING_KOREA,
+     CIDCoding::kKOREA,
      CPDF_CMap::MixedTwoBytes,
      1,
      {{0x81, 0xfe}}},
     {"KSCpc-EUC",
      CIDSET_KOREA1,
-     CIDCODING_KOREA,
+     CIDCoding::kKOREA,
      CPDF_CMap::MixedTwoBytes,
      1,
      {{0xa1, 0xfd}}},
-    {"UniKS-UCS2", CIDSET_KOREA1, CIDCODING_UCS2, CPDF_CMap::TwoBytes, 0, {}},
-    {"UniKS-UTF16", CIDSET_KOREA1, CIDCODING_UTF16, CPDF_CMap::TwoBytes, 0, {}},
+    {"UniKS-UCS2", CIDSET_KOREA1, CIDCoding::kUCS2, CPDF_CMap::TwoBytes, 0, {}},
+    {"UniKS-UTF16",
+     CIDSET_KOREA1,
+     CIDCoding::kUTF16,
+     CPDF_CMap::TwoBytes,
+     0,
+     {}},
 };
 
 const PredefinedCMap* GetPredefinedCMap(ByteStringView cmapid) {
@@ -255,12 +280,21 @@
   return 1;
 }
 
+const fxcmap::CMap* FindEmbeddedCMap(pdfium::span<const fxcmap::CMap> pCMaps,
+                                     ByteStringView bsName) {
+  for (size_t i = 0; i < pCMaps.size(); i++) {
+    if (bsName == pCMaps[i].m_Name)
+      return &pCMaps[i];
+  }
+  return nullptr;
+}
+
 }  // namespace
 
 CPDF_CMap::CPDF_CMap(ByteStringView bsPredefinedName)
     : m_bVertical(bsPredefinedName.Back() == 'V') {
   if (bsPredefinedName == "Identity-H" || bsPredefinedName == "Identity-V") {
-    m_Coding = CIDCODING_CID;
+    m_Coding = CIDCoding::kCID;
     m_bLoaded = true;
     return;
   }
@@ -284,10 +318,10 @@
 }
 
 CPDF_CMap::CPDF_CMap(pdfium::span<const uint8_t> spEmbeddedData)
-    : m_DirectCharcodeToCIDTable(65536) {
+    : m_DirectCharcodeToCIDTable(kDirectMapTableSize) {
   CPDF_CMapParser parser(this);
   CPDF_SimpleParser syntax(spEmbeddedData);
-  while (1) {
+  while (true) {
     ByteStringView word = syntax.GetWord();
     if (word.IsEmpty())
       break;
@@ -299,17 +333,18 @@
 CPDF_CMap::~CPDF_CMap() = default;
 
 uint16_t CPDF_CMap::CIDFromCharCode(uint32_t charcode) const {
-  if (m_Coding == CIDCODING_CID)
+  if (m_Coding == CIDCoding::kCID)
     return static_cast<uint16_t>(charcode);
 
   if (m_pEmbedMap)
-    return ::CIDFromCharCode(m_pEmbedMap.Get(), charcode);
+    return fxcmap::CIDFromCharCode(m_pEmbedMap, charcode);
 
   if (m_DirectCharcodeToCIDTable.empty())
     return static_cast<uint16_t>(charcode);
 
-  if (charcode < 0x10000)
-    return m_DirectCharcodeToCIDTable[charcode];
+  auto table_span = m_DirectCharcodeToCIDTable.span();
+  if (charcode < table_span.size())
+    return table_span[charcode];
 
   auto it = std::lower_bound(m_AdditionalCharcodeToCIDMappings.begin(),
                              m_AdditionalCharcodeToCIDMappings.end(), charcode,
@@ -346,7 +381,7 @@
       uint8_t codes[4];
       int char_size = 1;
       codes[0] = offset < pBytes.size() ? pBytes[offset++] : 0;
-      while (1) {
+      while (true) {
         int ret = CheckFourByteCodeRange(codes, char_size,
                                          m_MixedFourByteLeadingRanges);
         if (ret == 0)
@@ -361,7 +396,6 @@
           return 0;
         codes[char_size++] = pBytes[offset++];
       }
-      break;
     }
   }
   return 0;
@@ -466,7 +500,7 @@
 }
 
 void CPDF_CMap::SetAdditionalMappings(std::vector<CIDRange> mappings) {
-  ASSERT(m_AdditionalCharcodeToCIDMappings.empty());
+  DCHECK(m_AdditionalCharcodeToCIDMappings.empty());
   if (m_CodingScheme != MixedFourBytes || mappings.empty())
     return;
 
diff --git a/core/fpdfapi/font/cpdf_cmap.h b/core/fpdfapi/font/cpdf_cmap.h
index e2caf8d..416010a 100644
--- a/core/fpdfapi/font/cpdf_cmap.h
+++ b/core/fpdfapi/font/cpdf_cmap.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,27 +7,35 @@
 #ifndef CORE_FPDFAPI_FONT_CPDF_CMAP_H_
 #define CORE_FPDFAPI_FONT_CPDF_CMAP_H_
 
+#include <stdint.h>
+
 #include <vector>
 
 #include "core/fpdfapi/font/cpdf_cidfont.h"
+#include "core/fxcrt/fixed_zeroed_data_vector.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/span.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/containers/span.h"
 
-struct FXCMAP_CMap;
+namespace fxcmap {
+struct CMap;
+}
 
-enum CIDCoding : uint8_t {
-  CIDCODING_UNKNOWN = 0,
-  CIDCODING_GB,
-  CIDCODING_BIG5,
-  CIDCODING_JIS,
-  CIDCODING_KOREA,
-  CIDCODING_UCS2,
-  CIDCODING_CID,
-  CIDCODING_UTF16,
+enum class CIDCoding : uint8_t {
+  kUNKNOWN = 0,
+  kGB,
+  kBIG5,
+  kJIS,
+  kKOREA,
+  kUCS2,
+  kCID,
+  kUTF16,
 };
 
 class CPDF_CMap final : public Retainable {
  public:
+  static constexpr size_t kDirectMapTableSize = 65536;
+
   enum CodingScheme : uint8_t {
     OneByte,
     TwoBytes,
@@ -47,8 +55,7 @@
     uint16_t m_StartCID;
   };
 
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   bool IsLoaded() const { return m_bLoaded; }
   bool IsVertWriting() const { return m_bVertical; }
@@ -65,13 +72,13 @@
   void SetAdditionalMappings(std::vector<CIDRange> mappings);
   void SetMixedFourByteLeadingRanges(std::vector<CodeRange> ranges);
 
-  int GetCoding() const { return m_Coding; }
-  const FXCMAP_CMap* GetEmbedMap() const { return m_pEmbedMap.Get(); }
+  CIDCoding GetCoding() const { return m_Coding; }
+  const fxcmap::CMap* GetEmbedMap() const { return m_pEmbedMap; }
   CIDSet GetCharset() const { return m_Charset; }
   void SetCharset(CIDSet set) { m_Charset = set; }
 
   void SetDirectCharcodeToCIDTable(size_t idx, uint16_t val) {
-    m_DirectCharcodeToCIDTable[idx] = val;
+    m_DirectCharcodeToCIDTable.writable_span()[idx] = val;
   }
   bool IsDirectCharcodeToCIDTableIsEmpty() const {
     return m_DirectCharcodeToCIDTable.empty();
@@ -86,12 +93,12 @@
   bool m_bVertical = false;
   CIDSet m_Charset = CIDSET_UNKNOWN;
   CodingScheme m_CodingScheme = TwoBytes;
-  int m_Coding = CIDCODING_UNKNOWN;
+  CIDCoding m_Coding = CIDCoding::kUNKNOWN;
   std::vector<bool> m_MixedTwoByteLeadingBytes;
   std::vector<CodeRange> m_MixedFourByteLeadingRanges;
-  std::vector<uint16_t> m_DirectCharcodeToCIDTable;
+  FixedZeroedDataVector<uint16_t> m_DirectCharcodeToCIDTable;
   std::vector<CIDRange> m_AdditionalCharcodeToCIDMappings;
-  UnownedPtr<const FXCMAP_CMap> m_pEmbedMap;
+  UnownedPtr<const fxcmap::CMap> m_pEmbedMap;
 };
 
 #endif  // CORE_FPDFAPI_FONT_CPDF_CMAP_H_
diff --git a/core/fpdfapi/font/cpdf_cmapmanager.cpp b/core/fpdfapi/font/cpdf_cmapmanager.cpp
deleted file mode 100644
index 726b648..0000000
--- a/core/fpdfapi/font/cpdf_cmapmanager.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fpdfapi/font/cpdf_cmapmanager.h"
-
-#include <utility>
-
-#include "core/fpdfapi/font/cpdf_cid2unicodemap.h"
-#include "core/fpdfapi/font/cpdf_cmap.h"
-#include "third_party/base/ptr_util.h"
-
-namespace {
-
-RetainPtr<const CPDF_CMap> LoadPredefinedCMap(ByteStringView name) {
-  if (!name.IsEmpty() && name[0] == '/')
-    name = name.Last(name.GetLength() - 1);
-  return pdfium::MakeRetain<CPDF_CMap>(name);
-}
-
-}  // namespace
-
-CPDF_CMapManager::CPDF_CMapManager() = default;
-
-CPDF_CMapManager::~CPDF_CMapManager() = default;
-
-RetainPtr<const CPDF_CMap> CPDF_CMapManager::GetPredefinedCMap(
-    const ByteString& name) {
-  auto it = m_CMaps.find(name);
-  if (it != m_CMaps.end())
-    return it->second;
-
-  RetainPtr<const CPDF_CMap> pCMap = LoadPredefinedCMap(name.AsStringView());
-  if (!name.IsEmpty())
-    m_CMaps[name] = pCMap;
-
-  return pCMap;
-}
-
-CPDF_CID2UnicodeMap* CPDF_CMapManager::GetCID2UnicodeMap(CIDSet charset) {
-  if (!m_CID2UnicodeMaps[charset]) {
-    m_CID2UnicodeMaps[charset] =
-        pdfium::MakeUnique<CPDF_CID2UnicodeMap>(charset);
-  }
-  return m_CID2UnicodeMaps[charset].get();
-}
diff --git a/core/fpdfapi/font/cpdf_cmapmanager.h b/core/fpdfapi/font/cpdf_cmapmanager.h
deleted file mode 100644
index bc8d4e9..0000000
--- a/core/fpdfapi/font/cpdf_cmapmanager.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFAPI_FONT_CPDF_CMAPMANAGER_H_
-#define CORE_FPDFAPI_FONT_CPDF_CMAPMANAGER_H_
-
-#include <map>
-#include <memory>
-
-#include "core/fpdfapi/font/cpdf_cidfont.h"
-#include "core/fxcrt/bytestring.h"
-#include "core/fxcrt/retain_ptr.h"
-
-class CPDF_CMapManager {
- public:
-  CPDF_CMapManager();
-  ~CPDF_CMapManager();
-
-  RetainPtr<const CPDF_CMap> GetPredefinedCMap(const ByteString& name);
-  CPDF_CID2UnicodeMap* GetCID2UnicodeMap(CIDSet charset);
-
- private:
-  std::map<ByteString, RetainPtr<const CPDF_CMap>> m_CMaps;
-  std::unique_ptr<CPDF_CID2UnicodeMap> m_CID2UnicodeMaps[CIDSET_NUM_SETS];
-};
-
-#endif  // CORE_FPDFAPI_FONT_CPDF_CMAPMANAGER_H_
diff --git a/core/fpdfapi/font/cpdf_cmapparser.cpp b/core/fpdfapi/font/cpdf_cmapparser.cpp
index d1fe51c..87a5907 100644
--- a/core/fpdfapi/font/cpdf_cmapparser.cpp
+++ b/core/fpdfapi/font/cpdf_cmapparser.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/font/cpdf_cmapparser.h"
 
-#include <vector>
+#include <ctype.h>
+
+#include <iterator>
 
 #include "core/fpdfapi/cmaps/fpdf_cmaps.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
@@ -14,8 +16,8 @@
 #include "core/fpdfapi/parser/cpdf_simple_parser.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "core/fxge/fx_freetype.h"
-#include "third_party/base/logging.h"
+#include "core/fxge/freetype/fx_freetype.h"
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -35,7 +37,7 @@
 }
 
 void CPDF_CMapParser::ParseWord(ByteStringView word) {
-  ASSERT(!word.IsEmpty());
+  DCHECK(!word.IsEmpty());
 
   if (word == "begincidchar") {
     m_Status = kProcessingCidChar;
@@ -78,7 +80,7 @@
 }
 
 void CPDF_CMapParser::HandleCid(ByteStringView word) {
-  ASSERT(m_Status == kProcessingCidChar || m_Status == kProcessingCidRange);
+  DCHECK(m_Status == kProcessingCidChar || m_Status == kProcessingCidRange);
   bool bChar = m_Status == kProcessingCidChar;
 
   m_CodePoints[m_CodeSeq] = GetCode(word);
@@ -97,7 +99,7 @@
     EndCode = m_CodePoints[1];
     StartCID = static_cast<uint16_t>(m_CodePoints[2]);
   }
-  if (EndCode < 0x10000) {
+  if (EndCode < CPDF_CMap::kDirectMapTableSize) {
     for (uint32_t code = StartCode; code <= EndCode; code++) {
       m_pCMap->SetDirectCharcodeToCIDTable(
           code, static_cast<uint16_t>(StartCID + code - StartCode));
@@ -114,7 +116,7 @@
       return;
 
     if (m_CodeSeq % 2) {
-      Optional<CPDF_CMap::CodeRange> range =
+      absl::optional<CPDF_CMap::CodeRange> range =
           GetCodeRange(m_LastWord.AsStringView(), word);
       if (range.has_value())
         m_PendingRanges.push_back(range.value());
@@ -146,7 +148,7 @@
 
   FX_SAFE_UINT32 num = 0;
   if (word[0] == '<') {
-    for (size_t i = 1; i < word.GetLength() && std::isxdigit(word[i]); ++i) {
+    for (size_t i = 1; i < word.GetLength() && isxdigit(word[i]); ++i) {
       num = num * 16 + FXSYS_HexCharToInt(word[i]);
       if (!num.IsValid())
         return 0;
@@ -154,7 +156,7 @@
     return num.ValueOrDie();
   }
 
-  for (size_t i = 0; i < word.GetLength() && std::isdigit(word[i]); ++i) {
+  for (size_t i = 0; i < word.GetLength() && isdigit(word[i]); ++i) {
     num = num * 10 + FXSYS_DecimalCharToInt(static_cast<wchar_t>(word[i]));
     if (!num.IsValid())
       return 0;
@@ -163,11 +165,11 @@
 }
 
 // static
-Optional<CPDF_CMap::CodeRange> CPDF_CMapParser::GetCodeRange(
+absl::optional<CPDF_CMap::CodeRange> CPDF_CMapParser::GetCodeRange(
     ByteStringView first,
     ByteStringView second) {
   if (first.IsEmpty() || first[0] != '<')
-    return pdfium::nullopt;
+    return absl::nullopt;
 
   size_t i;
   for (i = 1; i < first.GetLength(); ++i) {
@@ -176,7 +178,7 @@
   }
   size_t char_size = (i - 1) / 2;
   if (char_size > 4)
-    return pdfium::nullopt;
+    return absl::nullopt;
 
   CPDF_CMap::CodeRange range;
   range.m_CharSize = char_size;
@@ -203,10 +205,10 @@
 CIDSet CPDF_CMapParser::CharsetFromOrdering(ByteStringView ordering) {
   static const char* const kCharsetNames[CIDSET_NUM_SETS] = {
       nullptr, "GB1", "CNS1", "Japan1", "Korea1", "UCS"};
-  static_assert(FX_ArraySize(kCharsetNames) == CIDSET_NUM_SETS,
+  static_assert(std::size(kCharsetNames) == CIDSET_NUM_SETS,
                 "Too many CID sets");
 
-  for (size_t charset = 1; charset < FX_ArraySize(kCharsetNames); ++charset) {
+  for (size_t charset = 1; charset < std::size(kCharsetNames); ++charset) {
     if (ordering == kCharsetNames[charset])
       return static_cast<CIDSet>(charset);
   }
diff --git a/core/fpdfapi/font/cpdf_cmapparser.h b/core/fpdfapi/font/cpdf_cmapparser.h
index b2454af..9219814 100644
--- a/core/fpdfapi/font/cpdf_cmapparser.h
+++ b/core/fpdfapi/font/cpdf_cmapparser.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,7 +13,7 @@
 #include "core/fpdfapi/font/cpdf_cidfont.h"
 #include "core/fpdfapi/font/cpdf_cmap.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CPDF_CMapParser {
  public:
@@ -43,8 +43,9 @@
   void HandleCodeSpaceRange(ByteStringView word);
 
   static uint32_t GetCode(ByteStringView word);
-  static Optional<CPDF_CMap::CodeRange> GetCodeRange(ByteStringView first,
-                                                     ByteStringView second);
+  static absl::optional<CPDF_CMap::CodeRange> GetCodeRange(
+      ByteStringView first,
+      ByteStringView second);
 
   Status m_Status = kStart;
   int m_CodeSeq = 0;
@@ -53,7 +54,7 @@
   std::vector<CPDF_CMap::CodeRange> m_PendingRanges;
   std::vector<CPDF_CMap::CIDRange> m_AdditionalCharcodeToCIDMappings;
   ByteString m_LastWord;
-  uint32_t m_CodePoints[4];
+  uint32_t m_CodePoints[4] = {};
 };
 
 #endif  // CORE_FPDFAPI_FONT_CPDF_CMAPPARSER_H_
diff --git a/core/fpdfapi/font/cpdf_cmapparser_unittest.cpp b/core/fpdfapi/font/cpdf_cmapparser_unittest.cpp
index da654f3..a9cdc71 100644
--- a/core/fpdfapi/font/cpdf_cmapparser_unittest.cpp
+++ b/core/fpdfapi/font/cpdf_cmapparser_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -37,7 +37,7 @@
 }
 
 TEST(cpdf_cmapparser, GetCodeRange) {
-  Optional<CPDF_CMap::CodeRange> range;
+  absl::optional<CPDF_CMap::CodeRange> range;
 
   // Must start with a <
   range = CPDF_CMapParser::GetCodeRange("", "");
diff --git a/core/fpdfapi/font/cpdf_font.cpp b/core/fpdfapi/font/cpdf_font.cpp
index 326bc6e..36413f8 100644
--- a/core/fpdfapi/font/cpdf_font.cpp
+++ b/core/fpdfapi/font/cpdf_font.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,12 +6,13 @@
 
 #include "core/fpdfapi/font/cpdf_font.h"
 
-#include <limits>
+#include <algorithm>
 #include <memory>
 #include <utility>
 #include <vector>
 
 #include "build/build_config.h"
+#include "constants/font_encodings.h"
 #include "core/fpdfapi/font/cpdf_cidfont.h"
 #include "core/fpdfapi/font/cpdf_fontencoding.h"
 #include "core/fpdfapi/font/cpdf_fontglobals.h"
@@ -25,13 +26,15 @@
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
-#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/stl_util.h"
 #include "core/fxge/cfx_fontmapper.h"
+#include "core/fxge/cfx_substfont.h"
+#include "core/fxge/freetype/fx_freetype.h"
 #include "core/fxge/fx_font.h"
-#include "core/fxge/fx_freetype.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
 
@@ -45,19 +48,15 @@
 
 }  // namespace
 
-CPDF_Font::CPDF_Font(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict)
+CPDF_Font::CPDF_Font(CPDF_Document* pDocument,
+                     RetainPtr<CPDF_Dictionary> pFontDict)
     : m_pDocument(pDocument),
-      m_pFontDict(pFontDict),
-      m_BaseFontName(pFontDict->GetStringFor("BaseFont")) {}
+      m_pFontDict(std::move(pFontDict)),
+      m_BaseFontName(m_pFontDict->GetByteStringFor("BaseFont")) {}
 
 CPDF_Font::~CPDF_Font() {
-  if (m_pFontFile) {
-    auto* pPageData = m_pDocument->GetPageData();
-    if (pPageData) {
-      pPageData->MaybePurgeFontFileStreamAcc(
-          m_pFontFile->GetStream()->AsStream());
-    }
-  }
+  if (m_pFontFile)
+    m_pDocument->MaybePurgeFontFileStreamAcc(std::move(m_pFontFile));
 }
 
 bool CPDF_Font::IsType1Font() const {
@@ -108,15 +107,11 @@
   return nullptr;
 }
 
-bool CPDF_Font::IsUnicodeCompatible() const {
-  return false;
-}
-
 size_t CPDF_Font::CountChar(ByteStringView pString) const {
   return pString.GetLength();
 }
 
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
 int CPDF_Font::GlyphFromCharCodeExt(uint32_t charcode) {
   return GlyphFromCharCode(charcode, nullptr);
 }
@@ -194,7 +189,7 @@
   }
   if (m_Descent > 10)
     m_Descent = -m_Descent;
-  const CPDF_Array* pBBox = pFontDesc->GetArrayFor("FontBBox");
+  RetainPtr<const CPDF_Array> pBBox = pFontDesc->GetArrayFor("FontBBox");
   if (pBBox) {
     m_FontBBox.left = pBBox->GetIntegerAt(0);
     m_FontBBox.bottom = pBBox->GetIntegerAt(1);
@@ -202,7 +197,7 @@
     m_FontBBox.top = pBBox->GetIntegerAt(3);
   }
 
-  const CPDF_Stream* pFontFile = pFontDesc->GetStreamFor("FontFile");
+  RetainPtr<const CPDF_Stream> pFontFile = pFontDesc->GetStreamFor("FontFile");
   if (!pFontFile)
     pFontFile = pFontDesc->GetStreamFor("FontFile2");
   if (!pFontFile)
@@ -210,15 +205,13 @@
   if (!pFontFile)
     return;
 
-  auto* pData = m_pDocument->GetPageData();
-  m_pFontFile = pData->GetFontFileStreamAcc(pFontFile);
+  const uint64_t key = pFontFile->KeyForCache();
+  m_pFontFile = m_pDocument->GetFontFileStreamAcc(std::move(pFontFile));
   if (!m_pFontFile)
     return;
 
-  if (!m_Font.LoadEmbedded(m_pFontFile->GetSpan(), IsVertWriting())) {
-    pData->MaybePurgeFontFileStreamAcc(m_pFontFile->GetStream()->AsStream());
-    m_pFontFile = nullptr;
-  }
+  if (!m_Font.LoadEmbedded(m_pFontFile->GetSpan(), IsVertWriting(), key))
+    m_pDocument->MaybePurgeFontFileStreamAcc(std::move(m_pFontFile));
 }
 
 void CPDF_Font::CheckFontMetrics() {
@@ -243,18 +236,10 @@
           m_FontBBox = rect;
           bFirst = false;
         } else {
-          if (m_FontBBox.top < rect.top) {
-            m_FontBBox.top = rect.top;
-          }
-          if (m_FontBBox.right < rect.right) {
-            m_FontBBox.right = rect.right;
-          }
-          if (m_FontBBox.left > rect.left) {
-            m_FontBBox.left = rect.left;
-          }
-          if (m_FontBBox.bottom > rect.bottom) {
-            m_FontBBox.bottom = rect.bottom;
-          }
+          m_FontBBox.left = std::min(m_FontBBox.left, rect.left);
+          m_FontBBox.top = std::max(m_FontBBox.top, rect.top);
+          m_FontBBox.right = std::max(m_FontBBox.right, rect.right);
+          m_FontBBox.bottom = std::min(m_FontBBox.bottom, rect.bottom);
         }
       }
     }
@@ -269,16 +254,16 @@
 
 void CPDF_Font::LoadUnicodeMap() const {
   m_bToUnicodeLoaded = true;
-  const CPDF_Stream* pStream = m_pFontDict->GetStreamFor("ToUnicode");
+  RetainPtr<const CPDF_Stream> pStream = m_pFontDict->GetStreamFor("ToUnicode");
   if (!pStream)
     return;
 
-  m_pToUnicodeMap = pdfium::MakeUnique<CPDF_ToUnicodeMap>(pStream);
+  m_pToUnicodeMap = std::make_unique<CPDF_ToUnicodeMap>(std::move(pStream));
 }
 
-uint32_t CPDF_Font::GetStringWidth(ByteStringView pString) {
+int CPDF_Font::GetStringWidth(ByteStringView pString) {
   size_t offset = 0;
-  uint32_t width = 0;
+  int width = 0;
   while (offset < pString.GetLength())
     width += GetCharWidthF(GetNextChar(pString, &offset));
   return width;
@@ -288,7 +273,7 @@
 RetainPtr<CPDF_Font> CPDF_Font::GetStockFont(CPDF_Document* pDoc,
                                              ByteStringView name) {
   ByteString fontname(name);
-  Optional<CFX_FontMapper::StandardFont> font_id =
+  absl::optional<CFX_FontMapper::StandardFont> font_id =
       CFX_FontMapper::GetStandardFontName(&fontname);
   if (!font_id.has_value())
     return nullptr;
@@ -302,37 +287,39 @@
   pDict->SetNewFor<CPDF_Name>("Type", "Font");
   pDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
   pDict->SetNewFor<CPDF_Name>("BaseFont", fontname);
-  pDict->SetNewFor<CPDF_Name>("Encoding", "WinAnsiEncoding");
-  pFont = CPDF_Font::Create(nullptr, pDict.Get(), nullptr);
+  pDict->SetNewFor<CPDF_Name>("Encoding",
+                              pdfium::font_encodings::kWinAnsiEncoding);
+  pFont = CPDF_Font::Create(nullptr, std::move(pDict), nullptr);
   pFontGlobals->Set(pDoc, font_id.value(), pFont);
   return pFont;
 }
 
 // static
 RetainPtr<CPDF_Font> CPDF_Font::Create(CPDF_Document* pDoc,
-                                       CPDF_Dictionary* pFontDict,
+                                       RetainPtr<CPDF_Dictionary> pFontDict,
                                        FormFactoryIface* pFactory) {
-  ByteString type = pFontDict->GetStringFor("Subtype");
+  ByteString type = pFontDict->GetByteStringFor("Subtype");
   RetainPtr<CPDF_Font> pFont;
   if (type == "TrueType") {
-    ByteString tag = pFontDict->GetStringFor("BaseFont").First(4);
-    for (size_t i = 0; i < FX_ArraySize(kChineseFontNames); ++i) {
+    ByteString tag = pFontDict->GetByteStringFor("BaseFont").First(4);
+    for (size_t i = 0; i < std::size(kChineseFontNames); ++i) {
       if (tag == ByteString(kChineseFontNames[i], kChineseFontNameSize)) {
-        const CPDF_Dictionary* pFontDesc =
+        RetainPtr<const CPDF_Dictionary> pFontDesc =
             pFontDict->GetDictFor("FontDescriptor");
         if (!pFontDesc || !pFontDesc->KeyExist("FontFile2"))
-          pFont = pdfium::MakeRetain<CPDF_CIDFont>(pDoc, pFontDict);
+          pFont = pdfium::MakeRetain<CPDF_CIDFont>(pDoc, std::move(pFontDict));
         break;
       }
     }
     if (!pFont)
-      pFont = pdfium::MakeRetain<CPDF_TrueTypeFont>(pDoc, pFontDict);
+      pFont = pdfium::MakeRetain<CPDF_TrueTypeFont>(pDoc, std::move(pFontDict));
   } else if (type == "Type3") {
-    pFont = pdfium::MakeRetain<CPDF_Type3Font>(pDoc, pFontDict, pFactory);
+    pFont = pdfium::MakeRetain<CPDF_Type3Font>(pDoc, std::move(pFontDict),
+                                               pFactory);
   } else if (type == "Type0") {
-    pFont = pdfium::MakeRetain<CPDF_CIDFont>(pDoc, pFontDict);
+    pFont = pdfium::MakeRetain<CPDF_CIDFont>(pDoc, std::move(pFontDict));
   } else {
-    pFont = pdfium::MakeRetain<CPDF_Type1Font>(pDoc, pFontDict);
+    pFont = pdfium::MakeRetain<CPDF_Type1Font>(pDoc, std::move(pFontDict));
   }
   if (!pFont->Load())
     return nullptr;
@@ -356,9 +343,16 @@
   return AsType1Font()->IsBase14Font();
 }
 
+absl::optional<FX_Charset> CPDF_Font::GetSubstFontCharset() const {
+  CFX_SubstFont* pFont = m_Font.GetSubstFont();
+  if (!pFont)
+    return absl::nullopt;
+  return pFont->m_Charset;
+}
+
 // static
 const char* CPDF_Font::GetAdobeCharName(
-    int iBaseEncoding,
+    FontEncoding base_encoding,
     const std::vector<ByteString>& charnames,
     uint32_t charcode) {
   if (charcode >= 256)
@@ -368,29 +362,30 @@
     return charnames[charcode].c_str();
 
   const char* name = nullptr;
-  if (iBaseEncoding)
-    name = PDF_CharNameFromPredefinedCharSet(iBaseEncoding, charcode);
+  if (base_encoding != FontEncoding::kBuiltin)
+    name = CharNameFromPredefinedCharSet(base_encoding, charcode);
   if (!name)
     return nullptr;
 
-  ASSERT(name[0]);
+  DCHECK(name[0]);
   return name;
 }
 
 uint32_t CPDF_Font::FallbackFontFromCharcode(uint32_t charcode) {
   if (m_FontFallbacks.empty()) {
-    m_FontFallbacks.push_back(pdfium::MakeUnique<CFX_Font>());
-    pdfium::base::CheckedNumeric<int> safeWeight = m_StemV;
+    m_FontFallbacks.push_back(std::make_unique<CFX_Font>());
+    FX_SAFE_INT32 safeWeight = m_StemV;
     safeWeight *= 5;
     m_FontFallbacks[0]->LoadSubst("Arial", IsTrueTypeFont(), m_Flags,
                                   safeWeight.ValueOrDefault(FXFONT_FW_NORMAL),
-                                  m_ItalicAngle, 0, IsVertWriting());
+                                  m_ItalicAngle, FX_CodePage::kDefANSI,
+                                  IsVertWriting());
   }
   return 0;
 }
 
 int CPDF_Font::FallbackGlyphFromCharcode(int fallbackFont, uint32_t charcode) {
-  if (!pdfium::IndexInBounds(m_FontFallbacks, fallbackFont))
+  if (!fxcrt::IndexInBounds(m_FontFallbacks, fallbackFont))
     return -1;
 
   WideString str = UnicodeFromCharCode(charcode);
@@ -410,26 +405,23 @@
 }
 
 // static
-int CPDF_Font::TT2PDF(int m, FXFT_FaceRec* face) {
+int CPDF_Font::TT2PDF(FT_Pos m, FXFT_FaceRec* face) {
   int upm = FXFT_Get_Face_UnitsPerEM(face);
   if (upm == 0)
-    return m;
+    return pdfium::base::saturated_cast<int>(m);
 
-  return static_cast<int>(
-      pdfium::clamp((m * 1000.0 + upm / 2) / upm,
-                    static_cast<double>(std::numeric_limits<int>::min()),
-                    static_cast<double>(std::numeric_limits<int>::max())));
+  const double dm = (m * 1000.0 + upm / 2) / upm;
+  return pdfium::base::saturated_cast<int>(dm);
 }
 
 // static
-bool CPDF_Font::FT_UseTTCharmap(FXFT_FaceRec* face,
-                                int platform_id,
-                                int encoding_id) {
-  auto** pCharMap = FXFT_Get_Face_Charmaps(face);
-  for (int i = 0; i < FXFT_Get_Face_CharmapCount(face); i++) {
-    if (FXFT_Get_Charmap_PlatformID(pCharMap[i]) == platform_id &&
-        FXFT_Get_Charmap_EncodingID(pCharMap[i]) == encoding_id) {
-      FT_Set_Charmap(face, pCharMap[i]);
+bool CPDF_Font::UseTTCharmap(FXFT_FaceRec* face,
+                             int platform_id,
+                             int encoding_id) {
+  for (int i = 0; i < face->num_charmaps; i++) {
+    if (FXFT_Get_Charmap_PlatformID(face->charmaps[i]) == platform_id &&
+        FXFT_Get_Charmap_EncodingID(face->charmaps[i]) == encoding_id) {
+      FT_Set_Charmap(face, face->charmaps[i]);
       return true;
     }
   }
@@ -437,7 +429,7 @@
 }
 
 int CPDF_Font::GetFontWeight() const {
-  pdfium::base::CheckedNumeric<int> safeStemV(m_StemV);
+  FX_SAFE_INT32 safeStemV(m_StemV);
   if (m_StemV < 140)
     safeStemV *= 5;
   else
diff --git a/core/fpdfapi/font/cpdf_font.h b/core/fpdfapi/font/cpdf_font.h
index db776fd..e192a5c 100644
--- a/core/fpdfapi/font/cpdf_font.h
+++ b/core/fpdfapi/font/cpdf_font.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,8 @@
 #ifndef CORE_FPDFAPI_FONT_CPDF_FONT_H_
 #define CORE_FPDFAPI_FONT_CPDF_FONT_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <utility>
 #include <vector>
@@ -15,59 +17,57 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_font.h"
+#include "core/fxge/freetype/fx_freetype.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CFX_DIBitmap;
-class CFX_SubstFont;
 class CPDF_CIDFont;
 class CPDF_Document;
-class CPDF_Object;
 class CPDF_TrueTypeFont;
 class CPDF_Type1Font;
 class CPDF_Type3Char;
 class CPDF_Type3Font;
 class CPDF_ToUnicodeMap;
+enum class FontEncoding;
 
 class CPDF_Font : public Retainable, public Observable {
  public:
   // Callback mechanism for Type3 fonts to get pixels from forms.
   class FormIface {
    public:
-    virtual ~FormIface() {}
+    virtual ~FormIface() = default;
 
     virtual void ParseContentForType3Char(CPDF_Type3Char* pChar) = 0;
     virtual bool HasPageObjects() const = 0;
     virtual CFX_FloatRect CalcBoundingBox() const = 0;
-    virtual Optional<std::pair<RetainPtr<CFX_DIBitmap>, CFX_Matrix>>
+    virtual absl::optional<std::pair<RetainPtr<CFX_DIBitmap>, CFX_Matrix>>
     GetBitmapAndMatrixFromSoleImageOfForm() const = 0;
   };
 
   // Callback mechanism for Type3 fonts to get new forms from upper layers.
   class FormFactoryIface {
    public:
-    virtual ~FormFactoryIface() {}
+    virtual ~FormFactoryIface() = default;
 
     virtual std::unique_ptr<FormIface> CreateForm(
         CPDF_Document* pDocument,
-        CPDF_Dictionary* pPageResources,
-        CPDF_Stream* pFormStream) = 0;
+        RetainPtr<CPDF_Dictionary> pPageResources,
+        RetainPtr<CPDF_Stream> pFormStream) = 0;
   };
 
-  static const uint32_t kInvalidCharCode = static_cast<uint32_t>(-1);
+  static constexpr uint32_t kInvalidCharCode = static_cast<uint32_t>(-1);
 
   // |pFactory| only required for Type3 fonts.
   static RetainPtr<CPDF_Font> Create(CPDF_Document* pDoc,
-                                     CPDF_Dictionary* pFontDict,
+                                     RetainPtr<CPDF_Dictionary> pFontDict,
                                      FormFactoryIface* pFactory);
   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;
@@ -83,12 +83,12 @@
 
   virtual void WillBeDestroyed();
   virtual bool IsVertWriting() const;
-  virtual bool IsUnicodeCompatible() const;
+  virtual bool IsUnicodeCompatible() const = 0;
   virtual uint32_t GetNextChar(ByteStringView pString, size_t* pOffset) const;
   virtual size_t CountChar(ByteStringView pString) const;
   virtual int AppendChar(char* buf, uint32_t charcode) const;
   virtual int GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) = 0;
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
   virtual int GlyphFromCharCodeExt(uint32_t charcode);
 #endif
   virtual WideString UnicodeFromCharCode(uint32_t charcode) const;
@@ -96,9 +96,14 @@
   virtual bool HasFontWidths() const;
 
   ByteString GetBaseFontName() const { return m_BaseFontName; }
-  CFX_SubstFont* GetSubstFont() const { return m_Font.GetSubstFont(); }
+  absl::optional<FX_Charset> GetSubstFontCharset() const;
   bool IsEmbedded() const { return IsType3Font() || m_pFontFile != nullptr; }
-  CPDF_Dictionary* GetFontDict() const { return m_pFontDict.Get(); }
+  RetainPtr<CPDF_Dictionary> GetMutableFontDict() { return m_pFontDict; }
+  RetainPtr<const CPDF_Dictionary> GetFontDict() const { return m_pFontDict; }
+  uint32_t GetFontDictObjNum() const { return m_pFontDict->GetObjNum(); }
+  bool FontDictIs(const CPDF_Dictionary* pThat) const {
+    return m_pFontDict == pThat;
+  }
   void ClearFontDict() { m_pFontDict = nullptr; }
   bool IsStandardFont() const;
   bool HasFace() const { return !!m_Font.GetFaceRec(); }
@@ -107,32 +112,49 @@
   const FX_RECT& GetFontBBox() const { return m_FontBBox; }
   int GetTypeAscent() const { return m_Ascent; }
   int GetTypeDescent() const { return m_Descent; }
-  uint32_t GetStringWidth(ByteStringView pString);
+  int GetStringWidth(ByteStringView pString);
   uint32_t FallbackFontFromCharcode(uint32_t charcode);
   int FallbackGlyphFromCharcode(int fallbackFont, uint32_t charcode);
   int GetFontFlags() const { return m_Flags; }
+  int GetItalicAngle() const { return m_ItalicAngle; }
   int GetFontWeight() const;
 
-  virtual uint32_t GetCharWidthF(uint32_t charcode) = 0;
+  virtual int GetCharWidthF(uint32_t charcode) = 0;
   virtual FX_RECT GetCharBBox(uint32_t charcode) = 0;
 
   // Can return nullptr for stock Type1 fonts. Always returns non-null for other
   // font types.
-  CPDF_Document* GetDocument() const { return m_pDocument.Get(); }
+  CPDF_Document* GetDocument() const { return m_pDocument; }
 
   CFX_Font* GetFont() { return &m_Font; }
   const CFX_Font* GetFont() const { return &m_Font; }
 
   CFX_Font* GetFontFallback(int position);
 
- protected:
-  CPDF_Font(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict);
+  const ByteString& GetResourceName() const { return m_ResourceName; }
+  void SetResourceName(const ByteString& name) { m_ResourceName = name; }
 
-  static int TT2PDF(int m, FXFT_FaceRec* face);
-  static bool FT_UseTTCharmap(FXFT_FaceRec* face,
-                              int platform_id,
-                              int encoding_id);
-  static const char* GetAdobeCharName(int iBaseEncoding,
+ protected:
+  CPDF_Font(CPDF_Document* pDocument, RetainPtr<CPDF_Dictionary> pFontDict);
+  ~CPDF_Font() override;
+
+  static int TT2PDF(FT_Pos m, FXFT_FaceRec* face);
+
+  // Commonly used wrappers for UseTTCharmap().
+  static bool UseTTCharmapMSUnicode(FXFT_FaceRec* face) {
+    return UseTTCharmap(face, 3, 1);
+  }
+  static bool UseTTCharmapMSSymbol(FXFT_FaceRec* face) {
+    return UseTTCharmap(face, 3, 0);
+  }
+  static bool UseTTCharmapMacRoman(FXFT_FaceRec* face) {
+    return UseTTCharmap(face, 1, 0);
+  }
+  static bool UseTTCharmap(FXFT_FaceRec* face,
+                           int platform_id,
+                           int encoding_id);
+
+  static const char* GetAdobeCharName(FontEncoding base_encoding,
                                       const std::vector<ByteString>& charnames,
                                       uint32_t charcode);
 
@@ -143,6 +165,7 @@
   void CheckFontMetrics();
 
   UnownedPtr<CPDF_Document> const m_pDocument;
+  ByteString m_ResourceName;  // The resource name for this font.
   CFX_Font m_Font;
   std::vector<std::unique_ptr<CFX_Font>> m_FontFallbacks;
   RetainPtr<CPDF_StreamAcc> m_pFontFile;
diff --git a/core/fpdfapi/font/cpdf_fontencoding.cpp b/core/fpdfapi/font/cpdf_fontencoding.cpp
index a4e687e..ff2baed 100644
--- a/core/fpdfapi/font/cpdf_fontencoding.cpp
+++ b/core/fpdfapi/font/cpdf_fontencoding.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,20 +6,21 @@
 
 #include "core/fpdfapi/font/cpdf_fontencoding.h"
 
-#include <utility>
+#include <iterator>
 
+#include "constants/font_encodings.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "core/fxge/freetype/fx_freetype.h"
 #include "core/fxge/fx_font.h"
-#include "core/fxge/fx_freetype.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace {
 
-const uint16_t MSSymbolEncoding[CPDF_FontEncoding::kEncodingTableSize] = {
+const uint16_t kMSSymbolEncoding[CPDF_FontEncoding::kEncodingTableSize] = {
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
@@ -50,7 +51,7 @@
     0x2320, 0x0000, 0x2321, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000};
 
-const uint16_t StandardEncoding[CPDF_FontEncoding::kEncodingTableSize] = {
+const uint16_t kStandardEncoding[CPDF_FontEncoding::kEncodingTableSize] = {
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
@@ -81,7 +82,7 @@
     0x0000, 0x0000, 0x0131, 0x0000, 0x0000, 0x0142, 0x00f8, 0x0153, 0x00df,
     0x0000, 0x0000, 0x0000, 0x0000};
 
-const uint16_t MacRomanEncoding[CPDF_FontEncoding::kEncodingTableSize] = {
+const uint16_t kMacRomanEncoding[CPDF_FontEncoding::kEncodingTableSize] = {
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
@@ -112,7 +113,7 @@
     0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc, 0x00af, 0x02d8, 0x02d9, 0x02da,
     0x00b8, 0x02dd, 0x02db, 0x02c7};
 
-const uint16_t AdobeWinAnsiEncoding[CPDF_FontEncoding::kEncodingTableSize] = {
+const uint16_t kAdobeWinAnsiEncoding[CPDF_FontEncoding::kEncodingTableSize] = {
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
@@ -143,7 +144,7 @@
     0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb,
     0x00fc, 0x00fd, 0x00fe, 0x00ff};
 
-const uint16_t MacExpertEncoding[CPDF_FontEncoding::kEncodingTableSize] = {
+const uint16_t kMacExpertEncoding[CPDF_FontEncoding::kEncodingTableSize] = {
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
@@ -174,7 +175,7 @@
     0xf6f4, 0xf7af, 0xf6ea, 0x207f, 0xf6ef, 0xf6e2, 0xf6e8, 0xf6f7, 0xf6fc,
     0x0000, 0x0000, 0x0000, 0x0000};
 
-const uint16_t AdobeSymbolEncoding[CPDF_FontEncoding::kEncodingTableSize] = {
+const uint16_t kAdobeSymbolEncoding[CPDF_FontEncoding::kEncodingTableSize] = {
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
@@ -206,7 +207,7 @@
     0xF8FC, 0xF8FD, 0xF8FE, 0x0000,
 };
 
-const uint16_t ZapfEncoding[CPDF_FontEncoding::kEncodingTableSize] = {
+const uint16_t kZapfEncoding[CPDF_FontEncoding::kEncodingTableSize] = {
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
@@ -245,7 +246,7 @@
 constexpr size_t kPDFDocEncodingNamesTableSize =
     CPDF_FontEncoding::kEncodingTableSize - kPDFDocEncodingTableFirstChar;
 
-const char* const StandardEncodingNames[kEncodingNamesTableSize] = {
+const char* const kStandardEncodingNames[kEncodingNamesTableSize] = {
     "space",
     "exclam",
     "quotedbl",
@@ -472,7 +473,7 @@
     nullptr,
 };
 
-const char* const AdobeWinAnsiEncodingNames[kEncodingNamesTableSize] = {
+const char* const kAdobeWinAnsiEncodingNames[kEncodingNamesTableSize] = {
     "space",
     "exclam",
     "quotedbl",
@@ -699,7 +700,7 @@
     "ydieresis",
 };
 
-const char* const MacRomanEncodingNames[kEncodingNamesTableSize] = {
+const char* const kMacRomanEncodingNames[kEncodingNamesTableSize] = {
     "space",
     "exclam",
     "quotedbl",
@@ -926,7 +927,7 @@
     "caron",
 };
 
-const char* const MacExpertEncodingNames[kEncodingNamesTableSize] = {
+const char* const kMacExpertEncodingNames[kEncodingNamesTableSize] = {
     "space",
     "exclamsmall",
     "Hungarumlautsmall",
@@ -1153,7 +1154,7 @@
     nullptr,
 };
 
-const char* const PDFDocEncodingNames[kPDFDocEncodingNamesTableSize] = {
+const char* const kPDFDocEncodingNames[kPDFDocEncodingNamesTableSize] = {
     "breve",
     "caron",
     "circumflex",
@@ -1388,7 +1389,7 @@
     "ydieresis",
 };
 
-const char* const AdobeSymbolEncodingNames[kEncodingNamesTableSize] = {
+const char* const kAdobeSymbolEncodingNames[kEncodingNamesTableSize] = {
     "space",
     "exclam",
     "universal",
@@ -1615,7 +1616,7 @@
     nullptr,
 };
 
-const char* const ZapfEncodingNames[kEncodingNamesTableSize] = {
+const char* const kZapfEncodingNames[kEncodingNamesTableSize] = {
     "space", "a1",    "a2",    "a202",  "a3",    "a4",    "a5",    "a119",
     "a118",  "a117",  "a11",   "a12",   "a13",   "a14",   "a15",   "a16",
     "a105",  "a17",   "a18",   "a19",   "a20",   "a21",   "a22",   "a23",
@@ -1648,7 +1649,7 @@
 uint32_t PDF_FindCode(const uint16_t* pCodes, uint16_t unicode) {
   for (size_t i = 0; i < CPDF_FontEncoding::kEncodingTableSize; i++) {
     if (pCodes[i] == unicode)
-      return i;
+      return static_cast<uint32_t>(i);
   }
   return 0;
 }
@@ -1656,20 +1657,18 @@
 }  // namespace
 
 int CPDF_FontEncoding::CharCodeFromUnicode(wchar_t unicode) const {
-  for (size_t i = 0; i < FX_ArraySize(m_Unicodes); i++) {
+  for (size_t i = 0; i < std::size(m_Unicodes); i++) {
     if (m_Unicodes[i] == unicode)
-      return i;
+      return static_cast<int>(i);
   }
   return -1;
 }
 
-CPDF_FontEncoding::CPDF_FontEncoding(int PredefinedEncoding) {
-  const uint16_t* pSrc = PDF_UnicodesForPredefinedCharSet(PredefinedEncoding);
+CPDF_FontEncoding::CPDF_FontEncoding(FontEncoding predefined_encoding) {
+  const uint16_t* pSrc = UnicodesForPredefinedCharSet(predefined_encoding);
   if (pSrc) {
-    for (size_t i = 0; i < FX_ArraySize(m_Unicodes); i++)
+    for (size_t i = 0; i < std::size(m_Unicodes); i++)
       m_Unicodes[i] = pSrc[i];
-  } else {
-    memset(m_Unicodes, 0, sizeof(m_Unicodes));
   }
 }
 
@@ -1679,12 +1678,17 @@
 
 RetainPtr<CPDF_Object> CPDF_FontEncoding::Realize(
     WeakPtr<ByteStringPool> pPool) const {
-  int predefined = 0;
-  for (int cs = PDFFONT_ENCODING_WINANSI; cs < PDFFONT_ENCODING_ZAPFDINGBATS;
-       cs++) {
-    const uint16_t* pSrc = PDF_UnicodesForPredefinedCharSet(cs);
+  static constexpr FontEncoding kEncodings[] = {
+      FontEncoding::kWinAnsi,     FontEncoding::kMacRoman,
+      FontEncoding::kMacExpert,   FontEncoding::kStandard,
+      FontEncoding::kAdobeSymbol,
+  };
+
+  absl::optional<FontEncoding> predefined;
+  for (FontEncoding cs : kEncodings) {
+    const uint16_t* pSrc = UnicodesForPredefinedCharSet(cs);
     bool match = true;
-    for (size_t i = 0; i < FX_ArraySize(m_Unicodes); i++) {
+    for (size_t i = 0; i < std::size(m_Unicodes); i++) {
       if (m_Unicodes[i] != pSrc[i]) {
         match = false;
         break;
@@ -1695,79 +1699,87 @@
       break;
     }
   }
-  if (predefined) {
+  if (predefined.has_value()) {
     const char* pName;
-    if (predefined == PDFFONT_ENCODING_WINANSI)
-      pName = "WinAnsiEncoding";
-    else if (predefined == PDFFONT_ENCODING_MACROMAN)
-      pName = "MacRomanEncoding";
-    else if (predefined == PDFFONT_ENCODING_MACEXPERT)
-      pName = "MacExpertEncoding";
+    if (predefined.value() == FontEncoding::kWinAnsi)
+      pName = pdfium::font_encodings::kWinAnsiEncoding;
+    else if (predefined.value() == FontEncoding::kMacRoman)
+      pName = pdfium::font_encodings::kMacRomanEncoding;
+    else if (predefined.value() == FontEncoding::kMacExpert)
+      pName = pdfium::font_encodings::kMacExpertEncoding;
     else
       return nullptr;
 
     return pdfium::MakeRetain<CPDF_Name>(pPool, pName);
   }
   const uint16_t* pStandard =
-      PDF_UnicodesForPredefinedCharSet(PDFFONT_ENCODING_WINANSI);
+      UnicodesForPredefinedCharSet(FontEncoding::kWinAnsi);
   auto pDiff = pdfium::MakeRetain<CPDF_Array>();
-  for (size_t i = 0; i < FX_ArraySize(m_Unicodes); i++) {
+  for (size_t i = 0; i < std::size(m_Unicodes); i++) {
     if (pStandard[i] == m_Unicodes[i])
       continue;
 
-    pDiff->AddNew<CPDF_Number>(static_cast<int>(i));
-    pDiff->AddNew<CPDF_Name>(PDF_AdobeNameFromUnicode(m_Unicodes[i]));
+    pDiff->AppendNew<CPDF_Number>(static_cast<int>(i));
+    pDiff->AppendNew<CPDF_Name>(AdobeNameFromUnicode(m_Unicodes[i]));
   }
 
   auto pDict = pdfium::MakeRetain<CPDF_Dictionary>(pPool);
-  pDict->SetNewFor<CPDF_Name>("BaseEncoding", "WinAnsiEncoding");
+  pDict->SetNewFor<CPDF_Name>("BaseEncoding",
+                              pdfium::font_encodings::kWinAnsiEncoding);
   pDict->SetFor("Differences", pDiff);
   return pDict;
 }
 
-uint32_t FT_CharCodeFromUnicode(int encoding, wchar_t unicode) {
+uint32_t CharCodeFromUnicodeForFreetypeEncoding(int encoding, wchar_t unicode) {
   switch (encoding) {
     case FT_ENCODING_UNICODE:
       return unicode;
     case FT_ENCODING_ADOBE_STANDARD:
-      return PDF_FindCode(StandardEncoding, unicode);
+      return PDF_FindCode(kStandardEncoding, unicode);
     case FT_ENCODING_ADOBE_EXPERT:
-      return PDF_FindCode(MacExpertEncoding, unicode);
+      return PDF_FindCode(kMacExpertEncoding, unicode);
     case FT_ENCODING_ADOBE_LATIN_1:
-      return PDF_FindCode(AdobeWinAnsiEncoding, unicode);
+      return PDF_FindCode(kAdobeWinAnsiEncoding, unicode);
     case FT_ENCODING_APPLE_ROMAN:
-      return PDF_FindCode(MacRomanEncoding, unicode);
+      return PDF_FindCode(kMacRomanEncoding, unicode);
     case FT_ENCODING_ADOBE_CUSTOM:
-      return PDF_FindCode(PDFDocEncoding, unicode);
+      return PDF_FindCode(kPDFDocEncoding, unicode);
     case FT_ENCODING_MS_SYMBOL:
-      return PDF_FindCode(MSSymbolEncoding, unicode);
+      return PDF_FindCode(kMSSymbolEncoding, unicode);
   }
   return 0;
 }
-const uint16_t* PDF_UnicodesForPredefinedCharSet(int encoding) {
-  switch (encoding) {
-    case PDFFONT_ENCODING_WINANSI:
-      return AdobeWinAnsiEncoding;
-    case PDFFONT_ENCODING_MACROMAN:
-      return MacRomanEncoding;
-    case PDFFONT_ENCODING_MACEXPERT:
-      return MacExpertEncoding;
-    case PDFFONT_ENCODING_STANDARD:
-      return StandardEncoding;
-    case PDFFONT_ENCODING_ADOBE_SYMBOL:
-      return AdobeSymbolEncoding;
-    case PDFFONT_ENCODING_ZAPFDINGBATS:
-      return ZapfEncoding;
-    case PDFFONT_ENCODING_PDFDOC:
-      return PDFDocEncoding;
-    case PDFFONT_ENCODING_MS_SYMBOL:
-      return MSSymbolEncoding;
-  }
-  return nullptr;
+
+wchar_t UnicodeFromAppleRomanCharCode(uint8_t charcode) {
+  return kMacRomanEncoding[charcode];
 }
 
-const char* PDF_CharNameFromPredefinedCharSet(int encoding, uint8_t charcode) {
-  if (encoding == PDFFONT_ENCODING_PDFDOC) {
+const uint16_t* UnicodesForPredefinedCharSet(FontEncoding encoding) {
+  switch (encoding) {
+    case FontEncoding::kBuiltin:
+      return nullptr;
+    case FontEncoding::kWinAnsi:
+      return kAdobeWinAnsiEncoding;
+    case FontEncoding::kMacRoman:
+      return kMacRomanEncoding;
+    case FontEncoding::kMacExpert:
+      return kMacExpertEncoding;
+    case FontEncoding::kStandard:
+      return kStandardEncoding;
+    case FontEncoding::kAdobeSymbol:
+      return kAdobeSymbolEncoding;
+    case FontEncoding::kZapfDingbats:
+      return kZapfEncoding;
+    case FontEncoding::kPdfDoc:
+      return kPDFDocEncoding;
+    case FontEncoding::kMsSymbol:
+      return kMSSymbolEncoding;
+  }
+}
+
+const char* CharNameFromPredefinedCharSet(FontEncoding encoding,
+                                          uint8_t charcode) {
+  if (encoding == FontEncoding::kPdfDoc) {
     if (charcode < kPDFDocEncodingTableFirstChar)
       return nullptr;
 
@@ -1779,38 +1791,21 @@
     charcode -= kEncodingTableFirstChar;
   }
   switch (encoding) {
-    case PDFFONT_ENCODING_WINANSI:
-      return AdobeWinAnsiEncodingNames[charcode];
-    case PDFFONT_ENCODING_MACROMAN:
-      return MacRomanEncodingNames[charcode];
-    case PDFFONT_ENCODING_MACEXPERT:
-      return MacExpertEncodingNames[charcode];
-    case PDFFONT_ENCODING_STANDARD:
-      return StandardEncodingNames[charcode];
-    case PDFFONT_ENCODING_ADOBE_SYMBOL:
-      return AdobeSymbolEncodingNames[charcode];
-    case PDFFONT_ENCODING_ZAPFDINGBATS:
-      return ZapfEncodingNames[charcode];
-    case PDFFONT_ENCODING_PDFDOC:
-      return PDFDocEncodingNames[charcode];
+    case FontEncoding::kWinAnsi:
+      return kAdobeWinAnsiEncodingNames[charcode];
+    case FontEncoding::kMacRoman:
+      return kMacRomanEncodingNames[charcode];
+    case FontEncoding::kMacExpert:
+      return kMacExpertEncodingNames[charcode];
+    case FontEncoding::kStandard:
+      return kStandardEncodingNames[charcode];
+    case FontEncoding::kAdobeSymbol:
+      return kAdobeSymbolEncodingNames[charcode];
+    case FontEncoding::kZapfDingbats:
+      return kZapfEncodingNames[charcode];
+    case FontEncoding::kPdfDoc:
+      return kPDFDocEncodingNames[charcode];
+    default:
+      return nullptr;
   }
-  return nullptr;
-}
-
-wchar_t FT_UnicodeFromCharCode(int encoding, uint32_t charcode) {
-  switch (encoding) {
-    case FT_ENCODING_UNICODE:
-      return (uint16_t)charcode;
-    case FT_ENCODING_ADOBE_STANDARD:
-      return StandardEncoding[(uint8_t)charcode];
-    case FT_ENCODING_ADOBE_EXPERT:
-      return MacExpertEncoding[(uint8_t)charcode];
-    case FT_ENCODING_ADOBE_LATIN_1:
-      return AdobeWinAnsiEncoding[(uint8_t)charcode];
-    case FT_ENCODING_APPLE_ROMAN:
-      return MacRomanEncoding[(uint8_t)charcode];
-    case PDFFONT_ENCODING_PDFDOC:
-      return PDFDocEncoding[(uint8_t)charcode];
-  }
-  return 0;
 }
diff --git a/core/fpdfapi/font/cpdf_fontencoding.h b/core/fpdfapi/font/cpdf_fontencoding.h
index 1bfb0d5..272c8a6 100644
--- a/core/fpdfapi/font/cpdf_fontencoding.h
+++ b/core/fpdfapi/font/cpdf_fontencoding.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,27 +7,29 @@
 #ifndef CORE_FPDFAPI_FONT_CPDF_FONTENCODING_H_
 #define CORE_FPDFAPI_FONT_CPDF_FONTENCODING_H_
 
-#include <memory>
-
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/string_pool_template.h"
 #include "core/fxcrt/weak_ptr.h"
 
-#define PDFFONT_ENCODING_BUILTIN 0
-#define PDFFONT_ENCODING_WINANSI 1
-#define PDFFONT_ENCODING_MACROMAN 2
-#define PDFFONT_ENCODING_MACEXPERT 3
-#define PDFFONT_ENCODING_STANDARD 4
-#define PDFFONT_ENCODING_ADOBE_SYMBOL 5
-#define PDFFONT_ENCODING_ZAPFDINGBATS 6
-#define PDFFONT_ENCODING_PDFDOC 7
-#define PDFFONT_ENCODING_MS_SYMBOL 8
+enum class FontEncoding {
+  kBuiltin = 0,
+  kWinAnsi = 1,
+  kMacRoman = 2,
+  kMacExpert = 3,
+  kStandard = 4,
+  kAdobeSymbol = 5,
+  kZapfDingbats = 6,
+  kPdfDoc = 7,
+  kMsSymbol = 8,
+};
 
-uint32_t FT_CharCodeFromUnicode(int encoding, wchar_t unicode);
-wchar_t FT_UnicodeFromCharCode(int encoding, uint32_t charcode);
+uint32_t CharCodeFromUnicodeForFreetypeEncoding(int encoding, wchar_t unicode);
+wchar_t UnicodeFromAppleRomanCharCode(uint8_t charcode);
 
-const uint16_t* PDF_UnicodesForPredefinedCharSet(int encoding);
-const char* PDF_CharNameFromPredefinedCharSet(int encoding, uint8_t charcode);
+const uint16_t* UnicodesForPredefinedCharSet(FontEncoding encoding);
+const char* CharNameFromPredefinedCharSet(FontEncoding encoding,
+                                          uint8_t charcode);
 
 class CPDF_Object;
 
@@ -35,7 +37,7 @@
  public:
   static constexpr size_t kEncodingTableSize = 256;
 
-  explicit CPDF_FontEncoding(int PredefinedEncoding);
+  explicit CPDF_FontEncoding(FontEncoding predefined_encoding);
 
   bool IsIdentical(const CPDF_FontEncoding* pAnother) const;
 
@@ -51,7 +53,7 @@
   RetainPtr<CPDF_Object> Realize(WeakPtr<ByteStringPool> pPool) const;
 
  private:
-  wchar_t m_Unicodes[kEncodingTableSize];
+  wchar_t m_Unicodes[kEncodingTableSize] = {};
 };
 
 #endif  // CORE_FPDFAPI_FONT_CPDF_FONTENCODING_H_
diff --git a/core/fpdfapi/font/cpdf_fontglobals.cpp b/core/fpdfapi/font/cpdf_fontglobals.cpp
index 10b6c5e..aa5386d 100644
--- a/core/fpdfapi/font/cpdf_fontglobals.cpp
+++ b/core/fpdfapi/font/cpdf_fontglobals.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,44 +6,51 @@
 
 #include "core/fpdfapi/font/cpdf_fontglobals.h"
 
+#include <utility>
+
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h"
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h"
 #include "core/fpdfapi/font/cfx_stockfontarray.h"
+#include "core/fpdfapi/font/cpdf_cid2unicodemap.h"
+#include "core/fpdfapi/font/cpdf_cmap.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
 
 namespace {
 
 CPDF_FontGlobals* g_FontGlobals = nullptr;
 
+RetainPtr<const CPDF_CMap> LoadPredefinedCMap(ByteStringView name) {
+  if (!name.IsEmpty() && name[0] == '/')
+    name = name.Last(name.GetLength() - 1);
+  return pdfium::MakeRetain<CPDF_CMap>(name);
+}
+
 }  // namespace
 
 // static
 void CPDF_FontGlobals::Create() {
-  ASSERT(!g_FontGlobals);
+  DCHECK(!g_FontGlobals);
   g_FontGlobals = new CPDF_FontGlobals();
 }
 
 // static
 void CPDF_FontGlobals::Destroy() {
-  ASSERT(g_FontGlobals);
+  DCHECK(g_FontGlobals);
   delete g_FontGlobals;
   g_FontGlobals = nullptr;
 }
 
 // static
 CPDF_FontGlobals* CPDF_FontGlobals::GetInstance() {
-  ASSERT(g_FontGlobals);
+  DCHECK(g_FontGlobals);
   return g_FontGlobals;
 }
 
-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;
 
@@ -66,38 +73,63 @@
 
 void CPDF_FontGlobals::Set(CPDF_Document* pDoc,
                            CFX_FontMapper::StandardFont index,
-                           const RetainPtr<CPDF_Font>& pFont) {
-  if (!pdfium::ContainsKey(m_StockMap, pDoc))
-    m_StockMap[pDoc] = pdfium::MakeUnique<CFX_StockFontArray>();
-  m_StockMap[pDoc]->SetFont(index, pFont);
+                           RetainPtr<CPDF_Font> pFont) {
+  UnownedPtr<CPDF_Document> pKey(pDoc);
+  if (!pdfium::Contains(m_StockMap, pKey))
+    m_StockMap[pKey] = std::make_unique<CFX_StockFontArray>();
+  m_StockMap[pKey]->SetFont(index, std::move(pFont));
 }
 
 void CPDF_FontGlobals::Clear(CPDF_Document* pDoc) {
-  m_StockMap.erase(pDoc);
+  // Avoid constructing smart-pointer key as erase() doesn't invoke
+  // transparent lookup in the same way find() does.
+  auto it = m_StockMap.find(pDoc);
+  if (it != m_StockMap.end())
+    m_StockMap.erase(it);
 }
 
 void CPDF_FontGlobals::LoadEmbeddedGB1CMaps() {
-  SetEmbeddedCharset(CIDSET_GB1, pdfium::make_span(g_FXCMAP_GB1_cmaps,
-                                                   g_FXCMAP_GB1_cmaps_size));
-  SetEmbeddedToUnicode(CIDSET_GB1, g_FXCMAP_GB1CID2Unicode_5);
+  SetEmbeddedCharset(CIDSET_GB1, pdfium::make_span(fxcmap::kGB1_cmaps,
+                                                   fxcmap::kGB1_cmaps_size));
+  SetEmbeddedToUnicode(CIDSET_GB1, fxcmap::kGB1CID2Unicode_5);
 }
 
 void CPDF_FontGlobals::LoadEmbeddedCNS1CMaps() {
-  SetEmbeddedCharset(CIDSET_CNS1, pdfium::make_span(g_FXCMAP_CNS1_cmaps,
-                                                    g_FXCMAP_CNS1_cmaps_size));
-  SetEmbeddedToUnicode(CIDSET_CNS1, g_FXCMAP_CNS1CID2Unicode_5);
+  SetEmbeddedCharset(CIDSET_CNS1, pdfium::make_span(fxcmap::kCNS1_cmaps,
+                                                    fxcmap::kCNS1_cmaps_size));
+  SetEmbeddedToUnicode(CIDSET_CNS1, fxcmap::kCNS1CID2Unicode_5);
 }
 
 void CPDF_FontGlobals::LoadEmbeddedJapan1CMaps() {
   SetEmbeddedCharset(
       CIDSET_JAPAN1,
-      pdfium::make_span(g_FXCMAP_Japan1_cmaps, g_FXCMAP_Japan1_cmaps_size));
-  SetEmbeddedToUnicode(CIDSET_JAPAN1, g_FXCMAP_Japan1CID2Unicode_4);
+      pdfium::make_span(fxcmap::kJapan1_cmaps, fxcmap::kJapan1_cmaps_size));
+  SetEmbeddedToUnicode(CIDSET_JAPAN1, fxcmap::kJapan1CID2Unicode_4);
 }
 
 void CPDF_FontGlobals::LoadEmbeddedKorea1CMaps() {
   SetEmbeddedCharset(
       CIDSET_KOREA1,
-      pdfium::make_span(g_FXCMAP_Korea1_cmaps, g_FXCMAP_Korea1_cmaps_size));
-  SetEmbeddedToUnicode(CIDSET_KOREA1, g_FXCMAP_Korea1CID2Unicode_2);
+      pdfium::make_span(fxcmap::kKorea1_cmaps, fxcmap::kKorea1_cmaps_size));
+  SetEmbeddedToUnicode(CIDSET_KOREA1, fxcmap::kKorea1CID2Unicode_2);
+}
+
+RetainPtr<const CPDF_CMap> CPDF_FontGlobals::GetPredefinedCMap(
+    const ByteString& name) {
+  auto it = m_CMaps.find(name);
+  if (it != m_CMaps.end())
+    return it->second;
+
+  RetainPtr<const CPDF_CMap> pCMap = LoadPredefinedCMap(name.AsStringView());
+  if (!name.IsEmpty())
+    m_CMaps[name] = pCMap;
+
+  return pCMap;
+}
+
+CPDF_CID2UnicodeMap* CPDF_FontGlobals::GetCID2UnicodeMap(CIDSet charset) {
+  if (!m_CID2UnicodeMaps[charset]) {
+    m_CID2UnicodeMaps[charset] = std::make_unique<CPDF_CID2UnicodeMap>(charset);
+  }
+  return m_CID2UnicodeMaps[charset].get();
 }
diff --git a/core/fpdfapi/font/cpdf_fontglobals.h b/core/fpdfapi/font/cpdf_fontglobals.h
index c09f29c..f5148a0 100644
--- a/core/fpdfapi/font/cpdf_fontglobals.h
+++ b/core/fpdfapi/font/cpdf_fontglobals.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,19 @@
 #ifndef CORE_FPDFAPI_FONT_CPDF_FONTGLOBALS_H_
 #define CORE_FPDFAPI_FONT_CPDF_FONTGLOBALS_H_
 
+#include <array>
+#include <functional>
 #include <map>
 #include <memory>
 
 #include "core/fpdfapi/cmaps/fpdf_cmaps.h"
-#include "core/fpdfapi/font/cpdf_cmapmanager.h"
+#include "core/fpdfapi/font/cpdf_cidfont.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/cfx_fontmapper.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_StockFontArray;
+class CPDF_Font;
 
 class CPDF_FontGlobals {
  public:
@@ -33,22 +36,23 @@
                             CFX_FontMapper::StandardFont index);
   void Set(CPDF_Document* pDoc,
            CFX_FontMapper::StandardFont index,
-           const RetainPtr<CPDF_Font>& pFont);
+           RetainPtr<CPDF_Font> pFont);
 
-  void SetEmbeddedCharset(size_t idx, pdfium::span<const FXCMAP_CMap> map) {
+  void SetEmbeddedCharset(CIDSet idx, pdfium::span<const fxcmap::CMap> map) {
     m_EmbeddedCharsets[idx] = map;
   }
-  pdfium::span<const FXCMAP_CMap> GetEmbeddedCharset(size_t idx) const {
+  pdfium::span<const fxcmap::CMap> GetEmbeddedCharset(CIDSet idx) const {
     return m_EmbeddedCharsets[idx];
   }
-  void SetEmbeddedToUnicode(size_t idx, pdfium::span<const uint16_t> map) {
+  void SetEmbeddedToUnicode(CIDSet idx, pdfium::span<const uint16_t> map) {
     m_EmbeddedToUnicodes[idx] = map;
   }
-  pdfium::span<const uint16_t> GetEmbeddedToUnicode(size_t idx) {
+  pdfium::span<const uint16_t> GetEmbeddedToUnicode(CIDSet idx) {
     return m_EmbeddedToUnicodes[idx];
   }
 
-  CPDF_CMapManager* GetCMapManager() { return &m_CMapManager; }
+  RetainPtr<const CPDF_CMap> GetPredefinedCMap(const ByteString& name);
+  CPDF_CID2UnicodeMap* GetCID2UnicodeMap(CIDSet charset);
 
  private:
   CPDF_FontGlobals();
@@ -59,10 +63,17 @@
   void LoadEmbeddedJapan1CMaps();
   void LoadEmbeddedKorea1CMaps();
 
-  CPDF_CMapManager m_CMapManager;
-  pdfium::span<const FXCMAP_CMap> m_EmbeddedCharsets[CIDSET_NUM_SETS];
-  pdfium::span<const uint16_t> m_EmbeddedToUnicodes[CIDSET_NUM_SETS];
-  std::map<CPDF_Document*, std::unique_ptr<CFX_StockFontArray>> m_StockMap;
+  std::map<ByteString, RetainPtr<const CPDF_CMap>> m_CMaps;
+  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<>>
+      m_StockMap;
 };
 
 #endif  // CORE_FPDFAPI_FONT_CPDF_FONTGLOBALS_H_
diff --git a/core/fpdfapi/font/cpdf_simplefont.cpp b/core/fpdfapi/font/cpdf_simplefont.cpp
index c0fd4e0..ecdf72a 100644
--- a/core/fpdfapi/font/cpdf_simplefont.cpp
+++ b/core/fpdfapi/font/cpdf_simplefont.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,34 +6,40 @@
 
 #include "core/fpdfapi/font/cpdf_simplefont.h"
 
+#include <algorithm>
+#include <iterator>
+#include <utility>
+
+#include "constants/font_encodings.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxge/freetype/fx_freetype.h"
 #include "core/fxge/fx_font.h"
-#include "core/fxge/fx_freetype.h"
 #include "third_party/base/numerics/safe_math.h"
 
 namespace {
 
-void GetPredefinedEncoding(const ByteString& value, int* basemap) {
-  if (value == "WinAnsiEncoding")
-    *basemap = PDFFONT_ENCODING_WINANSI;
-  else if (value == "MacRomanEncoding")
-    *basemap = PDFFONT_ENCODING_MACROMAN;
-  else if (value == "MacExpertEncoding")
-    *basemap = PDFFONT_ENCODING_MACEXPERT;
-  else if (value == "PDFDocEncoding")
-    *basemap = PDFFONT_ENCODING_PDFDOC;
+void GetPredefinedEncoding(const ByteString& value, FontEncoding* basemap) {
+  if (value == pdfium::font_encodings::kWinAnsiEncoding)
+    *basemap = FontEncoding::kWinAnsi;
+  else if (value == pdfium::font_encodings::kMacRomanEncoding)
+    *basemap = FontEncoding::kMacRoman;
+  else if (value == pdfium::font_encodings::kMacExpertEncoding)
+    *basemap = FontEncoding::kMacExpert;
+  else if (value == pdfium::font_encodings::kPDFDocEncoding)
+    *basemap = FontEncoding::kPdfDoc;
 }
 
 }  // namespace
 
 CPDF_SimpleFont::CPDF_SimpleFont(CPDF_Document* pDocument,
-                                 CPDF_Dictionary* pFontDict)
-    : CPDF_Font(pDocument, pFontDict) {
+                                 RetainPtr<CPDF_Dictionary> pFontDict)
+    : CPDF_Font(pDocument, std::move(pFontDict)) {
   memset(m_CharWidth, 0xff, sizeof(m_CharWidth));
   memset(m_GlyphIndex, 0xff, sizeof(m_GlyphIndex));
-  for (size_t i = 0; i < FX_ArraySize(m_CharBBox); ++i)
+  for (size_t i = 0; i < std::size(m_CharBBox); ++i)
     m_CharBBox[i] = FX_RECT(-1, -1, -1, -1);
 }
 
@@ -78,8 +84,8 @@
   if (err)
     return;
 
-  int iHoriBearingX = FXFT_Get_Glyph_HoriBearingX(face);
-  int iHoriBearingY = FXFT_Get_Glyph_HoriBearingY(face);
+  FT_Pos iHoriBearingX = FXFT_Get_Glyph_HoriBearingX(face);
+  FT_Pos iHoriBearingY = FXFT_Get_Glyph_HoriBearingY(face);
   m_CharBBox[charcode] =
       FX_RECT(TT2PDF(iHoriBearingX, face), TT2PDF(iHoriBearingY, face),
               TT2PDF(iHoriBearingX + FXFT_Get_Glyph_Width(face), face),
@@ -98,31 +104,78 @@
   }
 }
 
+void CPDF_SimpleFont::LoadCharWidths(const CPDF_Dictionary* font_desc) {
+  RetainPtr<const CPDF_Array> width_array = m_pFontDict->GetArrayFor("Widths");
+  m_bUseFontWidth = !width_array;
+  if (!width_array)
+    return;
+
+  if (font_desc && font_desc->KeyExist("MissingWidth")) {
+    int missing_width = font_desc->GetIntegerFor("MissingWidth");
+    std::fill(std::begin(m_CharWidth), std::end(m_CharWidth), missing_width);
+  }
+
+  size_t width_start = m_pFontDict->GetIntegerFor("FirstChar", 0);
+  size_t width_end = m_pFontDict->GetIntegerFor("LastChar", 0);
+  if (width_start > 255)
+    return;
+
+  if (width_end == 0 || width_end >= width_start + width_array->size())
+    width_end = width_start + width_array->size() - 1;
+  if (width_end > 255)
+    width_end = 255;
+  for (size_t i = width_start; i <= width_end; i++)
+    m_CharWidth[i] = width_array->GetIntegerAt(i - width_start);
+}
+
+void CPDF_SimpleFont::LoadDifferences(const CPDF_Dictionary* encoding) {
+  RetainPtr<const CPDF_Array> diffs = encoding->GetArrayFor("Differences");
+  if (!diffs)
+    return;
+
+  m_CharNames.resize(kInternalTableSize);
+  uint32_t cur_code = 0;
+  for (uint32_t i = 0; i < diffs->size(); i++) {
+    RetainPtr<const CPDF_Object> element = diffs->GetDirectObjectAt(i);
+    if (!element)
+      continue;
+
+    const CPDF_Name* name = element->AsName();
+    if (name) {
+      if (cur_code < m_CharNames.size())
+        m_CharNames[cur_code] = name->GetString();
+      cur_code++;
+    } else {
+      cur_code = element->GetInteger();
+    }
+  }
+}
+
 void CPDF_SimpleFont::LoadPDFEncoding(bool bEmbedded, bool bTrueType) {
-  const CPDF_Object* pEncoding = m_pFontDict->GetDirectObjectFor("Encoding");
+  RetainPtr<const CPDF_Object> pEncoding =
+      m_pFontDict->GetDirectObjectFor("Encoding");
   if (!pEncoding) {
     if (m_BaseFontName == "Symbol") {
-      m_BaseEncoding = bTrueType ? PDFFONT_ENCODING_MS_SYMBOL
-                                 : PDFFONT_ENCODING_ADOBE_SYMBOL;
-    } else if (!bEmbedded && m_BaseEncoding == PDFFONT_ENCODING_BUILTIN) {
-      m_BaseEncoding = PDFFONT_ENCODING_WINANSI;
+      m_BaseEncoding =
+          bTrueType ? FontEncoding::kMsSymbol : FontEncoding::kAdobeSymbol;
+    } else if (!bEmbedded && m_BaseEncoding == FontEncoding::kBuiltin) {
+      m_BaseEncoding = FontEncoding::kWinAnsi;
     }
     return;
   }
   if (pEncoding->IsName()) {
-    if (m_BaseEncoding == PDFFONT_ENCODING_ADOBE_SYMBOL ||
-        m_BaseEncoding == PDFFONT_ENCODING_ZAPFDINGBATS) {
+    if (m_BaseEncoding == FontEncoding::kAdobeSymbol ||
+        m_BaseEncoding == FontEncoding::kZapfDingbats) {
       return;
     }
     if (FontStyleIsSymbolic(m_Flags) && m_BaseFontName == "Symbol") {
       if (!bTrueType)
-        m_BaseEncoding = PDFFONT_ENCODING_ADOBE_SYMBOL;
+        m_BaseEncoding = FontEncoding::kAdobeSymbol;
       return;
     }
     ByteString bsEncoding = pEncoding->GetString();
-    if (bsEncoding.Compare("MacExpertEncoding") == 0) {
-      bsEncoding = "WinAnsiEncoding";
-    }
+    if (bsEncoding == pdfium::font_encodings::kMacExpertEncoding)
+      bsEncoding = pdfium::font_encodings::kWinAnsiEncoding;
     GetPredefinedEncoding(bsEncoding, &m_BaseEncoding);
     return;
   }
@@ -131,39 +184,20 @@
   if (!pDict)
     return;
 
-  if (m_BaseEncoding != PDFFONT_ENCODING_ADOBE_SYMBOL &&
-      m_BaseEncoding != PDFFONT_ENCODING_ZAPFDINGBATS) {
-    ByteString bsEncoding = pDict->GetStringFor("BaseEncoding");
-    if (bTrueType && bsEncoding.Compare("MacExpertEncoding") == 0)
-      bsEncoding = "WinAnsiEncoding";
+  if (m_BaseEncoding != FontEncoding::kAdobeSymbol &&
+      m_BaseEncoding != FontEncoding::kZapfDingbats) {
+    ByteString bsEncoding = pDict->GetByteStringFor("BaseEncoding");
+    if (bTrueType && bsEncoding == pdfium::font_encodings::kMacExpertEncoding)
+      bsEncoding = pdfium::font_encodings::kWinAnsiEncoding;
     GetPredefinedEncoding(bsEncoding, &m_BaseEncoding);
   }
-  if ((!bEmbedded || bTrueType) && m_BaseEncoding == PDFFONT_ENCODING_BUILTIN)
-    m_BaseEncoding = PDFFONT_ENCODING_STANDARD;
+  if ((!bEmbedded || bTrueType) && m_BaseEncoding == FontEncoding::kBuiltin)
+    m_BaseEncoding = FontEncoding::kStandard;
 
-  const CPDF_Array* pDiffs = pDict->GetArrayFor("Differences");
-  if (!pDiffs)
-    return;
-
-  m_CharNames.resize(256);
-  uint32_t cur_code = 0;
-  for (uint32_t i = 0; i < pDiffs->size(); i++) {
-    const CPDF_Object* pElement = pDiffs->GetDirectObjectAt(i);
-    if (!pElement)
-      continue;
-
-    const CPDF_Name* pName = pElement->AsName();
-    if (pName) {
-      if (cur_code < 256)
-        m_CharNames[cur_code] = pName->GetString();
-      cur_code++;
-    } else {
-      cur_code = pElement->GetInteger();
-    }
-  }
+  LoadDifferences(pDict);
 }
 
-uint32_t CPDF_SimpleFont::GetCharWidthF(uint32_t charcode) {
+int CPDF_SimpleFont::GetCharWidthF(uint32_t charcode) {
   if (charcode > 0xff)
     charcode = 0;
 
@@ -187,30 +221,11 @@
 }
 
 bool CPDF_SimpleFont::LoadCommon() {
-  const CPDF_Dictionary* pFontDesc = m_pFontDict->GetDictFor("FontDescriptor");
-  if (pFontDesc) {
-    LoadFontDescriptor(pFontDesc);
-  }
-  const CPDF_Array* pWidthArray = m_pFontDict->GetArrayFor("Widths");
-  m_bUseFontWidth = !pWidthArray;
-  if (pWidthArray) {
-    if (pFontDesc && pFontDesc->KeyExist("MissingWidth")) {
-      int MissingWidth = pFontDesc->GetIntegerFor("MissingWidth");
-      for (int i = 0; i < 256; i++) {
-        m_CharWidth[i] = MissingWidth;
-      }
-    }
-    size_t width_start = m_pFontDict->GetIntegerFor("FirstChar", 0);
-    size_t width_end = m_pFontDict->GetIntegerFor("LastChar", 0);
-    if (width_start <= 255) {
-      if (width_end == 0 || width_end >= width_start + pWidthArray->size())
-        width_end = width_start + pWidthArray->size() - 1;
-      if (width_end > 255)
-        width_end = 255;
-      for (size_t i = width_start; i <= width_end; i++)
-        m_CharWidth[i] = pWidthArray->GetIntegerAt(i - width_start);
-    }
-  }
+  RetainPtr<const CPDF_Dictionary> pFontDesc =
+      m_pFontDict->GetDictFor("FontDescriptor");
+  if (pFontDesc)
+    LoadFontDescriptor(pFontDesc.Get());
+  LoadCharWidths(pFontDesc.Get());
   if (m_pFontFile) {
     if (m_BaseFontName.GetLength() > 8 && m_BaseFontName[7] == '+')
       m_BaseFontName = m_BaseFontName.Last(m_BaseFontName.GetLength() - 8);
@@ -218,7 +233,7 @@
     LoadSubstFont();
   }
   if (!FontStyleIsSymbolic(m_Flags))
-    m_BaseEncoding = PDFFONT_ENCODING_STANDARD;
+    m_BaseEncoding = FontEncoding::kStandard;
   LoadPDFEncoding(!!m_pFontFile, m_Font.IsTTFont());
   LoadGlyphMap();
   m_CharNames.clear();
@@ -228,7 +243,7 @@
   if (FontStyleIsAllCaps(m_Flags)) {
     static const unsigned char kLowercases[][2] = {
         {'a', 'z'}, {0xe0, 0xf6}, {0xf8, 0xfd}};
-    for (size_t range = 0; range < FX_ArraySize(kLowercases); ++range) {
+    for (size_t range = 0; range < std::size(kLowercases); ++range) {
       const auto& lower = kLowercases[range];
       for (int i = lower[0]; i <= lower[1]; ++i) {
         if (m_GlyphIndex[i] != 0xffff && m_pFontFile)
@@ -249,8 +264,9 @@
 
 void CPDF_SimpleFont::LoadSubstFont() {
   if (!m_bUseFontWidth && !FontStyleIsFixedPitch(m_Flags)) {
-    int width = 0, i;
-    for (i = 0; i < 256; i++) {
+    int width = 0;
+    size_t i;
+    for (i = 0; i < kInternalTableSize; i++) {
       if (m_CharWidth[i] == 0 || m_CharWidth[i] == 0xffff)
         continue;
 
@@ -259,17 +275,17 @@
       else if (width != m_CharWidth[i])
         break;
     }
-    if (i == 256 && width)
+    if (i == kInternalTableSize && width)
       m_Flags |= FXFONT_FIXED_PITCH;
   }
   m_Font.LoadSubst(m_BaseFontName, IsTrueTypeFont(), m_Flags, GetFontWeight(),
-                   m_ItalicAngle, 0, false);
+                   m_ItalicAngle, FX_CodePage::kDefANSI, false);
 }
 
 bool CPDF_SimpleFont::IsUnicodeCompatible() const {
-  return m_BaseEncoding != PDFFONT_ENCODING_BUILTIN &&
-         m_BaseEncoding != PDFFONT_ENCODING_ADOBE_SYMBOL &&
-         m_BaseEncoding != PDFFONT_ENCODING_ZAPFDINGBATS;
+  return m_BaseEncoding != FontEncoding::kBuiltin &&
+         m_BaseEncoding != FontEncoding::kAdobeSymbol &&
+         m_BaseEncoding != FontEncoding::kZapfDingbats;
 }
 
 WideString CPDF_SimpleFont::UnicodeFromCharCode(uint32_t charcode) const {
@@ -279,7 +295,7 @@
   wchar_t ret = m_Encoding.UnicodeFromCharCode((uint8_t)charcode);
   if (ret == 0)
     return WideString();
-  return ret;
+  return WideString(ret);
 }
 
 uint32_t CPDF_SimpleFont::CharCodeFromUnicode(wchar_t unicode) const {
diff --git a/core/fpdfapi/font/cpdf_simplefont.h b/core/fpdfapi/font/cpdf_simplefont.h
index 11359c3..e0d0400 100644
--- a/core/fpdfapi/font/cpdf_simplefont.h
+++ b/core/fpdfapi/font/cpdf_simplefont.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,20 @@
 #ifndef CORE_FPDFAPI_FONT_CPDF_SIMPLEFONT_H_
 #define CORE_FPDFAPI_FONT_CPDF_SIMPLEFONT_H_
 
+#include <stdint.h>
+
 #include <vector>
 
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/font/cpdf_fontencoding.h"
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
 
 class CPDF_SimpleFont : public CPDF_Font {
  public:
   ~CPDF_SimpleFont() override;
 
   // CPDF_Font
-  uint32_t GetCharWidthF(uint32_t charcode) override;
+  int GetCharWidthF(uint32_t charcode) override;
   FX_RECT GetCharBBox(uint32_t charcode) override;
   int GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) override;
   bool IsUnicodeCompatible() const override;
@@ -31,22 +32,27 @@
   bool HasFontWidths() const override;
 
  protected:
-  CPDF_SimpleFont(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict);
+  static constexpr size_t kInternalTableSize = 256;
+
+  CPDF_SimpleFont(CPDF_Document* pDocument,
+                  RetainPtr<CPDF_Dictionary> pFontDict);
 
   virtual void LoadGlyphMap() = 0;
 
   bool LoadCommon();
   void LoadSubstFont();
   void LoadCharMetrics(int charcode);
+  void LoadCharWidths(const CPDF_Dictionary* font_desc);
+  void LoadDifferences(const CPDF_Dictionary* encoding);
   void LoadPDFEncoding(bool bEmbedded, bool bTrueType);
 
-  CPDF_FontEncoding m_Encoding{PDFFONT_ENCODING_BUILTIN};
-  int m_BaseEncoding = PDFFONT_ENCODING_BUILTIN;
-  bool m_bUseFontWidth;
+  CPDF_FontEncoding m_Encoding{FontEncoding::kBuiltin};
+  FontEncoding m_BaseEncoding = FontEncoding::kBuiltin;
+  bool m_bUseFontWidth = false;
   std::vector<ByteString> m_CharNames;
-  uint16_t m_GlyphIndex[256];
-  uint16_t m_CharWidth[256];
-  FX_RECT m_CharBBox[256];
+  uint16_t m_GlyphIndex[kInternalTableSize];
+  uint16_t m_CharWidth[kInternalTableSize];
+  FX_RECT m_CharBBox[kInternalTableSize];
 };
 
 #endif  // CORE_FPDFAPI_FONT_CPDF_SIMPLEFONT_H_
diff --git a/core/fpdfapi/font/cpdf_tounicodemap.cpp b/core/fpdfapi/font/cpdf_tounicodemap.cpp
index 1872d5d..2e354a2 100644
--- a/core/fpdfapi/font/cpdf_tounicodemap.cpp
+++ b/core/fpdfapi/font/cpdf_tounicodemap.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,14 +6,17 @@
 
 #include "core/fpdfapi/font/cpdf_tounicodemap.h"
 
+#include <set>
 #include <utility>
 
 #include "core/fpdfapi/font/cpdf_cid2unicodemap.h"
 #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"
 #include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
@@ -37,56 +40,81 @@
 
 }  // namespace
 
-CPDF_ToUnicodeMap::CPDF_ToUnicodeMap(const CPDF_Stream* pStream) {
-  Load(pStream);
+CPDF_ToUnicodeMap::CPDF_ToUnicodeMap(RetainPtr<const CPDF_Stream> pStream) {
+  Load(std::move(pStream));
 }
 
 CPDF_ToUnicodeMap::~CPDF_ToUnicodeMap() = default;
 
 WideString CPDF_ToUnicodeMap::Lookup(uint32_t charcode) const {
-  auto it = m_Map.find(charcode);
-  if (it == m_Map.end()) {
+  auto it = m_Multimap.find(charcode);
+  if (it == m_Multimap.end()) {
     if (!m_pBaseMap)
       return WideString();
-    return m_pBaseMap->UnicodeFromCID(static_cast<uint16_t>(charcode));
+    return WideString(
+        m_pBaseMap->UnicodeFromCID(static_cast<uint16_t>(charcode)));
   }
 
-  uint32_t value = it->second;
+  uint32_t value = *it->second.begin();
   wchar_t unicode = static_cast<wchar_t>(value & 0xffff);
   if (unicode != 0xffff)
-    return unicode;
+    return WideString(unicode);
 
-  WideStringView buf = m_MultiCharBuf.AsStringView();
   size_t index = value >> 16;
-  if (!buf.IsValidIndex(index))
-    return WideString();
-  return WideString(buf.Substr(index + 1, buf[index]));
+  return index < m_MultiCharVec.size() ? m_MultiCharVec[index] : WideString();
 }
 
 uint32_t CPDF_ToUnicodeMap::ReverseLookup(wchar_t unicode) const {
-  for (const auto& pair : m_Map) {
-    if (pair.second == static_cast<uint32_t>(unicode))
+  for (const auto& pair : m_Multimap) {
+    if (pdfium::Contains(pair.second, static_cast<uint32_t>(unicode)))
       return pair.first;
   }
   return 0;
 }
 
+size_t CPDF_ToUnicodeMap::GetUnicodeCountByCharcodeForTesting(
+    uint32_t charcode) const {
+  auto it = m_Multimap.find(charcode);
+  return it != m_Multimap.end() ? it->second.size() : 0u;
+}
+
 // static
-pdfium::Optional<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 pdfium::nullopt;
+    return absl::nullopt;
 
   FX_SAFE_UINT32 code = 0;
   for (char c : str.Substr(1, len - 2)) {
     if (!FXSYS_IsHexDigit(c))
-      return pdfium::nullopt;
+      return absl::nullopt;
 
     code = code * 16 + FXSYS_HexCharToInt(c);
     if (!code.IsValid())
-      return pdfium::nullopt;
+      return absl::nullopt;
   }
-  return pdfium::Optional<uint32_t>(code.ValueOrDie());
+  return absl::optional<uint32_t>(code.ValueOrDie());
 }
 
 // static
@@ -113,12 +141,12 @@
   return result;
 }
 
-void CPDF_ToUnicodeMap::Load(const CPDF_Stream* pStream) {
+void CPDF_ToUnicodeMap::Load(RetainPtr<const CPDF_Stream> pStream) {
   CIDSet cid_set = CIDSET_UNKNOWN;
-  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
+  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(pStream));
   pAcc->LoadAllDataFiltered();
   CPDF_SimpleParser parser(pAcc->GetSpan());
-  while (1) {
+  while (true) {
     ByteStringView word = parser.GetWord();
     if (word.IsEmpty())
       break;
@@ -136,19 +164,18 @@
     else if (word == "/Adobe-GB1-UCS2")
       cid_set = CIDSET_GB1;
   }
-  if (cid_set) {
-    auto* manager = CPDF_FontGlobals::GetInstance()->GetCMapManager();
-    m_pBaseMap = manager->GetCID2UnicodeMap(cid_set);
+  if (cid_set != CIDSET_UNKNOWN) {
+    m_pBaseMap = CPDF_FontGlobals::GetInstance()->GetCID2UnicodeMap(cid_set);
   }
 }
 
 void CPDF_ToUnicodeMap::HandleBeginBFChar(CPDF_SimpleParser* pParser) {
-  while (1) {
+  while (true) {
     ByteStringView word = pParser->GetWord();
     if (word.IsEmpty() || word == "endbfchar")
       return;
 
-    pdfium::Optional<uint32_t> code = StringToCode(word);
+    absl::optional<uint32_t> code = StringToCode(word);
     if (!code.has_value())
       return;
 
@@ -157,17 +184,17 @@
 }
 
 void CPDF_ToUnicodeMap::HandleBeginBFRange(CPDF_SimpleParser* pParser) {
-  while (1) {
+  while (true) {
     ByteStringView lowcode_str = pParser->GetWord();
     if (lowcode_str.IsEmpty() || lowcode_str == "endbfrange")
       return;
 
-    pdfium::Optional<uint32_t> lowcode_opt = StringToCode(lowcode_str);
+    absl::optional<uint32_t> lowcode_opt = StringToCode(lowcode_str);
     if (!lowcode_opt.has_value())
       return;
 
     ByteStringView highcode_str = pParser->GetWord();
-    pdfium::Optional<uint32_t> highcode_opt = StringToCode(highcode_str);
+    absl::optional<uint32_t> highcode_opt = StringToCode(highcode_str);
     if (!highcode_opt.has_value())
       return;
 
@@ -176,36 +203,41 @@
 
     ByteStringView start = pParser->GetWord();
     if (start == "[") {
-      for (uint32_t code = lowcode; code <= highcode; code++)
-        SetCode(code, StringToWideString(pParser->GetWord()));
+      for (FX_SAFE_UINT32 code = lowcode;
+           code.IsValid() && code.ValueOrDie() <= highcode; code++) {
+        SetCode(code.ValueOrDie(), StringToWideString(pParser->GetWord()));
+      }
       pParser->GetWord();
       continue;
     }
 
     WideString destcode = StringToWideString(start);
     if (destcode.GetLength() == 1) {
-      pdfium::Optional<uint32_t> value_or_error = StringToCode(start);
+      absl::optional<uint32_t> value_or_error = StringToCode(start);
       if (!value_or_error.has_value())
         return;
 
       uint32_t value = value_or_error.value();
-      for (uint32_t code = lowcode; code <= highcode; code++)
-        m_Map[code] = value++;
+      for (FX_SAFE_UINT32 code = lowcode;
+           code.IsValid() && code.ValueOrDie() <= highcode; code++) {
+        InsertIntoMultimap(code.ValueOrDie(), value++);
+      }
     } else {
-      for (uint32_t code = lowcode; code <= highcode; code++) {
+      for (FX_SAFE_UINT32 code = lowcode;
+           code.IsValid() && code.ValueOrDie() <= highcode; code++) {
+        uint32_t code_value = code.ValueOrDie();
         WideString retcode =
-            code == lowcode ? destcode : StringDataAdd(destcode);
-        m_Map[code] = GetUnicode();
-        m_MultiCharBuf.AppendChar(retcode.GetLength());
-        m_MultiCharBuf << retcode;
+            code_value == lowcode ? destcode : StringDataAdd(destcode);
+        InsertIntoMultimap(code_value, GetMultiCharIndexIndicator());
+        m_MultiCharVec.push_back(retcode);
         destcode = std::move(retcode);
       }
     }
   }
 }
 
-uint32_t CPDF_ToUnicodeMap::GetUnicode() const {
-  FX_SAFE_UINT32 uni = m_MultiCharBuf.GetLength();
+uint32_t CPDF_ToUnicodeMap::GetMultiCharIndexIndicator() const {
+  FX_SAFE_UINT32 uni = m_MultiCharVec.size();
   uni = uni * 0x10000 + 0xffff;
   return uni.ValueOrDefault(0);
 }
@@ -216,10 +248,19 @@
     return;
 
   if (len == 1) {
-    m_Map[srccode] = destcode[0];
+    InsertIntoMultimap(srccode, destcode[0]);
   } else {
-    m_Map[srccode] = GetUnicode();
-    m_MultiCharBuf.AppendChar(len);
-    m_MultiCharBuf << destcode;
+    InsertIntoMultimap(srccode, GetMultiCharIndexIndicator());
+    m_MultiCharVec.push_back(destcode);
   }
 }
+
+void CPDF_ToUnicodeMap::InsertIntoMultimap(uint32_t code, uint32_t destcode) {
+  auto it = m_Multimap.find(code);
+  if (it == m_Multimap.end()) {
+    m_Multimap.emplace(code, std::set<uint32_t>{destcode});
+    return;
+  }
+
+  it->second.emplace(destcode);
+}
diff --git a/core/fpdfapi/font/cpdf_tounicodemap.h b/core/fpdfapi/font/cpdf_tounicodemap.h
index 9eaf625..7f4ba03 100644
--- a/core/fpdfapi/font/cpdf_tounicodemap.h
+++ b/core/fpdfapi/font/cpdf_tounicodemap.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,9 +8,13 @@
 #define CORE_FPDFAPI_FONT_CPDF_TOUNICODEMAP_H_
 
 #include <map>
+#include <set>
+#include <vector>
 
-#include "core/fxcrt/cfx_widetextbuf.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CPDF_CID2UnicodeMap;
 class CPDF_SimpleParser;
@@ -18,28 +22,34 @@
 
 class CPDF_ToUnicodeMap {
  public:
-  explicit CPDF_ToUnicodeMap(const CPDF_Stream* pStream);
+  explicit CPDF_ToUnicodeMap(RetainPtr<const CPDF_Stream> pStream);
   ~CPDF_ToUnicodeMap();
 
   WideString Lookup(uint32_t charcode) const;
   uint32_t ReverseLookup(wchar_t unicode) const;
 
+  size_t GetUnicodeCountByCharcodeForTesting(uint32_t charcode) const;
+
  private:
   friend class cpdf_tounicodemap_StringToCode_Test;
   friend class cpdf_tounicodemap_StringToWideString_Test;
 
-  static pdfium::Optional<uint32_t> StringToCode(ByteStringView str);
+  static absl::optional<uint32_t> StringToCode(ByteStringView input);
   static WideString StringToWideString(ByteStringView str);
 
-  void Load(const CPDF_Stream* pStream);
+  void Load(RetainPtr<const CPDF_Stream> pStream);
   void HandleBeginBFChar(CPDF_SimpleParser* pParser);
   void HandleBeginBFRange(CPDF_SimpleParser* pParser);
-  uint32_t GetUnicode() const;
+  uint32_t GetMultiCharIndexIndicator() const;
   void SetCode(uint32_t srccode, WideString destcode);
 
-  std::map<uint32_t, uint32_t> m_Map;
+  // Inserts a new entry which hasn't not been inserted into `m_Multimap`
+  // before.
+  void InsertIntoMultimap(uint32_t code, uint32_t destcode);
+
+  std::map<uint32_t, std::set<uint32_t>> m_Multimap;
   UnownedPtr<const CPDF_CID2UnicodeMap> m_pBaseMap;
-  CFX_WideTextBuf m_MultiCharBuf;
+  std::vector<WideString> m_MultiCharVec;
 };
 
 #endif  // CORE_FPDFAPI_FONT_CPDF_TOUNICODEMAP_H_
diff --git a/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp b/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp
index 7dacc5a..7a3015d 100644
--- a/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp
+++ b/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp
@@ -1,11 +1,14 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/font/cpdf_tounicodemap.h"
 
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/containers/span.h"
 
 TEST(cpdf_tounicodemap, StringToCode) {
   EXPECT_THAT(CPDF_ToUnicodeMap::StringToCode("<0001>"), testing::Optional(1u));
@@ -16,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());
@@ -47,3 +58,70 @@
   EXPECT_EQ(res, CPDF_ToUnicodeMap::StringToWideString("<c2abFaAb>"));
   EXPECT_EQ(res, CPDF_ToUnicodeMap::StringToWideString("<c2abFaAb12>"));
 }
+
+TEST(cpdf_tounicodemap, HandleBeginBFRangeAvoidIntegerOverflow) {
+  // Make sure there won't be infinite loops due to integer overflows in
+  // HandleBeginBFRange().
+  {
+    static constexpr uint8_t kInput1[] =
+        "beginbfrange<FFFFFFFF><FFFFFFFF>[<0041>]endbfrange";
+    auto stream = pdfium::MakeRetain<CPDF_Stream>();
+    stream->SetData(pdfium::make_span(kInput1));
+    CPDF_ToUnicodeMap map(stream);
+    EXPECT_STREQ(L"A", map.Lookup(0xffffffff).c_str());
+  }
+  {
+    static constexpr uint8_t kInput2[] =
+        "beginbfrange<FFFFFFFF><FFFFFFFF><0042>endbfrange";
+    auto stream = pdfium::MakeRetain<CPDF_Stream>();
+    stream->SetData(pdfium::make_span(kInput2));
+    CPDF_ToUnicodeMap map(stream);
+    EXPECT_STREQ(L"B", map.Lookup(0xffffffff).c_str());
+  }
+  {
+    static constexpr uint8_t kInput3[] =
+        "beginbfrange<FFFFFFFF><FFFFFFFF><00410042>endbfrange";
+    auto stream = pdfium::MakeRetain<CPDF_Stream>();
+    stream->SetData(pdfium::make_span(kInput3));
+    CPDF_ToUnicodeMap map(stream);
+    EXPECT_STREQ(L"AB", map.Lookup(0xffffffff).c_str());
+  }
+}
+
+TEST(cpdf_tounicodemap, InsertIntoMultimap) {
+  {
+    // Both the CIDs and the unicodes are different.
+    static constexpr uint8_t kInput1[] =
+        "beginbfchar<1><0041><2><0042>endbfchar";
+    auto stream = pdfium::MakeRetain<CPDF_Stream>();
+    stream->SetData(pdfium::make_span(kInput1));
+    CPDF_ToUnicodeMap map(stream);
+    EXPECT_EQ(1u, map.ReverseLookup(0x0041));
+    EXPECT_EQ(2u, map.ReverseLookup(0x0042));
+    EXPECT_EQ(1u, map.GetUnicodeCountByCharcodeForTesting(1u));
+    EXPECT_EQ(1u, map.GetUnicodeCountByCharcodeForTesting(2u));
+  }
+  {
+    // The same CID with different unicodes.
+    static constexpr uint8_t kInput2[] =
+        "beginbfrange<0><0><0041><0><0><0042>endbfrange";
+    auto stream = pdfium::MakeRetain<CPDF_Stream>();
+    stream->SetData(pdfium::make_span(kInput2));
+    CPDF_ToUnicodeMap map(stream);
+    EXPECT_EQ(0u, map.ReverseLookup(0x0041));
+    EXPECT_EQ(0u, map.ReverseLookup(0x0042));
+    EXPECT_EQ(2u, map.GetUnicodeCountByCharcodeForTesting(0u));
+  }
+  {
+    // Duplicate mappings of CID 0 to unicode "A". There should be only 1 entry
+    // in `m_Multimap`.
+    static constexpr uint8_t kInput3[] =
+        "beginbfrange<0><0>[<0041>]endbfrange\n"
+        "beginbfchar<0><0041>endbfchar";
+    auto stream = pdfium::MakeRetain<CPDF_Stream>();
+    stream->SetData(pdfium::make_span(kInput3));
+    CPDF_ToUnicodeMap map(stream);
+    EXPECT_EQ(0u, map.ReverseLookup(0x0041));
+    EXPECT_EQ(1u, map.GetUnicodeCountByCharcodeForTesting(0u));
+  }
+}
diff --git a/core/fpdfapi/font/cpdf_truetypefont.cpp b/core/fpdfapi/font/cpdf_truetypefont.cpp
index d107ed0..f19ace9 100644
--- a/core/fpdfapi/font/cpdf_truetypefont.cpp
+++ b/core/fpdfapi/font/cpdf_truetypefont.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,18 +6,36 @@
 
 #include "core/fpdfapi/font/cpdf_truetypefont.h"
 
+#include <algorithm>
+#include <utility>
+
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fxge/fx_font.h"
 
 namespace {
 
-const uint8_t kPrefix[4] = {0x00, 0xf0, 0xf1, 0xf2};
+constexpr uint8_t kPrefix[4] = {0x00, 0xf0, 0xf1, 0xf2};
+
+uint16_t GetGlyphIndexForMSSymbol(FXFT_FaceRec* face, uint32_t charcode) {
+  for (uint8_t c : kPrefix) {
+    uint16_t unicode = c * 256 + charcode;
+    uint16_t val = FT_Get_Char_Index(face, unicode);
+    if (val)
+      return val;
+  }
+  return 0;
+}
+
+bool IsWinAnsiOrMacRomanEncoding(FontEncoding encoding) {
+  return encoding == FontEncoding::kWinAnsi ||
+         encoding == FontEncoding::kMacRoman;
+}
 
 }  // namespace
 
 CPDF_TrueTypeFont::CPDF_TrueTypeFont(CPDF_Document* pDocument,
-                                     CPDF_Dictionary* pFontDict)
-    : CPDF_SimpleFont(pDocument, pFontDict) {}
+                                     RetainPtr<CPDF_Dictionary> pFontDict)
+    : CPDF_SimpleFont(pDocument, std::move(pFontDict)) {}
 
 CPDF_TrueTypeFont::~CPDF_TrueTypeFont() = default;
 
@@ -42,86 +60,37 @@
   if (!face)
     return;
 
-  int baseEncoding = m_BaseEncoding;
-  if (m_pFontFile && face->num_charmaps > 0 &&
-      (baseEncoding == PDFFONT_ENCODING_MACROMAN ||
-       baseEncoding == PDFFONT_ENCODING_WINANSI) &&
-      FontStyleIsSymbolic(m_Flags)) {
-    bool bSupportWin = false;
-    bool bSupportMac = false;
-    for (int i = 0; i < FXFT_Get_Face_CharmapCount(face); i++) {
-      int platform_id =
-          FXFT_Get_Charmap_PlatformID(FXFT_Get_Face_Charmaps(face)[i]);
-      if (platform_id == 0 || platform_id == 3) {
-        bSupportWin = true;
-      } else if (platform_id == 0 || platform_id == 1) {
-        bSupportMac = true;
-      }
-    }
-    if (baseEncoding == PDFFONT_ENCODING_WINANSI && !bSupportWin) {
-      baseEncoding =
-          bSupportMac ? PDFFONT_ENCODING_MACROMAN : PDFFONT_ENCODING_BUILTIN;
-    } else if (baseEncoding == PDFFONT_ENCODING_MACROMAN && !bSupportMac) {
-      baseEncoding =
-          bSupportWin ? PDFFONT_ENCODING_WINANSI : PDFFONT_ENCODING_BUILTIN;
-    }
-  }
-  if (((baseEncoding == PDFFONT_ENCODING_MACROMAN ||
-        baseEncoding == PDFFONT_ENCODING_WINANSI) &&
-       m_CharNames.empty()) ||
+  const FontEncoding base_encoding = DetermineEncoding();
+  if ((IsWinAnsiOrMacRomanEncoding(base_encoding) && m_CharNames.empty()) ||
       FontStyleIsNonSymbolic(m_Flags)) {
     if (!FXFT_Has_Glyph_Names(face) &&
         (!face->num_charmaps || !face->charmaps)) {
-      int nStartChar = m_pFontDict->GetIntegerFor("FirstChar");
-      if (nStartChar < 0 || nStartChar > 255)
-        return;
-
-      int charcode = 0;
-      for (; charcode < nStartChar; charcode++)
-        m_GlyphIndex[charcode] = 0;
-      uint16_t nGlyph = charcode - nStartChar + 3;
-      for (; charcode < 256; charcode++, nGlyph++)
-        m_GlyphIndex[charcode] = nGlyph;
+      SetGlyphIndicesFromFirstChar();
       return;
     }
-    bool bMSUnicode = FT_UseTTCharmap(face, 3, 1);
-    bool bMacRoman = false;
-    bool bMSSymbol = false;
-    if (!bMSUnicode) {
-      if (FontStyleIsNonSymbolic(m_Flags)) {
-        bMacRoman = FT_UseTTCharmap(face, 1, 0);
-        bMSSymbol = !bMacRoman && FT_UseTTCharmap(face, 3, 0);
-      } else {
-        bMSSymbol = FT_UseTTCharmap(face, 3, 0);
-        bMacRoman = !bMSSymbol && FT_UseTTCharmap(face, 1, 0);
-      }
-    }
+
+    const CharmapType charmap_type = DetermineCharmapType();
     bool bToUnicode = m_pFontDict->KeyExist("ToUnicode");
     for (uint32_t charcode = 0; charcode < 256; charcode++) {
-      const char* name = GetAdobeCharName(baseEncoding, m_CharNames, charcode);
+      const char* name = GetAdobeCharName(base_encoding, m_CharNames, charcode);
       if (!name) {
         m_GlyphIndex[charcode] =
             m_pFontFile ? FT_Get_Char_Index(face, charcode) : -1;
         continue;
       }
-      m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name));
-      if (bMSSymbol) {
-        for (size_t j = 0; j < FX_ArraySize(kPrefix); j++) {
-          uint16_t unicode = kPrefix[j] * 256 + charcode;
-          m_GlyphIndex[charcode] = FT_Get_Char_Index(face, unicode);
-          if (m_GlyphIndex[charcode])
-            break;
-        }
+      m_Encoding.SetUnicode(charcode, UnicodeFromAdobeName(name));
+      if (charmap_type == CharmapType::kMSSymbol) {
+        m_GlyphIndex[charcode] = GetGlyphIndexForMSSymbol(face, charcode);
       } else if (m_Encoding.UnicodeFromCharCode(charcode)) {
-        if (bMSUnicode) {
+        if (charmap_type == CharmapType::kMSUnicode) {
           m_GlyphIndex[charcode] =
               FT_Get_Char_Index(face, m_Encoding.UnicodeFromCharCode(charcode));
-        } else if (bMacRoman) {
-          uint32_t maccode =
-              FT_CharCodeFromUnicode(FT_ENCODING_APPLE_ROMAN,
-                                     m_Encoding.UnicodeFromCharCode(charcode));
+        } else if (charmap_type == CharmapType::kMacRoman) {
+          uint32_t maccode = CharCodeFromUnicodeForFreetypeEncoding(
+              FT_ENCODING_APPLE_ROMAN,
+              m_Encoding.UnicodeFromCharCode(charcode));
           if (!maccode) {
-            m_GlyphIndex[charcode] = FXFT_Get_Name_Index(face, name);
+            m_GlyphIndex[charcode] = FT_Get_Name_Index(face, name);
           } else {
             m_GlyphIndex[charcode] = FT_Get_Char_Index(face, maccode);
           }
@@ -135,7 +104,7 @@
         m_GlyphIndex[charcode] = FT_Get_Char_Index(face, 32);
         continue;
       }
-      m_GlyphIndex[charcode] = FXFT_Get_Name_Index(face, name);
+      m_GlyphIndex[charcode] = FT_Get_Name_Index(face, name);
       if (m_GlyphIndex[charcode] != 0 || !bToUnicode)
         continue;
 
@@ -147,70 +116,123 @@
     }
     return;
   }
-  if (FT_UseTTCharmap(face, 3, 0)) {
-    bool bFound = false;
-    for (int charcode = 0; charcode < 256; charcode++) {
-      for (size_t j = 0; j < FX_ArraySize(kPrefix); j++) {
-        uint16_t unicode = kPrefix[j] * 256 + charcode;
-        m_GlyphIndex[charcode] = FT_Get_Char_Index(face, unicode);
-        if (m_GlyphIndex[charcode]) {
-          bFound = true;
-          break;
-        }
-      }
-    }
-    if (bFound) {
-      if (baseEncoding != PDFFONT_ENCODING_BUILTIN) {
+  if (UseTTCharmapMSSymbol(face)) {
+    for (uint32_t charcode = 0; charcode < 256; charcode++)
+      m_GlyphIndex[charcode] = GetGlyphIndexForMSSymbol(face, charcode);
+    if (HasAnyGlyphIndex()) {
+      if (base_encoding != FontEncoding::kBuiltin) {
         for (uint32_t charcode = 0; charcode < 256; charcode++) {
           const char* name =
-              GetAdobeCharName(baseEncoding, m_CharNames, charcode);
+              GetAdobeCharName(base_encoding, m_CharNames, charcode);
           if (name)
-            m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name));
+            m_Encoding.SetUnicode(charcode, UnicodeFromAdobeName(name));
         }
-      } else if (FT_UseTTCharmap(face, 1, 0)) {
-        for (int charcode = 0; charcode < 256; charcode++) {
-          m_Encoding.SetUnicode(
-              charcode,
-              FT_UnicodeFromCharCode(FT_ENCODING_APPLE_ROMAN, charcode));
+      } else if (UseTTCharmapMacRoman(face)) {
+        for (uint32_t charcode = 0; charcode < 256; charcode++) {
+          m_Encoding.SetUnicode(charcode,
+                                UnicodeFromAppleRomanCharCode(charcode));
         }
       }
       return;
     }
   }
-  if (FT_UseTTCharmap(face, 1, 0)) {
-    bool bFound = false;
-    for (int charcode = 0; charcode < 256; charcode++) {
+  if (UseTTCharmapMacRoman(face)) {
+    for (uint32_t charcode = 0; charcode < 256; charcode++) {
       m_GlyphIndex[charcode] = FT_Get_Char_Index(face, charcode);
-      m_Encoding.SetUnicode(
-          charcode, FT_UnicodeFromCharCode(FT_ENCODING_APPLE_ROMAN, charcode));
-      if (m_GlyphIndex[charcode]) {
-        bFound = true;
-      }
+      m_Encoding.SetUnicode(charcode, UnicodeFromAppleRomanCharCode(charcode));
     }
-    if (m_pFontFile || bFound)
+    if (m_pFontFile || HasAnyGlyphIndex())
       return;
   }
   if (FXFT_Select_Charmap(face, FT_ENCODING_UNICODE) == 0) {
-    bool bFound = false;
-    const uint16_t* pUnicodes = PDF_UnicodesForPredefinedCharSet(baseEncoding);
+    const uint16_t* pUnicodes = UnicodesForPredefinedCharSet(base_encoding);
     for (uint32_t charcode = 0; charcode < 256; charcode++) {
       if (m_pFontFile) {
         m_Encoding.SetUnicode(charcode, charcode);
       } else {
-        const char* name = GetAdobeCharName(0, m_CharNames, charcode);
+        const char* name =
+            GetAdobeCharName(FontEncoding::kBuiltin, m_CharNames, charcode);
         if (name)
-          m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name));
+          m_Encoding.SetUnicode(charcode, UnicodeFromAdobeName(name));
         else if (pUnicodes)
           m_Encoding.SetUnicode(charcode, pUnicodes[charcode]);
       }
       m_GlyphIndex[charcode] =
           FT_Get_Char_Index(face, m_Encoding.UnicodeFromCharCode(charcode));
-      if (m_GlyphIndex[charcode])
-        bFound = true;
     }
-    if (bFound)
+    if (HasAnyGlyphIndex())
       return;
   }
   for (int charcode = 0; charcode < 256; charcode++)
     m_GlyphIndex[charcode] = charcode;
 }
+
+bool CPDF_TrueTypeFont::HasAnyGlyphIndex() const {
+  for (uint32_t charcode = 0; charcode < kInternalTableSize; charcode++) {
+    if (m_GlyphIndex[charcode])
+      return true;
+  }
+  return false;
+}
+
+CPDF_TrueTypeFont::CharmapType CPDF_TrueTypeFont::DetermineCharmapType() const {
+  if (UseTTCharmapMSUnicode(m_Font.GetFaceRec()))
+    return CharmapType::kMSUnicode;
+
+  if (FontStyleIsNonSymbolic(m_Flags)) {
+    if (UseTTCharmapMacRoman(m_Font.GetFaceRec()))
+      return CharmapType::kMacRoman;
+    if (UseTTCharmapMSSymbol(m_Font.GetFaceRec()))
+      return CharmapType::kMSSymbol;
+  } else {
+    if (UseTTCharmapMSSymbol(m_Font.GetFaceRec()))
+      return CharmapType::kMSSymbol;
+    if (UseTTCharmapMacRoman(m_Font.GetFaceRec()))
+      return CharmapType::kMacRoman;
+  }
+  return CharmapType::kOther;
+}
+
+FontEncoding CPDF_TrueTypeFont::DetermineEncoding() const {
+  if (!m_pFontFile || !FontStyleIsSymbolic(m_Flags) ||
+      !IsWinAnsiOrMacRomanEncoding(m_BaseEncoding)) {
+    return m_BaseEncoding;
+  }
+
+  // Not null - caller checked.
+  FXFT_FaceRec* face = m_Font.GetFaceRec();
+  if (face->num_charmaps <= 0)
+    return m_BaseEncoding;
+
+  bool support_win = false;
+  bool support_mac = false;
+  for (int i = 0; i < face->num_charmaps; i++) {
+    int platform_id = FXFT_Get_Charmap_PlatformID(face->charmaps[i]);
+    if (platform_id == kNamePlatformAppleUnicode ||
+        platform_id == kNamePlatformWindows) {
+      support_win = true;
+    } else if (platform_id == kNamePlatformMac) {
+      support_mac = true;
+    }
+    if (support_win && support_mac)
+      break;
+  }
+
+  if (m_BaseEncoding == FontEncoding::kWinAnsi && !support_win)
+    return support_mac ? FontEncoding::kMacRoman : FontEncoding::kBuiltin;
+  if (m_BaseEncoding == FontEncoding::kMacRoman && !support_mac)
+    return support_win ? FontEncoding::kWinAnsi : FontEncoding::kBuiltin;
+  return m_BaseEncoding;
+}
+
+void CPDF_TrueTypeFont::SetGlyphIndicesFromFirstChar() {
+  int start_char = m_pFontDict->GetIntegerFor("FirstChar");
+  if (start_char < 0 || start_char > 255)
+    return;
+
+  auto* it = std::begin(m_GlyphIndex);
+  std::fill(it, it + start_char, 0);
+  uint16_t glyph = 3;
+  for (int charcode = start_char; charcode < 256; charcode++, glyph++)
+    m_GlyphIndex[charcode] = glyph;
+}
diff --git a/core/fpdfapi/font/cpdf_truetypefont.h b/core/fpdfapi/font/cpdf_truetypefont.h
index caa1625..cc87073 100644
--- a/core/fpdfapi/font/cpdf_truetypefont.h
+++ b/core/fpdfapi/font/cpdf_truetypefont.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,13 +8,11 @@
 #define CORE_FPDFAPI_FONT_CPDF_TRUETYPEFONT_H_
 
 #include "core/fpdfapi/font/cpdf_simplefont.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_TrueTypeFont final : public CPDF_SimpleFont {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
+  CONSTRUCT_VIA_MAKE_RETAIN;
   ~CPDF_TrueTypeFont() override;
 
   // CPDF_Font:
@@ -23,13 +21,21 @@
   CPDF_TrueTypeFont* AsTrueTypeFont() override;
 
  private:
-  CPDF_TrueTypeFont(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict);
+  enum class CharmapType { kMSUnicode, kMSSymbol, kMacRoman, kOther };
+
+  CPDF_TrueTypeFont(CPDF_Document* pDocument,
+                    RetainPtr<CPDF_Dictionary> pFontDict);
 
   // CPDF_Font:
   bool Load() override;
 
   // CPDF_SimpleFont:
   void LoadGlyphMap() override;
+
+  bool HasAnyGlyphIndex() const;
+  CharmapType DetermineCharmapType() const;
+  FontEncoding DetermineEncoding() const;
+  void SetGlyphIndicesFromFirstChar();
 };
 
 #endif  // CORE_FPDFAPI_FONT_CPDF_TRUETYPEFONT_H_
diff --git a/core/fpdfapi/font/cpdf_type1font.cpp b/core/fpdfapi/font/cpdf_type1font.cpp
index 04dd7c2..55510e7 100644
--- a/core/fpdfapi/font/cpdf_type1font.cpp
+++ b/core/fpdfapi/font/cpdf_type1font.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,66 +7,67 @@
 #include "core/fpdfapi/font/cpdf_type1font.h"
 
 #include <algorithm>
+#include <iterator>
+#include <utility>
 
 #include "build/build_config.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/span_util.h"
 #include "core/fxge/cfx_fontmapper.h"
 #include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/freetype/fx_freetype.h"
 #include "core/fxge/fx_font.h"
-#include "core/fxge/fx_freetype.h"
 
-#if defined(OS_MACOSX)
-#include "core/fxge/apple/apple_int.h"
-#endif
+#if BUILDFLAG(IS_APPLE)
+#include <CoreFoundation/CFString.h>
+#include <CoreGraphics/CoreGraphics.h>
+#endif  // BUILDFLAG(IS_APPLE)
 
 namespace {
 
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
 struct GlyphNameMap {
   const char* m_pStrAdobe;    // Raw, POD struct.
   const char* m_pStrUnicode;  // Raw, POD struct.
 };
 
-const GlyphNameMap g_GlyphNameSubsts[] = {{"ff", "uniFB00"},
-                                          {"ffi", "uniFB03"},
-                                          {"ffl", "uniFB04"},
-                                          {"fi", "uniFB01"},
-                                          {"fl", "uniFB02"}};
+const GlyphNameMap kGlyphNameSubsts[] = {{"ff", "uniFB00"},
+                                         {"ffi", "uniFB03"},
+                                         {"ffl", "uniFB04"},
+                                         {"fi", "uniFB01"},
+                                         {"fl", "uniFB02"}};
 
 const char* GlyphNameRemap(const char* pStrAdobe) {
-  for (const auto& element : g_GlyphNameSubsts) {
+  for (const auto& element : kGlyphNameSubsts) {
     if (!FXSYS_stricmp(element.m_pStrAdobe, pStrAdobe))
       return element.m_pStrUnicode;
   }
   return nullptr;
 }
 
-#endif  // defined(OS_MACOSX)
+#endif  // BUILDFLAG(IS_APPLE)
 
 bool FT_UseType1Charmap(FXFT_FaceRec* face) {
-  if (FXFT_Get_Face_CharmapCount(face) == 0) {
+  if (face->num_charmaps == 0)
     return false;
-  }
-  if (FXFT_Get_Face_CharmapCount(face) == 1 &&
-      FXFT_Get_Charmap_Encoding(FXFT_Get_Face_Charmaps(face)[0]) ==
-          FT_ENCODING_UNICODE) {
+
+  bool is_first_charmap_unicode =
+      FXFT_Get_Charmap_Encoding(face->charmaps[0]) == FT_ENCODING_UNICODE;
+  if (face->num_charmaps == 1 && is_first_charmap_unicode)
     return false;
-  }
-  if (FXFT_Get_Charmap_Encoding(FXFT_Get_Face_Charmaps(face)[0]) ==
-      FT_ENCODING_UNICODE) {
-    FT_Set_Charmap(face, FXFT_Get_Face_Charmaps(face)[1]);
-  } else {
-    FT_Set_Charmap(face, FXFT_Get_Face_Charmaps(face)[0]);
-  }
+
+  int index = is_first_charmap_unicode ? 1 : 0;
+  FT_Set_Charmap(face, face->charmaps[index]);
   return true;
 }
 
 }  // namespace
 
 CPDF_Type1Font::CPDF_Type1Font(CPDF_Document* pDocument,
-                               CPDF_Dictionary* pFontDict)
-    : CPDF_SimpleFont(pDocument, pFontDict) {
-#if defined(OS_MACOSX)
+                               RetainPtr<CPDF_Dictionary> pFontDict)
+    : CPDF_SimpleFont(pDocument, std::move(pFontDict)) {
+#if BUILDFLAG(IS_APPLE)
   memset(m_ExtGID, 0xff, sizeof(m_ExtGID));
 #endif
 }
@@ -90,7 +91,8 @@
   if (!IsBase14Font())
     return LoadCommon();
 
-  const CPDF_Dictionary* pFontDesc = m_pFontDict->GetDictFor("FontDescriptor");
+  RetainPtr<const CPDF_Dictionary> pFontDesc =
+      m_pFontDict->GetDictFor("FontDescriptor");
   if (pFontDesc && pFontDesc->KeyExist("Flags")) {
     m_Flags = pFontDesc->GetIntegerFor("Flags");
   } else if (IsSymbolicFont()) {
@@ -99,19 +101,18 @@
     m_Flags = FXFONT_NONSYMBOLIC;
   }
   if (IsFixedFont()) {
-    for (int i = 0; i < 256; i++)
-      m_CharWidth[i] = 600;
+    std::fill(std::begin(m_CharWidth), std::end(m_CharWidth), 600);
   }
   if (m_Base14Font == CFX_FontMapper::kSymbol)
-    m_BaseEncoding = PDFFONT_ENCODING_ADOBE_SYMBOL;
+    m_BaseEncoding = FontEncoding::kAdobeSymbol;
   else if (m_Base14Font == CFX_FontMapper::kDingbats)
-    m_BaseEncoding = PDFFONT_ENCODING_ZAPFDINGBATS;
+    m_BaseEncoding = FontEncoding::kZapfDingbats;
   else if (FontStyleIsNonSymbolic(m_Flags))
-    m_BaseEncoding = PDFFONT_ENCODING_STANDARD;
+    m_BaseEncoding = FontEncoding::kStandard;
   return LoadCommon();
 }
 
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
 int CPDF_Type1Font::GlyphFromCharCodeExt(uint32_t charcode) {
   if (charcode > 0xff)
     return -1;
@@ -125,31 +126,29 @@
   if (!m_Font.GetFaceRec())
     return;
 
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
   bool bCoreText = true;
-  CQuartz2D& quartz2d =
-      static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatform())
-          ->m_quartz2d;
   if (!m_Font.GetPlatformFont()) {
     if (m_Font.GetPsName() == "DFHeiStd-W5")
       bCoreText = false;
 
+    auto* pPlatform = CFX_GEModule::Get()->GetPlatform();
     pdfium::span<const uint8_t> span = m_Font.GetFontSpan();
-    m_Font.SetPlatformFont(quartz2d.CreateFont(span.data(), span.size()));
+    m_Font.SetPlatformFont(pPlatform->CreatePlatformFont(span));
     if (!m_Font.GetPlatformFont())
       bCoreText = false;
   }
 #endif
   if (!IsEmbedded() && !IsSymbolicFont() && m_Font.IsTTFont()) {
-    if (FT_UseTTCharmap(m_Font.GetFaceRec(), 3, 0)) {
+    if (UseTTCharmapMSSymbol(m_Font.GetFaceRec())) {
       bool bGotOne = false;
-      for (uint32_t charcode = 0; charcode < 256; charcode++) {
+      for (uint32_t charcode = 0; charcode < kInternalTableSize; charcode++) {
         const uint8_t prefix[4] = {0x00, 0xf0, 0xf1, 0xf2};
         for (int j = 0; j < 4; j++) {
           uint16_t unicode = prefix[j] * 256 + charcode;
           m_GlyphIndex[charcode] =
               FT_Get_Char_Index(m_Font.GetFaceRec(), unicode);
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
           CalcExtGID(charcode);
 #endif
           if (m_GlyphIndex[charcode]) {
@@ -159,71 +158,65 @@
         }
       }
       if (bGotOne) {
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
         if (!bCoreText)
-          memcpy(m_ExtGID, m_GlyphIndex, 256);
+          memcpy(m_ExtGID, m_GlyphIndex, sizeof(m_ExtGID));
 #endif
         return;
       }
     }
     FXFT_Select_Charmap(m_Font.GetFaceRec(), FT_ENCODING_UNICODE);
-    if (m_BaseEncoding == 0)
-      m_BaseEncoding = PDFFONT_ENCODING_STANDARD;
+    if (m_BaseEncoding == FontEncoding::kBuiltin)
+      m_BaseEncoding = FontEncoding::kStandard;
 
-    for (uint32_t charcode = 0; charcode < 256; charcode++) {
+    for (uint32_t charcode = 0; charcode < kInternalTableSize; charcode++) {
       const char* name =
           GetAdobeCharName(m_BaseEncoding, m_CharNames, charcode);
       if (!name)
         continue;
 
-      m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name));
+      m_Encoding.SetUnicode(charcode, UnicodeFromAdobeName(name));
       m_GlyphIndex[charcode] = FT_Get_Char_Index(
           m_Font.GetFaceRec(), m_Encoding.UnicodeFromCharCode(charcode));
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
       CalcExtGID(charcode);
 #endif
       if (m_GlyphIndex[charcode] == 0 && strcmp(name, ".notdef") == 0) {
         m_Encoding.SetUnicode(charcode, 0x20);
         m_GlyphIndex[charcode] = FT_Get_Char_Index(m_Font.GetFaceRec(), 0x20);
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
         CalcExtGID(charcode);
 #endif
       }
     }
-#if defined(OS_MACOSX)
-    if (!bCoreText)
-      memcpy(m_ExtGID, m_GlyphIndex, 256);
+#if BUILDFLAG(IS_APPLE)
+    if (!bCoreText) {
+      fxcrt::spancpy(pdfium::make_span(m_ExtGID),
+                     pdfium::make_span(m_GlyphIndex));
+    }
 #endif
     return;
   }
   FT_UseType1Charmap(m_Font.GetFaceRec());
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
   if (bCoreText) {
     if (FontStyleIsSymbolic(m_Flags)) {
-      for (uint32_t charcode = 0; charcode < 256; charcode++) {
+      for (uint32_t charcode = 0; charcode < kInternalTableSize; charcode++) {
         const char* name =
             GetAdobeCharName(m_BaseEncoding, m_CharNames, charcode);
         if (name) {
-          m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name));
-          m_GlyphIndex[charcode] =
-              FXFT_Get_Name_Index(m_Font.GetFaceRec(), name);
+          m_Encoding.SetUnicode(charcode, UnicodeFromAdobeName(name));
+          m_GlyphIndex[charcode] = FT_Get_Name_Index(m_Font.GetFaceRec(), name);
           SetExtGID(name, charcode);
         } else {
           m_GlyphIndex[charcode] =
               FT_Get_Char_Index(m_Font.GetFaceRec(), charcode);
-          wchar_t unicode = 0;
-          if (m_GlyphIndex[charcode]) {
-            unicode =
-                FT_UnicodeFromCharCode(PDFFONT_ENCODING_STANDARD, charcode);
-          }
-          char name_glyph[256];
-          memset(name_glyph, 0, sizeof(name_glyph));
+          char name_glyph[kInternalTableSize] = {};
           FT_Get_Glyph_Name(m_Font.GetFaceRec(), m_GlyphIndex[charcode],
-                            name_glyph, 256);
-          name_glyph[255] = 0;
-          if (unicode == 0 && name_glyph[0] != 0)
-            unicode = PDF_UnicodeFromAdobeName(name_glyph);
-
+                            name_glyph, sizeof(name_glyph));
+          name_glyph[kInternalTableSize - 1] = 0;
+          const wchar_t unicode =
+              name_glyph[0] != 0 ? UnicodeFromAdobeName(name_glyph) : 0;
           m_Encoding.SetUnicode(charcode, unicode);
           SetExtGID(name_glyph, charcode);
         }
@@ -233,18 +226,18 @@
 
     bool bUnicode =
         FXFT_Select_Charmap(m_Font.GetFaceRec(), FT_ENCODING_UNICODE) == 0;
-    for (uint32_t charcode = 0; charcode < 256; charcode++) {
+    for (uint32_t charcode = 0; charcode < kInternalTableSize; charcode++) {
       const char* name =
           GetAdobeCharName(m_BaseEncoding, m_CharNames, charcode);
       if (!name)
         continue;
 
-      m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name));
+      m_Encoding.SetUnicode(charcode, UnicodeFromAdobeName(name));
       const char* pStrUnicode = GlyphNameRemap(name);
-      if (pStrUnicode && FXFT_Get_Name_Index(m_Font.GetFaceRec(), name) == 0) {
+      if (pStrUnicode && FT_Get_Name_Index(m_Font.GetFaceRec(), name) == 0) {
         name = pStrUnicode;
       }
-      m_GlyphIndex[charcode] = FXFT_Get_Name_Index(m_Font.GetFaceRec(), name);
+      m_GlyphIndex[charcode] = FT_Get_Name_Index(m_Font.GetFaceRec(), name);
       SetExtGID(name, charcode);
       if (m_GlyphIndex[charcode] != 0)
         continue;
@@ -263,65 +256,61 @@
     }
     return;
   }
-#endif  // defined(OS_MACOSX)
+#endif  // BUILDFLAG(IS_APPLE)
   if (FontStyleIsSymbolic(m_Flags)) {
-    for (int charcode = 0; charcode < 256; charcode++) {
-      const char* name =
-          GetAdobeCharName(m_BaseEncoding, m_CharNames, charcode);
+    for (size_t charcode = 0; charcode < kInternalTableSize; charcode++) {
+      const char* name = GetAdobeCharName(m_BaseEncoding, m_CharNames,
+                                          static_cast<uint32_t>(charcode));
       if (name) {
-        m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name));
-        m_GlyphIndex[charcode] = FXFT_Get_Name_Index(m_Font.GetFaceRec(), name);
+        m_Encoding.SetUnicode(charcode, UnicodeFromAdobeName(name));
+        m_GlyphIndex[charcode] = FT_Get_Name_Index(m_Font.GetFaceRec(), name);
       } else {
-        m_GlyphIndex[charcode] =
-            FT_Get_Char_Index(m_Font.GetFaceRec(), charcode);
+        m_GlyphIndex[charcode] = FT_Get_Char_Index(
+            m_Font.GetFaceRec(), static_cast<uint32_t>(charcode));
         if (m_GlyphIndex[charcode]) {
-          wchar_t unicode =
-              FT_UnicodeFromCharCode(PDFFONT_ENCODING_STANDARD, charcode);
-          if (unicode == 0) {
-            char name_glyph[256];
-            memset(name_glyph, 0, sizeof(name_glyph));
-            FT_Get_Glyph_Name(m_Font.GetFaceRec(), m_GlyphIndex[charcode],
-                              name_glyph, 256);
-            name_glyph[255] = 0;
-            if (name_glyph[0] != 0)
-              unicode = PDF_UnicodeFromAdobeName(name_glyph);
-          }
+          char name_glyph[kInternalTableSize] = {};
+          FT_Get_Glyph_Name(m_Font.GetFaceRec(), m_GlyphIndex[charcode],
+                            name_glyph, sizeof(name_glyph));
+          name_glyph[kInternalTableSize - 1] = 0;
+          const wchar_t unicode =
+              name_glyph[0] != 0 ? UnicodeFromAdobeName(name_glyph) : 0;
           m_Encoding.SetUnicode(charcode, unicode);
         }
       }
     }
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
     if (!bCoreText)
-      memcpy(m_ExtGID, m_GlyphIndex, 256);
-
+      memcpy(m_ExtGID, m_GlyphIndex, sizeof(m_ExtGID));
 #endif
     return;
   }
 
   bool bUnicode =
       FXFT_Select_Charmap(m_Font.GetFaceRec(), FT_ENCODING_UNICODE) == 0;
-  for (int charcode = 0; charcode < 256; charcode++) {
-    const char* name = GetAdobeCharName(m_BaseEncoding, m_CharNames, charcode);
+  for (size_t charcode = 0; charcode < kInternalTableSize; charcode++) {
+    const char* name = GetAdobeCharName(m_BaseEncoding, m_CharNames,
+                                        static_cast<uint32_t>(charcode));
     if (!name)
       continue;
 
-    m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name));
-    m_GlyphIndex[charcode] = FXFT_Get_Name_Index(m_Font.GetFaceRec(), name);
+    m_Encoding.SetUnicode(charcode, UnicodeFromAdobeName(name));
+    m_GlyphIndex[charcode] = FT_Get_Name_Index(m_Font.GetFaceRec(), name);
     if (m_GlyphIndex[charcode] != 0)
       continue;
 
     if (strcmp(name, ".notdef") != 0 && strcmp(name, "space") != 0) {
-      m_GlyphIndex[charcode] = FT_Get_Char_Index(
-          m_Font.GetFaceRec(),
-          bUnicode ? m_Encoding.UnicodeFromCharCode(charcode) : charcode);
+      m_GlyphIndex[charcode] =
+          FT_Get_Char_Index(m_Font.GetFaceRec(),
+                            bUnicode ? m_Encoding.UnicodeFromCharCode(charcode)
+                                     : static_cast<uint32_t>(charcode));
     } else {
       m_Encoding.SetUnicode(charcode, 0x20);
       m_GlyphIndex[charcode] = 0xffff;
     }
   }
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
   if (!bCoreText)
-    memcpy(m_ExtGID, m_GlyphIndex, 256);
+    memcpy(m_ExtGID, m_GlyphIndex, sizeof(m_ExtGID));
 #endif
 }
 
@@ -335,7 +324,7 @@
          CFX_FontMapper::IsFixedFont(m_Base14Font.value());
 }
 
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
 void CPDF_Type1Font::SetExtGID(const char* name, uint32_t charcode) {
   CFStringRef name_ct = CFStringCreateWithCStringNoCopy(
       kCFAllocatorDefault, name, kCFStringEncodingASCII, kCFAllocatorNull);
@@ -346,10 +335,10 @@
 }
 
 void CPDF_Type1Font::CalcExtGID(uint32_t charcode) {
-  char name_glyph[256];
+  char name_glyph[kInternalTableSize] = {};
   FT_Get_Glyph_Name(m_Font.GetFaceRec(), m_GlyphIndex[charcode], name_glyph,
-                    256);
-  name_glyph[255] = 0;
+                    sizeof(name_glyph));
+  name_glyph[kInternalTableSize - 1] = 0;
   SetExtGID(name_glyph, charcode);
 }
-#endif  // defined(OS_MACOSX)
+#endif  // BUILDFLAG(IS_APPLE)
diff --git a/core/fpdfapi/font/cpdf_type1font.h b/core/fpdfapi/font/cpdf_type1font.h
index 79dfe31..55f542f 100644
--- a/core/fpdfapi/font/cpdf_type1font.h
+++ b/core/fpdfapi/font/cpdf_type1font.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,30 +7,31 @@
 #ifndef CORE_FPDFAPI_FONT_CPDF_TYPE1FONT_H_
 #define CORE_FPDFAPI_FONT_CPDF_TYPE1FONT_H_
 
+#include <stdint.h>
+
 #include "build/build_config.h"
 #include "core/fpdfapi/font/cpdf_simplefont.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/cfx_fontmapper.h"
 
 class CPDF_Type1Font final : public CPDF_SimpleFont {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
+  CONSTRUCT_VIA_MAKE_RETAIN;
   ~CPDF_Type1Font() override;
 
   // CPDF_Font:
   bool IsType1Font() const override;
   const CPDF_Type1Font* AsType1Font() const override;
   CPDF_Type1Font* AsType1Font() override;
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
   int GlyphFromCharCodeExt(uint32_t charcode) override;
 #endif
 
   bool IsBase14Font() const { return m_Base14Font.has_value(); }
 
  private:
-  CPDF_Type1Font(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict);
+  CPDF_Type1Font(CPDF_Document* pDocument,
+                 RetainPtr<CPDF_Dictionary> pFontDict);
 
   // CPDF_Font:
   bool Load() override;
@@ -41,14 +42,14 @@
   bool IsSymbolicFont() const;
   bool IsFixedFont() const;
 
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
   void SetExtGID(const char* name, uint32_t charcode);
   void CalcExtGID(uint32_t charcode);
 
-  uint16_t m_ExtGID[256];
+  uint16_t m_ExtGID[kInternalTableSize];
 #endif
 
-  Optional<CFX_FontMapper::StandardFont> m_Base14Font;
+  absl::optional<CFX_FontMapper::StandardFont> m_Base14Font;
 };
 
 #endif  // CORE_FPDFAPI_FONT_CPDF_TYPE1FONT_H_
diff --git a/core/fpdfapi/font/cpdf_type3char.cpp b/core/fpdfapi/font/cpdf_type3char.cpp
index 3ad9bba..ff9a579 100644
--- a/core/fpdfapi/font/cpdf_type3char.cpp
+++ b/core/fpdfapi/font/cpdf_type3char.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,9 +8,9 @@
 
 #include <utility>
 
+#include "core/fxcrt/fx_system.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/ptr_util.h"
+#include "core/fxge/dib/fx_dib.h"
 
 namespace {
 
@@ -49,7 +49,7 @@
 }
 
 void CPDF_Type3Char::InitializeFromStreamData(bool bColored,
-                                              const float* pData) {
+                                              pdfium::span<const float> pData) {
   m_bColored = bColored;
   m_Width = FXSYS_roundf(TextUnitToGlyphUnit(pData[0]));
   m_BBox.left = FXSYS_roundf(TextUnitToGlyphUnit(pData[2]));
@@ -85,7 +85,3 @@
 RetainPtr<CFX_DIBitmap> CPDF_Type3Char::GetBitmap() {
   return m_pBitmap;
 }
-
-const RetainPtr<CFX_DIBitmap>& CPDF_Type3Char::GetBitmap() const {
-  return m_pBitmap;
-}
diff --git a/core/fpdfapi/font/cpdf_type3char.h b/core/fpdfapi/font/cpdf_type3char.h
index ca83452..990fb77 100644
--- a/core/fpdfapi/font/cpdf_type3char.h
+++ b/core/fpdfapi/font/cpdf_type3char.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,9 +12,9 @@
 
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_DIBitmap;
 
@@ -27,15 +27,14 @@
   static void TextUnitRectToGlyphUnitRect(CFX_FloatRect* pRect);
 
   bool LoadBitmapFromSoleImageOfForm();
-  void InitializeFromStreamData(bool bColored, const float* pData);
+  void InitializeFromStreamData(bool bColored, pdfium::span<const float> pData);
   void Transform(CPDF_Font::FormIface* pForm, const CFX_Matrix& matrix);
   void WillBeDestroyed();
 
   RetainPtr<CFX_DIBitmap> GetBitmap();
-  const RetainPtr<CFX_DIBitmap>& GetBitmap() const;
 
   bool colored() const { return m_bColored; }
-  uint32_t width() const { return m_Width; }
+  int width() const { return m_Width; }
   const CFX_Matrix& matrix() const { return m_ImageMatrix; }
   const FX_RECT& bbox() const { return m_BBox; }
 
@@ -46,7 +45,7 @@
   std::unique_ptr<CPDF_Font::FormIface> m_pForm;
   RetainPtr<CFX_DIBitmap> m_pBitmap;
   bool m_bColored = false;
-  uint32_t m_Width = 0;
+  int m_Width = 0;
   CFX_Matrix m_ImageMatrix;
   FX_RECT m_BBox;
 };
diff --git a/core/fpdfapi/font/cpdf_type3font.cpp b/core/fpdfapi/font/cpdf_type3font.cpp
index eae08ea..866b9e1 100644
--- a/core/fpdfapi/font/cpdf_type3font.cpp
+++ b/core/fpdfapi/font/cpdf_type3font.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,8 @@
 #include "core/fpdfapi/font/cpdf_type3font.h"
 
 #include <algorithm>
+#include <iterator>
+#include <type_traits>
 #include <utility>
 
 #include "core/fpdfapi/font/cpdf_type3char.h"
@@ -15,7 +17,7 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fxcrt/autorestorer.h"
 #include "core/fxcrt/fx_system.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -24,11 +26,11 @@
 }  // namespace
 
 CPDF_Type3Font::CPDF_Type3Font(CPDF_Document* pDocument,
-                               CPDF_Dictionary* pFontDict,
+                               RetainPtr<CPDF_Dictionary> pFontDict,
                                FormFactoryIface* pFormFactory)
-    : CPDF_SimpleFont(pDocument, pFontDict), m_pFormFactory(pFormFactory) {
-  ASSERT(GetDocument());
-  memset(m_CharWidthL, 0, sizeof(m_CharWidthL));
+    : CPDF_SimpleFont(pDocument, std::move(pFontDict)),
+      m_pFormFactory(pFormFactory) {
+  DCHECK(GetDocument());
 }
 
 CPDF_Type3Font::~CPDF_Type3Font() = default;
@@ -55,8 +57,8 @@
 }
 
 bool CPDF_Type3Font::Load() {
-  m_pFontResources.Reset(m_pFontDict->GetDictFor("Resources"));
-  const CPDF_Array* pMatrix = m_pFontDict->GetArrayFor("FontMatrix");
+  m_pFontResources = m_pFontDict->GetMutableDictFor("Resources");
+  RetainPtr<const CPDF_Array> pMatrix = m_pFontDict->GetArrayFor("FontMatrix");
   float xscale = 1.0f;
   float yscale = 1.0f;
   if (pMatrix) {
@@ -65,30 +67,31 @@
     yscale = m_FontMatrix.d;
   }
 
-  const CPDF_Array* pBBox = m_pFontDict->GetArrayFor("FontBBox");
+  RetainPtr<const CPDF_Array> pBBox = m_pFontDict->GetArrayFor("FontBBox");
   if (pBBox) {
     CFX_FloatRect box(
-        pBBox->GetNumberAt(0) * xscale, pBBox->GetNumberAt(1) * yscale,
-        pBBox->GetNumberAt(2) * xscale, pBBox->GetNumberAt(3) * yscale);
+        pBBox->GetFloatAt(0) * xscale, pBBox->GetFloatAt(1) * yscale,
+        pBBox->GetFloatAt(2) * xscale, pBBox->GetFloatAt(3) * yscale);
     CPDF_Type3Char::TextUnitRectToGlyphUnitRect(&box);
     m_FontBBox = box.ToFxRect();
   }
 
-  static constexpr size_t kCharLimit = FX_ArraySize(m_CharWidthL);
+  static constexpr size_t kCharLimit = std::extent<decltype(m_CharWidthL)>();
   int StartChar = m_pFontDict->GetIntegerFor("FirstChar");
   if (StartChar >= 0 && static_cast<size_t>(StartChar) < kCharLimit) {
-    const CPDF_Array* pWidthArray = m_pFontDict->GetArrayFor("Widths");
+    RetainPtr<const CPDF_Array> pWidthArray =
+        m_pFontDict->GetArrayFor("Widths");
     if (pWidthArray) {
       size_t count = std::min(pWidthArray->size(), kCharLimit);
       count = std::min(count, kCharLimit - StartChar);
       for (size_t i = 0; i < count; i++) {
         m_CharWidthL[StartChar + i] =
             FXSYS_roundf(CPDF_Type3Char::TextUnitToGlyphUnit(
-                pWidthArray->GetNumberAt(i) * xscale));
+                pWidthArray->GetFloatAt(i) * xscale));
       }
     }
   }
-  m_pCharProcs.Reset(m_pFontDict->GetDictFor("CharProcs"));
+  m_pCharProcs = m_pFontDict->GetMutableDictFor("CharProcs");
   if (m_pFontDict->GetDirectObjectFor("Encoding"))
     LoadPDFEncoding(false, false);
   return true;
@@ -115,16 +118,16 @@
   if (!m_pCharProcs)
     return nullptr;
 
-  CPDF_Stream* pStream = ToStream(m_pCharProcs->GetDirectObjectFor(name));
+  RetainPtr<CPDF_Stream> pStream =
+      ToStream(m_pCharProcs->GetMutableDirectObjectFor(name));
   if (!pStream)
     return nullptr;
 
   std::unique_ptr<CPDF_Font::FormIface> pForm = m_pFormFactory->CreateForm(
-      m_pDocument.Get(),
-      m_pFontResources ? m_pFontResources.Get() : m_pPageResources.Get(),
+      m_pDocument, m_pFontResources ? m_pFontResources : m_pPageResources,
       pStream);
 
-  auto pNewChar = pdfium::MakeUnique<CPDF_Type3Char>();
+  auto pNewChar = std::make_unique<CPDF_Type3Char>();
 
   // This can trigger recursion into this method. The content of |m_CacheMap|
   // can change as a result. Thus after it returns, check the cache again for
@@ -147,8 +150,8 @@
   return pCachedChar;
 }
 
-uint32_t CPDF_Type3Font::GetCharWidthF(uint32_t charcode) {
-  if (charcode >= FX_ArraySize(m_CharWidthL))
+int CPDF_Type3Font::GetCharWidthF(uint32_t charcode) {
+  if (charcode >= std::size(m_CharWidthL))
     charcode = 0;
 
   if (m_CharWidthL[charcode])
diff --git a/core/fpdfapi/font/cpdf_type3font.h b/core/fpdfapi/font/cpdf_type3font.h
index 1ef469b..4f5c650 100644
--- a/core/fpdfapi/font/cpdf_type3font.h
+++ b/core/fpdfapi/font/cpdf_type3font.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,24 +7,23 @@
 #ifndef CORE_FPDFAPI_FONT_CPDF_TYPE3FONT_H_
 #define CORE_FPDFAPI_FONT_CPDF_TYPE3FONT_H_
 
+#include <stdint.h>
+
 #include <map>
 #include <memory>
 
 #include "core/fpdfapi/font/cpdf_simplefont.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_Dictionary;
 class CPDF_Document;
-class CPDF_Stream;
 class CPDF_Type3Char;
 
 class CPDF_Type3Font final : public CPDF_SimpleFont {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
+  CONSTRUCT_VIA_MAKE_RETAIN;
   ~CPDF_Type3Font() override;
 
   // CPDF_Font:
@@ -32,7 +31,7 @@
   const CPDF_Type3Font* AsType3Font() const override;
   CPDF_Type3Font* AsType3Font() override;
   void WillBeDestroyed() override;
-  uint32_t GetCharWidthF(uint32_t charcode) override;
+  int GetCharWidthF(uint32_t charcode) override;
   FX_RECT GetCharBBox(uint32_t charcode) override;
 
   void SetPageResources(CPDF_Dictionary* pResources) {
@@ -45,7 +44,7 @@
 
  private:
   CPDF_Type3Font(CPDF_Document* pDocument,
-                 CPDF_Dictionary* pFontDict,
+                 RetainPtr<CPDF_Dictionary> pFontDict,
                  FormFactoryIface* pFormFactory);
 
   // CPDF_Font:
@@ -62,7 +61,7 @@
   RetainPtr<CPDF_Dictionary> m_pPageResources;
   RetainPtr<CPDF_Dictionary> m_pFontResources;
   std::map<uint32_t, std::unique_ptr<CPDF_Type3Char>> m_CacheMap;
-  uint32_t m_CharWidthL[256];
+  int m_CharWidthL[256] = {};
 };
 
 #endif  // CORE_FPDFAPI_FONT_CPDF_TYPE3FONT_H_
diff --git a/core/fpdfapi/page/Android.bp b/core/fpdfapi/page/Android.bp
index e44cca2..57f3683 100644
--- a/core/fpdfapi/page/Android.bp
+++ b/core/fpdfapi/page/Android.bp
@@ -13,11 +13,12 @@
 
     visibility: ["//external/pdfium:__subpackages__"],
 
-    header_libs: [
-        "libpdfium-constants",
+    exclude_srcs: [
+        "test_with_page_module.cpp",
     ],
 
     static_libs: [
+        "libpdfium-constants",
         "libpdfium-fxcodec",
         "libpdfium-fxcrt",
         "libpdfium-fxge",
diff --git a/core/fpdfapi/page/BUILD.gn b/core/fpdfapi/page/BUILD.gn
index 2ab0b72..f6bbd74 100644
--- a/core/fpdfapi/page/BUILD.gn
+++ b/core/fpdfapi/page/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -11,6 +11,8 @@
     "cpdf_allstates.h",
     "cpdf_annotcontext.cpp",
     "cpdf_annotcontext.h",
+    "cpdf_basedcs.cpp",
+    "cpdf_basedcs.h",
     "cpdf_clippath.cpp",
     "cpdf_clippath.h",
     "cpdf_color.cpp",
@@ -47,14 +49,20 @@
     "cpdf_iccprofile.h",
     "cpdf_image.cpp",
     "cpdf_image.h",
+    "cpdf_imageloader.cpp",
+    "cpdf_imageloader.h",
     "cpdf_imageobject.cpp",
     "cpdf_imageobject.h",
+    "cpdf_indexedcs.cpp",
+    "cpdf_indexedcs.h",
     "cpdf_meshstream.cpp",
     "cpdf_meshstream.h",
     "cpdf_occontext.cpp",
     "cpdf_occontext.h",
     "cpdf_page.cpp",
     "cpdf_page.h",
+    "cpdf_pageimagecache.cpp",
+    "cpdf_pageimagecache.h",
     "cpdf_pagemodule.cpp",
     "cpdf_pagemodule.h",
     "cpdf_pageobject.cpp",
@@ -99,30 +107,52 @@
     "cpdf_transparency.h",
     "ipdf_page.h",
   ]
-  configs += [ "../../../:pdfium_core_config" ]
+  configs += [ "../../../:pdfium_strict_config" ]
+  public_deps = [
+    "../../fxge",
+    "../parser",
+  ]
   deps = [
     "../../../constants",
     "../../fxcodec",
     "../../fxcrt",
-    "../../fxge",
     "../font",
-    "../parser",
   ]
   allow_circular_includes_from = []
-  if (pdf_use_skia || pdf_use_skia_paths) {
+  if (pdf_use_skia) {
     allow_circular_includes_from += [ "../../fxge" ]
   }
   visibility = [ "../../../*" ]
 }
 
+source_set("unit_test_support") {
+  testonly = true
+  sources = [
+    "test_with_page_module.cpp",
+    "test_with_page_module.h",
+  ]
+  configs += [ "../../../:pdfium_strict_config" ]
+  deps = [
+    "../page",
+    "//testing/gtest",
+  ]
+}
+
 pdfium_unittest_source_set("unittests") {
   sources = [
+    "cpdf_colorspace_unittest.cpp",
     "cpdf_devicecs_unittest.cpp",
+    "cpdf_function_unittest.cpp",
+    "cpdf_pageimagecache_unittest.cpp",
     "cpdf_pageobjectholder_unittest.cpp",
     "cpdf_psengine_unittest.cpp",
     "cpdf_streamcontentparser_unittest.cpp",
     "cpdf_streamparser_unittest.cpp",
   ]
-  deps = [ ":page" ]
+  deps = [
+    ":page",
+    "../parser",
+    "../render",
+  ]
   pdfium_root_dir = "../../../"
 }
diff --git a/core/fpdfapi/page/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 a12ce33..996b3f79 100644
--- a/core/fpdfapi/page/cpdf_allstates.cpp
+++ b/core/fpdfapi/page/cpdf_allstates.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,9 +16,8 @@
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/stl_util.h"
 
 CPDF_AllStates::CPDF_AllStates() = default;
 
@@ -26,6 +25,7 @@
 
 void CPDF_AllStates::Copy(const CPDF_AllStates& src) {
   CopyStates(src);
+  m_GraphicsResourceNames = src.m_GraphicsResourceNames;
   m_TextMatrix = src.m_TextMatrix;
   m_ParentMatrix = src.m_ParentMatrix;
   m_CTM = src.m_CTM;
@@ -43,17 +43,15 @@
   m_GraphState.SetLineDash(std::move(dashes), phase, scale);
 }
 
-void CPDF_AllStates::ProcessExtGS(CPDF_Dictionary* pGS,
+void CPDF_AllStates::ProcessExtGS(const CPDF_Dictionary* pGS,
                                   CPDF_StreamContentParser* pParser) {
   CPDF_DictionaryLocker locker(pGS);
   for (const auto& it : locker) {
-    const ByteString& key_str = it.first;
-    CPDF_Object* pElement = it.second.Get();
-    CPDF_Object* pObject = pElement ? pElement->GetDirect() : nullptr;
+    RetainPtr<CPDF_Object> pObject = it.second->GetMutableDirect();
     if (!pObject)
       continue;
 
-    uint32_t key = key_str.GetID();
+    uint32_t key = it.first.GetID();
     switch (key) {
       case FXBSTR_ID('L', 'W', 0, 0):
         m_GraphState.SetLineWidth(pObject->GetNumber());
@@ -70,60 +68,59 @@
         m_GraphState.SetMiterLimit(pObject->GetNumber());
         break;
       case FXBSTR_ID('D', 0, 0, 0): {
-        CPDF_Array* pDash = pObject->AsArray();
+        const CPDF_Array* pDash = pObject->AsArray();
         if (!pDash)
           break;
 
-        CPDF_Array* pArray = pDash->GetArrayAt(0);
+        RetainPtr<const CPDF_Array> pArray = pDash->GetArrayAt(0);
         if (!pArray)
           break;
 
-        SetLineDash(pArray, pDash->GetNumberAt(1), 1.0f);
+        SetLineDash(pArray.Get(), pDash->GetFloatAt(1), 1.0f);
         break;
       }
       case FXBSTR_ID('R', 'I', 0, 0):
         m_GeneralState.SetRenderIntent(pObject->GetString());
         break;
       case FXBSTR_ID('F', 'o', 'n', 't'): {
-        CPDF_Array* pFont = pObject->AsArray();
+        const CPDF_Array* pFont = pObject->AsArray();
         if (!pFont)
           break;
 
-        m_TextState.SetFontSize(pFont->GetNumberAt(1));
-        m_TextState.SetFont(pParser->FindFont(pFont->GetStringAt(0)));
+        m_TextState.SetFontSize(pFont->GetFloatAt(1));
+        m_TextState.SetFont(pParser->FindFont(pFont->GetByteStringAt(0)));
         break;
       }
       case FXBSTR_ID('T', 'R', 0, 0):
         if (pGS->KeyExist("TR2")) {
           continue;
         }
-        FALLTHROUGH;
+        [[fallthrough]];
       case FXBSTR_ID('T', 'R', '2', 0):
-        m_GeneralState.SetTR(pObject && !pObject->IsName() ? pObject : nullptr);
+        m_GeneralState.SetTR(!pObject->IsName() ? std::move(pObject) : nullptr);
         break;
       case FXBSTR_ID('B', 'M', 0, 0): {
-        CPDF_Array* pArray = pObject->AsArray();
-        m_GeneralState.SetBlendMode(pArray ? pArray->GetStringAt(0)
+        const CPDF_Array* pArray = pObject->AsArray();
+        m_GeneralState.SetBlendMode(pArray ? pArray->GetByteStringAt(0)
                                            : pObject->GetString());
         if (m_GeneralState.GetBlendType() > BlendMode::kMultiply)
           pParser->GetPageObjectHolder()->SetBackgroundAlphaNeeded(true);
         break;
       }
-      case FXBSTR_ID('S', 'M', 'a', 's'):
-        if (ToDictionary(pObject)) {
-          m_GeneralState.SetSoftMask(pObject);
+      case FXBSTR_ID('S', 'M', 'a', 's'): {
+        RetainPtr<CPDF_Dictionary> pMaskDict = ToDictionary(pObject);
+        m_GeneralState.SetSoftMask(pMaskDict);
+        if (pMaskDict)
           m_GeneralState.SetSMaskMatrix(pParser->GetCurStates()->m_CTM);
-        } else {
-          m_GeneralState.SetSoftMask(nullptr);
-        }
         break;
+      }
       case FXBSTR_ID('C', 'A', 0, 0):
         m_GeneralState.SetStrokeAlpha(
-            pdfium::clamp(pObject->GetNumber(), 0.0f, 1.0f));
+            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());
@@ -140,20 +137,20 @@
         if (pGS->KeyExist("BG2")) {
           continue;
         }
-        FALLTHROUGH;
+        [[fallthrough]];
       case FXBSTR_ID('B', 'G', '2', 0):
-        m_GeneralState.SetBG(pObject);
+        m_GeneralState.SetBG(std::move(pObject));
         break;
       case FXBSTR_ID('U', 'C', 'R', 0):
         if (pGS->KeyExist("UCR2")) {
           continue;
         }
-        FALLTHROUGH;
+        [[fallthrough]];
       case FXBSTR_ID('U', 'C', 'R', '2'):
-        m_GeneralState.SetUCR(pObject);
+        m_GeneralState.SetUCR(std::move(pObject));
         break;
       case FXBSTR_ID('H', 'T', 0, 0):
-        m_GeneralState.SetHT(pObject);
+        m_GeneralState.SetHT(std::move(pObject));
         break;
       case FXBSTR_ID('F', 'L', 0, 0):
         m_GeneralState.SetFlatness(pObject->GetNumber());
diff --git a/core/fpdfapi/page/cpdf_allstates.h b/core/fpdfapi/page/cpdf_allstates.h
index dc6f17f..3d4d9a3 100644
--- a/core/fpdfapi/page/cpdf_allstates.h
+++ b/core/fpdfapi/page/cpdf_allstates.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,9 +7,11 @@
 #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"
-#include "core/fxcrt/fx_system.h"
 
 class CPDF_Array;
 class CPDF_Dictionary;
@@ -21,9 +23,11 @@
   ~CPDF_AllStates() override;
 
   void Copy(const CPDF_AllStates& src);
-  void ProcessExtGS(CPDF_Dictionary* pGS, CPDF_StreamContentParser* pParser);
+  void ProcessExtGS(const CPDF_Dictionary* pGS,
+                    CPDF_StreamContentParser* pParser);
   void SetLineDash(const CPDF_Array* pArray, float phase, float scale);
 
+  std::vector<ByteString> m_GraphicsResourceNames;
   CFX_Matrix m_TextMatrix;
   CFX_Matrix m_CTM;
   CFX_Matrix m_ParentMatrix;
diff --git a/core/fpdfapi/page/cpdf_annotcontext.cpp b/core/fpdfapi/page/cpdf_annotcontext.cpp
index 1078e46..7148a09 100644
--- a/core/fpdfapi/page/cpdf_annotcontext.cpp
+++ b/core/fpdfapi/page/cpdf_annotcontext.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,30 +6,34 @@
 
 #include "core/fpdfapi/page/cpdf_annotcontext.h"
 
+#include <utility>
+
 #include "core/fpdfapi/page/cpdf_form.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
-CPDF_AnnotContext::CPDF_AnnotContext(CPDF_Dictionary* pAnnotDict,
-                                     CPDF_Page* pPage)
-    : m_pAnnotDict(pAnnotDict), m_pPage(pPage) {
-  ASSERT(m_pAnnotDict);
-  ASSERT(m_pPage);
+CPDF_AnnotContext::CPDF_AnnotContext(RetainPtr<CPDF_Dictionary> pAnnotDict,
+                                     IPDF_Page* pPage)
+    : m_pAnnotDict(std::move(pAnnotDict)), m_pPage(pPage) {
+  DCHECK(m_pAnnotDict);
+  DCHECK(m_pPage);
+  DCHECK(m_pPage->AsPDFPage());
 }
 
 CPDF_AnnotContext::~CPDF_AnnotContext() = default;
 
-void CPDF_AnnotContext::SetForm(CPDF_Stream* pStream) {
+void CPDF_AnnotContext::SetForm(RetainPtr<CPDF_Stream> pStream) {
   if (!pStream)
     return;
 
   // Reset the annotation matrix to be the identity matrix, since the
   // appearance stream already takes matrix into account.
-  pStream->GetDict()->SetMatrixFor("Matrix", CFX_Matrix());
+  pStream->GetMutableDict()->SetMatrixFor("Matrix", CFX_Matrix());
 
-  m_pAnnotForm = pdfium::MakeUnique<CPDF_Form>(
-      m_pPage->GetDocument(), m_pPage->m_pResources.Get(), pStream);
+  m_pAnnotForm = std::make_unique<CPDF_Form>(
+      m_pPage->GetDocument(), m_pPage->AsPDFPage()->GetMutableResources(),
+      pStream);
   m_pAnnotForm->ParseContent();
 }
diff --git a/core/fpdfapi/page/cpdf_annotcontext.h b/core/fpdfapi/page/cpdf_annotcontext.h
index ee9f3fc..dee2ad3 100644
--- a/core/fpdfapi/page/cpdf_annotcontext.h
+++ b/core/fpdfapi/page/cpdf_annotcontext.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,28 +14,29 @@
 
 class CPDF_Dictionary;
 class CPDF_Form;
-class CPDF_Page;
 class CPDF_Stream;
+class IPDF_Page;
 
 class CPDF_AnnotContext {
  public:
-  CPDF_AnnotContext(CPDF_Dictionary* pAnnotDict, CPDF_Page* pPage);
+  CPDF_AnnotContext(RetainPtr<CPDF_Dictionary> pAnnotDict, IPDF_Page* pPage);
   ~CPDF_AnnotContext();
 
-  void SetForm(CPDF_Stream* pStream);
+  void SetForm(RetainPtr<CPDF_Stream> pStream);
   bool HasForm() const { return !!m_pAnnotForm; }
   CPDF_Form* GetForm() const { return m_pAnnotForm.get(); }
 
   // Never nullptr.
-  CPDF_Dictionary* GetAnnotDict() const { return m_pAnnotDict.Get(); }
+  RetainPtr<CPDF_Dictionary> GetMutableAnnotDict() { return m_pAnnotDict; }
+  const CPDF_Dictionary* GetAnnotDict() const { return m_pAnnotDict.Get(); }
 
   // Never nullptr.
-  CPDF_Page* GetPage() const { return m_pPage.Get(); }
+  IPDF_Page* GetPage() const { return m_pPage; }
 
  private:
   std::unique_ptr<CPDF_Form> m_pAnnotForm;
   RetainPtr<CPDF_Dictionary> const m_pAnnotDict;
-  UnownedPtr<CPDF_Page> const m_pPage;
+  UnownedPtr<IPDF_Page> const m_pPage;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_ANNOTCONTEXT_H_
diff --git a/core/fpdfapi/page/cpdf_basedcs.cpp b/core/fpdfapi/page/cpdf_basedcs.cpp
new file mode 100644
index 0000000..c8988bb
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_basedcs.cpp
@@ -0,0 +1,17 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/page/cpdf_basedcs.h"
+
+CPDF_BasedCS::CPDF_BasedCS(Family family) : CPDF_ColorSpace(family) {}
+
+CPDF_BasedCS::~CPDF_BasedCS() = default;
+
+void CPDF_BasedCS::EnableStdConversion(bool bEnabled) {
+  CPDF_ColorSpace::EnableStdConversion(bEnabled);
+  if (m_pBaseCS)
+    m_pBaseCS->EnableStdConversion(bEnabled);
+}
diff --git a/core/fpdfapi/page/cpdf_basedcs.h b/core/fpdfapi/page/cpdf_basedcs.h
new file mode 100644
index 0000000..7c72f1e
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_basedcs.h
@@ -0,0 +1,29 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFAPI_PAGE_CPDF_BASEDCS_H_
+#define CORE_FPDFAPI_PAGE_CPDF_BASEDCS_H_
+
+#include "core/fpdfapi/page/cpdf_colorspace.h"
+#include "core/fxcrt/retain_ptr.h"
+
+// Represents a color space that is based on another color space. This includes
+// all the special color spaces in ISO 32000-1:2008, table 62, as well as the
+// ICCBased color space.
+class CPDF_BasedCS : public CPDF_ColorSpace {
+ public:
+  CONSTRUCT_VIA_MAKE_RETAIN;
+  ~CPDF_BasedCS() override;
+
+  void EnableStdConversion(bool bEnabled) final;
+
+ protected:
+  explicit CPDF_BasedCS(Family family);
+
+  RetainPtr<CPDF_ColorSpace> m_pBaseCS;  // May be fallback CS in some cases.
+};
+
+#endif  // CORE_FPDFAPI_PAGE_CPDF_BASEDCS_H_
diff --git a/core/fpdfapi/page/cpdf_clippath.cpp b/core/fpdfapi/page/cpdf_clippath.cpp
index 53b602d..032aff6 100644
--- a/core/fpdfapi/page/cpdf_clippath.cpp
+++ b/core/fpdfapi/page/cpdf_clippath.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -26,7 +26,7 @@
   return m_Ref.GetObject()->m_PathAndTypeList[i].first;
 }
 
-uint8_t CPDF_ClipPath::GetClipType(size_t i) const {
+CFX_FillRenderOptions::FillType CPDF_ClipPath::GetClipType(size_t i) const {
   return m_Ref.GetObject()->m_PathAndTypeList[i].second;
 }
 
@@ -74,9 +74,17 @@
   return rect;
 }
 
-void CPDF_ClipPath::AppendPath(CPDF_Path path, uint8_t type, bool bAutoMerge) {
+void CPDF_ClipPath::AppendPath(CPDF_Path path,
+                               CFX_FillRenderOptions::FillType type) {
   PathData* pData = m_Ref.GetPrivateCopy();
-  if (!pData->m_PathAndTypeList.empty() && bAutoMerge) {
+  pData->m_PathAndTypeList.emplace_back(path, type);
+}
+
+void CPDF_ClipPath::AppendPathWithAutoMerge(
+    CPDF_Path path,
+    CFX_FillRenderOptions::FillType type) {
+  PathData* pData = m_Ref.GetPrivateCopy();
+  if (!pData->m_PathAndTypeList.empty()) {
     const CPDF_Path& old_path = pData->m_PathAndTypeList.back().first;
     if (old_path.IsRect()) {
       CFX_PointF point0 = old_path.GetPoint(0);
@@ -87,7 +95,7 @@
         pData->m_PathAndTypeList.pop_back();
     }
   }
-  pData->m_PathAndTypeList.push_back(std::make_pair(path, type));
+  AppendPath(path, type);
 }
 
 void CPDF_ClipPath::AppendTexts(
@@ -107,7 +115,7 @@
     return;
 
   for (size_t i = 0; i < that.GetPathCount(); ++i)
-    AppendPath(that.GetPath(i), that.GetClipType(i), /*bAutoMerge=*/false);
+    AppendPath(that.GetPath(i), that.GetClipType(i));
 }
 
 void CPDF_ClipPath::Transform(const CFX_Matrix& matrix) {
@@ -123,10 +131,9 @@
 
 CPDF_ClipPath::PathData::PathData() = default;
 
-CPDF_ClipPath::PathData::PathData(const PathData& that) {
-  m_PathAndTypeList = that.m_PathAndTypeList;
-
-  m_TextList.resize(that.m_TextList.size());
+CPDF_ClipPath::PathData::PathData(const PathData& that)
+    : m_PathAndTypeList(that.m_PathAndTypeList),
+      m_TextList(that.m_TextList.size()) {
   for (size_t i = 0; i < that.m_TextList.size(); ++i) {
     if (that.m_TextList[i])
       m_TextList[i] = that.m_TextList[i]->Clone();
diff --git a/core/fpdfapi/page/cpdf_clippath.h b/core/fpdfapi/page/cpdf_clippath.h
index aff2839..e93f9a8 100644
--- a/core/fpdfapi/page/cpdf_clippath.h
+++ b/core/fpdfapi/page/cpdf_clippath.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,7 +13,9 @@
 
 #include "core/fpdfapi/page/cpdf_path.h"
 #include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/shared_copy_on_write.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
 
 class CPDF_TextObject;
 
@@ -35,11 +37,13 @@
 
   size_t GetPathCount() const;
   CPDF_Path GetPath(size_t i) const;
-  uint8_t GetClipType(size_t i) const;
+  CFX_FillRenderOptions::FillType GetClipType(size_t i) const;
   size_t GetTextCount() const;
   CPDF_TextObject* GetText(size_t i) const;
   CFX_FloatRect GetClipBox() const;
-  void AppendPath(CPDF_Path path, uint8_t type, bool bAutoMerge);
+  void AppendPath(CPDF_Path path, CFX_FillRenderOptions::FillType type);
+  void AppendPathWithAutoMerge(CPDF_Path path,
+                               CFX_FillRenderOptions::FillType type);
   void AppendTexts(std::vector<std::unique_ptr<CPDF_TextObject>>* pTexts);
   void CopyClipPath(const CPDF_ClipPath& that);
   void Transform(const CFX_Matrix& matrix);
@@ -47,12 +51,12 @@
  private:
   class PathData final : public Retainable {
    public:
-    template <typename T, typename... Args>
-    friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+    CONSTRUCT_VIA_MAKE_RETAIN;
 
     RetainPtr<PathData> Clone() const;
 
-    using PathAndTypeData = std::pair<CPDF_Path, uint8_t>;
+    using PathAndTypeData =
+        std::pair<CPDF_Path, CFX_FillRenderOptions::FillType>;
 
     std::vector<PathAndTypeData> m_PathAndTypeList;
     std::vector<std::unique_ptr<CPDF_TextObject>> m_TextList;
diff --git a/core/fpdfapi/page/cpdf_color.cpp b/core/fpdfapi/page/cpdf_color.cpp
index cfa6422..57e483c 100644
--- a/core/fpdfapi/page/cpdf_color.cpp
+++ b/core/fpdfapi/page/cpdf_color.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,11 +6,10 @@
 
 #include "core/fpdfapi/page/cpdf_color.h"
 
-#include "core/fpdfapi/page/cpdf_docpagedata.h"
+#include <utility>
+
 #include "core/fpdfapi/page/cpdf_patterncs.h"
-#include "core/fpdfapi/parser/cpdf_array.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fxcrt/fx_system.h"
+#include "third_party/base/check.h"
 
 CPDF_Color::CPDF_Color() = default;
 
@@ -25,35 +24,36 @@
 }
 
 bool CPDF_Color::IsPatternInternal() const {
-  return m_pCS->GetFamily() == PDFCS_PATTERN;
+  return m_pCS->GetFamily() == CPDF_ColorSpace::Family::kPattern;
 }
 
-void CPDF_Color::SetColorSpace(const RetainPtr<CPDF_ColorSpace>& pCS) {
-  m_pCS = pCS;
+void CPDF_Color::SetColorSpace(RetainPtr<CPDF_ColorSpace> colorspace) {
+  m_pCS = std::move(colorspace);
   if (IsPatternInternal()) {
     m_Buffer.clear();
-    m_pValue = pdfium::MakeUnique<PatternValue>();
+    m_pValue = std::make_unique<PatternValue>();
   } else {
-    m_Buffer = pCS->CreateBufAndSetDefaultColor();
+    m_Buffer = m_pCS->CreateBufAndSetDefaultColor();
     m_pValue.reset();
   }
 }
 
-void CPDF_Color::SetValueForNonPattern(const std::vector<float>& values) {
-  ASSERT(!IsPatternInternal());
-  ASSERT(m_pCS->CountComponents() <= values.size());
-  m_Buffer = values;
+void CPDF_Color::SetValueForNonPattern(std::vector<float> values) {
+  DCHECK(!IsPatternInternal());
+  DCHECK(m_pCS->CountComponents() <= values.size());
+  m_Buffer = std::move(values);
 }
 
-void CPDF_Color::SetValueForPattern(const RetainPtr<CPDF_Pattern>& pPattern,
-                                    const std::vector<float>& values) {
+void CPDF_Color::SetValueForPattern(RetainPtr<CPDF_Pattern> pattern,
+                                    pdfium::span<float> values) {
   if (values.size() > kMaxPatternColorComps)
     return;
 
-  if (!IsPattern())
-    SetColorSpace(CPDF_ColorSpace::GetStockCS(PDFCS_PATTERN));
-
-  m_pValue->SetPattern(pPattern);
+  if (!IsPattern()) {
+    SetColorSpace(
+        CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kPattern));
+  }
+  m_pValue->SetPattern(std::move(pattern));
   m_pValue->SetComps(values);
 }
 
@@ -62,8 +62,8 @@
     return *this;
 
   m_Buffer = that.m_Buffer;
-  m_pValue = that.m_pValue ? pdfium::MakeUnique<PatternValue>(*that.m_pValue)
-                           : nullptr;
+  m_pValue =
+      that.m_pValue ? std::make_unique<PatternValue>(*that.m_pValue) : nullptr;
   m_pCS = that.m_pCS;
   return *this;
 }
@@ -73,7 +73,8 @@
 }
 
 bool CPDF_Color::IsColorSpaceRGB() const {
-  return m_pCS == CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
+  return m_pCS ==
+         CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB);
 }
 
 bool CPDF_Color::GetRGB(int* R, int* G, int* B) const {
@@ -88,7 +89,7 @@
     }
   } else {
     if (!m_Buffer.empty())
-      result = m_pCS->GetRGB(m_Buffer.data(), &r, &g, &b);
+      result = m_pCS->GetRGB(m_Buffer, &r, &g, &b);
   }
   if (!result)
     return false;
@@ -99,7 +100,7 @@
   return true;
 }
 
-CPDF_Pattern* CPDF_Color::GetPattern() const {
-  ASSERT(IsPattern());
+RetainPtr<CPDF_Pattern> CPDF_Color::GetPattern() const {
+  DCHECK(IsPattern());
   return m_pValue ? m_pValue->GetPattern() : nullptr;
 }
diff --git a/core/fpdfapi/page/cpdf_color.h b/core/fpdfapi/page/cpdf_color.h
index b533d08..a2ba7cb 100644
--- a/core/fpdfapi/page/cpdf_color.h
+++ b/core/fpdfapi/page/cpdf_color.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,13 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_COLOR_H_
 #define CORE_FPDFAPI_PAGE_CPDF_COLOR_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_ColorSpace;
 class CPDF_Pattern;
@@ -28,16 +30,17 @@
 
   bool IsNull() const { return m_Buffer.empty() && !m_pValue; }
   bool IsPattern() const;
-  void SetColorSpace(const RetainPtr<CPDF_ColorSpace>& pCS);
-  void SetValueForNonPattern(const std::vector<float>& values);
-  void SetValueForPattern(const RetainPtr<CPDF_Pattern>& pPattern,
-                          const std::vector<float>& values);
+  void SetColorSpace(RetainPtr<CPDF_ColorSpace> colorspace);
+  void SetValueForNonPattern(std::vector<float> values);
+  void SetValueForPattern(RetainPtr<CPDF_Pattern> pattern,
+                          pdfium::span<float> values);
+
   uint32_t CountComponents() const;
   bool IsColorSpaceRGB() const;
   bool GetRGB(int* R, int* G, int* B) const;
 
   // Should only be called if IsPattern() returns true.
-  CPDF_Pattern* GetPattern() const;
+  RetainPtr<CPDF_Pattern> GetPattern() const;
 
  protected:
   bool IsPatternInternal() const;
diff --git a/core/fpdfapi/page/cpdf_colorspace.cpp b/core/fpdfapi/page/cpdf_colorspace.cpp
index fd4a2ad..2a9d2f4 100644
--- a/core/fpdfapi/page/cpdf_colorspace.cpp
+++ b/core/fpdfapi/page/cpdf_colorspace.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,13 @@
 
 #include "core/fpdfapi/page/cpdf_colorspace.h"
 
+#include <math.h>
+#include <stdint.h>
+
 #include <algorithm>
 #include <limits>
 #include <memory>
+#include <type_traits>
 #include <utility>
 #include <vector>
 
@@ -16,6 +20,7 @@
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
 #include "core/fpdfapi/page/cpdf_function.h"
 #include "core/fpdfapi/page/cpdf_iccprofile.h"
+#include "core/fpdfapi/page/cpdf_indexedcs.h"
 #include "core/fpdfapi/page/cpdf_pagemodule.h"
 #include "core/fpdfapi/page/cpdf_pattern.h"
 #include "core/fpdfapi/page/cpdf_patterncs.h"
@@ -25,19 +30,24 @@
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fpdfapi/parser/cpdf_stream_acc.h"
-#include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcodec/fx_codec.h"
-#include "core/fxcodec/icc/iccmodule.h"
-#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_2d_size.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/maybe_owned.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/scoped_set_insertion.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
-const uint8_t g_sRGBSamples1[] = {
+constexpr uint8_t kSRGBSamples1[] = {
     0,   3,   6,   10,  13,  15,  18,  20,  22,  23,  25,  27,  28,  30,  31,
     32,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,
     48,  49,  49,  50,  51,  52,  53,  53,  54,  55,  56,  56,  57,  58,  58,
@@ -53,7 +63,7 @@
     116, 117, 117, 117, 118, 118, 118, 118, 119, 119, 119, 120,
 };
 
-const uint8_t g_sRGBSamples2[] = {
+constexpr uint8_t kSRGBSamples2[] = {
     120, 121, 122, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
     136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149,
     150, 151, 152, 153, 154, 155, 155, 156, 157, 158, 159, 159, 160, 161, 162,
@@ -79,7 +89,7 @@
 }
 
 void GetBlackPoint(const CPDF_Dictionary* pDict, float* pPoints) {
-  const CPDF_Array* pParam = pDict->GetArrayFor("BlackPoint");
+  RetainPtr<const CPDF_Array> pParam = pDict->GetArrayFor("BlackPoint");
   if (!pParam || pParam->size() != kBlackWhitePointCount) {
     GetDefaultBlackPoint(pPoints);
     return;
@@ -87,7 +97,7 @@
 
   // Check to make sure all values are non-negative.
   for (size_t i = 0; i < kBlackWhitePointCount; ++i) {
-    pPoints[i] = pParam->GetNumberAt(i);
+    pPoints[i] = pParam->GetFloatAt(i);
     if (pPoints[i] < 0) {
       GetDefaultBlackPoint(pPoints);
       return;
@@ -96,29 +106,30 @@
 }
 
 bool GetWhitePoint(const CPDF_Dictionary* pDict, float* pPoints) {
-  const CPDF_Array* pParam = pDict->GetArrayFor("WhitePoint");
+  RetainPtr<const CPDF_Array> pParam = pDict->GetArrayFor("WhitePoint");
   if (!pParam || pParam->size() != kBlackWhitePointCount)
     return false;
 
   for (size_t i = 0; i < kBlackWhitePointCount; ++i)
-    pPoints[i] = pParam->GetNumberAt(i);
+    pPoints[i] = pParam->GetFloatAt(i);
   return pPoints[0] > 0.0f && pPoints[1] == 1.0f && pPoints[2] > 0.0f;
 }
 
 class CPDF_CalGray final : public CPDF_ColorSpace {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
+  CONSTRUCT_VIA_MAKE_RETAIN;
   ~CPDF_CalGray() override;
 
   // CPDF_ColorSpace:
-  bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override;
+  bool GetRGB(pdfium::span<const float> pBuf,
+              float* R,
+              float* G,
+              float* B) const override;
   uint32_t v_Load(CPDF_Document* pDoc,
                   const CPDF_Array* pArray,
                   std::set<const CPDF_Object*>* pVisited) override;
-  void TranslateImageLine(uint8_t* pDestBuf,
-                          const uint8_t* pSrcBuf,
+  void TranslateImageLine(pdfium::span<uint8_t> dest_span,
+                          pdfium::span<const uint8_t> src_span,
                           int pixels,
                           int image_width,
                           int image_height,
@@ -127,24 +138,25 @@
  private:
   static constexpr float kDefaultGamma = 1.0f;
 
-  explicit CPDF_CalGray(CPDF_Document* pDoc);
+  CPDF_CalGray();
 
   float m_Gamma = kDefaultGamma;
-  float m_WhitePoint[kBlackWhitePointCount];
-  float m_BlackPoint[kBlackWhitePointCount];
+  float m_WhitePoint[kBlackWhitePointCount] = {1.0f, 1.0f, 1.0f};
+  float m_BlackPoint[kBlackWhitePointCount] = {0.0f, 0.0f, 0.0f};
 };
 
 class CPDF_CalRGB final : public CPDF_ColorSpace {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
+  CONSTRUCT_VIA_MAKE_RETAIN;
   ~CPDF_CalRGB() override;
 
   // CPDF_ColorSpace:
-  bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override;
-  void TranslateImageLine(uint8_t* pDestBuf,
-                          const uint8_t* pSrcBuf,
+  bool GetRGB(pdfium::span<const float> pBuf,
+              float* R,
+              float* G,
+              float* B) const override;
+  void TranslateImageLine(pdfium::span<uint8_t> dest_span,
+                          pdfium::span<const uint8_t> src_span,
                           int pixels,
                           int image_width,
                           int image_height,
@@ -157,31 +169,32 @@
   static constexpr size_t kGammaCount = 3;
   static constexpr size_t kMatrixCount = 9;
 
-  explicit CPDF_CalRGB(CPDF_Document* pDoc);
+  CPDF_CalRGB();
 
-  float m_WhitePoint[kBlackWhitePointCount];
-  float m_BlackPoint[kBlackWhitePointCount];
-  float m_Gamma[kGammaCount];
-  float m_Matrix[kMatrixCount];
-  bool m_bGamma = false;
-  bool m_bMatrix = false;
+  float m_WhitePoint[kBlackWhitePointCount] = {1.0f, 1.0f, 1.0f};
+  float m_BlackPoint[kBlackWhitePointCount] = {0.0f, 0.0f, 0.0f};
+  float m_Gamma[kGammaCount] = {};
+  float m_Matrix[kMatrixCount] = {};
+  bool m_bHasGamma = false;
+  bool m_bHasMatrix = false;
 };
 
 class CPDF_LabCS final : public CPDF_ColorSpace {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
+  CONSTRUCT_VIA_MAKE_RETAIN;
   ~CPDF_LabCS() override;
 
   // CPDF_ColorSpace:
-  bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override;
+  bool GetRGB(pdfium::span<const float> pBuf,
+              float* R,
+              float* G,
+              float* B) const override;
   void GetDefaultValue(int iComponent,
                        float* value,
                        float* min,
                        float* max) const override;
-  void TranslateImageLine(uint8_t* pDestBuf,
-                          const uint8_t* pSrcBuf,
+  void TranslateImageLine(pdfium::span<uint8_t> dest_span,
+                          pdfium::span<const uint8_t> src_span,
                           int pixels,
                           int image_width,
                           int image_height,
@@ -193,25 +206,25 @@
  private:
   static constexpr size_t kRangesCount = 4;
 
-  explicit CPDF_LabCS(CPDF_Document* pDoc);
+  CPDF_LabCS();
 
-  float m_WhitePoint[kBlackWhitePointCount];
-  float m_BlackPoint[kBlackWhitePointCount];
-  float m_Ranges[kRangesCount];
+  float m_WhitePoint[kBlackWhitePointCount] = {1.0f, 1.0f, 1.0f};
+  float m_BlackPoint[kBlackWhitePointCount] = {0.0f, 0.0f, 0.0f};
+  float m_Ranges[kRangesCount] = {};
 };
 
-class CPDF_ICCBasedCS final : public CPDF_ColorSpace {
+class CPDF_ICCBasedCS final : public CPDF_BasedCS {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
+  CONSTRUCT_VIA_MAKE_RETAIN;
   ~CPDF_ICCBasedCS() override;
 
   // CPDF_ColorSpace:
-  bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override;
-  void EnableStdConversion(bool bEnabled) override;
-  void TranslateImageLine(uint8_t* pDestBuf,
-                          const uint8_t* pSrcBuf,
+  bool GetRGB(pdfium::span<const float> pBuf,
+              float* R,
+              float* G,
+              float* B) const override;
+  void TranslateImageLine(pdfium::span<uint8_t> dest_span,
+                          pdfium::span<const uint8_t> src_span,
                           int pixels,
                           int image_width,
                           int image_height,
@@ -222,7 +235,7 @@
                   std::set<const CPDF_Object*>* pVisited) override;
 
  private:
-  explicit CPDF_ICCBasedCS(CPDF_Document* pDoc);
+  CPDF_ICCBasedCS();
 
   // If no valid ICC profile or using sRGB, try looking for an alternate.
   bool FindAlternateProfile(CPDF_Document* pDoc,
@@ -234,85 +247,57 @@
   static std::vector<float> GetRanges(const CPDF_Dictionary* pDict,
                                       uint32_t nComponents);
 
-  RetainPtr<CPDF_ColorSpace> m_pAlterCS;
   RetainPtr<CPDF_IccProfile> m_pProfile;
-  mutable std::vector<uint8_t> m_pCache;
+  mutable DataVector<uint8_t> m_pCache;
   std::vector<float> m_pRanges;
 };
 
-class CPDF_IndexedCS final : public CPDF_ColorSpace {
+class CPDF_SeparationCS final : public CPDF_BasedCS {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  ~CPDF_IndexedCS() override;
-
-  // CPDF_ColorSpace:
-  bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override;
-  void EnableStdConversion(bool bEnabled) override;
-  uint32_t v_Load(CPDF_Document* pDoc,
-                  const CPDF_Array* pArray,
-                  std::set<const CPDF_Object*>* pVisited) override;
-
- private:
-  explicit CPDF_IndexedCS(CPDF_Document* pDoc);
-
-  RetainPtr<CPDF_ColorSpace> m_pBaseCS;
-  uint32_t m_nBaseComponents = 0;
-  int m_MaxIndex = 0;
-  ByteString m_Table;
-  std::vector<float> m_pCompMinMax;
-};
-
-class CPDF_SeparationCS final : public CPDF_ColorSpace {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
+  CONSTRUCT_VIA_MAKE_RETAIN;
   ~CPDF_SeparationCS() override;
 
   // CPDF_ColorSpace:
-  bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override;
+  bool GetRGB(pdfium::span<const float> pBuf,
+              float* R,
+              float* G,
+              float* B) const override;
   void GetDefaultValue(int iComponent,
                        float* value,
                        float* min,
                        float* max) const override;
-  void EnableStdConversion(bool bEnabled) override;
   uint32_t v_Load(CPDF_Document* pDoc,
                   const CPDF_Array* pArray,
                   std::set<const CPDF_Object*>* pVisited) override;
 
  private:
-  enum { None, All, Colorant } m_Type;
+  CPDF_SeparationCS();
 
-  explicit CPDF_SeparationCS(CPDF_Document* pDoc);
-
-  RetainPtr<CPDF_ColorSpace> m_pAltCS;
+  bool m_IsNoneType = false;
   std::unique_ptr<const CPDF_Function> m_pFunc;
 };
 
-class CPDF_DeviceNCS final : public CPDF_ColorSpace {
+class CPDF_DeviceNCS final : public CPDF_BasedCS {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
+  CONSTRUCT_VIA_MAKE_RETAIN;
   ~CPDF_DeviceNCS() override;
 
   // CPDF_ColorSpace:
-  bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override;
+  bool GetRGB(pdfium::span<const float> pBuf,
+              float* R,
+              float* G,
+              float* B) const override;
   void GetDefaultValue(int iComponent,
                        float* value,
                        float* min,
                        float* max) const override;
-  void EnableStdConversion(bool bEnabled) override;
   uint32_t v_Load(CPDF_Document* pDoc,
                   const CPDF_Array* pArray,
                   std::set<const CPDF_Object*>* pVisited) override;
 
  private:
-  explicit CPDF_DeviceNCS(CPDF_Document* pDoc);
+  CPDF_DeviceNCS();
 
-  RetainPtr<CPDF_ColorSpace> m_pAltCS;
   std::unique_ptr<const CPDF_Function> m_pFunc;
 };
 
@@ -387,11 +372,11 @@
 };
 
 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 g_sRGBSamples1[scale] / 255.0f;
-  return g_sRGBSamples2[scale / 4 - 48] / 255.0f;
+    return kSRGBSamples1[scale] / 255.0f;
+  return kSRGBSamples2[scale / 4 - 48] / 255.0f;
 }
 
 void XYZ_to_sRGB(float X, float Y, float Z, float* R, float* G, float* B) {
@@ -440,46 +425,36 @@
 
 }  // namespace
 
-PatternValue::PatternValue() {
-  std::fill(std::begin(m_Comps), std::end(m_Comps), 0.0f);
-}
+PatternValue::PatternValue() = default;
 
 PatternValue::PatternValue(const PatternValue& that) = default;
 
 PatternValue::~PatternValue() = default;
 
 void PatternValue::SetComps(pdfium::span<const float> comps) {
-  CHECK(comps.size() <= m_Comps.size());
-  std::copy(std::begin(comps), std::end(comps), std::begin(m_Comps));
+  fxcrt::spancpy(pdfium::make_span(m_Comps), comps);
 }
 
 // static
-RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::ColorspaceFromName(
+RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::GetStockCSForName(
     const ByteString& name) {
   if (name == "DeviceRGB" || name == "RGB")
-    return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
+    return GetStockCS(Family::kDeviceRGB);
   if (name == "DeviceGray" || name == "G")
-    return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY);
+    return GetStockCS(Family::kDeviceGray);
   if (name == "DeviceCMYK" || name == "CMYK")
-    return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
+    return GetStockCS(Family::kDeviceCMYK);
   if (name == "Pattern")
-    return CPDF_ColorSpace::GetStockCS(PDFCS_PATTERN);
+    return GetStockCS(Family::kPattern);
   return nullptr;
 }
 
 // static
-RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::GetStockCS(int family) {
+RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::GetStockCS(Family family) {
   return CPDF_PageModule::GetInstance()->GetStockCS(family);
 }
 
 // static
-RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::Load(CPDF_Document* pDoc,
-                                                 CPDF_Object* pObj) {
-  std::set<const CPDF_Object*> visited;
-  return Load(pDoc, pObj, &visited);
-}
-
-// static
 RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::Load(
     CPDF_Document* pDoc,
     const CPDF_Object* pObj,
@@ -487,25 +462,25 @@
   if (!pObj)
     return nullptr;
 
-  if (pdfium::ContainsKey(*pVisited, pObj))
+  if (pdfium::Contains(*pVisited, pObj))
     return nullptr;
 
-  pdfium::ScopedSetInsertion<const CPDF_Object*> insertion(pVisited, pObj);
+  ScopedSetInsertion<const CPDF_Object*> insertion(pVisited, pObj);
 
   if (pObj->IsName())
-    return ColorspaceFromName(pObj->GetString());
+    return GetStockCSForName(pObj->GetString());
 
   if (const CPDF_Stream* pStream = pObj->AsStream()) {
-    const CPDF_Dictionary* pDict = pStream->GetDict();
+    RetainPtr<const CPDF_Dictionary> pDict = pStream->GetDict();
     if (!pDict)
       return nullptr;
 
-    CPDF_DictionaryLocker locker(pDict);
+    CPDF_DictionaryLocker locker(std::move(pDict));
     for (const auto& it : locker) {
-      CPDF_Name* pValue = ToName(it.second.Get());
+      RetainPtr<const CPDF_Name> pValue = ToName(it.second);
       if (pValue) {
         RetainPtr<CPDF_ColorSpace> pRet =
-            ColorspaceFromName(pValue->GetString());
+            GetStockCSForName(pValue->GetString());
         if (pRet)
           return pRet;
       }
@@ -517,44 +492,19 @@
   if (!pArray || pArray->IsEmpty())
     return nullptr;
 
-  const CPDF_Object* pFamilyObj = pArray->GetDirectObjectAt(0);
+  RetainPtr<const CPDF_Object> pFamilyObj = pArray->GetDirectObjectAt(0);
   if (!pFamilyObj)
     return nullptr;
 
   ByteString familyname = pFamilyObj->GetString();
   if (pArray->size() == 1)
-    return ColorspaceFromName(familyname);
+    return GetStockCSForName(familyname);
 
-  RetainPtr<CPDF_ColorSpace> pCS;
-  switch (familyname.GetID()) {
-    case FXBSTR_ID('C', 'a', 'l', 'G'):
-      pCS = pdfium::MakeRetain<CPDF_CalGray>(pDoc);
-      break;
-    case FXBSTR_ID('C', 'a', 'l', 'R'):
-      pCS = pdfium::MakeRetain<CPDF_CalRGB>(pDoc);
-      break;
-    case FXBSTR_ID('L', 'a', 'b', 0):
-      pCS = pdfium::MakeRetain<CPDF_LabCS>(pDoc);
-      break;
-    case FXBSTR_ID('I', 'C', 'C', 'B'):
-      pCS = pdfium::MakeRetain<CPDF_ICCBasedCS>(pDoc);
-      break;
-    case FXBSTR_ID('I', 'n', 'd', 'e'):
-    case FXBSTR_ID('I', 0, 0, 0):
-      pCS = pdfium::MakeRetain<CPDF_IndexedCS>(pDoc);
-      break;
-    case FXBSTR_ID('S', 'e', 'p', 'a'):
-      pCS = pdfium::MakeRetain<CPDF_SeparationCS>(pDoc);
-      break;
-    case FXBSTR_ID('D', 'e', 'v', 'i'):
-      pCS = pdfium::MakeRetain<CPDF_DeviceNCS>(pDoc);
-      break;
-    case FXBSTR_ID('P', 'a', 't', 't'):
-      pCS = pdfium::MakeRetain<CPDF_PatternCS>(pDoc);
-      break;
-    default:
-      return nullptr;
-  }
+  RetainPtr<CPDF_ColorSpace> pCS =
+      CPDF_ColorSpace::AllocateColorSpace(familyname.AsStringView());
+  if (!pCS)
+    return nullptr;
+
   pCS->m_pArray.Reset(pArray);
   pCS->m_nComponents = pCS->v_Load(pDoc, pArray, pVisited);
   if (pCS->m_nComponents == 0)
@@ -564,17 +514,42 @@
 }
 
 // static
-uint32_t CPDF_ColorSpace::ComponentsForFamily(int family) {
+RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::AllocateColorSpace(
+    ByteStringView bsFamilyName) {
+  switch (bsFamilyName.GetID()) {
+    case FXBSTR_ID('C', 'a', 'l', 'G'):
+      return pdfium::MakeRetain<CPDF_CalGray>();
+    case FXBSTR_ID('C', 'a', 'l', 'R'):
+      return pdfium::MakeRetain<CPDF_CalRGB>();
+    case FXBSTR_ID('L', 'a', 'b', 0):
+      return pdfium::MakeRetain<CPDF_LabCS>();
+    case FXBSTR_ID('I', 'C', 'C', 'B'):
+      return pdfium::MakeRetain<CPDF_ICCBasedCS>();
+    case FXBSTR_ID('I', 'n', 'd', 'e'):
+    case FXBSTR_ID('I', 0, 0, 0):
+      return pdfium::MakeRetain<CPDF_IndexedCS>();
+    case FXBSTR_ID('S', 'e', 'p', 'a'):
+      return pdfium::MakeRetain<CPDF_SeparationCS>();
+    case FXBSTR_ID('D', 'e', 'v', 'i'):
+      return pdfium::MakeRetain<CPDF_DeviceNCS>();
+    case FXBSTR_ID('P', 'a', 't', 't'):
+      return pdfium::MakeRetain<CPDF_PatternCS>();
+    default:
+      return nullptr;
+  }
+}
+
+// static
+uint32_t CPDF_ColorSpace::ComponentsForFamily(Family family) {
   switch (family) {
-    case PDFCS_DEVICEGRAY:
+    case Family::kDeviceGray:
       return 1;
-    case PDFCS_DEVICERGB:
+    case Family::kDeviceRGB:
       return 3;
-    case PDFCS_DEVICECMYK:
+    case Family::kDeviceCMYK:
       return 4;
     default:
-      NOTREACHED();
-      return 4;
+      NOTREACHED_NORETURN();
   }
 }
 
@@ -584,7 +559,7 @@
 }
 
 std::vector<float> CPDF_ColorSpace::CreateBufAndSetDefaultColor() const {
-  ASSERT(m_Family != PDFCS_PATTERN);
+  DCHECK(m_Family != Family::kPattern);
 
   float min;
   float max;
@@ -608,21 +583,23 @@
   *max = 1.0f;
 }
 
-void CPDF_ColorSpace::TranslateImageLine(uint8_t* dest_buf,
-                                         const uint8_t* src_buf,
+void CPDF_ColorSpace::TranslateImageLine(pdfium::span<uint8_t> dest_span,
+                                         pdfium::span<const uint8_t> src_span,
                                          int pixels,
                                          int image_width,
                                          int image_height,
                                          bool bTransMask) const {
+  uint8_t* dest_buf = dest_span.data();
+  const uint8_t* src_buf = src_span.data();
   std::vector<float> src(m_nComponents);
   float R;
   float G;
   float B;
-  const int divisor = m_Family != PDFCS_INDEXED ? 255 : 1;
+  const int divisor = m_Family != Family::kIndexed ? 255 : 1;
   for (int i = 0; i < pixels; i++) {
     for (uint32_t j = 0; j < m_nComponents; j++)
       src[j] = static_cast<float>(*src_buf++) / divisor;
-    GetRGB(src.data(), &R, &G, &B);
+    GetRGB(src, &R, &G, &B);
     *dest_buf++ = static_cast<int32_t>(B * 255);
     *dest_buf++ = static_cast<int32_t>(G * 255);
     *dest_buf++ = static_cast<int32_t>(R * 255);
@@ -637,135 +614,126 @@
 }
 
 bool CPDF_ColorSpace::IsNormal() const {
-  return GetFamily() == PDFCS_DEVICEGRAY || GetFamily() == PDFCS_DEVICERGB ||
-         GetFamily() == PDFCS_DEVICECMYK || GetFamily() == PDFCS_CALGRAY ||
-         GetFamily() == PDFCS_CALRGB;
-}
-
-CPDF_PatternCS* CPDF_ColorSpace::AsPatternCS() {
-  NOTREACHED();
-  return nullptr;
+  return GetFamily() == Family::kDeviceGray ||
+         GetFamily() == Family::kDeviceRGB ||
+         GetFamily() == Family::kDeviceCMYK ||
+         GetFamily() == Family::kCalGray || GetFamily() == Family::kCalRGB;
 }
 
 const CPDF_PatternCS* CPDF_ColorSpace::AsPatternCS() const {
-  NOTREACHED();
   return nullptr;
 }
 
-bool CPDF_ColorSpace::GetPatternRGB(const PatternValue& value,
-                                    float* R,
-                                    float* G,
-                                    float* B) const {
-  NOTREACHED();
-  return false;
+const CPDF_IndexedCS* CPDF_ColorSpace::AsIndexedCS() const {
+  return nullptr;
 }
 
-CPDF_ColorSpace::CPDF_ColorSpace(CPDF_Document* pDoc, int family)
-    : m_pDocument(pDoc), m_Family(family) {}
+CPDF_ColorSpace::CPDF_ColorSpace(Family family) : m_Family(family) {}
 
 CPDF_ColorSpace::~CPDF_ColorSpace() = default;
 
 void CPDF_ColorSpace::SetComponentsForStockCS(uint32_t nComponents) {
-  ASSERT(!m_pDocument);  // Stock colorspace is not associated with a document.
   m_nComponents = nComponents;
 }
 
-CPDF_CalGray::CPDF_CalGray(CPDF_Document* pDoc)
-    : CPDF_ColorSpace(pDoc, PDFCS_CALGRAY) {}
+CPDF_CalGray::CPDF_CalGray() : CPDF_ColorSpace(Family::kCalGray) {}
 
 CPDF_CalGray::~CPDF_CalGray() = default;
 
 uint32_t CPDF_CalGray::v_Load(CPDF_Document* pDoc,
                               const CPDF_Array* pArray,
                               std::set<const CPDF_Object*>* pVisited) {
-  const CPDF_Dictionary* pDict = pArray->GetDictAt(1);
+  RetainPtr<const CPDF_Dictionary> pDict = pArray->GetDictAt(1);
   if (!pDict)
     return 0;
 
-  if (!GetWhitePoint(pDict, m_WhitePoint))
+  if (!GetWhitePoint(pDict.Get(), m_WhitePoint))
     return 0;
 
-  GetBlackPoint(pDict, m_BlackPoint);
+  GetBlackPoint(pDict.Get(), m_BlackPoint);
 
-  m_Gamma = pDict->GetNumberFor("Gamma");
+  m_Gamma = pDict->GetFloatFor("Gamma");
   if (m_Gamma == 0)
     m_Gamma = kDefaultGamma;
   return 1;
 }
 
-bool CPDF_CalGray::GetRGB(const float* pBuf,
+bool CPDF_CalGray::GetRGB(pdfium::span<const float> pBuf,
                           float* R,
                           float* G,
                           float* B) const {
-  *R = *pBuf;
-  *G = *pBuf;
-  *B = *pBuf;
+  *R = pBuf[0];
+  *G = pBuf[0];
+  *B = pBuf[0];
   return true;
 }
 
-void CPDF_CalGray::TranslateImageLine(uint8_t* pDestBuf,
-                                      const uint8_t* pSrcBuf,
+void CPDF_CalGray::TranslateImageLine(pdfium::span<uint8_t> dest_span,
+                                      pdfium::span<const uint8_t> src_span,
                                       int pixels,
                                       int image_width,
                                       int image_height,
                                       bool bTransMask) const {
+  uint8_t* pDestBuf = dest_span.data();
+  const uint8_t* pSrcBuf = src_span.data();
   for (int i = 0; i < pixels; i++) {
-    *pDestBuf++ = pSrcBuf[i];
-    *pDestBuf++ = pSrcBuf[i];
-    *pDestBuf++ = pSrcBuf[i];
+    // Compiler can not conclude that src/dest don't overlap.
+    const uint8_t pix = pSrcBuf[i];
+    *pDestBuf++ = pix;
+    *pDestBuf++ = pix;
+    *pDestBuf++ = pix;
   }
 }
 
-CPDF_CalRGB::CPDF_CalRGB(CPDF_Document* pDoc)
-    : CPDF_ColorSpace(pDoc, PDFCS_CALRGB) {}
+CPDF_CalRGB::CPDF_CalRGB() : CPDF_ColorSpace(Family::kCalRGB) {}
 
 CPDF_CalRGB::~CPDF_CalRGB() = default;
 
 uint32_t CPDF_CalRGB::v_Load(CPDF_Document* pDoc,
                              const CPDF_Array* pArray,
                              std::set<const CPDF_Object*>* pVisited) {
-  const CPDF_Dictionary* pDict = pArray->GetDictAt(1);
+  RetainPtr<const CPDF_Dictionary> pDict = pArray->GetDictAt(1);
   if (!pDict)
     return 0;
 
-  if (!GetWhitePoint(pDict, m_WhitePoint))
+  if (!GetWhitePoint(pDict.Get(), m_WhitePoint))
     return 0;
 
-  GetBlackPoint(pDict, m_BlackPoint);
+  GetBlackPoint(pDict.Get(), m_BlackPoint);
 
-  const CPDF_Array* pParam = pDict->GetArrayFor("Gamma");
-  if (pParam) {
-    m_bGamma = true;
-    for (size_t i = 0; i < FX_ArraySize(m_Gamma); ++i)
-      m_Gamma[i] = pParam->GetNumberAt(i);
+  RetainPtr<const CPDF_Array> pGamma = pDict->GetArrayFor("Gamma");
+  if (pGamma) {
+    m_bHasGamma = true;
+    for (size_t i = 0; i < std::size(m_Gamma); ++i)
+      m_Gamma[i] = pGamma->GetFloatAt(i);
   }
 
-  pParam = pDict->GetArrayFor("Matrix");
-  if (pParam) {
-    m_bMatrix = true;
-    for (size_t i = 0; i < FX_ArraySize(m_Matrix); ++i)
-      m_Matrix[i] = pParam->GetNumberAt(i);
+  RetainPtr<const CPDF_Array> pMatrix = pDict->GetArrayFor("Matrix");
+  if (pMatrix) {
+    m_bHasMatrix = true;
+    for (size_t i = 0; i < std::size(m_Matrix); ++i)
+      m_Matrix[i] = pMatrix->GetFloatAt(i);
   }
   return 3;
 }
 
-bool CPDF_CalRGB::GetRGB(const float* pBuf,
+bool CPDF_CalRGB::GetRGB(pdfium::span<const float> pBuf,
                          float* R,
                          float* G,
                          float* B) const {
   float A_ = pBuf[0];
   float B_ = pBuf[1];
   float C_ = pBuf[2];
-  if (m_bGamma) {
-    A_ = FXSYS_pow(A_, m_Gamma[0]);
-    B_ = FXSYS_pow(B_, m_Gamma[1]);
-    C_ = FXSYS_pow(C_, m_Gamma[2]);
+  if (m_bHasGamma) {
+    A_ = powf(A_, m_Gamma[0]);
+    B_ = powf(B_, m_Gamma[1]);
+    C_ = powf(C_, m_Gamma[2]);
   }
 
   float X;
   float Y;
   float Z;
-  if (m_bMatrix) {
+  if (m_bHasMatrix) {
     X = m_Matrix[0] * A_ + m_Matrix[3] * B_ + m_Matrix[6] * C_;
     Y = m_Matrix[1] * A_ + m_Matrix[4] * B_ + m_Matrix[7] * C_;
     Z = m_Matrix[2] * A_ + m_Matrix[5] * B_ + m_Matrix[8] * C_;
@@ -779,34 +747,37 @@
   return true;
 }
 
-void CPDF_CalRGB::TranslateImageLine(uint8_t* pDestBuf,
-                                     const uint8_t* pSrcBuf,
+void CPDF_CalRGB::TranslateImageLine(pdfium::span<uint8_t> dest_span,
+                                     pdfium::span<const uint8_t> src_span,
                                      int pixels,
                                      int image_width,
                                      int image_height,
                                      bool bTransMask) const {
-  if (bTransMask) {
-    float Cal[3];
-    float R;
-    float G;
-    float B;
-    for (int i = 0; i < pixels; i++) {
-      Cal[0] = static_cast<float>(pSrcBuf[2]) / 255;
-      Cal[1] = static_cast<float>(pSrcBuf[1]) / 255;
-      Cal[2] = static_cast<float>(pSrcBuf[0]) / 255;
-      GetRGB(Cal, &R, &G, &B);
-      pDestBuf[0] = FXSYS_roundf(B * 255);
-      pDestBuf[1] = FXSYS_roundf(G * 255);
-      pDestBuf[2] = FXSYS_roundf(R * 255);
-      pSrcBuf += 3;
-      pDestBuf += 3;
-    }
+  uint8_t* pDestBuf = dest_span.data();
+  const uint8_t* pSrcBuf = src_span.data();
+  if (!bTransMask) {
+    fxcodec::ReverseRGB(pDestBuf, pSrcBuf, pixels);
+    return;
   }
-  fxcodec::ReverseRGB(pDestBuf, pSrcBuf, pixels);
+
+  float Cal[3];
+  float R;
+  float G;
+  float B;
+  for (int i = 0; i < pixels; i++) {
+    Cal[0] = static_cast<float>(pSrcBuf[2]) / 255;
+    Cal[1] = static_cast<float>(pSrcBuf[1]) / 255;
+    Cal[2] = static_cast<float>(pSrcBuf[0]) / 255;
+    GetRGB(Cal, &R, &G, &B);
+    pDestBuf[0] = FXSYS_roundf(B * 255);
+    pDestBuf[1] = FXSYS_roundf(G * 255);
+    pDestBuf[2] = FXSYS_roundf(R * 255);
+    pSrcBuf += 3;
+    pDestBuf += 3;
+  }
 }
 
-CPDF_LabCS::CPDF_LabCS(CPDF_Document* pDoc)
-    : CPDF_ColorSpace(pDoc, PDFCS_LAB) {}
+CPDF_LabCS::CPDF_LabCS() : CPDF_ColorSpace(Family::kLab) {}
 
 CPDF_LabCS::~CPDF_LabCS() = default;
 
@@ -814,42 +785,50 @@
                                  float* value,
                                  float* min,
                                  float* max) const {
-  ASSERT(iComponent < 3);
-  if (iComponent == 0) {
-    *min = 0.0f;
-    *max = 100 * 1.0f;
-    *value = 0.0f;
-    return;
+  DCHECK_LT(iComponent, 3);
+
+  if (iComponent > 0) {
+    float range_min = m_Ranges[iComponent * 2 - 2];
+    float range_max = m_Ranges[iComponent * 2 - 1];
+    if (range_min <= range_max) {
+      *min = range_min;
+      *max = range_max;
+      *value = std::clamp(0.0f, *min, *max);
+      return;
+    }
   }
 
-  *min = m_Ranges[iComponent * 2 - 2];
-  *max = m_Ranges[iComponent * 2 - 1];
-  *value = pdfium::clamp(0.0f, *min, *max);
+  *min = 0.0f;
+  *max = 100.0f;
+  *value = 0.0f;
 }
 
 uint32_t CPDF_LabCS::v_Load(CPDF_Document* pDoc,
                             const CPDF_Array* pArray,
                             std::set<const CPDF_Object*>* pVisited) {
-  const CPDF_Dictionary* pDict = pArray->GetDictAt(1);
+  RetainPtr<const CPDF_Dictionary> pDict = pArray->GetDictAt(1);
   if (!pDict)
     return 0;
 
-  if (!GetWhitePoint(pDict, m_WhitePoint))
+  if (!GetWhitePoint(pDict.Get(), m_WhitePoint))
     return 0;
 
-  GetBlackPoint(pDict, m_BlackPoint);
+  GetBlackPoint(pDict.Get(), m_BlackPoint);
 
-  const CPDF_Array* pParam = pDict->GetArrayFor("Range");
+  RetainPtr<const CPDF_Array> pParam = pDict->GetArrayFor("Range");
   static constexpr float kDefaultRanges[kRangesCount] = {-100.0f, 100.0f,
                                                          -100.0f, 100.0f};
-  static_assert(FX_ArraySize(kDefaultRanges) == FX_ArraySize(m_Ranges),
+  static_assert(std::size(kDefaultRanges) == std::extent<decltype(m_Ranges)>(),
                 "Range size mismatch");
-  for (size_t i = 0; i < FX_ArraySize(kDefaultRanges); ++i)
-    m_Ranges[i] = pParam ? pParam->GetNumberAt(i) : kDefaultRanges[i];
+  for (size_t i = 0; i < std::size(kDefaultRanges); ++i)
+    m_Ranges[i] = pParam ? pParam->GetFloatAt(i) : kDefaultRanges[i];
   return 3;
 }
 
-bool CPDF_LabCS::GetRGB(const float* pBuf, float* R, float* G, float* B) const {
+bool CPDF_LabCS::GetRGB(pdfium::span<const float> pBuf,
+                        float* R,
+                        float* G,
+                        float* B) const {
   float Lstar = pBuf[0];
   float astar = pBuf[1];
   float bstar = pBuf[2];
@@ -878,12 +857,14 @@
   return true;
 }
 
-void CPDF_LabCS::TranslateImageLine(uint8_t* pDestBuf,
-                                    const uint8_t* pSrcBuf,
+void CPDF_LabCS::TranslateImageLine(pdfium::span<uint8_t> dest_span,
+                                    pdfium::span<const uint8_t> src_span,
                                     int pixels,
                                     int image_width,
                                     int image_height,
                                     bool bTransMask) const {
+  uint8_t* pDestBuf = dest_span.data();
+  const uint8_t* pSrcBuf = src_span.data();
   for (int i = 0; i < pixels; i++) {
     float lab[3];
     lab[0] = pSrcBuf[0] * 100 / 255.0f;
@@ -902,22 +883,21 @@
   }
 }
 
-CPDF_ICCBasedCS::CPDF_ICCBasedCS(CPDF_Document* pDoc)
-    : CPDF_ColorSpace(pDoc, PDFCS_ICCBASED) {}
+CPDF_ICCBasedCS::CPDF_ICCBasedCS() : CPDF_BasedCS(Family::kICCBased) {}
 
 CPDF_ICCBasedCS::~CPDF_ICCBasedCS() = default;
 
 uint32_t CPDF_ICCBasedCS::v_Load(CPDF_Document* pDoc,
                                  const CPDF_Array* pArray,
                                  std::set<const CPDF_Object*>* pVisited) {
-  const CPDF_Stream* pStream = pArray->GetStreamAt(1);
+  RetainPtr<const CPDF_Stream> pStream = pArray->GetStreamAt(1);
   if (!pStream)
     return 0;
 
   // The PDF 1.7 spec says the number of components must be valid. While some
   // PDF viewers tolerate invalid values, Acrobat does not, so be consistent
   // with Acrobat and reject bad values.
-  const CPDF_Dictionary* pDict = pStream->GetDict();
+  RetainPtr<const CPDF_Dictionary> pDict = pStream->GetDict();
   int32_t nDictComponents = pDict ? pDict->GetIntegerFor("N") : 0;
   if (!IsValidIccComponents(nDictComponents))
     return 0;
@@ -938,39 +918,38 @@
   // SRGB, a profile PDFium recognizes but does not support well, then try the
   // alternate profile.
   if (!m_pProfile->IsSupported() &&
-      !FindAlternateProfile(pDoc, pDict, pVisited, nComponents)) {
+      !FindAlternateProfile(pDoc, pDict.Get(), pVisited, nComponents)) {
     // If there is no alternate profile, use a stock profile as mentioned in
     // the PDF 1.7 spec in table 4.16 in the "Alternate" key description.
-    ASSERT(!m_pAlterCS);
-    m_pAlterCS = GetStockAlternateProfile(nComponents);
+    DCHECK(!m_pBaseCS);
+    m_pBaseCS = GetStockAlternateProfile(nComponents);
   }
 
-  m_pRanges = GetRanges(pDict, nComponents);
+  m_pRanges = GetRanges(pDict.Get(), nComponents);
   return nComponents;
 }
 
-bool CPDF_ICCBasedCS::GetRGB(const float* pBuf,
+bool CPDF_ICCBasedCS::GetRGB(pdfium::span<const float> pBuf,
                              float* R,
                              float* G,
                              float* B) const {
-  ASSERT(m_pProfile);
+  DCHECK(m_pProfile);
   if (m_pProfile->IsSRGB()) {
     *R = pBuf[0];
     *G = pBuf[1];
     *B = pBuf[2];
     return true;
   }
-  if (m_pProfile->transform()) {
+  if (m_pProfile->IsSupported()) {
     float rgb[3];
-    IccModule::Translate(m_pProfile->transform(), CountComponents(), pBuf, rgb);
+    m_pProfile->Translate(pBuf.first(CountComponents()), rgb);
     *R = rgb[0];
     *G = rgb[1];
     *B = rgb[2];
     return true;
   }
-
-  if (m_pAlterCS)
-    return m_pAlterCS->GetRGB(pBuf, R, G, B);
+  if (m_pBaseCS)
+    return m_pBaseCS->GetRGB(pBuf, R, G, B);
 
   *R = 0.0f;
   *G = 0.0f;
@@ -978,33 +957,27 @@
   return true;
 }
 
-void CPDF_ICCBasedCS::EnableStdConversion(bool bEnabled) {
-  CPDF_ColorSpace::EnableStdConversion(bEnabled);
-  if (m_pAlterCS)
-    m_pAlterCS->EnableStdConversion(bEnabled);
-}
-
-void CPDF_ICCBasedCS::TranslateImageLine(uint8_t* pDestBuf,
-                                         const uint8_t* pSrcBuf,
+void CPDF_ICCBasedCS::TranslateImageLine(pdfium::span<uint8_t> dest_span,
+                                         pdfium::span<const uint8_t> src_span,
                                          int pixels,
                                          int image_width,
                                          int image_height,
                                          bool bTransMask) const {
   if (m_pProfile->IsSRGB()) {
-    fxcodec::ReverseRGB(pDestBuf, pSrcBuf, pixels);
+    fxcodec::ReverseRGB(dest_span.data(), src_span.data(), pixels);
     return;
   }
-  if (!m_pProfile->transform()) {
-    if (m_pAlterCS) {
-      m_pAlterCS->TranslateImageLine(pDestBuf, pSrcBuf, pixels, image_width,
-                                     image_height, false);
+  if (!m_pProfile->IsSupported()) {
+    if (m_pBaseCS) {
+      m_pBaseCS->TranslateImageLine(dest_span, src_span, pixels, image_width,
+                                    image_height, false);
     }
     return;
   }
 
   // |nMaxColors| will not overflow since |nComponents| is limited in size.
   const uint32_t nComponents = CountComponents();
-  ASSERT(IsValidIccComponents(nComponents));
+  DCHECK(IsValidIccComponents(nComponents));
   int nMaxColors = 1;
   for (uint32_t i = 0; i < nComponents; i++)
     nMaxColors *= 52;
@@ -1016,15 +989,13 @@
     if (nPixelCount.IsValid())
       bTranslate = nPixelCount.ValueOrDie() < nMaxColors * 3 / 2;
   }
-  if (bTranslate) {
-    IccModule::TranslateScanline(m_pProfile->transform(), pDestBuf, pSrcBuf,
-                                 pixels);
+  if (bTranslate && m_pProfile->IsSupported()) {
+    m_pProfile->TranslateScanline(dest_span, src_span, pixels);
     return;
   }
-
   if (m_pCache.empty()) {
-    m_pCache = pdfium::Vector2D<uint8_t>(nMaxColors, 3);
-    auto temp_src = pdfium::Vector2D<uint8_t>(nMaxColors, nComponents);
+    m_pCache.resize(Fx2DSizeOrDie(nMaxColors, 3));
+    DataVector<uint8_t> temp_src(Fx2DSizeOrDie(nMaxColors, nComponents));
     size_t src_index = 0;
     for (int i = 0; i < nMaxColors; i++) {
       uint32_t color = i;
@@ -1035,9 +1006,12 @@
         order /= 52;
       }
     }
-    IccModule::TranslateScanline(m_pProfile->transform(), m_pCache.data(),
-                                 temp_src.data(), nMaxColors);
+    if (m_pProfile->IsSupported()) {
+      m_pProfile->TranslateScanline(m_pCache, temp_src, nMaxColors);
+    }
   }
+  uint8_t* pDestBuf = dest_span.data();
+  const uint8_t* pSrcBuf = src_span.data();
   for (int i = 0; i < pixels; i++) {
     int index = 0;
     for (uint32_t c = 0; c < nComponents; c++) {
@@ -1054,10 +1028,10 @@
 bool CPDF_ICCBasedCS::IsNormal() const {
   if (m_pProfile->IsSRGB())
     return true;
-  if (m_pProfile->transform())
-    return m_pProfile->transform()->IsNormal();
-  if (m_pAlterCS)
-    return m_pAlterCS->IsNormal();
+  if (m_pProfile->IsSupported())
+    return m_pProfile->IsNormal();
+  if (m_pBaseCS)
+    return m_pBaseCS->IsNormal();
   return false;
 }
 
@@ -1066,21 +1040,22 @@
     const CPDF_Dictionary* pDict,
     std::set<const CPDF_Object*>* pVisited,
     uint32_t nExpectedComponents) {
-  const CPDF_Object* pAlterCSObj = pDict->GetDirectObjectFor("Alternate");
+  RetainPtr<const CPDF_Object> pAlterCSObj =
+      pDict->GetDirectObjectFor("Alternate");
   if (!pAlterCSObj)
     return false;
 
-  auto pAlterCS = CPDF_ColorSpace::Load(pDoc, pAlterCSObj, pVisited);
+  auto pAlterCS = CPDF_ColorSpace::Load(pDoc, pAlterCSObj.Get(), pVisited);
   if (!pAlterCS)
     return false;
 
-  if (pAlterCS->GetFamily() == PDFCS_PATTERN)
+  if (pAlterCS->GetFamily() == Family::kPattern)
     return false;
 
   if (pAlterCS->CountComponents() != nExpectedComponents)
     return false;
 
-  m_pAlterCS = std::move(pAlterCS);
+  m_pBaseCS = std::move(pAlterCS);
   return true;
 }
 
@@ -1088,122 +1063,32 @@
 RetainPtr<CPDF_ColorSpace> CPDF_ICCBasedCS::GetStockAlternateProfile(
     uint32_t nComponents) {
   if (nComponents == 1)
-    return GetStockCS(PDFCS_DEVICEGRAY);
+    return GetStockCS(Family::kDeviceGray);
   if (nComponents == 3)
-    return GetStockCS(PDFCS_DEVICERGB);
+    return GetStockCS(Family::kDeviceRGB);
   if (nComponents == 4)
-    return GetStockCS(PDFCS_DEVICECMYK);
-  NOTREACHED();
-  return nullptr;
+    return GetStockCS(Family::kDeviceCMYK);
+  NOTREACHED_NORETURN();
 }
 
 // static
 std::vector<float> CPDF_ICCBasedCS::GetRanges(const CPDF_Dictionary* pDict,
                                               uint32_t nComponents) {
-  ASSERT(IsValidIccComponents(nComponents));
+  DCHECK(IsValidIccComponents(nComponents));
+  RetainPtr<const CPDF_Array> pRanges = pDict->GetArrayFor("Range");
+  if (pRanges && pRanges->size() >= nComponents * 2)
+    return ReadArrayElementsToVector(pRanges.Get(), nComponents * 2);
 
   std::vector<float> ranges;
-  const CPDF_Array* pRanges = pDict->GetArrayFor("Range");
-  if (pRanges) {
-    ranges = ReadArrayElementsToVector(pRanges, nComponents * 2);
-  } else {
-    ranges.reserve(nComponents * 2);
-    for (uint32_t i = 0; i < nComponents; i++) {
-      ranges.push_back(0.0f);
-      ranges.push_back(1.0f);
-    }
+  ranges.reserve(nComponents * 2);
+  for (uint32_t i = 0; i < nComponents; i++) {
+    ranges.push_back(0.0f);
+    ranges.push_back(1.0f);
   }
   return ranges;
 }
 
-CPDF_IndexedCS::CPDF_IndexedCS(CPDF_Document* pDoc)
-    : CPDF_ColorSpace(pDoc, PDFCS_INDEXED) {}
-
-CPDF_IndexedCS::~CPDF_IndexedCS() = default;
-
-uint32_t CPDF_IndexedCS::v_Load(CPDF_Document* pDoc,
-                                const CPDF_Array* pArray,
-                                std::set<const CPDF_Object*>* pVisited) {
-  if (pArray->size() < 4)
-    return 0;
-
-  const CPDF_Object* pBaseObj = pArray->GetDirectObjectAt(1);
-  if (pBaseObj == m_pArray)
-    return 0;
-
-  auto* pDocPageData = CPDF_DocPageData::FromDocument(pDoc);
-  m_pBaseCS = pDocPageData->GetColorSpaceGuarded(pBaseObj, nullptr, pVisited);
-  if (!m_pBaseCS)
-    return 0;
-
-  // The base color space cannot be a Pattern or Indexed space, according to the
-  // PDF 1.7 spec, page 263.
-  int family = m_pBaseCS->GetFamily();
-  if (family == PDFCS_INDEXED || family == PDFCS_PATTERN)
-    return 0;
-
-  m_nBaseComponents = m_pBaseCS->CountComponents();
-  m_pCompMinMax = pdfium::Vector2D<float>(m_nBaseComponents, 2);
-  float defvalue;
-  for (uint32_t i = 0; i < m_nBaseComponents; i++) {
-    m_pBaseCS->GetDefaultValue(i, &defvalue, &m_pCompMinMax[i * 2],
-                               &m_pCompMinMax[i * 2 + 1]);
-    m_pCompMinMax[i * 2 + 1] -= m_pCompMinMax[i * 2];
-  }
-  m_MaxIndex = pArray->GetIntegerAt(2);
-
-  const CPDF_Object* pTableObj = pArray->GetDirectObjectAt(3);
-  if (!pTableObj)
-    return 0;
-
-  if (const CPDF_String* pString = pTableObj->AsString()) {
-    m_Table = pString->GetString();
-  } else if (const CPDF_Stream* pStream = pTableObj->AsStream()) {
-    auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
-    pAcc->LoadAllDataFiltered();
-    m_Table = ByteStringView(pAcc->GetSpan());
-  }
-  return 1;
-}
-
-bool CPDF_IndexedCS::GetRGB(const float* pBuf,
-                            float* R,
-                            float* G,
-                            float* B) const {
-  int32_t index = static_cast<int32_t>(*pBuf);
-  if (index < 0 || index > m_MaxIndex)
-    return false;
-
-  if (m_nBaseComponents) {
-    FX_SAFE_SIZE_T length = index;
-    length += 1;
-    length *= m_nBaseComponents;
-    if (!length.IsValid() || length.ValueOrDie() > m_Table.GetLength()) {
-      *R = 0;
-      *G = 0;
-      *B = 0;
-      return false;
-    }
-  }
-  std::vector<float> comps(m_nBaseComponents);
-  const uint8_t* pTable = m_Table.raw_str();
-  for (uint32_t i = 0; i < m_nBaseComponents; ++i) {
-    comps[i] =
-        m_pCompMinMax[i * 2] +
-        m_pCompMinMax[i * 2 + 1] * pTable[index * m_nBaseComponents + i] / 255;
-  }
-  ASSERT(m_nBaseComponents == m_pBaseCS->CountComponents());
-  return m_pBaseCS->GetRGB(comps.data(), R, G, B);
-}
-
-void CPDF_IndexedCS::EnableStdConversion(bool bEnabled) {
-  CPDF_ColorSpace::EnableStdConversion(bEnabled);
-  if (m_pBaseCS)
-    m_pBaseCS->EnableStdConversion(bEnabled);
-}
-
-CPDF_SeparationCS::CPDF_SeparationCS(CPDF_Document* pDoc)
-    : CPDF_ColorSpace(pDoc, PDFCS_SEPARATION) {}
+CPDF_SeparationCS::CPDF_SeparationCS() : CPDF_BasedCS(Family::kSeparation) {}
 
 CPDF_SeparationCS::~CPDF_SeparationCS() = default;
 
@@ -1219,74 +1104,64 @@
 uint32_t CPDF_SeparationCS::v_Load(CPDF_Document* pDoc,
                                    const CPDF_Array* pArray,
                                    std::set<const CPDF_Object*>* pVisited) {
-  ByteString name = pArray->GetStringAt(1);
-  if (name == "None") {
-    m_Type = None;
+  m_IsNoneType = pArray->GetByteStringAt(1) == "None";
+  if (m_IsNoneType)
     return 1;
-  }
 
-  m_Type = Colorant;
-  const CPDF_Object* pAltCS = pArray->GetDirectObjectAt(2);
-  if (pAltCS == m_pArray)
+  RetainPtr<const CPDF_Object> pAltArray = pArray->GetDirectObjectAt(2);
+  if (HasSameArray(pAltArray.Get()))
     return 0;
 
-  m_pAltCS = Load(pDoc, pAltCS, pVisited);
-  if (!m_pAltCS)
+  m_pBaseCS = Load(pDoc, pAltArray.Get(), pVisited);
+  if (!m_pBaseCS)
     return 0;
 
-  if (m_pAltCS->IsSpecial())
+  if (m_pBaseCS->IsSpecial())
     return 0;
 
-  const CPDF_Object* pFuncObj = pArray->GetDirectObjectAt(3);
+  RetainPtr<const CPDF_Object> pFuncObj = pArray->GetDirectObjectAt(3);
   if (pFuncObj && !pFuncObj->IsName()) {
-    auto pFunc = CPDF_Function::Load(pFuncObj);
-    if (pFunc && pFunc->CountOutputs() >= m_pAltCS->CountComponents())
+    auto pFunc = CPDF_Function::Load(std::move(pFuncObj));
+    if (pFunc && pFunc->CountOutputs() >= m_pBaseCS->CountComponents())
       m_pFunc = std::move(pFunc);
   }
   return 1;
 }
 
-bool CPDF_SeparationCS::GetRGB(const float* pBuf,
+bool CPDF_SeparationCS::GetRGB(pdfium::span<const float> pBuf,
                                float* R,
                                float* G,
                                float* B) const {
-  if (m_Type == None)
+  if (m_IsNoneType)
     return false;
 
   if (!m_pFunc) {
-    if (!m_pAltCS)
+    if (!m_pBaseCS)
       return false;
 
-    int nComps = m_pAltCS->CountComponents();
+    int nComps = m_pBaseCS->CountComponents();
     std::vector<float> results(nComps);
     for (int i = 0; i < nComps; i++)
-      results[i] = *pBuf;
-    return m_pAltCS->GetRGB(results.data(), R, G, B);
+      results[i] = pBuf[0];
+    return m_pBaseCS->GetRGB(results, R, G, B);
   }
 
   // Using at least 16 elements due to the call m_pAltCS->GetRGB() below.
   std::vector<float> results(std::max(m_pFunc->CountOutputs(), 16u));
-  int nresults = 0;
-  if (!m_pFunc->Call(pBuf, 1, results.data(), &nresults) || nresults == 0)
+  uint32_t nresults = m_pFunc->Call(pBuf.first(1), results).value_or(0);
+  if (nresults == 0)
     return false;
 
-  if (m_pAltCS)
-    return m_pAltCS->GetRGB(results.data(), R, G, B);
+  if (m_pBaseCS)
+    return m_pBaseCS->GetRGB(results, R, G, B);
 
-  R = 0;
-  G = 0;
-  B = 0;
+  *R = 0.0f;
+  *G = 0.0f;
+  *B = 0.0f;
   return false;
 }
 
-void CPDF_SeparationCS::EnableStdConversion(bool bEnabled) {
-  CPDF_ColorSpace::EnableStdConversion(bEnabled);
-  if (m_pAltCS)
-    m_pAltCS->EnableStdConversion(bEnabled);
-}
-
-CPDF_DeviceNCS::CPDF_DeviceNCS(CPDF_Document* pDoc)
-    : CPDF_ColorSpace(pDoc, PDFCS_DEVICEN) {}
+CPDF_DeviceNCS::CPDF_DeviceNCS() : CPDF_BasedCS(Family::kDeviceN) {}
 
 CPDF_DeviceNCS::~CPDF_DeviceNCS() = default;
 
@@ -1302,29 +1177,29 @@
 uint32_t CPDF_DeviceNCS::v_Load(CPDF_Document* pDoc,
                                 const CPDF_Array* pArray,
                                 std::set<const CPDF_Object*>* pVisited) {
-  const CPDF_Array* pObj = ToArray(pArray->GetDirectObjectAt(1));
+  RetainPtr<const CPDF_Array> pObj = ToArray(pArray->GetDirectObjectAt(1));
   if (!pObj)
     return 0;
 
-  const CPDF_Object* pAltCS = pArray->GetDirectObjectAt(2);
-  if (!pAltCS || pAltCS == m_pArray)
+  RetainPtr<const CPDF_Object> pAltCS = pArray->GetDirectObjectAt(2);
+  if (!pAltCS || HasSameArray(pAltCS.Get()))
     return 0;
 
-  m_pAltCS = Load(pDoc, pAltCS, pVisited);
+  m_pBaseCS = Load(pDoc, pAltCS.Get(), pVisited);
   m_pFunc = CPDF_Function::Load(pArray->GetDirectObjectAt(3));
-  if (!m_pAltCS || !m_pFunc)
+  if (!m_pBaseCS || !m_pFunc)
     return 0;
 
-  if (m_pAltCS->IsSpecial())
+  if (m_pBaseCS->IsSpecial())
     return 0;
 
-  if (m_pFunc->CountOutputs() < m_pAltCS->CountComponents())
+  if (m_pFunc->CountOutputs() < m_pBaseCS->CountComponents())
     return 0;
 
-  return pObj->size();
+  return fxcrt::CollectionSize<uint32_t>(*pObj);
 }
 
-bool CPDF_DeviceNCS::GetRGB(const float* pBuf,
+bool CPDF_DeviceNCS::GetRGB(pdfium::span<const float> pBuf,
                             float* R,
                             float* G,
                             float* B) const {
@@ -1333,18 +1208,12 @@
 
   // Using at least 16 elements due to the call m_pAltCS->GetRGB() below.
   std::vector<float> results(std::max(m_pFunc->CountOutputs(), 16u));
-  int nresults = 0;
-  if (!m_pFunc->Call(pBuf, CountComponents(), results.data(), &nresults) ||
-      nresults == 0) {
+  uint32_t nresults =
+      m_pFunc->Call(pBuf.first(CountComponents()), pdfium::make_span(results))
+          .value_or(0);
+
+  if (nresults == 0)
     return false;
-  }
 
-  return m_pAltCS->GetRGB(results.data(), R, G, B);
-}
-
-void CPDF_DeviceNCS::EnableStdConversion(bool bEnabled) {
-  CPDF_ColorSpace::EnableStdConversion(bEnabled);
-  if (m_pAltCS) {
-    m_pAltCS->EnableStdConversion(bEnabled);
-  }
+  return m_pBaseCS->GetRGB(results, R, G, B);
 }
diff --git a/core/fpdfapi/page/cpdf_colorspace.h b/core/fpdfapi/page/cpdf_colorspace.h
index 75928d2..39f84ec 100644
--- a/core/fpdfapi/page/cpdf_colorspace.h
+++ b/core/fpdfapi/page/cpdf_colorspace.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,34 +7,25 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_COLORSPACE_H_
 #define CORE_FPDFAPI_PAGE_CPDF_COLORSPACE_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <array>
-#include <memory>
 #include <set>
+#include <utility>
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_pattern.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_object.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
-#define PDFCS_DEVICEGRAY 1
-#define PDFCS_DEVICERGB 2
-#define PDFCS_DEVICECMYK 3
-#define PDFCS_CALGRAY 4
-#define PDFCS_CALRGB 5
-#define PDFCS_LAB 6
-#define PDFCS_ICCBASED 7
-#define PDFCS_SEPARATION 8
-#define PDFCS_DEVICEN 9
-#define PDFCS_INDEXED 10
-#define PDFCS_PATTERN 11
-
-class CPDF_Array;
 class CPDF_Document;
-class CPDF_Object;
+class CPDF_IndexedCS;
 class CPDF_PatternCS;
 
 constexpr size_t kMaxPatternColorComps = 16;
@@ -51,43 +42,60 @@
     return {m_Comps.data(), m_Comps.size()};
   }
 
-  CPDF_Pattern* GetPattern() const { return m_pRetainedPattern.Get(); }
-  void SetPattern(const RetainPtr<CPDF_Pattern>& pPattern) {
-    m_pRetainedPattern = pPattern;
+  RetainPtr<CPDF_Pattern> GetPattern() const { return m_pRetainedPattern; }
+  void SetPattern(RetainPtr<CPDF_Pattern> pPattern) {
+    m_pRetainedPattern = std::move(pPattern);
   }
 
  private:
   RetainPtr<CPDF_Pattern> m_pRetainedPattern;
-  std::array<float, kMaxPatternColorComps> m_Comps;
+  std::array<float, kMaxPatternColorComps> m_Comps{};
 };
 
 class CPDF_ColorSpace : public Retainable, public Observable {
  public:
-  static RetainPtr<CPDF_ColorSpace> GetStockCS(int Family);
-  static RetainPtr<CPDF_ColorSpace> ColorspaceFromName(const ByteString& name);
-  static RetainPtr<CPDF_ColorSpace> Load(CPDF_Document* pDoc,
-                                         CPDF_Object* pObj);
+  enum class Family {
+    kUnknown = 0,
+    kDeviceGray = 1,
+    kDeviceRGB = 2,
+    kDeviceCMYK = 3,
+    kCalGray = 4,
+    kCalRGB = 5,
+    kLab = 6,
+    kICCBased = 7,
+    kSeparation = 8,
+    kDeviceN = 9,
+    kIndexed = 10,
+    kPattern = 11,
+  };
+
+  static RetainPtr<CPDF_ColorSpace> GetStockCS(Family family);
+  static RetainPtr<CPDF_ColorSpace> GetStockCSForName(const ByteString& name);
   static RetainPtr<CPDF_ColorSpace> Load(
       CPDF_Document* pDoc,
       const CPDF_Object* pObj,
       std::set<const CPDF_Object*>* pVisited);
-  static uint32_t ComponentsForFamily(int family);
-  static bool IsValidIccComponents(int components);
 
-  const CPDF_Array* GetArray() const { return m_pArray.Get(); }
-  CPDF_Document* GetDocument() const { return m_pDocument.Get(); }
+  static RetainPtr<CPDF_ColorSpace> AllocateColorSpaceForID(
+      CPDF_Document* pDocument,
+      uint32_t family_id);
+
+  static uint32_t ComponentsForFamily(Family family);
+  static bool IsValidIccComponents(int components);
 
   // Should only be called if this colorspace is not a pattern.
   std::vector<float> CreateBufAndSetDefaultColor() const;
 
   uint32_t CountComponents() const;
-  int GetFamily() const { return m_Family; }
+  Family GetFamily() const { return m_Family; }
   bool IsSpecial() const {
-    return GetFamily() == PDFCS_SEPARATION || GetFamily() == PDFCS_DEVICEN ||
-           GetFamily() == PDFCS_INDEXED || GetFamily() == PDFCS_PATTERN;
+    return GetFamily() == Family::kSeparation ||
+           GetFamily() == Family::kDeviceN || GetFamily() == Family::kIndexed ||
+           GetFamily() == Family::kPattern;
   }
 
-  virtual bool GetRGB(const float* pBuf,
+  // Use CPDF_Pattern::GetPatternRGB() instead of GetRGB() for patterns.
+  virtual bool GetRGB(pdfium::span<const float> pBuf,
                       float* R,
                       float* G,
                       float* B) const = 0;
@@ -96,8 +104,9 @@
                                float* value,
                                float* min,
                                float* max) const;
-  virtual void TranslateImageLine(uint8_t* dest_buf,
-                                  const uint8_t* src_buf,
+
+  virtual void TranslateImageLine(pdfium::span<uint8_t> dest_span,
+                                  pdfium::span<const uint8_t> src_span,
                                   int pixels,
                                   int image_width,
                                   int image_height,
@@ -105,18 +114,14 @@
   virtual void EnableStdConversion(bool bEnabled);
   virtual bool IsNormal() const;
 
-  // Returns |this| as a CPDF_PatternCS* if |this| is a pattern.
-  virtual CPDF_PatternCS* AsPatternCS();
+  // Returns `this` as a CPDF_PatternCS* if `this` is a pattern.
   virtual const CPDF_PatternCS* AsPatternCS() const;
 
-  // Use instead of GetRGB() for patterns.
-  virtual bool GetPatternRGB(const PatternValue& value,
-                             float* R,
-                             float* G,
-                             float* B) const;
+  // Returns `this` as a CPDF_IndexedCS* if `this` is indexed.
+  virtual const CPDF_IndexedCS* AsIndexedCS() const;
 
  protected:
-  CPDF_ColorSpace(CPDF_Document* pDoc, int family);
+  explicit CPDF_ColorSpace(Family family);
   ~CPDF_ColorSpace() override;
 
   // Returns the number of components, or 0 on failure.
@@ -128,13 +133,20 @@
   // components count.
   void SetComponentsForStockCS(uint32_t nComponents);
 
-  UnownedPtr<CPDF_Document> const m_pDocument;
-  RetainPtr<const CPDF_Array> m_pArray;
-  const int m_Family;
-  uint32_t m_dwStdConversion = 0;
+  bool IsStdConversionEnabled() const { return m_dwStdConversion != 0; }
+  bool HasSameArray(const CPDF_Object* pObj) const { return m_pArray == pObj; }
 
  private:
+  friend class CPDF_CalGray_TranslateImageLine_Test;
+  friend class CPDF_CalRGB_TranslateImageLine_Test;
+
+  static RetainPtr<CPDF_ColorSpace> AllocateColorSpace(
+      ByteStringView bsFamilyName);
+
+  const Family m_Family;
+  uint32_t m_dwStdConversion = 0;
   uint32_t m_nComponents = 0;
+  RetainPtr<const CPDF_Array> m_pArray;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_COLORSPACE_H_
diff --git a/core/fpdfapi/page/cpdf_colorspace_unittest.cpp b/core/fpdfapi/page/cpdf_colorspace_unittest.cpp
new file mode 100644
index 0000000..d0196fe
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_colorspace_unittest.cpp
@@ -0,0 +1,52 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/page/cpdf_colorspace.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include "core/fxcrt/retain_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(CPDF_CalGray, TranslateImageLine) {
+  const uint8_t kSrc[12] = {255, 0, 0, 0, 255, 0, 0, 0, 255, 128, 128, 128};
+  const uint8_t kExpect[12] = {255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+  RetainPtr<CPDF_ColorSpace> pCal = CPDF_ColorSpace::AllocateColorSpace("CalG");
+  ASSERT_TRUE(pCal);
+
+  uint8_t dst[12];
+  memset(dst, 0xbd, sizeof(dst));
+  pCal->TranslateImageLine(dst, kSrc, 4, 4, 1, true);
+  for (size_t i = 0; i < 12; ++i)
+    EXPECT_EQ(dst[i], kExpect[i]) << " at " << i;
+
+  memset(dst, 0xbd, sizeof(dst));
+  pCal->TranslateImageLine(dst, kSrc, 4, 4, 1, false);
+  for (size_t i = 0; i < 12; ++i)
+    EXPECT_EQ(dst[i], kExpect[i]) << " at " << i;
+}
+
+TEST(CPDF_CalRGB, TranslateImageLine) {
+  const uint8_t kSrc[12] = {255, 0, 0, 0, 255, 0, 0, 0, 255, 128, 128, 128};
+  const uint8_t kExpectMask[12] = {255, 58, 0,   0,   255, 0,
+                                   70,  0,  255, 188, 188, 188};
+  const uint8_t kExpectNomask[12] = {0,   0, 255, 0,   255, 0,
+                                     255, 0, 0,   128, 128, 128};
+
+  RetainPtr<CPDF_ColorSpace> pCal = CPDF_ColorSpace::AllocateColorSpace("CalR");
+  ASSERT_TRUE(pCal);
+
+  uint8_t dst[12];
+  memset(dst, 0xbd, sizeof(dst));
+  pCal->TranslateImageLine(dst, kSrc, 4, 4, 1, true);
+  for (size_t i = 0; i < 12; ++i)
+    EXPECT_EQ(dst[i], kExpectMask[i]) << " at " << i;
+
+  memset(dst, 0xbd, sizeof(dst));
+  pCal->TranslateImageLine(dst, kSrc, 4, 4, 1, false);
+  for (size_t i = 0; i < 12; ++i)
+    EXPECT_EQ(dst[i], kExpectNomask[i]) << " at " << i;
+}
diff --git a/core/fpdfapi/page/cpdf_colorstate.cpp b/core/fpdfapi/page/cpdf_colorstate.cpp
index 50dbb71..a03fd43 100644
--- a/core/fpdfapi/page/cpdf_colorstate.cpp
+++ b/core/fpdfapi/page/cpdf_colorstate.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,17 +6,20 @@
 
 #include "core/fpdfapi/page/cpdf_colorstate.h"
 
+#include <utility>
+
 #include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fpdfapi/page/cpdf_pattern.h"
 #include "core/fpdfapi/page/cpdf_tilingpattern.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/check.h"
 
-CPDF_ColorState::CPDF_ColorState() {}
+CPDF_ColorState::CPDF_ColorState() = default;
 
 CPDF_ColorState::CPDF_ColorState(const CPDF_ColorState& that)
     : m_Ref(that.m_Ref) {}
 
-CPDF_ColorState::~CPDF_ColorState() {}
+CPDF_ColorState::~CPDF_ColorState() = default;
 
 void CPDF_ColorState::Emplace() {
   m_Ref.Emplace();
@@ -70,72 +73,74 @@
   return pColor && !pColor->IsNull();
 }
 
-void CPDF_ColorState::SetFillColor(const RetainPtr<CPDF_ColorSpace>& pCS,
-                                   const std::vector<float>& values) {
+void CPDF_ColorState::SetFillColor(RetainPtr<CPDF_ColorSpace> colorspace,
+                                   std::vector<float> values) {
   ColorData* pData = m_Ref.GetPrivateCopy();
-  SetColor(pCS, values, &pData->m_FillColor, &pData->m_FillColorRef);
+  SetColor(std::move(colorspace), std::move(values), &pData->m_FillColor,
+           &pData->m_FillColorRef);
 }
 
-void CPDF_ColorState::SetStrokeColor(const RetainPtr<CPDF_ColorSpace>& pCS,
-                                     const std::vector<float>& values) {
+void CPDF_ColorState::SetStrokeColor(RetainPtr<CPDF_ColorSpace> colorspace,
+                                     std::vector<float> values) {
   ColorData* pData = m_Ref.GetPrivateCopy();
-  SetColor(pCS, values, &pData->m_StrokeColor, &pData->m_StrokeColorRef);
+  SetColor(std::move(colorspace), std::move(values), &pData->m_StrokeColor,
+           &pData->m_StrokeColorRef);
 }
 
-void CPDF_ColorState::SetFillPattern(const RetainPtr<CPDF_Pattern>& pPattern,
-                                     const std::vector<float>& values) {
+void CPDF_ColorState::SetFillPattern(RetainPtr<CPDF_Pattern> pattern,
+                                     pdfium::span<float> values) {
   ColorData* pData = m_Ref.GetPrivateCopy();
-  SetPattern(pPattern, values, &pData->m_FillColor, &pData->m_FillColorRef);
+  SetPattern(std::move(pattern), values, &pData->m_FillColor,
+             &pData->m_FillColorRef);
 }
 
-void CPDF_ColorState::SetStrokePattern(const RetainPtr<CPDF_Pattern>& pPattern,
-                                       const std::vector<float>& values) {
+void CPDF_ColorState::SetStrokePattern(RetainPtr<CPDF_Pattern> pattern,
+                                       pdfium::span<float> values) {
   ColorData* pData = m_Ref.GetPrivateCopy();
-  SetPattern(pPattern, values, &pData->m_StrokeColor, &pData->m_StrokeColorRef);
+  SetPattern(std::move(pattern), values, &pData->m_StrokeColor,
+             &pData->m_StrokeColorRef);
 }
 
-void CPDF_ColorState::SetColor(const RetainPtr<CPDF_ColorSpace>& pCS,
-                               const std::vector<float>& values,
+void CPDF_ColorState::SetColor(RetainPtr<CPDF_ColorSpace> colorspace,
+                               std::vector<float> values,
                                CPDF_Color* color,
                                FX_COLORREF* colorref) {
-  ASSERT(color);
-  ASSERT(colorref);
+  DCHECK(color);
+  DCHECK(colorref);
 
-  if (pCS)
-    color->SetColorSpace(pCS);
-  else if (color->IsNull())
-    color->SetColorSpace(CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY));
-
+  if (colorspace) {
+    color->SetColorSpace(std::move(colorspace));
+  } else if (color->IsNull()) {
+    color->SetColorSpace(
+        CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray));
+  }
   if (color->CountComponents() > values.size())
     return;
 
   if (!color->IsPattern())
-    color->SetValueForNonPattern(values);
+    color->SetValueForNonPattern(std::move(values));
   int R;
   int G;
   int B;
   *colorref = color->GetRGB(&R, &G, &B) ? FXSYS_BGR(B, G, R) : 0xFFFFFFFF;
 }
 
-void CPDF_ColorState::SetPattern(const RetainPtr<CPDF_Pattern>& pPattern,
-                                 const std::vector<float>& values,
+void CPDF_ColorState::SetPattern(RetainPtr<CPDF_Pattern> pattern,
+                                 pdfium::span<float> values,
                                  CPDF_Color* color,
                                  FX_COLORREF* colorref) {
-  ASSERT(color);
-  ASSERT(colorref);
-
-  color->SetValueForPattern(pPattern, values);
+  DCHECK(color);
+  DCHECK(colorref);
+  color->SetValueForPattern(pattern, values);
   int R;
   int G;
   int B;
-  bool ret = color->GetRGB(&R, &G, &B);
-  if (CPDF_TilingPattern* pTilingPattern = pPattern->AsTilingPattern()) {
-    if (!ret && pTilingPattern->colored()) {
-      *colorref = 0x00BFBFBF;
-      return;
-    }
+  if (color->GetRGB(&R, &G, &B)) {
+    *colorref = FXSYS_BGR(B, G, R);
+    return;
   }
-  *colorref = ret ? FXSYS_BGR(B, G, R) : 0xFFFFFFFF;
+  CPDF_TilingPattern* tiling = pattern->AsTilingPattern();
+  *colorref = tiling && tiling->colored() ? 0x00BFBFBF : 0xFFFFFFFF;
 }
 
 CPDF_ColorState::ColorData::ColorData() = default;
@@ -151,8 +156,10 @@
 void CPDF_ColorState::ColorData::SetDefault() {
   m_FillColorRef = 0;
   m_StrokeColorRef = 0;
-  m_FillColor.SetColorSpace(CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY));
-  m_StrokeColor.SetColorSpace(CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY));
+  m_FillColor.SetColorSpace(
+      CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray));
+  m_StrokeColor.SetColorSpace(
+      CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray));
 }
 
 RetainPtr<CPDF_ColorState::ColorData> CPDF_ColorState::ColorData::Clone()
diff --git a/core/fpdfapi/page/cpdf_colorstate.h b/core/fpdfapi/page/cpdf_colorstate.h
index f0f6ebd..6ddae8a 100644
--- a/core/fpdfapi/page/cpdf_colorstate.h
+++ b/core/fpdfapi/page/cpdf_colorstate.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,12 +10,11 @@
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_color.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/shared_copy_on_write.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/containers/span.h"
 
-class CPDF_Color;
 class CPDF_ColorSpace;
 class CPDF_Pattern;
 
@@ -42,22 +41,21 @@
   CPDF_Color* GetMutableStrokeColor();
   bool HasStrokeColor() const;
 
-  void SetFillColor(const RetainPtr<CPDF_ColorSpace>& pCS,
-                    const std::vector<float>& values);
-  void SetStrokeColor(const RetainPtr<CPDF_ColorSpace>& pCS,
-                      const std::vector<float>& values);
-  void SetFillPattern(const RetainPtr<CPDF_Pattern>& pattern,
-                      const std::vector<float>& values);
-  void SetStrokePattern(const RetainPtr<CPDF_Pattern>& pattern,
-                        const std::vector<float>& values);
+  void SetFillColor(RetainPtr<CPDF_ColorSpace> colorspace,
+                    std::vector<float> values);
+  void SetStrokeColor(RetainPtr<CPDF_ColorSpace> colorspace,
+                      std::vector<float> values);
+  void SetFillPattern(RetainPtr<CPDF_Pattern> pattern,
+                      pdfium::span<float> values);
+  void SetStrokePattern(RetainPtr<CPDF_Pattern> pattern,
+                        pdfium::span<float> values);
 
   bool HasRef() const { return !!m_Ref; }
 
  private:
   class ColorData final : public Retainable {
    public:
-    template <typename T, typename... Args>
-    friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+    CONSTRUCT_VIA_MAKE_RETAIN;
 
     RetainPtr<ColorData> Clone() const;
 
@@ -74,12 +72,12 @@
     ~ColorData() override;
   };
 
-  void SetColor(const RetainPtr<CPDF_ColorSpace>& pCS,
-                const std::vector<float>& values,
+  void SetColor(RetainPtr<CPDF_ColorSpace> colorspace,
+                std::vector<float> values,
                 CPDF_Color* color,
                 FX_COLORREF* colorref);
-  void SetPattern(const RetainPtr<CPDF_Pattern>& pPattern,
-                  const std::vector<float>& values,
+  void SetPattern(RetainPtr<CPDF_Pattern> pattern,
+                  pdfium::span<float> values,
                   CPDF_Color* color,
                   FX_COLORREF* colorref);
 
diff --git a/core/fpdfapi/page/cpdf_contentmarkitem.cpp b/core/fpdfapi/page/cpdf_contentmarkitem.cpp
index a361e5b..f68742b 100644
--- a/core/fpdfapi/page/cpdf_contentmarkitem.cpp
+++ b/core/fpdfapi/page/cpdf_contentmarkitem.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,28 +13,22 @@
 CPDF_ContentMarkItem::CPDF_ContentMarkItem(ByteString name)
     : m_MarkName(std::move(name)) {}
 
-CPDF_ContentMarkItem::~CPDF_ContentMarkItem() {}
+CPDF_ContentMarkItem::~CPDF_ContentMarkItem() = default;
 
-const CPDF_Dictionary* CPDF_ContentMarkItem::GetParam() const {
+RetainPtr<const CPDF_Dictionary> CPDF_ContentMarkItem::GetParam() const {
   switch (m_ParamType) {
     case kPropertiesDict:
       return m_pPropertiesHolder->GetDictFor(m_PropertyName);
     case kDirectDict:
-      return m_pDirectDict.Get();
+      return m_pDirectDict;
     case kNone:
-    default:
       return nullptr;
   }
 }
 
-CPDF_Dictionary* CPDF_ContentMarkItem::GetParam() {
-  return const_cast<CPDF_Dictionary*>(
-      static_cast<const CPDF_ContentMarkItem*>(this)->GetParam());
-}
-
-bool CPDF_ContentMarkItem::HasMCID() const {
-  const CPDF_Dictionary* pDict = GetParam();
-  return pDict && pDict->KeyExist("MCID");
+RetainPtr<CPDF_Dictionary> CPDF_ContentMarkItem::GetParam() {
+  return pdfium::WrapRetain(
+      const_cast<CPDF_Dictionary*>(std::as_const(*this).GetParam().Get()));
 }
 
 void CPDF_ContentMarkItem::SetDirectDict(RetainPtr<CPDF_Dictionary> pDict) {
@@ -43,9 +37,9 @@
 }
 
 void CPDF_ContentMarkItem::SetPropertiesHolder(
-    CPDF_Dictionary* pHolder,
+    RetainPtr<CPDF_Dictionary> pHolder,
     const ByteString& property_name) {
   m_ParamType = kPropertiesDict;
-  m_pPropertiesHolder.Reset(pHolder);
+  m_pPropertiesHolder = std::move(pHolder);
   m_PropertyName = property_name;
 }
diff --git a/core/fpdfapi/page/cpdf_contentmarkitem.h b/core/fpdfapi/page/cpdf_contentmarkitem.h
index 15a34e9..e3a041d 100644
--- a/core/fpdfapi/page/cpdf_contentmarkitem.h
+++ b/core/fpdfapi/page/cpdf_contentmarkitem.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,7 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_CONTENTMARKITEM_H_
 #define CORE_FPDFAPI_PAGE_CPDF_CONTENTMARKITEM_H_
 
-#include <memory>
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Dictionary;
@@ -19,21 +16,22 @@
  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; }
-  const CPDF_Dictionary* GetParam() const;
-  CPDF_Dictionary* GetParam();
+  RetainPtr<const CPDF_Dictionary> GetParam() const;
+  RetainPtr<CPDF_Dictionary> GetParam();
   const ByteString& GetPropertyName() const { return m_PropertyName; }
-  bool HasMCID() const;
 
   void SetDirectDict(RetainPtr<CPDF_Dictionary> pDict);
-  void SetPropertiesHolder(CPDF_Dictionary* pHolder,
+  void SetPropertiesHolder(RetainPtr<CPDF_Dictionary> pHolder,
                            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.cpp b/core/fpdfapi/page/cpdf_contentmarks.cpp
index 0b95ac5..4cb439e 100644
--- a/core/fpdfapi/page/cpdf_contentmarks.cpp
+++ b/core/fpdfapi/page/cpdf_contentmarks.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,14 +10,14 @@
 #include <utility>
 
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check_op.h"
 
-CPDF_ContentMarks::CPDF_ContentMarks() {}
+CPDF_ContentMarks::CPDF_ContentMarks() = default;
 
-CPDF_ContentMarks::~CPDF_ContentMarks() {}
+CPDF_ContentMarks::~CPDF_ContentMarks() = default;
 
 std::unique_ptr<CPDF_ContentMarks> CPDF_ContentMarks::Clone() {
-  auto result = pdfium::MakeUnique<CPDF_ContentMarks>();
+  auto result = std::make_unique<CPDF_ContentMarks>();
   if (m_pMarkData)
     result->m_pMarkData = pdfium::MakeRetain<MarkData>(*m_pMarkData);
   return result;
@@ -32,12 +32,10 @@
 }
 
 CPDF_ContentMarkItem* CPDF_ContentMarks::GetItem(size_t index) {
-  return const_cast<CPDF_ContentMarkItem*>(
-      static_cast<const CPDF_ContentMarks*>(this)->GetItem(index));
+  return m_pMarkData->GetItem(index);
 }
 
 const CPDF_ContentMarkItem* CPDF_ContentMarks::GetItem(size_t index) const {
-  ASSERT(index < CountItems());
   return m_pMarkData->GetItem(index);
 }
 
@@ -50,18 +48,20 @@
   m_pMarkData->AddMark(std::move(name));
 }
 
-void CPDF_ContentMarks::AddMarkWithDirectDict(ByteString name,
-                                              CPDF_Dictionary* pDict) {
+void CPDF_ContentMarks::AddMarkWithDirectDict(
+    ByteString name,
+    RetainPtr<CPDF_Dictionary> pDict) {
   EnsureMarkDataExists();
-  m_pMarkData->AddMarkWithDirectDict(std::move(name), pDict);
+  m_pMarkData->AddMarkWithDirectDict(std::move(name), std::move(pDict));
 }
 
 void CPDF_ContentMarks::AddMarkWithPropertiesHolder(
     const ByteString& name,
-    CPDF_Dictionary* pDict,
+    RetainPtr<CPDF_Dictionary> pDict,
     const ByteString& property_name) {
   EnsureMarkDataExists();
-  m_pMarkData->AddMarkWithPropertiesHolder(name, pDict, property_name);
+  m_pMarkData->AddMarkWithPropertiesHolder(name, std::move(pDict),
+                                           property_name);
 }
 
 bool CPDF_ContentMarks::RemoveMark(CPDF_ContentMarkItem* pMarkItem) {
@@ -73,15 +73,6 @@
     m_pMarkData = pdfium::MakeRetain<MarkData>();
 }
 
-void CPDF_ContentMarks::DeleteLastMark() {
-  if (!m_pMarkData)
-    return;
-
-  m_pMarkData->DeleteLastMark();
-  if (CountItems() == 0)
-    m_pMarkData.Reset();
-}
-
 size_t CPDF_ContentMarks::FindFirstDifference(
     const CPDF_ContentMarks* other) const {
   if (m_pMarkData == other->m_pMarkData)
@@ -96,12 +87,12 @@
   return min_len;
 }
 
-CPDF_ContentMarks::MarkData::MarkData() {}
+CPDF_ContentMarks::MarkData::MarkData() = default;
 
 CPDF_ContentMarks::MarkData::MarkData(const MarkData& src)
     : m_Marks(src.m_Marks) {}
 
-CPDF_ContentMarks::MarkData::~MarkData() {}
+CPDF_ContentMarks::MarkData::~MarkData() = default;
 
 size_t CPDF_ContentMarks::MarkData::CountItems() const {
   return m_Marks.size();
@@ -109,7 +100,7 @@
 
 bool CPDF_ContentMarks::MarkData::ContainsItem(
     const CPDF_ContentMarkItem* pItem) const {
-  for (const auto pMark : m_Marks) {
+  for (const auto& pMark : m_Marks) {
     if (pMark == pItem)
       return true;
   }
@@ -117,17 +108,19 @@
 }
 
 CPDF_ContentMarkItem* CPDF_ContentMarks::MarkData::GetItem(size_t index) {
+  CHECK_LT(index, m_Marks.size());
   return m_Marks[index].Get();
 }
 
 const CPDF_ContentMarkItem* CPDF_ContentMarks::MarkData::GetItem(
     size_t index) const {
+  CHECK_LT(index, m_Marks.size());
   return m_Marks[index].Get();
 }
 
 int CPDF_ContentMarks::MarkData::GetMarkedContentID() const {
-  for (const auto pMark : m_Marks) {
-    const CPDF_Dictionary* pDict = pMark->GetParam();
+  for (const auto& pMark : m_Marks) {
+    RetainPtr<const CPDF_Dictionary> pDict = pMark->GetParam();
     if (pDict && pDict->KeyExist("MCID"))
       return pDict->GetIntegerFor("MCID");
   }
@@ -141,7 +134,7 @@
 
 void CPDF_ContentMarks::MarkData::AddMarkWithDirectDict(
     ByteString name,
-    CPDF_Dictionary* pDict) {
+    RetainPtr<CPDF_Dictionary> pDict) {
   auto pItem = pdfium::MakeRetain<CPDF_ContentMarkItem>(std::move(name));
   pItem->SetDirectDict(ToDictionary(pDict->Clone()));
   m_Marks.push_back(pItem);
@@ -149,11 +142,11 @@
 
 void CPDF_ContentMarks::MarkData::AddMarkWithPropertiesHolder(
     const ByteString& name,
-    CPDF_Dictionary* pDict,
+    RetainPtr<CPDF_Dictionary> pDict,
     const ByteString& property_name) {
   auto pItem = pdfium::MakeRetain<CPDF_ContentMarkItem>(name);
-  pItem->SetPropertiesHolder(pDict, property_name);
-  m_Marks.push_back(pItem);
+  pItem->SetPropertiesHolder(std::move(pDict), property_name);
+  m_Marks.push_back(std::move(pItem));
 }
 
 bool CPDF_ContentMarks::MarkData::RemoveMark(CPDF_ContentMarkItem* pMarkItem) {
@@ -165,8 +158,3 @@
   }
   return false;
 }
-
-void CPDF_ContentMarks::MarkData::DeleteLastMark() {
-  if (!m_Marks.empty())
-    m_Marks.pop_back();
-}
diff --git a/core/fpdfapi/page/cpdf_contentmarks.h b/core/fpdfapi/page/cpdf_contentmarks.h
index 7bb25ec..ef3ebd6 100644
--- a/core/fpdfapi/page/cpdf_contentmarks.h
+++ b/core/fpdfapi/page/cpdf_contentmarks.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,12 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_CONTENTMARKS_H_
 #define CORE_FPDFAPI_PAGE_CPDF_CONTENTMARKS_H_
 
+#include <stddef.h>
+
 #include <memory>
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_contentmarkitem.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Dictionary;
@@ -31,20 +32,17 @@
   const CPDF_ContentMarkItem* GetItem(size_t index) const;
 
   void AddMark(ByteString name);
-  void AddMarkWithDirectDict(ByteString name, CPDF_Dictionary* pDict);
+  void AddMarkWithDirectDict(ByteString name, RetainPtr<CPDF_Dictionary> pDict);
   void AddMarkWithPropertiesHolder(const ByteString& name,
-                                   CPDF_Dictionary* pDict,
+                                   RetainPtr<CPDF_Dictionary> pDict,
                                    const ByteString& property_name);
   bool RemoveMark(CPDF_ContentMarkItem* pMarkItem);
-  void DeleteLastMark();
   size_t FindFirstDifference(const CPDF_ContentMarks* other) const;
 
  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;
@@ -53,14 +51,18 @@
 
     int GetMarkedContentID() const;
     void AddMark(ByteString name);
-    void AddMarkWithDirectDict(ByteString name, CPDF_Dictionary* pDict);
+    void AddMarkWithDirectDict(ByteString name,
+                               RetainPtr<CPDF_Dictionary> pDict);
     void AddMarkWithPropertiesHolder(const ByteString& name,
-                                     CPDF_Dictionary* pDict,
+                                     RetainPtr<CPDF_Dictionary> pDict,
                                      const ByteString& property_name);
     bool RemoveMark(CPDF_ContentMarkItem* pMarkItem);
-    void DeleteLastMark();
 
    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 5b3efe1..1fbcb0b 100644
--- a/core/fpdfapi/page/cpdf_contentparser.cpp
+++ b/core/fpdfapi/page/cpdf_contentparser.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,11 @@
 
 #include "core/fpdfapi/page/cpdf_contentparser.h"
 
+#include <utility>
+
 #include "constants/page_object.h"
 #include "core/fpdfapi/font/cpdf_type3char.h"
 #include "core/fpdfapi/page/cpdf_allstates.h"
-#include "core/fpdfapi/page/cpdf_form.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/page/cpdf_path.h"
@@ -17,53 +18,62 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "core/fxcrt/fixed_try_alloc_zeroed_data_vector.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/pauseindicator_iface.h"
-#include "core/fxge/render_defines.h"
-#include "third_party/base/ptr_util.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxcrt/stl_util.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 
 CPDF_ContentParser::CPDF_ContentParser(CPDF_Page* pPage)
-    : m_CurrentStage(Stage::kGetContent), m_pObjectHolder(pPage) {
-  ASSERT(pPage);
+    : m_CurrentStage(Stage::kGetContent), m_pPageObjectHolder(pPage) {
+  DCHECK(pPage);
   if (!pPage->GetDocument()) {
     m_CurrentStage = Stage::kComplete;
     return;
   }
 
-  CPDF_Object* pContent =
-      pPage->GetDict()->GetDirectObjectFor(pdfium::page_object::kContents);
+  RetainPtr<CPDF_Object> pContent =
+      pPage->GetMutableDict()->GetMutableDirectObjectFor(
+          pdfium::page_object::kContents);
   if (!pContent) {
     HandlePageContentFailure();
     return;
   }
 
-  CPDF_Stream* pStream = pContent->AsStream();
+  const CPDF_Stream* pStream = pContent->AsStream();
   if (pStream) {
     HandlePageContentStream(pStream);
     return;
   }
 
-  CPDF_Array* pArray = pContent->AsArray();
+  const CPDF_Array* pArray = pContent->AsArray();
   if (pArray && HandlePageContentArray(pArray))
     return;
 
   HandlePageContentFailure();
 }
 
-CPDF_ContentParser::CPDF_ContentParser(CPDF_Form* pForm,
-                                       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_pObjectHolder(pForm),
+      m_pPageObjectHolder(pPageObjectHolder),
       m_pType3Char(pType3Char) {
-  ASSERT(pForm);
-  CFX_Matrix form_matrix = pForm->GetDict()->GetMatrixFor("Matrix");
+  DCHECK(m_pPageObjectHolder);
+  CFX_Matrix form_matrix =
+      m_pPageObjectHolder->GetDict()->GetMatrixFor("Matrix");
   if (pGraphicStates)
     form_matrix.Concat(pGraphicStates->m_CTM);
 
-  CPDF_Array* pBBox = pForm->GetDict()->GetArrayFor("BBox");
+  RetainPtr<const CPDF_Array> pBBox =
+      m_pPageObjectHolder->GetDict()->GetArrayFor("BBox");
   CFX_FloatRect form_bbox;
   CPDF_Path ClipPath;
   if (pBBox) {
@@ -79,31 +89,33 @@
       form_bbox = pParentMatrix->TransformRect(form_bbox);
   }
 
-  CPDF_Dictionary* pResources = pForm->GetDict()->GetDictFor("Resources");
-  m_pParser = pdfium::MakeUnique<CPDF_StreamContentParser>(
-      pForm->GetDocument(), pForm->m_pPageResources.Get(),
-      pForm->m_pResources.Get(), pParentMatrix, pForm, pResources, form_bbox,
-      pGraphicStates, pParsedSet);
+  RetainPtr<CPDF_Dictionary> pResources =
+      m_pPageObjectHolder->GetMutableDict()->GetMutableDictFor("Resources");
+  m_pParser = std::make_unique<CPDF_StreamContentParser>(
+      m_pPageObjectHolder->GetDocument(),
+      m_pPageObjectHolder->GetMutablePageResources(),
+      m_pPageObjectHolder->GetMutableResources(), pParentMatrix,
+      m_pPageObjectHolder, std::move(pResources), form_bbox, pGraphicStates,
+      recursion_state);
   m_pParser->GetCurStates()->m_CTM = form_matrix;
   m_pParser->GetCurStates()->m_ParentMatrix = form_matrix;
   if (ClipPath.HasRef()) {
-    m_pParser->GetCurStates()->m_ClipPath.AppendPath(ClipPath, FXFILL_WINDING,
-                                                     true);
+    m_pParser->GetCurStates()->m_ClipPath.AppendPathWithAutoMerge(
+        ClipPath, CFX_FillRenderOptions::FillType::kWinding);
   }
-  if (pForm->GetTransparency().IsGroup()) {
+  if (m_pPageObjectHolder->GetTransparency().IsGroup()) {
     CPDF_GeneralState* pState = &m_pParser->GetCurStates()->m_GeneralState;
     pState->SetBlendType(BlendMode::kNormal);
     pState->SetStrokeAlpha(1.0f);
     pState->SetFillAlpha(1.0f);
     pState->SetSoftMask(nullptr);
   }
-  m_pSingleStream = pdfium::MakeRetain<CPDF_StreamAcc>(pForm->GetStream());
+  m_pSingleStream = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(pStream));
   m_pSingleStream->LoadAllDataFiltered();
-  m_pData.Reset(m_pSingleStream->GetData());
-  m_Size = m_pSingleStream->GetSize();
+  m_Data = m_pSingleStream->GetSpan();
 }
 
-CPDF_ContentParser::~CPDF_ContentParser() {}
+CPDF_ContentParser::~CPDF_ContentParser() = default;
 
 // Returning |true| means that there is more content to be processed and
 // Continue() should be called again. Returning |false| means that we've
@@ -127,19 +139,20 @@
   if (m_CurrentStage == Stage::kCheckClip)
     m_CurrentStage = CheckClip();
 
-  ASSERT(m_CurrentStage == Stage::kComplete);
+  DCHECK_EQ(m_CurrentStage, Stage::kComplete);
   return false;
 }
 
 CPDF_ContentParser::Stage CPDF_ContentParser::GetContent() {
-  ASSERT(m_CurrentStage == Stage::kGetContent);
-  ASSERT(m_pObjectHolder->IsPage());
-  CPDF_Array* pContent =
-      m_pObjectHolder->GetDict()->GetArrayFor(pdfium::page_object::kContents);
-  CPDF_Stream* pStreamObj = ToStream(
+  DCHECK_EQ(m_CurrentStage, Stage::kGetContent);
+  DCHECK(m_pPageObjectHolder->IsPage());
+  RetainPtr<const CPDF_Array> pContent =
+      m_pPageObjectHolder->GetDict()->GetArrayFor(
+          pdfium::page_object::kContents);
+  RetainPtr<const CPDF_Stream> pStreamObj = ToStream(
       pContent ? pContent->GetDirectObjectAt(m_CurrentOffset) : nullptr);
   m_StreamArray[m_CurrentOffset] =
-      pdfium::MakeRetain<CPDF_StreamAcc>(pStreamObj);
+      pdfium::MakeRetain<CPDF_StreamAcc>(std::move(pStreamObj));
   m_StreamArray[m_CurrentOffset]->LoadAllDataFiltered();
   m_CurrentOffset++;
 
@@ -151,54 +164,57 @@
   m_CurrentOffset = 0;
 
   if (m_StreamArray.empty()) {
-    m_pData.Reset(m_pSingleStream->GetData());
-    m_Size = m_pSingleStream->GetSize();
+    m_Data = m_pSingleStream->GetSpan();
     return Stage::kParse;
   }
 
-  FX_SAFE_UINT32 safeSize = 0;
+  FX_SAFE_UINT32 safe_size = 0;
   for (const auto& stream : m_StreamArray) {
-    m_StreamSegmentOffsets.push_back(safeSize.ValueOrDie());
-
-    safeSize += stream->GetSize();
-    safeSize += 1;
-    if (!safeSize.IsValid())
+    m_StreamSegmentOffsets.push_back(safe_size.ValueOrDie());
+    safe_size += stream->GetSize();
+    safe_size += 1;
+    if (!safe_size.IsValid())
       return Stage::kComplete;
   }
 
-  m_Size = safeSize.ValueOrDie();
-  m_pData.Reset(
-      std::unique_ptr<uint8_t, FxFreeDeleter>(FX_Alloc(uint8_t, m_Size)));
+  const size_t buffer_size = safe_size.ValueOrDie();
+  FixedTryAllocZeroedDataVector<uint8_t> buffer(buffer_size);
+  if (buffer.empty()) {
+    m_Data.emplace<pdfium::span<const uint8_t>>();
+    return Stage::kComplete;
+  }
 
-  uint32_t pos = 0;
+  size_t pos = 0;
+  auto data_span = buffer.writable_span();
   for (const auto& stream : m_StreamArray) {
-    memcpy(m_pData.Get() + pos, stream->GetData(), stream->GetSize());
+    fxcrt::spancpy(data_span.subspan(pos), stream->GetSpan());
     pos += stream->GetSize();
-    m_pData.Get()[pos++] = ' ';
+    data_span[pos++] = ' ';
   }
   m_StreamArray.clear();
-
+  m_Data = std::move(buffer);
   return Stage::kParse;
 }
 
 CPDF_ContentParser::Stage CPDF_ContentParser::Parse() {
   if (!m_pParser) {
-    m_pParsedSet = pdfium::MakeUnique<std::set<const uint8_t*>>();
-    m_pParser = pdfium::MakeUnique<CPDF_StreamContentParser>(
-        m_pObjectHolder->GetDocument(), m_pObjectHolder->m_pPageResources.Get(),
-        nullptr, nullptr, m_pObjectHolder.Get(),
-        m_pObjectHolder->m_pResources.Get(), m_pObjectHolder->GetBBox(),
-        nullptr, m_pParsedSet.get());
+    m_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_RecursionState);
     m_pParser->GetCurStates()->m_ColorState.SetDefault();
   }
-  if (m_CurrentOffset >= m_Size)
+  if (m_CurrentOffset >= GetData().size())
     return Stage::kCheckClip;
 
   if (m_StreamSegmentOffsets.empty())
     m_StreamSegmentOffsets.push_back(0);
 
   static constexpr uint32_t kParseStepLimit = 100;
-  m_CurrentOffset += m_pParser->Parse(m_pData.Get(), m_Size, m_CurrentOffset,
+  m_CurrentOffset += m_pParser->Parse(GetData(), m_CurrentOffset,
                                       kParseStepLimit, m_StreamSegmentOffsets);
   return Stage::kParse;
 }
@@ -209,7 +225,7 @@
                                            m_pParser->GetType3Data());
   }
 
-  for (auto& pObj : *m_pObjectHolder) {
+  for (auto& pObj : *m_pPageObjectHolder) {
     if (!pObj->m_ClipPath.HasRef())
       continue;
     if (pObj->m_ClipPath.GetPathCount() != 1)
@@ -230,14 +246,15 @@
   return Stage::kComplete;
 }
 
-void CPDF_ContentParser::HandlePageContentStream(CPDF_Stream* pStream) {
-  m_pSingleStream = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
+void CPDF_ContentParser::HandlePageContentStream(const CPDF_Stream* pStream) {
+  m_pSingleStream =
+      pdfium::MakeRetain<CPDF_StreamAcc>(pdfium::WrapRetain(pStream));
   m_pSingleStream->LoadAllDataFiltered();
   m_CurrentStage = Stage::kPrepareContent;
 }
 
-bool CPDF_ContentParser::HandlePageContentArray(CPDF_Array* pArray) {
-  m_nStreams = pArray->size();
+bool CPDF_ContentParser::HandlePageContentArray(const CPDF_Array* pArray) {
+  m_nStreams = fxcrt::CollectionSize<uint32_t>(*pArray);
   if (m_nStreams == 0)
     return false;
 
@@ -248,3 +265,9 @@
 void CPDF_ContentParser::HandlePageContentFailure() {
   m_CurrentStage = Stage::kComplete;
 }
+
+pdfium::span<const uint8_t> CPDF_ContentParser::GetData() const {
+  if (is_owned())
+    return absl::get<FixedTryAllocZeroedDataVector<uint8_t>>(m_Data).span();
+  return absl::get<pdfium::span<const uint8_t>>(m_Data);
+}
diff --git a/core/fpdfapi/page/cpdf_contentparser.h b/core/fpdfapi/page/cpdf_contentparser.h
index 2f9a44b..e2660e2 100644
--- a/core/fpdfapi/page/cpdf_contentparser.h
+++ b/core/fpdfapi/page/cpdf_contentparser.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,18 +7,20 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_CONTENTPARSER_H_
 #define CORE_FPDFAPI_PAGE_CPDF_CONTENTPARSER_H_
 
+#include <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/fx_memory_wrappers.h"
-#include "core/fxcrt/maybe_owned.h"
+#include "core/fxcrt/fixed_try_alloc_zeroed_data_vector.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
 
 class CPDF_AllStates;
 class CPDF_Array;
-class CPDF_Form;
 class CPDF_Page;
 class CPDF_PageObjectHolder;
 class CPDF_Stream;
@@ -29,11 +31,12 @@
 class CPDF_ContentParser {
  public:
   explicit CPDF_ContentParser(CPDF_Page* pPage);
-  CPDF_ContentParser(CPDF_Form* pForm,
+  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_Form::RecursionState* recursion_state);
   ~CPDF_ContentParser();
 
   const CPDF_AllStates* GetCurStates() const {
@@ -56,26 +59,31 @@
   Stage Parse();
   Stage CheckClip();
 
-  void HandlePageContentStream(CPDF_Stream* pStream);
-  bool HandlePageContentArray(CPDF_Array* pArray);
+  void HandlePageContentStream(const CPDF_Stream* pStream);
+  bool HandlePageContentArray(const CPDF_Array* pArray);
   void HandlePageContentFailure();
 
+  bool is_owned() const {
+    return absl::holds_alternative<FixedTryAllocZeroedDataVector<uint8_t>>(
+        m_Data);
+  }
+  pdfium::span<const uint8_t> GetData() const;
+
   Stage m_CurrentStage;
-  UnownedPtr<CPDF_PageObjectHolder> const m_pObjectHolder;
+  UnownedPtr<CPDF_PageObjectHolder> const m_pPageObjectHolder;
   UnownedPtr<CPDF_Type3Char> m_pType3Char;  // Only used when parsing forms.
   RetainPtr<CPDF_StreamAcc> m_pSingleStream;
   std::vector<RetainPtr<CPDF_StreamAcc>> m_StreamArray;
   std::vector<uint32_t> m_StreamSegmentOffsets;
-  MaybeOwned<uint8_t, FxFreeDeleter> m_pData;
+  absl::variant<pdfium::span<const uint8_t>,
+                FixedTryAllocZeroedDataVector<uint8_t>>
+      m_Data;
   uint32_t m_nStreams = 0;
-  uint32_t m_Size = 0;
   uint32_t m_CurrentOffset = 0;
-
   // Only used when parsing pages.
-  std::unique_ptr<std::set<const uint8_t*>> m_pParsedSet;
+  CPDF_Form::RecursionState m_RecursionState;
 
-  // |m_pParser| has a reference to |m_pParsedSet|, so must be below and thus
-  // destroyed first.
+  // 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 e557516..0faabce 100644
--- a/core/fpdfapi/page/cpdf_devicecs.cpp
+++ b/core/fpdfapi/page/cpdf_devicecs.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,6 @@
 
 #include "core/fpdfapi/page/cpdf_devicecs.h"
 
-#include <limits.h>
-
 #include <algorithm>
 
 #include "core/fpdfapi/parser/cpdf_array.h"
@@ -17,20 +15,20 @@
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fxcodec/fx_codec.h"
 #include "core/fxge/dib/cfx_cmyk_to_srgb.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
 float NormalizeChannel(float fVal) {
-  return pdfium::clamp(fVal, 0.0f, 1.0f);
+  return std::clamp(fVal, 0.0f, 1.0f);
 }
 
 }  // namespace
 
-CPDF_DeviceCS::CPDF_DeviceCS(int family) : CPDF_ColorSpace(nullptr, family) {
-  ASSERT(family == PDFCS_DEVICEGRAY || family == PDFCS_DEVICERGB ||
-         family == PDFCS_DEVICECMYK);
+CPDF_DeviceCS::CPDF_DeviceCS(Family family) : CPDF_ColorSpace(family) {
+  DCHECK(family == Family::kDeviceGray || family == Family::kDeviceRGB ||
+         family == Family::kDeviceCMYK);
   SetComponentsForStockCS(ComponentsForFamily(GetFamily()));
 }
 
@@ -41,27 +39,26 @@
                                std::set<const CPDF_Object*>* pVisited) {
   // Unlike other classes that inherit from CPDF_ColorSpace, CPDF_DeviceCS is
   // never loaded by CPDF_ColorSpace.
-  NOTREACHED();
-  return 0;
+  NOTREACHED_NORETURN();
 }
 
-bool CPDF_DeviceCS::GetRGB(const float* pBuf,
+bool CPDF_DeviceCS::GetRGB(pdfium::span<const float> pBuf,
                            float* R,
                            float* G,
                            float* B) const {
-  switch (m_Family) {
-    case PDFCS_DEVICEGRAY:
-      *R = NormalizeChannel(*pBuf);
+  switch (GetFamily()) {
+    case Family::kDeviceGray:
+      *R = NormalizeChannel(pBuf[0]);
       *G = *R;
       *B = *R;
       return true;
-    case PDFCS_DEVICERGB:
+    case Family::kDeviceRGB:
       *R = NormalizeChannel(pBuf[0]);
       *G = NormalizeChannel(pBuf[1]);
       *B = NormalizeChannel(pBuf[2]);
       return true;
-    case PDFCS_DEVICECMYK:
-      if (m_dwStdConversion) {
+    case Family::kDeviceCMYK:
+      if (IsStdConversionEnabled()) {
         float k = pBuf[3];
         *R = 1.0f - std::min(1.0f, pBuf[0] + k);
         *G = 1.0f - std::min(1.0f, pBuf[1] + k);
@@ -73,57 +70,74 @@
       }
       return true;
     default:
-      NOTREACHED();
-      return false;
+      NOTREACHED_NORETURN();
   }
 }
 
-void CPDF_DeviceCS::TranslateImageLine(uint8_t* pDestBuf,
-                                       const uint8_t* pSrcBuf,
+void CPDF_DeviceCS::TranslateImageLine(pdfium::span<uint8_t> dest_span,
+                                       pdfium::span<const uint8_t> src_span,
                                        int pixels,
                                        int image_width,
                                        int image_height,
                                        bool bTransMask) const {
-  switch (m_Family) {
-    case PDFCS_DEVICEGRAY:
+  uint8_t* pDestBuf = dest_span.data();
+  const uint8_t* pSrcBuf = src_span.data();
+  switch (GetFamily()) {
+    case Family::kDeviceGray:
       for (int i = 0; i < pixels; i++) {
-        *pDestBuf++ = pSrcBuf[i];
-        *pDestBuf++ = pSrcBuf[i];
-        *pDestBuf++ = pSrcBuf[i];
+        // Compiler can not conclude that src/dest don't overlap, avoid
+        // duplicate loads.
+        const uint8_t pix = pSrcBuf[i];
+        *pDestBuf++ = pix;
+        *pDestBuf++ = pix;
+        *pDestBuf++ = pix;
       }
       break;
-    case PDFCS_DEVICERGB:
+    case Family::kDeviceRGB:
       fxcodec::ReverseRGB(pDestBuf, pSrcBuf, pixels);
       break;
-    case PDFCS_DEVICECMYK:
+    case Family::kDeviceCMYK:
       if (bTransMask) {
         for (int i = 0; i < pixels; i++) {
-          int k = 255 - pSrcBuf[3];
-          pDestBuf[0] = ((255 - pSrcBuf[0]) * k) / 255;
-          pDestBuf[1] = ((255 - pSrcBuf[1]) * k) / 255;
-          pDestBuf[2] = ((255 - pSrcBuf[2]) * k) / 255;
+          // Compiler can't conclude src/dest don't overlap, avoid interleaved
+          // loads and stores.
+          const uint8_t s0 = pSrcBuf[0];
+          const uint8_t s1 = pSrcBuf[1];
+          const uint8_t s2 = pSrcBuf[2];
+          const int k = 255 - pSrcBuf[3];
+          pDestBuf[0] = ((255 - s0) * k) / 255;
+          pDestBuf[1] = ((255 - s1) * k) / 255;
+          pDestBuf[2] = ((255 - s2) * k) / 255;
           pDestBuf += 3;
           pSrcBuf += 4;
         }
       } else {
-        for (int i = 0; i < pixels; i++) {
-          if (m_dwStdConversion) {
-            uint8_t k = pSrcBuf[3];
-            pDestBuf[2] = 255 - std::min(255, pSrcBuf[0] + k);
-            pDestBuf[1] = 255 - std::min(255, pSrcBuf[1] + k);
-            pDestBuf[0] = 255 - std::min(255, pSrcBuf[2] + k);
-          } else {
+        if (IsStdConversionEnabled()) {
+          for (int i = 0; i < pixels; i++) {
+            // Compiler can't conclude src/dest don't overlap, avoid
+            // interleaved loads and stores.
+            const uint8_t s0 = pSrcBuf[0];
+            const uint8_t s1 = pSrcBuf[1];
+            const uint8_t s2 = pSrcBuf[2];
+            const uint8_t k = pSrcBuf[3];
+            pDestBuf[2] = 255 - std::min(255, s0 + k);
+            pDestBuf[1] = 255 - std::min(255, s1 + k);
+            pDestBuf[0] = 255 - std::min(255, s2 + k);
+            pSrcBuf += 4;
+            pDestBuf += 3;
+          }
+        } else {
+          for (int i = 0; i < pixels; i++) {
             std::tie(pDestBuf[2], pDestBuf[1], pDestBuf[0]) =
                 AdobeCMYK_to_sRGB1(pSrcBuf[0], pSrcBuf[1], pSrcBuf[2],
                                    pSrcBuf[3]);
+            pSrcBuf += 4;
+            pDestBuf += 3;
           }
-          pSrcBuf += 4;
-          pDestBuf += 3;
         }
       }
       break;
     default:
-      NOTREACHED();
-      break;
+      NOTREACHED_NORETURN();
   }
 }
diff --git a/core/fpdfapi/page/cpdf_devicecs.h b/core/fpdfapi/page/cpdf_devicecs.h
index c8e256c..8e8ca66 100644
--- a/core/fpdfapi/page/cpdf_devicecs.h
+++ b/core/fpdfapi/page/cpdf_devicecs.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,15 +14,16 @@
 
 class CPDF_DeviceCS final : public CPDF_ColorSpace {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
+  CONSTRUCT_VIA_MAKE_RETAIN;
   ~CPDF_DeviceCS() override;
 
   // CPDF_ColorSpace:
-  bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override;
-  void TranslateImageLine(uint8_t* pDestBuf,
-                          const uint8_t* pSrcBuf,
+  bool GetRGB(pdfium::span<const float> pBuf,
+              float* R,
+              float* G,
+              float* B) const override;
+  void TranslateImageLine(pdfium::span<uint8_t> dest_span,
+                          pdfium::span<const uint8_t> src_span,
                           int pixels,
                           int image_width,
                           int image_height,
@@ -32,7 +33,7 @@
                   std::set<const CPDF_Object*>* pVisited) override;
 
  private:
-  explicit CPDF_DeviceCS(int family);
+  explicit CPDF_DeviceCS(Family family);
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_DEVICECS_H_
diff --git a/core/fpdfapi/page/cpdf_devicecs_unittest.cpp b/core/fpdfapi/page/cpdf_devicecs_unittest.cpp
index fbf2558..4c3c348 100644
--- a/core/fpdfapi/page/cpdf_devicecs_unittest.cpp
+++ b/core/fpdfapi/page/cpdf_devicecs_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,7 +13,8 @@
   float R;
   float G;
   float B;
-  auto device_gray = pdfium::MakeRetain<CPDF_DeviceCS>(PDFCS_DEVICEGRAY);
+  auto device_gray =
+      pdfium::MakeRetain<CPDF_DeviceCS>(CPDF_ColorSpace::Family::kDeviceGray);
 
   // Test normal values. For gray, only first value from buf should be used.
   float buf[3] = {0.43f, 0.11f, 0.34f};
@@ -56,7 +57,8 @@
   float R;
   float G;
   float B;
-  auto device_rgb = pdfium::MakeRetain<CPDF_DeviceCS>(PDFCS_DEVICERGB);
+  auto device_rgb =
+      pdfium::MakeRetain<CPDF_DeviceCS>(CPDF_ColorSpace::Family::kDeviceRGB);
 
   // Test normal values
   float buf[3] = {0.13f, 1.0f, 0.652f};
@@ -85,7 +87,8 @@
   float R;
   float G;
   float B;
-  auto device_cmyk = pdfium::MakeRetain<CPDF_DeviceCS>(PDFCS_DEVICECMYK);
+  auto device_cmyk =
+      pdfium::MakeRetain<CPDF_DeviceCS>(CPDF_ColorSpace::Family::kDeviceCMYK);
 
   // Test normal values
   float buf[4] = {0.6f, 0.5f, 0.3f, 0.9f};
diff --git a/core/fpdfapi/page/cpdf_dib.cpp b/core/fpdfapi/page/cpdf_dib.cpp
index 6976a0d..30f1dcf 100644
--- a/core/fpdfapi/page/cpdf_dib.cpp
+++ b/core/fpdfapi/page/cpdf_dib.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include "core/fpdfapi/page/cpdf_dib.h"
 
+#include <stdint.h>
+
 #include <algorithm>
 #include <memory>
 #include <utility>
@@ -15,33 +17,39 @@
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
 #include "core/fpdfapi/page/cpdf_image.h"
 #include "core/fpdfapi/page/cpdf_imageobject.h"
+#include "core/fpdfapi/page/cpdf_indexedcs.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcodec/basic/basicmodule.h"
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxcodec/jbig2/jbig2module.h"
+#include "core/fxcodec/jbig2/jbig2_decoder.h"
 #include "core/fxcodec/jpeg/jpegmodule.h"
 #include "core/fxcodec/jpx/cjpx_decoder.h"
-#include "core/fxcodec/jpx/jpxmodule.h"
 #include "core/fxcodec/scanlinedecoder.h"
-#include "core/fxcrt/cfx_fixedbufgrow.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxge/calculate_pitch.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 
 namespace {
 
-constexpr int kMaxImageDimension = 0x01FFFF;
+bool IsValidDimension(int value) {
+  constexpr int kMaxImageDimension = 0x01FFFF;
+  return value > 0 && value <= kMaxImageDimension;
+}
 
 unsigned int GetBits8(const uint8_t* pData, uint64_t bitpos, size_t nbits) {
-  ASSERT(nbits == 1 || nbits == 2 || nbits == 4 || nbits == 8 || nbits == 16);
-  ASSERT((bitpos & (nbits - 1)) == 0);
+  DCHECK(nbits == 1 || nbits == 2 || nbits == 4 || nbits == 8 || nbits == 16);
+  DCHECK_EQ((bitpos & (nbits - 1)), 0u);
   unsigned int byte = pData[bitpos / 8];
   if (nbits == 8)
     return byte;
@@ -80,7 +88,7 @@
 }
 
 int CalculateBitsPerPixel(uint32_t bpc, uint32_t comps) {
-  // TODO(thestig): Can |bpp| be 0 here? Add an ASSERT() or handle it?
+  // TODO(thestig): Can |bpp| be 0 here? Add an DCHECK() or handle it?
   uint32_t bpp = bpc * comps;
   if (bpp == 1)
     return 1;
@@ -93,7 +101,7 @@
     CPDF_ColorSpace* pCS) {
   if (!pCS)
     return CJPX_Decoder::kNoColorSpace;
-  if (pCS->GetFamily() == PDFCS_INDEXED)
+  if (pCS->GetFamily() == CPDF_ColorSpace::Family::kIndexed)
     return CJPX_Decoder::kIndexedColorSpace;
   return CJPX_Decoder::kNormalColorSpace;
 }
@@ -101,179 +109,189 @@
 enum class JpxDecodeAction {
   kFail,
   kDoNothing,
+  kUseGray,
   kUseRgb,
   kUseCmyk,
+  kConvertArgbToRgb,
 };
 
-JpxDecodeAction GetJpxDecodeAction(uint32_t jpx_components,
-                                   const CPDF_ColorSpace* pdf_colorspace) {
-  if (pdf_colorspace) {
-    // Make sure the JPX image and the PDF colorspace agree on the number of
-    // components.
-    if (jpx_components != pdf_colorspace->CountComponents())
+// Decides which JpxDecodeAction to use based on the colorspace information from
+// the PDF and the JPX image. Called only when the PDF's image object contains a
+// "/ColorSpace" entry.
+JpxDecodeAction GetJpxDecodeActionFromColorSpaces(
+    const CJPX_Decoder::JpxImageInfo& jpx_info,
+    const CPDF_ColorSpace* pdf_colorspace) {
+  if (pdf_colorspace ==
+      CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray)) {
+    if (jpx_info.colorspace != OPJ_CLRSPC_GRAY &&
+        jpx_info.colorspace != OPJ_CLRSPC_UNSPECIFIED) {
       return JpxDecodeAction::kFail;
-
-    if (pdf_colorspace == CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB))
-      return JpxDecodeAction::kUseRgb;
-
-    return JpxDecodeAction::kDoNothing;
+    }
+    return JpxDecodeAction::kUseGray;
   }
 
-  // Cases where the PDF did not provide a colorspace.
-  // Choose how to decode based on the number of components in the JPX image.
-  switch (jpx_components) {
-    case 3:
+  if (pdf_colorspace ==
+      CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB)) {
+    if (jpx_info.colorspace != OPJ_CLRSPC_SRGB &&
+        jpx_info.colorspace != OPJ_CLRSPC_UNSPECIFIED) {
+      return JpxDecodeAction::kFail;
+    }
+
+    // The channel count of a JPX image can be different from the PDF color
+    // space's component count.
+    if (jpx_info.channels > 3) {
+      return JpxDecodeAction::kConvertArgbToRgb;
+    }
+    return JpxDecodeAction::kUseRgb;
+  }
+
+  if (pdf_colorspace ==
+      CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceCMYK)) {
+    if (jpx_info.colorspace != OPJ_CLRSPC_CMYK &&
+        jpx_info.colorspace != OPJ_CLRSPC_UNSPECIFIED) {
+      return JpxDecodeAction::kFail;
+    }
+    return JpxDecodeAction::kUseCmyk;
+  }
+
+  return JpxDecodeAction::kDoNothing;
+}
+
+JpxDecodeAction GetJpxDecodeActionFromImageColorSpace(
+    const CJPX_Decoder::JpxImageInfo& jpx_info) {
+  switch (jpx_info.colorspace) {
+    case OPJ_CLRSPC_SYCC:
+    case OPJ_CLRSPC_EYCC:
+    case OPJ_CLRSPC_UNKNOWN:
+    case OPJ_CLRSPC_UNSPECIFIED:
+      return JpxDecodeAction::kDoNothing;
+
+    case OPJ_CLRSPC_SRGB:
+      if (jpx_info.channels > 3) {
+        return JpxDecodeAction::kConvertArgbToRgb;
+      }
+
       return JpxDecodeAction::kUseRgb;
 
-    case 4:
+    case OPJ_CLRSPC_GRAY:
+      return JpxDecodeAction::kUseGray;
+
+    case OPJ_CLRSPC_CMYK:
       return JpxDecodeAction::kUseCmyk;
+  }
+}
+
+JpxDecodeAction GetJpxDecodeAction(const CJPX_Decoder::JpxImageInfo& jpx_info,
+                                   const CPDF_ColorSpace* pdf_colorspace) {
+  if (pdf_colorspace) {
+    return GetJpxDecodeActionFromColorSpaces(jpx_info, pdf_colorspace);
+  }
+
+  // When PDF does not provide a color space, check the image color space.
+  return GetJpxDecodeActionFromImageColorSpace(jpx_info);
+}
+
+int GetComponentCountFromOpjColorSpace(OPJ_COLOR_SPACE colorspace) {
+  switch (colorspace) {
+    case OPJ_CLRSPC_GRAY:
+      return 1;
+
+    case OPJ_CLRSPC_SRGB:
+    case OPJ_CLRSPC_SYCC:
+    case OPJ_CLRSPC_EYCC:
+      return 3;
+
+    case OPJ_CLRSPC_CMYK:
+      return 4;
 
     default:
-      return JpxDecodeAction::kDoNothing;
+      return 0;
   }
 }
 
 }  // namespace
 
-CPDF_DIB::CPDF_DIB() = default;
+CPDF_DIB::CPDF_DIB(CPDF_Document* pDoc, RetainPtr<const CPDF_Stream> pStream)
+    : m_pDocument(pDoc), m_pStream(std::move(pStream)) {}
 
 CPDF_DIB::~CPDF_DIB() = default;
 
-bool CPDF_DIB::Load(CPDF_Document* pDoc, const CPDF_Stream* pStream) {
-  if (!pStream)
+CPDF_DIB::JpxSMaskInlineData::JpxSMaskInlineData() = default;
+
+CPDF_DIB::JpxSMaskInlineData::~JpxSMaskInlineData() = default;
+
+bool CPDF_DIB::Load() {
+  if (!LoadInternal(nullptr, nullptr))
     return false;
 
-  m_pDocument = pDoc;
-  m_pDict.Reset(pStream->GetDict());
-  if (!m_pDict)
+  if (CreateDecoder(0) == LoadState::kFail)
     return false;
 
-  m_pStream.Reset(pStream);
-  m_Width = m_pDict->GetIntegerFor("Width");
-  m_Height = m_pDict->GetIntegerFor("Height");
-  if (m_Width <= 0 || m_Height <= 0 || m_Width > kMaxImageDimension ||
-      m_Height > kMaxImageDimension) {
-    return false;
-  }
-  m_GroupFamily = 0;
-  m_bLoadMask = false;
-  if (!LoadColorInfo(nullptr, nullptr))
-    return false;
-
-  if (m_bDoBpcCheck && (m_bpc == 0 || m_nComponents == 0))
-    return false;
-
-  FX_SAFE_UINT32 src_size =
-      fxcodec::CalculatePitch8(m_bpc, m_nComponents, m_Width) * m_Height;
-  if (!src_size.IsValid())
-    return false;
-
-  m_pStreamAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
-  m_pStreamAcc->LoadAllDataImageAcc(src_size.ValueOrDie());
-  if (m_pStreamAcc->GetSize() == 0 || !m_pStreamAcc->GetData())
-    return false;
-
-  if (CreateDecoder() == LoadState::kFail)
-    return false;
-
-  if (m_bImageMask)
-    SetMaskProperties();
-  else
-    m_bpp = CalculateBitsPerPixel(m_bpc, m_nComponents);
-
-  FX_SAFE_UINT32 pitch = fxcodec::CalculatePitch32(m_bpp, m_Width);
-  if (!pitch.IsValid())
-    return false;
-
-  m_pLineBuf.reset(FX_Alloc(uint8_t, pitch.ValueOrDie()));
-  LoadPalette();
-  if (m_bColorKey) {
-    m_bpp = 32;
-    m_AlphaFlag = 2;
-    pitch = fxcodec::CalculatePitch32(m_bpp, m_Width);
-    if (!pitch.IsValid())
-      return false;
-
-    m_pMaskedLine.reset(FX_Alloc(uint8_t, pitch.ValueOrDie()));
-  }
-  m_Pitch = pitch.ValueOrDie();
-  return true;
+  return ContinueInternal();
 }
 
 bool CPDF_DIB::ContinueToLoadMask() {
+  if (m_pColorSpace && m_bStdCS)
+    m_pColorSpace->EnableStdConversion(true);
+
+  return ContinueInternal();
+}
+
+bool CPDF_DIB::ContinueInternal() {
   if (m_bImageMask) {
     SetMaskProperties();
   } else {
     if (!m_bpc || !m_nComponents)
       return false;
 
-    m_bpp = CalculateBitsPerPixel(m_bpc, m_nComponents);
+    m_Format = MakeRGBFormat(CalculateBitsPerPixel(m_bpc, m_nComponents));
   }
 
-  FX_SAFE_UINT32 pitch = fxcodec::CalculatePitch32(m_bpp, m_Width);
-  if (!pitch.IsValid())
+  absl::optional<uint32_t> pitch =
+      fxge::CalculatePitch32(GetBppFromFormat(m_Format), m_Width);
+  if (!pitch.has_value())
     return false;
 
-  m_pLineBuf.reset(FX_Alloc(uint8_t, pitch.ValueOrDie()));
-  if (m_pColorSpace && m_bStdCS) {
-    m_pColorSpace->EnableStdConversion(true);
-  }
+  m_LineBuf = DataVector<uint8_t>(pitch.value());
   LoadPalette();
   if (m_bColorKey) {
-    m_bpp = 32;
-    m_AlphaFlag = 2;
-    pitch = fxcodec::CalculatePitch32(m_bpp, m_Width);
-    if (!pitch.IsValid())
+    m_Format = FXDIB_Format::kArgb;
+    pitch = fxge::CalculatePitch32(GetBppFromFormat(m_Format), m_Width);
+    if (!pitch.has_value())
       return false;
-    m_pMaskedLine.reset(FX_Alloc(uint8_t, pitch.ValueOrDie()));
+    m_MaskBuf = DataVector<uint8_t>(pitch.value());
   }
-  m_Pitch = pitch.ValueOrDie();
+  m_Pitch = pitch.value();
   return true;
 }
 
 CPDF_DIB::LoadState CPDF_DIB::StartLoadDIBBase(
-    CPDF_Document* pDoc,
-    const CPDF_Stream* pStream,
     bool bHasMask,
     const CPDF_Dictionary* pFormResources,
-    CPDF_Dictionary* pPageResources,
+    const CPDF_Dictionary* pPageResources,
     bool bStdCS,
-    uint32_t GroupFamily,
-    bool bLoadMask) {
-  if (!pStream)
-    return LoadState::kFail;
-
-  m_pDocument = pDoc;
-  m_pDict.Reset(pStream->GetDict());
-  m_pStream.Reset(pStream);
+    CPDF_ColorSpace::Family GroupFamily,
+    bool bLoadMask,
+    const CFX_Size& max_size_required) {
   m_bStdCS = bStdCS;
   m_bHasMask = bHasMask;
-  m_Width = m_pDict->GetIntegerFor("Width");
-  m_Height = m_pDict->GetIntegerFor("Height");
-  if (m_Width <= 0 || m_Height <= 0 || m_Width > kMaxImageDimension ||
-      m_Height > kMaxImageDimension) {
-    return LoadState::kFail;
-  }
   m_GroupFamily = GroupFamily;
   m_bLoadMask = bLoadMask;
-  if (!LoadColorInfo(m_pStream->IsInline() ? pFormResources : nullptr,
-                     pPageResources)) {
+
+  if (!m_pStream->IsInline())
+    pFormResources = nullptr;
+
+  if (!LoadInternal(pFormResources, pPageResources))
     return LoadState::kFail;
+
+  uint8_t resolution_levels_to_skip = 0;
+  if (max_size_required.width != 0 && max_size_required.height != 0) {
+    resolution_levels_to_skip = static_cast<uint8_t>(
+        std::log2(std::max(1, std::min(m_Width / max_size_required.width,
+                                       m_Height / max_size_required.height))));
   }
-  if (m_bDoBpcCheck && (m_bpc == 0 || m_nComponents == 0))
-    return LoadState::kFail;
 
-  FX_SAFE_UINT32 src_size =
-      fxcodec::CalculatePitch8(m_bpc, m_nComponents, m_Width) * m_Height;
-  if (!src_size.IsValid())
-    return LoadState::kFail;
-
-  m_pStreamAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
-  m_pStreamAcc->LoadAllDataImageAcc(src_size.ValueOrDie());
-  if (m_pStreamAcc->GetSize() == 0 || !m_pStreamAcc->GetData())
-    return LoadState::kFail;
-
-  LoadState iCreatedDecoder = CreateDecoder();
+  LoadState iCreatedDecoder = CreateDecoder(resolution_levels_to_skip);
   if (iCreatedDecoder == LoadState::kFail)
     return LoadState::kFail;
 
@@ -286,8 +304,8 @@
     return LoadState::kContinue;
   }
 
-  ASSERT(iCreatedDecoder == LoadState::kSuccess);
-  ASSERT(iLoadedMask == LoadState::kSuccess);
+  DCHECK_EQ(iCreatedDecoder, LoadState::kSuccess);
+  DCHECK_EQ(iLoadedMask, LoadState::kSuccess);
   if (m_pColorSpace && m_bStdCS)
     m_pColorSpace->EnableStdConversion(false);
   return LoadState::kSuccess;
@@ -308,47 +326,44 @@
     return LoadState::kFail;
 
   FXCODEC_STATUS iDecodeStatus;
-  Jbig2Module* pJbig2Module =
-      fxcodec::ModuleMgr::GetInstance()->GetJbig2Module();
   if (!m_pJbig2Context) {
-    m_pJbig2Context = pdfium::MakeUnique<Jbig2Context>();
+    m_pJbig2Context = std::make_unique<Jbig2Context>();
     if (m_pStreamAcc->GetImageParam()) {
-      const CPDF_Stream* pGlobals =
+      RetainPtr<const CPDF_Stream> pGlobals =
           m_pStreamAcc->GetImageParam()->GetStreamFor("JBIG2Globals");
       if (pGlobals) {
-        m_pGlobalAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pGlobals);
+        m_pGlobalAcc = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(pGlobals));
         m_pGlobalAcc->LoadAllDataFiltered();
       }
     }
-    uint32_t nSrcObjNum = 0;
+    uint64_t nSrcKey = 0;
     pdfium::span<const uint8_t> pSrcSpan;
     if (m_pStreamAcc) {
       pSrcSpan = m_pStreamAcc->GetSpan();
-      if (m_pStreamAcc->GetStream())
-        nSrcObjNum = m_pStreamAcc->GetStream()->GetObjNum();
+      nSrcKey = m_pStreamAcc->KeyForCache();
     }
-    uint32_t nGlobalObjNum = 0;
+    uint64_t nGlobalKey = 0;
     pdfium::span<const uint8_t> pGlobalSpan;
     if (m_pGlobalAcc) {
       pGlobalSpan = m_pGlobalAcc->GetSpan();
-      if (m_pGlobalAcc->GetStream())
-        nGlobalObjNum = m_pGlobalAcc->GetStream()->GetObjNum();
+      nGlobalKey = m_pGlobalAcc->KeyForCache();
     }
-    iDecodeStatus = pJbig2Module->StartDecode(
-        m_pJbig2Context.get(), m_pDocument->CodecContext(), m_Width, m_Height,
-        pSrcSpan, nSrcObjNum, pGlobalSpan, nGlobalObjNum,
-        m_pCachedBitmap->GetBuffer(), m_pCachedBitmap->GetPitch(), pPause);
+    iDecodeStatus = Jbig2Decoder::StartDecode(
+        m_pJbig2Context.get(), m_pDocument->GetOrCreateCodecContext(), m_Width,
+        m_Height, pSrcSpan, nSrcKey, pGlobalSpan, nGlobalKey,
+        m_pCachedBitmap->GetWritableBuffer(), m_pCachedBitmap->GetPitch(),
+        pPause);
   } else {
-    iDecodeStatus = pJbig2Module->ContinueDecode(m_pJbig2Context.get(), pPause);
+    iDecodeStatus = Jbig2Decoder::ContinueDecode(m_pJbig2Context.get(), pPause);
   }
 
-  if (iDecodeStatus < 0) {
+  if (iDecodeStatus == FXCODEC_STATUS::kError) {
     m_pJbig2Context.reset();
     m_pCachedBitmap.Reset();
     m_pGlobalAcc.Reset();
     return LoadState::kFail;
   }
-  if (iDecodeStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE)
+  if (iDecodeStatus == FXCODEC_STATUS::kDecodeToBeContinued)
     return LoadState::kContinue;
 
   LoadState iContinueStatus = LoadState::kSuccess;
@@ -368,58 +383,51 @@
 
 bool CPDF_DIB::LoadColorInfo(const CPDF_Dictionary* pFormResources,
                              const CPDF_Dictionary* pPageResources) {
+  absl::optional<DecoderArray> decoder_array = GetDecoderArray(m_pDict);
+  if (!decoder_array.has_value())
+    return false;
+
   m_bpc_orig = m_pDict->GetIntegerFor("BitsPerComponent");
   if (!IsMaybeValidBitsPerComponent(m_bpc_orig))
     return false;
 
-  if (m_pDict->GetIntegerFor("ImageMask"))
-    m_bImageMask = true;
+  m_bImageMask = m_pDict->GetBooleanFor("ImageMask", /*bDefault=*/false);
 
   if (m_bImageMask || !m_pDict->KeyExist("ColorSpace")) {
-    if (!m_bImageMask) {
-      const CPDF_Object* pFilter = m_pDict->GetDirectObjectFor("Filter");
-      if (pFilter) {
-        ByteString filter;
-        if (pFilter->IsName()) {
-          filter = pFilter->GetString();
-        } else if (const CPDF_Array* pArray = pFilter->AsArray()) {
-          if (!ValidateDecoderPipeline(pArray))
-            return false;
-          filter = pArray->GetStringAt(pArray->size() - 1);
-        }
-
-        if (filter == "JPXDecode") {
-          m_bDoBpcCheck = false;
-          return true;
-        }
+    if (!m_bImageMask && !decoder_array.value().empty()) {
+      const ByteString& filter = decoder_array.value().back().first;
+      if (filter == "JPXDecode") {
+        m_bDoBpcCheck = false;
+        return true;
       }
     }
     m_bImageMask = true;
     m_bpc = m_nComponents = 1;
-    const CPDF_Array* pDecode = m_pDict->GetArrayFor("Decode");
+    RetainPtr<const CPDF_Array> pDecode = m_pDict->GetArrayFor("Decode");
     m_bDefaultDecode = !pDecode || !pDecode->GetIntegerAt(0);
     return true;
   }
 
-  const CPDF_Object* pCSObj = m_pDict->GetDirectObjectFor("ColorSpace");
+  RetainPtr<const CPDF_Object> pCSObj =
+      m_pDict->GetDirectObjectFor("ColorSpace");
   if (!pCSObj)
     return false;
 
-  auto* pDocPageData = CPDF_DocPageData::FromDocument(m_pDocument.Get());
+  auto* pDocPageData = CPDF_DocPageData::FromDocument(m_pDocument);
   if (pFormResources)
-    m_pColorSpace = pDocPageData->GetColorSpace(pCSObj, pFormResources);
+    m_pColorSpace = pDocPageData->GetColorSpace(pCSObj.Get(), pFormResources);
   if (!m_pColorSpace)
-    m_pColorSpace = pDocPageData->GetColorSpace(pCSObj, pPageResources);
+    m_pColorSpace = pDocPageData->GetColorSpace(pCSObj.Get(), pPageResources);
   if (!m_pColorSpace)
     return false;
 
   // If the checks above failed to find a colorspace, and the next line to set
   // |m_nComponents| does not get reached, then a decoder can try to set
-  // |m_nComponents| based on the number of components in the image being
+  // |m_nComponents| based on the number of channels in the image being
   // decoded.
   m_nComponents = m_pColorSpace->CountComponents();
   m_Family = m_pColorSpace->GetFamily();
-  if (m_Family == PDFCS_ICCBASED && pCSObj->IsName()) {
+  if (m_Family == CPDF_ColorSpace::Family::kICCBased && pCSObj->IsName()) {
     ByteString cs = pCSObj->GetString();
     if (cs == "DeviceGray")
       m_nComponents = 1;
@@ -428,37 +436,44 @@
     else if (cs == "DeviceCMYK")
       m_nComponents = 4;
   }
-  ValidateDictParam();
-  return GetDecodeAndMaskArray(&m_bDefaultDecode, &m_bColorKey);
+
+  ByteString filter;
+  if (!decoder_array.value().empty())
+    filter = decoder_array.value().back().first;
+
+  if (!ValidateDictParam(filter))
+    return false;
+
+  return GetDecodeAndMaskArray();
 }
 
-bool CPDF_DIB::GetDecodeAndMaskArray(bool* bDefaultDecode, bool* bColorKey) {
+bool CPDF_DIB::GetDecodeAndMaskArray() {
   if (!m_pColorSpace)
     return false;
 
   m_CompData.resize(m_nComponents);
   int max_data = (1 << m_bpc) - 1;
-  const CPDF_Array* pDecode = m_pDict->GetArrayFor("Decode");
+  RetainPtr<const CPDF_Array> pDecode = m_pDict->GetArrayFor("Decode");
   if (pDecode) {
     for (uint32_t i = 0; i < m_nComponents; i++) {
-      m_CompData[i].m_DecodeMin = pDecode->GetNumberAt(i * 2);
-      float max = pDecode->GetNumberAt(i * 2 + 1);
+      m_CompData[i].m_DecodeMin = pDecode->GetFloatAt(i * 2);
+      float max = pDecode->GetFloatAt(i * 2 + 1);
       m_CompData[i].m_DecodeStep = (max - m_CompData[i].m_DecodeMin) / max_data;
       float def_value;
       float def_min;
       float def_max;
       m_pColorSpace->GetDefaultValue(i, &def_value, &def_min, &def_max);
-      if (m_Family == PDFCS_INDEXED)
+      if (m_Family == CPDF_ColorSpace::Family::kIndexed)
         def_max = max_data;
       if (def_min != m_CompData[i].m_DecodeMin || def_max != max)
-        *bDefaultDecode = false;
+        m_bDefaultDecode = false;
     }
   } else {
     for (uint32_t i = 0; i < m_nComponents; i++) {
       float def_value;
       m_pColorSpace->GetDefaultValue(i, &def_value, &m_CompData[i].m_DecodeMin,
                                      &m_CompData[i].m_DecodeStep);
-      if (m_Family == PDFCS_INDEXED)
+      if (m_Family == CPDF_ColorSpace::Family::kIndexed)
         m_CompData[i].m_DecodeStep = max_data;
       m_CompData[i].m_DecodeStep =
           (m_CompData[i].m_DecodeStep - m_CompData[i].m_DecodeMin) / max_data;
@@ -467,7 +482,7 @@
   if (m_pDict->KeyExist("SMask"))
     return true;
 
-  const CPDF_Object* pMask = m_pDict->GetDirectObjectFor("Mask");
+  RetainPtr<const CPDF_Object> pMask = m_pDict->GetDirectObjectFor("Mask");
   if (!pMask)
     return true;
 
@@ -480,12 +495,12 @@
         m_CompData[i].m_ColorKeyMax = std::min(max_num, max_data);
       }
     }
-    *bColorKey = true;
+    m_bColorKey = true;
   }
   return true;
 }
 
-CPDF_DIB::LoadState CPDF_DIB::CreateDecoder() {
+CPDF_DIB::LoadState CPDF_DIB::CreateDecoder(uint8_t resolution_levels_to_skip) {
   ByteString decoder = m_pStreamAcc->GetImageDecoder();
   if (decoder.IsEmpty())
     return LoadState::kSuccess;
@@ -494,14 +509,15 @@
     return LoadState::kFail;
 
   if (decoder == "JPXDecode") {
-    m_pCachedBitmap = LoadJpxBitmap();
+    m_pCachedBitmap = LoadJpxBitmap(resolution_levels_to_skip);
     return m_pCachedBitmap ? LoadState::kSuccess : LoadState::kFail;
   }
 
   if (decoder == "JBIG2Decode") {
     m_pCachedBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
     if (!m_pCachedBitmap->Create(
-            m_Width, m_Height, m_bImageMask ? FXDIB_1bppMask : FXDIB_1bppRgb)) {
+            m_Width, m_Height,
+            m_bImageMask ? FXDIB_Format::k1bppMask : FXDIB_Format::k1bppRgb)) {
       m_pCachedBitmap.Reset();
       return LoadState::kFail;
     }
@@ -510,7 +526,7 @@
   }
 
   pdfium::span<const uint8_t> src_span = m_pStreamAcc->GetSpan();
-  const CPDF_Dictionary* pParams = m_pStreamAcc->GetImageParam();
+  RetainPtr<const CPDF_Dictionary> pParams = m_pStreamAcc->GetImageParam();
   if (decoder == "CCITTFaxDecode") {
     m_pDecoder = CreateFaxDecoder(src_span, m_Width, m_Height, pParams);
   } else if (decoder == "FlateDecode") {
@@ -526,34 +542,33 @@
   if (!m_pDecoder)
     return LoadState::kFail;
 
-  FX_SAFE_UINT32 requested_pitch =
-      fxcodec::CalculatePitch8(m_bpc, m_nComponents, m_Width);
-  if (!requested_pitch.IsValid())
+  const absl::optional<uint32_t> requested_pitch =
+      fxge::CalculatePitch8(m_bpc, m_nComponents, m_Width);
+  if (!requested_pitch.has_value())
     return LoadState::kFail;
-  FX_SAFE_UINT32 provided_pitch = fxcodec::CalculatePitch8(
+  const absl::optional<uint32_t> provided_pitch = fxge::CalculatePitch8(
       m_pDecoder->GetBPC(), m_pDecoder->CountComps(), m_pDecoder->GetWidth());
-  if (!provided_pitch.IsValid())
+  if (!provided_pitch.has_value())
     return LoadState::kFail;
-  if (provided_pitch.ValueOrDie() < requested_pitch.ValueOrDie())
+  if (provided_pitch.value() < requested_pitch.value())
     return LoadState::kFail;
   return LoadState::kSuccess;
 }
 
 bool CPDF_DIB::CreateDCTDecoder(pdfium::span<const uint8_t> src_span,
                                 const CPDF_Dictionary* pParams) {
-  JpegModule* pJpegModule = fxcodec::ModuleMgr::GetInstance()->GetJpegModule();
-  m_pDecoder = pJpegModule->CreateDecoder(
+  m_pDecoder = JpegModule::CreateDecoder(
       src_span, m_Width, m_Height, m_nComponents,
       !pParams || pParams->GetIntegerFor("ColorTransform", 1));
   if (m_pDecoder)
     return true;
 
-  Optional<JpegModule::JpegImageInfo> info_opt =
-      pJpegModule->LoadInfo(src_span);
+  absl::optional<JpegModule::ImageInfo> info_opt =
+      JpegModule::LoadInfo(src_span);
   if (!info_opt.has_value())
     return false;
 
-  const JpegModule::JpegImageInfo& info = info_opt.value();
+  const JpegModule::ImageInfo& info = info_opt.value();
   m_Width = info.width;
   m_Height = info.height;
 
@@ -564,8 +579,8 @@
 
   if (m_nComponents == static_cast<uint32_t>(info.num_components)) {
     m_bpc = info.bits_per_components;
-    m_pDecoder = pJpegModule->CreateDecoder(
-        src_span, m_Width, m_Height, m_nComponents, info.color_transform);
+    m_pDecoder = JpegModule::CreateDecoder(src_span, m_Width, m_Height,
+                                           m_nComponents, info.color_transform);
     return true;
   }
 
@@ -574,20 +589,20 @@
   if (m_pColorSpace) {
     uint32_t colorspace_comps = m_pColorSpace->CountComponents();
     switch (m_Family) {
-      case PDFCS_DEVICEGRAY:
-      case PDFCS_DEVICERGB:
-      case PDFCS_DEVICECMYK: {
+      case CPDF_ColorSpace::Family::kDeviceGray:
+      case CPDF_ColorSpace::Family::kDeviceRGB:
+      case CPDF_ColorSpace::Family::kDeviceCMYK: {
         uint32_t dwMinComps = CPDF_ColorSpace::ComponentsForFamily(m_Family);
         if (colorspace_comps < dwMinComps || m_nComponents < dwMinComps)
           return false;
         break;
       }
-      case PDFCS_LAB: {
+      case CPDF_ColorSpace::Family::kLab: {
         if (m_nComponents != 3 || colorspace_comps < 3)
           return false;
         break;
       }
-      case PDFCS_ICCBASED: {
+      case CPDF_ColorSpace::Family::kICCBased: {
         if (!CPDF_ColorSpace::IsValidIccComponents(colorspace_comps) ||
             !CPDF_ColorSpace::IsValidIccComponents(m_nComponents) ||
             colorspace_comps < m_nComponents) {
@@ -602,25 +617,30 @@
       }
     }
   } else {
-    if (m_Family == PDFCS_LAB && m_nComponents != 3)
+    if (m_Family == CPDF_ColorSpace::Family::kLab && m_nComponents != 3)
       return false;
   }
-  if (!GetDecodeAndMaskArray(&m_bDefaultDecode, &m_bColorKey))
+  if (!GetDecodeAndMaskArray())
     return false;
 
   m_bpc = info.bits_per_components;
-  m_pDecoder = pJpegModule->CreateDecoder(src_span, m_Width, m_Height,
-                                          m_nComponents, info.color_transform);
+  m_pDecoder = JpegModule::CreateDecoder(src_span, m_Width, m_Height,
+                                         m_nComponents, info.color_transform);
   return true;
 }
 
-RetainPtr<CFX_DIBitmap> CPDF_DIB::LoadJpxBitmap() {
-  std::unique_ptr<CJPX_Decoder> decoder = JpxModule::CreateDecoder(
-      m_pStreamAcc->GetSpan(),
-      ColorSpaceOptionFromColorSpace(m_pColorSpace.Get()));
+RetainPtr<CFX_DIBitmap> CPDF_DIB::LoadJpxBitmap(
+    uint8_t resolution_levels_to_skip) {
+  std::unique_ptr<CJPX_Decoder> decoder =
+      CJPX_Decoder::Create(m_pStreamAcc->GetSpan(),
+                           ColorSpaceOptionFromColorSpace(m_pColorSpace.Get()),
+                           resolution_levels_to_skip);
   if (!decoder)
     return nullptr;
 
+  m_Height >>= resolution_levels_to_skip;
+  m_Width >>= resolution_levels_to_skip;
+
   if (!decoder->StartDecode())
     return nullptr;
 
@@ -632,43 +652,62 @@
 
   RetainPtr<CPDF_ColorSpace> original_colorspace = m_pColorSpace;
   bool swap_rgb = false;
-  switch (GetJpxDecodeAction(image_info.components, m_pColorSpace.Get())) {
+  bool convert_argb_to_rgb = false;
+  auto action = GetJpxDecodeAction(image_info, m_pColorSpace.Get());
+  switch (action) {
     case JpxDecodeAction::kFail:
       return nullptr;
 
     case JpxDecodeAction::kDoNothing:
       break;
 
+    case JpxDecodeAction::kUseGray:
+      m_pColorSpace =
+          CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray);
+      break;
+
     case JpxDecodeAction::kUseRgb:
-      DCHECK(image_info.components >= 3);
+      DCHECK(image_info.channels >= 3);
       swap_rgb = true;
       m_pColorSpace = nullptr;
       break;
 
     case JpxDecodeAction::kUseCmyk:
-      m_pColorSpace = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
+      m_pColorSpace =
+          CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceCMYK);
       break;
+
+    case JpxDecodeAction::kConvertArgbToRgb:
+      swap_rgb = true;
+      convert_argb_to_rgb = true;
+      m_pColorSpace.Reset();
   }
 
   // If |original_colorspace| exists, then LoadColorInfo() already set
   // |m_nComponents|.
   if (original_colorspace) {
-    DCHECK_NE(0, m_nComponents);
+    DCHECK_NE(0u, m_nComponents);
   } else {
-    DCHECK_EQ(0, m_nComponents);
-    m_nComponents = image_info.components;
+    DCHECK_EQ(0u, m_nComponents);
+    m_nComponents = GetComponentCountFromOpjColorSpace(image_info.colorspace);
+    if (m_nComponents == 0) {
+      return nullptr;
+    }
   }
 
   FXDIB_Format format;
-  if (image_info.components == 1) {
-    format = FXDIB_8bppRgb;
-  } else if (image_info.components <= 3) {
-    format = FXDIB_Rgb;
-  } else if (image_info.components == 4) {
-    format = FXDIB_Rgb32;
+  if (action == JpxDecodeAction::kUseGray) {
+    format = FXDIB_Format::k8bppRgb;
+  } else if (action == JpxDecodeAction::kUseRgb && image_info.channels == 3) {
+    format = FXDIB_Format::kRgb;
+  } else if (action == JpxDecodeAction::kConvertArgbToRgb &&
+             image_info.channels == 4) {
+    format = FXDIB_Format::kRgb32;
+  } else if (action == JpxDecodeAction::kUseRgb && image_info.channels == 4) {
+    format = FXDIB_Format::kRgb32;
   } else {
-    image_info.width = (image_info.width * image_info.components + 2) / 3;
-    format = FXDIB_Rgb;
+    image_info.width = (image_info.width * image_info.channels + 2) / 3;
+    format = FXDIB_Format::kRgb;
   }
 
   auto result_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
@@ -676,45 +715,144 @@
     return nullptr;
 
   result_bitmap->Clear(0xFFFFFFFF);
-  if (!decoder->Decode(result_bitmap->GetBuffer(), result_bitmap->GetPitch(),
-                       swap_rgb)) {
+  if (!decoder->Decode(result_bitmap->GetWritableBuffer(),
+                       result_bitmap->GetPitch(), swap_rgb, m_nComponents)) {
     return nullptr;
   }
 
-  if (m_pColorSpace && m_pColorSpace->GetFamily() == PDFCS_INDEXED &&
-      m_bpc < 8) {
+  if (convert_argb_to_rgb) {
+    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)) {
+      return nullptr;
+    }
+    if (m_pDict->GetIntegerFor("SMaskInData") == 1) {
+      // TODO(thestig): Acrobat does not support "/SMaskInData 1" combined with
+      // filters. Check for that and fail early.
+      DCHECK(m_JpxInlineData.data.empty());
+      m_JpxInlineData.width = image_info.width;
+      m_JpxInlineData.height = image_info.height;
+      m_JpxInlineData.data.reserve(image_info.width * image_info.height);
+      for (uint32_t row = 0; row < image_info.height; ++row) {
+        const uint8_t* src = result_bitmap->GetScanline(row).data();
+        uint8_t* dest = rgb_bitmap->GetWritableScanline(row).data();
+        for (uint32_t col = 0; col < image_info.width; ++col) {
+          uint8_t a = src[3];
+          m_JpxInlineData.data.push_back(a);
+          uint8_t na = 255 - a;
+          uint8_t b = (src[0] * a + 255 * na) / 255;
+          uint8_t g = (src[1] * a + 255 * na) / 255;
+          uint8_t r = (src[2] * a + 255 * na) / 255;
+          dest[0] = b;
+          dest[1] = g;
+          dest[2] = r;
+          src += 4;
+          dest += 3;
+        }
+      }
+    } else {
+      // TODO(thestig): Is there existing code that does this already?
+      for (uint32_t row = 0; row < image_info.height; ++row) {
+        const uint8_t* src = result_bitmap->GetScanline(row).data();
+        uint8_t* dest = rgb_bitmap->GetWritableScanline(row).data();
+        for (uint32_t col = 0; col < image_info.width; ++col) {
+          memcpy(dest, src, 3);
+          src += 4;
+          dest += 3;
+        }
+      }
+    }
+    result_bitmap = std::move(rgb_bitmap);
+  } else if (m_pColorSpace &&
+             m_pColorSpace->GetFamily() == CPDF_ColorSpace::Family::kIndexed &&
+             m_bpc < 8) {
     int scale = 8 - m_bpc;
     for (uint32_t row = 0; row < image_info.height; ++row) {
-      uint8_t* scanline = result_bitmap->GetWritableScanline(row);
+      uint8_t* scanline = result_bitmap->GetWritableScanline(row).data();
       for (uint32_t col = 0; col < image_info.width; ++col) {
         *scanline = (*scanline) >> scale;
         ++scanline;
       }
     }
   }
+
+  // TODO(crbug.com/pdfium/1747): Handle SMaskInData entries for different
+  // color space types.
+
   m_bpc = 8;
   return result_bitmap;
 }
 
+bool CPDF_DIB::LoadInternal(const CPDF_Dictionary* pFormResources,
+                            const CPDF_Dictionary* pPageResources) {
+  if (!m_pStream)
+    return false;
+
+  m_pDict = m_pStream->GetDict();
+  if (!m_pDict)
+    return false;
+
+  m_Width = m_pDict->GetIntegerFor("Width");
+  m_Height = m_pDict->GetIntegerFor("Height");
+  if (!IsValidDimension(m_Width) || !IsValidDimension(m_Height))
+    return false;
+
+  if (!LoadColorInfo(pFormResources, pPageResources))
+    return false;
+
+  if (m_bDoBpcCheck && (m_bpc == 0 || m_nComponents == 0))
+    return false;
+
+  const absl::optional<uint32_t> maybe_size =
+      fxge::CalculatePitch8(m_bpc, m_nComponents, m_Width);
+  if (!maybe_size.has_value())
+    return false;
+
+  FX_SAFE_UINT32 src_size = maybe_size.value();
+  src_size *= m_Height;
+  if (!src_size.IsValid())
+    return false;
+
+  m_pStreamAcc = pdfium::MakeRetain<CPDF_StreamAcc>(m_pStream);
+  m_pStreamAcc->LoadAllDataImageAcc(src_size.ValueOrDie());
+  return !m_pStreamAcc->GetSpan().empty();
+}
+
 CPDF_DIB::LoadState CPDF_DIB::StartLoadMask() {
   m_MatteColor = 0XFFFFFFFF;
+
+  if (!m_JpxInlineData.data.empty()) {
+    auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+    dict->SetNewFor<CPDF_Name>("Type", "XObject");
+    dict->SetNewFor<CPDF_Name>("Subtype", "Image");
+    dict->SetNewFor<CPDF_Name>("ColorSpace", "DeviceGray");
+    dict->SetNewFor<CPDF_Number>("Width", m_JpxInlineData.width);
+    dict->SetNewFor<CPDF_Number>("Height", m_JpxInlineData.height);
+    dict->SetNewFor<CPDF_Number>("BitsPerComponent", 8);
+
+    return StartLoadMaskDIB(
+        pdfium::MakeRetain<CPDF_Stream>(m_JpxInlineData.data, std::move(dict)));
+  }
+
   RetainPtr<const CPDF_Stream> mask(m_pDict->GetStreamFor("SMask"));
   if (!mask) {
-    mask.Reset(ToStream(m_pDict->GetDirectObjectFor("Mask")));
+    mask = ToStream(m_pDict->GetDirectObjectFor("Mask"));
     return mask ? StartLoadMaskDIB(std::move(mask)) : LoadState::kSuccess;
   }
 
-  const CPDF_Array* pMatte = mask->GetDict()->GetArrayFor("Matte");
-  if (pMatte && m_pColorSpace && m_Family != PDFCS_PATTERN &&
+  RetainPtr<const CPDF_Array> pMatte = mask->GetDict()->GetArrayFor("Matte");
+  if (pMatte && m_pColorSpace &&
+      m_Family != CPDF_ColorSpace::Family::kPattern &&
       pMatte->size() == m_nComponents &&
       m_pColorSpace->CountComponents() <= m_nComponents) {
     std::vector<float> colors =
-        ReadArrayElementsToVector(pMatte, m_nComponents);
+        ReadArrayElementsToVector(pMatte.Get(), m_nComponents);
 
     float R;
     float G;
     float B;
-    m_pColorSpace->GetRGB(colors.data(), &R, &G, &B);
+    m_pColorSpace->GetRGB(colors, &R, &G, &B);
     m_MatteColor = ArgbEncode(0, FXSYS_roundf(R * 255), FXSYS_roundf(G * 255),
                               FXSYS_roundf(B * 255));
   }
@@ -748,10 +886,11 @@
 }
 
 CPDF_DIB::LoadState CPDF_DIB::StartLoadMaskDIB(
-    RetainPtr<const CPDF_Stream> mask) {
-  m_pMask = pdfium::MakeRetain<CPDF_DIB>();
-  LoadState ret = m_pMask->StartLoadDIBBase(
-      m_pDocument.Get(), mask.Get(), false, nullptr, nullptr, true, 0, false);
+    RetainPtr<const CPDF_Stream> mask_stream) {
+  m_pMask = pdfium::MakeRetain<CPDF_DIB>(m_pDocument, std::move(mask_stream));
+  LoadState ret = m_pMask->StartLoadDIBBase(false, nullptr, nullptr, true,
+                                            CPDF_ColorSpace::Family::kUnknown,
+                                            false, {0, 0});
   if (ret == LoadState::kContinue) {
     if (m_Status == LoadState::kFail)
       m_Status = LoadState::kContinue;
@@ -763,7 +902,7 @@
 }
 
 void CPDF_DIB::LoadPalette() {
-  if (!m_pColorSpace || m_Family == PDFCS_PATTERN)
+  if (!m_pColorSpace || m_Family == CPDF_ColorSpace::Family::kPattern)
     return;
 
   if (m_bpc == 0)
@@ -778,17 +917,16 @@
     return;
 
   if (bits == 1) {
-    if (m_bDefaultDecode &&
-        (m_Family == PDFCS_DEVICEGRAY || m_Family == PDFCS_DEVICERGB)) {
+    if (m_bDefaultDecode && (m_Family == CPDF_ColorSpace::Family::kDeviceGray ||
+                             m_Family == CPDF_ColorSpace::Family::kDeviceRGB)) {
       return;
     }
     if (m_pColorSpace->CountComponents() > 3) {
       return;
     }
     float color_values[3];
-    color_values[0] = m_CompData[0].m_DecodeMin;
-    color_values[1] = color_values[0];
-    color_values[2] = color_values[0];
+    std::fill(std::begin(color_values), std::end(color_values),
+              m_CompData[0].m_DecodeMin);
 
     float R = 0.0f;
     float G = 0.0f;
@@ -797,12 +935,22 @@
 
     FX_ARGB argb0 = ArgbEncode(255, FXSYS_roundf(R * 255),
                                FXSYS_roundf(G * 255), FXSYS_roundf(B * 255));
-    color_values[0] += m_CompData[0].m_DecodeStep;
-    color_values[1] += m_CompData[0].m_DecodeStep;
-    color_values[2] += m_CompData[0].m_DecodeStep;
-    m_pColorSpace->GetRGB(color_values, &R, &G, &B);
-    FX_ARGB argb1 = ArgbEncode(255, FXSYS_roundf(R * 255),
-                               FXSYS_roundf(G * 255), FXSYS_roundf(B * 255));
+    FX_ARGB argb1;
+    const CPDF_IndexedCS* indexed_cs = m_pColorSpace->AsIndexedCS();
+    if (indexed_cs && indexed_cs->GetMaxIndex() == 0) {
+      // If an indexed color space's hival value is 0, only 1 color is specified
+      // in the lookup table. Another color should be set to 0xFF000000 by
+      // default to set the range of the color space.
+      argb1 = 0xFF000000;
+    } else {
+      color_values[0] += m_CompData[0].m_DecodeStep;
+      color_values[1] += m_CompData[0].m_DecodeStep;
+      color_values[2] += m_CompData[0].m_DecodeStep;
+      m_pColorSpace->GetRGB(color_values, &R, &G, &B);
+      argb1 = ArgbEncode(255, FXSYS_roundf(R * 255), FXSYS_roundf(G * 255),
+                         FXSYS_roundf(B * 255));
+    }
+
     if (argb0 != 0xFF000000 || argb1 != 0xFFFFFFFF) {
       SetPaletteArgb(0, argb0);
       SetPaletteArgb(1, argb1);
@@ -810,7 +958,8 @@
     return;
   }
   if (m_bpc == 8 && m_bDefaultDecode &&
-      m_pColorSpace == CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY)) {
+      m_pColorSpace ==
+          CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray)) {
     return;
   }
 
@@ -828,56 +977,49 @@
     float R = 0;
     float G = 0;
     float B = 0;
-    if (m_nComponents == 1 && m_Family == PDFCS_ICCBASED &&
+    if (m_nComponents == 1 && m_Family == CPDF_ColorSpace::Family::kICCBased &&
         m_pColorSpace->CountComponents() > 1) {
       int nComponents = m_pColorSpace->CountComponents();
       std::vector<float> temp_buf(nComponents);
       for (int k = 0; k < nComponents; ++k)
         temp_buf[k] = color_values[0];
-      m_pColorSpace->GetRGB(temp_buf.data(), &R, &G, &B);
+      m_pColorSpace->GetRGB(temp_buf, &R, &G, &B);
     } else {
-      m_pColorSpace->GetRGB(color_values.data(), &R, &G, &B);
+      m_pColorSpace->GetRGB(color_values, &R, &G, &B);
     }
     SetPaletteArgb(i, ArgbEncode(255, FXSYS_roundf(R * 255),
                                  FXSYS_roundf(G * 255), FXSYS_roundf(B * 255)));
   }
 }
 
-void CPDF_DIB::ValidateDictParam() {
+bool CPDF_DIB::ValidateDictParam(const ByteString& filter) {
   m_bpc = m_bpc_orig;
-  const CPDF_Object* pFilter = m_pDict->GetDirectObjectFor("Filter");
-  if (pFilter) {
-    if (pFilter->IsName()) {
-      ByteString filter = pFilter->GetString();
-      if (filter == "CCITTFaxDecode" || filter == "JBIG2Decode") {
-        m_bpc = 1;
-        m_nComponents = 1;
-      } else if (filter == "RunLengthDecode") {
-        if (m_bpc != 1) {
-          m_bpc = 8;
-        }
-      } else if (filter == "DCTDecode") {
-        m_bpc = 8;
-      }
-    } else if (const CPDF_Array* pArray = pFilter->AsArray()) {
-      ByteString filter = pArray->GetStringAt(pArray->size() - 1);
-      if (filter == "CCITTFaxDecode" || filter == "JBIG2Decode") {
-        m_bpc = 1;
-        m_nComponents = 1;
-      } else if (filter == "DCTDecode") {
-        // Previously, filter == "RunLengthDecode" was checked in the "if"
-        // statement as well, but too many documents don't conform to it.
-        m_bpc = 8;
-      }
-    }
+
+  // Per spec, |m_bpc| should always be 8 for RunLengthDecode, but too many
+  // documents do not conform to it. So skip this check.
+
+  if (filter == "JPXDecode") {
+    m_bDoBpcCheck = false;
+    return true;
   }
 
-  if (!IsAllowedBitsPerComponent(m_bpc))
+  if (filter == "CCITTFaxDecode" || filter == "JBIG2Decode") {
+    m_bpc = 1;
+    m_nComponents = 1;
+  } else if (filter == "DCTDecode") {
+    m_bpc = 8;
+  }
+
+  if (!IsAllowedBitsPerComponent(m_bpc)) {
     m_bpc = 0;
+    return false;
+  }
+  return true;
 }
 
-void CPDF_DIB::TranslateScanline24bpp(uint8_t* dest_scan,
-                                      const uint8_t* src_scan) const {
+void CPDF_DIB::TranslateScanline24bpp(
+    pdfium::span<uint8_t> dest_scan,
+    pdfium::span<const uint8_t> src_scan) const {
   if (m_bpc == 0)
     return;
 
@@ -900,7 +1042,7 @@
         color_values[color] = m_CompData[color].m_DecodeMin +
                               m_CompData[color].m_DecodeStep * data;
       } else {
-        unsigned int data = GetBits8(src_scan, src_bit_pos, m_bpc);
+        unsigned int data = GetBits8(src_scan.data(), src_bit_pos, m_bpc);
         color_values[color] = m_CompData[color].m_DecodeMin +
                               m_CompData[color].m_DecodeStep * data;
         src_bit_pos += m_bpc;
@@ -912,12 +1054,12 @@
       R = (1.0f - color_values[0]) * k;
       G = (1.0f - color_values[1]) * k;
       B = (1.0f - color_values[2]) * k;
-    } else if (m_Family != PDFCS_PATTERN) {
-      m_pColorSpace->GetRGB(color_values.data(), &R, &G, &B);
+    } else if (m_Family != CPDF_ColorSpace::Family::kPattern) {
+      m_pColorSpace->GetRGB(color_values, &R, &G, &B);
     }
-    R = pdfium::clamp(R, 0.0f, 1.0f);
-    G = pdfium::clamp(G, 0.0f, 1.0f);
-    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);
@@ -926,12 +1068,13 @@
 }
 
 bool CPDF_DIB::TranslateScanline24bppDefaultDecode(
-    uint8_t* dest_scan,
-    const uint8_t* src_scan) const {
+    pdfium::span<uint8_t> dest_scan,
+    pdfium::span<const uint8_t> src_scan) const {
   if (!m_bDefaultDecode)
     return false;
 
-  if (m_Family != PDFCS_DEVICERGB && m_Family != PDFCS_CALRGB) {
+  if (m_Family != CPDF_ColorSpace::Family::kDeviceRGB &&
+      m_Family != CPDF_ColorSpace::Family::kCalRGB) {
     if (m_bpc != 8)
       return false;
 
@@ -945,21 +1088,22 @@
   if (m_nComponents != 3)
     return true;
 
-  const uint8_t* src_pos = src_scan;
+  uint8_t* dest_pos = dest_scan.data();
+  const uint8_t* src_pos = src_scan.data();
   switch (m_bpc) {
     case 8:
       for (int column = 0; column < m_Width; column++) {
-        *dest_scan++ = src_pos[2];
-        *dest_scan++ = src_pos[1];
-        *dest_scan++ = *src_pos;
+        *dest_pos++ = src_pos[2];
+        *dest_pos++ = src_pos[1];
+        *dest_pos++ = *src_pos;
         src_pos += 3;
       }
       break;
     case 16:
       for (int col = 0; col < m_Width; col++) {
-        *dest_scan++ = src_pos[4];
-        *dest_scan++ = src_pos[2];
-        *dest_scan++ = *src_pos;
+        *dest_pos++ = src_pos[4];
+        *dest_pos++ = src_pos[2];
+        *dest_pos++ = *src_pos;
         src_pos += 6;
       }
       break;
@@ -968,18 +1112,18 @@
       uint64_t src_bit_pos = 0;
       size_t dest_byte_pos = 0;
       for (int column = 0; column < m_Width; column++) {
-        unsigned int R = GetBits8(src_scan, src_bit_pos, m_bpc);
+        unsigned int R = GetBits8(src_scan.data(), src_bit_pos, m_bpc);
         src_bit_pos += m_bpc;
-        unsigned int G = GetBits8(src_scan, src_bit_pos, m_bpc);
+        unsigned int G = GetBits8(src_scan.data(), src_bit_pos, m_bpc);
         src_bit_pos += m_bpc;
-        unsigned int B = GetBits8(src_scan, src_bit_pos, m_bpc);
+        unsigned int B = GetBits8(src_scan.data(), src_bit_pos, m_bpc);
         src_bit_pos += m_bpc;
         R = std::min(R, max_data);
         G = std::min(G, max_data);
         B = std::min(B, max_data);
-        dest_scan[dest_byte_pos] = B * 255 / max_data;
-        dest_scan[dest_byte_pos + 1] = G * 255 / max_data;
-        dest_scan[dest_byte_pos + 2] = R * 255 / max_data;
+        dest_pos[dest_byte_pos] = B * 255 / max_data;
+        dest_pos[dest_byte_pos + 1] = G * 255 / max_data;
+        dest_pos[dest_byte_pos + 2] = R * 255 / max_data;
         dest_byte_pos += 3;
       }
       break;
@@ -987,424 +1131,175 @@
   return true;
 }
 
-uint8_t* CPDF_DIB::GetBuffer() const {
-  return m_pCachedBitmap ? m_pCachedBitmap->GetBuffer() : nullptr;
+pdfium::span<const uint8_t> CPDF_DIB::GetBuffer() const {
+  return m_pCachedBitmap ? m_pCachedBitmap->GetBuffer()
+                         : pdfium::span<const uint8_t>();
 }
 
-const uint8_t* CPDF_DIB::GetScanline(int line) const {
+pdfium::span<const uint8_t> CPDF_DIB::GetScanline(int line) const {
   if (m_bpc == 0)
-    return nullptr;
+    return pdfium::span<const uint8_t>();
 
-  FX_SAFE_UINT32 src_pitch =
-      fxcodec::CalculatePitch8(m_bpc, m_nComponents, m_Width);
-  if (!src_pitch.IsValid())
-    return nullptr;
-  uint32_t src_pitch_value = src_pitch.ValueOrDie();
+  const absl::optional<uint32_t> src_pitch =
+      fxge::CalculatePitch8(m_bpc, m_nComponents, m_Width);
+  if (!src_pitch.has_value())
+    return pdfium::span<const uint8_t>();
 
-  const uint8_t* pSrcLine = nullptr;
+  uint32_t src_pitch_value = src_pitch.value();
+  // This is used as the buffer of `pSrcLine` when the stream is truncated,
+  // and the remaining bytes count is less than `src_pitch_value`
+  DataVector<uint8_t> temp_buffer;
+  pdfium::span<const uint8_t> pSrcLine;
+
   if (m_pCachedBitmap && src_pitch_value <= m_pCachedBitmap->GetPitch()) {
-    if (line >= m_pCachedBitmap->GetHeight()) {
+    if (line >= m_pCachedBitmap->GetHeight())
       line = m_pCachedBitmap->GetHeight() - 1;
-    }
     pSrcLine = m_pCachedBitmap->GetScanline(line);
   } else if (m_pDecoder) {
     pSrcLine = m_pDecoder->GetScanline(line);
-  } else if (m_pStreamAcc->GetSize() >= (line + 1) * src_pitch_value) {
-    pSrcLine = m_pStreamAcc->GetData() + line * src_pitch_value;
-  }
-  if (!pSrcLine) {
-    uint8_t* pLineBuf = m_pMaskedLine ? m_pMaskedLine.get() : m_pLineBuf.get();
-    memset(pLineBuf, 0xFF, m_Pitch);
-    return pLineBuf;
+  } else if (m_pStreamAcc->GetSize() > line * src_pitch_value) {
+    pdfium::span<const uint8_t> remaining_bytes =
+        m_pStreamAcc->GetSpan().subspan(line * src_pitch_value);
+    if (remaining_bytes.size() >= src_pitch_value) {
+      pSrcLine = remaining_bytes.first(src_pitch_value);
+    } else {
+      temp_buffer = DataVector<uint8_t>(src_pitch_value);
+      pdfium::span<uint8_t> result = temp_buffer;
+      fxcrt::spancpy(result, remaining_bytes);
+      pSrcLine = result;
+    }
   }
 
+  if (pSrcLine.empty()) {
+    pdfium::span<uint8_t> result = !m_MaskBuf.empty() ? m_MaskBuf : m_LineBuf;
+    fxcrt::spanset(result, 0);
+    return result;
+  }
   if (m_bpc * m_nComponents == 1) {
     if (m_bImageMask && m_bDefaultDecode) {
-      for (uint32_t i = 0; i < src_pitch_value; i++)
-        m_pLineBuf.get()[i] = ~pSrcLine[i];
-      return m_pLineBuf.get();
+      for (uint32_t i = 0; i < src_pitch_value; i++) {
+        // TODO(tsepez): Bounds check if cost is acceptable.
+        m_LineBuf[i] = ~pSrcLine.data()[i];
+      }
+      return pdfium::make_span(m_LineBuf).first(src_pitch_value);
     }
-
     if (!m_bColorKey) {
-      memcpy(m_pLineBuf.get(), pSrcLine, src_pitch_value);
-      return m_pLineBuf.get();
+      pdfium::span<uint8_t> result = m_LineBuf;
+      fxcrt::spancpy(result, pSrcLine.first(src_pitch_value));
+      return result.first(src_pitch_value);
     }
-
-    uint32_t reset_argb = m_pPalette ? m_pPalette.get()[0] : 0xFF000000;
-    uint32_t set_argb = m_pPalette ? m_pPalette.get()[1] : 0xFFFFFFFF;
-    if (m_CompData[0].m_ColorKeyMin == 0)
-      reset_argb = 0;
-    if (m_CompData[0].m_ColorKeyMax == 1)
-      set_argb = 0;
-    set_argb = FXARGB_TODIB(set_argb);
-    reset_argb = FXARGB_TODIB(reset_argb);
-    uint32_t* dest_scan = reinterpret_cast<uint32_t*>(m_pMaskedLine.get());
-    for (int col = 0; col < m_Width; col++) {
-      *dest_scan = GetBitValue(pSrcLine, col) ? set_argb : reset_argb;
-      dest_scan++;
+    uint32_t reset_argb = Get1BitResetValue();
+    uint32_t set_argb = Get1BitSetValue();
+    uint32_t* dest_scan = reinterpret_cast<uint32_t*>(m_MaskBuf.data());
+    for (int col = 0; col < m_Width; col++, dest_scan++) {
+      *dest_scan = GetBitValue(pSrcLine.data(), col) ? set_argb : reset_argb;
     }
-    return m_pMaskedLine.get();
+    return pdfium::make_span(m_MaskBuf).first(m_Width * sizeof(uint32_t));
   }
   if (m_bpc * m_nComponents <= 8) {
+    pdfium::span<uint8_t> result = m_LineBuf;
     if (m_bpc == 8) {
-      memcpy(m_pLineBuf.get(), pSrcLine, src_pitch_value);
+      fxcrt::spancpy(result, pSrcLine.first(src_pitch_value));
+      result = result.first(src_pitch_value);
     } else {
       uint64_t src_bit_pos = 0;
       for (int col = 0; col < m_Width; col++) {
         unsigned int color_index = 0;
         for (uint32_t color = 0; color < m_nComponents; color++) {
-          unsigned int data = GetBits8(pSrcLine, src_bit_pos, m_bpc);
+          unsigned int data = GetBits8(pSrcLine.data(), src_bit_pos, m_bpc);
           color_index |= data << (color * m_bpc);
           src_bit_pos += m_bpc;
         }
-        m_pLineBuf.get()[col] = color_index;
+        m_LineBuf[col] = color_index;
       }
+      result = result.first(m_Width);
     }
     if (!m_bColorKey)
-      return m_pLineBuf.get();
+      return result;
 
-    uint8_t* pDestPixel = m_pMaskedLine.get();
-    const uint8_t* pSrcPixel = m_pLineBuf.get();
-    for (int col = 0; col < m_Width; col++) {
-      uint8_t index = *pSrcPixel++;
-      if (m_pPalette) {
-        *pDestPixel++ = FXARGB_B(m_pPalette.get()[index]);
-        *pDestPixel++ = FXARGB_G(m_pPalette.get()[index]);
-        *pDestPixel++ = FXARGB_R(m_pPalette.get()[index]);
-      } else {
-        *pDestPixel++ = index;
-        *pDestPixel++ = index;
-        *pDestPixel++ = index;
+    uint8_t* pDestPixel = m_MaskBuf.data();
+    const uint8_t* pSrcPixel = m_LineBuf.data();
+    pdfium::span<const uint32_t> palette = GetPaletteSpan();
+    if (HasPalette()) {
+      for (int col = 0; col < m_Width; col++) {
+        uint8_t index = *pSrcPixel++;
+        *pDestPixel++ = FXARGB_B(palette[index]);
+        *pDestPixel++ = FXARGB_G(palette[index]);
+        *pDestPixel++ = FXARGB_R(palette[index]);
+        *pDestPixel++ =
+            IsColorIndexOutOfBounds(index, m_CompData[0]) ? 0xFF : 0;
       }
-      *pDestPixel = IsColorIndexOutOfBounds(index, m_CompData[0]) ? 0xFF : 0;
-      pDestPixel++;
+    } else {
+      for (int col = 0; col < m_Width; col++) {
+        uint8_t index = *pSrcPixel++;
+        *pDestPixel++ = index;
+        *pDestPixel++ = index;
+        *pDestPixel++ = index;
+        *pDestPixel++ =
+            IsColorIndexOutOfBounds(index, m_CompData[0]) ? 0xFF : 0;
+      }
     }
-    return m_pMaskedLine.get();
+    return pdfium::make_span(m_MaskBuf).first(4 * m_Width);
   }
   if (m_bColorKey) {
     if (m_nComponents == 3 && m_bpc == 8) {
-      uint8_t* alpha_channel = m_pMaskedLine.get() + 3;
+      uint8_t* alpha_channel = m_MaskBuf.data() + 3;
       for (int col = 0; col < m_Width; col++) {
-        const uint8_t* pPixel = pSrcLine + col * 3;
+        const uint8_t* pPixel = pSrcLine.data() + col * 3;
         alpha_channel[col * 4] =
             AreColorIndicesOutOfBounds(pPixel, m_CompData.data(), 3) ? 0xFF : 0;
       }
     } else {
-      memset(m_pMaskedLine.get(), 0xFF, m_Pitch);
+      fxcrt::spanset(pdfium::make_span(m_MaskBuf), 0xFF);
     }
   }
   if (m_pColorSpace) {
-    TranslateScanline24bpp(m_pLineBuf.get(), pSrcLine);
-    pSrcLine = m_pLineBuf.get();
+    TranslateScanline24bpp(m_LineBuf, pSrcLine);
+    src_pitch_value = 3 * m_Width;
+    pSrcLine = pdfium::make_span(m_LineBuf).first(src_pitch_value);
   }
   if (!m_bColorKey)
     return pSrcLine;
 
-  const uint8_t* pSrcPixel = pSrcLine;
-  uint8_t* pDestPixel = m_pMaskedLine.get();
+  // TODO(tsepez): Bounds check if cost is acceptable.
+  const uint8_t* pSrcPixel = pSrcLine.data();
+  uint8_t* pDestPixel = m_MaskBuf.data();
   for (int col = 0; col < m_Width; col++) {
     *pDestPixel++ = *pSrcPixel++;
     *pDestPixel++ = *pSrcPixel++;
     *pDestPixel++ = *pSrcPixel++;
     pDestPixel++;
   }
-  return m_pMaskedLine.get();
+  return pdfium::make_span(m_MaskBuf).first(4 * m_Width);
 }
 
 bool CPDF_DIB::SkipToScanline(int line, PauseIndicatorIface* pPause) const {
   return m_pDecoder && m_pDecoder->SkipToScanline(line, pPause);
 }
 
-void CPDF_DIB::DownSampleScanline(int line,
-                                  uint8_t* dest_scan,
-                                  int dest_bpp,
-                                  int dest_width,
-                                  bool bFlipX,
-                                  int clip_left,
-                                  int clip_width) const {
-  if (line < 0 || !dest_scan || dest_bpp <= 0 || dest_width <= 0 ||
-      clip_left < 0 || clip_width <= 0) {
-    return;
-  }
-
-  uint32_t src_width = m_Width;
-  FX_SAFE_UINT32 pitch =
-      fxcodec::CalculatePitch8(m_bpc, m_nComponents, m_Width);
-  if (!pitch.IsValid())
-    return;
-
-  const uint8_t* pSrcLine = nullptr;
-  if (m_pCachedBitmap) {
-    pSrcLine = m_pCachedBitmap->GetScanline(line);
-  } else if (m_pDecoder) {
-    pSrcLine = m_pDecoder->GetScanline(line);
-  } else {
-    uint32_t src_pitch = pitch.ValueOrDie();
-    pitch *= (line + 1);
-    if (!pitch.IsValid()) {
-      return;
-    }
-
-    if (m_pStreamAcc->GetSize() >= pitch.ValueOrDie()) {
-      pSrcLine = m_pStreamAcc->GetData() + line * src_pitch;
-    }
-  }
-  int orig_Bpp = m_bpc * m_nComponents / 8;
-  int dest_Bpp = dest_bpp / 8;
-  if (!pSrcLine) {
-    memset(dest_scan, 0xFF, dest_Bpp * clip_width);
-    return;
-  }
-
-  FX_SAFE_INT32 max_src_x = clip_left;
-  max_src_x += clip_width - 1;
-  max_src_x *= src_width;
-  max_src_x /= dest_width;
-  if (!max_src_x.IsValid())
-    return;
-
-  if (m_bpc * m_nComponents == 1) {
-    DownSampleScanline1Bit(orig_Bpp, dest_Bpp, src_width, pSrcLine, dest_scan,
-                           dest_width, bFlipX, clip_left, clip_width);
-  } else if (m_bpc * m_nComponents <= 8) {
-    DownSampleScanline8Bit(orig_Bpp, dest_Bpp, src_width, pSrcLine, dest_scan,
-                           dest_width, bFlipX, clip_left, clip_width);
-  } else {
-    DownSampleScanline32Bit(orig_Bpp, dest_Bpp, src_width, pSrcLine, dest_scan,
-                            dest_width, bFlipX, clip_left, clip_width);
-  }
-}
-
-void CPDF_DIB::DownSampleScanline1Bit(int orig_Bpp,
-                                      int dest_Bpp,
-                                      uint32_t src_width,
-                                      const uint8_t* pSrcLine,
-                                      uint8_t* dest_scan,
-                                      int dest_width,
-                                      bool bFlipX,
-                                      int clip_left,
-                                      int clip_width) const {
-  if (m_bColorKey && !m_bImageMask) {
-    uint32_t reset_argb = m_pPalette ? m_pPalette.get()[0] : 0xFF000000;
-    uint32_t set_argb = m_pPalette ? m_pPalette.get()[1] : 0xFFFFFFFF;
-    if (m_CompData[0].m_ColorKeyMin == 0)
-      reset_argb = 0;
-    if (m_CompData[0].m_ColorKeyMax == 1)
-      set_argb = 0;
-    set_argb = FXARGB_TODIB(set_argb);
-    reset_argb = FXARGB_TODIB(reset_argb);
-    uint32_t* dest_scan_dword = reinterpret_cast<uint32_t*>(dest_scan);
-    for (int i = 0; i < clip_width; i++) {
-      uint32_t src_x = (clip_left + i) * src_width / dest_width;
-      if (bFlipX)
-        src_x = src_width - src_x - 1;
-      src_x %= src_width;
-      dest_scan_dword[i] = GetBitValue(pSrcLine, src_x) ? set_argb : reset_argb;
-    }
-    return;
-  }
-
-  uint32_t set_argb = 0xFFFFFFFF;
-  uint32_t reset_argb = 0;
-  if (m_bImageMask) {
-    if (m_bDefaultDecode) {
-      set_argb = 0;
-      reset_argb = 0xFFFFFFFF;
-    }
-  } else if (m_pPalette && dest_Bpp != 1) {
-    reset_argb = m_pPalette.get()[0];
-    set_argb = m_pPalette.get()[1];
-  }
-  for (int i = 0; i < clip_width; i++) {
-    uint32_t src_x = (clip_left + i) * src_width / dest_width;
-    if (bFlipX)
-      src_x = src_width - src_x - 1;
-    src_x %= src_width;
-    int dest_pos = i * dest_Bpp;
-    uint32_t value_argb = GetBitValue(pSrcLine, src_x) ? set_argb : reset_argb;
-    if (dest_Bpp == 1) {
-      dest_scan[dest_pos] = static_cast<uint8_t>(value_argb);
-    } else if (dest_Bpp == 3) {
-      dest_scan[dest_pos] = FXARGB_B(value_argb);
-      dest_scan[dest_pos + 1] = FXARGB_G(value_argb);
-      dest_scan[dest_pos + 2] = FXARGB_R(value_argb);
-    } else {
-      *reinterpret_cast<uint32_t*>(dest_scan + dest_pos) = value_argb;
-    }
-  }
-}
-
-void CPDF_DIB::DownSampleScanline8Bit(int orig_Bpp,
-                                      int dest_Bpp,
-                                      uint32_t src_width,
-                                      const uint8_t* pSrcLine,
-                                      uint8_t* dest_scan,
-                                      int dest_width,
-                                      bool bFlipX,
-                                      int clip_left,
-                                      int clip_width) const {
-  if (m_bpc < 8) {
-    uint64_t src_bit_pos = 0;
-    for (uint32_t col = 0; col < src_width; col++) {
-      unsigned int color_index = 0;
-      for (uint32_t color = 0; color < m_nComponents; color++) {
-        unsigned int data = GetBits8(pSrcLine, src_bit_pos, m_bpc);
-        color_index |= data << (color * m_bpc);
-        src_bit_pos += m_bpc;
-      }
-      m_pLineBuf.get()[col] = color_index;
-    }
-    pSrcLine = m_pLineBuf.get();
-  }
-  if (m_bColorKey) {
-    for (int i = 0; i < clip_width; i++) {
-      uint32_t src_x = (clip_left + i) * src_width / dest_width;
-      if (bFlipX) {
-        src_x = src_width - src_x - 1;
-      }
-      src_x %= src_width;
-      uint8_t* pDestPixel = dest_scan + i * 4;
-      uint8_t index = pSrcLine[src_x];
-      if (m_pPalette) {
-        *pDestPixel++ = FXARGB_B(m_pPalette.get()[index]);
-        *pDestPixel++ = FXARGB_G(m_pPalette.get()[index]);
-        *pDestPixel++ = FXARGB_R(m_pPalette.get()[index]);
-      } else {
-        *pDestPixel++ = index;
-        *pDestPixel++ = index;
-        *pDestPixel++ = index;
-      }
-      *pDestPixel = (index < m_CompData[0].m_ColorKeyMin ||
-                     index > m_CompData[0].m_ColorKeyMax)
-                        ? 0xFF
-                        : 0;
-    }
-    return;
-  }
-  for (int i = 0; i < clip_width; i++) {
-    uint32_t src_x = (clip_left + i) * src_width / dest_width;
-    if (bFlipX)
-      src_x = src_width - src_x - 1;
-    src_x %= src_width;
-    uint8_t index = pSrcLine[src_x];
-    if (dest_Bpp == 1) {
-      dest_scan[i] = index;
-    } else {
-      int dest_pos = i * dest_Bpp;
-      FX_ARGB argb = m_pPalette.get()[index];
-      dest_scan[dest_pos] = FXARGB_B(argb);
-      dest_scan[dest_pos + 1] = FXARGB_G(argb);
-      dest_scan[dest_pos + 2] = FXARGB_R(argb);
-    }
-  }
-}
-
-void CPDF_DIB::DownSampleScanline32Bit(int orig_Bpp,
-                                       int dest_Bpp,
-                                       uint32_t src_width,
-                                       const uint8_t* pSrcLine,
-                                       uint8_t* dest_scan,
-                                       int dest_width,
-                                       bool bFlipX,
-                                       int clip_left,
-                                       int clip_width) const {
-  // last_src_x used to store the last seen src_x position which should be
-  // in [0, src_width). Set the initial value to be an invalid src_x value.
-  uint32_t last_src_x = src_width;
-  FX_ARGB last_argb = ArgbEncode(0xFF, 0xFF, 0xFF, 0xFF);
-  float unit_To8Bpc = 255.0f / ((1 << m_bpc) - 1);
-  for (int i = 0; i < clip_width; i++) {
-    int dest_x = clip_left + i;
-    uint32_t src_x = (bFlipX ? (dest_width - dest_x - 1) : dest_x) *
-                     (int64_t)src_width / dest_width;
-    src_x %= src_width;
-
-    uint8_t* pDestPixel = dest_scan + i * dest_Bpp;
-    FX_ARGB argb;
-    if (src_x == last_src_x) {
-      argb = last_argb;
-    } else {
-      CFX_FixedBufGrow<uint8_t, 16> extracted_components(m_nComponents);
-      const uint8_t* pSrcPixel = nullptr;
-      if (m_bpc % 8 != 0) {
-        // No need to check for 32-bit overflow, as |src_x| is bounded by
-        // |src_width| and DownSampleScanline() already checked for overflow
-        // with the pitch calculation.
-        size_t num_bits = src_x * m_bpc * m_nComponents;
-        uint64_t src_bit_pos = num_bits % 8;
-        pSrcPixel = pSrcLine + num_bits / 8;
-        for (uint32_t j = 0; j < m_nComponents; ++j) {
-          extracted_components[j] = static_cast<uint8_t>(
-              GetBits8(pSrcPixel, src_bit_pos, m_bpc) * unit_To8Bpc);
-          src_bit_pos += m_bpc;
-        }
-        pSrcPixel = extracted_components;
-      } else {
-        pSrcPixel = pSrcLine + src_x * orig_Bpp;
-        if (m_bpc == 16) {
-          for (uint32_t j = 0; j < m_nComponents; ++j)
-            extracted_components[j] = pSrcPixel[j * 2];
-          pSrcPixel = extracted_components;
-        }
-      }
-
-      if (m_pColorSpace) {
-        uint8_t color[4];
-        const bool bTransMask = TransMask();
-        if (!m_bDefaultDecode) {
-          for (uint32_t j = 0; j < m_nComponents; ++j) {
-            float component_value = static_cast<float>(pSrcPixel[j]);
-            int color_value = static_cast<int>(
-                (m_CompData[j].m_DecodeMin +
-                 m_CompData[j].m_DecodeStep * component_value) *
-                    255.0f +
-                0.5f);
-            extracted_components[j] = pdfium::clamp(color_value, 0, 255);
-          }
-        }
-        const uint8_t* pSrc =
-            m_bDefaultDecode ? pSrcPixel : extracted_components;
-        m_pColorSpace->TranslateImageLine(color, pSrc, 1, 0, 0, bTransMask);
-        argb = ArgbEncode(0xFF, color[2], color[1], color[0]);
-      } else {
-        argb = ArgbEncode(0xFF, pSrcPixel[2], pSrcPixel[1], pSrcPixel[0]);
-      }
-      if (m_bColorKey) {
-        int alpha = 0xFF;
-        if (m_nComponents == 3 && m_bpc == 8) {
-          alpha = (pSrcPixel[0] < m_CompData[0].m_ColorKeyMin ||
-                   pSrcPixel[0] > m_CompData[0].m_ColorKeyMax ||
-                   pSrcPixel[1] < m_CompData[1].m_ColorKeyMin ||
-                   pSrcPixel[1] > m_CompData[1].m_ColorKeyMax ||
-                   pSrcPixel[2] < m_CompData[2].m_ColorKeyMin ||
-                   pSrcPixel[2] > m_CompData[2].m_ColorKeyMax)
-                      ? 0xFF
-                      : 0;
-        }
-        argb &= 0xFFFFFF;
-        argb |= alpha << 24;
-      }
-      last_src_x = src_x;
-      last_argb = argb;
-    }
-    if (dest_Bpp == 4) {
-      *reinterpret_cast<uint32_t*>(pDestPixel) = FXARGB_TODIB(argb);
-    } else {
-      *pDestPixel++ = FXARGB_B(argb);
-      *pDestPixel++ = FXARGB_G(argb);
-      *pDestPixel = FXARGB_R(argb);
-    }
-  }
+size_t CPDF_DIB::GetEstimatedImageMemoryBurden() const {
+  return m_pCachedBitmap ? m_pCachedBitmap->GetEstimatedImageMemoryBurden() : 0;
 }
 
 bool CPDF_DIB::TransMask() const {
-  return m_bLoadMask && m_GroupFamily == PDFCS_DEVICECMYK &&
-         m_Family == PDFCS_DEVICECMYK;
+  return m_bLoadMask && m_GroupFamily == CPDF_ColorSpace::Family::kDeviceCMYK &&
+         m_Family == CPDF_ColorSpace::Family::kDeviceCMYK;
 }
 
 void CPDF_DIB::SetMaskProperties() {
-  m_bpp = 1;
   m_bpc = 1;
   m_nComponents = 1;
-  m_AlphaFlag = 1;
+  m_Format = FXDIB_Format::k1bppMask;
+}
+
+uint32_t CPDF_DIB::Get1BitSetValue() const {
+  if (m_CompData[0].m_ColorKeyMax == 1)
+    return 0x00000000;
+  return HasPalette() ? GetPaletteSpan()[1] : 0xFFFFFFFF;
+}
+
+uint32_t CPDF_DIB::Get1BitResetValue() const {
+  if (m_CompData[0].m_ColorKeyMin == 0)
+    return 0x00000000;
+  return HasPalette() ? GetPaletteSpan()[0] : 0xFF000000;
 }
diff --git a/core/fpdfapi/page/cpdf_dib.h b/core/fpdfapi/page/cpdf_dib.h
index 6960cea..5206bf5 100644
--- a/core/fpdfapi/page/cpdf_dib.h
+++ b/core/fpdfapi/page/cpdf_dib.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,17 +7,17 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_DIB_H_
 #define CORE_FPDFAPI_PAGE_CPDF_DIB_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
-#include "core/fpdfapi/page/cpdf_clippath.h"
 #include "core/fpdfapi/page/cpdf_colorspace.h"
-#include "core/fpdfapi/page/cpdf_graphicstates.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/dib/cfx_dibbase.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Dictionary;
 class CPDF_Document;
@@ -42,100 +42,79 @@
  public:
   enum class LoadState : uint8_t { kFail, kSuccess, kContinue };
 
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  bool Load(CPDF_Document* pDoc, const CPDF_Stream* pStream);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // CFX_DIBBase:
+  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;
-  uint8_t* GetBuffer() const override;
-  const uint8_t* GetScanline(int line) const override;
-  void DownSampleScanline(int line,
-                          uint8_t* dest_scan,
-                          int dest_bpp,
-                          int dest_width,
-                          bool bFlipX,
-                          int clip_left,
-                          int clip_width) const override;
+  size_t GetEstimatedImageMemoryBurden() const override;
 
   RetainPtr<CPDF_ColorSpace> GetColorSpace() const { return m_pColorSpace; }
   uint32_t GetMatteColor() const { return m_MatteColor; }
+  bool IsJBigImage() const;
 
-  LoadState StartLoadDIBBase(CPDF_Document* pDoc,
-                             const CPDF_Stream* pStream,
-                             bool bHasMask,
+  bool Load();
+  LoadState StartLoadDIBBase(bool bHasMask,
                              const CPDF_Dictionary* pFormResources,
-                             CPDF_Dictionary* pPageResources,
+                             const CPDF_Dictionary* pPageResources,
                              bool bStdCS,
-                             uint32_t GroupFamily,
-                             bool bLoadMask);
+                             CPDF_ColorSpace::Family GroupFamily,
+                             bool bLoadMask,
+                             const CFX_Size& max_size_required);
   LoadState ContinueLoadDIBBase(PauseIndicatorIface* pPause);
   RetainPtr<CPDF_DIB> DetachMask();
 
-  bool IsJBigImage() const;
-
  private:
-  CPDF_DIB();
+  CPDF_DIB(CPDF_Document* pDoc, RetainPtr<const CPDF_Stream> pStream);
   ~CPDF_DIB() override;
 
+  struct JpxSMaskInlineData {
+    JpxSMaskInlineData();
+    ~JpxSMaskInlineData();
+
+    int width = 0;
+    int height = 0;
+    DataVector<uint8_t> data;
+  };
+
+  bool LoadInternal(const CPDF_Dictionary* pFormResources,
+                    const CPDF_Dictionary* pPageResources);
+  bool ContinueInternal();
   LoadState StartLoadMask();
-  LoadState StartLoadMaskDIB(RetainPtr<const CPDF_Stream> mask);
+  LoadState StartLoadMaskDIB(RetainPtr<const CPDF_Stream> mask_stream);
   bool ContinueToLoadMask();
   LoadState ContinueLoadMaskDIB(PauseIndicatorIface* pPause);
   bool LoadColorInfo(const CPDF_Dictionary* pFormResources,
                      const CPDF_Dictionary* pPageResources);
-  bool GetDecodeAndMaskArray(bool* bDefaultDecode, bool* bColorKey);
-  RetainPtr<CFX_DIBitmap> LoadJpxBitmap();
+  bool GetDecodeAndMaskArray();
+  RetainPtr<CFX_DIBitmap> LoadJpxBitmap(uint8_t resolution_levels_to_skip);
   void LoadPalette();
-  LoadState CreateDecoder();
+  LoadState CreateDecoder(uint8_t resolution_levels_to_skip);
   bool CreateDCTDecoder(pdfium::span<const uint8_t> src_span,
                         const CPDF_Dictionary* pParams);
-  void TranslateScanline24bpp(uint8_t* dest_scan,
-                              const uint8_t* src_scan) const;
-  bool TranslateScanline24bppDefaultDecode(uint8_t* dest_scan,
-                                           const uint8_t* src_scan) const;
-  void ValidateDictParam();
-  void DownSampleScanline1Bit(int orig_Bpp,
-                              int dest_Bpp,
-                              uint32_t src_width,
-                              const uint8_t* pSrcLine,
-                              uint8_t* dest_scan,
-                              int dest_width,
-                              bool bFlipX,
-                              int clip_left,
-                              int clip_width) const;
-  void DownSampleScanline8Bit(int orig_Bpp,
-                              int dest_Bpp,
-                              uint32_t src_width,
-                              const uint8_t* pSrcLine,
-                              uint8_t* dest_scan,
-                              int dest_width,
-                              bool bFlipX,
-                              int clip_left,
-                              int clip_width) const;
-  void DownSampleScanline32Bit(int orig_Bpp,
-                               int dest_Bpp,
-                               uint32_t src_width,
-                               const uint8_t* pSrcLine,
-                               uint8_t* dest_scan,
-                               int dest_width,
-                               bool bFlipX,
-                               int clip_left,
-                               int clip_width) const;
+  void TranslateScanline24bpp(pdfium::span<uint8_t> dest_scan,
+                              pdfium::span<const uint8_t> src_scan) const;
+  bool TranslateScanline24bppDefaultDecode(
+      pdfium::span<uint8_t> dest_scan,
+      pdfium::span<const uint8_t> src_scan) const;
+  bool ValidateDictParam(const ByteString& filter);
   bool TransMask() const;
   void SetMaskProperties();
 
-  UnownedPtr<CPDF_Document> m_pDocument;
-  RetainPtr<const CPDF_Stream> m_pStream;
+  uint32_t Get1BitSetValue() const;
+  uint32_t Get1BitResetValue() const;
+
+  UnownedPtr<CPDF_Document> const m_pDocument;
+  RetainPtr<const CPDF_Stream> const m_pStream;
   RetainPtr<const CPDF_Dictionary> m_pDict;
   RetainPtr<CPDF_StreamAcc> m_pStreamAcc;
   RetainPtr<CPDF_ColorSpace> m_pColorSpace;
-  uint32_t m_Family = 0;
   uint32_t m_bpc = 0;
   uint32_t m_bpc_orig = 0;
   uint32_t m_nComponents = 0;
-  uint32_t m_GroupFamily = 0;
+  CPDF_ColorSpace::Family m_Family = CPDF_ColorSpace::Family::kUnknown;
+  CPDF_ColorSpace::Family m_GroupFamily = CPDF_ColorSpace::Family::kUnknown;
   uint32_t m_MatteColor = 0;
   LoadState m_Status = LoadState::kFail;
   bool m_bLoadMask = false;
@@ -146,13 +125,14 @@
   bool m_bHasMask = false;
   bool m_bStdCS = false;
   std::vector<DIB_COMP_DATA> m_CompData;
-  std::unique_ptr<uint8_t, FxFreeDeleter> m_pLineBuf;
-  std::unique_ptr<uint8_t, FxFreeDeleter> m_pMaskedLine;
+  mutable DataVector<uint8_t> m_LineBuf;
+  mutable DataVector<uint8_t> m_MaskBuf;
   RetainPtr<CFX_DIBitmap> m_pCachedBitmap;
   // Note: Must not create a cycle between CPDF_DIB instances.
   RetainPtr<CPDF_DIB> m_pMask;
   RetainPtr<CPDF_StreamAcc> m_pGlobalAcc;
   std::unique_ptr<fxcodec::ScanlineDecoder> m_pDecoder;
+  JpxSMaskInlineData m_JpxInlineData;
 
   // Must come after |m_pCachedBitmap|.
   std::unique_ptr<fxcodec::Jbig2Context> m_pJbig2Context;
diff --git a/core/fpdfapi/page/cpdf_docpagedata.cpp b/core/fpdfapi/page/cpdf_docpagedata.cpp
index 9c8bc92..b00a616 100644
--- a/core/fpdfapi/page/cpdf_docpagedata.cpp
+++ b/core/fpdfapi/page/cpdf_docpagedata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include "build/build_config.h"
+#include "constants/font_encodings.h"
 #include "core/fpdfapi/font/cpdf_type1font.h"
 #include "core/fpdfapi/page/cpdf_form.h"
 #include "core/fpdfapi/page/cpdf_iccprofile.h"
@@ -30,14 +31,16 @@
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/scoped_set_insertion.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_fontmapper.h"
 #include "core/fxge/cfx_substfont.h"
 #include "core/fxge/cfx_unicodeencoding.h"
 #include "core/fxge/fx_font.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
 
 namespace {
 
@@ -49,17 +52,17 @@
   }
   if (i == widths.size()) {
     int first = pWidthArray->GetIntegerAt(pWidthArray->size() - 1);
-    pWidthArray->AddNew<CPDF_Number>(first + static_cast<int>(widths.size()) -
-                                     1);
-    pWidthArray->AddNew<CPDF_Number>(widths[0]);
+    pWidthArray->AppendNew<CPDF_Number>(first +
+                                        static_cast<int>(widths.size()) - 1);
+    pWidthArray->AppendNew<CPDF_Number>(widths[0]);
     return;
   }
-  CPDF_Array* pWidthArray1 = pWidthArray->AddNew<CPDF_Array>();
+  auto pWidthArray1 = pWidthArray->AppendNew<CPDF_Array>();
   for (int w : widths)
-    pWidthArray1->AddNew<CPDF_Number>(w);
+    pWidthArray1->AppendNew<CPDF_Number>(w);
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 void InsertWidthArray(HDC hDC, int start, int end, CPDF_Array* pWidthArray) {
   std::vector<int> widths(end - start + 1);
   GetCharWidth(hDC, start, end, widths.data());
@@ -77,7 +80,7 @@
   }
   return result;
 }
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 void InsertWidthArray1(CFX_Font* pFont,
                        CFX_UnicodeEncoding* pEncoding,
@@ -116,7 +119,7 @@
   return flags;
 }
 
-void ProcessNonbCJK(CPDF_Dictionary* pBaseDict,
+void ProcessNonbCJK(RetainPtr<CPDF_Dictionary> pBaseDict,
                     bool bold,
                     bool italic,
                     ByteString basefont,
@@ -174,7 +177,8 @@
   CPDF_PageModule::GetInstance()->ClearStockFont(GetDocument());
 }
 
-RetainPtr<CPDF_Font> CPDF_DocPageData::GetFont(CPDF_Dictionary* pFontDict) {
+RetainPtr<CPDF_Font> CPDF_DocPageData::GetFont(
+    RetainPtr<CPDF_Dictionary> pFontDict) {
   if (!pFontDict)
     return nullptr;
 
@@ -187,7 +191,7 @@
   if (!pFont)
     return nullptr;
 
-  m_FontMap[pFontDict].Reset(pFont.Get());
+  m_FontMap[std::move(pFontDict)].Reset(pFont.Get());
   return pFont;
 }
 
@@ -217,7 +221,7 @@
     return pdfium::WrapRetain(pFont);
   }
 
-  CPDF_Dictionary* pDict = GetDocument()->NewIndirect<CPDF_Dictionary>();
+  auto pDict = GetDocument()->NewIndirect<CPDF_Dictionary>();
   pDict->SetNewFor<CPDF_Name>("Type", "Font");
   pDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
   pDict->SetNewFor<CPDF_Name>("BaseFont", fontName);
@@ -231,7 +235,7 @@
   if (!pFont)
     return nullptr;
 
-  m_FontMap[pDict].Reset(pFont.Get());
+  m_FontMap[std::move(pDict)].Reset(pFont.Get());
   return pFont;
 }
 
@@ -258,73 +262,76 @@
   if (!pCSObj)
     return nullptr;
 
-  if (pdfium::ContainsKey(*pVisitedInternal, pCSObj))
+  if (pdfium::Contains(*pVisitedInternal, pCSObj))
     return nullptr;
 
-  pdfium::ScopedSetInsertion<const CPDF_Object*> insertion(pVisitedInternal,
-                                                           pCSObj);
+  ScopedSetInsertion<const CPDF_Object*> insertion(pVisitedInternal, pCSObj);
 
   if (pCSObj->IsName()) {
     ByteString name = pCSObj->GetString();
-    RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::ColorspaceFromName(name);
+    RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::GetStockCSForName(name);
     if (!pCS && pResources) {
-      const CPDF_Dictionary* pList = pResources->GetDictFor("ColorSpace");
+      RetainPtr<const CPDF_Dictionary> pList =
+          pResources->GetDictFor("ColorSpace");
       if (pList) {
-        return GetColorSpaceInternal(pList->GetDirectObjectFor(name), nullptr,
-                                     pVisited, pVisitedInternal);
+        return GetColorSpaceInternal(pList->GetDirectObjectFor(name).Get(),
+                                     nullptr, pVisited, pVisitedInternal);
       }
     }
     if (!pCS || !pResources)
       return pCS;
 
-    const CPDF_Dictionary* pColorSpaces = pResources->GetDictFor("ColorSpace");
+    RetainPtr<const CPDF_Dictionary> pColorSpaces =
+        pResources->GetDictFor("ColorSpace");
     if (!pColorSpaces)
       return pCS;
 
-    const CPDF_Object* pDefaultCS = nullptr;
+    RetainPtr<const CPDF_Object> pDefaultCS;
     switch (pCS->GetFamily()) {
-      case PDFCS_DEVICERGB:
+      case CPDF_ColorSpace::Family::kDeviceRGB:
         pDefaultCS = pColorSpaces->GetDirectObjectFor("DefaultRGB");
         break;
-      case PDFCS_DEVICEGRAY:
+      case CPDF_ColorSpace::Family::kDeviceGray:
         pDefaultCS = pColorSpaces->GetDirectObjectFor("DefaultGray");
         break;
-      case PDFCS_DEVICECMYK:
+      case CPDF_ColorSpace::Family::kDeviceCMYK:
         pDefaultCS = pColorSpaces->GetDirectObjectFor("DefaultCMYK");
         break;
+      default:
+        break;
     }
     if (!pDefaultCS)
       return pCS;
 
-    return GetColorSpaceInternal(pDefaultCS, nullptr, pVisited,
+    return GetColorSpaceInternal(pDefaultCS.Get(), nullptr, pVisited,
                                  pVisitedInternal);
   }
 
-  const CPDF_Array* pArray = pCSObj->AsArray();
+  RetainPtr<const CPDF_Array> pArray(pCSObj->AsArray());
   if (!pArray || pArray->IsEmpty())
     return nullptr;
 
   if (pArray->size() == 1) {
-    return GetColorSpaceInternal(pArray->GetDirectObjectAt(0), pResources,
+    return GetColorSpaceInternal(pArray->GetDirectObjectAt(0).Get(), pResources,
                                  pVisited, pVisitedInternal);
   }
 
-  auto it = m_ColorSpaceMap.find(pCSObj);
+  auto it = m_ColorSpaceMap.find(pArray);
   if (it != m_ColorSpaceMap.end() && it->second)
     return pdfium::WrapRetain(it->second.Get());
 
   RetainPtr<CPDF_ColorSpace> pCS =
-      CPDF_ColorSpace::Load(GetDocument(), pArray, pVisited);
+      CPDF_ColorSpace::Load(GetDocument(), pArray.Get(), pVisited);
   if (!pCS)
     return nullptr;
 
-  m_ColorSpaceMap[pCSObj].Reset(pCS.Get());
+  m_ColorSpaceMap[std::move(pArray)].Reset(pCS.Get());
   return pCS;
 }
 
-RetainPtr<CPDF_Pattern> CPDF_DocPageData::GetPattern(CPDF_Object* pPatternObj,
-                                                     bool bShading,
-                                                     const CFX_Matrix& matrix) {
+RetainPtr<CPDF_Pattern> CPDF_DocPageData::GetPattern(
+    RetainPtr<CPDF_Object> pPatternObj,
+    const CFX_Matrix& matrix) {
   if (!pPatternObj)
     return nullptr;
 
@@ -332,33 +339,43 @@
   if (it != m_PatternMap.end() && it->second)
     return pdfium::WrapRetain(it->second.Get());
 
+  RetainPtr<const CPDF_Dictionary> pDict = pPatternObj->GetDict();
+  if (!pDict)
+    return nullptr;
+
   RetainPtr<CPDF_Pattern> pPattern;
-  if (bShading) {
+  int type = pDict->GetIntegerFor("PatternType");
+  if (type == CPDF_Pattern::kTiling) {
+    pPattern = pdfium::MakeRetain<CPDF_TilingPattern>(GetDocument(),
+                                                      pPatternObj, matrix);
+  } else if (type == CPDF_Pattern::kShading) {
     pPattern = pdfium::MakeRetain<CPDF_ShadingPattern>(
-        GetDocument(), pPatternObj, true, matrix);
+        GetDocument(), pPatternObj, false, matrix);
   } else {
-    CPDF_Dictionary* pDict = pPatternObj->GetDict();
-    if (!pDict)
-      return nullptr;
-
-    int type = pDict->GetIntegerFor("PatternType");
-    if (type == CPDF_Pattern::kTiling) {
-      pPattern = pdfium::MakeRetain<CPDF_TilingPattern>(GetDocument(),
-                                                        pPatternObj, matrix);
-    } else if (type == CPDF_Pattern::kShading) {
-      pPattern = pdfium::MakeRetain<CPDF_ShadingPattern>(
-          GetDocument(), pPatternObj, false, matrix);
-    } else {
-      return nullptr;
-    }
+    return nullptr;
   }
+  m_PatternMap[pPatternObj].Reset(pPattern.Get());
+  return pPattern;
+}
 
+RetainPtr<CPDF_ShadingPattern> CPDF_DocPageData::GetShading(
+    RetainPtr<CPDF_Object> pPatternObj,
+    const CFX_Matrix& matrix) {
+  if (!pPatternObj)
+    return nullptr;
+
+  auto it = m_PatternMap.find(pPatternObj);
+  if (it != m_PatternMap.end() && it->second)
+    return pdfium::WrapRetain(it->second->AsShadingPattern());
+
+  auto pPattern = pdfium::MakeRetain<CPDF_ShadingPattern>(
+      GetDocument(), pPatternObj, true, matrix);
   m_PatternMap[pPatternObj].Reset(pPattern.Get());
   return pPattern;
 }
 
 RetainPtr<CPDF_Image> CPDF_DocPageData::GetImage(uint32_t dwStreamObjNum) {
-  ASSERT(dwStreamObjNum);
+  DCHECK(dwStreamObjNum);
   auto it = m_ImageMap.find(dwStreamObjNum);
   if (it != m_ImageMap.end())
     return it->second;
@@ -369,14 +386,14 @@
 }
 
 void CPDF_DocPageData::MaybePurgeImage(uint32_t dwStreamObjNum) {
-  ASSERT(dwStreamObjNum);
+  DCHECK(dwStreamObjNum);
   auto it = m_ImageMap.find(dwStreamObjNum);
   if (it != m_ImageMap.end() && it->second->HasOneRef())
     m_ImageMap.erase(it);
 }
 
 RetainPtr<CPDF_IccProfile> CPDF_DocPageData::GetIccProfile(
-    const CPDF_Stream* pProfileStream) {
+    RetainPtr<const CPDF_Stream> pProfileStream) {
   if (!pProfileStream)
     return nullptr;
 
@@ -390,25 +407,25 @@
   ByteString bsDigest = pAccessor->ComputeDigest();
   auto hash_it = m_HashProfileMap.find(bsDigest);
   if (hash_it != m_HashProfileMap.end()) {
-    auto it_copied_stream = m_IccProfileMap.find(hash_it->second.Get());
+    auto it_copied_stream = m_IccProfileMap.find(hash_it->second);
     if (it_copied_stream != m_IccProfileMap.end() && it_copied_stream->second)
       return pdfium::WrapRetain(it_copied_stream->second.Get());
   }
   auto pProfile =
       pdfium::MakeRetain<CPDF_IccProfile>(pProfileStream, pAccessor->GetSpan());
   m_IccProfileMap[pProfileStream].Reset(pProfile.Get());
-  m_HashProfileMap[bsDigest].Reset(pProfileStream);
+  m_HashProfileMap[bsDigest] = std::move(pProfileStream);
   return pProfile;
 }
 
 RetainPtr<CPDF_StreamAcc> CPDF_DocPageData::GetFontFileStreamAcc(
-    const CPDF_Stream* pFontStream) {
-  ASSERT(pFontStream);
+    RetainPtr<const CPDF_Stream> pFontStream) {
+  DCHECK(pFontStream);
   auto it = m_FontFileMap.find(pFontStream);
   if (it != m_FontFileMap.end())
     return it->second;
 
-  const CPDF_Dictionary* pFontDict = pFontStream->GetDict();
+  RetainPtr<const CPDF_Dictionary> pFontDict = pFontStream->GetDict();
   int32_t len1 = pFontDict->GetIntegerFor("Length1");
   int32_t len2 = pFontDict->GetIntegerFor("Length2");
   int32_t len3 = pFontDict->GetIntegerFor("Length3");
@@ -422,38 +439,46 @@
 
   auto pFontAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pFontStream);
   pFontAcc->LoadAllDataFilteredWithEstimatedSize(org_size);
-  m_FontFileMap[pFontStream] = pFontAcc;
+  m_FontFileMap[std::move(pFontStream)] = pFontAcc;
   return pFontAcc;
 }
 
 void CPDF_DocPageData::MaybePurgeFontFileStreamAcc(
-    const CPDF_Stream* pFontStream) {
+    RetainPtr<CPDF_StreamAcc>&& pStreamAcc) {
+  if (!pStreamAcc)
+    return;
+
+  RetainPtr<const CPDF_Stream> pFontStream = pStreamAcc->GetStream();
   if (!pFontStream)
     return;
 
-  auto it = m_FontFileMap.find(pFontStream);
+  pStreamAcc.Reset();  // Drop moved caller's reference.
+  auto it = m_FontFileMap.find(std::move(pFontStream));
   if (it != m_FontFileMap.end() && it->second->HasOneRef())
     m_FontFileMap.erase(it);
 }
 
 std::unique_ptr<CPDF_Font::FormIface> CPDF_DocPageData::CreateForm(
     CPDF_Document* pDocument,
-    CPDF_Dictionary* pPageResources,
-    CPDF_Stream* pFormStream) {
-  return pdfium::MakeUnique<CPDF_Form>(pDocument, pPageResources, pFormStream);
+    RetainPtr<CPDF_Dictionary> pPageResources,
+    RetainPtr<CPDF_Stream> pFormStream) {
+  return std::make_unique<CPDF_Form>(pDocument, std::move(pPageResources),
+                                     std::move(pFormStream));
 }
 
 RetainPtr<CPDF_Font> CPDF_DocPageData::AddStandardFont(
     const ByteString& fontName,
     const CPDF_FontEncoding* pEncoding) {
   ByteString mutable_name(fontName);
-  if (!CFX_FontMapper::GetStandardFontName(&mutable_name))
+  absl::optional<CFX_FontMapper::StandardFont> font_id =
+      CFX_FontMapper::GetStandardFontName(&mutable_name);
+  if (!font_id.has_value())
     return nullptr;
   return GetStandardFont(mutable_name, pEncoding);
 }
 
 RetainPtr<CPDF_Font> CPDF_DocPageData::AddFont(std::unique_ptr<CFX_Font> pFont,
-                                               int charset) {
+                                               FX_Charset charset) {
   if (!pFont)
     return nullptr;
 
@@ -462,35 +487,37 @@
   basefont.Replace(" ", "");
   int flags =
       CalculateFlags(pFont->IsBold(), pFont->IsItalic(), pFont->IsFixedWidth(),
-                     false, false, charset == FX_CHARSET_Symbol);
+                     false, false, charset == FX_Charset::kSymbol);
 
-  CPDF_Dictionary* pBaseDict = GetDocument()->NewIndirect<CPDF_Dictionary>();
+  auto pBaseDict = GetDocument()->NewIndirect<CPDF_Dictionary>();
   pBaseDict->SetNewFor<CPDF_Name>("Type", "Font");
-  auto pEncoding = pdfium::MakeUnique<CFX_UnicodeEncoding>(pFont.get());
-  CPDF_Dictionary* pFontDict = pBaseDict;
+
+  auto pEncoding = std::make_unique<CFX_UnicodeEncoding>(pFont.get());
+  RetainPtr<CPDF_Dictionary> pFontDict = pBaseDict;
   if (!bCJK) {
     auto pWidths = pdfium::MakeRetain<CPDF_Array>();
     for (int charcode = 32; charcode < 128; charcode++) {
       int glyph_index = pEncoding->GlyphFromCharCode(charcode);
       int char_width = pFont->GetGlyphWidth(glyph_index);
-      pWidths->AddNew<CPDF_Number>(char_width);
+      pWidths->AppendNew<CPDF_Number>(char_width);
     }
-    if (charset == FX_CHARSET_ANSI || charset == FX_CHARSET_Default ||
-        charset == FX_CHARSET_Symbol) {
-      pBaseDict->SetNewFor<CPDF_Name>("Encoding", "WinAnsiEncoding");
+    if (charset == FX_Charset::kANSI || charset == FX_Charset::kDefault ||
+        charset == FX_Charset::kSymbol) {
+      pBaseDict->SetNewFor<CPDF_Name>("Encoding",
+                                      pdfium::font_encodings::kWinAnsiEncoding);
       for (int charcode = 128; charcode <= 255; charcode++) {
         int glyph_index = pEncoding->GlyphFromCharCode(charcode);
         int char_width = pFont->GetGlyphWidth(glyph_index);
-        pWidths->AddNew<CPDF_Number>(char_width);
+        pWidths->AppendNew<CPDF_Number>(char_width);
       }
     } else {
-      size_t i = CalculateEncodingDict(charset, pBaseDict);
-      if (i < FX_ArraySize(g_FX_CharsetUnicodes)) {
-        const uint16_t* pUnicodes = g_FX_CharsetUnicodes[i].m_pUnicodes;
+      size_t i = CalculateEncodingDict(charset, pBaseDict.Get());
+      if (i < std::size(kFX_CharsetUnicodes)) {
+        const uint16_t* pUnicodes = kFX_CharsetUnicodes[i].m_pUnicodes;
         for (int j = 0; j < 128; j++) {
           int glyph_index = pEncoding->GlyphFromCharCode(pUnicodes[j]);
           int char_width = pFont->GetGlyphWidth(glyph_index);
-          pWidths->AddNew<CPDF_Number>(char_width);
+          pWidths->AppendNew<CPDF_Number>(char_width);
         }
       }
     }
@@ -504,19 +531,18 @@
         });
   }
   int italicangle = pFont->GetSubstFontItalicAngle();
-  FX_RECT bbox;
-  pFont->GetBBox(&bbox);
+  FX_RECT bbox = pFont->GetBBox().value_or(FX_RECT());
   auto pBBox = pdfium::MakeRetain<CPDF_Array>();
-  pBBox->AddNew<CPDF_Number>(bbox.left);
-  pBBox->AddNew<CPDF_Number>(bbox.bottom);
-  pBBox->AddNew<CPDF_Number>(bbox.right);
-  pBBox->AddNew<CPDF_Number>(bbox.top);
+  pBBox->AppendNew<CPDF_Number>(bbox.left);
+  pBBox->AppendNew<CPDF_Number>(bbox.bottom);
+  pBBox->AppendNew<CPDF_Number>(bbox.right);
+  pBBox->AppendNew<CPDF_Number>(bbox.top);
   int32_t nStemV = 0;
   if (pFont->GetSubstFont()) {
     nStemV = pFont->GetSubstFont()->m_Weight / 5;
   } else {
     static const char stem_chars[] = {'i', 'I', '!', '1'};
-    const size_t count = FX_ArraySize(stem_chars);
+    const size_t count = std::size(stem_chars);
     uint32_t glyph = pEncoding->GlyphFromCharCode(stem_chars[0]);
     nStemV = pFont->GetGlyphWidth(glyph);
     for (size_t i = 1; i < count; i++) {
@@ -526,16 +552,16 @@
         nStemV = width;
     }
   }
-  CPDF_Dictionary* pFontDesc =
-      ToDictionary(GetDocument()->AddIndirectObject(CalculateFontDesc(
-          GetDocument(), basefont, flags, italicangle, pFont->GetAscent(),
-          pFont->GetDescent(), std::move(pBBox), nStemV)));
+  RetainPtr<CPDF_Dictionary> pFontDesc = CalculateFontDesc(
+      GetDocument(), basefont, flags, italicangle, pFont->GetAscent(),
+      pFont->GetDescent(), std::move(pBBox), nStemV);
+  uint32_t new_objnum = GetDocument()->AddIndirectObject(std::move(pFontDesc));
   pFontDict->SetNewFor<CPDF_Reference>("FontDescriptor", GetDocument(),
-                                       pFontDesc->GetObjNum());
+                                       new_objnum);
   return GetFont(pBaseDict);
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 RetainPtr<CPDF_Font> CPDF_DocPageData::AddWindowsFont(LOGFONTA* pLogFont) {
   pLogFont->lfHeight = -1000;
   pLogFont->lfWidth = 0;
@@ -553,13 +579,15 @@
   LPBYTE tm_buf = FX_Alloc(BYTE, tm_size);
   OUTLINETEXTMETRIC* ptm = reinterpret_cast<OUTLINETEXTMETRIC*>(tm_buf);
   GetOutlineTextMetrics(hDC, tm_size, ptm);
-  int flags = CalculateFlags(false, pLogFont->lfItalic != 0,
-                             (pLogFont->lfPitchAndFamily & 3) == FIXED_PITCH,
-                             (pLogFont->lfPitchAndFamily & 0xf8) == FF_ROMAN,
-                             (pLogFont->lfPitchAndFamily & 0xf8) == FF_SCRIPT,
-                             pLogFont->lfCharSet == FX_CHARSET_Symbol);
+  int flags = CalculateFlags(
+      false, pLogFont->lfItalic != 0,
+      (pLogFont->lfPitchAndFamily & 3) == FIXED_PITCH,
+      (pLogFont->lfPitchAndFamily & 0xf8) == FF_ROMAN,
+      (pLogFont->lfPitchAndFamily & 0xf8) == FF_SCRIPT,
+      pLogFont->lfCharSet == static_cast<int>(FX_Charset::kSymbol));
 
-  const bool bCJK = FX_CharSetIsCJK(pLogFont->lfCharSet);
+  const FX_Charset eCharset = FX_GetCharsetFromInt(pLogFont->lfCharSet);
+  const bool bCJK = FX_CharSetIsCJK(eCharset);
   ByteString basefont;
   if (bCJK)
     basefont = GetPSNameFromTT(hDC);
@@ -575,122 +603,122 @@
                  ptm->otmrcFontBox.right, ptm->otmrcFontBox.top};
   FX_Free(tm_buf);
   basefont.Replace(" ", "");
-  CPDF_Dictionary* pBaseDict = GetDocument()->NewIndirect<CPDF_Dictionary>();
+  auto pBaseDict = GetDocument()->NewIndirect<CPDF_Dictionary>();
   pBaseDict->SetNewFor<CPDF_Name>("Type", "Font");
-  CPDF_Dictionary* pFontDict = pBaseDict;
+  RetainPtr<CPDF_Dictionary> pFontDict = pBaseDict;
   if (!bCJK) {
-    if (pLogFont->lfCharSet == FX_CHARSET_ANSI ||
-        pLogFont->lfCharSet == FX_CHARSET_Default ||
-        pLogFont->lfCharSet == FX_CHARSET_Symbol) {
-      pBaseDict->SetNewFor<CPDF_Name>("Encoding", "WinAnsiEncoding");
+    if (eCharset == FX_Charset::kANSI || eCharset == FX_Charset::kDefault ||
+        eCharset == FX_Charset::kSymbol) {
+      pBaseDict->SetNewFor<CPDF_Name>("Encoding",
+                                      pdfium::font_encodings::kWinAnsiEncoding);
     } else {
-      CalculateEncodingDict(pLogFont->lfCharSet, pBaseDict);
+      CalculateEncodingDict(eCharset, pBaseDict.Get());
     }
     int char_widths[224];
     GetCharWidth(hDC, 32, 255, char_widths);
     auto pWidths = pdfium::MakeRetain<CPDF_Array>();
     for (size_t i = 0; i < 224; i++)
-      pWidths->AddNew<CPDF_Number>(char_widths[i]);
+      pWidths->AppendNew<CPDF_Number>(char_widths[i]);
     ProcessNonbCJK(pBaseDict, pLogFont->lfWeight > FW_MEDIUM,
                    pLogFont->lfItalic != 0, basefont, std::move(pWidths));
   } else {
     pFontDict =
-        ProcessbCJK(pBaseDict, pLogFont->lfCharSet, basefont,
+        ProcessbCJK(pBaseDict, eCharset, basefont,
                     [&hDC](wchar_t start, wchar_t end, CPDF_Array* widthArr) {
                       InsertWidthArray(hDC, start, end, widthArr);
                     });
   }
   auto pBBox = pdfium::MakeRetain<CPDF_Array>();
   for (int i = 0; i < 4; i++)
-    pBBox->AddNew<CPDF_Number>(bbox[i]);
+    pBBox->AppendNew<CPDF_Number>(bbox[i]);
   RetainPtr<CPDF_Dictionary> pFontDesc =
       CalculateFontDesc(GetDocument(), basefont, flags, italicangle, ascend,
                         descend, std::move(pBBox), pLogFont->lfWeight / 5);
   pFontDesc->SetNewFor<CPDF_Number>("CapHeight", capheight);
-  pFontDict->SetFor("FontDescriptor",
-                    GetDocument()
-                        ->AddIndirectObject(std::move(pFontDesc))
-                        ->MakeReference(GetDocument()));
+  GetDocument()->AddIndirectObject(pFontDesc);
+  pFontDict->SetFor("FontDescriptor", pFontDesc->MakeReference(GetDocument()));
   hFont = SelectObject(hDC, hFont);
   DeleteObject(hFont);
   DeleteDC(hDC);
-  return GetFont(pBaseDict);
+  return GetFont(std::move(pBaseDict));
 }
-#endif  //  defined(OS_WIN)
+#endif  //  BUILDFLAG(IS_WIN)
 
-size_t CPDF_DocPageData::CalculateEncodingDict(int charset,
+size_t CPDF_DocPageData::CalculateEncodingDict(FX_Charset charset,
                                                CPDF_Dictionary* pBaseDict) {
   size_t i;
-  for (i = 0; i < FX_ArraySize(g_FX_CharsetUnicodes); ++i) {
-    if (g_FX_CharsetUnicodes[i].m_Charset == charset)
+  for (i = 0; i < std::size(kFX_CharsetUnicodes); ++i) {
+    if (kFX_CharsetUnicodes[i].m_Charset == charset)
       break;
   }
-  if (i == FX_ArraySize(g_FX_CharsetUnicodes))
+  if (i == std::size(kFX_CharsetUnicodes))
     return i;
 
-  CPDF_Dictionary* pEncodingDict =
-      GetDocument()->NewIndirect<CPDF_Dictionary>();
-  pEncodingDict->SetNewFor<CPDF_Name>("BaseEncoding", "WinAnsiEncoding");
+  auto pEncodingDict = GetDocument()->NewIndirect<CPDF_Dictionary>();
+  pEncodingDict->SetNewFor<CPDF_Name>("BaseEncoding",
+                                      pdfium::font_encodings::kWinAnsiEncoding);
 
-  CPDF_Array* pArray = pEncodingDict->SetNewFor<CPDF_Array>("Differences");
-  pArray->AddNew<CPDF_Number>(128);
+  auto pArray = pEncodingDict->SetNewFor<CPDF_Array>("Differences");
+  pArray->AppendNew<CPDF_Number>(128);
 
-  const uint16_t* pUnicodes = g_FX_CharsetUnicodes[i].m_pUnicodes;
+  const uint16_t* pUnicodes = kFX_CharsetUnicodes[i].m_pUnicodes;
   for (int j = 0; j < 128; j++) {
-    ByteString name = PDF_AdobeNameFromUnicode(pUnicodes[j]);
-    pArray->AddNew<CPDF_Name>(name.IsEmpty() ? ".notdef" : name);
+    ByteString name = AdobeNameFromUnicode(pUnicodes[j]);
+    pArray->AppendNew<CPDF_Name>(name.IsEmpty() ? ".notdef" : name);
   }
   pBaseDict->SetNewFor<CPDF_Reference>("Encoding", GetDocument(),
                                        pEncodingDict->GetObjNum());
   return i;
 }
 
-CPDF_Dictionary* CPDF_DocPageData::ProcessbCJK(
-    CPDF_Dictionary* pBaseDict,
-    int charset,
+RetainPtr<CPDF_Dictionary> CPDF_DocPageData::ProcessbCJK(
+    RetainPtr<CPDF_Dictionary> pBaseDict,
+    FX_Charset charset,
     ByteString basefont,
     std::function<void(wchar_t, wchar_t, CPDF_Array*)> Insert) {
-  CPDF_Dictionary* pFontDict = GetDocument()->NewIndirect<CPDF_Dictionary>();
+  auto pFontDict = GetDocument()->NewIndirect<CPDF_Dictionary>();
   ByteString cmap;
   ByteString ordering;
   int supplement = 0;
-  CPDF_Array* pWidthArray = pFontDict->SetNewFor<CPDF_Array>("W");
+  auto pWidthArray = pFontDict->SetNewFor<CPDF_Array>("W");
   switch (charset) {
-    case FX_CHARSET_ChineseTraditional:
+    case FX_Charset::kChineseTraditional:
       cmap = "ETenms-B5-H";
       ordering = "CNS1";
       supplement = 4;
-      pWidthArray->AddNew<CPDF_Number>(1);
-      Insert(0x20, 0x7e, pWidthArray);
+      pWidthArray->AppendNew<CPDF_Number>(1);
+      Insert(0x20, 0x7e, pWidthArray.Get());
       break;
-    case FX_CHARSET_ChineseSimplified:
+    case FX_Charset::kChineseSimplified:
       cmap = "GBK-EUC-H";
       ordering = "GB1";
       supplement = 2;
-      pWidthArray->AddNew<CPDF_Number>(7716);
-      Insert(0x20, 0x20, pWidthArray);
-      pWidthArray->AddNew<CPDF_Number>(814);
-      Insert(0x21, 0x7e, pWidthArray);
+      pWidthArray->AppendNew<CPDF_Number>(7716);
+      Insert(0x20, 0x20, pWidthArray.Get());
+      pWidthArray->AppendNew<CPDF_Number>(814);
+      Insert(0x21, 0x7e, pWidthArray.Get());
       break;
-    case FX_CHARSET_Hangul:
+    case FX_Charset::kHangul:
       cmap = "KSCms-UHC-H";
       ordering = "Korea1";
       supplement = 2;
-      pWidthArray->AddNew<CPDF_Number>(1);
-      Insert(0x20, 0x7e, pWidthArray);
+      pWidthArray->AppendNew<CPDF_Number>(1);
+      Insert(0x20, 0x7e, pWidthArray.Get());
       break;
-    case FX_CHARSET_ShiftJIS:
+    case FX_Charset::kShiftJIS:
       cmap = "90ms-RKSJ-H";
       ordering = "Japan1";
       supplement = 5;
-      pWidthArray->AddNew<CPDF_Number>(231);
-      Insert(0x20, 0x7d, pWidthArray);
-      pWidthArray->AddNew<CPDF_Number>(326);
-      Insert(0xa0, 0xa0, pWidthArray);
-      pWidthArray->AddNew<CPDF_Number>(327);
-      Insert(0xa1, 0xdf, pWidthArray);
-      pWidthArray->AddNew<CPDF_Number>(631);
-      Insert(0x7e, 0x7e, pWidthArray);
+      pWidthArray->AppendNew<CPDF_Number>(231);
+      Insert(0x20, 0x7d, pWidthArray.Get());
+      pWidthArray->AppendNew<CPDF_Number>(326);
+      Insert(0xa0, 0xa0, pWidthArray.Get());
+      pWidthArray->AppendNew<CPDF_Number>(327);
+      Insert(0xa1, 0xdf, pWidthArray.Get());
+      pWidthArray->AppendNew<CPDF_Number>(631);
+      Insert(0x7e, 0x7e, pWidthArray.Get());
+      break;
+    default:
       break;
   }
   pBaseDict->SetNewFor<CPDF_Name>("Subtype", "Type0");
@@ -700,13 +728,12 @@
   pFontDict->SetNewFor<CPDF_Name>("Subtype", "CIDFontType2");
   pFontDict->SetNewFor<CPDF_Name>("BaseFont", basefont);
 
-  CPDF_Dictionary* pCIDSysInfo =
-      pFontDict->SetNewFor<CPDF_Dictionary>("CIDSystemInfo");
+  auto pCIDSysInfo = pFontDict->SetNewFor<CPDF_Dictionary>("CIDSystemInfo");
   pCIDSysInfo->SetNewFor<CPDF_String>("Registry", "Adobe", false);
   pCIDSysInfo->SetNewFor<CPDF_String>("Ordering", ordering, false);
   pCIDSysInfo->SetNewFor<CPDF_Number>("Supplement", supplement);
 
-  CPDF_Array* pArray = pBaseDict->SetNewFor<CPDF_Array>("DescendantFonts");
-  pArray->AddNew<CPDF_Reference>(GetDocument(), pFontDict->GetObjNum());
+  auto pArray = pBaseDict->SetNewFor<CPDF_Array>("DescendantFonts");
+  pArray->AppendNew<CPDF_Reference>(GetDocument(), pFontDict->GetObjNum());
   return pFontDict;
 }
diff --git a/core/fpdfapi/page/cpdf_docpagedata.h b/core/fpdfapi/page/cpdf_docpagedata.h
index 854d80d..59c0354 100644
--- a/core/fpdfapi/page/cpdf_docpagedata.h
+++ b/core/fpdfapi/page/cpdf_docpagedata.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,11 +14,11 @@
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/fx_codepage_forward.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
 
 class CFX_Font;
 class CPDF_Dictionary;
@@ -30,8 +30,8 @@
 class CPDF_Stream;
 class CPDF_StreamAcc;
 
-class CPDF_DocPageData : public CPDF_Document::PageDataIface,
-                         public CPDF_Font::FormFactoryIface {
+class CPDF_DocPageData final : public CPDF_Document::PageDataIface,
+                               public CPDF_Font::FormFactoryIface {
  public:
   static CPDF_DocPageData* FromDocument(const CPDF_Document* pDoc);
 
@@ -41,24 +41,27 @@
   // CPDF_Document::PageDataIface:
   void ClearStockFont() override;
   RetainPtr<CPDF_StreamAcc> GetFontFileStreamAcc(
-      const CPDF_Stream* pFontStream) override;
-  void MaybePurgeFontFileStreamAcc(const CPDF_Stream* pFontStream) override;
+      RetainPtr<const CPDF_Stream> pFontStream) override;
+  void MaybePurgeFontFileStreamAcc(
+      RetainPtr<CPDF_StreamAcc>&& pStreamAcc) override;
+  void MaybePurgeImage(uint32_t dwStreamObjNum) override;
 
   // CPDF_Font::FormFactoryIFace:
   std::unique_ptr<CPDF_Font::FormIface> CreateForm(
       CPDF_Document* pDocument,
-      CPDF_Dictionary* pPageResources,
-      CPDF_Stream* pFormStream) override;
+      RetainPtr<CPDF_Dictionary> pPageResources,
+      RetainPtr<CPDF_Stream> pFormStream) override;
 
   bool IsForceClear() const { return m_bForceClear; }
 
-  RetainPtr<CPDF_Font> AddFont(std::unique_ptr<CFX_Font> pFont, int charset);
-  RetainPtr<CPDF_Font> GetFont(CPDF_Dictionary* pFontDict);
+  RetainPtr<CPDF_Font> AddFont(std::unique_ptr<CFX_Font> pFont,
+                               FX_Charset charset);
+  RetainPtr<CPDF_Font> GetFont(RetainPtr<CPDF_Dictionary> pFontDict);
   RetainPtr<CPDF_Font> AddStandardFont(const ByteString& fontName,
                                        const CPDF_FontEncoding* pEncoding);
   RetainPtr<CPDF_Font> GetStandardFont(const ByteString& fontName,
                                        const CPDF_FontEncoding* pEncoding);
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   RetainPtr<CPDF_Font> AddWindowsFont(LOGFONTA* pLogFont);
 #endif
 
@@ -74,14 +77,15 @@
       const CPDF_Dictionary* pResources,
       std::set<const CPDF_Object*>* pVisited);
 
-  RetainPtr<CPDF_Pattern> GetPattern(CPDF_Object* pPatternObj,
-                                     bool bShading,
+  RetainPtr<CPDF_Pattern> GetPattern(RetainPtr<CPDF_Object> pPatternObj,
                                      const CFX_Matrix& matrix);
+  RetainPtr<CPDF_ShadingPattern> GetShading(RetainPtr<CPDF_Object> pPatternObj,
+                                            const CFX_Matrix& matrix);
 
   RetainPtr<CPDF_Image> GetImage(uint32_t dwStreamObjNum);
-  void MaybePurgeImage(uint32_t dwStreamObjNum);
 
-  RetainPtr<CPDF_IccProfile> GetIccProfile(const CPDF_Stream* pProfileStream);
+  RetainPtr<CPDF_IccProfile> GetIccProfile(
+      RetainPtr<const CPDF_Stream> pProfileStream);
 
  private:
   // Loads a colorspace in a context that might be while loading another
@@ -95,24 +99,27 @@
       std::set<const CPDF_Object*>* pVisited,
       std::set<const CPDF_Object*>* pVisitedInternal);
 
-  size_t CalculateEncodingDict(int charset, CPDF_Dictionary* pBaseDict);
-  CPDF_Dictionary* ProcessbCJK(
-      CPDF_Dictionary* pBaseDict,
-      int charset,
+  size_t CalculateEncodingDict(FX_Charset charset, CPDF_Dictionary* pBaseDict);
+  RetainPtr<CPDF_Dictionary> ProcessbCJK(
+      RetainPtr<CPDF_Dictionary> pBaseDict,
+      FX_Charset charset,
       ByteString basefont,
       std::function<void(wchar_t, wchar_t, CPDF_Array*)> Insert);
-  void Clear(bool bForceRelease);
 
   bool m_bForceClear = false;
 
   // Specific destruction order may be required between maps.
   std::map<ByteString, RetainPtr<const CPDF_Stream>> m_HashProfileMap;
-  std::map<const CPDF_Object*, ObservedPtr<CPDF_ColorSpace>> m_ColorSpaceMap;
-  std::map<const CPDF_Stream*, RetainPtr<CPDF_StreamAcc>> m_FontFileMap;
-  std::map<const CPDF_Stream*, ObservedPtr<CPDF_IccProfile>> m_IccProfileMap;
-  std::map<const CPDF_Object*, ObservedPtr<CPDF_Pattern>> m_PatternMap;
+  std::map<RetainPtr<const CPDF_Array>, ObservedPtr<CPDF_ColorSpace>>
+      m_ColorSpaceMap;
+  std::map<RetainPtr<const CPDF_Stream>, RetainPtr<CPDF_StreamAcc>>
+      m_FontFileMap;
+  std::map<RetainPtr<const CPDF_Stream>, ObservedPtr<CPDF_IccProfile>>
+      m_IccProfileMap;
+  std::map<RetainPtr<const CPDF_Object>, ObservedPtr<CPDF_Pattern>>
+      m_PatternMap;
   std::map<uint32_t, RetainPtr<CPDF_Image>> m_ImageMap;
-  std::map<const CPDF_Dictionary*, ObservedPtr<CPDF_Font>> m_FontMap;
+  std::map<RetainPtr<const CPDF_Dictionary>, ObservedPtr<CPDF_Font>> m_FontMap;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_DOCPAGEDATA_H_
diff --git a/core/fpdfapi/page/cpdf_expintfunc.cpp b/core/fpdfapi/page/cpdf_expintfunc.cpp
index 7194f97..a8de8b9 100644
--- a/core/fpdfapi/page/cpdf_expintfunc.cpp
+++ b/core/fpdfapi/page/cpdf_expintfunc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,41 +6,44 @@
 
 #include "core/fpdfapi/page/cpdf_expintfunc.h"
 
+#include <math.h>
+
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_2d_size.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/stl_util.h"
 
 CPDF_ExpIntFunc::CPDF_ExpIntFunc()
-    : CPDF_Function(Type::kType2ExpotentialInterpolation) {}
+    : CPDF_Function(Type::kType2ExponentialInterpolation) {}
 
 CPDF_ExpIntFunc::~CPDF_ExpIntFunc() = default;
 
-bool CPDF_ExpIntFunc::v_Init(const CPDF_Object* pObj,
-                             std::set<const CPDF_Object*>* pVisited) {
-  const CPDF_Dictionary* pDict = pObj->GetDict();
+bool CPDF_ExpIntFunc::v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) {
+  RetainPtr<const CPDF_Dictionary> pDict = pObj->GetDict();
   if (!pDict)
     return false;
 
-  const CPDF_Number* pExponent = ToNumber(pDict->GetObjectFor("N"));
+  RetainPtr<const CPDF_Number> pExponent = pDict->GetNumberFor("N");
   if (!pExponent)
     return false;
 
   m_Exponent = pExponent->GetNumber();
 
-  const CPDF_Array* pArray0 = pDict->GetArrayFor("C0");
+  RetainPtr<const CPDF_Array> pArray0 = pDict->GetArrayFor("C0");
   if (pArray0 && m_nOutputs == 0)
-    m_nOutputs = pArray0->size();
+    m_nOutputs = fxcrt::CollectionSize<uint32_t>(*pArray0);
   if (m_nOutputs == 0)
     m_nOutputs = 1;
 
-  const CPDF_Array* pArray1 = pDict->GetArrayFor("C1");
-  m_BeginValues = pdfium::Vector2D<float>(m_nOutputs, 2);
-  m_EndValues = pdfium::Vector2D<float>(m_nOutputs, 2);
+  RetainPtr<const CPDF_Array> pArray1 = pDict->GetArrayFor("C1");
+  m_BeginValues = DataVector<float>(Fx2DSizeOrDie(m_nOutputs, 2));
+  m_EndValues = DataVector<float>(m_BeginValues.size());
   for (uint32_t i = 0; i < m_nOutputs; i++) {
-    m_BeginValues[i] = pArray0 ? pArray0->GetNumberAt(i) : 0.0f;
-    m_EndValues[i] = pArray1 ? pArray1->GetNumberAt(i) : 1.0f;
+    m_BeginValues[i] = pArray0 ? pArray0->GetFloatAt(i) : 0.0f;
+    m_EndValues[i] = pArray1 ? pArray1->GetFloatAt(i) : 1.0f;
   }
 
   FX_SAFE_UINT32 nOutputs = m_nOutputs;
@@ -53,12 +56,13 @@
   return true;
 }
 
-bool CPDF_ExpIntFunc::v_Call(const float* inputs, float* results) const {
+bool CPDF_ExpIntFunc::v_Call(pdfium::span<const float> inputs,
+                             pdfium::span<float> results) const {
   for (uint32_t i = 0; i < m_nInputs; i++) {
     for (uint32_t j = 0; j < m_nOrigOutputs; j++) {
       results[i * m_nOrigOutputs + j] =
-          m_BeginValues[j] + FXSYS_pow(inputs[i], m_Exponent) *
-                                 (m_EndValues[j] - m_BeginValues[j]);
+          m_BeginValues[j] +
+          powf(inputs[i], m_Exponent) * (m_EndValues[j] - m_BeginValues[j]);
     }
   }
   return true;
diff --git a/core/fpdfapi/page/cpdf_expintfunc.h b/core/fpdfapi/page/cpdf_expintfunc.h
index 95bdab6..22e7f12 100644
--- a/core/fpdfapi/page/cpdf_expintfunc.h
+++ b/core/fpdfapi/page/cpdf_expintfunc.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,25 +7,36 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_EXPINTFUNC_H_
 #define CORE_FPDFAPI_PAGE_CPDF_EXPINTFUNC_H_
 
-#include <set>
-#include <vector>
-
 #include "core/fpdfapi/page/cpdf_function.h"
+#include "core/fxcrt/data_vector.h"
+
+#if defined(_SKIA_SUPPORT_)
+#include "third_party/base/containers/span.h"
+#endif
 
 class CPDF_ExpIntFunc final : public CPDF_Function {
  public:
   CPDF_ExpIntFunc();
   ~CPDF_ExpIntFunc() override;
 
-  // CPDF_Function
-  bool v_Init(const CPDF_Object* pObj,
-              std::set<const CPDF_Object*>* pVisited) override;
-  bool v_Call(const float* inputs, float* results) const override;
+  // CPDF_Function:
+  bool v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) override;
+  bool v_Call(pdfium::span<const float> inputs,
+              pdfium::span<float> results) const override;
 
+  uint32_t GetOrigOutputs() const { return m_nOrigOutputs; }
+  float GetExponent() const { return m_Exponent; }
+
+#if defined(_SKIA_SUPPORT_)
+  pdfium::span<const float> GetBeginValues() const { return m_BeginValues; }
+  pdfium::span<const float> GetEndValues() const { return m_EndValues; }
+#endif
+
+ private:
   uint32_t m_nOrigOutputs = 0;
   float m_Exponent = 0.0f;
-  std::vector<float> m_BeginValues;
-  std::vector<float> m_EndValues;
+  DataVector<float> m_BeginValues;
+  DataVector<float> m_EndValues;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_EXPINTFUNC_H_
diff --git a/core/fpdfapi/page/cpdf_form.cpp b/core/fpdfapi/page/cpdf_form.cpp
index ab3296e..e66ff0b 100644
--- a/core/fpdfapi/page/cpdf_form.cpp
+++ b/core/fpdfapi/page/cpdf_form.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #include "core/fpdfapi/page/cpdf_form.h"
 
 #include <algorithm>
+#include <memory>
 
 #include "core/fpdfapi/page/cpdf_contentparser.h"
 #include "core/fpdfapi/page/cpdf_imageobject.h"
@@ -15,7 +16,11 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check_op.h"
+
+CPDF_Form::RecursionState::RecursionState() = default;
+
+CPDF_Form::RecursionState::~RecursionState() = default;
 
 // static
 CPDF_Dictionary* CPDF_Form::ChooseResourcesDict(
@@ -28,22 +33,27 @@
 }
 
 CPDF_Form::CPDF_Form(CPDF_Document* pDoc,
-                     CPDF_Dictionary* pPageResources,
-                     CPDF_Stream* pFormStream)
-    : CPDF_Form(pDoc, pPageResources, pFormStream, nullptr) {}
+                     RetainPtr<CPDF_Dictionary> pPageResources,
+                     RetainPtr<CPDF_Stream> pFormStream)
+    : CPDF_Form(pDoc,
+                std::move(pPageResources),
+                std::move(pFormStream),
+                nullptr) {}
 
 CPDF_Form::CPDF_Form(CPDF_Document* pDoc,
-                     CPDF_Dictionary* pPageResources,
-                     CPDF_Stream* pFormStream,
+                     RetainPtr<CPDF_Dictionary> pPageResources,
+                     RetainPtr<CPDF_Stream> pFormStream,
                      CPDF_Dictionary* pParentResources)
-    : CPDF_PageObjectHolder(
-          pDoc,
-          pFormStream->GetDict(),
-          pPageResources,
-          ChooseResourcesDict(pFormStream->GetDict()->GetDictFor("Resources"),
-                              pParentResources,
-                              pPageResources)),
-      m_pFormStream(pFormStream) {
+    : CPDF_PageObjectHolder(pDoc,
+                            pFormStream->GetMutableDict(),
+                            pPageResources,
+                            pdfium::WrapRetain(ChooseResourcesDict(
+                                pFormStream->GetMutableDict()
+                                    ->GetMutableDictFor("Resources")
+                                    .Get(),
+                                pParentResources,
+                                pPageResources.Get()))),
+      m_pFormStream(std::move(pFormStream)) {
   LoadTransparencyInfo();
 }
 
@@ -55,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) {
@@ -66,21 +76,16 @@
 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) {
-    if (!pParsedSet) {
-      if (!m_ParsedSet)
-        m_ParsedSet = pdfium::MakeUnique<std::set<const uint8_t*>>();
-      pParsedSet = m_ParsedSet.get();
-    }
-    StartParse(pdfium::MakeUnique<CPDF_ContentParser>(
-        this, pGraphicStates, pParentMatrix, pType3Char, pParsedSet));
+    StartParse(std::make_unique<CPDF_ContentParser>(
+        GetStream(), this, pGraphicStates, pParentMatrix, pType3Char,
+        recursion_state ? recursion_state : &m_RecursionState));
   }
-
-  ASSERT(GetParseState() == ParseState::kParsing);
+  DCHECK_EQ(GetParseState(), ParseState::kParsing);
   ContinueParse(nullptr);
 }
 
@@ -106,18 +111,18 @@
   return CFX_FloatRect(left, bottom, right, top);
 }
 
-const CPDF_Stream* CPDF_Form::GetStream() const {
-  return m_pFormStream.Get();
+RetainPtr<const CPDF_Stream> CPDF_Form::GetStream() const {
+  return m_pFormStream;
 }
 
-Optional<std::pair<RetainPtr<CFX_DIBitmap>, CFX_Matrix>>
+absl::optional<std::pair<RetainPtr<CFX_DIBitmap>, CFX_Matrix>>
 CPDF_Form::GetBitmapAndMatrixFromSoleImageOfForm() const {
   if (GetPageObjectCount() != 1)
-    return {};
+    return absl::nullopt;
 
   CPDF_ImageObject* pImageObject = (*begin())->AsImage();
   if (!pImageObject)
-    return {};
+    return absl::nullopt;
 
   return {{pImageObject->GetIndependentBitmap(), pImageObject->matrix()}};
 }
diff --git a/core/fpdfapi/page/cpdf_form.h b/core/fpdfapi/page/cpdf_form.h
index e8b9d1f..ba0fbd4 100644
--- a/core/fpdfapi/page/cpdf_form.h
+++ b/core/fpdfapi/page/cpdf_form.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,35 +7,42 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_FORM_H_
 #define CORE_FPDFAPI_PAGE_CPDF_FORM_H_
 
-#include <memory>
 #include <set>
 #include <utility>
 
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/page/cpdf_pageobjectholder.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CFX_Matrix;
 class CPDF_AllStates;
 class CPDF_Dictionary;
 class CPDF_Document;
-class CPDF_ImageObject;
 class CPDF_Stream;
 class CPDF_Type3Char;
 
 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,
                                               CPDF_Dictionary* pPageResources);
 
   CPDF_Form(CPDF_Document* pDocument,
-            CPDF_Dictionary* pPageResources,
-            CPDF_Stream* pFormStream);
+            RetainPtr<CPDF_Dictionary> pPageResources,
+            RetainPtr<CPDF_Stream> pFormStream);
   CPDF_Form(CPDF_Document* pDocument,
-            CPDF_Dictionary* pPageResources,
-            CPDF_Stream* pFormStream,
+            RetainPtr<CPDF_Dictionary> pPageResources,
+            RetainPtr<CPDF_Stream> pFormStream,
             CPDF_Dictionary* pParentResources);
   ~CPDF_Form() override;
 
@@ -43,23 +50,23 @@
   void ParseContentForType3Char(CPDF_Type3Char* pType3Char) override;
   bool HasPageObjects() const override;
   CFX_FloatRect CalcBoundingBox() const override;
-  Optional<std::pair<RetainPtr<CFX_DIBitmap>, CFX_Matrix>>
+  absl::optional<std::pair<RetainPtr<CFX_DIBitmap>, CFX_Matrix>>
   GetBitmapAndMatrixFromSoleImageOfForm() const override;
 
   void ParseContent();
   void ParseContent(const CPDF_AllStates* pGraphicStates,
                     const CFX_Matrix* pParentMatrix,
-                    std::set<const uint8_t*>* pParsedSet);
+                    RecursionState* recursion_state);
 
-  const CPDF_Stream* GetStream() const;
+  RetainPtr<const CPDF_Stream> GetStream() const;
 
  private:
   void ParseContentInternal(const CPDF_AllStates* pGraphicStates,
                             const CFX_Matrix* pParentMatrix,
                             CPDF_Type3Char* pType3Char,
-                            std::set<const uint8_t*>* pParsedSet);
+                            RecursionState* recursion_state);
 
-  std::unique_ptr<std::set<const uint8_t*>> m_ParsedSet;
+  RecursionState m_RecursionState;
   RetainPtr<CPDF_Stream> const m_pFormStream;
 };
 
diff --git a/core/fpdfapi/page/cpdf_formobject.cpp b/core/fpdfapi/page/cpdf_formobject.cpp
index a7c2643..8b270ca 100644
--- a/core/fpdfapi/page/cpdf_formobject.cpp
+++ b/core/fpdfapi/page/cpdf_formobject.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,11 +17,12 @@
       m_pForm(std::move(pForm)),
       m_FormMatrix(matrix) {}
 
-CPDF_FormObject::~CPDF_FormObject() {}
+CPDF_FormObject::~CPDF_FormObject() = default;
 
 void CPDF_FormObject::Transform(const CFX_Matrix& matrix) {
   m_FormMatrix.Concat(matrix);
   CalcBoundingBox();
+  SetDirty(true);
 }
 
 bool CPDF_FormObject::IsForm() const {
@@ -37,9 +38,14 @@
 }
 
 CPDF_PageObject::Type CPDF_FormObject::GetType() const {
-  return FORM;
+  return Type::kForm;
 }
 
 void CPDF_FormObject::CalcBoundingBox() {
   SetRect(m_FormMatrix.TransformRect(m_pForm->CalcBoundingBox()));
 }
+
+void CPDF_FormObject::SetFormMatrix(const CFX_Matrix& matrix) {
+  m_FormMatrix = matrix;
+  CalcBoundingBox();
+}
diff --git a/core/fpdfapi/page/cpdf_formobject.h b/core/fpdfapi/page/cpdf_formobject.h
index c24bfbf..a18a774 100644
--- a/core/fpdfapi/page/cpdf_formobject.h
+++ b/core/fpdfapi/page/cpdf_formobject.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -31,6 +31,7 @@
   void CalcBoundingBox();
   const CPDF_Form* form() const { return m_pForm.get(); }
   const CFX_Matrix& form_matrix() const { return m_FormMatrix; }
+  void SetFormMatrix(const CFX_Matrix& matrix);
 
  private:
   std::unique_ptr<CPDF_Form> const m_pForm;
diff --git a/core/fpdfapi/page/cpdf_function.cpp b/core/fpdfapi/page/cpdf_function.cpp
index 688c48e..5e885e1 100644
--- a/core/fpdfapi/page/cpdf_function.cpp
+++ b/core/fpdfapi/page/cpdf_function.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include "core/fpdfapi/page/cpdf_function.h"
 
+#include <algorithm>
+#include <utility>
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_expintfunc.h"
@@ -17,26 +19,44 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/scoped_set_insertion.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/containers/contains.h"
+
+namespace {
+
+CPDF_Function::Type IntegerToFunctionType(int iType) {
+  switch (iType) {
+    case 0:
+    case 2:
+    case 3:
+    case 4:
+      return static_cast<CPDF_Function::Type>(iType);
+    default:
+      return CPDF_Function::Type::kTypeInvalid;
+  }
+}
+
+}  // namespace
 
 // static
 std::unique_ptr<CPDF_Function> CPDF_Function::Load(
-    const CPDF_Object* pFuncObj) {
-  std::set<const CPDF_Object*> visited;
-  return Load(pFuncObj, &visited);
+    RetainPtr<const CPDF_Object> pFuncObj) {
+  VisitedSet visited;
+  return Load(std::move(pFuncObj), &visited);
 }
 
 // static
 std::unique_ptr<CPDF_Function> CPDF_Function::Load(
-    const CPDF_Object* pFuncObj,
-    std::set<const CPDF_Object*>* pVisited) {
+    RetainPtr<const CPDF_Object> pFuncObj,
+    VisitedSet* pVisited) {
   if (!pFuncObj)
     return nullptr;
 
-  if (pdfium::ContainsKey(*pVisited, pFuncObj))
+  if (pdfium::Contains(*pVisited, pFuncObj))
     return nullptr;
-  pdfium::ScopedSetInsertion<const CPDF_Object*> insertion(pVisited, pFuncObj);
+
+  ScopedSetInsertion<VisitedSet::value_type> insertion(pVisited, pFuncObj);
 
   int iType = -1;
   if (const CPDF_Stream* pStream = pFuncObj->AsStream())
@@ -47,13 +67,13 @@
   std::unique_ptr<CPDF_Function> pFunc;
   Type type = IntegerToFunctionType(iType);
   if (type == Type::kType0Sampled)
-    pFunc = pdfium::MakeUnique<CPDF_SampledFunc>();
-  else if (type == Type::kType2ExpotentialInterpolation)
-    pFunc = pdfium::MakeUnique<CPDF_ExpIntFunc>();
+    pFunc = std::make_unique<CPDF_SampledFunc>();
+  else if (type == Type::kType2ExponentialInterpolation)
+    pFunc = std::make_unique<CPDF_ExpIntFunc>();
   else if (type == Type::kType3Stitching)
-    pFunc = pdfium::MakeUnique<CPDF_StitchFunc>();
+    pFunc = std::make_unique<CPDF_StitchFunc>();
   else if (type == Type::kType4PostScript)
-    pFunc = pdfium::MakeUnique<CPDF_PSFunc>();
+    pFunc = std::make_unique<CPDF_PSFunc>();
 
   if (!pFunc || !pFunc->Init(pFuncObj, pVisited))
     return nullptr;
@@ -61,42 +81,28 @@
   return pFunc;
 }
 
-// static
-CPDF_Function::Type CPDF_Function::IntegerToFunctionType(int iType) {
-  switch (iType) {
-    case 0:
-    case 2:
-    case 3:
-    case 4:
-      return static_cast<Type>(iType);
-    default:
-      return Type::kTypeInvalid;
-  }
-}
-
 CPDF_Function::CPDF_Function(Type type) : m_Type(type) {}
 
 CPDF_Function::~CPDF_Function() = default;
 
-bool CPDF_Function::Init(const CPDF_Object* pObj,
-                         std::set<const CPDF_Object*>* pVisited) {
+bool CPDF_Function::Init(const CPDF_Object* pObj, VisitedSet* pVisited) {
   const CPDF_Stream* pStream = pObj->AsStream();
-  const CPDF_Dictionary* pDict =
-      pStream ? pStream->GetDict() : pObj->AsDictionary();
+  RetainPtr<const CPDF_Dictionary> pDict =
+      pStream ? pStream->GetDict() : pdfium::WrapRetain(pObj->AsDictionary());
 
-  const CPDF_Array* pDomains = pDict->GetArrayFor("Domain");
+  RetainPtr<const CPDF_Array> pDomains = pDict->GetArrayFor("Domain");
   if (!pDomains)
     return false;
 
-  m_nInputs = pDomains->size() / 2;
+  m_nInputs = fxcrt::CollectionSize<uint32_t>(*pDomains) / 2;
   if (m_nInputs == 0)
     return false;
 
   size_t nInputs = m_nInputs * 2;
-  m_Domains = ReadArrayElementsToVector(pDomains, nInputs);
+  m_Domains = ReadArrayElementsToVector(pDomains.Get(), nInputs);
 
-  const CPDF_Array* pRanges = pDict->GetArrayFor("Range");
-  m_nOutputs = pRanges ? pRanges->size() / 2 : 0;
+  RetainPtr<const CPDF_Array> pRanges = pDict->GetArrayFor("Range");
+  m_nOutputs = pRanges ? fxcrt::CollectionSize<uint32_t>(*pRanges) / 2 : 0;
 
   // Ranges are required for type 0 and type 4 functions. A non-zero
   // |m_nOutputs| here implied Ranges meets the requirements.
@@ -107,7 +113,7 @@
 
   if (m_nOutputs > 0) {
     size_t nOutputs = m_nOutputs * 2;
-    m_Ranges = ReadArrayElementsToVector(pRanges, nOutputs);
+    m_Ranges = ReadArrayElementsToVector(pRanges.Get(), nOutputs);
   }
 
   uint32_t old_outputs = m_nOutputs;
@@ -122,30 +128,36 @@
   return true;
 }
 
-bool CPDF_Function::Call(const float* inputs,
-                         uint32_t ninputs,
-                         float* results,
-                         int* nresults) const {
-  if (m_nInputs != ninputs)
-    return false;
+absl::optional<uint32_t> CPDF_Function::Call(
+    pdfium::span<const float> inputs,
+    pdfium::span<float> results) const {
+  if (m_nInputs != inputs.size())
+    return absl::nullopt;
 
-  *nresults = m_nOutputs;
   std::vector<float> clamped_inputs(m_nInputs);
   for (uint32_t i = 0; i < m_nInputs; i++) {
-    clamped_inputs[i] =
-        pdfium::clamp(inputs[i], m_Domains[i * 2], m_Domains[i * 2 + 1]);
+    float domain1 = m_Domains[i * 2];
+    float domain2 = m_Domains[i * 2 + 1];
+    if (domain1 > domain2)
+      return absl::nullopt;
+
+    clamped_inputs[i] = std::clamp(inputs[i], domain1, domain2);
   }
-  if (!v_Call(clamped_inputs.data(), results))
-    return false;
+  if (!v_Call(clamped_inputs, results))
+    return absl::nullopt;
 
   if (m_Ranges.empty())
-    return true;
+    return m_nOutputs;
 
   for (uint32_t i = 0; i < m_nOutputs; i++) {
-    results[i] =
-        pdfium::clamp(results[i], m_Ranges[i * 2], m_Ranges[i * 2 + 1]);
+    float range1 = m_Ranges[i * 2];
+    float range2 = m_Ranges[i * 2 + 1];
+    if (range1 > range2)
+      return absl::nullopt;
+
+    results[i] = std::clamp(results[i], range1, range2);
   }
-  return true;
+  return m_nOutputs;
 }
 
 // See PDF Reference 1.7, page 170.
@@ -158,6 +170,7 @@
   return ymin + (divisor ? (x - xmin) * (ymax - ymin) / divisor : 0);
 }
 
+#if defined(_SKIA_SUPPORT_)
 const CPDF_SampledFunc* CPDF_Function::ToSampledFunc() const {
   return m_Type == Type::kType0Sampled
              ? static_cast<const CPDF_SampledFunc*>(this)
@@ -165,7 +178,7 @@
 }
 
 const CPDF_ExpIntFunc* CPDF_Function::ToExpIntFunc() const {
-  return m_Type == Type::kType2ExpotentialInterpolation
+  return m_Type == Type::kType2ExponentialInterpolation
              ? static_cast<const CPDF_ExpIntFunc*>(this)
              : nullptr;
 }
@@ -175,3 +188,4 @@
              ? static_cast<const CPDF_StitchFunc*>(this)
              : nullptr;
 }
+#endif  // defined(_SKIA_SUPPORT_)
diff --git a/core/fpdfapi/page/cpdf_function.h b/core/fpdfapi/page/cpdf_function.h
index 5f4e125..d943e60 100644
--- a/core/fpdfapi/page/cpdf_function.h
+++ b/core/fpdfapi/page/cpdf_function.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,10 @@
 #include <set>
 #include <vector>
 
+#include "core/fxcrt/retain_ptr.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/containers/span.h"
+
 class CPDF_ExpIntFunc;
 class CPDF_Object;
 class CPDF_SampledFunc;
@@ -18,23 +22,22 @@
 
 class CPDF_Function {
  public:
+  // Valid values are from ISO 32000-1:2008 spec, table 38. DO NOT CHANGE.
   enum class Type {
     kTypeInvalid = -1,
     kType0Sampled = 0,
-    kType2ExpotentialInterpolation = 2,
+    kType2ExponentialInterpolation = 2,
     kType3Stitching = 3,
     kType4PostScript = 4,
   };
 
-  static std::unique_ptr<CPDF_Function> Load(const CPDF_Object* pFuncObj);
-  static Type IntegerToFunctionType(int iType);
+  static std::unique_ptr<CPDF_Function> Load(
+      RetainPtr<const CPDF_Object> pFuncObj);
 
   virtual ~CPDF_Function();
 
-  bool Call(const float* inputs,
-            uint32_t ninputs,
-            float* results,
-            int* nresults) const;
+  absl::optional<uint32_t> Call(pdfium::span<const float> inputs,
+                                pdfium::span<float> results) const;
   uint32_t CountInputs() const { return m_nInputs; }
   uint32_t CountOutputs() const { return m_nOutputs; }
   float GetDomain(int i) const { return m_Domains[i]; }
@@ -45,24 +48,27 @@
                     float ymin,
                     float ymax) const;
 
+#if defined(_SKIA_SUPPORT_)
   const CPDF_SampledFunc* ToSampledFunc() const;
   const CPDF_ExpIntFunc* ToExpIntFunc() const;
   const CPDF_StitchFunc* ToStitchFunc() const;
+#endif  // defined(_SKIA_SUPPORT_)
 
  protected:
   explicit CPDF_Function(Type type);
 
+  using VisitedSet = std::set<RetainPtr<const CPDF_Object>>;
   static std::unique_ptr<CPDF_Function> Load(
-      const CPDF_Object* pFuncObj,
-      std::set<const CPDF_Object*>* pVisited);
-  bool Init(const CPDF_Object* pObj, std::set<const CPDF_Object*>* pVisited);
-  virtual bool v_Init(const CPDF_Object* pObj,
-                      std::set<const CPDF_Object*>* pVisited) = 0;
-  virtual bool v_Call(const float* inputs, float* results) const = 0;
+      RetainPtr<const CPDF_Object> pFuncObj,
+      VisitedSet* pVisited);
+  bool Init(const CPDF_Object* pObj, VisitedSet* pVisited);
+  virtual bool v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) = 0;
+  virtual bool v_Call(pdfium::span<const float> inputs,
+                      pdfium::span<float> results) const = 0;
 
   const Type m_Type;
-  uint32_t m_nInputs;
-  uint32_t m_nOutputs;
+  uint32_t m_nInputs = 0;
+  uint32_t m_nOutputs = 0;
   std::vector<float> m_Domains;
   std::vector<float> m_Ranges;
 };
diff --git a/core/fpdfapi/page/cpdf_function_unittest.cpp b/core/fpdfapi/page/cpdf_function_unittest.cpp
new file mode 100644
index 0000000..af0ca03
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_function_unittest.cpp
@@ -0,0 +1,43 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/page/cpdf_function.h"
+
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(CPDFFunction, BadFunctionType) {
+  auto pDict = pdfium::MakeRetain<CPDF_Dictionary>();
+  pDict->SetNewFor<CPDF_Number>("FunctionType", -2);
+  EXPECT_FALSE(CPDF_Function::Load(pDict));
+
+  pDict->SetNewFor<CPDF_Number>("FunctionType", 5);
+  EXPECT_FALSE(CPDF_Function::Load(pDict));
+}
+
+TEST(CPDFFunction, NoDomain) {
+  auto pDict = pdfium::MakeRetain<CPDF_Dictionary>();
+  pDict->SetNewFor<CPDF_Number>("FunctionType", 0);
+  EXPECT_FALSE(CPDF_Function::Load(pDict));
+}
+
+TEST(CPDFFunction, EmptyDomain) {
+  auto pDict = pdfium::MakeRetain<CPDF_Dictionary>();
+  pDict->SetNewFor<CPDF_Number>("FunctionType", 0);
+  pDict->SetNewFor<CPDF_Array>("Domain");
+  EXPECT_FALSE(CPDF_Function::Load(pDict));
+}
+
+TEST(CPDFFunction, NoRange) {
+  auto pDict = pdfium::MakeRetain<CPDF_Dictionary>();
+  pDict->SetNewFor<CPDF_Number>("FunctionType", 0);
+
+  auto pArray = pDict->SetNewFor<CPDF_Array>("Domain");
+  pArray->AppendNew<CPDF_Number>(0);
+  pArray->AppendNew<CPDF_Number>(10);
+  EXPECT_FALSE(CPDF_Function::Load(pDict));
+}
diff --git a/core/fpdfapi/page/cpdf_generalstate.cpp b/core/fpdfapi/page/cpdf_generalstate.cpp
index 6463b57..8ebefa3 100644
--- a/core/fpdfapi/page/cpdf_generalstate.cpp
+++ b/core/fpdfapi/page/cpdf_generalstate.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,11 @@
 
 #include "core/fpdfapi/page/cpdf_generalstate.h"
 
+#include <utility>
+
 #include "core/fpdfapi/page/cpdf_transferfunc.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_object.h"
 
 namespace {
 
@@ -66,12 +69,12 @@
 
 }  // namespace
 
-CPDF_GeneralState::CPDF_GeneralState() {}
+CPDF_GeneralState::CPDF_GeneralState() = default;
 
 CPDF_GeneralState::CPDF_GeneralState(const CPDF_GeneralState& that)
     : m_Ref(that.m_Ref) {}
 
-CPDF_GeneralState::~CPDF_GeneralState() {}
+CPDF_GeneralState::~CPDF_GeneralState() = default;
 
 void CPDF_GeneralState::SetRenderIntent(const ByteString& ri) {
   m_Ref.GetPrivateCopy()->m_RenderIntent = RI_StringToId(ri);
@@ -142,22 +145,27 @@
   m_Ref.GetPrivateCopy()->m_StrokeAlpha = alpha;
 }
 
-CPDF_Object* CPDF_GeneralState::GetSoftMask() const {
+RetainPtr<const CPDF_Dictionary> CPDF_GeneralState::GetSoftMask() const {
   const StateData* pData = m_Ref.GetObject();
-  return pData ? pData->m_pSoftMask.Get() : nullptr;
+  return pData ? pData->m_pSoftMask : nullptr;
 }
 
-void CPDF_GeneralState::SetSoftMask(CPDF_Object* pObject) {
-  m_Ref.GetPrivateCopy()->m_pSoftMask.Reset(pObject);
-}
-
-const CPDF_Object* CPDF_GeneralState::GetTR() const {
+RetainPtr<CPDF_Dictionary> CPDF_GeneralState::GetMutableSoftMask() {
   const StateData* pData = m_Ref.GetObject();
-  return pData ? pData->m_pTR.Get() : nullptr;
+  return pData ? pData->m_pSoftMask : nullptr;
 }
 
-void CPDF_GeneralState::SetTR(CPDF_Object* pObject) {
-  m_Ref.GetPrivateCopy()->m_pTR.Reset(pObject);
+void CPDF_GeneralState::SetSoftMask(RetainPtr<CPDF_Dictionary> pDict) {
+  m_Ref.GetPrivateCopy()->m_pSoftMask = std::move(pDict);
+}
+
+RetainPtr<const CPDF_Object> CPDF_GeneralState::GetTR() const {
+  const StateData* pData = m_Ref.GetObject();
+  return pData ? pData->m_pTR : nullptr;
+}
+
+void CPDF_GeneralState::SetTR(RetainPtr<const CPDF_Object> pObject) {
+  m_Ref.GetPrivateCopy()->m_pTR = std::move(pObject);
 }
 
 RetainPtr<CPDF_TransferFunc> CPDF_GeneralState::GetTransferFunc() const {
@@ -165,9 +173,8 @@
   return pData ? pData->m_pTransferFunc : nullptr;
 }
 
-void CPDF_GeneralState::SetTransferFunc(
-    const RetainPtr<CPDF_TransferFunc>& pFunc) {
-  m_Ref.GetPrivateCopy()->m_pTransferFunc = pFunc;
+void CPDF_GeneralState::SetTransferFunc(RetainPtr<CPDF_TransferFunc> pFunc) {
+  m_Ref.GetPrivateCopy()->m_pTransferFunc = std::move(pFunc);
 }
 
 void CPDF_GeneralState::SetBlendMode(const ByteString& mode) {
@@ -211,16 +218,16 @@
   m_Ref.GetPrivateCopy()->m_OPMode = mode;
 }
 
-void CPDF_GeneralState::SetBG(CPDF_Object* pObject) {
-  m_Ref.GetPrivateCopy()->m_pBG.Reset(pObject);
+void CPDF_GeneralState::SetBG(RetainPtr<const CPDF_Object> pObject) {
+  m_Ref.GetPrivateCopy()->m_pBG = std::move(pObject);
 }
 
-void CPDF_GeneralState::SetUCR(CPDF_Object* pObject) {
-  m_Ref.GetPrivateCopy()->m_pUCR.Reset(pObject);
+void CPDF_GeneralState::SetUCR(RetainPtr<const CPDF_Object> pObject) {
+  m_Ref.GetPrivateCopy()->m_pUCR = std::move(pObject);
 }
 
-void CPDF_GeneralState::SetHT(CPDF_Object* pObject) {
-  m_Ref.GetPrivateCopy()->m_pHT.Reset(pObject);
+void CPDF_GeneralState::SetHT(RetainPtr<const CPDF_Object> pObject) {
+  m_Ref.GetPrivateCopy()->m_pHT = std::move(pObject);
 }
 
 void CPDF_GeneralState::SetFlatness(float flatness) {
diff --git a/core/fpdfapi/page/cpdf_generalstate.h b/core/fpdfapi/page/cpdf_generalstate.h
index f374380..2fb2285 100644
--- a/core/fpdfapi/page/cpdf_generalstate.h
+++ b/core/fpdfapi/page/cpdf_generalstate.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,12 +8,13 @@
 #define CORE_FPDFAPI_PAGE_CPDF_GENERALSTATE_H_
 
 #include "constants/transparency.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/shared_copy_on_write.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
 
+class CPDF_Dictionary;
 class CPDF_Object;
 class CPDF_TransferFunc;
 
@@ -38,14 +39,15 @@
   float GetStrokeAlpha() const;
   void SetStrokeAlpha(float alpha);
 
-  CPDF_Object* GetSoftMask() const;
-  void SetSoftMask(CPDF_Object* pObject);
+  RetainPtr<const CPDF_Dictionary> GetSoftMask() const;
+  RetainPtr<CPDF_Dictionary> GetMutableSoftMask();
+  void SetSoftMask(RetainPtr<CPDF_Dictionary> pDict);
 
-  const CPDF_Object* GetTR() const;
-  void SetTR(CPDF_Object* pObject);
+  RetainPtr<const CPDF_Object> GetTR() const;
+  void SetTR(RetainPtr<const CPDF_Object> pObject);
 
   RetainPtr<CPDF_TransferFunc> GetTransferFunc() const;
-  void SetTransferFunc(const RetainPtr<CPDF_TransferFunc>& pFunc);
+  void SetTransferFunc(RetainPtr<CPDF_TransferFunc> pFunc);
 
   void SetBlendMode(const ByteString& mode);
 
@@ -61,9 +63,9 @@
   int GetOPMode() const;
   void SetOPMode(int mode);
 
-  void SetBG(CPDF_Object* pObject);
-  void SetUCR(CPDF_Object* pObject);
-  void SetHT(CPDF_Object* pObject);
+  void SetBG(RetainPtr<const CPDF_Object> pObject);
+  void SetUCR(RetainPtr<const CPDF_Object> pObject);
+  void SetHT(RetainPtr<const CPDF_Object> pObject);
 
   void SetFlatness(float flatness);
   void SetSmoothness(float smoothness);
@@ -80,14 +82,13 @@
  private:
   class StateData final : public Retainable {
    public:
-    template <typename T, typename... Args>
-    friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+    CONSTRUCT_VIA_MAKE_RETAIN;
 
     RetainPtr<StateData> Clone() const;
 
     ByteString m_BlendMode = pdfium::transparency::kNormal;
     BlendMode m_BlendType = BlendMode::kNormal;
-    RetainPtr<CPDF_Object> m_pSoftMask;
+    RetainPtr<CPDF_Dictionary> m_pSoftMask;
     CFX_Matrix m_SMaskMatrix;
     float m_StrokeAlpha = 1.0f;
     float m_FillAlpha = 1.0f;
diff --git a/core/fpdfapi/page/cpdf_graphicstates.cpp b/core/fpdfapi/page/cpdf_graphicstates.cpp
index 962bc0a..8f19a2d 100644
--- a/core/fpdfapi/page/cpdf_graphicstates.cpp
+++ b/core/fpdfapi/page/cpdf_graphicstates.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,9 @@
 
 #include "core/fpdfapi/page/cpdf_graphicstates.h"
 
-CPDF_GraphicStates::CPDF_GraphicStates() {}
+CPDF_GraphicStates::CPDF_GraphicStates() = default;
 
-CPDF_GraphicStates::~CPDF_GraphicStates() {}
+CPDF_GraphicStates::~CPDF_GraphicStates() = default;
 
 void CPDF_GraphicStates::DefaultStates() {
   m_ColorState.Emplace();
diff --git a/core/fpdfapi/page/cpdf_graphicstates.h b/core/fpdfapi/page/cpdf_graphicstates.h
index b7e7fa2..9f604c7 100644
--- a/core/fpdfapi/page/cpdf_graphicstates.h
+++ b/core/fpdfapi/page/cpdf_graphicstates.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fpdfapi/page/cpdf_iccprofile.cpp b/core/fpdfapi/page/cpdf_iccprofile.cpp
index f8d40e4..82bbe9b 100644
--- a/core/fpdfapi/page/cpdf_iccprofile.cpp
+++ b/core/fpdfapi/page/cpdf_iccprofile.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,10 @@
 
 #include "core/fpdfapi/page/cpdf_iccprofile.h"
 
+#include <utility>
+
 #include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fxcodec/icc/iccmodule.h"
+#include "core/fxcodec/icc/icc_transform.h"
 
 namespace {
 
@@ -18,17 +20,32 @@
 
 }  // namespace
 
-CPDF_IccProfile::CPDF_IccProfile(const CPDF_Stream* pStream,
+CPDF_IccProfile::CPDF_IccProfile(RetainPtr<const CPDF_Stream> pStream,
                                  pdfium::span<const uint8_t> span)
-    : m_bsRGB(DetectSRGB(span)), m_pStream(pStream) {
+    : m_bsRGB(DetectSRGB(span)), m_pStream(std::move(pStream)) {
   if (m_bsRGB) {
     m_nSrcComponents = 3;
     return;
   }
 
-  m_Transform = IccModule::CreateTransformSRGB(span);
+  m_Transform = fxcodec::IccTransform::CreateTransformSRGB(span);
   if (m_Transform)
     m_nSrcComponents = m_Transform->components();
 }
 
 CPDF_IccProfile::~CPDF_IccProfile() = default;
+
+bool CPDF_IccProfile::IsNormal() const {
+  return m_Transform->IsNormal();
+}
+
+void CPDF_IccProfile::Translate(pdfium::span<const float> pSrcValues,
+                                pdfium::span<float> pDestValues) {
+  m_Transform->Translate(pSrcValues, pDestValues);
+}
+
+void CPDF_IccProfile::TranslateScanline(pdfium::span<uint8_t> pDest,
+                                        pdfium::span<const uint8_t> pSrc,
+                                        int pixels) {
+  m_Transform->TranslateScanline(pDest, pSrc, pixels);
+}
diff --git a/core/fpdfapi/page/cpdf_iccprofile.h b/core/fpdfapi/page/cpdf_iccprofile.h
index 070f884..3dd9f14 100644
--- a/core/fpdfapi/page/cpdf_iccprofile.h
+++ b/core/fpdfapi/page/cpdf_iccprofile.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,38 +7,46 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_ICCPROFILE_H_
 #define CORE_FPDFAPI_PAGE_CPDF_ICCPROFILE_H_
 
+#include <stdint.h>
+
 #include <memory>
 
 #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;
 
 namespace fxcodec {
-class CLcmsCmm;
+class IccTransform;
 }  // namespace fxcodec
 
 class CPDF_IccProfile final : public Retainable, public Observable {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
-  const CPDF_Stream* GetStream() const { return m_pStream.Get(); }
   bool IsValid() const { return IsSRGB() || IsSupported(); }
   bool IsSRGB() const { return m_bsRGB; }
   bool IsSupported() const { return !!m_Transform; }
-  fxcodec::CLcmsCmm* transform() { return m_Transform.get(); }
   uint32_t GetComponents() const { return m_nSrcComponents; }
 
+  bool IsNormal() const;
+  void Translate(pdfium::span<const float> pSrcValues,
+                 pdfium::span<float> pDestValues);
+  void TranslateScanline(pdfium::span<uint8_t> pDest,
+                         pdfium::span<const uint8_t> pSrc,
+                         int pixels);
+
  private:
-  CPDF_IccProfile(const CPDF_Stream* pStream, pdfium::span<const uint8_t> span);
+  // Keeps stream alive for the duration of the CPDF_IccProfile.
+  CPDF_IccProfile(RetainPtr<const CPDF_Stream> pStream,
+                  pdfium::span<const uint8_t> span);
   ~CPDF_IccProfile() override;
 
   const bool m_bsRGB;
   uint32_t m_nSrcComponents = 0;
-  RetainPtr<const CPDF_Stream> const m_pStream;
-  std::unique_ptr<fxcodec::CLcmsCmm> m_Transform;
+  RetainPtr<const CPDF_Stream> const m_pStream;  // Used by `m_Transform`.
+  std::unique_ptr<fxcodec::IccTransform> m_Transform;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_ICCPROFILE_H_
diff --git a/core/fpdfapi/page/cpdf_image.cpp b/core/fpdfapi/page/cpdf_image.cpp
index ce46538..6b70819 100644
--- a/core/fpdfapi/page/cpdf_image.cpp
+++ b/core/fpdfapi/page/cpdf_image.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,14 +6,16 @@
 
 #include "core/fpdfapi/page/cpdf_image.h"
 
+#include <stdint.h>
+
 #include <algorithm>
 #include <memory>
 #include <utility>
-#include <vector>
 
 #include "constants/stream_dict_common.h"
 #include "core/fpdfapi/page/cpdf_dib.h"
 #include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_boolean.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
@@ -23,14 +25,16 @@
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fxcodec/fx_codec.h"
 #include "core/fxcodec/jpeg/jpegmodule.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_2d_size.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/span_util.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/check.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/ptr_util.h"
 
 // static
 bool CPDF_Image::IsValidJpegComponent(int32_t comps) {
@@ -43,28 +47,29 @@
 }
 
 CPDF_Image::CPDF_Image(CPDF_Document* pDoc) : m_pDocument(pDoc) {
-  ASSERT(m_pDocument);
+  DCHECK(m_pDocument);
 }
 
 CPDF_Image::CPDF_Image(CPDF_Document* pDoc, RetainPtr<CPDF_Stream> pStream)
     : m_bIsInline(true), m_pDocument(pDoc), m_pStream(std::move(pStream)) {
-  ASSERT(m_pDocument);
-  FinishInitialization(m_pStream->GetDict());
+  DCHECK(m_pDocument);
+  FinishInitialization();
 }
 
 CPDF_Image::CPDF_Image(CPDF_Document* pDoc, uint32_t dwStreamObjNum)
     : m_pDocument(pDoc),
-      m_pStream(ToStream(pDoc->GetIndirectObject(dwStreamObjNum))) {
-  ASSERT(m_pDocument);
-  FinishInitialization(m_pStream->GetDict());
+      m_pStream(ToStream(pDoc->GetMutableIndirectObject(dwStreamObjNum))) {
+  DCHECK(m_pDocument);
+  FinishInitialization();
 }
 
 CPDF_Image::~CPDF_Image() = default;
 
-void CPDF_Image::FinishInitialization(CPDF_Dictionary* pStreamDict) {
-  m_pOC.Reset(pStreamDict->GetDictFor("OC"));
+void CPDF_Image::FinishInitialization() {
+  RetainPtr<const CPDF_Dictionary> pStreamDict = m_pStream->GetDict();
+  m_pOC = pStreamDict->GetDictFor("OC");
   m_bIsMask = !pStreamDict->KeyExist("ColorSpace") ||
-              pStreamDict->GetIntegerFor("ImageMask");
+              pStreamDict->GetBooleanFor("ImageMask", /*bDefault=*/false);
   m_bInterpolate = !!pStreamDict->GetIntegerFor("Interpolate");
   m_Height = pStreamDict->GetIntegerFor("Height");
   m_Width = pStreamDict->GetIntegerFor("Width");
@@ -77,18 +82,26 @@
   m_pDocument->AddIndirectObject(m_pStream);
 }
 
-CPDF_Dictionary* CPDF_Image::GetDict() const {
+RetainPtr<const CPDF_Dictionary> CPDF_Image::GetDict() const {
   return m_pStream ? m_pStream->GetDict() : nullptr;
 }
 
+RetainPtr<const CPDF_Stream> CPDF_Image::GetStream() const {
+  return m_pStream;
+}
+
+RetainPtr<const CPDF_Dictionary> CPDF_Image::GetOC() const {
+  return m_pOC;
+}
+
 RetainPtr<CPDF_Dictionary> CPDF_Image::InitJPEG(
     pdfium::span<uint8_t> src_span) {
-  Optional<JpegModule::JpegImageInfo> info_opt =
-      fxcodec::ModuleMgr::GetInstance()->GetJpegModule()->LoadInfo(src_span);
+  absl::optional<JpegModule::ImageInfo> info_opt =
+      JpegModule::LoadInfo(src_span);
   if (!info_opt.has_value())
     return nullptr;
 
-  const JpegModule::JpegImageInfo& info = info_opt.value();
+  const JpegModule::ImageInfo& info = info_opt.value();
   if (!IsValidJpegComponent(info.num_components) ||
       !IsValidJpegBitsPerComponent(info.bits_per_components)) {
     return nullptr;
@@ -103,17 +116,17 @@
     csname = "DeviceRGB";
   } else if (info.num_components == 4) {
     csname = "DeviceCMYK";
-    CPDF_Array* pDecode = pDict->SetNewFor<CPDF_Array>("Decode");
+    auto pDecode = pDict->SetNewFor<CPDF_Array>("Decode");
     for (int n = 0; n < 4; n++) {
-      pDecode->AddNew<CPDF_Number>(1);
-      pDecode->AddNew<CPDF_Number>(0);
+      pDecode->AppendNew<CPDF_Number>(1);
+      pDecode->AppendNew<CPDF_Number>(0);
     }
   }
   pDict->SetNewFor<CPDF_Name>("ColorSpace", csname);
   pDict->SetNewFor<CPDF_Number>("BitsPerComponent", info.bits_per_components);
   pDict->SetNewFor<CPDF_Name>("Filter", "DCTDecode");
   if (!info.color_transform) {
-    CPDF_Dictionary* pParms =
+    auto pParms =
         pDict->SetNewFor<CPDF_Dictionary>(pdfium::stream::kDecodeParms);
     pParms->SetNewFor<CPDF_Number>("ColorTransform", 0);
   }
@@ -125,43 +138,43 @@
   return pDict;
 }
 
-void CPDF_Image::SetJpegImage(const RetainPtr<IFX_SeekableReadStream>& pFile) {
+void CPDF_Image::SetJpegImage(RetainPtr<IFX_SeekableReadStream> pFile) {
   uint32_t size = pdfium::base::checked_cast<uint32_t>(pFile->GetSize());
   if (!size)
     return;
 
   uint32_t dwEstimateSize = std::min(size, 8192U);
-  std::vector<uint8_t> data(dwEstimateSize);
-  if (!pFile->ReadBlockAtOffset(data.data(), 0, dwEstimateSize))
+  DataVector<uint8_t> data(dwEstimateSize);
+  if (!pFile->ReadBlockAtOffset(data, 0))
     return;
 
   RetainPtr<CPDF_Dictionary> pDict = InitJPEG(data);
   if (!pDict && size > dwEstimateSize) {
     data.resize(size);
-    if (pFile->ReadBlockAtOffset(data.data(), 0, size))
+    if (pFile->ReadBlockAtOffset(data, 0))
       pDict = InitJPEG(data);
   }
   if (!pDict)
     return;
 
-  m_pStream->InitStreamFromFile(pFile, std::move(pDict));
+  m_pStream->InitStreamFromFile(std::move(pFile), std::move(pDict));
 }
 
-void CPDF_Image::SetJpegImageInline(
-    const RetainPtr<IFX_SeekableReadStream>& pFile) {
+void CPDF_Image::SetJpegImageInline(RetainPtr<IFX_SeekableReadStream> pFile) {
   uint32_t size = pdfium::base::checked_cast<uint32_t>(pFile->GetSize());
   if (!size)
     return;
 
-  std::vector<uint8_t> data(size);
-  if (!pFile->ReadBlockAtOffset(data.data(), 0, size))
+  DataVector<uint8_t> data(size);
+  if (!pFile->ReadBlockAtOffset(data, 0))
     return;
 
   RetainPtr<CPDF_Dictionary> pDict = InitJPEG(data);
   if (!pDict)
     return;
 
-  m_pStream->InitStream(data, std::move(pDict));
+  m_pStream =
+      pdfium::MakeRetain<CPDF_Stream>(std::move(data), std::move(pDict));
 }
 
 void CPDF_Image::SetImage(const RetainPtr<CFX_DIBitmap>& pBitmap) {
@@ -184,7 +197,7 @@
     int32_t set_r = 0;
     int32_t set_g = 0;
     int32_t set_b = 0;
-    if (!pBitmap->IsAlphaMask()) {
+    if (!pBitmap->IsMaskFormat()) {
       std::tie(reset_a, reset_r, reset_g, reset_b) =
           ArgbDecode(pBitmap->GetPaletteArgb(0));
       std::tie(set_a, set_r, set_g, set_b) =
@@ -193,15 +206,15 @@
     if (set_a == 0 || reset_a == 0) {
       pDict->SetNewFor<CPDF_Boolean>("ImageMask", true);
       if (reset_a == 0) {
-        CPDF_Array* pArray = pDict->SetNewFor<CPDF_Array>("Decode");
-        pArray->AddNew<CPDF_Number>(1);
-        pArray->AddNew<CPDF_Number>(0);
+        auto pArray = pDict->SetNewFor<CPDF_Array>("Decode");
+        pArray->AppendNew<CPDF_Number>(1);
+        pArray->AppendNew<CPDF_Number>(0);
       }
     } else {
-      CPDF_Array* pCS = pDict->SetNewFor<CPDF_Array>("ColorSpace");
-      pCS->AddNew<CPDF_Name>("Indexed");
-      pCS->AddNew<CPDF_Name>("DeviceRGB");
-      pCS->AddNew<CPDF_Number>(1);
+      auto pCS = pDict->SetNewFor<CPDF_Array>("ColorSpace");
+      pCS->AppendNew<CPDF_Name>("Indexed");
+      pCS->AppendNew<CPDF_Name>("DeviceRGB");
+      pCS->AppendNew<CPDF_Number>(1);
       ByteString ct;
       {
         // Span's lifetime must end before ReleaseBuffer() below.
@@ -214,33 +227,32 @@
         pBuf[5] = static_cast<char>(set_b);
       }
       ct.ReleaseBuffer(6);
-      pCS->AddNew<CPDF_String>(ct, true);
+      pCS->AppendNew<CPDF_String>(ct, true);
     }
     pDict->SetNewFor<CPDF_Number>("BitsPerComponent", 1);
     dest_pitch = (BitmapWidth + 7) / 8;
   } else if (bpp == 8) {
-    size_t palette_size = pBitmap->GetPaletteSize();
+    size_t palette_size = pBitmap->GetRequiredPaletteSize();
     if (palette_size > 0) {
-      ASSERT(palette_size <= 256);
-      CPDF_Array* pCS = m_pDocument->NewIndirect<CPDF_Array>();
-      pCS->AddNew<CPDF_Name>("Indexed");
-      pCS->AddNew<CPDF_Name>("DeviceRGB");
-      pCS->AddNew<CPDF_Number>(static_cast<int>(palette_size - 1));
-      std::unique_ptr<uint8_t, FxFreeDeleter> pColorTable(
-          FX_Alloc2D(uint8_t, palette_size, 3));
-      uint8_t* ptr = pColorTable.get();
+      DCHECK(palette_size <= 256);
+      auto pCS = m_pDocument->NewIndirect<CPDF_Array>();
+      pCS->AppendNew<CPDF_Name>("Indexed");
+      pCS->AppendNew<CPDF_Name>("DeviceRGB");
+      pCS->AppendNew<CPDF_Number>(static_cast<int>(palette_size - 1));
+      DataVector<uint8_t> color_table(Fx2DSizeOrDie(palette_size, 3));
+      auto color_table_span = pdfium::make_span(color_table);
       for (size_t i = 0; i < palette_size; i++) {
         uint32_t argb = pBitmap->GetPaletteArgb(i);
-        ptr[0] = FXARGB_R(argb);
-        ptr[1] = FXARGB_G(argb);
-        ptr[2] = FXARGB_B(argb);
-        ptr += 3;
+        color_table_span[0] = FXARGB_R(argb);
+        color_table_span[1] = FXARGB_G(argb);
+        color_table_span[2] = FXARGB_B(argb);
+        color_table_span = color_table_span.subspan(3);
       }
       auto pNewDict = m_pDocument->New<CPDF_Dictionary>();
-      CPDF_Stream* pCTS = m_pDocument->NewIndirect<CPDF_Stream>(
-          std::move(pColorTable), palette_size * 3, std::move(pNewDict));
-      pCS->AddNew<CPDF_Reference>(m_pDocument.Get(), pCTS->GetObjNum());
-      pDict->SetNewFor<CPDF_Reference>("ColorSpace", m_pDocument.Get(),
+      auto pCTS = m_pDocument->NewIndirect<CPDF_Stream>(std::move(color_table),
+                                                        std::move(pNewDict));
+      pCS->AppendNew<CPDF_Reference>(m_pDocument, pCTS->GetObjNum());
+      pDict->SetNewFor<CPDF_Reference>("ColorSpace", m_pDocument,
                                        pCS->GetObjNum());
     } else {
       pDict->SetNewFor<CPDF_Name>("ColorSpace", "DeviceGray");
@@ -255,84 +267,78 @@
   }
 
   RetainPtr<CFX_DIBitmap> pMaskBitmap;
-  if (pBitmap->HasAlpha())
+  if (pBitmap->IsAlphaFormat())
     pMaskBitmap = pBitmap->CloneAlphaMask();
 
   if (pMaskBitmap) {
-    int32_t maskWidth = pMaskBitmap->GetWidth();
-    int32_t maskHeight = pMaskBitmap->GetHeight();
-    std::unique_ptr<uint8_t, FxFreeDeleter> mask_buf;
-    int32_t mask_size = 0;
+    const int32_t mask_width = pMaskBitmap->GetWidth();
+    const int32_t mask_height = pMaskBitmap->GetHeight();
+    DataVector<uint8_t> mask_buf;
     RetainPtr<CPDF_Dictionary> pMaskDict =
-        CreateXObjectImageDict(maskWidth, maskHeight);
+        CreateXObjectImageDict(mask_width, mask_height);
     pMaskDict->SetNewFor<CPDF_Name>("ColorSpace", "DeviceGray");
     pMaskDict->SetNewFor<CPDF_Number>("BitsPerComponent", 8);
-    if (pMaskBitmap->GetFormat() != FXDIB_1bppMask) {
-      mask_buf.reset(FX_Alloc2D(uint8_t, maskHeight, maskWidth));
-      mask_size = maskHeight * maskWidth;  // Safe since checked alloc returned.
-      for (int32_t a = 0; a < maskHeight; a++) {
-        memcpy(mask_buf.get() + a * maskWidth, pMaskBitmap->GetScanline(a),
-               maskWidth);
+    if (pMaskBitmap->GetFormat() != FXDIB_Format::k1bppMask) {
+      mask_buf.resize(Fx2DSizeOrDie(mask_width, mask_height));
+      for (int32_t a = 0; a < mask_height; a++) {
+        fxcrt::spancpy(pdfium::make_span(mask_buf).subspan(a * mask_width),
+                       pMaskBitmap->GetScanline(a).first(mask_width));
       }
     }
-    pMaskDict->SetNewFor<CPDF_Number>("Length", mask_size);
-    CPDF_Stream* pNewStream = m_pDocument->NewIndirect<CPDF_Stream>(
-        std::move(mask_buf), mask_size, std::move(pMaskDict));
-    pDict->SetNewFor<CPDF_Reference>("SMask", m_pDocument.Get(),
+    pMaskDict->SetNewFor<CPDF_Number>(
+        "Length", pdfium::base::checked_cast<int>(mask_buf.size()));
+    auto pNewStream = m_pDocument->NewIndirect<CPDF_Stream>(
+        std::move(mask_buf), std::move(pMaskDict));
+    pDict->SetNewFor<CPDF_Reference>("SMask", m_pDocument,
                                      pNewStream->GetObjNum());
   }
 
-  uint8_t* src_buf = pBitmap->GetBuffer();
-  int32_t src_pitch = pBitmap->GetPitch();
-  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf(
-      FX_Alloc2D(uint8_t, dest_pitch, BitmapHeight));
-  // Safe as checked alloc returned.
-  size_t dest_size = dest_pitch * BitmapHeight;
-  auto dest_span = pdfium::make_span(dest_buf.get(), dest_size);
-  size_t dest_span_offset = 0;
+  DataVector<uint8_t> dest_buf(Fx2DSizeOrDie(dest_pitch, BitmapHeight));
+  pdfium::span<uint8_t> dest_span = pdfium::make_span(dest_buf);
+  pdfium::span<const uint8_t> src_span = pBitmap->GetBuffer();
+  const int32_t src_pitch = pBitmap->GetPitch();
   if (bCopyWithoutAlpha) {
     for (int32_t i = 0; i < BitmapHeight; i++) {
-      memcpy(&dest_span[dest_span_offset], src_buf, dest_pitch);
-      dest_span_offset += dest_pitch;
-      src_buf += src_pitch;
+      fxcrt::spancpy(dest_span, src_span.first(dest_pitch));
+      dest_span = dest_span.subspan(dest_pitch);
+      src_span = src_span.subspan(src_pitch);
     }
   } else {
-    int32_t src_offset = 0;
+    const size_t src_step = bpp == 24 ? 3 : 4;
     for (int32_t row = 0; row < BitmapHeight; row++) {
-      size_t dest_span_row_offset = dest_span_offset;
-      src_offset = row * src_pitch;
+      uint8_t* dest_ptr = dest_span.data();
+      const uint8_t* src_ptr = src_span.data();
       for (int32_t column = 0; column < BitmapWidth; column++) {
-        float alpha = 1;
-        dest_span[dest_span_row_offset] =
-            static_cast<uint8_t>(src_buf[src_offset + 2] * alpha);
-        dest_span[dest_span_row_offset + 1] =
-            static_cast<uint8_t>(src_buf[src_offset + 1] * alpha);
-        dest_span[dest_span_row_offset + 2] =
-            static_cast<uint8_t>(src_buf[src_offset] * alpha);
-        dest_span_row_offset += 3;
-        src_offset += bpp == 24 ? 3 : 4;
+        dest_ptr[0] = src_ptr[2];
+        dest_ptr[1] = src_ptr[1];
+        dest_ptr[2] = src_ptr[0];
+        dest_ptr += 3;
+        src_ptr += src_step;
       }
-
-      dest_span_offset += dest_pitch;
+      dest_span = dest_span.subspan(dest_pitch);
+      src_span = src_span.subspan(src_pitch);
     }
   }
-  if (!m_pStream)
-    m_pStream = pdfium::MakeRetain<CPDF_Stream>();
 
-  m_pStream->InitStream(dest_span, std::move(pDict));
-  m_bIsMask = pBitmap->IsAlphaMask();
+  m_pStream =
+      pdfium::MakeRetain<CPDF_Stream>(std::move(dest_buf), std::move(pDict));
+  m_bIsMask = pBitmap->IsMaskFormat();
   m_Width = BitmapWidth;
   m_Height = BitmapHeight;
 }
 
 void CPDF_Image::ResetCache(CPDF_Page* pPage) {
   RetainPtr<CPDF_Image> pHolder(this);
-  pPage->GetRenderCache()->ResetBitmapForImage(pHolder);
+  pPage->GetPageImageCache()->ResetBitmapForImage(std::move(pHolder));
+}
+
+RetainPtr<CPDF_DIB> CPDF_Image::CreateNewDIB() const {
+  return pdfium::MakeRetain<CPDF_DIB>(GetDocument(), GetStream());
 }
 
 RetainPtr<CFX_DIBBase> CPDF_Image::LoadDIBBase() const {
-  auto source = pdfium::MakeRetain<CPDF_DIB>();
-  if (!source->Load(m_pDocument.Get(), m_pStream.Get()))
+  RetainPtr<CPDF_DIB> source = CreateNewDIB();
+  if (!source->Load())
     return nullptr;
 
   if (!source->IsJBigImage())
@@ -353,14 +359,15 @@
 }
 
 bool CPDF_Image::StartLoadDIBBase(const CPDF_Dictionary* pFormResource,
-                                  CPDF_Dictionary* pPageResource,
+                                  const CPDF_Dictionary* pPageResource,
                                   bool bStdCS,
-                                  uint32_t GroupFamily,
-                                  bool bLoadMask) {
-  auto source = pdfium::MakeRetain<CPDF_DIB>();
-  CPDF_DIB::LoadState ret = source->StartLoadDIBBase(
-      m_pDocument.Get(), m_pStream.Get(), true, pFormResource, pPageResource,
-      bStdCS, GroupFamily, bLoadMask);
+                                  CPDF_ColorSpace::Family GroupFamily,
+                                  bool bLoadMask,
+                                  const CFX_Size& max_size_required) {
+  RetainPtr<CPDF_DIB> source = CreateNewDIB();
+  CPDF_DIB::LoadState ret =
+      source->StartLoadDIBBase(true, pFormResource, pPageResource, bStdCS,
+                               GroupFamily, bLoadMask, max_size_required);
   if (ret == CPDF_DIB::LoadState::kFail) {
     m_pDIBBase.Reset();
     return false;
diff --git a/core/fpdfapi/page/cpdf_image.h b/core/fpdfapi/page/cpdf_image.h
index 4194d49..35357fc 100644
--- a/core/fpdfapi/page/cpdf_image.h
+++ b/core/fpdfapi/page/cpdf_image.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,16 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_IMAGE_H_
 #define CORE_FPDFAPI_PAGE_CPDF_IMAGE_H_
 
-#include <memory>
+#include <stdint.h>
 
-#include "core/fxcrt/fx_system.h"
+#include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_DIBBase;
 class CFX_DIBitmap;
+class CPDF_DIB;
 class CPDF_Dictionary;
 class CPDF_Document;
 class CPDF_Page;
@@ -25,40 +26,43 @@
 
 class CPDF_Image final : public Retainable {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   static bool IsValidJpegComponent(int32_t comps);
   static bool IsValidJpegBitsPerComponent(int32_t bpc);
 
   void ConvertStreamToIndirectObject();
 
-  CPDF_Dictionary* GetDict() const;
-  CPDF_Stream* GetStream() const { return m_pStream.Get(); }
-  const CPDF_Dictionary* GetOC() const { return m_pOC.Get(); }
-  CPDF_Document* GetDocument() const { return m_pDocument.Get(); }
+  RetainPtr<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; }
   int32_t GetPixelWidth() const { return m_Width; }
-
+  uint32_t GetMatteColor() const { return m_MatteColor; }
   bool IsInline() const { return m_bIsInline; }
   bool IsMask() const { return m_bIsMask; }
   bool IsInterpol() const { return m_bInterpolate; }
 
+  RetainPtr<CPDF_DIB> CreateNewDIB() const;
   RetainPtr<CFX_DIBBase> LoadDIBBase() const;
 
   void SetImage(const RetainPtr<CFX_DIBitmap>& pBitmap);
-  void SetJpegImage(const RetainPtr<IFX_SeekableReadStream>& pFile);
-  void SetJpegImageInline(const RetainPtr<IFX_SeekableReadStream>& pFile);
+  void SetJpegImage(RetainPtr<IFX_SeekableReadStream> pFile);
+  void SetJpegImageInline(RetainPtr<IFX_SeekableReadStream> pFile);
 
   void ResetCache(CPDF_Page* pPage);
 
   // Returns whether to Continue() or not.
   bool StartLoadDIBBase(const CPDF_Dictionary* pFormResource,
-                        CPDF_Dictionary* pPageResource,
+                        const CPDF_Dictionary* pPageResource,
                         bool bStdCS,
-                        uint32_t GroupFamily,
-                        bool bLoadMask);
+                        CPDF_ColorSpace::Family GroupFamily,
+                        bool bLoadMask,
+                        const CFX_Size& max_size_required);
 
   // Returns whether to Continue() or not.
   bool Continue(PauseIndicatorIface* pPause);
@@ -66,27 +70,25 @@
   RetainPtr<CFX_DIBBase> DetachBitmap();
   RetainPtr<CFX_DIBBase> DetachMask();
 
-  RetainPtr<CFX_DIBBase> m_pDIBBase;
-  RetainPtr<CFX_DIBBase> m_pMask;
-  uint32_t m_MatteColor = 0;
-
  private:
   explicit CPDF_Image(CPDF_Document* pDoc);
   CPDF_Image(CPDF_Document* pDoc, RetainPtr<CPDF_Stream> pStream);
   CPDF_Image(CPDF_Document* pDoc, uint32_t dwStreamObjNum);
   ~CPDF_Image() override;
 
-  void FinishInitialization(CPDF_Dictionary* pStreamDict);
+  void FinishInitialization();
   RetainPtr<CPDF_Dictionary> InitJPEG(pdfium::span<uint8_t> src_span);
-
   RetainPtr<CPDF_Dictionary> CreateXObjectImageDict(int width, int height);
 
   int32_t m_Height = 0;
   int32_t m_Width = 0;
+  uint32_t m_MatteColor = 0;
   bool m_bIsInline = false;
   bool m_bIsMask = false;
   bool m_bInterpolate = false;
   UnownedPtr<CPDF_Document> const m_pDocument;
+  RetainPtr<CFX_DIBBase> m_pDIBBase;
+  RetainPtr<CFX_DIBBase> m_pMask;
   RetainPtr<CPDF_Stream> m_pStream;
   RetainPtr<const CPDF_Dictionary> m_pOC;
 };
diff --git a/core/fpdfapi/page/cpdf_imageloader.cpp b/core/fpdfapi/page/cpdf_imageloader.cpp
new file mode 100644
index 0000000..78f4f1f
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_imageloader.cpp
@@ -0,0 +1,80 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/page/cpdf_imageloader.h"
+
+#include <utility>
+
+#include "core/fpdfapi/page/cpdf_dib.h"
+#include "core/fpdfapi/page/cpdf_image.h"
+#include "core/fpdfapi/page/cpdf_imageobject.h"
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
+#include "core/fpdfapi/page/cpdf_transferfunc.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "third_party/base/check.h"
+
+CPDF_ImageLoader::CPDF_ImageLoader() = default;
+
+CPDF_ImageLoader::~CPDF_ImageLoader() = default;
+
+bool CPDF_ImageLoader::Start(const CPDF_ImageObject* pImage,
+                             CPDF_PageImageCache* pPageImageCache,
+                             const CPDF_Dictionary* pFormResource,
+                             const CPDF_Dictionary* pPageResource,
+                             bool bStdCS,
+                             CPDF_ColorSpace::Family eFamily,
+                             bool bLoadMask,
+                             const CFX_Size& max_size_required) {
+  m_pCache = pPageImageCache;
+  m_pImageObject = pImage;
+  bool ret;
+  if (m_pCache) {
+    ret = m_pCache->StartGetCachedBitmap(m_pImageObject->GetImage(),
+                                         pFormResource, pPageResource, bStdCS,
+                                         eFamily, bLoadMask, max_size_required);
+  } else {
+    ret = m_pImageObject->GetImage()->StartLoadDIBBase(
+        pFormResource, pPageResource, bStdCS, eFamily, bLoadMask,
+        max_size_required);
+  }
+  if (!ret)
+    HandleFailure();
+  return ret;
+}
+
+bool CPDF_ImageLoader::Continue(PauseIndicatorIface* pPause) {
+  bool ret = m_pCache ? m_pCache->Continue(pPause)
+                      : m_pImageObject->GetImage()->Continue(pPause);
+  if (!ret)
+    HandleFailure();
+  return ret;
+}
+
+RetainPtr<CFX_DIBBase> CPDF_ImageLoader::TranslateImage(
+    RetainPtr<CPDF_TransferFunc> pTransferFunc) {
+  DCHECK(pTransferFunc);
+  DCHECK(!pTransferFunc->GetIdentity());
+  m_pBitmap = pTransferFunc->TranslateImage(std::move(m_pBitmap));
+  if (m_bCached && m_pMask)
+    m_pMask = m_pMask->Realize();
+  m_bCached = false;
+  return m_pBitmap;
+}
+
+void CPDF_ImageLoader::HandleFailure() {
+  if (m_pCache) {
+    m_bCached = true;
+    m_pBitmap = m_pCache->DetachCurBitmap();
+    m_pMask = m_pCache->DetachCurMask();
+    m_MatteColor = m_pCache->GetCurMatteColor();
+    return;
+  }
+  RetainPtr<CPDF_Image> pImage = m_pImageObject->GetImage();
+  m_bCached = false;
+  m_pBitmap = pImage->DetachBitmap();
+  m_pMask = pImage->DetachMask();
+  m_MatteColor = pImage->GetMatteColor();
+}
diff --git a/core/fpdfapi/page/cpdf_imageloader.h b/core/fpdfapi/page/cpdf_imageloader.h
new file mode 100644
index 0000000..b0fcfb9
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_imageloader.h
@@ -0,0 +1,54 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFAPI_PAGE_CPDF_IMAGELOADER_H_
+#define CORE_FPDFAPI_PAGE_CPDF_IMAGELOADER_H_
+
+#include "core/fpdfapi/page/cpdf_colorspace.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+
+class CFX_DIBBase;
+class CPDF_Dictionary;
+class CPDF_ImageObject;
+class CPDF_PageImageCache;
+class CPDF_TransferFunc;
+class PauseIndicatorIface;
+
+class CPDF_ImageLoader {
+ public:
+  CPDF_ImageLoader();
+  ~CPDF_ImageLoader();
+
+  bool Start(const CPDF_ImageObject* pImage,
+             CPDF_PageImageCache* pPageImageCache,
+             const CPDF_Dictionary* pFormResource,
+             const CPDF_Dictionary* pPageResource,
+             bool bStdCS,
+             CPDF_ColorSpace::Family eFamily,
+             bool bLoadMask,
+             const CFX_Size& max_size_required);
+  bool Continue(PauseIndicatorIface* pPause);
+
+  RetainPtr<CFX_DIBBase> TranslateImage(
+      RetainPtr<CPDF_TransferFunc> pTransferFunc);
+
+  const RetainPtr<CFX_DIBBase>& GetBitmap() const { return m_pBitmap; }
+  const RetainPtr<CFX_DIBBase>& GetMask() const { return m_pMask; }
+  uint32_t MatteColor() const { return m_MatteColor; }
+
+ private:
+  void HandleFailure();
+
+  uint32_t m_MatteColor = 0;
+  bool m_bCached = false;
+  RetainPtr<CFX_DIBBase> m_pBitmap;
+  RetainPtr<CFX_DIBBase> m_pMask;
+  UnownedPtr<CPDF_PageImageCache> m_pCache;
+  UnownedPtr<const CPDF_ImageObject> m_pImageObject;
+};
+
+#endif  // CORE_FPDFAPI_PAGE_CPDF_IMAGELOADER_H_
diff --git a/core/fpdfapi/page/cpdf_imageobject.cpp b/core/fpdfapi/page/cpdf_imageobject.cpp
index 0f6a26b..0511a02 100644
--- a/core/fpdfapi/page/cpdf_imageobject.cpp
+++ b/core/fpdfapi/page/cpdf_imageobject.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,12 +6,12 @@
 
 #include "core/fpdfapi/page/cpdf_imageobject.h"
 
-#include <memory>
+#include <utility>
 
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
 #include "core/fpdfapi/page/cpdf_image.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/fx_coordinates.h"
 #include "core/fxge/dib/cfx_dibbase.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 
@@ -25,7 +25,7 @@
 }
 
 CPDF_PageObject::Type CPDF_ImageObject::GetType() const {
-  return IMAGE;
+  return Type::kImage;
 }
 
 void CPDF_ImageObject::Transform(const CFX_Matrix& matrix) {
@@ -48,12 +48,13 @@
 
 void CPDF_ImageObject::CalcBoundingBox() {
   static constexpr CFX_FloatRect kRect(0.0f, 0.0f, 1.0f, 1.0f);
+  SetOriginalRect(kRect);
   SetRect(m_Matrix.TransformRect(kRect));
 }
 
-void CPDF_ImageObject::SetImage(const RetainPtr<CPDF_Image>& pImage) {
+void CPDF_ImageObject::SetImage(RetainPtr<CPDF_Image> pImage) {
   MaybePurgeCache();
-  m_pImage = pImage;
+  m_pImage = std::move(pImage);
 }
 
 RetainPtr<CPDF_Image> CPDF_ImageObject::GetImage() const {
@@ -63,23 +64,25 @@
 RetainPtr<CFX_DIBitmap> CPDF_ImageObject::GetIndependentBitmap() const {
   RetainPtr<CFX_DIBBase> pSource = GetImage()->LoadDIBBase();
 
-  // Clone() is non-virtual, and can't be overloaded by CPDF_DIB to
-  // return a clone of the subclass as one would typically expect from a
-  // such a method. Instead, it only clones the CFX_DIBBase, none of whose
-  // members point to objects owned by |this| or the form containing |this|.
-  // As a result, the clone may outlive them.
-  return pSource ? pSource->Clone(nullptr) : nullptr;
+  // Realize() is non-virtual, and can't be overloaded by CPDF_DIB to
+  // return a full-up CPDF_DIB subclass. Instead, it only works upon the
+  // CFX_DIBBase, which is convenient since none of its members point to
+  // objects owned by |this| or the form containing |this|. As a result,
+  // the new bitmap may outlive them, giving the "independent" property
+  // this method is named after.
+  return pSource ? pSource->Realize() : nullptr;
+}
+
+void CPDF_ImageObject::SetImageMatrix(const CFX_Matrix& matrix) {
+  m_Matrix = matrix;
+  CalcBoundingBox();
 }
 
 void CPDF_ImageObject::MaybePurgeCache() {
   if (!m_pImage)
     return;
 
-  auto* pPageData = CPDF_DocPageData::FromDocument(m_pImage->GetDocument());
-  if (!pPageData)
-    return;
-
-  CPDF_Stream* pStream = m_pImage->GetStream();
+  RetainPtr<const CPDF_Stream> pStream = m_pImage->GetStream();
   if (!pStream)
     return;
 
@@ -87,6 +90,9 @@
   if (!objnum)
     return;
 
+  auto* pDoc = m_pImage->GetDocument();
+  CHECK(pDoc);
+
   m_pImage.Reset();  // Clear my reference before asking the cache.
-  pPageData->MaybePurgeImage(objnum);
+  pDoc->MaybePurgeImage(objnum);
 }
diff --git a/core/fpdfapi/page/cpdf_imageobject.h b/core/fpdfapi/page/cpdf_imageobject.h
index f221a98..06e5a64 100644
--- a/core/fpdfapi/page/cpdf_imageobject.h
+++ b/core/fpdfapi/page/cpdf_imageobject.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -28,11 +28,11 @@
   const CPDF_ImageObject* AsImage() const override;
 
   void CalcBoundingBox();
-  void SetImage(const RetainPtr<CPDF_Image>& pImage);
+  void SetImage(RetainPtr<CPDF_Image> pImage);
   RetainPtr<CPDF_Image> GetImage() const;
   RetainPtr<CFX_DIBitmap> GetIndependentBitmap() const;
 
-  void set_matrix(const CFX_Matrix& matrix) { m_Matrix = matrix; }
+  void SetImageMatrix(const CFX_Matrix& matrix);
   const CFX_Matrix& matrix() const { return m_Matrix; }
 
  private:
diff --git a/core/fpdfapi/page/cpdf_indexedcs.cpp b/core/fpdfapi/page/cpdf_indexedcs.cpp
new file mode 100644
index 0000000..e35352b
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_indexedcs.cpp
@@ -0,0 +1,109 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/page/cpdf_indexedcs.h"
+
+#include <set>
+#include <vector>
+
+#include "core/fpdfapi/page/cpdf_colorspace.h"
+#include "core/fpdfapi/page/cpdf_docpagedata.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_object.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_2d_size.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/span.h"
+
+CPDF_IndexedCS::CPDF_IndexedCS() : CPDF_BasedCS(Family::kIndexed) {}
+
+CPDF_IndexedCS::~CPDF_IndexedCS() = default;
+
+const CPDF_IndexedCS* CPDF_IndexedCS::AsIndexedCS() const {
+  return this;
+}
+
+uint32_t CPDF_IndexedCS::v_Load(CPDF_Document* pDoc,
+                                const CPDF_Array* pArray,
+                                std::set<const CPDF_Object*>* pVisited) {
+  if (pArray->size() < 4)
+    return 0;
+
+  RetainPtr<const CPDF_Object> pBaseObj = pArray->GetDirectObjectAt(1);
+  if (HasSameArray(pBaseObj.Get()))
+    return 0;
+
+  auto* pDocPageData = CPDF_DocPageData::FromDocument(pDoc);
+  m_pBaseCS =
+      pDocPageData->GetColorSpaceGuarded(pBaseObj.Get(), nullptr, pVisited);
+  if (!m_pBaseCS)
+    return 0;
+
+  // The base color space cannot be a Pattern or Indexed space, according to the
+  // PDF 1.7 spec, page 263.
+  Family family = m_pBaseCS->GetFamily();
+  if (family == Family::kIndexed || family == Family::kPattern)
+    return 0;
+
+  m_nBaseComponents = m_pBaseCS->CountComponents();
+  DCHECK(m_nBaseComponents);
+  m_pCompMinMax = DataVector<float>(Fx2DSizeOrDie(m_nBaseComponents, 2));
+  float defvalue;
+  for (uint32_t i = 0; i < m_nBaseComponents; i++) {
+    m_pBaseCS->GetDefaultValue(i, &defvalue, &m_pCompMinMax[i * 2],
+                               &m_pCompMinMax[i * 2 + 1]);
+    m_pCompMinMax[i * 2 + 1] -= m_pCompMinMax[i * 2];
+  }
+  m_MaxIndex = pArray->GetIntegerAt(2);
+
+  RetainPtr<const CPDF_Object> pTableObj = pArray->GetDirectObjectAt(3);
+  if (!pTableObj)
+    return 0;
+
+  if (const CPDF_String* pString = pTableObj->AsString()) {
+    m_Table = pString->GetString();
+  } else if (const CPDF_Stream* pStream = pTableObj->AsStream()) {
+    auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pdfium::WrapRetain(pStream));
+    pAcc->LoadAllDataFiltered();
+    m_Table = ByteStringView(pAcc->GetSpan());
+  }
+  return 1;
+}
+
+bool CPDF_IndexedCS::GetRGB(pdfium::span<const float> pBuf,
+                            float* R,
+                            float* G,
+                            float* B) const {
+  int32_t index = static_cast<int32_t>(pBuf[0]);
+  if (index < 0 || index > m_MaxIndex)
+    return false;
+
+  DCHECK(m_nBaseComponents);
+  DCHECK_EQ(m_nBaseComponents, m_pBaseCS->CountComponents());
+
+  FX_SAFE_SIZE_T length = index;
+  length += 1;
+  length *= m_nBaseComponents;
+  if (!length.IsValid() || length.ValueOrDie() > m_Table.GetLength()) {
+    *R = 0;
+    *G = 0;
+    *B = 0;
+    return false;
+  }
+
+  std::vector<float> comps(m_nBaseComponents);
+  const uint8_t* pTable = m_Table.raw_str();
+  for (uint32_t i = 0; i < m_nBaseComponents; ++i) {
+    comps[i] =
+        m_pCompMinMax[i * 2] +
+        m_pCompMinMax[i * 2 + 1] * pTable[index * m_nBaseComponents + i] / 255;
+  }
+  return m_pBaseCS->GetRGB(comps, R, G, B);
+}
diff --git a/core/fpdfapi/page/cpdf_indexedcs.h b/core/fpdfapi/page/cpdf_indexedcs.h
new file mode 100644
index 0000000..325501f
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_indexedcs.h
@@ -0,0 +1,43 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FPDFAPI_PAGE_CPDF_INDEXEDCS_H_
+#define CORE_FPDFAPI_PAGE_CPDF_INDEXEDCS_H_
+
+#include <set>
+
+#include "core/fpdfapi/page/cpdf_basedcs.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/retain_ptr.h"
+
+class CPDF_Document;
+
+class CPDF_IndexedCS final : public CPDF_BasedCS {
+ public:
+  CONSTRUCT_VIA_MAKE_RETAIN;
+  ~CPDF_IndexedCS() override;
+
+  // CPDF_ColorSpace:
+  bool GetRGB(pdfium::span<const float> pBuf,
+              float* R,
+              float* G,
+              float* B) const override;
+  const CPDF_IndexedCS* AsIndexedCS() const override;
+  uint32_t v_Load(CPDF_Document* pDoc,
+                  const CPDF_Array* pArray,
+                  std::set<const CPDF_Object*>* pVisited) override;
+
+  int GetMaxIndex() const { return m_MaxIndex; }
+
+ private:
+  CPDF_IndexedCS();
+
+  uint32_t m_nBaseComponents = 0;
+  int m_MaxIndex = 0;
+  ByteString m_Table;
+  DataVector<float> m_pCompMinMax;
+};
+
+#endif  // CORE_FPDFAPI_PAGE_CPDF_INDEXEDCS_H_
diff --git a/core/fpdfapi/page/cpdf_meshstream.cpp b/core/fpdfapi/page/cpdf_meshstream.cpp
index 06b2329..6a0419e 100644
--- a/core/fpdfapi/page/cpdf_meshstream.cpp
+++ b/core/fpdfapi/page/cpdf_meshstream.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,14 +6,17 @@
 
 #include "core/fpdfapi/page/cpdf_meshstream.h"
 
+#include <utility>
+
 #include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fpdfapi/page/cpdf_function.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/span.h"
+#include "core/fxcrt/cfx_bitstream.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/span.h"
 
 namespace {
 
@@ -97,33 +100,21 @@
 CPDF_MeshStream::CPDF_MeshStream(
     ShadingType type,
     const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-    const CPDF_Stream* pShadingStream,
-    const RetainPtr<CPDF_ColorSpace>& pCS)
+    RetainPtr<const CPDF_Stream> pShadingStream,
+    RetainPtr<CPDF_ColorSpace> pCS)
     : m_type(type),
       m_funcs(funcs),
-      m_pShadingStream(pShadingStream),
-      m_pCS(pCS),
-      m_nCoordBits(0),
-      m_nComponentBits(0),
-      m_nFlagBits(0),
-      m_nComponents(0),
-      m_CoordMax(0),
-      m_ComponentMax(0),
-      m_xmin(0),
-      m_xmax(0),
-      m_ymin(0),
-      m_ymax(0),
-      m_pStream(pdfium::MakeRetain<CPDF_StreamAcc>(pShadingStream)) {
-  memset(&m_ColorMin, 0, sizeof(m_ColorMin));
-  memset(&m_ColorMax, 0, sizeof(m_ColorMax));
-}
+      m_pShadingStream(std::move(pShadingStream)),
+      m_pCS(std::move(pCS)),
+      m_pStream(pdfium::MakeRetain<CPDF_StreamAcc>(m_pShadingStream)) {}
 
-CPDF_MeshStream::~CPDF_MeshStream() {}
+CPDF_MeshStream::~CPDF_MeshStream() = default;
 
 bool CPDF_MeshStream::Load() {
   m_pStream->LoadAllDataFiltered();
-  m_BitStream = pdfium::MakeUnique<CFX_BitStream>(m_pStream->GetSpan());
-  const CPDF_Dictionary* pDict = m_pShadingStream->GetDict();
+  m_BitStream = std::make_unique<CFX_BitStream>(m_pStream->GetSpan());
+
+  RetainPtr<const CPDF_Dictionary> pDict = m_pShadingStream->GetDict();
   m_nCoordBits = pDict->GetIntegerFor("BitsPerCoordinate");
   m_nComponentBits = pDict->GetIntegerFor("BitsPerComponent");
   if (ShouldCheckBPC(m_type)) {
@@ -142,17 +133,17 @@
     return false;
 
   m_nComponents = m_funcs.empty() ? nComponents : 1;
-  const CPDF_Array* pDecode = pDict->GetArrayFor("Decode");
+  RetainPtr<const CPDF_Array> pDecode = pDict->GetArrayFor("Decode");
   if (!pDecode || pDecode->size() != 4 + m_nComponents * 2)
     return false;
 
-  m_xmin = pDecode->GetNumberAt(0);
-  m_xmax = pDecode->GetNumberAt(1);
-  m_ymin = pDecode->GetNumberAt(2);
-  m_ymax = pDecode->GetNumberAt(3);
+  m_xmin = pDecode->GetFloatAt(0);
+  m_xmax = pDecode->GetFloatAt(1);
+  m_ymin = pDecode->GetFloatAt(2);
+  m_ymax = pDecode->GetFloatAt(3);
   for (uint32_t i = 0; i < m_nComponents; ++i) {
-    m_ColorMin[i] = pDecode->GetNumberAt(i * 2 + 4);
-    m_ColorMax[i] = pDecode->GetNumberAt(i * 2 + 5);
+    m_ColorMin[i] = pDecode->GetFloatAt(i * 2 + 4);
+    m_ColorMax[i] = pDecode->GetFloatAt(i * 2 + 5);
   }
 
   if (ShouldCheckBPC(m_type)) {
@@ -162,6 +153,18 @@
   return true;
 }
 
+void CPDF_MeshStream::SkipBits(uint32_t nbits) {
+  m_BitStream->SkipBits(nbits);
+}
+
+void CPDF_MeshStream::ByteAlign() {
+  m_BitStream->ByteAlign();
+}
+
+bool CPDF_MeshStream::IsEOF() const {
+  return m_BitStream->IsEOF();
+}
+
 bool CPDF_MeshStream::CanReadFlag() const {
   return m_BitStream->BitsRemaining() >= m_nFlagBits;
 }
@@ -175,12 +178,12 @@
 }
 
 uint32_t CPDF_MeshStream::ReadFlag() {
-  ASSERT(ShouldCheckBitsPerFlag(m_type));
+  DCHECK(ShouldCheckBitsPerFlag(m_type));
   return m_BitStream->GetBits(m_nFlagBits) & 0x03;
 }
 
 CFX_PointF CPDF_MeshStream::ReadCoords() {
-  ASSERT(ShouldCheckBPC(m_type));
+  DCHECK(ShouldCheckBPC(m_type));
 
   CFX_PointF pos;
   if (m_nCoordBits == 32) {
@@ -198,7 +201,7 @@
 }
 
 std::tuple<float, float, float> CPDF_MeshStream::ReadColor() {
-  ASSERT(ShouldCheckBPC(m_type));
+  DCHECK(ShouldCheckBPC(m_type));
 
   float color_value[kMaxComponents];
   for (uint32_t i = 0; i < m_nComponents; ++i) {
@@ -215,12 +218,10 @@
     return std::tuple<float, float, float>(r, g, b);
   }
 
-  float result[kMaxComponents];
-  memset(result, 0, sizeof(result));
-  int nResults;
+  float result[kMaxComponents] = {};
   for (const auto& func : m_funcs) {
     if (func && func->CountOutputs() <= kMaxComponents)
-      func->Call(color_value, 1, result, &nResults);
+      func->Call(pdfium::make_span(color_value, 1), result);
   }
 
   m_pCS->GetRGB(result, &r, &g, &b);
@@ -253,7 +254,7 @@
     if (m_BitStream->IsEOF() || !CanReadCoords())
       return std::vector<CPDF_MeshVertex>();
 
-    vertices.push_back(CPDF_MeshVertex());
+    vertices.emplace_back();
     CPDF_MeshVertex& vertex = vertices.back();
     vertex.position = pObject2Bitmap.Transform(ReadCoords());
     if (!CanReadColor())
diff --git a/core/fpdfapi/page/cpdf_meshstream.h b/core/fpdfapi/page/cpdf_meshstream.h
index 939b192..b1dab6e 100644
--- a/core/fpdfapi/page/cpdf_meshstream.h
+++ b/core/fpdfapi/page/cpdf_meshstream.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_MESHSTREAM_H_
 #define CORE_FPDFAPI_PAGE_CPDF_MESHSTREAM_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <tuple>
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_shadingpattern.h"
-#include "core/fxcrt/cfx_bitstream.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 
 class CPDF_StreamAcc;
@@ -25,11 +25,12 @@
   ~CPDF_MeshVertex();
 
   CFX_PointF position;
-  float r;
-  float g;
-  float b;
+  float r = 0.0f;
+  float g = 0.0f;
+  float b = 0.0f;
 };
 
+class CFX_BitStream;
 class CFX_Matrix;
 class CPDF_ColorSpace;
 class CPDF_Function;
@@ -39,12 +40,15 @@
  public:
   CPDF_MeshStream(ShadingType type,
                   const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-                  const CPDF_Stream* pShadingStream,
-                  const RetainPtr<CPDF_ColorSpace>& pCS);
+                  RetainPtr<const CPDF_Stream> pShadingStream,
+                  RetainPtr<CPDF_ColorSpace> pCS);
   ~CPDF_MeshStream();
 
   bool Load();
+  void SkipBits(uint32_t nbits);
+  void ByteAlign();
 
+  bool IsEOF() const;
   bool CanReadFlag() const;
   bool CanReadCoords() const;
   bool CanReadColor() const;
@@ -59,31 +63,30 @@
   std::vector<CPDF_MeshVertex> ReadVertexRow(const CFX_Matrix& pObject2Bitmap,
                                              int count);
 
-  CFX_BitStream* BitStream() { return m_BitStream.get(); }
   uint32_t ComponentBits() const { return m_nComponentBits; }
   uint32_t Components() const { return m_nComponents; }
 
  private:
-  static const uint32_t kMaxComponents = 8;
+  static constexpr uint32_t kMaxComponents = 8;
 
   const ShadingType m_type;
   const std::vector<std::unique_ptr<CPDF_Function>>& m_funcs;
   RetainPtr<const CPDF_Stream> const m_pShadingStream;
   RetainPtr<CPDF_ColorSpace> const m_pCS;
-  uint32_t m_nCoordBits;
-  uint32_t m_nComponentBits;
-  uint32_t m_nFlagBits;
-  uint32_t m_nComponents;
-  uint32_t m_CoordMax;
-  uint32_t m_ComponentMax;
-  float m_xmin;
-  float m_xmax;
-  float m_ymin;
-  float m_ymax;
+  uint32_t m_nCoordBits = 0;
+  uint32_t m_nComponentBits = 0;
+  uint32_t m_nFlagBits = 0;
+  uint32_t m_nComponents = 0;
+  uint32_t m_CoordMax = 0;
+  uint32_t m_ComponentMax = 0;
+  float m_xmin = 0.0f;
+  float m_xmax = 0.0f;
+  float m_ymin = 0.0f;
+  float m_ymax = 0.0f;
   RetainPtr<CPDF_StreamAcc> m_pStream;
   std::unique_ptr<CFX_BitStream> m_BitStream;
-  float m_ColorMin[kMaxComponents];
-  float m_ColorMax[kMaxComponents];
+  float m_ColorMin[kMaxComponents] = {};
+  float m_ColorMax[kMaxComponents] = {};
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_MESHSTREAM_H_
diff --git a/core/fpdfapi/page/cpdf_occontext.cpp b/core/fpdfapi/page/cpdf_occontext.cpp
index 96f8a88..7d2b35f 100644
--- a/core/fpdfapi/page/cpdf_occontext.cpp
+++ b/core/fpdfapi/page/cpdf_occontext.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,31 +10,21 @@
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "third_party/base/check.h"
 
 namespace {
 
-int32_t FindGroup(const CPDF_Array* pArray, const CPDF_Dictionary* pGroupDict) {
-  if (!pArray || !pGroupDict)
-    return -1;
-
-  for (size_t i = 0; i < pArray->size(); i++) {
-    if (pArray->GetDictAt(i) == pGroupDict)
-      return i;
-  }
-  return -1;
-}
-
 bool HasIntent(const CPDF_Dictionary* pDict,
                ByteStringView csElement,
                ByteStringView csDef) {
-  const CPDF_Object* pIntent = pDict->GetDirectObjectFor("Intent");
+  RetainPtr<const CPDF_Object> pIntent = pDict->GetDirectObjectFor("Intent");
   if (!pIntent)
     return csElement == csDef;
 
   ByteString bsIntent;
   if (const CPDF_Array* pArray = pIntent->AsArray()) {
     for (size_t i = 0; i < pArray->size(); i++) {
-      bsIntent = pArray->GetStringAt(i);
+      bsIntent = pArray->GetByteStringAt(i);
       if (bsIntent == "All" || bsIntent == csElement)
         return true;
     }
@@ -44,28 +34,30 @@
   return bsIntent == "All" || bsIntent == csElement;
 }
 
-CPDF_Dictionary* GetConfig(CPDF_Document* pDoc,
-                           const CPDF_Dictionary* pOCGDict) {
-  ASSERT(pOCGDict);
-  CPDF_Dictionary* pOCProperties = pDoc->GetRoot()->GetDictFor("OCProperties");
+RetainPtr<const CPDF_Dictionary> GetConfig(CPDF_Document* pDoc,
+                                           const CPDF_Dictionary* pOCGDict) {
+  DCHECK(pOCGDict);
+  RetainPtr<const CPDF_Dictionary> pOCProperties =
+      pDoc->GetRoot()->GetDictFor("OCProperties");
   if (!pOCProperties)
     return nullptr;
 
-  CPDF_Array* pOCGs = pOCProperties->GetArrayFor("OCGs");
+  RetainPtr<const CPDF_Array> pOCGs = pOCProperties->GetArrayFor("OCGs");
   if (!pOCGs)
     return nullptr;
 
-  if (FindGroup(pOCGs, pOCGDict) < 0)
+  if (!pOCGs->Contains(pOCGDict))
     return nullptr;
 
-  CPDF_Dictionary* pConfig = pOCProperties->GetDictFor("D");
-  CPDF_Array* pConfigs = pOCProperties->GetArrayFor("Configs");
-  if (!pConfigs)
+  RetainPtr<const CPDF_Dictionary> pConfig = pOCProperties->GetDictFor("D");
+  RetainPtr<const CPDF_Array> pConfigArray =
+      pOCProperties->GetArrayFor("Configs");
+  if (!pConfigArray)
     return pConfig;
 
-  for (size_t i = 0; i < pConfigs->size(); i++) {
-    CPDF_Dictionary* pFind = pConfigs->GetDictAt(i);
-    if (pFind && HasIntent(pFind, "View", ""))
+  for (size_t i = 0; i < pConfigArray->size(); i++) {
+    RetainPtr<const CPDF_Dictionary> pFind = pConfigArray->GetDictAt(i);
+    if (pFind && HasIntent(pFind.Get(), "View", ""))
       return pFind;
   }
   return pConfig;
@@ -74,13 +66,13 @@
 ByteString GetUsageTypeString(CPDF_OCContext::UsageType eType) {
   ByteString csState;
   switch (eType) {
-    case CPDF_OCContext::Design:
+    case CPDF_OCContext::kDesign:
       csState = "Design";
       break;
-    case CPDF_OCContext::Print:
+    case CPDF_OCContext::kPrint:
       csState = "Print";
       break;
-    case CPDF_OCContext::Export:
+    case CPDF_OCContext::kExport:
       csState = "Export";
       break;
     default:
@@ -94,54 +86,52 @@
 
 CPDF_OCContext::CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType)
     : m_pDocument(pDoc), m_eUsageType(eUsageType) {
-  ASSERT(pDoc);
+  DCHECK(pDoc);
 }
 
-CPDF_OCContext::~CPDF_OCContext() {}
+CPDF_OCContext::~CPDF_OCContext() = default;
 
 bool CPDF_OCContext::LoadOCGStateFromConfig(
     const ByteString& csConfig,
     const CPDF_Dictionary* pOCGDict) const {
-  CPDF_Dictionary* pConfig = GetConfig(m_pDocument.Get(), pOCGDict);
+  RetainPtr<const CPDF_Dictionary> pConfig = GetConfig(m_pDocument, pOCGDict);
   if (!pConfig)
     return true;
 
-  bool bState = pConfig->GetStringFor("BaseState", "ON") != "OFF";
-  CPDF_Array* pArray = pConfig->GetArrayFor("ON");
-  if (pArray) {
-    if (FindGroup(pArray, pOCGDict) >= 0)
-      bState = true;
-  }
+  bool bState = pConfig->GetByteStringFor("BaseState", "ON") != "OFF";
+  RetainPtr<const CPDF_Array> pArray = pConfig->GetArrayFor("ON");
+  if (pArray && pArray->Contains(pOCGDict))
+    bState = true;
+
   pArray = pConfig->GetArrayFor("OFF");
-  if (pArray) {
-    if (FindGroup(pArray, pOCGDict) >= 0)
-      bState = false;
-  }
+  if (pArray && pArray->Contains(pOCGDict))
+    bState = false;
+
   pArray = pConfig->GetArrayFor("AS");
   if (!pArray)
     return bState;
 
   ByteString csFind = csConfig + "State";
   for (size_t i = 0; i < pArray->size(); i++) {
-    CPDF_Dictionary* pUsage = pArray->GetDictAt(i);
+    RetainPtr<const CPDF_Dictionary> pUsage = pArray->GetDictAt(i);
     if (!pUsage)
       continue;
 
-    if (pUsage->GetStringFor("Event", "View") != csConfig)
+    if (pUsage->GetByteStringFor("Event", "View") != csConfig)
       continue;
 
-    CPDF_Array* pOCGs = pUsage->GetArrayFor("OCGs");
+    RetainPtr<const CPDF_Array> pOCGs = pUsage->GetArrayFor("OCGs");
     if (!pOCGs)
       continue;
 
-    if (FindGroup(pOCGs, pOCGDict) < 0)
+    if (!pOCGs->Contains(pOCGDict))
       continue;
 
-    CPDF_Dictionary* pState = pUsage->GetDictFor(csConfig);
+    RetainPtr<const CPDF_Dictionary> pState = pUsage->GetDictFor(csConfig);
     if (!pState)
       continue;
 
-    bState = pState->GetStringFor(csFind) != "OFF";
+    bState = pState->GetByteStringFor(csFind) != "OFF";
   }
   return bState;
 }
@@ -151,18 +141,18 @@
     return true;
 
   ByteString csState = GetUsageTypeString(m_eUsageType);
-  const CPDF_Dictionary* pUsage = pOCGDict->GetDictFor("Usage");
+  RetainPtr<const CPDF_Dictionary> pUsage = pOCGDict->GetDictFor("Usage");
   if (pUsage) {
-    const CPDF_Dictionary* pState = pUsage->GetDictFor(csState);
+    RetainPtr<const CPDF_Dictionary> pState = pUsage->GetDictFor(csState);
     if (pState) {
       ByteString csFind = csState + "State";
       if (pState->KeyExist(csFind))
-        return pState->GetStringFor(csFind) != "OFF";
+        return pState->GetByteStringFor(csFind) != "OFF";
     }
     if (csState != "View") {
       pState = pUsage->GetDictFor("View");
       if (pState && pState->KeyExist("ViewState"))
-        return pState->GetStringFor("ViewState") != "OFF";
+        return pState->GetByteStringFor("ViewState") != "OFF";
     }
   }
   return LoadOCGStateFromConfig(csState, pOCGDict);
@@ -177,16 +167,17 @@
     return it->second;
 
   bool bState = LoadOCGState(pOCGDict);
-  m_OGCStateCache[pOCGDict] = bState;
+  m_OGCStateCache[pdfium::WrapRetain(pOCGDict)] = bState;
   return bState;
 }
 
-bool CPDF_OCContext::CheckObjectVisible(const CPDF_PageObject* pObj) const {
-  for (size_t i = 0; i < pObj->m_ContentMarks.CountItems(); ++i) {
-    const CPDF_ContentMarkItem* item = pObj->m_ContentMarks.GetItem(i);
+bool CPDF_OCContext::CheckPageObjectVisible(const CPDF_PageObject* pObj) const {
+  const CPDF_ContentMarks* pMarks = pObj->GetContentMarks();
+  for (size_t i = 0; i < pMarks->CountItems(); ++i) {
+    const CPDF_ContentMarkItem* item = pMarks->GetItem(i);
     if (item->GetName() == "OC" &&
         item->GetParamType() == CPDF_ContentMarkItem::kPropertiesDict &&
-        !CheckOCGVisible(item->GetParam())) {
+        !CheckOCGDictVisible(item->GetParam().Get())) {
       return false;
     }
   }
@@ -197,9 +188,9 @@
   if (nLevel > 32 || !pExpression)
     return false;
 
-  ByteString csOperator = pExpression->GetStringAt(0);
+  ByteString csOperator = pExpression->GetByteStringAt(0);
   if (csOperator == "Not") {
-    const CPDF_Object* pOCGObj = pExpression->GetDirectObjectAt(1);
+    RetainPtr<const CPDF_Object> pOCGObj = pExpression->GetDirectObjectAt(1);
     if (!pOCGObj)
       return false;
     if (const CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
@@ -214,7 +205,7 @@
 
   bool bValue = false;
   for (size_t i = 1; i < pExpression->size(); i++) {
-    const CPDF_Object* pOCGObj = pExpression->GetDirectObjectAt(1);
+    RetainPtr<const CPDF_Object> pOCGObj = pExpression->GetDirectObjectAt(i);
     if (!pOCGObj)
       continue;
 
@@ -238,12 +229,13 @@
 }
 
 bool CPDF_OCContext::LoadOCMDState(const CPDF_Dictionary* pOCMDDict) const {
-  const CPDF_Array* pVE = pOCMDDict->GetArrayFor("VE");
-  if (pVE)
-    return GetOCGVE(pVE, 0);
+  RetainPtr<const CPDF_Array> pVE = pOCMDDict->GetArrayFor("VE");
+  if (pVE) {
+    return GetOCGVE(pVE.Get(), 0);
+  }
 
-  ByteString csP = pOCMDDict->GetStringFor("P", "AnyOn");
-  const CPDF_Object* pOCGObj = pOCMDDict->GetDirectObjectFor("OCGs");
+  ByteString csP = pOCMDDict->GetByteStringFor("P", "AnyOn");
+  RetainPtr<const CPDF_Object> pOCGObj = pOCMDDict->GetDirectObjectFor("OCGs");
   if (!pOCGObj)
     return true;
 
@@ -260,12 +252,12 @@
   bool bValidEntrySeen = false;
   for (size_t i = 0; i < pArray->size(); i++) {
     bool bItem = true;
-    const CPDF_Dictionary* pItemDict = pArray->GetDictAt(i);
+    RetainPtr<const CPDF_Dictionary> pItemDict = pArray->GetDictAt(i);
     if (!pItemDict)
       continue;
 
     bValidEntrySeen = true;
-    bItem = GetOCGVisible(pItemDict);
+    bItem = GetOCGVisible(pItemDict.Get());
 
     if ((csP == "AnyOn" && bItem) || (csP == "AnyOff" && !bItem))
       return true;
@@ -276,11 +268,12 @@
   return !bValidEntrySeen || bState;
 }
 
-bool CPDF_OCContext::CheckOCGVisible(const CPDF_Dictionary* pOCGDict) const {
+bool CPDF_OCContext::CheckOCGDictVisible(
+    const CPDF_Dictionary* pOCGDict) const {
   if (!pOCGDict)
     return true;
 
-  ByteString csType = pOCGDict->GetStringFor("Type", "OCG");
+  ByteString csType = pOCGDict->GetByteStringFor("Type", "OCG");
   if (csType == "OCG")
     return GetOCGVisible(pOCGDict);
   return LoadOCMDState(pOCGDict);
diff --git a/core/fpdfapi/page/cpdf_occontext.h b/core/fpdfapi/page/cpdf_occontext.h
index 0a68639..c0012d1 100644
--- a/core/fpdfapi/page/cpdf_occontext.h
+++ b/core/fpdfapi/page/cpdf_occontext.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,12 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_OCCONTEXT_H_
 #define CORE_FPDFAPI_PAGE_CPDF_OCCONTEXT_H_
 
+#include <functional>
 #include <map>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_Array;
 class CPDF_Dictionary;
@@ -19,13 +21,12 @@
 
 class CPDF_OCContext final : public Retainable {
  public:
-  enum UsageType { View = 0, Design, Print, Export };
+  enum UsageType { kView = 0, kDesign, kPrint, kExport };
 
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
-  bool CheckOCGVisible(const CPDF_Dictionary* pOCGDict) const;
-  bool CheckObjectVisible(const CPDF_PageObject* pObj) const;
+  bool CheckOCGDictVisible(const CPDF_Dictionary* pOCGDict) const;
+  bool CheckPageObjectVisible(const CPDF_PageObject* pObj) const;
 
  private:
   CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType);
@@ -40,7 +41,8 @@
 
   UnownedPtr<CPDF_Document> const m_pDocument;
   const UsageType m_eUsageType;
-  mutable std::map<const CPDF_Dictionary*, bool> m_OGCStateCache;
+  mutable std::map<RetainPtr<const CPDF_Dictionary>, bool, std::less<>>
+      m_OGCStateCache;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_OCCONTEXT_H_
diff --git a/core/fpdfapi/page/cpdf_page.cpp b/core/fpdfapi/page/cpdf_page.cpp
index 210f266..d3493de 100644
--- a/core/fpdfapi/page/cpdf_page.cpp
+++ b/core/fpdfapi/page/cpdf_page.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,24 +11,26 @@
 
 #include "constants/page_object.h"
 #include "core/fpdfapi/page/cpdf_contentparser.h"
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_object.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/contains.h"
 
-CPDF_Page::CPDF_Page(CPDF_Document* pDocument, CPDF_Dictionary* pPageDict)
-    : CPDF_PageObjectHolder(pDocument, pPageDict, nullptr, nullptr),
+CPDF_Page::CPDF_Page(CPDF_Document* pDocument,
+                     RetainPtr<CPDF_Dictionary> pPageDict)
+    : CPDF_PageObjectHolder(pDocument, std::move(pPageDict), nullptr, nullptr),
       m_PageSize(100, 100),
       m_pPDFDocument(pDocument) {
-  ASSERT(pPageDict);
-
   // Cannot initialize |m_pResources| and |m_pPageResources| via the
   // CPDF_PageObjectHolder ctor because GetPageAttr() requires
   // CPDF_PageObjectHolder to finish initializing first.
-  CPDF_Object* pPageAttr = GetPageAttr(pdfium::page_object::kResources);
-  m_pResources.Reset(pPageAttr ? pPageAttr->GetDict() : nullptr);
+  RetainPtr<CPDF_Object> pPageAttr =
+      GetMutablePageAttr(pdfium::page_object::kResources);
+  m_pResources = pPageAttr ? pPageAttr->GetMutableDict() : nullptr;
   m_pPageResources = m_pResources;
 
   UpdateDimensions();
@@ -36,7 +38,7 @@
   LoadTransparencyInfo();
 }
 
-CPDF_Page::~CPDF_Page() {}
+CPDF_Page::~CPDF_Page() = default;
 
 CPDF_Page* CPDF_Page::AsPDFPage() {
   return this;
@@ -47,7 +49,7 @@
 }
 
 CPDF_Document* CPDF_Page::GetDocument() const {
-  return GetPDFDocument();
+  return m_pPDFDocument;
 }
 
 float CPDF_Page::GetPageWidth() const {
@@ -67,30 +69,34 @@
     return;
 
   if (GetParseState() == ParseState::kNotParsed)
-    StartParse(pdfium::MakeUnique<CPDF_ContentParser>(this));
+    StartParse(std::make_unique<CPDF_ContentParser>(this));
 
-  ASSERT(GetParseState() == ParseState::kParsing);
+  DCHECK_EQ(GetParseState(), ParseState::kParsing);
   ContinueParse(nullptr);
 }
 
-CPDF_Object* CPDF_Page::GetPageAttr(const ByteString& name) const {
-  CPDF_Dictionary* pPageDict = GetDict();
-  std::set<CPDF_Dictionary*> visited;
-  while (1) {
-    visited.insert(pPageDict);
-    if (CPDF_Object* pObj = pPageDict->GetDirectObjectFor(name))
+RetainPtr<CPDF_Object> CPDF_Page::GetMutablePageAttr(const ByteString& name) {
+  return pdfium::WrapRetain(const_cast<CPDF_Object*>(GetPageAttr(name).Get()));
+}
+
+RetainPtr<const CPDF_Object> CPDF_Page::GetPageAttr(
+    const ByteString& name) const {
+  std::set<RetainPtr<const CPDF_Dictionary>> visited;
+  RetainPtr<const CPDF_Dictionary> pPageDict = GetDict();
+  while (pPageDict && !pdfium::Contains(visited, pPageDict)) {
+    RetainPtr<const CPDF_Object> pObj = pPageDict->GetDirectObjectFor(name);
+    if (pObj)
       return pObj;
 
+    visited.insert(pPageDict);
     pPageDict = pPageDict->GetDictFor(pdfium::page_object::kParent);
-    if (!pPageDict || pdfium::ContainsKey(visited, pPageDict))
-      break;
   }
   return nullptr;
 }
 
 CFX_FloatRect CPDF_Page::GetBox(const ByteString& name) const {
   CFX_FloatRect box;
-  CPDF_Array* pBox = ToArray(GetPageAttr(name));
+  RetainPtr<const CPDF_Array> pBox = ToArray(GetPageAttr(name));
   if (pBox) {
     box = pBox->GetRect();
     box.Normalize();
@@ -98,7 +104,7 @@
   return box;
 }
 
-Optional<CFX_PointF> CPDF_Page::DeviceToPage(
+absl::optional<CFX_PointF> CPDF_Page::DeviceToPage(
     const FX_RECT& rect,
     int rotate,
     const CFX_PointF& device_point) const {
@@ -106,7 +112,7 @@
   return page2device.GetInverse().Transform(device_point);
 }
 
-Optional<CFX_PointF> CPDF_Page::PageToDevice(
+absl::optional<CFX_PointF> CPDF_Page::PageToDevice(
     const FX_RECT& rect,
     int rotate,
     const CFX_PointF& page_point) const {
@@ -172,11 +178,43 @@
 }
 
 int CPDF_Page::GetPageRotation() const {
-  CPDF_Object* pRotate = GetPageAttr(pdfium::page_object::kRotate);
+  RetainPtr<const CPDF_Object> pRotate =
+      GetPageAttr(pdfium::page_object::kRotate);
   int rotate = pRotate ? (pRotate->GetInteger() / 90) % 4 : 0;
   return (rotate < 0) ? (rotate + 4) : rotate;
 }
 
+RetainPtr<CPDF_Array> CPDF_Page::GetOrCreateAnnotsArray() {
+  return GetMutableDict()->GetOrCreateArrayFor("Annots");
+}
+
+RetainPtr<CPDF_Array> CPDF_Page::GetMutableAnnotsArray() {
+  return GetMutableDict()->GetMutableArrayFor("Annots");
+}
+
+RetainPtr<const CPDF_Array> CPDF_Page::GetAnnotsArray() const {
+  return GetDict()->GetArrayFor("Annots");
+}
+
+void CPDF_Page::AddPageImageCache() {
+  m_pPageImageCache = std::make_unique<CPDF_PageImageCache>(this);
+}
+
+void CPDF_Page::SetRenderContext(std::unique_ptr<RenderContextIface> pContext) {
+  DCHECK(!m_pRenderContext);
+  DCHECK(pContext);
+  m_pRenderContext = std::move(pContext);
+}
+
+void CPDF_Page::ClearRenderContext() {
+  m_pRenderContext.reset();
+}
+
+void CPDF_Page::ClearView() {
+  if (m_pView)
+    m_pView->ClearPage(this);
+}
+
 void CPDF_Page::UpdateDimensions() {
   CFX_FloatRect mediabox = GetBox(pdfium::page_object::kMediaBox);
   if (mediabox.IsEmpty())
@@ -214,5 +252,6 @@
     : m_pPage(pPage) {}
 
 CPDF_Page::RenderContextClearer::~RenderContextClearer() {
-  m_pPage->SetRenderContext(nullptr);
+  if (m_pPage)
+    m_pPage->ClearRenderContext();
 }
diff --git a/core/fpdfapi/page/cpdf_page.h b/core/fpdfapi/page/cpdf_page.h
index 9bdb75d..2b659e6 100644
--- a/core/fpdfapi/page/cpdf_page.h
+++ b/core/fpdfapi/page/cpdf_page.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,37 +13,35 @@
 #include "core/fpdfapi/page/cpdf_pageobjectholder.h"
 #include "core/fpdfapi/page/ipdf_page.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
+class CPDF_Array;
 class CPDF_Dictionary;
 class CPDF_Document;
-class CPDF_Image;
 class CPDF_Object;
+class CPDF_PageImageCache;
 
 class CPDF_Page final : public IPDF_Page, public CPDF_PageObjectHolder {
  public:
-  // Caller implements as desired, empty here due to layering.
-  class View : public Observable {};
+  // Caller implements as desired, exists here due to layering.
+  class View : public Observable {
+   public:
+    virtual void ClearPage(CPDF_Page* pPage) = 0;
+  };
 
   // Data for the render layer to attach to this page.
   class RenderContextIface {
    public:
-    virtual ~RenderContextIface() {}
-  };
-
-  // Cache for the render layer to attach to this page.
-  class RenderCacheIface {
-   public:
-    virtual ~RenderCacheIface() {}
-    virtual void ResetBitmapForImage(const RetainPtr<CPDF_Image>& pImage) = 0;
+    virtual ~RenderContextIface() = default;
   };
 
   class RenderContextClearer {
    public:
+    FX_STACK_ALLOCATED();
     explicit RenderContextClearer(CPDF_Page* pPage);
     ~RenderContextClearer();
 
@@ -51,8 +49,7 @@
     UnownedPtr<CPDF_Page> const m_pPage;
   };
 
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // IPDF_Page:
   CPDF_Page* AsPDFPage() override;
@@ -61,11 +58,11 @@
   float GetPageWidth() const override;
   float GetPageHeight() const override;
   CFX_Matrix GetDisplayMatrix(const FX_RECT& rect, int iRotate) const override;
-  Optional<CFX_PointF> DeviceToPage(
+  absl::optional<CFX_PointF> DeviceToPage(
       const FX_RECT& rect,
       int rotate,
       const CFX_PointF& device_point) const override;
-  Optional<CFX_PointF> PageToDevice(
+  absl::optional<CFX_PointF> PageToDevice(
       const FX_RECT& rect,
       int rotate,
       const CFX_PointF& page_point) const override;
@@ -75,35 +72,39 @@
 
   void ParseContent();
   const CFX_SizeF& GetPageSize() const { return m_PageSize; }
+  const CFX_Matrix& GetPageMatrix() const { return m_PageMatrix; }
   int GetPageRotation() const;
-  RenderCacheIface* GetRenderCache() const { return m_pRenderCache.get(); }
-  void SetRenderCache(std::unique_ptr<RenderCacheIface> pCache) {
-    m_pRenderCache = std::move(pCache);
-  }
 
-  RenderContextIface* GetRenderContext() const {
-    return m_pRenderContext.get();
-  }
-  void SetRenderContext(std::unique_ptr<RenderContextIface> pContext) {
-    m_pRenderContext = std::move(pContext);
-  }
+  RetainPtr<CPDF_Array> GetOrCreateAnnotsArray();
+  RetainPtr<CPDF_Array> GetMutableAnnotsArray();
+  RetainPtr<const CPDF_Array> GetAnnotsArray() const;
 
-  CPDF_Document* GetPDFDocument() const { return m_pPDFDocument.Get(); }
-  View* GetView() const { return m_pView.Get(); }
+  void AddPageImageCache();
+  CPDF_PageImageCache* GetPageImageCache() { return m_pPageImageCache.get(); }
+  RenderContextIface* GetRenderContext() { return m_pRenderContext.get(); }
+
+  // `pContext` cannot be null. `SetRenderContext()` cannot be called if the
+  // page already has a render context. Use `ClearRenderContext()` to reset the
+  // render context.
+  void SetRenderContext(std::unique_ptr<RenderContextIface> pContext);
+  void ClearRenderContext();
+
   void SetView(View* pView) { m_pView.Reset(pView); }
+  void ClearView();
   void UpdateDimensions();
 
  private:
-  CPDF_Page(CPDF_Document* pDocument, CPDF_Dictionary* pPageDict);
+  CPDF_Page(CPDF_Document* pDocument, RetainPtr<CPDF_Dictionary> pPageDict);
   ~CPDF_Page() override;
 
-  CPDF_Object* GetPageAttr(const ByteString& name) const;
+  RetainPtr<CPDF_Object> GetMutablePageAttr(const ByteString& name);
+  RetainPtr<const CPDF_Object> GetPageAttr(const ByteString& name) const;
   CFX_FloatRect GetBox(const ByteString& name) const;
 
   CFX_SizeF m_PageSize;
   CFX_Matrix m_PageMatrix;
-  UnownedPtr<CPDF_Document> m_pPDFDocument;
-  std::unique_ptr<RenderCacheIface> m_pRenderCache;
+  UnownedPtr<CPDF_Document> const m_pPDFDocument;
+  std::unique_ptr<CPDF_PageImageCache> m_pPageImageCache;
   std::unique_ptr<RenderContextIface> m_pRenderContext;
   ObservedPtr<View> m_pView;
 };
diff --git a/core/fpdfapi/page/cpdf_pageimagecache.cpp b/core/fpdfapi/page/cpdf_pageimagecache.cpp
new file mode 100644
index 0000000..cebb8b1
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_pageimagecache.cpp
@@ -0,0 +1,356 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "core/fpdfapi/page/cpdf_dib.h"
+#include "core/fpdfapi/page/cpdf_image.h"
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/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 {
+
+struct CacheInfo {
+  CacheInfo(uint32_t t, RetainPtr<const CPDF_Stream> stream)
+      : time(t), pStream(std::move(stream)) {}
+
+  uint32_t time;
+  RetainPtr<const CPDF_Stream> pStream;
+
+  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) {}
+
+CPDF_PageImageCache::~CPDF_PageImageCache() = default;
+
+void CPDF_PageImageCache::CacheOptimization(int32_t dwLimitCacheSize) {
+  if (m_nCacheSize <= (uint32_t)dwLimitCacheSize)
+    return;
+
+  uint32_t nCount = fxcrt::CollectionSize<uint32_t>(m_ImageCache);
+  std::vector<CacheInfo> cache_info;
+  cache_info.reserve(nCount);
+  for (const auto& it : m_ImageCache) {
+    cache_info.emplace_back(it.second->GetTimeCount(),
+                            it.second->GetImage()->GetStream());
+  }
+  std::sort(cache_info.begin(), cache_info.end());
+
+  // Check if time value is about to roll over and reset all entries.
+  // The comparison is legal because uint32_t is an unsigned type.
+  uint32_t nTimeCount = m_nTimeCount;
+  if (nTimeCount + 1 < nTimeCount) {
+    for (uint32_t i = 0; i < nCount; i++)
+      m_ImageCache[cache_info[i].pStream]->SetTimeCount(i);
+    m_nTimeCount = nCount;
+  }
+
+  size_t i = 0;
+  while (i + 15 < nCount)
+    ClearImageCacheEntry(cache_info[i++].pStream);
+
+  while (i < nCount && m_nCacheSize > (uint32_t)dwLimitCacheSize)
+    ClearImageCacheEntry(cache_info[i++].pStream);
+}
+
+void CPDF_PageImageCache::ClearImageCacheEntry(const CPDF_Stream* pStream) {
+  auto it = m_ImageCache.find(pStream);
+  if (it == m_ImageCache.end())
+    return;
+
+  m_nCacheSize -= it->second->EstimateSize();
+
+  // Avoid leaving `m_pCurImageCacheEntry` as a dangling pointer when `it` is
+  // about to be deleted.
+  if (m_pCurImageCacheEntry.Get() == it->second.get()) {
+    DCHECK(!m_pCurImageCacheEntry.IsOwned());
+    m_pCurImageCacheEntry.Reset();
+  }
+  m_ImageCache.erase(it);
+}
+
+bool CPDF_PageImageCache::StartGetCachedBitmap(
+    RetainPtr<CPDF_Image> pImage,
+    const CPDF_Dictionary* pFormResources,
+    const CPDF_Dictionary* pPageResources,
+    bool bStdCS,
+    CPDF_ColorSpace::Family eFamily,
+    bool bLoadMask,
+    const CFX_Size& max_size_required) {
+  // A cross-document image may have come from the embedder.
+  if (m_pPage->GetDocument() != pImage->GetDocument())
+    return false;
+
+  RetainPtr<const CPDF_Stream> pStream = pImage->GetStream();
+  const auto it = m_ImageCache.find(pStream);
+  m_bCurFindCache = it != m_ImageCache.end();
+  if (m_bCurFindCache) {
+    m_pCurImageCacheEntry = it->second.get();
+  } else {
+    m_pCurImageCacheEntry = std::make_unique<Entry>(std::move(pImage));
+  }
+  CPDF_DIB::LoadState ret = m_pCurImageCacheEntry->StartGetCachedBitmap(
+      this, pFormResources, pPageResources, bStdCS, eFamily, bLoadMask,
+      max_size_required);
+  if (ret == CPDF_DIB::LoadState::kContinue)
+    return true;
+
+  m_nTimeCount++;
+  if (!m_bCurFindCache)
+    m_ImageCache[pStream] = m_pCurImageCacheEntry.Release();
+
+  if (ret == CPDF_DIB::LoadState::kFail)
+    m_nCacheSize += m_pCurImageCacheEntry->EstimateSize();
+
+  return false;
+}
+
+bool CPDF_PageImageCache::Continue(PauseIndicatorIface* pPause) {
+  bool ret = m_pCurImageCacheEntry->Continue(pPause, this);
+  if (ret)
+    return true;
+
+  m_nTimeCount++;
+  if (!m_bCurFindCache) {
+    m_ImageCache[m_pCurImageCacheEntry->GetImage()->GetStream()] =
+        m_pCurImageCacheEntry.Release();
+  }
+  m_nCacheSize += m_pCurImageCacheEntry->EstimateSize();
+  return false;
+}
+
+void CPDF_PageImageCache::ResetBitmapForImage(RetainPtr<CPDF_Image> pImage) {
+  RetainPtr<const CPDF_Stream> pStream = pImage->GetStream();
+  const auto it = m_ImageCache.find(pStream);
+  if (it == m_ImageCache.end())
+    return;
+
+  Entry* pEntry = it->second.get();
+  m_nCacheSize -= pEntry->EstimateSize();
+  pEntry->Reset();
+  m_nCacheSize += pEntry->EstimateSize();
+}
+
+uint32_t CPDF_PageImageCache::GetCurMatteColor() const {
+  return m_pCurImageCacheEntry->GetMatteColor();
+}
+
+RetainPtr<CFX_DIBBase> CPDF_PageImageCache::DetachCurBitmap() {
+  return m_pCurImageCacheEntry->DetachBitmap();
+}
+
+RetainPtr<CFX_DIBBase> CPDF_PageImageCache::DetachCurMask() {
+  return m_pCurImageCacheEntry->DetachMask();
+}
+
+CPDF_PageImageCache::Entry::Entry(RetainPtr<CPDF_Image> pImage)
+    : m_pImage(std::move(pImage)) {}
+
+CPDF_PageImageCache::Entry::~Entry() = default;
+
+void CPDF_PageImageCache::Entry::Reset() {
+  m_pCachedBitmap.Reset();
+  CalcSize();
+}
+
+RetainPtr<CFX_DIBBase> CPDF_PageImageCache::Entry::DetachBitmap() {
+  return std::move(m_pCurBitmap);
+}
+
+RetainPtr<CFX_DIBBase> CPDF_PageImageCache::Entry::DetachMask() {
+  return std::move(m_pCurMask);
+}
+
+CPDF_DIB::LoadState CPDF_PageImageCache::Entry::StartGetCachedBitmap(
+    CPDF_PageImageCache* pPageImageCache,
+    const CPDF_Dictionary* pFormResources,
+    const CPDF_Dictionary* pPageResources,
+    bool bStdCS,
+    CPDF_ColorSpace::Family eFamily,
+    bool bLoadMask,
+    const CFX_Size& max_size_required) {
+  if (m_pCachedBitmap && IsCacheValid(max_size_required)) {
+    m_pCurBitmap = m_pCachedBitmap;
+    m_pCurMask = m_pCachedMask;
+    return CPDF_DIB::LoadState::kSuccess;
+  }
+
+  m_pCurBitmap = m_pImage->CreateNewDIB();
+  CPDF_DIB::LoadState ret = m_pCurBitmap.AsRaw<CPDF_DIB>()->StartLoadDIBBase(
+      true, pFormResources, pPageResources, bStdCS, eFamily, bLoadMask,
+      max_size_required);
+  m_bCachedSetMaxSizeRequired =
+      (max_size_required.width != 0 && max_size_required.height != 0);
+  if (ret == CPDF_DIB::LoadState::kContinue)
+    return CPDF_DIB::LoadState::kContinue;
+
+  if (ret == CPDF_DIB::LoadState::kSuccess)
+    ContinueGetCachedBitmap(pPageImageCache);
+  else
+    m_pCurBitmap.Reset();
+  return CPDF_DIB::LoadState::kFail;
+}
+
+bool CPDF_PageImageCache::Entry::Continue(
+    PauseIndicatorIface* pPause,
+    CPDF_PageImageCache* pPageImageCache) {
+  CPDF_DIB::LoadState ret =
+      m_pCurBitmap.AsRaw<CPDF_DIB>()->ContinueLoadDIBBase(pPause);
+  if (ret == CPDF_DIB::LoadState::kContinue)
+    return true;
+
+  if (ret == CPDF_DIB::LoadState::kSuccess)
+    ContinueGetCachedBitmap(pPageImageCache);
+  else
+    m_pCurBitmap.Reset();
+  return false;
+}
+
+void CPDF_PageImageCache::Entry::ContinueGetCachedBitmap(
+    CPDF_PageImageCache* pPageImageCache) {
+  m_MatteColor = m_pCurBitmap.AsRaw<CPDF_DIB>()->GetMatteColor();
+  m_pCurMask = m_pCurBitmap.AsRaw<CPDF_DIB>()->DetachMask();
+  m_dwTimeCount = pPageImageCache->GetTimeCount();
+  if (m_pCurBitmap->GetPitch() * m_pCurBitmap->GetHeight() < kHugeImageSize) {
+    m_pCachedBitmap = MakeCachedImage(m_pCurBitmap, /*realize_hint=*/true);
+    m_pCurBitmap.Reset();
+  } else {
+    m_pCachedBitmap = MakeCachedImage(m_pCurBitmap, /*realize_hint=*/false);
+  }
+  if (m_pCurMask) {
+    m_pCachedMask = MakeCachedImage(m_pCurMask, /*realize_hint=*/true);
+    m_pCurMask.Reset();
+  }
+  m_pCurBitmap = m_pCachedBitmap;
+  m_pCurMask = m_pCachedMask;
+  CalcSize();
+}
+
+void CPDF_PageImageCache::Entry::CalcSize() {
+  m_dwCacheSize = 0;
+  if (m_pCachedBitmap)
+    m_dwCacheSize += m_pCachedBitmap->GetEstimatedImageMemoryBurden();
+  if (m_pCachedMask)
+    m_dwCacheSize += m_pCachedMask->GetEstimatedImageMemoryBurden();
+}
+
+bool CPDF_PageImageCache::Entry::IsCacheValid(
+    const CFX_Size& max_size_required) const {
+  if (!m_bCachedSetMaxSizeRequired) {
+    return true;
+  }
+  if (max_size_required.width == 0 && max_size_required.height == 0) {
+    return false;
+  }
+
+  return (m_pCachedBitmap->GetWidth() >= max_size_required.width) &&
+         (m_pCachedBitmap->GetHeight() >= max_size_required.height);
+}
diff --git a/core/fpdfapi/page/cpdf_pageimagecache.h b/core/fpdfapi/page/cpdf_pageimagecache.h
new file mode 100644
index 0000000..5184ff6
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_pageimagecache.h
@@ -0,0 +1,107 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFAPI_PAGE_CPDF_PAGEIMAGECACHE_H_
+#define CORE_FPDFAPI_PAGE_CPDF_PAGEIMAGECACHE_H_
+
+#include <stdint.h>
+
+#include <functional>
+#include <map>
+#include <memory>
+
+#include "core/fpdfapi/page/cpdf_dib.h"
+#include "core/fxcrt/maybe_owned.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+
+class CPDF_Dictionary;
+class CPDF_Image;
+class CPDF_Page;
+class CPDF_Stream;
+class PauseIndicatorIface;
+
+class CPDF_PageImageCache {
+ public:
+  explicit CPDF_PageImageCache(CPDF_Page* pPage);
+  ~CPDF_PageImageCache();
+
+  void ResetBitmapForImage(RetainPtr<CPDF_Image> pImage);
+  void CacheOptimization(int32_t dwLimitCacheSize);
+  uint32_t GetTimeCount() const { return m_nTimeCount; }
+  CPDF_Page* GetPage() const { return m_pPage; }
+
+  bool StartGetCachedBitmap(RetainPtr<CPDF_Image> pImage,
+                            const CPDF_Dictionary* pFormResources,
+                            const CPDF_Dictionary* pPageResources,
+                            bool bStdCS,
+                            CPDF_ColorSpace::Family eFamily,
+                            bool bLoadMask,
+                            const CFX_Size& max_size_required);
+
+  bool Continue(PauseIndicatorIface* pPause);
+
+  uint32_t GetCurMatteColor() const;
+  RetainPtr<CFX_DIBBase> DetachCurBitmap();
+  RetainPtr<CFX_DIBBase> DetachCurMask();
+
+ private:
+  class Entry {
+   public:
+    explicit Entry(RetainPtr<CPDF_Image> pImage);
+    ~Entry();
+
+    void Reset();
+    uint32_t EstimateSize() const { return m_dwCacheSize; }
+    uint32_t GetMatteColor() const { return m_MatteColor; }
+    uint32_t GetTimeCount() const { return m_dwTimeCount; }
+    void SetTimeCount(uint32_t count) { m_dwTimeCount = count; }
+    CPDF_Image* GetImage() const { return m_pImage.Get(); }
+
+    CPDF_DIB::LoadState StartGetCachedBitmap(
+        CPDF_PageImageCache* pPageImageCache,
+        const CPDF_Dictionary* pFormResources,
+        const CPDF_Dictionary* pPageResources,
+        bool bStdCS,
+        CPDF_ColorSpace::Family eFamily,
+        bool bLoadMask,
+        const CFX_Size& max_size_required);
+
+    // Returns whether to Continue() or not.
+    bool Continue(PauseIndicatorIface* pPause,
+                  CPDF_PageImageCache* pPageImageCache);
+
+    RetainPtr<CFX_DIBBase> DetachBitmap();
+    RetainPtr<CFX_DIBBase> DetachMask();
+
+   private:
+    void ContinueGetCachedBitmap(CPDF_PageImageCache* pPageImageCache);
+    void CalcSize();
+    bool IsCacheValid(const CFX_Size& max_size_required) const;
+
+    uint32_t m_dwTimeCount = 0;
+    uint32_t m_MatteColor = 0;
+    uint32_t m_dwCacheSize = 0;
+    RetainPtr<CPDF_Image> const m_pImage;
+    RetainPtr<CFX_DIBBase> m_pCurBitmap;
+    RetainPtr<CFX_DIBBase> m_pCurMask;
+    RetainPtr<CFX_DIBBase> m_pCachedBitmap;
+    RetainPtr<CFX_DIBBase> m_pCachedMask;
+    bool m_bCachedSetMaxSizeRequired = false;
+  };
+
+  void ClearImageCacheEntry(const CPDF_Stream* pStream);
+
+  UnownedPtr<CPDF_Page> const m_pPage;
+  std::map<RetainPtr<const CPDF_Stream>, std::unique_ptr<Entry>, std::less<>>
+      m_ImageCache;
+  MaybeOwned<Entry> m_pCurImageCacheEntry;
+  uint32_t m_nTimeCount = 0;
+  uint32_t m_nCacheSize = 0;
+  bool m_bCurFindCache = false;
+};
+
+#endif  // CORE_FPDFAPI_PAGE_CPDF_PAGEIMAGECACHE_H_
diff --git a/core/fpdfapi/page/cpdf_pageimagecache_unittest.cpp b/core/fpdfapi/page/cpdf_pageimagecache_unittest.cpp
new file mode 100644
index 0000000..34e436f
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_pageimagecache_unittest.cpp
@@ -0,0 +1,81 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "core/fpdfapi/page/cpdf_docpagedata.h"
+#include "core/fpdfapi/page/cpdf_image.h"
+#include "core/fpdfapi/page/cpdf_imageobject.h"
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fpdfapi/parser/cpdf_parser.h"
+#include "core/fpdfapi/render/cpdf_docrenderdata.h"
+#include "core/fxcrt/fx_stream.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/utils/path_service.h"
+
+TEST(CPDFPageImageCache, RenderBug1924) {
+  // If you render a page with a JPEG2000 image as a thumbnail (small picture)
+  // first, the image that gets cached has a low resolution. If you afterwards
+  // render it full-size, you should get a larger image - the image cache will
+  // be regenerate.
+
+  CPDF_PageModule::Create();
+  {
+    std::string file_path;
+    ASSERT_TRUE(PathService::GetTestFilePath("jpx_lzw.pdf", &file_path));
+    auto document =
+        std::make_unique<CPDF_Document>(std::make_unique<CPDF_DocRenderData>(),
+                                        std::make_unique<CPDF_DocPageData>());
+    ASSERT_EQ(document->LoadDoc(
+                  IFX_SeekableReadStream::CreateFromFilename(file_path.c_str()),
+                  nullptr),
+              CPDF_Parser::SUCCESS);
+
+    RetainPtr<CPDF_Dictionary> page_dict =
+        document->GetMutablePageDictionary(0);
+    ASSERT_TRUE(page_dict);
+    auto page =
+        pdfium::MakeRetain<CPDF_Page>(document.get(), std::move(page_dict));
+    page->AddPageImageCache();
+    page->ParseContent();
+
+    CPDF_PageImageCache* page_image_cache = page->GetPageImageCache();
+    ASSERT_TRUE(page_image_cache);
+
+    CPDF_PageObject* page_obj = page->GetPageObjectByIndex(0);
+    ASSERT_TRUE(page_obj);
+    CPDF_ImageObject* image = page_obj->AsImage();
+    ASSERT_TRUE(image);
+
+    // Render with small scale.
+    bool should_continue = page_image_cache->StartGetCachedBitmap(
+        image->GetImage(), nullptr, page->GetMutablePageResources(), true,
+        CPDF_ColorSpace::Family::kICCBased, false, {50, 50});
+    while (should_continue)
+      should_continue = page_image_cache->Continue(nullptr);
+
+    RetainPtr<CFX_DIBBase> bitmap_small = page_image_cache->DetachCurBitmap();
+
+    // And render with large scale.
+    should_continue = page_image_cache->StartGetCachedBitmap(
+        image->GetImage(), nullptr, page->GetMutablePageResources(), true,
+        CPDF_ColorSpace::Family::kICCBased, false, {100, 100});
+    while (should_continue)
+      should_continue = page_image_cache->Continue(nullptr);
+
+    RetainPtr<CFX_DIBBase> bitmap_large = page_image_cache->DetachCurBitmap();
+
+    ASSERT_GT(bitmap_large->GetWidth(), bitmap_small->GetWidth());
+    ASSERT_GT(bitmap_large->GetHeight(), bitmap_small->GetHeight());
+
+    ASSERT_TRUE(page->AsPDFPage());
+    page->AsPDFPage()->ClearView();
+  }
+  CPDF_PageModule::Destroy();
+}
diff --git a/core/fpdfapi/page/cpdf_pagemodule.cpp b/core/fpdfapi/page/cpdf_pagemodule.cpp
index 705d313..6cda676 100644
--- a/core/fpdfapi/page/cpdf_pagemodule.cpp
+++ b/core/fpdfapi/page/cpdf_pagemodule.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,7 @@
 #include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fpdfapi/page/cpdf_devicecs.h"
 #include "core/fpdfapi/page/cpdf_patterncs.h"
-#include "core/fxcodec/fx_codec.h"
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -20,30 +20,31 @@
 
 // static
 void CPDF_PageModule::Create() {
-  ASSERT(!g_PageModule);
-  fxcodec::ModuleMgr::Create();
+  DCHECK(!g_PageModule);
   g_PageModule = new CPDF_PageModule();
 }
 
 // static
 void CPDF_PageModule::Destroy() {
-  ASSERT(g_PageModule);
+  DCHECK(g_PageModule);
   delete g_PageModule;
   g_PageModule = nullptr;
-  fxcodec::ModuleMgr::Destroy();
 }
 
 // static
 CPDF_PageModule* CPDF_PageModule::GetInstance() {
-  ASSERT(g_PageModule);
+  DCHECK(g_PageModule);
   return g_PageModule;
 }
 
 CPDF_PageModule::CPDF_PageModule()
-    : m_StockGrayCS(pdfium::MakeRetain<CPDF_DeviceCS>(PDFCS_DEVICEGRAY)),
-      m_StockRGBCS(pdfium::MakeRetain<CPDF_DeviceCS>(PDFCS_DEVICERGB)),
-      m_StockCMYKCS(pdfium::MakeRetain<CPDF_DeviceCS>(PDFCS_DEVICECMYK)),
-      m_StockPatternCS(pdfium::MakeRetain<CPDF_PatternCS>(nullptr)) {
+    : m_StockGrayCS(pdfium::MakeRetain<CPDF_DeviceCS>(
+          CPDF_ColorSpace::Family::kDeviceGray)),
+      m_StockRGBCS(pdfium::MakeRetain<CPDF_DeviceCS>(
+          CPDF_ColorSpace::Family::kDeviceRGB)),
+      m_StockCMYKCS(pdfium::MakeRetain<CPDF_DeviceCS>(
+          CPDF_ColorSpace::Family::kDeviceCMYK)),
+      m_StockPatternCS(pdfium::MakeRetain<CPDF_PatternCS>()) {
   m_StockPatternCS->InitializeStockPattern();
   CPDF_FontGlobals::Create();
   CPDF_FontGlobals::GetInstance()->LoadEmbeddedMaps();
@@ -53,14 +54,15 @@
   CPDF_FontGlobals::Destroy();
 }
 
-RetainPtr<CPDF_ColorSpace> CPDF_PageModule::GetStockCS(int family) {
-  if (family == PDFCS_DEVICEGRAY)
+RetainPtr<CPDF_ColorSpace> CPDF_PageModule::GetStockCS(
+    CPDF_ColorSpace::Family family) {
+  if (family == CPDF_ColorSpace::Family::kDeviceGray)
     return m_StockGrayCS;
-  if (family == PDFCS_DEVICERGB)
+  if (family == CPDF_ColorSpace::Family::kDeviceRGB)
     return m_StockRGBCS;
-  if (family == PDFCS_DEVICECMYK)
+  if (family == CPDF_ColorSpace::Family::kDeviceCMYK)
     return m_StockCMYKCS;
-  if (family == PDFCS_PATTERN)
+  if (family == CPDF_ColorSpace::Family::kPattern)
     return m_StockPatternCS;
   return nullptr;
 }
diff --git a/core/fpdfapi/page/cpdf_pagemodule.h b/core/fpdfapi/page/cpdf_pagemodule.h
index e7234b9..dfd1308 100644
--- a/core/fpdfapi/page/cpdf_pagemodule.h
+++ b/core/fpdfapi/page/cpdf_pagemodule.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,10 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_PAGEMODULE_H_
 #define CORE_FPDFAPI_PAGE_CPDF_PAGEMODULE_H_
 
+#include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Document;
-class CPDF_ColorSpace;
 class CPDF_DeviceCS;
 class CPDF_PatternCS;
 
@@ -21,7 +21,7 @@
   static void Destroy();
   static CPDF_PageModule* GetInstance();
 
-  RetainPtr<CPDF_ColorSpace> GetStockCS(int family);
+  RetainPtr<CPDF_ColorSpace> GetStockCS(CPDF_ColorSpace::Family family);
   void ClearStockFont(CPDF_Document* pDoc);
 
  private:
diff --git a/core/fpdfapi/page/cpdf_pageobject.cpp b/core/fpdfapi/page/cpdf_pageobject.cpp
index bde1704..a7b2156 100644
--- a/core/fpdfapi/page/cpdf_pageobject.cpp
+++ b/core/fpdfapi/page/cpdf_pageobject.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,10 @@
 
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 
+#include <utility>
+
+#include "core/fxcrt/fx_coordinates.h"
+
 CPDF_PageObject::CPDF_PageObject(int32_t content_stream)
     : m_ContentStream(content_stream) {}
 
@@ -71,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 f840214..ccdbbaf 100644
--- a/core/fpdfapi/page/cpdf_pageobject.h
+++ b/core/fpdfapi/page/cpdf_pageobject.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,14 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECT_H_
 #define CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECT_H_
 
+#include <stdint.h>
+
+#include <vector>
+
 #include "core/fpdfapi/page/cpdf_contentmarks.h"
 #include "core/fpdfapi/page/cpdf_graphicstates.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
 
 class CPDF_FormObject;
 class CPDF_ImageObject;
@@ -18,14 +22,18 @@
 class CPDF_ShadingObject;
 class CPDF_TextObject;
 
+// Represents an object within the page, like a form or image. Not to be
+// confused with the PDF spec's page object that lives in a page tree, which is
+// represented by CPDF_Page.
 class CPDF_PageObject : public CPDF_GraphicStates {
  public:
-  enum Type {
-    TEXT = 1,
-    PATH,
-    IMAGE,
-    SHADING,
-    FORM,
+  // Values must match corresponding values in //public.
+  enum class Type {
+    kText = 1,
+    kPath,
+    kImage,
+    kShading,
+    kForm,
   };
 
   static constexpr int32_t kNoContentStream = -1;
@@ -58,11 +66,19 @@
   void TransformClipPath(const CFX_Matrix& matrix);
   void TransformGeneralState(const CFX_Matrix& matrix);
 
+  void SetOriginalRect(const CFX_FloatRect& rect) { m_OriginalRect = rect; }
+  const CFX_FloatRect& GetOriginalRect() const { return m_OriginalRect; }
   void SetRect(const CFX_FloatRect& rect) { m_Rect = rect; }
   const CFX_FloatRect& GetRect() const { return m_Rect; }
   FX_RECT GetBBox() const;
   FX_RECT GetTransformedBBox(const CFX_Matrix& matrix) const;
 
+  CPDF_ContentMarks* GetContentMarks() { return &m_ContentMarks; }
+  const CPDF_ContentMarks* GetContentMarks() const { return &m_ContentMarks; }
+  void SetContentMarks(const CPDF_ContentMarks& marks) {
+    m_ContentMarks = marks;
+  }
+
   // Get what content stream the object was parsed from in its page. This number
   // is the index of the content stream in the "Contents" array, or 0 if there
   // is a single content stream. If the object is newly created,
@@ -75,16 +91,30 @@
     m_ContentStream = new_content_stream;
   }
 
-  CPDF_ContentMarks m_ContentMarks;
+  const ByteString& GetResourceName() const { return m_ResourceName; }
+  void SetResourceName(const ByteString& resource_name) {
+    m_ResourceName = resource_name;
+  }
+
+  const std::vector<ByteString>& GetGraphicsResourceNames() const {
+    return m_GraphicsResourceNames;
+  }
+  void SetGraphicsResourceNames(std::vector<ByteString> resource_names);
 
  protected:
   void CopyData(const CPDF_PageObject* pSrcObject);
 
-  CFX_FloatRect m_Rect;
-
  private:
+  CFX_FloatRect m_Rect;
+  CFX_FloatRect m_OriginalRect;
+  CPDF_ContentMarks m_ContentMarks;
   bool m_bDirty = false;
   int32_t m_ContentStream;
+  // 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_pageobjectholder.cpp b/core/fpdfapi/page/cpdf_pageobjectholder.cpp
index d7307ab..8aa30ca 100644
--- a/core/fpdfapi/page/cpdf_pageobjectholder.cpp
+++ b/core/fpdfapi/page/cpdf_pageobjectholder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,9 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fxcrt/fx_extension.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 
 bool GraphicsData::operator<(const GraphicsData& other) const {
   if (!FXSYS_SafeEQ(fillAlpha, other.fillAlpha))
@@ -32,15 +34,16 @@
   return type < other.type;
 }
 
-CPDF_PageObjectHolder::CPDF_PageObjectHolder(CPDF_Document* pDoc,
-                                             CPDF_Dictionary* pDict,
-                                             CPDF_Dictionary* pPageResources,
-                                             CPDF_Dictionary* pResources)
-    : m_pPageResources(pPageResources),
-      m_pResources(pResources),
-      m_pDict(pDict),
+CPDF_PageObjectHolder::CPDF_PageObjectHolder(
+    CPDF_Document* pDoc,
+    RetainPtr<CPDF_Dictionary> pDict,
+    RetainPtr<CPDF_Dictionary> pPageResources,
+    RetainPtr<CPDF_Dictionary> pResources)
+    : m_pPageResources(std::move(pPageResources)),
+      m_pResources(std::move(pResources)),
+      m_pDict(std::move(pDict)),
       m_pDocument(pDoc) {
-  ASSERT(m_pDict);
+  DCHECK(m_pDict);
 }
 
 CPDF_PageObjectHolder::~CPDF_PageObjectHolder() = default;
@@ -51,7 +54,7 @@
 
 void CPDF_PageObjectHolder::StartParse(
     std::unique_ptr<CPDF_ContentParser> pParser) {
-  ASSERT(m_ParseState == ParseState::kNotParsed);
+  DCHECK_EQ(m_ParseState, ParseState::kNotParsed);
   m_pParser = std::move(pParser);
   m_ParseState = ParseState::kParsing;
 }
@@ -60,7 +63,7 @@
   if (m_ParseState == ParseState::kParsed)
     return;
 
-  ASSERT(m_ParseState == ParseState::kParsing);
+  DCHECK_EQ(m_ParseState, ParseState::kParsing);
   if (m_pParser->Continue(pPause))
     return;
 
@@ -82,12 +85,40 @@
   return dirty_streams;
 }
 
+absl::optional<ByteString> CPDF_PageObjectHolder::GraphicsMapSearch(
+    const GraphicsData& gd) {
+  auto it = m_GraphicsMap.find(gd);
+  if (it == m_GraphicsMap.end())
+    return absl::nullopt;
+
+  return it->second;
+}
+
+void CPDF_PageObjectHolder::GraphicsMapInsert(const GraphicsData& gd,
+                                              const ByteString& str) {
+  m_GraphicsMap[gd] = str;
+}
+
+absl::optional<ByteString> CPDF_PageObjectHolder::FontsMapSearch(
+    const FontData& fd) {
+  auto it = m_FontsMap.find(fd);
+  if (it == m_FontsMap.end())
+    return absl::nullopt;
+
+  return it->second;
+}
+
+void CPDF_PageObjectHolder::FontsMapInsert(const FontData& fd,
+                                           const ByteString& str) {
+  m_FontsMap[fd] = str;
+}
+
 void CPDF_PageObjectHolder::LoadTransparencyInfo() {
-  CPDF_Dictionary* pGroup = m_pDict->GetDictFor("Group");
+  RetainPtr<const CPDF_Dictionary> pGroup = m_pDict->GetDictFor("Group");
   if (!pGroup)
     return;
 
-  if (pGroup->GetStringFor(pdfium::transparency::kGroupSubType) !=
+  if (pGroup->GetByteStringFor(pdfium::transparency::kGroupSubType) !=
       pdfium::transparency::kTransparency) {
     return;
   }
@@ -98,7 +129,7 @@
 
 CPDF_PageObject* CPDF_PageObjectHolder::GetPageObjectByIndex(
     size_t index) const {
-  return pdfium::IndexInBounds(m_PageObjectList, index)
+  return fxcrt::IndexInBounds(m_PageObjectList, index)
              ? m_PageObjectList[index].get()
              : nullptr;
 }
@@ -108,22 +139,21 @@
   m_PageObjectList.push_back(std::move(pPageObj));
 }
 
-bool CPDF_PageObjectHolder::RemovePageObject(CPDF_PageObject* pPageObj) {
-  pdfium::FakeUniquePtr<CPDF_PageObject> p(pPageObj);
-
-  auto it =
-      std::find(std::begin(m_PageObjectList), std::end(m_PageObjectList), p);
+std::unique_ptr<CPDF_PageObject> CPDF_PageObjectHolder::RemovePageObject(
+    CPDF_PageObject* pPageObj) {
+  auto it = std::find(std::begin(m_PageObjectList), std::end(m_PageObjectList),
+                      fxcrt::MakeFakeUniquePtr(pPageObj));
   if (it == std::end(m_PageObjectList))
-    return false;
+    return nullptr;
 
-  it->release();
+  std::unique_ptr<CPDF_PageObject> result = std::move(*it);
   m_PageObjectList.erase(it);
 
   int32_t content_stream = pPageObj->GetContentStream();
   if (content_stream >= 0)
     m_DirtyStreams.insert(content_stream);
 
-  return true;
+  return result;
 }
 
 bool CPDF_PageObjectHolder::ErasePageObjectAtIndex(size_t index) {
diff --git a/core/fpdfapi/page/cpdf_pageobjectholder.h b/core/fpdfapi/page/cpdf_pageobjectholder.h
index b9eff30..629c028 100644
--- a/core/fpdfapi/page/cpdf_pageobjectholder.h
+++ b/core/fpdfapi/page/cpdf_pageobjectholder.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,24 +7,28 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECTHOLDER_H_
 #define CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECTHOLDER_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <deque>
 #include <map>
 #include <memory>
 #include <set>
+#include <utility>
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_transparency.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CPDF_ContentParser;
-class CPDF_Dictionary;
 class CPDF_Document;
 class CPDF_PageObject;
-class CPDF_Stream;
 class PauseIndicatorIface;
 
 // These structs are used to keep track of resources that have already been
@@ -53,9 +57,9 @@
       std::deque<std::unique_ptr<CPDF_PageObject>>::const_iterator;
 
   CPDF_PageObjectHolder(CPDF_Document* pDoc,
-                        CPDF_Dictionary* pDict,
-                        CPDF_Dictionary* pPageResources,
-                        CPDF_Dictionary* pResources);
+                        RetainPtr<CPDF_Dictionary> pDict,
+                        RetainPtr<CPDF_Dictionary> pPageResources,
+                        RetainPtr<CPDF_Dictionary> pResources);
   virtual ~CPDF_PageObjectHolder();
 
   virtual bool IsPage() const;
@@ -64,13 +68,26 @@
   void ContinueParse(PauseIndicatorIface* pPause);
   ParseState GetParseState() const { return m_ParseState; }
 
-  CPDF_Document* GetDocument() const { return m_pDocument.Get(); }
-
-  CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
+  CPDF_Document* GetDocument() const { return m_pDocument; }
+  RetainPtr<const CPDF_Dictionary> GetDict() const { return m_pDict; }
+  RetainPtr<CPDF_Dictionary> GetMutableDict() { return m_pDict; }
+  RetainPtr<const CPDF_Dictionary> GetResources() const { return m_pResources; }
+  RetainPtr<CPDF_Dictionary> GetMutableResources() { return m_pResources; }
+  void SetResources(RetainPtr<CPDF_Dictionary> pDict) {
+    m_pResources = std::move(pDict);
+  }
+  RetainPtr<const CPDF_Dictionary> GetPageResources() const {
+    return m_pPageResources;
+  }
+  RetainPtr<CPDF_Dictionary> GetMutablePageResources() {
+    return m_pPageResources;
+  }
   size_t GetPageObjectCount() const { return m_PageObjectList.size(); }
   CPDF_PageObject* GetPageObjectByIndex(size_t index) const;
   void AppendPageObject(std::unique_ptr<CPDF_PageObject> pPageObj);
-  bool RemovePageObject(CPDF_PageObject* pPageObj);
+
+  // Remove `pPageObj` if present, and transfer ownership to the caller.
+  std::unique_ptr<CPDF_PageObject> RemovePageObject(CPDF_PageObject* pPageObj);
   bool ErasePageObjectAtIndex(size_t index);
 
   iterator begin() { return m_PageObjectList.begin(); }
@@ -96,14 +113,19 @@
   bool HasDirtyStreams() const { return !m_DirtyStreams.empty(); }
   std::set<int32_t> TakeDirtyStreams();
 
-  RetainPtr<CPDF_Dictionary> m_pPageResources;
-  RetainPtr<CPDF_Dictionary> m_pResources;
-  std::map<GraphicsData, ByteString> m_GraphicsMap;
-  std::map<FontData, ByteString> m_FontsMap;
+  absl::optional<ByteString> GraphicsMapSearch(const GraphicsData& gd);
+  void GraphicsMapInsert(const GraphicsData& gd, const ByteString& str);
+
+  absl::optional<ByteString> FontsMapSearch(const FontData& fd);
+  void FontsMapInsert(const FontData& fd, const ByteString& str);
 
  protected:
   void LoadTransparencyInfo();
 
+  RetainPtr<CPDF_Dictionary> m_pPageResources;
+  RetainPtr<CPDF_Dictionary> m_pResources;
+  std::map<GraphicsData, ByteString> m_GraphicsMap;
+  std::map<FontData, ByteString> m_FontsMap;
   CFX_FloatRect m_BBox;
   CPDF_Transparency m_Transparency;
 
diff --git a/core/fpdfapi/page/cpdf_pageobjectholder_unittest.cpp b/core/fpdfapi/page/cpdf_pageobjectholder_unittest.cpp
index 7760256..c599752 100644
--- a/core/fpdfapi/page/cpdf_pageobjectholder_unittest.cpp
+++ b/core/fpdfapi/page/cpdf_pageobjectholder_unittest.cpp
@@ -1,13 +1,22 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/page/cpdf_pageobjectholder.h"
 
-#include <limits>
+#include <math.h>
 
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+#include "core/fxcrt/fx_extension.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+bool SafeCompare(const float& x, const float& y) {
+  return FXSYS_SafeLT(x, y);
+}
+
 // See https://crbug.com/852273
 TEST(CPDFPageObjectHolder, GraphicsDataAsKey) {
   const float fMin = std::numeric_limits<float>::min();
@@ -23,6 +32,14 @@
     }
   }
 
+  // Validate the documented sort order.
+  std::vector<float> data = {fMax, fInf, fNan, fMin};
+  std::sort(data.begin(), data.end(), SafeCompare);
+  EXPECT_EQ(data[0], fMin);
+  EXPECT_EQ(data[1], fMax);
+  EXPECT_EQ(data[2], fInf);
+  EXPECT_EQ(isnan(data[3]), isnan(fNan));
+
   std::map<GraphicsData, int> graphics_map;
 
   // Insert in reverse index permuted order.
diff --git a/core/fpdfapi/page/cpdf_path.cpp b/core/fpdfapi/page/cpdf_path.cpp
index a07bb6a..cec8175 100644
--- a/core/fpdfapi/page/cpdf_path.cpp
+++ b/core/fpdfapi/page/cpdf_path.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,7 @@
 
 CPDF_Path::~CPDF_Path() = default;
 
-const std::vector<FX_PATHPOINT>& CPDF_Path::GetPoints() const {
+const std::vector<CFX_Path::Point>& CPDF_Path::GetPoints() const {
   return m_Ref.GetObject()->GetPoints();
 }
 
@@ -28,9 +28,10 @@
   return m_Ref.GetObject()->GetBoundingBox();
 }
 
-CFX_FloatRect CPDF_Path::GetBoundingBox(float line_width,
-                                        float miter_limit) const {
-  return m_Ref.GetObject()->GetBoundingBox(line_width, miter_limit);
+CFX_FloatRect CPDF_Path::GetBoundingBoxForStrokePath(float line_width,
+                                                     float miter_limit) const {
+  return m_Ref.GetObject()->GetBoundingBoxForStrokePath(line_width,
+                                                        miter_limit);
 }
 
 bool CPDF_Path::IsRect() const {
@@ -41,8 +42,8 @@
   m_Ref.GetPrivateCopy()->Transform(matrix);
 }
 
-void CPDF_Path::Append(const CFX_PathData* pData, const CFX_Matrix* pMatrix) {
-  m_Ref.GetPrivateCopy()->Append(pData, pMatrix);
+void CPDF_Path::Append(const CFX_Path& path, const CFX_Matrix* pMatrix) {
+  m_Ref.GetPrivateCopy()->Append(path, pMatrix);
 }
 
 void CPDF_Path::AppendFloatRect(const CFX_FloatRect& rect) {
@@ -54,9 +55,15 @@
 }
 
 void CPDF_Path::AppendPoint(const CFX_PointF& point,
-                            FXPT_TYPE type,
-                            bool close) {
-  CFX_PathData data;
-  data.AppendPoint(point, type, close);
-  Append(&data, nullptr);
+                            CFX_Path::Point::Type type) {
+  CFX_Path data;
+  data.AppendPoint(point, type);
+  Append(data, nullptr);
+}
+
+void CPDF_Path::AppendPointAndClose(const CFX_PointF& point,
+                                    CFX_Path::Point::Type type) {
+  CFX_Path data;
+  data.AppendPointAndClose(point, type);
+  Append(data, nullptr);
 }
diff --git a/core/fpdfapi/page/cpdf_path.h b/core/fpdfapi/page/cpdf_path.h
index 48f5634..f901f50 100644
--- a/core/fpdfapi/page/cpdf_path.h
+++ b/core/fpdfapi/page/cpdf_path.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,9 +9,8 @@
 
 #include <vector>
 
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/shared_copy_on_write.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 
 class CPDF_Path {
  public:
@@ -22,26 +21,28 @@
   void Emplace() { m_Ref.Emplace(); }
   bool HasRef() const { return !!m_Ref; }
 
-  const std::vector<FX_PATHPOINT>& GetPoints() const;
+  const std::vector<CFX_Path::Point>& GetPoints() const;
   void ClosePath();
 
   CFX_PointF GetPoint(int index) const;
   CFX_FloatRect GetBoundingBox() const;
-  CFX_FloatRect GetBoundingBox(float line_width, float miter_limit) const;
+  CFX_FloatRect GetBoundingBoxForStrokePath(float line_width,
+                                            float miter_limit) const;
 
   bool IsRect() const;
   void Transform(const CFX_Matrix& matrix);
 
-  void Append(const CFX_PathData* pData, const CFX_Matrix* pMatrix);
+  void Append(const CFX_Path& path, const CFX_Matrix* pMatrix);
   void AppendFloatRect(const CFX_FloatRect& rect);
   void AppendRect(float left, float bottom, float right, float top);
-  void AppendPoint(const CFX_PointF& point, FXPT_TYPE type, bool close);
+  void AppendPoint(const CFX_PointF& point, CFX_Path::Point::Type type);
+  void AppendPointAndClose(const CFX_PointF& point, CFX_Path::Point::Type type);
 
   // TODO(tsepez): Remove when all access thru this class.
-  const CFX_PathData* GetObject() const { return m_Ref.GetObject(); }
+  const CFX_Path* GetObject() const { return m_Ref.GetObject(); }
 
  private:
-  SharedCopyOnWrite<CFX_RetainablePathData> m_Ref;
+  SharedCopyOnWrite<CFX_RetainablePath> m_Ref;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_PATH_H_
diff --git a/core/fpdfapi/page/cpdf_pathobject.cpp b/core/fpdfapi/page/cpdf_pathobject.cpp
index eafbe77..b5e7469 100644
--- a/core/fpdfapi/page/cpdf_pathobject.cpp
+++ b/core/fpdfapi/page/cpdf_pathobject.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 CPDF_PathObject::~CPDF_PathObject() = default;
 
 CPDF_PageObject::Type CPDF_PathObject::GetType() const {
-  return PATH;
+  return Type::kPath;
 }
 
 void CPDF_PathObject::Transform(const CFX_Matrix& matrix) {
@@ -41,7 +41,8 @@
   CFX_FloatRect rect;
   float width = m_GraphState.GetLineWidth();
   if (m_bStroke && width != 0) {
-    rect = m_Path.GetBoundingBox(width, m_GraphState.GetMiterLimit());
+    rect =
+        m_Path.GetBoundingBoxForStrokePath(width, m_GraphState.GetMiterLimit());
   } else {
     rect = m_Path.GetBoundingBox();
   }
@@ -51,3 +52,8 @@
     rect.Inflate(0.5f, 0.5f);
   SetRect(rect);
 }
+
+void CPDF_PathObject::SetPathMatrix(const CFX_Matrix& matrix) {
+  m_Matrix = matrix;
+  CalcBoundingBox();
+}
diff --git a/core/fpdfapi/page/cpdf_pathobject.h b/core/fpdfapi/page/cpdf_pathobject.h
index 889d8a6..a0b19cb 100644
--- a/core/fpdfapi/page/cpdf_pathobject.h
+++ b/core/fpdfapi/page/cpdf_pathobject.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,12 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_PATHOBJECT_H_
 #define CORE_FPDFAPI_PAGE_CPDF_PATHOBJECT_H_
 
+#include <stdint.h>
+
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/page/cpdf_path.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxge/render_defines.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
 
 class CPDF_PathObject final : public CPDF_PageObject {
  public:
@@ -31,26 +32,41 @@
   bool stroke() const { return m_bStroke; }
   void set_stroke(bool stroke) { m_bStroke = stroke; }
 
-  // Layering, avoid caller knowledge of FXFILL_ values.
-  bool has_no_filltype() const { return m_FillType == 0; }
-  bool has_winding_filltype() const { return m_FillType == FXFILL_WINDING; }
-  bool has_alternate_filltype() const { return m_FillType == FXFILL_ALTERNATE; }
-  void set_no_filltype() { m_FillType = 0; }
-  void set_winding_filltype() { m_FillType = FXFILL_WINDING; }
-  void set_alternate_filltype() { m_FillType = FXFILL_ALTERNATE; }
+  // Layering, avoid caller knowledge of CFX_FillRenderOptions::FillType values.
+  bool has_no_filltype() const {
+    return m_FillType == CFX_FillRenderOptions::FillType::kNoFill;
+  }
+  bool has_winding_filltype() const {
+    return m_FillType == CFX_FillRenderOptions::FillType::kWinding;
+  }
+  bool has_alternate_filltype() const {
+    return m_FillType == CFX_FillRenderOptions::FillType::kEvenOdd;
+  }
+  void set_no_filltype() {
+    m_FillType = CFX_FillRenderOptions::FillType::kNoFill;
+  }
+  void set_winding_filltype() {
+    m_FillType = CFX_FillRenderOptions::FillType::kWinding;
+  }
+  void set_alternate_filltype() {
+    m_FillType = CFX_FillRenderOptions::FillType::kEvenOdd;
+  }
 
-  int filltype() const { return m_FillType; }
-  void set_filltype(int filltype) { m_FillType = filltype; }
+  CFX_FillRenderOptions::FillType filltype() const { return m_FillType; }
+  void set_filltype(CFX_FillRenderOptions::FillType fill_type) {
+    m_FillType = fill_type;
+  }
 
   CPDF_Path& path() { return m_Path; }
   const CPDF_Path& path() const { return m_Path; }
 
   const CFX_Matrix& matrix() const { return m_Matrix; }
-  void set_matrix(const CFX_Matrix& matrix) { m_Matrix = matrix; }
+  void SetPathMatrix(const CFX_Matrix& matrix);
 
  private:
   bool m_bStroke = false;
-  int m_FillType = 0;
+  CFX_FillRenderOptions::FillType m_FillType =
+      CFX_FillRenderOptions::FillType::kNoFill;
   CPDF_Path m_Path;
   CFX_Matrix m_Matrix;
 };
diff --git a/core/fpdfapi/page/cpdf_pattern.cpp b/core/fpdfapi/page/cpdf_pattern.cpp
index 36ecc98..297c77a 100644
--- a/core/fpdfapi/page/cpdf_pattern.cpp
+++ b/core/fpdfapi/page/cpdf_pattern.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,14 +6,19 @@
 
 #include "core/fpdfapi/page/cpdf_pattern.h"
 
+#include <utility>
+
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "third_party/base/check.h"
 
 CPDF_Pattern::CPDF_Pattern(CPDF_Document* pDoc,
-                           CPDF_Object* pObj,
+                           RetainPtr<CPDF_Object> pObj,
                            const CFX_Matrix& parentMatrix)
-    : m_pDocument(pDoc), m_pPatternObj(pObj), m_ParentMatrix(parentMatrix) {
-  ASSERT(m_pDocument);
-  ASSERT(m_pPatternObj);
+    : m_pDocument(pDoc),
+      m_pPatternObj(std::move(pObj)),
+      m_ParentMatrix(parentMatrix) {
+  DCHECK(m_pDocument);
+  DCHECK(m_pPatternObj);
 }
 
 CPDF_Pattern::~CPDF_Pattern() = default;
@@ -27,6 +32,6 @@
 }
 
 void CPDF_Pattern::SetPatternToFormMatrix() {
-  const CPDF_Dictionary* pDict = pattern_obj()->GetDict();
+  RetainPtr<const CPDF_Dictionary> pDict = pattern_obj()->GetDict();
   m_Pattern2Form = pDict->GetMatrixFor("Matrix") * m_ParentMatrix;
 }
diff --git a/core/fpdfapi/page/cpdf_pattern.h b/core/fpdfapi/page/cpdf_pattern.h
index 7e3eb7b..b773817 100644
--- a/core/fpdfapi/page/cpdf_pattern.h
+++ b/core/fpdfapi/page/cpdf_pattern.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,13 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_PATTERN_H_
 #define CORE_FPDFAPI_PAGE_CPDF_PATTERN_H_
 
+#include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_Document;
-class CPDF_Object;
 class CPDF_ShadingPattern;
 class CPDF_TilingPattern;
 
@@ -23,21 +22,21 @@
   // Values used in PDFs. Do not change.
   enum PatternType { kTiling = 1, kShading = 2 };
 
-  ~CPDF_Pattern() override;
-
   virtual CPDF_TilingPattern* AsTilingPattern();
   virtual CPDF_ShadingPattern* AsShadingPattern();
 
-  // All the getters that return pointers return non-NULL pointers.
-  CPDF_Document* document() const { return m_pDocument.Get(); }
-  CPDF_Object* pattern_obj() const { return m_pPatternObj.Get(); }
   const CFX_Matrix& pattern_to_form() const { return m_Pattern2Form; }
-  const CFX_Matrix& parent_matrix() const { return m_ParentMatrix; }
 
  protected:
   CPDF_Pattern(CPDF_Document* pDoc,
-               CPDF_Object* pObj,
+               RetainPtr<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; }
+  RetainPtr<CPDF_Object> pattern_obj() const { return m_pPatternObj; }
+  const CFX_Matrix& parent_matrix() const { return m_ParentMatrix; }
 
   void SetPatternToFormMatrix();
 
diff --git a/core/fpdfapi/page/cpdf_patterncs.cpp b/core/fpdfapi/page/cpdf_patterncs.cpp
index 1c5dc6c..7abc0b2 100644
--- a/core/fpdfapi/page/cpdf_patterncs.cpp
+++ b/core/fpdfapi/page/cpdf_patterncs.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,9 +9,9 @@
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "third_party/base/notreached.h"
 
-CPDF_PatternCS::CPDF_PatternCS(CPDF_Document* pDoc)
-    : CPDF_ColorSpace(pDoc, PDFCS_PATTERN) {}
+CPDF_PatternCS::CPDF_PatternCS() : CPDF_BasedCS(Family::kPattern) {}
 
 CPDF_PatternCS::~CPDF_PatternCS() = default;
 
@@ -22,16 +22,17 @@
 uint32_t CPDF_PatternCS::v_Load(CPDF_Document* pDoc,
                                 const CPDF_Array* pArray,
                                 std::set<const CPDF_Object*>* pVisited) {
-  const CPDF_Object* pBaseCS = pArray->GetDirectObjectAt(1);
-  if (pBaseCS == m_pArray)
+  RetainPtr<const CPDF_Object> pBaseCS = pArray->GetDirectObjectAt(1);
+  if (HasSameArray(pBaseCS.Get()))
     return 0;
 
   auto* pDocPageData = CPDF_DocPageData::FromDocument(pDoc);
-  m_pBaseCS = pDocPageData->GetColorSpaceGuarded(pBaseCS, nullptr, pVisited);
+  m_pBaseCS =
+      pDocPageData->GetColorSpaceGuarded(pBaseCS.Get(), nullptr, pVisited);
   if (!m_pBaseCS)
     return 1;
 
-  if (m_pBaseCS->GetFamily() == PDFCS_PATTERN)
+  if (m_pBaseCS->GetFamily() == Family::kPattern)
     return 0;
 
   if (m_pBaseCS->CountComponents() > kMaxPatternColorComps)
@@ -40,16 +41,11 @@
   return m_pBaseCS->CountComponents() + 1;
 }
 
-bool CPDF_PatternCS::GetRGB(const float* pBuf,
+bool CPDF_PatternCS::GetRGB(pdfium::span<const float> pBuf,
                             float* R,
                             float* G,
                             float* B) const {
-  NOTREACHED();
-  return false;
-}
-
-CPDF_PatternCS* CPDF_PatternCS::AsPatternCS() {
-  return this;
+  NOTREACHED_NORETURN();
 }
 
 const CPDF_PatternCS* CPDF_PatternCS::AsPatternCS() const {
@@ -60,7 +56,7 @@
                                    float* R,
                                    float* G,
                                    float* B) const {
-  if (m_pBaseCS && m_pBaseCS->GetRGB(value.GetComps().data(), R, G, B))
+  if (m_pBaseCS && m_pBaseCS->GetRGB(value.GetComps(), R, G, B))
     return true;
 
   *R = 0.75f;
diff --git a/core/fpdfapi/page/cpdf_patterncs.h b/core/fpdfapi/page/cpdf_patterncs.h
index b6b46f6..896a95c 100644
--- a/core/fpdfapi/page/cpdf_patterncs.h
+++ b/core/fpdfapi/page/cpdf_patterncs.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,16 +9,14 @@
 
 #include <set>
 
-#include "core/fpdfapi/page/cpdf_colorspace.h"
+#include "core/fpdfapi/page/cpdf_basedcs.h"
 #include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Document;
 
-class CPDF_PatternCS final : public CPDF_ColorSpace {
+class CPDF_PatternCS final : public CPDF_BasedCS {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
+  CONSTRUCT_VIA_MAKE_RETAIN;
   ~CPDF_PatternCS() override;
 
   // Called for the stock pattern, since it is not initialized via
@@ -26,21 +24,22 @@
   void InitializeStockPattern();
 
   // CPDF_ColorSpace:
-  bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override;
-  bool GetPatternRGB(const PatternValue& value,
-                     float* R,
-                     float* G,
-                     float* B) const override;
-  CPDF_PatternCS* AsPatternCS() override;
+  bool GetRGB(pdfium::span<const float> pBuf,
+              float* R,
+              float* G,
+              float* B) const override;
   const CPDF_PatternCS* AsPatternCS() const override;
   uint32_t v_Load(CPDF_Document* pDoc,
                   const CPDF_Array* pArray,
                   std::set<const CPDF_Object*>* pVisited) override;
 
- private:
-  explicit CPDF_PatternCS(CPDF_Document* pDoc);
+  bool GetPatternRGB(const PatternValue& value,
+                     float* R,
+                     float* G,
+                     float* B) const;
 
-  RetainPtr<CPDF_ColorSpace> m_pBaseCS;
+ private:
+  CPDF_PatternCS();
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_PATTERNCS_H_
diff --git a/core/fpdfapi/page/cpdf_psengine.cpp b/core/fpdfapi/page/cpdf_psengine.cpp
index 98f5c47..da7b00d 100644
--- a/core/fpdfapi/page/cpdf_psengine.cpp
+++ b/core/fpdfapi/page/cpdf_psengine.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,21 +6,24 @@
 
 #include "core/fpdfapi/page/cpdf_psengine.h"
 
+#include <math.h>
+
 #include <algorithm>
-#include <cmath>
 #include <limits>
 #include <utility>
 
 #include "core/fpdfapi/parser/cpdf_simple_parser.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/fx_string.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
 struct PDF_PSOpName {
-  const char* name;
+  // Inline string data reduces size for small strings.
+  const char name[9];
   PDF_PSOP op;
 };
 
@@ -72,7 +75,7 @@
 // Round half up is a nearest integer round with half-way numbers always rounded
 // up. Example: -5.5 rounds to -5.
 float RoundHalfUp(float f) {
-  if (std::isnan(f))
+  if (isnan(f))
     return 0;
   if (f > std::numeric_limits<float>::max() - 0.5f)
     return std::numeric_limits<float>::max();
@@ -82,44 +85,47 @@
 }  // namespace
 
 CPDF_PSOP::CPDF_PSOP()
-    : m_op(PSOP_PROC), m_value(0), m_proc(pdfium::MakeUnique<CPDF_PSProc>()) {}
+    : m_op(PSOP_PROC), m_value(0), m_proc(std::make_unique<CPDF_PSProc>()) {}
 
 CPDF_PSOP::CPDF_PSOP(PDF_PSOP op) : m_op(op), m_value(0) {
-  ASSERT(m_op != PSOP_CONST);
-  ASSERT(m_op != PSOP_PROC);
+  DCHECK(m_op != PSOP_CONST);
+  DCHECK(m_op != PSOP_PROC);
 }
 
 CPDF_PSOP::CPDF_PSOP(float value) : m_op(PSOP_CONST), m_value(value) {}
 
-CPDF_PSOP::~CPDF_PSOP() {}
+CPDF_PSOP::~CPDF_PSOP() = default;
+
+bool CPDF_PSOP::Parse(CPDF_SimpleParser* parser, int depth) {
+  CHECK_EQ(m_op, PSOP_PROC);
+  return m_proc->Parse(parser, depth);
+}
+
+void CPDF_PSOP::Execute(CPDF_PSEngine* pEngine) {
+  CHECK_EQ(m_op, PSOP_PROC);
+  m_proc->Execute(pEngine);
+}
 
 float CPDF_PSOP::GetFloatValue() const {
   if (m_op == PSOP_CONST)
     return m_value;
 
-  NOTREACHED();
-  return 0;
-}
-
-CPDF_PSProc* CPDF_PSOP::GetProc() const {
-  if (m_op == PSOP_PROC)
-    return m_proc.get();
-  NOTREACHED();
-  return nullptr;
+  NOTREACHED_NORETURN();
 }
 
 bool CPDF_PSEngine::Execute() {
   return m_MainProc.Execute(this);
 }
 
-CPDF_PSProc::CPDF_PSProc() {}
-CPDF_PSProc::~CPDF_PSProc() {}
+CPDF_PSProc::CPDF_PSProc() = default;
+
+CPDF_PSProc::~CPDF_PSProc() = default;
 
 bool CPDF_PSProc::Parse(CPDF_SimpleParser* parser, int depth) {
   if (depth > kMaxDepth)
     return false;
 
-  while (1) {
+  while (true) {
     ByteStringView word = parser->GetWord();
     if (word.IsEmpty())
       return false;
@@ -128,8 +134,8 @@
       return true;
 
     if (word == "{") {
-      m_Operators.push_back(pdfium::MakeUnique<CPDF_PSOP>());
-      if (!m_Operators.back()->GetProc()->Parse(parser, depth + 1))
+      m_Operators.push_back(std::make_unique<CPDF_PSOP>());
+      if (!m_Operators.back()->Parse(parser, depth + 1))
         return false;
       continue;
     }
@@ -154,14 +160,14 @@
         return false;
 
       if (pEngine->PopInt())
-        m_Operators[i - 1]->GetProc()->Execute(pEngine);
+        m_Operators[i - 1]->Execute(pEngine);
     } else if (op == PSOP_IFELSE) {
       if (i < 2 || m_Operators[i - 1]->GetOp() != PSOP_PROC ||
           m_Operators[i - 2]->GetOp() != PSOP_PROC) {
         return false;
       }
       size_t offset = pEngine->PopInt() ? 2 : 1;
-      m_Operators[i - offset]->GetProc()->Execute(pEngine);
+      m_Operators[i - offset]->Execute(pEngine);
     } else {
       pEngine->DoOperator(op);
     }
@@ -180,9 +186,9 @@
                          return name.name < word;
                        });
   if (pFound != std::end(kPsOpNames) && pFound->name == word)
-    m_Operators.push_back(pdfium::MakeUnique<CPDF_PSOP>(pFound->op));
+    m_Operators.push_back(std::make_unique<CPDF_PSOP>(pFound->op));
   else
-    m_Operators.push_back(pdfium::MakeUnique<CPDF_PSOP>(StringToFloat(word)));
+    m_Operators.push_back(std::make_unique<CPDF_PSOP>(StringToFloat(word)));
 }
 
 CPDF_PSEngine::CPDF_PSEngine() = default;
@@ -232,7 +238,7 @@
     case PSOP_DIV:
       d2 = Pop();
       d1 = Pop();
-      Push(d1 / d2);
+      Push(d2 ? d1 / d2 : 0);
       break;
     case PSOP_IDIV:
       i2 = PopInt();
@@ -286,16 +292,16 @@
       break;
     case PSOP_SIN:
       d1 = Pop();
-      Push(sin(d1 * FX_PI / 180.0f));
+      Push(sin(d1 * FXSYS_PI / 180.0f));
       break;
     case PSOP_COS:
       d1 = Pop();
-      Push(cos(d1 * FX_PI / 180.0f));
+      Push(cos(d1 * FXSYS_PI / 180.0f));
       break;
     case PSOP_ATAN:
       d2 = Pop();
       d1 = Pop();
-      d1 = atan2(d1, d2) * 180.0 / FX_PI;
+      d1 = atan2(d1, d2) * 180.0 / FXSYS_PI;
       if (d1 < 0) {
         d1 += 360;
       }
@@ -304,7 +310,7 @@
     case PSOP_EXP:
       d2 = Pop();
       d1 = Pop();
-      Push(FXSYS_pow(d1, d2));
+      Push(powf(d1, d2));
       break;
     case PSOP_LN:
       d1 = Pop();
diff --git a/core/fpdfapi/page/cpdf_psengine.h b/core/fpdfapi/page/cpdf_psengine.h
index 3db8db7..0b82bc0 100644
--- a/core/fpdfapi/page/cpdf_psengine.h
+++ b/core/fpdfapi/page/cpdf_psengine.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,14 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_PSENGINE_H_
 #define CORE_FPDFAPI_PAGE_CPDF_PSENGINE_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/span.h"
+#include "core/fxcrt/bytestring.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_PSEngine;
 class CPDF_PSProc;
@@ -72,8 +74,9 @@
   explicit CPDF_PSOP(float value);
   ~CPDF_PSOP();
 
+  bool Parse(CPDF_SimpleParser* parser, int depth);
+  void Execute(CPDF_PSEngine* pEngine);
   float GetFloatValue() const;
-  CPDF_PSProc* GetProc() const;
   PDF_PSOP GetOp() const { return m_op; }
 
  private:
@@ -98,7 +101,7 @@
   }
 
  private:
-  static const int kMaxDepth = 128;
+  static constexpr int kMaxDepth = 128;
 
   void AddOperator(ByteStringView word);
 
@@ -124,7 +127,7 @@
 
   uint32_t m_StackCount = 0;
   CPDF_PSProc m_MainProc;
-  float m_Stack[kPSEngineStackSize];
+  float m_Stack[kPSEngineStackSize] = {};
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_PSENGINE_H_
diff --git a/core/fpdfapi/page/cpdf_psengine_unittest.cpp b/core/fpdfapi/page/cpdf_psengine_unittest.cpp
index a922de1..ae458b3 100644
--- a/core/fpdfapi/page/cpdf_psengine_unittest.cpp
+++ b/core/fpdfapi/page/cpdf_psengine_unittest.cpp
@@ -1,14 +1,24 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "core/fpdfapi/page/cpdf_psengine.h"
+
+#include <iterator>
 #include <limits>
 
-#include "core/fpdfapi/page/cpdf_psengine.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
+float DoOperator0(CPDF_PSEngine* engine, PDF_PSOP op) {
+  EXPECT_EQ(0u, engine->GetStackSize());
+  engine->DoOperator(op);
+  float ret = engine->Pop();
+  EXPECT_EQ(0u, engine->GetStackSize());
+  return ret;
+}
+
 float DoOperator1(CPDF_PSEngine* engine, float v1, PDF_PSOP op) {
   EXPECT_EQ(0u, engine->GetStackSize());
   engine->Push(v1);
@@ -62,7 +72,7 @@
 
   CPDF_PSProc proc;
   EXPECT_EQ(0U, proc.num_operators());
-  for (size_t i = 0; i < FX_ArraySize(kTestData); ++i) {
+  for (size_t i = 0; i < std::size(kTestData); ++i) {
     ByteStringView word(kTestData[i].name);
     proc.AddOperatorForTesting(word);
     ASSERT_EQ(i + 1, proc.num_operators());
@@ -93,6 +103,17 @@
   EXPECT_FLOAT_EQ(5.0f, DoOperator1(&engine, -5, PSOP_ABS));
 }
 
+TEST(CPDF_PSEngine, DivByZero) {
+  CPDF_PSEngine engine;
+
+  // Integer divide by zero is defined as resulting in 0.
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 100, 0.0, PSOP_IDIV));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 100, 0.0, PSOP_MOD));
+
+  // floating divide by zero is defined as resulting in 0.
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 100, 0.0, PSOP_DIV));
+}
+
 TEST(CPDF_PSEngine, Ceiling) {
   CPDF_PSEngine engine;
 
@@ -184,10 +205,79 @@
 
   // Truncate does not behave according to the PostScript Language Reference for
   // values beyond the range of integers. This seems to match Acrobat's
-  // behavior. See https://crbug.com/1314.
-  float max_int = std::numeric_limits<int>::max();
-  EXPECT_FLOAT_EQ(-max_int,
-                  DoOperator1(&engine, max_int * 2.0f, PSOP_TRUNCATE));
+  // behavior. See https://crbug.com/pdfium/1314.
+  float max_int = static_cast<float>(std::numeric_limits<int>::max());
   EXPECT_FLOAT_EQ(-max_int,
                   DoOperator1(&engine, max_int * -1.5f, PSOP_TRUNCATE));
 }
+
+TEST(CPDF_PSEngine, Comparisons) {
+  CPDF_PSEngine engine;
+
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_EQ));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_EQ));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 255.0f, 1.0f, PSOP_EQ));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, -1.0f, 0.0f, PSOP_EQ));
+
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_NE));
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_NE));
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 255.0f, 1.0f, PSOP_NE));
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, -1.0f, 0.0f, PSOP_NE));
+
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_GT));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_GT));
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 255.0f, 1.0f, PSOP_GT));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, -1.0f, 0.0f, PSOP_GT));
+
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_GE));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_GE));
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 255.0f, 1.0f, PSOP_GE));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, -1.0f, 0.0f, PSOP_GE));
+
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_LT));
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_LT));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 255.0f, 1.0f, PSOP_LT));
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, -1.0f, 0.0f, PSOP_LT));
+
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_LE));
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_LE));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 255.0f, 1.0f, PSOP_LE));
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, -1.0f, 0.0f, PSOP_LE));
+}
+
+TEST(CPDF_PSEngine, Logic) {
+  CPDF_PSEngine engine;
+
+  EXPECT_FLOAT_EQ(1.0f, DoOperator0(&engine, PSOP_TRUE));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator0(&engine, PSOP_FALSE));
+
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_AND));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_AND));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 1.0f, 0.0f, PSOP_AND));
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 1.0f, 1.0f, PSOP_AND));
+
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_OR));
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_OR));
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 1.0f, 0.0f, PSOP_OR));
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 1.0f, 1.0f, PSOP_OR));
+
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_XOR));
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_XOR));
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 1.0f, 0.0f, PSOP_XOR));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 1.0f, 1.0f, PSOP_XOR));
+
+  EXPECT_FLOAT_EQ(1.0f, DoOperator1(&engine, 0.0f, PSOP_NOT));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, 1.0f, PSOP_NOT));
+}
+
+TEST(CPDF_PSEngine, MathFunctions) {
+  CPDF_PSEngine engine;
+
+  EXPECT_FLOAT_EQ(1.4142135f, DoOperator1(&engine, 2.0f, PSOP_SQRT));
+  EXPECT_FLOAT_EQ(0.8660254f, DoOperator1(&engine, 60.0f, PSOP_SIN));
+  EXPECT_FLOAT_EQ(0.5f, DoOperator1(&engine, 60.0f, PSOP_COS));
+  EXPECT_FLOAT_EQ(45.0f, DoOperator2(&engine, 1.0f, 1.0f, PSOP_ATAN));
+  EXPECT_FLOAT_EQ(1000.0f, DoOperator2(&engine, 10.0f, 3.0f, PSOP_EXP));
+  EXPECT_FLOAT_EQ(3.0f, DoOperator1(&engine, 1000.0f, PSOP_LOG));
+  EXPECT_FLOAT_EQ(2.302585f, DoOperator1(&engine, 10.0f, PSOP_LN));
+}
diff --git a/core/fpdfapi/page/cpdf_psfunc.cpp b/core/fpdfapi/page/cpdf_psfunc.cpp
index ffc5053..60ec52b 100644
--- a/core/fpdfapi/page/cpdf_psfunc.cpp
+++ b/core/fpdfapi/page/cpdf_psfunc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,16 +11,17 @@
 
 CPDF_PSFunc::CPDF_PSFunc() : CPDF_Function(Type::kType4PostScript) {}
 
-CPDF_PSFunc::~CPDF_PSFunc() {}
+CPDF_PSFunc::~CPDF_PSFunc() = default;
 
-bool CPDF_PSFunc::v_Init(const CPDF_Object* pObj,
-                         std::set<const CPDF_Object*>* pVisited) {
-  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pObj->AsStream());
+bool CPDF_PSFunc::v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) {
+  auto pAcc =
+      pdfium::MakeRetain<CPDF_StreamAcc>(pdfium::WrapRetain(pObj->AsStream()));
   pAcc->LoadAllDataFiltered();
   return m_PS.Parse(pAcc->GetSpan());
 }
 
-bool CPDF_PSFunc::v_Call(const float* inputs, float* results) const {
+bool CPDF_PSFunc::v_Call(pdfium::span<const float> inputs,
+                         pdfium::span<float> results) const {
   m_PS.Reset();
   for (uint32_t i = 0; i < m_nInputs; i++)
     m_PS.Push(inputs[i]);
diff --git a/core/fpdfapi/page/cpdf_psfunc.h b/core/fpdfapi/page/cpdf_psfunc.h
index b81c2e7..fd7a7d8 100644
--- a/core/fpdfapi/page/cpdf_psfunc.h
+++ b/core/fpdfapi/page/cpdf_psfunc.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,6 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_PSFUNC_H_
 #define CORE_FPDFAPI_PAGE_CPDF_PSFUNC_H_
 
-#include <set>
-
 #include "core/fpdfapi/page/cpdf_function.h"
 #include "core/fpdfapi/page/cpdf_psengine.h"
 
@@ -19,10 +17,10 @@
   CPDF_PSFunc();
   ~CPDF_PSFunc() override;
 
-  // CPDF_Function
-  bool v_Init(const CPDF_Object* pObj,
-              std::set<const CPDF_Object*>* pVisited) override;
-  bool v_Call(const float* inputs, float* results) const override;
+  // CPDF_Function:
+  bool v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) override;
+  bool v_Call(pdfium::span<const float> inputs,
+              pdfium::span<float> results) const override;
 
  private:
   mutable CPDF_PSEngine m_PS;  // Pre-initialized scratch space for v_Call().
diff --git a/core/fpdfapi/page/cpdf_sampledfunc.cpp b/core/fpdfapi/page/cpdf_sampledfunc.cpp
index c80e16c..1dd9a5b 100644
--- a/core/fpdfapi/page/cpdf_sampledfunc.cpp
+++ b/core/fpdfapi/page/cpdf_sampledfunc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,14 +6,16 @@
 
 #include "core/fpdfapi/page/cpdf_sampledfunc.h"
 
+#include <algorithm>
+#include <utility>
+
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fxcrt/cfx_bitstream.h"
-#include "core/fxcrt/cfx_fixedbufgrow.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/small_buffer.h"
 
 namespace {
 
@@ -38,16 +40,15 @@
 
 CPDF_SampledFunc::CPDF_SampledFunc() : CPDF_Function(Type::kType0Sampled) {}
 
-CPDF_SampledFunc::~CPDF_SampledFunc() {}
+CPDF_SampledFunc::~CPDF_SampledFunc() = default;
 
-bool CPDF_SampledFunc::v_Init(const CPDF_Object* pObj,
-                              std::set<const CPDF_Object*>* pVisited) {
-  const CPDF_Stream* pStream = pObj->AsStream();
+bool CPDF_SampledFunc::v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) {
+  RetainPtr<const CPDF_Stream> pStream(pObj->AsStream());
   if (!pStream)
     return false;
 
-  const CPDF_Dictionary* pDict = pStream->GetDict();
-  const CPDF_Array* pSize = pDict->GetArrayFor("Size");
+  RetainPtr<const CPDF_Dictionary> pDict = pStream->GetDict();
+  RetainPtr<const CPDF_Array> pSize = pDict->GetArrayFor("Size");
   if (!pSize || pSize->IsEmpty())
     return false;
 
@@ -57,7 +58,7 @@
 
   FX_SAFE_UINT32 nTotalSampleBits = m_nBitsPerSample;
   nTotalSampleBits *= m_nOutputs;
-  const CPDF_Array* pEncode = pDict->GetArrayFor("Encode");
+  RetainPtr<const CPDF_Array> pEncode = pDict->GetArrayFor("Encode");
   m_EncodeInfo.resize(m_nInputs);
   for (uint32_t i = 0; i < m_nInputs; i++) {
     int size = pSize->GetIntegerAt(i);
@@ -67,8 +68,8 @@
     m_EncodeInfo[i].sizes = size;
     nTotalSampleBits *= m_EncodeInfo[i].sizes;
     if (pEncode) {
-      m_EncodeInfo[i].encode_min = pEncode->GetNumberAt(i * 2);
-      m_EncodeInfo[i].encode_max = pEncode->GetNumberAt(i * 2 + 1);
+      m_EncodeInfo[i].encode_min = pEncode->GetFloatAt(i * 2);
+      m_EncodeInfo[i].encode_max = pEncode->GetFloatAt(i * 2 + 1);
     } else {
       m_EncodeInfo[i].encode_min = 0;
       m_EncodeInfo[i].encode_max =
@@ -80,17 +81,17 @@
     return false;
 
   m_SampleMax = 0xffffffff >> (32 - m_nBitsPerSample);
-  m_pSampleStream = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
+  m_pSampleStream = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(pStream));
   m_pSampleStream->LoadAllDataFiltered();
   if (nTotalSampleBytes.ValueOrDie() > m_pSampleStream->GetSize())
     return false;
 
-  const CPDF_Array* pDecode = pDict->GetArrayFor("Decode");
+  RetainPtr<const CPDF_Array> pDecode = pDict->GetArrayFor("Decode");
   m_DecodeInfo.resize(m_nOutputs);
   for (uint32_t i = 0; i < m_nOutputs; i++) {
     if (pDecode) {
-      m_DecodeInfo[i].decode_min = pDecode->GetNumberAt(2 * i);
-      m_DecodeInfo[i].decode_max = pDecode->GetNumberAt(2 * i + 1);
+      m_DecodeInfo[i].decode_min = pDecode->GetFloatAt(2 * i);
+      m_DecodeInfo[i].decode_max = pDecode->GetFloatAt(2 * i + 1);
     } else {
       m_DecodeInfo[i].decode_min = m_Ranges[i * 2];
       m_DecodeInfo[i].decode_max = m_Ranges[i * 2 + 1];
@@ -99,12 +100,13 @@
   return true;
 }
 
-bool CPDF_SampledFunc::v_Call(const float* inputs, float* results) const {
+bool CPDF_SampledFunc::v_Call(pdfium::span<const float> inputs,
+                              pdfium::span<float> results) const {
   int pos = 0;
-  CFX_FixedBufGrow<float, 16> encoded_input_buf(m_nInputs);
-  float* encoded_input = encoded_input_buf;
-  CFX_FixedBufGrow<uint32_t, 32> int_buf(m_nInputs * 2);
-  uint32_t* index = int_buf;
+  fxcrt::SmallBuffer<float, 16> encoded_input_buf(m_nInputs);
+  fxcrt::SmallBuffer<uint32_t, 32> int_buf(m_nInputs * 2);
+  float* encoded_input = encoded_input_buf.data();
+  uint32_t* index = int_buf.data();
   uint32_t* blocksize = index + m_nInputs;
   for (uint32_t i = 0; i < m_nInputs; i++) {
     if (i == 0)
@@ -114,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;
@@ -174,7 +176,7 @@
   return true;
 }
 
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
+#if defined(_SKIA_SUPPORT_)
 RetainPtr<CPDF_StreamAcc> CPDF_SampledFunc::GetSampleStream() const {
   return m_pSampleStream;
 }
diff --git a/core/fpdfapi/page/cpdf_sampledfunc.h b/core/fpdfapi/page/cpdf_sampledfunc.h
index 520b692..93c81df 100644
--- a/core/fpdfapi/page/cpdf_sampledfunc.h
+++ b/core/fpdfapi/page/cpdf_sampledfunc.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,6 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_SAMPLEDFUNC_H_
 #define CORE_FPDFAPI_PAGE_CPDF_SAMPLEDFUNC_H_
 
-#include <set>
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_function.h"
@@ -31,25 +30,25 @@
   CPDF_SampledFunc();
   ~CPDF_SampledFunc() override;
 
-  // CPDF_Function
-  bool v_Init(const CPDF_Object* pObj,
-              std::set<const CPDF_Object*>* pVisited) override;
-  bool v_Call(const float* inputs, float* results) const override;
+  // CPDF_Function:
+  bool v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) override;
+  bool v_Call(pdfium::span<const float> inputs,
+              pdfium::span<float> results) const override;
 
   const std::vector<SampleEncodeInfo>& GetEncodeInfo() const {
     return m_EncodeInfo;
   }
   uint32_t GetBitsPerSample() const { return m_nBitsPerSample; }
 
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
+#if defined(_SKIA_SUPPORT_)
   RetainPtr<CPDF_StreamAcc> GetSampleStream() const;
 #endif
 
  private:
   std::vector<SampleEncodeInfo> m_EncodeInfo;
   std::vector<SampleDecodeInfo> m_DecodeInfo;
-  uint32_t m_nBitsPerSample;
-  uint32_t m_SampleMax;
+  uint32_t m_nBitsPerSample = 0;
+  uint32_t m_SampleMax = 0;
   RetainPtr<CPDF_StreamAcc> m_pSampleStream;
 };
 
diff --git a/core/fpdfapi/page/cpdf_shadingobject.cpp b/core/fpdfapi/page/cpdf_shadingobject.cpp
index bdaceaa..06d43aa 100644
--- a/core/fpdfapi/page/cpdf_shadingobject.cpp
+++ b/core/fpdfapi/page/cpdf_shadingobject.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,18 +6,21 @@
 
 #include "core/fpdfapi/page/cpdf_shadingobject.h"
 
+#include <utility>
+
 #include "core/fpdfapi/page/cpdf_shadingpattern.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
 
 CPDF_ShadingObject::CPDF_ShadingObject(int32_t content_stream,
-                                       CPDF_ShadingPattern* pattern,
+                                       RetainPtr<CPDF_ShadingPattern> pattern,
                                        const CFX_Matrix& matrix)
-    : CPDF_PageObject(content_stream), m_pShading(pattern), m_Matrix(matrix) {}
+    : CPDF_PageObject(content_stream),
+      m_pShading(std::move(pattern)),
+      m_Matrix(matrix) {}
 
-CPDF_ShadingObject::~CPDF_ShadingObject() {}
+CPDF_ShadingObject::~CPDF_ShadingObject() = default;
 
 CPDF_PageObject::Type CPDF_ShadingObject::GetType() const {
-  return SHADING;
+  return Type::kShading;
 }
 
 void CPDF_ShadingObject::Transform(const CFX_Matrix& matrix) {
@@ -25,12 +28,11 @@
     m_ClipPath.Transform(matrix);
 
   m_Matrix.Concat(matrix);
-  if (m_ClipPath.HasRef()) {
+  if (m_ClipPath.HasRef())
     CalcBoundingBox();
-    return;
-  }
-
-  SetRect(matrix.TransformRect(GetRect()));
+  else
+    SetRect(matrix.TransformRect(GetRect()));
+  SetDirty(true);
 }
 
 bool CPDF_ShadingObject::IsShading() const {
diff --git a/core/fpdfapi/page/cpdf_shadingobject.h b/core/fpdfapi/page/cpdf_shadingobject.h
index 072a025..78aba1f 100644
--- a/core/fpdfapi/page/cpdf_shadingobject.h
+++ b/core/fpdfapi/page/cpdf_shadingobject.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
 class CPDF_ShadingObject final : public CPDF_PageObject {
  public:
   CPDF_ShadingObject(int32_t content_stream,
-                     CPDF_ShadingPattern* pattern,
+                     RetainPtr<CPDF_ShadingPattern> pattern,
                      const CFX_Matrix& matrix);
   ~CPDF_ShadingObject() override;
 
diff --git a/core/fpdfapi/page/cpdf_shadingpattern.cpp b/core/fpdfapi/page/cpdf_shadingpattern.cpp
index 69696ac..9d26ba9 100644
--- a/core/fpdfapi/page/cpdf_shadingpattern.cpp
+++ b/core/fpdfapi/page/cpdf_shadingpattern.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #include "core/fpdfapi/page/cpdf_shadingpattern.h"
 
 #include <algorithm>
+#include <utility>
 
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
 #include "core/fpdfapi/page/cpdf_function.h"
@@ -16,6 +17,8 @@
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/check.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
@@ -28,11 +31,12 @@
 }  // namespace
 
 CPDF_ShadingPattern::CPDF_ShadingPattern(CPDF_Document* pDoc,
-                                         CPDF_Object* pPatternObj,
+                                         RetainPtr<CPDF_Object> pPatternObj,
                                          bool bShading,
                                          const CFX_Matrix& parentMatrix)
-    : CPDF_Pattern(pDoc, pPatternObj, parentMatrix), m_bShading(bShading) {
-  ASSERT(document());
+    : CPDF_Pattern(pDoc, std::move(pPatternObj), parentMatrix),
+      m_bShading(bShading) {
+  DCHECK(document());
   if (!bShading)
     SetPatternToFormMatrix();
 }
@@ -47,40 +51,43 @@
   if (m_ShadingType != kInvalidShading)
     return true;
 
-  const CPDF_Object* pShadingObj = GetShadingObject();
-  const CPDF_Dictionary* pShadingDict =
+  RetainPtr<const CPDF_Object> pShadingObj = GetShadingObject();
+  RetainPtr<const CPDF_Dictionary> pShadingDict =
       pShadingObj ? pShadingObj->GetDict() : nullptr;
   if (!pShadingDict)
     return false;
 
   m_pFunctions.clear();
-  const CPDF_Object* pFunc = pShadingDict->GetDirectObjectFor("Function");
+  RetainPtr<const CPDF_Object> pFunc =
+      pShadingDict->GetDirectObjectFor("Function");
   if (pFunc) {
     if (const CPDF_Array* pArray = pFunc->AsArray()) {
       m_pFunctions.resize(std::min<size_t>(pArray->size(), 4));
-      for (size_t i = 0; i < m_pFunctions.size(); ++i)
+      for (size_t i = 0; i < m_pFunctions.size(); ++i) {
         m_pFunctions[i] = CPDF_Function::Load(pArray->GetDirectObjectAt(i));
+      }
     } else {
-      m_pFunctions.push_back(CPDF_Function::Load(pFunc));
+      m_pFunctions.push_back(CPDF_Function::Load(std::move(pFunc)));
     }
   }
-  const CPDF_Object* pCSObj = pShadingDict->GetDirectObjectFor("ColorSpace");
+  RetainPtr<const CPDF_Object> pCSObj =
+      pShadingDict->GetDirectObjectFor("ColorSpace");
   if (!pCSObj)
     return false;
 
   auto* pDocPageData = CPDF_DocPageData::FromDocument(document());
-  m_pCS = pDocPageData->GetColorSpace(pCSObj, nullptr);
+  m_pCS = pDocPageData->GetColorSpace(pCSObj.Get(), nullptr);
 
   // The color space is required and cannot be a Pattern space, according to the
   // PDF 1.7 spec, page 305.
-  if (!m_pCS || m_pCS->GetFamily() == PDFCS_PATTERN)
+  if (!m_pCS || m_pCS->GetFamily() == CPDF_ColorSpace::Family::kPattern)
     return false;
 
   m_ShadingType = ToShadingType(pShadingDict->GetIntegerFor("ShadingType"));
   return Validate();
 }
 
-const CPDF_Object* CPDF_ShadingPattern::GetShadingObject() const {
+RetainPtr<const CPDF_Object> CPDF_ShadingPattern::GetShadingObject() const {
   return m_bShading ? pattern_obj()
                     : pattern_obj()->GetDict()->GetDirectObjectFor("Shading");
 }
@@ -98,7 +105,7 @@
     case kFunctionBasedShading:
     case kAxialShading:
     case kRadialShading: {
-      if (m_pCS->GetFamily() == PDFCS_INDEXED)
+      if (m_pCS->GetFamily() == CPDF_ColorSpace::Family::kIndexed)
         return false;
       break;
     }
@@ -106,13 +113,14 @@
     case kLatticeFormGouraudTriangleMeshShading:
     case kCoonsPatchMeshShading:
     case kTensorProductPatchMeshShading: {
-      if (!m_pFunctions.empty() && m_pCS->GetFamily() == PDFCS_INDEXED)
+      if (!m_pFunctions.empty() &&
+          m_pCS->GetFamily() == CPDF_ColorSpace::Family::kIndexed) {
         return false;
+      }
       break;
     }
     default: {
-      NOTREACHED();
-      return false;
+      NOTREACHED_NORETURN();
     }
   }
 
@@ -139,10 +147,8 @@
              ValidateFunctions(nNumColorSpaceComponents, 1, 1);
     }
     default:
-      break;
+      NOTREACHED_NORETURN();
   }
-  NOTREACHED();
-  return false;
 }
 
 bool CPDF_ShadingPattern::ValidateFunctions(
diff --git a/core/fpdfapi/page/cpdf_shadingpattern.h b/core/fpdfapi/page/cpdf_shadingpattern.h
index 392aa27..1e26720 100644
--- a/core/fpdfapi/page/cpdf_shadingpattern.h
+++ b/core/fpdfapi/page/cpdf_shadingpattern.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,14 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_SHADINGPATTERN_H_
 #define CORE_FPDFAPI_PAGE_CPDF_SHADINGPATTERN_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fpdfapi/page/cpdf_pattern.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
 
 // Values used in PDFs except for |kInvalidShading| and |kMaxShading|.
 // Do not change.
@@ -38,9 +38,7 @@
 
 class CPDF_ShadingPattern final : public CPDF_Pattern {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
+  CONSTRUCT_VIA_MAKE_RETAIN;
   ~CPDF_ShadingPattern() override;
 
   // CPDF_Pattern:
@@ -56,7 +54,7 @@
 
   ShadingType GetShadingType() const { return m_ShadingType; }
   bool IsShadingObject() const { return m_bShading; }
-  const CPDF_Object* GetShadingObject() const;
+  RetainPtr<const CPDF_Object> GetShadingObject() const;
   RetainPtr<CPDF_ColorSpace> GetCS() const { return m_pCS; }
   const std::vector<std::unique_ptr<CPDF_Function>>& GetFuncs() const {
     return m_pFunctions;
@@ -64,7 +62,7 @@
 
  private:
   CPDF_ShadingPattern(CPDF_Document* pDoc,
-                      CPDF_Object* pPatternObj,
+                      RetainPtr<CPDF_Object> pPatternObj,
                       bool bShading,
                       const CFX_Matrix& parentMatrix);
   CPDF_ShadingPattern(const CPDF_ShadingPattern&) = delete;
diff --git a/core/fpdfapi/page/cpdf_stitchfunc.cpp b/core/fpdfapi/page/cpdf_stitchfunc.cpp
index 1ec48ac..f522bc3 100644
--- a/core/fpdfapi/page/cpdf_stitchfunc.cpp
+++ b/core/fpdfapi/page/cpdf_stitchfunc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,6 +12,7 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/stl_util.h"
 
 namespace {
 
@@ -21,30 +22,29 @@
 
 CPDF_StitchFunc::CPDF_StitchFunc() : CPDF_Function(Type::kType3Stitching) {}
 
-CPDF_StitchFunc::~CPDF_StitchFunc() {}
+CPDF_StitchFunc::~CPDF_StitchFunc() = default;
 
-bool CPDF_StitchFunc::v_Init(const CPDF_Object* pObj,
-                             std::set<const CPDF_Object*>* pVisited) {
+bool CPDF_StitchFunc::v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) {
   if (m_nInputs != kRequiredNumInputs)
     return false;
 
-  const CPDF_Dictionary* pDict = pObj->GetDict();
+  RetainPtr<const CPDF_Dictionary> pDict = pObj->GetDict();
   if (!pDict)
     return false;
 
-  const CPDF_Array* pFunctionsArray = pDict->GetArrayFor("Functions");
+  RetainPtr<const CPDF_Array> pFunctionsArray = pDict->GetArrayFor("Functions");
   if (!pFunctionsArray)
     return false;
 
-  const CPDF_Array* pBoundsArray = pDict->GetArrayFor("Bounds");
+  RetainPtr<const CPDF_Array> pBoundsArray = pDict->GetArrayFor("Bounds");
   if (!pBoundsArray)
     return false;
 
-  const CPDF_Array* pEncodeArray = pDict->GetArrayFor("Encode");
+  RetainPtr<const CPDF_Array> pEncodeArray = pDict->GetArrayFor("Encode");
   if (!pEncodeArray)
     return false;
 
-  const uint32_t nSubs = pFunctionsArray->size();
+  const uint32_t nSubs = fxcrt::CollectionSize<uint32_t>(*pFunctionsArray);
   if (nSubs == 0)
     return false;
 
@@ -65,13 +65,14 @@
 
   // Check sub-functions.
   {
-    Optional<uint32_t> nOutputs;
+    absl::optional<uint32_t> nOutputs;
     for (uint32_t i = 0; i < nSubs; ++i) {
-      const CPDF_Object* pSub = pFunctionsArray->GetDirectObjectAt(i);
+      RetainPtr<const CPDF_Object> pSub = pFunctionsArray->GetDirectObjectAt(i);
       if (pSub == pObj)
         return false;
 
-      std::unique_ptr<CPDF_Function> pFunc(CPDF_Function::Load(pSub, pVisited));
+      std::unique_ptr<CPDF_Function> pFunc =
+          CPDF_Function::Load(std::move(pSub), pVisited);
       if (!pFunc)
         return false;
 
@@ -84,29 +85,29 @@
       if (nFuncOutputs == 0)
         return false;
 
-      if (nOutputs) {
-        if (nFuncOutputs != *nOutputs)
+      if (nOutputs.has_value()) {
+        if (nOutputs != nFuncOutputs)
           return false;
       } else {
         nOutputs = nFuncOutputs;
       }
-
       m_pSubFunctions.push_back(std::move(pFunc));
     }
-    m_nOutputs = *nOutputs;
+    m_nOutputs = nOutputs.value();
   }
 
   m_bounds.reserve(nSubs + 1);
   m_bounds.push_back(m_Domains[0]);
   for (uint32_t i = 0; i < nSubs - 1; i++)
-    m_bounds.push_back(pBoundsArray->GetNumberAt(i));
+    m_bounds.push_back(pBoundsArray->GetFloatAt(i));
   m_bounds.push_back(m_Domains[1]);
 
-  m_encode = ReadArrayElementsToVector(pEncodeArray, nSubs * 2);
+  m_encode = ReadArrayElementsToVector(pEncodeArray.Get(), nSubs * 2);
   return true;
 }
 
-bool CPDF_StitchFunc::v_Call(const float* inputs, float* results) const {
+bool CPDF_StitchFunc::v_Call(pdfium::span<const float> inputs,
+                             pdfium::span<float> results) const {
   float input = inputs[0];
   size_t i;
   for (i = 0; i < m_pSubFunctions.size() - 1; i++) {
@@ -115,7 +116,7 @@
   }
   input = Interpolate(input, m_bounds[i], m_bounds[i + 1], m_encode[i * 2],
                       m_encode[i * 2 + 1]);
-  int nresults;
-  return m_pSubFunctions[i]->Call(&input, kRequiredNumInputs, results,
-                                  &nresults);
+  return m_pSubFunctions[i]
+      ->Call(pdfium::make_span(&input, 1), results)
+      .has_value();
 }
diff --git a/core/fpdfapi/page/cpdf_stitchfunc.h b/core/fpdfapi/page/cpdf_stitchfunc.h
index 761c9ba..e3490e9 100644
--- a/core/fpdfapi/page/cpdf_stitchfunc.h
+++ b/core/fpdfapi/page/cpdf_stitchfunc.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,6 @@
 #define CORE_FPDFAPI_PAGE_CPDF_STITCHFUNC_H_
 
 #include <memory>
-#include <set>
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_function.h"
@@ -18,10 +17,10 @@
   CPDF_StitchFunc();
   ~CPDF_StitchFunc() override;
 
-  // CPDF_Function
-  bool v_Init(const CPDF_Object* pObj,
-              std::set<const CPDF_Object*>* pVisited) override;
-  bool v_Call(const float* inputs, float* results) const override;
+  // CPDF_Function:
+  bool v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) override;
+  bool v_Call(pdfium::span<const float> inputs,
+              pdfium::span<float> results) const override;
 
   const std::vector<std::unique_ptr<CPDF_Function>>& GetSubFunctions() const {
     return m_pSubFunctions;
diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.cpp b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
index a18bead..cae9d72 100644
--- a/core/fpdfapi/page/cpdf_streamcontentparser.cpp
+++ b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -34,23 +34,31 @@
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fxcrt/autonuller.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/scoped_set_insertion.h"
+#include "core/fxcrt/stl_util.h"
+#include "core/fxge/cfx_graphstate.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/render_defines.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/span.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/containers/span.h"
+#include "third_party/base/no_destructor.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
-const int kMaxFormLevel = 30;
+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';
@@ -60,47 +68,39 @@
 const char kPathOperatorClosePath = 'h';
 const char kPathOperatorRectangle[] = "re";
 
-class CPDF_StreamParserAutoClearer {
- public:
-  CPDF_StreamParserAutoClearer(UnownedPtr<CPDF_StreamParser>* scoped_variable,
-                               CPDF_StreamParser* new_parser)
-      : scoped_variable_(scoped_variable) {
-    *scoped_variable_ = new_parser;
-  }
-  ~CPDF_StreamParserAutoClearer() { *scoped_variable_ = nullptr; }
-
- private:
-  UnownedPtr<CPDF_StreamParser>* scoped_variable_;
-};
-
 CFX_FloatRect GetShadingBBox(CPDF_ShadingPattern* pShading,
                              const CFX_Matrix& matrix) {
   ShadingType type = pShading->GetShadingType();
-  const CPDF_Stream* pStream = ToStream(pShading->GetShadingObject());
+  RetainPtr<const CPDF_Stream> pStream = ToStream(pShading->GetShadingObject());
   RetainPtr<CPDF_ColorSpace> pCS = pShading->GetCS();
   if (!pStream || !pCS)
     return CFX_FloatRect();
 
-  CPDF_MeshStream stream(type, pShading->GetFuncs(), pStream, pCS);
+  CPDF_MeshStream stream(type, pShading->GetFuncs(), std::move(pStream),
+                         std::move(pCS));
   if (!stream.Load())
     return CFX_FloatRect();
 
   CFX_FloatRect rect;
-  bool bStarted = false;
+  bool update_rect = false;
   bool bGouraud = type == kFreeFormGouraudTriangleMeshShading ||
                   type == kLatticeFormGouraudTriangleMeshShading;
 
-  int point_count = kSingleCoordinatePair;
+  int point_count;
   if (type == kTensorProductPatchMeshShading)
     point_count = kTensorCoordinatePairs;
   else if (type == kCoonsPatchMeshShading)
     point_count = kCoonsCoordinatePairs;
+  else
+    point_count = kSingleCoordinatePair;
 
-  int color_count = kSingleColorPerPatch;
+  int color_count;
   if (type == kCoonsPatchMeshShading || type == kTensorProductPatchMeshShading)
     color_count = kQuadColorsPerPatch;
+  else
+    color_count = kSingleColorPerPatch;
 
-  while (!stream.BitStream()->IsEOF()) {
+  while (!stream.IsEOF()) {
     uint32_t flag = 0;
     if (type != kLatticeFormGouraudTriangleMeshShading) {
       if (!stream.CanReadFlag())
@@ -113,15 +113,16 @@
       color_count -= 2;
     }
 
-    for (int i = 0; i < point_count; i++) {
+    for (int i = 0; i < point_count; ++i) {
       if (!stream.CanReadCoords())
         break;
+
       CFX_PointF origin = stream.ReadCoords();
-      if (bStarted) {
+      if (update_rect) {
         rect.UpdateRect(origin);
       } else {
-        rect.InitRect(origin);
-        bStarted = true;
+        rect = CFX_FloatRect(origin);
+        update_rect = true;
       }
     }
     FX_SAFE_UINT32 nBits = stream.Components();
@@ -130,9 +131,9 @@
     if (!nBits.IsValid())
       break;
 
-    stream.BitStream()->SkipBits(nBits.ValueOrDie());
+    stream.SkipBits(nBits.ValueOrDie());
     if (bGouraud)
-      stream.BitStream()->ByteAlign();
+      stream.ByteAlign();
   }
   return matrix.TransformRect(rect);
 }
@@ -163,16 +164,16 @@
   ByteStringView replacement;
 };
 
-ByteStringView FindFullName(const AbbrPair* table,
-                            size_t count,
+ByteStringView FindFullName(pdfium::span<const AbbrPair> table,
                             ByteStringView abbr) {
-  auto* it = std::find_if(table, table + count, [abbr](const AbbrPair& pair) {
-    return pair.abbr == abbr;
-  });
-  return it != table + count ? ByteStringView(it->full_name) : ByteStringView();
+  for (const auto& pair : table) {
+    if (pair.abbr == abbr)
+      return ByteStringView(pair.full_name);
+  }
+  return ByteStringView();
 }
 
-void ReplaceAbbr(CPDF_Object* pObj);
+void ReplaceAbbr(RetainPtr<CPDF_Object> pObj);
 
 void ReplaceAbbrInDictionary(CPDF_Dictionary* pDict) {
   std::vector<AbbrReplacementOp> replacements;
@@ -180,9 +181,8 @@
     CPDF_DictionaryLocker locker(pDict);
     for (const auto& it : locker) {
       ByteString key = it.first;
-      CPDF_Object* value = it.second.Get();
-      ByteStringView fullname = FindFullName(
-          kInlineKeyAbbr, FX_ArraySize(kInlineKeyAbbr), key.AsStringView());
+      ByteStringView fullname =
+          FindFullName(kInlineKeyAbbr, key.AsStringView());
       if (!fullname.IsEmpty()) {
         AbbrReplacementOp op;
         op.is_replace_key = true;
@@ -191,12 +191,10 @@
         replacements.push_back(op);
         key = fullname;
       }
-
+      RetainPtr<CPDF_Object> value = it.second;
       if (value->IsName()) {
         ByteString name = value->GetString();
-        fullname =
-            FindFullName(kInlineValueAbbr, FX_ArraySize(kInlineValueAbbr),
-                         name.AsStringView());
+        fullname = FindFullName(kInlineValueAbbr, name.AsStringView());
         if (!fullname.IsEmpty()) {
           AbbrReplacementOp op;
           op.is_replace_key = false;
@@ -205,7 +203,7 @@
           replacements.push_back(op);
         }
       } else {
-        ReplaceAbbr(value);
+        ReplaceAbbr(std::move(value));
       }
     }
   }
@@ -219,28 +217,27 @@
 
 void ReplaceAbbrInArray(CPDF_Array* pArray) {
   for (size_t i = 0; i < pArray->size(); ++i) {
-    CPDF_Object* pElement = pArray->GetObjectAt(i);
+    RetainPtr<CPDF_Object> pElement = pArray->GetMutableObjectAt(i);
     if (pElement->IsName()) {
       ByteString name = pElement->GetString();
       ByteStringView fullname =
-          FindFullName(kInlineValueAbbr, FX_ArraySize(kInlineValueAbbr),
-                       name.AsStringView());
+          FindFullName(kInlineValueAbbr, name.AsStringView());
       if (!fullname.IsEmpty())
         pArray->SetNewAt<CPDF_Name>(i, ByteString(fullname));
     } else {
-      ReplaceAbbr(pElement);
+      ReplaceAbbr(std::move(pElement));
     }
   }
 }
 
-void ReplaceAbbr(CPDF_Object* pObj) {
-  CPDF_Dictionary* pDict = pObj->AsDictionary();
+void ReplaceAbbr(RetainPtr<CPDF_Object> pObj) {
+  CPDF_Dictionary* pDict = pObj->AsMutableDictionary();
   if (pDict) {
     ReplaceAbbrInDictionary(pDict);
     return;
   }
 
-  CPDF_Array* pArray = pObj->AsArray();
+  CPDF_Array* pArray = pObj->AsMutableArray();
   if (pArray)
     ReplaceAbbrInArray(pArray);
 }
@@ -249,24 +246,24 @@
 
 CPDF_StreamContentParser::CPDF_StreamContentParser(
     CPDF_Document* pDocument,
-    CPDF_Dictionary* pPageResources,
-    CPDF_Dictionary* pParentResources,
+    RetainPtr<CPDF_Dictionary> pPageResources,
+    RetainPtr<CPDF_Dictionary> pParentResources,
     const CFX_Matrix* pmtContentToUser,
     CPDF_PageObjectHolder* pObjHolder,
-    CPDF_Dictionary* pResources,
+    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),
-      m_pResources(CPDF_Form::ChooseResourcesDict(pResources,
-                                                  pParentResources,
-                                                  pPageResources)),
+      m_pResources(CPDF_Form::ChooseResourcesDict(pResources.Get(),
+                                                  pParentResources.Get(),
+                                                  pPageResources.Get())),
       m_pObjectHolder(pObjHolder),
-      m_ParsedSet(pParsedSet),
+      m_RecursionState(recursion_state),
       m_BBox(rcBBox),
-      m_pCurStates(pdfium::MakeUnique<CPDF_AllStates>()) {
+      m_pCurStates(std::make_unique<CPDF_AllStates>()) {
   if (pmtContentToUser)
     m_mtContentToUser = *pmtContentToUser;
   if (pStates) {
@@ -279,7 +276,7 @@
   }
 
   // Add the sentinel.
-  m_ContentMarksStack.push(pdfium::MakeUnique<CPDF_ContentMarks>());
+  m_ContentMarksStack.push(std::make_unique<CPDF_ContentMarks>());
 }
 
 CPDF_StreamContentParser::~CPDF_StreamContentParser() {
@@ -292,7 +289,7 @@
     if (m_ParamStartPos == kParamBufSize) {
       m_ParamStartPos = 0;
     }
-    if (m_ParamBuf[m_ParamStartPos].m_Type == ContentParam::OBJECT)
+    if (m_ParamBuf[m_ParamStartPos].m_Type == ContentParam::Type::kObject)
       m_ParamBuf[m_ParamStartPos].m_pObject.Reset();
 
     return m_ParamStartPos;
@@ -307,27 +304,26 @@
 
 void CPDF_StreamContentParser::AddNameParam(ByteStringView bsName) {
   ContentParam& param = m_ParamBuf[GetNextParamPos()];
-  param.m_Type = ContentParam::NAME;
-  param.m_Name =
-      bsName.Contains('#') ? PDF_NameDecode(bsName) : ByteString(bsName);
+  param.m_Type = ContentParam::Type::kName;
+  param.m_Name = PDF_NameDecode(bsName);
 }
 
 void CPDF_StreamContentParser::AddNumberParam(ByteStringView str) {
   ContentParam& param = m_ParamBuf[GetNextParamPos()];
-  param.m_Type = ContentParam::NUMBER;
+  param.m_Type = ContentParam::Type::kNumber;
   param.m_Number = FX_Number(str);
 }
 
 void CPDF_StreamContentParser::AddObjectParam(RetainPtr<CPDF_Object> pObj) {
   ContentParam& param = m_ParamBuf[GetNextParamPos()];
-  param.m_Type = ContentParam::OBJECT;
+  param.m_Type = ContentParam::Type::kObject;
   param.m_pObject = std::move(pObj);
 }
 
 void CPDF_StreamContentParser::ClearAllParams() {
   uint32_t index = m_ParamStartPos;
   for (uint32_t i = 0; i < m_ParamCount; i++) {
-    if (m_ParamBuf[index].m_Type == ContentParam::OBJECT)
+    if (m_ParamBuf[index].m_Type == ContentParam::Type::kObject)
       m_ParamBuf[index].m_pObject.Reset();
     index++;
     if (index == kParamBufSize)
@@ -337,7 +333,7 @@
   m_ParamCount = 0;
 }
 
-CPDF_Object* CPDF_StreamContentParser::GetObject(uint32_t index) {
+RetainPtr<CPDF_Object> CPDF_StreamContentParser::GetObject(uint32_t index) {
   if (index >= m_ParamCount) {
     return nullptr;
   }
@@ -346,24 +342,23 @@
     real_index -= kParamBufSize;
   }
   ContentParam& param = m_ParamBuf[real_index];
-  if (param.m_Type == ContentParam::NUMBER) {
-    param.m_Type = ContentParam::OBJECT;
+  if (param.m_Type == ContentParam::Type::kNumber) {
+    param.m_Type = ContentParam::Type::kObject;
     param.m_pObject =
         param.m_Number.IsInteger()
             ? pdfium::MakeRetain<CPDF_Number>(param.m_Number.GetSigned())
             : pdfium::MakeRetain<CPDF_Number>(param.m_Number.GetFloat());
-    return param.m_pObject.Get();
+    return param.m_pObject;
   }
-  if (param.m_Type == ContentParam::NAME) {
-    param.m_Type = ContentParam::OBJECT;
+  if (param.m_Type == ContentParam::Type::kName) {
+    param.m_Type = ContentParam::Type::kObject;
     param.m_pObject = m_pDocument->New<CPDF_Name>(param.m_Name);
-    return param.m_pObject.Get();
+    return param.m_pObject;
   }
-  if (param.m_Type == ContentParam::OBJECT)
-    return param.m_pObject.Get();
+  if (param.m_Type == ContentParam::Type::kObject)
+    return param.m_pObject;
 
-  NOTREACHED();
-  return nullptr;
+  NOTREACHED_NORETURN();
 }
 
 ByteString CPDF_StreamContentParser::GetString(uint32_t index) const {
@@ -375,10 +370,10 @@
     real_index -= kParamBufSize;
 
   const ContentParam& param = m_ParamBuf[real_index];
-  if (param.m_Type == ContentParam::NAME)
+  if (param.m_Type == ContentParam::Type::kName)
     return param.m_Name;
 
-  if (param.m_Type == 0 && param.m_pObject)
+  if (param.m_Type == ContentParam::Type::kObject && param.m_pObject)
     return param.m_pObject->GetString();
 
   return ByteString();
@@ -393,10 +388,10 @@
     real_index -= kParamBufSize;
 
   const ContentParam& param = m_ParamBuf[real_index];
-  if (param.m_Type == ContentParam::NUMBER)
+  if (param.m_Type == ContentParam::Type::kNumber)
     return param.m_Number.GetFloat();
 
-  if (param.m_Type == 0 && param.m_pObject)
+  if (param.m_Type == ContentParam::Type::kObject && param.m_pObject)
     return param.m_pObject->GetNumber();
 
   return 0;
@@ -409,13 +404,22 @@
   return values;
 }
 
+CFX_PointF CPDF_StreamContentParser::GetPoint(uint32_t index) const {
+  return CFX_PointF(GetNumber(index + 1), GetNumber(index));
+}
+
+CFX_Matrix CPDF_StreamContentParser::GetMatrix() const {
+  return CFX_Matrix(GetNumber(5), GetNumber(4), GetNumber(3), GetNumber(2),
+                    GetNumber(1), GetNumber(0));
+}
+
 void CPDF_StreamContentParser::SetGraphicStates(CPDF_PageObject* pObj,
                                                 bool bColor,
                                                 bool bText,
                                                 bool bGraph) {
   pObj->m_GeneralState = m_pCurStates->m_GeneralState;
   pObj->m_ClipPath = m_pCurStates->m_ClipPath;
-  pObj->m_ContentMarks = *m_ContentMarksStack.top();
+  pObj->SetContentMarks(*m_ContentMarksStack.top());
   if (bColor) {
     pObj->m_ColorState = m_pCurStates->m_ColorState;
   }
@@ -425,6 +429,7 @@
   if (bText) {
     pObj->m_TextState = m_pCurStates->m_TextState;
   }
+  pObj->SetGraphicsResourceNames(m_pCurStates->m_GraphicsResourceNames);
 }
 
 // static
@@ -549,33 +554,34 @@
 }
 
 void CPDF_StreamContentParser::OnOperator(ByteStringView op) {
-  static const OpCodes s_OpCodes = InitializeOpCodes();
+  static const pdfium::base::NoDestructor<OpCodes> s_OpCodes(
+      InitializeOpCodes());
 
-  auto it = s_OpCodes.find(op.GetID());
-  if (it != s_OpCodes.end())
+  auto it = s_OpCodes->find(op.GetID());
+  if (it != s_OpCodes->end())
     (this->*it->second)();
 }
 
 void CPDF_StreamContentParser::Handle_CloseFillStrokePath() {
   Handle_ClosePath();
-  AddPathObject(FXFILL_WINDING, true);
+  AddPathObject(CFX_FillRenderOptions::FillType::kWinding, RenderType::kStroke);
 }
 
 void CPDF_StreamContentParser::Handle_FillStrokePath() {
-  AddPathObject(FXFILL_WINDING, true);
+  AddPathObject(CFX_FillRenderOptions::FillType::kWinding, RenderType::kStroke);
 }
 
 void CPDF_StreamContentParser::Handle_CloseEOFillStrokePath() {
-  AddPathPoint(m_PathStartX, m_PathStartY, FXPT_TYPE::LineTo, true);
-  AddPathObject(FXFILL_ALTERNATE, true);
+  AddPathPointAndClose(m_PathStart, CFX_Path::Point::Type::kLine);
+  AddPathObject(CFX_FillRenderOptions::FillType::kEvenOdd, RenderType::kStroke);
 }
 
 void CPDF_StreamContentParser::Handle_EOFillStrokePath() {
-  AddPathObject(FXFILL_ALTERNATE, true);
+  AddPathObject(CFX_FillRenderOptions::FillType::kEvenOdd, RenderType::kStroke);
 }
 
 void CPDF_StreamContentParser::Handle_BeginMarkedContent_Dictionary() {
-  CPDF_Object* pProperty = GetObject(0);
+  RetainPtr<CPDF_Object> pProperty = GetObject(0);
   if (!pProperty)
     return;
 
@@ -585,12 +591,13 @@
 
   if (pProperty->IsName()) {
     ByteString property_name = pProperty->GetString();
-    CPDF_Dictionary* pHolder = FindResourceHolder("Properties");
+    RetainPtr<CPDF_Dictionary> pHolder = FindResourceHolder("Properties");
     if (!pHolder || !pHolder->GetDictFor(property_name))
       return;
-    new_marks->AddMarkWithPropertiesHolder(tag, pHolder, property_name);
+    new_marks->AddMarkWithPropertiesHolder(tag, std::move(pHolder),
+                                           property_name);
   } else if (pProperty->IsDictionary()) {
-    new_marks->AddMarkWithDirectDict(tag, pProperty->AsDictionary());
+    new_marks->AddMarkWithDirectDict(tag, ToDictionary(pProperty));
   } else {
     return;
   }
@@ -600,31 +607,28 @@
 void CPDF_StreamContentParser::Handle_BeginImage() {
   FX_FILESIZE savePos = m_pSyntax->GetPos();
   auto pDict = m_pDocument->New<CPDF_Dictionary>();
-  while (1) {
-    CPDF_StreamParser::SyntaxType type = m_pSyntax->ParseNextElement();
-    if (type == CPDF_StreamParser::Keyword) {
+  while (true) {
+    CPDF_StreamParser::ElementType type = m_pSyntax->ParseNextElement();
+    if (type == CPDF_StreamParser::ElementType::kKeyword) {
       if (m_pSyntax->GetWord() != "ID") {
         m_pSyntax->SetPos(savePos);
         return;
       }
     }
-    if (type != CPDF_StreamParser::Name) {
+    if (type != CPDF_StreamParser::ElementType::kName) {
       break;
     }
     auto word = m_pSyntax->GetWord();
     ByteString key(word.Last(word.GetLength() - 1));
     auto pObj = m_pSyntax->ReadNextObject(false, false, 0);
-    if (!key.IsEmpty()) {
-      if (pObj && !pObj->IsInline()) {
-        pDict->SetNewFor<CPDF_Reference>(key, m_pDocument.Get(),
-                                         pObj->GetObjNum());
-      } else {
-        pDict->SetFor(key, std::move(pObj));
-      }
+    if (pObj && !pObj->IsInline()) {
+      pDict->SetNewFor<CPDF_Reference>(key, m_pDocument, pObj->GetObjNum());
+    } else {
+      pDict->SetFor(key, std::move(pObj));
     }
   }
-  ReplaceAbbr(pDict.Get());
-  CPDF_Object* pCSObj = nullptr;
+  ReplaceAbbr(pDict);
+  RetainPtr<const CPDF_Object> pCSObj;
   if (pDict->KeyExist("ColorSpace")) {
     pCSObj = pDict->GetDirectObjectFor("ColorSpace");
     if (pCSObj->IsName()) {
@@ -638,20 +642,20 @@
   }
   pDict->SetNewFor<CPDF_Name>("Subtype", "Image");
   RetainPtr<CPDF_Stream> pStream =
-      m_pSyntax->ReadInlineStream(m_pDocument.Get(), std::move(pDict), pCSObj);
-  while (1) {
-    CPDF_StreamParser::SyntaxType type = m_pSyntax->ParseNextElement();
-    if (type == CPDF_StreamParser::EndOfData) {
+      m_pSyntax->ReadInlineStream(m_pDocument, std::move(pDict), pCSObj.Get());
+  while (true) {
+    CPDF_StreamParser::ElementType type = m_pSyntax->ParseNextElement();
+    if (type == CPDF_StreamParser::ElementType::kEndOfData)
       break;
-    }
-    if (type != CPDF_StreamParser::Keyword) {
+
+    if (type != CPDF_StreamParser::ElementType::kKeyword)
       continue;
-    }
-    if (m_pSyntax->GetWord() == "EI") {
+
+    if (m_pSyntax->GetWord() == "EI")
       break;
-    }
   }
-  CPDF_ImageObject* pObj = AddImage(std::move(pStream));
+  CPDF_ImageObject* pObj =
+      AddImageFromStream(std::move(pStream), /*resource_name=*/"");
   // Record the bounding box of this image, so rendering code can draw it
   // properly.
   if (pObj && pObj->GetImage()->IsMask())
@@ -673,15 +677,13 @@
 }
 
 void CPDF_StreamContentParser::Handle_CurveTo_123() {
-  AddPathPoint(GetNumber(5), GetNumber(4), FXPT_TYPE::BezierTo, false);
-  AddPathPoint(GetNumber(3), GetNumber(2), FXPT_TYPE::BezierTo, false);
-  AddPathPoint(GetNumber(1), GetNumber(0), FXPT_TYPE::BezierTo, false);
+  AddPathPoint(GetPoint(4), CFX_Path::Point::Type::kBezier);
+  AddPathPoint(GetPoint(2), CFX_Path::Point::Type::kBezier);
+  AddPathPoint(GetPoint(0), CFX_Path::Point::Type::kBezier);
 }
 
 void CPDF_StreamContentParser::Handle_ConcatMatrix() {
-  CFX_Matrix new_matrix(GetNumber(5), GetNumber(4), GetNumber(3), GetNumber(2),
-                        GetNumber(1), GetNumber(0));
-  m_pCurStates->m_CTM = new_matrix * m_pCurStates->m_CTM;
+  m_pCurStates->m_CTM = GetMatrix() * m_pCurStates->m_CTM;
   OnChangeTextMatrix();
 }
 
@@ -690,7 +692,8 @@
   if (!pCS)
     return;
 
-  m_pCurStates->m_ColorState.GetMutableFillColor()->SetColorSpace(pCS);
+  m_pCurStates->m_ColorState.GetMutableFillColor()->SetColorSpace(
+      std::move(pCS));
 }
 
 void CPDF_StreamContentParser::Handle_SetColorSpace_Stroke() {
@@ -698,15 +701,16 @@
   if (!pCS)
     return;
 
-  m_pCurStates->m_ColorState.GetMutableStrokeColor()->SetColorSpace(pCS);
+  m_pCurStates->m_ColorState.GetMutableStrokeColor()->SetColorSpace(
+      std::move(pCS));
 }
 
 void CPDF_StreamContentParser::Handle_SetDash() {
-  CPDF_Array* pArray = ToArray(GetObject(1));
+  RetainPtr<CPDF_Array> pArray = ToArray(GetObject(1));
   if (!pArray)
     return;
 
-  m_pCurStates->SetLineDash(pArray, GetNumber(0), 1.0f);
+  m_pCurStates->SetLineDash(pArray.Get(), GetNumber(0), 1.0f);
 }
 
 void CPDF_StreamContentParser::Handle_SetCharWidth() {
@@ -726,7 +730,7 @@
   ByteString name = GetString(0);
   if (name == m_LastImageName && m_pLastImage && m_pLastImage->GetStream() &&
       m_pLastImage->GetStream()->GetObjNum()) {
-    CPDF_ImageObject* pObj = AddImage(m_pLastImage);
+    CPDF_ImageObject* pObj = AddLastImage();
     // Record the bounding box of this image, so rendering code can draw it
     // properly.
     if (pObj && pObj->GetImage()->IsMask())
@@ -734,25 +738,33 @@
     return;
   }
 
-  CPDF_Stream* pXObject = ToStream(FindResourceObj("XObject", name));
-  if (!pXObject) {
-    m_bResourceMissing = true;
+  RetainPtr<CPDF_Stream> pXObject(ToStream(FindResourceObj("XObject", name)));
+  if (!pXObject)
     return;
-  }
 
   ByteString type;
   if (pXObject->GetDict())
-    type = pXObject->GetDict()->GetStringFor("Subtype");
+    type = pXObject->GetDict()->GetByteStringFor("Subtype");
 
   if (type == "Form") {
-    AddForm(pXObject);
+    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;
   }
 
   if (type == "Image") {
-    CPDF_ImageObject* pObj = pXObject->IsInline()
-                                 ? AddImage(ToStream(pXObject->Clone()))
-                                 : AddImage(pXObject->GetObjNum());
+    CPDF_ImageObject* pObj =
+        pXObject->IsInline()
+            ? AddImageFromStream(ToStream(pXObject->Clone()), name)
+            : AddImageFromStreamObjNum(pXObject->GetObjNum(), name);
 
     m_LastImageName = std::move(name);
     if (pObj) {
@@ -763,20 +775,22 @@
   }
 }
 
-void CPDF_StreamContentParser::AddForm(CPDF_Stream* pStream) {
+void CPDF_StreamContentParser::AddForm(RetainPtr<CPDF_Stream> pStream,
+                                       const ByteString& name) {
   CPDF_AllStates status;
   status.m_GeneralState = m_pCurStates->m_GeneralState;
   status.m_GraphState = m_pCurStates->m_GraphState;
   status.m_ColorState = m_pCurStates->m_ColorState;
   status.m_TextState = m_pCurStates->m_TextState;
-  auto form = pdfium::MakeUnique<CPDF_Form>(
-      m_pDocument.Get(), m_pPageResources.Get(), pStream, m_pResources.Get());
-  form->ParseContent(&status, nullptr, m_ParsedSet.Get());
+  auto form = std::make_unique<CPDF_Form>(
+      m_pDocument, m_pPageResources, std::move(pStream), m_pResources.Get());
+  form->ParseContent(&status, nullptr, m_RecursionState);
 
   CFX_Matrix matrix = m_pCurStates->m_CTM * m_mtContentToUser;
-
-  auto pFormObj = pdfium::MakeUnique<CPDF_FormObject>(GetCurrentStreamIndex(),
-                                                      std::move(form), matrix);
+  auto pFormObj = std::make_unique<CPDF_FormObject>(GetCurrentStreamIndex(),
+                                                    std::move(form), matrix);
+  pFormObj->SetResourceName(name);
+  pFormObj->SetGraphicsResourceNames(m_pCurStates->m_GraphicsResourceNames);
   if (!m_pObjectHolder->BackgroundAlphaNeeded() &&
       pFormObj->form()->BackgroundAlphaNeeded()) {
     m_pObjectHolder->SetBackgroundAlphaNeeded(true);
@@ -786,37 +800,38 @@
   m_pObjectHolder->AppendPageObject(std::move(pFormObj));
 }
 
-CPDF_ImageObject* CPDF_StreamContentParser::AddImage(
-    RetainPtr<CPDF_Stream> pStream) {
+CPDF_ImageObject* CPDF_StreamContentParser::AddImageFromStream(
+    RetainPtr<CPDF_Stream> pStream,
+    const ByteString& name) {
   if (!pStream)
     return nullptr;
 
-  auto pImageObj =
-      pdfium::MakeUnique<CPDF_ImageObject>(GetCurrentStreamIndex());
+  auto pImageObj = std::make_unique<CPDF_ImageObject>(GetCurrentStreamIndex());
+  pImageObj->SetResourceName(name);
   pImageObj->SetImage(
-      pdfium::MakeRetain<CPDF_Image>(m_pDocument.Get(), std::move(pStream)));
+      pdfium::MakeRetain<CPDF_Image>(m_pDocument, std::move(pStream)));
 
   return AddImageObject(std::move(pImageObj));
 }
 
-CPDF_ImageObject* CPDF_StreamContentParser::AddImage(uint32_t streamObjNum) {
-  auto pImageObj =
-      pdfium::MakeUnique<CPDF_ImageObject>(GetCurrentStreamIndex());
-  pImageObj->SetImage(CPDF_DocPageData::FromDocument(m_pDocument.Get())
-                          ->GetImage(streamObjNum));
+CPDF_ImageObject* CPDF_StreamContentParser::AddImageFromStreamObjNum(
+    uint32_t stream_obj_num,
+    const ByteString& name) {
+  auto pImageObj = std::make_unique<CPDF_ImageObject>(GetCurrentStreamIndex());
+  pImageObj->SetResourceName(name);
+  pImageObj->SetImage(
+      CPDF_DocPageData::FromDocument(m_pDocument)->GetImage(stream_obj_num));
 
   return AddImageObject(std::move(pImageObj));
 }
 
-CPDF_ImageObject* CPDF_StreamContentParser::AddImage(
-    const RetainPtr<CPDF_Image>& pImage) {
-  if (!pImage)
-    return nullptr;
+CPDF_ImageObject* CPDF_StreamContentParser::AddLastImage() {
+  DCHECK(m_pLastImage);
 
-  auto pImageObj =
-      pdfium::MakeUnique<CPDF_ImageObject>(GetCurrentStreamIndex());
-  pImageObj->SetImage(CPDF_DocPageData::FromDocument(m_pDocument.Get())
-                          ->GetImage(pImage->GetStream()->GetObjNum()));
+  auto pImageObj = std::make_unique<CPDF_ImageObject>(GetCurrentStreamIndex());
+  pImageObj->SetResourceName(m_LastImageName);
+  pImageObj->SetImage(CPDF_DocPageData::FromDocument(m_pDocument)
+                          ->GetImage(m_pLastImage->GetStream()->GetObjNum()));
 
   return AddImageObject(std::move(pImageObj));
 }
@@ -827,8 +842,7 @@
                    false);
 
   CFX_Matrix ImageMatrix = m_pCurStates->m_CTM * m_mtContentToUser;
-  pImageObj->set_matrix(ImageMatrix);
-  pImageObj->CalcBoundingBox();
+  pImageObj->SetImageMatrix(ImageMatrix);
 
   CPDF_ImageObject* pRet = pImageObj.get();
   m_pObjectHolder->AppendPageObject(std::move(pImageObj));
@@ -836,12 +850,12 @@
 }
 
 std::vector<float> CPDF_StreamContentParser::GetColors() const {
-  ASSERT(m_ParamCount > 0);
+  DCHECK(m_ParamCount > 0);
   return GetNumbers(m_ParamCount);
 }
 
 std::vector<float> CPDF_StreamContentParser::GetNamedColors() const {
-  ASSERT(m_ParamCount > 0);
+  DCHECK(m_ParamCount > 0);
   const uint32_t nvalues = m_ParamCount - 1;
   std::vector<float> values(nvalues);
   for (size_t i = 0; i < nvalues; ++i)
@@ -871,47 +885,50 @@
 }
 
 void CPDF_StreamContentParser::Handle_FillPath() {
-  AddPathObject(FXFILL_WINDING, false);
+  AddPathObject(CFX_FillRenderOptions::FillType::kWinding, RenderType::kFill);
 }
 
 void CPDF_StreamContentParser::Handle_FillPathOld() {
-  AddPathObject(FXFILL_WINDING, false);
+  AddPathObject(CFX_FillRenderOptions::FillType::kWinding, RenderType::kFill);
 }
 
 void CPDF_StreamContentParser::Handle_EOFillPath() {
-  AddPathObject(FXFILL_ALTERNATE, false);
+  AddPathObject(CFX_FillRenderOptions::FillType::kEvenOdd, RenderType::kFill);
 }
 
 void CPDF_StreamContentParser::Handle_SetGray_Fill() {
-  RetainPtr<CPDF_ColorSpace> pCS =
-      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY);
-  m_pCurStates->m_ColorState.SetFillColor(pCS, GetNumbers(1));
+  m_pCurStates->m_ColorState.SetFillColor(
+      CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray),
+      GetNumbers(1));
 }
 
 void CPDF_StreamContentParser::Handle_SetGray_Stroke() {
-  RetainPtr<CPDF_ColorSpace> pCS =
-      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY);
-  m_pCurStates->m_ColorState.SetStrokeColor(pCS, GetNumbers(1));
+  m_pCurStates->m_ColorState.SetStrokeColor(
+      CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray),
+      GetNumbers(1));
 }
 
 void CPDF_StreamContentParser::Handle_SetExtendGraphState() {
   ByteString name = GetString(0);
-  CPDF_Dictionary* pGS = ToDictionary(FindResourceObj("ExtGState", name));
-  if (!pGS) {
-    m_bResourceMissing = true;
+  RetainPtr<CPDF_Dictionary> pGS =
+      ToDictionary(FindResourceObj("ExtGState", name));
+  if (!pGS)
     return;
-  }
-  m_pCurStates->ProcessExtGS(pGS, this);
+
+  CHECK(!name.IsEmpty());
+  m_pCurStates->m_GraphicsResourceNames.push_back(std::move(name));
+  m_pCurStates->ProcessExtGS(pGS.Get(), this);
 }
 
 void CPDF_StreamContentParser::Handle_ClosePath() {
   if (m_PathPoints.empty())
     return;
 
-  if (m_PathStartX != m_PathCurrentX || m_PathStartY != m_PathCurrentY)
-    AddPathPoint(m_PathStartX, m_PathStartY, FXPT_TYPE::LineTo, true);
-  else if (m_PathPoints.back().m_Type != FXPT_TYPE::MoveTo)
+  if (m_PathStart.x != m_PathCurrent.x || m_PathStart.y != m_PathCurrent.y) {
+    AddPathPointAndClose(m_PathStart, CFX_Path::Point::Type::kLine);
+  } else {
     m_PathPoints.back().m_CloseFigure = true;
+  }
 }
 
 void CPDF_StreamContentParser::Handle_SetFlat() {
@@ -934,32 +951,32 @@
   if (m_ParamCount != 4)
     return;
 
-  RetainPtr<CPDF_ColorSpace> pCS =
-      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
-  m_pCurStates->m_ColorState.SetFillColor(pCS, GetNumbers(4));
+  m_pCurStates->m_ColorState.SetFillColor(
+      CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceCMYK),
+      GetNumbers(4));
 }
 
 void CPDF_StreamContentParser::Handle_SetCMYKColor_Stroke() {
   if (m_ParamCount != 4)
     return;
 
-  RetainPtr<CPDF_ColorSpace> pCS =
-      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
-  m_pCurStates->m_ColorState.SetStrokeColor(pCS, GetNumbers(4));
+  m_pCurStates->m_ColorState.SetStrokeColor(
+      CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceCMYK),
+      GetNumbers(4));
 }
 
 void CPDF_StreamContentParser::Handle_LineTo() {
   if (m_ParamCount != 2)
     return;
 
-  AddPathPoint(GetNumber(1), GetNumber(0), FXPT_TYPE::LineTo, false);
+  AddPathPoint(GetPoint(0), CFX_Path::Point::Type::kLine);
 }
 
 void CPDF_StreamContentParser::Handle_MoveTo() {
   if (m_ParamCount != 2)
     return;
 
-  AddPathPoint(GetNumber(1), GetNumber(0), FXPT_TYPE::MoveTo, false);
+  AddPathPoint(GetPoint(0), CFX_Path::Point::Type::kMove);
   ParsePathObject();
 }
 
@@ -970,11 +987,11 @@
 void CPDF_StreamContentParser::Handle_MarkPlace() {}
 
 void CPDF_StreamContentParser::Handle_EndPath() {
-  AddPathObject(0, false);
+  AddPathObject(CFX_FillRenderOptions::FillType::kNoFill, RenderType::kFill);
 }
 
 void CPDF_StreamContentParser::Handle_SaveGraphState() {
-  auto pStates = pdfium::MakeUnique<CPDF_AllStates>();
+  auto pStates = std::make_unique<CPDF_AllStates>();
   pStates->Copy(*m_pCurStates);
   m_StateStack.push_back(std::move(pStates));
 }
@@ -988,44 +1005,48 @@
 }
 
 void CPDF_StreamContentParser::Handle_Rectangle() {
-  float x = GetNumber(3), y = GetNumber(2);
-  float w = GetNumber(1), h = GetNumber(0);
+  float x = GetNumber(3);
+  float y = GetNumber(2);
+  float w = GetNumber(1);
+  float h = GetNumber(0);
   AddPathRect(x, y, w, h);
 }
 
 void CPDF_StreamContentParser::AddPathRect(float x, float y, float w, float h) {
-  AddPathPoint(x, y, FXPT_TYPE::MoveTo, false);
-  AddPathPoint(x + w, y, FXPT_TYPE::LineTo, false);
-  AddPathPoint(x + w, y + h, FXPT_TYPE::LineTo, false);
-  AddPathPoint(x, y + h, FXPT_TYPE::LineTo, false);
-  AddPathPoint(x, y, FXPT_TYPE::LineTo, true);
+  AddPathPoint({x, y}, CFX_Path::Point::Type::kMove);
+  AddPathPoint({x + w, y}, CFX_Path::Point::Type::kLine);
+  AddPathPoint({x + w, y + h}, CFX_Path::Point::Type::kLine);
+  AddPathPoint({x, y + h}, CFX_Path::Point::Type::kLine);
+  AddPathPointAndClose({x, y}, CFX_Path::Point::Type::kLine);
 }
 
 void CPDF_StreamContentParser::Handle_SetRGBColor_Fill() {
   if (m_ParamCount != 3)
     return;
 
-  RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
-  m_pCurStates->m_ColorState.SetFillColor(pCS, GetNumbers(3));
+  m_pCurStates->m_ColorState.SetFillColor(
+      CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB),
+      GetNumbers(3));
 }
 
 void CPDF_StreamContentParser::Handle_SetRGBColor_Stroke() {
   if (m_ParamCount != 3)
     return;
 
-  RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
-  m_pCurStates->m_ColorState.SetStrokeColor(pCS, GetNumbers(3));
+  m_pCurStates->m_ColorState.SetStrokeColor(
+      CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB),
+      GetNumbers(3));
 }
 
 void CPDF_StreamContentParser::Handle_SetRenderIntent() {}
 
 void CPDF_StreamContentParser::Handle_CloseStrokePath() {
   Handle_ClosePath();
-  AddPathObject(0, true);
+  AddPathObject(CFX_FillRenderOptions::FillType::kNoFill, RenderType::kStroke);
 }
 
 void CPDF_StreamContentParser::Handle_StrokePath() {
-  AddPathObject(0, true);
+  AddPathObject(CFX_FillRenderOptions::FillType::kNoFill, RenderType::kStroke);
 }
 
 void CPDF_StreamContentParser::Handle_SetColor_Fill() {
@@ -1039,7 +1060,7 @@
 }
 
 void CPDF_StreamContentParser::Handle_SetColorPS_Fill() {
-  CPDF_Object* pLastParam = GetObject(0);
+  RetainPtr<CPDF_Object> pLastParam = GetObject(0);
   if (!pLastParam)
     return;
 
@@ -1050,13 +1071,16 @@
 
   // A valid |pLastParam| implies |m_ParamCount| > 0, so GetNamedColors() call
   // below is safe.
-  RetainPtr<CPDF_Pattern> pPattern = FindPattern(GetString(0), false);
-  if (pPattern)
-    m_pCurStates->m_ColorState.SetFillPattern(pPattern, GetNamedColors());
+  RetainPtr<CPDF_Pattern> pPattern = FindPattern(GetString(0));
+  if (!pPattern)
+    return;
+
+  std::vector<float> values = GetNamedColors();
+  m_pCurStates->m_ColorState.SetFillPattern(std::move(pPattern), values);
 }
 
 void CPDF_StreamContentParser::Handle_SetColorPS_Stroke() {
-  CPDF_Object* pLastParam = GetObject(0);
+  RetainPtr<CPDF_Object> pLastParam = GetObject(0);
   if (!pLastParam)
     return;
 
@@ -1067,17 +1091,16 @@
 
   // A valid |pLastParam| implies |m_ParamCount| > 0, so GetNamedColors() call
   // below is safe.
-  RetainPtr<CPDF_Pattern> pPattern = FindPattern(GetString(0), false);
-  if (pPattern)
-    m_pCurStates->m_ColorState.SetStrokePattern(pPattern, GetNamedColors());
-}
-
-void CPDF_StreamContentParser::Handle_ShadeFill() {
-  RetainPtr<CPDF_Pattern> pPattern = FindPattern(GetString(0), true);
+  RetainPtr<CPDF_Pattern> pPattern = FindPattern(GetString(0));
   if (!pPattern)
     return;
 
-  CPDF_ShadingPattern* pShading = pPattern->AsShadingPattern();
+  std::vector<float> values = GetNamedColors();
+  m_pCurStates->m_ColorState.SetStrokePattern(std::move(pPattern), values);
+}
+
+void CPDF_StreamContentParser::Handle_ShadeFill() {
+  RetainPtr<CPDF_ShadingPattern> pShading = FindShading(GetString(0));
   if (!pShading)
     return;
 
@@ -1085,13 +1108,13 @@
     return;
 
   CFX_Matrix matrix = m_pCurStates->m_CTM * m_mtContentToUser;
-  auto pObj = pdfium::MakeUnique<CPDF_ShadingObject>(GetCurrentStreamIndex(),
-                                                     pShading, matrix);
+  auto pObj = std::make_unique<CPDF_ShadingObject>(GetCurrentStreamIndex(),
+                                                   pShading, matrix);
   SetGraphicStates(pObj.get(), false, false, false);
   CFX_FloatRect bbox =
       pObj->m_ClipPath.HasRef() ? pObj->m_ClipPath.GetClipBox() : m_BBox;
   if (pShading->IsMeshShading())
-    bbox.Intersect(GetShadingBBox(pShading, pObj->matrix()));
+    bbox.Intersect(GetShadingBBox(pShading.Get(), pObj->matrix()));
   pObj->SetRect(bbox);
   m_pObjectHolder->AppendPageObject(std::move(pObj));
 }
@@ -1101,7 +1124,7 @@
 }
 
 void CPDF_StreamContentParser::Handle_MoveTextPoint() {
-  m_pCurStates->m_TextLinePos += CFX_PointF(GetNumber(1), GetNumber(0));
+  m_pCurStates->m_TextLinePos += GetPoint(0);
   m_pCurStates->m_TextPos = m_pCurStates->m_TextLinePos;
 }
 
@@ -1111,52 +1134,51 @@
 }
 
 void CPDF_StreamContentParser::Handle_SetFont() {
-  float fs = GetNumber(0);
-  if (fs == 0) {
-    constexpr float kDefaultFontSize = 0.0f;
-    fs = kDefaultFontSize;
-  }
-
-  m_pCurStates->m_TextState.SetFontSize(fs);
+  m_pCurStates->m_TextState.SetFontSize(GetNumber(0));
   RetainPtr<CPDF_Font> pFont = FindFont(GetString(1));
   if (pFont)
-    m_pCurStates->m_TextState.SetFont(pFont);
+    m_pCurStates->m_TextState.SetFont(std::move(pFont));
 }
 
-CPDF_Dictionary* CPDF_StreamContentParser::FindResourceHolder(
+RetainPtr<CPDF_Dictionary> CPDF_StreamContentParser::FindResourceHolder(
     const ByteString& type) {
   if (!m_pResources)
     return nullptr;
 
-  CPDF_Dictionary* pDict = m_pResources->GetDictFor(type);
+  RetainPtr<CPDF_Dictionary> pDict = m_pResources->GetMutableDictFor(type);
   if (pDict)
     return pDict;
 
   if (m_pResources == m_pPageResources || !m_pPageResources)
     return nullptr;
 
-  return m_pPageResources->GetDictFor(type);
+  return m_pPageResources->GetMutableDictFor(type);
 }
 
-CPDF_Object* CPDF_StreamContentParser::FindResourceObj(const ByteString& type,
-                                                       const ByteString& name) {
-  CPDF_Dictionary* pHolder = FindResourceHolder(type);
-  return pHolder ? pHolder->GetDirectObjectFor(name) : nullptr;
+RetainPtr<CPDF_Object> CPDF_StreamContentParser::FindResourceObj(
+    const ByteString& type,
+    const ByteString& name) {
+  RetainPtr<CPDF_Dictionary> pHolder = FindResourceHolder(type);
+  return pHolder ? pHolder->GetMutableDirectObjectFor(name) : nullptr;
 }
 
 RetainPtr<CPDF_Font> CPDF_StreamContentParser::FindFont(
     const ByteString& name) {
-  CPDF_Dictionary* pFontDict = ToDictionary(FindResourceObj("Font", name));
+  RetainPtr<CPDF_Dictionary> pFontDict(
+      ToDictionary(FindResourceObj("Font", name)));
   if (!pFontDict) {
-    m_bResourceMissing = true;
-    return CPDF_Font::GetStockFont(m_pDocument.Get(),
-                                   CFX_Font::kDefaultAnsiFontName);
+    return CPDF_Font::GetStockFont(m_pDocument, CFX_Font::kDefaultAnsiFontName);
   }
-  RetainPtr<CPDF_Font> pFont =
-      CPDF_DocPageData::FromDocument(m_pDocument.Get())->GetFont(pFontDict);
-  if (pFont && pFont->IsType3Font()) {
-    pFont->AsType3Font()->SetPageResources(m_pResources.Get());
-    pFont->AsType3Font()->CheckType3FontMetrics();
+  RetainPtr<CPDF_Font> pFont = CPDF_DocPageData::FromDocument(m_pDocument)
+                                   ->GetFont(std::move(pFontDict));
+  if (pFont) {
+    // Save `name` for later retrieval by the CPDF_TextObject that uses the
+    // font.
+    pFont->SetResourceName(name);
+    if (pFont->IsType3Font()) {
+      pFont->AsType3Font()->SetPageResources(m_pResources.Get());
+      pFont->AsType3Font()->CheckType3FontMetrics();
+    }
   }
   return pFont;
 }
@@ -1164,44 +1186,49 @@
 RetainPtr<CPDF_ColorSpace> CPDF_StreamContentParser::FindColorSpace(
     const ByteString& name) {
   if (name == "Pattern")
-    return CPDF_ColorSpace::GetStockCS(PDFCS_PATTERN);
+    return CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kPattern);
 
   if (name == "DeviceGray" || name == "DeviceCMYK" || name == "DeviceRGB") {
     ByteString defname = "Default";
     defname += name.Last(name.GetLength() - 7);
-    const CPDF_Object* pDefObj = FindResourceObj("ColorSpace", defname);
+    RetainPtr<const CPDF_Object> pDefObj =
+        FindResourceObj("ColorSpace", defname);
     if (!pDefObj) {
-      if (name == "DeviceGray")
-        return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY);
-
+      if (name == "DeviceGray") {
+        return CPDF_ColorSpace::GetStockCS(
+            CPDF_ColorSpace::Family::kDeviceGray);
+      }
       if (name == "DeviceRGB")
-        return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
+        return CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB);
 
-      return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
+      return CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceCMYK);
     }
-    return CPDF_DocPageData::FromDocument(m_pDocument.Get())
-        ->GetColorSpace(pDefObj, nullptr);
+    return CPDF_DocPageData::FromDocument(m_pDocument)
+        ->GetColorSpace(pDefObj.Get(), nullptr);
   }
-  const CPDF_Object* pCSObj = FindResourceObj("ColorSpace", name);
-  if (!pCSObj) {
-    m_bResourceMissing = true;
+  RetainPtr<const CPDF_Object> pCSObj = FindResourceObj("ColorSpace", name);
+  if (!pCSObj)
     return nullptr;
-  }
-  return CPDF_DocPageData::FromDocument(m_pDocument.Get())
-      ->GetColorSpace(pCSObj, nullptr);
+  return CPDF_DocPageData::FromDocument(m_pDocument)
+      ->GetColorSpace(pCSObj.Get(), nullptr);
 }
 
 RetainPtr<CPDF_Pattern> CPDF_StreamContentParser::FindPattern(
-    const ByteString& name,
-    bool bShading) {
-  CPDF_Object* pPattern =
-      FindResourceObj(bShading ? "Shading" : "Pattern", name);
-  if (!pPattern || (!pPattern->IsDictionary() && !pPattern->IsStream())) {
-    m_bResourceMissing = true;
+    const ByteString& name) {
+  RetainPtr<CPDF_Object> pPattern = FindResourceObj("Pattern", name);
+  if (!pPattern || (!pPattern->IsDictionary() && !pPattern->IsStream()))
     return nullptr;
-  }
-  return CPDF_DocPageData::FromDocument(m_pDocument.Get())
-      ->GetPattern(pPattern, bShading, m_pCurStates->m_ParentMatrix);
+  return CPDF_DocPageData::FromDocument(m_pDocument)
+      ->GetPattern(std::move(pPattern), m_pCurStates->m_ParentMatrix);
+}
+
+RetainPtr<CPDF_ShadingPattern> CPDF_StreamContentParser::FindShading(
+    const ByteString& name) {
+  RetainPtr<CPDF_Object> pPattern = FindResourceObj("Shading", name);
+  if (!pPattern || (!pPattern->IsDictionary() && !pPattern->IsStream()))
+    return nullptr;
+  return CPDF_DocPageData::FromDocument(m_pDocument)
+      ->GetShading(std::move(pPattern), m_pCurStates->m_ParentMatrix);
 }
 
 void CPDF_StreamContentParser::AddTextObject(const ByteString* pStrs,
@@ -1225,11 +1252,11 @@
       pFont->IsType3Font() ? TextRenderingMode::MODE_FILL
                            : m_pCurStates->m_TextState.GetTextMode();
   {
-    auto pText = pdfium::MakeUnique<CPDF_TextObject>(GetCurrentStreamIndex());
-    m_pLastTextObject = pText.get();
-    SetGraphicStates(m_pLastTextObject.Get(), true, true, true);
+    auto pText = std::make_unique<CPDF_TextObject>(GetCurrentStreamIndex());
+    pText->SetResourceName(pFont->GetResourceName());
+    SetGraphicStates(pText.get(), true, true, true);
     if (TextRenderingModeIsStrokeMode(text_mode)) {
-      float* pCTM = pText->m_TextState.GetMutableCTM();
+      pdfium::span<float> pCTM = pText->m_TextState.GetMutableCTM();
       pCTM[0] = m_pCurStates->m_CTM.a;
       pCTM[1] = m_pCurStates->m_CTM.c;
       pCTM[2] = m_pCurStates->m_CTM.b;
@@ -1244,10 +1271,8 @@
 
     m_pCurStates->m_TextPos +=
         pText->CalcPositionData(m_pCurStates->m_TextHorzScale);
-    if (TextRenderingModeIsClipMode(text_mode)) {
-      m_ClipTextList.push_back(
-          std::unique_ptr<CPDF_TextObject>(pText->Clone()));
-    }
+    if (TextRenderingModeIsClipMode(text_mode))
+      m_ClipTextList.push_back(pText->Clone());
     m_pObjectHolder->AppendPageObject(std::move(pText));
   }
   if (!kernings.empty() && kernings[nSegs - 1] != 0) {
@@ -1280,20 +1305,20 @@
 }
 
 void CPDF_StreamContentParser::Handle_ShowText_Positioning() {
-  CPDF_Array* pArray = ToArray(GetObject(0));
+  RetainPtr<CPDF_Array> pArray = ToArray(GetObject(0));
   if (!pArray)
     return;
 
   size_t n = pArray->size();
   size_t nsegs = 0;
   for (size_t i = 0; i < n; i++) {
-    const CPDF_Object* pDirectObject = pArray->GetDirectObjectAt(i);
+    RetainPtr<const CPDF_Object> pDirectObject = pArray->GetDirectObjectAt(i);
     if (pDirectObject && pDirectObject->IsString())
       nsegs++;
   }
   if (nsegs == 0) {
     for (size_t i = 0; i < n; i++) {
-      float fKerning = pArray->GetNumberAt(i);
+      float fKerning = pArray->GetFloatAt(i);
       if (fKerning != 0)
         m_pCurStates->m_TextPos.x -= GetHorizontalTextSize(fKerning);
     }
@@ -1304,7 +1329,7 @@
   size_t iSegment = 0;
   float fInitKerning = 0;
   for (size_t i = 0; i < n; i++) {
-    CPDF_Object* pObj = pArray->GetDirectObjectAt(i);
+    RetainPtr<const CPDF_Object> pObj = pArray->GetDirectObjectAt(i);
     if (!pObj)
       continue;
 
@@ -1330,9 +1355,7 @@
 }
 
 void CPDF_StreamContentParser::Handle_SetTextMatrix() {
-  m_pCurStates->m_TextMatrix =
-      CFX_Matrix(GetNumber(5), GetNumber(4), GetNumber(3), GetNumber(2),
-                 GetNumber(1), GetNumber(0));
+  m_pCurStates->m_TextMatrix = GetMatrix();
   OnChangeTextMatrix();
   m_pCurStates->m_TextPos = CFX_PointF();
   m_pCurStates->m_TextLinePos = CFX_PointF();
@@ -1344,7 +1367,8 @@
   text_matrix.Concat(m_pCurStates->m_TextMatrix);
   text_matrix.Concat(m_pCurStates->m_CTM);
   text_matrix.Concat(m_mtContentToUser);
-  float* pTextMatrix = m_pCurStates->m_TextState.GetMutableMatrix();
+  pdfium::span<float> pTextMatrix =
+      m_pCurStates->m_TextState.GetMutableMatrix();
   pTextMatrix[0] = text_matrix.a;
   pTextMatrix[1] = text_matrix.c;
   pTextMatrix[2] = text_matrix.b;
@@ -1379,9 +1403,9 @@
 }
 
 void CPDF_StreamContentParser::Handle_CurveTo_23() {
-  AddPathPoint(m_PathCurrentX, m_PathCurrentY, FXPT_TYPE::BezierTo, false);
-  AddPathPoint(GetNumber(3), GetNumber(2), FXPT_TYPE::BezierTo, false);
-  AddPathPoint(GetNumber(1), GetNumber(0), FXPT_TYPE::BezierTo, false);
+  AddPathPoint(m_PathCurrent, CFX_Path::Point::Type::kBezier);
+  AddPathPoint(GetPoint(2), CFX_Path::Point::Type::kBezier);
+  AddPathPoint(GetPoint(0), CFX_Path::Point::Type::kBezier);
 }
 
 void CPDF_StreamContentParser::Handle_SetLineWidth() {
@@ -1389,17 +1413,17 @@
 }
 
 void CPDF_StreamContentParser::Handle_Clip() {
-  m_PathClipType = FXFILL_WINDING;
+  m_PathClipType = CFX_FillRenderOptions::FillType::kWinding;
 }
 
 void CPDF_StreamContentParser::Handle_EOClip() {
-  m_PathClipType = FXFILL_ALTERNATE;
+  m_PathClipType = CFX_FillRenderOptions::FillType::kEvenOdd;
 }
 
 void CPDF_StreamContentParser::Handle_CurveTo_13() {
-  AddPathPoint(GetNumber(3), GetNumber(2), FXPT_TYPE::BezierTo, false);
-  AddPathPoint(GetNumber(1), GetNumber(0), FXPT_TYPE::BezierTo, false);
-  AddPathPoint(GetNumber(1), GetNumber(0), FXPT_TYPE::BezierTo, false);
+  AddPathPoint(GetPoint(2), CFX_Path::Point::Type::kBezier);
+  AddPathPoint(GetPoint(0), CFX_Path::Point::Type::kBezier);
+  AddPathPoint(GetPoint(0), CFX_Path::Point::Type::kBezier);
 }
 
 void CPDF_StreamContentParser::Handle_NextLineShowText() {
@@ -1415,129 +1439,152 @@
 
 void CPDF_StreamContentParser::Handle_Invalid() {}
 
-void CPDF_StreamContentParser::AddPathPoint(float x,
-                                            float y,
-                                            FXPT_TYPE type,
-                                            bool close) {
+void CPDF_StreamContentParser::AddPathPoint(const CFX_PointF& point,
+                                            CFX_Path::Point::Type type) {
   // If the path point is the same move as the previous one and neither of them
   // closes the path, then just skip it.
-  if (!close && type == FXPT_TYPE::MoveTo && !m_PathPoints.empty() &&
+  if (type == CFX_Path::Point::Type::kMove && !m_PathPoints.empty() &&
       !m_PathPoints.back().m_CloseFigure &&
-      m_PathPoints.back().m_Type == type && m_PathCurrentX == x &&
-      m_PathCurrentY == y) {
+      m_PathPoints.back().m_Type == type && m_PathCurrent == point) {
     return;
   }
 
-  m_PathCurrentX = x;
-  m_PathCurrentY = y;
-  if (type == FXPT_TYPE::MoveTo && !close) {
-    m_PathStartX = x;
-    m_PathStartY = y;
+  m_PathCurrent = point;
+  if (type == CFX_Path::Point::Type::kMove) {
+    m_PathStart = point;
     if (!m_PathPoints.empty() &&
-        m_PathPoints.back().IsTypeAndOpen(FXPT_TYPE::MoveTo)) {
-      m_PathPoints.back().m_Point = CFX_PointF(x, y);
+        m_PathPoints.back().IsTypeAndOpen(CFX_Path::Point::Type::kMove)) {
+      m_PathPoints.back().m_Point = point;
       return;
     }
   } else if (m_PathPoints.empty()) {
     return;
   }
-  m_PathPoints.push_back(FX_PATHPOINT(CFX_PointF(x, y), type, close));
+  m_PathPoints.emplace_back(point, type, /*close=*/false);
 }
 
-void CPDF_StreamContentParser::AddPathObject(int FillType, bool bStroke) {
-  std::vector<FX_PATHPOINT> PathPoints;
-  PathPoints.swap(m_PathPoints);
-  uint8_t PathClipType = m_PathClipType;
-  m_PathClipType = 0;
-
-  if (PathPoints.empty())
+void CPDF_StreamContentParser::AddPathPointAndClose(
+    const CFX_PointF& point,
+    CFX_Path::Point::Type type) {
+  m_PathCurrent = point;
+  if (m_PathPoints.empty())
     return;
 
-  if (PathPoints.size() == 1) {
-    if (PathClipType) {
+  m_PathPoints.emplace_back(point, type, /*close=*/true);
+}
+
+void CPDF_StreamContentParser::AddPathObject(
+    CFX_FillRenderOptions::FillType fill_type,
+    RenderType render_type) {
+  std::vector<CFX_Path::Point> path_points;
+  path_points.swap(m_PathPoints);
+  CFX_FillRenderOptions::FillType path_clip_type = m_PathClipType;
+  m_PathClipType = CFX_FillRenderOptions::FillType::kNoFill;
+
+  if (path_points.empty())
+    return;
+
+  if (path_points.size() == 1) {
+    if (path_clip_type != CFX_FillRenderOptions::FillType::kNoFill) {
       CPDF_Path path;
       path.AppendRect(0, 0, 0, 0);
-      m_pCurStates->m_ClipPath.AppendPath(path, FXFILL_WINDING, true);
+      m_pCurStates->m_ClipPath.AppendPathWithAutoMerge(
+          path, CFX_FillRenderOptions::FillType::kWinding);
+      return;
     }
-    return;
+
+    CFX_Path::Point& point = path_points.front();
+    if (point.m_Type != CFX_Path::Point::Type::kMove || !point.m_CloseFigure ||
+        m_pCurStates->m_GraphState.GetLineCap() !=
+            CFX_GraphStateData::LineCap::kRound) {
+      return;
+    }
+
+    // For round line cap only: When a path moves to a point and immediately
+    // gets closed, we can treat it as drawing a path from this point to itself
+    // and closing the path. This should not apply to butt line cap or
+    // projecting square line cap since they should not be rendered.
+    point.m_CloseFigure = false;
+    path_points.emplace_back(point.m_Point, CFX_Path::Point::Type::kLine,
+                             /*close=*/true);
   }
 
-  if (PathPoints.back().IsTypeAndOpen(FXPT_TYPE::MoveTo))
-    PathPoints.pop_back();
+  if (path_points.back().IsTypeAndOpen(CFX_Path::Point::Type::kMove))
+    path_points.pop_back();
 
-  CPDF_Path Path;
-  for (const auto& point : PathPoints)
-    Path.AppendPoint(point.m_Point, point.m_Type, point.m_CloseFigure);
+  CPDF_Path path;
+  for (const auto& point : path_points) {
+    if (point.m_CloseFigure)
+      path.AppendPointAndClose(point.m_Point, point.m_Type);
+    else
+      path.AppendPoint(point.m_Point, point.m_Type);
+  }
 
   CFX_Matrix matrix = m_pCurStates->m_CTM * m_mtContentToUser;
-  if (bStroke || FillType) {
-    auto pPathObj =
-        pdfium::MakeUnique<CPDF_PathObject>(GetCurrentStreamIndex());
+  bool bStroke = render_type == RenderType::kStroke;
+  if (bStroke || fill_type != CFX_FillRenderOptions::FillType::kNoFill) {
+    auto pPathObj = std::make_unique<CPDF_PathObject>(GetCurrentStreamIndex());
     pPathObj->set_stroke(bStroke);
-    pPathObj->set_filltype(FillType);
-    pPathObj->path() = Path;
-    pPathObj->set_matrix(matrix);
+    pPathObj->set_filltype(fill_type);
+    pPathObj->path() = path;
     SetGraphicStates(pPathObj.get(), true, false, true);
-    pPathObj->CalcBoundingBox();
+    pPathObj->SetPathMatrix(matrix);
     m_pObjectHolder->AppendPageObject(std::move(pPathObj));
   }
-  if (PathClipType) {
+  if (path_clip_type != CFX_FillRenderOptions::FillType::kNoFill) {
     if (!matrix.IsIdentity())
-      Path.Transform(matrix);
-    m_pCurStates->m_ClipPath.AppendPath(Path, PathClipType, true);
+      path.Transform(matrix);
+    m_pCurStates->m_ClipPath.AppendPathWithAutoMerge(path, path_clip_type);
   }
 }
 
 uint32_t CPDF_StreamContentParser::Parse(
-    const uint8_t* pData,
-    uint32_t dwSize,
+    pdfium::span<const uint8_t> pData,
     uint32_t start_offset,
     uint32_t max_cost,
     const std::vector<uint32_t>& stream_start_offsets) {
-  ASSERT(start_offset < dwSize);
+  DCHECK(start_offset < pData.size());
 
-  // Parsing will be done from |pDataStart|, for at most |size_left| bytes.
-  const uint8_t* pDataStart = pData + start_offset;
-  uint32_t size_left = dwSize - start_offset;
-
+  // 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::ContainsKey(*m_ParsedSet, pDataStart)) {
-    return size_left;
+  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;
 
-  pdfium::ScopedSetInsertion<const uint8_t*> scopedInsert(m_ParsedSet.Get(),
-                                                          pDataStart);
+  ScopedSetInsertion<const uint8_t*> scoped_insert(
+      &m_RecursionState->parsed_set, pDataStart.data());
 
   uint32_t init_obj_count = m_pObjectHolder->GetPageObjectCount();
-  CPDF_StreamParser syntax(pdfium::make_span(pDataStart, size_left),
-                           m_pDocument->GetByteStringPool());
-  CPDF_StreamParserAutoClearer auto_clearer(&m_pSyntax, &syntax);
-  while (1) {
+  AutoNuller<std::unique_ptr<CPDF_StreamParser>> auto_clearer(&m_pSyntax);
+  m_pSyntax = std::make_unique<CPDF_StreamParser>(
+      pDataStart, m_pDocument->GetByteStringPool());
+
+  while (true) {
     uint32_t cost = m_pObjectHolder->GetPageObjectCount() - init_obj_count;
     if (max_cost && cost >= max_cost) {
       break;
     }
-    switch (syntax.ParseNextElement()) {
-      case CPDF_StreamParser::EndOfData:
+    switch (m_pSyntax->ParseNextElement()) {
+      case CPDF_StreamParser::ElementType::kEndOfData:
         return m_pSyntax->GetPos();
-      case CPDF_StreamParser::Keyword:
-        OnOperator(syntax.GetWord());
+      case CPDF_StreamParser::ElementType::kKeyword:
+        OnOperator(m_pSyntax->GetWord());
         ClearAllParams();
         break;
-      case CPDF_StreamParser::Number:
-        AddNumberParam(syntax.GetWord());
+      case CPDF_StreamParser::ElementType::kNumber:
+        AddNumberParam(m_pSyntax->GetWord());
         break;
-      case CPDF_StreamParser::Name: {
-        auto word = syntax.GetWord();
+      case CPDF_StreamParser::ElementType::kName: {
+        auto word = m_pSyntax->GetWord();
         AddNameParam(word.Last(word.GetLength() - 1));
         break;
       }
       default:
-        AddObjectParam(syntax.GetObject());
+        AddObjectParam(m_pSyntax->GetObject());
     }
   }
   return m_pSyntax->GetPos();
@@ -1547,42 +1594,51 @@
   float params[6] = {};
   int nParams = 0;
   int last_pos = m_pSyntax->GetPos();
-  while (1) {
-    CPDF_StreamParser::SyntaxType type = m_pSyntax->ParseNextElement();
+  while (true) {
+    CPDF_StreamParser::ElementType type = m_pSyntax->ParseNextElement();
     bool bProcessed = true;
     switch (type) {
-      case CPDF_StreamParser::EndOfData:
+      case CPDF_StreamParser::ElementType::kEndOfData:
         return;
-      case CPDF_StreamParser::Keyword: {
+      case CPDF_StreamParser::ElementType::kKeyword: {
         ByteStringView strc = m_pSyntax->GetWord();
         int len = strc.GetLength();
         if (len == 1) {
           switch (strc[0]) {
             case kPathOperatorSubpath:
-              AddPathPoint(params[0], params[1], FXPT_TYPE::MoveTo, false);
+              AddPathPoint({params[0], params[1]},
+                           CFX_Path::Point::Type::kMove);
               nParams = 0;
               break;
             case kPathOperatorLine:
-              AddPathPoint(params[0], params[1], FXPT_TYPE::LineTo, false);
+              AddPathPoint({params[0], params[1]},
+                           CFX_Path::Point::Type::kLine);
               nParams = 0;
               break;
             case kPathOperatorCubicBezier1:
-              AddPathPoint(params[0], params[1], FXPT_TYPE::BezierTo, false);
-              AddPathPoint(params[2], params[3], FXPT_TYPE::BezierTo, false);
-              AddPathPoint(params[4], params[5], FXPT_TYPE::BezierTo, false);
+              AddPathPoint({params[0], params[1]},
+                           CFX_Path::Point::Type::kBezier);
+              AddPathPoint({params[2], params[3]},
+                           CFX_Path::Point::Type::kBezier);
+              AddPathPoint({params[4], params[5]},
+                           CFX_Path::Point::Type::kBezier);
               nParams = 0;
               break;
             case kPathOperatorCubicBezier2:
-              AddPathPoint(m_PathCurrentX, m_PathCurrentY, FXPT_TYPE::BezierTo,
-                           false);
-              AddPathPoint(params[0], params[1], FXPT_TYPE::BezierTo, false);
-              AddPathPoint(params[2], params[3], FXPT_TYPE::BezierTo, false);
+              AddPathPoint(m_PathCurrent, CFX_Path::Point::Type::kBezier);
+              AddPathPoint({params[0], params[1]},
+                           CFX_Path::Point::Type::kBezier);
+              AddPathPoint({params[2], params[3]},
+                           CFX_Path::Point::Type::kBezier);
               nParams = 0;
               break;
             case kPathOperatorCubicBezier3:
-              AddPathPoint(params[0], params[1], FXPT_TYPE::BezierTo, false);
-              AddPathPoint(params[2], params[3], FXPT_TYPE::BezierTo, false);
-              AddPathPoint(params[2], params[3], FXPT_TYPE::BezierTo, false);
+              AddPathPoint({params[0], params[1]},
+                           CFX_Path::Point::Type::kBezier);
+              AddPathPoint({params[2], params[3]},
+                           CFX_Path::Point::Type::kBezier);
+              AddPathPoint({params[2], params[3]},
+                           CFX_Path::Point::Type::kBezier);
               nParams = 0;
               break;
             case kPathOperatorClosePath:
@@ -1609,7 +1665,7 @@
         }
         break;
       }
-      case CPDF_StreamParser::Number: {
+      case CPDF_StreamParser::ElementType::kNumber: {
         if (nParams == 6)
           break;
 
@@ -1630,15 +1686,15 @@
 // static
 ByteStringView CPDF_StreamContentParser::FindKeyAbbreviationForTesting(
     ByteStringView abbr) {
-  return FindFullName(kInlineKeyAbbr, FX_ArraySize(kInlineKeyAbbr), abbr);
+  return FindFullName(kInlineKeyAbbr, abbr);
 }
 
 // static
 ByteStringView CPDF_StreamContentParser::FindValueAbbreviationForTesting(
     ByteStringView abbr) {
-  return FindFullName(kInlineValueAbbr, FX_ArraySize(kInlineValueAbbr), abbr);
+  return FindFullName(kInlineValueAbbr, abbr);
 }
 
-CPDF_StreamContentParser::ContentParam::ContentParam() {}
+CPDF_StreamContentParser::ContentParam::ContentParam() = default;
 
-CPDF_StreamContentParser::ContentParam::~ContentParam() {}
+CPDF_StreamContentParser::ContentParam::~ContentParam() = default;
diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.h b/core/fpdfapi/page/cpdf_streamcontentparser.h
index a270320..c31dd15 100644
--- a/core/fpdfapi/page/cpdf_streamcontentparser.h
+++ b/core/fpdfapi/page/cpdf_streamcontentparser.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,15 +9,19 @@
 
 #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"
-#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "core/fxge/cfx_path.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_AllStates;
 class CPDF_ColorSpace;
@@ -30,6 +34,7 @@
 class CPDF_PageObject;
 class CPDF_PageObjectHolder;
 class CPDF_Pattern;
+class CPDF_ShadingPattern;
 class CPDF_Stream;
 class CPDF_StreamParser;
 class CPDF_TextObject;
@@ -37,46 +42,45 @@
 class CPDF_StreamContentParser {
  public:
   CPDF_StreamContentParser(CPDF_Document* pDoc,
-                           CPDF_Dictionary* pPageResources,
-                           CPDF_Dictionary* pParentResources,
+                           RetainPtr<CPDF_Dictionary> pPageResources,
+                           RetainPtr<CPDF_Dictionary> pParentResources,
                            const CFX_Matrix* pmtContentToUser,
                            CPDF_PageObjectHolder* pObjHolder,
-                           CPDF_Dictionary* pResources,
+                           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(const uint8_t* pData,
-                 uint32_t dwSize,
+  uint32_t Parse(pdfium::span<const uint8_t> pData,
                  uint32_t start_offset,
                  uint32_t max_cost,
                  const std::vector<uint32_t>& stream_start_offsets);
-  CPDF_PageObjectHolder* GetPageObjectHolder() const {
-    return m_pObjectHolder.Get();
-  }
+  CPDF_PageObjectHolder* GetPageObjectHolder() const { return m_pObjectHolder; }
   CPDF_AllStates* GetCurStates() const { return m_pCurStates.get(); }
   bool IsColored() const { return m_bColored; }
-  const float* GetType3Data() const { return m_Type3Data; }
+  pdfium::span<const float> GetType3Data() const { return m_Type3Data; }
   RetainPtr<CPDF_Font> FindFont(const ByteString& name);
 
   static ByteStringView FindKeyAbbreviationForTesting(ByteStringView abbr);
   static ByteStringView FindValueAbbreviationForTesting(ByteStringView abbr);
 
  private:
+  enum class RenderType : bool { kFill = false, kStroke = true };
+
   struct ContentParam {
-    enum Type { OBJECT = 0, NUMBER, NAME };
+    enum class Type : uint8_t { kObject = 0, kNumber, kName };
 
     ContentParam();
     ~ContentParam();
 
-    Type m_Type;
+    Type m_Type = Type::kObject;
     FX_Number m_Number;
     ByteString m_Name;
     RetainPtr<CPDF_Object> m_pObject;
   };
 
-  static const int kParamBufSize = 16;
+  static constexpr int kParamBufSize = 16;
 
   using OpCodes = std::map<uint32_t, void (CPDF_StreamContentParser::*)()>;
   static OpCodes InitializeOpCodes();
@@ -86,7 +90,7 @@
   void AddObjectParam(RetainPtr<CPDF_Object> pObj);
   int GetNextParamPos();
   void ClearAllParams();
-  CPDF_Object* GetObject(uint32_t index);
+  RetainPtr<CPDF_Object> GetObject(uint32_t index);
   ByteString GetString(uint32_t index) const;
   float GetNumber(uint32_t index) const;
   // Calls GetNumber() |count| times and returns the values in reverse order.
@@ -95,6 +99,10 @@
   int GetInteger(uint32_t index) const {
     return static_cast<int>(GetNumber(index));
   }
+  // Makes a point from {GetNumber(index + 1), GetNumber(index)}.
+  CFX_PointF GetPoint(uint32_t index) const;
+  // Makes a matrix from {GetNumber(5), ..., GetNumber(0)}.
+  CFX_Matrix GetMatrix() const;
   void OnOperator(ByteStringView op);
   void AddTextObject(const ByteString* pStrs,
                      float fInitKerning,
@@ -105,22 +113,29 @@
 
   void OnChangeTextMatrix();
   void ParsePathObject();
-  void AddPathPoint(float x, float y, FXPT_TYPE type, bool close);
+  void AddPathPoint(const CFX_PointF& point, CFX_Path::Point::Type type);
+  void AddPathPointAndClose(const CFX_PointF& point,
+                            CFX_Path::Point::Type type);
   void AddPathRect(float x, float y, float w, float h);
-  void AddPathObject(int FillType, bool bStroke);
-  CPDF_ImageObject* AddImage(RetainPtr<CPDF_Stream> pStream);
-  CPDF_ImageObject* AddImage(uint32_t streamObjNum);
-  CPDF_ImageObject* AddImage(const RetainPtr<CPDF_Image>& pImage);
+  void AddPathObject(CFX_FillRenderOptions::FillType fill_type,
+                     RenderType render_type);
+  CPDF_ImageObject* AddImageFromStream(RetainPtr<CPDF_Stream> pStream,
+                                       const ByteString& name);
+  CPDF_ImageObject* AddImageFromStreamObjNum(uint32_t stream_obj_num,
+                                             const ByteString& name);
+  CPDF_ImageObject* AddLastImage();
 
-  void AddForm(CPDF_Stream* pStream);
+  void AddForm(RetainPtr<CPDF_Stream> pStream, const ByteString& name);
   void SetGraphicStates(CPDF_PageObject* pObj,
                         bool bColor,
                         bool bText,
                         bool bGraph);
   RetainPtr<CPDF_ColorSpace> FindColorSpace(const ByteString& name);
-  RetainPtr<CPDF_Pattern> FindPattern(const ByteString& name, bool bShading);
-  CPDF_Dictionary* FindResourceHolder(const ByteString& type);
-  CPDF_Object* FindResourceObj(const ByteString& type, const ByteString& name);
+  RetainPtr<CPDF_Pattern> FindPattern(const ByteString& name);
+  RetainPtr<CPDF_ShadingPattern> FindShading(const ByteString& name);
+  RetainPtr<CPDF_Dictionary> FindResourceHolder(const ByteString& type);
+  RetainPtr<CPDF_Object> FindResourceObj(const ByteString& type,
+                                         const ByteString& name);
 
   // Takes ownership of |pImageObj|, returns unowned pointer to it.
   CPDF_ImageObject* AddImageObject(std::unique_ptr<CPDF_ImageObject> pImageObj);
@@ -207,26 +222,23 @@
   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;
   uint32_t m_ParamCount = 0;
-  UnownedPtr<CPDF_StreamParser> m_pSyntax;
+  std::unique_ptr<CPDF_StreamParser> m_pSyntax;
   std::unique_ptr<CPDF_AllStates> m_pCurStates;
   std::stack<std::unique_ptr<CPDF_ContentMarks>> m_ContentMarksStack;
   std::vector<std::unique_ptr<CPDF_TextObject>> m_ClipTextList;
-  UnownedPtr<CPDF_TextObject> m_pLastTextObject;
-  std::vector<FX_PATHPOINT> m_PathPoints;
-  float m_PathStartX = 0.0f;
-  float m_PathStartY = 0.0f;
-  float m_PathCurrentX = 0.0f;
-  float m_PathCurrentY = 0.0f;
-  uint8_t m_PathClipType = 0;
+  std::vector<CFX_Path::Point> m_PathPoints;
+  CFX_PointF m_PathStart;
+  CFX_PointF m_PathCurrent;
+  CFX_FillRenderOptions::FillType m_PathClipType =
+      CFX_FillRenderOptions::FillType::kNoFill;
   ByteString m_LastImageName;
   RetainPtr<CPDF_Image> m_pLastImage;
   bool m_bColored = false;
-  bool m_bResourceMissing = false;
   std::vector<std::unique_ptr<CPDF_AllStates>> m_StateStack;
   float m_Type3Data[6] = {0.0f};
   ContentParam m_ParamBuf[kParamBufSize];
diff --git a/core/fpdfapi/page/cpdf_streamcontentparser_unittest.cpp b/core/fpdfapi/page/cpdf_streamcontentparser_unittest.cpp
index 0f4fc1e..8198daf 100644
--- a/core/fpdfapi/page/cpdf_streamcontentparser_unittest.cpp
+++ b/core/fpdfapi/page/cpdf_streamcontentparser_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fpdfapi/page/cpdf_streamparser.cpp b/core/fpdfapi/page/cpdf_streamparser.cpp
index 25172a6..dd0a76d 100644
--- a/core/fpdfapi/page/cpdf_streamparser.cpp
+++ b/core/fpdfapi/page/cpdf_streamparser.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,11 +6,10 @@
 
 #include "core/fpdfapi/page/cpdf_streamparser.h"
 
-#include <limits.h>
+#include <ctype.h>
 
 #include <algorithm>
 #include <memory>
-#include <sstream>
 #include <utility>
 
 #include "constants/stream_dict_common.h"
@@ -18,7 +17,6 @@
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_boolean.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_null.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
@@ -26,13 +24,15 @@
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
-#include "core/fxcodec/fx_codec.h"
 #include "core/fxcodec/jpeg/jpegmodule.h"
 #include "core/fxcodec/scanlinedecoder.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/ptr_util.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxge/calculate_pitch.h"
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -54,13 +54,18 @@
   if (width <= 0 || height <= 0)
     return FX_INVALID_OFFSET;
 
-  FX_SAFE_UINT32 size = fxcodec::CalculatePitch8(bpc, ncomps, width);
+  absl::optional<uint32_t> maybe_size =
+      fxge::CalculatePitch8(bpc, ncomps, width);
+  if (!maybe_size.has_value())
+    return FX_INVALID_OFFSET;
+
+  FX_SAFE_UINT32 size = maybe_size.value();
   size *= height;
   if (size.ValueOrDefault(0) == 0)
     return FX_INVALID_OFFSET;
 
   for (int row = 0; row < height; ++row) {
-    if (!pDecoder->GetScanline(row))
+    if (pDecoder->GetScanline(row).empty())
       break;
   }
   return pDecoder->GetSrcOffset();
@@ -70,37 +75,36 @@
                             int width,
                             int height,
                             const ByteString& decoder,
-                            const CPDF_Dictionary* pParam,
+                            RetainPtr<const CPDF_Dictionary> pParam,
                             uint32_t orig_size) {
   // |decoder| should not be an abbreviation.
-  ASSERT(decoder != "A85");
-  ASSERT(decoder != "AHx");
-  ASSERT(decoder != "CCF");
-  ASSERT(decoder != "DCT");
-  ASSERT(decoder != "Fl");
-  ASSERT(decoder != "LZW");
-  ASSERT(decoder != "RL");
+  DCHECK(decoder != "A85");
+  DCHECK(decoder != "AHx");
+  DCHECK(decoder != "CCF");
+  DCHECK(decoder != "DCT");
+  DCHECK(decoder != "Fl");
+  DCHECK(decoder != "LZW");
+  DCHECK(decoder != "RL");
 
   std::unique_ptr<uint8_t, FxFreeDeleter> ignored_result;
   uint32_t ignored_size;
   if (decoder == "FlateDecode") {
-    return FlateOrLZWDecode(false, src_span, pParam, orig_size, &ignored_result,
-                            &ignored_size);
+    return FlateOrLZWDecode(false, src_span, pParam.Get(), orig_size,
+                            &ignored_result, &ignored_size);
   }
   if (decoder == "LZWDecode") {
-    return FlateOrLZWDecode(true, src_span, pParam, 0, &ignored_result,
+    return FlateOrLZWDecode(true, src_span, pParam.Get(), 0, &ignored_result,
                             &ignored_size);
   }
   if (decoder == "DCTDecode") {
-    std::unique_ptr<ScanlineDecoder> pDecoder =
-        fxcodec::ModuleMgr::GetInstance()->GetJpegModule()->CreateDecoder(
-            src_span, width, height, 0,
-            !pParam || pParam->GetIntegerFor("ColorTransform", 1));
+    std::unique_ptr<ScanlineDecoder> pDecoder = JpegModule::CreateDecoder(
+        src_span, width, height, 0,
+        !pParam || pParam->GetIntegerFor("ColorTransform", 1));
     return DecodeAllScanlines(std::move(pDecoder));
   }
   if (decoder == "CCITTFaxDecode") {
     std::unique_ptr<ScanlineDecoder> pDecoder =
-        CreateFaxDecoder(src_span, width, height, pParam);
+        CreateFaxDecoder(src_span, width, height, pParam.Get());
     return DecodeAllScanlines(std::move(pDecoder));
   }
 
@@ -123,7 +127,7 @@
                                      const WeakPtr<ByteStringPool>& pPool)
     : m_pPool(pPool), m_pBuf(span) {}
 
-CPDF_StreamParser::~CPDF_StreamParser() {}
+CPDF_StreamParser::~CPDF_StreamParser() = default;
 
 RetainPtr<CPDF_Stream> CPDF_StreamParser::ReadInlineStream(
     CPDF_Document* pDoc,
@@ -136,13 +140,13 @@
     return nullptr;
 
   ByteString decoder;
-  const CPDF_Dictionary* pParam = nullptr;
-  CPDF_Object* pFilter = pDict->GetDirectObjectFor("Filter");
+  RetainPtr<const CPDF_Dictionary> pParam;
+  RetainPtr<const CPDF_Object> pFilter = pDict->GetDirectObjectFor("Filter");
   if (pFilter) {
     const CPDF_Array* pArray = pFilter->AsArray();
     if (pArray) {
-      decoder = pArray->GetStringAt(0);
-      const CPDF_Array* pParams =
+      decoder = pArray->GetByteStringAt(0);
+      RetainPtr<const CPDF_Array> pParams =
           pDict->GetArrayFor(pdfium::stream::kDecodeParms);
       if (pParams)
         pParam = pParams->GetDictAt(0);
@@ -161,36 +165,40 @@
     nComponents = pCS ? pCS->CountComponents() : 3;
     bpc = pDict->GetIntegerFor("BitsPerComponent");
   }
-  FX_SAFE_UINT32 size = fxcodec::CalculatePitch8(bpc, nComponents, width);
+  absl::optional<uint32_t> maybe_size =
+      fxge::CalculatePitch8(bpc, nComponents, width);
+  if (!maybe_size.has_value())
+    return nullptr;
+
+  FX_SAFE_UINT32 size = maybe_size.value();
   size *= height;
   if (!size.IsValid())
     return nullptr;
 
   uint32_t dwOrigSize = size.ValueOrDie();
-  std::unique_ptr<uint8_t, FxFreeDeleter> pData;
+  DataVector<uint8_t> data;
   uint32_t dwStreamSize;
   if (decoder.IsEmpty()) {
     dwOrigSize = std::min<uint32_t>(dwOrigSize, m_pBuf.size() - m_Pos);
-    pData.reset(FX_Alloc(uint8_t, dwOrigSize));
-    auto copy_span = m_pBuf.subspan(m_Pos, dwOrigSize);
-    memcpy(pData.get(), copy_span.data(), copy_span.size());
+    auto src_span = m_pBuf.subspan(m_Pos, dwOrigSize);
+    data = DataVector<uint8_t>(src_span.begin(), src_span.end());
     dwStreamSize = dwOrigSize;
     m_Pos += dwOrigSize;
   } else {
     dwStreamSize = DecodeInlineStream(m_pBuf.subspan(m_Pos), width, height,
-                                      decoder, pParam, dwOrigSize);
+                                      decoder, std::move(pParam), dwOrigSize);
     if (!pdfium::base::IsValueInRangeForNumericType<int>(dwStreamSize))
       return nullptr;
 
     uint32_t dwSavePos = m_Pos;
     m_Pos += dwStreamSize;
-    while (1) {
+    while (true) {
       uint32_t dwPrevPos = m_Pos;
-      CPDF_StreamParser::SyntaxType type = ParseNextElement();
-      if (type == CPDF_StreamParser::EndOfData)
+      ElementType type = ParseNextElement();
+      if (type == ElementType::kEndOfData)
         break;
 
-      if (type != CPDF_StreamParser::Keyword) {
+      if (type != ElementType::kKeyword) {
         dwStreamSize += m_Pos - dwPrevPos;
         continue;
       }
@@ -201,27 +209,25 @@
       dwStreamSize += m_Pos - dwPrevPos;
     }
     m_Pos = dwSavePos;
-    pData.reset(FX_Alloc(uint8_t, dwStreamSize));
-    auto copy_span = m_pBuf.subspan(m_Pos, dwStreamSize);
-    memcpy(pData.get(), copy_span.data(), copy_span.size());
+    auto src_span = m_pBuf.subspan(m_Pos, dwStreamSize);
+    data = DataVector<uint8_t>(src_span.begin(), src_span.end());
     m_Pos += dwStreamSize;
   }
   pDict->SetNewFor<CPDF_Number>("Length", static_cast<int>(dwStreamSize));
-  return pdfium::MakeRetain<CPDF_Stream>(std::move(pData), dwStreamSize,
-                                         std::move(pDict));
+  return pdfium::MakeRetain<CPDF_Stream>(std::move(data), std::move(pDict));
 }
 
-CPDF_StreamParser::SyntaxType CPDF_StreamParser::ParseNextElement() {
+CPDF_StreamParser::ElementType CPDF_StreamParser::ParseNextElement() {
   m_pLastObj.Reset();
   m_WordSize = 0;
   if (!PositionIsInBounds())
-    return EndOfData;
+    return ElementType::kEndOfData;
 
   uint8_t ch = m_pBuf[m_Pos++];
-  while (1) {
+  while (true) {
     while (PDFCharIsWhitespace(ch)) {
       if (!PositionIsInBounds())
-        return EndOfData;
+        return ElementType::kEndOfData;
 
       ch = m_pBuf[m_Pos++];
     }
@@ -229,9 +235,9 @@
     if (ch != '%')
       break;
 
-    while (1) {
+    while (true) {
       if (!PositionIsInBounds())
-        return EndOfData;
+        return ElementType::kEndOfData;
 
       ch = m_pBuf[m_Pos++];
       if (PDFCharIsLineEnding(ch))
@@ -242,11 +248,11 @@
   if (PDFCharIsDelimiter(ch) && ch != '/') {
     m_Pos--;
     m_pLastObj = ReadNextObject(false, false, 0);
-    return Others;
+    return ElementType::kOther;
   }
 
   bool bIsNumber = true;
-  while (1) {
+  while (true) {
     if (m_WordSize < kMaxWordLength)
       m_WordBuffer[m_WordSize++] = ch;
 
@@ -266,27 +272,27 @@
 
   m_WordBuffer[m_WordSize] = 0;
   if (bIsNumber)
-    return Number;
+    return ElementType::kNumber;
 
   if (m_WordBuffer[0] == '/')
-    return Name;
+    return ElementType::kName;
 
   if (m_WordSize == 4) {
-    if (WordBufferMatches(kTrue)) {
+    if (GetWord() == kTrue) {
       m_pLastObj = pdfium::MakeRetain<CPDF_Boolean>(true);
-      return Others;
+      return ElementType::kOther;
     }
-    if (WordBufferMatches(kNull)) {
+    if (GetWord() == kNull) {
       m_pLastObj = pdfium::MakeRetain<CPDF_Null>();
-      return Others;
+      return ElementType::kOther;
     }
   } else if (m_WordSize == 5) {
-    if (WordBufferMatches(kFalse)) {
+    if (GetWord() == kFalse) {
       m_pLastObj = pdfium::MakeRetain<CPDF_Boolean>(false);
-      return Others;
+      return ElementType::kOther;
     }
   }
-  return Keyword;
+  return ElementType::kKeyword;
 }
 
 RetainPtr<CPDF_Object> CPDF_StreamParser::ReadNextObject(
@@ -301,14 +307,12 @@
 
   if (bIsNumber) {
     m_WordBuffer[m_WordSize] = 0;
-    return pdfium::MakeRetain<CPDF_Number>(
-        ByteStringView(m_WordBuffer, m_WordSize));
+    return pdfium::MakeRetain<CPDF_Number>(GetWord());
   }
 
   int first_char = m_WordBuffer[0];
   if (first_char == '/') {
-    ByteString name =
-        PDF_NameDecode(ByteStringView(m_WordBuffer + 1, m_WordSize - 1));
+    ByteString name = PDF_NameDecode(GetWord().Substr(1));
     return pdfium::MakeRetain<CPDF_Name>(m_pPool, name);
   }
 
@@ -322,7 +326,7 @@
       return pdfium::MakeRetain<CPDF_String>(m_pPool, ReadHexString(), true);
 
     auto pDict = pdfium::MakeRetain<CPDF_Dictionary>(m_pPool);
-    while (1) {
+    while (true) {
       GetNextWord(bIsNumber);
       if (m_WordSize == 2 && m_WordBuffer[0] == '>')
         break;
@@ -330,15 +334,13 @@
       if (!m_WordSize || m_WordBuffer[0] != '/')
         return nullptr;
 
-      ByteString key =
-          PDF_NameDecode(ByteStringView(m_WordBuffer + 1, m_WordSize - 1));
+      ByteString key = PDF_NameDecode(GetWord().Substr(1));
       RetainPtr<CPDF_Object> pObj =
           ReadNextObject(true, bInArray, dwRecursionLevel + 1);
       if (!pObj)
         return nullptr;
 
-      if (!key.IsEmpty())
-        pDict->SetFor(key, std::move(pObj));
+      pDict->SetFor(key, std::move(pObj));
     }
     return pDict;
   }
@@ -348,11 +350,11 @@
       return nullptr;
 
     auto pArray = pdfium::MakeRetain<CPDF_Array>();
-    while (1) {
+    while (true) {
       RetainPtr<CPDF_Object> pObj =
           ReadNextObject(bAllowNestedArray, true, dwRecursionLevel + 1);
       if (pObj) {
-        pArray->Add(std::move(pObj));
+        pArray->Append(std::move(pObj));
         continue;
       }
       if (!m_WordSize || m_WordBuffer[0] == ']')
@@ -361,11 +363,11 @@
     return pArray;
   }
 
-  if (WordBufferMatches(kFalse))
+  if (GetWord() == kFalse)
     return pdfium::MakeRetain<CPDF_Boolean>(false);
-  if (WordBufferMatches(kTrue))
+  if (GetWord() == kTrue)
     return pdfium::MakeRetain<CPDF_Boolean>(true);
-  if (WordBufferMatches(kNull))
+  if (GetWord() == kNull)
     return pdfium::MakeRetain<CPDF_Null>();
   return nullptr;
 }
@@ -378,7 +380,7 @@
     return;
 
   uint8_t ch = m_pBuf[m_Pos++];
-  while (1) {
+  while (true) {
     while (PDFCharIsWhitespace(ch)) {
       if (!PositionIsInBounds()) {
         return;
@@ -389,7 +391,7 @@
     if (ch != '%')
       break;
 
-    while (1) {
+    while (true) {
       if (!PositionIsInBounds())
         return;
       ch = m_pBuf[m_Pos++];
@@ -402,7 +404,7 @@
     bIsNumber = false;
     m_WordBuffer[m_WordSize++] = ch;
     if (ch == '/') {
-      while (1) {
+      while (true) {
         if (!PositionIsInBounds())
           return;
         ch = m_pBuf[m_Pos++];
@@ -433,7 +435,7 @@
     return;
   }
 
-  while (1) {
+  while (true) {
     if (m_WordSize < kMaxWordLength)
       m_WordBuffer[m_WordSize++] = ch;
     if (!PDFCharIsNumeric(ch))
@@ -453,32 +455,27 @@
   if (!PositionIsInBounds())
     return ByteString();
 
-  uint8_t ch = m_pBuf[m_Pos++];
-  std::ostringstream buf;
+  ByteString buf;
   int parlevel = 0;
   int status = 0;
   int iEscCode = 0;
-  while (1) {
+  uint8_t ch = m_pBuf[m_Pos++];
+  while (true) {
     switch (status) {
       case 0:
         if (ch == ')') {
           if (parlevel == 0) {
-            if (buf.tellp() <= 0)
-              return ByteString();
-
-            return ByteString(
-                buf.str().c_str(),
-                std::min(static_cast<size_t>(buf.tellp()), kMaxStringLength));
+            return buf.First(std::min(buf.GetLength(), kMaxStringLength));
           }
           parlevel--;
-          buf << ')';
+          buf += ')';
         } else if (ch == '(') {
           parlevel++;
-          buf << '(';
+          buf += '(';
         } else if (ch == '\\') {
           status = 1;
         } else {
-          buf << static_cast<char>(ch);
+          buf += static_cast<char>(ch);
         }
         break;
       case 1:
@@ -494,17 +491,17 @@
         if (ch == '\n') {
           // Do nothing.
         } else if (ch == 'n') {
-          buf << '\n';
+          buf += '\n';
         } else if (ch == 'r') {
-          buf << '\r';
+          buf += '\r';
         } else if (ch == 't') {
-          buf << '\t';
+          buf += '\t';
         } else if (ch == 'b') {
-          buf << '\b';
+          buf += '\b';
         } else if (ch == 'f') {
-          buf << '\f';
+          buf += '\f';
         } else {
-          buf << static_cast<char>(ch);
+          buf += static_cast<char>(ch);
         }
         status = 0;
         break;
@@ -514,7 +511,7 @@
               iEscCode * 8 + FXSYS_DecimalCharToInt(static_cast<char>(ch));
           status = 3;
         } else {
-          buf << static_cast<char>(iEscCode);
+          buf += static_cast<char>(iEscCode);
           status = 0;
           continue;
         }
@@ -523,10 +520,10 @@
         if (FXSYS_IsOctalDigit(ch)) {
           iEscCode =
               iEscCode * 8 + FXSYS_DecimalCharToInt(static_cast<char>(ch));
-          buf << static_cast<char>(iEscCode);
+          buf += static_cast<char>(iEscCode);
           status = 0;
         } else {
-          buf << static_cast<char>(iEscCode);
+          buf += static_cast<char>(iEscCode);
           status = 0;
           continue;
         }
@@ -538,26 +535,17 @@
         break;
     }
     if (!PositionIsInBounds())
-      break;
+      return buf.First(std::min(buf.GetLength(), kMaxStringLength));
 
     ch = m_pBuf[m_Pos++];
   }
-  if (PositionIsInBounds())
-    ++m_Pos;
-
-  if (buf.tellp() <= 0)
-    return ByteString();
-
-  return ByteString(
-      buf.str().c_str(),
-      std::min(static_cast<size_t>(buf.tellp()), kMaxStringLength));
 }
 
 ByteString CPDF_StreamParser::ReadHexString() {
   if (!PositionIsInBounds())
     return ByteString();
 
-  std::ostringstream buf;
+  ByteString buf;
   bool bFirst = true;
   int code = 0;
   while (PositionIsInBounds()) {
@@ -565,7 +553,7 @@
     if (ch == '>')
       break;
 
-    if (!std::isxdigit(ch))
+    if (!isxdigit(ch))
       continue;
 
     int val = FXSYS_HexCharToInt(ch);
@@ -573,26 +561,16 @@
       code = val * 16;
     } else {
       code += val;
-      buf << static_cast<uint8_t>(code);
+      buf += static_cast<uint8_t>(code);
     }
     bFirst = !bFirst;
   }
   if (!bFirst)
-    buf << static_cast<char>(code);
+    buf += static_cast<char>(code);
 
-  if (buf.tellp() <= 0)
-    return ByteString();
-
-  return ByteString(
-      buf.str().c_str(),
-      std::min(static_cast<size_t>(buf.tellp()), kMaxStringLength));
+  return buf.First(std::min<size_t>(buf.GetLength(), kMaxStringLength));
 }
 
 bool CPDF_StreamParser::PositionIsInBounds() const {
   return m_Pos < m_pBuf.size();
 }
-
-bool CPDF_StreamParser::WordBufferMatches(const char* pWord) const {
-  const size_t iLength = strlen(pWord);
-  return m_WordSize == iLength && memcmp(m_WordBuffer, pWord, iLength) == 0;
-}
diff --git a/core/fpdfapi/page/cpdf_streamparser.h b/core/fpdfapi/page/cpdf_streamparser.h
index 6051b8c..ecefa02 100644
--- a/core/fpdfapi/page/cpdf_streamparser.h
+++ b/core/fpdfapi/page/cpdf_streamparser.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,28 +7,26 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_STREAMPARSER_H_
 #define CORE_FPDFAPI_PAGE_CPDF_STREAMPARSER_H_
 
-#include <memory>
-#include <utility>
-
-#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/string_pool_template.h"
 #include "core/fxcrt/weak_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Dictionary;
+class CPDF_Document;
 class CPDF_Object;
 class CPDF_Stream;
 
 class CPDF_StreamParser {
  public:
-  enum SyntaxType { EndOfData, Number, Keyword, Name, Others };
+  enum ElementType { kEndOfData, kNumber, kKeyword, kName, kOther };
 
   explicit CPDF_StreamParser(pdfium::span<const uint8_t> span);
   CPDF_StreamParser(pdfium::span<const uint8_t> span,
                     const WeakPtr<ByteStringPool>& pPool);
   ~CPDF_StreamParser();
 
-  SyntaxType ParseNextElement();
+  ElementType ParseNextElement();
   ByteStringView GetWord() const {
     return ByteStringView(m_WordBuffer, m_WordSize);
   }
@@ -44,20 +42,19 @@
 
  private:
   friend class cpdf_streamparser_ReadHexString_Test;
-  static const uint32_t kMaxWordLength = 255;
+  static constexpr uint32_t kMaxWordLength = 255;
 
   void GetNextWord(bool& bIsNumber);
   ByteString ReadString();
   ByteString ReadHexString();
   bool PositionIsInBounds() const;
-  bool WordBufferMatches(const char* pWord) const;
 
   uint32_t m_Pos = 0;       // Current byte position within |m_pBuf|.
   uint32_t m_WordSize = 0;  // Current byte position within |m_WordBuffer|.
   WeakPtr<ByteStringPool> m_pPool;
   RetainPtr<CPDF_Object> m_pLastObj;
   pdfium::span<const uint8_t> m_pBuf;
-  uint8_t m_WordBuffer[kMaxWordLength + 1];  // Include space for NUL.
+  uint8_t m_WordBuffer[kMaxWordLength + 1] = {};  // Include space for NUL.
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_STREAMPARSER_H_
diff --git a/core/fpdfapi/page/cpdf_streamparser_unittest.cpp b/core/fpdfapi/page/cpdf_streamparser_unittest.cpp
index 64bb57c..c9af44b 100644
--- a/core/fpdfapi/page/cpdf_streamparser_unittest.cpp
+++ b/core/fpdfapi/page/cpdf_streamparser_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fpdfapi/page/cpdf_textobject.cpp b/core/fpdfapi/page/cpdf_textobject.cpp
index 077e5ed..087883e 100644
--- a/core/fpdfapi/page/cpdf_textobject.cpp
+++ b/core/fpdfapi/page/cpdf_textobject.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,55 +7,62 @@
 #include "core/fpdfapi/page/cpdf_textobject.h"
 
 #include <algorithm>
-#include <utility>
 
 #include "core/fpdfapi/font/cpdf_cidfont.h"
 #include "core/fpdfapi/font/cpdf_font.h"
-#include "third_party/base/ptr_util.h"
+#include "core/fxcrt/fx_coordinates.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/span.h"
 
 #define ISLATINWORD(u) (u != 0x20 && u <= 0x28FF)
 
-CPDF_TextObjectItem::CPDF_TextObjectItem() : m_CharCode(0) {}
+namespace {
 
-CPDF_TextObjectItem::~CPDF_TextObjectItem() = default;
+bool IsVertWritingCIDFont(const CPDF_CIDFont* font) {
+  return font && font->IsVertWriting();
+}
+
+}  // namespace
+
+CPDF_TextObject::Item::Item() = default;
+
+CPDF_TextObject::Item::Item(const Item& that) = default;
+
+CPDF_TextObject::Item::~Item() = default;
 
 CPDF_TextObject::CPDF_TextObject(int32_t content_stream)
     : CPDF_PageObject(content_stream) {}
 
 CPDF_TextObject::CPDF_TextObject() : CPDF_TextObject(kNoContentStream) {}
 
-CPDF_TextObject::~CPDF_TextObject() {
-  // Move m_CharCodes to a local variable so it will be captured in crash dumps,
-  // to help with investigating crbug.com/782215.
-  auto char_codes_copy = std::move(m_CharCodes);
-}
+CPDF_TextObject::~CPDF_TextObject() = default;
 
 size_t CPDF_TextObject::CountItems() const {
   return m_CharCodes.size();
 }
 
-void CPDF_TextObject::GetItemInfo(size_t index,
-                                  CPDF_TextObjectItem* pInfo) const {
-  ASSERT(index < m_CharCodes.size());
-  pInfo->m_CharCode = m_CharCodes[index];
-  pInfo->m_Origin = CFX_PointF(index > 0 ? m_CharPos[index - 1] : 0, 0);
-  if (pInfo->m_CharCode == CPDF_Font::kInvalidCharCode)
-    return;
+CPDF_TextObject::Item CPDF_TextObject::GetItemInfo(size_t index) const {
+  DCHECK(index < m_CharCodes.size());
+
+  Item info;
+  info.m_CharCode = m_CharCodes[index];
+  info.m_Origin = CFX_PointF(index > 0 ? m_CharPos[index - 1] : 0, 0);
+  if (info.m_CharCode == CPDF_Font::kInvalidCharCode)
+    return info;
 
   RetainPtr<CPDF_Font> pFont = GetFont();
-  if (!pFont->IsCIDFont() || !pFont->AsCIDFont()->IsVertWriting())
-    return;
+  const CPDF_CIDFont* pCIDFont = pFont->AsCIDFont();
+  if (!IsVertWritingCIDFont(pCIDFont))
+    return info;
 
-  uint16_t CID = pFont->AsCIDFont()->CIDFromCharCode(pInfo->m_CharCode);
-  pInfo->m_Origin = CFX_PointF(0, pInfo->m_Origin.x);
+  uint16_t cid = pCIDFont->CIDFromCharCode(info.m_CharCode);
+  info.m_Origin = CFX_PointF(0, info.m_Origin.x);
 
-  short vx;
-  short vy;
-  pFont->AsCIDFont()->GetVertOrigin(CID, vx, vy);
-
+  CFX_Point16 vertical_origin = pCIDFont->GetVertOrigin(cid);
   float fontsize = GetFontSize();
-  pInfo->m_Origin.x -= fontsize * vx / 1000;
-  pInfo->m_Origin.y -= fontsize * vy / 1000;
+  info.m_Origin.x -= fontsize * vertical_origin.x / 1000;
+  info.m_Origin.y -= fontsize * vertical_origin.y / 1000;
+  return info;
 }
 
 size_t CPDF_TextObject::CountChars() const {
@@ -67,38 +74,28 @@
   return count;
 }
 
-void CPDF_TextObject::GetCharInfo(size_t index,
-                                  uint32_t* charcode,
-                                  float* kerning) const {
+uint32_t CPDF_TextObject::GetCharCode(size_t index) const {
   size_t count = 0;
-  for (size_t i = 0; i < m_CharCodes.size(); ++i) {
-    if (m_CharCodes[i] == CPDF_Font::kInvalidCharCode)
+  for (uint32_t code : m_CharCodes) {
+    if (code == CPDF_Font::kInvalidCharCode)
       continue;
     if (count++ != index)
       continue;
-    *charcode = m_CharCodes[i];
-    if (i == m_CharCodes.size() - 1 ||
-        m_CharCodes[i + 1] != CPDF_Font::kInvalidCharCode) {
-      *kerning = 0;
-    } else {
-      *kerning = m_CharPos[i];
-    }
-    return;
+    return code;
   }
+  return CPDF_Font::kInvalidCharCode;
 }
 
-void CPDF_TextObject::GetCharInfo(size_t index,
-                                  CPDF_TextObjectItem* pInfo) const {
+CPDF_TextObject::Item CPDF_TextObject::GetCharInfo(size_t index) const {
   size_t count = 0;
   for (size_t i = 0; i < m_CharCodes.size(); ++i) {
     uint32_t charcode = m_CharCodes[i];
     if (charcode == CPDF_Font::kInvalidCharCode)
       continue;
-    if (count++ != index)
-      continue;
-    GetItemInfo(i, pInfo);
-    break;
+    if (count++ == index)
+      return GetItemInfo(i);
   }
+  return Item();
 }
 
 int CPDF_TextObject::CountWords() const {
@@ -106,9 +103,7 @@
   bool bInLatinWord = false;
   int nWords = 0;
   for (size_t i = 0, sz = CountChars(); i < sz; ++i) {
-    uint32_t charcode = CPDF_Font::kInvalidCharCode;
-    float unused_kerning;
-    GetCharInfo(i, &charcode, &unused_kerning);
+    uint32_t charcode = GetCharCode(i);
 
     WideString swUnicode = pFont->UnicodeFromCharCode(charcode);
     uint16_t unicode = 0;
@@ -133,9 +128,7 @@
   int nWords = 0;
   bool bInLatinWord = false;
   for (size_t i = 0, sz = CountChars(); i < sz; ++i) {
-    uint32_t charcode = CPDF_Font::kInvalidCharCode;
-    float unused_kerning;
-    GetCharInfo(i, &charcode, &unused_kerning);
+    uint32_t charcode = GetCharCode(i);
 
     WideString swUnicode = pFont->UnicodeFromCharCode(charcode);
     uint16_t unicode = 0;
@@ -155,7 +148,7 @@
 }
 
 std::unique_ptr<CPDF_TextObject> CPDF_TextObject::Clone() const {
-  auto obj = pdfium::MakeUnique<CPDF_TextObject>();
+  auto obj = std::make_unique<CPDF_TextObject>();
   obj->CopyData(this);
   obj->m_CharCodes = m_CharCodes;
   obj->m_CharPos = m_CharPos;
@@ -164,19 +157,11 @@
 }
 
 CPDF_PageObject::Type CPDF_TextObject::GetType() const {
-  return TEXT;
+  return Type::kText;
 }
 
 void CPDF_TextObject::Transform(const CFX_Matrix& matrix) {
-  CFX_Matrix text_matrix = GetTextMatrix() * matrix;
-
-  float* pTextMatrix = m_TextState.GetMutableMatrix();
-  pTextMatrix[0] = text_matrix.a;
-  pTextMatrix[1] = text_matrix.c;
-  pTextMatrix[2] = text_matrix.b;
-  pTextMatrix[3] = text_matrix.d;
-  m_Pos = CFX_PointF(text_matrix.e, text_matrix.f);
-  CalcPositionData(0);
+  SetTextMatrix(GetTextMatrix() * matrix);
   SetDirty(true);
 }
 
@@ -193,21 +178,33 @@
 }
 
 CFX_Matrix CPDF_TextObject::GetTextMatrix() const {
-  const float* pTextMatrix = m_TextState.GetMatrix();
+  pdfium::span<const float> pTextMatrix = m_TextState.GetMatrix();
   return CFX_Matrix(pTextMatrix[0], pTextMatrix[2], pTextMatrix[1],
                     pTextMatrix[3], m_Pos.x, m_Pos.y);
 }
 
+void CPDF_TextObject::SetTextMatrix(const CFX_Matrix& matrix) {
+  pdfium::span<float> pTextMatrix = m_TextState.GetMutableMatrix();
+  pTextMatrix[0] = matrix.a;
+  pTextMatrix[1] = matrix.c;
+  pTextMatrix[2] = matrix.b;
+  pTextMatrix[3] = matrix.d;
+  m_Pos = CFX_PointF(matrix.e, matrix.f);
+  CalcPositionDataInternal(GetFont());
+}
+
 void CPDF_TextObject::SetSegments(const ByteString* pStrs,
                                   const std::vector<float>& kernings,
                                   size_t nSegs) {
+  CHECK(nSegs);
   m_CharCodes.clear();
   m_CharPos.clear();
   RetainPtr<CPDF_Font> pFont = GetFont();
-  int nChars = 0;
+  size_t nChars = nSegs - 1;
   for (size_t i = 0; i < nSegs; ++i)
     nChars += pFont->CountChar(pStrs[i].AsStringView());
-  nChars += nSegs - 1;
+
+  CHECK(nChars);
   m_CharCodes.resize(nChars);
   m_CharPos.resize(nChars - 1);
   size_t index = 0;
@@ -215,7 +212,7 @@
     ByteStringView segment = pStrs[i].AsStringView();
     size_t offset = 0;
     while (offset < segment.GetLength()) {
-      ASSERT(index < m_CharCodes.size());
+      DCHECK(index < m_CharCodes.size());
       m_CharCodes[index++] = pFont->GetNextChar(segment, &offset);
     }
     if (i != nSegs - 1) {
@@ -227,22 +224,19 @@
 
 void CPDF_TextObject::SetText(const ByteString& str) {
   SetSegments(&str, std::vector<float>(), 1);
-  RecalcPositionData();
+  CalcPositionDataInternal(GetFont());
   SetDirty(true);
 }
 
 float CPDF_TextObject::GetCharWidth(uint32_t charcode) const {
-  float fontsize = GetFontSize() / 1000;
+  const float fontsize = GetFontSize() / 1000;
   RetainPtr<CPDF_Font> pFont = GetFont();
-  bool bVertWriting = false;
-  CPDF_CIDFont* pCIDFont = pFont->AsCIDFont();
-  if (pCIDFont)
-    bVertWriting = pCIDFont->IsVertWriting();
-  if (!bVertWriting)
+  const CPDF_CIDFont* pCIDFont = pFont->AsCIDFont();
+  if (!IsVertWritingCIDFont(pCIDFont))
     return pFont->GetCharWidthF(charcode) * fontsize;
 
-  uint16_t CID = pCIDFont->CIDFromCharCode(charcode);
-  return pCIDFont->GetVertWidth(CID) * fontsize;
+  uint16_t cid = pCIDFont->CIDFromCharCode(charcode);
+  return pCIDFont->GetVertWidth(cid) * fontsize;
 }
 
 RetainPtr<CPDF_Font> CPDF_TextObject::GetFont() const {
@@ -257,21 +251,32 @@
   return m_TextState.GetTextMode();
 }
 
-CFX_PointF CPDF_TextObject::CalcPositionData(float horz_scale) {
-  float curpos = 0;
-  float min_x = 10000 * 1.0f;
-  float max_x = -10000 * 1.0f;
-  float min_y = 10000 * 1.0f;
-  float max_y = -10000 * 1.0f;
-  RetainPtr<CPDF_Font> pFont = GetFont();
-  bool bVertWriting = false;
-  CPDF_CIDFont* pCIDFont = pFont->AsCIDFont();
-  if (pCIDFont)
-    bVertWriting = pCIDFont->IsVertWriting();
+void CPDF_TextObject::SetTextRenderMode(TextRenderingMode mode) {
+  m_TextState.SetTextMode(mode);
+  SetDirty(true);
+}
 
-  float fontsize = GetFontSize();
+CFX_PointF CPDF_TextObject::CalcPositionData(float horz_scale) {
+  RetainPtr<CPDF_Font> pFont = GetFont();
+  const float curpos = CalcPositionDataInternal(pFont);
+  if (IsVertWritingCIDFont(pFont->AsCIDFont()))
+    return {0, curpos};
+  return {curpos * horz_scale, 0};
+}
+
+float CPDF_TextObject::CalcPositionDataInternal(
+    const RetainPtr<CPDF_Font>& pFont) {
+  float curpos = 0;
+  float min_x = 10000.0f;
+  float max_x = -10000.0f;
+  float min_y = 10000.0f;
+  float max_y = -10000.0f;
+  const CPDF_CIDFont* pCIDFont = pFont->AsCIDFont();
+  const bool bVertWriting = IsVertWritingCIDFont(pCIDFont);
+  const float fontsize = GetFontSize();
+
   for (size_t i = 0; i < m_CharCodes.size(); ++i) {
-    uint32_t charcode = m_CharCodes[i];
+    const uint32_t charcode = m_CharCodes[i];
     if (i > 0) {
       if (charcode == CPDF_Font::kInvalidCharCode) {
         curpos -= (m_CharPos[i - 1] * fontsize) / 1000;
@@ -282,34 +287,29 @@
 
     FX_RECT char_rect = pFont->GetCharBBox(charcode);
     float charwidth;
-    if (!bVertWriting) {
-      min_y = std::min(
-          min_y, static_cast<float>(std::min(char_rect.top, char_rect.bottom)));
-      max_y = std::max(
-          max_y, static_cast<float>(std::max(char_rect.top, char_rect.bottom)));
-      float char_left = curpos + char_rect.left * fontsize / 1000;
-      float char_right = curpos + char_rect.right * fontsize / 1000;
-      min_x = std::min(min_x, std::min(char_left, char_right));
-      max_x = std::max(max_x, std::max(char_left, char_right));
-      charwidth = pFont->GetCharWidthF(charcode) * fontsize / 1000;
-    } else {
-      uint16_t CID = pCIDFont->CIDFromCharCode(charcode);
-      short vx;
-      short vy;
-      pCIDFont->GetVertOrigin(CID, vx, vy);
-      char_rect.left -= vx;
-      char_rect.right -= vx;
-      char_rect.top -= vy;
-      char_rect.bottom -= vy;
+    if (bVertWriting) {
+      uint16_t cid = pCIDFont->CIDFromCharCode(charcode);
+      CFX_Point16 vertical_origin = pCIDFont->GetVertOrigin(cid);
+      char_rect.Offset(-vertical_origin.x, -vertical_origin.y);
       min_x = std::min(
           min_x, static_cast<float>(std::min(char_rect.left, char_rect.right)));
       max_x = std::max(
           max_x, static_cast<float>(std::max(char_rect.left, char_rect.right)));
-      float char_top = curpos + char_rect.top * fontsize / 1000;
-      float char_bottom = curpos + char_rect.bottom * fontsize / 1000;
+      const float char_top = curpos + char_rect.top * fontsize / 1000;
+      const float char_bottom = curpos + char_rect.bottom * fontsize / 1000;
       min_y = std::min(min_y, std::min(char_top, char_bottom));
       max_y = std::max(max_y, std::max(char_top, char_bottom));
-      charwidth = pCIDFont->GetVertWidth(CID) * fontsize / 1000;
+      charwidth = pCIDFont->GetVertWidth(cid) * fontsize / 1000;
+    } else {
+      min_y = std::min(
+          min_y, static_cast<float>(std::min(char_rect.top, char_rect.bottom)));
+      max_y = std::max(
+          max_y, static_cast<float>(std::max(char_rect.top, char_rect.bottom)));
+      const float char_left = curpos + char_rect.left * fontsize / 1000;
+      const float char_right = curpos + char_rect.right * fontsize / 1000;
+      min_x = std::min(min_x, std::min(char_left, char_right));
+      max_x = std::max(max_x, std::max(char_left, char_right));
+      charwidth = pFont->GetCharWidthF(charcode) * fontsize / 1000;
     }
     curpos += charwidth;
     if (charcode == ' ' && (!pCIDFont || pCIDFont->GetCharSize(' ') == 1))
@@ -318,31 +318,23 @@
     curpos += m_TextState.GetCharSpace();
   }
 
-  CFX_PointF ret;
   if (bVertWriting) {
-    ret.y = curpos;
     min_x = min_x * fontsize / 1000;
     max_x = max_x * fontsize / 1000;
   } else {
-    ret.x = curpos * horz_scale;
     min_y = min_y * fontsize / 1000;
     max_y = max_y * fontsize / 1000;
   }
-  SetRect(
-      GetTextMatrix().TransformRect(CFX_FloatRect(min_x, min_y, max_x, max_y)));
 
-  if (!TextRenderingModeIsStrokeMode(m_TextState.GetTextMode()))
-    return ret;
+  SetOriginalRect(CFX_FloatRect(min_x, min_y, max_x, max_y));
+  CFX_FloatRect rect = GetTextMatrix().TransformRect(GetOriginalRect());
+  if (TextRenderingModeIsStrokeMode(m_TextState.GetTextMode())) {
+    // TODO(crbug.com/pdfium/1840): Does the original rect need a similar
+    // adjustment?
+    const float half_width = m_GraphState.GetLineWidth() / 2;
+    rect.Inflate(half_width, half_width);
+  }
+  SetRect(rect);
 
-  float half_width = m_GraphState.GetLineWidth() / 2;
-  m_Rect.left -= half_width;
-  m_Rect.right += half_width;
-  m_Rect.top += half_width;
-  m_Rect.bottom -= half_width;
-
-  return ret;
-}
-
-void CPDF_TextObject::RecalcPositionData() {
-  CalcPositionData(1);
+  return curpos;
 }
diff --git a/core/fpdfapi/page/cpdf_textobject.h b/core/fpdfapi/page/cpdf_textobject.h
index ad9918e..4a936bc 100644
--- a/core/fpdfapi/page/cpdf_textobject.h
+++ b/core/fpdfapi/page/cpdf_textobject.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,30 +7,33 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_TEXTOBJECT_H_
 #define CORE_FPDFAPI_PAGE_CPDF_TEXTOBJECT_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_pageobject.h"
+#include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 
-class CPDF_TextObjectItem {
- public:
-  CPDF_TextObjectItem();
-  ~CPDF_TextObjectItem();
-
-  uint32_t m_CharCode;
-  CFX_PointF m_Origin;
-};
-
 class CPDF_TextObject final : public CPDF_PageObject {
  public:
+  struct Item {
+    Item();
+    Item(const Item& that);
+    ~Item();
+
+    uint32_t m_CharCode = 0;
+    CFX_PointF m_Origin;
+  };
+
   explicit CPDF_TextObject(int32_t content_stream);
   CPDF_TextObject();
   ~CPDF_TextObject() override;
 
-  // CPDF_PageObject
+  // CPDF_PageObject:
   Type GetType() const override;
   void Transform(const CFX_Matrix& matrix) override;
   bool IsText() const override;
@@ -40,11 +43,11 @@
   std::unique_ptr<CPDF_TextObject> Clone() const;
 
   size_t CountItems() const;
-  void GetItemInfo(size_t index, CPDF_TextObjectItem* pInfo) const;
+  Item GetItemInfo(size_t index) const;
 
   size_t CountChars() const;
-  void GetCharInfo(size_t index, uint32_t* charcode, float* kerning) const;
-  void GetCharInfo(size_t index, CPDF_TextObjectItem* pInfo) const;
+  uint32_t GetCharCode(size_t index) const;
+  Item GetCharInfo(size_t index) const;
   float GetCharWidth(uint32_t charcode) const;
   int CountWords() const;
   WideString GetWordString(int nWordIndex) const;
@@ -56,21 +59,25 @@
   float GetFontSize() const;
 
   TextRenderingMode GetTextRenderMode() const;
+  void SetTextRenderMode(TextRenderingMode mode);
 
   void SetText(const ByteString& str);
   void SetPosition(const CFX_PointF& pos) { m_Pos = pos; }
 
-  void RecalcPositionData();
-
   const std::vector<uint32_t>& GetCharCodes() const { return m_CharCodes; }
   const std::vector<float>& GetCharPositions() const { return m_CharPos; }
 
+  // Caller is expected to call SetDirty(true) when done changing the object.
+  void SetTextMatrix(const CFX_Matrix& matrix);
+
   void SetSegments(const ByteString* pStrs,
                    const std::vector<float>& kernings,
                    size_t nSegs);
   CFX_PointF CalcPositionData(float horz_scale);
 
  private:
+  float CalcPositionDataInternal(const RetainPtr<CPDF_Font>& pFont);
+
   CFX_PointF m_Pos;
   std::vector<uint32_t> m_CharCodes;
   std::vector<float> m_CharPos;
diff --git a/core/fpdfapi/page/cpdf_textstate.cpp b/core/fpdfapi/page/cpdf_textstate.cpp
index b8019d5..6e48b87 100644
--- a/core/fpdfapi/page/cpdf_textstate.cpp
+++ b/core/fpdfapi/page/cpdf_textstate.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,12 @@
 
 #include "core/fpdfapi/page/cpdf_textstate.h"
 
+#include <math.h>
+
+#include <utility>
+
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
 
 CPDF_TextState::CPDF_TextState() = default;
 
@@ -22,8 +25,8 @@
   return m_Ref.GetObject()->m_pFont;
 }
 
-void CPDF_TextState::SetFont(const RetainPtr<CPDF_Font>& pFont) {
-  m_Ref.GetPrivateCopy()->SetFont(pFont);
+void CPDF_TextState::SetFont(RetainPtr<CPDF_Font> pFont) {
+  m_Ref.GetPrivateCopy()->SetFont(std::move(pFont));
 }
 
 float CPDF_TextState::GetFontSize() const {
@@ -34,11 +37,11 @@
   m_Ref.GetPrivateCopy()->m_FontSize = size;
 }
 
-const float* CPDF_TextState::GetMatrix() const {
+pdfium::span<const float> CPDF_TextState::GetMatrix() const {
   return m_Ref.GetObject()->m_Matrix;
 }
 
-float* CPDF_TextState::GetMutableMatrix() {
+pdfium::span<float> CPDF_TextState::GetMutableMatrix() {
   return m_Ref.GetPrivateCopy()->m_Matrix;
 }
 
@@ -70,26 +73,15 @@
   m_Ref.GetPrivateCopy()->m_TextMode = mode;
 }
 
-const float* CPDF_TextState::GetCTM() const {
+pdfium::span<const float> CPDF_TextState::GetCTM() const {
   return m_Ref.GetObject()->m_CTM;
 }
 
-float* CPDF_TextState::GetMutableCTM() {
+pdfium::span<float> CPDF_TextState::GetMutableCTM() {
   return m_Ref.GetPrivateCopy()->m_CTM;
 }
 
-CPDF_TextState::TextData::TextData()
-    : m_pFont(nullptr),
-      m_pDocument(nullptr),
-      m_FontSize(1.0f),
-      m_CharSpace(0),
-      m_WordSpace(0),
-      m_TextMode(TextRenderingMode::MODE_FILL) {
-  m_Matrix[0] = m_Matrix[3] = 1.0f;
-  m_Matrix[1] = m_Matrix[2] = 0;
-  m_CTM[0] = m_CTM[3] = 1.0f;
-  m_CTM[1] = m_CTM[2] = 0;
-}
+CPDF_TextState::TextData::TextData() = default;
 
 CPDF_TextState::TextData::TextData(const TextData& that)
     : m_pFont(that.m_pFont),
@@ -105,8 +97,8 @@
     m_CTM[i] = that.m_CTM[i];
 
   if (m_pDocument && m_pFont) {
-    auto* pPageData = CPDF_DocPageData::FromDocument(m_pDocument.Get());
-    m_pFont = pPageData->GetFont(m_pFont->GetFontDict());
+    auto* pPageData = CPDF_DocPageData::FromDocument(m_pDocument);
+    m_pFont = pPageData->GetFont(m_pFont->GetMutableFontDict());
   }
 }
 
@@ -116,9 +108,9 @@
   return pdfium::MakeRetain<CPDF_TextState::TextData>(*this);
 }
 
-void CPDF_TextState::TextData::SetFont(const RetainPtr<CPDF_Font>& pFont) {
+void CPDF_TextState::TextData::SetFont(RetainPtr<CPDF_Font> pFont) {
   m_pDocument = pFont ? pFont->GetDocument() : nullptr;
-  m_pFont = pFont;
+  m_pFont = std::move(pFont);
 }
 
 float CPDF_TextState::TextData::GetFontSizeH() const {
diff --git a/core/fpdfapi/page/cpdf_textstate.h b/core/fpdfapi/page/cpdf_textstate.h
index 5cc0c1d..b462606 100644
--- a/core/fpdfapi/page/cpdf_textstate.h
+++ b/core/fpdfapi/page/cpdf_textstate.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,6 +10,7 @@
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/shared_copy_on_write.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Document;
 class CPDF_Font;
@@ -36,13 +37,13 @@
   void Emplace();
 
   RetainPtr<CPDF_Font> GetFont() const;
-  void SetFont(const RetainPtr<CPDF_Font>& pFont);
+  void SetFont(RetainPtr<CPDF_Font> pFont);
 
   float GetFontSize() const;
   void SetFontSize(float size);
 
-  const float* GetMatrix() const;
-  float* GetMutableMatrix();
+  pdfium::span<const float> GetMatrix() const;
+  pdfium::span<float> GetMutableMatrix();
 
   float GetCharSpace() const;
   void SetCharSpace(float sp);
@@ -55,29 +56,28 @@
   TextRenderingMode GetTextMode() const;
   void SetTextMode(TextRenderingMode mode);
 
-  const float* GetCTM() const;
-  float* GetMutableCTM();
+  pdfium::span<const float> GetCTM() const;
+  pdfium::span<float> GetMutableCTM();
 
  private:
   class TextData final : public Retainable {
    public:
-    template <typename T, typename... Args>
-    friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+    CONSTRUCT_VIA_MAKE_RETAIN;
 
     RetainPtr<TextData> Clone() const;
 
-    void SetFont(const RetainPtr<CPDF_Font>& pFont);
+    void SetFont(RetainPtr<CPDF_Font> pFont);
     float GetFontSizeV() const;
     float GetFontSizeH() const;
 
     RetainPtr<CPDF_Font> m_pFont;
-    UnownedPtr<CPDF_Document> m_pDocument;
-    float m_FontSize;
-    float m_CharSpace;
-    float m_WordSpace;
-    TextRenderingMode m_TextMode;
-    float m_Matrix[4];
-    float m_CTM[4];
+    UnownedPtr<const CPDF_Document> m_pDocument;
+    float m_FontSize = 1.0f;
+    float m_CharSpace = 0.0f;
+    float m_WordSpace = 0.0f;
+    TextRenderingMode m_TextMode = TextRenderingMode::MODE_FILL;
+    float m_Matrix[4] = {1.0f, 0.0f, 0.0f, 1.0f};
+    float m_CTM[4] = {1.0f, 0.0f, 0.0f, 1.0f};
 
    private:
     TextData();
diff --git a/core/fpdfapi/page/cpdf_tilingpattern.cpp b/core/fpdfapi/page/cpdf_tilingpattern.cpp
index 46a59db..d7b2fd3 100644
--- a/core/fpdfapi/page/cpdf_tilingpattern.cpp
+++ b/core/fpdfapi/page/cpdf_tilingpattern.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,19 +6,23 @@
 
 #include "core/fpdfapi/page/cpdf_tilingpattern.h"
 
+#include <math.h>
+
+#include <utility>
+
 #include "core/fpdfapi/page/cpdf_allstates.h"
 #include "core/fpdfapi/page/cpdf_form.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 CPDF_TilingPattern::CPDF_TilingPattern(CPDF_Document* pDoc,
-                                       CPDF_Object* pPatternObj,
+                                       RetainPtr<CPDF_Object> pPatternObj,
                                        const CFX_Matrix& parentMatrix)
-    : CPDF_Pattern(pDoc, pPatternObj, parentMatrix) {
-  ASSERT(document());
+    : CPDF_Pattern(pDoc, std::move(pPatternObj), parentMatrix) {
+  DCHECK(document());
   m_bColored = pattern_obj()->GetDict()->GetIntegerFor("PaintType") == 1;
   SetPatternToFormMatrix();
 }
@@ -30,17 +34,18 @@
 }
 
 std::unique_ptr<CPDF_Form> CPDF_TilingPattern::Load(CPDF_PageObject* pPageObj) {
-  const CPDF_Dictionary* pDict = pattern_obj()->GetDict();
+  RetainPtr<const CPDF_Dictionary> pDict = pattern_obj()->GetDict();
   m_bColored = pDict->GetIntegerFor("PaintType") == 1;
-  m_XStep = static_cast<float>(fabs(pDict->GetNumberFor("XStep")));
-  m_YStep = static_cast<float>(fabs(pDict->GetNumberFor("YStep")));
+  m_XStep = fabsf(pDict->GetFloatFor("XStep"));
+  m_YStep = fabsf(pDict->GetFloatFor("YStep"));
 
-  CPDF_Stream* pStream = pattern_obj()->AsStream();
+  RetainPtr<CPDF_Stream> pStream = ToStream(pattern_obj());
   if (!pStream)
     return nullptr;
 
   const CFX_Matrix& matrix = parent_matrix();
-  auto form = pdfium::MakeUnique<CPDF_Form>(document(), nullptr, pStream);
+  auto form =
+      std::make_unique<CPDF_Form>(document(), nullptr, std::move(pStream));
 
   CPDF_AllStates allStates;
   allStates.m_ColorState.Emplace();
diff --git a/core/fpdfapi/page/cpdf_tilingpattern.h b/core/fpdfapi/page/cpdf_tilingpattern.h
index 134da8e..519e457 100644
--- a/core/fpdfapi/page/cpdf_tilingpattern.h
+++ b/core/fpdfapi/page/cpdf_tilingpattern.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,7 @@
 
 #include "core/fpdfapi/page/cpdf_pattern.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Document;
 class CPDF_Form;
@@ -20,9 +20,7 @@
 
 class CPDF_TilingPattern final : public CPDF_Pattern {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
+  CONSTRUCT_VIA_MAKE_RETAIN;
   ~CPDF_TilingPattern() override;
 
   // CPDF_Pattern:
@@ -37,15 +35,15 @@
 
  private:
   CPDF_TilingPattern(CPDF_Document* pDoc,
-                     CPDF_Object* pPatternObj,
+                     RetainPtr<CPDF_Object> pPatternObj,
                      const CFX_Matrix& parentMatrix);
   CPDF_TilingPattern(const CPDF_TilingPattern&) = delete;
   CPDF_TilingPattern& operator=(const CPDF_TilingPattern&) = delete;
 
   bool m_bColored;
   CFX_FloatRect m_BBox;
-  float m_XStep;
-  float m_YStep;
+  float m_XStep = 0.0f;
+  float m_YStep = 0.0f;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_TILINGPATTERN_H_
diff --git a/core/fpdfapi/page/cpdf_transferfunc.cpp b/core/fpdfapi/page/cpdf_transferfunc.cpp
index 9e3092b..f7aa5e5 100644
--- a/core/fpdfapi/page/cpdf_transferfunc.cpp
+++ b/core/fpdfapi/page/cpdf_transferfunc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,39 +6,41 @@
 
 #include "core/fpdfapi/page/cpdf_transferfunc.h"
 
+#include <stdint.h>
+
 #include <utility>
 
 #include "core/fpdfapi/page/cpdf_transferfuncdib.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fxcrt/fixed_uninit_data_vector.h"
 #include "core/fxge/dib/cfx_dibbase.h"
+#include "third_party/base/check_op.h"
 
-CPDF_TransferFunc::CPDF_TransferFunc(CPDF_Document* pDoc,
-                                     bool bIdentify,
-                                     std::vector<uint8_t> samples_r,
-                                     std::vector<uint8_t> samples_g,
-                                     std::vector<uint8_t> samples_b)
-    : m_pPDFDoc(pDoc),
-      m_bIdentity(bIdentify),
+CPDF_TransferFunc::CPDF_TransferFunc(bool bIdentify,
+                                     FixedUninitDataVector<uint8_t> samples_r,
+                                     FixedUninitDataVector<uint8_t> samples_g,
+                                     FixedUninitDataVector<uint8_t> samples_b)
+    : m_bIdentity(bIdentify),
       m_SamplesR(std::move(samples_r)),
       m_SamplesG(std::move(samples_g)),
       m_SamplesB(std::move(samples_b)) {
-  ASSERT(m_SamplesR.size() == kChannelSampleSize);
-  ASSERT(m_SamplesG.size() == kChannelSampleSize);
-  ASSERT(m_SamplesB.size() == kChannelSampleSize);
+  DCHECK_EQ(m_SamplesR.size(), kChannelSampleSize);
+  DCHECK_EQ(m_SamplesG.size(), kChannelSampleSize);
+  DCHECK_EQ(m_SamplesB.size(), kChannelSampleSize);
 }
 
 CPDF_TransferFunc::~CPDF_TransferFunc() = default;
 
 FX_COLORREF CPDF_TransferFunc::TranslateColor(FX_COLORREF colorref) const {
-  return FXSYS_BGR(m_SamplesB[FXSYS_GetBValue(colorref)],
-                   m_SamplesG[FXSYS_GetGValue(colorref)],
-                   m_SamplesR[FXSYS_GetRValue(colorref)]);
+  return FXSYS_BGR(m_SamplesB.span()[FXSYS_GetBValue(colorref)],
+                   m_SamplesG.span()[FXSYS_GetGValue(colorref)],
+                   m_SamplesR.span()[FXSYS_GetRValue(colorref)]);
 }
 
 RetainPtr<CFX_DIBBase> CPDF_TransferFunc::TranslateImage(
-    const RetainPtr<CFX_DIBBase>& pSrc) {
-  RetainPtr<CPDF_TransferFunc> pHolder(this);
-  return pdfium::MakeRetain<CPDF_TransferFuncDIB>(pSrc, pHolder);
+    RetainPtr<CFX_DIBBase> pSrc) {
+  return pdfium::MakeRetain<CPDF_TransferFuncDIB>(std::move(pSrc),
+                                                  pdfium::WrapRetain(this));
 }
 
 pdfium::span<const uint8_t> CPDF_TransferFunc::GetSamplesR() const {
diff --git a/core/fpdfapi/page/cpdf_transferfunc.h b/core/fpdfapi/page/cpdf_transferfunc.h
index b2d997e..11ea98d 100644
--- a/core/fpdfapi/page/cpdf_transferfunc.h
+++ b/core/fpdfapi/page/cpdf_transferfunc.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,28 +7,24 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNC_H_
 #define CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNC_H_
 
-#include <vector>
+#include <stdint.h>
 
+#include "core/fxcrt/fixed_uninit_data_vector.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/span.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/containers/span.h"
 
-class CPDF_Document;
 class CFX_DIBBase;
 
 class CPDF_TransferFunc final : public Retainable, public Observable {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   static constexpr size_t kChannelSampleSize = 256;
 
   FX_COLORREF TranslateColor(FX_COLORREF colorref) const;
-  RetainPtr<CFX_DIBBase> TranslateImage(const RetainPtr<CFX_DIBBase>& pSrc);
-
-  const CPDF_Document* GetDocument() const { return m_pPDFDoc.Get(); }
+  RetainPtr<CFX_DIBBase> TranslateImage(RetainPtr<CFX_DIBBase> pSrc);
 
   // Spans are |kChannelSampleSize| in size.
   pdfium::span<const uint8_t> GetSamplesR() const;
@@ -38,18 +34,16 @@
   bool GetIdentity() const { return m_bIdentity; }
 
  private:
-  CPDF_TransferFunc(CPDF_Document* pDoc,
-                    bool bIdentify,
-                    std::vector<uint8_t> samples_r,
-                    std::vector<uint8_t> samples_g,
-                    std::vector<uint8_t> samples_b);
+  CPDF_TransferFunc(bool bIdentify,
+                    FixedUninitDataVector<uint8_t> samples_r,
+                    FixedUninitDataVector<uint8_t> samples_g,
+                    FixedUninitDataVector<uint8_t> samples_b);
   ~CPDF_TransferFunc() override;
 
-  UnownedPtr<CPDF_Document> const m_pPDFDoc;
   const bool m_bIdentity;
-  const std::vector<uint8_t> m_SamplesR;
-  const std::vector<uint8_t> m_SamplesG;
-  const std::vector<uint8_t> m_SamplesB;
+  const FixedUninitDataVector<uint8_t> m_SamplesR;
+  const FixedUninitDataVector<uint8_t> m_SamplesG;
+  const FixedUninitDataVector<uint8_t> m_SamplesB;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNC_H_
diff --git a/core/fpdfapi/page/cpdf_transferfuncdib.cpp b/core/fpdfapi/page/cpdf_transferfuncdib.cpp
index f985616..24e4007 100644
--- a/core/fpdfapi/page/cpdf_transferfuncdib.cpp
+++ b/core/fpdfapi/page/cpdf_transferfuncdib.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,50 +6,48 @@
 
 #include "core/fpdfapi/page/cpdf_transferfuncdib.h"
 
-#include <vector>
+#include <utility>
 
 #include "build/build_config.h"
 #include "core/fpdfapi/page/cpdf_transferfunc.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "third_party/base/compiler_specific.h"
+#include "core/fxge/calculate_pitch.h"
+#include "third_party/base/check.h"
 
 CPDF_TransferFuncDIB::CPDF_TransferFuncDIB(
-    const RetainPtr<CFX_DIBBase>& pSrc,
-    const RetainPtr<CPDF_TransferFunc>& pTransferFunc)
-    : m_pSrc(pSrc),
-      m_pTransferFunc(pTransferFunc),
-      m_RampR(pTransferFunc->GetSamplesR()),
-      m_RampG(pTransferFunc->GetSamplesG()),
-      m_RampB(pTransferFunc->GetSamplesB()) {
-  m_Width = pSrc->GetWidth();
-  m_Height = pSrc->GetHeight();
-  FXDIB_Format format = GetDestFormat();
-  m_bpp = GetBppFromFormat(format);
-  m_AlphaFlag = GetAlphaFlagFromFormat(format);
-  m_Pitch = (m_Width * m_bpp + 31) / 32 * 4;
-  m_pPalette.reset();
+    RetainPtr<CFX_DIBBase> pSrc,
+    RetainPtr<CPDF_TransferFunc> pTransferFunc)
+    : m_pSrc(std::move(pSrc)),
+      m_pTransferFunc(std::move(pTransferFunc)),
+      m_RampR(m_pTransferFunc->GetSamplesR()),
+      m_RampG(m_pTransferFunc->GetSamplesG()),
+      m_RampB(m_pTransferFunc->GetSamplesB()) {
+  m_Width = m_pSrc->GetWidth();
+  m_Height = m_pSrc->GetHeight();
+  m_Format = GetDestFormat();
+  m_Pitch = fxge::CalculatePitch32OrDie(GetBppFromFormat(m_Format), m_Width);
   m_Scanline.resize(m_Pitch);
+  DCHECK(m_palette.empty());
 }
 
 CPDF_TransferFuncDIB::~CPDF_TransferFuncDIB() = default;
 
 FXDIB_Format CPDF_TransferFuncDIB::GetDestFormat() const {
-  if (m_pSrc->IsAlphaMask())
-    return FXDIB_8bppMask;
+  if (m_pSrc->IsMaskFormat())
+    return FXDIB_Format::k8bppMask;
 
-#if defined(OS_MACOSX)
-  return m_pSrc->HasAlpha() ? FXDIB_Argb : FXDIB_Rgb32;
-#else
-  return m_pSrc->HasAlpha() ? FXDIB_Argb : FXDIB_Rgb;
-#endif
+  if (m_pSrc->IsAlphaFormat())
+    return FXDIB_Format::kArgb;
+
+  return CFX_DIBBase::kPlatformRGBFormat;
 }
 
 void CPDF_TransferFuncDIB::TranslateScanline(
-    const uint8_t* src_buf,
-    std::vector<uint8_t>* dest_buf) const {
+    pdfium::span<const uint8_t> src_span) const {
+  const uint8_t* src_buf = src_span.data();
   bool bSkip = false;
   switch (m_pSrc->GetFormat()) {
-    case FXDIB_1bppRgb: {
+    case FXDIB_Format::k1bppRgb: {
       int r0 = m_RampR[0];
       int g0 = m_RampG[0];
       int b0 = m_RampB[0];
@@ -59,84 +57,84 @@
       int index = 0;
       for (int i = 0; i < m_Width; i++) {
         if (src_buf[i / 8] & (1 << (7 - i % 8))) {
-          (*dest_buf)[index++] = b1;
-          (*dest_buf)[index++] = g1;
-          (*dest_buf)[index++] = r1;
+          m_Scanline[index++] = b1;
+          m_Scanline[index++] = g1;
+          m_Scanline[index++] = r1;
         } else {
-          (*dest_buf)[index++] = b0;
-          (*dest_buf)[index++] = g0;
-          (*dest_buf)[index++] = r0;
+          m_Scanline[index++] = b0;
+          m_Scanline[index++] = g0;
+          m_Scanline[index++] = r0;
         }
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
         index++;
 #endif
       }
       break;
     }
-    case FXDIB_1bppMask: {
+    case FXDIB_Format::k1bppMask: {
       int m0 = m_RampR[0];
       int m1 = m_RampR[255];
       int index = 0;
       for (int i = 0; i < m_Width; i++) {
         if (src_buf[i / 8] & (1 << (7 - i % 8)))
-          (*dest_buf)[index++] = m1;
+          m_Scanline[index++] = m1;
         else
-          (*dest_buf)[index++] = m0;
+          m_Scanline[index++] = m0;
       }
       break;
     }
-    case FXDIB_8bppRgb: {
-      FX_ARGB* pPal = m_pSrc->GetPalette();
+    case FXDIB_Format::k8bppRgb: {
+      pdfium::span<const uint32_t> src_palette = m_pSrc->GetPaletteSpan();
       int index = 0;
       for (int i = 0; i < m_Width; i++) {
-        if (pPal) {
-          FX_ARGB src_argb = pPal[*src_buf];
-          (*dest_buf)[index++] = m_RampB[FXARGB_R(src_argb)];
-          (*dest_buf)[index++] = m_RampG[FXARGB_G(src_argb)];
-          (*dest_buf)[index++] = m_RampR[FXARGB_B(src_argb)];
+        if (m_pSrc->HasPalette()) {
+          FX_ARGB src_argb = src_palette[*src_buf];
+          m_Scanline[index++] = m_RampB[FXARGB_R(src_argb)];
+          m_Scanline[index++] = m_RampG[FXARGB_G(src_argb)];
+          m_Scanline[index++] = m_RampR[FXARGB_B(src_argb)];
         } else {
           uint32_t src_byte = *src_buf;
-          (*dest_buf)[index++] = m_RampB[src_byte];
-          (*dest_buf)[index++] = m_RampG[src_byte];
-          (*dest_buf)[index++] = m_RampR[src_byte];
+          m_Scanline[index++] = m_RampB[src_byte];
+          m_Scanline[index++] = m_RampG[src_byte];
+          m_Scanline[index++] = m_RampR[src_byte];
         }
         src_buf++;
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
         index++;
 #endif
       }
       break;
     }
-    case FXDIB_8bppMask: {
+    case FXDIB_Format::k8bppMask: {
       int index = 0;
       for (int i = 0; i < m_Width; i++)
-        (*dest_buf)[index++] = m_RampR[*(src_buf++)];
+        m_Scanline[index++] = m_RampR[*(src_buf++)];
       break;
     }
-    case FXDIB_Rgb: {
+    case FXDIB_Format::kRgb: {
       int index = 0;
       for (int i = 0; i < m_Width; i++) {
-        (*dest_buf)[index++] = m_RampB[*(src_buf++)];
-        (*dest_buf)[index++] = m_RampG[*(src_buf++)];
-        (*dest_buf)[index++] = m_RampR[*(src_buf++)];
-#if defined(OS_MACOSX)
+        m_Scanline[index++] = m_RampB[*(src_buf++)];
+        m_Scanline[index++] = m_RampG[*(src_buf++)];
+        m_Scanline[index++] = m_RampR[*(src_buf++)];
+#if BUILDFLAG(IS_APPLE)
         index++;
 #endif
       }
       break;
     }
-    case FXDIB_Rgb32:
+    case FXDIB_Format::kRgb32:
       bSkip = true;
-      FALLTHROUGH;
-    case FXDIB_Argb: {
+      [[fallthrough]];
+    case FXDIB_Format::kArgb: {
       int index = 0;
       for (int i = 0; i < m_Width; i++) {
-        (*dest_buf)[index++] = m_RampB[*(src_buf++)];
-        (*dest_buf)[index++] = m_RampG[*(src_buf++)];
-        (*dest_buf)[index++] = m_RampR[*(src_buf++)];
+        m_Scanline[index++] = m_RampB[*(src_buf++)];
+        m_Scanline[index++] = m_RampG[*(src_buf++)];
+        m_Scanline[index++] = m_RampR[*(src_buf++)];
         if (!bSkip) {
-          (*dest_buf)[index++] = *src_buf;
-#if defined(OS_MACOSX)
+          m_Scanline[index++] = *src_buf;
+#if BUILDFLAG(IS_APPLE)
         } else {
           index++;
 #endif
@@ -150,56 +148,7 @@
   }
 }
 
-void CPDF_TransferFuncDIB::TranslateDownSamples(uint8_t* dest_buf,
-                                                const uint8_t* src_buf,
-                                                int pixels,
-                                                int Bpp) const {
-  if (Bpp == 8) {
-    for (int i = 0; i < pixels; i++)
-      *dest_buf++ = m_RampR[*(src_buf++)];
-  } else if (Bpp == 24) {
-    for (int i = 0; i < pixels; i++) {
-      *dest_buf++ = m_RampB[*(src_buf++)];
-      *dest_buf++ = m_RampG[*(src_buf++)];
-      *dest_buf++ = m_RampR[*(src_buf++)];
-    }
-  } else {
-#if defined(OS_MACOSX)
-    if (!m_pSrc->HasAlpha()) {
-      for (int i = 0; i < pixels; i++) {
-        *dest_buf++ = m_RampB[*(src_buf++)];
-        *dest_buf++ = m_RampG[*(src_buf++)];
-        *dest_buf++ = m_RampR[*(src_buf++)];
-        dest_buf++;
-        src_buf++;
-      }
-    } else {
-#endif
-      for (int i = 0; i < pixels; i++) {
-        *dest_buf++ = m_RampB[*(src_buf++)];
-        *dest_buf++ = m_RampG[*(src_buf++)];
-        *dest_buf++ = m_RampR[*(src_buf++)];
-        *dest_buf++ = *(src_buf++);
-      }
-#if defined(OS_MACOSX)
-    }
-#endif
-  }
-}
-
-const uint8_t* CPDF_TransferFuncDIB::GetScanline(int line) const {
-  TranslateScanline(m_pSrc->GetScanline(line), &m_Scanline);
-  return m_Scanline.data();
-}
-
-void CPDF_TransferFuncDIB::DownSampleScanline(int line,
-                                              uint8_t* dest_scan,
-                                              int dest_bpp,
-                                              int dest_width,
-                                              bool bFlipX,
-                                              int clip_left,
-                                              int clip_width) const {
-  m_pSrc->DownSampleScanline(line, dest_scan, dest_bpp, dest_width, bFlipX,
-                             clip_left, clip_width);
-  TranslateDownSamples(dest_scan, dest_scan, clip_width, dest_bpp);
+pdfium::span<const uint8_t> CPDF_TransferFuncDIB::GetScanline(int line) const {
+  TranslateScanline(m_pSrc->GetScanline(line));
+  return m_Scanline;
 }
diff --git a/core/fpdfapi/page/cpdf_transferfuncdib.h b/core/fpdfapi/page/cpdf_transferfuncdib.h
index c30718a..8173109 100644
--- a/core/fpdfapi/page/cpdf_transferfuncdib.h
+++ b/core/fpdfapi/page/cpdf_transferfuncdib.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,49 +7,36 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNCDIB_H_
 #define CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNCDIB_H_
 
-#include <vector>
+#include <stdint.h>
 
+#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;
 
 class CPDF_TransferFuncDIB final : public CFX_DIBBase {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  void TranslateScanline(const uint8_t* src_buf,
-                         std::vector<uint8_t>* dest_buf) const;
-  void TranslateDownSamples(uint8_t* dest_buf,
-                            const uint8_t* src_buf,
-                            int pixels,
-                            int Bpp) const;
-
- private:
-  CPDF_TransferFuncDIB(const RetainPtr<CFX_DIBBase>& pSrc,
-                       const RetainPtr<CPDF_TransferFunc>& pTransferFunc);
-  ~CPDF_TransferFuncDIB() override;
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // CFX_DIBBase:
-  const uint8_t* GetScanline(int line) const override;
-  void DownSampleScanline(int line,
-                          uint8_t* dest_scan,
-                          int dest_bpp,
-                          int dest_width,
-                          bool bFlipX,
-                          int clip_left,
-                          int clip_width) const override;
+  pdfium::span<const uint8_t> GetScanline(int line) const override;
 
+ private:
+  CPDF_TransferFuncDIB(RetainPtr<CFX_DIBBase> pSrc,
+                       RetainPtr<CPDF_TransferFunc> pTransferFunc);
+  ~CPDF_TransferFuncDIB() override;
+
+  void TranslateScanline(pdfium::span<const uint8_t> src_span) const;
   FXDIB_Format GetDestFormat() const;
 
   RetainPtr<CFX_DIBBase> const m_pSrc;
-  mutable std::vector<uint8_t> m_Scanline;
   RetainPtr<CPDF_TransferFunc> const m_pTransferFunc;
   const pdfium::span<const uint8_t> m_RampR;
   const pdfium::span<const uint8_t> m_RampG;
   const pdfium::span<const uint8_t> m_RampB;
+  mutable DataVector<uint8_t> m_Scanline;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNCDIB_H_
diff --git a/core/fpdfapi/page/cpdf_transparency.cpp b/core/fpdfapi/page/cpdf_transparency.cpp
index f9be541..811160d 100644
--- a/core/fpdfapi/page/cpdf_transparency.cpp
+++ b/core/fpdfapi/page/cpdf_transparency.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,3 +7,6 @@
 CPDF_Transparency::CPDF_Transparency() = default;
 
 CPDF_Transparency::CPDF_Transparency(const CPDF_Transparency& other) = default;
+
+CPDF_Transparency& CPDF_Transparency::operator=(
+    const CPDF_Transparency& other) = default;
diff --git a/core/fpdfapi/page/cpdf_transparency.h b/core/fpdfapi/page/cpdf_transparency.h
index 6d4972d..a2e0a25 100644
--- a/core/fpdfapi/page/cpdf_transparency.h
+++ b/core/fpdfapi/page/cpdf_transparency.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,6 +10,7 @@
   CPDF_Transparency();
 
   CPDF_Transparency(const CPDF_Transparency& other);
+  CPDF_Transparency& operator=(const CPDF_Transparency& other);
 
   bool IsGroup() const { return m_bGroup; }
   bool IsIsolated() const { return m_bIsolated; }
diff --git a/core/fpdfapi/page/ipdf_page.h b/core/fpdfapi/page/ipdf_page.h
index 9b28559..a71106b 100644
--- a/core/fpdfapi/page/ipdf_page.h
+++ b/core/fpdfapi/page/ipdf_page.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CPDF_Document;
 class CPDF_Page;
@@ -35,12 +35,12 @@
   virtual CFX_Matrix GetDisplayMatrix(const FX_RECT& rect,
                                       int iRotate) const = 0;
 
-  virtual Optional<CFX_PointF> DeviceToPage(
+  virtual absl::optional<CFX_PointF> DeviceToPage(
       const FX_RECT& rect,
       int rotate,
       const CFX_PointF& device_point) const = 0;
 
-  virtual Optional<CFX_PointF> PageToDevice(
+  virtual absl::optional<CFX_PointF> PageToDevice(
       const FX_RECT& rect,
       int rotate,
       const CFX_PointF& page_point) const = 0;
diff --git a/core/fpdfapi/page/test_with_page_module.cpp b/core/fpdfapi/page/test_with_page_module.cpp
new file mode 100644
index 0000000..0515ad9
--- /dev/null
+++ b/core/fpdfapi/page/test_with_page_module.cpp
@@ -0,0 +1,15 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/page/test_with_page_module.h"
+
+#include "core/fpdfapi/page/cpdf_pagemodule.h"
+
+void TestWithPageModule::SetUp() {
+  CPDF_PageModule::Create();
+}
+
+void TestWithPageModule::TearDown() {
+  CPDF_PageModule::Destroy();
+}
diff --git a/core/fpdfapi/page/test_with_page_module.h b/core/fpdfapi/page/test_with_page_module.h
new file mode 100644
index 0000000..0b39b84
--- /dev/null
+++ b/core/fpdfapi/page/test_with_page_module.h
@@ -0,0 +1,16 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FPDFAPI_PAGE_TEST_WITH_PAGE_MODULE_H_
+#define CORE_FPDFAPI_PAGE_TEST_WITH_PAGE_MODULE_H_
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+class TestWithPageModule : public testing::Test {
+ public:
+  void SetUp() override;
+  void TearDown() override;
+};
+
+#endif  // CORE_FPDFAPI_PAGE_TEST_WITH_PAGE_MODULE_H_
diff --git a/core/fpdfapi/parser/Android.bp b/core/fpdfapi/parser/Android.bp
index cacb42e..0ad9807 100644
--- a/core/fpdfapi/parser/Android.bp
+++ b/core/fpdfapi/parser/Android.bp
@@ -13,11 +13,8 @@
 
     visibility: ["//external/pdfium:__subpackages__"],
 
-    header_libs: [
-        "libpdfium-constants",
-    ],
-
     static_libs: [
+        "libpdfium-constants",
         "libpdfium-fdrm",
         "libpdfium-fxcodec",
         "libpdfium-fxcrt",
diff --git a/core/fpdfapi/parser/BUILD.gn b/core/fpdfapi/parser/BUILD.gn
index ad8a783..e3152d8 100644
--- a/core/fpdfapi/parser/BUILD.gn
+++ b/core/fpdfapi/parser/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -73,14 +73,16 @@
     "fpdf_parser_decode.h",
     "fpdf_parser_utility.cpp",
     "fpdf_parser_utility.h",
+    "object_tree_traversal_util.cpp",
+    "object_tree_traversal_util.h",
   ]
-  configs += [ "../../../:pdfium_core_config" ]
+  configs += [ "../../../:pdfium_strict_config" ]
   deps = [
     "../../../constants",
     "../../fdrm",
     "../../fxcodec",
-    "../../fxcrt",
   ]
+  public_deps = [ "../../fxcrt" ]
   allow_circular_includes_from = []
   visibility = [ "../../../*" ]
 
@@ -90,20 +92,36 @@
       "cpdf_seekablemultistream.h",
     ]
   }
-  if (pdf_use_skia || pdf_use_skia_paths) {
+  if (pdf_use_skia) {
     deps += [ "../../fxge" ]
     allow_circular_includes_from += [ "../../fxge" ]
   }
 }
 
+source_set("unit_test_support") {
+  testonly = true
+  sources = [
+    "cpdf_test_document.cpp",
+    "cpdf_test_document.h",
+  ]
+  configs += [ "../../../:pdfium_strict_config" ]
+  deps = [
+    ":parser",
+    "../page",
+    "../render",
+  ]
+}
+
 pdfium_unittest_source_set("unittests") {
   sources = [
     "cpdf_array_unittest.cpp",
     "cpdf_cross_ref_avail_unittest.cpp",
+    "cpdf_dictionary_unittest.cpp",
     "cpdf_document_unittest.cpp",
     "cpdf_hint_tables_unittest.cpp",
     "cpdf_indirect_object_holder_unittest.cpp",
     "cpdf_object_avail_unittest.cpp",
+    "cpdf_object_stream_unittest.cpp",
     "cpdf_object_unittest.cpp",
     "cpdf_object_walker_unittest.cpp",
     "cpdf_page_object_avail_unittest.cpp",
@@ -117,8 +135,10 @@
   ]
   deps = [
     ":parser",
+    ":unit_test_support",
     "../../../constants",
     "../page",
+    "../page:unit_test_support",
     "../render",
   ]
   pdfium_root_dir = "../../../"
@@ -126,10 +146,6 @@
   if (pdf_enable_xfa) {
     sources += [ "cpdf_seekablemultistream_unittest.cpp" ]
   }
-  if (is_clang) {
-    # Suppress no override warning for overridden functions.
-    cflags = [ "-Wno-inconsistent-missing-override" ]
-  }
 }
 
 pdfium_embeddertest_source_set("embeddertests") {
@@ -137,7 +153,11 @@
     "cpdf_parser_embeddertest.cpp",
     "cpdf_security_handler_embeddertest.cpp",
     "fpdf_parser_decode_embeddertest.cpp",
+    "object_tree_traversal_util_embeddertest.cpp",
   ]
-  deps = [ ":parser" ]
+  deps = [
+    ":parser",
+    "../../fxge",
+  ]
   pdfium_root_dir = "../../../"
 }
diff --git a/core/fpdfapi/parser/cfdf_document.cpp b/core/fpdfapi/parser/cfdf_document.cpp
index 054f999..ea34fbe 100644
--- a/core/fpdfapi/parser/cfdf_document.cpp
+++ b/core/fpdfapi/parser/cfdf_document.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,45 +13,44 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
-#include "core/fxcrt/cfx_readonlymemorystream.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/span.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
+#include "core/fxcrt/fx_string_wrappers.h"
+#include "third_party/base/containers/span.h"
 
 CFDF_Document::CFDF_Document() = default;
 
 CFDF_Document::~CFDF_Document() = default;
 
 std::unique_ptr<CFDF_Document> CFDF_Document::CreateNewDoc() {
-  auto pDoc = pdfium::MakeUnique<CFDF_Document>();
-  pDoc->m_pRootDict.Reset(pDoc->NewIndirect<CPDF_Dictionary>());
+  auto pDoc = std::make_unique<CFDF_Document>();
+  pDoc->m_pRootDict = pDoc->NewIndirect<CPDF_Dictionary>();
   pDoc->m_pRootDict->SetNewFor<CPDF_Dictionary>("FDF");
   return pDoc;
 }
 
 std::unique_ptr<CFDF_Document> CFDF_Document::ParseMemory(
     pdfium::span<const uint8_t> span) {
-  auto pDoc = pdfium::MakeUnique<CFDF_Document>();
-  pDoc->ParseStream(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(span));
+  auto pDoc = std::make_unique<CFDF_Document>();
+  pDoc->ParseStream(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(span));
   return pDoc->m_pRootDict ? std::move(pDoc) : nullptr;
 }
 
 void CFDF_Document::ParseStream(RetainPtr<IFX_SeekableReadStream> pFile) {
   m_pFile = std::move(pFile);
   CPDF_SyntaxParser parser(m_pFile);
-  while (1) {
-    bool bNumber;
-    ByteString word = parser.GetNextWord(&bNumber);
-    if (bNumber) {
-      uint32_t objnum = FXSYS_atoui(word.c_str());
+  while (true) {
+    CPDF_SyntaxParser::WordResult word_result = parser.GetNextWord();
+    if (word_result.is_number) {
+      uint32_t objnum = FXSYS_atoui(word_result.word.c_str());
       if (!objnum)
         break;
 
-      word = parser.GetNextWord(&bNumber);
-      if (!bNumber)
+      word_result = parser.GetNextWord();
+      if (!word_result.is_number)
         break;
 
-      word = parser.GetNextWord(nullptr);
-      if (word != "obj")
+      word_result = parser.GetNextWord();
+      if (word_result.word != "obj")
         break;
 
       RetainPtr<CPDF_Object> pObj = parser.GetObjectBody(this);
@@ -59,17 +58,17 @@
         break;
 
       ReplaceIndirectObjectIfHigherGeneration(objnum, std::move(pObj));
-      word = parser.GetNextWord(nullptr);
-      if (word != "endobj")
+      word_result = parser.GetNextWord();
+      if (word_result.word != "endobj")
         break;
     } else {
-      if (word != "trailer")
+      if (word_result.word != "trailer")
         break;
 
       RetainPtr<CPDF_Dictionary> pMainDict =
           ToDictionary(parser.GetObjectBody(this));
       if (pMainDict)
-        m_pRootDict.Reset(pMainDict->GetDictFor("Root"));
+        m_pRootDict = pMainDict->GetMutableDictFor("Root");
 
       break;
     }
@@ -80,7 +79,7 @@
   if (!m_pRootDict)
     return ByteString();
 
-  std::ostringstream buf;
+  fxcrt::ostringstream buf;
   buf << "%FDF-1.2\r\n";
   for (const auto& pair : *this)
     buf << pair.first << " 0 obj\r\n"
diff --git a/core/fpdfapi/parser/cfdf_document.h b/core/fpdfapi/parser/cfdf_document.h
index cad94d0..f327ab5 100644
--- a/core/fpdfapi/parser/cfdf_document.h
+++ b/core/fpdfapi/parser/cfdf_document.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -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;
@@ -26,7 +26,8 @@
   ~CFDF_Document() override;
 
   ByteString WriteToString() const;
-  CPDF_Dictionary* GetRoot() const { return m_pRootDict.Get(); }
+  const CPDF_Dictionary* GetRoot() const { return m_pRootDict.Get(); }
+  RetainPtr<CPDF_Dictionary> GetMutableRoot() const { return m_pRootDict; }
 
  private:
   void ParseStream(RetainPtr<IFX_SeekableReadStream> pFile);
diff --git a/core/fpdfapi/parser/cpdf_array.cpp b/core/fpdfapi/parser/cpdf_array.cpp
index 0d66cb0..55b82e7 100644
--- a/core/fpdfapi/parser/cpdf_array.cpp
+++ b/core/fpdfapi/parser/cpdf_array.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,15 +10,16 @@
 #include <utility>
 
 #include "core/fpdfapi/parser/cpdf_boolean.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fxcrt/fx_stream.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/notreached.h"
 
 CPDF_Array::CPDF_Array() = default;
 
@@ -28,7 +29,7 @@
   // Break cycles for cyclic references.
   m_ObjNum = kInvalidObjNum;
   for (auto& it : m_Objects) {
-    if (it && it->GetObjNum() == kInvalidObjNum)
+    if (it->GetObjNum() == kInvalidObjNum)
       it.Leak();
   }
 }
@@ -37,15 +38,7 @@
   return kArray;
 }
 
-bool CPDF_Array::IsArray() const {
-  return true;
-}
-
-CPDF_Array* CPDF_Array::AsArray() {
-  return this;
-}
-
-const CPDF_Array* CPDF_Array::AsArray() const {
+CPDF_Array* CPDF_Array::AsMutableArray() {
   return this;
 }
 
@@ -59,7 +52,7 @@
   pVisited->insert(this);
   auto pCopy = pdfium::MakeRetain<CPDF_Array>();
   for (const auto& pValue : m_Objects) {
-    if (!pdfium::ContainsKey(*pVisited, pValue.Get())) {
+    if (!pdfium::Contains(*pVisited, pValue.Get())) {
       std::set<const CPDF_Object*> visited(*pVisited);
       if (auto obj = pValue->CloneNonCyclic(bDirect, &visited))
         pCopy->m_Objects.push_back(std::move(obj));
@@ -73,10 +66,10 @@
   if (m_Objects.size() != 4)
     return rect;
 
-  rect.left = GetNumberAt(0);
-  rect.bottom = GetNumberAt(1);
-  rect.right = GetNumberAt(2);
-  rect.top = GetNumberAt(3);
+  rect.left = GetFloatAt(0);
+  rect.bottom = GetFloatAt(1);
+  rect.right = GetFloatAt(2);
+  rect.top = GetFloatAt(3);
   return rect;
 }
 
@@ -84,35 +77,48 @@
   if (m_Objects.size() != 6)
     return CFX_Matrix();
 
-  return CFX_Matrix(GetNumberAt(0), GetNumberAt(1), GetNumberAt(2),
-                    GetNumberAt(3), GetNumberAt(4), GetNumberAt(5));
+  return CFX_Matrix(GetFloatAt(0), GetFloatAt(1), GetFloatAt(2), GetFloatAt(3),
+                    GetFloatAt(4), GetFloatAt(5));
 }
 
-CPDF_Object* CPDF_Array::GetObjectAt(size_t index) {
-  if (index >= m_Objects.size())
-    return nullptr;
-  return m_Objects[index].Get();
+absl::optional<size_t> CPDF_Array::Find(const CPDF_Object* pThat) const {
+  for (size_t i = 0; i < size(); ++i) {
+    if (GetDirectObjectAt(i) == pThat)
+      return i;
+  }
+  return absl::nullopt;
 }
 
-const CPDF_Object* CPDF_Array::GetObjectAt(size_t index) const {
-  if (index >= m_Objects.size())
-    return nullptr;
-  return m_Objects[index].Get();
+bool CPDF_Array::Contains(const CPDF_Object* pThat) const {
+  return Find(pThat).has_value();
 }
 
-CPDF_Object* CPDF_Array::GetDirectObjectAt(size_t index) {
-  if (index >= m_Objects.size())
-    return nullptr;
-  return m_Objects[index]->GetDirect();
+CPDF_Object* CPDF_Array::GetMutableObjectAtInternal(size_t index) {
+  return index < m_Objects.size() ? m_Objects[index].Get() : nullptr;
 }
 
-const CPDF_Object* CPDF_Array::GetDirectObjectAt(size_t index) const {
-  if (index >= m_Objects.size())
-    return nullptr;
-  return m_Objects[index]->GetDirect();
+const CPDF_Object* CPDF_Array::GetObjectAtInternal(size_t index) const {
+  return const_cast<CPDF_Array*>(this)->GetMutableObjectAtInternal(index);
 }
 
-ByteString CPDF_Array::GetStringAt(size_t index) const {
+RetainPtr<CPDF_Object> CPDF_Array::GetMutableObjectAt(size_t index) {
+  return pdfium::WrapRetain(GetMutableObjectAtInternal(index));
+}
+
+RetainPtr<const CPDF_Object> CPDF_Array::GetObjectAt(size_t index) const {
+  return pdfium::WrapRetain(GetObjectAtInternal(index));
+}
+
+RetainPtr<const CPDF_Object> CPDF_Array::GetDirectObjectAt(size_t index) const {
+  return const_cast<CPDF_Array*>(this)->GetMutableDirectObjectAt(index);
+}
+
+RetainPtr<CPDF_Object> CPDF_Array::GetMutableDirectObjectAt(size_t index) {
+  RetainPtr<CPDF_Object> pObj = GetMutableObjectAt(index);
+  return pObj ? pObj->GetMutableDirect() : nullptr;
+}
+
+ByteString CPDF_Array::GetByteStringAt(size_t index) const {
   if (index >= m_Objects.size())
     return ByteString();
   return m_Objects[index]->GetString();
@@ -137,48 +143,51 @@
   return m_Objects[index]->GetInteger();
 }
 
-float CPDF_Array::GetNumberAt(size_t index) const {
+float CPDF_Array::GetFloatAt(size_t index) const {
   if (index >= m_Objects.size())
     return 0;
   return m_Objects[index]->GetNumber();
 }
 
-CPDF_Dictionary* CPDF_Array::GetDictAt(size_t index) {
-  CPDF_Object* p = GetDirectObjectAt(index);
+RetainPtr<CPDF_Dictionary> CPDF_Array::GetMutableDictAt(size_t index) {
+  RetainPtr<CPDF_Object> p = GetMutableDirectObjectAt(index);
   if (!p)
     return nullptr;
-  if (CPDF_Dictionary* pDict = p->AsDictionary())
-    return pDict;
-  if (CPDF_Stream* pStream = p->AsStream())
-    return pStream->GetDict();
+  CPDF_Dictionary* pDict = p->AsMutableDictionary();
+  if (pDict)
+    return pdfium::WrapRetain(pDict);
+  CPDF_Stream* pStream = p->AsMutableStream();
+  if (pStream)
+    return pStream->GetMutableDict();
   return nullptr;
 }
 
-const CPDF_Dictionary* CPDF_Array::GetDictAt(size_t index) const {
-  const CPDF_Object* p = GetDirectObjectAt(index);
-  if (!p)
-    return nullptr;
-  if (const CPDF_Dictionary* pDict = p->AsDictionary())
-    return pDict;
-  if (const CPDF_Stream* pStream = p->AsStream())
-    return pStream->GetDict();
-  return nullptr;
+RetainPtr<const CPDF_Dictionary> CPDF_Array::GetDictAt(size_t index) const {
+  return const_cast<CPDF_Array*>(this)->GetMutableDictAt(index);
 }
 
-CPDF_Stream* CPDF_Array::GetStreamAt(size_t index) {
-  return ToStream(GetDirectObjectAt(index));
+RetainPtr<CPDF_Stream> CPDF_Array::GetMutableStreamAt(size_t index) {
+  return ToStream(GetMutableDirectObjectAt(index));
 }
 
-const CPDF_Stream* CPDF_Array::GetStreamAt(size_t index) const {
-  return ToStream(GetDirectObjectAt(index));
+RetainPtr<const CPDF_Stream> CPDF_Array::GetStreamAt(size_t index) const {
+  return const_cast<CPDF_Array*>(this)->GetMutableStreamAt(index);
 }
 
-CPDF_Array* CPDF_Array::GetArrayAt(size_t index) {
-  return ToArray(GetDirectObjectAt(index));
+RetainPtr<CPDF_Array> CPDF_Array::GetMutableArrayAt(size_t index) {
+  return ToArray(GetMutableDirectObjectAt(index));
 }
 
-const CPDF_Array* CPDF_Array::GetArrayAt(size_t index) const {
-  return ToArray(GetDirectObjectAt(index));
+RetainPtr<const CPDF_Array> CPDF_Array::GetArrayAt(size_t index) const {
+  return const_cast<CPDF_Array*>(this)->GetMutableArrayAt(index);
+}
+
+RetainPtr<const CPDF_Number> CPDF_Array::GetNumberAt(size_t index) const {
+  return ToNumber(GetObjectAt(index));
+}
+
+RetainPtr<const CPDF_String> CPDF_Array::GetStringAt(size_t index) const {
+  return ToString(GetObjectAt(index));
 }
 
 void CPDF_Array::Clear() {
@@ -201,40 +210,52 @@
   if (!m_Objects[index] || m_Objects[index]->IsReference())
     return;
 
-  CPDF_Object* pNew = pHolder->AddIndirectObject(std::move(m_Objects[index]));
-  m_Objects[index] = pNew->MakeReference(pHolder);
+  pHolder->AddIndirectObject(m_Objects[index]);
+  m_Objects[index] = m_Objects[index]->MakeReference(pHolder);
 }
 
-CPDF_Object* CPDF_Array::SetAt(size_t index, RetainPtr<CPDF_Object> pObj) {
+void CPDF_Array::SetAt(size_t index, RetainPtr<CPDF_Object> pObj) {
+  (void)SetAtInternal(index, std::move(pObj));
+}
+
+void CPDF_Array::InsertAt(size_t index, RetainPtr<CPDF_Object> pObj) {
+  (void)InsertAtInternal(index, std::move(pObj));
+}
+
+void CPDF_Array::Append(RetainPtr<CPDF_Object> pObj) {
+  (void)AppendInternal(std::move(pObj));
+}
+
+CPDF_Object* CPDF_Array::SetAtInternal(size_t index,
+                                       RetainPtr<CPDF_Object> pObj) {
   CHECK(!IsLocked());
-  ASSERT(!pObj || pObj->IsInline());
-  if (index >= m_Objects.size()) {
-    NOTREACHED();
+  CHECK(pObj);
+  CHECK(pObj->IsInline());
+  if (index >= m_Objects.size())
     return nullptr;
-  }
+
   CPDF_Object* pRet = pObj.Get();
   m_Objects[index] = std::move(pObj);
   return pRet;
 }
 
-CPDF_Object* CPDF_Array::InsertAt(size_t index, RetainPtr<CPDF_Object> pObj) {
+CPDF_Object* CPDF_Array::InsertAtInternal(size_t index,
+                                          RetainPtr<CPDF_Object> pObj) {
   CHECK(!IsLocked());
-  CHECK(!pObj || pObj->IsInline());
+  CHECK(pObj);
+  CHECK(pObj->IsInline());
+  if (index > m_Objects.size())
+    return nullptr;
+
   CPDF_Object* pRet = pObj.Get();
-  if (index >= m_Objects.size()) {
-    // Allocate space first.
-    m_Objects.resize(index + 1);
-    m_Objects[index] = std::move(pObj);
-  } else {
-    // Directly insert.
-    m_Objects.insert(m_Objects.begin() + index, std::move(pObj));
-  }
+  m_Objects.insert(m_Objects.begin() + index, std::move(pObj));
   return pRet;
 }
 
-CPDF_Object* CPDF_Array::Add(RetainPtr<CPDF_Object> pObj) {
+CPDF_Object* CPDF_Array::AppendInternal(RetainPtr<CPDF_Object> pObj) {
   CHECK(!IsLocked());
-  CHECK(!pObj || pObj->IsInline());
+  CHECK(pObj);
+  CHECK(pObj->IsInline());
   CPDF_Object* pRet = pObj.Get();
   m_Objects.push_back(std::move(pObj));
   return pRet;
@@ -257,6 +278,16 @@
   m_pArray->m_LockCount++;
 }
 
+CPDF_ArrayLocker::CPDF_ArrayLocker(RetainPtr<CPDF_Array> pArray)
+    : m_pArray(std::move(pArray)) {
+  m_pArray->m_LockCount++;
+}
+
+CPDF_ArrayLocker::CPDF_ArrayLocker(RetainPtr<const CPDF_Array> pArray)
+    : m_pArray(std::move(pArray)) {
+  m_pArray->m_LockCount++;
+}
+
 CPDF_ArrayLocker::~CPDF_ArrayLocker() {
   m_pArray->m_LockCount--;
 }
diff --git a/core/fpdfapi/parser/cpdf_array.h b/core/fpdfapi/parser/cpdf_array.h
index 2a12d99..ea9a815 100644
--- a/core/fpdfapi/parser/cpdf_array.h
+++ b/core/fpdfapi/parser/cpdf_array.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,8 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_ARRAY_H_
 #define CORE_FPDFAPI_PARSER_CPDF_ARRAY_H_
 
-#include <memory>
+#include <stddef.h>
+
 #include <set>
 #include <type_traits>
 #include <utility>
@@ -17,94 +18,118 @@
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/check.h"
 
+// Arrays never contain nullptrs for objects within bounds, but some of the
+// methods will tolerate out-of-bounds indices and return nullptr for those
+// cases.
 class CPDF_Array final : public CPDF_Object {
  public:
   using const_iterator = std::vector<RetainPtr<CPDF_Object>>::const_iterator;
 
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // CPDF_Object:
   Type GetType() const override;
   RetainPtr<CPDF_Object> Clone() const override;
-  bool IsArray() const override;
-  CPDF_Array* AsArray() override;
-  const CPDF_Array* AsArray() const override;
+  CPDF_Array* AsMutableArray() override;
   bool WriteTo(IFX_ArchiveStream* archive,
                const CPDF_Encryptor* encryptor) const override;
 
   bool IsEmpty() const { return m_Objects.empty(); }
   size_t size() const { return m_Objects.size(); }
-  CPDF_Object* GetObjectAt(size_t index);
-  const CPDF_Object* GetObjectAt(size_t index) const;
-  CPDF_Object* GetDirectObjectAt(size_t index);
-  const CPDF_Object* GetDirectObjectAt(size_t index) const;
-  ByteString GetStringAt(size_t index) const;
+
+  // The Get*ObjectAt() methods tolerate out-of-bounds indices and return
+  // nullptr in those cases. Otherwise, for in-bound indices, the result
+  // is never nullptr.
+  RetainPtr<CPDF_Object> GetMutableObjectAt(size_t index);
+  RetainPtr<const CPDF_Object> GetObjectAt(size_t index) const;
+
+  // The Get*DirectObjectAt() methods tolerate out-of-bounds indices and
+  // return nullptr in those cases. Furthermore, for reference objects that
+  // do not correspond to a valid indirect object, nullptr is returned.
+  RetainPtr<CPDF_Object> GetMutableDirectObjectAt(size_t index);
+  RetainPtr<const CPDF_Object> GetDirectObjectAt(size_t index) const;
+
+  // The Get*At() methods tolerate out-of-bounds indices and return nullptr
+  // in those cases. Furthermore, these safely coerce to the sub-class,
+  // returning nullptr if the object at the location is of a different type.
+  ByteString GetByteStringAt(size_t index) const;
   WideString GetUnicodeTextAt(size_t index) const;
   bool GetBooleanAt(size_t index, bool bDefault) const;
   int GetIntegerAt(size_t index) const;
-  float GetNumberAt(size_t index) const;
-  CPDF_Dictionary* GetDictAt(size_t index);
-  const CPDF_Dictionary* GetDictAt(size_t index) const;
-  CPDF_Stream* GetStreamAt(size_t index);
-  const CPDF_Stream* GetStreamAt(size_t index) const;
-  CPDF_Array* GetArrayAt(size_t index);
-  const CPDF_Array* GetArrayAt(size_t index) const;
-  CFX_Matrix GetMatrix() const;
-  CFX_FloatRect GetRect() const;
+  float GetFloatAt(size_t index) const;
+  RetainPtr<CPDF_Dictionary> GetMutableDictAt(size_t index);
+  RetainPtr<const CPDF_Dictionary> GetDictAt(size_t index) const;
+  RetainPtr<CPDF_Stream> GetMutableStreamAt(size_t index);
+  RetainPtr<const CPDF_Stream> GetStreamAt(size_t index) const;
+  RetainPtr<CPDF_Array> GetMutableArrayAt(size_t index);
+  RetainPtr<const CPDF_Array> GetArrayAt(size_t index) const;
+  RetainPtr<const CPDF_Number> GetNumberAt(size_t index) const;
+  RetainPtr<const CPDF_String> GetStringAt(size_t index) const;
 
-  // Creates object owned by the array, returns unowned pointer to it.
+  CFX_FloatRect GetRect() const;
+  CFX_Matrix GetMatrix() const;
+
+  absl::optional<size_t> Find(const CPDF_Object* pThat) const;
+  bool Contains(const CPDF_Object* pThat) const;
+
+  // Creates object owned by the array, and returns a retained pointer to it.
   // We have special cases for objects that can intern strings from
   // a ByteStringPool. Prefer using these templates over direct calls
-  // to Add()/SetAt()/InsertAt() since by creating a new object with no
+  // to Append()/SetAt()/InsertAt() since by creating a new object with no
   // previous references, they ensure cycles can not be introduced.
   template <typename T, typename... Args>
-  typename std::enable_if<!CanInternStrings<T>::value, T*>::type AddNew(
-      Args&&... args) {
-    return static_cast<T*>(
-        Add(pdfium::MakeRetain<T>(std::forward<Args>(args)...)));
+  typename std::enable_if<!CanInternStrings<T>::value, RetainPtr<T>>::type
+  AppendNew(Args&&... args) {
+    return pdfium::WrapRetain(static_cast<T*>(
+        AppendInternal(pdfium::MakeRetain<T>(std::forward<Args>(args)...))));
   }
   template <typename T, typename... Args>
-  typename std::enable_if<CanInternStrings<T>::value, T*>::type AddNew(
-      Args&&... args) {
-    return static_cast<T*>(
-        Add(pdfium::MakeRetain<T>(m_pPool, std::forward<Args>(args)...)));
+  typename std::enable_if<CanInternStrings<T>::value, RetainPtr<T>>::type
+  AppendNew(Args&&... args) {
+    return pdfium::WrapRetain(static_cast<T*>(AppendInternal(
+        pdfium::MakeRetain<T>(m_pPool, std::forward<Args>(args)...))));
   }
   template <typename T, typename... Args>
-  typename std::enable_if<!CanInternStrings<T>::value, T*>::type SetNewAt(
-      size_t index,
-      Args&&... args) {
-    return static_cast<T*>(
-        SetAt(index, pdfium::MakeRetain<T>(std::forward<Args>(args)...)));
+  typename std::enable_if<!CanInternStrings<T>::value, RetainPtr<T>>::type
+  SetNewAt(size_t index, Args&&... args) {
+    return pdfium::WrapRetain(static_cast<T*>(SetAtInternal(
+        index, pdfium::MakeRetain<T>(std::forward<Args>(args)...))));
   }
   template <typename T, typename... Args>
-  typename std::enable_if<CanInternStrings<T>::value, T*>::type SetNewAt(
-      size_t index,
-      Args&&... args) {
-    return static_cast<T*>(SetAt(
-        index, pdfium::MakeRetain<T>(m_pPool, std::forward<Args>(args)...)));
+  typename std::enable_if<CanInternStrings<T>::value, RetainPtr<T>>::type
+  SetNewAt(size_t index, Args&&... args) {
+    return pdfium::WrapRetain(static_cast<T*>(SetAtInternal(
+        index, pdfium::MakeRetain<T>(m_pPool, std::forward<Args>(args)...))));
   }
   template <typename T, typename... Args>
-  typename std::enable_if<!CanInternStrings<T>::value, T*>::type InsertNewAt(
-      size_t index,
-      Args&&... args) {
-    return static_cast<T*>(
-        InsertAt(index, pdfium::MakeRetain<T>(std::forward<Args>(args)...)));
+  typename std::enable_if<!CanInternStrings<T>::value, RetainPtr<T>>::type
+  InsertNewAt(size_t index, Args&&... args) {
+    return pdfium::WrapRetain(static_cast<T*>(InsertAtInternal(
+        index, pdfium::MakeRetain<T>(std::forward<Args>(args)...))));
   }
   template <typename T, typename... Args>
-  typename std::enable_if<CanInternStrings<T>::value, T*>::type InsertNewAt(
-      size_t index,
-      Args&&... args) {
-    return static_cast<T*>(InsertAt(
-        index, pdfium::MakeRetain<T>(m_pPool, std::forward<Args>(args)...)));
+  typename std::enable_if<CanInternStrings<T>::value, RetainPtr<T>>::type
+  InsertNewAt(size_t index, Args&&... args) {
+    return pdfium::WrapRetain(static_cast<T*>(InsertAtInternal(
+        index, pdfium::MakeRetain<T>(m_pPool, std::forward<Args>(args)...))));
   }
 
-  // Takes ownership of |pObj|, returns unowned pointer to it.
-  CPDF_Object* Add(RetainPtr<CPDF_Object> pObj);
-  CPDF_Object* SetAt(size_t index, RetainPtr<CPDF_Object> pObj);
-  CPDF_Object* InsertAt(size_t index, RetainPtr<CPDF_Object> pObj);
+  // Adds non-null `pObj` to the end of the array, growing as appropriate.
+  void Append(RetainPtr<CPDF_Object> pObj);
+
+  // Overwrites the object at `index` with non-null `pObj`, if it is
+  // in bounds. Otherwise, `index` is out of bounds, and `pObj` is
+  // not stored.
+  void SetAt(size_t index, RetainPtr<CPDF_Object> pObj);
+
+  // Inserts non-null `pObj` at `index` and shifts by one position all of the
+  // objects beyond it like std::vector::insert(), if `index` is less than or
+  // equal to the current array size. Otherwise, `index` is out of bounds,
+  // and `pObj` is not stored.
+  void InsertAt(size_t index, RetainPtr<CPDF_Object> pObj);
 
   void Clear();
   void RemoveAt(size_t index);
@@ -119,6 +144,13 @@
   explicit CPDF_Array(const WeakPtr<ByteStringPool>& pPool);
   ~CPDF_Array() override;
 
+  // No guarantees about result lifetime, use with caution.
+  const CPDF_Object* GetObjectAtInternal(size_t index) const;
+  CPDF_Object* GetMutableObjectAtInternal(size_t index);
+  CPDF_Object* AppendInternal(RetainPtr<CPDF_Object> pObj);
+  CPDF_Object* SetAtInternal(size_t index, RetainPtr<CPDF_Object> pObj);
+  CPDF_Object* InsertAtInternal(size_t index, RetainPtr<CPDF_Object> pObj);
+
   RetainPtr<CPDF_Object> CloneNonCyclic(
       bool bDirect,
       std::set<const CPDF_Object*>* pVisited) const override;
@@ -130,9 +162,12 @@
 
 class CPDF_ArrayLocker {
  public:
+  FX_STACK_ALLOCATED();
   using const_iterator = CPDF_Array::const_iterator;
 
   explicit CPDF_ArrayLocker(const CPDF_Array* pArray);
+  explicit CPDF_ArrayLocker(RetainPtr<CPDF_Array> pArray);
+  explicit CPDF_ArrayLocker(RetainPtr<const CPDF_Array> pArray);
   ~CPDF_ArrayLocker();
 
   const_iterator begin() const {
@@ -149,7 +184,7 @@
 };
 
 inline CPDF_Array* ToArray(CPDF_Object* obj) {
-  return obj ? obj->AsArray() : nullptr;
+  return obj ? obj->AsMutableArray() : nullptr;
 }
 
 inline const CPDF_Array* ToArray(const CPDF_Object* obj) {
@@ -160,4 +195,8 @@
   return RetainPtr<CPDF_Array>(ToArray(obj.Get()));
 }
 
+inline RetainPtr<const CPDF_Array> ToArray(RetainPtr<const CPDF_Object> obj) {
+  return RetainPtr<const CPDF_Array>(ToArray(obj.Get()));
+}
+
 #endif  // CORE_FPDFAPI_PARSER_CPDF_ARRAY_H_
diff --git a/core/fpdfapi/parser/cpdf_array_unittest.cpp b/core/fpdfapi/parser/cpdf_array_unittest.cpp
index 457961f..9b3e841 100644
--- a/core/fpdfapi/parser/cpdf_array_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_array_unittest.cpp
@@ -1,24 +1,25 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/parser/cpdf_array.h"
 
+#include <iterator>
 #include <memory>
 #include <utility>
 
 #include "core/fpdfapi/parser/cpdf_boolean.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
-TEST(cpdf_array, GetBooleanAt) {
+TEST(ArrayTest, GetBooleanAt) {
   auto arr = pdfium::MakeRetain<CPDF_Array>();
-  arr->AddNew<CPDF_Boolean>(true);
-  arr->AddNew<CPDF_Boolean>(false);
-  arr->AddNew<CPDF_Number>(100);
-  arr->AddNew<CPDF_Number>(0);
+  arr->AppendNew<CPDF_Boolean>(true);
+  arr->AppendNew<CPDF_Boolean>(false);
+  arr->AppendNew<CPDF_Number>(100);
+  arr->AppendNew<CPDF_Number>(0);
 
   ASSERT_EQ(4u, arr->size());
   EXPECT_TRUE(arr->GetBooleanAt(0, true));
@@ -33,92 +34,88 @@
   EXPECT_FALSE(arr->GetBooleanAt(99, false));
 }
 
-TEST(cpdf_array, RemoveAt) {
+TEST(ArrayTest, RemoveAt) {
   {
     const int elems[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
     auto arr = pdfium::MakeRetain<CPDF_Array>();
-    for (size_t i = 0; i < FX_ArraySize(elems); ++i)
-      arr->AddNew<CPDF_Number>(elems[i]);
+    for (size_t i = 0; i < std::size(elems); ++i)
+      arr->AppendNew<CPDF_Number>(elems[i]);
     for (size_t i = 0; i < 3; ++i)
       arr->RemoveAt(3);
     const int expected[] = {1, 2, 3, 7, 8, 9, 10};
-    ASSERT_EQ(FX_ArraySize(expected), arr->size());
-    for (size_t i = 0; i < FX_ArraySize(expected); ++i)
+    ASSERT_EQ(std::size(expected), arr->size());
+    for (size_t i = 0; i < std::size(expected); ++i)
       EXPECT_EQ(expected[i], arr->GetIntegerAt(i));
     arr->RemoveAt(4);
     arr->RemoveAt(4);
     const int expected2[] = {1, 2, 3, 7, 10};
-    ASSERT_EQ(FX_ArraySize(expected2), arr->size());
-    for (size_t i = 0; i < FX_ArraySize(expected2); ++i)
+    ASSERT_EQ(std::size(expected2), arr->size());
+    for (size_t i = 0; i < std::size(expected2); ++i)
       EXPECT_EQ(expected2[i], arr->GetIntegerAt(i));
   }
   {
     // When the range is out of bound, RemoveAt() has no effect.
     const int elems[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
     auto arr = pdfium::MakeRetain<CPDF_Array>();
-    for (size_t i = 0; i < FX_ArraySize(elems); ++i)
-      arr->AddNew<CPDF_Number>(elems[i]);
+    for (size_t i = 0; i < std::size(elems); ++i)
+      arr->AppendNew<CPDF_Number>(elems[i]);
     arr->RemoveAt(11);
-    EXPECT_EQ(FX_ArraySize(elems), arr->size());
+    EXPECT_EQ(std::size(elems), arr->size());
   }
 }
 
-TEST(cpdf_array, Clear) {
+TEST(ArrayTest, Clear) {
   const int elems[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
   auto arr = pdfium::MakeRetain<CPDF_Array>();
   EXPECT_EQ(0U, arr->size());
-  for (size_t i = 0; i < FX_ArraySize(elems); ++i)
-    arr->AddNew<CPDF_Number>(elems[i]);
-  EXPECT_EQ(FX_ArraySize(elems), arr->size());
+  for (size_t i = 0; i < std::size(elems); ++i)
+    arr->AppendNew<CPDF_Number>(elems[i]);
+  EXPECT_EQ(std::size(elems), arr->size());
   arr->Clear();
   EXPECT_EQ(0U, arr->size());
 }
 
-TEST(cpdf_array, InsertAt) {
-  {
-    const int elems[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-    auto arr = pdfium::MakeRetain<CPDF_Array>();
-    for (size_t i = 0; i < FX_ArraySize(elems); ++i)
-      arr->InsertNewAt<CPDF_Number>(i, elems[i]);
-    ASSERT_EQ(FX_ArraySize(elems), arr->size());
-    for (size_t i = 0; i < FX_ArraySize(elems); ++i)
-      EXPECT_EQ(elems[i], arr->GetIntegerAt(i));
-    arr->InsertNewAt<CPDF_Number>(3, 33);
-    arr->InsertNewAt<CPDF_Number>(6, 55);
-    arr->InsertNewAt<CPDF_Number>(12, 12);
-    const int expected[] = {1, 2, 3, 33, 4, 5, 55, 6, 7, 8, 9, 10, 12};
-    ASSERT_EQ(FX_ArraySize(expected), arr->size());
-    for (size_t i = 0; i < FX_ArraySize(expected); ++i)
-      EXPECT_EQ(expected[i], arr->GetIntegerAt(i));
-  }
-  {
-    // When the position to insert is beyond the upper bound,
-    // an element is inserted at that position while other unfilled
-    // positions have nullptr.
-    const int elems[] = {1, 2};
-    auto arr = pdfium::MakeRetain<CPDF_Array>();
-    for (size_t i = 0; i < FX_ArraySize(elems); ++i)
-      arr->InsertNewAt<CPDF_Number>(i, elems[i]);
-    arr->InsertNewAt<CPDF_Number>(10, 10);
-    ASSERT_EQ(11u, arr->size());
-    for (size_t i = 0; i < FX_ArraySize(elems); ++i)
-      EXPECT_EQ(elems[i], arr->GetIntegerAt(i));
-    for (size_t i = FX_ArraySize(elems); i < 10; ++i)
-      EXPECT_EQ(nullptr, arr->GetObjectAt(i));
-    EXPECT_EQ(10, arr->GetIntegerAt(10));
-  }
+TEST(ArrayTest, SetAtBeyond) {
+  auto arr = pdfium::MakeRetain<CPDF_Array>();
+  EXPECT_FALSE(arr->SetNewAt<CPDF_Number>(0, 0));
+  EXPECT_TRUE(arr->InsertNewAt<CPDF_Number>(0, 0));
+  EXPECT_FALSE(arr->SetNewAt<CPDF_Number>(1, 0));
 }
 
-TEST(cpdf_array, Clone) {
+TEST(ArrayTest, InsertAt) {
+  const int elems[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+  auto arr = pdfium::MakeRetain<CPDF_Array>();
+  for (size_t i = 0; i < std::size(elems); ++i)
+    arr->InsertNewAt<CPDF_Number>(i, elems[i]);
+  ASSERT_EQ(std::size(elems), arr->size());
+  for (size_t i = 0; i < std::size(elems); ++i)
+    EXPECT_EQ(elems[i], arr->GetIntegerAt(i));
+  arr->InsertNewAt<CPDF_Number>(3, 33);
+  arr->InsertNewAt<CPDF_Number>(6, 55);
+  arr->InsertNewAt<CPDF_Number>(12, 12);
+  const int expected[] = {1, 2, 3, 33, 4, 5, 55, 6, 7, 8, 9, 10, 12};
+  ASSERT_EQ(std::size(expected), arr->size());
+  for (size_t i = 0; i < std::size(expected); ++i)
+    EXPECT_EQ(expected[i], arr->GetIntegerAt(i));
+}
+
+TEST(ArrayTest, InsertAtBeyond) {
+  auto arr = pdfium::MakeRetain<CPDF_Array>();
+  EXPECT_FALSE(arr->InsertNewAt<CPDF_Number>(1, 0));
+  EXPECT_TRUE(arr->InsertNewAt<CPDF_Number>(0, 0));
+  EXPECT_FALSE(arr->InsertNewAt<CPDF_Number>(2, 0));
+}
+
+TEST(ArrayTest, Clone) {
   {
     // Basic case.
     const int elems[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
     auto arr = pdfium::MakeRetain<CPDF_Array>();
-    for (size_t i = 0; i < FX_ArraySize(elems); ++i)
+    for (size_t i = 0; i < std::size(elems); ++i)
       arr->InsertNewAt<CPDF_Number>(i, elems[i]);
     RetainPtr<CPDF_Array> arr2 = ToArray(arr->Clone());
     ASSERT_EQ(arr->size(), arr2->size());
-    for (size_t i = 0; i < FX_ArraySize(elems); ++i) {
+    for (size_t i = 0; i < std::size(elems); ++i) {
       // Clone() always create new objects.
       EXPECT_NE(arr->GetObjectAt(i), arr2->GetObjectAt(i));
       EXPECT_EQ(arr->GetIntegerAt(i), arr2->GetIntegerAt(i));
@@ -132,7 +129,7 @@
         {1, 2, 3, 4, 5}, {10, 9, 8, 7, 6}, {11, 12, 13, 14, 15}};
     auto arr = pdfium::MakeRetain<CPDF_Array>();
     // Indirect references to indirect objects.
-    auto obj_holder = pdfium::MakeUnique<CPDF_IndirectObjectHolder>();
+    auto obj_holder = std::make_unique<CPDF_IndirectObjectHolder>();
     for (size_t i = 0; i < kNumOfRows; ++i) {
       auto arr_elem = pdfium::MakeRetain<CPDF_Array>();
       for (size_t j = 0; j < kNumOfRowElems; ++j) {
@@ -154,15 +151,15 @@
     RetainPtr<CPDF_Array> arr2 = ToArray(arr->CloneDirectObject());
     ASSERT_EQ(arr->size(), arr2->size());
     for (size_t i = 0; i < kNumOfRows; ++i) {
-      CPDF_Array* arr_elem = arr->GetObjectAt(i)->AsArray();
-      CPDF_Array* arr1_elem = arr1->GetObjectAt(i)->AsArray();
-      CPDF_Array* arr2_elem = arr2->GetObjectAt(i)->AsArray();
+      const CPDF_Array* arr_elem = arr->GetObjectAt(i)->AsArray();
+      const CPDF_Array* arr1_elem = arr1->GetObjectAt(i)->AsArray();
+      const CPDF_Array* arr2_elem = arr2->GetObjectAt(i)->AsArray();
       EXPECT_NE(arr_elem, arr1_elem);
       EXPECT_NE(arr_elem, arr2_elem);
       for (size_t j = 0; j < kNumOfRowElems; ++j) {
-        auto* elem_obj = arr_elem->GetObjectAt(j);
-        auto* elem_obj1 = arr1_elem->GetObjectAt(j);
-        auto* elem_obj2 = arr2_elem->GetObjectAt(j);
+        auto elem_obj = arr_elem->GetObjectAt(j);
+        auto elem_obj1 = arr1_elem->GetObjectAt(j);
+        auto elem_obj2 = arr2_elem->GetObjectAt(j);
         // Results from not deferencing reference objects.
         EXPECT_NE(elem_obj, elem_obj1);
         EXPECT_TRUE(elem_obj1->IsReference());
@@ -181,7 +178,7 @@
     for (size_t i = 0; i < kNumOfRows; ++i) {
       for (size_t j = 0; j < kNumOfRowElems; ++j) {
         // Results from not deferencing reference objects.
-        auto* elem_obj1 = arr1->GetObjectAt(i)->AsArray()->GetObjectAt(j);
+        auto elem_obj1 = arr1->GetObjectAt(i)->AsArray()->GetObjectAt(j);
         EXPECT_TRUE(elem_obj1->IsReference());
         EXPECT_EQ(elems[i][j], elem_obj1->GetInteger());
         // Results from deferencing reference objects.
@@ -192,16 +189,51 @@
   }
 }
 
-TEST(cpdf_array, Iterator) {
+TEST(ArrayTest, Find) {
+  auto arr = pdfium::MakeRetain<CPDF_Array>();
+  auto dict0 = pdfium::MakeRetain<CPDF_Dictionary>();
+  auto dict1 = pdfium::MakeRetain<CPDF_Dictionary>();
+  auto dict2 = pdfium::MakeRetain<CPDF_Dictionary>();
+  arr->Append(dict0);
+  arr->Append(dict1);
+
+  absl::optional<size_t> maybe_found = arr->Find(nullptr);
+  EXPECT_FALSE(maybe_found.has_value());
+
+  maybe_found = arr->Find(dict0.Get());
+  ASSERT_TRUE(maybe_found.has_value());
+  EXPECT_EQ(0u, maybe_found.value());
+
+  maybe_found = arr->Find(dict1.Get());
+  ASSERT_TRUE(maybe_found.has_value());
+  EXPECT_EQ(1u, maybe_found.value());
+
+  maybe_found = arr->Find(dict2.Get());
+  EXPECT_FALSE(maybe_found.has_value());
+}
+
+TEST(ArrayTest, Contains) {
+  auto arr = pdfium::MakeRetain<CPDF_Array>();
+  auto dict0 = pdfium::MakeRetain<CPDF_Dictionary>();
+  auto dict1 = pdfium::MakeRetain<CPDF_Dictionary>();
+  auto dict2 = pdfium::MakeRetain<CPDF_Dictionary>();
+  arr->Append(dict0);
+  arr->Append(dict1);
+  EXPECT_TRUE(arr->Contains(dict0.Get()));
+  EXPECT_TRUE(arr->Contains(dict1.Get()));
+  EXPECT_FALSE(arr->Contains(dict2.Get()));
+}
+
+TEST(ArrayTest, Iterator) {
   const int elems[] = {-23, -11,     3,         455,   2345877,
                        0,   7895330, -12564334, 10000, -100000};
   auto arr = pdfium::MakeRetain<CPDF_Array>();
-  for (size_t i = 0; i < FX_ArraySize(elems); ++i)
+  for (size_t i = 0; i < std::size(elems); ++i)
     arr->InsertNewAt<CPDF_Number>(i, elems[i]);
-  size_t index = 0;
 
-  CPDF_ArrayLocker locker(arr.Get());
+  size_t index = 0;
+  CPDF_ArrayLocker locker(arr);
   for (const auto& it : locker)
     EXPECT_EQ(elems[index++], it->AsNumber()->GetInteger());
-  EXPECT_EQ(FX_ArraySize(elems), index);
+  EXPECT_EQ(std::size(elems), index);
 }
diff --git a/core/fpdfapi/parser/cpdf_boolean.cpp b/core/fpdfapi/parser/cpdf_boolean.cpp
index b5e12eb..3404959 100644
--- a/core/fpdfapi/parser/cpdf_boolean.cpp
+++ b/core/fpdfapi/parser/cpdf_boolean.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,6 @@
 #include "core/fpdfapi/parser/cpdf_boolean.h"
 
 #include "core/fxcrt/fx_stream.h"
-#include "third_party/base/ptr_util.h"
 
 CPDF_Boolean::CPDF_Boolean() = default;
 
@@ -35,15 +34,7 @@
   m_bValue = (str == "true");
 }
 
-bool CPDF_Boolean::IsBoolean() const {
-  return true;
-}
-
-CPDF_Boolean* CPDF_Boolean::AsBoolean() {
-  return this;
-}
-
-const CPDF_Boolean* CPDF_Boolean::AsBoolean() const {
+CPDF_Boolean* CPDF_Boolean::AsMutableBoolean() {
   return this;
 }
 
diff --git a/core/fpdfapi/parser/cpdf_boolean.h b/core/fpdfapi/parser/cpdf_boolean.h
index 8ef47ad..9cec390 100644
--- a/core/fpdfapi/parser/cpdf_boolean.h
+++ b/core/fpdfapi/parser/cpdf_boolean.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,13 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_BOOLEAN_H_
 #define CORE_FPDFAPI_PARSER_CPDF_BOOLEAN_H_
 
-#include <memory>
-
 #include "core/fpdfapi/parser/cpdf_object.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Boolean final : public CPDF_Object {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // CPDF_Object:
   Type GetType() const override;
@@ -24,9 +21,7 @@
   ByteString GetString() const override;
   int GetInteger() const override;
   void SetString(const ByteString& str) override;
-  bool IsBoolean() const override;
-  CPDF_Boolean* AsBoolean() override;
-  const CPDF_Boolean* AsBoolean() const override;
+  CPDF_Boolean* AsMutableBoolean() override;
   bool WriteTo(IFX_ArchiveStream* archive,
                const CPDF_Encryptor* encryptor) const override;
 
@@ -39,7 +34,7 @@
 };
 
 inline CPDF_Boolean* ToBoolean(CPDF_Object* obj) {
-  return obj ? obj->AsBoolean() : nullptr;
+  return obj ? obj->AsMutableBoolean() : nullptr;
 }
 
 inline const CPDF_Boolean* ToBoolean(const CPDF_Object* obj) {
diff --git a/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp b/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp
index a6de007..8562e64 100644
--- a/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp
+++ b/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp
@@ -1,19 +1,17 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/parser/cpdf_cross_ref_avail.h"
 
-#include <algorithm>
-#include <memory>
-#include <vector>
-
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_read_validator.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
 
@@ -30,20 +28,20 @@
 CPDF_CrossRefAvail::CPDF_CrossRefAvail(CPDF_SyntaxParser* parser,
                                        FX_FILESIZE last_crossref_offset)
     : parser_(parser), last_crossref_offset_(last_crossref_offset) {
-  ASSERT(parser_);
+  DCHECK(parser_);
   AddCrossRefForCheck(last_crossref_offset);
 }
 
-CPDF_CrossRefAvail::~CPDF_CrossRefAvail() {}
+CPDF_CrossRefAvail::~CPDF_CrossRefAvail() = default;
 
 CPDF_DataAvail::DocAvailStatus CPDF_CrossRefAvail::CheckAvail() {
-  if (current_status_ == CPDF_DataAvail::DataAvailable)
-    return CPDF_DataAvail::DataAvailable;
+  if (status_ == CPDF_DataAvail::kDataAvailable)
+    return CPDF_DataAvail::kDataAvailable;
 
-  const CPDF_ReadValidator::Session read_session(GetValidator());
+  CPDF_ReadValidator::ScopedSession read_session(GetValidator());
   while (true) {
     bool check_result = false;
-    switch (current_state_) {
+    switch (state_) {
       case State::kCrossRefCheck:
         check_result = CheckCrossRef();
         break;
@@ -55,23 +53,18 @@
         break;
       case State::kDone:
         break;
-      default: {
-        current_status_ = CPDF_DataAvail::DataError;
-        NOTREACHED();
-        break;
-      }
     }
     if (!check_result)
       break;
 
-    ASSERT(!GetValidator()->has_read_problems());
+    DCHECK(!GetValidator()->has_read_problems());
   }
-  return current_status_;
+  return status_;
 }
 
 bool CPDF_CrossRefAvail::CheckReadProblems() {
   if (GetValidator()->read_error()) {
-    current_status_ = CPDF_DataAvail::DataError;
+    status_ = CPDF_DataAvail::kDataError;
     return true;
   }
   return GetValidator()->has_unavailable_data();
@@ -80,13 +73,13 @@
 bool CPDF_CrossRefAvail::CheckCrossRef() {
   if (cross_refs_for_check_.empty()) {
     // All cross refs were checked.
-    current_state_ = State::kDone;
-    current_status_ = CPDF_DataAvail::DataAvailable;
+    state_ = State::kDone;
+    status_ = CPDF_DataAvail::kDataAvailable;
     return true;
   }
   parser_->SetPos(cross_refs_for_check_.front());
 
-  const ByteString first_word = parser_->PeekNextWord(nullptr);
+  const ByteString first_word = parser_->PeekNextWord();
   if (CheckReadProblems())
     return false;
 
@@ -105,36 +98,36 @@
     return false;
 
   if (keyword != kCrossRefKeyword) {
-    current_status_ = CPDF_DataAvail::DataError;
+    status_ = CPDF_DataAvail::kDataError;
     return false;
   }
 
-  current_state_ = State::kCrossRefV4ItemCheck;
-  current_offset_ = parser_->GetPos();
+  state_ = State::kCrossRefV4ItemCheck;
+  offset_ = parser_->GetPos();
   return true;
 }
 
 bool CPDF_CrossRefAvail::CheckCrossRefV4Item() {
-  parser_->SetPos(current_offset_);
+  parser_->SetPos(offset_);
   const ByteString keyword = parser_->GetKeyword();
   if (CheckReadProblems())
     return false;
 
   if (keyword.IsEmpty()) {
-    current_status_ = CPDF_DataAvail::DataError;
+    status_ = CPDF_DataAvail::kDataError;
     return false;
   }
 
   if (keyword == kTrailerKeyword)
-    current_state_ = State::kCrossRefV4TrailerCheck;
+    state_ = State::kCrossRefV4TrailerCheck;
 
   // Go to next item.
-  current_offset_ = parser_->GetPos();
+  offset_ = parser_->GetPos();
   return true;
 }
 
 bool CPDF_CrossRefAvail::CheckCrossRefV4Trailer() {
-  parser_->SetPos(current_offset_);
+  parser_->SetPos(offset_);
 
   RetainPtr<CPDF_Dictionary> trailer =
       ToDictionary(parser_->GetObjectBody(nullptr));
@@ -142,30 +135,29 @@
     return false;
 
   if (!trailer) {
-    current_status_ = CPDF_DataAvail::DataError;
+    status_ = CPDF_DataAvail::kDataError;
     return false;
   }
 
   if (ToReference(trailer->GetObjectFor(kEncryptKey))) {
-    current_status_ = CPDF_DataAvail::DataError;
+    status_ = CPDF_DataAvail::kDataError;
     return false;
   }
 
-  const int32_t xrefpos =
-      GetDirectInteger(trailer.Get(), kPrevCrossRefFieldKey);
-  if (xrefpos &&
+  const int32_t xrefpos = trailer->GetDirectIntegerFor(kPrevCrossRefFieldKey);
+  if (xrefpos > 0 &&
       pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(xrefpos))
     AddCrossRefForCheck(static_cast<FX_FILESIZE>(xrefpos));
 
   const int32_t stream_xref_offset =
-      GetDirectInteger(trailer.Get(), kPrevCrossRefStreamOffsetFieldKey);
-  if (stream_xref_offset &&
+      trailer->GetDirectIntegerFor(kPrevCrossRefStreamOffsetFieldKey);
+  if (stream_xref_offset > 0 &&
       pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(
           stream_xref_offset))
     AddCrossRefForCheck(static_cast<FX_FILESIZE>(stream_xref_offset));
 
   // Goto check next crossref
-  current_state_ = State::kCrossRefCheck;
+  state_ = State::kCrossRefCheck;
   return true;
 }
 
@@ -175,32 +167,32 @@
   if (CheckReadProblems())
     return false;
 
-  const CPDF_Dictionary* trailer =
+  RetainPtr<const CPDF_Dictionary> trailer =
       cross_ref && cross_ref->IsStream() ? cross_ref->GetDict() : nullptr;
   if (!trailer) {
-    current_status_ = CPDF_DataAvail::DataError;
+    status_ = CPDF_DataAvail::kDataError;
     return false;
   }
 
   if (ToReference(trailer->GetObjectFor(kEncryptKey))) {
-    current_status_ = CPDF_DataAvail::DataError;
+    status_ = CPDF_DataAvail::kDataError;
     return false;
   }
 
-  const CPDF_Name* type_name = ToName(trailer->GetObjectFor(kTypeFieldKey));
-  if (type_name && type_name->GetString() == kXRefKeyword) {
+  if (trailer->GetNameFor(kTypeFieldKey) == kXRefKeyword) {
     const int32_t xrefpos = trailer->GetIntegerFor(kPrevCrossRefFieldKey);
-    if (xrefpos &&
-        pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(xrefpos))
+    if (xrefpos > 0 &&
+        pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(xrefpos)) {
       AddCrossRefForCheck(static_cast<FX_FILESIZE>(xrefpos));
+    }
   }
   // Goto check next crossref
-  current_state_ = State::kCrossRefCheck;
+  state_ = State::kCrossRefCheck;
   return true;
 }
 
 void CPDF_CrossRefAvail::AddCrossRefForCheck(FX_FILESIZE crossref_offset) {
-  if (registered_crossrefs_.count(crossref_offset))
+  if (pdfium::Contains(registered_crossrefs_, crossref_offset))
     return;
 
   cross_refs_for_check_.push(crossref_offset);
diff --git a/core/fpdfapi/parser/cpdf_cross_ref_avail.h b/core/fpdfapi/parser/cpdf_cross_ref_avail.h
index e550410..276fab1 100644
--- a/core/fpdfapi/parser/cpdf_cross_ref_avail.h
+++ b/core/fpdfapi/parser/cpdf_cross_ref_avail.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,6 +9,7 @@
 #include <set>
 
 #include "core/fpdfapi/parser/cpdf_data_avail.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_SyntaxParser;
@@ -42,12 +43,11 @@
 
   RetainPtr<CPDF_ReadValidator> GetValidator();
 
-  UnownedPtr<CPDF_SyntaxParser> parser_;
-  const FX_FILESIZE last_crossref_offset_ = 0;
-  CPDF_DataAvail::DocAvailStatus current_status_ =
-      CPDF_DataAvail::DataNotAvailable;
-  State current_state_ = State::kCrossRefCheck;
-  FX_FILESIZE current_offset_ = 0;
+  UnownedPtr<CPDF_SyntaxParser> const parser_;
+  const FX_FILESIZE last_crossref_offset_;
+  CPDF_DataAvail::DocAvailStatus status_ = CPDF_DataAvail::kDataNotAvailable;
+  State state_ = State::kCrossRefCheck;
+  FX_FILESIZE offset_ = 0;
   std::queue<FX_FILESIZE> cross_refs_for_check_;
   std::set<FX_FILESIZE> registered_crossrefs_;
 };
diff --git a/core/fpdfapi/parser/cpdf_cross_ref_avail_unittest.cpp b/core/fpdfapi/parser/cpdf_cross_ref_avail_unittest.cpp
index 6cdc3f1..57f3bd4 100644
--- a/core/fpdfapi/parser/cpdf_cross_ref_avail_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_cross_ref_avail_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,21 +8,20 @@
 #include <string>
 
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
 std::unique_ptr<CPDF_SyntaxParser> MakeParserForBuffer(
     pdfium::span<const uint8_t> buffer) {
-  return pdfium::MakeUnique<CPDF_SyntaxParser>(
-      pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(buffer));
+  return std::make_unique<CPDF_SyntaxParser>(
+      pdfium::MakeRetain<CFX_ReadOnlySpanStream>(buffer));
 }
 
 }  // namespace
 
-TEST(CPDF_CrossRefAvailTest, CheckCrossRefV4) {
+TEST(CrossRefAvailTest, CheckCrossRefV4) {
   const unsigned char xref_table[] =
       "xref \n"
       "0 6 \n"
@@ -39,13 +38,13 @@
   const FX_FILESIZE last_crossref_offset = 0;
 
   auto parser = MakeParserForBuffer(xref_table);
-  auto cross_ref_avail = pdfium::MakeUnique<CPDF_CrossRefAvail>(
-      parser.get(), last_crossref_offset);
+  auto cross_ref_avail =
+      std::make_unique<CPDF_CrossRefAvail>(parser.get(), last_crossref_offset);
 
-  EXPECT_EQ(CPDF_DataAvail::DataAvailable, cross_ref_avail->CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataAvailable, cross_ref_avail->CheckAvail());
 }
 
-TEST(CPDF_CrossRefAvailTest, CheckCrossRefStream) {
+TEST(CrossRefAvailTest, CheckCrossRefStream) {
   const unsigned char xref_stream[] =
       "16 0 obj\n"
       "<</Filter /FlateDecode>>"
@@ -56,13 +55,13 @@
   const FX_FILESIZE last_crossref_offset = 0;
 
   auto parser = MakeParserForBuffer(xref_stream);
-  auto cross_ref_avail = pdfium::MakeUnique<CPDF_CrossRefAvail>(
-      parser.get(), last_crossref_offset);
+  auto cross_ref_avail =
+      std::make_unique<CPDF_CrossRefAvail>(parser.get(), last_crossref_offset);
 
-  EXPECT_EQ(CPDF_DataAvail::DataAvailable, cross_ref_avail->CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataAvailable, cross_ref_avail->CheckAvail());
 }
 
-TEST(CPDF_CrossRefAvailTest, IncorrectStartOffset) {
+TEST(CrossRefAvailTest, IncorrectStartOffset) {
   const unsigned char xref_stream[] =
       "16 0 obj\n"
       "<</Filter /FlateDecode>>"
@@ -74,13 +73,13 @@
   const FX_FILESIZE last_crossref_offset = 70000;
 
   auto parser = MakeParserForBuffer(xref_stream);
-  auto cross_ref_avail = pdfium::MakeUnique<CPDF_CrossRefAvail>(
-      parser.get(), last_crossref_offset);
+  auto cross_ref_avail =
+      std::make_unique<CPDF_CrossRefAvail>(parser.get(), last_crossref_offset);
 
-  EXPECT_EQ(CPDF_DataAvail::DataError, cross_ref_avail->CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataError, cross_ref_avail->CheckAvail());
 }
 
-TEST(CPDF_CrossRefAvailTest, IncorrectPrevOffset) {
+TEST(CrossRefAvailTest, IncorrectPrevOffset) {
   const unsigned char xref_stream[] =
       "16 0 obj\n"
       "<</Type /XRef /Prev 70000>>"
@@ -91,12 +90,12 @@
   const FX_FILESIZE last_crossref_offset = 0;
 
   auto parser = MakeParserForBuffer(xref_stream);
-  auto cross_ref_avail = pdfium::MakeUnique<CPDF_CrossRefAvail>(
-      parser.get(), last_crossref_offset);
-  EXPECT_EQ(CPDF_DataAvail::DataError, cross_ref_avail->CheckAvail());
+  auto cross_ref_avail =
+      std::make_unique<CPDF_CrossRefAvail>(parser.get(), last_crossref_offset);
+  EXPECT_EQ(CPDF_DataAvail::kDataError, cross_ref_avail->CheckAvail());
 }
 
-TEST(CPDF_CrossRefAvailTest, IncorrectPrevStreamOffset) {
+TEST(CrossRefAvailTest, IncorrectPrevStreamOffset) {
   const unsigned char xref_table[] =
       "xref \n"
       "0 6 \n"
@@ -113,24 +112,24 @@
   const FX_FILESIZE last_crossref_offset = 0;
 
   auto parser = MakeParserForBuffer(xref_table);
-  auto cross_ref_avail = pdfium::MakeUnique<CPDF_CrossRefAvail>(
-      parser.get(), last_crossref_offset);
-  EXPECT_EQ(CPDF_DataAvail::DataError, cross_ref_avail->CheckAvail());
+  auto cross_ref_avail =
+      std::make_unique<CPDF_CrossRefAvail>(parser.get(), last_crossref_offset);
+  EXPECT_EQ(CPDF_DataAvail::kDataError, cross_ref_avail->CheckAvail());
 }
 
-TEST(CPDF_CrossRefAvailTest, IncorrectData) {
+TEST(CrossRefAvailTest, IncorrectData) {
   const unsigned char incorrect_data[] =
       "fiajaoilf w9ifaoihwoiafhja wfijaofijoiaw fhj oiawhfoiah "
       "wfoihoiwfghouiafghwoigahfi";
   const FX_FILESIZE last_crossref_offset = 0;
 
   auto parser = MakeParserForBuffer(incorrect_data);
-  auto cross_ref_avail = pdfium::MakeUnique<CPDF_CrossRefAvail>(
-      parser.get(), last_crossref_offset);
-  EXPECT_EQ(CPDF_DataAvail::DataError, cross_ref_avail->CheckAvail());
+  auto cross_ref_avail =
+      std::make_unique<CPDF_CrossRefAvail>(parser.get(), last_crossref_offset);
+  EXPECT_EQ(CPDF_DataAvail::kDataError, cross_ref_avail->CheckAvail());
 }
 
-TEST(CPDF_CrossRefAvailTest, ThreeCrossRefV4) {
+TEST(CrossRefAvailTest, ThreeCrossRefV4) {
   char int_buffer[100];
   std::string table = "pdf blah blah blah\n";
   size_t cur_offset = table.size();
@@ -173,12 +172,12 @@
   const FX_FILESIZE last_crossref_offset = static_cast<FX_FILESIZE>(cur_offset);
 
   auto parser = MakeParserForBuffer(pdfium::as_bytes(pdfium::make_span(table)));
-  auto cross_ref_avail = pdfium::MakeUnique<CPDF_CrossRefAvail>(
-      parser.get(), last_crossref_offset);
-  EXPECT_EQ(CPDF_DataAvail::DataAvailable, cross_ref_avail->CheckAvail());
+  auto cross_ref_avail =
+      std::make_unique<CPDF_CrossRefAvail>(parser.get(), last_crossref_offset);
+  EXPECT_EQ(CPDF_DataAvail::kDataAvailable, cross_ref_avail->CheckAvail());
 }
 
-TEST(CPDF_CrossRefAvailTest, ThreeCrossRefV5) {
+TEST(CrossRefAvailTest, ThreeCrossRefV5) {
   char int_buffer[100];
   std::string table = "pdf blah blah blah\n";
   size_t cur_offset = table.size();
@@ -217,12 +216,12 @@
   const FX_FILESIZE last_crossref_offset = static_cast<FX_FILESIZE>(cur_offset);
 
   auto parser = MakeParserForBuffer(pdfium::as_bytes(pdfium::make_span(table)));
-  auto cross_ref_avail = pdfium::MakeUnique<CPDF_CrossRefAvail>(
-      parser.get(), last_crossref_offset);
-  EXPECT_EQ(CPDF_DataAvail::DataAvailable, cross_ref_avail->CheckAvail());
+  auto cross_ref_avail =
+      std::make_unique<CPDF_CrossRefAvail>(parser.get(), last_crossref_offset);
+  EXPECT_EQ(CPDF_DataAvail::kDataAvailable, cross_ref_avail->CheckAvail());
 }
 
-TEST(CPDF_CrossRefAvailTest, Mixed) {
+TEST(CrossRefAvailTest, Mixed) {
   char int_buffer[100];
   std::string table = "pdf blah blah blah\n";
 
@@ -266,12 +265,12 @@
   const FX_FILESIZE last_crossref_offset = last_v4_table_offset;
 
   auto parser = MakeParserForBuffer(pdfium::as_bytes(pdfium::make_span(table)));
-  auto cross_ref_avail = pdfium::MakeUnique<CPDF_CrossRefAvail>(
-      parser.get(), last_crossref_offset);
-  EXPECT_EQ(CPDF_DataAvail::DataAvailable, cross_ref_avail->CheckAvail());
+  auto cross_ref_avail =
+      std::make_unique<CPDF_CrossRefAvail>(parser.get(), last_crossref_offset);
+  EXPECT_EQ(CPDF_DataAvail::kDataAvailable, cross_ref_avail->CheckAvail());
 }
 
-TEST(CPDF_CrossRefAvailTest, CrossRefV5IsNotStream) {
+TEST(CrossRefAvailTest, CrossRefV5IsNotStream) {
   const unsigned char invalid_xref_stream[] =
       "16 0 obj\n"
       "[/array /object]\n"
@@ -280,12 +279,12 @@
   const FX_FILESIZE last_crossref_offset = 0;
 
   auto parser = MakeParserForBuffer(invalid_xref_stream);
-  auto cross_ref_avail = pdfium::MakeUnique<CPDF_CrossRefAvail>(
-      parser.get(), last_crossref_offset);
-  EXPECT_EQ(CPDF_DataAvail::DataError, cross_ref_avail->CheckAvail());
+  auto cross_ref_avail =
+      std::make_unique<CPDF_CrossRefAvail>(parser.get(), last_crossref_offset);
+  EXPECT_EQ(CPDF_DataAvail::kDataError, cross_ref_avail->CheckAvail());
 }
 
-TEST(CPDF_CrossRefAvailTest, CrossRefV4WithEncryptRef) {
+TEST(CrossRefAvailTest, CrossRefV4WithEncryptRef) {
   const unsigned char xref_table[] =
       "xref \n"
       "0 6 \n"
@@ -303,12 +302,12 @@
   const FX_FILESIZE last_crossref_offset = 0;
 
   auto parser = MakeParserForBuffer(xref_table);
-  auto cross_ref_avail = pdfium::MakeUnique<CPDF_CrossRefAvail>(
-      parser.get(), last_crossref_offset);
-  EXPECT_EQ(CPDF_DataAvail::DataError, cross_ref_avail->CheckAvail());
+  auto cross_ref_avail =
+      std::make_unique<CPDF_CrossRefAvail>(parser.get(), last_crossref_offset);
+  EXPECT_EQ(CPDF_DataAvail::kDataError, cross_ref_avail->CheckAvail());
 }
 
-TEST(CPDF_CrossRefAvailTest, CrossRefStreamWithEncryptRef) {
+TEST(CrossRefAvailTest, CrossRefStreamWithEncryptRef) {
   const unsigned char xref_stream[] =
       "16 0 obj\n"
       "<</Filter /FlateDecode /Encrypt 77 0 R>>"
@@ -319,7 +318,7 @@
   const FX_FILESIZE last_crossref_offset = 0;
 
   auto parser = MakeParserForBuffer(xref_stream);
-  auto cross_ref_avail = pdfium::MakeUnique<CPDF_CrossRefAvail>(
-      parser.get(), last_crossref_offset);
-  EXPECT_EQ(CPDF_DataAvail::DataError, cross_ref_avail->CheckAvail());
+  auto cross_ref_avail =
+      std::make_unique<CPDF_CrossRefAvail>(parser.get(), last_crossref_offset);
+  EXPECT_EQ(CPDF_DataAvail::kDataError, cross_ref_avail->CheckAvail());
 }
diff --git a/core/fpdfapi/parser/cpdf_cross_ref_table.cpp b/core/fpdfapi/parser/cpdf_cross_ref_table.cpp
index bd25b6c..31177a3 100644
--- a/core/fpdfapi/parser/cpdf_cross_ref_table.cpp
+++ b/core/fpdfapi/parser/cpdf_cross_ref_table.cpp
@@ -1,15 +1,15 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/parser/cpdf_cross_ref_table.h"
 
 #include <utility>
-#include <vector>
 
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_parser.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/notreached.h"
 
 // static
 std::unique_ptr<CPDF_CrossRefTable> CPDF_CrossRefTable::MergeUp(
@@ -27,13 +27,16 @@
 
 CPDF_CrossRefTable::CPDF_CrossRefTable() = default;
 
-CPDF_CrossRefTable::CPDF_CrossRefTable(RetainPtr<CPDF_Dictionary> trailer)
-    : trailer_(std::move(trailer)) {}
+CPDF_CrossRefTable::CPDF_CrossRefTable(RetainPtr<CPDF_Dictionary> trailer,
+                                       uint32_t trailer_object_number)
+    : trailer_(std::move(trailer)),
+      trailer_object_number_(trailer_object_number) {}
 
 CPDF_CrossRefTable::~CPDF_CrossRefTable() = default;
 
 void CPDF_CrossRefTable::AddCompressed(uint32_t obj_num,
-                                       uint32_t archive_obj_num) {
+                                       uint32_t archive_obj_num,
+                                       uint32_t archive_obj_index) {
   if (obj_num >= CPDF_Parser::kMaxObjectNumber ||
       archive_obj_num >= CPDF_Parser::kMaxObjectNumber) {
     NOTREACHED();
@@ -48,7 +51,8 @@
     return;
 
   info.type = ObjectType::kCompressed;
-  info.archive_obj_num = archive_obj_num;
+  info.archive.obj_num = archive_obj_num;
+  info.archive.obj_index = archive_obj_index;
   info.gennum = 0;
 
   objects_info_[archive_obj_num].type = ObjectType::kObjStream;
@@ -88,8 +92,10 @@
   info.pos = 0;
 }
 
-void CPDF_CrossRefTable::SetTrailer(RetainPtr<CPDF_Dictionary> trailer) {
+void CPDF_CrossRefTable::SetTrailer(RetainPtr<CPDF_Dictionary> trailer,
+                                    uint32_t trailer_object_number) {
   trailer_ = std::move(trailer);
+  trailer_object_number_ = trailer_object_number;
 }
 
 const CPDF_CrossRefTable::ObjectInfo* CPDF_CrossRefTable::GetObjectInfo(
@@ -104,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::ContainsKey(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()) {
@@ -154,5 +170,5 @@
   new_trailer->SetFor("Prev", trailer_->RemoveFor("Prev"));
 
   for (const auto& key : new_trailer->GetKeys())
-    trailer_->SetFor(key, new_trailer->RemoveFor(key));
+    trailer_->SetFor(key, new_trailer->RemoveFor(key.AsStringView()));
 }
diff --git a/core/fpdfapi/parser/cpdf_cross_ref_table.h b/core/fpdfapi/parser/cpdf_cross_ref_table.h
index 66a51de..d76e29c 100644
--- a/core/fpdfapi/parser/cpdf_cross_ref_table.h
+++ b/core/fpdfapi/parser/cpdf_cross_ref_table.h
@@ -1,14 +1,16 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef CORE_FPDFAPI_PARSER_CPDF_CROSS_REF_TABLE_H_
 #define CORE_FPDFAPI_PARSER_CPDF_CROSS_REF_TABLE_H_
 
+#include <stdint.h>
+
 #include <map>
 #include <memory>
 
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/fx_types.h"
 #include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Dictionary;
@@ -25,16 +27,20 @@
   };
 
   struct ObjectInfo {
-    ObjectInfo() : pos(0), type(ObjectType::kFree), gennum(0) {}
-    // if type is ObjectType::kCompressed the archive_obj_num should be used.
-    // if type is ObjectType::kNotCompressed the pos should be used.
-    // In other cases its are unused.
+    ObjectInfo() = default;
+
+    // If `type` is `ObjectType::kCompressed`, `archive` should be used.
+    // If `type` is `ObjectType::kNotCompressed`, `pos` should be used.
+    // In other cases, it is unused.
     union {
-      FX_FILESIZE pos;
-      uint32_t archive_obj_num;
+      FX_FILESIZE pos = 0;
+      struct {
+        uint32_t obj_num;
+        uint32_t obj_index;
+      } archive;
     };
-    ObjectType type;
-    uint16_t gennum;
+    ObjectType type = ObjectType::kFree;
+    uint16_t gennum = 0;
   };
 
   // Merge cross reference tables.  Apply top on current.
@@ -43,14 +49,19 @@
       std::unique_ptr<CPDF_CrossRefTable> top);
 
   CPDF_CrossRefTable();
-  explicit CPDF_CrossRefTable(RetainPtr<CPDF_Dictionary> trailer);
+  CPDF_CrossRefTable(RetainPtr<CPDF_Dictionary> trailer,
+                     uint32_t trailer_object_number);
   ~CPDF_CrossRefTable();
 
-  void AddCompressed(uint32_t obj_num, uint32_t archive_obj_num);
+  void AddCompressed(uint32_t obj_num,
+                     uint32_t archive_obj_num,
+                     uint32_t archive_obj_index);
   void AddNormal(uint32_t obj_num, uint16_t gen_num, FX_FILESIZE pos);
   void SetFree(uint32_t obj_num);
 
-  void SetTrailer(RetainPtr<CPDF_Dictionary> trailer);
+  void SetTrailer(RetainPtr<CPDF_Dictionary> trailer,
+                  uint32_t trailer_object_number);
+  uint32_t trailer_object_number() const { return trailer_object_number_; }
   const CPDF_Dictionary* trailer() const { return trailer_.Get(); }
   CPDF_Dictionary* GetMutableTrailerForTesting() { return trailer_.Get(); }
 
@@ -62,13 +73,18 @@
 
   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_;
+  // `trailer_` can be the dictionary part of a XRef stream object. Since it is
+  // inline, it has no object number. Store the stream's object number, or 0 if
+  // there is none.
+  uint32_t trailer_object_number_ = 0;
   std::map<uint32_t, ObjectInfo> objects_info_;
 };
 
diff --git a/core/fpdfapi/parser/cpdf_crypto_handler.cpp b/core/fpdfapi/parser/cpdf_crypto_handler.cpp
index a19cf0a..43ae781 100644
--- a/core/fpdfapi/parser/cpdf_crypto_handler.cpp
+++ b/core/fpdfapi/parser/cpdf_crypto_handler.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,6 +23,8 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 
 namespace {
 
@@ -36,64 +38,55 @@
     const CPDF_Dictionary* dictionary) {
   if (!dictionary)
     return false;
-  const CPDF_Object* type_obj = dictionary->GetDirectObjectFor(kTypeKey);
+  RetainPtr<const CPDF_Object> type_obj =
+      dictionary->GetDirectObjectFor(kTypeKey);
   if (!type_obj)
     type_obj = dictionary->GetDirectObjectFor(pdfium::form_fields::kFT);
   return type_obj && type_obj->GetString() == pdfium::form_fields::kSig;
 }
 
-void CPDF_CryptoHandler::CryptBlock(bool bEncrypt,
-                                    uint32_t objnum,
-                                    uint32_t gennum,
-                                    pdfium::span<const uint8_t> source,
-                                    uint8_t* dest_buf,
-                                    uint32_t& dest_size) {
-  if (m_Cipher == FXCIPHER_NONE) {
+void CPDF_CryptoHandler::EncryptContent(uint32_t objnum,
+                                        uint32_t gennum,
+                                        pdfium::span<const uint8_t> source,
+                                        uint8_t* dest_buf,
+                                        size_t& dest_size) const {
+  if (m_Cipher == Cipher::kNone) {
     memcpy(dest_buf, source.data(), source.size());
     return;
   }
   uint8_t realkey[16];
   size_t realkeylen = sizeof(realkey);
-  if (m_Cipher != FXCIPHER_AES || m_KeyLen != 32) {
+  if (m_Cipher != Cipher::kAES || m_KeyLen != 32) {
     uint8_t key1[32];
     PopulateKey(objnum, gennum, key1);
 
-    if (m_Cipher == FXCIPHER_AES)
+    if (m_Cipher == Cipher::kAES)
       memcpy(key1 + m_KeyLen + 5, "sAlT", 4);
-    size_t len = m_Cipher == FXCIPHER_AES ? m_KeyLen + 9 : m_KeyLen + 5;
+    size_t len = m_Cipher == Cipher::kAES ? m_KeyLen + 9 : m_KeyLen + 5;
     CRYPT_MD5Generate({key1, len}, realkey);
     realkeylen = std::min(m_KeyLen + 5, sizeof(realkey));
   }
-  if (m_Cipher == FXCIPHER_AES) {
+  if (m_Cipher == Cipher::kAES) {
     CRYPT_AESSetKey(m_pAESContext.get(),
-                    m_KeyLen == 32 ? m_EncryptKey : realkey, m_KeyLen,
-                    bEncrypt);
-    if (bEncrypt) {
-      uint8_t iv[16];
-      for (int i = 0; i < 16; i++) {
-        iv[i] = (uint8_t)rand();
-      }
-      CRYPT_AESSetIV(m_pAESContext.get(), iv);
-      memcpy(dest_buf, iv, 16);
-      int nblocks = source.size() / 16;
-      CRYPT_AESEncrypt(m_pAESContext.get(), dest_buf + 16, source.data(),
-                       nblocks * 16);
-      uint8_t padding[16];
-      memcpy(padding, source.data() + nblocks * 16, source.size() % 16);
-      memset(padding + source.size() % 16, 16 - source.size() % 16,
-             16 - source.size() % 16);
-      CRYPT_AESEncrypt(m_pAESContext.get(), dest_buf + nblocks * 16 + 16,
-                       padding, 16);
-      dest_size = 32 + nblocks * 16;
-    } else {
-      CRYPT_AESSetIV(m_pAESContext.get(), source.data());
-      CRYPT_AESDecrypt(m_pAESContext.get(), dest_buf, source.data() + 16,
-                       source.size() - 16);
-      dest_size = source.size() - 16;
-      dest_size -= dest_buf[dest_size - 1];
+                    m_KeyLen == 32 ? m_EncryptKey : realkey, m_KeyLen);
+    uint8_t iv[16];
+    for (int i = 0; i < 16; i++) {
+      iv[i] = (uint8_t)rand();
     }
+    CRYPT_AESSetIV(m_pAESContext.get(), iv);
+    memcpy(dest_buf, iv, 16);
+    int nblocks = source.size() / 16;
+    CRYPT_AESEncrypt(m_pAESContext.get(), dest_buf + 16, source.data(),
+                     nblocks * 16);
+    uint8_t padding[16];
+    memcpy(padding, source.data() + nblocks * 16, source.size() % 16);
+    memset(padding + source.size() % 16, 16 - source.size() % 16,
+           16 - source.size() % 16);
+    CRYPT_AESEncrypt(m_pAESContext.get(), dest_buf + nblocks * 16 + 16, padding,
+                     16);
+    dest_size = 32 + nblocks * 16;
   } else {
-    ASSERT(dest_size == source.size());
+    DCHECK_EQ(dest_size, source.size());
     if (dest_buf != source.data())
       memcpy(dest_buf, source.data(), source.size());
     CRYPT_ArcFourCryptBlock({dest_buf, dest_size}, {realkey, realkeylen});
@@ -107,47 +100,33 @@
   uint8_t m_Block[16];
 };
 
-void* CPDF_CryptoHandler::CryptStart(uint32_t objnum,
-                                     uint32_t gennum,
-                                     bool bEncrypt) {
-  if (m_Cipher == FXCIPHER_NONE) {
+void* CPDF_CryptoHandler::DecryptStart(uint32_t objnum, uint32_t gennum) {
+  if (m_Cipher == Cipher::kNone)
     return this;
-  }
-  if (m_Cipher == FXCIPHER_AES && m_KeyLen == 32) {
+
+  if (m_Cipher == Cipher::kAES && m_KeyLen == 32) {
     AESCryptContext* pContext = FX_Alloc(AESCryptContext, 1);
     pContext->m_bIV = true;
     pContext->m_BlockOffset = 0;
-    CRYPT_AESSetKey(&pContext->m_Context, m_EncryptKey, 32, bEncrypt);
-    if (bEncrypt) {
-      for (int i = 0; i < 16; i++) {
-        pContext->m_Block[i] = (uint8_t)rand();
-      }
-      CRYPT_AESSetIV(&pContext->m_Context, pContext->m_Block);
-    }
+    CRYPT_AESSetKey(&pContext->m_Context, m_EncryptKey, 32);
     return pContext;
   }
   uint8_t key1[48];
   PopulateKey(objnum, gennum, key1);
 
-  if (m_Cipher == FXCIPHER_AES)
+  if (m_Cipher == Cipher::kAES)
     memcpy(key1 + m_KeyLen + 5, "sAlT", 4);
 
   uint8_t realkey[16];
-  size_t len = m_Cipher == FXCIPHER_AES ? m_KeyLen + 9 : m_KeyLen + 5;
+  size_t len = m_Cipher == Cipher::kAES ? m_KeyLen + 9 : m_KeyLen + 5;
   CRYPT_MD5Generate({key1, len}, realkey);
   size_t realkeylen = std::min(m_KeyLen + 5, sizeof(realkey));
 
-  if (m_Cipher == FXCIPHER_AES) {
+  if (m_Cipher == Cipher::kAES) {
     AESCryptContext* pContext = FX_Alloc(AESCryptContext, 1);
     pContext->m_bIV = true;
     pContext->m_BlockOffset = 0;
-    CRYPT_AESSetKey(&pContext->m_Context, realkey, 16, bEncrypt);
-    if (bEncrypt) {
-      for (int i = 0; i < 16; i++) {
-        pContext->m_Block[i] = (uint8_t)rand();
-      }
-      CRYPT_AESSetIV(&pContext->m_Context, pContext->m_Block);
-    }
+    CRYPT_AESSetKey(&pContext->m_Context, realkey, 16);
     return pContext;
   }
   CRYPT_rc4_context* pContext = FX_Alloc(CRYPT_rc4_context, 1);
@@ -155,32 +134,28 @@
   return pContext;
 }
 
-bool CPDF_CryptoHandler::CryptStream(void* context,
-                                     pdfium::span<const uint8_t> source,
-                                     CFX_BinaryBuf& dest_buf,
-                                     bool bEncrypt) {
+bool CPDF_CryptoHandler::DecryptStream(void* context,
+                                       pdfium::span<const uint8_t> source,
+                                       BinaryBuffer& dest_buf) {
   if (!context)
     return false;
 
-  if (m_Cipher == FXCIPHER_NONE) {
-    dest_buf.AppendBlock(source.data(), source.size());
+  if (m_Cipher == Cipher::kNone) {
+    dest_buf.AppendSpan(source);
     return true;
   }
-  if (m_Cipher == FXCIPHER_RC4) {
-    int old_size = dest_buf.GetSize();
-    dest_buf.AppendBlock(source.data(), source.size());
-    CRYPT_ArcFourCrypt(static_cast<CRYPT_rc4_context*>(context),
-                       dest_buf.GetSpan().subspan(old_size, source.size()));
+  if (m_Cipher == Cipher::kRC4) {
+    size_t old_size = dest_buf.GetSize();
+    dest_buf.AppendSpan(source);
+    CRYPT_ArcFourCrypt(
+        static_cast<CRYPT_rc4_context*>(context),
+        dest_buf.GetMutableSpan().subspan(old_size, source.size()));
     return true;
   }
   AESCryptContext* pContext = static_cast<AESCryptContext*>(context);
-  if (pContext->m_bIV && bEncrypt) {
-    dest_buf.AppendBlock(pContext->m_Block, 16);
-    pContext->m_bIV = false;
-  }
   uint32_t src_off = 0;
   uint32_t src_left = source.size();
-  while (1) {
+  while (true) {
     uint32_t copy_size = 16 - pContext->m_BlockOffset;
     if (copy_size > src_left) {
       copy_size = src_left;
@@ -191,20 +166,15 @@
     src_left -= copy_size;
     pContext->m_BlockOffset += copy_size;
     if (pContext->m_BlockOffset == 16) {
-      if (!bEncrypt && pContext->m_bIV) {
+      if (pContext->m_bIV) {
         CRYPT_AESSetIV(&pContext->m_Context, pContext->m_Block);
         pContext->m_bIV = false;
         pContext->m_BlockOffset = 0;
       } else if (src_off < source.size()) {
         uint8_t block_buf[16];
-        if (bEncrypt) {
-          CRYPT_AESEncrypt(&pContext->m_Context, block_buf, pContext->m_Block,
-                           16);
-        } else {
-          CRYPT_AESDecrypt(&pContext->m_Context, block_buf, pContext->m_Block,
-                           16);
-        }
-        dest_buf.AppendBlock(block_buf, 16);
+        CRYPT_AESDecrypt(&pContext->m_Context, block_buf, pContext->m_Block,
+                         16);
+        dest_buf.AppendSpan(block_buf);
         pContext->m_BlockOffset = 0;
       }
     }
@@ -214,37 +184,25 @@
   }
   return true;
 }
-bool CPDF_CryptoHandler::CryptFinish(void* context,
-                                     CFX_BinaryBuf& dest_buf,
-                                     bool bEncrypt) {
-  if (!context) {
+
+bool CPDF_CryptoHandler::DecryptFinish(void* context, BinaryBuffer& dest_buf) {
+  if (!context)
     return false;
-  }
-  if (m_Cipher == FXCIPHER_NONE) {
+
+  if (m_Cipher == Cipher::kNone)
     return true;
-  }
-  if (m_Cipher == FXCIPHER_RC4) {
+
+  if (m_Cipher == Cipher::kRC4) {
     FX_Free(context);
     return true;
   }
   auto* pContext = static_cast<AESCryptContext*>(context);
-  if (bEncrypt) {
-    uint8_t block_buf[16];
-    if (pContext->m_BlockOffset == 16) {
-      CRYPT_AESEncrypt(&pContext->m_Context, block_buf, pContext->m_Block, 16);
-      dest_buf.AppendBlock(block_buf, 16);
-      pContext->m_BlockOffset = 0;
-    }
-    memset(pContext->m_Block + pContext->m_BlockOffset,
-           (uint8_t)(16 - pContext->m_BlockOffset),
-           16 - pContext->m_BlockOffset);
-    CRYPT_AESEncrypt(&pContext->m_Context, block_buf, pContext->m_Block, 16);
-    dest_buf.AppendBlock(block_buf, 16);
-  } else if (pContext->m_BlockOffset == 16) {
+  if (pContext->m_BlockOffset == 16) {
     uint8_t block_buf[16];
     CRYPT_AESDecrypt(&pContext->m_Context, block_buf, pContext->m_Block, 16);
-    if (block_buf[15] <= 16) {
-      dest_buf.AppendBlock(block_buf, 16 - block_buf[15]);
+    if (block_buf[15] < 16) {
+      dest_buf.AppendSpan(
+          pdfium::make_span(block_buf).first(16 - block_buf[15]));
     }
   }
   FX_Free(pContext);
@@ -254,22 +212,19 @@
 ByteString CPDF_CryptoHandler::Decrypt(uint32_t objnum,
                                        uint32_t gennum,
                                        const ByteString& str) {
-  CFX_BinaryBuf dest_buf;
+  BinaryBuffer dest_buf;
   void* context = DecryptStart(objnum, gennum);
   DecryptStream(context, str.raw_span(), dest_buf);
   DecryptFinish(context, dest_buf);
-  return ByteString(dest_buf.GetBuffer(), dest_buf.GetSize());
+  return ByteString(dest_buf.GetSpan());
 }
 
-void* CPDF_CryptoHandler::DecryptStart(uint32_t objnum, uint32_t gennum) {
-  return CryptStart(objnum, gennum, false);
-}
-uint32_t CPDF_CryptoHandler::DecryptGetSize(uint32_t src_size) {
-  return m_Cipher == FXCIPHER_AES ? src_size - 16 : src_size;
+size_t CPDF_CryptoHandler::DecryptGetSize(size_t src_size) {
+  return m_Cipher == Cipher::kAES ? src_size - 16 : src_size;
 }
 
 bool CPDF_CryptoHandler::IsCipherAES() const {
-  return m_Cipher == FXCIPHER_AES;
+  return m_Cipher == Cipher::kAES;
 }
 
 bool CPDF_CryptoHandler::DecryptObjectTree(RetainPtr<CPDF_Object> object) {
@@ -277,20 +232,19 @@
     return false;
 
   struct MayBeSignature {
-    const CPDF_Dictionary* parent;
-    CPDF_Object* contents;
+    RetainPtr<const CPDF_Dictionary> parent;
+    RetainPtr<CPDF_Object> contents;
   };
 
   std::stack<MayBeSignature> may_be_sign_dictionaries;
   const uint32_t obj_num = object->GetObjNum();
   const uint32_t gen_num = object->GetGenNum();
 
-  CPDF_Object* object_to_decrypt = object.Get();
+  RetainPtr<CPDF_Object> object_to_decrypt = object;
   while (object_to_decrypt) {
-    CPDF_NonConstObjectWalker walker(object_to_decrypt);
-    object_to_decrypt = nullptr;
-    while (CPDF_Object* child = walker.GetNext()) {
-      const CPDF_Dictionary* parent_dict =
+    CPDF_NonConstObjectWalker walker(std::move(object_to_decrypt));
+    while (RetainPtr<CPDF_Object> child = walker.GetNext()) {
+      RetainPtr<const CPDF_Dictionary> parent_dict =
           walker.GetParent() ? walker.GetParent()->GetDict() : nullptr;
       if (walker.dictionary_key() == kContentsKey &&
           (parent_dict->KeyExist(kTypeKey) ||
@@ -301,21 +255,23 @@
         // Temporary skip it, to prevent signature corruption.
         // It will be decrypted on next interations, if this is not contents of
         // signature dictionary.
-        may_be_sign_dictionaries.push(MayBeSignature({parent_dict, child}));
+        may_be_sign_dictionaries.push(
+            {std::move(parent_dict), std::move(child)});
         walker.SkipWalkIntoCurrentObject();
         continue;
       }
       // Strings decryption.
       if (child->IsString()) {
         // TODO(art-snake): Move decryption into the CPDF_String class.
-        CPDF_String* str = child->AsString();
+        CPDF_String* str = child->AsMutableString();
         str->SetString(Decrypt(obj_num, gen_num, str->GetString()));
       }
       // Stream decryption.
       if (child->IsStream()) {
         // TODO(art-snake): Move decryption into the CPDF_Stream class.
-        CPDF_Stream* stream = child->AsStream();
-        auto stream_access = pdfium::MakeRetain<CPDF_StreamAcc>(stream);
+        CPDF_Stream* stream = child->AsMutableStream();
+        auto stream_access =
+            pdfium::MakeRetain<CPDF_StreamAcc>(pdfium::WrapRetain(stream));
         stream_access->LoadAllDataRaw();
 
         if (IsCipherAES() && stream_access->GetSize() < 16) {
@@ -323,7 +279,7 @@
           continue;
         }
 
-        CFX_BinaryBuf decrypted_buf;
+        BinaryBuffer decrypted_buf;
         decrypted_buf.EstimateSize(DecryptGetSize(stream_access->GetSize()));
 
         void* context = DecryptStart(obj_num, gen_num);
@@ -331,8 +287,7 @@
             DecryptStream(context, stream_access->GetSpan(), decrypted_buf);
         decrypt_result &= DecryptFinish(context, decrypted_buf);
         if (decrypt_result) {
-          const uint32_t decrypted_size = decrypted_buf.GetSize();
-          stream->TakeData(decrypted_buf.DetachBuffer(), decrypted_size);
+          stream->TakeData(decrypted_buf.DetachBuffer());
         } else {
           // Decryption failed, set the stream to empty
           stream->SetData({});
@@ -353,43 +308,24 @@
   return true;
 }
 
-bool CPDF_CryptoHandler::DecryptStream(void* context,
-                                       pdfium::span<const uint8_t> source,
-                                       CFX_BinaryBuf& dest_buf) {
-  return CryptStream(context, source, dest_buf, false);
-}
-
-bool CPDF_CryptoHandler::DecryptFinish(void* context, CFX_BinaryBuf& dest_buf) {
-  return CryptFinish(context, dest_buf, false);
-}
-
 size_t CPDF_CryptoHandler::EncryptGetSize(
     pdfium::span<const uint8_t> source) const {
-  return m_Cipher == FXCIPHER_AES ? source.size() + 32 : source.size();
+  return m_Cipher == Cipher::kAES ? source.size() + 32 : source.size();
 }
 
-bool CPDF_CryptoHandler::EncryptContent(uint32_t objnum,
-                                        uint32_t gennum,
-                                        pdfium::span<const uint8_t> source,
-                                        uint8_t* dest_buf,
-                                        uint32_t& dest_size) {
-  CryptBlock(true, objnum, gennum, source, dest_buf, dest_size);
-  return true;
-}
-
-CPDF_CryptoHandler::CPDF_CryptoHandler(int cipher,
+CPDF_CryptoHandler::CPDF_CryptoHandler(Cipher cipher,
                                        const uint8_t* key,
                                        size_t keylen)
     : m_KeyLen(std::min<size_t>(keylen, 32)), m_Cipher(cipher) {
-  ASSERT(cipher != FXCIPHER_AES || keylen == 16 || keylen == 24 ||
+  DCHECK(cipher != Cipher::kAES || keylen == 16 || keylen == 24 ||
          keylen == 32);
-  ASSERT(cipher != FXCIPHER_AES2 || keylen == 32);
-  ASSERT(cipher != FXCIPHER_RC4 || (keylen >= 5 && keylen <= 16));
+  DCHECK(cipher != Cipher::kAES2 || keylen == 32);
+  DCHECK(cipher != Cipher::kRC4 || (keylen >= 5 && keylen <= 16));
 
-  if (m_Cipher != FXCIPHER_NONE)
+  if (m_Cipher != Cipher::kNone)
     memcpy(m_EncryptKey, key, m_KeyLen);
 
-  if (m_Cipher == FXCIPHER_AES)
+  if (m_Cipher == Cipher::kAES)
     m_pAESContext.reset(FX_Alloc(CRYPT_aes_context, 1));
 }
 
@@ -397,7 +333,7 @@
 
 void CPDF_CryptoHandler::PopulateKey(uint32_t objnum,
                                      uint32_t gennum,
-                                     uint8_t* key) {
+                                     uint8_t* key) const {
   memcpy(key, m_EncryptKey, m_KeyLen);
   key[m_KeyLen + 0] = (uint8_t)objnum;
   key[m_KeyLen + 1] = (uint8_t)(objnum >> 8);
diff --git a/core/fpdfapi/parser/cpdf_crypto_handler.h b/core/fpdfapi/parser/cpdf_crypto_handler.h
index edfba97..ba934cc 100644
--- a/core/fpdfapi/parser/cpdf_crypto_handler.h
+++ b/core/fpdfapi/parser/cpdf_crypto_handler.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,63 +7,59 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_CRYPTO_HANDLER_H_
 #define CORE_FPDFAPI_PARSER_CPDF_CRYPTO_HANDLER_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <memory>
 
 #include "core/fdrm/fx_crypt.h"
-#include "core/fxcrt/cfx_binarybuf.h"
+#include "core/fxcrt/binary_buffer.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/span.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Dictionary;
 class CPDF_Object;
-class CPDF_SecurityHandler;
 
 class CPDF_CryptoHandler {
  public:
-  CPDF_CryptoHandler(int cipher, const uint8_t* key, size_t keylen);
-  ~CPDF_CryptoHandler();
+  enum class Cipher {
+    kNone = 0,
+    kRC4 = 1,
+    kAES = 2,
+    kAES2 = 3,
+  };
 
   static bool IsSignatureDictionary(const CPDF_Dictionary* dictionary);
 
+  CPDF_CryptoHandler(Cipher cipher, const uint8_t* key, size_t keylen);
+  ~CPDF_CryptoHandler();
+
   bool DecryptObjectTree(RetainPtr<CPDF_Object> object);
   size_t EncryptGetSize(pdfium::span<const uint8_t> source) const;
-  bool EncryptContent(uint32_t objnum,
+  void EncryptContent(uint32_t objnum,
                       uint32_t gennum,
                       pdfium::span<const uint8_t> source,
                       uint8_t* dest_buf,
-                      uint32_t& dest_size);
+                      size_t& dest_size) const;
 
   bool IsCipherAES() const;
 
  private:
-  uint32_t DecryptGetSize(uint32_t src_size);
+  size_t DecryptGetSize(size_t src_size);
   void* DecryptStart(uint32_t objnum, uint32_t gennum);
   ByteString Decrypt(uint32_t objnum, uint32_t gennum, const ByteString& str);
   bool DecryptStream(void* context,
                      pdfium::span<const uint8_t> source,
-                     CFX_BinaryBuf& dest_buf);
-  bool DecryptFinish(void* context, CFX_BinaryBuf& dest_buf);
-
-  void PopulateKey(uint32_t objnum, uint32_t gennum, uint8_t* key);
-  void CryptBlock(bool bEncrypt,
-                  uint32_t objnum,
-                  uint32_t gennum,
-                  pdfium::span<const uint8_t> source,
-                  uint8_t* dest_buf,
-                  uint32_t& dest_size);
-  void* CryptStart(uint32_t objnum, uint32_t gennum, bool bEncrypt);
-  bool CryptStream(void* context,
-                   pdfium::span<const uint8_t> source,
-                   CFX_BinaryBuf& dest_buf,
-                   bool bEncrypt);
-  bool CryptFinish(void* context, CFX_BinaryBuf& dest_buf, bool bEncrypt);
+                     BinaryBuffer& dest_buf);
+  bool DecryptFinish(void* context, BinaryBuffer& dest_buf);
+  void PopulateKey(uint32_t objnum, uint32_t gennum, uint8_t* key) const;
 
   const size_t m_KeyLen;
-  const int m_Cipher;
+  const Cipher m_Cipher;
   std::unique_ptr<CRYPT_aes_context, FxFreeDeleter> m_pAESContext;
-  uint8_t m_EncryptKey[32];
+  uint8_t m_EncryptKey[32] = {};
 };
 
 #endif  // CORE_FPDFAPI_PARSER_CPDF_CRYPTO_HANDLER_H_
diff --git a/core/fpdfapi/parser/cpdf_data_avail.cpp b/core/fpdfapi/parser/cpdf_data_avail.cpp
index 457ae61..bc3ff33 100644
--- a/core/fpdfapi/parser/cpdf_data_avail.cpp
+++ b/core/fpdfapi/parser/cpdf_data_avail.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -24,32 +24,31 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fxcrt/autorestorer.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/compiler_specific.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/notreached.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
 
 namespace {
 
-// static
-CPDF_Object* GetResourceObject(CPDF_Dictionary* pDict) {
+RetainPtr<CPDF_Object> GetResourceObject(RetainPtr<CPDF_Dictionary> pDict) {
   constexpr size_t kMaxHierarchyDepth = 64;
   size_t depth = 0;
 
-  CPDF_Dictionary* dictionary_to_check = pDict;
-  while (dictionary_to_check) {
-    CPDF_Object* result = dictionary_to_check->GetObjectFor("Resources");
+  while (pDict) {
+    RetainPtr<CPDF_Object> result = pDict->GetMutableObjectFor("Resources");
     if (result)
       return result;
-    CPDF_Object* parent = dictionary_to_check->GetObjectFor("Parent");
-    dictionary_to_check = parent ? parent->GetDict() : nullptr;
-
     if (++depth > kMaxHierarchyDepth) {
       // We have cycle in parents hierarchy.
       return nullptr;
     }
+    RetainPtr<CPDF_Object> parent = pDict->GetMutableObjectFor("Parent");
+    pDict = parent ? parent->GetMutableDict() : nullptr;
   }
   return nullptr;
 }
@@ -59,7 +58,7 @@
   HintsScope(RetainPtr<CPDF_ReadValidator> validator,
              CPDF_DataAvail::DownloadHints* hints)
       : validator_(std::move(validator)) {
-    ASSERT(validator_);
+    DCHECK(validator_);
     validator_->SetDownloadHints(hints);
   }
 
@@ -71,18 +70,15 @@
 
 }  // namespace
 
-CPDF_DataAvail::FileAvail::~FileAvail() {}
+CPDF_DataAvail::FileAvail::~FileAvail() = default;
 
-CPDF_DataAvail::DownloadHints::~DownloadHints() {}
+CPDF_DataAvail::DownloadHints::~DownloadHints() = default;
 
-CPDF_DataAvail::CPDF_DataAvail(
-    FileAvail* pFileAvail,
-    const RetainPtr<IFX_SeekableReadStream>& pFileRead,
-    bool bSupportHintTable)
-    : m_pFileRead(
-          pdfium::MakeRetain<CPDF_ReadValidator>(pFileRead, pFileAvail)),
-      m_dwFileLen(m_pFileRead->GetSize()),
-      m_bSupportHintTable(bSupportHintTable) {}
+CPDF_DataAvail::CPDF_DataAvail(FileAvail* pFileAvail,
+                               RetainPtr<IFX_SeekableReadStream> pFileRead)
+    : m_pFileRead(pdfium::MakeRetain<CPDF_ReadValidator>(std::move(pFileRead),
+                                                         pFileAvail)),
+      m_dwFileLen(m_pFileRead->GetSize()) {}
 
 CPDF_DataAvail::~CPDF_DataAvail() {
   m_pHintTables.reset();
@@ -101,47 +97,49 @@
 CPDF_DataAvail::DocAvailStatus CPDF_DataAvail::IsDocAvail(
     DownloadHints* pHints) {
   if (!m_dwFileLen)
-    return DataError;
+    return kDataError;
 
+  DCHECK(m_SeenPageObjList.empty());
+  AutoRestorer<std::set<uint32_t>> seen_objects_restorer(&m_SeenPageObjList);
   const HintsScope hints_scope(GetValidator(), pHints);
   while (!m_bDocAvail) {
     if (!CheckDocStatus())
-      return DataNotAvailable;
+      return kDataNotAvailable;
   }
 
-  return DataAvailable;
+  return kDataAvailable;
 }
 
 bool CPDF_DataAvail::CheckDocStatus() {
-  switch (m_docStatus) {
-    case PDF_DATAAVAIL_HEADER:
+  switch (m_internalStatus) {
+    case InternalStatus::kHeader:
       return CheckHeader();
-    case PDF_DATAAVAIL_FIRSTPAGE:
+    case InternalStatus::kFirstPage:
       return CheckFirstPage();
-    case PDF_DATAAVAIL_HINTTABLE:
+    case InternalStatus::kHintTable:
       return CheckHintTables();
-    case PDF_DATAAVAIL_LOADALLCROSSREF:
+    case InternalStatus::kLoadAllCrossRef:
       return CheckAndLoadAllXref();
-    case PDF_DATAAVAIL_LOADALLFILE:
+    case InternalStatus::kLoadAllFile:
       return LoadAllFile();
-    case PDF_DATAAVAIL_ROOT:
+    case InternalStatus::kRoot:
       return CheckRoot();
-    case PDF_DATAAVAIL_INFO:
+    case InternalStatus::kInfo:
       return CheckInfo();
-    case PDF_DATAAVAIL_PAGETREE:
+    case InternalStatus::kPageTree:
       if (m_bTotalLoadPageTree)
         return CheckPages();
       return LoadDocPages();
-    case PDF_DATAAVAIL_PAGE:
+    case InternalStatus::kPage:
       if (m_bTotalLoadPageTree)
         return CheckPage();
-      m_docStatus = PDF_DATAAVAIL_PAGE_LATERLOAD;
+      m_internalStatus = InternalStatus::kPageLaterLoad;
       return true;
-    case PDF_DATAAVAIL_ERROR:
+    case InternalStatus::kError:
       return LoadAllFile();
-    case PDF_DATAAVAIL_PAGE_LATERLOAD:
-      m_docStatus = PDF_DATAAVAIL_PAGE;
-      FALLTHROUGH;
+    case InternalStatus::kPageLaterLoad:
+      m_internalStatus = InternalStatus::kPage;
+      [[fallthrough]];
     default:
       m_bDocAvail = true;
       return true;
@@ -149,12 +147,12 @@
 }
 
 bool CPDF_DataAvail::CheckPageStatus() {
-  switch (m_docStatus) {
-    case PDF_DATAAVAIL_PAGETREE:
+  switch (m_internalStatus) {
+    case InternalStatus::kPageTree:
       return CheckPages();
-    case PDF_DATAAVAIL_PAGE:
+    case InternalStatus::kPage:
       return CheckPage();
-    case PDF_DATAAVAIL_ERROR:
+    case InternalStatus::kError:
       return LoadAllFile();
     default:
       m_bPagesTreeLoad = true;
@@ -165,7 +163,7 @@
 
 bool CPDF_DataAvail::LoadAllFile() {
   if (GetValidator()->CheckWholeFileAndRequestIfUnavailable()) {
-    m_docStatus = PDF_DATAAVAIL_DONE;
+    m_internalStatus = InternalStatus::kDone;
     return true;
   }
   return false;
@@ -173,62 +171,55 @@
 
 bool CPDF_DataAvail::CheckAndLoadAllXref() {
   if (!m_pCrossRefAvail) {
-    const CPDF_ReadValidator::Session read_session(GetValidator());
+    CPDF_ReadValidator::ScopedSession read_session(GetValidator());
     const FX_FILESIZE last_xref_offset = m_parser.ParseStartXRef();
     if (GetValidator()->has_read_problems())
       return false;
 
     if (last_xref_offset <= 0) {
-      m_docStatus = PDF_DATAAVAIL_ERROR;
+      m_internalStatus = InternalStatus::kError;
       return false;
     }
 
-    m_pCrossRefAvail = pdfium::MakeUnique<CPDF_CrossRefAvail>(GetSyntaxParser(),
-                                                              last_xref_offset);
+    m_pCrossRefAvail = std::make_unique<CPDF_CrossRefAvail>(GetSyntaxParser(),
+                                                            last_xref_offset);
   }
 
   switch (m_pCrossRefAvail->CheckAvail()) {
-    case DocAvailStatus::DataAvailable:
+    case kDataAvailable:
       break;
-    case DocAvailStatus::DataNotAvailable:
+    case kDataNotAvailable:
       return false;
-    case DocAvailStatus::DataError:
-      m_docStatus = PDF_DATAAVAIL_ERROR;
-      return false;
-    default:
-      NOTREACHED();
+    case kDataError:
+      m_internalStatus = InternalStatus::kError;
       return false;
   }
 
   if (!m_parser.LoadAllCrossRefV4(m_pCrossRefAvail->last_crossref_offset()) &&
       !m_parser.LoadAllCrossRefV5(m_pCrossRefAvail->last_crossref_offset())) {
-    m_docStatus = PDF_DATAAVAIL_LOADALLFILE;
+    m_internalStatus = InternalStatus::kLoadAllFile;
     return false;
   }
 
-  m_docStatus = PDF_DATAAVAIL_ROOT;
+  m_internalStatus = InternalStatus::kRoot;
   return true;
 }
 
 RetainPtr<CPDF_Object> CPDF_DataAvail::GetObject(uint32_t objnum,
                                                  bool* pExistInFile) {
-  CPDF_Parser* pParser = nullptr;
+  *pExistInFile = false;
+  CPDF_Parser* pParser = m_pDocument ? m_pDocument->GetParser() : &m_parser;
+  if (!pParser)
+    return nullptr;
 
-  if (pExistInFile)
-    *pExistInFile = true;
+  CPDF_ReadValidator::ScopedSession read_session(GetValidator());
+  RetainPtr<CPDF_Object> pRet = pParser->ParseIndirectObject(objnum);
+  if (!pRet)
+    return nullptr;
 
-  pParser = m_pDocument ? m_pDocument->GetParser() : &m_parser;
-
-  RetainPtr<CPDF_Object> pRet;
-  if (pParser) {
-    const CPDF_ReadValidator::Session read_session(GetValidator());
-    pRet = pParser->ParseIndirectObject(objnum);
-    if (GetValidator()->has_read_problems())
-      return nullptr;
-  }
-
-  if (!pRet && pExistInFile)
-    *pExistInFile = false;
+  *pExistInFile = true;
+  if (GetValidator()->has_read_problems())
+    return nullptr;
 
   return pRet;
 }
@@ -236,54 +227,64 @@
 bool CPDF_DataAvail::CheckInfo() {
   const uint32_t dwInfoObjNum = m_parser.GetInfoObjNum();
   if (dwInfoObjNum == CPDF_Object::kInvalidObjNum) {
-    m_docStatus = PDF_DATAAVAIL_PAGETREE;
+    m_internalStatus = InternalStatus::kPageTree;
     return true;
   }
 
-  const CPDF_ReadValidator::Session read_session(GetValidator());
+  CPDF_ReadValidator::ScopedSession read_session(GetValidator());
   m_parser.ParseIndirectObject(dwInfoObjNum);
   if (GetValidator()->has_read_problems())
     return false;
 
-  m_docStatus = PDF_DATAAVAIL_PAGETREE;
+  m_internalStatus = InternalStatus::kPageTree;
   return true;
 }
 
 bool CPDF_DataAvail::CheckRoot() {
   const uint32_t dwRootObjNum = m_parser.GetRootObjNum();
   if (dwRootObjNum == CPDF_Object::kInvalidObjNum) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
+    m_internalStatus = InternalStatus::kError;
     return true;
   }
 
-  const CPDF_ReadValidator::Session read_session(GetValidator());
+  CPDF_ReadValidator::ScopedSession read_session(GetValidator());
   m_pRoot = ToDictionary(m_parser.ParseIndirectObject(dwRootObjNum));
   if (GetValidator()->has_read_problems())
     return false;
 
-  const CPDF_Reference* pRef =
-      ToReference(m_pRoot ? m_pRoot->GetObjectFor("Pages") : nullptr);
+  if (!m_pRoot) {
+    m_internalStatus = InternalStatus::kError;
+    return false;
+  }
+
+  RetainPtr<const CPDF_Reference> pRef =
+      ToReference(m_pRoot->GetObjectFor("Pages"));
   if (!pRef) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
+    m_internalStatus = InternalStatus::kError;
     return false;
   }
 
   m_PagesObjNum = pRef->GetRefObjNum();
-  m_docStatus = PDF_DATAAVAIL_INFO;
+  m_internalStatus = InternalStatus::kInfo;
   return true;
 }
 
 bool CPDF_DataAvail::PreparePageItem() {
   const CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
-  const CPDF_Reference* pRef =
-      ToReference(pRoot ? pRoot->GetObjectFor("Pages") : nullptr);
+  if (!pRoot) {
+    m_internalStatus = InternalStatus::kError;
+    return false;
+  }
+
+  RetainPtr<const CPDF_Reference> pRef =
+      ToReference(pRoot->GetObjectFor("Pages"));
   if (!pRef) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
+    m_internalStatus = InternalStatus::kError;
     return false;
   }
 
   m_PagesObjNum = pRef->GetRefObjNum();
-  m_docStatus = PDF_DATAAVAIL_PAGETREE;
+  m_internalStatus = InternalStatus::kPageTree;
   return true;
 }
 
@@ -305,21 +306,23 @@
         UnavailObjList.push_back(dwPageObjNum);
       continue;
     }
-    CPDF_Array* pArray = ToArray(pObj.Get());
-    if (pArray) {
-      CPDF_ArrayLocker locker(pArray);
-      for (const auto& pArrayObj : locker) {
-        if (CPDF_Reference* pRef = ToReference(pArrayObj.Get()))
-          UnavailObjList.push_back(pRef->GetRefObjNum());
-      }
-    }
-    if (!pObj->IsDictionary())
-      continue;
 
-    ByteString type = pObj->GetDict()->GetStringFor("Type");
-    if (type == "Pages") {
-      m_PagesArray.push_back(std::move(pObj));
-      continue;
+    switch (pObj->GetType()) {
+      case CPDF_Object::kArray: {
+        CPDF_ArrayLocker locker(pObj->AsArray());
+        for (const auto& pArrayObj : locker) {
+          const CPDF_Reference* pRef = ToReference(pArrayObj.Get());
+          if (pRef)
+            UnavailObjList.push_back(pRef->GetRefObjNum());
+        }
+        break;
+      }
+      case CPDF_Object::kDictionary:
+        if (pObj->GetDict()->GetNameFor("Type") == "Pages")
+          m_PagesArray.push_back(std::move(pObj));
+        break;
+      default:
+        break;
     }
   }
   m_PageObjList.clear();
@@ -332,39 +335,50 @@
     RetainPtr<CPDF_Object> pPages = std::move(m_PagesArray[i]);
     if (pPages && !GetPageKids(pPages.Get())) {
       m_PagesArray.clear();
-      m_docStatus = PDF_DATAAVAIL_ERROR;
+      m_internalStatus = InternalStatus::kError;
       return false;
     }
   }
   m_PagesArray.clear();
   if (m_PageObjList.empty())
-    m_docStatus = PDF_DATAAVAIL_DONE;
+    m_internalStatus = InternalStatus::kDone;
 
   return true;
 }
 
 bool CPDF_DataAvail::GetPageKids(CPDF_Object* pPages) {
-  CPDF_Dictionary* pDict = pPages->GetDict();
-  CPDF_Object* pKids = pDict ? pDict->GetObjectFor("Kids") : nullptr;
+  RetainPtr<const CPDF_Dictionary> pDict = pPages->GetDict();
+  if (!pDict)
+    return true;
+
+  RetainPtr<const CPDF_Object> pKids = pDict->GetObjectFor("Kids");
   if (!pKids)
     return true;
 
+  std::vector<uint32_t> object_numbers;
   switch (pKids->GetType()) {
     case CPDF_Object::kReference:
-      m_PageObjList.push_back(pKids->AsReference()->GetRefObjNum());
+      object_numbers.push_back(pKids->AsReference()->GetRefObjNum());
       break;
     case CPDF_Object::kArray: {
-      CPDF_Array* pKidsArray = pKids->AsArray();
-      for (size_t i = 0; i < pKidsArray->size(); ++i) {
-        if (CPDF_Reference* pRef = ToReference(pKidsArray->GetObjectAt(i)))
-          m_PageObjList.push_back(pRef->GetRefObjNum());
+      CPDF_ArrayLocker locker(pKids->AsArray());
+      for (const auto& pArrayObj : locker) {
+        const CPDF_Reference* pRef = ToReference(pArrayObj.Get());
+        if (pRef)
+          object_numbers.push_back(pRef->GetRefObjNum());
       }
       break;
     }
     default:
-      m_docStatus = PDF_DATAAVAIL_ERROR;
+      m_internalStatus = InternalStatus::kError;
       return false;
   }
+
+  for (uint32_t num : object_numbers) {
+    bool inserted = m_SeenPageObjList.insert(num).second;
+    if (inserted)
+      m_PageObjList.push_back(num);
+  }
   return true;
 }
 
@@ -372,41 +386,38 @@
   bool bExists = false;
   RetainPtr<CPDF_Object> pPages = GetObject(m_PagesObjNum, &bExists);
   if (!bExists) {
-    m_docStatus = PDF_DATAAVAIL_LOADALLFILE;
+    m_internalStatus = InternalStatus::kLoadAllFile;
     return true;
   }
 
   if (!pPages) {
-    if (m_docStatus == PDF_DATAAVAIL_ERROR) {
-      m_docStatus = PDF_DATAAVAIL_LOADALLFILE;
+    if (m_internalStatus == InternalStatus::kError) {
+      m_internalStatus = InternalStatus::kLoadAllFile;
       return true;
     }
     return false;
   }
 
   if (!GetPageKids(pPages.Get())) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
+    m_internalStatus = InternalStatus::kError;
     return false;
   }
 
-  m_docStatus = PDF_DATAAVAIL_PAGE;
+  m_internalStatus = InternalStatus::kPage;
   return true;
 }
 
 bool CPDF_DataAvail::CheckHeader() {
   switch (CheckHeaderAndLinearized()) {
-    case DocAvailStatus::DataAvailable:
-      m_docStatus = m_pLinearized ? PDF_DATAAVAIL_FIRSTPAGE
-                                  : PDF_DATAAVAIL_LOADALLCROSSREF;
+    case kDataAvailable:
+      m_internalStatus = m_pLinearized ? InternalStatus::kFirstPage
+                                       : InternalStatus::kLoadAllCrossRef;
       return true;
-    case DocAvailStatus::DataNotAvailable:
+    case kDataNotAvailable:
       return false;
-    case DocAvailStatus::DataError:
-      m_docStatus = PDF_DATAAVAIL_ERROR;
+    case kDataError:
+      m_internalStatus = InternalStatus::kError;
       return true;
-    default:
-      NOTREACHED();
-      return false;
   }
 }
 
@@ -414,7 +425,7 @@
   if (!m_pLinearized->GetFirstPageEndOffset() ||
       !m_pLinearized->GetFileSize() ||
       !m_pLinearized->GetMainXRefTableFirstEntryOffset()) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
+    m_internalStatus = InternalStatus::kError;
     return false;
   }
 
@@ -429,24 +440,23 @@
                                                              data_size))
     return false;
 
-  m_docStatus =
-      m_bSupportHintTable ? PDF_DATAAVAIL_HINTTABLE : PDF_DATAAVAIL_DONE;
+  m_internalStatus = InternalStatus::kHintTable;
   return true;
 }
 
 bool CPDF_DataAvail::CheckHintTables() {
-  const CPDF_ReadValidator::Session read_session(GetValidator());
+  CPDF_ReadValidator::ScopedSession read_session(GetValidator());
   m_pHintTables =
       CPDF_HintTables::Parse(GetSyntaxParser(), m_pLinearized.get());
 
   if (GetValidator()->read_error()) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
+    m_internalStatus = InternalStatus::kError;
     return true;
   }
   if (GetValidator()->has_unavailable_data())
     return false;
 
-  m_docStatus = PDF_DATAAVAIL_DONE;
+  m_internalStatus = InternalStatus::kDone;
   return true;
 }
 
@@ -466,59 +476,56 @@
 
 CPDF_DataAvail::DocLinearizationStatus CPDF_DataAvail::IsLinearizedPDF() {
   switch (CheckHeaderAndLinearized()) {
-    case DocAvailStatus::DataAvailable:
-      return m_pLinearized ? DocLinearizationStatus::Linearized
-                           : DocLinearizationStatus::NotLinearized;
-    case DocAvailStatus::DataNotAvailable:
-      return DocLinearizationStatus::LinearizationUnknown;
-    case DocAvailStatus::DataError:
-      return DocLinearizationStatus::NotLinearized;
-    default:
-      NOTREACHED();
-      return DocLinearizationStatus::LinearizationUnknown;
+    case kDataAvailable:
+      return m_pLinearized ? kLinearized : kNotLinearized;
+    case kDataNotAvailable:
+      return kLinearizationUnknown;
+    case kDataError:
+      return kNotLinearized;
   }
 }
 
 CPDF_DataAvail::DocAvailStatus CPDF_DataAvail::CheckHeaderAndLinearized() {
   if (m_bHeaderAvail)
-    return DocAvailStatus::DataAvailable;
+    return kDataAvailable;
 
-  const CPDF_ReadValidator::Session read_session(GetValidator());
-  const Optional<FX_FILESIZE> header_offset = GetHeaderOffset(GetValidator());
+  CPDF_ReadValidator::ScopedSession read_session(GetValidator());
+  const absl::optional<FX_FILESIZE> header_offset =
+      GetHeaderOffset(GetValidator());
   if (GetValidator()->has_read_problems())
-    return DocAvailStatus::DataNotAvailable;
+    return kDataNotAvailable;
 
-  if (!header_offset)
-    return DocAvailStatus::DataError;
+  if (!header_offset.has_value())
+    return kDataError;
 
-  m_parser.m_pSyntax =
-      pdfium::MakeUnique<CPDF_SyntaxParser>(GetValidator(), *header_offset);
+  m_parser.m_pSyntax = std::make_unique<CPDF_SyntaxParser>(
+      GetValidator(), header_offset.value());
   m_pLinearized = m_parser.ParseLinearizedHeader();
   if (GetValidator()->has_read_problems())
-    return DocAvailStatus::DataNotAvailable;
+    return kDataNotAvailable;
 
   m_bHeaderAvail = true;
-  return DocAvailStatus::DataAvailable;
+  return kDataAvailable;
 }
 
 bool CPDF_DataAvail::CheckPage(uint32_t dwPage) {
   while (true) {
-    switch (m_docStatus) {
-      case PDF_DATAAVAIL_PAGETREE:
+    switch (m_internalStatus) {
+      case InternalStatus::kPageTree:
         if (!LoadDocPages())
           return false;
         break;
-      case PDF_DATAAVAIL_PAGE:
+      case InternalStatus::kPage:
         if (!LoadDocPage(dwPage))
           return false;
         break;
-      case PDF_DATAAVAIL_ERROR:
+      case InternalStatus::kError:
         return LoadAllFile();
       default:
         m_bPagesTreeLoad = true;
         m_bPagesLoad = true;
         m_bCurPageDictLoadOK = true;
-        m_docStatus = PDF_DATAAVAIL_PAGE;
+        m_internalStatus = InternalStatus::kPage;
         return true;
     }
   }
@@ -529,26 +536,26 @@
   bool bExists = false;
   RetainPtr<CPDF_Object> pPages = GetObject(dwPageNo, &bExists);
   if (!bExists) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
+    m_internalStatus = InternalStatus::kError;
     return false;
   }
 
   if (!pPages)
     return false;
 
-  CPDF_Array* pArray = pPages->AsArray();
+  const CPDF_Array* pArray = pPages->AsArray();
   if (!pArray) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
+    m_internalStatus = InternalStatus::kError;
     return false;
   }
 
-  pPageNode->m_type = PDF_PAGENODE_PAGES;
+  pPageNode->m_type = PageNode::Type::kPages;
   for (size_t i = 0; i < pArray->size(); ++i) {
-    CPDF_Reference* pKid = ToReference(pArray->GetObjectAt(i));
+    RetainPtr<const CPDF_Reference> pKid = ToReference(pArray->GetObjectAt(i));
     if (!pKid)
       continue;
 
-    auto pNode = pdfium::MakeUnique<PageNode>();
+    auto pNode = std::make_unique<PageNode>();
     pNode->m_dwPageNo = pKid->GetRefObjNum();
     pPageNode->m_ChildNodes.push_back(std::move(pNode));
   }
@@ -560,7 +567,7 @@
   bool bExists = false;
   RetainPtr<CPDF_Object> pPage = GetObject(dwPageNo, &bExists);
   if (!bExists) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
+    m_internalStatus = InternalStatus::kError;
     return false;
   }
 
@@ -569,51 +576,52 @@
 
   if (pPage->IsArray()) {
     pPageNode->m_dwPageNo = dwPageNo;
-    pPageNode->m_type = PDF_PAGENODE_ARRAY;
+    pPageNode->m_type = PageNode::Type::kArray;
     return true;
   }
 
   if (!pPage->IsDictionary()) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
+    m_internalStatus = InternalStatus::kError;
     return false;
   }
 
   pPageNode->m_dwPageNo = dwPageNo;
-  CPDF_Dictionary* pDict = pPage->GetDict();
-  const ByteString type = pDict->GetStringFor("Type");
+  RetainPtr<CPDF_Dictionary> pDict = pPage->GetMutableDict();
+  const ByteString type = pDict->GetNameFor("Type");
   if (type == "Page") {
-    pPageNode->m_type = PDF_PAGENODE_PAGE;
+    pPageNode->m_type = PageNode::Type::kPage;
     return true;
   }
 
   if (type != "Pages") {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
+    m_internalStatus = InternalStatus::kError;
     return false;
   }
 
-  pPageNode->m_type = PDF_PAGENODE_PAGES;
-  CPDF_Object* pKids = pDict->GetObjectFor("Kids");
+  pPageNode->m_type = PageNode::Type::kPages;
+  RetainPtr<CPDF_Object> pKids = pDict->GetMutableObjectFor("Kids");
   if (!pKids) {
-    m_docStatus = PDF_DATAAVAIL_PAGE;
+    m_internalStatus = InternalStatus::kPage;
     return true;
   }
 
   switch (pKids->GetType()) {
     case CPDF_Object::kReference: {
-      CPDF_Reference* pKid = pKids->AsReference();
-      auto pNode = pdfium::MakeUnique<PageNode>();
+      const CPDF_Reference* pKid = pKids->AsReference();
+      auto pNode = std::make_unique<PageNode>();
       pNode->m_dwPageNo = pKid->GetRefObjNum();
       pPageNode->m_ChildNodes.push_back(std::move(pNode));
       break;
     }
     case CPDF_Object::kArray: {
-      CPDF_Array* pKidsArray = pKids->AsArray();
+      const CPDF_Array* pKidsArray = pKids->AsArray();
       for (size_t i = 0; i < pKidsArray->size(); ++i) {
-        CPDF_Reference* pKid = ToReference(pKidsArray->GetObjectAt(i));
+        RetainPtr<const CPDF_Reference> pKid =
+            ToReference(pKidsArray->GetObjectAt(i));
         if (!pKid)
           continue;
 
-        auto pNode = pdfium::MakeUnique<PageNode>();
+        auto pNode = std::make_unique<PageNode>();
         pNode->m_dwPageNo = pKid->GetRefObjNum();
         pPageNode->m_ChildNodes.push_back(std::move(pNode));
       }
@@ -632,9 +640,9 @@
   if (level >= kMaxPageRecursionDepth)
     return false;
 
-  int32_t iSize = pdfium::CollectionSize<int32_t>(pageNode.m_ChildNodes);
+  int32_t iSize = fxcrt::CollectionSize<int32_t>(pageNode.m_ChildNodes);
   if (iSize <= 0 || iPage >= iSize) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
+    m_internalStatus = InternalStatus::kError;
     return false;
   }
   for (int32_t i = 0; i < iSize; ++i) {
@@ -642,33 +650,33 @@
     if (!pNode)
       continue;
 
-    if (pNode->m_type == PDF_PAGENODE_UNKNOWN) {
+    if (pNode->m_type == PageNode::Type::kUnknown) {
       // Updates the type for the unknown page node.
       if (!CheckUnknownPageNode(pNode->m_dwPageNo, pNode))
         return false;
     }
-    if (pNode->m_type == PDF_PAGENODE_ARRAY) {
+    if (pNode->m_type == PageNode::Type::kArray) {
       // Updates a more specific type for the array page node.
       if (!CheckArrayPageNode(pNode->m_dwPageNo, pNode))
         return false;
     }
     switch (pNode->m_type) {
-      case PDF_PAGENODE_PAGE:
+      case PageNode::Type::kPage:
         iCount++;
         if (iPage == iCount && m_pDocument)
           m_pDocument->SetPageObjNum(iPage, pNode->m_dwPageNo);
         break;
-      case PDF_PAGENODE_PAGES:
+      case PageNode::Type::kPages:
         if (!CheckPageNode(*pNode, iPage, iCount, level + 1))
           return false;
         break;
-      case PDF_PAGENODE_UNKNOWN:
-      case PDF_PAGENODE_ARRAY:
+      case PageNode::Type::kUnknown:
+      case PageNode::Type::kArray:
         // Already converted above, error if we get here.
         return false;
     }
     if (iPage == iCount) {
-      m_docStatus = PDF_DATAAVAIL_DONE;
+      m_internalStatus = InternalStatus::kDone;
       return true;
     }
   }
@@ -676,15 +684,15 @@
 }
 
 bool CPDF_DataAvail::LoadDocPage(uint32_t dwPage) {
-  FX_SAFE_INT32 safePage = pdfium::base::checked_cast<int32_t>(dwPage);
-  int32_t iPage = safePage.ValueOrDie();
+  int iPage = pdfium::base::checked_cast<int>(dwPage);
   if (m_pDocument->GetPageCount() <= iPage ||
       m_pDocument->IsPageLoaded(iPage)) {
-    m_docStatus = PDF_DATAAVAIL_DONE;
+    m_internalStatus = InternalStatus::kDone;
     return true;
   }
-  if (m_PageNode.m_type == PDF_PAGENODE_PAGE) {
-    m_docStatus = iPage == 0 ? PDF_DATAAVAIL_DONE : PDF_DATAAVAIL_ERROR;
+  if (m_PageNode.m_type == PageNode::Type::kPage) {
+    m_internalStatus =
+        iPage == 0 ? InternalStatus::kDone : InternalStatus::kError;
     return true;
   }
   int32_t iCount = -1;
@@ -695,15 +703,15 @@
   bool bExists = false;
   RetainPtr<CPDF_Object> pPages = GetObject(m_PagesObjNum, &bExists);
   if (!bExists) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
+    m_internalStatus = InternalStatus::kError;
     return false;
   }
   if (!pPages)
     return false;
 
-  CPDF_Dictionary* pPagesDict = pPages->GetDict();
+  RetainPtr<const CPDF_Dictionary> pPagesDict = pPages->GetDict();
   if (!pPagesDict) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
+    m_internalStatus = InternalStatus::kError;
     return false;
   }
   if (!pPagesDict->KeyExist("Kids"))
@@ -717,7 +725,7 @@
     return false;
 
   if (CheckPageCount()) {
-    m_docStatus = PDF_DATAAVAIL_PAGE;
+    m_internalStatus = InternalStatus::kPage;
     return true;
   }
 
@@ -740,11 +748,11 @@
 
 CPDF_DataAvail::DocAvailStatus CPDF_DataAvail::CheckLinearizedData() {
   if (m_bLinearedDataOK)
-    return DataAvailable;
-  ASSERT(m_pLinearized);
+    return kDataAvailable;
+  DCHECK(m_pLinearized);
   if (!m_pLinearized->GetMainXRefTableFirstEntryOffset() || !m_pDocument ||
       !m_pDocument->GetParser() || !m_pDocument->GetParser()->GetTrailer()) {
-    return DataError;
+    return kDataError;
   }
 
   if (!m_bMainXRefLoadTried) {
@@ -752,68 +760,66 @@
         m_pDocument->GetParser()->GetTrailer()->GetIntegerFor("Prev");
     const FX_FILESIZE main_xref_offset = prev.ValueOrDefault(-1);
     if (main_xref_offset < 0)
-      return DataError;
+      return kDataError;
 
     if (main_xref_offset == 0)
-      return DataAvailable;
+      return kDataAvailable;
 
     FX_SAFE_SIZE_T data_size = m_dwFileLen;
     data_size -= main_xref_offset;
     if (!data_size.IsValid())
-      return DataError;
+      return kDataError;
 
     if (!GetValidator()->CheckDataRangeAndRequestIfUnavailable(
             main_xref_offset, data_size.ValueOrDie()))
-      return DataNotAvailable;
+      return kDataNotAvailable;
 
     CPDF_Parser::Error eRet =
         m_pDocument->GetParser()->LoadLinearizedMainXRefTable();
     m_bMainXRefLoadTried = true;
     if (eRet != CPDF_Parser::SUCCESS)
-      return DataError;
+      return kDataError;
 
     if (!PreparePageItem())
-      return DataNotAvailable;
+      return kDataNotAvailable;
 
     m_bMainXRefLoadedOK = true;
     m_bLinearedDataOK = true;
   }
 
-  return m_bLinearedDataOK ? DataAvailable : DataNotAvailable;
+  return m_bLinearedDataOK ? kDataAvailable : kDataNotAvailable;
 }
 
 CPDF_DataAvail::DocAvailStatus CPDF_DataAvail::IsPageAvail(
     uint32_t dwPage,
     DownloadHints* pHints) {
   if (!m_pDocument)
-    return DataError;
+    return kDataError;
 
-  const FX_SAFE_INT32 safePage = pdfium::base::checked_cast<int32_t>(dwPage);
-  if (!safePage.IsValid())
-    return DataError;
-
-  if (safePage.ValueOrDie() >= m_pDocument->GetPageCount()) {
+  const int iPage = pdfium::base::checked_cast<int>(dwPage);
+  if (iPage >= m_pDocument->GetPageCount()) {
     // This is XFA page.
-    return DataAvailable;
+    return kDataAvailable;
   }
 
   if (IsFirstCheck(dwPage)) {
     m_bCurPageDictLoadOK = false;
   }
 
-  if (pdfium::ContainsKey(m_pagesLoadState, dwPage))
-    return DataAvailable;
+  if (pdfium::Contains(m_pagesLoadState, dwPage))
+    return kDataAvailable;
 
   const HintsScope hints_scope(GetValidator(), pHints);
   if (m_pLinearized) {
     if (dwPage == m_pLinearized->GetFirstPageNo()) {
-      auto* pPageDict = m_pDocument->GetPageDictionary(safePage.ValueOrDie());
+      RetainPtr<const CPDF_Dictionary> pPageDict =
+          m_pDocument->GetPageDictionary(iPage);
       if (!pPageDict)
-        return DataError;
+        return kDataError;
 
-      auto page_num_obj = std::make_pair(
-          dwPage, pdfium::MakeUnique<CPDF_PageObjectAvail>(
-                      GetValidator(), m_pDocument.Get(), pPageDict));
+      auto page_num_obj =
+          std::make_pair(dwPage, std::make_unique<CPDF_PageObjectAvail>(
+                                     GetValidator(), m_pDocument, pPageDict));
 
       CPDF_PageObjectAvail* page_obj_avail =
           m_PagesObjAvail.insert(std::move(page_num_obj)).first->second.get();
@@ -822,83 +828,84 @@
     }
 
     DocAvailStatus nResult = CheckLinearizedData();
-    if (nResult != DataAvailable)
+    if (nResult != kDataAvailable)
       return nResult;
 
     if (m_pHintTables) {
       nResult = m_pHintTables->CheckPage(dwPage);
-      if (nResult != DataAvailable)
+      if (nResult != kDataAvailable)
         return nResult;
       if (GetPageDictionary(dwPage)) {
         m_pagesLoadState.insert(dwPage);
-        return DataAvailable;
+        return kDataAvailable;
       }
     }
 
     if (!m_bMainXRefLoadedOK) {
       if (!LoadAllFile())
-        return DataNotAvailable;
+        return kDataNotAvailable;
       m_pDocument->GetParser()->RebuildCrossRef();
       ResetFirstCheck(dwPage);
-      return DataAvailable;
+      return kDataAvailable;
     }
     if (m_bTotalLoadPageTree) {
       if (!LoadPages())
-        return DataNotAvailable;
+        return kDataNotAvailable;
     } else {
       if (!m_bCurPageDictLoadOK && !CheckPage(dwPage))
-        return DataNotAvailable;
+        return kDataNotAvailable;
     }
   } else {
     if (!m_bTotalLoadPageTree && !m_bCurPageDictLoadOK && !CheckPage(dwPage)) {
-      return DataNotAvailable;
+      return kDataNotAvailable;
     }
   }
 
-  if (CheckAcroForm() == DocFormStatus::FormNotAvailable)
-    return DataNotAvailable;
+  if (CheckAcroForm() == kFormNotAvailable)
+    return kDataNotAvailable;
 
-  auto* pPageDict = m_pDocument->GetPageDictionary(safePage.ValueOrDie());
+  RetainPtr<CPDF_Dictionary> pPageDict =
+      m_pDocument->GetMutablePageDictionary(iPage);
   if (!pPageDict)
-    return DataError;
+    return kDataError;
 
   {
-    auto page_num_obj = std::make_pair(
-        dwPage, pdfium::MakeUnique<CPDF_PageObjectAvail>(
-                    GetValidator(), m_pDocument.Get(), pPageDict));
+    auto page_num_obj =
+        std::make_pair(dwPage, std::make_unique<CPDF_PageObjectAvail>(
+                                   GetValidator(), m_pDocument, pPageDict));
     CPDF_PageObjectAvail* page_obj_avail =
         m_PagesObjAvail.insert(std::move(page_num_obj)).first->second.get();
     const DocAvailStatus status = page_obj_avail->CheckAvail();
-    if (status != DocAvailStatus::DataAvailable)
+    if (status != kDataAvailable)
       return status;
   }
 
-  const DocAvailStatus resources_status = CheckResources(pPageDict);
-  if (resources_status != DocAvailStatus::DataAvailable)
+  const DocAvailStatus resources_status = CheckResources(std::move(pPageDict));
+  if (resources_status != kDataAvailable)
     return resources_status;
 
   m_bCurPageDictLoadOK = false;
   ResetFirstCheck(dwPage);
   m_pagesLoadState.insert(dwPage);
-  return DataAvailable;
+  return kDataAvailable;
 }
 
 CPDF_DataAvail::DocAvailStatus CPDF_DataAvail::CheckResources(
-    CPDF_Dictionary* page) {
-  ASSERT(page);
-  const CPDF_ReadValidator::Session read_session(GetValidator());
-  CPDF_Object* resources = GetResourceObject(page);
+    RetainPtr<CPDF_Dictionary> page) {
+  DCHECK(page);
+  CPDF_ReadValidator::ScopedSession read_session(GetValidator());
+  RetainPtr<CPDF_Object> resources = GetResourceObject(std::move(page));
   if (GetValidator()->has_read_problems())
-    return DocAvailStatus::DataNotAvailable;
+    return kDataNotAvailable;
 
   if (!resources)
-    return DocAvailStatus::DataAvailable;
+    return kDataAvailable;
 
   CPDF_PageObjectAvail* resource_avail =
       m_PagesResourcesAvail
-          .insert(std::make_pair(
-              resources, pdfium::MakeUnique<CPDF_PageObjectAvail>(
-                             GetValidator(), m_pDocument.Get(), resources)))
+          .insert(std::make_pair(resources,
+                                 std::make_unique<CPDF_PageObjectAvail>(
+                                     GetValidator(), m_pDocument, resources)))
           .first->second.get();
   return resource_avail->CheckAvail();
 }
@@ -918,10 +925,11 @@
   return m_pDocument ? m_pDocument->GetPageCount() : 0;
 }
 
-CPDF_Dictionary* CPDF_DataAvail::GetPageDictionary(int index) const {
+RetainPtr<const CPDF_Dictionary> CPDF_DataAvail::GetPageDictionary(
+    int index) const {
   if (!m_pDocument || index < 0 || index >= GetPageCount())
     return nullptr;
-  CPDF_Dictionary* page = m_pDocument->GetPageDictionary(index);
+  RetainPtr<const CPDF_Dictionary> page = m_pDocument->GetPageDictionary(index);
   if (page)
     return page;
   if (!m_pLinearized || !m_pHintTables)
@@ -941,8 +949,7 @@
   // Page object already can be parsed in document.
   if (!m_pDocument->GetIndirectObject(dwObjNum)) {
     m_pDocument->ReplaceIndirectObjectIfHigherGeneration(
-        dwObjNum,
-        ParseIndirectObjectAt(szPageStartPos, dwObjNum, m_pDocument.Get()));
+        dwObjNum, ParseIndirectObjectAt(szPageStartPos, dwObjNum, m_pDocument));
   }
   if (!ValidatePage(index))
     return nullptr;
@@ -957,64 +964,64 @@
 
 CPDF_DataAvail::DocFormStatus CPDF_DataAvail::CheckAcroForm() {
   if (!m_pDocument)
-    return FormAvailable;
+    return kFormAvailable;
 
   if (m_pLinearized) {
     DocAvailStatus nDocStatus = CheckLinearizedData();
-    if (nDocStatus == DataError)
-      return FormError;
-    if (nDocStatus == DataNotAvailable)
-      return FormNotAvailable;
+    if (nDocStatus == kDataError)
+      return kFormError;
+    if (nDocStatus == kDataNotAvailable)
+      return kFormNotAvailable;
   }
 
   if (!m_pFormAvail) {
-    CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
+    const CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
     if (!pRoot)
-      return FormAvailable;
+      return kFormAvailable;
 
-    CPDF_Object* pAcroForm = pRoot->GetObjectFor("AcroForm");
+    RetainPtr<const CPDF_Object> pAcroForm = pRoot->GetObjectFor("AcroForm");
     if (!pAcroForm)
-      return FormNotExist;
+      return kFormNotExist;
 
-    m_pFormAvail = pdfium::MakeUnique<CPDF_PageObjectAvail>(
-        GetValidator(), m_pDocument.Get(), pAcroForm);
+    m_pFormAvail = std::make_unique<CPDF_PageObjectAvail>(
+        GetValidator(), m_pDocument, std::move(pAcroForm));
   }
   switch (m_pFormAvail->CheckAvail()) {
-    case DocAvailStatus::DataError:
-      return DocFormStatus::FormError;
-    case DocAvailStatus::DataNotAvailable:
-      return DocFormStatus::FormNotAvailable;
-    case DocAvailStatus::DataAvailable:
-      return DocFormStatus::FormAvailable;
-    default:
-      NOTREACHED();
+    case kDataError:
+      return kFormError;
+    case kDataNotAvailable:
+      return kFormNotAvailable;
+    case kDataAvailable:
+      return kFormAvailable;
   }
-  return DocFormStatus::FormError;
 }
 
 bool CPDF_DataAvail::ValidatePage(uint32_t dwPage) const {
-  FX_SAFE_INT32 safePage = pdfium::base::checked_cast<int32_t>(dwPage);
-  auto* pPageDict = m_pDocument->GetPageDictionary(safePage.ValueOrDie());
+  int iPage = pdfium::base::checked_cast<int>(dwPage);
+  RetainPtr<const CPDF_Dictionary> pPageDict =
+      m_pDocument->GetPageDictionary(iPage);
   if (!pPageDict)
     return false;
-  CPDF_PageObjectAvail obj_avail(GetValidator(), m_pDocument.Get(), pPageDict);
-  return obj_avail.CheckAvail() == DocAvailStatus::DataAvailable;
+
+  CPDF_PageObjectAvail obj_avail(GetValidator(), m_pDocument,
+                                 std::move(pPageDict));
+  return obj_avail.CheckAvail() == kDataAvailable;
 }
 
 std::pair<CPDF_Parser::Error, std::unique_ptr<CPDF_Document>>
 CPDF_DataAvail::ParseDocument(
     std::unique_ptr<CPDF_Document::RenderDataIface> pRenderData,
     std::unique_ptr<CPDF_Document::PageDataIface> pPageData,
-    const char* password) {
+    const ByteString& password) {
   if (m_pDocument) {
     // We already returned parsed document.
     return std::make_pair(CPDF_Parser::HANDLER_ERROR, nullptr);
   }
-  auto document = pdfium::MakeUnique<CPDF_Document>(std::move(pRenderData),
-                                                    std::move(pPageData));
+  auto document = std::make_unique<CPDF_Document>(std::move(pRenderData),
+                                                  std::move(pPageData));
   document->AddObserver(this);
 
-  CPDF_ReadValidator::Session read_session(GetValidator());
+  CPDF_ReadValidator::ScopedSession read_session(GetValidator());
   CPDF_Parser::Error error =
       document->LoadLinearizedDoc(GetValidator(), password);
 
@@ -1031,6 +1038,6 @@
   return std::make_pair(CPDF_Parser::SUCCESS, std::move(document));
 }
 
-CPDF_DataAvail::PageNode::PageNode() : m_type(PDF_PAGENODE_UNKNOWN) {}
+CPDF_DataAvail::PageNode::PageNode() = default;
 
-CPDF_DataAvail::PageNode::~PageNode() {}
+CPDF_DataAvail::PageNode::~PageNode() = default;
diff --git a/core/fpdfapi/parser/cpdf_data_avail.h b/core/fpdfapi/parser/cpdf_data_avail.h
index 6bcea07..a3f215e 100644
--- a/core/fpdfapi/parser/cpdf_data_avail.h
+++ b/core/fpdfapi/parser/cpdf_data_avail.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_DATA_AVAIL_H_
 #define CORE_FPDFAPI_PARSER_CPDF_DATA_AVAIL_H_
 
+#include <functional>
 #include <map>
 #include <memory>
 #include <set>
@@ -15,6 +16,7 @@
 
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_parser.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_CrossRefAvail;
@@ -26,57 +28,34 @@
 class CPDF_ReadValidator;
 class CPDF_SyntaxParser;
 
-enum PDF_DATAAVAIL_STATUS {
-  PDF_DATAAVAIL_HEADER = 0,
-  PDF_DATAAVAIL_FIRSTPAGE,
-  PDF_DATAAVAIL_HINTTABLE,
-  PDF_DATAAVAIL_LOADALLCROSSREF,
-  PDF_DATAAVAIL_ROOT,
-  PDF_DATAAVAIL_INFO,
-  PDF_DATAAVAIL_PAGETREE,
-  PDF_DATAAVAIL_PAGE,
-  PDF_DATAAVAIL_PAGE_LATERLOAD,
-  PDF_DATAAVAIL_RESOURCES,
-  PDF_DATAAVAIL_DONE,
-  PDF_DATAAVAIL_ERROR,
-  PDF_DATAAVAIL_LOADALLFILE,
-};
-
-enum PDF_PAGENODE_TYPE {
-  PDF_PAGENODE_UNKNOWN = 0,
-  PDF_PAGENODE_PAGE,
-  PDF_PAGENODE_PAGES,
-  PDF_PAGENODE_ARRAY,
-};
-
 class CPDF_DataAvail final : public Observable::ObserverIface {
  public:
   // Must match PDF_DATA_* definitions in public/fpdf_dataavail.h, but cannot
   // #include that header. fpdfsdk/fpdf_dataavail.cpp has static_asserts
   // to make sure the two sets of values match.
   enum DocAvailStatus {
-    DataError = -1,        // PDF_DATA_ERROR
-    DataNotAvailable = 0,  // PDF_DATA_NOTAVAIL
-    DataAvailable = 1,     // PDF_DATA_AVAIL
+    kDataError = -1,        // PDF_DATA_ERROR
+    kDataNotAvailable = 0,  // PDF_DATA_NOTAVAIL
+    kDataAvailable = 1,     // PDF_DATA_AVAIL
   };
 
   // Must match PDF_*LINEAR* definitions in public/fpdf_dataavail.h, but cannot
   // #include that header. fpdfsdk/fpdf_dataavail.cpp has static_asserts
   // to make sure the two sets of values match.
   enum DocLinearizationStatus {
-    LinearizationUnknown = -1,  // PDF_LINEARIZATION_UNKNOWN
-    NotLinearized = 0,          // PDF_NOT_LINEARIZED
-    Linearized = 1,             // PDF_LINEARIZED
+    kLinearizationUnknown = -1,  // PDF_LINEARIZATION_UNKNOWN
+    kNotLinearized = 0,          // PDF_NOT_LINEARIZED
+    kLinearized = 1,             // PDF_LINEARIZED
   };
 
   // Must match PDF_FORM_* definitions in public/fpdf_dataavail.h, but cannot
   // #include that header. fpdfsdk/fpdf_dataavail.cpp has static_asserts
   // to make sure the two sets of values match.
   enum DocFormStatus {
-    FormError = -1,        // PDF_FORM_ERROR
-    FormNotAvailable = 0,  // PDF_FORM_NOTAVAIL
-    FormAvailable = 1,     // PDF_FORM_AVAIL
-    FormNotExist = 2,      // PDF_FORM_NOTEXIST
+    kFormError = -1,        // PDF_FORM_ERROR
+    kFormNotAvailable = 0,  // PDF_FORM_NOTAVAIL
+    kFormAvailable = 1,     // PDF_FORM_AVAIL
+    kFormNotExist = 2,      // PDF_FORM_NOTEXIST
   };
 
   class FileAvail {
@@ -92,11 +71,10 @@
   };
 
   CPDF_DataAvail(FileAvail* pFileAvail,
-                 const RetainPtr<IFX_SeekableReadStream>& pFileRead,
-                 bool bSupportHintTable);
+                 RetainPtr<IFX_SeekableReadStream> pFileRead);
   ~CPDF_DataAvail() override;
 
-  // CPDF_Document::Observer:
+  // Observable::ObserverIface:
   void OnObservableDestroyed() override;
 
   DocAvailStatus IsDocAvail(DownloadHints* pHints);
@@ -104,28 +82,48 @@
   DocFormStatus IsFormAvail(DownloadHints* pHints);
   DocLinearizationStatus IsLinearizedPDF();
   int GetPageCount() const;
-  CPDF_Dictionary* GetPageDictionary(int index) const;
+  RetainPtr<const CPDF_Dictionary> GetPageDictionary(int index) const;
   RetainPtr<CPDF_ReadValidator> GetValidator() const;
 
   std::pair<CPDF_Parser::Error, std::unique_ptr<CPDF_Document>> ParseDocument(
       std::unique_ptr<CPDF_Document::RenderDataIface> pRenderData,
       std::unique_ptr<CPDF_Document::PageDataIface> pPageData,
-      const char* password);
+      const ByteString& password);
 
-  const CPDF_HintTables* GetHintTables() const { return m_pHintTables.get(); }
+  const CPDF_HintTables* GetHintTablesForTest() const {
+    return m_pHintTables.get();
+  }
 
  private:
+  enum class InternalStatus : uint8_t {
+    kHeader = 0,
+    kFirstPage,
+    kHintTable,
+    kLoadAllCrossRef,
+    kRoot,
+    kInfo,
+    kPageTree,
+    kPage,
+    kPageLaterLoad,
+    kResources,
+    kDone,
+    kError,
+    kLoadAllFile,
+  };
+
   class PageNode {
    public:
+    enum class Type { kUnknown = 0, kPage, kPages, kArray };
+
     PageNode();
     ~PageNode();
 
-    PDF_PAGENODE_TYPE m_type;
-    uint32_t m_dwPageNo;
+    Type m_type = Type::kUnknown;
+    uint32_t m_dwPageNo = 0;
     std::vector<std::unique_ptr<PageNode>> m_ChildNodes;
   };
 
-  static const int kMaxPageRecursionDepth = 1024;
+  static constexpr int kMaxPageRecursionDepth = 1024;
 
   bool CheckDocStatus();
   bool CheckHeader();
@@ -135,7 +133,7 @@
   bool CheckInfo();
   bool CheckPages();
   bool CheckPage();
-  DocAvailStatus CheckResources(CPDF_Dictionary* page);
+  DocAvailStatus CheckResources(RetainPtr<CPDF_Dictionary> page);
   DocFormStatus CheckAcroForm();
   bool CheckPageStatus();
 
@@ -172,11 +170,12 @@
   RetainPtr<CPDF_Dictionary> m_pRoot;
   std::unique_ptr<CPDF_LinearizedHeader> m_pLinearized;
   bool m_bDocAvail = false;
+  InternalStatus m_internalStatus = InternalStatus::kHeader;
   std::unique_ptr<CPDF_CrossRefAvail> m_pCrossRefAvail;
-  PDF_DATAAVAIL_STATUS m_docStatus = PDF_DATAAVAIL_HEADER;
   const FX_FILESIZE m_dwFileLen;
   UnownedPtr<CPDF_Document> m_pDocument;
   std::vector<uint32_t> m_PageObjList;
+  std::set<uint32_t> m_SeenPageObjList;
   uint32_t m_PagesObjNum = 0;
   bool m_bLinearedDataOK = false;
   bool m_bMainXRefLoadTried = false;
@@ -187,15 +186,16 @@
   std::vector<RetainPtr<CPDF_Object>> m_PagesArray;
   bool m_bTotalLoadPageTree = false;
   bool m_bCurPageDictLoadOK = false;
+  bool m_bHeaderAvail = false;
   PageNode m_PageNode;
   std::set<uint32_t> m_pageMapCheckState;
   std::set<uint32_t> m_pagesLoadState;
   std::unique_ptr<CPDF_HintTables> m_pHintTables;
-  const bool m_bSupportHintTable;
   std::map<uint32_t, std::unique_ptr<CPDF_PageObjectAvail>> m_PagesObjAvail;
-  std::map<const CPDF_Object*, std::unique_ptr<CPDF_PageObjectAvail>>
+  std::map<RetainPtr<const CPDF_Object>,
+           std::unique_ptr<CPDF_PageObjectAvail>,
+           std::less<>>
       m_PagesResourcesAvail;
-  bool m_bHeaderAvail = false;
 };
 
 #endif  // CORE_FPDFAPI_PARSER_CPDF_DATA_AVAIL_H_
diff --git a/core/fpdfapi/parser/cpdf_dictionary.cpp b/core/fpdfapi/parser/cpdf_dictionary.cpp
index 6c4c6e6..bf6f593 100644
--- a/core/fpdfapi/parser/cpdf_dictionary.cpp
+++ b/core/fpdfapi/parser/cpdf_dictionary.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -19,9 +19,8 @@
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcrt/fx_stream.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
 
 CPDF_Dictionary::CPDF_Dictionary()
     : CPDF_Dictionary(WeakPtr<ByteStringPool>()) {}
@@ -34,7 +33,7 @@
   // and break cyclic references.
   m_ObjNum = kInvalidObjNum;
   for (auto& it : m_Map) {
-    if (it.second && it.second->GetObjNum() == kInvalidObjNum)
+    if (it.second->GetObjNum() == kInvalidObjNum)
       it.second.Leak();
   }
 }
@@ -43,23 +42,7 @@
   return kDictionary;
 }
 
-CPDF_Dictionary* CPDF_Dictionary::GetDict() {
-  return this;
-}
-
-const CPDF_Dictionary* CPDF_Dictionary::GetDict() const {
-  return this;
-}
-
-bool CPDF_Dictionary::IsDictionary() const {
-  return true;
-}
-
-CPDF_Dictionary* CPDF_Dictionary::AsDictionary() {
-  return this;
-}
-
-const CPDF_Dictionary* CPDF_Dictionary::AsDictionary() const {
+CPDF_Dictionary* CPDF_Dictionary::AsMutableDictionary() {
   return this;
 }
 
@@ -74,126 +57,203 @@
   auto pCopy = pdfium::MakeRetain<CPDF_Dictionary>(m_pPool);
   CPDF_DictionaryLocker locker(this);
   for (const auto& it : locker) {
-    if (!pdfium::ContainsKey(*pVisited, it.second.Get())) {
+    if (!pdfium::Contains(*pVisited, it.second.Get())) {
       std::set<const CPDF_Object*> visited(*pVisited);
-      if (auto obj = it.second->CloneNonCyclic(bDirect, &visited))
+      auto obj = it.second->CloneNonCyclic(bDirect, &visited);
+      if (obj)
         pCopy->m_Map.insert(std::make_pair(it.first, std::move(obj)));
     }
   }
   return pCopy;
 }
 
-const CPDF_Object* CPDF_Dictionary::GetObjectFor(const ByteString& key) const {
+const CPDF_Object* CPDF_Dictionary::GetObjectForInternal(
+    const ByteString& key) const {
   auto it = m_Map.find(key);
   return it != m_Map.end() ? it->second.Get() : nullptr;
 }
 
-CPDF_Object* CPDF_Dictionary::GetObjectFor(const ByteString& key) {
-  return const_cast<CPDF_Object*>(
-      static_cast<const CPDF_Dictionary*>(this)->GetObjectFor(key));
-}
-
-const CPDF_Object* CPDF_Dictionary::GetDirectObjectFor(
+RetainPtr<const CPDF_Object> CPDF_Dictionary::GetObjectFor(
     const ByteString& key) const {
-  const CPDF_Object* p = GetObjectFor(key);
-  return p ? p->GetDirect() : nullptr;
+  return pdfium::WrapRetain(GetObjectForInternal(key));
 }
 
-CPDF_Object* CPDF_Dictionary::GetDirectObjectFor(const ByteString& key) {
-  return const_cast<CPDF_Object*>(
-      static_cast<const CPDF_Dictionary*>(this)->GetDirectObjectFor(key));
+RetainPtr<CPDF_Object> CPDF_Dictionary::GetMutableObjectFor(
+    const ByteString& key) {
+  return pdfium::WrapRetain(
+      const_cast<CPDF_Object*>(GetObjectForInternal(key)));
 }
 
-ByteString CPDF_Dictionary::GetStringFor(const ByteString& key) const {
-  const CPDF_Object* p = GetObjectFor(key);
+const CPDF_Object* CPDF_Dictionary::GetDirectObjectForInternal(
+    const ByteString& key) const {
+  const CPDF_Object* p = GetObjectForInternal(key);
+  return p ? p->GetDirectInternal() : nullptr;
+}
+
+RetainPtr<const CPDF_Object> CPDF_Dictionary::GetDirectObjectFor(
+    const ByteString& key) const {
+  return pdfium::WrapRetain(GetDirectObjectForInternal(key));
+}
+
+RetainPtr<CPDF_Object> CPDF_Dictionary::GetMutableDirectObjectFor(
+    const ByteString& key) {
+  return pdfium::WrapRetain(
+      const_cast<CPDF_Object*>(GetDirectObjectForInternal(key)));
+}
+
+ByteString CPDF_Dictionary::GetByteStringFor(const ByteString& key) const {
+  const CPDF_Object* p = GetObjectForInternal(key);
   return p ? p->GetString() : ByteString();
 }
 
-WideString CPDF_Dictionary::GetUnicodeTextFor(const ByteString& key) const {
-  const CPDF_Object* p = GetObjectFor(key);
-  if (const CPDF_Reference* pRef = ToReference(p))
-    p = pRef->GetDirect();
-  return p ? p->GetUnicodeText() : WideString();
-}
-
-ByteString CPDF_Dictionary::GetStringFor(const ByteString& key,
-                                         const ByteString& def) const {
-  const CPDF_Object* p = GetObjectFor(key);
+ByteString CPDF_Dictionary::GetByteStringFor(const ByteString& key,
+                                             const ByteString& def) const {
+  const CPDF_Object* p = GetObjectForInternal(key);
   return p ? p->GetString() : ByteString(def);
 }
 
-int CPDF_Dictionary::GetIntegerFor(const ByteString& key) const {
-  const CPDF_Object* p = GetObjectFor(key);
-  return p ? p->GetInteger() : 0;
+WideString CPDF_Dictionary::GetUnicodeTextFor(const ByteString& key) const {
+  const CPDF_Object* p = GetObjectForInternal(key);
+  if (const CPDF_Reference* pRef = ToReference(p))
+    p = pRef->GetDirectInternal();
+  return p ? p->GetUnicodeText() : WideString();
 }
 
-int CPDF_Dictionary::GetIntegerFor(const ByteString& key, int def) const {
-  const CPDF_Object* p = GetObjectFor(key);
-  return p ? p->GetInteger() : def;
-}
-
-float CPDF_Dictionary::GetNumberFor(const ByteString& key) const {
-  const CPDF_Object* p = GetObjectFor(key);
-  return p ? p->GetNumber() : 0;
+ByteString CPDF_Dictionary::GetNameFor(const ByteString& key) const {
+  const CPDF_Name* p = ToName(GetObjectForInternal(key));
+  return p ? p->GetString() : ByteString();
 }
 
 bool CPDF_Dictionary::GetBooleanFor(const ByteString& key,
                                     bool bDefault) const {
-  const CPDF_Object* p = GetObjectFor(key);
+  const CPDF_Object* p = GetObjectForInternal(key);
   return ToBoolean(p) ? p->GetInteger() != 0 : bDefault;
 }
 
-const CPDF_Dictionary* CPDF_Dictionary::GetDictFor(
+int CPDF_Dictionary::GetIntegerFor(const ByteString& key) const {
+  const CPDF_Object* p = GetObjectForInternal(key);
+  return p ? p->GetInteger() : 0;
+}
+
+int CPDF_Dictionary::GetIntegerFor(const ByteString& key, int def) const {
+  const CPDF_Object* p = GetObjectForInternal(key);
+  return p ? p->GetInteger() : def;
+}
+
+int CPDF_Dictionary::GetDirectIntegerFor(const ByteString& key) const {
+  const CPDF_Number* p = ToNumber(GetObjectForInternal(key));
+  return p ? p->GetInteger() : 0;
+}
+
+float CPDF_Dictionary::GetFloatFor(const ByteString& key) const {
+  const CPDF_Object* p = GetObjectForInternal(key);
+  return p ? p->GetNumber() : 0;
+}
+
+const CPDF_Dictionary* CPDF_Dictionary::GetDictInternal() const {
+  return this;
+}
+
+const CPDF_Dictionary* CPDF_Dictionary::GetDictForInternal(
     const ByteString& key) const {
-  const CPDF_Object* p = GetDirectObjectFor(key);
-  if (!p)
-    return nullptr;
-  if (const CPDF_Dictionary* pDict = p->AsDictionary())
-    return pDict;
-  if (const CPDF_Stream* pStream = p->AsStream())
-    return pStream->GetDict();
-  return nullptr;
+  const CPDF_Object* p = GetDirectObjectForInternal(key);
+  return p ? p->GetDictInternal() : nullptr;
 }
 
-CPDF_Dictionary* CPDF_Dictionary::GetDictFor(const ByteString& key) {
-  return const_cast<CPDF_Dictionary*>(
-      static_cast<const CPDF_Dictionary*>(this)->GetDictFor(key));
+RetainPtr<const CPDF_Dictionary> CPDF_Dictionary::GetDictFor(
+    const ByteString& key) const {
+  return pdfium::WrapRetain(GetDictForInternal(key));
 }
 
-const CPDF_Array* CPDF_Dictionary::GetArrayFor(const ByteString& key) const {
-  return ToArray(GetDirectObjectFor(key));
+RetainPtr<CPDF_Dictionary> CPDF_Dictionary::GetMutableDictFor(
+    const ByteString& key) {
+  return pdfium::WrapRetain(
+      const_cast<CPDF_Dictionary*>(GetDictForInternal(key)));
 }
 
-CPDF_Array* CPDF_Dictionary::GetArrayFor(const ByteString& key) {
-  return ToArray(GetDirectObjectFor(key));
+RetainPtr<CPDF_Dictionary> CPDF_Dictionary::GetOrCreateDictFor(
+    const ByteString& key) {
+  RetainPtr<CPDF_Dictionary> result = GetMutableDictFor(key);
+  if (result)
+    return result;
+  return SetNewFor<CPDF_Dictionary>(key);
 }
 
-const CPDF_Stream* CPDF_Dictionary::GetStreamFor(const ByteString& key) const {
-  return ToStream(GetDirectObjectFor(key));
+const CPDF_Array* CPDF_Dictionary::GetArrayForInternal(
+    const ByteString& key) const {
+  return ToArray(GetDirectObjectForInternal(key));
 }
 
-CPDF_Stream* CPDF_Dictionary::GetStreamFor(const ByteString& key) {
-  return ToStream(GetDirectObjectFor(key));
+RetainPtr<const CPDF_Array> CPDF_Dictionary::GetArrayFor(
+    const ByteString& key) const {
+  return pdfium::WrapRetain(GetArrayForInternal(key));
+}
+
+RetainPtr<CPDF_Array> CPDF_Dictionary::GetMutableArrayFor(
+    const ByteString& key) {
+  return pdfium::WrapRetain(const_cast<CPDF_Array*>(GetArrayForInternal(key)));
+}
+
+RetainPtr<CPDF_Array> CPDF_Dictionary::GetOrCreateArrayFor(
+    const ByteString& key) {
+  RetainPtr<CPDF_Array> result = GetMutableArrayFor(key);
+  if (result)
+    return result;
+  return SetNewFor<CPDF_Array>(key);
+}
+
+const CPDF_Stream* CPDF_Dictionary::GetStreamForInternal(
+    const ByteString& key) const {
+  return ToStream(GetDirectObjectForInternal(key));
+}
+
+RetainPtr<const CPDF_Stream> CPDF_Dictionary::GetStreamFor(
+    const ByteString& key) const {
+  return pdfium::WrapRetain(GetStreamForInternal(key));
+}
+
+RetainPtr<CPDF_Stream> CPDF_Dictionary::GetMutableStreamFor(
+    const ByteString& key) {
+  return pdfium::WrapRetain(
+      const_cast<CPDF_Stream*>(GetStreamForInternal(key)));
+}
+
+const CPDF_Number* CPDF_Dictionary::GetNumberForInternal(
+    const ByteString& key) const {
+  return ToNumber(GetObjectForInternal(key));
+}
+
+RetainPtr<const CPDF_Number> CPDF_Dictionary::GetNumberFor(
+    const ByteString& key) const {
+  return pdfium::WrapRetain(GetNumberForInternal(key));
+}
+
+const CPDF_String* CPDF_Dictionary::GetStringForInternal(
+    const ByteString& key) const {
+  return ToString(GetObjectForInternal(key));
+}
+
+RetainPtr<const CPDF_String> CPDF_Dictionary::GetStringFor(
+    const ByteString& key) const {
+  return pdfium::WrapRetain(GetStringForInternal(key));
 }
 
 CFX_FloatRect CPDF_Dictionary::GetRectFor(const ByteString& key) const {
-  CFX_FloatRect rect;
-  const CPDF_Array* pArray = GetArrayFor(key);
+  const CPDF_Array* pArray = GetArrayForInternal(key);
   if (pArray)
-    rect = pArray->GetRect();
-  return rect;
+    return pArray->GetRect();
+  return CFX_FloatRect();
 }
 
 CFX_Matrix CPDF_Dictionary::GetMatrixFor(const ByteString& key) const {
-  CFX_Matrix matrix;
-  const CPDF_Array* pArray = GetArrayFor(key);
+  const CPDF_Array* pArray = GetArrayForInternal(key);
   if (pArray)
-    matrix = pArray->GetMatrix();
-  return matrix;
+    return pArray->GetMatrix();
+  return CFX_Matrix();
 }
 
 bool CPDF_Dictionary::KeyExist(const ByteString& key) const {
-  return pdfium::ContainsKey(m_Map, key);
+  return pdfium::Contains(m_Map, key);
 }
 
 std::vector<ByteString> CPDF_Dictionary::GetKeys() const {
@@ -204,14 +264,19 @@
   return result;
 }
 
-CPDF_Object* CPDF_Dictionary::SetFor(const ByteString& key,
-                                     RetainPtr<CPDF_Object> pObj) {
+void CPDF_Dictionary::SetFor(const ByteString& key,
+                             RetainPtr<CPDF_Object> pObj) {
+  (void)SetForInternal(key, std::move(pObj));
+}
+
+CPDF_Object* CPDF_Dictionary::SetForInternal(const ByteString& key,
+                                             RetainPtr<CPDF_Object> pObj) {
   CHECK(!IsLocked());
   if (!pObj) {
     m_Map.erase(key);
     return nullptr;
   }
-  ASSERT(pObj->IsInline());
+  DCHECK(pObj->IsInline());
   CPDF_Object* pRet = pObj.Get();
   m_Map[MaybeIntern(key)] = std::move(pObj);
   return pRet;
@@ -225,11 +290,11 @@
   if (it == m_Map.end() || it->second->IsReference())
     return;
 
-  CPDF_Object* pObj = pHolder->AddIndirectObject(std::move(it->second));
-  it->second = pObj->MakeReference(pHolder);
+  pHolder->AddIndirectObject(it->second);
+  it->second = it->second->MakeReference(pHolder);
 }
 
-RetainPtr<CPDF_Object> CPDF_Dictionary::RemoveFor(const ByteString& key) {
+RetainPtr<CPDF_Object> CPDF_Dictionary::RemoveFor(ByteStringView key) {
   CHECK(!IsLocked());
   RetainPtr<CPDF_Object> result;
   auto it = m_Map.find(key);
@@ -257,22 +322,22 @@
 
 void CPDF_Dictionary::SetRectFor(const ByteString& key,
                                  const CFX_FloatRect& rect) {
-  CPDF_Array* pArray = SetNewFor<CPDF_Array>(key);
-  pArray->AddNew<CPDF_Number>(rect.left);
-  pArray->AddNew<CPDF_Number>(rect.bottom);
-  pArray->AddNew<CPDF_Number>(rect.right);
-  pArray->AddNew<CPDF_Number>(rect.top);
+  auto pArray = SetNewFor<CPDF_Array>(key);
+  pArray->AppendNew<CPDF_Number>(rect.left);
+  pArray->AppendNew<CPDF_Number>(rect.bottom);
+  pArray->AppendNew<CPDF_Number>(rect.right);
+  pArray->AppendNew<CPDF_Number>(rect.top);
 }
 
 void CPDF_Dictionary::SetMatrixFor(const ByteString& key,
                                    const CFX_Matrix& matrix) {
-  CPDF_Array* pArray = SetNewFor<CPDF_Array>(key);
-  pArray->AddNew<CPDF_Number>(matrix.a);
-  pArray->AddNew<CPDF_Number>(matrix.b);
-  pArray->AddNew<CPDF_Number>(matrix.c);
-  pArray->AddNew<CPDF_Number>(matrix.d);
-  pArray->AddNew<CPDF_Number>(matrix.e);
-  pArray->AddNew<CPDF_Number>(matrix.f);
+  auto pArray = SetNewFor<CPDF_Array>(key);
+  pArray->AppendNew<CPDF_Number>(matrix.a);
+  pArray->AppendNew<CPDF_Number>(matrix.b);
+  pArray->AppendNew<CPDF_Number>(matrix.c);
+  pArray->AppendNew<CPDF_Number>(matrix.d);
+  pArray->AppendNew<CPDF_Number>(matrix.e);
+  pArray->AppendNew<CPDF_Number>(matrix.f);
 }
 
 ByteString CPDF_Dictionary::MaybeIntern(const ByteString& str) {
@@ -289,7 +354,7 @@
   CPDF_DictionaryLocker locker(this);
   for (const auto& it : locker) {
     const ByteString& key = it.first;
-    CPDF_Object* pValue = it.second.Get();
+    const RetainPtr<CPDF_Object>& pValue = it.second;
     if (!archive->WriteString("/") ||
         !archive->WriteString(PDF_NameEncode(key).AsStringView())) {
       return false;
@@ -308,6 +373,18 @@
   m_pDictionary->m_LockCount++;
 }
 
+CPDF_DictionaryLocker::CPDF_DictionaryLocker(
+    RetainPtr<CPDF_Dictionary> pDictionary)
+    : m_pDictionary(std::move(pDictionary)) {
+  m_pDictionary->m_LockCount++;
+}
+
+CPDF_DictionaryLocker::CPDF_DictionaryLocker(
+    RetainPtr<const CPDF_Dictionary> pDictionary)
+    : m_pDictionary(std::move(pDictionary)) {
+  m_pDictionary->m_LockCount++;
+}
+
 CPDF_DictionaryLocker::~CPDF_DictionaryLocker() {
   m_pDictionary->m_LockCount--;
 }
diff --git a/core/fpdfapi/parser/cpdf_dictionary.h b/core/fpdfapi/parser/cpdf_dictionary.h
index ac1b226..34be276 100644
--- a/core/fpdfapi/parser/cpdf_dictionary.h
+++ b/core/fpdfapi/parser/cpdf_dictionary.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,8 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_DICTIONARY_H_
 #define CORE_FPDFAPI_PARSER_CPDF_DICTIONARY_H_
 
+#include <functional>
 #include <map>
-#include <memory>
 #include <set>
 #include <utility>
 #include <vector>
@@ -19,92 +19,100 @@
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/string_pool_template.h"
 #include "core/fxcrt/weak_ptr.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 class CPDF_IndirectObjectHolder;
 
+// Dictionaries never contain nullptr for valid keys, but some of the methods
+// will return nullptr to indicate non-existent keys.
 class CPDF_Dictionary final : public CPDF_Object {
  public:
-  using const_iterator =
-      std::map<ByteString, RetainPtr<CPDF_Object>>::const_iterator;
+  using DictMap = std::map<ByteString, RetainPtr<CPDF_Object>, std::less<>>;
+  using const_iterator = DictMap::const_iterator;
 
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // CPDF_Object:
   Type GetType() const override;
   RetainPtr<CPDF_Object> Clone() const override;
-  CPDF_Dictionary* GetDict() override;
-  const CPDF_Dictionary* GetDict() const override;
-  bool IsDictionary() const override;
-  CPDF_Dictionary* AsDictionary() override;
-  const CPDF_Dictionary* AsDictionary() const override;
+  CPDF_Dictionary* AsMutableDictionary() override;
   bool WriteTo(IFX_ArchiveStream* archive,
                const CPDF_Encryptor* encryptor) const override;
 
   bool IsLocked() const { return !!m_LockCount; }
 
   size_t size() const { return m_Map.size(); }
-  const CPDF_Object* GetObjectFor(const ByteString& key) const;
-  CPDF_Object* GetObjectFor(const ByteString& key);
-  const CPDF_Object* GetDirectObjectFor(const ByteString& key) const;
-  CPDF_Object* GetDirectObjectFor(const ByteString& key);
-  ByteString GetStringFor(const ByteString& key) const;
-  ByteString GetStringFor(const ByteString& key,
-                          const ByteString& default_str) const;
+  RetainPtr<const CPDF_Object> GetObjectFor(const ByteString& key) const;
+  RetainPtr<CPDF_Object> GetMutableObjectFor(const ByteString& key);
+
+  RetainPtr<const CPDF_Object> GetDirectObjectFor(const ByteString& key) const;
+  RetainPtr<CPDF_Object> GetMutableDirectObjectFor(const ByteString& key);
+
+  // These will return the string representation of the object specified by
+  // |key|, for any object type that has a string representation.
+  ByteString GetByteStringFor(const ByteString& key) const;
+  ByteString GetByteStringFor(const ByteString& key,
+                              const ByteString& default_str) const;
   WideString GetUnicodeTextFor(const ByteString& key) const;
+
+  // This will only return the string representation of a name object specified
+  // by |key|. Useful when the PDF spec requires the value to be an object of
+  // type name. i.e. /Foo and not (Foo).
+  ByteString GetNameFor(const ByteString& key) const;
+
+  bool GetBooleanFor(const ByteString& key, bool bDefault) const;
   int GetIntegerFor(const ByteString& key) const;
   int GetIntegerFor(const ByteString& key, int default_int) const;
-  bool GetBooleanFor(const ByteString& key, bool bDefault) const;
-  float GetNumberFor(const ByteString& key) const;
-  const CPDF_Dictionary* GetDictFor(const ByteString& key) const;
-  CPDF_Dictionary* GetDictFor(const ByteString& key);
-  const CPDF_Stream* GetStreamFor(const ByteString& key) const;
-  CPDF_Stream* GetStreamFor(const ByteString& key);
-  const CPDF_Array* GetArrayFor(const ByteString& key) const;
-  CPDF_Array* GetArrayFor(const ByteString& key);
+  int GetDirectIntegerFor(const ByteString& key) const;
+  float GetFloatFor(const ByteString& key) const;
+  RetainPtr<const CPDF_Dictionary> GetDictFor(const ByteString& key) const;
+  RetainPtr<CPDF_Dictionary> GetMutableDictFor(const ByteString& key);
+  RetainPtr<CPDF_Dictionary> GetOrCreateDictFor(const ByteString& key);
+  RetainPtr<const CPDF_Array> GetArrayFor(const ByteString& key) const;
+  RetainPtr<CPDF_Array> GetMutableArrayFor(const ByteString& key);
+  RetainPtr<CPDF_Array> GetOrCreateArrayFor(const ByteString& key);
+  RetainPtr<const CPDF_Stream> GetStreamFor(const ByteString& key) const;
+  RetainPtr<CPDF_Stream> GetMutableStreamFor(const ByteString& key);
+  RetainPtr<const CPDF_Number> GetNumberFor(const ByteString& key) const;
+  RetainPtr<const CPDF_String> GetStringFor(const ByteString& key) const;
   CFX_FloatRect GetRectFor(const ByteString& key) const;
   CFX_Matrix GetMatrixFor(const ByteString& key) const;
-  float GetFloatFor(const ByteString& key) const { return GetNumberFor(key); }
 
   bool KeyExist(const ByteString& key) const;
   std::vector<ByteString> GetKeys() const;
 
   // Creates a new object owned by the dictionary and returns an unowned
-  // pointer to it. Prefer using these templates over calls to SetFor(),
-  // since by creating a new object with no previous references, they ensure
-  // cycles can not be introduced.
+  // pointer to it. Invalidates iterators for the element with the key |key|.
+  // Prefer using these templates over calls to SetFor(), since by creating
+  // a new object with no previous references, they ensure cycles can not be
+  // introduced.
   template <typename T, typename... Args>
-  typename std::enable_if<!CanInternStrings<T>::value, T*>::type SetNewFor(
-      const ByteString& key,
-      Args&&... args) {
-    CHECK(!IsLocked());
-    return static_cast<T*>(
-        SetFor(key, pdfium::MakeRetain<T>(std::forward<Args>(args)...)));
+  typename std::enable_if<!CanInternStrings<T>::value, RetainPtr<T>>::type
+  SetNewFor(const ByteString& key, Args&&... args) {
+    return pdfium::WrapRetain(static_cast<T*>(SetForInternal(
+        key, pdfium::MakeRetain<T>(std::forward<Args>(args)...))));
   }
   template <typename T, typename... Args>
-  typename std::enable_if<CanInternStrings<T>::value, T*>::type SetNewFor(
-      const ByteString& key,
-      Args&&... args) {
-    CHECK(!IsLocked());
-    return static_cast<T*>(SetFor(
-        key, pdfium::MakeRetain<T>(m_pPool, std::forward<Args>(args)...)));
+  typename std::enable_if<CanInternStrings<T>::value, RetainPtr<T>>::type
+  SetNewFor(const ByteString& key, Args&&... args) {
+    return pdfium::WrapRetain(static_cast<T*>(SetForInternal(
+        key, pdfium::MakeRetain<T>(m_pPool, std::forward<Args>(args)...))));
   }
 
+  // If |pObj| is null, then |key| is erased from the map. Otherwise, takes
+  // ownership of |pObj| and stores in in the map. Invalidates iterators for
+  // the element with the key |key|.
+  void SetFor(const ByteString& key, RetainPtr<CPDF_Object> pObj);
+
   // Convenience functions to convert native objects to array form.
   void SetRectFor(const ByteString& key, const CFX_FloatRect& rect);
   void SetMatrixFor(const ByteString& key, const CFX_Matrix& matrix);
 
-  // Set* functions invalidate iterators for the element with the key |key|.
-  // Takes ownership of |pObj|, returns an unowned pointer to it.
-  CPDF_Object* SetFor(const ByteString& key, RetainPtr<CPDF_Object> pObj);
-
   void ConvertToIndirectObjectFor(const ByteString& key,
                                   CPDF_IndirectObjectHolder* pHolder);
 
   // Invalidates iterators for the element with the key |key|.
-  RetainPtr<CPDF_Object> RemoveFor(const ByteString& key);
+  RetainPtr<CPDF_Object> RemoveFor(ByteStringView key);
 
   // Invalidates iterators for the element with the key |oldkey|.
   void ReplaceKey(const ByteString& oldkey, const ByteString& newkey);
@@ -118,21 +126,36 @@
   explicit CPDF_Dictionary(const WeakPtr<ByteStringPool>& pPool);
   ~CPDF_Dictionary() override;
 
+  // No guarantees about result lifetime, use with caution.
+  const CPDF_Object* GetObjectForInternal(const ByteString& key) const;
+  const CPDF_Object* GetDirectObjectForInternal(const ByteString& key) const;
+  const CPDF_Array* GetArrayForInternal(const ByteString& key) const;
+  const CPDF_Dictionary* GetDictForInternal(const ByteString& key) const;
+  const CPDF_Number* GetNumberForInternal(const ByteString& key) const;
+  const CPDF_Stream* GetStreamForInternal(const ByteString& key) const;
+  const CPDF_String* GetStringForInternal(const ByteString& key) const;
+  CPDF_Object* SetForInternal(const ByteString& key,
+                              RetainPtr<CPDF_Object> pObj);
+
   ByteString MaybeIntern(const ByteString& str);
+  const CPDF_Dictionary* GetDictInternal() const override;
   RetainPtr<CPDF_Object> CloneNonCyclic(
       bool bDirect,
       std::set<const CPDF_Object*>* visited) const override;
 
   mutable uint32_t m_LockCount = 0;
   WeakPtr<ByteStringPool> m_pPool;
-  std::map<ByteString, RetainPtr<CPDF_Object>> m_Map;
+  DictMap m_Map;
 };
 
 class CPDF_DictionaryLocker {
  public:
+  FX_STACK_ALLOCATED();
   using const_iterator = CPDF_Dictionary::const_iterator;
 
   explicit CPDF_DictionaryLocker(const CPDF_Dictionary* pDictionary);
+  explicit CPDF_DictionaryLocker(RetainPtr<CPDF_Dictionary> pDictionary);
+  explicit CPDF_DictionaryLocker(RetainPtr<const CPDF_Dictionary> pDictionary);
   ~CPDF_DictionaryLocker();
 
   const_iterator begin() const {
@@ -149,7 +172,7 @@
 };
 
 inline CPDF_Dictionary* ToDictionary(CPDF_Object* obj) {
-  return obj ? obj->AsDictionary() : nullptr;
+  return obj ? obj->AsMutableDictionary() : nullptr;
 }
 
 inline const CPDF_Dictionary* ToDictionary(const CPDF_Object* obj) {
@@ -160,4 +183,9 @@
   return RetainPtr<CPDF_Dictionary>(ToDictionary(obj.Get()));
 }
 
+inline RetainPtr<const CPDF_Dictionary> ToDictionary(
+    RetainPtr<const CPDF_Object> obj) {
+  return RetainPtr<const CPDF_Dictionary>(ToDictionary(obj.Get()));
+}
+
 #endif  // CORE_FPDFAPI_PARSER_CPDF_DICTIONARY_H_
diff --git a/core/fpdfapi/parser/cpdf_dictionary_unittest.cpp b/core/fpdfapi/parser/cpdf_dictionary_unittest.cpp
new file mode 100644
index 0000000..84acc3a
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_dictionary_unittest.cpp
@@ -0,0 +1,44 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+
+#include <utility>
+
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(DictionaryTest, Iterators) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Dictionary>("the-dictionary");
+  dict->SetNewFor<CPDF_Array>("the-array");
+  dict->SetNewFor<CPDF_Stream>("the-stream");
+  dict->SetNewFor<CPDF_Number>("the-number", 42);
+
+  CPDF_DictionaryLocker locked_dict(dict);
+  auto it = locked_dict.begin();
+  EXPECT_NE(it, locked_dict.end());
+  EXPECT_EQ(it->first, ByteString("the-array"));
+  EXPECT_TRUE(it->second->IsArray());
+
+  ++it;
+  EXPECT_NE(it, locked_dict.end());
+  EXPECT_EQ(it->first, ByteString("the-dictionary"));
+  EXPECT_TRUE(it->second->IsDictionary());
+
+  ++it;
+  EXPECT_NE(it, locked_dict.end());
+  EXPECT_EQ(it->first, ByteString("the-number"));
+  EXPECT_TRUE(it->second->IsNumber());
+
+  ++it;
+  EXPECT_NE(it, locked_dict.end());
+  EXPECT_EQ(it->first, ByteString("the-stream"));
+  EXPECT_TRUE(it->second->IsStream());
+
+  ++it;
+  EXPECT_EQ(it, locked_dict.end());
+}
diff --git a/core/fpdfapi/parser/cpdf_document.cpp b/core/fpdfapi/parser/cpdf_document.cpp
index d26d5c4..ee9dfcd 100644
--- a/core/fpdfapi/parser/cpdf_document.cpp
+++ b/core/fpdfapi/parser/cpdf_document.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,11 +6,8 @@
 
 #include "core/fpdfapi/parser/cpdf_document.h"
 
-#include <set>
 #include <utility>
-#include <vector>
 
-#include "build/build_config.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_linearized_header.h"
@@ -21,43 +18,109 @@
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
-#include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcodec/jbig2/JBig2_DocumentContext.h"
 #include "core/fxcrt/fx_codepage.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/scoped_set_insertion.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
 
 namespace {
 
 const int kMaxPageLevel = 1024;
 
-int CountPages(CPDF_Dictionary* pPages,
-               std::set<CPDF_Dictionary*>* visited_pages) {
+// Returns a value in the range [0, `CPDF_Document::kPageMaxNum`), or nullopt on
+// error.
+absl::optional<int> CountPages(
+    RetainPtr<CPDF_Dictionary> pPages,
+    std::set<RetainPtr<CPDF_Dictionary>>* visited_pages) {
   int count = pPages->GetIntegerFor("Count");
   if (count > 0 && count < CPDF_Document::kPageMaxNum)
     return count;
-  CPDF_Array* pKidList = pPages->GetArrayFor("Kids");
+  RetainPtr<CPDF_Array> pKidList = pPages->GetMutableArrayFor("Kids");
   if (!pKidList)
     return 0;
   count = 0;
   for (size_t i = 0; i < pKidList->size(); i++) {
-    CPDF_Dictionary* pKid = pKidList->GetDictAt(i);
-    if (!pKid || pdfium::ContainsKey(*visited_pages, pKid))
+    RetainPtr<CPDF_Dictionary> pKid = pKidList->GetMutableDictAt(i);
+    if (!pKid || pdfium::Contains(*visited_pages, pKid))
       continue;
     if (pKid->KeyExist("Kids")) {
       // Use |visited_pages| to help detect circular references of pages.
-      pdfium::ScopedSetInsertion<CPDF_Dictionary*> local_add(visited_pages,
-                                                             pKid);
-      count += CountPages(pKid, visited_pages);
+      ScopedSetInsertion<RetainPtr<CPDF_Dictionary>> local_add(visited_pages,
+                                                               pKid);
+      absl::optional<int> local_count =
+          CountPages(std::move(pKid), visited_pages);
+      if (!local_count.has_value()) {
+        return absl::nullopt;  // Propagate error.
+      }
+      count += local_count.value();
     } else {
       // This page is a leaf node.
       count++;
     }
+    if (count >= CPDF_Document::kPageMaxNum) {
+      return absl::nullopt;  // Error: too many pages.
+    }
   }
   pPages->SetNewFor<CPDF_Number>("Count", count);
   return count;
 }
 
+int FindPageIndex(const CPDF_Dictionary* pNode,
+                  uint32_t* skip_count,
+                  uint32_t objnum,
+                  int* index,
+                  int level) {
+  if (!pNode->KeyExist("Kids")) {
+    if (objnum == pNode->GetObjNum())
+      return *index;
+
+    if (*skip_count != 0)
+      (*skip_count)--;
+
+    (*index)++;
+    return -1;
+  }
+
+  RetainPtr<const CPDF_Array> pKidList = pNode->GetArrayFor("Kids");
+  if (!pKidList)
+    return -1;
+
+  if (level >= kMaxPageLevel)
+    return -1;
+
+  size_t count = pNode->GetIntegerFor("Count");
+  if (count <= *skip_count) {
+    (*skip_count) -= count;
+    (*index) += count;
+    return -1;
+  }
+
+  if (count && count == pKidList->size()) {
+    for (size_t i = 0; i < count; i++) {
+      RetainPtr<const CPDF_Reference> pKid =
+          ToReference(pKidList->GetObjectAt(i));
+      if (pKid && pKid->GetRefObjNum() == objnum)
+        return static_cast<int>(*index + i);
+    }
+  }
+
+  for (size_t i = 0; i < pKidList->size(); i++) {
+    RetainPtr<const CPDF_Dictionary> pKid = pKidList->GetDictAt(i);
+    if (!pKid || pKid == pNode)
+      continue;
+
+    int found_index =
+        FindPageIndex(pKid.Get(), skip_count, objnum, index, level + 1);
+    if (found_index >= 0)
+      return found_index;
+  }
+  return -1;
+}
+
 }  // namespace
 
 CPDF_Document::CPDF_Document(std::unique_ptr<RenderDataIface> pRenderData,
@@ -69,7 +132,19 @@
   m_pDocPage->SetDocument(this);
 }
 
-CPDF_Document::~CPDF_Document() = default;
+CPDF_Document::~CPDF_Document() {
+  // Be absolutely certain that |m_pExtension| is null before destroying
+  // the extension, to avoid re-entering it while being destroyed. clang
+  // seems to already do this for us, but the C++ standards seem to
+  // indicate the opposite.
+  m_pExtension.reset();
+}
+
+// static
+bool CPDF_Document::IsValidPageObject(const CPDF_Object* obj) {
+  // See ISO 32000-1:2008 spec, table 30.
+  return ValidateDictType(ToDictionary(obj), "Page");
+}
 
 RetainPtr<CPDF_Object> CPDF_Document::ParseIndirectObject(uint32_t objnum) {
   return m_pParser ? m_pParser->ParseIndirectObject(objnum) : nullptr;
@@ -78,30 +153,33 @@
 bool CPDF_Document::TryInit() {
   SetLastObjNum(m_pParser->GetLastObjNum());
 
-  CPDF_Object* pRootObj = GetOrParseIndirectObject(m_pParser->GetRootObjNum());
+  RetainPtr<CPDF_Object> pRootObj =
+      GetOrParseIndirectObject(m_pParser->GetRootObjNum());
   if (pRootObj)
-    m_pRootDict.Reset(pRootObj->GetDict());
+    m_pRootDict = pRootObj->GetMutableDict();
 
   LoadPages();
   return GetRoot() && GetPageCount() > 0;
 }
 
 CPDF_Parser::Error CPDF_Document::LoadDoc(
-    const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
-    const char* password) {
+    RetainPtr<IFX_SeekableReadStream> pFileAccess,
+    const ByteString& password) {
   if (!m_pParser)
-    SetParser(pdfium::MakeUnique<CPDF_Parser>(this));
+    SetParser(std::make_unique<CPDF_Parser>(this));
 
-  return HandleLoadResult(m_pParser->StartParse(pFileAccess, password));
+  return HandleLoadResult(
+      m_pParser->StartParse(std::move(pFileAccess), password));
 }
 
 CPDF_Parser::Error CPDF_Document::LoadLinearizedDoc(
-    const RetainPtr<CPDF_ReadValidator>& validator,
-    const char* password) {
+    RetainPtr<CPDF_ReadValidator> validator,
+    const ByteString& password) {
   if (!m_pParser)
-    SetParser(pdfium::MakeUnique<CPDF_Parser>(this));
+    SetParser(std::make_unique<CPDF_Parser>(this));
 
-  return HandleLoadResult(m_pParser->StartLinearizedParse(validator, password));
+  return HandleLoadResult(
+      m_pParser->StartLinearizedParse(std::move(validator), password));
 }
 
 void CPDF_Document::LoadPages() {
@@ -112,20 +190,27 @@
     return;
   }
 
-  m_PageList.resize(linearized_header->GetPageCount());
-  ASSERT(linearized_header->GetFirstPageNo() < m_PageList.size());
-  m_PageList[linearized_header->GetFirstPageNo()] =
-      linearized_header->GetFirstPageObjNum();
+  uint32_t objnum = linearized_header->GetFirstPageObjNum();
+  if (!IsValidPageObject(GetOrParseIndirectObject(objnum).Get())) {
+    m_PageList.resize(RetrievePageCount());
+    return;
+  }
+
+  uint32_t first_page_num = linearized_header->GetFirstPageNo();
+  uint32_t page_count = linearized_header->GetPageCount();
+  DCHECK(first_page_num < page_count);
+  m_PageList.resize(page_count);
+  m_PageList[first_page_num] = objnum;
 }
 
-CPDF_Dictionary* CPDF_Document::TraversePDFPages(int iPage,
-                                                 int* nPagesToGo,
-                                                 size_t level) {
+RetainPtr<CPDF_Dictionary> CPDF_Document::TraversePDFPages(int iPage,
+                                                           int* nPagesToGo,
+                                                           size_t level) {
   if (*nPagesToGo < 0 || m_bReachedMaxPageLevel)
     return nullptr;
 
-  CPDF_Dictionary* pPages = m_pTreeTraversal[level].first;
-  CPDF_Array* pKidList = pPages->GetArrayFor("Kids");
+  RetainPtr<CPDF_Dictionary> pPages = m_pTreeTraversal[level].first;
+  RetainPtr<CPDF_Array> pKidList = pPages->GetMutableArrayFor("Kids");
   if (!pKidList) {
     m_pTreeTraversal.pop_back();
     if (*nPagesToGo != 1)
@@ -138,12 +223,12 @@
     m_bReachedMaxPageLevel = true;
     return nullptr;
   }
-  CPDF_Dictionary* page = nullptr;
+  RetainPtr<CPDF_Dictionary> page;
   for (size_t i = m_pTreeTraversal[level].second; i < pKidList->size(); i++) {
     if (*nPagesToGo == 0)
       break;
     pKidList->ConvertToIndirectObjectAt(i, this);
-    CPDF_Dictionary* pKid = pKidList->GetDictAt(i);
+    RetainPtr<CPDF_Dictionary> pKid = pKidList->GetMutableDictAt(i);
     if (!pKid) {
       (*nPagesToGo)--;
       m_pTreeTraversal[level].second++;
@@ -158,22 +243,23 @@
       (*nPagesToGo)--;
       m_pTreeTraversal[level].second++;
       if (*nPagesToGo == 0) {
-        page = pKid;
+        page = std::move(pKid);
         break;
       }
     } else {
       // If the vector has size level+1, the child is not in yet
       if (m_pTreeTraversal.size() == level + 1)
-        m_pTreeTraversal.push_back(std::make_pair(pKid, 0));
+        m_pTreeTraversal.emplace_back(std::move(pKid), 0);
       // Now m_pTreeTraversal[level+1] should exist and be equal to pKid.
-      CPDF_Dictionary* pageKid = TraversePDFPages(iPage, nPagesToGo, level + 1);
+      RetainPtr<CPDF_Dictionary> pPageKid =
+          TraversePDFPages(iPage, nPagesToGo, level + 1);
       // Check if child was completely processed, i.e. it popped itself out
       if (m_pTreeTraversal.size() == level + 1)
         m_pTreeTraversal[level].second++;
       // If child did not finish, no pages to go, or max level reached, end
       if (m_pTreeTraversal.size() != level + 1 || *nPagesToGo == 0 ||
           m_bReachedMaxPageLevel) {
-        page = pageKid;
+        page = std::move(pPageKid);
         break;
       }
     }
@@ -190,7 +276,7 @@
 }
 
 void CPDF_Document::SetParser(std::unique_ptr<CPDF_Parser> pParser) {
-  ASSERT(!m_pParser);
+  DCHECK(!m_pParser);
   m_pParser = std::move(pParser);
 }
 
@@ -200,104 +286,75 @@
   return error;
 }
 
-const CPDF_Dictionary* CPDF_Document::GetPagesDict() const {
+RetainPtr<const CPDF_Dictionary> CPDF_Document::GetPagesDict() const {
   const CPDF_Dictionary* pRoot = GetRoot();
   return pRoot ? pRoot->GetDictFor("Pages") : nullptr;
 }
 
-CPDF_Dictionary* CPDF_Document::GetPagesDict() {
-  return const_cast<CPDF_Dictionary*>(
-      static_cast<const CPDF_Document*>(this)->GetPagesDict());
+RetainPtr<CPDF_Dictionary> CPDF_Document::GetMutablePagesDict() {
+  return pdfium::WrapRetain(
+      const_cast<CPDF_Dictionary*>(this->GetPagesDict().Get()));
 }
 
 bool CPDF_Document::IsPageLoaded(int iPage) const {
   return !!m_PageList[iPage];
 }
 
-CPDF_Dictionary* CPDF_Document::GetPageDictionary(int iPage) {
-  if (!pdfium::IndexInBounds(m_PageList, iPage))
+RetainPtr<const CPDF_Dictionary> CPDF_Document::GetPageDictionary(int iPage) {
+  if (!fxcrt::IndexInBounds(m_PageList, iPage))
     return nullptr;
 
   const uint32_t objnum = m_PageList[iPage];
   if (objnum) {
-    CPDF_Dictionary* result = ToDictionary(GetOrParseIndirectObject(objnum));
+    RetainPtr<CPDF_Dictionary> result =
+        ToDictionary(GetOrParseIndirectObject(objnum));
     if (result)
       return result;
   }
 
-  CPDF_Dictionary* pPages = GetPagesDict();
+  RetainPtr<CPDF_Dictionary> pPages = GetMutablePagesDict();
   if (!pPages)
     return nullptr;
 
   if (m_pTreeTraversal.empty()) {
     ResetTraversal();
-    m_pTreeTraversal.push_back(std::make_pair(pPages, 0));
+    m_pTreeTraversal.emplace_back(std::move(pPages), 0);
   }
   int nPagesToGo = iPage - m_iNextPageToTraverse + 1;
-  CPDF_Dictionary* pPage = TraversePDFPages(iPage, &nPagesToGo, 0);
+  RetainPtr<CPDF_Dictionary> pPage = TraversePDFPages(iPage, &nPagesToGo, 0);
   m_iNextPageToTraverse = iPage + 1;
   return pPage;
 }
 
+RetainPtr<CPDF_Dictionary> CPDF_Document::GetMutablePageDictionary(int iPage) {
+  return pdfium::WrapRetain(
+      const_cast<CPDF_Dictionary*>(GetPageDictionary(iPage).Get()));
+}
+
 void CPDF_Document::SetPageObjNum(int iPage, uint32_t objNum) {
   m_PageList[iPage] = objNum;
 }
 
-int CPDF_Document::FindPageIndex(const CPDF_Dictionary* pNode,
-                                 uint32_t* skip_count,
-                                 uint32_t objnum,
-                                 int* index,
-                                 int level) const {
-  if (!pNode->KeyExist("Kids")) {
-    if (objnum == pNode->GetObjNum())
-      return *index;
+JBig2_DocumentContext* CPDF_Document::GetOrCreateCodecContext() {
+  if (!m_pCodecContext)
+    m_pCodecContext = std::make_unique<JBig2_DocumentContext>();
+  return m_pCodecContext.get();
+}
 
-    if (*skip_count)
-      (*skip_count)--;
+RetainPtr<CPDF_Stream> CPDF_Document::CreateModifiedAPStream() {
+  auto stream = NewIndirect<CPDF_Stream>();
+  m_ModifiedAPStreamIDs.insert(stream->GetObjNum());
+  return stream;
+}
 
-    (*index)++;
-    return -1;
-  }
-
-  const CPDF_Array* pKidList = pNode->GetArrayFor("Kids");
-  if (!pKidList)
-    return -1;
-
-  if (level >= kMaxPageLevel)
-    return -1;
-
-  size_t count = pNode->GetIntegerFor("Count");
-  if (count <= *skip_count) {
-    (*skip_count) -= count;
-    (*index) += count;
-    return -1;
-  }
-
-  if (count && count == pKidList->size()) {
-    for (size_t i = 0; i < count; i++) {
-      const CPDF_Reference* pKid = ToReference(pKidList->GetObjectAt(i));
-      if (pKid && pKid->GetRefObjNum() == objnum)
-        return static_cast<int>(*index + i);
-    }
-  }
-
-  for (size_t i = 0; i < pKidList->size(); i++) {
-    const CPDF_Dictionary* pKid = pKidList->GetDictAt(i);
-    if (!pKid || pKid == pNode)
-      continue;
-
-    int found_index = FindPageIndex(pKid, skip_count, objnum, index, level + 1);
-    if (found_index >= 0)
-      return found_index;
-  }
-  return -1;
+bool CPDF_Document::IsModifiedAPStream(const CPDF_Stream* stream) const {
+  return stream && pdfium::Contains(m_ModifiedAPStreamIDs, stream->GetObjNum());
 }
 
 int CPDF_Document::GetPageIndex(uint32_t objnum) {
-  uint32_t nPages = m_PageList.size();
   uint32_t skip_count = 0;
   bool bSkipped = false;
-  for (uint32_t i = 0; i < nPages; i++) {
+  for (uint32_t i = 0; i < m_PageList.size(); ++i) {
     if (m_PageList[i] == objnum)
       return i;
 
@@ -306,7 +363,7 @@
       bSkipped = true;
     }
   }
-  const CPDF_Dictionary* pPages = GetPagesDict();
+  RetainPtr<const CPDF_Dictionary> pPages = GetPagesDict();
   if (!pPages)
     return -1;
 
@@ -314,28 +371,29 @@
   int found_index = FindPageIndex(pPages, &skip_count, objnum, &start_index, 0);
 
   // Corrupt page tree may yield out-of-range results.
-  if (!pdfium::IndexInBounds(m_PageList, found_index))
+  if (!fxcrt::IndexInBounds(m_PageList, found_index))
     return -1;
 
-  m_PageList[found_index] = objnum;
+  // Only update |m_PageList| when |objnum| points to a /Page object.
+  if (IsValidPageObject(GetOrParseIndirectObject(objnum).Get()))
+    m_PageList[found_index] = objnum;
   return found_index;
 }
 
 int CPDF_Document::GetPageCount() const {
-  return pdfium::CollectionSize<int>(m_PageList);
+  return fxcrt::CollectionSize<int>(m_PageList);
 }
 
 int CPDF_Document::RetrievePageCount() {
-  CPDF_Dictionary* pPages = GetPagesDict();
+  RetainPtr<CPDF_Dictionary> pPages = GetMutablePagesDict();
   if (!pPages)
     return 0;
 
   if (!pPages->KeyExist("Kids"))
     return 1;
 
-  std::set<CPDF_Dictionary*> visited_pages;
-  visited_pages.insert(pPages);
-  return CountPages(pPages, &visited_pages);
+  std::set<RetainPtr<CPDF_Dictionary>> visited_pages = {pPages};
+  return CountPages(std::move(pPages), &visited_pages).value_or(0);
 }
 
 uint32_t CPDF_Document::GetUserPermissions() const {
@@ -345,22 +403,38 @@
   return m_pExtension ? m_pExtension->GetUserPermissions() : 0;
 }
 
+RetainPtr<CPDF_StreamAcc> CPDF_Document::GetFontFileStreamAcc(
+    RetainPtr<const CPDF_Stream> pFontStream) {
+  return m_pDocPage->GetFontFileStreamAcc(std::move(pFontStream));
+}
+
+void CPDF_Document::MaybePurgeFontFileStreamAcc(
+    RetainPtr<CPDF_StreamAcc>&& pStreamAcc) {
+  if (m_pDocPage)
+    m_pDocPage->MaybePurgeFontFileStreamAcc(std::move(pStreamAcc));
+}
+
+void CPDF_Document::MaybePurgeImage(uint32_t objnum) {
+  if (m_pDocPage)
+    m_pDocPage->MaybePurgeImage(objnum);
+}
+
 void CPDF_Document::CreateNewDoc() {
-  ASSERT(!m_pRootDict);
-  ASSERT(!m_pInfoDict);
-  m_pRootDict.Reset(NewIndirect<CPDF_Dictionary>());
+  DCHECK(!m_pRootDict);
+  DCHECK(!m_pInfoDict);
+  m_pRootDict = NewIndirect<CPDF_Dictionary>();
   m_pRootDict->SetNewFor<CPDF_Name>("Type", "Catalog");
 
-  CPDF_Dictionary* pPages = NewIndirect<CPDF_Dictionary>();
+  auto pPages = NewIndirect<CPDF_Dictionary>();
   pPages->SetNewFor<CPDF_Name>("Type", "Pages");
   pPages->SetNewFor<CPDF_Number>("Count", 0);
   pPages->SetNewFor<CPDF_Array>("Kids");
   m_pRootDict->SetNewFor<CPDF_Reference>("Pages", this, pPages->GetObjNum());
-  m_pInfoDict.Reset(NewIndirect<CPDF_Dictionary>());
+  m_pInfoDict = NewIndirect<CPDF_Dictionary>();
 }
 
-CPDF_Dictionary* CPDF_Document::CreateNewPage(int iPage) {
-  CPDF_Dictionary* pDict = NewIndirect<CPDF_Dictionary>();
+RetainPtr<CPDF_Dictionary> CPDF_Document::CreateNewPage(int iPage) {
+  auto pDict = NewIndirect<CPDF_Dictionary>();
   pDict->SetNewFor<CPDF_Name>("Type", "Page");
   uint32_t dwObjNum = pDict->GetObjNum();
   if (!InsertNewPage(iPage, pDict)) {
@@ -370,18 +444,19 @@
   return pDict;
 }
 
-bool CPDF_Document::InsertDeletePDFPage(CPDF_Dictionary* pPages,
-                                        int nPagesToGo,
-                                        CPDF_Dictionary* pPageDict,
-                                        bool bInsert,
-                                        std::set<CPDF_Dictionary*>* pVisited) {
-  CPDF_Array* pKidList = pPages->GetArrayFor("Kids");
+bool CPDF_Document::InsertDeletePDFPage(
+    RetainPtr<CPDF_Dictionary> pPages,
+    int nPagesToGo,
+    RetainPtr<CPDF_Dictionary> pPageDict,
+    bool bInsert,
+    std::set<RetainPtr<CPDF_Dictionary>>* pVisited) {
+  RetainPtr<CPDF_Array> pKidList = pPages->GetMutableArrayFor("Kids");
   if (!pKidList)
     return false;
 
   for (size_t i = 0; i < pKidList->size(); i++) {
-    CPDF_Dictionary* pKid = pKidList->GetDictAt(i);
-    if (pKid->GetStringFor("Type") == "Page") {
+    RetainPtr<CPDF_Dictionary> pKid = pKidList->GetMutableDictAt(i);
+    if (pKid->GetNameFor("Type") == "Page") {
       if (nPagesToGo != 0) {
         nPagesToGo--;
         continue;
@@ -403,13 +478,14 @@
       nPagesToGo -= nPages;
       continue;
     }
-    if (pdfium::ContainsKey(*pVisited, pKid))
+    if (pdfium::Contains(*pVisited, pKid))
       return false;
 
-    pdfium::ScopedSetInsertion<CPDF_Dictionary*> insertion(pVisited, pKid);
-    if (!InsertDeletePDFPage(pKid, nPagesToGo, pPageDict, bInsert, pVisited))
+    ScopedSetInsertion<RetainPtr<CPDF_Dictionary>> insertion(pVisited, pKid);
+    if (!InsertDeletePDFPage(std::move(pKid), nPagesToGo, pPageDict, bInsert,
+                             pVisited)) {
       return false;
-
+    }
     pPages->SetNewFor<CPDF_Number>(
         "Count", pPages->GetIntegerFor("Count") + (bInsert ? 1 : -1));
     break;
@@ -417,9 +493,13 @@
   return true;
 }
 
-bool CPDF_Document::InsertNewPage(int iPage, CPDF_Dictionary* pPageDict) {
-  CPDF_Dictionary* pRoot = GetRoot();
-  CPDF_Dictionary* pPages = pRoot ? pRoot->GetDictFor("Pages") : nullptr;
+bool CPDF_Document::InsertNewPage(int iPage,
+                                  RetainPtr<CPDF_Dictionary> pPageDict) {
+  RetainPtr<CPDF_Dictionary> pRoot = GetMutableRoot();
+  if (!pRoot)
+    return false;
+
+  RetainPtr<CPDF_Dictionary> pPages = pRoot->GetMutableDictFor("Pages");
   if (!pPages)
     return false;
 
@@ -428,37 +508,42 @@
     return false;
 
   if (iPage == nPages) {
-    CPDF_Array* pPagesList = pPages->GetArrayFor("Kids");
-    if (!pPagesList)
-      pPagesList = pPages->SetNewFor<CPDF_Array>("Kids");
-    pPagesList->AddNew<CPDF_Reference>(this, pPageDict->GetObjNum());
+    RetainPtr<CPDF_Array> pPagesList = pPages->GetOrCreateArrayFor("Kids");
+    pPagesList->AppendNew<CPDF_Reference>(this, pPageDict->GetObjNum());
     pPages->SetNewFor<CPDF_Number>("Count", nPages + 1);
     pPageDict->SetNewFor<CPDF_Reference>("Parent", this, pPages->GetObjNum());
     ResetTraversal();
   } else {
-    std::set<CPDF_Dictionary*> stack = {pPages};
-    if (!InsertDeletePDFPage(pPages, iPage, pPageDict, true, &stack))
+    std::set<RetainPtr<CPDF_Dictionary>> stack = {pPages};
+    if (!InsertDeletePDFPage(std::move(pPages), iPage, pPageDict, true, &stack))
       return false;
   }
   m_PageList.insert(m_PageList.begin() + iPage, pPageDict->GetObjNum());
   return true;
 }
 
-CPDF_Dictionary* CPDF_Document::GetInfo() {
+RetainPtr<CPDF_Dictionary> CPDF_Document::GetInfo() {
   if (m_pInfoDict)
-    return m_pInfoDict.Get();
+    return m_pInfoDict;
 
-  if (!m_pParser || !m_pParser->GetInfoObjNum())
+  if (!m_pParser)
     return nullptr;
 
-  auto ref =
-      pdfium::MakeRetain<CPDF_Reference>(this, m_pParser->GetInfoObjNum());
-  m_pInfoDict.Reset(ToDictionary(ref->GetDirect()));
-  return m_pInfoDict.Get();
+  uint32_t info_obj_num = m_pParser->GetInfoObjNum();
+  if (info_obj_num == 0)
+    return nullptr;
+
+  auto ref = pdfium::MakeRetain<CPDF_Reference>(this, info_obj_num);
+  m_pInfoDict = ToDictionary(ref->GetMutableDirect());
+  return m_pInfoDict;
+}
+
+RetainPtr<const CPDF_Array> CPDF_Document::GetFileIdentifier() const {
+  return m_pParser ? m_pParser->GetIDArray() : nullptr;
 }
 
 void CPDF_Document::DeletePage(int iPage) {
-  CPDF_Dictionary* pPages = GetPagesDict();
+  RetainPtr<CPDF_Dictionary> pPages = GetMutablePagesDict();
   if (!pPages)
     return;
 
@@ -466,13 +551,21 @@
   if (iPage < 0 || iPage >= nPages)
     return;
 
-  std::set<CPDF_Dictionary*> stack = {pPages};
-  if (!InsertDeletePDFPage(pPages, iPage, nullptr, false, &stack))
+  std::set<RetainPtr<CPDF_Dictionary>> stack = {pPages};
+  if (!InsertDeletePDFPage(std::move(pPages), iPage, nullptr, false, &stack))
     return;
 
   m_PageList.erase(m_PageList.begin() + iPage);
 }
 
+void CPDF_Document::SetRootForTesting(RetainPtr<CPDF_Dictionary> root) {
+  m_pRootDict = std::move(root);
+}
+
+void CPDF_Document::ResizePageListForTesting(size_t size) {
+  m_PageList.resize(size);
+}
+
 CPDF_Document::StockFontClearer::StockFontClearer(
     CPDF_Document::PageDataIface* pPageData)
     : m_pPageData(pPageData) {}
diff --git a/core/fpdfapi/parser/cpdf_document.h b/core/fpdfapi/parser/cpdf_document.h
index d0d1729..9ceff30 100644
--- a/core/fpdfapi/parser/cpdf_document.h
+++ b/core/fpdfapi/parser/cpdf_document.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,32 +7,23 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_DOCUMENT_H_
 #define CORE_FPDFAPI_PARSER_CPDF_DOCUMENT_H_
 
-#include <functional>
 #include <memory>
 #include <set>
 #include <utility>
 #include <vector>
 
-#include "build/build_config.h"
-#include "core/fpdfapi/parser/cpdf_object.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_parser.h"
+#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
-class CFX_Matrix;
-class CPDF_LinearizedHeader;
-class CPDF_Object;
 class CPDF_ReadValidator;
 class CPDF_StreamAcc;
 class IFX_SeekableReadStream;
 class JBig2_DocumentContext;
 
-#define FPDFPERM_MODIFY 0x0008
-#define FPDFPERM_ANNOT_FORM 0x0020
-#define FPDFPERM_FILL_FORM 0x0100
-#define FPDFPERM_EXTRACT_ACCESS 0x0200
-
 class CPDF_Document : public Observable,
                       public CPDF_Parser::ParsedObjectsHolder {
  public:
@@ -40,7 +31,6 @@
   class Extension {
    public:
     virtual ~Extension() = default;
-    virtual CPDF_Document* GetPDFDoc() const = 0;
     virtual int GetPageCount() const = 0;
     virtual void DeletePage(int page_index) = 0;
     virtual uint32_t GetUserPermissions() const = 0;
@@ -62,12 +52,15 @@
 
     virtual void ClearStockFont() = 0;
     virtual RetainPtr<CPDF_StreamAcc> GetFontFileStreamAcc(
-        const CPDF_Stream* pFontStream) = 0;
+        RetainPtr<const CPDF_Stream> pFontStream) = 0;
     virtual void MaybePurgeFontFileStreamAcc(
-        const CPDF_Stream* pFontStream) = 0;
+        RetainPtr<CPDF_StreamAcc>&& pStreamAcc) = 0;
+    virtual void MaybePurgeImage(uint32_t objnum) = 0;
 
     void SetDocument(CPDF_Document* pDoc) { m_pDoc = pDoc; }
-    CPDF_Document* GetDocument() const { return m_pDoc.Get(); }
+
+   protected:
+    CPDF_Document* GetDocument() const { return m_pDoc; }
 
    private:
     UnownedPtr<CPDF_Document> m_pDoc;
@@ -79,13 +72,17 @@
     virtual ~RenderDataIface();
 
     void SetDocument(CPDF_Document* pDoc) { m_pDoc = pDoc; }
-    CPDF_Document* GetDocument() const { return m_pDoc.Get(); }
+
+   protected:
+    CPDF_Document* GetDocument() const { return m_pDoc; }
 
    private:
     UnownedPtr<CPDF_Document> m_pDoc;
   };
 
-  static const int kPageMaxNum = 0xFFFFF;
+  static constexpr int kPageMaxNum = 0xFFFFF;
+
+  static bool IsValidPageObject(const CPDF_Object* obj);
 
   CPDF_Document(std::unique_ptr<RenderDataIface> pRenderData,
                 std::unique_ptr<PageDataIface> pPageData);
@@ -97,53 +94,73 @@
   }
 
   CPDF_Parser* GetParser() const { return m_pParser.get(); }
-  CPDF_Dictionary* GetRoot() const { return m_pRootDict.Get(); }
-  CPDF_Dictionary* GetInfo();
+  const CPDF_Dictionary* GetRoot() const { return m_pRootDict.Get(); }
+  RetainPtr<CPDF_Dictionary> GetMutableRoot() { return m_pRootDict; }
+  RetainPtr<CPDF_Dictionary> GetInfo();
+  RetainPtr<const CPDF_Array> GetFileIdentifier() const;
 
   void DeletePage(int iPage);
   int GetPageCount() const;
   bool IsPageLoaded(int iPage) const;
-  CPDF_Dictionary* GetPageDictionary(int iPage);
+  RetainPtr<const CPDF_Dictionary> GetPageDictionary(int iPage);
+  RetainPtr<CPDF_Dictionary> GetMutablePageDictionary(int iPage);
   int GetPageIndex(uint32_t objnum);
   uint32_t GetUserPermissions() const;
 
+  // PageDataIface wrappers, try to avoid explicit getter calls.
+  RetainPtr<CPDF_StreamAcc> GetFontFileStreamAcc(
+      RetainPtr<const CPDF_Stream> pFontStream);
+  void MaybePurgeFontFileStreamAcc(RetainPtr<CPDF_StreamAcc>&& pStreamAcc);
+  void MaybePurgeImage(uint32_t objnum);
+
   // Returns a valid pointer, unless it is called during destruction.
   PageDataIface* GetPageData() const { return m_pDocPage.get(); }
   RenderDataIface* GetRenderData() const { return m_pDocRender.get(); }
 
   void SetPageObjNum(int iPage, uint32_t objNum);
 
-  std::unique_ptr<JBig2_DocumentContext>* CodecContext() {
-    return &m_pCodecContext;
-  }
+  JBig2_DocumentContext* GetOrCreateCodecContext();
   LinkListIface* GetLinksContext() const { return m_pLinksContext.get(); }
   void SetLinksContext(std::unique_ptr<LinkListIface> pContext) {
     m_pLinksContext = std::move(pContext);
   }
 
-  //  CPDF_Parser::ParsedObjectsHolder overrides:
-  bool TryInit() override;
+  // Behaves like NewIndirect<CPDF_Stream>(), but keeps track of the new stream.
+  RetainPtr<CPDF_Stream> CreateModifiedAPStream();
 
-  CPDF_Parser::Error LoadDoc(
-      const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
-      const char* password);
-  CPDF_Parser::Error LoadLinearizedDoc(
-      const RetainPtr<CPDF_ReadValidator>& validator,
-      const char* password);
+  // Returns whether CreateModifiedAPStream() created `stream`.
+  bool IsModifiedAPStream(const CPDF_Stream* stream) const;
+
+  // CPDF_Parser::ParsedObjectsHolder:
+  bool TryInit() override;
+  RetainPtr<CPDF_Object> ParseIndirectObject(uint32_t objnum) override;
+
+  CPDF_Parser::Error LoadDoc(RetainPtr<IFX_SeekableReadStream> pFileAccess,
+                             const ByteString& password);
+  CPDF_Parser::Error LoadLinearizedDoc(RetainPtr<CPDF_ReadValidator> validator,
+                                       const ByteString& password);
   bool has_valid_cross_reference_table() const {
     return m_bHasValidCrossReferenceTable;
   }
 
   void LoadPages();
   void CreateNewDoc();
-  CPDF_Dictionary* CreateNewPage(int iPage);
+  RetainPtr<CPDF_Dictionary> CreateNewPage(int iPage);
 
   void IncrementParsedPageCount() { ++m_ParsedPageCount; }
   uint32_t GetParsedPageCountForTesting() { return m_ParsedPageCount; }
 
  protected:
+  void SetParser(std::unique_ptr<CPDF_Parser> pParser);
+
+  void SetRootForTesting(RetainPtr<CPDF_Dictionary> root);
+  void ResizePageListForTesting(size_t size);
+
+ private:
   class StockFontClearer {
    public:
+    FX_STACK_ALLOCATED();
+
     explicit StockFontClearer(CPDF_Document::PageDataIface* pPageData);
     ~StockFontClearer();
 
@@ -153,24 +170,23 @@
 
   // Retrieve page count information by getting count value from the tree nodes
   int RetrievePageCount();
+
   // When this method is called, m_pTreeTraversal[level] exists.
-  CPDF_Dictionary* TraversePDFPages(int iPage, int* nPagesToGo, size_t level);
-  int FindPageIndex(const CPDF_Dictionary* pNode,
-                    uint32_t* skip_count,
-                    uint32_t objnum,
-                    int* index,
-                    int level) const;
-  RetainPtr<CPDF_Object> ParseIndirectObject(uint32_t objnum) override;
-  const CPDF_Dictionary* GetPagesDict() const;
-  CPDF_Dictionary* GetPagesDict();
-  bool InsertDeletePDFPage(CPDF_Dictionary* pPages,
+  RetainPtr<CPDF_Dictionary> TraversePDFPages(int iPage,
+                                              int* nPagesToGo,
+                                              size_t level);
+
+  RetainPtr<const CPDF_Dictionary> GetPagesDict() const;
+  RetainPtr<CPDF_Dictionary> GetMutablePagesDict();
+
+  bool InsertDeletePDFPage(RetainPtr<CPDF_Dictionary> pPages,
                            int nPagesToGo,
-                           CPDF_Dictionary* pPageDict,
+                           RetainPtr<CPDF_Dictionary> pPageDict,
                            bool bInsert,
-                           std::set<CPDF_Dictionary*>* pVisited);
-  bool InsertNewPage(int iPage, CPDF_Dictionary* pPageDict);
+                           std::set<RetainPtr<CPDF_Dictionary>>* pVisited);
+
+  bool InsertNewPage(int iPage, RetainPtr<CPDF_Dictionary> pPageDict);
   void ResetTraversal();
-  void SetParser(std::unique_ptr<CPDF_Parser> pParser);
   CPDF_Parser::Error HandleLoadResult(CPDF_Parser::Error error);
 
   std::unique_ptr<CPDF_Parser> m_pParser;
@@ -181,7 +197,7 @@
   // vector corresponds to the level being described. The pair contains a
   // pointer to the dictionary being processed at the level, and an index of the
   // of the child being processed within the dictionary's /Kids array.
-  std::vector<std::pair<CPDF_Dictionary*, size_t>> m_pTreeTraversal;
+  std::vector<std::pair<RetainPtr<CPDF_Dictionary>, size_t>> m_pTreeTraversal;
 
   // True if the CPDF_Parser succeeded without having to rebuild the cross
   // reference table.
@@ -196,6 +212,7 @@
   std::unique_ptr<PageDataIface> m_pDocPage;  // Must be after |m_pDocRender|.
   std::unique_ptr<JBig2_DocumentContext> m_pCodecContext;
   std::unique_ptr<LinkListIface> m_pLinksContext;
+  std::set<uint32_t> m_ModifiedAPStreamIDs;
   std::vector<uint32_t> m_PageList;  // Page number to page's dict objnum.
 
   // Must be second to last.
diff --git a/core/fpdfapi/parser/cpdf_document_unittest.cpp b/core/fpdfapi/parser/cpdf_document_unittest.cpp
index 1c52e51..bc2aaae 100644
--- a/core/fpdfapi/parser/cpdf_document_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_document_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,7 @@
 #include <memory>
 #include <utility>
 
-#include "core/fpdfapi/page/cpdf_docpagedata.h"
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fpdfapi/page/test_with_page_module.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_boolean.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
@@ -18,115 +17,114 @@
 #include "core/fpdfapi/parser/cpdf_parser.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfapi/render/cpdf_docrenderdata.h"
+#include "core/fpdfapi/parser/cpdf_test_document.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 namespace {
 
 const int kNumTestPages = 7;
 
-CPDF_Dictionary* CreatePageTreeNode(RetainPtr<CPDF_Array> kids,
-                                    CPDF_Document* pDoc,
-                                    int count) {
-  CPDF_Array* pUnowned = pDoc->AddIndirectObject(std::move(kids))->AsArray();
-  CPDF_Dictionary* pageNode = pDoc->NewIndirect<CPDF_Dictionary>();
-  pageNode->SetNewFor<CPDF_String>("Type", "Pages", false);
-  pageNode->SetNewFor<CPDF_Reference>("Kids", pDoc, pUnowned->GetObjNum());
+RetainPtr<CPDF_Dictionary> CreatePageTreeNode(RetainPtr<CPDF_Array> kids,
+                                              CPDF_Document* pDoc,
+                                              int count) {
+  uint32_t new_objnum = pDoc->AddIndirectObject(kids);
+  auto pageNode = pDoc->NewIndirect<CPDF_Dictionary>();
+  pageNode->SetNewFor<CPDF_Name>("Type", "Pages");
+  pageNode->SetNewFor<CPDF_Reference>("Kids", pDoc, new_objnum);
   pageNode->SetNewFor<CPDF_Number>("Count", count);
-  for (size_t i = 0; i < pUnowned->size(); i++) {
-    pUnowned->GetDictAt(i)->SetNewFor<CPDF_Reference>("Parent", pDoc,
-                                                      pageNode->GetObjNum());
+  for (size_t i = 0; i < kids->size(); i++) {
+    kids->GetMutableDictAt(i)->SetNewFor<CPDF_Reference>("Parent", pDoc,
+                                                         pageNode->GetObjNum());
   }
   return pageNode;
 }
 
 RetainPtr<CPDF_Dictionary> CreateNumberedPage(size_t number) {
   auto page = pdfium::MakeRetain<CPDF_Dictionary>();
-  page->SetNewFor<CPDF_String>("Type", "Page", false);
+  page->SetNewFor<CPDF_Name>("Type", "Page");
   page->SetNewFor<CPDF_Number>("PageNumbering", static_cast<int>(number));
   return page;
 }
 
-class CPDF_TestDocumentForPages final : public CPDF_Document {
+class CPDF_TestDocumentForPages final : public CPDF_TestDocument {
  public:
-  CPDF_TestDocumentForPages()
-      : CPDF_Document(pdfium::MakeUnique<CPDF_DocRenderData>(),
-                      pdfium::MakeUnique<CPDF_DocPageData>()) {
+  CPDF_TestDocumentForPages() {
     // Set up test
     auto zeroToTwo = pdfium::MakeRetain<CPDF_Array>();
-    zeroToTwo->AddNew<CPDF_Reference>(
-        this, AddIndirectObject(CreateNumberedPage(0))->GetObjNum());
-    zeroToTwo->AddNew<CPDF_Reference>(
-        this, AddIndirectObject(CreateNumberedPage(1))->GetObjNum());
-    zeroToTwo->AddNew<CPDF_Reference>(
-        this, AddIndirectObject(CreateNumberedPage(2))->GetObjNum());
-    CPDF_Dictionary* branch1 =
+    zeroToTwo->AppendNew<CPDF_Reference>(
+        this, AddIndirectObject(CreateNumberedPage(0)));
+    zeroToTwo->AppendNew<CPDF_Reference>(
+        this, AddIndirectObject(CreateNumberedPage(1)));
+    zeroToTwo->AppendNew<CPDF_Reference>(
+        this, AddIndirectObject(CreateNumberedPage(2)));
+    RetainPtr<CPDF_Dictionary> branch1 =
         CreatePageTreeNode(std::move(zeroToTwo), this, 3);
 
     auto zeroToThree = pdfium::MakeRetain<CPDF_Array>();
-    zeroToThree->AddNew<CPDF_Reference>(this, branch1->GetObjNum());
-    zeroToThree->AddNew<CPDF_Reference>(
-        this, AddIndirectObject(CreateNumberedPage(3))->GetObjNum());
-    CPDF_Dictionary* branch2 =
+    zeroToThree->AppendNew<CPDF_Reference>(this, branch1->GetObjNum());
+    zeroToThree->AppendNew<CPDF_Reference>(
+        this, AddIndirectObject(CreateNumberedPage(3)));
+    RetainPtr<CPDF_Dictionary> branch2 =
         CreatePageTreeNode(std::move(zeroToThree), this, 4);
 
     auto fourFive = pdfium::MakeRetain<CPDF_Array>();
-    fourFive->AddNew<CPDF_Reference>(
-        this, AddIndirectObject(CreateNumberedPage(4))->GetObjNum());
-    fourFive->AddNew<CPDF_Reference>(
-        this, AddIndirectObject(CreateNumberedPage(5))->GetObjNum());
-    CPDF_Dictionary* branch3 = CreatePageTreeNode(std::move(fourFive), this, 2);
+    fourFive->AppendNew<CPDF_Reference>(
+        this, AddIndirectObject(CreateNumberedPage(4)));
+    fourFive->AppendNew<CPDF_Reference>(
+        this, AddIndirectObject(CreateNumberedPage(5)));
+    RetainPtr<CPDF_Dictionary> branch3 =
+        CreatePageTreeNode(std::move(fourFive), this, 2);
 
     auto justSix = pdfium::MakeRetain<CPDF_Array>();
-    justSix->AddNew<CPDF_Reference>(
-        this, AddIndirectObject(CreateNumberedPage(6))->GetObjNum());
-    CPDF_Dictionary* branch4 = CreatePageTreeNode(std::move(justSix), this, 1);
+    justSix->AppendNew<CPDF_Reference>(
+        this, AddIndirectObject(CreateNumberedPage(6)));
+    RetainPtr<CPDF_Dictionary> branch4 =
+        CreatePageTreeNode(std::move(justSix), this, 1);
 
     auto allPages = pdfium::MakeRetain<CPDF_Array>();
-    allPages->AddNew<CPDF_Reference>(this, branch2->GetObjNum());
-    allPages->AddNew<CPDF_Reference>(this, branch3->GetObjNum());
-    allPages->AddNew<CPDF_Reference>(this, branch4->GetObjNum());
-    CPDF_Dictionary* pagesDict =
+    allPages->AppendNew<CPDF_Reference>(this, branch2->GetObjNum());
+    allPages->AppendNew<CPDF_Reference>(this, branch3->GetObjNum());
+    allPages->AppendNew<CPDF_Reference>(this, branch4->GetObjNum());
+    RetainPtr<CPDF_Dictionary> pagesDict =
         CreatePageTreeNode(std::move(allPages), this, kNumTestPages);
 
-    m_pRootDict.Reset(NewIndirect<CPDF_Dictionary>());
-    m_pRootDict->SetNewFor<CPDF_Reference>("Pages", this,
-                                           pagesDict->GetObjNum());
-    m_PageList.resize(kNumTestPages);
+    SetRootForTesting(NewIndirect<CPDF_Dictionary>());
+    GetMutableRoot()->SetNewFor<CPDF_Reference>("Pages", this,
+                                                pagesDict->GetObjNum());
+    ResizePageListForTesting(kNumTestPages);
   }
 
   void SetTreeSize(int size) {
-    m_pRootDict->SetNewFor<CPDF_Number>("Count", size);
-    m_PageList.resize(size);
+    GetMutableRoot()->SetNewFor<CPDF_Number>("Count", size);
+    ResizePageListForTesting(size);
   }
 };
 
-class CPDF_TestDocumentWithPageWithoutPageNum final : public CPDF_Document {
+class CPDF_TestDocumentWithPageWithoutPageNum final : public CPDF_TestDocument {
  public:
-  CPDF_TestDocumentWithPageWithoutPageNum()
-      : CPDF_Document(pdfium::MakeUnique<CPDF_DocRenderData>(),
-                      pdfium::MakeUnique<CPDF_DocPageData>()) {
+  CPDF_TestDocumentWithPageWithoutPageNum() {
     // Set up test
     auto allPages = pdfium::MakeRetain<CPDF_Array>();
-    allPages->AddNew<CPDF_Reference>(
-        this, AddIndirectObject(CreateNumberedPage(0))->GetObjNum());
-    allPages->AddNew<CPDF_Reference>(
-        this, AddIndirectObject(CreateNumberedPage(1))->GetObjNum());
+    allPages->AppendNew<CPDF_Reference>(
+        this, AddIndirectObject(CreateNumberedPage(0)));
+    allPages->AppendNew<CPDF_Reference>(
+        this, AddIndirectObject(CreateNumberedPage(1)));
     // Page without pageNum.
-    inlined_page_ = allPages->Add(CreateNumberedPage(2));
-    CPDF_Dictionary* pagesDict =
+    inlined_page_ = CreateNumberedPage(2);
+    allPages->Append(inlined_page_);
+    RetainPtr<CPDF_Dictionary> pagesDict =
         CreatePageTreeNode(std::move(allPages), this, 3);
-    m_pRootDict.Reset(NewIndirect<CPDF_Dictionary>());
-    m_pRootDict->SetNewFor<CPDF_Reference>("Pages", this,
-                                           pagesDict->GetObjNum());
-    m_PageList.resize(3);
+    SetRootForTesting(NewIndirect<CPDF_Dictionary>());
+    GetMutableRoot()->SetNewFor<CPDF_Reference>("Pages", this,
+                                                pagesDict->GetObjNum());
+    ResizePageListForTesting(3);
   }
 
-  const CPDF_Object* inlined_page() const { return inlined_page_; }
+  const CPDF_Object* inlined_page() const { return inlined_page_.Get(); }
 
  private:
-  const CPDF_Object* inlined_page_;
+  RetainPtr<CPDF_Object> inlined_page_;
 };
 
 class TestLinearized final : public CPDF_LinearizedHeader {
@@ -135,80 +133,75 @@
       : CPDF_LinearizedHeader(dict, 0) {}
 };
 
-class CPDF_TestDocPagesWithoutKids final : public CPDF_Document {
+class CPDF_TestDocPagesWithoutKids final : public CPDF_TestDocument {
  public:
-  CPDF_TestDocPagesWithoutKids()
-      : CPDF_Document(pdfium::MakeUnique<CPDF_DocRenderData>(),
-                      pdfium::MakeUnique<CPDF_DocPageData>()) {
-    CPDF_Dictionary* pagesDict = NewIndirect<CPDF_Dictionary>();
+  CPDF_TestDocPagesWithoutKids() {
+    auto pagesDict = NewIndirect<CPDF_Dictionary>();
     pagesDict->SetNewFor<CPDF_Name>("Type", "Pages");
     pagesDict->SetNewFor<CPDF_Number>("Count", 3);
-    m_PageList.resize(10);
-    m_pRootDict.Reset(NewIndirect<CPDF_Dictionary>());
-    m_pRootDict->SetNewFor<CPDF_Reference>("Pages", this,
-                                           pagesDict->GetObjNum());
+    ResizePageListForTesting(10);
+    SetRootForTesting(NewIndirect<CPDF_Dictionary>());
+    GetMutableRoot()->SetNewFor<CPDF_Reference>("Pages", this,
+                                                pagesDict->GetObjNum());
   }
 };
 
-class CPDF_TestDocumentAllowSetParser final : public CPDF_Document {
+class CPDF_TestDocumentAllowSetParser final : public CPDF_TestDocument {
  public:
-  CPDF_TestDocumentAllowSetParser()
-      : CPDF_Document(pdfium::MakeUnique<CPDF_DocRenderData>(),
-                      pdfium::MakeUnique<CPDF_DocPageData>()) {}
+  CPDF_TestDocumentAllowSetParser() = default;
 
   using CPDF_Document::SetParser;
 };
 
 }  // namespace
 
-class cpdf_document_test : public testing::Test {
- public:
-  void SetUp() override { CPDF_PageModule::Create(); }
-  void TearDown() override { CPDF_PageModule::Destroy(); }
-};
+using DocumentTest = TestWithPageModule;
 
-TEST_F(cpdf_document_test, GetPages) {
+TEST_F(DocumentTest, GetPages) {
   std::unique_ptr<CPDF_TestDocumentForPages> document =
-      pdfium::MakeUnique<CPDF_TestDocumentForPages>();
+      std::make_unique<CPDF_TestDocumentForPages>();
   for (int i = 0; i < kNumTestPages; i++) {
-    CPDF_Dictionary* page = document->GetPageDictionary(i);
+    RetainPtr<const CPDF_Dictionary> page = document->GetPageDictionary(i);
     ASSERT_TRUE(page);
     ASSERT_TRUE(page->KeyExist("PageNumbering"));
     EXPECT_EQ(i, page->GetIntegerFor("PageNumbering"));
   }
-  CPDF_Dictionary* page = document->GetPageDictionary(kNumTestPages);
+  RetainPtr<const CPDF_Dictionary> page =
+      document->GetPageDictionary(kNumTestPages);
   EXPECT_FALSE(page);
 }
 
-TEST_F(cpdf_document_test, GetPageWithoutObjNumTwice) {
-  auto document = pdfium::MakeUnique<CPDF_TestDocumentWithPageWithoutPageNum>();
-  CPDF_Dictionary* page = document->GetPageDictionary(2);
+TEST_F(DocumentTest, GetPageWithoutObjNumTwice) {
+  auto document = std::make_unique<CPDF_TestDocumentWithPageWithoutPageNum>();
+  RetainPtr<const CPDF_Dictionary> page = document->GetPageDictionary(2);
   ASSERT_TRUE(page);
   ASSERT_EQ(document->inlined_page(), page);
 
-  CPDF_Dictionary* second_call_page = document->GetPageDictionary(2);
+  RetainPtr<const CPDF_Dictionary> second_call_page =
+      document->GetPageDictionary(2);
   EXPECT_TRUE(second_call_page);
   EXPECT_EQ(page, second_call_page);
 }
 
-TEST_F(cpdf_document_test, GetPagesReverseOrder) {
+TEST_F(DocumentTest, GetPagesReverseOrder) {
   std::unique_ptr<CPDF_TestDocumentForPages> document =
-      pdfium::MakeUnique<CPDF_TestDocumentForPages>();
+      std::make_unique<CPDF_TestDocumentForPages>();
   for (int i = 6; i >= 0; i--) {
-    CPDF_Dictionary* page = document->GetPageDictionary(i);
+    RetainPtr<const CPDF_Dictionary> page = document->GetPageDictionary(i);
     ASSERT_TRUE(page);
     ASSERT_TRUE(page->KeyExist("PageNumbering"));
     EXPECT_EQ(i, page->GetIntegerFor("PageNumbering"));
   }
-  CPDF_Dictionary* page = document->GetPageDictionary(kNumTestPages);
+  RetainPtr<const CPDF_Dictionary> page =
+      document->GetPageDictionary(kNumTestPages);
   EXPECT_FALSE(page);
 }
 
-TEST_F(cpdf_document_test, GetPagesInDisorder) {
+TEST_F(DocumentTest, GetPagesInDisorder) {
   std::unique_ptr<CPDF_TestDocumentForPages> document =
-      pdfium::MakeUnique<CPDF_TestDocumentForPages>();
+      std::make_unique<CPDF_TestDocumentForPages>();
 
-  CPDF_Dictionary* page = document->GetPageDictionary(1);
+  RetainPtr<const CPDF_Dictionary> page = document->GetPageDictionary(1);
   ASSERT_TRUE(page);
   ASSERT_TRUE(page->KeyExist("PageNumbering"));
   EXPECT_EQ(1, page->GetIntegerFor("PageNumbering"));
@@ -227,36 +220,72 @@
   EXPECT_EQ(6, page->GetIntegerFor("PageNumbering"));
 }
 
-TEST_F(cpdf_document_test, UseCachedPageObjNumIfHaveNotPagesDict) {
-  // ObjNum can be added in CPDF_DataAvail::IsPageAvail, and PagesDict
-  // can be not exists in this case.
-  // (case, when hint table is used to page check in CPDF_DataAvail).
-  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
-  dict->SetNewFor<CPDF_Boolean>("Linearized", true);
-  const int page_count = 100;
-  dict->SetNewFor<CPDF_Number>("N", page_count);
-  auto linearized = pdfium::MakeUnique<TestLinearized>(dict.Get());
-  auto parser = pdfium::MakeUnique<CPDF_Parser>();
-  parser->SetLinearizedHeader(std::move(linearized));
-  CPDF_TestDocumentAllowSetParser document;
-  document.SetParser(std::move(parser));
-  document.LoadPages();
-  ASSERT_EQ(page_count, document.GetPageCount());
-  CPDF_Object* page_stub = document.NewIndirect<CPDF_Dictionary>();
-  const uint32_t obj_num = page_stub->GetObjNum();
-  const int test_page_num = 33;
+TEST_F(DocumentTest, IsValidPageObject) {
+  CPDF_TestDocumentForPages document;
 
-  EXPECT_FALSE(document.IsPageLoaded(test_page_num));
-  EXPECT_EQ(nullptr, document.GetPageDictionary(test_page_num));
+  auto dict_type_name_page = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict_type_name_page->SetNewFor<CPDF_Name>("Type", "Page");
+  document.AddIndirectObject(dict_type_name_page);
+  EXPECT_TRUE(CPDF_Document::IsValidPageObject(dict_type_name_page.Get()));
 
-  document.SetPageObjNum(test_page_num, obj_num);
-  EXPECT_TRUE(document.IsPageLoaded(test_page_num));
-  EXPECT_EQ(page_stub, document.GetPageDictionary(test_page_num));
+  auto dict_type_string_page = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict_type_string_page->SetNewFor<CPDF_String>("Type", "Page", false);
+  document.AddIndirectObject(dict_type_string_page);
+  EXPECT_FALSE(CPDF_Document::IsValidPageObject(dict_type_string_page.Get()));
+
+  auto dict_type_name_font = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict_type_name_font->SetNewFor<CPDF_Name>("Type", "Font");
+  document.AddIndirectObject(dict_type_name_font);
+  EXPECT_FALSE(CPDF_Document::IsValidPageObject(dict_type_name_font.Get()));
+
+  auto obj_no_type = document.NewIndirect<CPDF_Dictionary>();
+  EXPECT_FALSE(CPDF_Document::IsValidPageObject(obj_no_type.Get()));
 }
 
-TEST_F(cpdf_document_test, CountGreaterThanPageTree) {
+TEST_F(DocumentTest, UseCachedPageObjNumIfHaveNotPagesDict) {
+  // ObjNum can be added in CPDF_DataAvail::IsPageAvail(), and PagesDict may not
+  // exist in this case, e.g. when hint table is used to page check in
+  // CPDF_DataAvail.
+  constexpr int kPageCount = 100;
+  constexpr int kTestPageNum = 33;
+
+  auto linearization_dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  CPDF_TestDocumentAllowSetParser document;
+
+  {
+    auto first_page = CreateNumberedPage(0);
+    ASSERT_TRUE(first_page);
+
+    int first_page_obj_num = document.AddIndirectObject(first_page);
+    ASSERT_NE(kTestPageNum, first_page_obj_num);
+
+    linearization_dict->SetNewFor<CPDF_Boolean>("Linearized", true);
+    linearization_dict->SetNewFor<CPDF_Number>("N", kPageCount);
+    linearization_dict->SetNewFor<CPDF_Number>("O", first_page_obj_num);
+
+    auto parser = std::make_unique<CPDF_Parser>();
+    parser->SetLinearizedHeaderForTesting(
+        std::make_unique<TestLinearized>(linearization_dict.Get()));
+    document.SetParser(std::move(parser));
+  }
+
+  document.LoadPages();
+
+  ASSERT_EQ(kPageCount, document.GetPageCount());
+  auto page_stub = document.NewIndirect<CPDF_Dictionary>();
+  const uint32_t obj_num = page_stub->GetObjNum();
+
+  EXPECT_FALSE(document.IsPageLoaded(kTestPageNum));
+  EXPECT_FALSE(document.GetPageDictionary(kTestPageNum));
+
+  document.SetPageObjNum(kTestPageNum, obj_num);
+  EXPECT_TRUE(document.IsPageLoaded(kTestPageNum));
+  EXPECT_EQ(page_stub, document.GetPageDictionary(kTestPageNum));
+}
+
+TEST_F(DocumentTest, CountGreaterThanPageTree) {
   std::unique_ptr<CPDF_TestDocumentForPages> document =
-      pdfium::MakeUnique<CPDF_TestDocumentForPages>();
+      std::make_unique<CPDF_TestDocumentForPages>();
   document->SetTreeSize(kNumTestPages + 3);
   for (int i = 0; i < kNumTestPages; i++)
     EXPECT_TRUE(document->GetPageDictionary(i));
@@ -265,9 +294,9 @@
   EXPECT_TRUE(document->GetPageDictionary(kNumTestPages - 1));
 }
 
-TEST_F(cpdf_document_test, PagesWithoutKids) {
+TEST_F(DocumentTest, PagesWithoutKids) {
   // Set up a document with Pages dict without kids, and Count = 3
-  auto pDoc = pdfium::MakeUnique<CPDF_TestDocPagesWithoutKids>();
+  auto pDoc = std::make_unique<CPDF_TestDocPagesWithoutKids>();
   EXPECT_TRUE(pDoc->GetPageDictionary(0));
   // Test GetPage does not fetch pages out of range
   for (int i = 1; i < 5; i++)
diff --git a/core/fpdfapi/parser/cpdf_encryptor.cpp b/core/fpdfapi/parser/cpdf_encryptor.cpp
index 706d668..e1804fc 100644
--- a/core/fpdfapi/parser/cpdf_encryptor.cpp
+++ b/core/fpdfapi/parser/cpdf_encryptor.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,20 +6,24 @@
 
 #include "core/fpdfapi/parser/cpdf_encryptor.h"
 
-#include "core/fpdfapi/parser/cpdf_crypto_handler.h"
+#include <stdint.h>
 
-CPDF_Encryptor::CPDF_Encryptor(CPDF_CryptoHandler* pHandler, int objnum)
+#include "core/fpdfapi/parser/cpdf_crypto_handler.h"
+#include "core/fxcrt/data_vector.h"
+#include "third_party/base/check.h"
+
+CPDF_Encryptor::CPDF_Encryptor(const CPDF_CryptoHandler* pHandler, int objnum)
     : m_pHandler(pHandler), m_ObjNum(objnum) {
-  ASSERT(m_pHandler);
+  DCHECK(m_pHandler);
 }
 
-std::vector<uint8_t> CPDF_Encryptor::Encrypt(
+DataVector<uint8_t> CPDF_Encryptor::Encrypt(
     pdfium::span<const uint8_t> src_data) const {
   if (src_data.empty())
-    return std::vector<uint8_t>();
+    return DataVector<uint8_t>();
 
-  std::vector<uint8_t> result;
-  uint32_t buf_size = m_pHandler->EncryptGetSize(src_data);
+  DataVector<uint8_t> result;
+  size_t buf_size = m_pHandler->EncryptGetSize(src_data);
   result.resize(buf_size);
   m_pHandler->EncryptContent(m_ObjNum, 0, src_data, result.data(),
                              buf_size);  // Updates |buf_size| with actual.
@@ -27,4 +31,4 @@
   return result;
 }
 
-CPDF_Encryptor::~CPDF_Encryptor() {}
+CPDF_Encryptor::~CPDF_Encryptor() = default;
diff --git a/core/fpdfapi/parser/cpdf_encryptor.h b/core/fpdfapi/parser/cpdf_encryptor.h
index cea737a..d0aabcc 100644
--- a/core/fpdfapi/parser/cpdf_encryptor.h
+++ b/core/fpdfapi/parser/cpdf_encryptor.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,22 +9,21 @@
 
 #include <stdint.h>
 
-#include <vector>
-
+#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;
 
 class CPDF_Encryptor {
  public:
-  CPDF_Encryptor(CPDF_CryptoHandler* pHandler, int objnum);
+  CPDF_Encryptor(const CPDF_CryptoHandler* pHandler, int objnum);
   ~CPDF_Encryptor();
 
-  std::vector<uint8_t> Encrypt(pdfium::span<const uint8_t> src_data) const;
+  DataVector<uint8_t> Encrypt(pdfium::span<const uint8_t> src_data) const;
 
  private:
-  UnownedPtr<CPDF_CryptoHandler> const m_pHandler;
+  UnownedPtr<const CPDF_CryptoHandler> const m_pHandler;
   const int m_ObjNum;
 };
 
diff --git a/core/fpdfapi/parser/cpdf_flateencoder.cpp b/core/fpdfapi/parser/cpdf_flateencoder.cpp
index b685822..64acd72 100644
--- a/core/fpdfapi/parser/cpdf_flateencoder.cpp
+++ b/core/fpdfapi/parser/cpdf_flateencoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,6 @@
 
 #include "core/fpdfapi/parser/cpdf_flateencoder.h"
 
-#include <memory>
-#include <utility>
-
 #include "constants/stream_dict_common.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
@@ -16,10 +13,12 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "third_party/base/check.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
-CPDF_FlateEncoder::CPDF_FlateEncoder(const CPDF_Stream* pStream,
+CPDF_FlateEncoder::CPDF_FlateEncoder(RetainPtr<const CPDF_Stream> pStream,
                                      bool bFlateEncode)
-    : m_pAcc(pdfium::MakeRetain<CPDF_StreamAcc>(pStream)), m_dwSize(0) {
+    : m_pAcc(pdfium::MakeRetain<CPDF_StreamAcc>(pStream)) {
   m_pAcc->LoadAllDataRaw();
 
   bool bHasFilter = pStream->HasFilter();
@@ -27,56 +26,59 @@
     auto pDestAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
     pDestAcc->LoadAllDataFiltered();
 
-    m_dwSize = pDestAcc->GetSize();
-    m_pData = pDestAcc->DetachData();
+    m_Data = m_pAcc->GetSpan();
     m_pClonedDict = ToDictionary(pStream->GetDict()->Clone());
     m_pClonedDict->RemoveFor("Filter");
-    ASSERT(!m_pDict);
+    DCHECK(!m_pDict);
     return;
   }
   if (bHasFilter || !bFlateEncode) {
-    m_pData = m_pAcc->GetData();
-    m_dwSize = m_pAcc->GetSize();
-    m_pDict.Reset(pStream->GetDict());
-    ASSERT(!m_pClonedDict);
+    m_Data = m_pAcc->GetSpan();
+    m_pDict = pStream->GetDict();
+    DCHECK(!m_pClonedDict);
     return;
   }
 
-  // TODO(thestig): Move to Init() and check return value.
-  std::unique_ptr<uint8_t, FxFreeDeleter> buffer;
-  ::FlateEncode(m_pAcc->GetSpan(), &buffer, &m_dwSize);
-
-  m_pData = std::move(buffer);
+  // TODO(thestig): Move to Init() and check for empty return value?
+  m_Data = ::FlateEncode(m_pAcc->GetSpan());
   m_pClonedDict = ToDictionary(pStream->GetDict()->Clone());
-  m_pClonedDict->SetNewFor<CPDF_Number>("Length", static_cast<int>(m_dwSize));
+  m_pClonedDict->SetNewFor<CPDF_Number>(
+      "Length", pdfium::base::checked_cast<int>(GetSpan().size()));
   m_pClonedDict->SetNewFor<CPDF_Name>("Filter", "FlateDecode");
   m_pClonedDict->RemoveFor(pdfium::stream::kDecodeParms);
-  ASSERT(!m_pDict);
+  DCHECK(!m_pDict);
 }
 
-CPDF_FlateEncoder::~CPDF_FlateEncoder() {}
+CPDF_FlateEncoder::~CPDF_FlateEncoder() = default;
 
-void CPDF_FlateEncoder::CloneDict() {
-  if (m_pClonedDict) {
-    ASSERT(!m_pDict);
+void CPDF_FlateEncoder::UpdateLength(size_t size) {
+  if (static_cast<size_t>(GetDict()->GetIntegerFor("Length")) == size)
     return;
-  }
 
-  m_pClonedDict = ToDictionary(m_pDict->Clone());
-  ASSERT(m_pClonedDict);
-  m_pDict.Reset();
+  if (!m_pClonedDict) {
+    m_pClonedDict = ToDictionary(m_pDict->Clone());
+    m_pDict.Reset();
+  }
+  DCHECK(m_pClonedDict);
+  DCHECK(!m_pDict);
+  m_pClonedDict->SetNewFor<CPDF_Number>("Length", static_cast<int>(size));
 }
 
-CPDF_Dictionary* CPDF_FlateEncoder::GetClonedDict() {
-  ASSERT(!m_pDict);
-  return m_pClonedDict.Get();
+bool CPDF_FlateEncoder::WriteDictTo(IFX_ArchiveStream* archive,
+                                    const CPDF_Encryptor* encryptor) const {
+  return GetDict()->WriteTo(archive, encryptor);
 }
 
 const CPDF_Dictionary* CPDF_FlateEncoder::GetDict() const {
   if (m_pClonedDict) {
-    ASSERT(!m_pDict);
+    DCHECK(!m_pDict);
     return m_pClonedDict.Get();
   }
-
   return m_pDict.Get();
 }
+
+pdfium::span<const uint8_t> CPDF_FlateEncoder::GetSpan() const {
+  if (is_owned())
+    return absl::get<DataVector<uint8_t>>(m_Data);
+  return absl::get<pdfium::span<const uint8_t>>(m_Data);
+}
diff --git a/core/fpdfapi/parser/cpdf_flateencoder.h b/core/fpdfapi/parser/cpdf_flateencoder.h
index df0db82..a9b37f7 100644
--- a/core/fpdfapi/parser/cpdf_flateencoder.h
+++ b/core/fpdfapi/parser/cpdf_flateencoder.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,37 +7,42 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_FLATEENCODER_H_
 #define CORE_FPDFAPI_PARSER_CPDF_FLATEENCODER_H_
 
-#include <memory>
+#include <stdint.h>
 
-#include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxcrt/maybe_owned.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Dictionary;
+class CPDF_Encryptor;
 class CPDF_Stream;
 class CPDF_StreamAcc;
+class IFX_ArchiveStream;
 
 class CPDF_FlateEncoder {
  public:
-  CPDF_FlateEncoder(const CPDF_Stream* pStream, bool bFlateEncode);
+  CPDF_FlateEncoder(RetainPtr<const CPDF_Stream> pStream, bool bFlateEncode);
   ~CPDF_FlateEncoder();
 
-  void CloneDict();
-  CPDF_Dictionary* GetClonedDict();
+  void UpdateLength(size_t size);
+  bool WriteDictTo(IFX_ArchiveStream* archive,
+                   const CPDF_Encryptor* encryptor) const;
+
+  pdfium::span<const uint8_t> GetSpan() const;
+
+ private:
+  bool is_owned() const {
+    return absl::holds_alternative<DataVector<uint8_t>>(m_Data);
+  }
 
   // Returns |m_pClonedDict| if it is valid. Otherwise returns |m_pDict|.
   const CPDF_Dictionary* GetDict() const;
 
-  pdfium::span<const uint8_t> GetSpan() const {
-    return pdfium::make_span(m_pData.Get(), m_dwSize);
-  }
+  // Must outlive `m_Data`.
+  RetainPtr<CPDF_StreamAcc> const m_pAcc;
 
- private:
-  RetainPtr<CPDF_StreamAcc> m_pAcc;
-
-  uint32_t m_dwSize;
-  MaybeOwned<uint8_t, FxFreeDeleter> m_pData;
+  absl::variant<pdfium::span<const uint8_t>, DataVector<uint8_t>> m_Data;
 
   // Only one of these two pointers is valid at any time.
   RetainPtr<const CPDF_Dictionary> m_pDict;
diff --git a/core/fpdfapi/parser/cpdf_hint_tables.cpp b/core/fpdfapi/parser/cpdf_hint_tables.cpp
index e051254..5870d01 100644
--- a/core/fpdfapi/parser/cpdf_hint_tables.cpp
+++ b/core/fpdfapi/parser/cpdf_hint_tables.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,15 +13,16 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_linearized_header.h"
+#include "core/fpdfapi/parser/cpdf_parser.h"
 #include "core/fpdfapi/parser/cpdf_read_validator.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
 #include "core/fxcrt/cfx_bitstream.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/span.h"
 
 namespace {
 
@@ -45,8 +46,8 @@
 //  static
 std::unique_ptr<CPDF_HintTables> CPDF_HintTables::Parse(
     CPDF_SyntaxParser* parser,
-    CPDF_LinearizedHeader* pLinearized) {
-  ASSERT(parser);
+    const CPDF_LinearizedHeader* pLinearized) {
+  DCHECK(parser);
   if (!pLinearized || pLinearized->GetPageCount() <= 1 ||
       !pLinearized->HasHintTable()) {
     return nullptr;
@@ -67,7 +68,7 @@
   if (!hints_stream)
     return nullptr;
 
-  auto pHintTables = pdfium::MakeUnique<CPDF_HintTables>(
+  auto pHintTables = std::make_unique<CPDF_HintTables>(
       parser->GetValidator().Get(), pLinearized);
   if (!pHintTables->LoadHintStream(hints_stream.Get()))
     return nullptr;
@@ -76,15 +77,12 @@
 }
 
 CPDF_HintTables::CPDF_HintTables(CPDF_ReadValidator* pValidator,
-                                 CPDF_LinearizedHeader* pLinearized)
-    : m_pValidator(pValidator),
-      m_pLinearized(pLinearized),
-      m_nFirstPageSharedObjs(0),
-      m_szFirstPageObjOffset(0) {
-  ASSERT(m_pLinearized);
+                                 const CPDF_LinearizedHeader* pLinearized)
+    : m_pValidator(pValidator), m_pLinearized(pLinearized) {
+  DCHECK(m_pLinearized);
 }
 
-CPDF_HintTables::~CPDF_HintTables() {}
+CPDF_HintTables::~CPDF_HintTables() = default;
 
 bool CPDF_HintTables::ReadPageHintTable(CFX_BitStream* hStream) {
   const uint32_t nPages = m_pLinearized->GetPageCount();
@@ -104,7 +102,7 @@
 
   // Item 1: The least number of objects in a page.
   const uint32_t dwObjLeastNum = hStream->GetBits(32);
-  if (!dwObjLeastNum)
+  if (!dwObjLeastNum || dwObjLeastNum >= CPDF_Parser::kMaxObjectNumber)
     return false;
 
   // Item 2: The location of the first page's page object.
@@ -167,7 +165,7 @@
   m_PageInfos[nFirstPageNum].set_start_obj_num(
       m_pLinearized->GetFirstPageObjNum());
   // The object number of remaining pages starts from 1.
-  uint32_t dwStartObjNum = 1;
+  FX_SAFE_UINT32 dwStartObjNum = 1;
   for (uint32_t i = 0; i < nPages; ++i) {
     FX_SAFE_UINT32 safeDeltaObj = hStream->GetBits(dwDeltaObjectsBits);
     safeDeltaObj += dwObjLeastNum;
@@ -176,8 +174,12 @@
     m_PageInfos[i].set_objects_count(safeDeltaObj.ValueOrDie());
     if (i == nFirstPageNum)
       continue;
-    m_PageInfos[i].set_start_obj_num(dwStartObjNum);
+    m_PageInfos[i].set_start_obj_num(dwStartObjNum.ValueOrDie());
     dwStartObjNum += m_PageInfos[i].objects_count();
+    if (!dwStartObjNum.IsValid() ||
+        dwStartObjNum.ValueOrDie() >= CPDF_Parser::kMaxObjectNumber) {
+      return false;
+    }
   }
   hStream->ByteAlign();
 
@@ -194,7 +196,7 @@
     m_PageInfos[i].set_page_length(safePageLen.ValueOrDie());
   }
 
-  ASSERT(m_szFirstPageObjOffset);
+  DCHECK(m_szFirstPageObjOffset);
   m_PageInfos[nFirstPageNum].set_page_offset(m_szFirstPageObjOffset);
   FX_FILESIZE prev_page_end = m_pLinearized->GetFirstPageEndOffset();
   for (uint32_t i = 0; i < nPages; ++i) {
@@ -407,18 +409,18 @@
 
 CPDF_DataAvail::DocAvailStatus CPDF_HintTables::CheckPage(uint32_t index) {
   if (index == m_pLinearized->GetFirstPageNo())
-    return CPDF_DataAvail::DataAvailable;
+    return CPDF_DataAvail::kDataAvailable;
 
   if (index >= m_pLinearized->GetPageCount())
-    return CPDF_DataAvail::DataError;
+    return CPDF_DataAvail::kDataError;
 
   const uint32_t dwLength = m_PageInfos[index].page_length();
   if (!dwLength)
-    return CPDF_DataAvail::DataError;
+    return CPDF_DataAvail::kDataError;
 
   if (!m_pValidator->CheckDataRangeAndRequestIfUnavailable(
           m_PageInfos[index].page_offset(), dwLength)) {
-    return CPDF_DataAvail::DataNotAvailable;
+    return CPDF_DataAvail::kDataNotAvailable;
   }
 
   // Download data of shared objects in the page.
@@ -429,22 +431,25 @@
         m_SharedObjGroupInfos[dwIndex];
 
     if (!shared_group_info.m_szOffset || !shared_group_info.m_dwLength)
-      return CPDF_DataAvail::DataError;
+      return CPDF_DataAvail::kDataError;
 
     if (!m_pValidator->CheckDataRangeAndRequestIfUnavailable(
             shared_group_info.m_szOffset, shared_group_info.m_dwLength)) {
-      return CPDF_DataAvail::DataNotAvailable;
+      return CPDF_DataAvail::kDataNotAvailable;
     }
   }
-  return CPDF_DataAvail::DataAvailable;
+  return CPDF_DataAvail::kDataAvailable;
 }
 
 bool CPDF_HintTables::LoadHintStream(CPDF_Stream* pHintStream) {
   if (!pHintStream || !m_pLinearized->HasHintTable())
     return false;
 
-  CPDF_Dictionary* pDict = pHintStream->GetDict();
-  CPDF_Object* pOffset = pDict ? pDict->GetObjectFor("S") : nullptr;
+  RetainPtr<const CPDF_Dictionary> pDict = pHintStream->GetDict();
+  if (!pDict)
+    return false;
+
+  RetainPtr<const CPDF_Object> pOffset = pDict->GetObjectFor("S");
   if (!pOffset || !pOffset->IsNumber())
     return false;
 
@@ -452,7 +457,8 @@
   if (shared_hint_table_offset <= 0)
     return false;
 
-  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pHintStream);
+  auto pAcc =
+      pdfium::MakeRetain<CPDF_StreamAcc>(pdfium::WrapRetain(pHintStream));
   pAcc->LoadAllDataFiltered();
 
   uint32_t size = pAcc->GetSize();
@@ -484,7 +490,7 @@
   // itself were not present. That is, a position greater than the hint stream
   // offset shall have the hint stream length added to it to determine the
   // actual offset relative to the beginning of the file.
-  // See specification PDF 32000-1:2008 Annex F.4 (Hint tables).
+  // See ISO 32000-1:2008 spec, annex F.4 (Hint tables).
   // Note: The PDF spec does not mention this, but positions equal to the hint
   // stream offset also need to have the hint stream length added to it. e.g.
   // There exists linearized PDFs generated by Adobe software that have this
diff --git a/core/fpdfapi/parser/cpdf_hint_tables.h b/core/fpdfapi/parser/cpdf_hint_tables.h
index e3f280f..072d6e6 100644
--- a/core/fpdfapi/parser/cpdf_hint_tables.h
+++ b/core/fpdfapi/parser/cpdf_hint_tables.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -70,10 +70,10 @@
 
   static std::unique_ptr<CPDF_HintTables> Parse(
       CPDF_SyntaxParser* parser,
-      CPDF_LinearizedHeader* pLinearized);
+      const CPDF_LinearizedHeader* pLinearized);
 
   CPDF_HintTables(CPDF_ReadValidator* pValidator,
-                  CPDF_LinearizedHeader* pLinearized);
+                  const CPDF_LinearizedHeader* pLinearized);
   virtual ~CPDF_HintTables();
 
   bool GetPagePos(uint32_t index,
@@ -99,17 +99,12 @@
  private:
   FX_FILESIZE HintsOffsetToFileOffset(uint32_t hints_offset) const;
 
-  // Owned by |m_pDataAvail|.
-  UnownedPtr<CPDF_ReadValidator> m_pValidator;
-
-  // Owned by |m_pDataAvail|.
-  UnownedPtr<CPDF_LinearizedHeader> const m_pLinearized;
-
-  uint32_t m_nFirstPageSharedObjs;
-  FX_FILESIZE m_szFirstPageObjOffset;
-
+  uint32_t m_nFirstPageSharedObjs = 0;
+  FX_FILESIZE m_szFirstPageObjOffset = 0;
   std::vector<PageInfo> m_PageInfos;
   std::vector<SharedObjGroupInfo> m_SharedObjGroupInfos;
+  UnownedPtr<CPDF_ReadValidator> m_pValidator;
+  UnownedPtr<const CPDF_LinearizedHeader> const m_pLinearized;
 };
 
 #endif  // CORE_FPDFAPI_PARSER_CPDF_HINT_TABLES_H_
diff --git a/core/fpdfapi/parser/cpdf_hint_tables_unittest.cpp b/core/fpdfapi/parser/cpdf_hint_tables_unittest.cpp
index 27592ba..c61afa1 100644
--- a/core/fpdfapi/parser/cpdf_hint_tables_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_hint_tables_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,7 @@
 #include <string>
 #include <utility>
 
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fpdfapi/page/test_with_page_module.h"
 #include "core/fpdfapi/parser/cpdf_data_avail.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_linearized_header.h"
@@ -16,12 +16,13 @@
 #include "core/fpdfapi/parser/cpdf_read_validator.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/cfx_read_only_string_stream.h"
 #include "core/fxcrt/fx_stream.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/utils/path_service.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -29,15 +30,15 @@
     const std::string& file_name) {
   std::string file_path;
   PathService::GetTestFilePath(file_name, &file_path);
-  ASSERT(!file_path.empty());
+  DCHECK(!file_path.empty());
   return pdfium::MakeRetain<CPDF_ReadValidator>(
       IFX_SeekableReadStream::CreateFromFilename(file_path.c_str()), nullptr);
 }
 
 std::unique_ptr<CPDF_DataAvail> MakeDataAvailFromFile(
     const std::string& file_name) {
-  return pdfium::MakeUnique<CPDF_DataAvail>(
-      nullptr, MakeValidatorFromFile(file_name), true);
+  return std::make_unique<CPDF_DataAvail>(nullptr,
+                                          MakeValidatorFromFile(file_name));
 }
 
 class TestLinearizedHeader final : public CPDF_LinearizedHeader {
@@ -47,36 +48,28 @@
       : CPDF_LinearizedHeader(pDict, szLastXRefOffset) {}
 
   static std::unique_ptr<CPDF_LinearizedHeader> MakeHeader(
-      const std::string& inline_data) {
-    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-        pdfium::as_bytes(pdfium::make_span(inline_data))));
+      ByteString inline_data) {
+    CPDF_SyntaxParser parser(
+        pdfium::MakeRetain<CFX_ReadOnlyStringStream>(std::move(inline_data)));
     RetainPtr<CPDF_Dictionary> dict =
         ToDictionary(parser.GetObjectBody(nullptr));
-    ASSERT(dict);
-    return pdfium::MakeUnique<TestLinearizedHeader>(dict.Get(), 0);
+    DCHECK(dict);
+    return std::make_unique<TestLinearizedHeader>(dict.Get(), 0);
   }
 };
 
 }  // namespace
 
-class CPDF_HintTablesTest : public testing::Test {
- public:
-  CPDF_HintTablesTest() {
-    // Needs for encoding Hint table stream.
-    CPDF_PageModule::Create();
-  }
+// Needs page module for encoding Hint table stream.
+using HintTablesTest = TestWithPageModule;
 
-  ~CPDF_HintTablesTest() override { CPDF_PageModule::Destroy(); }
-};
-
-TEST_F(CPDF_HintTablesTest, Load) {
+TEST_F(HintTablesTest, Load) {
   auto data_avail = MakeDataAvailFromFile("feature_linearized_loading.pdf");
-  ASSERT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable,
-            data_avail->IsDocAvail(nullptr));
+  ASSERT_EQ(CPDF_DataAvail::kDataAvailable, data_avail->IsDocAvail(nullptr));
 
-  ASSERT_TRUE(data_avail->GetHintTables());
+  ASSERT_TRUE(data_avail->GetHintTablesForTest());
 
-  const CPDF_HintTables* hint_tables = data_avail->GetHintTables();
+  const CPDF_HintTables* hint_tables = data_avail->GetHintTablesForTest();
   FX_FILESIZE page_start = 0;
   FX_FILESIZE page_length = 0;
   uint32_t page_obj_num = 0;
@@ -97,12 +90,11 @@
       hint_tables->GetPagePos(2, &page_start, &page_length, &page_obj_num));
 }
 
-TEST_F(CPDF_HintTablesTest, PageAndGroupInfos) {
+TEST_F(HintTablesTest, PageAndGroupInfos) {
   auto data_avail = MakeDataAvailFromFile("feature_linearized_loading.pdf");
-  ASSERT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable,
-            data_avail->IsDocAvail(nullptr));
+  ASSERT_EQ(CPDF_DataAvail::kDataAvailable, data_avail->IsDocAvail(nullptr));
 
-  const CPDF_HintTables* hint_tables = data_avail->GetHintTables();
+  const CPDF_HintTables* hint_tables = data_avail->GetHintTablesForTest();
   ASSERT_TRUE(hint_tables);
   ASSERT_EQ(2u, hint_tables->PageInfos().size());
 
@@ -159,7 +151,7 @@
   EXPECT_EQ(1u, hint_tables->SharedGroupInfos()[5].m_dwObjectsCount);
 }
 
-TEST_F(CPDF_HintTablesTest, FirstPageOffset) {
+TEST_F(HintTablesTest, FirstPageOffset) {
   // Test that valid hint table is loaded, and have correct offset of first page
   // object.
   const auto linearized_header = TestLinearizedHeader::MakeHeader(
@@ -172,8 +164,8 @@
   CPDF_SyntaxParser parser(validator, 0);
   RetainPtr<CPDF_Stream> stream = ToStream(parser.GetObjectBody(nullptr));
   ASSERT_TRUE(stream);
-  auto hint_tables = pdfium::MakeUnique<CPDF_HintTables>(
-      validator.Get(), linearized_header.get());
+  auto hint_tables = std::make_unique<CPDF_HintTables>(validator.Get(),
+                                                       linearized_header.get());
   // Check that hint table will load.
   ASSERT_TRUE(hint_tables->LoadHintStream(stream.Get()));
   // Check that hint table have correct first page offset.
diff --git a/core/fpdfapi/parser/cpdf_indirect_object_holder.cpp b/core/fpdfapi/parser/cpdf_indirect_object_holder.cpp
index b2e1b54..d4ff401 100644
--- a/core/fpdfapi/parser/cpdf_indirect_object_holder.cpp
+++ b/core/fpdfapi/parser/cpdf_indirect_object_holder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,46 +7,64 @@
 #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
 
 #include <algorithm>
+#include <memory>
 #include <utility>
 
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fpdfapi/parser/cpdf_parser.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 namespace {
 
-CPDF_Object* FilterInvalidObjNum(CPDF_Object* obj) {
+const CPDF_Object* FilterInvalidObjNum(const CPDF_Object* obj) {
   return obj && obj->GetObjNum() != CPDF_Object::kInvalidObjNum ? obj : nullptr;
 }
 
 }  // namespace
 
 CPDF_IndirectObjectHolder::CPDF_IndirectObjectHolder()
-    : m_LastObjNum(0),
-      m_pByteStringPool(pdfium::MakeUnique<ByteStringPool>()) {}
+    : m_pByteStringPool(std::make_unique<ByteStringPool>()) {}
 
 CPDF_IndirectObjectHolder::~CPDF_IndirectObjectHolder() {
   m_pByteStringPool.DeleteObject();  // Make weak.
 }
 
-CPDF_Object* CPDF_IndirectObjectHolder::GetIndirectObject(
+RetainPtr<const CPDF_Object> CPDF_IndirectObjectHolder::GetIndirectObject(
     uint32_t objnum) const {
-  auto it = m_IndirectObjs.find(objnum);
-  return (it != m_IndirectObjs.end()) ? FilterInvalidObjNum(it->second.Get())
-                                      : nullptr;
+  return pdfium::WrapRetain(GetIndirectObjectInternal(objnum));
 }
 
-CPDF_Object* CPDF_IndirectObjectHolder::GetOrParseIndirectObject(
+RetainPtr<CPDF_Object> CPDF_IndirectObjectHolder::GetMutableIndirectObject(
+    uint32_t objnum) {
+  return pdfium::WrapRetain(
+      const_cast<CPDF_Object*>(GetIndirectObjectInternal(objnum)));
+}
+
+const CPDF_Object* CPDF_IndirectObjectHolder::GetIndirectObjectInternal(
+    uint32_t objnum) const {
+  auto it = m_IndirectObjs.find(objnum);
+  if (it == m_IndirectObjs.end())
+    return nullptr;
+
+  return FilterInvalidObjNum(it->second.Get());
+}
+
+RetainPtr<CPDF_Object> CPDF_IndirectObjectHolder::GetOrParseIndirectObject(
+    uint32_t objnum) {
+  return pdfium::WrapRetain(GetOrParseIndirectObjectInternal(objnum));
+}
+
+CPDF_Object* CPDF_IndirectObjectHolder::GetOrParseIndirectObjectInternal(
     uint32_t objnum) {
   if (objnum == 0 || objnum == CPDF_Object::kInvalidObjNum)
     return nullptr;
 
   // Add item anyway to prevent recursively parsing of same object.
   auto insert_result = m_IndirectObjs.insert(std::make_pair(objnum, nullptr));
-  if (!insert_result.second)
-    return FilterInvalidObjNum(insert_result.first->second.Get());
-
+  if (!insert_result.second) {
+    return const_cast<CPDF_Object*>(
+        FilterInvalidObjNum(insert_result.first->second.Get()));
+  }
   RetainPtr<CPDF_Object> pNewObj = ParseIndirectObject(objnum);
   if (!pNewObj) {
     m_IndirectObjs.erase(insert_result.first);
@@ -55,8 +73,10 @@
 
   pNewObj->SetObjNum(objnum);
   m_LastObjNum = std::max(m_LastObjNum, objnum);
+
+  CPDF_Object* result = pNewObj.Get();
   insert_result.first->second = std::move(pNewObj);
-  return insert_result.first->second.Get();
+  return result;
 }
 
 RetainPtr<CPDF_Object> CPDF_IndirectObjectHolder::ParseIndirectObject(
@@ -64,20 +84,18 @@
   return nullptr;
 }
 
-CPDF_Object* CPDF_IndirectObjectHolder::AddIndirectObject(
+uint32_t CPDF_IndirectObjectHolder::AddIndirectObject(
     RetainPtr<CPDF_Object> pObj) {
   CHECK(!pObj->GetObjNum());
   pObj->SetObjNum(++m_LastObjNum);
-
-  auto& obj_holder = m_IndirectObjs[m_LastObjNum];
-  obj_holder = std::move(pObj);
-  return obj_holder.Get();
+  m_IndirectObjs[m_LastObjNum] = std::move(pObj);
+  return m_LastObjNum;
 }
 
 bool CPDF_IndirectObjectHolder::ReplaceIndirectObjectIfHigherGeneration(
     uint32_t objnum,
     RetainPtr<CPDF_Object> pObj) {
-  ASSERT(objnum);
+  DCHECK(objnum);
   if (!pObj || objnum == CPDF_Object::kInvalidObjNum)
     return false;
 
diff --git a/core/fpdfapi/parser/cpdf_indirect_object_holder.h b/core/fpdfapi/parser/cpdf_indirect_object_holder.h
index 1887cc8..cdf0bc8 100644
--- a/core/fpdfapi/parser/cpdf_indirect_object_holder.h
+++ b/core/fpdfapi/parser/cpdf_indirect_object_holder.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,17 +7,16 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_INDIRECT_OBJECT_HOLDER_H_
 #define CORE_FPDFAPI_PARSER_CPDF_INDIRECT_OBJECT_HOLDER_H_
 
+#include <stdint.h>
+
 #include <map>
-#include <memory>
 #include <type_traits>
 #include <utility>
-#include <vector>
 
 #include "core/fpdfapi/parser/cpdf_object.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/string_pool_template.h"
 #include "core/fxcrt/weak_ptr.h"
-#include "third_party/base/ptr_util.h"
 
 class CPDF_IndirectObjectHolder {
  public:
@@ -27,39 +26,39 @@
   CPDF_IndirectObjectHolder();
   virtual ~CPDF_IndirectObjectHolder();
 
-  CPDF_Object* GetIndirectObject(uint32_t objnum) const;
-  virtual CPDF_Object* GetOrParseIndirectObject(uint32_t objnum);
+  RetainPtr<CPDF_Object> GetOrParseIndirectObject(uint32_t objnum);
+  RetainPtr<const CPDF_Object> GetIndirectObject(uint32_t objnum) const;
+  RetainPtr<CPDF_Object> GetMutableIndirectObject(uint32_t objnum);
   void DeleteIndirectObject(uint32_t objnum);
 
-  // Creates and adds a new object owned by the indirect object holder,
-  // and returns an unowned pointer to it.  We have a special case to
-  // handle objects that can intern strings from our ByteStringPool.
+  // Creates and adds a new object retained by the indirect object holder,
+  // and returns a retained pointer to it.
   template <typename T, typename... Args>
-  typename std::enable_if<!CanInternStrings<T>::value, T*>::type NewIndirect(
-      Args&&... args) {
-    return static_cast<T*>(
-        AddIndirectObject(pdfium::MakeRetain<T>(std::forward<Args>(args)...)));
-  }
-  template <typename T, typename... Args>
-  typename std::enable_if<CanInternStrings<T>::value, T*>::type NewIndirect(
-      Args&&... args) {
-    return static_cast<T*>(AddIndirectObject(
-        pdfium::MakeRetain<T>(m_pByteStringPool, std::forward<Args>(args)...)));
+  RetainPtr<T> NewIndirect(Args&&... args) {
+    auto obj = New<T>(std::forward<Args>(args)...);
+    AddIndirectObject(obj);
+    return obj;
   }
 
-  // Creates and adds a new object not owned by the indirect object holder,
-  // but which can intern strings from it.
+  // Creates and adds a new object not retained by the indirect object holder,
+  // but which can intern strings from it. We have a special cast to handle
+  // objects that can intern strings from our ByteStringPool.
   template <typename T, typename... Args>
   typename std::enable_if<CanInternStrings<T>::value, RetainPtr<T>>::type New(
       Args&&... args) {
     return pdfium::MakeRetain<T>(m_pByteStringPool,
                                  std::forward<Args>(args)...);
   }
+  template <typename T, typename... Args>
+  typename std::enable_if<!CanInternStrings<T>::value, RetainPtr<T>>::type New(
+      Args&&... args) {
+    return pdfium::MakeRetain<T>(std::forward<Args>(args)...);
+  }
 
-  // Takes ownership of |pObj|, returns unowned pointer to it.
-  CPDF_Object* AddIndirectObject(RetainPtr<CPDF_Object> pObj);
+  // Always Retains |pObj|, returns its new object number.
+  uint32_t AddIndirectObject(RetainPtr<CPDF_Object> pObj);
 
-  // Always takes ownership of |pObj|, return true if higher generation number.
+  // If higher generation number, retains |pObj| and returns true.
   bool ReplaceIndirectObjectIfHigherGeneration(uint32_t objnum,
                                                RetainPtr<CPDF_Object> pObj);
 
@@ -77,7 +76,12 @@
   virtual RetainPtr<CPDF_Object> ParseIndirectObject(uint32_t objnum);
 
  private:
-  uint32_t m_LastObjNum;
+  friend class CPDF_Reference;
+
+  const CPDF_Object* GetIndirectObjectInternal(uint32_t objnum) const;
+  CPDF_Object* GetOrParseIndirectObjectInternal(uint32_t objnum);
+
+  uint32_t m_LastObjNum = 0;
   std::map<uint32_t, RetainPtr<CPDF_Object>> m_IndirectObjs;
   WeakPtr<ByteStringPool> m_pByteStringPool;
 };
diff --git a/core/fpdfapi/parser/cpdf_indirect_object_holder_unittest.cpp b/core/fpdfapi/parser/cpdf_indirect_object_holder_unittest.cpp
index 5494855..94e9b38 100644
--- a/core/fpdfapi/parser/cpdf_indirect_object_holder_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_indirect_object_holder_unittest.cpp
@@ -1,37 +1,36 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
 
-#include <memory>
-#include <utility>
-
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_null.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 namespace {
 
 class MockIndirectObjectHolder final : public CPDF_IndirectObjectHolder {
  public:
-  MockIndirectObjectHolder() {}
-  ~MockIndirectObjectHolder() override {}
+  MockIndirectObjectHolder() = default;
+  ~MockIndirectObjectHolder() override = default;
 
   MOCK_METHOD1(ParseIndirectObject, RetainPtr<CPDF_Object>(uint32_t objnum));
 };
 
 }  // namespace
 
-TEST(CPDF_IndirectObjectHolderTest, RecursiveParseOfSameObject) {
+TEST(IndirectObjectHolderTest, RecursiveParseOfSameObject) {
   MockIndirectObjectHolder mock_holder;
   // ParseIndirectObject should not be called again on recursively same object
   // parse request.
   EXPECT_CALL(mock_holder, ParseIndirectObject(::testing::_))
       .WillOnce(::testing::WithArg<0>(::testing::Invoke(
           [&mock_holder](uint32_t objnum) -> RetainPtr<CPDF_Object> {
-            const CPDF_Object* same_parse =
+            RetainPtr<const CPDF_Object> same_parse =
                 mock_holder.GetOrParseIndirectObject(objnum);
             CHECK(!same_parse);
             return pdfium::MakeRetain<CPDF_Null>();
@@ -40,7 +39,7 @@
   EXPECT_TRUE(mock_holder.GetOrParseIndirectObject(1000));
 }
 
-TEST(CPDF_IndirectObjectHolderTest, GetObjectMethods) {
+TEST(IndirectObjectHolderTest, GetObjectMethods) {
   static constexpr uint32_t kObjNum = 1000;
   MockIndirectObjectHolder mock_holder;
 
@@ -63,7 +62,7 @@
   EXPECT_EQ(kObjNum, mock_holder.GetIndirectObject(kObjNum)->GetObjNum());
 }
 
-TEST(CPDF_IndirectObjectHolderTest, ParseInvalidObjNum) {
+TEST(IndirectObjectHolderTest, ParseInvalidObjNum) {
   MockIndirectObjectHolder mock_holder;
 
   EXPECT_CALL(mock_holder, ParseIndirectObject(::testing::_)).Times(0);
@@ -71,10 +70,23 @@
       mock_holder.GetOrParseIndirectObject(CPDF_Object::kInvalidObjNum));
 }
 
-TEST(CPDF_IndirectObjectHolderTest, ReplaceObjectWithInvalidObjNum) {
+TEST(IndirectObjectHolderTest, ReplaceObjectWithInvalidObjNum) {
   MockIndirectObjectHolder mock_holder;
 
   EXPECT_CALL(mock_holder, ParseIndirectObject(::testing::_)).Times(0);
   EXPECT_FALSE(mock_holder.ReplaceIndirectObjectIfHigherGeneration(
       CPDF_Object::kInvalidObjNum, pdfium::MakeRetain<CPDF_Null>()));
 }
+
+TEST(IndirectObjectHolderTest, TemplateNewMethods) {
+  MockIndirectObjectHolder mock_holder;
+
+  auto pDict = mock_holder.NewIndirect<CPDF_Dictionary>();
+  auto pArray = mock_holder.NewIndirect<CPDF_Array>();
+  mock_holder.DeleteIndirectObject(pDict->GetObjNum());
+  mock_holder.DeleteIndirectObject(pArray->GetObjNum());
+
+  // No longer UAF since NewIndirect<> returns retained objects.
+  EXPECT_TRUE(pDict->IsDictionary());
+  EXPECT_TRUE(pArray->IsArray());
+}
diff --git a/core/fpdfapi/parser/cpdf_linearized_header.cpp b/core/fpdfapi/parser/cpdf_linearized_header.cpp
index c7dc54a..b093578 100644
--- a/core/fpdfapi/parser/cpdf_linearized_header.cpp
+++ b/core/fpdfapi/parser/cpdf_linearized_header.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,9 +13,11 @@
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fpdfapi/parser/cpdf_parser.h"
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/memory/ptr_util.h"
 
 namespace {
 
@@ -24,12 +26,12 @@
 
 template <class T>
 bool IsValidNumericDictionaryValue(const CPDF_Dictionary* pDict,
-                                   const char* key,
+                                   const ByteString& key,
                                    T min_value,
                                    bool must_exist = true) {
   if (!pDict->KeyExist(key))
     return !must_exist;
-  const CPDF_Number* pNum = ToNumber(pDict->GetObjectFor(key));
+  RetainPtr<const CPDF_Number> pNum = pDict->GetNumberFor(key);
   if (!pNum || !pNum->IsInteger())
     return false;
   const int raw_value = pNum->GetInteger();
@@ -40,12 +42,13 @@
 
 bool IsLinearizedHeaderValid(const CPDF_LinearizedHeader* header,
                              FX_FILESIZE document_size) {
-  ASSERT(header);
+  DCHECK(header);
   return header->GetFileSize() == document_size &&
          header->GetFirstPageNo() < kMaxInt &&
          header->GetFirstPageNo() < header->GetPageCount() &&
          header->GetMainXRefTableFirstEntryOffset() < document_size &&
          header->GetFirstPageEndOffset() < document_size &&
+         header->GetFirstPageObjNum() < CPDF_Parser::kMaxObjectNumber &&
          header->GetLastXRefOffset() < document_size &&
          header->GetHintStart() < document_size;
 }
@@ -71,7 +74,7 @@
   }
   // Move parser to the start of the xref table for the documents first page.
   // (skpping endobj keyword)
-  if (parser->GetNextWord(nullptr) != "endobj")
+  if (parser->GetNextWord().word != "endobj")
     return nullptr;
 
   auto result = pdfium::WrapUnique(
@@ -92,7 +95,7 @@
       m_szFirstPageEndOffset(pDict->GetIntegerFor("E")),
       m_FirstPageObjNum(pDict->GetIntegerFor("O")),
       m_szLastXRefOffset(szLastXRefOffset) {
-  const CPDF_Array* pHintStreamRange = pDict->GetArrayFor("H");
+  RetainPtr<const CPDF_Array> pHintStreamRange = pDict->GetArrayFor("H");
   const size_t nHintStreamSize =
       pHintStreamRange ? pHintStreamRange->size() : 0;
   if (nHintStreamSize == 2 || nHintStreamSize == 4) {
@@ -103,7 +106,7 @@
   }
 }
 
-CPDF_LinearizedHeader::~CPDF_LinearizedHeader() {}
+CPDF_LinearizedHeader::~CPDF_LinearizedHeader() = default;
 
 bool CPDF_LinearizedHeader::HasHintTable() const {
   return GetPageCount() > 1 && GetHintStart() > 0 && GetHintLength() > 0;
diff --git a/core/fpdfapi/parser/cpdf_linearized_header.h b/core/fpdfapi/parser/cpdf_linearized_header.h
index 44e0422..10e2453 100644
--- a/core/fpdfapi/parser/cpdf_linearized_header.h
+++ b/core/fpdfapi/parser/cpdf_linearized_header.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,13 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_LINEARIZED_HEADER_H_
 #define CORE_FPDFAPI_PARSER_CPDF_LINEARIZED_HEADER_H_
 
+#include <stdint.h>
+
 #include <memory>
 
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/fx_types.h"
 
 class CPDF_Dictionary;
-class CPDF_Object;
 class CPDF_SyntaxParser;
 
 class CPDF_LinearizedHeader {
@@ -31,7 +32,7 @@
   uint32_t GetPageCount() const { return m_PageCount; }
   // Will only return values > 0.
   FX_FILESIZE GetFirstPageEndOffset() const { return m_szFirstPageEndOffset; }
-  // Will only return values > 0.
+  // Will only return values in the range [1, `CPDF_Parser::kMaxObjectNumber`).
   uint32_t GetFirstPageObjNum() const { return m_FirstPageObjNum; }
   // Will only return values > 0.
   FX_FILESIZE GetLastXRefOffset() const { return m_szLastXRefOffset; }
diff --git a/core/fpdfapi/parser/cpdf_name.cpp b/core/fpdfapi/parser/cpdf_name.cpp
index 9f3f49f..6236ef4 100644
--- a/core/fpdfapi/parser/cpdf_name.cpp
+++ b/core/fpdfapi/parser/cpdf_name.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,6 @@
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcrt/fx_stream.h"
-#include "third_party/base/ptr_util.h"
 
 CPDF_Name::CPDF_Name(WeakPtr<ByteStringPool> pPool, const ByteString& str)
     : m_Name(str) {
@@ -17,7 +16,7 @@
     m_Name = pPool->Intern(m_Name);
 }
 
-CPDF_Name::~CPDF_Name() {}
+CPDF_Name::~CPDF_Name() = default;
 
 CPDF_Object::Type CPDF_Name::GetType() const {
   return kName;
@@ -35,15 +34,7 @@
   m_Name = str;
 }
 
-bool CPDF_Name::IsName() const {
-  return true;
-}
-
-CPDF_Name* CPDF_Name::AsName() {
-  return this;
-}
-
-const CPDF_Name* CPDF_Name::AsName() const {
+CPDF_Name* CPDF_Name::AsMutableName() {
   return this;
 }
 
@@ -53,6 +44,9 @@
 
 bool CPDF_Name::WriteTo(IFX_ArchiveStream* archive,
                         const CPDF_Encryptor* encryptor) const {
-  return archive->WriteString("/") &&
-         archive->WriteString(PDF_NameEncode(GetString()).AsStringView());
+  if (!archive->WriteString("/"))
+    return false;
+
+  const ByteString name = PDF_NameEncode(GetString());
+  return name.IsEmpty() || archive->WriteString(name.AsStringView());
 }
diff --git a/core/fpdfapi/parser/cpdf_name.h b/core/fpdfapi/parser/cpdf_name.h
index cfd90bb..c3c023f 100644
--- a/core/fpdfapi/parser/cpdf_name.h
+++ b/core/fpdfapi/parser/cpdf_name.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,14 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_NAME_H_
 #define CORE_FPDFAPI_PARSER_CPDF_NAME_H_
 
-#include <memory>
-
 #include "core/fpdfapi/parser/cpdf_object.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/string_pool_template.h"
 #include "core/fxcrt/weak_ptr.h"
 
 class CPDF_Name final : public CPDF_Object {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // CPDF_Object:
   Type GetType() const override;
@@ -24,9 +22,7 @@
   ByteString GetString() const override;
   WideString GetUnicodeText() const override;
   void SetString(const ByteString& str) override;
-  bool IsName() const override;
-  CPDF_Name* AsName() override;
-  const CPDF_Name* AsName() const override;
+  CPDF_Name* AsMutableName() override;
   bool WriteTo(IFX_ArchiveStream* archive,
                const CPDF_Encryptor* encryptor) const override;
 
@@ -38,11 +34,19 @@
 };
 
 inline CPDF_Name* ToName(CPDF_Object* obj) {
-  return obj ? obj->AsName() : nullptr;
+  return obj ? obj->AsMutableName() : nullptr;
 }
 
 inline const CPDF_Name* ToName(const CPDF_Object* obj) {
   return obj ? obj->AsName() : nullptr;
 }
 
+inline RetainPtr<const CPDF_Name> ToName(RetainPtr<CPDF_Object> obj) {
+  return RetainPtr<CPDF_Name>(ToName(obj.Get()));
+}
+
+inline RetainPtr<const CPDF_Name> ToName(RetainPtr<const CPDF_Object> obj) {
+  return RetainPtr<const CPDF_Name>(ToName(obj.Get()));
+}
+
 #endif  // CORE_FPDFAPI_PARSER_CPDF_NAME_H_
diff --git a/core/fpdfapi/parser/cpdf_null.cpp b/core/fpdfapi/parser/cpdf_null.cpp
index 71299c1..44a6c45 100644
--- a/core/fpdfapi/parser/cpdf_null.cpp
+++ b/core/fpdfapi/parser/cpdf_null.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,9 +7,8 @@
 #include "core/fpdfapi/parser/cpdf_null.h"
 
 #include "core/fxcrt/fx_stream.h"
-#include "third_party/base/ptr_util.h"
 
-CPDF_Null::CPDF_Null() {}
+CPDF_Null::CPDF_Null() = default;
 
 CPDF_Object::Type CPDF_Null::GetType() const {
   return kNullobj;
@@ -19,11 +18,11 @@
   return pdfium::MakeRetain<CPDF_Null>();
 }
 
+CPDF_Null* CPDF_Null::AsMutableNull() {
+  return this;
+}
+
 bool CPDF_Null::WriteTo(IFX_ArchiveStream* archive,
                         const CPDF_Encryptor* encryptor) const {
   return archive->WriteString(" null");
 }
-
-bool CPDF_Null::IsNull() const {
-  return true;
-}
diff --git a/core/fpdfapi/parser/cpdf_null.h b/core/fpdfapi/parser/cpdf_null.h
index 767583b..3d5a237 100644
--- a/core/fpdfapi/parser/cpdf_null.h
+++ b/core/fpdfapi/parser/cpdf_null.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,21 +7,19 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_NULL_H_
 #define CORE_FPDFAPI_PARSER_CPDF_NULL_H_
 
-#include <memory>
-
 #include "core/fpdfapi/parser/cpdf_object.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Null final : public CPDF_Object {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // CPDF_Object.
   Type GetType() const override;
   RetainPtr<CPDF_Object> Clone() const override;
+  CPDF_Null* AsMutableNull() override;
   bool WriteTo(IFX_ArchiveStream* archive,
                const CPDF_Encryptor* encryptor) const override;
-  bool IsNull() const override;
 
  private:
   CPDF_Null();
diff --git a/core/fpdfapi/parser/cpdf_number.cpp b/core/fpdfapi/parser/cpdf_number.cpp
index 24abf20..6b9d78d 100644
--- a/core/fpdfapi/parser/cpdf_number.cpp
+++ b/core/fpdfapi/parser/cpdf_number.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,9 +7,8 @@
 #include "core/fpdfapi/parser/cpdf_number.h"
 
 #include "core/fxcrt/fx_stream.h"
-#include "third_party/base/ptr_util.h"
 
-CPDF_Number::CPDF_Number() {}
+CPDF_Number::CPDF_Number() = default;
 
 CPDF_Number::CPDF_Number(int value) : m_Number(value) {}
 
@@ -17,7 +16,7 @@
 
 CPDF_Number::CPDF_Number(ByteStringView str) : m_Number(str) {}
 
-CPDF_Number::~CPDF_Number() {}
+CPDF_Number::~CPDF_Number() = default;
 
 CPDF_Object::Type CPDF_Number::GetType() const {
   return kNumber;
@@ -37,15 +36,7 @@
   return m_Number.GetSigned();
 }
 
-bool CPDF_Number::IsNumber() const {
-  return true;
-}
-
-CPDF_Number* CPDF_Number::AsNumber() {
-  return this;
-}
-
-const CPDF_Number* CPDF_Number::AsNumber() const {
+CPDF_Number* CPDF_Number::AsMutableNumber() {
   return this;
 }
 
diff --git a/core/fpdfapi/parser/cpdf_number.h b/core/fpdfapi/parser/cpdf_number.h
index dc75340..93b0f9d 100644
--- a/core/fpdfapi/parser/cpdf_number.h
+++ b/core/fpdfapi/parser/cpdf_number.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,17 +7,14 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_NUMBER_H_
 #define CORE_FPDFAPI_PARSER_CPDF_NUMBER_H_
 
-#include <memory>
-
 #include "core/fpdfapi/parser/cpdf_object.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_number.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Number final : public CPDF_Object {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // CPDF_Object:
   Type GetType() const override;
@@ -26,9 +23,7 @@
   float GetNumber() const override;
   int GetInteger() const override;
   void SetString(const ByteString& str) override;
-  bool IsNumber() const override;
-  CPDF_Number* AsNumber() override;
-  const CPDF_Number* AsNumber() const override;
+  CPDF_Number* AsMutableNumber() override;
   bool WriteTo(IFX_ArchiveStream* archive,
                const CPDF_Encryptor* encryptor) const override;
 
@@ -45,11 +40,19 @@
 };
 
 inline CPDF_Number* ToNumber(CPDF_Object* obj) {
-  return obj ? obj->AsNumber() : nullptr;
+  return obj ? obj->AsMutableNumber() : nullptr;
 }
 
 inline const CPDF_Number* ToNumber(const CPDF_Object* obj) {
   return obj ? obj->AsNumber() : nullptr;
 }
 
+inline RetainPtr<CPDF_Number> ToNumber(RetainPtr<CPDF_Object> obj) {
+  return RetainPtr<CPDF_Number>(ToNumber(obj.Get()));
+}
+
+inline RetainPtr<const CPDF_Number> ToNumber(RetainPtr<const CPDF_Object> obj) {
+  return RetainPtr<const CPDF_Number>(ToNumber(obj.Get()));
+}
+
 #endif  // CORE_FPDFAPI_PARSER_CPDF_NUMBER_H_
diff --git a/core/fpdfapi/parser/cpdf_object.cpp b/core/fpdfapi/parser/cpdf_object.cpp
index b61ee7e..3916392 100644
--- a/core/fpdfapi/parser/cpdf_object.cpp
+++ b/core/fpdfapi/parser/cpdf_object.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,16 +14,33 @@
 #include "core/fpdfapi/parser/cpdf_parser.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fxcrt/fx_string.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/notreached.h"
 
-CPDF_Object::~CPDF_Object() {}
+CPDF_Object::~CPDF_Object() = default;
 
-CPDF_Object* CPDF_Object::GetDirect() {
-  return this;
+static_assert(sizeof(uint64_t) >= sizeof(CPDF_Object*),
+              "Need a bigger type for cache keys");
+
+static_assert(CPDF_Parser::kMaxObjectNumber < static_cast<uint32_t>(1) << 31,
+              "Need a smaller kMaxObjNumber for cache keys");
+
+uint64_t CPDF_Object::KeyForCache() const {
+  if (IsInline())
+    return (static_cast<uint64_t>(1) << 63) | reinterpret_cast<uint64_t>(this);
+
+  return (static_cast<uint64_t>(m_ObjNum) << 32) |
+         static_cast<uint64_t>(m_GenNum);
 }
 
-const CPDF_Object* CPDF_Object::GetDirect() const {
+RetainPtr<CPDF_Object> CPDF_Object::GetMutableDirect() {
+  return pdfium::WrapRetain(const_cast<CPDF_Object*>(GetDirectInternal()));
+}
+
+RetainPtr<const CPDF_Object> CPDF_Object::GetDirect() const {
+  return pdfium::WrapRetain(GetDirectInternal());
+}
+
+const CPDF_Object* CPDF_Object::GetDirectInternal() const {
   return this;
 }
 
@@ -58,11 +75,15 @@
   return 0;
 }
 
-CPDF_Dictionary* CPDF_Object::GetDict() {
-  return nullptr;
+RetainPtr<const CPDF_Dictionary> CPDF_Object::GetDict() const {
+  return pdfium::WrapRetain(GetDictInternal());
 }
 
-const CPDF_Dictionary* CPDF_Object::GetDict() const {
+RetainPtr<CPDF_Dictionary> CPDF_Object::GetMutableDict() {
+  return pdfium::WrapRetain(const_cast<CPDF_Dictionary*>(GetDictInternal()));
+}
+
+const CPDF_Dictionary* CPDF_Object::GetDictInternal() const {
   return nullptr;
 }
 
@@ -70,107 +91,79 @@
   NOTREACHED();
 }
 
-bool CPDF_Object::IsArray() const {
-  return false;
-}
-
-bool CPDF_Object::IsBoolean() const {
-  return false;
-}
-
-bool CPDF_Object::IsDictionary() const {
-  return false;
-}
-
-bool CPDF_Object::IsName() const {
-  return false;
-}
-
-bool CPDF_Object::IsNumber() const {
-  return false;
-}
-
-bool CPDF_Object::IsReference() const {
-  return false;
-}
-
-bool CPDF_Object::IsStream() const {
-  return false;
-}
-
-bool CPDF_Object::IsString() const {
-  return false;
-}
-
-bool CPDF_Object::IsNull() const {
-  return false;
-}
-
-CPDF_Array* CPDF_Object::AsArray() {
+CPDF_Array* CPDF_Object::AsMutableArray() {
   return nullptr;
 }
 
 const CPDF_Array* CPDF_Object::AsArray() const {
-  return nullptr;
+  return const_cast<CPDF_Object*>(this)->AsMutableArray();
 }
 
-CPDF_Boolean* CPDF_Object::AsBoolean() {
+CPDF_Boolean* CPDF_Object::AsMutableBoolean() {
   return nullptr;
 }
 
 const CPDF_Boolean* CPDF_Object::AsBoolean() const {
-  return nullptr;
+  return const_cast<CPDF_Object*>(this)->AsMutableBoolean();
 }
 
-CPDF_Dictionary* CPDF_Object::AsDictionary() {
+CPDF_Dictionary* CPDF_Object::AsMutableDictionary() {
   return nullptr;
 }
 
 const CPDF_Dictionary* CPDF_Object::AsDictionary() const {
-  return nullptr;
+  return const_cast<CPDF_Object*>(this)->AsMutableDictionary();
 }
 
-CPDF_Name* CPDF_Object::AsName() {
+CPDF_Name* CPDF_Object::AsMutableName() {
   return nullptr;
 }
 
 const CPDF_Name* CPDF_Object::AsName() const {
+  return const_cast<CPDF_Object*>(this)->AsMutableName();
+}
+
+CPDF_Null* CPDF_Object::AsMutableNull() {
   return nullptr;
 }
 
-CPDF_Number* CPDF_Object::AsNumber() {
+const CPDF_Null* CPDF_Object::AsNull() const {
+  return const_cast<CPDF_Object*>(this)->AsMutableNull();
+}
+
+CPDF_Number* CPDF_Object::AsMutableNumber() {
   return nullptr;
 }
 
 const CPDF_Number* CPDF_Object::AsNumber() const {
-  return nullptr;
+  return const_cast<CPDF_Object*>(this)->AsMutableNumber();
 }
 
-CPDF_Reference* CPDF_Object::AsReference() {
+CPDF_Reference* CPDF_Object::AsMutableReference() {
   return nullptr;
 }
 
 const CPDF_Reference* CPDF_Object::AsReference() const {
-  return nullptr;
+  return const_cast<CPDF_Object*>(this)->AsMutableReference();
 }
 
-CPDF_Stream* CPDF_Object::AsStream() {
+CPDF_Stream* CPDF_Object::AsMutableStream() {
   return nullptr;
 }
 
 const CPDF_Stream* CPDF_Object::AsStream() const {
-  return nullptr;
+  return const_cast<CPDF_Object*>(this)->AsMutableStream();
 }
 
-CPDF_String* CPDF_Object::AsString() {
+CPDF_String* CPDF_Object::AsMutableString() {
   return nullptr;
 }
 
 const CPDF_String* CPDF_Object::AsString() const {
-  return nullptr;
+  return const_cast<CPDF_Object*>(this)->AsMutableString();
 }
 
-RetainPtr<CPDF_Object> CPDF_Object::MakeReference(
+RetainPtr<CPDF_Reference> CPDF_Object::MakeReference(
     CPDF_IndirectObjectHolder* holder) const {
   if (IsInline()) {
     NOTREACHED();
diff --git a/core/fpdfapi/parser/cpdf_object.h b/core/fpdfapi/parser/cpdf_object.h
index 77810ca..4793b61 100644
--- a/core/fpdfapi/parser/cpdf_object.h
+++ b/core/fpdfapi/parser/cpdf_object.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,13 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_OBJECT_H_
 #define CORE_FPDFAPI_PARSER_CPDF_OBJECT_H_
 
-#include <memory>
+#include <stdint.h>
+
 #include <set>
 #include <type_traits>
 
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Array;
 class CPDF_Boolean;
@@ -27,9 +28,28 @@
 class CPDF_String;
 class IFX_ArchiveStream;
 
+// ISO 32000-1:2008 defines PDF objects. When CPDF_Parser parses a PDF object,
+// it represents the PDF object using CPDF_Objects. Take this PDF object for
+// example:
+//
+// 4 0 obj <<
+//   /Type /Pages
+//   /Count 1
+//   /Kids [9 0 R]
+// >>
+//
+// Multiple CPDF_Objects instances are necessary to represent this PDF object:
+// 1) A CPDF_Dictionary with object number 4 that contains 3 elements.
+// 2) A CPDF_Name for /Pages.
+// 3) A CPDF_Number for the count of 1.
+// 4) A CPDF_Array for [9 0 R], which contains 1 element.
+// 5) A CPDF_Reference that references object 9 0.
+//
+// CPDF_Object (1) has an object number of 4. All the other CPDF_Objects are
+// inline objects. CPDF_Object represent that by using an object number of 0.
 class CPDF_Object : public Retainable {
  public:
-  static const uint32_t kInvalidObjNum = static_cast<uint32_t>(-1);
+  static constexpr uint32_t kInvalidObjNum = static_cast<uint32_t>(-1);
   enum Type {
     kBoolean = 1,
     kNumber,
@@ -42,57 +62,38 @@
     kReference
   };
 
-  virtual Type GetType() const = 0;
   uint32_t GetObjNum() const { return m_ObjNum; }
   void SetObjNum(uint32_t objnum) { m_ObjNum = objnum; }
   uint32_t GetGenNum() const { return m_GenNum; }
   void SetGenNum(uint32_t gennum) { m_GenNum = gennum; }
   bool IsInline() const { return m_ObjNum == 0; }
+  uint64_t KeyForCache() const;
+
+  virtual Type GetType() const = 0;
 
   // Create a deep copy of the object.
   virtual RetainPtr<CPDF_Object> Clone() const = 0;
 
   // Create a deep copy of the object except any reference object be
   // copied to the object it points to directly.
-  virtual RetainPtr<CPDF_Object> CloneDirectObject() const;
+  RetainPtr<CPDF_Object> CloneDirectObject() const;
 
-  virtual CPDF_Object* GetDirect();
-  virtual const CPDF_Object* GetDirect() const;
   virtual ByteString GetString() const;
   virtual WideString GetUnicodeText() const;
   virtual float GetNumber() const;
   virtual int GetInteger() const;
-  virtual CPDF_Dictionary* GetDict();
-  virtual const CPDF_Dictionary* GetDict() const;
 
   virtual void SetString(const ByteString& str);
 
-  virtual bool IsArray() const;
-  virtual bool IsBoolean() const;
-  virtual bool IsDictionary() const;
-  virtual bool IsName() const;
-  virtual bool IsNumber() const;
-  virtual bool IsReference() const;
-  virtual bool IsStream() const;
-  virtual bool IsString() const;
-  virtual bool IsNull() const;
-
-  virtual CPDF_Array* AsArray();
-  virtual const CPDF_Array* AsArray() const;
-  virtual CPDF_Boolean* AsBoolean();
-  virtual const CPDF_Boolean* AsBoolean() const;
-  virtual CPDF_Dictionary* AsDictionary();
-  virtual const CPDF_Dictionary* AsDictionary() const;
-  virtual CPDF_Name* AsName();
-  virtual const CPDF_Name* AsName() const;
-  virtual CPDF_Number* AsNumber();
-  virtual const CPDF_Number* AsNumber() const;
-  virtual CPDF_Reference* AsReference();
-  virtual const CPDF_Reference* AsReference() const;
-  virtual CPDF_Stream* AsStream();
-  virtual const CPDF_Stream* AsStream() const;
-  virtual CPDF_String* AsString();
-  virtual const CPDF_String* AsString() const;
+  virtual CPDF_Array* AsMutableArray();
+  virtual CPDF_Boolean* AsMutableBoolean();
+  virtual CPDF_Dictionary* AsMutableDictionary();
+  virtual CPDF_Name* AsMutableName();
+  virtual CPDF_Null* AsMutableNull();
+  virtual CPDF_Number* AsMutableNumber();
+  virtual CPDF_Reference* AsMutableReference();
+  virtual CPDF_Stream* AsMutableStream();
+  virtual CPDF_String* AsMutableString();
 
   virtual bool WriteTo(IFX_ArchiveStream* archive,
                        const CPDF_Encryptor* encryptor) const = 0;
@@ -109,14 +110,46 @@
 
   // Return a reference to itself.
   // The object must be direct (!IsInlined).
-  virtual RetainPtr<CPDF_Object> MakeReference(
+  virtual RetainPtr<CPDF_Reference> MakeReference(
       CPDF_IndirectObjectHolder* holder) const;
 
+  RetainPtr<const CPDF_Object> GetDirect() const;    // Wraps virtual method.
+  RetainPtr<CPDF_Object> GetMutableDirect();         // Wraps virtual method.
+  RetainPtr<const CPDF_Dictionary> GetDict() const;  // Wraps virtual method.
+  RetainPtr<CPDF_Dictionary> GetMutableDict();       // Wraps virtual method.
+
+  // Const methods wrapping non-const virtual As*() methods.
+  const CPDF_Array* AsArray() const;
+  const CPDF_Boolean* AsBoolean() const;
+  const CPDF_Dictionary* AsDictionary() const;
+  const CPDF_Name* AsName() const;
+  const CPDF_Null* AsNull() const;
+  const CPDF_Number* AsNumber() const;
+  const CPDF_Reference* AsReference() const;
+  const CPDF_Stream* AsStream() const;
+  const CPDF_String* AsString() const;
+
+  // Type-testing methods merely wrap As*() methods.
+  bool IsArray() const { return !!AsArray(); }
+  bool IsBoolean() const { return !!AsBoolean(); }
+  bool IsDictionary() const { return !!AsDictionary(); }
+  bool IsName() const { return !!AsName(); }
+  bool IsNull() const { return !!AsNull(); }
+  bool IsNumber() const { return !!AsNumber(); }
+  bool IsReference() const { return !!AsReference(); }
+  bool IsStream() const { return !!AsStream(); }
+  bool IsString() const { return !!AsString(); }
+
  protected:
+  friend class CPDF_Dictionary;
+  friend class CPDF_Reference;
+
   CPDF_Object() = default;
   CPDF_Object(const CPDF_Object& src) = delete;
   ~CPDF_Object() override;
 
+  virtual const CPDF_Object* GetDirectInternal() const;
+  virtual const CPDF_Dictionary* GetDictInternal() const;
   RetainPtr<CPDF_Object> CloneObjectNonCyclic(bool bDirect) const;
 
   uint32_t m_ObjNum = 0;
@@ -125,10 +158,10 @@
 
 template <typename T>
 struct CanInternStrings {
-  static const bool value = std::is_same<T, CPDF_Array>::value ||
-                            std::is_same<T, CPDF_Dictionary>::value ||
-                            std::is_same<T, CPDF_Name>::value ||
-                            std::is_same<T, CPDF_String>::value;
+  static constexpr bool value = std::is_same<T, CPDF_Array>::value ||
+                                std::is_same<T, CPDF_Dictionary>::value ||
+                                std::is_same<T, CPDF_Name>::value ||
+                                std::is_same<T, CPDF_String>::value;
 };
 
 #endif  // CORE_FPDFAPI_PARSER_CPDF_OBJECT_H_
diff --git a/core/fpdfapi/parser/cpdf_object_avail.cpp b/core/fpdfapi/parser/cpdf_object_avail.cpp
index 1dc5125..8bb3473 100644
--- a/core/fpdfapi/parser/cpdf_object_avail.cpp
+++ b/core/fpdfapi/parser/cpdf_object_avail.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,42 +11,43 @@
 #include "core/fpdfapi/parser/cpdf_object_walker.h"
 #include "core/fpdfapi/parser/cpdf_read_validator.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
 
-CPDF_ObjectAvail::CPDF_ObjectAvail(
-    const RetainPtr<CPDF_ReadValidator>& validator,
-    CPDF_IndirectObjectHolder* holder,
-    CPDF_Object* root)
-    : validator_(validator), holder_(holder), root_(root) {
-  ASSERT(validator_);
-  ASSERT(holder);
-  ASSERT(root_);
+CPDF_ObjectAvail::CPDF_ObjectAvail(RetainPtr<CPDF_ReadValidator> validator,
+                                   CPDF_IndirectObjectHolder* holder,
+                                   RetainPtr<const CPDF_Object> root)
+    : validator_(std::move(validator)),
+      holder_(holder),
+      root_(std::move(root)) {
+  DCHECK(validator_);
+  DCHECK(holder);
+  DCHECK(root_);
   if (!root_->IsInline())
     parsed_objnums_.insert(root_->GetObjNum());
 }
 
-CPDF_ObjectAvail::CPDF_ObjectAvail(
-    const RetainPtr<CPDF_ReadValidator>& validator,
-    CPDF_IndirectObjectHolder* holder,
-    uint32_t obj_num)
-    : validator_(validator),
+CPDF_ObjectAvail::CPDF_ObjectAvail(RetainPtr<CPDF_ReadValidator> validator,
+                                   CPDF_IndirectObjectHolder* holder,
+                                   uint32_t obj_num)
+    : validator_(std::move(validator)),
       holder_(holder),
       root_(pdfium::MakeRetain<CPDF_Reference>(holder, obj_num)) {
-  ASSERT(validator_);
-  ASSERT(holder);
+  DCHECK(validator_);
+  DCHECK(holder);
 }
 
-CPDF_ObjectAvail::~CPDF_ObjectAvail() {}
+CPDF_ObjectAvail::~CPDF_ObjectAvail() = default;
 
 CPDF_DataAvail::DocAvailStatus CPDF_ObjectAvail::CheckAvail() {
   if (!LoadRootObject())
-    return CPDF_DataAvail::DocAvailStatus::DataNotAvailable;
+    return CPDF_DataAvail::kDataNotAvailable;
 
   if (CheckObjects()) {
     CleanMemory();
-    return CPDF_DataAvail::DocAvailStatus::DataAvailable;
+    return CPDF_DataAvail::kDataAvailable;
   }
-  return CPDF_DataAvail::DocAvailStatus::DataNotAvailable;
+  return CPDF_DataAvail::kDataNotAvailable;
 }
 
 bool CPDF_ObjectAvail::LoadRootObject() {
@@ -60,16 +61,17 @@
       return true;
     }
 
-    const CPDF_ReadValidator::Session parse_session(validator_);
-    CPDF_Object* direct = holder_->GetOrParseIndirectObject(ref_obj_num);
+    CPDF_ReadValidator::ScopedSession parse_session(validator_);
+    RetainPtr<CPDF_Object> direct =
+        holder_->GetOrParseIndirectObject(ref_obj_num);
     if (validator_->has_read_problems())
       return false;
 
     parsed_objnums_.insert(ref_obj_num);
-    root_.Reset(direct);
+    root_ = std::move(direct);
   }
   std::stack<uint32_t> non_parsed_objects_in_root;
-  if (AppendObjectSubRefs(root_.Get(), &non_parsed_objects_in_root)) {
+  if (AppendObjectSubRefs(root_, &non_parsed_objects_in_root)) {
     non_parsed_objects_ = std::move(non_parsed_objects_in_root);
     return true;
   }
@@ -90,13 +92,14 @@
     if (!checked_objects.insert(obj_num).second)
       continue;
 
-    const CPDF_ReadValidator::Session parse_session(validator_);
-    const CPDF_Object* direct = holder_->GetOrParseIndirectObject(obj_num);
+    CPDF_ReadValidator::ScopedSession parse_session(validator_);
+    RetainPtr<const CPDF_Object> direct =
+        holder_->GetOrParseIndirectObject(obj_num);
     if (direct == root_)
       continue;
 
     if (validator_->has_read_problems() ||
-        !AppendObjectSubRefs(direct, &objects_to_check)) {
+        !AppendObjectSubRefs(std::move(direct), &objects_to_check)) {
       non_parsed_objects_.push(obj_num);
       continue;
     }
@@ -105,21 +108,21 @@
   return non_parsed_objects_.empty();
 }
 
-bool CPDF_ObjectAvail::AppendObjectSubRefs(const CPDF_Object* object,
+bool CPDF_ObjectAvail::AppendObjectSubRefs(RetainPtr<const CPDF_Object> object,
                                            std::stack<uint32_t>* refs) const {
-  ASSERT(refs);
+  DCHECK(refs);
   if (!object)
     return true;
 
-  CPDF_ObjectWalker walker(object);
-  while (const CPDF_Object* obj = walker.GetNext()) {
-    const CPDF_ReadValidator::Session parse_session(validator_);
+  CPDF_ObjectWalker walker(std::move(object));
+  while (RetainPtr<const CPDF_Object> obj = walker.GetNext()) {
+    CPDF_ReadValidator::ScopedSession parse_session(validator_);
 
     // Skip if this object if it's an inlined root, the parent object or
     // explicitily excluded.
     const bool skip = (walker.GetParent() && obj == root_) ||
                       walker.dictionary_key() == "Parent" ||
-                      (obj != root_ && ExcludeObject(obj));
+                      (obj != root_ && ExcludeObject(obj.Get()));
 
     // We need to parse the object before we can do the exclusion check.
     // This is because the exclusion check may check against a referenced
@@ -148,5 +151,5 @@
 }
 
 bool CPDF_ObjectAvail::HasObjectParsed(uint32_t obj_num) const {
-  return parsed_objnums_.count(obj_num) > 0;
+  return pdfium::Contains(parsed_objnums_, obj_num);
 }
diff --git a/core/fpdfapi/parser/cpdf_object_avail.h b/core/fpdfapi/parser/cpdf_object_avail.h
index 901fb17..df7360d 100644
--- a/core/fpdfapi/parser/cpdf_object_avail.h
+++ b/core/fpdfapi/parser/cpdf_object_avail.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,17 +13,16 @@
 #include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_Object;
-class CPDF_Reference;
 class CPDF_IndirectObjectHolder;
 class CPDF_ReadValidator;
 
 // Helper for check availability of object tree.
 class CPDF_ObjectAvail {
  public:
-  CPDF_ObjectAvail(const RetainPtr<CPDF_ReadValidator>& validator,
+  CPDF_ObjectAvail(RetainPtr<CPDF_ReadValidator> validator,
                    CPDF_IndirectObjectHolder* holder,
-                   CPDF_Object* root);
-  CPDF_ObjectAvail(const RetainPtr<CPDF_ReadValidator>& validator,
+                   RetainPtr<const CPDF_Object> root);
+  CPDF_ObjectAvail(RetainPtr<CPDF_ReadValidator> validator,
                    CPDF_IndirectObjectHolder* holder,
                    uint32_t obj_num);
   virtual ~CPDF_ObjectAvail();
@@ -36,14 +35,14 @@
  private:
   bool LoadRootObject();
   bool CheckObjects();
-  bool AppendObjectSubRefs(const CPDF_Object* object,
+  bool AppendObjectSubRefs(RetainPtr<const CPDF_Object> object,
                            std::stack<uint32_t>* refs) const;
   void CleanMemory();
   bool HasObjectParsed(uint32_t obj_num) const;
 
-  RetainPtr<CPDF_ReadValidator> validator_;
-  UnownedPtr<CPDF_IndirectObjectHolder> holder_;
-  RetainPtr<CPDF_Object> root_;
+  RetainPtr<CPDF_ReadValidator> const validator_;
+  UnownedPtr<CPDF_IndirectObjectHolder> const holder_;
+  RetainPtr<const CPDF_Object> root_;
   std::set<uint32_t> parsed_objnums_;
   std::stack<uint32_t> non_parsed_objects_;
 };
diff --git a/core/fpdfapi/parser/cpdf_object_avail_unittest.cpp b/core/fpdfapi/parser/cpdf_object_avail_unittest.cpp
index f27ae63..97eb3d6 100644
--- a/core/fpdfapi/parser/cpdf_object_avail_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_object_avail_unittest.cpp
@@ -1,11 +1,10 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/parser/cpdf_object_avail.h"
 
 #include <map>
-#include <memory>
 #include <utility>
 
 #include "core/fpdfapi/parser/cpdf_array.h"
@@ -17,22 +16,22 @@
 #include "core/fxcrt/fx_stream.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/invalid_seekable_read_stream.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
 class TestReadValidator final : public CPDF_ReadValidator {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
-  void SimulateReadError() { ReadBlockAtOffset(nullptr, 0, 1); }
+  void SimulateReadError() { ReadBlockAtOffset({}, 0); }
 
  private:
   TestReadValidator()
       : CPDF_ReadValidator(pdfium::MakeRetain<InvalidSeekableReadStream>(100),
                            nullptr) {}
-  ~TestReadValidator() override {}
+  ~TestReadValidator() override = default;
 };
 
 class TestHolder final : public CPDF_IndirectObjectHolder {
@@ -42,10 +41,10 @@
     Available,
   };
   TestHolder() : validator_(pdfium::MakeRetain<TestReadValidator>()) {}
-  ~TestHolder() override {}
+  ~TestHolder() override = default;
 
   // CPDF_IndirectObjectHolder overrides:
-  CPDF_Object* GetOrParseIndirectObject(uint32_t objnum) override {
+  RetainPtr<CPDF_Object> ParseIndirectObject(uint32_t objnum) override {
     auto it = objects_data_.find(objnum);
     if (it == objects_data_.end())
       return nullptr;
@@ -55,7 +54,7 @@
       validator_->SimulateReadError();
       return nullptr;
     }
-    return obj_data.object.Get();
+    return obj_data.object;
   }
 
   RetainPtr<CPDF_ReadValidator> GetValidator() { return validator_; }
@@ -66,13 +65,13 @@
     ObjectData object_data;
     object_data.object = std::move(object);
     object_data.state = state;
-    ASSERT(objects_data_.find(objnum) == objects_data_.end());
+    DCHECK(objects_data_.find(objnum) == objects_data_.end());
     objects_data_[objnum] = std::move(object_data);
   }
 
   void SetObjectState(uint32_t objnum, ObjectState state) {
     auto it = objects_data_.find(objnum);
-    ASSERT(it != objects_data_.end());
+    DCHECK(it != objects_data_.end());
     ObjectData& obj_data = it->second;
     obj_data.state = state;
   }
@@ -96,7 +95,7 @@
 class CPDF_ObjectAvailFailOnExclude final : public CPDF_ObjectAvail {
  public:
   using CPDF_ObjectAvail::CPDF_ObjectAvail;
-  ~CPDF_ObjectAvailFailOnExclude() override {}
+  ~CPDF_ObjectAvailFailOnExclude() override = default;
   bool ExcludeObject(const CPDF_Object* object) const override {
     NOTREACHED();
     return false;
@@ -106,7 +105,7 @@
 class CPDF_ObjectAvailExcludeArray final : public CPDF_ObjectAvail {
  public:
   using CPDF_ObjectAvail::CPDF_ObjectAvail;
-  ~CPDF_ObjectAvailExcludeArray() override {}
+  ~CPDF_ObjectAvailExcludeArray() override = default;
   bool ExcludeObject(const CPDF_Object* object) const override {
     return object->IsArray();
   }
@@ -115,49 +114,46 @@
 class CPDF_ObjectAvailExcludeTypeKey final : public CPDF_ObjectAvail {
  public:
   using CPDF_ObjectAvail::CPDF_ObjectAvail;
-  ~CPDF_ObjectAvailExcludeTypeKey() override {}
+  ~CPDF_ObjectAvailExcludeTypeKey() override = default;
   bool ExcludeObject(const CPDF_Object* object) const override {
     // The value of "Type" may be reference, and if it is not available, we can
     // incorrect filter objects.
     // In this case CPDF_ObjectAvail should wait availability of this item and
     // call ExcludeObject again.
     return object->IsDictionary() &&
-           object->GetDict()->GetStringFor("Type") == "Exclude me";
+           object->GetDict()->GetByteStringFor("Type") == "Exclude me";
   }
 };
 
 }  // namespace
 
-TEST(CPDF_ObjectAvailTest, OneObject) {
+TEST(ObjectAvailTest, OneObject) {
   TestHolder holder;
   holder.AddObject(1, pdfium::MakeRetain<CPDF_String>(nullptr, "string", false),
                    TestHolder::ObjectState::Unavailable);
   CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1);
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
-            avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
   holder.SetObjectState(1, TestHolder::ObjectState::Available);
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
 }
 
-TEST(CPDF_ObjectAvailTest, OneReferencedObject) {
+TEST(ObjectAvailTest, OneReferencedObject) {
   TestHolder holder;
   holder.AddObject(1, pdfium::MakeRetain<CPDF_Reference>(&holder, 2),
                    TestHolder::ObjectState::Unavailable);
   holder.AddObject(2, pdfium::MakeRetain<CPDF_String>(nullptr, "string", false),
                    TestHolder::ObjectState::Unavailable);
   CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1);
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
-            avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
 
   holder.SetObjectState(1, TestHolder::ObjectState::Available);
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
-            avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
 
   holder.SetObjectState(2, TestHolder::ObjectState::Available);
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
 }
 
-TEST(CPDF_ObjectAvailTest, CycledReferences) {
+TEST(ObjectAvailTest, CycledReferences) {
   TestHolder holder;
   holder.AddObject(1, pdfium::MakeRetain<CPDF_Reference>(&holder, 2),
                    TestHolder::ObjectState::Unavailable);
@@ -167,48 +163,44 @@
                    TestHolder::ObjectState::Unavailable);
 
   CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1);
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
-            avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
 
   holder.SetObjectState(1, TestHolder::ObjectState::Available);
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
-            avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
 
   holder.SetObjectState(2, TestHolder::ObjectState::Available);
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
-            avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
 
   holder.SetObjectState(3, TestHolder::ObjectState::Available);
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
 }
 
-TEST(CPDF_ObjectAvailTest, DoNotCheckParent) {
+TEST(ObjectAvailTest, DoNotCheckParent) {
   TestHolder holder;
   holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Unavailable);
   holder.AddObject(2, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Unavailable);
 
-  holder.GetTestObject(2)->GetDict()->SetNewFor<CPDF_Reference>("Parent",
-                                                                &holder, 1);
+  holder.GetTestObject(2)->GetMutableDict()->SetNewFor<CPDF_Reference>(
+      "Parent", &holder, 1);
 
   CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 2);
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
-            avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
 
   holder.SetObjectState(2, TestHolder::ObjectState::Available);
   //  Object should be available in case when "Parent" object is unavailable.
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
 }
 
-TEST(CPDF_ObjectAvailTest, Generic) {
+TEST(ObjectAvailTest, Generic) {
   TestHolder holder;
   const uint32_t kDepth = 100;
   for (uint32_t i = 1; i < kDepth; ++i) {
     holder.AddObject(i, pdfium::MakeRetain<CPDF_Dictionary>(),
                      TestHolder::ObjectState::Unavailable);
     // Add ref to next dictionary.
-    holder.GetTestObject(i)->GetDict()->SetNewFor<CPDF_Reference>(
+    holder.GetTestObject(i)->GetMutableDict()->SetNewFor<CPDF_Reference>(
         "Child", &holder, i + 1);
   }
   // Add final object
@@ -217,40 +209,40 @@
 
   CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1);
   for (uint32_t i = 1; i <= kDepth; ++i) {
-    EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
-              avail.CheckAvail());
+    EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
     holder.SetObjectState(i, TestHolder::ObjectState::Available);
   }
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
 }
 
-TEST(CPDF_ObjectAvailTest, NotExcludeRoot) {
+TEST(ObjectAvailTest, NotExcludeRoot) {
   TestHolder holder;
   holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
   CPDF_ObjectAvailFailOnExclude avail(holder.GetValidator(), &holder, 1);
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
 }
 
-TEST(CPDF_ObjectAvailTest, NotExcludeReferedRoot) {
+TEST(ObjectAvailTest, NotExcludeReferedRoot) {
   TestHolder holder;
   holder.AddObject(1, pdfium::MakeRetain<CPDF_Reference>(&holder, 2),
                    TestHolder::ObjectState::Available);
   holder.AddObject(2, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
   CPDF_ObjectAvailFailOnExclude avail(holder.GetValidator(), &holder, 1);
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
 }
 
-TEST(CPDF_ObjectAvailTest, Exclude) {
+TEST(ObjectAvailTest, Exclude) {
   TestHolder holder;
   holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
-  holder.GetTestObject(1)->GetDict()->SetNewFor<CPDF_Reference>("ArrayRef",
-                                                                &holder, 2);
+  holder.GetTestObject(1)->GetMutableDict()->SetNewFor<CPDF_Reference>(
+      "ArrayRef", &holder, 2);
   holder.AddObject(2, pdfium::MakeRetain<CPDF_Array>(),
                    TestHolder::ObjectState::Available);
-  holder.GetTestObject(2)->AsArray()->AddNew<CPDF_Reference>(&holder, 2);
+  holder.GetTestObject(2)->AsMutableArray()->AppendNew<CPDF_Reference>(&holder,
+                                                                       2);
 
   // Add string, which is refered by array item. It is should not be checked.
   holder.AddObject(
@@ -258,28 +250,28 @@
       pdfium::MakeRetain<CPDF_String>(nullptr, "Not available string", false),
       TestHolder::ObjectState::Unavailable);
   CPDF_ObjectAvailExcludeArray avail(holder.GetValidator(), &holder, 1);
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
 }
 
-TEST(CPDF_ObjectAvailTest, ReadErrorOnExclude) {
+TEST(ObjectAvailTest, ReadErrorOnExclude) {
   TestHolder holder;
   holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
-  holder.GetTestObject(1)->GetDict()->SetNewFor<CPDF_Reference>("DictRef",
-                                                                &holder, 2);
+  holder.GetTestObject(1)->GetMutableDict()->SetNewFor<CPDF_Reference>(
+      "DictRef", &holder, 2);
   holder.AddObject(2, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
 
-  holder.GetTestObject(2)->GetDict()->SetNewFor<CPDF_Reference>("Type", &holder,
-                                                                3);
+  holder.GetTestObject(2)->GetMutableDict()->SetNewFor<CPDF_Reference>(
+      "Type", &holder, 3);
   // The value of "Type" key is not available at start
   holder.AddObject(
       3, pdfium::MakeRetain<CPDF_String>(nullptr, "Exclude me", false),
       TestHolder::ObjectState::Unavailable);
 
-  holder.GetTestObject(2)->GetDict()->SetNewFor<CPDF_Reference>("OtherData",
-                                                                &holder, 4);
-  // Add string, which is refered by dictionary item. It is should not be
+  holder.GetTestObject(2)->GetMutableDict()->SetNewFor<CPDF_Reference>(
+      "OtherData", &holder, 4);
+  // Add string, which is referred by dictionary item. It is should not be
   // checked, because the dictionary with it, should be skipped.
   holder.AddObject(
       4,
@@ -288,30 +280,29 @@
 
   CPDF_ObjectAvailExcludeTypeKey avail(holder.GetValidator(), &holder, 1);
 
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
-            avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
 
   // Make "Type" value object available.
   holder.SetObjectState(3, TestHolder::ObjectState::Available);
 
   // Now object should be available, although the object '4' is not available,
   // because it is in skipped dictionary.
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
 }
 
-TEST(CPDF_ObjectAvailTest, IgnoreNotExistsObject) {
+TEST(ObjectAvailTest, IgnoreNotExistsObject) {
   TestHolder holder;
   holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
-  holder.GetTestObject(1)->GetDict()->SetNewFor<CPDF_Reference>(
+  holder.GetTestObject(1)->GetMutableDict()->SetNewFor<CPDF_Reference>(
       "NotExistsObjRef", &holder, 2);
   CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1);
   // Now object should be available, although the object '2' is not exists. But
   // all exists in file related data are checked.
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
 }
 
-TEST(CPDF_ObjectAvailTest, CheckTwice) {
+TEST(ObjectAvailTest, CheckTwice) {
   TestHolder holder;
   holder.AddObject(1, pdfium::MakeRetain<CPDF_String>(nullptr, "string", false),
                    TestHolder::ObjectState::Unavailable);
@@ -323,27 +314,24 @@
   EXPECT_EQ(avail.CheckAvail(), avail.CheckAvail());
 }
 
-TEST(CPDF_ObjectAvailTest, SelfReferedInlinedObject) {
+TEST(ObjectAvailTest, SelfReferedInlinedObject) {
   TestHolder holder;
   holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
 
-  holder.GetTestObject(1)->GetDict()->SetNewFor<CPDF_Reference>("Data", &holder,
-                                                                2);
-  auto* root =
-      holder.GetTestObject(1)->GetDict()->SetNewFor<CPDF_Dictionary>("Dict");
+  holder.GetTestObject(1)->GetMutableDict()->SetNewFor<CPDF_Reference>(
+      "Data", &holder, 2);
+  auto root =
+      holder.GetTestObject(1)->GetMutableDict()->SetNewFor<CPDF_Dictionary>(
+          "Dict");
 
   root->SetNewFor<CPDF_Reference>("Self", &holder, 1);
-
   holder.AddObject(2, pdfium::MakeRetain<CPDF_String>(nullptr, "Data", false),
                    TestHolder::ObjectState::Unavailable);
 
   CPDF_ObjectAvail avail(holder.GetValidator(), &holder, root);
-
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
-            avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail());
 
   holder.SetObjectState(2, TestHolder::ObjectState::Available);
-
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
 }
diff --git a/core/fpdfapi/parser/cpdf_object_stream.cpp b/core/fpdfapi/parser/cpdf_object_stream.cpp
index a515dbb..19294da 100644
--- a/core/fpdfapi/parser/cpdf_object_stream.cpp
+++ b/core/fpdfapi/parser/cpdf_object_stream.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,26 +13,26 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/memory/ptr_util.h"
 
-// static
-bool CPDF_ObjectStream::IsObjectsStreamObject(const CPDF_Object* object) {
+namespace {
+
+bool IsObjectStream(const CPDF_Object* object) {
   const CPDF_Stream* stream = ToStream(object);
   if (!stream)
     return false;
 
-  const CPDF_Dictionary* stream_dict = stream->GetDict();
-  if (!stream_dict)
+  // See ISO 32000-1:2008 spec, table 16.
+  RetainPtr<const CPDF_Dictionary> stream_dict = stream->GetDict();
+  if (!ValidateDictType(stream_dict.Get(), "ObjStm"))
     return false;
 
-  if (stream_dict->GetStringFor("Type") != "ObjStm")
-    return false;
-
-  const CPDF_Number* number_of_objects =
-      ToNumber(stream_dict->GetObjectFor("N"));
+  RetainPtr<const CPDF_Number> number_of_objects =
+      stream_dict->GetNumberFor("N");
   if (!number_of_objects || !number_of_objects->IsInteger() ||
       number_of_objects->GetInteger() < 0 ||
       number_of_objects->GetInteger() >=
@@ -40,8 +40,8 @@
     return false;
   }
 
-  const CPDF_Number* first_object_offset =
-      ToNumber(stream_dict->GetObjectFor("First"));
+  RetainPtr<const CPDF_Number> first_object_offset =
+      stream_dict->GetNumberFor("First");
   if (!first_object_offset || !first_object_offset->IsInteger() ||
       first_object_offset->GetInteger() < 0) {
     return false;
@@ -50,56 +50,49 @@
   return true;
 }
 
+}  // namespace
+
 //  static
 std::unique_ptr<CPDF_ObjectStream> CPDF_ObjectStream::Create(
-    const CPDF_Stream* stream) {
-  if (!IsObjectsStreamObject(stream))
+    RetainPtr<const CPDF_Stream> stream) {
+  if (!IsObjectStream(stream.Get()))
     return nullptr;
-  // The ctor of CPDF_ObjectStream is protected. Use WrapUnique instead
-  // MakeUnique.
-  return pdfium::WrapUnique(new CPDF_ObjectStream(stream));
+
+  // Protected constructor.
+  return pdfium::WrapUnique(new CPDF_ObjectStream(std::move(stream)));
 }
 
-CPDF_ObjectStream::CPDF_ObjectStream(const CPDF_Stream* obj_stream)
-    : obj_num_(obj_stream->GetObjNum()),
+CPDF_ObjectStream::CPDF_ObjectStream(RetainPtr<const CPDF_Stream> obj_stream)
+    : stream_acc_(pdfium::MakeRetain<CPDF_StreamAcc>(obj_stream)),
       first_object_offset_(obj_stream->GetDict()->GetIntegerFor("First")) {
-  ASSERT(IsObjectsStreamObject(obj_stream));
-  if (const auto* extends_ref =
-          ToReference(obj_stream->GetDict()->GetObjectFor("Extends"))) {
-    extends_obj_num_ = extends_ref->GetRefObjNum();
-  }
-  Init(obj_stream);
+  DCHECK(IsObjectStream(obj_stream.Get()));
+  Init(obj_stream.Get());
 }
 
 CPDF_ObjectStream::~CPDF_ObjectStream() = default;
 
-bool CPDF_ObjectStream::HasObject(uint32_t obj_number) const {
-  return pdfium::ContainsKey(objects_offsets_, obj_number);
-}
-
 RetainPtr<CPDF_Object> CPDF_ObjectStream::ParseObject(
     CPDF_IndirectObjectHolder* pObjList,
-    uint32_t obj_number) const {
-  const auto it = objects_offsets_.find(obj_number);
-  if (it == objects_offsets_.end())
+    uint32_t obj_number,
+    uint32_t archive_obj_index) const {
+  if (archive_obj_index >= object_info_.size())
     return nullptr;
 
-  RetainPtr<CPDF_Object> result = ParseObjectAtOffset(pObjList, it->second);
-  if (!result)
+  const auto& info = object_info_[archive_obj_index];
+  if (info.obj_num != obj_number)
     return nullptr;
 
-  result->SetObjNum(obj_number);
+  RetainPtr<CPDF_Object> result =
+      ParseObjectAtOffset(pObjList, info.obj_offset);
+  if (result)
+    result->SetObjNum(obj_number);
   return result;
 }
 
 void CPDF_ObjectStream::Init(const CPDF_Stream* stream) {
-  {
-    auto stream_acc = pdfium::MakeRetain<CPDF_StreamAcc>(stream);
-    stream_acc->LoadAllDataFiltered();
-    const uint32_t data_size = stream_acc->GetSize();
-    data_stream_ = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-        stream_acc->DetachData(), data_size);
-  }
+  stream_acc_->LoadAllDataFiltered();
+  data_stream_ =
+      pdfium::MakeRetain<CFX_ReadOnlySpanStream>(stream_acc_->GetSpan());
 
   CPDF_SyntaxParser syntax(data_stream_);
   const int object_count = stream->GetDict()->GetIntegerFor("N");
@@ -112,7 +105,7 @@
     if (!obj_num)
       continue;
 
-    objects_offsets_[obj_num] = obj_offset;
+    object_info_.emplace_back(obj_num, obj_offset);
   }
 }
 
diff --git a/core/fpdfapi/parser/cpdf_object_stream.h b/core/fpdfapi/parser/cpdf_object_stream.h
index 2fa1634..cfa0737 100644
--- a/core/fpdfapi/parser/cpdf_object_stream.h
+++ b/core/fpdfapi/parser/cpdf_object_stream.h
@@ -1,54 +1,60 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef CORE_FPDFAPI_PARSER_CPDF_OBJECT_STREAM_H_
 #define CORE_FPDFAPI_PARSER_CPDF_OBJECT_STREAM_H_
 
-#include <map>
 #include <memory>
+#include <vector>
 
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fxcrt/retain_ptr.h"
 
 class CPDF_IndirectObjectHolder;
 class CPDF_Stream;
+class CPDF_StreamAcc;
 class IFX_SeekableReadStream;
 
 // Implementation of logic of PDF "Object Streams".
-// See "PDF 32000-1:2008" Spec. section 7.5.7.
+// See ISO 32000-1:2008 spec, section 7.5.7.
 class CPDF_ObjectStream {
  public:
-  static bool IsObjectsStreamObject(const CPDF_Object* object);
+  struct ObjectInfo {
+    ObjectInfo(uint32_t obj_num, uint32_t obj_offset)
+        : obj_num(obj_num), obj_offset(obj_offset) {}
 
-  static std::unique_ptr<CPDF_ObjectStream> Create(const CPDF_Stream* stream);
+    bool operator==(const ObjectInfo& that) const {
+      return obj_num == that.obj_num && obj_offset == that.obj_offset;
+    }
+
+    uint32_t obj_num;
+    uint32_t obj_offset;
+  };
+
+  static std::unique_ptr<CPDF_ObjectStream> Create(
+      RetainPtr<const CPDF_Stream> stream);
 
   ~CPDF_ObjectStream();
 
-  uint32_t obj_num() const { return obj_num_; }
-  uint32_t extends_obj_num() const { return extends_obj_num_; }
-
-  bool HasObject(uint32_t obj_number) const;
   RetainPtr<CPDF_Object> ParseObject(CPDF_IndirectObjectHolder* pObjList,
-                                     uint32_t obj_number) const;
-  const std::map<uint32_t, uint32_t>& objects_offsets() const {
-    return objects_offsets_;
-  }
+                                     uint32_t obj_number,
+                                     uint32_t archive_obj_index) const;
+  const std::vector<ObjectInfo>& object_info() const { return object_info_; }
 
- protected:
-  explicit CPDF_ObjectStream(const CPDF_Stream* stream);
+ private:
+  explicit CPDF_ObjectStream(RetainPtr<const CPDF_Stream> stream);
 
   void Init(const CPDF_Stream* stream);
   RetainPtr<CPDF_Object> ParseObjectAtOffset(
       CPDF_IndirectObjectHolder* pObjList,
       uint32_t object_offset) const;
 
-  uint32_t obj_num_ = CPDF_Object::kInvalidObjNum;
-  uint32_t extends_obj_num_ = CPDF_Object::kInvalidObjNum;
-
+  // Must outlive `data_stream_`.
+  RetainPtr<CPDF_StreamAcc> const stream_acc_;
   RetainPtr<IFX_SeekableReadStream> data_stream_;
   int first_object_offset_ = 0;
-  std::map<uint32_t, uint32_t> objects_offsets_;
+  std::vector<ObjectInfo> object_info_;
 };
 
 #endif  // CORE_FPDFAPI_PARSER_CPDF_OBJECT_STREAM_H_
diff --git a/core/fpdfapi/parser/cpdf_object_stream_unittest.cpp b/core/fpdfapi/parser/cpdf_object_stream_unittest.cpp
new file mode 100644
index 0000000..43306f4
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_object_stream_unittest.cpp
@@ -0,0 +1,509 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/parser/cpdf_object_stream.h"
+
+#include <iterator>
+#include <utility>
+
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fxcrt/data_vector.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::ElementsAre;
+
+namespace {
+
+constexpr char kNormalStreamContent[] =
+    "10 0 11 14 12 21<</Name /Foo>>[1 2 3]4";
+constexpr int kNormalStreamContentOffset = 16;
+static_assert(kNormalStreamContent[kNormalStreamContentOffset] == '<',
+              "Wrong offset");
+static_assert(kNormalStreamContent[kNormalStreamContentOffset + 1] == '<',
+              "Wrong offset");
+
+}  // namespace
+
+TEST(ObjectStreamTest, StreamDictNormal) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("N", 3);
+  dict->SetNewFor<CPDF_Number>("First", kNormalStreamContentOffset);
+
+  ByteStringView contents_view(kNormalStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  auto obj_stream = CPDF_ObjectStream::Create(std::move(stream));
+  ASSERT_TRUE(obj_stream);
+
+  EXPECT_THAT(obj_stream->object_info(),
+              ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0),
+                          CPDF_ObjectStream::ObjectInfo(11, 14),
+                          CPDF_ObjectStream::ObjectInfo(12, 21)));
+
+  // Check expected indices.
+  CPDF_IndirectObjectHolder holder;
+  RetainPtr<CPDF_Object> obj10 = obj_stream->ParseObject(&holder, 10, 0);
+  ASSERT_TRUE(obj10);
+  EXPECT_EQ(10u, obj10->GetObjNum());
+  EXPECT_EQ(0u, obj10->GetGenNum());
+  EXPECT_TRUE(obj10->IsDictionary());
+
+  RetainPtr<CPDF_Object> obj11 = obj_stream->ParseObject(&holder, 11, 1);
+  ASSERT_TRUE(obj11);
+  EXPECT_EQ(11u, obj11->GetObjNum());
+  EXPECT_EQ(0u, obj11->GetGenNum());
+  EXPECT_TRUE(obj11->IsArray());
+
+  RetainPtr<CPDF_Object> obj12 = obj_stream->ParseObject(&holder, 12, 2);
+  ASSERT_TRUE(obj12);
+  EXPECT_EQ(12u, obj12->GetObjNum());
+  EXPECT_EQ(0u, obj12->GetGenNum());
+  EXPECT_TRUE(obj12->IsNumber());
+
+  // Check bad indices.
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 1));
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 2));
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 3));
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 0));
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 2));
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 3));
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 12, 0));
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 12, 1));
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 12, 3));
+}
+
+TEST(ObjectStreamTest, StreamNoDict) {
+  ByteStringView contents_view(kNormalStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()),
+      /*pDict=*/nullptr);
+  EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream)));
+}
+
+TEST(ObjectStreamTest, StreamDictNoType) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Number>("N", 3);
+  dict->SetNewFor<CPDF_Number>("First", 5);
+
+  ByteStringView contents_view(kNormalStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream)));
+}
+
+TEST(ObjectStreamTest, StreamDictWrongType) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_String>("Type", "ObjStm", /*bHex=*/false);
+  dict->SetNewFor<CPDF_Number>("N", 3);
+  dict->SetNewFor<CPDF_Number>("First", 5);
+
+  ByteStringView contents_view(kNormalStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream)));
+}
+
+TEST(ObjectStreamTest, StreamDictWrongTypeValue) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStmmmm");
+  dict->SetNewFor<CPDF_Number>("N", 3);
+  dict->SetNewFor<CPDF_Number>("First", 5);
+
+  ByteStringView contents_view(kNormalStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream)));
+}
+
+TEST(ObjectStreamTest, StreamDictNoCount) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("First", 5);
+
+  ByteStringView contents_view(kNormalStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream)));
+}
+
+TEST(ObjectStreamTest, StreamDictFloatCount) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("N", 2.2f);
+  dict->SetNewFor<CPDF_Number>("First", 5);
+
+  ByteStringView contents_view(kNormalStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream)));
+}
+
+TEST(ObjectStreamTest, StreamDictNegativeCount) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("N", -1);
+  dict->SetNewFor<CPDF_Number>("First", 5);
+
+  ByteStringView contents_view(kNormalStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream)));
+}
+
+TEST(ObjectStreamTest, StreamDictCountTooBig) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("N", 999999999);
+  dict->SetNewFor<CPDF_Number>("First", 5);
+
+  ByteStringView contents_view(kNormalStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream)));
+}
+
+TEST(ObjectStreamTest, StreamDictNoOffset) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("N", 3);
+
+  ByteStringView contents_view(kNormalStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream)));
+}
+
+TEST(ObjectStreamTest, StreamDictFloatOffset) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("N", 3);
+  dict->SetNewFor<CPDF_Number>("First", 5.5f);
+
+  ByteStringView contents_view(kNormalStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream)));
+}
+
+TEST(ObjectStreamTest, StreamDictNegativeOffset) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("N", 3);
+  dict->SetNewFor<CPDF_Number>("First", -5);
+
+  ByteStringView contents_view(kNormalStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream)));
+}
+
+TEST(ObjectStreamTest, StreamDictOffsetTooBig) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("N", 3);
+  constexpr int kTooBigOffset = std::size(kNormalStreamContent);
+  dict->SetNewFor<CPDF_Number>("First", kTooBigOffset);
+
+  ByteStringView contents_view(kNormalStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  auto obj_stream = CPDF_ObjectStream::Create(std::move(stream));
+  ASSERT_TRUE(obj_stream);
+
+  EXPECT_THAT(obj_stream->object_info(),
+              ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0),
+                          CPDF_ObjectStream::ObjectInfo(11, 14),
+                          CPDF_ObjectStream::ObjectInfo(12, 21)));
+
+  CPDF_IndirectObjectHolder holder;
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 0));
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 1));
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 12, 2));
+}
+
+TEST(ObjectStreamTest, StreamDictTooFewCount) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("N", 2);
+  dict->SetNewFor<CPDF_Number>("First", kNormalStreamContentOffset);
+
+  ByteStringView contents_view(kNormalStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  auto obj_stream = CPDF_ObjectStream::Create(std::move(stream));
+  ASSERT_TRUE(obj_stream);
+
+  EXPECT_THAT(obj_stream->object_info(),
+              ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0),
+                          CPDF_ObjectStream::ObjectInfo(11, 14)));
+
+  CPDF_IndirectObjectHolder holder;
+  RetainPtr<CPDF_Object> obj10 = obj_stream->ParseObject(&holder, 10, 0);
+  ASSERT_TRUE(obj10);
+  EXPECT_EQ(10u, obj10->GetObjNum());
+  EXPECT_EQ(0u, obj10->GetGenNum());
+  EXPECT_TRUE(obj10->IsDictionary());
+
+  RetainPtr<CPDF_Object> obj11 = obj_stream->ParseObject(&holder, 11, 1);
+  ASSERT_TRUE(obj11);
+  EXPECT_EQ(11u, obj11->GetObjNum());
+  EXPECT_EQ(0u, obj11->GetGenNum());
+  EXPECT_TRUE(obj11->IsArray());
+
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 12, 2));
+}
+
+TEST(ObjectStreamTest, StreamDictTooManyObject) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("N", 9);
+  dict->SetNewFor<CPDF_Number>("First", kNormalStreamContentOffset);
+
+  ByteStringView contents_view(kNormalStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  auto obj_stream = CPDF_ObjectStream::Create(std::move(stream));
+  ASSERT_TRUE(obj_stream);
+
+  // TODO(thestig): Can this avoid finding object 2?
+  EXPECT_THAT(obj_stream->object_info(),
+              ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0),
+                          CPDF_ObjectStream::ObjectInfo(11, 14),
+                          CPDF_ObjectStream::ObjectInfo(12, 21),
+                          CPDF_ObjectStream::ObjectInfo(2, 3)));
+
+  CPDF_IndirectObjectHolder holder;
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 2, 0));
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 2, 1));
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 2, 2));
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 2, 3));
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 2, 4));
+}
+
+TEST(ObjectStreamTest, StreamDictGarbageObjNum) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("N", 3);
+  dict->SetNewFor<CPDF_Number>("First", 19);
+
+  const char kStreamContent[] = "10 0 hi 14 12 21<</Name /Foo>>[1 2 3]4";
+  ByteStringView contents_view(kStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  auto obj_stream = CPDF_ObjectStream::Create(std::move(stream));
+  ASSERT_TRUE(obj_stream);
+
+  EXPECT_THAT(obj_stream->object_info(),
+              ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0),
+                          CPDF_ObjectStream::ObjectInfo(12, 21)));
+}
+
+TEST(ObjectStreamTest, StreamDictGarbageObjectOffset) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("N", 3);
+  dict->SetNewFor<CPDF_Number>("First", 16);
+
+  const char kStreamContent[] = "10 0 11 hi 12 21<</Name /Foo>>[1 2 3]4";
+  ByteStringView contents_view(kStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  auto obj_stream = CPDF_ObjectStream::Create(std::move(stream));
+  ASSERT_TRUE(obj_stream);
+
+  // TODO(thestig): Should object 11 be rejected?
+  EXPECT_THAT(obj_stream->object_info(),
+              ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0),
+                          CPDF_ObjectStream::ObjectInfo(11, 0),
+                          CPDF_ObjectStream::ObjectInfo(12, 21)));
+
+  CPDF_IndirectObjectHolder holder;
+  RetainPtr<CPDF_Object> obj10 = obj_stream->ParseObject(&holder, 10, 0);
+  ASSERT_TRUE(obj10);
+  EXPECT_EQ(10u, obj10->GetObjNum());
+  EXPECT_EQ(0u, obj10->GetGenNum());
+  EXPECT_TRUE(obj10->IsDictionary());
+
+  RetainPtr<CPDF_Object> obj11 = obj_stream->ParseObject(&holder, 11, 1);
+  ASSERT_TRUE(obj11);
+  EXPECT_EQ(11u, obj11->GetObjNum());
+  EXPECT_EQ(0u, obj11->GetGenNum());
+  EXPECT_TRUE(obj11->IsDictionary());
+}
+
+TEST(ObjectStreamTest, StreamDictNegativeObjectOffset) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("N", 3);
+  dict->SetNewFor<CPDF_Number>("First", 16);
+
+  const char kStreamContent[] = "10 0 11 -1 12 21<</Name /Foo>>[1 2 3]4";
+  ByteStringView contents_view(kStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  auto obj_stream = CPDF_ObjectStream::Create(std::move(stream));
+  ASSERT_TRUE(obj_stream);
+
+  // TODO(thestig): Should object 11 be rejected?
+  EXPECT_THAT(obj_stream->object_info(),
+              ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0),
+                          CPDF_ObjectStream::ObjectInfo(11, 4294967295),
+                          CPDF_ObjectStream::ObjectInfo(12, 21)));
+
+  CPDF_IndirectObjectHolder holder;
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 1));
+}
+
+TEST(ObjectStreamTest, StreamDictObjectOffsetTooBig) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("N", 3);
+  dict->SetNewFor<CPDF_Number>("First", 17);
+
+  const char kStreamContent[] = "10 0 11 999 12 21<</Name /Foo>>[1 2 3]4";
+  ByteStringView contents_view(kStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  auto obj_stream = CPDF_ObjectStream::Create(std::move(stream));
+  ASSERT_TRUE(obj_stream);
+
+  // TODO(thestig): Should object 11 be rejected?
+  EXPECT_THAT(obj_stream->object_info(),
+              ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0),
+                          CPDF_ObjectStream::ObjectInfo(11, 999),
+                          CPDF_ObjectStream::ObjectInfo(12, 21)));
+
+  CPDF_IndirectObjectHolder holder;
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 1));
+}
+
+TEST(ObjectStreamTest, StreamDictDuplicateObjNum) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("N", 3);
+  dict->SetNewFor<CPDF_Number>("First", 16);
+
+  const char kStreamContent[] = "10 0 10 14 12 21<</Name /Foo>>[1 2 3]4";
+  ByteStringView contents_view(kStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  auto obj_stream = CPDF_ObjectStream::Create(std::move(stream));
+  ASSERT_TRUE(obj_stream);
+
+  EXPECT_THAT(obj_stream->object_info(),
+              ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0),
+                          CPDF_ObjectStream::ObjectInfo(10, 14),
+                          CPDF_ObjectStream::ObjectInfo(12, 21)));
+
+  CPDF_IndirectObjectHolder holder;
+  RetainPtr<CPDF_Object> obj10 = obj_stream->ParseObject(&holder, 10, 0);
+  ASSERT_TRUE(obj10);
+  EXPECT_EQ(10u, obj10->GetObjNum());
+  EXPECT_EQ(0u, obj10->GetGenNum());
+  EXPECT_TRUE(obj10->IsDictionary());
+
+  obj10 = obj_stream->ParseObject(&holder, 10, 1);
+  ASSERT_TRUE(obj10);
+  EXPECT_EQ(10u, obj10->GetObjNum());
+  EXPECT_EQ(0u, obj10->GetGenNum());
+  EXPECT_TRUE(obj10->IsArray());
+
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 2));
+  EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 3));
+
+  RetainPtr<CPDF_Object> obj12 = obj_stream->ParseObject(&holder, 12, 2);
+  ASSERT_TRUE(obj12);
+  EXPECT_EQ(12u, obj12->GetObjNum());
+  EXPECT_EQ(0u, obj12->GetGenNum());
+  EXPECT_TRUE(obj12->IsNumber());
+}
+
+TEST(ObjectStreamTest, StreamDictUnorderedObjectNumbers) {
+  // ISO 32000-1:2008 spec. section 7.5.7, note 6 says there is no restriction
+  // on object number ordering.
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("N", 3);
+  dict->SetNewFor<CPDF_Number>("First", 16);
+
+  const char kStreamContent[] = "11 0 12 14 10 21<</Name /Foo>>[1 2 3]4";
+  ByteStringView contents_view(kStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  auto obj_stream = CPDF_ObjectStream::Create(std::move(stream));
+  ASSERT_TRUE(obj_stream);
+
+  EXPECT_THAT(obj_stream->object_info(),
+              ElementsAre(CPDF_ObjectStream::ObjectInfo(11, 0),
+                          CPDF_ObjectStream::ObjectInfo(12, 14),
+                          CPDF_ObjectStream::ObjectInfo(10, 21)));
+
+  CPDF_IndirectObjectHolder holder;
+  RetainPtr<CPDF_Object> obj10 = obj_stream->ParseObject(&holder, 10, 2);
+  ASSERT_TRUE(obj10);
+  EXPECT_EQ(10u, obj10->GetObjNum());
+  EXPECT_EQ(0u, obj10->GetGenNum());
+  EXPECT_TRUE(obj10->IsNumber());
+
+  RetainPtr<CPDF_Object> obj11 = obj_stream->ParseObject(&holder, 11, 0);
+  ASSERT_TRUE(obj11);
+  EXPECT_EQ(11u, obj11->GetObjNum());
+  EXPECT_EQ(0u, obj11->GetGenNum());
+  EXPECT_TRUE(obj11->IsDictionary());
+
+  RetainPtr<CPDF_Object> obj12 = obj_stream->ParseObject(&holder, 12, 1);
+  ASSERT_TRUE(obj12);
+  EXPECT_EQ(12u, obj12->GetObjNum());
+  EXPECT_EQ(0u, obj12->GetGenNum());
+  EXPECT_TRUE(obj12->IsArray());
+}
+
+TEST(ObjectStreamTest, StreamDictUnorderedObjectOffsets) {
+  // ISO 32000-1:2008 spec. section 7.5.7, says offsets shall be in increasing
+  // order.
+  // TODO(thestig): Should CPDF_ObjectStream check for this and reject this
+  // object stream?
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+  dict->SetNewFor<CPDF_Number>("N", 3);
+  dict->SetNewFor<CPDF_Number>("First", 16);
+
+  const char kStreamContent[] = "10 21 11 0 12 14<</Name /Foo>>[1 2 3]4";
+  ByteStringView contents_view(kStreamContent);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(contents_view.begin(), contents_view.end()), dict);
+  auto obj_stream = CPDF_ObjectStream::Create(std::move(stream));
+  ASSERT_TRUE(obj_stream);
+
+  EXPECT_THAT(obj_stream->object_info(),
+              ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 21),
+                          CPDF_ObjectStream::ObjectInfo(11, 0),
+                          CPDF_ObjectStream::ObjectInfo(12, 14)));
+
+  CPDF_IndirectObjectHolder holder;
+  RetainPtr<CPDF_Object> obj10 = obj_stream->ParseObject(&holder, 10, 0);
+  ASSERT_TRUE(obj10);
+  EXPECT_EQ(10u, obj10->GetObjNum());
+  EXPECT_EQ(0u, obj10->GetGenNum());
+  EXPECT_TRUE(obj10->IsNumber());
+
+  RetainPtr<CPDF_Object> obj11 = obj_stream->ParseObject(&holder, 11, 1);
+  ASSERT_TRUE(obj11);
+  EXPECT_EQ(11u, obj11->GetObjNum());
+  EXPECT_EQ(0u, obj11->GetGenNum());
+  EXPECT_TRUE(obj11->IsDictionary());
+
+  RetainPtr<CPDF_Object> obj12 = obj_stream->ParseObject(&holder, 12, 2);
+  ASSERT_TRUE(obj12);
+  EXPECT_EQ(12u, obj12->GetObjNum());
+  EXPECT_EQ(0u, obj12->GetGenNum());
+  EXPECT_TRUE(obj12->IsArray());
+}
diff --git a/core/fpdfapi/parser/cpdf_object_unittest.cpp b/core/fpdfapi/parser/cpdf_object_unittest.cpp
index ca02618..a57b85e 100644
--- a/core/fpdfapi/parser/cpdf_object_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_object_unittest.cpp
@@ -1,7 +1,11 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "core/fpdfapi/parser/cpdf_object.h"
+
+#include <stdint.h>
+
 #include <memory>
 #include <string>
 #include <utility>
@@ -19,9 +23,9 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -34,9 +38,9 @@
                         CPDF_Array* arr_val,
                         CPDF_Dictionary* dict_val,
                         CPDF_Stream* stream_val) {
-  EXPECT_STREQ(str_val, arr->GetStringAt(index).c_str());
+  EXPECT_STREQ(str_val, arr->GetByteStringAt(index).c_str());
   EXPECT_EQ(int_val, arr->GetIntegerAt(index));
-  EXPECT_EQ(float_val, arr->GetNumberAt(index));
+  EXPECT_EQ(float_val, arr->GetFloatAt(index));
   EXPECT_EQ(arr_val, arr->GetArrayAt(index));
   EXPECT_EQ(dict_val, arr->GetDictAt(index));
   EXPECT_EQ(stream_val, arr->GetStreamAt(index));
@@ -69,16 +73,14 @@
     m_DictObj->SetNewFor<CPDF_Boolean>("bool", false);
     m_DictObj->SetNewFor<CPDF_Number>("num", 0.23f);
     // Stream object.
-    const char content[] = "abcdefghijklmnopqrstuvwxyz";
-    size_t buf_len = FX_ArraySize(content);
-    std::unique_ptr<uint8_t, FxFreeDeleter> buf(FX_Alloc(uint8_t, buf_len));
-    memcpy(buf.get(), content, buf_len);
+    static constexpr char kContents[] = "abcdefghijklmnopqrstuvwxyz";
     auto pNewDict = pdfium::MakeRetain<CPDF_Dictionary>();
     m_StreamDictObj = pNewDict;
     m_StreamDictObj->SetNewFor<CPDF_String>("key1", L" test dict");
     m_StreamDictObj->SetNewFor<CPDF_Number>("key2", -1);
-    auto stream_obj = pdfium::MakeRetain<CPDF_Stream>(std::move(buf), buf_len,
-                                                      std::move(pNewDict));
+    auto stream_obj = pdfium::MakeRetain<CPDF_Stream>(
+        DataVector<uint8_t>(std::begin(kContents), std::end(kContents)),
+        std::move(pNewDict));
     // Null Object.
     auto null_obj = pdfium::MakeRetain<CPDF_Null>();
     // All direct objects.
@@ -92,21 +94,22 @@
         CPDF_Object::kNumber,  CPDF_Object::kString,  CPDF_Object::kString,
         CPDF_Object::kName,    CPDF_Object::kArray,   CPDF_Object::kDictionary,
         CPDF_Object::kStream,  CPDF_Object::kNullobj};
-    for (size_t i = 0; i < FX_ArraySize(objs); ++i)
+    for (size_t i = 0; i < std::size(objs); ++i)
       m_DirectObjs.emplace_back(objs[i]);
 
     // Indirect references to indirect objects.
-    m_ObjHolder = pdfium::MakeUnique<CPDF_IndirectObjectHolder>();
-    m_IndirectObjs = {m_ObjHolder->AddIndirectObject(boolean_true_obj->Clone()),
-                      m_ObjHolder->AddIndirectObject(number_int_obj->Clone()),
-                      m_ObjHolder->AddIndirectObject(str_spec_obj->Clone()),
-                      m_ObjHolder->AddIndirectObject(name_obj->Clone()),
-                      m_ObjHolder->AddIndirectObject(m_ArrayObj->Clone()),
-                      m_ObjHolder->AddIndirectObject(m_DictObj->Clone()),
-                      m_ObjHolder->AddIndirectObject(stream_obj->Clone())};
-    for (CPDF_Object* pObj : m_IndirectObjs) {
-      m_RefObjs.emplace_back(pdfium::MakeRetain<CPDF_Reference>(
-          m_ObjHolder.get(), pObj->GetObjNum()));
+    m_ObjHolder = std::make_unique<CPDF_IndirectObjectHolder>();
+    m_IndirectObjNums = {
+        m_ObjHolder->AddIndirectObject(boolean_true_obj->Clone()),
+        m_ObjHolder->AddIndirectObject(number_int_obj->Clone()),
+        m_ObjHolder->AddIndirectObject(str_spec_obj->Clone()),
+        m_ObjHolder->AddIndirectObject(name_obj->Clone()),
+        m_ObjHolder->AddIndirectObject(m_ArrayObj->Clone()),
+        m_ObjHolder->AddIndirectObject(m_DictObj->Clone()),
+        m_ObjHolder->AddIndirectObject(stream_obj->Clone())};
+    for (uint32_t objnum : m_IndirectObjNums) {
+      m_RefObjs.emplace_back(
+          pdfium::MakeRetain<CPDF_Reference>(m_ObjHolder.get(), objnum));
     }
   }
 
@@ -130,8 +133,10 @@
         if (array1->size() != array2->size())
           return false;
         for (size_t i = 0; i < array1->size(); ++i) {
-          if (!Equal(array1->GetObjectAt(i), array2->GetObjectAt(i)))
+          if (!Equal(array1->GetObjectAt(i).Get(),
+                     array2->GetObjectAt(i).Get())) {
             return false;
+          }
         }
         return true;
       }
@@ -142,7 +147,7 @@
           return false;
         CPDF_DictionaryLocker locker1(dict1);
         for (const auto& item : locker1) {
-          if (!Equal(item.second.Get(), dict2->GetObjectFor(item.first)))
+          if (!Equal(item.second.Get(), dict2->GetObjectFor(item.first).Get()))
             return false;
         }
         return true;
@@ -150,25 +155,28 @@
       case CPDF_Object::kNullobj:
         return true;
       case CPDF_Object::kStream: {
-        const CPDF_Stream* stream1 = obj1->AsStream();
-        const CPDF_Stream* stream2 = obj2->AsStream();
+        RetainPtr<const CPDF_Stream> stream1(obj1->AsStream());
+        RetainPtr<const CPDF_Stream> stream2(obj2->AsStream());
         if (!stream1->GetDict() && !stream2->GetDict())
           return true;
         // Compare dictionaries.
-        if (!Equal(stream1->GetDict(), stream2->GetDict()))
+        if (!Equal(stream1->GetDict().Get(), stream2->GetDict().Get()))
           return false;
 
-        auto streamAcc1 = pdfium::MakeRetain<CPDF_StreamAcc>(stream1);
+        auto streamAcc1 =
+            pdfium::MakeRetain<CPDF_StreamAcc>(std::move(stream1));
         streamAcc1->LoadAllDataRaw();
-        auto streamAcc2 = pdfium::MakeRetain<CPDF_StreamAcc>(stream2);
+        auto streamAcc2 =
+            pdfium::MakeRetain<CPDF_StreamAcc>(std::move(stream2));
         streamAcc2->LoadAllDataRaw();
+        pdfium::span<const uint8_t> span1 = streamAcc1->GetSpan();
+        pdfium::span<const uint8_t> span2 = streamAcc2->GetSpan();
 
         // Compare sizes.
-        if (streamAcc1->GetSize() != streamAcc2->GetSize())
+        if (span1.size() != span2.size())
           return false;
 
-        return memcmp(streamAcc1->GetData(), streamAcc2->GetData(),
-                      streamAcc2->GetSize()) == 0;
+        return memcmp(span1.data(), span2.data(), span2.size()) == 0;
       }
       case CPDF_Object::kReference:
         return obj1->AsReference()->GetRefObjNum() ==
@@ -187,7 +195,7 @@
   RetainPtr<CPDF_Dictionary> m_DictObj;
   RetainPtr<CPDF_Dictionary> m_StreamDictObj;
   RetainPtr<CPDF_Array> m_ArrayObj;
-  std::vector<CPDF_Object*> m_IndirectObjs;
+  std::vector<uint32_t> m_IndirectObjNums;
 };
 
 TEST_F(PDFObjectsTest, GetString) {
@@ -265,7 +273,24 @@
                                                          m_DictObj.Get(),
                                                          m_StreamDictObj.Get()};
   for (size_t i = 0; i < m_RefObjs.size(); ++i)
-    EXPECT_TRUE(Equal(indirect_obj_results[i], m_RefObjs[i]->GetDict()));
+    EXPECT_TRUE(Equal(indirect_obj_results[i], m_RefObjs[i]->GetDict().Get()));
+}
+
+TEST_F(PDFObjectsTest, GetNameFor) {
+  m_DictObj->SetNewFor<CPDF_String>("string", "ium", false);
+  m_DictObj->SetNewFor<CPDF_Name>("name", "Pdf");
+
+  EXPECT_STREQ("", m_DictObj->GetNameFor("invalid").c_str());
+  EXPECT_STREQ("", m_DictObj->GetNameFor("bool").c_str());
+  EXPECT_STREQ("", m_DictObj->GetNameFor("num").c_str());
+  EXPECT_STREQ("", m_DictObj->GetNameFor("string").c_str());
+  EXPECT_STREQ("Pdf", m_DictObj->GetNameFor("name").c_str());
+
+  EXPECT_STREQ("", m_DictObj->GetByteStringFor("invalid").c_str());
+  EXPECT_STREQ("false", m_DictObj->GetByteStringFor("bool").c_str());
+  EXPECT_STREQ("0.23", m_DictObj->GetByteStringFor("num").c_str());
+  EXPECT_STREQ("ium", m_DictObj->GetByteStringFor("string").c_str());
+  EXPECT_STREQ("Pdf", m_DictObj->GetByteStringFor("name").c_str());
 }
 
 TEST_F(PDFObjectsTest, GetArray) {
@@ -278,7 +303,7 @@
 
   // Check indirect references.
   for (const auto& it : m_RefObjs)
-    EXPECT_EQ(nullptr, it->AsArray());
+    EXPECT_FALSE(it->AsArray());
 }
 
 TEST_F(PDFObjectsTest, Clone) {
@@ -312,7 +337,7 @@
 
   // Check indirect references.
   for (size_t i = 0; i < m_RefObjs.size(); ++i)
-    EXPECT_EQ(m_IndirectObjs[i], m_RefObjs[i]->GetDirect());
+    EXPECT_EQ(m_IndirectObjNums[i], m_RefObjs[i]->GetDirect()->GetObjNum());
 }
 
 TEST_F(PDFObjectsTest, SetString) {
@@ -321,7 +346,7 @@
                                     "changed", "",     "NewName"};
   const char* const expected[] = {"true",    "false", "3.125",  "97",
                                   "changed", "",      "NewName"};
-  for (size_t i = 0; i < FX_ArraySize(set_values); ++i) {
+  for (size_t i = 0; i < std::size(set_values); ++i) {
     m_DirectObjs[i]->SetString(set_values[i]);
     EXPECT_STREQ(expected[i], m_DirectObjs[i]->GetString().c_str());
   }
@@ -335,7 +360,7 @@
       EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsArray());
     } else {
       EXPECT_FALSE(m_DirectObjs[i]->IsArray());
-      EXPECT_EQ(nullptr, m_DirectObjs[i]->AsArray());
+      EXPECT_FALSE(m_DirectObjs[i]->AsArray());
     }
 
     if (m_DirectObjTypes[i] == CPDF_Object::kBoolean) {
@@ -343,7 +368,7 @@
       EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsBoolean());
     } else {
       EXPECT_FALSE(m_DirectObjs[i]->IsBoolean());
-      EXPECT_EQ(nullptr, m_DirectObjs[i]->AsBoolean());
+      EXPECT_FALSE(m_DirectObjs[i]->AsBoolean());
     }
 
     if (m_DirectObjTypes[i] == CPDF_Object::kName) {
@@ -351,7 +376,7 @@
       EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsName());
     } else {
       EXPECT_FALSE(m_DirectObjs[i]->IsName());
-      EXPECT_EQ(nullptr, m_DirectObjs[i]->AsName());
+      EXPECT_FALSE(m_DirectObjs[i]->AsName());
     }
 
     if (m_DirectObjTypes[i] == CPDF_Object::kNumber) {
@@ -359,7 +384,7 @@
       EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsNumber());
     } else {
       EXPECT_FALSE(m_DirectObjs[i]->IsNumber());
-      EXPECT_EQ(nullptr, m_DirectObjs[i]->AsNumber());
+      EXPECT_FALSE(m_DirectObjs[i]->AsNumber());
     }
 
     if (m_DirectObjTypes[i] == CPDF_Object::kString) {
@@ -367,7 +392,7 @@
       EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsString());
     } else {
       EXPECT_FALSE(m_DirectObjs[i]->IsString());
-      EXPECT_EQ(nullptr, m_DirectObjs[i]->AsString());
+      EXPECT_FALSE(m_DirectObjs[i]->AsString());
     }
 
     if (m_DirectObjTypes[i] == CPDF_Object::kDictionary) {
@@ -375,7 +400,7 @@
       EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsDictionary());
     } else {
       EXPECT_FALSE(m_DirectObjs[i]->IsDictionary());
-      EXPECT_EQ(nullptr, m_DirectObjs[i]->AsDictionary());
+      EXPECT_FALSE(m_DirectObjs[i]->AsDictionary());
     }
 
     if (m_DirectObjTypes[i] == CPDF_Object::kStream) {
@@ -383,11 +408,11 @@
       EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsStream());
     } else {
       EXPECT_FALSE(m_DirectObjs[i]->IsStream());
-      EXPECT_EQ(nullptr, m_DirectObjs[i]->AsStream());
+      EXPECT_FALSE(m_DirectObjs[i]->AsStream());
     }
 
     EXPECT_FALSE(m_DirectObjs[i]->IsReference());
-    EXPECT_EQ(nullptr, m_DirectObjs[i]->AsReference());
+    EXPECT_FALSE(m_DirectObjs[i]->AsReference());
   }
   // Check indirect references.
   for (size_t i = 0; i < m_RefObjs.size(); ++i) {
@@ -408,17 +433,33 @@
             ToReference(ref_obj.Get())->GetRefObjNum());
 }
 
+TEST_F(PDFObjectsTest, KeyForCache) {
+  std::set<uint64_t> key_set;
+
+  // Check all direct objects inserted without collision.
+  for (const auto& direct : m_DirectObjs) {
+    EXPECT_TRUE(key_set.insert(direct->KeyForCache()).second);
+  }
+  // Check indirect objects inserted without collision.
+  for (const auto& pair : *m_ObjHolder) {
+    EXPECT_TRUE(key_set.insert(pair.second->KeyForCache()).second);
+  }
+
+  // Check all expected objects counted.
+  EXPECT_EQ(18u, key_set.size());
+}
+
 TEST(PDFArrayTest, GetMatrix) {
   float elems[][6] = {{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
                       {1, 2, 3, 4, 5, 6},
                       {2.3f, 4.05f, 3, -2, -3, 0.0f},
                       {0.05f, 0.1f, 0.56f, 0.67f, 1.34f, 99.9f}};
-  for (size_t i = 0; i < FX_ArraySize(elems); ++i) {
+  for (size_t i = 0; i < std::size(elems); ++i) {
     auto arr = pdfium::MakeRetain<CPDF_Array>();
     CFX_Matrix matrix(elems[i][0], elems[i][1], elems[i][2], elems[i][3],
                       elems[i][4], elems[i][5]);
     for (size_t j = 0; j < 6; ++j)
-      arr->AddNew<CPDF_Number>(elems[i][j]);
+      arr->AppendNew<CPDF_Number>(elems[i][j]);
     CFX_Matrix arr_matrix = arr->GetMatrix();
     EXPECT_EQ(matrix.a, arr_matrix.a);
     EXPECT_EQ(matrix.b, arr_matrix.b);
@@ -434,11 +475,11 @@
                       {1, 2, 5, 6},
                       {2.3f, 4.05f, -3, 0.0f},
                       {0.05f, 0.1f, 1.34f, 99.9f}};
-  for (size_t i = 0; i < FX_ArraySize(elems); ++i) {
+  for (size_t i = 0; i < std::size(elems); ++i) {
     auto arr = pdfium::MakeRetain<CPDF_Array>();
-    CFX_FloatRect rect(elems[i]);
+    CFX_FloatRect rect(elems[i][0], elems[i][1], elems[i][2], elems[i][3]);
     for (size_t j = 0; j < 4; ++j)
-      arr->AddNew<CPDF_Number>(elems[i][j]);
+      arr->AppendNew<CPDF_Number>(elems[i][j]);
     CFX_FloatRect arr_rect = arr->GetRect();
     EXPECT_EQ(rect.left, arr_rect.left);
     EXPECT_EQ(rect.right, arr_rect.right);
@@ -452,9 +493,9 @@
     // Boolean array.
     const bool vals[] = {true, false, false, true, true};
     auto arr = pdfium::MakeRetain<CPDF_Array>();
-    for (size_t i = 0; i < FX_ArraySize(vals); ++i)
+    for (size_t i = 0; i < std::size(vals); ++i)
       arr->InsertNewAt<CPDF_Boolean>(i, vals[i]);
-    for (size_t i = 0; i < FX_ArraySize(vals); ++i) {
+    for (size_t i = 0; i < std::size(vals); ++i) {
       TestArrayAccessors(arr.Get(), i,                // Array and index.
                          vals[i] ? "true" : "false",  // String value.
                          nullptr,                     // Const string value.
@@ -469,9 +510,9 @@
     // Integer array.
     const int vals[] = {10, 0, -345, 2089345456, -1000000000, 567, 93658767};
     auto arr = pdfium::MakeRetain<CPDF_Array>();
-    for (size_t i = 0; i < FX_ArraySize(vals); ++i)
+    for (size_t i = 0; i < std::size(vals); ++i)
       arr->InsertNewAt<CPDF_Number>(i, vals[i]);
-    for (size_t i = 0; i < FX_ArraySize(vals); ++i) {
+    for (size_t i = 0; i < std::size(vals); ++i) {
       char buf[33];
       TestArrayAccessors(arr.Get(), i,                  // Array and index.
                          FXSYS_itoa(vals[i], buf, 10),  // String value.
@@ -490,9 +531,9 @@
     const char* const expected_str[] = {
         "0", "0", "10", "10", "0.0345", "897.34", "-2.5", "-1", "-345", "0"};
     auto arr = pdfium::MakeRetain<CPDF_Array>();
-    for (size_t i = 0; i < FX_ArraySize(vals); ++i)
+    for (size_t i = 0; i < std::size(vals); ++i)
       arr->InsertNewAt<CPDF_Number>(i, vals[i]);
-    for (size_t i = 0; i < FX_ArraySize(vals); ++i) {
+    for (size_t i = 0; i < std::size(vals); ++i) {
       TestArrayAccessors(arr.Get(), i,     // Array and index.
                          expected_str[i],  // String value.
                          nullptr,          // Const string value.
@@ -509,11 +550,11 @@
                                 ".",    "EYREW",    "It is a joke :)"};
     auto string_array = pdfium::MakeRetain<CPDF_Array>();
     auto name_array = pdfium::MakeRetain<CPDF_Array>();
-    for (size_t i = 0; i < FX_ArraySize(vals); ++i) {
+    for (size_t i = 0; i < std::size(vals); ++i) {
       string_array->InsertNewAt<CPDF_String>(i, vals[i], false);
       name_array->InsertNewAt<CPDF_Name>(i, vals[i]);
     }
-    for (size_t i = 0; i < FX_ArraySize(vals); ++i) {
+    for (size_t i = 0; i < std::size(vals); ++i) {
       TestArrayAccessors(string_array.Get(), i,  // Array and index.
                          vals[i],                // String value.
                          vals[i],                // Const string value.
@@ -550,32 +591,32 @@
   }
   {
     // Array of array.
-    CPDF_Array* vals[3];
+    RetainPtr<CPDF_Array> vals[3];
     auto arr = pdfium::MakeRetain<CPDF_Array>();
     for (size_t i = 0; i < 3; ++i) {
-      vals[i] = arr->AddNew<CPDF_Array>();
+      vals[i] = arr->AppendNew<CPDF_Array>();
       for (size_t j = 0; j < 3; ++j) {
         int value = j + 100;
-        vals[i]->InsertNewAt<CPDF_Number>(i, value);
+        vals[i]->InsertNewAt<CPDF_Number>(j, value);
       }
     }
     for (size_t i = 0; i < 3; ++i) {
-      TestArrayAccessors(arr.Get(), i,  // Array and index.
-                         "",            // String value.
-                         nullptr,       // Const string value.
-                         0,             // Integer value.
-                         0,             // Float value.
-                         vals[i],       // Array value.
-                         nullptr,       // Dictionary value.
-                         nullptr);      // Stream value.
+      TestArrayAccessors(arr.Get(), i,   // Array and index.
+                         "",             // String value.
+                         nullptr,        // Const string value.
+                         0,              // Integer value.
+                         0,              // Float value.
+                         vals[i].Get(),  // Array value.
+                         nullptr,        // Dictionary value.
+                         nullptr);       // Stream value.
     }
   }
   {
     // Dictionary array.
-    CPDF_Dictionary* vals[3];
+    RetainPtr<CPDF_Dictionary> vals[3];
     auto arr = pdfium::MakeRetain<CPDF_Array>();
     for (size_t i = 0; i < 3; ++i) {
-      vals[i] = arr->AddNew<CPDF_Dictionary>();
+      vals[i] = arr->AppendNew<CPDF_Dictionary>();
       for (size_t j = 0; j < 3; ++j) {
         std::string key("key");
         char buf[33];
@@ -585,20 +626,20 @@
       }
     }
     for (size_t i = 0; i < 3; ++i) {
-      TestArrayAccessors(arr.Get(), i,  // Array and index.
-                         "",            // String value.
-                         nullptr,       // Const string value.
-                         0,             // Integer value.
-                         0,             // Float value.
-                         nullptr,       // Array value.
-                         vals[i],       // Dictionary value.
-                         nullptr);      // Stream value.
+      TestArrayAccessors(arr.Get(), i,   // Array and index.
+                         "",             // String value.
+                         nullptr,        // Const string value.
+                         0,              // Integer value.
+                         0,              // Float value.
+                         nullptr,        // Array value.
+                         vals[i].Get(),  // Dictionary value.
+                         nullptr);       // Stream value.
     }
   }
   {
     // Stream array.
     RetainPtr<CPDF_Dictionary> vals[3];
-    CPDF_Stream* stream_vals[3];
+    RetainPtr<CPDF_Stream> stream_vals[3];
     auto arr = pdfium::MakeRetain<CPDF_Array>();
     for (size_t i = 0; i < 3; ++i) {
       vals[i] = pdfium::MakeRetain<CPDF_Dictionary>();
@@ -609,23 +650,20 @@
         int value = j + 200;
         vals[i]->SetNewFor<CPDF_Number>(key.c_str(), value);
       }
-      uint8_t content[] = "content: this is a stream";
-      size_t data_size = FX_ArraySize(content);
-      std::unique_ptr<uint8_t, FxFreeDeleter> data(
-          FX_Alloc(uint8_t, data_size));
-      memcpy(data.get(), content, data_size);
-      stream_vals[i] =
-          arr->AddNew<CPDF_Stream>(std::move(data), data_size, vals[i]);
+      static constexpr uint8_t kContents[] = "content: this is a stream";
+      stream_vals[i] = arr->AppendNew<CPDF_Stream>(
+          DataVector<uint8_t>(std::begin(kContents), std::end(kContents)),
+          vals[i]);
     }
     for (size_t i = 0; i < 3; ++i) {
-      TestArrayAccessors(arr.Get(), i,     // Array and index.
-                         "",               // String value.
-                         nullptr,          // Const string value.
-                         0,                // Integer value.
-                         0,                // Float value.
-                         nullptr,          // Array value.
-                         vals[i].Get(),    // Dictionary value.
-                         stream_vals[i]);  // Stream value.
+      TestArrayAccessors(arr.Get(), i,           // Array and index.
+                         "",                     // String value.
+                         nullptr,                // Const string value.
+                         0,                      // Integer value.
+                         0,                      // Float value.
+                         nullptr,                // Array value.
+                         vals[i].Get(),          // Dictionary value.
+                         stream_vals[i].Get());  // Stream value.
     }
   }
   {
@@ -643,25 +681,23 @@
     arr->InsertNewAt<CPDF_Name>(9, "test");
     arr->InsertNewAt<CPDF_Null>(10);
 
-    CPDF_Array* arr_val = arr->InsertNewAt<CPDF_Array>(11);
-    arr_val->AddNew<CPDF_Number>(1);
-    arr_val->AddNew<CPDF_Number>(2);
+    auto arr_val = arr->InsertNewAt<CPDF_Array>(11);
+    arr_val->AppendNew<CPDF_Number>(1);
+    arr_val->AppendNew<CPDF_Number>(2);
 
-    CPDF_Dictionary* dict_val = arr->InsertNewAt<CPDF_Dictionary>(12);
+    auto dict_val = arr->InsertNewAt<CPDF_Dictionary>(12);
     dict_val->SetNewFor<CPDF_String>("key1", "Linda", false);
     dict_val->SetNewFor<CPDF_String>("key2", "Zoe", false);
 
     auto stream_dict = pdfium::MakeRetain<CPDF_Dictionary>();
     stream_dict->SetNewFor<CPDF_String>("key1", "John", false);
     stream_dict->SetNewFor<CPDF_String>("key2", "King", false);
-    uint8_t data[] = "A stream for test";
+    static constexpr uint8_t kData[] = "A stream for test";
     // The data buffer will be owned by stream object, so it needs to be
     // dynamically allocated.
-    size_t buf_size = sizeof(data);
-    std::unique_ptr<uint8_t, FxFreeDeleter> buf(FX_Alloc(uint8_t, buf_size));
-    memcpy(buf.get(), data, buf_size);
     CPDF_Stream* stream_val = arr->InsertNewAt<CPDF_Stream>(
-        13, std::move(buf), buf_size, stream_dict);
+        13, DataVector<uint8_t>(std::begin(kData), std::end(kData)),
+        stream_dict);
     const char* const expected_str[] = {
         "true",          "false", "0",    "-1234", "2345", "0.05", "",
         "It is a test!", "NAME",  "test", "",      "",     "",     ""};
@@ -670,22 +706,22 @@
     const float expected_float[] = {0, 0, 0, -1234, 2345, 0.05f, 0,
                                     0, 0, 0, 0,     0,    0,     0};
     for (size_t i = 0; i < arr->size(); ++i) {
-      EXPECT_STREQ(expected_str[i], arr->GetStringAt(i).c_str());
+      EXPECT_STREQ(expected_str[i], arr->GetByteStringAt(i).c_str());
       EXPECT_EQ(expected_int[i], arr->GetIntegerAt(i));
-      EXPECT_EQ(expected_float[i], arr->GetNumberAt(i));
+      EXPECT_EQ(expected_float[i], arr->GetFloatAt(i));
       if (i == 11)
         EXPECT_EQ(arr_val, arr->GetArrayAt(i));
       else
-        EXPECT_EQ(nullptr, arr->GetArrayAt(i));
+        EXPECT_FALSE(arr->GetArrayAt(i));
       if (i == 13) {
         EXPECT_EQ(stream_dict, arr->GetDictAt(i));
         EXPECT_EQ(stream_val, arr->GetStreamAt(i));
       } else {
-        EXPECT_EQ(nullptr, arr->GetStreamAt(i));
+        EXPECT_FALSE(arr->GetStreamAt(i));
         if (i == 12)
           EXPECT_EQ(dict_val, arr->GetDictAt(i));
         else
-          EXPECT_EQ(nullptr, arr->GetDictAt(i));
+          EXPECT_FALSE(arr->GetDictAt(i));
       }
     }
   }
@@ -695,9 +731,9 @@
   float vals[] = {1.0f,         -1.0f, 0,    0.456734f,
                   12345.54321f, 0.5f,  1000, 0.000045f};
   auto arr = pdfium::MakeRetain<CPDF_Array>();
-  for (size_t i = 0; i < FX_ArraySize(vals); ++i)
-    arr->AddNew<CPDF_Number>(vals[i]);
-  for (size_t i = 0; i < FX_ArraySize(vals); ++i) {
+  for (size_t i = 0; i < std::size(vals); ++i)
+    arr->AppendNew<CPDF_Number>(vals[i]);
+  for (size_t i = 0; i < std::size(vals); ++i) {
     EXPECT_EQ(CPDF_Object::kNumber, arr->GetObjectAt(i)->GetType());
     EXPECT_EQ(vals[i], arr->GetObjectAt(i)->GetNumber());
   }
@@ -706,9 +742,9 @@
 TEST(PDFArrayTest, AddInteger) {
   int vals[] = {0, 1, 934435456, 876, 10000, -1, -24354656, -100};
   auto arr = pdfium::MakeRetain<CPDF_Array>();
-  for (size_t i = 0; i < FX_ArraySize(vals); ++i)
-    arr->AddNew<CPDF_Number>(vals[i]);
-  for (size_t i = 0; i < FX_ArraySize(vals); ++i) {
+  for (size_t i = 0; i < std::size(vals); ++i)
+    arr->AppendNew<CPDF_Number>(vals[i]);
+  for (size_t i = 0; i < std::size(vals); ++i) {
     EXPECT_EQ(CPDF_Object::kNumber, arr->GetObjectAt(i)->GetType());
     EXPECT_EQ(vals[i], arr->GetObjectAt(i)->GetNumber());
   }
@@ -721,10 +757,10 @@
   auto string_array = pdfium::MakeRetain<CPDF_Array>();
   auto name_array = pdfium::MakeRetain<CPDF_Array>();
   for (const char* val : kVals) {
-    string_array->AddNew<CPDF_String>(val, false);
-    name_array->AddNew<CPDF_Name>(val);
+    string_array->AppendNew<CPDF_String>(val, false);
+    name_array->AppendNew<CPDF_Name>(val);
   }
-  for (size_t i = 0; i < FX_ArraySize(kVals); ++i) {
+  for (size_t i = 0; i < std::size(kVals); ++i) {
     EXPECT_EQ(CPDF_Object::kString, string_array->GetObjectAt(i)->GetType());
     EXPECT_STREQ(kVals[i], string_array->GetObjectAt(i)->GetString().c_str());
     EXPECT_EQ(CPDF_Object::kName, name_array->GetObjectAt(i)->GetType());
@@ -733,7 +769,7 @@
 }
 
 TEST(PDFArrayTest, AddReferenceAndGetObjectAt) {
-  auto holder = pdfium::MakeUnique<CPDF_IndirectObjectHolder>();
+  auto holder = std::make_unique<CPDF_IndirectObjectHolder>();
   auto boolean_obj = pdfium::MakeRetain<CPDF_Boolean>(true);
   auto int_obj = pdfium::MakeRetain<CPDF_Number>(-1234);
   auto float_obj = pdfium::MakeRetain<CPDF_Number>(2345.089f);
@@ -747,14 +783,15 @@
   auto arr = pdfium::MakeRetain<CPDF_Array>();
   auto arr1 = pdfium::MakeRetain<CPDF_Array>();
   // Create two arrays of references by different AddReference() APIs.
-  for (size_t i = 0; i < FX_ArraySize(indirect_objs); ++i) {
+  for (size_t i = 0; i < std::size(indirect_objs); ++i) {
     holder->ReplaceIndirectObjectIfHigherGeneration(obj_nums[i],
                                                     indirect_objs[i]);
-    arr->AddNew<CPDF_Reference>(holder.get(), obj_nums[i]);
-    arr1->AddNew<CPDF_Reference>(holder.get(), indirect_objs[i]->GetObjNum());
+    arr->AppendNew<CPDF_Reference>(holder.get(), obj_nums[i]);
+    arr1->AppendNew<CPDF_Reference>(holder.get(),
+                                    indirect_objs[i]->GetObjNum());
   }
   // Check indirect objects.
-  for (size_t i = 0; i < FX_ArraySize(obj_nums); ++i)
+  for (size_t i = 0; i < std::size(obj_nums); ++i)
     EXPECT_EQ(indirect_objs[i], holder->GetOrParseIndirectObject(obj_nums[i]));
   // Check arrays.
   EXPECT_EQ(arr->size(), arr1->size());
@@ -771,9 +808,9 @@
 TEST(PDFArrayTest, CloneDirectObject) {
   CPDF_IndirectObjectHolder objects_holder;
   auto array = pdfium::MakeRetain<CPDF_Array>();
-  array->AddNew<CPDF_Reference>(&objects_holder, 1234);
+  array->AppendNew<CPDF_Reference>(&objects_holder, 1234);
   ASSERT_EQ(1U, array->size());
-  CPDF_Object* obj = array->GetObjectAt(0);
+  RetainPtr<const CPDF_Object> obj = array->GetObjectAt(0);
   ASSERT_TRUE(obj);
   EXPECT_TRUE(obj->IsReference());
 
@@ -783,17 +820,17 @@
 
   RetainPtr<CPDF_Array> cloned_array = ToArray(std::move(cloned_array_object));
   ASSERT_EQ(0U, cloned_array->size());
-  CPDF_Object* cloned_obj = cloned_array->GetObjectAt(0);
+  RetainPtr<const CPDF_Object> cloned_obj = cloned_array->GetObjectAt(0);
   EXPECT_FALSE(cloned_obj);
 }
 
 TEST(PDFArrayTest, ConvertIndirect) {
   CPDF_IndirectObjectHolder objects_holder;
   auto array = pdfium::MakeRetain<CPDF_Array>();
-  CPDF_Object* pObj = array->AddNew<CPDF_Number>(42);
+  auto pObj = array->AppendNew<CPDF_Number>(42);
   array->ConvertToIndirectObjectAt(0, &objects_holder);
-  CPDF_Object* pRef = array->GetObjectAt(0);
-  CPDF_Object* pNum = array->GetDirectObjectAt(0);
+  RetainPtr<const CPDF_Object> pRef = array->GetObjectAt(0);
+  RetainPtr<const CPDF_Object> pNum = array->GetDirectObjectAt(0);
   EXPECT_TRUE(pRef->IsReference());
   EXPECT_TRUE(pNum->IsNumber());
   EXPECT_NE(pObj, pRef);
@@ -802,18 +839,18 @@
 }
 
 TEST(PDFStreamTest, SetData) {
-  std::vector<uint8_t> data(100);
-  auto stream = pdfium::MakeRetain<CPDF_Stream>();
-  stream->InitStream(data, pdfium::MakeRetain<CPDF_Dictionary>());
+  DataVector<uint8_t> data(100);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      data, pdfium::MakeRetain<CPDF_Dictionary>());
   EXPECT_EQ(static_cast<int>(data.size()),
             stream->GetDict()->GetIntegerFor(pdfium::stream::kLength));
 
-  stream->GetDict()->SetNewFor<CPDF_String>(pdfium::stream::kFilter,
-                                            L"SomeFilter");
-  stream->GetDict()->SetNewFor<CPDF_String>(pdfium::stream::kDecodeParms,
-                                            L"SomeParams");
+  stream->GetMutableDict()->SetNewFor<CPDF_String>(pdfium::stream::kFilter,
+                                                   L"SomeFilter");
+  stream->GetMutableDict()->SetNewFor<CPDF_String>(pdfium::stream::kDecodeParms,
+                                                   L"SomeParams");
 
-  std::vector<uint8_t> new_data(data.size() * 2);
+  DataVector<uint8_t> new_data(data.size() * 2);
   stream->SetData(new_data);
 
   // The "Length" field should be updated for new data size.
@@ -828,18 +865,18 @@
 }
 
 TEST(PDFStreamTest, SetDataAndRemoveFilter) {
-  std::vector<uint8_t> data(100);
-  auto stream = pdfium::MakeRetain<CPDF_Stream>();
-  stream->InitStream(data, pdfium::MakeRetain<CPDF_Dictionary>());
+  DataVector<uint8_t> data(100);
+  auto stream = pdfium::MakeRetain<CPDF_Stream>(
+      data, pdfium::MakeRetain<CPDF_Dictionary>());
   EXPECT_EQ(static_cast<int>(data.size()),
             stream->GetDict()->GetIntegerFor(pdfium::stream::kLength));
 
-  stream->GetDict()->SetNewFor<CPDF_String>(pdfium::stream::kFilter,
-                                            L"SomeFilter");
-  stream->GetDict()->SetNewFor<CPDF_String>(pdfium::stream::kDecodeParms,
-                                            L"SomeParams");
+  stream->GetMutableDict()->SetNewFor<CPDF_String>(pdfium::stream::kFilter,
+                                                   L"SomeFilter");
+  stream->GetMutableDict()->SetNewFor<CPDF_String>(pdfium::stream::kDecodeParms,
+                                                   L"SomeParams");
 
-  std::vector<uint8_t> new_data(data.size() * 2);
+  DataVector<uint8_t> new_data(data.size() * 2);
   stream->SetDataAndRemoveFilter(new_data);
   // The "Length" field should be updated for new data size.
   EXPECT_EQ(static_cast<int>(new_data.size()),
@@ -854,20 +891,16 @@
   static constexpr uint32_t kBufSize = 100;
   // The length field should be created on stream create.
   {
-    std::unique_ptr<uint8_t, FxFreeDeleter> data;
-    data.reset(FX_Alloc(uint8_t, kBufSize));
     auto stream = pdfium::MakeRetain<CPDF_Stream>(
-        std::move(data), kBufSize, pdfium::MakeRetain<CPDF_Dictionary>());
+        DataVector<uint8_t>(kBufSize), pdfium::MakeRetain<CPDF_Dictionary>());
     EXPECT_EQ(static_cast<int>(kBufSize),
               stream->GetDict()->GetIntegerFor(pdfium::stream::kLength));
   }
   // The length field should be corrected on stream create.
   {
-    std::unique_ptr<uint8_t, FxFreeDeleter> data;
-    data.reset(FX_Alloc(uint8_t, kBufSize));
     auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
     dict->SetNewFor<CPDF_Number>(pdfium::stream::kLength, 30000);
-    auto stream = pdfium::MakeRetain<CPDF_Stream>(std::move(data), kBufSize,
+    auto stream = pdfium::MakeRetain<CPDF_Stream>(DataVector<uint8_t>(kBufSize),
                                                   std::move(dict));
     EXPECT_EQ(static_cast<int>(kBufSize),
               stream->GetDict()->GetIntegerFor(pdfium::stream::kLength));
@@ -879,7 +912,7 @@
   auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
   dict->SetNewFor<CPDF_Reference>("foo", &objects_holder, 1234);
   ASSERT_EQ(1U, dict->size());
-  CPDF_Object* obj = dict->GetObjectFor("foo");
+  RetainPtr<const CPDF_Object> obj = dict->GetObjectFor("foo");
   ASSERT_TRUE(obj);
   EXPECT_TRUE(obj->IsReference());
 
@@ -890,7 +923,7 @@
   RetainPtr<CPDF_Dictionary> cloned_dict =
       ToDictionary(std::move(cloned_dict_object));
   ASSERT_EQ(0U, cloned_dict->size());
-  CPDF_Object* cloned_obj = cloned_dict->GetObjectFor("foo");
+  RetainPtr<const CPDF_Object> cloned_obj = cloned_dict->GetObjectFor("foo");
   EXPECT_FALSE(cloned_obj);
 }
 
@@ -898,44 +931,43 @@
   {
     // Create a dictionary/array pair with a reference loop.
     auto arr_obj = pdfium::MakeRetain<CPDF_Array>();
-    CPDF_Dictionary* dict_obj = arr_obj->InsertNewAt<CPDF_Dictionary>(0);
+    auto dict_obj = arr_obj->InsertNewAt<CPDF_Dictionary>(0);
     dict_obj->SetFor("arr", arr_obj);
     // Clone this object to see whether stack overflow will be triggered.
     RetainPtr<CPDF_Array> cloned_array = ToArray(arr_obj->Clone());
     // Cloned object should be the same as the original.
     ASSERT_TRUE(cloned_array);
     EXPECT_EQ(1u, cloned_array->size());
-    CPDF_Object* cloned_dict = cloned_array->GetObjectAt(0);
+    RetainPtr<const CPDF_Object> cloned_dict = cloned_array->GetObjectAt(0);
     ASSERT_TRUE(cloned_dict);
     ASSERT_TRUE(cloned_dict->IsDictionary());
     // Recursively referenced object is not cloned.
-    EXPECT_EQ(nullptr, cloned_dict->AsDictionary()->GetObjectFor("arr"));
+    EXPECT_FALSE(cloned_dict->AsDictionary()->GetObjectFor("arr"));
     dict_obj->RemoveFor("arr");  // Break deliberate cycle for cleanup.
   }
   {
     // Create a dictionary/stream pair with a reference loop.
     auto dict_obj = pdfium::MakeRetain<CPDF_Dictionary>();
-    CPDF_Stream* stream_obj =
-        dict_obj->SetNewFor<CPDF_Stream>("stream", nullptr, 0, dict_obj);
+    auto stream_obj = dict_obj->SetNewFor<CPDF_Stream>("stream", dict_obj);
     // Clone this object to see whether stack overflow will be triggered.
     RetainPtr<CPDF_Stream> cloned_stream = ToStream(stream_obj->Clone());
     // Cloned object should be the same as the original.
     ASSERT_TRUE(cloned_stream);
-    CPDF_Object* cloned_dict = cloned_stream->GetDict();
+    RetainPtr<const CPDF_Object> cloned_dict = cloned_stream->GetDict();
     ASSERT_TRUE(cloned_dict);
     ASSERT_TRUE(cloned_dict->IsDictionary());
     // Recursively referenced object is not cloned.
-    EXPECT_EQ(nullptr, cloned_dict->AsDictionary()->GetObjectFor("stream"));
+    EXPECT_FALSE(cloned_dict->AsDictionary()->GetObjectFor("stream"));
     dict_obj->RemoveFor("stream");  // Break deliberate cycle for cleanup.
   }
   {
     CPDF_IndirectObjectHolder objects_holder;
     // Create an object with a reference loop.
-    CPDF_Dictionary* dict_obj = objects_holder.NewIndirect<CPDF_Dictionary>();
-    RetainPtr<CPDF_Array> arr_obj = pdfium::MakeRetain<CPDF_Array>();
+    auto dict_obj = objects_holder.NewIndirect<CPDF_Dictionary>();
+    auto arr_obj = pdfium::MakeRetain<CPDF_Array>();
     arr_obj->InsertNewAt<CPDF_Reference>(0, &objects_holder,
                                          dict_obj->GetObjNum());
-    CPDF_Object* elem0 = arr_obj->GetObjectAt(0);
+    RetainPtr<const CPDF_Object> elem0 = arr_obj->GetObjectAt(0);
     dict_obj->SetFor("arr", std::move(arr_obj));
     EXPECT_EQ(1u, dict_obj->GetObjNum());
     ASSERT_TRUE(elem0);
@@ -948,12 +980,12 @@
         ToDictionary(dict_obj->CloneDirectObject());
     // Cloned object should be the same as the original.
     ASSERT_TRUE(cloned_dict);
-    CPDF_Object* cloned_arr = cloned_dict->GetObjectFor("arr");
+    RetainPtr<const CPDF_Object> cloned_arr = cloned_dict->GetObjectFor("arr");
     ASSERT_TRUE(cloned_arr);
     ASSERT_TRUE(cloned_arr->IsArray());
     EXPECT_EQ(0U, cloned_arr->AsArray()->size());
     // Recursively referenced object is not cloned.
-    EXPECT_EQ(nullptr, cloned_arr->AsArray()->GetObjectAt(0));
+    EXPECT_FALSE(cloned_arr->AsArray()->GetObjectAt(0));
     dict_obj->RemoveFor("arr");  // Break deliberate cycle for cleanup.
   }
 }
@@ -961,10 +993,10 @@
 TEST(PDFDictionaryTest, ConvertIndirect) {
   CPDF_IndirectObjectHolder objects_holder;
   auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
-  CPDF_Object* pObj = dict->SetNewFor<CPDF_Number>("clams", 42);
+  auto pObj = dict->SetNewFor<CPDF_Number>("clams", 42);
   dict->ConvertToIndirectObjectFor("clams", &objects_holder);
-  CPDF_Object* pRef = dict->GetObjectFor("clams");
-  CPDF_Object* pNum = dict->GetDirectObjectFor("clams");
+  RetainPtr<const CPDF_Object> pRef = dict->GetObjectFor("clams");
+  RetainPtr<const CPDF_Object> pNum = dict->GetDirectObjectFor("clams");
   EXPECT_TRUE(pRef->IsReference());
   EXPECT_TRUE(pNum->IsNumber());
   EXPECT_NE(pObj, pRef);
@@ -974,7 +1006,7 @@
 
 TEST(PDFDictionaryTest, ExtractObjectOnRemove) {
   auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
-  CPDF_Object* pObj = dict->SetNewFor<CPDF_Number>("child", 42);
+  auto pObj = dict->SetNewFor<CPDF_Number>("child", 42);
   auto extracted_object = dict->RemoveFor("child");
   EXPECT_EQ(pObj, extracted_object.Get());
 
@@ -983,7 +1015,7 @@
 }
 
 TEST(PDFRefernceTest, MakeReferenceToReference) {
-  auto obj_holder = pdfium::MakeUnique<CPDF_IndirectObjectHolder>();
+  auto obj_holder = std::make_unique<CPDF_IndirectObjectHolder>();
   auto original_ref = pdfium::MakeRetain<CPDF_Reference>(obj_holder.get(), 42);
   original_ref->SetObjNum(1952);
   ASSERT_FALSE(original_ref->IsInline());
diff --git a/core/fpdfapi/parser/cpdf_object_walker.cpp b/core/fpdfapi/parser/cpdf_object_walker.cpp
index efd4a5c..71acdfb 100644
--- a/core/fpdfapi/parser/cpdf_object_walker.cpp
+++ b/core/fpdfapi/parser/cpdf_object_walker.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,21 +9,22 @@
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 namespace {
 
 class StreamIterator final : public CPDF_ObjectWalker::SubobjectIterator {
  public:
-  explicit StreamIterator(const CPDF_Stream* stream)
-      : SubobjectIterator(stream) {}
-  ~StreamIterator() override {}
+  explicit StreamIterator(RetainPtr<const CPDF_Stream> stream)
+      : SubobjectIterator(std::move(stream)) {}
+
+  ~StreamIterator() override = default;
 
   bool IsFinished() const override { return IsStarted() && is_finished_; }
 
-  const CPDF_Object* IncrementImpl() override {
-    ASSERT(IsStarted());
-    ASSERT(!IsFinished());
+  RetainPtr<const CPDF_Object> IncrementImpl() override {
+    DCHECK(IsStarted());
+    DCHECK(!IsFinished());
     is_finished_ = true;
     return object()->GetDict();
   }
@@ -36,25 +37,26 @@
 
 class DictionaryIterator final : public CPDF_ObjectWalker::SubobjectIterator {
  public:
-  explicit DictionaryIterator(const CPDF_Dictionary* dictionary)
+  explicit DictionaryIterator(RetainPtr<const CPDF_Dictionary> dictionary)
       : SubobjectIterator(dictionary), locker_(dictionary) {}
-  ~DictionaryIterator() override {}
+
+  ~DictionaryIterator() override = default;
 
   bool IsFinished() const override {
     return IsStarted() && dict_iterator_ == locker_.end();
   }
 
-  const CPDF_Object* IncrementImpl() override {
-    ASSERT(IsStarted());
-    ASSERT(!IsFinished());
-    const CPDF_Object* result = dict_iterator_->second.Get();
+  RetainPtr<const CPDF_Object> IncrementImpl() override {
+    DCHECK(IsStarted());
+    DCHECK(!IsFinished());
+    RetainPtr<const CPDF_Object> result = dict_iterator_->second;
     dict_key_ = dict_iterator_->first;
     ++dict_iterator_;
     return result;
   }
 
   void Start() override {
-    ASSERT(!IsStarted());
+    DCHECK(!IsStarted());
     dict_iterator_ = locker_.begin();
   }
 
@@ -68,19 +70,19 @@
 
 class ArrayIterator final : public CPDF_ObjectWalker::SubobjectIterator {
  public:
-  explicit ArrayIterator(const CPDF_Array* array)
+  explicit ArrayIterator(RetainPtr<const CPDF_Array> array)
       : SubobjectIterator(array), locker_(array) {}
 
-  ~ArrayIterator() override {}
+  ~ArrayIterator() override = default;
 
   bool IsFinished() const override {
     return IsStarted() && arr_iterator_ == locker_.end();
   }
 
-  const CPDF_Object* IncrementImpl() override {
-    ASSERT(IsStarted());
-    ASSERT(!IsFinished());
-    const CPDF_Object* result = arr_iterator_->Get();
+  RetainPtr<const CPDF_Object> IncrementImpl() override {
+    DCHECK(IsStarted());
+    DCHECK(!IsFinished());
+    RetainPtr<const CPDF_Object> result = *arr_iterator_;
     ++arr_iterator_;
     return result;
   }
@@ -94,15 +96,15 @@
 
 }  // namespace
 
-CPDF_ObjectWalker::SubobjectIterator::~SubobjectIterator() {}
+CPDF_ObjectWalker::SubobjectIterator::~SubobjectIterator() = default;
 
-const CPDF_Object* CPDF_ObjectWalker::SubobjectIterator::Increment() {
+RetainPtr<const CPDF_Object> CPDF_ObjectWalker::SubobjectIterator::Increment() {
   if (!IsStarted()) {
     Start();
     is_started_ = true;
   }
   while (!IsFinished()) {
-    const CPDF_Object* result = IncrementImpl();
+    RetainPtr<const CPDF_Object> result = IncrementImpl();
     if (result)
       return result;
   }
@@ -110,46 +112,44 @@
 }
 
 CPDF_ObjectWalker::SubobjectIterator::SubobjectIterator(
-    const CPDF_Object* object)
-    : object_(object) {
-  ASSERT(object_);
+    RetainPtr<const CPDF_Object> object)
+    : object_(std::move(object)) {
+  DCHECK(object_);
 }
 
 // static
 std::unique_ptr<CPDF_ObjectWalker::SubobjectIterator>
-CPDF_ObjectWalker::MakeIterator(const CPDF_Object* object) {
+CPDF_ObjectWalker::MakeIterator(RetainPtr<const CPDF_Object> object) {
   if (object->IsStream())
-    return pdfium::MakeUnique<StreamIterator>(object->AsStream());
+    return std::make_unique<StreamIterator>(ToStream(object));
   if (object->IsDictionary())
-    return pdfium::MakeUnique<DictionaryIterator>(object->AsDictionary());
+    return std::make_unique<DictionaryIterator>(ToDictionary(object));
   if (object->IsArray())
-    return pdfium::MakeUnique<ArrayIterator>(object->AsArray());
+    return std::make_unique<ArrayIterator>(ToArray(object));
   return nullptr;
 }
 
-CPDF_ObjectWalker::CPDF_ObjectWalker(const CPDF_Object* root)
-    : next_object_(root) {}
+CPDF_ObjectWalker::CPDF_ObjectWalker(RetainPtr<const CPDF_Object> root)
+    : next_object_(std::move(root)) {}
 
 CPDF_ObjectWalker::~CPDF_ObjectWalker() = default;
 
-const CPDF_Object* CPDF_ObjectWalker::GetNext() {
+RetainPtr<const CPDF_Object> CPDF_ObjectWalker::GetNext() {
   while (!stack_.empty() || next_object_) {
     if (next_object_) {
-      auto new_iterator = MakeIterator(next_object_.Get());
+      auto new_iterator = MakeIterator(next_object_);
       if (new_iterator) {
         // Schedule walk within composite objects.
         stack_.push(std::move(new_iterator));
       }
-      auto* result = next_object_.Get();
-      next_object_ = nullptr;
-      return result;
+      return std::move(next_object_);  // next_object_ is NULL after move.
     }
 
     SubobjectIterator* it = stack_.top().get();
     if (it->IsFinished()) {
       stack_.pop();
     } else {
-      next_object_.Reset(it->Increment());
+      next_object_ = it->Increment();
       parent_object_.Reset(it->object());
       dict_key_ = parent_object_->IsDictionary()
                       ? static_cast<DictionaryIterator*>(it)->dict_key()
@@ -167,3 +167,12 @@
     return;
   stack_.pop();
 }
+
+CPDF_NonConstObjectWalker::CPDF_NonConstObjectWalker(
+    RetainPtr<CPDF_Object> root)
+    : CPDF_ObjectWalker(std::move(root)) {}
+
+RetainPtr<CPDF_Object> CPDF_NonConstObjectWalker::GetNext() {
+  return pdfium::WrapRetain(
+      const_cast<CPDF_Object*>(CPDF_ObjectWalker::GetNext().Get()));
+}
diff --git a/core/fpdfapi/parser/cpdf_object_walker.h b/core/fpdfapi/parser/cpdf_object_walker.h
index d019286..c779da1 100644
--- a/core/fpdfapi/parser/cpdf_object_walker.h
+++ b/core/fpdfapi/parser/cpdf_object_walker.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,7 @@
 #include <memory>
 #include <stack>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Object;
@@ -22,13 +22,13 @@
     virtual ~SubobjectIterator();
     virtual bool IsFinished() const = 0;
     bool IsStarted() const { return is_started_; }
-    const CPDF_Object* Increment();
+    RetainPtr<const CPDF_Object> Increment();
     const CPDF_Object* object() const { return object_.Get(); }
 
    protected:
-    explicit SubobjectIterator(const CPDF_Object* object);
+    explicit SubobjectIterator(RetainPtr<const CPDF_Object> object);
 
-    virtual const CPDF_Object* IncrementImpl() = 0;
+    virtual RetainPtr<const CPDF_Object> IncrementImpl() = 0;
     virtual void Start() = 0;
 
    private:
@@ -36,10 +36,10 @@
     bool is_started_ = false;
   };
 
-  explicit CPDF_ObjectWalker(const CPDF_Object* root);
+  explicit CPDF_ObjectWalker(RetainPtr<const CPDF_Object> root);
   ~CPDF_ObjectWalker();
 
-  const CPDF_Object* GetNext();
+  RetainPtr<const CPDF_Object> GetNext();
   void SkipWalkIntoCurrentObject();
 
   size_t current_depth() const { return current_depth_; }
@@ -48,7 +48,7 @@
 
  private:
   static std::unique_ptr<SubobjectIterator> MakeIterator(
-      const CPDF_Object* object);
+      RetainPtr<const CPDF_Object> object);
 
   RetainPtr<const CPDF_Object> next_object_;
   RetainPtr<const CPDF_Object> parent_object_;
@@ -59,12 +59,8 @@
 
 class CPDF_NonConstObjectWalker final : public CPDF_ObjectWalker {
  public:
-  explicit CPDF_NonConstObjectWalker(CPDF_Object* root)
-      : CPDF_ObjectWalker(root) {}
-
-  CPDF_Object* GetNext() {
-    return const_cast<CPDF_Object*>(CPDF_ObjectWalker::GetNext());
-  }
+  explicit CPDF_NonConstObjectWalker(RetainPtr<CPDF_Object> root);
+  RetainPtr<CPDF_Object> GetNext();
 };
 
 #endif  // CORE_FPDFAPI_PARSER_CPDF_OBJECT_WALKER_H_
diff --git a/core/fpdfapi/parser/cpdf_object_walker_unittest.cpp b/core/fpdfapi/parser/cpdf_object_walker_unittest.cpp
index 4dde72f..ab0c2c3 100644
--- a/core/fpdfapi/parser/cpdf_object_walker_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_object_walker_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,14 +18,13 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
-std::string Walk(CPDF_Object* object) {
+std::string Walk(RetainPtr<CPDF_Object> object) {
   std::ostringstream result;
-  CPDF_ObjectWalker walker(object);
-  while (const CPDF_Object* obj = walker.GetNext()) {
+  CPDF_ObjectWalker walker(std::move(object));
+  while (RetainPtr<const CPDF_Object> obj = walker.GetNext()) {
     if (obj->IsDictionary())
       result << " Dict";
     else if (obj->IsArray())
@@ -54,64 +53,63 @@
 
 }  // namespace
 
-TEST(CPDF_ObjectWalkerTest, Simple) {
-  EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Null>().Get()), "Null");
-  EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Dictionary>().Get()), "Dict");
-  EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Array>().Get()), "Arr");
-  EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_String>().Get()), "Str");
-  EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Boolean>().Get()), "Bool");
-  EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Stream>().Get()), "Stream");
-  EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Reference>(nullptr, 0).Get()), "Ref");
+TEST(ObjectWalkerTest, Simple) {
+  EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Null>()), "Null");
+  EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Dictionary>()), "Dict");
+  EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Array>()), "Arr");
+  EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_String>()), "Str");
+  EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Boolean>()), "Bool");
+  EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Stream>()), "Stream");
+  EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Reference>(nullptr, 0)), "Ref");
 }
 
-TEST(CPDF_ObjectWalkerTest, CombinedObject) {
+TEST(ObjectWalkerTest, CombinedObject) {
   auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
   dict->SetFor("1", pdfium::MakeRetain<CPDF_String>());
   dict->SetFor("2", pdfium::MakeRetain<CPDF_Boolean>());
   auto array = pdfium::MakeRetain<CPDF_Array>();
-  array->Add(pdfium::MakeRetain<CPDF_Reference>(nullptr, 0));
-  array->Add(pdfium::MakeRetain<CPDF_Null>());
-  array->Add(pdfium::MakeRetain<CPDF_Stream>(
-      nullptr, 0, pdfium::MakeRetain<CPDF_Dictionary>()));
+  array->Append(pdfium::MakeRetain<CPDF_Reference>(nullptr, 0));
+  array->Append(pdfium::MakeRetain<CPDF_Null>());
+  array->Append(
+      pdfium::MakeRetain<CPDF_Stream>(pdfium::MakeRetain<CPDF_Dictionary>()));
   dict->SetFor("3", std::move(array));
   // The last number for stream length.
-  EXPECT_EQ(Walk(dict.Get()), "Dict Str Bool Arr Ref Null Stream Dict Num");
+  EXPECT_EQ(Walk(dict), "Dict Str Bool Arr Ref Null Stream Dict Num");
 }
 
-TEST(CPDF_ObjectWalkerTest, GetParent) {
+TEST(ObjectWalkerTest, GetParent) {
   auto level_4 = pdfium::MakeRetain<CPDF_Number>(0);
   auto level_3 = pdfium::MakeRetain<CPDF_Dictionary>();
   level_3->SetFor("Length", std::move(level_4));
-  auto level_2 =
-      pdfium::MakeRetain<CPDF_Stream>(nullptr, 0, std::move(level_3));
+  auto level_2 = pdfium::MakeRetain<CPDF_Stream>(std::move(level_3));
   auto level_1 = pdfium::MakeRetain<CPDF_Array>();
-  level_1->Add(std::move(level_2));
+  level_1->Append(std::move(level_2));
   auto level_0 = pdfium::MakeRetain<CPDF_Dictionary>();
   level_0->SetFor("Array", std::move(level_1));
 
   // We have <</Array [ stream( << /Length 0 >>) ]>>
   // In this case each step will increase depth.
   // And on each step the prev object should be parent for current.
-  const CPDF_Object* cur_parent = nullptr;
-  CPDF_ObjectWalker walker(level_0.Get());
-  while (const CPDF_Object* obj = walker.GetNext()) {
+  RetainPtr<const CPDF_Object> cur_parent;
+  CPDF_ObjectWalker walker(level_0);
+  while (RetainPtr<const CPDF_Object> obj = walker.GetNext()) {
     EXPECT_EQ(cur_parent, walker.GetParent());
-    cur_parent = obj;
+    cur_parent = std::move(obj);
   }
 }
 
-TEST(CPDF_ObjectWalkerTest, SkipWalkIntoCurrentObject) {
+TEST(ObjectWalkerTest, SkipWalkIntoCurrentObject) {
   auto root_array = pdfium::MakeRetain<CPDF_Array>();
   // Add 2 null objects into |root_array|. [ null1, null2 ]
-  root_array->AddNew<CPDF_Null>();
-  root_array->AddNew<CPDF_Null>();
+  root_array->AppendNew<CPDF_Null>();
+  root_array->AppendNew<CPDF_Null>();
   // |root_array| will contain 4 null objects after this.
   // [ null1, null2, [ null3, null4 ] ]
-  root_array->Add(root_array->Clone());
+  root_array->Append(root_array->Clone());
 
   int non_array_objects = 0;
-  CPDF_ObjectWalker walker(root_array.Get());
-  while (const CPDF_Object* obj = walker.GetNext()) {
+  CPDF_ObjectWalker walker(root_array);
+  while (RetainPtr<const CPDF_Object> obj = walker.GetNext()) {
     if (obj != root_array && obj->IsArray()) {
       // skip other array except root.
       walker.SkipWalkIntoCurrentObject();
@@ -123,7 +121,7 @@
   EXPECT_EQ(2, non_array_objects);
 }
 
-TEST(CPDF_ObjectWalkerTest, DictionaryKey) {
+TEST(ObjectWalkerTest, DictionaryKey) {
   auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
   dict->SetFor("1", pdfium::MakeRetain<CPDF_Null>());
   dict->SetFor("2", pdfium::MakeRetain<CPDF_Null>());
@@ -131,8 +129,8 @@
   dict->SetFor("4", pdfium::MakeRetain<CPDF_Null>());
   dict->SetFor("5", pdfium::MakeRetain<CPDF_Null>());
 
-  CPDF_ObjectWalker walker(dict.Get());
-  while (const CPDF_Object* obj = walker.GetNext()) {
+  CPDF_ObjectWalker walker(dict);
+  while (RetainPtr<const CPDF_Object> obj = walker.GetNext()) {
     if (dict == obj) {
       // Ignore root dictinary object
       continue;
diff --git a/core/fpdfapi/parser/cpdf_page_object_avail.cpp b/core/fpdfapi/parser/cpdf_page_object_avail.cpp
index 6673885..af46e70 100644
--- a/core/fpdfapi/parser/cpdf_page_object_avail.cpp
+++ b/core/fpdfapi/parser/cpdf_page_object_avail.cpp
@@ -1,17 +1,18 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/parser/cpdf_page_object_avail.h"
 
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
 
-CPDF_PageObjectAvail::~CPDF_PageObjectAvail() {}
+CPDF_PageObjectAvail::~CPDF_PageObjectAvail() = default;
 
 bool CPDF_PageObjectAvail::ExcludeObject(const CPDF_Object* object) const {
   if (CPDF_ObjectAvail::ExcludeObject(object))
     return true;
 
-  return object->IsDictionary() &&
-         object->GetDict()->GetStringFor("Type") == "Page";
+  // See ISO 32000-1:2008 spec, table 30.
+  return ValidateDictType(ToDictionary(object), "Page");
 }
diff --git a/core/fpdfapi/parser/cpdf_page_object_avail.h b/core/fpdfapi/parser/cpdf_page_object_avail.h
index bb9e80a..01a2c6a 100644
--- a/core/fpdfapi/parser/cpdf_page_object_avail.h
+++ b/core/fpdfapi/parser/cpdf_page_object_avail.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fpdfapi/parser/cpdf_page_object_avail_unittest.cpp b/core/fpdfapi/parser/cpdf_page_object_avail_unittest.cpp
index 6a76118..2486fed 100644
--- a/core/fpdfapi/parser/cpdf_page_object_avail_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_page_object_avail_unittest.cpp
@@ -1,38 +1,37 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/parser/cpdf_page_object_avail.h"
 
 #include <map>
-#include <memory>
 #include <utility>
 
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_read_validator.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fxcrt/fx_stream.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/invalid_seekable_read_stream.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 namespace {
 
 class TestReadValidator final : public CPDF_ReadValidator {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
-  void SimulateReadError() { ReadBlockAtOffset(nullptr, 0, 1); }
+  void SimulateReadError() { ReadBlockAtOffset({}, 0); }
 
  private:
   TestReadValidator()
       : CPDF_ReadValidator(pdfium::MakeRetain<InvalidSeekableReadStream>(100),
                            nullptr) {}
-  ~TestReadValidator() override {}
+  ~TestReadValidator() override = default;
 };
 
 class TestHolder final : public CPDF_IndirectObjectHolder {
@@ -42,10 +41,10 @@
     Available,
   };
   TestHolder() : validator_(pdfium::MakeRetain<TestReadValidator>()) {}
-  ~TestHolder() override {}
+  ~TestHolder() override = default;
 
   // CPDF_IndirectObjectHolder overrides:
-  CPDF_Object* GetOrParseIndirectObject(uint32_t objnum) override {
+  RetainPtr<CPDF_Object> ParseIndirectObject(uint32_t objnum) override {
     auto it = objects_data_.find(objnum);
     if (it == objects_data_.end())
       return nullptr;
@@ -55,7 +54,7 @@
       validator_->SimulateReadError();
       return nullptr;
     }
-    return obj_data.object.Get();
+    return obj_data.object;
   }
 
   RetainPtr<CPDF_ReadValidator> GetValidator() { return validator_; }
@@ -66,13 +65,13 @@
     ObjectData object_data;
     object_data.object = std::move(object);
     object_data.state = state;
-    ASSERT(objects_data_.find(objnum) == objects_data_.end());
+    DCHECK(objects_data_.find(objnum) == objects_data_.end());
     objects_data_[objnum] = std::move(object_data);
   }
 
   void SetObjectState(uint32_t objnum, ObjectState state) {
     auto it = objects_data_.find(objnum);
-    ASSERT(it != objects_data_.end());
+    DCHECK(it != objects_data_.end());
     ObjectData& obj_data = it->second;
     obj_data.state = state;
   }
@@ -95,22 +94,23 @@
 
 }  // namespace
 
-TEST(CPDF_PageObjectAvailTest, ExcludePages) {
+TEST(PageObjectAvailTest, ExcludePages) {
   TestHolder holder;
   holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
-  holder.GetTestObject(1)->GetDict()->SetNewFor<CPDF_Reference>("Kids", &holder,
-                                                                2);
+  holder.GetTestObject(1)->GetMutableDict()->SetNewFor<CPDF_Reference>(
+      "Kids", &holder, 2);
   holder.AddObject(2, pdfium::MakeRetain<CPDF_Array>(),
                    TestHolder::ObjectState::Available);
-  holder.GetTestObject(2)->AsArray()->AddNew<CPDF_Reference>(&holder, 3);
+  holder.GetTestObject(2)->AsMutableArray()->AppendNew<CPDF_Reference>(&holder,
+                                                                       3);
 
   holder.AddObject(3, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
-  holder.GetTestObject(3)->GetDict()->SetFor(
-      "Type", pdfium::MakeRetain<CPDF_String>(nullptr, "Page", false));
-  holder.GetTestObject(3)->GetDict()->SetNewFor<CPDF_Reference>("OtherPageData",
-                                                                &holder, 4);
+  holder.GetTestObject(3)->GetMutableDict()->SetFor(
+      "Type", pdfium::MakeRetain<CPDF_Name>(nullptr, "Page"));
+  holder.GetTestObject(3)->GetMutableDict()->SetNewFor<CPDF_Reference>(
+      "OtherPageData", &holder, 4);
   // Add unavailable object related to other page.
   holder.AddObject(
       4, pdfium::MakeRetain<CPDF_String>(nullptr, "Other page data", false),
@@ -119,5 +119,5 @@
   CPDF_PageObjectAvail avail(holder.GetValidator(), &holder, 1);
   // Now object should be available, although the object '4' is not available,
   // because it is in skipped other page.
-  EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+  EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail());
 }
diff --git a/core/fpdfapi/parser/cpdf_parser.cpp b/core/fpdfapi/parser/cpdf_parser.cpp
index 3e2819b..098e67e 100644
--- a/core/fpdfapi/parser/cpdf_parser.cpp
+++ b/core/fpdfapi/parser/cpdf_parser.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,9 @@
 
 #include "core/fpdfapi/parser/cpdf_parser.h"
 
+#include <ctype.h>
+#include <stdint.h>
+
 #include <algorithm>
 #include <utility>
 #include <vector>
@@ -25,27 +28,119 @@
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcrt/autorestorer.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/scoped_set_insertion.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/containers/span.h"
+#include "third_party/base/notreached.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;
 
-uint32_t GetVarInt(const uint8_t* p, int32_t n) {
+// The required number of fields in a /W array in a cross-reference stream
+// dictionary.
+constexpr size_t kMinFieldCount = 3;
+
+// V4 trailers are inline.
+constexpr uint32_t kNoV4TrailerObjectNumber = 0;
+
+struct CrossRefV5IndexEntry {
+  uint32_t start_obj_num;
+  uint32_t obj_count;
+};
+
+CPDF_Parser::ObjectType GetObjectTypeFromCrossRefStreamType(
+    uint32_t cross_ref_stream_type) {
+  switch (cross_ref_stream_type) {
+    case 0:
+      return CPDF_Parser::ObjectType::kFree;
+    case 1:
+      return CPDF_Parser::ObjectType::kNotCompressed;
+    case 2:
+      return CPDF_Parser::ObjectType::kCompressed;
+    default:
+      return CPDF_Parser::ObjectType::kNull;
+  }
+}
+
+// Use the Get*XRefStreamEntry() functions below, instead of calling this
+// directly.
+uint32_t GetVarInt(pdfium::span<const uint8_t> input) {
   uint32_t result = 0;
-  for (int32_t i = 0; i < n; ++i)
-    result = result * 256 + p[i];
+  for (uint8_t c : input)
+    result = result * 256 + c;
   return result;
 }
 
+// The following 3 functions retrieve variable length entries from
+// cross-reference streams, as described in ISO 32000-1:2008 table 18. There are
+// only 3 fields for any given entry.
+uint32_t GetFirstXRefStreamEntry(pdfium::span<const uint8_t> entry_span,
+                                 pdfium::span<const uint32_t> field_widths) {
+  return GetVarInt(entry_span.first(field_widths[0]));
+}
+
+uint32_t GetSecondXRefStreamEntry(pdfium::span<const uint8_t> entry_span,
+                                  pdfium::span<const uint32_t> field_widths) {
+  return GetVarInt(entry_span.subspan(field_widths[0], field_widths[1]));
+}
+
+uint32_t GetThirdXRefStreamEntry(pdfium::span<const uint8_t> entry_span,
+                                 pdfium::span<const uint32_t> field_widths) {
+  return GetVarInt(
+      entry_span.subspan(field_widths[0] + field_widths[1], field_widths[2]));
+}
+
+std::vector<CrossRefV5IndexEntry> GetCrossRefV5Indices(const CPDF_Array* array,
+                                                       uint32_t size) {
+  std::vector<CrossRefV5IndexEntry> indices;
+  if (array) {
+    for (size_t i = 0; i < array->size() / 2; i++) {
+      RetainPtr<const CPDF_Number> pStartNumObj = array->GetNumberAt(i * 2);
+      if (!pStartNumObj)
+        continue;
+
+      RetainPtr<const CPDF_Number> pCountObj = array->GetNumberAt(i * 2 + 1);
+      if (!pCountObj)
+        continue;
+
+      int nStartNum = pStartNumObj->GetInteger();
+      int nCount = pCountObj->GetInteger();
+      if (nStartNum < 0 || nCount <= 0)
+        continue;
+
+      indices.push_back(
+          {static_cast<uint32_t>(nStartNum), static_cast<uint32_t>(nCount)});
+    }
+  }
+
+  if (indices.empty())
+    indices.push_back({0, size});
+  return indices;
+}
+
+std::vector<uint32_t> GetFieldWidths(const CPDF_Array* array) {
+  std::vector<uint32_t> results;
+  if (!array)
+    return results;
+
+  CPDF_ArrayLocker locker(array);
+  for (const auto& obj : locker)
+    results.push_back(obj->GetInteger());
+  return results;
+}
+
 class ObjectsHolderStub final : public CPDF_Parser::ParsedObjectsHolder {
  public:
   ObjectsHolderStub() = default;
@@ -57,18 +152,16 @@
 
 CPDF_Parser::CPDF_Parser(ParsedObjectsHolder* holder)
     : m_pObjectsHolder(holder),
-      m_CrossRefTable(pdfium::MakeUnique<CPDF_CrossRefTable>()) {
+      m_CrossRefTable(std::make_unique<CPDF_CrossRefTable>()) {
   if (!holder) {
-    m_pOwnedObjectsHolder = pdfium::MakeUnique<ObjectsHolderStub>();
+    m_pOwnedObjectsHolder = std::make_unique<ObjectsHolderStub>();
     m_pObjectsHolder = m_pOwnedObjectsHolder.get();
   }
 }
 
 CPDF_Parser::CPDF_Parser() : CPDF_Parser(nullptr) {}
 
-CPDF_Parser::~CPDF_Parser() {
-  ReleaseEncryptHandler();
-}
+CPDF_Parser::~CPDF_Parser() = default;
 
 uint32_t CPDF_Parser::GetLastObjNum() const {
   return m_CrossRefTable->objects_info().empty()
@@ -86,7 +179,7 @@
 }
 
 CPDF_Parser::ObjectType CPDF_Parser::GetObjectType(uint32_t objnum) const {
-  ASSERT(IsValidObjectNumber(objnum));
+  DCHECK(IsValidObjectNumber(objnum));
   const auto* info = m_CrossRefTable->GetObjectInfo(objnum);
   return info ? info->type : ObjectType::kFree;
 }
@@ -108,19 +201,15 @@
   return GetObjectType(objnum) == ObjectType::kFree;
 }
 
-void CPDF_Parser::ShrinkObjectMap(uint32_t size) {
-  m_CrossRefTable->ShrinkObjectMap(size);
-}
-
-bool CPDF_Parser::InitSyntaxParser(
-    const RetainPtr<CPDF_ReadValidator>& validator) {
-  const Optional<FX_FILESIZE> header_offset = GetHeaderOffset(validator);
-  if (!header_offset)
+bool CPDF_Parser::InitSyntaxParser(RetainPtr<CPDF_ReadValidator> validator) {
+  const absl::optional<FX_FILESIZE> header_offset = GetHeaderOffset(validator);
+  if (!header_offset.has_value())
     return false;
-  if (validator->GetSize() < *header_offset + kPDFHeaderSize)
+  if (validator->GetSize() < header_offset.value() + kPDFHeaderSize)
     return false;
 
-  m_pSyntax = pdfium::MakeUnique<CPDF_SyntaxParser>(validator, *header_offset);
+  m_pSyntax = std::make_unique<CPDF_SyntaxParser>(std::move(validator),
+                                                  header_offset.value());
   return ParseFileVersion();
 }
 
@@ -130,30 +219,30 @@
   if (!m_pSyntax->GetCharAt(5, ch))
     return false;
 
-  if (std::isdigit(ch))
+  if (isdigit(ch))
     m_FileVersion = FXSYS_DecimalCharToInt(static_cast<wchar_t>(ch)) * 10;
 
   if (!m_pSyntax->GetCharAt(7, ch))
     return false;
 
-  if (std::isdigit(ch))
+  if (isdigit(ch))
     m_FileVersion += FXSYS_DecimalCharToInt(static_cast<wchar_t>(ch));
   return true;
 }
 
 CPDF_Parser::Error CPDF_Parser::StartParse(
-    const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
-    const char* password) {
-  if (!InitSyntaxParser(
-          pdfium::MakeRetain<CPDF_ReadValidator>(pFileAccess, nullptr)))
+    RetainPtr<IFX_SeekableReadStream> pFileAccess,
+    const ByteString& password) {
+  if (!InitSyntaxParser(pdfium::MakeRetain<CPDF_ReadValidator>(
+          std::move(pFileAccess), nullptr)))
     return FORMAT_ERROR;
   SetPassword(password);
   return StartParseInternal();
 }
 
 CPDF_Parser::Error CPDF_Parser::StartParseInternal() {
-  ASSERT(!m_bHasParsed);
-  ASSERT(!m_bXRefTableRebuilt);
+  DCHECK(!m_bHasParsed);
+  DCHECK(!m_bXRefTableRebuilt);
   m_bHasParsed = true;
   m_bXRefStream = false;
 
@@ -203,7 +292,7 @@
       return eRet;
   }
   if (m_pSecurityHandler && !m_pSecurityHandler->IsMetadataEncrypted()) {
-    CPDF_Reference* pMetadata =
+    RetainPtr<const CPDF_Reference> pMetadata =
         ToReference(GetRoot()->GetObjectFor("Metadata"));
     if (pMetadata)
       m_MetadataObjnum = pMetadata->GetRefObjNum();
@@ -221,12 +310,12 @@
   m_pSyntax->GetKeyword();
 
   // Read XRef offset.
-  bool bNumber;
-  const ByteString xref_offset_str = m_pSyntax->GetNextWord(&bNumber);
-  if (!bNumber || xref_offset_str.IsEmpty())
+  const CPDF_SyntaxParser::WordResult xref_offset_result =
+      m_pSyntax->GetNextWord();
+  if (!xref_offset_result.is_number || xref_offset_result.word.IsEmpty())
     return 0;
 
-  const FX_SAFE_FILESIZE result = FXSYS_atoi64(xref_offset_str.c_str());
+  const FX_SAFE_FILESIZE result = FXSYS_atoi64(xref_offset_result.word.c_str());
   if (!result.IsValid() || result.ValueOrDie() >= m_pSyntax->GetDocumentSize())
     return 0;
 
@@ -238,11 +327,11 @@
   if (!GetTrailer())
     return FORMAT_ERROR;
 
-  const CPDF_Dictionary* pEncryptDict = GetEncryptDict();
+  RetainPtr<const CPDF_Dictionary> pEncryptDict = GetEncryptDict();
   if (!pEncryptDict)
     return SUCCESS;
 
-  if (pEncryptDict->GetStringFor("Filter") != "Standard")
+  if (pEncryptDict->GetNameFor("Filter") != "Standard")
     return HANDLER_ERROR;
 
   auto pSecurityHandler = pdfium::MakeRetain<CPDF_SecurityHandler>();
@@ -263,16 +352,15 @@
 // in the cross reference table are all off by one.
 bool CPDF_Parser::VerifyCrossRefV4() {
   for (const auto& it : m_CrossRefTable->objects_info()) {
-    if (it.second.pos == 0)
+    if (it.second.pos <= 0)
       continue;
     // Find the first non-zero position.
     FX_FILESIZE SavedPos = m_pSyntax->GetPos();
     m_pSyntax->SetPos(it.second.pos);
-    bool is_num = false;
-    ByteString num_str = m_pSyntax->GetNextWord(&is_num);
+    CPDF_SyntaxParser::WordResult word_result = m_pSyntax->GetNextWord();
     m_pSyntax->SetPos(SavedPos);
-    if (!is_num || num_str.IsEmpty() ||
-        FXSYS_atoui(num_str.c_str()) != it.first) {
+    if (!word_result.is_number || word_result.word.IsEmpty() ||
+        FXSYS_atoui(word_result.word.c_str()) != it.first) {
       // If the object number read doesn't match the one stored,
       // something is wrong with the cross reference table.
       return false;
@@ -290,50 +378,50 @@
   if (!trailer)
     return false;
 
-  m_CrossRefTable->SetTrailer(std::move(trailer));
-  int32_t xrefsize = GetDirectInteger(GetTrailer(), "Size");
+  m_CrossRefTable->SetTrailer(std::move(trailer), kNoV4TrailerObjectNumber);
+  const int32_t xrefsize = GetTrailer()->GetDirectIntegerFor("Size");
   if (xrefsize > 0 && xrefsize <= kMaxXRefSize)
-    ShrinkObjectMap(xrefsize);
+    m_CrossRefTable->SetObjectMapSize(xrefsize);
 
-  std::vector<FX_FILESIZE> xref_stream_list{
-      GetDirectInteger(GetTrailer(), "XRefStm")};
+  FX_FILESIZE xref_stm = GetTrailer()->GetDirectIntegerFor("XRefStm");
+  std::vector<FX_FILESIZE> xref_stream_list{xref_stm};
   std::vector<FX_FILESIZE> xref_list{xref_offset};
   std::set<FX_FILESIZE> seen_xref_offset{xref_offset};
 
   // When the trailer doesn't have Prev entry or Prev entry value is not
   // numerical, GetDirectInteger() returns 0. Loading will end.
-  xref_offset = GetDirectInteger(GetTrailer(), "Prev");
-  while (xref_offset) {
+  xref_offset = GetTrailer()->GetDirectIntegerFor("Prev");
+  while (xref_offset > 0) {
     // Check for circular references.
-    if (pdfium::ContainsKey(seen_xref_offset, xref_offset))
+    if (pdfium::Contains(seen_xref_offset, xref_offset))
       return false;
 
     seen_xref_offset.insert(xref_offset);
+    xref_list.insert(xref_list.begin(), xref_offset);
 
     // SLOW ...
-    xref_list.insert(xref_list.begin(), xref_offset);
     LoadCrossRefV4(xref_offset, true);
 
     RetainPtr<CPDF_Dictionary> pDict(LoadTrailerV4());
     if (!pDict)
       return false;
 
-    xref_offset = GetDirectInteger(pDict.Get(), "Prev");
+    xref_offset = pDict->GetDirectIntegerFor("Prev");
+    xref_stm = pDict->GetIntegerFor("XRefStm");
+    xref_stream_list.insert(xref_stream_list.begin(), xref_stm);
 
     // SLOW ...
-    xref_stream_list.insert(xref_stream_list.begin(),
-                            pDict->GetIntegerFor("XRefStm"));
-
     m_CrossRefTable = CPDF_CrossRefTable::MergeUp(
-        pdfium::MakeUnique<CPDF_CrossRefTable>(std::move(pDict)),
+        std::make_unique<CPDF_CrossRefTable>(std::move(pDict),
+                                             kNoV4TrailerObjectNumber),
         std::move(m_CrossRefTable));
   }
 
   for (size_t i = 0; i < xref_list.size(); ++i) {
-    if (!LoadCrossRefV4(xref_list[i], false))
+    if (xref_list[i] > 0 && !LoadCrossRefV4(xref_list[i], false))
       return false;
 
-    if (xref_stream_list[i] && !LoadCrossRefV5(&xref_stream_list[i], false))
+    if (xref_stream_list[i] > 0 && !LoadCrossRefV5(&xref_stream_list[i], false))
       return false;
 
     if (i == 0 && !VerifyCrossRefV4())
@@ -351,59 +439,60 @@
     return false;
 
   // GetTrailer() currently returns the first-page trailer.
-  if (GetDirectInteger(GetTrailer(), "Size") == 0)
+  if (GetTrailer()->GetDirectIntegerFor("Size") == 0)
     return false;
 
   // Read /XRefStm from the first-page trailer. No need to read /Prev for the
   // first-page trailer, as the caller already did that and passed it in as
   // |main_xref_offset|.
-  std::vector<FX_FILESIZE> xref_stream_list{
-      GetDirectInteger(GetTrailer(), "XRefStm")};
+  FX_FILESIZE xref_stm = GetTrailer()->GetDirectIntegerFor("XRefStm");
+  std::vector<FX_FILESIZE> xref_stream_list{xref_stm};
   std::vector<FX_FILESIZE> xref_list{main_xref_offset};
   std::set<FX_FILESIZE> seen_xref_offset{main_xref_offset};
 
   // Merge the trailers.
   m_CrossRefTable = CPDF_CrossRefTable::MergeUp(
-      pdfium::MakeUnique<CPDF_CrossRefTable>(std::move(main_trailer)),
+      std::make_unique<CPDF_CrossRefTable>(std::move(main_trailer),
+                                           kNoV4TrailerObjectNumber),
       std::move(m_CrossRefTable));
 
   // Now GetTrailer() returns the merged trailer, where /Prev is from the
   // main-trailer.
-  FX_FILESIZE xref_offset = GetDirectInteger(GetTrailer(), "Prev");
-  while (xref_offset) {
+  FX_FILESIZE xref_offset = GetTrailer()->GetDirectIntegerFor("Prev");
+  while (xref_offset > 0) {
     // Check for circular references.
-    if (pdfium::ContainsKey(seen_xref_offset, xref_offset))
+    if (pdfium::Contains(seen_xref_offset, xref_offset))
       return false;
 
     seen_xref_offset.insert(xref_offset);
+    xref_list.insert(xref_list.begin(), xref_offset);
 
     // SLOW ...
-    xref_list.insert(xref_list.begin(), xref_offset);
     LoadCrossRefV4(xref_offset, true);
 
     RetainPtr<CPDF_Dictionary> pDict(LoadTrailerV4());
     if (!pDict)
       return false;
 
-    xref_offset = GetDirectInteger(pDict.Get(), "Prev");
+    xref_offset = pDict->GetDirectIntegerFor("Prev");
+    xref_stm = pDict->GetIntegerFor("XRefStm");
+    xref_stream_list.insert(xref_stream_list.begin(), xref_stm);
 
     // SLOW ...
-    xref_stream_list.insert(xref_stream_list.begin(),
-                            pDict->GetIntegerFor("XRefStm"));
-
     m_CrossRefTable = CPDF_CrossRefTable::MergeUp(
-        pdfium::MakeUnique<CPDF_CrossRefTable>(std::move(pDict)),
+        std::make_unique<CPDF_CrossRefTable>(std::move(pDict),
+                                             kNoV4TrailerObjectNumber),
         std::move(m_CrossRefTable));
   }
 
-  if (xref_stream_list[0] && !LoadCrossRefV5(&xref_stream_list[0], false))
+  if (xref_stream_list[0] > 0 && !LoadCrossRefV5(&xref_stream_list[0], false))
     return false;
 
   for (size_t i = 1; i < xref_list.size(); ++i) {
-    if (!LoadCrossRefV4(xref_list[i], false))
+    if (xref_list[i] > 0 && !LoadCrossRefV4(xref_list[i], false))
       return false;
 
-    if (xref_stream_list[i] && !LoadCrossRefV5(&xref_stream_list[i], false))
+    if (xref_stream_list[i] > 0 && !LoadCrossRefV5(&xref_stream_list[i], false))
       return false;
   }
   return true;
@@ -419,11 +508,11 @@
   // Each entry shall be exactly 20 byte.
   // A sample entry looks like:
   // "0000000000 00007 f\r\n"
-  static constexpr int32_t kEntryConstSize = 20;
+  static constexpr int32_t kEntrySize = 20;
 
   if (!out_objects) {
     FX_SAFE_FILESIZE pos = count;
-    pos *= kEntryConstSize;
+    pos *= kEntrySize;
     pos += m_pSyntax->GetPos();
     if (!pos.IsValid())
       return false;
@@ -439,33 +528,32 @@
   if (new_size.ValueOrDie() > kMaxXRefSize)
     return false;
 
-  const size_t max_entries_in_file =
-      m_pSyntax->GetDocumentSize() / kEntryConstSize;
+  const size_t max_entries_in_file = m_pSyntax->GetDocumentSize() / kEntrySize;
   if (new_size.ValueOrDie() > max_entries_in_file)
     return false;
 
   out_objects->resize(new_size.ValueOrDie());
 
-  std::vector<char> buf(1024 * kEntryConstSize + 1);
+  DataVector<char> buf(1024 * kEntrySize + 1);
   buf.back() = '\0';
 
-  uint32_t nBytesToRead = count;
-  while (nBytesToRead > 0) {
-    const uint32_t block_size = std::min(nBytesToRead, 1024u);
-    if (!m_pSyntax->ReadBlock(reinterpret_cast<uint8_t*>(buf.data()),
-                              block_size * kEntryConstSize)) {
+  uint32_t entries_to_read = count;
+  while (entries_to_read > 0) {
+    const uint32_t entries_in_block = std::min(entries_to_read, 1024u);
+    const uint32_t bytes_to_read = entries_in_block * kEntrySize;
+    auto block_span = pdfium::make_span(buf).first(bytes_to_read);
+    if (!m_pSyntax->ReadBlock(pdfium::as_writable_bytes(block_span)))
       return false;
-    }
 
-    for (uint32_t i = 0; i < block_size; i++) {
-      uint32_t iObjectIndex = count - nBytesToRead + i;
+    for (uint32_t i = 0; i < entries_in_block; i++) {
+      uint32_t iObjectIndex = count - entries_to_read + i;
       CrossRefObjData& obj_data =
           (*out_objects)[start_obj_index + iObjectIndex];
       const uint32_t objnum = start_objnum + iObjectIndex;
       obj_data.obj_num = objnum;
       ObjectInfo& info = obj_data.info;
 
-      char* pEntry = &buf[i * kEntryConstSize];
+      const char* pEntry = &buf[i * kEntrySize];
       if (pEntry[17] == 'f') {
         info.pos = 0;
         info.type = ObjectType::kFree;
@@ -476,7 +564,7 @@
 
         if (offset.ValueOrDie() == 0) {
           for (int32_t c = 0; c < 10; c++) {
-            if (!std::isdigit(pEntry[c]))
+            if (!isdigit(pEntry[c]))
               return false;
           }
         }
@@ -490,7 +578,7 @@
         info.type = ObjectType::kNotCompressed;
       }
     }
-    nBytesToRead -= block_size;
+    entries_to_read -= entries_in_block;
   }
   return true;
 }
@@ -502,14 +590,14 @@
   if (m_pSyntax->GetKeyword() != "xref")
     return false;
   std::vector<CrossRefObjData> result_objects;
-  while (1) {
+  while (true) {
     FX_FILESIZE saved_pos = m_pSyntax->GetPos();
-    bool bIsNumber;
-    ByteString word = m_pSyntax->GetNextWord(&bIsNumber);
+    CPDF_SyntaxParser::WordResult word_result = m_pSyntax->GetNextWord();
+    const ByteString& word = word_result.word;
     if (word.IsEmpty())
       return false;
 
-    if (!bIsNumber) {
+    if (!word_result.is_number) {
       m_pSyntax->SetPos(saved_pos);
       break;
     }
@@ -554,10 +642,9 @@
         m_CrossRefTable->AddNormal(obj.obj_num, obj.info.gennum, obj.info.pos);
         break;
       case ObjectType::kCompressed:
-        m_CrossRefTable->AddCompressed(obj.obj_num, obj.info.archive_obj_num);
+        m_CrossRefTable->AddCompressed(obj.obj_num, obj.info.archive.obj_num,
+                                       obj.info.archive.obj_index);
         break;
-      default:
-        NOTREACHED();
     }
   }
 }
@@ -567,13 +654,13 @@
     return false;
 
   std::set<FX_FILESIZE> seen_xref_offset;
-  while (xref_offset) {
+  while (xref_offset > 0) {
     seen_xref_offset.insert(xref_offset);
     if (!LoadCrossRefV5(&xref_offset, false))
       return false;
 
     // Check for circular references.
-    if (pdfium::ContainsKey(seen_xref_offset, xref_offset))
+    if (pdfium::Contains(seen_xref_offset, xref_offset))
       return false;
   }
   m_ObjectStreamMap.clear();
@@ -582,17 +669,17 @@
 }
 
 bool CPDF_Parser::RebuildCrossRef() {
-  auto cross_ref_table = pdfium::MakeUnique<CPDF_CrossRefTable>();
+  auto cross_ref_table = std::make_unique<CPDF_CrossRefTable>();
 
   const uint32_t kBufferSize = 4096;
   m_pSyntax->SetReadBufferSize(kBufferSize);
   m_pSyntax->SetPos(0);
 
-  bool bIsNumber;
   std::vector<std::pair<uint32_t, FX_FILESIZE>> numbers;
-  for (ByteString word = m_pSyntax->GetNextWord(&bIsNumber); !word.IsEmpty();
-       word = m_pSyntax->GetNextWord(&bIsNumber)) {
-    if (bIsNumber) {
+  for (CPDF_SyntaxParser::WordResult result = m_pSyntax->GetNextWord();
+       !result.word.IsEmpty(); result = m_pSyntax->GetNextWord()) {
+    const ByteString& word = result.word;
+    if (result.is_number) {
       numbers.emplace_back(FXSYS_atoui(word.c_str()),
                            m_pSyntax->GetPos() - word.GetLength());
       if (numbers.size() > 2u)
@@ -607,11 +694,17 @@
     } else if (word == "trailer") {
       RetainPtr<CPDF_Object> pTrailer = m_pSyntax->GetObjectBody(nullptr);
       if (pTrailer) {
+        CPDF_Stream* stream_trailer = pTrailer->AsMutableStream();
+        // Grab the object number from `pTrailer` before potentially calling
+        // std::move(pTrailer) below.
+        const uint32_t trailer_object_number = pTrailer->GetObjNum();
+        RetainPtr<CPDF_Dictionary> trailer_dict =
+            stream_trailer ? stream_trailer->GetMutableDict()
+                           : ToDictionary(std::move(pTrailer));
         cross_ref_table = CPDF_CrossRefTable::MergeUp(
             std::move(cross_ref_table),
-            pdfium::MakeUnique<CPDF_CrossRefTable>(ToDictionary(
-                pTrailer->IsStream() ? pTrailer->AsStream()->GetDict()->Clone()
-                                     : std::move(pTrailer))));
+            std::make_unique<CPDF_CrossRefTable>(std::move(trailer_dict),
+                                                 trailer_object_number));
       }
     } else if (word == "obj" && numbers.size() == 2u) {
       const FX_FILESIZE obj_pos = numbers[0].second;
@@ -623,20 +716,24 @@
           ToStream(m_pSyntax->GetIndirectObject(
               nullptr, CPDF_SyntaxParser::ParseType::kStrict));
 
-      if (pStream && pStream->GetDict()->GetStringFor("Type") == "XRef") {
+      if (pStream && pStream->GetDict()->GetNameFor("Type") == "XRef") {
         cross_ref_table = CPDF_CrossRefTable::MergeUp(
             std::move(cross_ref_table),
-            pdfium::MakeUnique<CPDF_CrossRefTable>(
-                ToDictionary(pStream->GetDict()->Clone())));
+            std::make_unique<CPDF_CrossRefTable>(
+                ToDictionary(pStream->GetDict()->Clone()),
+                pStream->GetObjNum()));
       }
 
       if (obj_num < kMaxObjectNumber) {
         cross_ref_table->AddNormal(obj_num, gen_num, obj_pos);
-        if (const auto object_stream =
-                CPDF_ObjectStream::Create(pStream.Get())) {
-          for (const auto& it : object_stream->objects_offsets()) {
-            if (it.first < kMaxObjectNumber)
-              cross_ref_table->AddCompressed(it.first, obj_num);
+        const auto object_stream =
+            CPDF_ObjectStream::Create(std::move(pStream));
+        if (object_stream) {
+          const auto& object_info = object_stream->object_info();
+          for (size_t i = 0; i < object_info.size(); ++i) {
+            const auto& info = object_info[i];
+            if (info.obj_num < kMaxObjectNumber)
+              cross_ref_table->AddCompressed(info.obj_num, obj_num, i);
           }
         }
       }
@@ -657,161 +754,174 @@
   if (!pObject || !pObject->GetObjNum())
     return false;
 
-  CPDF_Stream* pStream = pObject->AsStream();
+  RetainPtr<const CPDF_Stream> pStream(pObject->AsStream());
   if (!pStream)
     return false;
 
-  CPDF_Dictionary* pDict = pStream->GetDict();
-  *pos = pDict->GetIntegerFor("Prev");
+  RetainPtr<const CPDF_Dictionary> pDict = pStream->GetDict();
+  int32_t prev = pDict->GetIntegerFor("Prev");
+  if (prev < 0)
+    return false;
+
   int32_t size = pDict->GetIntegerFor("Size");
   if (size < 0)
     return false;
 
+  *pos = prev;
+
   RetainPtr<CPDF_Dictionary> pNewTrailer = ToDictionary(pDict->Clone());
   if (bMainXRef) {
-    m_CrossRefTable =
-        pdfium::MakeUnique<CPDF_CrossRefTable>(std::move(pNewTrailer));
-    m_CrossRefTable->ShrinkObjectMap(size);
+    m_CrossRefTable = std::make_unique<CPDF_CrossRefTable>(
+        std::move(pNewTrailer), pStream->GetObjNum());
+    m_CrossRefTable->SetObjectMapSize(size);
   } else {
     m_CrossRefTable = CPDF_CrossRefTable::MergeUp(
-        pdfium::MakeUnique<CPDF_CrossRefTable>(std::move(pNewTrailer)),
+        std::make_unique<CPDF_CrossRefTable>(std::move(pNewTrailer),
+                                             pStream->GetObjNum()),
         std::move(m_CrossRefTable));
   }
 
-  std::vector<std::pair<int32_t, int32_t>> arrIndex;
-  CPDF_Array* pArray = pDict->GetArrayFor("Index");
-  if (pArray) {
-    for (size_t i = 0; i < pArray->size() / 2; i++) {
-      CPDF_Object* pStartNumObj = pArray->GetObjectAt(i * 2);
-      CPDF_Object* pCountObj = pArray->GetObjectAt(i * 2 + 1);
+  std::vector<CrossRefV5IndexEntry> indices =
+      GetCrossRefV5Indices(pDict->GetArrayFor("Index").Get(), size);
 
-      if (ToNumber(pStartNumObj) && ToNumber(pCountObj)) {
-        int nStartNum = pStartNumObj->GetInteger();
-        int nCount = pCountObj->GetInteger();
-        if (nStartNum >= 0 && nCount > 0)
-          arrIndex.push_back(std::make_pair(nStartNum, nCount));
-      }
-    }
-  }
-
-  if (arrIndex.empty())
-    arrIndex.push_back(std::make_pair(0, size));
-
-  pArray = pDict->GetArrayFor("W");
-  if (!pArray)
+  std::vector<uint32_t> field_widths =
+      GetFieldWidths(pDict->GetArrayFor("W").Get());
+  if (field_widths.size() < kMinFieldCount)
     return false;
 
-  std::vector<uint32_t> WidthArray;
-  FX_SAFE_UINT32 dwAccWidth = 0;
-  for (size_t i = 0; i < pArray->size(); ++i) {
-    WidthArray.push_back(pArray->GetIntegerAt(i));
-    dwAccWidth += WidthArray[i];
-  }
-
-  if (!dwAccWidth.IsValid() || WidthArray.size() < 3)
+  FX_SAFE_UINT32 dwAccWidth;
+  for (uint32_t width : field_widths)
+    dwAccWidth += width;
+  if (!dwAccWidth.IsValid())
     return false;
 
-  uint32_t totalWidth = dwAccWidth.ValueOrDie();
-  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
+  uint32_t total_width = dwAccWidth.ValueOrDie();
+  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(pStream));
   pAcc->LoadAllDataFiltered();
 
-  const uint8_t* pData = pAcc->GetData();
-  uint32_t dwTotalSize = pAcc->GetSize();
+  pdfium::span<const uint8_t> data_span = pAcc->GetSpan();
   uint32_t segindex = 0;
-  for (const auto& index : arrIndex) {
-    const int32_t startnum = index.first;
-    if (startnum < 0)
+  for (const auto& index : indices) {
+    FX_SAFE_UINT32 seg_end = segindex;
+    seg_end += index.obj_count;
+    seg_end *= total_width;
+    if (!seg_end.IsValid() || seg_end.ValueOrDie() > data_span.size())
       continue;
 
-    uint32_t count = pdfium::base::checked_cast<uint32_t>(index.second);
-    FX_SAFE_UINT32 dwCaculatedSize = segindex;
-    dwCaculatedSize += count;
-    dwCaculatedSize *= totalWidth;
-    if (!dwCaculatedSize.IsValid() ||
-        dwCaculatedSize.ValueOrDie() > dwTotalSize) {
+    pdfium::span<const uint8_t> seg_span = data_span.subspan(
+        segindex * total_width, index.obj_count * total_width);
+    FX_SAFE_UINT32 safe_new_size = index.start_obj_num;
+    safe_new_size += index.obj_count;
+    if (!safe_new_size.IsValid()) {
       continue;
     }
 
-    const uint8_t* segstart = pData + segindex * totalWidth;
-    FX_SAFE_UINT32 dwMaxObjNum = startnum;
-    dwMaxObjNum += count;
-    uint32_t dwV5Size =
+    // 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;
-    if (!dwMaxObjNum.IsValid() || dwMaxObjNum.ValueOrDie() > dwV5Size)
-      continue;
-
-    for (uint32_t i = 0; i < count; i++) {
-      ObjectType type = ObjectType::kNotCompressed;
-      const uint8_t* entrystart = segstart + i * totalWidth;
-      if (WidthArray[0]) {
-        const uint32_t cross_ref_stream_obj_type =
-            GetVarInt(entrystart, WidthArray[0]);
-        type = GetObjectTypeFromCrossRefStreamType(cross_ref_stream_obj_type);
-        if (type == ObjectType::kNull)
-          continue;
-      }
-
-      const uint32_t objnum = startnum + i;
-      if (objnum >= CPDF_Parser::kMaxObjectNumber)
-        continue;
-
-      const ObjectType existing_type = GetObjectType(objnum);
-      if (existing_type == ObjectType::kNull) {
-        uint32_t offset = GetVarInt(entrystart + WidthArray[0], WidthArray[1]);
-        if (pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(offset))
-          m_CrossRefTable->AddNormal(objnum, 0, offset);
-        continue;
-      }
-
-      if (existing_type != ObjectType::kFree)
-        continue;
-
-      if (type == ObjectType::kFree) {
-        m_CrossRefTable->SetFree(objnum);
-        continue;
-      }
-
-      const uint32_t entry_value =
-          GetVarInt(entrystart + WidthArray[0], WidthArray[1]);
-      if (type == ObjectType::kNotCompressed) {
-        const uint32_t offset = entry_value;
-        if (pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(offset))
-          m_CrossRefTable->AddNormal(objnum, 0, offset);
-        continue;
-      }
-
-      ASSERT(type == ObjectType::kCompressed);
-      const uint32_t archive_obj_num = entry_value;
-      if (!IsValidObjectNumber(archive_obj_num))
-        return false;
-
-      m_CrossRefTable->AddCompressed(objnum, archive_obj_num);
+    // 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);
     }
-    segindex += count;
+
+    for (uint32_t i = 0; i < index.obj_count; ++i) {
+      const uint32_t obj_num = index.start_obj_num + i;
+      if (obj_num >= kMaxObjectNumber) {
+        break;
+      }
+
+      ProcessCrossRefV5Entry(seg_span.subspan(i * total_width, total_width),
+                             field_widths, obj_num);
+    }
+
+    segindex += index.obj_count;
   }
   return true;
 }
 
-const CPDF_Array* CPDF_Parser::GetIDArray() const {
+void CPDF_Parser::ProcessCrossRefV5Entry(
+    pdfium::span<const uint8_t> entry_span,
+    pdfium::span<const uint32_t> field_widths,
+    uint32_t obj_num) {
+  DCHECK_GE(field_widths.size(), kMinFieldCount);
+  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);
+  if (existing_type == ObjectType::kNull) {
+    const uint32_t offset = GetSecondXRefStreamEntry(entry_span, field_widths);
+    if (pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(offset))
+      m_CrossRefTable->AddNormal(obj_num, 0, offset);
+    return;
+  }
+
+  if (existing_type != ObjectType::kFree)
+    return;
+
+  if (type == ObjectType::kFree) {
+    m_CrossRefTable->SetFree(obj_num);
+    return;
+  }
+
+  if (type == ObjectType::kNotCompressed) {
+    const uint32_t offset = GetSecondXRefStreamEntry(entry_span, field_widths);
+    if (pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(offset))
+      m_CrossRefTable->AddNormal(obj_num, 0, offset);
+    return;
+  }
+
+  DCHECK_EQ(type, ObjectType::kCompressed);
+  const uint32_t archive_obj_num =
+      GetSecondXRefStreamEntry(entry_span, field_widths);
+  if (!IsValidObjectNumber(archive_obj_num)) {
+    return;
+  }
+
+  const uint32_t archive_obj_index =
+      GetThirdXRefStreamEntry(entry_span, field_widths);
+  m_CrossRefTable->AddCompressed(obj_num, archive_obj_num, archive_obj_index);
+}
+
+RetainPtr<const CPDF_Array> CPDF_Parser::GetIDArray() const {
   return GetTrailer() ? GetTrailer()->GetArrayFor("ID") : nullptr;
 }
 
-CPDF_Dictionary* CPDF_Parser::GetRoot() const {
-  CPDF_Object* obj =
+RetainPtr<const CPDF_Dictionary> CPDF_Parser::GetRoot() const {
+  RetainPtr<CPDF_Object> obj =
       m_pObjectsHolder->GetOrParseIndirectObject(GetRootObjNum());
   return obj ? obj->GetDict() : nullptr;
 }
 
-const CPDF_Dictionary* CPDF_Parser::GetEncryptDict() const {
+RetainPtr<const CPDF_Dictionary> CPDF_Parser::GetEncryptDict() const {
   if (!GetTrailer())
     return nullptr;
 
-  const CPDF_Object* pEncryptObj = GetTrailer()->GetObjectFor("Encrypt");
+  RetainPtr<const CPDF_Object> pEncryptObj =
+      GetTrailer()->GetObjectFor("Encrypt");
   if (!pEncryptObj)
     return nullptr;
 
   if (pEncryptObj->IsDictionary())
-    return ToDictionary(pEncryptObj);
+    return pdfium::WrapRetain(pEncryptObj->AsDictionary());
 
   if (pEncryptObj->IsReference()) {
     return ToDictionary(m_pObjectsHolder->GetOrParseIndirectObject(
@@ -832,6 +942,10 @@
   return m_CrossRefTable->GetMutableTrailerForTesting();
 }
 
+uint32_t CPDF_Parser::GetTrailerObjectNumber() const {
+  return m_CrossRefTable->trailer_object_number();
+}
+
 RetainPtr<CPDF_Dictionary> CPDF_Parser::GetCombinedTrailer() const {
   return m_CrossRefTable->trailer()
              ? ToDictionary(m_CrossRefTable->trailer()->Clone())
@@ -839,7 +953,7 @@
 }
 
 uint32_t CPDF_Parser::GetInfoObjNum() const {
-  const CPDF_Reference* pRef =
+  RetainPtr<const CPDF_Reference> pRef =
       ToReference(m_CrossRefTable->trailer()
                       ? m_CrossRefTable->trailer()->GetObjectFor("Info")
                       : nullptr);
@@ -847,7 +961,7 @@
 }
 
 uint32_t CPDF_Parser::GetRootObjNum() const {
-  const CPDF_Reference* pRef =
+  RetainPtr<const CPDF_Reference> pRef =
       ToReference(m_CrossRefTable->trailer()
                       ? m_CrossRefTable->trailer()->GetObjectFor("Root")
                       : nullptr);
@@ -859,10 +973,10 @@
     return nullptr;
 
   // Prevent circular parsing the same object.
-  if (pdfium::ContainsKey(m_ParsingObjNums, objnum))
+  if (pdfium::Contains(m_ParsingObjNums, objnum))
     return nullptr;
 
-  pdfium::ScopedSetInsertion<uint32_t> local_insert(&m_ParsingObjNums, objnum);
+  ScopedSetInsertion<uint32_t> local_insert(&m_ParsingObjNums, objnum);
   if (GetObjectType(objnum) == ObjectType::kNotCompressed) {
     FX_FILESIZE pos = GetObjectPositionOrZero(objnum);
     if (pos <= 0)
@@ -872,22 +986,20 @@
   if (GetObjectType(objnum) != ObjectType::kCompressed)
     return nullptr;
 
-  const CPDF_ObjectStream* pObjStream =
-      GetObjectStream(m_CrossRefTable->GetObjectInfo(objnum)->archive_obj_num);
+  const ObjectInfo& info = *m_CrossRefTable->GetObjectInfo(objnum);
+  const CPDF_ObjectStream* pObjStream = GetObjectStream(info.archive.obj_num);
   if (!pObjStream)
     return nullptr;
 
-  return pObjStream->ParseObject(m_pObjectsHolder.Get(), objnum);
+  return pObjStream->ParseObject(m_pObjectsHolder, objnum,
+                                 info.archive.obj_index);
 }
 
 const CPDF_ObjectStream* CPDF_Parser::GetObjectStream(uint32_t object_number) {
   // Prevent circular parsing the same object.
-  if (pdfium::ContainsKey(m_ParsingObjNums, object_number))
+  if (pdfium::Contains(m_ParsingObjNums, object_number))
     return nullptr;
 
-  pdfium::ScopedSetInsertion<uint32_t> local_insert(&m_ParsingObjNums,
-                                                    object_number);
-
   auto it = m_ObjectStreamMap.find(object_number);
   if (it != m_ObjectStreamMap.end())
     return it->second.get();
@@ -900,13 +1012,16 @@
   if (object_pos <= 0)
     return nullptr;
 
+  // Keep track of `object_number` before doing more parsing.
+  ScopedSetInsertion<uint32_t> local_insert(&m_ParsingObjNums, object_number);
+
   RetainPtr<CPDF_Object> object =
       ParseIndirectObjectAt(object_pos, object_number);
   if (!object)
     return nullptr;
 
   std::unique_ptr<CPDF_ObjectStream> objs_stream =
-      CPDF_ObjectStream::Create(ToStream(object.Get()));
+      CPDF_ObjectStream::Create(ToStream(object));
   const CPDF_ObjectStream* result = objs_stream.get();
   m_ObjectStreamMap[object_number] = std::move(objs_stream);
 
@@ -919,7 +1034,7 @@
   m_pSyntax->SetPos(pos);
 
   auto result = m_pSyntax->GetIndirectObject(
-      m_pObjectsHolder.Get(), CPDF_SyntaxParser::ParseType::kLoose);
+      m_pObjectsHolder, CPDF_SyntaxParser::ParseType::kLoose);
   m_pSyntax->SetPos(saved_pos);
   if (result && objnum && result->GetObjNum() != objnum)
     return nullptr;
@@ -934,11 +1049,15 @@
   return result;
 }
 
+FX_FILESIZE CPDF_Parser::GetDocumentSize() const {
+  return m_pSyntax->GetDocumentSize();
+}
+
 uint32_t CPDF_Parser::GetFirstPageNo() const {
   return m_pLinearized ? m_pLinearized->GetFirstPageNo() : 0;
 }
 
-void CPDF_Parser::SetLinearizedHeader(
+void CPDF_Parser::SetLinearizedHeaderForTesting(
     std::unique_ptr<CPDF_LinearizedHeader> pLinearized) {
   m_pLinearized = std::move(pLinearized);
 }
@@ -947,7 +1066,7 @@
   if (m_pSyntax->GetKeyword() != "trailer")
     return nullptr;
 
-  return ToDictionary(m_pSyntax->GetObjectBody(m_pObjectsHolder.Get()));
+  return ToDictionary(m_pSyntax->GetObjectBody(m_pObjectsHolder));
 }
 
 uint32_t CPDF_Parser::GetPermissions() const {
@@ -959,15 +1078,15 @@
 }
 
 CPDF_Parser::Error CPDF_Parser::StartLinearizedParse(
-    const RetainPtr<CPDF_ReadValidator>& validator,
-    const char* password) {
-  ASSERT(!m_bHasParsed);
-  ASSERT(!m_bXRefTableRebuilt);
+    RetainPtr<CPDF_ReadValidator> validator,
+    const ByteString& password) {
+  DCHECK(!m_bHasParsed);
+  DCHECK(!m_bXRefTableRebuilt);
   SetPassword(password);
   m_bXRefStream = false;
   m_LastXRefOffset = 0;
 
-  if (!InitSyntaxParser(validator))
+  if (!InitSyntaxParser(std::move(validator)))
     return FORMAT_ERROR;
 
   m_pLinearized = ParseLinearizedHeader();
@@ -991,10 +1110,16 @@
     if (!trailer)
       return SUCCESS;
 
-    m_CrossRefTable->SetTrailer(std::move(trailer));
-    int32_t xrefsize = GetDirectInteger(GetTrailer(), "Size");
-    if (xrefsize > 0)
-      ShrinkObjectMap(xrefsize);
+    m_CrossRefTable->SetTrailer(std::move(trailer), kNoV4TrailerObjectNumber);
+    const int32_t xrefsize = GetTrailer()->GetDirectIntegerFor("Size");
+    if (xrefsize > 0) {
+      // Check if `xrefsize` is correct. If it is incorrect, give up and rebuild
+      // the xref table.
+      const uint32_t expected_last_obj_num = xrefsize - 1;
+      if (GetLastObjNum() != expected_last_obj_num && !RebuildCrossRef()) {
+        return FORMAT_ERROR;
+      }
+    }
   }
 
   Error eRet = SetEncryptHandler();
@@ -1029,8 +1154,9 @@
   }
 
   if (m_pSecurityHandler && m_pSecurityHandler->IsMetadataEncrypted()) {
-    if (CPDF_Reference* pMetadata =
-            ToReference(GetRoot()->GetObjectFor("Metadata")))
+    RetainPtr<const CPDF_Reference> pMetadata =
+        ToReference(GetRoot()->GetObjectFor("Metadata"));
+    if (pMetadata)
       m_MetadataObjnum = pMetadata->GetRefObjNum();
   }
   return SUCCESS;
@@ -1048,7 +1174,7 @@
       return false;
 
     // Check for circular references.
-    if (pdfium::ContainsKey(seen_xref_offset, xref_offset))
+    if (pdfium::Contains(seen_xref_offset, xref_offset))
       return false;
   }
   m_ObjectStreamMap.clear();
@@ -1078,16 +1204,69 @@
   return SUCCESS;
 }
 
-CPDF_Parser::ObjectType CPDF_Parser::GetObjectTypeFromCrossRefStreamType(
-    uint32_t cross_ref_stream_type) const {
-  switch (cross_ref_stream_type) {
-    case 0:
-      return CPDF_Parser::ObjectType::kFree;
-    case 1:
-      return CPDF_Parser::ObjectType::kNotCompressed;
-    case 2:
-      return CPDF_Parser::ObjectType::kCompressed;
-    default:
-      return CPDF_Parser::ObjectType::kNull;
+void CPDF_Parser::SetSyntaxParserForTesting(
+    std::unique_ptr<CPDF_SyntaxParser> parser) {
+  m_pSyntax = std::move(parser);
+}
+
+std::vector<unsigned int> CPDF_Parser::GetTrailerEnds() {
+  std::vector<unsigned int> trailer_ends;
+  m_pSyntax->SetTrailerEnds(&trailer_ends);
+
+  // Traverse the document.
+  m_pSyntax->SetPos(0);
+  while (true) {
+    CPDF_SyntaxParser::WordResult word_result = m_pSyntax->GetNextWord();
+    if (word_result.is_number) {
+      // The object number was read. Read the generation number.
+      word_result = m_pSyntax->GetNextWord();
+      if (!word_result.is_number)
+        break;
+
+      word_result = m_pSyntax->GetNextWord();
+      if (word_result.word != "obj")
+        break;
+
+      m_pSyntax->GetObjectBody(nullptr);
+
+      word_result = m_pSyntax->GetNextWord();
+      if (word_result.word != "endobj")
+        break;
+    } else if (word_result.word == "trailer") {
+      m_pSyntax->GetObjectBody(nullptr);
+    } else if (word_result.word == "startxref") {
+      m_pSyntax->GetNextWord();
+    } else if (word_result.word == "xref") {
+      while (true) {
+        word_result = m_pSyntax->GetNextWord();
+        if (word_result.word.IsEmpty() || word_result.word == "startxref")
+          break;
+      }
+      m_pSyntax->GetNextWord();
+    } else {
+      break;
+    }
   }
+
+  // Stop recording trailer ends.
+  m_pSyntax->SetTrailerEnds(nullptr);
+  return trailer_ends;
+}
+
+bool CPDF_Parser::WriteToArchive(IFX_ArchiveStream* archive,
+                                 FX_FILESIZE src_size) {
+  static constexpr FX_FILESIZE kBufferSize = 4096;
+  DataVector<uint8_t> buffer(kBufferSize);
+  m_pSyntax->SetPos(0);
+  while (src_size) {
+    const uint32_t block_size =
+        static_cast<uint32_t>(std::min(kBufferSize, src_size));
+    auto block_span = pdfium::make_span(buffer).first(block_size);
+    if (!m_pSyntax->ReadBlock(block_span))
+      return false;
+    if (!archive->WriteBlock(pdfium::make_span(buffer).first(block_size)))
+      return false;
+    src_size -= block_size;
+  }
+  return true;
 }
diff --git a/core/fpdfapi/parser/cpdf_parser.h b/core/fpdfapi/parser/cpdf_parser.h
index d44244a..09dc724 100644
--- a/core/fpdfapi/parser/cpdf_parser.h
+++ b/core/fpdfapi/parser/cpdf_parser.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,9 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_PARSER_H_
 #define CORE_FPDFAPI_PARSER_CPDF_PARSER_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <limits>
 #include <map>
 #include <memory>
@@ -15,13 +18,12 @@
 
 #include "core/fpdfapi/parser/cpdf_cross_ref_table.h"
 #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/fx_types.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_Array;
-class CPDF_CryptoHandler;
 class CPDF_Dictionary;
 class CPDF_LinearizedHeader;
 class CPDF_Object;
@@ -29,10 +31,14 @@
 class CPDF_ReadValidator;
 class CPDF_SecurityHandler;
 class CPDF_SyntaxParser;
+class IFX_ArchiveStream;
 class IFX_SeekableReadStream;
 
 class CPDF_Parser {
  public:
+  using ObjectType = CPDF_CrossRefTable::ObjectType;
+  using ObjectInfo = CPDF_CrossRefTable::ObjectInfo;
+
   class ParsedObjectsHolder : public CPDF_IndirectObjectHolder {
    public:
     virtual bool TryInit() = 0;
@@ -53,18 +59,18 @@
   // are non-consecutive.
   static constexpr uint32_t kMaxObjectNumber = 4 * 1024 * 1024;
 
-  static const size_t kInvalidPos = std::numeric_limits<size_t>::max();
+  static constexpr size_t kInvalidPos = std::numeric_limits<size_t>::max();
 
   explicit CPDF_Parser(ParsedObjectsHolder* holder);
   CPDF_Parser();
   ~CPDF_Parser();
 
-  Error StartParse(const RetainPtr<IFX_SeekableReadStream>& pFile,
-                   const char* password);
-  Error StartLinearizedParse(const RetainPtr<CPDF_ReadValidator>& validator,
-                             const char* password);
+  Error StartParse(RetainPtr<IFX_SeekableReadStream> pFile,
+                   const ByteString& password);
+  Error StartLinearizedParse(RetainPtr<CPDF_ReadValidator> validator,
+                             const ByteString& password);
 
-  void SetPassword(const char* password) { m_Password = password; }
+  void SetPassword(const ByteString& password) { m_Password = password; }
   ByteString GetPassword() const { return m_Password; }
 
   // Take the GetPassword() value and encode it, if necessary, based on the
@@ -73,6 +79,7 @@
 
   const CPDF_Dictionary* GetTrailer() const;
   CPDF_Dictionary* GetMutableTrailerForTesting();
+  uint32_t GetTrailerObjectNumber() const;
 
   // Returns a new trailer which combines the last read trailer with the /Root
   // and /Info from previous ones.
@@ -83,10 +90,9 @@
   uint32_t GetPermissions() const;
   uint32_t GetRootObjNum() const;
   uint32_t GetInfoObjNum() const;
-  const CPDF_Array* GetIDArray() const;
-  CPDF_Dictionary* GetRoot() const;
-
-  const CPDF_Dictionary* GetEncryptDict() const;
+  RetainPtr<const CPDF_Array> GetIDArray() const;
+  RetainPtr<const CPDF_Dictionary> GetRoot() const;
+  RetainPtr<const CPDF_Dictionary> GetEncryptDict() const;
 
   RetainPtr<CPDF_Object> ParseIndirectObject(uint32_t objnum);
 
@@ -105,6 +111,7 @@
   RetainPtr<CPDF_Object> ParseIndirectObjectAt(FX_FILESIZE pos,
                                                uint32_t objnum);
 
+  FX_FILESIZE GetDocumentSize() const;
   uint32_t GetFirstPageNo() const;
   const CPDF_LinearizedHeader* GetLinearizedHeader() const {
     return m_pLinearized.get();
@@ -116,24 +123,22 @@
 
   bool xref_table_rebuilt() const { return m_bXRefTableRebuilt; }
 
-  CPDF_SyntaxParser* GetSyntax() const { return m_pSyntax.get(); }
+  std::vector<unsigned int> GetTrailerEnds();
+  bool WriteToArchive(IFX_ArchiveStream* archive, FX_FILESIZE src_size);
 
-  void SetLinearizedHeader(std::unique_ptr<CPDF_LinearizedHeader> pLinearized);
+  void SetLinearizedHeaderForTesting(
+      std::unique_ptr<CPDF_LinearizedHeader> pLinearized);
 
  protected:
-  using ObjectType = CPDF_CrossRefTable::ObjectType;
-  using ObjectInfo = CPDF_CrossRefTable::ObjectInfo;
-
   bool LoadCrossRefV4(FX_FILESIZE pos, bool bSkip);
   bool RebuildCrossRef();
+  Error StartParseInternal();
+  FX_FILESIZE ParseStartXRef();
+  std::unique_ptr<CPDF_LinearizedHeader> ParseLinearizedHeader();
 
-  std::unique_ptr<CPDF_SyntaxParser> m_pSyntax;
+  void SetSyntaxParserForTesting(std::unique_ptr<CPDF_SyntaxParser> parser);
 
  private:
-  friend class cpdf_parser_BadStartXrefShouldNotBuildCrossRefTable_Test;
-  friend class cpdf_parser_ParseStartXRefWithHeaderOffset_Test;
-  friend class cpdf_parser_ParseStartXRef_Test;
-  friend class cpdf_parser_ParseLinearizedWithHeaderOffset_Test;
   friend class CPDF_DataAvail;
 
   struct CrossRefObjData {
@@ -141,11 +146,12 @@
     ObjectInfo info;
   };
 
-  Error StartParseInternal();
-  FX_FILESIZE ParseStartXRef();
   bool LoadAllCrossRefV4(FX_FILESIZE xref_offset);
   bool LoadAllCrossRefV5(FX_FILESIZE xref_offset);
   bool LoadCrossRefV5(FX_FILESIZE* pos, bool bMainXRef);
+  void ProcessCrossRefV5Entry(pdfium::span<const uint8_t> entry_span,
+                              pdfium::span<const uint32_t> field_widths,
+                              uint32_t obj_num);
   RetainPtr<CPDF_Dictionary> LoadTrailerV4();
   Error SetEncryptHandler();
   void ReleaseEncryptHandler();
@@ -153,8 +159,6 @@
   bool LoadLinearizedAllCrossRefV5(FX_FILESIZE main_xref_offset);
   Error LoadLinearizedMainXRefTable();
   const CPDF_ObjectStream* GetObjectStream(uint32_t object_number);
-  std::unique_ptr<CPDF_LinearizedHeader> ParseLinearizedHeader();
-  void ShrinkObjectMap(uint32_t size);
   // A simple check whether the cross reference table matches with
   // the objects.
   bool VerifyCrossRefV4();
@@ -168,13 +172,12 @@
   bool ParseCrossRefV4(std::vector<CrossRefObjData>* out_objects);
   void MergeCrossRefObjectsData(const std::vector<CrossRefObjData>& objects);
 
-  bool InitSyntaxParser(const RetainPtr<CPDF_ReadValidator>& validator);
+  bool InitSyntaxParser(RetainPtr<CPDF_ReadValidator> validator);
   bool ParseFileVersion();
 
   ObjectType GetObjectType(uint32_t objnum) const;
-  ObjectType GetObjectTypeFromCrossRefStreamType(
-      uint32_t cross_ref_stream_type) const;
 
+  std::unique_ptr<CPDF_SyntaxParser> m_pSyntax;
   std::unique_ptr<ParsedObjectsHolder> m_pOwnedObjectsHolder;
   UnownedPtr<ParsedObjectsHolder> m_pObjectsHolder;
 
@@ -182,11 +185,11 @@
   bool m_bXRefStream = false;
   bool m_bXRefTableRebuilt = false;
   int m_FileVersion = 0;
+  uint32_t m_MetadataObjnum = 0;
   // m_CrossRefTable must be destroyed after m_pSecurityHandler due to the
   // ownership of the ID array data.
   std::unique_ptr<CPDF_CrossRefTable> m_CrossRefTable;
-  FX_FILESIZE m_LastXRefOffset;
-  RetainPtr<CPDF_SecurityHandler> m_pSecurityHandler;
+  FX_FILESIZE m_LastXRefOffset = 0;
   ByteString m_Password;
   std::unique_ptr<CPDF_LinearizedHeader> m_pLinearized;
 
@@ -196,7 +199,7 @@
   // All indirect object numbers that are being parsed.
   std::set<uint32_t> m_ParsingObjNums;
 
-  uint32_t m_MetadataObjnum = 0;
+  RetainPtr<CPDF_SecurityHandler> m_pSecurityHandler;
 };
 
 #endif  // CORE_FPDFAPI_PARSER_CPDF_PARSER_H_
diff --git a/core/fpdfapi/parser/cpdf_parser_embeddertest.cpp b/core/fpdfapi/parser/cpdf_parser_embeddertest.cpp
index 9294534..17ac00f 100644
--- a/core/fpdfapi/parser/cpdf_parser_embeddertest.cpp
+++ b/core/fpdfapi/parser/cpdf_parser_embeddertest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,45 +8,45 @@
 
 class CPDFParserEmbedderTest : public EmbedderTest {};
 
-TEST_F(CPDFParserEmbedderTest, LoadError_454695) {
+TEST_F(CPDFParserEmbedderTest, LoadErrorBug454695) {
   // Test a dictionary with hex string instead of correct content.
   // Verify that the defective pdf shouldn't be opened correctly.
   EXPECT_FALSE(OpenDocument("bug_454695.pdf"));
 }
 
-TEST_F(CPDFParserEmbedderTest, Bug_481363) {
+TEST_F(CPDFParserEmbedderTest, Bug481363) {
   // Test colorspace object with malformed dictionary.
-  EXPECT_TRUE(OpenDocument("bug_481363.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_481363.pdf"));
   FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
+  EXPECT_TRUE(page);
   UnloadPage(page);
 }
 
-TEST_F(CPDFParserEmbedderTest, Bug_544880) {
+TEST_F(CPDFParserEmbedderTest, Bug544880) {
   // Test self referencing /Pages object.
-  EXPECT_TRUE(OpenDocument("bug_544880.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_544880.pdf"));
   // Shouldn't crash. We don't check the return value here because we get the
   // the count from the "/Count 1" in the testcase (at the time of writing)
   // rather than the actual count (0).
   (void)GetPageCount();
 }
 
-TEST_F(CPDFParserEmbedderTest, Bug_325a) {
+TEST_F(CPDFParserEmbedderTest, Bug325a) {
   EXPECT_FALSE(OpenDocument("bug_325_a.pdf"));
 }
 
-TEST_F(CPDFParserEmbedderTest, Bug_325b) {
+TEST_F(CPDFParserEmbedderTest, Bug325b) {
   EXPECT_FALSE(OpenDocument("bug_325_b.pdf"));
 }
 
-TEST_F(CPDFParserEmbedderTest, Bug_602650) {
+TEST_F(CPDFParserEmbedderTest, Bug602650) {
   // Test the case that cross reference entries, which are well formed,
   // but do not match with the objects.
-  EXPECT_TRUE(OpenDocument("bug_602650.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_602650.pdf"));
   FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
+  EXPECT_TRUE(page);
   FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
-  EXPECT_NE(nullptr, text_page);
+  EXPECT_TRUE(text_page);
   // The page should not be blank.
   EXPECT_LT(0, FPDFText_CountChars(text_page));
 
@@ -54,29 +54,34 @@
   UnloadPage(page);
 }
 
-TEST_F(CPDFParserEmbedderTest, Bug_757705) {
-  EXPECT_TRUE(OpenDocument("bug_757705.pdf"));
+TEST_F(CPDFParserEmbedderTest, Bug757705) {
+  ASSERT_TRUE(OpenDocument("bug_757705.pdf"));
 }
 
 TEST_F(CPDFParserEmbedderTest, LoadMainCrossRefTable) {
-  EXPECT_TRUE(OpenDocumentLinearized("feature_linearized_loading.pdf"));
+  ASSERT_TRUE(OpenDocumentLinearized("feature_linearized_loading.pdf"));
   // To check that main cross ref table is loaded correctly,will be enough to
   // check that the second page was correctly loaded. Because it is contains
   // crossrefs for second page.
   EXPECT_EQ(2, GetPageCount());
   FPDF_PAGE page = LoadPage(1);
-  EXPECT_NE(nullptr, page);
+  EXPECT_TRUE(page);
   FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
-  EXPECT_NE(nullptr, text_page);
+  EXPECT_TRUE(text_page);
   // The page should not be blank.
   EXPECT_LT(0, FPDFText_CountChars(text_page));
   FPDFText_ClosePage(text_page);
   UnloadPage(page);
 }
 
-TEST_F(CPDFParserEmbedderTest, Bug_828049) {
-  EXPECT_TRUE(OpenDocument("bug_828049.pdf"));
+TEST_F(CPDFParserEmbedderTest, Bug828049) {
+  ASSERT_TRUE(OpenDocument("bug_828049.pdf"));
   FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
+  EXPECT_TRUE(page);
   UnloadPage(page);
 }
+
+// crbug.com/1191313
+TEST_F(CPDFParserEmbedderTest, InvalidDictionaryKeys) {
+  ASSERT_TRUE(OpenDocument("bad_dict_keys.pdf"));
+}
diff --git a/core/fpdfapi/parser/cpdf_parser_unittest.cpp b/core/fpdfapi/parser/cpdf_parser_unittest.cpp
index 06328f0..0759807 100644
--- a/core/fpdfapi/parser/cpdf_parser_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_parser_unittest.cpp
@@ -1,39 +1,105 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "core/fpdfapi/parser/cpdf_parser.h"
+
 #include <limits>
 #include <memory>
+#include <ostream>
 #include <string>
+#include <utility>
 #include <vector>
 
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_linearized_header.h"
 #include "core/fpdfapi/parser/cpdf_object.h"
-#include "core/fpdfapi/parser/cpdf_parser.h"
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/utils/path_service.h"
-#include "third_party/base/ptr_util.h"
+
+using testing::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 {
+ public:
+  TestObjectsHolder() = default;
+  ~TestObjectsHolder() override = default;
+
+  // CPDF_Parser::ParsedObjectsHolder:
+  bool TryInit() override { return true; }
+  MOCK_METHOD1(ParseIndirectObject, RetainPtr<CPDF_Object>(uint32_t objnum));
+};
+
 }  // 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:
-  CPDF_TestParser() {}
-  ~CPDF_TestParser() {}
+  CPDF_TestParser() : CPDF_Parser(&object_holder_) {}
+  ~CPDF_TestParser() = default;
 
   // Setup reading from a file and initial states.
   bool InitTestFromFile(const char* path) {
@@ -43,15 +109,16 @@
       return false;
 
     // For the test file, the header is set at the beginning.
-    m_pSyntax = pdfium::MakeUnique<CPDF_SyntaxParser>(pFileAccess);
+    SetSyntaxParserForTesting(
+        std::make_unique<CPDF_SyntaxParser>(std::move(pFileAccess)));
     return true;
   }
 
   // Setup reading from a buffer and initial states.
   bool InitTestFromBufferWithOffset(pdfium::span<const uint8_t> buffer,
                                     FX_FILESIZE header_offset) {
-    m_pSyntax = CPDF_SyntaxParser::CreateForTesting(
-        pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(buffer), header_offset);
+    SetSyntaxParserForTesting(CPDF_SyntaxParser::CreateForTesting(
+        pdfium::MakeRetain<CFX_ReadOnlySpanStream>(buffer), header_offset));
     return true;
   }
 
@@ -59,17 +126,20 @@
     return InitTestFromBufferWithOffset(buffer, 0 /*header_offset*/);
   }
 
+  // Expose protected CPDF_Parser methods for testing.
+  using CPDF_Parser::LoadCrossRefV4;
+  using CPDF_Parser::ParseLinearizedHeader;
+  using CPDF_Parser::ParseStartXRef;
+  using CPDF_Parser::RebuildCrossRef;
+  using CPDF_Parser::StartParseInternal;
+
+  TestObjectsHolder& object_holder() { return object_holder_; }
+
  private:
-  // Add test cases here as private friend so that protected members in
-  // CPDF_Parser can be accessed by test cases.
-  // Need to access RebuildCrossRef.
-  FRIEND_TEST(cpdf_parser, RebuildCrossRefCorrectly);
-  FRIEND_TEST(cpdf_parser, RebuildCrossRefFailed);
-  // Need to access LoadCrossRefV4.
-  FRIEND_TEST(cpdf_parser, LoadCrossRefV4);
+  TestObjectsHolder object_holder_;
 };
 
-TEST(cpdf_parser, RebuildCrossRefCorrectly) {
+TEST(ParserTest, RebuildCrossRefCorrectly) {
   CPDF_TestParser parser;
   std::string test_file;
   ASSERT_TRUE(PathService::GetTestFilePath("parser_rebuildxref_correct.pdf",
@@ -79,13 +149,17 @@
   ASSERT_TRUE(parser.RebuildCrossRef());
   const FX_FILESIZE offsets[] = {0, 15, 61, 154, 296, 374, 450};
   const uint16_t versions[] = {0, 0, 2, 4, 6, 8, 0};
-  for (size_t i = 0; i < FX_ArraySize(offsets); ++i)
+  for (size_t i = 0; i < std::size(offsets); ++i)
     EXPECT_EQ(offsets[i], GetObjInfo(parser, i).pos);
-  for (size_t i = 0; i < FX_ArraySize(versions); ++i)
+  for (size_t i = 0; i < std::size(versions); ++i)
     EXPECT_EQ(versions[i], GetObjInfo(parser, i).gennum);
+
+  const CPDF_CrossRefTable* cross_ref_table = parser.GetCrossRefTable();
+  ASSERT_TRUE(cross_ref_table);
+  EXPECT_EQ(0u, cross_ref_table->trailer_object_number());
 }
 
-TEST(cpdf_parser, RebuildCrossRefFailed) {
+TEST(ParserTest, RebuildCrossRefFailed) {
   CPDF_TestParser parser;
   std::string test_file;
   ASSERT_TRUE(PathService::GetTestFilePath(
@@ -95,7 +169,7 @@
   ASSERT_FALSE(parser.RebuildCrossRef());
 }
 
-TEST(cpdf_parser, LoadCrossRefV4) {
+TEST(ParserTest, LoadCrossRefV4) {
   {
     static const unsigned char kXrefTable[] =
         "xref \n"
@@ -119,9 +193,9 @@
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kNotCompressed,
         CPDF_TestParser::ObjectType::kNotCompressed};
-    static_assert(FX_ArraySize(kOffsets) == FX_ArraySize(kTypes),
+    static_assert(std::size(kOffsets) == std::size(kTypes),
                   "kOffsets / kTypes size mismatch");
-    for (size_t i = 0; i < FX_ArraySize(kOffsets); ++i) {
+    for (size_t i = 0; i < std::size(kOffsets); ++i) {
       EXPECT_EQ(kOffsets[i], GetObjInfo(parser, i).pos);
       EXPECT_EQ(kTypes[i], GetObjInfo(parser, i).type);
     }
@@ -159,9 +233,9 @@
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kNotCompressed};
-    static_assert(FX_ArraySize(kOffsets) == FX_ArraySize(kTypes),
+    static_assert(std::size(kOffsets) == std::size(kTypes),
                   "kOffsets / kTypes size mismatch");
-    for (size_t i = 0; i < FX_ArraySize(kOffsets); ++i) {
+    for (size_t i = 0; i < std::size(kOffsets); ++i) {
       EXPECT_EQ(kOffsets[i], GetObjInfo(parser, i).pos);
       EXPECT_EQ(kTypes[i], GetObjInfo(parser, i).type);
     }
@@ -199,9 +273,9 @@
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kNotCompressed};
-    static_assert(FX_ArraySize(kOffsets) == FX_ArraySize(kTypes),
+    static_assert(std::size(kOffsets) == std::size(kTypes),
                   "kOffsets / kTypes size mismatch");
-    for (size_t i = 0; i < FX_ArraySize(kOffsets); ++i) {
+    for (size_t i = 0; i < std::size(kOffsets); ++i) {
       EXPECT_EQ(kOffsets[i], GetObjInfo(parser, i).pos);
       EXPECT_EQ(kTypes[i], GetObjInfo(parser, i).type);
     }
@@ -231,9 +305,9 @@
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kNotCompressed,
         CPDF_TestParser::ObjectType::kNotCompressed};
-    static_assert(FX_ArraySize(kOffsets) == FX_ArraySize(kTypes),
+    static_assert(std::size(kOffsets) == std::size(kTypes),
                   "kOffsets / kTypes size mismatch");
-    for (size_t i = 0; i < FX_ArraySize(kOffsets); ++i) {
+    for (size_t i = 0; i < std::size(kOffsets); ++i) {
       EXPECT_EQ(kOffsets[i], GetObjInfo(parser, i).pos);
       EXPECT_EQ(kTypes[i], GetObjInfo(parser, i).type);
     }
@@ -264,7 +338,7 @@
   }
 }
 
-TEST(cpdf_parser, ParseStartXRef) {
+TEST(ParserTest, ParseStartXRef) {
   CPDF_TestParser parser;
   std::string test_file;
   ASSERT_TRUE(
@@ -278,7 +352,7 @@
   EXPECT_EQ(75u, cross_ref_v5_obj->GetObjNum());
 }
 
-TEST(cpdf_parser, ParseStartXRefWithHeaderOffset) {
+TEST(ParserTest, ParseStartXRefWithHeaderOffset) {
   static constexpr FX_FILESIZE kTestHeaderOffset = 765;
   std::string test_file;
   ASSERT_TRUE(
@@ -288,8 +362,8 @@
   ASSERT_TRUE(pFileAccess);
 
   std::vector<unsigned char> data(pFileAccess->GetSize() + kTestHeaderOffset);
-  ASSERT_TRUE(pFileAccess->ReadBlockAtOffset(&data.front() + kTestHeaderOffset,
-                                             0, pFileAccess->GetSize()));
+  ASSERT_TRUE(pFileAccess->ReadBlockAtOffset(
+      pdfium::make_span(data).subspan(kTestHeaderOffset), 0));
   CPDF_TestParser parser;
   parser.InitTestFromBufferWithOffset(data, kTestHeaderOffset);
 
@@ -300,7 +374,7 @@
   EXPECT_EQ(75u, cross_ref_v5_obj->GetObjNum());
 }
 
-TEST(cpdf_parser, ParseLinearizedWithHeaderOffset) {
+TEST(ParserTest, ParseLinearizedWithHeaderOffset) {
   static constexpr FX_FILESIZE kTestHeaderOffset = 765;
   std::string test_file;
   ASSERT_TRUE(PathService::GetTestFilePath("linearized.pdf", &test_file));
@@ -309,15 +383,19 @@
   ASSERT_TRUE(pFileAccess);
 
   std::vector<unsigned char> data(pFileAccess->GetSize() + kTestHeaderOffset);
-  ASSERT_TRUE(pFileAccess->ReadBlockAtOffset(&data.front() + kTestHeaderOffset,
-                                             0, pFileAccess->GetSize()));
+  ASSERT_TRUE(pFileAccess->ReadBlockAtOffset(
+      pdfium::make_span(data).subspan(kTestHeaderOffset), 0));
+
   CPDF_TestParser parser;
   parser.InitTestFromBufferWithOffset(data, kTestHeaderOffset);
-
   EXPECT_TRUE(parser.ParseLinearizedHeader());
+
+  const CPDF_CrossRefTable* cross_ref_table = parser.GetCrossRefTable();
+  ASSERT_TRUE(cross_ref_table);
+  EXPECT_EQ(0u, cross_ref_table->trailer_object_number());
 }
 
-TEST(cpdf_parser, BadStartXrefShouldNotBuildCrossRefTable) {
+TEST(ParserTest, BadStartXrefShouldNotBuildCrossRefTable) {
   const unsigned char kData[] =
       "%PDF1-7 0 obj <</Size 2 /W [0 0 0]\n>>\n"
       "stream\n"
@@ -333,3 +411,412 @@
   ASSERT_TRUE(parser.GetCrossRefTable());
   EXPECT_EQ(0u, parser.GetCrossRefTable()->objects_info().size());
 }
+
+class ParserXRefTest : public testing::Test {
+ public:
+  ParserXRefTest() = default;
+  ~ParserXRefTest() override = default;
+
+  // 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,
+                "Unexpected kMaxObjectNumber");
+  const unsigned char kData[] =
+      "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
+      "7 0 obj <<\n"
+      "  /Filter /ASCIIHexDecode\n"
+      "  /Index [4194303 3]\n"
+      "  /Root 1 0 R\n"
+      "  /Size 4194306\n"
+      "  /W [1 1 1]\n"
+      ">>\n"
+      "stream\n"
+      "01 00 00\n"
+      "01 0F 00\n"
+      "01 12 00\n"
+      "endstream\n"
+      "endobj\n"
+      "startxref\n"
+      "14\n"
+      "%%EOF\n";
+  ASSERT_TRUE(parser().InitTestFromBuffer(kData));
+  EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
+  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.
+  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.
+  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_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"
+      "7 0 obj <<\n"
+      "  /Filter /ASCIIHexDecode\n"
+      "  /Root 1 0 R\n"
+      "  /Size 3\n"
+      "  /W [1 1 1]\n"
+      ">>\n"
+      "stream\n"
+      "02 FF 00\n"
+      "01 0F 00\n"
+      "01 12 00\n"
+      "endstream\n"
+      "endobj\n"
+      "startxref\n"
+      "14\n"
+      "%%EOF\n";
+  ASSERT_TRUE(parser().InitTestFromBuffer(kData));
+  EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
+  EXPECT_FALSE(parser().xref_table_rebuilt());
+
+  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();
+
+  // 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;
+
+  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_read_validator.cpp b/core/fpdfapi/parser/cpdf_read_validator.cpp
index 80e6517..b6fd872 100644
--- a/core/fpdfapi/parser/cpdf_read_validator.cpp
+++ b/core/fpdfapi/parser/cpdf_read_validator.cpp
@@ -1,14 +1,15 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/parser/cpdf_read_validator.h"
 
 #include <algorithm>
+#include <utility>
 
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/logging.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
@@ -26,55 +27,55 @@
 
 }  // namespace
 
-CPDF_ReadValidator::Session::Session(
-    const RetainPtr<CPDF_ReadValidator>& validator)
-    : validator_(validator.BackPointer()) {
-  ASSERT(validator_);
-  saved_read_error_ = validator_->read_error_;
-  saved_has_unavailable_data_ = validator_->has_unavailable_data_;
+CPDF_ReadValidator::ScopedSession::ScopedSession(
+    RetainPtr<CPDF_ReadValidator> validator)
+    : validator_(std::move(validator)),
+      saved_read_error_(validator_->read_error_),
+      saved_has_unavailable_data_(validator_->has_unavailable_data_) {
   validator_->ResetErrors();
 }
 
-CPDF_ReadValidator::Session::~Session() {
+CPDF_ReadValidator::ScopedSession::~ScopedSession() {
   validator_->read_error_ |= saved_read_error_;
   validator_->has_unavailable_data_ |= saved_has_unavailable_data_;
 }
 
 CPDF_ReadValidator::CPDF_ReadValidator(
-    const RetainPtr<IFX_SeekableReadStream>& file_read,
+    RetainPtr<IFX_SeekableReadStream> file_read,
     CPDF_DataAvail::FileAvail* file_avail)
-    : file_read_(file_read),
+    : file_read_(std::move(file_read)),
       file_avail_(file_avail),
-      read_error_(false),
-      has_unavailable_data_(false),
-      whole_file_already_available_(false),
-      file_size_(file_read->GetSize()) {}
+      file_size_(file_read_->GetSize()) {}
 
-CPDF_ReadValidator::~CPDF_ReadValidator() {}
+CPDF_ReadValidator::~CPDF_ReadValidator() = default;
 
 void CPDF_ReadValidator::ResetErrors() {
   read_error_ = false;
   has_unavailable_data_ = false;
 }
 
-bool CPDF_ReadValidator::ReadBlockAtOffset(void* buffer,
-                                           FX_FILESIZE offset,
-                                           size_t size) {
-  FX_SAFE_FILESIZE end_offset = offset;
-  end_offset += size;
-  if (!end_offset.IsValid() || end_offset.ValueOrDie() > file_size_)
-    return false;
-
-  if (!IsDataRangeAvailable(offset, size)) {
-    ScheduleDownload(offset, size);
+bool CPDF_ReadValidator::ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                                           FX_FILESIZE offset) {
+  if (offset < 0) {
+    NOTREACHED();
     return false;
   }
 
-  if (file_read_->ReadBlockAtOffset(buffer, offset, size))
+  FX_SAFE_FILESIZE end_offset = offset;
+  end_offset += buffer.size();
+  if (!end_offset.IsValid() || end_offset.ValueOrDie() > file_size_)
+    return false;
+
+  if (!IsDataRangeAvailable(offset, buffer.size())) {
+    ScheduleDownload(offset, buffer.size());
+    return false;
+  }
+
+  if (file_read_->ReadBlockAtOffset(buffer, offset))
     return true;
 
   read_error_ = true;
-  ScheduleDownload(offset, size);
+  ScheduleDownload(offset, buffer.size());
   return false;
 }
 
@@ -116,8 +117,7 @@
   const FX_SAFE_SIZE_T safe_size = file_size_;
   whole_file_already_available_ =
       whole_file_already_available_ ||
-      (safe_size.IsValid() ? IsDataRangeAvailable(0, safe_size.ValueOrDie())
-                           : false);
+      (safe_size.IsValid() && IsDataRangeAvailable(0, safe_size.ValueOrDie()));
 
   return whole_file_already_available_;
 }
diff --git a/core/fpdfapi/parser/cpdf_read_validator.h b/core/fpdfapi/parser/cpdf_read_validator.h
index 6adde02..c866541 100644
--- a/core/fpdfapi/parser/cpdf_read_validator.h
+++ b/core/fpdfapi/parser/cpdf_read_validator.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,23 +6,29 @@
 #define CORE_FPDFAPI_PARSER_CPDF_READ_VALIDATOR_H_
 
 #include "core/fpdfapi/parser/cpdf_data_avail.h"
+#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_ReadValidator : public IFX_SeekableReadStream {
  public:
-  class Session {
+  class ScopedSession {
    public:
-    explicit Session(const RetainPtr<CPDF_ReadValidator>& validator);
-    ~Session();
+    FX_STACK_ALLOCATED();
+
+    explicit ScopedSession(RetainPtr<CPDF_ReadValidator> validator);
+    ScopedSession(const ScopedSession& that) = delete;
+    ScopedSession& operator=(const ScopedSession& that) = delete;
+    ~ScopedSession();
 
    private:
-    UnownedPtr<CPDF_ReadValidator> validator_;
-    bool saved_read_error_;
-    bool saved_has_unavailable_data_;
+    RetainPtr<CPDF_ReadValidator> const validator_;
+    const bool saved_read_error_;
+    const bool saved_has_unavailable_data_;
   };
 
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   void SetDownloadHints(CPDF_DataAvail::DownloadHints* hints) {
     hints_ = hints;
@@ -34,20 +40,17 @@
   }
 
   void ResetErrors();
-
   bool IsWholeFileAvailable();
-
   bool CheckDataRangeAndRequestIfUnavailable(FX_FILESIZE offset, size_t size);
   bool CheckWholeFileAndRequestIfUnavailable();
 
   // IFX_SeekableReadStream overrides:
-  bool ReadBlockAtOffset(void* buffer,
-                         FX_FILESIZE offset,
-                         size_t size) override;
+  bool ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                         FX_FILESIZE offset) override;
   FX_FILESIZE GetSize() override;
 
  protected:
-  CPDF_ReadValidator(const RetainPtr<IFX_SeekableReadStream>& file_read,
+  CPDF_ReadValidator(RetainPtr<IFX_SeekableReadStream> file_read,
                      CPDF_DataAvail::FileAvail* file_avail);
   ~CPDF_ReadValidator() override;
 
@@ -55,14 +58,12 @@
   void ScheduleDownload(FX_FILESIZE offset, size_t size);
   bool IsDataRangeAvailable(FX_FILESIZE offset, size_t size) const;
 
-  RetainPtr<IFX_SeekableReadStream> file_read_;
-  UnownedPtr<CPDF_DataAvail::FileAvail> file_avail_;
-
+  RetainPtr<IFX_SeekableReadStream> const file_read_;
+  UnownedPtr<CPDF_DataAvail::FileAvail> const file_avail_;
   UnownedPtr<CPDF_DataAvail::DownloadHints> hints_;
-
-  bool read_error_;
-  bool has_unavailable_data_;
-  bool whole_file_already_available_;
+  bool read_error_ = false;
+  bool has_unavailable_data_ = false;
+  bool whole_file_already_available_ = false;
   const FX_FILESIZE file_size_;
 };
 
diff --git a/core/fpdfapi/parser/cpdf_read_validator_unittest.cpp b/core/fpdfapi/parser/cpdf_read_validator_unittest.cpp
index 38b4bf9..bb3006a 100644
--- a/core/fpdfapi/parser/cpdf_read_validator_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_read_validator_unittest.cpp
@@ -1,14 +1,17 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/parser/cpdf_read_validator.h"
 
+#include <stdint.h>
+
 #include <limits>
 #include <utility>
-#include <vector>
 
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
+#include "core/fxcrt/cfx_read_only_vector_stream.h"
+#include "core/fxcrt/data_vector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/invalid_seekable_read_stream.h"
 
@@ -23,7 +26,7 @@
 class MockFileAvail final : public CPDF_DataAvail::FileAvail {
  public:
   MockFileAvail() : available_range_(0, 0) {}
-  ~MockFileAvail() override {}
+  ~MockFileAvail() override = default;
 
   bool IsDataAvail(FX_FILESIZE offset, size_t size) override {
     return available_range_.first <= offset &&
@@ -45,7 +48,7 @@
 class MockDownloadHints final : public CPDF_DataAvail::DownloadHints {
  public:
   MockDownloadHints() : last_requested_range_(0, 0) {}
-  ~MockDownloadHints() override {}
+  ~MockDownloadHints() override = default;
 
   void AddSegment(FX_FILESIZE offset, size_t size) override {
     last_requested_range_.first = offset;
@@ -64,42 +67,39 @@
 
 }  // namespace
 
-TEST(CPDF_ReadValidatorTest, UnavailableData) {
-  std::vector<uint8_t> test_data(kTestDataSize);
-  auto file = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(test_data);
+TEST(ReadValidatorTest, UnavailableData) {
+  DataVector<uint8_t> test_data(kTestDataSize);
+  auto file =
+      pdfium::MakeRetain<CFX_ReadOnlyVectorStream>(std::move(test_data));
   MockFileAvail file_avail;
-  auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
+  auto validator =
+      pdfium::MakeRetain<CPDF_ReadValidator>(std::move(file), &file_avail);
 
-  std::vector<uint8_t> read_buffer(100);
-  EXPECT_FALSE(validator->ReadBlockAtOffset(read_buffer.data(), 5000,
-                                            read_buffer.size()));
-
+  DataVector<uint8_t> read_buffer(100);
+  EXPECT_FALSE(validator->ReadBlockAtOffset(read_buffer, 5000));
   EXPECT_FALSE(validator->read_error());
   EXPECT_TRUE(validator->has_unavailable_data());
 
   validator->ResetErrors();
-
   file_avail.SetAvailableRange(5000, 5000 + read_buffer.size());
-
-  EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer.data(), 5000,
-                                           read_buffer.size()));
+  EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer, 5000));
   EXPECT_FALSE(validator->read_error());
   EXPECT_FALSE(validator->has_unavailable_data());
 }
 
-TEST(CPDF_ReadValidatorTest, UnavailableDataWithHints) {
-  std::vector<uint8_t> test_data(kTestDataSize);
-  auto file = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(test_data);
+TEST(ReadValidatorTest, UnavailableDataWithHints) {
+  DataVector<uint8_t> test_data(kTestDataSize);
+  auto file =
+      pdfium::MakeRetain<CFX_ReadOnlyVectorStream>(std::move(test_data));
   MockFileAvail file_avail;
-  auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
+  auto validator =
+      pdfium::MakeRetain<CPDF_ReadValidator>(std::move(file), &file_avail);
 
   MockDownloadHints hints;
   validator->SetDownloadHints(&hints);
 
-  std::vector<uint8_t> read_buffer(100);
-
-  EXPECT_FALSE(validator->ReadBlockAtOffset(read_buffer.data(), 5000,
-                                            read_buffer.size()));
+  DataVector<uint8_t> read_buffer(100);
+  EXPECT_FALSE(validator->ReadBlockAtOffset(read_buffer, 5000));
   EXPECT_FALSE(validator->read_error());
   EXPECT_TRUE(validator->has_unavailable_data());
 
@@ -110,8 +110,7 @@
   hints.Reset();
 
   validator->ResetErrors();
-  EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer.data(), 5000,
-                                           read_buffer.size()));
+  EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer, 5000));
   // No new request on already available data.
   EXPECT_EQ(MakeRange(0, 0), hints.GetLastRequstedRange());
   EXPECT_FALSE(validator->read_error());
@@ -120,8 +119,7 @@
   validator->ResetErrors();
   // Try read unavailable data at file end.
   EXPECT_FALSE(validator->ReadBlockAtOffset(
-      read_buffer.data(), validator->GetSize() - read_buffer.size(),
-      read_buffer.size()));
+      read_buffer, validator->GetSize() - read_buffer.size()));
   // Should not enlarge request at file end.
   EXPECT_EQ(validator->GetSize(), hints.GetLastRequstedRange().second);
   EXPECT_FALSE(validator->read_error());
@@ -130,63 +128,66 @@
   validator->SetDownloadHints(nullptr);
 }
 
-TEST(CPDF_ReadValidatorTest, ReadError) {
+TEST(ReadValidatorTest, ReadError) {
   auto file = pdfium::MakeRetain<InvalidSeekableReadStream>(kTestDataSize);
-  auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, nullptr);
+  auto validator =
+      pdfium::MakeRetain<CPDF_ReadValidator>(std::move(file), nullptr);
 
   static const uint32_t kBufferSize = 3 * 1000;
-  std::vector<uint8_t> buffer(kBufferSize);
+  DataVector<uint8_t> buffer(kBufferSize);
 
-  EXPECT_FALSE(validator->ReadBlockAtOffset(buffer.data(), 5000, 100));
+  EXPECT_FALSE(
+      validator->ReadBlockAtOffset(pdfium::make_span(buffer).first(100), 5000));
   EXPECT_TRUE(validator->read_error());
   EXPECT_TRUE(validator->has_unavailable_data());
 }
 
-TEST(CPDF_ReadValidatorTest, IntOverflow) {
-  std::vector<uint8_t> test_data(kTestDataSize);
-  auto file = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(test_data);
+TEST(ReadValidatorTest, IntOverflow) {
+  DataVector<uint8_t> test_data(kTestDataSize);
+  auto file =
+      pdfium::MakeRetain<CFX_ReadOnlyVectorStream>(std::move(test_data));
   MockFileAvail file_avail;
-  auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
+  auto validator =
+      pdfium::MakeRetain<CPDF_ReadValidator>(std::move(file), &file_avail);
 
-  std::vector<uint8_t> read_buffer(100);
+  DataVector<uint8_t> read_buffer(100);
 
   // If we have int overflow, this is equal reading after file end. This is not
   // read_error, and in this case we have not unavailable data. It is just error
   // of input params.
   EXPECT_FALSE(validator->ReadBlockAtOffset(
-      read_buffer.data(), std::numeric_limits<FX_FILESIZE>::max() - 1,
-      read_buffer.size()));
+      read_buffer, std::numeric_limits<FX_FILESIZE>::max() - 1));
   EXPECT_FALSE(validator->read_error());
   EXPECT_FALSE(validator->has_unavailable_data());
 }
 
-TEST(CPDF_ReadValidatorTest, Session) {
-  std::vector<uint8_t> test_data(kTestDataSize);
+TEST(ReadValidatorTest, Session) {
+  DataVector<uint8_t> test_data(kTestDataSize);
 
   auto file = pdfium::MakeRetain<InvalidSeekableReadStream>(kTestDataSize);
   MockFileAvail file_avail;
   MockDownloadHints hints;
-  auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
+  auto validator =
+      pdfium::MakeRetain<CPDF_ReadValidator>(std::move(file), &file_avail);
   validator->SetDownloadHints(&hints);
 
-  const CPDF_ReadValidator::Session read_session(validator);
+  CPDF_ReadValidator::ScopedSession read_session(validator);
   ASSERT_FALSE(validator->has_read_problems());
 
   // Data is unavailable
-  validator->ReadBlockAtOffset(test_data.data(), 0, 100);
-
+  validator->ReadBlockAtOffset(pdfium::make_span(test_data).first(100), 0);
   EXPECT_TRUE(validator->has_read_problems());
   EXPECT_TRUE(validator->has_unavailable_data());
   EXPECT_FALSE(validator->read_error());
 
   {
-    const CPDF_ReadValidator::Session read_subsession(validator);
+    CPDF_ReadValidator::ScopedSession read_subsession(validator);
     // The read problems should be hidden.
     EXPECT_FALSE(validator->has_read_problems());
 
     file_avail.SetAvailableRange(0, 100);
     // Read fail.
-    validator->ReadBlockAtOffset(test_data.data(), 0, 100);
+    validator->ReadBlockAtOffset(pdfium::make_span(test_data).first(100), 0);
     EXPECT_TRUE(validator->has_read_problems());
     EXPECT_TRUE(validator->has_unavailable_data());
     EXPECT_TRUE(validator->read_error());
@@ -198,33 +199,33 @@
   EXPECT_TRUE(validator->read_error());
 }
 
-TEST(CPDF_ReadValidatorTest, SessionReset) {
-  std::vector<uint8_t> test_data(kTestDataSize);
+TEST(ReadValidatorTest, SessionReset) {
+  DataVector<uint8_t> test_data(kTestDataSize);
 
   auto file = pdfium::MakeRetain<InvalidSeekableReadStream>(kTestDataSize);
   MockFileAvail file_avail;
   MockDownloadHints hints;
-  auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
+  auto validator =
+      pdfium::MakeRetain<CPDF_ReadValidator>(std::move(file), &file_avail);
   validator->SetDownloadHints(&hints);
 
-  const CPDF_ReadValidator::Session read_session(validator);
+  CPDF_ReadValidator::ScopedSession read_session(validator);
   ASSERT_FALSE(validator->has_read_problems());
 
   // Data is unavailable
-  validator->ReadBlockAtOffset(test_data.data(), 0, 100);
-
+  validator->ReadBlockAtOffset(pdfium::make_span(test_data).first(100), 0);
   EXPECT_TRUE(validator->has_read_problems());
   EXPECT_TRUE(validator->has_unavailable_data());
   EXPECT_FALSE(validator->read_error());
 
   {
-    const CPDF_ReadValidator::Session read_subsession(validator);
+    CPDF_ReadValidator::ScopedSession read_subsession(validator);
     // The read problems should be hidden.
     EXPECT_FALSE(validator->has_read_problems());
 
     file_avail.SetAvailableRange(0, 100);
     // Read fail.
-    validator->ReadBlockAtOffset(test_data.data(), 0, 100);
+    validator->ReadBlockAtOffset(pdfium::make_span(test_data).first(100), 0);
     EXPECT_TRUE(validator->has_read_problems());
     EXPECT_TRUE(validator->has_unavailable_data());
     EXPECT_TRUE(validator->read_error());
@@ -240,11 +241,13 @@
   EXPECT_FALSE(validator->read_error());
 }
 
-TEST(CPDF_ReadValidatorTest, CheckDataRangeAndRequestIfUnavailable) {
-  std::vector<uint8_t> test_data(kTestDataSize);
-  auto file = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(test_data);
+TEST(ReadValidatorTest, CheckDataRangeAndRequestIfUnavailable) {
+  DataVector<uint8_t> test_data(kTestDataSize);
+  auto file =
+      pdfium::MakeRetain<CFX_ReadOnlyVectorStream>(std::move(test_data));
   MockFileAvail file_avail;
-  auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
+  auto validator =
+      pdfium::MakeRetain<CPDF_ReadValidator>(std::move(file), &file_avail);
 
   MockDownloadHints hints;
   validator->SetDownloadHints(&hints);
@@ -266,9 +269,8 @@
   EXPECT_FALSE(validator->read_error());
   EXPECT_FALSE(validator->has_unavailable_data());
 
-  std::vector<uint8_t> read_buffer(100);
-  EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer.data(), 5000,
-                                           read_buffer.size()));
+  DataVector<uint8_t> read_buffer(100);
+  EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer, 5000));
   // No new request on already available data.
   EXPECT_EQ(MakeRange(0, 0), hints.GetLastRequstedRange());
   EXPECT_FALSE(validator->read_error());
diff --git a/core/fpdfapi/parser/cpdf_reference.cpp b/core/fpdfapi/parser/cpdf_reference.cpp
index d50db34..8baa6b6 100644
--- a/core/fpdfapi/parser/cpdf_reference.cpp
+++ b/core/fpdfapi/parser/cpdf_reference.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,54 +6,42 @@
 
 #include "core/fpdfapi/parser/cpdf_reference.h"
 
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
 #include "core/fxcrt/fx_stream.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/contains.h"
 
 CPDF_Reference::CPDF_Reference(CPDF_IndirectObjectHolder* pDoc, uint32_t objnum)
     : m_pObjList(pDoc), m_RefObjNum(objnum) {}
 
-CPDF_Reference::~CPDF_Reference() {}
+CPDF_Reference::~CPDF_Reference() = default;
 
 CPDF_Object::Type CPDF_Reference::GetType() const {
   return kReference;
 }
 
 ByteString CPDF_Reference::GetString() const {
-  const CPDF_Object* obj = SafeGetDirect();
+  const CPDF_Object* obj = FastGetDirect();
   return obj ? obj->GetString() : ByteString();
 }
 
 float CPDF_Reference::GetNumber() const {
-  const CPDF_Object* obj = SafeGetDirect();
+  const CPDF_Object* obj = FastGetDirect();
   return obj ? obj->GetNumber() : 0;
 }
 
 int CPDF_Reference::GetInteger() const {
-  const CPDF_Object* obj = SafeGetDirect();
+  const CPDF_Object* obj = FastGetDirect();
   return obj ? obj->GetInteger() : 0;
 }
 
-CPDF_Dictionary* CPDF_Reference::GetDict() {
-  CPDF_Object* obj = SafeGetDirect();
-  return obj ? obj->GetDict() : nullptr;
+const CPDF_Dictionary* CPDF_Reference::GetDictInternal() const {
+  const CPDF_Object* obj = FastGetDirect();
+  return obj ? obj->GetDictInternal() : nullptr;
 }
 
-const CPDF_Dictionary* CPDF_Reference::GetDict() const {
-  const CPDF_Object* obj = SafeGetDirect();
-  return obj ? obj->GetDict() : nullptr;
-}
-
-bool CPDF_Reference::IsReference() const {
-  return true;
-}
-
-CPDF_Reference* CPDF_Reference::AsReference() {
-  return this;
-}
-
-const CPDF_Reference* CPDF_Reference::AsReference() const {
+CPDF_Reference* CPDF_Reference::AsMutableReference() {
   return this;
 }
 
@@ -65,22 +53,20 @@
     bool bDirect,
     std::set<const CPDF_Object*>* pVisited) const {
   pVisited->insert(this);
-  if (bDirect) {
-    auto* pDirect = GetDirect();
-    return pDirect && !pdfium::ContainsKey(*pVisited, pDirect)
-               ? pDirect->CloneNonCyclic(true, pVisited)
-               : nullptr;
+  if (!bDirect) {
+    return pdfium::MakeRetain<CPDF_Reference>(m_pObjList, m_RefObjNum);
   }
-  return pdfium::MakeRetain<CPDF_Reference>(m_pObjList.Get(), m_RefObjNum);
+  RetainPtr<const CPDF_Object> pDirect = GetDirect();
+  return pDirect && !pdfium::Contains(*pVisited, pDirect.Get())
+             ? pDirect->CloneNonCyclic(true, pVisited)
+             : nullptr;
 }
 
-CPDF_Object* CPDF_Reference::SafeGetDirect() {
-  CPDF_Object* obj = GetDirect();
-  return (obj && !obj->IsReference()) ? obj : nullptr;
-}
-
-const CPDF_Object* CPDF_Reference::SafeGetDirect() const {
-  const CPDF_Object* obj = GetDirect();
+const CPDF_Object* CPDF_Reference::FastGetDirect() const {
+  if (!m_pObjList)
+    return nullptr;
+  const CPDF_Object* obj =
+      m_pObjList->GetOrParseIndirectObjectInternal(m_RefObjNum);
   return (obj && !obj->IsReference()) ? obj : nullptr;
 }
 
@@ -89,13 +75,8 @@
   m_RefObjNum = objnum;
 }
 
-CPDF_Object* CPDF_Reference::GetDirect() {
-  return m_pObjList ? m_pObjList->GetOrParseIndirectObject(m_RefObjNum)
-                    : nullptr;
-}
-
-const CPDF_Object* CPDF_Reference::GetDirect() const {
-  return m_pObjList ? m_pObjList->GetOrParseIndirectObject(m_RefObjNum)
+const CPDF_Object* CPDF_Reference::GetDirectInternal() const {
+  return m_pObjList ? m_pObjList->GetOrParseIndirectObjectInternal(m_RefObjNum)
                     : nullptr;
 }
 
@@ -105,9 +86,9 @@
          archive->WriteString(" 0 R ");
 }
 
-RetainPtr<CPDF_Object> CPDF_Reference::MakeReference(
+RetainPtr<CPDF_Reference> CPDF_Reference::MakeReference(
     CPDF_IndirectObjectHolder* holder) const {
-  ASSERT(holder == m_pObjList);
+  DCHECK_EQ(holder, m_pObjList);
   // Do not allow reference to reference, just create other reference for same
   // object.
   return pdfium::MakeRetain<CPDF_Reference>(holder, GetRefObjNum());
diff --git a/core/fpdfapi/parser/cpdf_reference.h b/core/fpdfapi/parser/cpdf_reference.h
index 1ec7282..241b398 100644
--- a/core/fpdfapi/parser/cpdf_reference.h
+++ b/core/fpdfapi/parser/cpdf_reference.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,61 +7,67 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_REFERENCE_H_
 #define CORE_FPDFAPI_PARSER_CPDF_REFERENCE_H_
 
-#include <memory>
 #include <set>
 
 #include "core/fpdfapi/parser/cpdf_object.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_IndirectObjectHolder;
 
 class CPDF_Reference final : public CPDF_Object {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // CPDF_Object:
   Type GetType() const override;
   RetainPtr<CPDF_Object> Clone() const override;
-  CPDF_Object* GetDirect() override;
-  const CPDF_Object* GetDirect() const override;
   ByteString GetString() const override;
   float GetNumber() const override;
   int GetInteger() const override;
-  CPDF_Dictionary* GetDict() override;
-  const CPDF_Dictionary* GetDict() const override;
-  bool IsReference() const override;
-  CPDF_Reference* AsReference() override;
-  const CPDF_Reference* AsReference() const override;
+  CPDF_Reference* AsMutableReference() override;
   bool WriteTo(IFX_ArchiveStream* archive,
                const CPDF_Encryptor* encryptor) const override;
-  RetainPtr<CPDF_Object> MakeReference(
+  RetainPtr<CPDF_Reference> MakeReference(
       CPDF_IndirectObjectHolder* holder) const override;
 
-  CPDF_IndirectObjectHolder* GetObjList() const { return m_pObjList.Get(); }
   uint32_t GetRefObjNum() const { return m_RefObjNum; }
+  bool HasIndirectObjectHolder() const { return !!m_pObjList; }
   void SetRef(CPDF_IndirectObjectHolder* pDoc, uint32_t objnum);
 
  private:
+  friend class CPDF_Dictionary;
+
   CPDF_Reference(CPDF_IndirectObjectHolder* pDoc, uint32_t objnum);
   ~CPDF_Reference() override;
 
+  const CPDF_Object* GetDirectInternal() const override;
+  const CPDF_Dictionary* GetDictInternal() const override;
   RetainPtr<CPDF_Object> CloneNonCyclic(
       bool bDirect,
       std::set<const CPDF_Object*>* pVisited) const override;
-  CPDF_Object* SafeGetDirect();
-  const CPDF_Object* SafeGetDirect() const;
+
+  const CPDF_Object* FastGetDirect() const;
 
   UnownedPtr<CPDF_IndirectObjectHolder> m_pObjList;
   uint32_t m_RefObjNum;
 };
 
 inline CPDF_Reference* ToReference(CPDF_Object* obj) {
-  return obj ? obj->AsReference() : nullptr;
+  return obj ? obj->AsMutableReference() : nullptr;
 }
 
 inline const CPDF_Reference* ToReference(const CPDF_Object* obj) {
   return obj ? obj->AsReference() : nullptr;
 }
 
+inline RetainPtr<CPDF_Reference> ToReference(RetainPtr<CPDF_Object> obj) {
+  return RetainPtr<CPDF_Reference>(ToReference(obj.Get()));
+}
+
+inline RetainPtr<const CPDF_Reference> ToReference(
+    RetainPtr<const CPDF_Object> obj) {
+  return RetainPtr<const CPDF_Reference>(ToReference(obj.Get()));
+}
+
 #endif  // CORE_FPDFAPI_PARSER_CPDF_REFERENCE_H_
diff --git a/core/fpdfapi/parser/cpdf_security_handler.cpp b/core/fpdfapi/parser/cpdf_security_handler.cpp
index 126fc6b..5f2883d 100644
--- a/core/fpdfapi/parser/cpdf_security_handler.cpp
+++ b/core/fpdfapi/parser/cpdf_security_handler.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,11 +6,11 @@
 
 #include "core/fpdfapi/parser/cpdf_security_handler.h"
 
+#include <stdint.h>
 #include <time.h>
 
 #include <algorithm>
 #include <utility>
-#include <vector>
 
 #include "core/fdrm/fx_crypt.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
@@ -18,8 +18,12 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_memcpy_wrappers.h"
 #include "core/fxcrt/fx_random.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
@@ -32,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,
@@ -47,7 +52,7 @@
   GetPassCode(password, passcode);
   CRYPT_md5_context md5 = CRYPT_MD5Start();
   CRYPT_MD5Update(&md5, passcode);
-  ByteString okey = pEncrypt->GetStringFor("O");
+  ByteString okey = pEncrypt->GetByteStringFor("O");
   CRYPT_MD5Update(&md5, okey.raw_span());
   uint32_t perm = pEncrypt->GetIntegerFor("P");
   CRYPT_MD5Update(&md5, pdfium::as_bytes(pdfium::make_span(&perm, 1)));
@@ -70,20 +75,18 @@
   memcpy(key, digest, copy_len);
 }
 
-bool IsValidKeyLengthForCipher(int cipher, size_t keylen) {
+bool IsValidKeyLengthForCipher(CPDF_CryptoHandler::Cipher cipher,
+                               size_t keylen) {
   switch (cipher) {
-    case FXCIPHER_AES:
+    case CPDF_CryptoHandler::Cipher::kAES:
       return keylen == 16 || keylen == 24 || keylen == 32;
-    case FXCIPHER_AES2:
+    case CPDF_CryptoHandler::Cipher::kAES2:
       return keylen == 32;
-    case FXCIPHER_RC4:
+    case CPDF_CryptoHandler::Cipher::kRC4:
       return keylen >= 5 && keylen <= 16;
-    case FXCIPHER_NONE:
+    case CPDF_CryptoHandler::Cipher::kNone:
       return true;
-    default:
-      NOTREACHED();
   }
-  return false;
 }
 
 #define FX_GET_32WORD(n, b, i)                                        \
@@ -118,13 +121,13 @@
   uint8_t digest[32];
   CRYPT_SHA256Finish(&sha, digest);
 
-  std::vector<uint8_t> buf;
+  DataVector<uint8_t> buf;
   uint8_t* input = digest;
   uint8_t* key = input;
   uint8_t* iv = input + 16;
   uint8_t* E = nullptr;
   int iBufLen = 0;
-  std::vector<uint8_t> interDigest;
+  DataVector<uint8_t> interDigest;
   int i = 0;
   int iBlockSize = 32;
   CRYPT_aes_context aes = {};
@@ -136,7 +139,7 @@
     iBufLen = iRoundSize * 64;
     buf.resize(iBufLen);
     E = buf.data();
-    std::vector<uint8_t> content;
+    DataVector<uint8_t> content;
     for (int j = 0; j < 64; ++j) {
       content.insert(std::end(content), password.raw_str(),
                      password.raw_str() + password.GetLength());
@@ -145,7 +148,7 @@
         content.insert(std::end(content), vector, vector + 48);
       }
     }
-    CRYPT_AESSetKey(&aes, key, 16, true);
+    CRYPT_AESSetKey(&aes, key, 16);
     CRYPT_AESSetIV(&aes, iv);
     CRYPT_AESEncrypt(&aes, E, content.data(), iBufLen);
     int iHash = 0;
@@ -188,15 +191,15 @@
 CPDF_SecurityHandler::~CPDF_SecurityHandler() = default;
 
 bool CPDF_SecurityHandler::OnInit(const CPDF_Dictionary* pEncryptDict,
-                                  const CPDF_Array* pIdArray,
+                                  RetainPtr<const CPDF_Array> pIdArray,
                                   const ByteString& password) {
   if (pIdArray)
-    m_FileId = pIdArray->GetStringAt(0);
+    m_FileId = pIdArray->GetByteStringAt(0);
   else
     m_FileId.clear();
   if (!LoadDict(pEncryptDict))
     return false;
-  if (m_Cipher == FXCIPHER_NONE)
+  if (m_Cipher == CPDF_CryptoHandler::Cipher::kNone)
     return true;
   if (!CheckSecurity(password))
     return false;
@@ -215,7 +218,8 @@
 
 uint32_t CPDF_SecurityHandler::GetPermissions() const {
   uint32_t dwPermission = m_bOwnerUnlocked ? 0xFFFFFFFF : m_Permissions;
-  if (m_pEncryptDict && m_pEncryptDict->GetStringFor("Filter") == "Standard") {
+  if (m_pEncryptDict &&
+      m_pEncryptDict->GetByteStringFor("Filter") == "Standard") {
     // See PDF Reference 1.7, page 123, table 3.20.
     dwPermission &= 0xFFFFFFFC;
     dwPermission |= 0xFFFFF0C0;
@@ -225,21 +229,23 @@
 
 static bool LoadCryptInfo(const CPDF_Dictionary* pEncryptDict,
                           const ByteString& name,
-                          int* cipher,
+                          CPDF_CryptoHandler::Cipher* cipher,
                           size_t* keylen_out) {
   int Version = pEncryptDict->GetIntegerFor("V");
-  *cipher = FXCIPHER_RC4;
+  *cipher = CPDF_CryptoHandler::Cipher::kRC4;
   *keylen_out = 0;
   int keylen = 0;
   if (Version >= 4) {
-    const CPDF_Dictionary* pCryptFilters = pEncryptDict->GetDictFor("CF");
+    RetainPtr<const CPDF_Dictionary> pCryptFilters =
+        pEncryptDict->GetDictFor("CF");
     if (!pCryptFilters)
       return false;
 
     if (name == "Identity") {
-      *cipher = FXCIPHER_NONE;
+      *cipher = CPDF_CryptoHandler::Cipher::kNone;
     } else {
-      const CPDF_Dictionary* pDefFilter = pCryptFilters->GetDictFor(name);
+      RetainPtr<const CPDF_Dictionary> pDefFilter =
+          pCryptFilters->GetDictFor(name);
       if (!pDefFilter)
         return false;
 
@@ -259,9 +265,9 @@
         nKeyBits *= 8;
       }
       keylen = nKeyBits / 8;
-      ByteString cipher_name = pDefFilter->GetStringFor("CFM");
+      ByteString cipher_name = pDefFilter->GetByteStringFor("CFM");
       if (cipher_name == "AESV2" || cipher_name == "AESV3")
-        *cipher = FXCIPHER_AES;
+        *cipher = CPDF_CryptoHandler::Cipher::kAES;
     }
   } else {
     keylen = Version > 1 ? pEncryptDict->GetIntegerFor("Length", 40) / 8 : 5;
@@ -284,8 +290,8 @@
   if (m_Version < 4)
     return LoadCryptInfo(pEncryptDict, ByteString(), &m_Cipher, &m_KeyLen);
 
-  ByteString stmf_name = pEncryptDict->GetStringFor("StmF");
-  ByteString strf_name = pEncryptDict->GetStringFor("StrF");
+  ByteString stmf_name = pEncryptDict->GetByteStringFor("StmF");
+  ByteString strf_name = pEncryptDict->GetByteStringFor("StrF");
   if (stmf_name != strf_name)
     return false;
 
@@ -293,7 +299,7 @@
 }
 
 bool CPDF_SecurityHandler::LoadDict(const CPDF_Dictionary* pEncryptDict,
-                                    int* cipher,
+                                    CPDF_CryptoHandler::Cipher* cipher,
                                     size_t* key_len) {
   m_pEncryptDict.Reset(pEncryptDict);
   m_Version = pEncryptDict->GetIntegerFor("V");
@@ -303,8 +309,8 @@
   ByteString strf_name;
   ByteString stmf_name;
   if (m_Version >= 4) {
-    stmf_name = pEncryptDict->GetStringFor("StmF");
-    strf_name = pEncryptDict->GetStringFor("StrF");
+    stmf_name = pEncryptDict->GetByteStringFor("StmF");
+    strf_name = pEncryptDict->GetByteStringFor("StrF");
     if (stmf_name != strf_name)
       return false;
   }
@@ -318,14 +324,14 @@
 
 bool CPDF_SecurityHandler::AES256_CheckPassword(const ByteString& password,
                                                 bool bOwner) {
-  ASSERT(m_pEncryptDict);
-  ASSERT(m_Revision >= 5);
+  DCHECK(m_pEncryptDict);
+  DCHECK(m_Revision >= 5);
 
-  ByteString okey = m_pEncryptDict->GetStringFor("O");
+  ByteString okey = m_pEncryptDict->GetByteStringFor("O");
   if (okey.GetLength() < 48)
     return false;
 
-  ByteString ukey = m_pEncryptDict->GetStringFor("U");
+  ByteString ukey = m_pEncryptDict->GetByteStringFor("U");
   if (ukey.GetLength() < 48)
     return false;
 
@@ -357,18 +363,18 @@
       CRYPT_SHA256Update(&sha, ukey.raw_str(), 48);
     CRYPT_SHA256Finish(&sha, digest);
   }
-  ByteString ekey = m_pEncryptDict->GetStringFor(bOwner ? "OE" : "UE");
+  ByteString ekey = m_pEncryptDict->GetByteStringFor(bOwner ? "OE" : "UE");
   if (ekey.GetLength() < 32)
     return false;
 
   CRYPT_aes_context aes = {};
-  CRYPT_AESSetKey(&aes, digest, sizeof(digest), false);
+  CRYPT_AESSetKey(&aes, digest, sizeof(digest));
   uint8_t iv[16] = {};
   CRYPT_AESSetIV(&aes, iv);
   CRYPT_AESDecrypt(&aes, m_EncryptKey, ekey.raw_str(), 32);
-  CRYPT_AESSetKey(&aes, m_EncryptKey, sizeof(m_EncryptKey), false);
+  CRYPT_AESSetKey(&aes, m_EncryptKey, sizeof(m_EncryptKey));
   CRYPT_AESSetIV(&aes, iv);
-  ByteString perms = m_pEncryptDict->GetStringFor("Perms");
+  ByteString perms = m_pEncryptDict->GetByteStringFor("Perms");
   if (perms.IsEmpty())
     return false;
 
@@ -381,7 +387,7 @@
   if (buf[9] != 'a' || buf[10] != 'd' || buf[11] != 'b')
     return false;
 
-  if (FXDWORD_GET_LSBFIRST(buf) != m_Permissions)
+  if (FXSYS_UINT32_GET_LSBFIRST(buf) != m_Permissions)
     return false;
 
   // Relax this check as there appear to be some non-conforming documents
@@ -437,7 +443,7 @@
   CalcEncryptKey(m_pEncryptDict.Get(), password, m_EncryptKey, m_KeyLen,
                  bIgnoreEncryptMeta, m_FileId);
   ByteString ukey =
-      m_pEncryptDict ? m_pEncryptDict->GetStringFor("U") : ByteString();
+      m_pEncryptDict ? m_pEncryptDict->GetByteStringFor("U") : ByteString();
   if (ukey.GetLength() < 16) {
     return false;
   }
@@ -470,7 +476,7 @@
 ByteString CPDF_SecurityHandler::GetUserPassword(
     const ByteString& owner_password) const {
   constexpr size_t kRequiredOkeyLength = 32;
-  ByteString okey = m_pEncryptDict->GetStringFor("O");
+  ByteString okey = m_pEncryptDict->GetByteStringFor("O");
   size_t okeylen = std::min<size_t>(okey.GetLength(), kRequiredOkeyLength);
   if (okeylen < kRequiredOkeyLength)
     return ByteString();
@@ -539,9 +545,9 @@
                                             const ByteString& user_password,
                                             const ByteString& owner_password,
                                             bool bDefault) {
-  ASSERT(pEncryptDict);
+  DCHECK(pEncryptDict);
 
-  int cipher = FXCIPHER_NONE;
+  CPDF_CryptoHandler::Cipher cipher = CPDF_CryptoHandler::Cipher::kNone;
   size_t key_len = 0;
   if (!LoadDict(pEncryptDict, &cipher, &key_len)) {
     return;
@@ -552,7 +558,7 @@
 
   if (m_Revision >= 5) {
     uint32_t random[4];
-    FX_Random_GenerateMT(random, FX_ArraySize(random));
+    FX_Random_GenerateMT(random, std::size(random));
     CRYPT_sha2_context sha;
     CRYPT_SHA256Start(&sha);
     CRYPT_SHA256Update(&sha, reinterpret_cast<uint8_t*>(random),
@@ -590,7 +596,7 @@
 
   ByteString file_id;
   if (pIdArray)
-    file_id = pIdArray->GetStringAt(0);
+    file_id = pIdArray->GetByteStringAt(0);
 
   CalcEncryptKey(m_pEncryptDict.Get(), user_password, m_EncryptKey, key_len,
                  false, file_id);
@@ -646,7 +652,7 @@
   uint8_t digest[20];
   CRYPT_SHA1Finish(&sha, digest);
 
-  ByteString ukey = pEncryptDict->GetStringFor("U");
+  ByteString ukey = pEncryptDict->GetByteStringFor("U");
   CRYPT_sha2_context sha2;
   uint8_t digest1[48];
   if (m_Revision >= 6) {
@@ -677,7 +683,7 @@
     CRYPT_SHA256Finish(&sha2, digest1);
   }
   CRYPT_aes_context aes = {};
-  CRYPT_AESSetKey(&aes, digest1, 32, true);
+  CRYPT_AESSetKey(&aes, digest1, 32);
   uint8_t iv[16] = {};
   CRYPT_AESSetIV(&aes, iv);
   CRYPT_AESEncrypt(&aes, digest1, m_EncryptKey, sizeof(m_EncryptKey));
@@ -706,7 +712,7 @@
   FX_Random_GenerateMT(buf_random, 1);
 
   CRYPT_aes_context aes = {};
-  CRYPT_AESSetKey(&aes, m_EncryptKey, sizeof(m_EncryptKey), true);
+  CRYPT_AESSetKey(&aes, m_EncryptKey, sizeof(m_EncryptKey));
 
   uint8_t iv[16] = {};
   CRYPT_AESSetIV(&aes, iv);
@@ -718,5 +724,5 @@
 
 void CPDF_SecurityHandler::InitCryptoHandler() {
   m_pCryptoHandler =
-      pdfium::MakeUnique<CPDF_CryptoHandler>(m_Cipher, m_EncryptKey, m_KeyLen);
+      std::make_unique<CPDF_CryptoHandler>(m_Cipher, m_EncryptKey, m_KeyLen);
 }
diff --git a/core/fpdfapi/parser/cpdf_security_handler.h b/core/fpdfapi/parser/cpdf_security_handler.h
index 05eb689..300dd9b 100644
--- a/core/fpdfapi/parser/cpdf_security_handler.h
+++ b/core/fpdfapi/parser/cpdf_security_handler.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,29 +7,24 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_SECURITY_HANDLER_H_
 #define CORE_FPDFAPI_PARSER_CPDF_SECURITY_HANDLER_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <memory>
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fpdfapi/parser/cpdf_crypto_handler.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/retain_ptr.h"
 
-#define FXCIPHER_NONE 0
-#define FXCIPHER_RC4 1
-#define FXCIPHER_AES 2
-#define FXCIPHER_AES2 3
-
 class CPDF_Array;
-class CPDF_CryptoHandler;
 class CPDF_Dictionary;
-class CPDF_Parser;
 
-class CPDF_SecurityHandler : public Retainable {
+class CPDF_SecurityHandler final : public Retainable {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   bool OnInit(const CPDF_Dictionary* pEncryptDict,
-              const CPDF_Array* pIdArray,
+              RetainPtr<const CPDF_Array> pIdArray,
               const ByteString& password);
   void OnCreate(CPDF_Dictionary* pEncryptDict,
                 const CPDF_Array* pIdArray,
@@ -63,7 +58,7 @@
 
   bool LoadDict(const CPDF_Dictionary* pEncryptDict);
   bool LoadDict(const CPDF_Dictionary* pEncryptDict,
-                int* cipher,
+                CPDF_CryptoHandler::Cipher* cipher,
                 size_t* key_len);
 
   ByteString GetUserPassword(const ByteString& owner_password) const;
@@ -89,13 +84,13 @@
   int m_Version = 0;
   int m_Revision = 0;
   uint32_t m_Permissions = 0;
-  int m_Cipher = FXCIPHER_NONE;
   size_t m_KeyLen = 0;
+  CPDF_CryptoHandler::Cipher m_Cipher = CPDF_CryptoHandler::Cipher::kNone;
   PasswordEncodingConversion m_PasswordEncodingConversion = kUnknown;
   ByteString m_FileId;
   RetainPtr<const CPDF_Dictionary> m_pEncryptDict;
   std::unique_ptr<CPDF_CryptoHandler> m_pCryptoHandler;
-  uint8_t m_EncryptKey[32];
+  uint8_t m_EncryptKey[32] = {};
 };
 
 #endif  // CORE_FPDFAPI_PARSER_CPDF_SECURITY_HANDLER_H_
diff --git a/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp b/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
index ee2a621..cf1a2b5 100644
--- a/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
+++ b/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
@@ -1,8 +1,7 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
 #include <string>
 
 #include "build/build_config.h"
@@ -10,11 +9,13 @@
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_parser.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
 #include "public/cpp/fpdf_scopers.h"
 #include "public/fpdf_edit.h"
 #include "public/fpdf_save.h"
 #include "public/fpdfview.h"
 #include "testing/embedder_test.h"
+#include "testing/embedder_test_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -92,31 +93,16 @@
   void VerifyHelloWorldPage(FPDF_PAGE page) {
     ASSERT_TRUE(page);
 
-#if defined(OS_WIN)
-    const char kExpectedHash[] = "795b7ce1626931aa06af0fa23b7d80bb";
-#elif defined(OS_MACOSX)
-    const char kExpectedHash[] = "b90475ca64d1348c3bf5e2b77ad9187a";
-#else
-    const char kExpectedHash[] = "2baa4c0e1758deba1b9c908e1fbd04ed";
-#endif
-
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kExpectedHash);
+    CompareBitmap(page_bitmap.get(), 200, 200, pdfium::HelloWorldChecksum());
   }
 
   void VerifyModifiedHelloWorldPage(FPDF_PAGE page) {
     ASSERT_TRUE(page);
 
-#if defined(OS_WIN)
-    const char kExpectedHash[] = "93db13099042bafefb3c22a165bad684";
-#elif defined(OS_MACOSX)
-    const char kExpectedHash[] = "f8fbd14a048b9e2ea8e5f059f22a910e";
-#else
-    const char kExpectedHash[] = "93dcc09055f87a2792c8e3065af99a1b";
-#endif
-
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kExpectedHash);
+    CompareBitmap(page_bitmap.get(), 200, 200,
+                  pdfium::HelloWorldRemovedChecksum());
   }
 };
 
@@ -148,20 +134,17 @@
   EXPECT_EQ(0xFFFFFFFC, FPDF_GetDocPermissions(document()));
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_PasswordAfterGenerateSave DISABLED_PasswordAfterGenerateSave
+TEST_F(CPDFSecurityHandlerEmbedderTest, PasswordAfterGenerateSave) {
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "ad97491cab71c02f1f4ef5ba0a7b5593";
+    }
+#if BUILDFLAG(IS_APPLE)
+    return "2a308e8cc20a6221112c387d122075a8";
 #else
-#define MAYBE_PasswordAfterGenerateSave PasswordAfterGenerateSave
-#endif
-TEST_F(CPDFSecurityHandlerEmbedderTest, MAYBE_PasswordAfterGenerateSave) {
-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
-  const char md5[] = "7048dca58e2ed8f93339008b91e4eb4e";
-#elif defined(OS_MACOSX)
-  const char md5[] = "6951b6c9891dfe0332a5b1983e484400";
-#else
-  const char md5[] = "041c2fb541c8907cc22ce101b686c79e";
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
+    return "9fe7eef8e51d15a604001854be6ed1ee";
+#endif  // BUILDFLAG(IS_APPLE)
+  }();
   {
     ASSERT_TRUE(OpenDocumentWithOptions("encrypted.pdf", "5678",
                                         LinearizeOption::kMustLinearize,
@@ -174,7 +157,7 @@
     EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
     FPDFPage_InsertObject(page, red_rect);
     ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-    CompareBitmap(bitmap.get(), 612, 792, md5);
+    CompareBitmap(bitmap.get(), 612, 792, checksum);
     EXPECT_TRUE(FPDFPage_GenerateContent(page));
     SetWholeFileAvailable();
     EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
@@ -196,8 +179,9 @@
   for (const auto& test : tests) {
     ASSERT_TRUE(OpenSavedDocumentWithPassword(test.password));
     FPDF_PAGE page = LoadSavedPage(0);
-    VerifySavedRendering(page, 612, 792, md5);
-    EXPECT_EQ(test.permissions, FPDF_GetDocPermissions(saved_document_));
+    ASSERT_TRUE(page);
+    VerifySavedRendering(page, 612, 792, checksum);
+    EXPECT_EQ(test.permissions, FPDF_GetDocPermissions(saved_document()));
 
     CloseSavedPage(page);
     CloseSavedDocument();
@@ -679,3 +663,7 @@
   VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelLatin1);
   VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelUTF8);
 }
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, Bug1124998) {
+  OpenAndVerifyHelloWorldDocumentWithPassword("bug_1124998.pdf", "test");
+}
diff --git a/core/fpdfapi/parser/cpdf_seekablemultistream.cpp b/core/fpdfapi/parser/cpdf_seekablemultistream.cpp
index d2a0417..bad6d97 100644
--- a/core/fpdfapi/parser/cpdf_seekablemultistream.cpp
+++ b/core/fpdfapi/parser/cpdf_seekablemultistream.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,32 +7,35 @@
 #include "core/fpdfapi/parser/cpdf_seekablemultistream.h"
 
 #include <algorithm>
+#include <utility>
 
+#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/notreached.h"
 
 CPDF_SeekableMultiStream::CPDF_SeekableMultiStream(
-    const std::vector<const CPDF_Stream*>& streams) {
-  for (const CPDF_Stream* pStream : streams) {
-    m_Data.push_back(pdfium::MakeRetain<CPDF_StreamAcc>(pStream));
+    std::vector<RetainPtr<const CPDF_Stream>> streams) {
+  for (auto& pStream : streams) {
+    m_Data.push_back(pdfium::MakeRetain<CPDF_StreamAcc>(std::move(pStream)));
     m_Data.back()->LoadAllDataFiltered();
   }
 }
 
-CPDF_SeekableMultiStream::~CPDF_SeekableMultiStream() {}
+CPDF_SeekableMultiStream::~CPDF_SeekableMultiStream() = default;
 
 FX_FILESIZE CPDF_SeekableMultiStream::GetSize() {
-  uint32_t dwSize = 0;
+  FX_SAFE_FILESIZE dwSize = 0;
   for (const auto& acc : m_Data)
     dwSize += acc->GetSize();
-  return dwSize;
+  return dwSize.ValueOrDie();
 }
 
-bool CPDF_SeekableMultiStream::ReadBlockAtOffset(void* buffer,
-                                                 FX_FILESIZE offset,
-                                                 size_t size) {
-  int32_t iCount = pdfium::CollectionSize<int32_t>(m_Data);
+bool CPDF_SeekableMultiStream::ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                                                 FX_FILESIZE offset) {
+  int32_t iCount = fxcrt::CollectionSize<int32_t>(m_Data);
   int32_t index = 0;
   while (index < iCount) {
     const auto& acc = m_Data[index];
@@ -44,22 +47,20 @@
     index++;
   }
   while (index < iCount) {
-    const auto& acc = m_Data[index];
-    uint32_t dwSize = acc->GetSize();
-    size_t dwRead = std::min(size, static_cast<size_t>(dwSize - offset));
-    memcpy(buffer, acc->GetSpan().subspan(offset, dwRead).data(), dwRead);
-    size -= dwRead;
-    if (size == 0)
+    auto acc_span = m_Data[index]->GetSpan();
+    size_t dwRead = std::min<size_t>(buffer.size(), acc_span.size() - offset);
+    fxcrt::spancpy(buffer, acc_span.subspan(offset, dwRead));
+    buffer = buffer.subspan(dwRead);
+    if (buffer.empty())
       return true;
 
-    buffer = static_cast<uint8_t*>(buffer) + dwRead;
     offset = 0;
     index++;
   }
   return false;
 }
 
-size_t CPDF_SeekableMultiStream::ReadBlock(void* buffer, size_t size) {
+size_t CPDF_SeekableMultiStream::ReadBlock(pdfium::span<uint8_t> buffer) {
   NOTREACHED();
   return 0;
 }
@@ -77,9 +78,9 @@
   return false;
 }
 
-bool CPDF_SeekableMultiStream::WriteBlockAtOffset(const void* pData,
-                                                  FX_FILESIZE offset,
-                                                  size_t size) {
+bool CPDF_SeekableMultiStream::WriteBlockAtOffset(
+    pdfium::span<const uint8_t> buffer,
+    FX_FILESIZE offset) {
   NOTREACHED();
   return false;
 }
diff --git a/core/fpdfapi/parser/cpdf_seekablemultistream.h b/core/fpdfapi/parser/cpdf_seekablemultistream.h
index 30a8479..fc659a6 100644
--- a/core/fpdfapi/parser/cpdf_seekablemultistream.h
+++ b/core/fpdfapi/parser/cpdf_seekablemultistream.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,21 +18,19 @@
 class CPDF_SeekableMultiStream final : public IFX_SeekableStream {
  public:
   explicit CPDF_SeekableMultiStream(
-      const std::vector<const CPDF_Stream*>& streams);
+      std::vector<RetainPtr<const CPDF_Stream>> streams);
   ~CPDF_SeekableMultiStream() override;
 
-  // IFX_SeekableReadStream
+  // IFX_SeekableReadStream:
   FX_FILESIZE GetPosition() override;
   FX_FILESIZE GetSize() override;
-  bool ReadBlockAtOffset(void* buffer,
-                         FX_FILESIZE offset,
-                         size_t size) override;
-  size_t ReadBlock(void* buffer, size_t size) override;
   bool IsEOF() override;
+  size_t ReadBlock(pdfium::span<uint8_t> buffer) override;
+  bool ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                         FX_FILESIZE offset) override;
+  bool WriteBlockAtOffset(pdfium::span<const uint8_t> buffer,
+                          FX_FILESIZE offset) override;
   bool Flush() override;
-  bool WriteBlockAtOffset(const void* pData,
-                          FX_FILESIZE offset,
-                          size_t size) override;
 
  private:
   std::vector<RetainPtr<CPDF_StreamAcc>> m_Data;
diff --git a/core/fpdfapi/parser/cpdf_seekablemultistream_unittest.cpp b/core/fpdfapi/parser/cpdf_seekablemultistream_unittest.cpp
index 4f806fa..5e63db0 100644
--- a/core/fpdfapi/parser/cpdf_seekablemultistream_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_seekablemultistream_unittest.cpp
@@ -1,85 +1,91 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/parser/cpdf_seekablemultistream.h"
 
-#include <memory>
+#include <utility>
 #include <vector>
 
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/data_vector.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
 TEST(CPDFSeekableMultiStreamTest, NoStreams) {
-  std::vector<const CPDF_Stream*> streams;
-  auto fileread = pdfium::MakeRetain<CPDF_SeekableMultiStream>(streams);
+  std::vector<RetainPtr<const CPDF_Stream>> streams;
+  auto fileread =
+      pdfium::MakeRetain<CPDF_SeekableMultiStream>(std::move(streams));
 
   uint8_t output_buffer[16];
   memset(output_buffer, 0xbd, sizeof(output_buffer));
-  EXPECT_FALSE(fileread->ReadBlockAtOffset(output_buffer, 0, 0));
+  EXPECT_FALSE(fileread->ReadBlockAtOffset({output_buffer, 0}, 0));
   EXPECT_EQ(0xbd, output_buffer[0]);
 }
 
 TEST(CXFAFileReadTest, EmptyStreams) {
-  std::vector<const CPDF_Stream*> streams;
-  auto stream1 = pdfium::MakeRetain<CPDF_Stream>();
-  streams.push_back(stream1.Get());
-  auto fileread = pdfium::MakeRetain<CPDF_SeekableMultiStream>(streams);
+  std::vector<RetainPtr<const CPDF_Stream>> streams;
+  streams.push_back(pdfium::MakeRetain<CPDF_Stream>());
+  auto fileread =
+      pdfium::MakeRetain<CPDF_SeekableMultiStream>(std::move(streams));
 
   uint8_t output_buffer[16];
   memset(output_buffer, 0xbd, sizeof(output_buffer));
-  EXPECT_FALSE(fileread->ReadBlockAtOffset(output_buffer, 0, 0));
+  EXPECT_FALSE(fileread->ReadBlockAtOffset({output_buffer, 0}, 0));
   EXPECT_EQ(0xbd, output_buffer[0]);
 }
 
 TEST(CXFAFileReadTest, NormalStreams) {
-  std::vector<const CPDF_Stream*> streams;
-  auto stream1 = pdfium::MakeRetain<CPDF_Stream>();
-  auto stream2 = pdfium::MakeRetain<CPDF_Stream>();
-  auto stream3 = pdfium::MakeRetain<CPDF_Stream>();
-
   // 16 chars total.
-  stream1->InitStream(ByteStringView("one t").raw_span(),
-                      pdfium::MakeRetain<CPDF_Dictionary>());
-  stream2->InitStream(ByteStringView("wo ").raw_span(),
-                      pdfium::MakeRetain<CPDF_Dictionary>());
-  stream3->InitStream(ByteStringView("three!!!").raw_span(),
-                      pdfium::MakeRetain<CPDF_Dictionary>());
+  static const char kOne[] = "one t";
+  static const char kTwo[] = "wo ";
+  static const char kThree[] = "three!!!";
 
-  streams.push_back(stream1.Get());
-  streams.push_back(stream2.Get());
-  streams.push_back(stream3.Get());
-  auto fileread = pdfium::MakeRetain<CPDF_SeekableMultiStream>(streams);
+  ByteStringView one_view(kOne);
+  ByteStringView two_view(kTwo);
+  ByteStringView three_view(kThree);
+  auto stream1 = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(one_view.begin(), one_view.end()),
+      pdfium::MakeRetain<CPDF_Dictionary>());
+  auto stream2 = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(two_view.begin(), two_view.end()),
+      pdfium::MakeRetain<CPDF_Dictionary>());
+  auto stream3 = pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(three_view.begin(), three_view.end()),
+      pdfium::MakeRetain<CPDF_Dictionary>());
+
+  std::vector<RetainPtr<const CPDF_Stream>> streams;
+  streams.push_back(std::move(stream1));
+  streams.push_back(std::move(stream2));
+  streams.push_back(std::move(stream3));
+  auto fileread =
+      pdfium::MakeRetain<CPDF_SeekableMultiStream>(std::move(streams));
 
   uint8_t output_buffer[16];
   memset(output_buffer, 0xbd, sizeof(output_buffer));
-  EXPECT_TRUE(fileread->ReadBlockAtOffset(output_buffer, 0, 0));
+  EXPECT_TRUE(fileread->ReadBlockAtOffset({output_buffer, 0}, 0));
   EXPECT_EQ(0xbd, output_buffer[0]);
 
   memset(output_buffer, 0xbd, sizeof(output_buffer));
-  EXPECT_TRUE(fileread->ReadBlockAtOffset(output_buffer, 1, 0));
+  EXPECT_TRUE(fileread->ReadBlockAtOffset({output_buffer, 0}, 1));
   EXPECT_EQ(0xbd, output_buffer[0]);
 
   memset(output_buffer, 0xbd, sizeof(output_buffer));
-  EXPECT_TRUE(fileread->ReadBlockAtOffset(output_buffer, 0, 1));
+  EXPECT_TRUE(fileread->ReadBlockAtOffset({output_buffer, 1}, 0));
   EXPECT_EQ(0, memcmp(output_buffer, "o", 1));
   EXPECT_EQ(0xbd, output_buffer[1]);
 
   memset(output_buffer, 0xbd, sizeof(output_buffer));
-  EXPECT_TRUE(
-      fileread->ReadBlockAtOffset(output_buffer, 0, sizeof(output_buffer)));
+  EXPECT_TRUE(fileread->ReadBlockAtOffset(output_buffer, 0));
   EXPECT_EQ(0, memcmp(output_buffer, "one two three!!!", 16));
 
   memset(output_buffer, 0xbd, sizeof(output_buffer));
-  EXPECT_TRUE(fileread->ReadBlockAtOffset(output_buffer, 2, 10));
+  EXPECT_TRUE(fileread->ReadBlockAtOffset({output_buffer, 10}, 2));
   EXPECT_EQ(0, memcmp(output_buffer, "e two thre", 10));
   EXPECT_EQ(0xbd, output_buffer[11]);
 
   memset(output_buffer, 0xbd, sizeof(output_buffer));
-  EXPECT_FALSE(
-      fileread->ReadBlockAtOffset(output_buffer, 1, sizeof(output_buffer)));
+  EXPECT_FALSE(fileread->ReadBlockAtOffset(output_buffer, 1));
   EXPECT_EQ(0, memcmp(output_buffer, "ne two three!!!", 15));
   EXPECT_EQ(0xbd, output_buffer[15]);
 }
diff --git a/core/fpdfapi/parser/cpdf_simple_parser.cpp b/core/fpdfapi/parser/cpdf_simple_parser.cpp
index ff6e2cf..ce09904 100644
--- a/core/fpdfapi/parser/cpdf_simple_parser.cpp
+++ b/core/fpdfapi/parser/cpdf_simple_parser.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,7 +17,7 @@
   uint8_t ch;
 
   // Skip whitespace and comment lines.
-  while (1) {
+  while (true) {
     if (data_.size() <= cur_pos_)
       return ByteStringView();
 
@@ -31,7 +31,7 @@
     if (ch != '%')
       break;
 
-    while (1) {
+    while (true) {
       if (data_.size() <= cur_pos_)
         return ByteStringView();
 
@@ -46,7 +46,7 @@
   if (PDFCharIsDelimiter(ch)) {
     // Find names
     if (ch == '/') {
-      while (1) {
+      while (true) {
         if (data_.size() <= cur_pos_)
           break;
 
diff --git a/core/fpdfapi/parser/cpdf_simple_parser.h b/core/fpdfapi/parser/cpdf_simple_parser.h
index f9461b6..c953be6 100644
--- a/core/fpdfapi/parser/cpdf_simple_parser.h
+++ b/core/fpdfapi/parser/cpdf_simple_parser.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,9 +7,10 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_SIMPLE_PARSER_H_
 #define CORE_FPDFAPI_PARSER_CPDF_SIMPLE_PARSER_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/span.h"
+#include <stdint.h>
+
+#include "core/fxcrt/bytestring.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 5834d77..77483b3 100644
--- a/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp
@@ -1,15 +1,16 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/parser/cpdf_simple_parser.h"
 
-#include <string>
+#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[] = {
@@ -50,15 +51,15 @@
       STR_IN_OUT_CASE(" $^&&*\t\0sdff ", "$^&&*"),
       STR_IN_OUT_CASE("\n\r+3.5656 -11.0", "+3.5656"),
   };
-  for (size_t i = 0; i < FX_ArraySize(test_data); ++i) {
+  for (size_t i = 0; i < std::size(test_data); ++i) {
     const pdfium::StrFuncTestData& data = test_data[i];
     CPDF_SimpleParser parser(pdfium::make_span(data.input, data.input_size));
     ByteStringView word = parser.GetWord();
     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.cpp b/core/fpdfapi/parser/cpdf_stream.cpp
index 4b5ef5c..fa08ffa 100644
--- a/core/fpdfapi/parser/cpdf_stream.cpp
+++ b/core/fpdfapi/parser/cpdf_stream.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,10 @@
 
 #include "core/fpdfapi/parser/cpdf_stream.h"
 
+#include <stdint.h>
+
+#include <sstream>
 #include <utility>
-#include <vector>
 
 #include "constants/stream_dict_common.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
@@ -16,74 +18,64 @@
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fxcrt/cfx_memorystream.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/span_util.h"
+#include "third_party/base/containers/contains.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
 
 namespace {
 
 bool IsMetaDataStreamDictionary(const CPDF_Dictionary* dict) {
-  return dict && dict->GetStringFor("Type") == "Metadata" &&
-         dict->GetStringFor("Subtype") == "XML";
+  // See ISO 32000-1:2008 spec, table 315.
+  return ValidateDictType(dict, "Metadata") &&
+         dict->GetNameFor("Subtype") == "XML";
 }
 
 }  // namespace
 
-CPDF_Stream::CPDF_Stream() {}
+CPDF_Stream::CPDF_Stream() = default;
 
-CPDF_Stream::CPDF_Stream(std::unique_ptr<uint8_t, FxFreeDeleter> pData,
-                         uint32_t size,
+CPDF_Stream::CPDF_Stream(RetainPtr<CPDF_Dictionary> pDict)
+    : CPDF_Stream(DataVector<uint8_t>(), std::move(pDict)) {}
+
+CPDF_Stream::CPDF_Stream(DataVector<uint8_t> pData,
                          RetainPtr<CPDF_Dictionary> pDict)
-    : m_pDict(std::move(pDict)) {
-  TakeData(std::move(pData), size);
+    : data_(std::move(pData)), dict_(std::move(pDict)) {
+  SetLengthInDict(pdfium::base::checked_cast<int>(
+      absl::get<DataVector<uint8_t>>(data_).size()));
 }
 
 CPDF_Stream::~CPDF_Stream() {
   m_ObjNum = kInvalidObjNum;
-  if (m_pDict && m_pDict->GetObjNum() == kInvalidObjNum)
-    m_pDict.Leak();  // lowercase release, release ownership.
+  if (dict_ && dict_->GetObjNum() == kInvalidObjNum)
+    dict_.Leak();  // lowercase release, release ownership.
 }
 
 CPDF_Object::Type CPDF_Stream::GetType() const {
   return kStream;
 }
 
-CPDF_Dictionary* CPDF_Stream::GetDict() {
-  return m_pDict.Get();
+const CPDF_Dictionary* CPDF_Stream::GetDictInternal() const {
+  return dict_.Get();
 }
 
-const CPDF_Dictionary* CPDF_Stream::GetDict() const {
-  return m_pDict.Get();
-}
-
-bool CPDF_Stream::IsStream() const {
-  return true;
-}
-
-CPDF_Stream* CPDF_Stream::AsStream() {
+CPDF_Stream* CPDF_Stream::AsMutableStream() {
   return this;
 }
 
-const CPDF_Stream* CPDF_Stream::AsStream() const {
-  return this;
+void CPDF_Stream::InitStreamWithEmptyData(RetainPtr<CPDF_Dictionary> pDict) {
+  dict_ = std::move(pDict);
+  TakeData({});
 }
 
-void CPDF_Stream::InitStream(pdfium::span<const uint8_t> pData,
-                             RetainPtr<CPDF_Dictionary> pDict) {
-  m_pDict = std::move(pDict);
-  SetData(pData);
-}
-
-void CPDF_Stream::InitStreamFromFile(
-    const RetainPtr<IFX_SeekableReadStream>& pFile,
-    RetainPtr<CPDF_Dictionary> pDict) {
-  m_bMemoryBased = false;
-  m_pDataBuf.reset();
-  m_pFile = pFile;
-  m_dwSize = pdfium::base::checked_cast<uint32_t>(pFile->GetSize());
-  m_pDict = std::move(pDict);
-  m_pDict->SetNewFor<CPDF_Number>("Length", static_cast<int>(m_dwSize));
+void CPDF_Stream::InitStreamFromFile(RetainPtr<IFX_SeekableReadStream> pFile,
+                                     RetainPtr<CPDF_Dictionary> pDict) {
+  data_ = pFile;
+  dict_ = std::move(pDict);
+  SetLengthInDict(pdfium::base::checked_cast<int>(pFile->GetSize()));
 }
 
 RetainPtr<CPDF_Object> CPDF_Stream::Clone() const {
@@ -94,29 +86,27 @@
     bool bDirect,
     std::set<const CPDF_Object*>* pVisited) const {
   pVisited->insert(this);
-  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(this);
+  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pdfium::WrapRetain(this));
   pAcc->LoadAllDataRaw();
 
-  uint32_t streamSize = pAcc->GetSize();
-  const CPDF_Dictionary* pDict = GetDict();
+  RetainPtr<const CPDF_Dictionary> pDict = GetDict();
   RetainPtr<CPDF_Dictionary> pNewDict;
-  if (pDict && !pdfium::ContainsKey(*pVisited, pDict)) {
-    pNewDict =
-        ToDictionary(static_cast<const CPDF_Object*>(pDict)->CloneNonCyclic(
-            bDirect, pVisited));
+  if (pDict && !pdfium::Contains(*pVisited, pDict.Get())) {
+    pNewDict = ToDictionary(static_cast<const CPDF_Object*>(pDict.Get())
+                                ->CloneNonCyclic(bDirect, pVisited));
   }
-  return pdfium::MakeRetain<CPDF_Stream>(pAcc->DetachData(), streamSize,
+  return pdfium::MakeRetain<CPDF_Stream>(pAcc->DetachData(),
                                          std::move(pNewDict));
 }
 
 void CPDF_Stream::SetDataAndRemoveFilter(pdfium::span<const uint8_t> pData) {
   SetData(pData);
-  m_pDict->RemoveFor("Filter");
-  m_pDict->RemoveFor(pdfium::stream::kDecodeParms);
+  dict_->RemoveFor("Filter");
+  dict_->RemoveFor(pdfium::stream::kDecodeParms);
 }
 
 void CPDF_Stream::SetDataFromStringstreamAndRemoveFilter(
-    std::ostringstream* stream) {
+    fxcrt::ostringstream* stream) {
   if (stream->tellp() <= 0) {
     SetDataAndRemoveFilter({});
     return;
@@ -128,26 +118,17 @@
 }
 
 void CPDF_Stream::SetData(pdfium::span<const uint8_t> pData) {
-  std::unique_ptr<uint8_t, FxFreeDeleter> data_copy;
-  if (!pData.empty()) {
-    data_copy.reset(FX_Alloc(uint8_t, pData.size()));
-    memcpy(data_copy.get(), pData.data(), pData.size());
-  }
-  TakeData(std::move(data_copy), pData.size());
+  DataVector<uint8_t> data_copy(pData.begin(), pData.end());
+  TakeData(std::move(data_copy));
 }
 
-void CPDF_Stream::TakeData(std::unique_ptr<uint8_t, FxFreeDeleter> pData,
-                           uint32_t size) {
-  m_bMemoryBased = true;
-  m_pFile = nullptr;
-  m_pDataBuf = std::move(pData);
-  m_dwSize = size;
-  if (!m_pDict)
-    m_pDict = pdfium::MakeRetain<CPDF_Dictionary>();
-  m_pDict->SetNewFor<CPDF_Number>("Length", static_cast<int>(size));
+void CPDF_Stream::TakeData(DataVector<uint8_t> data) {
+  const size_t size = data.size();
+  data_ = std::move(data);
+  SetLengthInDict(pdfium::base::checked_cast<int>(size));
 }
 
-void CPDF_Stream::SetDataFromStringstream(std::ostringstream* stream) {
+void CPDF_Stream::SetDataFromStringstream(fxcrt::ostringstream* stream) {
   if (stream->tellp() <= 0) {
     SetData({});
     return;
@@ -156,59 +137,72 @@
            static_cast<size_t>(stream->tellp())});
 }
 
-bool CPDF_Stream::ReadRawData(FX_FILESIZE offset,
-                              uint8_t* buf,
-                              uint32_t size) const {
-  if (!m_bMemoryBased && m_pFile)
-    return m_pFile->ReadBlockAtOffset(buf, offset, size);
+DataVector<uint8_t> CPDF_Stream::ReadAllRawData() const {
+  CHECK(IsFileBased());
 
-  if (m_pDataBuf)
-    memcpy(buf, m_pDataBuf.get() + offset, size);
+  DataVector<uint8_t> result(GetRawSize());
+  DCHECK(!result.empty());
 
-  return true;
+  auto underlying_stream = absl::get<RetainPtr<IFX_SeekableReadStream>>(data_);
+  if (!underlying_stream->ReadBlockAtOffset(result, 0))
+    return DataVector<uint8_t>();
+
+  return result;
 }
 
 bool CPDF_Stream::HasFilter() const {
-  return m_pDict && m_pDict->KeyExist("Filter");
+  return dict_ && dict_->KeyExist("Filter");
 }
 
 WideString CPDF_Stream::GetUnicodeText() const {
-  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(this);
+  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pdfium::WrapRetain(this));
   pAcc->LoadAllDataFiltered();
   return PDF_DecodeText(pAcc->GetSpan());
 }
 
 bool CPDF_Stream::WriteTo(IFX_ArchiveStream* archive,
                           const CPDF_Encryptor* encryptor) const {
-  const bool is_metadata = IsMetaDataStreamDictionary(GetDict());
-  CPDF_FlateEncoder encoder(this, !is_metadata);
+  const bool is_metadata = IsMetaDataStreamDictionary(GetDict().Get());
+  CPDF_FlateEncoder encoder(pdfium::WrapRetain(this), !is_metadata);
 
-  std::vector<uint8_t> encrypted_data;
+  DataVector<uint8_t> encrypted_data;
   pdfium::span<const uint8_t> data = encoder.GetSpan();
-
   if (encryptor && !is_metadata) {
     encrypted_data = encryptor->Encrypt(data);
     data = encrypted_data;
   }
 
-  size_t size = data.size();
-  if (static_cast<size_t>(encoder.GetDict()->GetIntegerFor("Length")) != size) {
-    encoder.CloneDict();
-    encoder.GetClonedDict()->SetNewFor<CPDF_Number>("Length",
-                                                    static_cast<int>(size));
-  }
-
-  if (!encoder.GetDict()->WriteTo(archive, encryptor))
+  encoder.UpdateLength(data.size());
+  if (!encoder.WriteDictTo(archive, encryptor))
     return false;
 
   if (!archive->WriteString("stream\r\n"))
     return false;
 
-  if (size && !archive->WriteBlock(data.data(), size))
+  if (!archive->WriteBlock(data))
     return false;
 
-  if (!archive->WriteString("\r\nendstream"))
-    return false;
+  return archive->WriteString("\r\nendstream");
+}
 
-  return true;
+size_t CPDF_Stream::GetRawSize() const {
+  if (IsFileBased()) {
+    return pdfium::base::checked_cast<size_t>(
+        absl::get<RetainPtr<IFX_SeekableReadStream>>(data_)->GetSize());
+  }
+  if (IsMemoryBased())
+    return absl::get<DataVector<uint8_t>>(data_).size();
+  DCHECK(IsUninitialized());
+  return 0;
+}
+
+pdfium::span<const uint8_t> CPDF_Stream::GetInMemoryRawData() const {
+  DCHECK(IsMemoryBased());
+  return absl::get<DataVector<uint8_t>>(data_);
+}
+
+void CPDF_Stream::SetLengthInDict(int length) {
+  if (!dict_)
+    dict_ = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict_->SetNewFor<CPDF_Number>("Length", length);
 }
diff --git a/core/fpdfapi/parser/cpdf_stream.h b/core/fpdfapi/parser/cpdf_stream.h
index d29f655..f1bb78b 100644
--- a/core/fpdfapi/parser/cpdf_stream.h
+++ b/core/fpdfapi/parser/cpdf_stream.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,80 +7,96 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_STREAM_H_
 #define CORE_FPDFAPI_PARSER_CPDF_STREAM_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <set>
-#include <sstream>
 
 #include "core/fpdfapi/parser/cpdf_object.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_string_wrappers.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
+
+class IFX_SeekableReadStream;
 
 class CPDF_Stream final : public CPDF_Object {
  public:
-  static const int kFileBufSize = 512;
+  static constexpr int kFileBufSize = 512;
 
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // CPDF_Object:
   Type GetType() const override;
   RetainPtr<CPDF_Object> Clone() const override;
-  CPDF_Dictionary* GetDict() override;
-  const CPDF_Dictionary* GetDict() const override;
   WideString GetUnicodeText() const override;
-  bool IsStream() const override;
-  CPDF_Stream* AsStream() override;
-  const CPDF_Stream* AsStream() const override;
+  CPDF_Stream* AsMutableStream() override;
   bool WriteTo(IFX_ArchiveStream* archive,
                const CPDF_Encryptor* encryptor) const override;
 
-  uint32_t GetRawSize() const { return m_dwSize; }
-  // Will be null in case when stream is not memory based.
-  // Use CPDF_StreamAcc to data access in all cases.
-  uint8_t* GetInMemoryRawData() const { return m_pDataBuf.get(); }
+  size_t GetRawSize() const;
+  // Can only be called when stream is memory-based.
+  // This is meant to be used by CPDF_StreamAcc only.
+  // Other callers should use CPDF_StreamAcc to access data in all cases.
+  pdfium::span<const uint8_t> GetInMemoryRawData() const;
 
-  // Copies span into internally-owned buffer.
+  // Copies span or stream into internally-owned buffer.
   void SetData(pdfium::span<const uint8_t> pData);
+  void SetDataFromStringstream(fxcrt::ostringstream* stream);
 
-  void TakeData(std::unique_ptr<uint8_t, FxFreeDeleter> pData, uint32_t size);
-
-  void SetDataFromStringstream(std::ostringstream* stream);
+  void TakeData(DataVector<uint8_t> data);
 
   // Set data and remove "Filter" and "DecodeParms" fields from stream
-  // dictionary.
+  // dictionary. Copies span or stream into internally-owned buffer.
   void SetDataAndRemoveFilter(pdfium::span<const uint8_t> pData);
-  void SetDataFromStringstreamAndRemoveFilter(std::ostringstream* stream);
+  void SetDataFromStringstreamAndRemoveFilter(fxcrt::ostringstream* stream);
 
-  void InitStream(pdfium::span<const uint8_t> pData,
-                  RetainPtr<CPDF_Dictionary> pDict);
-  void InitStreamFromFile(const RetainPtr<IFX_SeekableReadStream>& pFile,
+  void InitStreamWithEmptyData(RetainPtr<CPDF_Dictionary> pDict);
+  void InitStreamFromFile(RetainPtr<IFX_SeekableReadStream> pFile,
                           RetainPtr<CPDF_Dictionary> pDict);
 
-  bool ReadRawData(FX_FILESIZE offset, uint8_t* pBuf, uint32_t buf_size) const;
+  // Can only be called when a stream is not memory-based.
+  DataVector<uint8_t> ReadAllRawData() const;
 
-  bool IsMemoryBased() const { return m_bMemoryBased; }
+  bool IsUninitialized() const {
+    return absl::holds_alternative<absl::monostate>(data_);
+  }
+  bool IsFileBased() const {
+    return absl::holds_alternative<RetainPtr<IFX_SeekableReadStream>>(data_);
+  }
+  bool IsMemoryBased() const {
+    return absl::holds_alternative<DataVector<uint8_t>>(data_);
+  }
   bool HasFilter() const;
 
  private:
+  friend class CPDF_Dictionary;
+
+  // Uninitialized.
   CPDF_Stream();
-  CPDF_Stream(std::unique_ptr<uint8_t, FxFreeDeleter> pData,
-              uint32_t size,
-              RetainPtr<CPDF_Dictionary> pDict);
+
+  // Initializes with empty data.
+  explicit CPDF_Stream(RetainPtr<CPDF_Dictionary> pDict);
+
+  CPDF_Stream(DataVector<uint8_t> pData, RetainPtr<CPDF_Dictionary> pDict);
   ~CPDF_Stream() override;
 
+  const CPDF_Dictionary* GetDictInternal() const override;
   RetainPtr<CPDF_Object> CloneNonCyclic(
       bool bDirect,
       std::set<const CPDF_Object*>* pVisited) const override;
 
-  bool m_bMemoryBased = true;
-  uint32_t m_dwSize = 0;
-  RetainPtr<CPDF_Dictionary> m_pDict;
-  std::unique_ptr<uint8_t, FxFreeDeleter> m_pDataBuf;
-  RetainPtr<IFX_SeekableReadStream> m_pFile;
+  void SetLengthInDict(int length);
+
+  absl::variant<absl::monostate,
+                RetainPtr<IFX_SeekableReadStream>,
+                DataVector<uint8_t>>
+      data_;
+  RetainPtr<CPDF_Dictionary> dict_;
 };
 
 inline CPDF_Stream* ToStream(CPDF_Object* obj) {
-  return obj ? obj->AsStream() : nullptr;
+  return obj ? obj->AsMutableStream() : nullptr;
 }
 
 inline const CPDF_Stream* ToStream(const CPDF_Object* obj) {
@@ -91,4 +107,8 @@
   return RetainPtr<CPDF_Stream>(ToStream(obj.Get()));
 }
 
+inline RetainPtr<const CPDF_Stream> ToStream(RetainPtr<const CPDF_Object> obj) {
+  return RetainPtr<const CPDF_Stream>(ToStream(obj.Get()));
+}
+
 #endif  // CORE_FPDFAPI_PARSER_CPDF_STREAM_H_
diff --git a/core/fpdfapi/parser/cpdf_stream_acc.cpp b/core/fpdfapi/parser/cpdf_stream_acc.cpp
index cd99751..4f42c6d 100644
--- a/core/fpdfapi/parser/cpdf_stream_acc.cpp
+++ b/core/fpdfapi/parser/cpdf_stream_acc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,16 @@
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 
 #include <utility>
-#include <vector>
 
 #include "core/fdrm/fx_crypt.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "core/fxcrt/data_vector.h"
+#include "third_party/base/check_op.h"
 
-CPDF_StreamAcc::CPDF_StreamAcc(const CPDF_Stream* pStream)
-    : m_pStream(pStream) {}
+CPDF_StreamAcc::CPDF_StreamAcc(RetainPtr<const CPDF_Stream> pStream)
+    : m_pStream(std::move(pStream)) {}
 
 CPDF_StreamAcc::~CPDF_StreamAcc() = default;
 
@@ -23,8 +24,8 @@
                                  uint32_t estimated_size,
                                  bool bImageAcc) {
   if (bRawAccess) {
-    ASSERT(!estimated_size);
-    ASSERT(!bImageAcc);
+    DCHECK(!estimated_size);
+    DCHECK(!bImageAcc);
   }
 
   if (!m_pStream)
@@ -54,117 +55,117 @@
   LoadAllData(true, 0, false);
 }
 
-const CPDF_Dictionary* CPDF_StreamAcc::GetDict() const {
-  return m_pStream ? m_pStream->GetDict() : nullptr;
+RetainPtr<const CPDF_Stream> CPDF_StreamAcc::GetStream() const {
+  return m_pStream;
 }
 
-uint8_t* CPDF_StreamAcc::GetData() const {
-  if (m_pData.IsOwned())
-    return m_pData.Get();
-  return m_pStream ? m_pStream->GetInMemoryRawData() : nullptr;
+int CPDF_StreamAcc::GetLength1ForTest() const {
+  return m_pStream->GetDict()->GetIntegerFor("Length1");
+}
+
+RetainPtr<const CPDF_Dictionary> CPDF_StreamAcc::GetImageParam() const {
+  return m_pImageParam;
 }
 
 uint32_t CPDF_StreamAcc::GetSize() const {
-  if (m_pData.IsOwned())
-    return m_dwSize;
-  return (m_pStream && m_pStream->IsMemoryBased()) ? m_pStream->GetRawSize()
-                                                   : 0;
-}
-
-pdfium::span<uint8_t> CPDF_StreamAcc::GetSpan() {
-  return {GetData(), GetSize()};
+  return GetSpan().size();
 }
 
 pdfium::span<const uint8_t> CPDF_StreamAcc::GetSpan() const {
-  return {GetData(), GetSize()};
+  if (is_owned())
+    return absl::get<DataVector<uint8_t>>(m_Data);
+  if (m_pStream && m_pStream->IsMemoryBased())
+    return m_pStream->GetInMemoryRawData();
+  return {};
+}
+
+uint64_t CPDF_StreamAcc::KeyForCache() const {
+  return m_pStream ? m_pStream->KeyForCache() : 0;
 }
 
 ByteString CPDF_StreamAcc::ComputeDigest() const {
   uint8_t digest[20];
-  CRYPT_SHA1Generate(GetData(), GetSize(), digest);
+  pdfium::span<const uint8_t> span = GetSpan();
+  CRYPT_SHA1Generate(span.data(), span.size(), digest);
   return ByteString(digest, 20);
 }
 
-std::unique_ptr<uint8_t, FxFreeDeleter> CPDF_StreamAcc::DetachData() {
-  if (m_pData.IsOwned()) {
-    std::unique_ptr<uint8_t, FxFreeDeleter> p = m_pData.ReleaseAndClear();
-    m_dwSize = 0;
-    return p;
-  }
-  std::unique_ptr<uint8_t, FxFreeDeleter> p(FX_Alloc(uint8_t, m_dwSize));
-  memcpy(p.get(), m_pData.Get(), m_dwSize);
-  return p;
+DataVector<uint8_t> CPDF_StreamAcc::DetachData() {
+  if (is_owned())
+    return std::move(absl::get<DataVector<uint8_t>>(m_Data));
+
+  auto span = absl::get<pdfium::span<const uint8_t>>(m_Data);
+  return DataVector<uint8_t>(span.begin(), span.end());
 }
 
 void CPDF_StreamAcc::ProcessRawData() {
+  if (m_pStream->IsUninitialized())
+    return;
+
   uint32_t dwSrcSize = m_pStream->GetRawSize();
   if (dwSrcSize == 0)
     return;
 
   if (m_pStream->IsMemoryBased()) {
-    m_pData = m_pStream->GetInMemoryRawData();
-    m_dwSize = dwSrcSize;
+    m_Data = m_pStream->GetInMemoryRawData();
     return;
   }
 
-  std::unique_ptr<uint8_t, FxFreeDeleter> pData = ReadRawStream();
-  if (!pData)
+  DataVector<uint8_t> data = ReadRawStream();
+  if (data.empty())
     return;
 
-  m_pData = std::move(pData);
-  m_dwSize = dwSrcSize;
+  m_Data = std::move(data);
 }
 
 void CPDF_StreamAcc::ProcessFilteredData(uint32_t estimated_size,
                                          bool bImageAcc) {
+  if (m_pStream->IsUninitialized())
+    return;
+
   uint32_t dwSrcSize = m_pStream->GetRawSize();
   if (dwSrcSize == 0)
     return;
 
-  MaybeOwned<uint8_t, FxFreeDeleter> pSrcData;
+  absl::variant<pdfium::span<const uint8_t>, DataVector<uint8_t>> src_data;
+  pdfium::span<const uint8_t> src_span;
   if (m_pStream->IsMemoryBased()) {
-    pSrcData = m_pStream->GetInMemoryRawData();
+    src_span = m_pStream->GetInMemoryRawData();
+    src_data = src_span;
   } else {
-    std::unique_ptr<uint8_t, FxFreeDeleter> pTempSrcData = ReadRawStream();
-    if (!pTempSrcData)
+    DataVector<uint8_t> temp_src_data = ReadRawStream();
+    if (temp_src_data.empty())
       return;
 
-    pSrcData = std::move(pTempSrcData);
+    src_span = pdfium::make_span(temp_src_data);
+    src_data = std::move(temp_src_data);
   }
 
   std::unique_ptr<uint8_t, FxFreeDeleter> pDecodedData;
   uint32_t dwDecodedSize = 0;
 
-  Optional<std::vector<std::pair<ByteString, const CPDF_Object*>>>
-      decoder_array = GetDecoderArray(m_pStream->GetDict());
-  if (!decoder_array.has_value() ||
-      !PDF_DataDecode({pSrcData.Get(), dwSrcSize}, estimated_size, bImageAcc,
+  absl::optional<DecoderArray> decoder_array =
+      GetDecoderArray(m_pStream->GetDict());
+  if (!decoder_array.has_value() || decoder_array.value().empty() ||
+      !PDF_DataDecode(src_span, estimated_size, bImageAcc,
                       decoder_array.value(), &pDecodedData, &dwDecodedSize,
                       &m_ImageDecoder, &m_pImageParam)) {
-    m_pData = std::move(pSrcData);
-    m_dwSize = dwSrcSize;
+    m_Data = std::move(src_data);
     return;
   }
 
   if (pDecodedData) {
-    ASSERT(pDecodedData.get() != pSrcData.Get());
-    m_pData = std::move(pDecodedData);
-    m_dwSize = dwDecodedSize;
+    DCHECK_NE(pDecodedData.get(), src_span.data());
+    // TODO(crbug.com/pdfium/1872): Avoid copying.
+    m_Data = DataVector<uint8_t>(pDecodedData.get(),
+                                 pDecodedData.get() + dwDecodedSize);
   } else {
-    m_pData = std::move(pSrcData);
-    m_dwSize = dwSrcSize;
+    m_Data = std::move(src_data);
   }
 }
 
-std::unique_ptr<uint8_t, FxFreeDeleter> CPDF_StreamAcc::ReadRawStream() const {
-  ASSERT(m_pStream);
-  ASSERT(!m_pStream->IsMemoryBased());
-
-  uint32_t dwSrcSize = m_pStream->GetRawSize();
-  ASSERT(dwSrcSize);
-  std::unique_ptr<uint8_t, FxFreeDeleter> pSrcData(
-      FX_Alloc(uint8_t, dwSrcSize));
-  if (!m_pStream->ReadRawData(0, pSrcData.get(), dwSrcSize))
-    return nullptr;
-  return pSrcData;
+DataVector<uint8_t> CPDF_StreamAcc::ReadRawStream() const {
+  DCHECK(m_pStream);
+  DCHECK(m_pStream->IsFileBased());
+  return m_pStream->ReadAllRawData();
 }
diff --git a/core/fpdfapi/parser/cpdf_stream_acc.h b/core/fpdfapi/parser/cpdf_stream_acc.h
index 046fe5d..74657dd 100644
--- a/core/fpdfapi/parser/cpdf_stream_acc.h
+++ b/core/fpdfapi/parser/cpdf_stream_acc.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,22 +7,22 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_STREAM_ACC_H_
 #define CORE_FPDFAPI_PARSER_CPDF_STREAM_ACC_H_
 
+#include <stdint.h>
+
 #include <memory>
 
-#include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/maybe_owned.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Dictionary;
 class CPDF_Stream;
 
 class CPDF_StreamAcc final : public Retainable {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   CPDF_StreamAcc(const CPDF_StreamAcc&) = delete;
   CPDF_StreamAcc& operator=(const CPDF_StreamAcc&) = delete;
@@ -32,34 +32,38 @@
   void LoadAllDataImageAcc(uint32_t estimated_size);
   void LoadAllDataRaw();
 
-  const CPDF_Stream* GetStream() const { return m_pStream.Get(); }
-  const CPDF_Dictionary* GetDict() const;
+  RetainPtr<const CPDF_Stream> GetStream() const;
+  RetainPtr<const CPDF_Dictionary> GetImageParam() const;
 
-  uint8_t* GetData() const;
   uint32_t GetSize() const;
-  pdfium::span<uint8_t> GetSpan();
   pdfium::span<const uint8_t> GetSpan() const;
+  uint64_t KeyForCache() const;
   ByteString ComputeDigest() const;
   ByteString GetImageDecoder() const { return m_ImageDecoder; }
-  const CPDF_Dictionary* GetImageParam() const { return m_pImageParam.Get(); }
-  std::unique_ptr<uint8_t, FxFreeDeleter> DetachData();
+  DataVector<uint8_t> DetachData();
+
+  int GetLength1ForTest() const;
 
  private:
-  explicit CPDF_StreamAcc(const CPDF_Stream* pStream);
+  explicit CPDF_StreamAcc(RetainPtr<const CPDF_Stream> pStream);
   ~CPDF_StreamAcc() override;
 
   void LoadAllData(bool bRawAccess, uint32_t estimated_size, bool bImageAcc);
   void ProcessRawData();
   void ProcessFilteredData(uint32_t estimated_size, bool bImageAcc);
 
-  // Reads the raw data from |m_pStream|, or return nullptr on failure.
-  std::unique_ptr<uint8_t, FxFreeDeleter> ReadRawStream() const;
+  // Returns the raw data from `m_pStream`, or no data on failure.
+  DataVector<uint8_t> ReadRawStream() const;
 
-  MaybeOwned<uint8_t, FxFreeDeleter> m_pData;
-  uint32_t m_dwSize = 0;
+  bool is_owned() const {
+    return absl::holds_alternative<DataVector<uint8_t>>(m_Data);
+  }
+
   ByteString m_ImageDecoder;
   RetainPtr<const CPDF_Dictionary> m_pImageParam;
+  // Needs to outlive `m_Data` when the data is not owned.
   RetainPtr<const CPDF_Stream> const m_pStream;
+  absl::variant<pdfium::span<const uint8_t>, DataVector<uint8_t>> m_Data;
 };
 
 #endif  // CORE_FPDFAPI_PARSER_CPDF_STREAM_ACC_H_
diff --git a/core/fpdfapi/parser/cpdf_stream_acc_unittest.cpp b/core/fpdfapi/parser/cpdf_stream_acc_unittest.cpp
index 72ba93d..c22e655 100644
--- a/core/fpdfapi/parser/cpdf_stream_acc_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_stream_acc_unittest.cpp
@@ -1,22 +1,35 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 
+#include <utility>
+
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fxcrt/fx_stream.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/invalid_seekable_read_stream.h"
 
-TEST(CPDF_StreamAccTest, ReadRawDataFailed) {
+TEST(StreamAccTest, ReadRawDataFailed) {
   auto stream = pdfium::MakeRetain<CPDF_Stream>();
   stream->InitStreamFromFile(
       pdfium::MakeRetain<InvalidSeekableReadStream>(1024),
       pdfium::MakeRetain<CPDF_Dictionary>());
-  auto stream_acc = pdfium::MakeRetain<CPDF_StreamAcc>(stream.Get());
+  auto stream_acc = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(stream));
   stream_acc->LoadAllDataRaw();
-  EXPECT_EQ(0u, stream_acc->GetSize());
-  EXPECT_FALSE(stream_acc->GetData());
+  EXPECT_TRUE(stream_acc->GetSpan().empty());
+}
+
+// Regression test for crbug.com/1361849. Should not trigger
+// ProbeForLowSeverityLifetimeIssue() failure.
+TEST(StreamAccTest, DataStreamLifeTime) {
+  constexpr uint8_t kData[] = {'a', 'b', 'c'};
+  auto stream = pdfium::MakeRetain<CPDF_Stream>();
+  stream->SetData(kData);
+  auto stream_acc = pdfium::MakeRetain<CPDF_StreamAcc>(stream);
+  stream_acc->LoadAllDataRaw();
+  stream.Reset();
+  EXPECT_EQ(pdfium::make_span(kData), stream_acc->GetSpan());
 }
diff --git a/core/fpdfapi/parser/cpdf_string.cpp b/core/fpdfapi/parser/cpdf_string.cpp
index 7058169..7716926 100644
--- a/core/fpdfapi/parser/cpdf_string.cpp
+++ b/core/fpdfapi/parser/cpdf_string.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,13 +6,14 @@
 
 #include "core/fpdfapi/parser/cpdf_string.h"
 
+#include <stdint.h>
+
 #include <utility>
-#include <vector>
 
 #include "core/fpdfapi/parser/cpdf_encryptor.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_stream.h"
-#include "third_party/base/ptr_util.h"
 
 CPDF_String::CPDF_String() = default;
 
@@ -24,7 +25,7 @@
     m_String = pPool->Intern(m_String);
 }
 
-CPDF_String::CPDF_String(WeakPtr<ByteStringPool> pPool, const WideString& str)
+CPDF_String::CPDF_String(WeakPtr<ByteStringPool> pPool, WideStringView str)
     : m_String(PDF_EncodeText(str)) {
   if (pPool)
     m_String = pPool->Intern(m_String);
@@ -51,15 +52,7 @@
   m_String = str;
 }
 
-bool CPDF_String::IsString() const {
-  return true;
-}
-
-CPDF_String* CPDF_String::AsString() {
-  return this;
-}
-
-const CPDF_String* CPDF_String::AsString() const {
+CPDF_String* CPDF_String::AsMutableString() {
   return this;
 }
 
@@ -69,13 +62,19 @@
 
 bool CPDF_String::WriteTo(IFX_ArchiveStream* archive,
                           const CPDF_Encryptor* encryptor) const {
-  std::vector<uint8_t> encrypted_data;
+  DataVector<uint8_t> encrypted_data;
   pdfium::span<const uint8_t> data = m_String.raw_span();
   if (encryptor) {
     encrypted_data = encryptor->Encrypt(data);
     data = encrypted_data;
   }
-  const ByteString content =
-      PDF_EncodeString(ByteString(data.data(), data.size()), IsHex());
+  ByteStringView raw(data.data(), data.size());
+  ByteString content =
+      m_bHex ? PDF_HexEncodeString(raw) : PDF_EncodeString(raw);
   return archive->WriteString(content.AsStringView());
 }
+
+ByteString CPDF_String::EncodeString() const {
+  return m_bHex ? PDF_HexEncodeString(m_String.AsStringView())
+                : PDF_EncodeString(m_String.AsStringView());
+}
diff --git a/core/fpdfapi/parser/cpdf_string.h b/core/fpdfapi/parser/cpdf_string.h
index 8efc71b..5ae6277 100644
--- a/core/fpdfapi/parser/cpdf_string.h
+++ b/core/fpdfapi/parser/cpdf_string.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,18 +7,15 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_STRING_H_
 #define CORE_FPDFAPI_PARSER_CPDF_STRING_H_
 
-#include <memory>
-
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/string_pool_template.h"
 #include "core/fxcrt/weak_ptr.h"
 
 class CPDF_String final : public CPDF_Object {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // CPDF_Object:
   Type GetType() const override;
@@ -26,18 +23,17 @@
   ByteString GetString() const override;
   WideString GetUnicodeText() const override;
   void SetString(const ByteString& str) override;
-  bool IsString() const override;
-  CPDF_String* AsString() override;
-  const CPDF_String* AsString() const override;
+  CPDF_String* AsMutableString() override;
   bool WriteTo(IFX_ArchiveStream* archive,
                const CPDF_Encryptor* encryptor) const override;
 
   bool IsHex() const { return m_bHex; }
+  ByteString EncodeString() const;
 
  private:
   CPDF_String();
   CPDF_String(WeakPtr<ByteStringPool> pPool, const ByteString& str, bool bHex);
-  CPDF_String(WeakPtr<ByteStringPool> pPool, const WideString& str);
+  CPDF_String(WeakPtr<ByteStringPool> pPool, WideStringView str);
   ~CPDF_String() override;
 
   ByteString m_String;
@@ -45,11 +41,15 @@
 };
 
 inline CPDF_String* ToString(CPDF_Object* obj) {
-  return obj ? obj->AsString() : nullptr;
+  return obj ? obj->AsMutableString() : nullptr;
 }
 
 inline const CPDF_String* ToString(const CPDF_Object* obj) {
   return obj ? obj->AsString() : nullptr;
 }
 
+inline RetainPtr<const CPDF_String> ToString(RetainPtr<const CPDF_Object> obj) {
+  return RetainPtr<const CPDF_String>(ToString(obj.Get()));
+}
+
 #endif  // CORE_FPDFAPI_PARSER_CPDF_STRING_H_
diff --git a/core/fpdfapi/parser/cpdf_syntax_parser.cpp b/core/fpdfapi/parser/cpdf_syntax_parser.cpp
index 0a55c27..0a85e8e 100644
--- a/core/fpdfapi/parser/cpdf_syntax_parser.cpp
+++ b/core/fpdfapi/parser/cpdf_syntax_parser.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,10 @@
 
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
 
+#include <ctype.h>
+
 #include <algorithm>
-#include <sstream>
 #include <utility>
-#include <vector>
 
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_boolean.h"
@@ -24,39 +24,46 @@
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcrt/autorestorer.h"
-#include "core/fxcrt/cfx_binarybuf.h"
+#include "core/fxcrt/cfx_read_only_vector_stream.h"
+#include "core/fxcrt/fixed_uninit_data_vector.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 #include "third_party/base/numerics/safe_math.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
-enum class ReadStatus { Normal, Backslash, Octal, FinishOctal, CarriageReturn };
+enum class ReadStatus {
+  kNormal,
+  kBackslash,
+  kOctal,
+  kFinishOctal,
+  kCarriageReturn
+};
 
 class ReadableSubStream final : public IFX_SeekableReadStream {
  public:
-  ReadableSubStream(const RetainPtr<IFX_SeekableReadStream>& pFileRead,
+  ReadableSubStream(RetainPtr<IFX_SeekableReadStream> pFileRead,
                     FX_FILESIZE part_offset,
                     FX_FILESIZE part_size)
-      : m_pFileRead(pFileRead),
+      : m_pFileRead(std::move(pFileRead)),
         m_PartOffset(part_offset),
         m_PartSize(part_size) {}
 
   ~ReadableSubStream() override = default;
 
   // IFX_SeekableReadStream overrides:
-  bool ReadBlockAtOffset(void* buffer,
-                         FX_FILESIZE offset,
-                         size_t size) override {
+  bool ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                         FX_FILESIZE offset) override {
     FX_SAFE_FILESIZE safe_end = offset;
-    safe_end += size;
+    safe_end += buffer.size();
     // Check that requested range is valid, to prevent calling of ReadBlock
     // of original m_pFileRead with incorrect params.
     if (!safe_end.IsValid() || safe_end.ValueOrDie() > m_PartSize)
       return false;
 
-    return m_pFileRead->ReadBlockAtOffset(buffer, m_PartOffset + offset, size);
+    return m_pFileRead->ReadBlockAtOffset(buffer, m_PartOffset + offset);
   }
 
   FX_FILESIZE GetSize() override { return m_PartSize; }
@@ -74,26 +81,26 @@
 
 // static
 std::unique_ptr<CPDF_SyntaxParser> CPDF_SyntaxParser::CreateForTesting(
-    const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
+    RetainPtr<IFX_SeekableReadStream> pFileAccess,
     FX_FILESIZE HeaderOffset) {
-  return pdfium::MakeUnique<CPDF_SyntaxParser>(
-      pdfium::MakeRetain<CPDF_ReadValidator>(pFileAccess, nullptr),
+  return std::make_unique<CPDF_SyntaxParser>(
+      pdfium::MakeRetain<CPDF_ReadValidator>(std::move(pFileAccess), nullptr),
       HeaderOffset);
 }
 
 CPDF_SyntaxParser::CPDF_SyntaxParser(
-    const RetainPtr<IFX_SeekableReadStream>& pFileAccess)
+    RetainPtr<IFX_SeekableReadStream> pFileAccess)
     : CPDF_SyntaxParser(
-          pdfium::MakeRetain<CPDF_ReadValidator>(pFileAccess, nullptr),
+          pdfium::MakeRetain<CPDF_ReadValidator>(std::move(pFileAccess),
+                                                 nullptr),
           0) {}
 
-CPDF_SyntaxParser::CPDF_SyntaxParser(
-    const RetainPtr<CPDF_ReadValidator>& validator,
-    FX_FILESIZE HeaderOffset)
-    : m_pFileAccess(validator),
+CPDF_SyntaxParser::CPDF_SyntaxParser(RetainPtr<CPDF_ReadValidator> validator,
+                                     FX_FILESIZE HeaderOffset)
+    : m_pFileAccess(std::move(validator)),
       m_HeaderOffset(HeaderOffset),
       m_FileLen(m_pFileAccess->GetSize()) {
-  ASSERT(m_HeaderOffset <= m_FileLen);
+  DCHECK(m_HeaderOffset <= m_FileLen);
 }
 
 CPDF_SyntaxParser::~CPDF_SyntaxParser() = default;
@@ -114,8 +121,7 @@
     read_size = m_FileLen - read_pos;
 
   m_pFileBuf.resize(read_size);
-  if (!m_pFileAccess->ReadBlockAtOffset(m_pFileBuf.data(), read_pos,
-                                        read_size)) {
+  if (!m_pFileAccess->ReadBlockAtOffset(m_pFileBuf, read_pos)) {
     m_pFileBuf.clear();
     return false;
   }
@@ -157,36 +163,34 @@
   return true;
 }
 
-bool CPDF_SyntaxParser::ReadBlock(uint8_t* pBuf, uint32_t size) {
-  if (!m_pFileAccess->ReadBlockAtOffset(pBuf, m_Pos + m_HeaderOffset, size))
+bool CPDF_SyntaxParser::ReadBlock(pdfium::span<uint8_t> buffer) {
+  if (!m_pFileAccess->ReadBlockAtOffset(buffer, m_Pos + m_HeaderOffset))
     return false;
-  m_Pos += size;
+  m_Pos += buffer.size();
   return true;
 }
 
-void CPDF_SyntaxParser::GetNextWordInternal(bool* bIsNumber) {
+CPDF_SyntaxParser::WordType CPDF_SyntaxParser::GetNextWordInternal() {
   m_WordSize = 0;
-  if (bIsNumber)
-    *bIsNumber = true;
+  WordType word_type = WordType::kNumber;
 
   ToNextWord();
   uint8_t ch;
   if (!GetNextChar(ch))
-    return;
+    return word_type;
 
   if (PDFCharIsDelimiter(ch)) {
-    if (bIsNumber)
-      *bIsNumber = false;
+    word_type = WordType::kWord;
 
     m_WordBuffer[m_WordSize++] = ch;
     if (ch == '/') {
-      while (1) {
+      while (true) {
         if (!GetNextChar(ch))
-          return;
+          return word_type;
 
         if (!PDFCharIsOther(ch) && !PDFCharIsNumeric(ch)) {
           m_Pos--;
-          return;
+          return word_type;
         }
 
         if (m_WordSize < sizeof(m_WordBuffer) - 1)
@@ -194,7 +198,7 @@
       }
     } else if (ch == '<') {
       if (!GetNextChar(ch))
-        return;
+        return word_type;
 
       if (ch == '<')
         m_WordBuffer[m_WordSize++] = ch;
@@ -202,33 +206,32 @@
         m_Pos--;
     } else if (ch == '>') {
       if (!GetNextChar(ch))
-        return;
+        return word_type;
 
       if (ch == '>')
         m_WordBuffer[m_WordSize++] = ch;
       else
         m_Pos--;
     }
-    return;
+    return word_type;
   }
 
-  while (1) {
+  while (true) {
     if (m_WordSize < sizeof(m_WordBuffer) - 1)
       m_WordBuffer[m_WordSize++] = ch;
 
-    if (!PDFCharIsNumeric(ch)) {
-      if (bIsNumber)
-        *bIsNumber = false;
-    }
+    if (!PDFCharIsNumeric(ch))
+      word_type = WordType::kWord;
 
     if (!GetNextChar(ch))
-      return;
+      return word_type;
 
     if (PDFCharIsDelimiter(ch) || PDFCharIsWhitespace(ch)) {
       m_Pos--;
       break;
     }
   }
+  return word_type;
 }
 
 ByteString CPDF_SyntaxParser::ReadString() {
@@ -236,13 +239,13 @@
   if (!GetNextChar(ch))
     return ByteString();
 
-  std::ostringstream buf;
+  ByteString buf;
   int32_t parlevel = 0;
-  ReadStatus status = ReadStatus::Normal;
+  ReadStatus status = ReadStatus::kNormal;
   int32_t iEscCode = 0;
-  while (1) {
+  while (true) {
     switch (status) {
-      case ReadStatus::Normal:
+      case ReadStatus::kNormal:
         if (ch == ')') {
           if (parlevel == 0)
             return ByteString(buf);
@@ -251,60 +254,59 @@
           parlevel++;
         }
         if (ch == '\\')
-          status = ReadStatus::Backslash;
+          status = ReadStatus::kBackslash;
         else
-          buf << static_cast<char>(ch);
+          buf += static_cast<char>(ch);
         break;
-      case ReadStatus::Backslash:
+      case ReadStatus::kBackslash:
         if (FXSYS_IsOctalDigit(ch)) {
           iEscCode = FXSYS_DecimalCharToInt(static_cast<wchar_t>(ch));
-          status = ReadStatus::Octal;
+          status = ReadStatus::kOctal;
           break;
         }
-
         if (ch == '\r') {
-          status = ReadStatus::CarriageReturn;
+          status = ReadStatus::kCarriageReturn;
           break;
         }
         if (ch == 'n') {
-          buf << '\n';
+          buf += '\n';
         } else if (ch == 'r') {
-          buf << '\r';
+          buf += '\r';
         } else if (ch == 't') {
-          buf << '\t';
+          buf += '\t';
         } else if (ch == 'b') {
-          buf << '\b';
+          buf += '\b';
         } else if (ch == 'f') {
-          buf << '\f';
+          buf += '\f';
         } else if (ch != '\n') {
-          buf << static_cast<char>(ch);
+          buf += static_cast<char>(ch);
         }
-        status = ReadStatus::Normal;
+        status = ReadStatus::kNormal;
         break;
-      case ReadStatus::Octal:
+      case ReadStatus::kOctal:
         if (FXSYS_IsOctalDigit(ch)) {
           iEscCode =
               iEscCode * 8 + FXSYS_DecimalCharToInt(static_cast<wchar_t>(ch));
-          status = ReadStatus::FinishOctal;
+          status = ReadStatus::kFinishOctal;
         } else {
-          buf << static_cast<char>(iEscCode);
-          status = ReadStatus::Normal;
+          buf += static_cast<char>(iEscCode);
+          status = ReadStatus::kNormal;
           continue;
         }
         break;
-      case ReadStatus::FinishOctal:
-        status = ReadStatus::Normal;
+      case ReadStatus::kFinishOctal:
+        status = ReadStatus::kNormal;
         if (FXSYS_IsOctalDigit(ch)) {
           iEscCode =
               iEscCode * 8 + FXSYS_DecimalCharToInt(static_cast<wchar_t>(ch));
-          buf << static_cast<char>(iEscCode);
+          buf += static_cast<char>(iEscCode);
         } else {
-          buf << static_cast<char>(iEscCode);
+          buf += static_cast<char>(iEscCode);
           continue;
         }
         break;
-      case ReadStatus::CarriageReturn:
-        status = ReadStatus::Normal;
+      case ReadStatus::kCarriageReturn:
+        status = ReadStatus::kNormal;
         if (ch != '\n')
           continue;
         break;
@@ -315,7 +317,7 @@
   }
 
   GetNextChar(ch);
-  return ByteString(buf);
+  return buf;
 }
 
 ByteString CPDF_SyntaxParser::ReadHexString() {
@@ -323,20 +325,20 @@
   if (!GetNextChar(ch))
     return ByteString();
 
-  std::ostringstream buf;
+  ByteString buf;
   bool bFirst = true;
   uint8_t code = 0;
-  while (1) {
+  while (true) {
     if (ch == '>')
       break;
 
-    if (std::isxdigit(ch)) {
+    if (isxdigit(ch)) {
       int val = FXSYS_HexCharToInt(ch);
       if (bFirst) {
         code = val * 16;
       } else {
         code += val;
-        buf << static_cast<char>(code);
+        buf += static_cast<char>(code);
       }
       bFirst = !bFirst;
     }
@@ -345,9 +347,9 @@
       break;
   }
   if (!bFirst)
-    buf << static_cast<char>(code);
+    buf += static_cast<char>(code);
 
-  return ByteString(buf);
+  return buf;
 }
 
 void CPDF_SyntaxParser::ToNextLine() {
@@ -366,11 +368,16 @@
 }
 
 void CPDF_SyntaxParser::ToNextWord() {
+  if (m_TrailerEnds) {
+    RecordingToNextWord();
+    return;
+  }
+
   uint8_t ch;
   if (!GetNextChar(ch))
     return;
 
-  while (1) {
+  while (true) {
     while (PDFCharIsWhitespace(ch)) {
       if (!GetNextChar(ch))
         return;
@@ -379,7 +386,7 @@
     if (ch != '%')
       break;
 
-    while (1) {
+    while (true) {
       if (!GetNextChar(ch))
         return;
       if (PDFCharIsLineEnding(ch))
@@ -389,31 +396,97 @@
   m_Pos--;
 }
 
-ByteString CPDF_SyntaxParser::GetNextWord(bool* bIsNumber) {
-  const CPDF_ReadValidator::Session read_session(GetValidator());
-  GetNextWordInternal(bIsNumber);
-  ByteString ret;
-  if (!GetValidator()->has_read_problems())
-    ret = ByteString(m_WordBuffer, m_WordSize);
-  return ret;
+// A state machine which goes % -> E -> O -> F -> line ending.
+enum class EofState {
+  kInitial = 0,
+  kNonPercent,
+  kPercent,
+  kE,
+  kO,
+  kF,
+  kInvalid,
+};
+
+void CPDF_SyntaxParser::RecordingToNextWord() {
+  DCHECK(m_TrailerEnds);
+
+  EofState eof_state = EofState::kInitial;
+  // Find the first character which is neither whitespace, nor part of a
+  // comment.
+  while (true) {
+    uint8_t ch;
+    if (!GetNextChar(ch))
+      return;
+    switch (eof_state) {
+      case EofState::kInitial:
+        if (!PDFCharIsWhitespace(ch))
+          eof_state = ch == '%' ? EofState::kPercent : EofState::kNonPercent;
+        break;
+      case EofState::kNonPercent:
+        break;
+      case EofState::kPercent:
+        if (ch == 'E')
+          eof_state = EofState::kE;
+        else if (ch != '%')
+          eof_state = EofState::kInvalid;
+        break;
+      case EofState::kE:
+        eof_state = ch == 'O' ? EofState::kO : EofState::kInvalid;
+        break;
+      case EofState::kO:
+        eof_state = ch == 'F' ? EofState::kF : EofState::kInvalid;
+        break;
+      case EofState::kF:
+        if (ch == '\r') {
+          // See if \r has to be combined with a \n that follows it
+          // immediately.
+          if (GetNextChar(ch) && ch != '\n') {
+            ch = '\r';
+            m_Pos--;
+          }
+        }
+        // If we now have a \r, that's not followed by a \n, so both are OK.
+        if (ch == '\r' || ch == '\n')
+          m_TrailerEnds->push_back(m_Pos);
+        eof_state = EofState::kInvalid;
+        break;
+      case EofState::kInvalid:
+        break;
+    }
+    if (PDFCharIsLineEnding(ch))
+      eof_state = EofState::kInitial;
+    if (eof_state == EofState::kNonPercent)
+      break;
+  }
+  m_Pos--;
 }
 
-ByteString CPDF_SyntaxParser::PeekNextWord(bool* bIsNumber) {
+CPDF_SyntaxParser::WordResult CPDF_SyntaxParser::GetNextWord() {
+  CPDF_ReadValidator::ScopedSession read_session(GetValidator());
+  WordType word_type = GetNextWordInternal();
+  ByteString word;
+  if (!GetValidator()->has_read_problems())
+    word = ByteString(m_WordBuffer, m_WordSize);
+  return {word, word_type == WordType::kNumber};
+}
+
+ByteString CPDF_SyntaxParser::PeekNextWord() {
   AutoRestorer<FX_FILESIZE> save_pos(&m_Pos);
-  return GetNextWord(bIsNumber);
+  return GetNextWord().word;
 }
 
 ByteString CPDF_SyntaxParser::GetKeyword() {
-  return GetNextWord(nullptr);
+  return GetNextWord().word;
 }
 
 void CPDF_SyntaxParser::SetPos(FX_FILESIZE pos) {
+  DCHECK_GE(pos, 0);
   m_Pos = std::min(pos, m_FileLen);
 }
 
 RetainPtr<CPDF_Object> CPDF_SyntaxParser::GetObjectBody(
     CPDF_IndirectObjectHolder* pObjList) {
-  const CPDF_ReadValidator::Session read_session(GetValidator());
+  CPDF_ReadValidator::ScopedSession read_session(GetValidator());
   auto result = GetObjectBodyInternal(pObjList, ParseType::kLoose);
   if (GetValidator()->has_read_problems())
     return nullptr;
@@ -428,19 +501,19 @@
     return nullptr;
 
   FX_FILESIZE SavedObjPos = m_Pos;
-  bool bIsNumber;
-  ByteString word = GetNextWord(&bIsNumber);
+  WordResult word_result = GetNextWord();
+  const ByteString& word = word_result.word;
   if (word.IsEmpty())
     return nullptr;
 
-  if (bIsNumber) {
+  if (word_result.is_number) {
     AutoRestorer<FX_FILESIZE> pos_restorer(&m_Pos);
-    ByteString nextword = GetNextWord(&bIsNumber);
-    if (!bIsNumber)
+    WordResult nextword = GetNextWord();
+    if (!nextword.is_number)
       return pdfium::MakeRetain<CPDF_Number>(word.AsStringView());
 
-    ByteString nextword2 = GetNextWord(nullptr);
-    if (nextword2 != "R")
+    WordResult nextword2 = GetNextWord();
+    if (nextword2.word != "R")
       return pdfium::MakeRetain<CPDF_Number>(word.AsStringView());
 
     pos_restorer.AbandonRestoration();
@@ -469,7 +542,7 @@
     auto pArray = pdfium::MakeRetain<CPDF_Array>();
     while (RetainPtr<CPDF_Object> pObj =
                GetObjectBodyInternal(pObjList, ParseType::kLoose)) {
-      pArray->Add(std::move(pObj));
+      pArray->Append(std::move(pObj));
     }
     return (parse_type == ParseType::kLoose || m_WordBuffer[0] == ']')
                ? std::move(pArray)
@@ -483,8 +556,9 @@
   if (word == "<<") {
     RetainPtr<CPDF_Dictionary> pDict =
         pdfium::MakeRetain<CPDF_Dictionary>(m_pPool);
-    while (1) {
-      ByteString inner_word = GetNextWord(nullptr);
+    while (true) {
+      WordResult inner_word_result = GetNextWord();
+      const ByteString& inner_word = inner_word_result.word;
       if (inner_word.IsEmpty())
         return nullptr;
 
@@ -513,14 +587,14 @@
         return nullptr;
       }
 
-      if (!key.IsEmpty()) {
-        ByteString keyNoSlash(key.raw_str() + 1, key.GetLength() - 1);
-        pDict->SetFor(keyNoSlash, std::move(pObj));
+      // `key` has to be "/X" at the minimum.
+      if (key.GetLength() > 1) {
+        pDict->SetFor(key.Substr(1), std::move(pObj));
       }
     }
 
     AutoRestorer<FX_FILESIZE> pos_restorer(&m_Pos);
-    if (GetNextWord(nullptr) != "stream")
+    if (GetNextWord().word != "stream")
       return pDict;
     pos_restorer.AbandonRestoration();
     return ReadStream(std::move(pDict));
@@ -534,22 +608,23 @@
 RetainPtr<CPDF_Object> CPDF_SyntaxParser::GetIndirectObject(
     CPDF_IndirectObjectHolder* pObjList,
     ParseType parse_type) {
-  const CPDF_ReadValidator::Session read_session(GetValidator());
+  CPDF_ReadValidator::ScopedSession read_session(GetValidator());
   const FX_FILESIZE saved_pos = GetPos();
-  bool is_number = false;
-  ByteString word = GetNextWord(&is_number);
-  if (!is_number || word.IsEmpty()) {
-    SetPos(saved_pos);
-    return nullptr;
-  }
-  const uint32_t parser_objnum = FXSYS_atoui(word.c_str());
 
-  word = GetNextWord(&is_number);
-  if (!is_number || word.IsEmpty()) {
+  WordResult objnum_word_result = GetNextWord();
+  if (!objnum_word_result.is_number || objnum_word_result.word.IsEmpty()) {
     SetPos(saved_pos);
     return nullptr;
   }
-  const uint32_t parser_gennum = FXSYS_atoui(word.c_str());
+  const uint32_t parser_objnum = FXSYS_atoui(objnum_word_result.word.c_str());
+
+  WordResult gennum_word_result = GetNextWord();
+  const ByteString& gennum_word = gennum_word_result.word;
+  if (!gennum_word_result.is_number || gennum_word.IsEmpty()) {
+    SetPos(saved_pos);
+    return nullptr;
+  }
+  const uint32_t parser_gennum = FXSYS_atoui(gennum_word.c_str());
 
   if (GetKeyword() != "obj") {
     SetPos(saved_pos);
@@ -633,7 +708,8 @@
 
 RetainPtr<CPDF_Stream> CPDF_SyntaxParser::ReadStream(
     RetainPtr<CPDF_Dictionary> pDict) {
-  const CPDF_Number* pLenObj = ToNumber(pDict->GetDirectObjectFor("Length"));
+  RetainPtr<const CPDF_Number> pLenObj =
+      ToNumber(pDict->GetDirectObjectFor("Length"));
   FX_FILESIZE len = pLenObj ? pLenObj->GetInteger() : -1;
 
   // Locate the start of stream.
@@ -647,7 +723,7 @@
       len = -1;
   }
 
-  RetainPtr<IFX_SeekableReadStream> data;
+  RetainPtr<IFX_SeekableReadStream> substream;
   if (len > 0) {
     // Check data availability first to allow the Validator to request data
     // smoothly, without jumps.
@@ -656,7 +732,7 @@
       return nullptr;
     }
 
-    data = pdfium::MakeRetain<ReadableSubStream>(
+    substream = pdfium::MakeRetain<ReadableSubStream>(
         GetValidator(), m_HeaderOffset + GetPos(), len);
     SetPos(GetPos() + len);
   }
@@ -667,10 +743,10 @@
   // Note, we allow zero length streams as we need to pass them through when we
   // are importing pages into a new document.
   if (len >= 0) {
-    const CPDF_ReadValidator::Session read_session(GetValidator());
+    CPDF_ReadValidator::ScopedSession read_session(GetValidator());
     m_Pos += ReadEOLMarkers(GetPos());
     memset(m_WordBuffer, 0, kEndStreamStr.GetLength() + 1);
-    GetNextWordInternal(nullptr);
+    GetNextWordInternal();
     if (GetValidator()->has_read_problems())
       return nullptr;
 
@@ -679,7 +755,7 @@
     // specified length, it signals the end of stream.
     if (memcmp(m_WordBuffer, kEndStreamStr.raw_str(),
                kEndStreamStr.GetLength()) != 0) {
-      data.Reset();
+      substream.Reset();
       len = -1;
       SetPos(streamStartPos);
     }
@@ -693,7 +769,7 @@
       return nullptr;
 
     len = streamEndPos - streamStartPos;
-    ASSERT(len >= 0);
+    DCHECK_GE(len, 0);
     if (len > 0) {
       SetPos(streamStartPos);
       // Check data availability first to allow the Validator to request data
@@ -703,22 +779,41 @@
         return nullptr;
       }
 
-      data = pdfium::MakeRetain<ReadableSubStream>(
+      substream = pdfium::MakeRetain<ReadableSubStream>(
           GetValidator(), m_HeaderOffset + GetPos(), len);
       SetPos(GetPos() + len);
     }
   }
 
-  auto pStream = pdfium::MakeRetain<CPDF_Stream>();
-  if (data) {
-    pStream->InitStreamFromFile(data, std::move(pDict));
+  RetainPtr<CPDF_Stream> pStream;
+  if (substream) {
+    // It is unclear from CPDF_SyntaxParser's perspective what object
+    // `substream` is ultimately holding references to. To avoid unexpectedly
+    // changing object lifetimes by handing `substream` to `pStream`, make a
+    // copy of the data here.
+    FixedUninitDataVector<uint8_t> data(substream->GetSize());
+    bool did_read = substream->ReadBlockAtOffset(data.writable_span(), 0);
+    CHECK(did_read);
+    auto data_as_stream =
+        pdfium::MakeRetain<CFX_ReadOnlyVectorStream>(std::move(data));
+
+    pStream = pdfium::MakeRetain<CPDF_Stream>();
+    pStream->InitStreamFromFile(std::move(data_as_stream), std::move(pDict));
   } else {
     DCHECK(!len);
-    pStream->InitStream({}, std::move(pDict));  // Empty stream
+    pStream = pdfium::MakeRetain<CPDF_Stream>(std::move(pDict));
   }
   const FX_FILESIZE end_stream_offset = GetPos();
   memset(m_WordBuffer, 0, kEndObjStr.GetLength() + 1);
-  GetNextWordInternal(nullptr);
+  GetNextWordInternal();
+
+  // Allow whitespace after endstream and before a newline.
+  unsigned char ch = 0;
+  while (GetNextChar(ch)) {
+    if (!PDFCharIsWhitespace(ch) || PDFCharIsLineEnding(ch))
+      break;
+  }
+  SetPos(GetPos() - 1);
 
   int numMarkers = ReadEOLMarkers(GetPos());
   if (m_WordSize == static_cast<unsigned int>(kEndObjStr.GetLength()) &&
@@ -730,15 +825,17 @@
 }
 
 uint32_t CPDF_SyntaxParser::GetDirectNum() {
-  bool bIsNumber;
-  GetNextWordInternal(&bIsNumber);
-  if (!bIsNumber)
+  if (GetNextWordInternal() != WordType::kNumber)
     return 0;
 
   m_WordBuffer[m_WordSize] = 0;
   return FXSYS_atoui(reinterpret_cast<const char*>(m_WordBuffer));
 }
 
+RetainPtr<CPDF_ReadValidator> CPDF_SyntaxParser::GetValidator() const {
+  return m_pFileAccess;
+}
+
 bool CPDF_SyntaxParser::IsWholeWord(FX_FILESIZE startpos,
                                     FX_FILESIZE limit,
                                     ByteStringView tag,
@@ -750,8 +847,8 @@
                      !PDFCharIsWhitespace(tag[taglen - 1]);
 
   uint8_t ch;
-  if (bCheckRight && startpos + (int32_t)taglen <= limit &&
-      GetCharAt(startpos + (int32_t)taglen, ch)) {
+  if (bCheckRight && startpos + static_cast<int32_t>(taglen) <= limit &&
+      GetCharAt(startpos + static_cast<int32_t>(taglen), ch)) {
     if (PDFCharIsNumeric(ch) || PDFCharIsOther(ch) ||
         (checkKeyword && PDFCharIsDelimiter(ch))) {
       return false;
@@ -775,7 +872,7 @@
 
   FX_FILESIZE pos = m_Pos;
   int32_t offset = taglen - 1;
-  while (1) {
+  while (true) {
     if (limit && pos <= m_Pos - limit)
       return false;
 
@@ -804,10 +901,10 @@
 FX_FILESIZE CPDF_SyntaxParser::FindTag(ByteStringView tag) {
   const FX_FILESIZE startpos = GetPos();
   const int32_t taglen = tag.GetLength();
-  ASSERT(taglen > 0);
+  DCHECK_GT(taglen, 0);
 
   int32_t match = 0;
-  while (1) {
+  while (true) {
     uint8_t ch;
     if (!GetNextChar(ch))
       return -1;
@@ -820,7 +917,6 @@
       match = ch == tag[0] ? 1 : 0;
     }
   }
-  return -1;
 }
 
 bool CPDF_SyntaxParser::IsPositionRead(FX_FILESIZE pos) const {
diff --git a/core/fpdfapi/parser/cpdf_syntax_parser.h b/core/fpdfapi/parser/cpdf_syntax_parser.h
index d36aedf..d3f5747 100644
--- a/core/fpdfapi/parser/cpdf_syntax_parser.h
+++ b/core/fpdfapi/parser/cpdf_syntax_parser.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,20 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_SYNTAX_PARSER_H_
 #define CORE_FPDFAPI_PARSER_CPDF_SYNTAX_PARSER_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
 #include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_types.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/string_pool_template.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxcrt/weak_ptr.h"
+#include "third_party/base/containers/span.h"
 
-class CPDF_CryptoHandler;
 class CPDF_Dictionary;
 class CPDF_IndirectObjectHolder;
 class CPDF_Object;
@@ -24,15 +30,19 @@
 
 class CPDF_SyntaxParser {
  public:
-  enum class ParseType { kStrict, kLoose };
+  enum class ParseType : bool { kStrict, kLoose };
+
+  struct WordResult {
+    ByteString word;
+    bool is_number;
+  };
 
   static std::unique_ptr<CPDF_SyntaxParser> CreateForTesting(
-      const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
+      RetainPtr<IFX_SeekableReadStream> pFileAccess,
       FX_FILESIZE HeaderOffset);
 
-  explicit CPDF_SyntaxParser(
-      const RetainPtr<IFX_SeekableReadStream>& pFileAccess);
-  CPDF_SyntaxParser(const RetainPtr<CPDF_ReadValidator>& pValidator,
+  explicit CPDF_SyntaxParser(RetainPtr<IFX_SeekableReadStream> pFileAccess);
+  CPDF_SyntaxParser(RetainPtr<CPDF_ReadValidator> pValidator,
                     FX_FILESIZE HeaderOffset);
   ~CPDF_SyntaxParser();
 
@@ -44,23 +54,21 @@
   void SetPos(FX_FILESIZE pos);
 
   RetainPtr<CPDF_Object> GetObjectBody(CPDF_IndirectObjectHolder* pObjList);
-
   RetainPtr<CPDF_Object> GetIndirectObject(CPDF_IndirectObjectHolder* pObjList,
                                            ParseType parse_type);
 
   ByteString GetKeyword();
   void ToNextLine();
   void ToNextWord();
+  void RecordingToNextWord();
   bool BackwardsSearchToWord(ByteStringView word, FX_FILESIZE limit);
   FX_FILESIZE FindTag(ByteStringView tag);
-  bool ReadBlock(uint8_t* pBuf, uint32_t size);
+  bool ReadBlock(pdfium::span<uint8_t> buffer);
   bool GetCharAt(FX_FILESIZE pos, uint8_t& ch);
-  ByteString GetNextWord(bool* bIsNumber);
-  ByteString PeekNextWord(bool* bIsNumber);
+  WordResult GetNextWord();
+  ByteString PeekNextWord();
 
-  const RetainPtr<CPDF_ReadValidator>& GetValidator() const {
-    return m_pFileAccess;
-  }
+  RetainPtr<CPDF_ReadValidator> GetValidator() const;
   uint32_t GetDirectNum();
   bool GetNextChar(uint8_t& ch);
 
@@ -75,16 +83,22 @@
   ByteString ReadString();
   ByteString ReadHexString();
 
+  void SetTrailerEnds(std::vector<unsigned int>* trailer_ends) {
+    m_TrailerEnds = trailer_ends;
+  }
+
  private:
+  enum class WordType : bool { kWord, kNumber };
+
   friend class CPDF_DataAvail;
   friend class cpdf_syntax_parser_ReadHexString_Test;
 
-  static const int kParserMaxRecursionDepth = 64;
+  static constexpr int kParserMaxRecursionDepth = 64;
   static int s_CurrentRecursionDepth;
 
   bool ReadBlockAt(FX_FILESIZE read_pos);
   bool GetCharAtBackward(FX_FILESIZE pos, uint8_t* ch);
-  void GetNextWordInternal(bool* bIsNumber);
+  WordType GetNextWordInternal();
   bool IsWholeWord(FX_FILESIZE startpos,
                    FX_FILESIZE limit,
                    ByteStringView tag,
@@ -109,11 +123,14 @@
   const FX_FILESIZE m_FileLen;
   FX_FILESIZE m_Pos = 0;
   WeakPtr<ByteStringPool> m_pPool;
-  std::vector<uint8_t> m_pFileBuf;
+  DataVector<uint8_t> m_pFileBuf;
   FX_FILESIZE m_BufOffset = 0;
   uint32_t m_WordSize = 0;
-  uint8_t m_WordBuffer[257];
+  uint8_t m_WordBuffer[257] = {};
   uint32_t m_ReadBufferSize = CPDF_Stream::kFileBufSize;
+
+  // The syntax parser records traversed trailer end byte offsets here.
+  UnownedPtr<std::vector<unsigned int>> m_TrailerEnds;
 };
 
 #endif  // CORE_FPDFAPI_PARSER_CPDF_SYNTAX_PARSER_H_
diff --git a/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp b/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp
index 53f0a17..94ab977 100644
--- a/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp
@@ -1,24 +1,23 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include <limits>
-#include <string>
 
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fpdfapi/parser/cpdf_parser.h"
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
 #include "core/fxcrt/fx_extension.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/utils/path_service.h"
 
-TEST(cpdf_syntax_parser, ReadHexString) {
+TEST(SyntaxParserTest, ReadHexString) {
   {
     // Empty string.
     static const uint8_t data[] = "";
-    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-        pdfium::make_span(data, 0)));
+    CPDF_SyntaxParser parser(
+        pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(data, 0)));
     EXPECT_EQ("", parser.ReadHexString());
     EXPECT_EQ(0, parser.GetPos());
   }
@@ -26,8 +25,8 @@
   {
     // Blank string.
     static const uint8_t data[] = "  ";
-    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-        pdfium::make_span(data, 2)));
+    CPDF_SyntaxParser parser(
+        pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(data, 2)));
     EXPECT_EQ("", parser.ReadHexString());
     EXPECT_EQ(2, parser.GetPos());
   }
@@ -35,8 +34,8 @@
   {
     // Skips unknown characters.
     static const uint8_t data[] = "z12b";
-    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-        pdfium::make_span(data, 4)));
+    CPDF_SyntaxParser parser(
+        pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(data, 4)));
     EXPECT_EQ("\x12\xb0", parser.ReadHexString());
     EXPECT_EQ(4, parser.GetPos());
   }
@@ -44,7 +43,7 @@
   {
     // Skips unknown characters.
     static const uint8_t data[] = "*<&*#$^&@1";
-    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
         pdfium::make_span(data, 10)));
     EXPECT_EQ("\x10", parser.ReadHexString());
     EXPECT_EQ(10, parser.GetPos());
@@ -53,8 +52,8 @@
   {
     // Skips unknown characters.
     static const uint8_t data[] = "\x80zab";
-    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-        pdfium::make_span(data, 4)));
+    CPDF_SyntaxParser parser(
+        pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(data, 4)));
     EXPECT_EQ("\xab", parser.ReadHexString());
     EXPECT_EQ(4, parser.GetPos());
   }
@@ -62,8 +61,8 @@
   {
     // Skips unknown characters.
     static const uint8_t data[] = "\xffzab";
-    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-        pdfium::make_span(data, 4)));
+    CPDF_SyntaxParser parser(
+        pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(data, 4)));
     EXPECT_EQ("\xab", parser.ReadHexString());
     EXPECT_EQ(4, parser.GetPos());
   }
@@ -71,8 +70,8 @@
   {
     // Regular conversion.
     static const uint8_t data[] = "1A2b>abcd";
-    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-        pdfium::make_span(data, 9)));
+    CPDF_SyntaxParser parser(
+        pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(data, 9)));
     EXPECT_EQ("\x1a\x2b", parser.ReadHexString());
     EXPECT_EQ(5, parser.GetPos());
   }
@@ -80,17 +79,14 @@
   {
     // Position out of bounds.
     static const uint8_t data[] = "12ab>";
-    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-        pdfium::make_span(data, 5)));
+    CPDF_SyntaxParser parser(
+        pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(data, 5)));
     parser.SetPos(5);
     EXPECT_EQ("", parser.ReadHexString());
 
     parser.SetPos(6);
     EXPECT_EQ("", parser.ReadHexString());
 
-    parser.SetPos(-1);
-    EXPECT_EQ("", parser.ReadHexString());
-
     parser.SetPos(std::numeric_limits<FX_FILESIZE>::max());
     EXPECT_EQ("", parser.ReadHexString());
 
@@ -102,8 +98,8 @@
   {
     // Missing ending >.
     static const uint8_t data[] = "1A2b";
-    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-        pdfium::make_span(data, 4)));
+    CPDF_SyntaxParser parser(
+        pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(data, 4)));
     EXPECT_EQ("\x1a\x2b", parser.ReadHexString());
     EXPECT_EQ(4, parser.GetPos());
   }
@@ -111,8 +107,8 @@
   {
     // Missing ending >.
     static const uint8_t data[] = "12abz";
-    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-        pdfium::make_span(data, 5)));
+    CPDF_SyntaxParser parser(
+        pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(data, 5)));
     EXPECT_EQ("\x12\xab", parser.ReadHexString());
     EXPECT_EQ(5, parser.GetPos());
   }
@@ -120,8 +116,8 @@
   {
     // Uneven number of bytes.
     static const uint8_t data[] = "1A2>asdf";
-    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-        pdfium::make_span(data, 8)));
+    CPDF_SyntaxParser parser(
+        pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(data, 8)));
     EXPECT_EQ("\x1a\x20", parser.ReadHexString());
     EXPECT_EQ(4, parser.GetPos());
   }
@@ -129,8 +125,8 @@
   {
     // Uneven number of bytes.
     static const uint8_t data[] = "1A2zasdf";
-    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-        pdfium::make_span(data, 8)));
+    CPDF_SyntaxParser parser(
+        pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(data, 8)));
     EXPECT_EQ("\x1a\x2a\xdf", parser.ReadHexString());
     EXPECT_EQ(8, parser.GetPos());
   }
@@ -138,25 +134,25 @@
   {
     // Just ending character.
     static const uint8_t data[] = ">";
-    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-        pdfium::make_span(data, 1)));
+    CPDF_SyntaxParser parser(
+        pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(data, 1)));
     EXPECT_EQ("", parser.ReadHexString());
     EXPECT_EQ(1, parser.GetPos());
   }
 }
 
-TEST(cpdf_syntax_parser, GetInvalidReference) {
+TEST(SyntaxParserTest, GetInvalidReference) {
   // Data with a reference with number CPDF_Object::kInvalidObjNum
   static const uint8_t data[] = "4294967295 0 R";
-  CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-      pdfium::make_span(data, 14)));
+  CPDF_SyntaxParser parser(
+      pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(data, 14)));
   RetainPtr<CPDF_Object> ref = parser.GetObjectBody(nullptr);
   EXPECT_FALSE(ref);
 }
 
-TEST(cpdf_syntax_parser, PeekNextWord) {
+TEST(SyntaxParserTest, PeekNextWord) {
   static const uint8_t data[] = "    WORD ";
-  CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(data));
-  EXPECT_EQ("WORD", parser.PeekNextWord(nullptr));
-  EXPECT_EQ("WORD", parser.GetNextWord(nullptr));
+  CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(data));
+  EXPECT_EQ("WORD", parser.PeekNextWord());
+  EXPECT_EQ("WORD", parser.GetNextWord().word);
 }
diff --git a/core/fpdfapi/parser/cpdf_test_document.cpp b/core/fpdfapi/parser/cpdf_test_document.cpp
new file mode 100644
index 0000000..d13103f
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_test_document.cpp
@@ -0,0 +1,19 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/parser/cpdf_test_document.h"
+
+#include <memory>
+#include <utility>
+
+#include "core/fpdfapi/page/cpdf_docpagedata.h"
+#include "core/fpdfapi/render/cpdf_docrenderdata.h"
+
+CPDF_TestDocument::CPDF_TestDocument()
+    : CPDF_Document(std::make_unique<CPDF_DocRenderData>(),
+                    std::make_unique<CPDF_DocPageData>()) {}
+
+void CPDF_TestDocument::SetRoot(RetainPtr<CPDF_Dictionary> root) {
+  SetRootForTesting(std::move(root));
+}
diff --git a/core/fpdfapi/parser/cpdf_test_document.h b/core/fpdfapi/parser/cpdf_test_document.h
new file mode 100644
index 0000000..f50dc8e
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_test_document.h
@@ -0,0 +1,20 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FPDFAPI_PARSER_CPDF_TEST_DOCUMENT_H_
+#define CORE_FPDFAPI_PARSER_CPDF_TEST_DOCUMENT_H_
+
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fxcrt/retain_ptr.h"
+
+class CPDF_Dictionary;
+
+class CPDF_TestDocument : public CPDF_Document {
+ public:
+  CPDF_TestDocument();
+
+  void SetRoot(RetainPtr<CPDF_Dictionary> root);
+};
+
+#endif  // CORE_FPDFAPI_PARSER_CPDF_TEST_DOCUMENT_H_
diff --git a/core/fpdfapi/parser/fpdf_parser_decode.cpp b/core/fpdfapi/parser/fpdf_parser_decode.cpp
index 7074061..1fbee03 100644
--- a/core/fpdfapi/parser/fpdf_parser_decode.cpp
+++ b/core/fpdfapi/parser/fpdf_parser_decode.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,25 +6,27 @@
 
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 
+#include <ctype.h>
 #include <limits.h>
+#include <stddef.h>
 
 #include <algorithm>
-#include <sstream>
 #include <utility>
-#include <vector>
 
+#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"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcodec/fax/faxmodule.h"
 #include "core/fxcodec/flate/flatemodule.h"
-#include "core/fxcodec/fx_codec.h"
 #include "core/fxcodec/scanlinedecoder.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/numerics/safe_math.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxcrt/utf16.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
 
 namespace {
 
@@ -42,7 +44,7 @@
   if (Colors < 0 || BitsPerComponent < 0 || Columns < 0)
     return false;
 
-  pdfium::base::CheckedNumeric<int> check = Columns;
+  FX_SAFE_INT32 check = Columns;
   check *= Colors;
   check *= BitsPerComponent;
   if (!check.IsValid())
@@ -57,7 +59,7 @@
 
 }  // namespace
 
-const uint16_t PDFDocEncoding[256] = {
+const uint16_t kPDFDocEncoding[256] = {
     0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008,
     0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011,
     0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x02d8, 0x02c7, 0x02c6,
@@ -94,8 +96,10 @@
     return true;
 
   for (size_t i = 0; i < count; ++i) {
-    if (!pDecoders->GetObjectAt(i)->IsName())
+    RetainPtr<const CPDF_Object> object = pDecoders->GetDirectObjectAt(i);
+    if (!object || !object->IsName()) {
       return false;
+    }
   }
 
   if (count == 1)
@@ -106,7 +110,7 @@
       "FlateDecode",    "Fl",  "LZWDecode",       "LZW", "ASCII85Decode", "A85",
       "ASCIIHexDecode", "AHx", "RunLengthDecode", "RL"};
   for (size_t i = 0; i < count - 1; ++i) {
-    if (!pdfium::ContainsValue(kValidDecoders, pDecoders->GetStringAt(i)))
+    if (!pdfium::Contains(kValidDecoders, pDecoders->GetByteStringAt(i)))
       return false;
   }
   return true;
@@ -219,7 +223,7 @@
       ++i;
       break;
     }
-    if (!std::isxdigit(ch))
+    if (!isxdigit(ch))
       continue;
 
     int digit = FXSYS_HexCharToInt(ch);
@@ -273,18 +277,17 @@
       if (buf_left < copy_len) {
         uint32_t delta = copy_len - buf_left;
         copy_len = buf_left;
-        memset(&dest_span[dest_count + copy_len], '\0', delta);
+        fxcrt::spanclr(dest_span.subspan(dest_count + copy_len, delta));
       }
       auto copy_span = src_span.subspan(i + 1, copy_len);
-      memcpy(&dest_span[dest_count], copy_span.data(), copy_span.size());
+      fxcrt::spancpy(dest_span.subspan(dest_count), copy_span);
       dest_count += src_span[i] + 1;
       i += src_span[i] + 2;
     } else {
-      int fill = 0;
-      if (i < src_span.size() - 1)
-        fill = src_span[i + 1];
-      memset(&dest_span[dest_count], fill, 257 - src_span[i]);
-      dest_count += 257 - src_span[i];
+      const uint8_t fill = i < src_span.size() - 1 ? src_span[i + 1] : 0;
+      const size_t fill_size = 257 - src_span[i];
+      fxcrt::spanset(dest_span.subspan(dest_count, fill_size), fill);
+      dest_count += fill_size;
       i += 2;
     }
   }
@@ -365,43 +368,46 @@
                                        estimated_size, dest_buf, dest_size);
 }
 
-Optional<std::vector<std::pair<ByteString, const CPDF_Object*>>>
-GetDecoderArray(const CPDF_Dictionary* pDict) {
-  const CPDF_Object* pDecoder = pDict->GetDirectObjectFor("Filter");
-  if (!pDecoder || (!pDecoder->IsArray() && !pDecoder->IsName()))
-    return {};
+absl::optional<DecoderArray> GetDecoderArray(
+    RetainPtr<const CPDF_Dictionary> pDict) {
+  RetainPtr<const CPDF_Object> pFilter = pDict->GetDirectObjectFor("Filter");
+  if (!pFilter)
+    return DecoderArray();
 
-  const CPDF_Object* pParams =
+  if (!pFilter->IsArray() && !pFilter->IsName())
+    return absl::nullopt;
+
+  RetainPtr<const CPDF_Object> pParams =
       pDict->GetDirectObjectFor(pdfium::stream::kDecodeParms);
 
-  std::vector<std::pair<ByteString, const CPDF_Object*>> decoder_array;
-  if (const CPDF_Array* pDecoders = pDecoder->AsArray()) {
+  DecoderArray decoder_array;
+  if (const CPDF_Array* pDecoders = pFilter->AsArray()) {
     if (!ValidateDecoderPipeline(pDecoders))
-      return {};
+      return absl::nullopt;
 
-    const CPDF_Array* pParamsArray = ToArray(pParams);
+    RetainPtr<const CPDF_Array> pParamsArray = ToArray(pParams);
     for (size_t i = 0; i < pDecoders->size(); ++i) {
-      decoder_array.push_back(
-          {pDecoders->GetStringAt(i),
-           pParamsArray ? pParamsArray->GetDictAt(i) : nullptr});
+      decoder_array.emplace_back(
+          pDecoders->GetByteStringAt(i),
+          pParamsArray ? pParamsArray->GetDictAt(i) : nullptr);
     }
   } else {
-    decoder_array.push_back(
-        {pDecoder->GetString(), pParams ? pParams->GetDict() : nullptr});
+    DCHECK(pFilter->IsName());
+    decoder_array.emplace_back(pFilter->GetString(),
+                               pParams ? pParams->GetDict() : nullptr);
   }
 
   return decoder_array;
 }
 
-bool PDF_DataDecode(
-    pdfium::span<const uint8_t> src_span,
-    uint32_t last_estimated_size,
-    bool bImageAcc,
-    const std::vector<std::pair<ByteString, const CPDF_Object*>>& decoder_array,
-    std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-    uint32_t* dest_size,
-    ByteString* ImageEncoding,
-    RetainPtr<const CPDF_Dictionary>* pImageParams) {
+bool PDF_DataDecode(pdfium::span<const uint8_t> src_span,
+                    uint32_t last_estimated_size,
+                    bool bImageAcc,
+                    const DecoderArray& decoder_array,
+                    std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                    uint32_t* dest_size,
+                    ByteString* ImageEncoding,
+                    RetainPtr<const CPDF_Dictionary>* pImageParams) {
   std::unique_ptr<uint8_t, FxFreeDeleter> result;
   // May be changed to point to |result| in the for-loop below. So put it below
   // |result| and let it get destroyed first.
@@ -410,7 +416,8 @@
   for (size_t i = 0; i < nSize; ++i) {
     int estimated_size = i == nSize - 1 ? last_estimated_size : 0;
     ByteString decoder = decoder_array[i].first;
-    const CPDF_Dictionary* pParam = ToDictionary(decoder_array[i].second);
+    RetainPtr<const CPDF_Dictionary> pParam =
+        ToDictionary(decoder_array[i].second);
     std::unique_ptr<uint8_t, FxFreeDeleter> new_buf;
     uint32_t new_size = 0xFFFFFFFF;
     uint32_t offset = FX_INVALID_OFFSET;
@@ -421,7 +428,7 @@
         *ImageEncoding = "FlateDecode";
         *dest_buf = std::move(result);
         *dest_size = last_span.size();
-        pImageParams->Reset(pParam);
+        *pImageParams = std::move(pParam);
         return true;
       }
       offset = FlateOrLZWDecode(false, last_span, pParam, estimated_size,
@@ -438,7 +445,7 @@
         *ImageEncoding = "RunLengthDecode";
         *dest_buf = std::move(result);
         *dest_size = last_span.size();
-        pImageParams->Reset(pParam);
+        *pImageParams = std::move(pParam);
         return true;
       }
       offset = RunLengthDecode(last_span, &new_buf, &new_size);
@@ -449,7 +456,7 @@
       else if (decoder == "CCF")
         decoder = "CCITTFaxDecode";
       *ImageEncoding = std::move(decoder);
-      pImageParams->Reset(pParam);
+      *pImageParams = std::move(pParam);
       *dest_buf = std::move(result);
       *dest_size = last_span.size();
       return true;
@@ -468,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))) {
@@ -481,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);
 
@@ -501,19 +512,45 @@
           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)
-      dest_buf[i] = PDFDocEncoding[span[i]];
+      dest_buf[i] = kPDFDocEncoding[span[i]];
     dest_pos = span.size();
   }
   result.ReleaseBuffer(dest_pos);
   return result;
 }
 
-ByteString PDF_EncodeText(const WideString& str) {
+ByteString PDF_EncodeText(WideStringView str) {
   size_t i = 0;
   size_t len = str.GetLength();
   ByteString result;
@@ -522,7 +559,7 @@
     for (i = 0; i < len; ++i) {
       int code;
       for (code = 0; code < 256; ++code) {
-        if (PDFDocEncoding[code] == str[i])
+        if (kPDFDocEncoding[code] == str[i])
           break;
       }
       if (code == 256)
@@ -541,59 +578,76 @@
   }
 
   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;
 }
 
-ByteString PDF_EncodeString(const ByteString& src, bool bHex) {
-  std::ostringstream result;
-  int srclen = src.GetLength();
-  if (bHex) {
-    result << '<';
-    for (int i = 0; i < srclen; ++i) {
-      char buf[2];
-      FXSYS_IntToTwoHexChars(src[i], buf);
-      result << buf[0];
-      result << buf[1];
-    }
-    result << '>';
-    return ByteString(result);
-  }
-  result << '(';
-  for (int i = 0; i < srclen; ++i) {
+ByteString PDF_EncodeString(ByteStringView src) {
+  ByteString result;
+  result.Reserve(src.GetLength() + 2);
+  result += '(';
+  for (size_t i = 0; i < src.GetLength(); ++i) {
     uint8_t ch = src[i];
     if (ch == 0x0a) {
-      result << "\\n";
+      result += "\\n";
       continue;
     }
     if (ch == 0x0d) {
-      result << "\\r";
+      result += "\\r";
       continue;
     }
     if (ch == ')' || ch == '\\' || ch == '(')
-      result << '\\';
-    result << static_cast<char>(ch);
+      result += '\\';
+    result += static_cast<char>(ch);
   }
-  result << ')';
-  return ByteString(result);
+  result += ')';
+  return result;
 }
 
-bool FlateEncode(pdfium::span<const uint8_t> src_span,
-                 std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-                 uint32_t* dest_size) {
-  return FlateModule::Encode(src_span.data(), src_span.size(), dest_buf,
-                             dest_size);
+ByteString PDF_HexEncodeString(ByteStringView src) {
+  ByteString result;
+  result.Reserve(2 * src.GetLength() + 2);
+  result += '<';
+  for (size_t i = 0; i < src.GetLength(); ++i) {
+    char buf[2];
+    FXSYS_IntToTwoHexChars(src[i], buf);
+    result += buf[0];
+    result += buf[1];
+  }
+  result += '>';
+  return result;
+}
+
+DataVector<uint8_t> FlateEncode(pdfium::span<const uint8_t> src_span) {
+  return FlateModule::Encode(src_span);
 }
 
 uint32_t FlateDecode(pdfium::span<const uint8_t> src_span,
diff --git a/core/fpdfapi/parser/fpdf_parser_decode.h b/core/fpdfapi/parser/fpdf_parser_decode.h
index ebb64e6..6e146b5 100644
--- a/core/fpdfapi/parser/fpdf_parser_decode.h
+++ b/core/fpdfapi/parser/fpdf_parser_decode.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,18 @@
 #ifndef CORE_FPDFAPI_PARSER_FPDF_PARSER_DECODE_H_
 #define CORE_FPDFAPI_PARSER_FPDF_PARSER_DECODE_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <utility>
 #include <vector>
 
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/optional.h"
-#include "third_party/base/span.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Array;
 class CPDF_Dictionary;
@@ -26,13 +29,14 @@
 }
 
 // Indexed by 8-bit char code, contains unicode code points.
-extern const uint16_t PDFDocEncoding[256];
+extern const uint16_t kPDFDocEncoding[256];
 
 bool ValidateDecoderPipeline(const CPDF_Array* pDecoders);
 
-ByteString PDF_EncodeString(const ByteString& src, bool bHex);
+ByteString PDF_EncodeString(ByteStringView src);
+ByteString PDF_HexEncodeString(ByteStringView src);
 WideString PDF_DecodeText(pdfium::span<const uint8_t> span);
-ByteString PDF_EncodeText(const WideString& str);
+ByteString PDF_EncodeText(WideStringView str);
 
 std::unique_ptr<fxcodec::ScanlineDecoder> CreateFaxDecoder(
     pdfium::span<const uint8_t> src_span,
@@ -48,9 +52,7 @@
     int bpc,
     const CPDF_Dictionary* pParams);
 
-bool FlateEncode(pdfium::span<const uint8_t> src_span,
-                 std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-                 uint32_t* dest_size);
+DataVector<uint8_t> FlateEncode(pdfium::span<const uint8_t> src_span);
 
 uint32_t FlateDecode(pdfium::span<const uint8_t> src_span,
                      std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
@@ -75,17 +77,23 @@
                           std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
                           uint32_t* dest_size);
 
-Optional<std::vector<std::pair<ByteString, const CPDF_Object*>>>
-GetDecoderArray(const CPDF_Dictionary* pDict);
+// Returns absl::nullopt if the filter in |pDict| is the wrong type or an
+// invalid decoder pipeline.
+// Returns an empty vector if there is no filter, or if the filter is an empty
+// array.
+// Otherwise, returns a vector of decoders.
+using DecoderArray =
+    std::vector<std::pair<ByteString, RetainPtr<const CPDF_Object>>>;
+absl::optional<DecoderArray> GetDecoderArray(
+    RetainPtr<const CPDF_Dictionary> pDict);
 
-bool PDF_DataDecode(
-    pdfium::span<const uint8_t> src_span,
-    uint32_t estimated_size,
-    bool bImageAcc,
-    const std::vector<std::pair<ByteString, const CPDF_Object*>>& decoder_array,
-    std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-    uint32_t* dest_size,
-    ByteString* ImageEncoding,
-    RetainPtr<const CPDF_Dictionary>* pImageParams);
+bool PDF_DataDecode(pdfium::span<const uint8_t> src_span,
+                    uint32_t estimated_size,
+                    bool bImageAcc,
+                    const DecoderArray& decoder_array,
+                    std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                    uint32_t* dest_size,
+                    ByteString* ImageEncoding,
+                    RetainPtr<const CPDF_Dictionary>* pImageParams);
 
 #endif  // CORE_FPDFAPI_PARSER_FPDF_PARSER_DECODE_H_
diff --git a/core/fpdfapi/parser/fpdf_parser_decode_embeddertest.cpp b/core/fpdfapi/parser/fpdf_parser_decode_embeddertest.cpp
index 32a34db..e5b8b05 100644
--- a/core/fpdfapi/parser/fpdf_parser_decode_embeddertest.cpp
+++ b/core/fpdfapi/parser/fpdf_parser_decode_embeddertest.cpp
@@ -1,136 +1,48 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <cstring>
-#include <memory>
-#include <string>
-
 #include "build/build_config.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
 #include "public/cpp/fpdf_scopers.h"
 #include "testing/embedder_test.h"
+#include "testing/embedder_test_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/test_support.h"
 
 using FPDFParserDecodeEmbedderTest = EmbedderTest;
+using pdfium::kBlankPage612By792Checksum;
 
-// NOTE: python's zlib.compress() and zlib.decompress() may be useful for
-// external validation of the FlateEncode/FlateDecode test cases.
-
-TEST_F(FPDFParserDecodeEmbedderTest, FlateEncode) {
-  static const pdfium::StrFuncTestData flate_encode_cases[] = {
-      STR_IN_OUT_CASE("", "\x78\x9c\x03\x00\x00\x00\x00\x01"),
-      STR_IN_OUT_CASE(" ", "\x78\x9c\x53\x00\x00\x00\x21\x00\x21"),
-      STR_IN_OUT_CASE("123", "\x78\x9c\x33\x34\x32\x06\x00\01\x2d\x00\x97"),
-      STR_IN_OUT_CASE("\x00\xff", "\x78\x9c\x63\xf8\x0f\x00\x01\x01\x01\x00"),
-      STR_IN_OUT_CASE(
-          "1 0 0 -1 29 763 cm\n0 0 555 735 re\nW n\nq\n0 0 555 734.394 re\n"
-          "W n\nq\n0.8009 0 0 0.8009 0 0 cm\n1 1 1 RG 1 1 1 rg\n/G0 gs\n"
-          "0 0 693 917 re\nf\nQ\nQ\n",
-          "\x78\x9c\x33\x54\x30\x00\x42\x5d\x43\x05\x23\x4b\x05\x73\x33\x63"
-          "\x85\xe4\x5c\x2e\x90\x80\xa9\xa9\xa9\x82\xb9\xb1\xa9\x42\x51\x2a"
-          "\x57\xb8\x42\x1e\x57\x21\x92\xa0\x89\x9e\xb1\xa5\x09\x92\x84\x9e"
-          "\x85\x81\x81\x25\xd8\x14\x24\x26\xd0\x18\x43\x05\x10\x0c\x72\x57"
-          "\x80\x30\x8a\xd2\xb9\xf4\xdd\x0d\x14\xd2\x8b\xc1\x46\x99\x59\x1a"
-          "\x2b\x58\x1a\x9a\x83\x8c\x49\xe3\x0a\x04\x42\x00\x37\x4c\x1b\x42"),
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(flate_encode_cases); ++i) {
-    const pdfium::StrFuncTestData& data = flate_encode_cases[i];
-    std::unique_ptr<uint8_t, FxFreeDeleter> buf;
-    uint32_t buf_size;
-    EXPECT_TRUE(FlateEncode({data.input, data.input_size}, &buf, &buf_size));
-    ASSERT_TRUE(buf);
-    EXPECT_EQ(data.expected_size, buf_size) << " for case " << i;
-    if (data.expected_size != buf_size)
-      continue;
-    EXPECT_EQ(0, memcmp(data.expected, buf.get(), data.expected_size))
-        << " for case " << i;
-  }
-}
-
-TEST_F(FPDFParserDecodeEmbedderTest, FlateDecode) {
-  static const pdfium::DecodeTestData flate_decode_cases[] = {
-      STR_IN_OUT_CASE("", "", 0),
-      STR_IN_OUT_CASE("preposterous nonsense", "", 2),
-      STR_IN_OUT_CASE("\x78\x9c\x03\x00\x00\x00\x00\x01", "", 8),
-      STR_IN_OUT_CASE("\x78\x9c\x53\x00\x00\x00\x21\x00\x21", " ", 9),
-      STR_IN_OUT_CASE("\x78\x9c\x33\x34\x32\x06\x00\01\x2d\x00\x97", "123", 11),
-      STR_IN_OUT_CASE("\x78\x9c\x63\xf8\x0f\x00\x01\x01\x01\x00", "\x00\xff",
-                      10),
-      STR_IN_OUT_CASE(
-          "\x78\x9c\x33\x54\x30\x00\x42\x5d\x43\x05\x23\x4b\x05\x73\x33\x63"
-          "\x85\xe4\x5c\x2e\x90\x80\xa9\xa9\xa9\x82\xb9\xb1\xa9\x42\x51\x2a"
-          "\x57\xb8\x42\x1e\x57\x21\x92\xa0\x89\x9e\xb1\xa5\x09\x92\x84\x9e"
-          "\x85\x81\x81\x25\xd8\x14\x24\x26\xd0\x18\x43\x05\x10\x0c\x72\x57"
-          "\x80\x30\x8a\xd2\xb9\xf4\xdd\x0d\x14\xd2\x8b\xc1\x46\x99\x59\x1a"
-          "\x2b\x58\x1a\x9a\x83\x8c\x49\xe3\x0a\x04\x42\x00\x37\x4c\x1b\x42",
-          "1 0 0 -1 29 763 cm\n0 0 555 735 re\nW n\nq\n0 0 555 734.394 re\n"
-          "W n\nq\n0.8009 0 0 0.8009 0 0 cm\n1 1 1 RG 1 1 1 rg\n/G0 gs\n"
-          "0 0 693 917 re\nf\nQ\nQ\n",
-          96),
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(flate_decode_cases); ++i) {
-    const pdfium::DecodeTestData& data = flate_decode_cases[i];
-    std::unique_ptr<uint8_t, FxFreeDeleter> buf;
-    uint32_t buf_size;
-    EXPECT_EQ(data.processed_size,
-              FlateDecode({data.input, data.input_size}, &buf, &buf_size))
-        << " for case " << i;
-    ASSERT_TRUE(buf);
-    EXPECT_EQ(data.expected_size, buf_size) << " for case " << i;
-    if (data.expected_size != buf_size)
-      continue;
-    EXPECT_EQ(0, memcmp(data.expected, buf.get(), data.expected_size))
-        << " for case " << i;
-  }
-}
-
-TEST_F(FPDFParserDecodeEmbedderTest, Bug_552046) {
+TEST_F(FPDFParserDecodeEmbedderTest, Bug552046) {
   // Tests specifying multiple image filters for a stream. Should not cause a
   // crash when rendered.
-  EXPECT_TRUE(OpenDocument("bug_552046.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_552046.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-  CompareBitmap(bitmap.get(), 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3");
+  CompareBitmap(bitmap.get(), 612, 792, kBlankPage612By792Checksum);
   UnloadPage(page);
 }
 
-TEST_F(FPDFParserDecodeEmbedderTest, Bug_555784) {
+TEST_F(FPDFParserDecodeEmbedderTest, Bug555784) {
   // Tests bad input to the run length decoder that caused a heap overflow.
   // Should not cause a crash when rendered.
-  EXPECT_TRUE(OpenDocument("bug_555784.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_555784.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-  CompareBitmap(bitmap.get(), 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3");
+  CompareBitmap(bitmap.get(), 612, 792, kBlankPage612By792Checksum);
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_Bug_455199 DISABLED_Bug_455199
-#else
-#define MAYBE_Bug_455199 Bug_455199
-#endif
-TEST_F(FPDFParserDecodeEmbedderTest, MAYBE_Bug_455199) {
+TEST_F(FPDFParserDecodeEmbedderTest, Bug455199) {
   // Tests object numbers with a value > 01000000.
   // Should open successfully.
-  EXPECT_TRUE(OpenDocument("bug_455199.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_455199.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-#if defined(OS_MACOSX)
-  const char kExpectedMd5sum[] = "b90475ca64d1348c3bf5e2b77ad9187a";
-#elif defined(OS_WIN)
-  const char kExpectedMd5sum[] = "795b7ce1626931aa06af0fa23b7d80bb";
-#else
-  const char kExpectedMd5sum[] = "2baa4c0e1758deba1b9c908e1fbd04ed";
-#endif
-  CompareBitmap(bitmap.get(), 200, 200, kExpectedMd5sum);
+
+  CompareBitmap(bitmap.get(), 200, 200, pdfium::HelloWorldChecksum());
   UnloadPage(page);
 }
diff --git a/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp b/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp
index 311226f..cd3e82a 100644
--- a/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp
+++ b/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp
@@ -1,17 +1,45 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 
+#include <stddef.h>
+#include <stdint.h>
+
+#include <iterator>
+
 #include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fxcrt/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"
 
-TEST(fpdf_parser_decode, ValidateDecoderPipeline) {
+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) {
   {
     // Empty decoder list is always valid.
     auto decoders = pdfium::MakeRetain<CPDF_Array>();
@@ -20,99 +48,221 @@
   {
     // 1 decoder is almost always valid.
     auto decoders = pdfium::MakeRetain<CPDF_Array>();
-    decoders->AddNew<CPDF_Name>("FlateEncode");
+    decoders->AppendNew<CPDF_Name>("FlateEncode");
     EXPECT_TRUE(ValidateDecoderPipeline(decoders.Get()));
   }
   {
     // 1 decoder is almost always valid, even with an unknown decoder.
     auto decoders = pdfium::MakeRetain<CPDF_Array>();
-    decoders->AddNew<CPDF_Name>("FooBar");
+    decoders->AppendNew<CPDF_Name>("FooBar");
     EXPECT_TRUE(ValidateDecoderPipeline(decoders.Get()));
   }
   {
     // Valid 2 decoder pipeline.
     auto decoders = pdfium::MakeRetain<CPDF_Array>();
-    decoders->AddNew<CPDF_Name>("AHx");
-    decoders->AddNew<CPDF_Name>("LZWDecode");
+    decoders->AppendNew<CPDF_Name>("AHx");
+    decoders->AppendNew<CPDF_Name>("LZWDecode");
     EXPECT_TRUE(ValidateDecoderPipeline(decoders.Get()));
   }
   {
     // Valid 2 decoder pipeline.
     auto decoders = pdfium::MakeRetain<CPDF_Array>();
-    decoders->AddNew<CPDF_Name>("ASCII85Decode");
-    decoders->AddNew<CPDF_Name>("ASCII85Decode");
+    decoders->AppendNew<CPDF_Name>("ASCII85Decode");
+    decoders->AppendNew<CPDF_Name>("ASCII85Decode");
     EXPECT_TRUE(ValidateDecoderPipeline(decoders.Get()));
   }
   {
     // Valid 5 decoder pipeline.
     auto decoders = pdfium::MakeRetain<CPDF_Array>();
-    decoders->AddNew<CPDF_Name>("ASCII85Decode");
-    decoders->AddNew<CPDF_Name>("A85");
-    decoders->AddNew<CPDF_Name>("RunLengthDecode");
-    decoders->AddNew<CPDF_Name>("FlateDecode");
-    decoders->AddNew<CPDF_Name>("RL");
+    decoders->AppendNew<CPDF_Name>("ASCII85Decode");
+    decoders->AppendNew<CPDF_Name>("A85");
+    decoders->AppendNew<CPDF_Name>("RunLengthDecode");
+    decoders->AppendNew<CPDF_Name>("FlateDecode");
+    decoders->AppendNew<CPDF_Name>("RL");
     EXPECT_TRUE(ValidateDecoderPipeline(decoders.Get()));
   }
   {
     // Valid 5 decoder pipeline, with an image decoder at the end.
     auto decoders = pdfium::MakeRetain<CPDF_Array>();
-    decoders->AddNew<CPDF_Name>("RunLengthDecode");
-    decoders->AddNew<CPDF_Name>("ASCII85Decode");
-    decoders->AddNew<CPDF_Name>("FlateDecode");
-    decoders->AddNew<CPDF_Name>("LZW");
-    decoders->AddNew<CPDF_Name>("DCTDecode");
+    decoders->AppendNew<CPDF_Name>("RunLengthDecode");
+    decoders->AppendNew<CPDF_Name>("ASCII85Decode");
+    decoders->AppendNew<CPDF_Name>("FlateDecode");
+    decoders->AppendNew<CPDF_Name>("LZW");
+    decoders->AppendNew<CPDF_Name>("DCTDecode");
     EXPECT_TRUE(ValidateDecoderPipeline(decoders.Get()));
   }
   {
     // Invalid 1 decoder pipeline due to wrong type.
     auto decoders = pdfium::MakeRetain<CPDF_Array>();
-    decoders->AddNew<CPDF_String>("FlateEncode", false);
+    decoders->AppendNew<CPDF_String>("FlateEncode", false);
     EXPECT_FALSE(ValidateDecoderPipeline(decoders.Get()));
   }
   {
     // Invalid 2 decoder pipeline, with 2 image decoders.
     auto decoders = pdfium::MakeRetain<CPDF_Array>();
-    decoders->AddNew<CPDF_Name>("DCTDecode");
-    decoders->AddNew<CPDF_Name>("CCITTFaxDecode");
+    decoders->AppendNew<CPDF_Name>("DCTDecode");
+    decoders->AppendNew<CPDF_Name>("CCITTFaxDecode");
     EXPECT_FALSE(ValidateDecoderPipeline(decoders.Get()));
   }
   {
     // Invalid 2 decoder pipeline, with 1 image decoder at the start.
     auto decoders = pdfium::MakeRetain<CPDF_Array>();
-    decoders->AddNew<CPDF_Name>("DCTDecode");
-    decoders->AddNew<CPDF_Name>("FlateDecode");
+    decoders->AppendNew<CPDF_Name>("DCTDecode");
+    decoders->AppendNew<CPDF_Name>("FlateDecode");
     EXPECT_FALSE(ValidateDecoderPipeline(decoders.Get()));
   }
   {
     // Invalid 2 decoder pipeline due to wrong type.
     auto decoders = pdfium::MakeRetain<CPDF_Array>();
-    decoders->AddNew<CPDF_String>("AHx", false);
-    decoders->AddNew<CPDF_Name>("LZWDecode");
+    decoders->AppendNew<CPDF_String>("AHx", false);
+    decoders->AppendNew<CPDF_Name>("LZWDecode");
     EXPECT_FALSE(ValidateDecoderPipeline(decoders.Get()));
   }
   {
     // Invalid 5 decoder pipeline.
     auto decoders = pdfium::MakeRetain<CPDF_Array>();
-    decoders->AddNew<CPDF_Name>("FlateDecode");
-    decoders->AddNew<CPDF_Name>("FlateDecode");
-    decoders->AddNew<CPDF_Name>("DCTDecode");
-    decoders->AddNew<CPDF_Name>("FlateDecode");
-    decoders->AddNew<CPDF_Name>("FlateDecode");
+    decoders->AppendNew<CPDF_Name>("FlateDecode");
+    decoders->AppendNew<CPDF_Name>("FlateDecode");
+    decoders->AppendNew<CPDF_Name>("DCTDecode");
+    decoders->AppendNew<CPDF_Name>("FlateDecode");
+    decoders->AppendNew<CPDF_Name>("FlateDecode");
     EXPECT_FALSE(ValidateDecoderPipeline(decoders.Get()));
   }
   {
     // Invalid 5 decoder pipeline due to wrong type.
     auto decoders = pdfium::MakeRetain<CPDF_Array>();
-    decoders->AddNew<CPDF_Name>("ASCII85Decode");
-    decoders->AddNew<CPDF_Name>("A85");
-    decoders->AddNew<CPDF_Name>("RunLengthDecode");
-    decoders->AddNew<CPDF_Name>("FlateDecode");
-    decoders->AddNew<CPDF_String>("RL", false);
+    decoders->AppendNew<CPDF_Name>("ASCII85Decode");
+    decoders->AppendNew<CPDF_Name>("A85");
+    decoders->AppendNew<CPDF_Name>("RunLengthDecode");
+    decoders->AppendNew<CPDF_Name>("FlateDecode");
+    decoders->AppendNew<CPDF_String>("RL", false);
     EXPECT_FALSE(ValidateDecoderPipeline(decoders.Get()));
   }
 }
 
-TEST(fpdf_parser_decode, A85Decode) {
+TEST(ParserDecodeTest, ValidateDecoderPipelineWithIndirectObjects) {
+  {
+    // Valid 2 decoder pipeline with indirect objects.
+    CPDF_IndirectObjectHolder objects_holder;
+    auto decoder = pdfium::MakeRetain<CPDF_Name>(nullptr, "FlateDecode");
+    uint32_t decoder_number =
+        objects_holder.AddIndirectObject(std::move(decoder));
+
+    auto decoders = pdfium::MakeRetain<CPDF_Array>();
+    decoders->AppendNew<CPDF_Reference>(&objects_holder, decoder_number);
+    decoders->AppendNew<CPDF_Name>("LZW");
+    EXPECT_TRUE(ValidateDecoderPipeline(decoders.Get()));
+  }
+  {
+    // Valid 5 decoder pipeline with indirect objects, with an image decoder at
+    // the end.
+    CPDF_IndirectObjectHolder objects_holder;
+    auto decoder = pdfium::MakeRetain<CPDF_Name>(nullptr, "LZW");
+    uint32_t decoder_number =
+        objects_holder.AddIndirectObject(std::move(decoder));
+
+    auto decoders = pdfium::MakeRetain<CPDF_Array>();
+    decoders->AppendNew<CPDF_Name>("RunLengthDecode");
+    decoders->AppendNew<CPDF_Name>("ASCII85Decode");
+    decoders->AppendNew<CPDF_Name>("FlateDecode");
+    decoders->AppendNew<CPDF_Reference>(&objects_holder, decoder_number);
+    decoders->AppendNew<CPDF_Name>("DCTDecode");
+    EXPECT_TRUE(ValidateDecoderPipeline(decoders.Get()));
+  }
+  {
+    // Invalid 2 decoder pipeline due to wrong type indirect object.
+    CPDF_IndirectObjectHolder objects_holder;
+    auto decoder =
+        pdfium::MakeRetain<CPDF_String>(nullptr, "FlateDecode", false);
+    uint32_t decoder_number =
+        objects_holder.AddIndirectObject(std::move(decoder));
+
+    auto decoders = pdfium::MakeRetain<CPDF_Array>();
+    decoders->AppendNew<CPDF_Reference>(&objects_holder, decoder_number);
+    decoders->AppendNew<CPDF_Name>("LZW");
+    EXPECT_FALSE(ValidateDecoderPipeline(decoders.Get()));
+  }
+  {
+    // Invalid 2 decoder pipeline due to invalid indirect object.
+    CPDF_IndirectObjectHolder objects_holder;
+    auto decoder = pdfium::MakeRetain<CPDF_Name>(nullptr, "DCTDecode");
+    uint32_t decoder_number =
+        objects_holder.AddIndirectObject(std::move(decoder));
+
+    auto decoders = pdfium::MakeRetain<CPDF_Array>();
+    decoders->AppendNew<CPDF_Reference>(&objects_holder, decoder_number);
+    decoders->AppendNew<CPDF_Name>("LZW");
+    EXPECT_FALSE(ValidateDecoderPipeline(decoders.Get()));
+  }
+}
+
+// TODO(thestig): Test decoder params.
+TEST(ParserDecodeTest, GetDecoderArray) {
+  {
+    // Treat no filter as an empty filter array.
+    auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+    absl::optional<DecoderArray> decoder_array = GetDecoderArray(dict);
+    ASSERT_TRUE(decoder_array.has_value());
+    EXPECT_TRUE(decoder_array.value().empty());
+  }
+  {
+    // Wrong filter type.
+    auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+    dict->SetNewFor<CPDF_String>("Filter", "RL", false);
+    absl::optional<DecoderArray> decoder_array = GetDecoderArray(dict);
+    EXPECT_FALSE(decoder_array.has_value());
+  }
+  {
+    // Filter name.
+    auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+    dict->SetNewFor<CPDF_Name>("Filter", "RL");
+    absl::optional<DecoderArray> decoder_array = GetDecoderArray(dict);
+    ASSERT_TRUE(decoder_array.has_value());
+    ASSERT_EQ(1u, decoder_array.value().size());
+    EXPECT_EQ("RL", decoder_array.value()[0].first);
+  }
+  {
+    // Empty filter array.
+    auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+    dict->SetNewFor<CPDF_Array>("Filter");
+    absl::optional<DecoderArray> decoder_array = GetDecoderArray(dict);
+    ASSERT_TRUE(decoder_array.has_value());
+    EXPECT_TRUE(decoder_array.value().empty());
+  }
+  {
+    // Valid 1 element filter array.
+    auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+    auto filter_array = dict->SetNewFor<CPDF_Array>("Filter");
+    filter_array->AppendNew<CPDF_Name>("FooBar");
+    absl::optional<DecoderArray> decoder_array = GetDecoderArray(dict);
+    ASSERT_TRUE(decoder_array.has_value());
+    ASSERT_EQ(1u, decoder_array.value().size());
+    EXPECT_EQ("FooBar", decoder_array.value()[0].first);
+  }
+  {
+    // Valid 2 element filter array.
+    auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+    auto filter_array = dict->SetNewFor<CPDF_Array>("Filter");
+    filter_array->AppendNew<CPDF_Name>("AHx");
+    filter_array->AppendNew<CPDF_Name>("LZWDecode");
+    absl::optional<DecoderArray> decoder_array = GetDecoderArray(dict);
+    ASSERT_TRUE(decoder_array.has_value());
+    ASSERT_EQ(2u, decoder_array.value().size());
+    EXPECT_EQ("AHx", decoder_array.value()[0].first);
+    EXPECT_EQ("LZWDecode", decoder_array.value()[1].first);
+  }
+  {
+    // Invalid 2 element filter array.
+    auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+    auto invalid_filter_array = dict->SetNewFor<CPDF_Array>("Filter");
+    invalid_filter_array->AppendNew<CPDF_Name>("DCTDecode");
+    invalid_filter_array->AppendNew<CPDF_Name>("CCITTFaxDecode");
+    absl::optional<DecoderArray> decoder_array = GetDecoderArray(dict);
+    EXPECT_FALSE(decoder_array.has_value());
+  }
+}
+
+TEST(ParserDecodeTest, A85Decode) {
   const pdfium::DecodeTestData kTestData[] = {
       // Empty src string.
       STR_IN_OUT_CASE("", "", 0),
@@ -147,7 +297,76 @@
   }
 }
 
-TEST(fpdf_parser_decode, HexDecode) {
+// NOTE: python's zlib.compress() and zlib.decompress() may be useful for
+// external validation of the FlateDncode/FlateEecode test cases.
+TEST(FPDFParserDecodeEmbedderTest, FlateDecode) {
+  static const pdfium::DecodeTestData flate_decode_cases[] = {
+      STR_IN_OUT_CASE("", "", 0),
+      STR_IN_OUT_CASE("preposterous nonsense", "", 2),
+      STR_IN_OUT_CASE("\x78\x9c\x03\x00\x00\x00\x00\x01", "", 8),
+      STR_IN_OUT_CASE("\x78\x9c\x53\x00\x00\x00\x21\x00\x21", " ", 9),
+      STR_IN_OUT_CASE("\x78\x9c\x33\x34\x32\x06\x00\01\x2d\x00\x97", "123", 11),
+      STR_IN_OUT_CASE("\x78\x9c\x63\xf8\x0f\x00\x01\x01\x01\x00", "\x00\xff",
+                      10),
+      STR_IN_OUT_CASE(
+          "\x78\x9c\x33\x54\x30\x00\x42\x5d\x43\x05\x23\x4b\x05\x73\x33\x63"
+          "\x85\xe4\x5c\x2e\x90\x80\xa9\xa9\xa9\x82\xb9\xb1\xa9\x42\x51\x2a"
+          "\x57\xb8\x42\x1e\x57\x21\x92\xa0\x89\x9e\xb1\xa5\x09\x92\x84\x9e"
+          "\x85\x81\x81\x25\xd8\x14\x24\x26\xd0\x18\x43\x05\x10\x0c\x72\x57"
+          "\x80\x30\x8a\xd2\xb9\xf4\xdd\x0d\x14\xd2\x8b\xc1\x46\x99\x59\x1a"
+          "\x2b\x58\x1a\x9a\x83\x8c\x49\xe3\x0a\x04\x42\x00\x37\x4c\x1b\x42",
+          "1 0 0 -1 29 763 cm\n0 0 555 735 re\nW n\nq\n0 0 555 734.394 re\n"
+          "W n\nq\n0.8009 0 0 0.8009 0 0 cm\n1 1 1 RG 1 1 1 rg\n/G0 gs\n"
+          "0 0 693 917 re\nf\nQ\nQ\n",
+          96),
+  };
+
+  for (size_t i = 0; i < std::size(flate_decode_cases); ++i) {
+    const pdfium::DecodeTestData& data = flate_decode_cases[i];
+    std::unique_ptr<uint8_t, FxFreeDeleter> buf;
+    uint32_t buf_size;
+    EXPECT_EQ(data.processed_size,
+              FlateDecode({data.input, data.input_size}, &buf, &buf_size))
+        << " for case " << i;
+    ASSERT_TRUE(buf);
+    EXPECT_EQ(data.expected_size, buf_size) << " for case " << i;
+    if (data.expected_size != buf_size)
+      continue;
+    EXPECT_EQ(0, memcmp(data.expected, buf.get(), data.expected_size))
+        << " for case " << i;
+  }
+}
+
+TEST(ParserDecodeTest, FlateEncode) {
+  static const pdfium::StrFuncTestData flate_encode_cases[] = {
+      STR_IN_OUT_CASE("", "\x78\x9c\x03\x00\x00\x00\x00\x01"),
+      STR_IN_OUT_CASE(" ", "\x78\x9c\x53\x00\x00\x00\x21\x00\x21"),
+      STR_IN_OUT_CASE("123", "\x78\x9c\x33\x34\x32\x06\x00\01\x2d\x00\x97"),
+      STR_IN_OUT_CASE("\x00\xff", "\x78\x9c\x63\xf8\x0f\x00\x01\x01\x01\x00"),
+      STR_IN_OUT_CASE(
+          "1 0 0 -1 29 763 cm\n0 0 555 735 re\nW n\nq\n0 0 555 734.394 re\n"
+          "W n\nq\n0.8009 0 0 0.8009 0 0 cm\n1 1 1 RG 1 1 1 rg\n/G0 gs\n"
+          "0 0 693 917 re\nf\nQ\nQ\n",
+          "\x78\x9c\x33\x54\x30\x00\x42\x5d\x43\x05\x23\x4b\x05\x73\x33\x63"
+          "\x85\xe4\x5c\x2e\x90\x80\xa9\xa9\xa9\x82\xb9\xb1\xa9\x42\x51\x2a"
+          "\x57\xb8\x42\x1e\x57\x21\x92\xa0\x89\x9e\xb1\xa5\x09\x92\x84\x9e"
+          "\x85\x81\x81\x25\xd8\x14\x24\x26\xd0\x18\x43\x05\x10\x0c\x72\x57"
+          "\x80\x30\x8a\xd2\xb9\xf4\xdd\x0d\x14\xd2\x8b\xc1\x46\x99\x59\x1a"
+          "\x2b\x58\x1a\x9a\x83\x8c\x49\xe3\x0a\x04\x42\x00\x37\x4c\x1b\x42"),
+  };
+
+  for (size_t i = 0; i < std::size(flate_encode_cases); ++i) {
+    const pdfium::StrFuncTestData& data = flate_encode_cases[i];
+    DataVector<uint8_t> result = FlateEncode({data.input, data.input_size});
+    EXPECT_EQ(data.expected_size, result.size()) << " for case " << i;
+    if (data.expected_size != result.size())
+      continue;
+    EXPECT_EQ(0, memcmp(data.expected, result.data(), data.expected_size))
+        << " for case " << i;
+  }
+}
+
+TEST(ParserDecodeTest, HexDecode) {
   const pdfium::DecodeTestData kTestData[] = {
       // Empty src string.
       STR_IN_OUT_CASE("", "", 0),
@@ -182,83 +401,103 @@
   }
 }
 
-TEST(fpdf_parser_decode, 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},
-  };
+TEST(ParserDecodeTest, DecodeText) {
+  // 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")));
 }
 
-TEST(fpdf_parser_decode, 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},
-  };
+// 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")));
+}
 
-  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;
+// 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) {
+  // Empty src string.
+  EXPECT_EQ("", PDF_EncodeText(L""));
+
+  // 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 af58e8d..c2f626a 100644
--- a/core/fpdfapi/parser/fpdf_parser_utility.cpp
+++ b/core/fpdfapi/parser/fpdf_parser_utility.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,12 @@
 
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 
+#include <ostream>
+#include <utility>
+
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_boolean.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
@@ -18,14 +20,14 @@
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_stream.h"
-#include "third_party/base/logging.h"
+#include "third_party/base/check.h"
 
 // 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',
@@ -72,29 +74,21 @@
     'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
     'R', 'R', 'R', 'R', 'R', 'R', 'R', 'W'};
 
-Optional<FX_FILESIZE> GetHeaderOffset(
+absl::optional<FX_FILESIZE> GetHeaderOffset(
     const RetainPtr<IFX_SeekableReadStream>& pFile) {
   static constexpr size_t kBufSize = 4;
   uint8_t buf[kBufSize];
   for (FX_FILESIZE offset = 0; offset <= 1024; ++offset) {
-    if (!pFile->ReadBlockAtOffset(buf, offset, kBufSize))
-      return {};
+    if (!pFile->ReadBlockAtOffset(buf, offset))
+      return absl::nullopt;
 
     if (memcmp(buf, "%PDF", 4) == 0)
       return offset;
   }
-  return {};
-}
-
-int32_t GetDirectInteger(const CPDF_Dictionary* pDict, const ByteString& key) {
-  const CPDF_Number* pObj = ToNumber(pDict->GetObjectFor(key));
-  return pObj ? pObj->GetInteger() : 0;
+  return absl::nullopt;
 }
 
 ByteString PDF_NameDecode(ByteStringView orig) {
-  if (!orig.Contains('#'))
-    return ByteString(orig);
-
   size_t src_size = orig.GetLength();
   size_t out_index = 0;
   ByteString result;
@@ -155,30 +149,29 @@
 
 std::vector<float> ReadArrayElementsToVector(const CPDF_Array* pArray,
                                              size_t nCount) {
-  ASSERT(pArray);
-  ASSERT(pArray->size() >= nCount);
+  DCHECK(pArray);
+  DCHECK(pArray->size() >= nCount);
   std::vector<float> ret(nCount);
   for (size_t i = 0; i < nCount; ++i)
-    ret[i] = pArray->GetNumberAt(i);
+    ret[i] = pArray->GetFloatAt(i);
   return ret;
 }
 
-bool ValidateDictType(const CPDF_Dictionary* dict, const ByteString& type) {
-  ASSERT(dict);
-  ASSERT(!type.IsEmpty());
-  const CPDF_Name* name_obj = ToName(dict->GetObjectFor("Type"));
-  return name_obj && name_obj->GetString() == type;
+bool ValidateDictType(const CPDF_Dictionary* dict, ByteStringView type) {
+  DCHECK(!type.IsEmpty());
+  return dict && dict->GetNameFor("Type") == type;
 }
 
 bool ValidateDictAllResourcesOfType(const CPDF_Dictionary* dict,
-                                    const ByteString& type) {
+                                    ByteStringView type) {
   if (!dict)
     return false;
 
   CPDF_DictionaryLocker locker(dict);
   for (const auto& it : locker) {
-    const CPDF_Dictionary* entry = ToDictionary(it.second.Get()->GetDirect());
-    if (!entry || !ValidateDictType(entry, type))
+    RetainPtr<const CPDF_Dictionary> entry =
+        ToDictionary(it.second->GetDirect());
+    if (!ValidateDictType(entry.Get(), type))
       return false;
   }
   return true;
@@ -188,6 +181,12 @@
   return ValidateDictAllResourcesOfType(dict, "Font");
 }
 
+bool ValidateDictOptionalType(const CPDF_Dictionary* dict,
+                              ByteStringView type) {
+  DCHECK(!type.IsEmpty());
+  return dict && (!dict->KeyExist("Type") || dict->GetNameFor("Type") == type);
+}
+
 std::ostream& operator<<(std::ostream& buf, const CPDF_Object* pObj) {
   if (!pObj) {
     buf << " null";
@@ -202,7 +201,7 @@
       buf << " " << pObj->GetString();
       break;
     case CPDF_Object::kString:
-      buf << PDF_EncodeString(pObj->GetString(), pObj->AsString()->IsHex());
+      buf << pObj->AsString()->EncodeString();
       break;
     case CPDF_Object::kName: {
       ByteString str = pObj->GetString();
@@ -217,11 +216,11 @@
       const CPDF_Array* p = pObj->AsArray();
       buf << "[";
       for (size_t i = 0; i < p->size(); i++) {
-        const CPDF_Object* pElement = p->GetObjectAt(i);
-        if (pElement && !pElement->IsInline()) {
+        RetainPtr<const CPDF_Object> pElement = p->GetObjectAt(i);
+        if (!pElement->IsInline()) {
           buf << " " << pElement->GetObjNum() << " 0 R";
         } else {
-          buf << pElement;
+          buf << pElement.Get();
         }
       }
       buf << "]";
@@ -232,9 +231,9 @@
       buf << "<<";
       for (const auto& it : locker) {
         const ByteString& key = it.first;
-        CPDF_Object* pValue = it.second.Get();
+        const RetainPtr<CPDF_Object>& pValue = it.second;
         buf << "/" << PDF_NameEncode(key);
-        if (pValue && !pValue->IsInline()) {
+        if (!pValue->IsInline()) {
           buf << " " << pValue->GetObjNum() << " 0 R ";
         } else {
           buf << pValue;
@@ -244,18 +243,15 @@
       break;
     }
     case CPDF_Object::kStream: {
-      const CPDF_Stream* p = pObj->AsStream();
-      buf << p->GetDict() << "stream\r\n";
-      auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(p);
+      RetainPtr<const CPDF_Stream> p(pObj->AsStream());
+      buf << p->GetDict().Get() << "stream\r\n";
+      auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(p));
       pAcc->LoadAllDataRaw();
-      buf.write(reinterpret_cast<const char*>(pAcc->GetData()),
-                pAcc->GetSize());
+      pdfium::span<const uint8_t> span = pAcc->GetSpan();
+      buf.write(reinterpret_cast<const char*>(span.data()), span.size());
       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 75f4076..521e20a 100644
--- a/core/fpdfapi/parser/fpdf_parser_utility.h
+++ b/core/fpdfapi/parser/fpdf_parser_utility.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,32 +7,32 @@
 #ifndef CORE_FPDFAPI_PARSER_FPDF_PARSER_UTILITY_H_
 #define CORE_FPDFAPI_PARSER_FPDF_PARSER_UTILITY_H_
 
-#include <ostream>
+#include <iosfwd>
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CPDF_Array;
 class CPDF_Dictionary;
 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) {
@@ -42,11 +42,9 @@
 // On success, return a positive offset value to the PDF header. If the header
 // cannot be found, or if there is an error reading from |pFile|, then return
 // nullopt.
-Optional<FX_FILESIZE> GetHeaderOffset(
+absl::optional<FX_FILESIZE> GetHeaderOffset(
     const RetainPtr<IFX_SeekableReadStream>& pFile);
 
-int32_t GetDirectInteger(const CPDF_Dictionary* pDict, const ByteString& key);
-
 ByteString PDF_NameDecode(ByteStringView orig);
 ByteString PDF_NameEncode(const ByteString& orig);
 
@@ -55,17 +53,21 @@
 std::vector<float> ReadArrayElementsToVector(const CPDF_Array* pArray,
                                              size_t nCount);
 
-// Returns true if |dict| has a /Type name entry that matches |type|.
-bool ValidateDictType(const CPDF_Dictionary* dict, const ByteString& type);
+// Returns true if |dict| is non-null and has a /Type name entry that matches
+// |type|.
+bool ValidateDictType(const CPDF_Dictionary* dict, ByteStringView type);
 
 // Returns true if |dict| is non-null and all entries in |dict| are dictionaries
 // of |type|.
 bool ValidateDictAllResourcesOfType(const CPDF_Dictionary* dict,
-                                    const ByteString& type);
+                                    ByteStringView type);
 
 // Shorthand for ValidateDictAllResourcesOfType(dict, "Font").
 bool ValidateFontResourceDict(const CPDF_Dictionary* dict);
 
+// Like ValidateDictType(), but /Type can also not exist.
+bool ValidateDictOptionalType(const CPDF_Dictionary* dict, ByteStringView type);
+
 std::ostream& operator<<(std::ostream& buf, const CPDF_Object* pObj);
 
 #endif  // CORE_FPDFAPI_PARSER_FPDF_PARSER_UTILITY_H_
diff --git a/core/fpdfapi/parser/fpdf_parser_utility_unittest.cpp b/core/fpdfapi/parser/fpdf_parser_utility_unittest.cpp
index 098c3bd..898ab62 100644
--- a/core/fpdfapi/parser/fpdf_parser_utility_unittest.cpp
+++ b/core/fpdfapi/parser/fpdf_parser_utility_unittest.cpp
@@ -1,20 +1,21 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 
-#include "core/fpdfapi/page/cpdf_docpagedata.h"
+#include <memory>
+
 #include "core/fpdfapi/page/cpdf_pagemodule.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfapi/render/cpdf_docrenderdata.h"
+#include "core/fpdfapi/parser/cpdf_test_document.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-TEST(fpdf_parser_utility, PDF_NameDecode) {
+TEST(ParserUtilityTest, NameDecode) {
   EXPECT_EQ("", PDF_NameDecode(""));
   EXPECT_EQ("A", PDF_NameDecode("A"));
   EXPECT_EQ("#", PDF_NameDecode("#"));
@@ -23,7 +24,7 @@
   EXPECT_EQ("A1", PDF_NameDecode("#411"));
 }
 
-TEST(fpdf_parser_utility, PDF_NameEncode) {
+TEST(ParserUtilityTest, NameEncode) {
   EXPECT_EQ("", PDF_NameEncode(""));
   EXPECT_EQ("A", PDF_NameEncode("A"));
   EXPECT_EQ("#23", PDF_NameEncode("#"));
@@ -33,7 +34,7 @@
   EXPECT_EQ("f#C2#A5", PDF_NameEncode("f\xc2\xa5"));
 }
 
-TEST(fpdf_parser_utility, ValidateDictType) {
+TEST(ParserUtilityTest, ValidateDictType) {
   auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
 
   // No type.
@@ -51,7 +52,7 @@
   EXPECT_FALSE(ValidateDictType(dict.Get(), "bar"));
 }
 
-TEST(fpdf_parser_utility, ValidateDictAllResourcesOfType) {
+TEST(ParserUtilityTest, ValidateDictAllResourcesOfType) {
   CPDF_PageModule::Create();
 
   {
@@ -67,7 +68,7 @@
     EXPECT_FALSE(ValidateDictAllResourcesOfType(nullptr, "bar"));
 
     // Add two correct dictionary entries and one string entry.
-    CPDF_Dictionary* new_dict = dict->SetNewFor<CPDF_Dictionary>("f1");
+    auto new_dict = dict->SetNewFor<CPDF_Dictionary>("f1");
     new_dict->SetNewFor<CPDF_Name>("Type", "foo");
     new_dict = dict->SetNewFor<CPDF_Dictionary>("f2");
     new_dict->SetNewFor<CPDF_Name>("Type", "foo");
@@ -89,14 +90,11 @@
 
   {
     // Indirect dictionary.
-    auto doc = pdfium::MakeUnique<CPDF_Document>(
-        pdfium::MakeUnique<CPDF_DocRenderData>(),
-        pdfium::MakeUnique<CPDF_DocPageData>());
-
+    auto doc = std::make_unique<CPDF_TestDocument>();
     auto dict = doc->New<CPDF_Dictionary>();
 
     // Add a correct dictionary entry.
-    CPDF_Dictionary* new_dict = doc->NewIndirect<CPDF_Dictionary>();
+    auto new_dict = doc->NewIndirect<CPDF_Dictionary>();
     new_dict->SetNewFor<CPDF_Name>("Type", "foo");
     dict->SetNewFor<CPDF_Reference>("f1", doc.get(), new_dict->GetObjNum());
 
@@ -106,3 +104,21 @@
 
   CPDF_PageModule::Destroy();
 }
+
+TEST(ParserUtilityTest, ValidateDictOptionalType) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+
+  // No type is ok.
+  EXPECT_TRUE(ValidateDictOptionalType(dict.Get(), "foo"));
+  EXPECT_TRUE(ValidateDictOptionalType(dict.Get(), "bar"));
+
+  // Add the wrong object type.
+  dict->SetNewFor<CPDF_String>("Type", L"foo");
+  EXPECT_FALSE(ValidateDictOptionalType(dict.Get(), "foo"));
+  EXPECT_FALSE(ValidateDictOptionalType(dict.Get(), "bar"));
+
+  // Add the correct object type.
+  dict->SetNewFor<CPDF_Name>("Type", "foo");
+  EXPECT_TRUE(ValidateDictOptionalType(dict.Get(), "foo"));
+  EXPECT_FALSE(ValidateDictOptionalType(dict.Get(), "bar"));
+}
diff --git a/core/fpdfapi/parser/object_tree_traversal_util.cpp b/core/fpdfapi/parser/object_tree_traversal_util.cpp
new file mode 100644
index 0000000..d319484
--- /dev/null
+++ b/core/fpdfapi/parser/object_tree_traversal_util.cpp
@@ -0,0 +1,222 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/parser/object_tree_traversal_util.h"
+
+#include <stdint.h>
+
+#include <map>
+#include <queue>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_reference.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+
+namespace {
+
+class ObjectTreeTraverser {
+ public:
+  explicit ObjectTreeTraverser(const CPDF_Document* document)
+      : document_(document) {
+    const CPDF_Parser* parser = document_->GetParser();
+    const CPDF_Dictionary* trailer = parser ? parser->GetTrailer() : nullptr;
+    const CPDF_Dictionary* root = trailer ? trailer : document_->GetRoot();
+    const uint32_t root_object_number =
+        trailer ? parser->GetTrailerObjectNumber() : root->GetObjNum();
+    // If `root` is a trailer, then it may not have an object number, as many
+    // trailers are inlined.
+    if (root_object_number) {
+      referenced_objects_[root_object_number] = 1;
+      object_number_map_[root] = root_object_number;
+    }
+
+    object_queue_.push(pdfium::WrapRetain(root));
+    seen_objects_.insert(root);
+  }
+  ~ObjectTreeTraverser() = default;
+
+  void Traverse() { CalculateReferenceCounts(GetReferenceEntries()); }
+
+  const std::map<uint32_t, int>& referenced_objects() {
+    return referenced_objects_;
+  }
+
+ private:
+  struct ReferenceEntry {
+    uint32_t ref_object_number;
+    uint32_t referenced_object_number;
+  };
+
+  std::vector<ReferenceEntry> GetReferenceEntries() {
+    std::vector<ReferenceEntry> reference_entries;
+    while (!object_queue_.empty()) {
+      RetainPtr<const CPDF_Object> current_object = object_queue_.front();
+      object_queue_.pop();
+
+      switch (current_object->GetType()) {
+        case CPDF_Object::kArray: {
+          CPDF_ArrayLocker locker(current_object->AsArray());
+          for (const auto& it : locker) {
+            PushNewObject(current_object, it);
+          }
+          break;
+        }
+        case CPDF_Object::kDictionary: {
+          CPDF_DictionaryLocker locker(current_object->AsDictionary());
+          for (const auto& it : locker) {
+            PushNewObject(current_object, it.second);
+          }
+          break;
+        }
+        case CPDF_Object::kReference: {
+          const CPDF_Reference* ref_object = current_object->AsReference();
+          const uint32_t ref_object_number = GetObjectNumber(ref_object);
+          const uint32_t referenced_object_number = ref_object->GetRefObjNum();
+
+          RetainPtr<const CPDF_Object> referenced_object;
+          if (ref_object->HasIndirectObjectHolder()) {
+            // Calling GetIndirectObject() does not work for normal references.
+            referenced_object = ref_object->GetDirect();
+          } else {
+            // Calling GetDirect() does not work for references from trailers.
+            referenced_object =
+                document_->GetIndirectObject(referenced_object_number);
+          }
+          // Unlike the other object types, CPDF_Reference can point at nullptr.
+          if (referenced_object) {
+            CHECK(referenced_object_number);
+            reference_entries.push_back(
+                {ref_object_number, referenced_object_number});
+            PushNewObject(ref_object, referenced_object);
+          }
+          break;
+        }
+        case CPDF_Object::kStream: {
+          RetainPtr<const CPDF_Dictionary> dict =
+              current_object->AsStream()->GetDict();
+          CHECK(dict->IsInline());  // i.e. No object number.
+          CPDF_DictionaryLocker locker(dict);
+          for (const auto& it : locker) {
+            PushNewObject(current_object, it.second);
+          }
+          break;
+        }
+        default: {
+          break;
+        }
+      }
+    }
+    return reference_entries;
+  }
+
+  void CalculateReferenceCounts(
+      const std::vector<ReferenceEntry>& reference_entries) {
+    // Tracks PDF objects that referenced other PDF objects, identified by their
+    // object numbers. Never 0.
+    std::set<uint32_t> seen_ref_objects;
+
+    for (const ReferenceEntry& entry : reference_entries) {
+      // Make sure this is not a self-reference.
+      if (entry.referenced_object_number == entry.ref_object_number) {
+        continue;
+      }
+
+      // Make sure this is not a circular reference.
+      if (pdfium::Contains(seen_ref_objects, entry.ref_object_number) &&
+          pdfium::Contains(seen_ref_objects, entry.referenced_object_number)) {
+        continue;
+      }
+
+      ++referenced_objects_[entry.referenced_object_number];
+      if (entry.ref_object_number) {
+        seen_ref_objects.insert(entry.ref_object_number);
+      }
+    }
+  }
+
+  void PushNewObject(const CPDF_Object* parent_object,
+                     RetainPtr<const CPDF_Object> child_object) {
+    CHECK(parent_object);
+    CHECK(child_object);
+    const bool inserted = seen_objects_.insert(child_object).second;
+    if (!inserted) {
+      return;
+    }
+    const uint32_t child_object_number = child_object->GetObjNum();
+    if (child_object_number) {
+      object_number_map_[child_object] = child_object_number;
+    } else {
+      // This search can fail for inlined trailers.
+      auto it = object_number_map_.find(parent_object);
+      if (it != object_number_map_.end()) {
+        object_number_map_[child_object] = it->second;
+      }
+    }
+    object_queue_.push(std::move(child_object));
+  }
+
+  // Returns 0 if not found.
+  uint32_t GetObjectNumber(const CPDF_Object* object) const {
+    auto it = object_number_map_.find(object);
+    return it != object_number_map_.end() ? it->second : 0;
+  }
+
+  UnownedPtr<const CPDF_Document> const document_;
+
+  // Queue of objects to traverse.
+  // - Pointers in the queue are non-null.
+  // - The same pointer never enters the queue twice.
+  std::queue<RetainPtr<const CPDF_Object>> object_queue_;
+
+  // Map of objects to "top-level" object numbers. For inline objects, this is
+  // the ancestor object with an object number. The keys are non-null and the
+  // values are never 0.
+  // This is used to prevent self-references, as a single PDF object, with
+  // inlined objects, is represented by multiple CPDF_Objects.
+  std::map<const CPDF_Object*, uint32_t> object_number_map_;
+
+  // Tracks traversed objects to prevent duplicates from getting into
+  // `object_queue_` and `object_number_map_`.
+  std::set<const CPDF_Object*> seen_objects_;
+
+  // Tracks which PDF objects are referenced.
+  // Key: object number
+  // Value: number of times referenced
+  std::map<uint32_t, int> referenced_objects_;
+};
+
+}  // namespace
+
+std::set<uint32_t> GetObjectsWithReferences(const CPDF_Document* document) {
+  ObjectTreeTraverser traverser(document);
+  traverser.Traverse();
+
+  std::set<uint32_t> results;
+  for (const auto& it : traverser.referenced_objects()) {
+    results.insert(it.first);
+  }
+  return results;
+}
+
+std::set<uint32_t> GetObjectsWithMultipleReferences(
+    const CPDF_Document* document) {
+  ObjectTreeTraverser traverser(document);
+  traverser.Traverse();
+
+  std::set<uint32_t> results;
+  for (const auto& it : traverser.referenced_objects()) {
+    if (it.second > 1) {
+      results.insert(it.first);
+    }
+  }
+  return results;
+}
diff --git a/core/fpdfapi/parser/object_tree_traversal_util.h b/core/fpdfapi/parser/object_tree_traversal_util.h
new file mode 100644
index 0000000..e9db96d
--- /dev/null
+++ b/core/fpdfapi/parser/object_tree_traversal_util.h
@@ -0,0 +1,46 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FPDFAPI_PARSER_OBJECT_TREE_TRAVERSAL_UTIL_H_
+#define CORE_FPDFAPI_PARSER_OBJECT_TREE_TRAVERSAL_UTIL_H_
+
+#include <stdint.h>
+
+#include <set>
+
+class CPDF_Document;
+
+// Traverses `document` starting with its trailer, if it has one, or starting at
+// the catalog, which always exists. The trailer should have a reference to the
+// catalog. The traversal avoids cycles.
+// Returns all the PDF objects (not CPDF_Objects) the traversal reached as a set
+// of object numbers.
+std::set<uint32_t> GetObjectsWithReferences(const CPDF_Document* document);
+
+// Same as GetObjectsWithReferences(), but only returns the objects with
+// multiple references. References that would create a cycle are ignored.
+//
+// In this example, where (A) is the root node:
+//
+//     A -> B
+//     A -> C
+//     B -> D
+//     C -> D
+//
+// GetObjectsWithMultipleReferences() returns {D}, since both (B) and (C)
+// references to (D), and there are no cycles.
+//
+// In this example, where (A) is the root node:
+//
+//     A -> B
+//     B -> C
+//     C -> B
+//
+// GetObjectsWithMultipleReferences() returns {}, even though both (A) and (C)
+// references (B). Since (B) -> (C) -> (B) creates a cycle, the (C) -> (B)
+// reference does not count.
+std::set<uint32_t> GetObjectsWithMultipleReferences(
+    const CPDF_Document* document);
+
+#endif  // CORE_FPDFAPI_PARSER_OBJECT_TREE_TRAVERSAL_UTIL_H_
diff --git a/core/fpdfapi/parser/object_tree_traversal_util_embeddertest.cpp b/core/fpdfapi/parser/object_tree_traversal_util_embeddertest.cpp
new file mode 100644
index 0000000..ed0c8e1
--- /dev/null
+++ b/core/fpdfapi/parser/object_tree_traversal_util_embeddertest.cpp
@@ -0,0 +1,112 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/parser/object_tree_traversal_util.h"
+
+#include <stdint.h>
+
+#include <set>
+
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "testing/embedder_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::UnorderedElementsAreArray;
+using ObjectTreeTraversalUtilEmbedderTest = EmbedderTest;
+
+namespace {
+
+CPDF_Document* GetCPDFDocument(FPDF_DOCUMENT document) {
+  // This is cheating slightly to avoid a layering violation, since this file
+  // cannot include fpdfsdk/cpdfsdk_helpers.h to get access to
+  // CPDFDocumentFromFPDFDocument().
+  return reinterpret_cast<CPDF_Document*>((document));
+}
+
+}  // namespace
+
+TEST_F(ObjectTreeTraversalUtilEmbedderTest, GetObjectsWithReferencesBasic) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  CPDF_Document* doc = GetCPDFDocument(document());
+  std::set<uint32_t> referenced_objects = GetObjectsWithReferences(doc);
+  EXPECT_THAT(referenced_objects,
+              UnorderedElementsAreArray({1, 2, 3, 4, 5, 6}));
+}
+
+TEST_F(ObjectTreeTraversalUtilEmbedderTest, GetObjectsWithReferencesNewDoc) {
+  ScopedFPDFDocument new_doc(FPDF_CreateNewDocument());
+  CPDF_Document* doc = GetCPDFDocument(new_doc.get());
+  std::set<uint32_t> referenced_objects = GetObjectsWithReferences(doc);
+  // Empty documents have a catalog and an empty pages object.
+  EXPECT_THAT(referenced_objects, UnorderedElementsAreArray({1, 2}));
+}
+
+TEST_F(ObjectTreeTraversalUtilEmbedderTest,
+       GetObjectsWithReferencesCircularRefs) {
+  ASSERT_TRUE(OpenDocument("circular_viewer_ref.pdf"));
+  CPDF_Document* doc = GetCPDFDocument(document());
+  std::set<uint32_t> referenced_objects = GetObjectsWithReferences(doc);
+  // The trailer points at a catalog, and the catalog only references itself.
+  EXPECT_THAT(referenced_objects, UnorderedElementsAreArray({1}));
+}
+
+TEST_F(ObjectTreeTraversalUtilEmbedderTest,
+       GetObjectsWithReferencesCrossRefStream) {
+  ASSERT_TRUE(OpenDocument("bug_1399.pdf"));
+  CPDF_Document* doc = GetCPDFDocument(document());
+  std::set<uint32_t> referenced_objects = GetObjectsWithReferences(doc);
+  // The trailer is the dictionary inside /XRef object 16 0. Note that it
+  // references object 3 0, but the rest of the document does not.
+  EXPECT_THAT(referenced_objects,
+              UnorderedElementsAreArray({1, 2, 3, 4, 5, 12, 13, 14, 16}));
+}
+
+TEST_F(ObjectTreeTraversalUtilEmbedderTest,
+       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());
+  std::set<uint32_t> referenced_objects = GetObjectsWithMultipleReferences(doc);
+  EXPECT_TRUE(referenced_objects.empty());
+}
+
+TEST_F(ObjectTreeTraversalUtilEmbedderTest,
+       GetObjectsWithMultipleReferencesNewDoc) {
+  ScopedFPDFDocument new_doc(FPDF_CreateNewDocument());
+  CPDF_Document* doc = GetCPDFDocument(new_doc.get());
+  std::set<uint32_t> referenced_objects = GetObjectsWithMultipleReferences(doc);
+  EXPECT_TRUE(referenced_objects.empty());
+}
+
+TEST_F(ObjectTreeTraversalUtilEmbedderTest,
+       GetObjectsWithMultipleReferencesCircularRefs) {
+  ASSERT_TRUE(OpenDocument("circular_viewer_ref.pdf"));
+  CPDF_Document* doc = GetCPDFDocument(document());
+  std::set<uint32_t> referenced_objects = GetObjectsWithMultipleReferences(doc);
+  EXPECT_TRUE(referenced_objects.empty());
+}
+
+TEST_F(ObjectTreeTraversalUtilEmbedderTest,
+       GetObjectsWithMultipleReferencesSharedObjects) {
+  ASSERT_TRUE(OpenDocument("hello_world_2_pages.pdf"));
+  CPDF_Document* doc = GetCPDFDocument(document());
+  std::set<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/Android.bp b/core/fpdfapi/render/Android.bp
index 79b0db6..cd2b82d 100644
--- a/core/fpdfapi/render/Android.bp
+++ b/core/fpdfapi/render/Android.bp
@@ -13,11 +13,8 @@
 
     visibility: ["//external/pdfium:__subpackages__"],
 
-    header_libs: [
-        "libpdfium-constants",
-    ],
-
     static_libs: [
+        "libpdfium-constants",
         "libpdfium-fxcodec",
         "libpdfium-fxcrt",
         "libpdfium-fxge",
diff --git a/core/fpdfapi/render/BUILD.gn b/core/fpdfapi/render/BUILD.gn
index ddce77a..bb76ef8 100644
--- a/core/fpdfapi/render/BUILD.gn
+++ b/core/fpdfapi/render/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -7,20 +7,14 @@
 
 source_set("render") {
   sources = [
-    "cpdf_charposlist.cpp",
-    "cpdf_charposlist.h",
+    "charposlist.cpp",
+    "charposlist.h",
     "cpdf_devicebuffer.cpp",
     "cpdf_devicebuffer.h",
     "cpdf_docrenderdata.cpp",
     "cpdf_docrenderdata.h",
-    "cpdf_imagecacheentry.cpp",
-    "cpdf_imagecacheentry.h",
-    "cpdf_imageloader.cpp",
-    "cpdf_imageloader.h",
     "cpdf_imagerenderer.cpp",
     "cpdf_imagerenderer.h",
-    "cpdf_pagerendercache.cpp",
-    "cpdf_pagerendercache.h",
     "cpdf_pagerendercontext.cpp",
     "cpdf_pagerendercontext.h",
     "cpdf_progressiverenderer.cpp",
@@ -33,6 +27,8 @@
     "cpdf_rendershading.h",
     "cpdf_renderstatus.cpp",
     "cpdf_renderstatus.h",
+    "cpdf_rendertiling.cpp",
+    "cpdf_rendertiling.h",
     "cpdf_scaledrenderbuffer.cpp",
     "cpdf_scaledrenderbuffer.h",
     "cpdf_textrenderer.cpp",
@@ -42,7 +38,10 @@
     "cpdf_type3glyphmap.cpp",
     "cpdf_type3glyphmap.h",
   ]
-  configs += [ "../../../:pdfium_core_config" ]
+  configs += [
+    "../../../:pdfium_strict_config",
+    "../../../:pdfium_noshorten_config",
+  ]
   deps = [
     "../../../constants",
     "../../fxcodec",
@@ -52,13 +51,13 @@
     "../page",
     "../parser",
   ]
+  visibility = [ "../../../*" ]
   if (is_win) {
     sources += [
       "cpdf_windowsrenderdevice.cpp",
       "cpdf_windowsrenderdevice.h",
     ]
   }
-  visibility = [ "../../../*" ]
 }
 
 pdfium_unittest_source_set("unittests") {
@@ -76,5 +75,6 @@
     "fpdf_progressive_render_embeddertest.cpp",
     "fpdf_render_pattern_embeddertest.cpp",
   ]
+  deps = [ "../../fxge" ]
   pdfium_root_dir = "../../../"
 }
diff --git a/core/fpdfapi/render/charposlist.cpp b/core/fpdfapi/render/charposlist.cpp
new file mode 100644
index 0000000..aba0ada
--- /dev/null
+++ b/core/fpdfapi/render/charposlist.cpp
@@ -0,0 +1,196 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/render/charposlist.h"
+
+#include "build/build_config.h"
+#include "core/fpdfapi/font/cpdf_cidfont.h"
+#include "core/fpdfapi/font/cpdf_font.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxge/cfx_fontmapper.h"
+#include "core/fxge/cfx_substfont.h"
+#include "core/fxge/text_char_pos.h"
+
+namespace {
+
+bool ShouldUseExistingFont(const CPDF_Font* font,
+                           uint32_t glyph_id,
+                           bool has_to_unicode) {
+  // Check for invalid glyph ID.
+  if (glyph_id == static_cast<uint32_t>(-1))
+    return false;
+
+  if (!font->IsTrueTypeFont())
+    return true;
+
+  // For TrueType fonts, a glyph ID of 0 may be invalid.
+  //
+  // When a "ToUnicode" entry exists in the font dictionary, it indicates
+  // a "ToUnicode" mapping file is used to convert from CIDs (which
+  // begins at decimal 0) to Unicode code. (See ToUnicode Mapping File
+  // Tutorial - Adobe
+  // https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/5411.ToUnicode.pdf
+  // and
+  // https://www.freetype.org/freetype2/docs/tutorial/step1.html#section-6)
+  return glyph_id != 0 || has_to_unicode;
+}
+
+// The following is not a perfect solution and can be further improved.
+// For example, if `subst_font` is "Book" and the `base_font_name` is "Bookman",
+// this function will return "true" even though the actual font "Bookman"
+// is not loaded.
+// An exact string match is not possible here, because `subst_font_name`
+// will be the same value for different postscript names.
+// For example: "Times New Roman" as `subst_font_name` for all of these
+// `base_font_name` values: "TimesNewRoman,Bold", "TimesNewRomanPS-Bold",
+// "TimesNewRomanBold" and "TimesNewRoman-Bold".
+bool IsActualFontLoaded(const CFX_SubstFont* subst_font,
+                        const ByteString& base_font_name) {
+  // Skip if we loaded the actual font.
+  // example: TimesNewRoman,Bold -> Times New Roman
+  ByteString subst_font_name = subst_font->m_Family;
+  subst_font_name.Remove(' ');
+  subst_font_name.MakeLower();
+
+  absl::optional<size_t> find =
+      base_font_name.Find(subst_font_name.AsStringView());
+  return find.has_value() && find.value() == 0;
+}
+
+bool ApplyGlyphSpacingHeuristic(const CPDF_Font* font,
+                                const CFX_Font* current_font,
+                                bool is_vertical_writing) {
+  if (is_vertical_writing || font->IsEmbedded() || !font->HasFontWidths()) {
+    return false;
+  }
+
+  // Skip if we loaded a standard alternate font.
+  // example: Helvetica -> Arial
+  ByteString base_font_name = font->GetBaseFontName();
+  base_font_name.MakeLower();
+
+  auto standard_font_name =
+      CFX_FontMapper::GetStandardFontName(&base_font_name);
+  if (standard_font_name.has_value()) {
+    return false;
+  }
+
+  CFX_SubstFont* subst_font = current_font->GetSubstFont();
+  if (subst_font->IsBuiltInGenericFont()) {
+    return false;
+  }
+
+  return !IsActualFontLoaded(subst_font, base_font_name);
+}
+
+}  // namespace
+
+std::vector<TextCharPos> GetCharPosList(pdfium::span<const uint32_t> char_codes,
+                                        pdfium::span<const float> char_pos,
+                                        CPDF_Font* font,
+                                        float font_size) {
+  std::vector<TextCharPos> results;
+  results.reserve(char_codes.size());
+
+  CPDF_CIDFont* cid_font = font->AsCIDFont();
+  bool is_vertical_writing = cid_font && cid_font->IsVertWriting();
+  bool has_to_unicode = !!font->GetFontDict()->GetStreamFor("ToUnicode");
+  for (size_t i = 0; i < char_codes.size(); ++i) {
+    uint32_t char_code = char_codes[i];
+    if (char_code == static_cast<uint32_t>(-1))
+      continue;
+
+    bool is_vertical_glyph = false;
+    results.emplace_back();
+    TextCharPos& text_char_pos = results.back();
+    if (cid_font)
+      text_char_pos.m_bFontStyle = true;
+    WideString unicode = font->UnicodeFromCharCode(char_code);
+    text_char_pos.m_Unicode = !unicode.IsEmpty() ? unicode[0] : char_code;
+    text_char_pos.m_GlyphIndex =
+        font->GlyphFromCharCode(char_code, &is_vertical_glyph);
+    uint32_t glyph_id = text_char_pos.m_GlyphIndex;
+#if BUILDFLAG(IS_APPLE)
+    text_char_pos.m_ExtGID = font->GlyphFromCharCodeExt(char_code);
+    glyph_id = text_char_pos.m_ExtGID != static_cast<uint32_t>(-1)
+                   ? text_char_pos.m_ExtGID
+                   : text_char_pos.m_GlyphIndex;
+#endif
+    CFX_Font* current_font;
+    if (ShouldUseExistingFont(font, glyph_id, has_to_unicode)) {
+      current_font = font->GetFont();
+      text_char_pos.m_FallbackFontPosition = -1;
+    } else {
+      int32_t fallback_position = font->FallbackFontFromCharcode(char_code);
+      current_font = font->GetFontFallback(fallback_position);
+      text_char_pos.m_FallbackFontPosition = fallback_position;
+      text_char_pos.m_GlyphIndex =
+          font->FallbackGlyphFromCharcode(fallback_position, char_code);
+#if BUILDFLAG(IS_APPLE)
+      text_char_pos.m_ExtGID = text_char_pos.m_GlyphIndex;
+#endif
+    }
+
+    if (!font->IsEmbedded() && !font->IsCIDFont())
+      text_char_pos.m_FontCharWidth = font->GetCharWidthF(char_code);
+    else
+      text_char_pos.m_FontCharWidth = 0;
+
+    text_char_pos.m_Origin = CFX_PointF(i > 0 ? char_pos[i - 1] : 0, 0);
+    text_char_pos.m_bGlyphAdjust = false;
+
+    float scaling_factor = 1.0f;
+    if (ApplyGlyphSpacingHeuristic(font, current_font, is_vertical_writing)) {
+      int pdf_glyph_width = font->GetCharWidthF(char_code);
+      int font_glyph_width =
+          current_font->GetGlyphWidth(text_char_pos.m_GlyphIndex);
+      if (font_glyph_width && pdf_glyph_width > font_glyph_width + 1) {
+        // Move the initial x position by half of the excess (transformed to
+        // text space coordinates).
+        text_char_pos.m_Origin.x +=
+            (pdf_glyph_width - font_glyph_width) * font_size / 2000.0f;
+      } else if (pdf_glyph_width && font_glyph_width &&
+                 pdf_glyph_width < font_glyph_width) {
+        scaling_factor = static_cast<float>(pdf_glyph_width) / font_glyph_width;
+        text_char_pos.m_AdjustMatrix[0] = scaling_factor;
+        text_char_pos.m_AdjustMatrix[1] = 0.0f;
+        text_char_pos.m_AdjustMatrix[2] = 0.0f;
+        text_char_pos.m_AdjustMatrix[3] = 1.0f;
+        text_char_pos.m_bGlyphAdjust = true;
+      }
+    }
+    if (!cid_font)
+      continue;
+
+    uint16_t cid = cid_font->CIDFromCharCode(char_code);
+    if (is_vertical_writing) {
+      text_char_pos.m_Origin = CFX_PointF(0, text_char_pos.m_Origin.x);
+
+      CFX_Point16 vertical_origin = cid_font->GetVertOrigin(cid);
+      text_char_pos.m_Origin.x -= font_size * vertical_origin.x / 1000;
+      text_char_pos.m_Origin.y -= font_size * vertical_origin.y / 1000;
+    }
+
+    const uint8_t* cid_transform = cid_font->GetCIDTransform(cid);
+    if (cid_transform && !is_vertical_glyph) {
+      text_char_pos.m_AdjustMatrix[0] =
+          cid_font->CIDTransformToFloat(cid_transform[0]) * scaling_factor;
+      text_char_pos.m_AdjustMatrix[1] =
+          cid_font->CIDTransformToFloat(cid_transform[1]) * scaling_factor;
+      text_char_pos.m_AdjustMatrix[2] =
+          cid_font->CIDTransformToFloat(cid_transform[2]);
+      text_char_pos.m_AdjustMatrix[3] =
+          cid_font->CIDTransformToFloat(cid_transform[3]);
+      text_char_pos.m_Origin.x +=
+          cid_font->CIDTransformToFloat(cid_transform[4]) * font_size;
+      text_char_pos.m_Origin.y +=
+          cid_font->CIDTransformToFloat(cid_transform[5]) * font_size;
+      text_char_pos.m_bGlyphAdjust = true;
+    }
+  }
+
+  return results;
+}
diff --git a/core/fpdfapi/render/charposlist.h b/core/fpdfapi/render/charposlist.h
new file mode 100644
index 0000000..47c5d57
--- /dev/null
+++ b/core/fpdfapi/render/charposlist.h
@@ -0,0 +1,24 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFAPI_RENDER_CHARPOSLIST_H_
+#define CORE_FPDFAPI_RENDER_CHARPOSLIST_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "third_party/base/containers/span.h"
+
+class CPDF_Font;
+class TextCharPos;
+
+std::vector<TextCharPos> GetCharPosList(pdfium::span<const uint32_t> char_codes,
+                                        pdfium::span<const float> char_pos,
+                                        CPDF_Font* font,
+                                        float font_size);
+
+#endif  // CORE_FPDFAPI_RENDER_CHARPOSLIST_H_
diff --git a/core/fpdfapi/render/cpdf_charposlist.cpp b/core/fpdfapi/render/cpdf_charposlist.cpp
deleted file mode 100644
index b8d0cf2..0000000
--- a/core/fpdfapi/render/cpdf_charposlist.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fpdfapi/render/cpdf_charposlist.h"
-
-#include "build/build_config.h"
-#include "core/fpdfapi/font/cpdf_cidfont.h"
-#include "core/fpdfapi/font/cpdf_font.h"
-#include "core/fxge/cfx_substfont.h"
-#include "core/fxge/text_char_pos.h"
-
-CPDF_CharPosList::CPDF_CharPosList(const std::vector<uint32_t>& charCodes,
-                                   const std::vector<float>& charPos,
-                                   CPDF_Font* pFont,
-                                   float font_size) {
-  m_CharPos.reserve(charCodes.size());
-  CPDF_CIDFont* pCIDFont = pFont->AsCIDFont();
-  bool bVertWriting = pCIDFont && pCIDFont->IsVertWriting();
-  bool bToUnicode = !!pFont->GetFontDict()->GetStreamFor("ToUnicode");
-  for (size_t i = 0; i < charCodes.size(); ++i) {
-    uint32_t CharCode = charCodes[i];
-    if (CharCode == static_cast<uint32_t>(-1))
-      continue;
-
-    bool bVert = false;
-    m_CharPos.emplace_back();
-    TextCharPos& charpos = m_CharPos.back();
-    if (pCIDFont)
-      charpos.m_bFontStyle = true;
-    WideString unicode = pFont->UnicodeFromCharCode(CharCode);
-    charpos.m_Unicode = !unicode.IsEmpty() ? unicode[0] : CharCode;
-    charpos.m_GlyphIndex = pFont->GlyphFromCharCode(CharCode, &bVert);
-    uint32_t GlyphID = charpos.m_GlyphIndex;
-#if defined(OS_MACOSX)
-    charpos.m_ExtGID = pFont->GlyphFromCharCodeExt(CharCode);
-    GlyphID = charpos.m_ExtGID != static_cast<uint32_t>(-1)
-                  ? charpos.m_ExtGID
-                  : charpos.m_GlyphIndex;
-#endif
-    bool bIsInvalidGlyph = GlyphID == static_cast<uint32_t>(-1);
-    bool bIsTrueTypeZeroGlyph = GlyphID == 0 && pFont->IsTrueTypeFont();
-    bool bUseFallbackFont = false;
-    if (bIsInvalidGlyph || bIsTrueTypeZeroGlyph) {
-      charpos.m_FallbackFontPosition =
-          pFont->FallbackFontFromCharcode(CharCode);
-      charpos.m_GlyphIndex = pFont->FallbackGlyphFromCharcode(
-          charpos.m_FallbackFontPosition, CharCode);
-      if (bIsTrueTypeZeroGlyph &&
-          charpos.m_GlyphIndex == static_cast<uint32_t>(-1)) {
-        // For a TrueType font character, when finding the glyph from the
-        // fallback font fails, switch back to using the original font.
-
-        // When keyword "ToUnicode" exists in the PDF file, it indicates
-        // a "ToUnicode" mapping file is used to convert from CIDs (which
-        // begins at decimal 0) to Unicode code. (See ToUnicode Mapping File
-        // Tutorial - Adobe
-        // https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/5411.ToUnicode.pdf
-        // and
-        // https://www.freetype.org/freetype2/docs/tutorial/step1.html#section-6)
-        if (bToUnicode)
-          charpos.m_GlyphIndex = 0;
-      } else {
-        bUseFallbackFont = true;
-      }
-    }
-    CFX_Font* pCurrentFont;
-    if (bUseFallbackFont) {
-      pCurrentFont = pFont->GetFontFallback(charpos.m_FallbackFontPosition);
-#if defined(OS_MACOSX)
-      charpos.m_ExtGID = charpos.m_GlyphIndex;
-#endif
-    } else {
-      pCurrentFont = pFont->GetFont();
-      charpos.m_FallbackFontPosition = -1;
-    }
-
-    if (!pFont->IsEmbedded() && !pFont->IsCIDFont())
-      charpos.m_FontCharWidth = pFont->GetCharWidthF(CharCode);
-    else
-      charpos.m_FontCharWidth = 0;
-
-    charpos.m_Origin = CFX_PointF(i > 0 ? charPos[i - 1] : 0, 0);
-    charpos.m_bGlyphAdjust = false;
-
-    float scalingFactor = 1.0f;
-    if (!pFont->IsEmbedded() && pFont->HasFontWidths() && !bVertWriting &&
-        !pCurrentFont->GetSubstFont()->m_bFlagMM) {
-      uint32_t pdfGlyphWidth = pFont->GetCharWidthF(CharCode);
-      uint32_t ftGlyphWidth =
-          pCurrentFont ? pCurrentFont->GetGlyphWidth(charpos.m_GlyphIndex) : 0;
-      if (ftGlyphWidth && pdfGlyphWidth > ftGlyphWidth + 1) {
-        // Move the initial x position by half of the excess (transformed to
-        // text space coordinates).
-        charpos.m_Origin.x +=
-            (pdfGlyphWidth - ftGlyphWidth) * font_size / 2000.0f;
-      } else if (pdfGlyphWidth && ftGlyphWidth &&
-                 pdfGlyphWidth < ftGlyphWidth) {
-        scalingFactor = static_cast<float>(pdfGlyphWidth) / ftGlyphWidth;
-        charpos.m_AdjustMatrix[0] = scalingFactor;
-        charpos.m_AdjustMatrix[1] = 0.0f;
-        charpos.m_AdjustMatrix[2] = 0.0f;
-        charpos.m_AdjustMatrix[3] = 1.0f;
-        charpos.m_bGlyphAdjust = true;
-      }
-    }
-    if (!pCIDFont)
-      continue;
-
-    uint16_t CID = pCIDFont->CIDFromCharCode(CharCode);
-    if (bVertWriting) {
-      charpos.m_Origin = CFX_PointF(0, charpos.m_Origin.x);
-
-      short vx;
-      short vy;
-      pCIDFont->GetVertOrigin(CID, vx, vy);
-      charpos.m_Origin.x -= font_size * vx / 1000;
-      charpos.m_Origin.y -= font_size * vy / 1000;
-    }
-
-    const uint8_t* pTransform = pCIDFont->GetCIDTransform(CID);
-    if (pTransform && !bVert) {
-      charpos.m_AdjustMatrix[0] =
-          pCIDFont->CIDTransformToFloat(pTransform[0]) * scalingFactor;
-      charpos.m_AdjustMatrix[1] =
-          pCIDFont->CIDTransformToFloat(pTransform[1]) * scalingFactor;
-      charpos.m_AdjustMatrix[2] = pCIDFont->CIDTransformToFloat(pTransform[2]);
-      charpos.m_AdjustMatrix[3] = pCIDFont->CIDTransformToFloat(pTransform[3]);
-      charpos.m_Origin.x +=
-          pCIDFont->CIDTransformToFloat(pTransform[4]) * font_size;
-      charpos.m_Origin.y +=
-          pCIDFont->CIDTransformToFloat(pTransform[5]) * font_size;
-      charpos.m_bGlyphAdjust = true;
-    }
-  }
-}
-
-CPDF_CharPosList::~CPDF_CharPosList() = default;
diff --git a/core/fpdfapi/render/cpdf_charposlist.h b/core/fpdfapi/render/cpdf_charposlist.h
deleted file mode 100644
index b06fcfc..0000000
--- a/core/fpdfapi/render/cpdf_charposlist.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFAPI_RENDER_CPDF_CHARPOSLIST_H_
-#define CORE_FPDFAPI_RENDER_CPDF_CHARPOSLIST_H_
-
-#include <vector>
-
-#include "core/fxcrt/fx_system.h"
-
-class CPDF_Font;
-class TextCharPos;
-
-class CPDF_CharPosList {
- public:
-  CPDF_CharPosList(const std::vector<uint32_t>& charCodes,
-                   const std::vector<float>& charPos,
-                   CPDF_Font* pFont,
-                   float font_size);
-  ~CPDF_CharPosList();
-
-  const std::vector<TextCharPos>& Get() const { return m_CharPos; }
-
- private:
-  std::vector<TextCharPos> m_CharPos;
-};
-
-#endif  // CORE_FPDFAPI_RENDER_CPDF_CHARPOSLIST_H_
diff --git a/core/fpdfapi/render/cpdf_devicebuffer.cpp b/core/fpdfapi/render/cpdf_devicebuffer.cpp
index 3426f01..b7722f1 100644
--- a/core/fpdfapi/render/cpdf_devicebuffer.cpp
+++ b/core/fpdfapi/render/cpdf_devicebuffer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,16 +8,17 @@
 
 #include "build/build_config.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/render/cpdf_rendercontext.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
 
 namespace {
 
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
 constexpr bool kScaleDeviceBuffer = false;
 #else
 constexpr bool kScaleDeviceBuffer = true;
@@ -67,7 +68,7 @@
   FX_RECT bitmap_rect =
       m_Matrix.TransformRect(CFX_FloatRect(m_Rect)).GetOuterRect();
   return m_pBitmap->Create(bitmap_rect.Width(), bitmap_rect.Height(),
-                           FXDIB_Argb);
+                           FXDIB_Format::kArgb);
 }
 
 void CPDF_DeviceBuffer::OutputToDevice() {
@@ -83,7 +84,7 @@
   auto pBuffer = pdfium::MakeRetain<CFX_DIBitmap>();
   m_pDevice->CreateCompatibleBitmap(pBuffer, m_pBitmap->GetWidth(),
                                     m_pBitmap->GetHeight());
-  m_pContext->GetBackground(pBuffer, m_pObject.Get(), nullptr, m_Matrix);
+  m_pContext->GetBackground(pBuffer, m_pObject, nullptr, m_Matrix);
   pBuffer->CompositeBitmap(0, 0, pBuffer->GetWidth(), pBuffer->GetHeight(),
                            m_pBitmap, 0, 0, BlendMode::kNormal, nullptr, false);
   m_pDevice->StretchDIBits(pBuffer, m_Rect.left, m_Rect.top, m_Rect.Width(),
diff --git a/core/fpdfapi/render/cpdf_devicebuffer.h b/core/fpdfapi/render/cpdf_devicebuffer.h
index 1f16dbe..1084465 100644
--- a/core/fpdfapi/render/cpdf_devicebuffer.h
+++ b/core/fpdfapi/render/cpdf_devicebuffer.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fpdfapi/render/cpdf_docrenderdata.cpp b/core/fpdfapi/render/cpdf_docrenderdata.cpp
index ba702b7..e63d54d 100644
--- a/core/fpdfapi/render/cpdf_docrenderdata.cpp
+++ b/core/fpdfapi/render/cpdf_docrenderdata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,12 @@
 
 #include "core/fpdfapi/render/cpdf_docrenderdata.h"
 
+#include <stdint.h>
+
+#include <algorithm>
 #include <array>
 #include <memory>
 #include <utility>
-#include <vector>
 
 #include "core/fpdfapi/font/cpdf_type3font.h"
 #include "core/fpdfapi/page/cpdf_dib.h"
@@ -18,6 +20,11 @@
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/render/cpdf_type3cache.h"
+#include "core/fxcrt/fixed_uninit_data_vector.h"
+
+#if BUILDFLAG(IS_WIN)
+#include "core/fxge/win32/cfx_psfonttracker.h"
+#endif
 
 namespace {
 
@@ -47,7 +54,7 @@
 }
 
 RetainPtr<CPDF_TransferFunc> CPDF_DocRenderData::GetTransferFunc(
-    const CPDF_Object* pObj) {
+    RetainPtr<const CPDF_Object> pObj) {
   if (!pObj)
     return nullptr;
 
@@ -60,8 +67,16 @@
   return pFunc;
 }
 
+#if BUILDFLAG(IS_WIN)
+CFX_PSFontTracker* CPDF_DocRenderData::GetPSFontTracker() {
+  if (!m_PSFontTracker)
+    m_PSFontTracker = std::make_unique<CFX_PSFontTracker>();
+  return m_PSFontTracker.get();
+}
+#endif
+
 RetainPtr<CPDF_TransferFunc> CPDF_DocRenderData::CreateTransferFunc(
-    const CPDF_Object* pObj) const {
+    RetainPtr<const CPDF_Object> pObj) const {
   std::unique_ptr<CPDF_Function> pFuncs[3];
   const CPDF_Array* pArray = pObj->AsArray();
   if (pArray) {
@@ -79,42 +94,48 @@
       return nullptr;
   }
 
-  int noutput;
   float output[kMaxOutputs];
-  memset(output, 0, sizeof(output));
+  std::fill(std::begin(output), std::end(output), 0.0f);
 
   bool bIdentity = true;
-  std::vector<uint8_t> samples_r(CPDF_TransferFunc::kChannelSampleSize);
-  std::vector<uint8_t> samples_g(CPDF_TransferFunc::kChannelSampleSize);
-  std::vector<uint8_t> samples_b(CPDF_TransferFunc::kChannelSampleSize);
-  std::array<pdfium::span<uint8_t>, 3> samples = {samples_r, samples_g,
-                                                  samples_b};
-  for (size_t v = 0; v < CPDF_TransferFunc::kChannelSampleSize; ++v) {
-    float input = static_cast<float>(v) / 255.0f;
-    if (pArray) {
+  FixedUninitDataVector<uint8_t> samples_r(
+      CPDF_TransferFunc::kChannelSampleSize);
+  FixedUninitDataVector<uint8_t> samples_g(
+      CPDF_TransferFunc::kChannelSampleSize);
+  FixedUninitDataVector<uint8_t> samples_b(
+      CPDF_TransferFunc::kChannelSampleSize);
+  std::array<pdfium::span<uint8_t>, 3> samples = {samples_r.writable_span(),
+                                                  samples_g.writable_span(),
+                                                  samples_b.writable_span()};
+  if (pArray) {
+    for (size_t v = 0; v < CPDF_TransferFunc::kChannelSampleSize; ++v) {
+      float input = static_cast<float>(v) / 255.0f;
       for (int i = 0; i < 3; ++i) {
         if (pFuncs[i]->CountOutputs() > kMaxOutputs) {
           samples[i][v] = v;
           continue;
         }
-        pFuncs[i]->Call(&input, 1, output, &noutput);
+        pFuncs[i]->Call(pdfium::make_span(&input, 1), output);
         size_t o = FXSYS_roundf(output[0] * 255);
         if (o != v)
           bIdentity = false;
         samples[i][v] = o;
       }
-      continue;
     }
-    if (pFuncs[0]->CountOutputs() <= kMaxOutputs)
-      pFuncs[0]->Call(&input, 1, output, &noutput);
-    size_t o = FXSYS_roundf(output[0] * 255);
-    if (o != v)
-      bIdentity = false;
-    for (auto& channel : samples)
-      channel[v] = o;
+  } else {
+    for (size_t v = 0; v < CPDF_TransferFunc::kChannelSampleSize; ++v) {
+      float input = static_cast<float>(v) / 255.0f;
+      if (pFuncs[0]->CountOutputs() <= kMaxOutputs)
+        pFuncs[0]->Call(pdfium::make_span(&input, 1), output);
+      size_t o = FXSYS_roundf(output[0] * 255);
+      if (o != v)
+        bIdentity = false;
+      for (auto& channel : samples)
+        channel[v] = o;
+    }
   }
 
-  return pdfium::MakeRetain<CPDF_TransferFunc>(
-      GetDocument(), bIdentity, std::move(samples_r), std::move(samples_g),
-      std::move(samples_b));
+  return pdfium::MakeRetain<CPDF_TransferFunc>(bIdentity, std::move(samples_r),
+                                               std::move(samples_g),
+                                               std::move(samples_b));
 }
diff --git a/core/fpdfapi/render/cpdf_docrenderdata.h b/core/fpdfapi/render/cpdf_docrenderdata.h
index 32b90a3..1208931 100644
--- a/core/fpdfapi/render/cpdf_docrenderdata.h
+++ b/core/fpdfapi/render/cpdf_docrenderdata.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,18 +7,28 @@
 #ifndef CORE_FPDFAPI_RENDER_CPDF_DOCRENDERDATA_H_
 #define CORE_FPDFAPI_RENDER_CPDF_DOCRENDERDATA_H_
 
+#include <functional>
 #include <map>
 
+#include "build/build_config.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
 
+#if BUILDFLAG(IS_WIN)
+#include <memory>
+#endif
+
 class CPDF_Font;
 class CPDF_Object;
 class CPDF_TransferFunc;
 class CPDF_Type3Cache;
 class CPDF_Type3Font;
 
+#if BUILDFLAG(IS_WIN)
+class CFX_PSFontTracker;
+#endif
+
 class CPDF_DocRenderData : public CPDF_Document::RenderDataIface {
  public:
   static CPDF_DocRenderData* FromDocument(const CPDF_Document* pDoc);
@@ -30,17 +40,29 @@
   CPDF_DocRenderData& operator=(const CPDF_DocRenderData&) = delete;
 
   RetainPtr<CPDF_Type3Cache> GetCachedType3(CPDF_Type3Font* pFont);
-  RetainPtr<CPDF_TransferFunc> GetTransferFunc(const CPDF_Object* pObj);
+  RetainPtr<CPDF_TransferFunc> GetTransferFunc(
+      RetainPtr<const CPDF_Object> pObj);
+
+#if BUILDFLAG(IS_WIN)
+  CFX_PSFontTracker* GetPSFontTracker();
+#endif
 
  protected:
   // protected for use by test subclasses.
   RetainPtr<CPDF_TransferFunc> CreateTransferFunc(
-      const CPDF_Object* pObj) const;
+      RetainPtr<const CPDF_Object> pObj) const;
 
  private:
+  // TODO(tsepez): investigate this map outliving its font keys.
   std::map<CPDF_Font*, ObservedPtr<CPDF_Type3Cache>> m_Type3FaceMap;
-  std::map<const CPDF_Object*, ObservedPtr<CPDF_TransferFunc>>
+  std::map<RetainPtr<const CPDF_Object>,
+           ObservedPtr<CPDF_TransferFunc>,
+           std::less<>>
       m_TransferFuncMap;
+
+#if BUILDFLAG(IS_WIN)
+  std::unique_ptr<CFX_PSFontTracker> m_PSFontTracker;
+#endif
 };
 
 #endif  // CORE_FPDFAPI_RENDER_CPDF_DOCRENDERDATA_H_
diff --git a/core/fpdfapi/render/cpdf_docrenderdata_unittest.cpp b/core/fpdfapi/render/cpdf_docrenderdata_unittest.cpp
index 2fb25b6..df7bb1c 100644
--- a/core/fpdfapi/render/cpdf_docrenderdata_unittest.cpp
+++ b/core/fpdfapi/render/cpdf_docrenderdata_unittest.cpp
@@ -1,9 +1,10 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/render/cpdf_docrenderdata.h"
 
+#include <iterator>
 #include <memory>
 #include <utility>
 
@@ -12,9 +13,8 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/data_vector.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -73,26 +73,23 @@
 RetainPtr<CPDF_Stream> CreateType0FunctionStream() {
   auto func_dict = pdfium::MakeRetain<CPDF_Dictionary>();
   func_dict->SetNewFor<CPDF_Number>("FunctionType", 0);
-
-  CPDF_Array* domain_array = func_dict->SetNewFor<CPDF_Array>("Domain");
-  domain_array->AddNew<CPDF_Number>(0);
-  domain_array->AddNew<CPDF_Number>(1);
-
-  CPDF_Array* range_array = func_dict->SetNewFor<CPDF_Array>("Range");
-  range_array->AddNew<CPDF_Number>(0);
-  range_array->AddNew<CPDF_Number>(0.5f);
-
-  CPDF_Array* size_array = func_dict->SetNewFor<CPDF_Array>("Size");
-  size_array->AddNew<CPDF_Number>(4);
-
   func_dict->SetNewFor<CPDF_Number>("BitsPerSample", 8);
 
-  static const char content[] = "1234";
-  size_t len = FX_ArraySize(content);
-  std::unique_ptr<uint8_t, FxFreeDeleter> buf(FX_Alloc(uint8_t, len));
-  memcpy(buf.get(), content, len);
-  return pdfium::MakeRetain<CPDF_Stream>(std::move(buf), len,
-                                         std::move(func_dict));
+  auto domain_array = func_dict->SetNewFor<CPDF_Array>("Domain");
+  domain_array->AppendNew<CPDF_Number>(0);
+  domain_array->AppendNew<CPDF_Number>(1);
+
+  auto range_array = func_dict->SetNewFor<CPDF_Array>("Range");
+  range_array->AppendNew<CPDF_Number>(0);
+  range_array->AppendNew<CPDF_Number>(0.5f);
+
+  auto size_array = func_dict->SetNewFor<CPDF_Array>("Size");
+  size_array->AppendNew<CPDF_Number>(4);
+
+  static constexpr uint8_t kContents[] = "1234";
+  return pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(std::begin(kContents), std::end(kContents)),
+      std::move(func_dict));
 }
 
 RetainPtr<CPDF_Dictionary> CreateType2FunctionDict() {
@@ -100,19 +97,19 @@
   func_dict->SetNewFor<CPDF_Number>("FunctionType", 2);
   func_dict->SetNewFor<CPDF_Number>("N", 1);
 
-  CPDF_Array* domain_array = func_dict->SetNewFor<CPDF_Array>("Domain");
-  domain_array->AddNew<CPDF_Number>(0);
-  domain_array->AddNew<CPDF_Number>(1);
+  auto domain_array = func_dict->SetNewFor<CPDF_Array>("Domain");
+  domain_array->AppendNew<CPDF_Number>(0);
+  domain_array->AppendNew<CPDF_Number>(1);
 
-  CPDF_Array* c0_array = func_dict->SetNewFor<CPDF_Array>("C0");
-  c0_array->AddNew<CPDF_Number>(0.1f);
-  c0_array->AddNew<CPDF_Number>(0.2f);
-  c0_array->AddNew<CPDF_Number>(0.8f);
+  auto c0_array = func_dict->SetNewFor<CPDF_Array>("C0");
+  c0_array->AppendNew<CPDF_Number>(0.1f);
+  c0_array->AppendNew<CPDF_Number>(0.2f);
+  c0_array->AppendNew<CPDF_Number>(0.8f);
 
-  CPDF_Array* c1_array = func_dict->SetNewFor<CPDF_Array>("C1");
-  c1_array->AddNew<CPDF_Number>(0.05f);
-  c1_array->AddNew<CPDF_Number>(0.01f);
-  c1_array->AddNew<CPDF_Number>(0.4f);
+  auto c1_array = func_dict->SetNewFor<CPDF_Array>("C1");
+  c1_array->AppendNew<CPDF_Number>(0.05f);
+  c1_array->AppendNew<CPDF_Number>(0.01f);
+  c1_array->AppendNew<CPDF_Number>(0.4f);
 
   return func_dict;
 }
@@ -121,49 +118,45 @@
   auto func_dict = pdfium::MakeRetain<CPDF_Dictionary>();
   func_dict->SetNewFor<CPDF_Number>("FunctionType", 4);
 
-  CPDF_Array* domain_array = func_dict->SetNewFor<CPDF_Array>("Domain");
-  domain_array->AddNew<CPDF_Number>(0);
-  domain_array->AddNew<CPDF_Number>(1);
+  auto domain_array = func_dict->SetNewFor<CPDF_Array>("Domain");
+  domain_array->AppendNew<CPDF_Number>(0);
+  domain_array->AppendNew<CPDF_Number>(1);
 
-  CPDF_Array* range_array = func_dict->SetNewFor<CPDF_Array>("Range");
-  range_array->AddNew<CPDF_Number>(-1);
-  range_array->AddNew<CPDF_Number>(1);
+  auto range_array = func_dict->SetNewFor<CPDF_Array>("Range");
+  range_array->AppendNew<CPDF_Number>(-1);
+  range_array->AppendNew<CPDF_Number>(1);
 
-  static const char content[] = "{ 360 mul sin 2 div }";
-  size_t len = FX_ArraySize(content);
-  std::unique_ptr<uint8_t, FxFreeDeleter> buf(FX_Alloc(uint8_t, len));
-  memcpy(buf.get(), content, len);
-  return pdfium::MakeRetain<CPDF_Stream>(std::move(buf), len,
-                                         std::move(func_dict));
+  static constexpr uint8_t kContents[] = "{ 360 mul sin 2 div }";
+  return pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(std::begin(kContents), std::end(kContents)),
+      std::move(func_dict));
 }
 
 RetainPtr<CPDF_Stream> CreateBadType4FunctionStream() {
   auto func_dict = pdfium::MakeRetain<CPDF_Dictionary>();
   func_dict->SetNewFor<CPDF_Number>("FunctionType", 4);
 
-  CPDF_Array* domain_array = func_dict->SetNewFor<CPDF_Array>("Domain");
-  domain_array->AddNew<CPDF_Number>(0);
-  domain_array->AddNew<CPDF_Number>(1);
+  auto domain_array = func_dict->SetNewFor<CPDF_Array>("Domain");
+  domain_array->AppendNew<CPDF_Number>(0);
+  domain_array->AppendNew<CPDF_Number>(1);
 
-  CPDF_Array* range_array = func_dict->SetNewFor<CPDF_Array>("Range");
-  range_array->AddNew<CPDF_Number>(-1);
-  range_array->AddNew<CPDF_Number>(1);
+  auto range_array = func_dict->SetNewFor<CPDF_Array>("Range");
+  range_array->AppendNew<CPDF_Number>(-1);
+  range_array->AppendNew<CPDF_Number>(1);
 
-  static const char content[] = "garbage";
-  size_t len = FX_ArraySize(content);
-  std::unique_ptr<uint8_t, FxFreeDeleter> buf(FX_Alloc(uint8_t, len));
-  memcpy(buf.get(), content, len);
-  return pdfium::MakeRetain<CPDF_Stream>(std::move(buf), len,
-                                         std::move(func_dict));
+  static constexpr uint8_t kContents[] = "garbage";
+  return pdfium::MakeRetain<CPDF_Stream>(
+      DataVector<uint8_t>(std::begin(kContents), std::end(kContents)),
+      std::move(func_dict));
 }
 
 class TestDocRenderData : public CPDF_DocRenderData {
  public:
-  TestDocRenderData() : CPDF_DocRenderData() {}
+  TestDocRenderData() = default;
 
   RetainPtr<CPDF_TransferFunc> CreateTransferFuncForTesting(
-      const CPDF_Object* pObj) const {
-    return CreateTransferFunc(pObj);
+      RetainPtr<const CPDF_Object> pObj) const {
+    return CreateTransferFunc(std::move(pObj));
   }
 };
 
@@ -171,18 +164,18 @@
   RetainPtr<CPDF_Dictionary> func_dict = CreateType2FunctionDict();
 
   TestDocRenderData render_data;
-  auto func = render_data.CreateTransferFuncForTesting(func_dict.Get());
+  auto func = render_data.CreateTransferFuncForTesting(func_dict);
   ASSERT_TRUE(func);
   EXPECT_FALSE(func->GetIdentity());
 
   auto r_samples = func->GetSamplesR();
   auto g_samples = func->GetSamplesG();
   auto b_samples = func->GetSamplesB();
-  ASSERT_EQ(FX_ArraySize(kExpectedType2FunctionSamples), r_samples.size());
-  ASSERT_EQ(FX_ArraySize(kExpectedType2FunctionSamples), g_samples.size());
-  ASSERT_EQ(FX_ArraySize(kExpectedType2FunctionSamples), b_samples.size());
+  ASSERT_EQ(std::size(kExpectedType2FunctionSamples), r_samples.size());
+  ASSERT_EQ(std::size(kExpectedType2FunctionSamples), g_samples.size());
+  ASSERT_EQ(std::size(kExpectedType2FunctionSamples), b_samples.size());
 
-  for (size_t i = 0; i < FX_ArraySize(kExpectedType2FunctionSamples); ++i) {
+  for (size_t i = 0; i < std::size(kExpectedType2FunctionSamples); ++i) {
     EXPECT_EQ(kExpectedType2FunctionSamples[i], r_samples[i]);
     EXPECT_EQ(kExpectedType2FunctionSamples[i], g_samples[i]);
     EXPECT_EQ(kExpectedType2FunctionSamples[i], b_samples[i]);
@@ -202,23 +195,23 @@
 
 TEST(CPDF_DocRenderDataTest, TransferFunctionArray) {
   auto func_array = pdfium::MakeRetain<CPDF_Array>();
-  func_array->Add(CreateType0FunctionStream());
-  func_array->Add(CreateType2FunctionDict());
-  func_array->Add(CreateType4FunctionStream());
+  func_array->Append(CreateType0FunctionStream());
+  func_array->Append(CreateType2FunctionDict());
+  func_array->Append(CreateType4FunctionStream());
 
   TestDocRenderData render_data;
-  auto func = render_data.CreateTransferFuncForTesting(func_array.Get());
+  auto func = render_data.CreateTransferFuncForTesting(func_array);
   ASSERT_TRUE(func);
   EXPECT_FALSE(func->GetIdentity());
 
   auto r_samples = func->GetSamplesR();
   auto g_samples = func->GetSamplesG();
   auto b_samples = func->GetSamplesB();
-  ASSERT_EQ(FX_ArraySize(kExpectedType0FunctionSamples), r_samples.size());
-  ASSERT_EQ(FX_ArraySize(kExpectedType2FunctionSamples), g_samples.size());
-  ASSERT_EQ(FX_ArraySize(kExpectedType4FunctionSamples), b_samples.size());
+  ASSERT_EQ(std::size(kExpectedType0FunctionSamples), r_samples.size());
+  ASSERT_EQ(std::size(kExpectedType2FunctionSamples), g_samples.size());
+  ASSERT_EQ(std::size(kExpectedType4FunctionSamples), b_samples.size());
 
-  for (size_t i = 0; i < FX_ArraySize(kExpectedType2FunctionSamples); ++i) {
+  for (size_t i = 0; i < std::size(kExpectedType2FunctionSamples); ++i) {
     EXPECT_EQ(kExpectedType0FunctionSamples[i], r_samples[i]);
     EXPECT_EQ(kExpectedType2FunctionSamples[i], g_samples[i]);
     EXPECT_EQ(kExpectedType4FunctionSamples[i], b_samples[i]);
@@ -241,7 +234,7 @@
     auto func_stream = CreateBadType4FunctionStream();
 
     TestDocRenderData render_data;
-    auto func = render_data.CreateTransferFuncForTesting(func_stream.Get());
+    auto func = render_data.CreateTransferFuncForTesting(func_stream);
     EXPECT_FALSE(func);
   }
 
@@ -249,18 +242,18 @@
     auto func_array = pdfium::MakeRetain<CPDF_Array>();
 
     TestDocRenderData render_data;
-    auto func = render_data.CreateTransferFuncForTesting(func_array.Get());
+    auto func = render_data.CreateTransferFuncForTesting(func_array);
     EXPECT_FALSE(func);
   }
 
   {
     auto func_array = pdfium::MakeRetain<CPDF_Array>();
-    func_array->Add(CreateType0FunctionStream());
-    func_array->Add(CreateType2FunctionDict());
-    func_array->Add(CreateBadType4FunctionStream());
+    func_array->Append(CreateType0FunctionStream());
+    func_array->Append(CreateType2FunctionDict());
+    func_array->Append(CreateBadType4FunctionStream());
 
     TestDocRenderData render_data;
-    auto func = render_data.CreateTransferFuncForTesting(func_array.Get());
+    auto func = render_data.CreateTransferFuncForTesting(func_array);
     EXPECT_FALSE(func);
   }
 }
diff --git a/core/fpdfapi/render/cpdf_imagecacheentry.cpp b/core/fpdfapi/render/cpdf_imagecacheentry.cpp
deleted file mode 100644
index 3805b2d..0000000
--- a/core/fpdfapi/render/cpdf_imagecacheentry.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fpdfapi/render/cpdf_imagecacheentry.h"
-
-#include <memory>
-#include <utility>
-
-#include "core/fpdfapi/page/cpdf_dib.h"
-#include "core/fpdfapi/page/cpdf_image.h"
-#include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
-#include "core/fpdfapi/render/cpdf_rendercontext.h"
-#include "core/fpdfapi/render/cpdf_renderstatus.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-
-namespace {
-
-uint32_t GetEstimatedImageSize(const RetainPtr<CFX_DIBBase>& pDIB) {
-  if (!pDIB || !pDIB->GetBuffer())
-    return 0;
-
-  int height = pDIB->GetHeight();
-  ASSERT(pdfium::base::IsValueInRangeForNumericType<uint32_t>(height));
-  return static_cast<uint32_t>(height) * pDIB->GetPitch() +
-         pDIB->GetPaletteSize() * 4;
-}
-
-}  // namespace
-
-CPDF_ImageCacheEntry::CPDF_ImageCacheEntry(CPDF_Document* pDoc,
-                                           const RetainPtr<CPDF_Image>& pImage)
-    : m_pDocument(pDoc), m_pImage(pImage) {}
-
-CPDF_ImageCacheEntry::~CPDF_ImageCacheEntry() = default;
-
-void CPDF_ImageCacheEntry::Reset() {
-  m_pCachedBitmap.Reset();
-  CalcSize();
-}
-
-RetainPtr<CFX_DIBBase> CPDF_ImageCacheEntry::DetachBitmap() {
-  return std::move(m_pCurBitmap);
-}
-
-RetainPtr<CFX_DIBBase> CPDF_ImageCacheEntry::DetachMask() {
-  return std::move(m_pCurMask);
-}
-
-CPDF_DIB::LoadState CPDF_ImageCacheEntry::StartGetCachedBitmap(
-    const CPDF_Dictionary* pFormResources,
-    CPDF_Dictionary* pPageResources,
-    bool bStdCS,
-    uint32_t GroupFamily,
-    bool bLoadMask,
-    CPDF_RenderStatus* pRenderStatus) {
-  ASSERT(pRenderStatus);
-
-  if (m_pCachedBitmap) {
-    m_pCurBitmap = m_pCachedBitmap;
-    m_pCurMask = m_pCachedMask;
-    return CPDF_DIB::LoadState::kSuccess;
-  }
-
-  m_pCurBitmap = pdfium::MakeRetain<CPDF_DIB>();
-  CPDF_DIB::LoadState ret = m_pCurBitmap.As<CPDF_DIB>()->StartLoadDIBBase(
-      m_pDocument.Get(), m_pImage->GetStream(), true, pFormResources,
-      pPageResources, bStdCS, GroupFamily, bLoadMask);
-  if (ret == CPDF_DIB::LoadState::kContinue)
-    return CPDF_DIB::LoadState::kContinue;
-
-  if (ret == CPDF_DIB::LoadState::kSuccess)
-    ContinueGetCachedBitmap(pRenderStatus);
-  else
-    m_pCurBitmap.Reset();
-  return CPDF_DIB::LoadState::kFail;
-}
-
-bool CPDF_ImageCacheEntry::Continue(PauseIndicatorIface* pPause,
-                                    CPDF_RenderStatus* pRenderStatus) {
-  CPDF_DIB::LoadState ret =
-      m_pCurBitmap.As<CPDF_DIB>()->ContinueLoadDIBBase(pPause);
-  if (ret == CPDF_DIB::LoadState::kContinue)
-    return true;
-
-  if (ret == CPDF_DIB::LoadState::kSuccess)
-    ContinueGetCachedBitmap(pRenderStatus);
-  else
-    m_pCurBitmap.Reset();
-  return false;
-}
-
-void CPDF_ImageCacheEntry::ContinueGetCachedBitmap(
-    CPDF_RenderStatus* pRenderStatus) {
-  m_MatteColor = m_pCurBitmap.As<CPDF_DIB>()->GetMatteColor();
-  m_pCurMask = m_pCurBitmap.As<CPDF_DIB>()->DetachMask();
-  CPDF_RenderContext* pContext = pRenderStatus->GetContext();
-  CPDF_PageRenderCache* pPageRenderCache = pContext->GetPageCache();
-  m_dwTimeCount = pPageRenderCache->GetTimeCount();
-  if (m_pCurBitmap->GetPitch() * m_pCurBitmap->GetHeight() < kHugeImageSize) {
-    m_pCachedBitmap = m_pCurBitmap->Clone(nullptr);
-    m_pCurBitmap.Reset();
-  } else {
-    m_pCachedBitmap = m_pCurBitmap;
-  }
-  if (m_pCurMask) {
-    m_pCachedMask = m_pCurMask->Clone(nullptr);
-    m_pCurMask.Reset();
-  }
-  m_pCurBitmap = m_pCachedBitmap;
-  m_pCurMask = m_pCachedMask;
-  CalcSize();
-}
-
-void CPDF_ImageCacheEntry::CalcSize() {
-  m_dwCacheSize = GetEstimatedImageSize(m_pCachedBitmap) +
-                  GetEstimatedImageSize(m_pCachedMask);
-}
diff --git a/core/fpdfapi/render/cpdf_imagecacheentry.h b/core/fpdfapi/render/cpdf_imagecacheentry.h
deleted file mode 100644
index c3f373e..0000000
--- a/core/fpdfapi/render/cpdf_imagecacheentry.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFAPI_RENDER_CPDF_IMAGECACHEENTRY_H_
-#define CORE_FPDFAPI_RENDER_CPDF_IMAGECACHEENTRY_H_
-
-#include "core/fpdfapi/page/cpdf_dib.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class CPDF_Dictionary;
-class CPDF_Document;
-class CPDF_Image;
-class CPDF_RenderStatus;
-class PauseIndicatorIface;
-
-class CPDF_ImageCacheEntry {
- public:
-  CPDF_ImageCacheEntry(CPDF_Document* pDoc,
-                       const RetainPtr<CPDF_Image>& pImage);
-  ~CPDF_ImageCacheEntry();
-
-  void Reset();
-  uint32_t EstimateSize() const { return m_dwCacheSize; }
-  uint32_t GetTimeCount() const { return m_dwTimeCount; }
-  CPDF_Image* GetImage() const { return m_pImage.Get(); }
-
-  CPDF_DIB::LoadState StartGetCachedBitmap(
-      const CPDF_Dictionary* pFormResources,
-      CPDF_Dictionary* pPageResources,
-      bool bStdCS,
-      uint32_t GroupFamily,
-      bool bLoadMask,
-      CPDF_RenderStatus* pRenderStatus);
-
-  // Returns whether to Continue() or not.
-  bool Continue(PauseIndicatorIface* pPause, CPDF_RenderStatus* pRenderStatus);
-
-  RetainPtr<CFX_DIBBase> DetachBitmap();
-  RetainPtr<CFX_DIBBase> DetachMask();
-
-  int m_dwTimeCount = 0;
-  uint32_t m_MatteColor = 0;
-
- private:
-  void ContinueGetCachedBitmap(CPDF_RenderStatus* pRenderStatus);
-  void CalcSize();
-
-  UnownedPtr<CPDF_Document> const m_pDocument;
-  RetainPtr<CPDF_Image> const m_pImage;
-  RetainPtr<CFX_DIBBase> m_pCurBitmap;
-  RetainPtr<CFX_DIBBase> m_pCurMask;
-  RetainPtr<CFX_DIBBase> m_pCachedBitmap;
-  RetainPtr<CFX_DIBBase> m_pCachedMask;
-  uint32_t m_dwCacheSize = 0;
-};
-
-#endif  // CORE_FPDFAPI_RENDER_CPDF_IMAGECACHEENTRY_H_
diff --git a/core/fpdfapi/render/cpdf_imageloader.cpp b/core/fpdfapi/render/cpdf_imageloader.cpp
deleted file mode 100644
index fac4799..0000000
--- a/core/fpdfapi/render/cpdf_imageloader.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fpdfapi/render/cpdf_imageloader.h"
-
-#include "core/fpdfapi/page/cpdf_dib.h"
-#include "core/fpdfapi/page/cpdf_image.h"
-#include "core/fpdfapi/page/cpdf_imageobject.h"
-#include "core/fpdfapi/page/cpdf_transferfunc.h"
-#include "core/fpdfapi/render/cpdf_imagecacheentry.h"
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
-#include "core/fpdfapi/render/cpdf_renderstatus.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-
-CPDF_ImageLoader::CPDF_ImageLoader() = default;
-
-CPDF_ImageLoader::~CPDF_ImageLoader() = default;
-
-bool CPDF_ImageLoader::Start(CPDF_ImageObject* pImage,
-                             CPDF_PageRenderCache* pCache,
-                             bool bStdCS,
-                             uint32_t GroupFamily,
-                             bool bLoadMask,
-                             CPDF_RenderStatus* pRenderStatus) {
-  m_pCache = pCache;
-  m_pImageObject = pImage;
-  bool ret;
-  if (pCache) {
-    ret = pCache->StartGetCachedBitmap(m_pImageObject->GetImage(), bStdCS,
-                                       GroupFamily, bLoadMask, pRenderStatus);
-  } else {
-    ret = m_pImageObject->GetImage()->StartLoadDIBBase(
-        pRenderStatus->GetFormResource(), pRenderStatus->GetPageResource(),
-        bStdCS, GroupFamily, bLoadMask);
-  }
-  if (!ret)
-    HandleFailure();
-  return ret;
-}
-
-bool CPDF_ImageLoader::Continue(PauseIndicatorIface* pPause,
-                                CPDF_RenderStatus* pRenderStatus) {
-  bool ret = m_pCache ? m_pCache->Continue(pPause, pRenderStatus)
-                      : m_pImageObject->GetImage()->Continue(pPause);
-  if (!ret)
-    HandleFailure();
-  return ret;
-}
-
-RetainPtr<CFX_DIBBase> CPDF_ImageLoader::TranslateImage(
-    const RetainPtr<CPDF_TransferFunc>& pTransferFunc) {
-  ASSERT(pTransferFunc);
-  ASSERT(!pTransferFunc->GetIdentity());
-
-  m_pBitmap = pTransferFunc->TranslateImage(m_pBitmap);
-  if (m_bCached && m_pMask)
-    m_pMask = m_pMask->Clone(nullptr);
-  m_bCached = false;
-  return m_pBitmap;
-}
-
-void CPDF_ImageLoader::HandleFailure() {
-  if (m_pCache) {
-    CPDF_ImageCacheEntry* entry = m_pCache->GetCurImageCacheEntry();
-    m_bCached = true;
-    m_pBitmap = entry->DetachBitmap();
-    m_pMask = entry->DetachMask();
-    m_MatteColor = entry->m_MatteColor;
-    return;
-  }
-  RetainPtr<CPDF_Image> pImage = m_pImageObject->GetImage();
-  m_bCached = false;
-  m_pBitmap = pImage->DetachBitmap();
-  m_pMask = pImage->DetachMask();
-  m_MatteColor = pImage->m_MatteColor;
-}
diff --git a/core/fpdfapi/render/cpdf_imageloader.h b/core/fpdfapi/render/cpdf_imageloader.h
deleted file mode 100644
index 7e12c01..0000000
--- a/core/fpdfapi/render/cpdf_imageloader.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFAPI_RENDER_CPDF_IMAGELOADER_H_
-#define CORE_FPDFAPI_RENDER_CPDF_IMAGELOADER_H_
-
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class CFX_DIBBase;
-class CPDF_ImageObject;
-class CPDF_PageRenderCache;
-class CPDF_RenderStatus;
-class CPDF_TransferFunc;
-class PauseIndicatorIface;
-
-class CPDF_ImageLoader {
- public:
-  CPDF_ImageLoader();
-  ~CPDF_ImageLoader();
-
-  bool Start(CPDF_ImageObject* pImage,
-             CPDF_PageRenderCache* pCache,
-             bool bStdCS,
-             uint32_t GroupFamily,
-             bool bLoadMask,
-             CPDF_RenderStatus* pRenderStatus);
-  bool Continue(PauseIndicatorIface* pPause, CPDF_RenderStatus* pRenderStatus);
-
-  RetainPtr<CFX_DIBBase> TranslateImage(
-      const RetainPtr<CPDF_TransferFunc>& pTransferFunc);
-
-  const RetainPtr<CFX_DIBBase>& GetBitmap() const { return m_pBitmap; }
-  const RetainPtr<CFX_DIBBase>& GetMask() const { return m_pMask; }
-  uint32_t MatteColor() const { return m_MatteColor; }
-
- private:
-  void HandleFailure();
-
-  uint32_t m_MatteColor = 0;
-  bool m_bCached = false;
-  RetainPtr<CFX_DIBBase> m_pBitmap;
-  RetainPtr<CFX_DIBBase> m_pMask;
-  UnownedPtr<CPDF_PageRenderCache> m_pCache;
-  UnownedPtr<CPDF_ImageObject> m_pImageObject;
-};
-
-#endif  // CORE_FPDFAPI_RENDER_CPDF_IMAGELOADER_H_
diff --git a/core/fpdfapi/render/cpdf_imagerenderer.cpp b/core/fpdfapi/render/cpdf_imagerenderer.cpp
index 2d3ec4c..a773cd2 100644
--- a/core/fpdfapi/render/cpdf_imagerenderer.cpp
+++ b/core/fpdfapi/render/cpdf_imagerenderer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,40 +6,40 @@
 
 #include "core/fpdfapi/render/cpdf_imagerenderer.h"
 
+#include <math.h>
+
 #include <algorithm>
 #include <memory>
+#include <utility>
 
 #include "core/fpdfapi/page/cpdf_dib.h"
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
 #include "core/fpdfapi/page/cpdf_image.h"
+#include "core/fpdfapi/page/cpdf_imageloader.h"
 #include "core/fpdfapi/page/cpdf_imageobject.h"
 #include "core/fpdfapi/page/cpdf_occontext.h"
 #include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/page/cpdf_shadingpattern.h"
 #include "core/fpdfapi/page/cpdf_tilingpattern.h"
 #include "core/fpdfapi/page/cpdf_transferfunc.h"
-#include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
+#include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fpdfapi/render/cpdf_rendercontext.h"
 #include "core/fpdfapi/render/cpdf_renderstatus.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/maybe_owned.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/dib/cfx_dibbase.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/dib/cfx_imagestretcher.h"
 #include "core/fxge/dib/cfx_imagetransformer.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-#ifdef _SKIA_SUPPORT_
-#include "core/fxge/skia/fx_skia_device.h"
-#endif
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -52,9 +52,21 @@
   return safe_val.ValueOrDefault(kLimit) >= kLimit;
 }
 
+void ClearBitmap(CFX_DefaultRenderDevice& bitmap_device, uint32_t color) {
+#if defined(_SKIA_SUPPORT_)
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    bitmap_device.Clear(color);
+    return;
+  }
+#endif
+  bitmap_device.GetBitmap()->Clear(color);
+}
+
 }  // namespace
 
-CPDF_ImageRenderer::CPDF_ImageRenderer() = default;
+CPDF_ImageRenderer::CPDF_ImageRenderer(CPDF_RenderStatus* pStatus)
+    : m_pRenderStatus(pStatus),
+      m_pLoader(std::make_unique<CPDF_ImageLoader>()) {}
 
 CPDF_ImageRenderer::~CPDF_ImageRenderer() = default;
 
@@ -62,47 +74,51 @@
   if (!GetUnitRect().has_value())
     return false;
 
-  if (m_Loader.Start(m_pImageObject.Get(),
-                     m_pRenderStatus->GetContext()->GetPageCache(), m_bStdCS,
-                     m_pRenderStatus->GetGroupFamily(),
-                     m_pRenderStatus->GetLoadMask(), m_pRenderStatus.Get())) {
-    m_Mode = Mode::kDefault;
-    return true;
+  if (!m_pLoader->Start(
+          m_pImageObject, m_pRenderStatus->GetContext()->GetPageCache(),
+          m_pRenderStatus->GetFormResource(),
+          m_pRenderStatus->GetPageResource(), m_bStdCS,
+          m_pRenderStatus->GetGroupFamily(), m_pRenderStatus->GetLoadMask(),
+          {m_pRenderStatus->GetRenderDevice()->GetWidth(),
+           m_pRenderStatus->GetRenderDevice()->GetHeight()})) {
+    return false;
   }
-  return false;
+  m_Mode = Mode::kDefault;
+  return true;
 }
 
 bool CPDF_ImageRenderer::StartRenderDIBBase() {
-  if (!m_Loader.GetBitmap())
+  if (!m_pLoader->GetBitmap())
     return false;
 
   CPDF_GeneralState& state = m_pImageObject->m_GeneralState;
   m_BitmapAlpha = FXSYS_roundf(255 * state.GetFillAlpha());
-  m_pDIBBase = m_Loader.GetBitmap();
+  m_pDIBBase = m_pLoader->GetBitmap();
   if (GetRenderOptions().ColorModeIs(CPDF_RenderOptions::kAlpha) &&
-      !m_Loader.GetMask()) {
+      !m_pLoader->GetMask()) {
     return StartBitmapAlpha();
   }
-  if (state.GetTR()) {
+  RetainPtr<const CPDF_Object> pTR = state.GetTR();
+  if (pTR) {
     if (!state.GetTransferFunc())
-      state.SetTransferFunc(m_pRenderStatus->GetTransferFunc(state.GetTR()));
+      state.SetTransferFunc(m_pRenderStatus->GetTransferFunc(std::move(pTR)));
 
     if (state.GetTransferFunc() && !state.GetTransferFunc()->GetIdentity())
-      m_pDIBBase = m_Loader.TranslateImage(state.GetTransferFunc());
+      m_pDIBBase = m_pLoader->TranslateImage(state.GetTransferFunc());
   }
   m_FillArgb = 0;
   m_bPatternColor = false;
   m_pPattern = nullptr;
-  if (m_pDIBBase->IsAlphaMask()) {
+  if (m_pDIBBase->IsMaskFormat()) {
     const CPDF_Color* pColor = m_pImageObject->m_ColorState.GetFillColor();
     if (pColor && pColor->IsPattern()) {
-      m_pPattern.Reset(pColor->GetPattern());
+      m_pPattern = pColor->GetPattern();
       if (m_pPattern)
         m_bPatternColor = true;
     }
-    m_FillArgb = m_pRenderStatus->GetFillArgb(m_pImageObject.Get());
+    m_FillArgb = m_pRenderStatus->GetFillArgb(m_pImageObject);
   } else if (GetRenderOptions().ColorModeIs(CPDF_RenderOptions::kGray)) {
-    RetainPtr<CFX_DIBitmap> pClone = m_pDIBBase->Clone(nullptr);
+    RetainPtr<CFX_DIBitmap> pClone = m_pDIBBase->Realize();
     if (!pClone)
       return false;
 
@@ -123,7 +139,7 @@
   else if (m_pImageObject->GetImage()->IsInterpol())
     m_ResampleOptions.bInterpolateBilinear = true;
 
-  if (m_Loader.GetMask())
+  if (m_pLoader->GetMask())
     return DrawMaskedImage();
 
   if (m_bPatternColor)
@@ -142,62 +158,58 @@
   } else {
     pDocument = m_pImageObject->GetImage()->GetDocument();
   }
-  CPDF_Dictionary* pPageResources =
-      pPage ? pPage->m_pPageResources.Get() : nullptr;
-  CPDF_Object* pCSObj =
-      m_pImageObject->GetImage()->GetStream()->GetDict()->GetDirectObjectFor(
-          "ColorSpace");
+  RetainPtr<const CPDF_Dictionary> pPageResources =
+      pPage ? pPage->GetPageResources() : nullptr;
+  RetainPtr<const CPDF_Dictionary> pStreamDict =
+      m_pImageObject->GetImage()->GetStream()->GetDict();
+  RetainPtr<const CPDF_Object> pCSObj =
+      pStreamDict->GetDirectObjectFor("ColorSpace");
   auto* pData = CPDF_DocPageData::FromDocument(pDocument);
   RetainPtr<CPDF_ColorSpace> pColorSpace =
-      pData->GetColorSpace(pCSObj, pPageResources);
+      pData->GetColorSpace(pCSObj.Get(), pPageResources);
   if (pColorSpace) {
-    int format = pColorSpace->GetFamily();
-    if (format == PDFCS_DEVICECMYK || format == PDFCS_SEPARATION ||
-        format == PDFCS_DEVICEN) {
+    CPDF_ColorSpace::Family format = pColorSpace->GetFamily();
+    if (format == CPDF_ColorSpace::Family::kDeviceCMYK ||
+        format == CPDF_ColorSpace::Family::kSeparation ||
+        format == CPDF_ColorSpace::Family::kDeviceN) {
       m_BlendType = BlendMode::kDarken;
     }
   }
   return StartDIBBase();
 }
 
-bool CPDF_ImageRenderer::Start(CPDF_RenderStatus* pStatus,
-                               CPDF_ImageObject* pImageObject,
+bool CPDF_ImageRenderer::Start(CPDF_ImageObject* pImageObject,
                                const CFX_Matrix& mtObj2Device,
                                bool bStdCS,
                                BlendMode blendType) {
-  ASSERT(pImageObject);
-  m_pRenderStatus = pStatus;
+  DCHECK(pImageObject);
   m_bStdCS = bStdCS;
   m_pImageObject = pImageObject;
   m_BlendType = blendType;
   m_mtObj2Device = mtObj2Device;
-  const CPDF_Dictionary* pOC = m_pImageObject->GetImage()->GetOC();
-  if (pOC && GetRenderOptions().GetOCContext() &&
-      !GetRenderOptions().GetOCContext()->CheckOCGVisible(pOC)) {
+  RetainPtr<const CPDF_Dictionary> pOC = m_pImageObject->GetImage()->GetOC();
+  if (pOC && !GetRenderOptions().CheckOCGDictVisible(pOC))
     return false;
-  }
+
   m_ImageMatrix = m_pImageObject->matrix() * mtObj2Device;
   if (StartLoadDIBBase())
     return true;
+
   return StartRenderDIBBase();
 }
 
-bool CPDF_ImageRenderer::Start(CPDF_RenderStatus* pStatus,
-                               const RetainPtr<CFX_DIBBase>& pDIBBase,
+bool CPDF_ImageRenderer::Start(RetainPtr<CFX_DIBBase> pDIBBase,
                                FX_ARGB bitmap_argb,
-                               int bitmap_alpha,
                                const CFX_Matrix& mtImage2Device,
                                const FXDIB_ResampleOptions& options,
-                               bool bStdCS,
-                               BlendMode blendType) {
-  m_pRenderStatus = pStatus;
-  m_pDIBBase = pDIBBase;
+                               bool bStdCS) {
+  m_pDIBBase = std::move(pDIBBase);
   m_FillArgb = bitmap_argb;
-  m_BitmapAlpha = bitmap_alpha;
+  m_BitmapAlpha = 255;
   m_ImageMatrix = mtImage2Device;
   m_ResampleOptions = options;
   m_bStdCS = bStdCS;
-  m_BlendType = blendType;
+  m_BlendType = BlendMode::kNormal;
   return StartDIBBase();
 }
 
@@ -222,7 +234,7 @@
 void CPDF_ImageRenderer::CalculateDrawImage(
     CFX_DefaultRenderDevice* pBitmapDevice1,
     CFX_DefaultRenderDevice* pBitmapDevice2,
-    const RetainPtr<CFX_DIBBase>& pDIBBase,
+    RetainPtr<CFX_DIBBase> pDIBBase,
     const CFX_Matrix& mtNewMatrix,
     const FX_RECT& rect) const {
   CPDF_RenderStatus bitmap_render(m_pRenderStatus->GetContext(),
@@ -231,19 +243,21 @@
   bitmap_render.SetStdCS(true);
   bitmap_render.Initialize(nullptr, nullptr);
 
-  CPDF_ImageRenderer image_render;
-  if (image_render.Start(&bitmap_render, pDIBBase, 0xffffffff, 255, mtNewMatrix,
-                         m_ResampleOptions, true, BlendMode::kNormal)) {
+  CPDF_ImageRenderer image_render(&bitmap_render);
+  if (image_render.Start(std::move(pDIBBase), 0xffffffff, mtNewMatrix,
+                         m_ResampleOptions, true)) {
     image_render.Continue(nullptr);
   }
-  if (m_Loader.MatteColor() == 0xffffffff)
+  if (m_pLoader->MatteColor() == 0xffffffff)
     return;
-  int matte_r = FXARGB_R(m_Loader.MatteColor());
-  int matte_g = FXARGB_G(m_Loader.MatteColor());
-  int matte_b = FXARGB_B(m_Loader.MatteColor());
+  int matte_r = FXARGB_R(m_pLoader->MatteColor());
+  int matte_g = FXARGB_G(m_pLoader->MatteColor());
+  int matte_b = FXARGB_B(m_pLoader->MatteColor());
   for (int row = 0; row < rect.Height(); row++) {
-    uint8_t* dest_scan = pBitmapDevice1->GetBitmap()->GetWritableScanline(row);
-    const uint8_t* mask_scan = pBitmapDevice2->GetBitmap()->GetScanline(row);
+    uint8_t* dest_scan =
+        pBitmapDevice1->GetBitmap()->GetWritableScanline(row).data();
+    const uint8_t* mask_scan =
+        pBitmapDevice2->GetBitmap()->GetScanline(row).data();
     for (int col = 0; col < rect.Width(); col++) {
       int alpha = *mask_scan++;
       if (!alpha) {
@@ -251,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++;
     }
   }
@@ -277,10 +291,10 @@
 
   CFX_Matrix new_matrix = GetDrawMatrix(rect);
   CFX_DefaultRenderDevice bitmap_device1;
-  if (!bitmap_device1.Create(rect.Width(), rect.Height(), FXDIB_Rgb32, nullptr))
+  if (!bitmap_device1.Create(rect.Width(), rect.Height(), FXDIB_Format::kArgb,
+                             nullptr)) {
     return true;
-
-  bitmap_device1.GetBitmap()->Clear(0xffffff);
+  }
 
   CPDF_RenderStatus bitmap_render(m_pRenderStatus->GetContext(),
                                   &bitmap_device1);
@@ -293,23 +307,22 @@
   patternDevice.Translate(static_cast<float>(-rect.left),
                           static_cast<float>(-rect.top));
   if (CPDF_TilingPattern* pTilingPattern = m_pPattern->AsTilingPattern()) {
-    bitmap_render.DrawTilingPattern(pTilingPattern, m_pImageObject.Get(),
+    bitmap_render.DrawTilingPattern(pTilingPattern, m_pImageObject,
                                     patternDevice, false);
   } else if (CPDF_ShadingPattern* pShadingPattern =
                  m_pPattern->AsShadingPattern()) {
-    bitmap_render.DrawShadingPattern(pShadingPattern, m_pImageObject.Get(),
+    bitmap_render.DrawShadingPattern(pShadingPattern, m_pImageObject,
                                      patternDevice, false);
   }
 
   CFX_DefaultRenderDevice bitmap_device2;
-  if (!bitmap_device2.Create(rect.Width(), rect.Height(), FXDIB_8bppRgb,
-                             nullptr)) {
+  if (!bitmap_device2.Create(rect.Width(), rect.Height(),
+                             FXDIB_Format::k8bppRgb, nullptr)) {
     return true;
   }
-  bitmap_device2.GetBitmap()->Clear(0);
   CalculateDrawImage(&bitmap_device1, &bitmap_device2, m_pDIBBase, new_matrix,
                      rect);
-  bitmap_device2.GetBitmap()->ConvertFormat(FXDIB_8bppMask);
+  bitmap_device2.GetBitmap()->ConvertFormat(FXDIB_Format::k8bppMask);
   bitmap_device1.GetBitmap()->MultiplyAlpha(bitmap_device2.GetBitmap());
   bitmap_device1.GetBitmap()->MultiplyAlpha(255);
   m_pRenderStatus->GetRenderDevice()->SetDIBitsWithBlend(
@@ -329,48 +342,42 @@
 
   CFX_Matrix new_matrix = GetDrawMatrix(rect);
   CFX_DefaultRenderDevice bitmap_device1;
-  if (!bitmap_device1.Create(rect.Width(), rect.Height(), FXDIB_Rgb32, nullptr))
+  if (!bitmap_device1.Create(rect.Width(), rect.Height(), FXDIB_Format::kRgb32,
+                             nullptr)) {
     return true;
-
-#if defined _SKIA_SUPPORT_
-  bitmap_device1.Clear(0xffffff);
-#else
-  bitmap_device1.GetBitmap()->Clear(0xffffff);
-#endif
+  }
+  ClearBitmap(bitmap_device1, 0xffffffff);
   CPDF_RenderStatus bitmap_render(m_pRenderStatus->GetContext(),
                                   &bitmap_device1);
   bitmap_render.SetDropObjects(m_pRenderStatus->GetDropObjects());
   bitmap_render.SetStdCS(true);
   bitmap_render.Initialize(nullptr, nullptr);
-  CPDF_ImageRenderer image_render;
-  if (image_render.Start(&bitmap_render, m_pDIBBase, 0, 255, new_matrix,
-                         m_ResampleOptions, true, BlendMode::kNormal)) {
+  CPDF_ImageRenderer image_render(&bitmap_render);
+  if (image_render.Start(m_pDIBBase, 0, new_matrix, m_ResampleOptions, true)) {
     image_render.Continue(nullptr);
   }
   CFX_DefaultRenderDevice bitmap_device2;
-  if (!bitmap_device2.Create(rect.Width(), rect.Height(), FXDIB_8bppRgb,
-                             nullptr))
+  if (!bitmap_device2.Create(rect.Width(), rect.Height(),
+                             FXDIB_Format::k8bppRgb, nullptr)) {
     return true;
-
-#if defined _SKIA_SUPPORT_
-  bitmap_device2.Clear(0);
-#else
-  bitmap_device2.GetBitmap()->Clear(0);
-#endif
-  CalculateDrawImage(&bitmap_device1, &bitmap_device2, m_Loader.GetMask(),
+  }
+  CalculateDrawImage(&bitmap_device1, &bitmap_device2, m_pLoader->GetMask(),
                      new_matrix, rect);
-#ifdef _SKIA_SUPPORT_
-  m_pRenderStatus->GetRenderDevice()->SetBitsWithMask(
-      bitmap_device1.GetBitmap(), bitmap_device2.GetBitmap(), rect.left,
-      rect.top, m_BitmapAlpha, m_BlendType);
-#else
-  bitmap_device2.GetBitmap()->ConvertFormat(FXDIB_8bppMask);
+  DCHECK(!bitmap_device2.GetBitmap()->HasPalette());
+  bitmap_device2.GetBitmap()->ConvertFormat(FXDIB_Format::k8bppMask);
+#if defined(_SKIA_SUPPORT_)
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    m_pRenderStatus->GetRenderDevice()->SetBitsWithMask(
+        bitmap_device1.GetBitmap(), bitmap_device2.GetBitmap(), rect.left,
+        rect.top, m_BitmapAlpha, m_BlendType);
+    return false;
+  }
+#endif
   bitmap_device1.GetBitmap()->MultiplyAlpha(bitmap_device2.GetBitmap());
   if (m_BitmapAlpha < 255)
     bitmap_device1.GetBitmap()->MultiplyAlpha(m_BitmapAlpha);
   m_pRenderStatus->GetRenderDevice()->SetDIBitsWithBlend(
       bitmap_device1.GetBitmap(), rect.left, rect.top, m_BlendType);
-#endif  //  _SKIA_SUPPORT_
   return false;
 }
 
@@ -388,20 +395,6 @@
       m_ResampleOptions.bInterpolateBilinear = true;
     }
   }
-#ifdef _SKIA_SUPPORT_
-  RetainPtr<CFX_DIBitmap> premultiplied = m_pDIBBase->Clone(nullptr);
-  if (m_pDIBBase->HasAlpha())
-    CFX_SkiaDeviceDriver::PreMultiply(premultiplied);
-  if (m_pRenderStatus->GetRenderDevice()->StartDIBitsWithBlend(
-          premultiplied, m_BitmapAlpha, m_FillArgb, m_ImageMatrix,
-          m_ResampleOptions, &m_DeviceHandle, m_BlendType)) {
-    if (m_DeviceHandle) {
-      m_Mode = Mode::kBlend;
-      return true;
-    }
-    return false;
-  }
-#else
   if (m_pRenderStatus->GetRenderDevice()->StartDIBitsWithBlend(
           m_pDIBBase, m_BitmapAlpha, m_FillArgb, m_ImageMatrix,
           m_ResampleOptions, &m_DeviceHandle, m_BlendType)) {
@@ -411,7 +404,6 @@
     }
     return false;
   }
-#endif
 
   if ((fabs(m_ImageMatrix.b) >= 0.5f || m_ImageMatrix.a == 0) ||
       (fabs(m_ImageMatrix.c) >= 0.5f || m_ImageMatrix.d == 0)) {
@@ -420,19 +412,19 @@
       return false;
     }
 
-    Optional<FX_RECT> image_rect = GetUnitRect();
+    absl::optional<FX_RECT> image_rect = GetUnitRect();
     if (!image_rect.has_value())
       return false;
 
     FX_RECT clip_box = m_pRenderStatus->GetRenderDevice()->GetClipBox();
     clip_box.Intersect(image_rect.value());
     m_Mode = Mode::kTransform;
-    m_pTransformer = pdfium::MakeUnique<CFX_ImageTransformer>(
+    m_pTransformer = std::make_unique<CFX_ImageTransformer>(
         m_pDIBBase, m_ImageMatrix, m_ResampleOptions, &clip_box);
     return true;
   }
 
-  Optional<FX_RECT> image_rect = GetUnitRect();
+  absl::optional<FX_RECT> image_rect = GetUnitRect();
   if (!image_rect.has_value())
     return false;
 
@@ -452,7 +444,7 @@
       return false;
     }
   }
-  if (m_pDIBBase->IsAlphaMask()) {
+  if (m_pDIBBase->IsMaskFormat()) {
     if (m_BitmapAlpha != 255)
       m_FillArgb = FXARGB_MUL_ALPHA(m_FillArgb, m_BitmapAlpha);
     if (m_pRenderStatus->GetRenderDevice()->StretchBitMaskWithFlags(
@@ -484,17 +476,18 @@
 
 bool CPDF_ImageRenderer::StartBitmapAlpha() {
   if (m_pDIBBase->IsOpaqueImage()) {
-    CFX_PathData path;
+    CFX_Path path;
     path.AppendRect(0, 0, 1, 1);
     path.Transform(m_ImageMatrix);
     uint32_t fill_color =
         ArgbEncode(0xff, m_BitmapAlpha, m_BitmapAlpha, m_BitmapAlpha);
-    m_pRenderStatus->GetRenderDevice()->DrawPath(&path, nullptr, nullptr,
-                                                 fill_color, 0, FXFILL_WINDING);
+    m_pRenderStatus->GetRenderDevice()->DrawPath(
+        path, nullptr, nullptr, fill_color, 0,
+        CFX_FillRenderOptions::WindingOptions());
     return false;
   }
   RetainPtr<CFX_DIBBase> pAlphaMask;
-  if (m_pDIBBase->IsAlphaMask())
+  if (m_pDIBBase->IsMaskFormat())
     pAlphaMask = m_pDIBBase;
   else
     pAlphaMask = m_pDIBBase->CloneAlphaMask();
@@ -513,7 +506,7 @@
     return false;
   }
 
-  Optional<FX_RECT> image_rect = GetUnitRect();
+  absl::optional<FX_RECT> image_rect = GetUnitRect();
   if (!image_rect.has_value())
     return false;
 
@@ -543,12 +536,10 @@
     case Mode::kTransform:
       return ContinueTransform(pPause);
   }
-  NOTREACHED();
-  return false;
 }
 
 bool CPDF_ImageRenderer::ContinueDefault(PauseIndicatorIface* pPause) {
-  if (m_Loader.Continue(pPause, m_pRenderStatus.Get()))
+  if (m_pLoader->Continue(pPause))
     return true;
 
   if (!StartRenderDIBBase())
@@ -573,7 +564,7 @@
   if (!pBitmap)
     return false;
 
-  if (pBitmap->IsAlphaMask()) {
+  if (pBitmap->IsMaskFormat()) {
     if (m_BitmapAlpha != 255)
       m_FillArgb = FXARGB_MUL_ALPHA(m_FillArgb, m_BitmapAlpha);
     m_Result = m_pRenderStatus->GetRenderDevice()->SetBitMask(
@@ -590,37 +581,24 @@
 }
 
 void CPDF_ImageRenderer::HandleFilters() {
-  CPDF_Object* pFilters =
-      m_pImageObject->GetImage()->GetStream()->GetDict()->GetDirectObjectFor(
-          "Filter");
-  if (!pFilters)
+  absl::optional<DecoderArray> decoder_array =
+      GetDecoderArray(m_pImageObject->GetImage()->GetStream()->GetDict());
+  if (!decoder_array.has_value())
     return;
 
-  if (pFilters->IsName()) {
-    ByteString bsDecodeType = pFilters->GetString();
-    if (bsDecodeType == "DCTDecode" || bsDecodeType == "JPXDecode")
+  for (const auto& decoder : decoder_array.value()) {
+    if (decoder.first == "DCTDecode" || decoder.first == "JPXDecode") {
       m_ResampleOptions.bLossy = true;
-    return;
-  }
-
-  CPDF_Array* pArray = pFilters->AsArray();
-  if (!pArray)
-    return;
-
-  for (size_t i = 0; i < pArray->size(); i++) {
-    ByteString bsDecodeType = pArray->GetStringAt(i);
-    if (bsDecodeType == "DCTDecode" || bsDecodeType == "JPXDecode") {
-      m_ResampleOptions.bLossy = true;
-      break;
+      return;
     }
   }
 }
 
-Optional<FX_RECT> CPDF_ImageRenderer::GetUnitRect() const {
+absl::optional<FX_RECT> CPDF_ImageRenderer::GetUnitRect() const {
   CFX_FloatRect image_rect_f = m_ImageMatrix.GetUnitRect();
   FX_RECT image_rect = image_rect_f.GetOuterRect();
   if (!image_rect.Valid())
-    return {};
+    return absl::nullopt;
   return image_rect;
 }
 
@@ -629,7 +607,7 @@
                                                    int* top,
                                                    int* width,
                                                    int* height) const {
-  ASSERT(rect.Valid());
+  DCHECK(rect.Valid());
 
   int dest_width = rect.Width();
   int dest_height = rect.Height();
diff --git a/core/fpdfapi/render/cpdf_imagerenderer.h b/core/fpdfapi/render/cpdf_imagerenderer.h
index d7c7bf0..530ac95 100644
--- a/core/fpdfapi/render/cpdf_imagerenderer.h
+++ b/core/fpdfapi/render/cpdf_imagerenderer.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,42 +9,37 @@
 
 #include <memory>
 
-#include "core/fpdfapi/render/cpdf_imageloader.h"
 #include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/dib/cfx_imagerenderer.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/optional.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
-class CFX_DIBitmap;
 class CFX_DIBBase;
 class CFX_DefaultRenderDevice;
 class CFX_ImageTransformer;
+class CPDF_ImageLoader;
 class CPDF_ImageObject;
-class CPDF_PageObject;
 class CPDF_Pattern;
 class CPDF_RenderOptions;
 class CPDF_RenderStatus;
 
 class CPDF_ImageRenderer {
  public:
-  CPDF_ImageRenderer();
+  explicit CPDF_ImageRenderer(CPDF_RenderStatus* pStatus);
   ~CPDF_ImageRenderer();
 
-  bool Start(CPDF_RenderStatus* pStatus,
-             CPDF_ImageObject* pImageObject,
+  bool Start(CPDF_ImageObject* pImageObject,
              const CFX_Matrix& mtObj2Device,
              bool bStdCS,
              BlendMode blendType);
 
-  bool Start(CPDF_RenderStatus* pStatus,
-             const RetainPtr<CFX_DIBBase>& pDIBBase,
+  bool Start(RetainPtr<CFX_DIBBase> pDIBBase,
              FX_ARGB bitmap_argb,
-             int bitmap_alpha,
              const CFX_Matrix& mtImage2Device,
              const FXDIB_ResampleOptions& options,
-             bool bStdCS,
-             BlendMode blendType);
+             bool bStdCS);
 
   bool Continue(PauseIndicatorIface* pPause);
   bool GetResult() const { return m_Result; }
@@ -71,25 +66,25 @@
   CFX_Matrix GetDrawMatrix(const FX_RECT& rect) const;
   void CalculateDrawImage(CFX_DefaultRenderDevice* pBitmapDevice1,
                           CFX_DefaultRenderDevice* pBitmapDevice2,
-                          const RetainPtr<CFX_DIBBase>& pDIBBase,
+                          RetainPtr<CFX_DIBBase> pDIBBase,
                           const CFX_Matrix& mtNewMatrix,
                           const FX_RECT& rect) const;
   const CPDF_RenderOptions& GetRenderOptions() const;
   void HandleFilters();
-  Optional<FX_RECT> GetUnitRect() const;
+  absl::optional<FX_RECT> GetUnitRect() const;
   bool GetDimensionsFromUnitRect(const FX_RECT& rect,
                                  int* left,
                                  int* top,
                                  int* width,
                                  int* height) const;
 
-  UnownedPtr<CPDF_RenderStatus> m_pRenderStatus;
+  UnownedPtr<CPDF_RenderStatus> const m_pRenderStatus;
   UnownedPtr<CPDF_ImageObject> m_pImageObject;
   RetainPtr<CPDF_Pattern> m_pPattern;
   RetainPtr<CFX_DIBBase> m_pDIBBase;
   CFX_Matrix m_mtObj2Device;
   CFX_Matrix m_ImageMatrix;
-  CPDF_ImageLoader m_Loader;
+  std::unique_ptr<CPDF_ImageLoader> const m_pLoader;
   std::unique_ptr<CFX_ImageTransformer> m_pTransformer;
   std::unique_ptr<CFX_ImageRenderer> m_DeviceHandle;
   Mode m_Mode = Mode::kNone;
diff --git a/core/fpdfapi/render/cpdf_pagerendercache.cpp b/core/fpdfapi/render/cpdf_pagerendercache.cpp
deleted file mode 100644
index ca4f8ee..0000000
--- a/core/fpdfapi/render/cpdf_pagerendercache.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
-
-#include <algorithm>
-#include <vector>
-
-#include "core/fpdfapi/page/cpdf_image.h"
-#include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fpdfapi/render/cpdf_imagecacheentry.h"
-#include "core/fpdfapi/render/cpdf_renderstatus.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "third_party/base/ptr_util.h"
-
-namespace {
-
-struct CacheInfo {
-  CacheInfo(uint32_t t, CPDF_Stream* stream) : time(t), pStream(stream) {}
-
-  uint32_t time;
-  CPDF_Stream* pStream;
-
-  bool operator<(const CacheInfo& other) const { return time < other.time; }
-};
-
-}  // namespace
-
-CPDF_PageRenderCache::CPDF_PageRenderCache(CPDF_Page* pPage) : m_pPage(pPage) {}
-
-CPDF_PageRenderCache::~CPDF_PageRenderCache() = default;
-
-void CPDF_PageRenderCache::CacheOptimization(int32_t dwLimitCacheSize) {
-  if (m_nCacheSize <= (uint32_t)dwLimitCacheSize)
-    return;
-
-  size_t nCount = m_ImageCache.size();
-  std::vector<CacheInfo> cache_info;
-  cache_info.reserve(nCount);
-  for (const auto& it : m_ImageCache) {
-    cache_info.emplace_back(it.second->GetTimeCount(),
-                            it.second->GetImage()->GetStream());
-  }
-  std::sort(cache_info.begin(), cache_info.end());
-
-  // Check if time value is about to roll over and reset all entries.
-  // The comparision is legal because uint32_t is an unsigned type.
-  uint32_t nTimeCount = m_nTimeCount;
-  if (nTimeCount + 1 < nTimeCount) {
-    for (size_t i = 0; i < nCount; i++)
-      m_ImageCache[cache_info[i].pStream]->m_dwTimeCount = i;
-    m_nTimeCount = nCount;
-  }
-
-  size_t i = 0;
-  while (i + 15 < nCount)
-    ClearImageCacheEntry(cache_info[i++].pStream);
-
-  while (i < nCount && m_nCacheSize > (uint32_t)dwLimitCacheSize)
-    ClearImageCacheEntry(cache_info[i++].pStream);
-}
-
-void CPDF_PageRenderCache::ClearImageCacheEntry(CPDF_Stream* pStream) {
-  auto it = m_ImageCache.find(pStream);
-  if (it == m_ImageCache.end())
-    return;
-
-  m_nCacheSize -= it->second->EstimateSize();
-  m_ImageCache.erase(it);
-}
-
-bool CPDF_PageRenderCache::StartGetCachedBitmap(
-    const RetainPtr<CPDF_Image>& pImage,
-    bool bStdCS,
-    uint32_t GroupFamily,
-    bool bLoadMask,
-    CPDF_RenderStatus* pRenderStatus) {
-  CPDF_Stream* pStream = pImage->GetStream();
-  const auto it = m_ImageCache.find(pStream);
-  m_bCurFindCache = it != m_ImageCache.end();
-  if (m_bCurFindCache) {
-    m_pCurImageCacheEntry = it->second.get();
-  } else {
-    m_pCurImageCacheEntry = pdfium::MakeUnique<CPDF_ImageCacheEntry>(
-        m_pPage->GetDocument(), pImage);
-  }
-  CPDF_DIB::LoadState ret = m_pCurImageCacheEntry->StartGetCachedBitmap(
-      pRenderStatus->GetFormResource(), m_pPage->m_pPageResources.Get(), bStdCS,
-      GroupFamily, bLoadMask, pRenderStatus);
-  if (ret == CPDF_DIB::LoadState::kContinue)
-    return true;
-
-  m_nTimeCount++;
-  if (!m_bCurFindCache)
-    m_ImageCache[pStream] = m_pCurImageCacheEntry.Release();
-
-  if (ret == CPDF_DIB::LoadState::kFail)
-    m_nCacheSize += m_pCurImageCacheEntry->EstimateSize();
-
-  return false;
-}
-
-bool CPDF_PageRenderCache::Continue(PauseIndicatorIface* pPause,
-                                    CPDF_RenderStatus* pRenderStatus) {
-  bool ret = m_pCurImageCacheEntry->Continue(pPause, pRenderStatus);
-  if (ret)
-    return true;
-
-  m_nTimeCount++;
-  if (!m_bCurFindCache) {
-    m_ImageCache[m_pCurImageCacheEntry->GetImage()->GetStream()] =
-        m_pCurImageCacheEntry.Release();
-  }
-  m_nCacheSize += m_pCurImageCacheEntry->EstimateSize();
-  return false;
-}
-
-void CPDF_PageRenderCache::ResetBitmapForImage(
-    const RetainPtr<CPDF_Image>& pImage) {
-  CPDF_ImageCacheEntry* pEntry;
-  CPDF_Stream* pStream = pImage->GetStream();
-  const auto it = m_ImageCache.find(pStream);
-  if (it == m_ImageCache.end())
-    return;
-
-  pEntry = it->second.get();
-  m_nCacheSize -= pEntry->EstimateSize();
-  pEntry->Reset();
-  m_nCacheSize += pEntry->EstimateSize();
-}
diff --git a/core/fpdfapi/render/cpdf_pagerendercache.h b/core/fpdfapi/render/cpdf_pagerendercache.h
deleted file mode 100644
index 1fb2a61..0000000
--- a/core/fpdfapi/render/cpdf_pagerendercache.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFAPI_RENDER_CPDF_PAGERENDERCACHE_H_
-#define CORE_FPDFAPI_RENDER_CPDF_PAGERENDERCACHE_H_
-
-#include <map>
-#include <memory>
-
-#include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/maybe_owned.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class CPDF_Image;
-class CPDF_ImageCacheEntry;
-class CPDF_Page;
-class CPDF_RenderStatus;
-class CPDF_Stream;
-class PauseIndicatorIface;
-
-class CPDF_PageRenderCache : public CPDF_Page::RenderCacheIface {
- public:
-  explicit CPDF_PageRenderCache(CPDF_Page* pPage);
-  ~CPDF_PageRenderCache() override;
-
-  // CPDF_Page::RenderCacheIface:
-  void ResetBitmapForImage(const RetainPtr<CPDF_Image>& pImage) override;
-
-  void CacheOptimization(int32_t dwLimitCacheSize);
-  uint32_t GetTimeCount() const { return m_nTimeCount; }
-  CPDF_Page* GetPage() const { return m_pPage.Get(); }
-  CPDF_ImageCacheEntry* GetCurImageCacheEntry() const {
-    return m_pCurImageCacheEntry.Get();
-  }
-
-  bool StartGetCachedBitmap(const RetainPtr<CPDF_Image>& pImage,
-                            bool bStdCS,
-                            uint32_t GroupFamily,
-                            bool bLoadMask,
-                            CPDF_RenderStatus* pRenderStatus);
-
-  bool Continue(PauseIndicatorIface* pPause, CPDF_RenderStatus* pRenderStatus);
-
- private:
-  void ClearImageCacheEntry(CPDF_Stream* pStream);
-
-  UnownedPtr<CPDF_Page> const m_pPage;
-  std::map<CPDF_Stream*, std::unique_ptr<CPDF_ImageCacheEntry>> m_ImageCache;
-  MaybeOwned<CPDF_ImageCacheEntry> m_pCurImageCacheEntry;
-  uint32_t m_nTimeCount = 0;
-  uint32_t m_nCacheSize = 0;
-  bool m_bCurFindCache = false;
-};
-
-#endif  // CORE_FPDFAPI_RENDER_CPDF_PAGERENDERCACHE_H_
diff --git a/core/fpdfapi/render/cpdf_pagerendercontext.cpp b/core/fpdfapi/render/cpdf_pagerendercontext.cpp
index 8dd66c7..9899041 100644
--- a/core/fpdfapi/render/cpdf_pagerendercontext.cpp
+++ b/core/fpdfapi/render/cpdf_pagerendercontext.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,6 +12,6 @@
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fxge/cfx_renderdevice.h"
 
-CPDF_PageRenderContext::CPDF_PageRenderContext() {}
+CPDF_PageRenderContext::CPDF_PageRenderContext() = default;
 
-CPDF_PageRenderContext::~CPDF_PageRenderContext() {}
+CPDF_PageRenderContext::~CPDF_PageRenderContext() = default;
diff --git a/core/fpdfapi/render/cpdf_pagerendercontext.h b/core/fpdfapi/render/cpdf_pagerendercontext.h
index 8a5a5eb..f6366c4 100644
--- a/core/fpdfapi/render/cpdf_pagerendercontext.h
+++ b/core/fpdfapi/render/cpdf_pagerendercontext.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,7 +22,7 @@
   // Context merely manages the lifetime for callers.
   class AnnotListIface {
    public:
-    virtual ~AnnotListIface() {}
+    virtual ~AnnotListIface() = default;
   };
 
   CPDF_PageRenderContext();
diff --git a/core/fpdfapi/render/cpdf_progressiverenderer.cpp b/core/fpdfapi/render/cpdf_progressiverenderer.cpp
index 6e5fc36..c29077a 100644
--- a/core/fpdfapi/render/cpdf_progressiverenderer.cpp
+++ b/core/fpdfapi/render/cpdf_progressiverenderer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,14 +8,13 @@
 
 #include "core/fpdfapi/page/cpdf_image.h"
 #include "core/fpdfapi/page/cpdf_imageobject.h"
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/page/cpdf_pageobjectholder.h"
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fpdfapi/render/cpdf_renderstatus.h"
 #include "core/fxcrt/pauseindicator_iface.h"
 #include "core/fxge/cfx_renderdevice.h"
-#include "third_party/base/ptr_util.h"
 
 CPDF_ProgressiveRenderer::CPDF_ProgressiveRenderer(
     CPDF_RenderContext* pContext,
@@ -47,26 +46,26 @@
         return;
       }
       m_pCurrentLayer = m_pContext->GetLayer(m_LayerIndex);
-      m_LastObjectRendered = m_pCurrentLayer->m_pObjectHolder->end();
-      m_pRenderStatus = pdfium::MakeUnique<CPDF_RenderStatus>(m_pContext.Get(),
-                                                              m_pDevice.Get());
+      m_LastObjectRendered = m_pCurrentLayer->GetObjectHolder()->end();
+      m_pRenderStatus =
+          std::make_unique<CPDF_RenderStatus>(m_pContext, m_pDevice);
       if (m_pOptions)
         m_pRenderStatus->SetOptions(*m_pOptions);
       m_pRenderStatus->SetTransparency(
-          m_pCurrentLayer->m_pObjectHolder->GetTransparency());
+          m_pCurrentLayer->GetObjectHolder()->GetTransparency());
       m_pRenderStatus->Initialize(nullptr, nullptr);
       m_pDevice->SaveState();
-      m_ClipRect = m_pCurrentLayer->m_Matrix.GetInverse().TransformRect(
+      m_ClipRect = m_pCurrentLayer->GetMatrix().GetInverse().TransformRect(
           CFX_FloatRect(m_pDevice->GetClipBox()));
     }
     CPDF_PageObjectHolder::const_iterator iter;
     CPDF_PageObjectHolder::const_iterator iterEnd =
-        m_pCurrentLayer->m_pObjectHolder->end();
+        m_pCurrentLayer->GetObjectHolder()->end();
     if (m_LastObjectRendered != iterEnd) {
       iter = m_LastObjectRendered;
       ++iter;
     } else {
-      iter = m_pCurrentLayer->m_pObjectHolder->begin();
+      iter = m_pCurrentLayer->GetObjectHolder()->begin();
     }
     int nObjsToGo = kStepLimit;
     bool is_mask = false;
@@ -81,13 +80,13 @@
           if (m_pDevice->GetDeviceType() == DeviceType::kPrinter) {
             m_LastObjectRendered = iter;
             m_pRenderStatus->ProcessClipPath(pCurObj->m_ClipPath,
-                                             m_pCurrentLayer->m_Matrix);
+                                             m_pCurrentLayer->GetMatrix());
             return;
           }
           is_mask = true;
         }
         if (m_pRenderStatus->ContinueSingleObject(
-                pCurObj, m_pCurrentLayer->m_Matrix, pPause)) {
+                pCurObj, m_pCurrentLayer->GetMatrix(), pPause)) {
           return;
         }
         if (pCurObj->IsImage() && m_pRenderStatus->GetRenderOptions()
@@ -111,7 +110,7 @@
       if (is_mask && iter != iterEnd)
         return;
     }
-    if (m_pCurrentLayer->m_pObjectHolder->GetParseState() ==
+    if (m_pCurrentLayer->GetObjectHolder()->GetParseState() ==
         CPDF_PageObjectHolder::ParseState::kParsed) {
       m_pRenderStatus.reset();
       m_pDevice->RestoreState(false);
@@ -122,8 +121,8 @@
     } else if (is_mask) {
       return;
     } else {
-      m_pCurrentLayer->m_pObjectHolder->ContinueParse(pPause);
-      if (m_pCurrentLayer->m_pObjectHolder->GetParseState() !=
+      m_pCurrentLayer->GetObjectHolder()->ContinueParse(pPause);
+      if (m_pCurrentLayer->GetObjectHolder()->GetParseState() !=
           CPDF_PageObjectHolder::ParseState::kParsed) {
         return;
       }
diff --git a/core/fpdfapi/render/cpdf_progressiverenderer.h b/core/fpdfapi/render/cpdf_progressiverenderer.h
index fd399f9..7af65a0 100644
--- a/core/fpdfapi/render/cpdf_progressiverenderer.h
+++ b/core/fpdfapi/render/cpdf_progressiverenderer.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,14 @@
 #ifndef CORE_FPDFAPI_RENDER_CPDF_PROGRESSIVERENDERER_H_
 #define CORE_FPDFAPI_RENDER_CPDF_PROGRESSIVERENDERER_H_
 
+#include <stdint.h>
+
 #include <memory>
 
 #include "core/fpdfapi/page/cpdf_pageobjectholder.h"
 #include "core/fpdfapi/render/cpdf_rendercontext.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_RenderOptions;
 class CPDF_RenderStatus;
@@ -42,16 +44,16 @@
 
  private:
   // Maximum page objects to render before checking for pause.
-  static const int kStepLimit = 100;
+  static constexpr int kStepLimit = 100;
 
   Status m_Status = kReady;
   UnownedPtr<CPDF_RenderContext> const m_pContext;
   UnownedPtr<CFX_RenderDevice> const m_pDevice;
-  const CPDF_RenderOptions* const m_pOptions;
+  UnownedPtr<const CPDF_RenderOptions> const m_pOptions;
   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_rendercontext.cpp b/core/fpdfapi/render/cpdf_rendercontext.cpp
index 1280cee..4116840 100644
--- a/core/fpdfapi/render/cpdf_rendercontext.cpp
+++ b/core/fpdfapi/render/cpdf_rendercontext.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,11 +6,13 @@
 
 #include "core/fpdfapi/render/cpdf_rendercontext.h"
 
+#include <utility>
+
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/page/cpdf_pageobjectholder.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
 #include "core/fpdfapi/render/cpdf_progressiverenderer.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fpdfapi/render/cpdf_renderstatus.h"
@@ -18,41 +20,32 @@
 #include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
 
-CPDF_RenderContext::CPDF_RenderContext(CPDF_Document* pDoc,
-                                       CPDF_Dictionary* pPageResources,
-                                       CPDF_PageRenderCache* pPageCache)
+CPDF_RenderContext::CPDF_RenderContext(
+    CPDF_Document* pDoc,
+    RetainPtr<CPDF_Dictionary> pPageResources,
+    CPDF_PageImageCache* pPageCache)
     : m_pDocument(pDoc),
-      m_pPageResources(pPageResources),
+      m_pPageResources(std::move(pPageResources)),
       m_pPageCache(pPageCache) {}
 
 CPDF_RenderContext::~CPDF_RenderContext() = default;
 
-void CPDF_RenderContext::GetBackground(const RetainPtr<CFX_DIBitmap>& pBuffer,
+void CPDF_RenderContext::GetBackground(RetainPtr<CFX_DIBitmap> pBuffer,
                                        const CPDF_PageObject* pObj,
                                        const CPDF_RenderOptions* pOptions,
                                        const CFX_Matrix& mtFinal) {
   CFX_DefaultRenderDevice device;
-  device.Attach(pBuffer, false, nullptr, false);
-
+  device.Attach(std::move(pBuffer));
   device.FillRect(FX_RECT(0, 0, device.GetWidth(), device.GetHeight()),
                   0xffffffff);
   Render(&device, pObj, pOptions, &mtFinal);
 }
 
 void CPDF_RenderContext::AppendLayer(CPDF_PageObjectHolder* pObjectHolder,
-                                     const CFX_Matrix* pObject2Device) {
-  m_Layers.emplace_back();
-  m_Layers.back().m_pObjectHolder = pObjectHolder;
-  if (pObject2Device)
-    m_Layers.back().m_Matrix = *pObject2Device;
-}
-
-void CPDF_RenderContext::Render(CFX_RenderDevice* pDevice,
-                                const CPDF_RenderOptions* pOptions,
-                                const CFX_Matrix* pLastMatrix) {
-  Render(pDevice, nullptr, pOptions, pLastMatrix);
+                                     const CFX_Matrix& mtObject2Device) {
+  m_Layers.emplace_back(pObjectHolder, mtObject2Device);
 }
 
 void CPDF_RenderContext::Render(CFX_RenderDevice* pDevice,
@@ -65,14 +58,14 @@
     if (pOptions)
       status.SetOptions(*pOptions);
     status.SetStopObject(pStopObj);
-    status.SetTransparency(layer.m_pObjectHolder->GetTransparency());
-    CFX_Matrix final_matrix = layer.m_Matrix;
+    status.SetTransparency(layer.GetObjectHolder()->GetTransparency());
+    CFX_Matrix final_matrix = layer.GetMatrix();
     if (pLastMatrix) {
       final_matrix *= *pLastMatrix;
       status.SetDeviceMatrix(*pLastMatrix);
     }
     status.Initialize(nullptr, nullptr);
-    status.RenderObjectList(layer.m_pObjectHolder.Get(), final_matrix);
+    status.RenderObjectList(layer.GetObjectHolder(), final_matrix);
     if (status.GetRenderOptions().GetOptions().bLimitedImageCache) {
       m_pPageCache->CacheOptimization(
           status.GetRenderOptions().GetCacheSizeLimit());
@@ -82,7 +75,9 @@
   }
 }
 
-CPDF_RenderContext::Layer::Layer() = default;
+CPDF_RenderContext::Layer::Layer(CPDF_PageObjectHolder* pHolder,
+                                 const CFX_Matrix& matrix)
+    : m_pObjectHolder(pHolder), m_Matrix(matrix) {}
 
 CPDF_RenderContext::Layer::Layer(const Layer& that) = default;
 
diff --git a/core/fpdfapi/render/cpdf_rendercontext.h b/core/fpdfapi/render/cpdf_rendercontext.h
index d9efbc4..383e798 100644
--- a/core/fpdfapi/render/cpdf_rendercontext.h
+++ b/core/fpdfapi/render/cpdf_rendercontext.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,46 +13,46 @@
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
-class CPDF_Dictionary;
-class CPDF_Document;
-class CPDF_PageObject;
-class CPDF_PageObjectHolder;
-class CPDF_PageRenderCache;
-class CPDF_RenderOptions;
 class CFX_DIBitmap;
 class CFX_Matrix;
 class CFX_RenderDevice;
+class CPDF_Dictionary;
+class CPDF_Document;
+class CPDF_PageImageCache;
+class CPDF_PageObject;
+class CPDF_PageObjectHolder;
+class CPDF_RenderOptions;
 
 class CPDF_RenderContext {
  public:
   class Layer {
    public:
-    Layer();
+    Layer(CPDF_PageObjectHolder* pHolder, const CFX_Matrix& matrix);
     Layer(const Layer& that);
     ~Layer();
 
-    UnownedPtr<CPDF_PageObjectHolder> m_pObjectHolder;
-    CFX_Matrix m_Matrix;
+    CPDF_PageObjectHolder* GetObjectHolder() { return m_pObjectHolder; }
+    const CFX_Matrix& GetMatrix() const { return m_Matrix; }
+
+   private:
+    UnownedPtr<CPDF_PageObjectHolder> const m_pObjectHolder;
+    const CFX_Matrix m_Matrix;
   };
 
   CPDF_RenderContext(CPDF_Document* pDoc,
-                     CPDF_Dictionary* pPageResources,
-                     CPDF_PageRenderCache* pPageCache);
+                     RetainPtr<CPDF_Dictionary> pPageResources,
+                     CPDF_PageImageCache* pPageCache);
   ~CPDF_RenderContext();
 
   void AppendLayer(CPDF_PageObjectHolder* pObjectHolder,
-                   const CFX_Matrix* pObject2Device);
-
-  void Render(CFX_RenderDevice* pDevice,
-              const CPDF_RenderOptions* pOptions,
-              const CFX_Matrix* pLastMatrix);
+                   const CFX_Matrix& mtObject2Device);
 
   void Render(CFX_RenderDevice* pDevice,
               const CPDF_PageObject* pStopObj,
               const CPDF_RenderOptions* pOptions,
               const CFX_Matrix* pLastMatrix);
 
-  void GetBackground(const RetainPtr<CFX_DIBitmap>& pBuffer,
+  void GetBackground(RetainPtr<CFX_DIBitmap> pBuffer,
                      const CPDF_PageObject* pObj,
                      const CPDF_RenderOptions* pOptions,
                      const CFX_Matrix& mtFinal);
@@ -60,14 +60,19 @@
   size_t CountLayers() const { return m_Layers.size(); }
   Layer* GetLayer(uint32_t index) { return &m_Layers[index]; }
 
-  CPDF_Document* GetDocument() const { return m_pDocument.Get(); }
-  CPDF_Dictionary* GetPageResources() const { return m_pPageResources.Get(); }
-  CPDF_PageRenderCache* GetPageCache() const { return m_pPageCache.Get(); }
+  CPDF_Document* GetDocument() const { return m_pDocument; }
+  const CPDF_Dictionary* GetPageResources() const {
+    return m_pPageResources.Get();
+  }
+  RetainPtr<CPDF_Dictionary> GetMutablePageResources() {
+    return m_pPageResources;
+  }
+  CPDF_PageImageCache* GetPageCache() const { return m_pPageCache; }
 
- protected:
+ private:
   UnownedPtr<CPDF_Document> const m_pDocument;
   RetainPtr<CPDF_Dictionary> const m_pPageResources;
-  UnownedPtr<CPDF_PageRenderCache> const m_pPageCache;
+  UnownedPtr<CPDF_PageImageCache> const m_pPageCache;
   std::vector<Layer> m_Layers;
 };
 
diff --git a/core/fpdfapi/render/cpdf_renderoptions.cpp b/core/fpdfapi/render/cpdf_renderoptions.cpp
index 11efe9e..68bb472 100644
--- a/core/fpdfapi/render/cpdf_renderoptions.cpp
+++ b/core/fpdfapi/render/cpdf_renderoptions.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,6 +17,9 @@
 CPDF_RenderOptions::Options::Options(const CPDF_RenderOptions::Options& rhs) =
     default;
 
+CPDF_RenderOptions::Options& CPDF_RenderOptions::Options::operator=(
+    const CPDF_RenderOptions::Options& rhs) = default;
+
 CPDF_RenderOptions::CPDF_RenderOptions() {
   // TODO(thestig): Make constexpr to initialize |m_Options| once C++14 is
   // available.
@@ -42,6 +45,34 @@
   return ArgbEncode(a, gray, gray, gray);
 }
 
+FX_ARGB CPDF_RenderOptions::TranslateObjectColor(
+    FX_ARGB argb,
+    CPDF_PageObject::Type object_type,
+    RenderType render_type) const {
+  if (!ColorModeIs(kForcedColor))
+    return TranslateColor(argb);
+
+  switch (object_type) {
+    case CPDF_PageObject::Type::kPath:
+      return render_type == RenderType::kFill ? m_ColorScheme.path_fill_color
+                                              : m_ColorScheme.path_stroke_color;
+    case CPDF_PageObject::Type::kText:
+      return render_type == RenderType::kFill ? m_ColorScheme.text_fill_color
+                                              : m_ColorScheme.text_stroke_color;
+    default:
+      return argb;
+  }
+}
+
 uint32_t CPDF_RenderOptions::GetCacheSizeLimit() const {
   return kCacheSizeLimitBytes;
 }
+
+bool CPDF_RenderOptions::CheckOCGDictVisible(const CPDF_Dictionary* pOC) const {
+  return !m_pOCContext || m_pOCContext->CheckOCGDictVisible(pOC);
+}
+
+bool CPDF_RenderOptions::CheckPageObjectVisible(
+    const CPDF_PageObject* pPageObj) const {
+  return !m_pOCContext || m_pOCContext->CheckPageObjectVisible(pPageObj);
+}
diff --git a/core/fpdfapi/render/cpdf_renderoptions.h b/core/fpdfapi/render/cpdf_renderoptions.h
index 84f7e4c..12eb91a 100644
--- a/core/fpdfapi/render/cpdf_renderoptions.h
+++ b/core/fpdfapi/render/cpdf_renderoptions.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,35 +7,43 @@
 #ifndef CORE_FPDFAPI_RENDER_CPDF_RENDEROPTIONS_H_
 #define CORE_FPDFAPI_RENDER_CPDF_RENDEROPTIONS_H_
 
+#include <stdint.h>
+
 #include "core/fpdfapi/page/cpdf_occontext.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+
+class CPDF_Dictionary;
 
 class CPDF_RenderOptions {
  public:
-  enum Type : uint8_t { kNormal = 0, kGray, kAlpha };
+  enum Type : uint8_t { kNormal = 0, kGray, kAlpha, kForcedColor };
+
+  enum RenderType : uint8_t { kFill = 0, kStroke };
 
   struct Options {
     Options();
     Options(const Options& rhs);
+    Options& operator=(const Options& rhs);
 
     bool bClearType = false;
-    bool bPrintGraphicText = false;
-    bool bPrintPreview = false;
-    bool bBGRStripe = false;
     bool bNoNativeText = false;
     bool bForceHalftone = false;
     bool bRectAA = false;
-    bool bFillFullcover = false;
-    bool bPrintImageText = false;
-    bool bOverprint = false;
-    bool bThinLine = false;
     bool bBreakForMasks = false;
     bool bNoTextSmooth = false;
     bool bNoPathSmooth = false;
     bool bNoImageSmooth = false;
     bool bLimitedImageCache = false;
+    bool bConvertFillToStroke = false;
+  };
+
+  struct ColorScheme {
+    FX_ARGB path_fill_color;
+    FX_ARGB path_stroke_color;
+    FX_ARGB text_fill_color;
+    FX_ARGB text_stroke_color;
   };
 
   CPDF_RenderOptions();
@@ -43,6 +51,13 @@
   ~CPDF_RenderOptions();
 
   FX_ARGB TranslateColor(FX_ARGB argb) const;
+  FX_ARGB TranslateObjectColor(FX_ARGB argb,
+                               CPDF_PageObject::Type object_type,
+                               RenderType render_type) const;
+
+  void SetColorScheme(const ColorScheme& color_scheme) {
+    m_ColorScheme = color_scheme;
+  }
 
   void SetColorMode(Type mode) { m_ColorMode = mode; }
   bool ColorModeIs(Type mode) const { return m_ColorMode == mode; }
@@ -51,6 +66,8 @@
   Options& GetOptions() { return m_Options; }
 
   uint32_t GetCacheSizeLimit() const;
+  bool CheckOCGDictVisible(const CPDF_Dictionary* pOC) const;
+  bool CheckPageObjectVisible(const CPDF_PageObject* pPageObj) const;
 
   void SetDrawAnnots(bool draw) { m_bDrawAnnots = draw; }
   bool GetDrawAnnots() const { return m_bDrawAnnots; }
@@ -58,12 +75,12 @@
   void SetOCContext(RetainPtr<CPDF_OCContext> context) {
     m_pOCContext = context;
   }
-  const CPDF_OCContext* GetOCContext() const { return m_pOCContext.Get(); }
 
  private:
   Type m_ColorMode = kNormal;
   bool m_bDrawAnnots = false;
   Options m_Options;
+  ColorScheme m_ColorScheme = {};
   RetainPtr<CPDF_OCContext> m_pOCContext;
 };
 
diff --git a/core/fpdfapi/render/cpdf_rendershading.cpp b/core/fpdfapi/render/cpdf_rendershading.cpp
index 53fb79a..bce967f 100644
--- a/core/fpdfapi/render/cpdf_rendershading.cpp
+++ b/core/fpdfapi/render/cpdf_rendershading.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,10 @@
 
 #include "core/fpdfapi/render/cpdf_rendershading.h"
 
+#include <math.h>
+
 #include <algorithm>
 #include <array>
-#include <cmath>
 #include <memory>
 #include <utility>
 #include <vector>
@@ -25,9 +26,16 @@
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/span.h"
 
 namespace {
 
@@ -57,28 +65,28 @@
     const RetainPtr<CPDF_ColorSpace>& pCS,
     int alpha,
     size_t results_count) {
-  ASSERT(results_count >= CountOutputsFromFunctions(funcs));
-  ASSERT(results_count >= pCS->CountComponents());
+  DCHECK(results_count >= CountOutputsFromFunctions(funcs));
+  DCHECK(results_count >= pCS->CountComponents());
   std::array<FX_ARGB, kShadingSteps> shading_steps;
   std::vector<float> result_array(results_count);
   float diff = t_max - t_min;
   for (int i = 0; i < kShadingSteps; ++i) {
     float input = diff * i / kShadingSteps + t_min;
-    int offset = 0;
+    pdfium::span<float> result_span = pdfium::make_span(result_array);
     for (const auto& func : funcs) {
-      if (func) {
-        int nresults = 0;
-        if (func->Call(&input, 1, &result_array[offset], &nresults))
-          offset += nresults;
-      }
+      if (!func)
+        continue;
+      absl::optional<uint32_t> nresults =
+          func->Call(pdfium::make_span(&input, 1), result_span);
+      if (nresults.has_value())
+        result_span = result_span.subspan(nresults.value());
     }
     float R = 0.0f;
     float G = 0.0f;
     float B = 0.0f;
-    pCS->GetRGB(result_array.data(), &R, &G, &B);
-    shading_steps[i] =
-        FXARGB_TODIB(ArgbEncode(alpha, FXSYS_roundf(R * 255),
-                                FXSYS_roundf(G * 255), FXSYS_roundf(B * 255)));
+    pCS->GetRGB(result_array, &R, &G, &B);
+    shading_steps[i] = ArgbEncode(alpha, FXSYS_roundf(R * 255),
+                                  FXSYS_roundf(G * 255), FXSYS_roundf(B * 255));
   }
   return shading_steps;
 }
@@ -89,26 +97,26 @@
                       const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
                       const RetainPtr<CPDF_ColorSpace>& pCS,
                       int alpha) {
-  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
+  DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb);
 
   const uint32_t total_results = GetValidatedOutputsCount(funcs, pCS);
   if (total_results == 0)
     return;
 
-  const CPDF_Array* pCoords = pDict->GetArrayFor("Coords");
+  RetainPtr<const CPDF_Array> pCoords = pDict->GetArrayFor("Coords");
   if (!pCoords)
     return;
 
-  float start_x = pCoords->GetNumberAt(0);
-  float start_y = pCoords->GetNumberAt(1);
-  float end_x = pCoords->GetNumberAt(2);
-  float end_y = pCoords->GetNumberAt(3);
+  float start_x = pCoords->GetFloatAt(0);
+  float start_y = pCoords->GetFloatAt(1);
+  float end_x = pCoords->GetFloatAt(2);
+  float end_y = pCoords->GetFloatAt(3);
   float t_min = 0;
   float t_max = 1.0f;
-  const CPDF_Array* pArray = pDict->GetArrayFor("Domain");
+  RetainPtr<const CPDF_Array> pArray = pDict->GetArrayFor("Domain");
   if (pArray) {
-    t_min = pArray->GetNumberAt(0);
-    t_max = pArray->GetNumberAt(1);
+    t_min = pArray->GetFloatAt(0);
+    t_max = pArray->GetFloatAt(1);
   }
   pArray = pDict->GetArrayFor("Extend");
   const bool bStartExtend = pArray && pArray->GetBooleanAt(0, false);
@@ -123,18 +131,17 @@
   std::array<FX_ARGB, kShadingSteps> shading_steps =
       GetShadingSteps(t_min, t_max, funcs, pCS, alpha, total_results);
 
-  int pitch = pBitmap->GetPitch();
   CFX_Matrix matrix = mtObject2Bitmap.GetInverse();
   for (int row = 0; row < height; row++) {
     uint32_t* dib_buf =
-        reinterpret_cast<uint32_t*>(pBitmap->GetBuffer() + row * pitch);
+        reinterpret_cast<uint32_t*>(pBitmap->GetWritableScanline(row).data());
     for (int column = 0; column < width; column++) {
       CFX_PointF pos = matrix.Transform(
           CFX_PointF(static_cast<float>(column), static_cast<float>(row)));
       float scale =
           (((pos.x - start_x) * x_span) + ((pos.y - start_y) * y_span)) /
           axis_len_square;
-      int index = (int32_t)(scale * (kShadingSteps - 1));
+      int index = static_cast<int32_t>(scale * (kShadingSteps - 1));
       if (index < 0) {
         if (!bStartExtend)
           continue;
@@ -157,28 +164,28 @@
                        const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
                        const RetainPtr<CPDF_ColorSpace>& pCS,
                        int alpha) {
-  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
+  DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb);
 
   const uint32_t total_results = GetValidatedOutputsCount(funcs, pCS);
   if (total_results == 0)
     return;
 
-  const CPDF_Array* pCoords = pDict->GetArrayFor("Coords");
+  RetainPtr<const CPDF_Array> pCoords = pDict->GetArrayFor("Coords");
   if (!pCoords)
     return;
 
-  float start_x = pCoords->GetNumberAt(0);
-  float start_y = pCoords->GetNumberAt(1);
-  float start_r = pCoords->GetNumberAt(2);
-  float end_x = pCoords->GetNumberAt(3);
-  float end_y = pCoords->GetNumberAt(4);
-  float end_r = pCoords->GetNumberAt(5);
+  float start_x = pCoords->GetFloatAt(0);
+  float start_y = pCoords->GetFloatAt(1);
+  float start_r = pCoords->GetFloatAt(2);
+  float end_x = pCoords->GetFloatAt(3);
+  float end_y = pCoords->GetFloatAt(4);
+  float end_r = pCoords->GetFloatAt(5);
   float t_min = 0;
   float t_max = 1.0f;
-  const CPDF_Array* pArray = pDict->GetArrayFor("Domain");
+  RetainPtr<const CPDF_Array> pArray = pDict->GetArrayFor("Domain");
   if (pArray) {
-    t_min = pArray->GetNumberAt(0);
-    t_max = pArray->GetNumberAt(1);
+    t_min = pArray->GetFloatAt(0);
+    t_max = pArray->GetFloatAt(1);
   }
   pArray = pDict->GetArrayFor("Extend");
   const bool bStartExtend = pArray && pArray->GetBooleanAt(0, false);
@@ -191,19 +198,16 @@
   const float dy = end_y - start_y;
   const float dr = end_r - start_r;
   const float a = dx * dx + dy * dy - dr * dr;
-  const bool a_is_float_zero = IsFloatZero(a);
+  const bool a_is_float_zero = FXSYS_IsFloatZero(a);
 
   int width = pBitmap->GetWidth();
   int height = pBitmap->GetHeight();
-  int pitch = pBitmap->GetPitch();
-
-  bool bDecreasing =
-      (dr < 0 && static_cast<int>(sqrt(dx * dx + dy * dy)) < -dr);
+  bool bDecreasing = dr < 0 && static_cast<int>(FXSYS_sqrt2(dx, dy)) < -dr;
 
   CFX_Matrix matrix = mtObject2Bitmap.GetInverse();
   for (int row = 0; row < height; row++) {
     uint32_t* dib_buf =
-        reinterpret_cast<uint32_t*>(pBitmap->GetBuffer() + row * pitch);
+        reinterpret_cast<uint32_t*>(pBitmap->GetWritableScanline(row).data());
     for (int column = 0; column < width; column++) {
       CFX_PointF pos = matrix.Transform(
           CFX_PointF(static_cast<float>(column), static_cast<float>(row)));
@@ -212,7 +216,7 @@
       float b = -2 * (pos_dx * dx + pos_dy * dy + start_r * dr);
       float c = pos_dx * pos_dx + pos_dy * pos_dy - start_r * start_r;
       float s;
-      if (IsFloatZero(b)) {
+      if (FXSYS_IsFloatZero(b)) {
         s = sqrt(-c / a);
       } else if (a_is_float_zero) {
         s = -c / b;
@@ -256,57 +260,57 @@
                      const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
                      const RetainPtr<CPDF_ColorSpace>& pCS,
                      int alpha) {
-  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
+  DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb);
 
   const uint32_t total_results = GetValidatedOutputsCount(funcs, pCS);
   if (total_results == 0)
     return;
 
-  const CPDF_Array* pDomain = pDict->GetArrayFor("Domain");
+  RetainPtr<const CPDF_Array> pDomain = pDict->GetArrayFor("Domain");
   float xmin = 0.0f;
   float ymin = 0.0f;
   float xmax = 1.0f;
   float ymax = 1.0f;
   if (pDomain) {
-    xmin = pDomain->GetNumberAt(0);
-    xmax = pDomain->GetNumberAt(1);
-    ymin = pDomain->GetNumberAt(2);
-    ymax = pDomain->GetNumberAt(3);
+    xmin = pDomain->GetFloatAt(0);
+    xmax = pDomain->GetFloatAt(1);
+    ymin = pDomain->GetFloatAt(2);
+    ymax = pDomain->GetFloatAt(3);
   }
   CFX_Matrix mtDomain2Target = pDict->GetMatrixFor("Matrix");
   CFX_Matrix matrix =
       mtObject2Bitmap.GetInverse() * mtDomain2Target.GetInverse();
   int width = pBitmap->GetWidth();
   int height = pBitmap->GetHeight();
-  int pitch = pBitmap->GetPitch();
 
-  ASSERT(total_results >= CountOutputsFromFunctions(funcs));
-  ASSERT(total_results >= pCS->CountComponents());
+  DCHECK(total_results >= CountOutputsFromFunctions(funcs));
+  DCHECK(total_results >= pCS->CountComponents());
   std::vector<float> result_array(total_results);
   for (int row = 0; row < height; ++row) {
-    uint32_t* dib_buf = (uint32_t*)(pBitmap->GetBuffer() + row * pitch);
+    uint32_t* dib_buf =
+        reinterpret_cast<uint32_t*>(pBitmap->GetWritableScanline(row).data());
     for (int column = 0; column < width; column++) {
       CFX_PointF pos = matrix.Transform(
           CFX_PointF(static_cast<float>(column), static_cast<float>(row)));
       if (pos.x < xmin || pos.x > xmax || pos.y < ymin || pos.y > ymax)
         continue;
 
-      float input[] = {pos.x, pos.y};
-      int offset = 0;
+      float input[2] = {pos.x, pos.y};
+      pdfium::span<float> result_span = pdfium::make_span(result_array);
       for (const auto& func : funcs) {
-        if (func) {
-          int nresults;
-          if (func->Call(input, 2, &result_array[offset], &nresults))
-            offset += nresults;
-        }
+        if (!func)
+          continue;
+        absl::optional<uint32_t> nresults = func->Call(input, result_span);
+        if (nresults.has_value())
+          result_span = result_span.subspan(nresults.value());
       }
-
       float R = 0.0f;
       float G = 0.0f;
       float B = 0.0f;
-      pCS->GetRGB(result_array.data(), &R, &G, &B);
-      dib_buf[column] = FXARGB_TODIB(ArgbEncode(
-          alpha, (int32_t)(R * 255), (int32_t)(G * 255), (int32_t)(B * 255)));
+      pCS->GetRGB(result_array, &R, &G, &B);
+      dib_buf[column] = ArgbEncode(alpha, static_cast<int32_t>(R * 255),
+                                   static_cast<int32_t>(G * 255),
+                                   static_cast<int32_t>(B * 255));
     }
   }
 }
@@ -340,9 +344,8 @@
   if (min_y == max_y)
     return;
 
-  int min_yi = std::max(static_cast<int>(floor(min_y)), 0);
-  int max_yi = static_cast<int>(ceil(max_y));
-
+  int min_yi = std::max(static_cast<int>(floorf(min_y)), 0);
+  int max_yi = static_cast<int>(ceilf(max_y));
   if (max_yi >= pBitmap->GetHeight())
     max_yi = pBitmap->GetHeight() - 1;
 
@@ -371,40 +374,42 @@
     if (nIntersects != 2)
       continue;
 
-    int min_x, max_x, start_index, end_index;
+    int min_x;
+    int max_x;
+    int start_index;
+    int end_index;
     if (inter_x[0] < inter_x[1]) {
-      min_x = (int)floor(inter_x[0]);
-      max_x = (int)ceil(inter_x[1]);
+      min_x = static_cast<int>(floorf(inter_x[0]));
+      max_x = static_cast<int>(ceilf(inter_x[1]));
       start_index = 0;
       end_index = 1;
     } else {
-      min_x = (int)floor(inter_x[1]);
-      max_x = (int)ceil(inter_x[0]);
+      min_x = static_cast<int>(floorf(inter_x[1]));
+      max_x = static_cast<int>(ceilf(inter_x[0]));
       start_index = 1;
       end_index = 0;
     }
 
-    int start_x = std::max(min_x, 0);
-    int end_x = max_x;
-    if (end_x > pBitmap->GetWidth())
-      end_x = pBitmap->GetWidth();
-
-    uint8_t* dib_buf =
-        pBitmap->GetBuffer() + y * pBitmap->GetPitch() + start_x * 4;
+    int start_x = 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);
-    float R = r[start_index] + (start_x - min_x) * r_unit;
-    float G = g[start_index] + (start_x - min_x) * g_unit;
-    float B = b[start_index] + (start_x - min_x) * b_unit;
+    float r_result = r[start_index] + (start_x - min_x) * r_unit;
+    float g_result = g[start_index] + (start_x - min_x) * g_unit;
+    float b_result = b[start_index] + (start_x - min_x) * b_unit;
+    pdfium::span<uint8_t> dib_span =
+        pBitmap->GetWritableScanline(y).subspan(start_x * 4);
+
     for (int x = start_x; x < end_x; x++) {
-      R += r_unit;
-      G += g_unit;
-      B += b_unit;
-      FXARGB_SETDIB(dib_buf,
-                    ArgbEncode(alpha, (int32_t)(R * 255), (int32_t)(G * 255),
-                               (int32_t)(B * 255)));
-      dib_buf += 4;
+      uint8_t* dib_buf = dib_span.data();
+      r_result += r_unit;
+      g_result += g_unit;
+      b_result += b_unit;
+      FXARGB_SETDIB(dib_buf, ArgbEncode(alpha, static_cast<int>(r_result * 255),
+                                        static_cast<int>(g_result * 255),
+                                        static_cast<int>(b_result * 255)));
+      dib_span = dib_span.subspan(4);
     }
   }
 }
@@ -412,21 +417,19 @@
 void DrawFreeGouraudShading(
     const RetainPtr<CFX_DIBitmap>& pBitmap,
     const CFX_Matrix& mtObject2Bitmap,
-    const CPDF_Stream* pShadingStream,
+    RetainPtr<const CPDF_Stream> pShadingStream,
     const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-    const RetainPtr<CPDF_ColorSpace>& pCS,
+    RetainPtr<CPDF_ColorSpace> pCS,
     int alpha) {
-  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
+  DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb);
 
   CPDF_MeshStream stream(kFreeFormGouraudTriangleMeshShading, funcs,
-                         pShadingStream, pCS);
+                         std::move(pShadingStream), std::move(pCS));
   if (!stream.Load())
     return;
 
   CPDF_MeshVertex triangle[3];
-  memset(triangle, 0, sizeof(triangle));
-
-  while (!stream.BitStream()->IsEOF()) {
+  while (!stream.IsEOF()) {
     CPDF_MeshVertex vertex;
     uint32_t flag;
     if (!stream.ReadVertex(mtObject2Bitmap, &vertex, &flag))
@@ -434,9 +437,9 @@
 
     if (flag == 0) {
       triangle[0] = vertex;
-      for (int j = 1; j < 3; j++) {
-        uint32_t tflag;
-        if (!stream.ReadVertex(mtObject2Bitmap, &triangle[j], &tflag))
+      for (int i = 1; i < 3; ++i) {
+        uint32_t dummy_flag;
+        if (!stream.ReadVertex(mtObject2Bitmap, &triangle[i], &dummy_flag))
           return;
       }
     } else {
@@ -453,18 +456,18 @@
 void DrawLatticeGouraudShading(
     const RetainPtr<CFX_DIBitmap>& pBitmap,
     const CFX_Matrix& mtObject2Bitmap,
-    const CPDF_Stream* pShadingStream,
+    RetainPtr<const CPDF_Stream> pShadingStream,
     const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-    const RetainPtr<CPDF_ColorSpace>& pCS,
+    RetainPtr<CPDF_ColorSpace> pCS,
     int alpha) {
-  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
+  DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb);
 
   int row_verts = pShadingStream->GetDict()->GetIntegerFor("VerticesPerRow");
   if (row_verts < 2)
     return;
 
   CPDF_MeshStream stream(kLatticeFormGouraudTriangleMeshShading, funcs,
-                         pShadingStream, pCS);
+                         std::move(pShadingStream), std::move(pCS));
   if (!stream.Load())
     return;
 
@@ -474,7 +477,7 @@
     return;
 
   int last_index = 0;
-  while (1) {
+  while (true) {
     vertices[1 - last_index] = stream.ReadVertexRow(mtObject2Bitmap, row_verts);
     if (vertices[1 - last_index].empty())
       return;
@@ -492,123 +495,127 @@
   }
 }
 
-struct Coon_BezierCoeff {
-  float a, b, c, d;
-  void FromPoints(float p0, float p1, float p2, float p3) {
+struct CoonBezierCoeff {
+  void InitFromPoints(float p0, float p1, float p2, float p3) {
     a = -p0 + 3 * p1 - 3 * p2 + p3;
     b = 3 * p0 - 6 * p1 + 3 * p2;
     c = -3 * p0 + 3 * p1;
     d = p0;
   }
-  Coon_BezierCoeff first_half() {
-    Coon_BezierCoeff result;
-    result.a = a / 8;
-    result.b = b / 4;
-    result.c = c / 2;
-    result.d = d;
-    return result;
-  }
-  Coon_BezierCoeff second_half() {
-    Coon_BezierCoeff result;
-    result.a = a / 8;
-    result.b = 3 * a / 8 + b / 4;
-    result.c = 3 * a / 8 + b / 2 + c / 2;
-    result.d = a / 8 + b / 4 + c / 2 + d;
-    return result;
-  }
-  void GetPoints(float p[4]) {
-    p[0] = d;
-    p[1] = c / 3 + p[0];
-    p[2] = b / 3 - p[0] + 2 * p[1];
-    p[3] = a + p[0] - 3 * p[1] + 3 * p[2];
-  }
-  void GetPointsReverse(float p[4]) {
-    p[3] = d;
-    p[2] = c / 3 + p[3];
-    p[1] = b / 3 - p[3] + 2 * p[2];
-    p[0] = a + p[3] - 3 * p[2] + 3 * p[1];
-  }
-  void BezierInterpol(Coon_BezierCoeff& C1,
-                      Coon_BezierCoeff& C2,
-                      Coon_BezierCoeff& D1,
-                      Coon_BezierCoeff& D2) {
+
+  void InitFromBezierInterpolation(const CoonBezierCoeff& C1,
+                                   const CoonBezierCoeff& C2,
+                                   const CoonBezierCoeff& D1,
+                                   const CoonBezierCoeff& D2) {
     a = (D1.a + D2.a) / 2;
     b = (D1.b + D2.b) / 2;
     c = (D1.c + D2.c) / 2 - (C1.a / 8 + C1.b / 4 + C1.c / 2) +
         (C2.a / 8 + C2.b / 4) + (-C1.d + D2.d) / 2 - (C2.a + C2.b) / 2;
     d = C1.a / 8 + C1.b / 4 + C1.c / 2 + C1.d;
   }
-  float Distance() {
+
+  CoonBezierCoeff first_half() const {
+    CoonBezierCoeff result;
+    result.a = a / 8;
+    result.b = b / 4;
+    result.c = c / 2;
+    result.d = d;
+    return result;
+  }
+
+  CoonBezierCoeff second_half() const {
+    CoonBezierCoeff result;
+    result.a = a / 8;
+    result.b = 3 * a / 8 + b / 4;
+    result.c = 3 * a / 8 + b / 2 + c / 2;
+    result.d = a / 8 + b / 4 + c / 2 + d;
+    return result;
+  }
+
+  void GetPoints(float p[4]) const {
+    p[0] = d;
+    p[1] = c / 3 + p[0];
+    p[2] = b / 3 - p[0] + 2 * p[1];
+    p[3] = a + p[0] - 3 * p[1] + 3 * p[2];
+  }
+
+  float Distance() const {
     float dis = a + b + c;
     return dis < 0 ? -dis : dis;
   }
+
+  float a;
+  float b;
+  float c;
+  float d;
 };
 
-struct Coon_Bezier {
-  Coon_BezierCoeff x, y;
-  void FromPoints(float x0,
-                  float y0,
-                  float x1,
-                  float y1,
-                  float x2,
-                  float y2,
-                  float x3,
-                  float y3) {
-    x.FromPoints(x0, x1, x2, x3);
-    y.FromPoints(y0, y1, y2, y3);
+struct CoonBezier {
+  void InitFromPoints(float x0,
+                      float y0,
+                      float x1,
+                      float y1,
+                      float x2,
+                      float y2,
+                      float x3,
+                      float y3) {
+    x.InitFromPoints(x0, x1, x2, x3);
+    y.InitFromPoints(y0, y1, y2, y3);
   }
 
-  Coon_Bezier first_half() {
-    Coon_Bezier result;
+  void InitFromBezierInterpolation(const CoonBezier& C1,
+                                   const CoonBezier& C2,
+                                   const CoonBezier& D1,
+                                   const CoonBezier& D2) {
+    x.InitFromBezierInterpolation(C1.x, C2.x, D1.x, D2.x);
+    y.InitFromBezierInterpolation(C1.y, C2.y, D1.y, D2.y);
+  }
+
+  CoonBezier first_half() const {
+    CoonBezier result;
     result.x = x.first_half();
     result.y = y.first_half();
     return result;
   }
 
-  Coon_Bezier second_half() {
-    Coon_Bezier result;
+  CoonBezier second_half() const {
+    CoonBezier result;
     result.x = x.second_half();
     result.y = y.second_half();
     return result;
   }
 
-  void BezierInterpol(Coon_Bezier& C1,
-                      Coon_Bezier& C2,
-                      Coon_Bezier& D1,
-                      Coon_Bezier& D2) {
-    x.BezierInterpol(C1.x, C2.x, D1.x, D2.x);
-    y.BezierInterpol(C1.y, C2.y, D1.y, D2.y);
+  void GetPoints(pdfium::span<CFX_Path::Point> path_points) const {
+    constexpr size_t kPointsCount = 4;
+    float points_x[kPointsCount];
+    float points_y[kPointsCount];
+    x.GetPoints(points_x);
+    y.GetPoints(points_y);
+    for (size_t i = 0; i < kPointsCount; ++i)
+      path_points[i].m_Point = {points_x[i], points_y[i]};
   }
 
-  void GetPoints(std::vector<FX_PATHPOINT>& pPoints, size_t start_idx) {
-    float p[4];
-    int i;
-    x.GetPoints(p);
-    for (i = 0; i < 4; i++)
-      pPoints[start_idx + i].m_Point.x = p[i];
-
-    y.GetPoints(p);
-    for (i = 0; i < 4; i++)
-      pPoints[start_idx + i].m_Point.y = p[i];
+  void GetPointsReverse(pdfium::span<CFX_Path::Point> path_points) const {
+    constexpr size_t kPointsCount = 4;
+    float points_x[kPointsCount];
+    float points_y[kPointsCount];
+    x.GetPoints(points_x);
+    y.GetPoints(points_y);
+    for (size_t i = 0; i < kPointsCount; ++i) {
+      size_t reverse_index = kPointsCount - i - 1;
+      path_points[i].m_Point = {points_x[reverse_index],
+                                points_y[reverse_index]};
+    }
   }
 
-  void GetPointsReverse(std::vector<FX_PATHPOINT>& pPoints, size_t start_idx) {
-    float p[4];
-    int i;
-    x.GetPointsReverse(p);
-    for (i = 0; i < 4; i++)
-      pPoints[i + start_idx].m_Point.x = p[i];
+  float Distance() const { return x.Distance() + y.Distance(); }
 
-    y.GetPointsReverse(p);
-    for (i = 0; i < 4; i++)
-      pPoints[i + start_idx].m_Point.y = p[i];
-  }
-
-  float Distance() { return x.Distance() + y.Distance(); }
+  CoonBezierCoeff x;
+  CoonBezierCoeff y;
 };
 
 int Interpolate(int p1, int p2, int delta1, int delta2, bool* overflow) {
-  pdfium::base::CheckedNumeric<int> p = p2;
+  FX_SAFE_INT32 p = p2;
   p -= p1;
   p *= delta1;
   p /= delta2;
@@ -632,15 +639,11 @@
   return Interpolate(x1, x2, y, y_scale, overflow);
 }
 
-struct Coon_Color {
-  Coon_Color() { memset(comp, 0, sizeof(int) * 3); }
+struct CoonColor {
+  CoonColor() = default;
 
   // Returns true if successful, false if overflow detected.
-  bool BiInterpol(Coon_Color colors[4],
-                  int x,
-                  int y,
-                  int x_scale,
-                  int y_scale) {
+  bool BiInterpol(CoonColor colors[4], int x, int y, int x_scale, int y_scale) {
     bool overflow = false;
     for (int i = 0; i < 3; i++) {
       comp[i] = BiInterpolImpl(colors[0].comp[i], colors[1].comp[i],
@@ -650,27 +653,28 @@
     return !overflow;
   }
 
-  int Distance(Coon_Color& o) {
+  int Distance(const CoonColor& o) const {
     return std::max({abs(comp[0] - o.comp[0]), abs(comp[1] - o.comp[1]),
                      abs(comp[2] - o.comp[2])});
   }
 
-  int comp[3];
+  int comp[3] = {};
 };
 
-#define COONCOLOR_THRESHOLD 4
-struct CPDF_PatchDrawer {
+struct PatchDrawer {
+  static constexpr int kCoonColorThreshold = 4;
+
   void Draw(int x_scale,
             int y_scale,
             int left,
             int bottom,
-            Coon_Bezier C1,
-            Coon_Bezier C2,
-            Coon_Bezier D1,
-            Coon_Bezier D2) {
+            CoonBezier C1,
+            CoonBezier C2,
+            CoonBezier D1,
+            CoonBezier D2) {
     bool bSmall = C1.Distance() < 2 && C2.Distance() < 2 && D1.Distance() < 2 &&
                   D2.Distance() < 2;
-    Coon_Color div_colors[4];
+    CoonColor div_colors[4];
     int d_bottom = 0;
     int d_left = 0;
     int d_top = 0;
@@ -699,35 +703,37 @@
     }
 
     if (bSmall ||
-        (d_bottom < COONCOLOR_THRESHOLD && d_left < COONCOLOR_THRESHOLD &&
-         d_top < COONCOLOR_THRESHOLD && d_right < COONCOLOR_THRESHOLD)) {
-      std::vector<FX_PATHPOINT>& pPoints = path.GetPoints();
-      C1.GetPoints(pPoints, 0);
-      D2.GetPoints(pPoints, 3);
-      C2.GetPointsReverse(pPoints, 6);
-      D1.GetPointsReverse(pPoints, 9);
-      int fillFlags = FXFILL_WINDING | FXFILL_FULLCOVER;
+        (d_bottom < kCoonColorThreshold && d_left < kCoonColorThreshold &&
+         d_top < kCoonColorThreshold && d_right < kCoonColorThreshold)) {
+      pdfium::span<CFX_Path::Point> points = path.GetPoints();
+      C1.GetPoints(points.subspan(0, 4));
+      D2.GetPoints(points.subspan(3, 4));
+      C2.GetPointsReverse(points.subspan(6, 4));
+      D1.GetPointsReverse(points.subspan(9, 4));
+      CFX_FillRenderOptions fill_options(
+          CFX_FillRenderOptions::WindingOptions());
+      fill_options.full_cover = true;
       if (bNoPathSmooth)
-        fillFlags |= FXFILL_NOPATHSMOOTH;
+        fill_options.aliased_path = true;
       pDevice->DrawPath(
-          &path, nullptr, nullptr,
+          path, nullptr, nullptr,
           ArgbEncode(alpha, div_colors[0].comp[0], div_colors[0].comp[1],
                      div_colors[0].comp[2]),
-          0, fillFlags);
+          0, fill_options);
     } else {
-      if (d_bottom < COONCOLOR_THRESHOLD && d_top < COONCOLOR_THRESHOLD) {
-        Coon_Bezier m1;
-        m1.BezierInterpol(D1, D2, C1, C2);
+      if (d_bottom < kCoonColorThreshold && d_top < kCoonColorThreshold) {
+        CoonBezier m1;
+        m1.InitFromBezierInterpolation(D1, D2, C1, C2);
         y_scale *= 2;
         bottom *= 2;
         Draw(x_scale, y_scale, left, bottom, C1, m1, D1.first_half(),
              D2.first_half());
         Draw(x_scale, y_scale, left, bottom + 1, m1, C2, D1.second_half(),
              D2.second_half());
-      } else if (d_left < COONCOLOR_THRESHOLD &&
-                 d_right < COONCOLOR_THRESHOLD) {
-        Coon_Bezier m2;
-        m2.BezierInterpol(C1, C2, D1, D2);
+      } else if (d_left < kCoonColorThreshold &&
+                 d_right < kCoonColorThreshold) {
+        CoonBezier m2;
+        m2.InitFromBezierInterpolation(C1, C2, D1, D2);
         x_scale *= 2;
         left *= 2;
         Draw(x_scale, y_scale, left, bottom, C1.first_half(), C2.first_half(),
@@ -735,13 +741,14 @@
         Draw(x_scale, y_scale, left + 1, bottom, C1.second_half(),
              C2.second_half(), m2, D2);
       } else {
-        Coon_Bezier m1, m2;
-        m1.BezierInterpol(D1, D2, C1, C2);
-        m2.BezierInterpol(C1, C2, D1, D2);
-        Coon_Bezier m1f = m1.first_half();
-        Coon_Bezier m1s = m1.second_half();
-        Coon_Bezier m2f = m2.first_half();
-        Coon_Bezier m2s = m2.second_half();
+        CoonBezier m1;
+        CoonBezier m2;
+        m1.InitFromBezierInterpolation(D1, D2, C1, C2);
+        m2.InitFromBezierInterpolation(C1, C2, D1, D2);
+        CoonBezier m1f = m1.first_half();
+        CoonBezier m1s = m1.second_half();
+        CoonBezier m2f = m2.first_half();
+        CoonBezier m2s = m2.second_half();
         x_scale *= 2;
         y_scale *= 2;
         left *= 2;
@@ -759,49 +766,54 @@
   }
 
   int max_delta;
-  CFX_PathData path;
-  CFX_RenderDevice* pDevice;
+  CFX_Path path;
+  UnownedPtr<CFX_RenderDevice> pDevice;
   int bNoPathSmooth;
   int alpha;
-  Coon_Color patch_colors[4];
+  CoonColor patch_colors[4];
 };
 
 void DrawCoonPatchMeshes(
     ShadingType type,
     const RetainPtr<CFX_DIBitmap>& pBitmap,
     const CFX_Matrix& mtObject2Bitmap,
-    const CPDF_Stream* pShadingStream,
+    RetainPtr<const CPDF_Stream> pShadingStream,
     const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-    const RetainPtr<CPDF_ColorSpace>& pCS,
+    RetainPtr<CPDF_ColorSpace> pCS,
     bool bNoPathSmooth,
     int alpha) {
-  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
-  ASSERT(type == kCoonsPatchMeshShading ||
+  DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb);
+  DCHECK(type == kCoonsPatchMeshShading ||
          type == kTensorProductPatchMeshShading);
 
   CFX_DefaultRenderDevice device;
-  device.Attach(pBitmap, false, nullptr, false);
-  CPDF_MeshStream stream(type, funcs, pShadingStream, pCS);
+  device.Attach(pBitmap);
+
+  CPDF_MeshStream stream(type, funcs, std::move(pShadingStream),
+                         std::move(pCS));
   if (!stream.Load())
     return;
 
-  CPDF_PatchDrawer patch;
+  PatchDrawer patch;
   patch.alpha = alpha;
   patch.pDevice = &device;
   patch.bNoPathSmooth = bNoPathSmooth;
 
   for (int i = 0; i < 13; i++) {
-    patch.path.AppendPoint(
-        CFX_PointF(), i == 0 ? FXPT_TYPE::MoveTo : FXPT_TYPE::BezierTo, false);
+    patch.path.AppendPoint(CFX_PointF(), i == 0
+                                             ? CFX_Path::Point::Type::kMove
+                                             : CFX_Path::Point::Type::kBezier);
   }
 
   CFX_PointF coords[16];
   int point_count = type == kTensorProductPatchMeshShading ? 16 : 12;
-  while (!stream.BitStream()->IsEOF()) {
+  while (!stream.IsEOF()) {
     if (!stream.CanReadFlag())
       break;
     uint32_t flag = stream.ReadFlag();
-    int iStartPoint = 0, iStartColor = 0, i = 0;
+    int iStartPoint = 0;
+    int iStartColor = 0;
+    int i = 0;
     if (flag) {
       iStartPoint = 4;
       iStartColor = 2;
@@ -809,11 +821,12 @@
       for (i = 0; i < 4; i++) {
         tempCoords[i] = coords[(flag * 3 + i) % 12];
       }
-      memcpy(coords, tempCoords, sizeof(tempCoords));
-      Coon_Color tempColors[2];
-      tempColors[0] = patch.patch_colors[flag];
-      tempColors[1] = patch.patch_colors[(flag + 1) % 4];
-      memcpy(patch.patch_colors, tempColors, sizeof(Coon_Color) * 2);
+      fxcrt::spancpy(pdfium::make_span(coords), pdfium::make_span(tempCoords));
+      CoonColor tempColors[2] = {
+          tempColors[0] = patch.patch_colors[flag],
+          tempColors[1] = patch.patch_colors[(flag + 1) % 4]};
+      fxcrt::spancpy(pdfium::make_span(patch.patch_colors),
+                     pdfium::make_span(tempColors));
     }
     for (i = iStartPoint; i < point_count; i++) {
       if (!stream.CanReadCoords())
@@ -830,24 +843,28 @@
       float b;
       std::tie(r, g, b) = stream.ReadColor();
 
-      patch.patch_colors[i].comp[0] = (int32_t)(r * 255);
-      patch.patch_colors[i].comp[1] = (int32_t)(g * 255);
-      patch.patch_colors[i].comp[2] = (int32_t)(b * 255);
+      patch.patch_colors[i].comp[0] = static_cast<int32_t>(r * 255);
+      patch.patch_colors[i].comp[1] = static_cast<int32_t>(g * 255);
+      patch.patch_colors[i].comp[2] = static_cast<int32_t>(b * 255);
     }
-    CFX_FloatRect bbox = CFX_FloatRect::GetBBox(coords, point_count);
+    CFX_FloatRect bbox =
+        CFX_FloatRect::GetBBox(pdfium::make_span(coords).first(point_count));
     if (bbox.right <= 0 || bbox.left >= (float)pBitmap->GetWidth() ||
         bbox.top <= 0 || bbox.bottom >= (float)pBitmap->GetHeight()) {
       continue;
     }
-    Coon_Bezier C1, C2, D1, D2;
-    C1.FromPoints(coords[0].x, coords[0].y, coords[11].x, coords[11].y,
-                  coords[10].x, coords[10].y, coords[9].x, coords[9].y);
-    C2.FromPoints(coords[3].x, coords[3].y, coords[4].x, coords[4].y,
-                  coords[5].x, coords[5].y, coords[6].x, coords[6].y);
-    D1.FromPoints(coords[0].x, coords[0].y, coords[1].x, coords[1].y,
-                  coords[2].x, coords[2].y, coords[3].x, coords[3].y);
-    D2.FromPoints(coords[9].x, coords[9].y, coords[8].x, coords[8].y,
-                  coords[7].x, coords[7].y, coords[6].x, coords[6].y);
+    CoonBezier C1;
+    CoonBezier C2;
+    CoonBezier D1;
+    CoonBezier D2;
+    C1.InitFromPoints(coords[0].x, coords[0].y, coords[11].x, coords[11].y,
+                      coords[10].x, coords[10].y, coords[9].x, coords[9].y);
+    C2.InitFromPoints(coords[3].x, coords[3].y, coords[4].x, coords[4].y,
+                      coords[5].x, coords[5].y, coords[6].x, coords[6].y);
+    D1.InitFromPoints(coords[0].x, coords[0].y, coords[1].x, coords[1].y,
+                      coords[2].x, coords[2].y, coords[3].x, coords[3].y);
+    D2.InitFromPoints(coords[9].x, coords[9].y, coords[8].x, coords[8].y,
+                      coords[7].x, coords[7].y, coords[6].x, coords[6].y);
     patch.Draw(1, 1, 0, 0, C1, C2, D1, D2);
   }
 }
@@ -863,25 +880,26 @@
                               const FX_RECT& clip_rect,
                               int alpha,
                               const CPDF_RenderOptions& options) {
-  const auto& funcs = pPattern->GetFuncs();
-  const CPDF_Dictionary* pDict = pPattern->GetShadingObject()->GetDict();
   RetainPtr<CPDF_ColorSpace> pColorSpace = pPattern->GetCS();
   if (!pColorSpace)
     return;
 
   FX_ARGB background = 0;
+  RetainPtr<const CPDF_Dictionary> pDict =
+      pPattern->GetShadingObject()->GetDict();
   if (!pPattern->IsShadingObject() && pDict->KeyExist("Background")) {
-    const CPDF_Array* pBackColor = pDict->GetArrayFor("Background");
+    RetainPtr<const CPDF_Array> pBackColor = pDict->GetArrayFor("Background");
     if (pBackColor && pBackColor->size() >= pColorSpace->CountComponents()) {
-      std::vector<float> comps =
-          ReadArrayElementsToVector(pBackColor, pColorSpace->CountComponents());
+      std::vector<float> comps = ReadArrayElementsToVector(
+          pBackColor.Get(), pColorSpace->CountComponents());
 
       float R = 0.0f;
       float G = 0.0f;
       float B = 0.0f;
-      pColorSpace->GetRGB(comps.data(), &R, &G, &B);
-      background = ArgbEncode(255, (int32_t)(R * 255), (int32_t)(G * 255),
-                              (int32_t)(B * 255));
+      pColorSpace->GetRGB(comps, &R, &G, &B);
+      background = ArgbEncode(255, static_cast<int32_t>(R * 255),
+                              static_cast<int32_t>(G * 255),
+                              static_cast<int32_t>(B * 255));
     }
   }
   FX_RECT clip_rect_bbox = clip_rect;
@@ -891,64 +909,80 @@
   }
   bool bAlphaMode = options.ColorModeIs(CPDF_RenderOptions::kAlpha);
   if (pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SHADING &&
-      pDevice->GetDeviceDriver()->DrawShading(
-          pPattern, &mtMatrix, clip_rect_bbox, alpha, bAlphaMode)) {
+      pDevice->DrawShading(pPattern, &mtMatrix, clip_rect_bbox, alpha,
+                           bAlphaMode)) {
     return;
   }
   CPDF_DeviceBuffer buffer(pContext, pDevice, clip_rect_bbox, pCurObj, 150);
   if (!buffer.Initialize())
     return;
 
-  CFX_Matrix FinalMatrix = mtMatrix * buffer.GetMatrix();
   RetainPtr<CFX_DIBitmap> pBitmap = buffer.GetBitmap();
-  if (!pBitmap->GetBuffer())
+  if (pBitmap->GetBuffer().empty())
     return;
 
-  pBitmap->Clear(background);
+  if (background != 0) {
+    pBitmap->Clear(background);
+  }
+  const CFX_Matrix final_matrix = mtMatrix * buffer.GetMatrix();
+  const auto& funcs = pPattern->GetFuncs();
   switch (pPattern->GetShadingType()) {
     case kInvalidShading:
     case kMaxShading:
       return;
     case kFunctionBasedShading:
-      DrawFuncShading(pBitmap, FinalMatrix, pDict, funcs, pColorSpace, alpha);
+      DrawFuncShading(pBitmap, final_matrix, pDict.Get(), funcs, pColorSpace,
+                      alpha);
       break;
     case kAxialShading:
-      DrawAxialShading(pBitmap, FinalMatrix, pDict, funcs, pColorSpace, alpha);
+      DrawAxialShading(pBitmap, final_matrix, pDict.Get(), funcs, pColorSpace,
+                       alpha);
       break;
     case kRadialShading:
-      DrawRadialShading(pBitmap, FinalMatrix, pDict, funcs, pColorSpace, alpha);
+      DrawRadialShading(pBitmap, final_matrix, pDict.Get(), funcs, pColorSpace,
+                        alpha);
       break;
     case kFreeFormGouraudTriangleMeshShading: {
       // The shading object can be a stream or a dictionary. We do not handle
       // the case of dictionary at the moment.
-      if (const CPDF_Stream* pStream = ToStream(pPattern->GetShadingObject())) {
-        DrawFreeGouraudShading(pBitmap, FinalMatrix, pStream, funcs,
+      RetainPtr<const CPDF_Stream> pStream =
+          ToStream(pPattern->GetShadingObject());
+      if (pStream) {
+        DrawFreeGouraudShading(pBitmap, final_matrix, std::move(pStream), funcs,
                                pColorSpace, alpha);
       }
-    } break;
+      break;
+    }
     case kLatticeFormGouraudTriangleMeshShading: {
       // The shading object can be a stream or a dictionary. We do not handle
       // the case of dictionary at the moment.
-      if (const CPDF_Stream* pStream = ToStream(pPattern->GetShadingObject())) {
-        DrawLatticeGouraudShading(pBitmap, FinalMatrix, pStream, funcs,
-                                  pColorSpace, alpha);
+      RetainPtr<const CPDF_Stream> pStream =
+          ToStream(pPattern->GetShadingObject());
+      if (pStream) {
+        DrawLatticeGouraudShading(pBitmap, final_matrix, std::move(pStream),
+                                  funcs, pColorSpace, alpha);
       }
-    } break;
+      break;
+    }
     case kCoonsPatchMeshShading:
     case kTensorProductPatchMeshShading: {
       // The shading object can be a stream or a dictionary. We do not handle
       // the case of dictionary at the moment.
-      if (const CPDF_Stream* pStream = ToStream(pPattern->GetShadingObject())) {
-        DrawCoonPatchMeshes(pPattern->GetShadingType(), pBitmap, FinalMatrix,
-                            pStream, funcs, pColorSpace,
+      RetainPtr<const CPDF_Stream> pStream =
+          ToStream(pPattern->GetShadingObject());
+      if (pStream) {
+        DrawCoonPatchMeshes(pPattern->GetShadingType(), pBitmap, final_matrix,
+                            std::move(pStream), funcs, pColorSpace,
                             options.GetOptions().bNoPathSmooth, alpha);
       }
-    } break;
+      break;
+    }
   }
   if (bAlphaMode)
-    pBitmap->LoadChannelFromAlpha(FXDIB_Red, pBitmap);
+    pBitmap->SetRedFromBitmap(pBitmap);
 
   if (options.ColorModeIs(CPDF_RenderOptions::kGray))
     pBitmap->ConvertColorScale(0, 0xffffff);
+
   buffer.OutputToDevice();
 }
diff --git a/core/fpdfapi/render/cpdf_rendershading.h b/core/fpdfapi/render/cpdf_rendershading.h
index 8c0d8a4..a8702c9 100644
--- a/core/fpdfapi/render/cpdf_rendershading.h
+++ b/core/fpdfapi/render/cpdf_rendershading.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fpdfapi/render/cpdf_renderstatus.cpp b/core/fpdfapi/render/cpdf_renderstatus.cpp
index 79d31b9..39101f0 100644
--- a/core/fpdfapi/render/cpdf_renderstatus.cpp
+++ b/core/fpdfapi/render/cpdf_renderstatus.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,9 @@
 
 #include "core/fpdfapi/render/cpdf_renderstatus.h"
 
+#include <stdint.h>
+
 #include <algorithm>
-#include <cmath>
-#include <limits>
 #include <memory>
 #include <numeric>
 #include <set>
@@ -29,6 +29,7 @@
 #include "core/fpdfapi/page/cpdf_imageobject.h"
 #include "core/fpdfapi/page/cpdf_occontext.h"
 #include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/page/cpdf_pathobject.h"
 #include "core/fpdfapi/page/cpdf_shadingobject.h"
@@ -40,82 +41,91 @@
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
-#include "core/fpdfapi/render/cpdf_charposlist.h"
+#include "core/fpdfapi/render/charposlist.h"
 #include "core/fpdfapi/render/cpdf_docrenderdata.h"
 #include "core/fpdfapi/render/cpdf_imagerenderer.h"
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
 #include "core/fpdfapi/render/cpdf_rendercontext.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fpdfapi/render/cpdf_rendershading.h"
+#include "core/fpdfapi/render/cpdf_rendertiling.h"
 #include "core/fpdfapi/render/cpdf_scaledrenderbuffer.h"
 #include "core/fpdfapi/render/cpdf_textrenderer.h"
 #include "core/fpdfapi/render/cpdf_type3cache.h"
 #include "core/fxcrt/autorestorer.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_2d_size.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_glyphbitmap.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/fx_font.h"
 #include "core/fxge/renderdevicedriver_iface.h"
 #include "core/fxge/text_char_pos.h"
 #include "core/fxge/text_glyph_pos.h"
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/numerics/safe_math.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-#ifdef _SKIA_SUPPORT_
-#include "core/fxge/skia/fx_skia_device.h"
-#endif
+#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"
 
 namespace {
 
 constexpr int kRenderMaxRecursionDepth = 64;
 int g_CurrentRecursionDepth = 0;
 
-RetainPtr<CFX_DIBitmap> DrawPatternBitmap(
-    CPDF_Document* pDoc,
-    CPDF_PageRenderCache* pCache,
-    CPDF_TilingPattern* pPattern,
-    CPDF_Form* pPatternForm,
-    const CFX_Matrix& mtObject2Device,
-    int width,
-    int height,
-    const CPDF_RenderOptions::Options& draw_options) {
-  auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pBitmap->Create(width, height,
-                       pPattern->colored() ? FXDIB_Argb : FXDIB_8bppMask)) {
-    return nullptr;
+CFX_FillRenderOptions GetFillOptionsForDrawPathWithBlend(
+    const CPDF_RenderOptions::Options& options,
+    const CPDF_PathObject* path_obj,
+    CFX_FillRenderOptions::FillType fill_type,
+    bool is_stroke,
+    bool is_type3_char) {
+  CFX_FillRenderOptions fill_options(fill_type);
+  if (fill_type != CFX_FillRenderOptions::FillType::kNoFill && options.bRectAA)
+    fill_options.rect_aa = true;
+  if (options.bNoPathSmooth)
+    fill_options.aliased_path = true;
+  if (path_obj->m_GeneralState.GetStrokeAdjust())
+    fill_options.adjust_stroke = true;
+  if (is_stroke)
+    fill_options.stroke = true;
+  if (is_type3_char)
+    fill_options.text_mode = true;
+
+  return fill_options;
+}
+
+CFX_FillRenderOptions GetFillOptionsForDrawTextPath(
+    const CPDF_RenderOptions::Options& options,
+    const CPDF_TextObject* text_obj,
+    bool is_stroke,
+    bool is_fill) {
+  CFX_FillRenderOptions fill_options;
+  if (is_stroke && is_fill) {
+    fill_options.stroke = true;
+    fill_options.stroke_text_mode = true;
   }
-  CFX_DefaultRenderDevice bitmap_device;
-  bitmap_device.Attach(pBitmap, false, nullptr, false);
-  pBitmap->Clear(0);
-  CFX_FloatRect cell_bbox =
-      pPattern->pattern_to_form().TransformRect(pPattern->bbox());
-  cell_bbox = mtObject2Device.TransformRect(cell_bbox);
-  CFX_FloatRect bitmap_rect(0.0f, 0.0f, width, height);
-  CFX_Matrix mtAdjust;
-  mtAdjust.MatchRect(bitmap_rect, cell_bbox);
+  if (text_obj->m_GeneralState.GetStrokeAdjust())
+    fill_options.adjust_stroke = true;
+  if (options.bNoTextSmooth)
+    fill_options.aliased_path = true;
 
-  CFX_Matrix mtPattern2Bitmap = mtObject2Device * mtAdjust;
-  CPDF_RenderOptions options;
-  if (!pPattern->colored())
-    options.SetColorMode(CPDF_RenderOptions::kAlpha);
+  return fill_options;
+}
 
-  options.GetOptions() = draw_options;
-  options.GetOptions().bForceHalftone = true;
-
-  CPDF_RenderContext context(pDoc, nullptr, pCache);
-  context.AppendLayer(pPatternForm, &mtPattern2Bitmap);
-  context.Render(&bitmap_device, &options, nullptr);
-#if defined _SKIA_SUPPORT_PATHS_
-  bitmap_device.Flush(true);
-  pBitmap->UnPreMultiply();
+FXDIB_Format GetFormatForLuminosity(bool is_luminosity) {
+  if (!is_luminosity)
+    return FXDIB_Format::k8bppMask;
+#if BUILDFLAG(IS_APPLE)
+  return FXDIB_Format::kRgb32;
+#else
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    return FXDIB_Format::kRgb32;
+  return FXDIB_Format::kRgb;
 #endif
-  return pBitmap;
 }
 
 bool IsAvailableMatrix(const CFX_Matrix& matrix) {
@@ -146,29 +156,13 @@
   return pChar && (!pChar->colored() || MissingStrokeColor(pColorState));
 }
 
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-class ScopedSkiaDeviceFlush {
- public:
-  explicit ScopedSkiaDeviceFlush(CFX_RenderDevice* pDevice)
-      : m_pDevice(pDevice) {}
-
-  ScopedSkiaDeviceFlush(const ScopedSkiaDeviceFlush&) = delete;
-  ScopedSkiaDeviceFlush& operator=(const ScopedSkiaDeviceFlush&) = delete;
-
-  ~ScopedSkiaDeviceFlush() { m_pDevice->Flush(/*release=*/false); }
-
- private:
-  CFX_RenderDevice* const m_pDevice;
-};
-#endif
-
 }  // namespace
 
 CPDF_RenderStatus::CPDF_RenderStatus(CPDF_RenderContext* pContext,
                                      CFX_RenderDevice* pDevice)
     : m_pContext(pContext), m_pDevice(pDevice) {}
 
-CPDF_RenderStatus::~CPDF_RenderStatus() {}
+CPDF_RenderStatus::~CPDF_RenderStatus() = default;
 
 void CPDF_RenderStatus::Initialize(const CPDF_RenderStatus* pParentStatus,
                                    const CPDF_GraphicStates* pInitialStates) {
@@ -198,9 +192,6 @@
 void CPDF_RenderStatus::RenderObjectList(
     const CPDF_PageObjectHolder* pObjectHolder,
     const CFX_Matrix& mtObj2Device) {
-#if defined _SKIA_SUPPORT_
-  DebugVerifyDeviceIsPreMultiplied();
-#endif
   CFX_FloatRect clip_rect = mtObj2Device.GetInverse().TransformRect(
       CFX_FloatRect(m_pDevice->GetClipBox()));
   for (const auto& pCurObj : *pObjectHolder) {
@@ -221,23 +212,16 @@
     if (m_bStopped)
       return;
   }
-#if defined _SKIA_SUPPORT_
-  DebugVerifyDeviceIsPreMultiplied();
-#endif
 }
 
 void CPDF_RenderStatus::RenderSingleObject(CPDF_PageObject* pObj,
                                            const CFX_Matrix& mtObj2Device) {
-#if defined _SKIA_SUPPORT_
-  DebugVerifyDeviceIsPreMultiplied();
-#endif
   AutoRestorer<int> restorer(&g_CurrentRecursionDepth);
   if (++g_CurrentRecursionDepth > kRenderMaxRecursionDepth) {
     return;
   }
   m_pCurObj = pObj;
-  if (m_Options.GetOCContext() &&
-      !m_Options.GetOCContext()->CheckObjectVisible(pObj)) {
+  if (!m_Options.CheckPageObjectVisible(pObj)) {
     return;
   }
   ProcessClipPath(pObj->m_ClipPath, mtObj2Device);
@@ -245,9 +229,6 @@
     return;
   }
   ProcessObjectNoClip(pObj, mtObj2Device);
-#if defined _SKIA_SUPPORT_
-  DebugVerifyDeviceIsPreMultiplied();
-#endif
 }
 
 bool CPDF_RenderStatus::ContinueSingleObject(CPDF_PageObject* pObj,
@@ -264,10 +245,8 @@
   }
 
   m_pCurObj = pObj;
-  if (m_Options.GetOCContext() &&
-      !m_Options.GetOCContext()->CheckObjectVisible(pObj)) {
+  if (!m_Options.CheckPageObjectVisible(pObj))
     return false;
-  }
 
   ProcessClipPath(pObj->m_ClipPath, mtObj2Device);
   if (ProcessTransparency(pObj, mtObj2Device))
@@ -278,8 +257,8 @@
     return false;
   }
 
-  m_pImageRenderer = pdfium::MakeUnique<CPDF_ImageRenderer>();
-  if (!m_pImageRenderer->Start(this, pObj->AsImage(), mtObj2Device, false,
+  m_pImageRenderer = std::make_unique<CPDF_ImageRenderer>(this);
+  if (!m_pImageRenderer->Start(pObj->AsImage(), mtObj2Device, false,
                                BlendMode::kNormal)) {
     if (!m_pImageRenderer->GetResult())
       DrawObjWithBackground(pObj, mtObj2Device);
@@ -299,42 +278,36 @@
 
 void CPDF_RenderStatus::ProcessObjectNoClip(CPDF_PageObject* pObj,
                                             const CFX_Matrix& mtObj2Device) {
-#if defined _SKIA_SUPPORT_
-  DebugVerifyDeviceIsPreMultiplied();
-#endif
   bool bRet = false;
   switch (pObj->GetType()) {
-    case CPDF_PageObject::TEXT:
+    case CPDF_PageObject::Type::kText:
       bRet = ProcessText(pObj->AsText(), mtObj2Device, nullptr);
       break;
-    case CPDF_PageObject::PATH:
+    case CPDF_PageObject::Type::kPath:
       bRet = ProcessPath(pObj->AsPath(), mtObj2Device);
       break;
-    case CPDF_PageObject::IMAGE:
+    case CPDF_PageObject::Type::kImage:
       bRet = ProcessImage(pObj->AsImage(), mtObj2Device);
       break;
-    case CPDF_PageObject::SHADING:
+    case CPDF_PageObject::Type::kShading:
       ProcessShading(pObj->AsShading(), mtObj2Device);
       return;
-    case CPDF_PageObject::FORM:
+    case CPDF_PageObject::Type::kForm:
       bRet = ProcessForm(pObj->AsForm(), mtObj2Device);
       break;
   }
   if (!bRet)
     DrawObjWithBackground(pObj, mtObj2Device);
-#if defined _SKIA_SUPPORT_
-  DebugVerifyDeviceIsPreMultiplied();
-#endif
 }
 
 bool CPDF_RenderStatus::DrawObjWithBlend(CPDF_PageObject* pObj,
                                          const CFX_Matrix& mtObj2Device) {
   switch (pObj->GetType()) {
-    case CPDF_PageObject::PATH:
+    case CPDF_PageObject::Type::kPath:
       return ProcessPath(pObj->AsPath(), mtObj2Device);
-    case CPDF_PageObject::IMAGE:
+    case CPDF_PageObject::Type::kImage:
       return ProcessImage(pObj->AsImage(), mtObj2Device);
-    case CPDF_PageObject::FORM:
+    case CPDF_PageObject::Type::kForm:
       return ProcessForm(pObj->AsForm(), mtObj2Device);
     default:
       return false;
@@ -347,26 +320,22 @@
   if (rect.IsEmpty())
     return;
 
-  int res = 300;
-  if (pObj->IsImage() && m_pDevice->GetDeviceType() == DeviceType::kPrinter)
-    res = 0;
-
+  int res = (pObj->IsImage() && m_bPrint) ? 0 : 300;
   CPDF_ScaledRenderBuffer buffer;
-  if (!buffer.Initialize(m_pContext.Get(), m_pDevice, rect, pObj, &m_Options,
-                         res)) {
+  if (!buffer.Initialize(m_pContext, m_pDevice, rect, pObj, &m_Options, res)) {
     return;
   }
+  RetainPtr<const CPDF_Dictionary> pFormResource;
   CFX_Matrix matrix = mtObj2Device * buffer.GetMatrix();
-  const CPDF_Dictionary* pFormResource = nullptr;
   const CPDF_FormObject* pFormObj = pObj->AsForm();
   if (pFormObj)
     pFormResource = pFormObj->form()->GetDict()->GetDictFor("Resources");
-  CPDF_RenderStatus status(m_pContext.Get(), buffer.GetDevice());
+  CPDF_RenderStatus status(m_pContext, buffer.GetDevice());
   status.SetOptions(m_Options);
   status.SetDeviceMatrix(buffer.GetMatrix());
   status.SetTransparency(m_Transparency);
   status.SetDropObjects(m_bDropObjects);
-  status.SetFormResource(pFormResource);
+  status.SetFormResource(std::move(pFormResource));
   status.Initialize(nullptr, nullptr);
   status.RenderSingleObject(pObj, matrix);
   buffer.OutputToDevice();
@@ -374,23 +343,20 @@
 
 bool CPDF_RenderStatus::ProcessForm(const CPDF_FormObject* pFormObj,
                                     const CFX_Matrix& mtObj2Device) {
-#if defined _SKIA_SUPPORT_
-  DebugVerifyDeviceIsPreMultiplied();
-#endif
-  const CPDF_Dictionary* pOC = pFormObj->form()->GetDict()->GetDictFor("OC");
-  if (pOC && m_Options.GetOCContext() &&
-      !m_Options.GetOCContext()->CheckOCGVisible(pOC)) {
+  RetainPtr<const CPDF_Dictionary> pOC =
+      pFormObj->form()->GetDict()->GetDictFor("OC");
+  if (pOC && !m_Options.CheckOCGDictVisible(pOC.Get()))
     return true;
-  }
+
   CFX_Matrix matrix = pFormObj->form_matrix() * mtObj2Device;
-  const CPDF_Dictionary* pResources =
+  RetainPtr<const CPDF_Dictionary> pResources =
       pFormObj->form()->GetDict()->GetDictFor("Resources");
-  CPDF_RenderStatus status(m_pContext.Get(), m_pDevice);
+  CPDF_RenderStatus status(m_pContext, m_pDevice);
   status.SetOptions(m_Options);
-  status.SetStopObject(m_pStopObj.Get());
+  status.SetStopObject(m_pStopObj);
   status.SetTransparency(m_Transparency);
   status.SetDropObjects(m_bDropObjects);
-  status.SetFormResource(pResources);
+  status.SetFormResource(std::move(pResources));
   status.Initialize(this, pFormObj);
   status.m_curBlend = m_curBlend;
   {
@@ -398,63 +364,59 @@
     status.RenderObjectList(pFormObj->form(), matrix);
     m_bStopped = status.m_bStopped;
   }
-#if defined _SKIA_SUPPORT_
-  DebugVerifyDeviceIsPreMultiplied();
-#endif
   return true;
 }
 
-bool CPDF_RenderStatus::ProcessPath(CPDF_PathObject* pPathObj,
+bool CPDF_RenderStatus::ProcessPath(CPDF_PathObject* path_obj,
                                     const CFX_Matrix& mtObj2Device) {
-  int FillType = pPathObj->filltype();
-  bool bStroke = pPathObj->stroke();
-  ProcessPathPattern(pPathObj, mtObj2Device, &FillType, &bStroke);
-  if (FillType == 0 && !bStroke)
+  CFX_FillRenderOptions::FillType fill_type = path_obj->filltype();
+  bool stroke = path_obj->stroke();
+  ProcessPathPattern(path_obj, mtObj2Device, &fill_type, &stroke);
+  if (fill_type == CFX_FillRenderOptions::FillType::kNoFill && !stroke)
     return true;
 
-  uint32_t fill_argb = FillType ? GetFillArgb(pPathObj) : 0;
-  uint32_t stroke_argb = bStroke ? GetStrokeArgb(pPathObj) : 0;
-  CFX_Matrix path_matrix = pPathObj->matrix() * mtObj2Device;
+  // If the option to convert fill paths to stroke is enabled for forced color,
+  // set |fill_type| to FillType::kNoFill and |stroke| to true.
+  CPDF_RenderOptions::Options& options = m_Options.GetOptions();
+  if (m_Options.ColorModeIs(CPDF_RenderOptions::Type::kForcedColor) &&
+      options.bConvertFillToStroke &&
+      fill_type != CFX_FillRenderOptions::FillType::kNoFill) {
+    stroke = true;
+    fill_type = CFX_FillRenderOptions::FillType::kNoFill;
+  }
+
+  uint32_t fill_argb = fill_type != CFX_FillRenderOptions::FillType::kNoFill
+                           ? GetFillArgb(path_obj)
+                           : 0;
+  uint32_t stroke_argb = stroke ? GetStrokeArgb(path_obj) : 0;
+  CFX_Matrix path_matrix = path_obj->matrix() * mtObj2Device;
   if (!IsAvailableMatrix(path_matrix))
     return true;
 
-  if (FillType && m_Options.GetOptions().bRectAA)
-    FillType |= FXFILL_RECT_AA;
-  if (m_Options.GetOptions().bFillFullcover)
-    FillType |= FXFILL_FULLCOVER;
-  if (m_Options.GetOptions().bNoPathSmooth)
-    FillType |= FXFILL_NOPATHSMOOTH;
-  if (bStroke)
-    FillType |= FX_FILL_STROKE;
-
-  const CPDF_PageObject* pPageObj =
-      static_cast<const CPDF_PageObject*>(pPathObj);
-  if (pPageObj->m_GeneralState.GetStrokeAdjust())
-    FillType |= FX_STROKE_ADJUST;
-  if (m_pType3Char)
-    FillType |= FX_FILL_TEXT_MODE;
-
-  CFX_GraphState graphState = pPathObj->m_GraphState;
-  if (m_Options.GetOptions().bThinLine)
-    graphState.SetLineWidth(0);
   return m_pDevice->DrawPathWithBlend(
-      pPathObj->path().GetObject(), &path_matrix, graphState.GetObject(),
-      fill_argb, stroke_argb, FillType, m_curBlend);
+      *path_obj->path().GetObject(), &path_matrix,
+      path_obj->m_GraphState.GetObject(), fill_argb, stroke_argb,
+      GetFillOptionsForDrawPathWithBlend(options, path_obj, fill_type, stroke,
+                                         m_pType3Char),
+      m_curBlend);
 }
 
 RetainPtr<CPDF_TransferFunc> CPDF_RenderStatus::GetTransferFunc(
-    const CPDF_Object* pObj) const {
-  ASSERT(pObj);
+    RetainPtr<const CPDF_Object> pObj) const {
+  DCHECK(pObj);
   auto* pDocCache = CPDF_DocRenderData::FromDocument(m_pContext->GetDocument());
-  return pDocCache ? pDocCache->GetTransferFunc(pObj) : nullptr;
+  return pDocCache ? pDocCache->GetTransferFunc(std::move(pObj)) : nullptr;
 }
 
-FX_ARGB CPDF_RenderStatus::GetFillArgbInternal(CPDF_PageObject* pObj,
-                                               bool bType3) const {
-  const CPDF_ColorState* pColorState = &pObj->m_ColorState;
-  if (!bType3 && Type3CharMissingFillColor(m_pType3Char.Get(), pColorState))
+FX_ARGB CPDF_RenderStatus::GetFillArgb(CPDF_PageObject* pObj) const {
+  if (Type3CharMissingFillColor(m_pType3Char, &pObj->m_ColorState))
     return m_T3FillColor;
 
+  return GetFillArgbForType3(pObj);
+}
+
+FX_ARGB CPDF_RenderStatus::GetFillArgbForType3(CPDF_PageObject* pObj) const {
+  const CPDF_ColorState* pColorState = &pObj->m_ColorState;
   if (MissingFillColor(pColorState))
     pColorState = &m_InitialStates.m_ColorState;
 
@@ -464,22 +426,24 @@
 
   int32_t alpha =
       static_cast<int32_t>((pObj->m_GeneralState.GetFillAlpha() * 255));
-  if (pObj->m_GeneralState.GetTR()) {
+  RetainPtr<const CPDF_Object> pTR = pObj->m_GeneralState.GetTR();
+  if (pTR) {
     if (!pObj->m_GeneralState.GetTransferFunc()) {
-      pObj->m_GeneralState.SetTransferFunc(
-          GetTransferFunc(pObj->m_GeneralState.GetTR()));
+      pObj->m_GeneralState.SetTransferFunc(GetTransferFunc(std::move(pTR)));
     }
     if (pObj->m_GeneralState.GetTransferFunc()) {
       colorref =
           pObj->m_GeneralState.GetTransferFunc()->TranslateColor(colorref);
     }
   }
-  return m_Options.TranslateColor(AlphaAndColorRefToArgb(alpha, colorref));
+  return m_Options.TranslateObjectColor(AlphaAndColorRefToArgb(alpha, colorref),
+                                        pObj->GetType(),
+                                        CPDF_RenderOptions::RenderType::kFill);
 }
 
 FX_ARGB CPDF_RenderStatus::GetStrokeArgb(CPDF_PageObject* pObj) const {
   const CPDF_ColorState* pColorState = &pObj->m_ColorState;
-  if (Type3CharMissingStrokeColor(m_pType3Char.Get(), pColorState))
+  if (Type3CharMissingStrokeColor(m_pType3Char, pColorState))
     return m_T3FillColor;
 
   if (MissingStrokeColor(pColorState))
@@ -491,17 +455,19 @@
 
   int32_t alpha = static_cast<int32_t>(pObj->m_GeneralState.GetStrokeAlpha() *
                                        255);  // not rounded.
-  if (pObj->m_GeneralState.GetTR()) {
+  RetainPtr<const CPDF_Object> pTR = pObj->m_GeneralState.GetTR();
+  if (pTR) {
     if (!pObj->m_GeneralState.GetTransferFunc()) {
-      pObj->m_GeneralState.SetTransferFunc(
-          GetTransferFunc(pObj->m_GeneralState.GetTR()));
+      pObj->m_GeneralState.SetTransferFunc(GetTransferFunc(std::move(pTR)));
     }
     if (pObj->m_GeneralState.GetTransferFunc()) {
       colorref =
           pObj->m_GeneralState.GetTransferFunc()->TranslateColor(colorref);
     }
   }
-  return m_Options.TranslateColor(AlphaAndColorRefToArgb(alpha, colorref));
+  return m_Options.TranslateObjectColor(
+      AlphaAndColorRefToArgb(alpha, colorref), pObj->GetType(),
+      CPDF_RenderOptions::RenderType::kStroke);
 }
 
 void CPDF_RenderStatus::ProcessClipPath(const CPDF_ClipPath& ClipPath,
@@ -519,34 +485,36 @@
   m_LastClipPath = ClipPath;
   m_pDevice->RestoreState(true);
   for (size_t i = 0; i < ClipPath.GetPathCount(); ++i) {
-    const CFX_PathData* pPathData = ClipPath.GetPath(i).GetObject();
-    if (!pPathData)
+    const CFX_Path* pPath = ClipPath.GetPath(i).GetObject();
+    if (!pPath)
       continue;
 
-    if (pPathData->GetPoints().empty()) {
-      CFX_PathData EmptyPath;
-      EmptyPath.AppendRect(-1, -1, 0, 0);
-      m_pDevice->SetClip_PathFill(&EmptyPath, nullptr, FXFILL_WINDING);
+    if (pPath->GetPoints().empty()) {
+      CFX_Path empty_path;
+      empty_path.AppendRect(-1, -1, 0, 0);
+      m_pDevice->SetClip_PathFill(empty_path, nullptr,
+                                  CFX_FillRenderOptions::WindingOptions());
     } else {
-      m_pDevice->SetClip_PathFill(pPathData, &mtObj2Device,
-                                  ClipPath.GetClipType(i));
+      m_pDevice->SetClip_PathFill(
+          *pPath, &mtObj2Device,
+          CFX_FillRenderOptions(ClipPath.GetClipType(i)));
     }
   }
 
   if (ClipPath.GetTextCount() == 0)
     return;
 
-  if (m_pDevice->GetDeviceType() == DeviceType::kDisplay &&
+  if (!m_bPrint &&
       !(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SOFT_CLIP)) {
     return;
   }
 
-  std::unique_ptr<CFX_PathData> pTextClippingPath;
+  std::unique_ptr<CFX_Path> pTextClippingPath;
   for (size_t i = 0; i < ClipPath.GetTextCount(); ++i) {
     CPDF_TextObject* pText = ClipPath.GetText(i);
     if (pText) {
       if (!pTextClippingPath)
-        pTextClippingPath = pdfium::MakeUnique<CFX_PathData>();
+        pTextClippingPath = std::make_unique<CFX_Path>();
       ProcessText(pText, mtObj2Device, pTextClippingPath.get());
       continue;
     }
@@ -554,60 +522,55 @@
     if (!pTextClippingPath)
       continue;
 
-    int fill_mode = FXFILL_WINDING;
+    CFX_FillRenderOptions fill_options(CFX_FillRenderOptions::WindingOptions());
     if (m_Options.GetOptions().bNoTextSmooth)
-      fill_mode |= FXFILL_NOPATHSMOOTH;
-    m_pDevice->SetClip_PathFill(pTextClippingPath.get(), nullptr, fill_mode);
+      fill_options.aliased_path = true;
+    m_pDevice->SetClip_PathFill(*pTextClippingPath, nullptr, fill_options);
     pTextClippingPath.reset();
   }
 }
 
-bool CPDF_RenderStatus::ClipPattern(const CPDF_PageObject* pPageObj,
+bool CPDF_RenderStatus::ClipPattern(const CPDF_PageObject* page_obj,
                                     const CFX_Matrix& mtObj2Device,
-                                    bool bStroke) {
-  if (pPageObj->IsPath())
-    return SelectClipPath(pPageObj->AsPath(), mtObj2Device, bStroke);
-  if (pPageObj->IsImage()) {
-    m_pDevice->SetClip_Rect(pPageObj->GetTransformedBBox(mtObj2Device));
+                                    bool stroke) {
+  if (page_obj->IsPath())
+    return SelectClipPath(page_obj->AsPath(), mtObj2Device, stroke);
+  if (page_obj->IsImage()) {
+    m_pDevice->SetClip_Rect(page_obj->GetTransformedBBox(mtObj2Device));
     return true;
   }
   return false;
 }
 
-bool CPDF_RenderStatus::SelectClipPath(const CPDF_PathObject* pPathObj,
+bool CPDF_RenderStatus::SelectClipPath(const CPDF_PathObject* path_obj,
                                        const CFX_Matrix& mtObj2Device,
-                                       bool bStroke) {
-  CFX_Matrix path_matrix = pPathObj->matrix() * mtObj2Device;
-  if (bStroke) {
-    CFX_GraphState graphState = pPathObj->m_GraphState;
-    if (m_Options.GetOptions().bThinLine)
-      graphState.SetLineWidth(0);
-    return m_pDevice->SetClip_PathStroke(pPathObj->path().GetObject(),
-                                         &path_matrix, graphState.GetObject());
+                                       bool stroke) {
+  CFX_Matrix path_matrix = path_obj->matrix() * mtObj2Device;
+  if (stroke) {
+    return m_pDevice->SetClip_PathStroke(*path_obj->path().GetObject(),
+                                         &path_matrix,
+                                         path_obj->m_GraphState.GetObject());
   }
-  int fill_mode = pPathObj->filltype();
+  CFX_FillRenderOptions fill_options(path_obj->filltype());
   if (m_Options.GetOptions().bNoPathSmooth) {
-    fill_mode |= FXFILL_NOPATHSMOOTH;
+    fill_options.aliased_path = true;
   }
-  return m_pDevice->SetClip_PathFill(pPathObj->path().GetObject(), &path_matrix,
-                                     fill_mode);
+  return m_pDevice->SetClip_PathFill(*path_obj->path().GetObject(),
+                                     &path_matrix, fill_options);
 }
 
 bool CPDF_RenderStatus::ProcessTransparency(CPDF_PageObject* pPageObj,
                                             const CFX_Matrix& mtObj2Device) {
-#if defined _SKIA_SUPPORT_
-  DebugVerifyDeviceIsPreMultiplied();
-#endif
-  BlendMode blend_type = pPageObj->m_GeneralState.GetBlendType();
-  CPDF_Dictionary* pSMaskDict =
-      ToDictionary(pPageObj->m_GeneralState.GetSoftMask());
+  const BlendMode blend_type = pPageObj->m_GeneralState.GetBlendType();
+  RetainPtr<CPDF_Dictionary> pSMaskDict =
+      pPageObj->m_GeneralState.GetMutableSoftMask();
   if (pSMaskDict) {
     if (pPageObj->IsImage() &&
         pPageObj->AsImage()->GetImage()->GetDict()->KeyExist("SMask")) {
       pSMaskDict = nullptr;
     }
   }
-  const CPDF_Dictionary* pFormResource = nullptr;
+  RetainPtr<const CPDF_Dictionary> pFormResource;
   float group_alpha = 1.0f;
   CPDF_Transparency transparency = m_Transparency;
   bool bGroupTransparent = false;
@@ -620,36 +583,8 @@
   }
   bool bTextClip =
       (pPageObj->m_ClipPath.HasRef() &&
-       pPageObj->m_ClipPath.GetTextCount() > 0 &&
-       m_pDevice->GetDeviceType() == DeviceType::kDisplay &&
+       pPageObj->m_ClipPath.GetTextCount() > 0 && !m_bPrint &&
        !(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SOFT_CLIP));
-  if (m_Options.GetOptions().bOverprint && pPageObj->IsImage() &&
-      pPageObj->m_GeneralState.GetFillOP() &&
-      pPageObj->m_GeneralState.GetStrokeOP()) {
-    CPDF_Document* pDocument = nullptr;
-    CPDF_Page* pPage = nullptr;
-    if (m_pContext->GetPageCache()) {
-      pPage = m_pContext->GetPageCache()->GetPage();
-      pDocument = pPage->GetDocument();
-    } else {
-      pDocument = pPageObj->AsImage()->GetImage()->GetDocument();
-    }
-    const CPDF_Dictionary* pPageResources =
-        pPage ? pPage->m_pPageResources.Get() : nullptr;
-    auto* pImageStream = pPageObj->AsImage()->GetImage()->GetStream();
-    const CPDF_Object* pCSObj =
-        pImageStream->GetDict()->GetDirectObjectFor("ColorSpace");
-    RetainPtr<CPDF_ColorSpace> pColorSpace =
-        CPDF_DocPageData::FromDocument(pDocument)->GetColorSpace(
-            pCSObj, pPageResources);
-    if (pColorSpace) {
-      int format = pColorSpace->GetFamily();
-      if (format == PDFCS_DEVICECMYK || format == PDFCS_SEPARATION ||
-          format == PDFCS_DEVICEN) {
-        blend_type = BlendMode::kDarken;
-      }
-    }
-  }
   if (!pSMaskDict && group_alpha == 1.0f && blend_type == BlendMode::kNormal &&
       !bTextClip && !bGroupTransparent) {
     return false;
@@ -685,24 +620,20 @@
       return true;
     m_pDevice->GetDIBits(backdrop, rect.left, rect.top);
   }
-  if (!bitmap_device.Create(width, height, FXDIB_Argb, backdrop))
+  if (!bitmap_device.Create(width, height, FXDIB_Format::kArgb, backdrop))
     return true;
 
-  RetainPtr<CFX_DIBitmap> bitmap = bitmap_device.GetBitmap();
-  bitmap->Clear(0);
-
   CFX_Matrix new_matrix = mtObj2Device;
   new_matrix.Translate(-rect.left, -rect.top);
 
   RetainPtr<CFX_DIBitmap> pTextMask;
   if (bTextClip) {
     pTextMask = pdfium::MakeRetain<CFX_DIBitmap>();
-    if (!pTextMask->Create(width, height, FXDIB_8bppMask))
+    if (!pTextMask->Create(width, height, FXDIB_Format::k8bppMask))
       return true;
 
-    pTextMask->Clear(0);
     CFX_DefaultRenderDevice text_device;
-    text_device.Attach(pTextMask, false, nullptr, false);
+    text_device.Attach(pTextMask);
     for (size_t i = 0; i < pPageObj->m_ClipPath.GetTextCount(); ++i) {
       CPDF_TextObject* textobj = pPageObj->m_ClipPath.GetText(i);
       if (!textobj)
@@ -714,125 +645,116 @@
           textobj->m_TextState.GetFont().Get(),
           textobj->m_TextState.GetFontSize(), textobj->GetTextMatrix(),
           &new_matrix, textobj->m_GraphState.GetObject(), 0xffffffff, 0,
-          nullptr, 0);
+          nullptr, CFX_FillRenderOptions());
     }
   }
-  CPDF_RenderStatus bitmap_render(m_pContext.Get(), &bitmap_device);
+  CPDF_RenderStatus bitmap_render(m_pContext, &bitmap_device);
   bitmap_render.SetOptions(m_Options);
-  bitmap_render.SetStopObject(m_pStopObj.Get());
+  bitmap_render.SetStopObject(m_pStopObj);
   bitmap_render.SetStdCS(true);
   bitmap_render.SetDropObjects(m_bDropObjects);
-  bitmap_render.SetFormResource(pFormResource);
+  bitmap_render.SetFormResource(std::move(pFormResource));
   bitmap_render.Initialize(nullptr, nullptr);
   bitmap_render.ProcessObjectNoClip(pPageObj, new_matrix);
-#if defined _SKIA_SUPPORT_PATHS_
-  bitmap_device.Flush(true);
-  bitmap->UnPreMultiply();
-#endif
+#if defined(_SKIA_SUPPORT_)
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    // Safe because `CFX_SkiaDeviceDriver` always uses pre-multiplied alpha.
+    // TODO(crbug.com/pdfium/2011): Remove the need for this.
+    bitmap_device.GetBitmap()->ForcePreMultiply();
+  }
+#endif  // _SKIA_SUPPORT
   m_bStopped = bitmap_render.m_bStopped;
   if (pSMaskDict) {
     CFX_Matrix smask_matrix =
         *pPageObj->m_GeneralState.GetSMaskMatrix() * mtObj2Device;
     RetainPtr<CFX_DIBBase> pSMaskSource =
-        LoadSMask(pSMaskDict, &rect, &smask_matrix);
+        LoadSMask(pSMaskDict.Get(), &rect, smask_matrix);
     if (pSMaskSource)
-      bitmap->MultiplyAlpha(pSMaskSource);
+      bitmap_device.MultiplyAlpha(pSMaskSource);
   }
   if (pTextMask) {
-    bitmap->MultiplyAlpha(pTextMask);
+    bitmap_device.MultiplyAlpha(pTextMask);
     pTextMask.Reset();
   }
-  int32_t blitAlpha = 255;
   if (group_alpha != 1.0f && transparency.IsGroup()) {
-    blitAlpha = (int32_t)(group_alpha * 255);
-#ifndef _SKIA_SUPPORT_
-    bitmap->MultiplyAlpha(blitAlpha);
-    blitAlpha = 255;
-#endif
+    bitmap_device.MultiplyAlpha(group_alpha);
   }
   transparency = m_Transparency;
   if (pPageObj->IsForm()) {
     transparency.SetGroup();
   }
-  CompositeDIBitmap(bitmap, rect.left, rect.top, 0, blitAlpha, blend_type,
-                    transparency);
-#if defined _SKIA_SUPPORT_
-  DebugVerifyDeviceIsPreMultiplied();
-#endif
+  CompositeDIBitmap(bitmap_device.GetBitmap(), rect.left, rect.top, 0, 255,
+                    blend_type, transparency);
   return true;
 }
 
+FX_RECT CPDF_RenderStatus::GetClippedBBox(const FX_RECT& rect) const {
+  FX_RECT bbox = rect;
+  bbox.Intersect(m_pDevice->GetClipBox());
+  return bbox;
+}
+
 RetainPtr<CFX_DIBitmap> CPDF_RenderStatus::GetBackdrop(
     const CPDF_PageObject* pObj,
-    const FX_RECT& rect,
-    bool bBackAlphaRequired,
-    int* left,
-    int* top) {
-  FX_RECT bbox = rect;
-  bbox.Intersect(m_pDevice->GetClipBox());
-  *left = bbox.left;
-  *top = bbox.top;
+    const FX_RECT& bbox,
+    bool bBackAlphaRequired) {
   int width = bbox.Width();
   int height = bbox.Height();
   auto pBackdrop = pdfium::MakeRetain<CFX_DIBitmap>();
   if (bBackAlphaRequired && !m_bDropObjects)
-    pBackdrop->Create(width, height, FXDIB_Argb);
+    pBackdrop->Create(width, height, FXDIB_Format::kArgb);
   else
     m_pDevice->CreateCompatibleBitmap(pBackdrop, width, height);
 
-  if (!pBackdrop->GetBuffer())
+  if (pBackdrop->GetBuffer().empty())
     return nullptr;
 
   bool bNeedDraw;
-  if (pBackdrop->HasAlpha())
+  if (pBackdrop->IsAlphaFormat())
     bNeedDraw = !(m_pDevice->GetRenderCaps() & FXRC_ALPHA_OUTPUT);
   else
     bNeedDraw = !(m_pDevice->GetRenderCaps() & FXRC_GET_BITS);
 
   if (!bNeedDraw) {
-    m_pDevice->GetDIBits(pBackdrop, *left, *top);
+    m_pDevice->GetDIBits(pBackdrop, bbox.left, bbox.top);
     return pBackdrop;
   }
   CFX_Matrix FinalMatrix = m_DeviceMatrix;
-  FinalMatrix.Translate(-*left, -*top);
-  pBackdrop->Clear(pBackdrop->HasAlpha() ? 0 : 0xffffffff);
+  FinalMatrix.Translate(-bbox.left, -bbox.top);
+  if (!pBackdrop->IsAlphaFormat()) {
+    pBackdrop->Clear(0xffffffff);
+  }
 
   CFX_DefaultRenderDevice device;
-  device.Attach(pBackdrop, false, nullptr, false);
+  device.Attach(pBackdrop);
   m_pContext->Render(&device, pObj, &m_Options, &FinalMatrix);
   return pBackdrop;
 }
 
 std::unique_ptr<CPDF_GraphicStates> CPDF_RenderStatus::CloneObjStates(
     const CPDF_GraphicStates* pSrcStates,
-    bool bStroke) {
+    bool stroke) {
   if (!pSrcStates)
     return nullptr;
 
-  auto pStates = pdfium::MakeUnique<CPDF_GraphicStates>();
+  auto pStates = std::make_unique<CPDF_GraphicStates>();
   pStates->CopyStates(*pSrcStates);
-  const CPDF_Color* pObjColor = bStroke
+  const CPDF_Color* pObjColor = stroke
                                     ? pSrcStates->m_ColorState.GetStrokeColor()
                                     : pSrcStates->m_ColorState.GetFillColor();
   if (!pObjColor->IsNull()) {
     pStates->m_ColorState.SetFillColorRef(
-        bStroke ? pSrcStates->m_ColorState.GetStrokeColorRef()
-                : pSrcStates->m_ColorState.GetFillColorRef());
+        stroke ? pSrcStates->m_ColorState.GetStrokeColorRef()
+               : pSrcStates->m_ColorState.GetFillColorRef());
     pStates->m_ColorState.SetStrokeColorRef(
         pStates->m_ColorState.GetFillColorRef());
   }
   return pStates;
 }
 
-#if defined _SKIA_SUPPORT_
-void CPDF_RenderStatus::DebugVerifyDeviceIsPreMultiplied() const {
-  m_pDevice->DebugVerifyBitmapIsPreMultiplied();
-}
-#endif
-
 bool CPDF_RenderStatus::ProcessText(CPDF_TextObject* textobj,
                                     const CFX_Matrix& mtObj2Device,
-                                    CFX_PathData* pClippingPath) {
+                                    CFX_Path* clipping_path) {
   if (textobj->GetCharCodes().empty())
     return true;
 
@@ -844,29 +766,29 @@
   if (pFont->IsType3Font())
     return ProcessType3Text(textobj, mtObj2Device);
 
-  bool bFill = false;
-  bool bStroke = false;
-  bool bClip = false;
-  if (pClippingPath) {
-    bClip = true;
+  bool is_fill = false;
+  bool is_stroke = false;
+  bool is_clip = false;
+  if (clipping_path) {
+    is_clip = true;
   } else {
     switch (text_render_mode) {
       case TextRenderingMode::MODE_FILL:
       case TextRenderingMode::MODE_FILL_CLIP:
-        bFill = true;
+        is_fill = true;
         break;
       case TextRenderingMode::MODE_STROKE:
       case TextRenderingMode::MODE_STROKE_CLIP:
         if (pFont->HasFace())
-          bStroke = true;
+          is_stroke = true;
         else
-          bFill = true;
+          is_fill = true;
         break;
       case TextRenderingMode::MODE_FILL_STROKE:
       case TextRenderingMode::MODE_FILL_STROKE_CLIP:
-        bFill = true;
+        is_fill = true;
         if (pFont->HasFace())
-          bStroke = true;
+          is_stroke = true;
         break;
       case TextRenderingMode::MODE_INVISIBLE:
         // Already handled above, but the compiler is not smart enough to
@@ -883,14 +805,14 @@
   FX_ARGB stroke_argb = 0;
   FX_ARGB fill_argb = 0;
   bool bPattern = false;
-  if (bStroke) {
+  if (is_stroke) {
     if (textobj->m_ColorState.GetStrokeColor()->IsPattern()) {
       bPattern = true;
     } else {
       stroke_argb = GetStrokeArgb(textobj);
     }
   }
-  if (bFill) {
+  if (is_fill) {
     if (textobj->m_ColorState.GetFillColor()->IsPattern()) {
       bPattern = true;
     } else {
@@ -904,14 +826,14 @@
   float font_size = textobj->m_TextState.GetFontSize();
   if (bPattern) {
     DrawTextPathWithPattern(textobj, mtObj2Device, pFont.Get(), font_size,
-                            &text_matrix, bFill, bStroke);
+                            text_matrix, is_fill, is_stroke);
     return true;
   }
-  if (bClip || bStroke) {
+  if (is_clip || is_stroke) {
     const CFX_Matrix* pDeviceMatrix = &mtObj2Device;
     CFX_Matrix device_matrix;
-    if (bStroke) {
-      const float* pCTM = textobj->m_TextState.GetCTM();
+    if (is_stroke) {
+      pdfium::span<const float> pCTM = textobj->m_TextState.GetCTM();
       if (pCTM[0] != 1.0f || pCTM[3] != 1.0f) {
         CFX_Matrix ctm(pCTM[0], pCTM[1], pCTM[2], pCTM[3], 0, 0);
         text_matrix *= ctm.GetInverse();
@@ -919,20 +841,13 @@
         pDeviceMatrix = &device_matrix;
       }
     }
-    int flag = 0;
-    if (bStroke && bFill) {
-      flag |= FX_FILL_STROKE;
-      flag |= FX_STROKE_TEXT_MODE;
-    }
-    if (textobj->m_GeneralState.GetStrokeAdjust())
-      flag |= FX_STROKE_ADJUST;
-    if (m_Options.GetOptions().bNoTextSmooth)
-      flag |= FXFILL_NOPATHSMOOTH;
     return CPDF_TextRenderer::DrawTextPath(
         m_pDevice, textobj->GetCharCodes(), textobj->GetCharPositions(),
         pFont.Get(), font_size, text_matrix, pDeviceMatrix,
         textobj->m_GraphState.GetObject(), fill_argb, stroke_argb,
-        pClippingPath, flag);
+        clipping_path,
+        GetFillOptionsForDrawTextPath(m_Options.GetOptions(), textobj,
+                                      is_stroke, is_fill));
   }
   text_matrix.Concat(mtObj2Device);
   return CPDF_TextRenderer::DrawNormalText(
@@ -944,13 +859,12 @@
 bool CPDF_RenderStatus::ProcessType3Text(CPDF_TextObject* textobj,
                                          const CFX_Matrix& mtObj2Device) {
   CPDF_Type3Font* pType3Font = textobj->m_TextState.GetFont()->AsType3Font();
-  if (pdfium::ContainsValue(m_Type3FontCache, pType3Font))
+  if (pdfium::Contains(m_Type3FontCache, pType3Font))
     return true;
 
-  DeviceType device_type = m_pDevice->GetDeviceType();
   FX_ARGB fill_argb = GetFillArgbForType3(textobj);
   int fill_alpha = FXARGB_A(fill_argb);
-  if (device_type != DeviceType::kDisplay && fill_alpha < 255)
+  if (m_bPrint && fill_alpha < 255)
     return false;
 
   CFX_Matrix text_matrix = textobj->GetTextMatrix();
@@ -961,7 +875,7 @@
   // Must come before |glyphs|, because |glyphs| points into |refTypeCache|.
   std::set<RetainPtr<CPDF_Type3Cache>> refTypeCache;
   std::vector<TextGlyphPos> glyphs;
-  if (device_type == DeviceType::kDisplay)
+  if (!m_bPrint)
     glyphs.resize(textobj->GetCharCodes().size());
 
   for (size_t iChar = 0; iChar < textobj->GetCharCodes().size(); ++iChar) {
@@ -984,7 +898,7 @@
           if (!glyph.m_pGlyph)
             continue;
 
-          Optional<CFX_Point> point = glyph.GetOrigin({0, 0});
+          absl::optional<CFX_Point> point = glyph.GetOrigin({0, 0});
           if (!point.has_value())
             continue;
 
@@ -1001,20 +915,20 @@
       options.GetOptions().bRectAA = true;
 
       const auto* pForm = static_cast<const CPDF_Form*>(pType3Char->form());
-      const CPDF_Dictionary* pFormResource =
+      RetainPtr<const CPDF_Dictionary> pFormResource =
           pForm->GetDict()->GetDictFor("Resources");
 
       if (fill_alpha == 255) {
-        CPDF_RenderStatus status(m_pContext.Get(), m_pDevice);
+        CPDF_RenderStatus status(m_pContext, m_pDevice);
         status.SetOptions(options);
         status.SetTransparency(pForm->GetTransparency());
         status.SetType3Char(pType3Char);
         status.SetFillColor(fill_argb);
         status.SetDropObjects(m_bDropObjects);
-        status.SetFormResource(pFormResource);
+        status.SetFormResource(std::move(pFormResource));
         status.Initialize(this, pStates.get());
         status.m_Type3FontCache = m_Type3FontCache;
-        status.m_Type3FontCache.push_back(pType3Font);
+        status.m_Type3FontCache.emplace_back(pType3Font);
 
         CFX_RenderDevice::StateRestorer restorer(m_pDevice);
         status.RenderObjectList(pForm, matrix);
@@ -1025,32 +939,40 @@
           continue;
 
         CFX_DefaultRenderDevice bitmap_device;
-        if (!bitmap_device.Create(rect.Width(), rect.Height(), FXDIB_Argb,
-                                  nullptr)) {
+        if (!bitmap_device.Create(rect.Width(), rect.Height(),
+                                  FXDIB_Format::kArgb, nullptr)) {
           return true;
         }
-        bitmap_device.GetBitmap()->Clear(0);
-        CPDF_RenderStatus status(m_pContext.Get(), &bitmap_device);
+        CPDF_RenderStatus status(m_pContext, &bitmap_device);
         status.SetOptions(options);
         status.SetTransparency(pForm->GetTransparency());
         status.SetType3Char(pType3Char);
         status.SetFillColor(fill_argb);
         status.SetDropObjects(m_bDropObjects);
-        status.SetFormResource(pFormResource);
+        status.SetFormResource(std::move(pFormResource));
         status.Initialize(this, pStates.get());
         status.m_Type3FontCache = m_Type3FontCache;
-        status.m_Type3FontCache.push_back(pType3Font);
+        status.m_Type3FontCache.emplace_back(pType3Font);
         matrix.Translate(-rect.left, -rect.top);
         status.RenderObjectList(pForm, matrix);
         m_pDevice->SetDIBits(bitmap_device.GetBitmap(), rect.left, rect.top);
       }
     } else if (pType3Char->GetBitmap()) {
-      if (device_type == DeviceType::kDisplay) {
+      if (m_bPrint) {
+        CFX_Matrix image_matrix = pType3Char->matrix() * matrix;
+        CPDF_ImageRenderer renderer(this);
+        if (renderer.Start(pType3Char->GetBitmap(), fill_argb, image_matrix,
+                           FXDIB_ResampleOptions(), false)) {
+          renderer.Continue(nullptr);
+        }
+        if (!renderer.GetResult())
+          return false;
+      } else {
         CPDF_Document* pDoc = pType3Font->GetDocument();
         RetainPtr<CPDF_Type3Cache> pCache =
             CPDF_DocRenderData::FromDocument(pDoc)->GetCachedType3(pType3Font);
 
-        const CFX_GlyphBitmap* pBitmap = pCache->LoadGlyph(charcode, &matrix);
+        const CFX_GlyphBitmap* pBitmap = pCache->LoadGlyph(charcode, matrix);
         if (!pBitmap)
           continue;
 
@@ -1074,16 +996,6 @@
           glyphs[iChar].m_pGlyph = pBitmap;
           glyphs[iChar].m_Origin = origin;
         }
-      } else {
-        CFX_Matrix image_matrix = pType3Char->matrix() * matrix;
-        CPDF_ImageRenderer renderer;
-        if (renderer.Start(this, pType3Char->GetBitmap(), fill_argb, 255,
-                           image_matrix, FXDIB_ResampleOptions(), false,
-                           BlendMode::kNormal)) {
-          renderer.Continue(nullptr);
-        }
-        if (!renderer.GetResult())
-          return false;
       }
     }
   }
@@ -1093,15 +1005,14 @@
 
   FX_RECT rect = GetGlyphsBBox(glyphs, 0);
   auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pBitmap->Create(rect.Width(), rect.Height(), FXDIB_8bppMask))
+  if (!pBitmap->Create(rect.Width(), rect.Height(), FXDIB_Format::k8bppMask))
     return true;
 
-  pBitmap->Clear(0);
   for (const TextGlyphPos& glyph : glyphs) {
-    if (!glyph.m_pGlyph)
+    if (!glyph.m_pGlyph || !glyph.m_pGlyph->GetBitmap()->IsMaskFormat())
       continue;
 
-    Optional<CFX_Point> point = glyph.GetOrigin({rect.left, rect.top});
+    absl::optional<CFX_Point> point = glyph.GetOrigin({rect.left, rect.top});
     if (!point.has_value())
       continue;
 
@@ -1118,15 +1029,15 @@
                                                 const CFX_Matrix& mtObj2Device,
                                                 CPDF_Font* pFont,
                                                 float font_size,
-                                                const CFX_Matrix* pTextMatrix,
-                                                bool bFill,
-                                                bool bStroke) {
-  if (!bStroke) {
+                                                const CFX_Matrix& mtTextMatrix,
+                                                bool fill,
+                                                bool stroke) {
+  if (!stroke) {
     std::vector<std::unique_ptr<CPDF_TextObject>> pCopy;
-    pCopy.push_back(std::unique_ptr<CPDF_TextObject>(textobj->Clone()));
+    pCopy.push_back(textobj->Clone());
 
     CPDF_PathObject path;
-    path.set_filltype(FXFILL_WINDING);
+    path.set_filltype(CFX_FillRenderOptions::FillType::kWinding);
     path.m_ClipPath.CopyClipPath(m_LastClipPath);
     path.m_ClipPath.AppendTexts(&pCopy);
     path.m_ColorState = textobj->m_ColorState;
@@ -1138,13 +1049,14 @@
     RenderSingleObject(&path, mtObj2Device);
     return;
   }
-  const CPDF_CharPosList CharPosList(
+
+  std::vector<TextCharPos> char_pos_list = GetCharPosList(
       textobj->GetCharCodes(), textobj->GetCharPositions(), pFont, font_size);
-  for (const TextCharPos& charpos : CharPosList.Get()) {
+  for (const TextCharPos& charpos : char_pos_list) {
     auto* font = charpos.m_FallbackFontPosition == -1
                      ? pFont->GetFont()
                      : pFont->GetFontFallback(charpos.m_FallbackFontPosition);
-    const CFX_PathData* pPath =
+    const CFX_Path* pPath =
         font->LoadGlyphPath(charpos.m_GlyphIndex, charpos.m_FontCharWidth);
     if (!pPath)
       continue;
@@ -1153,19 +1065,14 @@
     path.m_GraphState = textobj->m_GraphState;
     path.m_ColorState = textobj->m_ColorState;
 
-    CFX_Matrix matrix;
-    if (charpos.m_bGlyphAdjust) {
-      matrix = CFX_Matrix(charpos.m_AdjustMatrix[0], charpos.m_AdjustMatrix[1],
-                          charpos.m_AdjustMatrix[2], charpos.m_AdjustMatrix[3],
-                          0, 0);
-    }
-    matrix.Concat(CFX_Matrix(font_size, 0, 0, font_size, charpos.m_Origin.x,
-                             charpos.m_Origin.y));
-    path.set_stroke(bStroke);
-    path.set_filltype(bFill ? FXFILL_WINDING : 0);
-    path.path().Append(pPath, &matrix);
-    path.set_matrix(*pTextMatrix);
-    path.CalcBoundingBox();
+    CFX_Matrix matrix = charpos.GetEffectiveMatrix(CFX_Matrix(
+        font_size, 0, 0, font_size, charpos.m_Origin.x, charpos.m_Origin.y));
+    matrix.Concat(mtTextMatrix);
+    path.set_stroke(stroke);
+    path.set_filltype(fill ? CFX_FillRenderOptions::FillType::kWinding
+                           : CFX_FillRenderOptions::FillType::kNoFill);
+    path.path().Append(*pPath, &matrix);
+    path.SetPathMatrix(CFX_Matrix());
     ProcessPath(&path, mtObj2Device);
   }
 }
@@ -1173,12 +1080,12 @@
 void CPDF_RenderStatus::DrawShadingPattern(CPDF_ShadingPattern* pattern,
                                            const CPDF_PageObject* pPageObj,
                                            const CFX_Matrix& mtObj2Device,
-                                           bool bStroke) {
+                                           bool stroke) {
   if (!pattern->Load())
     return;
 
   CFX_RenderDevice::StateRestorer restorer(m_pDevice);
-  if (!ClipPattern(pPageObj, mtObj2Device, bStroke))
+  if (!ClipPattern(pPageObj, mtObj2Device, stroke))
     return;
 
   FX_RECT rect = GetObjectClippedRect(pPageObj, mtObj2Device);
@@ -1187,263 +1094,93 @@
 
   CFX_Matrix matrix = pattern->pattern_to_form() * mtObj2Device;
   int alpha =
-      FXSYS_roundf(255 * (bStroke ? pPageObj->m_GeneralState.GetStrokeAlpha()
-                                  : pPageObj->m_GeneralState.GetFillAlpha()));
-  CPDF_RenderShading::Draw(m_pDevice, m_pContext.Get(), m_pCurObj.Get(),
-                           pattern, matrix, rect, alpha, m_Options);
+      FXSYS_roundf(255 * (stroke ? pPageObj->m_GeneralState.GetStrokeAlpha()
+                                 : pPageObj->m_GeneralState.GetFillAlpha()));
+  CPDF_RenderShading::Draw(m_pDevice, m_pContext, m_pCurObj, pattern, matrix,
+                           rect, alpha, m_Options);
 }
 
 void CPDF_RenderStatus::ProcessShading(const CPDF_ShadingObject* pShadingObj,
                                        const CFX_Matrix& mtObj2Device) {
-  FX_RECT rect = pShadingObj->GetTransformedBBox(mtObj2Device);
-  FX_RECT clip_box = m_pDevice->GetClipBox();
-  rect.Intersect(clip_box);
+  FX_RECT rect = GetObjectClippedRect(pShadingObj, mtObj2Device);
   if (rect.IsEmpty())
     return;
 
   CFX_Matrix matrix = pShadingObj->matrix() * mtObj2Device;
   CPDF_RenderShading::Draw(
-      m_pDevice, m_pContext.Get(), m_pCurObj.Get(), pShadingObj->pattern(),
-      matrix, rect,
+      m_pDevice, m_pContext, m_pCurObj, pShadingObj->pattern(), matrix, rect,
       FXSYS_roundf(255 * pShadingObj->m_GeneralState.GetFillAlpha()),
       m_Options);
 }
 
-void CPDF_RenderStatus::DrawTilingPattern(CPDF_TilingPattern* pPattern,
+void CPDF_RenderStatus::DrawTilingPattern(CPDF_TilingPattern* pattern,
                                           CPDF_PageObject* pPageObj,
                                           const CFX_Matrix& mtObj2Device,
-                                          bool bStroke) {
-  const std::unique_ptr<CPDF_Form> pPatternForm = pPattern->Load(pPageObj);
+                                          bool stroke) {
+  const std::unique_ptr<CPDF_Form> pPatternForm = pattern->Load(pPageObj);
   if (!pPatternForm)
     return;
 
   CFX_RenderDevice::StateRestorer restorer(m_pDevice);
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-  ScopedSkiaDeviceFlush scoped_skia_device_flush(m_pDevice);
-#endif
-  if (!ClipPattern(pPageObj, mtObj2Device, bStroke))
+  if (!ClipPattern(pPageObj, mtObj2Device, stroke))
     return;
 
   FX_RECT clip_box = m_pDevice->GetClipBox();
   if (clip_box.IsEmpty())
     return;
 
-  const CFX_Matrix mtPattern2Device =
-      pPattern->pattern_to_form() * mtObj2Device;
-
-  CFX_FloatRect cell_bbox = mtPattern2Device.TransformRect(pPattern->bbox());
-
-  float ceil_height = std::ceil(cell_bbox.Height());
-  float ceil_width = std::ceil(cell_bbox.Width());
-
-  // Validate the float will fit into the int when the conversion is done.
-  if (!pdfium::base::IsValueInRangeForNumericType<int>(ceil_height) ||
-      !pdfium::base::IsValueInRangeForNumericType<int>(ceil_width)) {
-    return;
-  }
-
-  int width = static_cast<int>(ceil_width);
-  int height = static_cast<int>(ceil_height);
-  if (width <= 0)
-    width = 1;
-  if (height <= 0)
-    height = 1;
-
-  CFX_FloatRect clip_box_p =
-      mtPattern2Device.GetInverse().TransformRect(CFX_FloatRect(clip_box));
-  int min_col = static_cast<int>(
-      ceil((clip_box_p.left - pPattern->bbox().right) / pPattern->x_step()));
-  int max_col = static_cast<int>(
-      floor((clip_box_p.right - pPattern->bbox().left) / pPattern->x_step()));
-  int min_row = static_cast<int>(
-      ceil((clip_box_p.bottom - pPattern->bbox().top) / pPattern->y_step()));
-  int max_row = static_cast<int>(
-      floor((clip_box_p.top - pPattern->bbox().bottom) / pPattern->y_step()));
-
-  // Make sure we can fit the needed width * height into an int.
-  if (height > std::numeric_limits<int>::max() / width)
+  RetainPtr<CFX_DIBitmap> pScreen =
+      CPDF_RenderTiling::Draw(this, pPageObj, pattern, pPatternForm.get(),
+                              mtObj2Device, clip_box, stroke);
+  if (!pScreen)
     return;
 
-  if (width > clip_box.Width() || height > clip_box.Height() ||
-      width * height > clip_box.Width() * clip_box.Height()) {
-    std::unique_ptr<CPDF_GraphicStates> pStates;
-    if (!pPattern->colored())
-      pStates = CloneObjStates(pPageObj, bStroke);
-
-    const CPDF_Dictionary* pFormDict = pPatternForm->GetDict();
-    const CPDF_Dictionary* pFormResource =
-        pFormDict ? pFormDict->GetDictFor("Resources") : nullptr;
-    for (int col = min_col; col <= max_col; col++) {
-      for (int row = min_row; row <= max_row; row++) {
-        CFX_PointF original = mtPattern2Device.Transform(
-            CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step()));
-        CFX_Matrix matrix = mtObj2Device;
-        matrix.Translate(original.x - mtPattern2Device.e,
-                         original.y - mtPattern2Device.f);
-        CFX_RenderDevice::StateRestorer restorer2(m_pDevice);
-        CPDF_RenderStatus status(m_pContext.Get(), m_pDevice);
-        status.SetOptions(m_Options);
-        status.SetTransparency(pPatternForm->GetTransparency());
-        status.SetFormResource(pFormResource);
-        status.SetDropObjects(m_bDropObjects);
-        status.Initialize(this, pStates.get());
-        status.RenderObjectList(pPatternForm.get(), matrix);
-      }
-    }
-    return;
-  }
-
-  bool bAligned =
-      pPattern->bbox().left == 0 && pPattern->bbox().bottom == 0 &&
-      pPattern->bbox().right == pPattern->x_step() &&
-      pPattern->bbox().top == pPattern->y_step() &&
-      (mtPattern2Device.IsScaled() || mtPattern2Device.Is90Rotated());
-  if (bAligned) {
-    int orig_x = FXSYS_roundf(mtPattern2Device.e);
-    int orig_y = FXSYS_roundf(mtPattern2Device.f);
-    min_col = (clip_box.left - orig_x) / width;
-    if (clip_box.left < orig_x)
-      min_col--;
-
-    max_col = (clip_box.right - orig_x) / width;
-    if (clip_box.right <= orig_x)
-      max_col--;
-
-    min_row = (clip_box.top - orig_y) / height;
-    if (clip_box.top < orig_y)
-      min_row--;
-
-    max_row = (clip_box.bottom - orig_y) / height;
-    if (clip_box.bottom <= orig_y)
-      max_row--;
-  }
-  float left_offset = cell_bbox.left - mtPattern2Device.e;
-  float top_offset = cell_bbox.bottom - mtPattern2Device.f;
-  RetainPtr<CFX_DIBitmap> pPatternBitmap;
-  if (width * height < 16) {
-    RetainPtr<CFX_DIBitmap> pEnlargedBitmap = DrawPatternBitmap(
-        m_pContext->GetDocument(), m_pContext->GetPageCache(), pPattern,
-        pPatternForm.get(), mtObj2Device, 8, 8, m_Options.GetOptions());
-    pPatternBitmap = pEnlargedBitmap->StretchTo(
-        width, height, FXDIB_ResampleOptions(), nullptr);
-  } else {
-    pPatternBitmap =
-        DrawPatternBitmap(m_pContext->GetDocument(), m_pContext->GetPageCache(),
-                          pPattern, pPatternForm.get(), mtObj2Device, width,
-                          height, m_Options.GetOptions());
-  }
-  if (!pPatternBitmap)
-    return;
-
-  if (m_Options.ColorModeIs(CPDF_RenderOptions::kGray))
-    pPatternBitmap->ConvertColorScale(0, 0xffffff);
-
-  FX_ARGB fill_argb = GetFillArgb(pPageObj);
-  int clip_width = clip_box.right - clip_box.left;
-  int clip_height = clip_box.bottom - clip_box.top;
-  auto pScreen = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pScreen->Create(clip_width, clip_height, FXDIB_Argb))
-    return;
-
-  pScreen->Clear(0);
-  const uint8_t* const src_buf = pPatternBitmap->GetBuffer();
-  for (int col = min_col; col <= max_col; col++) {
-    for (int row = min_row; row <= max_row; row++) {
-      int start_x;
-      int start_y;
-      if (bAligned) {
-        start_x =
-            FXSYS_roundf(mtPattern2Device.e) + col * width - clip_box.left;
-        start_y =
-            FXSYS_roundf(mtPattern2Device.f) + row * height - clip_box.top;
-      } else {
-        CFX_PointF original = mtPattern2Device.Transform(
-            CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step()));
-
-        pdfium::base::CheckedNumeric<int> safeStartX =
-            FXSYS_roundf(original.x + left_offset);
-        pdfium::base::CheckedNumeric<int> safeStartY =
-            FXSYS_roundf(original.y + top_offset);
-
-        safeStartX -= clip_box.left;
-        safeStartY -= clip_box.top;
-        if (!safeStartX.IsValid() || !safeStartY.IsValid())
-          return;
-
-        start_x = safeStartX.ValueOrDie();
-        start_y = safeStartY.ValueOrDie();
-      }
-      if (width == 1 && height == 1) {
-        if (start_x < 0 || start_x >= clip_box.Width() || start_y < 0 ||
-            start_y >= clip_box.Height()) {
-          continue;
-        }
-        uint32_t* dest_buf = reinterpret_cast<uint32_t*>(
-            pScreen->GetBuffer() + pScreen->GetPitch() * start_y + start_x * 4);
-        if (pPattern->colored()) {
-          const auto* src_buf32 = reinterpret_cast<const uint32_t*>(src_buf);
-          *dest_buf = *src_buf32;
-        } else {
-          *dest_buf = (*src_buf << 24) | (fill_argb & 0xffffff);
-        }
-      } else {
-        if (pPattern->colored()) {
-          pScreen->CompositeBitmap(start_x, start_y, width, height,
-                                   pPatternBitmap, 0, 0, BlendMode::kNormal,
-                                   nullptr, false);
-        } else {
-          pScreen->CompositeMask(start_x, start_y, width, height,
-                                 pPatternBitmap, fill_argb, 0, 0,
-                                 BlendMode::kNormal, nullptr, false);
-        }
-      }
-    }
-  }
   CompositeDIBitmap(pScreen, clip_box.left, clip_box.top, 0, 255,
                     BlendMode::kNormal, CPDF_Transparency());
 }
 
-void CPDF_RenderStatus::DrawPathWithPattern(CPDF_PathObject* pPathObj,
+void CPDF_RenderStatus::DrawPathWithPattern(CPDF_PathObject* path_obj,
                                             const CFX_Matrix& mtObj2Device,
                                             const CPDF_Color* pColor,
-                                            bool bStroke) {
-  CPDF_Pattern* pattern = pColor->GetPattern();
+                                            bool stroke) {
+  RetainPtr<CPDF_Pattern> pattern = pColor->GetPattern();
   if (!pattern)
     return;
 
   if (CPDF_TilingPattern* pTilingPattern = pattern->AsTilingPattern())
-    DrawTilingPattern(pTilingPattern, pPathObj, mtObj2Device, bStroke);
+    DrawTilingPattern(pTilingPattern, path_obj, mtObj2Device, stroke);
   else if (CPDF_ShadingPattern* pShadingPattern = pattern->AsShadingPattern())
-    DrawShadingPattern(pShadingPattern, pPathObj, mtObj2Device, bStroke);
+    DrawShadingPattern(pShadingPattern, path_obj, mtObj2Device, stroke);
 }
 
-void CPDF_RenderStatus::ProcessPathPattern(CPDF_PathObject* pPathObj,
-                                           const CFX_Matrix& mtObj2Device,
-                                           int* filltype,
-                                           bool* bStroke) {
-  ASSERT(filltype);
-  ASSERT(bStroke);
+void CPDF_RenderStatus::ProcessPathPattern(
+    CPDF_PathObject* path_obj,
+    const CFX_Matrix& mtObj2Device,
+    CFX_FillRenderOptions::FillType* fill_type,
+    bool* stroke) {
+  DCHECK(fill_type);
+  DCHECK(stroke);
 
-  if (*filltype) {
-    const CPDF_Color& FillColor = *pPathObj->m_ColorState.GetFillColor();
+  if (*fill_type != CFX_FillRenderOptions::FillType::kNoFill) {
+    const CPDF_Color& FillColor = *path_obj->m_ColorState.GetFillColor();
     if (FillColor.IsPattern()) {
-      DrawPathWithPattern(pPathObj, mtObj2Device, &FillColor, false);
-      *filltype = 0;
+      DrawPathWithPattern(path_obj, mtObj2Device, &FillColor, false);
+      *fill_type = CFX_FillRenderOptions::FillType::kNoFill;
     }
   }
-  if (*bStroke) {
-    const CPDF_Color& StrokeColor = *pPathObj->m_ColorState.GetStrokeColor();
+  if (*stroke) {
+    const CPDF_Color& StrokeColor = *path_obj->m_ColorState.GetStrokeColor();
     if (StrokeColor.IsPattern()) {
-      DrawPathWithPattern(pPathObj, mtObj2Device, &StrokeColor, true);
-      *bStroke = false;
+      DrawPathWithPattern(path_obj, mtObj2Device, &StrokeColor, true);
+      *stroke = false;
     }
   }
 }
 
 bool CPDF_RenderStatus::ProcessImage(CPDF_ImageObject* pImageObj,
                                      const CFX_Matrix& mtObj2Device) {
-  CPDF_ImageRenderer render;
-  if (render.Start(this, pImageObj, mtObj2Device, m_bStdCS, m_curBlend))
+  CPDF_ImageRenderer render(this);
+  if (render.Start(pImageObj, mtObj2Device, m_bStdCS, m_curBlend))
     render.Continue(nullptr);
   return render.GetResult();
 }
@@ -1460,22 +1197,18 @@
     return;
 
   if (blend_mode == BlendMode::kNormal) {
-    if (!pDIBitmap->IsAlphaMask()) {
+    if (!pDIBitmap->IsMaskFormat()) {
       if (bitmap_alpha < 255) {
-#ifdef _SKIA_SUPPORT_
-        std::unique_ptr<CFX_ImageRenderer> dummy;
-        CFX_Matrix m = CFX_RenderDevice::GetFlipMatrix(
-            pDIBitmap->GetWidth(), pDIBitmap->GetHeight(), left, top);
-        m_pDevice->StartDIBits(pDIBitmap, bitmap_alpha, 0, m,
-                               FXDIB_ResampleOptions(), &dummy);
-        return;
-#else
+        if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+          std::unique_ptr<CFX_ImageRenderer> dummy;
+          CFX_Matrix m = CFX_RenderDevice::GetFlipMatrix(
+              pDIBitmap->GetWidth(), pDIBitmap->GetHeight(), left, top);
+          m_pDevice->StartDIBits(pDIBitmap, bitmap_alpha, 0, m,
+                                 FXDIB_ResampleOptions(), &dummy);
+          return;
+        }
         pDIBitmap->MultiplyAlpha(bitmap_alpha);
-#endif
       }
-#ifdef _SKIA_SUPPORT_
-      CFX_SkiaDeviceDriver::PreMultiply(pDIBitmap);
-#endif
       if (m_pDevice->SetDIBits(pDIBitmap, left, top)) {
         return;
       }
@@ -1499,7 +1232,7 @@
        (m_pDevice->GetRenderCaps() & FXRC_GET_BITS) && !bBackAlphaRequired);
   if (bGetBackGround) {
     if (bIsolated || !transparency.IsGroup()) {
-      if (!pDIBitmap->IsAlphaMask())
+      if (!pDIBitmap->IsMaskFormat())
         m_pDevice->SetDIBitsWithBlend(pDIBitmap, left, top, blend_mode);
       return;
     }
@@ -1509,7 +1242,7 @@
     rect.Intersect(m_pDevice->GetClipBox());
     RetainPtr<CFX_DIBitmap> pClone;
     if (m_pDevice->GetBackDrop() && m_pDevice->GetBitmap()) {
-      pClone = m_pDevice->GetBackDrop()->Clone(&rect);
+      pClone = m_pDevice->GetBackDrop()->ClipTo(rect);
       if (!pClone)
         return;
 
@@ -1519,7 +1252,7 @@
                               BlendMode::kNormal, nullptr, false);
       left = std::min(left, 0);
       top = std::min(top, 0);
-      if (pDIBitmap->IsAlphaMask()) {
+      if (pDIBitmap->IsMaskFormat()) {
         pClone->CompositeMask(0, 0, pClone->GetWidth(), pClone->GetHeight(),
                               pDIBitmap, mask_argb, left, top, blend_mode,
                               nullptr, false);
@@ -1534,124 +1267,116 @@
     if (m_pDevice->GetBackDrop()) {
       m_pDevice->SetDIBits(pClone, rect.left, rect.top);
     } else {
-      if (!pDIBitmap->IsAlphaMask()) {
+      if (!pDIBitmap->IsMaskFormat()) {
         m_pDevice->SetDIBitsWithBlend(pDIBitmap, rect.left, rect.top,
                                       blend_mode);
       }
     }
     return;
   }
-  int back_left;
-  int back_top;
-  FX_RECT rect(left, top, left + pDIBitmap->GetWidth(),
-               top + pDIBitmap->GetHeight());
+  FX_RECT bbox = GetClippedBBox(FX_RECT(left, top, left + pDIBitmap->GetWidth(),
+                                        top + pDIBitmap->GetHeight()));
   RetainPtr<CFX_DIBitmap> pBackdrop = GetBackdrop(
-      m_pCurObj.Get(), rect, blend_mode != BlendMode::kNormal && bIsolated,
-      &back_left, &back_top);
+      m_pCurObj, bbox, blend_mode != BlendMode::kNormal && bIsolated);
   if (!pBackdrop)
     return;
 
-  if (pDIBitmap->IsAlphaMask()) {
-    pBackdrop->CompositeMask(left - back_left, top - back_top,
+  if (pDIBitmap->IsMaskFormat()) {
+    pBackdrop->CompositeMask(left - bbox.left, top - bbox.top,
                              pDIBitmap->GetWidth(), pDIBitmap->GetHeight(),
                              pDIBitmap, mask_argb, 0, 0, blend_mode, nullptr,
                              false);
   } else {
-    pBackdrop->CompositeBitmap(left - back_left, top - back_top,
+    pBackdrop->CompositeBitmap(left - bbox.left, top - bbox.top,
                                pDIBitmap->GetWidth(), pDIBitmap->GetHeight(),
                                pDIBitmap, 0, 0, blend_mode, nullptr, false);
   }
 
   auto pBackdrop1 = pdfium::MakeRetain<CFX_DIBitmap>();
   pBackdrop1->Create(pBackdrop->GetWidth(), pBackdrop->GetHeight(),
-                     FXDIB_Rgb32);
+                     FXDIB_Format::kRgb32);
   pBackdrop1->Clear((uint32_t)-1);
   pBackdrop1->CompositeBitmap(0, 0, pBackdrop->GetWidth(),
                               pBackdrop->GetHeight(), pBackdrop, 0, 0,
                               BlendMode::kNormal, nullptr, false);
   pBackdrop = std::move(pBackdrop1);
-  m_pDevice->SetDIBits(pBackdrop, back_left, back_top);
+  m_pDevice->SetDIBits(pBackdrop, bbox.left, bbox.top);
 }
 
 RetainPtr<CFX_DIBitmap> CPDF_RenderStatus::LoadSMask(
     CPDF_Dictionary* pSMaskDict,
     FX_RECT* pClipRect,
-    const CFX_Matrix* pMatrix) {
+    const CFX_Matrix& mtMatrix) {
   if (!pSMaskDict)
     return nullptr;
 
-  CPDF_Stream* pGroup = pSMaskDict->GetStreamFor(pdfium::transparency::kG);
+  RetainPtr<CPDF_Stream> pGroup =
+      pSMaskDict->GetMutableStreamFor(pdfium::transparency::kG);
   if (!pGroup)
     return nullptr;
 
   std::unique_ptr<CPDF_Function> pFunc;
-  const CPDF_Object* pFuncObj =
+  RetainPtr<const CPDF_Object> pFuncObj =
       pSMaskDict->GetDirectObjectFor(pdfium::transparency::kTR);
   if (pFuncObj && (pFuncObj->IsDictionary() || pFuncObj->IsStream()))
-    pFunc = CPDF_Function::Load(pFuncObj);
+    pFunc = CPDF_Function::Load(std::move(pFuncObj));
 
-  CFX_Matrix matrix = *pMatrix;
+  CFX_Matrix matrix = mtMatrix;
   matrix.Translate(-pClipRect->left, -pClipRect->top);
 
-  CPDF_Form form(m_pContext->GetDocument(), m_pContext->GetPageResources(),
-                 pGroup);
+  CPDF_Form form(m_pContext->GetDocument(),
+                 m_pContext->GetMutablePageResources(), pGroup);
   form.ParseContent();
 
   CFX_DefaultRenderDevice bitmap_device;
   bool bLuminosity =
-      pSMaskDict->GetStringFor(pdfium::transparency::kSoftMaskSubType) !=
+      pSMaskDict->GetByteStringFor(pdfium::transparency::kSoftMaskSubType) !=
       pdfium::transparency::kAlpha;
   int width = pClipRect->right - pClipRect->left;
   int height = pClipRect->bottom - pClipRect->top;
-  FXDIB_Format format;
-#if defined(OS_MACOSX) || defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
-  format = bLuminosity ? FXDIB_Rgb32 : FXDIB_8bppMask;
-#else
-  format = bLuminosity ? FXDIB_Rgb : FXDIB_8bppMask;
-#endif
+  FXDIB_Format format = GetFormatForLuminosity(bLuminosity);
   if (!bitmap_device.Create(width, height, format, nullptr))
     return nullptr;
 
   RetainPtr<CFX_DIBitmap> bitmap = bitmap_device.GetBitmap();
-  int nCSFamily = 0;
+  CPDF_ColorSpace::Family nCSFamily = CPDF_ColorSpace::Family::kUnknown;
   if (bLuminosity) {
     FX_ARGB back_color =
-        GetBackColor(pSMaskDict, pGroup->GetDict(), &nCSFamily);
+        GetBackColor(pSMaskDict, pGroup->GetDict().Get(), &nCSFamily);
     bitmap->Clear(back_color);
   } else {
     bitmap->Clear(0);
   }
 
-  const CPDF_Dictionary* pFormResource =
+  RetainPtr<const CPDF_Dictionary> pFormResource =
       form.GetDict()->GetDictFor("Resources");
   CPDF_RenderOptions options;
   options.SetColorMode(bLuminosity ? CPDF_RenderOptions::kNormal
                                    : CPDF_RenderOptions::kAlpha);
-  CPDF_RenderStatus status(m_pContext.Get(), &bitmap_device);
+  CPDF_RenderStatus status(m_pContext, &bitmap_device);
   status.SetOptions(options);
   status.SetGroupFamily(nCSFamily);
   status.SetLoadMask(bLuminosity);
   status.SetStdCS(true);
-  status.SetFormResource(pFormResource);
+  status.SetFormResource(std::move(pFormResource));
   status.SetDropObjects(m_bDropObjects);
   status.Initialize(nullptr, nullptr);
   status.RenderObjectList(&form, matrix);
 
   auto pMask = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pMask->Create(width, height, FXDIB_8bppMask))
+  if (!pMask->Create(width, height, FXDIB_Format::k8bppMask))
     return nullptr;
 
-  uint8_t* dest_buf = pMask->GetBuffer();
+  pdfium::span<uint8_t> dest_buf = pMask->GetWritableBuffer();
+  pdfium::span<const uint8_t> src_buf = bitmap->GetBuffer();
   int dest_pitch = pMask->GetPitch();
-  uint8_t* src_buf = bitmap->GetBuffer();
   int src_pitch = bitmap->GetPitch();
-  std::vector<uint8_t> transfers(256);
+  DataVector<uint8_t> transfers(256);
   if (pFunc) {
     std::vector<float> results(pFunc->CountOutputs());
     for (size_t i = 0; i < transfers.size(); ++i) {
       float input = i / 255.0f;
-      int nresult;
-      pFunc->Call(&input, 1, results.data(), &nresult);
+      pFunc->Call(pdfium::make_span(&input, 1), results);
       transfers[i] = FXSYS_roundf(results[0] * 255);
     }
   } else {
@@ -1659,10 +1384,12 @@
     std::iota(transfers.begin(), transfers.end(), 0);
   }
   if (bLuminosity) {
-    int Bpp = bitmap->GetBPP() / 8;
+    const int Bpp = bitmap->GetBPP() / 8;
     for (int row = 0; row < height; row++) {
-      uint8_t* dest_pos = dest_buf + row * dest_pitch;
-      uint8_t* src_pos = src_buf + row * src_pitch;
+      const size_t dest_offset = Fx2DSizeOrDie(row, dest_pitch);
+      const size_t src_offset = Fx2DSizeOrDie(row, src_pitch);
+      uint8_t* dest_pos = dest_buf.subspan(dest_offset).data();
+      const uint8_t* src_pos = src_buf.subspan(src_offset).data();
       for (int col = 0; col < width; col++) {
         *dest_pos++ = transfers[FXRGB2GRAY(src_pos[2], src_pos[1], *src_pos)];
         src_pos += Bpp;
@@ -1674,33 +1401,34 @@
       dest_buf[i] = transfers[src_buf[i]];
     }
   } else {
-    memcpy(dest_buf, src_buf, dest_pitch * height);
+    fxcrt::spancpy(dest_buf, src_buf.first(dest_pitch * height));
   }
   return pMask;
 }
 
 FX_ARGB CPDF_RenderStatus::GetBackColor(const CPDF_Dictionary* pSMaskDict,
                                         const CPDF_Dictionary* pGroupDict,
-                                        int* pCSFamily) {
+                                        CPDF_ColorSpace::Family* pCSFamily) {
   static constexpr FX_ARGB kDefaultColor = ArgbEncode(255, 0, 0, 0);
-  const CPDF_Array* pBC = pSMaskDict->GetArrayFor(pdfium::transparency::kBC);
+  RetainPtr<const CPDF_Array> pBC =
+      pSMaskDict->GetArrayFor(pdfium::transparency::kBC);
   if (!pBC)
     return kDefaultColor;
 
-  const CPDF_Object* pCSObj = nullptr;
-  const CPDF_Dictionary* pGroup =
+  RetainPtr<const CPDF_Object> pCSObj;
+  RetainPtr<const CPDF_Dictionary> pGroup =
       pGroupDict ? pGroupDict->GetDictFor("Group") : nullptr;
   if (pGroup)
     pCSObj = pGroup->GetDirectObjectFor(pdfium::transparency::kCS);
   RetainPtr<CPDF_ColorSpace> pCS =
       CPDF_DocPageData::FromDocument(m_pContext->GetDocument())
-          ->GetColorSpace(pCSObj, nullptr);
+          ->GetColorSpace(pCSObj.Get(), nullptr);
   if (!pCS)
     return kDefaultColor;
 
-  int family = pCS->GetFamily();
-  if (family == PDFCS_LAB || pCS->IsSpecial() ||
-      (family == PDFCS_ICCBASED && !pCS->IsNormal())) {
+  CPDF_ColorSpace::Family family = pCS->GetFamily();
+  if (family == CPDF_ColorSpace::Family::kLab || pCS->IsSpecial() ||
+      (family == CPDF_ColorSpace::Family::kICCBased && !pCS->IsNormal())) {
     return kDefaultColor;
   }
 
@@ -1709,13 +1437,13 @@
 
   uint32_t comps = std::max(8u, pCS->CountComponents());
   size_t count = std::min<size_t>(8, pBC->size());
-  std::vector<float> floats = ReadArrayElementsToVector(pBC, count);
+  std::vector<float> floats = ReadArrayElementsToVector(pBC.Get(), count);
   floats.resize(comps);
 
   float R;
   float G;
   float B;
-  pCS->GetRGB(floats.data(), &R, &G, &B);
+  pCS->GetRGB(floats, &R, &G, &B);
   return ArgbEncode(255, static_cast<int>(R * 255), static_cast<int>(G * 255),
                     static_cast<int>(B * 255));
 }
diff --git a/core/fpdfapi/render/cpdf_renderstatus.h b/core/fpdfapi/render/cpdf_renderstatus.h
index 76eb71c..76596f0 100644
--- a/core/fpdfapi/render/cpdf_renderstatus.h
+++ b/core/fpdfapi/render/cpdf_renderstatus.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,24 +8,25 @@
 #define CORE_FPDFAPI_RENDER_CPDF_RENDERSTATUS_H_
 
 #include <memory>
+#include <utility>
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_clippath.h"
+#include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fpdfapi/page/cpdf_graphicstates.h"
 #include "core/fpdfapi/page/cpdf_transparency.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
 
 class CFX_DIBitmap;
-class CFX_PathData;
+class CFX_Path;
 class CFX_RenderDevice;
 class CPDF_Color;
 class CPDF_Font;
 class CPDF_FormObject;
-class CPDF_ImageCacheEntry;
 class CPDF_ImageObject;
 class CPDF_ImageRenderer;
 class CPDF_Object;
@@ -37,7 +38,6 @@
 class CPDF_ShadingPattern;
 class CPDF_TilingPattern;
 class CPDF_TransferFunc;
-class CPDF_Type3Cache;
 class CPDF_Type3Char;
 class CPDF_Type3Font;
 class PauseIndicatorIface;
@@ -51,15 +51,17 @@
   void SetOptions(const CPDF_RenderOptions& options) { m_Options = options; }
   void SetDeviceMatrix(const CFX_Matrix& matrix) { m_DeviceMatrix = matrix; }
   void SetStopObject(const CPDF_PageObject* pStopObj) { m_pStopObj = pStopObj; }
-  void SetFormResource(const CPDF_Dictionary* pRes) {
-    m_pFormResource.Reset(pRes);
+  void SetFormResource(RetainPtr<const CPDF_Dictionary> pRes) {
+    m_pFormResource = std::move(pRes);
   }
   void SetType3Char(CPDF_Type3Char* pType3Char) { m_pType3Char = pType3Char; }
   void SetFillColor(FX_ARGB color) { m_T3FillColor = color; }
   void SetDropObjects(bool bDropObjects) { m_bDropObjects = bDropObjects; }
   void SetLoadMask(bool bLoadMask) { m_bLoadMask = bLoadMask; }
   void SetStdCS(bool bStdCS) { m_bStdCS = bStdCS; }
-  void SetGroupFamily(uint32_t family) { m_GroupFamily = family; }
+  void SetGroupFamily(CPDF_ColorSpace::Family family) {
+    m_GroupFamily = family;
+  }
   void SetTransparency(const CPDF_Transparency& transparency) {
     m_Transparency = transparency;
   }
@@ -77,41 +79,39 @@
   void ProcessClipPath(const CPDF_ClipPath& ClipPath,
                        const CFX_Matrix& mtObj2Device);
 
-  uint32_t GetGroupFamily() const { return m_GroupFamily; }
+  CPDF_ColorSpace::Family GetGroupFamily() const { return m_GroupFamily; }
   bool GetLoadMask() const { return m_bLoadMask; }
   bool GetDropObjects() const { return m_bDropObjects; }
   bool IsPrint() const { return m_bPrint; }
   bool IsStopped() const { return m_bStopped; }
-  CPDF_RenderContext* GetContext() const { return m_pContext.Get(); }
+  CPDF_RenderContext* GetContext() const { return m_pContext; }
   const CPDF_Dictionary* GetFormResource() const {
     return m_pFormResource.Get();
   }
-  CPDF_Dictionary* GetPageResource() const { return m_pPageResource.Get(); }
+  const CPDF_Dictionary* GetPageResource() const {
+    return m_pPageResource.Get();
+  }
   CFX_RenderDevice* GetRenderDevice() const { return m_pDevice; }
   const CPDF_RenderOptions& GetRenderOptions() const { return m_Options; }
 
-#if defined _SKIA_SUPPORT_
+#if defined(_SKIA_SUPPORT_)
   void DebugVerifyDeviceIsPreMultiplied() const;
 #endif
 
   RetainPtr<CPDF_TransferFunc> GetTransferFunc(
-      const CPDF_Object* pObject) const;
+      RetainPtr<const CPDF_Object> pObject) const;
 
-  FX_ARGB GetFillArgb(CPDF_PageObject* pObj) const {
-    return GetFillArgbInternal(pObj, false);
-  }
-  FX_ARGB GetFillArgbForType3(CPDF_PageObject* pObj) const {
-    return GetFillArgbInternal(pObj, true);
-  }
+  FX_ARGB GetFillArgb(CPDF_PageObject* pObj) const;
+  FX_ARGB GetFillArgbForType3(CPDF_PageObject* pObj) const;
 
-  void DrawTilingPattern(CPDF_TilingPattern* pPattern,
+  void DrawTilingPattern(CPDF_TilingPattern* pattern,
                          CPDF_PageObject* pPageObj,
                          const CFX_Matrix& mtObj2Device,
-                         bool bStroke);
-  void DrawShadingPattern(CPDF_ShadingPattern* pPattern,
+                         bool stroke);
+  void DrawShadingPattern(CPDF_ShadingPattern* pattern,
                           const CPDF_PageObject* pPageObj,
                           const CFX_Matrix& mtObj2Device,
-                          bool bStroke);
+                          bool stroke);
   void CompositeDIBitmap(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
                          int left,
                          int top,
@@ -120,12 +120,11 @@
                          BlendMode blend_mode,
                          const CPDF_Transparency& transparency);
 
- private:
   static std::unique_ptr<CPDF_GraphicStates> CloneObjStates(
       const CPDF_GraphicStates* pSrcStates,
-      bool bStroke);
+      bool stroke);
 
-  FX_ARGB GetFillArgbInternal(CPDF_PageObject* pObj, bool bType3) const;
+ private:
   bool ProcessTransparency(CPDF_PageObject* PageObj,
                            const CFX_Matrix& mtObj2Device);
   void ProcessObjectNoClip(CPDF_PageObject* pObj,
@@ -133,21 +132,21 @@
   void DrawObjWithBackground(CPDF_PageObject* pObj,
                              const CFX_Matrix& mtObj2Device);
   bool DrawObjWithBlend(CPDF_PageObject* pObj, const CFX_Matrix& mtObj2Device);
-  bool ProcessPath(CPDF_PathObject* pPathObj, const CFX_Matrix& mtObj2Device);
-  void ProcessPathPattern(CPDF_PathObject* pPathObj,
+  bool ProcessPath(CPDF_PathObject* path_obj, const CFX_Matrix& mtObj2Device);
+  void ProcessPathPattern(CPDF_PathObject* path_obj,
                           const CFX_Matrix& mtObj2Device,
-                          int* filltype,
-                          bool* bStroke);
-  void DrawPathWithPattern(CPDF_PathObject* pPathObj,
+                          CFX_FillRenderOptions::FillType* fill_type,
+                          bool* stroke);
+  void DrawPathWithPattern(CPDF_PathObject* path_obj,
                            const CFX_Matrix& mtObj2Device,
                            const CPDF_Color* pColor,
-                           bool bStroke);
-  bool ClipPattern(const CPDF_PageObject* pPageObj,
+                           bool stroke);
+  bool ClipPattern(const CPDF_PageObject* page_obj,
                    const CFX_Matrix& mtObj2Device,
-                   bool bStroke);
-  bool SelectClipPath(const CPDF_PathObject* pPathObj,
+                   bool stroke);
+  bool SelectClipPath(const CPDF_PathObject* path_obj,
                       const CFX_Matrix& mtObj2Device,
-                      bool bStroke);
+                      bool stroke);
   bool ProcessImage(CPDF_ImageObject* pImageObj,
                     const CFX_Matrix& mtObj2Device);
   void ProcessShading(const CPDF_ShadingObject* pShadingObj,
@@ -156,52 +155,51 @@
                         const CFX_Matrix& mtObj2Device);
   bool ProcessText(CPDF_TextObject* textobj,
                    const CFX_Matrix& mtObj2Device,
-                   CFX_PathData* pClippingPath);
+                   CFX_Path* clipping_path);
   void DrawTextPathWithPattern(const CPDF_TextObject* textobj,
                                const CFX_Matrix& mtObj2Device,
                                CPDF_Font* pFont,
                                float font_size,
-                               const CFX_Matrix* pTextMatrix,
-                               bool bFill,
-                               bool bStroke);
+                               const CFX_Matrix& mtTextMatrix,
+                               bool fill,
+                               bool stroke);
   bool ProcessForm(const CPDF_FormObject* pFormObj,
                    const CFX_Matrix& mtObj2Device);
+  FX_RECT GetClippedBBox(const FX_RECT& rect) const;
   RetainPtr<CFX_DIBitmap> GetBackdrop(const CPDF_PageObject* pObj,
-                                      const FX_RECT& rect,
-                                      bool bBackAlphaRequired,
-                                      int* left,
-                                      int* top);
+                                      const FX_RECT& bbox,
+                                      bool bBackAlphaRequired);
   RetainPtr<CFX_DIBitmap> LoadSMask(CPDF_Dictionary* pSMaskDict,
                                     FX_RECT* pClipRect,
-                                    const CFX_Matrix* pMatrix);
+                                    const CFX_Matrix& mtMatrix);
   // Optionally write the colorspace family value into |pCSFamily|.
   FX_ARGB GetBackColor(const CPDF_Dictionary* pSMaskDict,
                        const CPDF_Dictionary* pGroupDict,
-                       int* pCSFamily);
+                       CPDF_ColorSpace::Family* pCSFamily);
   FX_ARGB GetStrokeArgb(CPDF_PageObject* pObj) const;
   FX_RECT GetObjectClippedRect(const CPDF_PageObject* pObj,
                                const CFX_Matrix& mtObj2Device) const;
 
   CPDF_RenderOptions m_Options;
   RetainPtr<const CPDF_Dictionary> m_pFormResource;
-  RetainPtr<CPDF_Dictionary> m_pPageResource;
-  std::vector<CPDF_Type3Font*> m_Type3FontCache;
+  RetainPtr<const CPDF_Dictionary> m_pPageResource;
+  std::vector<UnownedPtr<const CPDF_Type3Font>> m_Type3FontCache;
   UnownedPtr<CPDF_RenderContext> const m_pContext;
-  bool m_bStopped = false;
-  CFX_RenderDevice* const m_pDevice;
+  UnownedPtr<CFX_RenderDevice> const m_pDevice;
   CFX_Matrix m_DeviceMatrix;
   CPDF_ClipPath m_LastClipPath;
   UnownedPtr<const CPDF_PageObject> m_pCurObj;
   UnownedPtr<const CPDF_PageObject> m_pStopObj;
   CPDF_GraphicStates m_InitialStates;
   std::unique_ptr<CPDF_ImageRenderer> m_pImageRenderer;
+  UnownedPtr<const CPDF_Type3Char> m_pType3Char;
   CPDF_Transparency m_Transparency;
+  bool m_bStopped = false;
   bool m_bPrint = false;
   bool m_bDropObjects = false;
   bool m_bStdCS = false;
   bool m_bLoadMask = false;
-  uint32_t m_GroupFamily = 0;
-  UnownedPtr<CPDF_Type3Char> m_pType3Char;
+  CPDF_ColorSpace::Family m_GroupFamily = CPDF_ColorSpace::Family::kUnknown;
   FX_ARGB m_T3FillColor = 0;
   BlendMode m_curBlend = BlendMode::kNormal;
 };
diff --git a/core/fpdfapi/render/cpdf_rendertiling.cpp b/core/fpdfapi/render/cpdf_rendertiling.cpp
new file mode 100644
index 0000000..5f9dcd7
--- /dev/null
+++ b/core/fpdfapi/render/cpdf_rendertiling.cpp
@@ -0,0 +1,251 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/render/cpdf_rendertiling.h"
+
+#include <limits>
+#include <memory>
+
+#include "core/fpdfapi/page/cpdf_form.h"
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
+#include "core/fpdfapi/page/cpdf_tilingpattern.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/render/cpdf_rendercontext.h"
+#include "core/fpdfapi/render/cpdf_renderoptions.h"
+#include "core/fpdfapi/render/cpdf_renderstatus.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+
+namespace {
+
+RetainPtr<CFX_DIBitmap> DrawPatternBitmap(
+    CPDF_Document* pDoc,
+    CPDF_PageImageCache* pCache,
+    CPDF_TilingPattern* pPattern,
+    CPDF_Form* pPatternForm,
+    const CFX_Matrix& mtObject2Device,
+    int width,
+    int height,
+    const CPDF_RenderOptions::Options& draw_options) {
+  auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!pBitmap->Create(width, height,
+                       pPattern->colored() ? FXDIB_Format::kArgb
+                                           : FXDIB_Format::k8bppMask)) {
+    return nullptr;
+  }
+  CFX_DefaultRenderDevice bitmap_device;
+  bitmap_device.AttachWithBackdropAndGroupKnockout(
+      pBitmap, /*pBackdropBitmap=*/nullptr, /*bGroupKnockout=*/true);
+  CFX_FloatRect cell_bbox =
+      pPattern->pattern_to_form().TransformRect(pPattern->bbox());
+  cell_bbox = mtObject2Device.TransformRect(cell_bbox);
+  CFX_FloatRect bitmap_rect(0.0f, 0.0f, width, height);
+  CFX_Matrix mtAdjust;
+  mtAdjust.MatchRect(bitmap_rect, cell_bbox);
+
+  CFX_Matrix mtPattern2Bitmap = mtObject2Device * mtAdjust;
+  CPDF_RenderOptions options;
+  if (!pPattern->colored())
+    options.SetColorMode(CPDF_RenderOptions::kAlpha);
+
+  options.GetOptions() = draw_options;
+  options.GetOptions().bForceHalftone = true;
+
+  CPDF_RenderContext context(pDoc, nullptr, pCache);
+  context.AppendLayer(pPatternForm, mtPattern2Bitmap);
+  context.Render(&bitmap_device, nullptr, &options, nullptr);
+
+#if defined(_SKIA_SUPPORT_)
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    pBitmap->UnPreMultiply();
+#endif  // defined(_SKIA_SUPPORT_)
+  return pBitmap;
+}
+
+}  // namespace
+
+// static
+RetainPtr<CFX_DIBitmap> CPDF_RenderTiling::Draw(
+    CPDF_RenderStatus* pRenderStatus,
+    CPDF_PageObject* pPageObj,
+    CPDF_TilingPattern* pPattern,
+    CPDF_Form* pPatternForm,
+    const CFX_Matrix& mtObj2Device,
+    const FX_RECT& clip_box,
+    bool bStroke) {
+  const CFX_Matrix mtPattern2Device =
+      pPattern->pattern_to_form() * mtObj2Device;
+
+  CFX_FloatRect cell_bbox = mtPattern2Device.TransformRect(pPattern->bbox());
+
+  float ceil_height = std::ceil(cell_bbox.Height());
+  float ceil_width = std::ceil(cell_bbox.Width());
+
+  // Validate the float will fit into the int when the conversion is done.
+  if (!pdfium::base::IsValueInRangeForNumericType<int>(ceil_height) ||
+      !pdfium::base::IsValueInRangeForNumericType<int>(ceil_width)) {
+    return nullptr;
+  }
+
+  int width = static_cast<int>(ceil_width);
+  int height = static_cast<int>(ceil_height);
+  if (width <= 0)
+    width = 1;
+  if (height <= 0)
+    height = 1;
+
+  CFX_FloatRect clip_box_p =
+      mtPattern2Device.GetInverse().TransformRect(CFX_FloatRect(clip_box));
+  int min_col = static_cast<int>(
+      ceil((clip_box_p.left - pPattern->bbox().right) / pPattern->x_step()));
+  int max_col = static_cast<int>(
+      floor((clip_box_p.right - pPattern->bbox().left) / pPattern->x_step()));
+  int min_row = static_cast<int>(
+      ceil((clip_box_p.bottom - pPattern->bbox().top) / pPattern->y_step()));
+  int max_row = static_cast<int>(
+      floor((clip_box_p.top - pPattern->bbox().bottom) / pPattern->y_step()));
+
+  // Make sure we can fit the needed width * height into an int.
+  if (height > std::numeric_limits<int>::max() / width)
+    return nullptr;
+
+  CFX_RenderDevice* pDevice = pRenderStatus->GetRenderDevice();
+  CPDF_RenderContext* pContext = pRenderStatus->GetContext();
+  const CPDF_RenderOptions& options = pRenderStatus->GetRenderOptions();
+  if (width > clip_box.Width() || height > clip_box.Height() ||
+      width * height > clip_box.Width() * clip_box.Height()) {
+    std::unique_ptr<CPDF_GraphicStates> pStates;
+    if (!pPattern->colored())
+      pStates = CPDF_RenderStatus::CloneObjStates(pPageObj, bStroke);
+
+    RetainPtr<const CPDF_Dictionary> pFormResource =
+        pPatternForm->GetDict()->GetDictFor("Resources");
+    for (int col = min_col; col <= max_col; col++) {
+      for (int row = min_row; row <= max_row; row++) {
+        CFX_PointF original = mtPattern2Device.Transform(
+            CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step()));
+        CFX_Matrix matrix = mtObj2Device;
+        matrix.Translate(original.x - mtPattern2Device.e,
+                         original.y - mtPattern2Device.f);
+        CFX_RenderDevice::StateRestorer restorer2(pDevice);
+        CPDF_RenderStatus status(pContext, pDevice);
+        status.SetOptions(options);
+        status.SetTransparency(pPatternForm->GetTransparency());
+        status.SetFormResource(pFormResource);
+        status.SetDropObjects(pRenderStatus->GetDropObjects());
+        status.Initialize(pRenderStatus, pStates.get());
+        status.RenderObjectList(pPatternForm, matrix);
+      }
+    }
+    return nullptr;
+  }
+
+  bool bAligned =
+      pPattern->bbox().left == 0 && pPattern->bbox().bottom == 0 &&
+      pPattern->bbox().right == pPattern->x_step() &&
+      pPattern->bbox().top == pPattern->y_step() &&
+      (mtPattern2Device.IsScaled() || mtPattern2Device.Is90Rotated());
+  if (bAligned) {
+    int orig_x = FXSYS_roundf(mtPattern2Device.e);
+    int orig_y = FXSYS_roundf(mtPattern2Device.f);
+    min_col = (clip_box.left - orig_x) / width;
+    if (clip_box.left < orig_x)
+      min_col--;
+
+    max_col = (clip_box.right - orig_x) / width;
+    if (clip_box.right <= orig_x)
+      max_col--;
+
+    min_row = (clip_box.top - orig_y) / height;
+    if (clip_box.top < orig_y)
+      min_row--;
+
+    max_row = (clip_box.bottom - orig_y) / height;
+    if (clip_box.bottom <= orig_y)
+      max_row--;
+  }
+  float left_offset = cell_bbox.left - mtPattern2Device.e;
+  float top_offset = cell_bbox.bottom - mtPattern2Device.f;
+  RetainPtr<CFX_DIBitmap> pPatternBitmap;
+  if (width * height < 16) {
+    RetainPtr<CFX_DIBitmap> pEnlargedBitmap = DrawPatternBitmap(
+        pContext->GetDocument(), pContext->GetPageCache(), pPattern,
+        pPatternForm, mtObj2Device, 8, 8, options.GetOptions());
+    pPatternBitmap = pEnlargedBitmap->StretchTo(
+        width, height, FXDIB_ResampleOptions(), nullptr);
+  } else {
+    pPatternBitmap = DrawPatternBitmap(
+        pContext->GetDocument(), pContext->GetPageCache(), pPattern,
+        pPatternForm, mtObj2Device, width, height, options.GetOptions());
+  }
+  if (!pPatternBitmap)
+    return nullptr;
+
+  if (options.ColorModeIs(CPDF_RenderOptions::kGray))
+    pPatternBitmap->ConvertColorScale(0, 0xffffff);
+
+  FX_ARGB fill_argb = pRenderStatus->GetFillArgb(pPageObj);
+  int clip_width = clip_box.right - clip_box.left;
+  int clip_height = clip_box.bottom - clip_box.top;
+  auto pScreen = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!pScreen->Create(clip_width, clip_height, FXDIB_Format::kArgb))
+    return nullptr;
+
+  pdfium::span<const uint8_t> src_buf = pPatternBitmap->GetBuffer();
+  for (int col = min_col; col <= max_col; col++) {
+    for (int row = min_row; row <= max_row; row++) {
+      int start_x;
+      int start_y;
+      if (bAligned) {
+        start_x =
+            FXSYS_roundf(mtPattern2Device.e) + col * width - clip_box.left;
+        start_y =
+            FXSYS_roundf(mtPattern2Device.f) + row * height - clip_box.top;
+      } else {
+        CFX_PointF original = mtPattern2Device.Transform(
+            CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step()));
+
+        FX_SAFE_INT32 safeStartX = FXSYS_roundf(original.x + left_offset);
+        FX_SAFE_INT32 safeStartY = FXSYS_roundf(original.y + top_offset);
+
+        safeStartX -= clip_box.left;
+        safeStartY -= clip_box.top;
+        if (!safeStartX.IsValid() || !safeStartY.IsValid())
+          return nullptr;
+
+        start_x = safeStartX.ValueOrDie();
+        start_y = safeStartY.ValueOrDie();
+      }
+      if (width == 1 && height == 1) {
+        if (start_x < 0 || start_x >= clip_box.Width() || start_y < 0 ||
+            start_y >= clip_box.Height()) {
+          continue;
+        }
+        uint32_t* dest_buf = reinterpret_cast<uint32_t*>(
+            pScreen->GetWritableScanline(start_y).subspan(start_x * 4).data());
+        if (pPattern->colored()) {
+          const auto* src_buf32 =
+              reinterpret_cast<const uint32_t*>(src_buf.data());
+          *dest_buf = *src_buf32;
+        } else {
+          *dest_buf = (*(src_buf.data()) << 24) | (fill_argb & 0xffffff);
+        }
+      } else {
+        if (pPattern->colored()) {
+          pScreen->CompositeBitmap(start_x, start_y, width, height,
+                                   pPatternBitmap, 0, 0, BlendMode::kNormal,
+                                   nullptr, false);
+        } else {
+          pScreen->CompositeMask(start_x, start_y, width, height,
+                                 pPatternBitmap, fill_argb, 0, 0,
+                                 BlendMode::kNormal, nullptr, false);
+        }
+      }
+    }
+  }
+  return pScreen;
+}
diff --git a/core/fpdfapi/render/cpdf_rendertiling.h b/core/fpdfapi/render/cpdf_rendertiling.h
new file mode 100644
index 0000000..34fb56b
--- /dev/null
+++ b/core/fpdfapi/render/cpdf_rendertiling.h
@@ -0,0 +1,35 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFAPI_RENDER_CPDF_RENDERTILING_H_
+#define CORE_FPDFAPI_RENDER_CPDF_RENDERTILING_H_
+
+#include "core/fxcrt/retain_ptr.h"
+
+class CFX_DIBitmap;
+class CFX_Matrix;
+class CPDF_Form;
+class CPDF_PageObject;
+class CPDF_RenderStatus;
+class CPDF_TilingPattern;
+struct FX_RECT;
+
+class CPDF_RenderTiling {
+ public:
+  static RetainPtr<CFX_DIBitmap> Draw(CPDF_RenderStatus* pRenderStatus,
+                                      CPDF_PageObject* pPageObj,
+                                      CPDF_TilingPattern* pPattern,
+                                      CPDF_Form* pPatternForm,
+                                      const CFX_Matrix& mtObj2Device,
+                                      const FX_RECT& clip_box,
+                                      bool bStroke);
+
+  CPDF_RenderTiling() = delete;
+  CPDF_RenderTiling(const CPDF_RenderTiling&) = delete;
+  CPDF_RenderTiling& operator=(const CPDF_RenderTiling&) = delete;
+};
+
+#endif  // CORE_FPDFAPI_RENDER_CPDF_RENDERTILING_H_
diff --git a/core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp b/core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp
index 8536b3b..26e8b7f 100644
--- a/core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp
+++ b/core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,11 +6,11 @@
 
 #include "core/fpdfapi/render/cpdf_scaledrenderbuffer.h"
 
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/render/cpdf_devicebuffer.h"
 #include "core/fpdfapi/render/cpdf_rendercontext.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -18,9 +18,9 @@
 
 }  // namespace
 
-CPDF_ScaledRenderBuffer::CPDF_ScaledRenderBuffer() {}
+CPDF_ScaledRenderBuffer::CPDF_ScaledRenderBuffer() = default;
 
-CPDF_ScaledRenderBuffer::~CPDF_ScaledRenderBuffer() {}
+CPDF_ScaledRenderBuffer::~CPDF_ScaledRenderBuffer() = default;
 
 bool CPDF_ScaledRenderBuffer::Initialize(CPDF_RenderContext* pContext,
                                          CFX_RenderDevice* pDevice,
@@ -32,41 +32,39 @@
   if (m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_GET_BITS)
     return true;
 
-  m_pContext = pContext;
   m_Rect = rect;
-  m_pObject = pObj;
   m_Matrix = CPDF_DeviceBuffer::CalculateMatrix(pDevice, rect, max_dpi,
                                                 /*scale=*/true);
-  m_pBitmapDevice = pdfium::MakeUnique<CFX_DefaultRenderDevice>();
+  m_pBitmapDevice = std::make_unique<CFX_DefaultRenderDevice>();
   bool bIsAlpha =
       !!(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_ALPHA_OUTPUT);
-  FXDIB_Format dibFormat = bIsAlpha ? FXDIB_Argb : FXDIB_Rgb;
-  while (1) {
+  FXDIB_Format dibFormat = bIsAlpha ? FXDIB_Format::kArgb : FXDIB_Format::kRgb;
+  while (true) {
     FX_RECT bitmap_rect =
         m_Matrix.TransformRect(CFX_FloatRect(rect)).GetOuterRect();
     int32_t width = bitmap_rect.Width();
     int32_t height = bitmap_rect.Height();
     // Set to 0 to make CalculatePitchAndSize() calculate it.
-    uint32_t pitch = 0;
-    uint32_t size;
-    if (!CFX_DIBitmap::CalculatePitchAndSize(width, height, dibFormat, &pitch,
-                                             &size)) {
+    constexpr uint32_t kNoPitch = 0;
+    absl::optional<CFX_DIBitmap::PitchAndSize> pitch_size =
+        CFX_DIBitmap::CalculatePitchAndSize(width, height, dibFormat, kNoPitch);
+    if (!pitch_size.has_value())
       return false;
-    }
 
-    if (size <= kImageSizeLimitBytes &&
+    if (pitch_size.value().size <= kImageSizeLimitBytes &&
         m_pBitmapDevice->Create(width, height, dibFormat, nullptr)) {
       break;
     }
     m_Matrix.Scale(0.5f, 0.5f);
   }
-  m_pContext->GetBackground(m_pBitmapDevice->GetBitmap(), m_pObject.Get(),
-                            pOptions, m_Matrix);
+  pContext->GetBackground(m_pBitmapDevice->GetBitmap(), pObj, pOptions,
+                          m_Matrix);
   return true;
 }
 
 CFX_RenderDevice* CPDF_ScaledRenderBuffer::GetDevice() const {
-  return m_pBitmapDevice ? m_pBitmapDevice.get() : m_pDevice.Get();
+  return m_pBitmapDevice ? static_cast<CFX_RenderDevice*>(m_pBitmapDevice.get())
+                         : m_pDevice.get();
 }
 
 void CPDF_ScaledRenderBuffer::OutputToDevice() {
diff --git a/core/fpdfapi/render/cpdf_scaledrenderbuffer.h b/core/fpdfapi/render/cpdf_scaledrenderbuffer.h
index 0e7ac07..3fb9c0b 100644
--- a/core/fpdfapi/render/cpdf_scaledrenderbuffer.h
+++ b/core/fpdfapi/render/cpdf_scaledrenderbuffer.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -36,10 +36,8 @@
 
  private:
   UnownedPtr<CFX_RenderDevice> m_pDevice;
-  UnownedPtr<CPDF_RenderContext> m_pContext;
-  FX_RECT m_Rect;
-  UnownedPtr<const CPDF_PageObject> m_pObject;
   std::unique_ptr<CFX_DefaultRenderDevice> m_pBitmapDevice;
+  FX_RECT m_Rect;
   CFX_Matrix m_Matrix;
 };
 
diff --git a/core/fpdfapi/render/cpdf_textrenderer.cpp b/core/fpdfapi/render/cpdf_textrenderer.cpp
index fe1d258..e1a2acb 100644
--- a/core/fpdfapi/render/cpdf_textrenderer.cpp
+++ b/core/fpdfapi/render/cpdf_textrenderer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,15 @@
 #include "core/fpdfapi/render/cpdf_textrenderer.h"
 
 #include <algorithm>
+#include <vector>
 
 #include "core/fpdfapi/font/cpdf_font.h"
-#include "core/fpdfapi/render/cpdf_charposlist.h"
+#include "core/fpdfapi/render/charposlist.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
+#include "core/fxge/cfx_textrenderoptions.h"
 #include "core/fxge/fx_font.h"
 #include "core/fxge/text_char_pos.h"
 
@@ -23,23 +25,43 @@
   return position == -1 ? pFont->GetFont() : pFont->GetFontFallback(position);
 }
 
+CFX_TextRenderOptions GetTextRenderOptionsHelper(
+    const CPDF_Font* pFont,
+    const CPDF_RenderOptions& options) {
+  CFX_TextRenderOptions text_options;
+
+  if (pFont->IsCIDFont())
+    text_options.font_is_cid = true;
+
+  if (options.GetOptions().bNoTextSmooth)
+    text_options.aliasing_type = CFX_TextRenderOptions::kAliasing;
+  else if (options.GetOptions().bClearType)
+    text_options.aliasing_type = CFX_TextRenderOptions::kLcd;
+
+  if (options.GetOptions().bNoNativeText)
+    text_options.native_text = false;
+
+  return text_options;
+}
+
 }  // namespace
 
 // static
-bool CPDF_TextRenderer::DrawTextPath(CFX_RenderDevice* pDevice,
-                                     const std::vector<uint32_t>& charCodes,
-                                     const std::vector<float>& charPos,
-                                     CPDF_Font* pFont,
-                                     float font_size,
-                                     const CFX_Matrix& mtText2User,
-                                     const CFX_Matrix* pUser2Device,
-                                     const CFX_GraphStateData* pGraphState,
-                                     FX_ARGB fill_argb,
-                                     FX_ARGB stroke_argb,
-                                     CFX_PathData* pClippingPath,
-                                     int nFlag) {
-  const CPDF_CharPosList CharPosList(charCodes, charPos, pFont, font_size);
-  const std::vector<TextCharPos>& pos = CharPosList.Get();
+bool CPDF_TextRenderer::DrawTextPath(
+    CFX_RenderDevice* pDevice,
+    pdfium::span<const uint32_t> char_codes,
+    pdfium::span<const float> char_pos,
+    CPDF_Font* pFont,
+    float font_size,
+    const CFX_Matrix& mtText2User,
+    const CFX_Matrix* pUser2Device,
+    const CFX_GraphStateData* pGraphState,
+    FX_ARGB fill_argb,
+    FX_ARGB stroke_argb,
+    CFX_Path* pClippingPath,
+    const CFX_FillRenderOptions& fill_options) {
+  std::vector<TextCharPos> pos =
+      GetCharPosList(char_codes, char_pos, pFont, font_size);
   if (pos.empty())
     return true;
 
@@ -52,19 +74,20 @@
       continue;
 
     CFX_Font* font = GetFont(pFont, fontPosition);
-    if (!pDevice->DrawTextPath(i - startIndex, &pos[startIndex], font,
-                               font_size, mtText2User, pUser2Device,
-                               pGraphState, fill_argb, stroke_argb,
-                               pClippingPath, nFlag)) {
+    if (!pDevice->DrawTextPath(
+            pdfium::make_span(pos).subspan(startIndex, i - startIndex), font,
+            font_size, mtText2User, pUser2Device, pGraphState, fill_argb,
+            stroke_argb, pClippingPath, fill_options)) {
       bDraw = false;
     }
     fontPosition = curFontPosition;
     startIndex = i;
   }
   CFX_Font* font = GetFont(pFont, fontPosition);
-  if (!pDevice->DrawTextPath(pos.size() - startIndex, &pos[startIndex], font,
+  if (!pDevice->DrawTextPath(pdfium::make_span(pos).subspan(startIndex), font,
                              font_size, mtText2User, pUser2Device, pGraphState,
-                             fill_argb, stroke_argb, pClippingPath, nFlag)) {
+                             fill_argb, stroke_argb, pClippingPath,
+                             fill_options)) {
     bDraw = false;
   }
   return bDraw;
@@ -83,8 +106,8 @@
   if (pFont->IsType3Font())
     return;
 
-  int nChars = pFont->CountChar(str.AsStringView());
-  if (nChars <= 0)
+  size_t nChars = pFont->CountChar(str.AsStringView());
+  if (nChars == 0)
     return;
 
   size_t offset = 0;
@@ -93,7 +116,7 @@
   codes.resize(nChars);
   positions.resize(nChars - 1);
   float cur_pos = 0;
-  for (int i = 0; i < nChars; i++) {
+  for (size_t i = 0; i < nChars; i++) {
     codes[i] = pFont->GetNextChar(str.AsStringView(), &offset);
     if (i)
       positions[i - 1] = cur_pos;
@@ -108,36 +131,20 @@
 
 // static
 bool CPDF_TextRenderer::DrawNormalText(CFX_RenderDevice* pDevice,
-                                       const std::vector<uint32_t>& charCodes,
-                                       const std::vector<float>& charPos,
+                                       pdfium::span<const uint32_t> char_codes,
+                                       pdfium::span<const float> char_pos,
                                        CPDF_Font* pFont,
                                        float font_size,
                                        const CFX_Matrix& mtText2Device,
                                        FX_ARGB fill_argb,
                                        const CPDF_RenderOptions& options) {
-  const CPDF_CharPosList CharPosList(charCodes, charPos, pFont, font_size);
-  const std::vector<TextCharPos>& pos = CharPosList.Get();
+  std::vector<TextCharPos> pos =
+      GetCharPosList(char_codes, char_pos, pFont, font_size);
   if (pos.empty())
     return true;
 
-  int fxge_flags = 0;
-  if (options.GetOptions().bClearType) {
-    fxge_flags |= FXTEXT_CLEARTYPE;
-    if (options.GetOptions().bBGRStripe)
-      fxge_flags |= FXTEXT_BGR_STRIPE;
-  }
-  if (options.GetOptions().bNoTextSmooth)
-    fxge_flags |= FXTEXT_NOSMOOTH;
-  if (options.GetOptions().bPrintGraphicText)
-    fxge_flags |= FXTEXT_PRINTGRAPHICTEXT;
-  if (options.GetOptions().bNoNativeText)
-    fxge_flags |= FXTEXT_NO_NATIVETEXT;
-  if (options.GetOptions().bPrintImageText)
-    fxge_flags |= FXTEXT_PRINTIMAGETEXT;
-
-  if (pFont->IsCIDFont())
-    fxge_flags |= FXFONT_CIDFONT;
-
+  CFX_TextRenderOptions text_options =
+      GetTextRenderOptionsHelper(pFont, options);
   bool bDraw = true;
   int32_t fontPosition = pos[0].m_FallbackFontPosition;
   size_t startIndex = 0;
@@ -147,18 +154,18 @@
       continue;
 
     CFX_Font* font = GetFont(pFont, fontPosition);
-    if (!pDevice->DrawNormalText(i - startIndex, &pos[startIndex], font,
-                                 font_size, mtText2Device, fill_argb,
-                                 fxge_flags)) {
+    if (!pDevice->DrawNormalText(
+            pdfium::make_span(pos).subspan(startIndex, i - startIndex), font,
+            font_size, mtText2Device, fill_argb, text_options)) {
       bDraw = false;
     }
     fontPosition = curFontPosition;
     startIndex = i;
   }
   CFX_Font* font = GetFont(pFont, fontPosition);
-  if (!pDevice->DrawNormalText(pos.size() - startIndex, &pos[startIndex], font,
+  if (!pDevice->DrawNormalText(pdfium::make_span(pos).subspan(startIndex), font,
                                font_size, mtText2Device, fill_argb,
-                               fxge_flags)) {
+                               text_options)) {
     bDraw = false;
   }
   return bDraw;
diff --git a/core/fpdfapi/render/cpdf_textrenderer.h b/core/fpdfapi/render/cpdf_textrenderer.h
index 91ab4cc..857340f 100644
--- a/core/fpdfapi/render/cpdf_textrenderer.h
+++ b/core/fpdfapi/render/cpdf_textrenderer.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,18 +7,19 @@
 #ifndef CORE_FPDFAPI_RENDER_CPDF_TEXTRENDERER_H_
 #define CORE_FPDFAPI_RENDER_CPDF_TEXTRENDERER_H_
 
-#include <vector>
+#include <stdint.h>
 
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_RenderDevice;
 class CFX_GraphStateData;
-class CFX_PathData;
+class CFX_Path;
 class CPDF_RenderOptions;
 class CPDF_Font;
+struct CFX_FillRenderOptions;
 
 class CPDF_TextRenderer {
  public:
@@ -33,8 +34,8 @@
                              const CPDF_RenderOptions& options);
 
   static bool DrawTextPath(CFX_RenderDevice* pDevice,
-                           const std::vector<uint32_t>& charCodes,
-                           const std::vector<float>& charPos,
+                           pdfium::span<const uint32_t> char_codes,
+                           pdfium::span<const float> char_pos,
                            CPDF_Font* pFont,
                            float font_size,
                            const CFX_Matrix& mtText2User,
@@ -42,12 +43,12 @@
                            const CFX_GraphStateData* pGraphState,
                            FX_ARGB fill_argb,
                            FX_ARGB stroke_argb,
-                           CFX_PathData* pClippingPath,
-                           int nFlag);
+                           CFX_Path* pClippingPath,
+                           const CFX_FillRenderOptions& fill_options);
 
   static bool DrawNormalText(CFX_RenderDevice* pDevice,
-                             const std::vector<uint32_t>& charCodes,
-                             const std::vector<float>& charPos,
+                             pdfium::span<const uint32_t> char_codes,
+                             pdfium::span<const float> char_pos,
                              CPDF_Font* pFont,
                              float font_size,
                              const CFX_Matrix& mtText2Device,
diff --git a/core/fpdfapi/render/cpdf_type3cache.cpp b/core/fpdfapi/render/cpdf_type3cache.cpp
index 41e630b..f009456 100644
--- a/core/fpdfapi/render/cpdf_type3cache.cpp
+++ b/core/fpdfapi/render/cpdf_type3cache.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,43 +6,23 @@
 
 #include "core/fpdfapi/render/cpdf_type3cache.h"
 
-#include <map>
+#include <math.h>
+
 #include <memory>
 #include <utility>
 
 #include "core/fpdfapi/font/cpdf_type3char.h"
 #include "core/fpdfapi/font/cpdf_type3font.h"
-#include "core/fpdfapi/page/cpdf_docpagedata.h"
-#include "core/fpdfapi/render/cpdf_docrenderdata.h"
 #include "core/fpdfapi/render/cpdf_type3glyphmap.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxge/cfx_glyphbitmap.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/ptr_util.h"
+#include "core/fxge/dib/fx_dib.h"
 
 namespace {
 
-struct CPDF_UniqueKeyGen {
-  void Generate(int count, ...);
-
-  int m_KeyLen;
-  char m_Key[128];
-};
-
-void CPDF_UniqueKeyGen::Generate(int count, ...) {
-  va_list argList;
-  va_start(argList, count);
-  for (int i = 0; i < count; i++) {
-    int p = va_arg(argList, int);
-    (reinterpret_cast<uint32_t*>(m_Key))[i] = p;
-  }
-  va_end(argList);
-  m_KeyLen = count * sizeof(uint32_t);
-}
-
-bool IsScanLine1bpp(uint8_t* pBuf, int width) {
+bool IsScanLine1bpp(const uint8_t* pBuf, int width) {
   int size = width / 8;
   for (int i = 0; i < size; i++) {
     if (pBuf[i])
@@ -51,7 +31,7 @@
   return (width % 8) && (pBuf[width / 8] & (0xff << (8 - width % 8)));
 }
 
-bool IsScanLine8bpp(uint8_t* pBuf, int width) {
+bool IsScanLine8bpp(const uint8_t* pBuf, int width) {
   for (int i = 0; i < width; i++) {
     if (pBuf[i] > 0x40)
       return true;
@@ -59,26 +39,34 @@
   return false;
 }
 
-int DetectFirstLastScan(const RetainPtr<CFX_DIBitmap>& pBitmap, bool bFirst) {
-  int height = pBitmap->GetHeight();
-  int pitch = pBitmap->GetPitch();
-  int width = pBitmap->GetWidth();
-  int bpp = pBitmap->GetBPP();
+bool IsScanLineBpp(int bpp, const uint8_t* pBuf, int width) {
+  if (bpp == 1)
+    return IsScanLine1bpp(pBuf, width);
   if (bpp > 8)
     width *= bpp / 8;
-  uint8_t* pBuf = pBitmap->GetBuffer();
-  int line = bFirst ? 0 : height - 1;
-  int line_step = bFirst ? 1 : -1;
-  int line_end = bFirst ? height : -1;
-  while (line != line_end) {
-    if (bpp == 1) {
-      if (IsScanLine1bpp(pBuf + line * pitch, width))
-        return line;
-    } else {
-      if (IsScanLine8bpp(pBuf + line * pitch, width))
-        return line;
-    }
-    line += line_step;
+  return IsScanLine8bpp(pBuf, width);
+}
+
+int DetectFirstScan(const RetainPtr<CFX_DIBitmap>& pBitmap) {
+  const int height = pBitmap->GetHeight();
+  const int width = pBitmap->GetWidth();
+  const int bpp = pBitmap->GetBPP();
+  for (int line = 0; line < height; ++line) {
+    const uint8_t* pBuf = pBitmap->GetScanline(line).data();
+    if (IsScanLineBpp(bpp, pBuf, width))
+      return line;
+  }
+  return -1;
+}
+
+int DetectLastScan(const RetainPtr<CFX_DIBitmap>& pBitmap) {
+  const int height = pBitmap->GetHeight();
+  const int bpp = pBitmap->GetBPP();
+  const int width = pBitmap->GetWidth();
+  for (int line = height - 1; line >= 0; --line) {
+    const uint8_t* pBuf = pBitmap->GetScanline(line).data();
+    if (IsScanLineBpp(bpp, pBuf, width))
+      return line;
   }
   return -1;
 }
@@ -90,18 +78,19 @@
 CPDF_Type3Cache::~CPDF_Type3Cache() = default;
 
 const CFX_GlyphBitmap* CPDF_Type3Cache::LoadGlyph(uint32_t charcode,
-                                                  const CFX_Matrix* pMatrix) {
-  CPDF_UniqueKeyGen keygen;
-  keygen.Generate(
-      4, FXSYS_roundf(pMatrix->a * 10000), FXSYS_roundf(pMatrix->b * 10000),
-      FXSYS_roundf(pMatrix->c * 10000), FXSYS_roundf(pMatrix->d * 10000));
-  ByteString FaceGlyphsKey(keygen.m_Key, keygen.m_KeyLen);
+                                                  const CFX_Matrix& mtMatrix) {
+  SizeKey keygen = {
+      FXSYS_roundf(mtMatrix.a * 10000),
+      FXSYS_roundf(mtMatrix.b * 10000),
+      FXSYS_roundf(mtMatrix.c * 10000),
+      FXSYS_roundf(mtMatrix.d * 10000),
+  };
   CPDF_Type3GlyphMap* pSizeCache;
-  auto it = m_SizeMap.find(FaceGlyphsKey);
+  auto it = m_SizeMap.find(keygen);
   if (it == m_SizeMap.end()) {
-    auto pNew = pdfium::MakeUnique<CPDF_Type3GlyphMap>();
+    auto pNew = std::make_unique<CPDF_Type3GlyphMap>();
     pSizeCache = pNew.get();
-    m_SizeMap[FaceGlyphsKey] = std::move(pNew);
+    m_SizeMap[keygen] = std::move(pNew);
   } else {
     pSizeCache = it->second.get();
   }
@@ -110,7 +99,7 @@
     return pExisting;
 
   std::unique_ptr<CFX_GlyphBitmap> pNewBitmap =
-      RenderGlyph(pSizeCache, charcode, pMatrix);
+      RenderGlyph(pSizeCache, charcode, mtMatrix);
   CFX_GlyphBitmap* pGlyphBitmap = pNewBitmap.get();
   pSizeCache->SetBitmap(charcode, std::move(pNewBitmap));
   return pGlyphBitmap;
@@ -119,22 +108,25 @@
 std::unique_ptr<CFX_GlyphBitmap> CPDF_Type3Cache::RenderGlyph(
     CPDF_Type3GlyphMap* pSize,
     uint32_t charcode,
-    const CFX_Matrix* pMatrix) {
-  const CPDF_Type3Char* pChar = m_pFont->LoadChar(charcode);
-  if (!pChar || !pChar->GetBitmap())
+    const CFX_Matrix& mtMatrix) {
+  CPDF_Type3Char* pChar = m_pFont->LoadChar(charcode);
+  if (!pChar)
     return nullptr;
 
-  CFX_Matrix text_matrix(pMatrix->a, pMatrix->b, pMatrix->c, pMatrix->d, 0, 0);
+  RetainPtr<CFX_DIBitmap> pBitmap = pChar->GetBitmap();
+  if (!pBitmap)
+    return nullptr;
+
+  CFX_Matrix text_matrix(mtMatrix.a, mtMatrix.b, mtMatrix.c, mtMatrix.d, 0, 0);
   CFX_Matrix image_matrix = pChar->matrix() * text_matrix;
 
-  RetainPtr<CFX_DIBitmap> pBitmap = pChar->GetBitmap();
   RetainPtr<CFX_DIBitmap> pResBitmap;
   int left = 0;
   int top = 0;
   if (fabs(image_matrix.b) < fabs(image_matrix.a) / 100 &&
       fabs(image_matrix.c) < fabs(image_matrix.d) / 100) {
-    int top_line = DetectFirstLastScan(pBitmap, true);
-    int bottom_line = DetectFirstLastScan(pBitmap, false);
+    int top_line = DetectFirstScan(pBitmap);
+    int bottom_line = DetectLastScan(pBitmap);
     if (top_line == 0 && bottom_line == pBitmap->GetHeight() - 1) {
       float top_y = image_matrix.d + image_matrix.f;
       float bottom_y = image_matrix.f;
@@ -162,7 +154,7 @@
   if (!pResBitmap)
     return nullptr;
 
-  auto pGlyph = pdfium::MakeUnique<CFX_GlyphBitmap>(left, -top);
+  auto pGlyph = std::make_unique<CFX_GlyphBitmap>(left, -top);
   pGlyph->GetBitmap()->TakeOver(std::move(pResBitmap));
   return pGlyph;
 }
diff --git a/core/fpdfapi/render/cpdf_type3cache.h b/core/fpdfapi/render/cpdf_type3cache.h
index 4371a01..e154a72 100644
--- a/core/fpdfapi/render/cpdf_type3cache.h
+++ b/core/fpdfapi/render/cpdf_type3cache.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,13 @@
 #ifndef CORE_FPDFAPI_RENDER_CPDF_TYPE3CACHE_H_
 #define CORE_FPDFAPI_RENDER_CPDF_TYPE3CACHE_H_
 
+#include <stdint.h>
+
 #include <map>
 #include <memory>
+#include <tuple>
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
 
@@ -22,22 +24,23 @@
 
 class CPDF_Type3Cache final : public Retainable, public Observable {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   const CFX_GlyphBitmap* LoadGlyph(uint32_t charcode,
-                                   const CFX_Matrix* pMatrix);
+                                   const CFX_Matrix& mtMatrix);
 
  private:
+  using SizeKey = std::tuple<int, int, int, int>;
+
   explicit CPDF_Type3Cache(CPDF_Type3Font* pFont);
   ~CPDF_Type3Cache() override;
 
   std::unique_ptr<CFX_GlyphBitmap> RenderGlyph(CPDF_Type3GlyphMap* pSize,
                                                uint32_t charcode,
-                                               const CFX_Matrix* pMatrix);
+                                               const CFX_Matrix& mtMatrix);
 
   RetainPtr<CPDF_Type3Font> const m_pFont;
-  std::map<ByteString, std::unique_ptr<CPDF_Type3GlyphMap>> m_SizeMap;
+  std::map<SizeKey, std::unique_ptr<CPDF_Type3GlyphMap>> m_SizeMap;
 };
 
 #endif  // CORE_FPDFAPI_RENDER_CPDF_TYPE3CACHE_H_
diff --git a/core/fpdfapi/render/cpdf_type3glyphmap.cpp b/core/fpdfapi/render/cpdf_type3glyphmap.cpp
index b144991..10b2b4a 100644
--- a/core/fpdfapi/render/cpdf_type3glyphmap.cpp
+++ b/core/fpdfapi/render/cpdf_type3glyphmap.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,12 @@
 
 #include "core/fpdfapi/render/cpdf_type3glyphmap.h"
 
+#include <math.h>
+
 #include <algorithm>
-#include <map>
 #include <utility>
 
+#include "core/fxcrt/fx_system.h"
 #include "core/fxge/cfx_glyphbitmap.h"
 #include "core/fxge/fx_font.h"
 
@@ -39,7 +41,7 @@
 
 CPDF_Type3GlyphMap::CPDF_Type3GlyphMap() {}
 
-CPDF_Type3GlyphMap::~CPDF_Type3GlyphMap() {}
+CPDF_Type3GlyphMap::~CPDF_Type3GlyphMap() = default;
 
 std::pair<int, int> CPDF_Type3GlyphMap::AdjustBlue(float top, float bottom) {
   return std::make_pair(AdjustBlueHelper(top, &m_TopBlue),
diff --git a/core/fpdfapi/render/cpdf_type3glyphmap.h b/core/fpdfapi/render/cpdf_type3glyphmap.h
index fced0ee..4965732 100644
--- a/core/fpdfapi/render/cpdf_type3glyphmap.h
+++ b/core/fpdfapi/render/cpdf_type3glyphmap.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #ifndef CORE_FPDFAPI_RENDER_CPDF_TYPE3GLYPHMAP_H_
 #define CORE_FPDFAPI_RENDER_CPDF_TYPE3GLYPHMAP_H_
 
+#include <stdint.h>
+
 #include <map>
 #include <memory>
 #include <utility>
 #include <vector>
 
-#include "core/fxcrt/fx_system.h"
-
 class CFX_GlyphBitmap;
 
 class CPDF_Type3GlyphMap {
diff --git a/core/fpdfapi/render/cpdf_windowsrenderdevice.cpp b/core/fpdfapi/render/cpdf_windowsrenderdevice.cpp
index 8e1f212..78410dd 100644
--- a/core/fpdfapi/render/cpdf_windowsrenderdevice.cpp
+++ b/core/fpdfapi/render/cpdf_windowsrenderdevice.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include "core/fpdfapi/render/cpdf_windowsrenderdevice.h"
 
+#include <sstream>
+
 #include "core/fxcodec/basic/basicmodule.h"
 #include "core/fxcodec/fax/faxmodule.h"
 #include "core/fxcodec/flate/flatemodule.h"
@@ -20,7 +22,9 @@
 
 }  // namespace
 
-CPDF_WindowsRenderDevice::CPDF_WindowsRenderDevice(HDC hDC)
-    : CFX_WindowsRenderDevice(hDC, &kEncoderIface) {}
+CPDF_WindowsRenderDevice::CPDF_WindowsRenderDevice(
+    HDC hDC,
+    CFX_PSFontTracker* ps_font_tracker)
+    : CFX_WindowsRenderDevice(hDC, ps_font_tracker, &kEncoderIface) {}
 
 CPDF_WindowsRenderDevice::~CPDF_WindowsRenderDevice() = default;
diff --git a/core/fpdfapi/render/cpdf_windowsrenderdevice.h b/core/fpdfapi/render/cpdf_windowsrenderdevice.h
index 51d150e..c83c588 100644
--- a/core/fpdfapi/render/cpdf_windowsrenderdevice.h
+++ b/core/fpdfapi/render/cpdf_windowsrenderdevice.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,9 +9,11 @@
 
 #include "core/fxge/cfx_windowsrenderdevice.h"
 
+class CFX_PSFontTracker;
+
 class CPDF_WindowsRenderDevice final : public CFX_WindowsRenderDevice {
  public:
-  explicit CPDF_WindowsRenderDevice(HDC hDC);
+  CPDF_WindowsRenderDevice(HDC hDC, CFX_PSFontTracker* ps_font_tracker);
   ~CPDF_WindowsRenderDevice() override;
 };
 
diff --git a/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp b/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp
index 271a251..428c56b 100644
--- a/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp
+++ b/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp
@@ -1,28 +1,84 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <stdint.h>
+
 #include <utility>
 
 #include "build/build_config.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/dib/fx_dib.h"
 #include "public/fpdf_progressive.h"
 #include "testing/embedder_test.h"
+#include "testing/embedder_test_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/check.h"
+
+namespace {
+
+constexpr FX_ARGB kBlack = 0xFF000000;
+constexpr FX_ARGB kBlue = 0xFF0000FF;
+constexpr FX_ARGB kGreen = 0xFF00FF00;
+constexpr FX_ARGB kRed = 0xFFFF0000;
+constexpr FX_ARGB kWhite = 0xFFFFFFFF;
+
+const char* AnnotationStampWithApBaseContentChecksum() {
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    return "4fedc838daa6762cf7eee180986a0f1b";
+  }
+#if BUILDFLAG(IS_APPLE)
+  return "243f3d6267d9db09198fed9f8c4957fd";
+#else
+  return "e31414933c9ff3950773981e5bf61678";
+#endif
+}
+
+}  // namespace
 
 class FPDFProgressiveRenderEmbedderTest : public EmbedderTest {
  public:
+  class FakePause : public IFSDK_PAUSE {
+   public:
+    explicit FakePause(bool should_pause) : should_pause_(should_pause) {
+      IFSDK_PAUSE::version = 1;
+      IFSDK_PAUSE::user = nullptr;
+      IFSDK_PAUSE::NeedToPauseNow = Pause_NeedToPauseNow;
+    }
+    ~FakePause() = default;
+    static FPDF_BOOL Pause_NeedToPauseNow(IFSDK_PAUSE* param) {
+      return static_cast<FakePause*>(param)->should_pause_;
+    }
+
+   private:
+    const bool should_pause_;
+  };
+
   // StartRenderPageWithFlags() with no flags.
   // The call returns true if the rendering is complete.
   bool StartRenderPage(FPDF_PAGE page, IFSDK_PAUSE* pause);
 
-  // Start rendering of |page| into a bitmap with the ability to pause the
+  // Start rendering of |page| into a bitmap with the ability to |pause| the
   // rendering with the specified rendering |flags|.
   // The call returns true if the rendering is complete.
   //
   // See public/fpdfview.h for a list of page rendering flags.
   bool StartRenderPageWithFlags(FPDF_PAGE page, IFSDK_PAUSE* pause, int flags);
 
+  // Start rendering of |page| into a bitmap with the ability to pause the
+  // rendering with the specified rendering |flags| and the specified
+  // |color_scheme|. This also takes in the |background_color| for the bitmap.
+  // The call returns true if the rendering is complete.
+  //
+  // See public/fpdfview.h for the list of page rendering flags and
+  // the list of colors in the scheme.
+  bool StartRenderPageWithColorSchemeAndBackground(
+      FPDF_PAGE page,
+      IFSDK_PAUSE* pause,
+      int flags,
+      const FPDF_COLORSCHEME* color_scheme,
+      uint32_t background_color);
+
   // Continue rendering of |page| into the bitmap created in
   // StartRenderPageWithFlags().
   // The call returns true if the rendering is complete.
@@ -40,6 +96,33 @@
   ScopedFPDFBitmap FinishRenderPageWithForms(FPDF_PAGE page,
                                              FPDF_FORMHANDLE handle);
 
+  // Convert the |page| into a bitmap with a |background_color|, using the
+  // color scheme render API with the specific |flags| and |color_scheme|.
+  // The form handle associated with |page| should be passed in via |handle|.
+  // If |handle| is nullptr, then forms on the page will not be rendered.
+  // This returns the bitmap generated by the progressive render calls.
+  //
+  // See public/fpdfview.h for a list of page rendering flags and
+  // the color scheme that can be applied for rendering.
+  ScopedFPDFBitmap RenderPageWithForcedColorScheme(
+      FPDF_PAGE page,
+      FPDF_FORMHANDLE handle,
+      int flags,
+      const FPDF_COLORSCHEME* color_scheme,
+      FX_ARGB background_color);
+
+ protected:
+  // Utility method to render the |page_num| of the currently loaded Pdf
+  // using RenderPageWithForcedColorScheme() passing in the render options
+  // and expected values for bitmap verification.
+  void VerifyRenderingWithColorScheme(int page_num,
+                                      int flags,
+                                      const FPDF_COLORSCHEME* color_scheme,
+                                      FX_ARGB background_color,
+                                      int bitmap_width,
+                                      int bitmap_height,
+                                      const char* md5);
+
  private:
   // Keeps the bitmap used for progressive rendering alive until
   // FPDF_RenderPage_Close() is called after which the bitmap is returned
@@ -72,9 +155,31 @@
   return rv != FPDF_RENDER_TOBECONTINUED;
 }
 
+bool FPDFProgressiveRenderEmbedderTest::
+    StartRenderPageWithColorSchemeAndBackground(
+        FPDF_PAGE page,
+        IFSDK_PAUSE* pause,
+        int flags,
+        const FPDF_COLORSCHEME* color_scheme,
+        uint32_t background_color) {
+  int width = static_cast<int>(FPDF_GetPageWidth(page));
+  int height = static_cast<int>(FPDF_GetPageHeight(page));
+  progressive_render_flags_ = flags;
+  int alpha = FPDFPage_HasTransparency(page) ? 1 : 0;
+  progressive_render_bitmap_ =
+      ScopedFPDFBitmap(FPDFBitmap_Create(width, height, alpha));
+  DCHECK(progressive_render_bitmap_);
+  FPDFBitmap_FillRect(progressive_render_bitmap_.get(), 0, 0, width, height,
+                      background_color);
+  int rv = FPDF_RenderPageBitmapWithColorScheme_Start(
+      progressive_render_bitmap_.get(), page, 0, 0, width, height, 0,
+      progressive_render_flags_, color_scheme, pause);
+  return rv != FPDF_RENDER_TOBECONTINUED;
+}
+
 bool FPDFProgressiveRenderEmbedderTest::ContinueRenderPage(FPDF_PAGE page,
                                                            IFSDK_PAUSE* pause) {
-  ASSERT(progressive_render_bitmap_);
+  DCHECK(progressive_render_bitmap_);
 
   int rv = FPDF_RenderPage_Continue(page, pause);
   return rv != FPDF_RENDER_TOBECONTINUED;
@@ -88,7 +193,7 @@
 ScopedFPDFBitmap FPDFProgressiveRenderEmbedderTest::FinishRenderPageWithForms(
     FPDF_PAGE page,
     FPDF_FORMHANDLE handle) {
-  ASSERT(progressive_render_bitmap_);
+  DCHECK(progressive_render_bitmap_);
 
   int width = static_cast<int>(FPDF_GetPageWidth(page));
   int height = static_cast<int>(FPDF_GetPageHeight(page));
@@ -98,68 +203,43 @@
   return std::move(progressive_render_bitmap_);
 }
 
-class FakePause : public IFSDK_PAUSE {
- public:
-  explicit FakePause(bool should_pause) : should_pause_(should_pause) {
-    IFSDK_PAUSE::version = 1;
-    IFSDK_PAUSE::user = nullptr;
-    IFSDK_PAUSE::NeedToPauseNow = Pause_NeedToPauseNow;
+ScopedFPDFBitmap
+FPDFProgressiveRenderEmbedderTest::RenderPageWithForcedColorScheme(
+    FPDF_PAGE page,
+    FPDF_FORMHANDLE handle,
+    int flags,
+    const FPDF_COLORSCHEME* color_scheme,
+    FX_ARGB background_color) {
+  FakePause pause(true);
+  bool render_done = StartRenderPageWithColorSchemeAndBackground(
+                         page, &pause, flags, color_scheme, background_color) ==
+                     FPDF_RENDER_TOBECONTINUED;
+  EXPECT_FALSE(render_done);
+
+  while (!render_done) {
+    render_done = ContinueRenderPage(page, &pause);
   }
-  ~FakePause() = default;
+  return FinishRenderPageWithForms(page, form_handle());
+}
 
-  static FPDF_BOOL Pause_NeedToPauseNow(IFSDK_PAUSE* param) {
-    return static_cast<FakePause*>(param)->should_pause_;
-  }
-
- private:
-  const bool should_pause_ = false;
-};
-
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_RenderWithoutPause DISABLED_RenderWithoutPause
-#else
-#define MAYBE_RenderWithoutPause RenderWithoutPause
-#endif
-TEST_F(FPDFProgressiveRenderEmbedderTest, MAYBE_RenderWithoutPause) {
-#if defined(OS_WIN)
-  static constexpr char kMd5BaseContent[] = "649d6792ea50faf98c013c2d81710595";
-#elif defined(OS_MACOSX)
-  static constexpr char kMd5BaseContent[] = "5f933aac2a74434be1b4d0bdb5334f0b";
-#else
-  static constexpr char kMd5BaseContent[] = "a24edc7740f1d6f76899652dcf825dea";
-#endif
-
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderWithoutPause) {
   // Test rendering of page content using progressive render APIs
   // without pausing the rendering.
-  EXPECT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   FakePause pause(false);
   EXPECT_TRUE(StartRenderPage(page, &pause));
   ScopedFPDFBitmap bitmap = FinishRenderPage(page);
-  CompareBitmap(bitmap.get(), 595, 842, kMd5BaseContent);
+  CompareBitmap(bitmap.get(), 595, 842,
+                AnnotationStampWithApBaseContentChecksum());
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_RenderWithPause DISABLED_RenderWithPause
-#else
-#define MAYBE_RenderWithPause RenderWithPause
-#endif
-TEST_F(FPDFProgressiveRenderEmbedderTest, MAYBE_RenderWithPause) {
-#if defined(OS_WIN)
-  static constexpr char kMd5BaseContent[] = "649d6792ea50faf98c013c2d81710595";
-#elif defined(OS_MACOSX)
-  static constexpr char kMd5BaseContent[] = "5f933aac2a74434be1b4d0bdb5334f0b";
-#else
-  static constexpr char kMd5BaseContent[] = "a24edc7740f1d6f76899652dcf825dea";
-#endif
-
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderWithPause) {
   // Test rendering of page content using progressive render APIs
   // with pause in rendering.
-  EXPECT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   FakePause pause(true);
@@ -170,31 +250,15 @@
     render_done = ContinueRenderPage(page, &pause);
   }
   ScopedFPDFBitmap bitmap = FinishRenderPage(page);
-  CompareBitmap(bitmap.get(), 595, 842, kMd5BaseContent);
+  CompareBitmap(bitmap.get(), 595, 842,
+                AnnotationStampWithApBaseContentChecksum());
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_RenderAnnotWithPause DISABLED_RenderAnnotWithPause
-#else
-#define MAYBE_RenderAnnotWithPause RenderAnnotWithPause
-#endif
-TEST_F(FPDFProgressiveRenderEmbedderTest, MAYBE_RenderAnnotWithPause) {
-#if defined(OS_WIN)
-  static constexpr char kMd5ContentWithAnnot[] =
-      "6aa001a77ec05d0f1b0d1d22e28744d4";
-#elif defined(OS_MACOSX)
-  static constexpr char kMd5ContentWithAnnot[] =
-      "c35408717759562d1f8bf33d317483d2";
-#else
-  static constexpr char kMd5ContentWithAnnot[] =
-      "b42cef463483e668eaf4055a65e4f1f5";
-#endif
-
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderAnnotWithPause) {
   // Test rendering of the page with annotations using progressive render APIs
   // with pause in rendering.
-  EXPECT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   FakePause pause(true);
@@ -205,31 +269,15 @@
     render_done = ContinueRenderPage(page, &pause);
   }
   ScopedFPDFBitmap bitmap = FinishRenderPage(page);
-  CompareBitmap(bitmap.get(), 595, 842, kMd5ContentWithAnnot);
+  CompareBitmap(bitmap.get(), 595, 842,
+                pdfium::AnnotationStampWithApChecksum());
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_RenderFormsWithPause DISABLED_RenderFormsWithPause
-#else
-#define MAYBE_RenderFormsWithPause RenderFormsWithPause
-#endif
-TEST_F(FPDFProgressiveRenderEmbedderTest, MAYBE_RenderFormsWithPause) {
-#if defined(OS_WIN)
-  static constexpr char kMd5ContentWithForms[] =
-      "d3204faa62b607f0bd3893c9c22cabcb";
-#elif defined(OS_MACOSX)
-  static constexpr char kMd5ContentWithForms[] =
-      "5f11dbe575fe197a37c3fb422559f8ff";
-#else
-  static constexpr char kMd5ContentWithForms[] =
-      "b890950d4b9bc163b1a96797f3004b53";
-#endif
-
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderFormsWithPause) {
   // Test rendering of the page with forms using progressive render APIs
   // with pause in rendering.
-  EXPECT_TRUE(OpenDocument("text_form.pdf"));
+  ASSERT_TRUE(OpenDocument("text_form.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   FakePause pause(true);
@@ -239,7 +287,189 @@
   while (!render_done) {
     render_done = ContinueRenderPage(page, &pause);
   }
-  ScopedFPDFBitmap bitmap = FinishRenderPageWithForms(page, form_handle_);
-  CompareBitmap(bitmap.get(), 300, 300, kMd5ContentWithForms);
+  ScopedFPDFBitmap bitmap = FinishRenderPageWithForms(page, form_handle());
+  CompareBitmap(bitmap.get(), 300, 300, pdfium::TextFormChecksum());
   UnloadPage(page);
 }
+
+void FPDFProgressiveRenderEmbedderTest::VerifyRenderingWithColorScheme(
+    int page_num,
+    int flags,
+    const FPDF_COLORSCHEME* color_scheme,
+    FX_ARGB background_color,
+    int bitmap_width,
+    int bitmap_height,
+    const char* md5) {
+  ASSERT_TRUE(document());
+
+  FPDF_PAGE page = LoadPage(page_num);
+  ASSERT_TRUE(page);
+
+  ScopedFPDFBitmap bitmap = RenderPageWithForcedColorScheme(
+      page, form_handle(), flags, color_scheme, background_color);
+  ASSERT_TRUE(bitmap);
+  CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, md5);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderTextWithColorScheme) {
+  // Test rendering of text with forced color scheme on.
+  const char* content_with_text_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "edd919ec8b59fab1f16b5f2adb1175f3";
+#if BUILDFLAG(IS_APPLE)
+    return "ee4ec12f54ce8d117a73bd9b85a8954d";
+#else
+    return "704db63ed2bf77254ecaa8035b85f21a";
+#endif  // BUILDFLAG(IS_APPLE)
+  }();
+
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+
+  FPDF_COLORSCHEME color_scheme{kBlack, kWhite, kWhite, kWhite};
+  VerifyRenderingWithColorScheme(/*page_num=*/0, /*flags=*/0, &color_scheme,
+                                 kBlack, 200, 200, content_with_text_checksum);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderPathWithColorScheme) {
+  // Test rendering of paths with forced color scheme on.
+  const char* rectangles_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "4b0f850a94698d07b6cd2814d1b4ccb7";
+    return "249f59b0d066c4f6bd89782a80822219";
+  }();
+
+  ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+
+  FPDF_COLORSCHEME color_scheme{kWhite, kRed, kBlue, kBlue};
+  VerifyRenderingWithColorScheme(/*page_num=*/0, /*flags=*/0, &color_scheme,
+                                 kBlack, 200, 300, rectangles_checksum);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest,
+       RenderPathWithColorSchemeAndConvertFillToStroke) {
+  // Test rendering of paths with forced color scheme on and conversion from
+  // fill to stroke enabled. The fill paths should be rendered as stroke.
+  const char* rectangles_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "c1cbbd2ce6921f608a3c55140592419b";
+    return "0ebcc11e617635eca1fa9ce475383a80";
+  }();
+
+  ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+
+  FPDF_COLORSCHEME color_scheme{kWhite, kRed, kBlue, kBlue};
+  VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_CONVERT_FILL_TO_STROKE,
+                                 &color_scheme, kBlack, 200, 300,
+                                 rectangles_checksum);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderHighlightWithColorScheme) {
+  // Test rendering of highlight with forced color scheme on.
+  //
+  // Note: The fill color rendered for highlight is different from the normal
+  // path since highlights have Multiply blend mode, while the other path has
+  // Normal blend mode.
+  const char* content_with_highlight_fill_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "49dcfcfdc38d200bb3d57a2ca3086034";
+#if BUILDFLAG(IS_APPLE)
+    return "a820afec9b99d3d3f2e9e9382bbad7c1";
+#else
+    return "a08a0639f89446f66f3689ee8e08b9fe";
+#endif  // BUILDFLAG(IS_APPLE)
+  }();
+
+  ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
+
+  FPDF_COLORSCHEME color_scheme{kRed, kGreen, kWhite, kWhite};
+  VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT, &color_scheme,
+                                 kBlue, 612, 792,
+                                 content_with_highlight_fill_checksum);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest,
+       RenderHighlightWithColorSchemeAndConvertFillToStroke) {
+  // Test rendering of highlight with forced color and converting fill to
+  // stroke. The highlight should be rendered as a stroke of the rect.
+  //
+  // Note: The stroke color rendered for highlight is different from the normal
+  // path since highlights have Multiply blend mode, while the other path has
+  // Normal blend mode.
+
+  const char* md5_content_with_highlight = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "c609e8810fba2f12db8f8a2b043d97bd";
+#if BUILDFLAG(IS_APPLE)
+    return "8837bea0b3520164b1784e513c882a2d";
+#else
+    return "3dd8c02f5c06bac85e0d2c8bf37d1dc4";
+#endif  // BUILDFLAG(IS_APPLE)
+  }();
+
+  ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
+
+  FPDF_COLORSCHEME color_scheme{kRed, kGreen, kWhite, kWhite};
+  VerifyRenderingWithColorScheme(
+      /*page_num=*/0, FPDF_ANNOT | FPDF_CONVERT_FILL_TO_STROKE, &color_scheme,
+      kBlue, 612, 792, md5_content_with_highlight);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderInkWithColorScheme) {
+  // Test rendering of multiple ink with forced color scheme on.
+  const char* content_with_ink_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+#if BUILDFLAG(IS_APPLE)
+      return "5108aa537b6ecc37b3f0a35b76c1b379";
+#else
+      return "b39d9f68ff71963d82c43eb20caa8f4d";
+#endif
+    }
+    return "797bce7dc6c50ee86b095405df9fe5aa";
+  }();
+
+  ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
+
+  FPDF_COLORSCHEME color_scheme{kBlack, kGreen, kRed, kRed};
+  VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT, &color_scheme,
+                                 kBlack, 612, 792, content_with_ink_checksum);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderStampWithColorScheme) {
+  // Test rendering of static annotation with forced color scheme on.
+  const char* content_with_stamp_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "6e028012a4854ebfd9ee92da862bf679";
+#if BUILDFLAG(IS_APPLE)
+    return "8170c539e95f22f14eb8f266a5f1bbed";
+#else
+    return "d1fd087e59d4dcebf47b56570bdb8c22";
+#endif
+  }();
+
+  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+
+  FPDF_COLORSCHEME color_scheme{kBlue, kGreen, kRed, kRed};
+  VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT, &color_scheme,
+                                 kWhite, 595, 842, content_with_stamp_checksum);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderFormWithColorScheme) {
+  // Test rendering of form does not change with forced color scheme on.
+  const char* content_with_form_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "9f75d98afc6d6313bd87e6562ea6df15";
+    return "080f7a4381606659301440e1b14dca35";
+  }();
+
+  ASSERT_TRUE(OpenDocument("annotiter.pdf"));
+
+  FPDF_COLORSCHEME color_scheme{kGreen, kGreen, kRed, kRed};
+  VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT, &color_scheme,
+                                 kWhite, 612, 792, content_with_form_checksum);
+
+  // Verify that the MD5 hash matches when rendered without |color_scheme|.
+  VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT,
+                                 /*color_scheme=*/nullptr, kWhite, 612, 792,
+                                 content_with_form_checksum);
+}
diff --git a/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp b/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp
index 6de721c..f3b6cae 100644
--- a/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp
+++ b/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp
@@ -1,21 +1,20 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
-
 #include "public/cpp/fpdf_scopers.h"
 #include "testing/embedder_test.h"
+#include "testing/embedder_test_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 class FPDFRenderPatternEmbedderTest : public EmbedderTest {};
 
 TEST_F(FPDFRenderPatternEmbedderTest, LoadError_547706) {
   // Test shading where object is a dictionary instead of a stream.
-  EXPECT_TRUE(OpenDocument("bug_547706.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_547706.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-  CompareBitmap(bitmap.get(), 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3");
+  CompareBitmap(bitmap.get(), 612, 792, pdfium::kBlankPage612By792Checksum);
   UnloadPage(page);
 }
diff --git a/core/fpdfdoc/Android.bp b/core/fpdfdoc/Android.bp
index 6931dc5..00321a4 100644
--- a/core/fpdfdoc/Android.bp
+++ b/core/fpdfdoc/Android.bp
@@ -13,11 +13,8 @@
 
     visibility: ["//external/pdfium:__subpackages__"],
 
-    header_libs: [
-        "libpdfium-constants",
-    ],
-
     static_libs: [
+        "libpdfium-constants",
         "libpdfium-font",
         "libpdfium-page",
         "libpdfium-parser",
diff --git a/core/fpdfdoc/BUILD.gn b/core/fpdfdoc/BUILD.gn
index f25ace9..5e11adb 100644
--- a/core/fpdfdoc/BUILD.gn
+++ b/core/fpdfdoc/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -7,10 +7,6 @@
 
 source_set("fpdfdoc") {
   sources = [
-    "cba_fontmap.cpp",
-    "cba_fontmap.h",
-    "cline.cpp",
-    "cline.h",
     "cpdf_aaction.cpp",
     "cpdf_aaction.h",
     "cpdf_action.cpp",
@@ -21,6 +17,8 @@
     "cpdf_annotlist.h",
     "cpdf_apsettings.cpp",
     "cpdf_apsettings.h",
+    "cpdf_bafontmap.cpp",
+    "cpdf_bafontmap.h",
     "cpdf_bookmark.cpp",
     "cpdf_bookmark.h",
     "cpdf_bookmarktree.cpp",
@@ -37,6 +35,8 @@
     "cpdf_formcontrol.h",
     "cpdf_formfield.cpp",
     "cpdf_formfield.h",
+    "cpdf_generateap.cpp",
+    "cpdf_generateap.h",
     "cpdf_icon.cpp",
     "cpdf_icon.h",
     "cpdf_iconfit.cpp",
@@ -59,29 +59,28 @@
     "cpdf_structelement.h",
     "cpdf_structtree.cpp",
     "cpdf_structtree.h",
-    "cpdf_variabletext.cpp",
-    "cpdf_variabletext.h",
     "cpdf_viewerpreferences.cpp",
     "cpdf_viewerpreferences.h",
     "cpvt_floatrect.h",
     "cpvt_fontmap.cpp",
     "cpvt_fontmap.h",
-    "cpvt_generateap.cpp",
-    "cpvt_generateap.h",
     "cpvt_line.h",
     "cpvt_lineinfo.h",
+    "cpvt_section.cpp",
+    "cpvt_section.h",
+    "cpvt_variabletext.cpp",
+    "cpvt_variabletext.h",
     "cpvt_word.h",
     "cpvt_wordinfo.cpp",
     "cpvt_wordinfo.h",
     "cpvt_wordplace.h",
     "cpvt_wordrange.h",
-    "csection.cpp",
-    "csection.h",
-    "ctypeset.cpp",
-    "ctypeset.h",
     "ipvt_fontmap.h",
   ]
-  configs += [ "../../:pdfium_core_config" ]
+  configs += [
+    "../../:pdfium_strict_config",
+    "../../:pdfium_noshorten_config",
+  ]
   deps = [
     "../../constants",
     "../fpdfapi/font",
@@ -96,7 +95,10 @@
 
 pdfium_unittest_source_set("unittests") {
   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",
     "cpdf_filespec_unittest.cpp",
@@ -106,7 +108,13 @@
   ]
   deps = [
     ":fpdfdoc",
+    "../../constants",
+    "../fpdfapi/font",
+    "../fpdfapi/page",
+    "../fpdfapi/page:unit_test_support",
     "../fpdfapi/parser",
+    "../fpdfapi/parser:unit_test_support",
+    "../fpdfapi/render",
   ]
   pdfium_root_dir = "../../"
 }
diff --git a/core/fpdfdoc/cba_fontmap.cpp b/core/fpdfdoc/cba_fontmap.cpp
deleted file mode 100644
index c71c724..0000000
--- a/core/fpdfdoc/cba_fontmap.cpp
+++ /dev/null
@@ -1,493 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fpdfdoc/cba_fontmap.h"
-
-#include <memory>
-#include <utility>
-
-#include "constants/annotation_common.h"
-#include "core/fpdfapi/font/cpdf_font.h"
-#include "core/fpdfapi/font/cpdf_fontencoding.h"
-#include "core/fpdfapi/page/cpdf_docpagedata.h"
-#include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/parser/cpdf_parser.h"
-#include "core/fpdfapi/parser/cpdf_reference.h"
-#include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fpdfapi/parser/fpdf_parser_utility.h"
-#include "core/fpdfdoc/cpdf_defaultappearance.h"
-#include "core/fpdfdoc/cpdf_formfield.h"
-#include "core/fpdfdoc/ipvt_fontmap.h"
-#include "core/fxcrt/fx_codepage.h"
-#include "core/fxge/cfx_fontmapper.h"
-#include "core/fxge/cfx_fontmgr.h"
-#include "core/fxge/cfx_gemodule.h"
-#include "core/fxge/cfx_substfont.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-namespace {
-
-bool FindNativeTrueTypeFont(ByteString sFontFaceName) {
-  CFX_FontMgr* pFontMgr = CFX_GEModule::Get()->GetFontMgr();
-  if (!pFontMgr)
-    return false;
-
-  CFX_FontMapper* pFontMapper = pFontMgr->GetBuiltinMapper();
-  pFontMapper->LoadInstalledFonts();
-
-  for (const auto& font : pFontMapper->m_InstalledTTFonts) {
-    if (font.Compare(sFontFaceName.AsStringView()))
-      return true;
-  }
-  for (const auto& fontPair : pFontMapper->m_LocalizedTTFonts) {
-    if (fontPair.first.Compare(sFontFaceName.AsStringView()))
-      return true;
-  }
-  return false;
-}
-
-RetainPtr<CPDF_Font> AddNativeTrueTypeFontToPDF(CPDF_Document* pDoc,
-                                                ByteString sFontFaceName,
-                                                uint8_t nCharset) {
-  if (!pDoc)
-    return nullptr;
-
-  auto pFXFont = pdfium::MakeUnique<CFX_Font>();
-  pFXFont->LoadSubst(sFontFaceName, true, 0, 0, 0,
-                     FX_GetCodePageFromCharset(nCharset), false);
-
-  auto* pDocPageData = CPDF_DocPageData::FromDocument(pDoc);
-  return pDocPageData->AddFont(std::move(pFXFont), nCharset);
-}
-
-}  // namespace
-
-CBA_FontMap::Data::Data() = default;
-
-CBA_FontMap::Data::~Data() = default;
-
-CBA_FontMap::CBA_FontMap(CPDF_Document* pDocument, CPDF_Dictionary* pAnnotDict)
-    : m_pDocument(pDocument), m_pAnnotDict(pAnnotDict) {
-  Initialize();
-}
-
-CBA_FontMap::~CBA_FontMap() {
-  Clear();
-}
-
-RetainPtr<CPDF_Font> CBA_FontMap::GetPDFFont(int32_t nFontIndex) {
-  if (pdfium::IndexInBounds(m_Data, nFontIndex))
-    return m_Data[nFontIndex]->pFont;
-  return nullptr;
-}
-
-ByteString CBA_FontMap::GetPDFFontAlias(int32_t nFontIndex) {
-  if (pdfium::IndexInBounds(m_Data, nFontIndex))
-    return m_Data[nFontIndex]->sFontName;
-  return ByteString();
-}
-
-int32_t CBA_FontMap::GetWordFontIndex(uint16_t word,
-                                      int32_t nCharset,
-                                      int32_t nFontIndex) {
-  if (nFontIndex > 0) {
-    if (KnowWord(nFontIndex, word))
-      return nFontIndex;
-  } else {
-    if (!m_Data.empty()) {
-      const Data* pData = m_Data.front().get();
-      if (nCharset == FX_CHARSET_Default ||
-          pData->nCharset == FX_CHARSET_Symbol || nCharset == pData->nCharset) {
-        if (KnowWord(0, word))
-          return 0;
-      }
-    }
-  }
-
-  int32_t nNewFontIndex =
-      GetFontIndex(GetCachedNativeFontName(nCharset), nCharset, true);
-  if (nNewFontIndex >= 0) {
-    if (KnowWord(nNewFontIndex, word))
-      return nNewFontIndex;
-  }
-  nNewFontIndex = GetFontIndex(CFX_Font::kUniversalDefaultFontName,
-                               FX_CHARSET_Default, false);
-  if (nNewFontIndex >= 0) {
-    if (KnowWord(nNewFontIndex, word))
-      return nNewFontIndex;
-  }
-  return -1;
-}
-
-int32_t CBA_FontMap::CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) {
-  if (!pdfium::IndexInBounds(m_Data, nFontIndex))
-    return -1;
-
-  Data* pData = m_Data[nFontIndex].get();
-  if (!pData->pFont)
-    return -1;
-
-  if (pData->pFont->IsUnicodeCompatible())
-    return pData->pFont->CharCodeFromUnicode(word);
-
-  return word < 0xFF ? word : -1;
-}
-
-int32_t CBA_FontMap::CharSetFromUnicode(uint16_t word, int32_t nOldCharset) {
-  // to avoid CJK Font to show ASCII
-  if (word < 0x7F)
-    return FX_CHARSET_ANSI;
-
-  // follow the old charset
-  if (nOldCharset != FX_CHARSET_Default)
-    return nOldCharset;
-
-  return CFX_Font::GetCharSetFromUnicode(word);
-}
-
-int32_t CBA_FontMap::GetNativeCharset() {
-  return FX_GetCharsetFromCodePage(FXSYS_GetACP());
-}
-
-void CBA_FontMap::Reset() {
-  Clear();
-  m_pDefaultFont = nullptr;
-  m_sDefaultFontName.clear();
-}
-
-void CBA_FontMap::SetAPType(const ByteString& sAPType) {
-  m_sAPType = sAPType;
-
-  Reset();
-  Initialize();
-}
-
-void CBA_FontMap::Initialize() {
-  int32_t nCharset = FX_CHARSET_Default;
-
-  if (!m_pDefaultFont) {
-    m_pDefaultFont = GetAnnotDefaultFont(&m_sDefaultFontName);
-    if (m_pDefaultFont) {
-      if (const CFX_SubstFont* pSubstFont = m_pDefaultFont->GetSubstFont()) {
-        nCharset = pSubstFont->m_Charset;
-      } else {
-        if (m_sDefaultFontName == "Wingdings" ||
-            m_sDefaultFontName == "Wingdings2" ||
-            m_sDefaultFontName == "Wingdings3" ||
-            m_sDefaultFontName == "Webdings")
-          nCharset = FX_CHARSET_Symbol;
-        else
-          nCharset = FX_CHARSET_ANSI;
-      }
-      AddFontData(m_pDefaultFont, m_sDefaultFontName, nCharset);
-      AddFontToAnnotDict(m_pDefaultFont, m_sDefaultFontName);
-    }
-  }
-
-  if (nCharset != FX_CHARSET_ANSI)
-    GetFontIndex(CFX_Font::kDefaultAnsiFontName, FX_CHARSET_ANSI, false);
-}
-
-RetainPtr<CPDF_Font> CBA_FontMap::FindFontSameCharset(ByteString* sFontAlias,
-                                                      int32_t nCharset) {
-  if (m_pAnnotDict->GetStringFor(pdfium::annotation::kSubtype) != "Widget")
-    return nullptr;
-
-  const CPDF_Dictionary* pRootDict = m_pDocument->GetRoot();
-  if (!pRootDict)
-    return nullptr;
-
-  const CPDF_Dictionary* pAcroFormDict = pRootDict->GetDictFor("AcroForm");
-  if (!pAcroFormDict)
-    return nullptr;
-
-  const CPDF_Dictionary* pDRDict = pAcroFormDict->GetDictFor("DR");
-  if (!pDRDict)
-    return nullptr;
-
-  return FindResFontSameCharset(pDRDict, sFontAlias, nCharset);
-}
-
-RetainPtr<CPDF_Font> CBA_FontMap::FindResFontSameCharset(
-    const CPDF_Dictionary* pResDict,
-    ByteString* sFontAlias,
-    int32_t nCharset) {
-  if (!pResDict)
-    return nullptr;
-
-  const CPDF_Dictionary* pFonts = pResDict->GetDictFor("Font");
-  if (!pFonts)
-    return nullptr;
-
-  RetainPtr<CPDF_Font> pFind;
-  CPDF_DictionaryLocker locker(pFonts);
-  for (const auto& it : locker) {
-    const ByteString& csKey = it.first;
-    if (!it.second)
-      continue;
-
-    CPDF_Dictionary* pElement = ToDictionary(it.second->GetDirect());
-    if (!pElement || pElement->GetStringFor("Type") != "Font")
-      continue;
-
-    auto* pData = CPDF_DocPageData::FromDocument(m_pDocument.Get());
-    RetainPtr<CPDF_Font> pFont = pData->GetFont(pElement);
-    if (!pFont)
-      continue;
-
-    const CFX_SubstFont* pSubst = pFont->GetSubstFont();
-    if (!pSubst)
-      continue;
-
-    if (pSubst->m_Charset == nCharset) {
-      *sFontAlias = csKey;
-      pFind = std::move(pFont);
-    }
-  }
-  return pFind;
-}
-
-RetainPtr<CPDF_Font> CBA_FontMap::GetAnnotDefaultFont(ByteString* sAlias) {
-  CPDF_Dictionary* pAcroFormDict = nullptr;
-  const bool bWidget =
-      (m_pAnnotDict->GetStringFor(pdfium::annotation::kSubtype) == "Widget");
-  if (bWidget) {
-    CPDF_Dictionary* pRootDict = m_pDocument->GetRoot();
-    if (pRootDict)
-      pAcroFormDict = pRootDict->GetDictFor("AcroForm");
-  }
-
-  ByteString sDA;
-  const CPDF_Object* pObj =
-      CPDF_FormField::GetFieldAttr(m_pAnnotDict.Get(), "DA");
-  if (pObj)
-    sDA = pObj->GetString();
-
-  if (bWidget) {
-    if (sDA.IsEmpty()) {
-      pObj = CPDF_FormField::GetFieldAttr(pAcroFormDict, "DA");
-      sDA = pObj ? pObj->GetString() : ByteString();
-    }
-  }
-  if (sDA.IsEmpty())
-    return nullptr;
-
-  CPDF_DefaultAppearance appearance(sDA);
-  float font_size;
-  Optional<ByteString> font = appearance.GetFont(&font_size);
-  *sAlias = font.value_or(ByteString());
-
-  CPDF_Dictionary* pFontDict = nullptr;
-  if (CPDF_Dictionary* pAPDict =
-          m_pAnnotDict->GetDictFor(pdfium::annotation::kAP)) {
-    if (CPDF_Dictionary* pNormalDict = pAPDict->GetDictFor("N")) {
-      if (CPDF_Dictionary* pNormalResDict =
-              pNormalDict->GetDictFor("Resources")) {
-        if (CPDF_Dictionary* pResFontDict = pNormalResDict->GetDictFor("Font"))
-          pFontDict = pResFontDict->GetDictFor(*sAlias);
-      }
-    }
-  }
-  if (bWidget && !pFontDict && pAcroFormDict) {
-    if (CPDF_Dictionary* pDRDict = pAcroFormDict->GetDictFor("DR")) {
-      if (CPDF_Dictionary* pDRFontDict = pDRDict->GetDictFor("Font"))
-        pFontDict = pDRFontDict->GetDictFor(*sAlias);
-    }
-  }
-  if (!pFontDict)
-    return nullptr;
-
-  return CPDF_DocPageData::FromDocument(m_pDocument.Get())->GetFont(pFontDict);
-}
-
-void CBA_FontMap::AddFontToAnnotDict(const RetainPtr<CPDF_Font>& pFont,
-                                     const ByteString& sAlias) {
-  if (!pFont)
-    return;
-
-  CPDF_Dictionary* pAPDict = m_pAnnotDict->GetDictFor(pdfium::annotation::kAP);
-  if (!pAPDict)
-    pAPDict = m_pAnnotDict->SetNewFor<CPDF_Dictionary>(pdfium::annotation::kAP);
-
-  // to avoid checkbox and radiobutton
-  if (ToDictionary(pAPDict->GetObjectFor(m_sAPType)))
-    return;
-
-  CPDF_Stream* pStream = pAPDict->GetStreamFor(m_sAPType);
-  if (!pStream) {
-    pStream = m_pDocument->NewIndirect<CPDF_Stream>();
-    pAPDict->SetNewFor<CPDF_Reference>(m_sAPType, m_pDocument.Get(),
-                                       pStream->GetObjNum());
-  }
-
-  CPDF_Dictionary* pStreamDict = pStream->GetDict();
-  if (!pStreamDict) {
-    auto pOwnedDict = m_pDocument->New<CPDF_Dictionary>();
-    pStreamDict = pOwnedDict.Get();
-    pStream->InitStream({}, std::move(pOwnedDict));
-  }
-
-  CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources");
-  if (!pStreamResList)
-    pStreamResList = pStreamDict->SetNewFor<CPDF_Dictionary>("Resources");
-  CPDF_Dictionary* pStreamResFontList = pStreamResList->GetDictFor("Font");
-  if (!pStreamResFontList) {
-    pStreamResFontList = m_pDocument->NewIndirect<CPDF_Dictionary>();
-    pStreamResList->SetNewFor<CPDF_Reference>("Font", m_pDocument.Get(),
-                                              pStreamResFontList->GetObjNum());
-  }
-  if (!pStreamResFontList->KeyExist(sAlias)) {
-    CPDF_Dictionary* pFontDict = pFont->GetFontDict();
-    RetainPtr<CPDF_Object> pObject =
-        pFontDict->IsInline() ? pFontDict->Clone()
-                              : pFontDict->MakeReference(m_pDocument.Get());
-    pStreamResFontList->SetFor(sAlias, std::move(pObject));
-  }
-}
-
-bool CBA_FontMap::KnowWord(int32_t nFontIndex, uint16_t word) {
-  return pdfium::IndexInBounds(m_Data, nFontIndex) &&
-         CharCodeFromUnicode(nFontIndex, word) >= 0;
-}
-
-void CBA_FontMap::Clear() {
-  m_Data.clear();
-  m_NativeFont.clear();
-}
-
-int32_t CBA_FontMap::GetFontIndex(const ByteString& sFontName,
-                                  int32_t nCharset,
-                                  bool bFind) {
-  int32_t nFontIndex = FindFont(EncodeFontAlias(sFontName, nCharset), nCharset);
-  if (nFontIndex >= 0)
-    return nFontIndex;
-
-  ByteString sAlias;
-  RetainPtr<CPDF_Font> pFont =
-      bFind ? FindFontSameCharset(&sAlias, nCharset) : nullptr;
-  if (!pFont) {
-    ByteString sTemp = sFontName;
-    pFont = AddFontToDocument(sTemp, nCharset);
-    sAlias = EncodeFontAlias(sTemp, nCharset);
-  }
-  AddFontToAnnotDict(pFont, sAlias);
-  return AddFontData(pFont, sAlias, nCharset);
-}
-
-int32_t CBA_FontMap::AddFontData(const RetainPtr<CPDF_Font>& pFont,
-                                 const ByteString& sFontAlias,
-                                 int32_t nCharset) {
-  auto pNewData = pdfium::MakeUnique<Data>();
-  pNewData->pFont = pFont;
-  pNewData->sFontName = sFontAlias;
-  pNewData->nCharset = nCharset;
-  m_Data.push_back(std::move(pNewData));
-  return pdfium::CollectionSize<int32_t>(m_Data) - 1;
-}
-
-ByteString CBA_FontMap::EncodeFontAlias(const ByteString& sFontName,
-                                        int32_t nCharset) {
-  return EncodeFontAlias(sFontName) + ByteString::Format("_%02X", nCharset);
-}
-
-ByteString CBA_FontMap::EncodeFontAlias(const ByteString& sFontName) {
-  ByteString sRet = sFontName;
-  sRet.Remove(' ');
-  return sRet;
-}
-
-int32_t CBA_FontMap::FindFont(const ByteString& sFontName, int32_t nCharset) {
-  int32_t i = 0;
-  for (const auto& pData : m_Data) {
-    if ((nCharset == FX_CHARSET_Default || nCharset == pData->nCharset) &&
-        (sFontName.IsEmpty() || pData->sFontName == sFontName)) {
-      return i;
-    }
-    ++i;
-  }
-  return -1;
-}
-
-ByteString CBA_FontMap::GetNativeFontName(int32_t nCharset) {
-  if (nCharset == FX_CHARSET_Default)
-    nCharset = GetNativeCharset();
-
-  ByteString sFontName = CFX_Font::GetDefaultFontNameByCharset(nCharset);
-  if (!FindNativeTrueTypeFont(sFontName))
-    return ByteString();
-
-  return sFontName;
-}
-
-ByteString CBA_FontMap::GetCachedNativeFontName(int32_t nCharset) {
-  for (const auto& pData : m_NativeFont) {
-    if (pData && pData->nCharset == nCharset)
-      return pData->sFontName;
-  }
-
-  ByteString sNew = GetNativeFontName(nCharset);
-  if (sNew.IsEmpty())
-    return ByteString();
-
-  auto pNewData = pdfium::MakeUnique<Native>();
-  pNewData->nCharset = nCharset;
-  pNewData->sFontName = sNew;
-  m_NativeFont.push_back(std::move(pNewData));
-  return sNew;
-}
-
-RetainPtr<CPDF_Font> CBA_FontMap::AddFontToDocument(ByteString sFontName,
-                                                    uint8_t nCharset) {
-  if (IsStandardFont(sFontName))
-    return AddStandardFont(sFontName);
-
-  return AddSystemFont(sFontName, nCharset);
-}
-
-bool CBA_FontMap::IsStandardFont(const ByteString& sFontName) {
-  static const char* const kStandardFontNames[] = {"Courier",
-                                                   "Courier-Bold",
-                                                   "Courier-BoldOblique",
-                                                   "Courier-Oblique",
-                                                   "Helvetica",
-                                                   "Helvetica-Bold",
-                                                   "Helvetica-BoldOblique",
-                                                   "Helvetica-Oblique",
-                                                   "Times-Roman",
-                                                   "Times-Bold",
-                                                   "Times-Italic",
-                                                   "Times-BoldItalic",
-                                                   "Symbol",
-                                                   "ZapfDingbats"};
-  for (const char* name : kStandardFontNames) {
-    if (sFontName == name)
-      return true;
-  }
-  return false;
-}
-
-RetainPtr<CPDF_Font> CBA_FontMap::AddStandardFont(ByteString sFontName) {
-  auto* pPageData = CPDF_DocPageData::FromDocument(m_pDocument.Get());
-  if (sFontName == "ZapfDingbats")
-    return pPageData->AddStandardFont(sFontName, nullptr);
-
-  static const CPDF_FontEncoding fe(PDFFONT_ENCODING_WINANSI);
-  return pPageData->AddStandardFont(sFontName, &fe);
-}
-
-RetainPtr<CPDF_Font> CBA_FontMap::AddSystemFont(ByteString sFontName,
-                                                uint8_t nCharset) {
-  if (sFontName.IsEmpty())
-    sFontName = GetNativeFontName(nCharset);
-
-  if (nCharset == FX_CHARSET_Default)
-    nCharset = GetNativeCharset();
-
-  return AddNativeTrueTypeFontToPDF(m_pDocument.Get(), sFontName, nCharset);
-}
diff --git a/core/fpdfdoc/cba_fontmap.h b/core/fpdfdoc/cba_fontmap.h
deleted file mode 100644
index 7130064..0000000
--- a/core/fpdfdoc/cba_fontmap.h
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFDOC_CBA_FONTMAP_H_
-#define CORE_FPDFDOC_CBA_FONTMAP_H_
-
-#include <memory>
-#include <vector>
-
-#include "core/fpdfdoc/ipvt_fontmap.h"
-#include "core/fxcrt/fx_codepage.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class CPDF_Dictionary;
-class CPDF_Document;
-
-class CBA_FontMap final : public IPVT_FontMap {
- public:
-  static int32_t GetNativeCharset();
-
-  CBA_FontMap(CPDF_Document* pDocument, CPDF_Dictionary* pAnnotDict);
-  ~CBA_FontMap() override;
-
-  // IPVT_FontMap
-  RetainPtr<CPDF_Font> GetPDFFont(int32_t nFontIndex) override;
-  ByteString GetPDFFontAlias(int32_t nFontIndex) override;
-  int32_t GetWordFontIndex(uint16_t word,
-                           int32_t nCharset,
-                           int32_t nFontIndex) override;
-  int32_t CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) override;
-  int32_t CharSetFromUnicode(uint16_t word, int32_t nOldCharset) override;
-
-  void Reset();
-  void SetAPType(const ByteString& sAPType);
-
- private:
-  struct Data {
-    Data();
-    ~Data();
-
-    RetainPtr<CPDF_Font> pFont;
-    int32_t nCharset;
-    ByteString sFontName;
-  };
-
-  struct Native {
-    int32_t nCharset;
-    ByteString sFontName;
-  };
-
-  void Initialize();
-  RetainPtr<CPDF_Font> FindFontSameCharset(ByteString* sFontAlias,
-                                           int32_t nCharset);
-  RetainPtr<CPDF_Font> FindResFontSameCharset(const CPDF_Dictionary* pResDict,
-                                              ByteString* sFontAlias,
-                                              int32_t nCharset);
-  RetainPtr<CPDF_Font> GetAnnotDefaultFont(ByteString* sAlias);
-  void AddFontToAnnotDict(const RetainPtr<CPDF_Font>& pFont,
-                          const ByteString& sAlias);
-
-  bool KnowWord(int32_t nFontIndex, uint16_t word);
-
-  void Clear();
-  int32_t GetFontIndex(const ByteString& sFontName,
-                       int32_t nCharset,
-                       bool bFind);
-  int32_t AddFontData(const RetainPtr<CPDF_Font>& pFont,
-                      const ByteString& sFontAlias,
-                      int32_t nCharset);
-
-  ByteString EncodeFontAlias(const ByteString& sFontName, int32_t nCharset);
-  ByteString EncodeFontAlias(const ByteString& sFontName);
-
-  int32_t FindFont(const ByteString& sFontName, int32_t nCharset);
-  ByteString GetNativeFontName(int32_t nCharset);
-  ByteString GetCachedNativeFontName(int32_t nCharset);
-  bool IsStandardFont(const ByteString& sFontName);
-  RetainPtr<CPDF_Font> AddFontToDocument(ByteString sFontName,
-                                         uint8_t nCharset);
-  RetainPtr<CPDF_Font> AddStandardFont(ByteString sFontName);
-  RetainPtr<CPDF_Font> AddSystemFont(ByteString sFontName, uint8_t nCharset);
-
-  std::vector<std::unique_ptr<Data>> m_Data;
-  std::vector<std::unique_ptr<Native>> m_NativeFont;
-  UnownedPtr<CPDF_Document> const m_pDocument;
-  RetainPtr<CPDF_Dictionary> const m_pAnnotDict;
-  RetainPtr<CPDF_Font> m_pDefaultFont;
-  ByteString m_sDefaultFontName;
-  ByteString m_sAPType = "N";
-};
-
-#endif  // CORE_FPDFDOC_CBA_FONTMAP_H_
diff --git a/core/fpdfdoc/cline.cpp b/core/fpdfdoc/cline.cpp
deleted file mode 100644
index 7940dc8..0000000
--- a/core/fpdfdoc/cline.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fpdfdoc/cline.h"
-
-CLine::CLine(const CPVT_LineInfo& lineinfo) : m_LineInfo(lineinfo) {}
-
-CLine::~CLine() = default;
-
-CPVT_WordPlace CLine::GetBeginWordPlace() const {
-  return CPVT_WordPlace(LinePlace.nSecIndex, LinePlace.nLineIndex, -1);
-}
-
-CPVT_WordPlace CLine::GetEndWordPlace() const {
-  return CPVT_WordPlace(LinePlace.nSecIndex, LinePlace.nLineIndex,
-                        m_LineInfo.nEndWordIndex);
-}
-
-CPVT_WordPlace CLine::GetPrevWordPlace(const CPVT_WordPlace& place) const {
-  if (place.nWordIndex > m_LineInfo.nEndWordIndex) {
-    return CPVT_WordPlace(place.nSecIndex, place.nLineIndex,
-                          m_LineInfo.nEndWordIndex);
-  }
-  return CPVT_WordPlace(place.nSecIndex, place.nLineIndex,
-                        place.nWordIndex - 1);
-}
-
-CPVT_WordPlace CLine::GetNextWordPlace(const CPVT_WordPlace& place) const {
-  if (place.nWordIndex < m_LineInfo.nBeginWordIndex) {
-    return CPVT_WordPlace(place.nSecIndex, place.nLineIndex,
-                          m_LineInfo.nBeginWordIndex);
-  }
-  return CPVT_WordPlace(place.nSecIndex, place.nLineIndex,
-                        place.nWordIndex + 1);
-}
diff --git a/core/fpdfdoc/cline.h b/core/fpdfdoc/cline.h
deleted file mode 100644
index b7e32d4..0000000
--- a/core/fpdfdoc/cline.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFDOC_CLINE_H_
-#define CORE_FPDFDOC_CLINE_H_
-
-#include "core/fpdfdoc/cpvt_lineinfo.h"
-#include "core/fpdfdoc/cpvt_wordplace.h"
-
-class CLine {
- public:
-  explicit CLine(const CPVT_LineInfo& lineinfo);
-  ~CLine();
-
-  CPVT_WordPlace GetBeginWordPlace() const;
-  CPVT_WordPlace GetEndWordPlace() const;
-  CPVT_WordPlace GetPrevWordPlace(const CPVT_WordPlace& place) const;
-  CPVT_WordPlace GetNextWordPlace(const CPVT_WordPlace& place) const;
-  CPVT_WordPlace LinePlace;
-  CPVT_LineInfo m_LineInfo;
-};
-
-#endif  // CORE_FPDFDOC_CLINE_H_
diff --git a/core/fpdfdoc/cpdf_aaction.cpp b/core/fpdfdoc/cpdf_aaction.cpp
index 8284913..4b62a7f 100644
--- a/core/fpdfdoc/cpdf_aaction.cpp
+++ b/core/fpdfdoc/cpdf_aaction.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,9 @@
 
 #include "core/fpdfdoc/cpdf_aaction.h"
 
+#include <iterator>
+#include <utility>
+
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 
 namespace {
@@ -36,12 +39,13 @@
 
 // |kAATypes| should have one less element than enum AActionType due to
 // |kDocumentOpen|, which is an artificial type.
-static_assert(FX_ArraySize(kAATypes) == CPDF_AAction::kNumberOfActions - 1,
+static_assert(std::size(kAATypes) == CPDF_AAction::kNumberOfActions - 1,
               "kAATypes count mismatch");
 
 }  // namespace
 
-CPDF_AAction::CPDF_AAction(const CPDF_Dictionary* pDict) : m_pDict(pDict) {}
+CPDF_AAction::CPDF_AAction(RetainPtr<const CPDF_Dictionary> pDict)
+    : m_pDict(std::move(pDict)) {}
 
 CPDF_AAction::CPDF_AAction(const CPDF_AAction& that) = default;
 
@@ -56,10 +60,11 @@
 }
 
 // static
-bool CPDF_AAction::IsUserClick(AActionType eType) {
-  switch (eType) {
+bool CPDF_AAction::IsUserInput(AActionType type) {
+  switch (type) {
     case kButtonUp:
     case kButtonDown:
+    case kKeyStroke:
       return true;
     default:
       return false;
diff --git a/core/fpdfdoc/cpdf_aaction.h b/core/fpdfdoc/cpdf_aaction.h
index c3c65e8..9b4cbcb 100644
--- a/core/fpdfdoc/cpdf_aaction.h
+++ b/core/fpdfdoc/cpdf_aaction.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,10 @@
 #ifndef CORE_FPDFDOC_CPDF_AACTION_H_
 #define CORE_FPDFDOC_CPDF_AACTION_H_
 
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfdoc/cpdf_action.h"
 #include "core/fxcrt/retain_ptr.h"
 
-class CPDF_Dictionary;
-
 class CPDF_AAction {
  public:
   enum AActionType {
@@ -40,15 +39,15 @@
     kNumberOfActions  // Must be last.
   };
 
-  explicit CPDF_AAction(const CPDF_Dictionary* pDict);
+  explicit CPDF_AAction(RetainPtr<const CPDF_Dictionary> pDict);
   CPDF_AAction(const CPDF_AAction& that);
   ~CPDF_AAction();
 
   bool ActionExist(AActionType eType) const;
   CPDF_Action GetAction(AActionType eType) const;
-  const CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
+  bool HasDict() const { return !!m_pDict; }
 
-  static bool IsUserClick(AActionType eType);
+  static bool IsUserInput(AActionType type);
 
  private:
   RetainPtr<const CPDF_Dictionary> const m_pDict;
diff --git a/core/fpdfdoc/cpdf_action.cpp b/core/fpdfdoc/cpdf_action.cpp
index 5133c1b..1370c7f 100644
--- a/core/fpdfdoc/cpdf_action.cpp
+++ b/core/fpdfdoc/cpdf_action.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,105 +6,94 @@
 
 #include "core/fpdfdoc/cpdf_action.h"
 
+#include <iterator>
+#include <utility>
+
 #include "constants/stream_dict_common.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fpdfdoc/cpdf_filespec.h"
-#include "core/fpdfdoc/cpdf_nametree.h"
 
 namespace {
 
-const char* const g_sATypes[] = {
-    "Unknown",     "GoTo",       "GoToR",     "GoToE",      "Launch",
-    "Thread",      "URI",        "Sound",     "Movie",      "Hide",
-    "Named",       "SubmitForm", "ResetForm", "ImportData", "JavaScript",
-    "SetOCGState", "Rendition",  "Trans",     "GoTo3DView", nullptr};
+const char* const kActionTypeStrings[] = {
+    "GoTo",       "GoToR",     "GoToE",      "Launch",     "Thread",
+    "URI",        "Sound",     "Movie",      "Hide",       "Named",
+    "SubmitForm", "ResetForm", "ImportData", "JavaScript", "SetOCGState",
+    "Rendition",  "Trans",     "GoTo3DView"};
 
 }  // namespace
 
-CPDF_Action::CPDF_Action(const CPDF_Dictionary* pDict) : m_pDict(pDict) {}
+CPDF_Action::CPDF_Action(RetainPtr<const CPDF_Dictionary> pDict)
+    : m_pDict(std::move(pDict)) {}
 
 CPDF_Action::CPDF_Action(const CPDF_Action& that) = default;
 
 CPDF_Action::~CPDF_Action() = default;
 
-CPDF_Action::ActionType CPDF_Action::GetType() const {
-  if (!m_pDict)
-    return Unknown;
+CPDF_Action::Type CPDF_Action::GetType() const {
+  // See ISO 32000-1:2008 spec, table 193.
+  if (!ValidateDictOptionalType(m_pDict.Get(), "Action"))
+    return Type::kUnknown;
 
-  // Validate |m_pDict|. Type is optional, but must be valid if present.
-  const CPDF_Object* pType = m_pDict->GetObjectFor("Type");
-  if (pType) {
-    const CPDF_Name* pName = pType->AsName();
-    if (!pName || pName->GetString() != "Action")
-      return Unknown;
-  }
-
-  ByteString csType = m_pDict->GetStringFor("S");
+  ByteString csType = m_pDict->GetNameFor("S");
   if (csType.IsEmpty())
-    return Unknown;
+    return Type::kUnknown;
 
-  for (int i = 0; g_sATypes[i]; ++i) {
-    if (csType == g_sATypes[i])
-      return static_cast<ActionType>(i);
+  static_assert(
+      std::size(kActionTypeStrings) == static_cast<size_t>(Type::kLast),
+      "Type mismatch");
+  for (size_t i = 0; i < std::size(kActionTypeStrings); ++i) {
+    if (csType == kActionTypeStrings[i])
+      return static_cast<Type>(i + 1);
   }
-  return Unknown;
+  return Type::kUnknown;
 }
 
 CPDF_Dest CPDF_Action::GetDest(CPDF_Document* pDoc) const {
-  ActionType type = GetType();
-  if (type != GoTo && type != GoToR)
-    return CPDF_Dest();
-
-  const CPDF_Object* pDest = m_pDict->GetDirectObjectFor("D");
-  if (!pDest)
-    return CPDF_Dest();
-  if (pDest->IsString() || pDest->IsName()) {
-    CPDF_NameTree name_tree(pDoc, "Dests");
-    return CPDF_Dest(name_tree.LookupNamedDest(pDoc, pDest->GetUnicodeText()));
+  Type type = GetType();
+  if (type != Type::kGoTo && type != Type::kGoToR && type != Type::kGoToE) {
+    return CPDF_Dest(nullptr);
   }
-  if (const CPDF_Array* pArray = pDest->AsArray())
-    return CPDF_Dest(pArray);
-
-  return CPDF_Dest();
+  return CPDF_Dest::Create(pDoc, m_pDict->GetDirectObjectFor("D"));
 }
 
 WideString CPDF_Action::GetFilePath() const {
-  ActionType type = GetType();
-  if (type != GoToR && type != Launch && type != SubmitForm &&
-      type != ImportData) {
+  Type type = GetType();
+  if (type != Type::kGoToR && type != Type::kGoToE && type != Type::kLaunch &&
+      type != Type::kSubmitForm && type != Type::kImportData) {
     return WideString();
   }
 
-  const CPDF_Object* pFile = m_pDict->GetDirectObjectFor(pdfium::stream::kF);
+  RetainPtr<const CPDF_Object> pFile =
+      m_pDict->GetDirectObjectFor(pdfium::stream::kF);
   if (pFile)
-    return CPDF_FileSpec(pFile).GetFileName();
+    return CPDF_FileSpec(std::move(pFile)).GetFileName();
 
-  if (type != Launch)
+  if (type != Type::kLaunch)
     return WideString();
 
-  const CPDF_Dictionary* pWinDict = m_pDict->GetDictFor("Win");
+  RetainPtr<const CPDF_Dictionary> pWinDict = m_pDict->GetDictFor("Win");
   if (!pWinDict)
     return WideString();
 
   return WideString::FromDefANSI(
-      pWinDict->GetStringFor(pdfium::stream::kF).AsStringView());
+      pWinDict->GetByteStringFor(pdfium::stream::kF).AsStringView());
 }
 
 ByteString CPDF_Action::GetURI(const CPDF_Document* pDoc) const {
-  ActionType type = GetType();
-  if (type != URI)
+  if (GetType() != Type::kURI)
     return ByteString();
 
-  ByteString csURI = m_pDict->GetStringFor("URI");
-  const CPDF_Dictionary* pRoot = pDoc->GetRoot();
-  const CPDF_Dictionary* pURI = pRoot->GetDictFor("URI");
+  ByteString csURI = m_pDict->GetByteStringFor("URI");
+  RetainPtr<const CPDF_Dictionary> pURI = pDoc->GetRoot()->GetDictFor("URI");
   if (pURI) {
     auto result = csURI.Find(":");
     if (!result.has_value() || result.value() == 0) {
-      auto* pBase = pURI->GetDirectObjectFor("Base");
+      RetainPtr<const CPDF_Object> pBase = pURI->GetDirectObjectFor("Base");
       if (pBase && (pBase->IsString() || pBase->IsStream()))
         csURI = pBase->GetString() + csURI;
     }
@@ -117,46 +106,55 @@
 }
 
 ByteString CPDF_Action::GetNamedAction() const {
-  return m_pDict->GetStringFor("N");
+  return m_pDict->GetByteStringFor("N");
 }
 
 uint32_t CPDF_Action::GetFlags() const {
   return m_pDict->GetIntegerFor("Flags");
 }
 
-std::vector<const CPDF_Object*> CPDF_Action::GetAllFields() const {
-  std::vector<const CPDF_Object*> result;
+bool CPDF_Action::HasFields() const {
+  return m_pDict->KeyExist("Fields");
+}
+
+std::vector<RetainPtr<const CPDF_Object>> CPDF_Action::GetAllFields() const {
+  std::vector<RetainPtr<const CPDF_Object>> result;
   if (!m_pDict)
     return result;
 
-  ByteString csType = m_pDict->GetStringFor("S");
-  const CPDF_Object* pFields = csType == "Hide"
-                                   ? m_pDict->GetDirectObjectFor("T")
-                                   : m_pDict->GetArrayFor("Fields");
+  ByteString csType = m_pDict->GetByteStringFor("S");
+  RetainPtr<const CPDF_Object> pFields = csType == "Hide"
+                                             ? m_pDict->GetDirectObjectFor("T")
+                                             : m_pDict->GetArrayFor("Fields");
   if (!pFields)
     return result;
 
   if (pFields->IsDictionary() || pFields->IsString()) {
-    result.push_back(pFields);
-  } else if (const CPDF_Array* pArray = pFields->AsArray()) {
-    for (size_t i = 0; i < pArray->size(); ++i) {
-      const CPDF_Object* pObj = pArray->GetDirectObjectAt(i);
-      if (pObj)
-        result.push_back(pObj);
-    }
+    result.push_back(std::move(pFields));
+    return result;
+  }
+
+  const CPDF_Array* pArray = pFields->AsArray();
+  if (!pArray)
+    return result;
+
+  for (size_t i = 0; i < pArray->size(); ++i) {
+    RetainPtr<const CPDF_Object> pObj = pArray->GetDirectObjectAt(i);
+    if (pObj)
+      result.push_back(std::move(pObj));
   }
   return result;
 }
 
-Optional<WideString> CPDF_Action::MaybeGetJavaScript() const {
-  const CPDF_Object* pObject = GetJavaScriptObject();
+absl::optional<WideString> CPDF_Action::MaybeGetJavaScript() const {
+  RetainPtr<const CPDF_Object> pObject = GetJavaScriptObject();
   if (!pObject)
-    return pdfium::nullopt;
+    return absl::nullopt;
   return pObject->GetUnicodeText();
 }
 
 WideString CPDF_Action::GetJavaScript() const {
-  const CPDF_Object* pObject = GetJavaScriptObject();
+  RetainPtr<const CPDF_Object> pObject = GetJavaScriptObject();
   return pObject ? pObject->GetUnicodeText() : WideString();
 }
 
@@ -164,7 +162,7 @@
   if (!m_pDict || !m_pDict->KeyExist("Next"))
     return 0;
 
-  const CPDF_Object* pNext = m_pDict->GetDirectObjectFor("Next");
+  RetainPtr<const CPDF_Object> pNext = m_pDict->GetDirectObjectFor("Next");
   if (!pNext)
     return 0;
   if (pNext->IsDictionary())
@@ -177,20 +175,24 @@
   if (!m_pDict || !m_pDict->KeyExist("Next"))
     return CPDF_Action(nullptr);
 
-  const CPDF_Object* pNext = m_pDict->GetDirectObjectFor("Next");
-  if (const CPDF_Array* pArray = ToArray(pNext))
+  RetainPtr<const CPDF_Object> pNext = m_pDict->GetDirectObjectFor("Next");
+  if (!pNext)
+    return CPDF_Action(nullptr);
+
+  if (const CPDF_Array* pArray = pNext->AsArray())
     return CPDF_Action(pArray->GetDictAt(iIndex));
-  if (const CPDF_Dictionary* pDict = ToDictionary(pNext)) {
+
+  if (const CPDF_Dictionary* pDict = pNext->AsDictionary()) {
     if (iIndex == 0)
-      return CPDF_Action(pDict);
+      return CPDF_Action(pdfium::WrapRetain(pDict));
   }
   return CPDF_Action(nullptr);
 }
 
-const CPDF_Object* CPDF_Action::GetJavaScriptObject() const {
+RetainPtr<const CPDF_Object> CPDF_Action::GetJavaScriptObject() const {
   if (!m_pDict)
     return nullptr;
 
-  const CPDF_Object* pJS = m_pDict->GetDirectObjectFor("JS");
+  RetainPtr<const CPDF_Object> pJS = m_pDict->GetDirectObjectFor("JS");
   return (pJS && (pJS->IsString() || pJS->IsStream())) ? pJS : nullptr;
 }
diff --git a/core/fpdfdoc/cpdf_action.h b/core/fpdfdoc/cpdf_action.h
index c7cc2d8..cb9e930 100644
--- a/core/fpdfdoc/cpdf_action.h
+++ b/core/fpdfdoc/cpdf_action.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,7 @@
 #include "core/fpdfdoc/cpdf_dest.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CPDF_Dictionary;
 class CPDF_Document;
@@ -20,46 +20,49 @@
 
 class CPDF_Action {
  public:
-  enum ActionType {
-    Unknown = 0,
-    GoTo,
-    GoToR,
-    GoToE,
-    Launch,
-    Thread,
-    URI,
-    Sound,
-    Movie,
-    Hide,
-    Named,
-    SubmitForm,
-    ResetForm,
-    ImportData,
-    JavaScript,
-    SetOCGState,
-    Rendition,
-    Trans,
-    GoTo3DView
+  enum class Type {
+    kUnknown = 0,
+    kGoTo,
+    kGoToR,
+    kGoToE,
+    kLaunch,
+    kThread,
+    kURI,
+    kSound,
+    kMovie,
+    kHide,
+    kNamed,
+    kSubmitForm,
+    kResetForm,
+    kImportData,
+    kJavaScript,
+    kSetOCGState,
+    kRendition,
+    kTrans,
+    kGoTo3DView,
+    kLast = kGoTo3DView
   };
 
-  explicit CPDF_Action(const CPDF_Dictionary* pDict);
+  explicit CPDF_Action(RetainPtr<const CPDF_Dictionary> pDict);
   CPDF_Action(const CPDF_Action& that);
   ~CPDF_Action();
 
+  bool HasDict() const { return !!m_pDict; }
   const CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
 
-  ActionType GetType() const;
+  Type GetType() const;
   CPDF_Dest GetDest(CPDF_Document* pDoc) const;
   WideString GetFilePath() const;
   ByteString GetURI(const CPDF_Document* pDoc) const;
   bool GetHideStatus() const;
   ByteString GetNamedAction() const;
   uint32_t GetFlags() const;
+  bool HasFields() const;
 
-  std::vector<const CPDF_Object*> GetAllFields() const;
+  std::vector<RetainPtr<const CPDF_Object>> GetAllFields() const;
 
   // Differentiates between empty JS entry and no JS entry.
-  Optional<WideString> MaybeGetJavaScript() const;
+  absl::optional<WideString> MaybeGetJavaScript() const;
 
   // Returns empty string for empty JS entry and no JS entry.
   WideString GetJavaScript() const;
@@ -68,7 +71,7 @@
   CPDF_Action GetSubAction(size_t iIndex) const;
 
  private:
-  const CPDF_Object* GetJavaScriptObject() const;
+  RetainPtr<const CPDF_Object> GetJavaScriptObject() const;
 
   RetainPtr<const CPDF_Dictionary> const m_pDict;
 };
diff --git a/core/fpdfdoc/cpdf_action_unittest.cpp b/core/fpdfdoc/cpdf_action_unittest.cpp
new file mode 100644
index 0000000..a787711
--- /dev/null
+++ b/core/fpdfdoc/cpdf_action_unittest.cpp
@@ -0,0 +1,127 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfdoc/cpdf_action.h"
+
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+RetainPtr<CPDF_Dictionary> CreateActionDictWithType(
+    const ByteString& action_type) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "Action");
+  dict->SetNewFor<CPDF_Name>("S", action_type);
+  return dict;
+}
+
+RetainPtr<CPDF_Dictionary> CreateActionDictWithoutType(
+    const ByteString& action_type) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("S", action_type);
+  return dict;
+}
+
+RetainPtr<CPDF_Dictionary> CreateActionDictWithInvalidType(
+    const ByteString& action_type) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "Lights");
+  dict->SetNewFor<CPDF_Name>("S", action_type);
+  return dict;
+}
+
+RetainPtr<CPDF_Dictionary> CreateInvalidActionDictWithType(
+    const ByteString& action_type) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "Action");
+  dict->SetNewFor<CPDF_String>("S", action_type, /*is_hex=*/false);
+  return dict;
+}
+
+RetainPtr<CPDF_Dictionary> CreateInvalidActionDictWithoutType(
+    const ByteString& action_type) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_String>("S", action_type, /*is_hex=*/false);
+  return dict;
+}
+
+}  // namespace
+
+TEST(CPDFActionTest, GetType) {
+  static constexpr struct {
+    const char* action_type;
+    CPDF_Action::Type expected_type;
+  } kValidTestCases[] = {
+      {"GoTo", CPDF_Action::Type::kGoTo},
+      {"GoToR", CPDF_Action::Type::kGoToR},
+      {"GoToE", CPDF_Action::Type::kGoToE},
+      {"Launch", CPDF_Action::Type::kLaunch},
+      {"Thread", CPDF_Action::Type::kThread},
+      {"URI", CPDF_Action::Type::kURI},
+      {"Sound", CPDF_Action::Type::kSound},
+      {"Movie", CPDF_Action::Type::kMovie},
+      {"Hide", CPDF_Action::Type::kHide},
+      {"Named", CPDF_Action::Type::kNamed},
+      {"SubmitForm", CPDF_Action::Type::kSubmitForm},
+      {"ResetForm", CPDF_Action::Type::kResetForm},
+      {"ImportData", CPDF_Action::Type::kImportData},
+      {"JavaScript", CPDF_Action::Type::kJavaScript},
+      {"SetOCGState", CPDF_Action::Type::kSetOCGState},
+      {"Rendition", CPDF_Action::Type::kRendition},
+      {"Trans", CPDF_Action::Type::kTrans},
+      {"GoTo3DView", CPDF_Action::Type::kGoTo3DView},
+  };
+
+  // Test correctly constructed actions.
+  for (const auto& test_case : kValidTestCases) {
+    {
+      // Type is present.
+      CPDF_Action action(CreateActionDictWithType(test_case.action_type));
+      EXPECT_EQ(test_case.expected_type, action.GetType());
+    }
+    {
+      // Type is optional, so omitting it is ok.
+      CPDF_Action action(CreateActionDictWithoutType(test_case.action_type));
+      EXPECT_EQ(test_case.expected_type, action.GetType());
+    }
+  }
+
+  // Test incorrectly constructed actions.
+  for (const auto& test_case : kValidTestCases) {
+    {
+      // Type is optional, but must be valid if present.
+      CPDF_Action action(
+          CreateActionDictWithInvalidType(test_case.action_type));
+      EXPECT_EQ(CPDF_Action::Type::kUnknown, action.GetType());
+    }
+    {
+      // The action type (/S) must be a name.
+      CPDF_Action action(
+          CreateInvalidActionDictWithType(test_case.action_type));
+      EXPECT_EQ(CPDF_Action::Type::kUnknown, action.GetType());
+    }
+    {
+      // The action type (/S) must be a name.
+      CPDF_Action action(
+          CreateInvalidActionDictWithoutType(test_case.action_type));
+      EXPECT_EQ(CPDF_Action::Type::kUnknown, action.GetType());
+    }
+  }
+
+  static constexpr const char* kInvalidTestCases[] = {
+      "Camera",
+      "Javascript",
+      "Unknown",
+  };
+
+  // Test invalid actions.
+  for (const char* test_case : kInvalidTestCases) {
+    CPDF_Action action(CreateActionDictWithType(test_case));
+    EXPECT_EQ(CPDF_Action::Type::kUnknown, action.GetType());
+  }
+}
diff --git a/core/fpdfdoc/cpdf_annot.cpp b/core/fpdfdoc/cpdf_annot.cpp
index f474074..e0ecd88 100644
--- a/core/fpdfdoc/cpdf_annot.cpp
+++ b/core/fpdfdoc/cpdf_annot.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,19 +13,21 @@
 #include "constants/annotation_flags.h"
 #include "core/fpdfapi/page/cpdf_form.h"
 #include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_boolean.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
 #include "core/fpdfapi/render/cpdf_rendercontext.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
-#include "core/fpdfdoc/cpvt_generateap.h"
+#include "core/fpdfdoc/cpdf_generateap.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -38,10 +40,10 @@
          type == CPDF_Annot::Subtype::UNDERLINE;
 }
 
-CPDF_Form* AnnotGetMatrix(const CPDF_Page* pPage,
+CPDF_Form* AnnotGetMatrix(CPDF_Page* pPage,
                           CPDF_Annot* pAnnot,
                           CPDF_Annot::AppearanceMode mode,
-                          const CFX_Matrix* pUser2Device,
+                          const CFX_Matrix& mtUser2Device,
                           CFX_Matrix* matrix) {
   CPDF_Form* pForm = pAnnot->GetAPForm(pPage, mode);
   if (!pForm)
@@ -51,77 +53,88 @@
   CFX_FloatRect form_bbox =
       form_matrix.TransformRect(pForm->GetDict()->GetRectFor("BBox"));
   matrix->MatchRect(pAnnot->GetRect(), form_bbox);
-  matrix->Concat(*pUser2Device);
+
+  // Compensate for page rotation.
+  if ((pAnnot->GetFlags() & pdfium::annotation_flags::kNoRotate) &&
+      pPage->GetPageRotation() != 0) {
+    // Rotate annotation rect around top-left angle (according to the
+    // specification).
+    const float offset_x = pAnnot->GetRect().Left();
+    const float offset_y = pAnnot->GetRect().Top();
+    matrix->Concat({1, 0, 0, 1, -offset_x, -offset_y});
+    // GetPageRotation returns value in fractions of pi/2.
+    const float angle = FXSYS_PI / 2 * pPage->GetPageRotation();
+    matrix->Rotate(angle);
+    matrix->Concat({1, 0, 0, 1, offset_x, offset_y});
+  }
+
+  matrix->Concat(mtUser2Device);
   return pForm;
 }
 
-CPDF_Stream* GetAnnotAPInternal(CPDF_Dictionary* pAnnotDict,
-                                CPDF_Annot::AppearanceMode eMode,
-                                bool bFallbackToNormal) {
-  CPDF_Dictionary* pAP = pAnnotDict->GetDictFor(pdfium::annotation::kAP);
+RetainPtr<CPDF_Stream> GetAnnotAPInternal(CPDF_Dictionary* pAnnotDict,
+                                          CPDF_Annot::AppearanceMode eMode,
+                                          bool bFallbackToNormal) {
+  RetainPtr<CPDF_Dictionary> pAP =
+      pAnnotDict->GetMutableDictFor(pdfium::annotation::kAP);
   if (!pAP)
     return nullptr;
 
   const char* ap_entry = "N";
-  if (eMode == CPDF_Annot::Down)
+  if (eMode == CPDF_Annot::AppearanceMode::kDown)
     ap_entry = "D";
-  else if (eMode == CPDF_Annot::Rollover)
+  else if (eMode == CPDF_Annot::AppearanceMode::kRollover)
     ap_entry = "R";
   if (bFallbackToNormal && !pAP->KeyExist(ap_entry))
     ap_entry = "N";
 
-  CPDF_Object* psub = pAP->GetDirectObjectFor(ap_entry);
+  RetainPtr<CPDF_Object> psub = pAP->GetMutableDirectObjectFor(ap_entry);
   if (!psub)
     return nullptr;
-  if (CPDF_Stream* pStream = psub->AsStream())
+
+  RetainPtr<CPDF_Stream> pStream(psub->AsMutableStream());
+  if (pStream)
     return pStream;
 
-  CPDF_Dictionary* pDict = psub->AsDictionary();
+  CPDF_Dictionary* pDict = psub->AsMutableDictionary();
   if (!pDict)
     return nullptr;
 
-  ByteString as = pAnnotDict->GetStringFor(pdfium::annotation::kAS);
+  ByteString as = pAnnotDict->GetByteStringFor(pdfium::annotation::kAS);
   if (as.IsEmpty()) {
-    ByteString value = pAnnotDict->GetStringFor("V");
+    ByteString value = pAnnotDict->GetByteStringFor("V");
     if (value.IsEmpty()) {
-      const CPDF_Dictionary* pParentDict = pAnnotDict->GetDictFor("Parent");
-      value = pParentDict ? pParentDict->GetStringFor("V") : ByteString();
+      RetainPtr<const CPDF_Dictionary> pParentDict =
+          pAnnotDict->GetDictFor("Parent");
+      value = pParentDict ? pParentDict->GetByteStringFor("V") : ByteString();
     }
     as = (!value.IsEmpty() && pDict->KeyExist(value)) ? value : "Off";
   }
-  return pDict->GetStreamFor(as);
+  return pDict->GetMutableStreamFor(as);
 }
 
 }  // namespace
 
 CPDF_Annot::CPDF_Annot(RetainPtr<CPDF_Dictionary> pDict,
                        CPDF_Document* pDocument)
-    : m_pAnnotDict(std::move(pDict)), m_pDocument(pDocument) {
-  Init();
-}
-
-CPDF_Annot::CPDF_Annot(CPDF_Dictionary* pDict, CPDF_Document* pDocument)
-    : m_pAnnotDict(pDict), m_pDocument(pDocument) {
-  Init();
+    : m_pAnnotDict(std::move(pDict)),
+      m_pDocument(pDocument),
+      m_nSubtype(StringToAnnotSubtype(
+          m_pAnnotDict->GetByteStringFor(pdfium::annotation::kSubtype))),
+      m_bIsTextMarkupAnnotation(IsTextMarkupAnnotation(m_nSubtype)),
+      m_bHasGeneratedAP(
+          m_pAnnotDict->GetBooleanFor(kPDFiumKey_HasGeneratedAP, false)) {
+  GenerateAPIfNeeded();
 }
 
 CPDF_Annot::~CPDF_Annot() {
   ClearCachedAP();
 }
 
-void CPDF_Annot::Init() {
-  m_nSubtype = StringToAnnotSubtype(
-      m_pAnnotDict->GetStringFor(pdfium::annotation::kSubtype));
-  m_bIsTextMarkupAnnotation = IsTextMarkupAnnotation(m_nSubtype);
-  m_bHasGeneratedAP =
-      m_pAnnotDict->GetBooleanFor(kPDFiumKey_HasGeneratedAP, false);
-  GenerateAPIfNeeded();
-}
-
 void CPDF_Annot::GenerateAPIfNeeded() {
   if (!ShouldGenerateAP())
     return;
-  if (!CPVT_GenerateAP::GenerateAnnotAP(m_pDocument.Get(), m_pAnnotDict.Get(),
+  if (!CPDF_GenerateAP::GenerateAnnotAP(m_pDocument, m_pAnnotDict.Get(),
                                         m_nSubtype)) {
     return;
   }
@@ -133,7 +146,7 @@
 bool CPDF_Annot::ShouldGenerateAP() const {
   // If AP dictionary exists and defines an appearance for normal mode, we use
   // the appearance defined in the existing AP dictionary.
-  const CPDF_Dictionary* pAP =
+  RetainPtr<const CPDF_Dictionary> pAP =
       m_pAnnotDict->GetDictFor(pdfium::annotation::kAP);
   if (pAP && pAP->GetDictFor("N"))
     return false;
@@ -177,20 +190,20 @@
   return !!(GetFlags() & pdfium::annotation_flags::kHidden);
 }
 
-CPDF_Stream* GetAnnotAP(CPDF_Dictionary* pAnnotDict,
-                        CPDF_Annot::AppearanceMode eMode) {
-  ASSERT(pAnnotDict);
+RetainPtr<CPDF_Stream> GetAnnotAP(CPDF_Dictionary* pAnnotDict,
+                                  CPDF_Annot::AppearanceMode eMode) {
+  DCHECK(pAnnotDict);
   return GetAnnotAPInternal(pAnnotDict, eMode, true);
 }
 
-CPDF_Stream* GetAnnotAPNoFallback(CPDF_Dictionary* pAnnotDict,
-                                  CPDF_Annot::AppearanceMode eMode) {
-  ASSERT(pAnnotDict);
+RetainPtr<CPDF_Stream> GetAnnotAPNoFallback(CPDF_Dictionary* pAnnotDict,
+                                            CPDF_Annot::AppearanceMode eMode) {
+  DCHECK(pAnnotDict);
   return GetAnnotAPInternal(pAnnotDict, eMode, false);
 }
 
-CPDF_Form* CPDF_Annot::GetAPForm(const CPDF_Page* pPage, AppearanceMode mode) {
-  CPDF_Stream* pStream = GetAnnotAP(m_pAnnotDict.Get(), mode);
+CPDF_Form* CPDF_Annot::GetAPForm(CPDF_Page* pPage, AppearanceMode mode) {
+  RetainPtr<CPDF_Stream> pStream = GetAnnotAP(m_pAnnotDict.Get(), mode);
   if (!pStream)
     return nullptr;
 
@@ -198,8 +211,8 @@
   if (it != m_APMap.end())
     return it->second.get();
 
-  auto pNewForm = pdfium::MakeUnique<CPDF_Form>(
-      m_pDocument.Get(), pPage->m_pResources.Get(), pStream);
+  auto pNewForm = std::make_unique<CPDF_Form>(
+      m_pDocument, pPage->GetMutableResources(), pStream);
   pNewForm->ParseContent();
 
   CPDF_Form* pResult = pNewForm.get();
@@ -207,11 +220,22 @@
   return pResult;
 }
 
+void CPDF_Annot::SetPopupAnnotOpenState(bool bOpenState) {
+  if (m_pPopupAnnot)
+    m_pPopupAnnot->SetOpenState(bOpenState);
+}
+
+absl::optional<CFX_FloatRect> CPDF_Annot::GetPopupAnnotRect() const {
+  if (!m_pPopupAnnot)
+    return absl::nullopt;
+  return m_pPopupAnnot->GetRect();
+}
+
 // static
 CFX_FloatRect CPDF_Annot::RectFromQuadPointsArray(const CPDF_Array* pArray,
                                                   size_t nIndex) {
-  ASSERT(pArray);
-  ASSERT(nIndex < pArray->size() / 8);
+  DCHECK(pArray);
+  DCHECK(nIndex < pArray->size() / 8);
 
   // QuadPoints are defined with 4 pairs of numbers
   // ([ pair0, pair1, pair2, pair3 ]), where
@@ -225,22 +249,22 @@
   // pair1 = top_right.
 
   return CFX_FloatRect(
-      pArray->GetNumberAt(4 + nIndex * 8), pArray->GetNumberAt(5 + nIndex * 8),
-      pArray->GetNumberAt(2 + nIndex * 8), pArray->GetNumberAt(3 + nIndex * 8));
+      pArray->GetFloatAt(4 + nIndex * 8), pArray->GetFloatAt(5 + nIndex * 8),
+      pArray->GetFloatAt(2 + nIndex * 8), pArray->GetFloatAt(3 + nIndex * 8));
 }
 
 // static
 CFX_FloatRect CPDF_Annot::BoundingRectFromQuadPoints(
     const CPDF_Dictionary* pAnnotDict) {
   CFX_FloatRect ret;
-  const CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
-  size_t nQuadPointCount = pArray ? QuadPointCount(pArray) : 0;
+  RetainPtr<const CPDF_Array> pArray = pAnnotDict->GetArrayFor("QuadPoints");
+  size_t nQuadPointCount = pArray ? QuadPointCount(pArray.Get()) : 0;
   if (nQuadPointCount == 0)
     return ret;
 
-  ret = RectFromQuadPointsArray(pArray, 0);
+  ret = RectFromQuadPointsArray(pArray.Get(), 0);
   for (size_t i = 1; i < nQuadPointCount; ++i) {
-    CFX_FloatRect rect = RectFromQuadPointsArray(pArray, i);
+    CFX_FloatRect rect = RectFromQuadPointsArray(pArray.Get(), i);
     ret.Union(rect);
   }
   return ret;
@@ -249,11 +273,11 @@
 // static
 CFX_FloatRect CPDF_Annot::RectFromQuadPoints(const CPDF_Dictionary* pAnnotDict,
                                              size_t nIndex) {
-  const CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
-  size_t nQuadPointCount = pArray ? QuadPointCount(pArray) : 0;
+  RetainPtr<const CPDF_Array> pArray = pAnnotDict->GetArrayFor("QuadPoints");
+  size_t nQuadPointCount = pArray ? QuadPointCount(pArray.Get()) : 0;
   if (nIndex >= nQuadPointCount)
     return CFX_FloatRect();
-  return RectFromQuadPointsArray(pArray, nIndex);
+  return RectFromQuadPointsArray(pArray.Get(), nIndex);
 }
 
 // static
@@ -313,6 +337,8 @@
     return CPDF_Annot::Subtype::RICHMEDIA;
   if (sSubtype == "XFAWidget")
     return CPDF_Annot::Subtype::XFAWIDGET;
+  if (sSubtype == "Redact")
+    return CPDF_Annot::Subtype::REDACT;
   return CPDF_Annot::Subtype::UNKNOWN;
 }
 
@@ -372,6 +398,8 @@
     return "RichMedia";
   if (nSubtype == CPDF_Annot::Subtype::XFAWIDGET)
     return "XFAWidget";
+  if (nSubtype == CPDF_Annot::Subtype::REDACT)
+    return "Redact";
   return ByteString();
 }
 
@@ -383,57 +411,55 @@
 bool CPDF_Annot::DrawAppearance(CPDF_Page* pPage,
                                 CFX_RenderDevice* pDevice,
                                 const CFX_Matrix& mtUser2Device,
-                                AppearanceMode mode,
-                                const CPDF_RenderOptions* pOptions) {
+                                AppearanceMode mode) {
   if (!ShouldDrawAnnotation())
     return false;
 
   // It might happen that by the time this annotation instance was created,
-  // it was flagged as "hidden" (e.g. /F 2), and hence CPVT_GenerateAP decided
+  // it was flagged as "hidden" (e.g. /F 2), and hence CPDF_GenerateAP decided
   // to not "generate" its AP.
   // If for a reason the object is no longer hidden, but still does not have
   // its "AP" generated, generate it now.
   GenerateAPIfNeeded();
 
   CFX_Matrix matrix;
-  CPDF_Form* pForm = AnnotGetMatrix(pPage, this, mode, &mtUser2Device, &matrix);
+  CPDF_Form* pForm = AnnotGetMatrix(pPage, this, mode, mtUser2Device, &matrix);
   if (!pForm)
     return false;
 
-  CPDF_RenderContext context(
-      pPage->GetDocument(), pPage->m_pPageResources.Get(),
-      static_cast<CPDF_PageRenderCache*>(pPage->GetRenderCache()));
-  context.AppendLayer(pForm, &matrix);
-  context.Render(pDevice, pOptions, nullptr);
+  CPDF_RenderContext context(pPage->GetDocument(),
+                             pPage->GetMutablePageResources(),
+                             pPage->GetPageImageCache());
+  context.AppendLayer(pForm, matrix);
+  context.Render(pDevice, nullptr, nullptr, nullptr);
   return true;
 }
 
-bool CPDF_Annot::DrawInContext(const CPDF_Page* pPage,
+bool CPDF_Annot::DrawInContext(CPDF_Page* pPage,
                                CPDF_RenderContext* pContext,
-                               const CFX_Matrix* pUser2Device,
+                               const CFX_Matrix& mtUser2Device,
                                AppearanceMode mode) {
   if (!ShouldDrawAnnotation())
     return false;
 
   // It might happen that by the time this annotation instance was created,
-  // it was flagged as "hidden" (e.g. /F 2), and hence CPVT_GenerateAP decided
+  // it was flagged as "hidden" (e.g. /F 2), and hence CPDF_GenerateAP decided
   // to not "generate" its AP.
   // If for a reason the object is no longer hidden, but still does not have
   // its "AP" generated, generate it now.
   GenerateAPIfNeeded();
 
   CFX_Matrix matrix;
-  CPDF_Form* pForm = AnnotGetMatrix(pPage, this, mode, pUser2Device, &matrix);
+  CPDF_Form* pForm = AnnotGetMatrix(pPage, this, mode, mtUser2Device, &matrix);
   if (!pForm)
     return false;
 
-  pContext->AppendLayer(pForm, &matrix);
+  pContext->AppendLayer(pForm, matrix);
   return true;
 }
 
 void CPDF_Annot::DrawBorder(CFX_RenderDevice* pDevice,
-                            const CFX_Matrix* pUser2Device,
-                            const CPDF_RenderOptions* pOptions) {
+                            const CFX_Matrix* pUser2Device) {
   if (GetSubtype() == CPDF_Annot::Subtype::POPUP)
     return;
 
@@ -441,24 +467,23 @@
   if (annot_flags & pdfium::annotation_flags::kHidden)
     return;
 
-  bool bPrinting = pDevice->GetDeviceType() == DeviceType::kPrinter ||
-                   (pOptions && pOptions->GetOptions().bPrintPreview);
+  bool bPrinting = pDevice->GetDeviceType() == DeviceType::kPrinter;
   if (bPrinting && (annot_flags & pdfium::annotation_flags::kPrint) == 0) {
     return;
   }
   if (!bPrinting && (annot_flags & pdfium::annotation_flags::kNoView)) {
     return;
   }
-  CPDF_Dictionary* pBS = m_pAnnotDict->GetDictFor("BS");
+  RetainPtr<const CPDF_Dictionary> pBS = m_pAnnotDict->GetDictFor("BS");
   char style_char;
   float width;
-  CPDF_Array* pDashArray = nullptr;
+  RetainPtr<const CPDF_Array> pDashArray;
   if (!pBS) {
-    CPDF_Array* pBorderArray =
+    RetainPtr<const CPDF_Array> pBorderArray =
         m_pAnnotDict->GetArrayFor(pdfium::annotation::kBorder);
     style_char = 'S';
     if (pBorderArray) {
-      width = pBorderArray->GetNumberAt(2);
+      width = pBorderArray->GetFloatAt(2);
       if (pBorderArray->size() == 4) {
         pDashArray = pBorderArray->GetArrayAt(3);
         if (!pDashArray) {
@@ -467,7 +492,7 @@
         size_t nLen = pDashArray->size();
         size_t i = 0;
         for (; i < nLen; ++i) {
-          CPDF_Object* pObj = pDashArray->GetDirectObjectAt(i);
+          RetainPtr<const CPDF_Object> pObj = pDashArray->GetDirectObjectAt(i);
           if (pObj && pObj->GetInteger()) {
             break;
           }
@@ -481,28 +506,35 @@
       width = 1;
     }
   } else {
-    ByteString style = pBS->GetStringFor("S");
+    ByteString style = pBS->GetByteStringFor("S");
     pDashArray = pBS->GetArrayFor("D");
-    style_char = style[1];
-    width = pBS->GetNumberFor("W");
+    style_char = style[0];
+    width = pBS->GetFloatFor("W");
   }
   if (width <= 0) {
     return;
   }
-  CPDF_Array* pColor = m_pAnnotDict->GetArrayFor(pdfium::annotation::kC);
+  RetainPtr<const CPDF_Array> pColor =
+      m_pAnnotDict->GetArrayFor(pdfium::annotation::kC);
   uint32_t argb = 0xff000000;
   if (pColor) {
-    int R = (int32_t)(pColor->GetNumberAt(0) * 255);
-    int G = (int32_t)(pColor->GetNumberAt(1) * 255);
-    int B = (int32_t)(pColor->GetNumberAt(2) * 255);
+    int R = static_cast<int32_t>(pColor->GetFloatAt(0) * 255);
+    int G = static_cast<int32_t>(pColor->GetFloatAt(1) * 255);
+    int B = static_cast<int32_t>(pColor->GetFloatAt(2) * 255);
     argb = ArgbEncode(0xff, R, G, B);
   }
   CFX_GraphStateData graph_state;
   graph_state.m_LineWidth = width;
+  if (style_char == 'U') {
+    // TODO(https://crbug.com/237527): Handle the "Underline" border style
+    // instead of drawing the rectangle border.
+    return;
+  }
+
   if (style_char == 'D') {
     if (pDashArray) {
       graph_state.m_DashArray =
-          ReadArrayElementsToVector(pDashArray, pDashArray->size());
+          ReadArrayElementsToVector(pDashArray.Get(), pDashArray->size());
       if (graph_state.m_DashArray.size() % 2)
         graph_state.m_DashArray.push_back(graph_state.m_DashArray.back());
     } else {
@@ -512,12 +544,9 @@
 
   CFX_FloatRect rect = GetRect();
   rect.Deflate(width / 2, width / 2);
-  CFX_PathData path;
+
+  CFX_Path path;
   path.AppendFloatRect(rect);
-
-  int fill_type = 0;
-  if (pOptions && pOptions->GetOptions().bNoPathSmooth)
-    fill_type |= FXFILL_NOPATHSMOOTH;
-
-  pDevice->DrawPath(&path, pUser2Device, &graph_state, argb, argb, fill_type);
+  pDevice->DrawPath(path, pUser2Device, &graph_state, argb, argb,
+                    CFX_FillRenderOptions());
 }
diff --git a/core/fpdfdoc/cpdf_annot.h b/core/fpdfdoc/cpdf_annot.h
index 64ab69d..4f8b2f8 100644
--- a/core/fpdfdoc/cpdf_annot.h
+++ b/core/fpdfdoc/cpdf_annot.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,19 @@
 #ifndef CORE_FPDFDOC_CPDF_ANNOT_H_
 #define CORE_FPDFDOC_CPDF_ANNOT_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <map>
 #include <memory>
 
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/maybe_owned.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CFX_RenderDevice;
 class CPDF_Array;
@@ -22,13 +28,11 @@
 class CPDF_Form;
 class CPDF_Page;
 class CPDF_RenderContext;
-class CPDF_RenderOptions;
-class CPDF_Stream;
 
 class CPDF_Annot {
  public:
-  enum AppearanceMode { Normal, Rollover, Down };
-  enum class Subtype {
+  enum class AppearanceMode { kNormal, kRollover, kDown };
+  enum class Subtype : uint8_t {
     UNKNOWN = 0,
     TEXT,
     LINK,
@@ -56,11 +60,12 @@
     WATERMARK,
     THREED,
     RICHMEDIA,
-    XFAWIDGET
+    XFAWIDGET,
+    REDACT
   };
 
-  static CPDF_Annot::Subtype StringToAnnotSubtype(const ByteString& sSubtype);
-  static ByteString AnnotSubtypeToString(CPDF_Annot::Subtype nSubtype);
+  static Subtype StringToAnnotSubtype(const ByteString& sSubtype);
+  static ByteString AnnotSubtypeToString(Subtype nSubtype);
   static CFX_FloatRect RectFromQuadPointsArray(const CPDF_Array* pArray,
                                                size_t nIndex);
   static CFX_FloatRect BoundingRectFromQuadPoints(
@@ -69,41 +74,35 @@
                                           size_t nIndex);
   static size_t QuadPointCount(const CPDF_Array* pArray);
 
-  // The second constructor does not take ownership of the dictionary.
   CPDF_Annot(RetainPtr<CPDF_Dictionary> pDict, CPDF_Document* pDocument);
-  CPDF_Annot(CPDF_Dictionary* pDict, CPDF_Document* pDocument);
   ~CPDF_Annot();
 
-  CPDF_Annot::Subtype GetSubtype() const;
+  Subtype GetSubtype() const;
   uint32_t GetFlags() const;
   CFX_FloatRect GetRect() const;
-  CPDF_Document* GetDocument() const { return m_pDocument.Get(); }
   const CPDF_Dictionary* GetAnnotDict() const { return m_pAnnotDict.Get(); }
-  CPDF_Dictionary* GetAnnotDict() { return m_pAnnotDict.Get(); }
+  RetainPtr<CPDF_Dictionary> GetMutableAnnotDict() { return m_pAnnotDict; }
 
   bool IsHidden() const;
 
   bool DrawAppearance(CPDF_Page* pPage,
                       CFX_RenderDevice* pDevice,
                       const CFX_Matrix& mtUser2Device,
-                      AppearanceMode mode,
-                      const CPDF_RenderOptions* pOptions);
-  bool DrawInContext(const CPDF_Page* pPage,
+                      AppearanceMode mode);
+  bool DrawInContext(CPDF_Page* pPage,
                      CPDF_RenderContext* pContext,
-                     const CFX_Matrix* pUser2Device,
+                     const CFX_Matrix& mtUser2Device,
                      AppearanceMode mode);
 
   void ClearCachedAP();
-  void DrawBorder(CFX_RenderDevice* pDevice,
-                  const CFX_Matrix* pUser2Device,
-                  const CPDF_RenderOptions* pOptions);
-  CPDF_Form* GetAPForm(const CPDF_Page* pPage, AppearanceMode mode);
+  void DrawBorder(CFX_RenderDevice* pDevice, const CFX_Matrix* pUser2Device);
+  CPDF_Form* GetAPForm(CPDF_Page* pPage, AppearanceMode mode);
   void SetOpenState(bool bOpenState) { m_bOpenState = bOpenState; }
-  CPDF_Annot* GetPopupAnnot() const { return m_pPopupAnnot.Get(); }
+  void SetPopupAnnotOpenState(bool bOpenState);
+  absl::optional<CFX_FloatRect> GetPopupAnnotRect() const;
   void SetPopupAnnot(CPDF_Annot* pAnnot) { m_pPopupAnnot = pAnnot; }
 
  private:
-  void Init();
   void GenerateAPIfNeeded();
   bool ShouldGenerateAP() const;
   bool ShouldDrawAnnotation() const;
@@ -112,25 +111,25 @@
 
   RetainPtr<CPDF_Dictionary> const m_pAnnotDict;
   UnownedPtr<CPDF_Document> const m_pDocument;
-  CPDF_Annot::Subtype m_nSubtype;
-  std::map<CPDF_Stream*, std::unique_ptr<CPDF_Form>> m_APMap;
+  std::map<RetainPtr<CPDF_Stream>, std::unique_ptr<CPDF_Form>> m_APMap;
   // If non-null, then this is not a popup annotation.
   UnownedPtr<CPDF_Annot> m_pPopupAnnot;
+  const Subtype m_nSubtype;
+  const bool m_bIsTextMarkupAnnotation;
   // |m_bOpenState| is only set for popup annotations.
   bool m_bOpenState = false;
   bool m_bHasGeneratedAP;
-  bool m_bIsTextMarkupAnnotation;
 };
 
 // Get the AP in an annotation dict for a given appearance mode.
 // If |eMode| is not Normal and there is not AP for that mode, falls back to
 // the Normal AP.
-CPDF_Stream* GetAnnotAP(CPDF_Dictionary* pAnnotDict,
-                        CPDF_Annot::AppearanceMode eMode);
+RetainPtr<CPDF_Stream> GetAnnotAP(CPDF_Dictionary* pAnnotDict,
+                                  CPDF_Annot::AppearanceMode eMode);
 
 // Get the AP in an annotation dict for a given appearance mode.
 // No fallbacks to Normal like in GetAnnotAP.
-CPDF_Stream* GetAnnotAPNoFallback(CPDF_Dictionary* pAnnotDict,
-                                  CPDF_Annot::AppearanceMode eMode);
+RetainPtr<CPDF_Stream> GetAnnotAPNoFallback(CPDF_Dictionary* pAnnotDict,
+                                            CPDF_Annot::AppearanceMode eMode);
 
 #endif  // CORE_FPDFDOC_CPDF_ANNOT_H_
diff --git a/core/fpdfdoc/cpdf_annot_unittest.cpp b/core/fpdfdoc/cpdf_annot_unittest.cpp
index a7625e9..a988719 100644
--- a/core/fpdfdoc/cpdf_annot_unittest.cpp
+++ b/core/fpdfdoc/cpdf_annot_unittest.cpp
@@ -1,10 +1,9 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfdoc/cpdf_annot.h"
 
-#include <memory>
 #include <vector>
 
 #include "core/fpdfapi/parser/cpdf_array.h"
@@ -18,7 +17,7 @@
     const std::vector<int>& points) {
   auto array = pdfium::MakeRetain<CPDF_Array>();
   for (float point : points)
-    array->AddNew<CPDF_Number>(point);
+    array->AppendNew<CPDF_Number>(point);
   return array;
 }
 
@@ -124,14 +123,14 @@
   EXPECT_EQ(0u, CPDF_Annot::QuadPointCount(array.Get()));
 
   for (int i = 0; i < 7; ++i) {
-    array->AddNew<CPDF_Number>(0);
+    array->AppendNew<CPDF_Number>(0);
     EXPECT_EQ(0u, CPDF_Annot::QuadPointCount(array.Get()));
   }
   for (int i = 0; i < 8; ++i) {
-    array->AddNew<CPDF_Number>(0);
+    array->AppendNew<CPDF_Number>(0);
     EXPECT_EQ(1u, CPDF_Annot::QuadPointCount(array.Get()));
   }
   for (int i = 0; i < 50; ++i)
-    array->AddNew<CPDF_Number>(0);
+    array->AppendNew<CPDF_Number>(0);
   EXPECT_EQ(8u, CPDF_Annot::QuadPointCount(array.Get()));
 }
diff --git a/core/fpdfdoc/cpdf_annotlist.cpp b/core/fpdfdoc/cpdf_annotlist.cpp
index 37208e3..ff3ed81 100644
--- a/core/fpdfdoc/cpdf_annotlist.cpp
+++ b/core/fpdfdoc/cpdf_annotlist.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,13 +23,13 @@
 #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"
+#include "core/fpdfdoc/cpdf_generateap.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
-#include "core/fpdfdoc/cpvt_generateap.h"
 #include "core/fxge/cfx_renderdevice.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -49,6 +49,7 @@
     case CPDF_Annot::Subtype::CARET:
     case CPDF_Annot::Subtype::INK:
     case CPDF_Annot::Subtype::FILEATTACHMENT:
+    case CPDF_Annot::Subtype::REDACT:
       return true;
     case CPDF_Annot::Subtype::UNKNOWN:
     case CPDF_Annot::Subtype::LINK:
@@ -64,7 +65,6 @@
     case CPDF_Annot::Subtype::THREED:
     case CPDF_Annot::Subtype::RICHMEDIA:
     case CPDF_Annot::Subtype::XFAWIDGET:
-    default:
       return false;
   }
 }
@@ -79,21 +79,22 @@
   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");
   pAnnotDict->SetNewFor<CPDF_Name>(pdfium::annotation::kSubtype, "Popup");
   pAnnotDict->SetNewFor<CPDF_String>(
       pdfium::form_fields::kT,
-      pParentDict->GetStringFor(pdfium::form_fields::kT), false);
-  pAnnotDict->SetNewFor<CPDF_String>(pdfium::annotation::kContents,
-                                     sContents.ToUTF8(), false);
+      pParentDict->GetByteStringFor(pdfium::form_fields::kT), false);
+  pAnnotDict->SetNewFor<CPDF_String>(pdfium::annotation::kContents, contents,
+                                     /*bHex=*/false);
 
   CFX_FloatRect rect = pParentDict->GetRectFor(pdfium::annotation::kRect);
   rect.Normalize();
@@ -117,37 +118,37 @@
   pAnnotDict->SetNewFor<CPDF_Number>(pdfium::annotation::kF, 0);
 
   auto pPopupAnnot =
-      pdfium::MakeUnique<CPDF_Annot>(std::move(pAnnotDict), pDocument);
+      std::make_unique<CPDF_Annot>(std::move(pAnnotDict), pDocument);
   pAnnot->SetPopupAnnot(pPopupAnnot.get());
   return pPopupAnnot;
 }
 
 void GenerateAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
   if (!pAnnotDict ||
-      pAnnotDict->GetStringFor(pdfium::annotation::kSubtype) != "Widget") {
+      pAnnotDict->GetByteStringFor(pdfium::annotation::kSubtype) != "Widget") {
     return;
   }
 
-  CPDF_Object* pFieldTypeObj =
-      CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kFT);
+  RetainPtr<const CPDF_Object> pFieldTypeObj =
+      CPDF_FormField::GetFieldAttrForDict(pAnnotDict, pdfium::form_fields::kFT);
   if (!pFieldTypeObj)
     return;
 
   ByteString field_type = pFieldTypeObj->GetString();
   if (field_type == pdfium::form_fields::kTx) {
-    CPVT_GenerateAP::GenerateFormAP(pDoc, pAnnotDict,
-                                    CPVT_GenerateAP::kTextField);
+    CPDF_GenerateAP::GenerateFormAP(pDoc, pAnnotDict,
+                                    CPDF_GenerateAP::kTextField);
     return;
   }
 
-  CPDF_Object* pFieldFlagsObj =
-      CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kFf);
+  RetainPtr<const CPDF_Object> pFieldFlagsObj =
+      CPDF_FormField::GetFieldAttrForDict(pAnnotDict, pdfium::form_fields::kFf);
   uint32_t flags = pFieldFlagsObj ? pFieldFlagsObj->GetInteger() : 0;
   if (field_type == pdfium::form_fields::kCh) {
     auto type = (flags & pdfium::form_flags::kChoiceCombo)
-                    ? CPVT_GenerateAP::kComboBox
-                    : CPVT_GenerateAP::kListBox;
-    CPVT_GenerateAP::GenerateFormAP(pDoc, pAnnotDict, type);
+                    ? CPDF_GenerateAP::kComboBox
+                    : CPDF_GenerateAP::kListBox;
+    CPDF_GenerateAP::GenerateFormAP(pDoc, pAnnotDict, type);
     return;
   }
 
@@ -158,53 +159,53 @@
   if (pAnnotDict->KeyExist(pdfium::annotation::kAS))
     return;
 
-  CPDF_Dictionary* pParentDict =
+  RetainPtr<const CPDF_Dictionary> pParentDict =
       pAnnotDict->GetDictFor(pdfium::form_fields::kParent);
   if (!pParentDict || !pParentDict->KeyExist(pdfium::annotation::kAS))
     return;
 
   pAnnotDict->SetNewFor<CPDF_String>(
       pdfium::annotation::kAS,
-      pParentDict->GetStringFor(pdfium::annotation::kAS), false);
+      pParentDict->GetByteStringFor(pdfium::annotation::kAS), false);
 }
 
 }  // namespace
 
 CPDF_AnnotList::CPDF_AnnotList(CPDF_Page* pPage)
     : m_pDocument(pPage->GetDocument()) {
-  CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots");
+  RetainPtr<CPDF_Array> pAnnots = pPage->GetMutableAnnotsArray();
   if (!pAnnots)
     return;
 
   const CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
-  const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
+  RetainPtr<const CPDF_Dictionary> pAcroForm = pRoot->GetDictFor("AcroForm");
   bool bRegenerateAP =
       pAcroForm && pAcroForm->GetBooleanFor("NeedAppearances", false);
   for (size_t i = 0; i < pAnnots->size(); ++i) {
-    CPDF_Dictionary* pDict = ToDictionary(pAnnots->GetDirectObjectAt(i));
+    RetainPtr<CPDF_Dictionary> pDict =
+        ToDictionary(pAnnots->GetMutableDirectObjectAt(i));
     if (!pDict)
       continue;
     const ByteString subtype =
-        pDict->GetStringFor(pdfium::annotation::kSubtype);
+        pDict->GetByteStringFor(pdfium::annotation::kSubtype);
     if (subtype == "Popup") {
       // Skip creating Popup annotations in the PDF document since PDFium
       // provides its own Popup annotations.
       continue;
     }
-    pAnnots->ConvertToIndirectObjectAt(i, m_pDocument.Get());
-    m_AnnotList.push_back(
-        pdfium::MakeUnique<CPDF_Annot>(pDict, m_pDocument.Get()));
+    pAnnots->ConvertToIndirectObjectAt(i, m_pDocument);
+    m_AnnotList.push_back(std::make_unique<CPDF_Annot>(pDict, m_pDocument));
     if (bRegenerateAP && subtype == "Widget" &&
         CPDF_InteractiveForm::IsUpdateAPEnabled() &&
         !pDict->GetDictFor(pdfium::annotation::kAP)) {
-      GenerateAP(m_pDocument.Get(), pDict);
+      GenerateAP(m_pDocument, pDict.Get());
     }
   }
 
   m_nAnnotCount = m_AnnotList.size();
   for (size_t i = 0; i < m_nAnnotCount; ++i) {
     std::unique_ptr<CPDF_Annot> pPopupAnnot =
-        CreatePopupAnnot(m_pDocument.Get(), pPage, m_AnnotList[i].get());
+        CreatePopupAnnot(m_pDocument, pPage, m_AnnotList[i].get());
     if (pPopupAnnot)
       m_AnnotList.push_back(std::move(pPopupAnnot));
   }
@@ -221,14 +222,20 @@
   m_AnnotList.clear();
 }
 
+bool CPDF_AnnotList::Contains(const CPDF_Annot* pAnnot) const {
+  auto it = std::find_if(m_AnnotList.begin(), m_AnnotList.end(),
+                         [pAnnot](const std::unique_ptr<CPDF_Annot>& annot) {
+                           return annot.get() == pAnnot;
+                         });
+  return it != m_AnnotList.end();
+}
+
 void CPDF_AnnotList::DisplayPass(CPDF_Page* pPage,
                                  CFX_RenderDevice* pDevice,
                                  CPDF_RenderContext* pContext,
                                  bool bPrinting,
-                                 const CFX_Matrix* pMatrix,
-                                 bool bWidgetPass,
-                                 CPDF_RenderOptions* pOptions,
-                                 FX_RECT* clip_rect) {
+                                 const CFX_Matrix& mtMatrix,
+                                 bool bWidgetPass) {
   for (const auto& pAnnot : m_AnnotList) {
     bool bWidget = pAnnot->GetSubtype() == CPDF_Annot::Subtype::WIDGET;
     if ((bWidgetPass && !bWidget) || (!bWidgetPass && bWidget))
@@ -244,29 +251,12 @@
     if (!bPrinting && (annot_flags & pdfium::annotation_flags::kNoView))
       continue;
 
-    if (pOptions) {
-      const CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
-      const CPDF_OCContext* pOCContext = pOptions->GetOCContext();
-      if (pAnnotDict && pOCContext &&
-          !pOCContext->CheckOCGVisible(
-              pAnnotDict->GetDictFor(pdfium::annotation::kOC))) {
-        continue;
-      }
-    }
-
-    CFX_Matrix matrix = *pMatrix;
-    if (clip_rect) {
-      FX_RECT annot_rect =
-          matrix.TransformRect(pAnnot->GetRect()).GetOuterRect();
-      annot_rect.Intersect(*clip_rect);
-      if (annot_rect.IsEmpty())
-        continue;
-    }
     if (pContext) {
-      pAnnot->DrawInContext(pPage, pContext, &matrix, CPDF_Annot::Normal);
-    } else if (!pAnnot->DrawAppearance(pPage, pDevice, matrix,
-                                       CPDF_Annot::Normal, pOptions)) {
-      pAnnot->DrawBorder(pDevice, &matrix, pOptions);
+      pAnnot->DrawInContext(pPage, pContext, mtMatrix,
+                            CPDF_Annot::AppearanceMode::kNormal);
+    } else if (!pAnnot->DrawAppearance(pPage, pDevice, mtMatrix,
+                                       CPDF_Annot::AppearanceMode::kNormal)) {
+      pAnnot->DrawBorder(pDevice, &mtMatrix);
     }
   }
 }
@@ -275,29 +265,9 @@
                                    CFX_RenderDevice* pDevice,
                                    CPDF_RenderContext* pContext,
                                    bool bPrinting,
-                                   const CFX_Matrix* pUser2Device,
-                                   uint32_t dwAnnotFlags,
-                                   CPDF_RenderOptions* pOptions,
-                                   FX_RECT* pClipRect) {
-  if (dwAnnotFlags & pdfium::annotation_flags::kInvisible) {
-    DisplayPass(pPage, pDevice, pContext, bPrinting, pUser2Device, false,
-                pOptions, pClipRect);
-  }
-  if (dwAnnotFlags & pdfium::annotation_flags::kHidden) {
-    DisplayPass(pPage, pDevice, pContext, bPrinting, pUser2Device, true,
-                pOptions, pClipRect);
-  }
-}
-
-void CPDF_AnnotList::DisplayAnnots(CPDF_Page* pPage,
-                                   CPDF_RenderContext* pContext,
-                                   bool bPrinting,
-                                   const CFX_Matrix* pMatrix,
-                                   bool bShowWidget,
-                                   CPDF_RenderOptions* pOptions) {
-  uint32_t dwAnnotFlags = bShowWidget ? pdfium::annotation_flags::kInvisible |
-                                            pdfium::annotation_flags::kHidden
-                                      : pdfium::annotation_flags::kInvisible;
-  DisplayAnnots(pPage, nullptr, pContext, bPrinting, pMatrix, dwAnnotFlags,
-                pOptions, nullptr);
+                                   const CFX_Matrix& mtUser2Device,
+                                   bool bShowWidget) {
+  DisplayPass(pPage, pDevice, pContext, bPrinting, mtUser2Device, false);
+  if (bShowWidget)
+    DisplayPass(pPage, pDevice, pContext, bPrinting, mtUser2Device, true);
 }
diff --git a/core/fpdfdoc/cpdf_annotlist.h b/core/fpdfdoc/cpdf_annotlist.h
index 85075cf..ff98c8a 100644
--- a/core/fpdfdoc/cpdf_annotlist.h
+++ b/core/fpdfdoc/cpdf_annotlist.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,14 @@
 #ifndef CORE_FPDFDOC_CPDF_ANNOTLIST_H_
 #define CORE_FPDFDOC_CPDF_ANNOTLIST_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
 #include "core/fpdfapi/render/cpdf_pagerendercontext.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CFX_RenderDevice;
@@ -20,44 +22,30 @@
 class CPDF_Document;
 class CPDF_Page;
 class CPDF_RenderContext;
-class CPDF_RenderOptions;
 
-class CPDF_AnnotList : public CPDF_PageRenderContext::AnnotListIface {
+class CPDF_AnnotList final : public CPDF_PageRenderContext::AnnotListIface {
  public:
   explicit CPDF_AnnotList(CPDF_Page* pPage);
   ~CPDF_AnnotList() override;
 
   void DisplayAnnots(CPDF_Page* pPage,
-                     CPDF_RenderContext* pContext,
-                     bool bPrinting,
-                     const CFX_Matrix* pMatrix,
-                     bool bShowWidget,
-                     CPDF_RenderOptions* pOptions);
-
-  void DisplayAnnots(CPDF_Page* pPage,
                      CFX_RenderDevice* pDevice,
                      CPDF_RenderContext* pContext,
                      bool bPrinting,
-                     const CFX_Matrix* pUser2Device,
-                     uint32_t dwAnnotFlags,
-                     CPDF_RenderOptions* pOptions,
-                     FX_RECT* pClipRect);
+                     const CFX_Matrix& mtUser2Device,
+                     bool bShowWidget);
 
   size_t Count() const { return m_AnnotList.size(); }
   CPDF_Annot* GetAt(size_t index) const { return m_AnnotList[index].get(); }
-  const std::vector<std::unique_ptr<CPDF_Annot>>& All() const {
-    return m_AnnotList;
-  }
+  bool Contains(const CPDF_Annot* pAnnot) const;
 
  private:
   void DisplayPass(CPDF_Page* pPage,
                    CFX_RenderDevice* pDevice,
                    CPDF_RenderContext* pContext,
                    bool bPrinting,
-                   const CFX_Matrix* pMatrix,
-                   bool bWidget,
-                   CPDF_RenderOptions* pOptions,
-                   FX_RECT* clip_rect);
+                   const CFX_Matrix& mtMatrix,
+                   bool bWidget);
 
   UnownedPtr<CPDF_Document> const m_pDocument;
 
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_apsettings.cpp b/core/fpdfdoc/cpdf_apsettings.cpp
index 341764f..e7cd305 100644
--- a/core/fpdfdoc/cpdf_apsettings.cpp
+++ b/core/fpdfdoc/cpdf_apsettings.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,17 +7,19 @@
 #include "core/fpdfdoc/cpdf_apsettings.h"
 
 #include <algorithm>
+#include <utility>
 
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfdoc/cpdf_formcontrol.h"
-#include "core/fxge/cfx_color.h"
 
-CPDF_ApSettings::CPDF_ApSettings(CPDF_Dictionary* pDict) : m_pDict(pDict) {}
+CPDF_ApSettings::CPDF_ApSettings(RetainPtr<CPDF_Dictionary> pDict)
+    : m_pDict(std::move(pDict)) {}
 
 CPDF_ApSettings::CPDF_ApSettings(const CPDF_ApSettings& that) = default;
 
-CPDF_ApSettings::~CPDF_ApSettings() {}
+CPDF_ApSettings::~CPDF_ApSettings() = default;
 
 bool CPDF_ApSettings::HasMKEntry(const ByteString& csEntry) const {
   return m_pDict && m_pDict->KeyExist(csEntry);
@@ -27,91 +29,80 @@
   return m_pDict ? m_pDict->GetIntegerFor("R") : 0;
 }
 
-FX_ARGB CPDF_ApSettings::GetColor(int& iColorType,
-                                  const ByteString& csEntry) const {
-  iColorType = CFX_Color::kTransparent;
+CFX_Color::TypeAndARGB CPDF_ApSettings::GetColorARGB(
+    const ByteString& csEntry) const {
   if (!m_pDict)
-    return 0;
+    return {CFX_Color::Type::kTransparent, 0};
 
-  CPDF_Array* pEntry = m_pDict->GetArrayFor(csEntry);
+  RetainPtr<const CPDF_Array> pEntry = m_pDict->GetArrayFor(csEntry);
   if (!pEntry)
-    return 0;
+    return {CFX_Color::Type::kTransparent, 0};
 
-  FX_ARGB color = 0;
-  size_t dwCount = pEntry->size();
+  const size_t dwCount = pEntry->size();
   if (dwCount == 1) {
-    iColorType = CFX_Color::kGray;
-    float g = pEntry->GetNumberAt(0) * 255;
-    return ArgbEncode(255, (int)g, (int)g, (int)g);
+    const float g = pEntry->GetFloatAt(0) * 255;
+    return {CFX_Color::Type::kGray, ArgbEncode(255, (int)g, (int)g, (int)g)};
   }
   if (dwCount == 3) {
-    iColorType = CFX_Color::kRGB;
-    float r = pEntry->GetNumberAt(0) * 255;
-    float g = pEntry->GetNumberAt(1) * 255;
-    float b = pEntry->GetNumberAt(2) * 255;
-    return ArgbEncode(255, (int)r, (int)g, (int)b);
+    float r = pEntry->GetFloatAt(0) * 255;
+    float g = pEntry->GetFloatAt(1) * 255;
+    float b = pEntry->GetFloatAt(2) * 255;
+    return {CFX_Color::Type::kRGB, ArgbEncode(255, (int)r, (int)g, (int)b)};
   }
   if (dwCount == 4) {
-    iColorType = CFX_Color::kCMYK;
-    float c = pEntry->GetNumberAt(0);
-    float m = pEntry->GetNumberAt(1);
-    float y = pEntry->GetNumberAt(2);
-    float k = pEntry->GetNumberAt(3);
-    float r = 1.0f - std::min(1.0f, c + k);
-    float g = 1.0f - std::min(1.0f, m + k);
-    float b = 1.0f - std::min(1.0f, y + k);
-    return ArgbEncode(255, (int)(r * 255), (int)(g * 255), (int)(b * 255));
+    float c = pEntry->GetFloatAt(0);
+    float m = pEntry->GetFloatAt(1);
+    float y = pEntry->GetFloatAt(2);
+    float k = pEntry->GetFloatAt(3);
+    float r = (1.0f - std::min(1.0f, c + k)) * 255;
+    float g = (1.0f - std::min(1.0f, m + k)) * 255;
+    float b = (1.0f - std::min(1.0f, y + k)) * 255;
+    return {CFX_Color::Type::kCMYK, ArgbEncode(255, (int)r, (int)g, (int)b)};
   }
-  return color;
+  return {CFX_Color::Type::kTransparent, 0};
 }
 
-float CPDF_ApSettings::GetOriginalColor(int index,
-                                        const ByteString& csEntry) const {
+float CPDF_ApSettings::GetOriginalColorComponent(
+    int index,
+    const ByteString& csEntry) const {
   if (!m_pDict)
     return 0;
 
-  CPDF_Array* pEntry = m_pDict->GetArrayFor(csEntry);
-  return pEntry ? pEntry->GetNumberAt(index) : 0;
+  RetainPtr<const CPDF_Array> pEntry = m_pDict->GetArrayFor(csEntry);
+  return pEntry ? pEntry->GetFloatAt(index) : 0;
 }
 
-void CPDF_ApSettings::GetOriginalColor(int& iColorType,
-                                       float fc[4],
-                                       const ByteString& csEntry) const {
-  iColorType = CFX_Color::kTransparent;
-  for (int i = 0; i < 4; i++)
-    fc[i] = 0;
-
+CFX_Color CPDF_ApSettings::GetOriginalColor(const ByteString& csEntry) const {
   if (!m_pDict)
-    return;
+    return CFX_Color();
 
-  CPDF_Array* pEntry = m_pDict->GetArrayFor(csEntry);
+  RetainPtr<const CPDF_Array> pEntry = m_pDict->GetArrayFor(csEntry);
   if (!pEntry)
-    return;
+    return CFX_Color();
 
   size_t dwCount = pEntry->size();
   if (dwCount == 1) {
-    iColorType = CFX_Color::kGray;
-    fc[0] = pEntry->GetNumberAt(0);
-  } else if (dwCount == 3) {
-    iColorType = CFX_Color::kRGB;
-    fc[0] = pEntry->GetNumberAt(0);
-    fc[1] = pEntry->GetNumberAt(1);
-    fc[2] = pEntry->GetNumberAt(2);
-  } else if (dwCount == 4) {
-    iColorType = CFX_Color::kCMYK;
-    fc[0] = pEntry->GetNumberAt(0);
-    fc[1] = pEntry->GetNumberAt(1);
-    fc[2] = pEntry->GetNumberAt(2);
-    fc[3] = pEntry->GetNumberAt(3);
+    return CFX_Color(CFX_Color::Type::kGray, pEntry->GetFloatAt(0));
   }
+  if (dwCount == 3) {
+    return CFX_Color(CFX_Color::Type::kRGB, pEntry->GetFloatAt(0),
+                     pEntry->GetFloatAt(1), pEntry->GetFloatAt(2));
+  }
+  if (dwCount == 4) {
+    return CFX_Color(CFX_Color::Type::kCMYK, pEntry->GetFloatAt(0),
+                     pEntry->GetFloatAt(1), pEntry->GetFloatAt(2),
+                     pEntry->GetFloatAt(3));
+  }
+  return CFX_Color();
 }
 
 WideString CPDF_ApSettings::GetCaption(const ByteString& csEntry) const {
   return m_pDict ? m_pDict->GetUnicodeTextFor(csEntry) : WideString();
 }
 
-CPDF_Stream* CPDF_ApSettings::GetIcon(const ByteString& csEntry) const {
-  return m_pDict ? m_pDict->GetStreamFor(csEntry) : nullptr;
+RetainPtr<CPDF_Stream> CPDF_ApSettings::GetIcon(
+    const ByteString& csEntry) const {
+  return m_pDict ? m_pDict->GetMutableStreamFor(csEntry) : nullptr;
 }
 
 CPDF_IconFit CPDF_ApSettings::GetIconFit() const {
diff --git a/core/fpdfdoc/cpdf_apsettings.h b/core/fpdfdoc/cpdf_apsettings.h
index 2a16336..31e8e60 100644
--- a/core/fpdfdoc/cpdf_apsettings.h
+++ b/core/fpdfdoc/cpdf_apsettings.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,12 +9,11 @@
 
 #include "core/fpdfdoc/cpdf_iconfit.h"
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/cfx_color.h"
+#include "core/fxge/dib/fx_dib.h"
 
 class CPDF_Dictionary;
-class CPDF_FormControl;
 class CPDF_Stream;
 
 // Corresponds to PDF spec section 12.5.6.19 (Widget annotation TP dictionary).
@@ -28,56 +27,25 @@
 
 class CPDF_ApSettings {
  public:
-  explicit CPDF_ApSettings(CPDF_Dictionary* pDict);
+  explicit CPDF_ApSettings(RetainPtr<CPDF_Dictionary> pDict);
   CPDF_ApSettings(const CPDF_ApSettings& that);
   ~CPDF_ApSettings();
 
   bool HasMKEntry(const ByteString& csEntry) const;
   int GetRotation() const;
 
-  FX_ARGB GetBorderColor(int& iColorType) const {
-    return GetColor(iColorType, "BC");
-  }
-
-  float GetOriginalBorderColor(int index) const {
-    return GetOriginalColor(index, "BC");
-  }
-
-  void GetOriginalBorderColor(int& iColorType, float fc[4]) const {
-    GetOriginalColor(iColorType, fc, "BC");
-  }
-
-  FX_ARGB GetBackgroundColor(int& iColorType) const {
-    return GetColor(iColorType, "BG");
-  }
-
-  float GetOriginalBackgroundColor(int index) const {
-    return GetOriginalColor(index, "BG");
-  }
-
-  void GetOriginalBackgroundColor(int& iColorType, float fc[4]) const {
-    GetOriginalColor(iColorType, fc, "BG");
-  }
-
-  WideString GetNormalCaption() const { return GetCaption("CA"); }
-  WideString GetRolloverCaption() const { return GetCaption("RC"); }
-  WideString GetDownCaption() const { return GetCaption("AC"); }
-  CPDF_Stream* GetNormalIcon() const { return GetIcon("I"); }
-  CPDF_Stream* GetRolloverIcon() const { return GetIcon("RI"); }
-  CPDF_Stream* GetDownIcon() const { return GetIcon("IX"); }
   CPDF_IconFit GetIconFit() const;
 
   // Returns one of the TEXTPOS_* values above.
   int GetTextPosition() const;
 
-  FX_ARGB GetColor(int& iColorType, const ByteString& csEntry) const;
-  float GetOriginalColor(int index, const ByteString& csEntry) const;
-  void GetOriginalColor(int& iColorType,
-                        float fc[4],
-                        const ByteString& csEntry) const;
+  CFX_Color::TypeAndARGB GetColorARGB(const ByteString& csEntry) const;
+
+  float GetOriginalColorComponent(int index, const ByteString& csEntry) const;
+  CFX_Color GetOriginalColor(const ByteString& csEntry) const;
 
   WideString GetCaption(const ByteString& csEntry) const;
-  CPDF_Stream* GetIcon(const ByteString& csEntry) const;
+  RetainPtr<CPDF_Stream> GetIcon(const ByteString& csEntry) const;
 
  private:
   RetainPtr<CPDF_Dictionary> const m_pDict;
diff --git a/core/fpdfdoc/cpdf_bafontmap.cpp b/core/fpdfdoc/cpdf_bafontmap.cpp
new file mode 100644
index 0000000..c8617bb
--- /dev/null
+++ b/core/fpdfdoc/cpdf_bafontmap.cpp
@@ -0,0 +1,432 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfdoc/cpdf_bafontmap.h"
+
+#include <memory>
+#include <utility>
+
+#include "constants/annotation_common.h"
+#include "core/fpdfapi/font/cpdf_font.h"
+#include "core/fpdfapi/font/cpdf_fontencoding.h"
+#include "core/fpdfapi/page/cpdf_docpagedata.h"
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_parser.h"
+#include "core/fpdfapi/parser/cpdf_reference.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fpdfdoc/cpdf_defaultappearance.h"
+#include "core/fpdfdoc/cpdf_formfield.h"
+#include "core/fpdfdoc/ipvt_fontmap.h"
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/stl_util.h"
+#include "core/fxge/cfx_fontmapper.h"
+#include "core/fxge/cfx_fontmgr.h"
+#include "core/fxge/cfx_gemodule.h"
+
+namespace {
+
+bool FindNativeTrueTypeFont(ByteStringView sFontFaceName) {
+  CFX_FontMgr* pFontMgr = CFX_GEModule::Get()->GetFontMgr();
+  CFX_FontMapper* pFontMapper = pFontMgr->GetBuiltinMapper();
+  pFontMapper->LoadInstalledFonts();
+  return pFontMapper->HasInstalledFont(sFontFaceName) ||
+         pFontMapper->HasLocalizedFont(sFontFaceName);
+}
+
+RetainPtr<CPDF_Font> AddNativeTrueTypeFontToPDF(CPDF_Document* pDoc,
+                                                ByteString sFontFaceName,
+                                                FX_Charset nCharset) {
+  if (!pDoc)
+    return nullptr;
+
+  auto pFXFont = std::make_unique<CFX_Font>();
+  pFXFont->LoadSubst(sFontFaceName, true, 0, 0, 0,
+                     FX_GetCodePageFromCharset(nCharset), false);
+
+  auto* pDocPageData = CPDF_DocPageData::FromDocument(pDoc);
+  return pDocPageData->AddFont(std::move(pFXFont), nCharset);
+}
+
+ByteString EncodeFontAlias(ByteString sFontName, FX_Charset nCharset) {
+  sFontName.Remove(' ');
+  sFontName += ByteString::Format("_%02X", nCharset);
+  return sFontName;
+}
+
+}  // namespace
+
+CPDF_BAFontMap::Data::Data() = default;
+
+CPDF_BAFontMap::Data::~Data() = default;
+
+CPDF_BAFontMap::CPDF_BAFontMap(CPDF_Document* pDocument,
+                               RetainPtr<CPDF_Dictionary> pAnnotDict,
+                               const ByteString& sAPType)
+    : m_pDocument(pDocument),
+      m_pAnnotDict(std::move(pAnnotDict)),
+      m_sAPType(sAPType) {
+  FX_Charset nCharset = FX_Charset::kDefault;
+  m_pDefaultFont = GetAnnotDefaultFont(&m_sDefaultFontName);
+  if (m_pDefaultFont) {
+    auto maybe_charset = m_pDefaultFont->GetSubstFontCharset();
+    if (maybe_charset.has_value()) {
+      nCharset = maybe_charset.value();
+    } else if (m_sDefaultFontName == "Wingdings" ||
+               m_sDefaultFontName == "Wingdings2" ||
+               m_sDefaultFontName == "Wingdings3" ||
+               m_sDefaultFontName == "Webdings") {
+      nCharset = FX_Charset::kSymbol;
+    } else {
+      nCharset = FX_Charset::kANSI;
+    }
+    AddFontData(m_pDefaultFont, m_sDefaultFontName, nCharset);
+    AddFontToAnnotDict(m_pDefaultFont, m_sDefaultFontName);
+  }
+
+  if (nCharset != FX_Charset::kANSI)
+    GetFontIndex(CFX_Font::kDefaultAnsiFontName, FX_Charset::kANSI, false);
+}
+
+CPDF_BAFontMap::~CPDF_BAFontMap() = default;
+
+RetainPtr<CPDF_Font> CPDF_BAFontMap::GetPDFFont(int32_t nFontIndex) {
+  if (fxcrt::IndexInBounds(m_Data, nFontIndex))
+    return m_Data[nFontIndex]->pFont;
+  return nullptr;
+}
+
+ByteString CPDF_BAFontMap::GetPDFFontAlias(int32_t nFontIndex) {
+  if (fxcrt::IndexInBounds(m_Data, nFontIndex))
+    return m_Data[nFontIndex]->sFontName;
+  return ByteString();
+}
+
+int32_t CPDF_BAFontMap::GetWordFontIndex(uint16_t word,
+                                         FX_Charset nCharset,
+                                         int32_t nFontIndex) {
+  if (nFontIndex > 0) {
+    if (KnowWord(nFontIndex, word))
+      return nFontIndex;
+  } else {
+    if (!m_Data.empty()) {
+      const Data* pData = m_Data.front().get();
+      if (nCharset == FX_Charset::kDefault ||
+          pData->nCharset == FX_Charset::kSymbol ||
+          nCharset == pData->nCharset) {
+        if (KnowWord(0, word))
+          return 0;
+      }
+    }
+  }
+
+  int32_t nNewFontIndex =
+      GetFontIndex(GetCachedNativeFontName(nCharset), nCharset, true);
+  if (nNewFontIndex >= 0) {
+    if (KnowWord(nNewFontIndex, word))
+      return nNewFontIndex;
+  }
+  nNewFontIndex = GetFontIndex(CFX_Font::kUniversalDefaultFontName,
+                               FX_Charset::kDefault, false);
+  if (nNewFontIndex >= 0) {
+    if (KnowWord(nNewFontIndex, word))
+      return nNewFontIndex;
+  }
+  return -1;
+}
+
+int32_t CPDF_BAFontMap::CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) {
+  if (!fxcrt::IndexInBounds(m_Data, nFontIndex))
+    return -1;
+
+  Data* pData = m_Data[nFontIndex].get();
+  if (!pData->pFont)
+    return -1;
+
+  if (pData->pFont->IsUnicodeCompatible())
+    return pData->pFont->CharCodeFromUnicode(word);
+
+  return word < 0xFF ? word : -1;
+}
+
+FX_Charset CPDF_BAFontMap::CharSetFromUnicode(uint16_t word,
+                                              FX_Charset nOldCharset) {
+  // to avoid CJK Font to show ASCII
+  if (word < 0x7F)
+    return FX_Charset::kANSI;
+
+  // follow the old charset
+  if (nOldCharset != FX_Charset::kDefault)
+    return nOldCharset;
+
+  return CFX_Font::GetCharSetFromUnicode(word);
+}
+
+FX_Charset CPDF_BAFontMap::GetNativeCharset() {
+  return FX_GetCharsetFromCodePage(FX_GetACP());
+}
+
+RetainPtr<CPDF_Font> CPDF_BAFontMap::FindFontSameCharset(ByteString* sFontAlias,
+                                                         FX_Charset nCharset) {
+  if (m_pAnnotDict->GetNameFor(pdfium::annotation::kSubtype) != "Widget")
+    return nullptr;
+
+  const CPDF_Dictionary* pRootDict = m_pDocument->GetRoot();
+  if (!pRootDict)
+    return nullptr;
+
+  RetainPtr<const CPDF_Dictionary> pAcroFormDict =
+      pRootDict->GetDictFor("AcroForm");
+  if (!pAcroFormDict)
+    return nullptr;
+
+  RetainPtr<const CPDF_Dictionary> pDRDict = pAcroFormDict->GetDictFor("DR");
+  if (!pDRDict)
+    return nullptr;
+
+  return FindResFontSameCharset(pDRDict.Get(), sFontAlias, nCharset);
+}
+
+RetainPtr<CPDF_Font> CPDF_BAFontMap::FindResFontSameCharset(
+    const CPDF_Dictionary* pResDict,
+    ByteString* sFontAlias,
+    FX_Charset nCharset) {
+  if (!pResDict)
+    return nullptr;
+
+  RetainPtr<const CPDF_Dictionary> pFonts = pResDict->GetDictFor("Font");
+  if (!pFonts)
+    return nullptr;
+
+  RetainPtr<CPDF_Font> pFind;
+  CPDF_DictionaryLocker locker(pFonts);
+  for (const auto& it : locker) {
+    const ByteString& csKey = it.first;
+    RetainPtr<CPDF_Dictionary> pElement =
+        ToDictionary(it.second->GetMutableDirect());
+    if (!ValidateDictType(pElement.Get(), "Font"))
+      continue;
+
+    auto* pData = CPDF_DocPageData::FromDocument(m_pDocument);
+    RetainPtr<CPDF_Font> pFont = pData->GetFont(std::move(pElement));
+    if (!pFont)
+      continue;
+
+    auto maybe_charset = pFont->GetSubstFontCharset();
+    if (maybe_charset.has_value() && maybe_charset.value() == nCharset) {
+      *sFontAlias = csKey;
+      pFind = std::move(pFont);
+    }
+  }
+  return pFind;
+}
+
+RetainPtr<CPDF_Font> CPDF_BAFontMap::GetAnnotDefaultFont(ByteString* sAlias) {
+  RetainPtr<CPDF_Dictionary> pAcroFormDict;
+  const bool bWidget =
+      (m_pAnnotDict->GetNameFor(pdfium::annotation::kSubtype) == "Widget");
+  if (bWidget) {
+    RetainPtr<CPDF_Dictionary> pRootDict = m_pDocument->GetMutableRoot();
+    if (pRootDict)
+      pAcroFormDict = pRootDict->GetMutableDictFor("AcroForm");
+  }
+
+  ByteString sDA;
+  RetainPtr<const CPDF_Object> pObj =
+      CPDF_FormField::GetFieldAttrForDict(m_pAnnotDict.Get(), "DA");
+  if (pObj)
+    sDA = pObj->GetString();
+
+  if (bWidget) {
+    if (sDA.IsEmpty()) {
+      pObj = CPDF_FormField::GetFieldAttrForDict(pAcroFormDict.Get(), "DA");
+      sDA = pObj ? pObj->GetString() : ByteString();
+    }
+  }
+  if (sDA.IsEmpty())
+    return nullptr;
+
+  CPDF_DefaultAppearance appearance(sDA);
+  float font_size;
+  absl::optional<ByteString> font = appearance.GetFont(&font_size);
+  *sAlias = font.value_or(ByteString());
+
+  RetainPtr<CPDF_Dictionary> pFontDict;
+  if (RetainPtr<CPDF_Dictionary> pAPDict =
+          m_pAnnotDict->GetMutableDictFor(pdfium::annotation::kAP)) {
+    if (RetainPtr<CPDF_Dictionary> pNormalDict =
+            pAPDict->GetMutableDictFor("N")) {
+      if (RetainPtr<CPDF_Dictionary> pNormalResDict =
+              pNormalDict->GetMutableDictFor("Resources")) {
+        if (RetainPtr<CPDF_Dictionary> pResFontDict =
+                pNormalResDict->GetMutableDictFor("Font")) {
+          pFontDict = pResFontDict->GetMutableDictFor(*sAlias);
+        }
+      }
+    }
+  }
+  if (bWidget && !pFontDict && pAcroFormDict) {
+    if (RetainPtr<CPDF_Dictionary> pDRDict =
+            pAcroFormDict->GetMutableDictFor("DR")) {
+      if (RetainPtr<CPDF_Dictionary> pDRFontDict =
+              pDRDict->GetMutableDictFor("Font")) {
+        pFontDict = pDRFontDict->GetMutableDictFor(*sAlias);
+      }
+    }
+  }
+  if (!pFontDict)
+    return nullptr;
+
+  return CPDF_DocPageData::FromDocument(m_pDocument)->GetFont(pFontDict);
+}
+
+void CPDF_BAFontMap::AddFontToAnnotDict(const RetainPtr<CPDF_Font>& pFont,
+                                        const ByteString& sAlias) {
+  if (!pFont)
+    return;
+
+  RetainPtr<CPDF_Dictionary> pAPDict =
+      m_pAnnotDict->GetOrCreateDictFor(pdfium::annotation::kAP);
+
+  // to avoid checkbox and radiobutton
+  if (ToDictionary(pAPDict->GetObjectFor(m_sAPType)))
+    return;
+
+  RetainPtr<CPDF_Stream> pStream = pAPDict->GetMutableStreamFor(m_sAPType);
+  if (!pStream) {
+    pStream = m_pDocument->NewIndirect<CPDF_Stream>();
+    pAPDict->SetNewFor<CPDF_Reference>(m_sAPType, m_pDocument,
+                                       pStream->GetObjNum());
+  }
+
+  RetainPtr<CPDF_Dictionary> pStreamDict = pStream->GetMutableDict();
+  if (!pStreamDict) {
+    pStreamDict = m_pDocument->New<CPDF_Dictionary>();
+    pStream->InitStreamWithEmptyData(pStreamDict);
+  }
+
+  RetainPtr<CPDF_Dictionary> pStreamResList =
+      pStreamDict->GetOrCreateDictFor("Resources");
+  RetainPtr<CPDF_Dictionary> pStreamResFontList =
+      pStreamResList->GetMutableDictFor("Font");
+  if (!pStreamResFontList) {
+    pStreamResFontList = m_pDocument->NewIndirect<CPDF_Dictionary>();
+    pStreamResList->SetNewFor<CPDF_Reference>("Font", m_pDocument,
+                                              pStreamResFontList->GetObjNum());
+  }
+  if (!pStreamResFontList->KeyExist(sAlias)) {
+    RetainPtr<const CPDF_Dictionary> pFontDict = pFont->GetFontDict();
+    RetainPtr<CPDF_Object> pObject =
+        pFontDict->IsInline() ? pFontDict->Clone()
+                              : pFontDict->MakeReference(m_pDocument);
+    pStreamResFontList->SetFor(sAlias, std::move(pObject));
+  }
+}
+
+bool CPDF_BAFontMap::KnowWord(int32_t nFontIndex, uint16_t word) {
+  return fxcrt::IndexInBounds(m_Data, nFontIndex) &&
+         CharCodeFromUnicode(nFontIndex, word) >= 0;
+}
+
+int32_t CPDF_BAFontMap::GetFontIndex(const ByteString& sFontName,
+                                     FX_Charset nCharset,
+                                     bool bFind) {
+  int32_t nFontIndex = FindFont(EncodeFontAlias(sFontName, nCharset), nCharset);
+  if (nFontIndex >= 0)
+    return nFontIndex;
+
+  ByteString sAlias;
+  RetainPtr<CPDF_Font> pFont =
+      bFind ? FindFontSameCharset(&sAlias, nCharset) : nullptr;
+  if (!pFont) {
+    pFont = AddFontToDocument(sFontName, nCharset);
+    sAlias = EncodeFontAlias(sFontName, nCharset);
+  }
+  AddFontToAnnotDict(pFont, sAlias);
+  return AddFontData(pFont, sAlias, nCharset);
+}
+
+int32_t CPDF_BAFontMap::AddFontData(const RetainPtr<CPDF_Font>& pFont,
+                                    const ByteString& sFontAlias,
+                                    FX_Charset nCharset) {
+  auto pNewData = std::make_unique<Data>();
+  pNewData->pFont = pFont;
+  pNewData->sFontName = sFontAlias;
+  pNewData->nCharset = nCharset;
+  m_Data.push_back(std::move(pNewData));
+  return fxcrt::CollectionSize<int32_t>(m_Data) - 1;
+}
+
+int32_t CPDF_BAFontMap::FindFont(const ByteString& sFontName,
+                                 FX_Charset nCharset) {
+  int32_t i = 0;
+  for (const auto& pData : m_Data) {
+    if ((nCharset == FX_Charset::kDefault || nCharset == pData->nCharset) &&
+        (sFontName.IsEmpty() || pData->sFontName == sFontName)) {
+      return i;
+    }
+    ++i;
+  }
+  return -1;
+}
+
+ByteString CPDF_BAFontMap::GetNativeFontName(FX_Charset nCharset) {
+  if (nCharset == FX_Charset::kDefault)
+    nCharset = GetNativeCharset();
+
+  ByteString sFontName = CFX_Font::GetDefaultFontNameByCharset(nCharset);
+  if (!FindNativeTrueTypeFont(sFontName.AsStringView()))
+    return ByteString();
+
+  return sFontName;
+}
+
+ByteString CPDF_BAFontMap::GetCachedNativeFontName(FX_Charset nCharset) {
+  for (const auto& pData : m_NativeFont) {
+    if (pData && pData->nCharset == nCharset)
+      return pData->sFontName;
+  }
+
+  ByteString sNew = GetNativeFontName(nCharset);
+  if (sNew.IsEmpty())
+    return ByteString();
+
+  auto pNewData = std::make_unique<Native>();
+  pNewData->nCharset = nCharset;
+  pNewData->sFontName = sNew;
+  m_NativeFont.push_back(std::move(pNewData));
+  return sNew;
+}
+
+RetainPtr<CPDF_Font> CPDF_BAFontMap::AddFontToDocument(ByteString sFontName,
+                                                       FX_Charset nCharset) {
+  if (CFX_FontMapper::IsStandardFontName(sFontName))
+    return AddStandardFont(sFontName);
+
+  return AddSystemFont(sFontName, nCharset);
+}
+
+RetainPtr<CPDF_Font> CPDF_BAFontMap::AddStandardFont(ByteString sFontName) {
+  auto* pPageData = CPDF_DocPageData::FromDocument(m_pDocument);
+  if (sFontName == "ZapfDingbats")
+    return pPageData->AddStandardFont(sFontName, nullptr);
+
+  static const CPDF_FontEncoding fe(FontEncoding::kWinAnsi);
+  return pPageData->AddStandardFont(sFontName, &fe);
+}
+
+RetainPtr<CPDF_Font> CPDF_BAFontMap::AddSystemFont(ByteString sFontName,
+                                                   FX_Charset nCharset) {
+  if (sFontName.IsEmpty())
+    sFontName = GetNativeFontName(nCharset);
+
+  if (nCharset == FX_Charset::kDefault)
+    nCharset = GetNativeCharset();
+
+  return AddNativeTrueTypeFontToPDF(m_pDocument, sFontName, nCharset);
+}
diff --git a/core/fpdfdoc/cpdf_bafontmap.h b/core/fpdfdoc/cpdf_bafontmap.h
new file mode 100644
index 0000000..5cd4bcd
--- /dev/null
+++ b/core/fpdfdoc/cpdf_bafontmap.h
@@ -0,0 +1,89 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFDOC_CPDF_BAFONTMAP_H_
+#define CORE_FPDFDOC_CPDF_BAFONTMAP_H_
+
+#include <memory>
+#include <vector>
+
+#include "core/fpdfdoc/ipvt_fontmap.h"
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+
+class CPDF_Dictionary;
+class CPDF_Document;
+
+class CPDF_BAFontMap final : public IPVT_FontMap {
+ public:
+  static FX_Charset GetNativeCharset();
+
+  CPDF_BAFontMap(CPDF_Document* pDocument,
+                 RetainPtr<CPDF_Dictionary> pAnnotDict,
+                 const ByteString& sAPType);
+  ~CPDF_BAFontMap() override;
+
+  // IPVT_FontMap:
+  RetainPtr<CPDF_Font> GetPDFFont(int32_t nFontIndex) override;
+  ByteString GetPDFFontAlias(int32_t nFontIndex) override;
+  int32_t GetWordFontIndex(uint16_t word,
+                           FX_Charset nCharset,
+                           int32_t nFontIndex) override;
+  int32_t CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) override;
+  FX_Charset CharSetFromUnicode(uint16_t word, FX_Charset nOldCharset) override;
+
+ private:
+  struct Data {
+    Data();
+    ~Data();
+
+    FX_Charset nCharset = FX_Charset::kANSI;
+    RetainPtr<CPDF_Font> pFont;
+    ByteString sFontName;
+  };
+
+  struct Native {
+    FX_Charset nCharset;
+    ByteString sFontName;
+  };
+
+  RetainPtr<CPDF_Font> FindFontSameCharset(ByteString* sFontAlias,
+                                           FX_Charset nCharset);
+  RetainPtr<CPDF_Font> FindResFontSameCharset(const CPDF_Dictionary* pResDict,
+                                              ByteString* sFontAlias,
+                                              FX_Charset nCharset);
+  RetainPtr<CPDF_Font> GetAnnotDefaultFont(ByteString* sAlias);
+  void AddFontToAnnotDict(const RetainPtr<CPDF_Font>& pFont,
+                          const ByteString& sAlias);
+
+  bool KnowWord(int32_t nFontIndex, uint16_t word);
+
+  int32_t GetFontIndex(const ByteString& sFontName,
+                       FX_Charset nCharset,
+                       bool bFind);
+  int32_t AddFontData(const RetainPtr<CPDF_Font>& pFont,
+                      const ByteString& sFontAlias,
+                      FX_Charset nCharset);
+
+  int32_t FindFont(const ByteString& sFontName, FX_Charset nCharset);
+  ByteString GetNativeFontName(FX_Charset nCharset);
+  ByteString GetCachedNativeFontName(FX_Charset nCharset);
+  RetainPtr<CPDF_Font> AddFontToDocument(ByteString sFontName,
+                                         FX_Charset nCharset);
+  RetainPtr<CPDF_Font> AddStandardFont(ByteString sFontName);
+  RetainPtr<CPDF_Font> AddSystemFont(ByteString sFontName, FX_Charset nCharset);
+
+  std::vector<std::unique_ptr<Data>> m_Data;
+  std::vector<std::unique_ptr<Native>> m_NativeFont;
+  UnownedPtr<CPDF_Document> const m_pDocument;
+  RetainPtr<CPDF_Dictionary> const m_pAnnotDict;
+  RetainPtr<CPDF_Font> m_pDefaultFont;
+  ByteString m_sDefaultFontName;
+  const ByteString m_sAPType;
+};
+
+#endif  // CORE_FPDFDOC_CPDF_BAFONTMAP_H_
diff --git a/core/fpdfdoc/cpdf_bafontmap_unittest.cpp b/core/fpdfdoc/cpdf_bafontmap_unittest.cpp
new file mode 100644
index 0000000..25e7da5
--- /dev/null
+++ b/core/fpdfdoc/cpdf_bafontmap_unittest.cpp
@@ -0,0 +1,63 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfdoc/cpdf_bafontmap.h"
+
+#include <utility>
+
+#include "build/build_config.h"
+#include "constants/annotation_common.h"
+#include "core/fpdfapi/font/cpdf_font.h"
+#include "core/fpdfapi/page/test_with_page_module.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fpdfapi/parser/cpdf_test_document.h"
+
+using BAFontMapTest = TestWithPageModule;
+
+TEST_F(BAFontMapTest, DefaultFont) {
+  // Without any font resources, CPDF_BAFontMap generates a default font.
+  CPDF_TestDocument doc;
+
+  auto annot_dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  annot_dict->SetNewFor<CPDF_Name>(pdfium::annotation::kSubtype, "Widget");
+  annot_dict->SetNewFor<CPDF_String>("DA", "0 0 0 rg /F1 12 Tf",
+                                     /*bHex=*/false);
+
+  CPDF_BAFontMap font_map(&doc, std::move(annot_dict), "N");
+#if !BUILDFLAG(IS_WIN)
+  // TODO(thestig): Figure out why this does not work on Windows.
+  EXPECT_EQ(font_map.GetPDFFontAlias(0), "Helvetica_00");
+#endif
+  RetainPtr<CPDF_Font> font = font_map.GetPDFFont(0);
+  ASSERT_TRUE(font);
+  EXPECT_TRUE(font->IsType1Font());
+  EXPECT_EQ(font->GetBaseFontName(), "Helvetica");
+}
+
+TEST_F(BAFontMapTest, Bug853238) {
+  CPDF_TestDocument doc;
+  auto root_dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  auto acroform_dict = root_dict->SetNewFor<CPDF_Dictionary>("AcroForm");
+  auto annot_dr_dict = acroform_dict->SetNewFor<CPDF_Dictionary>("DR");
+  auto annot_font_dict = annot_dr_dict->SetNewFor<CPDF_Dictionary>("Font");
+  auto annot_font_f1_dict = annot_font_dict->SetNewFor<CPDF_Dictionary>("F1");
+  annot_font_f1_dict->SetNewFor<CPDF_Name>("Type", "Font");
+  annot_font_f1_dict->SetNewFor<CPDF_Name>("Subtype", "Type1");
+  annot_font_f1_dict->SetNewFor<CPDF_Name>("BaseFont", "Times-Roman");
+  doc.SetRoot(root_dict);
+
+  auto annot_dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  annot_dict->SetNewFor<CPDF_Name>(pdfium::annotation::kSubtype, "Widget");
+  annot_dict->SetNewFor<CPDF_String>("DA", "0 0 0 rg /F1 12 Tf",
+                                     /*bHex=*/false);
+
+  CPDF_BAFontMap font_map(&doc, std::move(annot_dict), "N");
+  EXPECT_EQ(font_map.GetPDFFontAlias(0), "F1");
+  RetainPtr<CPDF_Font> font = font_map.GetPDFFont(0);
+  ASSERT_TRUE(font);
+  EXPECT_TRUE(font->IsType1Font());
+  EXPECT_EQ(font->GetBaseFontName(), "Times-Roman");
+}
diff --git a/core/fpdfdoc/cpdf_bookmark.cpp b/core/fpdfdoc/cpdf_bookmark.cpp
index b08fba5..f4176b9 100644
--- a/core/fpdfdoc/cpdf_bookmark.cpp
+++ b/core/fpdfdoc/cpdf_bookmark.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,21 +6,20 @@
 
 #include "core/fpdfdoc/cpdf_bookmark.h"
 
-#include <memory>
-#include <vector>
+#include <utility>
 
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfdoc/cpdf_nametree.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxge/dib/fx_dib.h"
 
 CPDF_Bookmark::CPDF_Bookmark() = default;
 
 CPDF_Bookmark::CPDF_Bookmark(const CPDF_Bookmark& that) = default;
 
-CPDF_Bookmark::CPDF_Bookmark(const CPDF_Dictionary* pDict) : m_pDict(pDict) {}
+CPDF_Bookmark::CPDF_Bookmark(RetainPtr<const CPDF_Dictionary> pDict)
+    : m_pDict(std::move(pDict)) {}
 
 CPDF_Bookmark::~CPDF_Bookmark() = default;
 
@@ -28,17 +27,18 @@
   if (!m_pDict)
     return WideString();
 
-  const CPDF_String* pString = ToString(m_pDict->GetDirectObjectFor("Title"));
+  RetainPtr<const CPDF_String> pString =
+      ToString(m_pDict->GetDirectObjectFor("Title"));
   if (!pString)
     return WideString();
 
   WideString title = pString->GetUnicodeText();
-  int len = title.GetLength();
+  size_t len = title.GetLength();
   if (!len)
     return WideString();
 
-  std::vector<wchar_t, FxAllocAllocator<wchar_t>> buf(len);
-  for (int i = 0; i < len; i++) {
+  DataVector<wchar_t> buf(len);
+  for (size_t i = 0; i < len; i++) {
     wchar_t w = title[i];
     buf[i] = w > 0x20 ? w : 0x20;
   }
@@ -47,21 +47,14 @@
 
 CPDF_Dest CPDF_Bookmark::GetDest(CPDF_Document* pDocument) const {
   if (!m_pDict)
-    return CPDF_Dest();
-
-  const CPDF_Object* pDest = m_pDict->GetDirectObjectFor("Dest");
-  if (!pDest)
-    return CPDF_Dest();
-  if (pDest->IsString() || pDest->IsName()) {
-    CPDF_NameTree name_tree(pDocument, "Dests");
-    return CPDF_Dest(
-        name_tree.LookupNamedDest(pDocument, pDest->GetUnicodeText()));
-  }
-  if (const CPDF_Array* pArray = pDest->AsArray())
-    return CPDF_Dest(pArray);
-  return CPDF_Dest();
+    return CPDF_Dest(nullptr);
+  return CPDF_Dest::Create(pDocument, m_pDict->GetDirectObjectFor("Dest"));
 }
 
 CPDF_Action CPDF_Bookmark::GetAction() const {
   return CPDF_Action(m_pDict ? m_pDict->GetDictFor("A") : nullptr);
 }
+
+int CPDF_Bookmark::GetCount() const {
+  return m_pDict->GetIntegerFor("Count");
+}
diff --git a/core/fpdfdoc/cpdf_bookmark.h b/core/fpdfdoc/cpdf_bookmark.h
index b185c03..11a9d18 100644
--- a/core/fpdfdoc/cpdf_bookmark.h
+++ b/core/fpdfdoc/cpdf_bookmark.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,8 +9,8 @@
 
 #include "core/fpdfdoc/cpdf_action.h"
 #include "core/fpdfdoc/cpdf_dest.h"
-#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/widestring.h"
 
 class CPDF_Dictionary;
 class CPDF_Document;
@@ -19,7 +19,7 @@
  public:
   CPDF_Bookmark();
   CPDF_Bookmark(const CPDF_Bookmark& that);
-  explicit CPDF_Bookmark(const CPDF_Dictionary* pDict);
+  explicit CPDF_Bookmark(RetainPtr<const CPDF_Dictionary> pDict);
   ~CPDF_Bookmark();
 
   const CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
@@ -27,6 +27,7 @@
   WideString GetTitle() const;
   CPDF_Dest GetDest(CPDF_Document* pDocument) const;
   CPDF_Action GetAction() const;
+  int GetCount() const;
 
  private:
   RetainPtr<const CPDF_Dictionary> m_pDict;
diff --git a/core/fpdfdoc/cpdf_bookmarktree.cpp b/core/fpdfdoc/cpdf_bookmarktree.cpp
index 5c4fffe..1f1aea9 100644
--- a/core/fpdfdoc/cpdf_bookmarktree.cpp
+++ b/core/fpdfdoc/cpdf_bookmarktree.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,32 +6,37 @@
 
 #include "core/fpdfdoc/cpdf_bookmarktree.h"
 
+#include <utility>
+
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 
-CPDF_BookmarkTree::CPDF_BookmarkTree(CPDF_Document* pDoc) : m_pDocument(pDoc) {}
+CPDF_BookmarkTree::CPDF_BookmarkTree(const CPDF_Document* doc)
+    : document_(doc) {}
 
 CPDF_BookmarkTree::~CPDF_BookmarkTree() = default;
 
-CPDF_Bookmark CPDF_BookmarkTree::GetFirstChild(CPDF_Bookmark* parent) const {
-  const CPDF_Dictionary* pParentDict = parent->GetDict();
-  if (pParentDict)
-    return CPDF_Bookmark(pParentDict->GetDictFor("First"));
+CPDF_Bookmark CPDF_BookmarkTree::GetFirstChild(
+    const CPDF_Bookmark& parent) const {
+  const CPDF_Dictionary* parent_dict = parent.GetDict();
+  if (parent_dict)
+    return CPDF_Bookmark(parent_dict->GetDictFor("First"));
 
-  CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
-  if (!pRoot)
+  const CPDF_Dictionary* root = document_->GetRoot();
+  if (!root)
     return CPDF_Bookmark();
 
-  CPDF_Dictionary* pOutlines = pRoot->GetDictFor("Outlines");
-  return pOutlines ? CPDF_Bookmark(pOutlines->GetDictFor("First"))
-                   : CPDF_Bookmark();
+  RetainPtr<const CPDF_Dictionary> outlines = root->GetDictFor("Outlines");
+  return outlines ? CPDF_Bookmark(outlines->GetDictFor("First"))
+                  : CPDF_Bookmark();
 }
 
-CPDF_Bookmark CPDF_BookmarkTree::GetNextSibling(CPDF_Bookmark* bookmark) const {
-  const CPDF_Dictionary* pDict = bookmark->GetDict();
-  if (!pDict)
+CPDF_Bookmark CPDF_BookmarkTree::GetNextSibling(
+    const CPDF_Bookmark& bookmark) const {
+  const CPDF_Dictionary* dict = bookmark.GetDict();
+  if (!dict)
     return CPDF_Bookmark();
 
-  const CPDF_Dictionary* pNext = pDict->GetDictFor("Next");
-  return pNext == pDict ? CPDF_Bookmark() : CPDF_Bookmark(pNext);
+  RetainPtr<const CPDF_Dictionary> next = dict->GetDictFor("Next");
+  return next != dict ? CPDF_Bookmark(std::move(next)) : CPDF_Bookmark();
 }
diff --git a/core/fpdfdoc/cpdf_bookmarktree.h b/core/fpdfdoc/cpdf_bookmarktree.h
index b374bfa..144879b 100644
--- a/core/fpdfdoc/cpdf_bookmarktree.h
+++ b/core/fpdfdoc/cpdf_bookmarktree.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,15 +14,14 @@
 
 class CPDF_BookmarkTree {
  public:
-  explicit CPDF_BookmarkTree(CPDF_Document* pDoc);
+  explicit CPDF_BookmarkTree(const CPDF_Document* doc);
   ~CPDF_BookmarkTree();
 
-  CPDF_Bookmark GetFirstChild(CPDF_Bookmark* parent) const;
-  CPDF_Bookmark GetNextSibling(CPDF_Bookmark* bookmark) const;
-  CPDF_Document* GetDocument() const { return m_pDocument.Get(); }
+  CPDF_Bookmark GetFirstChild(const CPDF_Bookmark& parent) const;
+  CPDF_Bookmark GetNextSibling(const CPDF_Bookmark& bookmark) const;
 
  private:
-  UnownedPtr<CPDF_Document> const m_pDocument;
+  UnownedPtr<const CPDF_Document> const document_;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_BOOKMARKTREE_H_
diff --git a/core/fpdfdoc/cpdf_color_utils.cpp b/core/fpdfdoc/cpdf_color_utils.cpp
index 198b1ca..b4e1d86 100644
--- a/core/fpdfdoc/cpdf_color_utils.cpp
+++ b/core/fpdfdoc/cpdf_color_utils.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,6 +9,7 @@
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfdoc/cpdf_defaultappearance.h"
 #include "core/fxcrt/bytestring.h"
+#include "third_party/base/notreached.h"
 
 namespace fpdfdoc {
 
@@ -16,16 +17,16 @@
   CFX_Color rt;
   switch (array.size()) {
     case 1:
-      rt = CFX_Color(CFX_Color::kGray, array.GetNumberAt(0));
+      rt = CFX_Color(CFX_Color::Type::kGray, array.GetFloatAt(0));
       break;
     case 3:
-      rt = CFX_Color(CFX_Color::kRGB, array.GetNumberAt(0),
-                     array.GetNumberAt(1), array.GetNumberAt(2));
+      rt = CFX_Color(CFX_Color::Type::kRGB, array.GetFloatAt(0),
+                     array.GetFloatAt(1), array.GetFloatAt(2));
       break;
     case 4:
-      rt = CFX_Color(CFX_Color::kCMYK, array.GetNumberAt(0),
-                     array.GetNumberAt(1), array.GetNumberAt(2),
-                     array.GetNumberAt(3));
+      rt = CFX_Color(CFX_Color::Type::kCMYK, array.GetFloatAt(0),
+                     array.GetFloatAt(1), array.GetFloatAt(2),
+                     array.GetFloatAt(3));
       break;
   }
   return rt;
@@ -33,21 +34,7 @@
 
 CFX_Color CFXColorFromString(const ByteString& str) {
   CPDF_DefaultAppearance appearance(str);
-  float values[4];
-  Optional<CFX_Color::Type> color_type = appearance.GetColor(values);
-  if (!color_type || *color_type == CFX_Color::kTransparent)
-    return CFX_Color(CFX_Color::kTransparent);
-  if (*color_type == CFX_Color::kGray)
-    return CFX_Color(CFX_Color::kGray, values[0]);
-  if (*color_type == CFX_Color::kRGB)
-    return CFX_Color(CFX_Color::kRGB, values[0], values[1], values[2]);
-  if (*color_type == CFX_Color::kCMYK) {
-    return CFX_Color(CFX_Color::kCMYK, values[0], values[1], values[2],
-                     values[3]);
-  }
-
-  NOTREACHED();
-  return CFX_Color(CFX_Color::kTransparent);
+  return appearance.GetColor().value_or(CFX_Color());
 }
 
 }  // namespace fpdfdoc
diff --git a/core/fpdfdoc/cpdf_color_utils.h b/core/fpdfdoc/cpdf_color_utils.h
index 993a7d7..05998cb 100644
--- a/core/fpdfdoc/cpdf_color_utils.h
+++ b/core/fpdfdoc/cpdf_color_utils.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fpdfdoc/cpdf_defaultappearance.cpp b/core/fpdfdoc/cpdf_defaultappearance.cpp
index e6ef4f7..e3b91a9 100644
--- a/core/fpdfdoc/cpdf_defaultappearance.cpp
+++ b/core/fpdfdoc/cpdf_defaultappearance.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,9 @@
 
 #include "core/fpdfapi/parser/cpdf_simple_parser.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fxcrt/fx_string.h"
 #include "core/fxge/cfx_color.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
@@ -27,7 +29,7 @@
   int buf_count = 0;
 
   parser->SetCurPos(0);
-  while (1) {
+  while (true) {
     pBuf[buf_index++] = parser->GetCurPos();
     if (buf_index == nParams)
       buf_index = 0;
@@ -48,15 +50,25 @@
       return true;
     }
   }
-  return false;
 }
 
 }  // namespace
 
-Optional<ByteString> CPDF_DefaultAppearance::GetFont(float* fFontSize) {
+CPDF_DefaultAppearance::CPDF_DefaultAppearance() = default;
+
+CPDF_DefaultAppearance::CPDF_DefaultAppearance(const ByteString& csDA)
+    : m_csDA(csDA) {}
+
+CPDF_DefaultAppearance::CPDF_DefaultAppearance(
+    const CPDF_DefaultAppearance& cDA) = default;
+
+CPDF_DefaultAppearance::~CPDF_DefaultAppearance() = default;
+
+absl::optional<ByteString> CPDF_DefaultAppearance::GetFont(
+    float* fFontSize) const {
   *fFontSize = 0.0f;
   if (m_csDA.IsEmpty())
-    return {};
+    return absl::nullopt;
 
   ByteString csFontNameTag;
   CPDF_SimpleParser syntax(m_csDA.AsStringView().raw_span());
@@ -65,67 +77,67 @@
     csFontNameTag.Delete(0, 1);
     *fFontSize = StringToFloat(syntax.GetWord());
   }
-  return {PDF_NameDecode(csFontNameTag.AsStringView())};
+  return PDF_NameDecode(csFontNameTag.AsStringView());
 }
 
-Optional<CFX_Color::Type> CPDF_DefaultAppearance::GetColor(float fc[4]) {
-  for (int c = 0; c < 4; c++)
-    fc[c] = 0;
-
+absl::optional<CFX_Color> CPDF_DefaultAppearance::GetColor() const {
   if (m_csDA.IsEmpty())
-    return {};
+    return absl::nullopt;
 
   CPDF_SimpleParser syntax(m_csDA.AsStringView().raw_span());
   if (FindTagParamFromStart(&syntax, "g", 1)) {
-    fc[0] = StringToFloat(syntax.GetWord());
-    return {CFX_Color::kGray};
+    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::kRGB};
+    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::kCMYK};
+    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 {};
+  return absl::nullopt;
 }
 
-std::pair<Optional<CFX_Color::Type>, FX_ARGB>
-CPDF_DefaultAppearance::GetColor() {
-  float values[4];
-  Optional<CFX_Color::Type> type = GetColor(values);
-  if (!type)
-    return {type, 0};
+absl::optional<CFX_Color::TypeAndARGB> CPDF_DefaultAppearance::GetColorARGB()
+    const {
+  absl::optional<CFX_Color> maybe_color = GetColor();
+  if (!maybe_color.has_value())
+    return absl::nullopt;
 
-  if (*type == CFX_Color::kGray) {
-    int g = static_cast<int>(values[0] * 255 + 0.5f);
-    return {type, ArgbEncode(255, g, g, g)};
+  const CFX_Color& color = maybe_color.value();
+  if (color.nColorType == CFX_Color::Type::kGray) {
+    int g = static_cast<int>(color.fColor1 * 255 + 0.5f);
+    return CFX_Color::TypeAndARGB(CFX_Color::Type::kGray,
+                                  ArgbEncode(255, g, g, g));
   }
-  if (*type == CFX_Color::kRGB) {
-    int r = static_cast<int>(values[0] * 255 + 0.5f);
-    int g = static_cast<int>(values[1] * 255 + 0.5f);
-    int b = static_cast<int>(values[2] * 255 + 0.5f);
-    return {type, ArgbEncode(255, r, g, b)};
+  if (color.nColorType == CFX_Color::Type::kRGB) {
+    int r = static_cast<int>(color.fColor1 * 255 + 0.5f);
+    int g = static_cast<int>(color.fColor2 * 255 + 0.5f);
+    int b = static_cast<int>(color.fColor3 * 255 + 0.5f);
+    return CFX_Color::TypeAndARGB(CFX_Color::Type::kRGB,
+                                  ArgbEncode(255, r, g, b));
   }
-  if (*type == CFX_Color::kCMYK) {
-    float r = 1.0f - std::min(1.0f, values[0] + values[3]);
-    float g = 1.0f - std::min(1.0f, values[1] + values[3]);
-    float b = 1.0f - std::min(1.0f, values[2] + values[3]);
-    return {type, ArgbEncode(255, static_cast<int>(r * 255 + 0.5f),
-                             static_cast<int>(g * 255 + 0.5f),
-                             static_cast<int>(b * 255 + 0.5f))};
+  if (color.nColorType == CFX_Color::Type::kCMYK) {
+    float r = 1.0f - std::min(1.0f, color.fColor1 + color.fColor4);
+    float g = 1.0f - std::min(1.0f, color.fColor2 + color.fColor4);
+    float b = 1.0f - std::min(1.0f, color.fColor3 + color.fColor4);
+    return CFX_Color::TypeAndARGB(
+        CFX_Color::Type::kCMYK,
+        ArgbEncode(255, static_cast<int>(r * 255 + 0.5f),
+                   static_cast<int>(g * 255 + 0.5f),
+                   static_cast<int>(b * 255 + 0.5f)));
   }
-  NOTREACHED();
-  return {{}, 0};
+  NOTREACHED_NORETURN();
 }
 
+// static
 bool CPDF_DefaultAppearance::FindTagParamFromStartForTesting(
     CPDF_SimpleParser* parser,
     ByteStringView token,
diff --git a/core/fpdfdoc/cpdf_defaultappearance.h b/core/fpdfdoc/cpdf_defaultappearance.h
index 97762f8..42389ec 100644
--- a/core/fpdfdoc/cpdf_defaultappearance.h
+++ b/core/fpdfdoc/cpdf_defaultappearance.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,32 +7,30 @@
 #ifndef CORE_FPDFDOC_CPDF_DEFAULTAPPEARANCE_H_
 #define CORE_FPDFDOC_CPDF_DEFAULTAPPEARANCE_H_
 
-#include <utility>
-
-#include "core/fpdfapi/parser/cpdf_simple_parser.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxge/cfx_color.h"
-#include "core/fxge/fx_dib.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+class CPDF_SimpleParser;
 
 class CPDF_DefaultAppearance {
  public:
-  CPDF_DefaultAppearance() {}
-  explicit CPDF_DefaultAppearance(const ByteString& csDA) : m_csDA(csDA) {}
-  CPDF_DefaultAppearance(const CPDF_DefaultAppearance& cDA)
-      : m_csDA(cDA.m_csDA) {}
+  CPDF_DefaultAppearance();
+  explicit CPDF_DefaultAppearance(const ByteString& csDA);
+  CPDF_DefaultAppearance(const CPDF_DefaultAppearance& cDA);
+  ~CPDF_DefaultAppearance();
 
-  Optional<ByteString> GetFont(float* fFontSize);
+  absl::optional<ByteString> GetFont(float* fFontSize) const;
 
-  Optional<CFX_Color::Type> GetColor(float fc[4]);
-  std::pair<Optional<CFX_Color::Type>, FX_ARGB> GetColor();
+  absl::optional<CFX_Color> GetColor() const;
+  absl::optional<CFX_Color::TypeAndARGB> GetColorARGB() const;
 
-  bool FindTagParamFromStartForTesting(CPDF_SimpleParser* parser,
-                                       ByteStringView token,
-                                       int nParams);
+  static bool FindTagParamFromStartForTesting(CPDF_SimpleParser* parser,
+                                              ByteStringView token,
+                                              int nParams);
 
  private:
-  ByteString m_csDA;
+  const ByteString m_csDA;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_DEFAULTAPPEARANCE_H_
diff --git a/core/fpdfdoc/cpdf_defaultappearance_unittest.cpp b/core/fpdfdoc/cpdf_defaultappearance_unittest.cpp
index 6f6c525..ce08bf3 100644
--- a/core/fpdfdoc/cpdf_defaultappearance_unittest.cpp
+++ b/core/fpdfdoc/cpdf_defaultappearance_unittest.cpp
@@ -1,12 +1,15 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfdoc/cpdf_defaultappearance.h"
 
+#include <iterator>
+
+#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 {
@@ -35,13 +38,12 @@
       STR_IN_TEST_CASE("1 2 3 4 5 6 7 8 cm", "cm", 6, true, 3),
   };
 
-  CPDF_DefaultAppearance da;
-  for (size_t i = 0; i < FX_ArraySize(test_data); ++i) {
+  for (size_t i = 0; i < std::size(test_data); ++i) {
     CPDF_SimpleParser parser(
         pdfium::make_span(test_data[i].input, test_data[i].input_size));
     EXPECT_EQ(test_data[i].result,
-              da.FindTagParamFromStartForTesting(&parser, test_data[i].token,
-                                                 test_data[i].num_params))
+              CPDF_DefaultAppearance::FindTagParamFromStartForTesting(
+                  &parser, test_data[i].token, test_data[i].num_params))
         << " for case " << i;
     EXPECT_EQ(test_data[i].result_pos, parser.GetCurPos()) << " for case " << i;
   }
diff --git a/core/fpdfdoc/cpdf_dest.cpp b/core/fpdfdoc/cpdf_dest.cpp
index 1a07acf..b7682e1 100644
--- a/core/fpdfdoc/cpdf_dest.cpp
+++ b/core/fpdfdoc/cpdf_dest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,42 +7,54 @@
 #include "core/fpdfdoc/cpdf_dest.h"
 
 #include <algorithm>
+#include <iterator>
+#include <utility>
 
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fpdfdoc/cpdf_nametree.h"
 
 namespace {
 
 // These arrays are indexed by the PDFDEST_VIEW_* constants.
 
 // Last element is a sentinel.
-const char* const g_sZoomModes[] = {"Unknown", "XYZ",  "Fit",  "FitH",
-                                    "FitV",    "FitR", "FitB", "FitBH",
-                                    "FitBV",   nullptr};
+const char* const kZoomModes[] = {"Unknown", "XYZ",  "Fit",   "FitH",  "FitV",
+                                  "FitR",    "FitB", "FitBH", "FitBV", nullptr};
 
-const uint8_t g_sZoomModeMaxParamCount[] = {0, 3, 0, 1, 1, 4, 0, 1, 1, 0};
+constexpr uint8_t kZoomModeMaxParamCount[] = {0, 3, 0, 1, 1, 4, 0, 1, 1, 0};
 
-static_assert(FX_ArraySize(g_sZoomModes) ==
-                  FX_ArraySize(g_sZoomModeMaxParamCount),
+static_assert(std::size(kZoomModes) == std::size(kZoomModeMaxParamCount),
               "Zoom mode count Mismatch");
 
 }  // namespace
 
-CPDF_Dest::CPDF_Dest() {}
-
-CPDF_Dest::CPDF_Dest(const CPDF_Array* pArray) : m_pArray(pArray) {}
+CPDF_Dest::CPDF_Dest(RetainPtr<const CPDF_Array> pArray)
+    : m_pArray(std::move(pArray)) {}
 
 CPDF_Dest::CPDF_Dest(const CPDF_Dest& that) = default;
 
-CPDF_Dest::~CPDF_Dest() {}
+CPDF_Dest::~CPDF_Dest() = default;
+
+// static
+CPDF_Dest CPDF_Dest::Create(CPDF_Document* pDoc,
+                            RetainPtr<const CPDF_Object> pDest) {
+  if (!pDest)
+    return CPDF_Dest(nullptr);
+
+  if (pDest->IsString() || pDest->IsName())
+    return CPDF_Dest(CPDF_NameTree::LookupNamedDest(pDoc, pDest->GetString()));
+
+  return CPDF_Dest(ToArray(pDest));
+}
 
 int CPDF_Dest::GetDestPageIndex(CPDF_Document* pDoc) const {
   if (!m_pArray)
     return -1;
 
-  const CPDF_Object* pPage = m_pArray->GetDirectObjectAt(0);
+  RetainPtr<const CPDF_Object> pPage = m_pArray->GetDirectObjectAt(0);
   if (!pPage)
     return -1;
 
@@ -55,17 +67,28 @@
   return pDoc->GetPageIndex(pPage->GetObjNum());
 }
 
+std::vector<float> CPDF_Dest::GetScrollPositionArray() const {
+  std::vector<float> result;
+  if (m_pArray) {
+    // Skip over index 0 which contains destination page details, and index 1
+    // which contains a parameter that describes the rest of the array.
+    for (size_t i = 2; i < m_pArray->size(); i++)
+      result.push_back(m_pArray->GetFloatAt(i));
+  }
+  return result;
+}
+
 int CPDF_Dest::GetZoomMode() const {
   if (!m_pArray)
     return 0;
 
-  const CPDF_Object* pArray = m_pArray->GetDirectObjectAt(1);
+  RetainPtr<const CPDF_Object> pArray = m_pArray->GetDirectObjectAt(1);
   if (!pArray)
     return 0;
 
   ByteString mode = pArray->GetString();
-  for (int i = 1; g_sZoomModes[i]; ++i) {
-    if (mode == g_sZoomModes[i])
+  for (int i = 1; kZoomModes[i]; ++i) {
+    if (mode == kZoomModes[i])
       return i;
   }
 
@@ -88,13 +111,14 @@
   if (m_pArray->size() < 5)
     return false;
 
-  const CPDF_Name* xyz = ToName(m_pArray->GetDirectObjectAt(1));
+  RetainPtr<const CPDF_Name> xyz = ToName(m_pArray->GetDirectObjectAt(1));
   if (!xyz || xyz->GetString() != "XYZ")
     return false;
 
-  const CPDF_Number* numX = ToNumber(m_pArray->GetDirectObjectAt(2));
-  const CPDF_Number* numY = ToNumber(m_pArray->GetDirectObjectAt(3));
-  const CPDF_Number* numZoom = ToNumber(m_pArray->GetDirectObjectAt(4));
+  RetainPtr<const CPDF_Number> numX = ToNumber(m_pArray->GetDirectObjectAt(2));
+  RetainPtr<const CPDF_Number> numY = ToNumber(m_pArray->GetDirectObjectAt(3));
+  RetainPtr<const CPDF_Number> numZoom =
+      ToNumber(m_pArray->GetDirectObjectAt(4));
 
   // If the value is a CPDF_Null then ToNumber will return nullptr.
   *pHasX = !!numX;
@@ -118,15 +142,15 @@
   return true;
 }
 
-unsigned long CPDF_Dest::GetNumParams() const {
+size_t CPDF_Dest::GetNumParams() const {
   if (!m_pArray || m_pArray->size() < 2)
     return 0;
 
-  unsigned long maxParamsForFitType = g_sZoomModeMaxParamCount[GetZoomMode()];
-  unsigned long numParamsInArray = m_pArray->size() - 2;
+  size_t maxParamsForFitType = kZoomModeMaxParamCount[GetZoomMode()];
+  size_t numParamsInArray = m_pArray->size() - 2;
   return std::min(maxParamsForFitType, numParamsInArray);
 }
 
-float CPDF_Dest::GetParam(int index) const {
-  return m_pArray ? m_pArray->GetNumberAt(2 + index) : 0;
+float CPDF_Dest::GetParam(size_t index) const {
+  return m_pArray ? m_pArray->GetFloatAt(2 + index) : 0;
 }
diff --git a/core/fpdfdoc/cpdf_dest.h b/core/fpdfdoc/cpdf_dest.h
index c215634..c32c7da 100644
--- a/core/fpdfdoc/cpdf_dest.h
+++ b/core/fpdfdoc/cpdf_dest.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,29 +7,34 @@
 #ifndef CORE_FPDFDOC_CPDF_DEST_H_
 #define CORE_FPDFDOC_CPDF_DEST_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include <vector>
+
+#include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Document;
-class CPDF_Array;
+class CPDF_Object;
 
 class CPDF_Dest {
  public:
-  CPDF_Dest();
-  explicit CPDF_Dest(const CPDF_Array* pArray);
+  explicit CPDF_Dest(RetainPtr<const CPDF_Array> pArray);
   CPDF_Dest(const CPDF_Dest& that);
   ~CPDF_Dest();
 
+  // Use when |pDest| is an object of an unknown type. Can pass in nullptr.
+  static CPDF_Dest Create(CPDF_Document* pDoc,
+                          RetainPtr<const CPDF_Object> pDest);
+
   const CPDF_Array* GetArray() const { return m_pArray.Get(); }
+
   int GetDestPageIndex(CPDF_Document* pDoc) const;
+  std::vector<float> GetScrollPositionArray() const;
 
   // Returns the zoom mode, as one of the PDFDEST_VIEW_* values in fpdf_doc.h.
   int GetZoomMode() const;
 
-  unsigned long GetNumParams() const;
-  float GetParam(int index) const;
-
+  size_t GetNumParams() const;
+  float GetParam(size_t index) const;
   bool GetXYZ(bool* pHasX,
               bool* pHasY,
               bool* pHasZoom,
diff --git a/core/fpdfdoc/cpdf_dest_unittest.cpp b/core/fpdfdoc/cpdf_dest_unittest.cpp
index 07e46b3..ae2d979 100644
--- a/core/fpdfdoc/cpdf_dest_unittest.cpp
+++ b/core/fpdfdoc/cpdf_dest_unittest.cpp
@@ -1,15 +1,16 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfdoc/cpdf_dest.h"
 
+#include <memory>
+
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_null.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
 TEST(cpdf_dest, GetXYZ) {
   bool hasX;
@@ -21,23 +22,23 @@
 
   // |array| must outlive |dest|.
   auto array = pdfium::MakeRetain<CPDF_Array>();
-  array->AddNew<CPDF_Number>(0);  // Page Index.
-  array->AddNew<CPDF_Name>("XYZ");
-  array->AddNew<CPDF_Number>(4);  // X
+  array->AppendNew<CPDF_Number>(0);  // Page Index.
+  array->AppendNew<CPDF_Name>("XYZ");
+  array->AppendNew<CPDF_Number>(4);  // X
   {
-    auto dest = pdfium::MakeUnique<CPDF_Dest>();
-    EXPECT_FALSE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom));
+    CPDF_Dest dest(nullptr);
+    EXPECT_FALSE(dest.GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom));
   }
   {
     // Not enough entries.
-    auto dest = pdfium::MakeUnique<CPDF_Dest>(array.Get());
-    EXPECT_FALSE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom));
+    CPDF_Dest dest(array);
+    EXPECT_FALSE(dest.GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom));
   }
-  array->AddNew<CPDF_Number>(5);  // Y
-  array->AddNew<CPDF_Number>(6);  // Zoom.
+  array->AppendNew<CPDF_Number>(5);  // Y
+  array->AppendNew<CPDF_Number>(6);  // Zoom.
   {
-    auto dest = pdfium::MakeUnique<CPDF_Dest>(array.Get());
-    EXPECT_TRUE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom));
+    CPDF_Dest dest(array);
+    EXPECT_TRUE(dest.GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom));
     EXPECT_TRUE(hasX);
     EXPECT_TRUE(hasY);
     EXPECT_TRUE(hasZoom);
@@ -48,8 +49,8 @@
   // Set zoom to 0.
   array->SetNewAt<CPDF_Number>(4, 0);
   {
-    auto dest = pdfium::MakeUnique<CPDF_Dest>(array.Get());
-    EXPECT_TRUE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom));
+    CPDF_Dest dest(array);
+    EXPECT_TRUE(dest.GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom));
     EXPECT_FALSE(hasZoom);
   }
   // Set values to null.
@@ -57,8 +58,8 @@
   array->SetNewAt<CPDF_Null>(3);
   array->SetNewAt<CPDF_Null>(4);
   {
-    auto dest = pdfium::MakeUnique<CPDF_Dest>(array.Get());
-    EXPECT_TRUE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom));
+    CPDF_Dest dest(array);
+    EXPECT_TRUE(dest.GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom));
     EXPECT_FALSE(hasX);
     EXPECT_FALSE(hasY);
     EXPECT_FALSE(hasZoom);
diff --git a/core/fpdfdoc/cpdf_filespec.cpp b/core/fpdfdoc/cpdf_filespec.cpp
index 506e176..2e982b2 100644
--- a/core/fpdfdoc/cpdf_filespec.cpp
+++ b/core/fpdfdoc/cpdf_filespec.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,8 @@
 
 #include "core/fpdfdoc/cpdf_filespec.h"
 
-#include <vector>
+#include <iterator>
+#include <utility>
 
 #include "build/build_config.h"
 #include "constants/stream_dict_common.h"
@@ -17,15 +18,17 @@
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fxcrt/fx_system.h"
+#include "third_party/base/check.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
-#if defined(OS_MACOSX) || defined(OS_WIN)
+#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN)
 WideString ChangeSlashToPlatform(const wchar_t* str) {
   WideString result;
   while (*str) {
     if (*str == '/') {
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
       result += L':';
 #else
       result += L'\\';
@@ -50,30 +53,26 @@
   }
   return result;
 }
-#endif  // defined(OS_MACOSX) || defined(OS_WIN)
+#endif  // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN)
 
 }  // namespace
 
-CPDF_FileSpec::CPDF_FileSpec(const CPDF_Object* pObj) : m_pObj(pObj) {
-  ASSERT(m_pObj);
+CPDF_FileSpec::CPDF_FileSpec(RetainPtr<const CPDF_Object> pObj)
+    : m_pObj(std::move(pObj)) {
+  DCHECK(m_pObj);
 }
 
-CPDF_FileSpec::CPDF_FileSpec(CPDF_Object* pObj)
-    : m_pObj(pObj), m_pWritableObj(pObj) {
-  ASSERT(m_pObj);
-}
-
-CPDF_FileSpec::~CPDF_FileSpec() {}
+CPDF_FileSpec::~CPDF_FileSpec() = default;
 
 WideString CPDF_FileSpec::DecodeFileName(const WideString& filepath) {
   if (filepath.GetLength() <= 1)
     return WideString();
 
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
   if (filepath.First(sizeof("/Mac") - 1) == WideStringView(L"/Mac"))
     return ChangeSlashToPlatform(filepath.c_str() + 1);
   return ChangeSlashToPlatform(filepath.c_str());
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
 
   if (filepath[0] != L'/')
     return ChangeSlashToPlatform(filepath.c_str());
@@ -98,21 +97,23 @@
 WideString CPDF_FileSpec::GetFileName() const {
   WideString csFileName;
   if (const CPDF_Dictionary* pDict = m_pObj->AsDictionary()) {
-    const CPDF_String* pUF = ToString(pDict->GetDirectObjectFor("UF"));
+    RetainPtr<const CPDF_String> pUF =
+        ToString(pDict->GetDirectObjectFor("UF"));
     if (pUF)
       csFileName = pUF->GetUnicodeText();
     if (csFileName.IsEmpty()) {
-      const CPDF_String* pK =
+      RetainPtr<const CPDF_String> pK =
           ToString(pDict->GetDirectObjectFor(pdfium::stream::kF));
       if (pK)
         csFileName = WideString::FromDefANSI(pK->GetString().AsStringView());
     }
-    if (pDict->GetStringFor("FS") == "URL")
+    if (pDict->GetByteStringFor("FS") == "URL")
       return csFileName;
 
     if (csFileName.IsEmpty()) {
       for (const auto* key : {"DOS", "Mac", "Unix"}) {
-        const CPDF_String* pValue = ToString(pDict->GetDirectObjectFor(key));
+        RetainPtr<const CPDF_String> pValue =
+            ToString(pDict->GetDirectObjectFor(key));
         if (pValue) {
           csFileName =
               WideString::FromDefANSI(pValue->GetString().AsStringView());
@@ -126,24 +127,24 @@
   return DecodeFileName(csFileName);
 }
 
-const CPDF_Stream* CPDF_FileSpec::GetFileStream() const {
+RetainPtr<const CPDF_Stream> CPDF_FileSpec::GetFileStream() const {
   const CPDF_Dictionary* pDict = m_pObj->AsDictionary();
   if (!pDict)
     return nullptr;
 
   // Get the embedded files dictionary.
-  const CPDF_Dictionary* pFiles = pDict->GetDictFor("EF");
+  RetainPtr<const CPDF_Dictionary> pFiles = pDict->GetDictFor("EF");
   if (!pFiles)
     return nullptr;
 
   // List of keys to check for the file specification string.
   // Follows the same precedence order as GetFileName().
   static constexpr const char* kKeys[] = {"UF", "F", "DOS", "Mac", "Unix"};
-  size_t end = pDict->GetStringFor("FS") == "URL" ? 2 : FX_ArraySize(kKeys);
+  size_t end = pDict->GetByteStringFor("FS") == "URL" ? 2 : std::size(kKeys);
   for (size_t i = 0; i < end; ++i) {
     ByteString key = kKeys[i];
     if (!pDict->GetUnicodeTextFor(key).IsEmpty()) {
-      const CPDF_Stream* pStream = pFiles->GetStreamFor(key);
+      RetainPtr<const CPDF_Stream> pStream = pFiles->GetStreamFor(key);
       if (pStream)
         return pStream;
     }
@@ -151,30 +152,25 @@
   return nullptr;
 }
 
-CPDF_Stream* CPDF_FileSpec::GetFileStream() {
-  return const_cast<CPDF_Stream*>(
-      static_cast<const CPDF_FileSpec*>(this)->GetFileStream());
-}
-
-const CPDF_Dictionary* CPDF_FileSpec::GetParamsDict() const {
-  const CPDF_Stream* pStream = GetFileStream();
+RetainPtr<const CPDF_Dictionary> CPDF_FileSpec::GetParamsDict() const {
+  RetainPtr<const CPDF_Stream> pStream = GetFileStream();
   if (!pStream)
     return nullptr;
 
-  const CPDF_Dictionary* pDict = pStream->GetDict();
+  RetainPtr<const CPDF_Dictionary> pDict = pStream->GetDict();
   return pDict ? pDict->GetDictFor("Params") : nullptr;
 }
 
-CPDF_Dictionary* CPDF_FileSpec::GetParamsDict() {
-  return const_cast<CPDF_Dictionary*>(
-      static_cast<const CPDF_FileSpec*>(this)->GetParamsDict());
+RetainPtr<CPDF_Dictionary> CPDF_FileSpec::GetMutableParamsDict() {
+  return pdfium::WrapRetain(
+      const_cast<CPDF_Dictionary*>(GetParamsDict().Get()));
 }
 
 WideString CPDF_FileSpec::EncodeFileName(const WideString& filepath) {
   if (filepath.GetLength() <= 1)
     return WideString();
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   if (filepath[1] == L':') {
     WideString result(L'/');
     result += filepath[0];
@@ -190,7 +186,7 @@
   if (filepath[0] == L'\\')
     return L'/' + ChangeSlashToPDF(filepath.c_str());
   return ChangeSlashToPDF(filepath.c_str());
-#elif defined(OS_MACOSX)
+#elif BUILDFLAG(IS_APPLE)
   if (filepath.First(sizeof("Mac") - 1).EqualsASCII("Mac"))
     return L'/' + ChangeSlashToPDF(filepath.c_str());
   return ChangeSlashToPDF(filepath.c_str());
@@ -198,18 +194,3 @@
   return WideString(filepath);
 #endif
 }
-
-void CPDF_FileSpec::SetFileName(const WideString& wsFileName) {
-  if (!m_pWritableObj) {
-    NOTREACHED();
-    return;
-  }
-
-  WideString wsStr = EncodeFileName(wsFileName);
-  if (m_pObj->IsString()) {
-    m_pWritableObj->SetString(wsStr.ToDefANSI());
-  } else if (CPDF_Dictionary* pDict = m_pWritableObj->AsDictionary()) {
-    pDict->SetNewFor<CPDF_String>(pdfium::stream::kF, wsStr.ToDefANSI(), false);
-    pDict->SetNewFor<CPDF_String>("UF", wsStr);
-  }
-}
diff --git a/core/fpdfdoc/cpdf_filespec.h b/core/fpdfdoc/cpdf_filespec.h
index 820b948..d943811 100644
--- a/core/fpdfdoc/cpdf_filespec.h
+++ b/core/fpdfdoc/cpdf_filespec.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,10 @@
 #ifndef CORE_FPDFDOC_CPDF_FILESPEC_H_
 #define CORE_FPDFDOC_CPDF_FILESPEC_H_
 
-#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/string_pool_template.h"
 #include "core/fxcrt/weak_ptr.h"
+#include "core/fxcrt/widestring.h"
 
 class CPDF_Dictionary;
 class CPDF_Object;
@@ -18,8 +18,7 @@
 
 class CPDF_FileSpec {
  public:
-  explicit CPDF_FileSpec(const CPDF_Object* pObj);
-  explicit CPDF_FileSpec(CPDF_Object* pObj);
+  explicit CPDF_FileSpec(RetainPtr<const CPDF_Object> pObj);
   ~CPDF_FileSpec();
 
   // Convert a platform dependent file name into pdf format.
@@ -28,20 +27,13 @@
   // Convert a pdf file name into platform dependent format.
   static WideString DecodeFileName(const WideString& filepath);
 
-  const CPDF_Object* GetObj() const { return m_pObj.Get(); }
-  CPDF_Object* GetObj() { return m_pWritableObj.Get(); }
   WideString GetFileName() const;
-  const CPDF_Stream* GetFileStream() const;
-  CPDF_Stream* GetFileStream();
-  const CPDF_Dictionary* GetParamsDict() const;
-  CPDF_Dictionary* GetParamsDict();
-
-  // Set this file spec to refer to a file name (not a url).
-  void SetFileName(const WideString& wsFileName);
+  RetainPtr<const CPDF_Stream> GetFileStream() const;
+  RetainPtr<const CPDF_Dictionary> GetParamsDict() const;
+  RetainPtr<CPDF_Dictionary> GetMutableParamsDict();
 
  private:
   RetainPtr<const CPDF_Object> const m_pObj;
-  RetainPtr<CPDF_Object> const m_pWritableObj;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_FILESPEC_H_
diff --git a/core/fpdfdoc/cpdf_filespec_unittest.cpp b/core/fpdfdoc/cpdf_filespec_unittest.cpp
index a075c5e..7c517a9 100644
--- a/core/fpdfdoc/cpdf_filespec_unittest.cpp
+++ b/core/fpdfdoc/cpdf_filespec_unittest.cpp
@@ -1,7 +1,9 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "core/fpdfdoc/cpdf_filespec.h"
+
 #include <memory>
 #include <utility>
 #include <vector>
@@ -12,11 +14,9 @@
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfdoc/cpdf_filespec.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/data_vector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/test_support.h"
-#include "third_party/base/ptr_util.h"
 
 TEST(cpdf_filespec, EncodeDecodeFileName) {
   static const std::vector<pdfium::NullTermWstrFuncTestData> test_data = {
@@ -24,7 +24,7 @@
     {L"", L""},
     // only file name.
     {L"test.pdf", L"test.pdf"},
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     // With drive identifier.
     {L"r:\\pdfdocs\\spec.pdf", L"/r/pdfdocs/spec.pdf"},
     // Relative path.
@@ -35,7 +35,7 @@
     {L"\\\\pdfdocs\\spec.pdf", L"/pdfdocs/spec.pdf"},
 // Network resource name. It is not supported yet.
 // {L"pclib/eng:\\pdfdocs\\spec.pdf", L"/pclib/eng/pdfdocs/spec.pdf"},
-#elif defined(OS_MACOSX)
+#elif BUILDFLAG(IS_APPLE)
     // Absolute path with colon separator.
     {L"Mac HD:PDFDocs:spec.pdf", L"/Mac HD/PDFDocs/spec.pdf"},
     // Relative path with colon separator.
@@ -62,10 +62,10 @@
   {
     // String object.
     static const pdfium::NullTermWstrFuncTestData test_data = {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
       L"/C/docs/test.pdf",
       L"C:\\docs\\test.pdf"
-#elif defined(OS_MACOSX)
+#elif BUILDFLAG(IS_APPLE)
       L"/Mac HD/docs/test.pdf",
       L"Mac HD:docs:test.pdf"
 #else
@@ -74,19 +74,19 @@
 #endif
     };
     auto str_obj = pdfium::MakeRetain<CPDF_String>(nullptr, test_data.input);
-    CPDF_FileSpec file_spec(str_obj.Get());
+    CPDF_FileSpec file_spec(str_obj);
     EXPECT_STREQ(test_data.expected, file_spec.GetFileName().c_str());
   }
   {
     // Dictionary object.
     static const pdfium::NullTermWstrFuncTestData test_data[] = {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
       {L"/C/docs/test.pdf", L"C:\\docs\\test.pdf"},
       {L"/D/docs/test.pdf", L"D:\\docs\\test.pdf"},
       {L"/E/docs/test.pdf", L"E:\\docs\\test.pdf"},
       {L"/F/docs/test.pdf", L"F:\\docs\\test.pdf"},
       {L"/G/docs/test.pdf", L"G:\\docs\\test.pdf"},
-#elif defined(OS_MACOSX)
+#elif BUILDFLAG(IS_APPLE)
       {L"/Mac HD/docs1/test.pdf", L"Mac HD:docs1:test.pdf"},
       {L"/Mac HD/docs2/test.pdf", L"Mac HD:docs2:test.pdf"},
       {L"/Mac HD/docs3/test.pdf", L"Mac HD:docs3:test.pdf"},
@@ -102,12 +102,11 @@
     };
     // Keyword fields in reverse order of precedence to retrieve the file name.
     const char* const keywords[] = {"Unix", "Mac", "DOS", "F", "UF"};
-    static_assert(FX_ArraySize(test_data) == FX_ArraySize(keywords),
-                  "size mismatch");
+    static_assert(std::size(test_data) == std::size(keywords), "size mismatch");
     auto dict_obj = pdfium::MakeRetain<CPDF_Dictionary>();
-    CPDF_FileSpec file_spec(dict_obj.Get());
+    CPDF_FileSpec file_spec(dict_obj);
     EXPECT_TRUE(file_spec.GetFileName().IsEmpty());
-    for (size_t i = 0; i < FX_ArraySize(keywords); ++i) {
+    for (size_t i = 0; i < std::size(keywords); ++i) {
       dict_obj->SetNewFor<CPDF_String>(keywords[i], test_data[i].input);
       EXPECT_STREQ(test_data[i].expected, file_spec.GetFileName().c_str());
     }
@@ -120,13 +119,13 @@
   {
     // Invalid object.
     auto name_obj = pdfium::MakeRetain<CPDF_Name>(nullptr, "test.pdf");
-    CPDF_FileSpec file_spec(name_obj.Get());
+    CPDF_FileSpec file_spec(name_obj);
     EXPECT_TRUE(file_spec.GetFileName().IsEmpty());
   }
   {
     // Invalid CPDF_Name objects in dictionary. See https://crbug.com/959183
     auto dict_obj = pdfium::MakeRetain<CPDF_Dictionary>();
-    CPDF_FileSpec file_spec(dict_obj.Get());
+    CPDF_FileSpec file_spec(dict_obj);
     for (const char* key : {"Unix", "Mac", "DOS", "F", "UF"}) {
       dict_obj->SetNewFor<CPDF_Name>(key, "http://evil.org");
       EXPECT_TRUE(file_spec.GetFileName().IsEmpty());
@@ -136,84 +135,49 @@
   }
 }
 
-TEST(cpdf_filespec, SetFileName) {
-  static const pdfium::NullTermWstrFuncTestData test_data = {
-#if defined(OS_WIN)
-    L"C:\\docs\\test.pdf",
-    L"/C/docs/test.pdf"
-#elif defined(OS_MACOSX)
-    L"Mac HD:docs:test.pdf",
-    L"/Mac HD/docs/test.pdf"
-#else
-    L"/docs/test.pdf",
-    L"/docs/test.pdf"
-#endif
-  };
-  // String object.
-  auto str_obj = pdfium::MakeRetain<CPDF_String>(nullptr, L"babababa");
-  CPDF_FileSpec file_spec1(str_obj.Get());
-  file_spec1.SetFileName(test_data.input);
-  // Check internal object value.
-  EXPECT_STREQ(test_data.expected, str_obj->GetUnicodeText().c_str());
-  // Check we can get the file name back.
-  EXPECT_STREQ(test_data.input, file_spec1.GetFileName().c_str());
-
-  // Dictionary object.
-  auto dict_obj = pdfium::MakeRetain<CPDF_Dictionary>();
-  CPDF_FileSpec file_spec2(dict_obj.Get());
-  file_spec2.SetFileName(test_data.input);
-  // Check internal object value.
-  EXPECT_STREQ(test_data.expected, dict_obj->GetUnicodeTextFor("F").c_str());
-  EXPECT_STREQ(test_data.expected, dict_obj->GetUnicodeTextFor("UF").c_str());
-  // Check we can get the file name back.
-  EXPECT_STREQ(test_data.input, file_spec2.GetFileName().c_str());
-}
-
 TEST(cpdf_filespec, GetFileStream) {
   {
     // Invalid object.
     auto name_obj = pdfium::MakeRetain<CPDF_Name>(nullptr, "test.pdf");
-    CPDF_FileSpec file_spec(name_obj.Get());
+    CPDF_FileSpec file_spec(name_obj);
     EXPECT_FALSE(file_spec.GetFileStream());
   }
   {
     // Dictionary object missing its embedded files dictionary.
     auto dict_obj = pdfium::MakeRetain<CPDF_Dictionary>();
-    CPDF_FileSpec file_spec(dict_obj.Get());
+    CPDF_FileSpec file_spec(dict_obj);
     EXPECT_FALSE(file_spec.GetFileStream());
   }
   {
     // Dictionary object with an empty embedded files dictionary.
     auto dict_obj = pdfium::MakeRetain<CPDF_Dictionary>();
     dict_obj->SetNewFor<CPDF_Dictionary>("EF");
-    CPDF_FileSpec file_spec(dict_obj.Get());
+    CPDF_FileSpec file_spec(dict_obj);
     EXPECT_FALSE(file_spec.GetFileStream());
   }
   {
     // Dictionary object with a non-empty embedded files dictionary.
     auto dict_obj = pdfium::MakeRetain<CPDF_Dictionary>();
     dict_obj->SetNewFor<CPDF_Dictionary>("EF");
-    CPDF_FileSpec file_spec(dict_obj.Get());
+    CPDF_FileSpec file_spec(dict_obj);
 
     const wchar_t file_name[] = L"test.pdf";
     const char* const keys[] = {"Unix", "Mac", "DOS", "F", "UF"};
     const char* const streams[] = {"test1", "test2", "test3", "test4", "test5"};
-    static_assert(FX_ArraySize(keys) == FX_ArraySize(streams), "size mismatch");
-    CPDF_Dictionary* file_dict =
-        file_spec.GetObj()->AsDictionary()->GetDictFor("EF");
+    static_assert(std::size(keys) == std::size(streams), "size mismatch");
+    RetainPtr<CPDF_Dictionary> file_dict = dict_obj->GetMutableDictFor("EF");
 
     // Keys in reverse order of precedence to retrieve the file content stream.
-    for (size_t i = 0; i < FX_ArraySize(keys); ++i) {
+    for (size_t i = 0; i < std::size(keys); ++i) {
       // Set the file name.
       dict_obj->SetNewFor<CPDF_String>(keys[i], file_name);
 
       // Set the file stream.
       auto pDict = pdfium::MakeRetain<CPDF_Dictionary>();
       size_t buf_len = strlen(streams[i]) + 1;
-      std::unique_ptr<uint8_t, FxFreeDeleter> buf(FX_Alloc(uint8_t, buf_len));
-      memcpy(buf.get(), streams[i], buf_len);
-      file_dict->SetNewFor<CPDF_Stream>(keys[i], std::move(buf), buf_len,
-                                        std::move(pDict));
+      file_dict->SetNewFor<CPDF_Stream>(
+          keys[i], DataVector<uint8_t>(streams[i], streams[i] + buf_len),
+          std::move(pDict));
 
       // Check that the file content stream is as expected.
       EXPECT_STREQ(
@@ -232,7 +196,7 @@
   {
     // Invalid object.
     auto name_obj = pdfium::MakeRetain<CPDF_Name>(nullptr, "test.pdf");
-    CPDF_FileSpec file_spec(name_obj.Get());
+    CPDF_FileSpec file_spec(name_obj);
     EXPECT_FALSE(file_spec.GetParamsDict());
   }
   {
@@ -240,26 +204,26 @@
     auto dict_obj = pdfium::MakeRetain<CPDF_Dictionary>();
     dict_obj->SetNewFor<CPDF_Dictionary>("EF");
     dict_obj->SetNewFor<CPDF_String>("UF", L"test.pdf");
-    CPDF_FileSpec file_spec(dict_obj.Get());
+    CPDF_FileSpec file_spec(dict_obj);
     EXPECT_FALSE(file_spec.GetParamsDict());
 
     // Add a file stream to the embedded files dictionary.
-    CPDF_Dictionary* file_dict =
-        file_spec.GetObj()->AsDictionary()->GetDictFor("EF");
+    RetainPtr<CPDF_Dictionary> file_dict = dict_obj->GetMutableDictFor("EF");
     auto pDict = pdfium::MakeRetain<CPDF_Dictionary>();
-    std::unique_ptr<uint8_t, FxFreeDeleter> buf(FX_Alloc(uint8_t, 6));
-    memcpy(buf.get(), "hello", 6);
-    file_dict->SetNewFor<CPDF_Stream>("UF", std::move(buf), 6,
-                                      std::move(pDict));
+    static constexpr char kHello[] = "hello";
+    file_dict->SetNewFor<CPDF_Stream>(
+        "UF", DataVector<uint8_t>(std::begin(kHello), std::end(kHello)),
+        std::move(pDict));
 
     // Add a params dictionary to the file stream.
-    CPDF_Stream* stream = file_dict->GetStreamFor("UF");
-    CPDF_Dictionary* stream_dict = stream->GetDict();
+    RetainPtr<CPDF_Stream> stream = file_dict->GetMutableStreamFor("UF");
+    RetainPtr<CPDF_Dictionary> stream_dict = stream->GetMutableDict();
     stream_dict->SetNewFor<CPDF_Dictionary>("Params");
     EXPECT_TRUE(file_spec.GetParamsDict());
 
     // Add a parameter to the params dictionary.
-    CPDF_Dictionary* params_dict = stream_dict->GetDictFor("Params");
+    RetainPtr<CPDF_Dictionary> params_dict =
+        stream_dict->GetMutableDictFor("Params");
     params_dict->SetNewFor<CPDF_Number>("Size", 6);
     EXPECT_EQ(6, file_spec.GetParamsDict()->GetIntegerFor("Size"));
   }
diff --git a/core/fpdfdoc/cpdf_formcontrol.cpp b/core/fpdfdoc/cpdf_formcontrol.cpp
index b98afba..31bbdf3 100644
--- a/core/fpdfdoc/cpdf_formcontrol.cpp
+++ b/core/fpdfdoc/cpdf_formcontrol.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,32 +6,45 @@
 
 #include "core/fpdfdoc/cpdf_formcontrol.h"
 
-#include <algorithm>
+#include <iterator>
+#include <utility>
 
+#include "constants/form_fields.h"
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
+#include "third_party/base/check.h"
 
 namespace {
 
-const char* const g_sHighlightingMode[] = {
-    // Must match order of HighlightingMode enum.
-    "N", "I", "O", "P", "T"};
+constexpr char kHighlightModes[] = {'N', 'I', 'O', 'P', 'T'};
+
+// Order of |kHighlightModes| must match order of HighlightingMode enum.
+static_assert(kHighlightModes[CPDF_FormControl::kNone] == 'N',
+              "HighlightingMode mismatch");
+static_assert(kHighlightModes[CPDF_FormControl::kInvert] == 'I',
+              "HighlightingMode mismatch");
+static_assert(kHighlightModes[CPDF_FormControl::kOutline] == 'O',
+              "HighlightingMode mismatch");
+static_assert(kHighlightModes[CPDF_FormControl::kPush] == 'P',
+              "HighlightingMode mismatch");
+static_assert(kHighlightModes[CPDF_FormControl::kToggle] == 'T',
+              "HighlightingMode mismatch");
 
 }  // namespace
 
 CPDF_FormControl::CPDF_FormControl(CPDF_FormField* pField,
-                                   CPDF_Dictionary* pWidgetDict)
-    : m_pField(pField),
-      m_pWidgetDict(pWidgetDict),
-      m_pForm(m_pField->GetForm()) {}
+                                   RetainPtr<CPDF_Dictionary> pWidgetDict,
+                                   CPDF_InteractiveForm* pForm)
+    : m_pField(pField), m_pWidgetDict(std::move(pWidgetDict)), m_pForm(pForm) {
+  DCHECK(m_pWidgetDict);
+}
 
 CPDF_FormControl::~CPDF_FormControl() = default;
 
@@ -40,16 +53,15 @@
 }
 
 ByteString CPDF_FormControl::GetOnStateName() const {
-  ASSERT(GetType() == CPDF_FormField::kCheckBox ||
+  DCHECK(GetType() == CPDF_FormField::kCheckBox ||
          GetType() == CPDF_FormField::kRadioButton);
-  ByteString csOn;
-  CPDF_Dictionary* pAP = m_pWidgetDict->GetDictFor("AP");
+  RetainPtr<const CPDF_Dictionary> pAP = m_pWidgetDict->GetDictFor("AP");
   if (!pAP)
-    return csOn;
+    return ByteString();
 
-  CPDF_Dictionary* pN = pAP->GetDictFor("N");
+  RetainPtr<const CPDF_Dictionary> pN = pAP->GetDictFor("N");
   if (!pN)
-    return csOn;
+    return ByteString();
 
   CPDF_DictionaryLocker locker(pN);
   for (const auto& it : locker) {
@@ -60,41 +72,40 @@
 }
 
 ByteString CPDF_FormControl::GetCheckedAPState() const {
-  ASSERT(GetType() == CPDF_FormField::kCheckBox ||
+  DCHECK(GetType() == CPDF_FormField::kCheckBox ||
          GetType() == CPDF_FormField::kRadioButton);
   ByteString csOn = GetOnStateName();
-  if (ToArray(CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "Opt")))
-    csOn = ByteString::Format("%d", m_pField->GetControlIndex(this));
+  if (ToArray(m_pField->GetFieldAttr("Opt")))
+    csOn = ByteString::FormatInteger(m_pField->GetControlIndex(this));
   if (csOn.IsEmpty())
     csOn = "Yes";
   return csOn;
 }
 
 WideString CPDF_FormControl::GetExportValue() const {
-  ASSERT(GetType() == CPDF_FormField::kCheckBox ||
+  DCHECK(GetType() == CPDF_FormField::kCheckBox ||
          GetType() == CPDF_FormField::kRadioButton);
   ByteString csOn = GetOnStateName();
-  CPDF_Array* pArray =
-      ToArray(CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "Opt"));
+  RetainPtr<const CPDF_Array> pArray = ToArray(m_pField->GetFieldAttr("Opt"));
   if (pArray)
-    csOn = pArray->GetStringAt(m_pField->GetControlIndex(this));
+    csOn = pArray->GetByteStringAt(m_pField->GetControlIndex(this));
   if (csOn.IsEmpty())
     csOn = "Yes";
   return PDF_DecodeText(csOn.raw_span());
 }
 
 bool CPDF_FormControl::IsChecked() const {
-  ASSERT(GetType() == CPDF_FormField::kCheckBox ||
+  DCHECK(GetType() == CPDF_FormField::kCheckBox ||
          GetType() == CPDF_FormField::kRadioButton);
   ByteString csOn = GetOnStateName();
-  ByteString csAS = m_pWidgetDict->GetStringFor("AS");
+  ByteString csAS = m_pWidgetDict->GetByteStringFor("AS");
   return csAS == csOn;
 }
 
 bool CPDF_FormControl::IsDefaultChecked() const {
-  ASSERT(GetType() == CPDF_FormField::kCheckBox ||
+  DCHECK(GetType() == CPDF_FormField::kCheckBox ||
          GetType() == CPDF_FormField::kRadioButton);
-  CPDF_Object* pDV = CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "DV");
+  RetainPtr<const CPDF_Object> pDV = m_pField->GetFieldAttr("DV");
   if (!pDV)
     return false;
 
@@ -104,9 +115,9 @@
 }
 
 void CPDF_FormControl::CheckControl(bool bChecked) {
-  ASSERT(GetType() == CPDF_FormField::kCheckBox ||
+  DCHECK(GetType() == CPDF_FormField::kCheckBox ||
          GetType() == CPDF_FormField::kRadioButton);
-  ByteString csOldAS = m_pWidgetDict->GetStringFor("AS", "Off");
+  ByteString csOldAS = m_pWidgetDict->GetByteStringFor("AS", "Off");
   ByteString csAS = "Off";
   if (bChecked)
     csAS = GetOnStateName();
@@ -117,20 +128,17 @@
 
 CPDF_FormControl::HighlightingMode CPDF_FormControl::GetHighlightingMode()
     const {
-  if (!m_pWidgetDict)
-    return Invert;
-
-  ByteString csH = m_pWidgetDict->GetStringFor("H", "I");
-  for (size_t i = 0; i < FX_ArraySize(g_sHighlightingMode); ++i) {
-    if (csH == g_sHighlightingMode[i])
+  ByteString csH = m_pWidgetDict->GetByteStringFor("H", "I");
+  for (size_t i = 0; i < std::size(kHighlightModes); ++i) {
+    // TODO(tsepez): disambiguate string ctors.
+    if (csH == ByteStringView(kHighlightModes[i]))
       return static_cast<HighlightingMode>(i);
   }
-  return Invert;
+  return kInvert;
 }
 
 CPDF_ApSettings CPDF_FormControl::GetMK() const {
-  return CPDF_ApSettings(m_pWidgetDict ? m_pWidgetDict->GetDictFor("MK")
-                                       : nullptr);
+  return CPDF_ApSettings(m_pWidgetDict->GetMutableDictFor("MK"));
 }
 
 bool CPDF_FormControl::HasMKEntry(const ByteString& csEntry) const {
@@ -141,25 +149,25 @@
   return GetMK().GetRotation();
 }
 
-FX_ARGB CPDF_FormControl::GetColor(int& iColorType, const ByteString& csEntry) {
-  return GetMK().GetColor(iColorType, csEntry);
+CFX_Color::TypeAndARGB CPDF_FormControl::GetColorARGB(
+    const ByteString& csEntry) {
+  return GetMK().GetColorARGB(csEntry);
 }
 
-float CPDF_FormControl::GetOriginalColor(int index, const ByteString& csEntry) {
-  return GetMK().GetOriginalColor(index, csEntry);
+float CPDF_FormControl::GetOriginalColorComponent(int index,
+                                                  const ByteString& csEntry) {
+  return GetMK().GetOriginalColorComponent(index, csEntry);
 }
 
-void CPDF_FormControl::GetOriginalColor(int& iColorType,
-                                        float fc[4],
-                                        const ByteString& csEntry) {
-  GetMK().GetOriginalColor(iColorType, fc, csEntry);
+CFX_Color CPDF_FormControl::GetOriginalColor(const ByteString& csEntry) {
+  return GetMK().GetOriginalColor(csEntry);
 }
 
 WideString CPDF_FormControl::GetCaption(const ByteString& csEntry) const {
   return GetMK().GetCaption(csEntry);
 }
 
-CPDF_Stream* CPDF_FormControl::GetIcon(const ByteString& csEntry) {
+RetainPtr<CPDF_Stream> CPDF_FormControl::GetIcon(const ByteString& csEntry) {
   return GetMK().GetIcon(csEntry);
 }
 
@@ -171,43 +179,23 @@
   return GetMK().GetTextPosition();
 }
 
-CPDF_Action CPDF_FormControl::GetAction() const {
-  if (!m_pWidgetDict)
-    return CPDF_Action(nullptr);
-
-  if (m_pWidgetDict->KeyExist("A"))
-    return CPDF_Action(m_pWidgetDict->GetDictFor("A"));
-
-  CPDF_Object* pObj = CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "A");
-  return CPDF_Action(pObj ? pObj->GetDict() : nullptr);
-}
-
-CPDF_AAction CPDF_FormControl::GetAdditionalAction() const {
-  if (!m_pWidgetDict)
-    return CPDF_AAction(nullptr);
-
-  if (m_pWidgetDict->KeyExist("AA"))
-    return CPDF_AAction(m_pWidgetDict->GetDictFor("AA"));
-  return m_pField->GetAdditionalAction();
-}
-
 CPDF_DefaultAppearance CPDF_FormControl::GetDefaultAppearance() const {
-  if (!m_pWidgetDict)
-    return CPDF_DefaultAppearance();
+  if (m_pWidgetDict->KeyExist(pdfium::form_fields::kDA)) {
+    return CPDF_DefaultAppearance(
+        m_pWidgetDict->GetByteStringFor(pdfium::form_fields::kDA));
+  }
+  RetainPtr<const CPDF_Object> pObj =
+      m_pField->GetFieldAttr(pdfium::form_fields::kDA);
+  if (pObj)
+    return CPDF_DefaultAppearance(pObj->GetString());
 
-  if (m_pWidgetDict->KeyExist("DA"))
-    return CPDF_DefaultAppearance(m_pWidgetDict->GetStringFor("DA"));
-
-  CPDF_Object* pObj = CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "DA");
-  if (!pObj)
-    return m_pForm->GetDefaultAppearance();
-  return CPDF_DefaultAppearance(pObj->GetString());
+  return m_pForm->GetDefaultAppearance();
 }
 
-Optional<WideString> CPDF_FormControl::GetDefaultControlFontName() const {
+absl::optional<WideString> CPDF_FormControl::GetDefaultControlFontName() const {
   RetainPtr<CPDF_Font> pFont = GetDefaultControlFont();
   if (!pFont)
-    return {};
+    return absl::nullopt;
 
   return WideString::FromDefANSI(pFont->GetBaseFontName().AsStringView());
 }
@@ -215,53 +203,55 @@
 RetainPtr<CPDF_Font> CPDF_FormControl::GetDefaultControlFont() const {
   float fFontSize;
   CPDF_DefaultAppearance cDA = GetDefaultAppearance();
-  Optional<ByteString> csFontNameTag = cDA.GetFont(&fFontSize);
-  if (!csFontNameTag || csFontNameTag->IsEmpty())
+  absl::optional<ByteString> csFontNameTag = cDA.GetFont(&fFontSize);
+  if (!csFontNameTag.has_value() || csFontNameTag->IsEmpty())
     return nullptr;
 
-  CPDF_Object* pObj = CPDF_FormField::GetFieldAttr(m_pWidgetDict.Get(), "DR");
-  if (CPDF_Dictionary* pDict = ToDictionary(pObj)) {
-    CPDF_Dictionary* pFonts = pDict->GetDictFor("Font");
-    if (ValidateFontResourceDict(pFonts)) {
-      CPDF_Dictionary* pElement = pFonts->GetDictFor(*csFontNameTag);
+  RetainPtr<CPDF_Dictionary> pDRDict = ToDictionary(
+      CPDF_FormField::GetMutableFieldAttrForDict(m_pWidgetDict.Get(), "DR"));
+  if (pDRDict) {
+    RetainPtr<CPDF_Dictionary> pFonts = pDRDict->GetMutableDictFor("Font");
+    if (ValidateFontResourceDict(pFonts.Get())) {
+      RetainPtr<CPDF_Dictionary> pElement =
+          pFonts->GetMutableDictFor(csFontNameTag.value());
       if (pElement) {
-        auto* pData = CPDF_DocPageData::FromDocument(m_pForm->GetDocument());
-        RetainPtr<CPDF_Font> pFont = pData->GetFont(pElement);
+        RetainPtr<CPDF_Font> pFont =
+            m_pForm->GetFontForElement(std::move(pElement));
         if (pFont)
           return pFont;
       }
     }
   }
-  RetainPtr<CPDF_Font> pFormFont = m_pForm->GetFormFont(*csFontNameTag);
+  RetainPtr<CPDF_Font> pFormFont = m_pForm->GetFormFont(csFontNameTag.value());
   if (pFormFont)
     return pFormFont;
 
-  CPDF_Dictionary* pPageDict = m_pWidgetDict->GetDictFor("P");
-  CPDF_Dictionary* pDict =
-      ToDictionary(CPDF_FormField::GetFieldAttr(pPageDict, "Resources"));
+  RetainPtr<CPDF_Dictionary> pPageDict = m_pWidgetDict->GetMutableDictFor("P");
+  RetainPtr<CPDF_Dictionary> pDict = ToDictionary(
+      CPDF_FormField::GetMutableFieldAttrForDict(pPageDict.Get(), "Resources"));
   if (!pDict)
     return nullptr;
 
-  CPDF_Dictionary* pFonts = pDict->GetDictFor("Font");
-  if (!ValidateFontResourceDict(pFonts))
+  RetainPtr<CPDF_Dictionary> pFonts = pDict->GetMutableDictFor("Font");
+  if (!ValidateFontResourceDict(pFonts.Get()))
     return nullptr;
 
-  CPDF_Dictionary* pElement = pFonts->GetDictFor(*csFontNameTag);
+  RetainPtr<CPDF_Dictionary> pElement =
+      pFonts->GetMutableDictFor(csFontNameTag.value());
   if (!pElement)
     return nullptr;
 
-  auto* pDocPageData = CPDF_DocPageData::FromDocument(m_pForm->GetDocument());
-  return pDocPageData->GetFont(pElement);
+  return m_pForm->GetFontForElement(std::move(pElement));
 }
 
 int CPDF_FormControl::GetControlAlignment() const {
-  if (!m_pWidgetDict)
-    return 0;
-  if (m_pWidgetDict->KeyExist("Q"))
-    return m_pWidgetDict->GetIntegerFor("Q", 0);
+  if (m_pWidgetDict->KeyExist(pdfium::form_fields::kQ))
+    return m_pWidgetDict->GetIntegerFor(pdfium::form_fields::kQ, 0);
 
-  CPDF_Object* pObj = CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "Q");
+  RetainPtr<const CPDF_Object> pObj =
+      m_pField->GetFieldAttr(pdfium::form_fields::kQ);
   if (pObj)
     return pObj->GetInteger();
+
   return m_pForm->GetFormAlignment();
 }
diff --git a/core/fpdfdoc/cpdf_formcontrol.h b/core/fpdfdoc/cpdf_formcontrol.h
index d825634..59eec21 100644
--- a/core/fpdfdoc/cpdf_formcontrol.h
+++ b/core/fpdfdoc/cpdf_formcontrol.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #ifndef CORE_FPDFDOC_CPDF_FORMCONTROL_H_
 #define CORE_FPDFDOC_CPDF_FORMCONTROL_H_
 
+#include "constants/appearance.h"
 #include "core/fpdfdoc/cpdf_aaction.h"
 #include "core/fpdfdoc/cpdf_action.h"
 #include "core/fpdfdoc/cpdf_annot.h"
@@ -18,31 +19,32 @@
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/optional.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxge/cfx_color.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CFX_RenderDevice;
 class CPDF_Dictionary;
 class CPDF_Font;
 class CPDF_FormField;
 class CPDF_InteractiveForm;
-class CPDF_OCContext;
-class CPDF_RenderOptions;
 class CPDF_Stream;
 
 class CPDF_FormControl {
  public:
-  enum HighlightingMode { None = 0, Invert, Outline, Push, Toggle };
+  enum HighlightingMode { kNone = 0, kInvert, kOutline, kPush, kToggle };
 
-  CPDF_FormControl(CPDF_FormField* pField, CPDF_Dictionary* pWidgetDict);
+  CPDF_FormControl(CPDF_FormField* pField,
+                   RetainPtr<CPDF_Dictionary> pWidgetDict,
+                   CPDF_InteractiveForm* pForm);
   ~CPDF_FormControl();
 
   CPDF_FormField::Type GetType() const { return m_pField->GetType(); }
-  const CPDF_InteractiveForm* GetInteractiveForm() const {
-    return m_pForm.Get();
+  CPDF_FormField* GetField() const { return m_pField; }
+  RetainPtr<const CPDF_Dictionary> GetWidgetDict() const {
+    return m_pWidgetDict;
   }
-  CPDF_FormField* GetField() const { return m_pField.Get(); }
-  CPDF_Dictionary* GetWidget() const { return m_pWidgetDict.Get(); }
   CFX_FloatRect GetRect() const;
 
   ByteString GetCheckedAPState() const;
@@ -55,43 +57,42 @@
   bool HasMKEntry(const ByteString& csEntry) const;
   int GetRotation() const;
 
-  FX_ARGB GetBorderColor(int& iColorType) { return GetColor(iColorType, "BC"); }
+  CFX_Color::TypeAndARGB GetColorARGB(const ByteString& csEntry);
+  float GetOriginalColorComponent(int index, const ByteString& csEntry);
 
-  float GetOriginalBorderColor(int index) {
-    return GetOriginalColor(index, "BC");
+  CFX_Color GetOriginalBorderColor() {
+    return GetOriginalColor(pdfium::appearance::kBC);
   }
 
-  void GetOriginalBorderColor(int& iColorType, float fc[4]) {
-    GetOriginalColor(iColorType, fc, "BC");
+  CFX_Color GetOriginalBackgroundColor() {
+    return GetOriginalColor(pdfium::appearance::kBG);
   }
 
-  FX_ARGB GetBackgroundColor(int& iColorType) {
-    return GetColor(iColorType, "BG");
+  WideString GetNormalCaption() const {
+    return GetCaption(pdfium::appearance::kCA);
+  }
+  WideString GetRolloverCaption() const {
+    return GetCaption(pdfium::appearance::kRC);
+  }
+  WideString GetDownCaption() const {
+    return GetCaption(pdfium::appearance::kAC);
   }
 
-  float GetOriginalBackgroundColor(int index) {
-    return GetOriginalColor(index, "BG");
+  RetainPtr<CPDF_Stream> GetNormalIcon() {
+    return GetIcon(pdfium::appearance::kI);
   }
-
-  void GetOriginalBackgroundColor(int& iColorType, float fc[4]) {
-    GetOriginalColor(iColorType, fc, "BG");
+  RetainPtr<CPDF_Stream> GetRolloverIcon() {
+    return GetIcon(pdfium::appearance::kRI);
   }
-
-  WideString GetNormalCaption() const { return GetCaption("CA"); }
-  WideString GetRolloverCaption() const { return GetCaption("RC"); }
-  WideString GetDownCaption() const { return GetCaption("AC"); }
-
-  CPDF_Stream* GetNormalIcon() { return GetIcon("I"); }
-  CPDF_Stream* GetRolloverIcon() { return GetIcon("RI"); }
-  CPDF_Stream* GetDownIcon() { return GetIcon("IX"); }
+  RetainPtr<CPDF_Stream> GetDownIcon() {
+    return GetIcon(pdfium::appearance::kIX);
+  }
   CPDF_IconFit GetIconFit() const;
 
   int GetTextPosition() const;
-  CPDF_Action GetAction() const;
-  CPDF_AAction GetAdditionalAction() const;
   CPDF_DefaultAppearance GetDefaultAppearance() const;
 
-  Optional<WideString> GetDefaultControlFontName() const;
+  absl::optional<WideString> GetDefaultControlFontName() const;
   int GetControlAlignment() const;
 
   ByteString GetOnStateName() const;
@@ -99,14 +100,10 @@
 
  private:
   RetainPtr<CPDF_Font> GetDefaultControlFont() const;
-  FX_ARGB GetColor(int& iColorType, const ByteString& csEntry);
-  float GetOriginalColor(int index, const ByteString& csEntry);
-  void GetOriginalColor(int& iColorType,
-                        float fc[4],
-                        const ByteString& csEntry);
+  CFX_Color GetOriginalColor(const ByteString& csEntry);
 
   WideString GetCaption(const ByteString& csEntry) const;
-  CPDF_Stream* GetIcon(const ByteString& csEntry);
+  RetainPtr<CPDF_Stream> GetIcon(const ByteString& csEntry);
   CPDF_ApSettings GetMK() const;
 
   UnownedPtr<CPDF_FormField> const m_pField;
diff --git a/core/fpdfdoc/cpdf_formfield.cpp b/core/fpdfdoc/cpdf_formfield.cpp
index 1eae005..705827d 100644
--- a/core/fpdfdoc/cpdf_formfield.cpp
+++ b/core/fpdfdoc/cpdf_formfield.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "core/fpdfdoc/cpdf_formfield.h"
 
-#include <memory>
+#include <map>
 #include <set>
 #include <utility>
 
@@ -17,66 +17,71 @@
 #include "core/fpdfapi/parser/cfdf_document.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fpdfdoc/cpdf_defaultappearance.h"
 #include "core/fpdfdoc/cpdf_formcontrol.h"
+#include "core/fpdfdoc/cpdf_generateap.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
-#include "core/fpdfdoc/cpvt_generateap.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
 
 namespace {
 
-const CPDF_Object* GetFieldAttrRecursive(const CPDF_Dictionary* pFieldDict,
-                                         const ByteString& name,
-                                         int nLevel) {
+RetainPtr<const CPDF_Object> GetFieldAttrRecursive(
+    const CPDF_Dictionary* pFieldDict,
+    const ByteString& name,
+    int nLevel) {
   static constexpr int kGetFieldMaxRecursion = 32;
   if (!pFieldDict || nLevel > kGetFieldMaxRecursion)
     return nullptr;
 
-  const CPDF_Object* pAttr = pFieldDict->GetDirectObjectFor(name);
+  RetainPtr<const CPDF_Object> pAttr = pFieldDict->GetDirectObjectFor(name);
   if (pAttr)
     return pAttr;
 
   return GetFieldAttrRecursive(
-      pFieldDict->GetDictFor(pdfium::form_fields::kParent), name, nLevel + 1);
+      pFieldDict->GetDictFor(pdfium::form_fields::kParent).Get(), name,
+      nLevel + 1);
 }
 
 }  // namespace
 
 // static
-Optional<FormFieldType> CPDF_FormField::IntToFormFieldType(int value) {
+absl::optional<FormFieldType> CPDF_FormField::IntToFormFieldType(int value) {
   if (value >= static_cast<int>(FormFieldType::kUnknown) &&
       value < static_cast<int>(kFormFieldTypeCount)) {
-    return {static_cast<FormFieldType>(value)};
+    return static_cast<FormFieldType>(value);
   }
-  return {};
+  return absl::nullopt;
 }
 
 // static
-const CPDF_Object* CPDF_FormField::GetFieldAttr(
+RetainPtr<const CPDF_Object> CPDF_FormField::GetFieldAttrForDict(
     const CPDF_Dictionary* pFieldDict,
     const ByteString& name) {
   return GetFieldAttrRecursive(pFieldDict, name, 0);
 }
 
 // static
-CPDF_Object* CPDF_FormField::GetFieldAttr(CPDF_Dictionary* pFieldDict,
-                                          const ByteString& name) {
-  return const_cast<CPDF_Object*>(GetFieldAttrRecursive(
-      static_cast<const CPDF_Dictionary*>(pFieldDict), name, 0));
+RetainPtr<CPDF_Object> CPDF_FormField::GetMutableFieldAttrForDict(
+    CPDF_Dictionary* pFieldDict,
+    const ByteString& name) {
+  return pdfium::WrapRetain(const_cast<CPDF_Object*>(
+      GetFieldAttrRecursive(pFieldDict, name, 0).Get()));
 }
 
 // static
-WideString CPDF_FormField::GetFullNameForDict(CPDF_Dictionary* pFieldDict) {
+WideString CPDF_FormField::GetFullNameForDict(
+    const CPDF_Dictionary* pFieldDict) {
   WideString full_name;
-  std::set<CPDF_Dictionary*> visited;
-  CPDF_Dictionary* pLevel = pFieldDict;
+  std::set<const CPDF_Dictionary*> visited;
+  const CPDF_Dictionary* pLevel = pFieldDict;
   while (pLevel) {
     visited.insert(pLevel);
     WideString short_name = pLevel->GetUnicodeTextFor(pdfium::form_fields::kT);
@@ -86,24 +91,24 @@
       else
         full_name = short_name + L'.' + full_name;
     }
-    pLevel = pLevel->GetDictFor(pdfium::form_fields::kParent);
-    if (pdfium::ContainsKey(visited, pLevel))
+    pLevel = pLevel->GetDictFor(pdfium::form_fields::kParent).Get();
+    if (pdfium::Contains(visited, pLevel))
       break;
   }
   return full_name;
 }
 
 CPDF_FormField::CPDF_FormField(CPDF_InteractiveForm* pForm,
-                               CPDF_Dictionary* pDict)
-    : m_pForm(pForm), m_pDict(pDict) {
+                               RetainPtr<CPDF_Dictionary> pDict)
+    : m_pForm(pForm), m_pDict(std::move(pDict)) {
   InitFieldFlags();
 }
 
 CPDF_FormField::~CPDF_FormField() = default;
 
 void CPDF_FormField::InitFieldFlags() {
-  const CPDF_Object* ft_attr =
-      GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kFT);
+  RetainPtr<const CPDF_Object> ft_attr =
+      GetFieldAttrInternal(pdfium::form_fields::kFT);
   ByteString type_name = ft_attr ? ft_attr->GetString() : ByteString();
   uint32_t flags = GetFieldFlags();
   m_bRequired = flags & pdfium::form_flags::kRequired;
@@ -126,7 +131,6 @@
       m_Type = kRichText;
     else
       m_Type = kText;
-    LoadDA();
   } else if (type_name == pdfium::form_fields::kCh) {
     if (flags & pdfium::form_flags::kChoiceCombo) {
       m_Type = kComboBox;
@@ -134,7 +138,7 @@
       m_Type = kListBox;
       m_bIsMultiSelectListBox = flags & pdfium::form_flags::kChoiceMultiSelect;
     }
-    LoadDA();
+    m_bUseSelectedIndices = UseSelectedIndicesObject();
   } else if (type_name == pdfium::form_fields::kSig) {
     m_Type = kSign;
   }
@@ -144,7 +148,16 @@
   return GetFullNameForDict(m_pDict.Get());
 }
 
-bool CPDF_FormField::ResetField(NotificationOption notify) {
+RetainPtr<const CPDF_Object> CPDF_FormField::GetFieldAttr(
+    const ByteString& name) const {
+  return GetFieldAttrInternal(name);
+}
+
+RetainPtr<const CPDF_Dictionary> CPDF_FormField::GetFieldDict() const {
+  return pdfium::WrapRetain(GetFieldDictInternal());
+}
+
+bool CPDF_FormField::ResetField() {
   switch (m_Type) {
     case kCheckBox:
     case kRadioButton: {
@@ -155,8 +168,7 @@
         CheckControl(i, GetControl(i)->IsDefaultChecked(),
                      NotificationOption::kDoNotNotify);
       }
-      if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify())
-        m_pForm->GetFormNotify()->AfterCheckedStatusChange(this);
+      m_pForm->NotifyAfterCheckedStatusChange(this);
       break;
     }
     case kComboBox:
@@ -166,55 +178,57 @@
       int iIndex = GetDefaultSelectedItem();
       if (iIndex >= 0)
         csValue = GetOptionLabel(iIndex);
-      if (notify == NotificationOption::kNotify &&
-          !NotifyListOrComboBoxBeforeChange(csValue)) {
+      if (!NotifyListOrComboBoxBeforeChange(csValue)) {
         return false;
       }
-      SetItemSelection(iIndex, true, NotificationOption::kDoNotNotify);
-      if (notify == NotificationOption::kNotify)
-        NotifyListOrComboBoxAfterChange();
+      SetItemSelection(iIndex, NotificationOption::kDoNotNotify);
+      NotifyListOrComboBoxAfterChange();
       break;
     }
     case kText:
     case kRichText:
     case kFile:
     default: {
-      const CPDF_Object* pDV = GetDefaultValueObject();
       WideString csDValue;
-      if (pDV)
-        csDValue = pDV->GetUnicodeText();
-
       WideString csValue;
       {
-        // Limit the scope of |pV| because it may get invalidated below.
-        const CPDF_Object* pV = GetValueObject();
+        // Limit scope of |pDV| and |pV| because they may get invalidated
+        // during notification below.
+        RetainPtr<const CPDF_Object> pDV = GetDefaultValueObject();
+        if (pDV)
+          csDValue = pDV->GetUnicodeText();
+
+        RetainPtr<const CPDF_Object> pV = GetValueObject();
         if (pV)
           csValue = pV->GetUnicodeText();
       }
 
-      bool bHasRV = !!GetFieldAttr(m_pDict.Get(), "RV");
+      bool bHasRV = !!GetFieldAttrInternal(pdfium::form_fields::kRV);
       if (!bHasRV && (csDValue == csValue))
         return false;
 
-      if (notify == NotificationOption::kNotify &&
-          !NotifyBeforeValueChange(csDValue)) {
+      if (!m_pForm->NotifyBeforeValueChange(this, csDValue))
         return false;
-      }
-      if (pDV) {
-        RetainPtr<CPDF_Object> pClone = pDV->Clone();
-        if (!pClone)
-          return false;
 
-        m_pDict->SetFor(pdfium::form_fields::kV, std::move(pClone));
-        if (bHasRV) {
-          m_pDict->SetFor("RV", pDV->Clone());
+      {
+        // Limit scope of |pDV| because it may get invalidated during
+        // notification below.
+        RetainPtr<const CPDF_Object> pDV = GetDefaultValueObject();
+        if (pDV) {
+          RetainPtr<CPDF_Object> pClone = pDV->Clone();
+          if (!pClone)
+            return false;
+
+          m_pDict->SetFor(pdfium::form_fields::kV, std::move(pClone));
+          if (bHasRV) {
+            m_pDict->SetFor(pdfium::form_fields::kRV, pDV->Clone());
+          }
+        } else {
+          m_pDict->RemoveFor(pdfium::form_fields::kV);
+          m_pDict->RemoveFor(pdfium::form_fields::kRV);
         }
-      } else {
-        m_pDict->RemoveFor(pdfium::form_fields::kV);
-        m_pDict->RemoveFor("RV");
       }
-      if (notify == NotificationOption::kNotify)
-        NotifyAfterValueChange();
+      m_pForm->NotifyAfterValueChange(this);
       break;
     }
   }
@@ -222,11 +236,11 @@
 }
 
 int CPDF_FormField::CountControls() const {
-  return pdfium::CollectionSize<int>(GetControls());
+  return fxcrt::CollectionSize<int>(GetControls());
 }
 
 CPDF_FormControl* CPDF_FormField::GetControl(int index) const {
-  return GetControls()[index].Get();
+  return GetControls()[index];
 }
 
 int CPDF_FormField::GetControlIndex(const CPDF_FormControl* pControl) const {
@@ -235,7 +249,10 @@
 
   const auto& controls = GetControls();
   auto it = std::find(controls.begin(), controls.end(), pControl);
-  return it != controls.end() ? it - controls.begin() : -1;
+  if (it == controls.end())
+    return -1;
+
+  return pdfium::base::checked_cast<int>(it - controls.begin());
 }
 
 FormFieldType CPDF_FormField::GetFieldType() const {
@@ -262,42 +279,39 @@
 }
 
 CPDF_AAction CPDF_FormField::GetAdditionalAction() const {
-  CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kAA);
+  RetainPtr<const CPDF_Object> pObj =
+      GetFieldAttrInternal(pdfium::form_fields::kAA);
   return CPDF_AAction(pObj ? pObj->GetDict() : nullptr);
 }
 
 WideString CPDF_FormField::GetAlternateName() const {
-  const CPDF_Object* pObj =
-      GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kTU);
+  RetainPtr<const CPDF_Object> pObj =
+      GetFieldAttrInternal(pdfium::form_fields::kTU);
   return pObj ? pObj->GetUnicodeText() : WideString();
 }
 
 WideString CPDF_FormField::GetMappingName() const {
-  const CPDF_Object* pObj =
-      GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kTM);
+  RetainPtr<const CPDF_Object> pObj =
+      GetFieldAttrInternal(pdfium::form_fields::kTM);
   return pObj ? pObj->GetUnicodeText() : WideString();
 }
 
 uint32_t CPDF_FormField::GetFieldFlags() const {
-  const CPDF_Object* pObj =
-      GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kFf);
+  RetainPtr<const CPDF_Object> pObj =
+      GetFieldAttrInternal(pdfium::form_fields::kFf);
   return pObj ? pObj->GetInteger() : 0;
 }
 
-ByteString CPDF_FormField::GetDefaultStyle() const {
-  const CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), "DS");
-  return pObj ? pObj->GetString() : ByteString();
-}
-
-void CPDF_FormField::SetOpt(RetainPtr<CPDF_Object> pOpt) {
-  m_pDict->SetFor("Opt", std::move(pOpt));
+void CPDF_FormField::SetFieldFlags(uint32_t dwFlags) {
+  m_pDict->SetNewFor<CPDF_Number>(pdfium::form_fields::kFf,
+                                  static_cast<int>(dwFlags));
 }
 
 WideString CPDF_FormField::GetValue(bool bDefault) const {
   if (GetType() == kCheckBox || GetType() == kRadioButton)
     return GetCheckValue(bDefault);
 
-  const CPDF_Object* pValue =
+  RetainPtr<const CPDF_Object> pValue =
       bDefault ? GetDefaultValueObject() : GetValueObject();
   if (!pValue) {
     if (!bDefault && m_Type != kText)
@@ -310,11 +324,13 @@
     case CPDF_Object::kString:
     case CPDF_Object::kStream:
       return pValue->GetUnicodeText();
-    case CPDF_Object::kArray:
-      pValue = pValue->AsArray()->GetDirectObjectAt(0);
-      if (pValue)
-        return pValue->GetUnicodeText();
+    case CPDF_Object::kArray: {
+      RetainPtr<const CPDF_Object> pNewValue =
+          pValue->AsArray()->GetDirectObjectAt(0);
+      if (pNewValue)
+        return pNewValue->GetUnicodeText();
       break;
+    }
     default:
       break;
   }
@@ -344,26 +360,27 @@
     case kComboBox: {
       WideString csValue = value;
       if (notify == NotificationOption::kNotify &&
-          !NotifyBeforeValueChange(csValue)) {
+          !m_pForm->NotifyBeforeValueChange(this, csValue)) {
         return false;
       }
       ByteString key(bDefault ? pdfium::form_fields::kDV
                               : pdfium::form_fields::kV);
-      m_pDict->SetNewFor<CPDF_String>(key, csValue);
+      m_pDict->SetNewFor<CPDF_String>(key, csValue.AsStringView());
       int iIndex = FindOption(csValue);
       if (iIndex < 0) {
         if (m_Type == kRichText && !bDefault) {
-          m_pDict->SetFor("RV", m_pDict->GetObjectFor(key)->Clone());
+          m_pDict->SetFor(pdfium::form_fields::kRV,
+                          m_pDict->GetObjectFor(key)->Clone());
         }
         m_pDict->RemoveFor("I");
       } else {
         if (!bDefault) {
           ClearSelection(NotificationOption::kDoNotNotify);
-          SetItemSelection(iIndex, true, NotificationOption::kDoNotNotify);
+          SetItemSelection(iIndex, NotificationOption::kDoNotNotify);
         }
       }
       if (notify == NotificationOption::kNotify)
-        NotifyAfterValueChange();
+        m_pForm->NotifyAfterValueChange(this);
       break;
     }
     case kListBox: {
@@ -375,15 +392,15 @@
         return false;
 
       if (notify == NotificationOption::kNotify &&
-          !NotifyBeforeSelectionChange(value)) {
+          !m_pForm->NotifyBeforeSelectionChange(this, value)) {
         return false;
       }
       if (!bDefault) {
         ClearSelection(NotificationOption::kDoNotNotify);
-        SetItemSelection(iIndex, true, NotificationOption::kDoNotNotify);
+        SetItemSelection(iIndex, NotificationOption::kDoNotNotify);
       }
       if (notify == NotificationOption::kNotify)
-        NotifyAfterSelectionChange();
+        m_pForm->NotifyAfterSelectionChange(this);
       break;
     }
     default:
@@ -398,14 +415,15 @@
 }
 
 int CPDF_FormField::GetMaxLen() const {
-  if (const CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), "MaxLen"))
+  RetainPtr<const CPDF_Object> pObj = GetFieldAttrInternal("MaxLen");
+  if (pObj)
     return pObj->GetInteger();
 
   for (auto& pControl : GetControls()) {
     if (!pControl)
       continue;
 
-    const CPDF_Dictionary* pWidgetDict = pControl->GetWidget();
+    RetainPtr<const CPDF_Dictionary> pWidgetDict = pControl->GetWidgetDict();
     if (pWidgetDict->KeyExist("MaxLen"))
       return pWidgetDict->GetIntegerFor("MaxLen");
   }
@@ -420,7 +438,7 @@
   if (pValue->IsString() || pValue->IsNumber())
     return pValue->GetString().IsEmpty() ? 0 : 1;
   const CPDF_Array* pArray = pValue->AsArray();
-  return pArray ? pArray->size() : 0;
+  return pArray ? fxcrt::CollectionSize<int>(*pArray) : 0;
 }
 
 int CPDF_FormField::GetSelectedIndex(int index) const {
@@ -441,7 +459,8 @@
     if (!pArray || index < 0)
       return -1;
 
-    const CPDF_Object* elementValue = pArray->GetDirectObjectAt(index);
+    RetainPtr<const CPDF_Object> elementValue =
+        pArray->GetDirectObjectAt(index);
     sel_value = elementValue ? elementValue->GetUnicodeText() : WideString();
   }
   if (index < CountSelectedOptions()) {
@@ -458,7 +477,7 @@
 }
 
 bool CPDF_FormField::ClearSelection(NotificationOption notify) {
-  if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify()) {
+  if (notify == NotificationOption::kNotify) {
     WideString csValue;
     int iIndex = GetSelectedIndex(0);
     if (iIndex >= 0)
@@ -474,43 +493,17 @@
 }
 
 bool CPDF_FormField::IsItemSelected(int index) const {
-  ASSERT(GetType() == kComboBox || GetType() == kListBox);
+  DCHECK(GetType() == kComboBox || GetType() == kListBox);
   if (index < 0 || index >= CountOptions())
     return false;
-  if (IsOptionSelected(index))
-    return true;
 
-  WideString opt_value = GetOptionValue(index);
-  const CPDF_Object* pValue = GetValueOrSelectedIndicesObject();
-  if (!pValue)
-    return false;
-
-  if (pValue->IsString())
-    return pValue->GetUnicodeText() == opt_value;
-
-  if (pValue->IsNumber()) {
-    if (pValue->GetString().IsEmpty())
-      return false;
-    return (pValue->GetInteger() == index);
-  }
-
-  const CPDF_Array* pArray = pValue->AsArray();
-  if (!pArray)
-    return false;
-
-  for (int i = 0; i < CountSelectedOptions(); ++i) {
-    if (GetSelectedOptionIndex(i) == index) {
-      const CPDF_Object* pDirectObj = pArray->GetDirectObjectAt(i);
-      return pDirectObj && pDirectObj->GetUnicodeText() == opt_value;
-    }
-  }
-  return false;
+  // First consider the /I entry if it is valid, then fall back to the /V entry.
+  return m_bUseSelectedIndices ? IsSelectedIndex(index)
+                               : IsSelectedOption(GetOptionValue(index));
 }
 
-bool CPDF_FormField::SetItemSelection(int index,
-                                      bool bSelected,
-                                      NotificationOption notify) {
-  ASSERT(GetType() == kComboBox || GetType() == kListBox);
+bool CPDF_FormField::SetItemSelection(int index, NotificationOption notify) {
+  DCHECK(GetType() == kComboBox || GetType() == kListBox);
   if (index < 0 || index >= CountOptions())
     return false;
 
@@ -520,10 +513,12 @@
     return false;
   }
 
-  if (bSelected)
-    SetItemSelectionSelected(index, opt_value);
-  else
-    SetItemSelectionUnselected(index, opt_value);
+  SetItemSelectionSelected(index, opt_value);
+
+  // UseSelectedIndicesObject() has a non-trivial linearithmic run-time, so run
+  // only if necessary.
+  if (!m_bUseSelectedIndices)
+    m_bUseSelectedIndices = UseSelectedIndicesObject();
 
   if (notify == NotificationOption::kNotify)
     NotifyListOrComboBoxAfterChange();
@@ -533,69 +528,30 @@
 void CPDF_FormField::SetItemSelectionSelected(int index,
                                               const WideString& opt_value) {
   if (GetType() != kListBox) {
-    m_pDict->SetNewFor<CPDF_String>(pdfium::form_fields::kV, opt_value);
-    CPDF_Array* pI = m_pDict->SetNewFor<CPDF_Array>("I");
-    pI->AddNew<CPDF_Number>(index);
+    m_pDict->SetNewFor<CPDF_String>(pdfium::form_fields::kV,
+                                    opt_value.AsStringView());
+    auto pI = m_pDict->SetNewFor<CPDF_Array>("I");
+    pI->AppendNew<CPDF_Number>(index);
     return;
   }
 
-  SelectOption(index, true, NotificationOption::kDoNotNotify);
+  SelectOption(index);
   if (!m_bIsMultiSelectListBox) {
-    m_pDict->SetNewFor<CPDF_String>(pdfium::form_fields::kV, opt_value);
+    m_pDict->SetNewFor<CPDF_String>(pdfium::form_fields::kV,
+                                    opt_value.AsStringView());
     return;
   }
 
-  CPDF_Array* pArray = m_pDict->SetNewFor<CPDF_Array>(pdfium::form_fields::kV);
+  auto pArray = m_pDict->SetNewFor<CPDF_Array>(pdfium::form_fields::kV);
   for (int i = 0; i < CountOptions(); i++) {
     if (i == index || IsItemSelected(i))
-      pArray->AddNew<CPDF_String>(GetOptionValue(i));
+      pArray->AppendNew<CPDF_String>(GetOptionValue(i).AsStringView());
   }
 }
 
-void CPDF_FormField::SetItemSelectionUnselected(int index,
-                                                const WideString& opt_value) {
-  const CPDF_Object* pValue = GetValueObject();
-  if (!pValue)
-    return;
-
-  if (GetType() != kListBox) {
-    m_pDict->RemoveFor(pdfium::form_fields::kV);
-    m_pDict->RemoveFor("I");
-    return;
-  }
-
-  SelectOption(index, false, NotificationOption::kDoNotNotify);
-  if (pValue->IsString()) {
-    if (pValue->GetUnicodeText() == opt_value) {
-      m_pDict->RemoveFor(pdfium::form_fields::kV);
-    }
-    return;
-  }
-
-  if (!pValue->IsArray())
-    return;
-
-  auto pArray = pdfium::MakeRetain<CPDF_Array>();
-  for (int i = 0; i < CountOptions(); i++) {
-    if (i != index && IsItemSelected(i))
-      pArray->AddNew<CPDF_String>(GetOptionValue(i));
-  }
-  if (pArray->size() > 0) {
-    m_pDict->SetFor(pdfium::form_fields::kV, pArray);
-  }
-}
-
-bool CPDF_FormField::IsItemDefaultSelected(int index) const {
-  ASSERT(GetType() == kComboBox || GetType() == kListBox);
-  if (index < 0 || index >= CountOptions())
-    return false;
-  int iDVIndex = GetDefaultSelectedItem();
-  return iDVIndex >= 0 && iDVIndex == index;
-}
-
 int CPDF_FormField::GetDefaultSelectedItem() const {
-  ASSERT(GetType() == kComboBox || GetType() == kListBox);
-  const CPDF_Object* pValue = GetDefaultValueObject();
+  DCHECK(GetType() == kComboBox || GetType() == kListBox);
+  RetainPtr<const CPDF_Object> pValue = GetDefaultValueObject();
   if (!pValue)
     return -1;
   WideString csDV = pValue->GetUnicodeText();
@@ -609,22 +565,27 @@
 }
 
 int CPDF_FormField::CountOptions() const {
-  const CPDF_Array* pArray = ToArray(GetFieldAttr(m_pDict.Get(), "Opt"));
-  return pArray ? pArray->size() : 0;
+  RetainPtr<const CPDF_Array> pArray = ToArray(GetFieldAttrInternal("Opt"));
+  return pArray ? fxcrt::CollectionSize<int>(*pArray) : 0;
 }
 
 WideString CPDF_FormField::GetOptionText(int index, int sub_index) const {
-  const CPDF_Array* pArray = ToArray(GetFieldAttr(m_pDict.Get(), "Opt"));
+  RetainPtr<const CPDF_Array> pArray = ToArray(GetFieldAttrInternal("Opt"));
   if (!pArray)
     return WideString();
 
-  const CPDF_Object* pOption = pArray->GetDirectObjectAt(index);
+  RetainPtr<const CPDF_Object> pOption = pArray->GetDirectObjectAt(index);
   if (!pOption)
     return WideString();
-  if (const CPDF_Array* pOptionArray = pOption->AsArray())
+
+  const CPDF_Array* pOptionArray = pOption->AsArray();
+  if (pOptionArray)
     pOption = pOptionArray->GetDirectObjectAt(sub_index);
 
-  const CPDF_String* pString = ToString(pOption);
+  if (!pOption)
+    return WideString();
+
+  const CPDF_String* pString = pOption->AsString();
   return pString ? pString->GetUnicodeText() : WideString();
 }
 
@@ -647,7 +608,7 @@
 bool CPDF_FormField::CheckControl(int iControlIndex,
                                   bool bChecked,
                                   NotificationOption notify) {
-  ASSERT(GetType() == kCheckBox || GetType() == kRadioButton);
+  DCHECK(GetType() == kCheckBox || GetType() == kRadioButton);
   CPDF_FormControl* pControl = GetControl(iControlIndex);
   if (!pControl)
     return false;
@@ -676,9 +637,9 @@
     }
   }
 
-  const CPDF_Object* pOpt = GetFieldAttr(m_pDict.Get(), "Opt");
+  RetainPtr<const CPDF_Object> pOpt = GetFieldAttrInternal("Opt");
   if (!ToArray(pOpt)) {
-    ByteString csBExport = PDF_EncodeText(csWExport);
+    ByteString csBExport = PDF_EncodeText(csWExport.AsStringView());
     if (bChecked) {
       m_pDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kV, csBExport);
     } else {
@@ -691,15 +652,15 @@
     }
   } else if (bChecked) {
     m_pDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kV,
-                                  ByteString::Format("%d", iControlIndex));
+                                  ByteString::FormatInteger(iControlIndex));
   }
-  if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify())
-    m_pForm->GetFormNotify()->AfterCheckedStatusChange(this);
+  if (notify == NotificationOption::kNotify)
+    m_pForm->NotifyAfterCheckedStatusChange(this);
   return true;
 }
 
 WideString CPDF_FormField::GetCheckValue(bool bDefault) const {
-  ASSERT(GetType() == kCheckBox || GetType() == kRadioButton);
+  DCHECK(GetType() == kCheckBox || GetType() == kRadioButton);
   WideString csExport = L"Off";
   int iCount = CountControls();
   for (int i = 0; i < iCount; i++) {
@@ -717,7 +678,7 @@
 bool CPDF_FormField::SetCheckValue(const WideString& value,
                                    bool bDefault,
                                    NotificationOption notify) {
-  ASSERT(GetType() == kCheckBox || GetType() == kRadioButton);
+  DCHECK(GetType() == kCheckBox || GetType() == kRadioButton);
   int iCount = CountControls();
   for (int i = 0; i < iCount; i++) {
     CPDF_FormControl* pControl = GetControl(i);
@@ -730,163 +691,170 @@
     if (val)
       break;
   }
-  if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify())
-    m_pForm->GetFormNotify()->AfterCheckedStatusChange(this);
+  if (notify == NotificationOption::kNotify)
+    m_pForm->NotifyAfterCheckedStatusChange(this);
   return true;
 }
 
 int CPDF_FormField::GetTopVisibleIndex() const {
-  const CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), "TI");
+  RetainPtr<const CPDF_Object> pObj = GetFieldAttrInternal("TI");
   return pObj ? pObj->GetInteger() : 0;
 }
 
 int CPDF_FormField::CountSelectedOptions() const {
-  const CPDF_Array* pArray = ToArray(GetSelectedIndicesObject());
-  return pArray ? pArray->size() : 0;
+  RetainPtr<const CPDF_Array> pArray = ToArray(GetSelectedIndicesObject());
+  return pArray ? fxcrt::CollectionSize<int>(*pArray) : 0;
 }
 
 int CPDF_FormField::GetSelectedOptionIndex(int index) const {
-  const CPDF_Array* pArray = ToArray(GetSelectedIndicesObject());
+  if (index < 0)
+    return 0;
+
+  RetainPtr<const CPDF_Array> pArray = ToArray(GetSelectedIndicesObject());
   if (!pArray)
     return -1;
 
-  int iCount = pArray->size();
-  if (iCount < 0 || index >= iCount)
-    return -1;
-  return pArray->GetIntegerAt(index);
+  return index < fxcrt::CollectionSize<int>(*pArray)
+             ? pArray->GetIntegerAt(index)
+             : -1;
 }
 
-bool CPDF_FormField::IsOptionSelected(int iOptIndex) const {
-  const CPDF_Array* pArray = ToArray(GetSelectedIndicesObject());
-  if (!pArray)
+bool CPDF_FormField::IsSelectedOption(const WideString& wsOptValue) const {
+  RetainPtr<const CPDF_Object> pValueObject = GetValueObject();
+  if (!pValueObject)
     return false;
 
-  CPDF_ArrayLocker locker(pArray);
-  for (const auto& pObj : locker) {
-    if (pObj->GetInteger() == iOptIndex)
-      return true;
+  const CPDF_Array* pValueArray = pValueObject->AsArray();
+  if (pValueArray) {
+    CPDF_ArrayLocker locker(pValueArray);
+    for (const auto& pObj : locker) {
+      if (pObj->IsString() && pObj->GetUnicodeText() == wsOptValue)
+        return true;
+    }
   }
-  return false;
+
+  return pValueObject->IsString() &&
+         pValueObject->GetUnicodeText() == wsOptValue;
 }
 
-bool CPDF_FormField::SelectOption(int iOptIndex,
-                                  bool bSelected,
-                                  NotificationOption notify) {
-  CPDF_Array* pArray = m_pDict->GetArrayFor("I");
-  if (!pArray) {
-    if (!bSelected)
-      return true;
+bool CPDF_FormField::IsSelectedIndex(int iOptIndex) const {
+  RetainPtr<const CPDF_Object> pSelectedIndicesObject =
+      GetSelectedIndicesObject();
+  if (!pSelectedIndicesObject)
+    return false;
 
-    pArray = m_pDict->SetNewFor<CPDF_Array>("I");
+  const CPDF_Array* pSelectedIndicesArray = pSelectedIndicesObject->AsArray();
+  if (pSelectedIndicesArray) {
+    CPDF_ArrayLocker locker(pSelectedIndicesArray);
+    for (const auto& pObj : locker) {
+      if (pObj->IsNumber() && pObj->GetInteger() == iOptIndex)
+        return true;
+    }
   }
 
-  bool bReturn = false;
+  return pSelectedIndicesObject->IsNumber() &&
+         pSelectedIndicesObject->GetInteger() == iOptIndex;
+}
+
+void CPDF_FormField::SelectOption(int iOptIndex) {
+  RetainPtr<CPDF_Array> pArray = m_pDict->GetOrCreateArrayFor("I");
   for (size_t i = 0; i < pArray->size(); i++) {
     int iFind = pArray->GetIntegerAt(i);
-    if (iFind == iOptIndex) {
-      if (bSelected)
-        return true;
-
-      if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify()) {
-        WideString csValue = GetOptionLabel(iOptIndex);
-        if (!NotifyListOrComboBoxBeforeChange(csValue))
-          return false;
-      }
-      pArray->RemoveAt(i);
-      bReturn = true;
-      break;
-    }
+    if (iFind == iOptIndex)
+      return;
 
     if (iFind > iOptIndex) {
-      if (!bSelected)
-        continue;
-
-      if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify()) {
-        WideString csValue = GetOptionLabel(iOptIndex);
-        if (!NotifyListOrComboBoxBeforeChange(csValue))
-          return false;
-      }
       pArray->InsertNewAt<CPDF_Number>(i, iOptIndex);
-      bReturn = true;
-      break;
+      return;
     }
   }
-  if (!bReturn) {
-    if (bSelected)
-      pArray->AddNew<CPDF_Number>(iOptIndex);
-    if (pArray->IsEmpty())
-      m_pDict->RemoveFor("I");
+  pArray->AppendNew<CPDF_Number>(iOptIndex);
+}
+
+bool CPDF_FormField::UseSelectedIndicesObject() const {
+  DCHECK(GetType() == kComboBox || GetType() == kListBox);
+
+  RetainPtr<const CPDF_Object> pSelectedIndicesObject =
+      GetSelectedIndicesObject();
+  if (!pSelectedIndicesObject)
+    return false;
+
+  // If there's not value object, then just use the indices object.
+  RetainPtr<const CPDF_Object> pValueObject = GetValueObject();
+  if (!pValueObject)
+    return true;
+
+  // Verify that the selected indices object is either an array or a number and
+  // count the number of indices.
+  size_t selected_indices_size;
+  const CPDF_Array* pSelectedIndicesArray = pSelectedIndicesObject->AsArray();
+  if (pSelectedIndicesArray)
+    selected_indices_size = pSelectedIndicesArray->size();
+  else if (pSelectedIndicesObject->IsNumber())
+    selected_indices_size = 1;
+  else
+    return false;
+
+  // Verify that the number of values is equal to |selected_indices_size|. Then,
+  // count the number of occurrences of each of the distinct values in the
+  // values object.
+  std::map<WideString, size_t> values;
+  const CPDF_Array* pValueArray = pValueObject->AsArray();
+  if (pValueArray) {
+    if (pValueArray->size() != selected_indices_size)
+      return false;
+    CPDF_ArrayLocker locker(pValueArray);
+    for (const auto& pObj : locker) {
+      if (pObj->IsString())
+        values[pObj->GetUnicodeText()]++;
+    }
+  } else if (pValueObject->IsString()) {
+    if (selected_indices_size != 1)
+      return false;
+    values[pValueObject->GetUnicodeText()]++;
   }
-  if (notify == NotificationOption::kNotify)
-    NotifyListOrComboBoxAfterChange();
 
-  return true;
-}
+  // Validate each index in the selected indices object. Then, verify that items
+  // identified by selected indices entry do not differ from those in the values
+  // entry of the field dictionary.
+  const int num_options = CountOptions();
+  if (pSelectedIndicesArray) {
+    CPDF_ArrayLocker locker(pSelectedIndicesArray);
+    for (const auto& pObj : locker) {
+      if (!pObj->IsNumber())
+        return false;
 
-void CPDF_FormField::LoadDA() {
-  CPDF_Dictionary* pFormDict = m_pForm->GetFormDict();
-  if (!pFormDict)
-    return;
+      int index = pObj->GetInteger();
+      if (index < 0 || index >= num_options)
+        return false;
 
-  ByteString DA;
-  if (const CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), "DA"))
-    DA = pObj->GetString();
+      WideString wsOptValue = GetOptionValue(index);
+      auto it = values.find(wsOptValue);
+      if (it == values.end())
+        return false;
 
-  if (DA.IsEmpty())
-    DA = pFormDict->GetStringFor("DA");
+      it->second--;
+      if (it->second == 0)
+        values.erase(it);
+    }
 
-  if (DA.IsEmpty())
-    return;
+    return values.empty();
+  }
 
-  CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR");
-  if (!pDR)
-    return;
+  DCHECK(pSelectedIndicesObject->IsNumber());
+  int index = pSelectedIndicesObject->GetInteger();
+  if (index < 0 || index >= num_options)
+    return false;
 
-  CPDF_Dictionary* pFont = pDR->GetDictFor("Font");
-  if (!ValidateFontResourceDict(pFont))
-    return;
-
-  CPDF_DefaultAppearance appearance(DA);
-  Optional<ByteString> font_name = appearance.GetFont(&m_FontSize);
-  if (!font_name)
-    return;
-
-  CPDF_Dictionary* pFontDict = pFont->GetDictFor(*font_name);
-  if (!pFontDict)
-    return;
-
-  auto* pData = CPDF_DocPageData::FromDocument(m_pForm->GetDocument());
-  m_pFont = pData->GetFont(pFontDict);
-}
-
-bool CPDF_FormField::NotifyBeforeSelectionChange(const WideString& value) {
-  auto* pNotify = m_pForm->GetFormNotify();
-  return !pNotify || pNotify->BeforeSelectionChange(this, value);
-}
-
-void CPDF_FormField::NotifyAfterSelectionChange() {
-  auto* pNotify = m_pForm->GetFormNotify();
-  if (pNotify)
-    pNotify->AfterSelectionChange(this);
-}
-
-bool CPDF_FormField::NotifyBeforeValueChange(const WideString& value) {
-  auto* pNotify = m_pForm->GetFormNotify();
-  return !pNotify || pNotify->BeforeValueChange(this, value);
-}
-
-void CPDF_FormField::NotifyAfterValueChange() {
-  auto* pNotify = m_pForm->GetFormNotify();
-  if (pNotify)
-    pNotify->AfterValueChange(this);
+  return pdfium::Contains(values, GetOptionValue(index));
 }
 
 bool CPDF_FormField::NotifyListOrComboBoxBeforeChange(const WideString& value) {
   switch (GetType()) {
     case kListBox:
-      return NotifyBeforeSelectionChange(value);
+      return m_pForm->NotifyBeforeSelectionChange(this, value);
     case kComboBox:
-      return NotifyBeforeValueChange(value);
+      return m_pForm->NotifyBeforeValueChange(this, value);
     default:
       return true;
   }
@@ -895,32 +863,42 @@
 void CPDF_FormField::NotifyListOrComboBoxAfterChange() {
   switch (GetType()) {
     case kListBox:
-      NotifyAfterSelectionChange();
+      m_pForm->NotifyAfterSelectionChange(this);
       break;
     case kComboBox:
-      NotifyAfterValueChange();
+      m_pForm->NotifyAfterValueChange(this);
       break;
     default:
       break;
   }
 }
 
-const CPDF_Object* CPDF_FormField::GetDefaultValueObject() const {
-  return GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kDV);
+RetainPtr<const CPDF_Object> CPDF_FormField::GetFieldAttrInternal(
+    const ByteString& name) const {
+  return GetFieldAttrRecursive(m_pDict.Get(), name, 0);
 }
 
-const CPDF_Object* CPDF_FormField::GetValueObject() const {
-  return GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kV);
+const CPDF_Dictionary* CPDF_FormField::GetFieldDictInternal() const {
+  return m_pDict.Get();
 }
 
-const CPDF_Object* CPDF_FormField::GetSelectedIndicesObject() const {
-  ASSERT(GetType() == kComboBox || GetType() == kListBox);
-  return GetFieldAttr(m_pDict.Get(), "I");
+RetainPtr<const CPDF_Object> CPDF_FormField::GetDefaultValueObject() const {
+  return GetFieldAttrInternal(pdfium::form_fields::kDV);
 }
 
-const CPDF_Object* CPDF_FormField::GetValueOrSelectedIndicesObject() const {
-  ASSERT(GetType() == kComboBox || GetType() == kListBox);
-  const CPDF_Object* pValue = GetValueObject();
+RetainPtr<const CPDF_Object> CPDF_FormField::GetValueObject() const {
+  return GetFieldAttrInternal(pdfium::form_fields::kV);
+}
+
+RetainPtr<const CPDF_Object> CPDF_FormField::GetSelectedIndicesObject() const {
+  DCHECK(GetType() == kComboBox || GetType() == kListBox);
+  return GetFieldAttrInternal("I");
+}
+
+RetainPtr<const CPDF_Object> CPDF_FormField::GetValueOrSelectedIndicesObject()
+    const {
+  DCHECK(GetType() == kComboBox || GetType() == kListBox);
+  RetainPtr<const CPDF_Object> pValue = GetValueObject();
   return pValue ? pValue : GetSelectedIndicesObject();
 }
 
diff --git a/core/fpdfdoc/cpdf_formfield.h b/core/fpdfdoc/cpdf_formfield.h
index aa51ddc..e6e4e9b 100644
--- a/core/fpdfdoc/cpdf_formfield.h
+++ b/core/fpdfdoc/cpdf_formfield.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,24 +7,24 @@
 #ifndef CORE_FPDFDOC_CPDF_FORMFIELD_H_
 #define CORE_FPDFDOC_CPDF_FORMFIELD_H_
 
-#include <memory>
+#include <stddef.h>
+#include <stdint.h>
+
 #include <utility>
 #include <vector>
 
 #include "core/fpdfdoc/cpdf_aaction.h"
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_Dictionary;
-class CPDF_Font;
 class CPDF_FormControl;
 class CPDF_InteractiveForm;
 class CPDF_Object;
 class CPDF_String;
 
-enum class NotificationOption { kDoNotNotify = 0, kNotify };
+enum class NotificationOption : bool { kDoNotNotify = false, kNotify = true };
 
 enum class FormFieldType : uint8_t {
   kUnknown = 0,
@@ -69,23 +69,24 @@
     kSign
   };
 
-  CPDF_FormField(CPDF_InteractiveForm* pForm, CPDF_Dictionary* pDict);
+  CPDF_FormField(CPDF_InteractiveForm* pForm, RetainPtr<CPDF_Dictionary> pDict);
   ~CPDF_FormField();
 
-  static Optional<FormFieldType> IntToFormFieldType(int value);
-
-  static const CPDF_Object* GetFieldAttr(const CPDF_Dictionary* pFieldDict,
-                                         const ByteString& name);
-  static CPDF_Object* GetFieldAttr(CPDF_Dictionary* pFieldDict,
-                                   const ByteString& name);
-
-  static WideString GetFullNameForDict(CPDF_Dictionary* pFieldDict);
+  static absl::optional<FormFieldType> IntToFormFieldType(int value);
+  static WideString GetFullNameForDict(const CPDF_Dictionary* pFieldDict);
+  static RetainPtr<const CPDF_Object> GetFieldAttrForDict(
+      const CPDF_Dictionary* pFieldDict,
+      const ByteString& name);
+  static RetainPtr<CPDF_Object> GetMutableFieldAttrForDict(
+      CPDF_Dictionary* pFieldDict,
+      const ByteString& name);
 
   WideString GetFullName() const;
   Type GetType() const { return m_Type; }
 
-  CPDF_Dictionary* GetFieldDict() const { return m_pDict.Get(); }
-  bool ResetField(NotificationOption notify);
+  RetainPtr<const CPDF_Object> GetFieldAttr(const ByteString& name) const;
+  RetainPtr<const CPDF_Dictionary> GetFieldDict() const;
+  bool ResetField();
 
   int CountControls() const;
   CPDF_FormControl* GetControl(int index) const;
@@ -98,7 +99,7 @@
   WideString GetMappingName() const;
 
   uint32_t GetFieldFlags() const;
-  ByteString GetDefaultStyle() const;
+  void SetFieldFlags(uint32_t dwFlags);
 
   bool IsRequired() const { return m_bRequired; }
   bool IsNoExport() const { return m_bNoExport; }
@@ -113,9 +114,7 @@
 
   bool ClearSelection(NotificationOption notify);
   bool IsItemSelected(int index) const;
-  bool SetItemSelection(int index, bool bSelected, NotificationOption notify);
-
-  bool IsItemDefaultSelected(int index) const;
+  bool SetItemSelection(int index, NotificationOption notify);
 
   int GetDefaultSelectedItem() const;
   int CountOptions() const;
@@ -130,19 +129,16 @@
   int GetTopVisibleIndex() const;
   int CountSelectedOptions() const;
   int GetSelectedOptionIndex(int index) const;
-  bool IsOptionSelected(int iOptIndex) const;
-  bool SelectOption(int iOptIndex, bool bSelected, NotificationOption notify);
+  bool IsSelectedOption(const WideString& wsOptValue) const;
+  bool IsSelectedIndex(int iOptIndex) const;
+  void SelectOption(int iOptIndex);
 
-  float GetFontSize() const { return m_FontSize; }
-  CPDF_Font* GetFont() const { return m_pFont.Get(); }
-
-  CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
-  CPDF_InteractiveForm* GetForm() const { return m_pForm.Get(); }
+  // Verifies if there is a valid selected indicies (/I) object and whether its
+  // entries are consistent with the value (/V) object.
+  bool UseSelectedIndicesObject() const;
 
   WideString GetCheckValue(bool bDefault) const;
 
-  void SetOpt(RetainPtr<CPDF_Object> pOpt);
-
  private:
   WideString GetValue(bool bDefault) const;
   bool SetValue(const WideString& value,
@@ -156,23 +152,21 @@
                      bool bDefault,
                      NotificationOption notify);
   void SetItemSelectionSelected(int index, const WideString& opt_value);
-  void SetItemSelectionUnselected(int index, const WideString& opt_value);
-  bool NotifyBeforeSelectionChange(const WideString& value);
-  void NotifyAfterSelectionChange();
-  bool NotifyBeforeValueChange(const WideString& value);
-  void NotifyAfterValueChange();
   bool NotifyListOrComboBoxBeforeChange(const WideString& value);
   void NotifyListOrComboBoxAfterChange();
 
-  const CPDF_Object* GetDefaultValueObject() const;
-  const CPDF_Object* GetValueObject() const;
+  RetainPtr<const CPDF_Object> GetFieldAttrInternal(
+      const ByteString& name) const;
+  const CPDF_Dictionary* GetFieldDictInternal() const;
+  RetainPtr<const CPDF_Object> GetDefaultValueObject() const;
+  RetainPtr<const CPDF_Object> GetValueObject() const;
 
   // For choice fields.
-  const CPDF_Object* GetSelectedIndicesObject() const;
+  RetainPtr<const CPDF_Object> GetSelectedIndicesObject() const;
 
   // For choice fields.
   // Value object takes precedence over selected indices object.
-  const CPDF_Object* GetValueOrSelectedIndicesObject() const;
+  RetainPtr<const CPDF_Object> GetValueOrSelectedIndicesObject() const;
 
   const std::vector<UnownedPtr<CPDF_FormControl>>& GetControls() const;
 
@@ -181,10 +175,9 @@
   bool m_bNoExport = false;
   bool m_bIsMultiSelectListBox = false;
   bool m_bIsUnison = false;
-  float m_FontSize = 0;
+  bool m_bUseSelectedIndices = false;
   UnownedPtr<CPDF_InteractiveForm> const m_pForm;
   RetainPtr<CPDF_Dictionary> const m_pDict;
-  RetainPtr<CPDF_Font> m_pFont;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_FORMFIELD_H_
diff --git a/core/fpdfdoc/cpdf_formfield_unittest.cpp b/core/fpdfdoc/cpdf_formfield_unittest.cpp
index ca75891..41b99f5 100644
--- a/core/fpdfdoc/cpdf_formfield_unittest.cpp
+++ b/core/fpdfdoc/cpdf_formfield_unittest.cpp
@@ -1,49 +1,335 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfdoc/cpdf_formfield.h"
 
+#include <vector>
+
+#include "constants/form_fields.h"
+#include "constants/form_flags.h"
+#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
+#include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fpdfapi/parser/cpdf_test_document.h"
+#include "core/fpdfdoc/cpdf_interactiveform.h"
+#include "core/fxcrt/fx_memory.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/containers/contains.h"
 
-TEST(cpdf_formfield, GetFullNameForDict) {
+namespace {
+
+// Create and destroys the page module that is necessary when instantiating a
+// CPDF_Document.
+class ScopedCPDF_PageModule {
+ public:
+  FX_STACK_ALLOCATED();
+
+  ScopedCPDF_PageModule() { CPDF_PageModule::Create(); }
+  ~ScopedCPDF_PageModule() { CPDF_PageModule::Destroy(); }
+};
+
+void TestMultiselectFieldDict(RetainPtr<CPDF_Array> opt_array,
+                              RetainPtr<CPDF_Object> values,
+                              RetainPtr<CPDF_Object> selected_indices,
+                              bool expected_use_indices,
+                              const std::vector<int>& expected_indices,
+                              const std::vector<int>& excluded_indices) {
+  auto form_dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  form_dict->SetNewFor<CPDF_Name>("Type", "Annot");
+  form_dict->SetNewFor<CPDF_Name>("Subtype", "Widget");
+  form_dict->SetNewFor<CPDF_Name>(pdfium::form_fields::kFT,
+                                  pdfium::form_fields::kCh);
+  constexpr int kMuliSelectFlag = pdfium::form_flags::kChoiceMultiSelect;
+  form_dict->SetNewFor<CPDF_Number>(pdfium::form_fields::kFf, kMuliSelectFlag);
+  form_dict->SetFor("Opt", opt_array);
+  form_dict->SetFor(pdfium::form_fields::kV, values);
+  form_dict->SetFor("I", selected_indices);
+
+  CPDF_TestDocument doc;
+  CPDF_InteractiveForm form(&doc);
+  CPDF_FormField form_field(&form, std::move(form_dict));
+  EXPECT_EQ(expected_use_indices, form_field.UseSelectedIndicesObject());
+  for (int i = 0; i < form_field.CountOptions(); i++) {
+    const bool expected_selected = pdfium::Contains(expected_indices, i);
+    EXPECT_EQ(expected_selected, form_field.IsItemSelected(i));
+  }
+  for (int i : excluded_indices) {
+    EXPECT_FALSE(form_field.IsItemSelected(i));
+  }
+}
+
+}  // namespace
+
+TEST(CPDF_FormFieldTest, GetFullNameForDict) {
   WideString name = CPDF_FormField::GetFullNameForDict(nullptr);
   EXPECT_TRUE(name.IsEmpty());
 
   CPDF_IndirectObjectHolder obj_holder;
-  CPDF_Dictionary* root = obj_holder.NewIndirect<CPDF_Dictionary>();
+  auto root = obj_holder.NewIndirect<CPDF_Dictionary>();
   root->SetNewFor<CPDF_Name>("T", "foo");
-  name = CPDF_FormField::GetFullNameForDict(root);
+  name = CPDF_FormField::GetFullNameForDict(root.Get());
   EXPECT_STREQ("foo", name.ToUTF8().c_str());
 
-  CPDF_Dictionary* dict1 = obj_holder.NewIndirect<CPDF_Dictionary>();
+  auto dict1 = obj_holder.NewIndirect<CPDF_Dictionary>();
   root->SetNewFor<CPDF_Reference>("Parent", &obj_holder, dict1->GetObjNum());
   dict1->SetNewFor<CPDF_Name>("T", "bar");
-  name = CPDF_FormField::GetFullNameForDict(root);
+  name = CPDF_FormField::GetFullNameForDict(root.Get());
   EXPECT_STREQ("bar.foo", name.ToUTF8().c_str());
 
-  CPDF_Dictionary* dict2 = dict1->SetNewFor<CPDF_Dictionary>("Parent");
-  name = CPDF_FormField::GetFullNameForDict(root);
+  auto dict2 = dict1->SetNewFor<CPDF_Dictionary>("Parent");
+  name = CPDF_FormField::GetFullNameForDict(root.Get());
   EXPECT_STREQ("bar.foo", name.ToUTF8().c_str());
 
-  CPDF_Dictionary* dict3 = obj_holder.NewIndirect<CPDF_Dictionary>();
+  auto dict3 = obj_holder.NewIndirect<CPDF_Dictionary>();
   dict2->SetNewFor<CPDF_Reference>("Parent", &obj_holder, dict3->GetObjNum());
 
   dict3->SetNewFor<CPDF_Name>("T", "qux");
-  name = CPDF_FormField::GetFullNameForDict(root);
+  name = CPDF_FormField::GetFullNameForDict(root.Get());
   EXPECT_STREQ("qux.bar.foo", name.ToUTF8().c_str());
 
   dict3->SetNewFor<CPDF_Reference>("Parent", &obj_holder, root->GetObjNum());
-  name = CPDF_FormField::GetFullNameForDict(root);
+  name = CPDF_FormField::GetFullNameForDict(root.Get());
   EXPECT_STREQ("qux.bar.foo", name.ToUTF8().c_str());
-  name = CPDF_FormField::GetFullNameForDict(dict1);
+  name = CPDF_FormField::GetFullNameForDict(dict1.Get());
   EXPECT_STREQ("foo.qux.bar", name.ToUTF8().c_str());
-  name = CPDF_FormField::GetFullNameForDict(dict2);
+  name = CPDF_FormField::GetFullNameForDict(dict2.Get());
   EXPECT_STREQ("bar.foo.qux", name.ToUTF8().c_str());
-  name = CPDF_FormField::GetFullNameForDict(dict3);
+  name = CPDF_FormField::GetFullNameForDict(dict3.Get());
   EXPECT_STREQ("bar.foo.qux", name.ToUTF8().c_str());
 }
+
+TEST(CPDF_FormFieldTest, IsItemSelected) {
+  ScopedCPDF_PageModule page_module;
+
+  auto opt_array = pdfium::MakeRetain<CPDF_Array>();
+  opt_array->AppendNew<CPDF_String>(L"Alpha");
+  opt_array->AppendNew<CPDF_String>(L"Beta");
+  opt_array->AppendNew<CPDF_String>(L"Gamma");
+  opt_array->AppendNew<CPDF_String>(L"Delta");
+  opt_array->AppendNew<CPDF_String>(L"Epsilon");
+
+  {
+    // No Values (/V) or Selected Indices (/I) objects.
+    std::vector<int> expected_indices;
+    std::vector<int> excluded_indices{-1, 5};
+    TestMultiselectFieldDict(opt_array, /*values=*/nullptr,
+                             /*selected_indices=*/nullptr,
+                             /*expected_use_indices=*/false, expected_indices,
+                             excluded_indices);
+  }
+  {
+    // Values (/V) object is just a string.
+    auto values = pdfium::MakeRetain<CPDF_String>(/*pPool=*/nullptr, L"Gamma");
+    std::vector<int> expected_indices{2};
+    std::vector<int> excluded_indices{-1, 5};
+    TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
+                             /*expected_use_indices=*/false, expected_indices,
+                             excluded_indices);
+  }
+  {
+    // Values (/V) object is just an invalid string.
+    auto values = pdfium::MakeRetain<CPDF_String>(/*pPool=*/nullptr, L"Omega");
+    std::vector<int> expected_indices;
+    std::vector<int> excluded_indices{-1, 5};
+    TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
+                             /*expected_use_indices=*/false, expected_indices,
+                             excluded_indices);
+  }
+  {
+    // Values (/V) object is an array with one object.
+    auto values = pdfium::MakeRetain<CPDF_Array>();
+    values->AppendNew<CPDF_String>(L"Beta");
+    std::vector<int> expected_indices{1};
+    std::vector<int> excluded_indices{-1, 5};
+    TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
+                             /*expected_use_indices=*/false, expected_indices,
+                             excluded_indices);
+  }
+  {
+    // Values (/V) object is an array with one invalid object.
+    auto values = pdfium::MakeRetain<CPDF_Array>();
+    values->AppendNew<CPDF_String>(L"Omega");
+    std::vector<int> expected_indices;
+    std::vector<int> excluded_indices{-1, 5};
+    TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
+                             /*expected_use_indices=*/false, expected_indices,
+                             excluded_indices);
+  }
+  {
+    // Values (/V) object is an array with multiple objects.
+    auto values = pdfium::MakeRetain<CPDF_Array>();
+    values->AppendNew<CPDF_String>(L"Beta");
+    values->AppendNew<CPDF_String>(L"Epsilon");
+    std::vector<int> expected_indices{1, 4};
+    std::vector<int> excluded_indices{-1, 5};
+    TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
+                             /*expected_use_indices=*/false, expected_indices,
+                             excluded_indices);
+  }
+  {
+    // Values (/V) object is an array with multiple objects with one invalid.
+    auto values = pdfium::MakeRetain<CPDF_Array>();
+    values->AppendNew<CPDF_String>(L"Beta");
+    values->AppendNew<CPDF_String>(L"Epsilon");
+    values->AppendNew<CPDF_String>(L"Omega");
+    std::vector<int> expected_indices{1, 4};
+    std::vector<int> excluded_indices{-1, 5};
+    TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
+                             /*expected_use_indices=*/false, expected_indices,
+                             excluded_indices);
+  }
+  {
+    // Selected indices (/I) object is just a number.
+    auto selected_indices = pdfium::MakeRetain<CPDF_Number>(3);
+    std::vector<int> expected_indices{3};
+    std::vector<int> excluded_indices{-1, 5};
+    TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices,
+                             /*expected_use_indices=*/true, expected_indices,
+                             excluded_indices);
+  }
+  {
+    // Selected indices (/I) object is just an invalid number.
+    auto selected_indices = pdfium::MakeRetain<CPDF_Number>(26);
+    std::vector<int> expected_indices;
+    std::vector<int> excluded_indices{-1, 5, 26};
+    TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices,
+                             /*expected_use_indices=*/true, expected_indices,
+                             excluded_indices);
+  }
+  {
+    // Selected indices (/I) object is an array with one object.
+    auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
+    selected_indices->AppendNew<CPDF_Number>(0);
+    std::vector<int> expected_indices{0};
+    std::vector<int> excluded_indices{-1, 5};
+    TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices,
+                             /*expected_use_indices=*/true, expected_indices,
+                             excluded_indices);
+  }
+  {
+    // Selected indices (/I) object is an array with multiple objects.
+    auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
+    selected_indices->AppendNew<CPDF_Number>(0);
+    selected_indices->AppendNew<CPDF_Number>(2);
+    selected_indices->AppendNew<CPDF_Number>(3);
+    std::vector<int> expected_indices{0, 2, 3};
+    std::vector<int> excluded_indices{-1, 5};
+    TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices,
+                             /*expected_use_indices=*/true, expected_indices,
+                             excluded_indices);
+  }
+  {
+    // Selected indices (/I) object is an array with multiple objects and some
+    // are invalid.
+    auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
+    selected_indices->AppendNew<CPDF_Number>(0);
+    selected_indices->AppendNew<CPDF_Number>(2);
+    selected_indices->AppendNew<CPDF_Number>(3);
+    selected_indices->AppendNew<CPDF_Number>(-5);
+    selected_indices->AppendNew<CPDF_Number>(12);
+    selected_indices->AppendNew<CPDF_Number>(42);
+    std::vector<int> expected_indices{0, 2, 3};
+    std::vector<int> excluded_indices{-5, -1, 5, 12, 42};
+    TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices,
+                             /*expected_use_indices=*/true, expected_indices,
+                             excluded_indices);
+  }
+  {
+    // Values (/V) or Selected Indices (/I) objects conflict with different
+    // lengths.
+    auto values = pdfium::MakeRetain<CPDF_Array>();
+    values->AppendNew<CPDF_String>(L"Beta");
+    values->AppendNew<CPDF_String>(L"Epsilon");
+    auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
+    selected_indices->AppendNew<CPDF_Number>(0);
+    selected_indices->AppendNew<CPDF_Number>(2);
+    selected_indices->AppendNew<CPDF_Number>(3);
+    std::vector<int> expected_indices{1, 4};
+    std::vector<int> excluded_indices{-1, 5};
+    TestMultiselectFieldDict(opt_array, values, selected_indices,
+                             /*expected_use_indices=*/false, expected_indices,
+                             excluded_indices);
+  }
+  {
+    // Values (/V) or Selected Indices (/I) objects conflict with same lengths.
+    auto values = pdfium::MakeRetain<CPDF_Array>();
+    values->AppendNew<CPDF_String>(L"Alpha");
+    values->AppendNew<CPDF_String>(L"Epsilon");
+    auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
+    selected_indices->AppendNew<CPDF_Number>(2);
+    selected_indices->AppendNew<CPDF_Number>(3);
+    std::vector<int> expected_indices{0, 4};
+    std::vector<int> excluded_indices{-1, 5};
+    TestMultiselectFieldDict(opt_array, values, selected_indices,
+                             /*expected_use_indices=*/false, expected_indices,
+                             excluded_indices);
+  }
+  {
+    // Values (/V) or Selected Indices (/I) objects conflict with values being
+    // invalid.
+    auto values = pdfium::MakeRetain<CPDF_Array>();
+    values->AppendNew<CPDF_String>(L"Beta");
+    values->AppendNew<CPDF_String>(L"Epsilon");
+    values->AppendNew<CPDF_String>(L"Omega");
+    auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
+    selected_indices->AppendNew<CPDF_Number>(1);
+    selected_indices->AppendNew<CPDF_Number>(4);
+    std::vector<int> expected_indices{1, 4};
+    std::vector<int> excluded_indices{-1, 5};
+    TestMultiselectFieldDict(opt_array, values, selected_indices,
+                             /*expected_use_indices=*/false, expected_indices,
+                             excluded_indices);
+  }
+  {
+    // Values (/V) or Selected Indices (/I) objects conflict with selected
+    // indices being invalid.
+    auto values = pdfium::MakeRetain<CPDF_Array>();
+    values->AppendNew<CPDF_String>(L"Beta");
+    values->AppendNew<CPDF_String>(L"Epsilon");
+    auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
+    selected_indices->AppendNew<CPDF_Number>(1);
+    selected_indices->AppendNew<CPDF_Number>(4);
+    selected_indices->AppendNew<CPDF_Number>(26);
+    std::vector<int> expected_indices{1, 4};
+    std::vector<int> excluded_indices{-1, 5, 26};
+    TestMultiselectFieldDict(opt_array, values, selected_indices,
+                             /*expected_use_indices=*/false, expected_indices,
+                             excluded_indices);
+  }
+  {
+    // Values (/V) or Selected Indices (/I) objects conflict with both being
+    // invalid.
+    auto values = pdfium::MakeRetain<CPDF_Array>();
+    values->AppendNew<CPDF_String>(L"Beta");
+    values->AppendNew<CPDF_String>(L"Epsilon");
+    values->AppendNew<CPDF_String>(L"Omega");
+    auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
+    selected_indices->AppendNew<CPDF_Number>(0);
+    selected_indices->AppendNew<CPDF_Number>(2);
+    selected_indices->AppendNew<CPDF_Number>(3);
+    selected_indices->AppendNew<CPDF_Number>(26);
+    std::vector<int> expected_indices{1, 4};
+    std::vector<int> excluded_indices{-1, 5, 26};
+    TestMultiselectFieldDict(opt_array, values, selected_indices,
+                             /*expected_use_indices=*/false, expected_indices,
+                             excluded_indices);
+  }
+  {
+    // Values (/V) or Selected Indices (/I) objects conflict with each not being
+    // an array.
+    auto values = pdfium::MakeRetain<CPDF_String>(/*pPool=*/nullptr, L"Gamma");
+    auto selected_indices = pdfium::MakeRetain<CPDF_Number>(4);
+    std::vector<int> expected_indices{2};
+    std::vector<int> excluded_indices{-1, 5};
+    TestMultiselectFieldDict(opt_array, values, selected_indices,
+                             /*expected_use_indices=*/false, expected_indices,
+                             excluded_indices);
+  }
+}
diff --git a/core/fpdfdoc/cpdf_generateap.cpp b/core/fpdfdoc/cpdf_generateap.cpp
new file mode 100644
index 0000000..234da4c
--- /dev/null
+++ b/core/fpdfdoc/cpdf_generateap.cpp
@@ -0,0 +1,1380 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfdoc/cpdf_generateap.h"
+
+#include <algorithm>
+#include <sstream>
+#include <utility>
+
+#include "constants/annotation_common.h"
+#include "constants/appearance.h"
+#include "constants/font_encodings.h"
+#include "constants/form_fields.h"
+#include "core/fpdfapi/font/cpdf_font.h"
+#include "core/fpdfapi/page/cpdf_docpagedata.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_boolean.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fpdfapi/parser/cpdf_reference.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fpdfdoc/cpdf_annot.h"
+#include "core/fpdfdoc/cpdf_color_utils.h"
+#include "core/fpdfdoc/cpdf_defaultappearance.h"
+#include "core/fpdfdoc/cpdf_formfield.h"
+#include "core/fpdfdoc/cpvt_fontmap.h"
+#include "core/fpdfdoc/cpvt_variabletext.h"
+#include "core/fpdfdoc/cpvt_word.h"
+#include "core/fxcrt/fx_string_wrappers.h"
+#include "core/fxge/cfx_renderdevice.h"
+
+namespace {
+
+struct CPVT_Dash {
+  CPVT_Dash(int32_t dash, int32_t gap, int32_t phase)
+      : nDash(dash), nGap(gap), nPhase(phase) {}
+
+  int32_t nDash;
+  int32_t nGap;
+  int32_t nPhase;
+};
+
+enum class PaintOperation { kStroke, kFill };
+
+ByteString GetPDFWordString(IPVT_FontMap* pFontMap,
+                            int32_t nFontIndex,
+                            uint16_t Word,
+                            uint16_t SubWord) {
+  if (SubWord > 0)
+    return ByteString::Format("%c", SubWord);
+
+  if (!pFontMap)
+    return ByteString();
+
+  RetainPtr<CPDF_Font> pPDFFont = pFontMap->GetPDFFont(nFontIndex);
+  if (!pPDFFont)
+    return ByteString();
+
+  if (pPDFFont->GetBaseFontName() == "Symbol" ||
+      pPDFFont->GetBaseFontName() == "ZapfDingbats") {
+    return ByteString::Format("%c", Word);
+  }
+
+  ByteString sWord;
+  uint32_t dwCharCode = pPDFFont->CharCodeFromUnicode(Word);
+  if (dwCharCode != CPDF_Font::kInvalidCharCode)
+    pPDFFont->AppendChar(&sWord, dwCharCode);
+
+  return sWord;
+}
+
+ByteString GetWordRenderString(ByteStringView strWords) {
+  if (strWords.IsEmpty())
+    return ByteString();
+  return PDF_EncodeString(strWords) + " Tj\n";
+}
+
+ByteString GetFontSetString(IPVT_FontMap* pFontMap,
+                            int32_t nFontIndex,
+                            float fFontSize) {
+  fxcrt::ostringstream sRet;
+  if (pFontMap) {
+    ByteString sFontAlias = pFontMap->GetPDFFontAlias(nFontIndex);
+    if (sFontAlias.GetLength() > 0 && fFontSize > 0)
+      sRet << "/" << sFontAlias << " " << fFontSize << " Tf\n";
+  }
+  return ByteString(sRet);
+}
+
+ByteString GenerateEditAP(IPVT_FontMap* pFontMap,
+                          CPVT_VariableText::Iterator* pIterator,
+                          const CFX_PointF& ptOffset,
+                          bool bContinuous,
+                          uint16_t SubWord) {
+  fxcrt::ostringstream sEditStream;
+  fxcrt::ostringstream sLineStream;
+  CFX_PointF ptOld;
+  CFX_PointF ptNew;
+  int32_t nCurFontIndex = -1;
+  CPVT_WordPlace oldplace;
+  ByteString sWords;
+  pIterator->SetAt(0);
+  while (pIterator->NextWord()) {
+    CPVT_WordPlace place = pIterator->GetWordPlace();
+    if (bContinuous) {
+      if (place.LineCmp(oldplace) != 0) {
+        if (!sWords.IsEmpty()) {
+          sLineStream << GetWordRenderString(sWords.AsStringView());
+          sEditStream << sLineStream.str();
+          sLineStream.str("");
+          sWords.clear();
+        }
+        CPVT_Word word;
+        if (pIterator->GetWord(word)) {
+          ptNew = CFX_PointF(word.ptWord.x + ptOffset.x,
+                             word.ptWord.y + ptOffset.y);
+        } else {
+          CPVT_Line line;
+          pIterator->GetLine(line);
+          ptNew = CFX_PointF(line.ptLine.x + ptOffset.x,
+                             line.ptLine.y + ptOffset.y);
+        }
+        if (ptNew != ptOld) {
+          sLineStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y
+                      << " Td\n";
+          ptOld = ptNew;
+        }
+      }
+      CPVT_Word word;
+      if (pIterator->GetWord(word)) {
+        if (word.nFontIndex != nCurFontIndex) {
+          if (!sWords.IsEmpty()) {
+            sLineStream << GetWordRenderString(sWords.AsStringView());
+            sWords.clear();
+          }
+          sLineStream << GetFontSetString(pFontMap, word.nFontIndex,
+                                          word.fFontSize);
+          nCurFontIndex = word.nFontIndex;
+        }
+        sWords += GetPDFWordString(pFontMap, nCurFontIndex, word.Word, SubWord);
+      }
+      oldplace = place;
+    } else {
+      CPVT_Word word;
+      if (pIterator->GetWord(word)) {
+        ptNew =
+            CFX_PointF(word.ptWord.x + ptOffset.x, word.ptWord.y + ptOffset.y);
+        if (ptNew != ptOld) {
+          sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y
+                      << " Td\n";
+          ptOld = ptNew;
+        }
+        if (word.nFontIndex != nCurFontIndex) {
+          sEditStream << GetFontSetString(pFontMap, word.nFontIndex,
+                                          word.fFontSize);
+          nCurFontIndex = word.nFontIndex;
+        }
+        sEditStream << GetWordRenderString(
+            GetPDFWordString(pFontMap, nCurFontIndex, word.Word, SubWord)
+                .AsStringView());
+      }
+    }
+  }
+  if (!sWords.IsEmpty()) {
+    sLineStream << GetWordRenderString(sWords.AsStringView());
+    sEditStream << sLineStream.str();
+  }
+  return ByteString(sEditStream);
+}
+
+ByteString GenerateColorAP(const CFX_Color& color, PaintOperation nOperation) {
+  fxcrt::ostringstream sColorStream;
+  switch (color.nColorType) {
+    case CFX_Color::Type::kRGB:
+      sColorStream << color.fColor1 << " " << color.fColor2 << " "
+                   << color.fColor3 << " "
+                   << (nOperation == PaintOperation::kStroke ? "RG" : "rg")
+                   << "\n";
+      break;
+    case CFX_Color::Type::kGray:
+      sColorStream << color.fColor1 << " "
+                   << (nOperation == PaintOperation::kStroke ? "G" : "g")
+                   << "\n";
+      break;
+    case CFX_Color::Type::kCMYK:
+      sColorStream << color.fColor1 << " " << color.fColor2 << " "
+                   << color.fColor3 << " " << color.fColor4 << " "
+                   << (nOperation == PaintOperation::kStroke ? "K" : "k")
+                   << "\n";
+      break;
+    case CFX_Color::Type::kTransparent:
+      break;
+  }
+  return ByteString(sColorStream);
+}
+
+ByteString GenerateBorderAP(const CFX_FloatRect& rect,
+                            float fWidth,
+                            const CFX_Color& color,
+                            const CFX_Color& crLeftTop,
+                            const CFX_Color& crRightBottom,
+                            BorderStyle nStyle,
+                            const CPVT_Dash& dash) {
+  fxcrt::ostringstream sAppStream;
+  ByteString sColor;
+  float fLeft = rect.left;
+  float fRight = rect.right;
+  float fTop = rect.top;
+  float fBottom = rect.bottom;
+  if (fWidth > 0.0f) {
+    float fHalfWidth = fWidth / 2.0f;
+    switch (nStyle) {
+      case BorderStyle::kSolid:
+        sColor = GenerateColorAP(color, PaintOperation::kFill);
+        if (sColor.GetLength() > 0) {
+          sAppStream << sColor;
+          sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " "
+                     << fTop - fBottom << " re\n";
+          sAppStream << fLeft + fWidth << " " << fBottom + fWidth << " "
+                     << fRight - fLeft - fWidth * 2 << " "
+                     << fTop - fBottom - fWidth * 2 << " re\n";
+          sAppStream << "f*\n";
+        }
+        break;
+      case BorderStyle::kDash:
+        sColor = GenerateColorAP(color, PaintOperation::kStroke);
+        if (sColor.GetLength() > 0) {
+          sAppStream << sColor;
+          sAppStream << fWidth << " w"
+                     << " [" << dash.nDash << " " << dash.nGap << "] "
+                     << dash.nPhase << " d\n";
+          sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2
+                     << " m\n";
+          sAppStream << fLeft + fWidth / 2 << " " << fTop - fWidth / 2
+                     << " l\n";
+          sAppStream << fRight - fWidth / 2 << " " << fTop - fWidth / 2
+                     << " l\n";
+          sAppStream << fRight - fWidth / 2 << " " << fBottom + fWidth / 2
+                     << " l\n";
+          sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2
+                     << " l S\n";
+        }
+        break;
+      case BorderStyle::kBeveled:
+      case BorderStyle::kInset:
+        sColor = GenerateColorAP(crLeftTop, PaintOperation::kFill);
+        if (sColor.GetLength() > 0) {
+          sAppStream << sColor;
+          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth
+                     << " m\n";
+          sAppStream << fLeft + fHalfWidth << " " << fTop - fHalfWidth
+                     << " l\n";
+          sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth
+                     << " l\n";
+          sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
+                     << " l\n";
+          sAppStream << fLeft + fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
+                     << " l\n";
+          sAppStream << fLeft + fHalfWidth * 2 << " "
+                     << fBottom + fHalfWidth * 2 << " l f\n";
+        }
+        sColor = GenerateColorAP(crRightBottom, PaintOperation::kFill);
+        if (sColor.GetLength() > 0) {
+          sAppStream << sColor;
+          sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth
+                     << " m\n";
+          sAppStream << fRight - fHalfWidth << " " << fBottom + fHalfWidth
+                     << " l\n";
+          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth
+                     << " l\n";
+          sAppStream << fLeft + fHalfWidth * 2 << " "
+                     << fBottom + fHalfWidth * 2 << " l\n";
+          sAppStream << fRight - fHalfWidth * 2 << " "
+                     << fBottom + fHalfWidth * 2 << " l\n";
+          sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
+                     << " l f\n";
+        }
+        sColor = GenerateColorAP(color, PaintOperation::kFill);
+        if (sColor.GetLength() > 0) {
+          sAppStream << sColor;
+          sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " "
+                     << fTop - fBottom << " re\n";
+          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " "
+                     << fRight - fLeft - fHalfWidth * 2 << " "
+                     << fTop - fBottom - fHalfWidth * 2 << " re f*\n";
+        }
+        break;
+      case BorderStyle::kUnderline:
+        sColor = GenerateColorAP(color, PaintOperation::kStroke);
+        if (sColor.GetLength() > 0) {
+          sAppStream << sColor;
+          sAppStream << fWidth << " w\n";
+          sAppStream << fLeft << " " << fBottom + fWidth / 2 << " m\n";
+          sAppStream << fRight << " " << fBottom + fWidth / 2 << " l S\n";
+        }
+        break;
+    }
+  }
+  return ByteString(sAppStream);
+}
+
+ByteString GetColorStringWithDefault(const CPDF_Array* pColor,
+                                     const CFX_Color& crDefaultColor,
+                                     PaintOperation nOperation) {
+  if (pColor) {
+    CFX_Color color = fpdfdoc::CFXColorFromArray(*pColor);
+    return GenerateColorAP(color, nOperation);
+  }
+
+  return GenerateColorAP(crDefaultColor, nOperation);
+}
+
+float GetBorderWidth(const CPDF_Dictionary* pDict) {
+  RetainPtr<const CPDF_Dictionary> pBorderStyleDict = pDict->GetDictFor("BS");
+  if (pBorderStyleDict && pBorderStyleDict->KeyExist("W"))
+    return pBorderStyleDict->GetFloatFor("W");
+
+  auto pBorderArray = pDict->GetArrayFor(pdfium::annotation::kBorder);
+  if (pBorderArray && pBorderArray->size() > 2)
+    return pBorderArray->GetFloatAt(2);
+
+  return 1;
+}
+
+RetainPtr<const CPDF_Array> GetDashArray(const CPDF_Dictionary* pDict) {
+  RetainPtr<const CPDF_Dictionary> pBorderStyleDict = pDict->GetDictFor("BS");
+  if (pBorderStyleDict && pBorderStyleDict->GetByteStringFor("S") == "D")
+    return pBorderStyleDict->GetArrayFor("D");
+
+  RetainPtr<const CPDF_Array> pBorderArray =
+      pDict->GetArrayFor(pdfium::annotation::kBorder);
+  if (pBorderArray && pBorderArray->size() == 4)
+    return pBorderArray->GetArrayAt(3);
+
+  return nullptr;
+}
+
+ByteString GetDashPatternString(const CPDF_Dictionary* pDict) {
+  RetainPtr<const CPDF_Array> pDashArray = GetDashArray(pDict);
+  if (!pDashArray || pDashArray->IsEmpty())
+    return ByteString();
+
+  // Support maximum of ten elements in the dash array.
+  size_t pDashArrayCount = std::min<size_t>(pDashArray->size(), 10);
+  fxcrt::ostringstream sDashStream;
+
+  sDashStream << "[";
+  for (size_t i = 0; i < pDashArrayCount; ++i)
+    sDashStream << pDashArray->GetFloatAt(i) << " ";
+  sDashStream << "] 0 d\n";
+
+  return ByteString(sDashStream);
+}
+
+ByteString GetPopupContentsString(CPDF_Document* pDoc,
+                                  const CPDF_Dictionary& pAnnotDict,
+                                  RetainPtr<CPDF_Font> pDefFont,
+                                  const ByteString& sFontName) {
+  WideString swValue(pAnnotDict.GetUnicodeTextFor(pdfium::form_fields::kT));
+  swValue += L'\n';
+  swValue += pAnnotDict.GetUnicodeTextFor(pdfium::annotation::kContents);
+
+  CPVT_FontMap map(pDoc, nullptr, std::move(pDefFont), sFontName);
+  CPVT_VariableText::Provider prd(&map);
+  CPVT_VariableText vt(&prd);
+  vt.SetPlateRect(pAnnotDict.GetRectFor(pdfium::annotation::kRect));
+  vt.SetFontSize(12);
+  vt.SetAutoReturn(true);
+  vt.SetMultiLine(true);
+  vt.Initialize();
+  vt.SetText(swValue);
+  vt.RearrangeAll();
+
+  CFX_PointF ptOffset(3.0f, -3.0f);
+  ByteString sContent =
+      GenerateEditAP(&map, vt.GetIterator(), ptOffset, false, 0);
+
+  if (sContent.IsEmpty())
+    return ByteString();
+
+  ByteString sColorAP = GenerateColorAP(
+      CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kFill);
+
+  return ByteString{"BT\n", sColorAP.AsStringView(), sContent.AsStringView(),
+                    "ET\n", "Q\n"};
+}
+
+RetainPtr<CPDF_Dictionary> GenerateResourceFontDict(
+    CPDF_Document* pDoc,
+    const ByteString& sFontDictName) {
+  auto pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
+  pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
+  pFontDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
+  pFontDict->SetNewFor<CPDF_Name>("BaseFont", CFX_Font::kDefaultAnsiFontName);
+  pFontDict->SetNewFor<CPDF_Name>("Encoding",
+                                  pdfium::font_encodings::kWinAnsiEncoding);
+
+  auto pResourceFontDict = pDoc->New<CPDF_Dictionary>();
+  pResourceFontDict->SetNewFor<CPDF_Reference>(sFontDictName, pDoc,
+                                               pFontDict->GetObjNum());
+  return pResourceFontDict;
+}
+
+ByteString GetPaintOperatorString(bool bIsStrokeRect, bool bIsFillRect) {
+  if (bIsStrokeRect)
+    return bIsFillRect ? "b" : "s";
+  return bIsFillRect ? "f" : "n";
+}
+
+ByteString GenerateTextSymbolAP(const CFX_FloatRect& rect) {
+  fxcrt::ostringstream sAppStream;
+  sAppStream << GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 1, 1, 0),
+                                PaintOperation::kFill);
+  sAppStream << GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0),
+                                PaintOperation::kStroke);
+
+  const float fBorderWidth = 1;
+  sAppStream << fBorderWidth << " w\n";
+
+  const float fHalfWidth = fBorderWidth / 2;
+  const float fTipDelta = 4;
+
+  CFX_FloatRect outerRect1 = rect;
+  outerRect1.Deflate(fHalfWidth, fHalfWidth);
+  outerRect1.bottom += fTipDelta;
+
+  CFX_FloatRect outerRect2 = outerRect1;
+  outerRect2.left += fTipDelta;
+  outerRect2.right = outerRect2.left + fTipDelta;
+  outerRect2.top = outerRect2.bottom - fTipDelta;
+  float outerRect2Middle = (outerRect2.left + outerRect2.right) / 2;
+
+  // Draw outer boxes.
+  sAppStream << outerRect1.left << " " << outerRect1.bottom << " m\n"
+             << outerRect1.left << " " << outerRect1.top << " l\n"
+             << outerRect1.right << " " << outerRect1.top << " l\n"
+             << outerRect1.right << " " << outerRect1.bottom << " l\n"
+             << outerRect2.right << " " << outerRect2.bottom << " l\n"
+             << outerRect2Middle << " " << outerRect2.top << " l\n"
+             << outerRect2.left << " " << outerRect2.bottom << " l\n"
+             << outerRect1.left << " " << outerRect1.bottom << " l\n";
+
+  // Draw inner lines.
+  CFX_FloatRect lineRect = outerRect1;
+  const float fXDelta = 2;
+  const float fYDelta = (lineRect.top - lineRect.bottom) / 4;
+
+  lineRect.left += fXDelta;
+  lineRect.right -= fXDelta;
+  for (int i = 0; i < 3; ++i) {
+    lineRect.top -= fYDelta;
+    sAppStream << lineRect.left << " " << lineRect.top << " m\n"
+               << lineRect.right << " " << lineRect.top << " l\n";
+  }
+  sAppStream << "B*\n";
+
+  return ByteString(sAppStream);
+}
+
+RetainPtr<CPDF_Dictionary> GenerateExtGStateDict(
+    const CPDF_Dictionary& pAnnotDict,
+    const ByteString& sExtGSDictName,
+    const ByteString& sBlendMode) {
+  auto pGSDict =
+      pdfium::MakeRetain<CPDF_Dictionary>(pAnnotDict.GetByteStringPool());
+  pGSDict->SetNewFor<CPDF_Name>("Type", "ExtGState");
+
+  float fOpacity = pAnnotDict.KeyExist("CA") ? pAnnotDict.GetFloatFor("CA") : 1;
+  pGSDict->SetNewFor<CPDF_Number>("CA", fOpacity);
+  pGSDict->SetNewFor<CPDF_Number>("ca", fOpacity);
+  pGSDict->SetNewFor<CPDF_Boolean>("AIS", false);
+  pGSDict->SetNewFor<CPDF_Name>("BM", sBlendMode);
+
+  auto pExtGStateDict =
+      pdfium::MakeRetain<CPDF_Dictionary>(pAnnotDict.GetByteStringPool());
+  pExtGStateDict->SetFor(sExtGSDictName, pGSDict);
+  return pExtGStateDict;
+}
+
+RetainPtr<CPDF_Dictionary> GenerateResourceDict(
+    CPDF_Document* pDoc,
+    RetainPtr<CPDF_Dictionary> pExtGStateDict,
+    RetainPtr<CPDF_Dictionary> pResourceFontDict) {
+  auto pResourceDict = pDoc->New<CPDF_Dictionary>();
+  if (pExtGStateDict)
+    pResourceDict->SetFor("ExtGState", pExtGStateDict);
+  if (pResourceFontDict)
+    pResourceDict->SetFor("Font", pResourceFontDict);
+  return pResourceDict;
+}
+
+void GenerateAndSetAPDict(CPDF_Document* pDoc,
+                          CPDF_Dictionary* pAnnotDict,
+                          fxcrt::ostringstream* psAppStream,
+                          RetainPtr<CPDF_Dictionary> pResourceDict,
+                          bool bIsTextMarkupAnnotation) {
+  auto pNormalStream = pDoc->NewIndirect<CPDF_Stream>();
+  pNormalStream->SetDataFromStringstream(psAppStream);
+
+  RetainPtr<CPDF_Dictionary> pAPDict =
+      pAnnotDict->GetOrCreateDictFor(pdfium::annotation::kAP);
+  pAPDict->SetNewFor<CPDF_Reference>("N", pDoc, pNormalStream->GetObjNum());
+
+  RetainPtr<CPDF_Dictionary> pStreamDict = pNormalStream->GetMutableDict();
+  pStreamDict->SetNewFor<CPDF_Number>("FormType", 1);
+  pStreamDict->SetNewFor<CPDF_Name>("Type", "XObject");
+  pStreamDict->SetNewFor<CPDF_Name>("Subtype", "Form");
+  pStreamDict->SetMatrixFor("Matrix", CFX_Matrix());
+
+  CFX_FloatRect rect = bIsTextMarkupAnnotation
+                           ? CPDF_Annot::BoundingRectFromQuadPoints(pAnnotDict)
+                           : pAnnotDict->GetRectFor(pdfium::annotation::kRect);
+  pStreamDict->SetRectFor("BBox", rect);
+  pStreamDict->SetFor("Resources", pResourceDict);
+}
+
+bool GenerateCircleAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
+  fxcrt::ostringstream sAppStream;
+  ByteString sExtGSDictName = "GS";
+  sAppStream << "/" << sExtGSDictName << " gs ";
+
+  RetainPtr<const CPDF_Array> pInteriorColor = pAnnotDict->GetArrayFor("IC");
+  sAppStream << GetColorStringWithDefault(
+      pInteriorColor.Get(), CFX_Color(CFX_Color::Type::kTransparent),
+      PaintOperation::kFill);
+
+  sAppStream << GetColorStringWithDefault(
+      pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(),
+      CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
+
+  float fBorderWidth = GetBorderWidth(pAnnotDict);
+  bool bIsStrokeRect = fBorderWidth > 0;
+
+  if (bIsStrokeRect) {
+    sAppStream << fBorderWidth << " w ";
+    sAppStream << GetDashPatternString(pAnnotDict);
+  }
+
+  CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
+  rect.Normalize();
+
+  if (bIsStrokeRect) {
+    // Deflating rect because stroking a path entails painting all points
+    // whose perpendicular distance from the path in user space is less than
+    // or equal to half the line width.
+    rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
+  }
+
+  const float fMiddleX = (rect.left + rect.right) / 2;
+  const float fMiddleY = (rect.top + rect.bottom) / 2;
+
+  // |fL| is precalculated approximate value of 4 * tan((3.14 / 2) / 4) / 3,
+  // where |fL| * radius is a good approximation of control points for
+  // arc with 90 degrees.
+  const float fL = 0.5523f;
+  const float fDeltaX = fL * rect.Width() / 2.0;
+  const float fDeltaY = fL * rect.Height() / 2.0;
+
+  // Starting point
+  sAppStream << fMiddleX << " " << rect.top << " m\n";
+  // First Bezier Curve
+  sAppStream << fMiddleX + fDeltaX << " " << rect.top << " " << rect.right
+             << " " << fMiddleY + fDeltaY << " " << rect.right << " "
+             << fMiddleY << " c\n";
+  // Second Bezier Curve
+  sAppStream << rect.right << " " << fMiddleY - fDeltaY << " "
+             << fMiddleX + fDeltaX << " " << rect.bottom << " " << fMiddleX
+             << " " << rect.bottom << " c\n";
+  // Third Bezier Curve
+  sAppStream << fMiddleX - fDeltaX << " " << rect.bottom << " " << rect.left
+             << " " << fMiddleY - fDeltaY << " " << rect.left << " " << fMiddleY
+             << " c\n";
+  // Fourth Bezier Curve
+  sAppStream << rect.left << " " << fMiddleY + fDeltaY << " "
+             << fMiddleX - fDeltaX << " " << rect.top << " " << fMiddleX << " "
+             << rect.top << " c\n";
+
+  bool bIsFillRect = pInteriorColor && !pInteriorColor->IsEmpty();
+  sAppStream << GetPaintOperatorString(bIsStrokeRect, bIsFillRect) << "\n";
+
+  auto pExtGStateDict =
+      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
+  auto pResourceDict =
+      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
+  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
+                       false /*IsTextMarkupAnnotation*/);
+  return true;
+}
+
+bool GenerateHighlightAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
+  fxcrt::ostringstream sAppStream;
+  ByteString sExtGSDictName = "GS";
+  sAppStream << "/" << sExtGSDictName << " gs ";
+
+  sAppStream << GetColorStringWithDefault(
+      pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(),
+      CFX_Color(CFX_Color::Type::kRGB, 1, 1, 0), PaintOperation::kFill);
+
+  RetainPtr<const CPDF_Array> pArray = pAnnotDict->GetArrayFor("QuadPoints");
+  if (pArray) {
+    size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray.Get());
+    for (size_t i = 0; i < nQuadPointCount; ++i) {
+      CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
+      rect.Normalize();
+
+      sAppStream << rect.left << " " << rect.top << " m " << rect.right << " "
+                 << rect.top << " l " << rect.right << " " << rect.bottom
+                 << " l " << rect.left << " " << rect.bottom << " l h f\n";
+    }
+  }
+
+  auto pExtGStateDict =
+      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Multiply");
+  auto pResourceDict =
+      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
+  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
+                       true /*IsTextMarkupAnnotation*/);
+
+  return true;
+}
+
+bool GenerateInkAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
+  RetainPtr<const CPDF_Array> pInkList = pAnnotDict->GetArrayFor("InkList");
+  if (!pInkList || pInkList->IsEmpty())
+    return false;
+
+  float fBorderWidth = GetBorderWidth(pAnnotDict);
+  const bool bIsStroke = fBorderWidth > 0;
+  if (!bIsStroke)
+    return false;
+
+  ByteString sExtGSDictName = "GS";
+  fxcrt::ostringstream sAppStream;
+  sAppStream << "/" << sExtGSDictName << " gs ";
+  sAppStream << GetColorStringWithDefault(
+      pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(),
+      CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
+
+  sAppStream << fBorderWidth << " w ";
+  sAppStream << GetDashPatternString(pAnnotDict);
+
+  // Set inflated rect as a new rect because paths near the border with large
+  // width should not be clipped to the original rect.
+  CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
+  rect.Inflate(fBorderWidth / 2, fBorderWidth / 2);
+  pAnnotDict->SetRectFor(pdfium::annotation::kRect, rect);
+
+  for (size_t i = 0; i < pInkList->size(); i++) {
+    RetainPtr<const CPDF_Array> pInkCoordList = pInkList->GetArrayAt(i);
+    if (!pInkCoordList || pInkCoordList->size() < 2)
+      continue;
+
+    sAppStream << pInkCoordList->GetFloatAt(0) << " "
+               << pInkCoordList->GetFloatAt(1) << " m ";
+
+    for (size_t j = 0; j < pInkCoordList->size() - 1; j += 2) {
+      sAppStream << pInkCoordList->GetFloatAt(j) << " "
+                 << pInkCoordList->GetFloatAt(j + 1) << " l ";
+    }
+
+    sAppStream << "S\n";
+  }
+
+  auto pExtGStateDict =
+      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
+  auto pResourceDict =
+      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
+  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
+                       false /*IsTextMarkupAnnotation*/);
+  return true;
+}
+
+bool GenerateTextAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
+  fxcrt::ostringstream sAppStream;
+  ByteString sExtGSDictName = "GS";
+  sAppStream << "/" << sExtGSDictName << " gs ";
+
+  CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
+  const float fNoteLength = 20;
+  CFX_FloatRect noteRect(rect.left, rect.bottom, rect.left + fNoteLength,
+                         rect.bottom + fNoteLength);
+  pAnnotDict->SetRectFor(pdfium::annotation::kRect, noteRect);
+
+  sAppStream << GenerateTextSymbolAP(noteRect);
+
+  auto pExtGStateDict =
+      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
+  auto pResourceDict =
+      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
+  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
+                       false /*IsTextMarkupAnnotation*/);
+  return true;
+}
+
+bool GenerateUnderlineAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
+  fxcrt::ostringstream sAppStream;
+  ByteString sExtGSDictName = "GS";
+  sAppStream << "/" << sExtGSDictName << " gs ";
+
+  sAppStream << GetColorStringWithDefault(
+      pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(),
+      CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
+
+  RetainPtr<const CPDF_Array> pArray = pAnnotDict->GetArrayFor("QuadPoints");
+  if (pArray) {
+    static constexpr float kLineWidth = 1.0f;
+    sAppStream << kLineWidth << " w ";
+    size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray.Get());
+    for (size_t i = 0; i < nQuadPointCount; ++i) {
+      CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
+      rect.Normalize();
+      sAppStream << rect.left << " " << rect.bottom + kLineWidth << " m "
+                 << rect.right << " " << rect.bottom + kLineWidth << " l S\n";
+    }
+  }
+
+  auto pExtGStateDict =
+      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
+  auto pResourceDict =
+      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
+  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
+                       true /*IsTextMarkupAnnotation*/);
+  return true;
+}
+
+bool GeneratePopupAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
+  fxcrt::ostringstream sAppStream;
+  ByteString sExtGSDictName = "GS";
+  sAppStream << "/" << sExtGSDictName << " gs\n";
+
+  sAppStream << GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 1, 1, 0),
+                                PaintOperation::kFill);
+  sAppStream << GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0),
+                                PaintOperation::kStroke);
+
+  const float fBorderWidth = 1;
+  sAppStream << fBorderWidth << " w\n";
+
+  CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
+  rect.Normalize();
+  rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
+
+  sAppStream << rect.left << " " << rect.bottom << " " << rect.Width() << " "
+             << rect.Height() << " re b\n";
+
+  ByteString sFontName = "FONT";
+  RetainPtr<CPDF_Dictionary> pResourceFontDict =
+      GenerateResourceFontDict(pDoc, sFontName);
+
+  auto* pData = CPDF_DocPageData::FromDocument(pDoc);
+  RetainPtr<CPDF_Font> pDefFont = pData->GetFont(pResourceFontDict);
+  if (!pDefFont)
+    return false;
+
+  RetainPtr<CPDF_Dictionary> pExtGStateDict =
+      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
+  RetainPtr<CPDF_Dictionary> pResourceDict = GenerateResourceDict(
+      pDoc, std::move(pExtGStateDict), std::move(pResourceFontDict));
+
+  sAppStream << GetPopupContentsString(pDoc, *pAnnotDict, std::move(pDefFont),
+                                       sFontName);
+  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
+                       false /*IsTextMarkupAnnotation*/);
+  return true;
+}
+
+bool GenerateSquareAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
+  const ByteString sExtGSDictName = "GS";
+  fxcrt::ostringstream sAppStream;
+  sAppStream << "/" << sExtGSDictName << " gs ";
+
+  RetainPtr<const CPDF_Array> pInteriorColor = pAnnotDict->GetArrayFor("IC");
+  sAppStream << GetColorStringWithDefault(
+      pInteriorColor.Get(), CFX_Color(CFX_Color::Type::kTransparent),
+      PaintOperation::kFill);
+
+  sAppStream << GetColorStringWithDefault(
+      pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(),
+      CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
+
+  float fBorderWidth = GetBorderWidth(pAnnotDict);
+  const bool bIsStrokeRect = fBorderWidth > 0;
+  if (bIsStrokeRect) {
+    sAppStream << fBorderWidth << " w ";
+    sAppStream << GetDashPatternString(pAnnotDict);
+  }
+
+  CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
+  rect.Normalize();
+
+  if (bIsStrokeRect) {
+    // Deflating rect because stroking a path entails painting all points
+    // whose perpendicular distance from the path in user space is less than
+    // or equal to half the line width.
+    rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
+  }
+
+  const bool bIsFillRect = pInteriorColor && (pInteriorColor->size() > 0);
+  sAppStream << rect.left << " " << rect.bottom << " " << rect.Width() << " "
+             << rect.Height() << " re "
+             << GetPaintOperatorString(bIsStrokeRect, bIsFillRect) << "\n";
+
+  auto pExtGStateDict =
+      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
+  auto pResourceDict =
+      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
+  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
+                       false /*IsTextMarkupAnnotation*/);
+  return true;
+}
+
+bool GenerateSquigglyAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
+  fxcrt::ostringstream sAppStream;
+  ByteString sExtGSDictName = "GS";
+  sAppStream << "/" << sExtGSDictName << " gs ";
+
+  sAppStream << GetColorStringWithDefault(
+      pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(),
+      CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
+
+  RetainPtr<const CPDF_Array> pArray = pAnnotDict->GetArrayFor("QuadPoints");
+  if (pArray) {
+    static constexpr float kLineWidth = 1.0f;
+    static constexpr float kDelta = 2.0f;
+    sAppStream << kLineWidth << " w ";
+    size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray.Get());
+    for (size_t i = 0; i < nQuadPointCount; ++i) {
+      CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
+      rect.Normalize();
+
+      const float fTop = rect.bottom + kDelta;
+      const float fBottom = rect.bottom;
+      sAppStream << rect.left << " " << fTop << " m ";
+
+      float fX = rect.left + kDelta;
+      bool isUpwards = false;
+      while (fX < rect.right) {
+        sAppStream << fX << " " << (isUpwards ? fTop : fBottom) << " l ";
+        fX += kDelta;
+        isUpwards = !isUpwards;
+      }
+
+      float fRemainder = rect.right - (fX - kDelta);
+      if (isUpwards)
+        sAppStream << rect.right << " " << fBottom + fRemainder << " l ";
+      else
+        sAppStream << rect.right << " " << fTop - fRemainder << " l ";
+
+      sAppStream << "S\n";
+    }
+  }
+
+  auto pExtGStateDict =
+      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
+  auto pResourceDict =
+      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
+  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
+                       true /*IsTextMarkupAnnotation*/);
+  return true;
+}
+
+bool GenerateStrikeOutAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
+  fxcrt::ostringstream sAppStream;
+  ByteString sExtGSDictName = "GS";
+  sAppStream << "/" << sExtGSDictName << " gs ";
+
+  sAppStream << GetColorStringWithDefault(
+      pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(),
+      CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
+
+  RetainPtr<const CPDF_Array> pArray = pAnnotDict->GetArrayFor("QuadPoints");
+  if (pArray) {
+    static constexpr float kLineWidth = 1.0f;
+    size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray.Get());
+    for (size_t i = 0; i < nQuadPointCount; ++i) {
+      CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
+      rect.Normalize();
+
+      float fY = (rect.top + rect.bottom) / 2;
+      sAppStream << kLineWidth << " w " << rect.left << " " << fY << " m "
+                 << rect.right << " " << fY << " l S\n";
+    }
+  }
+
+  auto pExtGStateDict =
+      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
+  auto pResourceDict =
+      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
+  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
+                       true /*IsTextMarkupAnnotation*/);
+  return true;
+}
+
+}  // namespace
+
+// static
+void CPDF_GenerateAP::GenerateFormAP(CPDF_Document* pDoc,
+                                     CPDF_Dictionary* pAnnotDict,
+                                     FormType type) {
+  RetainPtr<CPDF_Dictionary> pRootDict = pDoc->GetMutableRoot();
+  if (!pRootDict)
+    return;
+
+  RetainPtr<CPDF_Dictionary> pFormDict =
+      pRootDict->GetMutableDictFor("AcroForm");
+  if (!pFormDict)
+    return;
+
+  ByteString DA;
+  RetainPtr<const CPDF_Object> pDAObj =
+      CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "DA");
+  if (pDAObj)
+    DA = pDAObj->GetString();
+  if (DA.IsEmpty())
+    DA = pFormDict->GetByteStringFor("DA");
+  if (DA.IsEmpty())
+    return;
+
+  CPDF_DefaultAppearance appearance(DA);
+
+  float fFontSize = 0;
+  absl::optional<ByteString> font = appearance.GetFont(&fFontSize);
+  if (!font.has_value())
+    return;
+
+  ByteString font_name = font.value();
+
+  CFX_Color crText = fpdfdoc::CFXColorFromString(DA);
+  RetainPtr<CPDF_Dictionary> pDRDict = pFormDict->GetMutableDictFor("DR");
+  if (!pDRDict)
+    return;
+
+  RetainPtr<CPDF_Dictionary> pDRFontDict = pDRDict->GetMutableDictFor("Font");
+  if (!ValidateFontResourceDict(pDRFontDict.Get()))
+    return;
+
+  RetainPtr<CPDF_Dictionary> pFontDict =
+      pDRFontDict->GetMutableDictFor(font_name);
+  if (!pFontDict) {
+    pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
+    pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
+    pFontDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
+    pFontDict->SetNewFor<CPDF_Name>("BaseFont", CFX_Font::kDefaultAnsiFontName);
+    pFontDict->SetNewFor<CPDF_Name>("Encoding",
+                                    pdfium::font_encodings::kWinAnsiEncoding);
+    pDRFontDict->SetNewFor<CPDF_Reference>(font_name, pDoc,
+                                           pFontDict->GetObjNum());
+  }
+  auto* pData = CPDF_DocPageData::FromDocument(pDoc);
+  RetainPtr<CPDF_Font> pDefFont = pData->GetFont(pFontDict);
+  if (!pDefFont)
+    return;
+
+  CFX_FloatRect rcAnnot = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
+  RetainPtr<const CPDF_Dictionary> pMKDict = pAnnotDict->GetDictFor("MK");
+  int32_t nRotate =
+      pMKDict ? pMKDict->GetIntegerFor(pdfium::appearance::kR) : 0;
+
+  CFX_FloatRect rcBBox;
+  CFX_Matrix matrix;
+  switch (nRotate % 360) {
+    case 0:
+      rcBBox = CFX_FloatRect(0, 0, rcAnnot.right - rcAnnot.left,
+                             rcAnnot.top - rcAnnot.bottom);
+      break;
+    case 90:
+      matrix = CFX_Matrix(0, 1, -1, 0, rcAnnot.right - rcAnnot.left, 0);
+      rcBBox = CFX_FloatRect(0, 0, rcAnnot.top - rcAnnot.bottom,
+                             rcAnnot.right - rcAnnot.left);
+      break;
+    case 180:
+      matrix = CFX_Matrix(-1, 0, 0, -1, rcAnnot.right - rcAnnot.left,
+                          rcAnnot.top - rcAnnot.bottom);
+      rcBBox = CFX_FloatRect(0, 0, rcAnnot.right - rcAnnot.left,
+                             rcAnnot.top - rcAnnot.bottom);
+      break;
+    case 270:
+      matrix = CFX_Matrix(0, -1, 1, 0, 0, rcAnnot.top - rcAnnot.bottom);
+      rcBBox = CFX_FloatRect(0, 0, rcAnnot.top - rcAnnot.bottom,
+                             rcAnnot.right - rcAnnot.left);
+      break;
+  }
+
+  BorderStyle nBorderStyle = BorderStyle::kSolid;
+  float fBorderWidth = 1;
+  CPVT_Dash dsBorder(3, 0, 0);
+  CFX_Color crLeftTop;
+  CFX_Color crRightBottom;
+  if (RetainPtr<const CPDF_Dictionary> pBSDict = pAnnotDict->GetDictFor("BS")) {
+    if (pBSDict->KeyExist("W"))
+      fBorderWidth = pBSDict->GetFloatFor("W");
+
+    if (RetainPtr<const CPDF_Array> pArray = pBSDict->GetArrayFor("D")) {
+      dsBorder = CPVT_Dash(pArray->GetIntegerAt(0), pArray->GetIntegerAt(1),
+                           pArray->GetIntegerAt(2));
+    }
+    if (pBSDict->GetByteStringFor("S").GetLength()) {
+      switch (pBSDict->GetByteStringFor("S")[0]) {
+        case 'S':
+          nBorderStyle = BorderStyle::kSolid;
+          break;
+        case 'D':
+          nBorderStyle = BorderStyle::kDash;
+          break;
+        case 'B':
+          nBorderStyle = BorderStyle::kBeveled;
+          fBorderWidth *= 2;
+          crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
+          crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.5);
+          break;
+        case 'I':
+          nBorderStyle = BorderStyle::kInset;
+          fBorderWidth *= 2;
+          crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5);
+          crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75);
+          break;
+        case 'U':
+          nBorderStyle = BorderStyle::kUnderline;
+          break;
+      }
+    }
+  }
+  CFX_Color crBorder;
+  CFX_Color crBG;
+  if (pMKDict) {
+    RetainPtr<const CPDF_Array> pArray =
+        pMKDict->GetArrayFor(pdfium::appearance::kBC);
+    if (pArray)
+      crBorder = fpdfdoc::CFXColorFromArray(*pArray);
+    pArray = pMKDict->GetArrayFor(pdfium::appearance::kBG);
+    if (pArray)
+      crBG = fpdfdoc::CFXColorFromArray(*pArray);
+  }
+  fxcrt::ostringstream sAppStream;
+  ByteString sBG = GenerateColorAP(crBG, PaintOperation::kFill);
+  if (sBG.GetLength() > 0) {
+    sAppStream << "q\n"
+               << sBG << rcBBox.left << " " << rcBBox.bottom << " "
+               << rcBBox.Width() << " " << rcBBox.Height() << " re f\n"
+               << "Q\n";
+  }
+  ByteString sBorderStream =
+      GenerateBorderAP(rcBBox, fBorderWidth, crBorder, crLeftTop, crRightBottom,
+                       nBorderStyle, dsBorder);
+  if (sBorderStream.GetLength() > 0)
+    sAppStream << "q\n" << sBorderStream << "Q\n";
+
+  CFX_FloatRect rcBody =
+      CFX_FloatRect(rcBBox.left + fBorderWidth, rcBBox.bottom + fBorderWidth,
+                    rcBBox.right - fBorderWidth, rcBBox.top - fBorderWidth);
+  rcBody.Normalize();
+
+  RetainPtr<CPDF_Dictionary> pAPDict =
+      pAnnotDict->GetOrCreateDictFor(pdfium::annotation::kAP);
+  RetainPtr<CPDF_Stream> pNormalStream = pAPDict->GetMutableStreamFor("N");
+  if (!pNormalStream) {
+    pNormalStream = pDoc->NewIndirect<CPDF_Stream>();
+    pAPDict->SetNewFor<CPDF_Reference>("N", pDoc, pNormalStream->GetObjNum());
+  }
+  RetainPtr<CPDF_Dictionary> pStreamDict = pNormalStream->GetMutableDict();
+  if (pStreamDict) {
+    RetainPtr<CPDF_Dictionary> pStreamResList =
+        pStreamDict->GetMutableDictFor("Resources");
+    if (pStreamResList) {
+      RetainPtr<CPDF_Dictionary> pStreamResFontList =
+          pStreamResList->GetMutableDictFor("Font");
+      if (pStreamResFontList) {
+        if (!ValidateFontResourceDict(pStreamResFontList.Get()))
+          return;
+      } else {
+        pStreamResFontList = pStreamResList->SetNewFor<CPDF_Dictionary>("Font");
+      }
+      if (!pStreamResFontList->KeyExist(font_name)) {
+        pStreamResFontList->SetNewFor<CPDF_Reference>(font_name, pDoc,
+                                                      pFontDict->GetObjNum());
+      }
+    } else {
+      pStreamDict->SetFor("Resources", pFormDict->GetDictFor("DR")->Clone());
+    }
+    pStreamDict->SetMatrixFor("Matrix", matrix);
+    pStreamDict->SetRectFor("BBox", rcBBox);
+  }
+  CPVT_FontMap map(
+      pDoc, pStreamDict ? pStreamDict->GetMutableDictFor("Resources") : nullptr,
+      std::move(pDefFont), font_name);
+  CPVT_VariableText::Provider prd(&map);
+
+  switch (type) {
+    case CPDF_GenerateAP::kTextField: {
+      RetainPtr<const CPDF_Object> pV = CPDF_FormField::GetFieldAttrForDict(
+          pAnnotDict, pdfium::form_fields::kV);
+      WideString swValue = pV ? pV->GetUnicodeText() : WideString();
+      RetainPtr<const CPDF_Object> pQ =
+          CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "Q");
+      int32_t nAlign = pQ ? pQ->GetInteger() : 0;
+      RetainPtr<const CPDF_Object> pFf = CPDF_FormField::GetFieldAttrForDict(
+          pAnnotDict, pdfium::form_fields::kFf);
+      uint32_t dwFlags = pFf ? pFf->GetInteger() : 0;
+      RetainPtr<const CPDF_Object> pMaxLen =
+          CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "MaxLen");
+      uint32_t dwMaxLen = pMaxLen ? pMaxLen->GetInteger() : 0;
+      CPVT_VariableText vt(&prd);
+      vt.SetPlateRect(rcBody);
+      vt.SetAlignment(nAlign);
+      if (FXSYS_IsFloatZero(fFontSize))
+        vt.SetAutoFontSize(true);
+      else
+        vt.SetFontSize(fFontSize);
+
+      bool bMultiLine = (dwFlags >> 12) & 1;
+      if (bMultiLine) {
+        vt.SetMultiLine(true);
+        vt.SetAutoReturn(true);
+      }
+      uint16_t subWord = 0;
+      if ((dwFlags >> 13) & 1) {
+        subWord = '*';
+        vt.SetPasswordChar(subWord);
+      }
+      bool bCharArray = (dwFlags >> 24) & 1;
+      if (bCharArray)
+        vt.SetCharArray(dwMaxLen);
+      else
+        vt.SetLimitChar(dwMaxLen);
+
+      vt.Initialize();
+      vt.SetText(swValue);
+      vt.RearrangeAll();
+      CFX_FloatRect rcContent = vt.GetContentRect();
+      CFX_PointF ptOffset;
+      if (!bMultiLine) {
+        ptOffset =
+            CFX_PointF(0.0f, (rcContent.Height() - rcBody.Height()) / 2.0f);
+      }
+      ByteString sBody = GenerateEditAP(&map, vt.GetIterator(), ptOffset,
+                                        !bCharArray, subWord);
+      if (sBody.GetLength() > 0) {
+        sAppStream << "/Tx BMC\n"
+                   << "q\n";
+        if (rcContent.Width() > rcBody.Width() ||
+            rcContent.Height() > rcBody.Height()) {
+          sAppStream << rcBody.left << " " << rcBody.bottom << " "
+                     << rcBody.Width() << " " << rcBody.Height()
+                     << " re\nW\nn\n";
+        }
+        sAppStream << "BT\n"
+                   << GenerateColorAP(crText, PaintOperation::kFill) << sBody
+                   << "ET\n"
+                   << "Q\nEMC\n";
+      }
+      break;
+    }
+    case CPDF_GenerateAP::kComboBox: {
+      RetainPtr<const CPDF_Object> pV = CPDF_FormField::GetFieldAttrForDict(
+          pAnnotDict, pdfium::form_fields::kV);
+      WideString swValue = pV ? pV->GetUnicodeText() : WideString();
+      CPVT_VariableText vt(&prd);
+      CFX_FloatRect rcButton = rcBody;
+      rcButton.left = rcButton.right - 13;
+      rcButton.Normalize();
+      CFX_FloatRect rcEdit = rcBody;
+      rcEdit.right = rcButton.left;
+      rcEdit.Normalize();
+      vt.SetPlateRect(rcEdit);
+      if (FXSYS_IsFloatZero(fFontSize))
+        vt.SetAutoFontSize(true);
+      else
+        vt.SetFontSize(fFontSize);
+
+      vt.Initialize();
+      vt.SetText(swValue);
+      vt.RearrangeAll();
+      CFX_FloatRect rcContent = vt.GetContentRect();
+      CFX_PointF ptOffset =
+          CFX_PointF(0.0f, (rcContent.Height() - rcEdit.Height()) / 2.0f);
+      ByteString sEdit =
+          GenerateEditAP(&map, vt.GetIterator(), ptOffset, true, 0);
+      if (sEdit.GetLength() > 0) {
+        sAppStream << "/Tx BMC\n"
+                   << "q\n";
+        sAppStream << rcEdit.left << " " << rcEdit.bottom << " "
+                   << rcEdit.Width() << " " << rcEdit.Height() << " re\nW\nn\n";
+        sAppStream << "BT\n"
+                   << GenerateColorAP(crText, PaintOperation::kFill) << sEdit
+                   << "ET\n"
+                   << "Q\nEMC\n";
+      }
+      ByteString sButton =
+          GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 220.0f / 255.0f,
+                                    220.0f / 255.0f, 220.0f / 255.0f),
+                          PaintOperation::kFill);
+      if (sButton.GetLength() > 0 && !rcButton.IsEmpty()) {
+        sAppStream << "q\n" << sButton;
+        sAppStream << rcButton.left << " " << rcButton.bottom << " "
+                   << rcButton.Width() << " " << rcButton.Height() << " re f\n";
+        sAppStream << "Q\n";
+        ByteString sButtonBorder =
+            GenerateBorderAP(rcButton, 2, CFX_Color(CFX_Color::Type::kGray, 0),
+                             CFX_Color(CFX_Color::Type::kGray, 1),
+                             CFX_Color(CFX_Color::Type::kGray, 0.5),
+                             BorderStyle::kBeveled, CPVT_Dash(3, 0, 0));
+        if (sButtonBorder.GetLength() > 0)
+          sAppStream << "q\n" << sButtonBorder << "Q\n";
+
+        CFX_PointF ptCenter = CFX_PointF((rcButton.left + rcButton.right) / 2,
+                                         (rcButton.top + rcButton.bottom) / 2);
+        if (FXSYS_IsFloatBigger(rcButton.Width(), 6) &&
+            FXSYS_IsFloatBigger(rcButton.Height(), 6)) {
+          sAppStream << "q\n"
+                     << " 0 g\n";
+          sAppStream << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " m\n";
+          sAppStream << ptCenter.x + 3 << " " << ptCenter.y + 1.5f << " l\n";
+          sAppStream << ptCenter.x << " " << ptCenter.y - 1.5f << " l\n";
+          sAppStream << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " l f\n";
+          sAppStream << sButton << "Q\n";
+        }
+      }
+      break;
+    }
+    case CPDF_GenerateAP::kListBox: {
+      RetainPtr<const CPDF_Array> pOpts =
+          ToArray(CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "Opt"));
+      RetainPtr<const CPDF_Array> pSels =
+          ToArray(CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "I"));
+      RetainPtr<const CPDF_Object> pTi =
+          CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "TI");
+      int32_t nTop = pTi ? pTi->GetInteger() : 0;
+      fxcrt::ostringstream sBody;
+      if (pOpts) {
+        float fy = rcBody.top;
+        for (size_t i = nTop, sz = pOpts->size(); i < sz; i++) {
+          if (FXSYS_IsFloatSmaller(fy, rcBody.bottom))
+            break;
+
+          if (RetainPtr<const CPDF_Object> pOpt = pOpts->GetDirectObjectAt(i)) {
+            WideString swItem;
+            if (pOpt->IsString()) {
+              swItem = pOpt->GetUnicodeText();
+            } else if (const CPDF_Array* pArray = pOpt->AsArray()) {
+              RetainPtr<const CPDF_Object> pDirectObj =
+                  pArray->GetDirectObjectAt(1);
+              if (pDirectObj)
+                swItem = pDirectObj->GetUnicodeText();
+            }
+            bool bSelected = false;
+            if (pSels) {
+              for (size_t s = 0, ssz = pSels->size(); s < ssz; s++) {
+                int value = pSels->GetIntegerAt(s);
+                if (value >= 0 && i == static_cast<size_t>(value)) {
+                  bSelected = true;
+                  break;
+                }
+              }
+            }
+            CPVT_VariableText vt(&prd);
+            vt.SetPlateRect(
+                CFX_FloatRect(rcBody.left, 0.0f, rcBody.right, 0.0f));
+            vt.SetFontSize(FXSYS_IsFloatZero(fFontSize) ? 12.0f : fFontSize);
+            vt.Initialize();
+            vt.SetText(swItem);
+            vt.RearrangeAll();
+
+            float fItemHeight = vt.GetContentRect().Height();
+            if (bSelected) {
+              CFX_FloatRect rcItem = CFX_FloatRect(
+                  rcBody.left, fy - fItemHeight, rcBody.right, fy);
+              sBody << "q\n"
+                    << GenerateColorAP(
+                           CFX_Color(CFX_Color::Type::kRGB, 0, 51.0f / 255.0f,
+                                     113.0f / 255.0f),
+                           PaintOperation::kFill)
+                    << rcItem.left << " " << rcItem.bottom << " "
+                    << rcItem.Width() << " " << rcItem.Height() << " re f\n"
+                    << "Q\n";
+              sBody << "BT\n"
+                    << GenerateColorAP(CFX_Color(CFX_Color::Type::kGray, 1),
+                                       PaintOperation::kFill)
+                    << GenerateEditAP(&map, vt.GetIterator(),
+                                      CFX_PointF(0.0f, fy), true, 0)
+                    << "ET\n";
+            } else {
+              sBody << "BT\n"
+                    << GenerateColorAP(crText, PaintOperation::kFill)
+                    << GenerateEditAP(&map, vt.GetIterator(),
+                                      CFX_PointF(0.0f, fy), true, 0)
+                    << "ET\n";
+            }
+            fy -= fItemHeight;
+          }
+        }
+      }
+      if (sBody.tellp() > 0) {
+        sAppStream << "/Tx BMC\nq\n"
+                   << rcBody.left << " " << rcBody.bottom << " "
+                   << rcBody.Width() << " " << rcBody.Height() << " re\nW\nn\n"
+                   << sBody.str() << "Q\nEMC\n";
+      }
+      break;
+    }
+  }
+
+  if (!pNormalStream)
+    return;
+
+  pNormalStream->SetDataFromStringstreamAndRemoveFilter(&sAppStream);
+  pStreamDict = pNormalStream->GetMutableDict();
+  if (!pStreamDict)
+    return;
+
+  pStreamDict->SetMatrixFor("Matrix", matrix);
+  pStreamDict->SetRectFor("BBox", rcBBox);
+  RetainPtr<CPDF_Dictionary> pStreamResList =
+      pStreamDict->GetMutableDictFor("Resources");
+  if (!pStreamResList) {
+    pStreamDict->SetFor("Resources", pFormDict->GetDictFor("DR")->Clone());
+    return;
+  }
+
+  RetainPtr<CPDF_Dictionary> pStreamResFontList =
+      pStreamResList->GetMutableDictFor("Font");
+  if (pStreamResFontList) {
+    if (!ValidateFontResourceDict(pStreamResFontList.Get()))
+      return;
+  } else {
+    pStreamResFontList = pStreamResList->SetNewFor<CPDF_Dictionary>("Font");
+  }
+
+  if (!pStreamResFontList->KeyExist(font_name)) {
+    pStreamResFontList->SetNewFor<CPDF_Reference>(font_name, pDoc,
+                                                  pFontDict->GetObjNum());
+  }
+}
+
+// static
+void CPDF_GenerateAP::GenerateEmptyAP(CPDF_Document* pDoc,
+                                      CPDF_Dictionary* pAnnotDict) {
+  auto pExtGStateDict = GenerateExtGStateDict(*pAnnotDict, "GS", "Normal");
+  auto pResourceDict =
+      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
+
+  fxcrt::ostringstream sStream;
+  GenerateAndSetAPDict(pDoc, pAnnotDict, &sStream, std::move(pResourceDict),
+                       false);
+}
+
+// static
+bool CPDF_GenerateAP::GenerateAnnotAP(CPDF_Document* pDoc,
+                                      CPDF_Dictionary* pAnnotDict,
+                                      CPDF_Annot::Subtype subtype) {
+  switch (subtype) {
+    case CPDF_Annot::Subtype::CIRCLE:
+      return GenerateCircleAP(pDoc, pAnnotDict);
+    case CPDF_Annot::Subtype::HIGHLIGHT:
+      return GenerateHighlightAP(pDoc, pAnnotDict);
+    case CPDF_Annot::Subtype::INK:
+      return GenerateInkAP(pDoc, pAnnotDict);
+    case CPDF_Annot::Subtype::POPUP:
+      return GeneratePopupAP(pDoc, pAnnotDict);
+    case CPDF_Annot::Subtype::SQUARE:
+      return GenerateSquareAP(pDoc, pAnnotDict);
+    case CPDF_Annot::Subtype::SQUIGGLY:
+      return GenerateSquigglyAP(pDoc, pAnnotDict);
+    case CPDF_Annot::Subtype::STRIKEOUT:
+      return GenerateStrikeOutAP(pDoc, pAnnotDict);
+    case CPDF_Annot::Subtype::TEXT:
+      return GenerateTextAP(pDoc, pAnnotDict);
+    case CPDF_Annot::Subtype::UNDERLINE:
+      return GenerateUnderlineAP(pDoc, pAnnotDict);
+    default:
+      return false;
+  }
+}
diff --git a/core/fpdfdoc/cpdf_generateap.h b/core/fpdfdoc/cpdf_generateap.h
new file mode 100644
index 0000000..520ce4f
--- /dev/null
+++ b/core/fpdfdoc/cpdf_generateap.h
@@ -0,0 +1,34 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFDOC_CPDF_GENERATEAP_H_
+#define CORE_FPDFDOC_CPDF_GENERATEAP_H_
+
+#include "core/fpdfdoc/cpdf_annot.h"
+
+class CPDF_Dictionary;
+class CPDF_Document;
+
+class CPDF_GenerateAP {
+ public:
+  enum FormType { kTextField, kComboBox, kListBox };
+
+  static void GenerateFormAP(CPDF_Document* pDoc,
+                             CPDF_Dictionary* pAnnotDict,
+                             FormType type);
+
+  static void GenerateEmptyAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict);
+
+  static bool GenerateAnnotAP(CPDF_Document* pDoc,
+                              CPDF_Dictionary* pAnnotDict,
+                              CPDF_Annot::Subtype subtype);
+
+  CPDF_GenerateAP() = delete;
+  CPDF_GenerateAP(const CPDF_GenerateAP&) = delete;
+  CPDF_GenerateAP& operator=(const CPDF_GenerateAP&) = delete;
+};
+
+#endif  // CORE_FPDFDOC_CPDF_GENERATEAP_H_
diff --git a/core/fpdfdoc/cpdf_icon.cpp b/core/fpdfdoc/cpdf_icon.cpp
index a450e1e..8c4909a 100644
--- a/core/fpdfdoc/cpdf_icon.cpp
+++ b/core/fpdfdoc/cpdf_icon.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,15 +6,18 @@
 
 #include "core/fpdfdoc/cpdf_icon.h"
 
+#include <utility>
+
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 
-CPDF_Icon::CPDF_Icon(CPDF_Stream* pStream) : m_pStream(pStream) {}
+CPDF_Icon::CPDF_Icon(RetainPtr<const CPDF_Stream> pStream)
+    : m_pStream(std::move(pStream)) {}
 
 CPDF_Icon::~CPDF_Icon() = default;
 
 CFX_SizeF CPDF_Icon::GetImageSize() const {
-  CPDF_Dictionary* pDict = m_pStream->GetDict();
+  RetainPtr<const CPDF_Dictionary> pDict = m_pStream->GetDict();
   if (!pDict)
     return CFX_SizeF();
 
@@ -23,7 +26,7 @@
 }
 
 CFX_Matrix CPDF_Icon::GetImageMatrix() const {
-  CPDF_Dictionary* pDict = m_pStream->GetDict();
+  RetainPtr<const CPDF_Dictionary> pDict = m_pStream->GetDict();
   if (!pDict)
     return CFX_Matrix();
 
@@ -31,9 +34,9 @@
 }
 
 ByteString CPDF_Icon::GetImageAlias() const {
-  CPDF_Dictionary* pDict = m_pStream->GetDict();
+  RetainPtr<const CPDF_Dictionary> pDict = m_pStream->GetDict();
   if (!pDict)
     return ByteString();
 
-  return pDict->GetStringFor("Name");
+  return pDict->GetByteStringFor("Name");
 }
diff --git a/core/fpdfdoc/cpdf_icon.h b/core/fpdfdoc/cpdf_icon.h
index 519fcda..c1d2d27 100644
--- a/core/fpdfdoc/cpdf_icon.h
+++ b/core/fpdfdoc/cpdf_icon.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #ifndef CORE_FPDFDOC_CPDF_ICON_H_
 #define CORE_FPDFDOC_CPDF_ICON_H_
 
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Stream;
 
 class CPDF_Icon final {
  public:
-  CPDF_Icon(CPDF_Stream* pStream);
+  explicit CPDF_Icon(RetainPtr<const CPDF_Stream> pStream);
   ~CPDF_Icon();
 
   CFX_SizeF GetImageSize() const;
@@ -23,7 +23,7 @@
   ByteString GetImageAlias() const;
 
  private:
-  RetainPtr<CPDF_Stream> const m_pStream;
+  RetainPtr<const CPDF_Stream> const m_pStream;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_ICON_H_
diff --git a/core/fpdfdoc/cpdf_iconfit.cpp b/core/fpdfdoc/cpdf_iconfit.cpp
index 55702a3..5eb95e2 100644
--- a/core/fpdfdoc/cpdf_iconfit.cpp
+++ b/core/fpdfdoc/cpdf_iconfit.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,9 @@
 
 #include "core/fpdfdoc/cpdf_iconfit.h"
 
+#include <algorithm>
+#include <utility>
+
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fxcrt/fx_string.h"
@@ -16,7 +19,8 @@
 
 }  // namespace
 
-CPDF_IconFit::CPDF_IconFit(const CPDF_Dictionary* pDict) : m_pDict(pDict) {}
+CPDF_IconFit::CPDF_IconFit(RetainPtr<const CPDF_Dictionary> pDict)
+    : m_pDict(std::move(pDict)) {}
 
 CPDF_IconFit::CPDF_IconFit(const CPDF_IconFit& that) = default;
 
@@ -24,20 +28,20 @@
 
 CPDF_IconFit::ScaleMethod CPDF_IconFit::GetScaleMethod() const {
   if (!m_pDict)
-    return Always;
+    return ScaleMethod::kAlways;
 
-  ByteString csSW = m_pDict->GetStringFor("SW", "A");
+  ByteString csSW = m_pDict->GetByteStringFor("SW", "A");
   if (csSW == "B")
-    return Bigger;
+    return ScaleMethod::kBigger;
   if (csSW == "S")
-    return Smaller;
+    return ScaleMethod::kSmaller;
   if (csSW == "N")
-    return Never;
-  return Always;
+    return ScaleMethod::kNever;
+  return ScaleMethod::kAlways;
 }
 
 bool CPDF_IconFit::IsProportionalScale() const {
-  return !m_pDict || m_pDict->GetStringFor("S", "P") != "A";
+  return !m_pDict || m_pDict->GetByteStringFor("S", "P") != "A";
 }
 
 CFX_PointF CPDF_IconFit::GetIconBottomLeftPosition() const {
@@ -46,15 +50,15 @@
   if (!m_pDict)
     return {fLeft, fBottom};
 
-  const CPDF_Array* pA = m_pDict->GetArrayFor("A");
+  RetainPtr<const CPDF_Array> pA = m_pDict->GetArrayFor("A");
   if (!pA)
     return {fLeft, fBottom};
 
   size_t dwCount = pA->size();
   if (dwCount > 0)
-    fLeft = pA->GetNumberAt(0);
+    fLeft = pA->GetFloatAt(0);
   if (dwCount > 1)
-    fBottom = pA->GetNumberAt(1);
+    fBottom = pA->GetFloatAt(1);
   return {fLeft, fBottom};
 }
 
@@ -66,11 +70,58 @@
   if (!m_pDict)
     return CFX_PointF();
 
-  const CPDF_Array* pA = m_pDict->GetArrayFor("A");
+  RetainPtr<const CPDF_Array> pA = m_pDict->GetArrayFor("A");
   if (!pA)
     return CFX_PointF();
 
   size_t dwCount = pA->size();
-  return {dwCount > 0 ? pA->GetNumberAt(0) : 0.0f,
-          dwCount > 1 ? pA->GetNumberAt(1) : 0.0f};
+  return {dwCount > 0 ? pA->GetFloatAt(0) : 0.0f,
+          dwCount > 1 ? pA->GetFloatAt(1) : 0.0f};
+}
+
+CFX_VectorF CPDF_IconFit::GetScale(const CFX_SizeF& image_size,
+                                   const CFX_FloatRect& rcPlate) const {
+  float fHScale = 1.0f;
+  float fVScale = 1.0f;
+  const float fPlateWidth = rcPlate.Width();
+  const float fPlateHeight = rcPlate.Height();
+  const float fImageWidth = image_size.width;
+  const float fImageHeight = image_size.height;
+  switch (GetScaleMethod()) {
+    case CPDF_IconFit::ScaleMethod::kAlways:
+      fHScale = fPlateWidth / std::max(fImageWidth, 1.0f);
+      fVScale = fPlateHeight / std::max(fImageHeight, 1.0f);
+      break;
+    case CPDF_IconFit::ScaleMethod::kBigger:
+      if (fPlateWidth < fImageWidth)
+        fHScale = fPlateWidth / std::max(fImageWidth, 1.0f);
+      if (fPlateHeight < fImageHeight)
+        fVScale = fPlateHeight / std::max(fImageHeight, 1.0f);
+      break;
+    case CPDF_IconFit::ScaleMethod::kSmaller:
+      if (fPlateWidth > fImageWidth)
+        fHScale = fPlateWidth / std::max(fImageWidth, 1.0f);
+      if (fPlateHeight > fImageHeight)
+        fVScale = fPlateHeight / std::max(fImageHeight, 1.0f);
+      break;
+    case CPDF_IconFit::ScaleMethod::kNever:
+      break;
+  }
+
+  if (IsProportionalScale()) {
+    float min_scale = std::min(fHScale, fVScale);
+    fHScale = min_scale;
+    fVScale = min_scale;
+  }
+  return {fHScale, fVScale};
+}
+
+CFX_VectorF CPDF_IconFit::GetImageOffset(const CFX_SizeF& image_size,
+                                         const CFX_VectorF& scale,
+                                         const CFX_FloatRect& rcPlate) const {
+  const CFX_PointF icon_position = GetIconPosition();
+  const float fImageFactWidth = image_size.width * scale.x;
+  const float fImageFactHeight = image_size.height * scale.y;
+  return {(rcPlate.Width() - fImageFactWidth) * icon_position.x,
+          (rcPlate.Height() - fImageFactHeight) * icon_position.y};
 }
diff --git a/core/fpdfdoc/cpdf_iconfit.h b/core/fpdfdoc/cpdf_iconfit.h
index 86a4918..3e55e1e 100644
--- a/core/fpdfdoc/cpdf_iconfit.h
+++ b/core/fpdfdoc/cpdf_iconfit.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,16 +8,15 @@
 #define CORE_FPDFDOC_CPDF_ICONFIT_H_
 
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Dictionary;
 
 class CPDF_IconFit {
  public:
-  enum ScaleMethod { Always = 0, Bigger, Smaller, Never };
+  enum class ScaleMethod { kAlways = 0, kBigger, kSmaller, kNever };
 
-  explicit CPDF_IconFit(const CPDF_Dictionary* pDict);
+  explicit CPDF_IconFit(RetainPtr<const CPDF_Dictionary> pDict);
   CPDF_IconFit(const CPDF_IconFit& that);
   ~CPDF_IconFit();
 
@@ -25,9 +24,15 @@
   bool IsProportionalScale() const;
   bool GetFittingBounds() const;
   CFX_PointF GetIconBottomLeftPosition() const;
-  CFX_PointF GetIconPosition() const;
+  CFX_VectorF GetScale(const CFX_SizeF& image_size,
+                       const CFX_FloatRect& rcPlate) const;
+  CFX_VectorF GetImageOffset(const CFX_SizeF& image_size,
+                             const CFX_VectorF& scale,
+                             const CFX_FloatRect& rcPlate) const;
 
  private:
+  CFX_PointF GetIconPosition() const;
+
   RetainPtr<const CPDF_Dictionary> const m_pDict;
 };
 
diff --git a/core/fpdfdoc/cpdf_interactiveform.cpp b/core/fpdfdoc/cpdf_interactiveform.cpp
index a4a1025..de7fd71 100644
--- a/core/fpdfdoc/cpdf_interactiveform.cpp
+++ b/core/fpdfdoc/cpdf_interactiveform.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 
 #include "build/build_config.h"
 #include "constants/form_fields.h"
+#include "constants/form_flags.h"
 #include "constants/stream_dict_common.h"
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/font/cpdf_fontencoding.h"
@@ -22,24 +23,96 @@
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fpdfdoc/cpdf_filespec.h"
 #include "core/fpdfdoc/cpdf_formcontrol.h"
 #include "core/fxcrt/fx_codepage.h"
-#include "core/fxge/cfx_substfont.h"
+#include "core/fxcrt/stl_util.h"
 #include "core/fxge/fx_font.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
 
 const int nMaxRecursion = 32;
 
-void AddFont(CPDF_Dictionary*& pFormDict,
-             CPDF_Document* pDocument,
-             const RetainPtr<CPDF_Font>& pFont,
-             ByteString* csNameTag);
+#if BUILDFLAG(IS_WIN)
+struct PDF_FONTDATA {
+  bool bFind;
+  LOGFONTA lf;
+};
+
+int CALLBACK EnumFontFamExProc(ENUMLOGFONTEXA* lpelfe,
+                               NEWTEXTMETRICEX* lpntme,
+                               DWORD FontType,
+                               LPARAM lParam) {
+  if (FontType != 0x004 || strchr(lpelfe->elfLogFont.lfFaceName, '@'))
+    return 1;
+
+  PDF_FONTDATA* pData = (PDF_FONTDATA*)lParam;
+  memcpy(&pData->lf, &lpelfe->elfLogFont, sizeof(LOGFONTA));
+  pData->bFind = true;
+  return 0;
+}
+
+bool RetrieveSpecificFont(FX_Charset charSet,
+                          LPCSTR pcsFontName,
+                          LOGFONTA& lf) {
+  memset(&lf, 0, sizeof(LOGFONTA));
+  lf.lfCharSet = static_cast<int>(charSet);
+  lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+  if (pcsFontName) {
+    // TODO(dsinclair): Should this be strncpy?
+    // NOLINTNEXTLINE(runtime/printf)
+    strcpy(lf.lfFaceName, pcsFontName);
+  }
+
+  PDF_FONTDATA fd;
+  memset(&fd, 0, sizeof(PDF_FONTDATA));
+  HDC hDC = ::GetDC(nullptr);
+  EnumFontFamiliesExA(hDC, &lf, (FONTENUMPROCA)EnumFontFamExProc, (LPARAM)&fd,
+                      0);
+  ::ReleaseDC(nullptr, hDC);
+  if (fd.bFind)
+    memcpy(&lf, &fd.lf, sizeof(LOGFONTA));
+
+  return fd.bFind;
+}
+#endif  // BUILDFLAG(IS_WIN)
+
+ByteString GetNativeFontName(FX_Charset charSet, void* pLogFont) {
+  ByteString csFontName;
+#if BUILDFLAG(IS_WIN)
+  LOGFONTA lf = {};
+  if (charSet == FX_Charset::kANSI) {
+    csFontName = CFX_Font::kDefaultAnsiFontName;
+    return csFontName;
+  }
+  bool bRet = false;
+  const ByteString default_font_name =
+      CFX_Font::GetDefaultFontNameByCharset(charSet);
+  if (!default_font_name.IsEmpty())
+    bRet = RetrieveSpecificFont(charSet, default_font_name.c_str(), lf);
+  if (!bRet) {
+    bRet =
+        RetrieveSpecificFont(charSet, CFX_Font::kUniversalDefaultFontName, lf);
+  }
+  if (!bRet)
+    bRet = RetrieveSpecificFont(charSet, "Microsoft Sans Serif", lf);
+  if (!bRet)
+    bRet = RetrieveSpecificFont(charSet, nullptr, lf);
+  if (bRet) {
+    if (pLogFont)
+      memcpy(pLogFont, &lf, sizeof(LOGFONTA));
+    csFontName = lf.lfFaceName;
+  }
+#endif
+  return csFontName;
+}
 
 ByteString GenerateNewFontResourceName(const CPDF_Dictionary* pResDict,
                                        const ByteString& csPrefix) {
@@ -58,8 +131,8 @@
     m++;
   }
 
-  const CPDF_Dictionary* pDict = pResDict->GetDictFor("Font");
-  ASSERT(pDict);
+  RetainPtr<const CPDF_Dictionary> pDict = pResDict->GetDictFor("Font");
+  DCHECK(pDict);
 
   int num = 0;
   ByteString bsNum;
@@ -67,146 +140,57 @@
     ByteString csKey = csTmp + bsNum;
     if (!pDict->KeyExist(csKey))
       return csKey;
+
     if (m < szCount)
       csTmp += csStr[m++];
     else
-      bsNum = ByteString::Format("%d", num++);
-
+      bsNum = ByteString::FormatInteger(num++);
     m++;
   }
-  return csTmp;
 }
 
-void InitDict(CPDF_Dictionary*& pFormDict, CPDF_Document* pDocument) {
-  if (!pDocument)
-    return;
-
-  if (!pFormDict) {
-    pFormDict = pDocument->NewIndirect<CPDF_Dictionary>();
-    pDocument->GetRoot()->SetNewFor<CPDF_Reference>("AcroForm", pDocument,
-                                                    pFormDict->GetObjNum());
-  }
-
-  ByteString csDA;
-  if (!pFormDict->KeyExist("DR")) {
-    ByteString csBaseName;
-    uint8_t charSet = CPDF_InteractiveForm::GetNativeCharSet();
-    RetainPtr<CPDF_Font> pFont = CPDF_InteractiveForm::AddStandardFont(
-        pDocument, CFX_Font::kDefaultAnsiFontName);
-    if (pFont)
-      AddFont(pFormDict, pDocument, pFont, &csBaseName);
-
-    if (charSet != FX_CHARSET_ANSI) {
-      ByteString csFontName =
-          CPDF_InteractiveForm::GetNativeFontName(charSet, nullptr);
-      if (!pFont || csFontName != CFX_Font::kDefaultAnsiFontName) {
-        pFont = CPDF_InteractiveForm::AddNativeFont(pDocument);
-        if (pFont) {
-          csBaseName.clear();
-          AddFont(pFormDict, pDocument, pFont, &csBaseName);
-        }
-      }
-    }
-    if (pFont)
-      csDA = "/" + PDF_NameEncode(csBaseName) + " 0 Tf";
-  }
-  if (!csDA.IsEmpty())
-    csDA += " ";
-
-  csDA += "0 g";
-  if (!pFormDict->KeyExist("DA"))
-    pFormDict->SetNewFor<CPDF_String>("DA", csDA, false);
+RetainPtr<CPDF_Font> AddStandardFont(CPDF_Document* pDocument) {
+  auto* pPageData = CPDF_DocPageData::FromDocument(pDocument);
+  static const CPDF_FontEncoding encoding(FontEncoding::kWinAnsi);
+  return pPageData->AddStandardFont(CFX_Font::kDefaultAnsiFontName, &encoding);
 }
 
-RetainPtr<CPDF_Font> GetFont(CPDF_Dictionary* pFormDict,
-                             CPDF_Document* pDocument,
-                             const ByteString& csNameTag) {
-  ByteString csAlias = PDF_NameDecode(csNameTag.AsStringView());
-  if (!pFormDict || csAlias.IsEmpty())
-    return nullptr;
+RetainPtr<CPDF_Font> AddNativeFont(FX_Charset charSet,
+                                   CPDF_Document* pDocument) {
+  DCHECK(pDocument);
 
-  CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR");
-  if (!pDR)
-    return nullptr;
-
-  CPDF_Dictionary* pFonts = pDR->GetDictFor("Font");
-  if (!ValidateFontResourceDict(pFonts))
-    return nullptr;
-
-  CPDF_Dictionary* pElement = pFonts->GetDictFor(csAlias);
-  if (!pElement || pElement->GetStringFor("Type") != "Font")
-    return nullptr;
-
-  return CPDF_DocPageData::FromDocument(pDocument)->GetFont(pElement);
-}
-
-RetainPtr<CPDF_Font> GetNativeFont(CPDF_Dictionary* pFormDict,
-                                   CPDF_Document* pDocument,
-                                   uint8_t charSet,
-                                   ByteString* csNameTag) {
-  if (!pFormDict)
-    return nullptr;
-
-  CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR");
-  if (!pDR)
-    return nullptr;
-
-  CPDF_Dictionary* pFonts = pDR->GetDictFor("Font");
-  if (!ValidateFontResourceDict(pFonts))
-    return nullptr;
-
-  CPDF_DictionaryLocker locker(pFonts);
-  for (const auto& it : locker) {
-    const ByteString& csKey = it.first;
-    if (!it.second)
-      continue;
-
-    CPDF_Dictionary* pElement = ToDictionary(it.second->GetDirect());
-    if (!pElement || pElement->GetStringFor("Type") != "Font")
-      continue;
-
-    auto* pData = CPDF_DocPageData::FromDocument(pDocument);
-    RetainPtr<CPDF_Font> pFind = pData->GetFont(pElement);
-    if (!pFind)
-      continue;
-
-    CFX_SubstFont* pSubst = pFind->GetSubstFont();
-    if (!pSubst)
-      continue;
-
-    if (pSubst->m_Charset == static_cast<int>(charSet)) {
-      *csNameTag = csKey;
-      return pFind;
-    }
+#if BUILDFLAG(IS_WIN)
+  LOGFONTA lf;
+  ByteString csFontName = GetNativeFontName(charSet, &lf);
+  if (!csFontName.IsEmpty()) {
+    if (csFontName == CFX_Font::kDefaultAnsiFontName)
+      return AddStandardFont(pDocument);
+    return CPDF_DocPageData::FromDocument(pDocument)->AddWindowsFont(&lf);
   }
+#endif
   return nullptr;
 }
 
-bool FindFont(CPDF_Dictionary* pFormDict,
+bool FindFont(const CPDF_Dictionary* pFormDict,
               const CPDF_Font* pFont,
               ByteString* csNameTag) {
-  if (!pFormDict || !pFont)
-    return false;
-
-  CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR");
+  RetainPtr<const CPDF_Dictionary> pDR = pFormDict->GetDictFor("DR");
   if (!pDR)
     return false;
 
-  CPDF_Dictionary* pFonts = pDR->GetDictFor("Font");
-  if (!ValidateFontResourceDict(pFonts))
+  RetainPtr<const CPDF_Dictionary> pFonts = pDR->GetDictFor("Font");
+  // TODO(tsepez): this eventually locks the dict, pass locker instead.
+  if (!ValidateFontResourceDict(pFonts.Get()))
     return false;
 
-  CPDF_DictionaryLocker locker(pFonts);
+  CPDF_DictionaryLocker locker(std::move(pFonts));
   for (const auto& it : locker) {
     const ByteString& csKey = it.first;
-    if (!it.second)
+    RetainPtr<const CPDF_Dictionary> pElement =
+        ToDictionary(it.second->GetDirect());
+    if (!ValidateDictType(pElement.Get(), "Font"))
       continue;
-    CPDF_Dictionary* pElement = ToDictionary(it.second->GetDirect());
-    if (!pElement)
-      continue;
-    if (pElement->GetStringFor("Type") != "Font")
-      continue;
-    if (pFont->GetFontDict() == pElement) {
+    if (pFont->FontDictIs(pElement)) {
       *csNameTag = csKey;
       return true;
     }
@@ -214,36 +198,33 @@
   return false;
 }
 
-bool FindFont(CPDF_Dictionary* pFormDict,
-              CPDF_Document* pDocument,
-              ByteString csFontName,
-              RetainPtr<CPDF_Font>& pFont,
-              ByteString* csNameTag) {
-  if (!pFormDict)
+bool FindFontFromDoc(const CPDF_Dictionary* pFormDict,
+                     CPDF_Document* pDocument,
+                     ByteString csFontName,
+                     RetainPtr<CPDF_Font>& pFont,
+                     ByteString* csNameTag) {
+  if (csFontName.IsEmpty())
     return false;
 
-  CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR");
+  RetainPtr<const CPDF_Dictionary> pDR = pFormDict->GetDictFor("DR");
   if (!pDR)
     return false;
 
-  CPDF_Dictionary* pFonts = pDR->GetDictFor("Font");
-  if (!ValidateFontResourceDict(pFonts))
+  RetainPtr<const CPDF_Dictionary> pFonts = pDR->GetDictFor("Font");
+  if (!ValidateFontResourceDict(pFonts.Get()))
     return false;
 
-  if (csFontName.GetLength() > 0)
-    csFontName.Remove(' ');
-
+  csFontName.Remove(' ');
   CPDF_DictionaryLocker locker(pFonts);
   for (const auto& it : locker) {
     const ByteString& csKey = it.first;
-    if (!it.second)
+    RetainPtr<CPDF_Dictionary> pElement =
+        ToDictionary(it.second->GetMutableDirect());
+    if (!ValidateDictType(pElement.Get(), "Font"))
       continue;
 
-    CPDF_Dictionary* pElement = ToDictionary(it.second->GetDirect());
-    if (!pElement || pElement->GetStringFor("Type") != "Font")
-      continue;
-
-    pFont = CPDF_DocPageData::FromDocument(pDocument)->GetFont(pElement);
+    auto* pData = CPDF_DocPageData::FromDocument(pDocument);
+    pFont = pData->GetFont(std::move(pElement));
     if (!pFont)
       continue;
 
@@ -257,140 +238,120 @@
   return false;
 }
 
-void AddFont(CPDF_Dictionary*& pFormDict,
+void AddFont(CPDF_Dictionary* pFormDict,
              CPDF_Document* pDocument,
              const RetainPtr<CPDF_Font>& pFont,
              ByteString* csNameTag) {
-  if (!pFont)
-    return;
-  if (!pFormDict)
-    InitDict(pFormDict, pDocument);
+  DCHECK(pFormDict);
+  DCHECK(pFont);
 
   ByteString csTag;
   if (FindFont(pFormDict, pFont.Get(), &csTag)) {
     *csNameTag = std::move(csTag);
     return;
   }
-  if (!pFormDict)
-    InitDict(pFormDict, pDocument);
 
-  CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR");
-  if (!pDR)
-    pDR = pFormDict->SetNewFor<CPDF_Dictionary>("DR");
-
-  CPDF_Dictionary* pFonts = pDR->GetDictFor("Font");
-  if (!pFonts)
-    pFonts = pDR->SetNewFor<CPDF_Dictionary>("Font");
+  RetainPtr<CPDF_Dictionary> pDR = pFormDict->GetOrCreateDictFor("DR");
+  RetainPtr<CPDF_Dictionary> pFonts = pDR->GetOrCreateDictFor("Font");
 
   if (csNameTag->IsEmpty())
     *csNameTag = pFont->GetBaseFontName();
 
   csNameTag->Remove(' ');
-  *csNameTag = GenerateNewFontResourceName(pDR, *csNameTag);
+  *csNameTag = GenerateNewFontResourceName(pDR.Get(), *csNameTag);
   pFonts->SetNewFor<CPDF_Reference>(*csNameTag, pDocument,
-                                    pFont->GetFontDict()->GetObjNum());
+                                    pFont->GetFontDictObjNum());
 }
 
-RetainPtr<CPDF_Font> AddNativeFont(CPDF_Dictionary*& pFormDict,
-                                   CPDF_Document* pDocument,
-                                   uint8_t charSet,
-                                   ByteString* csNameTag) {
-  if (!pFormDict)
-    InitDict(pFormDict, pDocument);
+FX_Charset GetNativeCharSet() {
+  return FX_GetCharsetFromCodePage(FX_GetACP());
+}
 
-  ByteString csTemp;
-  RetainPtr<CPDF_Font> pFont =
-      GetNativeFont(pFormDict, pDocument, charSet, &csTemp);
+RetainPtr<CPDF_Dictionary> InitDict(CPDF_Document* pDocument) {
+  auto pFormDict = pDocument->NewIndirect<CPDF_Dictionary>();
+  pDocument->GetMutableRoot()->SetNewFor<CPDF_Reference>(
+      "AcroForm", pDocument, pFormDict->GetObjNum());
+
+  ByteString csBaseName;
+  FX_Charset charSet = GetNativeCharSet();
+  RetainPtr<CPDF_Font> pFont = AddStandardFont(pDocument);
   if (pFont) {
-    *csNameTag = std::move(csTemp);
-    return pFont;
+    AddFont(pFormDict.Get(), pDocument, pFont, &csBaseName);
   }
-  ByteString csFontName =
-      CPDF_InteractiveForm::GetNativeFontName(charSet, nullptr);
-  if (!csFontName.IsEmpty() &&
-      FindFont(pFormDict, pDocument, csFontName, pFont, csNameTag)) {
-    return pFont;
+  if (charSet != FX_Charset::kANSI) {
+    ByteString csFontName = GetNativeFontName(charSet, nullptr);
+    if (!pFont || csFontName != CFX_Font::kDefaultAnsiFontName) {
+      pFont = AddNativeFont(charSet, pDocument);
+      if (pFont) {
+        csBaseName.clear();
+        AddFont(pFormDict.Get(), pDocument, pFont, &csBaseName);
+      }
+    }
   }
-  pFont = CPDF_InteractiveForm::AddNativeFont(charSet, pDocument);
-  if (!pFont)
+  ByteString csDA;
+  if (pFont)
+    csDA = "/" + PDF_NameEncode(csBaseName) + " 0 Tf ";
+  csDA += "0 g";
+  pFormDict->SetNewFor<CPDF_String>("DA", csDA, /*bHex=*/false);
+  return pFormDict;
+}
+
+RetainPtr<CPDF_Font> GetNativeFont(const CPDF_Dictionary* pFormDict,
+                                   CPDF_Document* pDocument,
+                                   FX_Charset charSet,
+                                   ByteString* csNameTag) {
+  RetainPtr<const CPDF_Dictionary> pDR = pFormDict->GetDictFor("DR");
+  if (!pDR)
     return nullptr;
 
-  AddFont(pFormDict, pDocument, pFont, csNameTag);
-  return pFont;
+  RetainPtr<const CPDF_Dictionary> pFonts = pDR->GetDictFor("Font");
+  if (!ValidateFontResourceDict(pFonts.Get()))
+    return nullptr;
+
+  CPDF_DictionaryLocker locker(pFonts);
+  for (const auto& it : locker) {
+    const ByteString& csKey = it.first;
+    RetainPtr<CPDF_Dictionary> pElement =
+        ToDictionary(it.second->GetMutableDirect());
+    if (!ValidateDictType(pElement.Get(), "Font"))
+      continue;
+
+    auto* pData = CPDF_DocPageData::FromDocument(pDocument);
+    RetainPtr<CPDF_Font> pFind = pData->GetFont(std::move(pElement));
+    if (!pFind)
+      continue;
+
+    auto maybe_charset = pFind->GetSubstFontCharset();
+    if (maybe_charset.has_value() && maybe_charset.value() == charSet) {
+      *csNameTag = csKey;
+      return pFind;
+    }
+  }
+  return nullptr;
 }
 
 class CFieldNameExtractor {
  public:
   explicit CFieldNameExtractor(const WideString& full_name)
-      : m_FullName(full_name) {
-    m_pCur = m_FullName.c_str();
-    m_pEnd = m_pCur + m_FullName.GetLength();
-  }
+      : m_FullName(full_name) {}
 
-  void GetNext(const wchar_t*& pSubName, size_t& size) {
-    pSubName = m_pCur;
-    while (m_pCur < m_pEnd && m_pCur[0] != L'.')
-      m_pCur++;
+  WideStringView GetNext() {
+    size_t start_pos = m_iCur;
+    while (m_iCur < m_FullName.GetLength() && m_FullName[m_iCur] != L'.')
+      ++m_iCur;
 
-    size = static_cast<size_t>(m_pCur - pSubName);
-    if (m_pCur < m_pEnd && m_pCur[0] == L'.')
-      m_pCur++;
+    size_t length = m_iCur - start_pos;
+    if (m_iCur < m_FullName.GetLength() && m_FullName[m_iCur] == L'.')
+      ++m_iCur;
+
+    return m_FullName.AsStringView().Substr(start_pos, length);
   }
 
  protected:
-  WideString m_FullName;
-  const wchar_t* m_pCur;
-  const wchar_t* m_pEnd;
+  const WideString m_FullName;
+  size_t m_iCur = 0;
 };
 
-#if defined(OS_WIN)
-struct PDF_FONTDATA {
-  bool bFind;
-  LOGFONTA lf;
-};
-
-static int CALLBACK EnumFontFamExProc(ENUMLOGFONTEXA* lpelfe,
-                                      NEWTEXTMETRICEX* lpntme,
-                                      DWORD FontType,
-                                      LPARAM lParam) {
-  if (FontType != 0x004 || strchr(lpelfe->elfLogFont.lfFaceName, '@'))
-    return 1;
-
-  PDF_FONTDATA* pData = (PDF_FONTDATA*)lParam;
-  memcpy(&pData->lf, &lpelfe->elfLogFont, sizeof(LOGFONTA));
-  pData->bFind = true;
-  return 0;
-}
-
-bool RetrieveSpecificFont(LOGFONTA& lf) {
-  PDF_FONTDATA fd;
-  memset(&fd, 0, sizeof(PDF_FONTDATA));
-  HDC hDC = ::GetDC(nullptr);
-  EnumFontFamiliesExA(hDC, &lf, (FONTENUMPROCA)EnumFontFamExProc, (LPARAM)&fd,
-                      0);
-  ::ReleaseDC(nullptr, hDC);
-  if (fd.bFind)
-    memcpy(&lf, &fd.lf, sizeof(LOGFONTA));
-
-  return fd.bFind;
-}
-
-bool RetrieveSpecificFont(uint8_t charSet,
-                          uint8_t pitchAndFamily,
-                          LPCSTR pcsFontName,
-                          LOGFONTA& lf) {
-  memset(&lf, 0, sizeof(LOGFONTA));
-  lf.lfCharSet = charSet;
-  lf.lfPitchAndFamily = pitchAndFamily;
-  if (pcsFontName) {
-    // TODO(dsinclair): Should this be strncpy?
-    // NOLINTNEXTLINE(runtime/printf)
-    strcpy(lf.lfFaceName, pcsFontName);
-  }
-  return RetrieveSpecificFont(lf);
-}
-#endif  // defined(OS_WIN)
-
 }  // namespace
 
 class CFieldTree {
@@ -465,15 +426,16 @@
                 std::unique_ptr<CPDF_FormField> pField);
   CPDF_FormField* GetField(const WideString& full_name);
 
+  Node* GetRoot() { return m_pRoot.get(); }
   Node* FindNode(const WideString& full_name);
   Node* AddChild(Node* pParent, const WideString& short_name);
+  Node* Lookup(Node* pParent, WideStringView short_name);
 
-  Node* Lookup(Node* pParent, const WideString& short_name);
-
-  Node m_Root;
+ private:
+  std::unique_ptr<Node> m_pRoot;
 };
 
-CFieldTree::CFieldTree() = default;
+CFieldTree::CFieldTree() : m_pRoot(std::make_unique<Node>()) {}
 
 CFieldTree::~CFieldTree() = default;
 
@@ -486,14 +448,13 @@
   if (level > nMaxRecursion)
     return nullptr;
 
-  auto pNew = pdfium::MakeUnique<Node>(short_name, pParent->GetLevel() + 1);
+  auto pNew = std::make_unique<Node>(short_name, pParent->GetLevel() + 1);
   Node* pChild = pNew.get();
   pParent->AddChildNode(std::move(pNew));
   return pChild;
 }
 
-CFieldTree::Node* CFieldTree::Lookup(Node* pParent,
-                                     const WideString& short_name) {
+CFieldTree::Node* CFieldTree::Lookup(Node* pParent, WideStringView short_name) {
   if (!pParent)
     return nullptr;
 
@@ -510,24 +471,22 @@
   if (full_name.IsEmpty())
     return false;
 
-  CFieldNameExtractor name_extractor(full_name);
-  const wchar_t* pName;
-  size_t nLength;
-  name_extractor.GetNext(pName, nLength);
-  Node* pNode = &m_Root;
+  Node* pNode = GetRoot();
   Node* pLast = nullptr;
-  while (nLength > 0) {
+  CFieldNameExtractor name_extractor(full_name);
+  while (true) {
+    WideStringView name_view = name_extractor.GetNext();
+    if (name_view.IsEmpty())
+      break;
     pLast = pNode;
-    WideString name = WideString(pName, nLength);
-    pNode = Lookup(pLast, name);
-    if (!pNode)
-      pNode = AddChild(pLast, name);
+    pNode = Lookup(pLast, name_view);
+    if (pNode)
+      continue;
+    pNode = AddChild(pLast, WideString(name_view));
     if (!pNode)
       return false;
-
-    name_extractor.GetNext(pName, nLength);
   }
-  if (pNode == &m_Root)
+  if (pNode == GetRoot())
     return false;
 
   pNode->SetField(std::move(pField));
@@ -538,17 +497,15 @@
   if (full_name.IsEmpty())
     return nullptr;
 
-  CFieldNameExtractor name_extractor(full_name);
-  const wchar_t* pName;
-  size_t nLength;
-  name_extractor.GetNext(pName, nLength);
-  Node* pNode = &m_Root;
+  Node* pNode = GetRoot();
   Node* pLast = nullptr;
-  while (nLength > 0 && pNode) {
+  CFieldNameExtractor name_extractor(full_name);
+  while (pNode) {
+    WideStringView name_view = name_extractor.GetNext();
+    if (name_view.IsEmpty())
+      break;
     pLast = pNode;
-    WideString name = WideString(pName, nLength);
-    pNode = Lookup(pLast, name);
-    name_extractor.GetNext(pName, nLength);
+    pNode = Lookup(pLast, name_view);
   }
   return pNode ? pNode->GetField() : nullptr;
 }
@@ -557,159 +514,103 @@
   if (full_name.IsEmpty())
     return nullptr;
 
-  CFieldNameExtractor name_extractor(full_name);
-  const wchar_t* pName;
-  size_t nLength;
-  name_extractor.GetNext(pName, nLength);
-  Node* pNode = &m_Root;
+  Node* pNode = GetRoot();
   Node* pLast = nullptr;
-  while (nLength > 0 && pNode) {
+  CFieldNameExtractor name_extractor(full_name);
+  while (pNode) {
+    WideStringView name_view = name_extractor.GetNext();
+    if (name_view.IsEmpty())
+      break;
     pLast = pNode;
-    WideString name = WideString(pName, nLength);
-    pNode = Lookup(pLast, name);
-    name_extractor.GetNext(pName, nLength);
+    pNode = Lookup(pLast, name_view);
   }
   return pNode;
 }
 
-RetainPtr<CPDF_Font> AddNativeInteractiveFormFont(CPDF_Dictionary*& pFormDict,
-                                                  CPDF_Document* pDocument,
-                                                  ByteString* csNameTag) {
-  uint8_t charSet = CPDF_InteractiveForm::GetNativeCharSet();
-  return AddNativeFont(pFormDict, pDocument, charSet, csNameTag);
-}
-
-// static
-uint8_t CPDF_InteractiveForm::GetNativeCharSet() {
-  return FX_GetCharsetFromCodePage(FXSYS_GetACP());
-}
-
 CPDF_InteractiveForm::CPDF_InteractiveForm(CPDF_Document* pDocument)
-    : m_pDocument(pDocument), m_pFieldTree(pdfium::MakeUnique<CFieldTree>()) {
-  CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
+    : m_pDocument(pDocument), m_pFieldTree(std::make_unique<CFieldTree>()) {
+  RetainPtr<CPDF_Dictionary> pRoot = m_pDocument->GetMutableRoot();
   if (!pRoot)
     return;
 
-  m_pFormDict.Reset(pRoot->GetDictFor("AcroForm"));
+  m_pFormDict = pRoot->GetMutableDictFor("AcroForm");
   if (!m_pFormDict)
     return;
 
-  CPDF_Array* pFields = m_pFormDict->GetArrayFor("Fields");
+  RetainPtr<CPDF_Array> pFields = m_pFormDict->GetMutableArrayFor("Fields");
   if (!pFields)
     return;
 
   for (size_t i = 0; i < pFields->size(); ++i)
-    LoadField(pFields->GetDictAt(i), 0);
+    LoadField(pFields->GetMutableDictAt(i), 0);
 }
 
 CPDF_InteractiveForm::~CPDF_InteractiveForm() = default;
 
 bool CPDF_InteractiveForm::s_bUpdateAP = true;
 
+// static
 bool CPDF_InteractiveForm::IsUpdateAPEnabled() {
   return s_bUpdateAP;
 }
 
+// static
 void CPDF_InteractiveForm::SetUpdateAP(bool bUpdateAP) {
   s_bUpdateAP = bUpdateAP;
 }
 
-RetainPtr<CPDF_Font> CPDF_InteractiveForm::AddStandardFont(
+// static
+RetainPtr<CPDF_Font> CPDF_InteractiveForm::AddNativeInteractiveFormFont(
     CPDF_Document* pDocument,
-    ByteString csFontName) {
-  if (!pDocument || csFontName.IsEmpty())
+    ByteString* csNameTag) {
+  DCHECK(pDocument);
+  DCHECK(csNameTag);
+
+  RetainPtr<CPDF_Dictionary> pFormDict =
+      pDocument->GetMutableRoot()->GetMutableDictFor("AcroForm");
+  if (!pFormDict)
+    pFormDict = InitDict(pDocument);
+
+  FX_Charset charSet = GetNativeCharSet();
+  ByteString csTemp;
+  RetainPtr<CPDF_Font> pFont =
+      GetNativeFont(pFormDict.Get(), pDocument, charSet, &csTemp);
+  if (pFont) {
+    *csNameTag = std::move(csTemp);
+    return pFont;
+  }
+  ByteString csFontName = GetNativeFontName(charSet, nullptr);
+  if (FindFontFromDoc(pFormDict.Get(), pDocument, csFontName, pFont, csNameTag))
+    return pFont;
+
+  pFont = AddNativeFont(charSet, pDocument);
+  if (!pFont)
     return nullptr;
 
-  auto* pPageData = CPDF_DocPageData::FromDocument(pDocument);
-  if (csFontName == "ZapfDingbats")
-    return pPageData->AddStandardFont(csFontName, nullptr);
-
-  static const CPDF_FontEncoding encoding(PDFFONT_ENCODING_WINANSI);
-  return pPageData->AddStandardFont(csFontName, &encoding);
-}
-
-ByteString CPDF_InteractiveForm::GetNativeFontName(uint8_t charSet,
-                                                   void* pLogFont) {
-  ByteString csFontName;
-#if defined(OS_WIN)
-  LOGFONTA lf = {};
-  if (charSet == FX_CHARSET_ANSI) {
-    csFontName = CFX_Font::kDefaultAnsiFontName;
-    return csFontName;
-  }
-  bool bRet = false;
-  const ByteString default_font_name =
-      CFX_Font::GetDefaultFontNameByCharset(charSet);
-  if (!default_font_name.IsEmpty()) {
-    bRet = RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE,
-                                default_font_name.c_str(), lf);
-  }
-  if (!bRet) {
-    bRet = RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE,
-                                CFX_Font::kUniversalDefaultFontName, lf);
-  }
-  if (!bRet) {
-    bRet = RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE,
-                                "Microsoft Sans Serif", lf);
-  }
-  if (!bRet) {
-    bRet =
-        RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE, nullptr, lf);
-  }
-  if (bRet) {
-    if (pLogFont)
-      memcpy(pLogFont, &lf, sizeof(LOGFONTA));
-
-    csFontName = lf.lfFaceName;
-    return csFontName;
-  }
-#endif
-  return csFontName;
-}
-
-RetainPtr<CPDF_Font> CPDF_InteractiveForm::AddNativeFont(
-    uint8_t charSet,
-    CPDF_Document* pDocument) {
-  if (!pDocument)
-    return nullptr;
-
-#if defined(OS_WIN)
-  LOGFONTA lf;
-  ByteString csFontName = GetNativeFontName(charSet, &lf);
-  if (!csFontName.IsEmpty()) {
-    if (csFontName == CFX_Font::kDefaultAnsiFontName)
-      return AddStandardFont(pDocument, csFontName);
-    return CPDF_DocPageData::FromDocument(pDocument)->AddWindowsFont(&lf);
-  }
-#endif
-  return nullptr;
-}
-
-RetainPtr<CPDF_Font> CPDF_InteractiveForm::AddNativeFont(
-    CPDF_Document* pDocument) {
-  return pDocument ? AddNativeFont(GetNativeCharSet(), pDocument) : nullptr;
+  AddFont(pFormDict.Get(), pDocument, pFont, csNameTag);
+  return pFont;
 }
 
 size_t CPDF_InteractiveForm::CountFields(const WideString& csFieldName) const {
   if (csFieldName.IsEmpty())
-    return m_pFieldTree->m_Root.CountFields();
+    return m_pFieldTree->GetRoot()->CountFields();
 
   CFieldTree::Node* pNode = m_pFieldTree->FindNode(csFieldName);
   return pNode ? pNode->CountFields() : 0;
 }
 
 CPDF_FormField* CPDF_InteractiveForm::GetField(
-    uint32_t index,
+    size_t index,
     const WideString& csFieldName) const {
   if (csFieldName.IsEmpty())
-    return m_pFieldTree->m_Root.GetFieldAtIndex(index);
+    return m_pFieldTree->GetRoot()->GetFieldAtIndex(index);
 
   CFieldTree::Node* pNode = m_pFieldTree->FindNode(csFieldName);
   return pNode ? pNode->GetFieldAtIndex(index) : nullptr;
 }
 
 CPDF_FormField* CPDF_InteractiveForm::GetFieldByDict(
-    CPDF_Dictionary* pFieldDict) const {
+    const CPDF_Dictionary* pFieldDict) const {
   if (!pFieldDict)
     return nullptr;
 
@@ -717,26 +618,26 @@
   return m_pFieldTree->GetField(csWName);
 }
 
-CPDF_FormControl* CPDF_InteractiveForm::GetControlAtPoint(
-    CPDF_Page* pPage,
+const CPDF_FormControl* CPDF_InteractiveForm::GetControlAtPoint(
+    const CPDF_Page* pPage,
     const CFX_PointF& point,
-
     int* z_order) const {
-  CPDF_Array* pAnnotList = pPage->GetDict()->GetArrayFor("Annots");
+  RetainPtr<const CPDF_Array> pAnnotList = pPage->GetAnnotsArray();
   if (!pAnnotList)
     return nullptr;
 
   for (size_t i = pAnnotList->size(); i > 0; --i) {
     size_t annot_index = i - 1;
-    const CPDF_Dictionary* pAnnot = pAnnotList->GetDictAt(annot_index);
+    RetainPtr<const CPDF_Dictionary> pAnnot =
+        pAnnotList->GetDictAt(annot_index);
     if (!pAnnot)
       continue;
 
-    const auto it = m_ControlMap.find(pAnnot);
+    const auto it = m_ControlMap.find(pAnnot.Get());
     if (it == m_ControlMap.end())
       continue;
 
-    CPDF_FormControl* pControl = it->second.get();
+    const CPDF_FormControl* pControl = it->second.get();
     if (!pControl->GetRect().Contains(point))
       continue;
 
@@ -761,156 +662,165 @@
   if (!m_pFormDict)
     return 0;
 
-  CPDF_Array* pArray = m_pFormDict->GetArrayFor("CO");
-  return pArray ? pArray->size() : 0;
+  RetainPtr<const CPDF_Array> pArray = m_pFormDict->GetArrayFor("CO");
+  return pArray ? fxcrt::CollectionSize<int>(*pArray) : 0;
 }
 
 CPDF_FormField* CPDF_InteractiveForm::GetFieldInCalculationOrder(int index) {
   if (!m_pFormDict || index < 0)
     return nullptr;
 
-  CPDF_Array* pArray = m_pFormDict->GetArrayFor("CO");
+  RetainPtr<const CPDF_Array> pArray = m_pFormDict->GetArrayFor("CO");
   if (!pArray)
     return nullptr;
 
-  CPDF_Dictionary* pElement = ToDictionary(pArray->GetDirectObjectAt(index));
-  return pElement ? GetFieldByDict(pElement) : nullptr;
+  RetainPtr<const CPDF_Dictionary> pElement =
+      ToDictionary(pArray->GetDirectObjectAt(index));
+  return pElement ? GetFieldByDict(pElement.Get()) : nullptr;
 }
 
 int CPDF_InteractiveForm::FindFieldInCalculationOrder(
     const CPDF_FormField* pField) {
-  if (!m_pFormDict || !pField)
+  if (!m_pFormDict)
     return -1;
 
-  CPDF_Array* pArray = m_pFormDict->GetArrayFor("CO");
+  RetainPtr<const CPDF_Array> pArray = m_pFormDict->GetArrayFor("CO");
   if (!pArray)
     return -1;
 
-  for (size_t i = 0; i < pArray->size(); i++) {
-    CPDF_Object* pElement = pArray->GetDirectObjectAt(i);
-    if (pElement == pField->GetDict())
-      return i;
-  }
-  return -1;
+  absl::optional<size_t> maybe_found = pArray->Find(pField->GetFieldDict());
+  if (!maybe_found.has_value())
+    return -1;
+
+  return pdfium::base::checked_cast<int>(maybe_found.value());
 }
 
 RetainPtr<CPDF_Font> CPDF_InteractiveForm::GetFormFont(
     ByteString csNameTag) const {
-  return GetFont(m_pFormDict.Get(), m_pDocument.Get(), csNameTag);
+  ByteString csAlias = PDF_NameDecode(csNameTag.AsStringView());
+  if (!m_pFormDict || csAlias.IsEmpty())
+    return nullptr;
+
+  RetainPtr<CPDF_Dictionary> pDR = m_pFormDict->GetMutableDictFor("DR");
+  if (!pDR)
+    return nullptr;
+
+  RetainPtr<CPDF_Dictionary> pFonts = pDR->GetMutableDictFor("Font");
+  if (!ValidateFontResourceDict(pFonts.Get()))
+    return nullptr;
+
+  RetainPtr<CPDF_Dictionary> pElement = pFonts->GetMutableDictFor(csAlias);
+  if (!ValidateDictType(pElement.Get(), "Font"))
+    return nullptr;
+
+  return GetFontForElement(std::move(pElement));
+}
+
+RetainPtr<CPDF_Font> CPDF_InteractiveForm::GetFontForElement(
+    RetainPtr<CPDF_Dictionary> pElement) const {
+  auto* pData = CPDF_DocPageData::FromDocument(m_pDocument);
+  return pData->GetFont(std::move(pElement));
 }
 
 CPDF_DefaultAppearance CPDF_InteractiveForm::GetDefaultAppearance() const {
   if (!m_pFormDict)
     return CPDF_DefaultAppearance();
-  return CPDF_DefaultAppearance(m_pFormDict->GetStringFor("DA"));
+  return CPDF_DefaultAppearance(m_pFormDict->GetByteStringFor("DA"));
 }
 
 int CPDF_InteractiveForm::GetFormAlignment() const {
   return m_pFormDict ? m_pFormDict->GetIntegerFor("Q", 0) : 0;
 }
 
-void CPDF_InteractiveForm::ResetForm(const std::vector<CPDF_FormField*>& fields,
-                                     bool bIncludeOrExclude,
-                                     NotificationOption notify) {
-  size_t nCount = m_pFieldTree->m_Root.CountFields();
+void CPDF_InteractiveForm::ResetForm(pdfium::span<CPDF_FormField*> fields,
+                                     bool bIncludeOrExclude) {
+  CFieldTree::Node* pRoot = m_pFieldTree->GetRoot();
+  const size_t nCount = pRoot->CountFields();
   for (size_t i = 0; i < nCount; ++i) {
-    CPDF_FormField* pField = m_pFieldTree->m_Root.GetFieldAtIndex(i);
+    CPDF_FormField* pField = pRoot->GetFieldAtIndex(i);
     if (!pField)
       continue;
 
-    if (bIncludeOrExclude == pdfium::ContainsValue(fields, pField))
-      pField->ResetField(notify);
+    if (bIncludeOrExclude == pdfium::Contains(fields, pField))
+      pField->ResetField();
   }
-  if (notify == NotificationOption::kNotify && m_pFormNotify)
+  if (m_pFormNotify)
     m_pFormNotify->AfterFormReset(this);
 }
 
-void CPDF_InteractiveForm::ResetForm(NotificationOption notify) {
-  size_t nCount = m_pFieldTree->m_Root.CountFields();
-  for (size_t i = 0; i < nCount; ++i) {
-    CPDF_FormField* pField = m_pFieldTree->m_Root.GetFieldAtIndex(i);
-    if (!pField)
-      continue;
-
-    pField->ResetField(notify);
-  }
-  if (notify == NotificationOption::kNotify && m_pFormNotify)
-    m_pFormNotify->AfterFormReset(this);
+void CPDF_InteractiveForm::ResetForm() {
+  ResetForm(/*fields=*/{}, /*bIncludeOrExclude=*/false);
 }
 
 const std::vector<UnownedPtr<CPDF_FormControl>>&
 CPDF_InteractiveForm::GetControlsForField(const CPDF_FormField* pField) {
-  return m_ControlLists[pField];
+  return m_ControlLists[pdfium::WrapUnowned(pField)];
 }
 
-void CPDF_InteractiveForm::LoadField(CPDF_Dictionary* pFieldDict, int nLevel) {
+void CPDF_InteractiveForm::LoadField(RetainPtr<CPDF_Dictionary> pFieldDict,
+                                     int nLevel) {
   if (nLevel > nMaxRecursion)
     return;
   if (!pFieldDict)
     return;
 
   uint32_t dwParentObjNum = pFieldDict->GetObjNum();
-  CPDF_Array* pKids = pFieldDict->GetArrayFor(pdfium::form_fields::kKids);
+  RetainPtr<CPDF_Array> pKids =
+      pFieldDict->GetMutableArrayFor(pdfium::form_fields::kKids);
   if (!pKids) {
-    AddTerminalField(pFieldDict);
+    AddTerminalField(std::move(pFieldDict));
     return;
   }
 
-  CPDF_Dictionary* pFirstKid = pKids->GetDictAt(0);
+  RetainPtr<const CPDF_Dictionary> pFirstKid = pKids->GetDictAt(0);
   if (!pFirstKid)
     return;
 
-  if (pFirstKid->KeyExist(pdfium::form_fields::kT) ||
-      pFirstKid->KeyExist(pdfium::form_fields::kKids)) {
-    for (size_t i = 0; i < pKids->size(); i++) {
-      CPDF_Dictionary* pChildDict = pKids->GetDictAt(i);
-      if (pChildDict) {
-        if (pChildDict->GetObjNum() != dwParentObjNum)
-          LoadField(pChildDict, nLevel + 1);
-      }
-    }
-  } else {
-    AddTerminalField(pFieldDict);
+  if (!pFirstKid->KeyExist(pdfium::form_fields::kT) &&
+      !pFirstKid->KeyExist(pdfium::form_fields::kKids)) {
+    AddTerminalField(std::move(pFieldDict));
+    return;
+  }
+  for (size_t i = 0; i < pKids->size(); i++) {
+    RetainPtr<CPDF_Dictionary> pChildDict = pKids->GetMutableDictAt(i);
+    if (pChildDict && pChildDict->GetObjNum() != dwParentObjNum)
+      LoadField(std::move(pChildDict), nLevel + 1);
   }
 }
 
-bool CPDF_InteractiveForm::HasXFAForm() const {
-  return m_pFormDict && m_pFormDict->GetArrayFor("XFA");
-}
-
 void CPDF_InteractiveForm::FixPageFields(CPDF_Page* pPage) {
-  CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots");
+  RetainPtr<CPDF_Array> pAnnots = pPage->GetMutableAnnotsArray();
   if (!pAnnots)
     return;
 
   for (size_t i = 0; i < pAnnots->size(); i++) {
-    CPDF_Dictionary* pAnnot = pAnnots->GetDictAt(i);
-    if (pAnnot && pAnnot->GetStringFor("Subtype") == "Widget")
-      LoadField(pAnnot, 0);
+    RetainPtr<CPDF_Dictionary> pAnnot = pAnnots->GetMutableDictAt(i);
+    if (pAnnot && pAnnot->GetNameFor("Subtype") == "Widget")
+      LoadField(std::move(pAnnot), 0);
   }
 }
 
-void CPDF_InteractiveForm::AddTerminalField(CPDF_Dictionary* pFieldDict) {
+void CPDF_InteractiveForm::AddTerminalField(
+    RetainPtr<CPDF_Dictionary> pFieldDict) {
   if (!pFieldDict->KeyExist(pdfium::form_fields::kFT)) {
     // Key "FT" is required for terminal fields, it is also inheritable.
-    CPDF_Dictionary* pParentDict =
+    RetainPtr<const CPDF_Dictionary> pParentDict =
         pFieldDict->GetDictFor(pdfium::form_fields::kParent);
     if (!pParentDict || !pParentDict->KeyExist(pdfium::form_fields::kFT))
       return;
   }
 
-  CPDF_Dictionary* pDict = pFieldDict;
-  WideString csWName = CPDF_FormField::GetFullNameForDict(pFieldDict);
+  WideString csWName = CPDF_FormField::GetFullNameForDict(pFieldDict.Get());
   if (csWName.IsEmpty())
     return;
 
   CPDF_FormField* pField = nullptr;
   pField = m_pFieldTree->GetField(csWName);
   if (!pField) {
-    CPDF_Dictionary* pParent = pFieldDict;
+    RetainPtr<CPDF_Dictionary> pParent(pFieldDict);
     if (!pFieldDict->KeyExist(pdfium::form_fields::kT) &&
-        pFieldDict->GetStringFor("Subtype") == "Widget") {
-      pParent = pFieldDict->GetDictFor(pdfium::form_fields::kParent);
+        pFieldDict->GetNameFor("Subtype") == "Widget") {
+      pParent = pFieldDict->GetMutableDictFor(pdfium::form_fields::kParent);
       if (!pParent)
         pParent = pFieldDict;
     }
@@ -918,71 +828,71 @@
     if (pParent && pParent != pFieldDict &&
         !pParent->KeyExist(pdfium::form_fields::kFT)) {
       if (pFieldDict->KeyExist(pdfium::form_fields::kFT)) {
-        CPDF_Object* pFTValue =
+        RetainPtr<const CPDF_Object> pFTValue =
             pFieldDict->GetDirectObjectFor(pdfium::form_fields::kFT);
         if (pFTValue)
           pParent->SetFor(pdfium::form_fields::kFT, pFTValue->Clone());
       }
 
       if (pFieldDict->KeyExist(pdfium::form_fields::kFf)) {
-        CPDF_Object* pFfValue =
+        RetainPtr<const CPDF_Object> pFfValue =
             pFieldDict->GetDirectObjectFor(pdfium::form_fields::kFf);
         if (pFfValue)
           pParent->SetFor(pdfium::form_fields::kFf, pFfValue->Clone());
       }
     }
 
-    auto newField = pdfium::MakeUnique<CPDF_FormField>(this, pParent);
+    auto newField = std::make_unique<CPDF_FormField>(this, std::move(pParent));
     pField = newField.get();
-    CPDF_Object* pTObj = pDict->GetObjectFor(pdfium::form_fields::kT);
+    RetainPtr<const CPDF_Object> pTObj =
+        pFieldDict->GetObjectFor(pdfium::form_fields::kT);
     if (ToReference(pTObj)) {
       RetainPtr<CPDF_Object> pClone = pTObj->CloneDirectObject();
       if (pClone)
-        pDict->SetFor(pdfium::form_fields::kT, std::move(pClone));
+        pFieldDict->SetFor(pdfium::form_fields::kT, std::move(pClone));
       else
-        pDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kT, ByteString());
+        pFieldDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kT, ByteString());
     }
     if (!m_pFieldTree->SetField(csWName, std::move(newField)))
       return;
   }
 
-  CPDF_Array* pKids = pFieldDict->GetArrayFor(pdfium::form_fields::kKids);
-  if (pKids) {
-    for (size_t i = 0; i < pKids->size(); i++) {
-      CPDF_Dictionary* pKid = pKids->GetDictAt(i);
-      if (!pKid)
-        continue;
-      if (pKid->GetStringFor("Subtype") != "Widget")
-        continue;
-
-      AddControl(pField, pKid);
-    }
-  } else {
-    if (pFieldDict->GetStringFor("Subtype") == "Widget")
-      AddControl(pField, pFieldDict);
+  RetainPtr<CPDF_Array> pKids =
+      pFieldDict->GetMutableArrayFor(pdfium::form_fields::kKids);
+  if (!pKids) {
+    if (pFieldDict->GetNameFor("Subtype") == "Widget")
+      AddControl(pField, std::move(pFieldDict));
+    return;
+  }
+  for (size_t i = 0; i < pKids->size(); i++) {
+    RetainPtr<CPDF_Dictionary> pKid = pKids->GetMutableDictAt(i);
+    if (pKid && pKid->GetNameFor("Subtype") == "Widget")
+      AddControl(pField, std::move(pKid));
   }
 }
 
 CPDF_FormControl* CPDF_InteractiveForm::AddControl(
     CPDF_FormField* pField,
-    CPDF_Dictionary* pWidgetDict) {
-  const auto it = m_ControlMap.find(pWidgetDict);
+    RetainPtr<CPDF_Dictionary> pWidgetDict) {
+  DCHECK(pWidgetDict);
+  const auto it = m_ControlMap.find(pWidgetDict.Get());
   if (it != m_ControlMap.end())
     return it->second.get();
 
-  auto pNew = pdfium::MakeUnique<CPDF_FormControl>(pField, pWidgetDict);
+  auto pNew = std::make_unique<CPDF_FormControl>(pField, pWidgetDict, this);
   CPDF_FormControl* pControl = pNew.get();
   m_ControlMap[pWidgetDict] = std::move(pNew);
-  m_ControlLists[pField].emplace_back(pControl);
+  m_ControlLists[pdfium::WrapUnowned(pField)].emplace_back(pControl);
   return pControl;
 }
 
 bool CPDF_InteractiveForm::CheckRequiredFields(
     const std::vector<CPDF_FormField*>* fields,
     bool bIncludeOrExclude) const {
-  size_t nCount = m_pFieldTree->m_Root.CountFields();
+  CFieldTree::Node* pRoot = m_pFieldTree->GetRoot();
+  const size_t nCount = pRoot->CountFields();
   for (size_t i = 0; i < nCount; ++i) {
-    CPDF_FormField* pField = m_pFieldTree->m_Root.GetFieldAtIndex(i);
+    CPDF_FormField* pField = pRoot->GetFieldAtIndex(i);
     if (!pField)
       continue;
 
@@ -997,11 +907,11 @@
 
     bool bFind = true;
     if (fields)
-      bFind = pdfium::ContainsValue(*fields, pField);
+      bFind = pdfium::Contains(*fields, pField);
     if (bIncludeOrExclude == bFind) {
-      const CPDF_Dictionary* pFieldDict = pField->GetDict();
+      RetainPtr<const CPDF_Dictionary> pFieldDict = pField->GetFieldDict();
       if (pField->IsRequired() &&
-          pFieldDict->GetStringFor(pdfium::form_fields::kV).IsEmpty()) {
+          pFieldDict->GetByteStringFor(pdfium::form_fields::kV).IsEmpty()) {
         return false;
       }
     }
@@ -1010,69 +920,67 @@
 }
 
 std::unique_ptr<CFDF_Document> CPDF_InteractiveForm::ExportToFDF(
-    const WideString& pdf_path,
-    bool bSimpleFileSpec) const {
+    const WideString& pdf_path) const {
   std::vector<CPDF_FormField*> fields;
-  size_t nCount = m_pFieldTree->m_Root.CountFields();
+  CFieldTree::Node* pRoot = m_pFieldTree->GetRoot();
+  const size_t nCount = pRoot->CountFields();
   for (size_t i = 0; i < nCount; ++i)
-    fields.push_back(m_pFieldTree->m_Root.GetFieldAtIndex(i));
-  return ExportToFDF(pdf_path, fields, true, bSimpleFileSpec);
+    fields.push_back(pRoot->GetFieldAtIndex(i));
+  return ExportToFDF(pdf_path, fields, true);
 }
 
 std::unique_ptr<CFDF_Document> CPDF_InteractiveForm::ExportToFDF(
     const WideString& pdf_path,
     const std::vector<CPDF_FormField*>& fields,
-    bool bIncludeOrExclude,
-    bool bSimpleFileSpec) const {
+    bool bIncludeOrExclude) const {
   std::unique_ptr<CFDF_Document> pDoc = CFDF_Document::CreateNewDoc();
   if (!pDoc)
     return nullptr;
 
-  CPDF_Dictionary* pMainDict = pDoc->GetRoot()->GetDictFor("FDF");
+  RetainPtr<CPDF_Dictionary> pMainDict =
+      pDoc->GetMutableRoot()->GetMutableDictFor("FDF");
   if (!pdf_path.IsEmpty()) {
-    if (bSimpleFileSpec) {
-      WideString wsFilePath = CPDF_FileSpec::EncodeFileName(pdf_path);
-      pMainDict->SetNewFor<CPDF_String>(pdfium::stream::kF,
-                                        wsFilePath.ToDefANSI(), false);
-      pMainDict->SetNewFor<CPDF_String>("UF", wsFilePath);
-    } else {
-      auto pNewDict = pDoc->New<CPDF_Dictionary>();
-      pNewDict->SetNewFor<CPDF_Name>("Type", "Filespec");
-      CPDF_FileSpec filespec(pNewDict.Get());
-      filespec.SetFileName(pdf_path);
-      pMainDict->SetFor("F", pNewDict);
-    }
+    auto pNewDict = pDoc->New<CPDF_Dictionary>();
+    pNewDict->SetNewFor<CPDF_Name>("Type", "Filespec");
+    WideString wsStr = CPDF_FileSpec::EncodeFileName(pdf_path);
+    pNewDict->SetNewFor<CPDF_String>(pdfium::stream::kF, wsStr.ToDefANSI(),
+                                     false);
+    pNewDict->SetNewFor<CPDF_String>("UF", wsStr.AsStringView());
+    pMainDict->SetFor("F", pNewDict);
   }
 
-  CPDF_Array* pFields = pMainDict->SetNewFor<CPDF_Array>("Fields");
-  size_t nCount = m_pFieldTree->m_Root.CountFields();
+  auto pFields = pMainDict->SetNewFor<CPDF_Array>("Fields");
+  CFieldTree::Node* pRoot = m_pFieldTree->GetRoot();
+  const size_t nCount = pRoot->CountFields();
   for (size_t i = 0; i < nCount; ++i) {
-    CPDF_FormField* pField = m_pFieldTree->m_Root.GetFieldAtIndex(i);
+    CPDF_FormField* pField = pRoot->GetFieldAtIndex(i);
     if (!pField || pField->GetType() == CPDF_FormField::kPushButton)
       continue;
 
     uint32_t dwFlags = pField->GetFieldFlags();
-    if (dwFlags & 0x04)
+    if (dwFlags & pdfium::form_flags::kNoExport)
       continue;
 
-    if (bIncludeOrExclude != pdfium::ContainsValue(fields, pField))
+    if (bIncludeOrExclude != pdfium::Contains(fields, pField))
       continue;
 
-    if ((dwFlags & 0x02) != 0 &&
-        pField->GetDict()->GetStringFor(pdfium::form_fields::kV).IsEmpty()) {
+    if ((dwFlags & pdfium::form_flags::kRequired) != 0 &&
+        pField->GetFieldDict()
+            ->GetByteStringFor(pdfium::form_fields::kV)
+            .IsEmpty()) {
       continue;
     }
 
     WideString fullname =
         CPDF_FormField::GetFullNameForDict(pField->GetFieldDict());
     auto pFieldDict = pDoc->New<CPDF_Dictionary>();
-    pFieldDict->SetNewFor<CPDF_String>(pdfium::form_fields::kT, fullname);
+    pFieldDict->SetNewFor<CPDF_String>(pdfium::form_fields::kT,
+                                       fullname.AsStringView());
     if (pField->GetType() == CPDF_FormField::kCheckBox ||
         pField->GetType() == CPDF_FormField::kRadioButton) {
       WideString csExport = pField->GetCheckValue(false);
-      ByteString csBExport = PDF_EncodeText(csExport);
-      CPDF_Object* pOpt =
-          CPDF_FormField::GetFieldAttr(pField->GetDict(), "Opt");
+      ByteString csBExport = PDF_EncodeText(csExport.AsStringView());
+      RetainPtr<const CPDF_Object> pOpt = pField->GetFieldAttr("Opt");
       if (pOpt) {
         pFieldDict->SetNewFor<CPDF_String>(pdfium::form_fields::kV, csBExport,
                                            false);
@@ -1080,12 +988,12 @@
         pFieldDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kV, csBExport);
       }
     } else {
-      CPDF_Object* pV = CPDF_FormField::GetFieldAttr(pField->GetDict(),
-                                                     pdfium::form_fields::kV);
+      RetainPtr<const CPDF_Object> pV =
+          pField->GetFieldAttr(pdfium::form_fields::kV);
       if (pV)
         pFieldDict->SetFor(pdfium::form_fields::kV, pV->CloneDirectObject());
     }
-    pFields->Add(pFieldDict);
+    pFields->Append(pFieldDict);
   }
   return pDoc;
 }
@@ -1093,3 +1001,31 @@
 void CPDF_InteractiveForm::SetNotifierIface(NotifierIface* pNotify) {
   m_pFormNotify = pNotify;
 }
+
+bool CPDF_InteractiveForm::NotifyBeforeValueChange(CPDF_FormField* pField,
+                                                   const WideString& csValue) {
+  return !m_pFormNotify || m_pFormNotify->BeforeValueChange(pField, csValue);
+}
+
+void CPDF_InteractiveForm::NotifyAfterValueChange(CPDF_FormField* pField) {
+  if (m_pFormNotify)
+    m_pFormNotify->AfterValueChange(pField);
+}
+
+bool CPDF_InteractiveForm::NotifyBeforeSelectionChange(
+    CPDF_FormField* pField,
+    const WideString& csValue) {
+  return !m_pFormNotify ||
+         m_pFormNotify->BeforeSelectionChange(pField, csValue);
+}
+
+void CPDF_InteractiveForm::NotifyAfterSelectionChange(CPDF_FormField* pField) {
+  if (m_pFormNotify)
+    m_pFormNotify->AfterSelectionChange(pField);
+}
+
+void CPDF_InteractiveForm::NotifyAfterCheckedStatusChange(
+    CPDF_FormField* pField) {
+  if (m_pFormNotify)
+    m_pFormNotify->AfterCheckedStatusChange(pField);
+}
diff --git a/core/fpdfdoc/cpdf_interactiveform.h b/core/fpdfdoc/cpdf_interactiveform.h
index 7d1f6c2..3b6e4f1 100644
--- a/core/fpdfdoc/cpdf_interactiveform.h
+++ b/core/fpdfdoc/cpdf_interactiveform.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,31 +7,31 @@
 #ifndef CORE_FPDFDOC_CPDF_INTERACTIVEFORM_H_
 #define CORE_FPDFDOC_CPDF_INTERACTIVEFORM_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
+#include <functional>
 #include <map>
 #include <memory>
 #include <vector>
 
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fpdfdoc/cpdf_defaultappearance.h"
 #include "core/fpdfdoc/cpdf_formfield.h"
+#include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/containers/span.h"
 
 class CFieldTree;
 class CFDF_Document;
 class CPDF_Document;
-class CPDF_Dictionary;
 class CPDF_Font;
 class CPDF_FormControl;
-class CPDF_Object;
 class CPDF_Page;
 
-RetainPtr<CPDF_Font> AddNativeInteractiveFormFont(CPDF_Dictionary*& pFormDict,
-                                                  CPDF_Document* pDocument,
-                                                  ByteString* csNameTag);
-
 class CPDF_InteractiveForm {
  public:
   class NotifierIface {
@@ -51,23 +51,19 @@
   explicit CPDF_InteractiveForm(CPDF_Document* pDocument);
   ~CPDF_InteractiveForm();
 
-  static void SetUpdateAP(bool bUpdateAP);
   static bool IsUpdateAPEnabled();
-  static uint8_t GetNativeCharSet();
-  static ByteString GetNativeFontName(uint8_t iCharSet, void* pLogFont);
-  static RetainPtr<CPDF_Font> AddStandardFont(CPDF_Document* pDocument,
-                                              ByteString csFontName);
-  static RetainPtr<CPDF_Font> AddNativeFont(uint8_t iCharSet,
-                                            CPDF_Document* pDocument);
-  static RetainPtr<CPDF_Font> AddNativeFont(CPDF_Document* pDocument);
+  static void SetUpdateAP(bool bUpdateAP);
+  static RetainPtr<CPDF_Font> AddNativeInteractiveFormFont(
+      CPDF_Document* pDocument,
+      ByteString* csNameTag);
 
   size_t CountFields(const WideString& csFieldName) const;
-  CPDF_FormField* GetField(uint32_t index, const WideString& csFieldName) const;
-  CPDF_FormField* GetFieldByDict(CPDF_Dictionary* pFieldDict) const;
+  CPDF_FormField* GetField(size_t index, const WideString& csFieldName) const;
+  CPDF_FormField* GetFieldByDict(const CPDF_Dictionary* pFieldDict) const;
 
-  CPDF_FormControl* GetControlAtPoint(CPDF_Page* pPage,
-                                      const CFX_PointF& point,
-                                      int* z_order) const;
+  const CPDF_FormControl* GetControlAtPoint(const CPDF_Page* pPage,
+                                            const CFX_PointF& point,
+                                            int* z_order) const;
   CPDF_FormControl* GetControlByDict(const CPDF_Dictionary* pWidgetDict) const;
 
   bool NeedConstructAP() const;
@@ -76,44 +72,42 @@
   int FindFieldInCalculationOrder(const CPDF_FormField* pField);
 
   RetainPtr<CPDF_Font> GetFormFont(ByteString csNameTag) const;
+  RetainPtr<CPDF_Font> GetFontForElement(
+      RetainPtr<CPDF_Dictionary> pElement) const;
   CPDF_DefaultAppearance GetDefaultAppearance() const;
   int GetFormAlignment() const;
-
   bool CheckRequiredFields(const std::vector<CPDF_FormField*>* fields,
                            bool bIncludeOrExclude) const;
 
-  std::unique_ptr<CFDF_Document> ExportToFDF(const WideString& pdf_path,
-                                             bool bSimpleFileSpec) const;
-
+  std::unique_ptr<CFDF_Document> ExportToFDF(const WideString& pdf_path) const;
   std::unique_ptr<CFDF_Document> ExportToFDF(
       const WideString& pdf_path,
       const std::vector<CPDF_FormField*>& fields,
-      bool bIncludeOrExclude,
-      bool bSimpleFileSpec) const;
+      bool bIncludeOrExclude) const;
 
-  void ResetForm(NotificationOption notify);
-
-  // TODO(tsepez): Use a span.
-  void ResetForm(const std::vector<CPDF_FormField*>& fields,
-                 bool bIncludeOrExclude,
-                 NotificationOption notify);
+  void ResetForm();
+  void ResetForm(pdfium::span<CPDF_FormField*> fields, bool bIncludeOrExclude);
 
   void SetNotifierIface(NotifierIface* pNotify);
-  bool HasXFAForm() const;
   void FixPageFields(CPDF_Page* pPage);
 
-  NotifierIface* GetFormNotify() const { return m_pFormNotify.Get(); }
-  CPDF_Document* GetDocument() const { return m_pDocument.Get(); }
-  CPDF_Dictionary* GetFormDict() const { return m_pFormDict.Get(); }
+  // Wrap callbacks thru NotifierIface.
+  bool NotifyBeforeValueChange(CPDF_FormField* pField,
+                               const WideString& csValue);
+  void NotifyAfterValueChange(CPDF_FormField* pField);
+  bool NotifyBeforeSelectionChange(CPDF_FormField* pField,
+                                   const WideString& csValue);
+  void NotifyAfterSelectionChange(CPDF_FormField* pField);
+  void NotifyAfterCheckedStatusChange(CPDF_FormField* pField);
 
   const std::vector<UnownedPtr<CPDF_FormControl>>& GetControlsForField(
       const CPDF_FormField* pField);
 
  private:
-  void LoadField(CPDF_Dictionary* pFieldDict, int nLevel);
-  void AddTerminalField(CPDF_Dictionary* pFieldDict);
+  void LoadField(RetainPtr<CPDF_Dictionary> pFieldDict, int nLevel);
+  void AddTerminalField(RetainPtr<CPDF_Dictionary> pFieldDict);
   CPDF_FormControl* AddControl(CPDF_FormField* pField,
-                               CPDF_Dictionary* pWidgetDict);
+                               RetainPtr<CPDF_Dictionary> pWidgetDict);
 
   static bool s_bUpdateAP;
 
@@ -121,10 +115,14 @@
   UnownedPtr<CPDF_Document> const m_pDocument;
   RetainPtr<CPDF_Dictionary> m_pFormDict;
   std::unique_ptr<CFieldTree> m_pFieldTree;
-  std::map<const CPDF_Dictionary*, std::unique_ptr<CPDF_FormControl>>
+  std::map<RetainPtr<const CPDF_Dictionary>,
+           std::unique_ptr<CPDF_FormControl>,
+           std::less<>>
       m_ControlMap;
   // Points into |m_ControlMap|.
-  std::map<const CPDF_FormField*, std::vector<UnownedPtr<CPDF_FormControl>>>
+  std::map<UnownedPtr<const CPDF_FormField>,
+           std::vector<UnownedPtr<CPDF_FormControl>>,
+           std::less<>>
       m_ControlLists;
   UnownedPtr<NotifierIface> m_pFormNotify;
 };
diff --git a/core/fpdfdoc/cpdf_link.cpp b/core/fpdfdoc/cpdf_link.cpp
index d5f24b1..d68585e 100644
--- a/core/fpdfdoc/cpdf_link.cpp
+++ b/core/fpdfdoc/cpdf_link.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,13 +6,14 @@
 
 #include "core/fpdfdoc/cpdf_link.h"
 
-#include "core/fpdfapi/parser/cpdf_array.h"
+#include <utility>
+
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfdoc/cpdf_nametree.h"
 
 CPDF_Link::CPDF_Link() = default;
 
-CPDF_Link::CPDF_Link(CPDF_Dictionary* pDict) : m_pDict(pDict) {}
+CPDF_Link::CPDF_Link(RetainPtr<CPDF_Dictionary> pDict)
+    : m_pDict(std::move(pDict)) {}
 
 CPDF_Link::CPDF_Link(const CPDF_Link& that) = default;
 
@@ -23,17 +24,7 @@
 }
 
 CPDF_Dest CPDF_Link::GetDest(CPDF_Document* pDoc) {
-  CPDF_Object* pDest = m_pDict->GetDirectObjectFor("Dest");
-  if (!pDest)
-    return CPDF_Dest();
-
-  if (pDest->IsString() || pDest->IsName()) {
-    CPDF_NameTree name_tree(pDoc, "Dests");
-    return CPDF_Dest(name_tree.LookupNamedDest(pDoc, pDest->GetUnicodeText()));
-  }
-  if (CPDF_Array* pArray = pDest->AsArray())
-    return CPDF_Dest(pArray);
-  return CPDF_Dest();
+  return CPDF_Dest::Create(pDoc, m_pDict->GetDirectObjectFor("Dest"));
 }
 
 CPDF_Action CPDF_Link::GetAction() {
diff --git a/core/fpdfdoc/cpdf_link.h b/core/fpdfdoc/cpdf_link.h
index aaa6e8e..2432fd6 100644
--- a/core/fpdfdoc/cpdf_link.h
+++ b/core/fpdfdoc/cpdf_link.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,21 +7,20 @@
 #ifndef CORE_FPDFDOC_CPDF_LINK_H_
 #define CORE_FPDFDOC_CPDF_LINK_H_
 
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfdoc/cpdf_action.h"
 #include "core/fpdfdoc/cpdf_dest.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/retain_ptr.h"
 
-class CPDF_Dictionary;
-
 class CPDF_Link {
  public:
   CPDF_Link();
-  explicit CPDF_Link(CPDF_Dictionary* pDict);
+  explicit CPDF_Link(RetainPtr<CPDF_Dictionary> pDict);
   CPDF_Link(const CPDF_Link& that);
   ~CPDF_Link();
 
-  CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
+  RetainPtr<CPDF_Dictionary> GetMutableDict() const { return m_pDict; }
   CFX_FloatRect GetRect();
   CPDF_Dest GetDest(CPDF_Document* pDoc);
   CPDF_Action GetAction();
diff --git a/core/fpdfdoc/cpdf_linklist.cpp b/core/fpdfdoc/cpdf_linklist.cpp
index 746bbab..cd4e202 100644
--- a/core/fpdfdoc/cpdf_linklist.cpp
+++ b/core/fpdfdoc/cpdf_linklist.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,15 +6,43 @@
 
 #include "core/fpdfdoc/cpdf_linklist.h"
 
+#include <utility>
+
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 CPDF_LinkList::CPDF_LinkList() = default;
 
 CPDF_LinkList::~CPDF_LinkList() = default;
 
-const std::vector<CPDF_Dictionary*>* CPDF_LinkList::GetPageLinks(
+CPDF_Link CPDF_LinkList::GetLinkAtPoint(CPDF_Page* pPage,
+                                        const CFX_PointF& point,
+                                        int* z_order) {
+  const std::vector<RetainPtr<CPDF_Dictionary>>* pPageLinkList =
+      GetPageLinks(pPage);
+  if (!pPageLinkList)
+    return CPDF_Link();
+
+  for (size_t i = pPageLinkList->size(); i > 0; --i) {
+    size_t annot_index = i - 1;
+    RetainPtr<CPDF_Dictionary> pAnnot = (*pPageLinkList)[annot_index];
+    if (!pAnnot)
+      continue;
+
+    CPDF_Link link(std::move(pAnnot));
+    if (!link.GetRect().Contains(point))
+      continue;
+
+    if (z_order)
+      *z_order = pdfium::base::checked_cast<int32_t>(annot_index);
+    return link;
+  }
+  return CPDF_Link();
+}
+
+const std::vector<RetainPtr<CPDF_Dictionary>>* CPDF_LinkList::GetPageLinks(
     CPDF_Page* pPage) {
   uint32_t objnum = pPage->GetDict()->GetObjNum();
   if (objnum == 0)
@@ -25,45 +53,16 @@
     return &it->second;
 
   // std::map::operator[] forces the creation of a map entry.
-  std::vector<CPDF_Dictionary*>& page_link_list = m_PageMap[objnum];
-  LoadPageLinks(pPage, &page_link_list);
-  return &page_link_list;
-}
-
-CPDF_Link CPDF_LinkList::GetLinkAtPoint(CPDF_Page* pPage,
-                                        const CFX_PointF& point,
-                                        int* z_order) {
-  const std::vector<CPDF_Dictionary*>* pPageLinkList = GetPageLinks(pPage);
-  if (!pPageLinkList)
-    return CPDF_Link();
-
-  for (size_t i = pPageLinkList->size(); i > 0; --i) {
-    size_t annot_index = i - 1;
-    CPDF_Dictionary* pAnnot = (*pPageLinkList)[annot_index];
-    if (!pAnnot)
-      continue;
-
-    CPDF_Link link(pAnnot);
-    if (!link.GetRect().Contains(point))
-      continue;
-
-    if (z_order)
-      *z_order = annot_index;
-    return link;
-  }
-  return CPDF_Link();
-}
-
-void CPDF_LinkList::LoadPageLinks(CPDF_Page* pPage,
-                                  std::vector<CPDF_Dictionary*>* pList) {
-  CPDF_Array* pAnnotList = pPage->GetDict()->GetArrayFor("Annots");
+  auto* page_link_list = &m_PageMap[objnum];
+  RetainPtr<CPDF_Array> pAnnotList = pPage->GetMutableAnnotsArray();
   if (!pAnnotList)
-    return;
+    return page_link_list;
 
   for (size_t i = 0; i < pAnnotList->size(); ++i) {
-    CPDF_Dictionary* pAnnot = pAnnotList->GetDictAt(i);
-    bool add_link = (pAnnot && pAnnot->GetStringFor("Subtype") == "Link");
+    RetainPtr<CPDF_Dictionary> pAnnot = pAnnotList->GetMutableDictAt(i);
+    bool add_link = (pAnnot && pAnnot->GetByteStringFor("Subtype") == "Link");
     // Add non-links as nullptrs to preserve z-order.
-    pList->push_back(add_link ? pAnnot : nullptr);
+    page_link_list->emplace_back(add_link ? pAnnot : nullptr);
   }
+  return page_link_list;
 }
diff --git a/core/fpdfdoc/cpdf_linklist.h b/core/fpdfdoc/cpdf_linklist.h
index 442f66c..d264718 100644
--- a/core/fpdfdoc/cpdf_linklist.h
+++ b/core/fpdfdoc/cpdf_linklist.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,17 +7,19 @@
 #ifndef CORE_FPDFDOC_CPDF_LINKLIST_H_
 #define CORE_FPDFDOC_CPDF_LINKLIST_H_
 
+#include <stdint.h>
+
 #include <map>
 #include <vector>
 
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfdoc/cpdf_link.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Page;
 class CPDF_Dictionary;
 
-class CPDF_LinkList : public CPDF_Document::LinkListIface {
+class CPDF_LinkList final : public CPDF_Document::LinkListIface {
  public:
   CPDF_LinkList();
   ~CPDF_LinkList() override;
@@ -27,10 +29,9 @@
                            int* z_order);
 
  private:
-  const std::vector<CPDF_Dictionary*>* GetPageLinks(CPDF_Page* pPage);
-  void LoadPageLinks(CPDF_Page* pPage, std::vector<CPDF_Dictionary*>* pList);
+  const std::vector<RetainPtr<CPDF_Dictionary>>* GetPageLinks(CPDF_Page* pPage);
 
-  std::map<uint32_t, std::vector<CPDF_Dictionary*>> m_PageMap;
+  std::map<uint32_t, std::vector<RetainPtr<CPDF_Dictionary>>> m_PageMap;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_LINKLIST_H_
diff --git a/core/fpdfdoc/cpdf_metadata.cpp b/core/fpdfdoc/cpdf_metadata.cpp
index da8dcb9..31564f4 100644
--- a/core/fpdfdoc/cpdf_metadata.cpp
+++ b/core/fpdfdoc/cpdf_metadata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,28 @@
 #include "core/fpdfdoc/cpdf_metadata.h"
 
 #include <memory>
+#include <utility>
 
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/xml/cfx_xmldocument.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlparser.h"
+#include "third_party/base/check.h"
 
 namespace {
 
-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/")) {
@@ -52,31 +61,35 @@
 
   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
 
-CPDF_Metadata::CPDF_Metadata(const CPDF_Stream* pStream) : stream_(pStream) {
-  ASSERT(pStream);
+CPDF_Metadata::CPDF_Metadata(RetainPtr<const CPDF_Stream> pStream)
+    : stream_(std::move(pStream)) {
+  DCHECK(stream_);
 }
 
 CPDF_Metadata::~CPDF_Metadata() = default;
 
 std::vector<UnsupportedFeature> CPDF_Metadata::CheckForSharedForm() const {
-  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(stream_.Get());
+  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(stream_);
   pAcc->LoadAllDataFiltered();
 
-  auto stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(pAcc->GetSpan());
+  auto stream = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pAcc->GetSpan());
   CFX_XMLParser parser(stream);
   std::unique_ptr<CFX_XMLDocument> doc = parser.Parse();
   if (!doc)
     return {};
 
   std::vector<UnsupportedFeature> unsupported;
-  CheckForSharedFormInternal(doc->GetRoot(), &unsupported);
+  CheckForSharedFormInternal(/*depth=*/0, doc->GetRoot(), &unsupported);
   return unsupported;
 }
diff --git a/core/fpdfdoc/cpdf_metadata.h b/core/fpdfdoc/cpdf_metadata.h
index 554492a..6b63e22 100644
--- a/core/fpdfdoc/cpdf_metadata.h
+++ b/core/fpdfdoc/cpdf_metadata.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -34,7 +34,7 @@
 
 class CPDF_Metadata {
  public:
-  explicit CPDF_Metadata(const CPDF_Stream* pStream);
+  explicit CPDF_Metadata(RetainPtr<const CPDF_Stream> pStream);
   ~CPDF_Metadata();
 
   std::vector<UnsupportedFeature> CheckForSharedForm() const;
diff --git a/core/fpdfdoc/cpdf_metadata_unittest.cpp b/core/fpdfdoc/cpdf_metadata_unittest.cpp
index 41f4b5d..4bd30ed 100644
--- a/core/fpdfdoc/cpdf_metadata_unittest.cpp
+++ b/core/fpdfdoc/cpdf_metadata_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,7 +17,7 @@
 
   auto stream = pdfium::MakeRetain<CPDF_Stream>();
   stream->SetData(ByteStringView(data).raw_span());
-  CPDF_Metadata metadata(stream.Get());
+  CPDF_Metadata metadata(stream);
 
   auto results = metadata.CheckForSharedForm();
   ASSERT_EQ(1U, results.size());
@@ -34,7 +34,7 @@
 
   auto stream = pdfium::MakeRetain<CPDF_Stream>();
   stream->SetData(ByteStringView(data).raw_span());
-  CPDF_Metadata metadata(stream.Get());
+  CPDF_Metadata metadata(stream);
 
   auto results = metadata.CheckForSharedForm();
   ASSERT_EQ(1U, results.size());
@@ -51,7 +51,7 @@
 
   auto stream = pdfium::MakeRetain<CPDF_Stream>();
   stream->SetData(ByteStringView(data).raw_span());
-  CPDF_Metadata metadata(stream.Get());
+  CPDF_Metadata metadata(stream);
 
   auto results = metadata.CheckForSharedForm();
   ASSERT_EQ(1U, results.size());
@@ -68,7 +68,7 @@
 
   auto stream = pdfium::MakeRetain<CPDF_Stream>();
   stream->SetData(ByteStringView(data).raw_span());
-  CPDF_Metadata metadata(stream.Get());
+  CPDF_Metadata metadata(stream);
 
   auto results = metadata.CheckForSharedForm();
   EXPECT_EQ(0U, results.size());
@@ -86,7 +86,7 @@
 
   auto stream = pdfium::MakeRetain<CPDF_Stream>();
   stream->SetData(ByteStringView(data).raw_span());
-  CPDF_Metadata metadata(stream.Get());
+  CPDF_Metadata metadata(stream);
 
   auto results = metadata.CheckForSharedForm();
   ASSERT_EQ(1U, results.size());
@@ -100,12 +100,45 @@
 
   auto stream = pdfium::MakeRetain<CPDF_Stream>();
   stream->SetData(ByteStringView(data).raw_span());
-  CPDF_Metadata metadata(stream.Get());
+  CPDF_Metadata metadata(stream);
 
   auto results = metadata.CheckForSharedForm();
   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"
@@ -116,7 +149,7 @@
 
   auto stream = pdfium::MakeRetain<CPDF_Stream>();
   stream->SetData(ByteStringView(data).raw_span());
-  CPDF_Metadata metadata(stream.Get());
+  CPDF_Metadata metadata(stream);
 
   auto results = metadata.CheckForSharedForm();
   EXPECT_EQ(0U, results.size());
@@ -146,7 +179,7 @@
 
   auto stream = pdfium::MakeRetain<CPDF_Stream>();
   stream->SetData(ByteStringView(data).raw_span());
-  CPDF_Metadata metadata(stream.Get());
+  CPDF_Metadata metadata(stream);
 
   auto results = metadata.CheckForSharedForm();
   ASSERT_EQ(3U, results.size());
diff --git a/core/fpdfdoc/cpdf_nametree.cpp b/core/fpdfdoc/cpdf_nametree.cpp
index b232540..95d540f 100644
--- a/core/fpdfdoc/cpdf_nametree.cpp
+++ b/core/fpdfdoc/cpdf_nametree.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,65 +6,83 @@
 
 #include "core/fpdfdoc/cpdf_nametree.h"
 
+#include <set>
 #include <utility>
 #include <vector>
 
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/memory/ptr_util.h"
 
 namespace {
 
 constexpr int kNameTreeMaxRecursion = 32;
 
-std::pair<WideString, WideString> GetNodeLimitsMaybeSwap(CPDF_Array* pLimits) {
-  ASSERT(pLimits);
+std::pair<WideString, WideString> GetNodeLimitsAndSanitize(
+    CPDF_Array* pLimits) {
+  DCHECK(pLimits);
   WideString csLeft = pLimits->GetUnicodeTextAt(0);
   WideString csRight = pLimits->GetUnicodeTextAt(1);
   // If the lower limit is greater than the upper limit, swap them.
   if (csLeft.Compare(csRight) > 0) {
-    pLimits->SetNewAt<CPDF_String>(0, csRight);
-    pLimits->SetNewAt<CPDF_String>(1, csLeft);
+    pLimits->SetNewAt<CPDF_String>(0, csRight.AsStringView());
+    pLimits->SetNewAt<CPDF_String>(1, csLeft.AsStringView());
     csLeft = pLimits->GetUnicodeTextAt(0);
     csRight = pLimits->GetUnicodeTextAt(1);
   }
+  while (pLimits->size() > 2)
+    pLimits->RemoveAt(pLimits->size() - 1);
   return {csLeft, csRight};
 }
 
 // Get the limit arrays that leaf array |pFind| is under in the tree with root
 // |pNode|. |pLimits| will hold all the limit arrays from the leaf up to before
 // the root. Return true if successful.
-bool GetNodeAncestorsLimits(CPDF_Dictionary* pNode,
-                            const CPDF_Array* pFind,
-                            int nLevel,
-                            std::vector<CPDF_Array*>* pLimits) {
+bool GetNodeAncestorsLimitsInternal(const RetainPtr<CPDF_Dictionary>& pNode,
+                                    const CPDF_Array* pFind,
+                                    int nLevel,
+                                    std::vector<CPDF_Array*>* pLimits) {
   if (nLevel > kNameTreeMaxRecursion)
     return false;
 
   if (pNode->GetArrayFor("Names") == pFind) {
-    pLimits->push_back(pNode->GetArrayFor("Limits"));
+    pLimits->push_back(pNode->GetMutableArrayFor("Limits").Get());
     return true;
   }
 
-  CPDF_Array* pKids = pNode->GetArrayFor("Kids");
+  RetainPtr<CPDF_Array> pKids = pNode->GetMutableArrayFor("Kids");
   if (!pKids)
     return false;
 
   for (size_t i = 0; i < pKids->size(); ++i) {
-    CPDF_Dictionary* pKid = pKids->GetDictAt(i);
+    RetainPtr<CPDF_Dictionary> pKid = pKids->GetMutableDictAt(i);
     if (!pKid)
       continue;
 
-    if (GetNodeAncestorsLimits(pKid, pFind, nLevel + 1, pLimits)) {
-      pLimits->push_back(pNode->GetArrayFor("Limits"));
+    if (GetNodeAncestorsLimitsInternal(pKid, pFind, nLevel + 1, pLimits)) {
+      pLimits->push_back(pNode->GetMutableArrayFor("Limits").Get());
       return true;
     }
   }
   return false;
 }
 
+// Wrapper for GetNodeAncestorsLimitsInternal() so callers do not need to know
+// about the details.
+std::vector<CPDF_Array*> GetNodeAncestorsLimits(
+    const RetainPtr<CPDF_Dictionary>& pNode,
+    const CPDF_Array* pFind) {
+  std::vector<CPDF_Array*> results;
+  GetNodeAncestorsLimitsInternal(pNode, pFind, 0, &results);
+  return results;
+}
+
 // Upon the deletion of |csName| from leaf array |pFind|, update the ancestors
 // of |pFind|. Specifically, the limits of |pFind|'s ancestors will be updated
 // if needed, and any ancestors that are now empty will be removed.
@@ -75,13 +93,13 @@
   if (nLevel > kNameTreeMaxRecursion)
     return false;
 
-  CPDF_Array* pLimits = pNode->GetArrayFor("Limits");
+  RetainPtr<CPDF_Array> pLimits = pNode->GetMutableArrayFor("Limits");
   WideString csLeft;
   WideString csRight;
   if (pLimits)
-    std::tie(csLeft, csRight) = GetNodeLimitsMaybeSwap(pLimits);
+    std::tie(csLeft, csRight) = GetNodeLimitsAndSanitize(pLimits.Get());
 
-  CPDF_Array* pNames = pNode->GetArrayFor("Names");
+  RetainPtr<const CPDF_Array> pNames = pNode->GetArrayFor("Names");
   if (pNames) {
     if (pNames != pFind)
       return false;
@@ -101,23 +119,24 @@
       if (wsName.Compare(csNewRight) > 0)
         csNewRight = wsName;
     }
-    pLimits->SetNewAt<CPDF_String>(0, csNewLeft);
-    pLimits->SetNewAt<CPDF_String>(1, csNewRight);
+    pLimits->SetNewAt<CPDF_String>(0, csNewLeft.AsStringView());
+    pLimits->SetNewAt<CPDF_String>(1, csNewRight.AsStringView());
     return true;
   }
 
-  CPDF_Array* pKids = pNode->GetArrayFor("Kids");
+  RetainPtr<CPDF_Array> pKids = pNode->GetMutableArrayFor("Kids");
   if (!pKids)
     return false;
 
   // Loop through the kids to find the leaf array |pFind|.
   for (size_t i = 0; i < pKids->size(); ++i) {
-    CPDF_Dictionary* pKid = pKids->GetDictAt(i);
+    RetainPtr<CPDF_Dictionary> pKid = pKids->GetMutableDictAt(i);
     if (!pKid)
       continue;
-    if (!UpdateNodesAndLimitsUponDeletion(pKid, pFind, csName, nLevel + 1))
+    if (!UpdateNodesAndLimitsUponDeletion(pKid.Get(), pFind, csName,
+                                          nLevel + 1)) {
       continue;
-
+    }
     // Remove this child node if it's empty.
     if ((pKid->KeyExist("Names") && pKid->GetArrayFor("Names")->IsEmpty()) ||
         (pKid->KeyExist("Kids") && pKid->GetArrayFor("Kids")->IsEmpty())) {
@@ -133,41 +152,72 @@
     WideString csNewLeft = csRight;
     WideString csNewRight = csLeft;
     for (size_t j = 0; j < pKids->size(); ++j) {
-      CPDF_Array* pKidLimits = pKids->GetDictAt(j)->GetArrayFor("Limits");
-      ASSERT(pKidLimits);
+      RetainPtr<const CPDF_Array> pKidLimits =
+          pKids->GetDictAt(j)->GetArrayFor("Limits");
+      DCHECK(pKidLimits);
       if (pKidLimits->GetUnicodeTextAt(0).Compare(csNewLeft) < 0)
         csNewLeft = pKidLimits->GetUnicodeTextAt(0);
       if (pKidLimits->GetUnicodeTextAt(1).Compare(csNewRight) > 0)
         csNewRight = pKidLimits->GetUnicodeTextAt(1);
     }
-    pLimits->SetNewAt<CPDF_String>(0, csNewLeft);
-    pLimits->SetNewAt<CPDF_String>(1, csNewRight);
+    pLimits->SetNewAt<CPDF_String>(0, csNewLeft.AsStringView());
+    pLimits->SetNewAt<CPDF_String>(1, csNewRight.AsStringView());
     return true;
   }
   return false;
 }
 
+bool IsTraversedObject(const CPDF_Object* obj,
+                       std::set<uint32_t>* seen_obj_nums) {
+  uint32_t obj_num = obj->GetObjNum();
+  if (!obj_num)
+    return false;
+
+  bool inserted = seen_obj_nums->insert(obj_num).second;
+  return !inserted;
+}
+
+bool IsArrayWithTraversedObject(const CPDF_Array* array,
+                                std::set<uint32_t>* seen_obj_nums) {
+  if (IsTraversedObject(array, seen_obj_nums))
+    return true;
+
+  CPDF_ArrayLocker locker(array);
+  for (const auto& item : locker) {
+    if (IsTraversedObject(item.Get(), seen_obj_nums))
+      return true;
+  }
+  return false;
+}
+
 // Search for |csName| in the tree with root |pNode|. If successful, return the
 // value that |csName| points to; |nIndex| will be the index of |csName|,
 // |ppFind| will be the leaf array that |csName| is found in, and |pFindIndex|
 // will be the index of |csName| in |ppFind|. If |csName| is not found, |ppFind|
 // will be the leaf array that |csName| should be added to, and |pFindIndex|
 // will be the index that it should be added at.
-CPDF_Object* SearchNameNodeByName(CPDF_Dictionary* pNode,
-                                  const WideString& csName,
-                                  int nLevel,
-                                  size_t* nIndex,
-                                  CPDF_Array** ppFind,
-                                  int* pFindIndex) {
+RetainPtr<const CPDF_Object> SearchNameNodeByNameInternal(
+    const RetainPtr<CPDF_Dictionary>& pNode,
+    const WideString& csName,
+    int nLevel,
+    size_t* nIndex,
+    RetainPtr<CPDF_Array>* ppFind,
+    int* pFindIndex,
+    std::set<uint32_t>* seen_obj_nums) {
   if (nLevel > kNameTreeMaxRecursion)
     return nullptr;
 
-  CPDF_Array* pLimits = pNode->GetArrayFor("Limits");
-  CPDF_Array* pNames = pNode->GetArrayFor("Names");
+  RetainPtr<CPDF_Array> pLimits = pNode->GetMutableArrayFor("Limits");
+  RetainPtr<CPDF_Array> pNames = pNode->GetMutableArrayFor("Names");
+  if (pNames && IsArrayWithTraversedObject(pNames.Get(), seen_obj_nums))
+    pNames.Reset();
+  if (pLimits && IsArrayWithTraversedObject(pLimits.Get(), seen_obj_nums))
+    pLimits.Reset();
+
   if (pLimits) {
     WideString csLeft;
     WideString csRight;
-    std::tie(csLeft, csRight) = GetNodeLimitsMaybeSwap(pLimits);
+    std::tie(csLeft, csRight) = GetNodeLimitsAndSanitize(pLimits.Get());
     // Skip this node if the name to look for is smaller than its lower limit.
     if (csName.Compare(csLeft) < 0)
       return nullptr;
@@ -178,8 +228,7 @@
       if (ppFind)
         *ppFind = pNames;
       if (pFindIndex)
-        *pFindIndex = pNames->size() / 2 - 1;
-
+        *pFindIndex = fxcrt::CollectionSize<int32_t>(*pNames) / 2 - 1;
       return nullptr;
     }
   }
@@ -195,7 +244,7 @@
       if (ppFind)
         *ppFind = pNames;
       if (pFindIndex)
-        *pFindIndex = i;
+        *pFindIndex = pdfium::base::checked_cast<int32_t>(i);
       if (iCompare < 0)
         continue;
 
@@ -207,127 +256,248 @@
   }
 
   // Search through the node's children.
-  CPDF_Array* pKids = pNode->GetArrayFor("Kids");
-  if (!pKids)
+  RetainPtr<CPDF_Array> pKids = pNode->GetMutableArrayFor("Kids");
+  if (!pKids || IsTraversedObject(pKids.Get(), seen_obj_nums))
     return nullptr;
 
   for (size_t i = 0; i < pKids->size(); i++) {
-    CPDF_Dictionary* pKid = pKids->GetDictAt(i);
-    if (!pKid)
+    RetainPtr<CPDF_Dictionary> pKid = pKids->GetMutableDictAt(i);
+    if (!pKid || IsTraversedObject(pKid.Get(), seen_obj_nums))
       continue;
 
-    CPDF_Object* pFound = SearchNameNodeByName(pKid, csName, nLevel + 1, nIndex,
-                                               ppFind, pFindIndex);
+    RetainPtr<const CPDF_Object> pFound = SearchNameNodeByNameInternal(
+        pKid, csName, nLevel + 1, nIndex, ppFind, pFindIndex, seen_obj_nums);
     if (pFound)
       return pFound;
   }
   return nullptr;
 }
 
-// Get the key-value pair at |nIndex| in the tree with root |pNode|. If
-// successful, return the value object; |csName| will be the key, |ppFind|
-// will be the leaf array that this pair is in, and |pFindIndex| will be the
-// index of the pair in |pFind|.
-CPDF_Object* SearchNameNodeByIndex(CPDF_Dictionary* pNode,
-                                   size_t nIndex,
-                                   int nLevel,
-                                   size_t* nCurIndex,
-                                   WideString* csName,
-                                   CPDF_Array** ppFind,
-                                   int* pFindIndex) {
-  if (nLevel > kNameTreeMaxRecursion)
-    return nullptr;
+// Wrapper for SearchNameNodeByNameInternal() so callers do not need to know
+// about the details.
+RetainPtr<const CPDF_Object> SearchNameNodeByName(
+    const RetainPtr<CPDF_Dictionary>& pNode,
+    const WideString& csName,
+    RetainPtr<CPDF_Array>* ppFind,
+    int* pFindIndex) {
+  size_t nIndex = 0;
+  std::set<uint32_t> seen_obj_nums;
+  return SearchNameNodeByNameInternal(pNode, csName, 0, &nIndex, ppFind,
+                                      pFindIndex, &seen_obj_nums);
+}
 
-  CPDF_Array* pNames = pNode->GetArrayFor("Names");
+struct IndexSearchResult {
+  // For the n-th object in a tree, the key and value.
+  WideString key;
+  RetainPtr<CPDF_Object> value;
+  // The leaf node that holds `key` and `value`.
+  RetainPtr<CPDF_Array> container;
+  // The index for `key` in `container`. Must be even.
+  size_t index;
+};
+
+// Find the `nTargetPairIndex` node in the tree with root `pNode`. `nLevel`
+// tracks the recursion level and `nCurPairIndex` tracks the progress towards
+// `nTargetPairIndex`.
+absl::optional<IndexSearchResult> SearchNameNodeByIndexInternal(
+    CPDF_Dictionary* pNode,
+    size_t nTargetPairIndex,
+    int nLevel,
+    size_t* nCurPairIndex) {
+  if (nLevel > kNameTreeMaxRecursion)
+    return absl::nullopt;
+
+  RetainPtr<CPDF_Array> pNames = pNode->GetMutableArrayFor("Names");
   if (pNames) {
     size_t nCount = pNames->size() / 2;
-    if (nIndex >= *nCurIndex + nCount) {
-      *nCurIndex += nCount;
-      return nullptr;
+    if (nTargetPairIndex >= *nCurPairIndex + nCount) {
+      *nCurPairIndex += nCount;
+      return absl::nullopt;
     }
-    if (ppFind)
-      *ppFind = pNames;
-    if (pFindIndex)
-      *pFindIndex = nIndex - *nCurIndex;
 
-    *csName = pNames->GetUnicodeTextAt((nIndex - *nCurIndex) * 2);
-    return pNames->GetDirectObjectAt((nIndex - *nCurIndex) * 2 + 1);
+    size_t index = 2 * (nTargetPairIndex - *nCurPairIndex);
+    RetainPtr<CPDF_Object> value = pNames->GetMutableDirectObjectAt(index + 1);
+    if (!value)
+      return absl::nullopt;
+
+    IndexSearchResult result;
+    result.key = pNames->GetUnicodeTextAt(index);
+    result.value = std::move(value);
+    result.container = std::move(pNames);
+    result.index = index;
+    return result;
   }
 
-  CPDF_Array* pKids = pNode->GetArrayFor("Kids");
+  RetainPtr<CPDF_Array> pKids = pNode->GetMutableArrayFor("Kids");
   if (!pKids)
-    return nullptr;
+    return absl::nullopt;
 
   for (size_t i = 0; i < pKids->size(); i++) {
-    CPDF_Dictionary* pKid = pKids->GetDictAt(i);
+    RetainPtr<CPDF_Dictionary> pKid = pKids->GetMutableDictAt(i);
     if (!pKid)
       continue;
-    CPDF_Object* pFound = SearchNameNodeByIndex(
-        pKid, nIndex, nLevel + 1, nCurIndex, csName, ppFind, pFindIndex);
-    if (pFound)
-      return pFound;
+    absl::optional<IndexSearchResult> result = SearchNameNodeByIndexInternal(
+        pKid.Get(), nTargetPairIndex, nLevel + 1, nCurPairIndex);
+    if (result.has_value())
+      return result;
   }
-  return nullptr;
+  return absl::nullopt;
+}
+
+// Wrapper for SearchNameNodeByIndexInternal() so callers do not need to know
+// about the details.
+absl::optional<IndexSearchResult> SearchNameNodeByIndex(
+    CPDF_Dictionary* pNode,
+    size_t nTargetPairIndex) {
+  size_t nCurPairIndex = 0;
+  return SearchNameNodeByIndexInternal(pNode, nTargetPairIndex, 0,
+                                       &nCurPairIndex);
 }
 
 // Get the total number of key-value pairs in the tree with root |pNode|.
-size_t CountNamesInternal(CPDF_Dictionary* pNode, int nLevel) {
+size_t CountNamesInternal(const CPDF_Dictionary* pNode,
+                          int nLevel,
+                          std::set<const CPDF_Dictionary*>& seen) {
   if (nLevel > kNameTreeMaxRecursion)
     return 0;
 
-  CPDF_Array* pNames = pNode->GetArrayFor("Names");
+  const bool inserted = seen.insert(pNode).second;
+  if (!inserted)
+    return 0;
+
+  RetainPtr<const CPDF_Array> pNames = pNode->GetArrayFor("Names");
   if (pNames)
     return pNames->size() / 2;
 
-  CPDF_Array* pKids = pNode->GetArrayFor("Kids");
+  RetainPtr<const CPDF_Array> pKids = pNode->GetArrayFor("Kids");
   if (!pKids)
     return 0;
 
   size_t nCount = 0;
   for (size_t i = 0; i < pKids->size(); i++) {
-    CPDF_Dictionary* pKid = pKids->GetDictAt(i);
+    RetainPtr<const CPDF_Dictionary> pKid = pKids->GetDictAt(i);
     if (!pKid)
       continue;
 
-    nCount += CountNamesInternal(pKid, nLevel + 1);
+    nCount += CountNamesInternal(pKid.Get(), nLevel + 1, seen);
   }
   return nCount;
 }
 
-}  // namespace
-
-CPDF_NameTree::CPDF_NameTree(CPDF_Dictionary* pRoot) : m_pRoot(pRoot) {}
-
-CPDF_NameTree::CPDF_NameTree(CPDF_Document* pDoc, const ByteString& category) {
-  CPDF_Dictionary* pRoot = pDoc->GetRoot();
-  if (!pRoot)
-    return;
-
-  CPDF_Dictionary* pNames = pRoot->GetDictFor("Names");
-  if (!pNames)
-    return;
-
-  m_pRoot.Reset(pNames->GetDictFor(category));
+RetainPtr<const CPDF_Array> GetNamedDestFromObject(
+    RetainPtr<const CPDF_Object> obj) {
+  RetainPtr<const CPDF_Array> array = ToArray(obj);
+  if (array)
+    return array;
+  RetainPtr<const CPDF_Dictionary> dict = ToDictionary(obj);
+  if (dict)
+    return dict->GetArrayFor("D");
+  return nullptr;
 }
 
-CPDF_NameTree::~CPDF_NameTree() {}
+RetainPtr<const CPDF_Array> LookupOldStyleNamedDest(CPDF_Document* pDoc,
+                                                    const ByteString& name) {
+  RetainPtr<const CPDF_Dictionary> pDests =
+      pDoc->GetRoot()->GetDictFor("Dests");
+  if (!pDests)
+    return nullptr;
+  return GetNamedDestFromObject(pDests->GetDirectObjectFor(name));
+}
+
+}  // namespace
+
+CPDF_NameTree::CPDF_NameTree(RetainPtr<CPDF_Dictionary> pRoot)
+    : m_pRoot(std::move(pRoot)) {
+  DCHECK(m_pRoot);
+}
+
+CPDF_NameTree::~CPDF_NameTree() = default;
+
+// static
+std::unique_ptr<CPDF_NameTree> CPDF_NameTree::Create(
+    CPDF_Document* pDoc,
+    const ByteString& category) {
+  RetainPtr<CPDF_Dictionary> pRoot = pDoc->GetMutableRoot();
+  if (!pRoot)
+    return nullptr;
+
+  RetainPtr<CPDF_Dictionary> pNames = pRoot->GetMutableDictFor("Names");
+  if (!pNames)
+    return nullptr;
+
+  RetainPtr<CPDF_Dictionary> pCategory = pNames->GetMutableDictFor(category);
+  if (!pCategory)
+    return nullptr;
+
+  return pdfium::WrapUnique(
+      new CPDF_NameTree(std::move(pCategory)));  // Private ctor.
+}
+
+// static
+std::unique_ptr<CPDF_NameTree> CPDF_NameTree::CreateWithRootNameArray(
+    CPDF_Document* pDoc,
+    const ByteString& category) {
+  RetainPtr<CPDF_Dictionary> pRoot = pDoc->GetMutableRoot();
+  if (!pRoot)
+    return nullptr;
+
+  // Retrieve the document's Names dictionary; create it if missing.
+  RetainPtr<CPDF_Dictionary> pNames = pRoot->GetMutableDictFor("Names");
+  if (!pNames) {
+    pNames = pDoc->NewIndirect<CPDF_Dictionary>();
+    pRoot->SetNewFor<CPDF_Reference>("Names", pDoc, pNames->GetObjNum());
+  }
+
+  // Create the |category| dictionary if missing.
+  RetainPtr<CPDF_Dictionary> pCategory = pNames->GetMutableDictFor(category);
+  if (!pCategory) {
+    pCategory = pDoc->NewIndirect<CPDF_Dictionary>();
+    pCategory->SetNewFor<CPDF_Array>("Names");
+    pNames->SetNewFor<CPDF_Reference>(category, pDoc, pCategory->GetObjNum());
+  }
+
+  return pdfium::WrapUnique(new CPDF_NameTree(pCategory));  // Private ctor.
+}
+
+// static
+std::unique_ptr<CPDF_NameTree> CPDF_NameTree::CreateForTesting(
+    CPDF_Dictionary* pRoot) {
+  return pdfium::WrapUnique(
+      new CPDF_NameTree(pdfium::WrapRetain(pRoot)));  // Private ctor.
+}
+
+// static
+RetainPtr<const CPDF_Array> CPDF_NameTree::LookupNamedDest(
+    CPDF_Document* pDoc,
+    const ByteString& name) {
+  RetainPtr<const CPDF_Array> dest_array;
+  std::unique_ptr<CPDF_NameTree> name_tree = Create(pDoc, "Dests");
+  if (name_tree)
+    dest_array = name_tree->LookupNewStyleNamedDest(name);
+  if (!dest_array)
+    dest_array = LookupOldStyleNamedDest(pDoc, name);
+  return dest_array;
+}
 
 size_t CPDF_NameTree::GetCount() const {
-  return m_pRoot ? CountNamesInternal(m_pRoot.Get(), 0) : 0;
+  std::set<const CPDF_Dictionary*> seen;
+  return CountNamesInternal(m_pRoot.Get(), 0, seen);
 }
 
 bool CPDF_NameTree::AddValueAndName(RetainPtr<CPDF_Object> pObj,
                                     const WideString& name) {
-  if (!m_pRoot)
-    return false;
-
-  size_t nIndex = 0;
-  CPDF_Array* pFind = nullptr;
+  RetainPtr<CPDF_Array> pFind;
   int nFindIndex = -1;
-  // Fail if the tree already contains this name or if the tree is too deep.
-  if (SearchNameNodeByName(m_pRoot.Get(), name, 0, &nIndex, &pFind,
-                           &nFindIndex)) {
-    return false;
+  // Handle the corner case where the root node is empty. i.e. No kids and no
+  // names. In which case, just insert into it and skip all the searches.
+  RetainPtr<CPDF_Array> pNames = m_pRoot->GetMutableArrayFor("Names");
+  if (pNames && pNames->IsEmpty() && !m_pRoot->GetArrayFor("Kids"))
+    pFind = pNames;
+
+  if (!pFind) {
+    // Fail if the tree already contains this name or if the tree is too deep.
+    if (SearchNameNodeByName(m_pRoot, name, &pFind, &nFindIndex))
+      return false;
   }
 
   // If the returned |pFind| is a nullptr, then |name| is smaller than all
@@ -335,96 +505,80 @@
   // |name| into. We instead will find the leftmost leaf array in which to place
   // |name| and |pObj|.
   if (!pFind) {
-    size_t nCurIndex = 0;
-    WideString csName;
-    SearchNameNodeByIndex(m_pRoot.Get(), 0, 0, &nCurIndex, &csName, &pFind,
-                          nullptr);
+    absl::optional<IndexSearchResult> result =
+        SearchNameNodeByIndex(m_pRoot.Get(), 0);
+    if (!result.has_value()) {
+      // Give up if that fails too.
+      return false;
+    }
+
+    pFind = result.value().container;
+    DCHECK(pFind);
   }
-  // Give up if that fails too.
-  if (!pFind)
-    return false;
 
   // Insert the name and the object into the leaf array found. Note that the
   // insertion position is right after the key-value pair returned by |index|.
   size_t nNameIndex = (nFindIndex + 1) * 2;
   size_t nValueIndex = nNameIndex + 1;
-  pFind->InsertNewAt<CPDF_String>(nNameIndex, name);
+  pFind->InsertNewAt<CPDF_String>(nNameIndex, name.AsStringView());
   pFind->InsertAt(nValueIndex, std::move(pObj));
 
   // Expand the limits that the newly added name is under, if the name falls
   // outside of the limits of its leaf array or any arrays above it.
-  std::vector<CPDF_Array*> pLimits;
-  GetNodeAncestorsLimits(m_pRoot.Get(), pFind, 0, &pLimits);
-  for (auto* pLimit : pLimits) {
-    if (!pLimit)
+  std::vector<CPDF_Array*> all_limits =
+      GetNodeAncestorsLimits(m_pRoot, pFind.Get());
+  for (auto* pLimits : all_limits) {
+    if (!pLimits)
       continue;
 
-    if (name.Compare(pLimit->GetUnicodeTextAt(0)) < 0)
-      pLimit->SetNewAt<CPDF_String>(0, name);
+    if (name.Compare(pLimits->GetUnicodeTextAt(0)) < 0)
+      pLimits->SetNewAt<CPDF_String>(0, name.AsStringView());
 
-    if (name.Compare(pLimit->GetUnicodeTextAt(1)) > 0)
-      pLimit->SetNewAt<CPDF_String>(1, name);
+    if (name.Compare(pLimits->GetUnicodeTextAt(1)) > 0)
+      pLimits->SetNewAt<CPDF_String>(1, name.AsStringView());
   }
   return true;
 }
 
-bool CPDF_NameTree::DeleteValueAndName(int nIndex) {
-  if (!m_pRoot)
-    return false;
-
-  size_t nCurIndex = 0;
-  WideString csName;
-  CPDF_Array* pFind = nullptr;
-  int nFindIndex = -1;
-  // Fail if the tree does not contain |nIndex|.
-  if (!SearchNameNodeByIndex(m_pRoot.Get(), nIndex, 0, &nCurIndex, &csName,
-                             &pFind, &nFindIndex)) {
+bool CPDF_NameTree::DeleteValueAndName(size_t nIndex) {
+  absl::optional<IndexSearchResult> result =
+      SearchNameNodeByIndex(m_pRoot.Get(), nIndex);
+  if (!result) {
+    // Fail if the tree does not contain |nIndex|.
     return false;
   }
 
   // Remove the name and the object from the leaf array |pFind|.
-  pFind->RemoveAt(nFindIndex * 2);
-  pFind->RemoveAt(nFindIndex * 2);
+  RetainPtr<CPDF_Array> pFind = result.value().container;
+  pFind->RemoveAt(result.value().index + 1);
+  pFind->RemoveAt(result.value().index);
 
   // Delete empty nodes and update the limits of |pFind|'s ancestors as needed.
-  UpdateNodesAndLimitsUponDeletion(m_pRoot.Get(), pFind, csName, 0);
+  UpdateNodesAndLimitsUponDeletion(m_pRoot.Get(), pFind.Get(),
+                                   result.value().key, 0);
   return true;
 }
 
-CPDF_Object* CPDF_NameTree::LookupValueAndName(int nIndex,
-                                               WideString* csName) const {
-  csName->clear();
-  if (!m_pRoot)
+RetainPtr<CPDF_Object> CPDF_NameTree::LookupValueAndName(
+    size_t nIndex,
+    WideString* csName) const {
+  absl::optional<IndexSearchResult> result =
+      SearchNameNodeByIndex(m_pRoot.Get(), nIndex);
+  if (!result) {
+    csName->clear();
     return nullptr;
-
-  size_t nCurIndex = 0;
-  return SearchNameNodeByIndex(m_pRoot.Get(), nIndex, 0, &nCurIndex, csName,
-                               nullptr, nullptr);
-}
-
-CPDF_Object* CPDF_NameTree::LookupValue(const WideString& csName) const {
-  if (!m_pRoot)
-    return nullptr;
-
-  size_t nIndex = 0;
-  return SearchNameNodeByName(m_pRoot.Get(), csName, 0, &nIndex, nullptr,
-                              nullptr);
-}
-
-CPDF_Array* CPDF_NameTree::LookupNamedDest(CPDF_Document* pDoc,
-                                           const WideString& sName) {
-  CPDF_Object* pValue = LookupValue(sName);
-  if (!pValue) {
-    CPDF_Dictionary* pDests = pDoc->GetRoot()->GetDictFor("Dests");
-    if (!pDests)
-      return nullptr;
-    pValue = pDests->GetDirectObjectFor(PDF_EncodeText(sName));
   }
-  if (!pValue)
-    return nullptr;
-  if (CPDF_Array* pArray = pValue->AsArray())
-    return pArray;
-  if (CPDF_Dictionary* pDict = pValue->AsDictionary())
-    return pDict->GetArrayFor("D");
-  return nullptr;
+
+  *csName = std::move(result.value().key);
+  return result.value().value;
+}
+
+RetainPtr<const CPDF_Object> CPDF_NameTree::LookupValue(
+    const WideString& csName) const {
+  return SearchNameNodeByName(m_pRoot, csName, nullptr, nullptr);
+}
+
+RetainPtr<const CPDF_Array> CPDF_NameTree::LookupNewStyleNamedDest(
+    const ByteString& sName) {
+  return GetNamedDestFromObject(LookupValue(PDF_DecodeText(sName.raw_span())));
 }
diff --git a/core/fpdfdoc/cpdf_nametree.h b/core/fpdfdoc/cpdf_nametree.h
index b018ae7..b40b232 100644
--- a/core/fpdfdoc/cpdf_nametree.h
+++ b/core/fpdfdoc/cpdf_nametree.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,8 @@
 #ifndef CORE_FPDFDOC_CPDF_NAMETREE_H_
 #define CORE_FPDFDOC_CPDF_NAMETREE_H_
 
+#include <stddef.h>
+
 #include <memory>
 
 #include "core/fxcrt/fx_string.h"
@@ -19,22 +21,42 @@
 
 class CPDF_NameTree {
  public:
-  explicit CPDF_NameTree(CPDF_Dictionary* pRoot);
-  CPDF_NameTree(CPDF_Document* pDoc, const ByteString& category);
+  CPDF_NameTree(const CPDF_NameTree&) = delete;
+  CPDF_NameTree& operator=(const CPDF_NameTree&) = delete;
   ~CPDF_NameTree();
 
-  bool AddValueAndName(RetainPtr<CPDF_Object> pObj, const WideString& name);
-  bool DeleteValueAndName(int nIndex);
+  static std::unique_ptr<CPDF_NameTree> Create(CPDF_Document* pDoc,
+                                               const ByteString& category);
 
-  CPDF_Object* LookupValueAndName(int nIndex, WideString* csName) const;
-  CPDF_Object* LookupValue(const WideString& csName) const;
-  CPDF_Array* LookupNamedDest(CPDF_Document* pDoc, const WideString& sName);
+  // If necessary, create missing Names dictionary in |pDoc|, and/or missing
+  // Names array in the dictionary that corresponds to |category|, if necessary.
+  // Returns nullptr on failure.
+  static std::unique_ptr<CPDF_NameTree> CreateWithRootNameArray(
+      CPDF_Document* pDoc,
+      const ByteString& category);
+
+  static std::unique_ptr<CPDF_NameTree> CreateForTesting(
+      CPDF_Dictionary* pRoot);
+
+  static RetainPtr<const CPDF_Array> LookupNamedDest(CPDF_Document* doc,
+                                                     const ByteString& name);
+
+  bool AddValueAndName(RetainPtr<CPDF_Object> pObj, const WideString& name);
+  bool DeleteValueAndName(size_t nIndex);
+
+  RetainPtr<CPDF_Object> LookupValueAndName(size_t nIndex,
+                                            WideString* csName) const;
+  RetainPtr<const CPDF_Object> LookupValue(const WideString& csName) const;
 
   size_t GetCount() const;
-  CPDF_Dictionary* GetRootForTest() const { return m_pRoot.Get(); }
+  CPDF_Dictionary* GetRootForTesting() const { return m_pRoot.Get(); }
 
  private:
-  RetainPtr<CPDF_Dictionary> m_pRoot;
+  explicit CPDF_NameTree(RetainPtr<CPDF_Dictionary> pRoot);
+
+  RetainPtr<const CPDF_Array> LookupNewStyleNamedDest(const ByteString& name);
+
+  RetainPtr<CPDF_Dictionary> const m_pRoot;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_NAMETREE_H_
diff --git a/core/fpdfdoc/cpdf_nametree_unittest.cpp b/core/fpdfdoc/cpdf_nametree_unittest.cpp
index 13b6b5c..a70359c 100644
--- a/core/fpdfdoc/cpdf_nametree_unittest.cpp
+++ b/core/fpdfdoc/cpdf_nametree_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,70 +7,96 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
-void AddNameKeyValue(CPDF_Array* pNames, const char* key, const int value) {
-  pNames->AddNew<CPDF_String>(key, false);
-  pNames->AddNew<CPDF_Number>(value);
+void AddNameKeyValue(CPDF_Array* names, const char* key, int value) {
+  names->AppendNew<CPDF_String>(key, false);
+  names->AppendNew<CPDF_Number>(value);
 }
 
-void CheckNameKeyValue(CPDF_Array* pNames,
-                       const int index,
+void CheckNameKeyValue(const CPDF_Array* names,
+                       int pair_index,
                        const char* key,
-                       const int value) {
-  EXPECT_STREQ(key, pNames->GetStringAt(index * 2).c_str());
-  EXPECT_EQ(value, pNames->GetIntegerAt(index * 2 + 1));
+                       int value) {
+  ASSERT_TRUE(names);
+  EXPECT_STREQ(key, names->GetByteStringAt(pair_index * 2).c_str());
+  EXPECT_EQ(value, names->GetIntegerAt(pair_index * 2 + 1));
 }
 
-void AddLimitsArray(CPDF_Dictionary* pNode,
+void AddLimitsArray(CPDF_Dictionary* node,
                     const char* least,
                     const char* greatest) {
-  CPDF_Array* pLimits = pNode->SetNewFor<CPDF_Array>("Limits");
-  pLimits->AddNew<CPDF_String>(least, false);
-  pLimits->AddNew<CPDF_String>(greatest, false);
+  auto limits = node->SetNewFor<CPDF_Array>("Limits");
+  limits->AppendNew<CPDF_String>(least, false);
+  limits->AppendNew<CPDF_String>(greatest, false);
 }
 
-void CheckLimitsArray(CPDF_Dictionary* pNode,
+void CheckLimitsArray(const CPDF_Dictionary* node,
                       const char* least,
                       const char* greatest) {
-  CPDF_Array* pLimits = pNode->GetArrayFor("Limits");
-  ASSERT_TRUE(pLimits);
-  EXPECT_STREQ(least, pLimits->GetStringAt(0).c_str());
-  EXPECT_STREQ(greatest, pLimits->GetStringAt(1).c_str());
+  ASSERT_TRUE(node);
+  RetainPtr<const CPDF_Array> limits = node->GetArrayFor("Limits");
+  ASSERT_TRUE(limits);
+  EXPECT_EQ(2u, limits->size());
+  RetainPtr<const CPDF_String> left = limits->GetStringAt(0);
+  ASSERT_TRUE(left);
+  RetainPtr<const CPDF_String> right = limits->GetStringAt(1);
+  ASSERT_TRUE(right);
+  EXPECT_STREQ(least, left->GetString().c_str());
+  EXPECT_STREQ(greatest, right->GetString().c_str());
 }
 
+// Set up a name tree with 3 levels and 5 nodes, per diagram:
+//
+//   [root]
+//     |
+//     |
+//     |
+//   [pKid1]
+//     |
+//     +------------+
+//     |            |
+//   [pGrandKid2] [pGrandKid3]
+//     |          {9.txt: 999}
+//     |
+//     +-----------------+
+//     |                 |
+//   [pGreatGrandKid4] [pGreatGrandKid5]
+//   {1.txt: 111}      {3.txt: 333}
+//   {2.txt: 222}      {5.txt: 555}
+//
 void FillNameTreeDict(CPDF_Dictionary* pRootDict) {
-  CPDF_Array* pKids = pRootDict->SetNewFor<CPDF_Array>("Kids");
-  CPDF_Dictionary* pKid1 = pKids->AddNew<CPDF_Dictionary>();
+  auto pRootKids = pRootDict->SetNewFor<CPDF_Array>("Kids");
+  auto pKid1 = pRootKids->AppendNew<CPDF_Dictionary>();
 
   // Make the lower and upper limit out of order on purpose.
-  AddLimitsArray(pKid1, "9.txt", "1.txt");
-  pKids = pKid1->SetNewFor<CPDF_Array>("Kids");
-  CPDF_Dictionary* pKid2 = pKids->AddNew<CPDF_Dictionary>();
-  CPDF_Dictionary* pKid3 = pKids->AddNew<CPDF_Dictionary>();
+  AddLimitsArray(pKid1.Get(), "9.txt", "1.txt");
+  auto pKids1Kids = pKid1->SetNewFor<CPDF_Array>("Kids");
+  auto pGrandKid2 = pKids1Kids->AppendNew<CPDF_Dictionary>();
+  auto pGrandKid3 = pKids1Kids->AppendNew<CPDF_Dictionary>();
 
-  AddLimitsArray(pKid2, "1.txt", "5.txt");
-  pKids = pKid2->SetNewFor<CPDF_Array>("Kids");
-  CPDF_Dictionary* pKid4 = pKids->AddNew<CPDF_Dictionary>();
-  CPDF_Dictionary* pKid5 = pKids->AddNew<CPDF_Dictionary>();
+  AddLimitsArray(pGrandKid2.Get(), "1.txt", "5.txt");
+  auto pGrandKid2Kids = pGrandKid2->SetNewFor<CPDF_Array>("Kids");
+  auto pGreatGrandKid4 = pGrandKid2Kids->AppendNew<CPDF_Dictionary>();
+  auto pGreatGrandKid5 = pGrandKid2Kids->AppendNew<CPDF_Dictionary>();
 
-  AddLimitsArray(pKid3, "9.txt", "9.txt");
-  CPDF_Array* pNames = pKid3->SetNewFor<CPDF_Array>("Names");
-  AddNameKeyValue(pNames, "9.txt", 999);
+  AddLimitsArray(pGrandKid3.Get(), "9.txt", "9.txt");
+  auto pNames = pGrandKid3->SetNewFor<CPDF_Array>("Names");
+  AddNameKeyValue(pNames.Get(), "9.txt", 999);
 
   // Make the lower and upper limit out of order on purpose.
-  AddLimitsArray(pKid4, "2.txt", "1.txt");
-  pNames = pKid4->SetNewFor<CPDF_Array>("Names");
-  AddNameKeyValue(pNames, "1.txt", 111);
-  AddNameKeyValue(pNames, "2.txt", 222);
+  AddLimitsArray(pGreatGrandKid4.Get(), "2.txt", "1.txt");
+  pNames = pGreatGrandKid4->SetNewFor<CPDF_Array>("Names");
+  AddNameKeyValue(pNames.Get(), "1.txt", 111);
+  AddNameKeyValue(pNames.Get(), "2.txt", 222);
 
-  AddLimitsArray(pKid5, "3.txt", "5.txt");
-  pNames = pKid5->SetNewFor<CPDF_Array>("Names");
-  AddNameKeyValue(pNames, "3.txt", 333);
-  AddNameKeyValue(pNames, "5.txt", 555);
+  AddLimitsArray(pGreatGrandKid5.Get(), "3.txt", "5.txt");
+  pNames = pGreatGrandKid5->SetNewFor<CPDF_Array>("Names");
+  AddNameKeyValue(pNames.Get(), "3.txt", 333);
+  AddNameKeyValue(pNames.Get(), "5.txt", 555);
 }
 
 }  // namespace
@@ -78,221 +104,283 @@
 TEST(cpdf_nametree, GetUnicodeNameWithBOM) {
   // Set up the root dictionary with a Names array.
   auto pRootDict = pdfium::MakeRetain<CPDF_Dictionary>();
-  CPDF_Array* pNames = pRootDict->SetNewFor<CPDF_Array>("Names");
+  auto pNames = pRootDict->SetNewFor<CPDF_Array>("Names");
 
   // Add the key "1" (with BOM) and value 100 into the array.
-  std::ostringstream buf;
   constexpr char kData[] = "\xFE\xFF\x00\x31";
-  for (size_t i = 0; i < sizeof(kData); ++i)
-    buf.put(kData[i]);
-  pNames->AddNew<CPDF_String>(ByteString(buf), true);
-  pNames->AddNew<CPDF_Number>(100);
+  pNames->AppendNew<CPDF_String>(ByteString(kData, sizeof(kData) - 1), true);
+  pNames->AppendNew<CPDF_Number>(100);
 
   // Check that the key is as expected.
-  CPDF_NameTree nameTree(pRootDict.Get());
-  WideString storedName;
-  nameTree.LookupValueAndName(0, &storedName);
-  EXPECT_STREQ(L"1", storedName.c_str());
+  std::unique_ptr<CPDF_NameTree> name_tree =
+      CPDF_NameTree::CreateForTesting(pRootDict.Get());
+  WideString stored_name;
+  name_tree->LookupValueAndName(0, &stored_name);
+  EXPECT_STREQ(L"1", stored_name.c_str());
 
   // Check that the correct value object can be obtained by looking up "1".
-  WideString matchName = L"1";
-  CPDF_Object* pObj = nameTree.LookupValue(matchName);
-  ASSERT_TRUE(pObj->IsNumber());
-  EXPECT_EQ(100, pObj->AsNumber()->GetInteger());
+  RetainPtr<const CPDF_Number> pNumber = ToNumber(name_tree->LookupValue(L"1"));
+  ASSERT_TRUE(pNumber);
+  EXPECT_EQ(100, pNumber->GetInteger());
+}
+
+TEST(cpdf_nametree, GetFromTreeWithLimitsArrayWith4Items) {
+  // After creating a name tree, mutate a /Limits array so it has excess
+  // elements.
+  auto pRootDict = pdfium::MakeRetain<CPDF_Dictionary>();
+  FillNameTreeDict(pRootDict.Get());
+  RetainPtr<CPDF_Dictionary> pKid1 =
+      pRootDict->GetMutableArrayFor("Kids")->GetMutableDictAt(0);
+  RetainPtr<CPDF_Dictionary> pGrandKid3 =
+      pKid1->GetMutableArrayFor("Kids")->GetMutableDictAt(1);
+  RetainPtr<CPDF_Array> pLimits = pGrandKid3->GetMutableArrayFor("Limits");
+  ASSERT_EQ(2u, pLimits->size());
+  pLimits->AppendNew<CPDF_Number>(5);
+  pLimits->AppendNew<CPDF_Number>(6);
+  ASSERT_EQ(4u, pLimits->size());
+  std::unique_ptr<CPDF_NameTree> name_tree =
+      CPDF_NameTree::CreateForTesting(pRootDict.Get());
+
+  RetainPtr<const CPDF_Number> pNumber =
+      ToNumber(name_tree->LookupValue(L"9.txt"));
+  ASSERT_TRUE(pNumber);
+  EXPECT_EQ(999, pNumber->GetInteger());
+  CheckLimitsArray(pKid1.Get(), "1.txt", "9.txt");
+  CheckLimitsArray(pGrandKid3.Get(), "9.txt", "9.txt");
 }
 
 TEST(cpdf_nametree, AddIntoNames) {
   // Set up a name tree with a single Names array.
   auto pRootDict = pdfium::MakeRetain<CPDF_Dictionary>();
-  CPDF_Array* pNames = pRootDict->SetNewFor<CPDF_Array>("Names");
-  AddNameKeyValue(pNames, "2.txt", 222);
-  AddNameKeyValue(pNames, "7.txt", 777);
+  auto pNames = pRootDict->SetNewFor<CPDF_Array>("Names");
+  AddNameKeyValue(pNames.Get(), "2.txt", 222);
+  AddNameKeyValue(pNames.Get(), "7.txt", 777);
 
-  CPDF_NameTree nameTree(pRootDict.Get());
-  pNames = nameTree.GetRootForTest()->GetArrayFor("Names");
+  std::unique_ptr<CPDF_NameTree> name_tree =
+      CPDF_NameTree::CreateForTesting(pRootDict.Get());
 
   // Insert a name that already exists in the names array.
-  EXPECT_FALSE(
-      nameTree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111), L"2.txt"));
+  EXPECT_FALSE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111),
+                                          L"2.txt"));
 
   // Insert in the beginning of the names array.
-  EXPECT_TRUE(
-      nameTree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111), L"1.txt"));
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111),
+                                         L"1.txt"));
 
   // Insert in the middle of the names array.
-  EXPECT_TRUE(
-      nameTree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(555), L"5.txt"));
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(555),
+                                         L"5.txt"));
 
   // Insert at the end of the names array.
-  EXPECT_TRUE(
-      nameTree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(999), L"9.txt"));
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(999),
+                                         L"9.txt"));
 
   // Check that the names array has the expected key-value pairs.
-  CheckNameKeyValue(pNames, 0, "1.txt", 111);
-  CheckNameKeyValue(pNames, 1, "2.txt", 222);
-  CheckNameKeyValue(pNames, 2, "5.txt", 555);
-  CheckNameKeyValue(pNames, 3, "7.txt", 777);
-  CheckNameKeyValue(pNames, 4, "9.txt", 999);
+  CheckNameKeyValue(pNames.Get(), 0, "1.txt", 111);
+  CheckNameKeyValue(pNames.Get(), 1, "2.txt", 222);
+  CheckNameKeyValue(pNames.Get(), 2, "5.txt", 555);
+  CheckNameKeyValue(pNames.Get(), 3, "7.txt", 777);
+  CheckNameKeyValue(pNames.Get(), 4, "9.txt", 999);
+}
+
+TEST(cpdf_nametree, AddIntoEmptyNames) {
+  // Set up a name tree with an empty Names array.
+  auto pRootDict = pdfium::MakeRetain<CPDF_Dictionary>();
+  auto pNames = pRootDict->SetNewFor<CPDF_Array>("Names");
+
+  std::unique_ptr<CPDF_NameTree> name_tree =
+      CPDF_NameTree::CreateForTesting(pRootDict.Get());
+
+  // Insert a name should work.
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111),
+                                         L"2.txt"));
+
+  // Insert a name that already exists in the names array.
+  EXPECT_FALSE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111),
+                                          L"2.txt"));
+
+  // Insert in the beginning of the names array.
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111),
+                                         L"1.txt"));
+
+  // Insert in the middle of the names array.
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(555),
+                                         L"5.txt"));
+
+  // Insert at the end of the names array.
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(999),
+                                         L"9.txt"));
+
+  // Check that the names array has the expected key-value pairs.
+  CheckNameKeyValue(pNames.Get(), 0, "1.txt", 111);
+  CheckNameKeyValue(pNames.Get(), 1, "2.txt", 111);
+  CheckNameKeyValue(pNames.Get(), 2, "5.txt", 555);
+  CheckNameKeyValue(pNames.Get(), 3, "9.txt", 999);
 }
 
 TEST(cpdf_nametree, AddIntoKids) {
-  // Set up a name tree with five nodes of three levels.
   auto pRootDict = pdfium::MakeRetain<CPDF_Dictionary>();
   FillNameTreeDict(pRootDict.Get());
-  CPDF_NameTree nameTree(pRootDict.Get());
+  std::unique_ptr<CPDF_NameTree> name_tree =
+      CPDF_NameTree::CreateForTesting(pRootDict.Get());
 
   // Check that adding an existing name would fail.
-  EXPECT_FALSE(
-      nameTree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(444), L"9.txt"));
+  EXPECT_FALSE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(444),
+                                          L"9.txt"));
 
   // Add a name within the limits of a leaf node.
-  EXPECT_TRUE(
-      nameTree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(444), L"4.txt"));
-  ASSERT_TRUE(nameTree.LookupValue(L"4.txt"));
-  EXPECT_EQ(444, nameTree.LookupValue(L"4.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(444),
+                                         L"4.txt"));
+  ASSERT_TRUE(name_tree->LookupValue(L"4.txt"));
+  EXPECT_EQ(444, name_tree->LookupValue(L"4.txt")->GetInteger());
 
   // Add a name that requires changing the limits of two bottom levels.
-  EXPECT_TRUE(
-      nameTree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(666), L"6.txt"));
-  ASSERT_TRUE(nameTree.LookupValue(L"6.txt"));
-  EXPECT_EQ(666, nameTree.LookupValue(L"6.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(666),
+                                         L"6.txt"));
+  ASSERT_TRUE(name_tree->LookupValue(L"6.txt"));
+  EXPECT_EQ(666, name_tree->LookupValue(L"6.txt")->GetInteger());
 
   // Add a name that requires changing the limits of two top levels.
-  EXPECT_TRUE(
-      nameTree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(99), L"99.txt"));
-  ASSERT_TRUE(nameTree.LookupValue(L"99.txt"));
-  EXPECT_EQ(99, nameTree.LookupValue(L"99.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(99),
+                                         L"99.txt"));
+  ASSERT_TRUE(name_tree->LookupValue(L"99.txt"));
+  EXPECT_EQ(99, name_tree->LookupValue(L"99.txt")->GetInteger());
 
   // Add a name that requires changing the lower limit of all levels.
-  EXPECT_TRUE(
-      nameTree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(-5), L"0.txt"));
-  ASSERT_TRUE(nameTree.LookupValue(L"0.txt"));
-  EXPECT_EQ(-5, nameTree.LookupValue(L"0.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(-5),
+                                         L"0.txt"));
+  ASSERT_TRUE(name_tree->LookupValue(L"0.txt"));
+  EXPECT_EQ(-5, name_tree->LookupValue(L"0.txt")->GetInteger());
 
   // Check that the node on the first level has the expected limits.
-  CPDF_Dictionary* pKid1 =
-      nameTree.GetRootForTest()->GetArrayFor("Kids")->GetDictAt(0);
+  RetainPtr<const CPDF_Dictionary> pKid1 =
+      name_tree->GetRootForTesting()->GetArrayFor("Kids")->GetDictAt(0);
   ASSERT_TRUE(pKid1);
-  CheckLimitsArray(pKid1, "0.txt", "99.txt");
+  CheckLimitsArray(pKid1.Get(), "0.txt", "99.txt");
 
   // Check that the nodes on the second level has the expected limits and names.
-  CPDF_Dictionary* pKid2 = pKid1->GetArrayFor("Kids")->GetDictAt(0);
-  ASSERT_TRUE(pKid2);
-  CheckLimitsArray(pKid2, "0.txt", "6.txt");
+  RetainPtr<const CPDF_Dictionary> pGrandKid2 =
+      pKid1->GetArrayFor("Kids")->GetDictAt(0);
+  ASSERT_TRUE(pGrandKid2);
+  CheckLimitsArray(pGrandKid2.Get(), "0.txt", "6.txt");
 
-  CPDF_Dictionary* pKid3 = pKid1->GetArrayFor("Kids")->GetDictAt(1);
-  ASSERT_TRUE(pKid3);
-  CheckLimitsArray(pKid3, "9.txt", "99.txt");
-  CPDF_Array* pNames = pKid3->GetArrayFor("Names");
-  ASSERT_TRUE(pNames);
-  CheckNameKeyValue(pNames, 0, "9.txt", 999);
-  CheckNameKeyValue(pNames, 1, "99.txt", 99);
+  RetainPtr<const CPDF_Dictionary> pGrandKid3 =
+      pKid1->GetArrayFor("Kids")->GetDictAt(1);
+  ASSERT_TRUE(pGrandKid3);
+  CheckLimitsArray(pGrandKid3.Get(), "9.txt", "99.txt");
+  RetainPtr<const CPDF_Array> pNames = pGrandKid3->GetArrayFor("Names");
+  CheckNameKeyValue(pNames.Get(), 0, "9.txt", 999);
+  CheckNameKeyValue(pNames.Get(), 1, "99.txt", 99);
 
   // Check that the nodes on the third level has the expected limits and names.
-  CPDF_Dictionary* pKid4 = pKid2->GetArrayFor("Kids")->GetDictAt(0);
-  ASSERT_TRUE(pKid4);
-  CheckLimitsArray(pKid4, "0.txt", "2.txt");
-  pNames = pKid4->GetArrayFor("Names");
-  ASSERT_TRUE(pNames);
-  CheckNameKeyValue(pNames, 0, "0.txt", -5);
-  CheckNameKeyValue(pNames, 1, "1.txt", 111);
-  CheckNameKeyValue(pNames, 2, "2.txt", 222);
+  RetainPtr<const CPDF_Dictionary> pGreatGrandKid4 =
+      pGrandKid2->GetArrayFor("Kids")->GetDictAt(0);
+  ASSERT_TRUE(pGreatGrandKid4);
+  CheckLimitsArray(pGreatGrandKid4.Get(), "0.txt", "2.txt");
+  pNames = pGreatGrandKid4->GetArrayFor("Names");
+  CheckNameKeyValue(pNames.Get(), 0, "0.txt", -5);
+  CheckNameKeyValue(pNames.Get(), 1, "1.txt", 111);
+  CheckNameKeyValue(pNames.Get(), 2, "2.txt", 222);
 
-  CPDF_Dictionary* pKid5 = pKid2->GetArrayFor("Kids")->GetDictAt(1);
-  ASSERT_TRUE(pKid5);
-  CheckLimitsArray(pKid5, "3.txt", "6.txt");
-  pNames = pKid5->GetArrayFor("Names");
-  ASSERT_TRUE(pNames);
-  CheckNameKeyValue(pNames, 0, "3.txt", 333);
-  CheckNameKeyValue(pNames, 1, "4.txt", 444);
-  CheckNameKeyValue(pNames, 2, "5.txt", 555);
-  CheckNameKeyValue(pNames, 3, "6.txt", 666);
+  RetainPtr<const CPDF_Dictionary> pGreatGrandKid5 =
+      pGrandKid2->GetArrayFor("Kids")->GetDictAt(1);
+  ASSERT_TRUE(pGreatGrandKid5);
+  CheckLimitsArray(pGreatGrandKid5.Get(), "3.txt", "6.txt");
+  pNames = pGreatGrandKid5->GetArrayFor("Names");
+  CheckNameKeyValue(pNames.Get(), 0, "3.txt", 333);
+  CheckNameKeyValue(pNames.Get(), 1, "4.txt", 444);
+  CheckNameKeyValue(pNames.Get(), 2, "5.txt", 555);
+  CheckNameKeyValue(pNames.Get(), 3, "6.txt", 666);
 }
 
 TEST(cpdf_nametree, DeleteFromKids) {
-  // Set up a name tree with five nodes of three levels.
   auto pRootDict = pdfium::MakeRetain<CPDF_Dictionary>();
   FillNameTreeDict(pRootDict.Get());
-  CPDF_NameTree nameTree(pRootDict.Get());
+  std::unique_ptr<CPDF_NameTree> name_tree =
+      CPDF_NameTree::CreateForTesting(pRootDict.Get());
 
   // Retrieve the kid dictionaries.
-  CPDF_Dictionary* pKid1 =
-      nameTree.GetRootForTest()->GetArrayFor("Kids")->GetDictAt(0);
+  RetainPtr<const CPDF_Dictionary> pKid1 =
+      name_tree->GetRootForTesting()->GetArrayFor("Kids")->GetDictAt(0);
   ASSERT_TRUE(pKid1);
-  CPDF_Dictionary* pKid2 = pKid1->GetArrayFor("Kids")->GetDictAt(0);
-  ASSERT_TRUE(pKid2);
-  CPDF_Dictionary* pKid3 = pKid1->GetArrayFor("Kids")->GetDictAt(1);
-  ASSERT_TRUE(pKid3);
-  CPDF_Dictionary* pKid4 = pKid2->GetArrayFor("Kids")->GetDictAt(0);
-  ASSERT_TRUE(pKid4);
-  CPDF_Dictionary* pKid5 = pKid2->GetArrayFor("Kids")->GetDictAt(1);
-  ASSERT_TRUE(pKid5);
+  RetainPtr<const CPDF_Dictionary> pGrandKid2 =
+      pKid1->GetArrayFor("Kids")->GetDictAt(0);
+  ASSERT_TRUE(pGrandKid2);
+  RetainPtr<const CPDF_Dictionary> pGrandKid3 =
+      pKid1->GetArrayFor("Kids")->GetDictAt(1);
+  ASSERT_TRUE(pGrandKid3);
+  RetainPtr<const CPDF_Dictionary> pGreatGrandKid4 =
+      pGrandKid2->GetArrayFor("Kids")->GetDictAt(0);
+  ASSERT_TRUE(pGreatGrandKid4);
+  RetainPtr<const CPDF_Dictionary> pGreatGrandKid5 =
+      pGrandKid2->GetArrayFor("Kids")->GetDictAt(1);
+  ASSERT_TRUE(pGreatGrandKid5);
 
   // Check that deleting an out-of-bound index would fail.
-  EXPECT_FALSE(nameTree.DeleteValueAndName(5));
+  EXPECT_FALSE(name_tree->DeleteValueAndName(5));
 
   // Delete the name "9.txt", and check that its node gets deleted and its
   // parent node's limits get updated.
   WideString csName;
-  ASSERT_TRUE(nameTree.LookupValue(L"9.txt"));
-  EXPECT_EQ(999, nameTree.LookupValue(L"9.txt")->GetInteger());
-  EXPECT_TRUE(nameTree.LookupValueAndName(4, &csName));
+  ASSERT_TRUE(name_tree->LookupValue(L"9.txt"));
+  EXPECT_EQ(999, name_tree->LookupValue(L"9.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->LookupValueAndName(4, &csName));
   EXPECT_STREQ(L"9.txt", csName.c_str());
   EXPECT_EQ(2u, pKid1->GetArrayFor("Kids")->size());
-  EXPECT_TRUE(nameTree.DeleteValueAndName(4));
+  EXPECT_TRUE(name_tree->DeleteValueAndName(4));
   EXPECT_EQ(1u, pKid1->GetArrayFor("Kids")->size());
-  CheckLimitsArray(pKid1, "1.txt", "5.txt");
+  CheckLimitsArray(pKid1.Get(), "1.txt", "5.txt");
 
   // Delete the name "2.txt", and check that its node does not get deleted, its
   // node's limits get updated, and no other limits get updated.
-  ASSERT_TRUE(nameTree.LookupValue(L"2.txt"));
-  EXPECT_EQ(222, nameTree.LookupValue(L"2.txt")->GetInteger());
-  EXPECT_TRUE(nameTree.LookupValueAndName(1, &csName));
+  ASSERT_TRUE(name_tree->LookupValue(L"2.txt"));
+  EXPECT_EQ(222, name_tree->LookupValue(L"2.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->LookupValueAndName(1, &csName));
   EXPECT_STREQ(L"2.txt", csName.c_str());
-  EXPECT_EQ(4u, pKid4->GetArrayFor("Names")->size());
-  EXPECT_TRUE(nameTree.DeleteValueAndName(1));
-  EXPECT_EQ(2u, pKid4->GetArrayFor("Names")->size());
-  CheckLimitsArray(pKid4, "1.txt", "1.txt");
-  CheckLimitsArray(pKid2, "1.txt", "5.txt");
-  CheckLimitsArray(pKid1, "1.txt", "5.txt");
+  EXPECT_EQ(4u, pGreatGrandKid4->GetArrayFor("Names")->size());
+  EXPECT_TRUE(name_tree->DeleteValueAndName(1));
+  EXPECT_EQ(2u, pGreatGrandKid4->GetArrayFor("Names")->size());
+  CheckLimitsArray(pGreatGrandKid4.Get(), "1.txt", "1.txt");
+  CheckLimitsArray(pGrandKid2.Get(), "1.txt", "5.txt");
+  CheckLimitsArray(pKid1.Get(), "1.txt", "5.txt");
 
   // Delete the name "1.txt", and check that its node gets deleted, and its
   // parent's and gradparent's limits get updated.
-  ASSERT_TRUE(nameTree.LookupValue(L"1.txt"));
-  EXPECT_EQ(111, nameTree.LookupValue(L"1.txt")->GetInteger());
-  EXPECT_TRUE(nameTree.LookupValueAndName(0, &csName));
+  ASSERT_TRUE(name_tree->LookupValue(L"1.txt"));
+  EXPECT_EQ(111, name_tree->LookupValue(L"1.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->LookupValueAndName(0, &csName));
   EXPECT_STREQ(L"1.txt", csName.c_str());
-  EXPECT_EQ(2u, pKid2->GetArrayFor("Kids")->size());
-  EXPECT_TRUE(nameTree.DeleteValueAndName(0));
-  EXPECT_EQ(1u, pKid2->GetArrayFor("Kids")->size());
-  CheckLimitsArray(pKid2, "3.txt", "5.txt");
-  CheckLimitsArray(pKid1, "3.txt", "5.txt");
+  EXPECT_EQ(2u, pGrandKid2->GetArrayFor("Kids")->size());
+  EXPECT_TRUE(name_tree->DeleteValueAndName(0));
+  EXPECT_EQ(1u, pGrandKid2->GetArrayFor("Kids")->size());
+  CheckLimitsArray(pGrandKid2.Get(), "3.txt", "5.txt");
+  CheckLimitsArray(pKid1.Get(), "3.txt", "5.txt");
 
   // Delete the name "3.txt", and check that its node does not get deleted, and
   // its node's, its parent's, and its grandparent's limits get updated.
-  ASSERT_TRUE(nameTree.LookupValue(L"3.txt"));
-  EXPECT_EQ(333, nameTree.LookupValue(L"3.txt")->GetInteger());
-  EXPECT_TRUE(nameTree.LookupValueAndName(0, &csName));
+  ASSERT_TRUE(name_tree->LookupValue(L"3.txt"));
+  EXPECT_EQ(333, name_tree->LookupValue(L"3.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->LookupValueAndName(0, &csName));
   EXPECT_STREQ(L"3.txt", csName.c_str());
-  EXPECT_EQ(4u, pKid5->GetArrayFor("Names")->size());
-  EXPECT_TRUE(nameTree.DeleteValueAndName(0));
-  EXPECT_EQ(2u, pKid5->GetArrayFor("Names")->size());
-  CheckLimitsArray(pKid5, "5.txt", "5.txt");
-  CheckLimitsArray(pKid2, "5.txt", "5.txt");
-  CheckLimitsArray(pKid1, "5.txt", "5.txt");
+  EXPECT_EQ(4u, pGreatGrandKid5->GetArrayFor("Names")->size());
+  EXPECT_TRUE(name_tree->DeleteValueAndName(0));
+  EXPECT_EQ(2u, pGreatGrandKid5->GetArrayFor("Names")->size());
+  CheckLimitsArray(pGreatGrandKid5.Get(), "5.txt", "5.txt");
+  CheckLimitsArray(pGrandKid2.Get(), "5.txt", "5.txt");
+  CheckLimitsArray(pKid1.Get(), "5.txt", "5.txt");
 
   // Delete the name "5.txt", and check that all nodes in the tree get deleted
   // since they are now all empty.
-  ASSERT_TRUE(nameTree.LookupValue(L"5.txt"));
-  EXPECT_EQ(555, nameTree.LookupValue(L"5.txt")->GetInteger());
-  EXPECT_TRUE(nameTree.LookupValueAndName(0, &csName));
+  ASSERT_TRUE(name_tree->LookupValue(L"5.txt"));
+  EXPECT_EQ(555, name_tree->LookupValue(L"5.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->LookupValueAndName(0, &csName));
   EXPECT_STREQ(L"5.txt", csName.c_str());
-  EXPECT_EQ(1u, nameTree.GetRootForTest()->GetArrayFor("Kids")->size());
-  EXPECT_TRUE(nameTree.DeleteValueAndName(0));
-  EXPECT_EQ(0u, nameTree.GetRootForTest()->GetArrayFor("Kids")->size());
+  EXPECT_EQ(1u, name_tree->GetRootForTesting()->GetArrayFor("Kids")->size());
+  EXPECT_TRUE(name_tree->DeleteValueAndName(0));
+  EXPECT_EQ(0u, name_tree->GetRootForTesting()->GetArrayFor("Kids")->size());
 
   // Check that the tree is now empty.
-  EXPECT_EQ(0u, nameTree.GetCount());
-  EXPECT_FALSE(nameTree.LookupValueAndName(0, &csName));
-  EXPECT_FALSE(nameTree.DeleteValueAndName(0));
+  EXPECT_EQ(0u, name_tree->GetCount());
+  EXPECT_FALSE(name_tree->LookupValueAndName(0, &csName));
+  EXPECT_FALSE(name_tree->DeleteValueAndName(0));
 }
diff --git a/core/fpdfdoc/cpdf_numbertree.cpp b/core/fpdfdoc/cpdf_numbertree.cpp
index 46257c8..f381634 100644
--- a/core/fpdfdoc/cpdf_numbertree.cpp
+++ b/core/fpdfdoc/cpdf_numbertree.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,18 +6,21 @@
 
 #include "core/fpdfdoc/cpdf_numbertree.h"
 
+#include <utility>
+
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 
 namespace {
 
-const CPDF_Object* SearchNumberNode(const CPDF_Dictionary* pNode, int num) {
-  const CPDF_Array* pLimits = pNode->GetArrayFor("Limits");
+RetainPtr<const CPDF_Object> SearchNumberNode(const CPDF_Dictionary* pNode,
+                                              int num) {
+  RetainPtr<const CPDF_Array> pLimits = pNode->GetArrayFor("Limits");
   if (pLimits &&
       (num < pLimits->GetIntegerAt(0) || num > pLimits->GetIntegerAt(1))) {
     return nullptr;
   }
-  const CPDF_Array* pNumbers = pNode->GetArrayFor("Nums");
+  RetainPtr<const CPDF_Array> pNumbers = pNode->GetArrayFor("Nums");
   if (pNumbers) {
     for (size_t i = 0; i < pNumbers->size() / 2; i++) {
       int index = pNumbers->GetIntegerAt(i * 2);
@@ -29,16 +32,16 @@
     return nullptr;
   }
 
-  const CPDF_Array* pKids = pNode->GetArrayFor("Kids");
+  RetainPtr<const CPDF_Array> pKids = pNode->GetArrayFor("Kids");
   if (!pKids)
     return nullptr;
 
   for (size_t i = 0; i < pKids->size(); i++) {
-    const CPDF_Dictionary* pKid = pKids->GetDictAt(i);
+    RetainPtr<const CPDF_Dictionary> pKid = pKids->GetDictAt(i);
     if (!pKid)
       continue;
 
-    const CPDF_Object* pFound = SearchNumberNode(pKid, num);
+    RetainPtr<const CPDF_Object> pFound = SearchNumberNode(pKid.Get(), num);
     if (pFound)
       return pFound;
   }
@@ -47,11 +50,11 @@
 
 }  // namespace
 
-CPDF_NumberTree::CPDF_NumberTree(const CPDF_Dictionary* pRoot)
-    : m_pRoot(pRoot) {}
+CPDF_NumberTree::CPDF_NumberTree(RetainPtr<const CPDF_Dictionary> pRoot)
+    : m_pRoot(std::move(pRoot)) {}
 
-CPDF_NumberTree::~CPDF_NumberTree() {}
+CPDF_NumberTree::~CPDF_NumberTree() = default;
 
-const CPDF_Object* CPDF_NumberTree::LookupValue(int num) const {
+RetainPtr<const CPDF_Object> CPDF_NumberTree::LookupValue(int num) const {
   return SearchNumberNode(m_pRoot.Get(), num);
 }
diff --git a/core/fpdfdoc/cpdf_numbertree.h b/core/fpdfdoc/cpdf_numbertree.h
index 2a3fa6e..1cd1ef8 100644
--- a/core/fpdfdoc/cpdf_numbertree.h
+++ b/core/fpdfdoc/cpdf_numbertree.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,10 +14,10 @@
 
 class CPDF_NumberTree {
  public:
-  explicit CPDF_NumberTree(const CPDF_Dictionary* pRoot);
+  explicit CPDF_NumberTree(RetainPtr<const CPDF_Dictionary> pRoot);
   ~CPDF_NumberTree();
 
-  const CPDF_Object* LookupValue(int num) const;
+  RetainPtr<const CPDF_Object> LookupValue(int num) const;
 
  protected:
   RetainPtr<const CPDF_Dictionary> const m_pRoot;
diff --git a/core/fpdfdoc/cpdf_pagelabel.cpp b/core/fpdfdoc/cpdf_pagelabel.cpp
index 8985def..2611936 100644
--- a/core/fpdfdoc/cpdf_pagelabel.cpp
+++ b/core/fpdfdoc/cpdf_pagelabel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include "core/fpdfdoc/cpdf_pagelabel.h"
 
+#include <utility>
+
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
@@ -15,8 +17,9 @@
 
 WideString MakeRoman(int num) {
   const int kArabic[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
-  const WideString kRoman[] = {L"m",  L"cm", L"d",  L"cd", L"c",  L"xc", L"l",
-                               L"xl", L"x",  L"ix", L"v",  L"iv", L"i"};
+  const WideStringView kRoman[] = {L"m",  L"cm", L"d",  L"cd", L"c",
+                                   L"xc", L"l",  L"xl", L"x",  L"ix",
+                                   L"v",  L"iv", L"i"};
   const int kMaxNum = 1000000;
 
   num %= kMaxNum;
@@ -53,7 +56,7 @@
   if (bsStyle.IsEmpty())
     return WideString();
   if (bsStyle == "D")
-    return WideString::Format(L"%d", num);
+    return WideString::FormatInteger(num);
   if (bsStyle == "R") {
     WideString wsNumPortion = MakeRoman(num);
     wsNumPortion.MakeUpper();
@@ -76,25 +79,25 @@
 CPDF_PageLabel::CPDF_PageLabel(CPDF_Document* pDocument)
     : m_pDocument(pDocument) {}
 
-CPDF_PageLabel::~CPDF_PageLabel() {}
+CPDF_PageLabel::~CPDF_PageLabel() = default;
 
-Optional<WideString> CPDF_PageLabel::GetLabel(int nPage) const {
+absl::optional<WideString> CPDF_PageLabel::GetLabel(int nPage) const {
   if (!m_pDocument)
-    return {};
+    return absl::nullopt;
 
   if (nPage < 0 || nPage >= m_pDocument->GetPageCount())
-    return {};
+    return absl::nullopt;
 
   const CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot();
   if (!pPDFRoot)
-    return {};
+    return absl::nullopt;
 
-  const CPDF_Dictionary* pLabels = pPDFRoot->GetDictFor("PageLabels");
+  RetainPtr<const CPDF_Dictionary> pLabels = pPDFRoot->GetDictFor("PageLabels");
   if (!pLabels)
-    return {};
+    return absl::nullopt;
 
-  CPDF_NumberTree numberTree(pLabels);
-  const CPDF_Object* pValue = nullptr;
+  CPDF_NumberTree numberTree(std::move(pLabels));
+  RetainPtr<const CPDF_Object> pValue;
   int n = nPage;
   while (n >= 0) {
     pValue = numberTree.LookupValue(n);
@@ -103,20 +106,19 @@
     n--;
   }
 
-  WideString label;
   if (pValue) {
     pValue = pValue->GetDirect();
     if (const CPDF_Dictionary* pLabel = pValue->AsDictionary()) {
+      WideString label;
       if (pLabel->KeyExist("P"))
         label += pLabel->GetUnicodeTextFor("P");
 
-      ByteString bsNumberingStyle = pLabel->GetStringFor("S", ByteString());
+      ByteString bsNumberingStyle = pLabel->GetByteStringFor("S", ByteString());
       int nLabelNum = nPage - n + pLabel->GetIntegerFor("St", 1);
       WideString wsNumPortion = GetLabelNumPortion(nLabelNum, bsNumberingStyle);
       label += wsNumPortion;
-      return {label};
+      return label;
     }
   }
-  label = WideString::Format(L"%d", nPage + 1);
-  return {label};
+  return WideString::FormatInteger(nPage + 1);
 }
diff --git a/core/fpdfdoc/cpdf_pagelabel.h b/core/fpdfdoc/cpdf_pagelabel.h
index 473485d..05ec237 100644
--- a/core/fpdfdoc/cpdf_pagelabel.h
+++ b/core/fpdfdoc/cpdf_pagelabel.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,9 @@
 #ifndef CORE_FPDFDOC_CPDF_PAGELABEL_H_
 #define CORE_FPDFDOC_CPDF_PAGELABEL_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "third_party/base/optional.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CPDF_Document;
 
@@ -17,7 +18,7 @@
   explicit CPDF_PageLabel(CPDF_Document* pDocument);
   ~CPDF_PageLabel();
 
-  Optional<WideString> GetLabel(int nPage) const;
+  absl::optional<WideString> GetLabel(int nPage) const;
 
  private:
   UnownedPtr<CPDF_Document> const m_pDocument;
diff --git a/core/fpdfdoc/cpdf_structelement.cpp b/core/fpdfdoc/cpdf_structelement.cpp
index 40477b4..95e887f 100644
--- a/core/fpdfdoc/cpdf_structelement.cpp
+++ b/core/fpdfdoc/cpdf_structelement.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,46 +16,66 @@
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfdoc/cpdf_structtree.h"
+#include "third_party/base/check.h"
 
-namespace {
+CPDF_StructElement::Kid::Kid() = default;
 
-ByteString GetStructElementType(CPDF_StructTree* pTree,
-                                const CPDF_Dictionary* pDict) {
-  ByteString type = pDict->GetStringFor("S");
-  if (pTree->GetRoleMap()) {
-    ByteString mapped = pTree->GetRoleMap()->GetStringFor(type);
-    if (!mapped.IsEmpty())
-      type = std::move(mapped);
-  }
-  return type;
-}
+CPDF_StructElement::Kid::Kid(const Kid& that) = default;
 
-}  // namespace
+CPDF_StructElement::Kid::~Kid() = default;
 
-CPDF_StructKid::CPDF_StructKid() = default;
-
-CPDF_StructKid::CPDF_StructKid(const CPDF_StructKid& that) = default;
-
-CPDF_StructKid::~CPDF_StructKid() = default;
-
-CPDF_StructElement::CPDF_StructElement(CPDF_StructTree* pTree,
-                                       CPDF_StructElement* pParent,
-                                       const CPDF_Dictionary* pDict)
+CPDF_StructElement::CPDF_StructElement(const CPDF_StructTree* pTree,
+                                       RetainPtr<const CPDF_Dictionary> pDict)
     : m_pTree(pTree),
-      m_pParent(pParent),
-      m_pDict(pDict),
-      m_Type(GetStructElementType(m_pTree.Get(), m_pDict.Get())) {
-  LoadKids(m_pDict.Get());
+      m_pDict(std::move(pDict)),
+      m_Type(m_pTree->GetRoleMapNameFor(m_pDict->GetNameFor("S"))) {
+  LoadKids();
 }
 
-CPDF_StructElement::~CPDF_StructElement() = default;
+CPDF_StructElement::~CPDF_StructElement() {
+  for (auto& kid : m_Kids) {
+    if (kid.m_Type == Kid::kElement && kid.m_pElement) {
+      kid.m_pElement->SetParent(nullptr);
+    }
+  }
+}
+
+ByteString CPDF_StructElement::GetObjType() const {
+  return m_pDict->GetByteStringFor("Type");
+}
 
 WideString CPDF_StructElement::GetAltText() const {
-  return GetDict()->GetUnicodeTextFor("Alt");
+  return m_pDict->GetUnicodeTextFor("Alt");
+}
+
+WideString CPDF_StructElement::GetActualText() const {
+  return m_pDict->GetUnicodeTextFor("ActualText");
 }
 
 WideString CPDF_StructElement::GetTitle() const {
-  return GetDict()->GetUnicodeTextFor("T");
+  return m_pDict->GetUnicodeTextFor("T");
+}
+
+absl::optional<WideString> CPDF_StructElement::GetID() const {
+  RetainPtr<const CPDF_Object> obj = m_pDict->GetObjectFor("ID");
+  if (!obj || !obj->IsString())
+    return absl::nullopt;
+  return obj->GetUnicodeText();
+}
+
+absl::optional<WideString> CPDF_StructElement::GetLang() const {
+  RetainPtr<const CPDF_Object> obj = m_pDict->GetObjectFor("Lang");
+  if (!obj || !obj->IsString())
+    return absl::nullopt;
+  return obj->GetUnicodeText();
+}
+
+RetainPtr<const CPDF_Object> CPDF_StructElement::GetA() const {
+  return m_pDict->GetObjectFor("A");
+}
+
+RetainPtr<const CPDF_Object> CPDF_StructElement::GetK() const {
+  return m_pDict->GetObjectFor("K");
 }
 
 size_t CPDF_StructElement::CountKids() const {
@@ -63,82 +83,93 @@
 }
 
 CPDF_StructElement* CPDF_StructElement::GetKidIfElement(size_t index) const {
-  return m_Kids[index].m_Type == CPDF_StructKid::kElement
-             ? m_Kids[index].m_pElement.Get()
-             : nullptr;
+  return m_Kids[index].m_Type == Kid::kElement ? m_Kids[index].m_pElement.Get()
+                                               : nullptr;
 }
 
-void CPDF_StructElement::LoadKids(const CPDF_Dictionary* pDict) {
-  const CPDF_Object* pObj = pDict->GetObjectFor("Pg");
-  uint32_t PageObjNum = 0;
-  if (const CPDF_Reference* pRef = ToReference(pObj))
-    PageObjNum = pRef->GetRefObjNum();
+bool CPDF_StructElement::UpdateKidIfElement(const CPDF_Dictionary* pDict,
+                                            CPDF_StructElement* pElement) {
+  bool bSave = false;
+  for (auto& kid : m_Kids) {
+    if (kid.m_Type == Kid::kElement && kid.m_pDict == pDict) {
+      kid.m_pElement.Reset(pElement);
+      bSave = true;
+    }
+  }
+  return bSave;
+}
 
-  const CPDF_Object* pKids = pDict->GetDirectObjectFor("K");
+void CPDF_StructElement::LoadKids() {
+  RetainPtr<const CPDF_Object> pObj = m_pDict->GetObjectFor("Pg");
+  const CPDF_Reference* pRef = ToReference(pObj.Get());
+  const uint32_t page_obj_num = pRef ? pRef->GetRefObjNum() : 0;
+  RetainPtr<const CPDF_Object> pKids = m_pDict->GetDirectObjectFor("K");
   if (!pKids)
     return;
 
-  m_Kids.clear();
+  DCHECK(m_Kids.empty());
   if (const CPDF_Array* pArray = pKids->AsArray()) {
     m_Kids.resize(pArray->size());
-    for (uint32_t i = 0; i < pArray->size(); i++) {
-      const CPDF_Object* pKid = pArray->GetDirectObjectAt(i);
-      LoadKid(PageObjNum, pKid, &m_Kids[i]);
+    for (size_t i = 0; i < pArray->size(); ++i) {
+      LoadKid(page_obj_num, pArray->GetDirectObjectAt(i), m_Kids[i]);
     }
     return;
   }
 
   m_Kids.resize(1);
-  LoadKid(PageObjNum, pKids, &m_Kids[0]);
+  LoadKid(page_obj_num, std::move(pKids), m_Kids[0]);
 }
 
-void CPDF_StructElement::LoadKid(uint32_t PageObjNum,
-                                 const CPDF_Object* pKidObj,
-                                 CPDF_StructKid* pKid) {
-  pKid->m_Type = CPDF_StructKid::kInvalid;
+void CPDF_StructElement::LoadKid(uint32_t page_obj_num,
+                                 RetainPtr<const CPDF_Object> pKidObj,
+                                 Kid& kid) {
   if (!pKidObj)
     return;
 
   if (pKidObj->IsNumber()) {
-    if (m_pTree->GetPage()->GetObjNum() != PageObjNum)
+    if (m_pTree->GetPageObjNum() != page_obj_num) {
       return;
+    }
 
-    pKid->m_Type = CPDF_StructKid::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;
   }
 
   const CPDF_Dictionary* pKidDict = pKidObj->AsDictionary();
   if (!pKidDict)
     return;
-  if (const CPDF_Reference* pRef = ToReference(pKidDict->GetObjectFor("Pg")))
-    PageObjNum = pRef->GetRefObjNum();
 
-  ByteString type = pKidDict->GetStringFor("Type");
+  if (RetainPtr<const CPDF_Reference> pRef =
+          ToReference(pKidDict->GetObjectFor("Pg"))) {
+    page_obj_num = pRef->GetRefObjNum();
+  }
+  ByteString type = pKidDict->GetNameFor("Type");
   if ((type == "MCR" || type == "OBJR") &&
-      m_pTree->GetPage()->GetObjNum() != PageObjNum) {
+      m_pTree->GetPageObjNum() != page_obj_num) {
     return;
   }
 
   if (type == "MCR") {
-    pKid->m_Type = CPDF_StructKid::kStreamContent;
-    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_Type = Kid::kStreamContent;
+    RetainPtr<const CPDF_Reference> pRef =
+        ToReference(pKidDict->GetObjectFor("Stm"));
+    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 = CPDF_StructKid::kObject;
-    const CPDF_Reference* pObj = ToReference(pKidDict->GetObjectFor("Obj"));
-    pKid->m_RefObjNum = pObj ? pObj->GetRefObjNum() : 0;
-    pKid->m_PageObjNum = PageObjNum;
+    kid.m_Type = Kid::kObject;
+    RetainPtr<const CPDF_Reference> pObj =
+        ToReference(pKidDict->GetObjectFor("Obj"));
+    kid.m_RefObjNum = pObj ? pObj->GetRefObjNum() : 0;
+    kid.m_PageObjNum = page_obj_num;
     return;
   }
 
-  pKid->m_Type = CPDF_StructKid::kElement;
-  pKid->m_pDict.Reset(pKidDict);
-  pKid->m_pElement = nullptr;
+  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 54042a9..72a0df3 100644
--- a/core/fpdfdoc/cpdf_structelement.h
+++ b/core/fpdfdoc/cpdf_structelement.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,60 +12,66 @@
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CPDF_Dictionary;
 class CPDF_Object;
-class CPDF_StructElement;
 class CPDF_StructTree;
 
-class CPDF_StructKid {
- public:
-  enum Type { kInvalid, kElement, kPageContent, kStreamContent, kObject };
-
-  CPDF_StructKid();
-  CPDF_StructKid(const CPDF_StructKid& that);
-  ~CPDF_StructKid();
-
-  Type m_Type = kInvalid;
-  uint32_t m_PageObjNum = 0;  // For {PageContent, StreamContent, Object} types.
-  uint32_t m_RefObjNum = 0;   // For {StreamContent, Object} types.
-  uint32_t m_ContentId = 0;   // For {PageContent, StreamContent} types.
-  RetainPtr<CPDF_StructElement> m_pElement;  // For Element.
-  RetainPtr<const CPDF_Dictionary> m_pDict;  // For Element.
-};
-
 class CPDF_StructElement final : public Retainable {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   ByteString GetType() const { return m_Type; }
+  ByteString GetObjType() const;
   WideString GetAltText() const;
+  WideString GetActualText() const;
   WideString GetTitle() const;
-
-  // Never returns nullptr.
-  const CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
+  absl::optional<WideString> GetID() const;
+  absl::optional<WideString> GetLang() const;
+  RetainPtr<const CPDF_Object> GetA() const;
+  RetainPtr<const CPDF_Object> GetK() const;
 
   size_t CountKids() const;
   CPDF_StructElement* GetKidIfElement(size_t index) const;
-  std::vector<CPDF_StructKid>* GetKids() { return &m_Kids; }
+  bool UpdateKidIfElement(const CPDF_Dictionary* pDict,
+                          CPDF_StructElement* pElement);
+
+  CPDF_StructElement* GetParent() const { return m_pParentElement; }
+  void SetParent(CPDF_StructElement* pParentElement) {
+    m_pParentElement = pParentElement;
+  }
 
  private:
-  CPDF_StructElement(CPDF_StructTree* pTree,
-                     CPDF_StructElement* pParent,
-                     const CPDF_Dictionary* pDict);
+  struct Kid {
+    enum Type { kInvalid, kElement, kPageContent, kStreamContent, kObject };
+
+    Kid();
+    Kid(const Kid& that);
+    ~Kid();
+
+    Type m_Type = kInvalid;
+    uint32_t m_PageObjNum = 0;  // For {PageContent, StreamContent, Object}.
+    uint32_t m_RefObjNum = 0;   // For {StreamContent, Object} types.
+    uint32_t m_ContentId = 0;   // For {PageContent, StreamContent} types.
+    RetainPtr<CPDF_StructElement> m_pElement;  // For Element type.
+    RetainPtr<const CPDF_Dictionary> m_pDict;  // For Element type.
+  };
+
+  CPDF_StructElement(const CPDF_StructTree* pTree,
+                     RetainPtr<const CPDF_Dictionary> pDict);
   ~CPDF_StructElement() override;
 
-  void LoadKids(const CPDF_Dictionary* pDict);
-  void LoadKid(uint32_t PageObjNum,
-               const CPDF_Object* pKidObj,
-               CPDF_StructKid* pKid);
+  void LoadKids();
+  void LoadKid(uint32_t page_obj_num,
+               RetainPtr<const CPDF_Object> pKidObj,
+               Kid& kid);
 
-  UnownedPtr<CPDF_StructTree> const m_pTree;
-  UnownedPtr<CPDF_StructElement> const m_pParent;
+  UnownedPtr<const CPDF_StructTree> const m_pTree;
   RetainPtr<const CPDF_Dictionary> const m_pDict;
+  UnownedPtr<CPDF_StructElement> m_pParentElement;
   const ByteString m_Type;
-  std::vector<CPDF_StructKid> m_Kids;
+  std::vector<Kid> m_Kids;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_STRUCTELEMENT_H_
diff --git a/core/fpdfdoc/cpdf_structtree.cpp b/core/fpdfdoc/cpdf_structtree.cpp
index 8fd43e2..2b00117 100644
--- a/core/fpdfdoc/cpdf_structtree.cpp
+++ b/core/fpdfdoc/cpdf_structtree.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include "core/fpdfdoc/cpdf_structtree.h"
 
+#include <utility>
+
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
@@ -13,13 +15,12 @@
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfdoc/cpdf_numbertree.h"
 #include "core/fpdfdoc/cpdf_structelement.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
 bool IsTagged(const CPDF_Document* pDoc) {
-  const CPDF_Dictionary* pCatalog = pDoc->GetRoot();
-  const CPDF_Dictionary* pMarkInfo = pCatalog->GetDictFor("MarkInfo");
+  RetainPtr<const CPDF_Dictionary> pMarkInfo =
+      pDoc->GetRoot()->GetDictFor("MarkInfo");
   return pMarkInfo && pMarkInfo->GetIntegerFor("Marked");
 }
 
@@ -28,12 +29,12 @@
 // static
 std::unique_ptr<CPDF_StructTree> CPDF_StructTree::LoadPage(
     const CPDF_Document* pDoc,
-    const CPDF_Dictionary* pPageDict) {
+    RetainPtr<const CPDF_Dictionary> pPageDict) {
   if (!IsTagged(pDoc))
     return nullptr;
 
-  auto pTree = pdfium::MakeUnique<CPDF_StructTree>(pDoc);
-  pTree->LoadPageTree(pPageDict);
+  auto pTree = std::make_unique<CPDF_StructTree>(pDoc);
+  pTree->LoadPageTree(std::move(pPageDict));
   return pTree;
 }
 
@@ -43,47 +44,60 @@
 
 CPDF_StructTree::~CPDF_StructTree() = default;
 
-void CPDF_StructTree::LoadPageTree(const CPDF_Dictionary* pPageDict) {
-  m_pPage.Reset(pPageDict);
+ByteString CPDF_StructTree::GetRoleMapNameFor(const ByteString& type) const {
+  if (m_pRoleMap) {
+    ByteString mapped = m_pRoleMap->GetNameFor(type);
+    if (!mapped.IsEmpty())
+      return mapped;
+  }
+  return type;
+}
+
+void CPDF_StructTree::LoadPageTree(RetainPtr<const CPDF_Dictionary> pPageDict) {
+  m_pPage = std::move(pPageDict);
   if (!m_pTreeRoot)
     return;
 
-  const CPDF_Object* pKids = m_pTreeRoot->GetDirectObjectFor("K");
+  RetainPtr<const CPDF_Object> pKids = m_pTreeRoot->GetDirectObjectFor("K");
   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 = pArray->size();
+    kids_count = pArray->size();
   else
     return;
 
   m_Kids.clear();
-  m_Kids.resize(dwKids);
-  const CPDF_Dictionary* pParentTree = m_pTreeRoot->GetDictFor("ParentTree");
+  m_Kids.resize(kids_count);
+
+  RetainPtr<const CPDF_Dictionary> pParentTree =
+      m_pTreeRoot->GetDictFor("ParentTree");
   if (!pParentTree)
     return;
 
-  CPDF_NumberTree parent_tree(pParentTree);
-  int parents_id = pPageDict->GetIntegerFor("StructParents", -1);
+  CPDF_NumberTree parent_tree(std::move(pParentTree));
+  int parents_id = m_pPage->GetIntegerFor("StructParents", -1);
   if (parents_id < 0)
     return;
 
-  const CPDF_Array* pParentArray = ToArray(parent_tree.LookupValue(parents_id));
+  RetainPtr<const CPDF_Array> pParentArray =
+      ToArray(parent_tree.LookupValue(parents_id));
   if (!pParentArray)
     return;
 
   StructElementMap element_map;
   for (size_t i = 0; i < pParentArray->size(); i++) {
-    if (const CPDF_Dictionary* pParent = pParentArray->GetDictAt(i))
-      AddPageNode(pParent, &element_map, 0);
+    RetainPtr<const CPDF_Dictionary> pParent = pParentArray->GetDictAt(i);
+    if (pParent)
+      AddPageNode(std::move(pParent), &element_map, 0);
   }
 }
 
 RetainPtr<CPDF_StructElement> CPDF_StructTree::AddPageNode(
-    const CPDF_Dictionary* pDict,
+    RetainPtr<const CPDF_Dictionary> pDict,
     StructElementMap* map,
     int nLevel) {
   static constexpr int kStructTreeMaxRecursion = 32;
@@ -94,33 +108,34 @@
   if (it != map->end())
     return it->second;
 
-  auto pElement = pdfium::MakeRetain<CPDF_StructElement>(this, nullptr, pDict);
-  (*map)[pDict] = pElement;
-  const CPDF_Dictionary* pParent = pDict->GetDictFor("P");
-  if (!pParent || pParent->GetStringFor("Type") == "StructTreeRoot") {
+  RetainPtr<const CPDF_Dictionary> key(pDict);
+  auto pElement = pdfium::MakeRetain<CPDF_StructElement>(this, pDict);
+  (*map)[key] = pElement;
+  RetainPtr<const CPDF_Dictionary> pParent = pDict->GetDictFor("P");
+  if (!pParent || pParent->GetNameFor("Type") == "StructTreeRoot") {
     if (!AddTopLevelNode(pDict, pElement))
-      map->erase(pDict);
+      map->erase(key);
     return pElement;
   }
 
   RetainPtr<CPDF_StructElement> pParentElement =
-      AddPageNode(pParent, map, nLevel + 1);
-  bool bSave = false;
-  for (CPDF_StructKid& kid : *pParentElement->GetKids()) {
-    if (kid.m_Type == CPDF_StructKid::kElement && kid.m_pDict == pDict) {
-      kid.m_pElement = pElement;
-      bSave = true;
-    }
+      AddPageNode(std::move(pParent), map, nLevel + 1);
+  if (!pParentElement)
+    return pElement;
+
+  if (!pParentElement->UpdateKidIfElement(pDict, pElement.Get())) {
+    map->erase(key);
+    return pElement;
   }
-  if (!bSave)
-    map->erase(pDict);
+
+  pElement->SetParent(pParentElement.Get());
   return pElement;
 }
 
 bool CPDF_StructTree::AddTopLevelNode(
     const CPDF_Dictionary* pDict,
     const RetainPtr<CPDF_StructElement>& pElement) {
-  const CPDF_Object* pObj = m_pTreeRoot->GetDirectObjectFor("K");
+  RetainPtr<const CPDF_Object> pObj = m_pTreeRoot->GetDirectObjectFor("K");
   if (!pObj)
     return false;
 
@@ -136,7 +151,8 @@
 
   bool bSave = false;
   for (size_t i = 0; i < pTopKids->size(); i++) {
-    const CPDF_Reference* pKidRef = ToReference(pTopKids->GetObjectAt(i));
+    RetainPtr<const CPDF_Reference> pKidRef =
+        ToReference(pTopKids->GetObjectAt(i));
     if (pKidRef && pKidRef->GetRefObjNum() == pDict->GetObjNum()) {
       m_Kids[i] = pElement;
       bSave = true;
diff --git a/core/fpdfdoc/cpdf_structtree.h b/core/fpdfdoc/cpdf_structtree.h
index b0eafba..8cca8b2 100644
--- a/core/fpdfdoc/cpdf_structtree.h
+++ b/core/fpdfdoc/cpdf_structtree.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,15 @@
 #ifndef CORE_FPDFDOC_CPDF_STRUCTTREE_H_
 #define CORE_FPDFDOC_CPDF_STRUCTTREE_H_
 
+#include <functional>
 #include <map>
 #include <memory>
 #include <vector>
 
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/retain_ptr.h"
 
-class CPDF_Dictionary;
 class CPDF_Document;
 class CPDF_StructElement;
 
@@ -21,25 +23,26 @@
  public:
   static std::unique_ptr<CPDF_StructTree> LoadPage(
       const CPDF_Document* pDoc,
-      const CPDF_Dictionary* pPageDict);
+      RetainPtr<const CPDF_Dictionary> pPageDict);
 
   explicit CPDF_StructTree(const CPDF_Document* pDoc);
   ~CPDF_StructTree();
 
   size_t CountTopElements() const { return m_Kids.size(); }
   CPDF_StructElement* GetTopElement(size_t i) const { return m_Kids[i].Get(); }
-  const CPDF_Dictionary* GetRoleMap() const { return m_pRoleMap.Get(); }
-  const CPDF_Dictionary* GetPage() const { return m_pPage.Get(); }
-  const CPDF_Dictionary* GetTreeRoot() const { return m_pTreeRoot.Get(); }
+  uint32_t GetPageObjNum() const { return m_pPage->GetObjNum(); }
+  ByteString GetRoleMapNameFor(const ByteString& type) const;
 
  private:
-  using StructElementMap =
-      std::map<const CPDF_Dictionary*, RetainPtr<CPDF_StructElement>>;
+  using StructElementMap = std::map<RetainPtr<const CPDF_Dictionary>,
+                                    RetainPtr<CPDF_StructElement>,
+                                    std::less<>>;
 
-  void LoadPageTree(const CPDF_Dictionary* pPageDict);
-  RetainPtr<CPDF_StructElement> AddPageNode(const CPDF_Dictionary* pDict,
-                                            StructElementMap* map,
-                                            int nLevel);
+  void LoadPageTree(RetainPtr<const CPDF_Dictionary> pPageDict);
+  RetainPtr<CPDF_StructElement> AddPageNode(
+      RetainPtr<const CPDF_Dictionary> pDict,
+      StructElementMap* map,
+      int nLevel);
   bool AddTopLevelNode(const CPDF_Dictionary* pDict,
                        const RetainPtr<CPDF_StructElement>& pElement);
 
diff --git a/core/fpdfdoc/cpdf_variabletext.cpp b/core/fpdfdoc/cpdf_variabletext.cpp
deleted file mode 100644
index d7d2265..0000000
--- a/core/fpdfdoc/cpdf_variabletext.cpp
+++ /dev/null
@@ -1,937 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fpdfdoc/cpdf_variabletext.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "core/fpdfapi/font/cpdf_font.h"
-#include "core/fpdfdoc/cline.h"
-#include "core/fpdfdoc/cpvt_word.h"
-#include "core/fpdfdoc/cpvt_wordinfo.h"
-#include "core/fpdfdoc/csection.h"
-#include "core/fpdfdoc/ipvt_fontmap.h"
-#include "core/fxcrt/fx_codepage.h"
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-namespace {
-
-const float kFontScale = 0.001f;
-const uint8_t kReturnLength = 1;
-
-const uint8_t gFontSizeSteps[] = {4,  6,  8,   9,   10,  12,  14, 18, 20,
-                                  25, 30, 35,  40,  45,  50,  55, 60, 70,
-                                  80, 90, 100, 110, 120, 130, 144};
-
-}  // namespace
-
-CPDF_VariableText::Provider::Provider(IPVT_FontMap* pFontMap)
-    : m_pFontMap(pFontMap) {
-  ASSERT(m_pFontMap);
-}
-
-CPDF_VariableText::Provider::~Provider() {}
-
-uint32_t CPDF_VariableText::Provider::GetCharWidth(int32_t nFontIndex,
-                                                   uint16_t word) {
-  RetainPtr<CPDF_Font> pPDFFont = m_pFontMap->GetPDFFont(nFontIndex);
-  if (!pPDFFont)
-    return 0;
-
-  uint32_t charcode = pPDFFont->CharCodeFromUnicode(word);
-  if (charcode == CPDF_Font::kInvalidCharCode)
-    return 0;
-
-  return pPDFFont->GetCharWidthF(charcode);
-}
-
-int32_t CPDF_VariableText::Provider::GetTypeAscent(int32_t nFontIndex) {
-  RetainPtr<CPDF_Font> pPDFFont = m_pFontMap->GetPDFFont(nFontIndex);
-  return pPDFFont ? pPDFFont->GetTypeAscent() : 0;
-}
-
-int32_t CPDF_VariableText::Provider::GetTypeDescent(int32_t nFontIndex) {
-  RetainPtr<CPDF_Font> pPDFFont = m_pFontMap->GetPDFFont(nFontIndex);
-  return pPDFFont ? pPDFFont->GetTypeDescent() : 0;
-}
-
-int32_t CPDF_VariableText::Provider::GetWordFontIndex(uint16_t word,
-                                                      int32_t charset,
-                                                      int32_t nFontIndex) {
-  if (RetainPtr<CPDF_Font> pDefFont = m_pFontMap->GetPDFFont(0)) {
-    if (pDefFont->CharCodeFromUnicode(word) != CPDF_Font::kInvalidCharCode)
-      return 0;
-  }
-  if (RetainPtr<CPDF_Font> pSysFont = m_pFontMap->GetPDFFont(1)) {
-    if (pSysFont->CharCodeFromUnicode(word) != CPDF_Font::kInvalidCharCode)
-      return 1;
-  }
-  return -1;
-}
-
-bool CPDF_VariableText::Provider::IsLatinWord(uint16_t word) {
-  return (word >= 0x61 && word <= 0x7A) || (word >= 0x41 && word <= 0x5A) ||
-         word == 0x2D || word == 0x27;
-}
-
-int32_t CPDF_VariableText::Provider::GetDefaultFontIndex() {
-  return 0;
-}
-
-CPDF_VariableText::Iterator::Iterator(CPDF_VariableText* pVT)
-    : m_CurPos(-1, -1, -1), m_pVT(pVT) {}
-
-CPDF_VariableText::Iterator::~Iterator() {}
-
-void CPDF_VariableText::Iterator::SetAt(int32_t nWordIndex) {
-  m_CurPos = m_pVT->WordIndexToWordPlace(nWordIndex);
-}
-
-void CPDF_VariableText::Iterator::SetAt(const CPVT_WordPlace& place) {
-  ASSERT(m_pVT);
-  m_CurPos = place;
-}
-
-bool CPDF_VariableText::Iterator::NextWord() {
-  if (m_CurPos == m_pVT->GetEndWordPlace())
-    return false;
-
-  m_CurPos = m_pVT->GetNextWordPlace(m_CurPos);
-  return true;
-}
-
-bool CPDF_VariableText::Iterator::PrevWord() {
-  if (m_CurPos == m_pVT->GetBeginWordPlace())
-    return false;
-
-  m_CurPos = m_pVT->GetPrevWordPlace(m_CurPos);
-  return true;
-}
-
-bool CPDF_VariableText::Iterator::NextLine() {
-  if (!pdfium::IndexInBounds(m_pVT->m_SectionArray, m_CurPos.nSecIndex))
-    return false;
-
-  CSection* pSection = m_pVT->m_SectionArray[m_CurPos.nSecIndex].get();
-  if (m_CurPos.nLineIndex <
-      pdfium::CollectionSize<int32_t>(pSection->m_LineArray) - 1) {
-    m_CurPos = CPVT_WordPlace(m_CurPos.nSecIndex, m_CurPos.nLineIndex + 1, -1);
-    return true;
-  }
-  if (m_CurPos.nSecIndex <
-      pdfium::CollectionSize<int32_t>(m_pVT->m_SectionArray) - 1) {
-    m_CurPos = CPVT_WordPlace(m_CurPos.nSecIndex + 1, 0, -1);
-    return true;
-  }
-  return false;
-}
-
-bool CPDF_VariableText::Iterator::GetWord(CPVT_Word& word) const {
-  word.WordPlace = m_CurPos;
-  if (!pdfium::IndexInBounds(m_pVT->m_SectionArray, m_CurPos.nSecIndex))
-    return false;
-
-  CSection* pSection = m_pVT->m_SectionArray[m_CurPos.nSecIndex].get();
-  if (!pdfium::IndexInBounds(pSection->m_LineArray, m_CurPos.nLineIndex) ||
-      !pdfium::IndexInBounds(pSection->m_WordArray, m_CurPos.nWordIndex)) {
-    return false;
-  }
-
-  CPVT_WordInfo* pWord = pSection->m_WordArray[m_CurPos.nWordIndex].get();
-  word.Word = pWord->Word;
-  word.nCharset = pWord->nCharset;
-  word.fWidth = m_pVT->GetWordWidth(*pWord);
-  word.ptWord =
-      m_pVT->InToOut(CFX_PointF(pWord->fWordX + pSection->m_Rect.left,
-                                pWord->fWordY + pSection->m_Rect.top));
-  word.fAscent = m_pVT->GetWordAscent(*pWord);
-  word.fDescent = m_pVT->GetWordDescent(*pWord);
-  word.nFontIndex = m_pVT->GetWordFontIndex(*pWord);
-  word.fFontSize = m_pVT->GetWordFontSize();
-  return true;
-}
-
-bool CPDF_VariableText::Iterator::GetLine(CPVT_Line& line) const {
-  ASSERT(m_pVT);
-  line.lineplace = CPVT_WordPlace(m_CurPos.nSecIndex, m_CurPos.nLineIndex, -1);
-  if (!pdfium::IndexInBounds(m_pVT->m_SectionArray, m_CurPos.nSecIndex))
-    return false;
-
-  CSection* pSection = m_pVT->m_SectionArray[m_CurPos.nSecIndex].get();
-  if (!pdfium::IndexInBounds(pSection->m_LineArray, m_CurPos.nLineIndex))
-    return false;
-
-  CLine* pLine = pSection->m_LineArray[m_CurPos.nLineIndex].get();
-  line.ptLine = m_pVT->InToOut(
-      CFX_PointF(pLine->m_LineInfo.fLineX + pSection->m_Rect.left,
-                 pLine->m_LineInfo.fLineY + pSection->m_Rect.top));
-  line.fLineWidth = pLine->m_LineInfo.fLineWidth;
-  line.fLineAscent = pLine->m_LineInfo.fLineAscent;
-  line.fLineDescent = pLine->m_LineInfo.fLineDescent;
-  line.lineEnd = pLine->GetEndWordPlace();
-  return true;
-}
-
-CPDF_VariableText::CPDF_VariableText() = default;
-
-CPDF_VariableText::~CPDF_VariableText() = default;
-
-void CPDF_VariableText::Initialize() {
-  if (m_bInitialized)
-    return;
-
-  CPVT_WordPlace place;
-  place.nSecIndex = 0;
-  AddSection(place);
-
-  CPVT_LineInfo lineinfo;
-  lineinfo.fLineAscent = GetFontAscent(GetDefaultFontIndex(), GetFontSize());
-  lineinfo.fLineDescent = GetFontDescent(GetDefaultFontIndex(), GetFontSize());
-  AddLine(place, lineinfo);
-
-  if (!m_SectionArray.empty())
-    m_SectionArray.front()->ResetLinePlace();
-
-  m_bInitialized = true;
-}
-
-CPVT_WordPlace CPDF_VariableText::InsertWord(const CPVT_WordPlace& place,
-                                             uint16_t word,
-                                             int32_t charset) {
-  int32_t nTotalWords = GetTotalWords();
-  if (m_nLimitChar > 0 && nTotalWords >= m_nLimitChar)
-    return place;
-  if (m_nCharArray > 0 && nTotalWords >= m_nCharArray)
-    return place;
-
-  CPVT_WordPlace newplace = place;
-  newplace.nWordIndex++;
-  int32_t nFontIndex =
-      GetSubWord() > 0 ? GetDefaultFontIndex()
-                       : GetWordFontIndex(word, charset, GetDefaultFontIndex());
-  return AddWord(newplace, CPVT_WordInfo(word, charset, nFontIndex));
-}
-
-CPVT_WordPlace CPDF_VariableText::InsertSection(const CPVT_WordPlace& place) {
-  int32_t nTotalWords = GetTotalWords();
-  if (m_nLimitChar > 0 && nTotalWords >= m_nLimitChar)
-    return place;
-  if (m_nCharArray > 0 && nTotalWords >= m_nCharArray)
-    return place;
-  if (!m_bMultiLine)
-    return place;
-
-  CPVT_WordPlace wordplace = place;
-  UpdateWordPlace(wordplace);
-  if (!pdfium::IndexInBounds(m_SectionArray, wordplace.nSecIndex))
-    return place;
-
-  CSection* pSection = m_SectionArray[wordplace.nSecIndex].get();
-  CPVT_WordPlace NewPlace(wordplace.nSecIndex + 1, 0, -1);
-  AddSection(NewPlace);
-  CPVT_WordPlace result = NewPlace;
-  if (pdfium::IndexInBounds(m_SectionArray, NewPlace.nSecIndex)) {
-    CSection* pNewSection = m_SectionArray[NewPlace.nSecIndex].get();
-    for (int32_t w = wordplace.nWordIndex + 1;
-         w < pdfium::CollectionSize<int32_t>(pSection->m_WordArray); ++w) {
-      NewPlace.nWordIndex++;
-      pNewSection->AddWord(NewPlace, *pSection->m_WordArray[w]);
-    }
-  }
-  ClearSectionRightWords(wordplace);
-  return result;
-}
-
-CPVT_WordPlace CPDF_VariableText::DeleteWords(
-    const CPVT_WordRange& PlaceRange) {
-  bool bLastSecPos =
-      pdfium::IndexInBounds(m_SectionArray, PlaceRange.EndPos.nSecIndex) &&
-      PlaceRange.EndPos ==
-          m_SectionArray[PlaceRange.EndPos.nSecIndex]->GetEndWordPlace();
-
-  ClearWords(PlaceRange);
-  if (PlaceRange.BeginPos.nSecIndex != PlaceRange.EndPos.nSecIndex) {
-    ClearEmptySections(PlaceRange);
-    if (!bLastSecPos)
-      LinkLatterSection(PlaceRange.BeginPos);
-  }
-  return PlaceRange.BeginPos;
-}
-
-CPVT_WordPlace CPDF_VariableText::DeleteWord(const CPVT_WordPlace& place) {
-  return ClearRightWord(AdjustLineHeader(place, true));
-}
-
-CPVT_WordPlace CPDF_VariableText::BackSpaceWord(const CPVT_WordPlace& place) {
-  return ClearLeftWord(AdjustLineHeader(place, true));
-}
-
-void CPDF_VariableText::SetText(const WideString& swText) {
-  DeleteWords(CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace()));
-  CPVT_WordPlace wp(0, 0, -1);
-  if (!m_SectionArray.empty())
-    m_SectionArray.front()->m_Rect = CPVT_FloatRect();
-
-  int32_t nCharCount = 0;
-  for (int32_t i = 0, sz = swText.GetLength(); i < sz; i++) {
-    if (m_nLimitChar > 0 && nCharCount >= m_nLimitChar)
-      break;
-    if (m_nCharArray > 0 && nCharCount >= m_nCharArray)
-      break;
-
-    uint16_t word = swText[i];
-    switch (word) {
-      case 0x0D:
-        if (m_bMultiLine) {
-          if (i + 1 < sz && swText[i + 1] == 0x0A)
-            i++;
-          wp.AdvanceSection();
-          AddSection(wp);
-        }
-        break;
-      case 0x0A:
-        if (m_bMultiLine) {
-          if (i + 1 < sz && swText[i + 1] == 0x0D)
-            i++;
-          wp.AdvanceSection();
-          AddSection(wp);
-        }
-        break;
-      case 0x09:
-        word = 0x20;
-        FALLTHROUGH;
-      default:
-        wp = InsertWord(wp, word, FX_CHARSET_Default);
-        break;
-    }
-    nCharCount++;
-  }
-}
-
-void CPDF_VariableText::UpdateWordPlace(CPVT_WordPlace& place) const {
-  if (place.nSecIndex < 0)
-    place = GetBeginWordPlace();
-  if (place.nSecIndex >= pdfium::CollectionSize<int32_t>(m_SectionArray))
-    place = GetEndWordPlace();
-
-  place = AdjustLineHeader(place, true);
-  if (pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
-    m_SectionArray[place.nSecIndex]->UpdateWordPlace(place);
-}
-
-int32_t CPDF_VariableText::WordPlaceToWordIndex(
-    const CPVT_WordPlace& place) const {
-  CPVT_WordPlace newplace = place;
-  UpdateWordPlace(newplace);
-  int32_t nIndex = 0;
-  int32_t i = 0;
-  int32_t sz = 0;
-  for (i = 0, sz = pdfium::CollectionSize<int32_t>(m_SectionArray);
-       i < sz && i < newplace.nSecIndex; i++) {
-    CSection* pSection = m_SectionArray[i].get();
-    nIndex += pdfium::CollectionSize<int32_t>(pSection->m_WordArray);
-    if (i != sz - 1)
-      nIndex += kReturnLength;
-  }
-  if (pdfium::IndexInBounds(m_SectionArray, i))
-    nIndex += newplace.nWordIndex + kReturnLength;
-  return nIndex;
-}
-
-CPVT_WordPlace CPDF_VariableText::WordIndexToWordPlace(int32_t index) const {
-  CPVT_WordPlace place = GetBeginWordPlace();
-  int32_t nOldIndex = 0;
-  int32_t nIndex = 0;
-  bool bFound = false;
-  for (int32_t i = 0, sz = pdfium::CollectionSize<int32_t>(m_SectionArray);
-       i < sz; i++) {
-    CSection* pSection = m_SectionArray[i].get();
-    nIndex += pdfium::CollectionSize<int32_t>(pSection->m_WordArray);
-    if (nIndex == index) {
-      place = pSection->GetEndWordPlace();
-      bFound = true;
-      break;
-    }
-    if (nIndex > index) {
-      place.nSecIndex = i;
-      place.nWordIndex = index - nOldIndex - 1;
-      pSection->UpdateWordPlace(place);
-      bFound = true;
-      break;
-    }
-    if (i != sz - 1)
-      nIndex += kReturnLength;
-    nOldIndex = nIndex;
-  }
-  if (!bFound)
-    place = GetEndWordPlace();
-  return place;
-}
-
-CPVT_WordPlace CPDF_VariableText::GetBeginWordPlace() const {
-  return m_bInitialized ? CPVT_WordPlace(0, 0, -1) : CPVT_WordPlace();
-}
-
-CPVT_WordPlace CPDF_VariableText::GetEndWordPlace() const {
-  if (m_SectionArray.empty())
-    return CPVT_WordPlace();
-  return m_SectionArray.back()->GetEndWordPlace();
-}
-
-CPVT_WordPlace CPDF_VariableText::GetPrevWordPlace(
-    const CPVT_WordPlace& place) const {
-  if (place.nSecIndex < 0)
-    return GetBeginWordPlace();
-  if (place.nSecIndex >= pdfium::CollectionSize<int32_t>(m_SectionArray))
-    return GetEndWordPlace();
-
-  CSection* pSection = m_SectionArray[place.nSecIndex].get();
-  if (place > pSection->GetBeginWordPlace())
-    return pSection->GetPrevWordPlace(place);
-  if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex - 1))
-    return GetBeginWordPlace();
-  return m_SectionArray[place.nSecIndex - 1]->GetEndWordPlace();
-}
-
-CPVT_WordPlace CPDF_VariableText::GetNextWordPlace(
-    const CPVT_WordPlace& place) const {
-  if (place.nSecIndex < 0)
-    return GetBeginWordPlace();
-  if (place.nSecIndex >= pdfium::CollectionSize<int32_t>(m_SectionArray))
-    return GetEndWordPlace();
-
-  CSection* pSection = m_SectionArray[place.nSecIndex].get();
-  if (place < pSection->GetEndWordPlace())
-    return pSection->GetNextWordPlace(place);
-  if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex + 1))
-    return GetEndWordPlace();
-  return m_SectionArray[place.nSecIndex + 1]->GetBeginWordPlace();
-}
-
-CPVT_WordPlace CPDF_VariableText::SearchWordPlace(
-    const CFX_PointF& point) const {
-  CFX_PointF pt = OutToIn(point);
-  CPVT_WordPlace place = GetBeginWordPlace();
-  int32_t nLeft = 0;
-  int32_t nRight = pdfium::CollectionSize<int32_t>(m_SectionArray) - 1;
-  int32_t nMid = pdfium::CollectionSize<int32_t>(m_SectionArray) / 2;
-  bool bUp = true;
-  bool bDown = true;
-  while (nLeft <= nRight) {
-    if (!pdfium::IndexInBounds(m_SectionArray, nMid))
-      break;
-    CSection* pSection = m_SectionArray[nMid].get();
-    if (IsFloatBigger(pt.y, pSection->m_Rect.top))
-      bUp = false;
-    if (IsFloatBigger(pSection->m_Rect.bottom, pt.y))
-      bDown = false;
-    if (IsFloatSmaller(pt.y, pSection->m_Rect.top)) {
-      nRight = nMid - 1;
-      nMid = (nLeft + nRight) / 2;
-      continue;
-    }
-    if (IsFloatBigger(pt.y, pSection->m_Rect.bottom)) {
-      nLeft = nMid + 1;
-      nMid = (nLeft + nRight) / 2;
-      continue;
-    }
-    place = pSection->SearchWordPlace(
-        CFX_PointF(pt.x - pSection->m_Rect.left, pt.y - pSection->m_Rect.top));
-    place.nSecIndex = nMid;
-    return place;
-  }
-  if (bUp)
-    place = GetBeginWordPlace();
-  if (bDown)
-    place = GetEndWordPlace();
-  return place;
-}
-
-CPVT_WordPlace CPDF_VariableText::GetUpWordPlace(
-    const CPVT_WordPlace& place,
-    const CFX_PointF& point) const {
-  if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
-    return place;
-
-  CSection* pSection = m_SectionArray[place.nSecIndex].get();
-  CPVT_WordPlace temp = place;
-  CFX_PointF pt = OutToIn(point);
-  if (temp.nLineIndex-- > 0) {
-    return pSection->SearchWordPlace(pt.x - pSection->m_Rect.left, temp);
-  }
-  if (temp.nSecIndex-- > 0) {
-    if (pdfium::IndexInBounds(m_SectionArray, temp.nSecIndex)) {
-      CSection* pLastSection = m_SectionArray[temp.nSecIndex].get();
-      temp.nLineIndex =
-          pdfium::CollectionSize<int32_t>(pLastSection->m_LineArray) - 1;
-      return pLastSection->SearchWordPlace(pt.x - pLastSection->m_Rect.left,
-                                           temp);
-    }
-  }
-  return place;
-}
-
-CPVT_WordPlace CPDF_VariableText::GetDownWordPlace(
-    const CPVT_WordPlace& place,
-    const CFX_PointF& point) const {
-  if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
-    return place;
-
-  CSection* pSection = m_SectionArray[place.nSecIndex].get();
-  CPVT_WordPlace temp = place;
-  CFX_PointF pt = OutToIn(point);
-  if (temp.nLineIndex++ <
-      pdfium::CollectionSize<int32_t>(pSection->m_LineArray) - 1) {
-    return pSection->SearchWordPlace(pt.x - pSection->m_Rect.left, temp);
-  }
-  temp.AdvanceSection();
-  if (!pdfium::IndexInBounds(m_SectionArray, temp.nSecIndex))
-    return place;
-
-  return m_SectionArray[temp.nSecIndex]->SearchWordPlace(
-      pt.x - pSection->m_Rect.left, temp);
-}
-
-CPVT_WordPlace CPDF_VariableText::GetLineBeginPlace(
-    const CPVT_WordPlace& place) const {
-  return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, -1);
-}
-
-CPVT_WordPlace CPDF_VariableText::GetLineEndPlace(
-    const CPVT_WordPlace& place) const {
-  if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
-    return place;
-
-  CSection* pSection = m_SectionArray[place.nSecIndex].get();
-  if (!pdfium::IndexInBounds(pSection->m_LineArray, place.nLineIndex))
-    return place;
-
-  return pSection->m_LineArray[place.nLineIndex]->GetEndWordPlace();
-}
-
-CPVT_WordPlace CPDF_VariableText::GetSectionBeginPlace(
-    const CPVT_WordPlace& place) const {
-  return CPVT_WordPlace(place.nSecIndex, 0, -1);
-}
-
-CPVT_WordPlace CPDF_VariableText::GetSectionEndPlace(
-    const CPVT_WordPlace& place) const {
-  if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
-    return place;
-
-  return m_SectionArray[place.nSecIndex]->GetEndWordPlace();
-}
-
-int32_t CPDF_VariableText::GetTotalWords() const {
-  int32_t nTotal = 0;
-  for (const auto& pSection : m_SectionArray) {
-    nTotal +=
-        pdfium::CollectionSize<int32_t>(pSection->m_WordArray) + kReturnLength;
-  }
-  return nTotal - kReturnLength;
-}
-
-CPVT_WordPlace CPDF_VariableText::AddSection(const CPVT_WordPlace& place) {
-  if (IsValid() && !m_bMultiLine)
-    return place;
-
-  int32_t nSecIndex = pdfium::clamp(
-      place.nSecIndex, 0, pdfium::CollectionSize<int32_t>(m_SectionArray));
-
-  auto pSection = pdfium::MakeUnique<CSection>(this);
-  pSection->m_Rect = CPVT_FloatRect();
-  pSection->SecPlace.nSecIndex = nSecIndex;
-  m_SectionArray.insert(m_SectionArray.begin() + nSecIndex,
-                        std::move(pSection));
-  return place;
-}
-
-CPVT_WordPlace CPDF_VariableText::AddLine(const CPVT_WordPlace& place,
-                                          const CPVT_LineInfo& lineinfo) {
-  if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
-    return place;
-
-  return m_SectionArray[place.nSecIndex]->AddLine(lineinfo);
-}
-
-CPVT_WordPlace CPDF_VariableText::AddWord(const CPVT_WordPlace& place,
-                                          const CPVT_WordInfo& wordinfo) {
-  if (m_SectionArray.empty())
-    return place;
-
-  CPVT_WordPlace newplace = place;
-  newplace.nSecIndex =
-      pdfium::clamp(newplace.nSecIndex, 0,
-                    pdfium::CollectionSize<int32_t>(m_SectionArray) - 1);
-  return m_SectionArray[newplace.nSecIndex]->AddWord(newplace, wordinfo);
-}
-
-void CPDF_VariableText::SetPlateRect(const CFX_FloatRect& rect) {
-  m_rcPlate = rect;
-}
-
-void CPDF_VariableText::SetContentRect(const CPVT_FloatRect& rect) {
-  m_rcContent = rect;
-}
-
-CFX_FloatRect CPDF_VariableText::GetContentRect() const {
-  return InToOut(CPVT_FloatRect(m_rcContent));
-}
-
-const CFX_FloatRect& CPDF_VariableText::GetPlateRect() const {
-  return m_rcPlate;
-}
-
-float CPDF_VariableText::GetWordFontSize() {
-  return GetFontSize();
-}
-
-int32_t CPDF_VariableText::GetWordFontIndex(const CPVT_WordInfo& WordInfo) {
-  return WordInfo.nFontIndex;
-}
-
-float CPDF_VariableText::GetWordWidth(int32_t nFontIndex,
-                                      uint16_t Word,
-                                      uint16_t SubWord,
-                                      float fCharSpace,
-                                      float fFontSize,
-                                      float fWordTail) {
-  return GetCharWidth(nFontIndex, Word, SubWord) * fFontSize * kFontScale +
-         fCharSpace + fWordTail;
-}
-
-float CPDF_VariableText::GetWordWidth(const CPVT_WordInfo& WordInfo) {
-  return GetWordWidth(GetWordFontIndex(WordInfo), WordInfo.Word, GetSubWord(),
-                      GetCharSpace(), GetWordFontSize(), WordInfo.fWordTail);
-}
-
-float CPDF_VariableText::GetLineAscent() {
-  return GetFontAscent(GetDefaultFontIndex(), GetFontSize());
-}
-
-float CPDF_VariableText::GetLineDescent() {
-  return GetFontDescent(GetDefaultFontIndex(), GetFontSize());
-}
-
-float CPDF_VariableText::GetFontAscent(int32_t nFontIndex, float fFontSize) {
-  return (float)GetTypeAscent(nFontIndex) * fFontSize * kFontScale;
-}
-
-float CPDF_VariableText::GetFontDescent(int32_t nFontIndex, float fFontSize) {
-  return (float)GetTypeDescent(nFontIndex) * fFontSize * kFontScale;
-}
-
-float CPDF_VariableText::GetWordAscent(const CPVT_WordInfo& WordInfo,
-                                       float fFontSize) {
-  return GetFontAscent(GetWordFontIndex(WordInfo), fFontSize);
-}
-
-float CPDF_VariableText::GetWordDescent(const CPVT_WordInfo& WordInfo,
-                                        float fFontSize) {
-  return GetFontDescent(GetWordFontIndex(WordInfo), fFontSize);
-}
-
-float CPDF_VariableText::GetWordAscent(const CPVT_WordInfo& WordInfo) {
-  return GetFontAscent(GetWordFontIndex(WordInfo), GetWordFontSize());
-}
-
-float CPDF_VariableText::GetWordDescent(const CPVT_WordInfo& WordInfo) {
-  return GetFontDescent(GetWordFontIndex(WordInfo), GetWordFontSize());
-}
-
-float CPDF_VariableText::GetLineLeading() {
-  return m_fLineLeading;
-}
-
-float CPDF_VariableText::GetLineIndent() {
-  return 0.0f;
-}
-
-int32_t CPDF_VariableText::GetAlignment() {
-  return m_nAlignment;
-}
-
-void CPDF_VariableText::ClearSectionRightWords(const CPVT_WordPlace& place) {
-  CPVT_WordPlace wordplace = AdjustLineHeader(place, true);
-  if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
-    return;
-
-  CSection* pSection = m_SectionArray[place.nSecIndex].get();
-  if (!pdfium::IndexInBounds(pSection->m_WordArray, wordplace.nWordIndex + 1))
-    return;
-
-  pSection->m_WordArray.erase(
-      pSection->m_WordArray.begin() + wordplace.nWordIndex + 1,
-      pSection->m_WordArray.end());
-}
-
-CPVT_WordPlace CPDF_VariableText::AdjustLineHeader(const CPVT_WordPlace& place,
-                                                   bool bPrevOrNext) const {
-  if (place.nWordIndex < 0 && place.nLineIndex > 0)
-    return bPrevOrNext ? GetPrevWordPlace(place) : GetNextWordPlace(place);
-  return place;
-}
-
-bool CPDF_VariableText::ClearEmptySection(const CPVT_WordPlace& place) {
-  if (place.nSecIndex == 0 && m_SectionArray.size() == 1)
-    return false;
-
-  if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
-    return false;
-
-  if (!m_SectionArray[place.nSecIndex]->m_WordArray.empty())
-    return false;
-
-  m_SectionArray.erase(m_SectionArray.begin() + place.nSecIndex);
-  return true;
-}
-
-void CPDF_VariableText::ClearEmptySections(const CPVT_WordRange& PlaceRange) {
-  CPVT_WordPlace wordplace;
-  for (int32_t s = PlaceRange.EndPos.nSecIndex;
-       s > PlaceRange.BeginPos.nSecIndex; s--) {
-    wordplace.nSecIndex = s;
-    ClearEmptySection(wordplace);
-  }
-}
-
-void CPDF_VariableText::LinkLatterSection(const CPVT_WordPlace& place) {
-  CPVT_WordPlace oldplace = AdjustLineHeader(place, true);
-  if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex + 1))
-    return;
-
-  CSection* pNextSection = m_SectionArray[place.nSecIndex + 1].get();
-  if (pdfium::IndexInBounds(m_SectionArray, oldplace.nSecIndex)) {
-    CSection* pSection = m_SectionArray[oldplace.nSecIndex].get();
-    for (auto& pWord : pNextSection->m_WordArray) {
-      oldplace.nWordIndex++;
-      pSection->AddWord(oldplace, *pWord);
-    }
-  }
-  m_SectionArray.erase(m_SectionArray.begin() + place.nSecIndex + 1);
-}
-
-void CPDF_VariableText::ClearWords(const CPVT_WordRange& PlaceRange) {
-  CPVT_WordRange NewRange;
-  NewRange.BeginPos = AdjustLineHeader(PlaceRange.BeginPos, true);
-  NewRange.EndPos = AdjustLineHeader(PlaceRange.EndPos, true);
-  for (int32_t s = NewRange.EndPos.nSecIndex; s >= NewRange.BeginPos.nSecIndex;
-       s--) {
-    if (pdfium::IndexInBounds(m_SectionArray, s))
-      m_SectionArray[s]->ClearWords(NewRange);
-  }
-}
-
-CPVT_WordPlace CPDF_VariableText::ClearLeftWord(const CPVT_WordPlace& place) {
-  if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
-    return place;
-
-  CSection* pSection = m_SectionArray[place.nSecIndex].get();
-  CPVT_WordPlace leftplace = GetPrevWordPlace(place);
-  if (leftplace == place)
-    return place;
-
-  if (leftplace.nSecIndex != place.nSecIndex) {
-    if (pSection->m_WordArray.empty())
-      ClearEmptySection(place);
-    else
-      LinkLatterSection(leftplace);
-  } else {
-    pSection->ClearWord(place);
-  }
-  return leftplace;
-}
-
-CPVT_WordPlace CPDF_VariableText::ClearRightWord(const CPVT_WordPlace& place) {
-  if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
-    return place;
-
-  CSection* pSection = m_SectionArray[place.nSecIndex].get();
-  CPVT_WordPlace rightplace = AdjustLineHeader(GetNextWordPlace(place), false);
-  if (rightplace == place)
-    return place;
-
-  if (rightplace.nSecIndex != place.nSecIndex)
-    LinkLatterSection(place);
-  else
-    pSection->ClearWord(rightplace);
-  return place;
-}
-
-void CPDF_VariableText::RearrangeAll() {
-  Rearrange(CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace()));
-}
-
-void CPDF_VariableText::RearrangePart(const CPVT_WordRange& PlaceRange) {
-  Rearrange(PlaceRange);
-}
-
-CPVT_FloatRect CPDF_VariableText::Rearrange(const CPVT_WordRange& PlaceRange) {
-  CPVT_FloatRect rcRet;
-  if (IsValid()) {
-    if (m_bAutoFontSize) {
-      SetFontSize(GetAutoFontSize());
-      rcRet = RearrangeSections(
-          CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace()));
-    } else {
-      rcRet = RearrangeSections(PlaceRange);
-    }
-  }
-  SetContentRect(rcRet);
-  return rcRet;
-}
-
-float CPDF_VariableText::GetAutoFontSize() {
-  int32_t nTotal = sizeof(gFontSizeSteps) / sizeof(uint8_t);
-  if (IsMultiLine())
-    nTotal /= 4;
-  if (nTotal <= 0)
-    return 0;
-  if (GetPlateWidth() <= 0)
-    return 0;
-
-  int32_t nLeft = 0;
-  int32_t nRight = nTotal - 1;
-  int32_t nMid = nTotal / 2;
-  while (nLeft <= nRight) {
-    if (IsBigger(gFontSizeSteps[nMid]))
-      nRight = nMid - 1;
-    else
-      nLeft = nMid + 1;
-    nMid = (nLeft + nRight) / 2;
-  }
-  return (float)gFontSizeSteps[nMid];
-}
-
-bool CPDF_VariableText::IsBigger(float fFontSize) const {
-  CFX_SizeF szTotal;
-  for (const auto& pSection : m_SectionArray) {
-    CFX_SizeF size = pSection->GetSectionSize(fFontSize);
-    szTotal.width = std::max(size.width, szTotal.width);
-    szTotal.height += size.height;
-    if (IsFloatBigger(szTotal.width, GetPlateWidth()) ||
-        IsFloatBigger(szTotal.height, GetPlateHeight())) {
-      return true;
-    }
-  }
-  return false;
-}
-
-CPVT_FloatRect CPDF_VariableText::RearrangeSections(
-    const CPVT_WordRange& PlaceRange) {
-  CPVT_WordPlace place;
-  float fPosY = 0;
-  float fOldHeight;
-  int32_t nSSecIndex = PlaceRange.BeginPos.nSecIndex;
-  int32_t nESecIndex = PlaceRange.EndPos.nSecIndex;
-  CPVT_FloatRect rcRet;
-  for (int32_t s = 0, sz = pdfium::CollectionSize<int32_t>(m_SectionArray);
-       s < sz; s++) {
-    place.nSecIndex = s;
-    CSection* pSection = m_SectionArray[s].get();
-    pSection->SecPlace = place;
-    CPVT_FloatRect rcSec = pSection->m_Rect;
-    if (s >= nSSecIndex) {
-      if (s <= nESecIndex) {
-        rcSec = pSection->Rearrange();
-        rcSec.top += fPosY;
-        rcSec.bottom += fPosY;
-      } else {
-        fOldHeight = pSection->m_Rect.bottom - pSection->m_Rect.top;
-        rcSec.top = fPosY;
-        rcSec.bottom = fPosY + fOldHeight;
-      }
-      pSection->m_Rect = rcSec;
-      pSection->ResetLinePlace();
-    }
-    if (s == 0) {
-      rcRet = rcSec;
-    } else {
-      rcRet.left = std::min(rcSec.left, rcRet.left);
-      rcRet.top = std::min(rcSec.top, rcRet.top);
-      rcRet.right = std::max(rcSec.right, rcRet.right);
-      rcRet.bottom = std::max(rcSec.bottom, rcRet.bottom);
-    }
-    fPosY += rcSec.Height();
-  }
-  return rcRet;
-}
-
-uint32_t CPDF_VariableText::GetCharWidth(int32_t nFontIndex,
-                                         uint16_t Word,
-                                         uint16_t SubWord) {
-  if (!m_pVTProvider)
-    return 0;
-  uint16_t word = SubWord ? SubWord : Word;
-  return m_pVTProvider->GetCharWidth(nFontIndex, word);
-}
-
-int32_t CPDF_VariableText::GetTypeAscent(int32_t nFontIndex) {
-  return m_pVTProvider ? m_pVTProvider->GetTypeAscent(nFontIndex) : 0;
-}
-
-int32_t CPDF_VariableText::GetTypeDescent(int32_t nFontIndex) {
-  return m_pVTProvider ? m_pVTProvider->GetTypeDescent(nFontIndex) : 0;
-}
-
-int32_t CPDF_VariableText::GetWordFontIndex(uint16_t word,
-                                            int32_t charset,
-                                            int32_t nFontIndex) {
-  return m_pVTProvider
-             ? m_pVTProvider->GetWordFontIndex(word, charset, nFontIndex)
-             : -1;
-}
-
-int32_t CPDF_VariableText::GetDefaultFontIndex() {
-  return m_pVTProvider ? m_pVTProvider->GetDefaultFontIndex() : -1;
-}
-
-bool CPDF_VariableText::IsLatinWord(uint16_t word) {
-  return m_pVTProvider && m_pVTProvider->IsLatinWord(word);
-}
-
-CPDF_VariableText::Iterator* CPDF_VariableText::GetIterator() {
-  if (!m_pVTIterator)
-    m_pVTIterator = pdfium::MakeUnique<CPDF_VariableText::Iterator>(this);
-  return m_pVTIterator.get();
-}
-
-void CPDF_VariableText::SetProvider(CPDF_VariableText::Provider* pProvider) {
-  m_pVTProvider = pProvider;
-}
-
-CFX_PointF CPDF_VariableText::GetBTPoint() const {
-  return CFX_PointF(m_rcPlate.left, m_rcPlate.top);
-}
-
-CFX_PointF CPDF_VariableText::GetETPoint() const {
-  return CFX_PointF(m_rcPlate.right, m_rcPlate.bottom);
-}
-
-CFX_PointF CPDF_VariableText::InToOut(const CFX_PointF& point) const {
-  return CFX_PointF(point.x + GetBTPoint().x, GetBTPoint().y - point.y);
-}
-
-CFX_PointF CPDF_VariableText::OutToIn(const CFX_PointF& point) const {
-  return CFX_PointF(point.x - GetBTPoint().x, GetBTPoint().y - point.y);
-}
-
-CFX_FloatRect CPDF_VariableText::InToOut(const CPVT_FloatRect& rect) const {
-  CFX_PointF ptLeftTop = InToOut(CFX_PointF(rect.left, rect.top));
-  CFX_PointF ptRightBottom = InToOut(CFX_PointF(rect.right, rect.bottom));
-  return CFX_FloatRect(ptLeftTop.x, ptRightBottom.y, ptRightBottom.x,
-                       ptLeftTop.y);
-}
-
-CPVT_FloatRect CPDF_VariableText::OutToIn(const CFX_FloatRect& rect) const {
-  CFX_PointF ptLeftTop = OutToIn(CFX_PointF(rect.left, rect.top));
-  CFX_PointF ptRightBottom = OutToIn(CFX_PointF(rect.right, rect.bottom));
-  return CPVT_FloatRect(ptLeftTop.x, ptLeftTop.y, ptRightBottom.x,
-                        ptRightBottom.y);
-}
diff --git a/core/fpdfdoc/cpdf_variabletext.h b/core/fpdfdoc/cpdf_variabletext.h
deleted file mode 100644
index aa47015..0000000
--- a/core/fpdfdoc/cpdf_variabletext.h
+++ /dev/null
@@ -1,212 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFDOC_CPDF_VARIABLETEXT_H_
-#define CORE_FPDFDOC_CPDF_VARIABLETEXT_H_
-
-#include <memory>
-#include <vector>
-
-#include "core/fpdfdoc/cpvt_floatrect.h"
-#include "core/fpdfdoc/cpvt_line.h"
-#include "core/fpdfdoc/cpvt_lineinfo.h"
-#include "core/fpdfdoc/cpvt_wordplace.h"
-#include "core/fpdfdoc/cpvt_wordrange.h"
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class CPVT_Word;
-class CSection;
-class IPVT_FontMap;
-struct CPVT_WordInfo;
-
-#define VARIABLETEXT_HALF 0.5f
-
-class CPDF_VariableText {
- public:
-  class Iterator {
-   public:
-    explicit Iterator(CPDF_VariableText* pVT);
-    ~Iterator();
-
-    bool NextWord();
-    bool PrevWord();
-    bool NextLine();
-    bool GetWord(CPVT_Word& word) const;
-    bool GetLine(CPVT_Line& line) const;
-    void SetAt(int32_t nWordIndex);
-    void SetAt(const CPVT_WordPlace& place);
-    const CPVT_WordPlace& GetWordPlace() const { return m_CurPos; }
-
-   private:
-    CPVT_WordPlace m_CurPos;
-    UnownedPtr<CPDF_VariableText> const m_pVT;
-  };
-
-  class Provider {
-   public:
-    explicit Provider(IPVT_FontMap* pFontMap);
-    virtual ~Provider();
-
-    virtual uint32_t GetCharWidth(int32_t nFontIndex, uint16_t word);
-    virtual int32_t GetTypeAscent(int32_t nFontIndex);
-    virtual int32_t GetTypeDescent(int32_t nFontIndex);
-    virtual int32_t GetWordFontIndex(uint16_t word,
-                                     int32_t charset,
-                                     int32_t nFontIndex);
-    virtual bool IsLatinWord(uint16_t word);
-    virtual int32_t GetDefaultFontIndex();
-
-   private:
-    UnownedPtr<IPVT_FontMap> const m_pFontMap;
-  };
-
-  CPDF_VariableText();
-  ~CPDF_VariableText();
-
-  void SetProvider(CPDF_VariableText::Provider* pProvider);
-  CPDF_VariableText::Iterator* GetIterator();
-
-  void SetContentRect(const CPVT_FloatRect& rect);
-  CFX_FloatRect GetContentRect() const;
-  void SetPlateRect(const CFX_FloatRect& rect);
-  const CFX_FloatRect& GetPlateRect() const;
-
-  void SetAlignment(int32_t nFormat) { m_nAlignment = nFormat; }
-  void SetPasswordChar(uint16_t wSubWord) { m_wSubWord = wSubWord; }
-  void SetLimitChar(int32_t nLimitChar) { m_nLimitChar = nLimitChar; }
-  void SetCharSpace(float fCharSpace) { m_fCharSpace = fCharSpace; }
-  void SetMultiLine(bool bMultiLine) { m_bMultiLine = bMultiLine; }
-  void SetAutoReturn(bool bAuto) { m_bLimitWidth = bAuto; }
-  void SetFontSize(float fFontSize) { m_fFontSize = fFontSize; }
-  void SetCharArray(int32_t nCharArray) { m_nCharArray = nCharArray; }
-  void SetAutoFontSize(bool bAuto) { m_bAutoFontSize = bAuto; }
-  void Initialize();
-
-  bool IsValid() const { return m_bInitialized; }
-
-  void RearrangeAll();
-  void RearrangePart(const CPVT_WordRange& PlaceRange);
-  void SetText(const WideString& text);
-  CPVT_WordPlace InsertWord(const CPVT_WordPlace& place,
-                            uint16_t word,
-                            int32_t charset);
-  CPVT_WordPlace InsertSection(const CPVT_WordPlace& place);
-  CPVT_WordPlace DeleteWords(const CPVT_WordRange& PlaceRange);
-  CPVT_WordPlace DeleteWord(const CPVT_WordPlace& place);
-  CPVT_WordPlace BackSpaceWord(const CPVT_WordPlace& place);
-
-  int32_t GetTotalWords() const;
-  float GetFontSize() const { return m_fFontSize; }
-  int32_t GetAlignment() const { return m_nAlignment; }
-  uint16_t GetPasswordChar() const { return GetSubWord(); }
-  int32_t GetCharArray() const { return m_nCharArray; }
-  int32_t GetLimitChar() const { return m_nLimitChar; }
-  bool IsMultiLine() const { return m_bMultiLine; }
-  float GetCharSpace() const { return m_fCharSpace; }
-  bool IsAutoReturn() const { return m_bLimitWidth; }
-
-  CPVT_WordPlace GetBeginWordPlace() const;
-  CPVT_WordPlace GetEndWordPlace() const;
-  CPVT_WordPlace GetPrevWordPlace(const CPVT_WordPlace& place) const;
-  CPVT_WordPlace GetNextWordPlace(const CPVT_WordPlace& place) const;
-  CPVT_WordPlace SearchWordPlace(const CFX_PointF& point) const;
-  CPVT_WordPlace GetUpWordPlace(const CPVT_WordPlace& place,
-                                const CFX_PointF& point) const;
-  CPVT_WordPlace GetDownWordPlace(const CPVT_WordPlace& place,
-                                  const CFX_PointF& point) const;
-  CPVT_WordPlace GetLineBeginPlace(const CPVT_WordPlace& place) const;
-  CPVT_WordPlace GetLineEndPlace(const CPVT_WordPlace& place) const;
-  CPVT_WordPlace GetSectionBeginPlace(const CPVT_WordPlace& place) const;
-  CPVT_WordPlace GetSectionEndPlace(const CPVT_WordPlace& place) const;
-  void UpdateWordPlace(CPVT_WordPlace& place) const;
-  CPVT_WordPlace AdjustLineHeader(const CPVT_WordPlace& place,
-                                  bool bPrevOrNext) const;
-  int32_t WordPlaceToWordIndex(const CPVT_WordPlace& place) const;
-  CPVT_WordPlace WordIndexToWordPlace(int32_t index) const;
-
-  uint16_t GetSubWord() const { return m_wSubWord; }
-
-  float GetPlateWidth() const { return m_rcPlate.right - m_rcPlate.left; }
-  float GetPlateHeight() const { return m_rcPlate.top - m_rcPlate.bottom; }
-  CFX_PointF GetBTPoint() const;
-  CFX_PointF GetETPoint() const;
-
-  CFX_PointF InToOut(const CFX_PointF& point) const;
-  CFX_PointF OutToIn(const CFX_PointF& point) const;
-  CFX_FloatRect InToOut(const CPVT_FloatRect& rect) const;
-  CPVT_FloatRect OutToIn(const CFX_FloatRect& rect) const;
-
-  float GetFontAscent(int32_t nFontIndex, float fFontSize);
-  float GetFontDescent(int32_t nFontIndex, float fFontSize);
-  int32_t GetDefaultFontIndex();
-  float GetLineLeading();
-  int32_t GetAlignment();
-  float GetWordWidth(const CPVT_WordInfo& WordInfo);
-  float GetWordWidth(int32_t nFontIndex,
-                     uint16_t Word,
-                     uint16_t SubWord,
-                     float fCharSpace,
-                     float fFontSize,
-                     float fWordTail);
-  float GetWordAscent(const CPVT_WordInfo& WordInfo);
-  float GetWordDescent(const CPVT_WordInfo& WordInfo);
-  float GetWordAscent(const CPVT_WordInfo& WordInfo, float fFontSize);
-  float GetWordDescent(const CPVT_WordInfo& WordInfo, float fFontSize);
-  float GetLineAscent();
-  float GetLineDescent();
-  float GetLineIndent();
-
- private:
-  uint32_t GetCharWidth(int32_t nFontIndex, uint16_t Word, uint16_t SubWord);
-  int32_t GetTypeAscent(int32_t nFontIndex);
-  int32_t GetTypeDescent(int32_t nFontIndex);
-  int32_t GetWordFontIndex(uint16_t word, int32_t charset, int32_t nFontIndex);
-  bool IsLatinWord(uint16_t word);
-
-  CPVT_WordPlace AddSection(const CPVT_WordPlace& place);
-  CPVT_WordPlace AddLine(const CPVT_WordPlace& place,
-                         const CPVT_LineInfo& lineinfo);
-  CPVT_WordPlace AddWord(const CPVT_WordPlace& place,
-                         const CPVT_WordInfo& wordinfo);
-  float GetWordFontSize();
-  int32_t GetWordFontIndex(const CPVT_WordInfo& WordInfo);
-
-  void ClearSectionRightWords(const CPVT_WordPlace& place);
-
-  bool ClearEmptySection(const CPVT_WordPlace& place);
-  void ClearEmptySections(const CPVT_WordRange& PlaceRange);
-  void LinkLatterSection(const CPVT_WordPlace& place);
-  void ClearWords(const CPVT_WordRange& PlaceRange);
-  CPVT_WordPlace ClearLeftWord(const CPVT_WordPlace& place);
-  CPVT_WordPlace ClearRightWord(const CPVT_WordPlace& place);
-
-  CPVT_FloatRect Rearrange(const CPVT_WordRange& PlaceRange);
-  float GetAutoFontSize();
-  bool IsBigger(float fFontSize) const;
-  CPVT_FloatRect RearrangeSections(const CPVT_WordRange& PlaceRange);
-
-  bool m_bInitialized = false;
-  bool m_bMultiLine = false;
-  bool m_bLimitWidth = false;
-  bool m_bAutoFontSize = false;
-  uint16_t m_wSubWord = 0;
-  int32_t m_nLimitChar = 0;
-  int32_t m_nCharArray = 0;
-  int32_t m_nAlignment = 0;
-  float m_fLineLeading = 0.0f;
-  float m_fCharSpace = 0.0f;
-  float m_fFontSize = 0.0f;
-  std::vector<std::unique_ptr<CSection>> m_SectionArray;
-  UnownedPtr<CPDF_VariableText::Provider> m_pVTProvider;
-  std::unique_ptr<CPDF_VariableText::Iterator> m_pVTIterator;
-  CFX_FloatRect m_rcPlate;
-  CPVT_FloatRect m_rcContent;
-};
-
-#endif  // CORE_FPDFDOC_CPDF_VARIABLETEXT_H_
diff --git a/core/fpdfdoc/cpdf_viewerpreferences.cpp b/core/fpdfdoc/cpdf_viewerpreferences.cpp
index be07fdc..e3f15f0 100644
--- a/core/fpdfdoc/cpdf_viewerpreferences.cpp
+++ b/core/fpdfdoc/cpdf_viewerpreferences.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,7 @@
 
 #include "core/fpdfdoc/cpdf_viewerpreferences.h"
 
+#include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
@@ -16,44 +17,45 @@
 CPDF_ViewerPreferences::~CPDF_ViewerPreferences() = default;
 
 bool CPDF_ViewerPreferences::IsDirectionR2L() const {
-  const CPDF_Dictionary* pDict = GetViewerPreferences();
-  return pDict && pDict->GetStringFor("Direction") == "R2L";
+  RetainPtr<const CPDF_Dictionary> pDict = GetViewerPreferences();
+  return pDict && pDict->GetByteStringFor("Direction") == "R2L";
 }
 
 bool CPDF_ViewerPreferences::PrintScaling() const {
-  const CPDF_Dictionary* pDict = GetViewerPreferences();
-  return !pDict || pDict->GetStringFor("PrintScaling") != "None";
+  RetainPtr<const CPDF_Dictionary> pDict = GetViewerPreferences();
+  return !pDict || pDict->GetByteStringFor("PrintScaling") != "None";
 }
 
 int32_t CPDF_ViewerPreferences::NumCopies() const {
-  const CPDF_Dictionary* pDict = GetViewerPreferences();
+  RetainPtr<const CPDF_Dictionary> pDict = GetViewerPreferences();
   return pDict ? pDict->GetIntegerFor("NumCopies") : 1;
 }
 
-CPDF_Array* CPDF_ViewerPreferences::PrintPageRange() const {
-  CPDF_Dictionary* pDict = GetViewerPreferences();
+RetainPtr<const CPDF_Array> CPDF_ViewerPreferences::PrintPageRange() const {
+  RetainPtr<const CPDF_Dictionary> pDict = GetViewerPreferences();
   return pDict ? pDict->GetArrayFor("PrintPageRange") : nullptr;
 }
 
 ByteString CPDF_ViewerPreferences::Duplex() const {
-  const CPDF_Dictionary* pDict = GetViewerPreferences();
-  return pDict ? pDict->GetStringFor("Duplex") : ByteString("None");
+  RetainPtr<const CPDF_Dictionary> pDict = GetViewerPreferences();
+  return pDict ? pDict->GetByteStringFor("Duplex") : ByteString("None");
 }
 
-Optional<ByteString> CPDF_ViewerPreferences::GenericName(
+absl::optional<ByteString> CPDF_ViewerPreferences::GenericName(
     const ByteString& bsKey) const {
-  const CPDF_Dictionary* pDict = GetViewerPreferences();
+  RetainPtr<const CPDF_Dictionary> pDict = GetViewerPreferences();
   if (!pDict)
-    return {};
+    return absl::nullopt;
 
-  const CPDF_Name* pName = ToName(pDict->GetObjectFor(bsKey));
+  RetainPtr<const CPDF_Name> pName = ToName(pDict->GetObjectFor(bsKey));
   if (!pName)
-    return {};
+    return absl::nullopt;
 
   return pName->GetString();
 }
 
-CPDF_Dictionary* CPDF_ViewerPreferences::GetViewerPreferences() const {
-  CPDF_Dictionary* pDict = m_pDoc->GetRoot();
+RetainPtr<const CPDF_Dictionary> CPDF_ViewerPreferences::GetViewerPreferences()
+    const {
+  const CPDF_Dictionary* pDict = m_pDoc->GetRoot();
   return pDict ? pDict->GetDictFor("ViewerPreferences") : nullptr;
 }
diff --git a/core/fpdfdoc/cpdf_viewerpreferences.h b/core/fpdfdoc/cpdf_viewerpreferences.h
index ff2b1c8..3e17048 100644
--- a/core/fpdfdoc/cpdf_viewerpreferences.h
+++ b/core/fpdfdoc/cpdf_viewerpreferences.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,12 @@
 #ifndef CORE_FPDFDOC_CPDF_VIEWERPREFERENCES_H_
 #define CORE_FPDFDOC_CPDF_VIEWERPREFERENCES_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include <stdint.h>
+
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CPDF_Array;
 class CPDF_Dictionary;
@@ -24,14 +26,14 @@
   bool IsDirectionR2L() const;
   bool PrintScaling() const;
   int32_t NumCopies() const;
-  CPDF_Array* PrintPageRange() const;
+  RetainPtr<const CPDF_Array> PrintPageRange() const;
   ByteString Duplex() const;
 
   // Gets the entry for |bsKey|.
-  Optional<ByteString> GenericName(const ByteString& bsKey) const;
+  absl::optional<ByteString> GenericName(const ByteString& bsKey) const;
 
  private:
-  CPDF_Dictionary* GetViewerPreferences() const;
+  RetainPtr<const CPDF_Dictionary> GetViewerPreferences() const;
 
   UnownedPtr<const CPDF_Document> const m_pDoc;
 };
diff --git a/core/fpdfdoc/cpvt_floatrect.h b/core/fpdfdoc/cpvt_floatrect.h
index d302ed0..a9add92 100644
--- a/core/fpdfdoc/cpvt_floatrect.h
+++ b/core/fpdfdoc/cpvt_floatrect.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fpdfdoc/cpvt_fontmap.cpp b/core/fpdfdoc/cpvt_fontmap.cpp
index b21bd0d..b492b5f 100644
--- a/core/fpdfdoc/cpvt_fontmap.cpp
+++ b/core/fpdfdoc/cpvt_fontmap.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include "core/fpdfdoc/cpvt_fontmap.h"
 
+#include <utility>
+
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
@@ -13,40 +15,37 @@
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "core/fxcrt/fx_codepage.h"
-#include "third_party/base/logging.h"
+#include "third_party/base/check.h"
+#include "third_party/base/notreached.h"
 
 CPVT_FontMap::CPVT_FontMap(CPDF_Document* pDoc,
-                           CPDF_Dictionary* pResDict,
-                           const RetainPtr<CPDF_Font>& pDefFont,
+                           RetainPtr<CPDF_Dictionary> pResDict,
+                           RetainPtr<CPDF_Font> pDefFont,
                            const ByteString& sDefFontAlias)
     : m_pDocument(pDoc),
-      m_pResDict(pResDict),
-      m_pDefFont(pDefFont),
+      m_pResDict(std::move(pResDict)),
+      m_pDefFont(std::move(pDefFont)),
       m_sDefFontAlias(sDefFontAlias) {}
 
-CPVT_FontMap::~CPVT_FontMap() {}
+CPVT_FontMap::~CPVT_FontMap() = default;
 
-// static
-RetainPtr<CPDF_Font> CPVT_FontMap::GetAnnotSysPDFFont(
-    CPDF_Document* pDoc,
-    CPDF_Dictionary* pResDict,
-    ByteString* sSysFontAlias) {
-  if (!pDoc || !pResDict)
-    return nullptr;
+void CPVT_FontMap::SetupAnnotSysPDFFont() {
+  if (!m_pDocument || !m_pResDict)
+    return;
 
-  CPDF_Dictionary* pFormDict = pDoc->GetRoot()->GetDictFor("AcroForm");
   RetainPtr<CPDF_Font> pPDFFont =
-      AddNativeInteractiveFormFont(pFormDict, pDoc, sSysFontAlias);
+      CPDF_InteractiveForm::AddNativeInteractiveFormFont(m_pDocument,
+                                                         &m_sSysFontAlias);
   if (!pPDFFont)
-    return nullptr;
+    return;
 
-  CPDF_Dictionary* pFontList = pResDict->GetDictFor("Font");
-  if (ValidateFontResourceDict(pFontList) &&
-      !pFontList->KeyExist(*sSysFontAlias)) {
-    pFontList->SetNewFor<CPDF_Reference>(*sSysFontAlias, pDoc,
-                                         pPDFFont->GetFontDict()->GetObjNum());
+  RetainPtr<CPDF_Dictionary> pFontList = m_pResDict->GetMutableDictFor("Font");
+  if (ValidateFontResourceDict(pFontList.Get()) &&
+      !pFontList->KeyExist(m_sSysFontAlias)) {
+    pFontList->SetNewFor<CPDF_Reference>(m_sSysFontAlias, m_pDocument,
+                                         pPDFFont->GetFontDictObjNum());
   }
-  return pPDFFont;
+  m_pSysFont = std::move(pPDFFont);
 }
 
 RetainPtr<CPDF_Font> CPVT_FontMap::GetPDFFont(int32_t nFontIndex) {
@@ -54,10 +53,8 @@
     case 0:
       return m_pDefFont;
     case 1:
-      if (!m_pSysFont) {
-        m_pSysFont = GetAnnotSysPDFFont(m_pDocument.Get(), m_pResDict.Get(),
-                                        &m_sSysFontAlias);
-      }
+      if (!m_pSysFont)
+        SetupAnnotSysPDFFont();
       return m_pSysFont;
     default:
       return nullptr;
@@ -69,10 +66,8 @@
     case 0:
       return m_sDefFontAlias;
     case 1:
-      if (!m_pSysFont) {
-        m_pSysFont = GetAnnotSysPDFFont(m_pDocument.Get(), m_pResDict.Get(),
-                                        &m_sSysFontAlias);
-      }
+      if (!m_pSysFont)
+        SetupAnnotSysPDFFont();
       return m_sSysFontAlias;
     default:
       return ByteString();
@@ -80,18 +75,16 @@
 }
 
 int32_t CPVT_FontMap::GetWordFontIndex(uint16_t word,
-                                       int32_t charset,
+                                       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();
 }
 
-int32_t CPVT_FontMap::CharSetFromUnicode(uint16_t word, int32_t nOldCharset) {
-  NOTREACHED();
-  return FX_CHARSET_ANSI;
+FX_Charset CPVT_FontMap::CharSetFromUnicode(uint16_t word,
+                                            FX_Charset nOldCharset) {
+  NOTREACHED_NORETURN();
 }
diff --git a/core/fpdfdoc/cpvt_fontmap.h b/core/fpdfdoc/cpvt_fontmap.h
index a85afcf..e3add6f 100644
--- a/core/fpdfdoc/cpvt_fontmap.h
+++ b/core/fpdfdoc/cpvt_fontmap.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,7 @@
 #include <stdint.h>
 
 #include "core/fpdfdoc/ipvt_fontmap.h"
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
@@ -21,8 +21,8 @@
 class CPVT_FontMap final : public IPVT_FontMap {
  public:
   CPVT_FontMap(CPDF_Document* pDoc,
-               CPDF_Dictionary* pResDict,
-               const RetainPtr<CPDF_Font>& pDefFont,
+               RetainPtr<CPDF_Dictionary> pResDict,
+               RetainPtr<CPDF_Font> pDefFont,
                const ByteString& sDefFontAlias);
   ~CPVT_FontMap() override;
 
@@ -30,16 +30,14 @@
   RetainPtr<CPDF_Font> GetPDFFont(int32_t nFontIndex) override;
   ByteString GetPDFFontAlias(int32_t nFontIndex) override;
   int32_t GetWordFontIndex(uint16_t word,
-                           int32_t charset,
+                           FX_Charset charset,
                            int32_t nFontIndex) override;
   int32_t CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) override;
-  int32_t CharSetFromUnicode(uint16_t word, int32_t nOldCharset) override;
-
-  static RetainPtr<CPDF_Font> GetAnnotSysPDFFont(CPDF_Document* pDoc,
-                                                 CPDF_Dictionary* pResDict,
-                                                 ByteString* sSysFontAlias);
+  FX_Charset CharSetFromUnicode(uint16_t word, FX_Charset nOldCharset) override;
 
  private:
+  void SetupAnnotSysPDFFont();
+
   UnownedPtr<CPDF_Document> const m_pDocument;
   RetainPtr<CPDF_Dictionary> const m_pResDict;
   RetainPtr<CPDF_Font> const m_pDefFont;
diff --git a/core/fpdfdoc/cpvt_generateap.cpp b/core/fpdfdoc/cpvt_generateap.cpp
deleted file mode 100644
index 2b90365..0000000
--- a/core/fpdfdoc/cpvt_generateap.cpp
+++ /dev/null
@@ -1,1386 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fpdfdoc/cpvt_generateap.h"
-
-#include <algorithm>
-#include <memory>
-#include <sstream>
-#include <utility>
-
-#include "constants/annotation_common.h"
-#include "constants/form_fields.h"
-#include "core/fpdfapi/font/cpdf_font.h"
-#include "core/fpdfapi/page/cpdf_docpagedata.h"
-#include "core/fpdfapi/parser/cpdf_array.h"
-#include "core/fpdfapi/parser/cpdf_boolean.h"
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/parser/cpdf_name.h"
-#include "core/fpdfapi/parser/cpdf_number.h"
-#include "core/fpdfapi/parser/cpdf_reference.h"
-#include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfapi/parser/fpdf_parser_decode.h"
-#include "core/fpdfapi/parser/fpdf_parser_utility.h"
-#include "core/fpdfdoc/cpdf_annot.h"
-#include "core/fpdfdoc/cpdf_color_utils.h"
-#include "core/fpdfdoc/cpdf_defaultappearance.h"
-#include "core/fpdfdoc/cpdf_formfield.h"
-#include "core/fpdfdoc/cpdf_variabletext.h"
-#include "core/fpdfdoc/cpvt_fontmap.h"
-#include "core/fpdfdoc/cpvt_word.h"
-#include "core/fxge/cfx_renderdevice.h"
-#include "third_party/base/ptr_util.h"
-
-namespace {
-
-struct CPVT_Dash {
-  CPVT_Dash(int32_t dash, int32_t gap, int32_t phase)
-      : nDash(dash), nGap(gap), nPhase(phase) {}
-
-  int32_t nDash;
-  int32_t nGap;
-  int32_t nPhase;
-};
-
-enum class PaintOperation { STROKE, FILL };
-
-ByteString GetPDFWordString(IPVT_FontMap* pFontMap,
-                            int32_t nFontIndex,
-                            uint16_t Word,
-                            uint16_t SubWord) {
-  if (SubWord > 0)
-    return ByteString::Format("%c", SubWord);
-
-  if (!pFontMap)
-    return ByteString();
-
-  RetainPtr<CPDF_Font> pPDFFont = pFontMap->GetPDFFont(nFontIndex);
-  if (!pPDFFont)
-    return ByteString();
-
-  if (pPDFFont->GetBaseFontName().Compare("Symbol") == 0 ||
-      pPDFFont->GetBaseFontName().Compare("ZapfDingbats") == 0) {
-    return ByteString::Format("%c", Word);
-  }
-
-  ByteString sWord;
-  uint32_t dwCharCode = pPDFFont->CharCodeFromUnicode(Word);
-  if (dwCharCode != CPDF_Font::kInvalidCharCode)
-    pPDFFont->AppendChar(&sWord, dwCharCode);
-
-  return sWord;
-}
-
-ByteString GetWordRenderString(const ByteString& strWords) {
-  if (strWords.GetLength() > 0)
-    return PDF_EncodeString(strWords, false) + " Tj\n";
-  return ByteString();
-}
-
-ByteString GetFontSetString(IPVT_FontMap* pFontMap,
-                            int32_t nFontIndex,
-                            float fFontSize) {
-  std::ostringstream sRet;
-  if (pFontMap) {
-    ByteString sFontAlias = pFontMap->GetPDFFontAlias(nFontIndex);
-    if (sFontAlias.GetLength() > 0 && fFontSize > 0)
-      sRet << "/" << sFontAlias << " " << fFontSize << " Tf\n";
-  }
-  return ByteString(sRet);
-}
-
-ByteString GenerateEditAP(IPVT_FontMap* pFontMap,
-                          CPDF_VariableText::Iterator* pIterator,
-                          const CFX_PointF& ptOffset,
-                          bool bContinuous,
-                          uint16_t SubWord) {
-  std::ostringstream sEditStream;
-  std::ostringstream sLineStream;
-  std::ostringstream sWords;
-  CFX_PointF ptOld;
-  CFX_PointF ptNew;
-  int32_t nCurFontIndex = -1;
-  CPVT_WordPlace oldplace;
-
-  pIterator->SetAt(0);
-  while (pIterator->NextWord()) {
-    CPVT_WordPlace place = pIterator->GetWordPlace();
-    if (bContinuous) {
-      if (place.LineCmp(oldplace) != 0) {
-        if (sWords.tellp() > 0) {
-          sLineStream << GetWordRenderString(ByteString(sWords));
-          sEditStream << sLineStream.str();
-          sLineStream.str("");
-          sWords.str("");
-        }
-        CPVT_Word word;
-        if (pIterator->GetWord(word)) {
-          ptNew = CFX_PointF(word.ptWord.x + ptOffset.x,
-                             word.ptWord.y + ptOffset.y);
-        } else {
-          CPVT_Line line;
-          pIterator->GetLine(line);
-          ptNew = CFX_PointF(line.ptLine.x + ptOffset.x,
-                             line.ptLine.y + ptOffset.y);
-        }
-        if (ptNew != ptOld) {
-          sLineStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y
-                      << " Td\n";
-          ptOld = ptNew;
-        }
-      }
-      CPVT_Word word;
-      if (pIterator->GetWord(word)) {
-        if (word.nFontIndex != nCurFontIndex) {
-          if (sWords.tellp() > 0) {
-            sLineStream << GetWordRenderString(ByteString(sWords));
-            sWords.str("");
-          }
-          sLineStream << GetFontSetString(pFontMap, word.nFontIndex,
-                                          word.fFontSize);
-          nCurFontIndex = word.nFontIndex;
-        }
-        sWords << GetPDFWordString(pFontMap, nCurFontIndex, word.Word, SubWord);
-      }
-      oldplace = place;
-    } else {
-      CPVT_Word word;
-      if (pIterator->GetWord(word)) {
-        ptNew =
-            CFX_PointF(word.ptWord.x + ptOffset.x, word.ptWord.y + ptOffset.y);
-        if (ptNew != ptOld) {
-          sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y
-                      << " Td\n";
-          ptOld = ptNew;
-        }
-        if (word.nFontIndex != nCurFontIndex) {
-          sEditStream << GetFontSetString(pFontMap, word.nFontIndex,
-                                          word.fFontSize);
-          nCurFontIndex = word.nFontIndex;
-        }
-        sEditStream << GetWordRenderString(
-            GetPDFWordString(pFontMap, nCurFontIndex, word.Word, SubWord));
-      }
-    }
-  }
-  if (sWords.tellp() > 0) {
-    sLineStream << GetWordRenderString(ByteString(sWords));
-    sEditStream << sLineStream.str();
-    sWords.str("");
-  }
-  return ByteString(sEditStream);
-}
-
-ByteString GenerateColorAP(const CFX_Color& color, PaintOperation nOperation) {
-  std::ostringstream sColorStream;
-  switch (color.nColorType) {
-    case CFX_Color::kRGB:
-      sColorStream << color.fColor1 << " " << color.fColor2 << " "
-                   << color.fColor3 << " "
-                   << (nOperation == PaintOperation::STROKE ? "RG" : "rg")
-                   << "\n";
-      break;
-    case CFX_Color::kGray:
-      sColorStream << color.fColor1 << " "
-                   << (nOperation == PaintOperation::STROKE ? "G" : "g")
-                   << "\n";
-      break;
-    case CFX_Color::kCMYK:
-      sColorStream << color.fColor1 << " " << color.fColor2 << " "
-                   << color.fColor3 << " " << color.fColor4 << " "
-                   << (nOperation == PaintOperation::STROKE ? "K" : "k")
-                   << "\n";
-      break;
-    case CFX_Color::kTransparent:
-      break;
-  }
-  return ByteString(sColorStream);
-}
-
-ByteString GenerateBorderAP(const CFX_FloatRect& rect,
-                            float fWidth,
-                            const CFX_Color& color,
-                            const CFX_Color& crLeftTop,
-                            const CFX_Color& crRightBottom,
-                            BorderStyle nStyle,
-                            const CPVT_Dash& dash) {
-  std::ostringstream sAppStream;
-  ByteString sColor;
-  float fLeft = rect.left;
-  float fRight = rect.right;
-  float fTop = rect.top;
-  float fBottom = rect.bottom;
-  if (fWidth > 0.0f) {
-    float fHalfWidth = fWidth / 2.0f;
-    switch (nStyle) {
-      default:
-      case BorderStyle::SOLID:
-        sColor = GenerateColorAP(color, PaintOperation::FILL);
-        if (sColor.GetLength() > 0) {
-          sAppStream << sColor;
-          sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " "
-                     << fTop - fBottom << " re\n";
-          sAppStream << fLeft + fWidth << " " << fBottom + fWidth << " "
-                     << fRight - fLeft - fWidth * 2 << " "
-                     << fTop - fBottom - fWidth * 2 << " re\n";
-          sAppStream << "f*\n";
-        }
-        break;
-      case BorderStyle::DASH:
-        sColor = GenerateColorAP(color, PaintOperation::STROKE);
-        if (sColor.GetLength() > 0) {
-          sAppStream << sColor;
-          sAppStream << fWidth << " w"
-                     << " [" << dash.nDash << " " << dash.nGap << "] "
-                     << dash.nPhase << " d\n";
-          sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2
-                     << " m\n";
-          sAppStream << fLeft + fWidth / 2 << " " << fTop - fWidth / 2
-                     << " l\n";
-          sAppStream << fRight - fWidth / 2 << " " << fTop - fWidth / 2
-                     << " l\n";
-          sAppStream << fRight - fWidth / 2 << " " << fBottom + fWidth / 2
-                     << " l\n";
-          sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2
-                     << " l S\n";
-        }
-        break;
-      case BorderStyle::BEVELED:
-      case BorderStyle::INSET:
-        sColor = GenerateColorAP(crLeftTop, PaintOperation::FILL);
-        if (sColor.GetLength() > 0) {
-          sAppStream << sColor;
-          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth
-                     << " m\n";
-          sAppStream << fLeft + fHalfWidth << " " << fTop - fHalfWidth
-                     << " l\n";
-          sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth
-                     << " l\n";
-          sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
-                     << " l\n";
-          sAppStream << fLeft + fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
-                     << " l\n";
-          sAppStream << fLeft + fHalfWidth * 2 << " "
-                     << fBottom + fHalfWidth * 2 << " l f\n";
-        }
-        sColor = GenerateColorAP(crRightBottom, PaintOperation::FILL);
-        if (sColor.GetLength() > 0) {
-          sAppStream << sColor;
-          sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth
-                     << " m\n";
-          sAppStream << fRight - fHalfWidth << " " << fBottom + fHalfWidth
-                     << " l\n";
-          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth
-                     << " l\n";
-          sAppStream << fLeft + fHalfWidth * 2 << " "
-                     << fBottom + fHalfWidth * 2 << " l\n";
-          sAppStream << fRight - fHalfWidth * 2 << " "
-                     << fBottom + fHalfWidth * 2 << " l\n";
-          sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
-                     << " l f\n";
-        }
-        sColor = GenerateColorAP(color, PaintOperation::FILL);
-        if (sColor.GetLength() > 0) {
-          sAppStream << sColor;
-          sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " "
-                     << fTop - fBottom << " re\n";
-          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " "
-                     << fRight - fLeft - fHalfWidth * 2 << " "
-                     << fTop - fBottom - fHalfWidth * 2 << " re f*\n";
-        }
-        break;
-      case BorderStyle::UNDERLINE:
-        sColor = GenerateColorAP(color, PaintOperation::STROKE);
-        if (sColor.GetLength() > 0) {
-          sAppStream << sColor;
-          sAppStream << fWidth << " w\n";
-          sAppStream << fLeft << " " << fBottom + fWidth / 2 << " m\n";
-          sAppStream << fRight << " " << fBottom + fWidth / 2 << " l S\n";
-        }
-        break;
-    }
-  }
-  return ByteString(sAppStream);
-}
-
-ByteString GetColorStringWithDefault(CPDF_Array* pColor,
-                                     const CFX_Color& crDefaultColor,
-                                     PaintOperation nOperation) {
-  if (pColor) {
-    CFX_Color color = fpdfdoc::CFXColorFromArray(*pColor);
-    return GenerateColorAP(color, nOperation);
-  }
-
-  return GenerateColorAP(crDefaultColor, nOperation);
-}
-
-float GetBorderWidth(const CPDF_Dictionary& pAnnotDict) {
-  if (const CPDF_Dictionary* pBorderStyleDict = pAnnotDict.GetDictFor("BS")) {
-    if (pBorderStyleDict->KeyExist("W"))
-      return pBorderStyleDict->GetNumberFor("W");
-  }
-
-  if (const CPDF_Array* pBorderArray =
-          pAnnotDict.GetArrayFor(pdfium::annotation::kBorder)) {
-    if (pBorderArray->size() > 2)
-      return pBorderArray->GetNumberAt(2);
-  }
-
-  return 1;
-}
-
-const CPDF_Array* GetDashArray(const CPDF_Dictionary& pAnnotDict) {
-  if (const CPDF_Dictionary* pBorderStyleDict = pAnnotDict.GetDictFor("BS")) {
-    if (pBorderStyleDict->GetStringFor("S") == "D")
-      return pBorderStyleDict->GetArrayFor("D");
-  }
-
-  if (const CPDF_Array* pBorderArray =
-          pAnnotDict.GetArrayFor(pdfium::annotation::kBorder)) {
-    if (pBorderArray->size() == 4)
-      return pBorderArray->GetArrayAt(3);
-  }
-
-  return nullptr;
-}
-
-ByteString GetDashPatternString(const CPDF_Dictionary& pAnnotDict) {
-  const CPDF_Array* pDashArray = GetDashArray(pAnnotDict);
-  if (!pDashArray || pDashArray->IsEmpty())
-    return ByteString();
-
-  // Support maximum of ten elements in the dash array.
-  size_t pDashArrayCount = std::min<size_t>(pDashArray->size(), 10);
-  std::ostringstream sDashStream;
-
-  sDashStream << "[";
-  for (size_t i = 0; i < pDashArrayCount; ++i)
-    sDashStream << pDashArray->GetNumberAt(i) << " ";
-  sDashStream << "] 0 d\n";
-
-  return ByteString(sDashStream);
-}
-
-ByteString GetPopupContentsString(CPDF_Document* pDoc,
-                                  const CPDF_Dictionary& pAnnotDict,
-                                  const RetainPtr<CPDF_Font>& pDefFont,
-                                  const ByteString& sFontName) {
-  WideString swValue(pAnnotDict.GetUnicodeTextFor(pdfium::form_fields::kT));
-  swValue += L'\n';
-  swValue += pAnnotDict.GetUnicodeTextFor(pdfium::annotation::kContents);
-  CPVT_FontMap map(pDoc, nullptr, pDefFont, sFontName);
-
-  CPDF_VariableText::Provider prd(&map);
-  CPDF_VariableText vt;
-  vt.SetProvider(&prd);
-  vt.SetPlateRect(pAnnotDict.GetRectFor(pdfium::annotation::kRect));
-  vt.SetFontSize(12);
-  vt.SetAutoReturn(true);
-  vt.SetMultiLine(true);
-
-  vt.Initialize();
-  vt.SetText(swValue);
-  vt.RearrangeAll();
-  CFX_PointF ptOffset(3.0f, -3.0f);
-  ByteString sContent =
-      GenerateEditAP(&map, vt.GetIterator(), ptOffset, false, 0);
-
-  if (sContent.IsEmpty())
-    return ByteString();
-
-  std::ostringstream sAppStream;
-  sAppStream << "BT\n"
-             << GenerateColorAP(CFX_Color(CFX_Color::kRGB, 0, 0, 0),
-                                PaintOperation::FILL)
-             << sContent << "ET\n"
-             << "Q\n";
-  return ByteString(sAppStream);
-}
-
-RetainPtr<CPDF_Dictionary> GenerateResourceFontDict(
-    CPDF_Document* pDoc,
-    const ByteString& sFontDictName) {
-  CPDF_Dictionary* pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
-  pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
-  pFontDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
-  pFontDict->SetNewFor<CPDF_Name>("BaseFont", CFX_Font::kDefaultAnsiFontName);
-  pFontDict->SetNewFor<CPDF_Name>("Encoding", "WinAnsiEncoding");
-
-  auto pResourceFontDict = pDoc->New<CPDF_Dictionary>();
-  pResourceFontDict->SetNewFor<CPDF_Reference>(sFontDictName, pDoc,
-                                               pFontDict->GetObjNum());
-  return pResourceFontDict;
-}
-
-ByteString GetPaintOperatorString(bool bIsStrokeRect, bool bIsFillRect) {
-  if (bIsStrokeRect)
-    return bIsFillRect ? "b" : "s";
-  return bIsFillRect ? "f" : "n";
-}
-
-ByteString GenerateTextSymbolAP(const CFX_FloatRect& rect) {
-  std::ostringstream sAppStream;
-  sAppStream << GenerateColorAP(CFX_Color(CFX_Color::kRGB, 1, 1, 0),
-                                PaintOperation::FILL);
-  sAppStream << GenerateColorAP(CFX_Color(CFX_Color::kRGB, 0, 0, 0),
-                                PaintOperation::STROKE);
-
-  const float fBorderWidth = 1;
-  sAppStream << fBorderWidth << " w\n";
-
-  const float fHalfWidth = fBorderWidth / 2;
-  const float fTipDelta = 4;
-
-  CFX_FloatRect outerRect1 = rect;
-  outerRect1.Deflate(fHalfWidth, fHalfWidth);
-  outerRect1.bottom += fTipDelta;
-
-  CFX_FloatRect outerRect2 = outerRect1;
-  outerRect2.left += fTipDelta;
-  outerRect2.right = outerRect2.left + fTipDelta;
-  outerRect2.top = outerRect2.bottom - fTipDelta;
-  float outerRect2Middle = (outerRect2.left + outerRect2.right) / 2;
-
-  // Draw outer boxes.
-  sAppStream << outerRect1.left << " " << outerRect1.bottom << " m\n"
-             << outerRect1.left << " " << outerRect1.top << " l\n"
-             << outerRect1.right << " " << outerRect1.top << " l\n"
-             << outerRect1.right << " " << outerRect1.bottom << " l\n"
-             << outerRect2.right << " " << outerRect2.bottom << " l\n"
-             << outerRect2Middle << " " << outerRect2.top << " l\n"
-             << outerRect2.left << " " << outerRect2.bottom << " l\n"
-             << outerRect1.left << " " << outerRect1.bottom << " l\n";
-
-  // Draw inner lines.
-  CFX_FloatRect lineRect = outerRect1;
-  const float fXDelta = 2;
-  const float fYDelta = (lineRect.top - lineRect.bottom) / 4;
-
-  lineRect.left += fXDelta;
-  lineRect.right -= fXDelta;
-  for (int i = 0; i < 3; ++i) {
-    lineRect.top -= fYDelta;
-    sAppStream << lineRect.left << " " << lineRect.top << " m\n"
-               << lineRect.right << " " << lineRect.top << " l\n";
-  }
-  sAppStream << "B*\n";
-
-  return ByteString(sAppStream);
-}
-
-RetainPtr<CPDF_Dictionary> GenerateExtGStateDict(
-    const CPDF_Dictionary& pAnnotDict,
-    const ByteString& sExtGSDictName,
-    const ByteString& sBlendMode) {
-  auto pGSDict =
-      pdfium::MakeRetain<CPDF_Dictionary>(pAnnotDict.GetByteStringPool());
-  pGSDict->SetNewFor<CPDF_Name>("Type", "ExtGState");
-
-  float fOpacity =
-      pAnnotDict.KeyExist("CA") ? pAnnotDict.GetNumberFor("CA") : 1;
-  pGSDict->SetNewFor<CPDF_Number>("CA", fOpacity);
-  pGSDict->SetNewFor<CPDF_Number>("ca", fOpacity);
-  pGSDict->SetNewFor<CPDF_Boolean>("AIS", false);
-  pGSDict->SetNewFor<CPDF_Name>("BM", sBlendMode);
-
-  auto pExtGStateDict =
-      pdfium::MakeRetain<CPDF_Dictionary>(pAnnotDict.GetByteStringPool());
-  pExtGStateDict->SetFor(sExtGSDictName, pGSDict);
-  return pExtGStateDict;
-}
-
-RetainPtr<CPDF_Dictionary> GenerateResourceDict(
-    CPDF_Document* pDoc,
-    RetainPtr<CPDF_Dictionary> pExtGStateDict,
-    RetainPtr<CPDF_Dictionary> pResourceFontDict) {
-  auto pResourceDict = pDoc->New<CPDF_Dictionary>();
-  if (pExtGStateDict)
-    pResourceDict->SetFor("ExtGState", pExtGStateDict);
-  if (pResourceFontDict)
-    pResourceDict->SetFor("Font", pResourceFontDict);
-  return pResourceDict;
-}
-
-void GenerateAndSetAPDict(CPDF_Document* pDoc,
-                          CPDF_Dictionary* pAnnotDict,
-                          std::ostringstream* psAppStream,
-                          RetainPtr<CPDF_Dictionary> pResourceDict,
-                          bool bIsTextMarkupAnnotation) {
-  CPDF_Stream* pNormalStream = pDoc->NewIndirect<CPDF_Stream>();
-  pNormalStream->SetDataFromStringstream(psAppStream);
-
-  CPDF_Dictionary* pAPDict = pAnnotDict->GetDictFor(pdfium::annotation::kAP);
-  if (!pAPDict)
-    pAPDict = pAnnotDict->SetNewFor<CPDF_Dictionary>(pdfium::annotation::kAP);
-
-  pAPDict->SetNewFor<CPDF_Reference>("N", pDoc, pNormalStream->GetObjNum());
-
-  CPDF_Dictionary* pStreamDict = pNormalStream->GetDict();
-  pStreamDict->SetNewFor<CPDF_Number>("FormType", 1);
-  pStreamDict->SetNewFor<CPDF_Name>("Type", "XObject");
-  pStreamDict->SetNewFor<CPDF_Name>("Subtype", "Form");
-  pStreamDict->SetMatrixFor("Matrix", CFX_Matrix());
-
-  CFX_FloatRect rect = bIsTextMarkupAnnotation
-                           ? CPDF_Annot::BoundingRectFromQuadPoints(pAnnotDict)
-                           : pAnnotDict->GetRectFor(pdfium::annotation::kRect);
-  pStreamDict->SetRectFor("BBox", rect);
-  pStreamDict->SetFor("Resources", pResourceDict);
-}
-
-bool GenerateCircleAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
-  std::ostringstream sAppStream;
-  ByteString sExtGSDictName = "GS";
-  sAppStream << "/" << sExtGSDictName << " gs ";
-
-  CPDF_Array* pInteriorColor = pAnnotDict->GetArrayFor("IC");
-  sAppStream << GetColorStringWithDefault(
-      pInteriorColor, CFX_Color(CFX_Color::kTransparent), PaintOperation::FILL);
-
-  sAppStream << GetColorStringWithDefault(
-      pAnnotDict->GetArrayFor(pdfium::annotation::kC),
-      CFX_Color(CFX_Color::kRGB, 0, 0, 0), PaintOperation::STROKE);
-
-  float fBorderWidth = GetBorderWidth(*pAnnotDict);
-  bool bIsStrokeRect = fBorderWidth > 0;
-
-  if (bIsStrokeRect) {
-    sAppStream << fBorderWidth << " w ";
-    sAppStream << GetDashPatternString(*pAnnotDict);
-  }
-
-  CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
-  rect.Normalize();
-
-  if (bIsStrokeRect) {
-    // Deflating rect because stroking a path entails painting all points whose
-    // perpendicular distance from the path in user space is less than or equal
-    // to half the line width.
-    rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
-  }
-
-  const float fMiddleX = (rect.left + rect.right) / 2;
-  const float fMiddleY = (rect.top + rect.bottom) / 2;
-
-  // |fL| is precalculated approximate value of 4 * tan((3.14 / 2) / 4) / 3,
-  // where |fL| * radius is a good approximation of control points for
-  // arc with 90 degrees.
-  const float fL = 0.5523f;
-  const float fDeltaX = fL * rect.Width() / 2.0;
-  const float fDeltaY = fL * rect.Height() / 2.0;
-
-  // Starting point
-  sAppStream << fMiddleX << " " << rect.top << " m\n";
-  // First Bezier Curve
-  sAppStream << fMiddleX + fDeltaX << " " << rect.top << " " << rect.right
-             << " " << fMiddleY + fDeltaY << " " << rect.right << " "
-             << fMiddleY << " c\n";
-  // Second Bezier Curve
-  sAppStream << rect.right << " " << fMiddleY - fDeltaY << " "
-             << fMiddleX + fDeltaX << " " << rect.bottom << " " << fMiddleX
-             << " " << rect.bottom << " c\n";
-  // Third Bezier Curve
-  sAppStream << fMiddleX - fDeltaX << " " << rect.bottom << " " << rect.left
-             << " " << fMiddleY - fDeltaY << " " << rect.left << " " << fMiddleY
-             << " c\n";
-  // Fourth Bezier Curve
-  sAppStream << rect.left << " " << fMiddleY + fDeltaY << " "
-             << fMiddleX - fDeltaX << " " << rect.top << " " << fMiddleX << " "
-             << rect.top << " c\n";
-
-  bool bIsFillRect = pInteriorColor && !pInteriorColor->IsEmpty();
-  sAppStream << GetPaintOperatorString(bIsStrokeRect, bIsFillRect) << "\n";
-
-  auto pExtGStateDict =
-      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
-  auto pResourceDict =
-      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
-  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
-                       false /*IsTextMarkupAnnotation*/);
-  return true;
-}
-
-bool GenerateHighlightAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
-  std::ostringstream sAppStream;
-  ByteString sExtGSDictName = "GS";
-  sAppStream << "/" << sExtGSDictName << " gs ";
-
-  sAppStream << GetColorStringWithDefault(
-      pAnnotDict->GetArrayFor(pdfium::annotation::kC),
-      CFX_Color(CFX_Color::kRGB, 1, 1, 0), PaintOperation::FILL);
-
-  CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
-  if (pArray) {
-    size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray);
-    for (size_t i = 0; i < nQuadPointCount; ++i) {
-      CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
-      rect.Normalize();
-
-      sAppStream << rect.left << " " << rect.top << " m " << rect.right << " "
-                 << rect.top << " l " << rect.right << " " << rect.bottom
-                 << " l " << rect.left << " " << rect.bottom << " l h f\n";
-    }
-  }
-
-  auto pExtGStateDict =
-      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Multiply");
-  auto pResourceDict =
-      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
-  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
-                       true /*IsTextMarkupAnnotation*/);
-
-  return true;
-}
-
-bool GenerateInkAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
-  float fBorderWidth = GetBorderWidth(*pAnnotDict);
-  bool bIsStroke = fBorderWidth > 0;
-
-  if (!bIsStroke)
-    return false;
-
-  CPDF_Array* pInkList = pAnnotDict->GetArrayFor("InkList");
-  if (!pInkList || pInkList->IsEmpty())
-    return false;
-
-  std::ostringstream sAppStream;
-  ByteString sExtGSDictName = "GS";
-  sAppStream << "/" << sExtGSDictName << " gs ";
-
-  sAppStream << GetColorStringWithDefault(
-      pAnnotDict->GetArrayFor(pdfium::annotation::kC),
-      CFX_Color(CFX_Color::kRGB, 0, 0, 0), PaintOperation::STROKE);
-
-  sAppStream << fBorderWidth << " w ";
-  sAppStream << GetDashPatternString(*pAnnotDict);
-
-  // Set inflated rect as a new rect because paths near the border with large
-  // width should not be clipped to the original rect.
-  CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
-  rect.Inflate(fBorderWidth / 2, fBorderWidth / 2);
-  pAnnotDict->SetRectFor(pdfium::annotation::kRect, rect);
-
-  for (size_t i = 0; i < pInkList->size(); i++) {
-    CPDF_Array* pInkCoordList = pInkList->GetArrayAt(i);
-    if (!pInkCoordList || pInkCoordList->size() < 2)
-      continue;
-
-    sAppStream << pInkCoordList->GetNumberAt(0) << " "
-               << pInkCoordList->GetNumberAt(1) << " m ";
-
-    for (size_t j = 0; j < pInkCoordList->size() - 1; j += 2) {
-      sAppStream << pInkCoordList->GetNumberAt(j) << " "
-                 << pInkCoordList->GetNumberAt(j + 1) << " l ";
-    }
-
-    sAppStream << "S\n";
-  }
-
-  auto pExtGStateDict =
-      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
-  auto pResourceDict =
-      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
-  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
-                       false /*IsTextMarkupAnnotation*/);
-  return true;
-}
-
-bool GenerateTextAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
-  std::ostringstream sAppStream;
-  ByteString sExtGSDictName = "GS";
-  sAppStream << "/" << sExtGSDictName << " gs ";
-
-  CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
-  const float fNoteLength = 20;
-  CFX_FloatRect noteRect(rect.left, rect.bottom, rect.left + fNoteLength,
-                         rect.bottom + fNoteLength);
-  pAnnotDict->SetRectFor(pdfium::annotation::kRect, noteRect);
-
-  sAppStream << GenerateTextSymbolAP(noteRect);
-
-  auto pExtGStateDict =
-      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
-  auto pResourceDict =
-      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
-  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
-                       false /*IsTextMarkupAnnotation*/);
-  return true;
-}
-
-bool GenerateUnderlineAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
-  std::ostringstream sAppStream;
-  ByteString sExtGSDictName = "GS";
-  sAppStream << "/" << sExtGSDictName << " gs ";
-
-  sAppStream << GetColorStringWithDefault(
-      pAnnotDict->GetArrayFor(pdfium::annotation::kC),
-      CFX_Color(CFX_Color::kRGB, 0, 0, 0), PaintOperation::STROKE);
-
-  CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
-  if (pArray) {
-    static constexpr float kLineWidth = 1.0f;
-    sAppStream << kLineWidth << " w ";
-    size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray);
-    for (size_t i = 0; i < nQuadPointCount; ++i) {
-      CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
-      rect.Normalize();
-      sAppStream << rect.left << " " << rect.bottom + kLineWidth << " m "
-                 << rect.right << " " << rect.bottom + kLineWidth << " l S\n";
-    }
-  }
-
-  auto pExtGStateDict =
-      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
-  auto pResourceDict =
-      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
-  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
-                       true /*IsTextMarkupAnnotation*/);
-  return true;
-}
-
-bool GeneratePopupAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
-  std::ostringstream sAppStream;
-  ByteString sExtGSDictName = "GS";
-  sAppStream << "/" << sExtGSDictName << " gs\n";
-
-  sAppStream << GenerateColorAP(CFX_Color(CFX_Color::kRGB, 1, 1, 0),
-                                PaintOperation::FILL);
-  sAppStream << GenerateColorAP(CFX_Color(CFX_Color::kRGB, 0, 0, 0),
-                                PaintOperation::STROKE);
-
-  const float fBorderWidth = 1;
-  sAppStream << fBorderWidth << " w\n";
-
-  CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
-  rect.Normalize();
-  rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
-
-  sAppStream << rect.left << " " << rect.bottom << " " << rect.Width() << " "
-             << rect.Height() << " re b\n";
-
-  ByteString sFontName = "FONT";
-  RetainPtr<CPDF_Dictionary> pResourceFontDict =
-      GenerateResourceFontDict(pDoc, sFontName);
-
-  auto* pData = CPDF_DocPageData::FromDocument(pDoc);
-  RetainPtr<CPDF_Font> pDefFont = pData->GetFont(pResourceFontDict.Get());
-  if (!pDefFont)
-    return false;
-
-  RetainPtr<CPDF_Dictionary> pExtGStateDict =
-      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
-  RetainPtr<CPDF_Dictionary> pResourceDict = GenerateResourceDict(
-      pDoc, std::move(pExtGStateDict), std::move(pResourceFontDict));
-
-  sAppStream << GetPopupContentsString(pDoc, *pAnnotDict, pDefFont, sFontName);
-  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
-                       false /*IsTextMarkupAnnotation*/);
-  return true;
-}
-
-bool GenerateSquareAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
-  std::ostringstream sAppStream;
-  ByteString sExtGSDictName = "GS";
-  sAppStream << "/" << sExtGSDictName << " gs ";
-
-  CPDF_Array* pInteriorColor = pAnnotDict->GetArrayFor("IC");
-  sAppStream << GetColorStringWithDefault(
-      pInteriorColor, CFX_Color(CFX_Color::kTransparent), PaintOperation::FILL);
-
-  sAppStream << GetColorStringWithDefault(
-      pAnnotDict->GetArrayFor(pdfium::annotation::kC),
-      CFX_Color(CFX_Color::kRGB, 0, 0, 0), PaintOperation::STROKE);
-
-  float fBorderWidth = GetBorderWidth(*pAnnotDict);
-  bool bIsStrokeRect = fBorderWidth > 0;
-
-  if (bIsStrokeRect) {
-    sAppStream << fBorderWidth << " w ";
-    sAppStream << GetDashPatternString(*pAnnotDict);
-  }
-
-  CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
-  rect.Normalize();
-
-  if (bIsStrokeRect) {
-    // Deflating rect because stroking a path entails painting all points whose
-    // perpendicular distance from the path in user space is less than or equal
-    // to half the line width.
-    rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
-  }
-
-  bool bIsFillRect = pInteriorColor && (pInteriorColor->size() > 0);
-
-  sAppStream << rect.left << " " << rect.bottom << " " << rect.Width() << " "
-             << rect.Height() << " re "
-             << GetPaintOperatorString(bIsStrokeRect, bIsFillRect) << "\n";
-
-  auto pExtGStateDict =
-      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
-  auto pResourceDict =
-      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
-  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
-                       false /*IsTextMarkupAnnotation*/);
-  return true;
-}
-
-bool GenerateSquigglyAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
-  std::ostringstream sAppStream;
-  ByteString sExtGSDictName = "GS";
-  sAppStream << "/" << sExtGSDictName << " gs ";
-
-  sAppStream << GetColorStringWithDefault(
-      pAnnotDict->GetArrayFor(pdfium::annotation::kC),
-      CFX_Color(CFX_Color::kRGB, 0, 0, 0), PaintOperation::STROKE);
-
-  CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
-  if (pArray) {
-    static constexpr float kLineWidth = 1.0f;
-    static constexpr float kDelta = 2.0f;
-    sAppStream << kLineWidth << " w ";
-    size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray);
-    for (size_t i = 0; i < nQuadPointCount; ++i) {
-      CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
-      rect.Normalize();
-
-      const float fTop = rect.bottom + kDelta;
-      const float fBottom = rect.bottom;
-      sAppStream << rect.left << " " << fTop << " m ";
-
-      float fX = rect.left + kDelta;
-      bool isUpwards = false;
-      while (fX < rect.right) {
-        sAppStream << fX << " " << (isUpwards ? fTop : fBottom) << " l ";
-        fX += kDelta;
-        isUpwards = !isUpwards;
-      }
-
-      float fRemainder = rect.right - (fX - kDelta);
-      if (isUpwards)
-        sAppStream << rect.right << " " << fBottom + fRemainder << " l ";
-      else
-        sAppStream << rect.right << " " << fTop - fRemainder << " l ";
-
-      sAppStream << "S\n";
-    }
-  }
-
-  auto pExtGStateDict =
-      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
-  auto pResourceDict =
-      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
-  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
-                       true /*IsTextMarkupAnnotation*/);
-  return true;
-}
-
-bool GenerateStrikeOutAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
-  std::ostringstream sAppStream;
-  ByteString sExtGSDictName = "GS";
-  sAppStream << "/" << sExtGSDictName << " gs ";
-
-  sAppStream << GetColorStringWithDefault(
-      pAnnotDict->GetArrayFor(pdfium::annotation::kC),
-      CFX_Color(CFX_Color::kRGB, 0, 0, 0), PaintOperation::STROKE);
-
-  CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
-  if (pArray) {
-    static constexpr float kLineWidth = 1.0f;
-    size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray);
-    for (size_t i = 0; i < nQuadPointCount; ++i) {
-      CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
-      rect.Normalize();
-
-      float fY = (rect.top + rect.bottom) / 2;
-      sAppStream << kLineWidth << " w " << rect.left << " " << fY << " m "
-                 << rect.right << " " << fY << " l S\n";
-    }
-  }
-
-  auto pExtGStateDict =
-      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
-  auto pResourceDict =
-      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
-  GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
-                       true /*IsTextMarkupAnnotation*/);
-  return true;
-}
-
-}  // namespace
-
-// static
-void CPVT_GenerateAP::GenerateFormAP(CPDF_Document* pDoc,
-                                     CPDF_Dictionary* pAnnotDict,
-                                     FormType type) {
-  CPDF_Dictionary* pRootDict = pDoc->GetRoot();
-  if (!pRootDict)
-    return;
-
-  CPDF_Dictionary* pFormDict = pRootDict->GetDictFor("AcroForm");
-  if (!pFormDict)
-    return;
-
-  ByteString DA;
-  if (CPDF_Object* pDAObj = CPDF_FormField::GetFieldAttr(pAnnotDict, "DA"))
-    DA = pDAObj->GetString();
-  if (DA.IsEmpty())
-    DA = pFormDict->GetStringFor("DA");
-  if (DA.IsEmpty())
-    return;
-
-  CPDF_DefaultAppearance appearance(DA);
-
-  float fFontSize = 0;
-  Optional<ByteString> font = appearance.GetFont(&fFontSize);
-  if (!font)
-    return;
-
-  ByteString font_name = *font;
-  CFX_Color crText = fpdfdoc::CFXColorFromString(DA);
-  CPDF_Dictionary* pDRDict = pFormDict->GetDictFor("DR");
-  if (!pDRDict)
-    return;
-
-  CPDF_Dictionary* pDRFontDict = pDRDict->GetDictFor("Font");
-  if (!ValidateFontResourceDict(pDRFontDict))
-    return;
-
-  CPDF_Dictionary* pFontDict = pDRFontDict->GetDictFor(font_name);
-  if (!pFontDict) {
-    pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
-    pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
-    pFontDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
-    pFontDict->SetNewFor<CPDF_Name>("BaseFont", CFX_Font::kDefaultAnsiFontName);
-    pFontDict->SetNewFor<CPDF_Name>("Encoding", "WinAnsiEncoding");
-    pDRFontDict->SetNewFor<CPDF_Reference>(font_name, pDoc,
-                                           pFontDict->GetObjNum());
-  }
-  auto* pData = CPDF_DocPageData::FromDocument(pDoc);
-  RetainPtr<CPDF_Font> pDefFont = pData->GetFont(pFontDict);
-  if (!pDefFont)
-    return;
-
-  CFX_FloatRect rcAnnot = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
-  CPDF_Dictionary* pMKDict = pAnnotDict->GetDictFor("MK");
-  int32_t nRotate = pMKDict ? pMKDict->GetIntegerFor("R") : 0;
-
-  CFX_FloatRect rcBBox;
-  CFX_Matrix matrix;
-  switch (nRotate % 360) {
-    case 0:
-      rcBBox = CFX_FloatRect(0, 0, rcAnnot.right - rcAnnot.left,
-                             rcAnnot.top - rcAnnot.bottom);
-      break;
-    case 90:
-      matrix = CFX_Matrix(0, 1, -1, 0, rcAnnot.right - rcAnnot.left, 0);
-      rcBBox = CFX_FloatRect(0, 0, rcAnnot.top - rcAnnot.bottom,
-                             rcAnnot.right - rcAnnot.left);
-      break;
-    case 180:
-      matrix = CFX_Matrix(-1, 0, 0, -1, rcAnnot.right - rcAnnot.left,
-                          rcAnnot.top - rcAnnot.bottom);
-      rcBBox = CFX_FloatRect(0, 0, rcAnnot.right - rcAnnot.left,
-                             rcAnnot.top - rcAnnot.bottom);
-      break;
-    case 270:
-      matrix = CFX_Matrix(0, -1, 1, 0, 0, rcAnnot.top - rcAnnot.bottom);
-      rcBBox = CFX_FloatRect(0, 0, rcAnnot.top - rcAnnot.bottom,
-                             rcAnnot.right - rcAnnot.left);
-      break;
-  }
-
-  BorderStyle nBorderStyle = BorderStyle::SOLID;
-  float fBorderWidth = 1;
-  CPVT_Dash dsBorder(3, 0, 0);
-  CFX_Color crLeftTop;
-  CFX_Color crRightBottom;
-  if (CPDF_Dictionary* pBSDict = pAnnotDict->GetDictFor("BS")) {
-    if (pBSDict->KeyExist("W"))
-      fBorderWidth = pBSDict->GetNumberFor("W");
-
-    if (CPDF_Array* pArray = pBSDict->GetArrayFor("D")) {
-      dsBorder = CPVT_Dash(pArray->GetIntegerAt(0), pArray->GetIntegerAt(1),
-                           pArray->GetIntegerAt(2));
-    }
-    if (pBSDict->GetStringFor("S").GetLength()) {
-      switch (pBSDict->GetStringFor("S")[0]) {
-        case 'S':
-          nBorderStyle = BorderStyle::SOLID;
-          break;
-        case 'D':
-          nBorderStyle = BorderStyle::DASH;
-          break;
-        case 'B':
-          nBorderStyle = BorderStyle::BEVELED;
-          fBorderWidth *= 2;
-          crLeftTop = CFX_Color(CFX_Color::kGray, 1);
-          crRightBottom = CFX_Color(CFX_Color::kGray, 0.5);
-          break;
-        case 'I':
-          nBorderStyle = BorderStyle::INSET;
-          fBorderWidth *= 2;
-          crLeftTop = CFX_Color(CFX_Color::kGray, 0.5);
-          crRightBottom = CFX_Color(CFX_Color::kGray, 0.75);
-          break;
-        case 'U':
-          nBorderStyle = BorderStyle::UNDERLINE;
-          break;
-      }
-    }
-  }
-  CFX_Color crBorder;
-  CFX_Color crBG;
-  if (pMKDict) {
-    if (CPDF_Array* pArray = pMKDict->GetArrayFor("BC"))
-      crBorder = fpdfdoc::CFXColorFromArray(*pArray);
-    if (CPDF_Array* pArray = pMKDict->GetArrayFor("BG"))
-      crBG = fpdfdoc::CFXColorFromArray(*pArray);
-  }
-  std::ostringstream sAppStream;
-  ByteString sBG = GenerateColorAP(crBG, PaintOperation::FILL);
-  if (sBG.GetLength() > 0) {
-    sAppStream << "q\n"
-               << sBG << rcBBox.left << " " << rcBBox.bottom << " "
-               << rcBBox.Width() << " " << rcBBox.Height() << " re f\n"
-               << "Q\n";
-  }
-  ByteString sBorderStream =
-      GenerateBorderAP(rcBBox, fBorderWidth, crBorder, crLeftTop, crRightBottom,
-                       nBorderStyle, dsBorder);
-  if (sBorderStream.GetLength() > 0)
-    sAppStream << "q\n" << sBorderStream << "Q\n";
-
-  CFX_FloatRect rcBody =
-      CFX_FloatRect(rcBBox.left + fBorderWidth, rcBBox.bottom + fBorderWidth,
-                    rcBBox.right - fBorderWidth, rcBBox.top - fBorderWidth);
-  rcBody.Normalize();
-
-  CPDF_Dictionary* pAPDict = pAnnotDict->GetDictFor(pdfium::annotation::kAP);
-  if (!pAPDict)
-    pAPDict = pAnnotDict->SetNewFor<CPDF_Dictionary>(pdfium::annotation::kAP);
-
-  CPDF_Stream* pNormalStream = pAPDict->GetStreamFor("N");
-  if (!pNormalStream) {
-    pNormalStream = pDoc->NewIndirect<CPDF_Stream>();
-    pAPDict->SetNewFor<CPDF_Reference>("N", pDoc, pNormalStream->GetObjNum());
-  }
-  CPDF_Dictionary* pStreamDict = pNormalStream->GetDict();
-  if (pStreamDict) {
-    CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources");
-    if (pStreamResList) {
-      CPDF_Dictionary* pStreamResFontList = pStreamResList->GetDictFor("Font");
-      if (pStreamResFontList) {
-        if (!ValidateFontResourceDict(pStreamResFontList))
-          return;
-      } else {
-        pStreamResFontList = pStreamResList->SetNewFor<CPDF_Dictionary>("Font");
-      }
-      if (!pStreamResFontList->KeyExist(font_name)) {
-        pStreamResFontList->SetNewFor<CPDF_Reference>(font_name, pDoc,
-                                                      pFontDict->GetObjNum());
-      }
-    } else {
-      pStreamDict->SetFor("Resources", pFormDict->GetDictFor("DR")->Clone());
-    }
-    pStreamDict->SetMatrixFor("Matrix", matrix);
-    pStreamDict->SetRectFor("BBox", rcBBox);
-  }
-  switch (type) {
-    case CPVT_GenerateAP::kTextField: {
-      const CPDF_Object* pV =
-          CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kV);
-      WideString swValue = pV ? pV->GetUnicodeText() : WideString();
-      const CPDF_Object* pQ = CPDF_FormField::GetFieldAttr(pAnnotDict, "Q");
-      int32_t nAlign = pQ ? pQ->GetInteger() : 0;
-      const CPDF_Object* pFf =
-          CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kFf);
-      uint32_t dwFlags = pFf ? pFf->GetInteger() : 0;
-      const CPDF_Object* pMaxLen =
-          CPDF_FormField::GetFieldAttr(pAnnotDict, "MaxLen");
-      uint32_t dwMaxLen = pMaxLen ? pMaxLen->GetInteger() : 0;
-      CPVT_FontMap map(
-          pDoc, pStreamDict ? pStreamDict->GetDictFor("Resources") : nullptr,
-          pDefFont, font_name);
-      CPDF_VariableText::Provider prd(&map);
-      CPDF_VariableText vt;
-      vt.SetProvider(&prd);
-      vt.SetPlateRect(rcBody);
-      vt.SetAlignment(nAlign);
-      if (IsFloatZero(fFontSize))
-        vt.SetAutoFontSize(true);
-      else
-        vt.SetFontSize(fFontSize);
-
-      bool bMultiLine = (dwFlags >> 12) & 1;
-      if (bMultiLine) {
-        vt.SetMultiLine(true);
-        vt.SetAutoReturn(true);
-      }
-      uint16_t subWord = 0;
-      if ((dwFlags >> 13) & 1) {
-        subWord = '*';
-        vt.SetPasswordChar(subWord);
-      }
-      bool bCharArray = (dwFlags >> 24) & 1;
-      if (bCharArray)
-        vt.SetCharArray(dwMaxLen);
-      else
-        vt.SetLimitChar(dwMaxLen);
-
-      vt.Initialize();
-      vt.SetText(swValue);
-      vt.RearrangeAll();
-      CFX_FloatRect rcContent = vt.GetContentRect();
-      CFX_PointF ptOffset;
-      if (!bMultiLine) {
-        ptOffset =
-            CFX_PointF(0.0f, (rcContent.Height() - rcBody.Height()) / 2.0f);
-      }
-      ByteString sBody = GenerateEditAP(&map, vt.GetIterator(), ptOffset,
-                                        !bCharArray, subWord);
-      if (sBody.GetLength() > 0) {
-        sAppStream << "/Tx BMC\n"
-                   << "q\n";
-        if (rcContent.Width() > rcBody.Width() ||
-            rcContent.Height() > rcBody.Height()) {
-          sAppStream << rcBody.left << " " << rcBody.bottom << " "
-                     << rcBody.Width() << " " << rcBody.Height()
-                     << " re\nW\nn\n";
-        }
-        sAppStream << "BT\n"
-                   << GenerateColorAP(crText, PaintOperation::FILL) << sBody
-                   << "ET\n"
-                   << "Q\nEMC\n";
-      }
-      break;
-    }
-    case CPVT_GenerateAP::kComboBox: {
-      const CPDF_Object* pV =
-          CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kV);
-      WideString swValue = pV ? pV->GetUnicodeText() : WideString();
-      CPVT_FontMap map(
-          pDoc, pStreamDict ? pStreamDict->GetDictFor("Resources") : nullptr,
-          pDefFont, font_name);
-      CPDF_VariableText::Provider prd(&map);
-      CPDF_VariableText vt;
-      vt.SetProvider(&prd);
-      CFX_FloatRect rcButton = rcBody;
-      rcButton.left = rcButton.right - 13;
-      rcButton.Normalize();
-      CFX_FloatRect rcEdit = rcBody;
-      rcEdit.right = rcButton.left;
-      rcEdit.Normalize();
-      vt.SetPlateRect(rcEdit);
-      if (IsFloatZero(fFontSize))
-        vt.SetAutoFontSize(true);
-      else
-        vt.SetFontSize(fFontSize);
-
-      vt.Initialize();
-      vt.SetText(swValue);
-      vt.RearrangeAll();
-      CFX_FloatRect rcContent = vt.GetContentRect();
-      CFX_PointF ptOffset =
-          CFX_PointF(0.0f, (rcContent.Height() - rcEdit.Height()) / 2.0f);
-      ByteString sEdit =
-          GenerateEditAP(&map, vt.GetIterator(), ptOffset, true, 0);
-      if (sEdit.GetLength() > 0) {
-        sAppStream << "/Tx BMC\n"
-                   << "q\n";
-        sAppStream << rcEdit.left << " " << rcEdit.bottom << " "
-                   << rcEdit.Width() << " " << rcEdit.Height() << " re\nW\nn\n";
-        sAppStream << "BT\n"
-                   << GenerateColorAP(crText, PaintOperation::FILL) << sEdit
-                   << "ET\n"
-                   << "Q\nEMC\n";
-      }
-      ByteString sButton =
-          GenerateColorAP(CFX_Color(CFX_Color::kRGB, 220.0f / 255.0f,
-                                    220.0f / 255.0f, 220.0f / 255.0f),
-                          PaintOperation::FILL);
-      if (sButton.GetLength() > 0 && !rcButton.IsEmpty()) {
-        sAppStream << "q\n" << sButton;
-        sAppStream << rcButton.left << " " << rcButton.bottom << " "
-                   << rcButton.Width() << " " << rcButton.Height() << " re f\n";
-        sAppStream << "Q\n";
-        ByteString sButtonBorder = GenerateBorderAP(
-            rcButton, 2, CFX_Color(CFX_Color::kGray, 0),
-            CFX_Color(CFX_Color::kGray, 1), CFX_Color(CFX_Color::kGray, 0.5),
-            BorderStyle::BEVELED, CPVT_Dash(3, 0, 0));
-        if (sButtonBorder.GetLength() > 0)
-          sAppStream << "q\n" << sButtonBorder << "Q\n";
-
-        CFX_PointF ptCenter = CFX_PointF((rcButton.left + rcButton.right) / 2,
-                                         (rcButton.top + rcButton.bottom) / 2);
-        if (IsFloatBigger(rcButton.Width(), 6) &&
-            IsFloatBigger(rcButton.Height(), 6)) {
-          sAppStream << "q\n"
-                     << " 0 g\n";
-          sAppStream << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " m\n";
-          sAppStream << ptCenter.x + 3 << " " << ptCenter.y + 1.5f << " l\n";
-          sAppStream << ptCenter.x << " " << ptCenter.y - 1.5f << " l\n";
-          sAppStream << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " l f\n";
-          sAppStream << sButton << "Q\n";
-        }
-      }
-      break;
-    }
-    case CPVT_GenerateAP::kListBox: {
-      CPVT_FontMap map(
-          pDoc, pStreamDict ? pStreamDict->GetDictFor("Resources") : nullptr,
-          pDefFont, font_name);
-      CPDF_VariableText::Provider prd(&map);
-      CPDF_Array* pOpts =
-          ToArray(CPDF_FormField::GetFieldAttr(pAnnotDict, "Opt"));
-      CPDF_Array* pSels =
-          ToArray(CPDF_FormField::GetFieldAttr(pAnnotDict, "I"));
-      CPDF_Object* pTi = CPDF_FormField::GetFieldAttr(pAnnotDict, "TI");
-      int32_t nTop = pTi ? pTi->GetInteger() : 0;
-      std::ostringstream sBody;
-      if (pOpts) {
-        float fy = rcBody.top;
-        for (size_t i = nTop, sz = pOpts->size(); i < sz; i++) {
-          if (IsFloatSmaller(fy, rcBody.bottom))
-            break;
-
-          if (CPDF_Object* pOpt = pOpts->GetDirectObjectAt(i)) {
-            WideString swItem;
-            if (pOpt->IsString()) {
-              swItem = pOpt->GetUnicodeText();
-            } else if (CPDF_Array* pArray = pOpt->AsArray()) {
-              CPDF_Object* pDirectObj = pArray->GetDirectObjectAt(1);
-              if (pDirectObj)
-                swItem = pDirectObj->GetUnicodeText();
-            }
-            bool bSelected = false;
-            if (pSels) {
-              for (size_t s = 0, ssz = pSels->size(); s < ssz; s++) {
-                int value = pSels->GetIntegerAt(s);
-                if (value >= 0 && i == static_cast<size_t>(value)) {
-                  bSelected = true;
-                  break;
-                }
-              }
-            }
-            CPDF_VariableText vt;
-            vt.SetProvider(&prd);
-            vt.SetPlateRect(
-                CFX_FloatRect(rcBody.left, 0.0f, rcBody.right, 0.0f));
-            vt.SetFontSize(IsFloatZero(fFontSize) ? 12.0f : fFontSize);
-
-            vt.Initialize();
-            vt.SetText(swItem);
-            vt.RearrangeAll();
-            float fItemHeight = vt.GetContentRect().Height();
-            if (bSelected) {
-              CFX_FloatRect rcItem = CFX_FloatRect(
-                  rcBody.left, fy - fItemHeight, rcBody.right, fy);
-              sBody << "q\n"
-                    << GenerateColorAP(
-                           CFX_Color(CFX_Color::kRGB, 0, 51.0f / 255.0f,
-                                     113.0f / 255.0f),
-                           PaintOperation::FILL)
-                    << rcItem.left << " " << rcItem.bottom << " "
-                    << rcItem.Width() << " " << rcItem.Height() << " re f\n"
-                    << "Q\n";
-              sBody << "BT\n"
-                    << GenerateColorAP(CFX_Color(CFX_Color::kGray, 1),
-                                       PaintOperation::FILL)
-                    << GenerateEditAP(&map, vt.GetIterator(),
-                                      CFX_PointF(0.0f, fy), true, 0)
-                    << "ET\n";
-            } else {
-              sBody << "BT\n"
-                    << GenerateColorAP(crText, PaintOperation::FILL)
-                    << GenerateEditAP(&map, vt.GetIterator(),
-                                      CFX_PointF(0.0f, fy), true, 0)
-                    << "ET\n";
-            }
-            fy -= fItemHeight;
-          }
-        }
-      }
-      if (sBody.tellp() > 0) {
-        sAppStream << "/Tx BMC\nq\n"
-                   << rcBody.left << " " << rcBody.bottom << " "
-                   << rcBody.Width() << " " << rcBody.Height() << " re\nW\nn\n"
-                   << sBody.str() << "Q\nEMC\n";
-      }
-      break;
-    }
-  }
-
-  if (!pNormalStream)
-    return;
-
-  pNormalStream->SetDataFromStringstreamAndRemoveFilter(&sAppStream);
-  pStreamDict = pNormalStream->GetDict();
-  if (!pStreamDict)
-    return;
-
-  pStreamDict->SetMatrixFor("Matrix", matrix);
-  pStreamDict->SetRectFor("BBox", rcBBox);
-  CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources");
-  if (!pStreamResList) {
-    pStreamDict->SetFor("Resources", pFormDict->GetDictFor("DR")->Clone());
-    return;
-  }
-
-  CPDF_Dictionary* pStreamResFontList = pStreamResList->GetDictFor("Font");
-  if (pStreamResFontList) {
-    if (!ValidateFontResourceDict(pStreamResFontList))
-      return;
-  } else {
-    pStreamResFontList = pStreamResList->SetNewFor<CPDF_Dictionary>("Font");
-  }
-
-  if (!pStreamResFontList->KeyExist(font_name)) {
-    pStreamResFontList->SetNewFor<CPDF_Reference>(font_name, pDoc,
-                                                  pFontDict->GetObjNum());
-  }
-}
-
-// static
-void CPVT_GenerateAP::GenerateEmptyAP(CPDF_Document* pDoc,
-                                      CPDF_Dictionary* pAnnotDict) {
-  auto pExtGStateDict = GenerateExtGStateDict(*pAnnotDict, "GS", "Normal");
-  auto pResourceDict =
-      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
-
-  std::ostringstream sStream;
-  GenerateAndSetAPDict(pDoc, pAnnotDict, &sStream, std::move(pResourceDict),
-                       false);
-}
-
-// static
-bool CPVT_GenerateAP::GenerateAnnotAP(CPDF_Document* pDoc,
-                                      CPDF_Dictionary* pAnnotDict,
-                                      CPDF_Annot::Subtype subtype) {
-  switch (subtype) {
-    case CPDF_Annot::Subtype::CIRCLE:
-      return GenerateCircleAP(pDoc, pAnnotDict);
-    case CPDF_Annot::Subtype::HIGHLIGHT:
-      return GenerateHighlightAP(pDoc, pAnnotDict);
-    case CPDF_Annot::Subtype::INK:
-      return GenerateInkAP(pDoc, pAnnotDict);
-    case CPDF_Annot::Subtype::POPUP:
-      return GeneratePopupAP(pDoc, pAnnotDict);
-    case CPDF_Annot::Subtype::SQUARE:
-      return GenerateSquareAP(pDoc, pAnnotDict);
-    case CPDF_Annot::Subtype::SQUIGGLY:
-      return GenerateSquigglyAP(pDoc, pAnnotDict);
-    case CPDF_Annot::Subtype::STRIKEOUT:
-      return GenerateStrikeOutAP(pDoc, pAnnotDict);
-    case CPDF_Annot::Subtype::TEXT:
-      return GenerateTextAP(pDoc, pAnnotDict);
-    case CPDF_Annot::Subtype::UNDERLINE:
-      return GenerateUnderlineAP(pDoc, pAnnotDict);
-    default:
-      return false;
-  }
-}
diff --git a/core/fpdfdoc/cpvt_generateap.h b/core/fpdfdoc/cpvt_generateap.h
deleted file mode 100644
index 78eebba..0000000
--- a/core/fpdfdoc/cpvt_generateap.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFDOC_CPVT_GENERATEAP_H_
-#define CORE_FPDFDOC_CPVT_GENERATEAP_H_
-
-#include "core/fpdfdoc/cpdf_annot.h"
-#include "core/fxcrt/fx_system.h"
-
-class CPDF_Dictionary;
-class CPDF_Document;
-
-class CPVT_GenerateAP {
- public:
-  enum FormType { kTextField, kComboBox, kListBox };
-
-  static void GenerateFormAP(CPDF_Document* pDoc,
-                             CPDF_Dictionary* pAnnotDict,
-                             FormType type);
-
-  static void GenerateEmptyAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict);
-
-  static bool GenerateAnnotAP(CPDF_Document* pDoc,
-                              CPDF_Dictionary* pAnnotDict,
-                              CPDF_Annot::Subtype subtype);
-
-  CPVT_GenerateAP() = delete;
-  CPVT_GenerateAP(const CPVT_GenerateAP&) = delete;
-  CPVT_GenerateAP& operator=(const CPVT_GenerateAP&) = delete;
-};
-
-#endif  // CORE_FPDFDOC_CPVT_GENERATEAP_H_
diff --git a/core/fpdfdoc/cpvt_line.h b/core/fpdfdoc/cpvt_line.h
index 087034d..f50ffd5 100644
--- a/core/fpdfdoc/cpvt_line.h
+++ b/core/fpdfdoc/cpvt_line.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,6 @@
 
 #include "core/fpdfdoc/cpvt_wordplace.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
 
 class CPVT_Line {
  public:
diff --git a/core/fpdfdoc/cpvt_lineinfo.h b/core/fpdfdoc/cpvt_lineinfo.h
index 96a3234..ac1f41d 100644
--- a/core/fpdfdoc/cpvt_lineinfo.h
+++ b/core/fpdfdoc/cpvt_lineinfo.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,30 +7,17 @@
 #ifndef CORE_FPDFDOC_CPVT_LINEINFO_H_
 #define CORE_FPDFDOC_CPVT_LINEINFO_H_
 
-#include "core/fxcrt/fx_system.h"
+#include <stdint.h>
 
-class CPVT_LineInfo {
- public:
-  CPVT_LineInfo();
-
-  int32_t nTotalWord;
-  int32_t nBeginWordIndex;
-  int32_t nEndWordIndex;
-  float fLineX;
-  float fLineY;
-  float fLineWidth;
-  float fLineAscent;
-  float fLineDescent;
+struct CPVT_LineInfo {
+  int32_t nTotalWord = 0;
+  int32_t nBeginWordIndex = -1;
+  int32_t nEndWordIndex = -1;
+  float fLineX = 0.0f;
+  float fLineY = 0.0f;
+  float fLineWidth = 0.0f;
+  float fLineAscent = 0.0f;
+  float fLineDescent = 0.0f;
 };
 
-inline CPVT_LineInfo::CPVT_LineInfo()
-    : nTotalWord(0),
-      nBeginWordIndex(-1),
-      nEndWordIndex(-1),
-      fLineX(0.0f),
-      fLineY(0.0f),
-      fLineWidth(0.0f),
-      fLineAscent(0.0f),
-      fLineDescent(0.0f) {}
-
 #endif  // CORE_FPDFDOC_CPVT_LINEINFO_H_
diff --git a/core/fpdfdoc/cpvt_section.cpp b/core/fpdfdoc/cpvt_section.cpp
new file mode 100644
index 0000000..5c452da
--- /dev/null
+++ b/core/fpdfdoc/cpvt_section.cpp
@@ -0,0 +1,760 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfdoc/cpvt_section.h"
+
+#include <algorithm>
+
+#include "core/fpdfdoc/cpvt_variabletext.h"
+#include "core/fpdfdoc/cpvt_wordinfo.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/check.h"
+
+namespace {
+
+constexpr uint8_t kSpecialChars[128] = {
+    0x00, 0x0C, 0x08, 0x0C, 0x08, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00,
+    0x10, 0x00, 0x00, 0x28, 0x0C, 0x08, 0x00, 0x00, 0x28, 0x28, 0x28, 0x28,
+    0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x08, 0x08,
+    0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0C, 0x00, 0x08, 0x00, 0x00,
+    0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x0C, 0x00, 0x08, 0x00, 0x00,
+};
+
+bool IsLatin(uint16_t word) {
+  if (word <= 0x007F)
+    return !!(kSpecialChars[word] & 0x01);
+
+  return ((word >= 0x00C0 && word <= 0x00FF) ||
+          (word >= 0x0100 && word <= 0x024F) ||
+          (word >= 0x1E00 && word <= 0x1EFF) ||
+          (word >= 0x2C60 && word <= 0x2C7F) ||
+          (word >= 0xA720 && word <= 0xA7FF) ||
+          (word >= 0xFF21 && word <= 0xFF3A) ||
+          (word >= 0xFF41 && word <= 0xFF5A));
+}
+
+bool IsDigit(uint32_t word) {
+  return word >= 0x0030 && word <= 0x0039;
+}
+
+bool IsCJK(uint32_t word) {
+  if ((word >= 0x1100 && word <= 0x11FF) ||
+      (word >= 0x2E80 && word <= 0x2FFF) ||
+      (word >= 0x3040 && word <= 0x9FBF) ||
+      (word >= 0xAC00 && word <= 0xD7AF) ||
+      (word >= 0xF900 && word <= 0xFAFF) ||
+      (word >= 0xFE30 && word <= 0xFE4F) ||
+      (word >= 0x20000 && word <= 0x2A6DF) ||
+      (word >= 0x2F800 && word <= 0x2FA1F)) {
+    return true;
+  }
+  if (word >= 0x3000 && word <= 0x303F) {
+    return (
+        word == 0x3005 || word == 0x3006 || word == 0x3021 || word == 0x3022 ||
+        word == 0x3023 || word == 0x3024 || word == 0x3025 || word == 0x3026 ||
+        word == 0x3027 || word == 0x3028 || word == 0x3029 || word == 0x3031 ||
+        word == 0x3032 || word == 0x3033 || word == 0x3034 || word == 0x3035);
+  }
+  return word >= 0xFF66 && word <= 0xFF9D;
+}
+
+bool IsPunctuation(uint32_t word) {
+  if (word <= 0x007F)
+    return !!(kSpecialChars[word] & 0x08);
+
+  if (word >= 0x0080 && word <= 0x00FF) {
+    return (word == 0x0082 || word == 0x0084 || word == 0x0085 ||
+            word == 0x0091 || word == 0x0092 || word == 0x0093 ||
+            word <= 0x0094 || word == 0x0096 || word == 0x00B4 ||
+            word == 0x00B8);
+  }
+
+  if (word >= 0x2000 && word <= 0x206F) {
+    return (
+        word == 0x2010 || word == 0x2011 || word == 0x2012 || word == 0x2013 ||
+        word == 0x2018 || word == 0x2019 || word == 0x201A || word == 0x201B ||
+        word == 0x201C || word == 0x201D || word == 0x201E || word == 0x201F ||
+        word == 0x2032 || word == 0x2033 || word == 0x2034 || word == 0x2035 ||
+        word == 0x2036 || word == 0x2037 || word == 0x203C || word == 0x203D ||
+        word == 0x203E || word == 0x2044);
+  }
+
+  if (word >= 0x3000 && word <= 0x303F) {
+    return (
+        word == 0x3001 || word == 0x3002 || word == 0x3003 || word == 0x3005 ||
+        word == 0x3009 || word == 0x300A || word == 0x300B || word == 0x300C ||
+        word == 0x300D || word == 0x300F || word == 0x300E || word == 0x3010 ||
+        word == 0x3011 || word == 0x3014 || word == 0x3015 || word == 0x3016 ||
+        word == 0x3017 || word == 0x3018 || word == 0x3019 || word == 0x301A ||
+        word == 0x301B || word == 0x301D || word == 0x301E || word == 0x301F);
+  }
+
+  if (word >= 0xFE50 && word <= 0xFE6F)
+    return (word >= 0xFE50 && word <= 0xFE5E) || word == 0xFE63;
+
+  if (word >= 0xFF00 && word <= 0xFFEF) {
+    return (
+        word == 0xFF01 || word == 0xFF02 || word == 0xFF07 || word == 0xFF08 ||
+        word == 0xFF09 || word == 0xFF0C || word == 0xFF0E || word == 0xFF0F ||
+        word == 0xFF1A || word == 0xFF1B || word == 0xFF1F || word == 0xFF3B ||
+        word == 0xFF3D || word == 0xFF40 || word == 0xFF5B || word == 0xFF5C ||
+        word == 0xFF5D || word == 0xFF61 || word == 0xFF62 || word == 0xFF63 ||
+        word == 0xFF64 || word == 0xFF65 || word == 0xFF9E || word == 0xFF9F);
+  }
+
+  return false;
+}
+
+bool IsConnectiveSymbol(uint32_t word) {
+  return word <= 0x007F && (kSpecialChars[word] & 0x20);
+}
+
+bool IsOpenStylePunctuation(uint32_t word) {
+  if (word <= 0x007F)
+    return !!(kSpecialChars[word] & 0x04);
+
+  return (word == 0x300A || word == 0x300C || word == 0x300E ||
+          word == 0x3010 || word == 0x3014 || word == 0x3016 ||
+          word == 0x3018 || word == 0x301A || word == 0xFF08 ||
+          word == 0xFF3B || word == 0xFF5B || word == 0xFF62);
+}
+
+bool IsCurrencySymbol(uint16_t word) {
+  return (word == 0x0024 || word == 0x0080 || word == 0x00A2 ||
+          word == 0x00A3 || word == 0x00A4 || word == 0x00A5 ||
+          (word >= 0x20A0 && word <= 0x20CF) || word == 0xFE69 ||
+          word == 0xFF04 || word == 0xFFE0 || word == 0xFFE1 ||
+          word == 0xFFE5 || word == 0xFFE6);
+}
+
+bool IsPrefixSymbol(uint16_t word) {
+  return IsCurrencySymbol(word) || word == 0x2116;
+}
+
+bool IsSpace(uint16_t word) {
+  return word == 0x0020 || word == 0x3000;
+}
+
+bool NeedDivision(uint16_t prevWord, uint16_t curWord) {
+  if ((IsLatin(prevWord) || IsDigit(prevWord)) &&
+      (IsLatin(curWord) || IsDigit(curWord))) {
+    return false;
+  }
+  if (IsSpace(curWord) || IsPunctuation(curWord)) {
+    return false;
+  }
+  if (IsConnectiveSymbol(prevWord) || IsConnectiveSymbol(curWord)) {
+    return false;
+  }
+  if (IsSpace(prevWord) || IsPunctuation(prevWord)) {
+    return true;
+  }
+  if (IsPrefixSymbol(prevWord)) {
+    return false;
+  }
+  if (IsPrefixSymbol(curWord) || IsCJK(curWord)) {
+    return true;
+  }
+  if (IsCJK(prevWord)) {
+    return true;
+  }
+  return false;
+}
+
+}  // namespace
+
+CPVT_Section::Line::Line(const CPVT_LineInfo& lineinfo)
+    : m_LineInfo(lineinfo) {}
+
+CPVT_Section::Line::~Line() = default;
+
+CPVT_WordPlace CPVT_Section::Line::GetBeginWordPlace() const {
+  return CPVT_WordPlace(m_LinePlace.nSecIndex, m_LinePlace.nLineIndex, -1);
+}
+
+CPVT_WordPlace CPVT_Section::Line::GetEndWordPlace() const {
+  return CPVT_WordPlace(m_LinePlace.nSecIndex, m_LinePlace.nLineIndex,
+                        m_LineInfo.nEndWordIndex);
+}
+
+CPVT_WordPlace CPVT_Section::Line::GetPrevWordPlace(
+    const CPVT_WordPlace& place) const {
+  if (place.nWordIndex > m_LineInfo.nEndWordIndex) {
+    return CPVT_WordPlace(place.nSecIndex, place.nLineIndex,
+                          m_LineInfo.nEndWordIndex);
+  }
+  return CPVT_WordPlace(place.nSecIndex, place.nLineIndex,
+                        place.nWordIndex - 1);
+}
+
+CPVT_WordPlace CPVT_Section::Line::GetNextWordPlace(
+    const CPVT_WordPlace& place) const {
+  if (place.nWordIndex < m_LineInfo.nBeginWordIndex) {
+    return CPVT_WordPlace(place.nSecIndex, place.nLineIndex,
+                          m_LineInfo.nBeginWordIndex);
+  }
+  return CPVT_WordPlace(place.nSecIndex, place.nLineIndex,
+                        place.nWordIndex + 1);
+}
+
+CPVT_Section::CPVT_Section(CPVT_VariableText* pVT) : m_pVT(pVT) {
+  DCHECK(m_pVT);
+}
+
+CPVT_Section::~CPVT_Section() = default;
+
+void CPVT_Section::ResetLinePlace() {
+  int32_t i = 0;
+  for (auto& pLine : m_LineArray) {
+    pLine->m_LinePlace = CPVT_WordPlace(m_SecPlace.nSecIndex, i, -1);
+    ++i;
+  }
+}
+
+CPVT_WordPlace CPVT_Section::AddWord(const CPVT_WordPlace& place,
+                                     const CPVT_WordInfo& wordinfo) {
+  int32_t nWordIndex = 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;
+}
+
+CPVT_WordPlace CPVT_Section::AddLine(const CPVT_LineInfo& lineinfo) {
+  m_LineArray.push_back(std::make_unique<Line>(lineinfo));
+  return CPVT_WordPlace(m_SecPlace.nSecIndex,
+                        fxcrt::CollectionSize<int32_t>(m_LineArray) - 1, -1);
+}
+
+CPVT_FloatRect CPVT_Section::Rearrange() {
+  if (m_pVT->GetCharArray() > 0)
+    return RearrangeCharArray();
+  return RearrangeTypeset();
+}
+
+CFX_SizeF CPVT_Section::GetSectionSize(float fFontSize) {
+  CPVT_FloatRect result = SplitLines(/*bTypeset=*/false, fFontSize);
+  return CFX_SizeF(result.Width(), result.Height());
+}
+
+CPVT_WordPlace CPVT_Section::GetBeginWordPlace() const {
+  if (m_LineArray.empty())
+    return m_SecPlace;
+  return m_LineArray.front()->GetBeginWordPlace();
+}
+
+CPVT_WordPlace CPVT_Section::GetEndWordPlace() const {
+  if (m_LineArray.empty())
+    return m_SecPlace;
+  return m_LineArray.back()->GetEndWordPlace();
+}
+
+CPVT_WordPlace CPVT_Section::GetPrevWordPlace(
+    const CPVT_WordPlace& place) const {
+  if (place.nLineIndex < 0)
+    return GetBeginWordPlace();
+
+  if (place.nLineIndex >= fxcrt::CollectionSize<int32_t>(m_LineArray))
+    return GetEndWordPlace();
+
+  Line* pLine = m_LineArray[place.nLineIndex].get();
+  if (place.nWordIndex == pLine->m_LineInfo.nBeginWordIndex)
+    return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, -1);
+
+  if (place.nWordIndex >= pLine->m_LineInfo.nBeginWordIndex)
+    return pLine->GetPrevWordPlace(place);
+
+  if (!fxcrt::IndexInBounds(m_LineArray, place.nLineIndex - 1))
+    return place;
+
+  return m_LineArray[place.nLineIndex - 1]->GetEndWordPlace();
+}
+
+CPVT_WordPlace CPVT_Section::GetNextWordPlace(
+    const CPVT_WordPlace& place) const {
+  if (place.nLineIndex < 0)
+    return GetBeginWordPlace();
+
+  if (place.nLineIndex >= fxcrt::CollectionSize<int32_t>(m_LineArray))
+    return GetEndWordPlace();
+
+  Line* pLine = m_LineArray[place.nLineIndex].get();
+  if (place.nWordIndex < pLine->m_LineInfo.nEndWordIndex)
+    return pLine->GetNextWordPlace(place);
+
+  if (!fxcrt::IndexInBounds(m_LineArray, place.nLineIndex + 1))
+    return place;
+
+  return m_LineArray[place.nLineIndex + 1]->GetBeginWordPlace();
+}
+
+void CPVT_Section::UpdateWordPlace(CPVT_WordPlace& place) const {
+  int32_t nLeft = 0;
+  int32_t nRight = fxcrt::CollectionSize<int32_t>(m_LineArray) - 1;
+  int32_t nMid = (nLeft + nRight) / 2;
+  while (nLeft <= nRight) {
+    Line* pLine = m_LineArray[nMid].get();
+    if (place.nWordIndex < pLine->m_LineInfo.nBeginWordIndex) {
+      nRight = nMid - 1;
+      nMid = (nLeft + nRight) / 2;
+    } else if (place.nWordIndex > pLine->m_LineInfo.nEndWordIndex) {
+      nLeft = nMid + 1;
+      nMid = (nLeft + nRight) / 2;
+    } else {
+      place.nLineIndex = nMid;
+      return;
+    }
+  }
+}
+
+CPVT_WordPlace CPVT_Section::SearchWordPlace(const CFX_PointF& point) const {
+  CPVT_WordPlace place = GetBeginWordPlace();
+  bool bUp = true;
+  bool bDown = true;
+  int32_t nLeft = 0;
+  int32_t nRight = fxcrt::CollectionSize<int32_t>(m_LineArray) - 1;
+  int32_t nMid = fxcrt::CollectionSize<int32_t>(m_LineArray) / 2;
+  while (nLeft <= nRight) {
+    Line* pLine = m_LineArray[nMid].get();
+    float fTop = pLine->m_LineInfo.fLineY - pLine->m_LineInfo.fLineAscent -
+                 m_pVT->GetLineLeading();
+    float fBottom = pLine->m_LineInfo.fLineY - pLine->m_LineInfo.fLineDescent;
+    if (FXSYS_IsFloatBigger(point.y, fTop))
+      bUp = false;
+    if (FXSYS_IsFloatSmaller(point.y, fBottom))
+      bDown = false;
+    if (FXSYS_IsFloatSmaller(point.y, fTop)) {
+      nRight = nMid - 1;
+      nMid = (nLeft + nRight) / 2;
+      continue;
+    }
+    if (FXSYS_IsFloatBigger(point.y, fBottom)) {
+      nLeft = nMid + 1;
+      nMid = (nLeft + nRight) / 2;
+      continue;
+    }
+    place = SearchWordPlace(
+        point.x,
+        CPVT_WordRange(pLine->GetNextWordPlace(pLine->GetBeginWordPlace()),
+                       pLine->GetEndWordPlace()));
+    place.nLineIndex = nMid;
+    return place;
+  }
+  if (bUp)
+    place = GetBeginWordPlace();
+  if (bDown)
+    place = GetEndWordPlace();
+  return place;
+}
+
+CPVT_WordPlace CPVT_Section::SearchWordPlace(
+    float fx,
+    const CPVT_WordPlace& lineplace) const {
+  if (!fxcrt::IndexInBounds(m_LineArray, lineplace.nLineIndex))
+    return GetBeginWordPlace();
+
+  Line* pLine = m_LineArray[lineplace.nLineIndex].get();
+  return SearchWordPlace(
+      fx - m_Rect.left,
+      CPVT_WordRange(pLine->GetNextWordPlace(pLine->GetBeginWordPlace()),
+                     pLine->GetEndWordPlace()));
+}
+
+CPVT_WordPlace CPVT_Section::SearchWordPlace(
+    float fx,
+    const CPVT_WordRange& range) const {
+  CPVT_WordPlace wordplace = range.BeginPos;
+  wordplace.nWordIndex = -1;
+
+  int32_t nLeft = range.BeginPos.nWordIndex;
+  int32_t nRight = range.EndPos.nWordIndex + 1;
+  int32_t nMid = (nLeft + nRight) / 2;
+  while (nLeft < nRight) {
+    if (nMid == nLeft)
+      break;
+    if (nMid == nRight) {
+      nMid--;
+      break;
+    }
+    if (!fxcrt::IndexInBounds(m_WordArray, nMid))
+      break;
+    CPVT_WordInfo* pWord = m_WordArray[nMid].get();
+    if (fx > pWord->fWordX + m_pVT->GetWordWidth(*pWord) * 0.5f) {
+      nLeft = nMid;
+      nMid = (nLeft + nRight) / 2;
+      continue;
+    }
+    nRight = nMid;
+    nMid = (nLeft + nRight) / 2;
+  }
+  if (fxcrt::IndexInBounds(m_WordArray, nMid)) {
+    CPVT_WordInfo* pWord = m_WordArray[nMid].get();
+    if (fx > pWord->fWordX + m_pVT->GetWordWidth(*pWord) * 0.5f)
+      wordplace.nWordIndex = nMid;
+  }
+  return wordplace;
+}
+
+int32_t CPVT_Section::GetLineArraySize() const {
+  return fxcrt::CollectionSize<int32_t>(m_LineArray);
+}
+
+const CPVT_Section::Line* CPVT_Section::GetLineFromArray(int32_t index) const {
+  if (!fxcrt::IndexInBounds(m_LineArray, index))
+    return nullptr;
+
+  return m_LineArray[index].get();
+}
+
+int32_t CPVT_Section::GetWordArraySize() const {
+  return fxcrt::CollectionSize<int32_t>(m_WordArray);
+}
+
+const CPVT_WordInfo* CPVT_Section::GetWordFromArray(int32_t index) const {
+  if (!fxcrt::IndexInBounds(m_WordArray, index))
+    return nullptr;
+
+  return m_WordArray[index].get();
+}
+
+void CPVT_Section::EraseWordsFrom(int32_t index) {
+  if (!fxcrt::IndexInBounds(m_WordArray, index))
+    return;
+
+  m_WordArray.erase(m_WordArray.begin() + index, m_WordArray.end());
+}
+
+CPVT_FloatRect CPVT_Section::RearrangeCharArray() const {
+  if (m_LineArray.empty())
+    return CPVT_FloatRect();
+
+  float fNodeWidth = m_pVT->GetPlateWidth() /
+                     (m_pVT->GetCharArray() <= 0 ? 1 : m_pVT->GetCharArray());
+  float fLineAscent =
+      m_pVT->GetFontAscent(m_pVT->GetDefaultFontIndex(), m_pVT->GetFontSize());
+  float fLineDescent =
+      m_pVT->GetFontDescent(m_pVT->GetDefaultFontIndex(), m_pVT->GetFontSize());
+  float x = 0.0f;
+  float y = m_pVT->GetLineLeading() + fLineAscent;
+  int32_t nStart = 0;
+  CPVT_Section::Line* pLine = m_LineArray.front().get();
+  switch (m_pVT->GetAlignment()) {
+    case 0:
+      pLine->m_LineInfo.fLineX = fNodeWidth * 0.5f;
+      break;
+    case 1:
+      nStart = (m_pVT->GetCharArray() -
+                fxcrt::CollectionSize<int32_t>(m_WordArray)) /
+               2;
+      pLine->m_LineInfo.fLineX = fNodeWidth * nStart - fNodeWidth * 0.5f;
+      break;
+    case 2:
+      nStart =
+          m_pVT->GetCharArray() - fxcrt::CollectionSize<int32_t>(m_WordArray);
+      pLine->m_LineInfo.fLineX = fNodeWidth * nStart - fNodeWidth * 0.5f;
+      break;
+  }
+  for (int32_t w = 0, sz = fxcrt::CollectionSize<int32_t>(m_WordArray); w < sz;
+       w++) {
+    if (w >= m_pVT->GetCharArray())
+      break;
+
+    float fNextWidth = 0;
+    if (fxcrt::IndexInBounds(m_WordArray, w + 1)) {
+      CPVT_WordInfo* pNextWord = m_WordArray[w + 1].get();
+      pNextWord->fWordTail = 0;
+      fNextWidth = m_pVT->GetWordWidth(*pNextWord);
+    }
+    CPVT_WordInfo* pWord = m_WordArray[w].get();
+    pWord->fWordTail = 0;
+    float fWordWidth = m_pVT->GetWordWidth(*pWord);
+    float fWordAscent = m_pVT->GetWordAscent(*pWord);
+    float fWordDescent = m_pVT->GetWordDescent(*pWord);
+    x = (float)(fNodeWidth * (w + nStart + 0.5) - fWordWidth * 0.5f);
+    pWord->fWordX = x;
+    pWord->fWordY = y;
+    if (w == 0) {
+      pLine->m_LineInfo.fLineX = x;
+    }
+    if (w != fxcrt::CollectionSize<int32_t>(m_WordArray) - 1) {
+      pWord->fWordTail = (fNodeWidth - (fWordWidth + fNextWidth) * 0.5f > 0
+                              ? fNodeWidth - (fWordWidth + fNextWidth) * 0.5f
+                              : 0);
+    } else {
+      pWord->fWordTail = 0;
+    }
+    x += fWordWidth;
+    fLineAscent = std::max(fLineAscent, fWordAscent);
+    fLineDescent = std::min(fLineDescent, fWordDescent);
+  }
+  pLine->m_LineInfo.nBeginWordIndex = 0;
+  pLine->m_LineInfo.nEndWordIndex =
+      fxcrt::CollectionSize<int32_t>(m_WordArray) - 1;
+  pLine->m_LineInfo.fLineY = y;
+  pLine->m_LineInfo.fLineWidth = x - pLine->m_LineInfo.fLineX;
+  pLine->m_LineInfo.fLineAscent = fLineAscent;
+  pLine->m_LineInfo.fLineDescent = fLineDescent;
+  return CPVT_FloatRect(0, 0, x, y - fLineDescent);
+}
+
+CPVT_FloatRect CPVT_Section::RearrangeTypeset() {
+  m_LineArray.clear();
+  return OutputLines(SplitLines(/*bTypeset=*/true, /*fFontSize=*/0.0f));
+}
+
+CPVT_FloatRect CPVT_Section::SplitLines(bool bTypeset, float fFontSize) {
+  CPVT_LineInfo line;
+  if (m_WordArray.empty()) {
+    float fLineAscent;
+    float fLineDescent;
+    if (bTypeset) {
+      fLineAscent = m_pVT->GetLineAscent();
+      fLineDescent = m_pVT->GetLineDescent();
+      line.nBeginWordIndex = -1;
+      line.nEndWordIndex = -1;
+      line.nTotalWord = 0;
+      line.fLineWidth = 0;
+      line.fLineAscent = fLineAscent;
+      line.fLineDescent = fLineDescent;
+      AddLine(line);
+    } else {
+      fLineAscent =
+          m_pVT->GetFontAscent(m_pVT->GetDefaultFontIndex(), fFontSize);
+      fLineDescent =
+          m_pVT->GetFontDescent(m_pVT->GetDefaultFontIndex(), fFontSize);
+    }
+    float fMaxY = m_pVT->GetLineLeading() + fLineAscent - fLineDescent;
+    return CPVT_FloatRect(0, 0, 0, fMaxY);
+  }
+
+  int32_t nLineHead = 0;
+  int32_t nLineTail = 0;
+  float fMaxX = 0.0f;
+  float fMaxY = 0.0f;
+  float fLineWidth = 0.0f;
+  float fBackupLineWidth = 0.0f;
+  float fLineAscent = 0.0f;
+  float fBackupLineAscent = 0.0f;
+  float fLineDescent = 0.0f;
+  float fBackupLineDescent = 0.0f;
+  int32_t nWordStartPos = 0;
+  bool bFullWord = false;
+  int32_t nLineFullWordIndex = 0;
+  int32_t nCharIndex = 0;
+  float fWordWidth = 0;
+  float fTypesetWidth =
+      std::max(m_pVT->GetPlateWidth() - m_pVT->GetLineIndent(), 0.0f);
+  int32_t nTotalWords = fxcrt::CollectionSize<int32_t>(m_WordArray);
+  bool bOpened = false;
+  int32_t i = 0;
+  while (i < nTotalWords) {
+    CPVT_WordInfo* pWord = m_WordArray[i].get();
+    CPVT_WordInfo* pOldWord = pWord;
+    if (i > 0) {
+      pOldWord = m_WordArray[i - 1].get();
+    }
+    if (pWord) {
+      if (bTypeset) {
+        fLineAscent = std::max(fLineAscent, m_pVT->GetWordAscent(*pWord));
+        fLineDescent = std::min(fLineDescent, m_pVT->GetWordDescent(*pWord));
+        fWordWidth = m_pVT->GetWordWidth(*pWord);
+      } else {
+        fLineAscent =
+            std::max(fLineAscent, m_pVT->GetWordAscent(*pWord, fFontSize));
+        fLineDescent =
+            std::min(fLineDescent, m_pVT->GetWordDescent(*pWord, fFontSize));
+        fWordWidth = m_pVT->GetWordWidth(pWord->nFontIndex, pWord->Word,
+                                         m_pVT->GetSubWord(), fFontSize,
+                                         pWord->fWordTail);
+      }
+      if (!bOpened) {
+        if (IsOpenStylePunctuation(pWord->Word)) {
+          bOpened = true;
+          bFullWord = true;
+        } else if (pOldWord) {
+          if (NeedDivision(pOldWord->Word, pWord->Word)) {
+            bFullWord = true;
+          }
+        }
+      } else {
+        if (!IsSpace(pWord->Word) && !IsOpenStylePunctuation(pWord->Word)) {
+          bOpened = false;
+        }
+      }
+      if (bFullWord) {
+        bFullWord = false;
+        if (nCharIndex > 0) {
+          nLineFullWordIndex++;
+        }
+        nWordStartPos = i;
+        fBackupLineWidth = fLineWidth;
+        fBackupLineAscent = fLineAscent;
+        fBackupLineDescent = fLineDescent;
+      }
+      nCharIndex++;
+    }
+    if (m_pVT->IsAutoReturn() && fTypesetWidth > 0 &&
+        fLineWidth + fWordWidth > fTypesetWidth) {
+      if (nLineFullWordIndex > 0) {
+        i = nWordStartPos;
+        fLineWidth = fBackupLineWidth;
+        fLineAscent = fBackupLineAscent;
+        fLineDescent = fBackupLineDescent;
+      }
+      if (nCharIndex == 1) {
+        fLineWidth = fWordWidth;
+        i++;
+      }
+      nLineTail = i - 1;
+      if (bTypeset) {
+        line.nBeginWordIndex = nLineHead;
+        line.nEndWordIndex = nLineTail;
+        line.nTotalWord = nLineTail - nLineHead + 1;
+        line.fLineWidth = fLineWidth;
+        line.fLineAscent = fLineAscent;
+        line.fLineDescent = fLineDescent;
+        AddLine(line);
+      }
+      fMaxY += (fLineAscent + m_pVT->GetLineLeading());
+      fMaxY -= fLineDescent;
+      fMaxX = std::max(fLineWidth, fMaxX);
+      nLineHead = i;
+      fLineWidth = 0.0f;
+      fLineAscent = 0.0f;
+      fLineDescent = 0.0f;
+      nCharIndex = 0;
+      nLineFullWordIndex = 0;
+      bFullWord = false;
+    } else {
+      fLineWidth += fWordWidth;
+      i++;
+    }
+  }
+  if (nLineHead <= nTotalWords - 1) {
+    nLineTail = nTotalWords - 1;
+    if (bTypeset) {
+      line.nBeginWordIndex = nLineHead;
+      line.nEndWordIndex = nLineTail;
+      line.nTotalWord = nLineTail - nLineHead + 1;
+      line.fLineWidth = fLineWidth;
+      line.fLineAscent = fLineAscent;
+      line.fLineDescent = fLineDescent;
+      AddLine(line);
+    }
+    fMaxY += (fLineAscent + m_pVT->GetLineLeading());
+    fMaxY -= fLineDescent;
+    fMaxX = std::max(fLineWidth, fMaxX);
+  }
+  return CPVT_FloatRect(0, 0, fMaxX, fMaxY);
+}
+
+CPVT_FloatRect CPVT_Section::OutputLines(const CPVT_FloatRect& rect) const {
+  float fMinX;
+  float fLineIndent = m_pVT->GetLineIndent();
+  float fTypesetWidth = std::max(m_pVT->GetPlateWidth() - fLineIndent, 0.0f);
+  switch (m_pVT->GetAlignment()) {
+    default:
+    case 0:
+      fMinX = 0.0f;
+      break;
+    case 1:
+      fMinX = (fTypesetWidth - rect.Width()) * 0.5f;
+      break;
+    case 2:
+      fMinX = fTypesetWidth - rect.Width();
+      break;
+  }
+  float fMaxX = fMinX + rect.Width();
+  float fMinY = 0.0f;
+  float fMaxY = rect.Height();
+  int32_t nTotalLines = fxcrt::CollectionSize<int32_t>(m_LineArray);
+  if (nTotalLines > 0) {
+    float fPosX = 0.0f;
+    float fPosY = 0.0f;
+    for (int32_t l = 0; l < nTotalLines; l++) {
+      CPVT_Section::Line* pLine = m_LineArray[l].get();
+      switch (m_pVT->GetAlignment()) {
+        default:
+        case 0:
+          fPosX = 0;
+          break;
+        case 1:
+          fPosX = (fTypesetWidth - pLine->m_LineInfo.fLineWidth) * 0.5f;
+          break;
+        case 2:
+          fPosX = fTypesetWidth - pLine->m_LineInfo.fLineWidth;
+          break;
+      }
+      fPosX += fLineIndent;
+      fPosY += m_pVT->GetLineLeading();
+      fPosY += pLine->m_LineInfo.fLineAscent;
+      pLine->m_LineInfo.fLineX = fPosX - fMinX;
+      pLine->m_LineInfo.fLineY = fPosY - fMinY;
+      for (int32_t w = pLine->m_LineInfo.nBeginWordIndex;
+           w <= pLine->m_LineInfo.nEndWordIndex; w++) {
+        if (fxcrt::IndexInBounds(m_WordArray, w)) {
+          CPVT_WordInfo* pWord = m_WordArray[w].get();
+          pWord->fWordX = fPosX - fMinX;
+          pWord->fWordY = fPosY - fMinY;
+
+          fPosX += m_pVT->GetWordWidth(*pWord);
+        }
+      }
+      fPosY -= pLine->m_LineInfo.fLineDescent;
+    }
+  }
+  return CPVT_FloatRect(fMinX, fMinY, fMaxX, fMaxY);
+}
+
+void CPVT_Section::ClearLeftWords(int32_t nWordIndex) {
+  for (int32_t i = nWordIndex; i >= 0; i--) {
+    if (fxcrt::IndexInBounds(m_WordArray, i))
+      m_WordArray.erase(m_WordArray.begin() + i);
+  }
+}
+
+void CPVT_Section::ClearRightWords(int32_t nWordIndex) {
+  int32_t sz = fxcrt::CollectionSize<int32_t>(m_WordArray);
+  for (int32_t i = sz - 1; i > nWordIndex; i--) {
+    if (fxcrt::IndexInBounds(m_WordArray, i))
+      m_WordArray.erase(m_WordArray.begin() + i);
+  }
+}
+
+void CPVT_Section::ClearMidWords(int32_t nBeginIndex, int32_t nEndIndex) {
+  for (int32_t i = nEndIndex; i > nBeginIndex; i--) {
+    if (fxcrt::IndexInBounds(m_WordArray, i))
+      m_WordArray.erase(m_WordArray.begin() + i);
+  }
+}
+
+void CPVT_Section::ClearWords(const CPVT_WordRange& PlaceRange) {
+  CPVT_WordPlace SecBeginPos = GetBeginWordPlace();
+  CPVT_WordPlace SecEndPos = GetEndWordPlace();
+  if (PlaceRange.BeginPos >= SecBeginPos) {
+    if (PlaceRange.EndPos <= SecEndPos) {
+      ClearMidWords(PlaceRange.BeginPos.nWordIndex,
+                    PlaceRange.EndPos.nWordIndex);
+    } else {
+      ClearRightWords(PlaceRange.BeginPos.nWordIndex);
+    }
+  } else if (PlaceRange.EndPos <= SecEndPos) {
+    ClearLeftWords(PlaceRange.EndPos.nWordIndex);
+  } else {
+    m_WordArray.clear();
+  }
+}
+
+void CPVT_Section::ClearWord(const CPVT_WordPlace& place) {
+  if (fxcrt::IndexInBounds(m_WordArray, place.nWordIndex))
+    m_WordArray.erase(m_WordArray.begin() + place.nWordIndex);
+}
diff --git a/core/fpdfdoc/cpvt_section.h b/core/fpdfdoc/cpvt_section.h
new file mode 100644
index 0000000..0f95acb
--- /dev/null
+++ b/core/fpdfdoc/cpvt_section.h
@@ -0,0 +1,90 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFDOC_CPVT_SECTION_H_
+#define CORE_FPDFDOC_CPVT_SECTION_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "core/fpdfdoc/cpvt_floatrect.h"
+#include "core/fpdfdoc/cpvt_lineinfo.h"
+#include "core/fpdfdoc/cpvt_wordinfo.h"
+#include "core/fpdfdoc/cpvt_wordrange.h"
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/unowned_ptr.h"
+
+class CPVT_VariableText;
+struct CPVT_LineInfo;
+struct CPVT_WordPlace;
+
+class CPVT_Section final {
+ public:
+  class Line {
+   public:
+    explicit Line(const CPVT_LineInfo& lineinfo);
+    ~Line();
+
+    CPVT_WordPlace GetBeginWordPlace() const;
+    CPVT_WordPlace GetEndWordPlace() const;
+    CPVT_WordPlace GetPrevWordPlace(const CPVT_WordPlace& place) const;
+    CPVT_WordPlace GetNextWordPlace(const CPVT_WordPlace& place) const;
+    CPVT_WordPlace m_LinePlace;
+    CPVT_LineInfo m_LineInfo;
+  };
+
+  explicit CPVT_Section(CPVT_VariableText* pVT);
+  ~CPVT_Section();
+
+  void ResetLinePlace();
+  CPVT_WordPlace AddWord(const CPVT_WordPlace& place,
+                         const CPVT_WordInfo& wordinfo);
+  CPVT_WordPlace AddLine(const CPVT_LineInfo& lineinfo);
+  void ClearWords(const CPVT_WordRange& PlaceRange);
+  void ClearWord(const CPVT_WordPlace& place);
+  CPVT_FloatRect Rearrange();
+  CFX_SizeF GetSectionSize(float fFontSize);
+  CPVT_WordPlace GetBeginWordPlace() const;
+  CPVT_WordPlace GetEndWordPlace() const;
+  CPVT_WordPlace GetPrevWordPlace(const CPVT_WordPlace& place) const;
+  CPVT_WordPlace GetNextWordPlace(const CPVT_WordPlace& place) const;
+  void UpdateWordPlace(CPVT_WordPlace& place) const;
+  CPVT_WordPlace SearchWordPlace(const CFX_PointF& point) const;
+  CPVT_WordPlace SearchWordPlace(float fx,
+                                 const CPVT_WordPlace& lineplace) const;
+  CPVT_WordPlace SearchWordPlace(float fx, const CPVT_WordRange& range) const;
+
+  void SetPlace(const CPVT_WordPlace& place) { m_SecPlace = place; }
+  void SetPlaceIndex(int32_t index) { m_SecPlace.nSecIndex = index; }
+  const CPVT_FloatRect& GetRect() const { return m_Rect; }
+  void SetRect(const CPVT_FloatRect& rect) { m_Rect = rect; }
+
+  int32_t GetLineArraySize() const;
+  const Line* GetLineFromArray(int32_t index) const;
+  int32_t GetWordArraySize() const;
+  const CPVT_WordInfo* GetWordFromArray(int32_t index) const;
+  void EraseWordsFrom(int32_t index);
+
+ private:
+  CPVT_FloatRect RearrangeCharArray() const;
+  CPVT_FloatRect RearrangeTypeset();
+  CPVT_FloatRect SplitLines(bool bTypeset, float fFontSize);
+  CPVT_FloatRect OutputLines(const CPVT_FloatRect& rect) const;
+
+  void ClearLeftWords(int32_t nWordIndex);
+  void ClearRightWords(int32_t nWordIndex);
+  void ClearMidWords(int32_t nBeginIndex, int32_t nEndIndex);
+
+  CPVT_WordPlace m_SecPlace;
+  CPVT_FloatRect m_Rect;
+  std::vector<std::unique_ptr<Line>> m_LineArray;
+  std::vector<std::unique_ptr<CPVT_WordInfo>> m_WordArray;
+  UnownedPtr<CPVT_VariableText> const m_pVT;
+};
+
+#endif  // CORE_FPDFDOC_CPVT_SECTION_H_
diff --git a/core/fpdfdoc/cpvt_variabletext.cpp b/core/fpdfdoc/cpvt_variabletext.cpp
new file mode 100644
index 0000000..f9cab3e
--- /dev/null
+++ b/core/fpdfdoc/cpvt_variabletext.cpp
@@ -0,0 +1,893 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfdoc/cpvt_variabletext.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "core/fpdfapi/font/cpdf_font.h"
+#include "core/fpdfdoc/cpvt_section.h"
+#include "core/fpdfdoc/cpvt_word.h"
+#include "core/fpdfdoc/cpvt_wordinfo.h"
+#include "core/fpdfdoc/ipvt_fontmap.h"
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/check.h"
+
+namespace {
+
+constexpr float kFontScale = 0.001f;
+constexpr uint8_t kReturnLength = 1;
+
+constexpr uint8_t kFontSizeSteps[] = {4,  6,  8,   9,   10,  12,  14, 18, 20,
+                                      25, 30, 35,  40,  45,  50,  55, 60, 70,
+                                      80, 90, 100, 110, 120, 130, 144};
+
+}  // namespace
+
+CPVT_VariableText::Provider::Provider(IPVT_FontMap* pFontMap)
+    : m_pFontMap(pFontMap) {
+  DCHECK(m_pFontMap);
+}
+
+CPVT_VariableText::Provider::~Provider() = default;
+
+int CPVT_VariableText::Provider::GetCharWidth(int32_t nFontIndex,
+                                              uint16_t word) {
+  RetainPtr<CPDF_Font> pPDFFont = m_pFontMap->GetPDFFont(nFontIndex);
+  if (!pPDFFont)
+    return 0;
+
+  uint32_t charcode = pPDFFont->CharCodeFromUnicode(word);
+  if (charcode == CPDF_Font::kInvalidCharCode)
+    return 0;
+
+  return pPDFFont->GetCharWidthF(charcode);
+}
+
+int32_t CPVT_VariableText::Provider::GetTypeAscent(int32_t nFontIndex) {
+  RetainPtr<CPDF_Font> pPDFFont = m_pFontMap->GetPDFFont(nFontIndex);
+  return pPDFFont ? pPDFFont->GetTypeAscent() : 0;
+}
+
+int32_t CPVT_VariableText::Provider::GetTypeDescent(int32_t nFontIndex) {
+  RetainPtr<CPDF_Font> pPDFFont = m_pFontMap->GetPDFFont(nFontIndex);
+  return pPDFFont ? pPDFFont->GetTypeDescent() : 0;
+}
+
+int32_t CPVT_VariableText::Provider::GetWordFontIndex(uint16_t word,
+                                                      FX_Charset charset,
+                                                      int32_t nFontIndex) {
+  if (RetainPtr<CPDF_Font> pDefFont = m_pFontMap->GetPDFFont(0)) {
+    if (pDefFont->CharCodeFromUnicode(word) != CPDF_Font::kInvalidCharCode)
+      return 0;
+  }
+  if (RetainPtr<CPDF_Font> pSysFont = m_pFontMap->GetPDFFont(1)) {
+    if (pSysFont->CharCodeFromUnicode(word) != CPDF_Font::kInvalidCharCode)
+      return 1;
+  }
+  return -1;
+}
+
+int32_t CPVT_VariableText::Provider::GetDefaultFontIndex() {
+  return 0;
+}
+
+CPVT_VariableText::Iterator::Iterator(CPVT_VariableText* pVT) : m_pVT(pVT) {
+  DCHECK(m_pVT);
+}
+
+CPVT_VariableText::Iterator::~Iterator() = default;
+
+void CPVT_VariableText::Iterator::SetAt(int32_t nWordIndex) {
+  m_CurPos = m_pVT->WordIndexToWordPlace(nWordIndex);
+}
+
+void CPVT_VariableText::Iterator::SetAt(const CPVT_WordPlace& place) {
+  m_CurPos = place;
+}
+
+bool CPVT_VariableText::Iterator::NextWord() {
+  if (m_CurPos == m_pVT->GetEndWordPlace())
+    return false;
+
+  m_CurPos = m_pVT->GetNextWordPlace(m_CurPos);
+  return true;
+}
+
+bool CPVT_VariableText::Iterator::NextLine() {
+  if (!fxcrt::IndexInBounds(m_pVT->m_SectionArray, m_CurPos.nSecIndex))
+    return false;
+
+  CPVT_Section* pSection = m_pVT->m_SectionArray[m_CurPos.nSecIndex].get();
+  if (m_CurPos.nLineIndex < pSection->GetLineArraySize() - 1) {
+    m_CurPos = CPVT_WordPlace(m_CurPos.nSecIndex, m_CurPos.nLineIndex + 1, -1);
+    return true;
+  }
+  if (m_CurPos.nSecIndex <
+      fxcrt::CollectionSize<int32_t>(m_pVT->m_SectionArray) - 1) {
+    m_CurPos = CPVT_WordPlace(m_CurPos.nSecIndex + 1, 0, -1);
+    return true;
+  }
+  return false;
+}
+
+bool CPVT_VariableText::Iterator::GetWord(CPVT_Word& word) const {
+  word.WordPlace = m_CurPos;
+  if (!fxcrt::IndexInBounds(m_pVT->m_SectionArray, m_CurPos.nSecIndex))
+    return false;
+
+  CPVT_Section* pSection = m_pVT->m_SectionArray[m_CurPos.nSecIndex].get();
+  if (!pSection->GetLineFromArray(m_CurPos.nLineIndex))
+    return false;
+
+  const CPVT_WordInfo* pInfo = pSection->GetWordFromArray(m_CurPos.nWordIndex);
+  if (!pInfo)
+    return false;
+
+  word.Word = pInfo->Word;
+  word.nCharset = pInfo->nCharset;
+  word.fWidth = m_pVT->GetWordWidth(*pInfo);
+  word.ptWord =
+      m_pVT->InToOut(CFX_PointF(pInfo->fWordX + pSection->GetRect().left,
+                                pInfo->fWordY + pSection->GetRect().top));
+  word.fAscent = m_pVT->GetWordAscent(*pInfo);
+  word.fDescent = m_pVT->GetWordDescent(*pInfo);
+  word.nFontIndex = pInfo->nFontIndex;
+  word.fFontSize = m_pVT->GetWordFontSize();
+  return true;
+}
+
+bool CPVT_VariableText::Iterator::GetLine(CPVT_Line& line) const {
+  DCHECK(m_pVT);
+  line.lineplace = CPVT_WordPlace(m_CurPos.nSecIndex, m_CurPos.nLineIndex, -1);
+  if (!fxcrt::IndexInBounds(m_pVT->m_SectionArray, m_CurPos.nSecIndex))
+    return false;
+
+  CPVT_Section* pSection = m_pVT->m_SectionArray[m_CurPos.nSecIndex].get();
+  const CPVT_Section::Line* pLine =
+      pSection->GetLineFromArray(m_CurPos.nLineIndex);
+  if (!pLine)
+    return false;
+
+  line.ptLine = m_pVT->InToOut(
+      CFX_PointF(pLine->m_LineInfo.fLineX + pSection->GetRect().left,
+                 pLine->m_LineInfo.fLineY + pSection->GetRect().top));
+  line.fLineWidth = pLine->m_LineInfo.fLineWidth;
+  line.fLineAscent = pLine->m_LineInfo.fLineAscent;
+  line.fLineDescent = pLine->m_LineInfo.fLineDescent;
+  line.lineEnd = pLine->GetEndWordPlace();
+  return true;
+}
+
+CPVT_VariableText::CPVT_VariableText(Provider* pProvider)
+    : m_pVTProvider(pProvider) {}
+
+CPVT_VariableText::~CPVT_VariableText() = default;
+
+void CPVT_VariableText::Initialize() {
+  if (m_bInitialized)
+    return;
+
+  CPVT_WordPlace place;
+  place.nSecIndex = 0;
+  AddSection(place);
+
+  CPVT_LineInfo lineinfo;
+  lineinfo.fLineAscent = GetFontAscent(GetDefaultFontIndex(), GetFontSize());
+  lineinfo.fLineDescent = GetFontDescent(GetDefaultFontIndex(), GetFontSize());
+  AddLine(place, lineinfo);
+
+  if (!m_SectionArray.empty())
+    m_SectionArray.front()->ResetLinePlace();
+
+  m_bInitialized = true;
+}
+
+CPVT_WordPlace CPVT_VariableText::InsertWord(const CPVT_WordPlace& place,
+                                             uint16_t word,
+                                             FX_Charset charset) {
+  int32_t nTotalWords = GetTotalWords();
+  if (m_nLimitChar > 0 && nTotalWords >= m_nLimitChar)
+    return place;
+  if (m_nCharArray > 0 && nTotalWords >= m_nCharArray)
+    return place;
+
+  CPVT_WordPlace newplace = place;
+  newplace.nWordIndex++;
+  int32_t nFontIndex =
+      GetSubWord() > 0 ? GetDefaultFontIndex()
+                       : GetWordFontIndex(word, charset, GetDefaultFontIndex());
+  return AddWord(newplace, CPVT_WordInfo(word, charset, nFontIndex));
+}
+
+CPVT_WordPlace CPVT_VariableText::InsertSection(const CPVT_WordPlace& place) {
+  int32_t nTotalWords = GetTotalWords();
+  if (m_nLimitChar > 0 && nTotalWords >= m_nLimitChar)
+    return place;
+  if (m_nCharArray > 0 && nTotalWords >= m_nCharArray)
+    return place;
+  if (!m_bMultiLine)
+    return place;
+
+  CPVT_WordPlace wordplace = place;
+  UpdateWordPlace(wordplace);
+  if (!fxcrt::IndexInBounds(m_SectionArray, wordplace.nSecIndex))
+    return place;
+
+  CPVT_Section* pSection = m_SectionArray[wordplace.nSecIndex].get();
+  CPVT_WordPlace NewPlace(wordplace.nSecIndex + 1, 0, -1);
+  AddSection(NewPlace);
+  CPVT_WordPlace result = NewPlace;
+  if (fxcrt::IndexInBounds(m_SectionArray, NewPlace.nSecIndex)) {
+    CPVT_Section* pNewSection = m_SectionArray[NewPlace.nSecIndex].get();
+    for (int32_t w = wordplace.nWordIndex + 1; w < pSection->GetWordArraySize();
+         ++w) {
+      NewPlace.nWordIndex++;
+      pNewSection->AddWord(NewPlace, *pSection->GetWordFromArray(w));
+    }
+  }
+  ClearSectionRightWords(wordplace);
+  return result;
+}
+
+CPVT_WordPlace CPVT_VariableText::DeleteWords(
+    const CPVT_WordRange& PlaceRange) {
+  bool bLastSecPos =
+      fxcrt::IndexInBounds(m_SectionArray, PlaceRange.EndPos.nSecIndex) &&
+      PlaceRange.EndPos ==
+          m_SectionArray[PlaceRange.EndPos.nSecIndex]->GetEndWordPlace();
+
+  ClearWords(PlaceRange);
+  if (PlaceRange.BeginPos.nSecIndex != PlaceRange.EndPos.nSecIndex) {
+    ClearEmptySections(PlaceRange);
+    if (!bLastSecPos)
+      LinkLatterSection(PlaceRange.BeginPos);
+  }
+  return PlaceRange.BeginPos;
+}
+
+CPVT_WordPlace CPVT_VariableText::DeleteWord(const CPVT_WordPlace& place) {
+  return ClearRightWord(PrevLineHeaderPlace(place));
+}
+
+CPVT_WordPlace CPVT_VariableText::BackSpaceWord(const CPVT_WordPlace& place) {
+  return ClearLeftWord(PrevLineHeaderPlace(place));
+}
+
+void CPVT_VariableText::SetText(const WideString& swText) {
+  DeleteWords(CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace()));
+  CPVT_WordPlace wp(0, 0, -1);
+  if (!m_SectionArray.empty())
+    m_SectionArray.front()->SetRect(CPVT_FloatRect());
+
+  FX_SAFE_INT32 nCharCount = 0;
+  for (size_t i = 0, sz = swText.GetLength(); i < sz; i++) {
+    if (m_nLimitChar > 0 && nCharCount.ValueOrDie() >= m_nLimitChar)
+      break;
+    if (m_nCharArray > 0 && nCharCount.ValueOrDie() >= m_nCharArray)
+      break;
+
+    uint16_t word = swText[i];
+    switch (word) {
+      case 0x0D:
+        if (m_bMultiLine) {
+          if (i + 1 < sz && swText[i + 1] == 0x0A)
+            i++;
+          wp.AdvanceSection();
+          AddSection(wp);
+        }
+        break;
+      case 0x0A:
+        if (m_bMultiLine) {
+          if (i + 1 < sz && swText[i + 1] == 0x0D)
+            i++;
+          wp.AdvanceSection();
+          AddSection(wp);
+        }
+        break;
+      case 0x09:
+        word = 0x20;
+        [[fallthrough]];
+      default:
+        wp = InsertWord(wp, word, FX_Charset::kDefault);
+        break;
+    }
+    nCharCount++;
+  }
+}
+
+void CPVT_VariableText::UpdateWordPlace(CPVT_WordPlace& place) const {
+  if (place.nSecIndex < 0)
+    place = GetBeginWordPlace();
+  if (static_cast<size_t>(place.nSecIndex) >= m_SectionArray.size())
+    place = GetEndWordPlace();
+
+  place = PrevLineHeaderPlace(place);
+  if (fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex))
+    m_SectionArray[place.nSecIndex]->UpdateWordPlace(place);
+}
+
+int32_t CPVT_VariableText::WordPlaceToWordIndex(
+    const CPVT_WordPlace& place) const {
+  CPVT_WordPlace newplace = place;
+  UpdateWordPlace(newplace);
+  int32_t nIndex = 0;
+  int32_t i = 0;
+  int32_t sz = 0;
+  for (i = 0, sz = fxcrt::CollectionSize<int32_t>(m_SectionArray);
+       i < sz && i < newplace.nSecIndex; i++) {
+    CPVT_Section* pSection = m_SectionArray[i].get();
+    nIndex += pSection->GetWordArraySize();
+    if (i != sz - 1)
+      nIndex += kReturnLength;
+  }
+  if (fxcrt::IndexInBounds(m_SectionArray, i))
+    nIndex += newplace.nWordIndex + kReturnLength;
+  return nIndex;
+}
+
+CPVT_WordPlace CPVT_VariableText::WordIndexToWordPlace(int32_t index) const {
+  CPVT_WordPlace place = GetBeginWordPlace();
+  int32_t nOldIndex = 0;
+  int32_t nIndex = 0;
+  bool bFound = false;
+  for (size_t i = 0; i < m_SectionArray.size(); ++i) {
+    CPVT_Section* pSection = m_SectionArray[i].get();
+    nIndex += pSection->GetWordArraySize();
+    if (nIndex == index) {
+      place = pSection->GetEndWordPlace();
+      bFound = true;
+      break;
+    }
+    if (nIndex > index) {
+      place.nSecIndex = pdfium::base::checked_cast<int32_t>(i);
+      place.nWordIndex = index - nOldIndex - 1;
+      pSection->UpdateWordPlace(place);
+      bFound = true;
+      break;
+    }
+    if (i != m_SectionArray.size() - 1)
+      nIndex += kReturnLength;
+    nOldIndex = nIndex;
+  }
+  if (!bFound)
+    place = GetEndWordPlace();
+  return place;
+}
+
+CPVT_WordPlace CPVT_VariableText::GetBeginWordPlace() const {
+  return m_bInitialized ? CPVT_WordPlace(0, 0, -1) : CPVT_WordPlace();
+}
+
+CPVT_WordPlace CPVT_VariableText::GetEndWordPlace() const {
+  if (m_SectionArray.empty())
+    return CPVT_WordPlace();
+  return m_SectionArray.back()->GetEndWordPlace();
+}
+
+CPVT_WordPlace CPVT_VariableText::GetPrevWordPlace(
+    const CPVT_WordPlace& place) const {
+  if (place.nSecIndex < 0)
+    return GetBeginWordPlace();
+  if (static_cast<size_t>(place.nSecIndex) >= m_SectionArray.size())
+    return GetEndWordPlace();
+
+  CPVT_Section* pSection = m_SectionArray[place.nSecIndex].get();
+  if (place > pSection->GetBeginWordPlace())
+    return pSection->GetPrevWordPlace(place);
+  if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex - 1))
+    return GetBeginWordPlace();
+  return m_SectionArray[place.nSecIndex - 1]->GetEndWordPlace();
+}
+
+CPVT_WordPlace CPVT_VariableText::GetNextWordPlace(
+    const CPVT_WordPlace& place) const {
+  if (place.nSecIndex < 0)
+    return GetBeginWordPlace();
+  if (static_cast<size_t>(place.nSecIndex) >= m_SectionArray.size())
+    return GetEndWordPlace();
+
+  CPVT_Section* pSection = m_SectionArray[place.nSecIndex].get();
+  if (place < pSection->GetEndWordPlace())
+    return pSection->GetNextWordPlace(place);
+  if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex + 1))
+    return GetEndWordPlace();
+  return m_SectionArray[place.nSecIndex + 1]->GetBeginWordPlace();
+}
+
+CPVT_WordPlace CPVT_VariableText::SearchWordPlace(
+    const CFX_PointF& point) const {
+  CFX_PointF pt = OutToIn(point);
+  CPVT_WordPlace place = GetBeginWordPlace();
+  int32_t nLeft = 0;
+  int32_t nRight = fxcrt::CollectionSize<int32_t>(m_SectionArray) - 1;
+  int32_t nMid = fxcrt::CollectionSize<int32_t>(m_SectionArray) / 2;
+  bool bUp = true;
+  bool bDown = true;
+  while (nLeft <= nRight) {
+    if (!fxcrt::IndexInBounds(m_SectionArray, nMid))
+      break;
+    CPVT_Section* pSection = m_SectionArray[nMid].get();
+    if (FXSYS_IsFloatBigger(pt.y, pSection->GetRect().top))
+      bUp = false;
+    if (FXSYS_IsFloatBigger(pSection->GetRect().bottom, pt.y))
+      bDown = false;
+    if (FXSYS_IsFloatSmaller(pt.y, pSection->GetRect().top)) {
+      nRight = nMid - 1;
+      nMid = (nLeft + nRight) / 2;
+      continue;
+    }
+    if (FXSYS_IsFloatBigger(pt.y, pSection->GetRect().bottom)) {
+      nLeft = nMid + 1;
+      nMid = (nLeft + nRight) / 2;
+      continue;
+    }
+    place = pSection->SearchWordPlace(CFX_PointF(
+        pt.x - pSection->GetRect().left, pt.y - pSection->GetRect().top));
+    place.nSecIndex = nMid;
+    return place;
+  }
+  if (bUp)
+    place = GetBeginWordPlace();
+  if (bDown)
+    place = GetEndWordPlace();
+  return place;
+}
+
+CPVT_WordPlace CPVT_VariableText::GetUpWordPlace(
+    const CPVT_WordPlace& place,
+    const CFX_PointF& point) const {
+  if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex))
+    return place;
+
+  CPVT_Section* pSection = m_SectionArray[place.nSecIndex].get();
+  CPVT_WordPlace temp = place;
+  CFX_PointF pt = OutToIn(point);
+  if (temp.nLineIndex-- > 0) {
+    return pSection->SearchWordPlace(pt.x - pSection->GetRect().left, temp);
+  }
+  if (temp.nSecIndex-- > 0) {
+    if (fxcrt::IndexInBounds(m_SectionArray, temp.nSecIndex)) {
+      CPVT_Section* pLastSection = m_SectionArray[temp.nSecIndex].get();
+      temp.nLineIndex = pLastSection->GetLineArraySize() - 1;
+      return pLastSection->SearchWordPlace(pt.x - pLastSection->GetRect().left,
+                                           temp);
+    }
+  }
+  return place;
+}
+
+CPVT_WordPlace CPVT_VariableText::GetDownWordPlace(
+    const CPVT_WordPlace& place,
+    const CFX_PointF& point) const {
+  if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex))
+    return place;
+
+  CPVT_Section* pSection = m_SectionArray[place.nSecIndex].get();
+  CPVT_WordPlace temp = place;
+  CFX_PointF pt = OutToIn(point);
+  if (temp.nLineIndex++ < pSection->GetLineArraySize() - 1) {
+    return pSection->SearchWordPlace(pt.x - pSection->GetRect().left, temp);
+  }
+  temp.AdvanceSection();
+  if (!fxcrt::IndexInBounds(m_SectionArray, temp.nSecIndex))
+    return place;
+
+  return m_SectionArray[temp.nSecIndex]->SearchWordPlace(
+      pt.x - pSection->GetRect().left, temp);
+}
+
+CPVT_WordPlace CPVT_VariableText::GetLineBeginPlace(
+    const CPVT_WordPlace& place) const {
+  return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, -1);
+}
+
+CPVT_WordPlace CPVT_VariableText::GetLineEndPlace(
+    const CPVT_WordPlace& place) const {
+  if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex))
+    return place;
+
+  CPVT_Section* pSection = m_SectionArray[place.nSecIndex].get();
+  const CPVT_Section::Line* pLine =
+      pSection->GetLineFromArray(place.nLineIndex);
+  if (!pLine)
+    return place;
+
+  return pLine->GetEndWordPlace();
+}
+
+CPVT_WordPlace CPVT_VariableText::GetSectionBeginPlace(
+    const CPVT_WordPlace& place) const {
+  return CPVT_WordPlace(place.nSecIndex, 0, -1);
+}
+
+CPVT_WordPlace CPVT_VariableText::GetSectionEndPlace(
+    const CPVT_WordPlace& place) const {
+  if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex))
+    return place;
+
+  return m_SectionArray[place.nSecIndex]->GetEndWordPlace();
+}
+
+int32_t CPVT_VariableText::GetTotalWords() const {
+  int32_t nTotal = 0;
+  for (const auto& pSection : m_SectionArray) {
+    nTotal += pSection->GetWordArraySize() + kReturnLength;
+  }
+  return nTotal - kReturnLength;
+}
+
+CPVT_WordPlace CPVT_VariableText::AddSection(const CPVT_WordPlace& place) {
+  if (IsValid() && !m_bMultiLine)
+    return place;
+
+  int32_t nSecIndex = std::clamp(
+      place.nSecIndex, 0, fxcrt::CollectionSize<int32_t>(m_SectionArray));
+
+  auto pSection = std::make_unique<CPVT_Section>(this);
+  pSection->SetRect(CPVT_FloatRect());
+  pSection->SetPlaceIndex(nSecIndex);
+  m_SectionArray.insert(m_SectionArray.begin() + nSecIndex,
+                        std::move(pSection));
+  return place;
+}
+
+CPVT_WordPlace CPVT_VariableText::AddLine(const CPVT_WordPlace& place,
+                                          const CPVT_LineInfo& lineinfo) {
+  if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex))
+    return place;
+
+  return m_SectionArray[place.nSecIndex]->AddLine(lineinfo);
+}
+
+CPVT_WordPlace CPVT_VariableText::AddWord(const CPVT_WordPlace& place,
+                                          const CPVT_WordInfo& wordinfo) {
+  if (m_SectionArray.empty())
+    return place;
+
+  CPVT_WordPlace newplace = place;
+  newplace.nSecIndex =
+      std::clamp(newplace.nSecIndex, 0,
+                 fxcrt::CollectionSize<int32_t>(m_SectionArray) - 1);
+  return m_SectionArray[newplace.nSecIndex]->AddWord(newplace, wordinfo);
+}
+
+void CPVT_VariableText::SetPlateRect(const CFX_FloatRect& rect) {
+  m_rcPlate = rect;
+}
+
+CFX_FloatRect CPVT_VariableText::GetContentRect() const {
+  return InToOut(m_rcContent);
+}
+
+const CFX_FloatRect& CPVT_VariableText::GetPlateRect() const {
+  return m_rcPlate;
+}
+
+float CPVT_VariableText::GetWordFontSize() const {
+  return GetFontSize();
+}
+
+float CPVT_VariableText::GetWordWidth(int32_t nFontIndex,
+                                      uint16_t Word,
+                                      uint16_t SubWord,
+                                      float fFontSize,
+                                      float fWordTail) const {
+  return GetCharWidth(nFontIndex, Word, SubWord) * fFontSize * kFontScale +
+         fWordTail;
+}
+
+float CPVT_VariableText::GetWordWidth(const CPVT_WordInfo& WordInfo) const {
+  return GetWordWidth(WordInfo.nFontIndex, WordInfo.Word, GetSubWord(),
+                      GetWordFontSize(), WordInfo.fWordTail);
+}
+
+float CPVT_VariableText::GetLineAscent() {
+  return GetFontAscent(GetDefaultFontIndex(), GetFontSize());
+}
+
+float CPVT_VariableText::GetLineDescent() {
+  return GetFontDescent(GetDefaultFontIndex(), GetFontSize());
+}
+
+float CPVT_VariableText::GetFontAscent(int32_t nFontIndex,
+                                       float fFontSize) const {
+  float ascent = m_pVTProvider ? m_pVTProvider->GetTypeAscent(nFontIndex) : 0;
+  return ascent * fFontSize * kFontScale;
+}
+
+float CPVT_VariableText::GetFontDescent(int32_t nFontIndex,
+                                        float fFontSize) const {
+  float descent = m_pVTProvider ? m_pVTProvider->GetTypeDescent(nFontIndex) : 0;
+  return descent * fFontSize * kFontScale;
+}
+
+float CPVT_VariableText::GetWordAscent(const CPVT_WordInfo& WordInfo,
+                                       float fFontSize) const {
+  return GetFontAscent(WordInfo.nFontIndex, fFontSize);
+}
+
+float CPVT_VariableText::GetWordDescent(const CPVT_WordInfo& WordInfo,
+                                        float fFontSize) const {
+  return GetFontDescent(WordInfo.nFontIndex, fFontSize);
+}
+
+float CPVT_VariableText::GetWordAscent(const CPVT_WordInfo& WordInfo) const {
+  return GetFontAscent(WordInfo.nFontIndex, GetWordFontSize());
+}
+
+float CPVT_VariableText::GetWordDescent(const CPVT_WordInfo& WordInfo) const {
+  return GetFontDescent(WordInfo.nFontIndex, GetWordFontSize());
+}
+
+float CPVT_VariableText::GetLineLeading() {
+  return m_fLineLeading;
+}
+
+float CPVT_VariableText::GetLineIndent() {
+  return 0.0f;
+}
+
+void CPVT_VariableText::ClearSectionRightWords(const CPVT_WordPlace& place) {
+  CPVT_WordPlace wordplace = PrevLineHeaderPlace(place);
+  if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex))
+    return;
+
+  CPVT_Section* pSection = m_SectionArray[place.nSecIndex].get();
+  pSection->EraseWordsFrom(wordplace.nWordIndex + 1);
+}
+
+CPVT_WordPlace CPVT_VariableText::PrevLineHeaderPlace(
+    const CPVT_WordPlace& place) const {
+  if (place.nWordIndex < 0 && place.nLineIndex > 0)
+    return GetPrevWordPlace(place);
+  return place;
+}
+
+CPVT_WordPlace CPVT_VariableText::NextLineHeaderPlace(
+    const CPVT_WordPlace& place) const {
+  if (place.nWordIndex < 0 && place.nLineIndex > 0)
+    return GetNextWordPlace(place);
+  return place;
+}
+
+bool CPVT_VariableText::ClearEmptySection(const CPVT_WordPlace& place) {
+  if (place.nSecIndex == 0 && m_SectionArray.size() == 1)
+    return false;
+
+  if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex))
+    return false;
+
+  if (m_SectionArray[place.nSecIndex]->GetWordArraySize() != 0)
+    return false;
+
+  m_SectionArray.erase(m_SectionArray.begin() + place.nSecIndex);
+  return true;
+}
+
+void CPVT_VariableText::ClearEmptySections(const CPVT_WordRange& PlaceRange) {
+  CPVT_WordPlace wordplace;
+  for (int32_t s = PlaceRange.EndPos.nSecIndex;
+       s > PlaceRange.BeginPos.nSecIndex; s--) {
+    wordplace.nSecIndex = s;
+    ClearEmptySection(wordplace);
+  }
+}
+
+void CPVT_VariableText::LinkLatterSection(const CPVT_WordPlace& place) {
+  CPVT_WordPlace oldplace = PrevLineHeaderPlace(place);
+  if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex + 1))
+    return;
+
+  CPVT_Section* pNextSection = m_SectionArray[place.nSecIndex + 1].get();
+  if (fxcrt::IndexInBounds(m_SectionArray, oldplace.nSecIndex)) {
+    CPVT_Section* pSection = m_SectionArray[oldplace.nSecIndex].get();
+    for (int32_t i = 0; i < pNextSection->GetWordArraySize(); ++i) {
+      oldplace.nWordIndex++;
+      pSection->AddWord(oldplace, *pNextSection->GetWordFromArray(i));
+    }
+  }
+  m_SectionArray.erase(m_SectionArray.begin() + place.nSecIndex + 1);
+}
+
+void CPVT_VariableText::ClearWords(const CPVT_WordRange& PlaceRange) {
+  CPVT_WordRange NewRange;
+  NewRange.BeginPos = PrevLineHeaderPlace(PlaceRange.BeginPos);
+  NewRange.EndPos = PrevLineHeaderPlace(PlaceRange.EndPos);
+  for (int32_t s = NewRange.EndPos.nSecIndex; s >= NewRange.BeginPos.nSecIndex;
+       s--) {
+    if (fxcrt::IndexInBounds(m_SectionArray, s))
+      m_SectionArray[s]->ClearWords(NewRange);
+  }
+}
+
+CPVT_WordPlace CPVT_VariableText::ClearLeftWord(const CPVT_WordPlace& place) {
+  if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex))
+    return place;
+
+  CPVT_Section* pSection = m_SectionArray[place.nSecIndex].get();
+  CPVT_WordPlace leftplace = GetPrevWordPlace(place);
+  if (leftplace == place)
+    return place;
+
+  if (leftplace.nSecIndex != place.nSecIndex) {
+    if (pSection->GetWordArraySize() == 0)
+      ClearEmptySection(place);
+    else
+      LinkLatterSection(leftplace);
+  } else {
+    pSection->ClearWord(place);
+  }
+  return leftplace;
+}
+
+CPVT_WordPlace CPVT_VariableText::ClearRightWord(const CPVT_WordPlace& place) {
+  if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex))
+    return place;
+
+  CPVT_Section* pSection = m_SectionArray[place.nSecIndex].get();
+  CPVT_WordPlace rightplace = NextLineHeaderPlace(GetNextWordPlace(place));
+  if (rightplace == place)
+    return place;
+
+  if (rightplace.nSecIndex != place.nSecIndex)
+    LinkLatterSection(place);
+  else
+    pSection->ClearWord(rightplace);
+  return place;
+}
+
+void CPVT_VariableText::RearrangeAll() {
+  Rearrange(CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace()));
+}
+
+void CPVT_VariableText::RearrangePart(const CPVT_WordRange& PlaceRange) {
+  Rearrange(PlaceRange);
+}
+
+void CPVT_VariableText::Rearrange(const CPVT_WordRange& PlaceRange) {
+  CPVT_FloatRect rcRet;
+  if (IsValid()) {
+    if (m_bAutoFontSize) {
+      SetFontSize(GetAutoFontSize());
+      rcRet = RearrangeSections(
+          CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace()));
+    } else {
+      rcRet = RearrangeSections(PlaceRange);
+    }
+  }
+  m_rcContent = rcRet;
+}
+
+float CPVT_VariableText::GetAutoFontSize() {
+  int32_t nTotal = sizeof(kFontSizeSteps) / sizeof(uint8_t);
+  if (IsMultiLine())
+    nTotal /= 4;
+  if (nTotal <= 0)
+    return 0;
+  if (GetPlateWidth() <= 0)
+    return 0;
+
+  int32_t nLeft = 0;
+  int32_t nRight = nTotal - 1;
+  int32_t nMid = nTotal / 2;
+  while (nLeft <= nRight) {
+    if (IsBigger(kFontSizeSteps[nMid]))
+      nRight = nMid - 1;
+    else
+      nLeft = nMid + 1;
+    nMid = (nLeft + nRight) / 2;
+  }
+  return (float)kFontSizeSteps[nMid];
+}
+
+bool CPVT_VariableText::IsBigger(float fFontSize) const {
+  CFX_SizeF szTotal;
+  for (const auto& pSection : m_SectionArray) {
+    CFX_SizeF size = pSection->GetSectionSize(fFontSize);
+    szTotal.width = std::max(size.width, szTotal.width);
+    szTotal.height += size.height;
+    if (FXSYS_IsFloatBigger(szTotal.width, GetPlateWidth()) ||
+        FXSYS_IsFloatBigger(szTotal.height, GetPlateHeight())) {
+      return true;
+    }
+  }
+  return false;
+}
+
+CPVT_FloatRect CPVT_VariableText::RearrangeSections(
+    const CPVT_WordRange& PlaceRange) {
+  float fPosY = 0;
+  CPVT_FloatRect rcRet;
+  for (int32_t s = 0, sz = fxcrt::CollectionSize<int32_t>(m_SectionArray);
+       s < sz; s++) {
+    CPVT_WordPlace place;
+    place.nSecIndex = s;
+    CPVT_Section* pSection = m_SectionArray[s].get();
+    pSection->SetPlace(place);
+    CPVT_FloatRect rcSec = pSection->GetRect();
+    if (s >= PlaceRange.BeginPos.nSecIndex) {
+      if (s <= PlaceRange.EndPos.nSecIndex) {
+        rcSec = pSection->Rearrange();
+        rcSec.top += fPosY;
+        rcSec.bottom += fPosY;
+      } else {
+        float fOldHeight = pSection->GetRect().bottom - pSection->GetRect().top;
+        rcSec.top = fPosY;
+        rcSec.bottom = fPosY + fOldHeight;
+      }
+      pSection->SetRect(rcSec);
+      pSection->ResetLinePlace();
+    }
+    if (s == 0) {
+      rcRet = rcSec;
+    } else {
+      rcRet.left = std::min(rcSec.left, rcRet.left);
+      rcRet.top = std::min(rcSec.top, rcRet.top);
+      rcRet.right = std::max(rcSec.right, rcRet.right);
+      rcRet.bottom = std::max(rcSec.bottom, rcRet.bottom);
+    }
+    fPosY += rcSec.Height();
+  }
+  return rcRet;
+}
+
+int CPVT_VariableText::GetCharWidth(int32_t nFontIndex,
+                                    uint16_t Word,
+                                    uint16_t SubWord) const {
+  if (!m_pVTProvider)
+    return 0;
+  uint16_t word = SubWord ? SubWord : Word;
+  return m_pVTProvider->GetCharWidth(nFontIndex, word);
+}
+
+int32_t CPVT_VariableText::GetWordFontIndex(uint16_t word,
+                                            FX_Charset charset,
+                                            int32_t nFontIndex) {
+  return m_pVTProvider
+             ? m_pVTProvider->GetWordFontIndex(word, charset, nFontIndex)
+             : -1;
+}
+
+int32_t CPVT_VariableText::GetDefaultFontIndex() {
+  return m_pVTProvider ? m_pVTProvider->GetDefaultFontIndex() : -1;
+}
+
+CPVT_VariableText::Iterator* CPVT_VariableText::GetIterator() {
+  if (!m_pVTIterator)
+    m_pVTIterator = std::make_unique<CPVT_VariableText::Iterator>(this);
+  return m_pVTIterator.get();
+}
+
+void CPVT_VariableText::SetProvider(Provider* pProvider) {
+  m_pVTProvider = pProvider;
+}
+
+CFX_PointF CPVT_VariableText::GetBTPoint() const {
+  return CFX_PointF(m_rcPlate.left, m_rcPlate.top);
+}
+
+CFX_PointF CPVT_VariableText::GetETPoint() const {
+  return CFX_PointF(m_rcPlate.right, m_rcPlate.bottom);
+}
+
+CFX_PointF CPVT_VariableText::InToOut(const CFX_PointF& point) const {
+  return CFX_PointF(point.x + GetBTPoint().x, GetBTPoint().y - point.y);
+}
+
+CFX_PointF CPVT_VariableText::OutToIn(const CFX_PointF& point) const {
+  return CFX_PointF(point.x - GetBTPoint().x, GetBTPoint().y - point.y);
+}
+
+CFX_FloatRect CPVT_VariableText::InToOut(const CPVT_FloatRect& rect) const {
+  CFX_PointF ptLeftTop = InToOut(CFX_PointF(rect.left, rect.top));
+  CFX_PointF ptRightBottom = InToOut(CFX_PointF(rect.right, rect.bottom));
+  return CFX_FloatRect(ptLeftTop.x, ptRightBottom.y, ptRightBottom.x,
+                       ptLeftTop.y);
+}
diff --git a/core/fpdfdoc/cpvt_variabletext.h b/core/fpdfdoc/cpvt_variabletext.h
new file mode 100644
index 0000000..a378dbb
--- /dev/null
+++ b/core/fpdfdoc/cpvt_variabletext.h
@@ -0,0 +1,203 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFDOC_CPVT_VARIABLETEXT_H_
+#define CORE_FPDFDOC_CPVT_VARIABLETEXT_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "core/fpdfdoc/cpvt_floatrect.h"
+#include "core/fpdfdoc/cpvt_line.h"
+#include "core/fpdfdoc/cpvt_lineinfo.h"
+#include "core/fpdfdoc/cpvt_wordplace.h"
+#include "core/fpdfdoc/cpvt_wordrange.h"
+#include "core/fxcrt/fx_codepage_forward.h"
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
+
+class CPVT_Section;
+class CPVT_Word;
+class IPVT_FontMap;
+struct CPVT_WordInfo;
+
+class CPVT_VariableText {
+ public:
+  class Iterator {
+   public:
+    explicit Iterator(CPVT_VariableText* pVT);
+    ~Iterator();
+
+    bool NextWord();
+    bool NextLine();
+    bool GetWord(CPVT_Word& word) const;
+    bool GetLine(CPVT_Line& line) const;
+    void SetAt(int32_t nWordIndex);
+    void SetAt(const CPVT_WordPlace& place);
+    const CPVT_WordPlace& GetWordPlace() const { return m_CurPos; }
+
+   private:
+    CPVT_WordPlace m_CurPos;
+    UnownedPtr<const CPVT_VariableText> const m_pVT;
+  };
+
+  class Provider {
+   public:
+    explicit Provider(IPVT_FontMap* pFontMap);
+    virtual ~Provider();
+
+    virtual int GetCharWidth(int32_t nFontIndex, uint16_t word);
+    virtual int32_t GetTypeAscent(int32_t nFontIndex);
+    virtual int32_t GetTypeDescent(int32_t nFontIndex);
+    virtual int32_t GetWordFontIndex(uint16_t word,
+                                     FX_Charset charset,
+                                     int32_t nFontIndex);
+    virtual int32_t GetDefaultFontIndex();
+
+    IPVT_FontMap* GetFontMap() { return m_pFontMap; }
+
+   private:
+    UnownedPtr<IPVT_FontMap> const m_pFontMap;
+  };
+
+  explicit CPVT_VariableText(Provider* Provider);
+  ~CPVT_VariableText();
+
+  void SetProvider(Provider* pProvider);
+  CPVT_VariableText::Iterator* GetIterator();
+
+  CFX_FloatRect GetContentRect() const;
+  void SetPlateRect(const CFX_FloatRect& rect);
+  const CFX_FloatRect& GetPlateRect() const;
+
+  void SetAlignment(int32_t nFormat) { m_nAlignment = nFormat; }
+  void SetPasswordChar(uint16_t wSubWord) { m_wSubWord = wSubWord; }
+  void SetLimitChar(int32_t nLimitChar) { m_nLimitChar = nLimitChar; }
+  void SetMultiLine(bool bMultiLine) { m_bMultiLine = bMultiLine; }
+  void SetAutoReturn(bool bAuto) { m_bLimitWidth = bAuto; }
+  void SetFontSize(float fFontSize) { m_fFontSize = fFontSize; }
+  void SetCharArray(int32_t nCharArray) { m_nCharArray = nCharArray; }
+  void SetAutoFontSize(bool bAuto) { m_bAutoFontSize = bAuto; }
+  void Initialize();
+
+  bool IsValid() const { return m_bInitialized; }
+
+  void RearrangeAll();
+  void RearrangePart(const CPVT_WordRange& PlaceRange);
+  void SetText(const WideString& text);
+  CPVT_WordPlace InsertWord(const CPVT_WordPlace& place,
+                            uint16_t word,
+                            FX_Charset charset);
+  CPVT_WordPlace InsertSection(const CPVT_WordPlace& place);
+  CPVT_WordPlace DeleteWords(const CPVT_WordRange& PlaceRange);
+  CPVT_WordPlace DeleteWord(const CPVT_WordPlace& place);
+  CPVT_WordPlace BackSpaceWord(const CPVT_WordPlace& place);
+
+  int32_t GetTotalWords() const;
+  float GetFontSize() const { return m_fFontSize; }
+  int32_t GetAlignment() const { return m_nAlignment; }
+  uint16_t GetPasswordChar() const { return GetSubWord(); }
+  int32_t GetCharArray() const { return m_nCharArray; }
+  int32_t GetLimitChar() const { return m_nLimitChar; }
+  bool IsMultiLine() const { return m_bMultiLine; }
+  bool IsAutoReturn() const { return m_bLimitWidth; }
+
+  CPVT_WordPlace GetBeginWordPlace() const;
+  CPVT_WordPlace GetEndWordPlace() const;
+  CPVT_WordPlace GetPrevWordPlace(const CPVT_WordPlace& place) const;
+  CPVT_WordPlace GetNextWordPlace(const CPVT_WordPlace& place) const;
+  CPVT_WordPlace SearchWordPlace(const CFX_PointF& point) const;
+  CPVT_WordPlace GetUpWordPlace(const CPVT_WordPlace& place,
+                                const CFX_PointF& point) const;
+  CPVT_WordPlace GetDownWordPlace(const CPVT_WordPlace& place,
+                                  const CFX_PointF& point) const;
+  CPVT_WordPlace GetLineBeginPlace(const CPVT_WordPlace& place) const;
+  CPVT_WordPlace GetLineEndPlace(const CPVT_WordPlace& place) const;
+  CPVT_WordPlace GetSectionBeginPlace(const CPVT_WordPlace& place) const;
+  CPVT_WordPlace GetSectionEndPlace(const CPVT_WordPlace& place) const;
+  void UpdateWordPlace(CPVT_WordPlace& place) const;
+  CPVT_WordPlace PrevLineHeaderPlace(const CPVT_WordPlace& place) const;
+  CPVT_WordPlace NextLineHeaderPlace(const CPVT_WordPlace& place) const;
+  int32_t WordPlaceToWordIndex(const CPVT_WordPlace& place) const;
+  CPVT_WordPlace WordIndexToWordPlace(int32_t index) const;
+
+  uint16_t GetSubWord() const { return m_wSubWord; }
+
+  float GetPlateWidth() const { return m_rcPlate.right - m_rcPlate.left; }
+  float GetPlateHeight() const { return m_rcPlate.top - m_rcPlate.bottom; }
+  CFX_PointF GetBTPoint() const;
+  CFX_PointF GetETPoint() const;
+
+  CFX_PointF InToOut(const CFX_PointF& point) const;
+  CFX_PointF OutToIn(const CFX_PointF& point) const;
+  CFX_FloatRect InToOut(const CPVT_FloatRect& rect) const;
+
+  float GetFontAscent(int32_t nFontIndex, float fFontSize) const;
+  float GetFontDescent(int32_t nFontIndex, float fFontSize) const;
+  int32_t GetDefaultFontIndex();
+  float GetLineLeading();
+  float GetWordWidth(const CPVT_WordInfo& WordInfo) const;
+  float GetWordWidth(int32_t nFontIndex,
+                     uint16_t Word,
+                     uint16_t SubWord,
+                     float fFontSize,
+                     float fWordTail) const;
+  float GetWordAscent(const CPVT_WordInfo& WordInfo) const;
+  float GetWordDescent(const CPVT_WordInfo& WordInfo) const;
+  float GetWordAscent(const CPVT_WordInfo& WordInfo, float fFontSize) const;
+  float GetWordDescent(const CPVT_WordInfo& WordInfo, float fFontSize) const;
+  float GetLineAscent();
+  float GetLineDescent();
+  float GetLineIndent();
+
+ private:
+  int GetCharWidth(int32_t nFontIndex, uint16_t Word, uint16_t SubWord) const;
+  int32_t GetWordFontIndex(uint16_t word,
+                           FX_Charset charset,
+                           int32_t nFontIndex);
+
+  CPVT_WordPlace AddSection(const CPVT_WordPlace& place);
+  CPVT_WordPlace AddLine(const CPVT_WordPlace& place,
+                         const CPVT_LineInfo& lineinfo);
+  CPVT_WordPlace AddWord(const CPVT_WordPlace& place,
+                         const CPVT_WordInfo& wordinfo);
+  float GetWordFontSize() const;
+
+  void ClearSectionRightWords(const CPVT_WordPlace& place);
+
+  bool ClearEmptySection(const CPVT_WordPlace& place);
+  void ClearEmptySections(const CPVT_WordRange& PlaceRange);
+  void LinkLatterSection(const CPVT_WordPlace& place);
+  void ClearWords(const CPVT_WordRange& PlaceRange);
+  CPVT_WordPlace ClearLeftWord(const CPVT_WordPlace& place);
+  CPVT_WordPlace ClearRightWord(const CPVT_WordPlace& place);
+
+  void Rearrange(const CPVT_WordRange& PlaceRange);
+  float GetAutoFontSize();
+  bool IsBigger(float fFontSize) const;
+  CPVT_FloatRect RearrangeSections(const CPVT_WordRange& PlaceRange);
+
+  bool m_bInitialized = false;
+  bool m_bMultiLine = false;
+  bool m_bLimitWidth = false;
+  bool m_bAutoFontSize = false;
+  uint16_t m_wSubWord = 0;
+  int32_t m_nLimitChar = 0;
+  int32_t m_nCharArray = 0;
+  int32_t m_nAlignment = 0;
+  float m_fLineLeading = 0.0f;
+  float m_fFontSize = 0.0f;
+  std::vector<std::unique_ptr<CPVT_Section>> m_SectionArray;
+  UnownedPtr<Provider> m_pVTProvider;
+  std::unique_ptr<Iterator> m_pVTIterator;
+  CFX_FloatRect m_rcPlate;
+  CPVT_FloatRect m_rcContent;
+};
+
+#endif  // CORE_FPDFDOC_CPVT_VARIABLETEXT_H_
diff --git a/core/fpdfdoc/cpvt_word.h b/core/fpdfdoc/cpvt_word.h
index ad0ba74..f5ec843 100644
--- a/core/fpdfdoc/cpvt_word.h
+++ b/core/fpdfdoc/cpvt_word.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,17 @@
 #ifndef CORE_FPDFDOC_CPVT_WORD_H_
 #define CORE_FPDFDOC_CPVT_WORD_H_
 
+#include <stdint.h>
+
 #include "core/fpdfdoc/cpvt_wordplace.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/fx_codepage.h"
 
 class CPVT_Word {
  public:
   CPVT_Word();
 
   uint16_t Word;
-  int32_t nCharset;
+  FX_Charset nCharset;
   CPVT_WordPlace WordPlace;
   CFX_PointF ptWord;
   float fAscent;
@@ -27,7 +29,7 @@
 
 inline CPVT_Word::CPVT_Word()
     : Word(0),
-      nCharset(0),
+      nCharset(FX_Charset::kANSI),
       fAscent(0.0f),
       fDescent(0.0f),
       fWidth(0.0f),
diff --git a/core/fpdfdoc/cpvt_wordinfo.cpp b/core/fpdfdoc/cpvt_wordinfo.cpp
index 1e1556b..a301a32 100644
--- a/core/fpdfdoc/cpvt_wordinfo.cpp
+++ b/core/fpdfdoc/cpvt_wordinfo.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
@@ -10,13 +10,15 @@
 
 CPVT_WordInfo::CPVT_WordInfo()
     : Word(0),
-      nCharset(FX_CHARSET_ANSI),
+      nCharset(FX_Charset::kANSI),
       fWordX(0.0f),
       fWordY(0.0f),
       fWordTail(0.0f),
       nFontIndex(-1) {}
 
-CPVT_WordInfo::CPVT_WordInfo(uint16_t word, int32_t charset, int32_t fontIndex)
+CPVT_WordInfo::CPVT_WordInfo(uint16_t word,
+                             FX_Charset charset,
+                             int32_t fontIndex)
     : Word(word),
       nCharset(charset),
       fWordX(0.0f),
@@ -26,7 +28,7 @@
 
 CPVT_WordInfo::CPVT_WordInfo(const CPVT_WordInfo& word)
     : Word(0),
-      nCharset(FX_CHARSET_ANSI),
+      nCharset(FX_Charset::kANSI),
       fWordX(0.0f),
       fWordY(0.0f),
       fWordTail(0.0f),
@@ -34,7 +36,7 @@
   operator=(word);
 }
 
-CPVT_WordInfo::~CPVT_WordInfo() {}
+CPVT_WordInfo::~CPVT_WordInfo() = default;
 
 CPVT_WordInfo& CPVT_WordInfo::operator=(const CPVT_WordInfo& word) {
   if (this == &word)
diff --git a/core/fpdfdoc/cpvt_wordinfo.h b/core/fpdfdoc/cpvt_wordinfo.h
index e23ab2c..98ef9f2 100644
--- a/core/fpdfdoc/cpvt_wordinfo.h
+++ b/core/fpdfdoc/cpvt_wordinfo.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,18 +7,20 @@
 #ifndef CORE_FPDFDOC_CPVT_WORDINFO_H_
 #define CORE_FPDFDOC_CPVT_WORDINFO_H_
 
-#include "core/fxcrt/fx_system.h"
+#include <stdint.h>
+
+#include "core/fxcrt/fx_codepage_forward.h"
 
 struct CPVT_WordInfo {
   CPVT_WordInfo();
-  CPVT_WordInfo(uint16_t word, int32_t charset, int32_t fontIndex);
+  CPVT_WordInfo(uint16_t word, FX_Charset charset, int32_t fontIndex);
   CPVT_WordInfo(const CPVT_WordInfo& word);
   ~CPVT_WordInfo();
 
   CPVT_WordInfo& operator=(const CPVT_WordInfo& word);
 
   uint16_t Word;
-  int32_t nCharset;
+  FX_Charset nCharset;
   float fWordX;
   float fWordY;
   float fWordTail;
diff --git a/core/fpdfdoc/cpvt_wordplace.h b/core/fpdfdoc/cpvt_wordplace.h
index c0a1a9c..2d0f9c8 100644
--- a/core/fpdfdoc/cpvt_wordplace.h
+++ b/core/fpdfdoc/cpvt_wordplace.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,10 @@
 #ifndef CORE_FPDFDOC_CPVT_WORDPLACE_H_
 #define CORE_FPDFDOC_CPVT_WORDPLACE_H_
 
-#include "core/fxcrt/fx_system.h"
+#include <stdint.h>
 
 struct CPVT_WordPlace {
-  CPVT_WordPlace() : nSecIndex(-1), nLineIndex(-1), nWordIndex(-1) {}
+  CPVT_WordPlace() = default;
 
   CPVT_WordPlace(int32_t other_nSecIndex,
                  int32_t other_nLineIndex,
@@ -65,9 +65,9 @@
     return nLineIndex - wp.nLineIndex;
   }
 
-  int32_t nSecIndex;
-  int32_t nLineIndex;
-  int32_t nWordIndex;
+  int32_t nSecIndex = -1;
+  int32_t nLineIndex = -1;
+  int32_t nWordIndex = -1;
 };
 
 #endif  // CORE_FPDFDOC_CPVT_WORDPLACE_H_
diff --git a/core/fpdfdoc/cpvt_wordrange.h b/core/fpdfdoc/cpvt_wordrange.h
index fbc691f..5d84af3 100644
--- a/core/fpdfdoc/cpvt_wordrange.h
+++ b/core/fpdfdoc/cpvt_wordrange.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,6 @@
 #include <utility>
 
 #include "core/fpdfdoc/cpvt_wordplace.h"
-#include "core/fxcrt/fx_system.h"
 
 struct CPVT_WordRange {
   CPVT_WordRange() = default;
diff --git a/core/fpdfdoc/csection.cpp b/core/fpdfdoc/csection.cpp
deleted file mode 100644
index ad962be..0000000
--- a/core/fpdfdoc/csection.cpp
+++ /dev/null
@@ -1,252 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fpdfdoc/csection.h"
-
-#include <algorithm>
-
-#include "core/fpdfdoc/cline.h"
-#include "core/fpdfdoc/cpdf_variabletext.h"
-#include "core/fpdfdoc/cpvt_wordinfo.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-CSection::CSection(CPDF_VariableText* pVT) : m_pVT(pVT) {
-  ASSERT(m_pVT);
-}
-
-CSection::~CSection() = default;
-
-void CSection::ResetLinePlace() {
-  int32_t i = 0;
-  for (auto& pLine : m_LineArray) {
-    pLine->LinePlace = CPVT_WordPlace(SecPlace.nSecIndex, i, -1);
-    ++i;
-  }
-}
-
-CPVT_WordPlace CSection::AddWord(const CPVT_WordPlace& place,
-                                 const CPVT_WordInfo& wordinfo) {
-  int32_t nWordIndex = pdfium::clamp(
-      place.nWordIndex, 0, pdfium::CollectionSize<int32_t>(m_WordArray));
-  m_WordArray.insert(m_WordArray.begin() + nWordIndex,
-                     pdfium::MakeUnique<CPVT_WordInfo>(wordinfo));
-  return place;
-}
-
-CPVT_WordPlace CSection::AddLine(const CPVT_LineInfo& lineinfo) {
-  m_LineArray.push_back(pdfium::MakeUnique<CLine>(lineinfo));
-  return CPVT_WordPlace(SecPlace.nSecIndex, m_LineArray.size() - 1, -1);
-}
-
-CPVT_FloatRect CSection::Rearrange() {
-  if (m_pVT->GetCharArray() > 0)
-    return CTypeset(this).CharArray();
-  return CTypeset(this).Typeset();
-}
-
-CFX_SizeF CSection::GetSectionSize(float fFontSize) {
-  return CTypeset(this).GetEditSize(fFontSize);
-}
-
-CPVT_WordPlace CSection::GetBeginWordPlace() const {
-  if (m_LineArray.empty())
-    return SecPlace;
-  return m_LineArray.front()->GetBeginWordPlace();
-}
-
-CPVT_WordPlace CSection::GetEndWordPlace() const {
-  if (m_LineArray.empty())
-    return SecPlace;
-  return m_LineArray.back()->GetEndWordPlace();
-}
-
-CPVT_WordPlace CSection::GetPrevWordPlace(const CPVT_WordPlace& place) const {
-  if (place.nLineIndex < 0)
-    return GetBeginWordPlace();
-
-  if (place.nLineIndex >= pdfium::CollectionSize<int32_t>(m_LineArray))
-    return GetEndWordPlace();
-
-  CLine* pLine = m_LineArray[place.nLineIndex].get();
-  if (place.nWordIndex == pLine->m_LineInfo.nBeginWordIndex)
-    return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, -1);
-
-  if (place.nWordIndex >= pLine->m_LineInfo.nBeginWordIndex)
-    return pLine->GetPrevWordPlace(place);
-
-  if (!pdfium::IndexInBounds(m_LineArray, place.nLineIndex - 1))
-    return place;
-
-  return m_LineArray[place.nLineIndex - 1]->GetEndWordPlace();
-}
-
-CPVT_WordPlace CSection::GetNextWordPlace(const CPVT_WordPlace& place) const {
-  if (place.nLineIndex < 0)
-    return GetBeginWordPlace();
-
-  if (place.nLineIndex >= pdfium::CollectionSize<int32_t>(m_LineArray))
-    return GetEndWordPlace();
-
-  CLine* pLine = m_LineArray[place.nLineIndex].get();
-  if (place.nWordIndex < pLine->m_LineInfo.nEndWordIndex)
-    return pLine->GetNextWordPlace(place);
-
-  if (!pdfium::IndexInBounds(m_LineArray, place.nLineIndex + 1))
-    return place;
-
-  return m_LineArray[place.nLineIndex + 1]->GetBeginWordPlace();
-}
-
-void CSection::UpdateWordPlace(CPVT_WordPlace& place) const {
-  int32_t nLeft = 0;
-  int32_t nRight = pdfium::CollectionSize<int32_t>(m_LineArray) - 1;
-  int32_t nMid = (nLeft + nRight) / 2;
-  while (nLeft <= nRight) {
-    CLine* pLine = m_LineArray[nMid].get();
-    if (place.nWordIndex < pLine->m_LineInfo.nBeginWordIndex) {
-      nRight = nMid - 1;
-      nMid = (nLeft + nRight) / 2;
-    } else if (place.nWordIndex > pLine->m_LineInfo.nEndWordIndex) {
-      nLeft = nMid + 1;
-      nMid = (nLeft + nRight) / 2;
-    } else {
-      place.nLineIndex = nMid;
-      return;
-    }
-  }
-}
-
-CPVT_WordPlace CSection::SearchWordPlace(const CFX_PointF& point) const {
-  CPVT_WordPlace place = GetBeginWordPlace();
-  bool bUp = true;
-  bool bDown = true;
-  int32_t nLeft = 0;
-  int32_t nRight = pdfium::CollectionSize<int32_t>(m_LineArray) - 1;
-  int32_t nMid = pdfium::CollectionSize<int32_t>(m_LineArray) / 2;
-  while (nLeft <= nRight) {
-    CLine* pLine = m_LineArray[nMid].get();
-    float fTop = pLine->m_LineInfo.fLineY - pLine->m_LineInfo.fLineAscent -
-                 m_pVT->GetLineLeading();
-    float fBottom = pLine->m_LineInfo.fLineY - pLine->m_LineInfo.fLineDescent;
-    if (IsFloatBigger(point.y, fTop))
-      bUp = false;
-    if (IsFloatSmaller(point.y, fBottom))
-      bDown = false;
-    if (IsFloatSmaller(point.y, fTop)) {
-      nRight = nMid - 1;
-      nMid = (nLeft + nRight) / 2;
-      continue;
-    }
-    if (IsFloatBigger(point.y, fBottom)) {
-      nLeft = nMid + 1;
-      nMid = (nLeft + nRight) / 2;
-      continue;
-    }
-    place = SearchWordPlace(
-        point.x,
-        CPVT_WordRange(pLine->GetNextWordPlace(pLine->GetBeginWordPlace()),
-                       pLine->GetEndWordPlace()));
-    place.nLineIndex = nMid;
-    return place;
-  }
-  if (bUp)
-    place = GetBeginWordPlace();
-  if (bDown)
-    place = GetEndWordPlace();
-  return place;
-}
-
-CPVT_WordPlace CSection::SearchWordPlace(
-    float fx,
-    const CPVT_WordPlace& lineplace) const {
-  if (!pdfium::IndexInBounds(m_LineArray, lineplace.nLineIndex))
-    return GetBeginWordPlace();
-
-  CLine* pLine = m_LineArray[lineplace.nLineIndex].get();
-  return SearchWordPlace(
-      fx - m_Rect.left,
-      CPVT_WordRange(pLine->GetNextWordPlace(pLine->GetBeginWordPlace()),
-                     pLine->GetEndWordPlace()));
-}
-
-CPVT_WordPlace CSection::SearchWordPlace(float fx,
-                                         const CPVT_WordRange& range) const {
-  CPVT_WordPlace wordplace = range.BeginPos;
-  wordplace.nWordIndex = -1;
-
-  int32_t nLeft = range.BeginPos.nWordIndex;
-  int32_t nRight = range.EndPos.nWordIndex + 1;
-  int32_t nMid = (nLeft + nRight) / 2;
-  while (nLeft < nRight) {
-    if (nMid == nLeft)
-      break;
-    if (nMid == nRight) {
-      nMid--;
-      break;
-    }
-    if (!pdfium::IndexInBounds(m_WordArray, nMid))
-      break;
-    CPVT_WordInfo* pWord = m_WordArray[nMid].get();
-    if (fx > pWord->fWordX + m_pVT->GetWordWidth(*pWord) * VARIABLETEXT_HALF) {
-      nLeft = nMid;
-      nMid = (nLeft + nRight) / 2;
-      continue;
-    }
-    nRight = nMid;
-    nMid = (nLeft + nRight) / 2;
-  }
-  if (pdfium::IndexInBounds(m_WordArray, nMid)) {
-    CPVT_WordInfo* pWord = m_WordArray[nMid].get();
-    if (fx > pWord->fWordX + m_pVT->GetWordWidth(*pWord) * VARIABLETEXT_HALF)
-      wordplace.nWordIndex = nMid;
-  }
-  return wordplace;
-}
-
-void CSection::ClearLeftWords(int32_t nWordIndex) {
-  for (int32_t i = nWordIndex; i >= 0; i--) {
-    if (pdfium::IndexInBounds(m_WordArray, i))
-      m_WordArray.erase(m_WordArray.begin() + i);
-  }
-}
-
-void CSection::ClearRightWords(int32_t nWordIndex) {
-  int32_t sz = pdfium::CollectionSize<int32_t>(m_WordArray);
-  for (int32_t i = sz - 1; i > nWordIndex; i--) {
-    if (pdfium::IndexInBounds(m_WordArray, i))
-      m_WordArray.erase(m_WordArray.begin() + i);
-  }
-}
-
-void CSection::ClearMidWords(int32_t nBeginIndex, int32_t nEndIndex) {
-  for (int32_t i = nEndIndex; i > nBeginIndex; i--) {
-    if (pdfium::IndexInBounds(m_WordArray, i))
-      m_WordArray.erase(m_WordArray.begin() + i);
-  }
-}
-
-void CSection::ClearWords(const CPVT_WordRange& PlaceRange) {
-  CPVT_WordPlace SecBeginPos = GetBeginWordPlace();
-  CPVT_WordPlace SecEndPos = GetEndWordPlace();
-  if (PlaceRange.BeginPos >= SecBeginPos) {
-    if (PlaceRange.EndPos <= SecEndPos) {
-      ClearMidWords(PlaceRange.BeginPos.nWordIndex,
-                    PlaceRange.EndPos.nWordIndex);
-    } else {
-      ClearRightWords(PlaceRange.BeginPos.nWordIndex);
-    }
-  } else if (PlaceRange.EndPos <= SecEndPos) {
-    ClearLeftWords(PlaceRange.EndPos.nWordIndex);
-  } else {
-    m_WordArray.clear();
-  }
-}
-
-void CSection::ClearWord(const CPVT_WordPlace& place) {
-  if (pdfium::IndexInBounds(m_WordArray, place.nWordIndex))
-    m_WordArray.erase(m_WordArray.begin() + place.nWordIndex);
-}
diff --git a/core/fpdfdoc/csection.h b/core/fpdfdoc/csection.h
deleted file mode 100644
index 7352c71..0000000
--- a/core/fpdfdoc/csection.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFDOC_CSECTION_H_
-#define CORE_FPDFDOC_CSECTION_H_
-
-#include <memory>
-#include <vector>
-
-#include "core/fpdfdoc/cline.h"
-#include "core/fpdfdoc/cpvt_wordinfo.h"
-#include "core/fpdfdoc/cpvt_wordrange.h"
-#include "core/fpdfdoc/ctypeset.h"
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
-
-class CPDF_VariableText;
-class CPVT_LineInfo;
-struct CPVT_WordLine;
-struct CPVT_WordPlace;
-
-class CSection final {
- public:
-  explicit CSection(CPDF_VariableText* pVT);
-  ~CSection();
-
-  void ResetLinePlace();
-  CPVT_WordPlace AddWord(const CPVT_WordPlace& place,
-                         const CPVT_WordInfo& wordinfo);
-  CPVT_WordPlace AddLine(const CPVT_LineInfo& lineinfo);
-  void ClearWords(const CPVT_WordRange& PlaceRange);
-  void ClearWord(const CPVT_WordPlace& place);
-  CPVT_FloatRect Rearrange();
-  CFX_SizeF GetSectionSize(float fFontSize);
-  CPVT_WordPlace GetBeginWordPlace() const;
-  CPVT_WordPlace GetEndWordPlace() const;
-  CPVT_WordPlace GetPrevWordPlace(const CPVT_WordPlace& place) const;
-  CPVT_WordPlace GetNextWordPlace(const CPVT_WordPlace& place) const;
-  void UpdateWordPlace(CPVT_WordPlace& place) const;
-  CPVT_WordPlace SearchWordPlace(const CFX_PointF& point) const;
-  CPVT_WordPlace SearchWordPlace(float fx,
-                                 const CPVT_WordPlace& lineplace) const;
-  CPVT_WordPlace SearchWordPlace(float fx, const CPVT_WordRange& range) const;
-
-  CPVT_WordPlace SecPlace;
-  CPVT_FloatRect m_Rect;
-  std::vector<std::unique_ptr<CLine>> m_LineArray;
-  std::vector<std::unique_ptr<CPVT_WordInfo>> m_WordArray;
-
- private:
-  friend class CTypeset;
-
-  void ClearLeftWords(int32_t nWordIndex);
-  void ClearRightWords(int32_t nWordIndex);
-  void ClearMidWords(int32_t nBeginIndex, int32_t nEndIndex);
-
-  UnownedPtr<CPDF_VariableText> const m_pVT;
-};
-
-#endif  // CORE_FPDFDOC_CSECTION_H_
diff --git a/core/fpdfdoc/ctypeset.cpp b/core/fpdfdoc/ctypeset.cpp
deleted file mode 100644
index 8c61f9e..0000000
--- a/core/fpdfdoc/ctypeset.cpp
+++ /dev/null
@@ -1,489 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fpdfdoc/ctypeset.h"
-
-#include <algorithm>
-
-#include "core/fpdfdoc/cline.h"
-#include "core/fpdfdoc/cpdf_variabletext.h"
-#include "core/fpdfdoc/cpvt_wordinfo.h"
-#include "core/fpdfdoc/csection.h"
-#include "third_party/base/stl_util.h"
-
-namespace {
-
-const uint8_t special_chars[128] = {
-    0x00, 0x0C, 0x08, 0x0C, 0x08, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00,
-    0x10, 0x00, 0x00, 0x28, 0x0C, 0x08, 0x00, 0x00, 0x28, 0x28, 0x28, 0x28,
-    0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x08, 0x08,
-    0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0C, 0x00, 0x08, 0x00, 0x00,
-    0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-    0x01, 0x01, 0x01, 0x0C, 0x00, 0x08, 0x00, 0x00,
-};
-
-bool IsLatin(uint16_t word) {
-  if (word <= 0x007F)
-    return !!(special_chars[word] & 0x01);
-
-  return ((word >= 0x00C0 && word <= 0x00FF) ||
-          (word >= 0x0100 && word <= 0x024F) ||
-          (word >= 0x1E00 && word <= 0x1EFF) ||
-          (word >= 0x2C60 && word <= 0x2C7F) ||
-          (word >= 0xA720 && word <= 0xA7FF) ||
-          (word >= 0xFF21 && word <= 0xFF3A) ||
-          (word >= 0xFF41 && word <= 0xFF5A));
-}
-
-bool IsDigit(uint32_t word) {
-  return word >= 0x0030 && word <= 0x0039;
-}
-
-bool IsCJK(uint32_t word) {
-  if ((word >= 0x1100 && word <= 0x11FF) ||
-      (word >= 0x2E80 && word <= 0x2FFF) ||
-      (word >= 0x3040 && word <= 0x9FBF) ||
-      (word >= 0xAC00 && word <= 0xD7AF) ||
-      (word >= 0xF900 && word <= 0xFAFF) ||
-      (word >= 0xFE30 && word <= 0xFE4F) ||
-      (word >= 0x20000 && word <= 0x2A6DF) ||
-      (word >= 0x2F800 && word <= 0x2FA1F)) {
-    return true;
-  }
-  if (word >= 0x3000 && word <= 0x303F) {
-    return (
-        word == 0x3005 || word == 0x3006 || word == 0x3021 || word == 0x3022 ||
-        word == 0x3023 || word == 0x3024 || word == 0x3025 || word == 0x3026 ||
-        word == 0x3027 || word == 0x3028 || word == 0x3029 || word == 0x3031 ||
-        word == 0x3032 || word == 0x3033 || word == 0x3034 || word == 0x3035);
-  }
-  return word >= 0xFF66 && word <= 0xFF9D;
-}
-
-bool IsPunctuation(uint32_t word) {
-  if (word <= 0x007F)
-    return !!(special_chars[word] & 0x08);
-
-  if (word >= 0x0080 && word <= 0x00FF) {
-    return (word == 0x0082 || word == 0x0084 || word == 0x0085 ||
-            word == 0x0091 || word == 0x0092 || word == 0x0093 ||
-            word <= 0x0094 || word == 0x0096 || word == 0x00B4 ||
-            word == 0x00B8);
-  }
-
-  if (word >= 0x2000 && word <= 0x206F) {
-    return (
-        word == 0x2010 || word == 0x2011 || word == 0x2012 || word == 0x2013 ||
-        word == 0x2018 || word == 0x2019 || word == 0x201A || word == 0x201B ||
-        word == 0x201C || word == 0x201D || word == 0x201E || word == 0x201F ||
-        word == 0x2032 || word == 0x2033 || word == 0x2034 || word == 0x2035 ||
-        word == 0x2036 || word == 0x2037 || word == 0x203C || word == 0x203D ||
-        word == 0x203E || word == 0x2044);
-  }
-
-  if (word >= 0x3000 && word <= 0x303F) {
-    return (
-        word == 0x3001 || word == 0x3002 || word == 0x3003 || word == 0x3005 ||
-        word == 0x3009 || word == 0x300A || word == 0x300B || word == 0x300C ||
-        word == 0x300D || word == 0x300F || word == 0x300E || word == 0x3010 ||
-        word == 0x3011 || word == 0x3014 || word == 0x3015 || word == 0x3016 ||
-        word == 0x3017 || word == 0x3018 || word == 0x3019 || word == 0x301A ||
-        word == 0x301B || word == 0x301D || word == 0x301E || word == 0x301F);
-  }
-
-  if (word >= 0xFE50 && word <= 0xFE6F)
-    return (word >= 0xFE50 && word <= 0xFE5E) || word == 0xFE63;
-
-  if (word >= 0xFF00 && word <= 0xFFEF) {
-    return (
-        word == 0xFF01 || word == 0xFF02 || word == 0xFF07 || word == 0xFF08 ||
-        word == 0xFF09 || word == 0xFF0C || word == 0xFF0E || word == 0xFF0F ||
-        word == 0xFF1A || word == 0xFF1B || word == 0xFF1F || word == 0xFF3B ||
-        word == 0xFF3D || word == 0xFF40 || word == 0xFF5B || word == 0xFF5C ||
-        word == 0xFF5D || word == 0xFF61 || word == 0xFF62 || word == 0xFF63 ||
-        word == 0xFF64 || word == 0xFF65 || word == 0xFF9E || word == 0xFF9F);
-  }
-
-  return false;
-}
-
-bool IsConnectiveSymbol(uint32_t word) {
-  return word <= 0x007F && (special_chars[word] & 0x20);
-}
-
-bool IsOpenStylePunctuation(uint32_t word) {
-  if (word <= 0x007F)
-    return !!(special_chars[word] & 0x04);
-
-  return (word == 0x300A || word == 0x300C || word == 0x300E ||
-          word == 0x3010 || word == 0x3014 || word == 0x3016 ||
-          word == 0x3018 || word == 0x301A || word == 0xFF08 ||
-          word == 0xFF3B || word == 0xFF5B || word == 0xFF62);
-}
-
-bool IsCurrencySymbol(uint16_t word) {
-  return (word == 0x0024 || word == 0x0080 || word == 0x00A2 ||
-          word == 0x00A3 || word == 0x00A4 || word == 0x00A5 ||
-          (word >= 0x20A0 && word <= 0x20CF) || word == 0xFE69 ||
-          word == 0xFF04 || word == 0xFFE0 || word == 0xFFE1 ||
-          word == 0xFFE5 || word == 0xFFE6);
-}
-
-bool IsPrefixSymbol(uint16_t word) {
-  return IsCurrencySymbol(word) || word == 0x2116;
-}
-
-bool IsSpace(uint16_t word) {
-  return word == 0x0020 || word == 0x3000;
-}
-
-bool NeedDivision(uint16_t prevWord, uint16_t curWord) {
-  if ((IsLatin(prevWord) || IsDigit(prevWord)) &&
-      (IsLatin(curWord) || IsDigit(curWord))) {
-    return false;
-  }
-  if (IsSpace(curWord) || IsPunctuation(curWord)) {
-    return false;
-  }
-  if (IsConnectiveSymbol(prevWord) || IsConnectiveSymbol(curWord)) {
-    return false;
-  }
-  if (IsSpace(prevWord) || IsPunctuation(prevWord)) {
-    return true;
-  }
-  if (IsPrefixSymbol(prevWord)) {
-    return false;
-  }
-  if (IsPrefixSymbol(curWord) || IsCJK(curWord)) {
-    return true;
-  }
-  if (IsCJK(prevWord)) {
-    return true;
-  }
-  return false;
-}
-
-}  // namespace
-
-CTypeset::CTypeset(CSection* pSection)
-    : m_pVT(pSection->m_pVT), m_pSection(pSection) {}
-
-CTypeset::~CTypeset() = default;
-
-CPVT_FloatRect CTypeset::CharArray() {
-  m_rcRet = CPVT_FloatRect();
-  if (m_pSection->m_LineArray.empty())
-    return m_rcRet;
-
-  float fNodeWidth = m_pVT->GetPlateWidth() /
-                     (m_pVT->GetCharArray() <= 0 ? 1 : m_pVT->GetCharArray());
-  float fLineAscent =
-      m_pVT->GetFontAscent(m_pVT->GetDefaultFontIndex(), m_pVT->GetFontSize());
-  float fLineDescent =
-      m_pVT->GetFontDescent(m_pVT->GetDefaultFontIndex(), m_pVT->GetFontSize());
-  float x = 0.0f;
-  float y = m_pVT->GetLineLeading() + fLineAscent;
-  int32_t nStart = 0;
-  CLine* pLine = m_pSection->m_LineArray.front().get();
-  switch (m_pVT->GetAlignment()) {
-    case 0:
-      pLine->m_LineInfo.fLineX = fNodeWidth * VARIABLETEXT_HALF;
-      break;
-    case 1:
-      nStart = (m_pVT->GetCharArray() -
-                pdfium::CollectionSize<int32_t>(m_pSection->m_WordArray)) /
-               2;
-      pLine->m_LineInfo.fLineX =
-          fNodeWidth * nStart - fNodeWidth * VARIABLETEXT_HALF;
-      break;
-    case 2:
-      nStart = m_pVT->GetCharArray() -
-               pdfium::CollectionSize<int32_t>(m_pSection->m_WordArray);
-      pLine->m_LineInfo.fLineX =
-          fNodeWidth * nStart - fNodeWidth * VARIABLETEXT_HALF;
-      break;
-  }
-  for (int32_t w = 0,
-               sz = pdfium::CollectionSize<int32_t>(m_pSection->m_WordArray);
-       w < sz; w++) {
-    if (w >= m_pVT->GetCharArray())
-      break;
-
-    float fNextWidth = 0;
-    if (pdfium::IndexInBounds(m_pSection->m_WordArray, w + 1)) {
-      CPVT_WordInfo* pNextWord = m_pSection->m_WordArray[w + 1].get();
-      pNextWord->fWordTail = 0;
-      fNextWidth = m_pVT->GetWordWidth(*pNextWord);
-    }
-    CPVT_WordInfo* pWord = m_pSection->m_WordArray[w].get();
-    pWord->fWordTail = 0;
-    float fWordWidth = m_pVT->GetWordWidth(*pWord);
-    float fWordAscent = m_pVT->GetWordAscent(*pWord);
-    float fWordDescent = m_pVT->GetWordDescent(*pWord);
-    x = (float)(fNodeWidth * (w + nStart + 0.5) -
-                fWordWidth * VARIABLETEXT_HALF);
-    pWord->fWordX = x;
-    pWord->fWordY = y;
-    if (w == 0) {
-      pLine->m_LineInfo.fLineX = x;
-    }
-    if (w != pdfium::CollectionSize<int32_t>(m_pSection->m_WordArray) - 1) {
-      pWord->fWordTail =
-          (fNodeWidth - (fWordWidth + fNextWidth) * VARIABLETEXT_HALF > 0
-               ? fNodeWidth - (fWordWidth + fNextWidth) * VARIABLETEXT_HALF
-               : 0);
-    } else {
-      pWord->fWordTail = 0;
-    }
-    x += fWordWidth;
-    fLineAscent = std::max(fLineAscent, fWordAscent);
-    fLineDescent = std::min(fLineDescent, fWordDescent);
-  }
-  pLine->m_LineInfo.nBeginWordIndex = 0;
-  pLine->m_LineInfo.nEndWordIndex =
-      pdfium::CollectionSize<int32_t>(m_pSection->m_WordArray) - 1;
-  pLine->m_LineInfo.fLineY = y;
-  pLine->m_LineInfo.fLineWidth = x - pLine->m_LineInfo.fLineX;
-  pLine->m_LineInfo.fLineAscent = fLineAscent;
-  pLine->m_LineInfo.fLineDescent = fLineDescent;
-  m_rcRet = CPVT_FloatRect(0, 0, x, y - fLineDescent);
-  return m_rcRet;
-}
-
-CFX_SizeF CTypeset::GetEditSize(float fFontSize) {
-  ASSERT(m_pSection);
-  ASSERT(m_pVT);
-  SplitLines(false, fFontSize);
-  return CFX_SizeF(m_rcRet.Width(), m_rcRet.Height());
-}
-
-CPVT_FloatRect CTypeset::Typeset() {
-  ASSERT(m_pVT);
-  m_pSection->m_LineArray.clear();
-  SplitLines(true, 0.0f);
-  OutputLines();
-  return m_rcRet;
-}
-
-void CTypeset::SplitLines(bool bTypeset, float fFontSize) {
-  ASSERT(m_pVT);
-  ASSERT(m_pSection);
-
-  CPVT_LineInfo line;
-  if (m_pSection->m_WordArray.empty()) {
-    float fLineAscent;
-    float fLineDescent;
-    if (bTypeset) {
-      fLineAscent = m_pVT->GetLineAscent();
-      fLineDescent = m_pVT->GetLineDescent();
-    } else {
-      fLineAscent =
-          m_pVT->GetFontAscent(m_pVT->GetDefaultFontIndex(), fFontSize);
-      fLineDescent =
-          m_pVT->GetFontDescent(m_pVT->GetDefaultFontIndex(), fFontSize);
-    }
-    if (bTypeset) {
-      line.nBeginWordIndex = -1;
-      line.nEndWordIndex = -1;
-      line.nTotalWord = 0;
-      line.fLineWidth = 0;
-      line.fLineAscent = fLineAscent;
-      line.fLineDescent = fLineDescent;
-      m_pSection->AddLine(line);
-    }
-    float fMaxY = m_pVT->GetLineLeading() + fLineAscent - fLineDescent;
-    m_rcRet = CPVT_FloatRect(0, 0, 0, fMaxY);
-    return;
-  }
-
-  int32_t nLineHead = 0;
-  int32_t nLineTail = 0;
-  float fMaxX = 0.0f;
-  float fMaxY = 0.0f;
-  float fLineWidth = 0.0f;
-  float fBackupLineWidth = 0.0f;
-  float fLineAscent = 0.0f;
-  float fBackupLineAscent = 0.0f;
-  float fLineDescent = 0.0f;
-  float fBackupLineDescent = 0.0f;
-  int32_t nWordStartPos = 0;
-  bool bFullWord = false;
-  int32_t nLineFullWordIndex = 0;
-  int32_t nCharIndex = 0;
-  float fWordWidth = 0;
-  float fTypesetWidth =
-      std::max(m_pVT->GetPlateWidth() - m_pVT->GetLineIndent(), 0.0f);
-  int32_t nTotalWords =
-      pdfium::CollectionSize<int32_t>(m_pSection->m_WordArray);
-  bool bOpened = false;
-  int32_t i = 0;
-  while (i < nTotalWords) {
-    CPVT_WordInfo* pWord = m_pSection->m_WordArray[i].get();
-    CPVT_WordInfo* pOldWord = pWord;
-    if (i > 0) {
-      pOldWord = m_pSection->m_WordArray[i - 1].get();
-    }
-    if (pWord) {
-      if (bTypeset) {
-        fLineAscent = std::max(fLineAscent, m_pVT->GetWordAscent(*pWord));
-        fLineDescent = std::min(fLineDescent, m_pVT->GetWordDescent(*pWord));
-        fWordWidth = m_pVT->GetWordWidth(*pWord);
-      } else {
-        fLineAscent =
-            std::max(fLineAscent, m_pVT->GetWordAscent(*pWord, fFontSize));
-        fLineDescent =
-            std::min(fLineDescent, m_pVT->GetWordDescent(*pWord, fFontSize));
-        fWordWidth = m_pVT->GetWordWidth(
-            pWord->nFontIndex, pWord->Word, m_pVT->GetSubWord(),
-            m_pVT->GetCharSpace(), fFontSize, pWord->fWordTail);
-      }
-      if (!bOpened) {
-        if (IsOpenStylePunctuation(pWord->Word)) {
-          bOpened = true;
-          bFullWord = true;
-        } else if (pOldWord) {
-          if (NeedDivision(pOldWord->Word, pWord->Word)) {
-            bFullWord = true;
-          }
-        }
-      } else {
-        if (!IsSpace(pWord->Word) && !IsOpenStylePunctuation(pWord->Word)) {
-          bOpened = false;
-        }
-      }
-      if (bFullWord) {
-        bFullWord = false;
-        if (nCharIndex > 0) {
-          nLineFullWordIndex++;
-        }
-        nWordStartPos = i;
-        fBackupLineWidth = fLineWidth;
-        fBackupLineAscent = fLineAscent;
-        fBackupLineDescent = fLineDescent;
-      }
-      nCharIndex++;
-    }
-    if (m_pVT->IsAutoReturn() && fTypesetWidth > 0 &&
-        fLineWidth + fWordWidth > fTypesetWidth) {
-      if (nLineFullWordIndex > 0) {
-        i = nWordStartPos;
-        fLineWidth = fBackupLineWidth;
-        fLineAscent = fBackupLineAscent;
-        fLineDescent = fBackupLineDescent;
-      }
-      if (nCharIndex == 1) {
-        fLineWidth = fWordWidth;
-        i++;
-      }
-      nLineTail = i - 1;
-      if (bTypeset) {
-        line.nBeginWordIndex = nLineHead;
-        line.nEndWordIndex = nLineTail;
-        line.nTotalWord = nLineTail - nLineHead + 1;
-        line.fLineWidth = fLineWidth;
-        line.fLineAscent = fLineAscent;
-        line.fLineDescent = fLineDescent;
-        m_pSection->AddLine(line);
-      }
-      fMaxY += (fLineAscent + m_pVT->GetLineLeading());
-      fMaxY -= fLineDescent;
-      fMaxX = std::max(fLineWidth, fMaxX);
-      nLineHead = i;
-      fLineWidth = 0.0f;
-      fLineAscent = 0.0f;
-      fLineDescent = 0.0f;
-      nCharIndex = 0;
-      nLineFullWordIndex = 0;
-      bFullWord = false;
-    } else {
-      fLineWidth += fWordWidth;
-      i++;
-    }
-  }
-  if (nLineHead <= nTotalWords - 1) {
-    nLineTail = nTotalWords - 1;
-    if (bTypeset) {
-      line.nBeginWordIndex = nLineHead;
-      line.nEndWordIndex = nLineTail;
-      line.nTotalWord = nLineTail - nLineHead + 1;
-      line.fLineWidth = fLineWidth;
-      line.fLineAscent = fLineAscent;
-      line.fLineDescent = fLineDescent;
-      m_pSection->AddLine(line);
-    }
-    fMaxY += (fLineAscent + m_pVT->GetLineLeading());
-    fMaxY -= fLineDescent;
-    fMaxX = std::max(fLineWidth, fMaxX);
-  }
-  m_rcRet = CPVT_FloatRect(0, 0, fMaxX, fMaxY);
-}
-
-void CTypeset::OutputLines() {
-  ASSERT(m_pVT);
-  ASSERT(m_pSection);
-  float fMinX;
-  float fLineIndent = m_pVT->GetLineIndent();
-  float fTypesetWidth = std::max(m_pVT->GetPlateWidth() - fLineIndent, 0.0f);
-  switch (m_pVT->GetAlignment()) {
-    default:
-    case 0:
-      fMinX = 0.0f;
-      break;
-    case 1:
-      fMinX = (fTypesetWidth - m_rcRet.Width()) * VARIABLETEXT_HALF;
-      break;
-    case 2:
-      fMinX = fTypesetWidth - m_rcRet.Width();
-      break;
-  }
-  float fMaxX = fMinX + m_rcRet.Width();
-  float fMinY = 0.0f;
-  float fMaxY = m_rcRet.Height();
-  int32_t nTotalLines =
-      pdfium::CollectionSize<int32_t>(m_pSection->m_LineArray);
-  if (nTotalLines > 0) {
-    float fPosX = 0.0f;
-    float fPosY = 0.0f;
-    for (int32_t l = 0; l < nTotalLines; l++) {
-      CLine* pLine = m_pSection->m_LineArray[l].get();
-      switch (m_pVT->GetAlignment()) {
-        default:
-        case 0:
-          fPosX = 0;
-          break;
-        case 1:
-          fPosX = (fTypesetWidth - pLine->m_LineInfo.fLineWidth) *
-                  VARIABLETEXT_HALF;
-          break;
-        case 2:
-          fPosX = fTypesetWidth - pLine->m_LineInfo.fLineWidth;
-          break;
-      }
-      fPosX += fLineIndent;
-      fPosY += m_pVT->GetLineLeading();
-      fPosY += pLine->m_LineInfo.fLineAscent;
-      pLine->m_LineInfo.fLineX = fPosX - fMinX;
-      pLine->m_LineInfo.fLineY = fPosY - fMinY;
-      for (int32_t w = pLine->m_LineInfo.nBeginWordIndex;
-           w <= pLine->m_LineInfo.nEndWordIndex; w++) {
-        if (pdfium::IndexInBounds(m_pSection->m_WordArray, w)) {
-          CPVT_WordInfo* pWord = m_pSection->m_WordArray[w].get();
-          pWord->fWordX = fPosX - fMinX;
-          pWord->fWordY = fPosY - fMinY;
-
-          fPosX += m_pVT->GetWordWidth(*pWord);
-        }
-      }
-      fPosY -= pLine->m_LineInfo.fLineDescent;
-    }
-  }
-  m_rcRet = CPVT_FloatRect(fMinX, fMinY, fMaxX, fMaxY);
-}
diff --git a/core/fpdfdoc/ctypeset.h b/core/fpdfdoc/ctypeset.h
deleted file mode 100644
index 3d56a3b..0000000
--- a/core/fpdfdoc/ctypeset.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFDOC_CTYPESET_H_
-#define CORE_FPDFDOC_CTYPESET_H_
-
-#include "core/fpdfdoc/cpvt_floatrect.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class CPDF_VariableText;
-class CSection;
-
-class CTypeset final {
- public:
-  explicit CTypeset(CSection* pSection);
-  ~CTypeset();
-
-  CFX_SizeF GetEditSize(float fFontSize);
-  CPVT_FloatRect Typeset();
-  CPVT_FloatRect CharArray();
-
- private:
-  void SplitLines(bool bTypeset, float fFontSize);
-  void OutputLines();
-
-  CPVT_FloatRect m_rcRet;
-  UnownedPtr<CPDF_VariableText> const m_pVT;
-  CSection* const m_pSection;
-};
-
-#endif  // CORE_FPDFDOC_CTYPESET_H_
diff --git a/core/fpdfdoc/ipvt_fontmap.h b/core/fpdfdoc/ipvt_fontmap.h
index e818356..dd210b0 100644
--- a/core/fpdfdoc/ipvt_fontmap.h
+++ b/core/fpdfdoc/ipvt_fontmap.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,8 @@
 
 #include <stdint.h>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/fx_codepage_forward.h"
 #include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Font;
@@ -21,10 +22,11 @@
   virtual RetainPtr<CPDF_Font> GetPDFFont(int32_t nFontIndex) = 0;
   virtual ByteString GetPDFFontAlias(int32_t nFontIndex) = 0;
   virtual int32_t GetWordFontIndex(uint16_t word,
-                                   int32_t charset,
+                                   FX_Charset charset,
                                    int32_t nFontIndex) = 0;
   virtual int32_t CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) = 0;
-  virtual int32_t CharSetFromUnicode(uint16_t word, int32_t nOldCharset) = 0;
+  virtual FX_Charset CharSetFromUnicode(uint16_t word,
+                                        FX_Charset nOldCharset) = 0;
 };
 
 #endif  // CORE_FPDFDOC_IPVT_FONTMAP_H_
diff --git a/core/fpdftext/BUILD.gn b/core/fpdftext/BUILD.gn
index f48a96d..cea247e 100644
--- a/core/fpdftext/BUILD.gn
+++ b/core/fpdftext/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -16,7 +16,7 @@
     "unicodenormalizationdata.cpp",
     "unicodenormalizationdata.h",
   ]
-  configs += [ "../../:pdfium_core_config" ]
+  configs += [ "../../:pdfium_strict_config" ]
   deps = [
     "../fpdfapi/font",
     "../fpdfapi/page",
diff --git a/core/fpdftext/cpdf_linkextract.cpp b/core/fpdftext/cpdf_linkextract.cpp
index f822f3b..66a3dda 100644
--- a/core/fpdftext/cpdf_linkextract.cpp
+++ b/core/fpdftext/cpdf_linkextract.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -112,11 +112,11 @@
 
 void CPDF_LinkExtract::ExtractLinks() {
   m_LinkArray.clear();
-  int start = 0;
-  int pos = 0;
+  size_t start = 0;
+  size_t pos = 0;
   bool bAfterHyphen = false;
   bool bLineBreak = false;
-  const int nTotalChar = m_pTextPage->CountChars();
+  const size_t nTotalChar = m_pTextPage->CountChars();
   const WideString page_text = m_pTextPage->GetAllPageText();
   while (pos < nTotalChar) {
     const CPDF_TextPage::CharInfo& char_info = m_pTextPage->GetCharInfo(pos);
@@ -130,7 +130,7 @@
       continue;
     }
 
-    int nCount = pos - start;
+    size_t nCount = pos - start;
     if (pos == nTotalChar - 1) {
       ++nCount;
     } else if (bAfterHyphen &&
@@ -163,13 +163,12 @@
       // Check for potential web URLs and email addresses.
       // Ftp address, file system links, data, blob etc. are not checked.
       if (nCount > 5) {
-        int32_t nStartOffset;
-        int32_t nCountOverload;
-        if (CheckWebLink(&strBeCheck, &nStartOffset, &nCountOverload)) {
-          m_LinkArray.push_back(
-              {start + nStartOffset, nCountOverload, strBeCheck});
+        auto maybe_link = CheckWebLink(strBeCheck);
+        if (maybe_link.has_value()) {
+          maybe_link.value().m_Start += start;
+          m_LinkArray.push_back(maybe_link.value());
         } else if (CheckMailLink(&strBeCheck)) {
-          m_LinkArray.push_back({start, nCount, strBeCheck});
+          m_LinkArray.push_back(Link{{start, nCount}, strBeCheck});
         }
       }
     }
@@ -177,36 +176,34 @@
   }
 }
 
-bool CPDF_LinkExtract::CheckWebLink(WideString* strBeCheck,
-                                    int32_t* nStart,
-                                    int32_t* nCount) {
+absl::optional<CPDF_LinkExtract::Link> CPDF_LinkExtract::CheckWebLink(
+    const WideString& strBeCheck) {
   static const wchar_t kHttpScheme[] = L"http";
   static const wchar_t kWWWAddrStart[] = L"www.";
 
   const size_t kHttpSchemeLen = FXSYS_len(kHttpScheme);
   const size_t kWWWAddrStartLen = FXSYS_len(kWWWAddrStart);
 
-  WideString str = *strBeCheck;
+  WideString str = strBeCheck;
   str.MakeLower();
 
-  size_t len = str.GetLength();
   // First, try to find the scheme.
   auto start = str.Find(kHttpScheme);
   if (start.has_value()) {
     size_t off = start.value() + kHttpSchemeLen;  // move after "http".
-    if (len > off + 4) {     // At least "://<char>" follows.
+    if (str.GetLength() > off + 4) {  // At least "://<char>" follows.
       if (str[off] == L's')  // "https" scheme is accepted.
         off++;
       if (str[off] == L':' && str[off + 1] == L'/' && str[off + 2] == L'/') {
         off += 3;
-        size_t end = TrimExternalBracketsFromWebLink(str, start.value(),
-                                                     str.GetLength() - 1);
-        end = FindWebLinkEnding(str, off, end);
+        const size_t end =
+            FindWebLinkEnding(str, off,
+                              TrimExternalBracketsFromWebLink(
+                                  str, start.value(), str.GetLength() - 1));
         if (end > off) {  // Non-empty host name.
-          *nStart = start.value();
-          *nCount = end - start.value() + 1;
-          *strBeCheck = strBeCheck->Substr(*nStart, *nCount);
-          return true;
+          const size_t nStart = start.value();
+          const size_t nCount = end - nStart + 1;
+          return Link{{nStart, nCount}, strBeCheck.Substr(nStart, nCount)};
         }
       }
     }
@@ -214,18 +211,23 @@
 
   // When there is no scheme, try to find url starting with "www.".
   start = str.Find(kWWWAddrStart);
-  if (start.has_value() && len > start.value() + kWWWAddrStartLen) {
-    size_t end = TrimExternalBracketsFromWebLink(str, start.value(),
-                                                 str.GetLength() - 1);
-    end = FindWebLinkEnding(str, start.value(), end);
-    if (end > start.value() + kWWWAddrStartLen) {
-      *nStart = start.value();
-      *nCount = end - start.value() + 1;
-      *strBeCheck = L"http://" + strBeCheck->Substr(*nStart, *nCount);
-      return true;
+  if (start.has_value()) {
+    size_t off = start.value() + kWWWAddrStartLen;
+    if (str.GetLength() > off) {
+      const size_t end =
+          FindWebLinkEnding(str, start.value(),
+                            TrimExternalBracketsFromWebLink(
+                                str, start.value(), str.GetLength() - 1));
+      if (end > off) {
+        const size_t nStart = start.value();
+        const size_t nCount = end - nStart + 1;
+        return Link{{nStart, nCount},
+                    L"http://" + strBeCheck.Substr(nStart, nCount)};
+      }
     }
   }
-  return false;
+
+  return absl::nullopt;
 }
 
 bool CPDF_LinkExtract::CheckMailLink(WideString* str) {
@@ -309,12 +311,9 @@
                                    m_LinkArray[index].m_Count);
 }
 
-bool CPDF_LinkExtract::GetTextRange(size_t index,
-                                    int* start_char_index,
-                                    int* char_count) const {
+absl::optional<CPDF_LinkExtract::Range> CPDF_LinkExtract::GetTextRange(
+    size_t index) const {
   if (index >= m_LinkArray.size())
-    return false;
-  *start_char_index = m_LinkArray[index].m_Start;
-  *char_count = m_LinkArray[index].m_Count;
-  return true;
+    return absl::nullopt;
+  return m_LinkArray[index];
 }
diff --git a/core/fpdftext/cpdf_linkextract.h b/core/fpdftext/cpdf_linkextract.h
index 56d70a3..3f0fa61 100644
--- a/core/fpdftext/cpdf_linkextract.h
+++ b/core/fpdftext/cpdf_linkextract.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,25 @@
 #ifndef CORE_FPDFTEXT_CPDF_LINKEXTRACT_H_
 #define CORE_FPDFTEXT_CPDF_LINKEXTRACT_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <vector>
 
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CPDF_TextPage;
 
 class CPDF_LinkExtract {
  public:
+  struct Range {
+    size_t m_Start;
+    size_t m_Count;
+  };
+
   explicit CPDF_LinkExtract(const CPDF_TextPage* pTextPage);
   ~CPDF_LinkExtract();
 
@@ -24,19 +33,16 @@
   size_t CountLinks() const { return m_LinkArray.size(); }
   WideString GetURL(size_t index) const;
   std::vector<CFX_FloatRect> GetRects(size_t index) const;
-  bool GetTextRange(size_t index, int* start_char_index, int* char_count) const;
+  absl::optional<Range> GetTextRange(size_t index) const;
 
  protected:
-  bool CheckWebLink(WideString* str, int32_t* nStart, int32_t* nCount);
-  bool CheckMailLink(WideString* str);
-
- private:
-  struct Link {
-    int m_Start;
-    int m_Count;
+  struct Link : public Range {
     WideString m_strUrl;
   };
 
+  absl::optional<Link> CheckWebLink(const WideString& str);
+  bool CheckMailLink(WideString* str);
+
   UnownedPtr<const CPDF_TextPage> const m_pTextPage;
   std::vector<Link> m_LinkArray;
 };
diff --git a/core/fpdftext/cpdf_linkextract_unittest.cpp b/core/fpdftext/cpdf_linkextract_unittest.cpp
index f4bd197..a0c69d6 100644
--- a/core/fpdftext/cpdf_linkextract_unittest.cpp
+++ b/core/fpdftext/cpdf_linkextract_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -21,7 +21,7 @@
 TEST(CPDF_LinkExtractTest, CheckMailLink) {
   CPDF_TestLinkExtract extractor;
   // Check cases that fail to extract valid mail link.
-  const wchar_t* const invalid_strs[] = {
+  const wchar_t* const kInvalidStrings[] = {
       L"",
       L"peter.pan",       // '@' is required.
       L"abc@server",      // Domain name needs at least one '.'.
@@ -30,15 +30,14 @@
       L"abc@.xyz.org",    // Domain name should not start with '.'.
       L"fan@g..com"       // Domain name should not have consecutive '.'
   };
-  for (size_t i = 0; i < FX_ArraySize(invalid_strs); ++i) {
-    const wchar_t* const input = invalid_strs[i];
+  for (const wchar_t* input : kInvalidStrings) {
     WideString text_str(input);
     EXPECT_FALSE(extractor.CheckMailLink(&text_str)) << input;
   }
 
   // Check cases that can extract valid mail link.
   // An array of {input_string, expected_extracted_email_address}.
-  const wchar_t* const valid_strs[][2] = {
+  const wchar_t* const kValidStrings[][2] = {
       {L"peter@abc.d", L"peter@abc.d"},
       {L"red.teddy.b@abc.com", L"red.teddy.b@abc.com"},
       {L"abc_@gmail.com", L"abc_@gmail.com"},  // '_' is ok before '@'.
@@ -51,11 +50,11 @@
       {L"fan@g.com..", L"fan@g.com"},           // Trim the ending periods.
       {L"CAP.cap@Gmail.Com", L"CAP.cap@Gmail.Com"},  // Keep the original case.
   };
-  for (size_t i = 0; i < FX_ArraySize(valid_strs); ++i) {
-    const wchar_t* const input = valid_strs[i][0];
+  for (const auto& it : kValidStrings) {
+    const wchar_t* const input = it[0];
     WideString text_str(input);
     WideString expected_str(L"mailto:");
-    expected_str += valid_strs[i][1];
+    expected_str += it[1];
     EXPECT_TRUE(extractor.CheckMailLink(&text_str)) << input;
     EXPECT_STREQ(expected_str.c_str(), text_str.c_str());
   }
@@ -65,8 +64,11 @@
   CPDF_TestLinkExtract extractor;
   // Check cases that fail to extract valid web link.
   // The last few are legit web addresses that we don't handle now.
-  const wchar_t* const invalid_cases[] = {
-      L"", L"http", L"www.", L"https-and-www",
+  const wchar_t* const kInvalidCases[] = {
+      L"",                          // Blank.
+      L"http",                      // No colon.
+      L"www.",                      // Missing domain.
+      L"https-and-www",             // Dash not colon.
       L"http:/abc.com",             // Missing slash.
       L"http://((()),",             // Only invalid chars in host name.
       L"ftp://example.com",         // Ftp scheme is not supported.
@@ -74,19 +76,11 @@
       L"http//[example.com",        // Invalid IPv6 address.
       L"http//[00:00:00:00:00:00",  // Invalid IPv6 address.
       L"http//[]",                  // Empty IPv6 address.
-      // Web addresses that in correct format that we don't handle.
-      L"abc.example.com",  // URL without scheme.
+      L"abc.example.com",           // URL without scheme.
   };
-  const int32_t DEFAULT_VALUE = -42;
-  for (size_t i = 0; i < FX_ArraySize(invalid_cases); ++i) {
-    const wchar_t* const input = invalid_cases[i];
-    WideString text_str(input);
-    int32_t start_offset = DEFAULT_VALUE;
-    int32_t count = DEFAULT_VALUE;
-    EXPECT_FALSE(extractor.CheckWebLink(&text_str, &start_offset, &count))
-        << input;
-    EXPECT_EQ(DEFAULT_VALUE, start_offset) << input;
-    EXPECT_EQ(DEFAULT_VALUE, count) << input;
+  for (const wchar_t* input : kInvalidCases) {
+    auto maybe_link = extractor.CheckWebLink(input);
+    EXPECT_FALSE(maybe_link.has_value()) << input;
   }
 
   // Check cases that can extract valid web link.
@@ -94,10 +88,10 @@
   struct ValidCase {
     const wchar_t* const input_string;
     const wchar_t* const url_extracted;
-    const int32_t start_offset;
-    const int32_t count;
+    const size_t start_offset;
+    const size_t count;
   };
-  const ValidCase valid_cases[] = {
+  const ValidCase kValidCases[] = {
       {L"http://www.example.com", L"http://www.example.com", 0,
        22},  // standard URL.
       {L"http://www.example.com:88", L"http://www.example.com:88", 0,
@@ -173,15 +167,11 @@
       {L"www.测试。net。", L"http://www.测试。net。", 0, 11},
       {L"www.测试.net;", L"http://www.测试.net;", 0, 11},
   };
-  for (size_t i = 0; i < FX_ArraySize(valid_cases); ++i) {
-    const wchar_t* const input = valid_cases[i].input_string;
-    WideString text_str(input);
-    int32_t start_offset = DEFAULT_VALUE;
-    int32_t count = DEFAULT_VALUE;
-    EXPECT_TRUE(extractor.CheckWebLink(&text_str, &start_offset, &count))
-        << input;
-    EXPECT_STREQ(valid_cases[i].url_extracted, text_str.c_str());
-    EXPECT_EQ(valid_cases[i].start_offset, start_offset) << input;
-    EXPECT_EQ(valid_cases[i].count, count) << input;
+  for (const auto& it : kValidCases) {
+    auto maybe_link = extractor.CheckWebLink(it.input_string);
+    ASSERT_TRUE(maybe_link.has_value()) << it.input_string;
+    EXPECT_STREQ(it.url_extracted, maybe_link.value().m_strUrl.c_str());
+    EXPECT_EQ(it.start_offset, maybe_link.value().m_Start) << it.input_string;
+    EXPECT_EQ(it.count, maybe_link.value().m_Count) << it.input_string;
   }
 }
diff --git a/core/fpdftext/cpdf_textpage.cpp b/core/fpdftext/cpdf_textpage.cpp
index 58a2959..99f3365 100644
--- a/core/fpdftext/cpdf_textpage.cpp
+++ b/core/fpdftext/cpdf_textpage.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,11 +6,14 @@
 
 #include "core/fpdftext/cpdf_textpage.h"
 
+#include <math.h>
+#include <stdint.h>
+
 #include <algorithm>
-#include <memory>
 #include <utility>
 #include <vector>
 
+#include "core/fpdfapi/font/cpdf_cidfont.h"
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/page/cpdf_form.h"
 #include "core/fpdfapi/page/cpdf_formobject.h"
@@ -20,24 +23,26 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdftext/unicodenormalizationdata.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_bidi.h"
 #include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_unicode.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 
 namespace {
 
 constexpr float kDefaultFontSize = 1.0f;
 constexpr float kSizeEpsilon = 0.01f;
 
-const uint16_t* const g_UnicodeData_Normalization_Maps[] = {
-    g_UnicodeData_Normalization_Map2, g_UnicodeData_Normalization_Map3,
-    g_UnicodeData_Normalization_Map4};
+const uint16_t* const kUnicodeDataNormalizationMaps[] = {
+    kUnicodeDataNormalizationMap2, kUnicodeDataNormalizationMap3,
+    kUnicodeDataNormalizationMap4};
 
 float NormalizeThreshold(float threshold, int t1, int t2, int t3) {
-  ASSERT(t1 < t2);
-  ASSERT(t2 < t3);
+  DCHECK(t1 < t2);
+  DCHECK(t2 < t3);
   if (threshold < t1)
     return threshold / 2.0f;
   if (threshold < t2)
@@ -58,8 +63,7 @@
       matrix.TransformDistance(pTextObj->m_TextState.GetCharSpace());
   float baseSpace = spacing;
   for (size_t i = 0; i < nItems; ++i) {
-    CPDF_TextObjectItem item;
-    pTextObj->GetItemInfo(i, &item);
+    CPDF_TextObject::Item item = pTextObj->GetItemInfo(i);
     if (item.m_CharCode == 0xffffffff) {
       float fontsize_h = pTextObj->m_TextState.GetFontSizeH();
       float kerning = -fontsize_h * item.m_Origin.x / 1000;
@@ -73,33 +77,24 @@
   return baseSpace;
 }
 
-size_t Unicode_GetNormalization(wchar_t wch, wchar_t* pDst) {
+DataVector<wchar_t> GetUnicodeNormalization(wchar_t wch) {
   wch = wch & 0xFFFF;
-  wchar_t wFind = g_UnicodeData_Normalization[wch];
-  if (!wFind) {
-    if (pDst)
-      *pDst = wch;
-    return 1;
-  }
+  wchar_t wFind = kUnicodeDataNormalization[wch];
+  if (!wFind)
+    return DataVector<wchar_t>(1, wch);
+
   if (wFind >= 0x8000) {
-    const uint16_t* pMap = g_UnicodeData_Normalization_Map1 + (wFind - 0x8000);
-    if (pDst)
-      *pDst = *pMap;
-    return 1;
+    return DataVector<wchar_t>(1,
+                               kUnicodeDataNormalizationMap1[wFind - 0x8000]);
   }
 
   wch = wFind & 0x0FFF;
   wFind >>= 12;
-  const uint16_t* pMap = g_UnicodeData_Normalization_Maps[wFind - 2] + wch;
+  const uint16_t* pMap = kUnicodeDataNormalizationMaps[wFind - 2] + wch;
   if (wFind == 4)
     wFind = static_cast<wchar_t>(*pMap++);
 
-  if (pDst) {
-    wchar_t n = wFind;
-    while (n--)
-      *pDst++ = *pMap++;
-  }
-  return static_cast<size_t>(wFind);
+  return DataVector<wchar_t>(pMap, pMap + wFind);
 }
 
 float MaskPercentFilled(const std::vector<bool>& mask,
@@ -143,8 +138,7 @@
   WideString str;
   str.Reserve(nItems);
   for (size_t i = 0; i < nItems; ++i) {
-    CPDF_TextObjectItem item;
-    text_obj.GetItemInfo(i, &item);
+    CPDF_TextObject::Item item = text_obj.GetItemInfo(i);
     if (item.m_CharCode == 0xffffffff)
       continue;
     WideString wstrItem = font.UnicodeFromCharCode(item.m_CharCode);
@@ -154,14 +148,15 @@
     if (wChar)
       str += wChar;
   }
-  return CFX_BidiString(str).OverallDirection() == CFX_BidiChar::RIGHT;
+  return CFX_BidiString(str).OverallDirection() ==
+         CFX_BidiChar::Direction::kRight;
 }
 
-uint32_t GetCharWidth(uint32_t charCode, CPDF_Font* pFont) {
+int GetCharWidth(uint32_t charCode, CPDF_Font* pFont) {
   if (charCode == CPDF_Font::kInvalidCharCode)
     return 0;
 
-  uint32_t w = pFont->GetCharWidthF(charCode);
+  int w = pFont->GetCharWidthF(charCode);
   if (w > 0)
     return w;
 
@@ -226,13 +221,59 @@
   return pPage->GetDisplayMatrix(rect, 0);
 }
 
+float GetFontSize(const CPDF_TextObject* text_object) {
+  bool has_font = text_object && text_object->GetFont();
+  return has_font ? text_object->GetFontSize() : kDefaultFontSize;
+}
+
+CFX_FloatRect GetLooseBounds(const CPDF_TextPage::CharInfo& charinfo) {
+  float font_size = GetFontSize(charinfo.m_pTextObj);
+  if (charinfo.m_pTextObj && !FXSYS_IsFloatZero(font_size)) {
+    bool is_vert_writing = charinfo.m_pTextObj->GetFont()->IsVertWriting();
+    if (is_vert_writing && charinfo.m_pTextObj->GetFont()->IsCIDFont()) {
+      CPDF_CIDFont* pCIDFont = charinfo.m_pTextObj->GetFont()->AsCIDFont();
+      uint16_t cid = pCIDFont->CIDFromCharCode(charinfo.m_CharCode);
+
+      CFX_Point16 vertical_origin = pCIDFont->GetVertOrigin(cid);
+      double offsetx = (vertical_origin.x - 500) * font_size / 1000.0;
+      double offsety = vertical_origin.y * font_size / 1000.0;
+      int16_t vert_width = pCIDFont->GetVertWidth(cid);
+      double height = vert_width * font_size / 1000.0;
+
+      float left = charinfo.m_Origin.x + offsetx;
+      float right = left + font_size;
+      float bottom = charinfo.m_Origin.y + offsety;
+      float top = bottom + height;
+      return CFX_FloatRect(left, bottom, right, top);
+    }
+
+    int ascent = charinfo.m_pTextObj->GetFont()->GetTypeAscent();
+    int descent = charinfo.m_pTextObj->GetFont()->GetTypeDescent();
+    if (ascent != descent) {
+      float width = charinfo.m_Matrix.a *
+                    charinfo.m_pTextObj->GetCharWidth(charinfo.m_CharCode);
+      float font_scale = charinfo.m_Matrix.a * font_size / (ascent - descent);
+
+      float left = charinfo.m_Origin.x;
+      float right = charinfo.m_Origin.x + (is_vert_writing ? -width : width);
+      float bottom = charinfo.m_Origin.y + descent * font_scale;
+      float top = charinfo.m_Origin.y + ascent * font_scale;
+      return CFX_FloatRect(left, bottom, right, top);
+    }
+  }
+
+  // Fallback to the tight bounds in empty text scenarios, or bad font metrics
+  return charinfo.m_CharBox;
+}
+
 }  // namespace
 
-PDFTEXT_Obj::PDFTEXT_Obj() {}
+CPDF_TextPage::TransformedTextObject::TransformedTextObject() = default;
 
-PDFTEXT_Obj::PDFTEXT_Obj(const PDFTEXT_Obj& that) = default;
+CPDF_TextPage::TransformedTextObject::TransformedTextObject(
+    const TransformedTextObject& that) = default;
 
-PDFTEXT_Obj::~PDFTEXT_Obj() {}
+CPDF_TextPage::TransformedTextObject::~TransformedTextObject() = default;
 
 CPDF_TextPage::CharInfo::CharInfo() = default;
 
@@ -253,100 +294,93 @@
 
   const int nCount = CountChars();
   if (nCount)
-    m_CharIndices.push_back(0);
+    m_CharIndices.push_back({0, 0});
 
+  bool skipped = false;
   for (int i = 0; i < nCount; ++i) {
     const CharInfo& charinfo = m_CharList[i];
     if (charinfo.m_CharType == CPDF_TextPage::CharType::kGenerated ||
         (charinfo.m_Unicode != 0 && !IsControlChar(charinfo)) ||
         (charinfo.m_Unicode == 0 && charinfo.m_CharCode != 0)) {
-      if (m_CharIndices.size() % 2) {
-        m_CharIndices.push_back(1);
-      } else {
-        if (m_CharIndices.empty())
-          continue;
-        m_CharIndices.back() += 1;
-      }
+      m_CharIndices.back().count++;
+      skipped = true;
     } else {
-      if (m_CharIndices.size() % 2) {
-        if (m_CharIndices.empty())
-          continue;
-        m_CharIndices.back() = i + 1;
+      if (skipped) {
+        m_CharIndices.push_back({i + 1, 0});
+        skipped = false;
       } else {
-        m_CharIndices.push_back(i + 1);
+        m_CharIndices.back().index = i + 1;
       }
     }
   }
-  if (m_CharIndices.size() % 2)
-    m_CharIndices.pop_back();
 }
 
 int CPDF_TextPage::CountChars() const {
-  return pdfium::CollectionSize<int>(m_CharList);
+  return fxcrt::CollectionSize<int>(m_CharList);
 }
 
 int CPDF_TextPage::CharIndexFromTextIndex(int text_index) const {
   int count = 0;
-  for (size_t i = 0; i < m_CharIndices.size(); i += 2) {
-    count += m_CharIndices[i + 1];
+  for (const auto& info : m_CharIndices) {
+    count += info.count;
     if (count > text_index)
-      return text_index - count + m_CharIndices[i + 1] + m_CharIndices[i];
+      return text_index - count + info.count + info.index;
   }
   return -1;
 }
 
 int CPDF_TextPage::TextIndexFromCharIndex(int char_index) const {
   int count = 0;
-  for (size_t i = 0; i < m_CharIndices.size(); i += 2) {
-    int text_index = char_index - m_CharIndices[i];
-    if (text_index < m_CharIndices[i + 1])
+  for (const auto& info : m_CharIndices) {
+    int text_index = char_index - info.index;
+    if (text_index < info.count)
       return text_index >= 0 ? text_index + count : -1;
 
-    count += m_CharIndices[i + 1];
+    count += info.count;
   }
   return -1;
 }
 
 std::vector<CFX_FloatRect> CPDF_TextPage::GetRectArray(int start,
-                                                       int nCount) const {
+                                                       int count) const {
   std::vector<CFX_FloatRect> rects;
-  if (start < 0 || nCount == 0)
+  if (start < 0 || count == 0)
     return rects;
 
-  const int nCharListSize = CountChars();
-  if (start >= nCharListSize)
+  const int number_of_chars = CountChars();
+  if (start >= number_of_chars)
     return rects;
 
-  if (nCount < 0 || start + nCount > nCharListSize)
-    nCount = nCharListSize - start;
-  ASSERT(nCount > 0);
+  if (count < 0 || start + count > number_of_chars)
+    count = number_of_chars - start;
+  DCHECK(count > 0);
 
-  CPDF_TextObject* pCurObj = nullptr;
+  const CPDF_TextObject* text_object = nullptr;
   CFX_FloatRect rect;
-  int curPos = start;
-  bool bFlagNewRect = true;
-  while (nCount--) {
-    const CharInfo& info_curchar = m_CharList[curPos++];
-    if (info_curchar.m_CharType == CPDF_TextPage::CharType::kGenerated)
+  int pos = start;
+  bool is_new_rect = true;
+  while (count--) {
+    const CharInfo& charinfo = m_CharList[pos++];
+    if (charinfo.m_CharType == CPDF_TextPage::CharType::kGenerated)
       continue;
-    if (info_curchar.m_CharBox.Width() < kSizeEpsilon ||
-        info_curchar.m_CharBox.Height() < kSizeEpsilon) {
+    if (charinfo.m_CharBox.Width() < kSizeEpsilon ||
+        charinfo.m_CharBox.Height() < kSizeEpsilon) {
       continue;
     }
-    if (!pCurObj)
-      pCurObj = info_curchar.m_pTextObj.Get();
-    if (pCurObj != info_curchar.m_pTextObj) {
+    if (!text_object)
+      text_object = charinfo.m_pTextObj;
+    if (text_object != charinfo.m_pTextObj) {
       rects.push_back(rect);
-      pCurObj = info_curchar.m_pTextObj.Get();
-      bFlagNewRect = true;
+      text_object = charinfo.m_pTextObj;
+      is_new_rect = true;
     }
-    if (bFlagNewRect) {
-      bFlagNewRect = false;
-      rect = info_curchar.m_CharBox;
+    if (is_new_rect) {
+      is_new_rect = false;
+      rect = charinfo.m_CharBox;
       rect.Normalize();
       continue;
     }
-    rect.Union(info_curchar.m_CharBox);
+    rect.Union(charinfo.m_CharBox);
   }
   rects.push_back(rect);
   return rects;
@@ -441,9 +475,11 @@
 
 float CPDF_TextPage::GetCharFontSize(size_t index) const {
   CHECK(index < m_CharList.size());
-  const CPDF_TextObject* text_object = m_CharList[index].m_pTextObj.Get();
-  bool has_font = text_object && text_object->GetFont();
-  return has_font ? text_object->GetFontSize() : kDefaultFontSize;
+  return GetFontSize(m_CharList[index].m_pTextObj);
+}
+
+CFX_FloatRect CPDF_TextPage::GetCharLooseBounds(size_t index) const {
+  return GetLooseBounds(GetCharInfo(index));
 }
 
 WideString CPDF_TextPage::GetPageText(int start, int count) const {
@@ -494,11 +530,11 @@
     return -1;
 
   m_SelRects = GetRectArray(start, nCount);
-  return pdfium::CollectionSize<int>(m_SelRects);
+  return fxcrt::CollectionSize<int>(m_SelRects);
 }
 
 bool CPDF_TextPage::GetRect(int rectIndex, CFX_FloatRect* pRect) const {
-  if (!pdfium::IndexInBounds(m_SelRects, rectIndex))
+  if (!fxcrt::IndexInBounds(m_SelRects, rectIndex))
     return false;
 
   *pRect = m_SelRects[rectIndex];
@@ -507,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());
@@ -525,13 +561,14 @@
     if (!pPageObj->IsText())
       continue;
 
-    int32_t minH = std::max(static_cast<int32_t>(pPageObj->GetRect().left), 0);
-    int32_t maxH =
-        std::min(static_cast<int32_t>(pPageObj->GetRect().right), nPageWidth);
-    int32_t minV =
-        std::max(static_cast<int32_t>(pPageObj->GetRect().bottom), 0);
-    int32_t maxV =
-        std::min(static_cast<int32_t>(pPageObj->GetRect().top), nPageHeight);
+    int32_t minH = static_cast<int32_t>(
+        std::clamp<float>(pPageObj->GetRect().left, 0.0f, nPageWidth));
+    int32_t maxH = static_cast<int32_t>(
+        std::clamp<float>(pPageObj->GetRect().right, 0.0f, nPageWidth));
+    int32_t minV = static_cast<int32_t>(
+        std::clamp<float>(pPageObj->GetRect().bottom, 0.0f, nPageHeight));
+    int32_t maxV = static_cast<int32_t>(
+        std::clamp<float>(pPageObj->GetRect().top, 0.0f, nPageHeight));
     if (minH >= maxH || minV >= maxV)
       continue;
 
@@ -568,14 +605,14 @@
 
 void CPDF_TextPage::AppendGeneratedCharacter(wchar_t unicode,
                                              const CFX_Matrix& formMatrix) {
-  Optional<CharInfo> pGenerateChar = GenerateCharInfo(unicode);
-  if (!pGenerateChar)
+  absl::optional<CharInfo> pGenerateChar = GenerateCharInfo(unicode);
+  if (!pGenerateChar.has_value())
     return;
 
   m_TextBuf.AppendChar(unicode);
   if (!formMatrix.IsIdentity())
     pGenerateChar->m_Matrix = formMatrix;
-  m_CharList.push_back(*pGenerateChar);
+  m_CharList.push_back(pGenerateChar.value());
 }
 
 void CPDF_TextPage::ProcessObject() {
@@ -590,14 +627,14 @@
 
     CFX_Matrix matrix;
     if (pObj->IsText())
-      ProcessTextObject(pObj->AsText(), matrix, m_pPage.Get(), it);
+      ProcessTextObject(pObj->AsText(), matrix, m_pPage, it);
     else if (pObj->IsForm())
       ProcessFormObject(pObj->AsForm(), matrix);
   }
-  for (const auto& obj : m_LineObj)
+  for (const auto& obj : mTextObjects)
     ProcessTextObject(obj);
 
-  m_LineObj.clear();
+  mTextObjects.clear();
   CloseTempLine();
 }
 
@@ -625,21 +662,17 @@
     m_CharList.push_back(info2);
     return;
   }
-
   info2.m_Index = m_TextBuf.GetLength();
-  size_t nCount = 0;
+  DataVector<wchar_t> normalized;
   if (wChar >= 0xFB00 && wChar <= 0xFB06)
-    nCount = Unicode_GetNormalization(wChar, nullptr);
-  if (nCount == 0) {
+    normalized = GetUnicodeNormalization(wChar);
+  if (normalized.empty()) {
     m_TextBuf.AppendChar(wChar);
     m_CharList.push_back(info2);
     return;
   }
-
-  std::unique_ptr<wchar_t, FxFreeDeleter> pDst(FX_Alloc(wchar_t, nCount));
-  Unicode_GetNormalization(wChar, pDst.get());
-  for (size_t nIndex = 0; nIndex < nCount; ++nIndex) {
-    info2.m_Unicode = pDst.get()[nIndex];
+  for (wchar_t normalized_char : normalized) {
+    info2.m_Unicode = normalized_char;
     info2.m_CharType = CPDF_TextPage::CharType::kPiece;
     m_TextBuf.AppendChar(info2.m_Unicode);
     m_CharList.push_back(info2);
@@ -654,21 +687,17 @@
     m_CharList.push_back(info2);
     return;
   }
-
   info2.m_Index = m_TextBuf.GetLength();
-  wChar = FX_GetMirrorChar(wChar);
-  size_t nCount = Unicode_GetNormalization(wChar, nullptr);
-  if (nCount == 0) {
+  wChar = pdfium::unicode::GetMirrorChar(wChar);
+  DataVector<wchar_t> normalized = GetUnicodeNormalization(wChar);
+  if (normalized.empty()) {
     info2.m_Unicode = wChar;
     m_TextBuf.AppendChar(info2.m_Unicode);
     m_CharList.push_back(info2);
     return;
   }
-
-  std::unique_ptr<wchar_t, FxFreeDeleter> pDst(FX_Alloc(wchar_t, nCount));
-  Unicode_GetNormalization(wChar, pDst.get());
-  for (size_t nIndex = 0; nIndex < nCount; ++nIndex) {
-    info2.m_Unicode = pDst.get()[nIndex];
+  for (wchar_t normalized_char : normalized) {
+    info2.m_Unicode = normalized_char;
     info2.m_CharType = CPDF_TextPage::CharType::kPiece;
     m_TextBuf.AppendChar(info2.m_Unicode);
     m_CharList.push_back(info2);
@@ -699,14 +728,16 @@
     bidi.SetOverallDirectionRight();
   CFX_BidiChar::Direction eCurrentDirection = bidi.OverallDirection();
   for (const auto& segment : bidi) {
-    if (segment.direction == CFX_BidiChar::RIGHT ||
-        (segment.direction == CFX_BidiChar::NEUTRAL &&
-         eCurrentDirection == CFX_BidiChar::RIGHT)) {
-      eCurrentDirection = CFX_BidiChar::RIGHT;
+    if (segment.direction == CFX_BidiChar::Direction::kRight ||
+        (segment.direction == CFX_BidiChar::Direction::kNeutral &&
+         eCurrentDirection == CFX_BidiChar::Direction::kRight)) {
+      eCurrentDirection = CFX_BidiChar::Direction::kRight;
       for (int m = segment.start + segment.count; m > segment.start; --m)
         AddCharInfoByRLDirection(str[m - 1], m_TempCharList[m - 1]);
     } else {
-      eCurrentDirection = CFX_BidiChar::LEFT;
+      if (segment.direction != CFX_BidiChar::Direction::kLeftWeak) {
+        eCurrentDirection = CFX_BidiChar::Direction::kLeft;
+      }
       for (int m = segment.start; m < segment.start + segment.count; ++m)
         AddCharInfoByLRDirection(str[m], m_TempCharList[m]);
     }
@@ -723,32 +754,31 @@
   if (fabs(pTextObj->GetRect().Width()) < kSizeEpsilon)
     return;
 
-  size_t count = m_LineObj.size();
-  PDFTEXT_Obj Obj;
-  Obj.m_pTextObj = pTextObj;
-  Obj.m_formMatrix = formMatrix;
+  size_t count = mTextObjects.size();
+  TransformedTextObject new_obj;
+  new_obj.m_pTextObj = pTextObj;
+  new_obj.m_formMatrix = formMatrix;
   if (count == 0) {
-    m_LineObj.push_back(Obj);
+    mTextObjects.push_back(new_obj);
     return;
   }
   if (IsSameAsPreTextObject(pTextObj, pObjList, ObjPos))
     return;
 
-  PDFTEXT_Obj prev_Obj = m_LineObj[count - 1];
-  size_t nItem = prev_Obj.m_pTextObj->CountItems();
+  TransformedTextObject prev_obj = mTextObjects[count - 1];
+  size_t nItem = prev_obj.m_pTextObj->CountItems();
   if (nItem == 0)
     return;
 
-  CPDF_TextObjectItem item;
-  prev_Obj.m_pTextObj->GetItemInfo(nItem - 1, &item);
+  CPDF_TextObject::Item item = prev_obj.m_pTextObj->GetItemInfo(nItem - 1);
   float prev_width =
-      GetCharWidth(item.m_CharCode, prev_Obj.m_pTextObj->GetFont().Get()) *
-      prev_Obj.m_pTextObj->GetFontSize() / 1000;
+      GetCharWidth(item.m_CharCode, prev_obj.m_pTextObj->GetFont().Get()) *
+      prev_obj.m_pTextObj->GetFontSize() / 1000;
 
   CFX_Matrix prev_matrix =
-      prev_Obj.m_pTextObj->GetTextMatrix() * prev_Obj.m_formMatrix;
+      prev_obj.m_pTextObj->GetTextMatrix() * prev_obj.m_formMatrix;
   prev_width = prev_matrix.TransformDistance(fabs(prev_width));
-  pTextObj->GetItemInfo(0, &item);
+  item = pTextObj->GetItemInfo(0);
   float this_width = GetCharWidth(item.m_CharCode, pTextObj->GetFont().Get()) *
                      pTextObj->GetFontSize() / 1000;
   this_width = fabs(this_width);
@@ -758,46 +788,46 @@
 
   float threshold = std::max(prev_width, this_width) / 4;
   CFX_PointF prev_pos = m_DisplayMatrix.Transform(
-      prev_Obj.m_formMatrix.Transform(prev_Obj.m_pTextObj->GetPos()));
+      prev_obj.m_formMatrix.Transform(prev_obj.m_pTextObj->GetPos()));
   CFX_PointF this_pos =
       m_DisplayMatrix.Transform(formMatrix.Transform(pTextObj->GetPos()));
   if (fabs(this_pos.y - prev_pos.y) > threshold * 2) {
     for (size_t i = 0; i < count; ++i)
-      ProcessTextObject(m_LineObj[i]);
-    m_LineObj.clear();
-    m_LineObj.push_back(Obj);
+      ProcessTextObject(mTextObjects[i]);
+    mTextObjects.clear();
+    mTextObjects.push_back(new_obj);
     return;
   }
 
   for (size_t i = count; i > 0; --i) {
-    PDFTEXT_Obj prev_text_obj = m_LineObj[i - 1];
+    TransformedTextObject prev_text_obj = mTextObjects[i - 1];
     CFX_PointF new_prev_pos =
         m_DisplayMatrix.Transform(prev_text_obj.m_formMatrix.Transform(
             prev_text_obj.m_pTextObj->GetPos()));
     if (this_pos.x >= new_prev_pos.x) {
-      m_LineObj.insert(m_LineObj.begin() + i, Obj);
+      mTextObjects.insert(mTextObjects.begin() + i, new_obj);
       return;
     }
   }
-  m_LineObj.insert(m_LineObj.begin(), Obj);
+  mTextObjects.insert(mTextObjects.begin(), new_obj);
 }
 
 CPDF_TextPage::MarkedContentState CPDF_TextPage::PreMarkedContent(
-    PDFTEXT_Obj Obj) {
-  CPDF_TextObject* pTextObj = Obj.m_pTextObj.Get();
-  size_t nContentMarks = pTextObj->m_ContentMarks.CountItems();
+    const CPDF_TextObject* pTextObj) {
+  const CPDF_ContentMarks* pMarks = pTextObj->GetContentMarks();
+  const size_t nContentMarks = pMarks->CountItems();
   if (nContentMarks == 0)
     return MarkedContentState::kPass;
 
   WideString actText;
   bool bExist = false;
-  const CPDF_Dictionary* pDict = nullptr;
+  RetainPtr<const CPDF_Dictionary> pDict;
   for (size_t i = 0; i < nContentMarks; ++i) {
-    const CPDF_ContentMarkItem* item = pTextObj->m_ContentMarks.GetItem(i);
+    const CPDF_ContentMarkItem* item = pMarks->GetItem(i);
     pDict = item->GetParam();
     if (!pDict)
       continue;
-    const CPDF_String* temp = ToString(pDict->GetObjectFor("ActualText"));
+    RetainPtr<const CPDF_String> temp = pDict->GetStringFor("ActualText");
     if (temp) {
       bExist = true;
       actText = temp->GetUnicodeText();
@@ -807,9 +837,9 @@
     return MarkedContentState::kPass;
 
   if (m_pPrevTextObj) {
-    const CPDF_ContentMarks& marks = m_pPrevTextObj->m_ContentMarks;
-    if (marks.CountItems() == nContentMarks &&
-        marks.GetItem(nContentMarks - 1)->GetParam() == pDict) {
+    const CPDF_ContentMarks* pPrevMarks = m_pPrevTextObj->GetContentMarks();
+    if (pPrevMarks->CountItems() == nContentMarks &&
+        pPrevMarks->GetItem(nContentMarks - 1)->GetParam() == pDict) {
       return MarkedContentState::kDone;
     }
   }
@@ -842,17 +872,14 @@
   return MarkedContentState::kDelay;
 }
 
-void CPDF_TextPage::ProcessMarkedContent(PDFTEXT_Obj Obj) {
-  CPDF_TextObject* pTextObj = Obj.m_pTextObj.Get();
-
-  size_t nContentMarks = pTextObj->m_ContentMarks.CountItems();
-  if (nContentMarks == 0)
-    return;
-
+void CPDF_TextPage::ProcessMarkedContent(const TransformedTextObject& obj) {
+  const CPDF_TextObject* pTextObj = obj.m_pTextObj;
+  const CPDF_ContentMarks* pMarks = pTextObj->GetContentMarks();
+  const size_t nContentMarks = pMarks->CountItems();
   WideString actText;
   for (size_t n = 0; n < nContentMarks; ++n) {
-    const CPDF_ContentMarkItem* item = pTextObj->m_ContentMarks.GetItem(n);
-    const CPDF_Dictionary* pDict = item->GetParam();
+    const CPDF_ContentMarkItem* item = pMarks->GetItem(n);
+    RetainPtr<const CPDF_Dictionary> pDict = item->GetParam();
     if (pDict)
       actText = pDict->GetUnicodeTextFor("ActualText");
   }
@@ -860,7 +887,7 @@
     return;
 
   RetainPtr<CPDF_Font> pFont = pTextObj->GetFont();
-  CFX_Matrix matrix = pTextObj->GetTextMatrix() * Obj.m_formMatrix;
+  CFX_Matrix matrix = pTextObj->GetTextMatrix() * obj.m_formMatrix;
 
   for (size_t k = 0; k < actText.GetLength(); ++k) {
     wchar_t wChar = actText[k];
@@ -892,67 +919,69 @@
     m_pPrevTextObj = pPrevCharInfo->m_pTextObj;
 }
 
-void CPDF_TextPage::SwapTempTextBuf(int32_t iCharListStartAppend,
-                                    int32_t iBufStartAppend) {
-  int32_t i = iCharListStartAppend;
-  int32_t j = pdfium::CollectionSize<int32_t>(m_TempCharList) - 1;
-  for (; i < j; ++i, --j) {
-    std::swap(m_TempCharList[i], m_TempCharList[j]);
-    std::swap(m_TempCharList[i].m_Index, m_TempCharList[j].m_Index);
+void CPDF_TextPage::SwapTempTextBuf(size_t iCharListStartAppend,
+                                    size_t iBufStartAppend) {
+  DCHECK(!m_TempCharList.empty());
+  if (iCharListStartAppend < m_TempCharList.size()) {
+    auto fwd = m_TempCharList.begin() + iCharListStartAppend;
+    auto rev = m_TempCharList.end() - 1;
+    for (; fwd < rev; ++fwd, --rev) {
+      std::swap(*fwd, *rev);
+      std::swap(fwd->m_Index, rev->m_Index);
+    }
   }
-  wchar_t* pTempBuffer = m_TempTextBuf.GetBuffer();
-  i = iBufStartAppend;
-  j = m_TempTextBuf.GetLength() - 1;
-  for (; i < j; ++i, --j)
-    std::swap(pTempBuffer[i], pTempBuffer[j]);
+  pdfium::span<wchar_t> temp_span = m_TempTextBuf.GetWideSpan();
+  DCHECK(!temp_span.empty());
+  if (iBufStartAppend < temp_span.size()) {
+    std::reverse(temp_span.begin() + iBufStartAppend, temp_span.end());
+  }
 }
 
-void CPDF_TextPage::ProcessTextObject(PDFTEXT_Obj Obj) {
-  CPDF_TextObject* pTextObj = Obj.m_pTextObj.Get();
+void CPDF_TextPage::ProcessTextObject(const TransformedTextObject& obj) {
+  const CPDF_TextObject* pTextObj = obj.m_pTextObj;
   if (fabs(pTextObj->GetRect().Width()) < kSizeEpsilon)
     return;
 
-  CFX_Matrix formMatrix = Obj.m_formMatrix;
+  CFX_Matrix form_matrix = obj.m_formMatrix;
   RetainPtr<CPDF_Font> pFont = pTextObj->GetFont();
-  CFX_Matrix matrix = pTextObj->GetTextMatrix() * formMatrix;
-  MarkedContentState ePreMKC = PreMarkedContent(Obj);
+  CFX_Matrix matrix = pTextObj->GetTextMatrix() * form_matrix;
+  MarkedContentState ePreMKC = PreMarkedContent(obj.m_pTextObj);
   if (ePreMKC == MarkedContentState::kDone) {
     m_pPrevTextObj = pTextObj;
-    m_PrevMatrix = formMatrix;
+    m_PrevMatrix = form_matrix;
     return;
   }
   GenerateCharacter result = GenerateCharacter::kNone;
   if (m_pPrevTextObj) {
-    result = ProcessInsertObject(pTextObj, formMatrix);
+    result = ProcessInsertObject(pTextObj, form_matrix);
     if (result == GenerateCharacter::kLineBreak)
-      m_CurlineRect = Obj.m_pTextObj->GetRect();
+      m_CurlineRect = pTextObj->GetRect();
     else
-      m_CurlineRect.Union(Obj.m_pTextObj->GetRect());
+      m_CurlineRect.Union(obj.m_pTextObj->GetRect());
 
     switch (result) {
       case GenerateCharacter::kNone:
         break;
       case GenerateCharacter::kSpace: {
-        Optional<CharInfo> pGenerateChar = GenerateCharInfo(L' ');
-        if (pGenerateChar) {
-          if (!formMatrix.IsIdentity())
-            pGenerateChar->m_Matrix = formMatrix;
+        absl::optional<CharInfo> pGenerateChar = GenerateCharInfo(L' ');
+        if (pGenerateChar.has_value()) {
+          if (!form_matrix.IsIdentity())
+            pGenerateChar->m_Matrix = form_matrix;
           m_TempTextBuf.AppendChar(L' ');
-          m_TempCharList.push_back(*pGenerateChar);
+          m_TempCharList.push_back(pGenerateChar.value());
         }
         break;
       }
       case GenerateCharacter::kLineBreak:
         CloseTempLine();
         if (m_TextBuf.GetSize()) {
-          AppendGeneratedCharacter(L'\r', formMatrix);
-          AppendGeneratedCharacter(L'\n', formMatrix);
+          AppendGeneratedCharacter(L'\r', form_matrix);
+          AppendGeneratedCharacter(L'\n', form_matrix);
         }
         break;
       case GenerateCharacter::kHyphen:
         if (pTextObj->CountChars() == 1) {
-          CPDF_TextObjectItem item;
-          pTextObj->GetCharInfo(0, &item);
+          CPDF_TextObject::Item item = pTextObj->GetCharInfo(0);
           WideString wstrItem =
               pTextObj->GetFont()->UnicodeFromCharCode(item.m_CharCode);
           if (wstrItem.IsEmpty())
@@ -974,32 +1003,30 @@
         break;
     }
   } else {
-    m_CurlineRect = Obj.m_pTextObj->GetRect();
+    m_CurlineRect = pTextObj->GetRect();
   }
 
   if (ePreMKC == MarkedContentState::kDelay) {
-    ProcessMarkedContent(Obj);
+    ProcessMarkedContent(obj);
     m_pPrevTextObj = pTextObj;
-    m_PrevMatrix = formMatrix;
+    m_PrevMatrix = form_matrix;
     return;
   }
   m_pPrevTextObj = pTextObj;
-  m_PrevMatrix = formMatrix;
+  m_PrevMatrix = form_matrix;
   float baseSpace = CalculateBaseSpace(pTextObj, matrix);
 
   const bool bR2L = IsRightToLeft(*pTextObj, *pFont);
   const bool bIsBidiAndMirrorInverse =
       bR2L && (matrix.a * matrix.d - matrix.b * matrix.c) < 0;
-  int32_t iBufStartAppend = m_TempTextBuf.GetLength();
-  int32_t iCharListStartAppend =
-      pdfium::CollectionSize<int32_t>(m_TempCharList);
+  const size_t iBufStartAppend = m_TempTextBuf.GetLength();
+  const size_t iCharListStartAppend = m_TempCharList.size();
 
   float spacing = 0;
   const size_t nItems = pTextObj->CountItems();
   for (size_t i = 0; i < nItems; ++i) {
-    CPDF_TextObjectItem item;
     CharInfo charinfo;
-    pTextObj->GetItemInfo(i, &item);
+    CPDF_TextObject::Item item = pTextObj->GetItemInfo(i);
     if (item.m_CharCode == 0xffffffff) {
       WideString str = m_TempTextBuf.MakeString();
       if (str.IsEmpty())
@@ -1039,7 +1066,7 @@
         charinfo.m_Index = m_TextBuf.GetLength();
         m_TempTextBuf.AppendChar(L' ');
         charinfo.m_CharCode = CPDF_Font::kInvalidCharCode;
-        charinfo.m_Matrix = formMatrix;
+        charinfo.m_Matrix = form_matrix;
         charinfo.m_Origin = matrix.Transform(item.m_Origin);
         charinfo.m_CharBox =
             CFX_FloatRect(charinfo.m_Origin.x, charinfo.m_Origin.y,
@@ -1072,8 +1099,7 @@
     charinfo.m_CharBox.bottom = rect.bottom * fFontSize + item.m_Origin.y;
     if (fabsf(charinfo.m_CharBox.top - charinfo.m_CharBox.bottom) <
         kSizeEpsilon) {
-      charinfo.m_CharBox.top =
-          charinfo.m_CharBox.bottom + pTextObj->GetFontSize();
+      charinfo.m_CharBox.top = charinfo.m_CharBox.bottom + fFontSize;
     }
     if (fabsf(charinfo.m_CharBox.right - charinfo.m_CharBox.left) <
         kSizeEpsilon) {
@@ -1088,14 +1114,14 @@
       m_TempTextBuf.AppendChar(0xfffe);
       continue;
     }
-    int nTotal = wstrItem.GetLength();
+    size_t nTotal = wstrItem.GetLength();
     bool bDel = false;
-    const int count = std::min(pdfium::CollectionSize<int>(m_TempCharList), 7);
+    const int count = std::min(fxcrt::CollectionSize<int>(m_TempCharList), 7);
     constexpr float kTextCharRatioGapDelta = 0.07f;
     float threshold = charinfo.m_Matrix.TransformXDistance(
         kTextCharRatioGapDelta * pTextObj->GetFontSize());
-    for (int n = pdfium::CollectionSize<int>(m_TempCharList);
-         n > pdfium::CollectionSize<int>(m_TempCharList) - count; --n) {
+    for (int n = fxcrt::CollectionSize<int>(m_TempCharList);
+         n > fxcrt::CollectionSize<int>(m_TempCharList) - count; --n) {
       const CharInfo& charinfo1 = m_TempCharList[n - 1];
       CFX_PointF diff = charinfo1.m_Origin - charinfo.m_Origin;
       if (charinfo1.m_CharCode == charinfo.m_CharCode &&
@@ -1106,7 +1132,7 @@
       }
     }
     if (!bDel) {
-      for (int nIndex = 0; nIndex < nTotal; ++nIndex) {
+      for (size_t nIndex = 0; nIndex < nTotal; ++nIndex) {
         charinfo.m_Unicode = wstrItem[nIndex];
         if (charinfo.m_Unicode) {
           charinfo.m_Index = m_TextBuf.GetLength();
@@ -1134,10 +1160,8 @@
   if (nChars <= 1)
     return m_TextlineDir;
 
-  CPDF_TextObjectItem first, last;
-  pTextObj->GetCharInfo(0, &first);
-  pTextObj->GetCharInfo(nChars - 1, &last);
-
+  CPDF_TextObject::Item first = pTextObj->GetCharInfo(0);
+  CPDF_TextObject::Item last = pTextObj->GetCharInfo(nChars - 1);
   CFX_Matrix textMatrix = pTextObj->GetTextMatrix();
   first.m_Origin = textMatrix.Transform(first.m_Origin);
   last.m_Origin = textMatrix.Transform(last.m_Origin);
@@ -1197,24 +1221,20 @@
   FindPreviousTextObject();
   TextOrientation WritingMode = GetTextObjectWritingMode(pObj);
   if (WritingMode == TextOrientation::kUnknown)
-    WritingMode = GetTextObjectWritingMode(m_pPrevTextObj.Get());
+    WritingMode = GetTextObjectWritingMode(m_pPrevTextObj);
 
   size_t nItem = m_pPrevTextObj->CountItems();
   if (nItem == 0)
     return GenerateCharacter::kNone;
 
-  CPDF_TextObjectItem PrevItem;
-  m_pPrevTextObj->GetItemInfo(nItem - 1, &PrevItem);
-
-  CPDF_TextObjectItem item;
-  pObj->GetItemInfo(0, &item);
-
+  CPDF_TextObject::Item PrevItem = m_pPrevTextObj->GetItemInfo(nItem - 1);
+  CPDF_TextObject::Item item = pObj->GetItemInfo(0);
   const CFX_FloatRect& this_rect = pObj->GetRect();
   const CFX_FloatRect& prev_rect = m_pPrevTextObj->GetRect();
-
   WideString wstrItem = pObj->GetFont()->UnicodeFromCharCode(item.m_CharCode);
   if (wstrItem.IsEmpty())
     wstrItem += static_cast<wchar_t>(item.m_CharCode);
+
   wchar_t curChar = wstrItem[0];
   if (WritingMode == TextOrientation::kHorizontal) {
     if (EndHorizontalLine(this_rect, prev_rect)) {
@@ -1230,11 +1250,11 @@
   }
 
   float last_pos = PrevItem.m_Origin.x;
-  uint32_t nLastWidth =
+  int nLastWidth =
       GetCharWidth(PrevItem.m_CharCode, m_pPrevTextObj->GetFont().Get());
   float last_width = nLastWidth * m_pPrevTextObj->GetFontSize() / 1000;
   last_width = fabs(last_width);
-  uint32_t nThisWidth = GetCharWidth(item.m_CharCode, pObj->GetFont().Get());
+  int nThisWidth = GetCharWidth(item.m_CharCode, pObj->GetFont().Get());
   float this_width = fabs(nThisWidth * pObj->GetFontSize() / 1000);
   float threshold = std::max(last_width, this_width) / 4;
 
@@ -1255,8 +1275,7 @@
          (fabs(pos.y) >= 1 || fabs(pos.y) > fabs(pos.x)))) {
       bNewline = true;
       if (nItem > 1) {
-        CPDF_TextObjectItem tempItem;
-        m_pPrevTextObj->GetItemInfo(0, &tempItem);
+        CPDF_TextObject::Item tempItem = m_pPrevTextObj->GetItemInfo(0);
         CFX_Matrix m = m_pPrevTextObj->GetTextMatrix();
         if (PrevItem.m_Origin.x > tempItem.m_Origin.x &&
             m_DisplayMatrix.a > 0.9 && m_DisplayMatrix.b < 0.1 &&
@@ -1349,11 +1368,11 @@
   if (nPreCount == 0)
     return true;
 
-  CPDF_TextObjectItem itemPer;
-  CPDF_TextObjectItem itemCur;
+  CPDF_TextObject::Item itemPer;
+  CPDF_TextObject::Item itemCur;
   for (size_t i = 0; i < nPreCount; ++i) {
-    pTextObj2->GetItemInfo(i, &itemPer);
-    pTextObj1->GetItemInfo(i, &itemCur);
+    itemPer = pTextObj2->GetItemInfo(i);
+    itemCur = pTextObj1->GetItemInfo(i);
     if (itemCur.m_CharCode != itemPer.m_CharCode)
       return false;
   }
@@ -1385,11 +1404,11 @@
   return false;
 }
 
-Optional<CPDF_TextPage::CharInfo> CPDF_TextPage::GenerateCharInfo(
+absl::optional<CPDF_TextPage::CharInfo> CPDF_TextPage::GenerateCharInfo(
     wchar_t unicode) {
   const CharInfo* pPrevCharInfo = GetPrevCharInfo();
   if (!pPrevCharInfo)
-    return {};
+    return absl::nullopt;
 
   CharInfo info;
   info.m_Index = m_TextBuf.GetLength();
diff --git a/core/fpdftext/cpdf_textpage.h b/core/fpdftext/cpdf_textpage.h
index bac7181..325e2a4 100644
--- a/core/fpdftext/cpdf_textpage.h
+++ b/core/fpdftext/cpdf_textpage.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,31 +7,32 @@
 #ifndef CORE_FPDFTEXT_CPDF_TEXTPAGE_H_
 #define CORE_FPDFTEXT_CPDF_TEXTPAGE_H_
 
+#include <stdint.h>
+
 #include <deque>
 #include <functional>
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_pageobjectholder.h"
-#include "core/fxcrt/cfx_widetextbuf.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/optional.h"
+#include "core/fxcrt/widestring.h"
+#include "core/fxcrt/widetext_buffer.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
-class CPDF_Font;
 class CPDF_FormObject;
 class CPDF_Page;
 class CPDF_TextObject;
 
-struct PDFTEXT_Obj {
-  PDFTEXT_Obj();
-  PDFTEXT_Obj(const PDFTEXT_Obj& that);
-  ~PDFTEXT_Obj();
-
-  UnownedPtr<CPDF_TextObject> m_pTextObj;
-  CFX_Matrix m_formMatrix;
+struct TextPageCharSegment {
+  int index;
+  int count;
 };
 
+FX_DATA_PARTITION_EXCEPTION(TextPageCharSegment);
+
 class CPDF_TextPage {
  public:
   enum class CharType : uint8_t {
@@ -54,7 +55,7 @@
     CharType m_CharType = CharType::kNormal;
     CFX_PointF m_Origin;
     CFX_FloatRect m_CharBox;
-    UnownedPtr<CPDF_TextObject> m_pTextObj;
+    UnownedPtr<const CPDF_TextObject> m_pTextObj;
     CFX_Matrix m_Matrix;
   };
 
@@ -69,8 +70,9 @@
   // These methods CHECK() to make sure |index| is within bounds.
   const CharInfo& GetCharInfo(size_t index) const;
   float GetCharFontSize(size_t index) const;
+  CFX_FloatRect GetCharLooseBounds(size_t index) const;
 
-  std::vector<CFX_FloatRect> GetRectArray(int start, int nCount) const;
+  std::vector<CFX_FloatRect> GetRectArray(int start, int count) const;
   int GetIndexAtPos(const CFX_PointF& point, const CFX_SizeF& tolerance) const;
   WideString GetTextByRect(const CFX_FloatRect& rect) const;
   WideString GetTextByObject(const CPDF_TextObject* pTextObj) const;
@@ -100,12 +102,21 @@
 
   enum class MarkedContentState { kPass = 0, kDone, kDelay };
 
+  struct TransformedTextObject {
+    TransformedTextObject();
+    TransformedTextObject(const TransformedTextObject& that);
+    ~TransformedTextObject();
+
+    UnownedPtr<const CPDF_TextObject> m_pTextObj;
+    CFX_Matrix m_formMatrix;
+  };
+
   void Init();
   bool IsHyphen(wchar_t curChar) const;
   void ProcessObject();
   void ProcessFormObject(CPDF_FormObject* pFormObj,
                          const CFX_Matrix& formMatrix);
-  void ProcessTextObject(PDFTEXT_Obj pObj);
+  void ProcessTextObject(const TransformedTextObject& obj);
   void ProcessTextObject(CPDF_TextObject* pTextObj,
                          const CFX_Matrix& formMatrix,
                          const CPDF_PageObjectHolder* pObjList,
@@ -113,15 +124,15 @@
   GenerateCharacter ProcessInsertObject(const CPDF_TextObject* pObj,
                                         const CFX_Matrix& formMatrix);
   const CharInfo* GetPrevCharInfo() const;
-  Optional<CharInfo> GenerateCharInfo(wchar_t unicode);
+  absl::optional<CharInfo> GenerateCharInfo(wchar_t unicode);
   bool IsSameAsPreTextObject(CPDF_TextObject* pTextObj,
                              const CPDF_PageObjectHolder* pObjList,
                              CPDF_PageObjectHolder::const_iterator iter) const;
   bool IsSameTextObject(CPDF_TextObject* pTextObj1,
                         CPDF_TextObject* pTextObj2) const;
   void CloseTempLine();
-  MarkedContentState PreMarkedContent(PDFTEXT_Obj pObj);
-  void ProcessMarkedContent(PDFTEXT_Obj pObj);
+  MarkedContentState PreMarkedContent(const CPDF_TextObject* pTextObj);
+  void ProcessMarkedContent(const TransformedTextObject& obj);
   void FindPreviousTextObject();
   void AddCharInfoByLRDirection(wchar_t wChar, const CharInfo& info);
   void AddCharInfoByRLDirection(wchar_t wChar, const CharInfo& info);
@@ -129,22 +140,22 @@
       const CPDF_TextObject* pTextObj) const;
   TextOrientation FindTextlineFlowOrientation() const;
   void AppendGeneratedCharacter(wchar_t unicode, const CFX_Matrix& formMatrix);
-  void SwapTempTextBuf(int32_t iCharListStartAppend, int32_t iBufStartAppend);
+  void SwapTempTextBuf(size_t iCharListStartAppend, size_t iBufStartAppend);
   WideString GetTextByPredicate(
       const std::function<bool(const CharInfo&)>& predicate) const;
 
   UnownedPtr<const CPDF_Page> const m_pPage;
-  std::vector<uint16_t> m_CharIndices;
+  DataVector<TextPageCharSegment> m_CharIndices;
   std::deque<CharInfo> m_CharList;
   std::deque<CharInfo> m_TempCharList;
-  CFX_WideTextBuf m_TextBuf;
-  CFX_WideTextBuf m_TempTextBuf;
-  UnownedPtr<CPDF_TextObject> m_pPrevTextObj;
+  WideTextBuffer m_TextBuf;
+  WideTextBuffer m_TempTextBuf;
+  UnownedPtr<const CPDF_TextObject> m_pPrevTextObj;
   CFX_Matrix m_PrevMatrix;
   const bool m_rtl;
   const CFX_Matrix m_DisplayMatrix;
   std::vector<CFX_FloatRect> m_SelRects;
-  std::vector<PDFTEXT_Obj> m_LineObj;
+  std::vector<TransformedTextObject> mTextObjects;
   TextOrientation m_TextlineDir = TextOrientation::kUnknown;
   CFX_FloatRect m_CurlineRect;
 };
diff --git a/core/fpdftext/cpdf_textpagefind.cpp b/core/fpdftext/cpdf_textpagefind.cpp
index 3df499f..4191748 100644
--- a/core/fpdftext/cpdf_textpagefind.cpp
+++ b/core/fpdftext/cpdf_textpagefind.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,16 +6,18 @@
 
 #include "core/fpdftext/cpdf_textpagefind.h"
 
-#include <cwchar>
-#include <cwctype>
+#include <wchar.h>
+
 #include <vector>
 
 #include "core/fpdftext/cpdf_textpage.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/fx_unicode.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/memory/ptr_util.h"
 
 namespace {
 
@@ -89,25 +91,25 @@
   return wsLower;
 }
 
-Optional<WideString> ExtractSubString(const wchar_t* lpszFullString,
-                                      int iSubString) {
-  ASSERT(lpszFullString);
+absl::optional<WideString> ExtractSubString(const wchar_t* lpszFullString,
+                                            int iSubString) {
+  DCHECK(lpszFullString);
 
   while (iSubString--) {
-    lpszFullString = std::wcschr(lpszFullString, L' ');
+    lpszFullString = wcschr(lpszFullString, L' ');
     if (!lpszFullString)
-      return {};
+      return absl::nullopt;
 
     lpszFullString++;
     while (*lpszFullString == L' ')
       lpszFullString++;
   }
 
-  const wchar_t* lpchEnd = std::wcschr(lpszFullString, L' ');
+  const wchar_t* lpchEnd = wcschr(lpszFullString, L' ');
   int nLen = lpchEnd ? static_cast<int>(lpchEnd - lpszFullString)
                      : static_cast<int>(wcslen(lpszFullString));
   if (nLen < 0)
-    return {};
+    return absl::nullopt;
 
   return WideString(lpszFullString, static_cast<size_t>(nLen));
 }
@@ -126,9 +128,9 @@
   }
 
   int index = 0;
-  while (1) {
-    Optional<WideString> word = ExtractSubString(findwhat.c_str(), index);
-    if (!word)
+  while (true) {
+    absl::optional<WideString> word = ExtractSubString(findwhat.c_str(), index);
+    if (!word.has_value())
       break;
 
     if (word->IsEmpty()) {
@@ -140,9 +142,9 @@
     size_t pos = 0;
     while (pos < word->GetLength()) {
       WideString curStr = word->Substr(pos, 1);
-      wchar_t curChar = (*word)[pos];
+      wchar_t curChar = word.value()[pos];
       if (IsIgnoreSpaceCharacter(curChar)) {
-        if (pos > 0 && curChar == 0x2019) {
+        if (pos > 0 && curChar == pdfium::unicode::kRightSingleQuotationMark) {
           pos++;
           continue;
         }
@@ -174,7 +176,7 @@
     const CPDF_TextPage* pTextPage,
     const WideString& findwhat,
     const Options& options,
-    Optional<size_t> startPos) {
+    absl::optional<size_t> startPos) {
   std::vector<WideString> findwhat_array =
       ExtractFindWhat(GetStringCase(findwhat, options.bMatchCase));
   auto find = pdfium::WrapUnique(
@@ -187,7 +189,7 @@
     const CPDF_TextPage* pTextPage,
     const std::vector<WideString>& findwhat_array,
     const Options& options,
-    Optional<size_t> startPos)
+    absl::optional<size_t> startPos)
     : m_pTextPage(pTextPage),
       m_strText(GetStringCase(pTextPage->GetAllPageText(), options.bMatchCase)),
       m_csFindWhatArray(findwhat_array),
@@ -216,8 +218,8 @@
   if (m_findNextStart.value() > strLen - 1)
     return false;
 
-  int nCount = pdfium::CollectionSize<int>(m_csFindWhatArray);
-  Optional<size_t> nResultPos = 0;
+  int nCount = fxcrt::CollectionSize<int>(m_csFindWhatArray);
+  absl::optional<size_t> nResultPos = 0;
   size_t nStartPos = m_findNextStart.value();
   bool bSpaceStart = false;
   for (int iWord = 0; iWord < nCount; iWord++) {
@@ -299,8 +301,7 @@
   if (m_strText.IsEmpty() || !m_findPreStart.has_value())
     return false;
 
-  CPDF_TextPageFind find_engine(m_pTextPage.Get(), m_csFindWhatArray, m_options,
-                                0);
+  CPDF_TextPageFind find_engine(m_pTextPage, m_csFindWhatArray, m_options, 0);
   if (!find_engine.FindFirst())
     return false;
 
diff --git a/core/fpdftext/cpdf_textpagefind.h b/core/fpdftext/cpdf_textpagefind.h
index c3b2956..ee5c46e 100644
--- a/core/fpdftext/cpdf_textpagefind.h
+++ b/core/fpdftext/cpdf_textpagefind.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,15 @@
 #ifndef CORE_FPDFTEXT_CPDF_TEXTPAGEFIND_H_
 #define CORE_FPDFTEXT_CPDF_TEXTPAGEFIND_H_
 
+#include <stddef.h>
+
 #include <memory>
 #include <vector>
 
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/optional.h"
+#include "core/fxcrt/widestring.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CPDF_TextPage;
 
@@ -30,7 +31,7 @@
       const CPDF_TextPage* pTextPage,
       const WideString& findwhat,
       const Options& options,
-      Optional<size_t> startPos);
+      absl::optional<size_t> startPos);
 
   ~CPDF_TextPageFind();
 
@@ -43,7 +44,7 @@
   CPDF_TextPageFind(const CPDF_TextPage* pTextPage,
                     const std::vector<WideString>& findwhat_array,
                     const Options& options,
-                    Optional<size_t> startPos);
+                    absl::optional<size_t> startPos);
 
   // Should be called immediately after construction.
   bool FindFirst();
@@ -53,8 +54,8 @@
   UnownedPtr<const CPDF_TextPage> const m_pTextPage;
   const WideString m_strText;
   const std::vector<WideString> m_csFindWhatArray;
-  Optional<size_t> m_findNextStart;
-  Optional<size_t> m_findPreStart;
+  absl::optional<size_t> m_findNextStart;
+  absl::optional<size_t> m_findPreStart;
   int m_resStart = 0;
   int m_resEnd = -1;
   const Options m_options;
diff --git a/core/fpdftext/unicodenormalizationdata.cpp b/core/fpdftext/unicodenormalizationdata.cpp
index 7cbc2f7..2b18afa 100644
--- a/core/fpdftext/unicodenormalizationdata.cpp
+++ b/core/fpdftext/unicodenormalizationdata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "core/fpdftext/unicodenormalizationdata.h"
 #include "core/fxcrt/fx_system.h"
 
-const uint16_t g_UnicodeData_Normalization[65536] = {
+const uint16_t kUnicodeDataNormalization[65536] = {
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
@@ -7291,7 +7291,7 @@
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000};
 
-const uint16_t g_UnicodeData_Normalization_Map1[5376] = {
+const uint16_t kUnicodeDataNormalizationMap1[5376] = {
     0x0066, 0x0053, 0x0053, 0x0059, 0x0020, 0x0308, 0x0061, 0x0052, 0x0304,
     0x0032, 0x0033, 0x0301, 0x03BC, 0x0327, 0x0031, 0x006F, 0x0041, 0x0041,
     0x0041, 0x0041, 0x0041, 0x0041, 0x0043, 0x0045, 0x0045, 0x0045, 0x0045,
@@ -7891,7 +7891,7 @@
     0x00AC, 0x00AF, 0x00A6, 0x00A5, 0x20A9, 0x2502, 0x2190, 0x2191, 0x2192,
     0x2193, 0x25A0, 0x25CB};
 
-const uint16_t g_UnicodeData_Normalization_Map2[1724] = {
+const uint16_t kUnicodeDataNormalizationMap2[1724] = {
     0x004F, 0x0045, 0x0054, 0x004D, 0x006F, 0x0065, 0x0049, 0x004A, 0x0069,
     0x006A, 0x004F, 0x0045, 0x006F, 0x0065, 0x0044, 0x017D, 0x0044, 0x017E,
     0x0064, 0x017E, 0x004C, 0x004A, 0x004C, 0x006A, 0x006C, 0x006A, 0x004E,
@@ -8085,7 +8085,7 @@
     0x0644, 0x0622, 0x0644, 0x0623, 0x0644, 0x0623, 0x0644, 0x0625, 0x0644,
     0x0625, 0x0644, 0x0627, 0x0644, 0x0627};
 
-const uint16_t g_UnicodeData_Normalization_Map3[1164] = {
+const uint16_t kUnicodeDataNormalizationMap3[1164] = {
     0x0031, 0x002F, 0x0034, 0x0031, 0x002F, 0x0032, 0x0033, 0x002F, 0x0034,
     0x002E, 0x002E, 0x002E, 0x2032, 0x2032, 0x2032, 0x2035, 0x2035, 0x2035,
     0x0061, 0x002F, 0x0063, 0x0061, 0x002F, 0x0073, 0x0063, 0x002F, 0x006F,
@@ -8217,7 +8217,7 @@
     0x0646, 0x062C, 0x064A, 0x0635, 0x0644, 0x06D2, 0x0642, 0x0644, 0x06D2,
     0x0635, 0x0644, 0x0649};
 
-const uint16_t g_UnicodeData_Normalization_Map4[488] = {
+const uint16_t kUnicodeDataNormalizationMap4[488] = {
     0x0004, 0x2032, 0x2032, 0x2032, 0x2032, 0x0004, 0x0031, 0x002F, 0x0031,
     0x0030, 0x0004, 0x0056, 0x0049, 0x0049, 0x0049, 0x0004, 0x0076, 0x0069,
     0x0069, 0x0069, 0x0004, 0x0028, 0x0031, 0x0030, 0x0029, 0x0004, 0x0028,
diff --git a/core/fpdftext/unicodenormalizationdata.h b/core/fpdftext/unicodenormalizationdata.h
index 39ee552..71cdb1e 100644
--- a/core/fpdftext/unicodenormalizationdata.h
+++ b/core/fpdftext/unicodenormalizationdata.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,10 +9,10 @@
 
 #include <stdint.h>
 
-extern const uint16_t g_UnicodeData_Normalization[];
-extern const uint16_t g_UnicodeData_Normalization_Map1[];
-extern const uint16_t g_UnicodeData_Normalization_Map2[];
-extern const uint16_t g_UnicodeData_Normalization_Map3[];
-extern const uint16_t g_UnicodeData_Normalization_Map4[];
+extern const uint16_t kUnicodeDataNormalization[];
+extern const uint16_t kUnicodeDataNormalizationMap1[];
+extern const uint16_t kUnicodeDataNormalizationMap2[];
+extern const uint16_t kUnicodeDataNormalizationMap3[];
+extern const uint16_t kUnicodeDataNormalizationMap4[];
 
 #endif  // CORE_FPDFTEXT_UNICODENORMALIZATIONDATA_H_
diff --git a/core/fxcodec/Android.bp b/core/fxcodec/Android.bp
index 7e5e69b..62caf7e 100644
--- a/core/fxcodec/Android.bp
+++ b/core/fxcodec/Android.bp
@@ -27,7 +27,8 @@
 
     exclude_srcs: [
         // pdf_enable_xfa
-        "progressivedecoder.cpp",
+        "progressive_decoder.cpp",
+        "jpeg/jpeg_progressive_decoder.cpp",
         // pdf_enable_xfa_bmp
         "bmp/*",
         // pdf_enable_xfa_gif
diff --git a/core/fxcodec/BUILD.gn b/core/fxcodec/BUILD.gn
index e0bc3a7..28f9915 100644
--- a/core/fxcodec/BUILD.gn
+++ b/core/fxcodec/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -11,7 +11,6 @@
     "basic/basicmodule.h",
     "cfx_codec_memory.cpp",
     "cfx_codec_memory.h",
-    "codec_module_iface.h",
     "fax/faxmodule.cpp",
     "fax/faxmodule.h",
     "flate/flatemodule.cpp",
@@ -19,8 +18,8 @@
     "fx_codec.cpp",
     "fx_codec.h",
     "fx_codec_def.h",
-    "icc/iccmodule.cpp",
-    "icc/iccmodule.h",
+    "icc/icc_transform.cpp",
+    "icc/icc_transform.h",
     "jbig2/JBig2_ArithDecoder.cpp",
     "jbig2/JBig2_ArithDecoder.h",
     "jbig2/JBig2_ArithIntDecoder.cpp",
@@ -57,20 +56,23 @@
     "jbig2/JBig2_SymbolDict.h",
     "jbig2/JBig2_TrdProc.cpp",
     "jbig2/JBig2_TrdProc.h",
-    "jbig2/jbig2module.cpp",
-    "jbig2/jbig2module.h",
+    "jbig2/jbig2_decoder.cpp",
+    "jbig2/jbig2_decoder.h",
+    "jpeg/jpeg_common.cpp",
+    "jpeg/jpeg_common.h",
     "jpeg/jpegmodule.cpp",
     "jpeg/jpegmodule.h",
     "jpx/cjpx_decoder.cpp",
     "jpx/cjpx_decoder.h",
     "jpx/jpx_decode_utils.cpp",
     "jpx/jpx_decode_utils.h",
-    "jpx/jpxmodule.cpp",
-    "jpx/jpxmodule.h",
     "scanlinedecoder.cpp",
     "scanlinedecoder.h",
   ]
-  configs += [ "../../:pdfium_core_config" ]
+  configs += [
+    "../../:pdfium_strict_config",
+    "../../:pdfium_noshorten_config",
+  ]
   include_dirs = []
   deps = [
     "../../third_party:lcms2",
@@ -83,13 +85,18 @@
 
   if (pdf_enable_xfa) {
     sources += [
-      "progressivedecoder.cpp",
-      "progressivedecoder.h",
+      "jpeg/jpeg_progressive_decoder.cpp",
+      "jpeg/jpeg_progressive_decoder.h",
+      "progressive_decoder.cpp",
+      "progressive_decoder.h",
+      "progressive_decoder_iface.h",
     ]
     if (pdf_enable_xfa_bmp) {
       sources += [
-        "bmp/bmpmodule.cpp",
-        "bmp/bmpmodule.h",
+        "bmp/bmp_decoder.cpp",
+        "bmp/bmp_decoder.h",
+        "bmp/bmp_progressive_decoder.cpp",
+        "bmp/bmp_progressive_decoder.h",
         "bmp/cfx_bmpcontext.cpp",
         "bmp/cfx_bmpcontext.h",
         "bmp/cfx_bmpdecompressor.cpp",
@@ -103,25 +110,27 @@
         "gif/cfx_gif.h",
         "gif/cfx_gifcontext.cpp",
         "gif/cfx_gifcontext.h",
-        "gif/cfx_lzwdecompressor.cpp",
-        "gif/cfx_lzwdecompressor.h",
-        "gif/gifmodule.cpp",
-        "gif/gifmodule.h",
+        "gif/gif_decoder.cpp",
+        "gif/gif_decoder.h",
+        "gif/gif_progressive_decoder.cpp",
+        "gif/gif_progressive_decoder.h",
+        "gif/lzw_decompressor.cpp",
+        "gif/lzw_decompressor.h",
       ]
     }
     if (pdf_enable_xfa_png) {
       sources += [
-        "png/pngmodule.cpp",
-        "png/pngmodule.h",
+        "png/png_decoder.cpp",
+        "png/png_decoder.h",
       ]
       deps += [ "../../third_party:png" ]
     }
     if (pdf_enable_xfa_tiff) {
       sources += [
-        "tiff/tiffmodule.cpp",
-        "tiff/tiffmodule.h",
+        "tiff/tiff_decoder.cpp",
+        "tiff/tiff_decoder.h",
       ]
-      deps += [ "../../third_party:fx_tiff" ]
+      deps += [ "../../third_party:tiff" ]
     }
   }
 
@@ -138,17 +147,18 @@
   ]
   deps = [
     ":fxcodec",
+    "../../third_party:libopenjpeg2",
     "../fpdfapi/parser",
   ]
   pdfium_root_dir = "../../"
 
   if (pdf_enable_xfa) {
-    sources += [ "progressivedecoder_unittest.cpp" ]
+    sources += [ "progressive_decoder_unittest.cpp" ]
     deps += [ "../fxge" ]
     if (pdf_enable_xfa_gif) {
       sources += [
         "gif/cfx_gifcontext_unittest.cpp",
-        "gif/cfx_lzwdecompressor_unittest.cpp",
+        "gif/lzw_decompressor_unittest.cpp",
       ]
     }
   }
diff --git a/core/fxcodec/basic/a85_unittest.cpp b/core/fxcodec/basic/a85_unittest.cpp
index e01d3d2..65dbf9a 100644
--- a/core/fxcodec/basic/a85_unittest.cpp
+++ b/core/fxcodec/basic/a85_unittest.cpp
@@ -1,162 +1,130 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include <stdint.h>
 
+#include <iterator>
 #include <limits>
 #include <memory>
 
 #include "core/fxcodec/basic/basicmodule.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/data_vector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-TEST(fxcodec, A85TestBadInputs) {
-  const uint8_t src_buf[] = {1, 2, 3, 4};
-  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
-  uint32_t dest_size = 0;
-
-  // Error codes, not segvs, should callers pass us a nullptr pointer.
-  EXPECT_FALSE(BasicModule::A85Encode(src_buf, &dest_buf, nullptr));
-  EXPECT_FALSE(BasicModule::A85Encode(src_buf, nullptr, &dest_size));
-  EXPECT_FALSE(BasicModule::A85Encode({}, &dest_buf, &dest_size));
+TEST(fxcodec, A85EmptyInput) {
+  EXPECT_TRUE(BasicModule::A85Encode({}).empty());
 }
 
 // No leftover bytes, just translate 2 sets of symbols.
-TEST(fxcodec, A85TestBasic) {
+TEST(fxcodec, A85Basic) {
   // Make sure really big values don't break.
   const uint8_t src_buf[] = {1, 2, 3, 4, 255, 255, 255, 255};
-  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
-  uint32_t dest_size = 0;
-  EXPECT_TRUE(BasicModule::A85Encode(src_buf, &dest_buf, &dest_size));
+  DataVector<uint8_t> dest_buf = BasicModule::A85Encode(src_buf);
 
   // Should have 5 chars for each set of 4 and 2 terminators.
   const uint8_t expected_out[] = {33, 60, 78, 63, 43,  115,
                                   56, 87, 45, 33, 126, 62};
-  ASSERT_EQ(FX_ArraySize(expected_out), dest_size);
+  ASSERT_EQ(std::size(expected_out), dest_buf.size());
 
   // Check the output
-  auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
-  for (uint32_t i = 0; i < dest_size; i++)
-    EXPECT_EQ(expected_out[i], dest_buf_span[i]) << " at " << i;
+  for (uint32_t i = 0; i < dest_buf.size(); i++)
+    EXPECT_EQ(expected_out[i], dest_buf[i]) << " at " << i;
 }
 
 // Leftover bytes.
-TEST(fxcodec, A85TestLeftoverBytes) {
-  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
-  uint32_t dest_size = 0;
-
+TEST(fxcodec, A85LeftoverBytes) {
   {
     // 1 Leftover Byte:
     const uint8_t src_buf_1leftover[] = {1, 2, 3, 4, 255};
-    EXPECT_TRUE(
-        BasicModule::A85Encode(src_buf_1leftover, &dest_buf, &dest_size));
+    DataVector<uint8_t> dest_buf = BasicModule::A85Encode(src_buf_1leftover);
 
     // 5 chars for first symbol + 2 + 2 terminators.
     uint8_t expected_out_1leftover[] = {33, 60, 78, 63, 43, 114, 114, 126, 62};
-    ASSERT_EQ(FX_ArraySize(expected_out_1leftover), dest_size);
+    ASSERT_EQ(std::size(expected_out_1leftover), dest_buf.size());
 
     // Check the output
-    auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
-    for (uint32_t i = 0; i < dest_size; i++)
-      EXPECT_EQ(expected_out_1leftover[i], dest_buf_span[i]) << " at " << i;
+    for (uint32_t i = 0; i < dest_buf.size(); i++)
+      EXPECT_EQ(expected_out_1leftover[i], dest_buf[i]) << " at " << i;
   }
 
   {
     // 2 Leftover bytes:
     const uint8_t src_buf_2leftover[] = {1, 2, 3, 4, 255, 254};
-    dest_buf.reset();
-    dest_size = 0;
-    EXPECT_TRUE(
-        BasicModule::A85Encode(src_buf_2leftover, &dest_buf, &dest_size));
+    DataVector<uint8_t> dest_buf = BasicModule::A85Encode(src_buf_2leftover);
     // 5 chars for first symbol + 3 + 2 terminators.
     const uint8_t expected_out_2leftover[] = {33,  60, 78, 63,  43,
                                               115, 56, 68, 126, 62};
-    ASSERT_EQ(FX_ArraySize(expected_out_2leftover), dest_size);
+    ASSERT_EQ(std::size(expected_out_2leftover), dest_buf.size());
 
     // Check the output
-    auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
-    for (uint32_t i = 0; i < dest_size; i++)
-      EXPECT_EQ(expected_out_2leftover[i], dest_buf_span[i]) << " at " << i;
+    for (uint32_t i = 0; i < dest_buf.size(); i++)
+      EXPECT_EQ(expected_out_2leftover[i], dest_buf[i]) << " at " << i;
   }
 
   {
     // 3 Leftover bytes:
     const uint8_t src_buf_3leftover[] = {1, 2, 3, 4, 255, 254, 253};
-    dest_buf.reset();
-    dest_size = 0;
-    EXPECT_TRUE(
-        BasicModule::A85Encode(src_buf_3leftover, &dest_buf, &dest_size));
+    DataVector<uint8_t> dest_buf = BasicModule::A85Encode(src_buf_3leftover);
     // 5 chars for first symbol + 4 + 2 terminators.
     const uint8_t expected_out_3leftover[] = {33, 60, 78,  63,  43, 115,
                                               56, 77, 114, 126, 62};
-    ASSERT_EQ(FX_ArraySize(expected_out_3leftover), dest_size);
+    ASSERT_EQ(std::size(expected_out_3leftover), dest_buf.size());
 
     // Check the output
-    auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
-    for (uint32_t i = 0; i < dest_size; i++)
-      EXPECT_EQ(expected_out_3leftover[i], dest_buf_span[i]) << " at " << i;
+    for (uint32_t i = 0; i < dest_buf.size(); i++)
+      EXPECT_EQ(expected_out_3leftover[i], dest_buf[i]) << " at " << i;
   }
 }
 
 // Test all zeros comes through as "z".
-TEST(fxcodec, A85TestZeros) {
-  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
-  uint32_t dest_size = 0;
-
+TEST(fxcodec, A85Zeros) {
   {
     // Make sure really big values don't break.
     const uint8_t src_buf[] = {1, 2, 3, 4, 0, 0, 0, 0};
-    EXPECT_TRUE(BasicModule::A85Encode(src_buf, &dest_buf, &dest_size));
+    DataVector<uint8_t> dest_buf = BasicModule::A85Encode(src_buf);
 
     // Should have 5 chars for first set of 4 + 1 for z + 2 terminators.
     const uint8_t expected_out[] = {33, 60, 78, 63, 43, 122, 126, 62};
-    ASSERT_EQ(FX_ArraySize(expected_out), dest_size);
+    ASSERT_EQ(std::size(expected_out), dest_buf.size());
 
     // Check the output
-    auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
-    for (uint32_t i = 0; i < dest_size; i++)
-      EXPECT_EQ(expected_out[i], dest_buf_span[i]) << " at " << i;
+    for (uint32_t i = 0; i < dest_buf.size(); i++)
+      EXPECT_EQ(expected_out[i], dest_buf[i]) << " at " << i;
   }
 
   {
     // Should also work if it is at the start:
     const uint8_t src_buf_2[] = {0, 0, 0, 0, 1, 2, 3, 4};
-    dest_buf.reset();
-    dest_size = 0;
-    EXPECT_TRUE(BasicModule::A85Encode(src_buf_2, &dest_buf, &dest_size));
+    DataVector<uint8_t> dest_buf = BasicModule::A85Encode(src_buf_2);
 
     // Should have 5 chars for set of 4 + 1 for z + 2 terminators.
     const uint8_t expected_out_2[] = {122, 33, 60, 78, 63, 43, 126, 62};
-    ASSERT_EQ(FX_ArraySize(expected_out_2), dest_size);
+    ASSERT_EQ(std::size(expected_out_2), dest_buf.size());
 
     // Check the output
-    auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
-    for (uint32_t i = 0; i < dest_size; i++)
-      EXPECT_EQ(expected_out_2[i], dest_buf_span[i]) << " at " << i;
+    for (uint32_t i = 0; i < dest_buf.size(); i++)
+      EXPECT_EQ(expected_out_2[i], dest_buf[i]) << " at " << i;
   }
 
   {
     // Try with 2 leftover zero bytes. Make sure we don't get a "z".
     const uint8_t src_buf_3[] = {1, 2, 3, 4, 0, 0};
-    dest_buf.reset();
-    dest_size = 0;
-    EXPECT_TRUE(BasicModule::A85Encode(src_buf_3, &dest_buf, &dest_size));
+    DataVector<uint8_t> dest_buf = BasicModule::A85Encode(src_buf_3);
 
     // Should have 5 chars for set of 4 + 3 for last 2 + 2 terminators.
     const uint8_t expected_out_leftover[] = {33, 60, 78, 63,  43,
                                              33, 33, 33, 126, 62};
-    ASSERT_EQ(FX_ArraySize(expected_out_leftover), dest_size);
+    ASSERT_EQ(std::size(expected_out_leftover), dest_buf.size());
 
     // Check the output
-    auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
-    for (uint32_t i = 0; i < dest_size; i++)
-      EXPECT_EQ(expected_out_leftover[i], dest_buf_span[i]) << " at " << i;
+    for (uint32_t i = 0; i < dest_buf.size(); i++)
+      EXPECT_EQ(expected_out_leftover[i], dest_buf[i]) << " at " << i;
   }
 }
 
 // Make sure we get returns in the expected locations.
-TEST(fxcodec, A85TestLineBreaks) {
+TEST(fxcodec, A85LineBreaks) {
   // Make sure really big values don't break.
   uint8_t src_buf[131] = {0};
   // 1 full line + most of a line of normal symbols.
@@ -173,21 +141,18 @@
     src_buf[k + 2] = 3;
     src_buf[k + 3] = 4;
   }
-  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
-  uint32_t dest_size = 0;
 
   // Should succeed.
-  EXPECT_TRUE(BasicModule::A85Encode(src_buf, &dest_buf, &dest_size));
+  DataVector<uint8_t> dest_buf = BasicModule::A85Encode(src_buf);
 
   // Should have 75 chars in the first row plus 2 char return,
   // 76 chars in the second row plus 2 char return,
   // and 9 chars in the last row with 2 terminators.
-  ASSERT_EQ(166u, dest_size);
+  ASSERT_EQ(166u, dest_buf.size());
 
   // Check for the returns.
-  auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
-  EXPECT_EQ(13, dest_buf_span[75]);
-  EXPECT_EQ(10, dest_buf_span[76]);
-  EXPECT_EQ(13, dest_buf_span[153]);
-  EXPECT_EQ(10, dest_buf_span[154]);
+  EXPECT_EQ(13, dest_buf[75]);
+  EXPECT_EQ(10, dest_buf[76]);
+  EXPECT_EQ(13, dest_buf[153]);
+  EXPECT_EQ(10, dest_buf[154]);
 }
diff --git a/core/fxcodec/basic/basicmodule.cpp b/core/fxcodec/basic/basicmodule.cpp
index d495454..452e464 100644
--- a/core/fxcodec/basic/basicmodule.cpp
+++ b/core/fxcodec/basic/basicmodule.cpp
@@ -1,15 +1,22 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxcodec/basic/basicmodule.h"
 
+#include <stdint.h>
+
 #include <algorithm>
 #include <utility>
 
 #include "core/fxcodec/scanlinedecoder.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/ptr_util.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/span_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 namespace fxcodec {
 
@@ -27,16 +34,16 @@
               int bpc);
 
   // ScanlineDecoder:
-  bool v_Rewind() override;
-  uint8_t* v_GetNextLine() override;
-  uint32_t GetSrcOffset() override { return m_SrcOffset; }
+  bool Rewind() override;
+  pdfium::span<uint8_t> GetNextLine() override;
+  uint32_t GetSrcOffset() override;
 
  private:
   bool CheckDestSize();
   void GetNextOperator();
   void UpdateOperator(uint8_t used_bytes);
 
-  std::unique_ptr<uint8_t, FxFreeDeleter> m_pScanline;
+  DataVector<uint8_t> m_Scanline;
   pdfium::span<const uint8_t> m_SrcBuf;
   size_t m_dwLineBytes = 0;
   size_t m_SrcOffset = 0;
@@ -46,7 +53,10 @@
 
 RLScanlineDecoder::RLScanlineDecoder() = default;
 
-RLScanlineDecoder::~RLScanlineDecoder() = default;
+RLScanlineDecoder::~RLScanlineDecoder() {
+  // Span in superclass can't outlive our buffer.
+  m_pLastScanline = pdfium::span<uint8_t>();
+}
 
 bool RLScanlineDecoder::CheckDestSize() {
   size_t i = 0;
@@ -101,40 +111,43 @@
   m_Pitch = pitch.ValueOrDie();
   // Overflow should already have been checked before this is called.
   m_dwLineBytes = (static_cast<uint32_t>(width) * nComps * bpc + 7) / 8;
-  m_pScanline.reset(FX_Alloc(uint8_t, m_Pitch));
+  m_Scanline.resize(m_Pitch);
   return CheckDestSize();
 }
 
-bool RLScanlineDecoder::v_Rewind() {
-  memset(m_pScanline.get(), 0, m_Pitch);
+bool RLScanlineDecoder::Rewind() {
+  fxcrt::spanclr(pdfium::make_span(m_Scanline));
   m_SrcOffset = 0;
   m_bEOD = false;
   m_Operator = 0;
   return true;
 }
 
-uint8_t* RLScanlineDecoder::v_GetNextLine() {
+pdfium::span<uint8_t> RLScanlineDecoder::GetNextLine() {
   if (m_SrcOffset == 0) {
     GetNextOperator();
   } else if (m_bEOD) {
-    return nullptr;
+    return pdfium::span<uint8_t>();
   }
-  memset(m_pScanline.get(), 0, m_Pitch);
   uint32_t col_pos = 0;
   bool eol = false;
+  auto scan_span = pdfium::make_span(m_Scanline);
+  fxcrt::spanclr(scan_span);
   while (m_SrcOffset < m_SrcBuf.size() && !eol) {
     if (m_Operator < 128) {
       uint32_t copy_len = m_Operator + 1;
       if (col_pos + copy_len >= m_dwLineBytes) {
-        copy_len = m_dwLineBytes - col_pos;
+        copy_len =
+            pdfium::base::checked_cast<uint32_t>(m_dwLineBytes - col_pos);
         eol = true;
       }
       if (copy_len >= m_SrcBuf.size() - m_SrcOffset) {
-        copy_len = m_SrcBuf.size() - m_SrcOffset;
+        copy_len =
+            pdfium::base::checked_cast<uint32_t>(m_SrcBuf.size() - m_SrcOffset);
         m_bEOD = true;
       }
       auto copy_span = m_SrcBuf.subspan(m_SrcOffset, copy_len);
-      memcpy(m_pScanline.get() + col_pos, copy_span.data(), copy_span.size());
+      fxcrt::spancpy(scan_span.subspan(col_pos), copy_span);
       col_pos += copy_len;
       UpdateOperator((uint8_t)copy_len);
     } else if (m_Operator > 128) {
@@ -144,10 +157,11 @@
       }
       uint32_t duplicate_len = 257 - m_Operator;
       if (col_pos + duplicate_len >= m_dwLineBytes) {
-        duplicate_len = m_dwLineBytes - col_pos;
+        duplicate_len =
+            pdfium::base::checked_cast<uint32_t>(m_dwLineBytes - col_pos);
         eol = true;
       }
-      memset(m_pScanline.get() + col_pos, fill, duplicate_len);
+      fxcrt::spanset(scan_span.subspan(col_pos, duplicate_len), fill);
       col_pos += duplicate_len;
       UpdateOperator((uint8_t)duplicate_len);
     } else {
@@ -155,7 +169,11 @@
       break;
     }
   }
-  return m_pScanline.get();
+  return m_Scanline;
+}
+
+uint32_t RLScanlineDecoder::GetSrcOffset() {
+  return pdfium::base::checked_cast<uint32_t>(m_SrcOffset);
 }
 
 void RLScanlineDecoder::GetNextOperator() {
@@ -171,7 +189,7 @@
     return;
   }
   if (m_Operator < 128) {
-    ASSERT((uint32_t)m_Operator + 1 >= used_bytes);
+    DCHECK((uint32_t)m_Operator + 1 >= used_bytes);
     if (used_bytes == m_Operator + 1) {
       m_SrcOffset += used_bytes;
       GetNextOperator();
@@ -185,7 +203,7 @@
     return;
   }
   uint8_t count = 257 - m_Operator;
-  ASSERT((uint32_t)count >= used_bytes);
+  DCHECK((uint32_t)count >= used_bytes);
   if (used_bytes == count) {
     m_SrcOffset++;
     GetNextOperator();
@@ -204,7 +222,7 @@
     int height,
     int nComps,
     int bpc) {
-  auto pDecoder = pdfium::MakeUnique<RLScanlineDecoder>();
+  auto pDecoder = std::make_unique<RLScanlineDecoder>();
   if (!pDecoder->Create(src_buf, width, height, nComps, bpc))
     return nullptr;
 
@@ -212,24 +230,14 @@
 }
 
 // static
-bool BasicModule::RunLengthEncode(
-    pdfium::span<const uint8_t> src_span,
-    std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-    uint32_t* dest_size) {
-  // Check inputs
-  if (src_span.empty() || !dest_buf || !dest_size)
-    return false;
+DataVector<uint8_t> BasicModule::RunLengthEncode(
+    pdfium::span<const uint8_t> src_span) {
+  if (src_span.empty())
+    return {};
 
-  // Edge case
-  if (src_span.size() == 1) {
-    *dest_size = 3;
-    dest_buf->reset(FX_Alloc(uint8_t, *dest_size));
-    auto dest_buf_span = pdfium::make_span(dest_buf->get(), *dest_size);
-    dest_buf_span[0] = 0;
-    dest_buf_span[1] = src_span[0];
-    dest_buf_span[2] = 128;
-    return true;
-  }
+  // Handle edge case.
+  if (src_span.size() == 1)
+    return {0, src_span[0], 128};
 
   // Worst case: 1 nonmatch, 2 match, 1 nonmatch, 2 match, etc. This becomes
   // 4 output chars for every 3 input, plus up to 4 more for the 1-2 chars
@@ -239,10 +247,10 @@
   estimated_size /= 3;
   estimated_size *= 4;
   estimated_size += 1;
-  dest_buf->reset(FX_Alloc(uint8_t, estimated_size.ValueOrDie()));
+  DataVector<uint8_t> result(estimated_size.ValueOrDie());
 
-  // Set up pointers.
-  uint8_t* out = dest_buf->get();
+  // Set up span and counts.
+  auto result_span = pdfium::make_span(result);
   uint32_t run_start = 0;
   uint32_t run_end = 1;
   uint8_t x = src_span[run_start];
@@ -259,56 +267,52 @@
         y = src_span[run_end];
     }
     if (run_end - run_start > 1) {  // Matched run but not at end of input.
-      out[0] = 257 - (run_end - run_start);
-      out[1] = x;
+      result_span[0] = 257 - (run_end - run_start);
+      result_span[1] = x;
       x = y;
       run_start = run_end;
       run_end++;
       if (run_end < src_span.size())
         y = src_span[run_end];
-      out += 2;
+      result_span = result_span.subspan(2);
       continue;
     }
     // Mismatched run
     while (x != y && run_end <= run_start + max_len) {
-      out[run_end - run_start] = x;
+      result_span[run_end - run_start] = x;
       x = y;
       run_end++;
       if (run_end == src_span.size()) {
         if (run_end <= run_start + max_len) {
-          out[run_end - run_start] = x;
+          result_span[run_end - run_start] = x;
           run_end++;
         }
         break;
       }
       y = src_span[run_end];
     }
-    out[0] = run_end - run_start - 2;
-    out += run_end - run_start;
+    result_span[0] = run_end - run_start - 2;
+    result_span = result_span.subspan(run_end - run_start);
     run_start = run_end - 1;
   }
   if (run_start < src_span.size()) {  // 1 leftover character
-    out[0] = 0;
-    out[1] = x;
-    out += 2;
+    result_span[0] = 0;
+    result_span[1] = x;
+    result_span = result_span.subspan(2);
   }
-  *out = 128;
-  *dest_size = out + 1 - dest_buf->get();
-  return true;
+  result_span[0] = 128;
+  size_t new_size = 1 + result.size() - result_span.size();
+  CHECK_LE(new_size, result.size());
+  result.resize(new_size);
+  return result;
 }
 
 // static
-bool BasicModule::A85Encode(pdfium::span<const uint8_t> src_span,
-                            std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-                            uint32_t* dest_size) {
-  // Check inputs.
-  if (!dest_buf || !dest_size)
-    return false;
-
-  if (src_span.empty()) {
-    *dest_size = 0;
-    return false;
-  }
+DataVector<uint8_t> BasicModule::A85Encode(
+    pdfium::span<const uint8_t> src_span) {
+  DataVector<uint8_t> result;
+  if (src_span.empty())
+    return result;
 
   // Worst case: 5 output for each 4 input (plus up to 4 from leftover), plus
   // 2 character new lines each 75 output chars plus 2 termination chars. May
@@ -319,33 +323,32 @@
   estimated_size += 4;
   estimated_size += src_span.size() / 30;
   estimated_size += 2;
-  dest_buf->reset(FX_Alloc(uint8_t, estimated_size.ValueOrDie()));
+  result.resize(estimated_size.ValueOrDie());
 
-  // Set up pointers.
-  uint8_t* out = dest_buf->get();
+  // Set up span and counts.
+  auto result_span = pdfium::make_span(result);
   uint32_t pos = 0;
   uint32_t line_length = 0;
   while (src_span.size() >= 4 && pos < src_span.size() - 3) {
-    uint32_t val = ((uint32_t)(src_span[pos]) << 24) +
-                   ((uint32_t)(src_span[pos + 1]) << 16) +
-                   ((uint32_t)(src_span[pos + 2]) << 8) +
-                   (uint32_t)(src_span[pos + 3]);
+    auto val_span = src_span.subspan(pos, 4);
+    uint32_t val = FXSYS_UINT32_GET_MSBFIRST(val_span);
     pos += 4;
     if (val == 0) {  // All zero special case
-      *out = 'z';
-      out++;
+      result_span[0] = 'z';
+      result_span = result_span.subspan(1);
       line_length++;
     } else {  // Compute base 85 characters and add 33.
       for (int i = 4; i >= 0; i--) {
-        out[i] = (uint8_t)(val % 85) + 33;
-        val = val / 85;
+        result_span[i] = (val % 85) + 33;
+        val /= 85;
       }
-      out += 5;
+      result_span = result_span.subspan(5);
       line_length += 5;
     }
     if (line_length >= 75) {  // Add a return.
-      *out++ = '\r';
-      *out++ = '\n';
+      result_span[0] = '\r';
+      result_span[1] = '\n';
+      result_span = result_span.subspan(2);
       line_length = 0;
     }
   }
@@ -359,18 +362,19 @@
     }
     for (int i = 4; i >= 0; i--) {
       if (i <= count)
-        out[i] = (uint8_t)(val % 85) + 33;
-      val = val / 85;
+        result_span[i] = (val % 85) + 33;
+      val /= 85;
     }
-    out += count + 1;
+    result_span = result_span.subspan(count + 1);
   }
 
   // Terminating characters.
-  out[0] = '~';
-  out[1] = '>';
-  out += 2;
-  *dest_size = out - dest_buf->get();
-  return true;
+  result_span[0] = '~';
+  result_span[1] = '>';
+  size_t new_size = 2 + result.size() - result_span.size();
+  CHECK_LE(new_size, result.size());
+  result.resize(new_size);
+  return result;
 }
 
 }  // namespace fxcodec
diff --git a/core/fxcodec/basic/basicmodule.h b/core/fxcodec/basic/basicmodule.h
index 5de4da9..61e3735 100644
--- a/core/fxcodec/basic/basicmodule.h
+++ b/core/fxcodec/basic/basicmodule.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,12 @@
 #ifndef CORE_FXCODEC_BASIC_BASICMODULE_H_
 #define CORE_FXCODEC_BASIC_BASICMODULE_H_
 
+#include <stdint.h>
+
 #include <memory>
 
-#include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/span.h"
+#include "core/fxcrt/data_vector.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcodec {
 
@@ -26,13 +27,10 @@
       int nComps,
       int bpc);
 
-  static bool RunLengthEncode(pdfium::span<const uint8_t> src_span,
-                              std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-                              uint32_t* dest_size);
+  static DataVector<uint8_t> RunLengthEncode(
+      pdfium::span<const uint8_t> src_span);
 
-  static bool A85Encode(pdfium::span<const uint8_t> src_span,
-                        std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-                        uint32_t* dest_size);
+  static DataVector<uint8_t> A85Encode(pdfium::span<const uint8_t> src_span);
 
   BasicModule() = delete;
   BasicModule(const BasicModule&) = delete;
diff --git a/core/fxcodec/basic/rle_unittest.cpp b/core/fxcodec/basic/rle_unittest.cpp
index 047b447..7f81b37 100644
--- a/core/fxcodec/basic/rle_unittest.cpp
+++ b/core/fxcodec/basic/rle_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,47 +9,34 @@
 
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fxcodec/basic/basicmodule.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/data_vector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-TEST(fxcodec, RLETestBadInputs) {
-  const uint8_t src_buf[] = {1};
-  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
-  uint32_t dest_size = 0;
-
-  // Error codes, not segvs, should callers pass us a nullptr pointer.
-  EXPECT_FALSE(BasicModule::RunLengthEncode(src_buf, &dest_buf, nullptr));
-  EXPECT_FALSE(BasicModule::RunLengthEncode(src_buf, nullptr, &dest_size));
-  EXPECT_FALSE(BasicModule::RunLengthEncode({}, &dest_buf, &dest_size));
+TEST(fxcodec, RLEEmptyInput) {
+  EXPECT_TRUE(BasicModule::RunLengthEncode({}).empty());
 }
 
 // Check length 1 input works. Check terminating character is applied.
-TEST(fxcodec, RLETestShortInput) {
+TEST(fxcodec, RLEShortInput) {
   const uint8_t src_buf[] = {1};
-  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
-  uint32_t dest_size = 0;
-
-  EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf, &dest_buf, &dest_size));
-  ASSERT_EQ(3u, dest_size);
-  auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
-  EXPECT_EQ(0, dest_buf_span[0]);
-  EXPECT_EQ(1, dest_buf_span[1]);
-  EXPECT_EQ(128, dest_buf_span[2]);
+  DataVector<uint8_t> dest_buf = BasicModule::RunLengthEncode(src_buf);
+  ASSERT_EQ(3u, dest_buf.size());
+  EXPECT_EQ(0, dest_buf[0]);
+  EXPECT_EQ(1, dest_buf[1]);
+  EXPECT_EQ(128, dest_buf[2]);
 }
 
 // Check a few basic cases (2 matching runs in a row, matching run followed
 // by a non-matching run, and non-matching run followed by a matching run).
-TEST(fxcodec, RLETestNormalInputs) {
-  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
-  uint32_t dest_size = 0;
+TEST(fxcodec, RLENormalInputs) {
   std::unique_ptr<uint8_t, FxFreeDeleter> decoded_buf;
   uint32_t decoded_size = 0;
 
   {
     // Case 1: Match, match
     const uint8_t src_buf_1[] = {2, 2, 2, 2, 4, 4, 4, 4, 4, 4};
-    EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_1, &dest_buf, &dest_size));
-    RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size);
+    DataVector<uint8_t> dest_buf = BasicModule::RunLengthEncode(src_buf_1);
+    RunLengthDecode(dest_buf, &decoded_buf, &decoded_size);
     ASSERT_EQ(sizeof(src_buf_1), decoded_size);
     auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size);
     for (uint32_t i = 0; i < decoded_size; i++)
@@ -59,12 +46,10 @@
   {
     // Case 2: Match, non-match
     const uint8_t src_buf_2[] = {2, 2, 2, 2, 1, 2, 3, 4, 5, 6};
-    dest_buf.reset();
-    dest_size = 0;
-    EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_2, &dest_buf, &dest_size));
+    DataVector<uint8_t> dest_buf = BasicModule::RunLengthEncode(src_buf_2);
     decoded_buf.reset();
     decoded_size = 0;
-    RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size);
+    RunLengthDecode(dest_buf, &decoded_buf, &decoded_size);
     ASSERT_EQ(sizeof(src_buf_2), decoded_size);
     auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size);
     for (uint32_t i = 0; i < decoded_size; i++)
@@ -74,12 +59,10 @@
   {
     // Case 3: Non-match, match
     const uint8_t src_buf_3[] = {1, 2, 3, 4, 5, 3, 3, 3, 3, 3};
-    dest_buf.reset();
-    dest_size = 0;
-    EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_3, &dest_buf, &dest_size));
+    DataVector<uint8_t> dest_buf = BasicModule::RunLengthEncode(src_buf_3);
     decoded_buf.reset();
     decoded_size = 0;
-    RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size);
+    RunLengthDecode(dest_buf, &decoded_buf, &decoded_size);
     ASSERT_EQ(sizeof(src_buf_3), decoded_size);
     auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size);
     for (uint32_t i = 0; i < decoded_size; i++)
@@ -89,17 +72,15 @@
 
 // Check that runs longer than 128 are broken up properly, both matched and
 // non-matched.
-TEST(fxcodec, RLETestFullLengthInputs) {
-  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
-  uint32_t dest_size = 0;
+TEST(fxcodec, RLEFullLengthInputs) {
   std::unique_ptr<uint8_t, FxFreeDeleter> decoded_buf;
   uint32_t decoded_size = 0;
 
   {
     // Case 1: Match, match
     const uint8_t src_buf_1[260] = {1};
-    EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_1, &dest_buf, &dest_size));
-    RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size);
+    DataVector<uint8_t> dest_buf = BasicModule::RunLengthEncode(src_buf_1);
+    RunLengthDecode(dest_buf, &decoded_buf, &decoded_size);
     ASSERT_EQ(sizeof(src_buf_1), decoded_size);
     auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size);
     for (uint32_t i = 0; i < decoded_size; i++)
@@ -111,12 +92,10 @@
     uint8_t src_buf_2[260] = {2};
     for (uint16_t i = 128; i < 260; i++)
       src_buf_2[i] = static_cast<uint8_t>(i - 125);
-    dest_buf.reset();
-    dest_size = 0;
-    EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_2, &dest_buf, &dest_size));
+    DataVector<uint8_t> dest_buf = BasicModule::RunLengthEncode(src_buf_2);
     decoded_buf.reset();
     decoded_size = 0;
-    RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size);
+    RunLengthDecode(dest_buf, &decoded_buf, &decoded_size);
     ASSERT_EQ(sizeof(src_buf_2), decoded_size);
     auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size);
     for (uint32_t i = 0; i < decoded_size; i++)
@@ -128,12 +107,10 @@
     uint8_t src_buf_3[260] = {3};
     for (uint8_t i = 0; i < 128; i++)
       src_buf_3[i] = i;
-    dest_buf.reset();
-    dest_size = 0;
-    EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_3, &dest_buf, &dest_size));
+    DataVector<uint8_t> dest_buf = BasicModule::RunLengthEncode(src_buf_3);
     decoded_buf.reset();
     decoded_size = 0;
-    RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size);
+    RunLengthDecode(dest_buf, &decoded_buf, &decoded_size);
     ASSERT_EQ(sizeof(src_buf_3), decoded_size);
     auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size);
     for (uint32_t i = 0; i < decoded_size; i++)
@@ -145,12 +122,10 @@
     uint8_t src_buf_4[260];
     for (uint16_t i = 0; i < 260; i++)
       src_buf_4[i] = static_cast<uint8_t>(i);
-    dest_buf.reset();
-    dest_size = 0;
-    EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_4, &dest_buf, &dest_size));
+    DataVector<uint8_t> dest_buf = BasicModule::RunLengthEncode(src_buf_4);
     decoded_buf.reset();
     decoded_size = 0;
-    RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size);
+    RunLengthDecode(dest_buf, &decoded_buf, &decoded_size);
     ASSERT_EQ(sizeof(src_buf_4), decoded_size);
     auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size);
     for (uint32_t i = 0; i < decoded_size; i++)
diff --git a/core/fxcodec/bmp/bmp_decoder.cpp b/core/fxcodec/bmp/bmp_decoder.cpp
new file mode 100644
index 0000000..74fd618
--- /dev/null
+++ b/core/fxcodec/bmp/bmp_decoder.cpp
@@ -0,0 +1,75 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/bmp/bmp_decoder.h"
+
+#include <utility>
+
+#include "core/fxcodec/bmp/cfx_bmpcontext.h"
+#include "core/fxcodec/cfx_codec_memory.h"
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcodec/fx_codec_def.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/check.h"
+
+namespace fxcodec {
+
+// static
+std::unique_ptr<ProgressiveDecoderIface::Context> BmpDecoder::StartDecode(
+    Delegate* pDelegate) {
+  return std::make_unique<CFX_BmpContext>(pDelegate);
+}
+
+// static
+BmpDecoder::Status BmpDecoder::ReadHeader(
+    ProgressiveDecoderIface::Context* pContext,
+    int32_t* width,
+    int32_t* height,
+    bool* tb_flag,
+    int32_t* components,
+    int32_t* pal_num,
+    const std::vector<uint32_t>** palette,
+    CFX_DIBAttribute* pAttribute) {
+  DCHECK(pAttribute);
+
+  auto* ctx = static_cast<CFX_BmpContext*>(pContext);
+  Status status = ctx->m_Bmp.ReadHeader();
+  if (status != Status::kSuccess)
+    return status;
+
+  *width = ctx->m_Bmp.width();
+  *height = ctx->m_Bmp.height();
+  *tb_flag = ctx->m_Bmp.img_tb_flag();
+  *components = ctx->m_Bmp.components();
+  *pal_num = ctx->m_Bmp.pal_num();
+  *palette = ctx->m_Bmp.palette();
+  pAttribute->m_wDPIUnit = CFX_DIBAttribute::kResUnitMeter;
+  pAttribute->m_nXDPI = ctx->m_Bmp.dpi_x();
+  pAttribute->m_nYDPI = ctx->m_Bmp.dpi_y();
+  return Status::kSuccess;
+}
+
+// static
+BmpDecoder::Status BmpDecoder::LoadImage(
+    ProgressiveDecoderIface::Context* pContext) {
+  return static_cast<CFX_BmpContext*>(pContext)->m_Bmp.DecodeImage();
+}
+
+// static
+FX_FILESIZE BmpDecoder::GetAvailInput(
+    ProgressiveDecoderIface::Context* pContext) {
+  return static_cast<CFX_BmpContext*>(pContext)->m_Bmp.GetAvailInput();
+}
+
+// static
+bool BmpDecoder::Input(ProgressiveDecoderIface::Context* pContext,
+                       RetainPtr<CFX_CodecMemory> codec_memory) {
+  auto* ctx = static_cast<CFX_BmpContext*>(pContext);
+  ctx->m_Bmp.SetInputBuffer(std::move(codec_memory));
+  return true;
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/bmp/bmp_decoder.h b/core/fxcodec/bmp/bmp_decoder.h
new file mode 100644
index 0000000..08a1638
--- /dev/null
+++ b/core/fxcodec/bmp/bmp_decoder.h
@@ -0,0 +1,61 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_BMP_BMP_DECODER_H_
+#define CORE_FXCODEC_BMP_BMP_DECODER_H_
+
+#include <memory>
+#include <vector>
+
+#include "core/fxcodec/progressive_decoder_iface.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "third_party/base/containers/span.h"
+
+#ifndef PDF_ENABLE_XFA_BMP
+#error "BMP must be enabled"
+#endif
+
+namespace fxcodec {
+
+class CFX_DIBAttribute;
+
+class BmpDecoder {
+ public:
+  class Delegate {
+   public:
+    virtual bool BmpInputImagePositionBuf(uint32_t rcd_pos) = 0;
+    virtual void BmpReadScanline(uint32_t row_num,
+                                 pdfium::span<const uint8_t> row_buf) = 0;
+  };
+
+  enum class Status : uint8_t { kFail, kSuccess, kContinue };
+
+  static std::unique_ptr<ProgressiveDecoderIface::Context> StartDecode(
+      Delegate* pDelegate);
+  static Status ReadHeader(ProgressiveDecoderIface::Context* pContext,
+                           int32_t* width,
+                           int32_t* height,
+                           bool* tb_flag,
+                           int32_t* components,
+                           int32_t* pal_num,
+                           const std::vector<uint32_t>** palette,
+                           CFX_DIBAttribute* pAttribute);
+  static Status LoadImage(ProgressiveDecoderIface::Context* pContext);
+  static FX_FILESIZE GetAvailInput(ProgressiveDecoderIface::Context* pContext);
+  static bool Input(ProgressiveDecoderIface::Context* pContext,
+                    RetainPtr<CFX_CodecMemory> codec_memory);
+
+  BmpDecoder() = delete;
+  BmpDecoder(const BmpDecoder&) = delete;
+  BmpDecoder& operator=(const BmpDecoder&) = delete;
+};
+
+}  // namespace fxcodec
+
+using BmpDecoder = fxcodec::BmpDecoder;
+
+#endif  // CORE_FXCODEC_BMP_BMP_DECODER_H_
diff --git a/core/fxcodec/bmp/bmp_progressive_decoder.cpp b/core/fxcodec/bmp/bmp_progressive_decoder.cpp
new file mode 100644
index 0000000..4ab9fda
--- /dev/null
+++ b/core/fxcodec/bmp/bmp_progressive_decoder.cpp
@@ -0,0 +1,33 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/bmp/bmp_progressive_decoder.h"
+
+#include "core/fxcodec/bmp/bmp_decoder.h"
+#include "core/fxcodec/cfx_codec_memory.h"
+
+namespace fxcodec {
+
+// static
+BmpProgressiveDecoder* BmpProgressiveDecoder::GetInstance() {
+  static pdfium::base::NoDestructor<BmpProgressiveDecoder> s;
+  return s.get();
+}
+
+BmpProgressiveDecoder::BmpProgressiveDecoder() = default;
+
+BmpProgressiveDecoder::~BmpProgressiveDecoder() = default;
+
+FX_FILESIZE BmpProgressiveDecoder::GetAvailInput(Context* context) const {
+  return BmpDecoder::GetAvailInput(context);
+}
+
+bool BmpProgressiveDecoder::Input(Context* context,
+                                  RetainPtr<CFX_CodecMemory> codec_memory) {
+  return BmpDecoder::Input(context, codec_memory);
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/bmp/bmp_progressive_decoder.h b/core/fxcodec/bmp/bmp_progressive_decoder.h
new file mode 100644
index 0000000..f2040c8
--- /dev/null
+++ b/core/fxcodec/bmp/bmp_progressive_decoder.h
@@ -0,0 +1,39 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_BMP_BMP_PROGRESSIVE_DECODER_H_
+#define CORE_FXCODEC_BMP_BMP_PROGRESSIVE_DECODER_H_
+
+#include "core/fxcodec/progressive_decoder_iface.h"
+#include "third_party/base/no_destructor.h"
+
+#ifndef PDF_ENABLE_XFA_BMP
+#error "BMP must be enabled"
+#endif
+
+namespace fxcodec {
+
+class BmpProgressiveDecoder final : public ProgressiveDecoderIface {
+ public:
+  static BmpProgressiveDecoder* GetInstance();
+
+  // ProgressiveDecoderIface:
+  FX_FILESIZE GetAvailInput(Context* context) const override;
+  bool Input(Context* context,
+             RetainPtr<CFX_CodecMemory> codec_memory) override;
+
+ private:
+  friend pdfium::base::NoDestructor<BmpProgressiveDecoder>;
+
+  BmpProgressiveDecoder();
+  ~BmpProgressiveDecoder() override;
+};
+
+}  // namespace fxcodec
+
+using BmpProgressiveDecoder = fxcodec::BmpProgressiveDecoder;
+
+#endif  // CORE_FXCODEC_BMP_BMP_PROGRESSIVE_DECODER_H_
diff --git a/core/fxcodec/bmp/bmpmodule.cpp b/core/fxcodec/bmp/bmpmodule.cpp
deleted file mode 100644
index 78854c4..0000000
--- a/core/fxcodec/bmp/bmpmodule.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/bmp/bmpmodule.h"
-
-#include <utility>
-
-#include "core/fxcodec/bmp/cfx_bmpcontext.h"
-#include "core/fxcodec/cfx_codec_memory.h"
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/ptr_util.h"
-
-namespace fxcodec {
-
-BmpModule::BmpModule() = default;
-
-BmpModule::~BmpModule() = default;
-
-std::unique_ptr<ModuleIface::Context> BmpModule::Start(Delegate* pDelegate) {
-  return pdfium::MakeUnique<CFX_BmpContext>(this, pDelegate);
-}
-
-BmpModule::Status BmpModule::ReadHeader(Context* pContext,
-                                        int32_t* width,
-                                        int32_t* height,
-                                        bool* tb_flag,
-                                        int32_t* components,
-                                        int32_t* pal_num,
-                                        const std::vector<uint32_t>** palette,
-                                        CFX_DIBAttribute* pAttribute) {
-  ASSERT(pAttribute);
-
-  auto* ctx = static_cast<CFX_BmpContext*>(pContext);
-  Status status = ctx->m_Bmp.ReadHeader();
-  if (status != Status::kSuccess)
-    return status;
-
-  *width = ctx->m_Bmp.width();
-  *height = ctx->m_Bmp.height();
-  *tb_flag = ctx->m_Bmp.img_tb_flag();
-  *components = ctx->m_Bmp.components();
-  *pal_num = ctx->m_Bmp.pal_num();
-  *palette = ctx->m_Bmp.palette();
-  pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_METER;
-  pAttribute->m_nXDPI = ctx->m_Bmp.dpi_x();
-  pAttribute->m_nYDPI = ctx->m_Bmp.dpi_y();
-  return Status::kSuccess;
-}
-
-BmpModule::Status BmpModule::LoadImage(Context* pContext) {
-  return static_cast<CFX_BmpContext*>(pContext)->m_Bmp.DecodeImage();
-}
-
-FX_FILESIZE BmpModule::GetAvailInput(Context* pContext) const {
-  return static_cast<CFX_BmpContext*>(pContext)->m_Bmp.GetAvailInput();
-}
-
-bool BmpModule::Input(Context* pContext,
-                      RetainPtr<CFX_CodecMemory> codec_memory,
-                      CFX_DIBAttribute*) {
-  auto* ctx = static_cast<CFX_BmpContext*>(pContext);
-  ctx->m_Bmp.SetInputBuffer(std::move(codec_memory));
-  return true;
-}
-
-}  // namespace fxcodec
diff --git a/core/fxcodec/bmp/bmpmodule.h b/core/fxcodec/bmp/bmpmodule.h
deleted file mode 100644
index 5630128..0000000
--- a/core/fxcodec/bmp/bmpmodule.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_BMP_BMPMODULE_H_
-#define CORE_FXCODEC_BMP_BMPMODULE_H_
-
-#include <memory>
-#include <vector>
-
-#include "core/fxcodec/codec_module_iface.h"
-#include "third_party/base/span.h"
-
-namespace fxcodec {
-
-class CFX_DIBAttribute;
-
-class BmpModule final : public ModuleIface {
- public:
-  class Delegate {
-   public:
-    virtual bool BmpInputImagePositionBuf(uint32_t rcd_pos) = 0;
-    virtual void BmpReadScanline(uint32_t row_num,
-                                 pdfium::span<const uint8_t> row_buf) = 0;
-  };
-
-  enum class Status : uint8_t { kFail, kSuccess, kContinue };
-
-  BmpModule();
-  ~BmpModule() override;
-
-  // ModuleIface:
-  FX_FILESIZE GetAvailInput(Context* pContext) const override;
-  bool Input(Context* pContext,
-             RetainPtr<CFX_CodecMemory> codec_memory,
-             CFX_DIBAttribute* pAttribute) override;
-
-  std::unique_ptr<Context> Start(Delegate* pDelegate);
-  Status ReadHeader(Context* pContext,
-                    int32_t* width,
-                    int32_t* height,
-                    bool* tb_flag,
-                    int32_t* components,
-                    int32_t* pal_num,
-                    const std::vector<uint32_t>** palette,
-                    CFX_DIBAttribute* pAttribute);
-  Status LoadImage(Context* pContext);
-};
-
-}  // namespace fxcodec
-
-using BmpModule = fxcodec::BmpModule;
-
-#endif  // CORE_FXCODEC_BMP_BMPMODULE_H_
diff --git a/core/fxcodec/bmp/cfx_bmpcontext.cpp b/core/fxcodec/bmp/cfx_bmpcontext.cpp
index a35a585..3d79901 100644
--- a/core/fxcodec/bmp/cfx_bmpcontext.cpp
+++ b/core/fxcodec/bmp/cfx_bmpcontext.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,9 +8,8 @@
 
 namespace fxcodec {
 
-CFX_BmpContext::CFX_BmpContext(BmpModule* pModule,
-                               BmpModule::Delegate* pDelegate)
-    : m_Bmp(this), m_pModule(pModule), m_pDelegate(pDelegate) {}
+CFX_BmpContext::CFX_BmpContext(BmpDecoder::Delegate* pDelegate)
+    : m_Bmp(this), m_pDelegate(pDelegate) {}
 
 CFX_BmpContext::~CFX_BmpContext() = default;
 
diff --git a/core/fxcodec/bmp/cfx_bmpcontext.h b/core/fxcodec/bmp/cfx_bmpcontext.h
index e45caf9..1f4c5e6 100644
--- a/core/fxcodec/bmp/cfx_bmpcontext.h
+++ b/core/fxcodec/bmp/cfx_bmpcontext.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,21 +7,20 @@
 #ifndef CORE_FXCODEC_BMP_CFX_BMPCONTEXT_H_
 #define CORE_FXCODEC_BMP_CFX_BMPCONTEXT_H_
 
-#include "core/fxcodec/bmp/bmpmodule.h"
+#include "core/fxcodec/bmp/bmp_decoder.h"
 #include "core/fxcodec/bmp/cfx_bmpdecompressor.h"
 #include "core/fxcodec/bmp/fx_bmp.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 namespace fxcodec {
 
-class CFX_BmpContext final : public ModuleIface::Context {
+class CFX_BmpContext final : public ProgressiveDecoderIface::Context {
  public:
-  CFX_BmpContext(BmpModule* pModule, BmpModule::Delegate* pDelegate);
+  explicit CFX_BmpContext(BmpDecoder::Delegate* pDelegate);
   ~CFX_BmpContext() override;
 
   CFX_BmpDecompressor m_Bmp;
-  UnownedPtr<BmpModule> const m_pModule;
-  UnownedPtr<BmpModule::Delegate> const m_pDelegate;
+  UnownedPtr<BmpDecoder::Delegate> const m_pDelegate;
 };
 
 }  // namespace fxcodec
diff --git a/core/fxcodec/bmp/cfx_bmpdecompressor.cpp b/core/fxcodec/bmp/cfx_bmpdecompressor.cpp
index 7414647..b7fdc84 100644
--- a/core/fxcodec/bmp/cfx_bmpdecompressor.cpp
+++ b/core/fxcodec/bmp/cfx_bmpdecompressor.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,17 +6,19 @@
 
 #include "core/fxcodec/bmp/cfx_bmpdecompressor.h"
 
+#include <stdint.h>
+
 #include <algorithm>
 #include <limits>
 #include <utility>
 
 #include "core/fxcodec/bmp/cfx_bmpcontext.h"
 #include "core/fxcodec/cfx_codec_memory.h"
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/fx_system.h"
-#include "third_party/base/logging.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxge/calculate_pitch.h"
 #include "third_party/base/numerics/safe_math.h"
 
 namespace fxcodec {
@@ -35,7 +37,6 @@
               "BmpInfoHeader has wrong size");
 
 constexpr uint16_t kBmpSignature = 0x4D42;
-constexpr int32_t kBmpPalOld = 1;
 constexpr uint8_t kRleMarker = 0;
 constexpr uint8_t kRleEol = 0;
 constexpr uint8_t kRleEoi = 1;
@@ -55,7 +56,7 @@
 
 }  // namespace
 
-CFX_BmpDecompressor::CFX_BmpDecompressor(CFX_BmpContext* context)
+CFX_BmpDecompressor::CFX_BmpDecompressor(const CFX_BmpContext* context)
     : context_(context) {}
 
 CFX_BmpDecompressor::~CFX_BmpDecompressor() = default;
@@ -70,15 +71,15 @@
   return context_->m_pDelegate->BmpInputImagePositionBuf(rcd_pos);
 }
 
-BmpModule::Status CFX_BmpDecompressor::ReadHeader() {
+BmpDecoder::Status CFX_BmpDecompressor::ReadHeader() {
   if (decode_status_ == DecodeStatus::kHeader) {
-    BmpModule::Status status = ReadBmpHeader();
-    if (status != BmpModule::Status::kSuccess)
+    BmpDecoder::Status status = ReadBmpHeader();
+    if (status != BmpDecoder::Status::kSuccess)
       return status;
   }
 
   if (decode_status_ != DecodeStatus::kPal)
-    return BmpModule::Status::kSuccess;
+    return BmpDecoder::Status::kSuccess;
 
   if (compress_flag_ == kBmpBitfields)
     return ReadBmpBitfields();
@@ -86,131 +87,131 @@
   return ReadBmpPalette();
 }
 
-BmpModule::Status CFX_BmpDecompressor::ReadBmpHeader() {
+BmpDecoder::Status CFX_BmpDecompressor::ReadBmpHeader() {
   BmpFileHeader bmp_header;
-  if (!ReadData(reinterpret_cast<uint8_t*>(&bmp_header),
-                sizeof(BmpFileHeader))) {
-    return BmpModule::Status::kContinue;
+  if (!ReadAllOrNone(
+          pdfium::as_writable_bytes(pdfium::make_span(&bmp_header, 1)))) {
+    return BmpDecoder::Status::kContinue;
   }
 
   bmp_header.bfType =
-      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&bmp_header.bfType));
-  bmp_header.bfOffBits =
-      FXDWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&bmp_header.bfOffBits));
+      FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&bmp_header.bfType));
+  data_offset_ = FXSYS_UINT32_GET_LSBFIRST(
+      reinterpret_cast<uint8_t*>(&bmp_header.bfOffBits));
   data_size_ =
-      FXDWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&bmp_header.bfSize));
+      FXSYS_UINT32_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&bmp_header.bfSize));
   if (bmp_header.bfType != kBmpSignature)
-    return BmpModule::Status::kFail;
+    return BmpDecoder::Status::kFail;
 
   size_t pos = input_buffer_->GetPosition();
-  if (!ReadData(reinterpret_cast<uint8_t*>(&img_ifh_size_),
-                sizeof(img_ifh_size_))) {
-    return BmpModule::Status::kContinue;
+  if (!ReadAllOrNone(
+          pdfium::as_writable_bytes(pdfium::make_span(&img_ifh_size_, 1)))) {
+    return BmpDecoder::Status::kContinue;
   }
   if (!input_buffer_->Seek(pos))
-    return BmpModule::Status::kFail;
+    return BmpDecoder::Status::kFail;
 
   img_ifh_size_ =
-      FXDWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_ifh_size_));
-  pal_type_ = 0;
-  BmpModule::Status status = ReadBmpHeaderIfh();
-  if (status != BmpModule::Status::kSuccess)
+      FXSYS_UINT32_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_ifh_size_));
+  pal_type_ = PalType::kNew;
+  BmpDecoder::Status status = ReadBmpHeaderIfh();
+  if (status != BmpDecoder::Status::kSuccess)
     return status;
 
   return ReadBmpHeaderDimensions();
 }
 
-BmpModule::Status CFX_BmpDecompressor::ReadBmpHeaderIfh() {
+BmpDecoder::Status CFX_BmpDecompressor::ReadBmpHeaderIfh() {
   if (img_ifh_size_ == kBmpCoreHeaderSize) {
-    pal_type_ = 1;
+    pal_type_ = PalType::kOld;
     BmpCoreHeader bmp_core_header;
-    if (!ReadData(reinterpret_cast<uint8_t*>(&bmp_core_header),
-                  sizeof(BmpCoreHeader))) {
-      return BmpModule::Status::kContinue;
+    if (!ReadAllOrNone(pdfium::as_writable_bytes(
+            pdfium::make_span(&bmp_core_header, 1)))) {
+      return BmpDecoder::Status::kContinue;
     }
 
-    width_ = FXWORD_GET_LSBFIRST(
+    width_ = FXSYS_UINT16_GET_LSBFIRST(
         reinterpret_cast<uint8_t*>(&bmp_core_header.bcWidth));
-    height_ = FXWORD_GET_LSBFIRST(
+    height_ = FXSYS_UINT16_GET_LSBFIRST(
         reinterpret_cast<uint8_t*>(&bmp_core_header.bcHeight));
-    bit_counts_ = FXWORD_GET_LSBFIRST(
+    bit_counts_ = FXSYS_UINT16_GET_LSBFIRST(
         reinterpret_cast<uint8_t*>(&bmp_core_header.bcBitCount));
     compress_flag_ = kBmpRgb;
     img_tb_flag_ = false;
-    return BmpModule::Status::kSuccess;
+    return BmpDecoder::Status::kSuccess;
   }
 
   if (img_ifh_size_ == kBmpInfoHeaderSize) {
     BmpInfoHeader bmp_info_header;
-    if (!ReadData(reinterpret_cast<uint8_t*>(&bmp_info_header),
-                  sizeof(BmpInfoHeader))) {
-      return BmpModule::Status::kContinue;
+    if (!ReadAllOrNone(pdfium::as_writable_bytes(
+            pdfium::make_span(&bmp_info_header, 1)))) {
+      return BmpDecoder::Status::kContinue;
     }
 
-    width_ = FXDWORD_GET_LSBFIRST(
+    width_ = FXSYS_UINT32_GET_LSBFIRST(
         reinterpret_cast<uint8_t*>(&bmp_info_header.biWidth));
-    int32_t signed_height = FXDWORD_GET_LSBFIRST(
+    int32_t signed_height = FXSYS_UINT32_GET_LSBFIRST(
         reinterpret_cast<uint8_t*>(&bmp_info_header.biHeight));
-    bit_counts_ = FXWORD_GET_LSBFIRST(
+    bit_counts_ = FXSYS_UINT16_GET_LSBFIRST(
         reinterpret_cast<uint8_t*>(&bmp_info_header.biBitCount));
-    compress_flag_ = FXDWORD_GET_LSBFIRST(
+    compress_flag_ = FXSYS_UINT32_GET_LSBFIRST(
         reinterpret_cast<uint8_t*>(&bmp_info_header.biCompression));
-    color_used_ = FXDWORD_GET_LSBFIRST(
+    color_used_ = FXSYS_UINT32_GET_LSBFIRST(
         reinterpret_cast<uint8_t*>(&bmp_info_header.biClrUsed));
-    dpi_x_ = static_cast<int32_t>(FXDWORD_GET_LSBFIRST(
+    dpi_x_ = static_cast<int32_t>(FXSYS_UINT32_GET_LSBFIRST(
         reinterpret_cast<uint8_t*>(&bmp_info_header.biXPelsPerMeter)));
-    dpi_y_ = static_cast<int32_t>(FXDWORD_GET_LSBFIRST(
+    dpi_y_ = static_cast<int32_t>(FXSYS_UINT32_GET_LSBFIRST(
         reinterpret_cast<uint8_t*>(&bmp_info_header.biYPelsPerMeter)));
     if (!SetHeight(signed_height))
-      return BmpModule::Status::kFail;
-    return BmpModule::Status::kSuccess;
+      return BmpDecoder::Status::kFail;
+    return BmpDecoder::Status::kSuccess;
   }
 
   if (img_ifh_size_ <= sizeof(BmpInfoHeader))
-    return BmpModule::Status::kFail;
+    return BmpDecoder::Status::kFail;
 
   FX_SAFE_SIZE_T new_pos = input_buffer_->GetPosition();
   BmpInfoHeader bmp_info_header;
-  if (!ReadData(reinterpret_cast<uint8_t*>(&bmp_info_header),
-                sizeof(bmp_info_header))) {
-    return BmpModule::Status::kContinue;
+  if (!ReadAllOrNone(
+          pdfium::as_writable_bytes(pdfium::make_span(&bmp_info_header, 1)))) {
+    return BmpDecoder::Status::kContinue;
   }
 
   new_pos += img_ifh_size_;
   if (!new_pos.IsValid())
-    return BmpModule::Status::kFail;
+    return BmpDecoder::Status::kFail;
 
   if (!input_buffer_->Seek(new_pos.ValueOrDie()))
-    return BmpModule::Status::kContinue;
+    return BmpDecoder::Status::kContinue;
 
   uint16_t bi_planes;
-  width_ = FXDWORD_GET_LSBFIRST(
+  width_ = FXSYS_UINT32_GET_LSBFIRST(
       reinterpret_cast<uint8_t*>(&bmp_info_header.biWidth));
-  int32_t signed_height = FXDWORD_GET_LSBFIRST(
+  int32_t signed_height = FXSYS_UINT32_GET_LSBFIRST(
       reinterpret_cast<uint8_t*>(&bmp_info_header.biHeight));
-  bit_counts_ = FXWORD_GET_LSBFIRST(
+  bit_counts_ = FXSYS_UINT16_GET_LSBFIRST(
       reinterpret_cast<uint8_t*>(&bmp_info_header.biBitCount));
-  compress_flag_ = FXDWORD_GET_LSBFIRST(
+  compress_flag_ = FXSYS_UINT32_GET_LSBFIRST(
       reinterpret_cast<uint8_t*>(&bmp_info_header.biCompression));
-  color_used_ = FXDWORD_GET_LSBFIRST(
+  color_used_ = FXSYS_UINT32_GET_LSBFIRST(
       reinterpret_cast<uint8_t*>(&bmp_info_header.biClrUsed));
-  bi_planes = FXWORD_GET_LSBFIRST(
+  bi_planes = FXSYS_UINT16_GET_LSBFIRST(
       reinterpret_cast<uint8_t*>(&bmp_info_header.biPlanes));
-  dpi_x_ = FXDWORD_GET_LSBFIRST(
+  dpi_x_ = FXSYS_UINT32_GET_LSBFIRST(
       reinterpret_cast<uint8_t*>(&bmp_info_header.biXPelsPerMeter));
-  dpi_y_ = FXDWORD_GET_LSBFIRST(
+  dpi_y_ = FXSYS_UINT32_GET_LSBFIRST(
       reinterpret_cast<uint8_t*>(&bmp_info_header.biYPelsPerMeter));
   if (!SetHeight(signed_height))
-    return BmpModule::Status::kFail;
+    return BmpDecoder::Status::kFail;
   if (compress_flag_ != kBmpRgb || bi_planes != 1 || color_used_ != 0)
-    return BmpModule::Status::kFail;
-  return BmpModule::Status::kSuccess;
+    return BmpDecoder::Status::kFail;
+  return BmpDecoder::Status::kSuccess;
 }
 
-BmpModule::Status CFX_BmpDecompressor::ReadBmpHeaderDimensions() {
+BmpDecoder::Status CFX_BmpDecompressor::ReadBmpHeaderDimensions() {
   if (width_ > kBmpMaxImageDimension || height_ > kBmpMaxImageDimension ||
       compress_flag_ > kBmpBitfields) {
-    return BmpModule::Status::kFail;
+    return BmpDecoder::Status::kFail;
   }
 
   switch (bit_counts_) {
@@ -220,35 +221,35 @@
     case 16:
     case 24: {
       if (color_used_ > 1U << bit_counts_)
-        return BmpModule::Status::kFail;
+        return BmpDecoder::Status::kFail;
       break;
     }
     case 32:
       break;
     default:
-      return BmpModule::Status::kFail;
+      return BmpDecoder::Status::kFail;
   }
-  FX_SAFE_UINT32 stride = CalculatePitch32(bit_counts_, width_);
-  if (!stride.IsValid())
-    return BmpModule::Status::kFail;
+  absl::optional<uint32_t> stride = fxge::CalculatePitch32(bit_counts_, width_);
+  if (!stride.has_value())
+    return BmpDecoder::Status::kFail;
 
-  src_row_bytes_ = stride.ValueOrDie();
+  src_row_bytes_ = stride.value();
   switch (bit_counts_) {
     case 1:
     case 4:
     case 8:
-      stride = CalculatePitch32(8, width_);
-      if (!stride.IsValid())
-        return BmpModule::Status::kFail;
-      out_row_bytes_ = stride.ValueOrDie();
+      stride = fxge::CalculatePitch32(8, width_);
+      if (!stride.has_value())
+        return BmpDecoder::Status::kFail;
+      out_row_bytes_ = stride.value();
       components_ = 1;
       break;
     case 16:
     case 24:
-      stride = CalculatePitch32(24, width_);
-      if (!stride.IsValid())
-        return BmpModule::Status::kFail;
-      out_row_bytes_ = stride.ValueOrDie();
+      stride = fxge::CalculatePitch32(24, width_);
+      if (!stride.has_value())
+        return BmpDecoder::Status::kFail;
+      out_row_bytes_ = stride.value();
       components_ = 3;
       break;
     case 32:
@@ -259,34 +260,35 @@
   out_row_buffer_.clear();
 
   if (out_row_bytes_ <= 0)
-    return BmpModule::Status::kFail;
+    return BmpDecoder::Status::kFail;
 
   out_row_buffer_.resize(out_row_bytes_);
   SaveDecodingStatus(DecodeStatus::kPal);
-  return BmpModule::Status::kSuccess;
+  return BmpDecoder::Status::kSuccess;
 }
 
-BmpModule::Status CFX_BmpDecompressor::ReadBmpBitfields() {
+BmpDecoder::Status CFX_BmpDecompressor::ReadBmpBitfields() {
   if (bit_counts_ != 16 && bit_counts_ != 32)
-    return BmpModule::Status::kFail;
+    return BmpDecoder::Status::kFail;
 
   uint32_t masks[3];
-  if (!ReadData(reinterpret_cast<uint8_t*>(masks), sizeof(masks)))
-    return BmpModule::Status::kContinue;
+  if (!ReadAllOrNone(pdfium::as_writable_bytes(pdfium::make_span(masks))))
+    return BmpDecoder::Status::kContinue;
 
-  mask_red_ = FXDWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&masks[0]));
-  mask_green_ = FXDWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&masks[1]));
-  mask_blue_ = FXDWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&masks[2]));
+  mask_red_ = FXSYS_UINT32_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&masks[0]));
+  mask_green_ =
+      FXSYS_UINT32_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&masks[1]));
+  mask_blue_ = FXSYS_UINT32_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&masks[2]));
   if (mask_red_ & mask_green_ || mask_red_ & mask_blue_ ||
       mask_green_ & mask_blue_) {
-    return BmpModule::Status::kFail;
+    return BmpDecoder::Status::kFail;
   }
   header_offset_ = std::max(header_offset_, 26 + img_ifh_size_);
   SaveDecodingStatus(DecodeStatus::kDataPre);
-  return BmpModule::Status::kSuccess;
+  return BmpDecoder::Status::kSuccess;
 }
 
-BmpModule::Status CFX_BmpDecompressor::ReadBmpPalette() {
+BmpDecoder::Status CFX_BmpDecompressor::ReadBmpPalette() {
   if (bit_counts_ == 16) {
     mask_red_ = 0x7C00;
     mask_green_ = 0x03E0;
@@ -297,15 +299,15 @@
     pal_num_ = 1 << bit_counts_;
     if (color_used_ != 0)
       pal_num_ = color_used_;
-    uint32_t src_pal_size = pal_num_ * (pal_type_ ? 3 : 4);
-    std::vector<uint8_t, FxAllocAllocator<uint8_t>> src_pal(src_pal_size);
+    size_t src_pal_size = pal_num_ * PaletteChannelCount();
+    DataVector<uint8_t> src_pal(src_pal_size);
     uint8_t* src_pal_data = src_pal.data();
-    if (!ReadData(src_pal_data, src_pal_size))
-      return BmpModule::Status::kContinue;
+    if (!ReadAllOrNone(src_pal))
+      return BmpDecoder::Status::kContinue;
 
     palette_.resize(pal_num_);
     int32_t src_pal_index = 0;
-    if (pal_type_ == kBmpPalOld) {
+    if (pal_type_ == PalType::kOld) {
       while (src_pal_index < pal_num_) {
         palette_[src_pal_index++] = BMP_PAL_ENCODE(
             0x00, src_pal_data[2], src_pal_data[1], src_pal_data[0]);
@@ -320,9 +322,9 @@
     }
   }
   header_offset_ = std::max(
-      header_offset_, 14 + img_ifh_size_ + pal_num_ * (pal_type_ ? 3 : 4));
+      header_offset_, 14 + img_ifh_size_ + pal_num_ * PaletteChannelCount());
   SaveDecodingStatus(DecodeStatus::kDataPre);
-  return BmpModule::Status::kSuccess;
+  return BmpDecoder::Status::kSuccess;
 }
 
 bool CFX_BmpDecompressor::ValidateFlag() const {
@@ -337,19 +339,23 @@
   }
 }
 
-BmpModule::Status CFX_BmpDecompressor::DecodeImage() {
+BmpDecoder::Status CFX_BmpDecompressor::DecodeImage() {
   if (decode_status_ == DecodeStatus::kDataPre) {
-    input_buffer_->Seek(0);
-    if (!GetDataPosition(header_offset_)) {
+    // In order to tolerate certain corrupt BMP files, use the header offset if
+    // the data offset would point into the header.
+    data_offset_ = std::max(header_offset_, data_offset_);
+
+    input_buffer_->Seek(input_buffer_->GetSize());
+    if (!GetDataPosition(data_offset_)) {
       decode_status_ = DecodeStatus::kTail;
-      return BmpModule::Status::kFail;
+      return BmpDecoder::Status::kFail;
     }
 
     row_num_ = 0;
     SaveDecodingStatus(DecodeStatus::kData);
   }
   if (decode_status_ != DecodeStatus::kData || !ValidateFlag())
-    return BmpModule::Status::kFail;
+    return BmpDecoder::Status::kFail;
 
   switch (compress_flag_) {
     case kBmpRgb:
@@ -360,7 +366,7 @@
     case kBmpRle4:
       return DecodeRLE4();
     default:
-      return BmpModule::Status::kFail;
+      return BmpDecoder::Status::kFail;
   }
 }
 
@@ -368,26 +374,41 @@
   return val < pal_num_;
 }
 
-BmpModule::Status CFX_BmpDecompressor::DecodeRGB() {
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> dest_buf(src_row_bytes_);
+BmpDecoder::Status CFX_BmpDecompressor::DecodeRGB() {
+  DataVector<uint8_t> dest_buf(src_row_bytes_);
   while (row_num_ < height_) {
     size_t idx = 0;
-    if (!ReadData(dest_buf.data(), src_row_bytes_))
-      return BmpModule::Status::kContinue;
+    if (!ReadAllOrNone(dest_buf))
+      return BmpDecoder::Status::kContinue;
 
     SaveDecodingStatus(DecodeStatus::kData);
     switch (bit_counts_) {
       case 1: {
-        for (uint32_t col = 0; col < width_; ++col)
-          out_row_buffer_[idx++] =
+        for (uint32_t col = 0; col < width_; ++col) {
+          uint8_t index =
               dest_buf[col >> 3] & (0x80 >> (col % 8)) ? 0x01 : 0x00;
+          if (!ValidateColorIndex(index))
+            return BmpDecoder::Status::kFail;
+          out_row_buffer_[idx++] = index;
+        }
         break;
       }
       case 4: {
         for (uint32_t col = 0; col < width_; ++col) {
-          out_row_buffer_[idx++] = (col & 0x01)
-                                       ? (dest_buf[col >> 1] & 0x0F)
+          uint8_t index = (col & 0x01) ? (dest_buf[col >> 1] & 0x0F)
                                        : ((dest_buf[col >> 1] & 0xF0) >> 4);
+          if (!ValidateColorIndex(index))
+            return BmpDecoder::Status::kFail;
+          out_row_buffer_[idx++] = index;
+        }
+        break;
+      }
+      case 8: {
+        for (uint32_t col = 0; col < width_; ++col) {
+          uint8_t index = dest_buf[col];
+          if (!ValidateColorIndex(index))
+            return BmpDecoder::Status::kFail;
+          out_row_buffer_[idx++] = index;
         }
         break;
       }
@@ -407,12 +428,12 @@
         green_bits += blue_bits;
         red_bits += green_bits;
         if (blue_bits > 8 || green_bits < 8 || red_bits < 8)
-          return BmpModule::Status::kContinue;
+          return BmpDecoder::Status::kContinue;
         blue_bits = 8 - blue_bits;
         green_bits -= 8;
         red_bits -= 8;
         for (uint32_t col = 0; col < width_; ++col) {
-          *buf = FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(buf));
+          *buf = FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(buf));
           out_row_buffer_[idx++] =
               static_cast<uint8_t>((*buf & mask_blue_) << blue_bits);
           out_row_buffer_[idx++] =
@@ -422,47 +443,42 @@
         }
         break;
       }
-      case 8:
       case 24:
       case 32:
-        uint8_t* dest_buf_data = dest_buf.data();
-        std::copy(dest_buf_data, dest_buf_data + src_row_bytes_,
-                  out_row_buffer_.begin());
+        // TODO(crbug.com/pdfium/1901): Apply bitfields.
+        fxcrt::spancpy(pdfium::make_span(out_row_buffer_),
+                       pdfium::make_span(dest_buf).first(src_row_bytes_));
         idx += src_row_bytes_;
         break;
     }
-    for (uint8_t byte : out_row_buffer_) {
-      if (!ValidateColorIndex(byte))
-        return BmpModule::Status::kFail;
-    }
     ReadNextScanline();
   }
   SaveDecodingStatus(DecodeStatus::kTail);
-  return BmpModule::Status::kSuccess;
+  return BmpDecoder::Status::kSuccess;
 }
 
-BmpModule::Status CFX_BmpDecompressor::DecodeRLE8() {
+BmpDecoder::Status CFX_BmpDecompressor::DecodeRLE8() {
   uint8_t first_part;
   col_num_ = 0;
   while (true) {
-    if (!ReadData(&first_part, sizeof(first_part)))
-      return BmpModule::Status::kContinue;
+    if (!ReadAllOrNone(pdfium::make_span(&first_part, 1)))
+      return BmpDecoder::Status::kContinue;
 
     switch (first_part) {
       case kRleMarker: {
-        if (!ReadData(&first_part, sizeof(first_part)))
-          return BmpModule::Status::kContinue;
+        if (!ReadAllOrNone(pdfium::make_span(&first_part, 1)))
+          return BmpDecoder::Status::kContinue;
 
         switch (first_part) {
           case kRleEol: {
             if (row_num_ >= height_) {
               SaveDecodingStatus(DecodeStatus::kTail);
-              return BmpModule::Status::kFail;
+              return BmpDecoder::Status::kFail;
             }
 
             ReadNextScanline();
             col_num_ = 0;
-            std::fill(out_row_buffer_.begin(), out_row_buffer_.end(), 0);
+            fxcrt::spanset(pdfium::make_span(out_row_buffer_), 0);
             SaveDecodingStatus(DecodeStatus::kData);
             continue;
           }
@@ -470,42 +486,42 @@
             if (row_num_ < height_)
               ReadNextScanline();
             SaveDecodingStatus(DecodeStatus::kTail);
-            return BmpModule::Status::kSuccess;
+            return BmpDecoder::Status::kSuccess;
           }
           case kRleDelta: {
             uint8_t delta[2];
-            if (!ReadData(delta, sizeof(delta)))
-              return BmpModule::Status::kContinue;
+            if (!ReadAllOrNone(delta))
+              return BmpDecoder::Status::kContinue;
 
             col_num_ += delta[0];
             size_t bmp_row_num__next = row_num_ + delta[1];
             if (col_num_ >= out_row_bytes_ || bmp_row_num__next >= height_)
-              return BmpModule::Status::kFail;
+              return BmpDecoder::Status::kFail;
 
             while (row_num_ < bmp_row_num__next) {
-              std::fill(out_row_buffer_.begin(), out_row_buffer_.end(), 0);
+              fxcrt::spanset(pdfium::make_span(out_row_buffer_), 0);
               ReadNextScanline();
             }
             break;
           }
           default: {
-            int32_t avail_size = out_row_bytes_ - col_num_;
+            int32_t avail_size =
+                pdfium::base::checked_cast<int32_t>(out_row_bytes_ - col_num_);
             if (!avail_size || static_cast<int32_t>(first_part) > avail_size)
-              return BmpModule::Status::kFail;
+              return BmpDecoder::Status::kFail;
 
             size_t second_part_size =
                 first_part & 1 ? first_part + 1 : first_part;
-            std::vector<uint8_t, FxAllocAllocator<uint8_t>> second_part(
-                second_part_size);
-            uint8_t* second_part_data = second_part.data();
-            if (!ReadData(second_part_data, second_part_size))
-              return BmpModule::Status::kContinue;
+            DataVector<uint8_t> second_part(second_part_size);
+            if (!ReadAllOrNone(second_part))
+              return BmpDecoder::Status::kContinue;
 
-            std::copy(second_part_data, second_part_data + first_part,
-                      out_row_buffer_.begin() + col_num_);
+            fxcrt::spancpy(pdfium::make_span(out_row_buffer_).subspan(col_num_),
+                           pdfium::make_span(second_part).first(first_part));
+
             for (size_t i = col_num_; i < col_num_ + first_part; ++i) {
               if (!ValidateColorIndex(out_row_buffer_[i]))
-                return BmpModule::Status::kFail;
+                return BmpDecoder::Status::kFail;
             }
             col_num_ += first_part;
           }
@@ -513,47 +529,49 @@
         break;
       }
       default: {
-        int32_t avail_size = out_row_bytes_ - col_num_;
+        int32_t avail_size =
+            pdfium::base::checked_cast<int32_t>(out_row_bytes_ - col_num_);
         if (!avail_size || static_cast<int32_t>(first_part) > avail_size)
-          return BmpModule::Status::kFail;
+          return BmpDecoder::Status::kFail;
 
         uint8_t second_part;
-        if (!ReadData(&second_part, sizeof(second_part)))
-          return BmpModule::Status::kContinue;
+        if (!ReadAllOrNone(pdfium::make_span(&second_part, 1)))
+          return BmpDecoder::Status::kContinue;
 
-        std::fill(out_row_buffer_.begin() + col_num_,
-                  out_row_buffer_.begin() + col_num_ + first_part, second_part);
+        fxcrt::spanset(
+            pdfium::make_span(out_row_buffer_).subspan(col_num_, first_part),
+            second_part);
+
         if (!ValidateColorIndex(out_row_buffer_[col_num_]))
-          return BmpModule::Status::kFail;
+          return BmpDecoder::Status::kFail;
         col_num_ += first_part;
       }
     }
   }
-  return BmpModule::Status::kFail;
 }
 
-BmpModule::Status CFX_BmpDecompressor::DecodeRLE4() {
+BmpDecoder::Status CFX_BmpDecompressor::DecodeRLE4() {
   uint8_t first_part;
   col_num_ = 0;
   while (true) {
-    if (!ReadData(&first_part, sizeof(first_part)))
-      return BmpModule::Status::kContinue;
+    if (!ReadAllOrNone(pdfium::make_span(&first_part, 1)))
+      return BmpDecoder::Status::kContinue;
 
     switch (first_part) {
       case kRleMarker: {
-        if (!ReadData(&first_part, sizeof(first_part)))
-          return BmpModule::Status::kContinue;
+        if (!ReadAllOrNone(pdfium::make_span(&first_part, 1)))
+          return BmpDecoder::Status::kContinue;
 
         switch (first_part) {
           case kRleEol: {
             if (row_num_ >= height_) {
               SaveDecodingStatus(DecodeStatus::kTail);
-              return BmpModule::Status::kFail;
+              return BmpDecoder::Status::kFail;
             }
 
             ReadNextScanline();
             col_num_ = 0;
-            std::fill(out_row_buffer_.begin(), out_row_buffer_.end(), 0);
+            fxcrt::spanset(pdfium::make_span(out_row_buffer_), 0);
             SaveDecodingStatus(DecodeStatus::kData);
             continue;
           }
@@ -561,47 +579,47 @@
             if (row_num_ < height_)
               ReadNextScanline();
             SaveDecodingStatus(DecodeStatus::kTail);
-            return BmpModule::Status::kSuccess;
+            return BmpDecoder::Status::kSuccess;
           }
           case kRleDelta: {
             uint8_t delta[2];
-            if (!ReadData(delta, sizeof(delta)))
-              return BmpModule::Status::kContinue;
+            if (!ReadAllOrNone(delta))
+              return BmpDecoder::Status::kContinue;
 
             col_num_ += delta[0];
             size_t bmp_row_num__next = row_num_ + delta[1];
             if (col_num_ >= out_row_bytes_ || bmp_row_num__next >= height_)
-              return BmpModule::Status::kFail;
+              return BmpDecoder::Status::kFail;
 
             while (row_num_ < bmp_row_num__next) {
-              std::fill(out_row_buffer_.begin(), out_row_buffer_.end(), 0);
+              fxcrt::spanset(pdfium::make_span(out_row_buffer_), 0);
               ReadNextScanline();
             }
             break;
           }
           default: {
-            int32_t avail_size = out_row_bytes_ - col_num_;
+            int32_t avail_size =
+                pdfium::base::checked_cast<int32_t>(out_row_bytes_ - col_num_);
             if (!avail_size)
-              return BmpModule::Status::kFail;
+              return BmpDecoder::Status::kFail;
             uint8_t size = HalfRoundUp(first_part);
             if (static_cast<int32_t>(first_part) > avail_size) {
               if (size + (col_num_ >> 1) > src_row_bytes_)
-                return BmpModule::Status::kFail;
+                return BmpDecoder::Status::kFail;
 
               first_part = avail_size - 1;
             }
             size_t second_part_size = size & 1 ? size + 1 : size;
-            std::vector<uint8_t, FxAllocAllocator<uint8_t>> second_part(
-                second_part_size);
+            DataVector<uint8_t> second_part(second_part_size);
             uint8_t* second_part_data = second_part.data();
-            if (!ReadData(second_part_data, second_part_size))
-              return BmpModule::Status::kContinue;
+            if (!ReadAllOrNone(second_part))
+              return BmpDecoder::Status::kContinue;
 
             for (uint8_t i = 0; i < first_part; i++) {
               uint8_t color = (i & 0x01) ? (*second_part_data++ & 0x0F)
                                          : (*second_part_data & 0xF0) >> 4;
               if (!ValidateColorIndex(color))
-                return BmpModule::Status::kFail;
+                return BmpDecoder::Status::kFail;
 
               out_row_buffer_[col_num_++] = color;
             }
@@ -610,38 +628,48 @@
         break;
       }
       default: {
-        int32_t avail_size = out_row_bytes_ - col_num_;
+        int32_t avail_size =
+            pdfium::base::checked_cast<int32_t>(out_row_bytes_ - col_num_);
         if (!avail_size)
-          return BmpModule::Status::kFail;
+          return BmpDecoder::Status::kFail;
 
         if (static_cast<int32_t>(first_part) > avail_size) {
           uint8_t size = HalfRoundUp(first_part);
           if (size + (col_num_ >> 1) > src_row_bytes_)
-            return BmpModule::Status::kFail;
+            return BmpDecoder::Status::kFail;
 
           first_part = avail_size - 1;
         }
         uint8_t second_part;
-        if (!ReadData(&second_part, sizeof(second_part)))
-          return BmpModule::Status::kContinue;
+        if (!ReadAllOrNone(pdfium::make_span(&second_part, 1)))
+          return BmpDecoder::Status::kContinue;
 
         for (uint8_t i = 0; i < first_part; i++) {
           uint8_t second_byte = second_part;
           second_byte =
               i & 0x01 ? (second_byte & 0x0F) : (second_byte & 0xF0) >> 4;
           if (!ValidateColorIndex(second_byte))
-            return BmpModule::Status::kFail;
+            return BmpDecoder::Status::kFail;
 
           out_row_buffer_[col_num_++] = second_byte;
         }
       }
     }
   }
-  return BmpModule::Status::kFail;
 }
 
-bool CFX_BmpDecompressor::ReadData(uint8_t* destination, uint32_t size) {
-  return input_buffer_ && input_buffer_->ReadBlock(destination, size) == size;
+bool CFX_BmpDecompressor::ReadAllOrNone(pdfium::span<uint8_t> buf) {
+  if (!input_buffer_)
+    return false;
+
+  size_t original_position = input_buffer_->GetPosition();
+  size_t read = input_buffer_->ReadBlock(buf);
+  if (read < buf.size()) {
+    input_buffer_->Seek(original_position);
+    return false;
+  }
+
+  return true;
 }
 
 void CFX_BmpDecompressor::SaveDecodingStatus(DecodeStatus status) {
diff --git a/core/fxcodec/bmp/cfx_bmpdecompressor.h b/core/fxcodec/bmp/cfx_bmpdecompressor.h
index 3f4e2d8..6432777 100644
--- a/core/fxcodec/bmp/cfx_bmpdecompressor.h
+++ b/core/fxcodec/bmp/cfx_bmpdecompressor.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,16 @@
 #ifndef CORE_FXCODEC_BMP_CFX_BMPDECOMPRESSOR_H_
 #define CORE_FXCODEC_BMP_CFX_BMPDECOMPRESSOR_H_
 
+#include <stdint.h>
+
 #include <vector>
 
-#include "core/fxcodec/bmp/bmpmodule.h"
+#include "core/fxcodec/bmp/bmp_decoder.h"
 #include "core/fxcodec/bmp/fx_bmp.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_CodecMemory;
 
@@ -22,11 +26,11 @@
 
 class CFX_BmpDecompressor {
  public:
-  explicit CFX_BmpDecompressor(CFX_BmpContext* context);
+  explicit CFX_BmpDecompressor(const CFX_BmpContext* context);
   ~CFX_BmpDecompressor();
 
-  BmpModule::Status DecodeImage();
-  BmpModule::Status ReadHeader();
+  BmpDecoder::Status DecodeImage();
+  BmpDecoder::Status ReadHeader();
   void SetInputBuffer(RetainPtr<CFX_CodecMemory> codec_memory);
   FX_FILESIZE GetAvailInput() const;
 
@@ -48,24 +52,27 @@
     kTail,
   };
 
-  BmpModule::Status ReadBmpHeader();
-  BmpModule::Status ReadBmpHeaderIfh();
-  BmpModule::Status ReadBmpHeaderDimensions();
-  BmpModule::Status ReadBmpBitfields();
-  BmpModule::Status ReadBmpPalette();
+  enum class PalType : bool { kNew, kOld };
+
+  BmpDecoder::Status ReadBmpHeader();
+  BmpDecoder::Status ReadBmpHeaderIfh();
+  BmpDecoder::Status ReadBmpHeaderDimensions();
+  BmpDecoder::Status ReadBmpBitfields();
+  BmpDecoder::Status ReadBmpPalette();
   bool GetDataPosition(uint32_t cur_pos);
   void ReadNextScanline();
-  BmpModule::Status DecodeRGB();
-  BmpModule::Status DecodeRLE8();
-  BmpModule::Status DecodeRLE4();
-  bool ReadData(uint8_t* destination, uint32_t size);
+  BmpDecoder::Status DecodeRGB();
+  BmpDecoder::Status DecodeRLE8();
+  BmpDecoder::Status DecodeRLE4();
+  bool ReadAllOrNone(pdfium::span<uint8_t> buf);
   void SaveDecodingStatus(DecodeStatus status);
   bool ValidateColorIndex(uint8_t val) const;
   bool ValidateFlag() const;
   bool SetHeight(int32_t signed_height);
+  int PaletteChannelCount() const { return pal_type_ == PalType::kNew ? 4 : 3; }
 
-  UnownedPtr<CFX_BmpContext> const context_;
-  std::vector<uint8_t> out_row_buffer_;
+  UnownedPtr<const CFX_BmpContext> const context_;
+  DataVector<uint8_t> out_row_buffer_;
   std::vector<uint32_t> palette_;
   uint32_t header_offset_ = 0;
   uint32_t width_ = 0;
@@ -78,7 +85,8 @@
   uint16_t bit_counts_ = 0;
   uint32_t color_used_ = 0;
   int32_t pal_num_ = 0;
-  int32_t pal_type_ = 0;
+  PalType pal_type_ = PalType::kNew;
+  uint32_t data_offset_ = 0;
   uint32_t data_size_ = 0;
   uint32_t img_ifh_size_ = 0;
   uint32_t row_num_ = 0;
diff --git a/core/fxcodec/bmp/fx_bmp.h b/core/fxcodec/bmp/fx_bmp.h
index 79c61bb..ae5cda1 100644
--- a/core/fxcodec/bmp/fx_bmp.h
+++ b/core/fxcodec/bmp/fx_bmp.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxcodec/cfx_codec_memory.cpp b/core/fxcodec/cfx_codec_memory.cpp
index 256e15c..8ac15d5 100644
--- a/core/fxcodec/cfx_codec_memory.cpp
+++ b/core/fxcodec/cfx_codec_memory.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include <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) {}
 
@@ -19,12 +21,12 @@
   return true;
 }
 
-size_t CFX_CodecMemory::ReadBlock(void* buffer, size_t size) {
-  if (!buffer || !size || IsEOF())
+size_t CFX_CodecMemory::ReadBlock(pdfium::span<uint8_t> buffer) {
+  if (buffer.empty() || IsEOF())
     return 0;
 
-  size_t bytes_to_read = std::min(size, size_ - pos_);
-  memcpy(buffer, buffer_.get() + pos_, bytes_to_read);
+  size_t bytes_to_read = std::min(buffer.size(), size_ - pos_);
+  fxcrt::spancpy(buffer, GetBufferSpan().subspan(pos_, bytes_to_read));
   pos_ += bytes_to_read;
   return bytes_to_read;
 }
@@ -42,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 57c1ae6..2076311 100644
--- a/core/fxcodec/cfx_codec_memory.h
+++ b/core/fxcodec/cfx_codec_memory.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,19 +9,22 @@
 
 #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:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
-  pdfium::span<uint8_t> GetSpan() { return {buffer_.get(), size_}; }
-  uint8_t* GetBuffer() { return buffer_.get(); }
+  // Returns a span over the unconsumed contents of the buffer.
+  pdfium::span<uint8_t> GetUnconsumedSpan() {
+    return GetBufferSpan().subspan(pos_);
+  }
+
+  pdfium::span<uint8_t> GetBufferSpan() { return {buffer_.get(), size_}; }
   size_t GetSize() const { return size_; }
   size_t GetPosition() const { return pos_; }
   bool IsEOF() const { return pos_ >= size_; }
-  size_t ReadBlock(void* buffer, size_t size);
+  size_t ReadBlock(pdfium::span<uint8_t> buffer);
 
   // Sets the cursor position to |pos| if possible.
   bool Seek(size_t pos);
diff --git a/core/fxcodec/codec_module_iface.h b/core/fxcodec/codec_module_iface.h
deleted file mode 100644
index 992d2ad..0000000
--- a/core/fxcodec/codec_module_iface.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_CODEC_MODULE_IFACE_H_
-#define CORE_FXCODEC_CODEC_MODULE_IFACE_H_
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/retain_ptr.h"
-
-class CFX_CodecMemory;
-
-namespace fxcodec {
-
-class CFX_DIBAttribute;
-
-class ModuleIface {
- public:
-  class Context {
-   public:
-    virtual ~Context() = default;
-  };
-
-  virtual ~ModuleIface() = default;
-
-  // Returns the number of unprocessed bytes remaining in the input buffer.
-  virtual FX_FILESIZE GetAvailInput(Context* pContext) const = 0;
-
-  // Provides a new input buffer to the codec. Returns true on success,
-  // setting details about the image extracted from the buffer into |pAttribute|
-  // (if provided and the codec is capable providing that information).
-  virtual bool Input(Context* pContext,
-                     RetainPtr<CFX_CodecMemory> codec_memory,
-                     CFX_DIBAttribute* pAttribute) = 0;
-};
-
-}  // namespace fxcodec
-
-using fxcodec::ModuleIface;
-
-#endif  // CORE_FXCODEC_CODEC_MODULE_IFACE_H_
diff --git a/core/fxcodec/fax/faxmodule.cpp b/core/fxcodec/fax/faxmodule.cpp
index 22ba764..a904186 100644
--- a/core/fxcodec/fax/faxmodule.cpp
+++ b/core/fxcodec/fax/faxmodule.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,19 +6,29 @@
 
 #include "core/fxcodec/fax/faxmodule.h"
 
+#include <stdint.h>
+
 #include <algorithm>
 #include <iterator>
 #include <memory>
-#include <vector>
+#include <utility>
 
 #include "build/build_config.h"
-#include "core/fxcodec/fx_codec.h"
 #include "core/fxcodec/scanlinedecoder.h"
-#include "core/fxcrt/cfx_binarybuf.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/binary_buffer.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_2d_size.h"
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxge/calculate_pitch.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/span.h"
+#include "third_party/base/numerics/safe_conversions.h"
+
+#if BUILDFLAG(IS_WIN)
+#include "core/fxcrt/span_util.h"
+#include "core/fxge/dib/cfx_dibbase.h"
+#endif
 
 namespace fxcodec {
 
@@ -44,8 +54,11 @@
 constexpr int kFaxBpc = 1;
 constexpr int kFaxComps = 1;
 
-int FindBit(const uint8_t* data_buf, int max_pos, int start_pos, bool bit) {
-  ASSERT(start_pos >= 0);
+int FindBit(pdfium::span<const uint8_t> data_buf,
+            int max_pos,
+            int start_pos,
+            bool bit) {
+  DCHECK(start_pos >= 0);
   if (start_pos >= max_pos)
     return max_pos;
 
@@ -72,7 +85,8 @@
         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
     const uint8_t* skip_block = bit ? skip_block_0 : skip_block_1;
     while (byte_pos < max_byte - kBulkReadSize &&
-           memcmp(data_buf + byte_pos, skip_block, kBulkReadSize) == 0) {
+           memcmp(data_buf.subspan(byte_pos).data(), skip_block,
+                  kBulkReadSize) == 0) {
       byte_pos += kBulkReadSize;
     }
   }
@@ -94,25 +108,25 @@
                    int* b1,
                    int* b2) {
   bool first_bit = a0 < 0 || (ref_buf[a0 / 8] & (1 << (7 - a0 % 8))) != 0;
-  *b1 = FindBit(ref_buf.data(), columns, a0 + 1, !first_bit);
+  *b1 = FindBit(ref_buf, columns, a0 + 1, !first_bit);
   if (*b1 >= columns) {
     *b1 = *b2 = columns;
     return;
   }
   if (first_bit == !a0color) {
-    *b1 = FindBit(ref_buf.data(), columns, *b1 + 1, first_bit);
+    *b1 = FindBit(ref_buf, columns, *b1 + 1, first_bit);
     first_bit = !first_bit;
   }
   if (*b1 >= columns) {
     *b1 = *b2 = columns;
     return;
   }
-  *b2 = FindBit(ref_buf.data(), columns, *b1 + 1, first_bit);
+  *b2 = FindBit(ref_buf, columns, *b1 + 1, first_bit);
 }
 
 void FaxFillBits(uint8_t* dest_buf, int columns, int startpos, int endpos) {
   startpos = std::max(startpos, 0);
-  endpos = pdfium::clamp(endpos, 0, columns);
+  endpos = std::clamp(endpos, 0, columns);
   if (startpos >= endpos)
     return;
 
@@ -138,7 +152,7 @@
   return !!(src_buf[pos / 8] & (1 << (7 - pos % 8)));
 }
 
-const uint8_t FaxBlackRunIns[] = {
+const uint8_t kFaxBlackRunIns[] = {
     0,          2,          0x02,       3,          0,          0x03,
     2,          0,          2,          0x02,       1,          0,
     0x03,       4,          0,          2,          0x02,       6,
@@ -195,7 +209,7 @@
     1088 / 256, 0x76,       1152 % 256, 1152 / 256, 0x77,       1216 % 256,
     1216 / 256, 0xff};
 
-const uint8_t FaxWhiteRunIns[] = {
+const uint8_t kFaxWhiteRunIns[] = {
     0,          0,          0,          6,          0x07,       2,
     0,          0x08,       3,          0,          0x0B,       4,
     0,          0x0C,       5,          0,          0x0E,       6,
@@ -253,13 +267,13 @@
     0xff,
 };
 
-int FaxGetRun(const uint8_t* ins_array,
+int FaxGetRun(pdfium::span<const uint8_t> ins_array,
               const uint8_t* src_buf,
               int* bitpos,
               int bitsize) {
   uint32_t code = 0;
   int ins_off = 0;
-  while (1) {
+  while (true) {
     uint8_t ins = ins_array[ins_off++];
     if (ins == 0xff)
       return -1;
@@ -288,7 +302,7 @@
                  int columns) {
   int a0 = -1;
   bool a0color = true;
-  while (1) {
+  while (true) {
     if (*bitpos >= bitsize)
       return;
 
@@ -312,8 +326,9 @@
         v_delta = bit2 ? 1 : -1;
       } else if (bit2) {
         int run_len1 = 0;
-        while (1) {
-          int run = FaxGetRun(a0color ? FaxWhiteRunIns : FaxBlackRunIns,
+        while (true) {
+          int run = FaxGetRun(a0color ? pdfium::make_span(kFaxWhiteRunIns)
+                                      : pdfium::make_span(kFaxBlackRunIns),
                               src_buf, bitpos, bitsize);
           run_len1 += run;
           if (run < 64)
@@ -329,8 +344,9 @@
           FaxFillBits(dest_buf, columns, a0, a1);
 
         int run_len2 = 0;
-        while (1) {
-          int run = FaxGetRun(a0color ? FaxBlackRunIns : FaxWhiteRunIns,
+        while (true) {
+          int run = FaxGetRun(a0color ? pdfium::make_span(kFaxBlackRunIns)
+                                      : pdfium::make_span(kFaxWhiteRunIns),
                               src_buf, bitpos, bitsize);
           run_len2 += run;
           if (run < 64)
@@ -424,14 +440,15 @@
                   int columns) {
   bool color = true;
   int startpos = 0;
-  while (1) {
+  while (true) {
     if (*bitpos >= bitsize)
       return;
 
     int run_len = 0;
-    while (1) {
-      int run = FaxGetRun(color ? FaxWhiteRunIns : FaxBlackRunIns, src_buf,
-                          bitpos, bitsize);
+    while (true) {
+      int run = FaxGetRun(color ? pdfium::make_span(kFaxWhiteRunIns)
+                                : pdfium::make_span(kFaxBlackRunIns),
+                          src_buf, bitpos, bitsize);
       if (run < 0) {
         while (*bitpos < bitsize) {
           if (NextBit(src_buf, bitpos))
@@ -466,8 +483,8 @@
   ~FaxDecoder() override;
 
   // ScanlineDecoder:
-  bool v_Rewind() override;
-  uint8_t* v_GetNextLine() override;
+  bool Rewind() override;
+  pdfium::span<uint8_t> GetNextLine() override;
   uint32_t GetSrcOffset() override;
 
  private:
@@ -479,8 +496,8 @@
   const bool m_bEndOfLine;
   const bool m_bBlack;
   const pdfium::span<const uint8_t> m_SrcSpan;
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_ScanlineBuf;
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_RefBuf;
+  DataVector<uint8_t> m_ScanlineBuf;
+  DataVector<uint8_t> m_RefBuf;
 };
 
 FaxDecoder::FaxDecoder(pdfium::span<const uint8_t> src_span,
@@ -496,7 +513,7 @@
                       height,
                       kFaxComps,
                       kFaxBpc,
-                      CalculatePitch32(kFaxBpc, width).ValueOrDie()),
+                      fxge::CalculatePitch32OrDie(kFaxBpc, width)),
       m_Encoding(K),
       m_bByteAlign(EncodedByteAlign),
       m_bEndOfLine(EndOfLine),
@@ -505,19 +522,22 @@
       m_ScanlineBuf(m_Pitch),
       m_RefBuf(m_Pitch) {}
 
-FaxDecoder::~FaxDecoder() = default;
+FaxDecoder::~FaxDecoder() {
+  // Span in superclass can't outlive our buffer.
+  m_pLastScanline = pdfium::span<uint8_t>();
+}
 
-bool FaxDecoder::v_Rewind() {
+bool FaxDecoder::Rewind() {
   memset(m_RefBuf.data(), 0xff, m_RefBuf.size());
   m_bitpos = 0;
   return true;
 }
 
-uint8_t* FaxDecoder::v_GetNextLine() {
-  int bitsize = m_SrcSpan.size() * 8;
+pdfium::span<uint8_t> FaxDecoder::GetNextLine() {
+  int bitsize = pdfium::base::checked_cast<int>(m_SrcSpan.size() * 8);
   FaxSkipEOL(m_SrcSpan.data(), bitsize, &m_bitpos);
   if (m_bitpos >= bitsize)
-    return nullptr;
+    return pdfium::span<uint8_t>();
 
   memset(m_ScanlineBuf.data(), 0xff, m_ScanlineBuf.size());
   if (m_Encoding < 0) {
@@ -555,16 +575,17 @@
   }
   if (m_bBlack)
     InvertBuffer();
-  return m_ScanlineBuf.data();
+  return m_ScanlineBuf;
 }
 
 uint32_t FaxDecoder::GetSrcOffset() {
-  return std::min(static_cast<size_t>((m_bitpos + 7) / 8), m_SrcSpan.size());
+  return pdfium::base::checked_cast<uint32_t>(
+      std::min<size_t>((m_bitpos + 7) / 8, m_SrcSpan.size()));
 }
 
 void FaxDecoder::InvertBuffer() {
-  ASSERT(m_Pitch == m_ScanlineBuf.size());
-  ASSERT(m_Pitch % 4 == 0);
+  DCHECK_EQ(m_Pitch, m_ScanlineBuf.size());
+  DCHECK_EQ(m_Pitch % 4, 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];
@@ -596,9 +617,8 @@
     return nullptr;
   }
 
-  return pdfium::MakeUnique<FaxDecoder>(src_span, actual_width, actual_height,
-                                        K, EndOfLine, EncodedByteAlign,
-                                        BlackIs1);
+  return std::make_unique<FaxDecoder>(src_span, actual_width, actual_height, K,
+                                      EndOfLine, EncodedByteAlign, BlackIs1);
 }
 
 // static
@@ -609,9 +629,9 @@
                            int height,
                            int pitch,
                            uint8_t* dest_buf) {
-  ASSERT(pitch != 0);
+  DCHECK(pitch != 0);
 
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> ref_buf(pitch, 0xff);
+  DataVector<uint8_t> ref_buf(pitch, 0xff);
   int bitpos = starting_bitpos;
   for (int iRow = 0; iRow < height; ++iRow) {
     uint8_t* line_buf = dest_buf + iRow * pitch;
@@ -622,7 +642,7 @@
   return bitpos;
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 namespace {
 const uint8_t BlackRunTerminator[128] = {
     0x37, 10, 0x02, 3,  0x03, 2,  0x02, 2,  0x03, 3,  0x03, 4,  0x02, 4,
@@ -668,34 +688,37 @@
 
 class FaxEncoder {
  public:
-  FaxEncoder(const uint8_t* src_buf, int width, int height, int pitch);
+  explicit FaxEncoder(RetainPtr<CFX_DIBBase> src);
   ~FaxEncoder();
-  void Encode(std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-              uint32_t* dest_size);
+  DataVector<uint8_t> Encode();
 
  private:
-  void FaxEncode2DLine(const uint8_t* src_buf);
+  void FaxEncode2DLine(pdfium::span<const uint8_t> src_span);
   void FaxEncodeRun(int run, bool bWhite);
   void AddBitStream(int data, int bitlen);
 
+  // Must outlive `m_RefLineSpan`.
+  RetainPtr<CFX_DIBBase> const m_Src;
   int m_DestBitpos = 0;
   const int m_Cols;
   const int m_Rows;
   const int m_Pitch;
-  const uint8_t* m_pSrcBuf;
-  CFX_BinaryBuf m_DestBuf;
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_RefLine;
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_LineBuf;
+  BinaryBuffer m_DestBuf;
+  // Must outlive `m_RefLineSpan`.
+  const DataVector<uint8_t> m_InitialRefLine;
+  DataVector<uint8_t> m_LineBuf;
+  pdfium::span<const uint8_t> m_RefLineSpan;
 };
 
-FaxEncoder::FaxEncoder(const uint8_t* src_buf, int width, int height, int pitch)
-    : m_Cols(width),
-      m_Rows(height),
-      m_Pitch(pitch),
-      m_pSrcBuf(src_buf),
-      m_RefLine(pitch, 0xff),
-      m_LineBuf(
-          pdfium::Vector2D<uint8_t, FxAllocAllocator<uint8_t>>(8, pitch)) {
+FaxEncoder::FaxEncoder(RetainPtr<CFX_DIBBase> src)
+    : m_Src(std::move(src)),
+      m_Cols(m_Src->GetWidth()),
+      m_Rows(m_Src->GetHeight()),
+      m_Pitch(m_Src->GetPitch()),
+      m_InitialRefLine(m_Pitch, 0xff),
+      m_LineBuf(Fx2DSizeOrDie(8, m_Pitch)),
+      m_RefLineSpan(m_InitialRefLine) {
+  DCHECK_EQ(1, m_Src->GetBPP());
   m_DestBuf.SetAllocStep(10240);
 }
 
@@ -725,14 +748,14 @@
   AddBitStream(*p, p[1]);
 }
 
-void FaxEncoder::FaxEncode2DLine(const uint8_t* src_buf) {
+void FaxEncoder::FaxEncode2DLine(pdfium::span<const uint8_t> src_span) {
   int a0 = -1;
   bool a0color = true;
   while (1) {
-    int a1 = FindBit(src_buf, m_Cols, a0 + 1, !a0color);
+    int a1 = FindBit(src_span, m_Cols, a0 + 1, !a0color);
     int b1;
     int b2;
-    FaxG4FindB1B2(m_RefLine, m_Cols, a0, a0color, &b1, &b2);
+    FaxG4FindB1B2(m_RefLineSpan, m_Cols, a0, a0color, &b1, &b2);
     if (b2 < a1) {
       m_DestBitpos += 3;
       m_LineBuf[m_DestBitpos / 8] |= 1 << (7 - m_DestBitpos % 8);
@@ -764,7 +787,7 @@
       a0 = a1;
       a0color = !a0color;
     } else {
-      int a2 = FindBit(src_buf, m_Cols, a1 + 1, a0color);
+      int a2 = FindBit(src_span, m_Cols, a1 + 1, a0color);
       ++m_DestBitpos;
       ++m_DestBitpos;
       m_LineBuf[m_DestBitpos / 8] |= 1 << (7 - m_DestBitpos % 8);
@@ -780,39 +803,34 @@
   }
 }
 
-void FaxEncoder::Encode(std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-                        uint32_t* dest_size) {
+DataVector<uint8_t> FaxEncoder::Encode() {
   m_DestBitpos = 0;
   uint8_t last_byte = 0;
   for (int i = 0; i < m_Rows; ++i) {
-    const uint8_t* scan_line = m_pSrcBuf + i * m_Pitch;
-    std::fill(std::begin(m_LineBuf), std::end(m_LineBuf), 0);
-    m_LineBuf[0] = last_byte;
+    pdfium::span<uint8_t> buf_span = pdfium::make_span(m_LineBuf);
+    fxcrt::spanset(buf_span, 0);
+    buf_span[0] = last_byte;
+    pdfium::span<const uint8_t> scan_line = m_Src->GetScanline(i);
     FaxEncode2DLine(scan_line);
-    m_DestBuf.AppendBlock(m_LineBuf.data(), m_DestBitpos / 8);
+    m_DestBuf.AppendSpan(buf_span.first(m_DestBitpos / 8));
     last_byte = m_LineBuf[m_DestBitpos / 8];
     m_DestBitpos %= 8;
-    memcpy(m_RefLine.data(), scan_line, m_Pitch);
+    m_RefLineSpan = scan_line;
   }
   if (m_DestBitpos)
-    m_DestBuf.AppendByte(last_byte);
-  *dest_size = m_DestBuf.GetSize();
-  *dest_buf = m_DestBuf.DetachBuffer();
+    m_DestBuf.AppendUint8(last_byte);
+  return m_DestBuf.DetachBuffer();
 }
 
 }  // namespace
 
 // static
-void FaxModule::FaxEncode(const uint8_t* src_buf,
-                          int width,
-                          int height,
-                          int pitch,
-                          std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-                          uint32_t* dest_size) {
-  FaxEncoder encoder(src_buf, width, height, pitch);
-  encoder.Encode(dest_buf, dest_size);
+DataVector<uint8_t> FaxModule::FaxEncode(RetainPtr<CFX_DIBBase> src) {
+  DCHECK_EQ(1, src->GetBPP());
+  FaxEncoder encoder(std::move(src));
+  return encoder.Encode();
 }
 
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 }  // namespace fxcodec
diff --git a/core/fxcodec/fax/faxmodule.h b/core/fxcodec/fax/faxmodule.h
index 0e1d7b2..2e7f5be 100644
--- a/core/fxcodec/fax/faxmodule.h
+++ b/core/fxcodec/fax/faxmodule.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,19 @@
 #ifndef CORE_FXCODEC_FAX_FAXMODULE_H_
 #define CORE_FXCODEC_FAX_FAXMODULE_H_
 
+#include <stdint.h>
+
 #include <memory>
 
 #include "build/build_config.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
+
+#if BUILDFLAG(IS_WIN)
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/retain_ptr.h"
+#endif
+
+class CFX_DIBBase;
 
 namespace fxcodec {
 
@@ -40,14 +47,10 @@
                          int pitch,
                          uint8_t* dest_buf);
 
-#if defined(OS_WIN)
-  static void FaxEncode(const uint8_t* src_buf,
-                        int width,
-                        int height,
-                        int pitch,
-                        std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-                        uint32_t* dest_size);
-#endif  // defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
+  // `src` must have a BPP value of 1.
+  static DataVector<uint8_t> FaxEncode(RetainPtr<CFX_DIBBase> src);
+#endif  // BUILDFLAG(IS_WIN)
 
   FaxModule() = delete;
   FaxModule(const FaxModule&) = delete;
diff --git a/core/fxcodec/flate/flatemodule.cpp b/core/fxcodec/flate/flatemodule.cpp
index 9663df3..f32955f 100644
--- a/core/fxcodec/flate/flatemodule.cpp
+++ b/core/fxcodec/flate/flatemodule.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,19 +6,27 @@
 
 #include "core/fxcodec/flate/flatemodule.h"
 
+#include <stdint.h>
+#include <string.h>
+
 #include <algorithm>
 #include <limits>
 #include <memory>
 #include <utility>
 #include <vector>
 
-#include "core/fxcodec/fx_codec.h"
 #include "core/fxcodec/scanlinedecoder.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fixed_zeroed_data_vector.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxge/calculate_pitch.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/span.h"
+#include "third_party/base/notreached.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/span.h"
 
 #if defined(USE_SYSTEM_ZLIB)
 #include <zlib.h>
@@ -58,7 +66,7 @@
 bool FlateCompress(unsigned char* dest_buf,
                    unsigned long* dest_size,
                    const unsigned char* src_buf,
-                   uint32_t src_size) {
+                   unsigned long src_size) {
   return compress(dest_buf, dest_size, src_buf, src_size) == Z_OK;
 }
 
@@ -84,7 +92,7 @@
   int ret = inflate(static_cast<z_stream*>(context), Z_SYNC_FLUSH);
 
   uint32_t post_pos = FlateGetPossiblyTruncatedTotalOut(context);
-  ASSERT(post_pos >= pre_pos);
+  DCHECK(post_pos >= pre_pos);
 
   uint32_t written = post_pos - pre_pos;
   if (written < dest_size)
@@ -129,22 +137,26 @@
   uint32_t dest_buf_size_ = 0;  // Actual allocated size.
   uint32_t dest_byte_pos_ = 0;  // Size used.
   uint32_t stack_len_ = 0;
-  uint8_t decode_stack_[4000];
+  FixedZeroedDataVector<uint8_t> decode_stack_;
   const uint8_t early_change_;
   uint8_t code_len_ = 9;
   uint32_t current_code_ = 0;
-  uint32_t codes_[5021];
+  FixedZeroedDataVector<uint32_t> codes_;
 };
 
 CLZWDecoder::CLZWDecoder(pdfium::span<const uint8_t> src_span,
                          bool early_change)
-    : src_span_(src_span), early_change_(early_change ? 1 : 0) {}
+    : src_span_(src_span),
+      decode_stack_(4000),
+      early_change_(early_change ? 1 : 0),
+      codes_(5021) {}
 
 void CLZWDecoder::AddCode(uint32_t prefix_code, uint8_t append_char) {
   if (current_code_ + early_change_ == 4094)
     return;
 
-  codes_[current_code_++] = (prefix_code << 16) | append_char;
+  pdfium::span<uint32_t> codes_span = codes_.writable_span();
+  codes_span[current_code_++] = (prefix_code << 16) | append_char;
   if (current_code_ + early_change_ == 512 - 258)
     code_len_ = 10;
   else if (current_code_ + early_change_ == 1024 - 258)
@@ -154,22 +166,24 @@
 }
 
 void CLZWDecoder::DecodeString(uint32_t code) {
-  while (1) {
+  pdfium::span<uint8_t> decode_span = decode_stack_.writable_span();
+  pdfium::span<const uint32_t> codes_span = codes_.span();
+  while (true) {
     int index = code - 258;
     if (index < 0 || static_cast<uint32_t>(index) >= current_code_)
       break;
 
-    uint32_t data = codes_[index];
-    if (stack_len_ >= sizeof(decode_stack_))
+    uint32_t data = codes_span[index];
+    if (stack_len_ >= decode_span.size())
       return;
 
-    decode_stack_[stack_len_++] = static_cast<uint8_t>(data);
+    decode_span[stack_len_++] = static_cast<uint8_t>(data);
     code = data >> 16;
   }
-  if (stack_len_ >= sizeof(decode_stack_))
+  if (stack_len_ >= decode_span.size())
     return;
 
-  decode_stack_[stack_len_++] = static_cast<uint8_t>(code);
+  decode_span[stack_len_++] = static_cast<uint8_t>(code);
 }
 
 void CLZWDecoder::ExpandDestBuf(uint32_t additional_size) {
@@ -185,6 +199,7 @@
 }
 
 bool CLZWDecoder::Decode() {
+  pdfium::span<uint8_t> decode_span = decode_stack_.writable_span();
   uint32_t old_code = 0xFFFFFFFF;
   uint8_t last_char = 0;
 
@@ -192,7 +207,7 @@
   // this size.
   dest_buf_size_ = 512;
   dest_buf_.reset(FX_Alloc(uint8_t, dest_buf_size_));
-  while (1) {
+  while (true) {
     if (src_bit_pos_ + code_len_ > src_span_.size() * 8)
       break;
 
@@ -242,11 +257,11 @@
     if (old_code == 0xFFFFFFFF)
       return false;
 
-    ASSERT(old_code < 256 || old_code >= 258);
+    DCHECK(old_code < 256 || old_code >= 258);
     stack_len_ = 0;
     if (code - 258 >= current_code_) {
-      if (stack_len_ < sizeof(decode_stack_))
-        decode_stack_[stack_len_++] = last_char;
+      if (stack_len_ < decode_stack_.size())
+        decode_span[stack_len_++] = last_char;
       DecodeString(old_code);
     } else {
       DecodeString(code);
@@ -265,9 +280,9 @@
     }
 
     for (uint32_t i = 0; i < stack_len_; i++)
-      dest_buf_.get()[dest_byte_pos_ + i] = decode_stack_[stack_len_ - i - 1];
+      dest_buf_.get()[dest_byte_pos_ + i] = decode_span[stack_len_ - i - 1];
     dest_byte_pos_ += stack_len_;
-    last_char = decode_stack_[stack_len_ - 1];
+    last_char = decode_span[stack_len_ - 1];
     if (old_code >= 258 && old_code - 258 >= current_code_)
       break;
 
@@ -289,13 +304,16 @@
   return (uint8_t)c;
 }
 
-void PNG_PredictLine(uint8_t* pDestData,
-                     const uint8_t* pSrcData,
-                     const uint8_t* pLastLine,
+void PNG_PredictLine(pdfium::span<uint8_t> dest_span,
+                     pdfium::span<const uint8_t> src_span,
+                     pdfium::span<const uint8_t> last_span,
                      int bpc,
                      int nColors,
                      int nPixels) {
-  const uint32_t row_size = CalculatePitch8(bpc, nColors, nPixels).ValueOrDie();
+  uint8_t* pDestData = dest_span.data();
+  const uint8_t* pSrcData = src_span.data();
+  const uint8_t* pLastLine = last_span.data();
+  const uint32_t row_size = fxge::CalculatePitch8OrDie(bpc, nColors, nPixels);
   const uint32_t BytesPerPixel = (bpc * nColors + 7) / 8;
   uint8_t tag = pSrcData[0];
   if (tag == 0) {
@@ -526,7 +544,9 @@
   FlateInput(context.get(), src_buf);
 
   const uint32_t kMaxInitialAllocSize = 10000000;
-  uint32_t guess_size = orig_size ? orig_size : src_buf.size() * 2;
+  uint32_t guess_size =
+      orig_size ? orig_size
+                : pdfium::base::checked_cast<uint32_t>(src_buf.size() * 2);
   guess_size = std::min(guess_size, kMaxInitialAllocSize);
 
   uint32_t buf_size = guess_size;
@@ -538,7 +558,7 @@
   std::vector<std::unique_ptr<uint8_t, FxFreeDeleter>> result_tmp_bufs;
   {
     std::unique_ptr<uint8_t, FxFreeDeleter> cur_buf = std::move(guess_buf);
-    while (1) {
+    while (true) {
       uint32_t ret = FlateOutput(context.get(), cur_buf.get(), buf_size);
       uint32_t avail_buf_size = FlateGetAvailOut(context.get());
       if (ret != Z_OK || avail_buf_size != 0) {
@@ -600,14 +620,14 @@
   ~FlateScanlineDecoder() override;
 
   // ScanlineDecoder:
-  bool v_Rewind() override;
-  uint8_t* v_GetNextLine() override;
+  bool Rewind() override;
+  pdfium::span<uint8_t> GetNextLine() override;
   uint32_t GetSrcOffset() override;
 
  protected:
   std::unique_ptr<z_stream, FlateDeleter> m_pFlate;
   const pdfium::span<const uint8_t> m_SrcBuf;
-  std::unique_ptr<uint8_t, FxFreeDeleter> const m_pScanline;
+  DataVector<uint8_t> m_Scanline;
 };
 
 FlateScanlineDecoder::FlateScanlineDecoder(pdfium::span<const uint8_t> src_span,
@@ -621,13 +641,16 @@
                       height,
                       nComps,
                       bpc,
-                      CalculatePitch8(bpc, nComps, width).ValueOrDie()),
+                      fxge::CalculatePitch8OrDie(bpc, nComps, width)),
       m_SrcBuf(src_span),
-      m_pScanline(FX_Alloc(uint8_t, m_Pitch)) {}
+      m_Scanline(m_Pitch) {}
 
-FlateScanlineDecoder::~FlateScanlineDecoder() = default;
+FlateScanlineDecoder::~FlateScanlineDecoder() {
+  // Span in superclass can't outlive our buffer.
+  m_pLastScanline = pdfium::span<uint8_t>();
+}
 
-bool FlateScanlineDecoder::v_Rewind() {
+bool FlateScanlineDecoder::Rewind() {
   m_pFlate.reset(FlateInit());
   if (!m_pFlate)
     return false;
@@ -636,9 +659,9 @@
   return true;
 }
 
-uint8_t* FlateScanlineDecoder::v_GetNextLine() {
-  FlateOutput(m_pFlate.get(), m_pScanline.get(), m_Pitch);
-  return m_pScanline.get();
+pdfium::span<uint8_t> FlateScanlineDecoder::GetNextLine() {
+  FlateOutput(m_pFlate.get(), m_Scanline.data(), m_Pitch);
+  return m_Scanline;
 }
 
 uint32_t FlateScanlineDecoder::GetSrcOffset() {
@@ -659,8 +682,8 @@
   ~FlatePredictorScanlineDecoder() override;
 
   // ScanlineDecoder:
-  bool v_Rewind() override;
-  uint8_t* v_GetNextLine() override;
+  bool Rewind() override;
+  pdfium::span<uint8_t> GetNextLine() override;
 
  private:
   void GetNextLineWithPredictedPitch();
@@ -672,9 +695,9 @@
   int m_Columns = 0;
   uint32_t m_PredictPitch = 0;
   size_t m_LeftOver = 0;
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_LastLine;
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_PredictBuffer;
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_PredictRaw;
+  DataVector<uint8_t> m_LastLine;
+  DataVector<uint8_t> m_PredictBuffer;
+  DataVector<uint8_t> m_PredictRaw;
 };
 
 FlatePredictorScanlineDecoder::FlatePredictorScanlineDecoder(
@@ -689,7 +712,7 @@
     int Columns)
     : FlateScanlineDecoder(src_span, width, height, comps, bpc),
       m_Predictor(predictor) {
-  ASSERT(m_Predictor != PredictorType::kNone);
+  DCHECK(m_Predictor != PredictorType::kNone);
   if (BitsPerComponent * Colors * Columns == 0) {
     BitsPerComponent = m_bpc;
     Colors = m_nComps;
@@ -699,46 +722,48 @@
   m_BitsPerComponent = BitsPerComponent;
   m_Columns = Columns;
   m_PredictPitch =
-      CalculatePitch8(m_BitsPerComponent, m_Colors, m_Columns).ValueOrDie();
+      fxge::CalculatePitch8OrDie(m_BitsPerComponent, m_Colors, m_Columns);
   m_LastLine.resize(m_PredictPitch);
   m_PredictBuffer.resize(m_PredictPitch);
   m_PredictRaw.resize(m_PredictPitch + 1);
 }
 
-FlatePredictorScanlineDecoder::~FlatePredictorScanlineDecoder() = default;
+FlatePredictorScanlineDecoder::~FlatePredictorScanlineDecoder() {
+  // Span in superclass can't outlive our buffer.
+  m_pLastScanline = pdfium::span<uint8_t>();
+}
 
-bool FlatePredictorScanlineDecoder::v_Rewind() {
-  if (!FlateScanlineDecoder::v_Rewind())
+bool FlatePredictorScanlineDecoder::Rewind() {
+  if (!FlateScanlineDecoder::Rewind())
     return false;
 
   m_LeftOver = 0;
   return true;
 }
 
-uint8_t* FlatePredictorScanlineDecoder::v_GetNextLine() {
+pdfium::span<uint8_t> FlatePredictorScanlineDecoder::GetNextLine() {
   if (m_Pitch == m_PredictPitch)
     GetNextLineWithPredictedPitch();
   else
     GetNextLineWithoutPredictedPitch();
-  return m_pScanline.get();
+  return m_Scanline;
 }
 
 void FlatePredictorScanlineDecoder::GetNextLineWithPredictedPitch() {
   switch (m_Predictor) {
     case PredictorType::kPng:
       FlateOutput(m_pFlate.get(), m_PredictRaw.data(), m_PredictPitch + 1);
-      PNG_PredictLine(m_pScanline.get(), m_PredictRaw.data(), m_LastLine.data(),
-                      m_BitsPerComponent, m_Colors, m_Columns);
-      memcpy(m_LastLine.data(), m_pScanline.get(), m_PredictPitch);
+      PNG_PredictLine(m_Scanline, m_PredictRaw, m_LastLine, m_BitsPerComponent,
+                      m_Colors, m_Columns);
+      memcpy(m_LastLine.data(), m_Scanline.data(), m_PredictPitch);
       break;
     case PredictorType::kFlate:
-      FlateOutput(m_pFlate.get(), m_pScanline.get(), m_Pitch);
-      TIFF_PredictLine(m_pScanline.get(), m_PredictPitch, m_bpc, m_nComps,
+      FlateOutput(m_pFlate.get(), m_Scanline.data(), m_Pitch);
+      TIFF_PredictLine(m_Scanline.data(), m_PredictPitch, m_bpc, m_nComps,
                        m_OutputWidth);
       break;
-    default:
-      NOTREACHED();
-      break;
+    case PredictorType::kNone:
+      NOTREACHED_NORETURN();
   }
 }
 
@@ -746,7 +771,7 @@
   size_t bytes_to_go = m_Pitch;
   size_t read_leftover = m_LeftOver > bytes_to_go ? bytes_to_go : m_LeftOver;
   if (read_leftover) {
-    memcpy(m_pScanline.get(), &m_PredictBuffer[m_PredictPitch - m_LeftOver],
+    memcpy(m_Scanline.data(), &m_PredictBuffer[m_PredictPitch - m_LeftOver],
            read_leftover);
     m_LeftOver -= read_leftover;
     bytes_to_go -= read_leftover;
@@ -755,9 +780,8 @@
     switch (m_Predictor) {
       case PredictorType::kPng:
         FlateOutput(m_pFlate.get(), m_PredictRaw.data(), m_PredictPitch + 1);
-        PNG_PredictLine(m_PredictBuffer.data(), m_PredictRaw.data(),
-                        m_LastLine.data(), m_BitsPerComponent, m_Colors,
-                        m_Columns);
+        PNG_PredictLine(m_PredictBuffer, m_PredictRaw, m_LastLine,
+                        m_BitsPerComponent, m_Colors, m_Columns);
         memcpy(m_LastLine.data(), m_PredictBuffer.data(), m_PredictPitch);
         break;
       case PredictorType::kFlate:
@@ -765,14 +789,13 @@
         TIFF_PredictLine(m_PredictBuffer.data(), m_PredictPitch,
                          m_BitsPerComponent, m_Colors, m_Columns);
         break;
-      default:
-        NOTREACHED();
-        break;
+      case PredictorType::kNone:
+        NOTREACHED_NORETURN();
     }
     size_t read_bytes =
         m_PredictPitch > bytes_to_go ? bytes_to_go : m_PredictPitch;
-    memcpy(m_pScanline.get() + m_Pitch - bytes_to_go, m_PredictBuffer.data(),
-           read_bytes);
+    fxcrt::spancpy(pdfium::make_span(m_Scanline).subspan(m_Pitch - bytes_to_go),
+                   pdfium::make_span(m_PredictBuffer).first(read_bytes));
     m_LeftOver += m_PredictPitch - read_bytes;
     bytes_to_go -= read_bytes;
   }
@@ -793,10 +816,10 @@
     int Columns) {
   PredictorType predictor_type = GetPredictor(predictor);
   if (predictor_type == PredictorType::kNone) {
-    return pdfium::MakeUnique<FlateScanlineDecoder>(src_span, width, height,
-                                                    nComps, bpc);
+    return std::make_unique<FlateScanlineDecoder>(src_span, width, height,
+                                                  nComps, bpc);
   }
-  return pdfium::MakeUnique<FlatePredictorScanlineDecoder>(
+  return std::make_unique<FlatePredictorScanlineDecoder>(
       src_span, width, height, nComps, bpc, predictor_type, Colors,
       BitsPerComponent, Columns);
 }
@@ -818,7 +841,7 @@
   PredictorType predictor_type = GetPredictor(predictor);
 
   if (bLZW) {
-    auto decoder = pdfium::MakeUnique<CLZWDecoder>(src_span, bEarlyChange);
+    auto decoder = std::make_unique<CLZWDecoder>(src_span, bEarlyChange);
     if (!decoder->Decode())
       return FX_INVALID_OFFSET;
 
@@ -841,26 +864,24 @@
       ret = TIFF_Predictor(Colors, BitsPerComponent, Columns, dest_buf,
                            dest_size);
       break;
-    default:
-      NOTREACHED();
-      break;
   }
   return ret ? offset : FX_INVALID_OFFSET;
 }
 
 // static
-bool FlateModule::Encode(const uint8_t* src_buf,
-                         uint32_t src_size,
-                         std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-                         uint32_t* dest_size) {
-  *dest_size = src_size + src_size / 1000 + 12;
-  dest_buf->reset(FX_Alloc(uint8_t, *dest_size));
-  unsigned long temp_size = *dest_size;
-  if (!FlateCompress(dest_buf->get(), &temp_size, src_buf, src_size))
-    return false;
+DataVector<uint8_t> FlateModule::Encode(pdfium::span<const uint8_t> src_span) {
+  const unsigned long src_size =
+      pdfium::base::checked_cast<unsigned long>(src_span.size());
+  pdfium::base::CheckedNumeric<unsigned long> safe_dest_size = src_size;
+  safe_dest_size += src_size / 1000;
+  safe_dest_size += 12;
+  unsigned long dest_size = safe_dest_size.ValueOrDie();
+  DataVector<uint8_t> dest_buf(dest_size);
+  if (!FlateCompress(dest_buf.data(), &dest_size, src_span.data(), src_size))
+    return {};
 
-  *dest_size = (uint32_t)temp_size;
-  return true;
+  dest_buf.resize(pdfium::base::checked_cast<size_t>(dest_size));
+  return dest_buf;
 }
 
 }  // namespace fxcodec
diff --git a/core/fxcodec/flate/flatemodule.h b/core/fxcodec/flate/flatemodule.h
index 8ce5aa5..f5a9509 100644
--- a/core/fxcodec/flate/flatemodule.h
+++ b/core/fxcodec/flate/flatemodule.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,13 @@
 #ifndef CORE_FXCODEC_FLATE_FLATEMODULE_H_
 #define CORE_FXCODEC_FLATE_FLATEMODULE_H_
 
+#include <stdint.h>
+
 #include <memory>
 
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcodec {
 
@@ -42,10 +44,7 @@
       std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
       uint32_t* dest_size);
 
-  static bool Encode(const uint8_t* src_buf,
-                     uint32_t src_size,
-                     std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-                     uint32_t* dest_size);
+  static DataVector<uint8_t> Encode(pdfium::span<const uint8_t> src_span);
 
   FlateModule() = delete;
   FlateModule(const FlateModule&) = delete;
diff --git a/core/fxcodec/fx_codec.cpp b/core/fxcodec/fx_codec.cpp
index 5625534..7181d57 100644
--- a/core/fxcodec/fx_codec.cpp
+++ b/core/fxcodec/fx_codec.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,110 +6,31 @@
 
 #include "core/fxcodec/fx_codec.h"
 
-#include <algorithm>
-#include <cmath>
-#include <memory>
 #include <utility>
 
-#include "core/fxcodec/jbig2/jbig2module.h"
-#include "core/fxcodec/jpeg/jpegmodule.h"
-#include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
+#include "core/fxge/dib/fx_dib.h"
 
 namespace fxcodec {
 
-namespace {
-
-ModuleMgr* g_ModuleMgr = nullptr;
-
-}  // namespace
-
-// static
-void ModuleMgr::Create() {
-  ASSERT(!g_ModuleMgr);
-  g_ModuleMgr = new ModuleMgr();
-}
-
-// static
-void ModuleMgr::Destroy() {
-  ASSERT(g_ModuleMgr);
-  delete g_ModuleMgr;
-  g_ModuleMgr = nullptr;
-}
-
-// static
-ModuleMgr* ModuleMgr::GetInstance() {
-  ASSERT(g_ModuleMgr);
-  return g_ModuleMgr;
-}
-
-ModuleMgr::ModuleMgr()
-    : m_pJpegModule(pdfium::MakeUnique<JpegModule>()),
-      m_pJbig2Module(pdfium::MakeUnique<Jbig2Module>()) {
-#ifdef PDF_ENABLE_XFA_BMP
-  SetBmpModule(pdfium::MakeUnique<BmpModule>());
-#endif
-
-#ifdef PDF_ENABLE_XFA_GIF
-  SetGifModule(pdfium::MakeUnique<GifModule>());
-#endif
-
-#ifdef PDF_ENABLE_XFA_PNG
-  SetPngModule(pdfium::MakeUnique<PngModule>());
-#endif
-
-#ifdef PDF_ENABLE_XFA_TIFF
-  SetTiffModule(pdfium::MakeUnique<TiffModule>());
-#endif
-}
-
-ModuleMgr::~ModuleMgr() = default;
-
 #ifdef PDF_ENABLE_XFA
 CFX_DIBAttribute::CFX_DIBAttribute() = default;
 
-CFX_DIBAttribute::~CFX_DIBAttribute() {
-  for (const auto& pair : m_Exif)
-    FX_Free(pair.second);
-}
+CFX_DIBAttribute::~CFX_DIBAttribute() = default;
 #endif  // PDF_ENABLE_XFA
 
 void ReverseRGB(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels) {
   if (pDestBuf == pSrcBuf) {
     for (int i = 0; i < pixels; i++) {
-      uint8_t temp = pDestBuf[2];
-      pDestBuf[2] = pDestBuf[0];
-      pDestBuf[0] = temp;
+      std::swap(pDestBuf[0], pDestBuf[2]);
       pDestBuf += 3;
     }
   } else {
     for (int i = 0; i < pixels; i++) {
-      *pDestBuf++ = pSrcBuf[2];
-      *pDestBuf++ = pSrcBuf[1];
-      *pDestBuf++ = pSrcBuf[0];
+      ReverseCopy3Bytes(pDestBuf, pSrcBuf);
+      pDestBuf += 3;
       pSrcBuf += 3;
     }
   }
 }
 
-FX_SAFE_UINT32 CalculatePitch8(uint32_t bpc, uint32_t components, int width) {
-  FX_SAFE_UINT32 pitch = bpc;
-  pitch *= components;
-  pitch *= width;
-  pitch += 7;
-  pitch /= 8;
-  return pitch;
-}
-
-FX_SAFE_UINT32 CalculatePitch32(int bpp, int width) {
-  FX_SAFE_UINT32 pitch = bpp;
-  pitch *= width;
-  pitch += 31;
-  pitch /= 32;  // quantized to number of 32-bit words.
-  pitch *= 4;   // and then back to bytes, (not just /8 in one step).
-  return pitch;
-}
-
 }  // namespace fxcodec
diff --git a/core/fxcodec/fx_codec.h b/core/fxcodec/fx_codec.h
index c7224ff..b102b43 100644
--- a/core/fxcodec/fx_codec.h
+++ b/core/fxcodec/fx_codec.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,125 +7,33 @@
 #ifndef CORE_FXCODEC_FX_CODEC_H_
 #define CORE_FXCODEC_FX_CODEC_H_
 
-#include <map>
-#include <memory>
-#include <utility>
-
-#include "core/fxcodec/fx_codec_def.h"
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxcrt/fx_string.h"
-
-#ifdef PDF_ENABLE_XFA
-#ifdef PDF_ENABLE_XFA_BMP
-#include "core/fxcodec/bmp/bmpmodule.h"
-#endif  // PDF_ENABLE_XFA_BMP
-
-#ifdef PDF_ENABLE_XFA_GIF
-#include "core/fxcodec/gif/gifmodule.h"
-#endif  // PDF_ENABLE_XFA_GIF
-
-#ifdef PDF_ENABLE_XFA_PNG
-#include "core/fxcodec/png/pngmodule.h"
-#endif  // PDF_ENABLE_XFA_PNG
-
-#ifdef PDF_ENABLE_XFA_TIFF
-#include "core/fxcodec/tiff/tiffmodule.h"
-#endif  // PDF_ENABLE_XFA_TIFF
-#endif  // PDF_ENABLE_XFA
+#include <stdint.h>
 
 namespace fxcodec {
 
 #ifdef PDF_ENABLE_XFA
 class CFX_DIBAttribute {
  public:
+  // Not an enum class yet because we still blindly cast integer results
+  // from third-party libraries to this type.
+  enum ResUnit : uint16_t {
+    kResUnitNone = 0,
+    kResUnitInch,
+    kResUnitCentimeter,
+    kResUnitMeter
+  };
+
   CFX_DIBAttribute();
   ~CFX_DIBAttribute();
 
   int32_t m_nXDPI = -1;
   int32_t m_nYDPI = -1;
-  uint16_t m_wDPIUnit = 0;
-  std::map<uint32_t, void*> m_Exif;
+  ResUnit m_wDPIUnit = kResUnitNone;
 };
 #endif  // PDF_ENABLE_XFA
 
-class Jbig2Module;
-class JpegModule;
-class ProgressiveDecoder;
-
-class ModuleMgr {
- public:
-  // Per-process singleton managed by callers.
-  static void Create();
-  static void Destroy();
-  static ModuleMgr* GetInstance();
-
-  JpegModule* GetJpegModule() const { return m_pJpegModule.get(); }
-  Jbig2Module* GetJbig2Module() const { return m_pJbig2Module.get(); }
-
-#ifdef PDF_ENABLE_XFA
-  std::unique_ptr<ProgressiveDecoder> CreateProgressiveDecoder();
-
-#ifdef PDF_ENABLE_XFA_BMP
-  BmpModule* GetBmpModule() const { return m_pBmpModule.get(); }
-  void SetBmpModule(std::unique_ptr<BmpModule> module) {
-    m_pBmpModule = std::move(module);
-  }
-#endif  // PDF_ENABLE_XFA_BMP
-
-#ifdef PDF_ENABLE_XFA_GIF
-  GifModule* GetGifModule() const { return m_pGifModule.get(); }
-  void SetGifModule(std::unique_ptr<GifModule> module) {
-    m_pGifModule = std::move(module);
-  }
-#endif  // PDF_ENABLE_XFA_GIF
-
-#ifdef PDF_ENABLE_XFA_PNG
-  PngModule* GetPngModule() const { return m_pPngModule.get(); }
-  void SetPngModule(std::unique_ptr<PngModule> module) {
-    m_pPngModule = std::move(module);
-  }
-#endif  // PDF_ENABLE_XFA_PNG
-
-#ifdef PDF_ENABLE_XFA_TIFF
-  TiffModule* GetTiffModule() const { return m_pTiffModule.get(); }
-  void SetTiffModule(std::unique_ptr<TiffModule> module) {
-    m_pTiffModule = std::move(module);
-  }
-#endif  // PDF_ENABLE_XFA_TIFF
-#endif  // PDF_ENABLE_XFA
-
- private:
-  ModuleMgr();
-  ~ModuleMgr();
-
-  std::unique_ptr<JpegModule> m_pJpegModule;
-  std::unique_ptr<Jbig2Module> m_pJbig2Module;
-
-#ifdef PDF_ENABLE_XFA
-#ifdef PDF_ENABLE_XFA_BMP
-  std::unique_ptr<BmpModule> m_pBmpModule;
-#endif  // PDF_ENABLE_XFA_BMP
-
-#ifdef PDF_ENABLE_XFA_GIF
-  std::unique_ptr<GifModule> m_pGifModule;
-#endif  // PDF_ENABLE_XFA_GIF
-
-#ifdef PDF_ENABLE_XFA_PNG
-  std::unique_ptr<PngModule> m_pPngModule;
-#endif  // PDF_ENABLE_XFA_PNG
-
-#ifdef PDF_ENABLE_XFA_TIFF
-  std::unique_ptr<TiffModule> m_pTiffModule;
-#endif  // PDF_ENABLE_XFA_TIFF
-#endif  // PDF_ENABLE_XFA
-};
-
 void ReverseRGB(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels);
 
-FX_SAFE_UINT32 CalculatePitch8(uint32_t bpc, uint32_t components, int width);
-FX_SAFE_UINT32 CalculatePitch32(int bpp, int width);
-
 }  // namespace fxcodec
 
 #ifdef PDF_ENABLE_XFA
diff --git a/core/fxcodec/fx_codec_def.h b/core/fxcodec/fx_codec_def.h
index 2d6b5ed..257007a 100644
--- a/core/fxcodec/fx_codec_def.h
+++ b/core/fxcodec/fx_codec_def.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,20 +7,13 @@
 #ifndef CORE_FXCODEC_FX_CODEC_DEF_H_
 #define CORE_FXCODEC_FX_CODEC_DEF_H_
 
-enum FXCODEC_STATUS {
-  FXCODEC_STATUS_ERROR = -1,
-  FXCODEC_STATUS_FRAME_READY,
-  FXCODEC_STATUS_FRAME_TOBECONTINUE,
-  FXCODEC_STATUS_DECODE_READY,
-  FXCODEC_STATUS_DECODE_TOBECONTINUE,
-  FXCODEC_STATUS_DECODE_FINISH,
-#ifdef PDF_ENABLE_XFA
-  FXCODEC_STATUS_ERR_MEMORY,
-#endif  // PDF_ENABLE_XFA
-  FXCODEC_STATUS_ERR_READ,
-  FXCODEC_STATUS_ERR_FLUSH,
-  FXCODEC_STATUS_ERR_FORMAT,
-  FXCODEC_STATUS_ERR_PARAMS
+enum class FXCODEC_STATUS {
+  kError = -1,
+  kFrameReady,
+  kFrameToBeContinued,
+  kDecodeReady,
+  kDecodeToBeContinued,
+  kDecodeFinished,
 };
 
 #ifdef PDF_ENABLE_XFA
@@ -41,13 +34,6 @@
 #endif  // PDF_ENABLE_XFA_TIFF
   FXCODEC_IMAGE_MAX
 };
-
-enum FXCODEC_RESUNIT {
-  FXCODEC_RESUNIT_NONE = 0,
-  FXCODEC_RESUNIT_INCH,
-  FXCODEC_RESUNIT_CENTIMETER,
-  FXCODEC_RESUNIT_METER
-};
 #endif  // PDF_ENABLE_XFA
 
 #endif  // CORE_FXCODEC_FX_CODEC_DEF_H_
diff --git a/core/fxcodec/gif/cfx_gif.cpp b/core/fxcodec/gif/cfx_gif.cpp
index efe4f4b..04c673f 100644
--- a/core/fxcodec/gif/cfx_gif.cpp
+++ b/core/fxcodec/gif/cfx_gif.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,8 +17,8 @@
               "CFX_GifHeader should have a size of 6");
 static_assert(sizeof(CFX_GifLocalScreenDescriptor) == 7,
               "CFX_GifLocalScreenDescriptor should have a size of 7");
-static_assert(sizeof(CFX_CFX_GifImageInfo) == 9,
-              "CFX_CFX_GifImageInfo should have a size of 9");
+static_assert(sizeof(CFX_GifImageInfo) == 9,
+              "CFX_GifImageInfo should have a size of 9");
 static_assert(sizeof(CFX_GifControlExtensionFlags) == 1,
               "CFX_GifControlExtensionFlags should have a size of 1");
 static_assert(sizeof(CFX_GifPlainTextExtension) == 13,
diff --git a/core/fxcodec/gif/cfx_gif.h b/core/fxcodec/gif/cfx_gif.h
index c0c9d00..8556612 100644
--- a/core/fxcodec/gif/cfx_gif.h
+++ b/core/fxcodec/gif/cfx_gif.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #ifndef CORE_FXCODEC_GIF_CFX_GIF_H_
 #define CORE_FXCODEC_GIF_CFX_GIF_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
-#include "core/fxcrt/fx_memory_wrappers.h"
-
-class CFX_GifContext;
+#include "core/fxcrt/data_vector.h"
 
 extern const char kGifSignature87[];
 extern const char kGifSignature89[];
@@ -64,7 +64,7 @@
   uint8_t pixel_aspect;
 };
 
-struct CFX_CFX_GifImageInfo {
+struct CFX_GifImageInfo {
   uint16_t left;
   uint16_t top;
   uint16_t width;
@@ -111,22 +111,15 @@
 };
 #pragma pack()
 
-enum class CFX_GifDecodeStatus {
-  Error,
-  Success,
-  Unfinished,
-  InsufficientDestSize,  // Only used internally by CGifLZWDecoder::Decode()
-};
-
 struct CFX_GifImage {
   CFX_GifImage();
   ~CFX_GifImage();
 
   std::unique_ptr<CFX_GifGraphicControlExtension> image_GCE;
   std::vector<CFX_GifPalette> local_palettes;
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> row_buffer;
-  CFX_CFX_GifImageInfo image_info;
-  uint8_t local_pallette_exp;
+  DataVector<uint8_t> row_buffer;
+  CFX_GifImageInfo image_info;
+  uint8_t local_palette_exp;
   uint8_t code_exp;
   uint32_t data_pos;
   int32_t row_num;
diff --git a/core/fxcodec/gif/cfx_gifcontext.cpp b/core/fxcodec/gif/cfx_gifcontext.cpp
index 267098a..9811759 100644
--- a/core/fxcodec/gif/cfx_gifcontext.cpp
+++ b/core/fxcodec/gif/cfx_gifcontext.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,14 +6,17 @@
 
 #include "core/fxcodec/gif/cfx_gifcontext.h"
 
+#include <stdint.h>
+#include <string.h>
+
 #include <algorithm>
+#include <iterator>
 #include <utility>
 
 #include "core/fxcodec/cfx_codec_memory.h"
-#include "core/fxcodec/gif/cfx_gif.h"
-#include "core/fxcodec/gif/gifmodule.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/stl_util.h"
 
 namespace fxcodec {
 
@@ -23,17 +26,13 @@
 
 }  // namespace
 
-CFX_GifContext::CFX_GifContext(GifModule* gif_module,
-                               GifModule::Delegate* delegate)
-    : gif_module_(gif_module), delegate_(delegate) {}
+CFX_GifContext::CFX_GifContext(GifDecoder::Delegate* delegate)
+    : delegate_(delegate) {}
 
 CFX_GifContext::~CFX_GifContext() = default;
 
-void CFX_GifContext::RecordCurrentPosition(uint32_t* cur_pos) {
-  delegate_->GifRecordCurrentPosition(*cur_pos);
-}
-
-void CFX_GifContext::ReadScanline(int32_t row_num, uint8_t* row_buf) {
+void CFX_GifContext::ReadScanline(int32_t row_num,
+                                  pdfium::span<uint8_t> row_buf) {
   delegate_->GifReadScanline(row_num, row_buf);
 }
 
@@ -44,33 +43,30 @@
                                        int32_t height,
                                        int32_t pal_num,
                                        CFX_GifPalette* pal,
-                                       int32_t delay_time,
-                                       bool user_input,
                                        int32_t trans_index,
-                                       int32_t disposal_method,
                                        bool interlace) {
   return delegate_->GifInputRecordPositionBuf(
       cur_pos, FX_RECT(left, top, left + width, top + height), pal_num, pal,
-      delay_time, user_input, trans_index, disposal_method, interlace);
+      trans_index, interlace);
 }
 
-CFX_GifDecodeStatus CFX_GifContext::ReadHeader() {
-  CFX_GifDecodeStatus status = ReadGifSignature();
-  if (status != CFX_GifDecodeStatus::Success)
+GifDecoder::Status CFX_GifContext::ReadHeader() {
+  GifDecoder::Status status = ReadGifSignature();
+  if (status != GifDecoder::Status::kSuccess)
     return status;
   return ReadLogicalScreenDescriptor();
 }
 
-CFX_GifDecodeStatus CFX_GifContext::GetFrame() {
-  CFX_GifDecodeStatus ret = CFX_GifDecodeStatus::Success;
+GifDecoder::Status CFX_GifContext::GetFrame() {
+  GifDecoder::Status ret = GifDecoder::Status::kSuccess;
   while (true) {
     switch (decode_status_) {
       case GIF_D_STATUS_TAIL:
-        return CFX_GifDecodeStatus::Success;
+        return GifDecoder::Status::kSuccess;
       case GIF_D_STATUS_SIG: {
         uint8_t signature;
         if (!ReadAllOrNone(&signature, sizeof(signature)))
-          return CFX_GifDecodeStatus::Unfinished;
+          return GifDecoder::Status::kUnfinished;
 
         switch (signature) {
           case GIF_SIG_EXTENSION:
@@ -81,7 +77,7 @@
             continue;
           case GIF_SIG_TRAILER:
             SaveDecodingStatus(GIF_D_STATUS_TAIL);
-            return CFX_GifDecodeStatus::Success;
+            return GifDecoder::Status::kSuccess;
           default:
             if (!input_buffer_->IsEOF()) {
               // The Gif File has non_standard Tag!
@@ -89,13 +85,13 @@
               continue;
             }
             // The Gif File Doesn't have Trailer Tag!
-            return CFX_GifDecodeStatus::Success;
+            return GifDecoder::Status::kSuccess;
         }
       }
       case GIF_D_STATUS_EXT: {
         uint8_t extension;
         if (!ReadAllOrNone(&extension, sizeof(extension)))
-          return CFX_GifDecodeStatus::Unfinished;
+          return GifDecoder::Status::kUnfinished;
 
         switch (extension) {
           case GIF_BLOCK_CE:
@@ -119,7 +115,7 @@
       }
       case GIF_D_STATUS_IMG_INFO: {
         ret = DecodeImageInfo();
-        if (ret != CFX_GifDecodeStatus::Success)
+        if (ret != GifDecoder::Status::kSuccess)
           return ret;
 
         continue;
@@ -129,13 +125,13 @@
         size_t read_marker = input_buffer_->GetPosition();
 
         if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size)))
-          return CFX_GifDecodeStatus::Unfinished;
+          return GifDecoder::Status::kUnfinished;
 
         while (img_data_size != GIF_BLOCK_TERMINAL) {
           if (!input_buffer_->Seek(input_buffer_->GetPosition() +
                                    img_data_size)) {
             input_buffer_->Seek(read_marker);
-            return CFX_GifDecodeStatus::Unfinished;
+            return GifDecoder::Status::kUnfinished;
           }
 
           // This saving of the scan state on partial reads is why
@@ -143,7 +139,7 @@
           SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
           read_marker = input_buffer_->GetPosition();
           if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size)))
-            return CFX_GifDecodeStatus::Unfinished;
+            return GifDecoder::Status::kUnfinished;
         }
 
         SaveDecodingStatus(GIF_D_STATUS_SIG);
@@ -151,26 +147,25 @@
       }
       default: {
         ret = DecodeExtension();
-        if (ret != CFX_GifDecodeStatus::Success)
+        if (ret != GifDecoder::Status::kSuccess)
           return ret;
         break;
       }
     }
   }
-  return CFX_GifDecodeStatus::Success;
 }
 
-CFX_GifDecodeStatus CFX_GifContext::LoadFrame(int32_t frame_num) {
-  if (!pdfium::IndexInBounds(images_, frame_num))
-    return CFX_GifDecodeStatus::Error;
+GifDecoder::Status CFX_GifContext::LoadFrame(size_t frame_num) {
+  if (frame_num >= images_.size())
+    return GifDecoder::Status::kError;
 
-  CFX_GifImage* gif_image = images_[static_cast<size_t>(frame_num)].get();
+  CFX_GifImage* gif_image = images_[frame_num].get();
   if (gif_image->image_info.height == 0)
-    return CFX_GifDecodeStatus::Error;
+    return GifDecoder::Status::kError;
 
   uint32_t gif_img_row_bytes = gif_image->image_info.width;
   if (gif_img_row_bytes == 0)
-    return CFX_GifDecodeStatus::Error;
+    return GifDecoder::Status::kError;
 
   if (decode_status_ == GIF_D_STATUS_TAIL) {
     gif_image->row_buffer.resize(gif_img_row_bytes);
@@ -186,33 +181,30 @@
       bool bRes = GetRecordPosition(
           gif_image->data_pos, gif_image->image_info.left,
           gif_image->image_info.top, gif_image->image_info.width,
-          gif_image->image_info.height, loc_pal_num, pLocalPalette, 0, 0, -1, 0,
+          gif_image->image_info.height, loc_pal_num, pLocalPalette, -1,
           gif_image->image_info.local_flags.interlace);
       if (!bRes) {
         gif_image->row_buffer.clear();
-        return CFX_GifDecodeStatus::Error;
+        return GifDecoder::Status::kError;
       }
     } else {
       bool bRes = GetRecordPosition(
           gif_image->data_pos, gif_image->image_info.left,
           gif_image->image_info.top, gif_image->image_info.width,
           gif_image->image_info.height, loc_pal_num, pLocalPalette,
-          static_cast<int32_t>(gif_image->image_GCE->delay_time),
-          gif_image->image_GCE->gce_flags.user_input,
           gif_image->image_GCE->gce_flags.transparency
               ? static_cast<int32_t>(gif_image->image_GCE->trans_index)
               : -1,
-          static_cast<int32_t>(gif_image->image_GCE->gce_flags.disposal_method),
           gif_image->image_info.local_flags.interlace);
       if (!bRes) {
         gif_image->row_buffer.clear();
-        return CFX_GifDecodeStatus::Error;
+        return GifDecoder::Status::kError;
       }
     }
 
     if (gif_image->code_exp > GIF_MAX_LZW_EXP) {
       gif_image->row_buffer.clear();
-      return CFX_GifDecodeStatus::Error;
+      return GifDecoder::Status::kError;
     }
 
     img_row_offset_ = 0;
@@ -223,114 +215,106 @@
   }
 
   uint8_t img_data_size;
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> img_data;
+  DataVector<uint8_t> img_data;
   size_t read_marker = input_buffer_->GetPosition();
 
+  // TODO(crbug.com/pdfium/1793): This logic can be simplified a lot, but it
+  // probably makes more sense to switch to a different GIF decoder altogether.
   if (decode_status_ == GIF_D_STATUS_IMG_DATA) {
     if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size)))
-      return CFX_GifDecodeStatus::Unfinished;
+      return GifDecoder::Status::kUnfinished;
 
     if (img_data_size != GIF_BLOCK_TERMINAL) {
       img_data.resize(img_data_size);
       if (!ReadAllOrNone(img_data.data(), img_data_size)) {
         input_buffer_->Seek(read_marker);
-        return CFX_GifDecodeStatus::Unfinished;
+        return GifDecoder::Status::kUnfinished;
       }
 
-      if (!lzw_decompressor_.get())
-        lzw_decompressor_ = CFX_LZWDecompressor::Create(
-            !gif_image->local_palettes.empty() ? gif_image->local_pallette_exp
-                                               : global_pal_exp_,
-            gif_image->code_exp);
+      if (!lzw_decompressor_) {
+        lzw_decompressor_ = LZWDecompressor::Create(GetPaletteExp(gif_image),
+                                                    gif_image->code_exp);
+        if (!lzw_decompressor_) {
+          DecodingFailureAtTailCleanup(gif_image);
+          return GifDecoder::Status::kError;
+        }
+      }
+      lzw_decompressor_->SetSource(img_data.data(), img_data_size);
+
       SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
       img_row_offset_ += img_row_avail_size_;
       img_row_avail_size_ = gif_img_row_bytes - img_row_offset_;
-      CFX_GifDecodeStatus ret =
-          lzw_decompressor_.get()
-              ? lzw_decompressor_->Decode(
-                    img_data.data(), img_data_size,
-                    gif_image->row_buffer.data() + img_row_offset_,
-                    &img_row_avail_size_)
-              : CFX_GifDecodeStatus::Error;
-      if (ret == CFX_GifDecodeStatus::Error) {
+      LZWDecompressor::Status ret = lzw_decompressor_->Decode(
+          gif_image->row_buffer.data() + img_row_offset_, &img_row_avail_size_);
+      if (ret == LZWDecompressor::Status::kError) {
         DecodingFailureAtTailCleanup(gif_image);
-        return CFX_GifDecodeStatus::Error;
+        return GifDecoder::Status::kError;
       }
 
-      while (ret != CFX_GifDecodeStatus::Error) {
-        if (ret == CFX_GifDecodeStatus::Success) {
-          ReadScanline(gif_image->row_num, gif_image->row_buffer.data());
+      while (ret != LZWDecompressor::Status::kError) {
+        if (ret == LZWDecompressor::Status::kSuccess) {
+          ReadScanline(gif_image->row_num, gif_image->row_buffer);
           gif_image->row_buffer.clear();
           SaveDecodingStatus(GIF_D_STATUS_TAIL);
-          return CFX_GifDecodeStatus::Success;
+          return GifDecoder::Status::kSuccess;
         }
 
-        if (ret == CFX_GifDecodeStatus::Unfinished) {
+        if (ret == LZWDecompressor::Status::kUnfinished) {
           read_marker = input_buffer_->GetPosition();
           if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size)))
-            return CFX_GifDecodeStatus::Unfinished;
+            return GifDecoder::Status::kUnfinished;
 
           if (img_data_size != GIF_BLOCK_TERMINAL) {
             img_data.resize(img_data_size);
             if (!ReadAllOrNone(img_data.data(), img_data_size)) {
               input_buffer_->Seek(read_marker);
-              return CFX_GifDecodeStatus::Unfinished;
+              return GifDecoder::Status::kUnfinished;
             }
 
-            if (!lzw_decompressor_.get())
-              lzw_decompressor_ = CFX_LZWDecompressor::Create(
-                  !gif_image->local_palettes.empty()
-                      ? gif_image->local_pallette_exp
-                      : global_pal_exp_,
-                  gif_image->code_exp);
+            lzw_decompressor_->SetSource(img_data.data(), img_data_size);
+
             SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
             img_row_offset_ += img_row_avail_size_;
             img_row_avail_size_ = gif_img_row_bytes - img_row_offset_;
-            ret = lzw_decompressor_.get()
-                      ? lzw_decompressor_->Decode(
-                            img_data.data(), img_data_size,
-                            gif_image->row_buffer.data() + img_row_offset_,
-                            &img_row_avail_size_)
-                      : CFX_GifDecodeStatus::Error;
+            ret = lzw_decompressor_->Decode(
+                gif_image->row_buffer.data() + img_row_offset_,
+                &img_row_avail_size_);
           }
         }
 
-        if (ret == CFX_GifDecodeStatus::InsufficientDestSize) {
+        if (ret == LZWDecompressor::Status::kInsufficientDestSize) {
           if (gif_image->image_info.local_flags.interlace) {
-            ReadScanline(gif_image->row_num, gif_image->row_buffer.data());
+            ReadScanline(gif_image->row_num, gif_image->row_buffer);
             gif_image->row_num += kGifInterlaceStep[img_pass_num_];
             if (gif_image->row_num >=
                 static_cast<int32_t>(gif_image->image_info.height)) {
               img_pass_num_++;
-              if (img_pass_num_ == FX_ArraySize(kGifInterlaceStep)) {
+              if (img_pass_num_ == std::size(kGifInterlaceStep)) {
                 DecodingFailureAtTailCleanup(gif_image);
-                return CFX_GifDecodeStatus::Error;
+                return GifDecoder::Status::kError;
               }
               gif_image->row_num = kGifInterlaceStep[img_pass_num_] / 2;
             }
           } else {
-            ReadScanline(gif_image->row_num++, gif_image->row_buffer.data());
+            ReadScanline(gif_image->row_num++, gif_image->row_buffer);
           }
+
           img_row_offset_ = 0;
           img_row_avail_size_ = gif_img_row_bytes;
-          ret = lzw_decompressor_.get()
-                    ? lzw_decompressor_->Decode(
-                          img_data.data(), img_data_size,
-                          gif_image->row_buffer.data() + img_row_offset_,
-                          &img_row_avail_size_)
-                    : CFX_GifDecodeStatus::Error;
+          ret = lzw_decompressor_->Decode(
+              gif_image->row_buffer.data() + img_row_offset_,
+              &img_row_avail_size_);
         }
 
-        if (ret == CFX_GifDecodeStatus::InsufficientDestSize ||
-            ret == CFX_GifDecodeStatus::Error) {
+        if (ret == LZWDecompressor::Status::kError) {
           DecodingFailureAtTailCleanup(gif_image);
-          return CFX_GifDecodeStatus::Error;
+          return GifDecoder::Status::kError;
         }
       }
     }
     SaveDecodingStatus(GIF_D_STATUS_TAIL);
   }
-  return CFX_GifDecodeStatus::Error;
+  return GifDecoder::Status::kError;
 }
 
 void CFX_GifContext::SetInputBuffer(RetainPtr<CFX_CodecMemory> codec_memory) {
@@ -341,7 +325,8 @@
   if (!input_buffer_)
     return 0;
 
-  return input_buffer_->GetSize() - input_buffer_->GetPosition();
+  return pdfium::base::checked_cast<uint32_t>(input_buffer_->GetSize() -
+                                              input_buffer_->GetPosition());
 }
 
 bool CFX_GifContext::ReadAllOrNone(uint8_t* dest, uint32_t size) {
@@ -349,7 +334,7 @@
     return false;
 
   size_t read_marker = input_buffer_->GetPosition();
-  size_t read = input_buffer_->ReadBlock(dest, size);
+  size_t read = input_buffer_->ReadBlock({dest, size});
   if (read < size) {
     input_buffer_->Seek(read_marker);
     return false;
@@ -358,30 +343,30 @@
   return true;
 }
 
-CFX_GifDecodeStatus CFX_GifContext::ReadGifSignature() {
+GifDecoder::Status CFX_GifContext::ReadGifSignature() {
   CFX_GifHeader header;
   if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&header), 6))
-    return CFX_GifDecodeStatus::Unfinished;
+    return GifDecoder::Status::kUnfinished;
 
   if (strncmp(header.signature, kGifSignature87, 6) != 0 &&
       strncmp(header.signature, kGifSignature89, 6) != 0) {
-    return CFX_GifDecodeStatus::Error;
+    return GifDecoder::Status::kError;
   }
 
-  return CFX_GifDecodeStatus::Success;
+  return GifDecoder::Status::kSuccess;
 }
 
-CFX_GifDecodeStatus CFX_GifContext::ReadLogicalScreenDescriptor() {
+GifDecoder::Status CFX_GifContext::ReadLogicalScreenDescriptor() {
   CFX_GifLocalScreenDescriptor lsd;
   size_t read_marker = input_buffer_->GetPosition();
 
   if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&lsd), sizeof(lsd)))
-    return CFX_GifDecodeStatus::Unfinished;
+    return GifDecoder::Status::kUnfinished;
 
   if (lsd.global_flags.global_pal) {
     uint32_t palette_count = unsigned(2 << lsd.global_flags.pal_bits);
     if (lsd.bc_index >= palette_count)
-      return CFX_GifDecodeStatus::Error;
+      return GifDecoder::Status::kError;
     bc_index_ = lsd.bc_index;
 
     uint32_t palette_size = palette_count * sizeof(CFX_GifPalette);
@@ -390,62 +375,62 @@
                        palette_size)) {
       // Roll back the read for the LSD
       input_buffer_->Seek(read_marker);
-      return CFX_GifDecodeStatus::Unfinished;
+      return GifDecoder::Status::kUnfinished;
     }
 
-    global_pal_exp_ = lsd.global_flags.pal_bits;
+    global_palette_exp_ = lsd.global_flags.pal_bits;
     global_sort_flag_ = lsd.global_flags.sort_flag;
     global_color_resolution_ = lsd.global_flags.color_resolution;
     std::swap(global_palette_, palette);
   }
 
   width_ = static_cast<int>(
-      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&lsd.width)));
+      FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&lsd.width)));
   height_ = static_cast<int>(
-      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&lsd.height)));
+      FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&lsd.height)));
 
-  return CFX_GifDecodeStatus::Success;
+  return GifDecoder::Status::kSuccess;
 }
 
 void CFX_GifContext::SaveDecodingStatus(int32_t status) {
   decode_status_ = status;
 }
 
-CFX_GifDecodeStatus CFX_GifContext::DecodeExtension() {
+GifDecoder::Status CFX_GifContext::DecodeExtension() {
   size_t read_marker = input_buffer_->GetPosition();
 
   switch (decode_status_) {
     case GIF_D_STATUS_EXT_CE: {
       if (!ScanForTerminalMarker()) {
         input_buffer_->Seek(read_marker);
-        return CFX_GifDecodeStatus::Unfinished;
+        return GifDecoder::Status::kUnfinished;
       }
       break;
     }
     case GIF_D_STATUS_EXT_PTE: {
       CFX_GifPlainTextExtension gif_pte;
       if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&gif_pte), sizeof(gif_pte)))
-        return CFX_GifDecodeStatus::Unfinished;
+        return GifDecoder::Status::kUnfinished;
 
       graphic_control_extension_ = nullptr;
       if (!ScanForTerminalMarker()) {
         input_buffer_->Seek(read_marker);
-        return CFX_GifDecodeStatus::Unfinished;
+        return GifDecoder::Status::kUnfinished;
       }
       break;
     }
     case GIF_D_STATUS_EXT_GCE: {
       CFX_GifGraphicControlExtension gif_gce;
       if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&gif_gce), sizeof(gif_gce)))
-        return CFX_GifDecodeStatus::Unfinished;
+        return GifDecoder::Status::kUnfinished;
 
       if (!graphic_control_extension_.get())
         graphic_control_extension_ =
-            pdfium::MakeUnique<CFX_GifGraphicControlExtension>();
+            std::make_unique<CFX_GifGraphicControlExtension>();
       graphic_control_extension_->block_size = gif_gce.block_size;
       graphic_control_extension_->gce_flags = gif_gce.gce_flags;
-      graphic_control_extension_->delay_time =
-          FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&gif_gce.delay_time));
+      graphic_control_extension_->delay_time = FXSYS_UINT16_GET_LSBFIRST(
+          reinterpret_cast<uint8_t*>(&gif_gce.delay_time));
       graphic_control_extension_->trans_index = gif_gce.trans_index;
       break;
     }
@@ -454,47 +439,47 @@
         graphic_control_extension_ = nullptr;
       if (!ScanForTerminalMarker()) {
         input_buffer_->Seek(read_marker);
-        return CFX_GifDecodeStatus::Unfinished;
+        return GifDecoder::Status::kUnfinished;
       }
     }
   }
 
   SaveDecodingStatus(GIF_D_STATUS_SIG);
-  return CFX_GifDecodeStatus::Success;
+  return GifDecoder::Status::kSuccess;
 }
 
-CFX_GifDecodeStatus CFX_GifContext::DecodeImageInfo() {
+GifDecoder::Status CFX_GifContext::DecodeImageInfo() {
   if (width_ <= 0 || height_ <= 0)
-    return CFX_GifDecodeStatus::Error;
+    return GifDecoder::Status::kError;
 
   size_t read_marker = input_buffer_->GetPosition();
-  CFX_CFX_GifImageInfo img_info;
+  CFX_GifImageInfo img_info;
   if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&img_info), sizeof(img_info)))
-    return CFX_GifDecodeStatus::Unfinished;
+    return GifDecoder::Status::kUnfinished;
 
-  auto gif_image = pdfium::MakeUnique<CFX_GifImage>();
+  auto gif_image = std::make_unique<CFX_GifImage>();
   gif_image->image_info.left =
-      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.left));
+      FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.left));
   gif_image->image_info.top =
-      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.top));
+      FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.top));
   gif_image->image_info.width =
-      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.width));
+      FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.width));
   gif_image->image_info.height =
-      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.height));
+      FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.height));
   gif_image->image_info.local_flags = img_info.local_flags;
   if (gif_image->image_info.left + gif_image->image_info.width > width_ ||
       gif_image->image_info.top + gif_image->image_info.height > height_)
-    return CFX_GifDecodeStatus::Error;
+    return GifDecoder::Status::kError;
 
   CFX_GifLocalFlags* gif_img_info_lf = &img_info.local_flags;
   if (gif_img_info_lf->local_pal) {
-    gif_image->local_pallette_exp = gif_img_info_lf->pal_bits;
+    gif_image->local_palette_exp = gif_img_info_lf->pal_bits;
     uint32_t loc_pal_count = unsigned(2 << gif_img_info_lf->pal_bits);
     std::vector<CFX_GifPalette> loc_pal(loc_pal_count);
     if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(loc_pal.data()),
                        loc_pal_count * sizeof(CFX_GifPalette))) {
       input_buffer_->Seek(read_marker);
-      return CFX_GifDecodeStatus::Unfinished;
+      return GifDecoder::Status::kUnfinished;
     }
 
     gif_image->local_palettes = std::move(loc_pal);
@@ -503,22 +488,20 @@
   uint8_t code_size;
   if (!ReadAllOrNone(&code_size, sizeof(code_size))) {
     input_buffer_->Seek(read_marker);
-    return CFX_GifDecodeStatus::Unfinished;
+    return GifDecoder::Status::kUnfinished;
   }
 
   gif_image->code_exp = code_size;
-  RecordCurrentPosition(&gif_image->data_pos);
-  gif_image->data_pos += input_buffer_->GetPosition();
+  gif_image->data_pos = delegate_->GifCurrentPosition();
   gif_image->image_GCE = nullptr;
   if (graphic_control_extension_.get()) {
     if (graphic_control_extension_->gce_flags.transparency) {
       // Need to test that the color that is going to be transparent is actually
       // in the palette being used.
       if (graphic_control_extension_->trans_index >=
-          2 << (gif_image->local_palettes.empty()
-                    ? global_pal_exp_
-                    : gif_image->local_pallette_exp))
-        return CFX_GifDecodeStatus::Error;
+          (2 << GetPaletteExp(gif_image.get()))) {
+        return GifDecoder::Status::kError;
+      }
     }
     gif_image->image_GCE = std::move(graphic_control_extension_);
     graphic_control_extension_ = nullptr;
@@ -526,7 +509,7 @@
 
   images_.push_back(std::move(gif_image));
   SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
-  return CFX_GifDecodeStatus::Success;
+  return GifDecoder::Status::kSuccess;
 }
 
 void CFX_GifContext::DecodingFailureAtTailCleanup(CFX_GifImage* gif_image) {
@@ -550,4 +533,9 @@
   return true;
 }
 
+uint8_t CFX_GifContext::GetPaletteExp(CFX_GifImage* gif_image) const {
+  return !gif_image->local_palettes.empty() ? gif_image->local_palette_exp
+                                            : global_palette_exp_;
+}
+
 }  // namespace fxcodec
diff --git a/core/fxcodec/gif/cfx_gifcontext.h b/core/fxcodec/gif/cfx_gifcontext.h
index d2ccd9d..e4d89c0 100644
--- a/core/fxcodec/gif/cfx_gifcontext.h
+++ b/core/fxcodec/gif/cfx_gifcontext.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,22 +11,22 @@
 #include <vector>
 
 #include "core/fxcodec/gif/cfx_gif.h"
-#include "core/fxcodec/gif/cfx_lzwdecompressor.h"
-#include "core/fxcodec/gif/gifmodule.h"
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcodec/gif/gif_decoder.h"
+#include "core/fxcodec/gif/lzw_decompressor.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_CodecMemory;
 
 namespace fxcodec {
 
-class CFX_GifContext : public ModuleIface::Context {
+class CFX_GifContext : public ProgressiveDecoderIface::Context {
  public:
-  CFX_GifContext(GifModule* gif_module, GifModule::Delegate* delegate);
+  explicit CFX_GifContext(GifDecoder::Delegate* delegate);
   ~CFX_GifContext() override;
 
-  void RecordCurrentPosition(uint32_t* cur_pos);
-  void ReadScanline(int32_t row_num, uint8_t* row_buf);
+  void ReadScanline(int32_t row_num, pdfium::span<uint8_t> row_buf);
   bool GetRecordPosition(uint32_t cur_pos,
                          int32_t left,
                          int32_t top,
@@ -34,28 +34,24 @@
                          int32_t height,
                          int32_t pal_num,
                          CFX_GifPalette* pal,
-                         int32_t delay_time,
-                         bool user_input,
                          int32_t trans_index,
-                         int32_t disposal_method,
                          bool interlace);
-  CFX_GifDecodeStatus ReadHeader();
-  CFX_GifDecodeStatus GetFrame();
-  CFX_GifDecodeStatus LoadFrame(int32_t frame_num);
+  GifDecoder::Status ReadHeader();
+  GifDecoder::Status GetFrame();
+  GifDecoder::Status LoadFrame(size_t frame_num);
   void SetInputBuffer(RetainPtr<CFX_CodecMemory> codec_memory);
   uint32_t GetAvailInput() const;
   size_t GetFrameNum() const { return images_.size(); }
 
-  UnownedPtr<GifModule> const gif_module_;
-  UnownedPtr<GifModule::Delegate> const delegate_;
+  UnownedPtr<GifDecoder::Delegate> const delegate_;
   std::vector<CFX_GifPalette> global_palette_;
-  uint8_t global_pal_exp_ = 0;
+  uint8_t global_palette_exp_ = 0;
   uint32_t img_row_offset_ = 0;
   uint32_t img_row_avail_size_ = 0;
   int32_t decode_status_ = GIF_D_STATUS_SIG;
   std::unique_ptr<CFX_GifGraphicControlExtension> graphic_control_extension_;
   std::vector<std::unique_ptr<CFX_GifImage>> images_;
-  std::unique_ptr<CFX_LZWDecompressor> lzw_decompressor_;
+  std::unique_ptr<LZWDecompressor> lzw_decompressor_;
   int width_ = 0;
   int height_ = 0;
   uint8_t bc_index_ = 0;
@@ -65,17 +61,18 @@
 
  protected:
   bool ReadAllOrNone(uint8_t* dest, uint32_t size);
-  CFX_GifDecodeStatus ReadGifSignature();
-  CFX_GifDecodeStatus ReadLogicalScreenDescriptor();
+  GifDecoder::Status ReadGifSignature();
+  GifDecoder::Status ReadLogicalScreenDescriptor();
 
   RetainPtr<CFX_CodecMemory> input_buffer_;
 
  private:
   void SaveDecodingStatus(int32_t status);
-  CFX_GifDecodeStatus DecodeExtension();
-  CFX_GifDecodeStatus DecodeImageInfo();
+  GifDecoder::Status DecodeExtension();
+  GifDecoder::Status DecodeImageInfo();
   void DecodingFailureAtTailCleanup(CFX_GifImage* gif_image);
   bool ScanForTerminalMarker();
+  uint8_t GetPaletteExp(CFX_GifImage* gif_image) const;
 };
 
 }  // namespace fxcodec
diff --git a/core/fxcodec/gif/cfx_gifcontext_unittest.cpp b/core/fxcodec/gif/cfx_gifcontext_unittest.cpp
index 20d34ea..ad731bb 100644
--- a/core/fxcodec/gif/cfx_gifcontext_unittest.cpp
+++ b/core/fxcodec/gif/cfx_gifcontext_unittest.cpp
@@ -1,21 +1,24 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxcodec/gif/cfx_gifcontext.h"
 
+#include <stdint.h>
+
 #include <utility>
 
 #include "core/fxcodec/cfx_codec_memory.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/span_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace fxcodec {
 
 class CFX_GifContextForTest final : public CFX_GifContext {
  public:
-  CFX_GifContextForTest(GifModule* gif_module, GifModule::Delegate* delegate)
-      : CFX_GifContext(gif_module, delegate) {}
-  ~CFX_GifContextForTest() override {}
+  CFX_GifContextForTest() : CFX_GifContext(nullptr) {}
+  ~CFX_GifContextForTest() override = default;
 
   using CFX_GifContext::ReadAllOrNone;
   using CFX_GifContext::ReadGifSignature;
@@ -24,14 +27,14 @@
   CFX_CodecMemory* InputBuffer() const { return input_buffer_.Get(); }
   void SetTestInputBuffer(pdfium::span<uint8_t> input) {
     auto pMemory = pdfium::MakeRetain<CFX_CodecMemory>(input.size());
-    memcpy(pMemory->GetBuffer(), input.data(), input.size());
+    fxcrt::spancpy(pMemory->GetBufferSpan(), input);
     SetInputBuffer(std::move(pMemory));
   }
 };
 
 TEST(CFX_GifContext, SetInputBuffer) {
   uint8_t buffer[] = {0x00, 0x01, 0x02};
-  CFX_GifContextForTest context(nullptr, nullptr);
+  CFX_GifContextForTest context;
 
   context.SetTestInputBuffer({nullptr, 0});
   EXPECT_EQ(0u, context.InputBuffer()->GetSize());
@@ -47,10 +50,10 @@
 }
 
 TEST(CFX_GifContext, ReadAllOrNone) {
-  std::vector<uint8_t> dest_buffer;
+  DataVector<uint8_t> dest_buffer;
   uint8_t src_buffer[] = {0x00, 0x01, 0x02, 0x03, 0x04,
                           0x05, 0x06, 0x07, 0x08, 0x09};
-  CFX_GifContextForTest context(nullptr, nullptr);
+  CFX_GifContextForTest context;
 
   context.SetTestInputBuffer({nullptr, 0});
   EXPECT_FALSE(context.ReadAllOrNone(nullptr, 0));
@@ -85,11 +88,11 @@
 }
 
 TEST(CFX_GifContext, ReadGifSignature) {
-  CFX_GifContextForTest context(nullptr, nullptr);
+  CFX_GifContextForTest context;
   {
     uint8_t data[1];
     context.SetTestInputBuffer({data, 0});
-    EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadGifSignature());
+    EXPECT_EQ(GifDecoder::Status::kUnfinished, context.ReadGifSignature());
     EXPECT_EQ(0u, context.InputBuffer()->GetPosition());
     context.SetTestInputBuffer({});
   }
@@ -97,14 +100,14 @@
   {
     uint8_t data[] = {'G', 'I', 'F'};
     context.SetTestInputBuffer(data);
-    EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadGifSignature());
+    EXPECT_EQ(GifDecoder::Status::kUnfinished, context.ReadGifSignature());
     EXPECT_EQ(0u, context.InputBuffer()->GetPosition());
     context.SetTestInputBuffer({});
   }
   {
     uint8_t data[] = {'N', 'O', 'T', 'G', 'I', 'F'};
     context.SetTestInputBuffer(data);
-    EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature());
+    EXPECT_EQ(GifDecoder::Status::kError, context.ReadGifSignature());
     EXPECT_EQ(6u, context.InputBuffer()->GetPosition());
     context.SetTestInputBuffer({});
   }
@@ -112,7 +115,7 @@
   {
     uint8_t data[] = {'G', 'I', 'F', '8', '0', 'a'};
     context.SetTestInputBuffer(data);
-    EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature());
+    EXPECT_EQ(GifDecoder::Status::kError, context.ReadGifSignature());
     EXPECT_EQ(6u, context.InputBuffer()->GetPosition());
     context.SetTestInputBuffer({});
   }
@@ -120,7 +123,7 @@
   {
     uint8_t data[] = {'G', 'I', 'F', '9', '2', 'a'};
     context.SetTestInputBuffer(data);
-    EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature());
+    EXPECT_EQ(GifDecoder::Status::kError, context.ReadGifSignature());
     EXPECT_EQ(6u, context.InputBuffer()->GetPosition());
     context.SetTestInputBuffer({});
   }
@@ -128,7 +131,7 @@
   {
     uint8_t data[] = {'G', 'I', 'F', '8', '7', 'a'};
     context.SetTestInputBuffer(data);
-    EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadGifSignature());
+    EXPECT_EQ(GifDecoder::Status::kSuccess, context.ReadGifSignature());
     EXPECT_EQ(6u, context.InputBuffer()->GetPosition());
     context.SetTestInputBuffer({});
   }
@@ -136,18 +139,18 @@
   {
     uint8_t data[] = {'G', 'I', 'F', '8', '9', 'a'};
     context.SetTestInputBuffer(data);
-    EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadGifSignature());
+    EXPECT_EQ(GifDecoder::Status::kSuccess, context.ReadGifSignature());
     EXPECT_EQ(6u, context.InputBuffer()->GetPosition());
     context.SetTestInputBuffer({});
   }
 }
 
 TEST(CFX_GifContext, ReadLocalScreenDescriptor) {
-  CFX_GifContextForTest context(nullptr, nullptr);
+  CFX_GifContextForTest context;
   {
     uint8_t data[1];
     context.SetTestInputBuffer({data, 0});
-    EXPECT_EQ(CFX_GifDecodeStatus::Unfinished,
+    EXPECT_EQ(GifDecoder::Status::kUnfinished,
               context.ReadLogicalScreenDescriptor());
     context.SetTestInputBuffer({});
   }
@@ -157,7 +160,7 @@
     memset(&lsd, 0, sizeof(CFX_GifLocalScreenDescriptor));
     context.SetTestInputBuffer(lsd);
 
-    EXPECT_EQ(CFX_GifDecodeStatus::Success,
+    EXPECT_EQ(GifDecoder::Status::kSuccess,
               context.ReadLogicalScreenDescriptor());
 
     EXPECT_EQ(sizeof(CFX_GifLocalScreenDescriptor),
@@ -173,7 +176,7 @@
                                                          0x00, 0x01, 0x02};
     context.SetTestInputBuffer(lsd);
 
-    EXPECT_EQ(CFX_GifDecodeStatus::Success,
+    EXPECT_EQ(GifDecoder::Status::kSuccess,
               context.ReadLogicalScreenDescriptor());
 
     EXPECT_EQ(sizeof(CFX_GifLocalScreenDescriptor),
@@ -189,7 +192,7 @@
                                                          0x80, 0x01, 0x02};
     context.SetTestInputBuffer(lsd);
 
-    EXPECT_EQ(CFX_GifDecodeStatus::Unfinished,
+    EXPECT_EQ(GifDecoder::Status::kUnfinished,
               context.ReadLogicalScreenDescriptor());
 
     EXPECT_EQ(0u, context.InputBuffer()->GetPosition());
@@ -205,14 +208,14 @@
     context.SetTestInputBuffer(
         {reinterpret_cast<uint8_t*>(&data), sizeof(data)});
 
-    EXPECT_EQ(CFX_GifDecodeStatus::Success,
+    EXPECT_EQ(GifDecoder::Status::kSuccess,
               context.ReadLogicalScreenDescriptor());
 
     EXPECT_EQ(sizeof(data), context.InputBuffer()->GetPosition());
     EXPECT_EQ(0x000A, context.width_);
     EXPECT_EQ(0x0F00, context.height_);
     EXPECT_EQ(1u, context.bc_index_);
-    EXPECT_EQ(1u, context.global_pal_exp_);
+    EXPECT_EQ(1u, context.global_palette_exp_);
     EXPECT_EQ(1, context.global_sort_flag_);
     EXPECT_EQ(2, context.global_color_resolution_);
     EXPECT_EQ(0, memcmp(data.palette, context.global_palette_.data(),
@@ -222,7 +225,7 @@
 }
 
 TEST(CFX_GifContext, ReadHeader) {
-  CFX_GifContextForTest context(nullptr, nullptr);
+  CFX_GifContextForTest context;
   // Bad signature
   {
     struct {
@@ -233,7 +236,7 @@
     context.SetTestInputBuffer(
         {reinterpret_cast<uint8_t*>(&data), sizeof(data)});
 
-    EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadHeader());
+    EXPECT_EQ(GifDecoder::Status::kError, context.ReadHeader());
     EXPECT_EQ(sizeof(data.signature), context.InputBuffer()->GetPosition());
     context.SetTestInputBuffer({});
   }
@@ -243,7 +246,7 @@
     context.SetTestInputBuffer(
         {reinterpret_cast<uint8_t*>(&signature), sizeof(signature)});
 
-    EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadHeader());
+    EXPECT_EQ(GifDecoder::Status::kUnfinished, context.ReadHeader());
     EXPECT_EQ(sizeof(signature), context.InputBuffer()->GetPosition());
     context.SetTestInputBuffer({});
   }
@@ -257,7 +260,7 @@
     context.SetTestInputBuffer(
         {reinterpret_cast<uint8_t*>(&data), sizeof(data)});
 
-    EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadHeader());
+    EXPECT_EQ(GifDecoder::Status::kSuccess, context.ReadHeader());
     EXPECT_EQ(sizeof(data), context.InputBuffer()->GetPosition());
     EXPECT_EQ(0x000A, context.width_);
     EXPECT_EQ(0x0F00, context.height_);
@@ -274,7 +277,7 @@
     context.SetTestInputBuffer(
         {reinterpret_cast<uint8_t*>(&data), sizeof(data)});
 
-    EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadHeader());
+    EXPECT_EQ(GifDecoder::Status::kUnfinished, context.ReadHeader());
     EXPECT_EQ(sizeof(data.signature), context.InputBuffer()->GetPosition());
     context.SetTestInputBuffer({});
   }
@@ -290,12 +293,12 @@
     context.SetTestInputBuffer(
         {reinterpret_cast<uint8_t*>(&data), sizeof(data)});
 
-    EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadHeader());
+    EXPECT_EQ(GifDecoder::Status::kSuccess, context.ReadHeader());
     EXPECT_EQ(sizeof(data), context.InputBuffer()->GetPosition());
     EXPECT_EQ(0x000A, context.width_);
     EXPECT_EQ(0x0F00, context.height_);
     EXPECT_EQ(1u, context.bc_index_);
-    EXPECT_EQ(1u, context.global_pal_exp_);
+    EXPECT_EQ(1u, context.global_palette_exp_);
     EXPECT_EQ(1, context.global_sort_flag_);
     EXPECT_EQ(2, context.global_color_resolution_);
     EXPECT_EQ(0, memcmp(data.palette, context.global_palette_.data(),
diff --git a/core/fxcodec/gif/cfx_lzwdecompressor.cpp b/core/fxcodec/gif/cfx_lzwdecompressor.cpp
deleted file mode 100644
index 12e0d89..0000000
--- a/core/fxcodec/gif/cfx_lzwdecompressor.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/gif/cfx_lzwdecompressor.h"
-
-#include <algorithm>
-#include <cstring>
-#include <memory>
-#include <utility>
-
-#include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/numerics/safe_math.h"
-
-std::unique_ptr<CFX_LZWDecompressor> CFX_LZWDecompressor::Create(
-    uint8_t color_exp,
-    uint8_t code_exp) {
-  // color_exp generates 2^(n + 1) codes, where as the code_exp reserves 2^n.
-  // This is a quirk of the GIF spec.
-  if (code_exp > GIF_MAX_LZW_EXP || code_exp < color_exp + 1)
-    return nullptr;
-  return std::unique_ptr<CFX_LZWDecompressor>(
-      new CFX_LZWDecompressor(color_exp, code_exp));
-}
-
-CFX_LZWDecompressor::CFX_LZWDecompressor(uint8_t color_exp, uint8_t code_exp)
-    : code_size_(code_exp),
-      code_size_cur_(0),
-      code_color_end_(static_cast<uint16_t>(1 << (color_exp + 1))),
-      code_clear_(static_cast<uint16_t>(1 << code_exp)),
-      code_end_(static_cast<uint16_t>((1 << code_exp) + 1)),
-      code_next_(0),
-      code_first_(0),
-      code_old_(0),
-      next_in_(nullptr),
-      avail_in_(0),
-      bits_left_(0),
-      code_store_(0) {}
-
-CFX_LZWDecompressor::~CFX_LZWDecompressor() {}
-
-CFX_GifDecodeStatus CFX_LZWDecompressor::Decode(const uint8_t* src_buf,
-                                                uint32_t src_size,
-                                                uint8_t* dest_buf,
-                                                uint32_t* dest_size) {
-  if (!src_buf || src_size == 0 || !dest_buf || !dest_size)
-    return CFX_GifDecodeStatus::Error;
-
-  if (*dest_size == 0)
-    return CFX_GifDecodeStatus::InsufficientDestSize;
-
-  next_in_ = src_buf;
-  avail_in_ = src_size;
-
-  ClearTable();
-
-  uint32_t i = 0;
-  if (decompressed_next_ != 0) {
-    uint32_t extracted_size = ExtractData(dest_buf, *dest_size);
-    if (decompressed_next_ != 0)
-      return CFX_GifDecodeStatus::InsufficientDestSize;
-
-    dest_buf += extracted_size;
-    i += extracted_size;
-  }
-
-  while (i <= *dest_size && (avail_in_ > 0 || bits_left_ >= code_size_cur_)) {
-    if (code_size_cur_ > GIF_MAX_LZW_EXP)
-      return CFX_GifDecodeStatus::Error;
-
-    if (avail_in_ > 0) {
-      if (bits_left_ > 31)
-        return CFX_GifDecodeStatus::Error;
-
-      FX_SAFE_UINT32 safe_code = *next_in_++;
-      safe_code <<= bits_left_;
-      safe_code |= code_store_;
-      if (!safe_code.IsValid())
-        return CFX_GifDecodeStatus::Error;
-
-      code_store_ = safe_code.ValueOrDie();
-      --avail_in_;
-      bits_left_ += 8;
-    }
-
-    while (bits_left_ >= code_size_cur_) {
-      uint16_t code =
-          static_cast<uint16_t>(code_store_) & ((1 << code_size_cur_) - 1);
-      code_store_ >>= code_size_cur_;
-      bits_left_ -= code_size_cur_;
-      if (code == code_clear_) {
-        ClearTable();
-        continue;
-      }
-      if (code == code_end_) {
-        *dest_size = i;
-        return CFX_GifDecodeStatus::Success;
-      }
-
-      if (code_old_ != static_cast<uint16_t>(-1)) {
-        if (code_next_ < GIF_MAX_LZW_CODE) {
-          if (code == code_next_) {
-            AddCode(code_old_, code_first_);
-            if (!DecodeString(code))
-              return CFX_GifDecodeStatus::Error;
-          } else if (code > code_next_) {
-            return CFX_GifDecodeStatus::Error;
-          } else {
-            if (!DecodeString(code))
-              return CFX_GifDecodeStatus::Error;
-
-            uint8_t append_char = decompressed_[decompressed_next_ - 1];
-            AddCode(code_old_, append_char);
-          }
-        }
-      } else {
-        if (!DecodeString(code))
-          return CFX_GifDecodeStatus::Error;
-      }
-
-      code_old_ = code;
-      uint32_t extracted_size = ExtractData(dest_buf, *dest_size - i);
-      if (decompressed_next_ != 0)
-        return CFX_GifDecodeStatus::InsufficientDestSize;
-
-      dest_buf += extracted_size;
-      i += extracted_size;
-    }
-  }
-
-  if (avail_in_ != 0)
-    return CFX_GifDecodeStatus::Error;
-
-  *dest_size = i;
-  return CFX_GifDecodeStatus::Unfinished;
-}
-
-void CFX_LZWDecompressor::ClearTable() {
-  code_size_cur_ = code_size_ + 1;
-  code_next_ = code_end_ + 1;
-  code_old_ = static_cast<uint16_t>(-1);
-  memset(code_table_, 0, sizeof(code_table_));
-  for (uint16_t i = 0; i < code_clear_; i++)
-    code_table_[i].suffix = static_cast<uint8_t>(i);
-  decompressed_.resize(code_next_ - code_clear_ + 1);
-  decompressed_next_ = 0;
-}
-
-void CFX_LZWDecompressor::AddCode(uint16_t prefix_code, uint8_t append_char) {
-  if (code_next_ == GIF_MAX_LZW_CODE)
-    return;
-
-  code_table_[code_next_].prefix = prefix_code;
-  code_table_[code_next_].suffix = append_char;
-  if (++code_next_ < GIF_MAX_LZW_CODE) {
-    if (code_next_ >> code_size_cur_)
-      code_size_cur_++;
-  }
-}
-
-bool CFX_LZWDecompressor::DecodeString(uint16_t code) {
-  decompressed_.resize(code_next_ - code_clear_ + 1);
-  decompressed_next_ = 0;
-
-  while (code >= code_clear_ && code <= code_next_) {
-    if (code == code_table_[code].prefix ||
-        decompressed_next_ >= decompressed_.size())
-      return false;
-
-    decompressed_[decompressed_next_++] = code_table_[code].suffix;
-    code = code_table_[code].prefix;
-  }
-
-  if (code >= code_color_end_)
-    return false;
-
-  decompressed_[decompressed_next_++] = static_cast<uint8_t>(code);
-  code_first_ = static_cast<uint8_t>(code);
-  return true;
-}
-
-uint32_t CFX_LZWDecompressor::ExtractData(uint8_t* dest_buf,
-                                          uint32_t dest_size) {
-  if (dest_size == 0)
-    return 0;
-
-  uint32_t copy_size = dest_size <= decompressed_next_
-                           ? dest_size
-                           : static_cast<uint32_t>(decompressed_next_);
-  std::reverse_copy(decompressed_.data() + decompressed_next_ - copy_size,
-                    decompressed_.data() + decompressed_next_, dest_buf);
-  decompressed_next_ -= copy_size;
-  return copy_size;
-}
diff --git a/core/fxcodec/gif/cfx_lzwdecompressor.h b/core/fxcodec/gif/cfx_lzwdecompressor.h
deleted file mode 100644
index d3ec588..0000000
--- a/core/fxcodec/gif/cfx_lzwdecompressor.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_GIF_CFX_LZWDECOMPRESSOR_H_
-#define CORE_FXCODEC_GIF_CFX_LZWDECOMPRESSOR_H_
-
-#include <memory>
-#include <vector>
-
-#include "core/fxcodec/gif/cfx_gif.h"
-
-class CFX_LZWDecompressor {
- public:
-  struct CodeEntry {
-    uint16_t prefix;
-    uint8_t suffix;
-  };
-
-  // Returns nullptr on error
-  static std::unique_ptr<CFX_LZWDecompressor> Create(uint8_t color_exp,
-                                                     uint8_t code_exp);
-  ~CFX_LZWDecompressor();
-
-  CFX_GifDecodeStatus Decode(const uint8_t* src_buf,
-                             uint32_t src_size,
-                             uint8_t* dest_buf,
-                             uint32_t* dest_size);
-
-  // Used by unittests, should not be called in production code.
-  uint32_t ExtractDataForTest(uint8_t* dest_buf, uint32_t dest_size) {
-    return ExtractData(dest_buf, dest_size);
-  }
-
-  std::vector<uint8_t>* DecompressedForTest() { return &decompressed_; }
-  size_t* DecompressedNextForTest() { return &decompressed_next_; }
-
- private:
-  CFX_LZWDecompressor(uint8_t color_exp, uint8_t code_exp);
-  void ClearTable();
-  void AddCode(uint16_t prefix_code, uint8_t append_char);
-  bool DecodeString(uint16_t code);
-  uint32_t ExtractData(uint8_t* dest_buf, uint32_t dest_size);
-
-  uint8_t code_size_;
-  uint8_t code_size_cur_;
-  uint16_t code_color_end_;
-  uint16_t code_clear_;
-  uint16_t code_end_;
-  uint16_t code_next_;
-  uint8_t code_first_;
-  std::vector<uint8_t> decompressed_;
-  size_t decompressed_next_;
-  uint16_t code_old_;
-  const uint8_t* next_in_;
-  uint32_t avail_in_;
-  uint8_t bits_left_;
-  uint32_t code_store_;
-  CodeEntry code_table_[GIF_MAX_LZW_CODE];
-};
-
-#endif  // CORE_FXCODEC_GIF_CFX_LZWDECOMPRESSOR_H_
diff --git a/core/fxcodec/gif/cfx_lzwdecompressor_unittest.cpp b/core/fxcodec/gif/cfx_lzwdecompressor_unittest.cpp
deleted file mode 100644
index e31fb78..0000000
--- a/core/fxcodec/gif/cfx_lzwdecompressor_unittest.cpp
+++ /dev/null
@@ -1,216 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "core/fxcodec/gif/cfx_lzwdecompressor.h"
-
-#include "core/fxcrt/fx_memory.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-TEST(CFX_LZWDecompressor, CreateBadParams) {
-  EXPECT_EQ(nullptr, CFX_LZWDecompressor::Create(0x10, 0x02));
-  EXPECT_EQ(nullptr, CFX_LZWDecompressor::Create(0x04, 0x0F));
-  EXPECT_EQ(nullptr, CFX_LZWDecompressor::Create(0x02, 0x02));
-}
-
-TEST(CFX_LZWDecompressor, ExtractData) {
-  uint8_t palette_exp = 0x1;
-  uint8_t code_exp = 0x2;
-  auto decompressor = CFX_LZWDecompressor::Create(palette_exp, code_exp);
-  ASSERT_NE(nullptr, decompressor);
-
-  // Check that 0 length extract does nothing
-  {
-    std::vector<uint8_t>* decompressed = decompressor->DecompressedForTest();
-    *decompressed = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-    *(decompressor->DecompressedNextForTest()) = decompressed->size();
-    uint8_t dest_buf[20];
-    memset(dest_buf, static_cast<uint8_t>(-1), sizeof(dest_buf));
-
-    EXPECT_EQ(0u, decompressor->ExtractDataForTest(dest_buf, 0));
-    for (size_t i = 0; i < FX_ArraySize(dest_buf); ++i)
-      EXPECT_EQ(static_cast<uint8_t>(-1), dest_buf[i]);
-
-    EXPECT_EQ(10u, *(decompressor->DecompressedNextForTest()));
-    for (size_t i = 0; i < *(decompressor->DecompressedNextForTest()); ++i)
-      EXPECT_EQ(i, (*decompressed)[i]);
-  }
-
-  // Check that less than decompressed size only gets the expected number
-  {
-    std::vector<uint8_t>* decompressed = decompressor->DecompressedForTest();
-    *decompressed = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-    *(decompressor->DecompressedNextForTest()) = decompressed->size();
-    uint8_t dest_buf[20];
-    memset(dest_buf, static_cast<uint8_t>(-1), sizeof(dest_buf));
-
-    EXPECT_EQ(5u, decompressor->ExtractDataForTest(dest_buf, 5));
-    size_t i = 0;
-    for (; i < 5; ++i)
-      EXPECT_EQ(9 - i, dest_buf[i]);
-    for (; i < FX_ArraySize(dest_buf); ++i)
-      EXPECT_EQ(static_cast<uint8_t>(-1), dest_buf[i]);
-
-    EXPECT_EQ(5u, *(decompressor->DecompressedNextForTest()));
-    for (i = 0; i < *(decompressor->DecompressedNextForTest()); ++i)
-      EXPECT_EQ(i, (*decompressed)[i]);
-  }
-
-  // Check that greater than decompressed size depletes the decompressor
-  {
-    std::vector<uint8_t>* decompressed = decompressor->DecompressedForTest();
-    *decompressed = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-    *(decompressor->DecompressedNextForTest()) = decompressed->size();
-    uint8_t dest_buf[20];
-    memset(dest_buf, static_cast<uint8_t>(-1), sizeof(dest_buf));
-
-    EXPECT_EQ(10u, decompressor->ExtractDataForTest(dest_buf,
-                                                    FX_ArraySize(dest_buf)));
-    size_t i = 0;
-    for (; i < 10; ++i)
-      EXPECT_EQ(9 - i, dest_buf[i]);
-    for (; i < FX_ArraySize(dest_buf); ++i)
-      EXPECT_EQ(static_cast<uint8_t>(-1), dest_buf[i]);
-
-    EXPECT_EQ(0u, *(decompressor->DecompressedNextForTest()));
-  }
-}
-
-TEST(CFX_LZWDecompressor, DecodeBadParams) {
-  uint8_t palette_exp = 0x0;
-  uint8_t code_exp = 0x2;
-  auto decompressor = CFX_LZWDecompressor::Create(palette_exp, code_exp);
-  ASSERT_NE(nullptr, decompressor);
-
-  uint8_t image_data[10];
-  uint32_t image_size = FX_ArraySize(image_data);
-
-  uint8_t output_data[10];
-  uint32_t output_size = FX_ArraySize(output_data);
-
-  EXPECT_EQ(
-      CFX_GifDecodeStatus::Error,
-      decompressor->Decode(nullptr, image_size, output_data, &output_size));
-  EXPECT_EQ(CFX_GifDecodeStatus::Error,
-            decompressor->Decode(image_data, 0, output_data, &output_size));
-  EXPECT_EQ(
-      CFX_GifDecodeStatus::Error,
-      decompressor->Decode(image_data, image_size, nullptr, &output_size));
-  EXPECT_EQ(CFX_GifDecodeStatus::Error,
-            decompressor->Decode(image_data, image_size, output_data, nullptr));
-
-  output_size = 0;
-  EXPECT_EQ(
-      CFX_GifDecodeStatus::InsufficientDestSize,
-      decompressor->Decode(image_data, image_size, output_data, &output_size));
-}
-
-TEST(CFX_LZWDecompressor, Decode1x1SingleColour) {
-  uint8_t palette_exp = 0x0;
-  uint8_t code_exp = 0x2;
-  auto decompressor = CFX_LZWDecompressor::Create(palette_exp, code_exp);
-  ASSERT_NE(nullptr, decompressor);
-
-  uint8_t image_data[] = {0x44, 0x01};
-  uint32_t image_size = FX_ArraySize(image_data);
-
-  uint8_t expected_data[] = {0x00};
-  uint8_t output_data[FX_ArraySize(expected_data)];
-  memset(output_data, 0, sizeof(output_data));
-  uint32_t output_size = FX_ArraySize(output_data);
-
-  EXPECT_EQ(
-      CFX_GifDecodeStatus::Success,
-      decompressor->Decode(image_data, image_size, output_data, &output_size));
-
-  EXPECT_EQ(FX_ArraySize(output_data), output_size);
-  EXPECT_TRUE(0 == memcmp(expected_data, output_data, sizeof(expected_data)));
-}
-
-TEST(CFX_LZWDecompressor, Decode10x10SingleColour) {
-  uint8_t palette_exp = 0x0;
-  uint8_t code_exp = 0x2;
-  auto decompressor = CFX_LZWDecompressor::Create(palette_exp, code_exp);
-  ASSERT_NE(nullptr, decompressor);
-
-  static constexpr uint8_t kImageData[] = {0x84, 0x8F, 0xA9, 0xCB,
-                                           0xED, 0x0F, 0x63, 0x2B};
-  uint32_t image_size = FX_ArraySize(kImageData);
-
-  static constexpr uint8_t kExpectedData[] = {
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00};
-  uint8_t output_data[FX_ArraySize(kExpectedData)];
-  memset(output_data, 0, sizeof(output_data));
-  uint32_t output_size = FX_ArraySize(output_data);
-
-  EXPECT_EQ(
-      CFX_GifDecodeStatus::Success,
-      decompressor->Decode(kImageData, image_size, output_data, &output_size));
-
-  EXPECT_EQ(FX_ArraySize(output_data), output_size);
-  EXPECT_TRUE(0 == memcmp(kExpectedData, output_data, sizeof(kExpectedData)));
-}
-
-TEST(CFX_LZWDecompressor, Decode10x10MultipleColour) {
-  uint8_t palette_exp = 0x1;
-  uint8_t code_exp = 0x2;
-  auto decompressor = CFX_LZWDecompressor::Create(palette_exp, code_exp);
-  ASSERT_NE(nullptr, decompressor);
-
-  static constexpr uint8_t kImageData[] = {
-      0x8C, 0x2D, 0x99, 0x87, 0x2A, 0x1C, 0xDC, 0x33, 0xA0, 0x02, 0x75,
-      0xEC, 0x95, 0xFA, 0xA8, 0xDE, 0x60, 0x8C, 0x04, 0x91, 0x4C, 0x01};
-  uint32_t image_size = FX_ArraySize(kImageData);
-
-  static constexpr uint8_t kExpectedData[] = {
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
-      0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02,
-      0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
-      0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02,
-      0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
-      0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01,
-      0x01, 0x01, 0x01, 0x01};
-
-  uint8_t output_data[FX_ArraySize(kExpectedData)];
-  memset(output_data, 0, sizeof(output_data));
-  uint32_t output_size = FX_ArraySize(output_data);
-
-  EXPECT_EQ(
-      CFX_GifDecodeStatus::Success,
-      decompressor->Decode(kImageData, image_size, output_data, &output_size));
-
-  EXPECT_EQ(FX_ArraySize(output_data), output_size);
-  EXPECT_TRUE(0 == memcmp(kExpectedData, output_data, sizeof(kExpectedData)));
-}
-
-TEST(CFX_LZWDecompressor, HandleColourCodeOutOfPalette) {
-  uint8_t palette_exp = 0x2;  // Image uses 10 colours, so the palette exp
-                              // should be 3, 2^(3+1) = 16 colours.
-  uint8_t code_exp = 0x4;
-  auto decompressor = CFX_LZWDecompressor::Create(palette_exp, code_exp);
-  ASSERT_NE(nullptr, decompressor);
-
-  static constexpr uint8_t kImageData[] = {
-      0x30, 0xC9, 0x49, 0x81, 0xBD, 0x78, 0xE8, 0xCD, 0x89, 0xFF,
-      0x60, 0x20, 0x8E, 0xE4, 0x61, 0x9E, 0xA8, 0xA1, 0xAE, 0x2C,
-      0xE2, 0xBE, 0xB0, 0x20, 0xCF, 0x74, 0x61, 0xDF, 0x78, 0x04};
-  uint32_t image_size = FX_ArraySize(kImageData);
-
-  uint8_t output_data[100];  // The uncompressed data is for a 10x10 image
-  memset(output_data, 0, sizeof(output_data));
-  uint32_t output_size = FX_ArraySize(output_data);
-
-  EXPECT_EQ(
-      CFX_GifDecodeStatus::Error,
-      decompressor->Decode(kImageData, image_size, output_data, &output_size));
-}
diff --git a/core/fxcodec/gif/gif_decoder.cpp b/core/fxcodec/gif/gif_decoder.cpp
new file mode 100644
index 0000000..4e7d417
--- /dev/null
+++ b/core/fxcodec/gif/gif_decoder.cpp
@@ -0,0 +1,74 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/gif/gif_decoder.h"
+
+#include "core/fxcodec/cfx_codec_memory.h"
+#include "core/fxcodec/gif/cfx_gifcontext.h"
+#include "core/fxge/dib/fx_dib.h"
+
+namespace fxcodec {
+
+// static
+std::unique_ptr<ProgressiveDecoderIface::Context> GifDecoder::StartDecode(
+    Delegate* pDelegate) {
+  return std::make_unique<CFX_GifContext>(pDelegate);
+}
+
+// static
+GifDecoder::Status GifDecoder::ReadHeader(
+    ProgressiveDecoderIface::Context* pContext,
+    int* width,
+    int* height,
+    int* pal_num,
+    CFX_GifPalette** pal_pp,
+    int* bg_index) {
+  auto* context = static_cast<CFX_GifContext*>(pContext);
+  Status ret = context->ReadHeader();
+  if (ret != Status::kSuccess)
+    return ret;
+
+  *width = context->width_;
+  *height = context->height_;
+  *pal_num = (2 << context->global_palette_exp_);
+  *pal_pp = context->global_palette_.empty() ? nullptr
+                                             : context->global_palette_.data();
+  *bg_index = context->bc_index_;
+  return Status::kSuccess;
+}
+
+// static
+std::pair<GifDecoder::Status, size_t> GifDecoder::LoadFrameInfo(
+    ProgressiveDecoderIface::Context* pContext) {
+  auto* context = static_cast<CFX_GifContext*>(pContext);
+  Status ret = context->GetFrame();
+  if (ret != Status::kSuccess)
+    return {ret, 0};
+  return {Status::kSuccess, context->GetFrameNum()};
+}
+
+// static
+GifDecoder::Status GifDecoder::LoadFrame(
+    ProgressiveDecoderIface::Context* pContext,
+    size_t frame_num) {
+  return static_cast<CFX_GifContext*>(pContext)->LoadFrame(frame_num);
+}
+
+// static
+FX_FILESIZE GifDecoder::GetAvailInput(
+    ProgressiveDecoderIface::Context* pContext) {
+  return static_cast<CFX_GifContext*>(pContext)->GetAvailInput();
+}
+
+// static
+bool GifDecoder::Input(ProgressiveDecoderIface::Context* pContext,
+                       RetainPtr<CFX_CodecMemory> codec_memory) {
+  auto* ctx = static_cast<CFX_GifContext*>(pContext);
+  ctx->SetInputBuffer(std::move(codec_memory));
+  return true;
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/gif/gif_decoder.h b/core/fxcodec/gif/gif_decoder.h
new file mode 100644
index 0000000..1e2d39a
--- /dev/null
+++ b/core/fxcodec/gif/gif_decoder.h
@@ -0,0 +1,70 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_GIF_GIF_DECODER_H_
+#define CORE_FXCODEC_GIF_GIF_DECODER_H_
+
+#include <memory>
+#include <utility>
+
+#include "core/fxcodec/gif/cfx_gif.h"
+#include "core/fxcodec/progressive_decoder_iface.h"
+#include "core/fxcrt/fx_coordinates.h"
+#include "third_party/base/containers/span.h"
+
+#ifndef PDF_ENABLE_XFA_GIF
+#error "GIF must be enabled"
+#endif
+
+namespace fxcodec {
+
+class GifDecoder {
+ public:
+  enum class Status {
+    kError,
+    kSuccess,
+    kUnfinished,
+  };
+
+  class Delegate {
+   public:
+    virtual uint32_t GifCurrentPosition() const = 0;
+    virtual bool GifInputRecordPositionBuf(uint32_t rcd_pos,
+                                           const FX_RECT& img_rc,
+                                           int32_t pal_num,
+                                           CFX_GifPalette* pal_ptr,
+                                           int32_t trans_index,
+                                           bool interlace) = 0;
+    virtual void GifReadScanline(int32_t row_num,
+                                 pdfium::span<uint8_t> row_buf) = 0;
+  };
+
+  static std::unique_ptr<ProgressiveDecoderIface::Context> StartDecode(
+      Delegate* pDelegate);
+  static Status ReadHeader(ProgressiveDecoderIface::Context* context,
+                           int* width,
+                           int* height,
+                           int* pal_num,
+                           CFX_GifPalette** pal_pp,
+                           int* bg_index);
+  static std::pair<Status, size_t> LoadFrameInfo(
+      ProgressiveDecoderIface::Context* context);
+  static Status LoadFrame(ProgressiveDecoderIface::Context* context,
+                          size_t frame_num);
+  static FX_FILESIZE GetAvailInput(ProgressiveDecoderIface::Context* context);
+  static bool Input(ProgressiveDecoderIface::Context* context,
+                    RetainPtr<CFX_CodecMemory> codec_memory);
+
+  GifDecoder() = delete;
+  GifDecoder(const GifDecoder&) = delete;
+  GifDecoder& operator=(const GifDecoder&) = delete;
+};
+
+}  // namespace fxcodec
+
+using GifDecoder = fxcodec::GifDecoder;
+
+#endif  // CORE_FXCODEC_GIF_GIF_DECODER_H_
diff --git a/core/fxcodec/gif/gif_progressive_decoder.cpp b/core/fxcodec/gif/gif_progressive_decoder.cpp
new file mode 100644
index 0000000..78c6a8c
--- /dev/null
+++ b/core/fxcodec/gif/gif_progressive_decoder.cpp
@@ -0,0 +1,33 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/gif/gif_progressive_decoder.h"
+
+#include "core/fxcodec/cfx_codec_memory.h"
+#include "core/fxcodec/gif/gif_decoder.h"
+
+namespace fxcodec {
+
+// static
+GifProgressiveDecoder* GifProgressiveDecoder::GetInstance() {
+  static pdfium::base::NoDestructor<GifProgressiveDecoder> s;
+  return s.get();
+}
+
+GifProgressiveDecoder::GifProgressiveDecoder() = default;
+
+GifProgressiveDecoder::~GifProgressiveDecoder() = default;
+
+FX_FILESIZE GifProgressiveDecoder::GetAvailInput(Context* context) const {
+  return GifDecoder::GetAvailInput(context);
+}
+
+bool GifProgressiveDecoder::Input(Context* context,
+                                  RetainPtr<CFX_CodecMemory> codec_memory) {
+  return GifDecoder::Input(context, codec_memory);
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/gif/gif_progressive_decoder.h b/core/fxcodec/gif/gif_progressive_decoder.h
new file mode 100644
index 0000000..6b58c3c
--- /dev/null
+++ b/core/fxcodec/gif/gif_progressive_decoder.h
@@ -0,0 +1,39 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_GIF_GIF_PROGRESSIVE_DECODER_H_
+#define CORE_FXCODEC_GIF_GIF_PROGRESSIVE_DECODER_H_
+
+#include "core/fxcodec/progressive_decoder_iface.h"
+#include "third_party/base/no_destructor.h"
+
+#ifndef PDF_ENABLE_XFA_GIF
+#error "GIF must be enabled"
+#endif
+
+namespace fxcodec {
+
+class GifProgressiveDecoder final : public ProgressiveDecoderIface {
+ public:
+  static GifProgressiveDecoder* GetInstance();
+
+  // ProgressiveDecoderIface:
+  FX_FILESIZE GetAvailInput(Context* context) const override;
+  bool Input(Context* context,
+             RetainPtr<CFX_CodecMemory> codec_memory) override;
+
+ private:
+  friend pdfium::base::NoDestructor<GifProgressiveDecoder>;
+
+  GifProgressiveDecoder();
+  ~GifProgressiveDecoder() override;
+};
+
+}  // namespace fxcodec
+
+using GifProgressiveDecoder = fxcodec::GifProgressiveDecoder;
+
+#endif  // CORE_FXCODEC_GIF_GIF_PROGRESSIVE_DECODER_H_
diff --git a/core/fxcodec/gif/gifmodule.cpp b/core/fxcodec/gif/gifmodule.cpp
deleted file mode 100644
index 041aa36..0000000
--- a/core/fxcodec/gif/gifmodule.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/gif/gifmodule.h"
-
-#include "core/fxcodec/cfx_codec_memory.h"
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxcodec/gif/cfx_gif.h"
-#include "core/fxcodec/gif/cfx_gifcontext.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/ptr_util.h"
-
-namespace fxcodec {
-
-GifModule::GifModule() = default;
-
-GifModule::~GifModule() = default;
-
-std::unique_ptr<ModuleIface::Context> GifModule::Start(Delegate* pDelegate) {
-  return pdfium::MakeUnique<CFX_GifContext>(this, pDelegate);
-}
-
-CFX_GifDecodeStatus GifModule::ReadHeader(Context* pContext,
-                                          int* width,
-                                          int* height,
-                                          int* pal_num,
-                                          CFX_GifPalette** pal_pp,
-                                          int* bg_index) {
-  auto* context = static_cast<CFX_GifContext*>(pContext);
-  CFX_GifDecodeStatus ret = context->ReadHeader();
-  if (ret != CFX_GifDecodeStatus::Success)
-    return ret;
-
-  *width = context->width_;
-  *height = context->height_;
-  *pal_num = (2 << context->global_pal_exp_);
-  *pal_pp = context->global_palette_.empty() ? nullptr
-                                             : context->global_palette_.data();
-  *bg_index = context->bc_index_;
-  return CFX_GifDecodeStatus::Success;
-}
-
-std::pair<CFX_GifDecodeStatus, size_t> GifModule::LoadFrameInfo(
-    Context* pContext) {
-  auto* context = static_cast<CFX_GifContext*>(pContext);
-  CFX_GifDecodeStatus ret = context->GetFrame();
-  if (ret != CFX_GifDecodeStatus::Success)
-    return {ret, 0};
-  return {CFX_GifDecodeStatus::Success, context->GetFrameNum()};
-}
-
-CFX_GifDecodeStatus GifModule::LoadFrame(Context* pContext, size_t frame_num) {
-  return static_cast<CFX_GifContext*>(pContext)->LoadFrame(frame_num);
-}
-
-FX_FILESIZE GifModule::GetAvailInput(Context* pContext) const {
-  return static_cast<CFX_GifContext*>(pContext)->GetAvailInput();
-}
-
-bool GifModule::Input(Context* pContext,
-                      RetainPtr<CFX_CodecMemory> codec_memory,
-                      CFX_DIBAttribute*) {
-  auto* ctx = static_cast<CFX_GifContext*>(pContext);
-  ctx->SetInputBuffer(std::move(codec_memory));
-  return true;
-}
-
-}  // namespace fxcodec
diff --git a/core/fxcodec/gif/gifmodule.h b/core/fxcodec/gif/gifmodule.h
deleted file mode 100644
index 2944e34..0000000
--- a/core/fxcodec/gif/gifmodule.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_GIF_GIFMODULE_H_
-#define CORE_FXCODEC_GIF_GIFMODULE_H_
-
-#include <memory>
-#include <utility>
-
-#include "core/fxcodec/codec_module_iface.h"
-#include "core/fxcodec/gif/cfx_gif.h"
-#include "core/fxcrt/fx_coordinates.h"
-
-namespace fxcodec {
-
-class CFX_DIBAttribute;
-
-class GifModule final : public ModuleIface {
- public:
-  class Delegate {
-   public:
-    virtual void GifRecordCurrentPosition(uint32_t& cur_pos) = 0;
-    virtual bool GifInputRecordPositionBuf(uint32_t rcd_pos,
-                                           const FX_RECT& img_rc,
-                                           int32_t pal_num,
-                                           CFX_GifPalette* pal_ptr,
-                                           int32_t delay_time,
-                                           bool user_input,
-                                           int32_t trans_index,
-                                           int32_t disposal_method,
-                                           bool interlace) = 0;
-    virtual void GifReadScanline(int32_t row_num, uint8_t* row_buf) = 0;
-  };
-
-  GifModule();
-  ~GifModule() override;
-
-  // ModuleIface:
-  FX_FILESIZE GetAvailInput(Context* context) const override;
-  bool Input(Context* context,
-             RetainPtr<CFX_CodecMemory> codec_memory,
-             CFX_DIBAttribute* pAttribute) override;
-
-  std::unique_ptr<Context> Start(Delegate* pDelegate);
-  CFX_GifDecodeStatus ReadHeader(Context* context,
-                                 int* width,
-                                 int* height,
-                                 int* pal_num,
-                                 CFX_GifPalette** pal_pp,
-                                 int* bg_index);
-  std::pair<CFX_GifDecodeStatus, size_t> LoadFrameInfo(Context* context);
-  CFX_GifDecodeStatus LoadFrame(Context* context, size_t frame_num);
-};
-
-}  // namespace fxcodec
-
-using GifModule = fxcodec::GifModule;
-
-#endif  // CORE_FXCODEC_GIF_GIFMODULE_H_
diff --git a/core/fxcodec/gif/lzw_decompressor.cpp b/core/fxcodec/gif/lzw_decompressor.cpp
new file mode 100644
index 0000000..dbfa0b9
--- /dev/null
+++ b/core/fxcodec/gif/lzw_decompressor.cpp
@@ -0,0 +1,196 @@
+// Copyright 2017 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/gif/lzw_decompressor.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <memory>
+#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"
+
+namespace fxcodec {
+
+std::unique_ptr<LZWDecompressor> LZWDecompressor::Create(uint8_t color_exp,
+                                                         uint8_t code_exp) {
+  // |color_exp| generates 2^(n + 1) codes, where as the code_exp reserves 2^n.
+  // This is a quirk of the GIF spec.
+  if (code_exp > GIF_MAX_LZW_EXP || code_exp < color_exp + 1)
+    return nullptr;
+
+  // Private ctor.
+  return pdfium::WrapUnique(new LZWDecompressor(color_exp, code_exp));
+}
+
+LZWDecompressor::LZWDecompressor(uint8_t color_exp, uint8_t code_exp)
+    : code_size_(code_exp),
+      code_color_end_(static_cast<uint16_t>(1 << (color_exp + 1))),
+      code_clear_(static_cast<uint16_t>(1 << code_exp)),
+      code_end_(static_cast<uint16_t>((1 << code_exp) + 1)) {
+  ClearTable();
+}
+
+LZWDecompressor::~LZWDecompressor() = default;
+
+void LZWDecompressor::SetSource(const uint8_t* src_buf, uint32_t src_size) {
+  next_in_ = src_buf;
+  avail_in_ = src_size;
+}
+
+LZWDecompressor::Status LZWDecompressor::Decode(uint8_t* dest_buf,
+                                                uint32_t* dest_size) {
+  if (!next_in_ || !dest_buf || !dest_size)
+    return Status::kError;
+
+  if (avail_in_ == 0)
+    return Status::kUnfinished;
+
+  if (*dest_size == 0)
+    return Status::kInsufficientDestSize;
+
+  uint32_t i = 0;
+  if (decompressed_next_ != 0) {
+    uint32_t extracted_size = ExtractData(dest_buf, *dest_size);
+    if (decompressed_next_ != 0)
+      return Status::kInsufficientDestSize;
+
+    dest_buf += extracted_size;
+    i += extracted_size;
+  }
+
+  while (i <= *dest_size && (avail_in_ > 0 || bits_left_ >= code_size_cur_)) {
+    if (code_size_cur_ > GIF_MAX_LZW_EXP)
+      return Status::kError;
+
+    if (avail_in_ > 0) {
+      if (bits_left_ > 31)
+        return Status::kError;
+
+      FX_SAFE_UINT32 safe_code = *next_in_++;
+      safe_code <<= bits_left_;
+      safe_code |= code_store_;
+      if (!safe_code.IsValid())
+        return Status::kError;
+
+      code_store_ = safe_code.ValueOrDie();
+      --avail_in_;
+      bits_left_ += 8;
+    }
+
+    while (bits_left_ >= code_size_cur_) {
+      uint16_t code =
+          static_cast<uint16_t>(code_store_) & ((1 << code_size_cur_) - 1);
+      code_store_ >>= code_size_cur_;
+      bits_left_ -= code_size_cur_;
+      if (code == code_clear_) {
+        ClearTable();
+        continue;
+      }
+      if (code == code_end_) {
+        *dest_size = i;
+        return Status::kSuccess;
+      }
+
+      if (code_old_ != static_cast<uint16_t>(-1)) {
+        if (code_next_ < GIF_MAX_LZW_CODE) {
+          if (code == code_next_) {
+            AddCode(code_old_, code_first_);
+            if (!DecodeString(code))
+              return Status::kError;
+          } else if (code > code_next_) {
+            return Status::kError;
+          } else {
+            if (!DecodeString(code))
+              return Status::kError;
+
+            uint8_t append_char = decompressed_[decompressed_next_ - 1];
+            AddCode(code_old_, append_char);
+          }
+        }
+      } else {
+        if (!DecodeString(code))
+          return Status::kError;
+      }
+
+      code_old_ = code;
+      uint32_t extracted_size = ExtractData(dest_buf, *dest_size - i);
+      if (decompressed_next_ != 0)
+        return Status::kInsufficientDestSize;
+
+      dest_buf += extracted_size;
+      i += extracted_size;
+    }
+  }
+
+  if (avail_in_ != 0)
+    return Status::kError;
+
+  *dest_size = i;
+  return Status::kUnfinished;
+}
+
+void LZWDecompressor::ClearTable() {
+  code_size_cur_ = code_size_ + 1;
+  code_next_ = code_end_ + 1;
+  code_old_ = static_cast<uint16_t>(-1);
+  memset(code_table_, 0, sizeof(code_table_));
+  for (uint16_t i = 0; i < code_clear_; i++)
+    code_table_[i].suffix = static_cast<uint8_t>(i);
+  decompressed_.resize(code_next_ - code_clear_ + 1);
+  decompressed_next_ = 0;
+}
+
+void LZWDecompressor::AddCode(uint16_t prefix_code, uint8_t append_char) {
+  if (code_next_ == GIF_MAX_LZW_CODE)
+    return;
+
+  code_table_[code_next_].prefix = prefix_code;
+  code_table_[code_next_].suffix = append_char;
+  if (++code_next_ < GIF_MAX_LZW_CODE) {
+    if (code_next_ >> code_size_cur_)
+      code_size_cur_++;
+  }
+}
+
+bool LZWDecompressor::DecodeString(uint16_t code) {
+  decompressed_.resize(code_next_ - code_clear_ + 1);
+  decompressed_next_ = 0;
+
+  while (code >= code_clear_ && code <= code_next_) {
+    if (code == code_table_[code].prefix ||
+        decompressed_next_ >= decompressed_.size())
+      return false;
+
+    decompressed_[decompressed_next_++] = code_table_[code].suffix;
+    code = code_table_[code].prefix;
+  }
+
+  if (code >= code_color_end_)
+    return false;
+
+  decompressed_[decompressed_next_++] = static_cast<uint8_t>(code);
+  code_first_ = static_cast<uint8_t>(code);
+  return true;
+}
+
+uint32_t LZWDecompressor::ExtractData(uint8_t* dest_buf, uint32_t dest_size) {
+  if (dest_size == 0)
+    return 0;
+
+  uint32_t copy_size = dest_size <= decompressed_next_
+                           ? dest_size
+                           : static_cast<uint32_t>(decompressed_next_);
+  std::reverse_copy(decompressed_.data() + decompressed_next_ - copy_size,
+                    decompressed_.data() + decompressed_next_, dest_buf);
+  decompressed_next_ -= copy_size;
+  return copy_size;
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/gif/lzw_decompressor.h b/core/fxcodec/gif/lzw_decompressor.h
new file mode 100644
index 0000000..c078fc8
--- /dev/null
+++ b/core/fxcodec/gif/lzw_decompressor.h
@@ -0,0 +1,79 @@
+// Copyright 2017 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_GIF_LZW_DECOMPRESSOR_H_
+#define CORE_FXCODEC_GIF_LZW_DECOMPRESSOR_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "core/fxcodec/gif/cfx_gif.h"
+#include "core/fxcrt/data_vector.h"
+
+namespace fxcodec {
+
+class LZWDecompressor {
+ public:
+  enum class Status {
+    kError,
+    kSuccess,
+    kUnfinished,
+    kInsufficientDestSize,
+  };
+
+  struct CodeEntry {
+    uint16_t prefix;
+    uint8_t suffix;
+  };
+
+  // Returns nullptr on error
+  static std::unique_ptr<LZWDecompressor> Create(uint8_t color_exp,
+                                                 uint8_t code_exp);
+  ~LZWDecompressor();
+
+  void SetSource(const uint8_t* src_buf, uint32_t src_size);
+  Status Decode(uint8_t* dest_buf, uint32_t* dest_size);
+
+  // Used by unittests, should not be called in production code.
+  uint32_t ExtractDataForTest(uint8_t* dest_buf, uint32_t dest_size) {
+    return ExtractData(dest_buf, dest_size);
+  }
+
+  DataVector<uint8_t>* DecompressedForTest() { return &decompressed_; }
+  size_t* DecompressedNextForTest() { return &decompressed_next_; }
+
+ private:
+  // Use Create() instead.
+  LZWDecompressor(uint8_t color_exp, uint8_t code_exp);
+
+  void ClearTable();
+  void AddCode(uint16_t prefix_code, uint8_t append_char);
+  bool DecodeString(uint16_t code);
+  uint32_t ExtractData(uint8_t* dest_buf, uint32_t dest_size);
+
+  const uint8_t code_size_;
+  uint8_t code_size_cur_ = 0;
+  const uint16_t code_color_end_;
+  const uint16_t code_clear_;
+  const uint16_t code_end_;
+  uint16_t code_next_ = 0;
+  uint8_t code_first_ = 0;
+  DataVector<uint8_t> decompressed_;
+  size_t decompressed_next_ = 0;
+  uint16_t code_old_ = 0;
+  const uint8_t* next_in_ = nullptr;
+  uint32_t avail_in_ = 0;
+  uint8_t bits_left_ = 0;
+  uint32_t code_store_ = 0;
+  CodeEntry code_table_[GIF_MAX_LZW_CODE];
+};
+
+}  // namespace fxcodec
+
+using LZWDecompressor = fxcodec::LZWDecompressor;
+
+#endif  // CORE_FXCODEC_GIF_LZW_DECOMPRESSOR_H_
diff --git a/core/fxcodec/gif/lzw_decompressor_unittest.cpp b/core/fxcodec/gif/lzw_decompressor_unittest.cpp
new file mode 100644
index 0000000..191a0e4
--- /dev/null
+++ b/core/fxcodec/gif/lzw_decompressor_unittest.cpp
@@ -0,0 +1,251 @@
+// Copyright 2017 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcodec/gif/lzw_decompressor.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include <iterator>
+
+#include "core/fxcrt/data_vector.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::ElementsAreArray;
+
+TEST(LZWDecompressor, CreateBadParams) {
+  EXPECT_FALSE(LZWDecompressor::Create(0x10, 0x02));
+  EXPECT_FALSE(LZWDecompressor::Create(0x04, 0x0F));
+  EXPECT_FALSE(LZWDecompressor::Create(0x02, 0x02));
+}
+
+TEST(LZWDecompressor, ExtractData) {
+  uint8_t palette_exp = 0x1;
+  uint8_t code_exp = 0x2;
+  auto decompressor = LZWDecompressor::Create(palette_exp, code_exp);
+  ASSERT_NE(nullptr, decompressor);
+
+  // Check that 0 length extract does nothing
+  {
+    DataVector<uint8_t>* decompressed = decompressor->DecompressedForTest();
+    *decompressed = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    *(decompressor->DecompressedNextForTest()) = decompressed->size();
+    uint8_t dest_buf[20];
+    memset(dest_buf, static_cast<uint8_t>(-1), sizeof(dest_buf));
+
+    EXPECT_EQ(0u, decompressor->ExtractDataForTest(dest_buf, 0));
+    for (size_t i = 0; i < std::size(dest_buf); ++i)
+      EXPECT_EQ(static_cast<uint8_t>(-1), dest_buf[i]);
+
+    EXPECT_EQ(10u, *(decompressor->DecompressedNextForTest()));
+    for (size_t i = 0; i < *(decompressor->DecompressedNextForTest()); ++i)
+      EXPECT_EQ(i, (*decompressed)[i]);
+  }
+
+  // Check that less than decompressed size only gets the expected number
+  {
+    DataVector<uint8_t>* decompressed = decompressor->DecompressedForTest();
+    *decompressed = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    *(decompressor->DecompressedNextForTest()) = decompressed->size();
+    uint8_t dest_buf[20];
+    memset(dest_buf, static_cast<uint8_t>(-1), sizeof(dest_buf));
+
+    EXPECT_EQ(5u, decompressor->ExtractDataForTest(dest_buf, 5));
+    size_t i = 0;
+    for (; i < 5; ++i)
+      EXPECT_EQ(9 - i, dest_buf[i]);
+    for (; i < std::size(dest_buf); ++i)
+      EXPECT_EQ(static_cast<uint8_t>(-1), dest_buf[i]);
+
+    EXPECT_EQ(5u, *(decompressor->DecompressedNextForTest()));
+    for (i = 0; i < *(decompressor->DecompressedNextForTest()); ++i)
+      EXPECT_EQ(i, (*decompressed)[i]);
+  }
+
+  // Check that greater than decompressed size depletes the decompressor
+  {
+    DataVector<uint8_t>* decompressed = decompressor->DecompressedForTest();
+    *decompressed = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    *(decompressor->DecompressedNextForTest()) = decompressed->size();
+    uint8_t dest_buf[20];
+    memset(dest_buf, static_cast<uint8_t>(-1), sizeof(dest_buf));
+
+    EXPECT_EQ(10u,
+              decompressor->ExtractDataForTest(dest_buf, std::size(dest_buf)));
+    size_t i = 0;
+    for (; i < 10; ++i)
+      EXPECT_EQ(9 - i, dest_buf[i]);
+    for (; i < std::size(dest_buf); ++i)
+      EXPECT_EQ(static_cast<uint8_t>(-1), dest_buf[i]);
+
+    EXPECT_EQ(0u, *(decompressor->DecompressedNextForTest()));
+  }
+}
+
+TEST(LZWDecompressor, DecodeBadParams) {
+  uint8_t palette_exp = 0x0;
+  uint8_t code_exp = 0x2;
+  auto decompressor = LZWDecompressor::Create(palette_exp, code_exp);
+  ASSERT_NE(nullptr, decompressor);
+
+  uint8_t image_data[10];
+  uint32_t image_size = std::size(image_data);
+
+  uint8_t output_data[10];
+  uint32_t output_size = std::size(output_data);
+
+  decompressor->SetSource(nullptr, image_size);
+  EXPECT_EQ(LZWDecompressor::Status::kError,
+            decompressor->Decode(output_data, &output_size));
+
+  decompressor->SetSource(image_data, 0);
+  EXPECT_EQ(LZWDecompressor::Status::kUnfinished,
+            decompressor->Decode(output_data, &output_size));
+
+  decompressor->SetSource(image_data, image_size);
+  EXPECT_EQ(LZWDecompressor::Status::kError,
+            decompressor->Decode(nullptr, &output_size));
+  EXPECT_EQ(LZWDecompressor::Status::kError,
+            decompressor->Decode(output_data, nullptr));
+
+  output_size = 0;
+  EXPECT_EQ(LZWDecompressor::Status::kInsufficientDestSize,
+            decompressor->Decode(output_data, &output_size));
+}
+
+TEST(LZWDecompressor, Decode1x1SingleColour) {
+  uint8_t palette_exp = 0x0;
+  uint8_t code_exp = 0x2;
+  auto decompressor = LZWDecompressor::Create(palette_exp, code_exp);
+  ASSERT_NE(nullptr, decompressor);
+
+  uint8_t image_data[] = {0x44, 0x01};
+  uint32_t image_size = std::size(image_data);
+
+  uint8_t expected_data[] = {0x00};
+  uint8_t output_data[std::size(expected_data)];
+  memset(output_data, 0, sizeof(output_data));
+  uint32_t output_size = std::size(output_data);
+
+  decompressor->SetSource(image_data, image_size);
+  EXPECT_EQ(LZWDecompressor::Status::kSuccess,
+            decompressor->Decode(output_data, &output_size));
+
+  EXPECT_EQ(std::size(output_data), output_size);
+  EXPECT_TRUE(0 == memcmp(expected_data, output_data, sizeof(expected_data)));
+}
+
+TEST(LZWDecompressor, Decode10x10SingleColour) {
+  uint8_t palette_exp = 0x0;
+  uint8_t code_exp = 0x2;
+  auto decompressor = LZWDecompressor::Create(palette_exp, code_exp);
+  ASSERT_NE(nullptr, decompressor);
+
+  static constexpr uint8_t kImageData[] = {0x84, 0x8F, 0xA9, 0xCB,
+                                           0xED, 0x0F, 0x63, 0x2B};
+  uint32_t image_size = std::size(kImageData);
+
+  static constexpr uint8_t kExpectedData[] = {
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00};
+  uint8_t output_data[std::size(kExpectedData)];
+  memset(output_data, 0, sizeof(output_data));
+  uint32_t output_size = std::size(output_data);
+
+  decompressor->SetSource(kImageData, image_size);
+  EXPECT_EQ(LZWDecompressor::Status::kSuccess,
+            decompressor->Decode(output_data, &output_size));
+
+  EXPECT_EQ(std::size(output_data), output_size);
+  EXPECT_TRUE(0 == memcmp(kExpectedData, output_data, sizeof(kExpectedData)));
+}
+
+TEST(LZWDecompressor, Decode10x10MultipleColour) {
+  uint8_t palette_exp = 0x1;
+  uint8_t code_exp = 0x2;
+  auto decompressor = LZWDecompressor::Create(palette_exp, code_exp);
+  ASSERT_NE(nullptr, decompressor);
+
+  static constexpr uint8_t kImageData[] = {
+      0x8C, 0x2D, 0x99, 0x87, 0x2A, 0x1C, 0xDC, 0x33, 0xA0, 0x02, 0x75,
+      0xEC, 0x95, 0xFA, 0xA8, 0xDE, 0x60, 0x8C, 0x04, 0x91, 0x4C, 0x01};
+  uint32_t image_size = std::size(kImageData);
+
+  static constexpr uint8_t kExpectedData[] = {
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+      0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+      0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02,
+      0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
+      0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01,
+      0x01, 0x01, 0x01, 0x01};
+
+  uint8_t output_data[std::size(kExpectedData)];
+  memset(output_data, 0, sizeof(output_data));
+  uint32_t output_size = std::size(output_data);
+
+  decompressor->SetSource(kImageData, image_size);
+  EXPECT_EQ(LZWDecompressor::Status::kSuccess,
+            decompressor->Decode(output_data, &output_size));
+
+  EXPECT_EQ(std::size(output_data), output_size);
+  EXPECT_TRUE(0 == memcmp(kExpectedData, output_data, sizeof(kExpectedData)));
+}
+
+TEST(LZWDecompressor, MultipleDecodes) {
+  auto decompressor = LZWDecompressor::Create(/*color_exp=*/0, /*code_exp=*/2);
+  ASSERT_NE(nullptr, decompressor);
+
+  static constexpr uint8_t kImageData[] = {0x84, 0x6f, 0x05};
+  decompressor->SetSource(kImageData, std::size(kImageData));
+
+  static constexpr uint8_t kExpectedScanline[] = {0x00, 0x00, 0x00, 0x00};
+  uint8_t output_data[std::size(kExpectedScanline)];
+
+  memset(output_data, 0xFF, sizeof(output_data));
+  uint32_t output_size = std::size(output_data);
+  EXPECT_EQ(LZWDecompressor::Status::kInsufficientDestSize,
+            decompressor->Decode(output_data, &output_size));
+  EXPECT_EQ(std::size(kExpectedScanline), output_size);
+  EXPECT_THAT(output_data, ElementsAreArray(kExpectedScanline));
+
+  memset(output_data, 0xFF, sizeof(output_data));
+  output_size = std::size(output_data);
+  EXPECT_EQ(LZWDecompressor::Status::kSuccess,
+            decompressor->Decode(output_data, &output_size));
+  EXPECT_EQ(std::size(kExpectedScanline), output_size);
+  EXPECT_THAT(output_data, ElementsAreArray(kExpectedScanline));
+}
+
+TEST(LZWDecompressor, HandleColourCodeOutOfPalette) {
+  uint8_t palette_exp = 0x2;  // Image uses 10 colours, so the palette exp
+                              // should be 3, 2^(3+1) = 16 colours.
+  uint8_t code_exp = 0x4;
+  auto decompressor = LZWDecompressor::Create(palette_exp, code_exp);
+  ASSERT_NE(nullptr, decompressor);
+
+  static constexpr uint8_t kImageData[] = {
+      0x30, 0xC9, 0x49, 0x81, 0xBD, 0x78, 0xE8, 0xCD, 0x89, 0xFF,
+      0x60, 0x20, 0x8E, 0xE4, 0x61, 0x9E, 0xA8, 0xA1, 0xAE, 0x2C,
+      0xE2, 0xBE, 0xB0, 0x20, 0xCF, 0x74, 0x61, 0xDF, 0x78, 0x04};
+  uint32_t image_size = std::size(kImageData);
+
+  uint8_t output_data[100];  // The uncompressed data is for a 10x10 image
+  memset(output_data, 0, sizeof(output_data));
+  uint32_t output_size = std::size(output_data);
+
+  decompressor->SetSource(kImageData, image_size);
+  EXPECT_EQ(LZWDecompressor::Status::kError,
+            decompressor->Decode(output_data, &output_size));
+}
diff --git a/core/fxcodec/icc/icc_transform.cpp b/core/fxcodec/icc/icc_transform.cpp
new file mode 100644
index 0000000..485988d
--- /dev/null
+++ b/core/fxcodec/icc/icc_transform.cpp
@@ -0,0 +1,145 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/icc/icc_transform.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "core/fxcrt/data_vector.h"
+#include "third_party/base/memory/ptr_util.h"
+#include "third_party/base/notreached.h"
+#include "third_party/base/numerics/safe_conversions.h"
+
+namespace fxcodec {
+
+namespace {
+
+// For use with std::unique_ptr<cmsHPROFILE>.
+struct CmsProfileDeleter {
+  inline void operator()(cmsHPROFILE p) { cmsCloseProfile(p); }
+};
+
+using ScopedCmsProfile = std::unique_ptr<void, CmsProfileDeleter>;
+
+bool Check3Components(cmsColorSpaceSignature cs) {
+  switch (cs) {
+    case cmsSigGrayData:
+    case cmsSigCmykData:
+      return false;
+    default:
+      return true;
+  }
+}
+
+}  // namespace
+
+IccTransform::IccTransform(cmsHTRANSFORM hTransform,
+                           int srcComponents,
+                           bool bIsLab,
+                           bool bNormal)
+    : m_hTransform(hTransform),
+      m_nSrcComponents(srcComponents),
+      m_bLab(bIsLab),
+      m_bNormal(bNormal) {}
+
+IccTransform::~IccTransform() {
+  cmsDeleteTransform(m_hTransform);
+}
+
+// static
+std::unique_ptr<IccTransform> IccTransform::CreateTransformSRGB(
+    pdfium::span<const uint8_t> span) {
+  ScopedCmsProfile srcProfile(cmsOpenProfileFromMem(
+      span.data(), pdfium::base::checked_cast<cmsUInt32Number>(span.size())));
+  if (!srcProfile)
+    return nullptr;
+
+  ScopedCmsProfile dstProfile(cmsCreate_sRGBProfile());
+  if (!dstProfile)
+    return nullptr;
+
+  cmsColorSpaceSignature srcCS = cmsGetColorSpace(srcProfile.get());
+  uint32_t nSrcComponents = cmsChannelsOf(srcCS);
+
+  // According to PDF spec, number of components must be 1, 3, or 4.
+  if (nSrcComponents != 1 && nSrcComponents != 3 && nSrcComponents != 4)
+    return nullptr;
+
+  int srcFormat;
+  bool bLab = false;
+  bool bNormal = false;
+  if (srcCS == cmsSigLabData) {
+    srcFormat =
+        COLORSPACE_SH(PT_Lab) | CHANNELS_SH(nSrcComponents) | BYTES_SH(0);
+    bLab = true;
+  } else {
+    srcFormat =
+        COLORSPACE_SH(PT_ANY) | CHANNELS_SH(nSrcComponents) | BYTES_SH(1);
+    // TODO(thestig): Check to see if lcms2 supports more colorspaces that can
+    // be considered normal.
+    bNormal = srcCS == cmsSigGrayData || srcCS == cmsSigRgbData ||
+              srcCS == cmsSigCmykData;
+  }
+  cmsColorSpaceSignature dstCS = cmsGetColorSpace(dstProfile.get());
+  if (!Check3Components(dstCS))
+    return nullptr;
+
+  cmsHTRANSFORM hTransform = nullptr;
+  switch (dstCS) {
+    case cmsSigRgbData:
+      hTransform =
+          cmsCreateTransform(srcProfile.get(), srcFormat, dstProfile.get(),
+                             TYPE_BGR_8, INTENT_PERCEPTUAL, /*dwFlags=*/0);
+      break;
+    case cmsSigGrayData:
+    case cmsSigCmykData:
+      // Check3Components() already filtered these types.
+      NOTREACHED_NORETURN();
+    default:
+      break;
+  }
+  if (!hTransform)
+    return nullptr;
+
+  // Private ctor.
+  return pdfium::WrapUnique(
+      new IccTransform(hTransform, nSrcComponents, bLab, bNormal));
+}
+
+void IccTransform::Translate(pdfium::span<const float> pSrcValues,
+                             pdfium::span<float> pDestValues) {
+  uint8_t output[4];
+  // TODO(npm): Currently the CmsDoTransform method is part of LCMS and it will
+  // apply some member of m_hTransform to the input. We need to go over all the
+  // places which set transform to verify that only `pSrcValues.size()`
+  // components are used.
+  if (m_bLab) {
+    DataVector<double> inputs(std::max<size_t>(pSrcValues.size(), 16));
+    for (uint32_t i = 0; i < pSrcValues.size(); ++i)
+      inputs[i] = pSrcValues[i];
+    cmsDoTransform(m_hTransform, inputs.data(), output, 1);
+  } else {
+    DataVector<uint8_t> inputs(std::max<size_t>(pSrcValues.size(), 16));
+    for (size_t i = 0; i < pSrcValues.size(); ++i) {
+      inputs[i] = std::clamp(static_cast<int>(pSrcValues[i] * 255.0f), 0, 255);
+    }
+    cmsDoTransform(m_hTransform, inputs.data(), output, 1);
+  }
+  pDestValues[0] = output[2] / 255.0f;
+  pDestValues[1] = output[1] / 255.0f;
+  pDestValues[2] = output[0] / 255.0f;
+}
+
+void IccTransform::TranslateScanline(pdfium::span<uint8_t> pDest,
+                                     pdfium::span<const uint8_t> pSrc,
+                                     int32_t pixels) {
+  cmsDoTransform(m_hTransform, pSrc.data(), pDest.data(), pixels);
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/icc/icc_transform.h b/core/fxcodec/icc/icc_transform.h
new file mode 100644
index 0000000..47cda07
--- /dev/null
+++ b/core/fxcodec/icc/icc_transform.h
@@ -0,0 +1,55 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_ICC_ICC_TRANSFORM_H_
+#define CORE_FXCODEC_ICC_ICC_TRANSFORM_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "core/fxcodec/fx_codec_def.h"
+#include "third_party/base/containers/span.h"
+
+#if defined(USE_SYSTEM_LCMS2)
+#include <lcms2.h>
+#else
+#include "third_party/lcms/include/lcms2.h"
+#endif
+
+namespace fxcodec {
+
+class IccTransform {
+ public:
+  static std::unique_ptr<IccTransform> CreateTransformSRGB(
+      pdfium::span<const uint8_t> span);
+
+  ~IccTransform();
+
+  void Translate(pdfium::span<const float> pSrcValues,
+                 pdfium::span<float> pDestValues);
+  void TranslateScanline(pdfium::span<uint8_t> pDest,
+                         pdfium::span<const uint8_t> pSrc,
+                         int pixels);
+
+  int components() const { return m_nSrcComponents; }
+  bool IsNormal() const { return m_bNormal; }
+
+ private:
+  IccTransform(cmsHTRANSFORM transform,
+               int srcComponents,
+               bool bIsLab,
+               bool bNormal);
+
+  const cmsHTRANSFORM m_hTransform;
+  const int m_nSrcComponents;
+  const bool m_bLab;
+  const bool m_bNormal;
+};
+
+}  // namespace fxcodec
+
+#endif  // CORE_FXCODEC_ICC_ICC_TRANSFORM_H_
diff --git a/core/fxcodec/icc/iccmodule.cpp b/core/fxcodec/icc/iccmodule.cpp
deleted file mode 100644
index e9a8cd6..0000000
--- a/core/fxcodec/icc/iccmodule.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/icc/iccmodule.h"
-
-#include <algorithm>
-#include <memory>
-#include <vector>
-
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-namespace fxcodec {
-
-namespace {
-
-// For use with std::unique_ptr<cmsHPROFILE>.
-struct CmsProfileDeleter {
-  inline void operator()(cmsHPROFILE p) { cmsCloseProfile(p); }
-};
-
-using ScopedCmsProfile = std::unique_ptr<void, CmsProfileDeleter>;
-
-bool Check3Components(cmsColorSpaceSignature cs) {
-  switch (cs) {
-    case cmsSigGrayData:
-    case cmsSigCmykData:
-      return false;
-    default:
-      return true;
-  }
-}
-
-}  // namespace
-
-CLcmsCmm::CLcmsCmm(cmsHTRANSFORM hTransform,
-                   int srcComponents,
-                   bool bIsLab,
-                   bool bNormal)
-    : m_hTransform(hTransform),
-      m_nSrcComponents(srcComponents),
-      m_bLab(bIsLab),
-      m_bNormal(bNormal) {}
-
-CLcmsCmm::~CLcmsCmm() {
-  cmsDeleteTransform(m_hTransform);
-}
-
-// static
-std::unique_ptr<CLcmsCmm> IccModule::CreateTransformSRGB(
-    pdfium::span<const uint8_t> span) {
-  ScopedCmsProfile srcProfile(cmsOpenProfileFromMem(span.data(), span.size()));
-  if (!srcProfile)
-    return nullptr;
-
-  ScopedCmsProfile dstProfile(cmsCreate_sRGBProfile());
-  if (!dstProfile)
-    return nullptr;
-
-  cmsColorSpaceSignature srcCS = cmsGetColorSpace(srcProfile.get());
-
-  uint32_t nSrcComponents = cmsChannelsOf(srcCS);
-  // According to PDF spec, number of components must be 1, 3, or 4.
-  if (nSrcComponents != 1 && nSrcComponents != 3 && nSrcComponents != 4)
-    return nullptr;
-
-  int srcFormat;
-  bool bLab = false;
-  bool bNormal = false;
-  if (srcCS == cmsSigLabData) {
-    srcFormat =
-        COLORSPACE_SH(PT_Lab) | CHANNELS_SH(nSrcComponents) | BYTES_SH(0);
-    bLab = true;
-  } else {
-    srcFormat =
-        COLORSPACE_SH(PT_ANY) | CHANNELS_SH(nSrcComponents) | BYTES_SH(1);
-    // TODO(thestig): Check to see if lcms2 supports more colorspaces that can
-    // be considered normal.
-    bNormal = srcCS == cmsSigGrayData || srcCS == cmsSigRgbData ||
-              srcCS == cmsSigCmykData;
-  }
-  cmsColorSpaceSignature dstCS = cmsGetColorSpace(dstProfile.get());
-  if (!Check3Components(dstCS))
-    return nullptr;
-
-  cmsHTRANSFORM hTransform = nullptr;
-  const int intent = 0;
-  switch (dstCS) {
-    case cmsSigRgbData:
-      hTransform = cmsCreateTransform(srcProfile.get(), srcFormat,
-                                      dstProfile.get(), TYPE_BGR_8, intent, 0);
-      break;
-    case cmsSigGrayData:
-    case cmsSigCmykData:
-      // Check3Components() already filtered these types.
-      NOTREACHED();
-      break;
-    default:
-      break;
-  }
-  if (!hTransform)
-    return nullptr;
-
-  return pdfium::MakeUnique<CLcmsCmm>(hTransform, nSrcComponents, bLab,
-                                      bNormal);
-}
-
-// static
-void IccModule::Translate(CLcmsCmm* pTransform,
-                          uint32_t nSrcComponents,
-                          const float* pSrcValues,
-                          float* pDestValues) {
-  if (!pTransform)
-    return;
-
-  uint8_t output[4];
-  // TODO(npm): Currently the CmsDoTransform method is part of LCMS and it will
-  // apply some member of m_hTransform to the input. We need to go over all the
-  // places which set transform to verify that only |nSrcComponents| are used.
-  if (pTransform->IsLab()) {
-    std::vector<double> inputs(std::max(nSrcComponents, 16u));
-    for (uint32_t i = 0; i < nSrcComponents; ++i)
-      inputs[i] = pSrcValues[i];
-    cmsDoTransform(pTransform->transform(), inputs.data(), output, 1);
-  } else {
-    std::vector<uint8_t> inputs(std::max(nSrcComponents, 16u));
-    for (uint32_t i = 0; i < nSrcComponents; ++i) {
-      inputs[i] =
-          pdfium::clamp(static_cast<int>(pSrcValues[i] * 255.0f), 0, 255);
-    }
-    cmsDoTransform(pTransform->transform(), inputs.data(), output, 1);
-  }
-  pDestValues[0] = output[2] / 255.0f;
-  pDestValues[1] = output[1] / 255.0f;
-  pDestValues[2] = output[0] / 255.0f;
-}
-
-// static
-void IccModule::TranslateScanline(CLcmsCmm* pTransform,
-                                  unsigned char* pDest,
-                                  const unsigned char* pSrc,
-                                  int32_t pixels) {
-  if (pTransform)
-    cmsDoTransform(pTransform->transform(), pSrc, pDest, pixels);
-}
-
-}  // namespace fxcodec
diff --git a/core/fxcodec/icc/iccmodule.h b/core/fxcodec/icc/iccmodule.h
deleted file mode 100644
index a031172..0000000
--- a/core/fxcodec/icc/iccmodule.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_ICC_ICCMODULE_H_
-#define CORE_FXCODEC_ICC_ICCMODULE_H_
-
-#include <memory>
-
-#include "core/fxcodec/fx_codec_def.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/span.h"
-
-#if defined(USE_SYSTEM_LCMS2)
-#include <lcms2.h>
-#else
-#include "third_party/lcms/include/lcms2.h"
-#endif
-
-namespace fxcodec {
-
-class CLcmsCmm {
- public:
-  CLcmsCmm(cmsHTRANSFORM transform,
-           int srcComponents,
-           bool bIsLab,
-           bool bNormal);
-  ~CLcmsCmm();
-
-  cmsHTRANSFORM transform() const { return m_hTransform; }
-  int components() const { return m_nSrcComponents; }
-  bool IsLab() const { return m_bLab; }
-  bool IsNormal() const { return m_bNormal; }
-
- private:
-  const cmsHTRANSFORM m_hTransform;
-  const int m_nSrcComponents;
-  const bool m_bLab;
-  const bool m_bNormal;
-};
-
-class IccModule {
- public:
-  static std::unique_ptr<CLcmsCmm> CreateTransformSRGB(
-      pdfium::span<const uint8_t> span);
-  static void Translate(CLcmsCmm* pTransform,
-                        uint32_t nSrcComponents,
-                        const float* pSrcValues,
-                        float* pDestValues);
-  static void TranslateScanline(CLcmsCmm* pTransform,
-                                uint8_t* pDest,
-                                const uint8_t* pSrc,
-                                int pixels);
-
-  IccModule() = delete;
-  IccModule(const IccModule&) = delete;
-  IccModule& operator=(const IccModule&) = delete;
-};
-
-}  // namespace fxcodec
-
-using CLcmsCmm = fxcodec::CLcmsCmm;
-using IccModule = fxcodec::IccModule;
-
-#endif  // CORE_FXCODEC_ICC_ICCMODULE_H_
diff --git a/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp b/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp
index 8d3fc5a..625ebf2 100644
--- a/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp
+++ b/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,10 @@
 
 #include "core/fxcodec/jbig2/JBig2_ArithDecoder.h"
 
+#include <iterator>
+
 #include "core/fxcodec/jbig2/JBig2_BitStream.h"
-#include "core/fxcrt/fx_memory.h"
+#include "third_party/base/check_op.h"
 
 namespace {
 
@@ -41,13 +43,13 @@
   if (qe.bSwitch)
     m_MPS = !m_MPS;
   m_I = qe.NLPS;
-  ASSERT(m_I < FX_ArraySize(kQeTable));
+  DCHECK_LT(m_I, std::size(kQeTable));
   return D;
 }
 
 int JBig2ArithCtx::DecodeNMPS(const JBig2ArithQe& qe) {
   m_I = qe.NMPS;
-  ASSERT(m_I < FX_ArraySize(kQeTable));
+  DCHECK_LT(m_I, std::size(kQeTable));
   return MPS();
 }
 
@@ -61,11 +63,10 @@
   m_A = kDefaultAValue;
 }
 
-CJBig2_ArithDecoder::~CJBig2_ArithDecoder() {}
+CJBig2_ArithDecoder::~CJBig2_ArithDecoder() = default;
 
 int CJBig2_ArithDecoder::Decode(JBig2ArithCtx* pCX) {
-  ASSERT(pCX);
-  ASSERT(pCX->I() < FX_ArraySize(kQeTable));
+  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_ArithDecoder.h b/core/fxcodec/jbig2/JBig2_ArithDecoder.h
index 9731ac6..004d5fc 100644
--- a/core/fxcodec/jbig2/JBig2_ArithDecoder.h
+++ b/core/fxcodec/jbig2/JBig2_ArithDecoder.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -32,7 +32,7 @@
   unsigned int I() const { return m_I; }
 
  private:
-  bool m_MPS = 0;
+  bool m_MPS = false;
   unsigned int m_I = 0;
 };
 
diff --git a/core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp b/core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp
index 0d09b31..508810a 100644
--- a/core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp
+++ b/core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,7 @@
 
 #include <vector>
 
-#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_safe_types.h"
 
 namespace {
 
@@ -16,10 +16,12 @@
   return (val << 1) | bitwise_or_val;
 }
 
-const struct ArithIntDecodeData {
+struct ArithIntDecodeData {
   int nNeedBits;
   int nValue;
-} g_ArithIntDecodeData[] = {
+};
+
+constexpr ArithIntDecodeData kArithIntDecodeData[] = {
     {2, 0}, {4, 4}, {6, 20}, {8, 84}, {12, 340}, {32, 4436},
 };
 
@@ -27,7 +29,7 @@
                        std::vector<JBig2ArithCtx>* context,
                        int* prev,
                        size_t depth) {
-  static const size_t kDepthEnd = FX_ArraySize(g_ArithIntDecodeData) - 1;
+  static const size_t kDepthEnd = std::size(kArithIntDecodeData) - 1;
   if (depth == kDepthEnd)
     return kDepthEnd;
 
@@ -45,7 +47,7 @@
   m_IAx.resize(512);
 }
 
-CJBig2_ArithIntDecoder::~CJBig2_ArithIntDecoder() {}
+CJBig2_ArithIntDecoder::~CJBig2_ArithIntDecoder() = default;
 
 bool CJBig2_ArithIntDecoder::Decode(CJBig2_ArithDecoder* pArithDecoder,
                                     int* nResult) {
@@ -60,15 +62,14 @@
       RecursiveDecode(pArithDecoder, &m_IAx, &PREV, 0);
 
   int nTemp = 0;
-  for (int i = 0; i < g_ArithIntDecodeData[nDecodeDataIndex].nNeedBits; ++i) {
+  for (int i = 0; i < kArithIntDecodeData[nDecodeDataIndex].nNeedBits; ++i) {
     int D = pArithDecoder->Decode(&m_IAx[PREV]);
     PREV = ShiftOr(PREV, D);
     if (PREV >= 256)
       PREV = (PREV & 511) | 256;
     nTemp = ShiftOr(nTemp, D);
   }
-  pdfium::base::CheckedNumeric<int> safeValue =
-      g_ArithIntDecodeData[nDecodeDataIndex].nValue;
+  FX_SAFE_INT32 safeValue = kArithIntDecodeData[nDecodeDataIndex].nValue;
   safeValue += nTemp;
 
   // Value does not fit in int.
@@ -90,7 +91,7 @@
   m_IAID.resize(static_cast<size_t>(1) << SBSYMCODELEN);
 }
 
-CJBig2_ArithIaidDecoder::~CJBig2_ArithIaidDecoder() {}
+CJBig2_ArithIaidDecoder::~CJBig2_ArithIaidDecoder() = default;
 
 void CJBig2_ArithIaidDecoder::Decode(CJBig2_ArithDecoder* pArithDecoder,
                                      uint32_t* nResult) {
diff --git a/core/fxcodec/jbig2/JBig2_ArithIntDecoder.h b/core/fxcodec/jbig2/JBig2_ArithIntDecoder.h
index 2221767..7245ffc 100644
--- a/core/fxcodec/jbig2/JBig2_ArithIntDecoder.h
+++ b/core/fxcodec/jbig2/JBig2_ArithIntDecoder.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,11 @@
 #ifndef CORE_FXCODEC_JBIG2_JBIG2_ARITHINTDECODER_H_
 #define CORE_FXCODEC_JBIG2_JBIG2_ARITHINTDECODER_H_
 
+#include <stdint.h>
+
 #include <vector>
 
 #include "core/fxcodec/jbig2/JBig2_ArithDecoder.h"
-#include "core/fxcrt/fx_system.h"
 
 class CJBig2_ArithIntDecoder {
  public:
diff --git a/core/fxcodec/jbig2/JBig2_BitStream.cpp b/core/fxcodec/jbig2/JBig2_BitStream.cpp
index fb2a888..6802c1b 100644
--- a/core/fxcodec/jbig2/JBig2_BitStream.cpp
+++ b/core/fxcodec/jbig2/JBig2_BitStream.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,6 +8,8 @@
 
 #include <algorithm>
 
+#include "third_party/base/numerics/safe_conversions.h"
+
 namespace {
 
 pdfium::span<const uint8_t> ValidatedSpan(pdfium::span<const uint8_t> sp) {
@@ -19,8 +21,8 @@
 }  // namespace
 
 CJBig2_BitStream::CJBig2_BitStream(pdfium::span<const uint8_t> pSrcStream,
-                                   uint32_t dwObjNum)
-    : m_Span(ValidatedSpan(pSrcStream)), m_dwObjNum(dwObjNum) {}
+                                   uint64_t key)
+    : m_Span(ValidatedSpan(pSrcStream)), m_Key(key) {}
 
 CJBig2_BitStream::~CJBig2_BitStream() = default;
 
@@ -143,7 +145,7 @@
 }
 
 void CJBig2_BitStream::setOffset(uint32_t dwOffset) {
-  m_dwByteIdx = std::min<size_t>(dwOffset, m_Span.size());
+  m_dwByteIdx = std::min(dwOffset, getLength());
 }
 
 uint32_t CJBig2_BitStream::getBitPos() const {
@@ -159,8 +161,12 @@
   return m_Span.data();
 }
 
+uint32_t CJBig2_BitStream::getLength() const {
+  return pdfium::base::checked_cast<uint32_t>(m_Span.size());
+}
+
 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) {
@@ -168,7 +174,7 @@
 }
 
 uint32_t CJBig2_BitStream::getByteLeft() const {
-  return m_Span.size() - m_dwByteIdx;
+  return getLength() - m_dwByteIdx;
 }
 
 void CJBig2_BitStream::AdvanceBit() {
@@ -181,13 +187,9 @@
 }
 
 bool CJBig2_BitStream::IsInBounds() const {
-  return m_dwByteIdx < m_Span.size();
+  return m_dwByteIdx < getLength();
 }
 
 uint32_t CJBig2_BitStream::LengthInBits() const {
-  return m_Span.size() << 3;
-}
-
-uint32_t CJBig2_BitStream::getObjNum() const {
-  return m_dwObjNum;
+  return getLength() << 3;
 }
diff --git a/core/fxcodec/jbig2/JBig2_BitStream.h b/core/fxcodec/jbig2/JBig2_BitStream.h
index 8032090..0a3a44d 100644
--- a/core/fxcodec/jbig2/JBig2_BitStream.h
+++ b/core/fxcodec/jbig2/JBig2_BitStream.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,11 +8,11 @@
 #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:
-  CJBig2_BitStream(pdfium::span<const uint8_t> pSrcStream, uint32_t dwObjNum);
+  CJBig2_BitStream(pdfium::span<const uint8_t> pSrcStream, uint64_t key);
   CJBig2_BitStream(const CJBig2_BitStream&) = delete;
   CJBig2_BitStream& operator=(const CJBig2_BitStream&) = delete;
   ~CJBig2_BitStream();
@@ -35,11 +35,11 @@
   uint32_t getBitPos() const;
   void setBitPos(uint32_t dwBitPos);
   const uint8_t* getBuf() const;
-  uint32_t getLength() const { return m_Span.size(); }
+  uint32_t getLength() const;
   const uint8_t* getPointer() const;
   void offset(uint32_t dwOffset);
   uint32_t getByteLeft() const;
-  uint32_t getObjNum() const;
+  uint64_t getKey() const { return m_Key; }
   bool IsInBounds() const;
 
  private:
@@ -49,7 +49,7 @@
   const pdfium::span<const uint8_t> m_Span;
   uint32_t m_dwByteIdx = 0;
   uint32_t m_dwBitIdx = 0;
-  const uint32_t m_dwObjNum;
+  const uint64_t m_Key;
 };
 
 #endif  // CORE_FXCODEC_JBIG2_JBIG2_BITSTREAM_H_
diff --git a/core/fxcodec/jbig2/JBig2_BitStream_unittest.cpp b/core/fxcodec/jbig2/JBig2_BitStream_unittest.cpp
index 8669489..8e207b0 100644
--- a/core/fxcodec/jbig2/JBig2_BitStream_unittest.cpp
+++ b/core/fxcodec/jbig2/JBig2_BitStream_unittest.cpp
@@ -1,14 +1,10 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxcodec/jbig2/JBig2_BitStream.h"
 
-#include <memory>
-#include <utility>
-
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
 TEST(JBig2_BitStream, ReadNBits) {
   const uint8_t kData[] = {0xb1};  // 10110001
diff --git a/core/fxcodec/jbig2/JBig2_Context.cpp b/core/fxcodec/jbig2/JBig2_Context.cpp
index d9c9ee8..0d9028e 100644
--- a/core/fxcodec/jbig2/JBig2_Context.cpp
+++ b/core/fxcodec/jbig2/JBig2_Context.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -25,7 +25,8 @@
 #include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/pauseindicator_iface.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/memory/ptr_util.h"
 
 namespace {
 
@@ -53,29 +54,29 @@
 // static
 std::unique_ptr<CJBig2_Context> CJBig2_Context::Create(
     pdfium::span<const uint8_t> pGlobalSpan,
-    uint32_t dwGlobalObjNum,
+    uint64_t global_key,
     pdfium::span<const uint8_t> pSrcSpan,
-    uint32_t dwSrcObjNum,
+    uint64_t src_key,
     std::list<CJBig2_CachePair>* pSymbolDictCache) {
   auto result = pdfium::WrapUnique(
-      new CJBig2_Context(pSrcSpan, dwSrcObjNum, pSymbolDictCache, false));
+      new CJBig2_Context(pSrcSpan, src_key, pSymbolDictCache, false));
   if (!pGlobalSpan.empty()) {
-    result->m_pGlobalContext = pdfium::WrapUnique(new CJBig2_Context(
-        pGlobalSpan, dwGlobalObjNum, pSymbolDictCache, true));
+    result->m_pGlobalContext = pdfium::WrapUnique(
+        new CJBig2_Context(pGlobalSpan, global_key, pSymbolDictCache, true));
   }
   return result;
 }
 
 CJBig2_Context::CJBig2_Context(pdfium::span<const uint8_t> pSrcSpan,
-                               uint32_t dwObjNum,
+                               uint64_t src_key,
                                std::list<CJBig2_CachePair>* pSymbolDictCache,
                                bool bIsGlobal)
-    : m_pStream(pdfium::MakeUnique<CJBig2_BitStream>(pSrcSpan, dwObjNum)),
+    : m_pStream(std::make_unique<CJBig2_BitStream>(pSrcSpan, src_key)),
       m_HuffmanTables(CJBig2_HuffmanTable::kNumHuffmanTables),
       m_bIsGlobal(bIsGlobal),
       m_pSymbolDictCache(pSymbolDictCache) {}
 
-CJBig2_Context::~CJBig2_Context() {}
+CJBig2_Context::~CJBig2_Context() = default;
 
 JBig2_Result CJBig2_Context::DecodeSequential(PauseIndicatorIface* pPause) {
   if (m_pStream->getByteLeft() <= 0)
@@ -84,17 +85,16 @@
   while (m_pStream->getByteLeft() >= JBIG2_MIN_SEGMENT_SIZE) {
     JBig2_Result nRet;
     if (!m_pSegment) {
-      m_pSegment = pdfium::MakeUnique<CJBig2_Segment>();
+      m_pSegment = std::make_unique<CJBig2_Segment>();
       nRet = ParseSegmentHeader(m_pSegment.get());
       if (nRet != JBig2_Result::kSuccess) {
         m_pSegment.reset();
         return nRet;
       }
-      m_dwOffset = m_pStream->getOffset();
+      m_nOffset = m_pStream->getOffset();
     }
     nRet = ParseSegmentData(m_pSegment.get(), pPause);
-    if (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE) {
-      m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
+    if (m_ProcessingStatus == FXCODEC_STATUS::kDecodeToBeContinued) {
       m_PauseStep = 2;
       return JBig2_Result::kSuccess;
     }
@@ -107,18 +107,19 @@
       return nRet;
     }
     if (m_pSegment->m_dwData_length != 0xffffffff) {
-      m_dwOffset += m_pSegment->m_dwData_length;
-      if (!m_dwOffset.IsValid())
+      FX_SAFE_UINT32 new_offset = m_nOffset;
+      new_offset += m_pSegment->m_dwData_length;
+      if (!new_offset.IsValid())
         return JBig2_Result::kFailure;
-
-      m_pStream->setOffset(m_dwOffset.ValueOrDie());
+      m_nOffset = new_offset.ValueOrDie();
+      m_pStream->setOffset(m_nOffset);
     } else {
       m_pStream->offset(4);
     }
     m_SegmentList.push_back(std::move(m_pSegment));
     if (m_pStream->getByteLeft() > 0 && m_pPage && pPause &&
         pPause->NeedToPauseNow()) {
-      m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
+      m_ProcessingStatus = FXCODEC_STATUS::kDecodeToBeContinued;
       m_PauseStep = 2;
       return JBig2_Result::kSuccess;
     }
@@ -126,7 +127,7 @@
   return JBig2_Result::kSuccess;
 }
 
-bool CJBig2_Context::GetFirstPage(uint8_t* pBuf,
+bool CJBig2_Context::GetFirstPage(pdfium::span<uint8_t> pBuf,
                                   int32_t width,
                                   int32_t height,
                                   int32_t stride,
@@ -134,42 +135,42 @@
   if (m_pGlobalContext) {
     JBig2_Result nRet = m_pGlobalContext->DecodeSequential(pPause);
     if (nRet != JBig2_Result::kSuccess) {
-      m_ProcessingStatus = FXCODEC_STATUS_ERROR;
+      m_ProcessingStatus = FXCODEC_STATUS::kError;
       return nRet == JBig2_Result::kSuccess;
     }
   }
   m_PauseStep = 0;
-  m_pPage = pdfium::MakeUnique<CJBig2_Image>(width, height, stride, pBuf);
+  m_pPage = std::make_unique<CJBig2_Image>(width, height, stride, pBuf);
   m_bBufSpecified = true;
   if (pPause && pPause->NeedToPauseNow()) {
     m_PauseStep = 1;
-    m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
+    m_ProcessingStatus = FXCODEC_STATUS::kDecodeToBeContinued;
     return true;
   }
   return Continue(pPause);
 }
 
 bool CJBig2_Context::Continue(PauseIndicatorIface* pPause) {
-  m_ProcessingStatus = FXCODEC_STATUS_DECODE_READY;
+  m_ProcessingStatus = FXCODEC_STATUS::kDecodeReady;
   JBig2_Result nRet = JBig2_Result::kSuccess;
   if (m_PauseStep == 5) {
-    m_ProcessingStatus = FXCODEC_STATUS_DECODE_FINISH;
+    m_ProcessingStatus = FXCODEC_STATUS::kDecodeFinished;
     return true;
   }
 
   if (m_PauseStep <= 2)
     nRet = DecodeSequential(pPause);
-  if (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE)
+  if (m_ProcessingStatus == FXCODEC_STATUS::kDecodeToBeContinued)
     return nRet == JBig2_Result::kSuccess;
 
   m_PauseStep = 5;
   if (!m_bBufSpecified && nRet == JBig2_Result::kSuccess) {
-    m_ProcessingStatus = FXCODEC_STATUS_DECODE_FINISH;
+    m_ProcessingStatus = FXCODEC_STATUS::kDecodeFinished;
     return true;
   }
   m_ProcessingStatus = nRet == JBig2_Result::kSuccess
-                           ? FXCODEC_STATUS_DECODE_FINISH
-                           : FXCODEC_STATUS_ERROR;
+                           ? FXCODEC_STATUS::kDecodeFinished
+                           : FXCODEC_STATUS::kError;
   return nRet == JBig2_Result::kSuccess;
 }
 
@@ -217,7 +218,7 @@
     }
     pSegment->m_nReferred_to_segment_count &= 0x1fffffff;
     if (pSegment->m_nReferred_to_segment_count >
-        JBIG2_MAX_REFERRED_SEGMENT_COUNT) {
+        kJBig2MaxReferredSegmentCount) {
       return JBig2_Result::kFailure;
     }
   } else {
@@ -269,7 +270,7 @@
   if (m_pStream->readInteger(&pSegment->m_dwData_length) != 0)
     return JBig2_Result::kFailure;
 
-  pSegment->m_dwObjNum = m_pStream->getObjNum();
+  pSegment->m_Key = m_pStream->getKey();
   pSegment->m_dwDataOffset = m_pStream->getOffset();
   pSegment->m_State = JBIG2_SEGMENT_DATA_UNPARSED;
   return JBig2_Result::kSuccess;
@@ -278,7 +279,7 @@
 JBig2_Result CJBig2_Context::ParseSegmentData(CJBig2_Segment* pSegment,
                                               PauseIndicatorIface* pPause) {
   JBig2_Result ret = ProcessingParseSegmentData(pSegment, pPause);
-  while (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE &&
+  while (m_ProcessingStatus == FXCODEC_STATUS::kDecodeToBeContinued &&
          m_pStream->getByteLeft() > 0) {
     ret = ProcessingParseSegmentData(pSegment, pPause);
   }
@@ -318,18 +319,21 @@
         return JBig2_Result::kFailure;
       return ParseGenericRefinementRegion(pSegment);
     case 48: {
-      uint16_t wTemp;
-      auto pPageInfo = pdfium::MakeUnique<JBig2PageInfo>();
+      uint8_t segment_flags;
+      uint16_t striping_info;
+      auto pPageInfo = std::make_unique<JBig2PageInfo>();
       if (m_pStream->readInteger(&pPageInfo->m_dwWidth) != 0 ||
           m_pStream->readInteger(&pPageInfo->m_dwHeight) != 0 ||
           m_pStream->readInteger(&pPageInfo->m_dwResolutionX) != 0 ||
           m_pStream->readInteger(&pPageInfo->m_dwResolutionY) != 0 ||
-          m_pStream->read1Byte(&pPageInfo->m_cFlags) != 0 ||
-          m_pStream->readShortInteger(&wTemp) != 0) {
+          m_pStream->read1Byte(&segment_flags) != 0 ||
+          m_pStream->readShortInteger(&striping_info) != 0) {
         return JBig2_Result::kFailure;
       }
-      pPageInfo->m_bIsStriped = !!(wTemp & 0x8000);
-      pPageInfo->m_wMaxStripeSize = wTemp & 0x7fff;
+
+      pPageInfo->m_bDefaultPixelValue = !!(segment_flags & 4);
+      pPageInfo->m_bIsStriped = !!(striping_info & 0x8000);
+      pPageInfo->m_wMaxStripeSize = striping_info & 0x7fff;
       bool bMaxHeight = (pPageInfo->m_dwHeight == 0xffffffff);
       if (bMaxHeight && !pPageInfo->m_bIsStriped)
         pPageInfo->m_bIsStriped = true;
@@ -337,23 +341,22 @@
       if (!m_bBufSpecified) {
         uint32_t height =
             bMaxHeight ? pPageInfo->m_wMaxStripeSize : pPageInfo->m_dwHeight;
-        m_pPage =
-            pdfium::MakeUnique<CJBig2_Image>(pPageInfo->m_dwWidth, height);
+        m_pPage = std::make_unique<CJBig2_Image>(pPageInfo->m_dwWidth, height);
       }
 
       if (!m_pPage->data()) {
-        m_ProcessingStatus = FXCODEC_STATUS_ERROR;
+        m_ProcessingStatus = FXCODEC_STATUS::kError;
         return JBig2_Result::kFailure;
       }
 
-      m_pPage->Fill((pPageInfo->m_cFlags & 4) ? 1 : 0);
+      m_pPage->Fill(pPageInfo->m_bDefaultPixelValue);
       m_PageInfoList.push_back(std::move(pPageInfo));
       m_bInPage = true;
-    } break;
+      break;
+    }
     case 49:
       m_bInPage = false;
       return JBig2_Result::kEndReached;
-      break;
     case 50:
       m_pStream->offset(pSegment->m_dwData_length);
       break;
@@ -378,19 +381,19 @@
   if (m_pStream->readShortInteger(&wFlags) != 0)
     return JBig2_Result::kFailure;
 
-  auto pSymbolDictDecoder = pdfium::MakeUnique<CJBig2_SDDProc>();
+  auto pSymbolDictDecoder = std::make_unique<CJBig2_SDDProc>();
   pSymbolDictDecoder->SDHUFF = wFlags & 0x0001;
   pSymbolDictDecoder->SDREFAGG = (wFlags >> 1) & 0x0001;
   pSymbolDictDecoder->SDTEMPLATE = (wFlags >> 10) & 0x0003;
   pSymbolDictDecoder->SDRTEMPLATE = !!((wFlags >> 12) & 0x0003);
-  if (pSymbolDictDecoder->SDHUFF == 0) {
+  if (!pSymbolDictDecoder->SDHUFF) {
     const uint32_t dwTemp = (pSymbolDictDecoder->SDTEMPLATE == 0) ? 8 : 2;
     for (uint32_t i = 0; i < dwTemp; ++i) {
       if (m_pStream->read1Byte((uint8_t*)&pSymbolDictDecoder->SDAT[i]) != 0)
         return JBig2_Result::kFailure;
     }
   }
-  if (pSymbolDictDecoder->SDREFAGG == 1 && !pSymbolDictDecoder->SDRTEMPLATE) {
+  if (pSymbolDictDecoder->SDREFAGG && !pSymbolDictDecoder->SDRTEMPLATE) {
     for (int32_t i = 0; i < 4; ++i) {
       if (m_pStream->read1Byte((uint8_t*)&pSymbolDictDecoder->SDRAT[i]) != 0)
         return JBig2_Result::kFailure;
@@ -400,8 +403,8 @@
       m_pStream->readInteger(&pSymbolDictDecoder->SDNUMNEWSYMS) != 0) {
     return JBig2_Result::kFailure;
   }
-  if (pSymbolDictDecoder->SDNUMEXSYMS > JBIG2_MAX_EXPORT_SYSMBOLS ||
-      pSymbolDictDecoder->SDNUMNEWSYMS > JBIG2_MAX_NEW_SYSMBOLS) {
+  if (pSymbolDictDecoder->SDNUMEXSYMS > kJBig2MaxExportSymbols ||
+      pSymbolDictDecoder->SDNUMNEWSYMS > kJBig2MaxNewSymbols) {
     return JBig2_Result::kFailure;
   }
   for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) {
@@ -409,28 +412,31 @@
       return JBig2_Result::kFailure;
   }
   CJBig2_Segment* pLRSeg = nullptr;
-  pSymbolDictDecoder->SDNUMINSYMS = 0;
+  FX_SAFE_UINT32 dwNumSyms = 0;
   for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) {
     CJBig2_Segment* pSeg =
         FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]);
     if (pSeg->m_cFlags.s.type == 0) {
-      pSymbolDictDecoder->SDNUMINSYMS += pSeg->m_SymbolDict->NumImages();
+      dwNumSyms += pSeg->m_SymbolDict->NumImages();
       pLRSeg = pSeg;
     }
   }
+  pSymbolDictDecoder->SDNUMINSYMS = dwNumSyms.ValueOrDie();
 
   std::unique_ptr<CJBig2_Image*, FxFreeDeleter> SDINSYMS;
   if (pSymbolDictDecoder->SDNUMINSYMS != 0) {
     SDINSYMS.reset(FX_Alloc(CJBig2_Image*, pSymbolDictDecoder->SDNUMINSYMS));
-    uint32_t dwTemp = 0;
+    dwNumSyms = 0;
     for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) {
       CJBig2_Segment* pSeg =
           FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]);
       if (pSeg->m_cFlags.s.type == 0) {
         const CJBig2_SymbolDict& dict = *pSeg->m_SymbolDict;
-        for (size_t j = 0; j < dict.NumImages(); ++j)
-          SDINSYMS.get()[dwTemp + j] = dict.GetImage(j);
-        dwTemp += dict.NumImages();
+        for (uint32_t j = 0; j < dict.NumImages(); ++j) {
+          uint32_t dwTemp = (dwNumSyms + j).ValueOrDie();
+          SDINSYMS.get()[dwTemp] = dict.GetImage(j);
+        }
+        dwNumSyms += dict.NumImages();
       }
     }
   }
@@ -438,7 +444,7 @@
 
   uint8_t cSDHUFFDH = (wFlags >> 2) & 0x0003;
   uint8_t cSDHUFFDW = (wFlags >> 4) & 0x0003;
-  if (pSymbolDictDecoder->SDHUFF == 1) {
+  if (pSymbolDictDecoder->SDHUFF) {
     if (cSDHUFFDH == 2 || cSDHUFFDW == 2)
       return JBig2_Result::kFailure;
 
@@ -475,7 +481,7 @@
         return JBig2_Result::kFailure;
       pSymbolDictDecoder->SDHUFFBMSIZE = pSeg->m_HuffmanTable.get();
     }
-    if (pSymbolDictDecoder->SDREFAGG == 1) {
+    if (pSymbolDictDecoder->SDREFAGG) {
       uint8_t cSDHUFFAGGINST = (wFlags >> 7) & 0x0001;
       if (cSDHUFFAGGINST == 0) {
         pSymbolDictDecoder->SDHUFFAGGINST = GetHuffmanTable(1);
@@ -489,8 +495,8 @@
     }
   }
 
-  const bool bUseGbContext = (pSymbolDictDecoder->SDHUFF == 0);
-  const bool bUseGrContext = (pSymbolDictDecoder->SDREFAGG == 1);
+  const bool bUseGbContext = !pSymbolDictDecoder->SDHUFF;
+  const bool bUseGrContext = pSymbolDictDecoder->SDREFAGG;
   const size_t gbContextSize =
       GetHuffContextSize(pSymbolDictDecoder->SDTEMPLATE);
   const size_t grContextSize =
@@ -515,8 +521,7 @@
       grContext.resize(grContextSize);
   }
 
-  CJBig2_CacheKey key =
-      CJBig2_CacheKey(pSegment->m_dwObjNum, pSegment->m_dwDataOffset);
+  CJBig2_CompoundKey key(pSegment->m_Key, pSegment->m_dwDataOffset);
   bool cache_hit = false;
   pSegment->m_nResultType = JBIG2_SYMBOL_DICT_POINTER;
   if (m_bIsGlobal && key.first != 0) {
@@ -535,7 +540,7 @@
   if (!cache_hit) {
     if (bUseGbContext) {
       auto pArithDecoder =
-          pdfium::MakeUnique<CJBig2_ArithDecoder>(m_pStream.get());
+          std::make_unique<CJBig2_ArithDecoder>(m_pStream.get());
       pSegment->m_SymbolDict = pSymbolDictDecoder->DecodeArith(
           pArithDecoder.get(), &gbContext, &grContext);
       if (!pSegment->m_SymbolDict)
@@ -580,7 +585,7 @@
   if (!CJBig2_Image::IsValidImageSize(ri.width, ri.height))
     return JBig2_Result::kFailure;
 
-  auto pTRD = pdfium::MakeUnique<CJBig2_TRDProc>();
+  auto pTRD = std::make_unique<CJBig2_TRDProc>();
   pTRD->SBW = ri.width;
   pTRD->SBH = ri.height;
   pTRD->SBHUFF = wFlags & 0x0001;
@@ -597,10 +602,10 @@
   }
   pTRD->SBRTEMPLATE = !!((wFlags >> 15) & 0x0001);
 
-  if (pTRD->SBHUFF == 1 && m_pStream->readShortInteger(&wFlags) != 0) {
+  if (pTRD->SBHUFF && m_pStream->readShortInteger(&wFlags) != 0) {
     return JBig2_Result::kFailure;
   }
-  if (pTRD->SBREFINE == 1 && !pTRD->SBRTEMPLATE) {
+  if (pTRD->SBREFINE && !pTRD->SBRTEMPLATE) {
     for (int32_t i = 0; i < 4; ++i) {
       if (m_pStream->read1Byte((uint8_t*)&pTRD->SBRAT[i]) != 0)
         return JBig2_Result::kFailure;
@@ -624,27 +629,30 @@
       return JBig2_Result::kFailure;
   }
 
-  pTRD->SBNUMSYMS = 0;
+  FX_SAFE_UINT32 dwNumSyms = 0;
   for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) {
     CJBig2_Segment* pSeg =
         FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]);
     if (pSeg->m_cFlags.s.type == 0) {
-      pTRD->SBNUMSYMS += pSeg->m_SymbolDict->NumImages();
+      dwNumSyms += pSeg->m_SymbolDict->NumImages();
     }
   }
+  pTRD->SBNUMSYMS = dwNumSyms.ValueOrDie();
 
   std::unique_ptr<CJBig2_Image*, FxFreeDeleter> SBSYMS;
   if (pTRD->SBNUMSYMS > 0) {
     SBSYMS.reset(FX_Alloc(CJBig2_Image*, pTRD->SBNUMSYMS));
-    dwTemp = 0;
+    dwNumSyms = 0;
     for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) {
       CJBig2_Segment* pSeg =
           FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]);
       if (pSeg->m_cFlags.s.type == 0) {
         const CJBig2_SymbolDict& dict = *pSeg->m_SymbolDict;
-        for (size_t j = 0; j < dict.NumImages(); ++j)
-          SBSYMS.get()[dwTemp + j] = dict.GetImage(j);
-        dwTemp += dict.NumImages();
+        for (uint32_t j = 0; j < dict.NumImages(); ++j) {
+          uint32_t dwIndex = (dwNumSyms + j).ValueOrDie();
+          SBSYMS.get()[dwIndex] = dict.GetImage(j);
+        }
+        dwNumSyms += dict.NumImages();
       }
     }
     pTRD->SBSYMS = SBSYMS.get();
@@ -652,7 +660,7 @@
     pTRD->SBSYMS = nullptr;
   }
 
-  if (pTRD->SBHUFF == 1) {
+  if (pTRD->SBHUFF) {
     std::vector<JBig2HuffmanCode> SBSYMCODES =
         DecodeSymbolIDHuffmanTable(pTRD->SBNUMSYMS);
     if (SBSYMCODES.empty())
@@ -668,7 +676,7 @@
     pTRD->SBSYMCODELEN = (uint8_t)dwTemp;
   }
 
-  if (pTRD->SBHUFF == 1) {
+  if (pTRD->SBHUFF) {
     uint8_t cSBHUFFFS = wFlags & 0x0003;
     uint8_t cSBHUFFDS = (wFlags >> 2) & 0x0003;
     uint8_t cSBHUFFDT = (wFlags >> 4) & 0x0003;
@@ -774,34 +782,30 @@
     }
   }
   std::unique_ptr<JBig2ArithCtx, FxFreeDeleter> grContext;
-  if (pTRD->SBREFINE == 1) {
+  if (pTRD->SBREFINE) {
     const size_t size = GetRefAggContextSize(pTRD->SBRTEMPLATE);
     grContext.reset(FX_Alloc(JBig2ArithCtx, size));
   }
-  if (pTRD->SBHUFF == 0) {
-    auto pArithDecoder =
-        pdfium::MakeUnique<CJBig2_ArithDecoder>(m_pStream.get());
-    pSegment->m_nResultType = JBIG2_IMAGE_POINTER;
+  pSegment->m_nResultType = JBIG2_IMAGE_POINTER;
+  if (pTRD->SBHUFF) {
+    pSegment->m_Image = pTRD->DecodeHuffman(m_pStream.get(), grContext.get());
+    if (!pSegment->m_Image)
+      return JBig2_Result::kFailure;
+    m_pStream->alignByte();
+  } else {
+    auto pArithDecoder = std::make_unique<CJBig2_ArithDecoder>(m_pStream.get());
     pSegment->m_Image =
         pTRD->DecodeArith(pArithDecoder.get(), grContext.get(), nullptr);
     if (!pSegment->m_Image)
       return JBig2_Result::kFailure;
     m_pStream->alignByte();
     m_pStream->offset(2);
-  } else {
-    pSegment->m_nResultType = JBIG2_IMAGE_POINTER;
-    pSegment->m_Image = pTRD->DecodeHuffman(m_pStream.get(), grContext.get());
-    if (!pSegment->m_Image)
-      return JBig2_Result::kFailure;
-    m_pStream->alignByte();
   }
   if (pSegment->m_cFlags.s.type != 4) {
     if (!m_bBufSpecified) {
       const auto& pPageInfo = m_PageInfoList.back();
-      if ((pPageInfo->m_bIsStriped == 1) &&
-          (ri.y + ri.height > m_pPage->height())) {
-        m_pPage->Expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0);
-      }
+      if (pPageInfo->m_bIsStriped && ri.y + ri.height > m_pPage->height())
+        m_pPage->Expand(ri.y + ri.height, pPageInfo->m_bDefaultPixelValue);
     }
     m_pPage->ComposeFrom(ri.x, ri.y, pSegment->m_Image.get(),
                          (JBig2ComposeOp)(ri.flags & 0x03));
@@ -813,25 +817,29 @@
 JBig2_Result CJBig2_Context::ParsePatternDict(CJBig2_Segment* pSegment,
                                               PauseIndicatorIface* pPause) {
   uint8_t cFlags;
-  auto pPDD = pdfium::MakeUnique<CJBig2_PDDProc>();
+  auto pPDD = std::make_unique<CJBig2_PDDProc>();
   if (m_pStream->read1Byte(&cFlags) != 0 ||
       m_pStream->read1Byte(&pPDD->HDPW) != 0 ||
       m_pStream->read1Byte(&pPDD->HDPH) != 0 ||
       m_pStream->readInteger(&pPDD->GRAYMAX) != 0) {
     return JBig2_Result::kFailure;
   }
-  if (pPDD->GRAYMAX > JBIG2_MAX_PATTERN_INDEX)
+  if (pPDD->GRAYMAX > kJBig2MaxPatternIndex)
     return JBig2_Result::kFailure;
 
   pPDD->HDMMR = cFlags & 0x01;
   pPDD->HDTEMPLATE = (cFlags >> 1) & 0x03;
   pSegment->m_nResultType = JBIG2_PATTERN_DICT_POINTER;
-  if (pPDD->HDMMR == 0) {
+  if (pPDD->HDMMR) {
+    pSegment->m_PatternDict = pPDD->DecodeMMR(m_pStream.get());
+    if (!pSegment->m_PatternDict)
+      return JBig2_Result::kFailure;
+    m_pStream->alignByte();
+  } else {
     const size_t size = GetHuffContextSize(pPDD->HDTEMPLATE);
     std::unique_ptr<JBig2ArithCtx, FxFreeDeleter> gbContext(
         FX_Alloc(JBig2ArithCtx, size));
-    auto pArithDecoder =
-        pdfium::MakeUnique<CJBig2_ArithDecoder>(m_pStream.get());
+    auto pArithDecoder = std::make_unique<CJBig2_ArithDecoder>(m_pStream.get());
     pSegment->m_PatternDict =
         pPDD->DecodeArith(pArithDecoder.get(), gbContext.get(), pPause);
     if (!pSegment->m_PatternDict)
@@ -839,11 +847,6 @@
 
     m_pStream->alignByte();
     m_pStream->offset(2);
-  } else {
-    pSegment->m_PatternDict = pPDD->DecodeMMR(m_pStream.get());
-    if (!pSegment->m_PatternDict)
-      return JBig2_Result::kFailure;
-    m_pStream->alignByte();
   }
   return JBig2_Result::kSuccess;
 }
@@ -852,7 +855,7 @@
                                                  PauseIndicatorIface* pPause) {
   uint8_t cFlags;
   JBig2RegionInfo ri;
-  auto pHRD = pdfium::MakeUnique<CJBig2_HTRDProc>();
+  auto pHRD = std::make_unique<CJBig2_HTRDProc>();
   if (ParseRegionInfo(&ri) != JBig2_Result::kSuccess ||
       m_pStream->read1Byte(&cFlags) != 0 ||
       m_pStream->readInteger(&pHRD->HGW) != 0 ||
@@ -894,12 +897,16 @@
   pHRD->HPW = pPatternDict->HDPATS[0]->width();
   pHRD->HPH = pPatternDict->HDPATS[0]->height();
   pSegment->m_nResultType = JBIG2_IMAGE_POINTER;
-  if (pHRD->HMMR == 0) {
+  if (pHRD->HMMR) {
+    pSegment->m_Image = pHRD->DecodeMMR(m_pStream.get());
+    if (!pSegment->m_Image)
+      return JBig2_Result::kFailure;
+    m_pStream->alignByte();
+  } else {
     const size_t size = GetHuffContextSize(pHRD->HTEMPLATE);
     std::unique_ptr<JBig2ArithCtx, FxFreeDeleter> gbContext(
         FX_Alloc(JBig2ArithCtx, size));
-    auto pArithDecoder =
-        pdfium::MakeUnique<CJBig2_ArithDecoder>(m_pStream.get());
+    auto pArithDecoder = std::make_unique<CJBig2_ArithDecoder>(m_pStream.get());
     pSegment->m_Image =
         pHRD->DecodeArith(pArithDecoder.get(), gbContext.get(), pPause);
     if (!pSegment->m_Image)
@@ -907,19 +914,12 @@
 
     m_pStream->alignByte();
     m_pStream->offset(2);
-  } else {
-    pSegment->m_Image = pHRD->DecodeMMR(m_pStream.get());
-    if (!pSegment->m_Image)
-      return JBig2_Result::kFailure;
-    m_pStream->alignByte();
   }
   if (pSegment->m_cFlags.s.type != 20) {
     if (!m_bBufSpecified) {
       const auto& pPageInfo = m_PageInfoList.back();
-      if (pPageInfo->m_bIsStriped == 1 &&
-          ri.y + ri.height > m_pPage->height()) {
-        m_pPage->Expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0);
-      }
+      if (pPageInfo->m_bIsStriped && ri.y + ri.height > m_pPage->height())
+        m_pPage->Expand(ri.y + ri.height, pPageInfo->m_bDefaultPixelValue);
     }
     m_pPage->ComposeFrom(ri.x, ri.y, pSegment->m_Image.get(),
                          (JBig2ComposeOp)(ri.flags & 0x03));
@@ -931,7 +931,7 @@
 JBig2_Result CJBig2_Context::ParseGenericRegion(CJBig2_Segment* pSegment,
                                                 PauseIndicatorIface* pPause) {
   if (!m_pGRD) {
-    auto pGRD = pdfium::MakeUnique<CJBig2_GRDProc>();
+    auto pGRD = std::make_unique<CJBig2_GRDProc>();
     uint8_t cFlags;
     if (ParseRegionInfo(&m_ri) != JBig2_Result::kSuccess ||
         m_pStream->read1Byte(&cFlags) != 0) {
@@ -944,7 +944,7 @@
     pGRD->MMR = cFlags & 0x01;
     pGRD->GBTEMPLATE = (cFlags >> 1) & 0x03;
     pGRD->TPGDON = (cFlags >> 3) & 0x01;
-    if (pGRD->MMR == 0) {
+    if (!pGRD->MMR) {
       if (pGRD->GBTEMPLATE == 0) {
         for (int32_t i = 0; i < 8; ++i) {
           if (m_pStream->read1Byte((uint8_t*)&pGRD->GBAT[i]) != 0)
@@ -957,18 +957,24 @@
         }
       }
     }
-    pGRD->USESKIP = 0;
+    pGRD->USESKIP = false;
     m_pGRD = std::move(pGRD);
   }
   pSegment->m_nResultType = JBIG2_IMAGE_POINTER;
-  if (m_pGRD->MMR == 0) {
+  if (m_pGRD->MMR) {
+    m_pGRD->StartDecodeMMR(&pSegment->m_Image, m_pStream.get());
+    if (!pSegment->m_Image) {
+      m_pGRD.reset();
+      return JBig2_Result::kFailure;
+    }
+    m_pStream->alignByte();
+  } else {
     if (m_gbContext.empty())
       m_gbContext.resize(GetHuffContextSize(m_pGRD->GBTEMPLATE));
 
     bool bStart = !m_pArithDecoder;
     if (bStart) {
-      m_pArithDecoder =
-          pdfium::MakeUnique<CJBig2_ArithDecoder>(m_pStream.get());
+      m_pArithDecoder = std::make_unique<CJBig2_ArithDecoder>(m_pStream.get());
     }
     {
       // |state.gbContext| can't exist when m_gbContext.clear() called below.
@@ -979,14 +985,14 @@
       state.pPause = pPause;
       m_ProcessingStatus = bStart ? m_pGRD->StartDecodeArith(&state)
                                   : m_pGRD->ContinueDecode(&state);
-      if (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE) {
+      if (m_ProcessingStatus == FXCODEC_STATUS::kDecodeToBeContinued) {
         if (pSegment->m_cFlags.s.type != 36) {
           if (!m_bBufSpecified) {
             const auto& pPageInfo = m_PageInfoList.back();
-            if ((pPageInfo->m_bIsStriped == 1) &&
-                (m_ri.y + m_ri.height > m_pPage->height())) {
+            if (pPageInfo->m_bIsStriped &&
+                m_ri.y + m_ri.height > m_pPage->height()) {
               m_pPage->Expand(m_ri.y + m_ri.height,
-                              (pPageInfo->m_cFlags & 4) ? 1 : 0);
+                              pPageInfo->m_bDefaultPixelValue);
             }
           }
           const FX_RECT& rect = m_pGRD->GetReplaceRect();
@@ -1000,28 +1006,18 @@
     m_pArithDecoder.reset();
     m_gbContext.clear();
     if (!pSegment->m_Image) {
-      m_ProcessingStatus = FXCODEC_STATUS_ERROR;
+      m_ProcessingStatus = FXCODEC_STATUS::kError;
       m_pGRD.reset();
       return JBig2_Result::kFailure;
     }
     m_pStream->alignByte();
     m_pStream->offset(2);
-  } else {
-    m_pGRD->StartDecodeMMR(&pSegment->m_Image, m_pStream.get());
-    if (!pSegment->m_Image) {
-      m_pGRD.reset();
-      return JBig2_Result::kFailure;
-    }
-    m_pStream->alignByte();
   }
   if (pSegment->m_cFlags.s.type != 36) {
     if (!m_bBufSpecified) {
       JBig2PageInfo* pPageInfo = m_PageInfoList.back().get();
-      if ((pPageInfo->m_bIsStriped == 1) &&
-          (m_ri.y + m_ri.height > m_pPage->height())) {
-        m_pPage->Expand(m_ri.y + m_ri.height,
-                        (pPageInfo->m_cFlags & 4) ? 1 : 0);
-      }
+      if (pPageInfo->m_bIsStriped && m_ri.y + m_ri.height > m_pPage->height())
+        m_pPage->Expand(m_ri.y + m_ri.height, pPageInfo->m_bDefaultPixelValue);
     }
     const FX_RECT& rect = m_pGRD->GetReplaceRect();
     m_pPage->ComposeFromWithRect(m_ri.x + rect.left, m_ri.y + rect.top,
@@ -1044,7 +1040,7 @@
   if (!CJBig2_Image::IsValidImageSize(ri.width, ri.height))
     return JBig2_Result::kFailure;
 
-  auto pGRRD = pdfium::MakeUnique<CJBig2_GRRDProc>();
+  auto pGRRD = std::make_unique<CJBig2_GRRDProc>();
   pGRRD->GRW = ri.width;
   pGRRD->GRH = ri.height;
   pGRRD->GRTEMPLATE = !!(cFlags & 0x01);
@@ -1080,7 +1076,7 @@
   const size_t size = GetRefAggContextSize(pGRRD->GRTEMPLATE);
   std::unique_ptr<JBig2ArithCtx, FxFreeDeleter> grContext(
       FX_Alloc(JBig2ArithCtx, size));
-  auto pArithDecoder = pdfium::MakeUnique<CJBig2_ArithDecoder>(m_pStream.get());
+  auto pArithDecoder = std::make_unique<CJBig2_ArithDecoder>(m_pStream.get());
   pSegment->m_nResultType = JBIG2_IMAGE_POINTER;
   pSegment->m_Image = pGRRD->Decode(pArithDecoder.get(), grContext.get());
   if (!pSegment->m_Image)
@@ -1091,10 +1087,8 @@
   if (pSegment->m_cFlags.s.type != 40) {
     if (!m_bBufSpecified) {
       JBig2PageInfo* pPageInfo = m_PageInfoList.back().get();
-      if ((pPageInfo->m_bIsStriped == 1) &&
-          (ri.y + ri.height > m_pPage->height())) {
-        m_pPage->Expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0);
-      }
+      if (pPageInfo->m_bIsStriped && ri.y + ri.height > m_pPage->height())
+        m_pPage->Expand(ri.y + ri.height, pPageInfo->m_bDefaultPixelValue);
     }
     m_pPage->ComposeFrom(ri.x, ri.y, pSegment->m_Image.get(),
                          (JBig2ComposeOp)(ri.flags & 0x03));
@@ -1106,7 +1100,7 @@
 JBig2_Result CJBig2_Context::ParseTable(CJBig2_Segment* pSegment) {
   pSegment->m_nResultType = JBIG2_HUFFMAN_TABLE_POINTER;
   pSegment->m_HuffmanTable.reset();
-  auto pHuff = pdfium::MakeUnique<CJBig2_HuffmanTable>(m_pStream.get());
+  auto pHuff = std::make_unique<CJBig2_HuffmanTable>(m_pStream.get());
   if (!pHuff->IsOK())
     return JBig2_Result::kFailure;
 
@@ -1200,10 +1194,10 @@
 }
 
 const CJBig2_HuffmanTable* CJBig2_Context::GetHuffmanTable(size_t idx) {
-  ASSERT(idx > 0);
-  ASSERT(idx < CJBig2_HuffmanTable::kNumHuffmanTables);
+  DCHECK(idx > 0);
+  DCHECK(idx < CJBig2_HuffmanTable::kNumHuffmanTables);
   if (!m_HuffmanTables[idx].get())
-    m_HuffmanTables[idx] = pdfium::MakeUnique<CJBig2_HuffmanTable>(idx);
+    m_HuffmanTables[idx] = std::make_unique<CJBig2_HuffmanTable>(idx);
   return m_HuffmanTables[idx].get();
 }
 
@@ -1221,7 +1215,7 @@
   LENCOUNT[0] = 0;
 
   for (int i = 1; i <= LENMAX; ++i) {
-    pdfium::base::CheckedNumeric<int> shifted = FIRSTCODE[i - 1];
+    FX_SAFE_INT32 shifted = FIRSTCODE[i - 1];
     shifted += LENCOUNT[i - 1];
     shifted <<= 1;
     if (!shifted.IsValid())
diff --git a/core/fxcodec/jbig2/JBig2_Context.h b/core/fxcodec/jbig2/JBig2_Context.h
index 49669ed..5572eb7 100644
--- a/core/fxcodec/jbig2/JBig2_Context.h
+++ b/core/fxcodec/jbig2/JBig2_Context.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,21 +13,16 @@
 #include <vector>
 
 #include "core/fxcodec/fx_codec_def.h"
+#include "core/fxcodec/jbig2/JBig2_DocumentContext.h"
 #include "core/fxcodec/jbig2/JBig2_Page.h"
 #include "core/fxcodec/jbig2/JBig2_Segment.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/span.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/containers/span.h"
 
 class CJBig2_ArithDecoder;
 class CJBig2_GRDProc;
-class CPDF_StreamAcc;
 class PauseIndicatorIface;
 
-// Cache is keyed by the ObjNum of a stream and an index within the stream.
-using CJBig2_CacheKey = std::pair<uint32_t, uint32_t>;
-using CJBig2_CachePair =
-    std::pair<CJBig2_CacheKey, std::unique_ptr<CJBig2_SymbolDict>>;
-
 #define JBIG2_MIN_SEGMENT_SIZE 11
 
 enum class JBig2_Result { kSuccess, kFailure, kEndReached };
@@ -36,16 +31,16 @@
  public:
   static std::unique_ptr<CJBig2_Context> Create(
       pdfium::span<const uint8_t> pGlobalSpan,
-      uint32_t dwGlobalObjNum,
+      uint64_t global_key,
       pdfium::span<const uint8_t> pSrcSpan,
-      uint32_t dwSrcObjNum,
+      uint64_t src_key,
       std::list<CJBig2_CachePair>* pSymbolDictCache);
 
   ~CJBig2_Context();
 
   static bool HuffmanAssignCode(JBig2HuffmanCode* SBSYMCODES, uint32_t NTEMP);
 
-  bool GetFirstPage(uint8_t* pBuf,
+  bool GetFirstPage(pdfium::span<uint8_t> pBuf,
                     int32_t width,
                     int32_t height,
                     int32_t stride,
@@ -56,7 +51,7 @@
 
  private:
   CJBig2_Context(pdfium::span<const uint8_t> pSrcSpan,
-                 uint32_t dwObjNum,
+                 uint64_t src_key,
                  std::list<CJBig2_CachePair>* pSymbolDictCache,
                  bool bIsGlobal);
 
@@ -96,14 +91,14 @@
   bool m_bInPage = false;
   bool m_bBufSpecified = false;
   int32_t m_PauseStep = 10;
-  FXCODEC_STATUS m_ProcessingStatus = FXCODEC_STATUS_FRAME_READY;
+  FXCODEC_STATUS m_ProcessingStatus = FXCODEC_STATUS::kFrameReady;
   std::vector<JBig2ArithCtx> m_gbContext;
   std::unique_ptr<CJBig2_ArithDecoder> m_pArithDecoder;
   std::unique_ptr<CJBig2_GRDProc> m_pGRD;
   std::unique_ptr<CJBig2_Segment> m_pSegment;
-  FX_SAFE_UINT32 m_dwOffset = 0;
-  JBig2RegionInfo m_ri;
-  std::list<CJBig2_CachePair>* const m_pSymbolDictCache;
+  uint32_t m_nOffset = 0;
+  JBig2RegionInfo m_ri = {};
+  UnownedPtr<std::list<CJBig2_CachePair>> const m_pSymbolDictCache;
 };
 
 #endif  // CORE_FXCODEC_JBIG2_JBIG2_CONTEXT_H_
diff --git a/core/fxcodec/jbig2/JBig2_Define.h b/core/fxcodec/jbig2/JBig2_Define.h
index 17eb007..f2796f5 100644
--- a/core/fxcodec/jbig2/JBig2_Define.h
+++ b/core/fxcodec/jbig2/JBig2_Define.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,8 +9,6 @@
 
 #include <stdint.h>
 
-#define JBIG2_OOB 1
-
 struct JBig2RegionInfo {
   int32_t width;
   int32_t height;
@@ -24,10 +22,12 @@
   int32_t code;
 };
 
-#define JBIG2_MAX_REFERRED_SEGMENT_COUNT 64
-#define JBIG2_MAX_EXPORT_SYSMBOLS 65535
-#define JBIG2_MAX_NEW_SYSMBOLS 65535
-#define JBIG2_MAX_PATTERN_INDEX 65535
-#define JBIG2_MAX_IMAGE_SIZE 65535
+constexpr int32_t kJBig2OOB = 1;
+
+constexpr int32_t kJBig2MaxReferredSegmentCount = 64;
+constexpr uint32_t kJBig2MaxExportSymbols = 65535;
+constexpr uint32_t kJBig2MaxNewSymbols = 65535;
+constexpr uint32_t kJBig2MaxPatternIndex = 65535;
+constexpr int32_t kJBig2MaxImageSize = 65535;
 
 #endif  // CORE_FXCODEC_JBIG2_JBIG2_DEFINE_H_
diff --git a/core/fxcodec/jbig2/JBig2_DocumentContext.cpp b/core/fxcodec/jbig2/JBig2_DocumentContext.cpp
index bca92fe..423c1ba 100644
--- a/core/fxcodec/jbig2/JBig2_DocumentContext.cpp
+++ b/core/fxcodec/jbig2/JBig2_DocumentContext.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
diff --git a/core/fxcodec/jbig2/JBig2_DocumentContext.h b/core/fxcodec/jbig2/JBig2_DocumentContext.h
index 9bcdd37..b2a44c5 100644
--- a/core/fxcodec/jbig2/JBig2_DocumentContext.h
+++ b/core/fxcodec/jbig2/JBig2_DocumentContext.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
@@ -13,9 +13,10 @@
 
 class CJBig2_SymbolDict;
 
-using CJBig2_CacheKey = std::pair<uint32_t, uint32_t>;
+// Cache is keyed by both the key of a stream and an index within the stream.
+using CJBig2_CompoundKey = std::pair<uint64_t, uint32_t>;
 using CJBig2_CachePair =
-    std::pair<CJBig2_CacheKey, std::unique_ptr<CJBig2_SymbolDict>>;
+    std::pair<CJBig2_CompoundKey, std::unique_ptr<CJBig2_SymbolDict>>;
 
 // Holds per-document JBig2 related data.
 class JBig2_DocumentContext {
diff --git a/core/fxcodec/jbig2/JBig2_GrdProc.cpp b/core/fxcodec/jbig2/JBig2_GrdProc.cpp
index df6e207..a940d0e 100644
--- a/core/fxcodec/jbig2/JBig2_GrdProc.cpp
+++ b/core/fxcodec/jbig2/JBig2_GrdProc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,7 +15,6 @@
 #include "core/fxcodec/jbig2/JBig2_BitStream.h"
 #include "core/fxcodec/jbig2/JBig2_Image.h"
 #include "core/fxcrt/pauseindicator_iface.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -63,7 +62,7 @@
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* gbContext) {
   if (!CJBig2_Image::IsValidImageSize(GBW, GBH))
-    return pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
+    return std::make_unique<CJBig2_Image>(GBW, GBH);
 
   switch (GBTEMPLATE) {
     case 0:
@@ -89,7 +88,7 @@
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* gbContext,
     int OPT) {
-  auto GBREG = pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
+  auto GBREG = std::make_unique<CJBig2_Image>(GBW, GBH);
   if (!GBREG->data())
     return nullptr;
 
@@ -195,11 +194,11 @@
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* gbContext,
     int UNOPT) {
-  auto GBREG = pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
+  auto GBREG = std::make_unique<CJBig2_Image>(GBW, GBH);
   if (!GBREG->data())
     return nullptr;
 
-  GBREG->Fill(0);
+  GBREG->Fill(false);
   int LTP = 0;
   uint8_t MOD2 = UNOPT % 2;
   uint8_t DIV2 = UNOPT / 2;
@@ -256,7 +255,7 @@
 std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::DecodeArithTemplate3Opt3(
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* gbContext) {
-  auto GBREG = pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
+  auto GBREG = std::make_unique<CJBig2_Image>(GBW, GBH);
   if (!GBREG->data())
     return nullptr;
 
@@ -341,11 +340,11 @@
 std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::DecodeArithTemplate3Unopt(
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* gbContext) {
-  auto GBREG = pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
+  auto GBREG = std::make_unique<CJBig2_Image>(GBW, GBH);
   if (!GBREG->data())
     return nullptr;
 
-  GBREG->Fill(0);
+  GBREG->Fill(false);
   int LTP = 0;
   for (uint32_t h = 0; h < GBH; h++) {
     if (TPGDON) {
@@ -387,19 +386,19 @@
 FXCODEC_STATUS CJBig2_GRDProc::StartDecodeArith(
     ProgressiveArithDecodeState* pState) {
   if (!CJBig2_Image::IsValidImageSize(GBW, GBH)) {
-    m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH;
-    return FXCODEC_STATUS_DECODE_FINISH;
+    m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished;
+    return FXCODEC_STATUS::kDecodeFinished;
   }
-  m_ProssiveStatus = FXCODEC_STATUS_DECODE_READY;
+  m_ProgressiveStatus = FXCODEC_STATUS::kDecodeReady;
   std::unique_ptr<CJBig2_Image>* pImage = pState->pImage;
   if (!*pImage)
-    *pImage = pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
+    *pImage = std::make_unique<CJBig2_Image>(GBW, GBH);
   if (!(*pImage)->data()) {
     *pImage = nullptr;
-    m_ProssiveStatus = FXCODEC_STATUS_ERROR;
-    return FXCODEC_STATUS_ERROR;
+    m_ProgressiveStatus = FXCODEC_STATUS::kError;
+    return FXCODEC_STATUS::kError;
   }
-  pImage->get()->Fill(0);
+  pImage->get()->Fill(false);
   m_DecodeType = 1;
   m_LTP = 0;
   m_pLine = nullptr;
@@ -437,25 +436,25 @@
       break;
   }
   CJBig2_Image* pImage = pState->pImage->get();
-  m_ProssiveStatus = func(*this, pState);
+  m_ProgressiveStatus = func(*this, pState);
   m_ReplaceRect.left = 0;
   m_ReplaceRect.right = pImage->width();
   m_ReplaceRect.top = iline;
   m_ReplaceRect.bottom = m_loopIndex;
-  if (m_ProssiveStatus == FXCODEC_STATUS_DECODE_FINISH)
+  if (m_ProgressiveStatus == FXCODEC_STATUS::kDecodeFinished)
     m_loopIndex = 0;
 
-  return m_ProssiveStatus;
+  return m_ProgressiveStatus;
 }
 
 FXCODEC_STATUS CJBig2_GRDProc::StartDecodeMMR(
     std::unique_ptr<CJBig2_Image>* pImage,
     CJBig2_BitStream* pStream) {
-  auto image = pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
+  auto image = std::make_unique<CJBig2_Image>(GBW, GBH);
   if (!image->data()) {
     *pImage = nullptr;
-    m_ProssiveStatus = FXCODEC_STATUS_ERROR;
-    return m_ProssiveStatus;
+    m_ProgressiveStatus = FXCODEC_STATUS::kError;
+    return m_ProgressiveStatus;
   }
   int bitpos = static_cast<int>(pStream->getBitPos());
   bitpos =
@@ -464,19 +463,19 @@
   pStream->setBitPos(bitpos);
   for (uint32_t i = 0; i < image->stride() * GBH; ++i)
     image->data()[i] = ~image->data()[i];
-  m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH;
+  m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished;
   *pImage = std::move(image);
-  return m_ProssiveStatus;
+  return m_ProgressiveStatus;
 }
 
 FXCODEC_STATUS CJBig2_GRDProc::ContinueDecode(
     ProgressiveArithDecodeState* pState) {
-  if (m_ProssiveStatus != FXCODEC_STATUS_DECODE_TOBECONTINUE)
-    return m_ProssiveStatus;
+  if (m_ProgressiveStatus != FXCODEC_STATUS::kDecodeToBeContinued)
+    return m_ProgressiveStatus;
 
   if (m_DecodeType != 1) {
-    m_ProssiveStatus = FXCODEC_STATUS_ERROR;
-    return m_ProssiveStatus;
+    m_ProgressiveStatus = FXCODEC_STATUS::kError;
+    return m_ProgressiveStatus;
   }
   return ProgressiveDecodeArith(pState);
 }
@@ -484,8 +483,8 @@
 FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Opt3(
     ProgressiveArithDecodeState* pState) {
   CJBig2_Image* pImage = pState->pImage->get();
-  JBig2ArithCtx* gbContext = pState->gbContext.Get();
-  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get();
+  JBig2ArithCtx* gbContext = pState->gbContext;
+  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder;
   if (!m_pLine)
     m_pLine = pImage->data();
   int32_t nStride = pImage->stride();
@@ -497,7 +496,7 @@
   for (; m_loopIndex < height; m_loopIndex++) {
     if (TPGDON) {
       if (pArithDecoder->IsComplete())
-        return FXCODEC_STATUS_ERROR;
+        return FXCODEC_STATUS::kError;
 
       m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x9b25]);
     }
@@ -516,7 +515,7 @@
           uint8_t cVal = 0;
           for (int32_t k = 7; k >= 0; k--) {
             if (pArithDecoder->IsComplete())
-              return FXCODEC_STATUS_ERROR;
+              return FXCODEC_STATUS::kError;
 
             int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
@@ -530,7 +529,7 @@
         uint8_t cVal1 = 0;
         for (int32_t k = 0; k < nBitsLeft; k++) {
           if (pArithDecoder->IsComplete())
-            return FXCODEC_STATUS_ERROR;
+            return FXCODEC_STATUS::kError;
 
           int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
@@ -550,7 +549,7 @@
           uint8_t cVal = 0;
           for (int32_t k = 7; k >= 0; k--) {
             if (pArithDecoder->IsComplete())
-              return FXCODEC_STATUS_ERROR;
+              return FXCODEC_STATUS::kError;
 
             int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
@@ -563,7 +562,7 @@
         uint8_t cVal1 = 0;
         for (int32_t k = 0; k < nBitsLeft; k++) {
           if (pArithDecoder->IsComplete())
-            return FXCODEC_STATUS_ERROR;
+            return FXCODEC_STATUS::kError;
 
           int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
@@ -576,23 +575,23 @@
     m_pLine += nStride;
     if (pState->pPause && pState->pPause->NeedToPauseNow()) {
       m_loopIndex++;
-      m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-      return FXCODEC_STATUS_DECODE_TOBECONTINUE;
+      m_ProgressiveStatus = FXCODEC_STATUS::kDecodeToBeContinued;
+      return FXCODEC_STATUS::kDecodeToBeContinued;
     }
   }
-  m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH;
-  return FXCODEC_STATUS_DECODE_FINISH;
+  m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished;
+  return FXCODEC_STATUS::kDecodeFinished;
 }
 
 FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Unopt(
     ProgressiveArithDecodeState* pState) {
   CJBig2_Image* pImage = pState->pImage->get();
-  JBig2ArithCtx* gbContext = pState->gbContext.Get();
-  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get();
+  JBig2ArithCtx* gbContext = pState->gbContext;
+  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder;
   for (; m_loopIndex < GBH; m_loopIndex++) {
     if (TPGDON) {
       if (pArithDecoder->IsComplete())
-        return FXCODEC_STATUS_ERROR;
+        return FXCODEC_STATUS::kError;
 
       m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x9b25]);
     }
@@ -618,7 +617,7 @@
           CONTEXT |= line1 << 12;
           CONTEXT |= pImage->GetPixel(w + GBAT[6], m_loopIndex + GBAT[7]) << 15;
           if (pArithDecoder->IsComplete())
-            return FXCODEC_STATUS_ERROR;
+            return FXCODEC_STATUS::kError;
 
           bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
         }
@@ -634,19 +633,19 @@
     }
     if (pState->pPause && pState->pPause->NeedToPauseNow()) {
       m_loopIndex++;
-      m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-      return FXCODEC_STATUS_DECODE_TOBECONTINUE;
+      m_ProgressiveStatus = FXCODEC_STATUS::kDecodeToBeContinued;
+      return FXCODEC_STATUS::kDecodeToBeContinued;
     }
   }
-  m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH;
-  return FXCODEC_STATUS_DECODE_FINISH;
+  m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished;
+  return FXCODEC_STATUS::kDecodeFinished;
 }
 
 FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Opt3(
     ProgressiveArithDecodeState* pState) {
   CJBig2_Image* pImage = pState->pImage->get();
-  JBig2ArithCtx* gbContext = pState->gbContext.Get();
-  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get();
+  JBig2ArithCtx* gbContext = pState->gbContext;
+  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder;
   if (!m_pLine)
     m_pLine = pImage->data();
   int32_t nStride = pImage->stride();
@@ -656,7 +655,7 @@
   for (; m_loopIndex < GBH; m_loopIndex++) {
     if (TPGDON) {
       if (pArithDecoder->IsComplete())
-        return FXCODEC_STATUS_ERROR;
+        return FXCODEC_STATUS::kError;
 
       m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x0795]);
     }
@@ -675,7 +674,7 @@
           uint8_t cVal = 0;
           for (int32_t k = 7; k >= 0; k--) {
             if (pArithDecoder->IsComplete())
-              return FXCODEC_STATUS_ERROR;
+              return FXCODEC_STATUS::kError;
 
             int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
@@ -689,7 +688,7 @@
         uint8_t cVal1 = 0;
         for (int32_t k = 0; k < nBitsLeft; k++) {
           if (pArithDecoder->IsComplete())
-            return FXCODEC_STATUS_ERROR;
+            return FXCODEC_STATUS::kError;
 
           int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
@@ -709,7 +708,7 @@
           uint8_t cVal = 0;
           for (int32_t k = 7; k >= 0; k--) {
             if (pArithDecoder->IsComplete())
-              return FXCODEC_STATUS_ERROR;
+              return FXCODEC_STATUS::kError;
 
             int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
@@ -722,7 +721,7 @@
         uint8_t cVal1 = 0;
         for (int32_t k = 0; k < nBitsLeft; k++) {
           if (pArithDecoder->IsComplete())
-            return FXCODEC_STATUS_ERROR;
+            return FXCODEC_STATUS::kError;
 
           int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
@@ -735,23 +734,23 @@
     m_pLine += nStride;
     if (pState->pPause && pState->pPause->NeedToPauseNow()) {
       m_loopIndex++;
-      m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-      return FXCODEC_STATUS_DECODE_TOBECONTINUE;
+      m_ProgressiveStatus = FXCODEC_STATUS::kDecodeToBeContinued;
+      return FXCODEC_STATUS::kDecodeToBeContinued;
     }
   }
-  m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH;
-  return FXCODEC_STATUS_DECODE_FINISH;
+  m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished;
+  return FXCODEC_STATUS::kDecodeFinished;
 }
 
 FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Unopt(
     ProgressiveArithDecodeState* pState) {
   CJBig2_Image* pImage = pState->pImage->get();
-  JBig2ArithCtx* gbContext = pState->gbContext.Get();
-  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get();
+  JBig2ArithCtx* gbContext = pState->gbContext;
+  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder;
   for (uint32_t h = 0; h < GBH; h++) {
     if (TPGDON) {
       if (pArithDecoder->IsComplete())
-        return FXCODEC_STATUS_ERROR;
+        return FXCODEC_STATUS::kError;
 
       m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x0795]);
     }
@@ -775,7 +774,7 @@
           CONTEXT |= line2 << 4;
           CONTEXT |= line1 << 9;
           if (pArithDecoder->IsComplete())
-            return FXCODEC_STATUS_ERROR;
+            return FXCODEC_STATUS::kError;
 
           bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
         }
@@ -789,19 +788,19 @@
     }
     if (pState->pPause && pState->pPause->NeedToPauseNow()) {
       m_loopIndex++;
-      m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-      return FXCODEC_STATUS_DECODE_TOBECONTINUE;
+      m_ProgressiveStatus = FXCODEC_STATUS::kDecodeToBeContinued;
+      return FXCODEC_STATUS::kDecodeToBeContinued;
     }
   }
-  m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH;
-  return FXCODEC_STATUS_DECODE_FINISH;
+  m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished;
+  return FXCODEC_STATUS::kDecodeFinished;
 }
 
 FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Opt3(
     ProgressiveArithDecodeState* pState) {
   CJBig2_Image* pImage = pState->pImage->get();
-  JBig2ArithCtx* gbContext = pState->gbContext.Get();
-  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get();
+  JBig2ArithCtx* gbContext = pState->gbContext;
+  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder;
   if (!m_pLine)
     m_pLine = pImage->data();
   int32_t nStride = pImage->stride();
@@ -811,7 +810,7 @@
   for (; m_loopIndex < GBH; m_loopIndex++) {
     if (TPGDON) {
       if (pArithDecoder->IsComplete())
-        return FXCODEC_STATUS_ERROR;
+        return FXCODEC_STATUS::kError;
 
       m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x00e5]);
     }
@@ -830,7 +829,7 @@
           uint8_t cVal = 0;
           for (int32_t k = 7; k >= 0; k--) {
             if (pArithDecoder->IsComplete())
-              return FXCODEC_STATUS_ERROR;
+              return FXCODEC_STATUS::kError;
 
             int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
@@ -844,7 +843,7 @@
         uint8_t cVal1 = 0;
         for (int32_t k = 0; k < nBitsLeft; k++) {
           if (pArithDecoder->IsComplete())
-            return FXCODEC_STATUS_ERROR;
+            return FXCODEC_STATUS::kError;
 
           int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
@@ -864,7 +863,7 @@
           uint8_t cVal = 0;
           for (int32_t k = 7; k >= 0; k--) {
             if (pArithDecoder->IsComplete())
-              return FXCODEC_STATUS_ERROR;
+              return FXCODEC_STATUS::kError;
 
             int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
@@ -877,7 +876,7 @@
         uint8_t cVal1 = 0;
         for (int32_t k = 0; k < nBitsLeft; k++) {
           if (pArithDecoder->IsComplete())
-            return FXCODEC_STATUS_ERROR;
+            return FXCODEC_STATUS::kError;
 
           int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
@@ -891,23 +890,23 @@
     if (pState->pPause && m_loopIndex % 50 == 0 &&
         pState->pPause->NeedToPauseNow()) {
       m_loopIndex++;
-      m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-      return FXCODEC_STATUS_DECODE_TOBECONTINUE;
+      m_ProgressiveStatus = FXCODEC_STATUS::kDecodeToBeContinued;
+      return FXCODEC_STATUS::kDecodeToBeContinued;
     }
   }
-  m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH;
-  return FXCODEC_STATUS_DECODE_FINISH;
+  m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished;
+  return FXCODEC_STATUS::kDecodeFinished;
 }
 
 FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Unopt(
     ProgressiveArithDecodeState* pState) {
   CJBig2_Image* pImage = pState->pImage->get();
-  JBig2ArithCtx* gbContext = pState->gbContext.Get();
-  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get();
+  JBig2ArithCtx* gbContext = pState->gbContext;
+  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder;
   for (; m_loopIndex < GBH; m_loopIndex++) {
     if (TPGDON) {
       if (pArithDecoder->IsComplete())
-        return FXCODEC_STATUS_ERROR;
+        return FXCODEC_STATUS::kError;
 
       m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x00e5]);
     }
@@ -929,7 +928,7 @@
           CONTEXT |= line2 << 3;
           CONTEXT |= line1 << 7;
           if (pArithDecoder->IsComplete())
-            return FXCODEC_STATUS_ERROR;
+            return FXCODEC_STATUS::kError;
 
           bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
         }
@@ -945,19 +944,19 @@
     }
     if (pState->pPause && pState->pPause->NeedToPauseNow()) {
       m_loopIndex++;
-      m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-      return FXCODEC_STATUS_DECODE_TOBECONTINUE;
+      m_ProgressiveStatus = FXCODEC_STATUS::kDecodeToBeContinued;
+      return FXCODEC_STATUS::kDecodeToBeContinued;
     }
   }
-  m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH;
-  return FXCODEC_STATUS_DECODE_FINISH;
+  m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished;
+  return FXCODEC_STATUS::kDecodeFinished;
 }
 
 FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Opt3(
     ProgressiveArithDecodeState* pState) {
   CJBig2_Image* pImage = pState->pImage->get();
-  JBig2ArithCtx* gbContext = pState->gbContext.Get();
-  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get();
+  JBig2ArithCtx* gbContext = pState->gbContext;
+  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder;
   if (!m_pLine)
     m_pLine = pImage->data();
   int32_t nStride = pImage->stride();
@@ -966,7 +965,7 @@
   for (; m_loopIndex < GBH; m_loopIndex++) {
     if (TPGDON) {
       if (pArithDecoder->IsComplete())
-        return FXCODEC_STATUS_ERROR;
+        return FXCODEC_STATUS::kError;
 
       m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x0195]);
     }
@@ -982,7 +981,7 @@
           uint8_t cVal = 0;
           for (int32_t k = 7; k >= 0; k--) {
             if (pArithDecoder->IsComplete())
-              return FXCODEC_STATUS_ERROR;
+              return FXCODEC_STATUS::kError;
 
             int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
@@ -995,7 +994,7 @@
         uint8_t cVal1 = 0;
         for (int32_t k = 0; k < nBitsLeft; k++) {
           if (pArithDecoder->IsComplete())
-            return FXCODEC_STATUS_ERROR;
+            return FXCODEC_STATUS::kError;
 
           int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
@@ -1009,7 +1008,7 @@
           uint8_t cVal = 0;
           for (int32_t k = 7; k >= 0; k--) {
             if (pArithDecoder->IsComplete())
-              return FXCODEC_STATUS_ERROR;
+              return FXCODEC_STATUS::kError;
 
             int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
@@ -1020,7 +1019,7 @@
         uint8_t cVal1 = 0;
         for (int32_t k = 0; k < nBitsLeft; k++) {
           if (pArithDecoder->IsComplete())
-            return FXCODEC_STATUS_ERROR;
+            return FXCODEC_STATUS::kError;
 
           int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
@@ -1032,23 +1031,23 @@
     m_pLine += nStride;
     if (pState->pPause && pState->pPause->NeedToPauseNow()) {
       m_loopIndex++;
-      m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-      return FXCODEC_STATUS_DECODE_TOBECONTINUE;
+      m_ProgressiveStatus = FXCODEC_STATUS::kDecodeToBeContinued;
+      return FXCODEC_STATUS::kDecodeToBeContinued;
     }
   }
-  m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH;
-  return FXCODEC_STATUS_DECODE_FINISH;
+  m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished;
+  return FXCODEC_STATUS::kDecodeFinished;
 }
 
 FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Unopt(
     ProgressiveArithDecodeState* pState) {
   CJBig2_Image* pImage = pState->pImage->get();
-  JBig2ArithCtx* gbContext = pState->gbContext.Get();
-  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get();
+  JBig2ArithCtx* gbContext = pState->gbContext;
+  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder;
   for (; m_loopIndex < GBH; m_loopIndex++) {
     if (TPGDON) {
       if (pArithDecoder->IsComplete())
-        return FXCODEC_STATUS_ERROR;
+        return FXCODEC_STATUS::kError;
 
       m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x0195]);
     }
@@ -1067,7 +1066,7 @@
           CONTEXT |= pImage->GetPixel(w + GBAT[0], m_loopIndex + GBAT[1]) << 4;
           CONTEXT |= line1 << 5;
           if (pArithDecoder->IsComplete())
-            return FXCODEC_STATUS_ERROR;
+            return FXCODEC_STATUS::kError;
 
           bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
         }
@@ -1081,10 +1080,10 @@
     }
     if (pState->pPause && pState->pPause->NeedToPauseNow()) {
       m_loopIndex++;
-      m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-      return FXCODEC_STATUS_DECODE_TOBECONTINUE;
+      m_ProgressiveStatus = FXCODEC_STATUS::kDecodeToBeContinued;
+      return FXCODEC_STATUS::kDecodeToBeContinued;
     }
   }
-  m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH;
-  return FXCODEC_STATUS_DECODE_FINISH;
+  m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished;
+  return FXCODEC_STATUS::kDecodeFinished;
 }
diff --git a/core/fxcodec/jbig2/JBig2_GrdProc.h b/core/fxcodec/jbig2/JBig2_GrdProc.h
index af6f223..c543ea1 100644
--- a/core/fxcodec/jbig2/JBig2_GrdProc.h
+++ b/core/fxcodec/jbig2/JBig2_GrdProc.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,14 @@
 #ifndef CORE_FXCODEC_JBIG2_JBIG2_GRDPROC_H_
 #define CORE_FXCODEC_JBIG2_JBIG2_GRDPROC_H_
 
+#include <stdint.h>
+
 #include <memory>
 
 #include "core/fxcodec/fx_codec_def.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 
 class CJBig2_ArithDecoder;
 class CJBig2_BitStream;
@@ -27,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;
@@ -93,8 +95,8 @@
       JBig2ArithCtx* gbContext);
 
   uint32_t m_loopIndex = 0;
-  uint8_t* m_pLine = nullptr;
-  FXCODEC_STATUS m_ProssiveStatus;
+  UNOWNED_PTR_EXCLUSION uint8_t* m_pLine = nullptr;
+  FXCODEC_STATUS m_ProgressiveStatus;
   uint16_t m_DecodeType = 0;
   int m_LTP = 0;
   FX_RECT m_ReplaceRect;
diff --git a/core/fxcodec/jbig2/JBig2_GrrdProc.cpp b/core/fxcodec/jbig2/JBig2_GrrdProc.cpp
index e673776..533fb79 100644
--- a/core/fxcodec/jbig2/JBig2_GrrdProc.cpp
+++ b/core/fxcodec/jbig2/JBig2_GrrdProc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,6 @@
 #include "core/fxcodec/jbig2/JBig2_ArithDecoder.h"
 #include "core/fxcodec/jbig2/JBig2_BitStream.h"
 #include "core/fxcodec/jbig2/JBig2_Image.h"
-#include "third_party/base/ptr_util.h"
 
 CJBig2_GRRDProc::CJBig2_GRRDProc() = default;
 
@@ -21,7 +20,7 @@
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* grContext) {
   if (!CJBig2_Image::IsValidImageSize(GRW, GRH))
-    return pdfium::MakeUnique<CJBig2_Image>(GRW, GRH);
+    return std::make_unique<CJBig2_Image>(GRW, GRH);
 
   if (!GRTEMPLATE) {
     if ((GRAT[0] == -1) && (GRAT[1] == -1) && (GRAT[2] == -1) &&
@@ -41,11 +40,11 @@
 std::unique_ptr<CJBig2_Image> CJBig2_GRRDProc::DecodeTemplate0Unopt(
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* grContext) {
-  auto GRREG = pdfium::MakeUnique<CJBig2_Image>(GRW, GRH);
+  auto GRREG = std::make_unique<CJBig2_Image>(GRW, GRH);
   if (!GRREG->data())
     return nullptr;
 
-  GRREG->Fill(0);
+  GRREG->Fill(false);
   int LTP = 0;
   for (uint32_t h = 0; h < GRH; h++) {
     if (TPGRON) {
@@ -149,7 +148,7 @@
 
   int32_t iGRW = static_cast<int32_t>(GRW);
   int32_t iGRH = static_cast<int32_t>(GRH);
-  auto GRREG = pdfium::MakeUnique<CJBig2_Image>(iGRW, iGRH);
+  auto GRREG = std::make_unique<CJBig2_Image>(iGRW, iGRH);
   if (!GRREG->data())
     return nullptr;
 
@@ -282,11 +281,11 @@
 std::unique_ptr<CJBig2_Image> CJBig2_GRRDProc::DecodeTemplate1Unopt(
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* grContext) {
-  auto GRREG = pdfium::MakeUnique<CJBig2_Image>(GRW, GRH);
+  auto GRREG = std::make_unique<CJBig2_Image>(GRW, GRH);
   if (!GRREG->data())
     return nullptr;
 
-  GRREG->Fill(0);
+  GRREG->Fill(false);
   int LTP = 0;
   for (uint32_t h = 0; h < GRH; h++) {
     if (TPGRON) {
@@ -396,7 +395,7 @@
 
   int32_t iGRW = static_cast<int32_t>(GRW);
   int32_t iGRH = static_cast<int32_t>(GRH);
-  auto GRREG = pdfium::MakeUnique<CJBig2_Image>(iGRW, iGRH);
+  auto GRREG = std::make_unique<CJBig2_Image>(iGRW, iGRH);
   if (!GRREG->data())
     return nullptr;
 
diff --git a/core/fxcodec/jbig2/JBig2_GrrdProc.h b/core/fxcodec/jbig2/JBig2_GrrdProc.h
index c7e3bf9..d1a729b 100644
--- a/core/fxcodec/jbig2/JBig2_GrrdProc.h
+++ b/core/fxcodec/jbig2/JBig2_GrrdProc.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,9 +7,10 @@
 #ifndef CORE_FXCODEC_JBIG2_JBIG2_GRRDPROC_H_
 #define CORE_FXCODEC_JBIG2_JBIG2_GRRDPROC_H_
 
+#include <stdint.h>
+
 #include <memory>
 
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CJBig2_ArithDecoder;
diff --git a/core/fxcodec/jbig2/JBig2_HtrdProc.cpp b/core/fxcodec/jbig2/JBig2_HtrdProc.cpp
index 59fbd89..16a87cc 100644
--- a/core/fxcodec/jbig2/JBig2_HtrdProc.cpp
+++ b/core/fxcodec/jbig2/JBig2_HtrdProc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,10 @@
 #include "core/fxcodec/jbig2/JBig2_BitStream.h"
 #include "core/fxcodec/jbig2/JBig2_GrdProc.h"
 #include "core/fxcodec/jbig2/JBig2_Image.h"
-#include "third_party/base/ptr_util.h"
+
+CJBig2_HTRDProc::CJBig2_HTRDProc() = default;
+
+CJBig2_HTRDProc::~CJBig2_HTRDProc() = default;
 
 std::unique_ptr<CJBig2_Image> CJBig2_HTRDProc::DecodeArith(
     CJBig2_ArithDecoder* pArithDecoder,
@@ -20,7 +23,7 @@
     PauseIndicatorIface* pPause) {
   std::unique_ptr<CJBig2_Image> HSKIP;
   if (HENABLESKIP == 1) {
-    HSKIP = pdfium::MakeUnique<CJBig2_Image>(HGW, HGH);
+    HSKIP = std::make_unique<CJBig2_Image>(HGW, HGH);
     for (uint32_t mg = 0; mg < HGH; ++mg) {
       for (uint32_t ng = 0; ng < HGW; ++ng) {
         int32_t x = (HGX + mg * HRY + ng * HRX) >> 8;
@@ -43,7 +46,7 @@
   GRD.GBW = HGW;
   GRD.GBH = HGH;
   GRD.GBTEMPLATE = HTEMPLATE;
-  GRD.TPGDON = 0;
+  GRD.TPGDON = false;
   GRD.USESKIP = HENABLESKIP;
   GRD.SKIP = HSKIP.get();
   if (HTEMPLATE <= 1)
@@ -71,7 +74,7 @@
     state.pPause = nullptr;
     FXCODEC_STATUS status = GRD.StartDecodeArith(&state);
     state.pPause = pPause;
-    while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE)
+    while (status == FXCODEC_STATUS::kDecodeToBeContinued)
       status = GRD.ContinueDecode(&state);
     if (!pImage)
       return nullptr;
@@ -116,7 +119,7 @@
 
 std::unique_ptr<CJBig2_Image> CJBig2_HTRDProc::DecodeImage(
     const std::vector<std::unique_ptr<CJBig2_Image>>& GSPLANES) {
-  auto HTREG = pdfium::MakeUnique<CJBig2_Image>(HBW, HBH);
+  auto HTREG = std::make_unique<CJBig2_Image>(HBW, HBH);
   if (!HTREG->data())
     return nullptr;
 
diff --git a/core/fxcodec/jbig2/JBig2_HtrdProc.h b/core/fxcodec/jbig2/JBig2_HtrdProc.h
index cd90aae..2073d8b 100644
--- a/core/fxcodec/jbig2/JBig2_HtrdProc.h
+++ b/core/fxcodec/jbig2/JBig2_HtrdProc.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,13 @@
 #ifndef CORE_FXCODEC_JBIG2_JBIG2_HTRDPROC_H_
 #define CORE_FXCODEC_JBIG2_JBIG2_HTRDPROC_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
 #include "core/fxcodec/jbig2/JBig2_Image.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/unowned_ptr.h"
 
 class CJBig2_ArithDecoder;
 class CJBig2_BitStream;
@@ -20,6 +22,9 @@
 
 class CJBig2_HTRDProc {
  public:
+  CJBig2_HTRDProc();
+  ~CJBig2_HTRDProc();
+
   std::unique_ptr<CJBig2_Image> DecodeArith(CJBig2_ArithDecoder* pArithDecoder,
                                             JBig2ArithCtx* gbContext,
                                             PauseIndicatorIface* pPause);
@@ -32,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_HuffmanDecoder.cpp b/core/fxcodec/jbig2/JBig2_HuffmanDecoder.cpp
index bea2b09..7852abe 100644
--- a/core/fxcodec/jbig2/JBig2_HuffmanDecoder.cpp
+++ b/core/fxcodec/jbig2/JBig2_HuffmanDecoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,13 +12,13 @@
 CJBig2_HuffmanDecoder::CJBig2_HuffmanDecoder(CJBig2_BitStream* pStream)
     : m_pStream(pStream) {}
 
-CJBig2_HuffmanDecoder::~CJBig2_HuffmanDecoder() {}
+CJBig2_HuffmanDecoder::~CJBig2_HuffmanDecoder() = default;
 
 int CJBig2_HuffmanDecoder::DecodeAValue(const CJBig2_HuffmanTable* pTable,
                                         int* nResult) {
   FX_SAFE_INT32 nSafeVal = 0;
   int nBits = 0;
-  while (1) {
+  while (true) {
     uint32_t nTmp;
     if (m_pStream->read1Bit(&nTmp) == -1)
       break;
@@ -36,7 +36,7 @@
         continue;
 
       if (pTable->IsHTOOB() && i == pTable->Size() - 1)
-        return JBIG2_OOB;
+        return kJBig2OOB;
 
       if (m_pStream->readNBits(pTable->GetRANGELEN()[i], &nTmp) == -1)
         return -1;
diff --git a/core/fxcodec/jbig2/JBig2_HuffmanDecoder.h b/core/fxcodec/jbig2/JBig2_HuffmanDecoder.h
index 48f8d0f..322c651 100644
--- a/core/fxcodec/jbig2/JBig2_HuffmanDecoder.h
+++ b/core/fxcodec/jbig2/JBig2_HuffmanDecoder.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp b/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp
index 392b718..f59ab4d 100644
--- a/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp
+++ b/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,13 +6,14 @@
 
 #include "core/fxcodec/jbig2/JBig2_HuffmanTable.h"
 
+#include <iterator>
 #include <limits>
-#include <vector>
 
 #include "core/fxcodec/jbig2/JBig2_BitStream.h"
 #include "core/fxcodec/jbig2/JBig2_Context.h"
-#include "core/fxcrt/fx_memory.h"
-#include "third_party/base/numerics/safe_math.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "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;
 };
 
@@ -107,36 +108,36 @@
 
 constexpr HuffmanTable kHuffmanTables[16] = {
     {false, nullptr, 0},  // Zero dummy to preserve indexing.
-    {false, kTableLine1, FX_ArraySize(kTableLine1)},
-    {true, kTableLine2, FX_ArraySize(kTableLine2)},
-    {true, kTableLine3, FX_ArraySize(kTableLine3)},
-    {false, kTableLine4, FX_ArraySize(kTableLine4)},
-    {false, kTableLine5, FX_ArraySize(kTableLine5)},
-    {false, kTableLine6, FX_ArraySize(kTableLine6)},
-    {false, kTableLine7, FX_ArraySize(kTableLine7)},
-    {true, kTableLine8, FX_ArraySize(kTableLine8)},
-    {true, kTableLine9, FX_ArraySize(kTableLine9)},
-    {true, kTableLine10, FX_ArraySize(kTableLine10)},
-    {false, kTableLine11, FX_ArraySize(kTableLine11)},
-    {false, kTableLine12, FX_ArraySize(kTableLine12)},
-    {false, kTableLine13, FX_ArraySize(kTableLine13)},
-    {false, kTableLine14, FX_ArraySize(kTableLine14)},
-    {false, kTableLine15, FX_ArraySize(kTableLine15)}};
+    {false, kTableLine1, std::size(kTableLine1)},
+    {true, kTableLine2, std::size(kTableLine2)},
+    {true, kTableLine3, std::size(kTableLine3)},
+    {false, kTableLine4, std::size(kTableLine4)},
+    {false, kTableLine5, std::size(kTableLine5)},
+    {false, kTableLine6, std::size(kTableLine6)},
+    {false, kTableLine7, std::size(kTableLine7)},
+    {true, kTableLine8, std::size(kTableLine8)},
+    {true, kTableLine9, std::size(kTableLine9)},
+    {true, kTableLine10, std::size(kTableLine10)},
+    {false, kTableLine11, std::size(kTableLine11)},
+    {false, kTableLine12, std::size(kTableLine12)},
+    {false, kTableLine13, std::size(kTableLine13)},
+    {false, kTableLine14, std::size(kTableLine14)},
+    {false, kTableLine15, std::size(kTableLine15)}};
 
 static_assert(CJBig2_HuffmanTable::kNumHuffmanTables ==
-                  FX_ArraySize(kHuffmanTables),
+                  std::size(kHuffmanTables),
               "kNumHuffmanTables must be equal to the size of kHuffmanTables");
 
 }  // namespace
 
 CJBig2_HuffmanTable::CJBig2_HuffmanTable(size_t idx) {
-  ASSERT(idx > 0);
-  ASSERT(idx < kNumHuffmanTables);
+  DCHECK(idx > 0);
+  DCHECK(idx < kNumHuffmanTables);
   const HuffmanTable& table = kHuffmanTables[idx];
   HTOOB = table.HTOOB;
-  NTEMP = table.size;
+  NTEMP = pdfium::base::checked_cast<uint32_t>(table.size);
   m_bOK = ParseFromStandardTable(idx);
-  ASSERT(m_bOK);
+  DCHECK(m_bOK);
 }
 
 CJBig2_HuffmanTable::CJBig2_HuffmanTable(CJBig2_BitStream* pStream)
@@ -144,7 +145,7 @@
   m_bOK = ParseFromCodedBuffer(pStream);
 }
 
-CJBig2_HuffmanTable::~CJBig2_HuffmanTable() {}
+CJBig2_HuffmanTable::~CJBig2_HuffmanTable() = default;
 
 bool CJBig2_HuffmanTable::ParseFromStandardTable(size_t idx) {
   const JBig2TableLine* pTable = kHuffmanTables[idx].lines;
@@ -180,7 +181,7 @@
     return false;
 
   ExtendBuffers(false);
-  pdfium::base::CheckedNumeric<int> cur_low = low;
+  FX_SAFE_INT32 cur_low = low;
   do {
     if ((pStream->readNBits(HTPS, &CODES[NTEMP].codelen) == -1) ||
         (pStream->readNBits(HTRS, &RANGELEN[NTEMP]) == -1) ||
@@ -234,7 +235,7 @@
     return;
 
   size += 16;
-  ASSERT(NTEMP < size);
+  DCHECK(NTEMP < size);
   CODES.resize(size);
   RANGELEN.resize(size);
   RANGELOW.resize(size);
diff --git a/core/fxcodec/jbig2/JBig2_HuffmanTable.h b/core/fxcodec/jbig2/JBig2_HuffmanTable.h
index 351eb9d..8e482f9 100644
--- a/core/fxcodec/jbig2/JBig2_HuffmanTable.h
+++ b/core/fxcodec/jbig2/JBig2_HuffmanTable.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,12 @@
 #ifndef CORE_FXCODEC_JBIG2_JBIG2_HUFFMANTABLE_H_
 #define CORE_FXCODEC_JBIG2_JBIG2_HUFFMANTABLE_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <vector>
 
 #include "core/fxcodec/jbig2/JBig2_Define.h"
-#include "core/fxcrt/fx_system.h"
 
 class CJBig2_BitStream;
 
diff --git a/core/fxcodec/jbig2/JBig2_Image.cpp b/core/fxcodec/jbig2/JBig2_Image.cpp
index 3cfb4f8..773f6ac 100644
--- a/core/fxcodec/jbig2/JBig2_Image.cpp
+++ b/core/fxcodec/jbig2/JBig2_Image.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,7 +15,7 @@
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 #define JBIG2_GETDWORD(buf)                  \
   ((static_cast<uint32_t>((buf)[0]) << 24) | \
@@ -29,14 +29,19 @@
    (buf)[2] = static_cast<uint8_t>((val) >> 8),  \
    (buf)[3] = static_cast<uint8_t>((val) >> 0))
 
-#define BIT_INDEX_TO_BYTE(x) ((x) >> 3)
-#define BIT_INDEX_TO_ALIGNED_BYTE(x) (((x) >> 5) << 2)
-
 namespace {
 
 const int kMaxImagePixels = INT_MAX - 31;
 const int kMaxImageBytes = kMaxImagePixels / 8;
 
+int BitIndexToByte(int index) {
+  return index / 8;
+}
+
+int BitIndexToAlignedByte(int index) {
+  return index / 32 * 4;
+}
+
 }  // namespace
 
 CJBig2_Image::CJBig2_Image(int32_t w, int32_t h) {
@@ -57,7 +62,7 @@
 CJBig2_Image::CJBig2_Image(int32_t w,
                            int32_t h,
                            int32_t stride,
-                           uint8_t* pBuf) {
+                           pdfium::span<uint8_t> pBuf) {
   if (w < 0 || h < 0)
     return;
 
@@ -72,7 +77,7 @@
   m_nWidth = w;
   m_nHeight = h;
   m_nStride = stride;
-  m_pData.Reset(pBuf);
+  m_pData.Reset(pBuf.data());
 }
 
 CJBig2_Image::CJBig2_Image(const CJBig2_Image& other)
@@ -86,12 +91,11 @@
   }
 }
 
-CJBig2_Image::~CJBig2_Image() {}
+CJBig2_Image::~CJBig2_Image() = default;
 
 // static
 bool CJBig2_Image::IsValidImageSize(int32_t w, int32_t h) {
-  return w > 0 && w <= JBIG2_MAX_IMAGE_SIZE && h > 0 &&
-         h <= JBIG2_MAX_IMAGE_SIZE;
+  return w > 0 && w <= kJBig2MaxImageSize && h > 0 && h <= kJBig2MaxImageSize;
 }
 
 int CJBig2_Image::GetPixel(int32_t x, int32_t y) const {
@@ -105,7 +109,7 @@
   if (!pLine)
     return 0;
 
-  int32_t m = BIT_INDEX_TO_BYTE(x);
+  int32_t m = BitIndexToByte(x);
   int32_t n = x & 7;
   return ((pLine[m] >> (7 - n)) & 1);
 }
@@ -121,7 +125,7 @@
   if (!pLine)
     return;
 
-  int32_t m = BIT_INDEX_TO_BYTE(x);
+  int32_t m = BitIndexToByte(x);
   int32_t n = 1 << (7 - (x & 7));
   if (v)
     pLine[m] |= n;
@@ -187,7 +191,7 @@
                                                      int32_t y,
                                                      int32_t w,
                                                      int32_t h) {
-  auto pImage = pdfium::MakeUnique<CJBig2_Image>(w, h);
+  auto pImage = std::make_unique<CJBig2_Image>(w, h);
   if (!pImage->data() || !m_pData)
     return pImage;
 
@@ -208,7 +212,7 @@
                                 int32_t w,
                                 int32_t h,
                                 CJBig2_Image* pImage) {
-  int32_t m = BIT_INDEX_TO_BYTE(x);
+  int32_t m = BitIndexToByte(x);
   int32_t bytes_to_copy = std::min(pImage->m_nStride, m_nStride - m);
   int32_t lines_to_copy = std::min(pImage->m_nHeight, m_nHeight - y);
   for (int32_t j = 0; j < lines_to_copy; j++)
@@ -220,7 +224,7 @@
                                 int32_t w,
                                 int32_t h,
                                 CJBig2_Image* pImage) {
-  int32_t m = BIT_INDEX_TO_ALIGNED_BYTE(x);
+  int32_t m = BitIndexToAlignedByte(x);
   int32_t n = x & 31;
   int32_t bytes_to_copy = std::min(pImage->m_nStride, m_nStride - m);
   int32_t lines_to_copy = std::min(pImage->m_nHeight, m_nHeight - y);
@@ -262,7 +266,7 @@
                                      int32_t y,
                                      JBig2ComposeOp op,
                                      const FX_RECT& rtSrc) {
-  ASSERT(m_pData);
+  DCHECK(m_pData);
 
   // TODO(weili): Check whether the range check is correct. Should x>=1048576?
   if (x < -1048576 || x > 1048576 || y < -1048576 || y > 1048576)
@@ -304,11 +308,11 @@
   uint32_t maskL = 0xffffffff >> d1;
   uint32_t maskR = 0xffffffff << ((32 - (xd1 & 31)) % 32);
   uint32_t maskM = maskL & maskR;
-  const uint8_t* lineSrc = GetLineUnsafe(rtSrc.top + ys0) +
-                           BIT_INDEX_TO_ALIGNED_BYTE(xs0 + rtSrc.left);
+  const uint8_t* lineSrc =
+      GetLineUnsafe(rtSrc.top + ys0) + BitIndexToAlignedByte(xs0 + rtSrc.left);
   const uint8_t* lineSrcEnd = data() + m_nHeight * m_nStride;
-  int32_t lineLeft = m_nStride - BIT_INDEX_TO_ALIGNED_BYTE(xs0);
-  uint8_t* lineDst = pDst->GetLineUnsafe(yd0) + BIT_INDEX_TO_ALIGNED_BYTE(xd0);
+  int32_t lineLeft = m_nStride - BitIndexToAlignedByte(xs0);
+  uint8_t* lineDst = pDst->GetLineUnsafe(yd0) + BitIndexToAlignedByte(xd0);
   if ((xd0 & ~31) == ((xd1 - 1) & ~31)) {
     if ((xs0 & ~31) == ((xs1 - 1) & ~31)) {
       if (s1 > d1) {
@@ -666,5 +670,5 @@
       }
     }
   }
-  return 1;
+  return true;
 }
diff --git a/core/fxcodec/jbig2/JBig2_Image.h b/core/fxcodec/jbig2/JBig2_Image.h
index 2be4ad3..1fe8402 100644
--- a/core/fxcodec/jbig2/JBig2_Image.h
+++ b/core/fxcodec/jbig2/JBig2_Image.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,6 +12,7 @@
 #include "core/fxcodec/jbig2/JBig2_Define.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/maybe_owned.h"
+#include "third_party/base/containers/span.h"
 
 struct FX_RECT;
 
@@ -26,7 +27,10 @@
 class CJBig2_Image {
  public:
   CJBig2_Image(int32_t w, int32_t h);
-  CJBig2_Image(int32_t w, int32_t h, int32_t stride, uint8_t* pBuf);
+  CJBig2_Image(int32_t w,
+               int32_t h,
+               int32_t stride,
+               pdfium::span<uint8_t> pBuf);
   CJBig2_Image(const CJBig2_Image& other);
   ~CJBig2_Image();
 
diff --git a/core/fxcodec/jbig2/JBig2_Image_unittest.cpp b/core/fxcodec/jbig2/JBig2_Image_unittest.cpp
index 13e6eb5..d76ab8a 100644
--- a/core/fxcodec/jbig2/JBig2_Image_unittest.cpp
+++ b/core/fxcodec/jbig2/JBig2_Image_unittest.cpp
@@ -1,14 +1,14 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 // TODO(tsepez) this requires a lot more testing.
 
+#include "core/fxcodec/jbig2/JBig2_Image.h"
+
 #include <stdint.h>
 
-#include "core/fxcodec/jbig2/JBig2_Image.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -75,15 +75,15 @@
   EXPECT_EQ(0, img.GetPixel(kWidthPixels, kHeightLines));
 
   // Out-of-bounds GetLine() returns null.
-  EXPECT_EQ(nullptr, img.GetLine(-1));
-  EXPECT_EQ(nullptr, img.GetLine(kHeightLines));
+  EXPECT_FALSE(img.GetLine(-1));
+  EXPECT_FALSE(img.GetLine(kHeightLines));
 }
 
 TEST(fxcodec, JBig2ImageCreateTooBig) {
   CJBig2_Image img(kWidthPixels, kTooLargeHeightLines);
   EXPECT_EQ(0, img.width());
   EXPECT_EQ(0, img.height());
-  EXPECT_EQ(nullptr, img.data());
+  EXPECT_FALSE(img.data());
 }
 
 TEST(fxcodec, JBig2ImageCreateExternal) {
@@ -102,7 +102,7 @@
   CJBig2_Image img(kWidthPixels, kTooLargeHeightLines, kStrideBytes, buf);
   EXPECT_EQ(0, img.width());
   EXPECT_EQ(0, img.height());
-  EXPECT_EQ(nullptr, img.data());
+  EXPECT_FALSE(img.data());
 }
 
 TEST(fxcodec, JBig2ImageCreateExternalBadStride) {
@@ -110,7 +110,7 @@
   CJBig2_Image img(kWidthPixels, kTooLargeHeightLines, kStrideBytes - 1, buf);
   EXPECT_EQ(0, img.width());
   EXPECT_EQ(0, img.height());
-  EXPECT_EQ(nullptr, img.data());
+  EXPECT_FALSE(img.data());
 }
 
 TEST(fxcodec, JBig2ImageExpand) {
@@ -162,7 +162,7 @@
 }
 
 TEST(fxcodec, JBig2EmptyImage) {
-  auto empty = pdfium::MakeUnique<CJBig2_Image>(0, 0);
+  auto empty = std::make_unique<CJBig2_Image>(0, 0);
 
   // Empty subimage.
   auto sub1 = empty->SubImage(0, 0, 0, 0);
@@ -192,61 +192,55 @@
 }
 
 TEST(fxcodec, JBig2SubImage) {
+  // clang-format off
   // 1-px wide rectangle in image.
-  uint8_t pattern[5][8] = {
-      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+  uint8_t pattern[40] = {
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   };
 
   // 1-px wide rectangle in image, offset 2 in x.
-  uint8_t pattern20[5][8] = {
-      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+  uint8_t pattern20[40] = {
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   };
 
   // 1-px wide rectangle in image, offset 2 in x and y, padded.
-  uint8_t pattern22[5][8] = {
-      {0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+  uint8_t pattern22[40] = {
+      0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   };
 
   // 1-px wide rectangle in image, offset 16 in x, 1 in y, padded.
-  uint8_t pattern161[5][8] = {
-      {0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
-      {0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+  uint8_t pattern161[40] = {
+      0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   };
+  // clang-format on
 
   // Image size a nice clean power of two.
-  auto img32 = pdfium::MakeUnique<CJBig2_Image>(
-      32, 5, 8, reinterpret_cast<uint8_t*>(pattern));
+  auto img32 = std::make_unique<CJBig2_Image>(32, 5, 8, pattern);
 
   // Image size not a nice clean value.
-  auto img37 = pdfium::MakeUnique<CJBig2_Image>(
-      37, 5, 8, reinterpret_cast<uint8_t*>(pattern));
+  auto img37 = std::make_unique<CJBig2_Image>(37, 5, 8, pattern);
 
   // Expected results to check against.
-  auto expected20 = pdfium::MakeUnique<CJBig2_Image>(
-      30, 5, 8, reinterpret_cast<uint8_t*>(pattern20));
-
-  auto expected22 = pdfium::MakeUnique<CJBig2_Image>(
-      30, 5, 8, reinterpret_cast<uint8_t*>(pattern22));
-
-  auto expected161 = pdfium::MakeUnique<CJBig2_Image>(
-      25, 5, 8, reinterpret_cast<uint8_t*>(pattern161));
-
-  auto expected_zeros = pdfium::MakeUnique<CJBig2_Image>(32, 5);
+  auto expected20 = std::make_unique<CJBig2_Image>(30, 5, 8, pattern20);
+  auto expected22 = std::make_unique<CJBig2_Image>(30, 5, 8, pattern22);
+  auto expected161 = std::make_unique<CJBig2_Image>(25, 5, 8, pattern161);
+  auto expected_zeros = std::make_unique<CJBig2_Image>(32, 5);
 
   // Empty subimage.
   auto sub = img32->SubImage(0, 0, 0, 0);
@@ -315,24 +309,23 @@
 }
 
 TEST(fxcodec, JBig2CopyLine) {
+  // clang-format off
   // Horizontal line in image.
-  uint8_t pattern[3][8] = {
-      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+  uint8_t pattern[24] = {
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   };
 
-  uint8_t expected_pattern[3][8] = {
-      {0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
-      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+  uint8_t expected_pattern[24] = {
+      0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   };
+  // clang-format on
 
-  auto img = pdfium::MakeUnique<CJBig2_Image>(
-      37, 3, 8, reinterpret_cast<uint8_t*>(pattern));
-
-  auto expected = pdfium::MakeUnique<CJBig2_Image>(
-      37, 3, 8, reinterpret_cast<uint8_t*>(expected_pattern));
+  auto img = std::make_unique<CJBig2_Image>(37, 3, 8, pattern);
+  auto expected = std::make_unique<CJBig2_Image>(37, 3, 8, expected_pattern);
 
   // Shuffle.
   img->CopyLine(2, 1);
diff --git a/core/fxcodec/jbig2/JBig2_Page.h b/core/fxcodec/jbig2/JBig2_Page.h
index 064b9b3..f41dfba 100644
--- a/core/fxcodec/jbig2/JBig2_Page.h
+++ b/core/fxcodec/jbig2/JBig2_Page.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,15 @@
 #ifndef CORE_FXCODEC_JBIG2_JBIG2_PAGE_H_
 #define CORE_FXCODEC_JBIG2_JBIG2_PAGE_H_
 
-#include "core/fxcrt/fx_system.h"
+#include <stdint.h>
 
 struct JBig2PageInfo {
   uint32_t m_dwWidth;
   uint32_t m_dwHeight;
   uint32_t m_dwResolutionX;
   uint32_t m_dwResolutionY;
-  uint8_t m_cFlags;
+  // Page segment flags, bit 2.
+  bool m_bDefaultPixelValue;
   bool m_bIsStriped;
   uint16_t m_wMaxStripeSize;
 };
diff --git a/core/fxcodec/jbig2/JBig2_PatternDict.cpp b/core/fxcodec/jbig2/JBig2_PatternDict.cpp
index 4e7959f..a125b2e 100644
--- a/core/fxcodec/jbig2/JBig2_PatternDict.cpp
+++ b/core/fxcodec/jbig2/JBig2_PatternDict.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,4 +9,4 @@
 CJBig2_PatternDict::CJBig2_PatternDict(uint32_t dict_size)
     : NUMPATS(dict_size), HDPATS(dict_size) {}
 
-CJBig2_PatternDict::~CJBig2_PatternDict() {}
+CJBig2_PatternDict::~CJBig2_PatternDict() = default;
diff --git a/core/fxcodec/jbig2/JBig2_PatternDict.h b/core/fxcodec/jbig2/JBig2_PatternDict.h
index ca566d0..3260405 100644
--- a/core/fxcodec/jbig2/JBig2_PatternDict.h
+++ b/core/fxcodec/jbig2/JBig2_PatternDict.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxcodec/jbig2/JBig2_PddProc.cpp b/core/fxcodec/jbig2/JBig2_PddProc.cpp
index 9d274f9..c91f591 100644
--- a/core/fxcodec/jbig2/JBig2_PddProc.cpp
+++ b/core/fxcodec/jbig2/JBig2_PddProc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,6 @@
 #include "core/fxcodec/jbig2/JBig2_GrdProc.h"
 #include "core/fxcodec/jbig2/JBig2_Image.h"
 #include "core/fxcodec/jbig2/JBig2_PatternDict.h"
-#include "third_party/base/ptr_util.h"
 
 std::unique_ptr<CJBig2_PatternDict> CJBig2_PDDProc::DecodeArith(
     CJBig2_ArithDecoder* pArithDecoder,
@@ -22,8 +21,8 @@
     return nullptr;
 
   pGRD->GBTEMPLATE = HDTEMPLATE;
-  pGRD->TPGDON = 0;
-  pGRD->USESKIP = 0;
+  pGRD->TPGDON = false;
+  pGRD->USESKIP = false;
   pGRD->GBAT[0] = -1 * static_cast<int32_t>(HDPW);
   pGRD->GBAT[1] = 0;
   if (pGRD->GBTEMPLATE == 0) {
@@ -44,12 +43,12 @@
 
   FXCODEC_STATUS status = pGRD->StartDecodeArith(&state);
   state.pPause = pPause;
-  while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE)
+  while (status == FXCODEC_STATUS::kDecodeToBeContinued)
     status = pGRD->ContinueDecode(&state);
   if (!BHDC)
     return nullptr;
 
-  auto pDict = pdfium::MakeUnique<CJBig2_PatternDict>(GRAYMAX + 1);
+  auto pDict = std::make_unique<CJBig2_PatternDict>(GRAYMAX + 1);
   for (uint32_t GRAY = 0; GRAY <= GRAYMAX; ++GRAY)
     pDict->HDPATS[GRAY] = BHDC->SubImage(HDPW * GRAY, 0, HDPW, HDPH);
   return pDict;
@@ -66,7 +65,7 @@
   if (!BHDC)
     return nullptr;
 
-  auto pDict = pdfium::MakeUnique<CJBig2_PatternDict>(GRAYMAX + 1);
+  auto pDict = std::make_unique<CJBig2_PatternDict>(GRAYMAX + 1);
   for (uint32_t GRAY = 0; GRAY <= GRAYMAX; ++GRAY)
     pDict->HDPATS[GRAY] = BHDC->SubImage(HDPW * GRAY, 0, HDPW, HDPH);
   return pDict;
@@ -75,10 +74,10 @@
 std::unique_ptr<CJBig2_GRDProc> CJBig2_PDDProc::CreateGRDProc() {
   uint32_t width = (GRAYMAX + 1) * HDPW;
   uint32_t height = HDPH;
-  if (width > JBIG2_MAX_IMAGE_SIZE || height > JBIG2_MAX_IMAGE_SIZE)
+  if (width > kJBig2MaxImageSize || height > kJBig2MaxImageSize)
     return nullptr;
 
-  auto pGRD = pdfium::MakeUnique<CJBig2_GRDProc>();
+  auto pGRD = std::make_unique<CJBig2_GRDProc>();
   pGRD->MMR = HDMMR;
   pGRD->GBW = width;
   pGRD->GBH = height;
diff --git a/core/fxcodec/jbig2/JBig2_PddProc.h b/core/fxcodec/jbig2/JBig2_PddProc.h
index be5fadf..b1bd93e 100644
--- a/core/fxcodec/jbig2/JBig2_PddProc.h
+++ b/core/fxcodec/jbig2/JBig2_PddProc.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,9 +7,9 @@
 #ifndef CORE_FXCODEC_JBIG2_JBIG2_PDDPROC_H_
 #define CORE_FXCODEC_JBIG2_JBIG2_PDDPROC_H_
 
-#include <memory>
+#include <stdint.h>
 
-#include "core/fxcrt/fx_system.h"
+#include <memory>
 
 class CJBig2_ArithDecoder;
 class CJBig2_BitStream;
diff --git a/core/fxcodec/jbig2/JBig2_SddProc.cpp b/core/fxcodec/jbig2/JBig2_SddProc.cpp
index 6fd1226..801d9d7 100644
--- a/core/fxcodec/jbig2/JBig2_SddProc.cpp
+++ b/core/fxcodec/jbig2/JBig2_SddProc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -21,7 +21,6 @@
 #include "core/fxcodec/jbig2/JBig2_SymbolDict.h"
 #include "core/fxcodec/jbig2/JBig2_TrdProc.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/ptr_util.h"
 
 CJBig2_SDDProc::CJBig2_SDDProc() = default;
 
@@ -31,57 +30,40 @@
     CJBig2_ArithDecoder* pArithDecoder,
     std::vector<JBig2ArithCtx>* gbContext,
     std::vector<JBig2ArithCtx>* grContext) {
-  std::vector<std::unique_ptr<CJBig2_Image>> SDNEWSYMS;
-  uint32_t HCHEIGHT, NSYMSDECODED;
-  int32_t HCDH;
-  uint32_t SYMWIDTH, TOTWIDTH;
-  int32_t DW;
-  uint32_t I, J, REFAGGNINST;
-  std::vector<bool> EXFLAGS;
-  uint32_t EXINDEX;
-  bool CUREXFLAG;
-  uint32_t EXRUNLENGTH;
-  uint32_t nTmp;
-  uint32_t SBNUMSYMS;
-  uint8_t SBSYMCODELEN;
-  int32_t RDXI, RDYI;
-  uint32_t num_ex_syms;
-  // Pointers are not owned
-  std::vector<CJBig2_Image*> SBSYMS;
-  std::unique_ptr<CJBig2_ArithIaidDecoder> IAID;
-  std::unique_ptr<CJBig2_SymbolDict> pDict;
-  auto IADH = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-  auto IADW = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-  auto IAAI = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-  auto IARDX = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-  auto IARDY = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-  auto IAEX = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-  auto IADT = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-  auto IAFS = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-  auto IADS = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-  auto IAIT = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-  auto IARI = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-  auto IARDW = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-  auto IARDH = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-  nTmp = 0;
-  while ((uint32_t)(1 << nTmp) < (SDNUMINSYMS + SDNUMNEWSYMS)) {
-    nTmp++;
-  }
-  IAID = pdfium::MakeUnique<CJBig2_ArithIaidDecoder>((uint8_t)nTmp);
-  SDNEWSYMS.resize(SDNUMNEWSYMS);
+  auto IADH = std::make_unique<CJBig2_ArithIntDecoder>();
+  auto IADW = std::make_unique<CJBig2_ArithIntDecoder>();
+  auto IAAI = std::make_unique<CJBig2_ArithIntDecoder>();
+  auto IARDX = std::make_unique<CJBig2_ArithIntDecoder>();
+  auto IARDY = std::make_unique<CJBig2_ArithIntDecoder>();
+  auto IAEX = std::make_unique<CJBig2_ArithIntDecoder>();
+  auto IADT = std::make_unique<CJBig2_ArithIntDecoder>();
+  auto IAFS = std::make_unique<CJBig2_ArithIntDecoder>();
+  auto IADS = std::make_unique<CJBig2_ArithIntDecoder>();
+  auto IAIT = std::make_unique<CJBig2_ArithIntDecoder>();
+  auto IARI = std::make_unique<CJBig2_ArithIntDecoder>();
+  auto IARDW = std::make_unique<CJBig2_ArithIntDecoder>();
+  auto IARDH = std::make_unique<CJBig2_ArithIntDecoder>();
 
-  HCHEIGHT = 0;
-  NSYMSDECODED = 0;
+  uint32_t SBSYMCODELENA = 0;
+  while ((uint32_t)(1 << SBSYMCODELENA) < (SDNUMINSYMS + SDNUMNEWSYMS)) {
+    SBSYMCODELENA++;
+  }
+  auto IAID = std::make_unique<CJBig2_ArithIaidDecoder>((uint8_t)SBSYMCODELENA);
+
+  std::vector<std::unique_ptr<CJBig2_Image>> SDNEWSYMS(SDNUMNEWSYMS);
+  uint32_t HCHEIGHT = 0;
+  uint32_t NSYMSDECODED = 0;
   while (NSYMSDECODED < SDNUMNEWSYMS) {
     std::unique_ptr<CJBig2_Image> BS;
+    int32_t HCDH;
     IADH->Decode(pArithDecoder, &HCDH);
     HCHEIGHT = HCHEIGHT + HCDH;
-    if ((int)HCHEIGHT < 0 || (int)HCHEIGHT > JBIG2_MAX_IMAGE_SIZE)
+    if ((int)HCHEIGHT < 0 || (int)HCHEIGHT > kJBig2MaxImageSize)
       return nullptr;
 
-    SYMWIDTH = 0;
-    TOTWIDTH = 0;
+    uint32_t SYMWIDTH = 0;
     for (;;) {
+      int32_t DW;
       if (!IADW->Decode(pArithDecoder, &DW))
         break;
 
@@ -89,24 +71,21 @@
         return nullptr;
 
       SYMWIDTH = SYMWIDTH + DW;
-      if ((int)SYMWIDTH < 0 || (int)SYMWIDTH > JBIG2_MAX_IMAGE_SIZE)
+      if ((int)SYMWIDTH < 0 || (int)SYMWIDTH > kJBig2MaxImageSize)
         return nullptr;
 
       if (HCHEIGHT == 0 || SYMWIDTH == 0) {
-        TOTWIDTH = TOTWIDTH + SYMWIDTH;
-        SDNEWSYMS[NSYMSDECODED] = nullptr;
-        NSYMSDECODED = NSYMSDECODED + 1;
+        ++NSYMSDECODED;
         continue;
       }
-      TOTWIDTH = TOTWIDTH + SYMWIDTH;
       if (SDREFAGG == 0) {
-        auto pGRD = pdfium::MakeUnique<CJBig2_GRDProc>();
-        pGRD->MMR = 0;
+        auto pGRD = std::make_unique<CJBig2_GRDProc>();
+        pGRD->MMR = false;
         pGRD->GBW = SYMWIDTH;
         pGRD->GBH = HCHEIGHT;
         pGRD->GBTEMPLATE = SDTEMPLATE;
-        pGRD->TPGDON = 0;
-        pGRD->USESKIP = 0;
+        pGRD->TPGDON = false;
+        pGRD->USESKIP = false;
         pGRD->GBAT[0] = SDAT[0];
         pGRD->GBAT[1] = SDAT[1];
         pGRD->GBAT[2] = SDAT[2];
@@ -119,40 +98,41 @@
         if (!BS)
           return nullptr;
       } else {
+        uint32_t REFAGGNINST;
         IAAI->Decode(pArithDecoder, (int*)&REFAGGNINST);
         if (REFAGGNINST > 1) {
           // Huffman tables must not outlive |pDecoder|.
-          auto SBHUFFFS = pdfium::MakeUnique<CJBig2_HuffmanTable>(6);
-          auto SBHUFFDS = pdfium::MakeUnique<CJBig2_HuffmanTable>(8);
-          auto SBHUFFDT = pdfium::MakeUnique<CJBig2_HuffmanTable>(11);
-          auto SBHUFFRDW = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
-          auto SBHUFFRDH = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
-          auto SBHUFFRDX = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
-          auto SBHUFFRDY = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
-          auto SBHUFFRSIZE = pdfium::MakeUnique<CJBig2_HuffmanTable>(1);
-          auto pDecoder = pdfium::MakeUnique<CJBig2_TRDProc>();
+          auto SBHUFFFS = std::make_unique<CJBig2_HuffmanTable>(6);
+          auto SBHUFFDS = std::make_unique<CJBig2_HuffmanTable>(8);
+          auto SBHUFFDT = std::make_unique<CJBig2_HuffmanTable>(11);
+          auto SBHUFFRDW = std::make_unique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRDH = std::make_unique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRDX = std::make_unique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRDY = std::make_unique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRSIZE = std::make_unique<CJBig2_HuffmanTable>(1);
+          auto pDecoder = std::make_unique<CJBig2_TRDProc>();
           pDecoder->SBHUFF = SDHUFF;
-          pDecoder->SBREFINE = 1;
+          pDecoder->SBREFINE = true;
           pDecoder->SBW = SYMWIDTH;
           pDecoder->SBH = HCHEIGHT;
           pDecoder->SBNUMINSTANCES = REFAGGNINST;
           pDecoder->SBSTRIPS = 1;
           pDecoder->SBNUMSYMS = SDNUMINSYMS + NSYMSDECODED;
-          SBNUMSYMS = pDecoder->SBNUMSYMS;
-          nTmp = 0;
-          while ((uint32_t)(1 << nTmp) < SBNUMSYMS) {
+          uint32_t nTmp = 0;
+          while ((uint32_t)(1 << nTmp) < pDecoder->SBNUMSYMS) {
             nTmp++;
           }
-          SBSYMCODELEN = (uint8_t)nTmp;
+          uint8_t SBSYMCODELEN = (uint8_t)nTmp;
           pDecoder->SBSYMCODELEN = SBSYMCODELEN;
-          SBSYMS.resize(SBNUMSYMS);
+          std::vector<CJBig2_Image*> SBSYMS;  // Pointers are not owned
+          SBSYMS.resize(pDecoder->SBNUMSYMS);
           std::copy(SDINSYMS, SDINSYMS + SDNUMINSYMS, SBSYMS.begin());
           for (size_t i = 0; i < NSYMSDECODED; ++i)
             SBSYMS[i + SDNUMINSYMS] = SDNEWSYMS[i].get();
           pDecoder->SBSYMS = SBSYMS.data();
-          pDecoder->SBDEFPIXEL = 0;
+          pDecoder->SBDEFPIXEL = false;
           pDecoder->SBCOMBOP = JBIG2_COMPOSE_OR;
-          pDecoder->TRANSPOSED = 0;
+          pDecoder->TRANSPOSED = false;
           pDecoder->REFCORNER = JBIG2_CORNER_TOPLEFT;
           pDecoder->SBDSOFFSET = 0;
           pDecoder->SBHUFFFS = SBHUFFFS.get();
@@ -183,29 +163,29 @@
           if (!BS)
             return nullptr;
         } else if (REFAGGNINST == 1) {
-          SBNUMSYMS = SDNUMINSYMS + NSYMSDECODED;
+          uint32_t SBNUMSYMS = SDNUMINSYMS + NSYMSDECODED;
           uint32_t IDI;
           IAID->Decode(pArithDecoder, &IDI);
-          IARDX->Decode(pArithDecoder, &RDXI);
-          IARDY->Decode(pArithDecoder, &RDYI);
           if (IDI >= SBNUMSYMS)
             return nullptr;
 
-          SBSYMS.resize(SBNUMSYMS);
-          std::copy(SDINSYMS, SDINSYMS + SDNUMINSYMS, SBSYMS.begin());
-          for (size_t i = 0; i < NSYMSDECODED; ++i)
-            SBSYMS[i + SDNUMINSYMS] = SDNEWSYMS[i].get();
-          if (!SBSYMS[IDI])
+          CJBig2_Image* sbsyms_idi = GetImage(IDI, SDNEWSYMS);
+          if (!sbsyms_idi)
             return nullptr;
 
-          auto pGRRD = pdfium::MakeUnique<CJBig2_GRRDProc>();
+          int32_t RDXI;
+          int32_t RDYI;
+          IARDX->Decode(pArithDecoder, &RDXI);
+          IARDY->Decode(pArithDecoder, &RDYI);
+
+          auto pGRRD = std::make_unique<CJBig2_GRRDProc>();
           pGRRD->GRW = SYMWIDTH;
           pGRRD->GRH = HCHEIGHT;
           pGRRD->GRTEMPLATE = SDRTEMPLATE;
-          pGRRD->GRREFERENCE = SBSYMS[IDI];
+          pGRRD->GRREFERENCE = sbsyms_idi;
           pGRRD->GRREFERENCEDX = RDXI;
           pGRRD->GRREFERENCEDY = RDYI;
-          pGRRD->TPGRON = 0;
+          pGRRD->TPGRON = false;
           pGRRD->GRAT[0] = SDRAT[0];
           pGRRD->GRAT[1] = SDRAT[1];
           pGRRD->GRAT[2] = SDRAT[2];
@@ -216,44 +196,46 @@
         }
       }
       SDNEWSYMS[NSYMSDECODED] = std::move(BS);
-      NSYMSDECODED = NSYMSDECODED + 1;
+      ++NSYMSDECODED;
     }
   }
-  EXINDEX = 0;
-  CUREXFLAG = 0;
-  EXFLAGS.resize(SDNUMINSYMS + SDNUMNEWSYMS);
-  num_ex_syms = 0;
-  while (EXINDEX < SDNUMINSYMS + SDNUMNEWSYMS) {
-    IAEX->Decode(pArithDecoder, (int*)&EXRUNLENGTH);
-    if (EXINDEX + EXRUNLENGTH > SDNUMINSYMS + SDNUMNEWSYMS)
-      return nullptr;
 
-    if (EXRUNLENGTH != 0) {
-      for (I = EXINDEX; I < EXINDEX + EXRUNLENGTH; I++) {
-        if (CUREXFLAG)
-          num_ex_syms++;
-        EXFLAGS[I] = CUREXFLAG;
-      }
+  std::vector<bool> EXFLAGS;
+  EXFLAGS.resize(SDNUMINSYMS + SDNUMNEWSYMS);
+  bool CUREXFLAG = false;
+  uint32_t EXINDEX = 0;
+  uint32_t num_ex_syms = 0;
+  while (EXINDEX < SDNUMINSYMS + SDNUMNEWSYMS) {
+    uint32_t EXRUNLENGTH;
+    IAEX->Decode(pArithDecoder, (int*)&EXRUNLENGTH);
+    FX_SAFE_UINT32 new_ex_size = EXINDEX;
+    new_ex_size += EXRUNLENGTH;
+    if (!new_ex_size.IsValid() ||
+        new_ex_size.ValueOrDie() > SDNUMINSYMS + SDNUMNEWSYMS) {
+      return nullptr;
     }
-    EXINDEX = EXINDEX + EXRUNLENGTH;
+
+    if (CUREXFLAG)
+      num_ex_syms += EXRUNLENGTH;
+    std::fill_n(EXFLAGS.begin() + EXINDEX, EXRUNLENGTH, CUREXFLAG);
+    EXINDEX = new_ex_size.ValueOrDie();
     CUREXFLAG = !CUREXFLAG;
   }
   if (num_ex_syms > SDNUMEXSYMS)
     return nullptr;
 
-  pDict = pdfium::MakeUnique<CJBig2_SymbolDict>();
-  J = 0;
-  for (I = 0; I < SDNUMINSYMS + SDNUMNEWSYMS; I++) {
-    if (!EXFLAGS[I] || J >= SDNUMEXSYMS)
+  std::unique_ptr<CJBig2_SymbolDict> pDict =
+      std::make_unique<CJBig2_SymbolDict>();
+  for (uint32_t i = 0, j = 0; i < SDNUMINSYMS + SDNUMNEWSYMS; ++i) {
+    if (!EXFLAGS[i] || j >= SDNUMEXSYMS)
       continue;
-    if (I < SDNUMINSYMS) {
-      pDict->AddImage(SDINSYMS[I]
-                          ? pdfium::MakeUnique<CJBig2_Image>(*SDINSYMS[I])
-                          : nullptr);
+    if (i < SDNUMINSYMS) {
+      pDict->AddImage(SDINSYMS[i] ? std::make_unique<CJBig2_Image>(*SDINSYMS[i])
+                                  : nullptr);
     } else {
-      pDict->AddImage(std::move(SDNEWSYMS[I - SDNUMINSYMS]));
+      pDict->AddImage(std::move(SDNEWSYMS[i - SDNUMINSYMS]));
     }
-    ++J;
+    ++j;
   }
   return pDict;
 }
@@ -262,51 +244,31 @@
     CJBig2_BitStream* pStream,
     std::vector<JBig2ArithCtx>* gbContext,
     std::vector<JBig2ArithCtx>* grContext) {
-  std::vector<std::unique_ptr<CJBig2_Image>> SDNEWSYMS;
+  auto pHuffmanDecoder = std::make_unique<CJBig2_HuffmanDecoder>(pStream);
+
+  std::vector<std::unique_ptr<CJBig2_Image>> SDNEWSYMS(SDNUMNEWSYMS);
   std::vector<uint32_t> SDNEWSYMWIDTHS;
-  uint32_t HCHEIGHT, NSYMSDECODED;
-  int32_t HCDH;
-  uint32_t SYMWIDTH, TOTWIDTH, HCFIRSTSYM;
-  int32_t DW;
-  uint32_t I, J, REFAGGNINST;
-  std::vector<bool> EXFLAGS;
-  uint32_t EXINDEX;
-  bool CUREXFLAG;
-  uint32_t EXRUNLENGTH;
-  int32_t nVal;
-  uint32_t nTmp;
-  uint32_t SBNUMSYMS;
-  uint8_t SBSYMCODELEN;
-  uint32_t IDI;
-  int32_t RDXI, RDYI;
-  uint32_t BMSIZE;
-  uint32_t num_ex_syms;
-  // Pointers are not owned
-  std::vector<CJBig2_Image*> SBSYMS;
-  auto pHuffmanDecoder = pdfium::MakeUnique<CJBig2_HuffmanDecoder>(pStream);
-  SDNEWSYMS.resize(SDNUMNEWSYMS);
   if (SDREFAGG == 0)
     SDNEWSYMWIDTHS.resize(SDNUMNEWSYMS);
-  auto pDict = pdfium::MakeUnique<CJBig2_SymbolDict>();
-  std::unique_ptr<CJBig2_HuffmanTable> pTable;
-
-  HCHEIGHT = 0;
-  NSYMSDECODED = 0;
+  uint32_t HCHEIGHT = 0;
+  uint32_t NSYMSDECODED = 0;
   std::unique_ptr<CJBig2_Image> BS;
   while (NSYMSDECODED < SDNUMNEWSYMS) {
-    if (pHuffmanDecoder->DecodeAValue(SDHUFFDH.Get(), &HCDH) != 0)
+    int32_t HCDH;
+    if (pHuffmanDecoder->DecodeAValue(SDHUFFDH, &HCDH) != 0)
       return nullptr;
 
     HCHEIGHT = HCHEIGHT + HCDH;
-    if ((int)HCHEIGHT < 0 || (int)HCHEIGHT > JBIG2_MAX_IMAGE_SIZE)
+    if ((int)HCHEIGHT < 0 || (int)HCHEIGHT > kJBig2MaxImageSize)
       return nullptr;
 
-    SYMWIDTH = 0;
-    TOTWIDTH = 0;
-    HCFIRSTSYM = NSYMSDECODED;
+    uint32_t SYMWIDTH = 0;
+    uint32_t TOTWIDTH = 0;
+    uint32_t HCFIRSTSYM = NSYMSDECODED;
     for (;;) {
-      nVal = pHuffmanDecoder->DecodeAValue(SDHUFFDW.Get(), &DW);
-      if (nVal == JBIG2_OOB)
+      int32_t DW;
+      int32_t nVal = pHuffmanDecoder->DecodeAValue(SDHUFFDW, &DW);
+      if (nVal == kJBig2OOB)
         break;
       if (nVal != 0)
         return nullptr;
@@ -314,57 +276,57 @@
         return nullptr;
 
       SYMWIDTH = SYMWIDTH + DW;
-      if ((int)SYMWIDTH < 0 || (int)SYMWIDTH > JBIG2_MAX_IMAGE_SIZE)
+      if ((int)SYMWIDTH < 0 || (int)SYMWIDTH > kJBig2MaxImageSize)
         return nullptr;
+
+      TOTWIDTH += SYMWIDTH;
       if (HCHEIGHT == 0 || SYMWIDTH == 0) {
-        TOTWIDTH = TOTWIDTH + SYMWIDTH;
-        SDNEWSYMS[NSYMSDECODED] = nullptr;
-        NSYMSDECODED = NSYMSDECODED + 1;
+        ++NSYMSDECODED;
         continue;
       }
-      TOTWIDTH = TOTWIDTH + SYMWIDTH;
       if (SDREFAGG == 1) {
-        if (pHuffmanDecoder->DecodeAValue(SDHUFFAGGINST.Get(),
-                                          (int*)&REFAGGNINST) != 0) {
+        uint32_t REFAGGNINST;
+        if (pHuffmanDecoder->DecodeAValue(SDHUFFAGGINST, (int*)&REFAGGNINST) !=
+            0) {
           return nullptr;
         }
         BS = nullptr;
         if (REFAGGNINST > 1) {
           // Huffman tables must outlive |pDecoder|.
-          auto SBHUFFFS = pdfium::MakeUnique<CJBig2_HuffmanTable>(6);
-          auto SBHUFFDS = pdfium::MakeUnique<CJBig2_HuffmanTable>(8);
-          auto SBHUFFDT = pdfium::MakeUnique<CJBig2_HuffmanTable>(11);
-          auto SBHUFFRDW = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
-          auto SBHUFFRDH = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
-          auto SBHUFFRDX = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
-          auto SBHUFFRDY = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
-          auto SBHUFFRSIZE = pdfium::MakeUnique<CJBig2_HuffmanTable>(1);
-          auto pDecoder = pdfium::MakeUnique<CJBig2_TRDProc>();
+          auto SBHUFFFS = std::make_unique<CJBig2_HuffmanTable>(6);
+          auto SBHUFFDS = std::make_unique<CJBig2_HuffmanTable>(8);
+          auto SBHUFFDT = std::make_unique<CJBig2_HuffmanTable>(11);
+          auto SBHUFFRDW = std::make_unique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRDH = std::make_unique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRDX = std::make_unique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRDY = std::make_unique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRSIZE = std::make_unique<CJBig2_HuffmanTable>(1);
+          auto pDecoder = std::make_unique<CJBig2_TRDProc>();
           pDecoder->SBHUFF = SDHUFF;
-          pDecoder->SBREFINE = 1;
+          pDecoder->SBREFINE = true;
           pDecoder->SBW = SYMWIDTH;
           pDecoder->SBH = HCHEIGHT;
           pDecoder->SBNUMINSTANCES = REFAGGNINST;
           pDecoder->SBSTRIPS = 1;
           pDecoder->SBNUMSYMS = SDNUMINSYMS + NSYMSDECODED;
-          SBNUMSYMS = pDecoder->SBNUMSYMS;
-          std::vector<JBig2HuffmanCode> SBSYMCODES(SBNUMSYMS);
-          nTmp = 1;
-          while (static_cast<uint32_t>(1 << nTmp) < SBNUMSYMS)
+          std::vector<JBig2HuffmanCode> SBSYMCODES(pDecoder->SBNUMSYMS);
+          uint32_t nTmp = 1;
+          while (static_cast<uint32_t>(1 << nTmp) < pDecoder->SBNUMSYMS)
             ++nTmp;
-          for (I = 0; I < SBNUMSYMS; ++I) {
-            SBSYMCODES[I].codelen = nTmp;
-            SBSYMCODES[I].code = I;
+          for (uint32_t i = 0; i < pDecoder->SBNUMSYMS; ++i) {
+            SBSYMCODES[i].codelen = nTmp;
+            SBSYMCODES[i].code = i;
           }
           pDecoder->SBSYMCODES = std::move(SBSYMCODES);
-          SBSYMS.resize(SBNUMSYMS);
+          std::vector<CJBig2_Image*> SBSYMS;  // Pointers are not owned
+          SBSYMS.resize(pDecoder->SBNUMSYMS);
           std::copy(SDINSYMS, SDINSYMS + SDNUMINSYMS, SBSYMS.begin());
           for (size_t i = 0; i < NSYMSDECODED; ++i)
             SBSYMS[i + SDNUMINSYMS] = SDNEWSYMS[i].get();
           pDecoder->SBSYMS = SBSYMS.data();
-          pDecoder->SBDEFPIXEL = 0;
+          pDecoder->SBDEFPIXEL = false;
           pDecoder->SBCOMBOP = JBIG2_COMPOSE_OR;
-          pDecoder->TRANSPOSED = 0;
+          pDecoder->TRANSPOSED = false;
           pDecoder->REFCORNER = JBIG2_CORNER_TOPLEFT;
           pDecoder->SBDSOFFSET = 0;
           pDecoder->SBHUFFFS = SBHUFFFS.get();
@@ -385,13 +347,14 @@
             return nullptr;
 
         } else if (REFAGGNINST == 1) {
-          SBNUMSYMS = SDNUMINSYMS + SDNUMNEWSYMS;
-          nTmp = 1;
+          uint32_t SBNUMSYMS = SDNUMINSYMS + SDNUMNEWSYMS;
+          uint32_t nTmp = 1;
           while ((uint32_t)(1 << nTmp) < SBNUMSYMS) {
             nTmp++;
           }
-          SBSYMCODELEN = (uint8_t)nTmp;
+          uint8_t SBSYMCODELEN = (uint8_t)nTmp;
           uint32_t uVal = 0;
+          uint32_t IDI;
           for (;;) {
             if (pStream->read1Bit(&nTmp) != 0)
               return nullptr;
@@ -404,32 +367,37 @@
             if (IDI < SBNUMSYMS)
               break;
           }
-          auto SBHUFFRDX = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
-          auto SBHUFFRSIZE = pdfium::MakeUnique<CJBig2_HuffmanTable>(1);
+
+          CJBig2_Image* sbsyms_idi = GetImage(IDI, SDNEWSYMS);
+          if (!sbsyms_idi)
+            return nullptr;
+
+          auto SBHUFFRDX = std::make_unique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRSIZE = std::make_unique<CJBig2_HuffmanTable>(1);
+          int32_t RDXI;
+          int32_t RDYI;
           if ((pHuffmanDecoder->DecodeAValue(SBHUFFRDX.get(), &RDXI) != 0) ||
               (pHuffmanDecoder->DecodeAValue(SBHUFFRDX.get(), &RDYI) != 0) ||
               (pHuffmanDecoder->DecodeAValue(SBHUFFRSIZE.get(), &nVal) != 0)) {
             return nullptr;
           }
+
           pStream->alignByte();
           nTmp = pStream->getOffset();
-          SBSYMS.resize(SBNUMSYMS);
-          std::copy(SDINSYMS, SDINSYMS + SDNUMINSYMS, SBSYMS.begin());
-          for (size_t i = 0; i < NSYMSDECODED; ++i)
-            SBSYMS[i + SDNUMINSYMS] = SDNEWSYMS[i].get();
-          auto pGRRD = pdfium::MakeUnique<CJBig2_GRRDProc>();
+
+          auto pGRRD = std::make_unique<CJBig2_GRRDProc>();
           pGRRD->GRW = SYMWIDTH;
           pGRRD->GRH = HCHEIGHT;
           pGRRD->GRTEMPLATE = SDRTEMPLATE;
-          pGRRD->GRREFERENCE = SBSYMS[IDI];
+          pGRRD->GRREFERENCE = sbsyms_idi;
           pGRRD->GRREFERENCEDX = RDXI;
           pGRRD->GRREFERENCEDY = RDYI;
-          pGRRD->TPGRON = 0;
+          pGRRD->TPGRON = false;
           pGRRD->GRAT[0] = SDRAT[0];
           pGRRD->GRAT[1] = SDRAT[1];
           pGRRD->GRAT[2] = SDRAT[2];
           pGRRD->GRAT[3] = SDRAT[3];
-          auto pArithDecoder = pdfium::MakeUnique<CJBig2_ArithDecoder>(pStream);
+          auto pArithDecoder = std::make_unique<CJBig2_ArithDecoder>(pStream);
           BS = pGRRD->Decode(pArithDecoder.get(), grContext->data());
           if (!BS)
             return nullptr;
@@ -443,89 +411,99 @@
       }
       if (SDREFAGG == 0)
         SDNEWSYMWIDTHS[NSYMSDECODED] = SYMWIDTH;
-      NSYMSDECODED = NSYMSDECODED + 1;
+      ++NSYMSDECODED;
     }
     if (SDREFAGG == 0) {
-      if (pHuffmanDecoder->DecodeAValue(SDHUFFBMSIZE.Get(),
-                                        (int32_t*)&BMSIZE) != 0) {
+      uint32_t BMSIZE;
+      if (pHuffmanDecoder->DecodeAValue(SDHUFFBMSIZE, (int32_t*)&BMSIZE) != 0) {
         return nullptr;
       }
       pStream->alignByte();
       std::unique_ptr<CJBig2_Image> BHC;
       if (BMSIZE == 0) {
-        FX_SAFE_UINT32 safe_stride = TOTWIDTH;
-        safe_stride += 7;
-        safe_stride /= 8;
-        FX_SAFE_UINT32 safe_image_size = safe_stride;
+        if (static_cast<int>(TOTWIDTH) > kJBig2MaxImageSize)
+          return nullptr;
+
+        // OK to not use FX_SAFE_UINT32 to calculate `stride` because
+        // `kJBig2MaxImageSize` is limiting the size.
+        const uint32_t stride = (TOTWIDTH + 7) / 8;
+        FX_SAFE_UINT32 safe_image_size = stride;
         safe_image_size *= HCHEIGHT;
         if (!safe_image_size.IsValid() ||
             pStream->getByteLeft() < safe_image_size.ValueOrDie()) {
           return nullptr;
         }
 
-        const uint32_t stride = safe_stride.ValueOrDie();
-        BHC = pdfium::MakeUnique<CJBig2_Image>(TOTWIDTH, HCHEIGHT);
-        for (I = 0; I < HCHEIGHT; I++) {
-          memcpy(BHC->data() + I * BHC->stride(), pStream->getPointer(),
+        BHC = std::make_unique<CJBig2_Image>(TOTWIDTH, HCHEIGHT);
+        for (uint32_t i = 0; i < HCHEIGHT; ++i) {
+          memcpy(BHC->data() + i * BHC->stride(), pStream->getPointer(),
                  stride);
           pStream->offset(stride);
         }
       } else {
-        auto pGRD = pdfium::MakeUnique<CJBig2_GRDProc>();
-        pGRD->MMR = 1;
+        auto pGRD = std::make_unique<CJBig2_GRDProc>();
+        pGRD->MMR = true;
         pGRD->GBW = TOTWIDTH;
         pGRD->GBH = HCHEIGHT;
         pGRD->StartDecodeMMR(&BHC, pStream);
         pStream->alignByte();
       }
-      nTmp = 0;
       if (!BHC)
         continue;
 
-      for (I = HCFIRSTSYM; I < NSYMSDECODED; ++I) {
-        SDNEWSYMS[I] = BHC->SubImage(nTmp, 0, SDNEWSYMWIDTHS[I], HCHEIGHT);
-        nTmp += SDNEWSYMWIDTHS[I];
+      uint32_t nTmp = 0;
+      for (uint32_t i = HCFIRSTSYM; i < NSYMSDECODED; ++i) {
+        SDNEWSYMS[i] = BHC->SubImage(nTmp, 0, SDNEWSYMWIDTHS[i], HCHEIGHT);
+        nTmp += SDNEWSYMWIDTHS[i];
       }
     }
   }
-  EXINDEX = 0;
-  CUREXFLAG = 0;
-  pTable = pdfium::MakeUnique<CJBig2_HuffmanTable>(1);
+
+  std::unique_ptr<CJBig2_HuffmanTable> pTable =
+      std::make_unique<CJBig2_HuffmanTable>(1);
+  std::vector<bool> EXFLAGS;
   EXFLAGS.resize(SDNUMINSYMS + SDNUMNEWSYMS);
-  num_ex_syms = 0;
+  bool CUREXFLAG = false;
+  uint32_t EXINDEX = 0;
+  uint32_t num_ex_syms = 0;
   while (EXINDEX < SDNUMINSYMS + SDNUMNEWSYMS) {
+    uint32_t EXRUNLENGTH;
     if (pHuffmanDecoder->DecodeAValue(pTable.get(), (int*)&EXRUNLENGTH) != 0)
       return nullptr;
 
-    if (EXINDEX + EXRUNLENGTH > SDNUMINSYMS + SDNUMNEWSYMS)
+    FX_SAFE_UINT32 new_ex_size = EXINDEX;
+    new_ex_size += EXRUNLENGTH;
+    if (!new_ex_size.IsValid() ||
+        new_ex_size.ValueOrDie() > SDNUMINSYMS + SDNUMNEWSYMS) {
       return nullptr;
-
-    if (EXRUNLENGTH != 0) {
-      for (I = EXINDEX; I < EXINDEX + EXRUNLENGTH; ++I) {
-        if (CUREXFLAG)
-          num_ex_syms++;
-
-        EXFLAGS[I] = CUREXFLAG;
-      }
     }
-    EXINDEX = EXINDEX + EXRUNLENGTH;
+
+    if (CUREXFLAG)
+      num_ex_syms += EXRUNLENGTH;
+    std::fill_n(EXFLAGS.begin() + EXINDEX, EXRUNLENGTH, CUREXFLAG);
+    EXINDEX = new_ex_size.ValueOrDie();
     CUREXFLAG = !CUREXFLAG;
   }
   if (num_ex_syms > SDNUMEXSYMS)
     return nullptr;
 
-  J = 0;
-  for (I = 0; I < SDNUMINSYMS + SDNUMNEWSYMS; ++I) {
-    if (!EXFLAGS[I] || J >= SDNUMEXSYMS)
+  auto pDict = std::make_unique<CJBig2_SymbolDict>();
+  for (uint32_t i = 0, j = 0; i < SDNUMINSYMS + SDNUMNEWSYMS; ++i) {
+    if (!EXFLAGS[i] || j >= SDNUMEXSYMS)
       continue;
-    if (I < SDNUMINSYMS) {
-      pDict->AddImage(SDINSYMS[I]
-                          ? pdfium::MakeUnique<CJBig2_Image>(*SDINSYMS[I])
-                          : nullptr);
+    if (i < SDNUMINSYMS) {
+      pDict->AddImage(SDINSYMS[i] ? std::make_unique<CJBig2_Image>(*SDINSYMS[i])
+                                  : nullptr);
     } else {
-      pDict->AddImage(std::move(SDNEWSYMS[I - SDNUMINSYMS]));
+      pDict->AddImage(std::move(SDNEWSYMS[i - SDNUMINSYMS]));
     }
-    ++J;
+    ++j;
   }
   return pDict;
 }
+
+CJBig2_Image* CJBig2_SDDProc::GetImage(
+    uint32_t i,
+    pdfium::span<const std::unique_ptr<CJBig2_Image>> new_syms) const {
+  return i < SDNUMINSYMS ? SDINSYMS[i] : new_syms[i - SDNUMINSYMS].get();
+}
diff --git a/core/fxcodec/jbig2/JBig2_SddProc.h b/core/fxcodec/jbig2/JBig2_SddProc.h
index abef010..a1e7590 100644
--- a/core/fxcodec/jbig2/JBig2_SddProc.h
+++ b/core/fxcodec/jbig2/JBig2_SddProc.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,15 @@
 #ifndef CORE_FXCODEC_JBIG2_JBIG2_SDDPROC_H_
 #define CORE_FXCODEC_JBIG2_JBIG2_SDDPROC_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
 #include "core/fxcodec/jbig2/JBig2_ArithDecoder.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
+#include "third_party/base/containers/span.h"
 
 class CJBig2_BitStream;
 class CJBig2_HuffmanTable;
@@ -41,13 +44,20 @@
   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;
   UnownedPtr<const CJBig2_HuffmanTable> SDHUFFAGGINST;
   int8_t SDAT[8];
   int8_t SDRAT[4];
+
+ private:
+  // Reads from `SDINSYMS` if `i` is in-bounds. Otherwise, reduce `i` by
+  // `SDNUMINSYMS` and read from `new_syms` at the new index.
+  CJBig2_Image* GetImage(
+      uint32_t i,
+      pdfium::span<const std::unique_ptr<CJBig2_Image>> new_syms) const;
 };
 
 #endif  // CORE_FXCODEC_JBIG2_JBIG2_SDDPROC_H_
diff --git a/core/fxcodec/jbig2/JBig2_Segment.cpp b/core/fxcodec/jbig2/JBig2_Segment.cpp
index 91fa2d9..63981b0 100644
--- a/core/fxcodec/jbig2/JBig2_Segment.cpp
+++ b/core/fxcodec/jbig2/JBig2_Segment.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,17 +6,6 @@
 
 #include "core/fxcodec/jbig2/JBig2_Segment.h"
 
-CJBig2_Segment::CJBig2_Segment()
-    : m_dwNumber(0),
-      m_nReferred_to_segment_count(0),
-      m_dwPage_association(0),
-      m_dwData_length(0),
-      m_dwHeader_Length(0),
-      m_dwObjNum(0),
-      m_dwDataOffset(0),
-      m_State(JBIG2_SEGMENT_HEADER_UNPARSED),
-      m_nResultType(JBIG2_VOID_POINTER) {
-  m_cFlags.c = 0;
-}
+CJBig2_Segment::CJBig2_Segment() = default;
 
-CJBig2_Segment::~CJBig2_Segment() {}
+CJBig2_Segment::~CJBig2_Segment() = default;
diff --git a/core/fxcodec/jbig2/JBig2_Segment.h b/core/fxcodec/jbig2/JBig2_Segment.h
index 45e2a3d..f588f0b 100644
--- a/core/fxcodec/jbig2/JBig2_Segment.h
+++ b/core/fxcodec/jbig2/JBig2_Segment.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -36,25 +36,24 @@
   CJBig2_Segment();
   ~CJBig2_Segment();
 
-  uint32_t m_dwNumber;
+  uint32_t m_dwNumber = 0;
   union {
     struct {
       uint8_t type : 6;
       uint8_t page_association_size : 1;
       uint8_t deferred_non_retain : 1;
     } s;
-    uint8_t c;
+    uint8_t c = 0;
   } m_cFlags;
-  int32_t m_nReferred_to_segment_count;
+  int32_t m_nReferred_to_segment_count = 0;
   std::vector<uint32_t> m_Referred_to_segment_numbers;
-  uint32_t m_dwPage_association;
-  uint32_t m_dwData_length;
-
-  uint32_t m_dwHeader_Length;
-  uint32_t m_dwObjNum;
-  uint32_t m_dwDataOffset;
-  JBig2_SegmentState m_State;
-  JBig2_ResultType m_nResultType;
+  uint32_t m_dwPage_association = 0;
+  uint32_t m_dwData_length = 0;
+  uint32_t m_dwHeader_Length = 0;
+  uint32_t m_dwDataOffset = 0;
+  uint64_t m_Key = 0;
+  JBig2_SegmentState m_State = JBIG2_SEGMENT_HEADER_UNPARSED;
+  JBig2_ResultType m_nResultType = JBIG2_VOID_POINTER;
   std::unique_ptr<CJBig2_SymbolDict> m_SymbolDict;
   std::unique_ptr<CJBig2_PatternDict> m_PatternDict;
   std::unique_ptr<CJBig2_Image> m_Image;
diff --git a/core/fxcodec/jbig2/JBig2_SymbolDict.cpp b/core/fxcodec/jbig2/JBig2_SymbolDict.cpp
index a6fafb4..431441d 100644
--- a/core/fxcodec/jbig2/JBig2_SymbolDict.cpp
+++ b/core/fxcodec/jbig2/JBig2_SymbolDict.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,15 @@
 #include "core/fxcodec/jbig2/JBig2_SymbolDict.h"
 
 #include "core/fxcodec/jbig2/JBig2_Image.h"
-#include "third_party/base/ptr_util.h"
 
 CJBig2_SymbolDict::CJBig2_SymbolDict() {}
 
-CJBig2_SymbolDict::~CJBig2_SymbolDict() {}
+CJBig2_SymbolDict::~CJBig2_SymbolDict() = default;
 
 std::unique_ptr<CJBig2_SymbolDict> CJBig2_SymbolDict::DeepCopy() const {
-  auto dst = pdfium::MakeUnique<CJBig2_SymbolDict>();
+  auto dst = std::make_unique<CJBig2_SymbolDict>();
   for (const auto& image : m_SDEXSYMS) {
-    dst->m_SDEXSYMS.push_back(image ? pdfium::MakeUnique<CJBig2_Image>(*image)
+    dst->m_SDEXSYMS.push_back(image ? std::make_unique<CJBig2_Image>(*image)
                                     : nullptr);
   }
   dst->m_gbContext = m_gbContext;
diff --git a/core/fxcodec/jbig2/JBig2_SymbolDict.h b/core/fxcodec/jbig2/JBig2_SymbolDict.h
index 956c83b..ffdd875 100644
--- a/core/fxcodec/jbig2/JBig2_SymbolDict.h
+++ b/core/fxcodec/jbig2/JBig2_SymbolDict.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxcodec/jbig2/JBig2_TrdProc.cpp b/core/fxcodec/jbig2/JBig2_TrdProc.cpp
index 7aa5989..0ea171b 100644
--- a/core/fxcodec/jbig2/JBig2_TrdProc.cpp
+++ b/core/fxcodec/jbig2/JBig2_TrdProc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,27 +14,26 @@
 #include "core/fxcodec/jbig2/JBig2_HuffmanDecoder.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/maybe_owned.h"
-#include "third_party/base/optional.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace {
 
-Optional<uint32_t> CheckTRDDimension(uint32_t dimension, int32_t delta) {
+absl::optional<uint32_t> CheckTRDDimension(uint32_t dimension, int32_t delta) {
   FX_SAFE_UINT32 result = dimension;
   result += delta;
   if (!result.IsValid())
-    return {};
-  return {result.ValueOrDie()};
+    return absl::nullopt;
+  return result.ValueOrDie();
 }
 
-Optional<int32_t> CheckTRDReferenceDimension(int32_t dimension,
-                                             uint32_t shift,
-                                             int32_t offset) {
+absl::optional<int32_t> CheckTRDReferenceDimension(int32_t dimension,
+                                                   uint32_t shift,
+                                                   int32_t offset) {
   FX_SAFE_INT32 result = offset;
   result += dimension >> shift;
   if (!result.IsValid())
-    return {};
-  return {result.ValueOrDie()};
+    return absl::nullopt;
+  return result.ValueOrDie();
 }
 
 }  // namespace
@@ -50,14 +49,14 @@
 std::unique_ptr<CJBig2_Image> CJBig2_TRDProc::DecodeHuffman(
     CJBig2_BitStream* pStream,
     JBig2ArithCtx* grContext) {
-  auto SBREG = pdfium::MakeUnique<CJBig2_Image>(SBW, SBH);
+  auto SBREG = std::make_unique<CJBig2_Image>(SBW, SBH);
   if (!SBREG->data())
     return nullptr;
 
   SBREG->Fill(SBDEFPIXEL);
   int32_t INITIAL_STRIPT;
-  auto pHuffmanDecoder = pdfium::MakeUnique<CJBig2_HuffmanDecoder>(pStream);
-  if (pHuffmanDecoder->DecodeAValue(SBHUFFDT.Get(), &INITIAL_STRIPT) != 0)
+  auto pHuffmanDecoder = std::make_unique<CJBig2_HuffmanDecoder>(pStream);
+  if (pHuffmanDecoder->DecodeAValue(SBHUFFDT, &INITIAL_STRIPT) != 0)
     return nullptr;
 
   FX_SAFE_INT32 STRIPT = INITIAL_STRIPT;
@@ -67,7 +66,7 @@
   uint32_t NINSTANCES = 0;
   while (NINSTANCES < SBNUMINSTANCES) {
     int32_t INITIAL_DT;
-    if (pHuffmanDecoder->DecodeAValue(SBHUFFDT.Get(), &INITIAL_DT) != 0)
+    if (pHuffmanDecoder->DecodeAValue(SBHUFFDT, &INITIAL_DT) != 0)
       return nullptr;
 
     FX_SAFE_INT32 DT = INITIAL_DT;
@@ -78,7 +77,7 @@
     for (;;) {
       if (bFirst) {
         int32_t DFS;
-        if (pHuffmanDecoder->DecodeAValue(SBHUFFFS.Get(), &DFS) != 0)
+        if (pHuffmanDecoder->DecodeAValue(SBHUFFFS, &DFS) != 0)
           return nullptr;
 
         FIRSTS += DFS;
@@ -86,8 +85,8 @@
         bFirst = false;
       } else {
         int32_t IDS;
-        int32_t nVal = pHuffmanDecoder->DecodeAValue(SBHUFFDS.Get(), &IDS);
-        if (nVal == JBIG2_OOB)
+        int32_t nVal = pHuffmanDecoder->DecodeAValue(SBHUFFDS, &IDS);
+        if (nVal == kJBig2OOB)
           break;
 
         if (nVal != 0)
@@ -134,7 +133,7 @@
         if (IDI < SBNUMSYMS)
           break;
       }
-      bool RI = 0;
+      bool RI = false;
       if (SBREFINE != 0 && pStream->read1Bit(&RI) != 0)
         return nullptr;
 
@@ -147,12 +146,11 @@
         int32_t RDXI;
         int32_t RDYI;
         int32_t HUFFRSIZE;
-        if ((pHuffmanDecoder->DecodeAValue(SBHUFFRDW.Get(), &RDWI) != 0) ||
-            (pHuffmanDecoder->DecodeAValue(SBHUFFRDH.Get(), &RDHI) != 0) ||
-            (pHuffmanDecoder->DecodeAValue(SBHUFFRDX.Get(), &RDXI) != 0) ||
-            (pHuffmanDecoder->DecodeAValue(SBHUFFRDY.Get(), &RDYI) != 0) ||
-            (pHuffmanDecoder->DecodeAValue(SBHUFFRSIZE.Get(), &HUFFRSIZE) !=
-             0)) {
+        if ((pHuffmanDecoder->DecodeAValue(SBHUFFRDW, &RDWI) != 0) ||
+            (pHuffmanDecoder->DecodeAValue(SBHUFFRDH, &RDHI) != 0) ||
+            (pHuffmanDecoder->DecodeAValue(SBHUFFRDX, &RDXI) != 0) ||
+            (pHuffmanDecoder->DecodeAValue(SBHUFFRDY, &RDYI) != 0) ||
+            (pHuffmanDecoder->DecodeAValue(SBHUFFRSIZE, &HUFFRSIZE) != 0)) {
           return nullptr;
         }
         pStream->alignByte();
@@ -161,32 +159,32 @@
         if (!IBOI)
           return nullptr;
 
-        Optional<uint32_t> WOI = CheckTRDDimension(IBOI->width(), RDWI);
-        Optional<uint32_t> HOI = CheckTRDDimension(IBOI->height(), RDHI);
-        if (!WOI || !HOI)
+        absl::optional<uint32_t> WOI = CheckTRDDimension(IBOI->width(), RDWI);
+        absl::optional<uint32_t> HOI = CheckTRDDimension(IBOI->height(), RDHI);
+        if (!WOI.has_value() || !HOI.has_value())
           return nullptr;
 
-        Optional<int32_t> GRREFERENCEDX =
+        absl::optional<int32_t> GRREFERENCEDX =
             CheckTRDReferenceDimension(RDWI, 2, RDXI);
-        Optional<int32_t> GRREFERENCEDY =
+        absl::optional<int32_t> GRREFERENCEDY =
             CheckTRDReferenceDimension(RDHI, 2, RDYI);
-        if (!GRREFERENCEDX || !GRREFERENCEDY)
+        if (!GRREFERENCEDX.has_value() || !GRREFERENCEDY.has_value())
           return nullptr;
 
-        auto pGRRD = pdfium::MakeUnique<CJBig2_GRRDProc>();
+        auto pGRRD = std::make_unique<CJBig2_GRRDProc>();
         pGRRD->GRW = WOI.value();
         pGRRD->GRH = HOI.value();
         pGRRD->GRTEMPLATE = SBRTEMPLATE;
         pGRRD->GRREFERENCE = IBOI;
         pGRRD->GRREFERENCEDX = GRREFERENCEDX.value();
         pGRRD->GRREFERENCEDY = GRREFERENCEDY.value();
-        pGRRD->TPGRON = 0;
+        pGRRD->TPGRON = false;
         pGRRD->GRAT[0] = SBRAT[0];
         pGRRD->GRAT[1] = SBRAT[1];
         pGRRD->GRAT[2] = SBRAT[2];
         pGRRD->GRAT[3] = SBRAT[3];
 
-        auto pArithDecoder = pdfium::MakeUnique<CJBig2_ArithDecoder>(pStream);
+        auto pArithDecoder = std::make_unique<CJBig2_ArithDecoder>(pStream);
         IBI = pGRRD->Decode(pArithDecoder.get(), grContext);
         if (!IBI)
           return nullptr;
@@ -226,7 +224,7 @@
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* grContext,
     JBig2IntDecoderState* pIDS) {
-  auto SBREG = pdfium::MakeUnique<CJBig2_Image>(SBW, SBH);
+  auto SBREG = std::make_unique<CJBig2_Image>(SBW, SBH);
   if (!SBREG->data())
     return nullptr;
 
@@ -234,7 +232,7 @@
   if (pIDS)
     pIADT = pIDS->IADT;
   else
-    pIADT = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
+    pIADT = std::make_unique<CJBig2_ArithIntDecoder>();
   int32_t INITIAL_STRIPT;
   if (!pIADT->Decode(pArithDecoder, &INITIAL_STRIPT))
     return nullptr;
@@ -259,15 +257,15 @@
     pIARDY = pIDS->IARDY;
     pIAID = pIDS->IAID;
   } else {
-    pIAFS = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-    pIADS = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-    pIAIT = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-    pIARI = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-    pIARDW = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-    pIARDH = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-    pIARDX = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-    pIARDY = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
-    pIAID = pdfium::MakeUnique<CJBig2_ArithIaidDecoder>(SBSYMCODELEN);
+    pIAFS = std::make_unique<CJBig2_ArithIntDecoder>();
+    pIADS = std::make_unique<CJBig2_ArithIntDecoder>();
+    pIAIT = std::make_unique<CJBig2_ArithIntDecoder>();
+    pIARI = std::make_unique<CJBig2_ArithIntDecoder>();
+    pIARDW = std::make_unique<CJBig2_ArithIntDecoder>();
+    pIARDH = std::make_unique<CJBig2_ArithIntDecoder>();
+    pIARDX = std::make_unique<CJBig2_ArithIntDecoder>();
+    pIARDY = std::make_unique<CJBig2_ArithIntDecoder>();
+    pIAID = std::make_unique<CJBig2_ArithIaidDecoder>(SBSYMCODELEN);
   }
 
   SBREG->Fill(SBDEFPIXEL);
@@ -341,26 +339,26 @@
         if (!IBOI)
           return nullptr;
 
-        Optional<uint32_t> WOI = CheckTRDDimension(IBOI->width(), RDWI);
-        Optional<uint32_t> HOI = CheckTRDDimension(IBOI->height(), RDHI);
-        if (!WOI || !HOI)
+        absl::optional<uint32_t> WOI = CheckTRDDimension(IBOI->width(), RDWI);
+        absl::optional<uint32_t> HOI = CheckTRDDimension(IBOI->height(), RDHI);
+        if (!WOI.has_value() || !HOI.has_value())
           return nullptr;
 
-        Optional<int32_t> GRREFERENCEDX =
+        absl::optional<int32_t> GRREFERENCEDX =
             CheckTRDReferenceDimension(RDWI, 1, RDXI);
-        Optional<int32_t> GRREFERENCEDY =
+        absl::optional<int32_t> GRREFERENCEDY =
             CheckTRDReferenceDimension(RDHI, 1, RDYI);
-        if (!GRREFERENCEDX || !GRREFERENCEDY)
+        if (!GRREFERENCEDX.has_value() || !GRREFERENCEDY.has_value())
           return nullptr;
 
-        auto pGRRD = pdfium::MakeUnique<CJBig2_GRRDProc>();
+        auto pGRRD = std::make_unique<CJBig2_GRRDProc>();
         pGRRD->GRW = WOI.value();
         pGRRD->GRH = HOI.value();
         pGRRD->GRTEMPLATE = SBRTEMPLATE;
         pGRRD->GRREFERENCE = IBOI;
         pGRRD->GRREFERENCEDX = GRREFERENCEDX.value();
         pGRRD->GRREFERENCEDY = GRREFERENCEDY.value();
-        pGRRD->TPGRON = 0;
+        pGRRD->TPGRON = false;
         pGRRD->GRAT[0] = SBRAT[0];
         pGRRD->GRAT[1] = SBRAT[1];
         pGRRD->GRAT[2] = SBRAT[2];
diff --git a/core/fxcodec/jbig2/JBig2_TrdProc.h b/core/fxcodec/jbig2/JBig2_TrdProc.h
index 8f4e42d..bd887d9 100644
--- a/core/fxcodec/jbig2/JBig2_TrdProc.h
+++ b/core/fxcodec/jbig2/JBig2_TrdProc.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,14 @@
 #ifndef CORE_FXCODEC_JBIG2_JBIG2_TRDPROC_H_
 #define CORE_FXCODEC_JBIG2_JBIG2_TRDPROC_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
 #include "core/fxcodec/jbig2/JBig2_Image.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 
 class CJBig2_ArithDecoder;
 class CJBig2_ArithIaidDecoder;
@@ -70,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.cpp b/core/fxcodec/jbig2/jbig2_decoder.cpp
new file mode 100644
index 0000000..0b95382
--- /dev/null
+++ b/core/fxcodec/jbig2/jbig2_decoder.cpp
@@ -0,0 +1,78 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/jbig2/jbig2_decoder.h"
+
+#include <string.h>
+
+#include "core/fxcodec/jbig2/JBig2_Context.h"
+#include "core/fxcodec/jbig2/JBig2_DocumentContext.h"
+#include "core/fxcrt/span_util.h"
+
+namespace fxcodec {
+
+namespace {
+
+FXCODEC_STATUS Decode(Jbig2Context* pJbig2Context, bool decode_success) {
+  FXCODEC_STATUS status = pJbig2Context->m_pContext->GetProcessingStatus();
+  if (status != FXCODEC_STATUS::kDecodeFinished)
+    return status;
+
+  pJbig2Context->m_pContext.reset();
+  if (!decode_success)
+    return FXCODEC_STATUS::kError;
+
+  int dword_size = pJbig2Context->m_height * pJbig2Context->m_dest_pitch / 4;
+  uint32_t* dword_buf = reinterpret_cast<uint32_t*>(pJbig2Context->m_dest_buf);
+  for (int i = 0; i < dword_size; i++)
+    dword_buf[i] = ~dword_buf[i];
+  return FXCODEC_STATUS::kDecodeFinished;
+}
+
+}  // namespace
+
+Jbig2Context::Jbig2Context() = default;
+
+Jbig2Context::~Jbig2Context() = default;
+
+// static
+FXCODEC_STATUS Jbig2Decoder::StartDecode(
+    Jbig2Context* pJbig2Context,
+    JBig2_DocumentContext* pJBig2DocumentContext,
+    uint32_t width,
+    uint32_t height,
+    pdfium::span<const uint8_t> src_span,
+    uint64_t src_key,
+    pdfium::span<const uint8_t> global_span,
+    uint64_t global_key,
+    pdfium::span<uint8_t> dest_buf,
+    uint32_t dest_pitch,
+    PauseIndicatorIface* pPause) {
+  pJbig2Context->m_width = width;
+  pJbig2Context->m_height = height;
+  pJbig2Context->m_pSrcSpan = src_span;
+  pJbig2Context->m_nSrcKey = src_key;
+  pJbig2Context->m_pGlobalSpan = global_span;
+  pJbig2Context->m_nGlobalKey = global_key;
+  pJbig2Context->m_dest_buf = dest_buf.data();
+  pJbig2Context->m_dest_pitch = dest_pitch;
+  fxcrt::spanset(dest_buf.first(height * dest_pitch), 0);
+  pJbig2Context->m_pContext =
+      CJBig2_Context::Create(global_span, global_key, src_span, src_key,
+                             pJBig2DocumentContext->GetSymbolDictCache());
+  bool succeeded = pJbig2Context->m_pContext->GetFirstPage(
+      dest_buf, width, height, dest_pitch, pPause);
+  return Decode(pJbig2Context, succeeded);
+}
+
+// static
+FXCODEC_STATUS Jbig2Decoder::ContinueDecode(Jbig2Context* pJbig2Context,
+                                            PauseIndicatorIface* pPause) {
+  bool succeeded = pJbig2Context->m_pContext->Continue(pPause);
+  return Decode(pJbig2Context, succeeded);
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/jbig2/jbig2_decoder.h b/core/fxcodec/jbig2/jbig2_decoder.h
new file mode 100644
index 0000000..745fa2e
--- /dev/null
+++ b/core/fxcodec/jbig2/jbig2_decoder.h
@@ -0,0 +1,66 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_JBIG2_JBIG2_DECODER_H_
+#define CORE_FXCODEC_JBIG2_JBIG2_DECODER_H_
+
+#include <memory>
+
+#include "core/fxcodec/fx_codec_def.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
+#include "third_party/base/containers/span.h"
+
+class CJBig2_Context;
+class JBig2_DocumentContext;
+class PauseIndicatorIface;
+
+namespace fxcodec {
+
+class Jbig2Context {
+ public:
+  Jbig2Context();
+  ~Jbig2Context();
+
+  uint32_t m_width = 0;
+  uint32_t m_height = 0;
+  uint64_t m_nGlobalKey = 0;
+  uint64_t m_nSrcKey = 0;
+  pdfium::span<const uint8_t> m_pGlobalSpan;
+  pdfium::span<const uint8_t> m_pSrcSpan;
+  UNOWNED_PTR_EXCLUSION uint8_t* m_dest_buf = nullptr;
+  uint32_t m_dest_pitch = 0;
+  std::unique_ptr<CJBig2_Context> m_pContext;
+};
+
+class Jbig2Decoder {
+ public:
+  static FXCODEC_STATUS StartDecode(
+      Jbig2Context* pJbig2Context,
+      JBig2_DocumentContext* pJbig2DocumentContext,
+      uint32_t width,
+      uint32_t height,
+      pdfium::span<const uint8_t> src_span,
+      uint64_t src_key,
+      pdfium::span<const uint8_t> global_span,
+      uint64_t global_key,
+      pdfium::span<uint8_t> dest_buf,
+      uint32_t dest_pitch,
+      PauseIndicatorIface* pPause);
+
+  static FXCODEC_STATUS ContinueDecode(Jbig2Context* pJbig2Context,
+                                       PauseIndicatorIface* pPause);
+
+  Jbig2Decoder() = delete;
+  Jbig2Decoder(const Jbig2Decoder&) = delete;
+  Jbig2Decoder& operator=(const Jbig2Decoder&) = delete;
+};
+
+}  // namespace fxcodec
+
+using Jbig2Context = fxcodec::Jbig2Context;
+using Jbig2Decoder = fxcodec::Jbig2Decoder;
+
+#endif  // CORE_FXCODEC_JBIG2_JBIG2_DECODER_H_
diff --git a/core/fxcodec/jbig2/jbig2_embeddertest.cpp b/core/fxcodec/jbig2/jbig2_embeddertest.cpp
index e2523fc..1b656fd 100644
--- a/core/fxcodec/jbig2/jbig2_embeddertest.cpp
+++ b/core/fxcodec/jbig2/jbig2_embeddertest.cpp
@@ -1,9 +1,7 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
-
 #include "public/cpp/fpdf_scopers.h"
 #include "testing/embedder_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -19,10 +17,10 @@
 TEST_F(JBig2EmbedderTest, MAYBE_Bug_631912) {
   // Test jbig2 image in PDF file can be loaded successfully.
   // Should not crash.
-  EXPECT_TRUE(OpenDocument("bug_631912.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_631912.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-  CompareBitmap(bitmap.get(), 691, 432, "24d75af646f8772c5ee7ced260452ae4");
+  CompareBitmap(bitmap.get(), 691, 432, "726c2b8c89df0ab40627322d1dddd521");
   UnloadPage(page);
 }
diff --git a/core/fxcodec/jbig2/jbig2module.cpp b/core/fxcodec/jbig2/jbig2module.cpp
deleted file mode 100644
index 6f99143..0000000
--- a/core/fxcodec/jbig2/jbig2module.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/jbig2/jbig2module.h"
-
-#include "core/fxcodec/jbig2/JBig2_Context.h"
-#include "core/fxcodec/jbig2/JBig2_DocumentContext.h"
-#include "third_party/base/ptr_util.h"
-
-namespace fxcodec {
-
-JBig2_DocumentContext* GetJBig2DocumentContext(
-    std::unique_ptr<JBig2_DocumentContext>* pContextHolder) {
-  if (!*pContextHolder)
-    *pContextHolder = pdfium::MakeUnique<JBig2_DocumentContext>();
-  return pContextHolder->get();
-}
-
-Jbig2Context::Jbig2Context() = default;
-
-Jbig2Context::~Jbig2Context() = default;
-
-Jbig2Module::Jbig2Module() = default;
-
-Jbig2Module::~Jbig2Module() = default;
-
-FXCODEC_STATUS Jbig2Module::StartDecode(
-    Jbig2Context* pJbig2Context,
-    std::unique_ptr<JBig2_DocumentContext>* pContextHolder,
-    uint32_t width,
-    uint32_t height,
-    pdfium::span<const uint8_t> src_span,
-    uint32_t src_objnum,
-    pdfium::span<const uint8_t> global_span,
-    uint32_t global_objnum,
-    uint8_t* dest_buf,
-    uint32_t dest_pitch,
-    PauseIndicatorIface* pPause) {
-  if (!pJbig2Context)
-    return FXCODEC_STATUS_ERR_PARAMS;
-
-  JBig2_DocumentContext* pJBig2DocumentContext =
-      GetJBig2DocumentContext(pContextHolder);
-  pJbig2Context->m_width = width;
-  pJbig2Context->m_height = height;
-  pJbig2Context->m_pSrcSpan = src_span;
-  pJbig2Context->m_nSrcObjNum = src_objnum;
-  pJbig2Context->m_pGlobalSpan = global_span;
-  pJbig2Context->m_nGlobalObjNum = global_objnum;
-  pJbig2Context->m_dest_buf = dest_buf;
-  pJbig2Context->m_dest_pitch = dest_pitch;
-  memset(dest_buf, 0, height * dest_pitch);
-  pJbig2Context->m_pContext =
-      CJBig2_Context::Create(global_span, global_objnum, src_span, src_objnum,
-                             pJBig2DocumentContext->GetSymbolDictCache());
-  bool succeeded = pJbig2Context->m_pContext->GetFirstPage(
-      dest_buf, width, height, dest_pitch, pPause);
-  return Decode(pJbig2Context, succeeded);
-}
-
-FXCODEC_STATUS Jbig2Module::ContinueDecode(Jbig2Context* pJbig2Context,
-                                           PauseIndicatorIface* pPause) {
-  bool succeeded = pJbig2Context->m_pContext->Continue(pPause);
-  return Decode(pJbig2Context, succeeded);
-}
-
-FXCODEC_STATUS Jbig2Module::Decode(Jbig2Context* pJbig2Context,
-                                   bool decode_success) {
-  FXCODEC_STATUS status = pJbig2Context->m_pContext->GetProcessingStatus();
-  if (status != FXCODEC_STATUS_DECODE_FINISH)
-    return status;
-
-  pJbig2Context->m_pContext.reset();
-  if (!decode_success)
-    return FXCODEC_STATUS_ERROR;
-
-  int dword_size = pJbig2Context->m_height * pJbig2Context->m_dest_pitch / 4;
-  uint32_t* dword_buf = reinterpret_cast<uint32_t*>(pJbig2Context->m_dest_buf);
-  for (int i = 0; i < dword_size; i++)
-    dword_buf[i] = ~dword_buf[i];
-  return FXCODEC_STATUS_DECODE_FINISH;
-}
-
-}  // namespace fxcodec
diff --git a/core/fxcodec/jbig2/jbig2module.h b/core/fxcodec/jbig2/jbig2module.h
deleted file mode 100644
index 6f9b0bb..0000000
--- a/core/fxcodec/jbig2/jbig2module.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_JBIG2_JBIG2MODULE_H_
-#define CORE_FXCODEC_JBIG2_JBIG2MODULE_H_
-
-#include <memory>
-
-#include "core/fxcodec/fx_codec_def.h"
-#include "third_party/base/span.h"
-
-class CJBig2_Context;
-class CJBig2_Image;
-class JBig2_DocumentContext;
-class PauseIndicatorIface;
-
-namespace fxcodec {
-
-class Jbig2Context {
- public:
-  Jbig2Context();
-  ~Jbig2Context();
-
-  uint32_t m_width = 0;
-  uint32_t m_height = 0;
-  uint32_t m_nGlobalObjNum = 0;
-  uint32_t m_nSrcObjNum = 0;
-  pdfium::span<const uint8_t> m_pGlobalSpan;
-  pdfium::span<const uint8_t> m_pSrcSpan;
-  uint8_t* m_dest_buf = nullptr;
-  uint32_t m_dest_pitch = 0;
-  std::unique_ptr<CJBig2_Context> m_pContext;
-};
-
-class Jbig2Module {
- public:
-  Jbig2Module();
-  ~Jbig2Module();
-
-  FXCODEC_STATUS StartDecode(
-      Jbig2Context* pJbig2Context,
-      std::unique_ptr<JBig2_DocumentContext>* pContextHolder,
-      uint32_t width,
-      uint32_t height,
-      pdfium::span<const uint8_t> src_span,
-      uint32_t src_objnum,
-      pdfium::span<const uint8_t> global_span,
-      uint32_t global_objnum,
-      uint8_t* dest_buf,
-      uint32_t dest_pitch,
-      PauseIndicatorIface* pPause);
-
-  FXCODEC_STATUS ContinueDecode(Jbig2Context* pJbig2Context,
-                                PauseIndicatorIface* pPause);
-
- private:
-  FXCODEC_STATUS Decode(Jbig2Context* pJbig2Context, bool decode_success);
-};
-
-}  // namespace fxcodec
-
-using Jbig2Context = fxcodec::Jbig2Context;
-using Jbig2Module = fxcodec::Jbig2Module;
-
-#endif  // CORE_FXCODEC_JBIG2_JBIG2MODULE_H_
diff --git a/core/fxcodec/jpeg/jpeg_common.cpp b/core/fxcodec/jpeg/jpeg_common.cpp
new file mode 100644
index 0000000..b65bd74
--- /dev/null
+++ b/core/fxcodec/jpeg/jpeg_common.cpp
@@ -0,0 +1,27 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/jpeg/jpeg_common.h"
+
+extern "C" {
+
+void src_do_nothing(jpeg_decompress_struct* cinfo) {}
+
+boolean src_fill_buffer(j_decompress_ptr cinfo) {
+  return FALSE;
+}
+
+boolean src_resync(j_decompress_ptr cinfo, int desired) {
+  return FALSE;
+}
+
+void error_do_nothing(j_common_ptr cinfo) {}
+
+void error_do_nothing_int(j_common_ptr cinfo, int) {}
+
+void error_do_nothing_char(j_common_ptr cinfo, char*) {}
+
+}  // extern "C"
diff --git a/core/fxcodec/jpeg/jpeg_common.h b/core/fxcodec/jpeg/jpeg_common.h
new file mode 100644
index 0000000..078ed51
--- /dev/null
+++ b/core/fxcodec/jpeg/jpeg_common.h
@@ -0,0 +1,45 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_JPEG_JPEG_COMMON_H_
+#define CORE_FXCODEC_JPEG_JPEG_COMMON_H_
+
+// Common code for interacting with libjpeg shared by other files in
+// core/fxcodec/jpeg/. Not intended to be included in headers.
+
+#include <stdio.h>
+
+#include "build/build_config.h"
+
+#if BUILDFLAG(IS_WIN)
+// windows.h must come before the third_party/libjpeg_turbo includes.
+#include <windows.h>
+#endif
+
+extern "C" {
+
+#undef FAR
+#if defined(USE_SYSTEM_LIBJPEG)
+#include <jerror.h>
+#include <jpeglib.h>
+#elif defined(USE_LIBJPEG_TURBO)
+#include "third_party/libjpeg_turbo/jerror.h"
+#include "third_party/libjpeg_turbo/jpeglib.h"
+#else
+#include "third_party/libjpeg/jerror.h"
+#include "third_party/libjpeg/jpeglib.h"
+#endif
+
+void src_do_nothing(jpeg_decompress_struct* cinfo);
+boolean src_fill_buffer(j_decompress_ptr cinfo);
+boolean src_resync(j_decompress_ptr cinfo, int desired);
+void error_do_nothing(j_common_ptr cinfo);
+void error_do_nothing_int(j_common_ptr cinfo, int);
+void error_do_nothing_char(j_common_ptr cinfo, char*);
+
+}  // extern "C"
+
+#endif  // CORE_FXCODEC_JPEG_JPEG_COMMON_H_
diff --git a/core/fxcodec/jpeg/jpeg_progressive_decoder.cpp b/core/fxcodec/jpeg/jpeg_progressive_decoder.cpp
new file mode 100644
index 0000000..1f1a7c3
--- /dev/null
+++ b/core/fxcodec/jpeg/jpeg_progressive_decoder.cpp
@@ -0,0 +1,178 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/jpeg/jpeg_progressive_decoder.h"
+
+#include <utility>
+
+#include "core/fxcodec/cfx_codec_memory.h"
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcodec/jpeg/jpeg_common.h"
+#include "core/fxcodec/scanlinedecoder.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxge/dib/cfx_dibbase.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/check.h"
+#include "third_party/base/memory/ptr_util.h"
+
+class CJpegContext final : public ProgressiveDecoderIface::Context {
+ public:
+  CJpegContext();
+  ~CJpegContext() override;
+
+  jmp_buf& GetJumpMark() { return m_JumpMark; }
+
+  jmp_buf m_JumpMark;
+  jpeg_decompress_struct m_Info = {};
+  jpeg_error_mgr m_ErrMgr = {};
+  jpeg_source_mgr m_SrcMgr = {};
+  unsigned int m_SkipSize = 0;
+};
+
+extern "C" {
+
+static void error_fatal(j_common_ptr cinfo) {
+  auto* pContext = reinterpret_cast<CJpegContext*>(cinfo->client_data);
+  longjmp(pContext->m_JumpMark, -1);
+}
+
+static void src_skip_data(jpeg_decompress_struct* cinfo, long num) {
+  if (cinfo->src->bytes_in_buffer < static_cast<size_t>(num)) {
+    auto* pContext = reinterpret_cast<CJpegContext*>(cinfo->client_data);
+    pContext->m_SkipSize = (unsigned int)(num - cinfo->src->bytes_in_buffer);
+    cinfo->src->bytes_in_buffer = 0;
+  } else {
+    cinfo->src->next_input_byte += num;
+    cinfo->src->bytes_in_buffer -= num;
+  }
+}
+
+}  // extern "C"
+
+static void JpegLoadAttribute(const jpeg_decompress_struct& info,
+                              CFX_DIBAttribute* pAttribute) {
+  pAttribute->m_nXDPI = info.X_density;
+  pAttribute->m_nYDPI = info.Y_density;
+  pAttribute->m_wDPIUnit =
+      static_cast<CFX_DIBAttribute::ResUnit>(info.density_unit);
+}
+
+CJpegContext::CJpegContext() {
+  m_Info.client_data = this;
+  m_Info.err = &m_ErrMgr;
+
+  m_ErrMgr.error_exit = error_fatal;
+  m_ErrMgr.emit_message = error_do_nothing_int;
+  m_ErrMgr.output_message = error_do_nothing;
+  m_ErrMgr.format_message = error_do_nothing_char;
+  m_ErrMgr.reset_error_mgr = error_do_nothing;
+
+  m_SrcMgr.init_source = src_do_nothing;
+  m_SrcMgr.term_source = src_do_nothing;
+  m_SrcMgr.skip_input_data = src_skip_data;
+  m_SrcMgr.fill_input_buffer = src_fill_buffer;
+  m_SrcMgr.resync_to_restart = src_resync;
+}
+
+CJpegContext::~CJpegContext() {
+  jpeg_destroy_decompress(&m_Info);
+}
+
+namespace fxcodec {
+
+// static
+JpegProgressiveDecoder* JpegProgressiveDecoder::GetInstance() {
+  static pdfium::base::NoDestructor<JpegProgressiveDecoder> s;
+  return s.get();
+}
+
+// static
+std::unique_ptr<ProgressiveDecoderIface::Context>
+JpegProgressiveDecoder::Start() {
+  // Use ordinary pointer until past the possibility of a longjump.
+  auto* pContext = new CJpegContext();
+  if (setjmp(pContext->m_JumpMark) == -1) {
+    delete pContext;
+    return nullptr;
+  }
+
+  jpeg_create_decompress(&pContext->m_Info);
+  pContext->m_Info.src = &pContext->m_SrcMgr;
+  pContext->m_SkipSize = 0;
+  return pdfium::WrapUnique(pContext);
+}
+
+// static
+jmp_buf& JpegProgressiveDecoder::GetJumpMark(Context* pContext) {
+  return static_cast<CJpegContext*>(pContext)->GetJumpMark();
+}
+
+// static
+int JpegProgressiveDecoder::ReadHeader(Context* pContext,
+                                       int* width,
+                                       int* height,
+                                       int* nComps,
+                                       CFX_DIBAttribute* pAttribute) {
+  DCHECK(pAttribute);
+
+  auto* ctx = static_cast<CJpegContext*>(pContext);
+  int ret = jpeg_read_header(&ctx->m_Info, TRUE);
+  if (ret == JPEG_SUSPENDED)
+    return 2;
+  if (ret != JPEG_HEADER_OK)
+    return 1;
+
+  *width = ctx->m_Info.image_width;
+  *height = ctx->m_Info.image_height;
+  *nComps = ctx->m_Info.num_components;
+  JpegLoadAttribute(ctx->m_Info, pAttribute);
+  return 0;
+}
+
+// static
+bool JpegProgressiveDecoder::StartScanline(Context* pContext, int down_scale) {
+  auto* ctx = static_cast<CJpegContext*>(pContext);
+  ctx->m_Info.scale_denom = static_cast<unsigned int>(down_scale);
+  return !!jpeg_start_decompress(&ctx->m_Info);
+}
+
+// static
+bool JpegProgressiveDecoder::ReadScanline(Context* pContext,
+                                          unsigned char* dest_buf) {
+  auto* ctx = static_cast<CJpegContext*>(pContext);
+  unsigned int nlines = jpeg_read_scanlines(&ctx->m_Info, &dest_buf, 1);
+  return nlines == 1;
+}
+
+FX_FILESIZE JpegProgressiveDecoder::GetAvailInput(Context* pContext) const {
+  auto* ctx = static_cast<CJpegContext*>(pContext);
+  return static_cast<FX_FILESIZE>(ctx->m_SrcMgr.bytes_in_buffer);
+}
+
+bool JpegProgressiveDecoder::Input(Context* pContext,
+                                   RetainPtr<CFX_CodecMemory> codec_memory) {
+  pdfium::span<uint8_t> src_buf = codec_memory->GetUnconsumedSpan();
+  auto* ctx = static_cast<CJpegContext*>(pContext);
+  if (ctx->m_SkipSize) {
+    if (ctx->m_SkipSize > src_buf.size()) {
+      ctx->m_SrcMgr.bytes_in_buffer = 0;
+      ctx->m_SkipSize -= src_buf.size();
+      return true;
+    }
+    src_buf = src_buf.subspan(ctx->m_SkipSize);
+    ctx->m_SkipSize = 0;
+  }
+  ctx->m_SrcMgr.next_input_byte = src_buf.data();
+  ctx->m_SrcMgr.bytes_in_buffer = src_buf.size();
+  return true;
+}
+
+JpegProgressiveDecoder::JpegProgressiveDecoder() = default;
+
+JpegProgressiveDecoder::~JpegProgressiveDecoder() = default;
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/jpeg/jpeg_progressive_decoder.h b/core/fxcodec/jpeg/jpeg_progressive_decoder.h
new file mode 100644
index 0000000..6429d01
--- /dev/null
+++ b/core/fxcodec/jpeg/jpeg_progressive_decoder.h
@@ -0,0 +1,54 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_JPEG_JPEG_PROGRESSIVE_DECODER_H_
+#define CORE_FXCODEC_JPEG_JPEG_PROGRESSIVE_DECODER_H_
+
+#include <setjmp.h>
+
+#include <memory>
+
+#include "core/fxcodec/progressive_decoder_iface.h"
+#include "third_party/base/no_destructor.h"
+
+namespace fxcodec {
+
+class CFX_DIBAttribute;
+
+class JpegProgressiveDecoder final : public ProgressiveDecoderIface {
+ public:
+  static JpegProgressiveDecoder* GetInstance();
+
+  static std::unique_ptr<Context> Start();
+
+  static jmp_buf& GetJumpMark(Context* pContext);
+
+  static int ReadHeader(Context* pContext,
+                        int* width,
+                        int* height,
+                        int* nComps,
+                        CFX_DIBAttribute* pAttribute);
+
+  static bool StartScanline(Context* pContext, int down_scale);
+  static bool ReadScanline(Context* pContext, uint8_t* dest_buf);
+
+  // ProgressiveDecoderIface:
+  FX_FILESIZE GetAvailInput(Context* pContext) const override;
+  bool Input(Context* pContext,
+             RetainPtr<CFX_CodecMemory> codec_memory) override;
+
+ private:
+  friend pdfium::base::NoDestructor<JpegProgressiveDecoder>;
+
+  JpegProgressiveDecoder();
+  ~JpegProgressiveDecoder() override;
+};
+
+}  // namespace fxcodec
+
+using JpegProgressiveDecoder = fxcodec::JpegProgressiveDecoder;
+
+#endif  // CORE_FXCODEC_JPEG_JPEG_PROGRESSIVE_DECODER_H_
diff --git a/core/fxcodec/jpeg/jpegmodule.cpp b/core/fxcodec/jpeg/jpegmodule.cpp
index 72925f1..ad80c7e 100644
--- a/core/fxcodec/jpeg/jpegmodule.cpp
+++ b/core/fxcodec/jpeg/jpegmodule.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,55 +7,27 @@
 #include "core/fxcodec/jpeg/jpegmodule.h"
 
 #include <setjmp.h>
+#include <stdint.h>
+#include <string.h>
 
 #include <memory>
 #include <utility>
 
 #include "build/build_config.h"
 #include "core/fxcodec/cfx_codec_memory.h"
-#include "core/fxcodec/fx_codec.h"
+#include "core/fxcodec/jpeg/jpeg_common.h"
 #include "core/fxcodec/scanlinedecoder.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxge/dib/cfx_dibbase.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/optional.h"
-#include "third_party/base/ptr_util.h"
-
-extern "C" {
-#undef FAR
-#if defined(USE_SYSTEM_LIBJPEG)
-#include <jerror.h>
-#include <jpeglib.h>
-#elif defined(USE_LIBJPEG_TURBO)
-#include "third_party/libjpeg_turbo/jerror.h"
-#include "third_party/libjpeg_turbo/jpeglib.h"
-#else
-#include "third_party/libjpeg/jerror.h"
-#include "third_party/libjpeg/jpeglib.h"
-#endif
-}  // extern "C"
-
-class CJpegContext final : public ModuleIface::Context {
- public:
-  CJpegContext();
-  ~CJpegContext() override;
-
-  jmp_buf& GetJumpMark() { return m_JumpMark; }
-
-  jmp_buf m_JumpMark;
-  jpeg_decompress_struct m_Info = {};
-  jpeg_error_mgr m_ErrMgr = {};
-  jpeg_source_mgr m_SrcMgr = {};
-  unsigned int m_SkipSize = 0;
-  void* (*m_AllocFunc)(unsigned int);
-  void (*m_FreeFunc)(void*);
-};
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 
 static pdfium::span<const uint8_t> JpegScanSOI(
     pdfium::span<const uint8_t> src_span) {
-  ASSERT(!src_span.empty());
+  DCHECK(!src_span.empty());
 
   for (size_t offset = 0; offset < src_span.size() - 1; ++offset) {
     if (src_span[offset] == 0xff && src_span[offset + 1] == 0xd8)
@@ -66,8 +38,6 @@
 
 extern "C" {
 
-static void src_do_nothing(jpeg_decompress_struct* cinfo) {}
-
 static void error_fatal(j_common_ptr cinfo) {
   longjmp(*(jmp_buf*)cinfo->client_data, -1);
 }
@@ -80,76 +50,25 @@
   cinfo->src->bytes_in_buffer -= num;
 }
 
-static boolean src_fill_buffer(j_decompress_ptr cinfo) {
-  return FALSE;
-}
-
-static boolean src_resync(j_decompress_ptr cinfo, int desired) {
-  return FALSE;
-}
-
-static void error_do_nothing(j_common_ptr cinfo) {}
-
-static void error_do_nothing1(j_common_ptr cinfo, int) {}
-
-static void error_do_nothing2(j_common_ptr cinfo, char*) {}
-
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 static void dest_do_nothing(j_compress_ptr cinfo) {}
 
 static boolean dest_empty(j_compress_ptr cinfo) {
   return false;
 }
-#endif  // defined(OS_WIN)
-
-static void error_fatal1(j_common_ptr cinfo) {
-  auto* pContext = reinterpret_cast<CJpegContext*>(cinfo->client_data);
-  longjmp(pContext->m_JumpMark, -1);
-}
-
-static void src_skip_data1(jpeg_decompress_struct* cinfo, long num) {
-  if (cinfo->src->bytes_in_buffer < static_cast<size_t>(num)) {
-    auto* pContext = reinterpret_cast<CJpegContext*>(cinfo->client_data);
-    pContext->m_SkipSize = (unsigned int)(num - cinfo->src->bytes_in_buffer);
-    cinfo->src->bytes_in_buffer = 0;
-  } else {
-    cinfo->src->next_input_byte += num;
-    cinfo->src->bytes_in_buffer -= num;
-  }
-}
-
-static void* jpeg_alloc_func(unsigned int size) {
-  return FX_Alloc(char, size);
-}
-
-static void jpeg_free_func(void* p) {
-  FX_Free(p);
-}
+#endif  // BUILDFLAG(IS_WIN)
 
 }  // extern "C"
 
-#ifdef PDF_ENABLE_XFA
-static void JpegLoadAttribute(const jpeg_decompress_struct& info,
-                              CFX_DIBAttribute* pAttribute) {
-  pAttribute->m_nXDPI = info.X_density;
-  pAttribute->m_nYDPI = info.Y_density;
-  pAttribute->m_wDPIUnit = info.density_unit;
-}
-#endif  // PDF_ENABLE_XFA
-
 static bool JpegLoadInfo(pdfium::span<const uint8_t> src_span,
-                         int* width,
-                         int* height,
-                         int* num_components,
-                         int* bits_per_components,
-                         bool* color_transform) {
+                         JpegModule::ImageInfo* pInfo) {
   src_span = JpegScanSOI(src_span);
   jpeg_decompress_struct cinfo;
   jpeg_error_mgr jerr;
   jerr.error_exit = error_fatal;
-  jerr.emit_message = error_do_nothing1;
+  jerr.emit_message = error_do_nothing_int;
   jerr.output_message = error_do_nothing;
-  jerr.format_message = error_do_nothing2;
+  jerr.format_message = error_do_nothing_char;
   jerr.reset_error_mgr = error_do_nothing;
   jerr.trace_level = 0;
   cinfo.err = &jerr;
@@ -177,38 +96,16 @@
     jpeg_destroy_decompress(&cinfo);
     return false;
   }
-  *width = cinfo.image_width;
-  *height = cinfo.image_height;
-  *num_components = cinfo.num_components;
-  *color_transform =
+  pInfo->width = cinfo.image_width;
+  pInfo->height = cinfo.image_height;
+  pInfo->num_components = cinfo.num_components;
+  pInfo->color_transform =
       cinfo.jpeg_color_space == JCS_YCbCr || cinfo.jpeg_color_space == JCS_YCCK;
-  *bits_per_components = cinfo.data_precision;
+  pInfo->bits_per_components = cinfo.data_precision;
   jpeg_destroy_decompress(&cinfo);
   return true;
 }
 
-CJpegContext::CJpegContext()
-    : m_AllocFunc(jpeg_alloc_func), m_FreeFunc(jpeg_free_func) {
-  m_Info.client_data = this;
-  m_Info.err = &m_ErrMgr;
-
-  m_ErrMgr.error_exit = error_fatal1;
-  m_ErrMgr.emit_message = error_do_nothing1;
-  m_ErrMgr.output_message = error_do_nothing;
-  m_ErrMgr.format_message = error_do_nothing2;
-  m_ErrMgr.reset_error_mgr = error_do_nothing;
-
-  m_SrcMgr.init_source = src_do_nothing;
-  m_SrcMgr.term_source = src_do_nothing;
-  m_SrcMgr.skip_input_data = src_skip_data1;
-  m_SrcMgr.fill_input_buffer = src_fill_buffer;
-  m_SrcMgr.resync_to_restart = src_resync;
-}
-
-CJpegContext::~CJpegContext() {
-  jpeg_destroy_decompress(&m_Info);
-}
-
 namespace fxcodec {
 
 namespace {
@@ -221,28 +118,18 @@
   ~JpegDecoder() override;
 
   bool Create(pdfium::span<const uint8_t> src_span,
-              int width,
-              int height,
+              uint32_t width,
+              uint32_t height,
               int nComps,
               bool ColorTransform);
 
   // ScanlineDecoder:
-  bool v_Rewind() override;
-  uint8_t* v_GetNextLine() override;
+  bool Rewind() override;
+  pdfium::span<uint8_t> GetNextLine() override;
   uint32_t GetSrcOffset() override;
 
   bool InitDecode(bool bAcceptKnownBadHeader);
 
-  jmp_buf m_JmpBuf;
-  jpeg_decompress_struct m_Cinfo;
-  jpeg_error_mgr m_Jerr;
-  jpeg_source_mgr m_Src;
-  pdfium::span<const uint8_t> m_SrcSpan;
-  std::unique_ptr<uint8_t, FxFreeDeleter> m_pScanlineBuf;
-  bool m_bInited = false;
-  bool m_bStarted = false;
-  bool m_bJpegTransform = false;
-
  private:
   void CalcPitch();
   void InitDecompressSrc();
@@ -264,8 +151,17 @@
   // For a given invalid height byte offset in
   // |kKnownBadHeaderWithInvalidHeightByteOffsetStarts|, the SOFn marker should
   // be this many bytes before that.
-  static const size_t kSofMarkerByteOffset = 5;
+  static constexpr size_t kSofMarkerByteOffset = 5;
 
+  jmp_buf m_JmpBuf;
+  jpeg_decompress_struct m_Cinfo;
+  jpeg_error_mgr m_Jerr;
+  jpeg_source_mgr m_Src;
+  pdfium::span<const uint8_t> m_SrcSpan;
+  DataVector<uint8_t> m_ScanlineBuf;
+  bool m_bInited = false;
+  bool m_bStarted = false;
+  bool m_bJpegTransform = false;
   uint32_t m_nDefaultScaleDenom = 1;
 };
 
@@ -278,6 +174,9 @@
 JpegDecoder::~JpegDecoder() {
   if (m_bInited)
     jpeg_destroy_decompress(&m_Cinfo);
+
+  // Span in superclass can't outlive our buffer.
+  m_pLastScanline = pdfium::span<uint8_t>();
 }
 
 bool JpegDecoder::InitDecode(bool bAcceptKnownBadHeader) {
@@ -291,7 +190,7 @@
   m_bInited = true;
 
   if (setjmp(m_JmpBuf) == -1) {
-    Optional<size_t> known_bad_header_offset;
+    absl::optional<size_t> known_bad_header_offset;
     if (bAcceptKnownBadHeader) {
       for (size_t offset : kKnownBadHeaderWithInvalidHeightByteOffsetStarts) {
         if (HasKnownBadHeaderWithInvalidHeight(offset)) {
@@ -332,8 +231,8 @@
 }
 
 bool JpegDecoder::Create(pdfium::span<const uint8_t> src_span,
-                         int width,
-                         int height,
+                         uint32_t width,
+                         uint32_t height,
                          int nComps,
                          bool ColorTransform) {
   m_SrcSpan = JpegScanSOI(src_span);
@@ -343,9 +242,9 @@
   PatchUpTrailer();
 
   m_Jerr.error_exit = error_fatal;
-  m_Jerr.emit_message = error_do_nothing1;
+  m_Jerr.emit_message = error_do_nothing_int;
   m_Jerr.output_message = error_do_nothing;
-  m_Jerr.format_message = error_do_nothing2;
+  m_Jerr.format_message = error_do_nothing_char;
   m_Jerr.reset_error_mgr = error_do_nothing;
   m_Src.init_source = src_do_nothing;
   m_Src.term_source = src_do_nothing;
@@ -361,18 +260,18 @@
   if (m_Cinfo.num_components < nComps)
     return false;
 
-  if (static_cast<int>(m_Cinfo.image_width) < width)
+  if (m_Cinfo.image_width < width)
     return false;
 
   CalcPitch();
-  m_pScanlineBuf.reset(FX_Alloc(uint8_t, m_Pitch));
+  m_ScanlineBuf = DataVector<uint8_t>(m_Pitch);
   m_nComps = m_Cinfo.num_components;
   m_bpc = 8;
   m_bStarted = false;
   return true;
 }
 
-bool JpegDecoder::v_Rewind() {
+bool JpegDecoder::Rewind() {
   if (m_bStarted) {
     jpeg_destroy_decompress(&m_Cinfo);
     if (!InitDecode(/*bAcceptKnownBadHeader=*/false)) {
@@ -389,21 +288,21 @@
     jpeg_destroy_decompress(&m_Cinfo);
     return false;
   }
-  if (static_cast<int>(m_Cinfo.output_width) > m_OrigWidth) {
-    NOTREACHED();
-    return false;
-  }
+  CHECK_LE(static_cast<int>(m_Cinfo.output_width), m_OrigWidth);
   m_bStarted = true;
   return true;
 }
 
-uint8_t* JpegDecoder::v_GetNextLine() {
+pdfium::span<uint8_t> JpegDecoder::GetNextLine() {
   if (setjmp(m_JmpBuf) == -1)
-    return nullptr;
+    return pdfium::span<uint8_t>();
 
-  uint8_t* row_array[] = {m_pScanlineBuf.get()};
+  uint8_t* row_array[] = {m_ScanlineBuf.data()};
   int nlines = jpeg_read_scanlines(&m_Cinfo, row_array, 1);
-  return nlines > 0 ? m_pScanlineBuf.get() : nullptr;
+  if (nlines <= 0)
+    return pdfium::span<uint8_t>();
+
+  return m_ScanlineBuf;
 }
 
 uint32_t JpegDecoder::GetSrcOffset() {
@@ -459,7 +358,7 @@
 
 void JpegDecoder::PatchUpKnownBadHeaderWithInvalidHeight(
     size_t dimension_offset) {
-  ASSERT(m_SrcSpan.size() > dimension_offset + 1u);
+  DCHECK(m_SrcSpan.size() > dimension_offset + 1u);
   uint8_t* pData = GetWritableSrcData() + dimension_offset;
   pData[0] = (m_OrigHeight >> 8) & 0xff;
   pData[1] = m_OrigHeight & 0xff;
@@ -477,117 +376,41 @@
 
 }  // namespace
 
+// static
 std::unique_ptr<ScanlineDecoder> JpegModule::CreateDecoder(
     pdfium::span<const uint8_t> src_span,
-    int width,
-    int height,
+    uint32_t width,
+    uint32_t height,
     int nComps,
     bool ColorTransform) {
-  ASSERT(!src_span.empty());
+  DCHECK(!src_span.empty());
 
-  auto pDecoder = pdfium::MakeUnique<JpegDecoder>();
+  auto pDecoder = std::make_unique<JpegDecoder>();
   if (!pDecoder->Create(src_span, width, height, nComps, ColorTransform))
     return nullptr;
 
   return std::move(pDecoder);
 }
 
-Optional<JpegModule::JpegImageInfo> JpegModule::LoadInfo(
+// static
+absl::optional<JpegModule::ImageInfo> JpegModule::LoadInfo(
     pdfium::span<const uint8_t> src_span) {
-  JpegImageInfo info;
-  if (!JpegLoadInfo(src_span, &info.width, &info.height, &info.num_components,
-                    &info.bits_per_components, &info.color_transform)) {
-    return pdfium::nullopt;
-  }
+  ImageInfo info;
+  if (!JpegLoadInfo(src_span, &info))
+    return absl::nullopt;
+
   return info;
 }
 
-std::unique_ptr<ModuleIface::Context> JpegModule::Start() {
-  // Use ordinary pointer until past the possibility of a longjump.
-  auto* pContext = new CJpegContext();
-  if (setjmp(pContext->m_JumpMark) == -1) {
-    delete pContext;
-    return nullptr;
-  }
-
-  jpeg_create_decompress(&pContext->m_Info);
-  pContext->m_Info.src = &pContext->m_SrcMgr;
-  pContext->m_SkipSize = 0;
-  return pdfium::WrapUnique(pContext);
-}
-
-bool JpegModule::Input(Context* pContext,
-                       RetainPtr<CFX_CodecMemory> codec_memory,
-                       CFX_DIBAttribute*) {
-  pdfium::span<uint8_t> src_buf = codec_memory->GetSpan();
-  auto* ctx = static_cast<CJpegContext*>(pContext);
-  if (ctx->m_SkipSize) {
-    if (ctx->m_SkipSize > src_buf.size()) {
-      ctx->m_SrcMgr.bytes_in_buffer = 0;
-      ctx->m_SkipSize -= src_buf.size();
-      return true;
-    }
-    src_buf = src_buf.subspan(ctx->m_SkipSize);
-    ctx->m_SkipSize = 0;
-  }
-  ctx->m_SrcMgr.next_input_byte = src_buf.data();
-  ctx->m_SrcMgr.bytes_in_buffer = src_buf.size();
-  return true;
-}
-
-#ifdef PDF_ENABLE_XFA
-int JpegModule::ReadHeader(Context* pContext,
-                           int* width,
-                           int* height,
-                           int* nComps,
-                           CFX_DIBAttribute* pAttribute) {
-  ASSERT(pAttribute);
-
-  auto* ctx = static_cast<CJpegContext*>(pContext);
-  int ret = jpeg_read_header(&ctx->m_Info, TRUE);
-  if (ret == JPEG_SUSPENDED)
-    return 2;
-  if (ret != JPEG_HEADER_OK)
-    return 1;
-
-  *width = ctx->m_Info.image_width;
-  *height = ctx->m_Info.image_height;
-  *nComps = ctx->m_Info.num_components;
-  JpegLoadAttribute(ctx->m_Info, pAttribute);
-  return 0;
-}
-#endif  // PDF_ENABLE_XFA
-
-bool JpegModule::StartScanline(Context* pContext, int down_scale) {
-  auto* ctx = static_cast<CJpegContext*>(pContext);
-  ctx->m_Info.scale_denom = static_cast<unsigned int>(down_scale);
-  return !!jpeg_start_decompress(&ctx->m_Info);
-}
-
-bool JpegModule::ReadScanline(Context* pContext, unsigned char* dest_buf) {
-  auto* ctx = static_cast<CJpegContext*>(pContext);
-  unsigned int nlines = jpeg_read_scanlines(&ctx->m_Info, &dest_buf, 1);
-  return nlines == 1;
-}
-
-FX_FILESIZE JpegModule::GetAvailInput(Context* pContext) const {
-  auto* ctx = static_cast<CJpegContext*>(pContext);
-  return static_cast<FX_FILESIZE>(ctx->m_SrcMgr.bytes_in_buffer);
-}
-
-jmp_buf& JpegModule::GetJumpMark(Context* pContext) {
-  return static_cast<CJpegContext*>(pContext)->GetJumpMark();
-}
-
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 bool JpegModule::JpegEncode(const RetainPtr<CFX_DIBBase>& pSource,
                             uint8_t** dest_buf,
                             size_t* dest_size) {
   jpeg_error_mgr jerr;
   jerr.error_exit = error_do_nothing;
-  jerr.emit_message = error_do_nothing1;
+  jerr.emit_message = error_do_nothing_int;
   jerr.output_message = error_do_nothing;
-  jerr.format_message = error_do_nothing2;
+  jerr.format_message = error_do_nothing_char;
   jerr.reset_error_mgr = error_do_nothing;
 
   jpeg_compress_struct cinfo;
@@ -595,7 +418,7 @@
   cinfo.err = &jerr;
   jpeg_create_compress(&cinfo);
   int Bpp = pSource->GetBPP() / 8;
-  uint32_t nComponents = Bpp >= 3 ? (pSource->IsCmykImage() ? 4 : 3) : 1;
+  uint32_t nComponents = Bpp >= 3 ? 3 : 1;
   uint32_t pitch = pSource->GetPitch();
   uint32_t width = pdfium::base::checked_cast<uint32_t>(pSource->GetWidth());
   uint32_t height = pdfium::base::checked_cast<uint32_t>(pSource->GetHeight());
@@ -642,25 +465,25 @@
   JSAMPROW row_pointer[1];
   JDIMENSION row;
   while (cinfo.next_scanline < cinfo.image_height) {
-    const uint8_t* src_scan = pSource->GetScanline(cinfo.next_scanline);
+    pdfium::span<const uint8_t> src_scan =
+        pSource->GetScanline(cinfo.next_scanline);
     if (nComponents > 1) {
       uint8_t* dest_scan = line_buf;
       if (nComponents == 3) {
         for (uint32_t i = 0; i < width; i++) {
-          dest_scan[0] = src_scan[2];
-          dest_scan[1] = src_scan[1];
-          dest_scan[2] = src_scan[0];
+          ReverseCopy3Bytes(dest_scan, src_scan.data());
           dest_scan += 3;
-          src_scan += Bpp;
+          src_scan = src_scan.subspan(Bpp);
         }
       } else {
         for (uint32_t i = 0; i < pitch; i++) {
-          *dest_scan++ = ~*src_scan++;
+          *dest_scan++ = ~src_scan.front();
+          src_scan = src_scan.subspan(1);
         }
       }
       row_pointer[0] = line_buf;
     } else {
-      row_pointer[0] = const_cast<uint8_t*>(src_scan);
+      row_pointer[0] = const_cast<uint8_t*>(src_scan.data());
     }
     row = cinfo.next_scanline;
     jpeg_write_scanlines(&cinfo, row_pointer, 1);
@@ -680,6 +503,6 @@
 
   return true;
 }
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 }  // namespace fxcodec
diff --git a/core/fxcodec/jpeg/jpegmodule.h b/core/fxcodec/jpeg/jpegmodule.h
index 0ecf204..0f61765 100644
--- a/core/fxcodec/jpeg/jpegmodule.h
+++ b/core/fxcodec/jpeg/jpegmodule.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,65 +7,53 @@
 #ifndef CORE_FXCODEC_JPEG_JPEGMODULE_H_
 #define CORE_FXCODEC_JPEG_JPEGMODULE_H_
 
-#include <csetjmp>
+#include <stdint.h>
+
 #include <memory>
 
 #include "build/build_config.h"
-#include "core/fxcodec/codec_module_iface.h"
-#include "third_party/base/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/containers/span.h"
+
+#if BUILDFLAG(IS_WIN)
+#include "core/fxcrt/retain_ptr.h"
+#endif
 
 class CFX_DIBBase;
 
 namespace fxcodec {
 
-class CFX_DIBAttribute;
 class ScanlineDecoder;
 
-class JpegModule final : public ModuleIface {
+class JpegModule {
  public:
-  struct JpegImageInfo {
-    int width;
-    int height;
+  struct ImageInfo {
+    uint32_t width;
+    uint32_t height;
     int num_components;
     int bits_per_components;
     bool color_transform;
   };
 
-  std::unique_ptr<ScanlineDecoder> CreateDecoder(
+  static std::unique_ptr<ScanlineDecoder> CreateDecoder(
       pdfium::span<const uint8_t> src_span,
-      int width,
-      int height,
+      uint32_t width,
+      uint32_t height,
       int nComps,
       bool ColorTransform);
 
-  // ModuleIface:
-  FX_FILESIZE GetAvailInput(Context* pContext) const override;
-  bool Input(Context* pContext,
-             RetainPtr<CFX_CodecMemory> codec_memory,
-             CFX_DIBAttribute* pAttribute) override;
+  static absl::optional<ImageInfo> LoadInfo(
+      pdfium::span<const uint8_t> src_span);
 
-  jmp_buf& GetJumpMark(Context* pContext);
-  Optional<JpegImageInfo> LoadInfo(pdfium::span<const uint8_t> src_span);
-
-  std::unique_ptr<Context> Start();
-
-#ifdef PDF_ENABLE_XFA
-  int ReadHeader(Context* pContext,
-                 int* width,
-                 int* height,
-                 int* nComps,
-                 CFX_DIBAttribute* pAttribute);
-#endif  // PDF_ENABLE_XFA
-
-  bool StartScanline(Context* pContext, int down_scale);
-  bool ReadScanline(Context* pContext, uint8_t* dest_buf);
-
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   static bool JpegEncode(const RetainPtr<CFX_DIBBase>& pSource,
                          uint8_t** dest_buf,
                          size_t* dest_size);
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
+
+  JpegModule() = delete;
+  JpegModule(const JpegModule&) = delete;
+  JpegModule& operator=(const JpegModule&) = delete;
 };
 
 }  // namespace fxcodec
diff --git a/core/fxcodec/jpx/DEPS b/core/fxcodec/jpx/DEPS
index d728e0f..56cdc44 100644
--- a/core/fxcodec/jpx/DEPS
+++ b/core/fxcodec/jpx/DEPS
@@ -1,3 +1,3 @@
 include_rules = [
-  '+third_party/libopenjpeg20',
+  '+third_party/libopenjpeg',
 ]
diff --git a/core/fxcodec/jpx/cjpx_decoder.cpp b/core/fxcodec/jpx/cjpx_decoder.cpp
index 95f76ff..2e7a72a 100644
--- a/core/fxcodec/jpx/cjpx_decoder.cpp
+++ b/core/fxcodec/jpx/cjpx_decoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,18 +6,22 @@
 
 #include "core/fxcodec/jpx/cjpx_decoder.h"
 
+#include <string.h>
+
 #include <algorithm>
 #include <limits>
 #include <utility>
+#include <vector>
 
 #include "core/fxcodec/jpx/jpx_decode_utils.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/optional.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxge/calculate_pitch.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/memory/ptr_util.h"
 
 #if !defined(USE_SYSTEM_LIBOPENJPEG2)
-#include "third_party/libopenjpeg20/opj_malloc.h"
+#include "third_party/libopenjpeg/opj_malloc.h"
 #endif
 
 namespace fxcodec {
@@ -56,19 +60,19 @@
   return stream;
 }
 
-Optional<OpjImageRgbData> alloc_rgb(size_t size) {
+absl::optional<OpjImageRgbData> alloc_rgb(size_t size) {
   OpjImageRgbData data;
   data.r.reset(static_cast<int*>(opj_image_data_alloc(size)));
   if (!data.r)
-    return {};
+    return absl::nullopt;
 
   data.g.reset(static_cast<int*>(opj_image_data_alloc(size)));
   if (!data.g)
-    return {};
+    return absl::nullopt;
 
   data.b.reset(static_cast<int*>(opj_image_data_alloc(size)));
   if (!data.b)
-    return {};
+    return absl::nullopt;
 
   return data;
 }
@@ -83,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) {
@@ -111,7 +115,7 @@
   if (!y || !cb || !cr)
     return;
 
-  Optional<OpjImageRgbData> data = alloc_rgb(max_size.ValueOrDie());
+  absl::optional<OpjImageRgbData> data = alloc_rgb(max_size.ValueOrDie());
   if (!data.has_value())
     return;
 
@@ -176,7 +180,7 @@
   if (!y || !cb || !cr)
     return;
 
-  Optional<OpjImageRgbData> data = alloc_rgb(safe_size.ValueOrDie());
+  absl::optional<OpjImageRgbData> data = alloc_rgb(safe_size.ValueOrDie());
   if (!data.has_value())
     return;
 
@@ -313,7 +317,7 @@
   if (!y || !cb || !cr)
     return;
 
-  Optional<OpjImageRgbData> data = alloc_rgb(max_size.ValueOrDie());
+  absl::optional<OpjImageRgbData> data = alloc_rgb(max_size.ValueOrDie());
   if (!data.has_value())
     return;
 
@@ -385,6 +389,18 @@
 }  // namespace
 
 // static
+std::unique_ptr<CJPX_Decoder> CJPX_Decoder::Create(
+    pdfium::span<const uint8_t> src_span,
+    CJPX_Decoder::ColorSpaceOption option,
+    uint8_t resolution_levels_to_skip) {
+  // Private ctor.
+  auto decoder = pdfium::WrapUnique(new CJPX_Decoder(option));
+  if (!decoder->Init(src_span, resolution_levels_to_skip))
+    return nullptr;
+  return decoder;
+}
+
+// static
 void CJPX_Decoder::Sycc420ToRgbForTesting(opj_image_t* img) {
   sycc420_to_rgb(img);
 }
@@ -394,23 +410,25 @@
 
 CJPX_Decoder::~CJPX_Decoder() {
   if (m_Codec)
-    opj_destroy_codec(m_Codec.Release());
+    opj_destroy_codec(m_Codec.ExtractAsDangling());
   if (m_Stream)
-    opj_stream_destroy(m_Stream.Release());
+    opj_stream_destroy(m_Stream.ExtractAsDangling());
   if (m_Image)
-    opj_image_destroy(m_Image.Release());
+    opj_image_destroy(m_Image.ExtractAsDangling());
 }
 
-bool CJPX_Decoder::Init(pdfium::span<const uint8_t> src_data) {
-  static const unsigned char szJP2Header[] = {
-      0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a};
-  if (src_data.empty() || src_data.size() < sizeof(szJP2Header))
+bool CJPX_Decoder::Init(pdfium::span<const uint8_t> src_data,
+                        uint8_t resolution_levels_to_skip) {
+  static constexpr uint8_t kJP2Header[] = {0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50,
+                                           0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a};
+  if (src_data.size() < sizeof(kJP2Header) ||
+      resolution_levels_to_skip > kMaxResolutionsToSkip) {
     return false;
+  }
 
   m_Image = nullptr;
   m_SrcData = src_data;
-  m_DecodeData =
-      pdfium::MakeUnique<DecodeData>(src_data.data(), src_data.size());
+  m_DecodeData = std::make_unique<DecodeData>(src_data.data(), src_data.size());
   m_Stream = fx_opj_stream_create_memory_stream(m_DecodeData.get());
   if (!m_Stream)
     return false;
@@ -418,7 +436,8 @@
   opj_set_default_decoder_parameters(&m_Parameters);
   m_Parameters.decod_format = 0;
   m_Parameters.cod_format = 3;
-  if (memcmp(m_SrcData.data(), szJP2Header, sizeof(szJP2Header)) == 0) {
+  m_Parameters.cp_reduce = resolution_levels_to_skip;
+  if (memcmp(m_SrcData.data(), kJP2Header, sizeof(kJP2Header)) == 0) {
     m_Codec = opj_create_decompress(OPJ_CODEC_JP2);
     m_Parameters.decod_format = 1;
   } else {
@@ -429,15 +448,15 @@
 
   if (m_ColorSpaceOption == kIndexedColorSpace)
     m_Parameters.flags |= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG;
-  opj_set_info_handler(m_Codec.Get(), fx_ignore_callback, nullptr);
-  opj_set_warning_handler(m_Codec.Get(), fx_ignore_callback, nullptr);
-  opj_set_error_handler(m_Codec.Get(), fx_ignore_callback, nullptr);
-  if (!opj_setup_decoder(m_Codec.Get(), &m_Parameters))
+  opj_set_info_handler(m_Codec, fx_ignore_callback, nullptr);
+  opj_set_warning_handler(m_Codec, fx_ignore_callback, nullptr);
+  opj_set_error_handler(m_Codec, fx_ignore_callback, nullptr);
+  if (!opj_setup_decoder(m_Codec, &m_Parameters))
     return false;
 
   m_Image = nullptr;
   opj_image_t* pTempImage = nullptr;
-  if (!opj_read_header(m_Stream.Get(), m_Codec.Get(), &pTempImage))
+  if (!opj_read_header(m_Stream, m_Codec, &pTempImage))
     return false;
 
   m_Image = pTempImage;
@@ -446,23 +465,23 @@
 
 bool CJPX_Decoder::StartDecode() {
   if (!m_Parameters.nb_tile_to_decode) {
-    if (!opj_set_decode_area(m_Codec.Get(), m_Image.Get(), m_Parameters.DA_x0,
+    if (!opj_set_decode_area(m_Codec, m_Image, m_Parameters.DA_x0,
                              m_Parameters.DA_y0, m_Parameters.DA_x1,
                              m_Parameters.DA_y1)) {
-      opj_image_destroy(m_Image.Release());
+      opj_image_destroy(m_Image.ExtractAsDangling());
       return false;
     }
-    if (!(opj_decode(m_Codec.Get(), m_Stream.Get(), m_Image.Get()) &&
-          opj_end_decompress(m_Codec.Get(), m_Stream.Get()))) {
-      opj_image_destroy(m_Image.Release());
+    if (!(opj_decode(m_Codec, m_Stream, m_Image) &&
+          opj_end_decompress(m_Codec, m_Stream))) {
+      opj_image_destroy(m_Image.ExtractAsDangling());
       return false;
     }
-  } else if (!opj_get_decoded_tile(m_Codec.Get(), m_Stream.Get(), m_Image.Get(),
+  } else if (!opj_get_decoded_tile(m_Codec, m_Stream, m_Image,
                                    m_Parameters.tile_index)) {
     return false;
   }
 
-  opj_stream_destroy(m_Stream.Release());
+  opj_stream_destroy(m_Stream.ExtractAsDangling());
   if (m_Image->color_space != OPJ_CLRSPC_SYCC && m_Image->numcomps == 3 &&
       m_Image->comps[0].dx == m_Image->comps[0].dy &&
       m_Image->comps[1].dx != 1) {
@@ -471,7 +490,7 @@
     m_Image->color_space = OPJ_CLRSPC_GRAY;
   }
   if (m_Image->color_space == OPJ_CLRSPC_SYCC)
-    color_sycc_to_rgb(m_Image.Get());
+    color_sycc_to_rgb(m_Image);
 
   if (m_Image->icc_profile_buf) {
     // TODO(palmer): Using |opj_free| here resolves the crash described in
@@ -489,24 +508,42 @@
 }
 
 CJPX_Decoder::JpxImageInfo CJPX_Decoder::GetInfo() const {
-  return {m_Image->x1, m_Image->y1, m_Image->numcomps};
+  return {m_Image->comps[0].w, m_Image->comps[0].h, m_Image->numcomps,
+          m_Image->color_space};
 }
 
-bool CJPX_Decoder::Decode(uint8_t* dest_buf, uint32_t pitch, bool swap_rgb) {
-  if (m_Image->comps[0].w != m_Image->x1 || m_Image->comps[0].h != m_Image->y1)
-    return false;
+bool CJPX_Decoder::Decode(pdfium::span<uint8_t> dest_buf,
+                          uint32_t pitch,
+                          bool swap_rgb,
+                          uint32_t component_count) {
+  CHECK_LE(component_count, m_Image->numcomps);
+  uint32_t channel_count = component_count;
+  if (channel_count == 3 && m_Image->numcomps == 4) {
+    // When decoding for an ARGB image, include the alpha channel in the channel
+    // count.
+    channel_count = 4;
+  }
 
-  if (pitch<(m_Image->comps[0].w * 8 * m_Image->numcomps + 31)>> 5 << 2)
+  absl::optional<uint32_t> calculated_pitch =
+      fxge::CalculatePitch32(8 * channel_count, m_Image->comps[0].w);
+  if (!calculated_pitch.has_value() || pitch < calculated_pitch.value()) {
     return false;
+  }
 
-  if (swap_rgb && m_Image->numcomps < 3)
+  if (swap_rgb && channel_count < 3) {
     return false;
+  }
 
-  memset(dest_buf, 0xff, m_Image->y1 * pitch);
+  // Initialize `channel_bufs` and `adjust_comps` to store information from all
+  // the channels of the JPX image. They will contain more information besides
+  // the color component data if `m_Image->numcomps` > `component_count`.
+  // Currently only the color component data is used for rendering.
+  // TODO(crbug.com/pdfium/1747): Make full use of the component information.
+  fxcrt::spanset(dest_buf.first(m_Image->comps[0].h * pitch), 0xff);
   std::vector<uint8_t*> channel_bufs(m_Image->numcomps);
   std::vector<int> adjust_comps(m_Image->numcomps);
   for (uint32_t i = 0; i < m_Image->numcomps; i++) {
-    channel_bufs[i] = dest_buf + i;
+    channel_bufs[i] = dest_buf.subspan(i).data();
     adjust_comps[i] = m_Image->comps[i].prec - 8;
     if (i > 0) {
       if (m_Image->comps[i].dx != m_Image->comps[i - 1].dx ||
@@ -521,47 +558,43 @@
 
   uint32_t width = m_Image->comps[0].w;
   uint32_t height = m_Image->comps[0].h;
-  for (uint32_t channel = 0; channel < m_Image->numcomps; ++channel) {
+  for (uint32_t channel = 0; channel < channel_count; ++channel) {
     uint8_t* pChannel = channel_bufs[channel];
-    if (adjust_comps[channel] < 0) {
+    const int adjust = adjust_comps[channel];
+    const opj_image_comp_t& comps = m_Image->comps[channel];
+    if (!comps.data)
+      continue;
+
+    // Perfomance-sensitive code below. Combining these 3 for-loops below will
+    // cause a slowdown.
+    const uint32_t src_offset = comps.sgnd ? 1 << (comps.prec - 1) : 0;
+    if (adjust < 0) {
       for (uint32_t row = 0; row < height; ++row) {
         uint8_t* pScanline = pChannel + row * pitch;
         for (uint32_t col = 0; col < width; ++col) {
-          uint8_t* pPixel = pScanline + col * m_Image->numcomps;
-          if (!m_Image->comps[channel].data)
-            continue;
-
-          int src = m_Image->comps[channel].data[row * width + col];
-          src += m_Image->comps[channel].sgnd
-                     ? 1 << (m_Image->comps[channel].prec - 1)
-                     : 0;
-          if (adjust_comps[channel] > 0) {
-            *pPixel = 0;
-          } else {
-            *pPixel = static_cast<uint8_t>(src << -adjust_comps[channel]);
-          }
+          uint8_t* pPixel = pScanline + col * channel_count;
+          int src = comps.data[row * width + col] + src_offset;
+          *pPixel = static_cast<uint8_t>(src << -adjust);
+        }
+      }
+    } else if (adjust == 0) {
+      for (uint32_t row = 0; row < height; ++row) {
+        uint8_t* pScanline = pChannel + row * pitch;
+        for (uint32_t col = 0; col < width; ++col) {
+          uint8_t* pPixel = pScanline + col * channel_count;
+          int src = comps.data[row * width + col] + src_offset;
+          *pPixel = static_cast<uint8_t>(src);
         }
       }
     } else {
       for (uint32_t row = 0; row < height; ++row) {
         uint8_t* pScanline = pChannel + row * pitch;
         for (uint32_t col = 0; col < width; ++col) {
-          uint8_t* pPixel = pScanline + col * m_Image->numcomps;
-          if (!m_Image->comps[channel].data)
-            continue;
-
-          int src = m_Image->comps[channel].data[row * width + col];
-          src += m_Image->comps[channel].sgnd
-                     ? 1 << (m_Image->comps[channel].prec - 1)
-                     : 0;
-          if (adjust_comps[channel] - 1 < 0) {
-            *pPixel = static_cast<uint8_t>((src >> adjust_comps[channel]));
-          } else {
-            int tmpPixel = (src >> adjust_comps[channel]) +
-                           ((src >> (adjust_comps[channel] - 1)) % 2);
-            tmpPixel = pdfium::clamp(tmpPixel, 0, 255);
-            *pPixel = static_cast<uint8_t>(tmpPixel);
-          }
+          uint8_t* pPixel = pScanline + col * channel_count;
+          int src = comps.data[row * width + col] + src_offset;
+          int pixel = (src >> adjust) + ((src >> (adjust - 1)) % 2);
+          pixel = 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 4d9883c..c6e67c8 100644
--- a/core/fxcodec/jpx/cjpx_decoder.h
+++ b/core/fxcodec/jpx/cjpx_decoder.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,17 @@
 #ifndef CORE_FXCODEC_JPX_CJPX_DECODER_H_
 #define CORE_FXCODEC_JPX_CJPX_DECODER_H_
 
+#include <stdint.h>
+
 #include <memory>
-#include <vector>
 
 #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>
 #else
-#include "third_party/libopenjpeg20/openjpeg.h"
+#include "third_party/libopenjpeg/openjpeg.h"
 #endif
 
 namespace fxcodec {
@@ -25,6 +26,10 @@
 
 class CJPX_Decoder {
  public:
+  // Calculated as log2(2^32 / 1), where 2^32 is the largest image dimension and
+  // 1 is the smallest required size.
+  static constexpr uint8_t kMaxResolutionsToSkip = 32;
+
   enum ColorSpaceOption {
     kNoColorSpace,
     kNormalColorSpace,
@@ -34,29 +39,52 @@
   struct JpxImageInfo {
     uint32_t width;
     uint32_t height;
-    uint32_t components;
+    uint32_t channels;
+    COLOR_SPACE colorspace;
   };
 
+  static std::unique_ptr<CJPX_Decoder> Create(
+      pdfium::span<const uint8_t> src_span,
+      CJPX_Decoder::ColorSpaceOption option,
+      uint8_t resolution_levels_to_skip);
+
   static void Sycc420ToRgbForTesting(opj_image_t* img);
 
-  explicit CJPX_Decoder(ColorSpaceOption option);
   ~CJPX_Decoder();
 
-  bool Init(pdfium::span<const uint8_t> src_data);
   JpxImageInfo GetInfo() const;
   bool StartDecode();
 
-  // |swap_rgb| can only be set for images with 3 or more components.
-  bool Decode(uint8_t* dest_buf, uint32_t pitch, bool swap_rgb);
+  // `swap_rgb` can only be set when an image's color space type contains at
+  // least 3 color components. Note that this `component_count` is not
+  // equivalent to `JpxImageInfo::channels`. The JpxImageInfo channels can
+  // contain extra information for rendering the image besides the color
+  // component information. Therefore the `JpxImageInfo::channels` must be no
+  // less than the component count.
+  //
+  // Example: If a JPX image's color space type is OPJ_CLRSPC_SRGB, the
+  // component count for this color space is 3, and the channel count of its
+  // JpxImageInfo can be 4. This is because the extra channel might contain
+  // extra information, such as the transparency level of the image.
+  bool Decode(pdfium::span<uint8_t> dest_buf,
+              uint32_t pitch,
+              bool swap_rgb,
+              uint32_t component_count);
 
  private:
+  // Use Create() to instantiate.
+  explicit CJPX_Decoder(ColorSpaceOption option);
+
+  bool Init(pdfium::span<const uint8_t> src_data,
+            uint8_t resolution_levels_to_skip);
+
   const ColorSpaceOption m_ColorSpaceOption;
   pdfium::span<const uint8_t> m_SrcData;
   UnownedPtr<opj_image_t> m_Image;
   UnownedPtr<opj_codec_t> m_Codec;
   std::unique_ptr<DecodeData> m_DecodeData;
   UnownedPtr<opj_stream_t> m_Stream;
-  opj_dparameters_t m_Parameters;
+  opj_dparameters_t m_Parameters = {};
 };
 
 }  // namespace fxcodec
diff --git a/core/fxcodec/jpx/jpx_decode_utils.cpp b/core/fxcodec/jpx/jpx_decode_utils.cpp
index d3842d7..52b716f 100644
--- a/core/fxcodec/jpx/jpx_decode_utils.cpp
+++ b/core/fxcodec/jpx/jpx_decode_utils.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,12 @@
 
 #include "core/fxcodec/jpx/jpx_decode_utils.h"
 
+#include <stddef.h>
 #include <string.h>
 
 #include <algorithm>
 #include <limits>
+#include <type_traits>
 
 namespace fxcodec {
 
diff --git a/core/fxcodec/jpx/jpx_decode_utils.h b/core/fxcodec/jpx/jpx_decode_utils.h
index 4403cb8..fc3d78a 100644
--- a/core/fxcodec/jpx/jpx_decode_utils.h
+++ b/core/fxcodec/jpx/jpx_decode_utils.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,7 @@
 #if defined(USE_SYSTEM_LIBOPENJPEG2)
 #include <openjpeg.h>
 #else
-#include "third_party/libopenjpeg20/openjpeg.h"
+#include "third_party/libopenjpeg/openjpeg.h"
 #endif
 
 namespace fxcodec {
diff --git a/core/fxcodec/jpx/jpx_unittest.cpp b/core/fxcodec/jpx/jpx_unittest.cpp
index def5065..84e6a35 100644
--- a/core/fxcodec/jpx/jpx_unittest.cpp
+++ b/core/fxcodec/jpx/jpx_unittest.cpp
@@ -1,16 +1,18 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <limits.h>
 #include <stdint.h>
 
 #include <limits>
 
 #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/libopenjpeg20/opj_malloc.h"
+#include "third_party/libopenjpeg/opj_malloc.h"
 
 namespace fxcodec {
 
@@ -423,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/jpx/jpxmodule.cpp b/core/fxcodec/jpx/jpxmodule.cpp
deleted file mode 100644
index 4229dc5..0000000
--- a/core/fxcodec/jpx/jpxmodule.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/jpx/jpxmodule.h"
-
-#include "third_party/base/ptr_util.h"
-
-namespace fxcodec {
-
-// static
-std::unique_ptr<CJPX_Decoder> JpxModule::CreateDecoder(
-    pdfium::span<const uint8_t> src_span,
-    CJPX_Decoder::ColorSpaceOption option) {
-  auto decoder = pdfium::MakeUnique<CJPX_Decoder>(option);
-  if (!decoder->Init(src_span))
-    return nullptr;
-
-  return decoder;
-}
-
-}  // namespace fxcodec
diff --git a/core/fxcodec/jpx/jpxmodule.h b/core/fxcodec/jpx/jpxmodule.h
deleted file mode 100644
index 0f2fb71..0000000
--- a/core/fxcodec/jpx/jpxmodule.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_JPX_JPXMODULE_H_
-#define CORE_FXCODEC_JPX_JPXMODULE_H_
-
-#include <memory>
-#include <vector>
-
-#include "core/fxcodec/jpx/cjpx_decoder.h"
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/span.h"
-
-namespace fxcodec {
-
-class JpxModule {
- public:
-  static std::unique_ptr<CJPX_Decoder> CreateDecoder(
-      pdfium::span<const uint8_t> src_span,
-      CJPX_Decoder::ColorSpaceOption option);
-
-  JpxModule() = delete;
-  JpxModule(const JpxModule&) = delete;
-  JpxModule& operator=(const JpxModule&) = delete;
-};
-
-}  // namespace fxcodec
-
-using JpxModule = fxcodec::JpxModule;
-
-#endif  // CORE_FXCODEC_JPX_JPXMODULE_H_
diff --git a/core/fxcodec/png/DEPS b/core/fxcodec/png/DEPS
index cc73855..9caa4d8 100644
--- a/core/fxcodec/png/DEPS
+++ b/core/fxcodec/png/DEPS
@@ -1,3 +1,3 @@
 include_rules = [
-  '+third_party/libpng16/png.h',
+  '+third_party/libpng/png.h',
 ]
diff --git a/core/fxcodec/png/png_decoder.cpp b/core/fxcodec/png/png_decoder.cpp
new file mode 100644
index 0000000..3cf4221
--- /dev/null
+++ b/core/fxcodec/png/png_decoder.cpp
@@ -0,0 +1,224 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/png/png_decoder.h"
+
+#include <setjmp.h>
+#include <string.h>
+
+#include "core/fxcodec/cfx_codec_memory.h"
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcodec/fx_codec_def.h"
+#include "core/fxcrt/unowned_ptr.h"
+
+#ifdef USE_SYSTEM_LIBPNG
+#include <png.h>
+#else
+#include "third_party/libpng/png.h"
+#endif
+
+#define PNG_ERROR_SIZE 256
+
+class CPngContext final : public ProgressiveDecoderIface::Context {
+ public:
+  explicit CPngContext(PngDecoder::Delegate* pDelegate);
+  ~CPngContext() override;
+
+  png_structp m_pPng = nullptr;
+  png_infop m_pInfo = nullptr;
+  UnownedPtr<PngDecoder::Delegate> const m_pDelegate;
+  char m_szLastError[PNG_ERROR_SIZE] = {};
+};
+
+extern "C" {
+
+void _png_error_data(png_structp png_ptr, png_const_charp error_msg) {
+  if (png_get_error_ptr(png_ptr)) {
+    strncpy(static_cast<char*>(png_get_error_ptr(png_ptr)), error_msg,
+            PNG_ERROR_SIZE - 1);
+  }
+
+  longjmp(png_jmpbuf(png_ptr), 1);
+}
+
+void _png_warning_data(png_structp png_ptr, png_const_charp error_msg) {}
+
+void _png_load_bmp_attribute(png_structp png_ptr,
+                             png_infop info_ptr,
+                             CFX_DIBAttribute* pAttribute) {
+  if (pAttribute) {
+#if defined(PNG_pHYs_SUPPORTED)
+    pAttribute->m_nXDPI = png_get_x_pixels_per_meter(png_ptr, info_ptr);
+    pAttribute->m_nYDPI = png_get_y_pixels_per_meter(png_ptr, info_ptr);
+    png_uint_32 res_x, res_y;
+    int unit_type;
+    png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type);
+    switch (unit_type) {
+      case PNG_RESOLUTION_METER:
+        pAttribute->m_wDPIUnit = CFX_DIBAttribute::kResUnitMeter;
+        break;
+      default:
+        pAttribute->m_wDPIUnit = CFX_DIBAttribute::kResUnitNone;
+    }
+#endif
+#if defined(PNG_iCCP_SUPPORTED)
+    png_charp icc_name;
+    png_bytep icc_profile;
+    png_uint_32 icc_proflen;
+    int compress_type;
+    png_get_iCCP(png_ptr, info_ptr, &icc_name, &compress_type, &icc_profile,
+                 &icc_proflen);
+#endif
+#if defined(PNG_TEXT_SUPPORTED)
+    int num_text;
+    png_textp text = nullptr;
+    png_get_text(png_ptr, info_ptr, &text, &num_text);
+#endif
+  }
+}
+
+void _png_get_header_func(png_structp png_ptr, png_infop info_ptr) {
+  auto* pContext =
+      reinterpret_cast<CPngContext*>(png_get_progressive_ptr(png_ptr));
+  if (!pContext)
+    return;
+
+  png_uint_32 width = 0;
+  png_uint_32 height = 0;
+  int bpc = 0;
+  int color_type = 0;
+  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bpc, &color_type, nullptr,
+               nullptr, nullptr);
+  int color_type1 = color_type;
+  if (bpc > 8)
+    png_set_strip_16(png_ptr);
+  else if (bpc < 8)
+    png_set_expand_gray_1_2_4_to_8(png_ptr);
+
+  bpc = 8;
+  if (color_type == PNG_COLOR_TYPE_PALETTE)
+    png_set_palette_to_rgb(png_ptr);
+
+  int pass = png_set_interlace_handling(png_ptr);
+  double gamma = 1.0;
+  if (!pContext->m_pDelegate->PngReadHeader(width, height, bpc, pass,
+                                            &color_type, &gamma)) {
+    png_error(pContext->m_pPng, "Read Header Callback Error");
+  }
+  int intent;
+  if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
+    png_set_gamma(png_ptr, gamma, 0.45455);
+  } else {
+    double image_gamma;
+    if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))
+      png_set_gamma(png_ptr, gamma, image_gamma);
+    else
+      png_set_gamma(png_ptr, gamma, 0.45455);
+  }
+  switch (color_type) {
+    case PNG_COLOR_TYPE_GRAY:
+    case PNG_COLOR_TYPE_GRAY_ALPHA: {
+      if (color_type1 & PNG_COLOR_MASK_COLOR) {
+        png_set_rgb_to_gray(png_ptr, 1, 0.299, 0.587);
+      }
+    } break;
+    case PNG_COLOR_TYPE_PALETTE:
+      if (color_type1 != PNG_COLOR_TYPE_PALETTE) {
+        png_error(pContext->m_pPng, "Not Support Output Palette Now");
+      }
+      [[fallthrough]];
+    case PNG_COLOR_TYPE_RGB:
+    case PNG_COLOR_TYPE_RGB_ALPHA:
+      if (!(color_type1 & PNG_COLOR_MASK_COLOR)) {
+        png_set_gray_to_rgb(png_ptr);
+      }
+      png_set_bgr(png_ptr);
+      break;
+  }
+  if (!(color_type & PNG_COLOR_MASK_ALPHA))
+    png_set_strip_alpha(png_ptr);
+
+  if (color_type & PNG_COLOR_MASK_ALPHA &&
+      !(color_type1 & PNG_COLOR_MASK_ALPHA)) {
+    png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+  }
+  png_read_update_info(png_ptr, info_ptr);
+}
+
+void _png_get_end_func(png_structp png_ptr, png_infop info_ptr) {}
+
+void _png_get_row_func(png_structp png_ptr,
+                       png_bytep new_row,
+                       png_uint_32 row_num,
+                       int pass) {
+  auto* pContext =
+      reinterpret_cast<CPngContext*>(png_get_progressive_ptr(png_ptr));
+  if (!pContext)
+    return;
+
+  uint8_t* src_buf;
+  if (!pContext->m_pDelegate->PngAskScanlineBuf(row_num, &src_buf))
+    png_error(png_ptr, "Ask Scanline buffer Callback Error");
+
+  if (src_buf)
+    png_progressive_combine_row(png_ptr, src_buf, new_row);
+
+  pContext->m_pDelegate->PngFillScanlineBufCompleted(pass, row_num);
+}
+
+}  // extern "C"
+
+CPngContext::CPngContext(PngDecoder::Delegate* pDelegate)
+    : m_pDelegate(pDelegate) {}
+
+CPngContext::~CPngContext() {
+  png_destroy_read_struct(m_pPng ? &m_pPng : nullptr,
+                          m_pInfo ? &m_pInfo : nullptr, nullptr);
+}
+
+namespace fxcodec {
+
+// static
+std::unique_ptr<ProgressiveDecoderIface::Context> PngDecoder::StartDecode(
+    Delegate* pDelegate) {
+  auto p = std::make_unique<CPngContext>(pDelegate);
+  p->m_pPng =
+      png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+  if (!p->m_pPng)
+    return nullptr;
+
+  p->m_pInfo = png_create_info_struct(p->m_pPng);
+  if (!p->m_pInfo)
+    return nullptr;
+
+  if (setjmp(png_jmpbuf(p->m_pPng)))
+    return nullptr;
+
+  png_set_progressive_read_fn(p->m_pPng, p.get(), _png_get_header_func,
+                              _png_get_row_func, _png_get_end_func);
+  png_set_error_fn(p->m_pPng, p->m_szLastError, _png_error_data,
+                   _png_warning_data);
+  return p;
+}
+
+// static
+bool PngDecoder::ContinueDecode(ProgressiveDecoderIface::Context* pContext,
+                                RetainPtr<CFX_CodecMemory> codec_memory,
+                                CFX_DIBAttribute* pAttribute) {
+  auto* ctx = static_cast<CPngContext*>(pContext);
+  if (setjmp(png_jmpbuf(ctx->m_pPng))) {
+    if (pAttribute &&
+        strcmp(ctx->m_szLastError, "Read Header Callback Error") == 0) {
+      _png_load_bmp_attribute(ctx->m_pPng, ctx->m_pInfo, pAttribute);
+    }
+    return false;
+  }
+  pdfium::span<uint8_t> src_buf = codec_memory->GetUnconsumedSpan();
+  png_process_data(ctx->m_pPng, ctx->m_pInfo, src_buf.data(), src_buf.size());
+  return true;
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/png/png_decoder.h b/core/fxcodec/png/png_decoder.h
new file mode 100644
index 0000000..664ea44
--- /dev/null
+++ b/core/fxcodec/png/png_decoder.h
@@ -0,0 +1,57 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_PNG_PNG_DECODER_H_
+#define CORE_FXCODEC_PNG_PNG_DECODER_H_
+
+#include <memory>
+
+#include "core/fxcodec/progressive_decoder_iface.h"
+#include "core/fxcrt/retain_ptr.h"
+
+#ifndef PDF_ENABLE_XFA_PNG
+#error "PNG must be enabled"
+#endif
+
+namespace fxcodec {
+
+class CFX_DIBAttribute;
+
+class PngDecoder {
+ public:
+  class Delegate {
+   public:
+    virtual bool PngReadHeader(int width,
+                               int height,
+                               int bpc,
+                               int pass,
+                               int* color_type,
+                               double* gamma) = 0;
+
+    // Returns true on success. |pSrcBuf| will be set if this succeeds.
+    // |pSrcBuf| does not take ownership of the buffer.
+    virtual bool PngAskScanlineBuf(int line, uint8_t** pSrcBuf) = 0;
+
+    virtual void PngFillScanlineBufCompleted(int pass, int line) = 0;
+  };
+
+  static std::unique_ptr<ProgressiveDecoderIface::Context> StartDecode(
+      Delegate* pDelegate);
+
+  static bool ContinueDecode(ProgressiveDecoderIface::Context* pContext,
+                             RetainPtr<CFX_CodecMemory> codec_memory,
+                             CFX_DIBAttribute* pAttribute);
+
+  PngDecoder() = delete;
+  PngDecoder(const PngDecoder&) = delete;
+  PngDecoder& operator=(const PngDecoder&) = delete;
+};
+
+}  // namespace fxcodec
+
+using PngDecoder = fxcodec::PngDecoder;
+
+#endif  // CORE_FXCODEC_PNG_PNG_DECODER_H_
diff --git a/core/fxcodec/png/pngmodule.cpp b/core/fxcodec/png/pngmodule.cpp
deleted file mode 100644
index d3a3c0b..0000000
--- a/core/fxcodec/png/pngmodule.cpp
+++ /dev/null
@@ -1,233 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/png/pngmodule.h"
-
-#include <algorithm>
-
-#include "core/fxcodec/cfx_codec_memory.h"
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/ptr_util.h"
-
-#ifdef USE_SYSTEM_LIBPNG
-#include <png.h>
-#else
-#include "third_party/libpng16/png.h"
-#endif
-
-#define PNG_ERROR_SIZE 256
-
-class CPngContext final : public ModuleIface::Context {
- public:
-  explicit CPngContext(PngModule::Delegate* pDelegate);
-  ~CPngContext() override;
-
-  png_structp m_pPng = nullptr;
-  png_infop m_pInfo = nullptr;
-  UnownedPtr<PngModule::Delegate> const m_pDelegate;
-  char m_szLastError[PNG_ERROR_SIZE];
-};
-
-extern "C" {
-
-void _png_error_data(png_structp png_ptr, png_const_charp error_msg) {
-  if (png_get_error_ptr(png_ptr)) {
-    strncpy(static_cast<char*>(png_get_error_ptr(png_ptr)), error_msg,
-            PNG_ERROR_SIZE - 1);
-  }
-
-  longjmp(png_jmpbuf(png_ptr), 1);
-}
-
-void _png_warning_data(png_structp png_ptr, png_const_charp error_msg) {}
-
-void _png_load_bmp_attribute(png_structp png_ptr,
-                             png_infop info_ptr,
-                             CFX_DIBAttribute* pAttribute) {
-  if (pAttribute) {
-#if defined(PNG_pHYs_SUPPORTED)
-    pAttribute->m_nXDPI = png_get_x_pixels_per_meter(png_ptr, info_ptr);
-    pAttribute->m_nYDPI = png_get_y_pixels_per_meter(png_ptr, info_ptr);
-    png_uint_32 res_x, res_y;
-    int unit_type;
-    png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type);
-    switch (unit_type) {
-      case PNG_RESOLUTION_METER:
-        pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_METER;
-        break;
-      default:
-        pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_NONE;
-    }
-#endif
-#if defined(PNG_iCCP_SUPPORTED)
-    png_charp icc_name;
-    png_bytep icc_profile;
-    png_uint_32 icc_proflen;
-    int compress_type;
-    png_get_iCCP(png_ptr, info_ptr, &icc_name, &compress_type, &icc_profile,
-                 &icc_proflen);
-#endif
-#if defined(PNG_TEXT_SUPPORTED)
-    int num_text;
-    png_textp text = nullptr;
-    png_get_text(png_ptr, info_ptr, &text, &num_text);
-#endif
-  }
-}
-
-void _png_get_header_func(png_structp png_ptr, png_infop info_ptr) {
-  auto* pContext =
-      reinterpret_cast<CPngContext*>(png_get_progressive_ptr(png_ptr));
-  if (!pContext)
-    return;
-
-  png_uint_32 width = 0;
-  png_uint_32 height = 0;
-  int bpc = 0;
-  int color_type = 0;
-  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bpc, &color_type, nullptr,
-               nullptr, nullptr);
-  int color_type1 = color_type;
-  if (bpc > 8)
-    png_set_strip_16(png_ptr);
-  else if (bpc < 8)
-    png_set_expand_gray_1_2_4_to_8(png_ptr);
-
-  bpc = 8;
-  if (color_type == PNG_COLOR_TYPE_PALETTE)
-    png_set_palette_to_rgb(png_ptr);
-
-  int pass = png_set_interlace_handling(png_ptr);
-  double gamma = 1.0;
-  if (!pContext->m_pDelegate->PngReadHeader(width, height, bpc, pass,
-                                            &color_type, &gamma)) {
-    png_error(pContext->m_pPng, "Read Header Callback Error");
-  }
-  int intent;
-  if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
-    png_set_gamma(png_ptr, gamma, 0.45455);
-  } else {
-    double image_gamma;
-    if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))
-      png_set_gamma(png_ptr, gamma, image_gamma);
-    else
-      png_set_gamma(png_ptr, gamma, 0.45455);
-  }
-  switch (color_type) {
-    case PNG_COLOR_TYPE_GRAY:
-    case PNG_COLOR_TYPE_GRAY_ALPHA: {
-      if (color_type1 & PNG_COLOR_MASK_COLOR) {
-        png_set_rgb_to_gray(png_ptr, 1, 0.299, 0.587);
-      }
-    } break;
-    case PNG_COLOR_TYPE_PALETTE:
-      if (color_type1 != PNG_COLOR_TYPE_PALETTE) {
-        png_error(pContext->m_pPng, "Not Support Output Palette Now");
-      }
-      FALLTHROUGH;
-    case PNG_COLOR_TYPE_RGB:
-    case PNG_COLOR_TYPE_RGB_ALPHA:
-      if (!(color_type1 & PNG_COLOR_MASK_COLOR)) {
-        png_set_gray_to_rgb(png_ptr);
-      }
-      png_set_bgr(png_ptr);
-      break;
-  }
-  if (!(color_type & PNG_COLOR_MASK_ALPHA))
-    png_set_strip_alpha(png_ptr);
-
-  if (color_type & PNG_COLOR_MASK_ALPHA &&
-      !(color_type1 & PNG_COLOR_MASK_ALPHA)) {
-    png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
-  }
-  png_read_update_info(png_ptr, info_ptr);
-}
-
-void _png_get_end_func(png_structp png_ptr, png_infop info_ptr) {}
-
-void _png_get_row_func(png_structp png_ptr,
-                       png_bytep new_row,
-                       png_uint_32 row_num,
-                       int pass) {
-  auto* pContext =
-      reinterpret_cast<CPngContext*>(png_get_progressive_ptr(png_ptr));
-  if (!pContext)
-    return;
-
-  uint8_t* src_buf;
-  if (!pContext->m_pDelegate->PngAskScanlineBuf(row_num, &src_buf))
-    png_error(png_ptr, "Ask Scanline buffer Callback Error");
-
-  if (src_buf)
-    png_progressive_combine_row(png_ptr, src_buf, new_row);
-
-  pContext->m_pDelegate->PngFillScanlineBufCompleted(pass, row_num);
-}
-
-}  // extern "C"
-
-CPngContext::CPngContext(PngModule::Delegate* pDelegate)
-    : m_pDelegate(pDelegate) {
-  memset(m_szLastError, 0, sizeof(m_szLastError));
-}
-
-CPngContext::~CPngContext() {
-  png_destroy_read_struct(m_pPng ? &m_pPng : nullptr,
-                          m_pInfo ? &m_pInfo : nullptr, nullptr);
-}
-
-namespace fxcodec {
-
-PngModule::PngModule() = default;
-
-PngModule::~PngModule() = default;
-
-std::unique_ptr<ModuleIface::Context> PngModule::Start(Delegate* pDelegate) {
-  auto p = pdfium::MakeUnique<CPngContext>(pDelegate);
-  p->m_pPng =
-      png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
-  if (!p->m_pPng)
-    return nullptr;
-
-  p->m_pInfo = png_create_info_struct(p->m_pPng);
-  if (!p->m_pInfo)
-    return nullptr;
-
-  if (setjmp(png_jmpbuf(p->m_pPng)))
-    return nullptr;
-
-  png_set_progressive_read_fn(p->m_pPng, p.get(), _png_get_header_func,
-                              _png_get_row_func, _png_get_end_func);
-  png_set_error_fn(p->m_pPng, p->m_szLastError, _png_error_data,
-                   _png_warning_data);
-  return p;
-}
-
-FX_FILESIZE PngModule::GetAvailInput(Context* pContext) const {
-  NOTREACHED();
-  return 0;
-}
-
-bool PngModule::Input(Context* pContext,
-                      RetainPtr<CFX_CodecMemory> codec_memory,
-                      CFX_DIBAttribute* pAttribute) {
-  auto* ctx = static_cast<CPngContext*>(pContext);
-  if (setjmp(png_jmpbuf(ctx->m_pPng))) {
-    if (pAttribute &&
-        strcmp(ctx->m_szLastError, "Read Header Callback Error") == 0) {
-      _png_load_bmp_attribute(ctx->m_pPng, ctx->m_pInfo, pAttribute);
-    }
-    return false;
-  }
-  pdfium::span<uint8_t> src_buf = codec_memory->GetSpan();
-  png_process_data(ctx->m_pPng, ctx->m_pInfo, src_buf.data(), src_buf.size());
-  return true;
-}
-
-}  // namespace fxcodec
diff --git a/core/fxcodec/png/pngmodule.h b/core/fxcodec/png/pngmodule.h
deleted file mode 100644
index ad3fd39..0000000
--- a/core/fxcodec/png/pngmodule.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_PNG_PNGMODULE_H_
-#define CORE_FXCODEC_PNG_PNGMODULE_H_
-
-#include <memory>
-
-#include "core/fxcodec/codec_module_iface.h"
-
-namespace fxcodec {
-
-class PngModule final : public ModuleIface {
- public:
-  class Delegate {
-   public:
-    virtual bool PngReadHeader(int width,
-                               int height,
-                               int bpc,
-                               int pass,
-                               int* color_type,
-                               double* gamma) = 0;
-
-    // Returns true on success. |pSrcBuf| will be set if this succeeds.
-    // |pSrcBuf| does not take ownership of the buffer.
-    virtual bool PngAskScanlineBuf(int line, uint8_t** pSrcBuf) = 0;
-
-    virtual void PngFillScanlineBufCompleted(int pass, int line) = 0;
-  };
-
-  PngModule();
-  ~PngModule() override;
-
-  // ModuleIface:
-  FX_FILESIZE GetAvailInput(Context* pContext) const override;
-  bool Input(Context* pContext,
-             RetainPtr<CFX_CodecMemory> codec_memory,
-             CFX_DIBAttribute* pAttribute) override;
-
-  std::unique_ptr<Context> Start(Delegate* pDelegate);
-};
-
-}  // namespace fxcodec
-
-using PngModule = fxcodec::PngModule;
-
-#endif  // CORE_FXCODEC_PNG_PNGMODULE_H_
diff --git a/core/fxcodec/progressive_decoder.cpp b/core/fxcodec/progressive_decoder.cpp
new file mode 100644
index 0000000..8e84500
--- /dev/null
+++ b/core/fxcodec/progressive_decoder.cpp
@@ -0,0 +1,2191 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/progressive_decoder.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "build/build_config.h"
+#include "core/fxcodec/cfx_codec_memory.h"
+#include "core/fxcodec/jpeg/jpeg_progressive_decoder.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxge/dib/cfx_cmyk_to_srgb.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/notreached.h"
+#include "third_party/base/numerics/safe_conversions.h"
+
+#ifdef PDF_ENABLE_XFA_BMP
+#include "core/fxcodec/bmp/bmp_progressive_decoder.h"
+#endif  // PDF_ENABLE_XFA_BMP
+
+#ifdef PDF_ENABLE_XFA_GIF
+#include "core/fxcodec/gif/gif_progressive_decoder.h"
+#endif  // PDF_ENABLE_XFA_GIF
+
+#ifdef PDF_ENABLE_XFA_TIFF
+#include "core/fxcodec/tiff/tiff_decoder.h"
+#endif  // PDF_ENABLE_XFA_TIFF
+
+namespace fxcodec {
+
+namespace {
+
+constexpr size_t kBlockSize = 4096;
+
+#ifdef PDF_ENABLE_XFA_PNG
+#if BUILDFLAG(IS_APPLE)
+const double kPngGamma = 1.7;
+#else
+const double kPngGamma = 2.2;
+#endif  // BUILDFLAG(IS_APPLE)
+#endif  // PDF_ENABLE_XFA_PNG
+
+void RGB2BGR(uint8_t* buffer, int width = 1) {
+  if (buffer && width > 0) {
+    uint8_t temp;
+    int i = 0;
+    int j = 0;
+    for (; i < width; i++, j += 3) {
+      temp = buffer[j];
+      buffer[j] = buffer[j + 2];
+      buffer[j + 2] = temp;
+    }
+  }
+}
+
+}  // namespace
+
+ProgressiveDecoder::HorzTable::HorzTable() = default;
+
+ProgressiveDecoder::HorzTable::~HorzTable() = default;
+
+void ProgressiveDecoder::HorzTable::CalculateWeights(int dest_len,
+                                                     int src_len) {
+  CHECK_GE(dest_len, 0);
+  m_ItemSize =
+      pdfium::base::checked_cast<int>(PixelWeight::TotalBytesForWeightCount(2));
+  FX_SAFE_SIZE_T safe_size = m_ItemSize;
+  safe_size *= dest_len;
+  m_pWeightTables.resize(safe_size.ValueOrDie(), 0);
+  double scale = (double)dest_len / (double)src_len;
+  if (scale > 1) {
+    int pre_dest_col = 0;
+    for (int src_col = 0; src_col < src_len; src_col++) {
+      double dest_col_f = src_col * scale;
+      int dest_col = FXSYS_roundf((float)dest_col_f);
+      PixelWeight* pWeight = GetPixelWeight(dest_col);
+      pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col;
+      pWeight->m_Weights[0] = CStretchEngine::kFixedPointOne;
+      pWeight->m_Weights[1] = 0;
+      if (src_col == src_len - 1 && dest_col < dest_len - 1) {
+        for (int dest_col_index = pre_dest_col + 1; dest_col_index < dest_len;
+             dest_col_index++) {
+          pWeight = GetPixelWeight(dest_col_index);
+          pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col;
+          pWeight->m_Weights[0] = CStretchEngine::kFixedPointOne;
+          pWeight->m_Weights[1] = 0;
+        }
+        return;
+      }
+      int dest_col_len = dest_col - pre_dest_col;
+      for (int dest_col_index = pre_dest_col + 1; dest_col_index < dest_col;
+           dest_col_index++) {
+        pWeight = GetPixelWeight(dest_col_index);
+        pWeight->m_SrcStart = src_col - 1;
+        pWeight->m_SrcEnd = src_col;
+        pWeight->m_Weights[0] = CStretchEngine::FixedFromFloat(
+            ((float)dest_col - (float)dest_col_index) / (float)dest_col_len);
+        pWeight->m_Weights[1] =
+            CStretchEngine::kFixedPointOne - pWeight->m_Weights[0];
+      }
+      pre_dest_col = dest_col;
+    }
+    return;
+  }
+  for (int dest_col = 0; dest_col < dest_len; dest_col++) {
+    double src_col_f = dest_col / scale;
+    int src_col = FXSYS_roundf((float)src_col_f);
+    PixelWeight* pWeight = GetPixelWeight(dest_col);
+    pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col;
+    pWeight->m_Weights[0] = CStretchEngine::kFixedPointOne;
+    pWeight->m_Weights[1] = 0;
+  }
+}
+
+ProgressiveDecoder::VertTable::VertTable() = default;
+
+ProgressiveDecoder::VertTable::~VertTable() = default;
+
+void ProgressiveDecoder::VertTable::CalculateWeights(int dest_len,
+                                                     int src_len) {
+  CHECK_GE(dest_len, 0);
+  m_ItemSize =
+      pdfium::base::checked_cast<int>(PixelWeight::TotalBytesForWeightCount(2));
+  FX_SAFE_SIZE_T safe_size = m_ItemSize;
+  safe_size *= dest_len;
+  m_pWeightTables.resize(safe_size.ValueOrDie(), 0);
+  double scale = (double)dest_len / (double)src_len;
+  if (scale <= 1) {
+    for (int dest_row = 0; dest_row < dest_len; dest_row++) {
+      PixelWeight* pWeight = GetPixelWeight(dest_row);
+      pWeight->m_SrcStart = dest_row;
+      pWeight->m_SrcEnd = dest_row;
+      pWeight->m_Weights[0] = CStretchEngine::kFixedPointOne;
+      pWeight->m_Weights[1] = 0;
+    }
+    return;
+  }
+
+  double step = 0.0;
+  int src_row = 0;
+  while (step < (double)dest_len) {
+    int start_step = (int)step;
+    step = scale * (++src_row);
+    int end_step = (int)step;
+    if (end_step >= dest_len) {
+      end_step = dest_len;
+      for (int dest_row = start_step; dest_row < end_step; dest_row++) {
+        PixelWeight* pWeight = GetPixelWeight(dest_row);
+        pWeight->m_SrcStart = start_step;
+        pWeight->m_SrcEnd = start_step;
+        pWeight->m_Weights[0] = CStretchEngine::kFixedPointOne;
+        pWeight->m_Weights[1] = 0;
+      }
+      return;
+    }
+    int length = end_step - start_step;
+    {
+      PixelWeight* pWeight = GetPixelWeight(start_step);
+      pWeight->m_SrcStart = start_step;
+      pWeight->m_SrcEnd = start_step;
+      pWeight->m_Weights[0] = CStretchEngine::kFixedPointOne;
+      pWeight->m_Weights[1] = 0;
+    }
+    for (int dest_row = start_step + 1; dest_row < end_step; dest_row++) {
+      PixelWeight* pWeight = GetPixelWeight(dest_row);
+      pWeight->m_SrcStart = start_step;
+      pWeight->m_SrcEnd = end_step;
+      pWeight->m_Weights[0] = CStretchEngine::FixedFromFloat(
+          (float)(end_step - dest_row) / (float)length);
+      pWeight->m_Weights[1] =
+          CStretchEngine::kFixedPointOne - pWeight->m_Weights[0];
+    }
+  }
+}
+
+ProgressiveDecoder::ProgressiveDecoder() = default;
+
+ProgressiveDecoder::~ProgressiveDecoder() = default;
+
+#ifdef PDF_ENABLE_XFA_PNG
+bool ProgressiveDecoder::PngReadHeader(int width,
+                                       int height,
+                                       int bpc,
+                                       int pass,
+                                       int* color_type,
+                                       double* gamma) {
+  if (!m_pDeviceBitmap) {
+    m_SrcWidth = width;
+    m_SrcHeight = height;
+    m_SrcBPC = bpc;
+    m_SrcPassNumber = pass;
+    switch (*color_type) {
+      case 0:
+        m_SrcComponents = 1;
+        break;
+      case 4:
+        m_SrcComponents = 2;
+        break;
+      case 2:
+        m_SrcComponents = 3;
+        break;
+      case 3:
+      case 6:
+        m_SrcComponents = 4;
+        break;
+      default:
+        m_SrcComponents = 0;
+        break;
+    }
+    m_clipBox = FX_RECT(0, 0, width, height);
+    return false;
+  }
+  FXDIB_Format format = m_pDeviceBitmap->GetFormat();
+  switch (format) {
+    case FXDIB_Format::k1bppMask:
+    case FXDIB_Format::k1bppRgb:
+      NOTREACHED_NORETURN();
+    case FXDIB_Format::k8bppMask:
+    case FXDIB_Format::k8bppRgb:
+      *color_type = 0;
+      break;
+    case FXDIB_Format::kRgb:
+      *color_type = 2;
+      break;
+    case FXDIB_Format::kRgb32:
+    case FXDIB_Format::kArgb:
+      *color_type = 6;
+      break;
+    default:
+      NOTREACHED_NORETURN();
+  }
+  *gamma = kPngGamma;
+  return true;
+}
+
+bool ProgressiveDecoder::PngAskScanlineBuf(int line, uint8_t** pSrcBuf) {
+  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
+  CHECK(pDIBitmap);
+  if (line < m_clipBox.top || line >= m_clipBox.bottom)
+    return true;
+
+  double scale_y = static_cast<double>(m_sizeY) / m_clipBox.Height();
+  int32_t row =
+      static_cast<int32_t>((line - m_clipBox.top) * scale_y) + m_startY;
+  *pSrcBuf = m_DecodeBuf.data();
+  int32_t src_Bpp = pDIBitmap->GetBPP() >> 3;
+  int32_t dest_Bpp = (m_SrcFormat & 0xff) >> 3;
+  int32_t src_left = m_startX;
+  int32_t dest_left = m_clipBox.left;
+  pdfium::span<const uint8_t> src_span =
+      pDIBitmap->GetScanline(row).subspan(src_left * src_Bpp);
+  pdfium::span<uint8_t> dest_span =
+      pdfium::make_span(m_DecodeBuf).subspan(dest_left * dest_Bpp);
+  const uint8_t* src_scan = src_span.data();
+  uint8_t* dest_scan = dest_span.data();
+  switch (pDIBitmap->GetFormat()) {
+    case FXDIB_Format::k1bppMask:
+    case FXDIB_Format::k1bppRgb:
+      for (int32_t src_col = 0; src_col < m_sizeX; src_col++) {
+        PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(src_col);
+        if (pPixelWeights->m_SrcStart != pPixelWeights->m_SrcEnd)
+          continue;
+        NOTREACHED_NORETURN();
+      }
+      return true;
+    case FXDIB_Format::k8bppMask:
+    case FXDIB_Format::k8bppRgb:
+      if (pDIBitmap->HasPalette())
+        return false;
+      for (int32_t src_col = 0; src_col < m_sizeX; src_col++) {
+        PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(src_col);
+        if (pPixelWeights->m_SrcStart != pPixelWeights->m_SrcEnd)
+          continue;
+        uint32_t dest_g = pPixelWeights->m_Weights[0] * src_scan[src_col];
+        dest_scan[pPixelWeights->m_SrcStart] =
+            CStretchEngine::PixelFromFixed(dest_g);
+      }
+      return true;
+    case FXDIB_Format::kRgb:
+    case FXDIB_Format::kRgb32:
+      for (int32_t src_col = 0; src_col < m_sizeX; src_col++) {
+        PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(src_col);
+        if (pPixelWeights->m_SrcStart != pPixelWeights->m_SrcEnd)
+          continue;
+        const uint8_t* p = src_scan + src_col * src_Bpp;
+        uint32_t dest_b = pPixelWeights->m_Weights[0] * (*p++);
+        uint32_t dest_g = pPixelWeights->m_Weights[0] * (*p++);
+        uint32_t dest_r = pPixelWeights->m_Weights[0] * (*p);
+        uint8_t* pDes = &dest_scan[pPixelWeights->m_SrcStart * dest_Bpp];
+        *pDes++ = CStretchEngine::PixelFromFixed(dest_b);
+        *pDes++ = CStretchEngine::PixelFromFixed(dest_g);
+        *pDes = CStretchEngine::PixelFromFixed(dest_r);
+      }
+      return true;
+    case FXDIB_Format::kArgb:
+      for (int32_t src_col = 0; src_col < m_sizeX; src_col++) {
+        PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(src_col);
+        if (pPixelWeights->m_SrcStart != pPixelWeights->m_SrcEnd)
+          continue;
+        const uint8_t* p = src_scan + src_col * src_Bpp;
+        uint32_t dest_b = pPixelWeights->m_Weights[0] * (*p++);
+        uint32_t dest_g = pPixelWeights->m_Weights[0] * (*p++);
+        uint32_t dest_r = pPixelWeights->m_Weights[0] * (*p++);
+        uint8_t dest_a = *p;
+        uint8_t* pDes = &dest_scan[pPixelWeights->m_SrcStart * dest_Bpp];
+        *pDes++ = CStretchEngine::PixelFromFixed(dest_b);
+        *pDes++ = CStretchEngine::PixelFromFixed(dest_g);
+        *pDes++ = CStretchEngine::PixelFromFixed(dest_r);
+        *pDes = dest_a;
+      }
+      return true;
+    default:
+      return false;
+  }
+}
+
+void ProgressiveDecoder::PngFillScanlineBufCompleted(int pass, int line) {
+  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
+  DCHECK(pDIBitmap);
+  int src_top = m_clipBox.top;
+  int src_bottom = m_clipBox.bottom;
+  int dest_top = m_startY;
+  int src_height = m_clipBox.Height();
+  int dest_height = m_sizeY;
+  if (line >= src_top && line < src_bottom) {
+    double scale_y = static_cast<double>(dest_height) / src_height;
+    int src_row = line - src_top;
+    int dest_row = (int)(src_row * scale_y) + dest_top;
+    if (dest_row >= dest_top + dest_height) {
+      return;
+    }
+    PngOneOneMapResampleHorz(pDIBitmap, dest_row, m_DecodeBuf, m_SrcFormat);
+    if (m_SrcPassNumber == 1 && scale_y > 1.0) {
+      ResampleVert(pDIBitmap, scale_y, dest_row);
+      return;
+    }
+    if (pass == 6 && scale_y > 1.0) {
+      ResampleVert(pDIBitmap, scale_y, dest_row);
+    }
+  }
+}
+#endif  // PDF_ENABLE_XFA_PNG
+
+#ifdef PDF_ENABLE_XFA_GIF
+uint32_t ProgressiveDecoder::GifCurrentPosition() const {
+  uint32_t remain_size = pdfium::base::checked_cast<uint32_t>(
+      GifDecoder::GetAvailInput(m_pGifContext.get()));
+  return m_offSet - remain_size;
+}
+
+bool ProgressiveDecoder::GifInputRecordPositionBuf(uint32_t rcd_pos,
+                                                   const FX_RECT& img_rc,
+                                                   int32_t pal_num,
+                                                   CFX_GifPalette* pal_ptr,
+                                                   int32_t trans_index,
+                                                   bool interlace) {
+  m_offSet = rcd_pos;
+
+  FXCODEC_STATUS error_status = FXCODEC_STATUS::kError;
+  m_pCodecMemory->Seek(m_pCodecMemory->GetSize());
+  if (!GifReadMoreData(&error_status))
+    return false;
+
+  CFX_GifPalette* pPalette = nullptr;
+  if (pal_num != 0 && pal_ptr) {
+    pPalette = pal_ptr;
+  } else {
+    if (!m_pGifPalette)
+      return false;
+    pal_num = m_GifPltNumber;
+    pPalette = m_pGifPalette;
+  }
+  m_SrcPalette.resize(pal_num);
+  m_SrcPaletteNumber = pal_num;
+  for (int i = 0; i < pal_num; i++) {
+    m_SrcPalette[i] =
+        ArgbEncode(0xff, pPalette[i].r, pPalette[i].g, pPalette[i].b);
+  }
+  m_GifTransIndex = trans_index;
+  m_GifFrameRect = img_rc;
+  m_SrcPassNumber = interlace ? 4 : 1;
+  int32_t pal_index = m_GifBgIndex;
+  RetainPtr<CFX_DIBitmap> pDevice = m_pDeviceBitmap;
+  if (trans_index >= pal_num)
+    trans_index = -1;
+  if (trans_index != -1) {
+    m_SrcPalette[trans_index] &= 0x00ffffff;
+    if (pDevice->IsAlphaFormat())
+      pal_index = trans_index;
+  }
+  if (pal_index >= pal_num)
+    return false;
+
+  int startX = m_startX;
+  int startY = m_startY;
+  int sizeX = m_sizeX;
+  int sizeY = m_sizeY;
+  int Bpp = pDevice->GetBPP() / 8;
+  FX_ARGB argb = m_SrcPalette[pal_index];
+  for (int row = 0; row < sizeY; row++) {
+    uint8_t* pScanline =
+        pDevice->GetWritableScanline(row + startY).subspan(startX * Bpp).data();
+    switch (m_TransMethod) {
+      case 3: {
+        uint8_t gray =
+            FXRGB2GRAY(FXARGB_R(argb), FXARGB_G(argb), FXARGB_B(argb));
+        memset(pScanline, gray, sizeX);
+        break;
+      }
+      case 8: {
+        for (int col = 0; col < sizeX; col++) {
+          *pScanline++ = FXARGB_B(argb);
+          *pScanline++ = FXARGB_G(argb);
+          *pScanline++ = FXARGB_R(argb);
+          pScanline += Bpp - 3;
+        }
+        break;
+      }
+      case 12: {
+        for (int col = 0; col < sizeX; col++) {
+          FXARGB_SETDIB(pScanline, argb);
+          pScanline += 4;
+        }
+        break;
+      }
+    }
+  }
+  return true;
+}
+
+void ProgressiveDecoder::GifReadScanline(int32_t row_num,
+                                         pdfium::span<uint8_t> row_buf) {
+  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
+  DCHECK(pDIBitmap);
+  int32_t img_width = m_GifFrameRect.Width();
+  if (!pDIBitmap->IsAlphaFormat()) {
+    pdfium::span<uint8_t> byte_span = row_buf;
+    for (int i = 0; i < img_width; i++) {
+      if (byte_span.front() == m_GifTransIndex) {
+        byte_span.front() = m_GifBgIndex;
+      }
+      byte_span = byte_span.subspan(1);
+    }
+  }
+  int32_t pal_index = m_GifBgIndex;
+  if (m_GifTransIndex != -1 && m_pDeviceBitmap->IsAlphaFormat()) {
+    pal_index = m_GifTransIndex;
+  }
+  const int32_t left = m_GifFrameRect.left;
+  const pdfium::span<uint8_t> decode_span = m_DecodeBuf;
+  fxcrt::spanset(decode_span.first(m_SrcWidth), pal_index);
+  fxcrt::spancpy(decode_span.subspan(left), row_buf.first(img_width));
+
+  bool bLastPass = (row_num % 2) == 1;
+  int32_t line = row_num + m_GifFrameRect.top;
+  int src_top = m_clipBox.top;
+  int src_bottom = m_clipBox.bottom;
+  int dest_top = m_startY;
+  int src_height = m_clipBox.Height();
+  int dest_height = m_sizeY;
+  if (line < src_top || line >= src_bottom)
+    return;
+
+  double scale_y = static_cast<double>(dest_height) / src_height;
+  int src_row = line - src_top;
+  int dest_row = (int)(src_row * scale_y) + dest_top;
+  if (dest_row >= dest_top + dest_height)
+    return;
+
+  ResampleScanline(pDIBitmap, dest_row, decode_span, m_SrcFormat);
+  if (scale_y > 1.0 && m_SrcPassNumber == 1) {
+    ResampleVert(pDIBitmap, scale_y, dest_row);
+    return;
+  }
+  if (scale_y <= 1.0)
+    return;
+
+  int dest_bottom = dest_top + m_sizeY;
+  int dest_Bpp = pDIBitmap->GetBPP() >> 3;
+  uint32_t dest_ScanOffset = m_startX * dest_Bpp;
+  if (dest_row + (int)scale_y >= dest_bottom - 1) {
+    const uint8_t* scan_src =
+        pDIBitmap->GetScanline(dest_row).subspan(dest_ScanOffset).data();
+    int cur_row = dest_row;
+    while (++cur_row < dest_bottom) {
+      uint8_t* scan_des = pDIBitmap->GetWritableScanline(cur_row)
+                              .subspan(dest_ScanOffset)
+                              .data();
+      uint32_t size = m_sizeX * dest_Bpp;
+      memmove(scan_des, scan_src, size);
+    }
+  }
+  if (bLastPass)
+    GifDoubleLineResampleVert(pDIBitmap, scale_y, dest_row);
+}
+#endif  // PDF_ENABLE_XFA_GIF
+
+#ifdef PDF_ENABLE_XFA_BMP
+bool ProgressiveDecoder::BmpInputImagePositionBuf(uint32_t rcd_pos) {
+  m_offSet = rcd_pos;
+  FXCODEC_STATUS error_status = FXCODEC_STATUS::kError;
+  return BmpReadMoreData(m_pBmpContext.get(), &error_status);
+}
+
+void ProgressiveDecoder::BmpReadScanline(uint32_t row_num,
+                                         pdfium::span<const uint8_t> row_buf) {
+  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
+  DCHECK(pDIBitmap);
+
+  fxcrt::spancpy(pdfium::make_span(m_DecodeBuf), row_buf.first(m_ScanlineSize));
+
+  int src_top = m_clipBox.top;
+  int src_bottom = m_clipBox.bottom;
+  int dest_top = m_startY;
+  int src_height = m_clipBox.Height();
+  int dest_height = m_sizeY;
+  if ((src_top >= 0 && row_num < static_cast<uint32_t>(src_top)) ||
+      src_bottom < 0 || row_num >= static_cast<uint32_t>(src_bottom)) {
+    return;
+  }
+
+  double scale_y = static_cast<double>(dest_height) / src_height;
+  int src_row = row_num - src_top;
+  int dest_row = (int)(src_row * scale_y) + dest_top;
+  if (dest_row >= dest_top + dest_height)
+    return;
+
+  ResampleScanline(pDIBitmap, dest_row, m_DecodeBuf, m_SrcFormat);
+  if (scale_y <= 1.0)
+    return;
+
+  if (m_BmpIsTopBottom) {
+    ResampleVert(pDIBitmap, scale_y, dest_row);
+    return;
+  }
+  ResampleVertBT(pDIBitmap, scale_y, dest_row);
+}
+
+void ProgressiveDecoder::ResampleVertBT(
+    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+    double scale_y,
+    int dest_row) {
+  int dest_Bpp = pDeviceBitmap->GetBPP() >> 3;
+  uint32_t dest_ScanOffset = m_startX * dest_Bpp;
+  int dest_top = m_startY;
+  int dest_bottom = m_startY + m_sizeY;
+  FX_SAFE_INT32 check_dest_row_1 = dest_row;
+  check_dest_row_1 += pdfium::base::checked_cast<int>(scale_y);
+  int dest_row_1 = check_dest_row_1.ValueOrDie();
+  if (dest_row_1 >= dest_bottom - 1) {
+    const uint8_t* scan_src =
+        pDeviceBitmap->GetScanline(dest_row).subspan(dest_ScanOffset).data();
+    while (++dest_row < dest_bottom) {
+      uint8_t* scan_des = pDeviceBitmap->GetWritableScanline(dest_row)
+                              .subspan(dest_ScanOffset)
+                              .data();
+      uint32_t size = m_sizeX * dest_Bpp;
+      memmove(scan_des, scan_src, size);
+    }
+    return;
+  }
+  for (; dest_row_1 > dest_row; dest_row_1--) {
+    uint8_t* scan_des = pDeviceBitmap->GetWritableScanline(dest_row_1)
+                            .subspan(dest_ScanOffset)
+                            .data();
+    PixelWeight* pWeight = m_WeightVert.GetPixelWeight(dest_row_1 - dest_top);
+    const uint8_t* scan_src1 =
+        pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top)
+            .subspan(dest_ScanOffset)
+            .data();
+    const uint8_t* scan_src2 =
+        pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top)
+            .subspan(dest_ScanOffset)
+            .data();
+    switch (pDeviceBitmap->GetFormat()) {
+      case FXDIB_Format::kInvalid:
+      case FXDIB_Format::k1bppMask:
+      case FXDIB_Format::k1bppRgb:
+        return;
+      case FXDIB_Format::k8bppMask:
+      case FXDIB_Format::k8bppRgb:
+        if (pDeviceBitmap->HasPalette())
+          return;
+        for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
+          uint32_t dest_g = 0;
+          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_g);
+        }
+        break;
+      case FXDIB_Format::kRgb:
+      case FXDIB_Format::kRgb32:
+        for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
+          uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++);
+          uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++);
+          uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++);
+          scan_src1 += dest_Bpp - 3;
+          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
+          scan_src2 += dest_Bpp - 3;
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_b);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_g);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_r);
+          scan_des += dest_Bpp - 3;
+        }
+        break;
+      case FXDIB_Format::kArgb:
+        for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
+          uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++);
+          uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++);
+          uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++);
+          uint32_t dest_a = pWeight->m_Weights[0] * (*scan_src1++);
+          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_a += pWeight->m_Weights[1] * (*scan_src2++);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_b);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_g);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_r);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_a);
+        }
+        break;
+    }
+  }
+}
+
+bool ProgressiveDecoder::BmpDetectImageTypeInBuffer(
+    CFX_DIBAttribute* pAttribute) {
+  std::unique_ptr<ProgressiveDecoderIface::Context> pBmpContext =
+      BmpDecoder::StartDecode(this);
+  BmpDecoder::Input(pBmpContext.get(), m_pCodecMemory);
+
+  const std::vector<uint32_t>* palette;
+  BmpDecoder::Status read_result = BmpDecoder::ReadHeader(
+      pBmpContext.get(), &m_SrcWidth, &m_SrcHeight, &m_BmpIsTopBottom,
+      &m_SrcComponents, &m_SrcPaletteNumber, &palette, pAttribute);
+  while (read_result == BmpDecoder::Status::kContinue) {
+    FXCODEC_STATUS error_status = FXCODEC_STATUS::kError;
+    if (!BmpReadMoreData(pBmpContext.get(), &error_status)) {
+      m_status = error_status;
+      return false;
+    }
+    read_result = BmpDecoder::ReadHeader(
+        pBmpContext.get(), &m_SrcWidth, &m_SrcHeight, &m_BmpIsTopBottom,
+        &m_SrcComponents, &m_SrcPaletteNumber, &palette, pAttribute);
+  }
+
+  if (read_result != BmpDecoder::Status::kSuccess) {
+    m_status = FXCODEC_STATUS::kError;
+    return false;
+  }
+
+  FXDIB_Format format = FXDIB_Format::kInvalid;
+  switch (m_SrcComponents) {
+    case 1:
+      m_SrcFormat = FXCodec_8bppRgb;
+      format = FXDIB_Format::k8bppRgb;
+      break;
+    case 3:
+      m_SrcFormat = FXCodec_Rgb;
+      format = FXDIB_Format::kRgb;
+      break;
+    case 4:
+      m_SrcFormat = FXCodec_Rgb32;
+      format = FXDIB_Format::kRgb32;
+      break;
+    default:
+      m_status = FXCODEC_STATUS::kError;
+      return false;
+  }
+
+  // Set to 0 to make CalculatePitchAndSize() calculate it.
+  constexpr uint32_t kNoPitch = 0;
+  absl::optional<CFX_DIBitmap::PitchAndSize> needed_data =
+      CFX_DIBitmap::CalculatePitchAndSize(m_SrcWidth, m_SrcHeight, format,
+                                          kNoPitch);
+  if (!needed_data.has_value()) {
+    m_status = FXCODEC_STATUS::kError;
+    return false;
+  }
+
+  uint32_t available_data = pdfium::base::checked_cast<uint32_t>(
+      m_pFile->GetSize() - m_offSet +
+      BmpDecoder::GetAvailInput(pBmpContext.get()));
+  if (needed_data.value().size > available_data) {
+    m_status = FXCODEC_STATUS::kError;
+    return false;
+  }
+
+  m_SrcBPC = 8;
+  m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
+  m_pBmpContext = std::move(pBmpContext);
+  if (m_SrcPaletteNumber) {
+    m_SrcPalette.resize(m_SrcPaletteNumber);
+    memcpy(m_SrcPalette.data(), palette->data(),
+           m_SrcPaletteNumber * sizeof(FX_ARGB));
+  } else {
+    m_SrcPalette.clear();
+  }
+  return true;
+}
+
+bool ProgressiveDecoder::BmpReadMoreData(
+    ProgressiveDecoderIface::Context* pContext,
+    FXCODEC_STATUS* err_status) {
+  return ReadMoreData(BmpProgressiveDecoder::GetInstance(), pContext,
+                      err_status);
+}
+
+FXCODEC_STATUS ProgressiveDecoder::BmpStartDecode() {
+  GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
+  m_ScanlineSize = FxAlignToBoundary<4>(m_SrcWidth * m_SrcComponents);
+  m_DecodeBuf.resize(m_ScanlineSize);
+  FXDIB_ResampleOptions options;
+  options.bInterpolateBilinear = true;
+  m_WeightHorz.CalculateWeights(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0,
+                                m_clipBox.Width(), options);
+  m_WeightVert.CalculateWeights(m_sizeY, m_clipBox.Height());
+  m_status = FXCODEC_STATUS::kDecodeToBeContinued;
+  return m_status;
+}
+
+FXCODEC_STATUS ProgressiveDecoder::BmpContinueDecode() {
+  BmpDecoder::Status read_res = BmpDecoder::LoadImage(m_pBmpContext.get());
+  while (read_res == BmpDecoder::Status::kContinue) {
+    FXCODEC_STATUS error_status = FXCODEC_STATUS::kDecodeFinished;
+    if (!BmpReadMoreData(m_pBmpContext.get(), &error_status)) {
+      m_pDeviceBitmap = nullptr;
+      m_pFile = nullptr;
+      m_status = error_status;
+      return m_status;
+    }
+    read_res = BmpDecoder::LoadImage(m_pBmpContext.get());
+  }
+
+  m_pDeviceBitmap = nullptr;
+  m_pFile = nullptr;
+  m_status = read_res == BmpDecoder::Status::kSuccess
+                 ? FXCODEC_STATUS::kDecodeFinished
+                 : FXCODEC_STATUS::kError;
+  return m_status;
+}
+#endif  // PDF_ENABLE_XFA_BMP
+
+#ifdef PDF_ENABLE_XFA_GIF
+bool ProgressiveDecoder::GifReadMoreData(FXCODEC_STATUS* err_status) {
+  return ReadMoreData(GifProgressiveDecoder::GetInstance(), m_pGifContext.get(),
+                      err_status);
+}
+
+bool ProgressiveDecoder::GifDetectImageTypeInBuffer() {
+  m_pGifContext = GifDecoder::StartDecode(this);
+  GifDecoder::Input(m_pGifContext.get(), m_pCodecMemory);
+  m_SrcComponents = 1;
+  GifDecoder::Status readResult =
+      GifDecoder::ReadHeader(m_pGifContext.get(), &m_SrcWidth, &m_SrcHeight,
+                             &m_GifPltNumber, &m_pGifPalette, &m_GifBgIndex);
+  while (readResult == GifDecoder::Status::kUnfinished) {
+    FXCODEC_STATUS error_status = FXCODEC_STATUS::kError;
+    if (!GifReadMoreData(&error_status)) {
+      m_pGifContext = nullptr;
+      m_status = error_status;
+      return false;
+    }
+    readResult =
+        GifDecoder::ReadHeader(m_pGifContext.get(), &m_SrcWidth, &m_SrcHeight,
+                               &m_GifPltNumber, &m_pGifPalette, &m_GifBgIndex);
+  }
+  if (readResult == GifDecoder::Status::kSuccess) {
+    m_SrcBPC = 8;
+    m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
+    return true;
+  }
+  m_pGifContext = nullptr;
+  m_status = FXCODEC_STATUS::kError;
+  return false;
+}
+
+FXCODEC_STATUS ProgressiveDecoder::GifStartDecode() {
+  m_SrcFormat = FXCodec_8bppRgb;
+  GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
+  int scanline_size = FxAlignToBoundary<4>(m_SrcWidth);
+  m_DecodeBuf.resize(scanline_size);
+  FXDIB_ResampleOptions options;
+  options.bInterpolateBilinear = true;
+  m_WeightHorz.CalculateWeights(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0,
+                                m_clipBox.Width(), options);
+  m_WeightVert.CalculateWeights(m_sizeY, m_clipBox.Height());
+  m_FrameCur = 0;
+  m_status = FXCODEC_STATUS::kDecodeToBeContinued;
+  return m_status;
+}
+
+FXCODEC_STATUS ProgressiveDecoder::GifContinueDecode() {
+  GifDecoder::Status readRes =
+      GifDecoder::LoadFrame(m_pGifContext.get(), m_FrameCur);
+  while (readRes == GifDecoder::Status::kUnfinished) {
+    FXCODEC_STATUS error_status = FXCODEC_STATUS::kDecodeFinished;
+    if (!GifReadMoreData(&error_status)) {
+      m_pDeviceBitmap = nullptr;
+      m_pFile = nullptr;
+      m_status = error_status;
+      return m_status;
+    }
+    readRes = GifDecoder::LoadFrame(m_pGifContext.get(), m_FrameCur);
+  }
+
+  if (readRes == GifDecoder::Status::kSuccess) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS::kDecodeFinished;
+    return m_status;
+  }
+
+  m_pDeviceBitmap = nullptr;
+  m_pFile = nullptr;
+  m_status = FXCODEC_STATUS::kError;
+  return m_status;
+}
+
+void ProgressiveDecoder::GifDoubleLineResampleVert(
+    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+    double scale_y,
+    int dest_row) {
+  int dest_Bpp = pDeviceBitmap->GetBPP() >> 3;
+  uint32_t dest_ScanOffset = m_startX * dest_Bpp;
+  int dest_top = m_startY;
+  pdfium::base::CheckedNumeric<double> scale_y2 = scale_y;
+  scale_y2 *= 2;
+  FX_SAFE_INT32 check_dest_row_1 = dest_row;
+  check_dest_row_1 -= scale_y2.ValueOrDie();
+  int dest_row_1 = check_dest_row_1.ValueOrDie();
+  dest_row_1 = std::max(dest_row_1, dest_top);
+  for (; dest_row_1 < dest_row; dest_row_1++) {
+    uint8_t* scan_des = pDeviceBitmap->GetWritableScanline(dest_row_1)
+                            .subspan(dest_ScanOffset)
+                            .data();
+    PixelWeight* pWeight = m_WeightVert.GetPixelWeight(dest_row_1 - dest_top);
+    const uint8_t* scan_src1 =
+        pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top)
+            .subspan(dest_ScanOffset)
+            .data();
+    const uint8_t* scan_src2 =
+        pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top)
+            .subspan(dest_ScanOffset)
+            .data();
+    switch (pDeviceBitmap->GetFormat()) {
+      case FXDIB_Format::kInvalid:
+      case FXDIB_Format::k1bppMask:
+      case FXDIB_Format::k1bppRgb:
+        return;
+      case FXDIB_Format::k8bppMask:
+      case FXDIB_Format::k8bppRgb:
+        if (pDeviceBitmap->HasPalette())
+          return;
+        for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
+          uint32_t dest_g = 0;
+          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_g);
+        }
+        break;
+      case FXDIB_Format::kRgb:
+      case FXDIB_Format::kRgb32:
+        for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
+          uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++);
+          uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++);
+          uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++);
+          scan_src1 += dest_Bpp - 3;
+          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
+          scan_src2 += dest_Bpp - 3;
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_b);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_g);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_r);
+          scan_des += dest_Bpp - 3;
+        }
+        break;
+      case FXDIB_Format::kArgb:
+        for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
+          uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++);
+          uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++);
+          uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++);
+          uint32_t dest_a = pWeight->m_Weights[0] * (*scan_src1++);
+          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_a += pWeight->m_Weights[1] * (*scan_src2++);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_b);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_g);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_r);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_a);
+        }
+        break;
+    }
+  }
+  int dest_bottom = dest_top + m_sizeY - 1;
+  if (dest_row + (int)(2 * scale_y) >= dest_bottom &&
+      dest_row + (int)scale_y < dest_bottom) {
+    GifDoubleLineResampleVert(pDeviceBitmap, scale_y, dest_row + (int)scale_y);
+  }
+}
+#endif  // PDF_ENABLE_XFA_GIF
+
+bool ProgressiveDecoder::JpegReadMoreData(FXCODEC_STATUS* err_status) {
+  return ReadMoreData(JpegProgressiveDecoder::GetInstance(),
+                      m_pJpegContext.get(), err_status);
+}
+
+bool ProgressiveDecoder::JpegDetectImageTypeInBuffer(
+    CFX_DIBAttribute* pAttribute) {
+  m_pJpegContext = JpegProgressiveDecoder::Start();
+  if (!m_pJpegContext) {
+    m_status = FXCODEC_STATUS::kError;
+    return false;
+  }
+  JpegProgressiveDecoder::GetInstance()->Input(m_pJpegContext.get(),
+                                               m_pCodecMemory);
+
+  // Setting jump marker before calling ReadHeader, since a longjmp to
+  // the marker indicates a fatal error.
+  if (setjmp(JpegProgressiveDecoder::GetJumpMark(m_pJpegContext.get())) == -1) {
+    m_pJpegContext.reset();
+    m_status = FXCODEC_STATUS::kError;
+    return false;
+  }
+
+  int32_t readResult = JpegProgressiveDecoder::ReadHeader(
+      m_pJpegContext.get(), &m_SrcWidth, &m_SrcHeight, &m_SrcComponents,
+      pAttribute);
+  while (readResult == 2) {
+    FXCODEC_STATUS error_status = FXCODEC_STATUS::kError;
+    if (!JpegReadMoreData(&error_status)) {
+      m_status = error_status;
+      return false;
+    }
+    readResult = JpegProgressiveDecoder::ReadHeader(
+        m_pJpegContext.get(), &m_SrcWidth, &m_SrcHeight, &m_SrcComponents,
+        pAttribute);
+  }
+  if (!readResult) {
+    m_SrcBPC = 8;
+    m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
+    return true;
+  }
+  m_pJpegContext.reset();
+  m_status = FXCODEC_STATUS::kError;
+  return false;
+}
+
+FXCODEC_STATUS ProgressiveDecoder::JpegStartDecode(FXDIB_Format format) {
+  int down_scale = GetDownScale();
+  // Setting jump marker before calling StartScanLine, since a longjmp to
+  // the marker indicates a fatal error.
+  if (setjmp(JpegProgressiveDecoder::GetJumpMark(m_pJpegContext.get())) == -1) {
+    m_pJpegContext.reset();
+    m_status = FXCODEC_STATUS::kError;
+    return FXCODEC_STATUS::kError;
+  }
+
+  bool startStatus =
+      JpegProgressiveDecoder::StartScanline(m_pJpegContext.get(), down_scale);
+  while (!startStatus) {
+    FXCODEC_STATUS error_status = FXCODEC_STATUS::kError;
+    if (!JpegReadMoreData(&error_status)) {
+      m_pDeviceBitmap = nullptr;
+      m_pFile = nullptr;
+      m_status = error_status;
+      return m_status;
+    }
+
+    startStatus =
+        JpegProgressiveDecoder::StartScanline(m_pJpegContext.get(), down_scale);
+  }
+  int scanline_size = (m_SrcWidth + down_scale - 1) / down_scale;
+  scanline_size = FxAlignToBoundary<4>(scanline_size * m_SrcComponents);
+  m_DecodeBuf.resize(scanline_size);
+  FXDIB_ResampleOptions options;
+  options.bInterpolateBilinear = true;
+  m_WeightHorz.CalculateWeights(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0,
+                                m_clipBox.Width(), options);
+  m_WeightVert.CalculateWeights(m_sizeY, m_clipBox.Height());
+  switch (m_SrcComponents) {
+    case 1:
+      m_SrcFormat = FXCodec_8bppGray;
+      break;
+    case 3:
+      m_SrcFormat = FXCodec_Rgb;
+      break;
+    case 4:
+      m_SrcFormat = FXCodec_Cmyk;
+      break;
+  }
+  GetTransMethod(format, m_SrcFormat);
+  m_status = FXCODEC_STATUS::kDecodeToBeContinued;
+  return m_status;
+}
+
+FXCODEC_STATUS ProgressiveDecoder::JpegContinueDecode() {
+  // JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule();
+  // Setting jump marker before calling ReadScanLine, since a longjmp to
+  // the marker indicates a fatal error.
+  if (setjmp(JpegProgressiveDecoder::GetJumpMark(m_pJpegContext.get())) == -1) {
+    m_pJpegContext.reset();
+    m_status = FXCODEC_STATUS::kError;
+    return FXCODEC_STATUS::kError;
+  }
+
+  while (true) {
+    bool readRes = JpegProgressiveDecoder::ReadScanline(m_pJpegContext.get(),
+                                                        m_DecodeBuf.data());
+    while (!readRes) {
+      FXCODEC_STATUS error_status = FXCODEC_STATUS::kDecodeFinished;
+      if (!JpegReadMoreData(&error_status)) {
+        m_pDeviceBitmap = nullptr;
+        m_pFile = nullptr;
+        m_status = error_status;
+        return m_status;
+      }
+      readRes = JpegProgressiveDecoder::ReadScanline(m_pJpegContext.get(),
+                                                     m_DecodeBuf.data());
+    }
+    if (m_SrcFormat == FXCodec_Rgb) {
+      int src_Bpp = (m_SrcFormat & 0xff) >> 3;
+      RGB2BGR(m_DecodeBuf.data() + m_clipBox.left * src_Bpp, m_clipBox.Width());
+    }
+    if (m_SrcRow >= m_clipBox.bottom) {
+      m_pDeviceBitmap = nullptr;
+      m_pFile = nullptr;
+      m_status = FXCODEC_STATUS::kDecodeFinished;
+      return m_status;
+    }
+    Resample(m_pDeviceBitmap, m_SrcRow, m_DecodeBuf.data(), m_SrcFormat);
+    m_SrcRow++;
+  }
+}
+
+#ifdef PDF_ENABLE_XFA_PNG
+void ProgressiveDecoder::PngOneOneMapResampleHorz(
+    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+    int32_t dest_line,
+    pdfium::span<uint8_t> src_span,
+    FXCodec_Format src_format) {
+  int32_t src_Bpp = (m_SrcFormat & 0xff) >> 3;
+  int32_t dest_Bpp = pDeviceBitmap->GetBPP() >> 3;
+  int32_t src_left = m_clipBox.left;
+  int32_t dest_left = m_startX;
+  uint8_t* src_scan = src_span.subspan(src_left * src_Bpp).data();
+  uint8_t* dest_scan = pDeviceBitmap->GetWritableScanline(dest_line)
+                           .subspan(dest_left * dest_Bpp)
+                           .data();
+  switch (pDeviceBitmap->GetFormat()) {
+    case FXDIB_Format::k1bppMask:
+    case FXDIB_Format::k1bppRgb:
+      NOTREACHED_NORETURN();
+    case FXDIB_Format::k8bppMask:
+    case FXDIB_Format::k8bppRgb:
+      if (pDeviceBitmap->HasPalette())
+        return;
+      for (int32_t dest_col = 0; dest_col < m_sizeX; dest_col++) {
+        PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(dest_col);
+        uint32_t dest_g =
+            pPixelWeights->m_Weights[0] * src_scan[pPixelWeights->m_SrcStart];
+        dest_g +=
+            pPixelWeights->m_Weights[1] * src_scan[pPixelWeights->m_SrcEnd];
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g);
+      }
+      break;
+    case FXDIB_Format::kRgb:
+    case FXDIB_Format::kRgb32:
+      for (int32_t dest_col = 0; dest_col < m_sizeX; dest_col++) {
+        PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(dest_col);
+        const uint8_t* p = src_scan + pPixelWeights->m_SrcStart * src_Bpp;
+        uint32_t dest_b = pPixelWeights->m_Weights[0] * (*p++);
+        uint32_t dest_g = pPixelWeights->m_Weights[0] * (*p++);
+        uint32_t dest_r = pPixelWeights->m_Weights[0] * (*p);
+        p = src_scan + pPixelWeights->m_SrcEnd * src_Bpp;
+        dest_b += pPixelWeights->m_Weights[1] * (*p++);
+        dest_g += pPixelWeights->m_Weights[1] * (*p++);
+        dest_r += pPixelWeights->m_Weights[1] * (*p);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r);
+        dest_scan += dest_Bpp - 3;
+      }
+      break;
+    case FXDIB_Format::kArgb:
+      for (int32_t dest_col = 0; dest_col < m_sizeX; dest_col++) {
+        PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(dest_col);
+        const uint8_t* p = src_scan + pPixelWeights->m_SrcStart * src_Bpp;
+        uint32_t dest_b = pPixelWeights->m_Weights[0] * (*p++);
+        uint32_t dest_g = pPixelWeights->m_Weights[0] * (*p++);
+        uint32_t dest_r = pPixelWeights->m_Weights[0] * (*p++);
+        uint32_t dest_a = pPixelWeights->m_Weights[0] * (*p);
+        p = src_scan + pPixelWeights->m_SrcEnd * src_Bpp;
+        dest_b += pPixelWeights->m_Weights[1] * (*p++);
+        dest_g += pPixelWeights->m_Weights[1] * (*p++);
+        dest_r += pPixelWeights->m_Weights[1] * (*p++);
+        dest_a += pPixelWeights->m_Weights[1] * (*p);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_a);
+      }
+      break;
+    default:
+      return;
+  }
+}
+
+bool ProgressiveDecoder::PngDetectImageTypeInBuffer(
+    CFX_DIBAttribute* pAttribute) {
+  m_pPngContext = PngDecoder::StartDecode(this);
+  if (!m_pPngContext) {
+    m_status = FXCODEC_STATUS::kError;
+    return false;
+  }
+  while (PngDecoder::ContinueDecode(m_pPngContext.get(), m_pCodecMemory,
+                                    pAttribute)) {
+    uint32_t remain_size = static_cast<uint32_t>(m_pFile->GetSize()) - m_offSet;
+    uint32_t input_size = std::min<uint32_t>(remain_size, kBlockSize);
+    if (input_size == 0) {
+      m_pPngContext.reset();
+      m_status = FXCODEC_STATUS::kError;
+      return false;
+    }
+    if (m_pCodecMemory && input_size > m_pCodecMemory->GetSize())
+      m_pCodecMemory = pdfium::MakeRetain<CFX_CodecMemory>(input_size);
+
+    if (!m_pFile->ReadBlockAtOffset(
+            m_pCodecMemory->GetBufferSpan().first(input_size), m_offSet)) {
+      m_status = FXCODEC_STATUS::kError;
+      return false;
+    }
+    m_offSet += input_size;
+  }
+  m_pPngContext.reset();
+  if (m_SrcPassNumber == 0) {
+    m_status = FXCODEC_STATUS::kError;
+    return false;
+  }
+  return true;
+}
+
+FXCODEC_STATUS ProgressiveDecoder::PngStartDecode() {
+  m_pPngContext = PngDecoder::StartDecode(this);
+  if (!m_pPngContext) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS::kError;
+    return m_status;
+  }
+  m_offSet = 0;
+  switch (m_pDeviceBitmap->GetFormat()) {
+    case FXDIB_Format::k8bppMask:
+    case FXDIB_Format::k8bppRgb:
+      m_SrcComponents = 1;
+      m_SrcFormat = FXCodec_8bppGray;
+      break;
+    case FXDIB_Format::kRgb:
+      m_SrcComponents = 3;
+      m_SrcFormat = FXCodec_Rgb;
+      break;
+    case FXDIB_Format::kRgb32:
+    case FXDIB_Format::kArgb:
+      m_SrcComponents = 4;
+      m_SrcFormat = FXCodec_Argb;
+      break;
+    default: {
+      m_pDeviceBitmap = nullptr;
+      m_pFile = nullptr;
+      m_status = FXCODEC_STATUS::kError;
+      return m_status;
+    }
+  }
+  GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
+  int scanline_size = FxAlignToBoundary<4>(m_SrcWidth * m_SrcComponents);
+  m_DecodeBuf.resize(scanline_size);
+  m_WeightHorzOO.CalculateWeights(m_sizeX, m_clipBox.Width());
+  m_WeightVert.CalculateWeights(m_sizeY, m_clipBox.Height());
+  m_status = FXCODEC_STATUS::kDecodeToBeContinued;
+  return m_status;
+}
+
+FXCODEC_STATUS ProgressiveDecoder::PngContinueDecode() {
+  while (true) {
+    uint32_t remain_size = (uint32_t)m_pFile->GetSize() - m_offSet;
+    uint32_t input_size = std::min<uint32_t>(remain_size, kBlockSize);
+    if (input_size == 0) {
+      m_pPngContext.reset();
+      m_pDeviceBitmap = nullptr;
+      m_pFile = nullptr;
+      m_status = FXCODEC_STATUS::kDecodeFinished;
+      return m_status;
+    }
+    if (m_pCodecMemory && input_size > m_pCodecMemory->GetSize())
+      m_pCodecMemory = pdfium::MakeRetain<CFX_CodecMemory>(input_size);
+
+    bool bResult = m_pFile->ReadBlockAtOffset(
+        m_pCodecMemory->GetBufferSpan().first(input_size), m_offSet);
+    if (!bResult) {
+      m_pDeviceBitmap = nullptr;
+      m_pFile = nullptr;
+      m_status = FXCODEC_STATUS::kError;
+      return m_status;
+    }
+    m_offSet += input_size;
+    bResult = PngDecoder::ContinueDecode(m_pPngContext.get(), m_pCodecMemory,
+                                         nullptr);
+    if (!bResult) {
+      m_pDeviceBitmap = nullptr;
+      m_pFile = nullptr;
+      m_status = FXCODEC_STATUS::kError;
+      return m_status;
+    }
+  }
+}
+#endif  // PDF_ENABLE_XFA_PNG
+
+#ifdef PDF_ENABLE_XFA_TIFF
+bool ProgressiveDecoder::TiffDetectImageTypeFromFile(
+    CFX_DIBAttribute* pAttribute) {
+  m_pTiffContext = TiffDecoder::CreateDecoder(m_pFile);
+  if (!m_pTiffContext) {
+    m_status = FXCODEC_STATUS::kError;
+    return false;
+  }
+  int32_t dummy_bpc;
+  bool ret = TiffDecoder::LoadFrameInfo(m_pTiffContext.get(), 0, &m_SrcWidth,
+                                        &m_SrcHeight, &m_SrcComponents,
+                                        &dummy_bpc, pAttribute);
+  m_SrcComponents = 4;
+  m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
+  if (!ret) {
+    m_pTiffContext.reset();
+    m_status = FXCODEC_STATUS::kError;
+    return false;
+  }
+  return true;
+}
+
+FXCODEC_STATUS ProgressiveDecoder::TiffContinueDecode() {
+  bool ret = false;
+  if (m_pDeviceBitmap->GetBPP() == 32 &&
+      m_pDeviceBitmap->GetWidth() == m_SrcWidth && m_SrcWidth == m_sizeX &&
+      m_pDeviceBitmap->GetHeight() == m_SrcHeight && m_SrcHeight == m_sizeY &&
+      m_startX == 0 && m_startY == 0 && m_clipBox.left == 0 &&
+      m_clipBox.top == 0 && m_clipBox.right == m_SrcWidth &&
+      m_clipBox.bottom == m_SrcHeight) {
+    ret = TiffDecoder::Decode(m_pTiffContext.get(), m_pDeviceBitmap);
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    if (!ret) {
+      m_status = FXCODEC_STATUS::kError;
+      return m_status;
+    }
+    m_status = FXCODEC_STATUS::kDecodeFinished;
+    return m_status;
+  }
+
+  auto pDIBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  pDIBitmap->Create(m_SrcWidth, m_SrcHeight, FXDIB_Format::kArgb);
+  if (pDIBitmap->GetBuffer().empty()) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS::kError;
+    return m_status;
+  }
+  ret = TiffDecoder::Decode(m_pTiffContext.get(), pDIBitmap);
+  if (!ret) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS::kError;
+    return m_status;
+  }
+  RetainPtr<CFX_DIBitmap> pClipBitmap =
+      (m_clipBox.left == 0 && m_clipBox.top == 0 &&
+       m_clipBox.right == m_SrcWidth && m_clipBox.bottom == m_SrcHeight)
+          ? pDIBitmap
+          : pDIBitmap->ClipTo(m_clipBox);
+  if (!pClipBitmap) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS::kError;
+    return m_status;
+  }
+  RetainPtr<CFX_DIBitmap> pFormatBitmap;
+  switch (m_pDeviceBitmap->GetFormat()) {
+    case FXDIB_Format::k8bppRgb:
+      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+      pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(),
+                            FXDIB_Format::k8bppRgb);
+      break;
+    case FXDIB_Format::k8bppMask:
+      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+      pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(),
+                            FXDIB_Format::k8bppMask);
+      break;
+    case FXDIB_Format::kRgb:
+      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+      pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(),
+                            FXDIB_Format::kRgb);
+      break;
+    case FXDIB_Format::kRgb32:
+      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+      pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(),
+                            FXDIB_Format::kRgb32);
+      break;
+    case FXDIB_Format::kArgb:
+      pFormatBitmap = pClipBitmap;
+      break;
+    default:
+      break;
+  }
+  switch (m_pDeviceBitmap->GetFormat()) {
+    case FXDIB_Format::k8bppRgb:
+    case FXDIB_Format::k8bppMask: {
+      for (int32_t row = 0; row < pClipBitmap->GetHeight(); row++) {
+        const uint8_t* src_line = pClipBitmap->GetScanline(row).data();
+        uint8_t* dest_line = pFormatBitmap->GetWritableScanline(row).data();
+        for (int32_t col = 0; col < pClipBitmap->GetWidth(); col++) {
+          uint8_t _a = 255 - src_line[3];
+          uint8_t b = (src_line[0] * src_line[3] + 0xFF * _a) / 255;
+          uint8_t g = (src_line[1] * src_line[3] + 0xFF * _a) / 255;
+          uint8_t r = (src_line[2] * src_line[3] + 0xFF * _a) / 255;
+          *dest_line++ = FXRGB2GRAY(r, g, b);
+          src_line += 4;
+        }
+      }
+    } break;
+    case FXDIB_Format::kRgb:
+    case FXDIB_Format::kRgb32: {
+      int32_t desBpp =
+          (m_pDeviceBitmap->GetFormat() == FXDIB_Format::kRgb) ? 3 : 4;
+      for (int32_t row = 0; row < pClipBitmap->GetHeight(); row++) {
+        const uint8_t* src_line = pClipBitmap->GetScanline(row).data();
+        uint8_t* dest_line = pFormatBitmap->GetWritableScanline(row).data();
+        for (int32_t col = 0; col < pClipBitmap->GetWidth(); col++) {
+          uint8_t _a = 255 - src_line[3];
+          uint8_t b = (src_line[0] * src_line[3] + 0xFF * _a) / 255;
+          uint8_t g = (src_line[1] * src_line[3] + 0xFF * _a) / 255;
+          uint8_t r = (src_line[2] * src_line[3] + 0xFF * _a) / 255;
+          *dest_line++ = b;
+          *dest_line++ = g;
+          *dest_line++ = r;
+          dest_line += desBpp - 3;
+          src_line += 4;
+        }
+      }
+    } break;
+    default:
+      break;
+  }
+  if (!pFormatBitmap) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS::kError;
+    return m_status;
+  }
+
+  FXDIB_ResampleOptions options;
+  options.bInterpolateBilinear = true;
+  RetainPtr<CFX_DIBitmap> pStrechBitmap =
+      pFormatBitmap->StretchTo(m_sizeX, m_sizeY, options, nullptr);
+  pFormatBitmap = nullptr;
+  if (!pStrechBitmap) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS::kError;
+    return m_status;
+  }
+  m_pDeviceBitmap->TransferBitmap(m_startX, m_startY, m_sizeX, m_sizeY,
+                                  pStrechBitmap, 0, 0);
+  m_pDeviceBitmap = nullptr;
+  m_pFile = nullptr;
+  m_status = FXCODEC_STATUS::kDecodeFinished;
+  return m_status;
+}
+#endif  // PDF_ENABLE_XFA_TIFF
+
+bool ProgressiveDecoder::DetectImageType(FXCODEC_IMAGE_TYPE imageType,
+                                         CFX_DIBAttribute* pAttribute) {
+#ifdef PDF_ENABLE_XFA_TIFF
+  if (imageType == FXCODEC_IMAGE_TIFF)
+    return TiffDetectImageTypeFromFile(pAttribute);
+#endif  // PDF_ENABLE_XFA_TIFF
+
+  size_t size = pdfium::base::checked_cast<size_t>(
+      std::min<FX_FILESIZE>(m_pFile->GetSize(), kBlockSize));
+  m_pCodecMemory = pdfium::MakeRetain<CFX_CodecMemory>(size);
+  m_offSet = 0;
+  if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBufferSpan().first(size),
+                                  m_offSet)) {
+    m_status = FXCODEC_STATUS::kError;
+    return false;
+  }
+  m_offSet += size;
+
+  if (imageType == FXCODEC_IMAGE_JPG)
+    return JpegDetectImageTypeInBuffer(pAttribute);
+
+#ifdef PDF_ENABLE_XFA_BMP
+  if (imageType == FXCODEC_IMAGE_BMP)
+    return BmpDetectImageTypeInBuffer(pAttribute);
+#endif  // PDF_ENABLE_XFA_BMP
+
+#ifdef PDF_ENABLE_XFA_GIF
+  if (imageType == FXCODEC_IMAGE_GIF)
+    return GifDetectImageTypeInBuffer();
+#endif  // PDF_ENABLE_XFA_GIF
+
+#ifdef PDF_ENABLE_XFA_PNG
+  if (imageType == FXCODEC_IMAGE_PNG)
+    return PngDetectImageTypeInBuffer(pAttribute);
+#endif  // PDF_ENABLE_XFA_PNG
+
+  m_status = FXCODEC_STATUS::kError;
+  return false;
+}
+
+bool ProgressiveDecoder::ReadMoreData(
+    ProgressiveDecoderIface* pModule,
+    ProgressiveDecoderIface::Context* pContext,
+    FXCODEC_STATUS* err_status) {
+  // Check for EOF.
+  if (m_offSet >= static_cast<uint32_t>(m_pFile->GetSize()))
+    return false;
+
+  // Try to get whatever remains.
+  uint32_t dwBytesToFetchFromFile =
+      pdfium::base::checked_cast<uint32_t>(m_pFile->GetSize() - m_offSet);
+
+  // Figure out if the codec stopped processing midway through the buffer.
+  size_t dwUnconsumed;
+  FX_SAFE_SIZE_T avail_input = pModule->GetAvailInput(pContext);
+  if (!avail_input.AssignIfValid(&dwUnconsumed))
+    return false;
+
+  if (dwUnconsumed == m_pCodecMemory->GetSize()) {
+    // Codec couldn't make any progress against the bytes in the buffer.
+    // Increase the buffer size so that there might be enough contiguous
+    // bytes to allow whatever operation is having difficulty to succeed.
+    dwBytesToFetchFromFile =
+        std::min<uint32_t>(dwBytesToFetchFromFile, kBlockSize);
+    size_t dwNewSize = m_pCodecMemory->GetSize() + dwBytesToFetchFromFile;
+    if (!m_pCodecMemory->TryResize(dwNewSize)) {
+      *err_status = FXCODEC_STATUS::kError;
+      return false;
+    }
+  } else {
+    // TODO(crbug.com/pdfium/1904): Simplify the `CFX_CodecMemory` API so we
+    // don't need to do this awkward dance to free up exactly enough buffer
+    // space for the next read.
+    size_t dwConsumable = m_pCodecMemory->GetSize() - dwUnconsumed;
+    dwBytesToFetchFromFile = pdfium::base::checked_cast<uint32_t>(
+        std::min<size_t>(dwBytesToFetchFromFile, dwConsumable));
+    m_pCodecMemory->Consume(dwBytesToFetchFromFile);
+    m_pCodecMemory->Seek(dwConsumable - dwBytesToFetchFromFile);
+    dwUnconsumed += m_pCodecMemory->GetPosition();
+  }
+
+  // Append new data past the bytes not yet processed by the codec.
+  if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBufferSpan().subspan(
+                                      dwUnconsumed, dwBytesToFetchFromFile),
+                                  m_offSet)) {
+    *err_status = FXCODEC_STATUS::kError;
+    return false;
+  }
+  m_offSet += dwBytesToFetchFromFile;
+  return pModule->Input(pContext, m_pCodecMemory);
+}
+
+FXCODEC_STATUS ProgressiveDecoder::LoadImageInfo(
+    RetainPtr<IFX_SeekableReadStream> pFile,
+    FXCODEC_IMAGE_TYPE imageType,
+    CFX_DIBAttribute* pAttribute,
+    bool bSkipImageTypeCheck) {
+  DCHECK(pAttribute);
+
+  switch (m_status) {
+    case FXCODEC_STATUS::kFrameReady:
+    case FXCODEC_STATUS::kFrameToBeContinued:
+    case FXCODEC_STATUS::kDecodeReady:
+    case FXCODEC_STATUS::kDecodeToBeContinued:
+      return FXCODEC_STATUS::kError;
+    default:
+      break;
+  }
+  m_pFile = std::move(pFile);
+  if (!m_pFile) {
+    m_status = FXCODEC_STATUS::kError;
+    return m_status;
+  }
+  m_offSet = 0;
+  m_SrcWidth = m_SrcHeight = 0;
+  m_SrcComponents = m_SrcBPC = 0;
+  m_clipBox = FX_RECT();
+  m_startX = m_startY = 0;
+  m_sizeX = m_sizeY = 0;
+  m_SrcPassNumber = 0;
+  if (imageType != FXCODEC_IMAGE_UNKNOWN &&
+      DetectImageType(imageType, pAttribute)) {
+    m_imageType = imageType;
+    m_status = FXCODEC_STATUS::kFrameReady;
+    return m_status;
+  }
+  // If we got here then the image data does not match the requested decoder.
+  // If we're skipping the type check then bail out at this point and return
+  // the failed status.
+  if (bSkipImageTypeCheck)
+    return m_status;
+
+  for (int type = FXCODEC_IMAGE_UNKNOWN + 1; type < FXCODEC_IMAGE_MAX; type++) {
+    if (DetectImageType(static_cast<FXCODEC_IMAGE_TYPE>(type), pAttribute)) {
+      m_imageType = static_cast<FXCODEC_IMAGE_TYPE>(type);
+      m_status = FXCODEC_STATUS::kFrameReady;
+      return m_status;
+    }
+  }
+  m_status = FXCODEC_STATUS::kError;
+  m_pFile = nullptr;
+  return m_status;
+}
+
+void ProgressiveDecoder::SetClipBox(FX_RECT* clip) {
+  if (m_status != FXCODEC_STATUS::kFrameReady)
+    return;
+
+  if (clip->IsEmpty()) {
+    m_clipBox = FX_RECT();
+    return;
+  }
+  clip->left = std::max(clip->left, 0);
+  clip->right = std::min(clip->right, m_SrcWidth);
+  clip->top = std::max(clip->top, 0);
+  clip->bottom = std::min(clip->bottom, m_SrcHeight);
+  if (clip->IsEmpty()) {
+    m_clipBox = FX_RECT();
+    return;
+  }
+  m_clipBox = *clip;
+}
+
+int ProgressiveDecoder::GetDownScale() {
+  int down_scale = 1;
+  int ratio_w = m_clipBox.Width() / m_sizeX;
+  int ratio_h = m_clipBox.Height() / m_sizeY;
+  int ratio = std::min(ratio_w, ratio_h);
+  if (ratio >= 8)
+    down_scale = 8;
+  else if (ratio >= 4)
+    down_scale = 4;
+  else if (ratio >= 2)
+    down_scale = 2;
+
+  m_clipBox.left /= down_scale;
+  m_clipBox.right /= down_scale;
+  m_clipBox.top /= down_scale;
+  m_clipBox.bottom /= down_scale;
+  if (m_clipBox.right == m_clipBox.left)
+    m_clipBox.right = m_clipBox.left + 1;
+  if (m_clipBox.bottom == m_clipBox.top)
+    m_clipBox.bottom = m_clipBox.top + 1;
+  return down_scale;
+}
+
+void ProgressiveDecoder::GetTransMethod(FXDIB_Format dest_format,
+                                        FXCodec_Format src_format) {
+  switch (dest_format) {
+    case FXDIB_Format::k1bppMask:
+    case FXDIB_Format::k1bppRgb: {
+      switch (src_format) {
+        case FXCodec_1bppGray:
+          m_TransMethod = 0;
+          break;
+        default:
+          m_TransMethod = -1;
+      }
+    } break;
+    case FXDIB_Format::k8bppMask:
+    case FXDIB_Format::k8bppRgb: {
+      switch (src_format) {
+        case FXCodec_1bppGray:
+          m_TransMethod = 1;
+          break;
+        case FXCodec_8bppGray:
+          m_TransMethod = 2;
+          break;
+        case FXCodec_1bppRgb:
+        case FXCodec_8bppRgb:
+          m_TransMethod = 3;
+          break;
+        case FXCodec_Rgb:
+        case FXCodec_Rgb32:
+        case FXCodec_Argb:
+          m_TransMethod = 4;
+          break;
+        case FXCodec_Cmyk:
+          m_TransMethod = 5;
+          break;
+        default:
+          m_TransMethod = -1;
+      }
+    } break;
+    case FXDIB_Format::kRgb: {
+      switch (src_format) {
+        case FXCodec_1bppGray:
+          m_TransMethod = 6;
+          break;
+        case FXCodec_8bppGray:
+          m_TransMethod = 7;
+          break;
+        case FXCodec_1bppRgb:
+        case FXCodec_8bppRgb:
+          m_TransMethod = 8;
+          break;
+        case FXCodec_Rgb:
+        case FXCodec_Rgb32:
+        case FXCodec_Argb:
+          m_TransMethod = 9;
+          break;
+        case FXCodec_Cmyk:
+          m_TransMethod = 10;
+          break;
+        default:
+          m_TransMethod = -1;
+      }
+    } break;
+    case FXDIB_Format::kRgb32:
+    case FXDIB_Format::kArgb: {
+      switch (src_format) {
+        case FXCodec_1bppGray:
+          m_TransMethod = 6;
+          break;
+        case FXCodec_8bppGray:
+          m_TransMethod = 7;
+          break;
+        case FXCodec_1bppRgb:
+        case FXCodec_8bppRgb:
+          if (dest_format == FXDIB_Format::kArgb) {
+            m_TransMethod = 12;
+          } else {
+            m_TransMethod = 8;
+          }
+          break;
+        case FXCodec_Rgb:
+        case FXCodec_Rgb32:
+          m_TransMethod = 9;
+          break;
+        case FXCodec_Cmyk:
+          m_TransMethod = 10;
+          break;
+        case FXCodec_Argb:
+          m_TransMethod = 11;
+          break;
+        default:
+          m_TransMethod = -1;
+      }
+    } break;
+    default:
+      m_TransMethod = -1;
+  }
+}
+
+void ProgressiveDecoder::ResampleScanline(
+    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+    int dest_line,
+    pdfium::span<uint8_t> src_span,
+    FXCodec_Format src_format) {
+  uint8_t* src_scan = src_span.data();
+  int src_left = m_clipBox.left;
+  int dest_left = m_startX;
+  uint8_t* dest_scan = pDeviceBitmap->GetWritableScanline(dest_line).data();
+  int src_bytes_per_pixel = (src_format & 0xff) / 8;
+  int dest_bytes_per_pixel = pDeviceBitmap->GetBPP() / 8;
+  src_scan += src_left * src_bytes_per_pixel;
+  dest_scan += dest_left * dest_bytes_per_pixel;
+  for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
+    PixelWeight* pPixelWeights = m_WeightHorz.GetPixelWeight(dest_col);
+    switch (m_TransMethod) {
+      case -1:
+        return;
+      case 0:
+        return;
+      case 1:
+        return;
+      case 2: {
+        uint32_t dest_g = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          uint32_t pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          dest_g += pixel_weight * src_scan[j];
+        }
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g);
+      } break;
+      case 3: {
+        uint32_t dest_r = 0;
+        uint32_t dest_g = 0;
+        uint32_t dest_b = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          uint32_t pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          uint32_t argb = m_SrcPalette[src_scan[j]];
+          dest_r += pixel_weight * FXARGB_R(argb);
+          dest_g += pixel_weight * FXARGB_G(argb);
+          dest_b += pixel_weight * FXARGB_B(argb);
+        }
+        *dest_scan++ = static_cast<uint8_t>(
+            FXRGB2GRAY(CStretchEngine::PixelFromFixed(dest_r),
+                       CStretchEngine::PixelFromFixed(dest_g),
+                       CStretchEngine::PixelFromFixed(dest_b)));
+      } break;
+      case 4: {
+        uint32_t dest_b = 0;
+        uint32_t dest_g = 0;
+        uint32_t dest_r = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          uint32_t pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel;
+          dest_b += pixel_weight * (*src_pixel++);
+          dest_g += pixel_weight * (*src_pixel++);
+          dest_r += pixel_weight * (*src_pixel);
+        }
+        *dest_scan++ = static_cast<uint8_t>(
+            FXRGB2GRAY(CStretchEngine::PixelFromFixed(dest_r),
+                       CStretchEngine::PixelFromFixed(dest_g),
+                       CStretchEngine::PixelFromFixed(dest_b)));
+      } break;
+      case 5: {
+        uint32_t dest_b = 0;
+        uint32_t dest_g = 0;
+        uint32_t dest_r = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          uint32_t pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel;
+          uint8_t src_b = 0;
+          uint8_t src_g = 0;
+          uint8_t src_r = 0;
+          std::tie(src_r, src_g, src_b) =
+              AdobeCMYK_to_sRGB1(255 - src_pixel[0], 255 - src_pixel[1],
+                                 255 - src_pixel[2], 255 - src_pixel[3]);
+          dest_b += pixel_weight * src_b;
+          dest_g += pixel_weight * src_g;
+          dest_r += pixel_weight * src_r;
+        }
+        *dest_scan++ = static_cast<uint8_t>(
+            FXRGB2GRAY(CStretchEngine::PixelFromFixed(dest_r),
+                       CStretchEngine::PixelFromFixed(dest_g),
+                       CStretchEngine::PixelFromFixed(dest_b)));
+      } break;
+      case 6:
+        return;
+      case 7: {
+        uint32_t dest_g = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          uint32_t pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          dest_g += pixel_weight * src_scan[j];
+        }
+        memset(dest_scan, CStretchEngine::PixelFromFixed(dest_g), 3);
+        dest_scan += dest_bytes_per_pixel;
+      } break;
+      case 8: {
+        uint32_t dest_r = 0;
+        uint32_t dest_g = 0;
+        uint32_t dest_b = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          uint32_t pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          uint32_t argb = m_SrcPalette[src_scan[j]];
+          dest_r += pixel_weight * FXARGB_R(argb);
+          dest_g += pixel_weight * FXARGB_G(argb);
+          dest_b += pixel_weight * FXARGB_B(argb);
+        }
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r);
+        dest_scan += dest_bytes_per_pixel - 3;
+      } break;
+      case 12: {
+#ifdef PDF_ENABLE_XFA_BMP
+        if (m_pBmpContext) {
+          uint32_t dest_r = 0;
+          uint32_t dest_g = 0;
+          uint32_t dest_b = 0;
+          for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+               j++) {
+            uint32_t pixel_weight =
+                pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+            uint32_t argb = m_SrcPalette[src_scan[j]];
+            dest_r += pixel_weight * FXARGB_R(argb);
+            dest_g += pixel_weight * FXARGB_G(argb);
+            dest_b += pixel_weight * FXARGB_B(argb);
+          }
+          *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b);
+          *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g);
+          *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r);
+          *dest_scan++ = 0xFF;
+          break;
+        }
+#endif  // PDF_ENABLE_XFA_BMP
+        uint32_t dest_a = 0;
+        uint32_t dest_r = 0;
+        uint32_t dest_g = 0;
+        uint32_t dest_b = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          uint32_t pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          unsigned long argb = m_SrcPalette[src_scan[j]];
+          dest_a += pixel_weight * FXARGB_A(argb);
+          dest_r += pixel_weight * FXARGB_R(argb);
+          dest_g += pixel_weight * FXARGB_G(argb);
+          dest_b += pixel_weight * FXARGB_B(argb);
+        }
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_a);
+      } break;
+      case 9: {
+        uint32_t dest_b = 0;
+        uint32_t dest_g = 0;
+        uint32_t dest_r = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          uint32_t pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel;
+          dest_b += pixel_weight * (*src_pixel++);
+          dest_g += pixel_weight * (*src_pixel++);
+          dest_r += pixel_weight * (*src_pixel);
+        }
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r);
+        dest_scan += dest_bytes_per_pixel - 3;
+      } break;
+      case 10: {
+        uint32_t dest_b = 0;
+        uint32_t dest_g = 0;
+        uint32_t dest_r = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          uint32_t pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel;
+          uint8_t src_b = 0;
+          uint8_t src_g = 0;
+          uint8_t src_r = 0;
+          std::tie(src_r, src_g, src_b) =
+              AdobeCMYK_to_sRGB1(255 - src_pixel[0], 255 - src_pixel[1],
+                                 255 - src_pixel[2], 255 - src_pixel[3]);
+          dest_b += pixel_weight * src_b;
+          dest_g += pixel_weight * src_g;
+          dest_r += pixel_weight * src_r;
+        }
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r);
+        dest_scan += dest_bytes_per_pixel - 3;
+      } break;
+      case 11: {
+        uint32_t dest_alpha = 0;
+        uint32_t dest_r = 0;
+        uint32_t dest_g = 0;
+        uint32_t dest_b = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          uint32_t pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel;
+          pixel_weight = pixel_weight * src_pixel[3] / 255;
+          dest_b += pixel_weight * (*src_pixel++);
+          dest_g += pixel_weight * (*src_pixel++);
+          dest_r += pixel_weight * (*src_pixel);
+          dest_alpha += pixel_weight;
+        }
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r);
+        *dest_scan++ = CStretchEngine::PixelFromFixed(dest_alpha * 255);
+      } break;
+      default:
+        return;
+    }
+  }
+}
+
+void ProgressiveDecoder::ResampleVert(
+    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+    double scale_y,
+    int dest_row) {
+  int dest_Bpp = pDeviceBitmap->GetBPP() >> 3;
+  uint32_t dest_ScanOffset = m_startX * dest_Bpp;
+  int dest_top = m_startY;
+  FX_SAFE_INT32 check_dest_row_1 = dest_row;
+  check_dest_row_1 -= pdfium::base::checked_cast<int>(scale_y);
+  int dest_row_1 = check_dest_row_1.ValueOrDie();
+  if (dest_row_1 < dest_top) {
+    int dest_bottom = dest_top + m_sizeY;
+    if (dest_row + (int)scale_y >= dest_bottom - 1) {
+      pdfium::span<const uint8_t> scan_src =
+          pDeviceBitmap->GetScanline(dest_row).subspan(dest_ScanOffset,
+                                                       m_sizeX * dest_Bpp);
+      while (++dest_row < dest_bottom) {
+        fxcrt::spanmove(pDeviceBitmap->GetWritableScanline(dest_row).subspan(
+                            dest_ScanOffset),
+                        scan_src);
+      }
+    }
+    return;
+  }
+  for (; dest_row_1 < dest_row; dest_row_1++) {
+    uint8_t* scan_des = pDeviceBitmap->GetWritableScanline(dest_row_1)
+                            .subspan(dest_ScanOffset)
+                            .data();
+    PixelWeight* pWeight = m_WeightVert.GetPixelWeight(dest_row_1 - dest_top);
+    const uint8_t* scan_src1 =
+        pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top)
+            .subspan(dest_ScanOffset)
+            .data();
+    const uint8_t* scan_src2 =
+        pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top)
+            .subspan(dest_ScanOffset)
+            .data();
+    switch (pDeviceBitmap->GetFormat()) {
+      case FXDIB_Format::kInvalid:
+      case FXDIB_Format::k1bppMask:
+      case FXDIB_Format::k1bppRgb:
+        return;
+      case FXDIB_Format::k8bppMask:
+      case FXDIB_Format::k8bppRgb:
+        if (pDeviceBitmap->HasPalette())
+          return;
+        for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
+          uint32_t dest_g = 0;
+          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_g);
+        }
+        break;
+      case FXDIB_Format::kRgb:
+      case FXDIB_Format::kRgb32:
+        for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
+          uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++);
+          uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++);
+          uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++);
+          scan_src1 += dest_Bpp - 3;
+          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
+          scan_src2 += dest_Bpp - 3;
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_b);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_g);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_r);
+          scan_des += dest_Bpp - 3;
+        }
+        break;
+      case FXDIB_Format::kArgb:
+        for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
+          uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++);
+          uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++);
+          uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++);
+          uint32_t dest_a = pWeight->m_Weights[0] * (*scan_src1++);
+          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_a += pWeight->m_Weights[1] * (*scan_src2++);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_b);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_g);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_r);
+          *scan_des++ = CStretchEngine::PixelFromFixed(dest_a);
+        }
+        break;
+    }
+  }
+  int dest_bottom = dest_top + m_sizeY;
+  if (dest_row + (int)scale_y >= dest_bottom - 1) {
+    pdfium::span<const uint8_t> scan_src =
+        pDeviceBitmap->GetScanline(dest_row).subspan(dest_ScanOffset,
+                                                     m_sizeX * dest_Bpp);
+    while (++dest_row < dest_bottom) {
+      fxcrt::spanmove(
+          pDeviceBitmap->GetWritableScanline(dest_row).subspan(dest_ScanOffset),
+          scan_src);
+    }
+  }
+}
+
+void ProgressiveDecoder::Resample(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+                                  int32_t src_line,
+                                  uint8_t* src_scan,
+                                  FXCodec_Format src_format) {
+  int src_top = m_clipBox.top;
+  int dest_top = m_startY;
+  int src_height = m_clipBox.Height();
+  int dest_height = m_sizeY;
+  if (src_line >= src_top) {
+    double scale_y = static_cast<double>(dest_height) / src_height;
+    int src_row = src_line - src_top;
+    int dest_row = (int)(src_row * scale_y) + dest_top;
+    if (dest_row >= dest_top + dest_height)
+      return;
+
+    ResampleScanline(pDeviceBitmap, dest_row, m_DecodeBuf, src_format);
+    if (scale_y > 1.0)
+      ResampleVert(pDeviceBitmap, scale_y, dest_row);
+  }
+}
+
+std::pair<FXCODEC_STATUS, size_t> ProgressiveDecoder::GetFrames() {
+  if (!(m_status == FXCODEC_STATUS::kFrameReady ||
+        m_status == FXCODEC_STATUS::kFrameToBeContinued)) {
+    return {FXCODEC_STATUS::kError, 0};
+  }
+
+  switch (m_imageType) {
+#ifdef PDF_ENABLE_XFA_BMP
+    case FXCODEC_IMAGE_BMP:
+#endif  // PDF_ENABLE_XFA_BMP
+    case FXCODEC_IMAGE_JPG:
+#ifdef PDF_ENABLE_XFA_PNG
+    case FXCODEC_IMAGE_PNG:
+#endif  // PDF_ENABLE_XFA_PNG
+#ifdef PDF_ENABLE_XFA_TIFF
+    case FXCODEC_IMAGE_TIFF:
+#endif  // PDF_ENABLE_XFA_TIFF
+      m_FrameNumber = 1;
+      m_status = FXCODEC_STATUS::kDecodeReady;
+      return {m_status, 1};
+#ifdef PDF_ENABLE_XFA_GIF
+    case FXCODEC_IMAGE_GIF: {
+      while (true) {
+        GifDecoder::Status readResult;
+        std::tie(readResult, m_FrameNumber) =
+            GifDecoder::LoadFrameInfo(m_pGifContext.get());
+        while (readResult == GifDecoder::Status::kUnfinished) {
+          FXCODEC_STATUS error_status = FXCODEC_STATUS::kError;
+          if (!GifReadMoreData(&error_status))
+            return {error_status, 0};
+
+          std::tie(readResult, m_FrameNumber) =
+              GifDecoder::LoadFrameInfo(m_pGifContext.get());
+        }
+        if (readResult == GifDecoder::Status::kSuccess) {
+          m_status = FXCODEC_STATUS::kDecodeReady;
+          return {m_status, m_FrameNumber};
+        }
+        m_pGifContext = nullptr;
+        m_status = FXCODEC_STATUS::kError;
+        return {m_status, 0};
+      }
+    }
+#endif  // PDF_ENABLE_XFA_GIF
+    default:
+      return {FXCODEC_STATUS::kError, 0};
+  }
+}
+
+FXCODEC_STATUS ProgressiveDecoder::StartDecode(
+    const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+    int start_x,
+    int start_y,
+    int size_x,
+    int size_y) {
+  if (m_status != FXCODEC_STATUS::kDecodeReady)
+    return FXCODEC_STATUS::kError;
+
+  if (!pDIBitmap || pDIBitmap->GetBPP() < 8 || m_FrameNumber == 0)
+    return FXCODEC_STATUS::kError;
+
+  m_pDeviceBitmap = pDIBitmap;
+  if (m_clipBox.IsEmpty())
+    return FXCODEC_STATUS::kError;
+  if (size_x <= 0 || size_x > 65535 || size_y <= 0 || size_y > 65535)
+    return FXCODEC_STATUS::kError;
+
+  FX_RECT device_rc =
+      FX_RECT(start_x, start_y, start_x + size_x, start_y + size_y);
+  int32_t out_range_x = device_rc.right - pDIBitmap->GetWidth();
+  int32_t out_range_y = device_rc.bottom - pDIBitmap->GetHeight();
+  device_rc.Intersect(
+      FX_RECT(0, 0, pDIBitmap->GetWidth(), pDIBitmap->GetHeight()));
+  if (device_rc.IsEmpty())
+    return FXCODEC_STATUS::kError;
+
+  m_startX = device_rc.left;
+  m_startY = device_rc.top;
+  m_sizeX = device_rc.Width();
+  m_sizeY = device_rc.Height();
+  m_FrameCur = 0;
+  if (start_x < 0 || out_range_x > 0) {
+    float scaleX = (float)m_clipBox.Width() / (float)size_x;
+    if (start_x < 0) {
+      m_clipBox.left -= static_cast<int32_t>(ceil((float)start_x * scaleX));
+    }
+    if (out_range_x > 0) {
+      m_clipBox.right -=
+          static_cast<int32_t>(floor((float)out_range_x * scaleX));
+    }
+  }
+  if (start_y < 0 || out_range_y > 0) {
+    float scaleY = (float)m_clipBox.Height() / (float)size_y;
+    if (start_y < 0) {
+      m_clipBox.top -= static_cast<int32_t>(ceil((float)start_y * scaleY));
+    }
+    if (out_range_y > 0) {
+      m_clipBox.bottom -=
+          static_cast<int32_t>(floor((float)out_range_y * scaleY));
+    }
+  }
+  if (m_clipBox.IsEmpty()) {
+    return FXCODEC_STATUS::kError;
+  }
+  switch (m_imageType) {
+#ifdef PDF_ENABLE_XFA_BMP
+    case FXCODEC_IMAGE_BMP:
+      return BmpStartDecode();
+#endif  // PDF_ENABLE_XFA_BMP
+#ifdef PDF_ENABLE_XFA_GIF
+    case FXCODEC_IMAGE_GIF:
+      return GifStartDecode();
+#endif  // PDF_ENABLE_XFA_GIF
+    case FXCODEC_IMAGE_JPG:
+      return JpegStartDecode(pDIBitmap->GetFormat());
+#ifdef PDF_ENABLE_XFA_PNG
+    case FXCODEC_IMAGE_PNG:
+      return PngStartDecode();
+#endif  // PDF_ENABLE_XFA_PNG
+#ifdef PDF_ENABLE_XFA_TIFF
+    case FXCODEC_IMAGE_TIFF:
+      m_status = FXCODEC_STATUS::kDecodeToBeContinued;
+      return m_status;
+#endif  // PDF_ENABLE_XFA_TIFF
+    default:
+      return FXCODEC_STATUS::kError;
+  }
+}
+
+FXCODEC_STATUS ProgressiveDecoder::ContinueDecode() {
+  if (m_status != FXCODEC_STATUS::kDecodeToBeContinued)
+    return FXCODEC_STATUS::kError;
+
+  switch (m_imageType) {
+    case FXCODEC_IMAGE_JPG:
+      return JpegContinueDecode();
+#ifdef PDF_ENABLE_XFA_BMP
+    case FXCODEC_IMAGE_BMP:
+      return BmpContinueDecode();
+#endif  // PDF_ENABLE_XFA_BMP
+#ifdef PDF_ENABLE_XFA_GIF
+    case FXCODEC_IMAGE_GIF:
+      return GifContinueDecode();
+#endif  // PDF_ENABLE_XFA_GIF
+#ifdef PDF_ENABLE_XFA_PNG
+    case FXCODEC_IMAGE_PNG:
+      return PngContinueDecode();
+#endif  // PDF_ENABLE_XFA_PNG
+#ifdef PDF_ENABLE_XFA_TIFF
+    case FXCODEC_IMAGE_TIFF:
+      return TiffContinueDecode();
+#endif  // PDF_ENABLE_XFA_TIFF
+    default:
+      return FXCODEC_STATUS::kError;
+  }
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/progressive_decoder.h b/core/fxcodec/progressive_decoder.h
new file mode 100644
index 0000000..6325c88
--- /dev/null
+++ b/core/fxcodec/progressive_decoder.h
@@ -0,0 +1,281 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_PROGRESSIVE_DECODER_H_
+#define CORE_FXCODEC_PROGRESSIVE_DECODER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+
+#include "core/fxcodec/fx_codec_def.h"
+#include "core/fxcodec/jpeg/jpegmodule.h"
+#include "core/fxcodec/progressive_decoder_iface.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
+#include "core/fxge/dib/cstretchengine.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/containers/span.h"
+
+#ifdef PDF_ENABLE_XFA_BMP
+#include "core/fxcodec/bmp/bmp_decoder.h"
+#endif  // PDF_ENABLE_XFA_BMP
+
+#ifdef PDF_ENABLE_XFA_GIF
+#include "core/fxcodec/gif/gif_decoder.h"
+#endif  // PDF_ENABLE_XFA_GIF
+
+#ifdef PDF_ENABLE_XFA_PNG
+#include "core/fxcodec/png/png_decoder.h"
+#endif  // PDF_ENABLE_XFA_PNG
+
+class CFX_DIBitmap;
+class IFX_SeekableReadStream;
+
+namespace fxcodec {
+
+class CFX_DIBAttribute;
+
+class Dummy {};  // Placeholder to work around C++ syntax issues
+
+class ProgressiveDecoder final :
+#ifdef PDF_ENABLE_XFA_BMP
+    public BmpDecoder::Delegate,
+#endif  // PDF_ENABLE_XFA_BMP
+#ifdef PDF_ENABLE_XFA_GIF
+    public GifDecoder::Delegate,
+#endif  // PDF_ENABLE_XFA_GIF
+#ifdef PDF_ENABLE_XFA_PNG
+    public PngDecoder::Delegate,
+#endif  // PDF_ENABLE_XFA_PNG
+    public Dummy {
+ public:
+  enum FXCodec_Format {
+    FXCodec_Invalid = 0,
+    FXCodec_1bppGray = 0x101,
+    FXCodec_1bppRgb = 0x001,
+    FXCodec_8bppGray = 0x108,
+    FXCodec_8bppRgb = 0x008,
+    FXCodec_Rgb = 0x018,
+    FXCodec_Rgb32 = 0x020,
+    FXCodec_Argb = 0x220,
+    FXCodec_Cmyk = 0x120
+  };
+
+  ProgressiveDecoder();
+  virtual ~ProgressiveDecoder();
+
+  FXCODEC_STATUS LoadImageInfo(RetainPtr<IFX_SeekableReadStream> pFile,
+                               FXCODEC_IMAGE_TYPE imageType,
+                               CFX_DIBAttribute* pAttribute,
+                               bool bSkipImageTypeCheck);
+
+  FXCODEC_IMAGE_TYPE GetType() const { return m_imageType; }
+  int32_t GetWidth() const { return m_SrcWidth; }
+  int32_t GetHeight() const { return m_SrcHeight; }
+  int32_t GetNumComponents() const { return m_SrcComponents; }
+  int32_t GetBPC() const { return m_SrcBPC; }
+  void SetClipBox(FX_RECT* clip);
+
+  std::pair<FXCODEC_STATUS, size_t> GetFrames();
+  FXCODEC_STATUS StartDecode(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                             int start_x,
+                             int start_y,
+                             int size_x,
+                             int size_y);
+
+  FXCODEC_STATUS ContinueDecode();
+
+#ifdef PDF_ENABLE_XFA_PNG
+  // PngDecoder::Delegate
+  bool PngReadHeader(int width,
+                     int height,
+                     int bpc,
+                     int pass,
+                     int* color_type,
+                     double* gamma) override;
+  bool PngAskScanlineBuf(int line, uint8_t** pSrcBuf) override;
+  void PngFillScanlineBufCompleted(int pass, int line) override;
+#endif  // PDF_ENABLE_XFA_PNG
+
+#ifdef PDF_ENABLE_XFA_GIF
+  // GifDecoder::Delegate
+  uint32_t GifCurrentPosition() const override;
+  bool GifInputRecordPositionBuf(uint32_t rcd_pos,
+                                 const FX_RECT& img_rc,
+                                 int32_t pal_num,
+                                 CFX_GifPalette* pal_ptr,
+                                 int32_t trans_index,
+                                 bool interlace) override;
+  void GifReadScanline(int32_t row_num, pdfium::span<uint8_t> row_buf) override;
+#endif  // PDF_ENABLE_XFA_GIF
+
+#ifdef PDF_ENABLE_XFA_BMP
+  // BmpDecoder::Delegate
+  bool BmpInputImagePositionBuf(uint32_t rcd_pos) override;
+  void BmpReadScanline(uint32_t row_num,
+                       pdfium::span<const uint8_t> row_buf) override;
+#endif  // PDF_ENABLE_XFA_BMP
+
+ private:
+  using WeightTable = CStretchEngine::WeightTable;
+  using PixelWeight = CStretchEngine::PixelWeight;
+
+  class HorzTable {
+   public:
+    HorzTable();
+    ~HorzTable();
+
+    void CalculateWeights(int dest_len, int src_len);
+    PixelWeight* GetPixelWeight(int pixel) {
+      return reinterpret_cast<PixelWeight*>(m_pWeightTables.data() +
+                                            pixel * m_ItemSize);
+    }
+
+   private:
+    int m_ItemSize = 0;
+    DataVector<uint8_t> m_pWeightTables;
+  };
+
+  class VertTable {
+   public:
+    VertTable();
+    ~VertTable();
+
+    void CalculateWeights(int dest_len, int src_len);
+    PixelWeight* GetPixelWeight(int pixel) {
+      return reinterpret_cast<PixelWeight*>(m_pWeightTables.data() +
+                                            pixel * m_ItemSize);
+    }
+
+   private:
+    int m_ItemSize = 0;
+    DataVector<uint8_t> m_pWeightTables;
+  };
+
+#ifdef PDF_ENABLE_XFA_BMP
+  bool BmpReadMoreData(ProgressiveDecoderIface::Context* pBmpContext,
+                       FXCODEC_STATUS* err_status);
+  bool BmpDetectImageTypeInBuffer(CFX_DIBAttribute* pAttribute);
+  FXCODEC_STATUS BmpStartDecode();
+  FXCODEC_STATUS BmpContinueDecode();
+#endif  // PDF_ENABLE_XFA_BMP
+
+#ifdef PDF_ENABLE_XFA_GIF
+  bool GifReadMoreData(FXCODEC_STATUS* err_status);
+  bool GifDetectImageTypeInBuffer();
+  FXCODEC_STATUS GifStartDecode();
+  FXCODEC_STATUS GifContinueDecode();
+  void GifDoubleLineResampleVert(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+                                 double scale_y,
+                                 int dest_row);
+#endif  // PDF_ENABLE_XFA_GIF
+
+#ifdef PDF_ENABLE_XFA_PNG
+  void PngOneOneMapResampleHorz(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+                                int32_t dest_line,
+                                pdfium::span<uint8_t> src_span,
+                                FXCodec_Format src_format);
+  bool PngDetectImageTypeInBuffer(CFX_DIBAttribute* pAttribute);
+  FXCODEC_STATUS PngStartDecode();
+  FXCODEC_STATUS PngContinueDecode();
+#endif  // PDF_ENABLE_XFA_PNG
+
+#ifdef PDF_ENABLE_XFA_TIFF
+  bool TiffDetectImageTypeFromFile(CFX_DIBAttribute* pAttribute);
+  FXCODEC_STATUS TiffContinueDecode();
+#endif  // PDF_ENABLE_XFA_TIFF
+
+  bool JpegReadMoreData(FXCODEC_STATUS* err_status);
+  bool JpegDetectImageTypeInBuffer(CFX_DIBAttribute* pAttribute);
+  FXCODEC_STATUS JpegStartDecode(FXDIB_Format format);
+  FXCODEC_STATUS JpegContinueDecode();
+
+  bool DetectImageType(FXCODEC_IMAGE_TYPE imageType,
+                       CFX_DIBAttribute* pAttribute);
+  bool ReadMoreData(ProgressiveDecoderIface* pModule,
+                    ProgressiveDecoderIface::Context* pContext,
+                    FXCODEC_STATUS* err_status);
+
+  int GetDownScale();
+  void GetTransMethod(FXDIB_Format dest_format, FXCodec_Format src_format);
+
+  void ResampleScanline(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+                        int32_t dest_line,
+                        pdfium::span<uint8_t> src_span,
+                        FXCodec_Format src_format);
+  void Resample(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+                int32_t src_line,
+                uint8_t* src_scan,
+                FXCodec_Format src_format);
+  void ResampleVert(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+                    double scale_y,
+                    int dest_row);
+  void ResampleVertBT(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+                      double scale_y,
+                      int dest_row);
+
+  FXCODEC_STATUS m_status = FXCODEC_STATUS::kDecodeFinished;
+  FXCODEC_IMAGE_TYPE m_imageType = FXCODEC_IMAGE_UNKNOWN;
+  RetainPtr<IFX_SeekableReadStream> m_pFile;
+  RetainPtr<CFX_DIBitmap> m_pDeviceBitmap;
+  RetainPtr<CFX_CodecMemory> m_pCodecMemory;
+  DataVector<uint8_t> m_DecodeBuf;
+  DataVector<FX_ARGB> m_SrcPalette;
+  std::unique_ptr<ProgressiveDecoderIface::Context> m_pJpegContext;
+#ifdef PDF_ENABLE_XFA_BMP
+  std::unique_ptr<ProgressiveDecoderIface::Context> m_pBmpContext;
+#endif  // PDF_ENABLE_XFA_BMP
+#ifdef PDF_ENABLE_XFA_GIF
+  std::unique_ptr<ProgressiveDecoderIface::Context> m_pGifContext;
+#endif  // PDF_ENABLE_XFA_GIF
+#ifdef PDF_ENABLE_XFA_PNG
+  std::unique_ptr<ProgressiveDecoderIface::Context> m_pPngContext;
+#endif  // PDF_ENABLE_XFA_PNG
+#ifdef PDF_ENABLE_XFA_TIFF
+  std::unique_ptr<ProgressiveDecoderIface::Context> m_pTiffContext;
+#endif  // PDF_ENABLE_XFA_TIFF
+  uint32_t m_offSet = 0;
+  int m_ScanlineSize = 0;
+  WeightTable m_WeightHorz;
+  VertTable m_WeightVert;
+  HorzTable m_WeightHorzOO;
+  int m_SrcWidth = 0;
+  int m_SrcHeight = 0;
+  int m_SrcComponents = 0;
+  int m_SrcBPC = 0;
+  FX_RECT m_clipBox;
+  int m_startX = 0;
+  int m_startY = 0;
+  int m_sizeX = 0;
+  int m_sizeY = 0;
+  int m_TransMethod = -1;
+  int m_SrcPaletteNumber = 0;
+  int m_SrcRow = 0;
+  FXCodec_Format m_SrcFormat = FXCodec_Invalid;
+  int m_SrcPassNumber = 0;
+  size_t m_FrameNumber = 0;
+  size_t m_FrameCur = 0;
+#ifdef PDF_ENABLE_XFA_GIF
+  int m_GifBgIndex = 0;
+  UNOWNED_PTR_EXCLUSION CFX_GifPalette* m_pGifPalette = nullptr;
+  int32_t m_GifPltNumber = 0;
+  int m_GifTransIndex = -1;
+  FX_RECT m_GifFrameRect;
+#endif  // PDF_ENABLE_XFA_GIF
+#ifdef PDF_ENABLE_XFA_BMP
+  bool m_BmpIsTopBottom = false;
+#endif  // PDF_ENABLE_XFA_BMP
+};
+
+}  // namespace fxcodec
+
+using ProgressiveDecoder = fxcodec::ProgressiveDecoder;
+
+#endif  // CORE_FXCODEC_PROGRESSIVE_DECODER_H_
diff --git a/core/fxcodec/progressive_decoder_iface.h b/core/fxcodec/progressive_decoder_iface.h
new file mode 100644
index 0000000..a21f234
--- /dev/null
+++ b/core/fxcodec/progressive_decoder_iface.h
@@ -0,0 +1,42 @@
+// Copyright 2018 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_PROGRESSIVE_DECODER_IFACE_H_
+#define CORE_FXCODEC_PROGRESSIVE_DECODER_IFACE_H_
+
+#include "core/fxcrt/fx_types.h"
+#include "core/fxcrt/retain_ptr.h"
+
+#ifndef PDF_ENABLE_XFA
+#error "XFA Only"
+#endif
+
+class CFX_CodecMemory;
+
+namespace fxcodec {
+
+class ProgressiveDecoderIface {
+ public:
+  class Context {
+   public:
+    virtual ~Context() = default;
+  };
+
+  virtual ~ProgressiveDecoderIface() = default;
+
+  // Returns the number of unprocessed bytes remaining in the input buffer.
+  virtual FX_FILESIZE GetAvailInput(Context* pContext) const = 0;
+
+  // Provides a new input buffer to the codec. Returns true on success.
+  virtual bool Input(Context* pContext,
+                     RetainPtr<CFX_CodecMemory> codec_memory) = 0;
+};
+
+}  // namespace fxcodec
+
+using fxcodec::ProgressiveDecoderIface;
+
+#endif  // CORE_FXCODEC_PROGRESSIVE_DECODER_IFACE_H_
diff --git a/core/fxcodec/progressive_decoder_unittest.cpp b/core/fxcodec/progressive_decoder_unittest.cpp
new file mode 100644
index 0000000..ccb0c03
--- /dev/null
+++ b/core/fxcodec/progressive_decoder_unittest.cpp
@@ -0,0 +1,477 @@
+// Copyright 2019 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcodec/progressive_decoder.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <array>
+#include <numeric>
+#include <tuple>
+#include <utility>
+
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcodec/fx_codec_def.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
+#include "core/fxcrt/cfx_read_only_vector_stream.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/containers/span.h"
+
+#ifdef PDF_ENABLE_XFA_BMP
+#include "core/fxcodec/bmp/bmp_decoder.h"
+#endif  // PDF_ENABLE_XFA_BMP
+
+#ifdef PDF_ENABLE_XFA_GIF
+#include "core/fxcodec/gif/gif_decoder.h"
+#endif  // PDF_ENABLE_XFA_GIF
+
+namespace fxcodec {
+
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::ElementsAreArray;
+
+template <size_t Size>
+constexpr std::array<uint8_t, Size> IotaArray(uint8_t start) {
+  std::array<uint8_t, Size> result;
+  std::iota(result.begin(), result.end(), start);
+  return result;
+}
+
+FXCODEC_STATUS DecodeToBitmap(ProgressiveDecoder& decoder,
+                              const fxcrt::RetainPtr<CFX_DIBitmap>& bitmap) {
+  FXCODEC_STATUS status = decoder.StartDecode(bitmap, 0, 0, bitmap->GetWidth(),
+                                              bitmap->GetHeight());
+  while (status == FXCODEC_STATUS::kDecodeToBeContinued)
+    status = decoder.ContinueDecode();
+  return status;
+}
+
+}  // namespace
+
+#ifdef PDF_ENABLE_XFA_BMP
+TEST(ProgressiveDecoder, Indexed8Bmp) {
+  static constexpr uint8_t kInput[] = {
+      0x42, 0x4d, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a,
+      0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+      0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b,
+      0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc0,
+      0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+  ProgressiveDecoder decoder;
+
+  auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
+  CFX_DIBAttribute attr;
+  FXCODEC_STATUS status =
+      decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true);
+  ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
+
+  ASSERT_EQ(1, decoder.GetWidth());
+  ASSERT_EQ(1, decoder.GetHeight());
+
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kRgb);
+
+  size_t frames;
+  std::tie(status, frames) = decoder.GetFrames();
+  ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
+  ASSERT_EQ(1u, frames);
+
+  status = DecodeToBitmap(decoder, bitmap);
+  EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
+  EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0x00));
+}
+
+TEST(ProgressiveDecoder, Indexed8BmpWithInvalidIndex) {
+  static constexpr uint8_t kInput[] = {
+      0x42, 0x4d, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a,
+      0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+      0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b,
+      0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc0,
+      0x80, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00};
+
+  ProgressiveDecoder decoder;
+
+  auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
+  CFX_DIBAttribute attr;
+  FXCODEC_STATUS status =
+      decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true);
+  ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
+
+  ASSERT_EQ(1, decoder.GetWidth());
+  ASSERT_EQ(1, decoder.GetHeight());
+
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kRgb);
+
+  size_t frames;
+  std::tie(status, frames) = decoder.GetFrames();
+  ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
+  ASSERT_EQ(1u, frames);
+
+  status = DecodeToBitmap(decoder, bitmap);
+  EXPECT_EQ(FXCODEC_STATUS::kError, status);
+}
+
+TEST(ProgressiveDecoder, Direct24Bmp) {
+  static constexpr uint8_t kInput[] = {
+      0x42, 0x4d, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00,
+      0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+      0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+      0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x80, 0x40, 0x00};
+
+  ProgressiveDecoder decoder;
+
+  auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
+  CFX_DIBAttribute attr;
+  FXCODEC_STATUS status =
+      decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true);
+  ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
+
+  ASSERT_EQ(1, decoder.GetWidth());
+  ASSERT_EQ(1, decoder.GetHeight());
+
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kRgb);
+
+  size_t frames;
+  std::tie(status, frames) = decoder.GetFrames();
+  ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
+  ASSERT_EQ(1u, frames);
+
+  status = DecodeToBitmap(decoder, bitmap);
+  EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
+  EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0x00));
+}
+
+TEST(ProgressiveDecoder, Direct32Bmp) {
+  static constexpr uint8_t kInput[] = {
+      0x42, 0x4d, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00,
+      0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+      0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+      0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x80, 0x40, 0xff};
+
+  ProgressiveDecoder decoder;
+
+  auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
+  CFX_DIBAttribute attr;
+  FXCODEC_STATUS status =
+      decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true);
+  ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
+
+  ASSERT_EQ(1, decoder.GetWidth());
+  ASSERT_EQ(1, decoder.GetHeight());
+
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kRgb);
+
+  size_t frames;
+  std::tie(status, frames) = decoder.GetFrames();
+  ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
+  ASSERT_EQ(1u, frames);
+
+  status = DecodeToBitmap(decoder, bitmap);
+  EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
+  EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0x00));
+}
+
+TEST(ProgressiveDecoder, BmpWithDataOffsetBeforeEndOfHeader) {
+  static constexpr uint8_t kInput[] = {
+      0x42, 0x4d, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00,
+      0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+      0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+      0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x80, 0x40, 0x00};
+
+  ProgressiveDecoder decoder;
+
+  auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
+  CFX_DIBAttribute attr;
+  FXCODEC_STATUS status =
+      decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true);
+  ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
+
+  ASSERT_EQ(1, decoder.GetWidth());
+  ASSERT_EQ(1, decoder.GetHeight());
+
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kRgb);
+
+  size_t frames;
+  std::tie(status, frames) = decoder.GetFrames();
+  ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
+  ASSERT_EQ(1u, frames);
+
+  status = DecodeToBitmap(decoder, bitmap);
+  EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
+  EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0x00));
+}
+
+TEST(ProgressiveDecoder, BmpWithDataOffsetAfterEndOfHeader) {
+  static constexpr uint8_t kInput[] = {
+      0x42, 0x4d, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00,
+      0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+      0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+      0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x80, 0x40, 0x00};
+
+  ProgressiveDecoder decoder;
+
+  auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
+  CFX_DIBAttribute attr;
+  FXCODEC_STATUS status =
+      decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true);
+  ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
+
+  ASSERT_EQ(1, decoder.GetWidth());
+  ASSERT_EQ(1, decoder.GetHeight());
+
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kRgb);
+
+  size_t frames;
+  std::tie(status, frames) = decoder.GetFrames();
+  ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
+  ASSERT_EQ(1u, frames);
+
+  status = DecodeToBitmap(decoder, bitmap);
+  EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
+  EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0x00));
+}
+
+TEST(ProgressiveDecoder, LargeBmp) {
+  // Construct a 24-bit BMP larger than `kBlockSize` (4096 bytes).
+  static constexpr uint8_t kWidth = 37;
+  static constexpr uint8_t kHeight = 38;
+  static constexpr size_t kScanlineSize = kWidth * 3 + 1;
+  DataVector<uint8_t> input = {
+      0x42,    0x4d, 0xd6, 0x10, 0x00, 0x00, 0x00, 0x00,   0x00, 0x00, 0x36,
+      0x00,    0x00, 0x00, 0x28, 0x00, 0x00, 0x00, kWidth, 0x00, 0x00, 0x00,
+      kHeight, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00,   0x00, 0x00, 0x00,
+      0x00,    0xa0, 0x10, 0x00, 0x00, 0x13, 0x0b, 0x00,   0x00, 0x13, 0x0b,
+      0x00,    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   0x00, 0x00};
+  input.resize(54 + kScanlineSize * kHeight);
+  std::iota(input.begin() + 54, input.end(), 0);
+  ASSERT_EQ(4310u, input.size());
+
+  ProgressiveDecoder decoder;
+
+  auto source = pdfium::MakeRetain<CFX_ReadOnlyVectorStream>(std::move(input));
+  CFX_DIBAttribute attr;
+  FXCODEC_STATUS status =
+      decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true);
+  ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
+
+  ASSERT_EQ(kWidth, decoder.GetWidth());
+  ASSERT_EQ(kHeight, decoder.GetHeight());
+
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kRgb);
+
+  size_t frames;
+  std::tie(status, frames) = decoder.GetFrames();
+  ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
+  ASSERT_EQ(1u, frames);
+
+  status = DecodeToBitmap(decoder, bitmap);
+  EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
+
+  for (size_t row = 0; row < kHeight; ++row) {
+    // BMP encodes rows from bottom to top by default.
+    pdfium::span<const uint8_t> scanline =
+        bitmap->GetScanline(kHeight - row - 1);
+
+    EXPECT_THAT(
+        scanline.subspan(0, kScanlineSize - 1),
+        ElementsAreArray(IotaArray<kScanlineSize - 1>(row * kScanlineSize)));
+
+    // Last byte is padding to a 32-bit boundary.
+    EXPECT_EQ(0, scanline[kScanlineSize - 1]);
+  }
+}
+#endif  // PDF_ENABLE_XFA_BMP
+
+#ifdef PDF_ENABLE_XFA_GIF
+TEST(ProgressiveDecoder, Gif87a) {
+  static constexpr uint8_t kInput[] = {
+      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0x01,
+      0x00, 0x40, 0x80, 0xc0, 0x80, 0x80, 0x80, 0x2c, 0x00, 0x00, 0x00, 0x00,
+      0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b};
+
+  ProgressiveDecoder decoder;
+
+  auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
+  CFX_DIBAttribute attr;
+  FXCODEC_STATUS status =
+      decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_GIF, &attr, true);
+  ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
+
+  ASSERT_EQ(1, decoder.GetWidth());
+  ASSERT_EQ(1, decoder.GetHeight());
+
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kArgb);
+
+  size_t frames;
+  std::tie(status, frames) = decoder.GetFrames();
+  ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
+  ASSERT_EQ(1u, frames);
+
+  status = DecodeToBitmap(decoder, bitmap);
+  EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
+  EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0xff));
+}
+
+TEST(ProgressiveDecoder, Gif89a) {
+  static constexpr uint8_t kInput[] = {
+      0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80,
+      0x01, 0x00, 0x40, 0x80, 0xc0, 0x80, 0x80, 0x80, 0x21, 0xf9, 0x04,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01,
+      0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b};
+
+  ProgressiveDecoder decoder;
+
+  auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
+  CFX_DIBAttribute attr;
+  FXCODEC_STATUS status =
+      decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_GIF, &attr, true);
+  ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
+
+  ASSERT_EQ(1, decoder.GetWidth());
+  ASSERT_EQ(1, decoder.GetHeight());
+
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kArgb);
+
+  size_t frames;
+  std::tie(status, frames) = decoder.GetFrames();
+  ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
+  ASSERT_EQ(1u, frames);
+
+  status = DecodeToBitmap(decoder, bitmap);
+  EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
+  EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0xff));
+}
+
+TEST(ProgressiveDecoder, GifInsufficientCodeSize) {
+  // This GIF causes `LZWDecompressor::Create()` to fail because the minimum
+  // code size is too small for the palette.
+  static constexpr uint8_t kInput[] = {
+      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x01, 0x00, 0x01, 0x00, 0x82,
+      0x01, 0x00, 0x40, 0x80, 0xc0, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81,
+      0x82, 0x82, 0x82, 0x83, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x85,
+      0x85, 0x86, 0x86, 0x86, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+      0x01, 0x00, 0x00, 0x02, 0x2,  0x44, 0x01, 0x00, 0x3b};
+
+  ProgressiveDecoder decoder;
+
+  auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
+  CFX_DIBAttribute attr;
+  FXCODEC_STATUS status =
+      decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_GIF, &attr, true);
+  ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
+
+  ASSERT_EQ(1, decoder.GetWidth());
+  ASSERT_EQ(1, decoder.GetHeight());
+
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kArgb);
+
+  size_t frames;
+  std::tie(status, frames) = decoder.GetFrames();
+  ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
+  ASSERT_EQ(1u, frames);
+
+  status = DecodeToBitmap(decoder, bitmap);
+  EXPECT_EQ(FXCODEC_STATUS::kError, status);
+}
+
+TEST(ProgressiveDecoder, GifDecodeAcrossScanlines) {
+  // This GIF contains an LZW code unit split across 2 scanlines. The decoder
+  // must continue decoding the second scanline using the residual data.
+  static constexpr uint8_t kInput[] = {
+      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x02, 0x00, 0x80, 0x01,
+      0x00, 0x40, 0x80, 0xc0, 0x80, 0x80, 0x80, 0x2c, 0x00, 0x00, 0x00, 0x00,
+      0x04, 0x00, 0x02, 0x00, 0x00, 0x02, 0x03, 0x84, 0x6f, 0x05, 0x00, 0x3b};
+
+  ProgressiveDecoder decoder;
+
+  auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
+  CFX_DIBAttribute attr;
+  FXCODEC_STATUS status =
+      decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_GIF, &attr, true);
+  ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
+
+  ASSERT_EQ(4, decoder.GetWidth());
+  ASSERT_EQ(2, decoder.GetHeight());
+
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kArgb);
+
+  size_t frames;
+  std::tie(status, frames) = decoder.GetFrames();
+  ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
+  ASSERT_EQ(1u, frames);
+
+  status = DecodeToBitmap(decoder, bitmap);
+  EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
+  EXPECT_THAT(bitmap->GetScanline(0),
+              ElementsAre(0xc0, 0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff, 0xc0,
+                          0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff));
+  EXPECT_THAT(bitmap->GetScanline(1),
+              ElementsAre(0xc0, 0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff, 0xc0,
+                          0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff));
+}
+
+TEST(ProgressiveDecoder, GifDecodeAcrossSubblocks) {
+  // This GIF contains a scanline split across 2 data sub-blocks. The decoder
+  // must continue decoding in the second sub-block.
+  static constexpr uint8_t kInput[] = {
+      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x02, 0x00,
+      0x80, 0x01, 0x00, 0x40, 0x80, 0xc0, 0x80, 0x80, 0x80, 0x2c,
+      0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x02,
+      0x02, 0x84, 0x6f, 0x01, 0x05, 0x00, 0x3b};
+
+  ProgressiveDecoder decoder;
+
+  auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
+  CFX_DIBAttribute attr;
+  FXCODEC_STATUS status =
+      decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_GIF, &attr, true);
+  ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
+
+  ASSERT_EQ(4, decoder.GetWidth());
+  ASSERT_EQ(2, decoder.GetHeight());
+
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kArgb);
+
+  size_t frames;
+  std::tie(status, frames) = decoder.GetFrames();
+  ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
+  ASSERT_EQ(1u, frames);
+
+  status = DecodeToBitmap(decoder, bitmap);
+  EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
+  EXPECT_THAT(bitmap->GetScanline(0),
+              ElementsAre(0xc0, 0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff, 0xc0,
+                          0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff));
+  EXPECT_THAT(bitmap->GetScanline(1),
+              ElementsAre(0xc0, 0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff, 0xc0,
+                          0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff));
+}
+#endif  // PDF_ENABLE_XFA_GIF
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/progressivedecoder.cpp b/core/fxcodec/progressivedecoder.cpp
deleted file mode 100644
index e948b39..0000000
--- a/core/fxcodec/progressivedecoder.cpp
+++ /dev/null
@@ -1,2313 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/progressivedecoder.h"
-
-#include <algorithm>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "build/build_config.h"
-#include "core/fxcodec/cfx_codec_memory.h"
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxcrt/fx_stream.h"
-#include "core/fxge/dib/cfx_cmyk_to_srgb.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/numerics/safe_math.h"
-#include "third_party/base/ptr_util.h"
-
-namespace fxcodec {
-
-namespace {
-
-constexpr size_t kBlockSize = 4096;
-
-#ifdef PDF_ENABLE_XFA_PNG
-#if defined(OS_MACOSX)
-const double kPngGamma = 1.7;
-#else
-const double kPngGamma = 2.2;
-#endif  // defined(OS_MACOSX)
-#endif  // PDF_ENABLE_XFA_PNG
-
-void RGB2BGR(uint8_t* buffer, int width = 1) {
-  if (buffer && width > 0) {
-    uint8_t temp;
-    int i = 0;
-    int j = 0;
-    for (; i < width; i++, j += 3) {
-      temp = buffer[j];
-      buffer[j] = buffer[j + 2];
-      buffer[j + 2] = temp;
-    }
-  }
-}
-
-}  // namespace
-
-ProgressiveDecoder::CFXCODEC_WeightTable::CFXCODEC_WeightTable() = default;
-
-ProgressiveDecoder::CFXCODEC_WeightTable::~CFXCODEC_WeightTable() = default;
-
-void ProgressiveDecoder::CFXCODEC_WeightTable::Calc(int dest_len, int src_len) {
-  double scale = static_cast<double>(src_len) / dest_len;
-  double base = dest_len < 0 ? src_len : 0.0;
-  m_ItemSize = (int)(sizeof(int) * 2 + sizeof(int) * (ceil(fabs(scale)) + 1));
-  m_DestMin = 0;
-  m_pWeightTables.resize(dest_len * m_ItemSize + 4);
-  if (fabs(scale) < 1.0) {
-    for (int dest_pixel = 0; dest_pixel < dest_len; dest_pixel++) {
-      PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel);
-      double src_pos = dest_pixel * scale + scale / 2 + base;
-      pixel_weights.m_SrcStart = (int)floor((float)src_pos - 1.0f / 2);
-      pixel_weights.m_SrcEnd = (int)floor((float)src_pos + 1.0f / 2);
-      pixel_weights.m_SrcStart = std::max(pixel_weights.m_SrcStart, 0);
-      pixel_weights.m_SrcEnd = std::min(pixel_weights.m_SrcEnd, src_len - 1);
-      if (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd) {
-        pixel_weights.m_Weights[0] = 65536;
-      } else {
-        pixel_weights.m_Weights[1] = FXSYS_roundf(
-            (float)(src_pos - pixel_weights.m_SrcStart - 1.0f / 2) * 65536);
-        pixel_weights.m_Weights[0] = 65536 - pixel_weights.m_Weights[1];
-      }
-    }
-    return;
-  }
-  for (int dest_pixel = 0; dest_pixel < dest_len; dest_pixel++) {
-    PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel);
-    double src_start = dest_pixel * scale + base;
-    double src_end = src_start + scale;
-    int start_i;
-    int end_i;
-    if (src_start < src_end) {
-      start_i = (int)floor((float)src_start);
-      end_i = (int)ceil((float)src_end);
-    } else {
-      start_i = (int)floor((float)src_end);
-      end_i = (int)ceil((float)src_start);
-    }
-    start_i = std::max(start_i, 0);
-    end_i = std::min(end_i, src_len - 1);
-    if (start_i > end_i) {
-      pixel_weights.m_SrcStart = start_i;
-      pixel_weights.m_SrcEnd = start_i;
-      continue;
-    }
-    pixel_weights.m_SrcStart = start_i;
-    pixel_weights.m_SrcEnd = end_i;
-    for (int j = start_i; j <= end_i; j++) {
-      double dest_start = ((float)j - base) / scale;
-      double dest_end = ((float)(j + 1) - base) / scale;
-      if (dest_start > dest_end) {
-        double temp = dest_start;
-        dest_start = dest_end;
-        dest_end = temp;
-      }
-      double area_start =
-          dest_start > (float)(dest_pixel) ? dest_start : (float)(dest_pixel);
-      double area_end = dest_end > (float)(dest_pixel + 1)
-                            ? (float)(dest_pixel + 1)
-                            : dest_end;
-      double weight = area_start >= area_end ? 0.0 : area_end - area_start;
-      if (weight == 0 && j == end_i) {
-        pixel_weights.m_SrcEnd--;
-        break;
-      }
-      pixel_weights.m_Weights[j - start_i] =
-          FXSYS_roundf((float)(weight * 65536));
-    }
-  }
-}
-
-ProgressiveDecoder::CFXCODEC_HorzTable::CFXCODEC_HorzTable() = default;
-
-ProgressiveDecoder::CFXCODEC_HorzTable::~CFXCODEC_HorzTable() = default;
-
-void ProgressiveDecoder::CFXCODEC_HorzTable::Calc(int dest_len, int src_len) {
-  double scale = (double)dest_len / (double)src_len;
-  m_ItemSize = sizeof(int) * 4;
-  int size = dest_len * m_ItemSize + 4;
-  m_pWeightTables.resize(size, 0);
-  if (scale > 1) {
-    int pre_dest_col = 0;
-    for (int src_col = 0; src_col < src_len; src_col++) {
-      double dest_col_f = src_col * scale;
-      int dest_col = FXSYS_roundf((float)dest_col_f);
-      PixelWeight* pWeight = GetPixelWeight(dest_col);
-      pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col;
-      pWeight->m_Weights[0] = 65536;
-      pWeight->m_Weights[1] = 0;
-      if (src_col == src_len - 1 && dest_col < dest_len - 1) {
-        for (int dest_col_index = pre_dest_col + 1; dest_col_index < dest_len;
-             dest_col_index++) {
-          pWeight = GetPixelWeight(dest_col_index);
-          pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col;
-          pWeight->m_Weights[0] = 65536;
-          pWeight->m_Weights[1] = 0;
-        }
-        return;
-      }
-      int dest_col_len = dest_col - pre_dest_col;
-      for (int dest_col_index = pre_dest_col + 1; dest_col_index < dest_col;
-           dest_col_index++) {
-        pWeight = GetPixelWeight(dest_col_index);
-        pWeight->m_SrcStart = src_col - 1;
-        pWeight->m_SrcEnd = src_col;
-        pWeight->m_Weights[0] =
-            FXSYS_roundf((float)(((float)dest_col - (float)dest_col_index) /
-                                 (float)dest_col_len * 65536));
-        pWeight->m_Weights[1] = 65536 - pWeight->m_Weights[0];
-      }
-      pre_dest_col = dest_col;
-    }
-    return;
-  }
-  for (int dest_col = 0; dest_col < dest_len; dest_col++) {
-    double src_col_f = dest_col / scale;
-    int src_col = FXSYS_roundf((float)src_col_f);
-    PixelWeight* pWeight = GetPixelWeight(dest_col);
-    pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col;
-    pWeight->m_Weights[0] = 65536;
-    pWeight->m_Weights[1] = 0;
-  }
-}
-
-ProgressiveDecoder::CFXCODEC_VertTable::CFXCODEC_VertTable() = default;
-
-ProgressiveDecoder::CFXCODEC_VertTable::~CFXCODEC_VertTable() = default;
-
-void ProgressiveDecoder::CFXCODEC_VertTable::Calc(int dest_len, int src_len) {
-  double scale = (double)dest_len / (double)src_len;
-  m_ItemSize = sizeof(int) * 4;
-  int size = dest_len * m_ItemSize + 4;
-  m_pWeightTables.resize(size, 0);
-  if (scale <= 1) {
-    for (int dest_row = 0; dest_row < dest_len; dest_row++) {
-      PixelWeight* pWeight = GetPixelWeight(dest_row);
-      pWeight->m_SrcStart = dest_row;
-      pWeight->m_SrcEnd = dest_row;
-      pWeight->m_Weights[0] = 65536;
-      pWeight->m_Weights[1] = 0;
-    }
-    return;
-  }
-
-  double step = 0.0;
-  int src_row = 0;
-  while (step < (double)dest_len) {
-    int start_step = (int)step;
-    step = scale * (++src_row);
-    int end_step = (int)step;
-    if (end_step >= dest_len) {
-      end_step = dest_len;
-      for (int dest_row = start_step; dest_row < end_step; dest_row++) {
-        PixelWeight* pWeight = GetPixelWeight(dest_row);
-        pWeight->m_SrcStart = start_step;
-        pWeight->m_SrcEnd = start_step;
-        pWeight->m_Weights[0] = 65536;
-        pWeight->m_Weights[1] = 0;
-      }
-      return;
-    }
-    int length = end_step - start_step;
-    {
-      PixelWeight* pWeight = GetPixelWeight(start_step);
-      pWeight->m_SrcStart = start_step;
-      pWeight->m_SrcEnd = start_step;
-      pWeight->m_Weights[0] = 65536;
-      pWeight->m_Weights[1] = 0;
-    }
-    for (int dest_row = start_step + 1; dest_row < end_step; dest_row++) {
-      PixelWeight* pWeight = GetPixelWeight(dest_row);
-      pWeight->m_SrcStart = start_step;
-      pWeight->m_SrcEnd = end_step;
-      pWeight->m_Weights[0] =
-          FXSYS_roundf((float)(end_step - dest_row) / (float)length * 65536);
-      pWeight->m_Weights[1] = 65536 - pWeight->m_Weights[0];
-    }
-  }
-}
-
-ProgressiveDecoder::ProgressiveDecoder(ModuleMgr* pCodecMgr)
-    : m_pCodecMgr(pCodecMgr) {}
-
-ProgressiveDecoder::~ProgressiveDecoder() = default;
-
-#ifdef PDF_ENABLE_XFA_PNG
-bool ProgressiveDecoder::PngReadHeader(int width,
-                                       int height,
-                                       int bpc,
-                                       int pass,
-                                       int* color_type,
-                                       double* gamma) {
-  if (!m_pDeviceBitmap) {
-    m_SrcWidth = width;
-    m_SrcHeight = height;
-    m_SrcBPC = bpc;
-    m_SrcPassNumber = pass;
-    switch (*color_type) {
-      case 0:
-        m_SrcComponents = 1;
-        break;
-      case 4:
-        m_SrcComponents = 2;
-        break;
-      case 2:
-        m_SrcComponents = 3;
-        break;
-      case 3:
-      case 6:
-        m_SrcComponents = 4;
-        break;
-      default:
-        m_SrcComponents = 0;
-        break;
-    }
-    m_clipBox = FX_RECT(0, 0, width, height);
-    return false;
-  }
-  FXDIB_Format format = m_pDeviceBitmap->GetFormat();
-  switch (format) {
-    case FXDIB_1bppMask:
-    case FXDIB_1bppRgb:
-      NOTREACHED();
-      return false;
-    case FXDIB_8bppMask:
-    case FXDIB_8bppRgb:
-      *color_type = 0;
-      break;
-    case FXDIB_Rgb:
-      *color_type = 2;
-      break;
-    case FXDIB_Rgb32:
-    case FXDIB_Argb:
-      *color_type = 6;
-      break;
-    default:
-      NOTREACHED();
-      return false;
-  }
-  *gamma = kPngGamma;
-  return true;
-}
-
-bool ProgressiveDecoder::PngAskScanlineBuf(int line, uint8_t** pSrcBuf) {
-  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
-  if (!pDIBitmap) {
-    NOTREACHED();
-    return false;
-  }
-  if (line >= m_clipBox.top && line < m_clipBox.bottom) {
-    double scale_y = static_cast<double>(m_sizeY) / m_clipBox.Height();
-    int32_t row = (int32_t)((line - m_clipBox.top) * scale_y) + m_startY;
-    const uint8_t* src_scan = pDIBitmap->GetScanline(row);
-    uint8_t* dest_scan = m_pDecodeBuf.get();
-    *pSrcBuf = m_pDecodeBuf.get();
-    int32_t src_Bpp = pDIBitmap->GetBPP() >> 3;
-    int32_t dest_Bpp = (m_SrcFormat & 0xff) >> 3;
-    int32_t src_left = m_startX;
-    int32_t dest_left = m_clipBox.left;
-    src_scan += src_left * src_Bpp;
-    dest_scan += dest_left * dest_Bpp;
-    for (int32_t src_col = 0; src_col < m_sizeX; src_col++) {
-      PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(src_col);
-      if (pPixelWeights->m_SrcStart != pPixelWeights->m_SrcEnd) {
-        continue;
-      }
-      switch (pDIBitmap->GetFormat()) {
-        case FXDIB_1bppMask:
-        case FXDIB_1bppRgb:
-          NOTREACHED();
-          return false;
-        case FXDIB_8bppMask:
-        case FXDIB_8bppRgb: {
-          if (pDIBitmap->GetPalette()) {
-            return false;
-          }
-          uint32_t dest_g = 0;
-          dest_g += pPixelWeights->m_Weights[0] * src_scan[src_col];
-          dest_scan[pPixelWeights->m_SrcStart] = (uint8_t)(dest_g >> 16);
-        } break;
-        case FXDIB_Rgb:
-        case FXDIB_Rgb32: {
-          uint32_t dest_b = 0;
-          uint32_t dest_g = 0;
-          uint32_t dest_r = 0;
-          const uint8_t* p = src_scan + src_col * src_Bpp;
-          dest_b += pPixelWeights->m_Weights[0] * (*p++);
-          dest_g += pPixelWeights->m_Weights[0] * (*p++);
-          dest_r += pPixelWeights->m_Weights[0] * (*p);
-          uint8_t* pDes = &dest_scan[pPixelWeights->m_SrcStart * dest_Bpp];
-          *pDes++ = (uint8_t)((dest_b) >> 16);
-          *pDes++ = (uint8_t)((dest_g) >> 16);
-          *pDes = (uint8_t)((dest_r) >> 16);
-        } break;
-        case FXDIB_Argb: {
-          uint32_t dest_r = 0;
-          uint32_t dest_g = 0;
-          uint32_t dest_b = 0;
-          const uint8_t* p = src_scan + src_col * src_Bpp;
-          dest_b += pPixelWeights->m_Weights[0] * (*p++);
-          dest_g += pPixelWeights->m_Weights[0] * (*p++);
-          dest_r += pPixelWeights->m_Weights[0] * (*p++);
-          uint8_t* pDes = &dest_scan[pPixelWeights->m_SrcStart * dest_Bpp];
-          *pDes++ = (uint8_t)((dest_b) >> 16);
-          *pDes++ = (uint8_t)((dest_g) >> 16);
-          *pDes++ = (uint8_t)((dest_r) >> 16);
-          *pDes = *p;
-        } break;
-        default:
-          return false;
-      }
-    }
-  }
-  return true;
-}
-
-void ProgressiveDecoder::PngFillScanlineBufCompleted(int pass, int line) {
-  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
-  ASSERT(pDIBitmap);
-  int src_top = m_clipBox.top;
-  int src_bottom = m_clipBox.bottom;
-  int dest_top = m_startY;
-  int src_height = m_clipBox.Height();
-  int dest_height = m_sizeY;
-  if (line >= src_top && line < src_bottom) {
-    double scale_y = static_cast<double>(dest_height) / src_height;
-    int src_row = line - src_top;
-    int dest_row = (int)(src_row * scale_y) + dest_top;
-    if (dest_row >= dest_top + dest_height) {
-      return;
-    }
-    PngOneOneMapResampleHorz(pDIBitmap, dest_row, m_pDecodeBuf.get(),
-                             m_SrcFormat);
-    if (m_SrcPassNumber == 1 && scale_y > 1.0) {
-      ResampleVert(pDIBitmap, scale_y, dest_row);
-      return;
-    }
-    if (pass == 6 && scale_y > 1.0) {
-      ResampleVert(pDIBitmap, scale_y, dest_row);
-    }
-  }
-}
-#endif  // PDF_ENABLE_XFA_PNG
-
-#ifdef PDF_ENABLE_XFA_GIF
-void ProgressiveDecoder::GifRecordCurrentPosition(uint32_t& cur_pos) {
-  uint32_t remain_size =
-      m_pCodecMgr->GetGifModule()->GetAvailInput(m_pGifContext.get());
-  cur_pos = m_offSet - remain_size;
-}
-
-bool ProgressiveDecoder::GifInputRecordPositionBuf(uint32_t rcd_pos,
-                                                   const FX_RECT& img_rc,
-                                                   int32_t pal_num,
-                                                   CFX_GifPalette* pal_ptr,
-                                                   int32_t delay_time,
-                                                   bool user_input,
-                                                   int32_t trans_index,
-                                                   int32_t disposal_method,
-                                                   bool interlace) {
-  m_offSet = rcd_pos;
-  m_InvalidateGifBuffer = true;
-
-  FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR;
-  if (!GifReadMoreData(m_pCodecMgr->GetGifModule(), error_status)) {
-    return false;
-  }
-  CFX_GifPalette* pPalette = nullptr;
-  if (pal_num != 0 && pal_ptr) {
-    pPalette = pal_ptr;
-  } else {
-    if (!m_pGifPalette)
-      return false;
-    pal_num = m_GifPltNumber;
-    pPalette = m_pGifPalette;
-  }
-  if (!m_pSrcPalette)
-    m_pSrcPalette.reset(FX_Alloc(FX_ARGB, pal_num));
-  else if (pal_num > m_SrcPaletteNumber)
-    m_pSrcPalette.reset(FX_Realloc(FX_ARGB, m_pSrcPalette.release(), pal_num));
-  if (!m_pSrcPalette)
-    return false;
-
-  m_SrcPaletteNumber = pal_num;
-  for (int i = 0; i < pal_num; i++) {
-    m_pSrcPalette.get()[i] =
-        ArgbEncode(0xff, pPalette[i].r, pPalette[i].g, pPalette[i].b);
-  }
-  m_GifTransIndex = trans_index;
-  m_GifFrameRect = img_rc;
-  m_SrcPassNumber = interlace ? 4 : 1;
-  int32_t pal_index = m_GifBgIndex;
-  RetainPtr<CFX_DIBitmap> pDevice = m_pDeviceBitmap;
-  if (trans_index >= pal_num)
-    trans_index = -1;
-  if (trans_index != -1) {
-    m_pSrcPalette.get()[trans_index] &= 0x00ffffff;
-    if (pDevice->HasAlpha())
-      pal_index = trans_index;
-  }
-  if (pal_index >= pal_num)
-    return false;
-
-  int startX = m_startX;
-  int startY = m_startY;
-  int sizeX = m_sizeX;
-  int sizeY = m_sizeY;
-  int Bpp = pDevice->GetBPP() / 8;
-  FX_ARGB argb = m_pSrcPalette.get()[pal_index];
-  for (int row = 0; row < sizeY; row++) {
-    uint8_t* pScanline =
-        pDevice->GetWritableScanline(row + startY) + startX * Bpp;
-    switch (m_TransMethod) {
-      case 3: {
-        uint8_t gray =
-            FXRGB2GRAY(FXARGB_R(argb), FXARGB_G(argb), FXARGB_B(argb));
-        memset(pScanline, gray, sizeX);
-        break;
-      }
-      case 8: {
-        for (int col = 0; col < sizeX; col++) {
-          *pScanline++ = FXARGB_B(argb);
-          *pScanline++ = FXARGB_G(argb);
-          *pScanline++ = FXARGB_R(argb);
-          pScanline += Bpp - 3;
-        }
-        break;
-      }
-      case 12: {
-        for (int col = 0; col < sizeX; col++) {
-          FXARGB_SETDIB(pScanline, argb);
-          pScanline += 4;
-        }
-        break;
-      }
-    }
-  }
-  return true;
-}
-
-void ProgressiveDecoder::GifReadScanline(int32_t row_num, uint8_t* row_buf) {
-  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
-  ASSERT(pDIBitmap);
-  int32_t img_width = m_GifFrameRect.Width();
-  if (!pDIBitmap->HasAlpha()) {
-    uint8_t* byte_ptr = row_buf;
-    for (int i = 0; i < img_width; i++) {
-      if (*byte_ptr == m_GifTransIndex) {
-        *byte_ptr = m_GifBgIndex;
-      }
-      byte_ptr++;
-    }
-  }
-  int32_t pal_index = m_GifBgIndex;
-  if (m_GifTransIndex != -1 && m_pDeviceBitmap->HasAlpha()) {
-    pal_index = m_GifTransIndex;
-  }
-  memset(m_pDecodeBuf.get(), pal_index, m_SrcWidth);
-  bool bLastPass = (row_num % 2) == 1;
-  int32_t line = row_num + m_GifFrameRect.top;
-  int32_t left = m_GifFrameRect.left;
-  memcpy(m_pDecodeBuf.get() + left, row_buf, img_width);
-  int src_top = m_clipBox.top;
-  int src_bottom = m_clipBox.bottom;
-  int dest_top = m_startY;
-  int src_height = m_clipBox.Height();
-  int dest_height = m_sizeY;
-  if (line < src_top || line >= src_bottom)
-    return;
-
-  double scale_y = static_cast<double>(dest_height) / src_height;
-  int src_row = line - src_top;
-  int dest_row = (int)(src_row * scale_y) + dest_top;
-  if (dest_row >= dest_top + dest_height)
-    return;
-
-  ReSampleScanline(pDIBitmap, dest_row, m_pDecodeBuf.get(), m_SrcFormat);
-  if (scale_y > 1.0 && m_SrcPassNumber == 1) {
-    ResampleVert(pDIBitmap, scale_y, dest_row);
-    return;
-  }
-  if (scale_y <= 1.0)
-    return;
-
-  int dest_bottom = dest_top + m_sizeY;
-  int dest_Bpp = pDIBitmap->GetBPP() >> 3;
-  uint32_t dest_ScanOffet = m_startX * dest_Bpp;
-  if (dest_row + (int)scale_y >= dest_bottom - 1) {
-    const uint8_t* scan_src = pDIBitmap->GetScanline(dest_row) + dest_ScanOffet;
-    int cur_row = dest_row;
-    while (++cur_row < dest_bottom) {
-      uint8_t* scan_des =
-          pDIBitmap->GetWritableScanline(cur_row) + dest_ScanOffet;
-      uint32_t size = m_sizeX * dest_Bpp;
-      memmove(scan_des, scan_src, size);
-    }
-  }
-  if (bLastPass)
-    GifDoubleLineResampleVert(pDIBitmap, scale_y, dest_row);
-}
-#endif  // PDF_ENABLE_XFA_GIF
-
-#ifdef PDF_ENABLE_XFA_BMP
-bool ProgressiveDecoder::BmpInputImagePositionBuf(uint32_t rcd_pos) {
-  m_offSet = rcd_pos;
-  FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR;
-  return BmpReadMoreData(m_pCodecMgr->GetBmpModule(), m_pBmpContext.get(),
-                         error_status);
-}
-
-void ProgressiveDecoder::BmpReadScanline(uint32_t row_num,
-                                         pdfium::span<const uint8_t> row_buf) {
-  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
-  ASSERT(pDIBitmap);
-
-  pdfium::span<const uint8_t> src_span = row_buf.first(m_ScanlineSize);
-  std::copy(std::begin(src_span), std::end(src_span), m_pDecodeBuf.get());
-
-  int src_top = m_clipBox.top;
-  int src_bottom = m_clipBox.bottom;
-  int dest_top = m_startY;
-  int src_height = m_clipBox.Height();
-  int dest_height = m_sizeY;
-  if ((src_top >= 0 && row_num < static_cast<uint32_t>(src_top)) ||
-      src_bottom < 0 || row_num >= static_cast<uint32_t>(src_bottom)) {
-    return;
-  }
-
-  double scale_y = static_cast<double>(dest_height) / src_height;
-  int src_row = row_num - src_top;
-  int dest_row = (int)(src_row * scale_y) + dest_top;
-  if (dest_row >= dest_top + dest_height)
-    return;
-
-  ReSampleScanline(pDIBitmap, dest_row, m_pDecodeBuf.get(), m_SrcFormat);
-  if (scale_y <= 1.0)
-    return;
-
-  if (m_BmpIsTopBottom) {
-    ResampleVert(pDIBitmap, scale_y, dest_row);
-    return;
-  }
-  ResampleVertBT(pDIBitmap, scale_y, dest_row);
-}
-
-void ProgressiveDecoder::ResampleVertBT(
-    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-    double scale_y,
-    int dest_row) {
-  int dest_Bpp = pDeviceBitmap->GetBPP() >> 3;
-  uint32_t dest_ScanOffet = m_startX * dest_Bpp;
-  int dest_top = m_startY;
-  int dest_bottom = m_startY + m_sizeY;
-  pdfium::base::CheckedNumeric<int> check_dest_row_1 = dest_row;
-  check_dest_row_1 += pdfium::base::checked_cast<int>(scale_y);
-  int dest_row_1 = check_dest_row_1.ValueOrDie();
-  if (dest_row_1 >= dest_bottom - 1) {
-    const uint8_t* scan_src =
-        pDeviceBitmap->GetScanline(dest_row) + dest_ScanOffet;
-    while (++dest_row < dest_bottom) {
-      uint8_t* scan_des =
-          pDeviceBitmap->GetWritableScanline(dest_row) + dest_ScanOffet;
-      uint32_t size = m_sizeX * dest_Bpp;
-      memmove(scan_des, scan_src, size);
-    }
-    return;
-  }
-  for (; dest_row_1 > dest_row; dest_row_1--) {
-    uint8_t* scan_des =
-        pDeviceBitmap->GetWritableScanline(dest_row_1) + dest_ScanOffet;
-    PixelWeight* pWeight = m_WeightVert.GetPixelWeight(dest_row_1 - dest_top);
-    const uint8_t* scan_src1 =
-        pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top) +
-        dest_ScanOffet;
-    const uint8_t* scan_src2 =
-        pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top) +
-        dest_ScanOffet;
-    for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
-      switch (pDeviceBitmap->GetFormat()) {
-        case FXDIB_Invalid:
-        case FXDIB_1bppMask:
-        case FXDIB_1bppRgb:
-          return;
-        case FXDIB_8bppMask:
-        case FXDIB_8bppRgb: {
-          if (pDeviceBitmap->GetPalette()) {
-            return;
-          }
-          int dest_g = 0;
-          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
-          *scan_des++ = (uint8_t)(dest_g >> 16);
-        } break;
-        case FXDIB_Rgb:
-        case FXDIB_Rgb32: {
-          uint32_t dest_b = 0;
-          uint32_t dest_g = 0;
-          uint32_t dest_r = 0;
-          dest_b += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_r += pWeight->m_Weights[0] * (*scan_src1++);
-          scan_src1 += dest_Bpp - 3;
-          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
-          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
-          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
-          scan_src2 += dest_Bpp - 3;
-          *scan_des++ = (uint8_t)((dest_b) >> 16);
-          *scan_des++ = (uint8_t)((dest_g) >> 16);
-          *scan_des++ = (uint8_t)((dest_r) >> 16);
-          scan_des += dest_Bpp - 3;
-        } break;
-        case FXDIB_Argb: {
-          uint32_t dest_a = 0;
-          uint32_t dest_b = 0;
-          uint32_t dest_g = 0;
-          uint32_t dest_r = 0;
-          dest_b += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_r += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_a += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
-          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
-          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
-          dest_a += pWeight->m_Weights[1] * (*scan_src2++);
-          *scan_des++ = (uint8_t)((dest_b) >> 16);
-          *scan_des++ = (uint8_t)((dest_g) >> 16);
-          *scan_des++ = (uint8_t)((dest_r) >> 16);
-          *scan_des++ = (uint8_t)((dest_a) >> 16);
-        } break;
-        default:
-          return;
-      }
-    }
-  }
-}
-
-bool ProgressiveDecoder::BmpDetectImageTypeInBuffer(
-    CFX_DIBAttribute* pAttribute) {
-  BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule();
-  if (!pBmpModule) {
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return false;
-  }
-
-  std::unique_ptr<ModuleIface::Context> pBmpContext = pBmpModule->Start(this);
-  pBmpModule->Input(pBmpContext.get(), m_pCodecMemory, nullptr);
-
-  const std::vector<uint32_t>* palette;
-  BmpModule::Status read_result = pBmpModule->ReadHeader(
-      pBmpContext.get(), &m_SrcWidth, &m_SrcHeight, &m_BmpIsTopBottom,
-      &m_SrcComponents, &m_SrcPaletteNumber, &palette, pAttribute);
-  while (read_result == BmpModule::Status::kContinue) {
-    FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT;
-    if (!BmpReadMoreData(pBmpModule, pBmpContext.get(), error_status)) {
-      m_status = error_status;
-      return false;
-    }
-    read_result = pBmpModule->ReadHeader(
-        pBmpContext.get(), &m_SrcWidth, &m_SrcHeight, &m_BmpIsTopBottom,
-        &m_SrcComponents, &m_SrcPaletteNumber, &palette, pAttribute);
-  }
-
-  if (read_result != BmpModule::Status::kSuccess) {
-    m_status = FXCODEC_STATUS_ERR_FORMAT;
-    return false;
-  }
-
-  FXDIB_Format format = FXDIB_Invalid;
-  switch (m_SrcComponents) {
-    case 1:
-      m_SrcFormat = FXCodec_8bppRgb;
-      format = FXDIB_8bppRgb;
-      break;
-    case 3:
-      m_SrcFormat = FXCodec_Rgb;
-      format = FXDIB_Rgb;
-      break;
-    case 4:
-      m_SrcFormat = FXCodec_Rgb32;
-      format = FXDIB_Rgb32;
-      break;
-    default:
-      m_status = FXCODEC_STATUS_ERR_FORMAT;
-      return false;
-  }
-
-  uint32_t pitch = 0;
-  uint32_t neededData = 0;
-  if (!CFX_DIBitmap::CalculatePitchAndSize(m_SrcWidth, m_SrcHeight, format,
-                                           &pitch, &neededData)) {
-    m_status = FXCODEC_STATUS_ERR_FORMAT;
-    return false;
-  }
-
-  uint32_t availableData = m_pFile->GetSize() - m_offSet +
-                           pBmpModule->GetAvailInput(pBmpContext.get());
-  if (neededData > availableData) {
-    m_status = FXCODEC_STATUS_ERR_FORMAT;
-    return false;
-  }
-
-  m_SrcBPC = 8;
-  m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
-  m_pBmpContext = std::move(pBmpContext);
-  if (m_SrcPaletteNumber) {
-    m_pSrcPalette.reset(FX_Alloc(FX_ARGB, m_SrcPaletteNumber));
-    memcpy(m_pSrcPalette.get(), palette->data(),
-           m_SrcPaletteNumber * sizeof(FX_ARGB));
-  } else {
-    m_pSrcPalette.reset();
-  }
-  return true;
-}
-
-bool ProgressiveDecoder::BmpReadMoreData(BmpModule* pBmpModule,
-                                         ModuleIface::Context* pContext,
-                                         FXCODEC_STATUS& err_status) {
-  return ReadMoreData(pBmpModule, pContext, false, err_status);
-}
-
-FXCODEC_STATUS ProgressiveDecoder::BmpStartDecode(
-    const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
-  BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule();
-  if (!pBmpModule) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
-  m_ScanlineSize = FxAlignToBoundary<4>(m_SrcWidth * m_SrcComponents);
-  m_pDecodeBuf.reset(FX_Alloc(uint8_t, m_ScanlineSize));
-  m_WeightHorz.Calc(m_sizeX, m_clipBox.Width());
-  m_WeightVert.Calc(m_sizeY, m_clipBox.Height());
-  m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-  return m_status;
-}
-
-FXCODEC_STATUS ProgressiveDecoder::BmpContinueDecode() {
-  BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule();
-  if (!pBmpModule) {
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-
-  BmpModule::Status read_res = pBmpModule->LoadImage(m_pBmpContext.get());
-  while (read_res == BmpModule::Status::kContinue) {
-    FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH;
-    if (!BmpReadMoreData(pBmpModule, m_pBmpContext.get(), error_status)) {
-      m_pDeviceBitmap = nullptr;
-      m_pFile = nullptr;
-      m_status = error_status;
-      return m_status;
-    }
-    read_res = pBmpModule->LoadImage(m_pBmpContext.get());
-  }
-
-  m_pDeviceBitmap = nullptr;
-  m_pFile = nullptr;
-  m_status = read_res == BmpModule::Status::kSuccess
-                 ? FXCODEC_STATUS_DECODE_FINISH
-                 : FXCODEC_STATUS_ERROR;
-  return m_status;
-}
-#endif  // PDF_ENABLE_XFA_BMP
-
-#ifdef PDF_ENABLE_XFA_GIF
-bool ProgressiveDecoder::GifReadMoreData(GifModule* pGifModule,
-                                         FXCODEC_STATUS& err_status) {
-  if (!ReadMoreData(pGifModule, m_pGifContext.get(), m_InvalidateGifBuffer,
-                    err_status)) {
-    return false;
-  }
-  m_InvalidateGifBuffer = false;
-  return true;
-}
-
-bool ProgressiveDecoder::GifDetectImageTypeInBuffer() {
-  GifModule* pGifModule = m_pCodecMgr->GetGifModule();
-  if (!pGifModule) {
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return false;
-  }
-  m_pGifContext = pGifModule->Start(this);
-  pGifModule->Input(m_pGifContext.get(), m_pCodecMemory, nullptr);
-  m_SrcComponents = 1;
-  CFX_GifDecodeStatus readResult =
-      pGifModule->ReadHeader(m_pGifContext.get(), &m_SrcWidth, &m_SrcHeight,
-                             &m_GifPltNumber, &m_pGifPalette, &m_GifBgIndex);
-  while (readResult == CFX_GifDecodeStatus::Unfinished) {
-    FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT;
-    if (!GifReadMoreData(pGifModule, error_status)) {
-      m_pGifContext = nullptr;
-      m_status = error_status;
-      return false;
-    }
-    readResult =
-        pGifModule->ReadHeader(m_pGifContext.get(), &m_SrcWidth, &m_SrcHeight,
-                               &m_GifPltNumber, &m_pGifPalette, &m_GifBgIndex);
-  }
-  if (readResult == CFX_GifDecodeStatus::Success) {
-    m_SrcBPC = 8;
-    m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
-    return true;
-  }
-  m_pGifContext = nullptr;
-  m_status = FXCODEC_STATUS_ERR_FORMAT;
-  return false;
-}
-
-FXCODEC_STATUS ProgressiveDecoder::GifStartDecode(
-    const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
-  GifModule* pGifModule = m_pCodecMgr->GetGifModule();
-  if (!pGifModule) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  m_SrcFormat = FXCodec_8bppRgb;
-  GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
-  int scanline_size = FxAlignToBoundary<4>(m_SrcWidth);
-  m_pDecodeBuf.reset(FX_Alloc(uint8_t, scanline_size));
-  m_WeightHorz.Calc(m_sizeX, m_clipBox.Width());
-  m_WeightVert.Calc(m_sizeY, m_clipBox.Height());
-  m_FrameCur = 0;
-  m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-  return m_status;
-}
-
-FXCODEC_STATUS ProgressiveDecoder::GifContinueDecode() {
-  GifModule* pGifModule = m_pCodecMgr->GetGifModule();
-  if (!pGifModule) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-
-  CFX_GifDecodeStatus readRes =
-      pGifModule->LoadFrame(m_pGifContext.get(), m_FrameCur);
-  while (readRes == CFX_GifDecodeStatus::Unfinished) {
-    FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH;
-    if (!GifReadMoreData(pGifModule, error_status)) {
-      m_pDeviceBitmap = nullptr;
-      m_pFile = nullptr;
-      m_status = error_status;
-      return m_status;
-    }
-    readRes = pGifModule->LoadFrame(m_pGifContext.get(), m_FrameCur);
-  }
-
-  if (readRes == CFX_GifDecodeStatus::Success) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_DECODE_FINISH;
-    return m_status;
-  }
-
-  m_pDeviceBitmap = nullptr;
-  m_pFile = nullptr;
-  m_status = FXCODEC_STATUS_ERROR;
-  return m_status;
-}
-
-void ProgressiveDecoder::GifDoubleLineResampleVert(
-    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-    double scale_y,
-    int dest_row) {
-  int dest_Bpp = pDeviceBitmap->GetBPP() >> 3;
-  uint32_t dest_ScanOffet = m_startX * dest_Bpp;
-  int dest_top = m_startY;
-  pdfium::base::CheckedNumeric<double> scale_y2 = scale_y;
-  scale_y2 *= 2;
-  pdfium::base::CheckedNumeric<int> check_dest_row_1 = dest_row;
-  check_dest_row_1 -= scale_y2.ValueOrDie();
-  int dest_row_1 = check_dest_row_1.ValueOrDie();
-  dest_row_1 = std::max(dest_row_1, dest_top);
-  for (; dest_row_1 < dest_row; dest_row_1++) {
-    uint8_t* scan_des =
-        pDeviceBitmap->GetWritableScanline(dest_row_1) + dest_ScanOffet;
-    PixelWeight* pWeight = m_WeightVert.GetPixelWeight(dest_row_1 - dest_top);
-    const uint8_t* scan_src1 =
-        pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top) +
-        dest_ScanOffet;
-    const uint8_t* scan_src2 =
-        pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top) +
-        dest_ScanOffet;
-    for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
-      switch (pDeviceBitmap->GetFormat()) {
-        case FXDIB_Invalid:
-        case FXDIB_1bppMask:
-        case FXDIB_1bppRgb:
-          return;
-        case FXDIB_8bppMask:
-        case FXDIB_8bppRgb: {
-          if (pDeviceBitmap->GetPalette()) {
-            return;
-          }
-          int dest_g = 0;
-          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
-          *scan_des++ = (uint8_t)(dest_g >> 16);
-        } break;
-        case FXDIB_Rgb:
-        case FXDIB_Rgb32: {
-          uint32_t dest_b = 0;
-          uint32_t dest_g = 0;
-          uint32_t dest_r = 0;
-          dest_b += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_r += pWeight->m_Weights[0] * (*scan_src1++);
-          scan_src1 += dest_Bpp - 3;
-          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
-          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
-          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
-          scan_src2 += dest_Bpp - 3;
-          *scan_des++ = (uint8_t)((dest_b) >> 16);
-          *scan_des++ = (uint8_t)((dest_g) >> 16);
-          *scan_des++ = (uint8_t)((dest_r) >> 16);
-          scan_des += dest_Bpp - 3;
-        } break;
-        case FXDIB_Argb: {
-          uint32_t dest_a = 0;
-          uint32_t dest_b = 0;
-          uint32_t dest_g = 0;
-          uint32_t dest_r = 0;
-          dest_b += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_r += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_a += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
-          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
-          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
-          dest_a += pWeight->m_Weights[1] * (*scan_src2++);
-          *scan_des++ = (uint8_t)((dest_b) >> 16);
-          *scan_des++ = (uint8_t)((dest_g) >> 16);
-          *scan_des++ = (uint8_t)((dest_r) >> 16);
-          *scan_des++ = (uint8_t)((dest_a) >> 16);
-        } break;
-        default:
-          return;
-      }
-    }
-  }
-  int dest_bottom = dest_top + m_sizeY - 1;
-  if (dest_row + (int)(2 * scale_y) >= dest_bottom &&
-      dest_row + (int)scale_y < dest_bottom) {
-    GifDoubleLineResampleVert(pDeviceBitmap, scale_y, dest_row + (int)scale_y);
-  }
-}
-#endif  // PDF_ENABLE_XFA_GIF
-
-bool ProgressiveDecoder::JpegReadMoreData(JpegModule* pJpegModule,
-                                          FXCODEC_STATUS& err_status) {
-  return ReadMoreData(pJpegModule, m_pJpegContext.get(), false, err_status);
-}
-
-bool ProgressiveDecoder::JpegDetectImageTypeInBuffer(
-    CFX_DIBAttribute* pAttribute) {
-  JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule();
-  m_pJpegContext = pJpegModule->Start();
-  if (!m_pJpegContext) {
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return false;
-  }
-  pJpegModule->Input(m_pJpegContext.get(), m_pCodecMemory, nullptr);
-
-  // Setting jump marker before calling ReadHeader, since a longjmp to
-  // the marker indicates a fatal error.
-  if (setjmp(pJpegModule->GetJumpMark(m_pJpegContext.get())) == -1) {
-    m_pJpegContext.reset();
-    m_status = FXCODEC_STATUS_ERR_FORMAT;
-    return false;
-  }
-
-  int32_t readResult =
-      pJpegModule->ReadHeader(m_pJpegContext.get(), &m_SrcWidth, &m_SrcHeight,
-                              &m_SrcComponents, pAttribute);
-  while (readResult == 2) {
-    FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT;
-    if (!JpegReadMoreData(pJpegModule, error_status)) {
-      m_status = error_status;
-      return false;
-    }
-    readResult =
-        pJpegModule->ReadHeader(m_pJpegContext.get(), &m_SrcWidth, &m_SrcHeight,
-                                &m_SrcComponents, pAttribute);
-  }
-  if (!readResult) {
-    m_SrcBPC = 8;
-    m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
-    return true;
-  }
-  m_pJpegContext.reset();
-  m_status = FXCODEC_STATUS_ERR_FORMAT;
-  return false;
-}
-
-FXCODEC_STATUS ProgressiveDecoder::JpegStartDecode(
-    const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
-  JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule();
-  int down_scale = 1;
-  GetDownScale(down_scale);
-  // Setting jump marker before calling StartScanLine, since a longjmp to
-  // the marker indicates a fatal error.
-  if (setjmp(pJpegModule->GetJumpMark(m_pJpegContext.get())) == -1) {
-    m_pJpegContext.reset();
-    m_status = FXCODEC_STATUS_ERROR;
-    return FXCODEC_STATUS_ERROR;
-  }
-
-  bool startStatus =
-      pJpegModule->StartScanline(m_pJpegContext.get(), down_scale);
-  while (!startStatus) {
-    FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR;
-    if (!JpegReadMoreData(pJpegModule, error_status)) {
-      m_pDeviceBitmap = nullptr;
-      m_pFile = nullptr;
-      m_status = error_status;
-      return m_status;
-    }
-
-    startStatus = pJpegModule->StartScanline(m_pJpegContext.get(), down_scale);
-  }
-  int scanline_size = (m_SrcWidth + down_scale - 1) / down_scale;
-  scanline_size = FxAlignToBoundary<4>(scanline_size * m_SrcComponents);
-  m_pDecodeBuf.reset(FX_Alloc(uint8_t, scanline_size));
-  m_WeightHorz.Calc(m_sizeX, m_clipBox.Width());
-  m_WeightVert.Calc(m_sizeY, m_clipBox.Height());
-  switch (m_SrcComponents) {
-    case 1:
-      m_SrcFormat = FXCodec_8bppGray;
-      break;
-    case 3:
-      m_SrcFormat = FXCodec_Rgb;
-      break;
-    case 4:
-      m_SrcFormat = FXCodec_Cmyk;
-      break;
-  }
-  GetTransMethod(pDIBitmap->GetFormat(), m_SrcFormat);
-  m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-  return m_status;
-}
-
-FXCODEC_STATUS ProgressiveDecoder::JpegContinueDecode() {
-  JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule();
-  // Setting jump marker before calling ReadScanLine, since a longjmp to
-  // the marker indicates a fatal error.
-  if (setjmp(pJpegModule->GetJumpMark(m_pJpegContext.get())) == -1) {
-    m_pJpegContext.reset();
-    m_status = FXCODEC_STATUS_ERROR;
-    return FXCODEC_STATUS_ERROR;
-  }
-
-  while (true) {
-    bool readRes =
-        pJpegModule->ReadScanline(m_pJpegContext.get(), m_pDecodeBuf.get());
-    while (!readRes) {
-      FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH;
-      if (!JpegReadMoreData(pJpegModule, error_status)) {
-        m_pDeviceBitmap = nullptr;
-        m_pFile = nullptr;
-        m_status = error_status;
-        return m_status;
-      }
-      readRes =
-          pJpegModule->ReadScanline(m_pJpegContext.get(), m_pDecodeBuf.get());
-    }
-    if (m_SrcFormat == FXCodec_Rgb) {
-      int src_Bpp = (m_SrcFormat & 0xff) >> 3;
-      RGB2BGR(m_pDecodeBuf.get() + m_clipBox.left * src_Bpp, m_clipBox.Width());
-    }
-    if (m_SrcRow >= m_clipBox.bottom) {
-      m_pDeviceBitmap = nullptr;
-      m_pFile = nullptr;
-      m_status = FXCODEC_STATUS_DECODE_FINISH;
-      return m_status;
-    }
-    Resample(m_pDeviceBitmap, m_SrcRow, m_pDecodeBuf.get(), m_SrcFormat);
-    m_SrcRow++;
-  }
-}
-
-#ifdef PDF_ENABLE_XFA_PNG
-void ProgressiveDecoder::PngOneOneMapResampleHorz(
-    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-    int32_t dest_line,
-    uint8_t* src_scan,
-    FXCodec_Format src_format) {
-  uint8_t* dest_scan = pDeviceBitmap->GetWritableScanline(dest_line);
-  int32_t src_Bpp = (m_SrcFormat & 0xff) >> 3;
-  int32_t dest_Bpp = pDeviceBitmap->GetBPP() >> 3;
-  int32_t src_left = m_clipBox.left;
-  int32_t dest_left = m_startX;
-  src_scan += src_left * src_Bpp;
-  dest_scan += dest_left * dest_Bpp;
-  for (int32_t dest_col = 0; dest_col < m_sizeX; dest_col++) {
-    PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(dest_col);
-    switch (pDeviceBitmap->GetFormat()) {
-      case FXDIB_1bppMask:
-      case FXDIB_1bppRgb:
-        NOTREACHED();
-        return;
-      case FXDIB_8bppMask:
-      case FXDIB_8bppRgb: {
-        if (pDeviceBitmap->GetPalette()) {
-          return;
-        }
-        uint32_t dest_g = 0;
-        dest_g +=
-            pPixelWeights->m_Weights[0] * src_scan[pPixelWeights->m_SrcStart];
-        dest_g +=
-            pPixelWeights->m_Weights[1] * src_scan[pPixelWeights->m_SrcEnd];
-        *dest_scan++ = (uint8_t)(dest_g >> 16);
-      } break;
-      case FXDIB_Rgb:
-      case FXDIB_Rgb32: {
-        uint32_t dest_b = 0;
-        uint32_t dest_g = 0;
-        uint32_t dest_r = 0;
-        const uint8_t* p = src_scan;
-        p = src_scan + pPixelWeights->m_SrcStart * src_Bpp;
-        dest_b += pPixelWeights->m_Weights[0] * (*p++);
-        dest_g += pPixelWeights->m_Weights[0] * (*p++);
-        dest_r += pPixelWeights->m_Weights[0] * (*p);
-        p = src_scan + pPixelWeights->m_SrcEnd * src_Bpp;
-        dest_b += pPixelWeights->m_Weights[1] * (*p++);
-        dest_g += pPixelWeights->m_Weights[1] * (*p++);
-        dest_r += pPixelWeights->m_Weights[1] * (*p);
-        *dest_scan++ = (uint8_t)((dest_b) >> 16);
-        *dest_scan++ = (uint8_t)((dest_g) >> 16);
-        *dest_scan++ = (uint8_t)((dest_r) >> 16);
-        dest_scan += dest_Bpp - 3;
-      } break;
-      case FXDIB_Argb: {
-        uint32_t dest_a = 0;
-        uint32_t dest_b = 0;
-        uint32_t dest_g = 0;
-        uint32_t dest_r = 0;
-        const uint8_t* p = src_scan;
-        p = src_scan + pPixelWeights->m_SrcStart * src_Bpp;
-        dest_b += pPixelWeights->m_Weights[0] * (*p++);
-        dest_g += pPixelWeights->m_Weights[0] * (*p++);
-        dest_r += pPixelWeights->m_Weights[0] * (*p++);
-        dest_a += pPixelWeights->m_Weights[0] * (*p);
-        p = src_scan + pPixelWeights->m_SrcEnd * src_Bpp;
-        dest_b += pPixelWeights->m_Weights[1] * (*p++);
-        dest_g += pPixelWeights->m_Weights[1] * (*p++);
-        dest_r += pPixelWeights->m_Weights[1] * (*p++);
-        dest_a += pPixelWeights->m_Weights[1] * (*p);
-        *dest_scan++ = (uint8_t)((dest_b) >> 16);
-        *dest_scan++ = (uint8_t)((dest_g) >> 16);
-        *dest_scan++ = (uint8_t)((dest_r) >> 16);
-        *dest_scan++ = (uint8_t)((dest_a) >> 16);
-      } break;
-      default:
-        return;
-    }
-  }
-}
-
-bool ProgressiveDecoder::PngDetectImageTypeInBuffer(
-    CFX_DIBAttribute* pAttribute) {
-  PngModule* pPngModule = m_pCodecMgr->GetPngModule();
-  if (!pPngModule) {
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return false;
-  }
-  m_pPngContext = pPngModule->Start(this);
-  if (!m_pPngContext) {
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return false;
-  }
-  while (pPngModule->Input(m_pPngContext.get(), m_pCodecMemory, pAttribute)) {
-    uint32_t remain_size = static_cast<uint32_t>(m_pFile->GetSize()) - m_offSet;
-    uint32_t input_size = std::min<uint32_t>(remain_size, kBlockSize);
-    if (input_size == 0) {
-      m_pPngContext.reset();
-      m_status = FXCODEC_STATUS_ERR_FORMAT;
-      return false;
-    }
-    if (m_pCodecMemory && input_size > m_pCodecMemory->GetSize())
-      m_pCodecMemory = pdfium::MakeRetain<CFX_CodecMemory>(input_size);
-
-    if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBuffer(), m_offSet,
-                                    input_size)) {
-      m_status = FXCODEC_STATUS_ERR_READ;
-      return false;
-    }
-    m_offSet += input_size;
-  }
-  m_pPngContext.reset();
-  if (m_SrcPassNumber == 0) {
-    m_status = FXCODEC_STATUS_ERR_FORMAT;
-    return false;
-  }
-  return true;
-}
-
-FXCODEC_STATUS ProgressiveDecoder::PngStartDecode(
-    const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
-  PngModule* pPngModule = m_pCodecMgr->GetPngModule();
-  if (!pPngModule) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  m_pPngContext = pPngModule->Start(this);
-  if (!m_pPngContext) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  m_offSet = 0;
-  switch (m_pDeviceBitmap->GetFormat()) {
-    case FXDIB_8bppMask:
-    case FXDIB_8bppRgb:
-      m_SrcComponents = 1;
-      m_SrcFormat = FXCodec_8bppGray;
-      break;
-    case FXDIB_Rgb:
-      m_SrcComponents = 3;
-      m_SrcFormat = FXCodec_Rgb;
-      break;
-    case FXDIB_Rgb32:
-    case FXDIB_Argb:
-      m_SrcComponents = 4;
-      m_SrcFormat = FXCodec_Argb;
-      break;
-    default: {
-      m_pDeviceBitmap = nullptr;
-      m_pFile = nullptr;
-      m_status = FXCODEC_STATUS_ERR_PARAMS;
-      return m_status;
-    }
-  }
-  GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
-  int scanline_size = FxAlignToBoundary<4>(m_SrcWidth * m_SrcComponents);
-  m_pDecodeBuf.reset(FX_Alloc(uint8_t, scanline_size));
-  m_WeightHorzOO.Calc(m_sizeX, m_clipBox.Width());
-  m_WeightVert.Calc(m_sizeY, m_clipBox.Height());
-  m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-  return m_status;
-}
-
-FXCODEC_STATUS ProgressiveDecoder::PngContinueDecode() {
-  PngModule* pPngModule = m_pCodecMgr->GetPngModule();
-  if (!pPngModule) {
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  while (true) {
-    uint32_t remain_size = (uint32_t)m_pFile->GetSize() - m_offSet;
-    uint32_t input_size = std::min<uint32_t>(remain_size, kBlockSize);
-    if (input_size == 0) {
-      m_pPngContext.reset();
-      m_pDeviceBitmap = nullptr;
-      m_pFile = nullptr;
-      m_status = FXCODEC_STATUS_DECODE_FINISH;
-      return m_status;
-    }
-    if (m_pCodecMemory && input_size > m_pCodecMemory->GetSize())
-      m_pCodecMemory = pdfium::MakeRetain<CFX_CodecMemory>(input_size);
-
-    bool bResult = m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBuffer(),
-                                              m_offSet, input_size);
-    if (!bResult) {
-      m_pDeviceBitmap = nullptr;
-      m_pFile = nullptr;
-      m_status = FXCODEC_STATUS_ERR_READ;
-      return m_status;
-    }
-    m_offSet += input_size;
-    bResult = pPngModule->Input(m_pPngContext.get(), m_pCodecMemory, nullptr);
-    if (!bResult) {
-      m_pDeviceBitmap = nullptr;
-      m_pFile = nullptr;
-      m_status = FXCODEC_STATUS_ERROR;
-      return m_status;
-    }
-  }
-}
-#endif  // PDF_ENABLE_XFA_PNG
-
-#ifdef PDF_ENABLE_XFA_TIFF
-bool ProgressiveDecoder::TiffDetectImageTypeFromFile(
-    CFX_DIBAttribute* pAttribute) {
-  TiffModule* pTiffModule = m_pCodecMgr->GetTiffModule();
-  if (!pTiffModule) {
-    m_status = FXCODEC_STATUS_ERR_FORMAT;
-    return false;
-  }
-  m_pTiffContext = pTiffModule->CreateDecoder(m_pFile);
-  if (!m_pTiffContext) {
-    m_status = FXCODEC_STATUS_ERR_FORMAT;
-    return false;
-  }
-  int32_t dummy_bpc;
-  bool ret = pTiffModule->LoadFrameInfo(m_pTiffContext.get(), 0, &m_SrcWidth,
-                                        &m_SrcHeight, &m_SrcComponents,
-                                        &dummy_bpc, pAttribute);
-  m_SrcComponents = 4;
-  m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
-  if (!ret) {
-    m_pTiffContext.reset();
-    m_status = FXCODEC_STATUS_ERR_FORMAT;
-    return false;
-  }
-  return true;
-}
-
-FXCODEC_STATUS ProgressiveDecoder::TiffContinueDecode() {
-  TiffModule* pTiffModule = m_pCodecMgr->GetTiffModule();
-  if (!pTiffModule) {
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  bool ret = false;
-  if (m_pDeviceBitmap->GetBPP() == 32 &&
-      m_pDeviceBitmap->GetWidth() == m_SrcWidth && m_SrcWidth == m_sizeX &&
-      m_pDeviceBitmap->GetHeight() == m_SrcHeight && m_SrcHeight == m_sizeY &&
-      m_startX == 0 && m_startY == 0 && m_clipBox.left == 0 &&
-      m_clipBox.top == 0 && m_clipBox.right == m_SrcWidth &&
-      m_clipBox.bottom == m_SrcHeight) {
-    ret = pTiffModule->Decode(m_pTiffContext.get(), m_pDeviceBitmap);
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    if (!ret) {
-      m_status = FXCODEC_STATUS_ERROR;
-      return m_status;
-    }
-    m_status = FXCODEC_STATUS_DECODE_FINISH;
-    return m_status;
-  }
-
-  auto pDIBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  pDIBitmap->Create(m_SrcWidth, m_SrcHeight, FXDIB_Argb);
-  if (!pDIBitmap->GetBuffer()) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  ret = pTiffModule->Decode(m_pTiffContext.get(), pDIBitmap);
-  if (!ret) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERROR;
-    return m_status;
-  }
-  RetainPtr<CFX_DIBitmap> pClipBitmap =
-      (m_clipBox.left == 0 && m_clipBox.top == 0 &&
-       m_clipBox.right == m_SrcWidth && m_clipBox.bottom == m_SrcHeight)
-          ? pDIBitmap
-          : pDIBitmap->Clone(&m_clipBox);
-  if (!pClipBitmap) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  RetainPtr<CFX_DIBitmap> pFormatBitmap;
-  switch (m_pDeviceBitmap->GetFormat()) {
-    case FXDIB_8bppRgb:
-      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-      pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(),
-                            FXDIB_8bppRgb);
-      break;
-    case FXDIB_8bppMask:
-      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-      pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(),
-                            FXDIB_8bppMask);
-      break;
-    case FXDIB_Rgb:
-      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-      pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(),
-                            FXDIB_Rgb);
-      break;
-    case FXDIB_Rgb32:
-      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-      pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(),
-                            FXDIB_Rgb32);
-      break;
-    case FXDIB_Argb:
-      pFormatBitmap = pClipBitmap;
-      break;
-    default:
-      break;
-  }
-  switch (m_pDeviceBitmap->GetFormat()) {
-    case FXDIB_8bppRgb:
-    case FXDIB_8bppMask: {
-      for (int32_t row = 0; row < pClipBitmap->GetHeight(); row++) {
-        const uint8_t* src_line = pClipBitmap->GetScanline(row);
-        uint8_t* dest_line = pFormatBitmap->GetWritableScanline(row);
-        for (int32_t col = 0; col < pClipBitmap->GetWidth(); col++) {
-          uint8_t _a = 255 - src_line[3];
-          uint8_t b = (src_line[0] * src_line[3] + 0xFF * _a) / 255;
-          uint8_t g = (src_line[1] * src_line[3] + 0xFF * _a) / 255;
-          uint8_t r = (src_line[2] * src_line[3] + 0xFF * _a) / 255;
-          *dest_line++ = FXRGB2GRAY(r, g, b);
-          src_line += 4;
-        }
-      }
-    } break;
-    case FXDIB_Rgb:
-    case FXDIB_Rgb32: {
-      int32_t desBpp = (m_pDeviceBitmap->GetFormat() == FXDIB_Rgb) ? 3 : 4;
-      for (int32_t row = 0; row < pClipBitmap->GetHeight(); row++) {
-        const uint8_t* src_line = pClipBitmap->GetScanline(row);
-        uint8_t* dest_line = pFormatBitmap->GetWritableScanline(row);
-        for (int32_t col = 0; col < pClipBitmap->GetWidth(); col++) {
-          uint8_t _a = 255 - src_line[3];
-          uint8_t b = (src_line[0] * src_line[3] + 0xFF * _a) / 255;
-          uint8_t g = (src_line[1] * src_line[3] + 0xFF * _a) / 255;
-          uint8_t r = (src_line[2] * src_line[3] + 0xFF * _a) / 255;
-          *dest_line++ = b;
-          *dest_line++ = g;
-          *dest_line++ = r;
-          dest_line += desBpp - 3;
-          src_line += 4;
-        }
-      }
-    } break;
-    default:
-      break;
-  }
-  if (!pFormatBitmap) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-
-  FXDIB_ResampleOptions options;
-  options.bInterpolateBilinear = true;
-  RetainPtr<CFX_DIBitmap> pStrechBitmap =
-      pFormatBitmap->StretchTo(m_sizeX, m_sizeY, options, nullptr);
-  pFormatBitmap = nullptr;
-  if (!pStrechBitmap) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  m_pDeviceBitmap->TransferBitmap(m_startX, m_startY, m_sizeX, m_sizeY,
-                                  pStrechBitmap, 0, 0);
-  m_pDeviceBitmap = nullptr;
-  m_pFile = nullptr;
-  m_status = FXCODEC_STATUS_DECODE_FINISH;
-  return m_status;
-}
-#endif  // PDF_ENABLE_XFA_TIFF
-
-bool ProgressiveDecoder::DetectImageType(FXCODEC_IMAGE_TYPE imageType,
-                                         CFX_DIBAttribute* pAttribute) {
-#ifdef PDF_ENABLE_XFA_TIFF
-  if (imageType == FXCODEC_IMAGE_TIFF)
-    return TiffDetectImageTypeFromFile(pAttribute);
-#endif  // PDF_ENABLE_XFA_TIFF
-
-  size_t size = std::min<size_t>(m_pFile->GetSize(), kBlockSize);
-  m_pCodecMemory = pdfium::MakeRetain<CFX_CodecMemory>(size);
-  m_offSet = 0;
-  if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBuffer(), m_offSet,
-                                  size)) {
-    m_status = FXCODEC_STATUS_ERR_READ;
-    return false;
-  }
-  m_offSet += size;
-
-  if (imageType == FXCODEC_IMAGE_JPG)
-    return JpegDetectImageTypeInBuffer(pAttribute);
-
-#ifdef PDF_ENABLE_XFA_BMP
-  if (imageType == FXCODEC_IMAGE_BMP)
-    return BmpDetectImageTypeInBuffer(pAttribute);
-#endif  // PDF_ENABLE_XFA_BMP
-
-#ifdef PDF_ENABLE_XFA_GIF
-  if (imageType == FXCODEC_IMAGE_GIF)
-    return GifDetectImageTypeInBuffer();
-#endif  // PDF_ENABLE_XFA_GIF
-
-#ifdef PDF_ENABLE_XFA_PNG
-  if (imageType == FXCODEC_IMAGE_PNG)
-    return PngDetectImageTypeInBuffer(pAttribute);
-#endif  // PDF_ENABLE_XFA_PNG
-
-  m_status = FXCODEC_STATUS_ERR_FORMAT;
-  return false;
-}
-
-bool ProgressiveDecoder::ReadMoreData(ModuleIface* pModule,
-                                      ModuleIface::Context* pContext,
-                                      bool invalidate_buffer,
-                                      FXCODEC_STATUS& err_status) {
-  // Check for EOF.
-  if (m_offSet >= static_cast<uint32_t>(m_pFile->GetSize()))
-    return false;
-
-  // Try to get whatever remains.
-  uint32_t dwBytesToFetchFromFile = m_pFile->GetSize() - m_offSet;
-
-  // Figure out if the codec stopped processing midway through the buffer.
-  size_t dwUnconsumed = 0;
-  if (!invalidate_buffer) {
-    FX_SAFE_SIZE_T avail_input = pModule->GetAvailInput(pContext);
-    if (!avail_input.IsValid())
-      return false;
-    dwUnconsumed = avail_input.ValueOrDie();
-  }
-
-  if (dwUnconsumed == m_pCodecMemory->GetSize()) {
-    // Codec couldn't make any progress against the bytes in the buffer.
-    // Increase the buffer size so that there might be enough contiguous
-    // bytes to allow whatever operation is having difficulty to succeed.
-    dwBytesToFetchFromFile =
-        std::min<uint32_t>(dwBytesToFetchFromFile, kBlockSize);
-    size_t dwNewSize = m_pCodecMemory->GetSize() + dwBytesToFetchFromFile;
-    if (!m_pCodecMemory->TryResize(dwNewSize)) {
-      err_status = FXCODEC_STATUS_ERR_MEMORY;
-      return false;
-    }
-  } else {
-    size_t dwConsumed = m_pCodecMemory->GetSize() - dwUnconsumed;
-    m_pCodecMemory->Consume(dwConsumed);
-    dwBytesToFetchFromFile =
-        std::min<uint32_t>(dwBytesToFetchFromFile, dwConsumed);
-  }
-
-  // Append new data past the bytes not yet processed by the codec.
-  if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBuffer() + dwUnconsumed,
-                                  m_offSet, dwBytesToFetchFromFile)) {
-    err_status = FXCODEC_STATUS_ERR_READ;
-    return false;
-  }
-  m_offSet += dwBytesToFetchFromFile;
-  return pModule->Input(pContext, m_pCodecMemory, nullptr);
-}
-
-FXCODEC_STATUS ProgressiveDecoder::LoadImageInfo(
-    const RetainPtr<IFX_SeekableReadStream>& pFile,
-    FXCODEC_IMAGE_TYPE imageType,
-    CFX_DIBAttribute* pAttribute,
-    bool bSkipImageTypeCheck) {
-  ASSERT(pAttribute);
-
-  switch (m_status) {
-    case FXCODEC_STATUS_FRAME_READY:
-    case FXCODEC_STATUS_FRAME_TOBECONTINUE:
-    case FXCODEC_STATUS_DECODE_READY:
-    case FXCODEC_STATUS_DECODE_TOBECONTINUE:
-      return FXCODEC_STATUS_ERROR;
-    default:
-      break;
-  }
-  if (!pFile) {
-    m_status = FXCODEC_STATUS_ERR_PARAMS;
-    m_pFile = nullptr;
-    return m_status;
-  }
-  m_pFile = pFile;
-  m_offSet = 0;
-  m_SrcWidth = m_SrcHeight = 0;
-  m_SrcComponents = m_SrcBPC = 0;
-  m_clipBox = FX_RECT();
-  m_startX = m_startY = 0;
-  m_sizeX = m_sizeY = 0;
-  m_SrcPassNumber = 0;
-  if (imageType != FXCODEC_IMAGE_UNKNOWN &&
-      DetectImageType(imageType, pAttribute)) {
-    m_imageType = imageType;
-    m_status = FXCODEC_STATUS_FRAME_READY;
-    return m_status;
-  }
-  // If we got here then the image data does not match the requested decoder.
-  // If we're skipping the type check then bail out at this point and return
-  // the failed status.
-  if (bSkipImageTypeCheck)
-    return m_status;
-
-  for (int type = FXCODEC_IMAGE_UNKNOWN + 1; type < FXCODEC_IMAGE_MAX; type++) {
-    if (DetectImageType(static_cast<FXCODEC_IMAGE_TYPE>(type), pAttribute)) {
-      m_imageType = static_cast<FXCODEC_IMAGE_TYPE>(type);
-      m_status = FXCODEC_STATUS_FRAME_READY;
-      return m_status;
-    }
-  }
-  m_status = FXCODEC_STATUS_ERR_FORMAT;
-  m_pFile = nullptr;
-  return m_status;
-}
-
-void ProgressiveDecoder::SetClipBox(FX_RECT* clip) {
-  if (m_status != FXCODEC_STATUS_FRAME_READY)
-    return;
-
-  if (clip->IsEmpty()) {
-    m_clipBox = FX_RECT();
-    return;
-  }
-  clip->left = std::max(clip->left, 0);
-  clip->right = std::min(clip->right, m_SrcWidth);
-  clip->top = std::max(clip->top, 0);
-  clip->bottom = std::min(clip->bottom, m_SrcHeight);
-  if (clip->IsEmpty()) {
-    m_clipBox = FX_RECT();
-    return;
-  }
-  m_clipBox = *clip;
-}
-
-void ProgressiveDecoder::GetDownScale(int& down_scale) {
-  down_scale = 1;
-  int ratio_w = m_clipBox.Width() / m_sizeX;
-  int ratio_h = m_clipBox.Height() / m_sizeY;
-  int ratio = (ratio_w > ratio_h) ? ratio_h : ratio_w;
-  if (ratio >= 8) {
-    down_scale = 8;
-  } else if (ratio >= 4) {
-    down_scale = 4;
-  } else if (ratio >= 2) {
-    down_scale = 2;
-  }
-  m_clipBox.left /= down_scale;
-  m_clipBox.right /= down_scale;
-  m_clipBox.top /= down_scale;
-  m_clipBox.bottom /= down_scale;
-  if (m_clipBox.right == m_clipBox.left) {
-    m_clipBox.right = m_clipBox.left + 1;
-  }
-  if (m_clipBox.bottom == m_clipBox.top) {
-    m_clipBox.bottom = m_clipBox.top + 1;
-  }
-}
-
-void ProgressiveDecoder::GetTransMethod(FXDIB_Format dest_format,
-                                        FXCodec_Format src_format) {
-  switch (dest_format) {
-    case FXDIB_1bppMask:
-    case FXDIB_1bppRgb: {
-      switch (src_format) {
-        case FXCodec_1bppGray:
-          m_TransMethod = 0;
-          break;
-        default:
-          m_TransMethod = -1;
-      }
-    } break;
-    case FXDIB_8bppMask:
-    case FXDIB_8bppRgb: {
-      switch (src_format) {
-        case FXCodec_1bppGray:
-          m_TransMethod = 1;
-          break;
-        case FXCodec_8bppGray:
-          m_TransMethod = 2;
-          break;
-        case FXCodec_1bppRgb:
-        case FXCodec_8bppRgb:
-          m_TransMethod = 3;
-          break;
-        case FXCodec_Rgb:
-        case FXCodec_Rgb32:
-        case FXCodec_Argb:
-          m_TransMethod = 4;
-          break;
-        case FXCodec_Cmyk:
-          m_TransMethod = 5;
-          break;
-        default:
-          m_TransMethod = -1;
-      }
-    } break;
-    case FXDIB_Rgb: {
-      switch (src_format) {
-        case FXCodec_1bppGray:
-          m_TransMethod = 6;
-          break;
-        case FXCodec_8bppGray:
-          m_TransMethod = 7;
-          break;
-        case FXCodec_1bppRgb:
-        case FXCodec_8bppRgb:
-          m_TransMethod = 8;
-          break;
-        case FXCodec_Rgb:
-        case FXCodec_Rgb32:
-        case FXCodec_Argb:
-          m_TransMethod = 9;
-          break;
-        case FXCodec_Cmyk:
-          m_TransMethod = 10;
-          break;
-        default:
-          m_TransMethod = -1;
-      }
-    } break;
-    case FXDIB_Rgb32:
-    case FXDIB_Argb: {
-      switch (src_format) {
-        case FXCodec_1bppGray:
-          m_TransMethod = 6;
-          break;
-        case FXCodec_8bppGray:
-          m_TransMethod = 7;
-          break;
-        case FXCodec_1bppRgb:
-        case FXCodec_8bppRgb:
-          if (dest_format == FXDIB_Argb) {
-            m_TransMethod = 12;
-          } else {
-            m_TransMethod = 8;
-          }
-          break;
-        case FXCodec_Rgb:
-        case FXCodec_Rgb32:
-          m_TransMethod = 9;
-          break;
-        case FXCodec_Cmyk:
-          m_TransMethod = 10;
-          break;
-        case FXCodec_Argb:
-          m_TransMethod = 11;
-          break;
-        default:
-          m_TransMethod = -1;
-      }
-    } break;
-    default:
-      m_TransMethod = -1;
-  }
-}
-
-void ProgressiveDecoder::ReSampleScanline(
-    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-    int dest_line,
-    uint8_t* src_scan,
-    FXCodec_Format src_format) {
-  int src_left = m_clipBox.left;
-  int dest_left = m_startX;
-  uint8_t* dest_scan =
-      pDeviceBitmap->GetBuffer() + dest_line * pDeviceBitmap->GetPitch();
-  int src_bytes_per_pixel = (src_format & 0xff) / 8;
-  int dest_bytes_per_pixel = pDeviceBitmap->GetBPP() / 8;
-  src_scan += src_left * src_bytes_per_pixel;
-  dest_scan += dest_left * dest_bytes_per_pixel;
-  for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
-    PixelWeight* pPixelWeights = m_WeightHorz.GetPixelWeight(dest_col);
-    switch (m_TransMethod) {
-      case -1:
-        return;
-      case 0:
-        return;
-      case 1:
-        return;
-      case 2: {
-        uint32_t dest_g = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          dest_g += pixel_weight * src_scan[j];
-        }
-        *dest_scan++ = (uint8_t)(dest_g >> 16);
-      } break;
-      case 3: {
-        int dest_r = 0;
-        int dest_g = 0;
-        int dest_b = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          unsigned long argb = m_pSrcPalette.get()[src_scan[j]];
-          dest_r += pixel_weight * FXARGB_R(argb);
-          dest_g += pixel_weight * FXARGB_G(argb);
-          dest_b += pixel_weight * FXARGB_B(argb);
-        }
-        *dest_scan++ =
-            (uint8_t)FXRGB2GRAY((dest_r >> 16), (dest_g >> 16), (dest_b >> 16));
-      } break;
-      case 4: {
-        uint32_t dest_b = 0;
-        uint32_t dest_g = 0;
-        uint32_t dest_r = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel;
-          dest_b += pixel_weight * (*src_pixel++);
-          dest_g += pixel_weight * (*src_pixel++);
-          dest_r += pixel_weight * (*src_pixel);
-        }
-        *dest_scan++ =
-            (uint8_t)FXRGB2GRAY((dest_r >> 16), (dest_g >> 16), (dest_b >> 16));
-      } break;
-      case 5: {
-        uint32_t dest_b = 0;
-        uint32_t dest_g = 0;
-        uint32_t dest_r = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel;
-          uint8_t src_b = 0;
-          uint8_t src_g = 0;
-          uint8_t src_r = 0;
-          std::tie(src_r, src_g, src_b) =
-              AdobeCMYK_to_sRGB1(255 - src_pixel[0], 255 - src_pixel[1],
-                                 255 - src_pixel[2], 255 - src_pixel[3]);
-          dest_b += pixel_weight * src_b;
-          dest_g += pixel_weight * src_g;
-          dest_r += pixel_weight * src_r;
-        }
-        *dest_scan++ =
-            (uint8_t)FXRGB2GRAY((dest_r >> 16), (dest_g >> 16), (dest_b >> 16));
-      } break;
-      case 6:
-        return;
-      case 7: {
-        uint32_t dest_g = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          dest_g += pixel_weight * src_scan[j];
-        }
-        memset(dest_scan, (uint8_t)(dest_g >> 16), 3);
-        dest_scan += dest_bytes_per_pixel;
-      } break;
-      case 8: {
-        int dest_r = 0;
-        int dest_g = 0;
-        int dest_b = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          unsigned long argb = m_pSrcPalette.get()[src_scan[j]];
-          dest_r += pixel_weight * FXARGB_R(argb);
-          dest_g += pixel_weight * FXARGB_G(argb);
-          dest_b += pixel_weight * FXARGB_B(argb);
-        }
-        *dest_scan++ = (uint8_t)((dest_b) >> 16);
-        *dest_scan++ = (uint8_t)((dest_g) >> 16);
-        *dest_scan++ = (uint8_t)((dest_r) >> 16);
-        dest_scan += dest_bytes_per_pixel - 3;
-      } break;
-      case 12: {
-#ifdef PDF_ENABLE_XFA_BMP
-        if (m_pBmpContext) {
-          int dest_r = 0;
-          int dest_g = 0;
-          int dest_b = 0;
-          for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-               j++) {
-            int pixel_weight =
-                pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-            unsigned long argb = m_pSrcPalette.get()[src_scan[j]];
-            dest_r += pixel_weight * FXARGB_R(argb);
-            dest_g += pixel_weight * FXARGB_G(argb);
-            dest_b += pixel_weight * FXARGB_B(argb);
-          }
-          *dest_scan++ = (uint8_t)((dest_b) >> 16);
-          *dest_scan++ = (uint8_t)((dest_g) >> 16);
-          *dest_scan++ = (uint8_t)((dest_r) >> 16);
-          *dest_scan++ = 0xFF;
-          break;
-        }
-#endif  // PDF_ENABLE_XFA_BMP
-        int dest_a = 0;
-        int dest_r = 0;
-        int dest_g = 0;
-        int dest_b = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          unsigned long argb = m_pSrcPalette.get()[src_scan[j]];
-          dest_a += pixel_weight * FXARGB_A(argb);
-          dest_r += pixel_weight * FXARGB_R(argb);
-          dest_g += pixel_weight * FXARGB_G(argb);
-          dest_b += pixel_weight * FXARGB_B(argb);
-        }
-        *dest_scan++ = (uint8_t)((dest_b) >> 16);
-        *dest_scan++ = (uint8_t)((dest_g) >> 16);
-        *dest_scan++ = (uint8_t)((dest_r) >> 16);
-        *dest_scan++ = (uint8_t)((dest_a) >> 16);
-      } break;
-      case 9: {
-        uint32_t dest_b = 0;
-        uint32_t dest_g = 0;
-        uint32_t dest_r = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel;
-          dest_b += pixel_weight * (*src_pixel++);
-          dest_g += pixel_weight * (*src_pixel++);
-          dest_r += pixel_weight * (*src_pixel);
-        }
-        *dest_scan++ = (uint8_t)((dest_b) >> 16);
-        *dest_scan++ = (uint8_t)((dest_g) >> 16);
-        *dest_scan++ = (uint8_t)((dest_r) >> 16);
-        dest_scan += dest_bytes_per_pixel - 3;
-      } break;
-      case 10: {
-        uint32_t dest_b = 0;
-        uint32_t dest_g = 0;
-        uint32_t dest_r = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel;
-          uint8_t src_b = 0;
-          uint8_t src_g = 0;
-          uint8_t src_r = 0;
-          std::tie(src_r, src_g, src_b) =
-              AdobeCMYK_to_sRGB1(255 - src_pixel[0], 255 - src_pixel[1],
-                                 255 - src_pixel[2], 255 - src_pixel[3]);
-          dest_b += pixel_weight * src_b;
-          dest_g += pixel_weight * src_g;
-          dest_r += pixel_weight * src_r;
-        }
-        *dest_scan++ = (uint8_t)((dest_b) >> 16);
-        *dest_scan++ = (uint8_t)((dest_g) >> 16);
-        *dest_scan++ = (uint8_t)((dest_r) >> 16);
-        dest_scan += dest_bytes_per_pixel - 3;
-      } break;
-      case 11: {
-        uint32_t dest_alpha = 0;
-        uint32_t dest_r = 0;
-        uint32_t dest_g = 0;
-        uint32_t dest_b = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel;
-          pixel_weight = pixel_weight * src_pixel[3] / 255;
-          dest_b += pixel_weight * (*src_pixel++);
-          dest_g += pixel_weight * (*src_pixel++);
-          dest_r += pixel_weight * (*src_pixel);
-          dest_alpha += pixel_weight;
-        }
-        *dest_scan++ = (uint8_t)((dest_b) >> 16);
-        *dest_scan++ = (uint8_t)((dest_g) >> 16);
-        *dest_scan++ = (uint8_t)((dest_r) >> 16);
-        *dest_scan++ = (uint8_t)((dest_alpha * 255) >> 16);
-      } break;
-      default:
-        return;
-    }
-  }
-}
-
-void ProgressiveDecoder::ResampleVert(
-    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-    double scale_y,
-    int dest_row) {
-  int dest_Bpp = pDeviceBitmap->GetBPP() >> 3;
-  uint32_t dest_ScanOffet = m_startX * dest_Bpp;
-  int dest_top = m_startY;
-  pdfium::base::CheckedNumeric<int> check_dest_row_1 = dest_row;
-  check_dest_row_1 -= pdfium::base::checked_cast<int>(scale_y);
-  int dest_row_1 = check_dest_row_1.ValueOrDie();
-  if (dest_row_1 < dest_top) {
-    int dest_bottom = dest_top + m_sizeY;
-    if (dest_row + (int)scale_y >= dest_bottom - 1) {
-      const uint8_t* scan_src =
-          pDeviceBitmap->GetScanline(dest_row) + dest_ScanOffet;
-      while (++dest_row < dest_bottom) {
-        uint8_t* scan_des =
-            pDeviceBitmap->GetWritableScanline(dest_row) + dest_ScanOffet;
-        uint32_t size = m_sizeX * dest_Bpp;
-        memmove(scan_des, scan_src, size);
-      }
-    }
-    return;
-  }
-  for (; dest_row_1 < dest_row; dest_row_1++) {
-    uint8_t* scan_des =
-        pDeviceBitmap->GetWritableScanline(dest_row_1) + dest_ScanOffet;
-    PixelWeight* pWeight = m_WeightVert.GetPixelWeight(dest_row_1 - dest_top);
-    const uint8_t* scan_src1 =
-        pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top) +
-        dest_ScanOffet;
-    const uint8_t* scan_src2 =
-        pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top) +
-        dest_ScanOffet;
-    for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
-      switch (pDeviceBitmap->GetFormat()) {
-        case FXDIB_Invalid:
-        case FXDIB_1bppMask:
-        case FXDIB_1bppRgb:
-          return;
-        case FXDIB_8bppMask:
-        case FXDIB_8bppRgb: {
-          if (pDeviceBitmap->GetPalette()) {
-            return;
-          }
-          int dest_g = 0;
-          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
-          *scan_des++ = (uint8_t)(dest_g >> 16);
-        } break;
-        case FXDIB_Rgb:
-        case FXDIB_Rgb32: {
-          uint32_t dest_b = 0;
-          uint32_t dest_g = 0;
-          uint32_t dest_r = 0;
-          dest_b += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_r += pWeight->m_Weights[0] * (*scan_src1++);
-          scan_src1 += dest_Bpp - 3;
-          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
-          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
-          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
-          scan_src2 += dest_Bpp - 3;
-          *scan_des++ = (uint8_t)((dest_b) >> 16);
-          *scan_des++ = (uint8_t)((dest_g) >> 16);
-          *scan_des++ = (uint8_t)((dest_r) >> 16);
-          scan_des += dest_Bpp - 3;
-        } break;
-        case FXDIB_Argb: {
-          uint32_t dest_a = 0;
-          uint32_t dest_b = 0;
-          uint32_t dest_g = 0;
-          uint32_t dest_r = 0;
-          dest_b += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_r += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_a += pWeight->m_Weights[0] * (*scan_src1++);
-          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
-          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
-          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
-          dest_a += pWeight->m_Weights[1] * (*scan_src2++);
-          *scan_des++ = (uint8_t)((dest_b) >> 16);
-          *scan_des++ = (uint8_t)((dest_g) >> 16);
-          *scan_des++ = (uint8_t)((dest_r) >> 16);
-          *scan_des++ = (uint8_t)((dest_a) >> 16);
-        } break;
-        default:
-          return;
-      }
-    }
-  }
-  int dest_bottom = dest_top + m_sizeY;
-  if (dest_row + (int)scale_y >= dest_bottom - 1) {
-    const uint8_t* scan_src =
-        pDeviceBitmap->GetScanline(dest_row) + dest_ScanOffet;
-    while (++dest_row < dest_bottom) {
-      uint8_t* scan_des =
-          pDeviceBitmap->GetWritableScanline(dest_row) + dest_ScanOffet;
-      uint32_t size = m_sizeX * dest_Bpp;
-      memmove(scan_des, scan_src, size);
-    }
-  }
-}
-
-void ProgressiveDecoder::Resample(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-                                  int32_t src_line,
-                                  uint8_t* src_scan,
-                                  FXCodec_Format src_format) {
-  int src_top = m_clipBox.top;
-  int dest_top = m_startY;
-  int src_height = m_clipBox.Height();
-  int dest_height = m_sizeY;
-  if (src_line >= src_top) {
-    double scale_y = static_cast<double>(dest_height) / src_height;
-    int src_row = src_line - src_top;
-    int dest_row = (int)(src_row * scale_y) + dest_top;
-    if (dest_row >= dest_top + dest_height)
-      return;
-
-    ReSampleScanline(pDeviceBitmap, dest_row, m_pDecodeBuf.get(), src_format);
-    if (scale_y > 1.0)
-      ResampleVert(pDeviceBitmap, scale_y, dest_row);
-  }
-}
-
-std::pair<FXCODEC_STATUS, size_t> ProgressiveDecoder::GetFrames() {
-  if (!(m_status == FXCODEC_STATUS_FRAME_READY ||
-        m_status == FXCODEC_STATUS_FRAME_TOBECONTINUE)) {
-    return {FXCODEC_STATUS_ERROR, 0};
-  }
-
-  switch (m_imageType) {
-#ifdef PDF_ENABLE_XFA_BMP
-    case FXCODEC_IMAGE_BMP:
-#endif  // PDF_ENABLE_XFA_BMP
-    case FXCODEC_IMAGE_JPG:
-#ifdef PDF_ENABLE_XFA_PNG
-    case FXCODEC_IMAGE_PNG:
-#endif  // PDF_ENABLE_XFA_PNG
-#ifdef PDF_ENABLE_XFA_TIFF
-    case FXCODEC_IMAGE_TIFF:
-#endif  // PDF_ENABLE_XFA_TIFF
-      m_FrameNumber = 1;
-      m_status = FXCODEC_STATUS_DECODE_READY;
-      return {m_status, 1};
-#ifdef PDF_ENABLE_XFA_GIF
-    case FXCODEC_IMAGE_GIF: {
-      GifModule* pGifModule = m_pCodecMgr->GetGifModule();
-      if (!pGifModule) {
-        m_status = FXCODEC_STATUS_ERR_MEMORY;
-        return {m_status, 0};
-      }
-      while (true) {
-        CFX_GifDecodeStatus readResult;
-        std::tie(readResult, m_FrameNumber) =
-            pGifModule->LoadFrameInfo(m_pGifContext.get());
-        while (readResult == CFX_GifDecodeStatus::Unfinished) {
-          FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_READ;
-          if (!GifReadMoreData(pGifModule, error_status))
-            return {error_status, 0};
-
-          std::tie(readResult, m_FrameNumber) =
-              pGifModule->LoadFrameInfo(m_pGifContext.get());
-        }
-        if (readResult == CFX_GifDecodeStatus::Success) {
-          m_status = FXCODEC_STATUS_DECODE_READY;
-          return {m_status, m_FrameNumber};
-        }
-        m_pGifContext = nullptr;
-        m_status = FXCODEC_STATUS_ERROR;
-        return {m_status, 0};
-      }
-    }
-#endif  // PDF_ENABLE_XFA_GIF
-    default:
-      return {FXCODEC_STATUS_ERROR, 0};
-  }
-}
-
-FXCODEC_STATUS ProgressiveDecoder::StartDecode(
-    const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-    int start_x,
-    int start_y,
-    int size_x,
-    int size_y) {
-  if (m_status != FXCODEC_STATUS_DECODE_READY)
-    return FXCODEC_STATUS_ERROR;
-
-  if (!pDIBitmap || pDIBitmap->GetBPP() < 8 || m_FrameNumber == 0)
-    return FXCODEC_STATUS_ERR_PARAMS;
-
-  m_pDeviceBitmap = pDIBitmap;
-  if (m_clipBox.IsEmpty())
-    return FXCODEC_STATUS_ERR_PARAMS;
-  if (size_x <= 0 || size_x > 65535 || size_y <= 0 || size_y > 65535)
-    return FXCODEC_STATUS_ERR_PARAMS;
-
-  FX_RECT device_rc =
-      FX_RECT(start_x, start_y, start_x + size_x, start_y + size_y);
-  int32_t out_range_x = device_rc.right - pDIBitmap->GetWidth();
-  int32_t out_range_y = device_rc.bottom - pDIBitmap->GetHeight();
-  device_rc.Intersect(
-      FX_RECT(0, 0, pDIBitmap->GetWidth(), pDIBitmap->GetHeight()));
-  if (device_rc.IsEmpty())
-    return FXCODEC_STATUS_ERR_PARAMS;
-
-  m_startX = device_rc.left;
-  m_startY = device_rc.top;
-  m_sizeX = device_rc.Width();
-  m_sizeY = device_rc.Height();
-  m_FrameCur = 0;
-  if (start_x < 0 || out_range_x > 0) {
-    float scaleX = (float)m_clipBox.Width() / (float)size_x;
-    if (start_x < 0) {
-      m_clipBox.left -= (int32_t)ceil((float)start_x * scaleX);
-    }
-    if (out_range_x > 0) {
-      m_clipBox.right -= (int32_t)floor((float)out_range_x * scaleX);
-    }
-  }
-  if (start_y < 0 || out_range_y > 0) {
-    float scaleY = (float)m_clipBox.Height() / (float)size_y;
-    if (start_y < 0) {
-      m_clipBox.top -= (int32_t)ceil((float)start_y * scaleY);
-    }
-    if (out_range_y > 0) {
-      m_clipBox.bottom -= (int32_t)floor((float)out_range_y * scaleY);
-    }
-  }
-  if (m_clipBox.IsEmpty()) {
-    return FXCODEC_STATUS_ERR_PARAMS;
-  }
-  switch (m_imageType) {
-#ifdef PDF_ENABLE_XFA_BMP
-    case FXCODEC_IMAGE_BMP:
-      return BmpStartDecode(pDIBitmap);
-#endif  // PDF_ENABLE_XFA_BMP
-#ifdef PDF_ENABLE_XFA_GIF
-    case FXCODEC_IMAGE_GIF:
-      return GifStartDecode(pDIBitmap);
-#endif  // PDF_ENABLE_XFA_GIF
-    case FXCODEC_IMAGE_JPG:
-      return JpegStartDecode(pDIBitmap);
-#ifdef PDF_ENABLE_XFA_PNG
-    case FXCODEC_IMAGE_PNG:
-      return PngStartDecode(pDIBitmap);
-#endif  // PDF_ENABLE_XFA_PNG
-#ifdef PDF_ENABLE_XFA_TIFF
-    case FXCODEC_IMAGE_TIFF:
-      m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-      return m_status;
-#endif  // PDF_ENABLE_XFA_TIFF
-    default:
-      return FXCODEC_STATUS_ERROR;
-  }
-}
-
-FXCODEC_STATUS ProgressiveDecoder::ContinueDecode() {
-  if (m_status != FXCODEC_STATUS_DECODE_TOBECONTINUE)
-    return FXCODEC_STATUS_ERROR;
-
-  switch (m_imageType) {
-    case FXCODEC_IMAGE_JPG:
-      return JpegContinueDecode();
-#ifdef PDF_ENABLE_XFA_BMP
-    case FXCODEC_IMAGE_BMP:
-      return BmpContinueDecode();
-#endif  // PDF_ENABLE_XFA_BMP
-#ifdef PDF_ENABLE_XFA_GIF
-    case FXCODEC_IMAGE_GIF:
-      return GifContinueDecode();
-#endif  // PDF_ENABLE_XFA_GIF
-#ifdef PDF_ENABLE_XFA_PNG
-    case FXCODEC_IMAGE_PNG:
-      return PngContinueDecode();
-#endif  // PDF_ENABLE_XFA_PNG
-#ifdef PDF_ENABLE_XFA_TIFF
-    case FXCODEC_IMAGE_TIFF:
-      return TiffContinueDecode();
-#endif  // PDF_ENABLE_XFA_TIFF
-    default:
-      return FXCODEC_STATUS_ERROR;
-  }
-}
-
-std::unique_ptr<ProgressiveDecoder> ModuleMgr::CreateProgressiveDecoder() {
-  return pdfium::MakeUnique<ProgressiveDecoder>(this);
-}
-
-}  // namespace fxcodec
diff --git a/core/fxcodec/progressivedecoder.h b/core/fxcodec/progressivedecoder.h
deleted file mode 100644
index 1852fe9..0000000
--- a/core/fxcodec/progressivedecoder.h
+++ /dev/null
@@ -1,305 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_PROGRESSIVEDECODER_H_
-#define CORE_FXCODEC_PROGRESSIVEDECODER_H_
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "core/fxcodec/fx_codec_def.h"
-#include "core/fxcodec/jpeg/jpegmodule.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_dib.h"
-
-#ifdef PDF_ENABLE_XFA_BMP
-#include "core/fxcodec/bmp/bmpmodule.h"
-#endif  // PDF_ENABLE_XFA_BMP
-
-#ifdef PDF_ENABLE_XFA_GIF
-#include "core/fxcodec/gif/gifmodule.h"
-#endif  // PDF_ENABLE_XFA_GIF
-
-#ifdef PDF_ENABLE_XFA_PNG
-#include "core/fxcodec/png/pngmodule.h"
-#endif  // PDF_ENABLE_XFA_PNG
-
-#ifdef PDF_ENABLE_XFA_TIFF
-#include "core/fxcodec/tiff/tiffmodule.h"
-#endif  // PDF_ENABLE_XFA_TIFF
-
-class CFX_DIBitmap;
-class IFX_SeekableReadStream;
-
-namespace fxcodec {
-
-class CFX_DIBAttribute;
-class ModuleMgr;
-
-class Dummy {};  // Placeholder to work around C++ syntax issues
-
-class ProgressiveDecoder :
-#ifdef PDF_ENABLE_XFA_BMP
-    public BmpModule::Delegate,
-#endif  // PDF_ENABLE_XFA_BMP
-#ifdef PDF_ENABLE_XFA_GIF
-    public GifModule::Delegate,
-#endif  // PDF_ENABLE_XFA_GIF
-#ifdef PDF_ENABLE_XFA_PNG
-    public PngModule::Delegate,
-#endif  // PDF_ENABLE_XFA_PNG
-    public Dummy {
- public:
-  enum FXCodec_Format {
-    FXCodec_Invalid = 0,
-    FXCodec_1bppGray = 0x101,
-    FXCodec_1bppRgb = 0x001,
-    FXCodec_8bppGray = 0x108,
-    FXCodec_8bppRgb = 0x008,
-    FXCodec_Rgb = 0x018,
-    FXCodec_Rgb32 = 0x020,
-    FXCodec_Argb = 0x220,
-    FXCodec_Cmyk = 0x120
-  };
-
-  explicit ProgressiveDecoder(ModuleMgr* pCodecMgr);
-  virtual ~ProgressiveDecoder();
-
-  FXCODEC_STATUS LoadImageInfo(const RetainPtr<IFX_SeekableReadStream>& pFile,
-                               FXCODEC_IMAGE_TYPE imageType,
-                               CFX_DIBAttribute* pAttribute,
-                               bool bSkipImageTypeCheck);
-
-  FXCODEC_IMAGE_TYPE GetType() const { return m_imageType; }
-  int32_t GetWidth() const { return m_SrcWidth; }
-  int32_t GetHeight() const { return m_SrcHeight; }
-  int32_t GetNumComponents() const { return m_SrcComponents; }
-  int32_t GetBPC() const { return m_SrcBPC; }
-  void SetClipBox(FX_RECT* clip);
-
-  std::pair<FXCODEC_STATUS, size_t> GetFrames();
-  FXCODEC_STATUS StartDecode(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                             int start_x,
-                             int start_y,
-                             int size_x,
-                             int size_y);
-
-  FXCODEC_STATUS ContinueDecode();
-
-  struct PixelWeight {
-    int m_SrcStart;
-    int m_SrcEnd;
-    int m_Weights[1];
-  };
-
-  class CFXCODEC_WeightTable {
-   public:
-    CFXCODEC_WeightTable();
-    ~CFXCODEC_WeightTable();
-
-    void Calc(int dest_len, int src_len);
-    PixelWeight* GetPixelWeight(int pixel) {
-      return reinterpret_cast<PixelWeight*>(m_pWeightTables.data() +
-                                            (pixel - m_DestMin) * m_ItemSize);
-    }
-
-    int m_DestMin;
-    int m_ItemSize;
-    std::vector<uint8_t> m_pWeightTables;
-  };
-
-  class CFXCODEC_HorzTable {
-   public:
-    CFXCODEC_HorzTable();
-    ~CFXCODEC_HorzTable();
-
-    void Calc(int dest_len, int src_len);
-    PixelWeight* GetPixelWeight(int pixel) {
-      return reinterpret_cast<PixelWeight*>(m_pWeightTables.data() +
-                                            pixel * m_ItemSize);
-    }
-
-    int m_ItemSize;
-    std::vector<uint8_t> m_pWeightTables;
-  };
-
-  class CFXCODEC_VertTable {
-   public:
-    CFXCODEC_VertTable();
-    ~CFXCODEC_VertTable();
-
-    void Calc(int dest_len, int src_len);
-    PixelWeight* GetPixelWeight(int pixel) {
-      return reinterpret_cast<PixelWeight*>(m_pWeightTables.data() +
-                                            pixel * m_ItemSize);
-    }
-    int m_ItemSize;
-    std::vector<uint8_t> m_pWeightTables;
-  };
-
-#ifdef PDF_ENABLE_XFA_PNG
-  // PngModule::Delegate
-  bool PngReadHeader(int width,
-                     int height,
-                     int bpc,
-                     int pass,
-                     int* color_type,
-                     double* gamma) override;
-  bool PngAskScanlineBuf(int line, uint8_t** pSrcBuf) override;
-  void PngFillScanlineBufCompleted(int pass, int line) override;
-#endif  // PDF_ENABLE_XFA_PNG
-
-#ifdef PDF_ENABLE_XFA_GIF
-  // GifModule::Delegate
-  void GifRecordCurrentPosition(uint32_t& cur_pos) override;
-  bool GifInputRecordPositionBuf(uint32_t rcd_pos,
-                                 const FX_RECT& img_rc,
-                                 int32_t pal_num,
-                                 CFX_GifPalette* pal_ptr,
-                                 int32_t delay_time,
-                                 bool user_input,
-                                 int32_t trans_index,
-                                 int32_t disposal_method,
-                                 bool interlace) override;
-  void GifReadScanline(int32_t row_num, uint8_t* row_buf) override;
-#endif  // PDF_ENABLE_XFA_GIF
-
-#ifdef PDF_ENABLE_XFA_BMP
-  // BmpModule::Delegate
-  bool BmpInputImagePositionBuf(uint32_t rcd_pos) override;
-  void BmpReadScanline(uint32_t row_num,
-                       pdfium::span<const uint8_t> row_buf) override;
-#endif  // PDF_ENABLE_XFA_BMP
-
- private:
-#ifdef PDF_ENABLE_XFA_BMP
-  bool BmpReadMoreData(BmpModule* pBmpModule,
-                       ModuleIface::Context* pBmpContext,
-                       FXCODEC_STATUS& err_status);
-  bool BmpDetectImageTypeInBuffer(CFX_DIBAttribute* pAttribute);
-  FXCODEC_STATUS BmpStartDecode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
-  FXCODEC_STATUS BmpContinueDecode();
-#endif  // PDF_ENABLE_XFA_BMP
-
-#ifdef PDF_ENABLE_XFA_GIF
-  bool GifReadMoreData(GifModule* pGifModule, FXCODEC_STATUS& err_status);
-  bool GifDetectImageTypeInBuffer();
-  FXCODEC_STATUS GifStartDecode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
-  FXCODEC_STATUS GifContinueDecode();
-  void GifDoubleLineResampleVert(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-                                 double scale_y,
-                                 int dest_row);
-#endif  // PDF_ENABLE_XFA_GIF
-
-#ifdef PDF_ENABLE_XFA_PNG
-  void PngOneOneMapResampleHorz(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-                                int32_t dest_line,
-                                uint8_t* src_scan,
-                                FXCodec_Format src_format);
-  bool PngDetectImageTypeInBuffer(CFX_DIBAttribute* pAttribute);
-  FXCODEC_STATUS PngStartDecode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
-  FXCODEC_STATUS PngContinueDecode();
-#endif  // PDF_ENABLE_XFA_PNG
-
-#ifdef PDF_ENABLE_XFA_TIFF
-  bool TiffDetectImageTypeFromFile(CFX_DIBAttribute* pAttribute);
-  FXCODEC_STATUS TiffContinueDecode();
-#endif  // PDF_ENABLE_XFA_TIFF
-
-  bool JpegReadMoreData(JpegModule* pJpegModule, FXCODEC_STATUS& err_status);
-  bool JpegDetectImageTypeInBuffer(CFX_DIBAttribute* pAttribute);
-  FXCODEC_STATUS JpegStartDecode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
-  FXCODEC_STATUS JpegContinueDecode();
-
-  bool DetectImageType(FXCODEC_IMAGE_TYPE imageType,
-                       CFX_DIBAttribute* pAttribute);
-  bool ReadMoreData(ModuleIface* pModule,
-                    ModuleIface::Context* pContext,
-                    bool invalidate_buffer,
-                    FXCODEC_STATUS& err_status);
-
-  void GetDownScale(int& down_scale);
-  void GetTransMethod(FXDIB_Format dest_format, FXCodec_Format src_format);
-
-  void ReSampleScanline(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-                        int32_t dest_line,
-                        uint8_t* src_scan,
-                        FXCodec_Format src_format);
-  void Resample(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-                int32_t src_line,
-                uint8_t* src_scan,
-                FXCodec_Format src_format);
-  void ResampleVert(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-                    double scale_y,
-                    int dest_row);
-  void ResampleVertBT(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-                      double scale_y,
-                      int dest_row);
-
-  FXCODEC_STATUS m_status = FXCODEC_STATUS_DECODE_FINISH;
-  FXCODEC_IMAGE_TYPE m_imageType = FXCODEC_IMAGE_UNKNOWN;
-  RetainPtr<IFX_SeekableReadStream> m_pFile;
-  RetainPtr<CFX_DIBitmap> m_pDeviceBitmap;
-  UnownedPtr<ModuleMgr> m_pCodecMgr;
-  RetainPtr<CFX_CodecMemory> m_pCodecMemory;
-  std::unique_ptr<uint8_t, FxFreeDeleter> m_pDecodeBuf;
-  std::unique_ptr<FX_ARGB, FxFreeDeleter> m_pSrcPalette;
-  std::unique_ptr<ModuleIface::Context> m_pJpegContext;
-#ifdef PDF_ENABLE_XFA_BMP
-  std::unique_ptr<ModuleIface::Context> m_pBmpContext;
-#endif  // PDF_ENABLE_XFA_BMP
-#ifdef PDF_ENABLE_XFA_GIF
-  std::unique_ptr<ModuleIface::Context> m_pGifContext;
-#endif  // PDF_ENABLE_XFA_GIF
-#ifdef PDF_ENABLE_XFA_PNG
-  std::unique_ptr<ModuleIface::Context> m_pPngContext;
-#endif  // PDF_ENABLE_XFA_PNG
-#ifdef PDF_ENABLE_XFA_TIFF
-  std::unique_ptr<ModuleIface::Context> m_pTiffContext;
-#endif  // PDF_ENABLE_XFA_TIFF
-  uint32_t m_offSet = 0;
-  int m_ScanlineSize = 0;
-  CFXCODEC_WeightTable m_WeightHorz;
-  CFXCODEC_VertTable m_WeightVert;
-  CFXCODEC_HorzTable m_WeightHorzOO;
-  int m_SrcWidth = 0;
-  int m_SrcHeight = 0;
-  int m_SrcComponents = 0;
-  int m_SrcBPC = 0;
-  FX_RECT m_clipBox;
-  int m_startX = 0;
-  int m_startY = 0;
-  int m_sizeX = 0;
-  int m_sizeY = 0;
-  int m_TransMethod = -1;
-  int m_SrcPaletteNumber = 0;
-  int m_SrcRow = 0;
-  FXCodec_Format m_SrcFormat = FXCodec_Invalid;
-  int m_SrcPassNumber = 0;
-  size_t m_FrameNumber = 0;
-  size_t m_FrameCur = 0;
-#ifdef PDF_ENABLE_XFA_GIF
-  int m_GifBgIndex = 0;
-  CFX_GifPalette* m_pGifPalette = nullptr;
-  int32_t m_GifPltNumber = 0;
-  int m_GifTransIndex = -1;
-  FX_RECT m_GifFrameRect;
-  bool m_InvalidateGifBuffer = true;
-#endif  // PDF_ENABLE_XFA_GIF
-#ifdef PDF_ENABLE_XFA_BMP
-  bool m_BmpIsTopBottom = false;
-#endif  // PDF_ENABLE_XFA_BMP
-};
-
-}  // namespace fxcodec
-
-using ProgressiveDecoder = fxcodec::ProgressiveDecoder;
-
-#endif  // CORE_FXCODEC_PROGRESSIVEDECODER_H_
diff --git a/core/fxcodec/progressivedecoder_unittest.cpp b/core/fxcodec/progressivedecoder_unittest.cpp
deleted file mode 100644
index 5c4e26f..0000000
--- a/core/fxcodec/progressivedecoder_unittest.cpp
+++ /dev/null
@@ -1,406 +0,0 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "core/fxcodec/progressivedecoder.h"
-
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxcrt/cfx_readonlymemorystream.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/span.h"
-
-#ifdef PDF_ENABLE_XFA_GIF
-#include "core/fxcodec/gif/gifmodule.h"
-#endif  // PDF_ENABLE_XFA_GIF
-
-namespace fxcodec {
-
-#ifdef PDF_ENABLE_XFA_GIF
-TEST(ProgressiveDecoder, BUG_895009) {
-  static constexpr uint8_t kInput[] = {
-      0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x62, 0x00, 0x21, 0x1b, 0x27, 0x01,
-      0x00, 0x2c, 0x3b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x71, 0x00, 0xf8, 0x20,
-      0x00, 0x71, 0x00, 0xf8, 0x0b, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
-      0x00, 0x01, 0x00, 0x01, 0xbe, 0x00, 0x01, 0xbe, 0x00, 0x21, 0xf9, 0xf9,
-      0x21, 0x01, 0x00, 0x21, 0xf9, 0x00, 0x21, 0xf9, 0x00, 0x21, 0x01, 0x20,
-      0x00, 0x21, 0xf9, 0x00, 0x21, 0x21, 0x00, 0x21, 0x00, 0x00, 0x21, 0x01,
-      0x00, 0x21, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
-      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x47,
-      0x49, 0x46, 0x38, 0x39, 0x61, 0x11, 0x00, 0x20, 0x00, 0xf1, 0x03, 0x32,
-      0x34, 0x37, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
-      0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x11, 0x00,
-      0x20, 0x00, 0xf1, 0x03, 0x32, 0x34, 0x37, 0x21, 0x01, 0x00, 0x00, 0x00,
-      0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xc3, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x5b, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xf5,
-      0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
-      0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
-      0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
-      0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
-      0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
-      0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
-      0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
-      0x03, 0x03, 0x03, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03,
-      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
-      0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x47, 0x49, 0x46, 0x37,
-      0x36, 0x61, 0x01, 0x04, 0x20, 0x00, 0xf1, 0x03, 0x31, 0x33, 0x33, 0x37,
-      0x32, 0x30, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
-      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03,
-      0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
-      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x03, 0x00, 0x01, 0x03,
-      0x01, 0x03, 0x3b, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
-      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
-      0x03, 0x3a, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
-      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xfd, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0xb4, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-
-  ModuleMgr::Create();
-  ModuleMgr::GetInstance()->SetGifModule(pdfium::MakeUnique<GifModule>());
-  {
-    std::unique_ptr<ProgressiveDecoder> decoder =
-        ModuleMgr::GetInstance()->CreateProgressiveDecoder();
-
-    auto source = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(kInput);
-    CFX_DIBAttribute attr;
-    FXCODEC_STATUS status =
-        decoder->LoadImageInfo(source, FXCODEC_IMAGE_GIF, &attr, true);
-    ASSERT_EQ(FXCODEC_STATUS_FRAME_READY, status);
-
-    ASSERT_EQ(98, decoder->GetWidth());
-    ASSERT_EQ(6945, decoder->GetHeight());
-
-    auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-    bitmap->Create(decoder->GetWidth(), decoder->GetHeight(), FXDIB_Argb);
-
-    size_t frames;
-    std::tie(status, frames) = decoder->GetFrames();
-    ASSERT_EQ(FXCODEC_STATUS_DECODE_READY, status);
-    ASSERT_EQ(1u, frames);
-
-    status = decoder->StartDecode(bitmap, 0, 0, bitmap->GetWidth(),
-                                  bitmap->GetHeight());
-    while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE)
-      status = decoder->ContinueDecode();
-    EXPECT_EQ(FXCODEC_STATUS_DECODE_FINISH, status);
-  }
-  ModuleMgr::Destroy();
-}
-#endif  // PDF_ENABLE_XFA_GIF
-
-}  // namespace fxcodec
diff --git a/core/fxcodec/scanlinedecoder.cpp b/core/fxcodec/scanlinedecoder.cpp
index 2aa4178..bce71b9 100644
--- a/core/fxcodec/scanlinedecoder.cpp
+++ b/core/fxcodec/scanlinedecoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -29,20 +29,20 @@
 
 ScanlineDecoder::~ScanlineDecoder() = default;
 
-const uint8_t* ScanlineDecoder::GetScanline(int line) {
+pdfium::span<const uint8_t> ScanlineDecoder::GetScanline(int line) {
   if (m_NextLine == line + 1)
     return m_pLastScanline;
 
   if (m_NextLine < 0 || m_NextLine > line) {
-    if (!v_Rewind())
-      return nullptr;
+    if (!Rewind())
+      return pdfium::span<const uint8_t>();
     m_NextLine = 0;
   }
   while (m_NextLine < line) {
-    ReadNextLine();
+    GetNextLine();
     m_NextLine++;
   }
-  m_pLastScanline = ReadNextLine();
+  m_pLastScanline = GetNextLine();
   m_NextLine++;
   return m_pLastScanline;
 }
@@ -52,12 +52,12 @@
     return false;
 
   if (m_NextLine < 0 || m_NextLine > line) {
-    v_Rewind();
+    Rewind();
     m_NextLine = 0;
   }
-  m_pLastScanline = nullptr;
+  m_pLastScanline = pdfium::span<uint8_t>();
   while (m_NextLine < line) {
-    m_pLastScanline = ReadNextLine();
+    m_pLastScanline = GetNextLine();
     m_NextLine++;
     if (pPause && pPause->NeedToPauseNow()) {
       return true;
@@ -66,8 +66,4 @@
   return false;
 }
 
-uint8_t* ScanlineDecoder::ReadNextLine() {
-  return v_GetNextLine();
-}
-
 }  // namespace fxcodec
diff --git a/core/fxcodec/scanlinedecoder.h b/core/fxcodec/scanlinedecoder.h
index e0014cf..f319147 100644
--- a/core/fxcodec/scanlinedecoder.h
+++ b/core/fxcodec/scanlinedecoder.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,9 @@
 #ifndef CORE_FXCODEC_SCANLINEDECODER_H_
 #define CORE_FXCODEC_SCANLINEDECODER_H_
 
-#include "core/fxcrt/fx_system.h"
+#include <stdint.h>
+
+#include "third_party/base/containers/span.h"
 
 class PauseIndicatorIface;
 
@@ -25,7 +27,7 @@
                   uint32_t nPitch);
   virtual ~ScanlineDecoder();
 
-  const uint8_t* GetScanline(int line);
+  pdfium::span<const uint8_t> GetScanline(int line);
   bool SkipToScanline(int line, PauseIndicatorIface* pPause);
 
   int GetWidth() const { return m_OutputWidth; }
@@ -36,10 +38,8 @@
   virtual uint32_t GetSrcOffset() = 0;
 
  protected:
-  virtual bool v_Rewind() = 0;
-  virtual uint8_t* v_GetNextLine() = 0;
-
-  uint8_t* ReadNextLine();
+  virtual bool Rewind() = 0;
+  virtual pdfium::span<uint8_t> GetNextLine() = 0;
 
   int m_OrigWidth;
   int m_OrigHeight;
@@ -49,7 +49,7 @@
   int m_bpc;
   uint32_t m_Pitch;
   int m_NextLine = -1;
-  uint8_t* m_pLastScanline = nullptr;
+  pdfium::span<uint8_t> m_pLastScanline;
 };
 
 }  // namespace fxcodec
diff --git a/core/fxcodec/tiff/tiff_decoder.cpp b/core/fxcodec/tiff/tiff_decoder.cpp
new file mode 100644
index 0000000..518dd0b
--- /dev/null
+++ b/core/fxcodec/tiff/tiff_decoder.cpp
@@ -0,0 +1,490 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/tiff/tiff_decoder.h"
+
+#include <limits>
+#include <memory>
+
+#include "core/fxcodec/cfx_codec_memory.h"
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcodec/fx_codec_def.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/check.h"
+#include "third_party/base/notreached.h"
+#include "third_party/base/numerics/safe_conversions.h"
+
+extern "C" {
+#include "third_party/libtiff/tiffiop.h"
+}  // extern C
+
+namespace {
+
+// For use with std::unique_ptr<TIFF>.
+struct TiffDeleter {
+  inline void operator()(TIFF* context) { TIFFClose(context); }
+};
+
+}  // namespace
+
+class CTiffContext final : public ProgressiveDecoderIface::Context {
+ public:
+  CTiffContext() = default;
+  ~CTiffContext() override = default;
+
+  bool InitDecoder(const RetainPtr<IFX_SeekableReadStream>& file_ptr);
+  bool LoadFrameInfo(int32_t frame,
+                     int32_t* width,
+                     int32_t* height,
+                     int32_t* comps,
+                     int32_t* bpc,
+                     CFX_DIBAttribute* pAttribute);
+  bool Decode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
+
+  RetainPtr<IFX_SeekableReadStream> io_in() const { return m_io_in; }
+  uint32_t offset() const { return m_offset; }
+  void set_offset(uint32_t offset) { m_offset = offset; }
+
+ private:
+  bool IsSupport(const RetainPtr<CFX_DIBitmap>& pDIBitmap) const;
+  void SetPalette(const RetainPtr<CFX_DIBitmap>& pDIBitmap, uint16_t bps);
+  bool Decode1bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                     int32_t height,
+                     int32_t width,
+                     uint16_t bps,
+                     uint16_t spp);
+  bool Decode8bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                     int32_t height,
+                     int32_t width,
+                     uint16_t bps,
+                     uint16_t spp);
+  bool Decode24bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                      int32_t height,
+                      int32_t width,
+                      uint16_t bps,
+                      uint16_t spp);
+
+  RetainPtr<IFX_SeekableReadStream> m_io_in;
+  uint32_t m_offset = 0;
+  std::unique_ptr<TIFF, TiffDeleter> m_tif_ctx;
+};
+
+void* _TIFFcalloc(tmsize_t nmemb, tmsize_t siz) {
+  return FXMEM_DefaultCalloc(nmemb, siz);
+}
+
+void* _TIFFmalloc(tmsize_t size) {
+  return FXMEM_DefaultAlloc(size);
+}
+
+void _TIFFfree(void* ptr) {
+  if (ptr)
+    FXMEM_DefaultFree(ptr);
+}
+
+void* _TIFFrealloc(void* ptr, tmsize_t size) {
+  return FXMEM_DefaultRealloc(ptr, size);
+}
+
+void _TIFFmemset(void* ptr, int val, tmsize_t size) {
+  memset(ptr, val, static_cast<size_t>(size));
+}
+
+void _TIFFmemcpy(void* des, const void* src, tmsize_t size) {
+  memcpy(des, src, static_cast<size_t>(size));
+}
+
+int _TIFFmemcmp(const void* ptr1, const void* ptr2, tmsize_t size) {
+  return memcmp(ptr1, ptr2, static_cast<size_t>(size));
+}
+
+TIFFErrorHandler _TIFFwarningHandler = nullptr;
+TIFFErrorHandler _TIFFerrorHandler = nullptr;
+
+namespace {
+
+tsize_t tiff_read(thandle_t context, tdata_t buf, tsize_t length) {
+  CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
+  FX_SAFE_UINT32 increment = pTiffContext->offset();
+  increment += length;
+  if (!increment.IsValid())
+    return 0;
+
+  FX_FILESIZE offset = pTiffContext->offset();
+  if (!pTiffContext->io_in()->ReadBlockAtOffset(
+          {static_cast<uint8_t*>(buf), static_cast<size_t>(length)}, offset)) {
+    return 0;
+  }
+  pTiffContext->set_offset(increment.ValueOrDie());
+  if (offset + length > pTiffContext->io_in()->GetSize()) {
+    return pdfium::base::checked_cast<tsize_t>(
+        pTiffContext->io_in()->GetSize() - offset);
+  }
+  return length;
+}
+
+tsize_t tiff_write(thandle_t context, tdata_t buf, tsize_t length) {
+  NOTREACHED_NORETURN();
+}
+
+toff_t tiff_seek(thandle_t context, toff_t offset, int whence) {
+  CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
+  FX_SAFE_FILESIZE safe_offset = offset;
+  if (!safe_offset.IsValid())
+    return static_cast<toff_t>(-1);
+  FX_FILESIZE file_offset = safe_offset.ValueOrDie();
+
+  switch (whence) {
+    case 0: {
+      if (file_offset > pTiffContext->io_in()->GetSize())
+        return static_cast<toff_t>(-1);
+      pTiffContext->set_offset(
+          pdfium::base::checked_cast<uint32_t>(file_offset));
+      return pTiffContext->offset();
+    }
+    case 1: {
+      FX_SAFE_UINT32 new_increment = pTiffContext->offset();
+      new_increment += file_offset;
+      if (!new_increment.IsValid())
+        return static_cast<toff_t>(-1);
+      pTiffContext->set_offset(new_increment.ValueOrDie());
+      return pTiffContext->offset();
+    }
+    case 2: {
+      if (pTiffContext->io_in()->GetSize() < file_offset)
+        return static_cast<toff_t>(-1);
+      pTiffContext->set_offset(pdfium::base::checked_cast<uint32_t>(
+          pTiffContext->io_in()->GetSize() - file_offset));
+      return pTiffContext->offset();
+    }
+    default:
+      return static_cast<toff_t>(-1);
+  }
+}
+
+int tiff_close(thandle_t context) {
+  return 0;
+}
+
+toff_t tiff_get_size(thandle_t context) {
+  CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
+  return static_cast<toff_t>(pTiffContext->io_in()->GetSize());
+}
+
+int tiff_map(thandle_t context, tdata_t*, toff_t*) {
+  return 0;
+}
+
+void tiff_unmap(thandle_t context, tdata_t, toff_t) {}
+
+TIFF* tiff_open(void* context, const char* mode) {
+  TIFF* tif = TIFFClientOpen("Tiff Image", mode, (thandle_t)context, tiff_read,
+                             tiff_write, tiff_seek, tiff_close, tiff_get_size,
+                             tiff_map, tiff_unmap);
+  if (tif) {
+    tif->tif_fd = (int)(intptr_t)context;
+  }
+  return tif;
+}
+
+void TiffBGRA2RGBA(uint8_t* pBuf, int32_t pixel, int32_t spp) {
+  for (int32_t n = 0; n < pixel; n++) {
+    uint8_t tmp = pBuf[0];
+    pBuf[0] = pBuf[2];
+    pBuf[2] = tmp;
+    pBuf += spp;
+  }
+}
+
+}  // namespace
+
+bool CTiffContext::InitDecoder(
+    const RetainPtr<IFX_SeekableReadStream>& file_ptr) {
+  m_io_in = file_ptr;
+  m_tif_ctx.reset(tiff_open(this, "r"));
+  return !!m_tif_ctx;
+}
+
+bool CTiffContext::LoadFrameInfo(int32_t frame,
+                                 int32_t* width,
+                                 int32_t* height,
+                                 int32_t* comps,
+                                 int32_t* bpc,
+                                 CFX_DIBAttribute* pAttribute) {
+  if (!TIFFSetDirectory(m_tif_ctx.get(), (uint16_t)frame))
+    return false;
+
+  uint32_t tif_width = 0;
+  uint32_t tif_height = 0;
+  uint16_t tif_comps = 0;
+  uint16_t tif_bpc = 0;
+  uint32_t tif_rps = 0;
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGEWIDTH, &tif_width);
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGELENGTH, &tif_height);
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_SAMPLESPERPIXEL, &tif_comps);
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_BITSPERSAMPLE, &tif_bpc);
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_ROWSPERSTRIP, &tif_rps);
+
+  uint16_t tif_resunit = 0;
+  if (TIFFGetField(m_tif_ctx.get(), TIFFTAG_RESOLUTIONUNIT, &tif_resunit)) {
+    pAttribute->m_wDPIUnit =
+        static_cast<CFX_DIBAttribute::ResUnit>(tif_resunit - 1);
+  } else {
+    pAttribute->m_wDPIUnit = CFX_DIBAttribute::kResUnitInch;
+  }
+
+  float tif_xdpi = 0.0f;
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_XRESOLUTION, &tif_xdpi);
+  if (tif_xdpi)
+    pAttribute->m_nXDPI = static_cast<int32_t>(tif_xdpi + 0.5f);
+
+  float tif_ydpi = 0.0f;
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_YRESOLUTION, &tif_ydpi);
+  if (tif_ydpi)
+    pAttribute->m_nYDPI = static_cast<int32_t>(tif_ydpi + 0.5f);
+
+  FX_SAFE_INT32 checked_width = tif_width;
+  FX_SAFE_INT32 checked_height = tif_height;
+  if (!checked_width.IsValid() || !checked_height.IsValid())
+    return false;
+
+  *width = checked_width.ValueOrDie();
+  *height = checked_height.ValueOrDie();
+  *comps = tif_comps;
+  *bpc = tif_bpc;
+  if (tif_rps > tif_height) {
+    tif_rps = tif_height;
+    TIFFSetField(m_tif_ctx.get(), TIFFTAG_ROWSPERSTRIP, tif_rps);
+  }
+  return true;
+}
+
+bool CTiffContext::IsSupport(const RetainPtr<CFX_DIBitmap>& pDIBitmap) const {
+  if (TIFFIsTiled(m_tif_ctx.get()))
+    return false;
+
+  uint16_t photometric = 0;
+  if (!TIFFGetField(m_tif_ctx.get(), TIFFTAG_PHOTOMETRIC, &photometric))
+    return false;
+
+  switch (pDIBitmap->GetBPP()) {
+    case 1:
+    case 8:
+      if (photometric != PHOTOMETRIC_PALETTE) {
+        return false;
+      }
+      break;
+    case 24:
+      if (photometric != PHOTOMETRIC_RGB) {
+        return false;
+      }
+      break;
+    default:
+      return false;
+  }
+  uint16_t planarconfig = 0;
+  if (!TIFFGetFieldDefaulted(m_tif_ctx.get(), TIFFTAG_PLANARCONFIG,
+                             &planarconfig))
+    return false;
+
+  return planarconfig != PLANARCONFIG_SEPARATE;
+}
+
+void CTiffContext::SetPalette(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                              uint16_t bps) {
+  uint16_t* red_orig = nullptr;
+  uint16_t* green_orig = nullptr;
+  uint16_t* blue_orig = nullptr;
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_COLORMAP, &red_orig, &green_orig,
+               &blue_orig);
+  for (int32_t i = pdfium::base::checked_cast<int32_t>((1L << bps) - 1); i >= 0;
+       i--) {
+#define CVT(x) ((uint16_t)((x) >> 8))
+    red_orig[i] = CVT(red_orig[i]);
+    green_orig[i] = CVT(green_orig[i]);
+    blue_orig[i] = CVT(blue_orig[i]);
+#undef CVT
+  }
+  int32_t len = 1 << bps;
+  for (int32_t index = 0; index < len; index++) {
+    uint32_t r = red_orig[index] & 0xFF;
+    uint32_t g = green_orig[index] & 0xFF;
+    uint32_t b = blue_orig[index] & 0xFF;
+    uint32_t color = (uint32_t)b | ((uint32_t)g << 8) | ((uint32_t)r << 16) |
+                     (((uint32_t)0xffL) << 24);
+    pDIBitmap->SetPaletteArgb(index, color);
+  }
+}
+
+bool CTiffContext::Decode1bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                                 int32_t height,
+                                 int32_t width,
+                                 uint16_t bps,
+                                 uint16_t spp) {
+  if (pDIBitmap->GetBPP() != 1 || spp != 1 || bps != 1 ||
+      !IsSupport(pDIBitmap)) {
+    return false;
+  }
+  SetPalette(pDIBitmap, bps);
+  int32_t size = static_cast<int32_t>(TIFFScanlineSize(m_tif_ctx.get()));
+  uint8_t* buf = (uint8_t*)_TIFFmalloc(size);
+  if (!buf) {
+    TIFFError(TIFFFileName(m_tif_ctx.get()), "No space for scanline buffer");
+    return false;
+  }
+  for (int32_t row = 0; row < height; row++) {
+    uint8_t* bitMapbuffer = pDIBitmap->GetWritableScanline(row).data();
+    TIFFReadScanline(m_tif_ctx.get(), buf, row, 0);
+    for (int32_t j = 0; j < size; j++) {
+      bitMapbuffer[j] = buf[j];
+    }
+  }
+  _TIFFfree(buf);
+  return true;
+}
+
+bool CTiffContext::Decode8bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                                 int32_t height,
+                                 int32_t width,
+                                 uint16_t bps,
+                                 uint16_t spp) {
+  if (pDIBitmap->GetBPP() != 8 || spp != 1 || (bps != 4 && bps != 8) ||
+      !IsSupport(pDIBitmap)) {
+    return false;
+  }
+  SetPalette(pDIBitmap, bps);
+  int32_t size = static_cast<int32_t>(TIFFScanlineSize(m_tif_ctx.get()));
+  uint8_t* buf = (uint8_t*)_TIFFmalloc(size);
+  if (!buf) {
+    TIFFError(TIFFFileName(m_tif_ctx.get()), "No space for scanline buffer");
+    return false;
+  }
+  for (int32_t row = 0; row < height; row++) {
+    uint8_t* bitMapbuffer = pDIBitmap->GetWritableScanline(row).data();
+    TIFFReadScanline(m_tif_ctx.get(), buf, row, 0);
+    for (int32_t j = 0; j < size; j++) {
+      switch (bps) {
+        case 4:
+          bitMapbuffer[2 * j + 0] = (buf[j] & 0xF0) >> 4;
+          bitMapbuffer[2 * j + 1] = (buf[j] & 0x0F) >> 0;
+          break;
+        case 8:
+          bitMapbuffer[j] = buf[j];
+          break;
+      }
+    }
+  }
+  _TIFFfree(buf);
+  return true;
+}
+
+bool CTiffContext::Decode24bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                                  int32_t height,
+                                  int32_t width,
+                                  uint16_t bps,
+                                  uint16_t spp) {
+  if (pDIBitmap->GetBPP() != 24 || !IsSupport(pDIBitmap))
+    return false;
+
+  int32_t size = static_cast<int32_t>(TIFFScanlineSize(m_tif_ctx.get()));
+  uint8_t* buf = (uint8_t*)_TIFFmalloc(size);
+  if (!buf) {
+    TIFFError(TIFFFileName(m_tif_ctx.get()), "No space for scanline buffer");
+    return false;
+  }
+  for (int32_t row = 0; row < height; row++) {
+    uint8_t* bitMapbuffer = pDIBitmap->GetWritableScanline(row).data();
+    TIFFReadScanline(m_tif_ctx.get(), buf, row, 0);
+    for (int32_t j = 0; j < size - 2; j += 3) {
+      bitMapbuffer[j + 0] = buf[j + 2];
+      bitMapbuffer[j + 1] = buf[j + 1];
+      bitMapbuffer[j + 2] = buf[j + 0];
+    }
+  }
+  _TIFFfree(buf);
+  return true;
+}
+
+bool CTiffContext::Decode(const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
+  uint32_t img_width = pDIBitmap->GetWidth();
+  uint32_t img_height = pDIBitmap->GetHeight();
+  uint32_t width = 0;
+  uint32_t height = 0;
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGEWIDTH, &width);
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGELENGTH, &height);
+  if (img_width != width || img_height != height)
+    return false;
+
+  if (pDIBitmap->GetBPP() == 32) {
+    uint16_t rotation = ORIENTATION_TOPLEFT;
+    TIFFGetField(m_tif_ctx.get(), TIFFTAG_ORIENTATION, &rotation);
+    if (TIFFReadRGBAImageOriented(m_tif_ctx.get(), img_width, img_height,
+                                  (uint32_t*)pDIBitmap->GetBuffer().data(),
+                                  rotation, 1)) {
+      for (uint32_t row = 0; row < img_height; row++) {
+        uint8_t* row_buf = pDIBitmap->GetWritableScanline(row).data();
+        TiffBGRA2RGBA(row_buf, img_width, 4);
+      }
+      return true;
+    }
+  }
+  uint16_t spp = 0;
+  uint16_t bps = 0;
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_SAMPLESPERPIXEL, &spp);
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_BITSPERSAMPLE, &bps);
+  FX_SAFE_UINT32 safe_bpp = bps;
+  safe_bpp *= spp;
+  if (!safe_bpp.IsValid())
+    return false;
+  uint32_t bpp = safe_bpp.ValueOrDie();
+  if (bpp == 1)
+    return Decode1bppRGB(pDIBitmap, height, width, bps, spp);
+  if (bpp <= 8)
+    return Decode8bppRGB(pDIBitmap, height, width, bps, spp);
+  if (bpp <= 24)
+    return Decode24bppRGB(pDIBitmap, height, width, bps, spp);
+  return false;
+}
+
+namespace fxcodec {
+
+// static
+std::unique_ptr<ProgressiveDecoderIface::Context> TiffDecoder::CreateDecoder(
+    const RetainPtr<IFX_SeekableReadStream>& file_ptr) {
+  auto pDecoder = std::make_unique<CTiffContext>();
+  if (!pDecoder->InitDecoder(file_ptr))
+    return nullptr;
+
+  return pDecoder;
+}
+
+// static
+bool TiffDecoder::LoadFrameInfo(ProgressiveDecoderIface::Context* pContext,
+                                int32_t frame,
+                                int32_t* width,
+                                int32_t* height,
+                                int32_t* comps,
+                                int32_t* bpc,
+                                CFX_DIBAttribute* pAttribute) {
+  DCHECK(pAttribute);
+
+  auto* ctx = static_cast<CTiffContext*>(pContext);
+  return ctx->LoadFrameInfo(frame, width, height, comps, bpc, pAttribute);
+}
+
+// static
+bool TiffDecoder::Decode(ProgressiveDecoderIface::Context* pContext,
+                         const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
+  auto* ctx = static_cast<CTiffContext*>(pContext);
+  return ctx->Decode(pDIBitmap);
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/tiff/tiff_decoder.h b/core/fxcodec/tiff/tiff_decoder.h
new file mode 100644
index 0000000..8804095
--- /dev/null
+++ b/core/fxcodec/tiff/tiff_decoder.h
@@ -0,0 +1,50 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_TIFF_TIFF_DECODER_H_
+#define CORE_FXCODEC_TIFF_TIFF_DECODER_H_
+
+#include <memory>
+
+#include "core/fxcodec/progressive_decoder_iface.h"
+#include "core/fxcrt/retain_ptr.h"
+
+#ifndef PDF_ENABLE_XFA_TIFF
+#error "TIFF must be enabled"
+#endif
+
+class CFX_DIBitmap;
+class IFX_SeekableReadStream;
+
+namespace fxcodec {
+
+class CFX_DIBAttribute;
+
+class TiffDecoder {
+ public:
+  static std::unique_ptr<ProgressiveDecoderIface::Context> CreateDecoder(
+      const RetainPtr<IFX_SeekableReadStream>& file_ptr);
+
+  static bool LoadFrameInfo(ProgressiveDecoderIface::Context* ctx,
+                            int32_t frame,
+                            int32_t* width,
+                            int32_t* height,
+                            int32_t* comps,
+                            int32_t* bpc,
+                            CFX_DIBAttribute* pAttribute);
+  static bool Decode(ProgressiveDecoderIface::Context* ctx,
+                     const RetainPtr<CFX_DIBitmap>& pDIBitmap);
+
+  TiffDecoder() = delete;
+  TiffDecoder(const TiffDecoder&) = delete;
+  TiffDecoder& operator=(const TiffDecoder&) = delete;
+};
+
+}  // namespace fxcodec
+
+using TiffDecoder = fxcodec::TiffDecoder;
+
+#endif  // CORE_FXCODEC_TIFF_TIFF_DECODER_H_
diff --git a/core/fxcodec/tiff/tiffmodule.cpp b/core/fxcodec/tiff/tiffmodule.cpp
deleted file mode 100644
index 179fca6..0000000
--- a/core/fxcodec/tiff/tiffmodule.cpp
+++ /dev/null
@@ -1,526 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/tiff/tiffmodule.h"
-
-#include <limits>
-#include <memory>
-
-#include "core/fxcodec/cfx_codec_memory.h"
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxcrt/fx_stream.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-
-extern "C" {
-#include "third_party/libtiff/tiffiop.h"
-}  // extern C
-
-namespace {
-
-// For use with std::unique_ptr<TIFF>.
-struct TiffDeleter {
-  inline void operator()(TIFF* context) { TIFFClose(context); }
-};
-
-}  // namespace
-
-class CTiffContext final : public ModuleIface::Context {
- public:
-  CTiffContext() = default;
-  ~CTiffContext() override = default;
-
-  bool InitDecoder(const RetainPtr<IFX_SeekableReadStream>& file_ptr);
-  bool LoadFrameInfo(int32_t frame,
-                     int32_t* width,
-                     int32_t* height,
-                     int32_t* comps,
-                     int32_t* bpc,
-                     CFX_DIBAttribute* pAttribute);
-  bool Decode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
-
-  RetainPtr<IFX_SeekableReadStream> io_in() const { return m_io_in; }
-  uint32_t offset() const { return m_offset; }
-  void set_offset(uint32_t offset) { m_offset = offset; }
-
- private:
-  bool IsSupport(const RetainPtr<CFX_DIBitmap>& pDIBitmap) const;
-  void SetPalette(const RetainPtr<CFX_DIBitmap>& pDIBitmap, uint16_t bps);
-  bool Decode1bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                     int32_t height,
-                     int32_t width,
-                     uint16_t bps,
-                     uint16_t spp);
-  bool Decode8bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                     int32_t height,
-                     int32_t width,
-                     uint16_t bps,
-                     uint16_t spp);
-  bool Decode24bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                      int32_t height,
-                      int32_t width,
-                      uint16_t bps,
-                      uint16_t spp);
-
-  RetainPtr<IFX_SeekableReadStream> m_io_in;
-  uint32_t m_offset = 0;
-  std::unique_ptr<TIFF, TiffDeleter> m_tif_ctx;
-};
-
-void* _TIFFcalloc(tmsize_t nmemb, tmsize_t siz) {
-  return FXMEM_DefaultCalloc(nmemb, siz);
-}
-
-void* _TIFFmalloc(tmsize_t size) {
-  return FXMEM_DefaultAlloc(size);
-}
-
-void _TIFFfree(void* ptr) {
-  if (ptr)
-    FXMEM_DefaultFree(ptr);
-}
-
-void* _TIFFrealloc(void* ptr, tmsize_t size) {
-  return FXMEM_DefaultRealloc(ptr, size);
-}
-
-void _TIFFmemset(void* ptr, int val, tmsize_t size) {
-  memset(ptr, val, static_cast<size_t>(size));
-}
-
-void _TIFFmemcpy(void* des, const void* src, tmsize_t size) {
-  memcpy(des, src, static_cast<size_t>(size));
-}
-
-int _TIFFmemcmp(const void* ptr1, const void* ptr2, tmsize_t size) {
-  return memcmp(ptr1, ptr2, static_cast<size_t>(size));
-}
-
-TIFFErrorHandler _TIFFwarningHandler = nullptr;
-TIFFErrorHandler _TIFFerrorHandler = nullptr;
-
-namespace {
-
-tsize_t tiff_read(thandle_t context, tdata_t buf, tsize_t length) {
-  CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
-  FX_SAFE_UINT32 increment = pTiffContext->offset();
-  increment += length;
-  if (!increment.IsValid())
-    return 0;
-
-  FX_FILESIZE offset = pTiffContext->offset();
-  if (!pTiffContext->io_in()->ReadBlockAtOffset(buf, offset, length))
-    return 0;
-
-  pTiffContext->set_offset(increment.ValueOrDie());
-  if (offset + length > pTiffContext->io_in()->GetSize())
-    return pTiffContext->io_in()->GetSize() - offset;
-
-  return length;
-}
-
-tsize_t tiff_write(thandle_t context, tdata_t buf, tsize_t length) {
-  NOTREACHED();
-  return 0;
-}
-
-toff_t tiff_seek(thandle_t context, toff_t offset, int whence) {
-  CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
-  FX_SAFE_FILESIZE safe_offset = offset;
-  if (!safe_offset.IsValid())
-    return static_cast<toff_t>(-1);
-  FX_FILESIZE file_offset = safe_offset.ValueOrDie();
-
-  switch (whence) {
-    case 0: {
-      if (file_offset > pTiffContext->io_in()->GetSize())
-        return static_cast<toff_t>(-1);
-      pTiffContext->set_offset(file_offset);
-      return pTiffContext->offset();
-    }
-    case 1: {
-      FX_SAFE_UINT32 new_increment = pTiffContext->offset();
-      new_increment += file_offset;
-      if (!new_increment.IsValid())
-        return static_cast<toff_t>(-1);
-      pTiffContext->set_offset(new_increment.ValueOrDie());
-      return pTiffContext->offset();
-    }
-    case 2: {
-      if (pTiffContext->io_in()->GetSize() < file_offset)
-        return static_cast<toff_t>(-1);
-      pTiffContext->set_offset(pTiffContext->io_in()->GetSize() - file_offset);
-      return pTiffContext->offset();
-    }
-    default:
-      return static_cast<toff_t>(-1);
-  }
-}
-
-int tiff_close(thandle_t context) {
-  return 0;
-}
-
-toff_t tiff_get_size(thandle_t context) {
-  CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
-  return static_cast<toff_t>(pTiffContext->io_in()->GetSize());
-}
-
-int tiff_map(thandle_t context, tdata_t*, toff_t*) {
-  return 0;
-}
-
-void tiff_unmap(thandle_t context, tdata_t, toff_t) {}
-
-TIFF* tiff_open(void* context, const char* mode) {
-  TIFF* tif = TIFFClientOpen("Tiff Image", mode, (thandle_t)context, tiff_read,
-                             tiff_write, tiff_seek, tiff_close, tiff_get_size,
-                             tiff_map, tiff_unmap);
-  if (tif) {
-    tif->tif_fd = (int)(intptr_t)context;
-  }
-  return tif;
-}
-
-template <class T>
-bool Tiff_Exif_GetInfo(TIFF* tif_ctx, ttag_t tag, CFX_DIBAttribute* pAttr) {
-  T val = 0;
-  TIFFGetField(tif_ctx, tag, &val);
-  if (!val)
-    return false;
-  T* ptr = FX_Alloc(T, 1);
-  *ptr = val;
-  pAttr->m_Exif[tag] = ptr;
-  return true;
-}
-
-void Tiff_Exif_GetStringInfo(TIFF* tif_ctx,
-                             ttag_t tag,
-                             CFX_DIBAttribute* pAttr) {
-  char* buf = nullptr;
-  TIFFGetField(tif_ctx, tag, &buf);
-  if (!buf)
-    return;
-  size_t size = strlen(buf);
-  uint8_t* ptr = FX_Alloc(uint8_t, size + 1);
-  memcpy(ptr, buf, size);
-  ptr[size] = 0;
-  pAttr->m_Exif[tag] = ptr;
-}
-
-void TiffBGRA2RGBA(uint8_t* pBuf, int32_t pixel, int32_t spp) {
-  for (int32_t n = 0; n < pixel; n++) {
-    uint8_t tmp = pBuf[0];
-    pBuf[0] = pBuf[2];
-    pBuf[2] = tmp;
-    pBuf += spp;
-  }
-}
-
-}  // namespace
-
-bool CTiffContext::InitDecoder(
-    const RetainPtr<IFX_SeekableReadStream>& file_ptr) {
-  m_io_in = file_ptr;
-  m_tif_ctx.reset(tiff_open(this, "r"));
-  return !!m_tif_ctx;
-}
-
-bool CTiffContext::LoadFrameInfo(int32_t frame,
-                                 int32_t* width,
-                                 int32_t* height,
-                                 int32_t* comps,
-                                 int32_t* bpc,
-                                 CFX_DIBAttribute* pAttribute) {
-  if (!TIFFSetDirectory(m_tif_ctx.get(), (uint16)frame))
-    return false;
-
-  uint32_t tif_width = 0;
-  uint32_t tif_height = 0;
-  uint16_t tif_comps = 0;
-  uint16_t tif_bpc = 0;
-  uint32_t tif_rps = 0;
-  TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGEWIDTH, &tif_width);
-  TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGELENGTH, &tif_height);
-  TIFFGetField(m_tif_ctx.get(), TIFFTAG_SAMPLESPERPIXEL, &tif_comps);
-  TIFFGetField(m_tif_ctx.get(), TIFFTAG_BITSPERSAMPLE, &tif_bpc);
-  TIFFGetField(m_tif_ctx.get(), TIFFTAG_ROWSPERSTRIP, &tif_rps);
-
-  pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_INCH;
-  if (TIFFGetField(m_tif_ctx.get(), TIFFTAG_RESOLUTIONUNIT,
-                   &pAttribute->m_wDPIUnit)) {
-    pAttribute->m_wDPIUnit--;
-  }
-  Tiff_Exif_GetInfo<uint16_t>(m_tif_ctx.get(), TIFFTAG_ORIENTATION, pAttribute);
-  if (Tiff_Exif_GetInfo<float>(m_tif_ctx.get(), TIFFTAG_XRESOLUTION,
-                               pAttribute)) {
-    void* val = pAttribute->m_Exif[TIFFTAG_XRESOLUTION];
-    float fDpi = val ? *reinterpret_cast<float*>(val) : 0;
-    pAttribute->m_nXDPI = (int32_t)(fDpi + 0.5f);
-  }
-  if (Tiff_Exif_GetInfo<float>(m_tif_ctx.get(), TIFFTAG_YRESOLUTION,
-                               pAttribute)) {
-    void* val = pAttribute->m_Exif[TIFFTAG_YRESOLUTION];
-    float fDpi = val ? *reinterpret_cast<float*>(val) : 0;
-    pAttribute->m_nYDPI = (int32_t)(fDpi + 0.5f);
-  }
-  Tiff_Exif_GetStringInfo(m_tif_ctx.get(), TIFFTAG_IMAGEDESCRIPTION,
-                          pAttribute);
-  Tiff_Exif_GetStringInfo(m_tif_ctx.get(), TIFFTAG_MAKE, pAttribute);
-  Tiff_Exif_GetStringInfo(m_tif_ctx.get(), TIFFTAG_MODEL, pAttribute);
-
-  FX_SAFE_INT32 checked_width = tif_width;
-  FX_SAFE_INT32 checked_height = tif_height;
-  if (!checked_width.IsValid() || !checked_height.IsValid())
-    return false;
-
-  *width = checked_width.ValueOrDie();
-  *height = checked_height.ValueOrDie();
-  *comps = tif_comps;
-  *bpc = tif_bpc;
-  if (tif_rps > tif_height) {
-    tif_rps = tif_height;
-    TIFFSetField(m_tif_ctx.get(), TIFFTAG_ROWSPERSTRIP, tif_rps);
-  }
-  return true;
-}
-
-bool CTiffContext::IsSupport(const RetainPtr<CFX_DIBitmap>& pDIBitmap) const {
-  if (TIFFIsTiled(m_tif_ctx.get()))
-    return false;
-
-  uint16_t photometric = 0;
-  if (!TIFFGetField(m_tif_ctx.get(), TIFFTAG_PHOTOMETRIC, &photometric))
-    return false;
-
-  switch (pDIBitmap->GetBPP()) {
-    case 1:
-    case 8:
-      if (photometric != PHOTOMETRIC_PALETTE) {
-        return false;
-      }
-      break;
-    case 24:
-      if (photometric != PHOTOMETRIC_RGB) {
-        return false;
-      }
-      break;
-    default:
-      return false;
-  }
-  uint16_t planarconfig = 0;
-  if (!TIFFGetFieldDefaulted(m_tif_ctx.get(), TIFFTAG_PLANARCONFIG,
-                             &planarconfig))
-    return false;
-
-  return planarconfig != PLANARCONFIG_SEPARATE;
-}
-
-void CTiffContext::SetPalette(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                              uint16_t bps) {
-  uint16_t* red_orig = nullptr;
-  uint16_t* green_orig = nullptr;
-  uint16_t* blue_orig = nullptr;
-  TIFFGetField(m_tif_ctx.get(), TIFFTAG_COLORMAP, &red_orig, &green_orig,
-               &blue_orig);
-  for (int32_t i = (1L << bps) - 1; i >= 0; i--) {
-#define CVT(x) ((uint16_t)((x) >> 8))
-    red_orig[i] = CVT(red_orig[i]);
-    green_orig[i] = CVT(green_orig[i]);
-    blue_orig[i] = CVT(blue_orig[i]);
-#undef CVT
-  }
-  int32_t len = 1 << bps;
-  for (int32_t index = 0; index < len; index++) {
-    uint32_t r = red_orig[index] & 0xFF;
-    uint32_t g = green_orig[index] & 0xFF;
-    uint32_t b = blue_orig[index] & 0xFF;
-    uint32_t color = (uint32_t)b | ((uint32_t)g << 8) | ((uint32_t)r << 16) |
-                     (((uint32)0xffL) << 24);
-    pDIBitmap->SetPaletteArgb(index, color);
-  }
-}
-
-bool CTiffContext::Decode1bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                                 int32_t height,
-                                 int32_t width,
-                                 uint16_t bps,
-                                 uint16_t spp) {
-  if (pDIBitmap->GetBPP() != 1 || spp != 1 || bps != 1 ||
-      !IsSupport(pDIBitmap)) {
-    return false;
-  }
-  SetPalette(pDIBitmap, bps);
-  int32_t size = (int32_t)TIFFScanlineSize(m_tif_ctx.get());
-  uint8_t* buf = (uint8_t*)_TIFFmalloc(size);
-  if (!buf) {
-    TIFFError(TIFFFileName(m_tif_ctx.get()), "No space for scanline buffer");
-    return false;
-  }
-  uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer();
-  uint32_t pitch = pDIBitmap->GetPitch();
-  for (int32_t row = 0; row < height; row++) {
-    TIFFReadScanline(m_tif_ctx.get(), buf, row, 0);
-    for (int32_t j = 0; j < size; j++) {
-      bitMapbuffer[row * pitch + j] = buf[j];
-    }
-  }
-  _TIFFfree(buf);
-  return true;
-}
-
-bool CTiffContext::Decode8bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                                 int32_t height,
-                                 int32_t width,
-                                 uint16_t bps,
-                                 uint16_t spp) {
-  if (pDIBitmap->GetBPP() != 8 || spp != 1 || (bps != 4 && bps != 8) ||
-      !IsSupport(pDIBitmap)) {
-    return false;
-  }
-  SetPalette(pDIBitmap, bps);
-  int32_t size = (int32_t)TIFFScanlineSize(m_tif_ctx.get());
-  uint8_t* buf = (uint8_t*)_TIFFmalloc(size);
-  if (!buf) {
-    TIFFError(TIFFFileName(m_tif_ctx.get()), "No space for scanline buffer");
-    return false;
-  }
-  uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer();
-  uint32_t pitch = pDIBitmap->GetPitch();
-  for (int32_t row = 0; row < height; row++) {
-    TIFFReadScanline(m_tif_ctx.get(), buf, row, 0);
-    for (int32_t j = 0; j < size; j++) {
-      switch (bps) {
-        case 4:
-          bitMapbuffer[row * pitch + 2 * j + 0] = (buf[j] & 0xF0) >> 4;
-          bitMapbuffer[row * pitch + 2 * j + 1] = (buf[j] & 0x0F) >> 0;
-          break;
-        case 8:
-          bitMapbuffer[row * pitch + j] = buf[j];
-          break;
-      }
-    }
-  }
-  _TIFFfree(buf);
-  return true;
-}
-
-bool CTiffContext::Decode24bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                                  int32_t height,
-                                  int32_t width,
-                                  uint16_t bps,
-                                  uint16_t spp) {
-  if (pDIBitmap->GetBPP() != 24 || !IsSupport(pDIBitmap))
-    return false;
-
-  int32_t size = (int32_t)TIFFScanlineSize(m_tif_ctx.get());
-  uint8_t* buf = (uint8_t*)_TIFFmalloc(size);
-  if (!buf) {
-    TIFFError(TIFFFileName(m_tif_ctx.get()), "No space for scanline buffer");
-    return false;
-  }
-  uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer();
-  uint32_t pitch = pDIBitmap->GetPitch();
-  for (int32_t row = 0; row < height; row++) {
-    TIFFReadScanline(m_tif_ctx.get(), buf, row, 0);
-    for (int32_t j = 0; j < size - 2; j += 3) {
-      bitMapbuffer[row * pitch + j + 0] = buf[j + 2];
-      bitMapbuffer[row * pitch + j + 1] = buf[j + 1];
-      bitMapbuffer[row * pitch + j + 2] = buf[j + 0];
-    }
-  }
-  _TIFFfree(buf);
-  return true;
-}
-
-bool CTiffContext::Decode(const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
-  uint32_t img_width = pDIBitmap->GetWidth();
-  uint32_t img_height = pDIBitmap->GetHeight();
-  uint32_t width = 0;
-  uint32_t height = 0;
-  TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGEWIDTH, &width);
-  TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGELENGTH, &height);
-  if (img_width != width || img_height != height)
-    return false;
-
-  if (pDIBitmap->GetBPP() == 32) {
-    uint16_t rotation = ORIENTATION_TOPLEFT;
-    TIFFGetField(m_tif_ctx.get(), TIFFTAG_ORIENTATION, &rotation);
-    if (TIFFReadRGBAImageOriented(m_tif_ctx.get(), img_width, img_height,
-                                  (uint32*)pDIBitmap->GetBuffer(), rotation,
-                                  1)) {
-      for (uint32_t row = 0; row < img_height; row++) {
-        uint8_t* row_buf = pDIBitmap->GetWritableScanline(row);
-        TiffBGRA2RGBA(row_buf, img_width, 4);
-      }
-      return true;
-    }
-  }
-  uint16_t spp = 0;
-  uint16_t bps = 0;
-  TIFFGetField(m_tif_ctx.get(), TIFFTAG_SAMPLESPERPIXEL, &spp);
-  TIFFGetField(m_tif_ctx.get(), TIFFTAG_BITSPERSAMPLE, &bps);
-  FX_SAFE_UINT32 safe_bpp = bps;
-  safe_bpp *= spp;
-  if (!safe_bpp.IsValid())
-    return false;
-  uint32_t bpp = safe_bpp.ValueOrDie();
-  if (bpp == 1)
-    return Decode1bppRGB(pDIBitmap, height, width, bps, spp);
-  if (bpp <= 8)
-    return Decode8bppRGB(pDIBitmap, height, width, bps, spp);
-  if (bpp <= 24)
-    return Decode24bppRGB(pDIBitmap, height, width, bps, spp);
-  return false;
-}
-
-namespace fxcodec {
-
-std::unique_ptr<ModuleIface::Context> TiffModule::CreateDecoder(
-    const RetainPtr<IFX_SeekableReadStream>& file_ptr) {
-  auto pDecoder = pdfium::MakeUnique<CTiffContext>();
-  if (!pDecoder->InitDecoder(file_ptr))
-    return nullptr;
-
-  return pDecoder;
-}
-
-FX_FILESIZE TiffModule::GetAvailInput(Context* pContext) const {
-  NOTREACHED();
-  return 0;
-}
-
-bool TiffModule::Input(Context* pContext,
-                       RetainPtr<CFX_CodecMemory> codec_memory,
-                       CFX_DIBAttribute*) {
-  NOTREACHED();
-  return false;
-}
-
-bool TiffModule::LoadFrameInfo(Context* pContext,
-                               int32_t frame,
-                               int32_t* width,
-                               int32_t* height,
-                               int32_t* comps,
-                               int32_t* bpc,
-                               CFX_DIBAttribute* pAttribute) {
-  ASSERT(pAttribute);
-
-  auto* ctx = static_cast<CTiffContext*>(pContext);
-  return ctx->LoadFrameInfo(frame, width, height, comps, bpc, pAttribute);
-}
-
-bool TiffModule::Decode(Context* pContext,
-                        const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
-  auto* ctx = static_cast<CTiffContext*>(pContext);
-  return ctx->Decode(pDIBitmap);
-}
-
-}  // namespace fxcodec
diff --git a/core/fxcodec/tiff/tiffmodule.h b/core/fxcodec/tiff/tiffmodule.h
deleted file mode 100644
index 770973b..0000000
--- a/core/fxcodec/tiff/tiffmodule.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_TIFF_TIFFMODULE_H_
-#define CORE_FXCODEC_TIFF_TIFFMODULE_H_
-
-#include <memory>
-
-#include "core/fxcodec/codec_module_iface.h"
-
-class CFX_DIBitmap;
-class IFX_SeekableReadStream;
-
-namespace fxcodec {
-
-class CFX_DIBAttribute;
-
-class TiffModule final : public ModuleIface {
- public:
-  std::unique_ptr<Context> CreateDecoder(
-      const RetainPtr<IFX_SeekableReadStream>& file_ptr);
-
-  // ModuleIface:
-  FX_FILESIZE GetAvailInput(Context* pContext) const override;
-  bool Input(Context* pContext,
-             RetainPtr<CFX_CodecMemory> codec_memory,
-             CFX_DIBAttribute* pAttribute) override;
-
-  bool LoadFrameInfo(Context* ctx,
-                     int32_t frame,
-                     int32_t* width,
-                     int32_t* height,
-                     int32_t* comps,
-                     int32_t* bpc,
-                     CFX_DIBAttribute* pAttribute);
-  bool Decode(Context* ctx, const RetainPtr<CFX_DIBitmap>& pDIBitmap);
-};
-
-}  // namespace fxcodec
-
-using TiffModule = fxcodec::TiffModule;
-
-#endif  // CORE_FXCODEC_TIFF_TIFFMODULE_H_
diff --git a/core/fxcrt/Android.bp b/core/fxcrt/Android.bp
index 5f6cb41..949a0d9 100644
--- a/core/fxcrt/Android.bp
+++ b/core/fxcrt/Android.bp
@@ -11,7 +11,7 @@
     name: "libpdfium-fxcrt",
     defaults: ["pdfium-core"],
 
-    visibility: ["//external/pdfium:__subpackages__"],
+    visibility: ["//external/pdfium:__subpackages__", "//cts/hostsidetests/securitybulletin/securityPatch/CVE-2016-8332"],
 
     export_shared_lib_headers: [
         "libicu",
@@ -28,8 +28,13 @@
     exclude_srcs: [
         // is_win
         "cfx_fileaccess_windows.cpp",
+        "fx_folder_windows.cpp",
         // pdf_enable_xfa
         "cfx_memorystream.cpp",
+        // pdf_use_partition_alloc
+        "fx_memory_pa.cpp",
+        // test module
+        "fake_time_test.cpp",
     ],
 
     srcs: [
diff --git a/core/fxcrt/BUILD.gn b/core/fxcrt/BUILD.gn
index 1512f97..c9bdfa0 100644
--- a/core/fxcrt/BUILD.gn
+++ b/core/fxcrt/BUILD.gn
@@ -1,44 +1,65 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build_overrides/build.gni")
 import("../../pdfium.gni")
 import("../../testing/test.gni")
 
+source_set("unowned_ptr") {
+  sources = [ "unowned_ptr.h" ]
+  deps = [ "../../third_party:pdfium_compiler_specific" ]
+  configs += [ "../../:pdfium_strict_config" ]
+  if (pdf_use_partition_alloc) {
+    public_deps = [
+      "//base/allocator/partition_allocator:partition_alloc_buildflags",
+      "//base/allocator/partition_allocator:raw_ptr",
+    ]
+  }
+}
+
 source_set("fxcrt") {
   sources = [
+    "autonuller.h",
     "autorestorer.h",
+    "binary_buffer.cpp",
+    "binary_buffer.h",
     "byteorder.h",
     "bytestring.cpp",
     "bytestring.h",
-    "cfx_binarybuf.cpp",
-    "cfx_binarybuf.h",
     "cfx_bitstream.cpp",
     "cfx_bitstream.h",
     "cfx_datetime.cpp",
     "cfx_datetime.h",
-    "cfx_fixedbufgrow.h",
-    "cfx_readonlymemorystream.cpp",
-    "cfx_readonlymemorystream.h",
+    "cfx_read_only_span_stream.cpp",
+    "cfx_read_only_span_stream.h",
+    "cfx_read_only_string_stream.cpp",
+    "cfx_read_only_string_stream.h",
+    "cfx_read_only_vector_stream.cpp",
+    "cfx_read_only_vector_stream.h",
     "cfx_seekablestreamproxy.cpp",
     "cfx_seekablestreamproxy.h",
     "cfx_timer.cpp",
     "cfx_timer.h",
-    "cfx_utf8decoder.cpp",
-    "cfx_utf8decoder.h",
-    "cfx_utf8encoder.cpp",
-    "cfx_utf8encoder.h",
-    "cfx_widetextbuf.cpp",
-    "cfx_widetextbuf.h",
+    "code_point_view.h",
+    "data_vector.h",
     "fileaccess_iface.h",
+    "fixed_size_data_vector.h",
+    "fixed_try_alloc_zeroed_data_vector.h",
+    "fixed_uninit_data_vector.h",
+    "fixed_zeroed_data_vector.h",
+    "fx_2d_size.h",
     "fx_bidi.cpp",
     "fx_bidi.h",
     "fx_codepage.cpp",
     "fx_codepage.h",
+    "fx_codepage_forward.h",
     "fx_coordinates.cpp",
     "fx_coordinates.h",
     "fx_extension.cpp",
     "fx_extension.h",
+    "fx_folder.h",
+    "fx_memcpy_wrappers.h",
     "fx_memory.cpp",
     "fx_memory.h",
     "fx_memory_wrappers.h",
@@ -51,26 +72,34 @@
     "fx_stream.h",
     "fx_string.cpp",
     "fx_string.h",
+    "fx_string_wrappers.h",
     "fx_system.cpp",
     "fx_system.h",
+    "fx_types.h",
     "fx_unicode.cpp",
     "fx_unicode.h",
+    "mask.h",
     "maybe_owned.h",
     "observed_ptr.cpp",
     "observed_ptr.h",
     "pauseindicator_iface.h",
     "retain_ptr.h",
-    "retained_tree_node.h",
+    "scoped_set_insertion.h",
     "shared_copy_on_write.h",
+    "small_buffer.h",
+    "span_util.h",
+    "stl_util.h",
+    "string_data_template.cpp",
     "string_data_template.h",
     "string_pool_template.h",
     "string_view_template.h",
-    "timerhandler_iface.h",
     "tree_node.h",
-    "unowned_ptr.h",
+    "utf16.h",
     "weak_ptr.h",
     "widestring.cpp",
     "widestring.h",
+    "widetext_buffer.cpp",
+    "widetext_buffer.h",
     "xml/cfx_xmlchardata.cpp",
     "xml/cfx_xmlchardata.h",
     "xml/cfx_xmldocument.cpp",
@@ -86,7 +115,10 @@
     "xml/cfx_xmltext.cpp",
     "xml/cfx_xmltext.h",
   ]
-  configs += [ "../../:pdfium_core_config" ]
+  configs += [
+    "../../:pdfium_strict_config",
+    "../../:pdfium_noshorten_config",
+  ]
   visibility = [
     "../*",
     "../../:*",
@@ -97,28 +129,39 @@
     "../../testing/fuzzers/*",
     "../../third_party:fx_agg",
     "../../third_party:fx_lcms2",
+    "../../third_party:fx_libopenjpeg",
     "../../third_party:fx_tiff",
-    "../../third_party:pdfium_base",
     "../../xfa/*",
   ]
   deps = [ "../../third_party:pdfium_base" ]
   public_deps = [
+    ":unowned_ptr",
     "../../:freetype_common",
     "../../third_party:pdfium_base",
     "//third_party/icu:icuuc",
   ]
-  allow_circular_includes_from = [ "../../third_party:pdfium_base" ]
-
+  if (pdf_use_partition_alloc) {
+    sources += [ "fx_memory_pa.cpp" ]
+    deps += [
+      "//base/allocator/partition_allocator:partition_alloc",
+      "//base/allocator/partition_allocator:partition_alloc_buildflags",
+      "//base/allocator/partition_allocator:raw_ptr",
+    ]
+  } else {
+    sources += [ "fx_memory_malloc.cpp" ]
+  }
   if (is_posix || is_fuchsia) {
     sources += [
       "cfx_fileaccess_posix.cpp",
       "cfx_fileaccess_posix.h",
+      "fx_folder_posix.cpp",
     ]
   }
   if (is_win) {
     sources += [
       "cfx_fileaccess_windows.cpp",
       "cfx_fileaccess_windows.h",
+      "fx_folder_windows.cpp",
     ]
   }
   if (pdf_enable_xfa) {
@@ -129,35 +172,70 @@
   }
 }
 
+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 = [
+    "fake_time_test.cpp",
+    "fake_time_test.h",
+  ]
+  configs += [ "../../:pdfium_strict_config" ]
+  deps = [
+    ":fxcrt",
+    ":test_support",
+    "//testing/gtest",
+  ]
+}
+
 pdfium_unittest_source_set("unittests") {
   sources = [
+    "autonuller_unittest.cpp",
     "autorestorer_unittest.cpp",
+    "binary_buffer_unittest.cpp",
     "byteorder_unittest.cpp",
     "bytestring_unittest.cpp",
     "cfx_bitstream_unittest.cpp",
+    "cfx_datetime_unittest.cpp",
     "cfx_seekablestreamproxy_unittest.cpp",
     "cfx_timer_unittest.cpp",
-    "cfx_widetextbuf_unittest.cpp",
+    "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",
     "fx_random_unittest.cpp",
+    "fx_safe_types_unittest.cpp",
     "fx_string_unittest.cpp",
+    "fx_string_wrappers_unittest.cpp",
     "fx_system_unittest.cpp",
+    "mask_unittest.cpp",
     "maybe_owned_unittest.cpp",
     "observed_ptr_unittest.cpp",
     "pdfium_span_unittest.cpp",
     "retain_ptr_unittest.cpp",
-    "retained_tree_node_unittest.cpp",
+    "scoped_set_insertion_unittest.cpp",
     "shared_copy_on_write_unittest.cpp",
+    "small_buffer_unittest.cpp",
+    "span_util_unittest.cpp",
     "string_pool_template_unittest.cpp",
     "tree_node_unittest.cpp",
     "unowned_ptr_unittest.cpp",
+    "utf16_unittest.cpp",
     "weak_ptr_unittest.cpp",
     "widestring_unittest.cpp",
+    "widetext_buffer_unittest.cpp",
     "xml/cfx_xmlchardata_unittest.cpp",
     "xml/cfx_xmldocument_unittest.cpp",
     "xml/cfx_xmlelement_unittest.cpp",
@@ -166,19 +244,13 @@
     "xml/cfx_xmlparser_unittest.cpp",
     "xml/cfx_xmltext_unittest.cpp",
   ]
-  deps = []
+  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",
-      "css/cfx_cssdeclaration_unittest.cpp",
-      "css/cfx_cssstylesheet_unittest.cpp",
-      "css/cfx_cssvaluelistparser_unittest.cpp",
-    ]
-    deps += [
-      "../fpdfapi/parser",
-      "css",
-    ]
+    sources += [ "cfx_memorystream_unittest.cpp" ]
+    deps += [ "../fpdfapi/parser" ]
   }
 }
diff --git a/core/fxcrt/DEPS b/core/fxcrt/DEPS
index 2be0352..5b61cbd 100644
--- a/core/fxcrt/DEPS
+++ b/core/fxcrt/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
+  '+base/allocator',
   '+third_party/icu',
 ]
diff --git a/core/fxcrt/autonuller.h b/core/fxcrt/autonuller.h
new file mode 100644
index 0000000..88ae0cb
--- /dev/null
+++ b/core/fxcrt/autonuller.h
@@ -0,0 +1,33 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_AUTONULLER_H_
+#define CORE_FXCRT_AUTONULLER_H_
+
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/unowned_ptr.h"
+
+namespace fxcrt {
+
+template <typename T>
+class AutoNuller {
+ public:
+  FX_STACK_ALLOCATED();
+
+  explicit AutoNuller(T* location) : m_Location(location) {}
+  ~AutoNuller() {
+    if (m_Location)
+      *m_Location = nullptr;
+  }
+  void AbandonNullification() { m_Location = nullptr; }
+
+ private:
+  UnownedPtr<T> m_Location;
+};
+
+}  // namespace fxcrt
+
+using fxcrt::AutoNuller;
+
+#endif  // CORE_FXCRT_AUTONULLER_H_
diff --git a/core/fxcrt/autonuller_unittest.cpp b/core/fxcrt/autonuller_unittest.cpp
new file mode 100644
index 0000000..929fa60
--- /dev/null
+++ b/core/fxcrt/autonuller_unittest.cpp
@@ -0,0 +1,53 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/autonuller.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(fxcrt, AutoNuller) {
+  int x = 5;
+  int* ptr;
+  {
+    AutoNuller<int*> nuller(&ptr);
+    ptr = &x;
+    EXPECT_EQ(&x, ptr);
+  }
+  EXPECT_FALSE(ptr);
+}
+
+TEST(fxcrt, AutoNullerAbandon) {
+  int x = 5;
+  int* ptr;
+  {
+    AutoNuller<int*> nuller(&ptr);
+    ptr = &x;
+    EXPECT_EQ(&x, ptr);
+    nuller.AbandonNullification();
+  }
+  EXPECT_EQ(&x, ptr);
+}
+
+TEST(fxcrt, AutoNullerUnownedPtr) {
+  int x = 5;
+  UnownedPtr<int> ptr;
+  {
+    AutoNuller<UnownedPtr<int>> nuller(&ptr);
+    ptr = &x;
+    EXPECT_EQ(&x, ptr);
+  }
+  EXPECT_FALSE(ptr);
+}
+
+TEST(fxcrt, AutoNullerUnownedPtrAbandon) {
+  int x = 5;
+  UnownedPtr<int> ptr;
+  {
+    AutoNuller<UnownedPtr<int>> nuller(&ptr);
+    ptr = &x;
+    EXPECT_EQ(&x, ptr);
+    nuller.AbandonNullification();
+  }
+  EXPECT_EQ(&x, ptr);
+}
diff --git a/core/fxcrt/autorestorer.h b/core/fxcrt/autorestorer.h
index cafa075..cecd0cd 100644
--- a/core/fxcrt/autorestorer.h
+++ b/core/fxcrt/autorestorer.h
@@ -1,15 +1,20 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef CORE_FXCRT_AUTORESTORER_H_
 #define CORE_FXCRT_AUTORESTORER_H_
 
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/unowned_ptr.h"
+
 namespace fxcrt {
 
 template <typename T>
 class AutoRestorer {
  public:
+  FX_STACK_ALLOCATED();
+
   explicit AutoRestorer(T* location)
       : m_Location(location), m_OldValue(*location) {}
   ~AutoRestorer() {
@@ -19,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/autorestorer_unittest.cpp b/core/fxcrt/autorestorer_unittest.cpp
index 6430fb6..b7ee576 100644
--- a/core/fxcrt/autorestorer_unittest.cpp
+++ b/core/fxcrt/autorestorer_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxcrt/binary_buffer.cpp b/core/fxcrt/binary_buffer.cpp
new file mode 100644
index 0000000..341d4c0
--- /dev/null
+++ b/core/fxcrt/binary_buffer.cpp
@@ -0,0 +1,122 @@
+// Copyright 2017 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/binary_buffer.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/span_util.h"
+
+namespace fxcrt {
+
+BinaryBuffer::BinaryBuffer() = default;
+
+BinaryBuffer::BinaryBuffer(BinaryBuffer&& that) noexcept
+    : m_AllocStep(that.m_AllocStep),
+      m_DataSize(that.m_DataSize),
+      m_buffer(std::move(that.m_buffer)) {
+  // Can't just default, need to leave |that| in a valid state, which means
+  // that the size members reflect the (null) moved-from buffer.
+  that.m_AllocStep = 0;
+  that.m_DataSize = 0;
+}
+
+BinaryBuffer::~BinaryBuffer() = default;
+
+BinaryBuffer& BinaryBuffer::operator=(BinaryBuffer&& that) noexcept {
+  // Can't just default, need to leave |that| in a valid state, which means
+  // that the size members reflect the (null) moved-from buffer.
+  m_AllocStep = that.m_AllocStep;
+  m_DataSize = that.m_DataSize;
+  m_buffer = std::move(that.m_buffer);
+  that.m_AllocStep = 0;
+  that.m_DataSize = 0;
+  return *this;
+}
+
+void BinaryBuffer::DeleteBuf(size_t start_index, size_t count) {
+  if (m_buffer.empty() || count > GetSize() || start_index > GetSize() - count)
+    return;
+
+  auto buffer_span = pdfium::make_span(m_buffer).first(GetSize());
+  fxcrt::spanmove(buffer_span.subspan(start_index),
+                  buffer_span.subspan(start_index + count));
+  m_DataSize -= count;
+}
+
+pdfium::span<uint8_t> BinaryBuffer::GetMutableSpan() {
+  return {m_buffer.data(), GetSize()};
+}
+
+pdfium::span<const uint8_t> BinaryBuffer::GetSpan() const {
+  return {m_buffer.data(), GetSize()};
+}
+
+size_t BinaryBuffer::GetLength() const {
+  return GetSize();
+}
+
+void BinaryBuffer::Clear() {
+  m_DataSize = 0;
+}
+
+DataVector<uint8_t> BinaryBuffer::DetachBuffer() {
+  m_buffer.resize(GetSize());
+  m_DataSize = 0;
+  return std::move(m_buffer);
+}
+
+void BinaryBuffer::EstimateSize(size_t size) {
+  if (m_buffer.size() < size)
+    ExpandBuf(size - GetSize());
+}
+
+void BinaryBuffer::ExpandBuf(size_t add_size) {
+  FX_SAFE_SIZE_T new_size = GetSize();
+  new_size += add_size;
+  if (m_buffer.size() >= new_size.ValueOrDie())
+    return;
+
+  size_t alloc_step = std::max(static_cast<size_t>(128),
+                               m_AllocStep ? m_AllocStep : m_buffer.size() / 4);
+  new_size += alloc_step - 1;  // Quantize, don't combine these lines.
+  new_size /= alloc_step;
+  new_size *= alloc_step;
+  m_buffer.resize(new_size.ValueOrDie());
+}
+
+void BinaryBuffer::AppendSpan(pdfium::span<const uint8_t> span) {
+  if (span.empty())
+    return;
+
+  ExpandBuf(span.size());
+  fxcrt::spancpy(pdfium::make_span(m_buffer).subspan(GetSize()), span);
+  m_DataSize += span.size();
+}
+
+void BinaryBuffer::AppendString(const ByteString& str) {
+  AppendSpan(str.raw_span());
+}
+
+void BinaryBuffer::AppendUint8(uint8_t value) {
+  AppendSpan({&value, 1});
+}
+
+void BinaryBuffer::AppendUint16(uint16_t value) {
+  AppendSpan({reinterpret_cast<uint8_t*>(&value), sizeof(value)});
+}
+
+void BinaryBuffer::AppendUint32(uint32_t value) {
+  AppendSpan({reinterpret_cast<uint8_t*>(&value), sizeof(value)});
+}
+
+void BinaryBuffer::AppendDouble(double value) {
+  AppendSpan({reinterpret_cast<uint8_t*>(&value), sizeof(value)});
+}
+
+}  // namespace fxcrt
diff --git a/core/fxcrt/binary_buffer.h b/core/fxcrt/binary_buffer.h
new file mode 100644
index 0000000..26f0caf
--- /dev/null
+++ b/core/fxcrt/binary_buffer.h
@@ -0,0 +1,63 @@
+// Copyright 2017 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCRT_BINARY_BUFFER_H_
+#define CORE_FXCRT_BINARY_BUFFER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/data_vector.h"
+#include "third_party/base/containers/span.h"
+
+namespace fxcrt {
+
+class BinaryBuffer {
+ public:
+  BinaryBuffer();
+  BinaryBuffer(BinaryBuffer&& that) noexcept;
+  BinaryBuffer(const BinaryBuffer& that) = delete;
+  virtual ~BinaryBuffer();
+
+  // Moved-from value will be left empty.
+  BinaryBuffer& operator=(BinaryBuffer&& that) noexcept;
+
+  BinaryBuffer& operator=(const BinaryBuffer& that) = delete;
+
+  pdfium::span<uint8_t> GetMutableSpan();
+  pdfium::span<const uint8_t> GetSpan() const;
+  bool IsEmpty() const { return GetLength() == 0; }
+  size_t GetSize() const { return m_DataSize; }  // In bytes.
+  virtual size_t GetLength() const;              // In subclass-specific units.
+
+  void Clear();
+  void SetAllocStep(size_t step) { m_AllocStep = step; }
+  void EstimateSize(size_t size);
+  void AppendSpan(pdfium::span<const uint8_t> span);
+  void AppendString(const ByteString& str);
+  void AppendUint8(uint8_t value);
+  void AppendUint16(uint16_t value);
+  void AppendUint32(uint32_t value);
+  void AppendDouble(double value);
+
+  // Releases ownership of `m_pBuffer` and returns it.
+  DataVector<uint8_t> DetachBuffer();
+
+ protected:
+  void ExpandBuf(size_t size);
+  void DeleteBuf(size_t start_index, size_t count);
+
+  size_t m_AllocStep = 0;
+  size_t m_DataSize = 0;
+  DataVector<uint8_t> m_buffer;
+};
+
+}  // namespace fxcrt
+
+using fxcrt::BinaryBuffer;
+
+#endif  // CORE_FXCRT_BINARY_BUFFER_H_
diff --git a/core/fxcrt/binary_buffer_unittest.cpp b/core/fxcrt/binary_buffer_unittest.cpp
new file mode 100644
index 0000000..30a7e24
--- /dev/null
+++ b/core/fxcrt/binary_buffer_unittest.cpp
@@ -0,0 +1,152 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/binary_buffer.h"
+
+#include <utility>
+#include <vector>
+
+#include "core/fxcrt/bytestring.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace fxcrt {
+
+TEST(BinaryBuffer, Empty) {
+  BinaryBuffer buffer;
+  EXPECT_TRUE(buffer.IsEmpty());
+  EXPECT_EQ(0u, buffer.GetSize());
+  EXPECT_EQ(0u, buffer.GetLength());
+  EXPECT_TRUE(buffer.GetSpan().empty());
+}
+
+TEST(BinaryBuffer, MoveConstruct) {
+  BinaryBuffer buffer;
+  buffer.AppendUint8(65u);
+
+  BinaryBuffer buffer2(std::move(buffer));
+
+  EXPECT_TRUE(buffer.IsEmpty());
+  EXPECT_EQ(0u, buffer.GetSize());
+  EXPECT_EQ(0u, buffer.GetLength());
+  EXPECT_TRUE(buffer.GetSpan().empty());
+
+  EXPECT_FALSE(buffer2.IsEmpty());
+  EXPECT_EQ(1u, buffer2.GetSize());
+  EXPECT_EQ(1u, buffer2.GetLength());
+  EXPECT_EQ(65u, buffer2.GetSpan()[0]);
+}
+
+TEST(BinaryBuffer, MoveAssign) {
+  BinaryBuffer buffer;
+  BinaryBuffer buffer2;
+  buffer.AppendUint8(65u);
+  buffer2 = std::move(buffer);
+
+  EXPECT_TRUE(buffer.IsEmpty());
+  EXPECT_EQ(0u, buffer.GetSize());
+  EXPECT_EQ(0u, buffer.GetLength());
+  EXPECT_TRUE(buffer.GetSpan().empty());
+
+  EXPECT_FALSE(buffer2.IsEmpty());
+  EXPECT_EQ(1u, buffer2.GetSize());
+  ASSERT_EQ(1u, buffer2.GetLength());
+  EXPECT_EQ(65u, buffer2.GetSpan()[0]);
+}
+
+TEST(BinaryBuffer, Clear) {
+  BinaryBuffer buffer;
+  buffer.AppendUint8(65u);
+  buffer.Clear();
+  EXPECT_TRUE(buffer.IsEmpty());
+  EXPECT_EQ(0u, buffer.GetSize());
+  EXPECT_EQ(0u, buffer.GetLength());
+  EXPECT_TRUE(buffer.GetSpan().empty());
+}
+
+TEST(BinaryBuffer, AppendSpans) {
+  BinaryBuffer buffer;
+  std::vector<uint8_t> aaa(3, 65u);
+  std::vector<uint8_t> bbb(3, 66u);
+  buffer.AppendSpan(aaa);
+  buffer.AppendSpan(bbb);
+  EXPECT_FALSE(buffer.IsEmpty());
+  EXPECT_EQ(6u, buffer.GetSize());
+  EXPECT_EQ(6u, buffer.GetLength());
+  EXPECT_EQ(65u, buffer.GetSpan()[0]);
+  EXPECT_EQ(65u, buffer.GetSpan()[1]);
+  EXPECT_EQ(65u, buffer.GetSpan()[2]);
+  EXPECT_EQ(66u, buffer.GetSpan()[3]);
+  EXPECT_EQ(66u, buffer.GetSpan()[4]);
+  EXPECT_EQ(66u, buffer.GetSpan()[5]);
+}
+
+TEST(BinaryBuffer, AppendBlocks) {
+  BinaryBuffer buffer;
+  std::vector<uint8_t> aaa(3, 65u);
+  std::vector<uint8_t> bbb(3, 66u);
+  buffer.AppendSpan(aaa);
+  buffer.AppendSpan(bbb);
+  EXPECT_EQ(6u, buffer.GetSize());
+  EXPECT_EQ(6u, buffer.GetLength());
+  EXPECT_EQ(65u, buffer.GetSpan()[0]);
+  EXPECT_EQ(65u, buffer.GetSpan()[1]);
+  EXPECT_EQ(65u, buffer.GetSpan()[2]);
+  EXPECT_EQ(66u, buffer.GetSpan()[3]);
+  EXPECT_EQ(66u, buffer.GetSpan()[4]);
+  EXPECT_EQ(66u, buffer.GetSpan()[5]);
+}
+
+TEST(BinaryBuffer, AppendStrings) {
+  BinaryBuffer buffer;
+  buffer.AppendString("AA");
+  buffer.AppendString("BB");
+  EXPECT_EQ(4u, buffer.GetSize());
+  EXPECT_EQ(4u, buffer.GetLength());
+  EXPECT_EQ(65u, buffer.GetSpan()[0]);
+  EXPECT_EQ(65u, buffer.GetSpan()[1]);
+  EXPECT_EQ(66u, buffer.GetSpan()[2]);
+  EXPECT_EQ(66u, buffer.GetSpan()[3]);
+}
+
+TEST(BinaryBuffer, AppendBytes) {
+  BinaryBuffer buffer;
+  buffer.AppendUint8(65u);
+  buffer.AppendUint8(66u);
+  EXPECT_EQ(2u, buffer.GetSize());
+  EXPECT_EQ(2u, buffer.GetLength());
+  EXPECT_EQ(65u, buffer.GetSpan()[0]);
+  EXPECT_EQ(66u, buffer.GetSpan()[1]);
+}
+
+// Assumes little endian.
+TEST(BinaryBuffer, AppendUint16) {
+  BinaryBuffer buffer;
+  buffer.AppendUint16(0x4321);
+  EXPECT_EQ(2u, buffer.GetSize());
+  EXPECT_EQ(2u, buffer.GetLength());
+  EXPECT_EQ(0x21u, buffer.GetSpan()[0]);
+  EXPECT_EQ(0x43u, buffer.GetSpan()[1]);
+}
+
+// Assumes little endian.
+TEST(BinaryBuffer, AppendUint32) {
+  BinaryBuffer buffer;
+  buffer.AppendUint32(0x87654321);
+  EXPECT_EQ(4u, buffer.GetSize());
+  EXPECT_EQ(4u, buffer.GetLength());
+  EXPECT_EQ(0x21u, buffer.GetSpan()[0]);
+  EXPECT_EQ(0x43u, buffer.GetSpan()[1]);
+  EXPECT_EQ(0x65u, buffer.GetSpan()[2]);
+  EXPECT_EQ(0x87u, buffer.GetSpan()[3]);
+}
+
+TEST(BinaryBuffer, AppendDouble) {
+  BinaryBuffer buffer;
+  buffer.AppendDouble(1234.5678);
+  EXPECT_EQ(8u, buffer.GetSize());
+  EXPECT_EQ(8u, buffer.GetLength());
+  // arch-dependent bit pattern.
+}
+
+}  // namespace fxcrt
diff --git a/core/fxcrt/byteorder.h b/core/fxcrt/byteorder.h
index 283ac4e..b6c7a64 100644
--- a/core/fxcrt/byteorder.h
+++ b/core/fxcrt/byteorder.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxcrt/byteorder_unittest.cpp b/core/fxcrt/byteorder_unittest.cpp
index 20f7a12..6688934 100644
--- a/core/fxcrt/byteorder_unittest.cpp
+++ b/core/fxcrt/byteorder_unittest.cpp
@@ -1,28 +1,14 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxcrt/byteorder.h"
 
+#include "core/fxcrt/fx_system.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
-// Original code to use as a reference implementation.
-
-#define FXWORD_GET_LSBFIRST(p)                                \
-  (static_cast<uint16_t>((static_cast<uint16_t>(p[1]) << 8) | \
-                         (static_cast<uint16_t>(p[0]))))
-#define FXWORD_GET_MSBFIRST(p)                                \
-  (static_cast<uint16_t>((static_cast<uint16_t>(p[0]) << 8) | \
-                         (static_cast<uint16_t>(p[1]))))
-#define FXDWORD_GET_LSBFIRST(p)                                                \
-  ((static_cast<uint32_t>(p[3]) << 24) | (static_cast<uint32_t>(p[2]) << 16) | \
-   (static_cast<uint32_t>(p[1]) << 8) | (static_cast<uint32_t>(p[0])))
-#define FXDWORD_GET_MSBFIRST(p)                                                \
-  ((static_cast<uint32_t>(p[0]) << 24) | (static_cast<uint32_t>(p[1]) << 16) | \
-   (static_cast<uint32_t>(p[2]) << 8) | (static_cast<uint32_t>(p[3])))
-
 constexpr uint32_t kTestValues32[] = {
     0x0,      0x1,        0x2,        0x3,        0x4,       0xfe,
     0xff,     0x100,      0x101,      0xffff,     0x10000,   0x123456,
@@ -37,7 +23,11 @@
   for (uint32_t v = 0; v < 0x10000; ++v) {
     const uint16_t v16 = v;
     uint16_t expected =
-        FXWORD_GET_LSBFIRST(reinterpret_cast<const uint8_t*>(&v16));
+        FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<const uint8_t*>(&v16));
+    EXPECT_EQ(expected, ByteSwapToLE16(v16)) << v;
+
+    // Check safety against unexpectedly signed bytes.
+    expected = FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<const int8_t*>(&v16));
     EXPECT_EQ(expected, ByteSwapToLE16(v16)) << v;
   }
 }
@@ -45,7 +35,11 @@
 TEST(ByteOrder, ByteSwapToLE32) {
   for (uint32_t v : kTestValues32) {
     uint32_t expected =
-        FXDWORD_GET_LSBFIRST(reinterpret_cast<const uint8_t*>(&v));
+        FXSYS_UINT32_GET_LSBFIRST(reinterpret_cast<const uint8_t*>(&v));
+    EXPECT_EQ(expected, ByteSwapToLE32(v)) << v;
+
+    // Check safety against unexpectedly signed bytes.
+    expected = FXSYS_UINT32_GET_LSBFIRST(reinterpret_cast<const int8_t*>(&v));
     EXPECT_EQ(expected, ByteSwapToLE32(v)) << v;
   }
 }
@@ -55,7 +49,11 @@
   for (uint32_t v = 0; v < 0x10000; ++v) {
     const uint16_t v16 = v;
     uint16_t expected =
-        FXWORD_GET_MSBFIRST(reinterpret_cast<const uint8_t*>(&v16));
+        FXSYS_UINT16_GET_MSBFIRST(reinterpret_cast<const uint8_t*>(&v16));
+    EXPECT_EQ(expected, ByteSwapToBE16(v16)) << v;
+
+    // Check safety against unexpectedly signed bytes.
+    expected = FXSYS_UINT16_GET_MSBFIRST(reinterpret_cast<const int8_t*>(&v16));
     EXPECT_EQ(expected, ByteSwapToBE16(v16)) << v;
   }
 }
@@ -63,7 +61,11 @@
 TEST(ByteOrder, ByteSwapToBE32) {
   for (uint32_t v : kTestValues32) {
     uint32_t expected =
-        FXDWORD_GET_MSBFIRST(reinterpret_cast<const uint8_t*>(&v));
+        FXSYS_UINT32_GET_MSBFIRST(reinterpret_cast<const uint8_t*>(&v));
+    EXPECT_EQ(expected, ByteSwapToBE32(v)) << v;
+
+    // Check safety against unexpectedly signed bytes.
+    expected = FXSYS_UINT32_GET_MSBFIRST(reinterpret_cast<const int8_t*>(&v));
     EXPECT_EQ(expected, ByteSwapToBE32(v)) << v;
   }
 }
diff --git a/core/fxcrt/bytestring.cpp b/core/fxcrt/bytestring.cpp
index 0778bc2..d2ba60e 100644
--- a/core/fxcrt/bytestring.cpp
+++ b/core/fxcrt/bytestring.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,21 +6,24 @@
 
 #include "core/fxcrt/bytestring.h"
 
+#include <ctype.h>
 #include <stddef.h>
 
 #include <algorithm>
-#include <cctype>
+#include <sstream>
 #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/containers/span.h"
 #include "third_party/base/numerics/safe_math.h"
-#include "third_party/base/span.h"
-#include "third_party/base/stl_util.h"
 
 template class fxcrt::StringDataTemplate<char>;
 template class fxcrt::StringViewTemplate<char>;
@@ -32,23 +35,22 @@
 constexpr char kTrimChars[] = "\x09\x0a\x0b\x0c\x0d\x20";
 
 const char* FX_strstr(const char* haystack,
-                      int haystack_len,
+                      size_t haystack_len,
                       const char* needle,
-                      int needle_len) {
-  if (needle_len > haystack_len || needle_len == 0) {
+                      size_t needle_len) {
+  if (needle_len > haystack_len || needle_len == 0)
     return nullptr;
-  }
+
   const char* end_ptr = haystack + haystack_len - needle_len;
   while (haystack <= end_ptr) {
-    int i = 0;
-    while (1) {
-      if (haystack[i] != needle[i]) {
+    size_t i = 0;
+    while (true) {
+      if (haystack[i] != needle[i])
         break;
-      }
+
       i++;
-      if (i == needle_len) {
+      if (i == needle_len)
         return haystack;
-      }
     }
     haystack++;
   }
@@ -62,10 +64,6 @@
 static_assert(sizeof(ByteString) <= sizeof(char*),
               "Strings must not require more space than pointers");
 
-#define FORCE_ANSI 0x10000
-#define FORCE_UNICODE 0x20000
-#define FORCE_INT64 0x40000
-
 // static
 ByteString ByteString::FormatInteger(int i) {
   char buf[32];
@@ -182,13 +180,21 @@
   }
 }
 
-ByteString::ByteString(const std::ostringstream& outStream) {
-  std::string str = outStream.str();
-  if (str.length() > 0)
-    m_pData.Reset(StringData::Create(str.c_str(), str.length()));
+ByteString::ByteString(const fxcrt::ostringstream& outStream) {
+  auto str = outStream.str();
+  if (!str.empty())
+    m_pData.Reset(StringData::Create(str.c_str(), str.size()));
 }
 
-ByteString::~ByteString() {}
+ByteString::~ByteString() = default;
+
+void ByteString::clear() {
+  if (m_pData && m_pData->CanOperateInPlace(0)) {
+    m_pData->m_nDataLength = 0;
+    return;
+  }
+  m_pData.Reset();
+}
 
 ByteString& ByteString::operator=(const char* str) {
   if (!str || !str[0])
@@ -215,7 +221,7 @@
   return *this;
 }
 
-ByteString& ByteString::operator=(ByteString&& that) {
+ByteString& ByteString::operator=(ByteString&& that) noexcept {
   if (m_pData != that.m_pData)
     m_pData = std::move(that.m_pData);
 
@@ -256,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 {
@@ -264,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 {
@@ -291,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);
 }
 
@@ -305,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);
 }
 
@@ -381,7 +387,7 @@
     return;
   }
 
-  ASSERT(m_pData->m_nRefs == 1);
+  DCHECK_EQ(m_pData->m_nRefs, 1);
   m_pData->m_nDataLength = nNewLength;
   m_pData->m_String[nNewLength] = 0;
   if (m_pData->m_nAllocLength - nNewLength >= 32) {
@@ -426,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)
@@ -435,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;
 }
@@ -469,6 +476,11 @@
   return m_pData ? m_pData->m_nRefs : 0;
 }
 
+ByteString ByteString::Substr(size_t offset) const {
+  // Unsigned underflow is well-defined and out-of-range is handled by Substr().
+  return Substr(offset, GetLength() - offset);
+}
+
 ByteString ByteString::Substr(size_t first, size_t count) const {
   if (!m_pData)
     return ByteString();
@@ -491,14 +503,11 @@
 }
 
 ByteString ByteString::First(size_t count) const {
-  if (count == 0 || !IsValidLength(count))
-    return ByteString();
   return Substr(0, count);
 }
 
 ByteString ByteString::Last(size_t count) const {
-  if (count == 0 || !IsValidLength(count))
-    return ByteString();
+  // Unsigned underflow is well-defined and out-of-range is handled by Substr().
   return Substr(GetLength() - count, count);
 }
 
@@ -514,7 +523,7 @@
 }
 
 void ByteString::SetAt(size_t index, char c) {
-  ASSERT(IsValidIndex(index));
+  DCHECK(IsValidIndex(index));
   ReallocBeforeWrite(m_pData->m_nDataLength);
   m_pData->m_String[index] = c;
 }
@@ -526,54 +535,57 @@
 
   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;
 }
 
-Optional<size_t> ByteString::Find(char ch, size_t start) const {
+absl::optional<size_t> ByteString::Find(char ch, size_t start) const {
   if (!m_pData)
-    return pdfium::nullopt;
+    return absl::nullopt;
 
   if (!IsValidIndex(start))
-    return pdfium::nullopt;
+    return absl::nullopt;
 
-  const char* pStr = static_cast<const char*>(
-      memchr(m_pData->m_String + start, ch, m_pData->m_nDataLength - start));
-  return pStr ? Optional<size_t>(static_cast<size_t>(pStr - m_pData->m_String))
-              : pdfium::nullopt;
+  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;
 }
 
-Optional<size_t> ByteString::Find(ByteStringView subStr, size_t start) const {
+absl::optional<size_t> ByteString::Find(ByteStringView subStr,
+                                        size_t start) const {
   if (!m_pData)
-    return pdfium::nullopt;
+    return absl::nullopt;
 
   if (!IsValidIndex(start))
-    return pdfium::nullopt;
+    return absl::nullopt;
 
   const char* pStr =
       FX_strstr(m_pData->m_String + start, m_pData->m_nDataLength - start,
                 subStr.unterminated_c_str(), subStr.GetLength());
-  return pStr ? Optional<size_t>(static_cast<size_t>(pStr - m_pData->m_String))
-              : pdfium::nullopt;
+  return pStr ? absl::optional<size_t>(
+                    static_cast<size_t>(pStr - m_pData->m_String))
+              : absl::nullopt;
 }
 
-Optional<size_t> ByteString::ReverseFind(char ch) const {
+absl::optional<size_t> ByteString::ReverseFind(char ch) const {
   if (!m_pData)
-    return pdfium::nullopt;
+    return absl::nullopt;
 
   size_t nLength = m_pData->m_nDataLength;
   while (nLength--) {
     if (m_pData->m_String[nLength] == ch)
       return nLength;
   }
-  return pdfium::nullopt;
+  return absl::nullopt;
 }
 
 void ByteString::MakeLower() {
-  if (!m_pData)
+  if (IsEmpty())
     return;
 
   ReallocBeforeWrite(m_pData->m_nDataLength);
@@ -581,7 +593,7 @@
 }
 
 void ByteString::MakeUpper() {
-  if (!m_pData)
+  if (IsEmpty())
     return;
 
   ReallocBeforeWrite(m_pData->m_nDataLength);
@@ -589,7 +601,7 @@
 }
 
 size_t ByteString::Remove(char chRemove) {
-  if (!m_pData || m_pData->m_nDataLength == 0)
+  if (IsEmpty())
     return 0;
 
   char* pstrSource = m_pData->m_String;
@@ -631,7 +643,7 @@
   size_t nCount = 0;
   const char* pStart = m_pData->m_String;
   char* pEnd = m_pData->m_String + m_pData->m_nDataLength;
-  while (1) {
+  while (true) {
     const char* pTarget = FX_strstr(pStart, static_cast<int>(pEnd - pStart),
                                     pOld.unterminated_c_str(), nSourceLen);
     if (!pTarget)
@@ -657,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;
 }
@@ -675,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)
@@ -727,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;
   }
 }
@@ -774,26 +787,30 @@
 
 }  // namespace fxcrt
 
-uint32_t FX_HashCode_GetA(ByteStringView str, bool bIgnoreCase) {
+uint32_t FX_HashCode_GetA(ByteStringView str) {
   uint32_t dwHashCode = 0;
-  if (bIgnoreCase) {
-    for (ByteStringView::UnsignedType c : str)
-      dwHashCode = 31 * dwHashCode + tolower(c);
-  } else {
-    for (ByteStringView::UnsignedType c : str)
-      dwHashCode = 31 * dwHashCode + c;
-  }
+  for (ByteStringView::UnsignedType c : str)
+    dwHashCode = 31 * dwHashCode + c;
   return dwHashCode;
 }
 
-uint32_t FX_HashCode_GetAsIfW(ByteStringView str, bool bIgnoreCase) {
+uint32_t FX_HashCode_GetLoweredA(ByteStringView str) {
   uint32_t dwHashCode = 0;
-  if (bIgnoreCase) {
-    for (ByteStringView::UnsignedType c : str)
-      dwHashCode = 1313 * dwHashCode + FXSYS_towlower(c);
-  } else {
-    for (ByteStringView::UnsignedType c : str)
-      dwHashCode = 1313 * dwHashCode + c;
-  }
+  for (ByteStringView::UnsignedType c : str)
+    dwHashCode = 31 * dwHashCode + tolower(c);
+  return dwHashCode;
+}
+
+uint32_t FX_HashCode_GetAsIfW(ByteStringView str) {
+  uint32_t dwHashCode = 0;
+  for (ByteStringView::UnsignedType c : str)
+    dwHashCode = 1313 * dwHashCode + c;
+  return dwHashCode;
+}
+
+uint32_t FX_HashCode_GetLoweredAsIfW(ByteStringView str) {
+  uint32_t dwHashCode = 0;
+  for (ByteStringView::UnsignedType c : str)
+    dwHashCode = 1313 * dwHashCode + FXSYS_towlower(c);
   return dwHashCode;
 }
diff --git a/core/fxcrt/bytestring.h b/core/fxcrt/bytestring.h
index 7c7f2ee..3c23d62 100644
--- a/core/fxcrt/bytestring.h
+++ b/core/fxcrt/bytestring.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,23 @@
 #ifndef CORE_FXCRT_BYTESTRING_H_
 #define CORE_FXCRT_BYTESTRING_H_
 
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
 #include <functional>
+#include <iosfwd>
 #include <iterator>
-#include <ostream>
-#include <sstream>
 #include <utility>
 
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/fx_string_wrappers.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/string_data_template.h"
 #include "core/fxcrt/string_view_template.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcrt {
 
@@ -31,11 +35,10 @@
   using const_iterator = const CharType*;
   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
 
-  static ByteString FormatInteger(int i) WARN_UNUSED_RESULT;
-  static ByteString FormatFloat(float f) WARN_UNUSED_RESULT;
-  static ByteString Format(const char* pFormat, ...) WARN_UNUSED_RESULT;
-  static ByteString FormatV(const char* pFormat,
-                            va_list argList) WARN_UNUSED_RESULT;
+  [[nodiscard]] static ByteString FormatInteger(int i);
+  [[nodiscard]] static ByteString FormatFloat(float f);
+  [[nodiscard]] static ByteString Format(const char* pFormat, ...);
+  [[nodiscard]] static ByteString FormatV(const char* pFormat, va_list argList);
 
   ByteString();
   ByteString(const ByteString& other);
@@ -43,10 +46,11 @@
   // Move-construct a ByteString. After construction, |other| is empty.
   ByteString(ByteString&& other) noexcept;
 
+  // Make a one-character string from a char.
+  explicit ByteString(char ch);
+
   // Deliberately implicit to avoid calling on every string literal.
   // NOLINTNEXTLINE(runtime/explicit)
-  ByteString(char ch);
-  // NOLINTNEXTLINE(runtime/explicit)
   ByteString(const char* ptr);
 
   // No implicit conversions from wide strings.
@@ -59,11 +63,13 @@
   explicit ByteString(ByteStringView bstrc);
   ByteString(ByteStringView str1, ByteStringView str2);
   ByteString(const std::initializer_list<ByteStringView>& list);
-  explicit ByteString(const std::ostringstream& outStream);
+  explicit ByteString(const fxcrt::ostringstream& outStream);
 
   ~ByteString();
 
-  void clear() { m_pData.Reset(); }
+  // Holds on to buffer if possible for later re-use. Assign ByteString()
+  // to force immediate release if desired.
+  void clear();
 
   // Explicit conversion to C-style string.
   // Note: Any subsequent modification of |this| will invalidate the result.
@@ -134,7 +140,7 @@
   ByteString& operator=(const ByteString& that);
 
   // Move-assign a ByteString. After assignment, |that| is empty.
-  ByteString& operator=(ByteString&& that);
+  ByteString& operator=(ByteString&& that) noexcept;
 
   ByteString& operator+=(char ch);
   ByteString& operator+=(const char* str);
@@ -163,13 +169,14 @@
   pdfium::span<char> GetBuffer(size_t nMinBufLength);
   void ReleaseBuffer(size_t nNewLength);
 
+  ByteString Substr(size_t offset) const;
   ByteString Substr(size_t first, size_t count) const;
   ByteString First(size_t count) const;
   ByteString Last(size_t count) const;
 
-  Optional<size_t> Find(ByteStringView subStr, size_t start = 0) const;
-  Optional<size_t> Find(char ch, size_t start = 0) const;
-  Optional<size_t> ReverseFind(char ch) const;
+  absl::optional<size_t> Find(ByteStringView subStr, size_t start = 0) const;
+  absl::optional<size_t> Find(char ch, size_t start = 0) const;
+  absl::optional<size_t> ReverseFind(char ch) const;
 
   bool Contains(ByteStringView lpszSub, size_t start = 0) const {
     return Find(lpszSub, start).has_value();
@@ -232,6 +239,12 @@
 inline bool operator<(const char* lhs, const ByteString& rhs) {
   return rhs.Compare(lhs) > 0;
 }
+inline bool operator<(const ByteStringView& lhs, const ByteString& rhs) {
+  return rhs.Compare(lhs) > 0;
+}
+inline bool operator<(const ByteStringView& lhs, const char* rhs) {
+  return lhs < ByteStringView(rhs);
+}
 
 inline ByteString operator+(ByteStringView str1, ByteStringView str2) {
   return ByteString(str1, str2);
@@ -246,7 +259,7 @@
   return ByteString(str1, ByteStringView(ch));
 }
 inline ByteString operator+(char ch, ByteStringView str2) {
-  return ByteString(ch, str2);
+  return ByteString(ByteStringView(ch), str2);
 }
 inline ByteString operator+(const ByteString& str1, const ByteString& str2) {
   return ByteString(str1.AsStringView(), str2.AsStringView());
@@ -255,7 +268,7 @@
   return ByteString(str1.AsStringView(), ByteStringView(ch));
 }
 inline ByteString operator+(char ch, const ByteString& str2) {
-  return ByteString(ch, str2.AsStringView());
+  return ByteString(ByteStringView(ch), str2.AsStringView());
 }
 inline ByteString operator+(const ByteString& str1, const char* str2) {
   return ByteString(str1.AsStringView(), str2);
@@ -273,19 +286,28 @@
 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;
 
-uint32_t FX_HashCode_GetA(ByteStringView str, bool bIgnoreCase);
-uint32_t FX_HashCode_GetAsIfW(ByteStringView str, bool bIgnoreCase);
+uint32_t FX_HashCode_GetA(ByteStringView str);
+uint32_t FX_HashCode_GetLoweredA(ByteStringView str);
+uint32_t FX_HashCode_GetAsIfW(ByteStringView str);
+uint32_t FX_HashCode_GetLoweredAsIfW(ByteStringView str);
 
 namespace std {
 
 template <>
 struct hash<ByteString> {
-  std::size_t operator()(const ByteString& str) const {
-    return FX_HashCode_GetA(str.AsStringView(), false);
+  size_t operator()(const ByteString& str) const {
+    return FX_HashCode_GetA(str.AsStringView());
   }
 };
 
diff --git a/core/fxcrt/bytestring_unittest.cpp b/core/fxcrt/bytestring_unittest.cpp
index e2e87f0..9d2f132 100644
--- a/core/fxcrt/bytestring_unittest.cpp
+++ b/core/fxcrt/bytestring_unittest.cpp
@@ -1,17 +1,21 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxcrt/bytestring.h"
 
+#include <limits.h>
+
 #include <algorithm>
+#include <functional>
 #include <iterator>
+#include <set>
 #include <vector>
 
 #include "core/fxcrt/fx_string.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/span.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcrt {
 
@@ -112,6 +116,26 @@
     }
     EXPECT_EQ(1, string1.ReferenceCountForTesting());
   }
+  {
+    // From char*.
+    ByteString string1 = "abc";
+    EXPECT_EQ("abc", string1);
+    string1 = nullptr;
+    EXPECT_TRUE(string1.IsEmpty());
+    string1 = "def";
+    EXPECT_EQ("def", string1);
+    string1 = "";
+    EXPECT_TRUE(string1.IsEmpty());
+  }
+  {
+    // From ByteStringView.
+    ByteString string1(ByteStringView("abc"));
+    EXPECT_EQ("abc", string1);
+    string1 = ByteStringView("");
+    EXPECT_TRUE(string1.IsEmpty());
+    string1 = ByteStringView("def");
+    EXPECT_EQ("def", string1);
+  }
 }
 
 TEST(ByteString, OperatorLT) {
@@ -207,6 +231,19 @@
   EXPECT_FALSE(def < c_abc);
   EXPECT_TRUE(abc < v_def);
   EXPECT_FALSE(def < v_abc);
+
+  EXPECT_TRUE(v_empty < a);
+  EXPECT_TRUE(v_empty < c_a);
+
+  std::set<ByteString, std::less<>> str_set;
+  bool inserted = str_set.insert(ByteString("hello")).second;
+  ASSERT_TRUE(inserted);
+  EXPECT_TRUE(pdfium::Contains(str_set, ByteString("hello")));
+  EXPECT_TRUE(pdfium::Contains(str_set, ByteStringView("hello")));
+  EXPECT_TRUE(pdfium::Contains(str_set, "hello"));
+  EXPECT_FALSE(pdfium::Contains(str_set, ByteString("goodbye")));
+  EXPECT_FALSE(pdfium::Contains(str_set, ByteStringView("goodbye")));
+  EXPECT_FALSE(pdfium::Contains(str_set, "goodbye"));
 }
 
 TEST(ByteString, OperatorEQ) {
@@ -506,7 +543,16 @@
 }
 
 TEST(ByteString, Replace) {
+  ByteString empty;
+  empty.Replace("", "CLAMS");
+  empty.Replace("xx", "CLAMS");
+  EXPECT_EQ("", empty);
+
   ByteString fred("FRED");
+  fred.Replace("", "");
+  EXPECT_EQ("FRED", fred);
+  fred.Replace("", "CLAMS");
+  EXPECT_EQ("FRED", fred);
   fred.Replace("FR", "BL");
   EXPECT_EQ("BLED", fred);
   fred.Replace("D", "DDY");
@@ -517,10 +563,20 @@
   EXPECT_EQ("BY", fred);
   fred.Replace("BY", "HI");
   EXPECT_EQ("HI", fred);
-  fred.Replace("", "CLAMS");
-  EXPECT_EQ("HI", fred);
-  fred.Replace("HI", "");
+  fred.Replace("I", "IHIHI");
+  EXPECT_EQ("HIHIHI", fred);
+  fred.Replace("HI", "HO");
+  EXPECT_EQ("HOHOHO", fred);
+  fred.Replace("HO", "");
   EXPECT_EQ("", fred);
+
+  ByteString five_xs("xxxxx");
+  five_xs.Replace("xx", "xxx");
+  EXPECT_EQ("xxxxxxx", five_xs);
+
+  ByteString five_ys("yyyyy");
+  five_ys.Replace("yy", "y");
+  EXPECT_EQ("yyy", five_ys);
 }
 
 TEST(ByteString, Insert) {
@@ -603,7 +659,20 @@
   EXPECT_EQ("", empty);
 }
 
-TEST(ByteString, Substr) {
+TEST(ByteString, OneArgSubstr) {
+  ByteString fred("FRED");
+  EXPECT_EQ("FRED", fred.Substr(0));
+  EXPECT_EQ("RED", fred.Substr(1));
+  EXPECT_EQ("ED", fred.Substr(2));
+  EXPECT_EQ("D", fred.Substr(3));
+  EXPECT_EQ("", fred.Substr(4));
+
+  ByteString empty;
+  EXPECT_EQ("", empty.Substr(0));
+  EXPECT_EQ("", empty.Substr(1));
+}
+
+TEST(ByteString, TwoArgSubstr) {
   ByteString fred("FRED");
   EXPECT_EQ("", fred.Substr(0, 0));
   EXPECT_EQ("", fred.Substr(3, 0));
@@ -665,9 +734,8 @@
   EXPECT_FALSE(empty_string.Find('a').has_value());
   EXPECT_FALSE(empty_string.Find('\0').has_value());
 
-  Optional<size_t> result;
   ByteString single_string("a");
-  result = single_string.Find('a');
+  absl::optional<size_t> result = single_string.Find('a');
   ASSERT_TRUE(result.has_value());
   EXPECT_EQ(0u, result.value());
   EXPECT_FALSE(single_string.Find('b').has_value());
@@ -714,9 +782,8 @@
   EXPECT_FALSE(empty_string.ReverseFind('a').has_value());
   EXPECT_FALSE(empty_string.ReverseFind('\0').has_value());
 
-  Optional<size_t> result;
   ByteString single_string("a");
-  result = single_string.ReverseFind('a');
+  absl::optional<size_t> result = single_string.ReverseFind('a');
   ASSERT_TRUE(result.has_value());
   EXPECT_EQ(0u, result.value());
   EXPECT_FALSE(single_string.ReverseFind('b').has_value());
@@ -751,6 +818,17 @@
   EXPECT_EQ("", empty);
   empty.MakeUpper();
   EXPECT_EQ("", empty);
+
+  ByteString empty_with_buffer("x");
+  empty_with_buffer.Delete(0);
+
+  ByteString additional_empty_with_buffer_ref = empty_with_buffer;
+  additional_empty_with_buffer_ref.MakeLower();
+  EXPECT_EQ("", additional_empty_with_buffer_ref);
+
+  additional_empty_with_buffer_ref = empty_with_buffer;
+  additional_empty_with_buffer_ref.MakeUpper();
+  EXPECT_EQ("", additional_empty_with_buffer_ref);
 }
 
 TEST(ByteString, Trim) {
@@ -1208,7 +1286,7 @@
   cleared_vec.pop_back();
   ByteStringView cleared_string(cleared_vec);
   EXPECT_EQ(0u, cleared_string.GetLength());
-  EXPECT_EQ(nullptr, cleared_string.raw_str());
+  EXPECT_FALSE(cleared_string.raw_str());
 }
 
 TEST(ByteStringView, GetID) {
@@ -1234,9 +1312,8 @@
   EXPECT_FALSE(empty_string.Find('a').has_value());
   EXPECT_FALSE(empty_string.Find('\0').has_value());
 
-  Optional<size_t> result;
   ByteStringView single_string("a");
-  result = single_string.Find('a');
+  absl::optional<size_t> result = single_string.Find('a');
   ASSERT_TRUE(result.has_value());
   EXPECT_EQ(0u, result.value());
   EXPECT_FALSE(single_string.Find('b').has_value());
@@ -1260,7 +1337,28 @@
   EXPECT_EQ(2u, result.value());
 }
 
-TEST(ByteStringView, Substr) {
+TEST(ByteStringView, OneArgSubstr) {
+  ByteStringView null_string;
+  EXPECT_EQ(null_string, null_string.Substr(0));
+  EXPECT_EQ(null_string, null_string.Substr(1));
+
+  ByteStringView empty_string("");
+  EXPECT_EQ("", empty_string.Substr(0));
+  EXPECT_EQ("", empty_string.Substr(1));
+
+  ByteStringView single_character("a");
+  EXPECT_EQ(single_character, single_character.Substr(0));
+  EXPECT_EQ("", single_character.Substr(1));
+
+  ByteStringView longer_string("abcdef");
+  EXPECT_EQ(longer_string, longer_string.Substr(0));
+  EXPECT_EQ("", longer_string.Substr(187));
+
+  ByteStringView trailing_substring("ef");
+  EXPECT_EQ(trailing_substring, longer_string.Substr(4));
+}
+
+TEST(ByteStringView, TwoArgSubstr) {
   ByteStringView null_string;
   EXPECT_EQ(null_string, null_string.Substr(0, 1));
   EXPECT_EQ(null_string, null_string.Substr(1, 1));
@@ -1603,9 +1701,9 @@
   EXPECT_TRUE(std::any_of(str.begin(), str.end(),
                           [](const char& c) { return c == 'a'; }));
 
-  EXPECT_TRUE(pdfium::ContainsValue(str, 'a'));
-  EXPECT_TRUE(pdfium::ContainsValue(str, 'b'));
-  EXPECT_FALSE(pdfium::ContainsValue(str, 'z'));
+  EXPECT_TRUE(pdfium::Contains(str, 'a'));
+  EXPECT_TRUE(pdfium::Contains(str, 'b'));
+  EXPECT_FALSE(pdfium::Contains(str, 'z'));
 }
 
 TEST(ByteString, FormatWidth) {
@@ -1630,19 +1728,19 @@
   EXPECT_EQ(0u, empty_str.GetLength());
 
   const char* cstr = empty_str.c_str();
-  EXPECT_NE(nullptr, cstr);
+  EXPECT_TRUE(cstr);
   EXPECT_EQ(0u, strlen(cstr));
 
   const uint8_t* rstr = empty_str.raw_str();
-  EXPECT_EQ(nullptr, rstr);
+  EXPECT_FALSE(rstr);
 
   pdfium::span<const char> cspan = empty_str.span();
   EXPECT_TRUE(cspan.empty());
-  EXPECT_EQ(nullptr, cspan.data());
+  EXPECT_FALSE(cspan.data());
 
   pdfium::span<const uint8_t> rspan = empty_str.raw_span();
   EXPECT_TRUE(rspan.empty());
-  EXPECT_EQ(nullptr, rspan.data());
+  EXPECT_FALSE(rspan.data());
 }
 
 TEST(ByteString, InitializerList) {
@@ -1720,9 +1818,9 @@
   EXPECT_TRUE(std::any_of(str.begin(), str.end(),
                           [](const char& c) { return c == 'a'; }));
 
-  EXPECT_TRUE(pdfium::ContainsValue(str, 'a'));
-  EXPECT_TRUE(pdfium::ContainsValue(str, 'b'));
-  EXPECT_FALSE(pdfium::ContainsValue(str, 'z'));
+  EXPECT_TRUE(pdfium::Contains(str, 'a'));
+  EXPECT_TRUE(pdfium::Contains(str, 'b'));
+  EXPECT_FALSE(pdfium::Contains(str, 'z'));
 }
 
 TEST(CFX_BytrString, EqualNoCase) {
@@ -1874,21 +1972,21 @@
 }
 
 TEST(ByteString, FX_HashCode_Ascii) {
-  EXPECT_EQ(0u, FX_HashCode_GetA("", false));
-  EXPECT_EQ(65u, FX_HashCode_GetA("A", false));
-  EXPECT_EQ(97u, FX_HashCode_GetA("A", true));
-  EXPECT_EQ(31 * 65u + 66u, FX_HashCode_GetA("AB", false));
-  EXPECT_EQ(31u * 65u + 255u, FX_HashCode_GetA("A\xff", false));
-  EXPECT_EQ(31u * 97u + 255u, FX_HashCode_GetA("A\xff", true));
+  EXPECT_EQ(0u, FX_HashCode_GetA(""));
+  EXPECT_EQ(65u, FX_HashCode_GetA("A"));
+  EXPECT_EQ(97u, FX_HashCode_GetLoweredA("A"));
+  EXPECT_EQ(31 * 65u + 66u, FX_HashCode_GetA("AB"));
+  EXPECT_EQ(31u * 65u + 255u, FX_HashCode_GetA("A\xff"));
+  EXPECT_EQ(31u * 97u + 255u, FX_HashCode_GetLoweredA("A\xff"));
 }
 
 TEST(ByteString, FX_HashCode_Wide) {
-  EXPECT_EQ(0u, FX_HashCode_GetAsIfW("", false));
-  EXPECT_EQ(65u, FX_HashCode_GetAsIfW("A", false));
-  EXPECT_EQ(97u, FX_HashCode_GetAsIfW("A", true));
-  EXPECT_EQ(1313u * 65u + 66u, FX_HashCode_GetAsIfW("AB", false));
-  EXPECT_EQ(1313u * 65u + 255u, FX_HashCode_GetAsIfW("A\xff", false));
-  EXPECT_EQ(1313u * 97u + 255u, FX_HashCode_GetAsIfW("A\xff", true));
+  EXPECT_EQ(0u, FX_HashCode_GetAsIfW(""));
+  EXPECT_EQ(65u, FX_HashCode_GetAsIfW("A"));
+  EXPECT_EQ(97u, FX_HashCode_GetLoweredAsIfW("A"));
+  EXPECT_EQ(1313u * 65u + 66u, FX_HashCode_GetAsIfW("AB"));
+  EXPECT_EQ(1313u * 65u + 255u, FX_HashCode_GetAsIfW("A\xff"));
+  EXPECT_EQ(1313u * 97u + 255u, FX_HashCode_GetLoweredAsIfW("A\xff"));
 }
 
 }  // namespace fxcrt
diff --git a/core/fxcrt/cfx_binarybuf.cpp b/core/fxcrt/cfx_binarybuf.cpp
deleted file mode 100644
index e24590c..0000000
--- a/core/fxcrt/cfx_binarybuf.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/cfx_binarybuf.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "core/fxcrt/fx_safe_types.h"
-
-CFX_BinaryBuf::CFX_BinaryBuf() = default;
-
-CFX_BinaryBuf::~CFX_BinaryBuf() = default;
-
-void CFX_BinaryBuf::Delete(size_t start_index, size_t count) {
-  if (!m_pBuffer || count > m_DataSize || start_index > m_DataSize - count)
-    return;
-
-  memmove(m_pBuffer.get() + start_index, m_pBuffer.get() + start_index + count,
-          m_DataSize - start_index - count);
-  m_DataSize -= count;
-}
-
-pdfium::span<uint8_t> CFX_BinaryBuf::GetSpan() {
-  return {GetBuffer(), GetSize()};
-}
-
-pdfium::span<const uint8_t> CFX_BinaryBuf::GetSpan() const {
-  return {GetBuffer(), GetSize()};
-}
-
-size_t CFX_BinaryBuf::GetLength() const {
-  return m_DataSize;
-}
-
-void CFX_BinaryBuf::Clear() {
-  m_DataSize = 0;
-}
-
-std::unique_ptr<uint8_t, FxFreeDeleter> CFX_BinaryBuf::DetachBuffer() {
-  m_DataSize = 0;
-  m_AllocSize = 0;
-  return std::move(m_pBuffer);
-}
-
-void CFX_BinaryBuf::EstimateSize(size_t size) {
-  if (m_AllocSize < size)
-    ExpandBuf(size - m_DataSize);
-}
-
-void CFX_BinaryBuf::ExpandBuf(size_t add_size) {
-  FX_SAFE_SIZE_T new_size = m_DataSize;
-  new_size += add_size;
-  if (m_AllocSize >= new_size.ValueOrDie())
-    return;
-
-  size_t alloc_step = std::max(static_cast<size_t>(128),
-                               m_AllocStep ? m_AllocStep : m_AllocSize / 4);
-  new_size += alloc_step - 1;  // Quantize, don't combine these lines.
-  new_size /= alloc_step;
-  new_size *= alloc_step;
-  m_AllocSize = new_size.ValueOrDie();
-  m_pBuffer.reset(m_pBuffer
-                      ? FX_Realloc(uint8_t, m_pBuffer.release(), m_AllocSize)
-                      : FX_Alloc(uint8_t, m_AllocSize));
-}
-
-void CFX_BinaryBuf::AppendSpan(pdfium::span<const uint8_t> span) {
-  return AppendBlock(span.data(), span.size());
-}
-
-void CFX_BinaryBuf::AppendBlock(const void* pBuf, size_t size) {
-  if (size == 0)
-    return;
-
-  ExpandBuf(size);
-  if (pBuf) {
-    memcpy(m_pBuffer.get() + m_DataSize, pBuf, size);
-  } else {
-    memset(m_pBuffer.get() + m_DataSize, 0, size);
-  }
-  m_DataSize += size;
-}
diff --git a/core/fxcrt/cfx_binarybuf.h b/core/fxcrt/cfx_binarybuf.h
deleted file mode 100644
index b757c9a..0000000
--- a/core/fxcrt/cfx_binarybuf.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_CFX_BINARYBUF_H_
-#define CORE_FXCRT_CFX_BINARYBUF_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/span.h"
-
-class CFX_BinaryBuf {
- public:
-  CFX_BinaryBuf();
-  virtual ~CFX_BinaryBuf();
-
-  pdfium::span<uint8_t> GetSpan();
-  pdfium::span<const uint8_t> GetSpan() const;
-  uint8_t* GetBuffer() const { return m_pBuffer.get(); }
-  size_t GetSize() const { return m_DataSize; }
-  virtual size_t GetLength() const;
-  bool IsEmpty() const { return GetLength() == 0; }
-
-  void Clear();
-  void SetAllocStep(size_t step) { m_AllocStep = step; }
-  void EstimateSize(size_t size);
-  void AppendSpan(pdfium::span<const uint8_t> span);
-  void AppendBlock(const void* pBuf, size_t size);
-  void AppendString(const ByteString& str) {
-    AppendBlock(str.c_str(), str.GetLength());
-  }
-
-  void AppendByte(uint8_t byte) {
-    ExpandBuf(1);
-    m_pBuffer.get()[m_DataSize++] = byte;
-  }
-
-  void Delete(size_t start_index, size_t count);
-
-  // Releases ownership of |m_pBuffer| and returns it.
-  std::unique_ptr<uint8_t, FxFreeDeleter> DetachBuffer();
-
- protected:
-  void ExpandBuf(size_t size);
-
-  size_t m_AllocStep = 0;
-  size_t m_AllocSize = 0;
-  size_t m_DataSize = 0;
-  std::unique_ptr<uint8_t, FxFreeDeleter> m_pBuffer;
-};
-
-#endif  // CORE_FXCRT_CFX_BINARYBUF_H_
diff --git a/core/fxcrt/cfx_bitstream.cpp b/core/fxcrt/cfx_bitstream.cpp
index d16fc2f..a1719da 100644
--- a/core/fxcrt/cfx_bitstream.cpp
+++ b/core/fxcrt/cfx_bitstream.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,31 +10,31 @@
 
 #include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_system.h"
+#include "third_party/base/check_op.h"
 
 CFX_BitStream::CFX_BitStream(pdfium::span<const uint8_t> pData)
-    : m_BitPos(0), m_BitSize(pData.size() * 8), m_pData(pData.data()) {
-  ASSERT(pData.size() <= std::numeric_limits<uint32_t>::max() / 8);
+    : m_BitSize(pData.size() * 8), m_pData(pData) {
+  CHECK_LE(m_pData.size(), std::numeric_limits<size_t>::max() / 8);
 }
 
-CFX_BitStream::~CFX_BitStream() {}
+CFX_BitStream::~CFX_BitStream() = default;
 
 void CFX_BitStream::ByteAlign() {
   m_BitPos = FxAlignToBoundary<8>(m_BitPos);
 }
 
 uint32_t CFX_BitStream::GetBits(uint32_t nBits) {
-  ASSERT(nBits > 0);
-  ASSERT(nBits <= 32);
+  DCHECK(nBits > 0);
+  DCHECK(nBits <= 32);
   if (nBits > m_BitSize || m_BitPos > m_BitSize - nBits)
     return 0;
 
   const uint32_t bit_pos = m_BitPos % 8;
-  uint32_t byte_pos = m_BitPos / 8;
-  const uint8_t* data = m_pData.Get();
-  uint8_t current_byte = data[byte_pos];
+  size_t byte_pos = m_BitPos / 8;
+  uint8_t current_byte = m_pData[byte_pos];
 
   if (nBits == 1) {
-    int bit = (current_byte & (1 << (7 - bit_pos))) ? 1 : 0;
+    uint32_t bit = (current_byte & (1 << (7 - bit_pos))) ? 1 : 0;
     m_BitPos++;
     return bit;
   }
@@ -54,10 +54,10 @@
   }
   while (bit_left >= 8) {
     bit_left -= 8;
-    result |= data[byte_pos++] << bit_left;
+    result |= m_pData[byte_pos++] << bit_left;
   }
   if (bit_left)
-    result |= data[byte_pos] >> (8 - bit_left);
+    result |= m_pData[byte_pos] >> (8 - bit_left);
   m_BitPos += nBits;
   return result;
 }
diff --git a/core/fxcrt/cfx_bitstream.h b/core/fxcrt/cfx_bitstream.h
index baa2a9f..8e168b8 100644
--- a/core/fxcrt/cfx_bitstream.h
+++ b/core/fxcrt/cfx_bitstream.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,10 @@
 #ifndef CORE_FXCRT_CFX_BITSTREAM_H_
 #define CORE_FXCRT_CFX_BITSTREAM_H_
 
+#include <stddef.h>
 #include <stdint.h>
 
-#include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_BitStream {
  public:
@@ -20,20 +20,20 @@
   void ByteAlign();
 
   bool IsEOF() const { return m_BitPos >= m_BitSize; }
-  uint32_t GetPos() const { return m_BitPos; }
+  size_t GetPos() const { return m_BitPos; }
   uint32_t GetBits(uint32_t nBits);
 
-  void SkipBits(uint32_t nBits) { m_BitPos += nBits; }
+  void SkipBits(size_t nBits) { m_BitPos += nBits; }
   void Rewind() { m_BitPos = 0; }
 
-  uint32_t BitsRemaining() const {
+  size_t BitsRemaining() const {
     return m_BitSize >= m_BitPos ? m_BitSize - m_BitPos : 0;
   }
 
  private:
-  uint32_t m_BitPos;
-  uint32_t m_BitSize;
-  UnownedPtr<const uint8_t> m_pData;
+  size_t m_BitPos = 0;
+  const size_t m_BitSize;
+  pdfium::span<const uint8_t> const m_pData;
 };
 
 #endif  // CORE_FXCRT_CFX_BITSTREAM_H_
diff --git a/core/fxcrt/cfx_bitstream_unittest.cpp b/core/fxcrt/cfx_bitstream_unittest.cpp
index 2ba3663..9debbd0 100644
--- a/core/fxcrt/cfx_bitstream_unittest.cpp
+++ b/core/fxcrt/cfx_bitstream_unittest.cpp
@@ -1,8 +1,11 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxcrt/cfx_bitstream.h"
+
+#include <limits>
+
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -116,6 +119,64 @@
   EXPECT_TRUE(bitstream.IsEOF());
   EXPECT_EQ(1063U, bitstream.GetPos());
   EXPECT_EQ(0U, bitstream.BitsRemaining());
+  EXPECT_EQ(0u, bitstream.GetBits(4));
+
+  // Align past the end.
+  bitstream.ByteAlign();
+  EXPECT_TRUE(bitstream.IsEOF());
+  EXPECT_EQ(1064U, bitstream.GetPos());
+  EXPECT_EQ(0U, bitstream.BitsRemaining());
+  EXPECT_EQ(0u, bitstream.GetBits(4));
+}
+
+TEST(fxcrt, BitStreamEmpty) {
+  CFX_BitStream bitstream({});
+  EXPECT_TRUE(bitstream.IsEOF());
+  EXPECT_EQ(0U, bitstream.GetPos());
+  EXPECT_EQ(0U, bitstream.BitsRemaining());
+
+  // Getting bits returns zero and doesn't advance.
+  EXPECT_EQ(0u, bitstream.GetBits(4));
+  EXPECT_EQ(0U, bitstream.GetPos());
+
+  // Skip past the end.
+  bitstream.SkipBits(63);
+  EXPECT_TRUE(bitstream.IsEOF());
+  EXPECT_EQ(63U, bitstream.GetPos());
+  EXPECT_EQ(0U, bitstream.BitsRemaining());
+  EXPECT_EQ(0u, bitstream.GetBits(4));
+
+  // Align past the end.
+  bitstream.ByteAlign();
+  EXPECT_TRUE(bitstream.IsEOF());
+  EXPECT_EQ(64U, bitstream.GetPos());
+  EXPECT_EQ(0U, bitstream.BitsRemaining());
+  EXPECT_EQ(0u, bitstream.GetBits(4));
+}
+
+TEST(fxcrt, BitStreamBig) {
+  // We can't actually allocate enough memory to test the limits of
+  // the bitstream arithmetic, but as long as we don't try to extract
+  // any bits, the calculations should be unaffected.
+  const uint8_t kNotReallyBigEnough[32] = {};
+  constexpr size_t kAllocationBytes = std::numeric_limits<size_t>::max() / 8;
+  constexpr size_t kAllocationBits = kAllocationBytes * 8;
+  CFX_BitStream bitstream({kNotReallyBigEnough, kAllocationBytes});
+  EXPECT_FALSE(bitstream.IsEOF());
+  EXPECT_EQ(0U, bitstream.GetPos());
+  EXPECT_EQ(kAllocationBits, bitstream.BitsRemaining());
+
+  // Skip some bits.
+  bitstream.SkipBits(kAllocationBits - 1023);
+  EXPECT_FALSE(bitstream.IsEOF());
+  EXPECT_EQ(kAllocationBits - 1023, bitstream.GetPos());
+  EXPECT_EQ(1023u, bitstream.BitsRemaining());
+
+  // Align to byte.
+  bitstream.ByteAlign();
+  EXPECT_FALSE(bitstream.IsEOF());
+  EXPECT_EQ(kAllocationBits - 1016, bitstream.GetPos());
+  EXPECT_EQ(1016u, bitstream.BitsRemaining());
 }
 
 TEST(fxcrt, BitStreamSameAsReferenceGetBits32) {
diff --git a/core/fxcrt/cfx_datetime.cpp b/core/fxcrt/cfx_datetime.cpp
index 56c6609..526bba4 100644
--- a/core/fxcrt/cfx_datetime.cpp
+++ b/core/fxcrt/cfx_datetime.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,51 +7,47 @@
 #include "core/fxcrt/cfx_datetime.h"
 
 #include "build/build_config.h"
+#include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_system.h"
-
-#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_MACOSX) || \
-    defined(OS_ASMJS) || defined(__wasm__)
-#include <sys/time.h>
-#include <time.h>
-#endif
+#include "third_party/base/check.h"
+#include "third_party/base/containers/span.h"
 
 namespace {
 
-const uint8_t g_FXDaysPerMonth[12] = {31, 28, 31, 30, 31, 30,
-                                      31, 31, 30, 31, 30, 31};
-const uint8_t g_FXDaysPerLeapMonth[12] = {31, 29, 31, 30, 31, 30,
-                                          31, 31, 30, 31, 30, 31};
-const int32_t g_FXDaysBeforeMonth[12] = {0,   31,  59,  90,  120, 151,
-                                         181, 212, 243, 273, 304, 334};
-const int32_t g_FXDaysBeforeLeapMonth[12] = {0,   31,  60,  91,  121, 152,
-                                             182, 213, 244, 274, 305, 335};
-const int32_t g_FXDaysPerYear = 365;
-const int32_t g_FXDaysPerLeapYear = 366;
+constexpr uint8_t kDaysPerMonth[12] = {31, 28, 31, 30, 31, 30,
+                                       31, 31, 30, 31, 30, 31};
+constexpr uint8_t kDaysPerLeapMonth[12] = {31, 29, 31, 30, 31, 30,
+                                           31, 31, 30, 31, 30, 31};
+constexpr int32_t kDaysBeforeMonth[12] = {0,   31,  59,  90,  120, 151,
+                                          181, 212, 243, 273, 304, 334};
+constexpr int32_t kDaysBeforeLeapMonth[12] = {0,   31,  60,  91,  121, 152,
+                                              182, 213, 244, 274, 305, 335};
+constexpr int32_t kDaysPerYear = 365;
+constexpr int32_t kDaysPerLeapYear = 366;
 
 int32_t DaysBeforeMonthInYear(int32_t iYear, uint8_t iMonth) {
-  ASSERT(iYear != 0);
-  ASSERT(iMonth >= 1);
-  ASSERT(iMonth <= 12);
-
-  const int32_t* p =
-      FX_IsLeapYear(iYear) ? g_FXDaysBeforeLeapMonth : g_FXDaysBeforeMonth;
+  DCHECK(iYear != 0);
+  pdfium::span<const int32_t> p = FX_IsLeapYear(iYear)
+                                      ? pdfium::make_span(kDaysBeforeLeapMonth)
+                                      : pdfium::make_span(kDaysBeforeMonth);
+  // Note: iMonth is one-based.
   return p[iMonth - 1];
 }
 
 int32_t DaysInYear(int32_t iYear) {
-  ASSERT(iYear != 0);
-  return FX_IsLeapYear(iYear) ? g_FXDaysPerLeapYear : g_FXDaysPerYear;
+  DCHECK(iYear != 0);
+  return FX_IsLeapYear(iYear) ? kDaysPerLeapYear : kDaysPerYear;
 }
 
 int64_t DateToDays(int32_t iYear,
                    uint8_t iMonth,
                    uint8_t iDay,
                    bool bIncludeThisDay) {
-  ASSERT(iYear != 0);
-  ASSERT(iMonth >= 1);
-  ASSERT(iMonth <= 12);
-  ASSERT(iDay >= 1);
-  ASSERT(iDay <= FX_DaysInMonth(iYear, iMonth));
+  DCHECK(iYear != 0);
+  DCHECK(iMonth >= 1);
+  DCHECK(iMonth <= 12);
+  DCHECK(iDay >= 1);
+  DCHECK(iDay <= FX_DaysInMonth(iYear, iMonth));
 
   int64_t iDays = DaysBeforeMonthInYear(iYear, iMonth);
   iDays += iDay;
@@ -68,61 +64,31 @@
          iYear / 400;
 }
 
-struct FXUT_SYSTEMTIME {
-  uint16_t wYear;
-  uint16_t wMonth;
-  uint16_t wDayOfWeek;
-  uint16_t wDay;
-  uint16_t wHour;
-  uint16_t wMinute;
-  uint16_t wSecond;
-  uint16_t wMillisecond;
-};
-
 }  // namespace
 
 uint8_t FX_DaysInMonth(int32_t iYear, uint8_t iMonth) {
-  ASSERT(iYear != 0);
-  ASSERT(iMonth >= 1);
-  ASSERT(iMonth <= 12);
-
-  const uint8_t* p =
-      FX_IsLeapYear(iYear) ? g_FXDaysPerLeapMonth : g_FXDaysPerMonth;
+  DCHECK(iYear != 0);
+  pdfium::span<const uint8_t> p = FX_IsLeapYear(iYear)
+                                      ? pdfium::make_span(kDaysPerLeapMonth)
+                                      : pdfium::make_span(kDaysPerMonth);
+  // Note: iMonth is one-based.
   return p[iMonth - 1];
 }
 
 bool FX_IsLeapYear(int32_t iYear) {
-  ASSERT(iYear != 0);
+  DCHECK(iYear != 0);
   return ((iYear % 4) == 0 && (iYear % 100) != 0) || (iYear % 400) == 0;
 }
 
 // static
 CFX_DateTime CFX_DateTime::Now() {
-  FXUT_SYSTEMTIME utLocal;
-#if defined(OS_WIN)
-  ::GetLocalTime((LPSYSTEMTIME)&utLocal);
-#else
-  timeval curTime;
-  gettimeofday(&curTime, nullptr);
-
-  struct tm st;
-  localtime_r(&curTime.tv_sec, &st);
-  utLocal.wYear = st.tm_year + 1900;
-  utLocal.wMonth = st.tm_mon + 1;
-  utLocal.wDayOfWeek = st.tm_wday;
-  utLocal.wDay = st.tm_mday;
-  utLocal.wHour = st.tm_hour;
-  utLocal.wMinute = st.tm_min;
-  utLocal.wSecond = st.tm_sec;
-  utLocal.wMillisecond = curTime.tv_usec / 1000;
-#endif  // defined(OS_WIN)
-
-  return CFX_DateTime(utLocal.wYear, static_cast<uint8_t>(utLocal.wMonth),
-                      static_cast<uint8_t>(utLocal.wDay),
-                      static_cast<uint8_t>(utLocal.wHour),
-                      static_cast<uint8_t>(utLocal.wMinute),
-                      static_cast<uint8_t>(utLocal.wSecond),
-                      static_cast<uint16_t>(utLocal.wMillisecond));
+  time_t t = FXSYS_time(nullptr);
+  struct tm* pTime = FXSYS_localtime(&t);
+  return CFX_DateTime(
+      pTime->tm_year + 1900, static_cast<uint8_t>(pTime->tm_mon + 1),
+      static_cast<uint8_t>(pTime->tm_mday),
+      static_cast<uint8_t>(pTime->tm_hour), static_cast<uint8_t>(pTime->tm_min),
+      static_cast<uint8_t>(pTime->tm_sec), 0);
 }
 
 int32_t CFX_DateTime::GetDayOfWeek() const {
diff --git a/core/fxcrt/cfx_datetime.h b/core/fxcrt/cfx_datetime.h
index a965df7..ef274bf 100644
--- a/core/fxcrt/cfx_datetime.h
+++ b/core/fxcrt/cfx_datetime.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,23 +7,16 @@
 #ifndef CORE_FXCRT_CFX_DATETIME_H_
 #define CORE_FXCRT_CFX_DATETIME_H_
 
-#include "core/fxcrt/fx_system.h"
+#include <stdint.h>
 
 bool FX_IsLeapYear(int32_t iYear);
 uint8_t FX_DaysInMonth(int32_t iYear, uint8_t iMonth);
 
 class CFX_DateTime {
  public:
-  static CFX_DateTime Now();
+  static CFX_DateTime Now();  // Accurate to seconds, subject to test overrides.
 
-  CFX_DateTime()
-      : year_(0),
-        month_(0),
-        day_(0),
-        hour_(0),
-        minute_(0),
-        second_(0),
-        millisecond_(0) {}
+  CFX_DateTime() = default;
   CFX_DateTime(int32_t year,
                uint8_t month,
                uint8_t day,
@@ -82,18 +75,13 @@
   bool operator==(const CFX_DateTime& other) const;
 
  private:
-  int32_t year_;
-  uint8_t month_;
-  uint8_t day_;
-  uint8_t hour_;
-  uint8_t minute_;
-  uint8_t second_;
-  uint16_t millisecond_;
-};
-
-struct FX_TIMEZONE {
-  int8_t tzHour;
-  uint8_t tzMinute;
+  int32_t year_ = 0;
+  uint8_t month_ = 0;
+  uint8_t day_ = 0;
+  uint8_t hour_ = 0;
+  uint8_t minute_ = 0;
+  uint8_t second_ = 0;
+  uint16_t millisecond_ = 0;
 };
 
 #endif  // CORE_FXCRT_CFX_DATETIME_H_
diff --git a/core/fxcrt/cfx_datetime_unittest.cpp b/core/fxcrt/cfx_datetime_unittest.cpp
new file mode 100644
index 0000000..5349e2c
--- /dev/null
+++ b/core/fxcrt/cfx_datetime_unittest.cpp
@@ -0,0 +1,18 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/cfx_datetime.h"
+
+#include "core/fxcrt/fake_time_test.h"
+
+TEST_F(FakeTimeTest, Now) {
+  CFX_DateTime dt = CFX_DateTime::Now();
+  EXPECT_EQ(2020, dt.GetYear());
+  EXPECT_EQ(4, dt.GetMonth());
+  EXPECT_EQ(23, dt.GetDay());
+  EXPECT_EQ(15, dt.GetHour());
+  EXPECT_EQ(5, dt.GetMinute());
+  EXPECT_EQ(21, dt.GetSecond());
+  EXPECT_EQ(0, dt.GetMillisecond());
+}
diff --git a/core/fxcrt/cfx_fileaccess_posix.cpp b/core/fxcrt/cfx_fileaccess_posix.cpp
index f08df66..fad4b3a 100644
--- a/core/fxcrt/cfx_fileaccess_posix.cpp
+++ b/core/fxcrt/cfx_fileaccess_posix.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,7 +13,7 @@
 #include <memory>
 
 #include "core/fxcrt/fx_stream.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 #ifndef O_BINARY
 #define O_BINARY 0
@@ -23,27 +23,9 @@
 #define O_LARGEFILE 0
 #endif  // O_LARGEFILE
 
-namespace {
-
-void GetFileMode(uint32_t dwModes, int32_t& nFlags, int32_t& nMasks) {
-  nFlags = O_BINARY | O_LARGEFILE;
-  if (dwModes & FX_FILEMODE_ReadOnly) {
-    nFlags |= O_RDONLY;
-    nMasks = 0;
-  } else {
-    nFlags |= O_RDWR | O_CREAT;
-    if (dwModes & FX_FILEMODE_Truncate) {
-      nFlags |= O_TRUNC;
-    }
-    nMasks = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
-  }
-}
-
-}  // namespace
-
 // static
 std::unique_ptr<FileAccessIface> FileAccessIface::Create() {
-  return pdfium::MakeUnique<CFX_FileAccess_Posix>();
+  return std::make_unique<CFX_FileAccess_Posix>();
 }
 
 CFX_FileAccess_Posix::CFX_FileAccess_Posix() : m_nFD(-1) {}
@@ -52,23 +34,16 @@
   Close();
 }
 
-bool CFX_FileAccess_Posix::Open(ByteStringView fileName, uint32_t dwMode) {
+bool CFX_FileAccess_Posix::Open(ByteStringView fileName) {
   if (m_nFD > -1)
     return false;
 
-  int32_t nFlags;
-  int32_t nMasks;
-  GetFileMode(dwMode, nFlags, nMasks);
-
   // TODO(tsepez): check usage of c_str() below.
-  m_nFD = open(fileName.unterminated_c_str(), nFlags, nMasks);
+  m_nFD =
+      open(fileName.unterminated_c_str(), O_BINARY | O_LARGEFILE | O_RDONLY);
   return m_nFD > -1;
 }
 
-bool CFX_FileAccess_Posix::Open(WideStringView fileName, uint32_t dwMode) {
-  return Open(FX_UTF8Encode(fileName).AsStringView(), dwMode);
-}
-
 void CFX_FileAccess_Posix::Close() {
   if (m_nFD < 0) {
     return;
@@ -83,7 +58,7 @@
   struct stat s;
   memset(&s, 0, sizeof(s));
   fstat(m_nFD, &s);
-  return s.st_size;
+  return pdfium::base::checked_cast<FX_FILESIZE>(s.st_size);
 }
 FX_FILESIZE CFX_FileAccess_Posix::GetPosition() const {
   if (m_nFD < 0) {
diff --git a/core/fxcrt/cfx_fileaccess_posix.h b/core/fxcrt/cfx_fileaccess_posix.h
index 39fdb23..3e25f28 100644
--- a/core/fxcrt/cfx_fileaccess_posix.h
+++ b/core/fxcrt/cfx_fileaccess_posix.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,14 @@
 #ifndef CORE_FXCRT_CFX_FILEACCESS_POSIX_H_
 #define CORE_FXCRT_CFX_FILEACCESS_POSIX_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include "build/build_config.h"
 #include "core/fxcrt/fileaccess_iface.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/fx_types.h"
 
-#if _FX_PLATFORM_ != _FX_PLATFORM_LINUX_ && !defined(OS_MACOSX) && \
-    !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_FUCHSIA)
 #error "Included on the wrong platform"
 #endif
 
@@ -22,8 +24,7 @@
   ~CFX_FileAccess_Posix() override;
 
   // FileAccessIface:
-  bool Open(ByteStringView fileName, uint32_t dwMode) override;
-  bool Open(WideStringView fileName, uint32_t dwMode) override;
+  bool Open(ByteStringView fileName) override;
   void Close() override;
   FX_FILESIZE GetSize() const override;
   FX_FILESIZE GetPosition() const override;
@@ -38,7 +39,7 @@
   bool Truncate(FX_FILESIZE szFile) override;
 
  private:
-  int32_t m_nFD;
+  int32_t m_nFD = -1;
 };
 
 #endif  // CORE_FXCRT_CFX_FILEACCESS_POSIX_H_
diff --git a/core/fxcrt/cfx_fileaccess_windows.cpp b/core/fxcrt/cfx_fileaccess_windows.cpp
index d1e5ff1..bef9f58 100644
--- a/core/fxcrt/cfx_fileaccess_windows.cpp
+++ b/core/fxcrt/cfx_fileaccess_windows.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,60 +10,26 @@
 
 #include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/fx_string.h"
-#include "third_party/base/ptr_util.h"
-
-namespace {
-
-void GetFileMode(uint32_t dwMode,
-                 uint32_t& dwAccess,
-                 uint32_t& dwShare,
-                 uint32_t& dwCreation) {
-  dwAccess = GENERIC_READ;
-  dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
-  if (!(dwMode & FX_FILEMODE_ReadOnly)) {
-    dwAccess |= GENERIC_WRITE;
-    dwCreation = (dwMode & FX_FILEMODE_Truncate) ? CREATE_ALWAYS : OPEN_ALWAYS;
-  } else {
-    dwCreation = OPEN_EXISTING;
-  }
-}
-
-}  // namespace
 
 // static
 std::unique_ptr<FileAccessIface> FileAccessIface::Create() {
-  return pdfium::MakeUnique<CFX_FileAccess_Windows>();
+  return std::make_unique<CFX_FileAccess_Windows>();
 }
 
-CFX_FileAccess_Windows::CFX_FileAccess_Windows() : m_hFile(nullptr) {}
+CFX_FileAccess_Windows::CFX_FileAccess_Windows() = default;
 
 CFX_FileAccess_Windows::~CFX_FileAccess_Windows() {
   Close();
 }
 
-bool CFX_FileAccess_Windows::Open(ByteStringView fileName, uint32_t dwMode) {
+bool CFX_FileAccess_Windows::Open(ByteStringView fileName) {
   if (m_hFile)
     return false;
 
-  uint32_t dwAccess, dwShare, dwCreation;
-  GetFileMode(dwMode, dwAccess, dwShare, dwCreation);
-  m_hFile = ::CreateFileA(fileName.unterminated_c_str(), dwAccess, dwShare,
-                          nullptr, dwCreation, FILE_ATTRIBUTE_NORMAL, nullptr);
-  if (m_hFile == INVALID_HANDLE_VALUE)
-    m_hFile = nullptr;
-
-  return !!m_hFile;
-}
-
-bool CFX_FileAccess_Windows::Open(WideStringView fileName, uint32_t dwMode) {
-  if (m_hFile)
-    return false;
-
-  uint32_t dwAccess, dwShare, dwCreation;
-  GetFileMode(dwMode, dwAccess, dwShare, dwCreation);
-  m_hFile =
-      ::CreateFileW((LPCWSTR)fileName.unterminated_c_str(), dwAccess, dwShare,
-                    nullptr, dwCreation, FILE_ATTRIBUTE_NORMAL, nullptr);
+  WideString wname = FX_UTF8Decode(fileName);
+  m_hFile = ::CreateFileW(wname.c_str(), GENERIC_READ,
+                          FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
+                          OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
   if (m_hFile == INVALID_HANDLE_VALUE)
     m_hFile = nullptr;
 
diff --git a/core/fxcrt/cfx_fileaccess_windows.h b/core/fxcrt/cfx_fileaccess_windows.h
index a95466f..89c79e9 100644
--- a/core/fxcrt/cfx_fileaccess_windows.h
+++ b/core/fxcrt/cfx_fileaccess_windows.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,15 @@
 #ifndef CORE_FXCRT_CFX_FILEACCESS_WINDOWS_H_
 #define CORE_FXCRT_CFX_FILEACCESS_WINDOWS_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include "build/build_config.h"
 #include "core/fxcrt/fileaccess_iface.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/fx_types.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
 #error "Included on the wrong platform"
 #endif
 
@@ -21,8 +25,7 @@
   ~CFX_FileAccess_Windows() override;
 
   // FileAccessIface
-  bool Open(ByteStringView fileName, uint32_t dwMode) override;
-  bool Open(WideStringView fileName, uint32_t dwMode) override;
+  bool Open(ByteStringView fileName) override;
   void Close() override;
   FX_FILESIZE GetSize() const override;
   FX_FILESIZE GetPosition() const override;
@@ -37,7 +40,7 @@
   bool Truncate(FX_FILESIZE szFile) override;
 
  private:
-  void* m_hFile;
+  UNOWNED_PTR_EXCLUSION void* m_hFile = nullptr;  // void type incompatible.
 };
 
 #endif  // CORE_FXCRT_CFX_FILEACCESS_WINDOWS_H_
diff --git a/core/fxcrt/cfx_fixedbufgrow.h b/core/fxcrt/cfx_fixedbufgrow.h
deleted file mode 100644
index daeac13..0000000
--- a/core/fxcrt/cfx_fixedbufgrow.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_CFX_FIXEDBUFGROW_H_
-#define CORE_FXCRT_CFX_FIXEDBUFGROW_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_memory_wrappers.h"
-
-template <class DataType, size_t FixedSize>
-class CFX_FixedBufGrow {
- public:
-  explicit CFX_FixedBufGrow(size_t data_size) {
-    if (data_size > FixedSize) {
-      m_pGrowData.reset(FX_Alloc(DataType, data_size));
-      return;
-    }
-    memset(m_FixedData, 0, sizeof(DataType) * FixedSize);
-  }
-  operator DataType*() { return m_pGrowData ? m_pGrowData.get() : m_FixedData; }
-
- private:
-  std::unique_ptr<DataType, FxFreeDeleter> m_pGrowData;
-  DataType m_FixedData[FixedSize];
-};
-
-#endif  // CORE_FXCRT_CFX_FIXEDBUFGROW_H_
diff --git a/core/fxcrt/cfx_memorystream.cpp b/core/fxcrt/cfx_memorystream.cpp
index b1f131e..32bb360 100644
--- a/core/fxcrt/cfx_memorystream.cpp
+++ b/core/fxcrt/cfx_memorystream.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,13 +10,9 @@
 #include <utility>
 
 #include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/span_util.h"
 
-CFX_MemoryStream::CFX_MemoryStream() : m_nTotalSize(0), m_nCurSize(0) {}
-
-CFX_MemoryStream::CFX_MemoryStream(
-    std::unique_ptr<uint8_t, FxFreeDeleter> pBuffer,
-    size_t nSize)
-    : m_data(std::move(pBuffer)), m_nTotalSize(nSize), m_nCurSize(nSize) {}
+CFX_MemoryStream::CFX_MemoryStream() = default;
 
 CFX_MemoryStream::~CFX_MemoryStream() = default;
 
@@ -36,48 +32,56 @@
   return true;
 }
 
-bool CFX_MemoryStream::ReadBlockAtOffset(void* buffer,
-                                         FX_FILESIZE offset,
-                                         size_t size) {
-  if (!buffer || offset < 0 || !size)
+pdfium::span<const uint8_t> CFX_MemoryStream::GetSpan() const {
+  return pdfium::make_span(m_data).first(m_nCurSize);
+}
+
+bool CFX_MemoryStream::ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                                         FX_FILESIZE offset) {
+  if (buffer.empty() || offset < 0)
     return false;
 
-  FX_SAFE_SIZE_T newPos = size;
-  newPos += offset;
-  if (!newPos.IsValid() || newPos.ValueOrDefault(0) == 0 ||
-      newPos.ValueOrDie() > m_nCurSize) {
+  FX_SAFE_SIZE_T new_pos = buffer.size();
+  new_pos += offset;
+  if (!new_pos.IsValid() || new_pos.ValueOrDefault(0) == 0 ||
+      new_pos.ValueOrDie() > m_nCurSize) {
     return false;
   }
 
-  m_nCurPos = newPos.ValueOrDie();
-  memcpy(buffer, &GetBuffer()[offset], size);
+  m_nCurPos = new_pos.ValueOrDie();
+  // Safe to cast `offset` because it was used to calculate `new_pos` above, and
+  // `new_pos` is valid.
+  fxcrt::spancpy(buffer,
+                 GetSpan().subspan(static_cast<size_t>(offset), buffer.size()));
   return true;
 }
 
-size_t CFX_MemoryStream::ReadBlock(void* buffer, size_t size) {
+size_t CFX_MemoryStream::ReadBlock(pdfium::span<uint8_t> buffer) {
   if (m_nCurPos >= m_nCurSize)
     return 0;
 
-  size_t nRead = std::min(size, m_nCurSize - m_nCurPos);
-  if (!ReadBlockAtOffset(buffer, static_cast<int32_t>(m_nCurPos), nRead))
+  size_t nRead = std::min(buffer.size(), m_nCurSize - m_nCurPos);
+  if (!ReadBlockAtOffset(buffer.first(nRead), static_cast<int32_t>(m_nCurPos)))
     return 0;
 
   return nRead;
 }
 
-bool CFX_MemoryStream::WriteBlockAtOffset(const void* buffer,
-                                          FX_FILESIZE offset,
-                                          size_t size) {
-  if (!buffer || offset < 0 || !size)
+bool CFX_MemoryStream::WriteBlockAtOffset(pdfium::span<const uint8_t> buffer,
+                                          FX_FILESIZE offset) {
+  if (offset < 0)
     return false;
 
-  FX_SAFE_SIZE_T safe_new_pos = size;
+  if (buffer.empty())
+    return true;
+
+  FX_SAFE_SIZE_T safe_new_pos = buffer.size();
   safe_new_pos += offset;
   if (!safe_new_pos.IsValid())
     return false;
 
   size_t new_pos = safe_new_pos.ValueOrDie();
-  if (new_pos > m_nTotalSize) {
+  if (new_pos > m_data.size()) {
     static constexpr size_t kBlockSize = 64 * 1024;
     FX_SAFE_SIZE_T new_size = new_pos;
     new_size *= 2;
@@ -87,15 +91,14 @@
     if (!new_size.IsValid())
       return false;
 
-    m_nTotalSize = new_size.ValueOrDie();
-    if (m_data)
-      m_data.reset(FX_Realloc(uint8_t, m_data.release(), m_nTotalSize));
-    else
-      m_data.reset(FX_Alloc(uint8_t, m_nTotalSize));
+    m_data.resize(new_size.ValueOrDie());
   }
   m_nCurPos = new_pos;
 
-  memcpy(&m_data.get()[offset], buffer, size);
+  // Safe to cast `offset` because it was used to calculate `safe_new_pos`
+  // above, and `safe_new_pos` is valid.
+  fxcrt::spancpy(pdfium::make_span(m_data).subspan(static_cast<size_t>(offset)),
+                 buffer);
   m_nCurSize = std::max(m_nCurSize, m_nCurPos);
 
   return true;
diff --git a/core/fxcrt/cfx_memorystream.h b/core/fxcrt/cfx_memorystream.h
index ce6fd64..b2620fc 100644
--- a/core/fxcrt/cfx_memorystream.h
+++ b/core/fxcrt/cfx_memorystream.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,41 +7,34 @@
 #ifndef CORE_FXCRT_CFX_MEMORYSTREAM_H_
 #define CORE_FXCRT_CFX_MEMORYSTREAM_H_
 
-#include <memory>
-
-#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_MemoryStream final : public IFX_SeekableStream {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // IFX_SeekableStream
   FX_FILESIZE GetSize() override;
   FX_FILESIZE GetPosition() override;
   bool IsEOF() override;
-  bool ReadBlockAtOffset(void* buffer,
-                         FX_FILESIZE offset,
-                         size_t size) override;
-  size_t ReadBlock(void* buffer, size_t size) override;
-  bool WriteBlockAtOffset(const void* buffer,
-                          FX_FILESIZE offset,
-                          size_t size) override;
+  size_t ReadBlock(pdfium::span<uint8_t> buffer) override;
+  bool ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                         FX_FILESIZE offset) override;
+  bool WriteBlockAtOffset(pdfium::span<const uint8_t> buffer,
+                          FX_FILESIZE offset) override;
   bool Flush() override;
 
-  const uint8_t* GetBuffer() const { return m_data.get(); }
+  pdfium::span<const uint8_t> GetSpan() const;
 
  private:
   CFX_MemoryStream();
-  CFX_MemoryStream(std::unique_ptr<uint8_t, FxFreeDeleter> pBuffer,
-                   size_t nSize);
   ~CFX_MemoryStream() override;
 
-  std::unique_ptr<uint8_t, FxFreeDeleter> m_data;
-  size_t m_nTotalSize;
-  size_t m_nCurSize;
+  DataVector<uint8_t> m_data;
+  size_t m_nCurSize = 0;
   size_t m_nCurPos = 0;
 };
 
diff --git a/core/fxcrt/cfx_memorystream_unittest.cpp b/core/fxcrt/cfx_memorystream_unittest.cpp
index 8339e8e..88b0e25 100644
--- a/core/fxcrt/cfx_memorystream_unittest.cpp
+++ b/core/fxcrt/cfx_memorystream_unittest.cpp
@@ -1,10 +1,11 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxcrt/cfx_memorystream.h"
 
 #include "core/fxcrt/retain_ptr.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -14,18 +15,45 @@
 
 }  // namespace
 
-TEST(CFX_MemoryStreamTest, SparseBlockWrites) {
+TEST(CFXMemoryStreamTest, SparseBlockWrites) {
   auto stream = pdfium::MakeRetain<CFX_MemoryStream>();
-  for (FX_FILESIZE offset = 0; offset <= 200000; offset += 100000)
-    stream->WriteBlockAtOffset(kSomeText, offset, kSomeTextLen);
-
+  for (FX_FILESIZE offset = 0; offset <= 200000; offset += 100000) {
+    stream->WriteBlockAtOffset(
+        {reinterpret_cast<const uint8_t*>(kSomeText), kSomeTextLen}, offset);
+  }
   EXPECT_EQ(200000 + kSomeTextLen, static_cast<size_t>(stream->GetSize()));
 }
 
-TEST(CFX_MemoryStreamTest, OverlappingBlockWrites) {
+TEST(CFXMemoryStreamTest, OverlappingBlockWrites) {
   auto stream = pdfium::MakeRetain<CFX_MemoryStream>();
-  for (FX_FILESIZE offset = 0; offset <= 100; ++offset)
-    stream->WriteBlockAtOffset(kSomeText, offset, kSomeTextLen);
-
+  for (FX_FILESIZE offset = 0; offset <= 100; ++offset) {
+    stream->WriteBlockAtOffset(
+        {reinterpret_cast<const uint8_t*>(kSomeText), kSomeTextLen}, offset);
+  }
   EXPECT_EQ(100 + kSomeTextLen, static_cast<size_t>(stream->GetSize()));
 }
+
+TEST(CFXMemoryStreamTest, ReadWriteBlockAtOffset) {
+  auto stream = pdfium::MakeRetain<CFX_MemoryStream>();
+  const uint8_t kData1[] = {'a', 'b', 'c'};
+  ASSERT_TRUE(stream->WriteBlock(kData1));
+  ASSERT_THAT(stream->GetSpan(), testing::ElementsAre('a', 'b', 'c'));
+
+  ASSERT_TRUE(stream->WriteBlockAtOffset(kData1, 5));
+  ASSERT_THAT(stream->GetSpan(),
+              testing::ElementsAre('a', 'b', 'c', '\0', '\0', 'a', 'b', 'c'));
+
+  uint8_t buffer[4];
+  ASSERT_TRUE(stream->ReadBlockAtOffset(buffer, 2));
+  ASSERT_THAT(buffer, testing::ElementsAre('c', '\0', '\0', 'a'));
+}
+
+TEST(CFXMemoryStreamTest, WriteZeroBytes) {
+  auto stream = pdfium::MakeRetain<CFX_MemoryStream>();
+  const uint8_t kData1[] = {'a', 'b', 'c'};
+  ASSERT_TRUE(stream->WriteBlock(kData1));
+  ASSERT_THAT(stream->GetSpan(), testing::ElementsAre('a', 'b', 'c'));
+
+  ASSERT_TRUE(stream->WriteBlock({}));
+  ASSERT_THAT(stream->GetSpan(), testing::ElementsAre('a', 'b', 'c'));
+}
diff --git a/core/fxcrt/cfx_read_only_span_stream.cpp b/core/fxcrt/cfx_read_only_span_stream.cpp
new file mode 100644
index 0000000..ca78fa8
--- /dev/null
+++ b/core/fxcrt/cfx_read_only_span_stream.cpp
@@ -0,0 +1,36 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/cfx_read_only_span_stream.h"
+
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/span_util.h"
+#include "third_party/base/numerics/safe_conversions.h"
+
+CFX_ReadOnlySpanStream::CFX_ReadOnlySpanStream(pdfium::span<const uint8_t> span)
+    : span_(span) {}
+
+CFX_ReadOnlySpanStream::~CFX_ReadOnlySpanStream() = default;
+
+FX_FILESIZE CFX_ReadOnlySpanStream::GetSize() {
+  return pdfium::base::checked_cast<FX_FILESIZE>(span_.size());
+}
+
+bool CFX_ReadOnlySpanStream::ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                                               FX_FILESIZE offset) {
+  if (buffer.empty() || offset < 0)
+    return false;
+
+  FX_SAFE_SIZE_T pos = buffer.size();
+  pos += offset;
+  if (!pos.IsValid() || pos.ValueOrDie() > span_.size())
+    return false;
+
+  fxcrt::spancpy(
+      buffer,
+      span_.subspan(pdfium::base::checked_cast<size_t>(offset), buffer.size()));
+  return true;
+}
diff --git a/core/fxcrt/cfx_read_only_span_stream.h b/core/fxcrt/cfx_read_only_span_stream.h
new file mode 100644
index 0000000..d476640
--- /dev/null
+++ b/core/fxcrt/cfx_read_only_span_stream.h
@@ -0,0 +1,30 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCRT_CFX_READ_ONLY_SPAN_STREAM_H_
+#define CORE_FXCRT_CFX_READ_ONLY_SPAN_STREAM_H_
+
+#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "third_party/base/containers/span.h"
+
+class CFX_ReadOnlySpanStream final : public IFX_SeekableReadStream {
+ public:
+  CONSTRUCT_VIA_MAKE_RETAIN;
+
+  // IFX_SeekableReadStream:
+  FX_FILESIZE GetSize() override;
+  bool ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                         FX_FILESIZE offset) override;
+
+ private:
+  explicit CFX_ReadOnlySpanStream(pdfium::span<const uint8_t> span);
+  ~CFX_ReadOnlySpanStream() override;
+
+  const pdfium::span<const uint8_t> span_;
+};
+
+#endif  // CORE_FXCRT_CFX_READ_ONLY_SPAN_STREAM_H_
diff --git a/core/fxcrt/cfx_read_only_string_stream.cpp b/core/fxcrt/cfx_read_only_string_stream.cpp
new file mode 100644
index 0000000..364330b
--- /dev/null
+++ b/core/fxcrt/cfx_read_only_string_stream.cpp
@@ -0,0 +1,25 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/cfx_read_only_string_stream.h"
+
+#include <utility>
+
+#include "core/fxcrt/cfx_read_only_span_stream.h"
+#include "third_party/base/containers/span.h"
+
+CFX_ReadOnlyStringStream::CFX_ReadOnlyStringStream(ByteString data)
+    : data_(std::move(data)),
+      stream_(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(data_.raw_span())) {}
+
+CFX_ReadOnlyStringStream::~CFX_ReadOnlyStringStream() = default;
+
+FX_FILESIZE CFX_ReadOnlyStringStream::GetSize() {
+  return stream_->GetSize();
+}
+
+bool CFX_ReadOnlyStringStream::ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                                                 FX_FILESIZE offset) {
+  return stream_->ReadBlockAtOffset(buffer, offset);
+}
diff --git a/core/fxcrt/cfx_read_only_string_stream.h b/core/fxcrt/cfx_read_only_string_stream.h
new file mode 100644
index 0000000..fe4b26f
--- /dev/null
+++ b/core/fxcrt/cfx_read_only_string_stream.h
@@ -0,0 +1,31 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_CFX_READ_ONLY_STRING_STREAM_H_
+#define CORE_FXCRT_CFX_READ_ONLY_STRING_STREAM_H_
+
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/retain_ptr.h"
+
+class CFX_ReadOnlySpanStream;
+
+class CFX_ReadOnlyStringStream final : public IFX_SeekableReadStream {
+ public:
+  CONSTRUCT_VIA_MAKE_RETAIN;
+
+  // IFX_SeekableReadStream:
+  FX_FILESIZE GetSize() override;
+  bool ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                         FX_FILESIZE offset) override;
+
+ private:
+  explicit CFX_ReadOnlyStringStream(ByteString data);
+  ~CFX_ReadOnlyStringStream() override;
+
+  const ByteString data_;
+  const RetainPtr<CFX_ReadOnlySpanStream> stream_;
+};
+
+#endif  // CORE_FXCRT_CFX_READ_ONLY_STRING_STREAM_H_
diff --git a/core/fxcrt/cfx_read_only_vector_stream.cpp b/core/fxcrt/cfx_read_only_vector_stream.cpp
new file mode 100644
index 0000000..97f0dcb
--- /dev/null
+++ b/core/fxcrt/cfx_read_only_vector_stream.cpp
@@ -0,0 +1,30 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/cfx_read_only_vector_stream.h"
+
+#include <utility>
+
+#include "core/fxcrt/cfx_read_only_span_stream.h"
+#include "third_party/base/containers/span.h"
+
+CFX_ReadOnlyVectorStream::CFX_ReadOnlyVectorStream(DataVector<uint8_t> data)
+    : data_(std::move(data)),
+      stream_(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(data_)) {}
+
+CFX_ReadOnlyVectorStream::CFX_ReadOnlyVectorStream(
+    FixedUninitDataVector<uint8_t> data)
+    : fixed_data_(std::move(data)),
+      stream_(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(fixed_data_)) {}
+
+CFX_ReadOnlyVectorStream::~CFX_ReadOnlyVectorStream() = default;
+
+FX_FILESIZE CFX_ReadOnlyVectorStream::GetSize() {
+  return stream_->GetSize();
+}
+
+bool CFX_ReadOnlyVectorStream::ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                                                 FX_FILESIZE offset) {
+  return stream_->ReadBlockAtOffset(buffer, offset);
+}
diff --git a/core/fxcrt/cfx_read_only_vector_stream.h b/core/fxcrt/cfx_read_only_vector_stream.h
new file mode 100644
index 0000000..7e9e1e9
--- /dev/null
+++ b/core/fxcrt/cfx_read_only_vector_stream.h
@@ -0,0 +1,37 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_CFX_READ_ONLY_VECTOR_STREAM_H_
+#define CORE_FXCRT_CFX_READ_ONLY_VECTOR_STREAM_H_
+
+#include <stdint.h>
+
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fixed_uninit_data_vector.h"
+#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/retain_ptr.h"
+
+class CFX_ReadOnlySpanStream;
+
+class CFX_ReadOnlyVectorStream final : public IFX_SeekableReadStream {
+ public:
+  CONSTRUCT_VIA_MAKE_RETAIN;
+
+  // IFX_SeekableReadStream:
+  FX_FILESIZE GetSize() override;
+  bool ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                         FX_FILESIZE offset) override;
+
+ private:
+  explicit CFX_ReadOnlyVectorStream(DataVector<uint8_t> data);
+  explicit CFX_ReadOnlyVectorStream(FixedUninitDataVector<uint8_t> data);
+  ~CFX_ReadOnlyVectorStream() override;
+
+  const DataVector<uint8_t> data_;
+  const FixedUninitDataVector<uint8_t> fixed_data_;
+  // Spans over either `data_` or `fixed_data_`.
+  const RetainPtr<CFX_ReadOnlySpanStream> stream_;
+};
+
+#endif  // CORE_FXCRT_CFX_READ_ONLY_VECTOR_STREAM_H_
diff --git a/core/fxcrt/cfx_readonlymemorystream.cpp b/core/fxcrt/cfx_readonlymemorystream.cpp
deleted file mode 100644
index 807788a..0000000
--- a/core/fxcrt/cfx_readonlymemorystream.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/cfx_readonlymemorystream.h"
-
-#include <utility>
-
-#include "core/fxcrt/fx_safe_types.h"
-
-CFX_ReadOnlyMemoryStream::CFX_ReadOnlyMemoryStream(
-    std::unique_ptr<uint8_t, FxFreeDeleter> data,
-    size_t size)
-    : m_data(std::move(data)), m_span(m_data.get(), size) {}
-
-CFX_ReadOnlyMemoryStream::CFX_ReadOnlyMemoryStream(
-    pdfium::span<const uint8_t> span)
-    : m_span(span) {}
-
-CFX_ReadOnlyMemoryStream::~CFX_ReadOnlyMemoryStream() = default;
-
-FX_FILESIZE CFX_ReadOnlyMemoryStream::GetSize() {
-  return pdfium::base::checked_cast<FX_FILESIZE>(m_span.size());
-}
-
-bool CFX_ReadOnlyMemoryStream::ReadBlockAtOffset(void* buffer,
-                                                 FX_FILESIZE offset,
-                                                 size_t size) {
-  if (!buffer || offset < 0 || size == 0)
-    return false;
-
-  FX_SAFE_SIZE_T pos = size;
-  pos += offset;
-  if (!pos.IsValid() || pos.ValueOrDie() > m_span.size())
-    return false;
-
-  auto copy_span = m_span.subspan(offset, size);
-  memcpy(buffer, copy_span.data(), copy_span.size());
-  return true;
-}
diff --git a/core/fxcrt/cfx_readonlymemorystream.h b/core/fxcrt/cfx_readonlymemorystream.h
deleted file mode 100644
index ea2b849..0000000
--- a/core/fxcrt/cfx_readonlymemorystream.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_CFX_READONLYMEMORYSTREAM_H_
-#define CORE_FXCRT_CFX_READONLYMEMORYSTREAM_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxcrt/fx_stream.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/span.h"
-
-class CFX_ReadOnlyMemoryStream final : public IFX_SeekableReadStream {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  // IFX_SeekableReadStream:
-  FX_FILESIZE GetSize() override;
-  bool ReadBlockAtOffset(void* buffer,
-                         FX_FILESIZE offset,
-                         size_t size) override;
-
- private:
-  CFX_ReadOnlyMemoryStream(std::unique_ptr<uint8_t, FxFreeDeleter> data,
-                           size_t size);
-  explicit CFX_ReadOnlyMemoryStream(pdfium::span<const uint8_t> span);
-  ~CFX_ReadOnlyMemoryStream() override;
-
-  std::unique_ptr<uint8_t, FxFreeDeleter> m_data;
-  const pdfium::span<const uint8_t> m_span;
-};
-
-#endif  // CORE_FXCRT_CFX_READONLYMEMORYSTREAM_H_
diff --git a/core/fxcrt/cfx_seekablestreamproxy.cpp b/core/fxcrt/cfx_seekablestreamproxy.cpp
index 67b304f..6fb7eac 100644
--- a/core/fxcrt/cfx_seekablestreamproxy.cpp
+++ b/core/fxcrt/cfx_seekablestreamproxy.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,48 +6,38 @@
 
 #include "core/fxcrt/cfx_seekablestreamproxy.h"
 
-#if defined(OS_WIN)
-#include <io.h>
-#endif
+#include <stdint.h>
 
 #include <algorithm>
 #include <limits>
-#include <memory>
 #include <utility>
-#include <vector>
 
 #include "build/build_config.h"
-#include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 
 namespace {
 
 // Returns {src bytes consumed, dst chars produced}.
 // Invalid sequences are silently not output.
-std::pair<size_t, size_t> UTF8Decode(const char* pSrc,
-                                     size_t srcLen,
-                                     wchar_t* pDst,
-                                     size_t dstLen) {
-  ASSERT(pDst);
-  ASSERT(dstLen > 0);
-
-  if (srcLen < 1)
-    return {0, 0};
+std::pair<size_t, size_t> UTF8Decode(pdfium::span<const uint8_t> pSrc,
+                                     pdfium::span<wchar_t> pDst) {
+  DCHECK(!pDst.empty());
 
   uint32_t dwCode = 0;
   int32_t iPending = 0;
   size_t iSrcNum = 0;
   size_t iDstNum = 0;
-  for (size_t iIndex = 0; iIndex < srcLen && iDstNum < dstLen; ++iIndex) {
+  for (size_t iIndex = 0; iIndex < pSrc.size() && iDstNum < pDst.size();
+       ++iIndex) {
     ++iSrcNum;
-    uint8_t byte = static_cast<uint8_t>(*(pSrc + iIndex));
+    uint8_t byte = pSrc[iIndex];
     if (byte < 0x80) {
       iPending = 0;
-      ++iDstNum;
-      *pDst++ = byte;
+      pDst[iDstNum++] = byte;
     } else if (byte < 0xc0) {
       if (iPending < 1)
         continue;
@@ -55,10 +45,8 @@
       dwCode = dwCode << 6;
       dwCode |= (byte & 0x3f);
       --iPending;
-      if (iPending == 0) {
-        ++iDstNum;
-        *pDst++ = dwCode;
-      }
+      if (iPending == 0)
+        pDst[iDstNum++] = dwCode;
     } else if (byte < 0xe0) {
       iPending = 1;
       dwCode = (byte & 0x1f);
@@ -83,8 +71,8 @@
 static_assert(sizeof(wchar_t) > 2, "wchar_t is too small");
 
 void UTF16ToWChar(void* pBuffer, size_t iLength) {
-  ASSERT(pBuffer);
-  ASSERT(iLength > 0);
+  DCHECK(pBuffer);
+  DCHECK_GT(iLength, 0u);
 
   uint16_t* pSrc = static_cast<uint16_t*>(pBuffer);
   wchar_t* pDst = static_cast<wchar_t*>(pBuffer);
@@ -112,11 +100,8 @@
 
 CFX_SeekableStreamProxy::CFX_SeekableStreamProxy(
     const RetainPtr<IFX_SeekableReadStream>& stream)
-    : m_wCodePage(FX_CODEPAGE_DefANSI),
-      m_wBOMLength(0),
-      m_iPosition(0),
-      m_pStream(stream) {
-  ASSERT(m_pStream);
+    : m_pStream(stream) {
+  DCHECK(m_pStream);
 
   Seek(From::Begin, 0);
 
@@ -126,18 +111,18 @@
   bom &= BOM_UTF8_MASK;
   if (bom == BOM_UTF8) {
     m_wBOMLength = 3;
-    m_wCodePage = FX_CODEPAGE_UTF8;
+    m_wCodePage = FX_CodePage::kUTF8;
   } else {
     bom &= BOM_UTF16_MASK;
     if (bom == BOM_UTF16_BE) {
       m_wBOMLength = 2;
-      m_wCodePage = FX_CODEPAGE_UTF16BE;
+      m_wCodePage = FX_CodePage::kUTF16BE;
     } else if (bom == BOM_UTF16_LE) {
       m_wBOMLength = 2;
-      m_wCodePage = FX_CODEPAGE_UTF16LE;
+      m_wCodePage = FX_CodePage::kUTF16LE;
     } else {
       m_wBOMLength = 0;
-      m_wCodePage = FXSYS_GetACP();
+      m_wCodePage = FX_GetACP();
     }
   }
 
@@ -170,26 +155,25 @@
           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(uint16_t wCodePage) {
+void CFX_SeekableStreamProxy::SetCodePage(FX_CodePage wCodePage) {
   if (m_wBOMLength > 0)
     return;
   m_wCodePage = wCodePage;
 }
 
 size_t CFX_SeekableStreamProxy::ReadData(uint8_t* pBuffer, size_t iBufferSize) {
-  ASSERT(pBuffer);
-  ASSERT(iBufferSize > 0);
+  DCHECK(pBuffer);
+  DCHECK(iBufferSize > 0);
 
   iBufferSize =
       std::min(iBufferSize, static_cast<size_t>(GetSize() - m_iPosition));
   if (iBufferSize <= 0)
     return 0;
 
-  if (!m_pStream->ReadBlockAtOffset(pBuffer, m_iPosition, iBufferSize))
+  if (!m_pStream->ReadBlockAtOffset({pBuffer, iBufferSize}, m_iPosition))
     return 0;
 
   FX_SAFE_FILESIZE new_pos = m_iPosition;
@@ -202,38 +186,33 @@
   if (!pStr || size == 0)
     return 0;
 
-  if (m_wCodePage == FX_CODEPAGE_UTF16LE ||
-      m_wCodePage == FX_CODEPAGE_UTF16BE) {
+  if (m_wCodePage == FX_CodePage::kUTF16LE ||
+      m_wCodePage == FX_CodePage::kUTF16BE) {
     size_t iBytes = size * 2;
     size_t iLen = ReadData(reinterpret_cast<uint8_t*>(pStr), iBytes);
     size = iLen / 2;
-    if (m_wCodePage == FX_CODEPAGE_UTF16BE)
+    if (m_wCodePage == FX_CodePage::kUTF16BE)
       SwapByteOrder(reinterpret_cast<uint16_t*>(pStr), size);
 
 #if defined(WCHAR_T_IS_UTF32)
     if (size > 0)
       UTF16ToWChar(pStr, size);
 #endif
-  } else {
-    FX_FILESIZE pos = GetPosition();
-    size_t iBytes = std::min(size, static_cast<size_t>(GetSize() - pos));
-
-    if (iBytes > 0) {
-      std::vector<uint8_t, FxAllocAllocator<uint8_t>> buf(iBytes);
-
-      size_t iLen = ReadData(buf.data(), iBytes);
-      if (m_wCodePage != FX_CODEPAGE_UTF8)
-        return 0;
-
-      size_t iSrc = 0;
-      std::tie(iSrc, size) =
-          UTF8Decode(reinterpret_cast<const char*>(buf.data()), iLen,
-                     static_cast<wchar_t*>(pStr), size);
-      Seek(From::Current, iSrc - iLen);
-    } else {
-      size = 0;
-    }
+    return size;
   }
 
+  FX_FILESIZE pos = GetPosition();
+  size_t iBytes = std::min(size, static_cast<size_t>(GetSize() - pos));
+  if (iBytes == 0)
+    return 0;
+
+  DataVector<uint8_t> buf(iBytes);
+  size_t iLen = ReadData(buf.data(), iBytes);
+  if (m_wCodePage != FX_CodePage::kUTF8)
+    return 0;
+
+  size_t iSrc;
+  std::tie(iSrc, size) = UTF8Decode({buf.data(), iLen}, {pStr, size});
+  Seek(From::Current, iSrc - iLen);
   return size;
 }
diff --git a/core/fxcrt/cfx_seekablestreamproxy.h b/core/fxcrt/cfx_seekablestreamproxy.h
index d6ea160..563134f 100644
--- a/core/fxcrt/cfx_seekablestreamproxy.h
+++ b/core/fxcrt/cfx_seekablestreamproxy.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,17 @@
 #ifndef CORE_FXCRT_CFX_SEEKABLESTREAMPROXY_H_
 #define CORE_FXCRT_CFX_SEEKABLESTREAMPROXY_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
+#include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_stream.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/fx_types.h"
 #include "core/fxcrt/retain_ptr.h"
 
 class CFX_SeekableStreamProxy final : public Retainable {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // Unlike IFX_SeekableStreamProxy, buffers and sizes are always in terms
   // of the number of wchar_t elementss, not bytes.
@@ -22,8 +25,8 @@
   bool IsEOF();
   size_t ReadBlock(wchar_t* pStr, size_t size);
 
-  uint16_t GetCodePage() const { return m_wCodePage; }
-  void SetCodePage(uint16_t wCodePage);
+  FX_CodePage GetCodePage() const { return m_wCodePage; }
+  void SetCodePage(FX_CodePage wCodePage);
 
  private:
   enum class From {
@@ -39,10 +42,10 @@
   void Seek(From eSeek, FX_FILESIZE iOffset);
   size_t ReadData(uint8_t* pBuffer, size_t iBufferSize);
 
-  uint16_t m_wCodePage;
-  size_t m_wBOMLength;
-  FX_FILESIZE m_iPosition;
-  RetainPtr<IFX_SeekableReadStream> m_pStream;
+  FX_CodePage m_wCodePage = FX_CodePage::kDefANSI;
+  size_t m_wBOMLength = 0;
+  FX_FILESIZE m_iPosition = 0;
+  RetainPtr<IFX_SeekableReadStream> const m_pStream;
 };
 
 #endif  // CORE_FXCRT_CFX_SEEKABLESTREAMPROXY_H_
diff --git a/core/fxcrt/cfx_seekablestreamproxy_unittest.cpp b/core/fxcrt/cfx_seekablestreamproxy_unittest.cpp
index b263fa8..05cc492 100644
--- a/core/fxcrt/cfx_seekablestreamproxy_unittest.cpp
+++ b/core/fxcrt/cfx_seekablestreamproxy_unittest.cpp
@@ -1,45 +1,43 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxcrt/cfx_seekablestreamproxy.h"
 
-#include <memory>
-#include <vector>
+#include <iterator>
 
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 TEST(SeekableStreamProxyTest, NullStream) {
   auto proxy_stream = pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-      pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+      pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
           pdfium::make_span<const uint8_t>(nullptr, 0)));
 
   wchar_t buffer[16];
-  EXPECT_EQ(0u, proxy_stream->ReadBlock(buffer, FX_ArraySize(buffer)));
+  EXPECT_EQ(0u, proxy_stream->ReadBlock(buffer, std::size(buffer)));
 }
 
 TEST(SeekableStreamProxyTest, DefaultStreamBOMNotRecognized) {
   const char data[] = "abcd";
   auto proxy_stream = pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-      pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(pdfium::make_span(
+      pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(
           reinterpret_cast<const uint8_t*>(data), sizeof(data) - 1)));
 
   wchar_t buffer[16];
-  EXPECT_EQ(0u, proxy_stream->ReadBlock(buffer, FX_ArraySize(buffer)));
+  EXPECT_EQ(0u, proxy_stream->ReadBlock(buffer, std::size(buffer)));
 }
 
 TEST(SeekableStreamProxyTest, UTF8Stream) {
   const char data[] = "\xEF\xBB\xBF*\xC2\xA2*";
   auto proxy_stream = pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-      pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(pdfium::make_span(
+      pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(
           reinterpret_cast<const uint8_t*>(data), sizeof(data) - 1)));
 
   wchar_t buffer[16];
-  EXPECT_EQ(3u, proxy_stream->ReadBlock(buffer, FX_ArraySize(buffer)));
+  EXPECT_EQ(3u, proxy_stream->ReadBlock(buffer, std::size(buffer)));
   EXPECT_EQ(L'*', buffer[0]);
   EXPECT_EQ(L'\u00A2', buffer[1]);
   EXPECT_EQ(L'*', buffer[2]);
@@ -48,11 +46,11 @@
 TEST(SeekableStreamProxyTest, UTF16LEStream) {
   const char data[] = "\xFF\xFE\x41\x00\x42\x01";
   auto proxy_stream = pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-      pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(pdfium::make_span(
+      pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(
           reinterpret_cast<const uint8_t*>(data), sizeof(data) - 1)));
 
   wchar_t buffer[16];
-  EXPECT_EQ(2u, proxy_stream->ReadBlock(buffer, FX_ArraySize(buffer)));
+  EXPECT_EQ(2u, proxy_stream->ReadBlock(buffer, std::size(buffer)));
   EXPECT_EQ(L'A', buffer[0]);
   EXPECT_EQ(L'\u0142', buffer[1]);
 }
@@ -60,11 +58,11 @@
 TEST(SeekableStreamProxyTest, UTF16BEStream) {
   const char data[] = "\xFE\xFF\x00\x41\x01\x42";
   auto proxy_stream = pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-      pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(pdfium::make_span(
+      pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(
           reinterpret_cast<const uint8_t*>(data), sizeof(data) - 1)));
 
   wchar_t buffer[16];
-  EXPECT_EQ(2u, proxy_stream->ReadBlock(buffer, FX_ArraySize(buffer)));
+  EXPECT_EQ(2u, proxy_stream->ReadBlock(buffer, std::size(buffer)));
   EXPECT_EQ(L'A', buffer[0]);
   EXPECT_EQ(L'\u0142', buffer[1]);
 }
diff --git a/core/fxcrt/cfx_timer.cpp b/core/fxcrt/cfx_timer.cpp
index 6ffb625..744b1eb 100644
--- a/core/fxcrt/cfx_timer.cpp
+++ b/core/fxcrt/cfx_timer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,6 +8,7 @@
 
 #include <map>
 
+#include "third_party/base/check.h"
 #include "third_party/base/no_destructor.h"
 
 namespace {
@@ -20,21 +21,23 @@
 
 }  // namespace
 
-CFX_Timer::CFX_Timer(TimerHandlerIface* pTimerHandler,
+CFX_Timer::CFX_Timer(HandlerIface* pHandlerIface,
                      CallbackIface* pCallbackIface,
                      int32_t nInterval)
-    : m_nTimerID(pTimerHandler->SetTimer(nInterval, TimerProc)),
-      m_pTimerHandler(pTimerHandler),
-      m_pCallbackIface(pCallbackIface) {
-  ASSERT(m_pCallbackIface);
-  if (HasValidID())
-    GetPWLTimerMap()[m_nTimerID] = this;
+    : m_pHandlerIface(pHandlerIface), m_pCallbackIface(pCallbackIface) {
+  DCHECK(m_pCallbackIface);
+  if (m_pHandlerIface) {
+    m_nTimerID = m_pHandlerIface->SetTimer(nInterval, TimerProc);
+    if (HasValidID())
+      GetPWLTimerMap()[m_nTimerID] = this;
+  }
 }
 
 CFX_Timer::~CFX_Timer() {
   if (HasValidID()) {
-    m_pTimerHandler->KillTimer(m_nTimerID);
     GetPWLTimerMap().erase(m_nTimerID);
+    if (m_pHandlerIface)
+      m_pHandlerIface->KillTimer(m_nTimerID);
   }
 }
 
diff --git a/core/fxcrt/cfx_timer.h b/core/fxcrt/cfx_timer.h
index fa97dda..24ed296 100644
--- a/core/fxcrt/cfx_timer.h
+++ b/core/fxcrt/cfx_timer.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,33 +7,48 @@
 #ifndef CORE_FXCRT_CFX_TIMER_H_
 #define CORE_FXCRT_CFX_TIMER_H_
 
-#include "core/fxcrt/timerhandler_iface.h"
-#include "core/fxcrt/unowned_ptr.h"
+#include <stdint.h>
 
-class CFX_TimerHandler;
+#include "core/fxcrt/observed_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
 
 class CFX_Timer {
  public:
+  // HandlerIface is implemented by upper layers that actually perform
+  // the system-dependent actions of scheduling and triggering timers.
+  class HandlerIface : public Observable {
+   public:
+    static constexpr int32_t kInvalidTimerID = 0;
+    using TimerCallback = void (*)(int32_t idEvent);
+
+    virtual ~HandlerIface() = default;
+
+    virtual int32_t SetTimer(int32_t uElapse, TimerCallback lpTimerFunc) = 0;
+    virtual void KillTimer(int32_t nTimerID) = 0;
+  };
+
+  // CallbackIface is implemented by layers that want to perform a
+  // specific action on timer expiry.
   class CallbackIface {
    public:
     virtual ~CallbackIface() = default;
     virtual void OnTimerFired() = 0;
   };
 
-  CFX_Timer(TimerHandlerIface* pTimerHandler,
+  CFX_Timer(HandlerIface* pHandlerIface,
             CallbackIface* pCallbackIface,
             int32_t nInterval);
   ~CFX_Timer();
 
   bool HasValidID() const {
-    return m_nTimerID != TimerHandlerIface::kInvalidTimerID;
+    return m_nTimerID != HandlerIface::kInvalidTimerID;
   }
 
  private:
   static void TimerProc(int32_t idEvent);
 
-  const int32_t m_nTimerID;
-  UnownedPtr<TimerHandlerIface> const m_pTimerHandler;
+  int32_t m_nTimerID = HandlerIface::kInvalidTimerID;
+  ObservedPtr<HandlerIface> m_pHandlerIface;
   UnownedPtr<CallbackIface> const m_pCallbackIface;
 };
 
diff --git a/core/fxcrt/cfx_timer_unittest.cpp b/core/fxcrt/cfx_timer_unittest.cpp
index b95de72..32220f0 100644
--- a/core/fxcrt/cfx_timer_unittest.cpp
+++ b/core/fxcrt/cfx_timer_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,17 +6,15 @@
 
 #include <memory>
 
-#include "core/fxcrt/timerhandler_iface.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
 using testing::_;
 using testing::DoAll;
 using testing::Return;
 using testing::SaveArg;
 
-class MockTimerScheduler : public TimerHandlerIface {
+class MockTimerScheduler : public CFX_Timer::HandlerIface {
  public:
   MOCK_METHOD2(SetTimer, int(int32_t uElapse, TimerCallback lpTimerFunc));
   MOCK_METHOD1(KillTimer, void(int32_t nID));
@@ -28,8 +26,8 @@
 };
 
 TEST(CFX_Timer, ValidTimers) {
-  TimerHandlerIface::TimerCallback fn1 = nullptr;
-  TimerHandlerIface::TimerCallback fn2 = nullptr;
+  CFX_Timer::HandlerIface::TimerCallback fn1 = nullptr;
+  CFX_Timer::HandlerIface::TimerCallback fn2 = nullptr;
 
   MockTimerScheduler scheduler;
   EXPECT_CALL(scheduler, SetTimer(100, _))
@@ -45,8 +43,8 @@
   MockTimerCallback cb2;
   EXPECT_CALL(cb2, OnTimerFired()).Times(2);
 
-  auto timer1 = pdfium::MakeUnique<CFX_Timer>(&scheduler, &cb1, 100);
-  auto timer2 = pdfium::MakeUnique<CFX_Timer>(&scheduler, &cb2, 200);
+  auto timer1 = std::make_unique<CFX_Timer>(&scheduler, &cb1, 100);
+  auto timer2 = std::make_unique<CFX_Timer>(&scheduler, &cb2, 200);
   EXPECT_TRUE(timer1->HasValidID());
   EXPECT_TRUE(timer2->HasValidID());
 
@@ -59,7 +57,7 @@
 }
 
 TEST(CFX_Timer, MisbehavingEmbedder) {
-  TimerHandlerIface::TimerCallback fn1 = nullptr;
+  CFX_Timer::HandlerIface::TimerCallback fn1 = nullptr;
 
   MockTimerScheduler scheduler;
   EXPECT_CALL(scheduler, SetTimer(100, _))
@@ -70,7 +68,7 @@
   EXPECT_CALL(cb1, OnTimerFired()).Times(0);
 
   {
-    auto timer1 = pdfium::MakeUnique<CFX_Timer>(&scheduler, &cb1, 100);
+    auto timer1 = std::make_unique<CFX_Timer>(&scheduler, &cb1, 100);
     EXPECT_TRUE(timer1->HasValidID());
 
     // Fire callback with bad arguments.
diff --git a/core/fxcrt/cfx_utf8decoder.cpp b/core/fxcrt/cfx_utf8decoder.cpp
deleted file mode 100644
index 68ea30a..0000000
--- a/core/fxcrt/cfx_utf8decoder.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/cfx_utf8decoder.h"
-
-CFX_UTF8Decoder::CFX_UTF8Decoder() = default;
-
-CFX_UTF8Decoder::~CFX_UTF8Decoder() = default;
-
-void CFX_UTF8Decoder::AppendCodePoint(uint32_t ch) {
-  m_Buffer.AppendChar(static_cast<wchar_t>(ch));
-}
-
-void CFX_UTF8Decoder::Input(uint8_t byte) {
-  if (byte < 0x80) {
-    m_PendingBytes = 0;
-    m_Buffer.AppendChar(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 a236aac..0000000
--- a/core/fxcrt/cfx_utf8decoder.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_CFX_UTF8DECODER_H_
-#define CORE_FXCRT_CFX_UTF8DECODER_H_
-
-#include "core/fxcrt/cfx_widetextbuf.h"
-
-class CFX_UTF8Decoder {
- public:
-  CFX_UTF8Decoder();
-  ~CFX_UTF8Decoder();
-
-  void Input(uint8_t byte);
-  void AppendCodePoint(uint32_t ch);
-  void ClearStatus() { m_PendingBytes = 0; }
-  WideStringView GetResult() const { return m_Buffer.AsStringView(); }
-
- private:
-  int m_PendingBytes = 0;
-  uint32_t m_PendingChar = 0;
-  CFX_WideTextBuf 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 9ed149f..0000000
--- a/core/fxcrt/cfx_utf8encoder.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/cfx_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 20afe4c..0000000
--- a/core/fxcrt/cfx_utf8encoder.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_CFX_UTF8ENCODER_H_
-#define CORE_FXCRT_CFX_UTF8ENCODER_H_
-
-#include <vector>
-
-#include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxcrt/fx_string.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:
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_Buffer;
-};
-
-#endif  // CORE_FXCRT_CFX_UTF8ENCODER_H_
diff --git a/core/fxcrt/cfx_widetextbuf.cpp b/core/fxcrt/cfx_widetextbuf.cpp
deleted file mode 100644
index dea620c..0000000
--- a/core/fxcrt/cfx_widetextbuf.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/cfx_widetextbuf.h"
-
-size_t CFX_WideTextBuf::GetLength() const {
-  return m_DataSize / sizeof(wchar_t);
-}
-
-void CFX_WideTextBuf::AppendChar(wchar_t ch) {
-  ExpandBuf(sizeof(wchar_t));
-  *reinterpret_cast<wchar_t*>(m_pBuffer.get() + m_DataSize) = ch;
-  m_DataSize += sizeof(wchar_t);
-}
-
-CFX_WideTextBuf& CFX_WideTextBuf::operator<<(ByteStringView ascii) {
-  ExpandBuf(ascii.GetLength() * sizeof(wchar_t));
-  for (uint8_t ch : ascii) {
-    *reinterpret_cast<wchar_t*>(m_pBuffer.get() + m_DataSize) = ch;
-    m_DataSize += sizeof(wchar_t);
-  }
-  return *this;
-}
-
-CFX_WideTextBuf& CFX_WideTextBuf::operator<<(WideStringView str) {
-  AppendBlock(str.unterminated_c_str(), str.GetLength() * sizeof(wchar_t));
-  return *this;
-}
-
-CFX_WideTextBuf& CFX_WideTextBuf::operator<<(const WideString& str) {
-  AppendBlock(str.c_str(), str.GetLength() * sizeof(wchar_t));
-  return *this;
-}
-
-CFX_WideTextBuf& CFX_WideTextBuf::operator<<(int i) {
-  char buf[32];
-  FXSYS_itoa(i, buf, 10);
-  size_t len = strlen(buf);
-  ExpandBuf(len * sizeof(wchar_t));
-  wchar_t* str = reinterpret_cast<wchar_t*>(m_pBuffer.get() + m_DataSize);
-  for (size_t j = 0; j < len; j++) {
-    *str++ = buf[j];
-  }
-  m_DataSize += len * sizeof(wchar_t);
-  return *this;
-}
-
-CFX_WideTextBuf& CFX_WideTextBuf::operator<<(double f) {
-  char buf[32];
-  size_t len = FloatToString((float)f, buf);
-  ExpandBuf(len * sizeof(wchar_t));
-  wchar_t* str = reinterpret_cast<wchar_t*>(m_pBuffer.get() + m_DataSize);
-  for (size_t i = 0; i < len; i++) {
-    *str++ = buf[i];
-  }
-  m_DataSize += len * sizeof(wchar_t);
-  return *this;
-}
-
-CFX_WideTextBuf& CFX_WideTextBuf::operator<<(const wchar_t* lpsz) {
-  AppendBlock(lpsz, wcslen(lpsz) * sizeof(wchar_t));
-  return *this;
-}
-
-CFX_WideTextBuf& CFX_WideTextBuf::operator<<(const CFX_WideTextBuf& buf) {
-  AppendBlock(buf.m_pBuffer.get(), buf.m_DataSize);
-  return *this;
-}
diff --git a/core/fxcrt/cfx_widetextbuf.h b/core/fxcrt/cfx_widetextbuf.h
deleted file mode 100644
index 0f89197..0000000
--- a/core/fxcrt/cfx_widetextbuf.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_CFX_WIDETEXTBUF_H_
-#define CORE_FXCRT_CFX_WIDETEXTBUF_H_
-
-#include "core/fxcrt/cfx_binarybuf.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-
-class CFX_WideTextBuf final : public CFX_BinaryBuf {
- public:
-  void AppendChar(wchar_t wch);
-  size_t GetLength() const override;
-  wchar_t* GetBuffer() const {
-    return reinterpret_cast<wchar_t*>(m_pBuffer.get());
-  }
-
-  WideStringView AsStringView() const {
-    return WideStringView(reinterpret_cast<const wchar_t*>(m_pBuffer.get()),
-                          m_DataSize / sizeof(wchar_t));
-  }
-  WideString MakeString() const {
-    return WideString(reinterpret_cast<const wchar_t*>(m_pBuffer.get()),
-                      m_DataSize / sizeof(wchar_t));
-  }
-
-  void Delete(int start_index, int count) {
-    CFX_BinaryBuf::Delete(start_index * sizeof(wchar_t),
-                          count * sizeof(wchar_t));
-  }
-
-  CFX_WideTextBuf& operator<<(int i);
-  CFX_WideTextBuf& operator<<(double f);
-  CFX_WideTextBuf& operator<<(ByteStringView ascii);
-  CFX_WideTextBuf& operator<<(const wchar_t* lpsz);
-  CFX_WideTextBuf& operator<<(WideStringView str);
-  CFX_WideTextBuf& operator<<(const WideString& str);
-  CFX_WideTextBuf& operator<<(const CFX_WideTextBuf& buf);
-};
-
-#endif  // CORE_FXCRT_CFX_WIDETEXTBUF_H_
diff --git a/core/fxcrt/cfx_widetextbuf_unittest.cpp b/core/fxcrt/cfx_widetextbuf_unittest.cpp
deleted file mode 100644
index ddca23f..0000000
--- a/core/fxcrt/cfx_widetextbuf_unittest.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "core/fxcrt/cfx_widetextbuf.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace fxcrt {
-
-TEST(WideTextBuf, EmptyBuf) {
-  CFX_WideTextBuf wtb;
-  EXPECT_EQ(nullptr, wtb.GetBuffer());
-  EXPECT_TRUE(wtb.AsStringView().IsEmpty());
-  EXPECT_TRUE(wtb.MakeString().IsEmpty());
-}
-
-TEST(WideTextBuf, OperatorLtLt) {
-  CFX_WideTextBuf wtb;
-  wtb << 42 << 3.14 << "clams" << L"\u208c\u208e";
-  EXPECT_TRUE(wtb.MakeString() == L"423.14clams\u208c\u208e");
-}
-
-TEST(WideTextBuf, Deletion) {
-  CFX_WideTextBuf wtb;
-  wtb << L"ABCDEFG";
-  EXPECT_TRUE(wtb.AsStringView().EqualsASCII("ABCDEFG"));
-
-  wtb.Delete(1, 3);
-  EXPECT_TRUE(wtb.AsStringView().EqualsASCII("AEFG"));
-
-  wtb.Delete(1, 0);
-  EXPECT_TRUE(wtb.AsStringView().EqualsASCII("AEFG"));
-
-  wtb.Delete(0, 2);
-  EXPECT_TRUE(wtb.AsStringView().EqualsASCII("FG"));
-
-  wtb.Delete(0, 2);
-  EXPECT_TRUE(wtb.AsStringView().EqualsASCII(""));
-
-  wtb.Delete(0, 0);
-  EXPECT_TRUE(wtb.AsStringView().EqualsASCII(""));
-}
-
-}  // namespace fxcrt
diff --git a/core/fxcrt/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/BUILD.gn b/core/fxcrt/css/BUILD.gn
index 66090c1..cd138b6 100644
--- a/core/fxcrt/css/BUILD.gn
+++ b/core/fxcrt/css/BUILD.gn
@@ -1,8 +1,9 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
 import("../../../pdfium.gni")
+import("../../../testing/test.gni")
 
 assert(pdf_enable_xfa)
 
@@ -21,10 +22,12 @@
     "cfx_cssdeclaration.h",
     "cfx_cssenumvalue.cpp",
     "cfx_cssenumvalue.h",
-    "cfx_cssexttextbuf.cpp",
-    "cfx_cssexttextbuf.h",
+    "cfx_cssinputtextbuf.cpp",
+    "cfx_cssinputtextbuf.h",
     "cfx_cssnumbervalue.cpp",
     "cfx_cssnumbervalue.h",
+    "cfx_cssoutputtextbuf.cpp",
+    "cfx_cssoutputtextbuf.h",
     "cfx_csspropertyholder.cpp",
     "cfx_csspropertyholder.h",
     "cfx_cssrulecollection.cpp",
@@ -41,8 +44,6 @@
     "cfx_cssstylesheet.h",
     "cfx_csssyntaxparser.cpp",
     "cfx_csssyntaxparser.h",
-    "cfx_csstextbuf.cpp",
-    "cfx_csstextbuf.h",
     "cfx_cssvalue.cpp",
     "cfx_cssvalue.h",
     "cfx_cssvaluelist.cpp",
@@ -50,10 +51,25 @@
     "cfx_cssvaluelistparser.cpp",
     "cfx_cssvaluelistparser.h",
   ]
+  configs += [
+    "../../../:pdfium_strict_config",
+    "../../../:pdfium_noshorten_config",
+  ]
   deps = [
     "../",
     "../../fxge",
   ]
-  configs += [ "../../../:pdfium_core_config" ]
   visibility = [ "../../../*" ]
 }
+
+pdfium_unittest_source_set("unittests") {
+  sources = [
+    "cfx_cssdata_unittest.cpp",
+    "cfx_cssdeclaration_unittest.cpp",
+    "cfx_cssstylesheet_unittest.cpp",
+    "cfx_csssyntaxparser_unittest.cpp",
+    "cfx_cssvaluelistparser_unittest.cpp",
+  ]
+  pdfium_root_dir = "../../../"
+  deps = [ ":css" ]
+}
diff --git a/core/fxcrt/css/cfx_css.h b/core/fxcrt/css/cfx_css.h
index 30d9ff8..1f71851 100644
--- a/core/fxcrt/css/cfx_css.h
+++ b/core/fxcrt/css/cfx_css.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,6 +9,8 @@
 
 #include <stdint.h>
 
+#include <type_traits>
+
 enum CFX_CSSVALUETYPE {
   CFX_CSSVALUETYPE_Primitive = 1 << 0,
   CFX_CSSVALUETYPE_List = 1 << 1,
@@ -19,112 +21,21 @@
   CFX_CSSVALUETYPE_MaybeString = 1 << 7,
   CFX_CSSVALUETYPE_MaybeColor = 1 << 8
 };
+using CFX_CSSValueTypeMask = std::underlying_type<CFX_CSSVALUETYPE>::type;
 
-enum class CFX_CSSPrimitiveType : uint8_t {
-  Unknown = 0,
-  Number,
-  String,
-  RGB,
-  Enum,
-  Function,
-  List,
-};
-
-// Any entries added/removed here, will need to be mirrored in
-// propertyValueTable, in core/fxcrt/css/cfx_cssdata.cpp.
-enum class CFX_CSSPropertyValue : uint8_t {
-  Bolder = 0,
-  None,
-  Dot,
-  Sub,
-  Top,
-  Right,
-  Normal,
-  Auto,
-  Text,
-  XSmall,
-  Thin,
-  Small,
-  Bottom,
-  Underline,
-  Double,
-  Lighter,
-  Oblique,
-  Super,
-  Center,
-  XxLarge,
-  Smaller,
-  Baseline,
-  Thick,
-  Justify,
-  Middle,
-  Medium,
-  ListItem,
-  XxSmall,
-  Bold,
-  SmallCaps,
-  Inline,
-  Overline,
-  TextBottom,
-  Larger,
-  InlineTable,
-  InlineBlock,
-  Blink,
-  Block,
-  Italic,
-  LineThrough,
-  XLarge,
-  Large,
-  Left,
-  TextTop,
-};
-
-// Any entries added/removed here, will need to be mirrored in
-// propertyTable, in core/fxcrt/css/cfx_cssdata.cpp.
+#undef CSS_PROP____
+#define CSS_PROP____(a, b, c, d) a,
 enum class CFX_CSSProperty : uint8_t {
-  BorderLeft = 0,
-  Top,
-  Margin,
-  TextIndent,
-  Right,
-  PaddingLeft,
-  MarginLeft,
-  Border,
-  BorderTop,
-  Bottom,
-  PaddingRight,
-  BorderBottom,
-  FontFamily,
-  FontWeight,
-  Color,
-  LetterSpacing,
-  TextAlign,
-  BorderRightWidth,
-  VerticalAlign,
-  PaddingTop,
-  FontVariant,
-  BorderWidth,
-  BorderBottomWidth,
-  BorderRight,
-  FontSize,
-  BorderSpacing,
-  FontStyle,
-  Font,
-  LineHeight,
-  MarginRight,
-  BorderLeftWidth,
-  Display,
-  PaddingBottom,
-  BorderTopWidth,
-  WordSpacing,
-  Left,
-  TextDecoration,
-  Padding,
-  MarginBottom,
-  MarginTop,
+#include "core/fxcrt/css/properties.inc"
 };
+#undef CSS_PROP____
 
-enum class CFX_CSSSelectorType : uint8_t { Element = 0, Descendant };
+#undef CSS_PROP_VALUE____
+#define CSS_PROP_VALUE____(a, b, c) a,
+enum class CFX_CSSPropertyValue : uint8_t {
+#include "core/fxcrt/css/property_values.inc"
+};
+#undef CSS_PROP_VALUE____
 
 enum class CFX_CSSLengthUnit : uint8_t {
   Auto,
@@ -173,18 +84,18 @@
   SmallCaps,
 };
 
-enum CFX_CSSTEXTDECORATION {
-  CFX_CSSTEXTDECORATION_None = 0,
-  CFX_CSSTEXTDECORATION_Underline = 1 << 0,
-  CFX_CSSTEXTDECORATION_Overline = 1 << 1,
-  CFX_CSSTEXTDECORATION_LineThrough = 1 << 2,
-  CFX_CSSTEXTDECORATION_Blink = 1 << 3,
-  CFX_CSSTEXTDECORATION_Double = 1 << 4,
+enum class CFX_CSSTEXTDECORATION : uint8_t {
+  kNone = 0,
+  kUnderline = 1 << 0,
+  kOverline = 1 << 1,
+  kLineThrough = 1 << 2,
+  kBlink = 1 << 3,
+  kDouble = 1 << 4,
 };
 
 class CFX_CSSLength {
  public:
-  CFX_CSSLength() {}
+  CFX_CSSLength() = default;
 
   CFX_CSSLength(CFX_CSSLengthUnit eUnit, float fValue)
       : m_unit(eUnit), m_fValue(fValue) {}
@@ -212,7 +123,7 @@
 
 class CFX_CSSRect {
  public:
-  CFX_CSSRect() {}
+  CFX_CSSRect() = default;
 
   CFX_CSSRect(CFX_CSSLengthUnit eUnit, float val)
       : left(eUnit, val),
diff --git a/core/fxcrt/css/cfx_csscolorvalue.cpp b/core/fxcrt/css/cfx_csscolorvalue.cpp
index 8c5473b..aba2fbf 100644
--- a/core/fxcrt/css/cfx_csscolorvalue.cpp
+++ b/core/fxcrt/css/cfx_csscolorvalue.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,6 @@
 #include "core/fxcrt/css/cfx_csscolorvalue.h"
 
 CFX_CSSColorValue::CFX_CSSColorValue(FX_ARGB value)
-    : CFX_CSSValue(CFX_CSSPrimitiveType::RGB), value_(value) {}
+    : CFX_CSSValue(PrimitiveType::kRGB), value_(value) {}
 
-CFX_CSSColorValue::~CFX_CSSColorValue() {}
+CFX_CSSColorValue::~CFX_CSSColorValue() = default;
diff --git a/core/fxcrt/css/cfx_csscolorvalue.h b/core/fxcrt/css/cfx_csscolorvalue.h
index fdc3fcb..3e551fc 100644
--- a/core/fxcrt/css/cfx_csscolorvalue.h
+++ b/core/fxcrt/css/cfx_csscolorvalue.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,7 @@
 #define CORE_FXCRT_CSS_CFX_CSSCOLORVALUE_H_
 
 #include "core/fxcrt/css/cfx_cssvalue.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
 
 class CFX_CSSColorValue final : public CFX_CSSValue {
  public:
diff --git a/core/fxcrt/css/cfx_csscomputedstyle.cpp b/core/fxcrt/css/cfx_csscomputedstyle.cpp
index 11ae901..489eed1 100644
--- a/core/fxcrt/css/cfx_csscomputedstyle.cpp
+++ b/core/fxcrt/css/cfx_csscomputedstyle.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,6 +8,7 @@
 
 #include "core/fxcrt/css/cfx_cssstringvalue.h"
 #include "core/fxcrt/css/cfx_cssvaluelist.h"
+#include "third_party/base/containers/adapters.h"
 
 CFX_CSSComputedStyle::CFX_CSSComputedStyle() = default;
 
@@ -15,25 +16,24 @@
 
 bool CFX_CSSComputedStyle::GetCustomStyle(const WideString& wsName,
                                           WideString* pValue) const {
-  for (auto iter = m_CustomProperties.rbegin();
-       iter != m_CustomProperties.rend(); ++iter) {
-    if (wsName == iter->name()) {
-      *pValue = iter->value();
+  for (const auto& prop : pdfium::base::Reversed(m_CustomProperties)) {
+    if (wsName == prop.name()) {
+      *pValue = prop.value();
       return true;
     }
   }
   return false;
 }
 
-int32_t CFX_CSSComputedStyle::CountFontFamilies() const {
-  return m_InheritedData.m_pFontFamily
-             ? m_InheritedData.m_pFontFamily->CountValues()
-             : 0;
-}
+absl::optional<WideString> CFX_CSSComputedStyle::GetLastFontFamily() const {
+  if (!m_InheritedData.m_pFontFamily ||
+      m_InheritedData.m_pFontFamily->values().empty()) {
+    return absl::nullopt;
+  }
 
-const WideString CFX_CSSComputedStyle::GetFontFamily(int32_t index) const {
-  return m_InheritedData.m_pFontFamily->GetValue(index)
-      .As<CFX_CSSStringValue>()
+  return m_InheritedData.m_pFontFamily->values()
+      .back()
+      .AsRaw<CFX_CSSStringValue>()
       ->Value();
 }
 
@@ -119,14 +119,14 @@
 }
 
 CFX_CSSVerticalAlign CFX_CSSComputedStyle::GetVerticalAlign() const {
-  return m_NonInheritedData.m_eVerticalAlign;
+  return m_NonInheritedData.m_eVerticalAlignType;
 }
 
 float CFX_CSSComputedStyle::GetNumberVerticalAlign() const {
   return m_NonInheritedData.m_fVerticalAlign;
 }
 
-uint32_t CFX_CSSComputedStyle::GetTextDecoration() const {
+Mask<CFX_CSSTEXTDECORATION> CFX_CSSComputedStyle::GetTextDecoration() const {
   return m_NonInheritedData.m_dwTextDecoration;
 }
 
@@ -147,11 +147,12 @@
 }
 
 void CFX_CSSComputedStyle::SetNumberVerticalAlign(float fAlign) {
-  m_NonInheritedData.m_eVerticalAlign = CFX_CSSVerticalAlign::Number,
+  m_NonInheritedData.m_eVerticalAlignType = CFX_CSSVerticalAlign::Number,
   m_NonInheritedData.m_fVerticalAlign = fAlign;
 }
 
-void CFX_CSSComputedStyle::SetTextDecoration(uint32_t dwTextDecoration) {
+void CFX_CSSComputedStyle::SetTextDecoration(
+    Mask<CFX_CSSTEXTDECORATION> dwTextDecoration) {
   m_NonInheritedData.m_dwTextDecoration = dwTextDecoration;
 }
 
@@ -166,29 +167,8 @@
   m_CustomProperties.push_back(prop);
 }
 
-CFX_CSSComputedStyle::InheritedData::InheritedData()
-    : m_LetterSpacing(CFX_CSSLengthUnit::Normal, 0),
-      m_WordSpacing(CFX_CSSLengthUnit::Normal, 0),
-      m_TextIndent(CFX_CSSLengthUnit::Point, 0),
-      m_pFontFamily(nullptr),
-      m_fFontSize(12.0f),
-      m_fLineHeight(14.0f),
-      m_dwFontColor(0xFF000000),
-      m_wFontWeight(400),
-      m_eFontVariant(CFX_CSSFontVariant::Normal),
-      m_eFontStyle(CFX_CSSFontStyle::Normal),
-      m_eTextAlign(CFX_CSSTextAlign::Left) {}
+CFX_CSSComputedStyle::InheritedData::InheritedData() = default;
 
-CFX_CSSComputedStyle::InheritedData::~InheritedData() {}
+CFX_CSSComputedStyle::InheritedData::~InheritedData() = default;
 
-CFX_CSSComputedStyle::NonInheritedData::NonInheritedData()
-    : m_MarginWidth(CFX_CSSLengthUnit::Point, 0),
-      m_BorderWidth(CFX_CSSLengthUnit::Point, 0),
-      m_PaddingWidth(CFX_CSSLengthUnit::Point, 0),
-      m_fVerticalAlign(0.0f),
-      m_eDisplay(CFX_CSSDisplay::Inline),
-      m_eVerticalAlign(CFX_CSSVerticalAlign::Baseline),
-      m_dwTextDecoration(0),
-      m_bHasMargin(false),
-      m_bHasBorder(false),
-      m_bHasPadding(false) {}
+CFX_CSSComputedStyle::NonInheritedData::NonInheritedData() = default;
diff --git a/core/fxcrt/css/cfx_csscomputedstyle.h b/core/fxcrt/css/cfx_csscomputedstyle.h
index d4959ee..9e685c4 100644
--- a/core/fxcrt/css/cfx_csscomputedstyle.h
+++ b/core/fxcrt/css/cfx_csscomputedstyle.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 #include "core/fxcrt/css/cfx_css.h"
 #include "core/fxcrt/css/cfx_csscustomproperty.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxcrt/mask.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CFX_CSSValueList;
 
@@ -23,44 +26,42 @@
     InheritedData();
     ~InheritedData();
 
-    CFX_CSSLength m_LetterSpacing;
-    CFX_CSSLength m_WordSpacing;
-    CFX_CSSLength m_TextIndent;
+    CFX_CSSLength m_LetterSpacing{CFX_CSSLengthUnit::Normal, 0};
+    CFX_CSSLength m_WordSpacing{CFX_CSSLengthUnit::Normal, 0};
+    CFX_CSSLength m_TextIndent{CFX_CSSLengthUnit::Point, 0};
     RetainPtr<CFX_CSSValueList> m_pFontFamily;
-    float m_fFontSize;
-    float m_fLineHeight;
-    FX_ARGB m_dwFontColor;
-    uint16_t m_wFontWeight;
-    CFX_CSSFontVariant m_eFontVariant;
-    CFX_CSSFontStyle m_eFontStyle;
-    CFX_CSSTextAlign m_eTextAlign;
+    float m_fFontSize = 12.0f;
+    float m_fLineHeight = 14.0f;
+    FX_ARGB m_dwFontColor = 0xFF000000;
+    uint16_t m_wFontWeight = 400;
+    CFX_CSSFontVariant m_eFontVariant = CFX_CSSFontVariant::Normal;
+    CFX_CSSFontStyle m_eFontStyle = CFX_CSSFontStyle::Normal;
+    CFX_CSSTextAlign m_eTextAlign = CFX_CSSTextAlign::Left;
   };
 
   class NonInheritedData {
    public:
     NonInheritedData();
 
-    CFX_CSSRect m_MarginWidth;
-    CFX_CSSRect m_BorderWidth;
-    CFX_CSSRect m_PaddingWidth;
+    CFX_CSSRect m_MarginWidth{CFX_CSSLengthUnit::Point, 0};
+    CFX_CSSRect m_BorderWidth{CFX_CSSLengthUnit::Point, 0};
+    CFX_CSSRect m_PaddingWidth{CFX_CSSLengthUnit::Point, 0};
     CFX_CSSLength m_Top;
     CFX_CSSLength m_Bottom;
     CFX_CSSLength m_Left;
     CFX_CSSLength m_Right;
-    float m_fVerticalAlign;
-    CFX_CSSDisplay m_eDisplay;
-    CFX_CSSVerticalAlign m_eVerticalAlign;
-    uint8_t m_dwTextDecoration;
-    bool m_bHasMargin;
-    bool m_bHasBorder;
-    bool m_bHasPadding;
+    float m_fVerticalAlign = 0.0f;
+    CFX_CSSDisplay m_eDisplay = CFX_CSSDisplay::Inline;
+    CFX_CSSVerticalAlign m_eVerticalAlignType = CFX_CSSVerticalAlign::Baseline;
+    Mask<CFX_CSSTEXTDECORATION> m_dwTextDecoration;
+    bool m_bHasMargin = false;
+    bool m_bHasBorder = false;
+    bool m_bHasPadding = false;
   };
 
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
-  int32_t CountFontFamilies() const;
-  const WideString GetFontFamily(int32_t index) const;
+  absl::optional<WideString> GetLastFontFamily() const;
   uint16_t GetFontWeight() const;
   CFX_CSSFontVariant GetFontVariant() const;
   CFX_CSSFontStyle GetFontStyle() const;
@@ -85,13 +86,13 @@
   CFX_CSSTextAlign GetTextAlign() const;
   CFX_CSSVerticalAlign GetVerticalAlign() const;
   float GetNumberVerticalAlign() const;
-  uint32_t GetTextDecoration() const;
+  Mask<CFX_CSSTEXTDECORATION> GetTextDecoration() const;
   const CFX_CSSLength& GetLetterSpacing() const;
   void SetLineHeight(float fLineHeight);
   void SetTextIndent(const CFX_CSSLength& textIndent);
   void SetTextAlign(CFX_CSSTextAlign eTextAlign);
   void SetNumberVerticalAlign(float fAlign);
-  void SetTextDecoration(uint32_t dwTextDecoration);
+  void SetTextDecoration(Mask<CFX_CSSTEXTDECORATION> dwTextDecoration);
   void SetLetterSpacing(const CFX_CSSLength& letterSpacing);
   void AddCustomStyle(const CFX_CSSCustomProperty& prop);
 
diff --git a/core/fxcrt/css/cfx_csscustomproperty.cpp b/core/fxcrt/css/cfx_csscustomproperty.cpp
index 353facd..f07887b 100644
--- a/core/fxcrt/css/cfx_csscustomproperty.cpp
+++ b/core/fxcrt/css/cfx_csscustomproperty.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,4 +11,4 @@
 CFX_CSSCustomProperty::CFX_CSSCustomProperty(const CFX_CSSCustomProperty& prop)
     : name_(prop.name_), value_(prop.value_) {}
 
-CFX_CSSCustomProperty::~CFX_CSSCustomProperty() {}
+CFX_CSSCustomProperty::~CFX_CSSCustomProperty() = default;
diff --git a/core/fxcrt/css/cfx_csscustomproperty.h b/core/fxcrt/css/cfx_csscustomproperty.h
index 15c3eca..450c2b1 100644
--- a/core/fxcrt/css/cfx_csscustomproperty.h
+++ b/core/fxcrt/css/cfx_csscustomproperty.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #ifndef CORE_FXCRT_CSS_CFX_CSSCUSTOMPROPERTY_H_
 #define CORE_FXCRT_CSS_CFX_CSSCUSTOMPROPERTY_H_
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 
 class CFX_CSSCustomProperty {
  public:
diff --git a/core/fxcrt/css/cfx_cssdata.cpp b/core/fxcrt/css/cfx_cssdata.cpp
index cfef220..0659a59 100644
--- a/core/fxcrt/css/cfx_cssdata.cpp
+++ b/core/fxcrt/css/cfx_cssdata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,256 +16,33 @@
 
 namespace {
 
-#undef PROP
-#define PROP(a, b, c, d) a, c, d
-
-const CFX_CSSData::Property propertyTable[] = {
-    {PROP(CFX_CSSProperty::BorderLeft,
-          "border-left",
-          0x04080036,
-          CFX_CSSVALUETYPE_Shorthand)},
-    {PROP(CFX_CSSProperty::Top,
-          "top",
-          0x0BEDAF33,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-              CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::Margin,
-          "margin",
-          0x0CB016BE,
-          CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum |
-              CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::TextIndent,
-          "text-indent",
-          0x169ADB74,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::Right,
-          "right",
-          0x193ADE3E,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-              CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::PaddingLeft,
-          "padding-left",
-          0x228CF02F,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::MarginLeft,
-          "margin-left",
-          0x297C5656,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber |
-              CFX_CSSVALUETYPE_MaybeEnum)},
-    {PROP(CFX_CSSProperty::Border,
-          "border",
-          0x2A23349E,
-          CFX_CSSVALUETYPE_Shorthand)},
-    {PROP(CFX_CSSProperty::BorderTop,
-          "border-top",
-          0x2B866ADE,
-          CFX_CSSVALUETYPE_Shorthand)},
-    {PROP(CFX_CSSProperty::Bottom,
-          "bottom",
-          0x399F02B5,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-              CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::PaddingRight,
-          "padding-right",
-          0x3F616AC2,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::BorderBottom,
-          "border-bottom",
-          0x452CE780,
-          CFX_CSSVALUETYPE_Shorthand)},
-    {PROP(CFX_CSSProperty::FontFamily,
-          "font-family",
-          0x574686E6,
-          CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeString)},
-    {PROP(CFX_CSSProperty::FontWeight,
-          "font-weight",
-          0x6692F60C,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-              CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::Color,
-          "color",
-          0x6E67921F,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-              CFX_CSSVALUETYPE_MaybeColor)},
-    {PROP(CFX_CSSProperty::LetterSpacing,
-          "letter-spacing",
-          0x70536102,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-              CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::TextAlign,
-          "text-align",
-          0x7553F1BD,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum)},
-    {PROP(CFX_CSSProperty::BorderRightWidth,
-          "border-right-width",
-          0x8F5A6036,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-              CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::VerticalAlign,
-          "vertical-align",
-          0x934A87D2,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-              CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::PaddingTop,
-          "padding-top",
-          0x959D22B7,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::FontVariant,
-          "font-variant",
-          0x9C785779,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum)},
-    {PROP(CFX_CSSProperty::BorderWidth,
-          "border-width",
-          0xA8DE4FEB,
-          CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum |
-              CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::BorderBottomWidth,
-          "border-bottom-width",
-          0xAE41204D,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-              CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::BorderRight,
-          "border-right",
-          0xB78E9EA9,
-          CFX_CSSVALUETYPE_Shorthand)},
-    {PROP(CFX_CSSProperty::FontSize,
-          "font-size",
-          0xB93956DF,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-              CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::BorderSpacing,
-          "border-spacing",
-          0xC72030F0,
-          CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::FontStyle,
-          "font-style",
-          0xCB1950F5,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum)},
-    {PROP(CFX_CSSProperty::Font,
-          "font",
-          0xCD308B77,
-          CFX_CSSVALUETYPE_Shorthand)},
-    {PROP(CFX_CSSProperty::LineHeight,
-          "line-height",
-          0xCFCACE2E,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-              CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::MarginRight,
-          "margin-right",
-          0xD13C58C9,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber |
-              CFX_CSSVALUETYPE_MaybeEnum)},
-    {PROP(CFX_CSSProperty::BorderLeftWidth,
-          "border-left-width",
-          0xD1E93D83,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-              CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::Display,
-          "display",
-          0xD4224C36,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum)},
-    {PROP(CFX_CSSProperty::PaddingBottom,
-          "padding-bottom",
-          0xE555B3B9,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::BorderTopWidth,
-          "border-top-width",
-          0xED2CB62B,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-              CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::WordSpacing,
-          "word-spacing",
-          0xEDA63BAE,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-              CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::Left,
-          "left",
-          0xF5AD782B,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-              CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::TextDecoration,
-          "text-decoration",
-          0xF7C634BA,
-          CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum)},
-    {PROP(CFX_CSSProperty::Padding,
-          "padding",
-          0xF8C373F7,
-          CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeNumber)},
-    {PROP(CFX_CSSProperty::MarginBottom,
-          "margin-bottom",
-          0xF93485A0,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber |
-              CFX_CSSVALUETYPE_MaybeEnum)},
-    {PROP(CFX_CSSProperty::MarginTop,
-          "margin-top",
-          0xFE51DCFE,
-          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber |
-              CFX_CSSVALUETYPE_MaybeEnum)},
+#undef CSS_PROP____
+#define CSS_PROP____(a, b, c, d) {CFX_CSSProperty::a, c, d},
+const CFX_CSSData::Property kPropertyTable[] = {
+#include "core/fxcrt/css/properties.inc"
 };
+#undef CSS_PROP____
 
-#undef PROP
-
-#undef PVAL
-#define PVAL(a, b, c) a, c
-
-const CFX_CSSData::PropertyValue propertyValueTable[] = {
-    {PVAL(CFX_CSSPropertyValue::Bolder, "bolder", 0x009F1058)},
-    {PVAL(CFX_CSSPropertyValue::None, "none", 0x048B6670)},
-    {PVAL(CFX_CSSPropertyValue::Dot, "dot", 0x0A48CB27)},
-    {PVAL(CFX_CSSPropertyValue::Sub, "sub", 0x0BD37FAA)},
-    {PVAL(CFX_CSSPropertyValue::Top, "top", 0x0BEDAF33)},
-    {PVAL(CFX_CSSPropertyValue::Right, "right", 0x193ADE3E)},
-    {PVAL(CFX_CSSPropertyValue::Normal, "normal", 0x247CF3E9)},
-    {PVAL(CFX_CSSPropertyValue::Auto, "auto", 0x2B35B6D9)},
-    {PVAL(CFX_CSSPropertyValue::Text, "text", 0x2D08AF85)},
-    {PVAL(CFX_CSSPropertyValue::XSmall, "x-small", 0x2D2FCAFE)},
-    {PVAL(CFX_CSSPropertyValue::Thin, "thin", 0x2D574D53)},
-    {PVAL(CFX_CSSPropertyValue::Small, "small", 0x316A3739)},
-    {PVAL(CFX_CSSPropertyValue::Bottom, "bottom", 0x399F02B5)},
-    {PVAL(CFX_CSSPropertyValue::Underline, "underline", 0x3A0273A6)},
-    {PVAL(CFX_CSSPropertyValue::Double, "double", 0x3D98515B)},
-    {PVAL(CFX_CSSPropertyValue::Lighter, "lighter", 0x45BEB7AF)},
-    {PVAL(CFX_CSSPropertyValue::Oblique, "oblique", 0x53EBDDB1)},
-    {PVAL(CFX_CSSPropertyValue::Super, "super", 0x6A4F842F)},
-    {PVAL(CFX_CSSPropertyValue::Center, "center", 0x6C51AFC1)},
-    {PVAL(CFX_CSSPropertyValue::XxLarge, "xx-large", 0x70BB1508)},
-    {PVAL(CFX_CSSPropertyValue::Smaller, "smaller", 0x849769F0)},
-    {PVAL(CFX_CSSPropertyValue::Baseline, "baseline", 0x87436BA3)},
-    {PVAL(CFX_CSSPropertyValue::Thick, "thick", 0x8CC35EB3)},
-    {PVAL(CFX_CSSPropertyValue::Justify, "justify", 0x8D269CAE)},
-    {PVAL(CFX_CSSPropertyValue::Middle, "middle", 0x947FA00F)},
-    {PVAL(CFX_CSSPropertyValue::Medium, "medium", 0xA084A381)},
-    {PVAL(CFX_CSSPropertyValue::ListItem, "list-item", 0xA32382B8)},
-    {PVAL(CFX_CSSPropertyValue::XxSmall, "xx-small", 0xADE1FC76)},
-    {PVAL(CFX_CSSPropertyValue::Bold, "bold", 0xB18313A1)},
-    {PVAL(CFX_CSSPropertyValue::SmallCaps, "small-caps", 0xB299428D)},
-    {PVAL(CFX_CSSPropertyValue::Inline, "inline", 0xC02D649F)},
-    {PVAL(CFX_CSSPropertyValue::Overline, "overline", 0xC0EC9FA4)},
-    {PVAL(CFX_CSSPropertyValue::TextBottom, "text-bottom", 0xC7D08D87)},
-    {PVAL(CFX_CSSPropertyValue::Larger, "larger", 0xCD3C409D)},
-    {PVAL(CFX_CSSPropertyValue::InlineTable, "inline-table", 0xD131F494)},
-    {PVAL(CFX_CSSPropertyValue::InlineBlock, "inline-block", 0xD26A8BD7)},
-    {PVAL(CFX_CSSPropertyValue::Blink, "blink", 0xDC36E390)},
-    {PVAL(CFX_CSSPropertyValue::Block, "block", 0xDCD480AB)},
-    {PVAL(CFX_CSSPropertyValue::Italic, "italic", 0xE31D5396)},
-    {PVAL(CFX_CSSPropertyValue::LineThrough, "line-through", 0xE4C5A276)},
-    {PVAL(CFX_CSSPropertyValue::XLarge, "x-large", 0xF008E390)},
-    {PVAL(CFX_CSSPropertyValue::Large, "large", 0xF4434FCB)},
-    {PVAL(CFX_CSSPropertyValue::Left, "left", 0xF5AD782B)},
-    {PVAL(CFX_CSSPropertyValue::TextTop, "text-top", 0xFCB58D45)},
+#undef CSS_PROP_VALUE____
+#define CSS_PROP_VALUE____(a, b, c) {CFX_CSSPropertyValue::a, c},
+const CFX_CSSData::PropertyValue kPropertyValueTable[] = {
+#include "core/fxcrt/css/property_values.inc"
 };
+#undef CSS_PROP_VALUE____
 
-#undef PVAL
-
-const CFX_CSSData::LengthUnit lengthUnitTable[] = {
-    {L"cm", CFX_CSSNumberType::CentiMeters}, {L"em", CFX_CSSNumberType::EMS},
-    {L"ex", CFX_CSSNumberType::EXS},         {L"in", CFX_CSSNumberType::Inches},
-    {L"mm", CFX_CSSNumberType::MilliMeters}, {L"pc", CFX_CSSNumberType::Picas},
-    {L"pt", CFX_CSSNumberType::Points},      {L"px", CFX_CSSNumberType::Pixels},
+const CFX_CSSData::LengthUnit kLengthUnitTable[] = {
+    {L"cm", CFX_CSSNumberValue::Unit::kCentiMeters},
+    {L"em", CFX_CSSNumberValue::Unit::kEMS},
+    {L"ex", CFX_CSSNumberValue::Unit::kEXS},
+    {L"in", CFX_CSSNumberValue::Unit::kInches},
+    {L"mm", CFX_CSSNumberValue::Unit::kMilliMeters},
+    {L"pc", CFX_CSSNumberValue::Unit::kPicas},
+    {L"pt", CFX_CSSNumberValue::Unit::kPoints},
+    {L"px", CFX_CSSNumberValue::Unit::kPixels},
 };
 
 // 16 colours from CSS 2.0 + alternate spelling of grey/gray.
-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},
@@ -281,20 +58,22 @@
   if (name.IsEmpty())
     return nullptr;
 
-  uint32_t hash = FX_HashCode_GetW(name, true);
-  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; });
+  uint32_t hash = FX_HashCode_GetLoweredW(name);
+  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(
@@ -302,15 +81,16 @@
   if (wsName.IsEmpty())
     return nullptr;
 
-  uint32_t hash = FX_HashCode_GetW(wsName, true);
+  uint32_t hash = FX_HashCode_GetLoweredW(wsName);
   auto* result = std::lower_bound(
-      std::begin(propertyValueTable), std::end(propertyValueTable), hash,
+      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;
 }
 
@@ -322,9 +102,9 @@
   WideString lowerName = WideString(wsName);
   lowerName.MakeLower();
 
-  for (auto* iter = std::begin(lengthUnitTable);
-       iter != std::end(lengthUnitTable); ++iter) {
-    if (lowerName.Compare(iter->value) == 0)
+  for (auto* iter = std::begin(kLengthUnitTable);
+       iter != std::end(kLengthUnitTable); ++iter) {
+    if (lowerName == iter->value)
       return iter;
   }
 
@@ -338,9 +118,9 @@
   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.Compare(iter->name) == 0)
+    if (lowerName == iter->name)
       return iter;
   }
   return nullptr;
diff --git a/core/fxcrt/css/cfx_cssdata.h b/core/fxcrt/css/cfx_cssdata.h
index d5b6151..c129cfe 100644
--- a/core/fxcrt/css/cfx_cssdata.h
+++ b/core/fxcrt/css/cfx_cssdata.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,15 +10,15 @@
 #include "core/fxcrt/css/cfx_css.h"
 #include "core/fxcrt/css/cfx_cssnumbervalue.h"
 #include "core/fxcrt/css/cfx_cssvalue.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxcrt/widestring.h"
+#include "core/fxge/dib/fx_dib.h"
 
 class CFX_CSSData {
  public:
   struct Property {
     CFX_CSSProperty eName;
     uint32_t dwHash;  // Hashed as wide string.
-    uint32_t dwType;
+    CFX_CSSValueTypeMask dwTypes;
   };
 
   struct PropertyValue {
@@ -27,12 +27,12 @@
   };
 
   struct LengthUnit {
-    const wchar_t* value;
-    CFX_CSSNumberType type;
+    const wchar_t* value;  // Raw, POD struct.
+    CFX_CSSNumberValue::Unit type;
   };
 
   struct Color {
-    const wchar_t* name;
+    const wchar_t* name;  // Raw, POD struct.
     FX_ARGB value;
   };
 
diff --git a/core/fxcrt/css/cfx_cssdata_unittest.cpp b/core/fxcrt/css/cfx_cssdata_unittest.cpp
new file mode 100644
index 0000000..9725b43
--- /dev/null
+++ b/core/fxcrt/css/cfx_cssdata_unittest.cpp
@@ -0,0 +1,34 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/css/cfx_cssdata.h"
+
+#include "core/fxcrt/bytestring.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(CSSDataTest, PropertyHashes) {
+  uint32_t max_hash = 0;
+#undef CSS_PROP____
+#define CSS_PROP____(a, b, c, d)                                       \
+  {                                                                    \
+    EXPECT_EQ(FX_HashCode_GetAsIfW(b), static_cast<uint32_t>(c)) << b; \
+    EXPECT_GT(static_cast<uint32_t>(c), max_hash) << b;                \
+    max_hash = c;                                                      \
+  }
+#include "core/fxcrt/css/properties.inc"
+#undef CSS_PROP____
+}
+
+TEST(CSSDataTest, PropertyValueHashes) {
+  uint32_t max_hash = 0;
+#undef CSS_PROP_VALUE____
+#define CSS_PROP_VALUE____(a, b, c)                                    \
+  {                                                                    \
+    EXPECT_EQ(FX_HashCode_GetAsIfW(b), static_cast<uint32_t>(c)) << b; \
+    EXPECT_GT(static_cast<uint32_t>(c), max_hash) << b;                \
+    max_hash = c;                                                      \
+  }
+#include "core/fxcrt/css/property_values.inc"
+#undef CSS_PROP_VALUE____
+}
diff --git a/core/fxcrt/css/cfx_cssdeclaration.cpp b/core/fxcrt/css/cfx_cssdeclaration.cpp
index de97b64..e1bdd0e 100644
--- a/core/fxcrt/css/cfx_cssdeclaration.cpp
+++ b/core/fxcrt/css/cfx_cssdeclaration.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,8 @@
 
 #include "core/fxcrt/css/cfx_cssdeclaration.h"
 
-#include <cmath>
+#include <math.h>
+
 #include <utility>
 
 #include "core/fxcrt/css/cfx_csscolorvalue.h"
@@ -18,8 +19,10 @@
 #include "core/fxcrt/css/cfx_cssvaluelist.h"
 #include "core/fxcrt/css/cfx_cssvaluelistparser.h"
 #include "core/fxcrt/fx_extension.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
+#include "core/fxcrt/fx_system.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
@@ -28,23 +31,23 @@
 }
 
 bool ParseCSSNumber(const wchar_t* pszValue,
-                    int32_t iValueLen,
+                    size_t nValueLen,
                     float* pValue,
-                    CFX_CSSNumberType* pOutUnit) {
-  ASSERT(pszValue);
-  ASSERT(iValueLen > 0);
+                    CFX_CSSNumberValue::Unit* pOutUnit) {
+  DCHECK(pszValue);
+  DCHECK_NE(nValueLen, 0);
 
-  int32_t iUsedLen = 0;
-  *pValue = FXSYS_wcstof(pszValue, iValueLen, &iUsedLen);
-  if (iUsedLen <= 0 || !std::isfinite(*pValue))
+  size_t nUsedLen = 0;
+  *pValue = FXSYS_wcstof(pszValue, nValueLen, &nUsedLen);
+  if (nUsedLen == 0 || !isfinite(*pValue))
     return false;
 
-  iValueLen -= iUsedLen;
-  pszValue += iUsedLen;
-  *pOutUnit = CFX_CSSNumberType::Number;
-  if (iValueLen >= 1 && *pszValue == '%') {
-    *pOutUnit = CFX_CSSNumberType::Percent;
-  } else if (iValueLen == 2) {
+  nValueLen -= nUsedLen;
+  pszValue += nUsedLen;
+  *pOutUnit = CFX_CSSNumberValue::Unit::kNumber;
+  if (nValueLen >= 1 && *pszValue == '%') {
+    *pOutUnit = CFX_CSSNumberValue::Unit::kPercent;
+  } else if (nValueLen == 2) {
     const CFX_CSSData::LengthUnit* pUnit =
         CFX_CSSData::GetLengthUnitByName(WideStringView(pszValue, 2));
     if (pUnit)
@@ -57,34 +60,34 @@
 
 // static
 bool CFX_CSSDeclaration::ParseCSSString(const wchar_t* pszValue,
-                                        int32_t iValueLen,
-                                        int32_t* iOffset,
-                                        int32_t* iLength) {
-  ASSERT(pszValue);
-  ASSERT(iValueLen > 0);
+                                        size_t nValueLen,
+                                        size_t* nOffset,
+                                        size_t* nLength) {
+  DCHECK(pszValue);
+  DCHECK_NE(nValueLen, 0);
 
-  *iOffset = 0;
-  *iLength = iValueLen;
-  if (iValueLen >= 2) {
-    wchar_t first = pszValue[0], last = pszValue[iValueLen - 1];
+  *nOffset = 0;
+  *nLength = nValueLen;
+  if (nValueLen >= 2) {
+    wchar_t first = pszValue[0];
+    wchar_t last = pszValue[nValueLen - 1];
     if ((first == '\"' && last == '\"') || (first == '\'' && last == '\'')) {
-      *iOffset = 1;
-      *iLength -= 2;
+      *nOffset = 1;
+      *nLength -= 2;
     }
   }
-  return iValueLen > 0;
+  return nValueLen > 0;
 }
 
 // static.
 bool CFX_CSSDeclaration::ParseCSSColor(const wchar_t* pszValue,
-                                       int32_t iValueLen,
+                                       size_t nValueLen,
                                        FX_ARGB* dwColor) {
-  ASSERT(pszValue);
-  ASSERT(iValueLen > 0);
-  ASSERT(dwColor);
+  DCHECK_NE(nValueLen, 0);
+  DCHECK(dwColor);
 
   if (*pszValue == '#') {
-    switch (iValueLen) {
+    switch (nValueLen) {
       case 4: {
         uint8_t red = Hex2Dec((uint8_t)pszValue[1], (uint8_t)pszValue[1]);
         uint8_t green = Hex2Dec((uint8_t)pszValue[2], (uint8_t)pszValue[2]);
@@ -104,24 +107,24 @@
     }
   }
 
-  if (iValueLen >= 10) {
-    if (pszValue[iValueLen - 1] != ')' || FXSYS_wcsnicmp(L"rgb(", pszValue, 4))
+  if (nValueLen >= 10) {
+    if (pszValue[nValueLen - 1] != ')' || FXSYS_wcsnicmp(L"rgb(", pszValue, 4))
       return false;
 
     uint8_t rgb[3] = {0};
     float fValue;
-    CFX_CSSPrimitiveType eType;
-    CFX_CSSValueListParser list(pszValue + 4, iValueLen - 5, ',');
+    CFX_CSSValue::PrimitiveType eType;
+    CFX_CSSValueListParser list(pszValue + 4, nValueLen - 5, ',');
     for (int32_t i = 0; i < 3; ++i) {
-      if (!list.NextValue(&eType, &pszValue, &iValueLen))
+      if (!list.NextValue(&eType, &pszValue, &nValueLen))
         return false;
-      if (eType != CFX_CSSPrimitiveType::Number)
+      if (eType != CFX_CSSValue::PrimitiveType::kNumber)
         return false;
-      CFX_CSSNumberType eNumType;
-      if (!ParseCSSNumber(pszValue, iValueLen, &fValue, &eNumType))
+      CFX_CSSNumberValue::Unit eNumType;
+      if (!ParseCSSNumber(pszValue, nValueLen, &fValue, &eNumType))
         return false;
 
-      rgb[i] = eNumType == CFX_CSSNumberType::Percent
+      rgb[i] = eNumType == CFX_CSSNumberValue::Unit::kPercent
                    ? FXSYS_roundf(fValue * 2.55f)
                    : FXSYS_roundf(fValue);
     }
@@ -130,7 +133,7 @@
   }
 
   const CFX_CSSData::Color* pColor =
-      CFX_CSSData::GetColorByName(WideStringView(pszValue, iValueLen));
+      CFX_CSSData::GetColorByName(WideStringView(pszValue, nValueLen));
   if (!pColor)
     return false;
 
@@ -138,9 +141,9 @@
   return true;
 }
 
-CFX_CSSDeclaration::CFX_CSSDeclaration() {}
+CFX_CSSDeclaration::CFX_CSSDeclaration() = default;
 
-CFX_CSSDeclaration::~CFX_CSSDeclaration() {}
+CFX_CSSDeclaration::~CFX_CSSDeclaration() = default;
 
 RetainPtr<CFX_CSSValue> CFX_CSSDeclaration::GetProperty(
     CFX_CSSProperty eProperty,
@@ -157,7 +160,7 @@
 void CFX_CSSDeclaration::AddPropertyHolder(CFX_CSSProperty eProperty,
                                            RetainPtr<CFX_CSSValue> pValue,
                                            bool bImportant) {
-  auto pHolder = pdfium::MakeUnique<CFX_CSSPropertyHolder>();
+  auto pHolder = std::make_unique<CFX_CSSPropertyHolder>();
   pHolder->bImportant = bImportant;
   pHolder->eProperty = eProperty;
   pHolder->pValue = pValue;
@@ -166,19 +169,20 @@
 
 void CFX_CSSDeclaration::AddProperty(const CFX_CSSData::Property* property,
                                      WideStringView value) {
-  ASSERT(!value.IsEmpty());
+  DCHECK(!value.IsEmpty());
 
   const wchar_t* pszValue = value.unterminated_c_str();
-  int32_t iValueLen = value.GetLength();
+  size_t nValueLen = value.GetLength();
   bool bImportant = false;
-  if (iValueLen >= 10 && pszValue[iValueLen - 10] == '!' &&
-      FXSYS_wcsnicmp(L"important", pszValue + iValueLen - 9, 9) == 0) {
-    if ((iValueLen -= 10) == 0)
+  if (nValueLen >= 10 && pszValue[nValueLen - 10] == '!' &&
+      FXSYS_wcsnicmp(L"important", pszValue + nValueLen - 9, 9) == 0) {
+    nValueLen -= 10;
+    if (nValueLen == 0)
       return;
 
     bImportant = true;
   }
-  const uint32_t dwType = property->dwType;
+  const CFX_CSSValueTypeMask dwType = property->dwTypes;
   switch (dwType & 0x0F) {
     case CFX_CSSVALUETYPE_Primitive: {
       static constexpr CFX_CSSVALUETYPE kValueGuessOrder[] = {
@@ -187,24 +191,24 @@
           CFX_CSSVALUETYPE_MaybeColor,
           CFX_CSSVALUETYPE_MaybeString,
       };
-      for (uint32_t guess : kValueGuessOrder) {
-        const uint32_t dwMatch = dwType & guess;
+      for (CFX_CSSVALUETYPE guess : kValueGuessOrder) {
+        const CFX_CSSValueTypeMask dwMatch = dwType & guess;
         if (dwMatch == 0)
           continue;
 
         RetainPtr<CFX_CSSValue> pCSSValue;
         switch (dwMatch) {
           case CFX_CSSVALUETYPE_MaybeNumber:
-            pCSSValue = ParseNumber(pszValue, iValueLen);
+            pCSSValue = ParseNumber(pszValue, nValueLen);
             break;
           case CFX_CSSVALUETYPE_MaybeEnum:
-            pCSSValue = ParseEnum(pszValue, iValueLen);
+            pCSSValue = ParseEnum(pszValue, nValueLen);
             break;
           case CFX_CSSVALUETYPE_MaybeColor:
-            pCSSValue = ParseColor(pszValue, iValueLen);
+            pCSSValue = ParseColor(pszValue, nValueLen);
             break;
           case CFX_CSSVALUETYPE_MaybeString:
-            pCSSValue = ParseString(pszValue, iValueLen);
+            pCSSValue = ParseString(pszValue, nValueLen);
             break;
           default:
             break;
@@ -223,10 +227,10 @@
       RetainPtr<CFX_CSSValue> pWidth;
       switch (property->eName) {
         case CFX_CSSProperty::Font:
-          ParseFontProperty(pszValue, iValueLen, bImportant);
+          ParseFontProperty(pszValue, nValueLen, bImportant);
           return;
         case CFX_CSSProperty::Border:
-          if (ParseBorderProperty(pszValue, iValueLen, pWidth)) {
+          if (ParseBorderProperty(pszValue, nValueLen, pWidth)) {
             AddPropertyHolder(CFX_CSSProperty::BorderLeftWidth, pWidth,
                               bImportant);
             AddPropertyHolder(CFX_CSSProperty::BorderTopWidth, pWidth,
@@ -239,28 +243,28 @@
           }
           break;
         case CFX_CSSProperty::BorderLeft:
-          if (ParseBorderProperty(pszValue, iValueLen, pWidth)) {
+          if (ParseBorderProperty(pszValue, nValueLen, pWidth)) {
             AddPropertyHolder(CFX_CSSProperty::BorderLeftWidth, pWidth,
                               bImportant);
             return;
           }
           break;
         case CFX_CSSProperty::BorderTop:
-          if (ParseBorderProperty(pszValue, iValueLen, pWidth)) {
+          if (ParseBorderProperty(pszValue, nValueLen, pWidth)) {
             AddPropertyHolder(CFX_CSSProperty::BorderTopWidth, pWidth,
                               bImportant);
             return;
           }
           break;
         case CFX_CSSProperty::BorderRight:
-          if (ParseBorderProperty(pszValue, iValueLen, pWidth)) {
+          if (ParseBorderProperty(pszValue, nValueLen, pWidth)) {
             AddPropertyHolder(CFX_CSSProperty::BorderRightWidth, pWidth,
                               bImportant);
             return;
           }
           break;
         case CFX_CSSProperty::BorderBottom:
-          if (ParseBorderProperty(pszValue, iValueLen, pWidth)) {
+          if (ParseBorderProperty(pszValue, nValueLen, pWidth)) {
             AddPropertyHolder(CFX_CSSProperty::BorderBottomWidth, pWidth,
                               bImportant);
             return;
@@ -271,7 +275,7 @@
       }
     } break;
     case CFX_CSSVALUETYPE_List:
-      ParseValueListProperty(property, pszValue, iValueLen, bImportant);
+      ParseValueListProperty(property, pszValue, nValueLen, bImportant);
       return;
     default:
       NOTREACHED();
@@ -282,73 +286,73 @@
 void CFX_CSSDeclaration::AddProperty(const WideString& prop,
                                      const WideString& value) {
   custom_properties_.push_back(
-      pdfium::MakeUnique<CFX_CSSCustomProperty>(prop, value));
+      std::make_unique<CFX_CSSCustomProperty>(prop, value));
 }
 
 RetainPtr<CFX_CSSValue> CFX_CSSDeclaration::ParseNumber(const wchar_t* pszValue,
-                                                        int32_t iValueLen) {
+                                                        size_t nValueLen) {
   float fValue;
-  CFX_CSSNumberType eUnit;
-  if (!ParseCSSNumber(pszValue, iValueLen, &fValue, &eUnit))
+  CFX_CSSNumberValue::Unit eUnit;
+  if (!ParseCSSNumber(pszValue, nValueLen, &fValue, &eUnit))
     return nullptr;
   return pdfium::MakeRetain<CFX_CSSNumberValue>(eUnit, fValue);
 }
 
 RetainPtr<CFX_CSSValue> CFX_CSSDeclaration::ParseEnum(const wchar_t* pszValue,
-                                                      int32_t iValueLen) {
+                                                      size_t nValueLen) {
   const CFX_CSSData::PropertyValue* pValue =
-      CFX_CSSData::GetPropertyValueByName(WideStringView(pszValue, iValueLen));
+      CFX_CSSData::GetPropertyValueByName(WideStringView(pszValue, nValueLen));
   return pValue ? pdfium::MakeRetain<CFX_CSSEnumValue>(pValue->eName) : nullptr;
 }
 
 RetainPtr<CFX_CSSValue> CFX_CSSDeclaration::ParseColor(const wchar_t* pszValue,
-                                                       int32_t iValueLen) {
+                                                       size_t nValueLen) {
   FX_ARGB dwColor;
-  if (!ParseCSSColor(pszValue, iValueLen, &dwColor))
+  if (!ParseCSSColor(pszValue, nValueLen, &dwColor))
     return nullptr;
   return pdfium::MakeRetain<CFX_CSSColorValue>(dwColor);
 }
 
 RetainPtr<CFX_CSSValue> CFX_CSSDeclaration::ParseString(const wchar_t* pszValue,
-                                                        int32_t iValueLen) {
-  int32_t iOffset;
-  if (!ParseCSSString(pszValue, iValueLen, &iOffset, &iValueLen))
+                                                        size_t nValueLen) {
+  size_t iOffset;
+  if (!ParseCSSString(pszValue, nValueLen, &iOffset, &nValueLen))
     return nullptr;
 
-  if (iValueLen <= 0)
+  if (nValueLen == 0)
     return nullptr;
 
   return pdfium::MakeRetain<CFX_CSSStringValue>(
-      WideString(pszValue + iOffset, iValueLen));
+      WideString(pszValue + iOffset, nValueLen));
 }
 
 void CFX_CSSDeclaration::ParseValueListProperty(
     const CFX_CSSData::Property* pProperty,
     const wchar_t* pszValue,
-    int32_t iValueLen,
+    size_t nValueLen,
     bool bImportant) {
   wchar_t separator =
       (pProperty->eName == CFX_CSSProperty::FontFamily) ? ',' : ' ';
-  CFX_CSSValueListParser parser(pszValue, iValueLen, separator);
+  CFX_CSSValueListParser parser(pszValue, nValueLen, separator);
 
-  const uint32_t dwType = pProperty->dwType;
-  CFX_CSSPrimitiveType eType;
+  const CFX_CSSValueTypeMask dwType = pProperty->dwTypes;
+  CFX_CSSValue::PrimitiveType eType;
   std::vector<RetainPtr<CFX_CSSValue>> list;
-  while (parser.NextValue(&eType, &pszValue, &iValueLen)) {
+  while (parser.NextValue(&eType, &pszValue, &nValueLen)) {
     switch (eType) {
-      case CFX_CSSPrimitiveType::Number:
+      case CFX_CSSValue::PrimitiveType::kNumber:
         if (dwType & CFX_CSSVALUETYPE_MaybeNumber) {
           float fValue;
-          CFX_CSSNumberType eNumType;
-          if (ParseCSSNumber(pszValue, iValueLen, &fValue, &eNumType))
+          CFX_CSSNumberValue::Unit eNumType;
+          if (ParseCSSNumber(pszValue, nValueLen, &fValue, &eNumType))
             list.push_back(
                 pdfium::MakeRetain<CFX_CSSNumberValue>(eNumType, fValue));
         }
         break;
-      case CFX_CSSPrimitiveType::String:
+      case CFX_CSSValue::PrimitiveType::kString:
         if (dwType & CFX_CSSVALUETYPE_MaybeColor) {
           FX_ARGB dwColor;
-          if (ParseCSSColor(pszValue, iValueLen, &dwColor)) {
+          if (ParseCSSColor(pszValue, nValueLen, &dwColor)) {
             list.push_back(pdfium::MakeRetain<CFX_CSSColorValue>(dwColor));
             continue;
           }
@@ -356,7 +360,7 @@
         if (dwType & CFX_CSSVALUETYPE_MaybeEnum) {
           const CFX_CSSData::PropertyValue* pValue =
               CFX_CSSData::GetPropertyValueByName(
-                  WideStringView(pszValue, iValueLen));
+                  WideStringView(pszValue, nValueLen));
           if (pValue) {
             list.push_back(pdfium::MakeRetain<CFX_CSSEnumValue>(pValue->eName));
             continue;
@@ -364,13 +368,13 @@
         }
         if (dwType & CFX_CSSVALUETYPE_MaybeString) {
           list.push_back(pdfium::MakeRetain<CFX_CSSStringValue>(
-              WideString(pszValue, iValueLen)));
+              WideString(pszValue, nValueLen)));
         }
         break;
-      case CFX_CSSPrimitiveType::RGB:
+      case CFX_CSSValue::PrimitiveType::kRGB:
         if (dwType & CFX_CSSVALUETYPE_MaybeColor) {
           FX_ARGB dwColor;
-          if (ParseCSSColor(pszValue, iValueLen, &dwColor)) {
+          if (ParseCSSColor(pszValue, nValueLen, &dwColor)) {
             list.push_back(pdfium::MakeRetain<CFX_CSSColorValue>(dwColor));
           }
         }
@@ -402,8 +406,8 @@
                          CFX_CSSProperty::PaddingBottom);
       return;
     default: {
-      auto pList = pdfium::MakeRetain<CFX_CSSValueList>(list);
-      AddPropertyHolder(pProperty->eName, pList, bImportant);
+      auto value_list = pdfium::MakeRetain<CFX_CSSValueList>(std::move(list));
+      AddPropertyHolder(pProperty->eName, value_list, bImportant);
       return;
     }
   }
@@ -448,33 +452,33 @@
 
 bool CFX_CSSDeclaration::ParseBorderProperty(
     const wchar_t* pszValue,
-    int32_t iValueLen,
+    size_t nValueLen,
     RetainPtr<CFX_CSSValue>& pWidth) const {
   pWidth.Reset(nullptr);
 
-  CFX_CSSValueListParser parser(pszValue, iValueLen, ' ');
-  CFX_CSSPrimitiveType eType;
-  while (parser.NextValue(&eType, &pszValue, &iValueLen)) {
+  CFX_CSSValue::PrimitiveType eType;
+  CFX_CSSValueListParser parser(pszValue, nValueLen, ' ');
+  while (parser.NextValue(&eType, &pszValue, &nValueLen)) {
     switch (eType) {
-      case CFX_CSSPrimitiveType::Number: {
+      case CFX_CSSValue::PrimitiveType::kNumber: {
         if (pWidth)
           continue;
 
         float fValue;
-        CFX_CSSNumberType eNumType;
-        if (ParseCSSNumber(pszValue, iValueLen, &fValue, &eNumType))
+        CFX_CSSNumberValue::Unit eNumType;
+        if (ParseCSSNumber(pszValue, nValueLen, &fValue, &eNumType))
           pWidth = pdfium::MakeRetain<CFX_CSSNumberValue>(eNumType, fValue);
         break;
       }
-      case CFX_CSSPrimitiveType::String: {
+      case CFX_CSSValue::PrimitiveType::kString: {
         const CFX_CSSData::Color* pColorItem =
-            CFX_CSSData::GetColorByName(WideStringView(pszValue, iValueLen));
+            CFX_CSSData::GetColorByName(WideStringView(pszValue, nValueLen));
         if (pColorItem)
           continue;
 
         const CFX_CSSData::PropertyValue* pValue =
             CFX_CSSData::GetPropertyValueByName(
-                WideStringView(pszValue, iValueLen));
+                WideStringView(pszValue, nValueLen));
         if (!pValue)
           continue;
 
@@ -494,30 +498,30 @@
         break;
     }
   }
-  if (!pWidth)
-    pWidth =
-        pdfium::MakeRetain<CFX_CSSNumberValue>(CFX_CSSNumberType::Number, 0.0f);
-
+  if (!pWidth) {
+    pWidth = pdfium::MakeRetain<CFX_CSSNumberValue>(
+        CFX_CSSNumberValue::Unit::kNumber, 0.0f);
+  }
   return true;
 }
 
 void CFX_CSSDeclaration::ParseFontProperty(const wchar_t* pszValue,
-                                           int32_t iValueLen,
+                                           size_t nValueLen,
                                            bool bImportant) {
-  CFX_CSSValueListParser parser(pszValue, iValueLen, '/');
+  CFX_CSSValueListParser parser(pszValue, nValueLen, '/');
   RetainPtr<CFX_CSSValue> pStyle;
   RetainPtr<CFX_CSSValue> pVariant;
   RetainPtr<CFX_CSSValue> pWeight;
   RetainPtr<CFX_CSSValue> pFontSize;
   RetainPtr<CFX_CSSValue> pLineHeight;
-  std::vector<RetainPtr<CFX_CSSValue>> familyList;
-  CFX_CSSPrimitiveType eType;
-  while (parser.NextValue(&eType, &pszValue, &iValueLen)) {
+  std::vector<RetainPtr<CFX_CSSValue>> family_list;
+  CFX_CSSValue::PrimitiveType eType;
+  while (parser.NextValue(&eType, &pszValue, &nValueLen)) {
     switch (eType) {
-      case CFX_CSSPrimitiveType::String: {
+      case CFX_CSSValue::PrimitiveType::kString: {
         const CFX_CSSData::PropertyValue* pValue =
             CFX_CSSData::GetPropertyValueByName(
-                WideStringView(pszValue, iValueLen));
+                WideStringView(pszValue, nValueLen));
         if (pValue) {
           switch (pValue->eName) {
             case CFX_CSSPropertyValue::XxSmall:
@@ -565,19 +569,19 @@
           }
         }
         if (pFontSize) {
-          familyList.push_back(pdfium::MakeRetain<CFX_CSSStringValue>(
-              WideString(pszValue, iValueLen)));
+          family_list.push_back(pdfium::MakeRetain<CFX_CSSStringValue>(
+              WideString(pszValue, nValueLen)));
         }
         parser.UseCommaSeparator();
         break;
       }
-      case CFX_CSSPrimitiveType::Number: {
+      case CFX_CSSValue::PrimitiveType::kNumber: {
         float fValue;
-        CFX_CSSNumberType eNumType;
-        if (!ParseCSSNumber(pszValue, iValueLen, &fValue, &eNumType))
+        CFX_CSSNumberValue::Unit eNumType;
+        if (!ParseCSSNumber(pszValue, nValueLen, &fValue, &eNumType))
           break;
-        if (eType == CFX_CSSPrimitiveType::Number) {
-          switch ((int32_t)fValue) {
+        if (eType == CFX_CSSValue::PrimitiveType::kNumber) {
+          switch (static_cast<int32_t>(fValue)) {
             case 100:
             case 200:
             case 300:
@@ -587,9 +591,10 @@
             case 700:
             case 800:
             case 900:
-              if (!pWeight)
+              if (!pWeight) {
                 pWeight = pdfium::MakeRetain<CFX_CSSNumberValue>(
-                    CFX_CSSNumberType::Number, fValue);
+                    CFX_CSSNumberValue::Unit::kNumber, fValue);
+              }
               continue;
           }
         }
@@ -630,9 +635,10 @@
   AddPropertyHolder(CFX_CSSProperty::FontWeight, pWeight, bImportant);
   AddPropertyHolder(CFX_CSSProperty::FontSize, pFontSize, bImportant);
   AddPropertyHolder(CFX_CSSProperty::LineHeight, pLineHeight, bImportant);
-  if (!familyList.empty()) {
-    auto pList = pdfium::MakeRetain<CFX_CSSValueList>(familyList);
-    AddPropertyHolder(CFX_CSSProperty::FontFamily, pList, bImportant);
+  if (!family_list.empty()) {
+    auto value_list =
+        pdfium::MakeRetain<CFX_CSSValueList>(std::move(family_list));
+    AddPropertyHolder(CFX_CSSProperty::FontFamily, value_list, bImportant);
   }
 }
 
diff --git a/core/fxcrt/css/cfx_cssdeclaration.h b/core/fxcrt/css/cfx_cssdeclaration.h
index f3d87cc..eef58e0 100644
--- a/core/fxcrt/css/cfx_cssdeclaration.h
+++ b/core/fxcrt/css/cfx_cssdeclaration.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "core/fxcrt/css/cfx_cssdata.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CFX_CSSPropertyHolder;
 class CFX_CSSCustomProperty;
@@ -23,11 +24,11 @@
       std::vector<std::unique_ptr<CFX_CSSCustomProperty>>::const_iterator;
 
   static bool ParseCSSString(const wchar_t* pszValue,
-                             int32_t iValueLen,
-                             int32_t* iOffset,
-                             int32_t* iLength);
+                             size_t nValueLen,
+                             size_t* nOffset,
+                             size_t* nLength);
   static bool ParseCSSColor(const wchar_t* pszValue,
-                            int32_t iValueLen,
+                            size_t nValueLen,
                             FX_ARGB* dwColor);
 
   CFX_CSSDeclaration();
@@ -52,19 +53,19 @@
   size_t PropertyCountForTesting() const;
 
   FX_ARGB ParseColorForTest(const wchar_t* pszValue,
-                            int32_t iValueLen,
+                            size_t nValueLen,
                             FX_ARGB* dwColor) const;
 
  private:
   void ParseFontProperty(const wchar_t* pszValue,
-                         int32_t iValueLen,
+                         size_t nValueLen,
                          bool bImportant);
   bool ParseBorderProperty(const wchar_t* pszValue,
-                           int32_t iValueLen,
+                           size_t nValueLen,
                            RetainPtr<CFX_CSSValue>& pWidth) const;
   void ParseValueListProperty(const CFX_CSSData::Property* pProperty,
                               const wchar_t* pszValue,
-                              int32_t iValueLen,
+                              size_t nValueLen,
                               bool bImportant);
   void Add4ValuesProperty(const std::vector<RetainPtr<CFX_CSSValue>>& list,
                           bool bImportant,
@@ -73,12 +74,11 @@
                           CFX_CSSProperty eRight,
                           CFX_CSSProperty eBottom);
   RetainPtr<CFX_CSSValue> ParseNumber(const wchar_t* pszValue,
-                                      int32_t iValueLen);
-  RetainPtr<CFX_CSSValue> ParseEnum(const wchar_t* pszValue, int32_t iValueLen);
-  RetainPtr<CFX_CSSValue> ParseColor(const wchar_t* pszValue,
-                                     int32_t iValueLen);
+                                      size_t nValueLen);
+  RetainPtr<CFX_CSSValue> ParseEnum(const wchar_t* pszValue, size_t nValueLen);
+  RetainPtr<CFX_CSSValue> ParseColor(const wchar_t* pszValue, size_t nValueLen);
   RetainPtr<CFX_CSSValue> ParseString(const wchar_t* pszValue,
-                                      int32_t iValueLen);
+                                      size_t nValueLen);
   void AddPropertyHolder(CFX_CSSProperty eProperty,
                          RetainPtr<CFX_CSSValue> pValue,
                          bool bImportant);
diff --git a/core/fxcrt/css/cfx_cssdeclaration_unittest.cpp b/core/fxcrt/css/cfx_cssdeclaration_unittest.cpp
index f4a032a..5868063 100644
--- a/core/fxcrt/css/cfx_cssdeclaration_unittest.cpp
+++ b/core/fxcrt/css/cfx_cssdeclaration_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxcrt/css/cfx_cssenumvalue.cpp b/core/fxcrt/css/cfx_cssenumvalue.cpp
index c9b39be..0e7b3cd 100644
--- a/core/fxcrt/css/cfx_cssenumvalue.cpp
+++ b/core/fxcrt/css/cfx_cssenumvalue.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,6 @@
 #include "core/fxcrt/css/cfx_cssenumvalue.h"
 
 CFX_CSSEnumValue::CFX_CSSEnumValue(CFX_CSSPropertyValue value)
-    : CFX_CSSValue(CFX_CSSPrimitiveType::Enum), value_(value) {}
+    : CFX_CSSValue(PrimitiveType::kEnum), value_(value) {}
 
-CFX_CSSEnumValue::~CFX_CSSEnumValue() {}
+CFX_CSSEnumValue::~CFX_CSSEnumValue() = default;
diff --git a/core/fxcrt/css/cfx_cssenumvalue.h b/core/fxcrt/css/cfx_cssenumvalue.h
index c397761..fe9961c 100644
--- a/core/fxcrt/css/cfx_cssenumvalue.h
+++ b/core/fxcrt/css/cfx_cssenumvalue.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxcrt/css/cfx_cssexttextbuf.cpp b/core/fxcrt/css/cfx_cssexttextbuf.cpp
deleted file mode 100644
index 287dc2e..0000000
--- a/core/fxcrt/css/cfx_cssexttextbuf.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/css/cfx_cssexttextbuf.h"
-
-CFX_CSSExtTextBuf::CFX_CSSExtTextBuf()
-    : m_pExtBuffer(nullptr), m_iDatLen(0), m_iDatPos(0) {}
-
-CFX_CSSExtTextBuf::~CFX_CSSExtTextBuf() {}
-
-void CFX_CSSExtTextBuf::AttachBuffer(const wchar_t* pBuffer, int32_t iBufLen) {
-  m_pExtBuffer = pBuffer;
-  m_iDatLen = iBufLen;
-}
diff --git a/core/fxcrt/css/cfx_cssexttextbuf.h b/core/fxcrt/css/cfx_cssexttextbuf.h
deleted file mode 100644
index 342b91e..0000000
--- a/core/fxcrt/css/cfx_cssexttextbuf.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_CSS_CFX_CSSEXTTEXTBUF_H_
-#define CORE_FXCRT_CSS_CFX_CSSEXTTEXTBUF_H_
-
-#include "core/fxcrt/fx_system.h"
-
-class CFX_CSSExtTextBuf {
- public:
-  CFX_CSSExtTextBuf();
-  ~CFX_CSSExtTextBuf();
-
-  void AttachBuffer(const wchar_t* pBuffer, int32_t iBufLen);
-
-  bool IsEOF() const { return m_iDatPos >= m_iDatLen; }
-
-  wchar_t GetChar() const { return m_pExtBuffer[m_iDatPos]; }
-  wchar_t GetNextChar() const {
-    return (m_iDatPos + 1 >= m_iDatLen) ? 0 : m_pExtBuffer[m_iDatPos + 1];
-  }
-
-  void MoveNext() { m_iDatPos++; }
-
- protected:
-  const wchar_t* m_pExtBuffer;
-  int32_t m_iDatLen;
-  int32_t m_iDatPos;
-};
-
-#endif  // CORE_FXCRT_CSS_CFX_CSSEXTTEXTBUF_H_
diff --git a/core/fxcrt/css/cfx_cssinputtextbuf.cpp b/core/fxcrt/css/cfx_cssinputtextbuf.cpp
new file mode 100644
index 0000000..3554f74
--- /dev/null
+++ b/core/fxcrt/css/cfx_cssinputtextbuf.cpp
@@ -0,0 +1,11 @@
+// Copyright 2017 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/css/cfx_cssinputtextbuf.h"
+
+CFX_CSSInputTextBuf::CFX_CSSInputTextBuf(WideStringView str) : m_Buffer(str) {}
+
+CFX_CSSInputTextBuf::~CFX_CSSInputTextBuf() = default;
diff --git a/core/fxcrt/css/cfx_cssinputtextbuf.h b/core/fxcrt/css/cfx_cssinputtextbuf.h
new file mode 100644
index 0000000..e702e1b
--- /dev/null
+++ b/core/fxcrt/css/cfx_cssinputtextbuf.h
@@ -0,0 +1,29 @@
+// Copyright 2017 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCRT_CSS_CFX_CSSINPUTTEXTBUF_H_
+#define CORE_FXCRT_CSS_CFX_CSSINPUTTEXTBUF_H_
+
+#include "core/fxcrt/widestring.h"
+
+class CFX_CSSInputTextBuf {
+ public:
+  explicit CFX_CSSInputTextBuf(WideStringView str);
+  ~CFX_CSSInputTextBuf();
+
+  bool IsEOF() const { return m_iPos >= m_Buffer.GetLength(); }
+  void MoveNext() { m_iPos++; }
+  wchar_t GetChar() const { return m_Buffer[m_iPos]; }
+  wchar_t GetNextChar() const {
+    return m_iPos + 1 < m_Buffer.GetLength() ? m_Buffer[m_iPos + 1] : 0;
+  }
+
+ protected:
+  const WideStringView m_Buffer;
+  size_t m_iPos = 0;
+};
+
+#endif  // CORE_FXCRT_CSS_CFX_CSSINPUTTEXTBUF_H_
diff --git a/core/fxcrt/css/cfx_cssnumbervalue.cpp b/core/fxcrt/css/cfx_cssnumbervalue.cpp
index d8f7247..72ae775 100644
--- a/core/fxcrt/css/cfx_cssnumbervalue.cpp
+++ b/core/fxcrt/css/cfx_cssnumbervalue.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,33 +6,35 @@
 
 #include "core/fxcrt/css/cfx_cssnumbervalue.h"
 
-CFX_CSSNumberValue::CFX_CSSNumberValue(CFX_CSSNumberType type, float value)
-    : CFX_CSSValue(CFX_CSSPrimitiveType::Number), type_(type), value_(value) {
-  if (type_ == CFX_CSSNumberType::Number && fabs(value_) < 0.001f)
+#include <math.h>
+
+CFX_CSSNumberValue::CFX_CSSNumberValue(Unit unit, float value)
+    : CFX_CSSValue(PrimitiveType::kNumber), unit_(unit), value_(value) {
+  if (unit_ == Unit::kNumber && fabs(value_) < 0.001f)
     value_ = 0.0f;
 }
 
-CFX_CSSNumberValue::~CFX_CSSNumberValue() {}
+CFX_CSSNumberValue::~CFX_CSSNumberValue() = default;
 
 float CFX_CSSNumberValue::Apply(float percentBase) const {
-  switch (type_) {
-    case CFX_CSSNumberType::Pixels:
-    case CFX_CSSNumberType::Number:
+  switch (unit_) {
+    case Unit::kPixels:
+    case Unit::kNumber:
       return value_ * 72 / 96;
-    case CFX_CSSNumberType::EMS:
-    case CFX_CSSNumberType::EXS:
+    case Unit::kEMS:
+    case Unit::kEXS:
       return value_ * percentBase;
-    case CFX_CSSNumberType::Percent:
+    case Unit::kPercent:
       return value_ * percentBase / 100.0f;
-    case CFX_CSSNumberType::CentiMeters:
+    case Unit::kCentiMeters:
       return value_ * 28.3464f;
-    case CFX_CSSNumberType::MilliMeters:
+    case Unit::kMilliMeters:
       return value_ * 2.8346f;
-    case CFX_CSSNumberType::Inches:
+    case Unit::kInches:
       return value_ * 72.0f;
-    case CFX_CSSNumberType::Picas:
+    case Unit::kPicas:
       return value_ / 12.0f;
-    case CFX_CSSNumberType::Points:
+    case Unit::kPoints:
       return value_;
   }
   return value_;
diff --git a/core/fxcrt/css/cfx_cssnumbervalue.h b/core/fxcrt/css/cfx_cssnumbervalue.h
index a977750..3b2556b 100644
--- a/core/fxcrt/css/cfx_cssnumbervalue.h
+++ b/core/fxcrt/css/cfx_cssnumbervalue.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,33 +8,31 @@
 #define CORE_FXCRT_CSS_CFX_CSSNUMBERVALUE_H_
 
 #include "core/fxcrt/css/cfx_cssvalue.h"
-#include "core/fxcrt/fx_system.h"
-
-enum class CFX_CSSNumberType {
-  Number,
-  Percent,
-  EMS,
-  EXS,
-  Pixels,
-  CentiMeters,
-  MilliMeters,
-  Inches,
-  Points,
-  Picas,
-};
 
 class CFX_CSSNumberValue final : public CFX_CSSValue {
  public:
-  CFX_CSSNumberValue(CFX_CSSNumberType type, float value);
+  enum class Unit {
+    kNumber,
+    kPercent,
+    kEMS,
+    kEXS,
+    kPixels,
+    kCentiMeters,
+    kMilliMeters,
+    kInches,
+    kPoints,
+    kPicas,
+  };
+
+  CFX_CSSNumberValue(Unit unit, float value);
   ~CFX_CSSNumberValue() override;
 
-  float Value() const { return value_; }
-  CFX_CSSNumberType Kind() const { return type_; }
-
+  Unit unit() const { return unit_; }
+  float value() const { return value_; }
   float Apply(float percentBase) const;
 
  private:
-  CFX_CSSNumberType type_;
+  Unit unit_;
   float value_;
 };
 
diff --git a/core/fxcrt/css/cfx_cssoutputtextbuf.cpp b/core/fxcrt/css/cfx_cssoutputtextbuf.cpp
new file mode 100644
index 0000000..a57d41a
--- /dev/null
+++ b/core/fxcrt/css/cfx_cssoutputtextbuf.cpp
@@ -0,0 +1,28 @@
+// Copyright 2017 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/css/cfx_cssoutputtextbuf.h"
+
+CFX_CSSOutputTextBuf::CFX_CSSOutputTextBuf() {
+  m_Buffer.reserve(32);
+}
+
+CFX_CSSOutputTextBuf::~CFX_CSSOutputTextBuf() = default;
+
+void CFX_CSSOutputTextBuf::AppendCharIfNotLeadingBlank(wchar_t wch) {
+  if (m_Buffer.empty() && wch <= ' ')
+    return;
+
+  m_Buffer.push_back(wch);
+}
+
+WideStringView CFX_CSSOutputTextBuf::GetTrailingBlankTrimmedString() const {
+  WideStringView result(m_Buffer);
+  while (!result.IsEmpty() && result.Back() <= ' ')
+    result = result.First(result.GetLength() - 1);
+
+  return result;
+}
diff --git a/core/fxcrt/css/cfx_cssoutputtextbuf.h b/core/fxcrt/css/cfx_cssoutputtextbuf.h
new file mode 100644
index 0000000..8cbb59e
--- /dev/null
+++ b/core/fxcrt/css/cfx_cssoutputtextbuf.h
@@ -0,0 +1,27 @@
+// Copyright 2017 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCRT_CSS_CFX_CSSOUTPUTTEXTBUF_H_
+#define CORE_FXCRT_CSS_CFX_CSSOUTPUTTEXTBUF_H_
+
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/widestring.h"
+
+class CFX_CSSOutputTextBuf {
+ public:
+  CFX_CSSOutputTextBuf();
+  ~CFX_CSSOutputTextBuf();
+
+  void Clear() { m_Buffer.clear(); }
+  bool IsEmpty() const { return m_Buffer.empty(); }
+  void AppendCharIfNotLeadingBlank(wchar_t wch);
+  WideStringView GetTrailingBlankTrimmedString() const;
+
+ protected:
+  DataVector<wchar_t> m_Buffer;
+};
+
+#endif  // CORE_FXCRT_CSS_CFX_CSSOUTPUTTEXTBUF_H_
diff --git a/core/fxcrt/css/cfx_csspropertyholder.cpp b/core/fxcrt/css/cfx_csspropertyholder.cpp
index 11e0d4b..d19a10c 100644
--- a/core/fxcrt/css/cfx_csspropertyholder.cpp
+++ b/core/fxcrt/css/cfx_csspropertyholder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,6 @@
 
 #include "core/fxcrt/css/cfx_csspropertyholder.h"
 
-CFX_CSSPropertyHolder::CFX_CSSPropertyHolder() {}
+CFX_CSSPropertyHolder::CFX_CSSPropertyHolder() = default;
 
-CFX_CSSPropertyHolder::~CFX_CSSPropertyHolder() {}
+CFX_CSSPropertyHolder::~CFX_CSSPropertyHolder() = default;
diff --git a/core/fxcrt/css/cfx_csspropertyholder.h b/core/fxcrt/css/cfx_csspropertyholder.h
index a271bda..ef5149f 100644
--- a/core/fxcrt/css/cfx_csspropertyholder.h
+++ b/core/fxcrt/css/cfx_csspropertyholder.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxcrt/css/cfx_cssrulecollection.cpp b/core/fxcrt/css/cfx_cssrulecollection.cpp
index f525821..89b6852 100644
--- a/core/fxcrt/css/cfx_cssrulecollection.cpp
+++ b/core/fxcrt/css/cfx_cssrulecollection.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,43 +14,35 @@
 #include "core/fxcrt/css/cfx_cssstylerule.h"
 #include "core/fxcrt/css/cfx_cssstylesheet.h"
 #include "core/fxcrt/css/cfx_csssyntaxparser.h"
-#include "third_party/base/ptr_util.h"
 
-CFX_CSSRuleCollection::CFX_CSSRuleCollection() : m_iSelectors(0) {}
+CFX_CSSRuleCollection::CFX_CSSRuleCollection() = default;
 
-CFX_CSSRuleCollection::~CFX_CSSRuleCollection() {
-  Clear();
-}
-
-void CFX_CSSRuleCollection::Clear() {
-  m_TagRules.clear();
-  m_iSelectors = 0;
-}
+CFX_CSSRuleCollection::~CFX_CSSRuleCollection() = default;
 
 const std::vector<std::unique_ptr<CFX_CSSRuleCollection::Data>>*
 CFX_CSSRuleCollection::GetTagRuleData(const WideString& tagname) const {
-  auto it = m_TagRules.find(FX_HashCode_GetW(tagname.AsStringView(), true));
+  auto it = m_TagRules.find(FX_HashCode_GetLoweredW(tagname.AsStringView()));
   return it != m_TagRules.end() ? &it->second : nullptr;
 }
 
-void CFX_CSSRuleCollection::AddRulesFrom(const CFX_CSSStyleSheet* sheet) {
-  int32_t iRules = sheet->CountRules();
-  for (int32_t j = 0; j < iRules; j++)
-    AddRulesFrom(sheet, sheet->GetRule(j));
+void CFX_CSSRuleCollection::SetRulesFromSheet(const CFX_CSSStyleSheet* sheet) {
+  m_TagRules.clear();
+  for (size_t i = 0; i < sheet->CountRules(); ++i)
+    AddRule(sheet->GetRule(i));
 }
 
-void CFX_CSSRuleCollection::AddRulesFrom(const CFX_CSSStyleSheet* pStyleSheet,
-                                         CFX_CSSStyleRule* pStyleRule) {
+void CFX_CSSRuleCollection::AddRule(CFX_CSSStyleRule* pStyleRule) {
   CFX_CSSDeclaration* pDeclaration = pStyleRule->GetDeclaration();
-  int32_t iSelectors = pStyleRule->CountSelectorLists();
-  for (int32_t i = 0; i < iSelectors; ++i) {
+  size_t nSelectors = pStyleRule->CountSelectorLists();
+  for (size_t i = 0; i < nSelectors; ++i) {
     CFX_CSSSelector* pSelector = pStyleRule->GetSelectorList(i);
-    m_TagRules[pSelector->GetNameHash()].push_back(
-        pdfium::MakeUnique<Data>(pSelector, pDeclaration));
-    m_iSelectors++;
+    m_TagRules[pSelector->name_hash()].push_back(
+        std::make_unique<Data>(pSelector, pDeclaration));
   }
 }
 
 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 72ae58c..125fdd2 100644
--- a/core/fxcrt/css/cfx_cssrulecollection.h
+++ b/core/fxcrt/css/cfx_cssrulecollection.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,8 @@
 #include <memory>
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
 
 class CFX_CSSDeclaration;
 class CFX_CSSSelector;
@@ -23,27 +24,24 @@
   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();
   ~CFX_CSSRuleCollection();
 
-  void AddRulesFrom(const CFX_CSSStyleSheet* sheet);
-  void Clear();
-  int32_t CountSelectors() const { return m_iSelectors; }
+  void SetRulesFromSheet(const CFX_CSSStyleSheet* sheet);
 
   const std::vector<std::unique_ptr<Data>>* GetTagRuleData(
       const WideString& tagname) const;
 
  private:
-  void AddRulesFrom(const CFX_CSSStyleSheet* pStyleSheet,
-                    CFX_CSSStyleRule* pRule);
+  void AddRule(CFX_CSSStyleRule* pRule);
 
   std::map<uint32_t, std::vector<std::unique_ptr<Data>>> m_TagRules;
-  int32_t m_iSelectors;
 };
 
 #endif  // CORE_FXCRT_CSS_CFX_CSSRULECOLLECTION_H_
diff --git a/core/fxcrt/css/cfx_cssselector.cpp b/core/fxcrt/css/cfx_cssselector.cpp
index cd90f62..f10bf5f 100644
--- a/core/fxcrt/css/cfx_cssselector.cpp
+++ b/core/fxcrt/css/cfx_cssselector.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,54 +9,34 @@
 #include <utility>
 
 #include "core/fxcrt/fx_extension.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 namespace {
 
-int32_t GetCSSNameLen(const wchar_t* psz, const wchar_t* pEnd) {
-  const wchar_t* pStart = psz;
-  while (psz < pEnd) {
-    if (!isascii(*psz) || (!isalnum(*psz) && *psz != '_' && *psz != '-')) {
-      break;
-    }
-    ++psz;
+size_t GetCSSNameLen(WideStringView str) {
+  for (size_t i = 0; i < str.GetLength(); ++i) {
+    wchar_t wch = str[i];
+    if (!isascii(wch) || (!isalnum(wch) && wch != '_' && wch != '-'))
+      return i;
   }
-  return psz - pStart;
+  return str.GetLength();
 }
 
 }  // namespace
 
-CFX_CSSSelector::CFX_CSSSelector(CFX_CSSSelectorType eType,
-                                 const wchar_t* psz,
-                                 int32_t iLen,
-                                 bool bIgnoreCase)
-    : m_eType(eType),
-      m_dwHash(FX_HashCode_GetW(WideStringView(psz, iLen), bIgnoreCase)) {}
+CFX_CSSSelector::CFX_CSSSelector(WideStringView str,
+                                 std::unique_ptr<CFX_CSSSelector> next)
+    : name_hash_(FX_HashCode_GetLoweredW(str)), next_(std::move(next)) {}
 
-CFX_CSSSelector::~CFX_CSSSelector() {}
-
-CFX_CSSSelectorType CFX_CSSSelector::GetType() const {
-  return m_eType;
-}
-
-uint32_t CFX_CSSSelector::GetNameHash() const {
-  return m_dwHash;
-}
-
-CFX_CSSSelector* CFX_CSSSelector::GetNextSelector() const {
-  return m_pNext.get();
-}
+CFX_CSSSelector::~CFX_CSSSelector() = default;
 
 // static.
 std::unique_ptr<CFX_CSSSelector> CFX_CSSSelector::FromString(
     WideStringView str) {
-  ASSERT(!str.IsEmpty());
+  DCHECK(!str.IsEmpty());
 
-  const wchar_t* psz = str.unterminated_c_str();
-  const wchar_t* pStart = psz;
-  const wchar_t* pEnd = psz + str.GetLength();
-  for (; psz < pEnd; ++psz) {
-    switch (*psz) {
+  for (wchar_t wch : str) {
+    switch (wch) {
       case '>':
       case '[':
       case '+':
@@ -64,24 +44,26 @@
     }
   }
 
-  std::unique_ptr<CFX_CSSSelector> pFirst = nullptr;
-  for (psz = pStart; psz < pEnd;) {
-    wchar_t wch = *psz;
-    if ((isascii(wch) && isalpha(wch)) || wch == '*') {
-      int32_t iNameLen = wch == '*' ? 1 : GetCSSNameLen(psz, pEnd);
-      auto p = pdfium::MakeUnique<CFX_CSSSelector>(CFX_CSSSelectorType::Element,
-                                                   psz, iNameLen, true);
-      if (pFirst) {
-        pFirst->SetType(CFX_CSSSelectorType::Descendant);
-        p->SetNext(std::move(pFirst));
-      }
-      pFirst = std::move(p);
-      psz += iNameLen;
-    } else if (wch == ' ') {
-      psz++;
-    } else {
-      return nullptr;
+  std::unique_ptr<CFX_CSSSelector> head;
+  for (size_t i = 0; i < str.GetLength();) {
+    wchar_t wch = str[i];
+    if (wch == ' ') {
+      ++i;
+      continue;
     }
+
+    const bool is_star = wch == '*';
+    const bool is_valid_char = is_star || (isascii(wch) && isalpha(wch));
+    if (!is_valid_char)
+      return nullptr;
+
+    if (head)
+      head->set_is_descendant();
+    size_t len = is_star ? 1 : GetCSSNameLen(str.Last(str.GetLength() - i));
+    auto new_head =
+        std::make_unique<CFX_CSSSelector>(str.Substr(i, len), std::move(head));
+    head = std::move(new_head);
+    i += len;
   }
-  return pFirst;
+  return head;
 }
diff --git a/core/fxcrt/css/cfx_cssselector.h b/core/fxcrt/css/cfx_cssselector.h
index 14b61ee..553c829 100644
--- a/core/fxcrt/css/cfx_cssselector.h
+++ b/core/fxcrt/css/cfx_cssselector.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,35 +8,26 @@
 #define CORE_FXCRT_CSS_CFX_CSSSELECTOR_H_
 
 #include <memory>
-#include <utility>
 
-#include "core/fxcrt/css/cfx_css.h"
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 
 class CFX_CSSSelector {
  public:
   static std::unique_ptr<CFX_CSSSelector> FromString(WideStringView str);
 
-  CFX_CSSSelector(CFX_CSSSelectorType eType,
-                  const wchar_t* psz,
-                  int32_t iLen,
-                  bool bIgnoreCase);
+  CFX_CSSSelector(WideStringView str, std::unique_ptr<CFX_CSSSelector> next);
   ~CFX_CSSSelector();
 
-  CFX_CSSSelectorType GetType() const;
-  uint32_t GetNameHash() const;
-  CFX_CSSSelector* GetNextSelector() const;
-
-  void SetNext(std::unique_ptr<CFX_CSSSelector> pNext) {
-    m_pNext = std::move(pNext);
-  }
+  bool is_descendant() const { return is_descendant_; }
+  uint32_t name_hash() const { return name_hash_; }
+  const CFX_CSSSelector* next_selector() const { return next_.get(); }
 
  private:
-  void SetType(CFX_CSSSelectorType eType) { m_eType = eType; }
+  void set_is_descendant() { is_descendant_ = true; }
 
-  CFX_CSSSelectorType m_eType;
-  uint32_t m_dwHash;
-  std::unique_ptr<CFX_CSSSelector> m_pNext;
+  bool is_descendant_ = false;
+  const uint32_t name_hash_;
+  const std::unique_ptr<CFX_CSSSelector> next_;
 };
 
 #endif  // CORE_FXCRT_CSS_CFX_CSSSELECTOR_H_
diff --git a/core/fxcrt/css/cfx_cssstringvalue.cpp b/core/fxcrt/css/cfx_cssstringvalue.cpp
index 6ff2a33..7fbb6b0 100644
--- a/core/fxcrt/css/cfx_cssstringvalue.cpp
+++ b/core/fxcrt/css/cfx_cssstringvalue.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,6 @@
 #include "core/fxcrt/css/cfx_cssstringvalue.h"
 
 CFX_CSSStringValue::CFX_CSSStringValue(const WideString& value)
-    : CFX_CSSValue(CFX_CSSPrimitiveType::String), value_(value) {}
+    : CFX_CSSValue(PrimitiveType::kString), value_(value) {}
 
-CFX_CSSStringValue::~CFX_CSSStringValue() {}
+CFX_CSSStringValue::~CFX_CSSStringValue() = default;
diff --git a/core/fxcrt/css/cfx_cssstringvalue.h b/core/fxcrt/css/cfx_cssstringvalue.h
index b59cef0..afc2d31 100644
--- a/core/fxcrt/css/cfx_cssstringvalue.h
+++ b/core/fxcrt/css/cfx_cssstringvalue.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxcrt/css/cfx_cssstylerule.cpp b/core/fxcrt/css/cfx_cssstylerule.cpp
index 504771e..9e8c3ed 100644
--- a/core/fxcrt/css/cfx_cssstylerule.cpp
+++ b/core/fxcrt/css/cfx_cssstylerule.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,15 +6,17 @@
 
 #include "core/fxcrt/css/cfx_cssstylerule.h"
 
-CFX_CSSStyleRule::CFX_CSSStyleRule() {}
+#include "third_party/base/check.h"
 
-CFX_CSSStyleRule::~CFX_CSSStyleRule() {}
+CFX_CSSStyleRule::CFX_CSSStyleRule() = default;
+
+CFX_CSSStyleRule::~CFX_CSSStyleRule() = default;
 
 size_t CFX_CSSStyleRule::CountSelectorLists() const {
   return m_ppSelector.size();
 }
 
-CFX_CSSSelector* CFX_CSSStyleRule::GetSelectorList(int32_t index) const {
+CFX_CSSSelector* CFX_CSSStyleRule::GetSelectorList(size_t index) const {
   return m_ppSelector[index].get();
 }
 
@@ -24,7 +26,6 @@
 
 void CFX_CSSStyleRule::SetSelector(
     std::vector<std::unique_ptr<CFX_CSSSelector>>* list) {
-  ASSERT(m_ppSelector.empty());
-
+  DCHECK(m_ppSelector.empty());
   m_ppSelector.swap(*list);
 }
diff --git a/core/fxcrt/css/cfx_cssstylerule.h b/core/fxcrt/css/cfx_cssstylerule.h
index bba1fc5..a731d19 100644
--- a/core/fxcrt/css/cfx_cssstylerule.h
+++ b/core/fxcrt/css/cfx_cssstylerule.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -19,7 +19,7 @@
   ~CFX_CSSStyleRule();
 
   size_t CountSelectorLists() const;
-  CFX_CSSSelector* GetSelectorList(int32_t index) const;
+  CFX_CSSSelector* GetSelectorList(size_t index) const;
   CFX_CSSDeclaration* GetDeclaration();
 
   void SetSelector(std::vector<std::unique_ptr<CFX_CSSSelector>>* list);
diff --git a/core/fxcrt/css/cfx_cssstyleselector.cpp b/core/fxcrt/css/cfx_cssstyleselector.cpp
index f6b48bd..864dae8 100644
--- a/core/fxcrt/css/cfx_cssstyleselector.cpp
+++ b/core/fxcrt/css/cfx_cssstyleselector.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -19,20 +19,21 @@
 #include "core/fxcrt/css/cfx_cssstylesheet.h"
 #include "core/fxcrt/css/cfx_csssyntaxparser.h"
 #include "core/fxcrt/css/cfx_cssvaluelist.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/adapters.h"
+#include "third_party/base/notreached.h"
 
-CFX_CSSStyleSelector::CFX_CSSStyleSelector() : m_fDefFontSize(12.0f) {}
+CFX_CSSStyleSelector::CFX_CSSStyleSelector() = default;
 
-CFX_CSSStyleSelector::~CFX_CSSStyleSelector() {}
+CFX_CSSStyleSelector::~CFX_CSSStyleSelector() = default;
 
-void CFX_CSSStyleSelector::SetDefFontSize(float fFontSize) {
-  ASSERT(fFontSize > 0);
-  m_fDefFontSize = fFontSize;
+void CFX_CSSStyleSelector::SetDefaultFontSize(float fFontSize) {
+  DCHECK(fFontSize > 0);
+  m_fDefaultFontSize = fFontSize;
 }
 
 RetainPtr<CFX_CSSComputedStyle> CFX_CSSStyleSelector::CreateComputedStyle(
-    CFX_CSSComputedStyle* pParentStyle) {
+    const CFX_CSSComputedStyle* pParentStyle) {
   auto pStyle = pdfium::MakeRetain<CFX_CSSComputedStyle>();
   if (pParentStyle)
     pStyle->m_InheritedData = pParentStyle->m_InheritedData;
@@ -45,14 +46,13 @@
 }
 
 void CFX_CSSStyleSelector::UpdateStyleIndex() {
-  m_UARules.Clear();
-  m_UARules.AddRulesFrom(m_UAStyles.get());
+  m_UARules.SetRulesFromSheet(m_UAStyles.get());
 }
 
 std::vector<const CFX_CSSDeclaration*> CFX_CSSStyleSelector::MatchDeclarations(
     const WideString& tagname) {
   std::vector<const CFX_CSSDeclaration*> matchedDecls;
-  if (m_UARules.CountSelectors() == 0 || tagname.IsEmpty())
+  if (tagname.IsEmpty())
     return matchedDecls;
 
   auto* rules = m_UARules.GetTagRuleData(tagname);
@@ -71,11 +71,9 @@
   // TODO(dsinclair): The code only supports a single level of selector at this
   // point. None of the code using selectors required the complexity so lets
   // just say we don't support them to simplify the code for now.
-  if (!pSel || pSel->GetNextSelector() ||
-      pSel->GetType() == CFX_CSSSelectorType::Descendant) {
+  if (!pSel || pSel->next_selector() || pSel->is_descendant())
     return false;
-  }
-  return pSel->GetNameHash() == FX_HashCode_GetW(tagname.AsStringView(), true);
+  return pSel->name_hash() == FX_HashCode_GetLoweredW(tagname.AsStringView());
 }
 
 void CFX_CSSStyleSelector::ComputeStyle(
@@ -85,7 +83,7 @@
     CFX_CSSComputedStyle* pDest) {
   std::unique_ptr<CFX_CSSDeclaration> pDecl;
   if (!styleString.IsEmpty() || !alignString.IsEmpty()) {
-    pDecl = pdfium::MakeUnique<CFX_CSSDeclaration>();
+    pDecl = std::make_unique<CFX_CSSDeclaration>();
 
     if (!styleString.IsEmpty())
       AppendInlineStyle(pDecl.get(), styleString);
@@ -139,22 +137,23 @@
 
 void CFX_CSSStyleSelector::AppendInlineStyle(CFX_CSSDeclaration* pDecl,
                                              const WideString& style) {
-  ASSERT(pDecl);
-  ASSERT(!style.IsEmpty());
+  DCHECK(pDecl);
+  DCHECK(!style.IsEmpty());
 
-  auto pSyntax = pdfium::MakeUnique<CFX_CSSSyntaxParser>(
-      style.c_str(), style.GetLength(), 32, true);
+  auto pSyntax = std::make_unique<CFX_CSSSyntaxParser>(style.AsStringView());
+  pSyntax->SetParseOnlyDeclarations();
+
   int32_t iLen2 = 0;
   const CFX_CSSData::Property* property = nullptr;
   WideString wsName;
-  while (1) {
-    CFX_CSSSyntaxStatus eStatus = pSyntax->DoSyntaxParse();
-    if (eStatus == CFX_CSSSyntaxStatus::PropertyName) {
+  while (true) {
+    CFX_CSSSyntaxParser::Status eStatus = pSyntax->DoSyntaxParse();
+    if (eStatus == CFX_CSSSyntaxParser::Status::kPropertyName) {
       WideStringView strValue = pSyntax->GetCurrentString();
       property = CFX_CSSData::GetPropertyByName(strValue);
       if (!property)
         wsName = WideString(strValue);
-    } else if (eStatus == CFX_CSSSyntaxStatus::PropertyValue) {
+    } else if (eStatus == CFX_CSSSyntaxParser::Status::kPropertyValue) {
       if (property || iLen2 > 0) {
         WideStringView strValue = pSyntax->GetCurrentString();
         if (!strValue.IsEmpty()) {
@@ -173,30 +172,30 @@
 void CFX_CSSStyleSelector::ApplyProperty(CFX_CSSProperty eProperty,
                                          const RetainPtr<CFX_CSSValue>& pValue,
                                          CFX_CSSComputedStyle* pComputedStyle) {
-  if (pValue->GetType() != CFX_CSSPrimitiveType::List) {
-    CFX_CSSPrimitiveType eType = pValue->GetType();
+  if (pValue->GetType() != CFX_CSSValue::PrimitiveType::kList) {
+    CFX_CSSValue::PrimitiveType eType = pValue->GetType();
     switch (eProperty) {
       case CFX_CSSProperty::Display:
-        if (eType == CFX_CSSPrimitiveType::Enum) {
+        if (eType == CFX_CSSValue::PrimitiveType::kEnum) {
           pComputedStyle->m_NonInheritedData.m_eDisplay =
-              ToDisplay(pValue.As<CFX_CSSEnumValue>()->Value());
+              ToDisplay(pValue.AsRaw<CFX_CSSEnumValue>()->Value());
         }
         break;
       case CFX_CSSProperty::FontSize: {
         float& fFontSize = pComputedStyle->m_InheritedData.m_fFontSize;
-        if (eType == CFX_CSSPrimitiveType::Number) {
-          fFontSize = pValue.As<CFX_CSSNumberValue>()->Apply(fFontSize);
-        } else if (eType == CFX_CSSPrimitiveType::Enum) {
+        if (eType == CFX_CSSValue::PrimitiveType::kNumber) {
+          fFontSize = pValue.AsRaw<CFX_CSSNumberValue>()->Apply(fFontSize);
+        } else if (eType == CFX_CSSValue::PrimitiveType::kEnum) {
           fFontSize =
-              ToFontSize(pValue.As<CFX_CSSEnumValue>()->Value(), fFontSize);
+              ToFontSize(pValue.AsRaw<CFX_CSSEnumValue>()->Value(), fFontSize);
         }
       } break;
       case CFX_CSSProperty::LineHeight:
-        if (eType == CFX_CSSPrimitiveType::Number) {
+        if (eType == CFX_CSSValue::PrimitiveType::kNumber) {
           RetainPtr<CFX_CSSNumberValue> v = pValue.As<CFX_CSSNumberValue>();
-          if (v->Kind() == CFX_CSSNumberType::Number) {
+          if (v->unit() == CFX_CSSNumberValue::Unit::kNumber) {
             pComputedStyle->m_InheritedData.m_fLineHeight =
-                v->Value() * pComputedStyle->m_InheritedData.m_fFontSize;
+                v->value() * pComputedStyle->m_InheritedData.m_fFontSize;
           } else {
             pComputedStyle->m_InheritedData.m_fLineHeight =
                 v->Apply(pComputedStyle->m_InheritedData.m_fFontSize);
@@ -204,9 +203,9 @@
         }
         break;
       case CFX_CSSProperty::TextAlign:
-        if (eType == CFX_CSSPrimitiveType::Enum) {
+        if (eType == CFX_CSSValue::PrimitiveType::kEnum) {
           pComputedStyle->m_InheritedData.m_eTextAlign =
-              ToTextAlign(pValue.As<CFX_CSSEnumValue>()->Value());
+              ToTextAlign(pValue.AsRaw<CFX_CSSEnumValue>()->Value());
         }
         break;
       case CFX_CSSProperty::TextIndent:
@@ -215,27 +214,28 @@
                              pComputedStyle->m_InheritedData.m_fFontSize);
         break;
       case CFX_CSSProperty::FontWeight:
-        if (eType == CFX_CSSPrimitiveType::Enum) {
+        if (eType == CFX_CSSValue::PrimitiveType::kEnum) {
           pComputedStyle->m_InheritedData.m_wFontWeight =
-              ToFontWeight(pValue.As<CFX_CSSEnumValue>()->Value());
-        } else if (eType == CFX_CSSPrimitiveType::Number) {
-          int32_t iValue =
-              (int32_t)pValue.As<CFX_CSSNumberValue>()->Value() / 100;
+              ToFontWeight(pValue.AsRaw<CFX_CSSEnumValue>()->Value());
+        } else if (eType == CFX_CSSValue::PrimitiveType::kNumber) {
+          int32_t iValue = static_cast<int32_t>(
+                               pValue.AsRaw<CFX_CSSNumberValue>()->value()) /
+                           100;
           if (iValue >= 1 && iValue <= 9) {
             pComputedStyle->m_InheritedData.m_wFontWeight = iValue * 100;
           }
         }
         break;
       case CFX_CSSProperty::FontStyle:
-        if (eType == CFX_CSSPrimitiveType::Enum) {
+        if (eType == CFX_CSSValue::PrimitiveType::kEnum) {
           pComputedStyle->m_InheritedData.m_eFontStyle =
-              ToFontStyle(pValue.As<CFX_CSSEnumValue>()->Value());
+              ToFontStyle(pValue.AsRaw<CFX_CSSEnumValue>()->Value());
         }
         break;
       case CFX_CSSProperty::Color:
-        if (eType == CFX_CSSPrimitiveType::RGB) {
+        if (eType == CFX_CSSValue::PrimitiveType::kRGB) {
           pComputedStyle->m_InheritedData.m_dwFontColor =
-              pValue.As<CFX_CSSColorValue>()->Value();
+              pValue.AsRaw<CFX_CSSColorValue>()->Value();
         }
         break;
       case CFX_CSSProperty::MarginLeft:
@@ -323,30 +323,30 @@
         }
         break;
       case CFX_CSSProperty::VerticalAlign:
-        if (eType == CFX_CSSPrimitiveType::Enum) {
-          pComputedStyle->m_NonInheritedData.m_eVerticalAlign =
-              ToVerticalAlign(pValue.As<CFX_CSSEnumValue>()->Value());
-        } else if (eType == CFX_CSSPrimitiveType::Number) {
-          pComputedStyle->m_NonInheritedData.m_eVerticalAlign =
+        if (eType == CFX_CSSValue::PrimitiveType::kEnum) {
+          pComputedStyle->m_NonInheritedData.m_eVerticalAlignType =
+              ToVerticalAlign(pValue.AsRaw<CFX_CSSEnumValue>()->Value());
+        } else if (eType == CFX_CSSValue::PrimitiveType::kNumber) {
+          pComputedStyle->m_NonInheritedData.m_eVerticalAlignType =
               CFX_CSSVerticalAlign::Number;
           pComputedStyle->m_NonInheritedData.m_fVerticalAlign =
-              pValue.As<CFX_CSSNumberValue>()->Apply(
+              pValue.AsRaw<CFX_CSSNumberValue>()->Apply(
                   pComputedStyle->m_InheritedData.m_fFontSize);
         }
         break;
       case CFX_CSSProperty::FontVariant:
-        if (eType == CFX_CSSPrimitiveType::Enum) {
+        if (eType == CFX_CSSValue::PrimitiveType::kEnum) {
           pComputedStyle->m_InheritedData.m_eFontVariant =
-              ToFontVariant(pValue.As<CFX_CSSEnumValue>()->Value());
+              ToFontVariant(pValue.AsRaw<CFX_CSSEnumValue>()->Value());
         }
         break;
       case CFX_CSSProperty::LetterSpacing:
-        if (eType == CFX_CSSPrimitiveType::Enum) {
+        if (eType == CFX_CSSValue::PrimitiveType::kEnum) {
           pComputedStyle->m_InheritedData.m_LetterSpacing.Set(
               CFX_CSSLengthUnit::Normal);
-        } else if (eType == CFX_CSSPrimitiveType::Number) {
-          if (pValue.As<CFX_CSSNumberValue>()->Kind() ==
-              CFX_CSSNumberType::Percent) {
+        } else if (eType == CFX_CSSValue::PrimitiveType::kNumber) {
+          if (pValue.AsRaw<CFX_CSSNumberValue>()->unit() ==
+              CFX_CSSNumberValue::Unit::kPercent) {
             break;
           }
 
@@ -356,12 +356,12 @@
         }
         break;
       case CFX_CSSProperty::WordSpacing:
-        if (eType == CFX_CSSPrimitiveType::Enum) {
+        if (eType == CFX_CSSValue::PrimitiveType::kEnum) {
           pComputedStyle->m_InheritedData.m_WordSpacing.Set(
               CFX_CSSLengthUnit::Normal);
-        } else if (eType == CFX_CSSPrimitiveType::Number) {
-          if (pValue.As<CFX_CSSNumberValue>()->Kind() ==
-              CFX_CSSNumberType::Percent) {
+        } else if (eType == CFX_CSSValue::PrimitiveType::kNumber) {
+          if (pValue.AsRaw<CFX_CSSNumberValue>()->unit() ==
+              CFX_CSSNumberValue::Unit::kPercent) {
             break;
           }
           SetLengthWithPercent(pComputedStyle->m_InheritedData.m_WordSpacing,
@@ -392,17 +392,16 @@
       default:
         break;
     }
-  } else if (pValue->GetType() == CFX_CSSPrimitiveType::List) {
-    RetainPtr<CFX_CSSValueList> pList = pValue.As<CFX_CSSValueList>();
-    int32_t iCount = pList->CountValues();
-    if (iCount > 0) {
+  } else if (pValue->GetType() == CFX_CSSValue::PrimitiveType::kList) {
+    RetainPtr<CFX_CSSValueList> value_list = pValue.As<CFX_CSSValueList>();
+    if (!value_list->values().empty()) {
       switch (eProperty) {
         case CFX_CSSProperty::FontFamily:
-          pComputedStyle->m_InheritedData.m_pFontFamily = pList;
+          pComputedStyle->m_InheritedData.m_pFontFamily = std::move(value_list);
           break;
         case CFX_CSSProperty::TextDecoration:
           pComputedStyle->m_NonInheritedData.m_dwTextDecoration =
-              ToTextDecoration(pList);
+              ToTextDecoration(value_list);
           break;
         default:
           break;
@@ -473,22 +472,22 @@
 
 bool CFX_CSSStyleSelector::SetLengthWithPercent(
     CFX_CSSLength& width,
-    CFX_CSSPrimitiveType eType,
+    CFX_CSSValue::PrimitiveType eType,
     const RetainPtr<CFX_CSSValue>& pValue,
     float fFontSize) {
-  if (eType == CFX_CSSPrimitiveType::Number) {
+  if (eType == CFX_CSSValue::PrimitiveType::kNumber) {
     RetainPtr<CFX_CSSNumberValue> v = pValue.As<CFX_CSSNumberValue>();
-    if (v->Kind() == CFX_CSSNumberType::Percent) {
+    if (v->unit() == CFX_CSSNumberValue::Unit::kPercent) {
       width.Set(CFX_CSSLengthUnit::Percent,
-                pValue.As<CFX_CSSNumberValue>()->Value() / 100.0f);
+                pValue.AsRaw<CFX_CSSNumberValue>()->value() / 100.0f);
       return width.NonZero();
     }
 
     float fValue = v->Apply(fFontSize);
     width.Set(CFX_CSSLengthUnit::Point, fValue);
     return width.NonZero();
-  } else if (eType == CFX_CSSPrimitiveType::Enum) {
-    switch (pValue.As<CFX_CSSEnumValue>()->Value()) {
+  } else if (eType == CFX_CSSValue::PrimitiveType::kEnum) {
+    switch (pValue.AsRaw<CFX_CSSEnumValue>()->Value()) {
       case CFX_CSSPropertyValue::Auto:
         width.Set(CFX_CSSLengthUnit::Auto);
         return true;
@@ -515,19 +514,19 @@
                                        float fCurFontSize) {
   switch (eValue) {
     case CFX_CSSPropertyValue::XxSmall:
-      return m_fDefFontSize / 1.2f / 1.2f / 1.2f;
+      return m_fDefaultFontSize / 1.2f / 1.2f / 1.2f;
     case CFX_CSSPropertyValue::XSmall:
-      return m_fDefFontSize / 1.2f / 1.2f;
+      return m_fDefaultFontSize / 1.2f / 1.2f;
     case CFX_CSSPropertyValue::Small:
-      return m_fDefFontSize / 1.2f;
+      return m_fDefaultFontSize / 1.2f;
     case CFX_CSSPropertyValue::Medium:
-      return m_fDefFontSize;
+      return m_fDefaultFontSize;
     case CFX_CSSPropertyValue::Large:
-      return m_fDefFontSize * 1.2f;
+      return m_fDefaultFontSize * 1.2f;
     case CFX_CSSPropertyValue::XLarge:
-      return m_fDefFontSize * 1.2f * 1.2f;
+      return m_fDefaultFontSize * 1.2f * 1.2f;
     case CFX_CSSPropertyValue::XxLarge:
-      return m_fDefFontSize * 1.2f * 1.2f * 1.2f;
+      return m_fDefaultFontSize * 1.2f * 1.2f * 1.2f;
     case CFX_CSSPropertyValue::Larger:
       return fCurFontSize * 1.2f;
     case CFX_CSSPropertyValue::Smaller:
@@ -560,29 +559,29 @@
   }
 }
 
-uint32_t CFX_CSSStyleSelector::ToTextDecoration(
+Mask<CFX_CSSTEXTDECORATION> CFX_CSSStyleSelector::ToTextDecoration(
     const RetainPtr<CFX_CSSValueList>& pValue) {
-  uint32_t dwDecoration = 0;
-  for (int32_t i = pValue->CountValues() - 1; i >= 0; --i) {
-    const RetainPtr<CFX_CSSValue> pVal = pValue->GetValue(i);
-    if (pVal->GetType() != CFX_CSSPrimitiveType::Enum)
+  Mask<CFX_CSSTEXTDECORATION> dwDecoration;
+  for (const RetainPtr<CFX_CSSValue>& val :
+       pdfium::base::Reversed(pValue->values())) {
+    if (val->GetType() != CFX_CSSValue::PrimitiveType::kEnum)
       continue;
 
-    switch (pVal.As<CFX_CSSEnumValue>()->Value()) {
+    switch (val.AsRaw<CFX_CSSEnumValue>()->Value()) {
       case CFX_CSSPropertyValue::Underline:
-        dwDecoration |= CFX_CSSTEXTDECORATION_Underline;
+        dwDecoration |= CFX_CSSTEXTDECORATION::kUnderline;
         break;
       case CFX_CSSPropertyValue::LineThrough:
-        dwDecoration |= CFX_CSSTEXTDECORATION_LineThrough;
+        dwDecoration |= CFX_CSSTEXTDECORATION::kLineThrough;
         break;
       case CFX_CSSPropertyValue::Overline:
-        dwDecoration |= CFX_CSSTEXTDECORATION_Overline;
+        dwDecoration |= CFX_CSSTEXTDECORATION::kOverline;
         break;
       case CFX_CSSPropertyValue::Blink:
-        dwDecoration |= CFX_CSSTEXTDECORATION_Blink;
+        dwDecoration |= CFX_CSSTEXTDECORATION::kBlink;
         break;
       case CFX_CSSPropertyValue::Double:
-        dwDecoration |= CFX_CSSTEXTDECORATION_Double;
+        dwDecoration |= CFX_CSSTEXTDECORATION::kDouble;
         break;
       default:
         break;
diff --git a/core/fxcrt/css/cfx_cssstyleselector.h b/core/fxcrt/css/cfx_cssstyleselector.h
index 13a0f74..7ff694f 100644
--- a/core/fxcrt/css/cfx_cssstyleselector.h
+++ b/core/fxcrt/css/cfx_cssstyleselector.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,16 @@
 #ifndef CORE_FXCRT_CSS_CFX_CSSSTYLESELECTOR_H_
 #define CORE_FXCRT_CSS_CFX_CSSSTYLESELECTOR_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
 #include "core/fxcrt/css/cfx_css.h"
 #include "core/fxcrt/css/cfx_cssrulecollection.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/css/cfx_cssvalue.h"
+#include "core/fxcrt/mask.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CFX_CSSComputedStyle;
 class CFX_CSSCustomProperty;
@@ -20,7 +24,6 @@
 class CFX_CSSPropertyHolder;
 class CFX_CSSSelector;
 class CFX_CSSStyleSheet;
-class CFX_CSSValue;
 class CFX_CSSValueList;
 
 class CFX_CSSStyleSelector {
@@ -28,12 +31,12 @@
   CFX_CSSStyleSelector();
   ~CFX_CSSStyleSelector();
 
-  void SetDefFontSize(float fFontSize);
+  void SetDefaultFontSize(float fFontSize);
   void SetUAStyleSheet(std::unique_ptr<CFX_CSSStyleSheet> pSheet);
   void UpdateStyleIndex();
 
   RetainPtr<CFX_CSSComputedStyle> CreateComputedStyle(
-      CFX_CSSComputedStyle* pParentStyle);
+      const CFX_CSSComputedStyle* pParentStyle);
 
   // Note, the dest style has to be an out param because the CXFA_TextParser
   // adds non-inherited data from the parent style. Attempting to copy
@@ -63,7 +66,7 @@
                      std::vector<const CFX_CSSCustomProperty*>* custom);
 
   bool SetLengthWithPercent(CFX_CSSLength& width,
-                            CFX_CSSPrimitiveType eType,
+                            CFX_CSSValue::PrimitiveType eType,
                             const RetainPtr<CFX_CSSValue>& pValue,
                             float fFontSize);
   float ToFontSize(CFX_CSSPropertyValue eValue, float fCurFontSize);
@@ -72,10 +75,11 @@
   uint16_t ToFontWeight(CFX_CSSPropertyValue eValue);
   CFX_CSSFontStyle ToFontStyle(CFX_CSSPropertyValue eValue);
   CFX_CSSVerticalAlign ToVerticalAlign(CFX_CSSPropertyValue eValue);
-  uint32_t ToTextDecoration(const RetainPtr<CFX_CSSValueList>& pList);
+  Mask<CFX_CSSTEXTDECORATION> ToTextDecoration(
+      const RetainPtr<CFX_CSSValueList>& pList);
   CFX_CSSFontVariant ToFontVariant(CFX_CSSPropertyValue eValue);
 
-  float m_fDefFontSize;
+  float m_fDefaultFontSize = 12.0f;
   std::unique_ptr<CFX_CSSStyleSheet> m_UAStyles;
   CFX_CSSRuleCollection m_UARules;
 };
diff --git a/core/fxcrt/css/cfx_cssstylesheet.cpp b/core/fxcrt/css/cfx_cssstylesheet.cpp
index c7300fb..b888311 100644
--- a/core/fxcrt/css/cfx_cssstylesheet.cpp
+++ b/core/fxcrt/css/cfx_cssstylesheet.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,75 +12,57 @@
 #include "core/fxcrt/css/cfx_cssdeclaration.h"
 #include "core/fxcrt/css/cfx_cssstylerule.h"
 #include "core/fxcrt/fx_codepage.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
 
-CFX_CSSStyleSheet::CFX_CSSStyleSheet() {}
+CFX_CSSStyleSheet::CFX_CSSStyleSheet() = default;
 
-CFX_CSSStyleSheet::~CFX_CSSStyleSheet() {
-  Reset();
+CFX_CSSStyleSheet::~CFX_CSSStyleSheet() = default;
+
+size_t CFX_CSSStyleSheet::CountRules() const {
+  return m_RuleArray.size();
 }
 
-void CFX_CSSStyleSheet::Reset() {
-  m_RuleArray.clear();
-  m_StringCache.clear();
-}
-
-int32_t CFX_CSSStyleSheet::CountRules() const {
-  return pdfium::CollectionSize<int32_t>(m_RuleArray);
-}
-
-CFX_CSSStyleRule* CFX_CSSStyleSheet::GetRule(int32_t index) const {
+CFX_CSSStyleRule* CFX_CSSStyleSheet::GetRule(size_t index) const {
   return m_RuleArray[index].get();
 }
 
-bool CFX_CSSStyleSheet::LoadBuffer(const wchar_t* pBuffer, int32_t iBufSize) {
-  ASSERT(pBuffer);
-  ASSERT(iBufSize > 0);
-
-  auto pSyntax = pdfium::MakeUnique<CFX_CSSSyntaxParser>(pBuffer, iBufSize);
-  Reset();
-  CFX_CSSSyntaxStatus eStatus;
-  do {
-    switch (eStatus = pSyntax->DoSyntaxParse()) {
-      case CFX_CSSSyntaxStatus::StyleRule:
-        eStatus = LoadStyleRule(pSyntax.get(), &m_RuleArray);
-        break;
-      default:
-        break;
-    }
-  } while (eStatus >= CFX_CSSSyntaxStatus::None);
-
-  m_StringCache.clear();
-  return eStatus != CFX_CSSSyntaxStatus::Error;
+bool CFX_CSSStyleSheet::LoadBuffer(WideStringView buffer) {
+  m_RuleArray.clear();
+  auto pSyntax = std::make_unique<CFX_CSSSyntaxParser>(buffer);
+  while (true) {
+    CFX_CSSSyntaxParser::Status eStatus = pSyntax->DoSyntaxParse();
+    if (eStatus == CFX_CSSSyntaxParser::Status::kStyleRule)
+      eStatus = LoadStyleRule(pSyntax.get());
+    if (eStatus == CFX_CSSSyntaxParser::Status::kEOS)
+      return true;
+    if (eStatus == CFX_CSSSyntaxParser::Status::kError)
+      return false;
+  }
 }
 
-CFX_CSSSyntaxStatus CFX_CSSStyleSheet::LoadStyleRule(
-    CFX_CSSSyntaxParser* pSyntax,
-    std::vector<std::unique_ptr<CFX_CSSStyleRule>>* ruleArray) {
+CFX_CSSSyntaxParser::Status CFX_CSSStyleSheet::LoadStyleRule(
+    CFX_CSSSyntaxParser* pSyntax) {
   std::vector<std::unique_ptr<CFX_CSSSelector>> selectors;
-
   CFX_CSSStyleRule* pStyleRule = nullptr;
   int32_t iValueLen = 0;
   const CFX_CSSData::Property* property = nullptr;
   WideString wsName;
-  while (1) {
+  while (true) {
     switch (pSyntax->DoSyntaxParse()) {
-      case CFX_CSSSyntaxStatus::Selector: {
+      case CFX_CSSSyntaxParser::Status::kSelector: {
         WideStringView strValue = pSyntax->GetCurrentString();
         auto pSelector = CFX_CSSSelector::FromString(strValue);
         if (pSelector)
           selectors.push_back(std::move(pSelector));
         break;
       }
-      case CFX_CSSSyntaxStatus::PropertyName: {
+      case CFX_CSSSyntaxParser::Status::kPropertyName: {
         WideStringView strValue = pSyntax->GetCurrentString();
         property = CFX_CSSData::GetPropertyByName(strValue);
         if (!property)
           wsName = WideString(strValue);
         break;
       }
-      case CFX_CSSSyntaxStatus::PropertyValue: {
+      case CFX_CSSSyntaxParser::Status::kPropertyValue: {
         if (property || iValueLen > 0) {
           WideStringView strValue = pSyntax->GetCurrentString();
           auto* decl = pStyleRule->GetDeclaration();
@@ -94,45 +76,45 @@
         }
         break;
       }
-      case CFX_CSSSyntaxStatus::DeclOpen: {
+      case CFX_CSSSyntaxParser::Status::kDeclOpen: {
         if (!pStyleRule && !selectors.empty()) {
-          auto rule = pdfium::MakeUnique<CFX_CSSStyleRule>();
+          auto rule = std::make_unique<CFX_CSSStyleRule>();
           pStyleRule = rule.get();
           pStyleRule->SetSelector(&selectors);
-          ruleArray->push_back(std::move(rule));
+          m_RuleArray.push_back(std::move(rule));
         } else {
           SkipRuleSet(pSyntax);
-          return CFX_CSSSyntaxStatus::None;
+          return CFX_CSSSyntaxParser::Status::kNone;
         }
         break;
       }
-      case CFX_CSSSyntaxStatus::DeclClose: {
+      case CFX_CSSSyntaxParser::Status::kDeclClose: {
         if (pStyleRule && pStyleRule->GetDeclaration()->empty()) {
-          ruleArray->pop_back();
+          m_RuleArray.pop_back();
           pStyleRule = nullptr;
         }
-        return CFX_CSSSyntaxStatus::None;
+        return CFX_CSSSyntaxParser::Status::kNone;
       }
-      case CFX_CSSSyntaxStatus::EOS:
-        return CFX_CSSSyntaxStatus::EOS;
-      case CFX_CSSSyntaxStatus::Error:
+      case CFX_CSSSyntaxParser::Status::kEOS:
+        return CFX_CSSSyntaxParser::Status::kEOS;
+      case CFX_CSSSyntaxParser::Status::kError:
       default:
-        return CFX_CSSSyntaxStatus::Error;
+        return CFX_CSSSyntaxParser::Status::kError;
     }
   }
 }
 
 void CFX_CSSStyleSheet::SkipRuleSet(CFX_CSSSyntaxParser* pSyntax) {
-  while (1) {
+  while (true) {
     switch (pSyntax->DoSyntaxParse()) {
-      case CFX_CSSSyntaxStatus::Selector:
-      case CFX_CSSSyntaxStatus::DeclOpen:
-      case CFX_CSSSyntaxStatus::PropertyName:
-      case CFX_CSSSyntaxStatus::PropertyValue:
+      case CFX_CSSSyntaxParser::Status::kSelector:
+      case CFX_CSSSyntaxParser::Status::kDeclOpen:
+      case CFX_CSSSyntaxParser::Status::kPropertyName:
+      case CFX_CSSSyntaxParser::Status::kPropertyValue:
         break;
-      case CFX_CSSSyntaxStatus::DeclClose:
-      case CFX_CSSSyntaxStatus::EOS:
-      case CFX_CSSSyntaxStatus::Error:
+      case CFX_CSSSyntaxParser::Status::kDeclClose:
+      case CFX_CSSSyntaxParser::Status::kEOS:
+      case CFX_CSSSyntaxParser::Status::kError:
       default:
         return;
     }
diff --git a/core/fxcrt/css/cfx_cssstylesheet.h b/core/fxcrt/css/cfx_cssstylesheet.h
index 3f3a94a..392877e 100644
--- a/core/fxcrt/css/cfx_cssstylesheet.h
+++ b/core/fxcrt/css/cfx_cssstylesheet.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,11 @@
 #ifndef CORE_FXCRT_CSS_CFX_CSSSTYLESHEET_H_
 #define CORE_FXCRT_CSS_CFX_CSSSTYLESHEET_H_
 
-#include <map>
 #include <memory>
 #include <vector>
 
 #include "core/fxcrt/css/cfx_csssyntaxparser.h"
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 
 class CFX_CSSStyleRule;
 
@@ -21,20 +20,15 @@
   CFX_CSSStyleSheet();
   ~CFX_CSSStyleSheet();
 
-  bool LoadBuffer(const wchar_t* pBuffer, int32_t iBufSize);
-
-  int32_t CountRules() const;
-  CFX_CSSStyleRule* GetRule(int32_t index) const;
+  bool LoadBuffer(WideStringView buffer);
+  size_t CountRules() const;
+  CFX_CSSStyleRule* GetRule(size_t index) const;
 
  private:
-  void Reset();
-  CFX_CSSSyntaxStatus LoadStyleRule(
-      CFX_CSSSyntaxParser* pSyntax,
-      std::vector<std::unique_ptr<CFX_CSSStyleRule>>* ruleArray);
+  CFX_CSSSyntaxParser::Status LoadStyleRule(CFX_CSSSyntaxParser* pSyntax);
   void SkipRuleSet(CFX_CSSSyntaxParser* pSyntax);
 
   std::vector<std::unique_ptr<CFX_CSSStyleRule>> m_RuleArray;
-  std::map<uint32_t, wchar_t*> m_StringCache;
 };
 
 #endif  // CORE_FXCRT_CSS_CFX_CSSSTYLESHEET_H_
diff --git a/core/fxcrt/css/cfx_cssstylesheet_unittest.cpp b/core/fxcrt/css/cfx_cssstylesheet_unittest.cpp
index baa002b..627c8e9 100644
--- a/core/fxcrt/css/cfx_cssstylesheet_unittest.cpp
+++ b/core/fxcrt/css/cfx_cssstylesheet_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,177 +15,236 @@
 #include "core/fxcrt/css/cfx_cssstylerule.h"
 #include "core/fxcrt/css/cfx_cssvaluelist.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
 
 class CFX_CSSStyleSheetTest : public testing::Test {
  public:
   void SetUp() override {
-    sheet_ = pdfium::MakeUnique<CFX_CSSStyleSheet>();
+    sheet_ = std::make_unique<CFX_CSSStyleSheet>();
     decl_ = nullptr;
   }
 
   void TearDown() override { decl_ = nullptr; }
 
-  void LoadAndVerifyDecl(const wchar_t* buf,
+  void VerifyLoadFails(WideStringView buf) {
+    DCHECK(sheet_);
+    EXPECT_FALSE(sheet_->LoadBuffer(buf));
+  }
+
+  void LoadAndVerifyRuleCount(WideStringView buf, size_t rule_count) {
+    DCHECK(sheet_);
+    EXPECT_TRUE(sheet_->LoadBuffer(buf));
+    EXPECT_EQ(sheet_->CountRules(), rule_count);
+  }
+
+  void LoadAndVerifyDecl(WideStringView buf,
                          const std::vector<WideString>& selectors,
                          size_t decl_count) {
-    ASSERT(sheet_);
-
-    EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf)));
-    EXPECT_EQ(sheet_->CountRules(), 1);
-
+    LoadAndVerifyRuleCount(buf, 1);
     CFX_CSSStyleRule* style = sheet_->GetRule(0);
+    ASSERT_TRUE(style);
     EXPECT_EQ(selectors.size(), style->CountSelectorLists());
 
     for (size_t i = 0; i < selectors.size(); i++) {
-      uint32_t hash = FX_HashCode_GetW(selectors[i].AsStringView(), true);
-      EXPECT_EQ(hash, style->GetSelectorList(i)->GetNameHash());
+      uint32_t hash = FX_HashCode_GetLoweredW(selectors[i].AsStringView());
+      EXPECT_EQ(hash, style->GetSelectorList(i)->name_hash());
     }
 
     decl_ = style->GetDeclaration();
+    ASSERT_TRUE(decl_);
     EXPECT_EQ(decl_->PropertyCountForTesting(), decl_count);
   }
 
-  void VerifyFloat(CFX_CSSProperty prop, float val, CFX_CSSNumberType type) {
-    ASSERT(decl_);
+  void VerifyFloat(CFX_CSSProperty prop,
+                   float val,
+                   CFX_CSSNumberValue::Unit unit) {
+    DCHECK(decl_);
 
     bool important;
     RetainPtr<CFX_CSSValue> v = decl_->GetProperty(prop, &important);
-    EXPECT_EQ(v->GetType(), CFX_CSSPrimitiveType::Number);
-    EXPECT_EQ(v.As<CFX_CSSNumberValue>()->Kind(), type);
-    EXPECT_EQ(v.As<CFX_CSSNumberValue>()->Value(), val);
+    EXPECT_EQ(v->GetType(), CFX_CSSValue::PrimitiveType::kNumber);
+    EXPECT_EQ(v.AsRaw<CFX_CSSNumberValue>()->unit(), unit);
+    EXPECT_EQ(v.AsRaw<CFX_CSSNumberValue>()->value(), val);
   }
 
   void VerifyEnum(CFX_CSSProperty prop, CFX_CSSPropertyValue val) {
-    ASSERT(decl_);
+    DCHECK(decl_);
 
     bool important;
     RetainPtr<CFX_CSSValue> v = decl_->GetProperty(prop, &important);
-    EXPECT_EQ(v->GetType(), CFX_CSSPrimitiveType::Enum);
-    EXPECT_EQ(v.As<CFX_CSSEnumValue>()->Value(), val);
+    EXPECT_EQ(v->GetType(), CFX_CSSValue::PrimitiveType::kEnum);
+    EXPECT_EQ(v.AsRaw<CFX_CSSEnumValue>()->Value(), val);
   }
 
   void VerifyList(CFX_CSSProperty prop,
-                  std::vector<CFX_CSSPropertyValue> values) {
-    ASSERT(decl_);
+                  std::vector<CFX_CSSPropertyValue> expected_values) {
+    DCHECK(decl_);
 
     bool important;
     RetainPtr<CFX_CSSValueList> list =
         decl_->GetProperty(prop, &important).As<CFX_CSSValueList>();
-    EXPECT_EQ(list->CountValues(), pdfium::CollectionSize<int32_t>(values));
+    ASSERT_TRUE(list);
+    const std::vector<RetainPtr<CFX_CSSValue>>& values = list->values();
+    ASSERT_EQ(values.size(), expected_values.size());
 
-    for (size_t i = 0; i < values.size(); i++) {
-      RetainPtr<CFX_CSSValue> val = list->GetValue(i);
-      EXPECT_EQ(val->GetType(), CFX_CSSPrimitiveType::Enum);
-      EXPECT_EQ(val.As<CFX_CSSEnumValue>()->Value(), values[i]);
+    for (size_t i = 0; i < expected_values.size(); ++i) {
+      const auto& val = values[i];
+      EXPECT_EQ(val->GetType(), CFX_CSSValue::PrimitiveType::kEnum);
+      EXPECT_EQ(val.AsRaw<CFX_CSSEnumValue>()->Value(), expected_values[i]);
     }
   }
 
+  static bool HasSelector(CFX_CSSStyleRule* style, WideStringView selector) {
+    uint32_t hash = FX_HashCode_GetLoweredW(selector);
+    for (size_t i = 0; i < style->CountSelectorLists(); ++i) {
+      if (style->GetSelectorList(i)->name_hash() == hash)
+        return true;
+    }
+    return false;
+  }
+
   std::unique_ptr<CFX_CSSStyleSheet> sheet_;
   CFX_CSSDeclaration* decl_;
 };
 
+TEST_F(CFX_CSSStyleSheetTest, ParseEmpty) {
+  LoadAndVerifyRuleCount(L"", 0);
+}
+
+TEST_F(CFX_CSSStyleSheetTest, ParseBlankEmpty) {
+  LoadAndVerifyRuleCount(L"  \n\r\t", 0);
+}
+
+TEST_F(CFX_CSSStyleSheetTest, ParseStrayClose1) {
+  VerifyLoadFails(L"}");
+}
+
+TEST_F(CFX_CSSStyleSheetTest, ParseStrayClose2) {
+  LoadAndVerifyRuleCount(L"foo }", 0);
+}
+
+TEST_F(CFX_CSSStyleSheetTest, ParseStrayClose3) {
+  VerifyLoadFails(L"foo {a: b}}");
+}
+
+TEST_F(CFX_CSSStyleSheetTest, ParseEmptySelector) {
+  VerifyLoadFails(L"{a: b}");
+}
+
+TEST_F(CFX_CSSStyleSheetTest, ParseEmptyBody) {
+  LoadAndVerifyRuleCount(L"foo {}", 0);
+}
+
 TEST_F(CFX_CSSStyleSheetTest, ParseMultipleSelectors) {
   const wchar_t* buf =
-      L"a { border: 10px; }\nb { text-decoration: underline; }";
-  EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf)));
-  EXPECT_EQ(2, sheet_->CountRules());
+      L"a { border: 10px; }\n"
+      L"bcdef { text-decoration: underline; }\n"
+      L"* { padding: 0; }\n";
+  EXPECT_TRUE(sheet_->LoadBuffer(buf));
+  ASSERT_EQ(3u, sheet_->CountRules());
 
   CFX_CSSStyleRule* style = sheet_->GetRule(0);
-  EXPECT_EQ(1UL, style->CountSelectorLists());
-
-  bool found_selector = false;
-  uint32_t hash = FX_HashCode_GetW(L"a", true);
-  for (size_t i = 0; i < style->CountSelectorLists(); i++) {
-    if (style->GetSelectorList(i)->GetNameHash() == hash) {
-      found_selector = true;
-      break;
-    }
-  }
-  EXPECT_TRUE(found_selector);
+  ASSERT_TRUE(style);
+  EXPECT_EQ(1u, style->CountSelectorLists());
+  EXPECT_TRUE(HasSelector(style, L"a"));
 
   decl_ = style->GetDeclaration();
-  EXPECT_EQ(4UL, decl_->PropertyCountForTesting());
+  ASSERT_TRUE(decl_);
+  EXPECT_EQ(4u, decl_->PropertyCountForTesting());
 
-  VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 10.0,
-              CFX_CSSNumberType::Pixels);
-  VerifyFloat(CFX_CSSProperty::BorderRightWidth, 10.0,
-              CFX_CSSNumberType::Pixels);
-  VerifyFloat(CFX_CSSProperty::BorderTopWidth, 10.0, CFX_CSSNumberType::Pixels);
-  VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 10.0,
-              CFX_CSSNumberType::Pixels);
+  VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 10.0f,
+              CFX_CSSNumberValue::Unit::kPixels);
+  VerifyFloat(CFX_CSSProperty::BorderRightWidth, 10.0f,
+              CFX_CSSNumberValue::Unit::kPixels);
+  VerifyFloat(CFX_CSSProperty::BorderTopWidth, 10.0f,
+              CFX_CSSNumberValue::Unit::kPixels);
+  VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 10.0f,
+              CFX_CSSNumberValue::Unit::kPixels);
 
   style = sheet_->GetRule(1);
-  EXPECT_EQ(1UL, style->CountSelectorLists());
-
-  found_selector = false;
-  hash = FX_HashCode_GetW(L"b", true);
-  for (size_t i = 0; i < style->CountSelectorLists(); i++) {
-    if (style->GetSelectorList(i)->GetNameHash() == hash) {
-      found_selector = true;
-      break;
-    }
-  }
-  EXPECT_TRUE(found_selector);
+  ASSERT_TRUE(style);
+  EXPECT_EQ(1u, style->CountSelectorLists());
+  EXPECT_TRUE(HasSelector(style, L"bcdef"));
+  EXPECT_FALSE(HasSelector(style, L"bcde"));
 
   decl_ = style->GetDeclaration();
-  EXPECT_EQ(1UL, decl_->PropertyCountForTesting());
+  ASSERT_TRUE(decl_);
+  EXPECT_EQ(1u, decl_->PropertyCountForTesting());
   VerifyList(CFX_CSSProperty::TextDecoration,
              {CFX_CSSPropertyValue::Underline});
+
+  style = sheet_->GetRule(2);
+  ASSERT_TRUE(style);
+  EXPECT_EQ(1u, style->CountSelectorLists());
+  EXPECT_TRUE(HasSelector(style, L"*"));
+
+  decl_ = style->GetDeclaration();
+  ASSERT_TRUE(decl_);
+  EXPECT_EQ(4u, decl_->PropertyCountForTesting());
+  VerifyFloat(CFX_CSSProperty::PaddingLeft, 0.0f,
+              CFX_CSSNumberValue::Unit::kNumber);
+  VerifyFloat(CFX_CSSProperty::PaddingRight, 0.0f,
+              CFX_CSSNumberValue::Unit::kNumber);
+  VerifyFloat(CFX_CSSProperty::PaddingTop, 0.0f,
+              CFX_CSSNumberValue::Unit::kNumber);
+  VerifyFloat(CFX_CSSProperty::PaddingBottom, 0.0f,
+              CFX_CSSNumberValue::Unit::kNumber);
 }
 
 TEST_F(CFX_CSSStyleSheetTest, ParseChildSelectors) {
   const wchar_t* buf = L"a b c { border: 10px; }";
-  EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf)));
-  EXPECT_EQ(1, sheet_->CountRules());
+  EXPECT_TRUE(sheet_->LoadBuffer(buf));
+  EXPECT_EQ(1u, sheet_->CountRules());
 
   CFX_CSSStyleRule* style = sheet_->GetRule(0);
-  EXPECT_EQ(1UL, style->CountSelectorLists());
+  ASSERT_TRUE(style);
+  EXPECT_EQ(1u, style->CountSelectorLists());
 
-  auto* sel = style->GetSelectorList(0);
-  EXPECT_TRUE(sel != nullptr);
-  EXPECT_EQ(FX_HashCode_GetW(L"c", true), sel->GetNameHash());
+  const auto* sel = style->GetSelectorList(0);
+  ASSERT_TRUE(sel);
+  EXPECT_EQ(FX_HashCode_GetLoweredW(L"c"), sel->name_hash());
 
-  sel = sel->GetNextSelector();
-  EXPECT_TRUE(sel != nullptr);
-  EXPECT_EQ(FX_HashCode_GetW(L"b", true), sel->GetNameHash());
+  sel = sel->next_selector();
+  ASSERT_TRUE(sel);
+  EXPECT_EQ(FX_HashCode_GetLoweredW(L"b"), sel->name_hash());
 
-  sel = sel->GetNextSelector();
-  EXPECT_TRUE(sel != nullptr);
-  EXPECT_EQ(FX_HashCode_GetW(L"a", true), sel->GetNameHash());
+  sel = sel->next_selector();
+  ASSERT_TRUE(sel);
+  EXPECT_EQ(FX_HashCode_GetLoweredW(L"a"), sel->name_hash());
 
-  sel = sel->GetNextSelector();
-  EXPECT_TRUE(sel == nullptr);
+  sel = sel->next_selector();
+  EXPECT_FALSE(sel);
 
   decl_ = style->GetDeclaration();
-  EXPECT_EQ(4UL, decl_->PropertyCountForTesting());
+  ASSERT_TRUE(decl_);
+  EXPECT_EQ(4u, decl_->PropertyCountForTesting());
 
-  VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 10.0,
-              CFX_CSSNumberType::Pixels);
-  VerifyFloat(CFX_CSSProperty::BorderRightWidth, 10.0,
-              CFX_CSSNumberType::Pixels);
-  VerifyFloat(CFX_CSSProperty::BorderTopWidth, 10.0, CFX_CSSNumberType::Pixels);
-  VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 10.0,
-              CFX_CSSNumberType::Pixels);
+  VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 10.0f,
+              CFX_CSSNumberValue::Unit::kPixels);
+  VerifyFloat(CFX_CSSProperty::BorderRightWidth, 10.0f,
+              CFX_CSSNumberValue::Unit::kPixels);
+  VerifyFloat(CFX_CSSProperty::BorderTopWidth, 10.0f,
+              CFX_CSSNumberValue::Unit::kPixels);
+  VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 10.0f,
+              CFX_CSSNumberValue::Unit::kPixels);
 }
 
 TEST_F(CFX_CSSStyleSheetTest, ParseUnhandledSelectors) {
   const wchar_t* buf = L"a > b { padding: 0; }";
-  EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf)));
-  EXPECT_EQ(0, sheet_->CountRules());
+  EXPECT_TRUE(sheet_->LoadBuffer(buf));
+  EXPECT_EQ(0u, sheet_->CountRules());
 
   buf = L"a[first] { padding: 0; }";
-  EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf)));
-  EXPECT_EQ(0, sheet_->CountRules());
+  EXPECT_TRUE(sheet_->LoadBuffer(buf));
+  EXPECT_EQ(0u, sheet_->CountRules());
 
   buf = L"a+b { padding: 0; }";
-  EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf)));
-  EXPECT_EQ(0, sheet_->CountRules());
+  EXPECT_TRUE(sheet_->LoadBuffer(buf));
+  EXPECT_EQ(0u, sheet_->CountRules());
 
   buf = L"a ^ b { padding: 0; }";
-  EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf)));
-  EXPECT_EQ(0, sheet_->CountRules());
+  EXPECT_TRUE(sheet_->LoadBuffer(buf));
+  EXPECT_EQ(0u, sheet_->CountRules());
 }
 
 TEST_F(CFX_CSSStyleSheetTest, ParseMultipleSelectorsCombined) {
@@ -194,27 +253,32 @@
 
 TEST_F(CFX_CSSStyleSheetTest, ParseBorder) {
   LoadAndVerifyDecl(L"a { border: 5px; }", {L"a"}, 4);
-  VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 5.0, CFX_CSSNumberType::Pixels);
+  VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 5.0,
+              CFX_CSSNumberValue::Unit::kPixels);
   VerifyFloat(CFX_CSSProperty::BorderRightWidth, 5.0,
-              CFX_CSSNumberType::Pixels);
-  VerifyFloat(CFX_CSSProperty::BorderTopWidth, 5.0, CFX_CSSNumberType::Pixels);
+              CFX_CSSNumberValue::Unit::kPixels);
+  VerifyFloat(CFX_CSSProperty::BorderTopWidth, 5.0,
+              CFX_CSSNumberValue::Unit::kPixels);
   VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 5.0,
-              CFX_CSSNumberType::Pixels);
+              CFX_CSSNumberValue::Unit::kPixels);
 }
 
 TEST_F(CFX_CSSStyleSheetTest, ParseBorderFull) {
   LoadAndVerifyDecl(L"a { border: 5px solid red; }", {L"a"}, 4);
-  VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 5.0, CFX_CSSNumberType::Pixels);
+  VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 5.0,
+              CFX_CSSNumberValue::Unit::kPixels);
   VerifyFloat(CFX_CSSProperty::BorderRightWidth, 5.0,
-              CFX_CSSNumberType::Pixels);
-  VerifyFloat(CFX_CSSProperty::BorderTopWidth, 5.0, CFX_CSSNumberType::Pixels);
+              CFX_CSSNumberValue::Unit::kPixels);
+  VerifyFloat(CFX_CSSProperty::BorderTopWidth, 5.0,
+              CFX_CSSNumberValue::Unit::kPixels);
   VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 5.0,
-              CFX_CSSNumberType::Pixels);
+              CFX_CSSNumberValue::Unit::kPixels);
 }
 
 TEST_F(CFX_CSSStyleSheetTest, ParseBorderLeft) {
   LoadAndVerifyDecl(L"a { border-left: 2.5pc; }", {L"a"}, 1);
-  VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 2.5, CFX_CSSNumberType::Picas);
+  VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 2.5,
+              CFX_CSSNumberValue::Unit::kPicas);
 }
 
 TEST_F(CFX_CSSStyleSheetTest, ParseBorderLeftThick) {
@@ -224,16 +288,49 @@
 
 TEST_F(CFX_CSSStyleSheetTest, ParseBorderRight) {
   LoadAndVerifyDecl(L"a { border-right: 2.5pc; }", {L"a"}, 1);
-  VerifyFloat(CFX_CSSProperty::BorderRightWidth, 2.5, CFX_CSSNumberType::Picas);
+  VerifyFloat(CFX_CSSProperty::BorderRightWidth, 2.5,
+              CFX_CSSNumberValue::Unit::kPicas);
 }
 
 TEST_F(CFX_CSSStyleSheetTest, ParseBorderTop) {
   LoadAndVerifyDecl(L"a { border-top: 2.5pc; }", {L"a"}, 1);
-  VerifyFloat(CFX_CSSProperty::BorderTopWidth, 2.5, CFX_CSSNumberType::Picas);
+  VerifyFloat(CFX_CSSProperty::BorderTopWidth, 2.5,
+              CFX_CSSNumberValue::Unit::kPicas);
 }
 
 TEST_F(CFX_CSSStyleSheetTest, ParseBorderBottom) {
   LoadAndVerifyDecl(L"a { border-bottom: 2.5pc; }", {L"a"}, 1);
   VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 2.5,
-              CFX_CSSNumberType::Picas);
+              CFX_CSSNumberValue::Unit::kPicas);
+}
+
+TEST_F(CFX_CSSStyleSheetTest, ParseWithCommentsInSelector) {
+  LoadAndVerifyDecl(L"/**{*/a/**}*/ { border-bottom: 2.5pc; }", {L"a"}, 1);
+  VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 2.5,
+              CFX_CSSNumberValue::Unit::kPicas);
+}
+
+TEST_F(CFX_CSSStyleSheetTest, ParseWithCommentsInProperty) {
+  LoadAndVerifyDecl(L"a { /*}*/border-bottom: 2.5pc; }", {L"a"}, 1);
+  VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 2.5,
+              CFX_CSSNumberValue::Unit::kPicas);
+}
+
+TEST_F(CFX_CSSStyleSheetTest, ParseWithCommentsInValue) {
+  LoadAndVerifyDecl(L"a { border-bottom: /*;*/2.5pc;/* color:red;*/ }", {L"a"},
+                    1);
+  VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 2.5,
+              CFX_CSSNumberValue::Unit::kPicas);
+}
+
+TEST_F(CFX_CSSStyleSheetTest, ParseWithUnterminatedCommentInSelector) {
+  LoadAndVerifyRuleCount(L"a/* { border-bottom: 2.5pc; }", 0);
+}
+
+TEST_F(CFX_CSSStyleSheetTest, ParseWithUnterminatedCommentInProperty) {
+  LoadAndVerifyRuleCount(L"a { /*border-bottom: 2.5pc; }", 1);
+}
+
+TEST_F(CFX_CSSStyleSheetTest, ParseWithUnterminatedCommentInValue) {
+  LoadAndVerifyRuleCount(L"a { border-bottom: /*2.5pc; }", 1);
 }
diff --git a/core/fxcrt/css/cfx_csssyntaxparser.cpp b/core/fxcrt/css/cfx_csssyntaxparser.cpp
index 9cc9991..e5a4d41 100644
--- a/core/fxcrt/css/cfx_csssyntaxparser.cpp
+++ b/core/fxcrt/css/cfx_csssyntaxparser.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,14 +6,10 @@
 
 #include "core/fxcrt/css/cfx_csssyntaxparser.h"
 
-#include <algorithm>
-
 #include "core/fxcrt/css/cfx_cssdata.h"
 #include "core/fxcrt/css/cfx_cssdeclaration.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_extension.h"
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/logging.h"
 
 namespace {
 
@@ -24,215 +20,156 @@
 
 }  // namespace
 
-CFX_CSSSyntaxParser::CFX_CSSSyntaxParser(const wchar_t* pBuffer,
-                                         int32_t iBufferSize)
-    : CFX_CSSSyntaxParser(pBuffer, iBufferSize, 32, false) {}
+CFX_CSSSyntaxParser::CFX_CSSSyntaxParser(WideStringView str) : m_Input(str) {}
 
-CFX_CSSSyntaxParser::CFX_CSSSyntaxParser(const wchar_t* pBuffer,
-                                         int32_t iBufferSize,
-                                         int32_t iTextDatSize,
-                                         bool bOnlyDeclaration)
-    : m_iTextDataLen(0),
-      m_dwCheck(0xFFFFFFFF),
-      m_eStatus(CFX_CSSSyntaxStatus::None) {
-  ASSERT(pBuffer);
-  ASSERT(iBufferSize > 0);
-  ASSERT(iTextDatSize > 0);
+CFX_CSSSyntaxParser::~CFX_CSSSyntaxParser() = default;
 
-  m_eMode = bOnlyDeclaration ? CFX_CSSSyntaxMode::PropertyName
-                             : CFX_CSSSyntaxMode::RuleSet;
-  m_TextData.InitWithSize(iTextDatSize);
-  m_TextPlane.AttachBuffer(pBuffer, iBufferSize);
+void CFX_CSSSyntaxParser::SetParseOnlyDeclarations() {
+  m_eMode = Mode::kPropertyName;
 }
 
-CFX_CSSSyntaxParser::~CFX_CSSSyntaxParser() {}
+CFX_CSSSyntaxParser::Status CFX_CSSSyntaxParser::DoSyntaxParse() {
+  m_Output.Clear();
+  if (m_bHasError)
+    return Status::kError;
 
-CFX_CSSSyntaxStatus CFX_CSSSyntaxParser::DoSyntaxParse() {
-  while (m_eStatus >= CFX_CSSSyntaxStatus::None) {
-    if (m_TextPlane.IsEOF()) {
-      if (m_eMode == CFX_CSSSyntaxMode::PropertyValue &&
-          m_TextData.GetLength() > 0) {
-        SaveTextData();
-        m_eStatus = CFX_CSSSyntaxStatus::PropertyValue;
-        return m_eStatus;
-      }
-      m_eStatus = CFX_CSSSyntaxStatus::EOS;
-      return m_eStatus;
-    }
-    wchar_t wch;
-    while (!m_TextPlane.IsEOF()) {
-      wch = m_TextPlane.GetChar();
-      switch (m_eMode) {
-        case CFX_CSSSyntaxMode::RuleSet:
-          switch (wch) {
-            case '}':
-              m_TextPlane.MoveNext();
-              if (RestoreMode())
-                return CFX_CSSSyntaxStatus::DeclClose;
+  while (!m_Input.IsEOF()) {
+    wchar_t wch = m_Input.GetChar();
+    switch (m_eMode) {
+      case Mode::kRuleSet:
+        switch (wch) {
+          case '}':
+            m_bHasError = true;
+            return Status::kError;
+          case '/':
+            if (m_Input.GetNextChar() == '*') {
+              SaveMode(Mode::kRuleSet);
+              m_eMode = Mode::kComment;
+              break;
+            }
+            [[fallthrough]];
+          default:
+            if (wch <= ' ') {
+              m_Input.MoveNext();
+            } else if (IsSelectorStart(wch)) {
+              m_eMode = Mode::kSelector;
+              return Status::kStyleRule;
+            } else {
+              m_bHasError = true;
+              return Status::kError;
+            }
+            break;
+        }
+        break;
+      case Mode::kSelector:
+        switch (wch) {
+          case ',':
+            m_Input.MoveNext();
+            if (!m_Output.IsEmpty())
+              return Status::kSelector;
+            break;
+          case '{':
+            if (!m_Output.IsEmpty())
+              return Status::kSelector;
+            m_Input.MoveNext();
+            SaveMode(Mode::kRuleSet);  // Back to validate ruleset again.
+            m_eMode = Mode::kPropertyName;
+            return Status::kDeclOpen;
+          case '/':
+            if (m_Input.GetNextChar() == '*') {
+              SaveMode(Mode::kSelector);
+              m_eMode = Mode::kComment;
+              if (!m_Output.IsEmpty())
+                return Status::kSelector;
+              break;
+            }
+            [[fallthrough]];
+          default:
+            m_Output.AppendCharIfNotLeadingBlank(wch);
+            m_Input.MoveNext();
+            break;
+        }
+        break;
+      case Mode::kPropertyName:
+        switch (wch) {
+          case ':':
+            m_Input.MoveNext();
+            m_eMode = Mode::kPropertyValue;
+            return Status::kPropertyName;
+          case '}':
+            m_Input.MoveNext();
+            if (!RestoreMode())
+              return Status::kError;
 
-              m_eStatus = CFX_CSSSyntaxStatus::Error;
-              return m_eStatus;
-            case '/':
-              if (m_TextPlane.GetNextChar() == '*') {
-                m_ModeStack.push(m_eMode);
-                SwitchMode(CFX_CSSSyntaxMode::Comment);
-                break;
-              }
-              FALLTHROUGH;
-            default:
-              if (wch <= ' ') {
-                m_TextPlane.MoveNext();
-              } else if (IsSelectorStart(wch)) {
-                SwitchMode(CFX_CSSSyntaxMode::Selector);
-                return CFX_CSSSyntaxStatus::StyleRule;
-              } else {
-                m_eStatus = CFX_CSSSyntaxStatus::Error;
-                return m_eStatus;
-              }
+            return Status::kDeclClose;
+          case '/':
+            if (m_Input.GetNextChar() == '*') {
+              SaveMode(Mode::kPropertyName);
+              m_eMode = Mode::kComment;
+              if (!m_Output.IsEmpty())
+                return Status::kPropertyName;
               break;
-          }
-          break;
-        case CFX_CSSSyntaxMode::Selector:
-          switch (wch) {
-            case ',':
-              m_TextPlane.MoveNext();
-              SwitchMode(CFX_CSSSyntaxMode::Selector);
-              if (m_iTextDataLen > 0)
-                return CFX_CSSSyntaxStatus::Selector;
+            }
+            [[fallthrough]];
+          default:
+            m_Output.AppendCharIfNotLeadingBlank(wch);
+            m_Input.MoveNext();
+            break;
+        }
+        break;
+      case Mode::kPropertyValue:
+        switch (wch) {
+          case ';':
+            m_Input.MoveNext();
+            [[fallthrough]];
+          case '}':
+            m_eMode = Mode::kPropertyName;
+            return Status::kPropertyValue;
+          case '/':
+            if (m_Input.GetNextChar() == '*') {
+              SaveMode(Mode::kPropertyValue);
+              m_eMode = Mode::kComment;
+              if (!m_Output.IsEmpty())
+                return Status::kPropertyValue;
               break;
-            case '{':
-              if (m_TextData.GetLength() > 0) {
-                SaveTextData();
-                return CFX_CSSSyntaxStatus::Selector;
-              }
-              m_TextPlane.MoveNext();
-              m_ModeStack.push(CFX_CSSSyntaxMode::RuleSet);
-              SwitchMode(CFX_CSSSyntaxMode::PropertyName);
-              return CFX_CSSSyntaxStatus::DeclOpen;
-            case '/':
-              if (m_TextPlane.GetNextChar() == '*') {
-                if (SwitchToComment() > 0)
-                  return CFX_CSSSyntaxStatus::Selector;
-                break;
-              }
-              FALLTHROUGH;
-            default:
-              AppendChar(wch);
-              break;
-          }
-          break;
-        case CFX_CSSSyntaxMode::PropertyName:
-          switch (wch) {
-            case ':':
-              m_TextPlane.MoveNext();
-              SwitchMode(CFX_CSSSyntaxMode::PropertyValue);
-              return CFX_CSSSyntaxStatus::PropertyName;
-            case '}':
-              m_TextPlane.MoveNext();
-              if (RestoreMode())
-                return CFX_CSSSyntaxStatus::DeclClose;
-
-              m_eStatus = CFX_CSSSyntaxStatus::Error;
-              return m_eStatus;
-            case '/':
-              if (m_TextPlane.GetNextChar() == '*') {
-                if (SwitchToComment() > 0)
-                  return CFX_CSSSyntaxStatus::PropertyName;
-                break;
-              }
-              FALLTHROUGH;
-            default:
-              AppendChar(wch);
-              break;
-          }
-          break;
-        case CFX_CSSSyntaxMode::PropertyValue:
-          switch (wch) {
-            case ';':
-              m_TextPlane.MoveNext();
-              FALLTHROUGH;
-            case '}':
-              SwitchMode(CFX_CSSSyntaxMode::PropertyName);
-              return CFX_CSSSyntaxStatus::PropertyValue;
-            case '/':
-              if (m_TextPlane.GetNextChar() == '*') {
-                if (SwitchToComment() > 0)
-                  return CFX_CSSSyntaxStatus::PropertyValue;
-                break;
-              }
-              FALLTHROUGH;
-            default:
-              AppendChar(wch);
-              break;
-          }
-          break;
-        case CFX_CSSSyntaxMode::Comment:
-          if (wch == '/' && m_TextData.GetLength() > 0 &&
-              m_TextData.GetBuffer()[m_TextData.GetLength() - 1] == '*') {
-            RestoreMode();
-          } else {
-            m_TextData.AppendChar(wch);
-          }
-          m_TextPlane.MoveNext();
-          break;
-        case CFX_CSSSyntaxMode::UnknownRule:
-          if (wch == ';')
-            SwitchMode(CFX_CSSSyntaxMode::RuleSet);
-          m_TextPlane.MoveNext();
-          break;
-        default:
-          NOTREACHED();
-          break;
-      }
+            }
+            [[fallthrough]];
+          default:
+            m_Output.AppendCharIfNotLeadingBlank(wch);
+            m_Input.MoveNext();
+            break;
+        }
+        break;
+      case Mode::kComment:
+        if (wch == '*' && m_Input.GetNextChar() == '/') {
+          if (!RestoreMode())
+            return Status::kError;
+          m_Input.MoveNext();
+        }
+        m_Input.MoveNext();
+        break;
     }
   }
-  return m_eStatus;
+  if (m_eMode == Mode::kPropertyValue && !m_Output.IsEmpty())
+    return Status::kPropertyValue;
+
+  return Status::kEOS;
 }
 
-bool CFX_CSSSyntaxParser::IsImportEnabled() const {
-  if ((m_dwCheck & CFX_CSSSYNTAXCHECK_AllowImport) == 0)
-    return false;
-  if (m_ModeStack.size() > 1)
-    return false;
-  return true;
-}
-
-bool CFX_CSSSyntaxParser::AppendChar(wchar_t wch) {
-  m_TextPlane.MoveNext();
-  if (m_TextData.GetLength() > 0 || wch > ' ') {
-    m_TextData.AppendChar(wch);
-    return true;
-  }
-  return false;
-}
-
-int32_t CFX_CSSSyntaxParser::SaveTextData() {
-  m_iTextDataLen = m_TextData.TrimEnd();
-  m_TextData.Clear();
-  return m_iTextDataLen;
-}
-
-void CFX_CSSSyntaxParser::SwitchMode(CFX_CSSSyntaxMode eMode) {
-  m_eMode = eMode;
-  SaveTextData();
-}
-
-int32_t CFX_CSSSyntaxParser::SwitchToComment() {
-  int32_t iLength = m_TextData.GetLength();
-  m_ModeStack.push(m_eMode);
-  SwitchMode(CFX_CSSSyntaxMode::Comment);
-  return iLength;
+void CFX_CSSSyntaxParser::SaveMode(Mode mode) {
+  m_ModeStack.push(mode);
 }
 
 bool CFX_CSSSyntaxParser::RestoreMode() {
-  if (m_ModeStack.empty())
+  if (m_ModeStack.empty()) {
+    m_bHasError = true;
     return false;
-
-  SwitchMode(m_ModeStack.top());
+  }
+  m_eMode = m_ModeStack.top();
   m_ModeStack.pop();
   return true;
 }
 
 WideStringView CFX_CSSSyntaxParser::GetCurrentString() const {
-  return WideStringView(m_TextData.GetBuffer(), m_iTextDataLen);
+  return m_Output.GetTrailingBlankTrimmedString();
 }
diff --git a/core/fxcrt/css/cfx_csssyntaxparser.h b/core/fxcrt/css/cfx_csssyntaxparser.h
index 778f9a3..1f66f30 100644
--- a/core/fxcrt/css/cfx_csssyntaxparser.h
+++ b/core/fxcrt/css/cfx_csssyntaxparser.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,67 +9,48 @@
 
 #include <stack>
 
-#include "core/fxcrt/css/cfx_cssexttextbuf.h"
-#include "core/fxcrt/css/cfx_csstextbuf.h"
-#include "core/fxcrt/fx_string.h"
-
-#define CFX_CSSSYNTAXCHECK_AllowCharset 1
-#define CFX_CSSSYNTAXCHECK_AllowImport 2
-
-enum class CFX_CSSSyntaxMode {
-  RuleSet,
-  Comment,
-  UnknownRule,
-  Selector,
-  PropertyName,
-  PropertyValue,
-};
-
-enum class CFX_CSSSyntaxStatus : uint8_t {
-  Error,
-  EOS,
-  None,
-  StyleRule,
-  Selector,
-  DeclOpen,
-  DeclClose,
-  PropertyName,
-  PropertyValue,
-};
+#include "core/fxcrt/css/cfx_cssinputtextbuf.h"
+#include "core/fxcrt/css/cfx_cssoutputtextbuf.h"
+#include "core/fxcrt/widestring.h"
 
 class CFX_CSSSyntaxParser {
  public:
-  CFX_CSSSyntaxParser(const wchar_t* pBuffer, int32_t iBufferSize);
-  CFX_CSSSyntaxParser(const wchar_t* pBuffer,
-                      int32_t iBufferSize,
-                      int32_t iTextDatSize,
-                      bool bOnlyDeclaration);
+  enum class Status : uint8_t {
+    kError,
+    kEOS,
+    kNone,
+    kStyleRule,
+    kSelector,
+    kDeclOpen,
+    kDeclClose,
+    kPropertyName,
+    kPropertyValue,
+  };
+
+  explicit CFX_CSSSyntaxParser(WideStringView str);
   ~CFX_CSSSyntaxParser();
 
-  CFX_CSSSyntaxStatus DoSyntaxParse();
+  void SetParseOnlyDeclarations();
+  Status DoSyntaxParse();
   WideStringView GetCurrentString() const;
 
- protected:
-  void SwitchMode(CFX_CSSSyntaxMode eMode);
-  int32_t SwitchToComment();
+ private:
+  enum class Mode : uint8_t {
+    kRuleSet,
+    kComment,
+    kSelector,
+    kPropertyName,
+    kPropertyValue,
+  };
 
+  void SaveMode(Mode eMode);
   bool RestoreMode();
-  bool AppendChar(wchar_t wch);
-  int32_t SaveTextData();
-  bool IsCharsetEnabled() const {
-    return (m_dwCheck & CFX_CSSSYNTAXCHECK_AllowCharset) != 0;
-  }
-  void DisableCharset() { m_dwCheck = CFX_CSSSYNTAXCHECK_AllowImport; }
-  bool IsImportEnabled() const;
-  void DisableImport() { m_dwCheck = 0; }
 
-  CFX_CSSTextBuf m_TextData;
-  CFX_CSSExtTextBuf m_TextPlane;
-  int32_t m_iTextDataLen;
-  uint32_t m_dwCheck;
-  CFX_CSSSyntaxMode m_eMode;
-  CFX_CSSSyntaxStatus m_eStatus;
-  std::stack<CFX_CSSSyntaxMode> m_ModeStack;
+  bool m_bHasError = false;
+  Mode m_eMode = Mode::kRuleSet;
+  CFX_CSSOutputTextBuf m_Output;
+  CFX_CSSInputTextBuf m_Input;
+  std::stack<Mode> m_ModeStack;
 };
 
 #endif  // CORE_FXCRT_CSS_CFX_CSSSYNTAXPARSER_H_
diff --git a/core/fxcrt/css/cfx_csssyntaxparser_unittest.cpp b/core/fxcrt/css/cfx_csssyntaxparser_unittest.cpp
new file mode 100644
index 0000000..74157f2
--- /dev/null
+++ b/core/fxcrt/css/cfx_csssyntaxparser_unittest.cpp
@@ -0,0 +1,227 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/css/cfx_csssyntaxparser.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+// These tests cover the "declaration only" mode of the CSS Syntax Parser
+// (i.e. inline style="" attribute). The cfx_cssstylesheet_unitttest.cpp
+// file covers the full-up selector { ... } parsing.
+
+TEST(CSSSyntaxParserTest, ParseEmpty) {
+  const wchar_t* input = L"";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
+}
+
+TEST(CSSSyntaxParserTest, ParseBlank) {
+  const wchar_t* input = L"  \n\r\t";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
+}
+
+TEST(CSSSyntaxParserTest, ParseMissingColon) {
+  const wchar_t* input = L"foo ";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
+}
+
+TEST(CSSSyntaxParserTest, ParseMissingValue) {
+  const wchar_t* input = L"foo: ";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
+  EXPECT_EQ(L"foo", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
+}
+
+TEST(CSSSyntaxParserTest, ParseSingleProp1) {
+  const wchar_t* input = L"foo:bar";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
+  EXPECT_EQ(L"foo", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
+            parser.DoSyntaxParse());
+  EXPECT_EQ(L"bar", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
+}
+
+TEST(CSSSyntaxParserTest, ParseSingleProp2) {
+  const wchar_t* input = L"foo:bar;";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
+  EXPECT_EQ(L"foo", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
+            parser.DoSyntaxParse());
+  EXPECT_EQ(L"bar", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
+}
+
+TEST(CSSSyntaxParserTest, ParseMissingColonMultiple) {
+  const wchar_t* input = L"foo:bar; baz";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
+  EXPECT_EQ(L"foo", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
+            parser.DoSyntaxParse());
+  EXPECT_EQ(L"bar", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
+}
+
+TEST(CSSSyntaxParserTest, ParseMissingPropertyMultiple) {
+  const wchar_t* input = L"foo:bar; baz: ";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
+  EXPECT_EQ(L"foo", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
+            parser.DoSyntaxParse());
+  EXPECT_EQ(L"bar", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
+  EXPECT_EQ(L"baz", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
+}
+
+TEST(CSSSyntaxParserTest, ParseMultipleProp1) {
+  const wchar_t* input = L"foo : bar; baz : bam";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
+  EXPECT_EQ(L"foo", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
+            parser.DoSyntaxParse());
+  EXPECT_EQ(L"bar", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
+  EXPECT_EQ(L"baz", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
+            parser.DoSyntaxParse());
+  EXPECT_EQ(L"bam", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
+}
+
+TEST(CSSSyntaxParserTest, ParseMultipleProp2) {
+  const wchar_t* input = L"foo:bar;baz:bam;";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
+  EXPECT_EQ(L"foo", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
+            parser.DoSyntaxParse());
+  EXPECT_EQ(L"bar", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
+  EXPECT_EQ(L"baz", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
+            parser.DoSyntaxParse());
+  EXPECT_EQ(L"bam", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
+}
+
+TEST(CSSSyntaxParserTest, ParseOpenBrace1) {
+  const wchar_t* input = L"{a:3}";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+
+  // TODO(tsepez): these should fail on stray punctuation.
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
+  EXPECT_EQ(L"{a", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
+            parser.DoSyntaxParse());
+  EXPECT_EQ(L"3", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse());
+}
+
+TEST(CSSSyntaxParserTest, ParseOpenBrace2) {
+  const wchar_t* input = L"foo {a:3}";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+
+  // TODO(tsepez): these should fail on stray punctuation.
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
+  EXPECT_EQ(L"foo {a", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
+            parser.DoSyntaxParse());
+  EXPECT_EQ(L"3", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse());
+}
+
+TEST(CSSSyntaxParserTest, ParseOpenBrace3) {
+  const wchar_t* input = L"foo: bar {a:3}";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+
+  // TODO(tsepez): these should fail on stray punctuation.
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
+  EXPECT_EQ(L"foo", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
+            parser.DoSyntaxParse());
+  EXPECT_EQ(L"bar {a:3", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse());
+}
+
+TEST(CSSSyntaxParserTest, ParseOpenBrace4) {
+  const wchar_t* input = L"foo: bar; {a:3}";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
+  EXPECT_EQ(L"foo", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
+            parser.DoSyntaxParse());
+  EXPECT_EQ(L"bar", parser.GetCurrentString());
+
+  // TODO(tsepez): these should fail on stray punctuation.
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
+  EXPECT_EQ(L"{a", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
+            parser.DoSyntaxParse());
+  EXPECT_EQ(L"3", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse());
+}
+
+TEST(CSSSyntaxParserTest, ParseCloseBrace1) {
+  const wchar_t* input = L"} foo:bar";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse());
+}
+
+TEST(CSSSyntaxParserTest, ParseCloseBrace2) {
+  const wchar_t* input = L"foo}:bar";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse());
+}
+
+TEST(CSSSyntaxParserTest, ParseCloseBrace3) {
+  const wchar_t* input = L"foo:bar}";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
+  EXPECT_EQ(L"foo", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
+            parser.DoSyntaxParse());
+  EXPECT_EQ(L"bar", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse());
+}
+
+TEST(CSSSyntaxParserTest, ParseCloseBrace4) {
+  const wchar_t* input = L"foo:bar;}";
+  CFX_CSSSyntaxParser parser(input);
+  parser.SetParseOnlyDeclarations();
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
+  EXPECT_EQ(L"foo", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
+            parser.DoSyntaxParse());
+  EXPECT_EQ(L"bar", parser.GetCurrentString());
+  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse());
+}
diff --git a/core/fxcrt/css/cfx_csstextbuf.cpp b/core/fxcrt/css/cfx_csstextbuf.cpp
deleted file mode 100644
index f2f3b94..0000000
--- a/core/fxcrt/css/cfx_csstextbuf.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/css/cfx_csstextbuf.h"
-
-#include "core/fxcrt/fx_memory.h"
-
-CFX_CSSTextBuf::CFX_CSSTextBuf()
-    : m_pBuffer(nullptr), m_iBufLen(0), m_iDatLen(0) {}
-
-CFX_CSSTextBuf::~CFX_CSSTextBuf() {
-  FX_Free(m_pBuffer);
-  m_pBuffer = nullptr;
-  m_iDatLen = m_iBufLen;
-}
-
-void CFX_CSSTextBuf::InitWithSize(int32_t iAllocSize) {
-  ExpandBuf(iAllocSize);
-}
-
-void CFX_CSSTextBuf::AppendChar(wchar_t wch) {
-  if (m_iDatLen >= m_iBufLen)
-    ExpandBuf(m_iBufLen * 2);
-
-  m_pBuffer[m_iDatLen++] = wch;
-}
-
-int32_t CFX_CSSTextBuf::TrimEnd() {
-  while (m_iDatLen > 0 && m_pBuffer[m_iDatLen - 1] <= ' ')
-    --m_iDatLen;
-  AppendChar(0);
-  return --m_iDatLen;
-}
-
-void CFX_CSSTextBuf::ExpandBuf(int32_t iDesiredSize) {
-  ASSERT(iDesiredSize > 0);
-  if (m_pBuffer && m_iBufLen == iDesiredSize)
-    return;
-
-  if (m_pBuffer)
-    m_pBuffer = FX_Realloc(wchar_t, m_pBuffer, iDesiredSize);
-  else
-    m_pBuffer = FX_Alloc(wchar_t, iDesiredSize);
-
-  m_iBufLen = iDesiredSize;
-}
diff --git a/core/fxcrt/css/cfx_csstextbuf.h b/core/fxcrt/css/cfx_csstextbuf.h
deleted file mode 100644
index e1b9c64..0000000
--- a/core/fxcrt/css/cfx_csstextbuf.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_CSS_CFX_CSSTEXTBUF_H_
-#define CORE_FXCRT_CSS_CFX_CSSTEXTBUF_H_
-
-#include "core/fxcrt/fx_system.h"
-
-class CFX_CSSTextBuf {
- public:
-  CFX_CSSTextBuf();
-  ~CFX_CSSTextBuf();
-
-  void InitWithSize(int32_t iAllocSize);
-  void AppendChar(wchar_t wch);
-
-  void Clear() { m_iDatLen = 0; }
-
-  int32_t TrimEnd();
-
-  int32_t GetLength() const { return m_iDatLen; }
-  const wchar_t* GetBuffer() const { return m_pBuffer; }
-
- protected:
-  void ExpandBuf(int32_t iDesiredSize);
-
-  wchar_t* m_pBuffer;
-  int32_t m_iBufLen;
-  int32_t m_iDatLen;
-};
-
-#endif  // CORE_FXCRT_CSS_CFX_CSSTEXTBUF_H_
diff --git a/core/fxcrt/css/cfx_cssvalue.cpp b/core/fxcrt/css/cfx_cssvalue.cpp
index a55fc74..9e22437 100644
--- a/core/fxcrt/css/cfx_cssvalue.cpp
+++ b/core/fxcrt/css/cfx_cssvalue.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,4 +6,6 @@
 
 #include "core/fxcrt/css/cfx_cssvalue.h"
 
-CFX_CSSValue::CFX_CSSValue(CFX_CSSPrimitiveType type) : m_value(type) {}
+CFX_CSSValue::CFX_CSSValue(PrimitiveType type) : m_value(type) {}
+
+CFX_CSSValue::~CFX_CSSValue() = default;
diff --git a/core/fxcrt/css/cfx_cssvalue.h b/core/fxcrt/css/cfx_cssvalue.h
index 30aace2..ccb8bbe 100644
--- a/core/fxcrt/css/cfx_cssvalue.h
+++ b/core/fxcrt/css/cfx_cssvalue.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,13 +12,24 @@
 
 class CFX_CSSValue : public Retainable {
  public:
-  CFX_CSSPrimitiveType GetType() const { return m_value; }
+  enum class PrimitiveType : uint8_t {
+    kUnknown = 0,
+    kNumber,
+    kString,
+    kRGB,
+    kEnum,
+    kFunction,
+    kList,
+  };
+
+  PrimitiveType GetType() const { return m_value; }
 
  protected:
-  explicit CFX_CSSValue(CFX_CSSPrimitiveType type);
+  explicit CFX_CSSValue(PrimitiveType type);
+  ~CFX_CSSValue() override;
 
  private:
-  CFX_CSSPrimitiveType m_value;
+  const PrimitiveType m_value;
 };
 
 #endif  // CORE_FXCRT_CSS_CFX_CSSVALUE_H_
diff --git a/core/fxcrt/css/cfx_cssvaluelist.cpp b/core/fxcrt/css/cfx_cssvaluelist.cpp
index b96f165..c1f0311 100644
--- a/core/fxcrt/css/cfx_cssvaluelist.cpp
+++ b/core/fxcrt/css/cfx_cssvaluelist.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,15 +10,7 @@
 
 #include "core/fxcrt/css/cfx_css.h"
 
-CFX_CSSValueList::CFX_CSSValueList(std::vector<RetainPtr<CFX_CSSValue>>& list)
-    : CFX_CSSValue(CFX_CSSPrimitiveType::List), m_ppList(std::move(list)) {}
+CFX_CSSValueList::CFX_CSSValueList(std::vector<RetainPtr<CFX_CSSValue>> list)
+    : CFX_CSSValue(PrimitiveType::kList), list_(std::move(list)) {}
 
-CFX_CSSValueList::~CFX_CSSValueList() {}
-
-int32_t CFX_CSSValueList::CountValues() const {
-  return m_ppList.size();
-}
-
-RetainPtr<CFX_CSSValue> CFX_CSSValueList::GetValue(int32_t index) const {
-  return m_ppList[index];
-}
+CFX_CSSValueList::~CFX_CSSValueList() = default;
diff --git a/core/fxcrt/css/cfx_cssvaluelist.h b/core/fxcrt/css/cfx_cssvaluelist.h
index 4b87f4c..c51790a 100644
--- a/core/fxcrt/css/cfx_cssvaluelist.h
+++ b/core/fxcrt/css/cfx_cssvaluelist.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,17 +10,17 @@
 #include <vector>
 
 #include "core/fxcrt/css/cfx_cssvalue.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CFX_CSSValueList final : public CFX_CSSValue {
  public:
-  explicit CFX_CSSValueList(std::vector<RetainPtr<CFX_CSSValue>>& list);
+  explicit CFX_CSSValueList(std::vector<RetainPtr<CFX_CSSValue>> list);
   ~CFX_CSSValueList() override;
 
-  int32_t CountValues() const;
-  RetainPtr<CFX_CSSValue> GetValue(int32_t index) const;
+  const std::vector<RetainPtr<CFX_CSSValue>>& values() const { return list_; }
 
  private:
-  std::vector<RetainPtr<CFX_CSSValue>> m_ppList;
+  std::vector<RetainPtr<CFX_CSSValue>> list_;
 };
 
 #endif  // CORE_FXCRT_CSS_CFX_CSSVALUELIST_H_
diff --git a/core/fxcrt/css/cfx_cssvaluelistparser.cpp b/core/fxcrt/css/cfx_cssvaluelistparser.cpp
index 447ba9f..3d2613c 100644
--- a/core/fxcrt/css/cfx_cssvaluelistparser.cpp
+++ b/core/fxcrt/css/cfx_cssvaluelistparser.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,79 +7,81 @@
 #include "core/fxcrt/css/cfx_cssvaluelistparser.h"
 
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_system.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 
 CFX_CSSValueListParser::CFX_CSSValueListParser(const wchar_t* psz,
-                                               int32_t iLen,
+                                               size_t nLen,
                                                wchar_t separator)
-    : m_Separator(separator), m_pCur(psz), m_pEnd(psz + iLen) {
-  ASSERT(psz);
-  ASSERT(iLen > 0);
+    : m_Separator(separator), m_pCur(psz), m_pEnd(psz + nLen) {
+  DCHECK(psz);
+  DCHECK_NE(nLen, 0);
 }
 
-bool CFX_CSSValueListParser::NextValue(CFX_CSSPrimitiveType* eType,
+bool CFX_CSSValueListParser::NextValue(CFX_CSSValue::PrimitiveType* eType,
                                        const wchar_t** pStart,
-                                       int32_t* iLength) {
+                                       size_t* nLength) {
   while (m_pCur < m_pEnd && (*m_pCur <= ' ' || *m_pCur == m_Separator))
     ++m_pCur;
 
   if (m_pCur >= m_pEnd)
     return false;
 
-  *eType = CFX_CSSPrimitiveType::Unknown;
+  *eType = CFX_CSSValue::PrimitiveType::kUnknown;
   *pStart = m_pCur;
-  *iLength = 0;
+  *nLength = 0;
   wchar_t wch = *m_pCur;
   if (wch == '#') {
-    *iLength = SkipTo(' ', false, false);
-    if (*iLength == 4 || *iLength == 7)
-      *eType = CFX_CSSPrimitiveType::RGB;
+    *nLength = SkipToChar(' ');
+    if (*nLength == 4 || *nLength == 7)
+      *eType = CFX_CSSValue::PrimitiveType::kRGB;
   } else if (FXSYS_IsDecimalDigit(wch) || wch == '.' || wch == '-' ||
              wch == '+') {
     while (m_pCur < m_pEnd && (*m_pCur > ' ' && *m_pCur != m_Separator))
       ++m_pCur;
 
-    *iLength = m_pCur - *pStart;
-    *eType = CFX_CSSPrimitiveType::Number;
+    *nLength = m_pCur - *pStart;
+    *eType = CFX_CSSValue::PrimitiveType::kNumber;
   } else if (wch == '\"' || wch == '\'') {
     ++(*pStart);
     m_pCur++;
-    *iLength = SkipTo(wch, false, false);
+    *nLength = SkipToChar(wch);
     m_pCur++;
-    *eType = CFX_CSSPrimitiveType::String;
+    *eType = CFX_CSSValue::PrimitiveType::kString;
   } else if (m_pEnd - m_pCur > 5 && m_pCur[3] == '(') {
     if (FXSYS_wcsnicmp(L"rgb", m_pCur, 3) == 0) {
-      *iLength = SkipTo(')', false, false) + 1;
+      *nLength = SkipToChar(')') + 1;
       m_pCur++;
-      *eType = CFX_CSSPrimitiveType::RGB;
+      *eType = CFX_CSSValue::PrimitiveType::kRGB;
     }
   } else {
-    *iLength = SkipTo(m_Separator, true, true);
-    *eType = CFX_CSSPrimitiveType::String;
+    *nLength = SkipToCharMatchingParens(m_Separator);
+    *eType = CFX_CSSValue::PrimitiveType::kString;
   }
-  return m_pCur <= m_pEnd && *iLength > 0;
+  return m_pCur <= m_pEnd && *nLength > 0;
 }
 
-int32_t CFX_CSSValueListParser::SkipTo(wchar_t wch,
-                                       bool breakOnSpace,
-                                       bool matchBrackets) {
+size_t CFX_CSSValueListParser::SkipToChar(wchar_t wch) {
   const wchar_t* pStart = m_pCur;
-  int32_t bracketCount = 0;
   while (m_pCur < m_pEnd && *m_pCur != wch) {
-    if (breakOnSpace && *m_pCur <= ' ')
-      break;
-    if (!matchBrackets) {
-      m_pCur++;
-      continue;
-    }
+    m_pCur++;
+  }
+  return m_pCur - pStart;
+}
 
+size_t CFX_CSSValueListParser::SkipToCharMatchingParens(wchar_t wch) {
+  const wchar_t* pStart = m_pCur;
+  int64_t bracketCount = 0;
+  while (m_pCur < m_pEnd && *m_pCur != wch) {
+    if (*m_pCur <= ' ')
+      break;
     if (*m_pCur == '(')
       bracketCount++;
     else if (*m_pCur == ')')
       bracketCount--;
-
     m_pCur++;
   }
-
   while (bracketCount > 0 && m_pCur < m_pEnd) {
     if (*m_pCur == ')')
       bracketCount--;
diff --git a/core/fxcrt/css/cfx_cssvaluelistparser.h b/core/fxcrt/css/cfx_cssvaluelistparser.h
index 6872ee2..050d002 100644
--- a/core/fxcrt/css/cfx_cssvaluelistparser.h
+++ b/core/fxcrt/css/cfx_cssvaluelistparser.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,20 +7,22 @@
 #ifndef CORE_FXCRT_CSS_CFX_CSSVALUELISTPARSER_H_
 #define CORE_FXCRT_CSS_CFX_CSSVALUELISTPARSER_H_
 
-#include "core/fxcrt/css/cfx_css.h"
-#include "core/fxcrt/fx_system.h"
+#include <stdint.h>
+
+#include "core/fxcrt/css/cfx_cssvalue.h"
 
 class CFX_CSSValueListParser {
  public:
-  CFX_CSSValueListParser(const wchar_t* psz, int32_t iLen, wchar_t separator);
+  CFX_CSSValueListParser(const wchar_t* psz, size_t nLen, wchar_t separator);
 
-  bool NextValue(CFX_CSSPrimitiveType* eType,
+  bool NextValue(CFX_CSSValue::PrimitiveType* eType,
                  const wchar_t** pStart,
-                 int32_t* iLength);
+                 size_t* nLength);
   void UseCommaSeparator() { m_Separator = ','; }
 
  private:
-  int32_t SkipTo(wchar_t wch, bool breakOnSpace, bool matchBrackets);
+  size_t SkipToChar(wchar_t wch);
+  size_t SkipToCharMatchingParens(wchar_t wch);
 
   wchar_t m_Separator;
   const wchar_t* m_pCur;
diff --git a/core/fxcrt/css/cfx_cssvaluelistparser_unittest.cpp b/core/fxcrt/css/cfx_cssvaluelistparser_unittest.cpp
index bb065f8..93d0e4a 100644
--- a/core/fxcrt/css/cfx_cssvaluelistparser_unittest.cpp
+++ b/core/fxcrt/css/cfx_cssvaluelistparser_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,136 +6,136 @@
 
 #include "core/fxcrt/css/cfx_cssvaluelistparser.h"
 
+#include <memory>
+
 #include "core/fxcrt/widestring.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
 TEST(CFX_CSSValueListParserTest, rgb_short) {
-  CFX_CSSPrimitiveType type;
+  CFX_CSSValue::PrimitiveType type;
   const wchar_t* start;
-  int32_t len;
+  size_t len;
 
-  auto parser = pdfium::MakeUnique<CFX_CSSValueListParser>(L"#abc", 4, L' ');
+  auto parser = std::make_unique<CFX_CSSValueListParser>(L"#abc", 4, L' ');
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::RGB, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kRGB, type);
   EXPECT_EQ(L"#abc", WideString(start, len));
   EXPECT_FALSE(parser->NextValue(&type, &start, &len));
 
-  parser = pdfium::MakeUnique<CFX_CSSValueListParser>(L"#abcdef", 7, L' ');
+  parser = std::make_unique<CFX_CSSValueListParser>(L"#abcdef", 7, L' ');
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::RGB, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kRGB, type);
   EXPECT_EQ(L"#abcdef", WideString(start, len));
   EXPECT_FALSE(parser->NextValue(&type, &start, &len));
 
   parser =
-      pdfium::MakeUnique<CFX_CSSValueListParser>(L"rgb(1, 255, 4)", 14, L' ');
+      std::make_unique<CFX_CSSValueListParser>(L"rgb(1, 255, 4)", 14, L' ');
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::RGB, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kRGB, type);
   EXPECT_EQ(L"rgb(1, 255, 4)", WideString(start, len));
 
-  parser = pdfium::MakeUnique<CFX_CSSValueListParser>(L"#abcdefghij", 11, L' ');
+  parser = std::make_unique<CFX_CSSValueListParser>(L"#abcdefghij", 11, L' ');
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::Unknown, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kUnknown, type);
   EXPECT_EQ(L"#abcdefghij", WideString(start, len));
   EXPECT_FALSE(parser->NextValue(&type, &start, &len));
 }
 
 TEST(CFX_CSSValueListParserTest, number_parsing) {
-  CFX_CSSPrimitiveType type;
+  CFX_CSSValue::PrimitiveType type;
   const wchar_t* start;
-  int32_t len;
+  size_t len;
 
-  auto parser = pdfium::MakeUnique<CFX_CSSValueListParser>(L"1234", 4, L' ');
+  auto parser = std::make_unique<CFX_CSSValueListParser>(L"1234", 4, L' ');
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::Number, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type);
   EXPECT_EQ(L"1234", WideString(start, len));
 
-  parser = pdfium::MakeUnique<CFX_CSSValueListParser>(L"-1234", 5, L' ');
+  parser = std::make_unique<CFX_CSSValueListParser>(L"-1234", 5, L' ');
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::Number, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type);
   EXPECT_EQ(L"-1234", WideString(start, len));
 
-  parser = pdfium::MakeUnique<CFX_CSSValueListParser>(L"+1234", 5, L' ');
+  parser = std::make_unique<CFX_CSSValueListParser>(L"+1234", 5, L' ');
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::Number, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type);
   EXPECT_EQ(L"+1234", WideString(start, len));
 
-  parser = pdfium::MakeUnique<CFX_CSSValueListParser>(L".1234", 5, L' ');
+  parser = std::make_unique<CFX_CSSValueListParser>(L".1234", 5, L' ');
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::Number, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type);
   EXPECT_EQ(L".1234", WideString(start, len));
 
-  parser = pdfium::MakeUnique<CFX_CSSValueListParser>(L"4321.1234", 9, L' ');
+  parser = std::make_unique<CFX_CSSValueListParser>(L"4321.1234", 9, L' ');
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::Number, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type);
   EXPECT_EQ(L"4321.1234", WideString(start, len));
 
   // TODO(dsinclair): These should probably fail but currently don't.
-  parser = pdfium::MakeUnique<CFX_CSSValueListParser>(L"4321.12.34", 10, L' ');
+  parser = std::make_unique<CFX_CSSValueListParser>(L"4321.12.34", 10, L' ');
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::Number, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type);
   EXPECT_EQ(L"4321.12.34", WideString(start, len));
 
-  parser = pdfium::MakeUnique<CFX_CSSValueListParser>(L"43a1.12.34", 10, L' ');
+  parser = std::make_unique<CFX_CSSValueListParser>(L"43a1.12.34", 10, L' ');
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::Number, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type);
   EXPECT_EQ(L"43a1.12.34", WideString(start, len));
 }
 
 TEST(CFX_CSSValueListParserTest, string_parsing) {
-  CFX_CSSPrimitiveType type;
+  CFX_CSSValue::PrimitiveType type;
   const wchar_t* start;
-  int32_t len;
+  size_t len;
 
-  auto parser =
-      pdfium::MakeUnique<CFX_CSSValueListParser>(L"'string'", 8, L' ');
+  auto parser = std::make_unique<CFX_CSSValueListParser>(L"'string'", 8, L' ');
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::String, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kString, type);
   EXPECT_EQ(L"string", WideString(start, len));
 
-  parser = pdfium::MakeUnique<CFX_CSSValueListParser>(L"\"another string\"", 16,
-                                                      L' ');
+  parser =
+      std::make_unique<CFX_CSSValueListParser>(L"\"another string\"", 16, L' ');
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::String, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kString, type);
   EXPECT_EQ(L"another string", WideString(start, len));
 
-  parser = pdfium::MakeUnique<CFX_CSSValueListParser>(L"standalone", 10, L' ');
+  parser = std::make_unique<CFX_CSSValueListParser>(L"standalone", 10, L' ');
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::String, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kString, type);
   EXPECT_EQ(L"standalone", WideString(start, len));
 }
 
 TEST(CFX_CSSValueListParserTest, multiparsing) {
-  CFX_CSSPrimitiveType type;
+  CFX_CSSValue::PrimitiveType type;
   const wchar_t* start;
-  int32_t len;
+  size_t len;
 
-  auto parser = pdfium::MakeUnique<CFX_CSSValueListParser>(L"1, 2, 3", 7, L',');
+  auto parser = std::make_unique<CFX_CSSValueListParser>(L"1, 2, 3", 7, L',');
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::Number, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type);
   EXPECT_EQ(L"1", WideString(start, len));
 
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::Number, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type);
   EXPECT_EQ(L"2", WideString(start, len));
 
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::Number, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type);
   EXPECT_EQ(L"3", WideString(start, len));
 
   EXPECT_FALSE(parser->NextValue(&type, &start, &len));
 
-  parser = pdfium::MakeUnique<CFX_CSSValueListParser>(L"'str', rgb(1, 2, 3), 4",
-                                                      22, L',');
+  parser = std::make_unique<CFX_CSSValueListParser>(L"'str', rgb(1, 2, 3), 4",
+                                                    22, L',');
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::String, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kString, type);
   EXPECT_EQ(L"str", WideString(start, len));
 
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::RGB, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kRGB, type);
   EXPECT_EQ(L"rgb(1, 2, 3)", WideString(start, len));
 
   EXPECT_TRUE(parser->NextValue(&type, &start, &len));
-  EXPECT_EQ(CFX_CSSPrimitiveType::Number, type);
+  EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type);
   EXPECT_EQ(L"4", WideString(start, len));
 }
diff --git a/core/fxcrt/css/properties.inc b/core/fxcrt/css/properties.inc
new file mode 100644
index 0000000..3f18a06
--- /dev/null
+++ b/core/fxcrt/css/properties.inc
@@ -0,0 +1,188 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+CSS_PROP____(BorderLeft,
+             "border-left",
+             0x04080036,
+             CFX_CSSVALUETYPE_Shorthand)
+CSS_PROP____(Top,
+             "top",
+             0x0BEDAF33,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+             CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(Margin,
+             "margin",
+             0x0CB016BE,
+             CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum |
+             CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(TextIndent,
+             "text-indent",
+             0x169ADB74,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(Right,
+             "right",
+             0x193ADE3E,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+             CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(PaddingLeft,
+             "padding-left",
+             0x228CF02F,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(MarginLeft,
+             "margin-left",
+             0x297C5656,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber |
+             CFX_CSSVALUETYPE_MaybeEnum)
+CSS_PROP____(Border,
+             "border",
+             0x2A23349E,
+             CFX_CSSVALUETYPE_Shorthand)
+CSS_PROP____(BorderTop,
+             "border-top",
+             0x2B866ADE,
+             CFX_CSSVALUETYPE_Shorthand)
+CSS_PROP____(Bottom,
+             "bottom",
+             0x399F02B5,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+             CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(PaddingRight,
+             "padding-right",
+             0x3F616AC2,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(BorderBottom,
+             "border-bottom",
+             0x452CE780,
+             CFX_CSSVALUETYPE_Shorthand)
+CSS_PROP____(FontFamily,
+             "font-family",
+             0x574686E6,
+             CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeString)
+CSS_PROP____(FontWeight,
+             "font-weight",
+             0x6692F60C,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+             CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(Color,
+             "color",
+             0x6E67921F,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+             CFX_CSSVALUETYPE_MaybeColor)
+CSS_PROP____(LetterSpacing,
+             "letter-spacing",
+             0x70536102,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+             CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(TextAlign,
+             "text-align",
+             0x7553F1BD,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum)
+CSS_PROP____(BorderRightWidth,
+             "border-right-width",
+             0x8F5A6036,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+             CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(VerticalAlign,
+             "vertical-align",
+             0x934A87D2,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+             CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(PaddingTop,
+             "padding-top",
+             0x959D22B7,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(FontVariant,
+             "font-variant",
+             0x9C785779,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum)
+CSS_PROP____(BorderWidth,
+             "border-width",
+             0xA8DE4FEB,
+             CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum |
+             CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(BorderBottomWidth,
+             "border-bottom-width",
+             0xAE41204D,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+             CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(BorderRight,
+             "border-right",
+             0xB78E9EA9,
+             CFX_CSSVALUETYPE_Shorthand)
+CSS_PROP____(FontSize,
+             "font-size",
+             0xB93956DF,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+             CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(BorderSpacing,
+             "border-spacing",
+             0xC72030F0,
+             CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(FontStyle,
+             "font-style",
+             0xCB1950F5,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum)
+CSS_PROP____(Font,
+             "font",
+             0xCD308B77,
+             CFX_CSSVALUETYPE_Shorthand)
+CSS_PROP____(LineHeight,
+             "line-height",
+             0xCFCACE2E,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+             CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(MarginRight,
+             "margin-right",
+             0xD13C58C9,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber |
+             CFX_CSSVALUETYPE_MaybeEnum)
+CSS_PROP____(BorderLeftWidth,
+             "border-left-width",
+             0xD1E93D83,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+             CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(Display,
+             "display",
+             0xD4224C36,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum)
+CSS_PROP____(PaddingBottom,
+             "padding-bottom",
+             0xE555B3B9,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(BorderTopWidth,
+             "border-top-width",
+             0xED2CB62B,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+             CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(WordSpacing,
+             "word-spacing",
+             0xEDA63BAE,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+             CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(Left,
+             "left",
+             0xF5AD782B,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+             CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(TextDecoration,
+             "text-decoration",
+             0xF7C634BA,
+             CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum)
+CSS_PROP____(Padding,
+             "padding",
+             0xF8C373F7,
+             CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeNumber)
+CSS_PROP____(MarginBottom,
+             "margin-bottom",
+             0xF93485A0,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber |
+             CFX_CSSVALUETYPE_MaybeEnum)
+CSS_PROP____(MarginTop,
+             "margin-top",
+             0xFE51DCFE,
+             CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber |
+             CFX_CSSVALUETYPE_MaybeEnum)
+
diff --git a/core/fxcrt/css/property_values.inc b/core/fxcrt/css/property_values.inc
new file mode 100644
index 0000000..c93bb3a
--- /dev/null
+++ b/core/fxcrt/css/property_values.inc
@@ -0,0 +1,50 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+CSS_PROP_VALUE____(Bolder, "bolder", 0x009F1058)
+CSS_PROP_VALUE____(None, "none", 0x048B6670)
+CSS_PROP_VALUE____(Dot, "dot", 0x0A48CB27)
+CSS_PROP_VALUE____(Sub, "sub", 0x0BD37FAA)
+CSS_PROP_VALUE____(Top, "top", 0x0BEDAF33)
+CSS_PROP_VALUE____(Right, "right", 0x193ADE3E)
+CSS_PROP_VALUE____(Normal, "normal", 0x247CF3E9)
+CSS_PROP_VALUE____(Auto, "auto", 0x2B35B6D9)
+CSS_PROP_VALUE____(Text, "text", 0x2D08AF85)
+CSS_PROP_VALUE____(XSmall, "x-small", 0x2D2FCAFE)
+CSS_PROP_VALUE____(Thin, "thin", 0x2D574D53)
+CSS_PROP_VALUE____(Small, "small", 0x316A3739)
+CSS_PROP_VALUE____(Bottom, "bottom", 0x399F02B5)
+CSS_PROP_VALUE____(Underline, "underline", 0x3A0273A6)
+CSS_PROP_VALUE____(Double, "double", 0x3D98515B)
+CSS_PROP_VALUE____(Lighter, "lighter", 0x45BEB7AF)
+CSS_PROP_VALUE____(Oblique, "oblique", 0x53EBDDB1)
+CSS_PROP_VALUE____(Super, "super", 0x6A4F842F)
+CSS_PROP_VALUE____(Center, "center", 0x6C51AFC1)
+CSS_PROP_VALUE____(XxLarge, "xx-large", 0x70BB1508)
+CSS_PROP_VALUE____(Smaller, "smaller", 0x849769F0)
+CSS_PROP_VALUE____(Baseline, "baseline", 0x87436BA3)
+CSS_PROP_VALUE____(Thick, "thick", 0x8CC35EB3)
+CSS_PROP_VALUE____(Justify, "justify", 0x8D269CAE)
+CSS_PROP_VALUE____(Middle, "middle", 0x947FA00F)
+CSS_PROP_VALUE____(Medium, "medium", 0xA084A381)
+CSS_PROP_VALUE____(ListItem, "list-item", 0xA32382B8)
+CSS_PROP_VALUE____(XxSmall, "xx-small", 0xADE1FC76)
+CSS_PROP_VALUE____(Bold, "bold", 0xB18313A1)
+CSS_PROP_VALUE____(SmallCaps, "small-caps", 0xB299428D)
+CSS_PROP_VALUE____(Inline, "inline", 0xC02D649F)
+CSS_PROP_VALUE____(Overline, "overline", 0xC0EC9FA4)
+CSS_PROP_VALUE____(TextBottom, "text-bottom", 0xC7D08D87)
+CSS_PROP_VALUE____(Larger, "larger", 0xCD3C409D)
+CSS_PROP_VALUE____(InlineTable, "inline-table", 0xD131F494)
+CSS_PROP_VALUE____(InlineBlock, "inline-block", 0xD26A8BD7)
+CSS_PROP_VALUE____(Blink, "blink", 0xDC36E390)
+CSS_PROP_VALUE____(Block, "block", 0xDCD480AB)
+CSS_PROP_VALUE____(Italic, "italic", 0xE31D5396)
+CSS_PROP_VALUE____(LineThrough, "line-through", 0xE4C5A276)
+CSS_PROP_VALUE____(XLarge, "x-large", 0xF008E390)
+CSS_PROP_VALUE____(Large, "large", 0xF4434FCB)
+CSS_PROP_VALUE____(Left, "left", 0xF5AD782B)
+CSS_PROP_VALUE____(TextTop, "text-top", 0xFCB58D45)
diff --git a/core/fxcrt/data_vector.h b/core/fxcrt/data_vector.h
new file mode 100644
index 0000000..258c5a9
--- /dev/null
+++ b/core/fxcrt/data_vector.h
@@ -0,0 +1,21 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_DATA_VECTOR_H_
+#define CORE_FXCRT_DATA_VECTOR_H_
+
+#include <vector>
+
+#include "core/fxcrt/fx_memory_wrappers.h"
+
+namespace fxcrt {
+
+template <typename T>
+using DataVector = std::vector<T, FxAllocAllocator<T>>;
+
+}  // namespace fxcrt
+
+using fxcrt::DataVector;
+
+#endif  // CORE_FXCRT_DATA_VECTOR_H_
diff --git a/core/fxcrt/fake_time_test.cpp b/core/fxcrt/fake_time_test.cpp
new file mode 100644
index 0000000..a3968ff
--- /dev/null
+++ b/core/fxcrt/fake_time_test.cpp
@@ -0,0 +1,18 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/fake_time_test.h"
+
+#include "core/fxcrt/fx_extension.h"
+
+void FakeTimeTest::SetUp() {
+  // Arbitrary, picked descending digits, 2020-04-23 15:05:21.
+  FXSYS_SetTimeFunction([]() -> time_t { return 1587654321; });
+  FXSYS_SetLocaltimeFunction([](const time_t* t) { return gmtime(t); });
+}
+
+void FakeTimeTest::TearDown() {
+  FXSYS_SetTimeFunction(nullptr);
+  FXSYS_SetLocaltimeFunction(nullptr);
+}
diff --git a/core/fxcrt/fake_time_test.h b/core/fxcrt/fake_time_test.h
new file mode 100644
index 0000000..9e514d1
--- /dev/null
+++ b/core/fxcrt/fake_time_test.h
@@ -0,0 +1,16 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_FAKE_TIME_TEST_H_
+#define CORE_FXCRT_FAKE_TIME_TEST_H_
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+class FakeTimeTest : public ::testing::Test {
+ public:
+  void SetUp() override;
+  void TearDown() override;
+};
+
+#endif  // CORE_FXCRT_FAKE_TIME_TEST_H_
diff --git a/core/fxcrt/fileaccess_iface.h b/core/fxcrt/fileaccess_iface.h
index c744b54..29aae63 100644
--- a/core/fxcrt/fileaccess_iface.h
+++ b/core/fxcrt/fileaccess_iface.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,6 @@
 
 #include <memory>
 
-#include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/fx_string.h"
 
 class FileAccessIface {
@@ -17,8 +16,8 @@
   static std::unique_ptr<FileAccessIface> Create();
   virtual ~FileAccessIface() = default;
 
-  virtual bool Open(ByteStringView fileName, uint32_t dwMode) = 0;
-  virtual bool Open(WideStringView fileName, uint32_t dwMode) = 0;
+  // Opens in read-only mode. `fileName` is UTF-8 on all platforms.
+  virtual bool Open(ByteStringView fileName) = 0;
   virtual void Close() = 0;
   virtual FX_FILESIZE GetSize() const = 0;
   virtual FX_FILESIZE GetPosition() const = 0;
diff --git a/core/fxcrt/fixed_size_data_vector.h b/core/fxcrt/fixed_size_data_vector.h
new file mode 100644
index 0000000..bec5e10
--- /dev/null
+++ b/core/fxcrt/fixed_size_data_vector.h
@@ -0,0 +1,100 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_FIXED_SIZE_DATA_VECTOR_H_
+#define CORE_FXCRT_FIXED_SIZE_DATA_VECTOR_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "third_party/base/containers/span.h"
+
+namespace fxcrt {
+
+enum class DataVectorAllocOption {
+  kInitialized,
+  kUninitialized,
+  kTryInitialized,
+};
+
+// A simple data container that has a fixed size.
+// Unlike std::vector, it cannot be implicitly copied and its data is only
+// accessible using spans.
+// It can either initialize its data with zeros, or leave its data
+// uninitialized.
+template <typename T, DataVectorAllocOption OPTION>
+class FixedSizeDataVector {
+ public:
+  FixedSizeDataVector() : FixedSizeDataVector(0) {}
+  explicit FixedSizeDataVector(size_t size)
+      : data_(MaybeInit(size, OPTION)), size_(CalculateSize(size, OPTION)) {}
+  FixedSizeDataVector(const FixedSizeDataVector&) = delete;
+  FixedSizeDataVector& operator=(const FixedSizeDataVector&) = delete;
+  template <DataVectorAllocOption OTHER_OPTION>
+  FixedSizeDataVector(FixedSizeDataVector<T, OTHER_OPTION>&& that) noexcept {
+    data_ = std::move(that.data_);
+    size_ = that.size_;
+    that.size_ = 0;
+  }
+  template <DataVectorAllocOption OTHER_OPTION>
+  FixedSizeDataVector& operator=(
+      FixedSizeDataVector<T, OTHER_OPTION>&& that) noexcept {
+    data_ = std::move(that.data_);
+    size_ = that.size_;
+    that.size_ = 0;
+    return *this;
+  }
+  ~FixedSizeDataVector() = default;
+
+  operator pdfium::span<const T>() const { return span(); }
+
+  pdfium::span<T> writable_span() {
+    return pdfium::make_span(data_.get(), size_);
+  }
+
+  pdfium::span<const T> span() const {
+    return pdfium::make_span(data_.get(), size_);
+  }
+
+  size_t size() const { return size_; }
+  bool empty() const { return size_ == 0; }
+
+ private:
+  friend class FixedSizeDataVector<T, DataVectorAllocOption::kInitialized>;
+  friend class FixedSizeDataVector<T, DataVectorAllocOption::kUninitialized>;
+  friend class FixedSizeDataVector<T, DataVectorAllocOption::kTryInitialized>;
+
+  static T* MaybeInit(size_t size, DataVectorAllocOption alloc_option) {
+    if (size == 0)
+      return nullptr;
+    switch (alloc_option) {
+      case DataVectorAllocOption::kInitialized:
+        return FX_Alloc(T, size);
+      case DataVectorAllocOption::kUninitialized:
+        return FX_AllocUninit(T, size);
+      case DataVectorAllocOption::kTryInitialized:
+        return FX_TryAlloc(T, size);
+    }
+  }
+
+  size_t CalculateSize(size_t size, DataVectorAllocOption alloc_option) const {
+    switch (alloc_option) {
+      case DataVectorAllocOption::kInitialized:
+      case DataVectorAllocOption::kUninitialized:
+        return size;
+      case DataVectorAllocOption::kTryInitialized:
+        return data_ ? size : 0;
+    }
+  }
+
+  std::unique_ptr<T, FxFreeDeleter> data_;
+  size_t size_;
+};
+
+}  // namespace fxcrt
+
+#endif  // CORE_FXCRT_FIXED_SIZE_DATA_VECTOR_H_
diff --git a/core/fxcrt/fixed_try_alloc_zeroed_data_vector.h b/core/fxcrt/fixed_try_alloc_zeroed_data_vector.h
new file mode 100644
index 0000000..e7f1bf8
--- /dev/null
+++ b/core/fxcrt/fixed_try_alloc_zeroed_data_vector.h
@@ -0,0 +1,17 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_FIXED_TRY_ALLOC_ZEROED_DATA_VECTOR_H_
+#define CORE_FXCRT_FIXED_TRY_ALLOC_ZEROED_DATA_VECTOR_H_
+
+#include "core/fxcrt/fixed_size_data_vector.h"
+
+// WARNING: Since FX_TryAlloc() can fail, one must always check if a
+// FixedTryAllocZeroedDataVector is empty after creating one.
+template <typename T>
+using FixedTryAllocZeroedDataVector =
+    fxcrt::FixedSizeDataVector<T,
+                               fxcrt::DataVectorAllocOption::kTryInitialized>;
+
+#endif  // CORE_FXCRT_FIXED_TRY_ALLOC_ZEROED_DATA_VECTOR_H_
diff --git a/core/fxcrt/fixed_try_alloc_zeroed_data_vector_unittest.cpp b/core/fxcrt/fixed_try_alloc_zeroed_data_vector_unittest.cpp
new file mode 100644
index 0000000..613d838
--- /dev/null
+++ b/core/fxcrt/fixed_try_alloc_zeroed_data_vector_unittest.cpp
@@ -0,0 +1,108 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/fixed_try_alloc_zeroed_data_vector.h"
+
+#include <limits>
+#include <utility>
+
+#include "core/fxcrt/fixed_uninit_data_vector.h"
+#include "core/fxcrt/fixed_zeroed_data_vector.h"
+#include "core/fxcrt/span_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/containers/span.h"
+
+TEST(FixedTryAllocZeroedDataVector, NoData) {
+  FixedTryAllocZeroedDataVector<int> vec;
+  EXPECT_EQ(0u, vec.size());
+  EXPECT_TRUE(vec.empty());
+  EXPECT_TRUE(vec.span().empty());
+  EXPECT_TRUE(vec.writable_span().empty());
+}
+
+TEST(FixedTryAllocZeroedDataVector, WithData) {
+  FixedTryAllocZeroedDataVector<int> vec(4);
+  EXPECT_FALSE(vec.empty());
+  EXPECT_EQ(4u, vec.size());
+  EXPECT_EQ(4u, vec.span().size());
+  EXPECT_EQ(4u, vec.writable_span().size());
+  EXPECT_THAT(vec.span(), testing::ElementsAre(0, 0, 0, 0));
+
+  constexpr int kData[] = {1, 2, 3, 4};
+  fxcrt::spancpy(vec.writable_span(), pdfium::make_span(kData));
+  EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
+}
+
+TEST(FixedTryAllocZeroedDataVector, AllocFailure) {
+  constexpr size_t kCloseToMaxByteAlloc =
+      std::numeric_limits<size_t>::max() - 100;
+  FixedTryAllocZeroedDataVector<int> vec(kCloseToMaxByteAlloc);
+  EXPECT_TRUE(vec.empty());
+  EXPECT_EQ(0u, vec.size());
+  EXPECT_EQ(0u, vec.span().size());
+  EXPECT_EQ(0u, vec.writable_span().size());
+}
+
+TEST(FixedTryAllocZeroedDataVector, Move) {
+  FixedTryAllocZeroedDataVector<int> vec(4);
+  constexpr int kData[] = {1, 2, 3, 4};
+  ASSERT_EQ(4u, vec.writable_span().size());
+  fxcrt::spancpy(vec.writable_span(), pdfium::make_span(kData));
+  const int* const original_data_ptr = vec.span().data();
+
+  FixedTryAllocZeroedDataVector<int> vec2(std::move(vec));
+  EXPECT_FALSE(vec2.empty());
+  EXPECT_EQ(4u, vec2.size());
+  EXPECT_EQ(4u, vec2.span().size());
+  EXPECT_EQ(4u, vec2.writable_span().size());
+  EXPECT_THAT(vec2.span(), testing::ElementsAre(1, 2, 3, 4));
+  EXPECT_EQ(vec2.span().data(), original_data_ptr);
+
+  EXPECT_EQ(0u, vec.size());
+  EXPECT_TRUE(vec.empty());
+  EXPECT_TRUE(vec.span().empty());
+  EXPECT_TRUE(vec.writable_span().empty());
+
+  vec = std::move(vec2);
+  EXPECT_FALSE(vec.empty());
+  EXPECT_EQ(4u, vec.size());
+  EXPECT_EQ(4u, vec.span().size());
+  EXPECT_EQ(4u, vec.writable_span().size());
+  EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
+  EXPECT_EQ(vec.span().data(), original_data_ptr);
+
+  EXPECT_EQ(0u, vec2.size());
+  EXPECT_TRUE(vec2.empty());
+  EXPECT_TRUE(vec2.span().empty());
+  EXPECT_TRUE(vec2.writable_span().empty());
+}
+
+TEST(FixedTryAllocZeroedDataVector, AssignFromFixedZeroedDataVector) {
+  FixedTryAllocZeroedDataVector<int> vec;
+
+  FixedZeroedDataVector<int> vec2(4);
+  constexpr int kData[] = {1, 2, 3, 4};
+  ASSERT_EQ(4u, vec2.writable_span().size());
+  fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData));
+
+  vec = std::move(vec2);
+  EXPECT_TRUE(vec2.empty());
+  EXPECT_EQ(4u, vec.span().size());
+  EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
+}
+
+TEST(FixedTryAllocZeroedDataVector, AssignFromFixedUninitDataVector) {
+  FixedTryAllocZeroedDataVector<int> vec;
+
+  FixedUninitDataVector<int> vec2(4);
+  constexpr int kData[] = {1, 2, 3, 4};
+  ASSERT_EQ(4u, vec2.writable_span().size());
+  fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData));
+
+  vec = std::move(vec2);
+  EXPECT_TRUE(vec2.empty());
+  EXPECT_EQ(4u, vec.span().size());
+  EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
+}
diff --git a/core/fxcrt/fixed_uninit_data_vector.h b/core/fxcrt/fixed_uninit_data_vector.h
new file mode 100644
index 0000000..a208f5e
--- /dev/null
+++ b/core/fxcrt/fixed_uninit_data_vector.h
@@ -0,0 +1,14 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_FIXED_UNINIT_DATA_VECTOR_H_
+#define CORE_FXCRT_FIXED_UNINIT_DATA_VECTOR_H_
+
+#include "core/fxcrt/fixed_size_data_vector.h"
+
+template <typename T>
+using FixedUninitDataVector =
+    fxcrt::FixedSizeDataVector<T, fxcrt::DataVectorAllocOption::kUninitialized>;
+
+#endif  // CORE_FXCRT_FIXED_UNINIT_DATA_VECTOR_H_
diff --git a/core/fxcrt/fixed_uninit_data_vector_unittest.cpp b/core/fxcrt/fixed_uninit_data_vector_unittest.cpp
new file mode 100644
index 0000000..70eda6a
--- /dev/null
+++ b/core/fxcrt/fixed_uninit_data_vector_unittest.cpp
@@ -0,0 +1,96 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/fixed_uninit_data_vector.h"
+
+#include <utility>
+
+#include "core/fxcrt/fixed_try_alloc_zeroed_data_vector.h"
+#include "core/fxcrt/fixed_zeroed_data_vector.h"
+#include "core/fxcrt/span_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/containers/span.h"
+
+TEST(FixedUninitDataVector, NoData) {
+  FixedUninitDataVector<int> vec;
+  EXPECT_EQ(0u, vec.size());
+  EXPECT_TRUE(vec.empty());
+  EXPECT_TRUE(vec.span().empty());
+  EXPECT_TRUE(vec.writable_span().empty());
+}
+
+TEST(FixedUninitDataVector, WithData) {
+  FixedUninitDataVector<int> vec(4);
+  EXPECT_FALSE(vec.empty());
+  EXPECT_EQ(4u, vec.size());
+  EXPECT_EQ(4u, vec.span().size());
+  EXPECT_EQ(4u, vec.writable_span().size());
+
+  constexpr int kData[] = {1, 2, 3, 4};
+  fxcrt::spancpy(vec.writable_span(), pdfium::make_span(kData));
+  EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
+}
+
+TEST(FixedUninitDataVector, Move) {
+  FixedUninitDataVector<int> vec(4);
+  constexpr int kData[] = {1, 2, 3, 4};
+  ASSERT_EQ(4u, vec.writable_span().size());
+  fxcrt::spancpy(vec.writable_span(), pdfium::make_span(kData));
+  const int* const original_data_ptr = vec.span().data();
+
+  FixedUninitDataVector<int> vec2(std::move(vec));
+  EXPECT_FALSE(vec2.empty());
+  EXPECT_EQ(4u, vec2.size());
+  EXPECT_EQ(4u, vec2.span().size());
+  EXPECT_EQ(4u, vec2.writable_span().size());
+  EXPECT_THAT(vec2.span(), testing::ElementsAre(1, 2, 3, 4));
+  EXPECT_EQ(vec2.span().data(), original_data_ptr);
+
+  EXPECT_EQ(0u, vec.size());
+  EXPECT_TRUE(vec.empty());
+  EXPECT_TRUE(vec.span().empty());
+  EXPECT_TRUE(vec.writable_span().empty());
+
+  vec = std::move(vec2);
+  EXPECT_FALSE(vec.empty());
+  EXPECT_EQ(4u, vec.size());
+  EXPECT_EQ(4u, vec.span().size());
+  EXPECT_EQ(4u, vec.writable_span().size());
+  EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
+  EXPECT_EQ(vec.span().data(), original_data_ptr);
+
+  EXPECT_EQ(0u, vec2.size());
+  EXPECT_TRUE(vec2.empty());
+  EXPECT_TRUE(vec2.span().empty());
+  EXPECT_TRUE(vec2.writable_span().empty());
+}
+
+TEST(FixedUninitDataVector, AssignFromFixedZeroedDataVector) {
+  FixedUninitDataVector<int> vec;
+
+  FixedZeroedDataVector<int> vec2(4);
+  constexpr int kData[] = {1, 2, 3, 4};
+  ASSERT_EQ(4u, vec2.writable_span().size());
+  fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData));
+
+  vec = std::move(vec2);
+  EXPECT_TRUE(vec2.empty());
+  EXPECT_EQ(4u, vec.span().size());
+  EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
+}
+
+TEST(FixedUninitDataVector, AssignFromFixedTryAllocZeroedDataVector) {
+  FixedUninitDataVector<int> vec;
+
+  FixedTryAllocZeroedDataVector<int> vec2(4);
+  constexpr int kData[] = {1, 2, 3, 4};
+  ASSERT_EQ(4u, vec2.writable_span().size());
+  fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData));
+
+  vec = std::move(vec2);
+  EXPECT_TRUE(vec2.empty());
+  EXPECT_EQ(4u, vec.span().size());
+  EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
+}
diff --git a/core/fxcrt/fixed_zeroed_data_vector.h b/core/fxcrt/fixed_zeroed_data_vector.h
new file mode 100644
index 0000000..c8b91a6
--- /dev/null
+++ b/core/fxcrt/fixed_zeroed_data_vector.h
@@ -0,0 +1,14 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_FIXED_ZEROED_DATA_VECTOR_H_
+#define CORE_FXCRT_FIXED_ZEROED_DATA_VECTOR_H_
+
+#include "core/fxcrt/fixed_size_data_vector.h"
+
+template <typename T>
+using FixedZeroedDataVector =
+    fxcrt::FixedSizeDataVector<T, fxcrt::DataVectorAllocOption::kInitialized>;
+
+#endif  // CORE_FXCRT_FIXED_ZEROED_DATA_VECTOR_H_
diff --git a/core/fxcrt/fixed_zeroed_data_vector_unittest.cpp b/core/fxcrt/fixed_zeroed_data_vector_unittest.cpp
new file mode 100644
index 0000000..297818f
--- /dev/null
+++ b/core/fxcrt/fixed_zeroed_data_vector_unittest.cpp
@@ -0,0 +1,97 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/fixed_zeroed_data_vector.h"
+
+#include <utility>
+
+#include "core/fxcrt/fixed_try_alloc_zeroed_data_vector.h"
+#include "core/fxcrt/fixed_uninit_data_vector.h"
+#include "core/fxcrt/span_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/containers/span.h"
+
+TEST(FixedZeroedDataVector, NoData) {
+  FixedZeroedDataVector<int> vec;
+  EXPECT_EQ(0u, vec.size());
+  EXPECT_TRUE(vec.empty());
+  EXPECT_TRUE(vec.span().empty());
+  EXPECT_TRUE(vec.writable_span().empty());
+}
+
+TEST(FixedZeroedDataVector, WithData) {
+  FixedZeroedDataVector<int> vec(4);
+  EXPECT_FALSE(vec.empty());
+  EXPECT_EQ(4u, vec.size());
+  EXPECT_EQ(4u, vec.span().size());
+  EXPECT_EQ(4u, vec.writable_span().size());
+  EXPECT_THAT(vec.span(), testing::ElementsAre(0, 0, 0, 0));
+
+  constexpr int kData[] = {1, 2, 3, 4};
+  fxcrt::spancpy(vec.writable_span(), pdfium::make_span(kData));
+  EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
+}
+
+TEST(FixedZeroedDataVector, Move) {
+  FixedZeroedDataVector<int> vec(4);
+  constexpr int kData[] = {1, 2, 3, 4};
+  ASSERT_EQ(4u, vec.writable_span().size());
+  fxcrt::spancpy(vec.writable_span(), pdfium::make_span(kData));
+  const int* const original_data_ptr = vec.span().data();
+
+  FixedZeroedDataVector<int> vec2(std::move(vec));
+  EXPECT_FALSE(vec2.empty());
+  EXPECT_EQ(4u, vec2.size());
+  EXPECT_EQ(4u, vec2.span().size());
+  EXPECT_EQ(4u, vec2.writable_span().size());
+  EXPECT_THAT(vec2.span(), testing::ElementsAre(1, 2, 3, 4));
+  EXPECT_EQ(vec2.span().data(), original_data_ptr);
+
+  EXPECT_EQ(0u, vec.size());
+  EXPECT_TRUE(vec.empty());
+  EXPECT_TRUE(vec.span().empty());
+  EXPECT_TRUE(vec.writable_span().empty());
+
+  vec = std::move(vec2);
+  EXPECT_FALSE(vec.empty());
+  EXPECT_EQ(4u, vec.size());
+  EXPECT_EQ(4u, vec.span().size());
+  EXPECT_EQ(4u, vec.writable_span().size());
+  EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
+  EXPECT_EQ(vec.span().data(), original_data_ptr);
+
+  EXPECT_EQ(0u, vec2.size());
+  EXPECT_TRUE(vec2.empty());
+  EXPECT_TRUE(vec2.span().empty());
+  EXPECT_TRUE(vec2.writable_span().empty());
+}
+
+TEST(FixedZeroedDataVector, AssignFromFixedUninitDataVector) {
+  FixedZeroedDataVector<int> vec;
+
+  FixedUninitDataVector<int> vec2(4);
+  constexpr int kData[] = {1, 2, 3, 4};
+  ASSERT_EQ(4u, vec2.writable_span().size());
+  fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData));
+
+  vec = std::move(vec2);
+  EXPECT_TRUE(vec2.empty());
+  EXPECT_EQ(4u, vec.span().size());
+  EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
+}
+
+TEST(FixedZeroedDataVector, AssignFromFixedTryAllocZeroedDataVector) {
+  FixedZeroedDataVector<int> vec;
+
+  FixedTryAllocZeroedDataVector<int> vec2(4);
+  constexpr int kData[] = {1, 2, 3, 4};
+  ASSERT_EQ(4u, vec2.writable_span().size());
+  fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData));
+
+  vec = std::move(vec2);
+  EXPECT_TRUE(vec2.empty());
+  EXPECT_EQ(4u, vec.span().size());
+  EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
+}
diff --git a/core/fxcrt/fx_2d_size.h b/core/fxcrt/fx_2d_size.h
new file mode 100644
index 0000000..9b7f5d1
--- /dev/null
+++ b/core/fxcrt/fx_2d_size.h
@@ -0,0 +1,17 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_FX_2D_SIZE_H_
+#define CORE_FXCRT_FX_2D_SIZE_H_
+
+#include "core/fxcrt/fx_safe_types.h"
+
+template <typename T, typename U>
+size_t Fx2DSizeOrDie(const T& w, const U& h) {
+  FX_SAFE_SIZE_T safe_size = w;
+  safe_size *= h;
+  return safe_size.ValueOrDie();
+}
+
+#endif  // CORE_FXCRT_FX_2D_SIZE_H_
diff --git a/core/fxcrt/fx_bidi.cpp b/core/fxcrt/fx_bidi.cpp
index c5fb5c6..756aa12 100644
--- a/core/fxcrt/fx_bidi.cpp
+++ b/core/fxcrt/fx_bidi.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,25 +9,33 @@
 #include <algorithm>
 
 #include "core/fxcrt/fx_unicode.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check_op.h"
 
 CFX_BidiChar::CFX_BidiChar()
-    : m_CurrentSegment({0, 0, NEUTRAL}), m_LastSegment({0, 0, NEUTRAL}) {}
+    : m_CurrentSegment({0, 0, Direction::kNeutral}),
+      m_LastSegment({0, 0, Direction::kNeutral}) {}
 
 bool CFX_BidiChar::AppendChar(wchar_t wch) {
   Direction direction;
-  switch (FX_GetBidiClass(wch)) {
+  switch (pdfium::unicode::GetBidiClass(wch)) {
     case FX_BIDICLASS::kL:
+      direction = Direction::kLeft;
+      break;
     case FX_BIDICLASS::kAN:
     case FX_BIDICLASS::kEN:
-      direction = LEFT;
+    case FX_BIDICLASS::kNSM:
+    case FX_BIDICLASS::kCS:
+    case FX_BIDICLASS::kES:
+    case FX_BIDICLASS::kET:
+    case FX_BIDICLASS::kBN:
+      direction = Direction::kLeftWeak;
       break;
     case FX_BIDICLASS::kR:
     case FX_BIDICLASS::kAL:
-      direction = RIGHT;
+      direction = Direction::kRight;
       break;
     default:
-      direction = NEUTRAL;
+      direction = Direction::kNeutral;
       break;
   }
 
@@ -40,7 +48,7 @@
 }
 
 bool CFX_BidiChar::EndChar() {
-  StartNewSegment(NEUTRAL);
+  StartNewSegment(Direction::kNeutral);
   return m_LastSegment.count > 0;
 }
 
@@ -60,30 +68,31 @@
   if (bidi.EndChar())
     m_Order.push_back(bidi.GetSegmentInfo());
 
-  size_t nR2L = std::count_if(m_Order.begin(), m_Order.end(),
-                              [](const CFX_BidiChar::Segment& seg) {
-                                return seg.direction == CFX_BidiChar::RIGHT;
-                              });
+  size_t nR2L = std::count_if(
+      m_Order.begin(), m_Order.end(), [](const CFX_BidiChar::Segment& seg) {
+        return seg.direction == CFX_BidiChar::Direction::kRight;
+      });
 
-  size_t nL2R = std::count_if(m_Order.begin(), m_Order.end(),
-                              [](const CFX_BidiChar::Segment& seg) {
-                                return seg.direction == CFX_BidiChar::LEFT;
-                              });
+  size_t nL2R = std::count_if(
+      m_Order.begin(), m_Order.end(), [](const CFX_BidiChar::Segment& seg) {
+        return seg.direction == CFX_BidiChar::Direction::kLeft;
+      });
 
   if (nR2L > 0 && nR2L >= nL2R)
     SetOverallDirectionRight();
 }
 
-CFX_BidiString::~CFX_BidiString() {}
+CFX_BidiString::~CFX_BidiString() = default;
 
 CFX_BidiChar::Direction CFX_BidiString::OverallDirection() const {
-  ASSERT(m_eOverallDirection != CFX_BidiChar::NEUTRAL);
+  DCHECK_NE(m_eOverallDirection, CFX_BidiChar::Direction::kNeutral);
+  DCHECK_NE(m_eOverallDirection, CFX_BidiChar::Direction::kLeftWeak);
   return m_eOverallDirection;
 }
 
 void CFX_BidiString::SetOverallDirectionRight() {
-  if (m_eOverallDirection != CFX_BidiChar::RIGHT) {
+  if (m_eOverallDirection != CFX_BidiChar::Direction::kRight) {
     std::reverse(m_Order.begin(), m_Order.end());
-    m_eOverallDirection = CFX_BidiChar::RIGHT;
+    m_eOverallDirection = CFX_BidiChar::Direction::kRight;
   }
 }
diff --git a/core/fxcrt/fx_bidi.h b/core/fxcrt/fx_bidi.h
index ade9a2a..19301c4 100644
--- a/core/fxcrt/fx_bidi.h
+++ b/core/fxcrt/fx_bidi.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,16 @@
 #ifndef CORE_FXCRT_FX_BIDI_H_
 #define CORE_FXCRT_FX_BIDI_H_
 
+#include <stdint.h>
+
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/widestring.h"
 
 // Processes characters and group them into segments based on text direction.
 class CFX_BidiChar {
  public:
-  enum Direction { NEUTRAL, LEFT, RIGHT };
+  enum class Direction { kNeutral, kLeft, kRight, kLeftWeak };
   struct Segment {
     int32_t start;        // Start position.
     int32_t count;        // Character count.
@@ -24,7 +25,7 @@
 
   CFX_BidiChar();
 
-  // Append a character and classify it as left, right, or neutral.
+  // Append a character and classify it as left, left-weak, right, or neutral.
   // Returns true if the character has a different direction than the
   // existing direction to indicate there is a segment to process.
   bool AppendChar(wchar_t wch);
@@ -64,7 +65,7 @@
  private:
   const WideString& m_Str;
   std::vector<CFX_BidiChar::Segment> m_Order;
-  CFX_BidiChar::Direction m_eOverallDirection = CFX_BidiChar::LEFT;
+  CFX_BidiChar::Direction m_eOverallDirection = CFX_BidiChar::Direction::kLeft;
 };
 
 #endif  // CORE_FXCRT_FX_BIDI_H_
diff --git a/core/fxcrt/fx_bidi_unittest.cpp b/core/fxcrt/fx_bidi_unittest.cpp
index ad598a6..1b90e32 100644
--- a/core/fxcrt/fx_bidi_unittest.cpp
+++ b/core/fxcrt/fx_bidi_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,9 +7,10 @@
 
 namespace {
 
-const wchar_t kNeutralChar = 32;
-const wchar_t kLeftChar = 65;
-const wchar_t kRightChar = 1424;
+const wchar_t kNeutralChar = 32;   // ' '
+const wchar_t kLeftChar = 65;      // 'A'
+const wchar_t kRightChar = 1488;   // 'א'
+const wchar_t kLeftWeakChar = 46;  // '.'
 
 }  // namespace
 
@@ -18,7 +19,7 @@
   CFX_BidiChar::Segment info;
 
   info = bidi.GetSegmentInfo();
-  EXPECT_EQ(CFX_BidiChar::NEUTRAL, info.direction);
+  EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, info.direction);
   EXPECT_EQ(0, info.start);
   EXPECT_EQ(0, info.count);
   EXPECT_FALSE(bidi.EndChar());
@@ -37,13 +38,13 @@
   EXPECT_FALSE(bidi.AppendChar(kLeftChar));
 
   info = bidi.GetSegmentInfo();
-  EXPECT_EQ(CFX_BidiChar::NEUTRAL, info.direction);
+  EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, info.direction);
   EXPECT_EQ(0, info.start);
   EXPECT_EQ(0, info.count);
 
   EXPECT_TRUE(bidi.EndChar());
   info = bidi.GetSegmentInfo();
-  EXPECT_EQ(CFX_BidiChar::LEFT, info.direction);
+  EXPECT_EQ(CFX_BidiChar::Direction::kLeft, info.direction);
   EXPECT_EQ(0, info.start);
   EXPECT_EQ(3, info.count);
 
@@ -71,13 +72,47 @@
   EXPECT_FALSE(bidi.AppendChar(kNeutralChar));
   EXPECT_TRUE(bidi.AppendChar(kRightChar));
   info = bidi.GetSegmentInfo();
-  EXPECT_EQ(CFX_BidiChar::NEUTRAL, info.direction);
+  EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, info.direction);
   EXPECT_EQ(3, info.start);
   EXPECT_EQ(4, info.count);
 
   EXPECT_TRUE(bidi.EndChar());
   info = bidi.GetSegmentInfo();
-  EXPECT_EQ(CFX_BidiChar::RIGHT, info.direction);
+  EXPECT_EQ(CFX_BidiChar::Direction::kRight, info.direction);
+  EXPECT_EQ(7, info.start);
+  EXPECT_EQ(1, info.count);
+
+  EXPECT_FALSE(bidi.EndChar());
+}
+
+TEST(fxcrt, BidiCharLeftLeftWeakRight) {
+  CFX_BidiChar bidi;
+  CFX_BidiChar::Segment info;
+
+  EXPECT_TRUE(bidi.AppendChar(kLeftChar));
+  info = bidi.GetSegmentInfo();
+  EXPECT_EQ(0, info.start);
+  EXPECT_EQ(0, info.count);
+
+  EXPECT_FALSE(bidi.AppendChar(kLeftChar));
+  EXPECT_FALSE(bidi.AppendChar(kLeftChar));
+  EXPECT_TRUE(bidi.AppendChar(kLeftWeakChar));
+  info = bidi.GetSegmentInfo();
+  EXPECT_EQ(0, info.start);
+  EXPECT_EQ(3, info.count);
+
+  EXPECT_FALSE(bidi.AppendChar(kLeftWeakChar));
+  EXPECT_FALSE(bidi.AppendChar(kLeftWeakChar));
+  EXPECT_FALSE(bidi.AppendChar(kLeftWeakChar));
+  EXPECT_TRUE(bidi.AppendChar(kRightChar));
+  info = bidi.GetSegmentInfo();
+  EXPECT_EQ(CFX_BidiChar::Direction::kLeftWeak, info.direction);
+  EXPECT_EQ(3, info.start);
+  EXPECT_EQ(4, info.count);
+
+  EXPECT_TRUE(bidi.EndChar());
+  info = bidi.GetSegmentInfo();
+  EXPECT_EQ(CFX_BidiChar::Direction::kRight, info.direction);
   EXPECT_EQ(7, info.start);
   EXPECT_EQ(1, info.count);
 
@@ -105,13 +140,13 @@
   EXPECT_FALSE(bidi.AppendChar(kRightChar));
   EXPECT_TRUE(bidi.AppendChar(kLeftChar));
   info = bidi.GetSegmentInfo();
-  EXPECT_EQ(CFX_BidiChar::RIGHT, info.direction);
+  EXPECT_EQ(CFX_BidiChar::Direction::kRight, info.direction);
   EXPECT_EQ(3, info.start);
   EXPECT_EQ(4, info.count);
 
   EXPECT_TRUE(bidi.EndChar());
   info = bidi.GetSegmentInfo();
-  EXPECT_EQ(CFX_BidiChar::LEFT, info.direction);
+  EXPECT_EQ(CFX_BidiChar::Direction::kLeft, info.direction);
   EXPECT_EQ(7, info.start);
   EXPECT_EQ(1, info.count);
 
@@ -120,7 +155,7 @@
 
 TEST(fxcrt, BidiStringEmpty) {
   CFX_BidiString bidi(L"");
-  EXPECT_EQ(CFX_BidiChar::LEFT, bidi.OverallDirection());
+  EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection());
   EXPECT_TRUE(bidi.begin() == bidi.end());
 }
 
@@ -128,28 +163,28 @@
   {
     const wchar_t str[] = {kNeutralChar, 0};
     CFX_BidiString bidi(str);
-    EXPECT_EQ(CFX_BidiChar::LEFT, bidi.OverallDirection());
+    EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection());
 
     auto it = bidi.begin();
-    ASSERT_FALSE(it == bidi.end());
+    ASSERT_NE(it, bidi.end());
     EXPECT_EQ(0, it->start);
     EXPECT_EQ(1, it->count);
-    EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction);
+    EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction);
     ++it;
-    EXPECT_TRUE(it == bidi.end());
+    EXPECT_EQ(it, bidi.end());
   }
   {
     const wchar_t str[] = {kNeutralChar, kNeutralChar, kNeutralChar, 0};
     CFX_BidiString bidi(str);
-    EXPECT_EQ(CFX_BidiChar::LEFT, bidi.OverallDirection());
+    EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection());
 
     auto it = bidi.begin();
-    ASSERT_FALSE(it == bidi.end());
+    ASSERT_NE(it, bidi.end());
     EXPECT_EQ(0, it->start);
     EXPECT_EQ(3, it->count);
-    EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction);
+    EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction);
     ++it;
-    EXPECT_TRUE(it == bidi.end());
+    EXPECT_EQ(it, bidi.end());
   }
 }
 
@@ -157,44 +192,87 @@
   {
     const wchar_t str[] = {kLeftChar, 0};
     CFX_BidiString bidi(str);
-    EXPECT_EQ(CFX_BidiChar::LEFT, bidi.OverallDirection());
+    EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection());
 
     auto it = bidi.begin();
-    ASSERT_FALSE(it == bidi.end());
+    ASSERT_NE(it, bidi.end());
     EXPECT_EQ(0, it->start);
     EXPECT_EQ(0, it->count);
-    EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction);
-    ASSERT_FALSE(it == bidi.end());
+    EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction);
+    ASSERT_NE(it, bidi.end());
 
     ++it;
     EXPECT_EQ(0, it->start);
     EXPECT_EQ(1, it->count);
-    EXPECT_EQ(CFX_BidiChar::LEFT, it->direction);
-    ASSERT_FALSE(it == bidi.end());
+    EXPECT_EQ(CFX_BidiChar::Direction::kLeft, it->direction);
+    ASSERT_NE(it, bidi.end());
 
     ++it;
-    EXPECT_TRUE(it == bidi.end());
+    EXPECT_EQ(it, bidi.end());
   }
   {
     const wchar_t str[] = {kLeftChar, kLeftChar, kLeftChar, 0};
     CFX_BidiString bidi(str);
-    EXPECT_EQ(CFX_BidiChar::LEFT, bidi.OverallDirection());
+    EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection());
 
     auto it = bidi.begin();
-    ASSERT_FALSE(it == bidi.end());
+    ASSERT_NE(it, bidi.end());
     EXPECT_EQ(0, it->start);
     EXPECT_EQ(0, it->count);
-    EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction);
-    ASSERT_FALSE(it == bidi.end());
+    EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction);
+    ASSERT_NE(it, bidi.end());
 
     ++it;
     EXPECT_EQ(0, it->start);
     EXPECT_EQ(3, it->count);
-    EXPECT_EQ(CFX_BidiChar::LEFT, it->direction);
-    ASSERT_FALSE(it == bidi.end());
+    EXPECT_EQ(CFX_BidiChar::Direction::kLeft, it->direction);
+    ASSERT_NE(it, bidi.end());
 
     ++it;
-    EXPECT_TRUE(it == bidi.end());
+    EXPECT_EQ(it, bidi.end());
+  }
+}
+
+TEST(fxcrt, BidiStringAllLeftWeak) {
+  {
+    const wchar_t str[] = {kLeftWeakChar, 0};
+    CFX_BidiString bidi(str);
+    EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection());
+
+    auto it = bidi.begin();
+    ASSERT_NE(it, bidi.end());
+    EXPECT_EQ(0, it->start);
+    EXPECT_EQ(0, it->count);
+    EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction);
+
+    ++it;
+    ASSERT_NE(it, bidi.end());
+    EXPECT_EQ(0, it->start);
+    EXPECT_EQ(1, it->count);
+    EXPECT_EQ(CFX_BidiChar::Direction::kLeftWeak, it->direction);
+
+    ++it;
+    EXPECT_EQ(it, bidi.end());
+  }
+  {
+    const wchar_t str[] = {kLeftWeakChar, kLeftWeakChar, kLeftWeakChar, 0};
+    CFX_BidiString bidi(str);
+    EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection());
+
+    auto it = bidi.begin();
+    ASSERT_NE(it, bidi.end());
+    EXPECT_EQ(0, it->start);
+    EXPECT_EQ(0, it->count);
+    EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction);
+
+    ++it;
+    ASSERT_NE(it, bidi.end());
+    EXPECT_EQ(0, it->start);
+    EXPECT_EQ(3, it->count);
+    EXPECT_EQ(CFX_BidiChar::Direction::kLeftWeak, it->direction);
+
+    ++it;
+    EXPECT_EQ(it, bidi.end());
   }
 }
 
@@ -202,164 +280,209 @@
   {
     const wchar_t str[] = {kRightChar, 0};
     CFX_BidiString bidi(str);
-    EXPECT_EQ(CFX_BidiChar::RIGHT, bidi.OverallDirection());
+    EXPECT_EQ(CFX_BidiChar::Direction::kRight, bidi.OverallDirection());
 
     auto it = bidi.begin();
     EXPECT_EQ(0, it->start);
     EXPECT_EQ(1, it->count);
-    EXPECT_EQ(CFX_BidiChar::RIGHT, it->direction);
-    ASSERT_FALSE(it == bidi.end());
+    EXPECT_EQ(CFX_BidiChar::Direction::kRight, it->direction);
+    ASSERT_NE(it, bidi.end());
 
     ++it;
-    ASSERT_FALSE(it == bidi.end());
+    ASSERT_NE(it, bidi.end());
     EXPECT_EQ(0, it->start);
     EXPECT_EQ(0, it->count);
-    EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction);
-    ASSERT_FALSE(it == bidi.end());
+    EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction);
+    ASSERT_NE(it, bidi.end());
 
     ++it;
-    EXPECT_TRUE(it == bidi.end());
+    EXPECT_EQ(it, bidi.end());
   }
   {
     const wchar_t str[] = {kRightChar, kRightChar, kRightChar, 0};
     CFX_BidiString bidi(str);
-    EXPECT_EQ(CFX_BidiChar::RIGHT, bidi.OverallDirection());
+    EXPECT_EQ(CFX_BidiChar::Direction::kRight, bidi.OverallDirection());
 
     auto it = bidi.begin();
     EXPECT_EQ(0, it->start);
     EXPECT_EQ(3, it->count);
-    EXPECT_EQ(CFX_BidiChar::RIGHT, it->direction);
-    ASSERT_FALSE(it == bidi.end());
+    EXPECT_EQ(CFX_BidiChar::Direction::kRight, it->direction);
+    ASSERT_NE(it, bidi.end());
 
     ++it;
-    ASSERT_FALSE(it == bidi.end());
+    ASSERT_NE(it, bidi.end());
     EXPECT_EQ(0, it->start);
     EXPECT_EQ(0, it->count);
-    EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction);
-    ASSERT_FALSE(it == bidi.end());
+    EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction);
+    ASSERT_NE(it, bidi.end());
 
     ++it;
-    EXPECT_TRUE(it == bidi.end());
+    EXPECT_EQ(it, bidi.end());
   }
 }
 
 TEST(fxcrt, BidiStringLeftNeutralLeftRight) {
   const wchar_t str[] = {kLeftChar, kNeutralChar, kLeftChar, kRightChar, 0};
   CFX_BidiString bidi(str);
-  EXPECT_EQ(CFX_BidiChar::LEFT, bidi.OverallDirection());
+  EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection());
 
   auto it = bidi.begin();
-  ASSERT_FALSE(it == bidi.end());
+  ASSERT_NE(it, bidi.end());
   EXPECT_EQ(0, it->start);
   EXPECT_EQ(0, it->count);
-  EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction);
-  ASSERT_FALSE(it == bidi.end());
+  EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction);
+  ASSERT_NE(it, bidi.end());
 
   ++it;
   EXPECT_EQ(0, it->start);
   EXPECT_EQ(1, it->count);
-  EXPECT_EQ(CFX_BidiChar::LEFT, it->direction);
-  ASSERT_FALSE(it == bidi.end());
+  EXPECT_EQ(CFX_BidiChar::Direction::kLeft, it->direction);
+  ASSERT_NE(it, bidi.end());
 
   ++it;
   EXPECT_EQ(1, it->start);
   EXPECT_EQ(1, it->count);
-  EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction);
-  ASSERT_FALSE(it == bidi.end());
+  EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction);
+  ASSERT_NE(it, bidi.end());
 
   ++it;
   EXPECT_EQ(2, it->start);
   EXPECT_EQ(1, it->count);
-  EXPECT_EQ(CFX_BidiChar::LEFT, it->direction);
-  ASSERT_FALSE(it == bidi.end());
+  EXPECT_EQ(CFX_BidiChar::Direction::kLeft, it->direction);
+  ASSERT_NE(it, bidi.end());
 
   ++it;
   EXPECT_EQ(3, it->start);
   EXPECT_EQ(1, it->count);
-  EXPECT_EQ(CFX_BidiChar::RIGHT, it->direction);
-  ASSERT_FALSE(it == bidi.end());
+  EXPECT_EQ(CFX_BidiChar::Direction::kRight, it->direction);
+  ASSERT_NE(it, bidi.end());
 
   ++it;
-  EXPECT_TRUE(it == bidi.end());
+  EXPECT_EQ(it, bidi.end());
 }
 
 TEST(fxcrt, BidiStringRightNeutralLeftRight) {
   const wchar_t str[] = {kRightChar, kNeutralChar, kLeftChar, kRightChar, 0};
   CFX_BidiString bidi(str);
-  EXPECT_EQ(CFX_BidiChar::RIGHT, bidi.OverallDirection());
+  EXPECT_EQ(CFX_BidiChar::Direction::kRight, bidi.OverallDirection());
 
   auto it = bidi.begin();
   EXPECT_EQ(3, it->start);
   EXPECT_EQ(1, it->count);
-  EXPECT_EQ(CFX_BidiChar::RIGHT, it->direction);
-  ASSERT_FALSE(it == bidi.end());
+  EXPECT_EQ(CFX_BidiChar::Direction::kRight, it->direction);
+  ASSERT_NE(it, bidi.end());
 
   ++it;
   EXPECT_EQ(2, it->start);
   EXPECT_EQ(1, it->count);
-  EXPECT_EQ(CFX_BidiChar::LEFT, it->direction);
-  ASSERT_FALSE(it == bidi.end());
+  EXPECT_EQ(CFX_BidiChar::Direction::kLeft, it->direction);
+  ASSERT_NE(it, bidi.end());
 
   ++it;
   EXPECT_EQ(1, it->start);
   EXPECT_EQ(1, it->count);
-  EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction);
-  ASSERT_FALSE(it == bidi.end());
+  EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction);
+  ASSERT_NE(it, bidi.end());
 
   ++it;
   EXPECT_EQ(0, it->start);
   EXPECT_EQ(1, it->count);
-  EXPECT_EQ(CFX_BidiChar::RIGHT, it->direction);
-  ASSERT_FALSE(it == bidi.end());
+  EXPECT_EQ(CFX_BidiChar::Direction::kRight, it->direction);
+  ASSERT_NE(it, bidi.end());
 
   ++it;
-  ASSERT_FALSE(it == bidi.end());
+  ASSERT_NE(it, bidi.end());
   EXPECT_EQ(0, it->start);
   EXPECT_EQ(0, it->count);
-  EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction);
-  ASSERT_FALSE(it == bidi.end());
+  EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction);
+  ASSERT_NE(it, bidi.end());
 
   ++it;
-  EXPECT_TRUE(it == bidi.end());
+  EXPECT_EQ(it, bidi.end());
+}
+
+TEST(fxcrt, BidiStringRightLeftWeakLeftRight) {
+  const wchar_t str[] = {kRightChar, kLeftWeakChar, kLeftChar, kRightChar, 0};
+  CFX_BidiString bidi(str);
+  EXPECT_EQ(CFX_BidiChar::Direction::kRight, bidi.OverallDirection());
+
+  auto it = bidi.begin();
+  ASSERT_NE(it, bidi.end());
+  EXPECT_EQ(3, it->start);
+  EXPECT_EQ(1, it->count);
+  EXPECT_EQ(CFX_BidiChar::Direction::kRight, it->direction);
+
+  ++it;
+  ASSERT_NE(it, bidi.end());
+  EXPECT_EQ(2, it->start);
+  EXPECT_EQ(1, it->count);
+  EXPECT_EQ(CFX_BidiChar::Direction::kLeft, it->direction);
+
+  ++it;
+  ASSERT_NE(it, bidi.end());
+  EXPECT_EQ(1, it->start);
+  EXPECT_EQ(1, it->count);
+  EXPECT_EQ(CFX_BidiChar::Direction::kLeftWeak, it->direction);
+
+  ++it;
+  ASSERT_NE(it, bidi.end());
+  EXPECT_EQ(0, it->start);
+  EXPECT_EQ(1, it->count);
+  EXPECT_EQ(CFX_BidiChar::Direction::kRight, it->direction);
+
+  ++it;
+  ASSERT_NE(it, bidi.end());
+  EXPECT_EQ(0, it->start);
+  EXPECT_EQ(0, it->count);
+  EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction);
+
+  ++it;
+  EXPECT_EQ(it, bidi.end());
 }
 
 TEST(fxcrt, BidiStringReverse) {
-  const wchar_t str[] = {kLeftChar, kNeutralChar, kRightChar, kLeftChar, 0};
+  const wchar_t str[] = {kLeftChar,     kNeutralChar, kRightChar,
+                         kLeftWeakChar, kLeftChar,    0};
   CFX_BidiString bidi(str);
-  EXPECT_EQ(CFX_BidiChar::LEFT, bidi.OverallDirection());
+  EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection());
   bidi.SetOverallDirectionRight();
 
   auto it = bidi.begin();
-  ASSERT_FALSE(it == bidi.end());
+  ASSERT_NE(it, bidi.end());
+  EXPECT_EQ(4, it->start);
+  EXPECT_EQ(1, it->count);
+  EXPECT_EQ(CFX_BidiChar::Direction::kLeft, it->direction);
+
+  ++it;
+  ASSERT_NE(it, bidi.end());
   EXPECT_EQ(3, it->start);
   EXPECT_EQ(1, it->count);
-  EXPECT_EQ(CFX_BidiChar::LEFT, it->direction);
+  EXPECT_EQ(CFX_BidiChar::Direction::kLeftWeak, it->direction);
 
   ++it;
-  ASSERT_FALSE(it == bidi.end());
+  ASSERT_NE(it, bidi.end());
   EXPECT_EQ(2, it->start);
   EXPECT_EQ(1, it->count);
-  EXPECT_EQ(CFX_BidiChar::RIGHT, it->direction);
-  ASSERT_FALSE(it == bidi.end());
+  EXPECT_EQ(CFX_BidiChar::Direction::kRight, it->direction);
 
   ++it;
-  ASSERT_FALSE(it == bidi.end());
+  ASSERT_NE(it, bidi.end());
   EXPECT_EQ(1, it->start);
   EXPECT_EQ(1, it->count);
-  EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction);
+  EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction);
 
   ++it;
-  ASSERT_FALSE(it == bidi.end());
+  ASSERT_NE(it, bidi.end());
   EXPECT_EQ(0, it->start);
   EXPECT_EQ(1, it->count);
-  EXPECT_EQ(CFX_BidiChar::LEFT, it->direction);
+  EXPECT_EQ(CFX_BidiChar::Direction::kLeft, it->direction);
 
   ++it;
-  ASSERT_FALSE(it == bidi.end());
+  ASSERT_NE(it, bidi.end());
   EXPECT_EQ(0, it->start);
   EXPECT_EQ(0, it->count);
-  EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction);
+  EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction);
 
   ++it;
-  EXPECT_TRUE(it == bidi.end());
+  EXPECT_EQ(it, bidi.end());
 }
diff --git a/core/fxcrt/fx_codepage.cpp b/core/fxcrt/fx_codepage.cpp
index c85291f..20a5bfa 100644
--- a/core/fxcrt/fx_codepage.cpp
+++ b/core/fxcrt/fx_codepage.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,9 +10,16 @@
 #include <iterator>
 #include <utility>
 
+#include "build/build_config.h"
+#include "third_party/base/numerics/safe_math.h"
+
+#if BUILDFLAG(IS_WIN)
+#include <windows.h>
+#endif
+
 namespace {
 
-const uint16_t g_FX_MSDOSThaiUnicodes[128] = {
+const uint16_t kFX_MSDOSThaiUnicodes[128] = {
     0x20AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x2026, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2018,
     0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x0000, 0x0000,
@@ -30,7 +37,7 @@
     0x0000, 0x0000,
 };
 
-const uint16_t g_FX_MSWinEasternEuropeanUnicodes[128] = {
+const uint16_t kFX_MSWinEasternEuropeanUnicodes[128] = {
     0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000,
     0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179, 0x0000, 0x2018,
     0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0161,
@@ -48,7 +55,7 @@
     0x0163, 0x02D9,
 };
 
-const uint16_t g_FX_MSWinCyrillicUnicodes[128] = {
+const uint16_t kFX_MSWinCyrillicUnicodes[128] = {
     0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, 0x20AC,
     0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F, 0x0452, 0x2018,
     0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0459,
@@ -66,7 +73,7 @@
     0x044E, 0x044F,
 };
 
-const uint16_t g_FX_MSWinGreekUnicodes[128] = {
+const uint16_t kFX_MSWinGreekUnicodes[128] = {
     0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000,
     0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2018,
     0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0000,
@@ -84,7 +91,7 @@
     0x03CE, 0x0000,
 };
 
-const uint16_t g_FX_MSWinTurkishUnicodes[128] = {
+const uint16_t kFX_MSWinTurkishUnicodes[128] = {
     0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6,
     0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000, 0x0000, 0x2018,
     0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161,
@@ -102,7 +109,7 @@
     0x015F, 0x00FF,
 };
 
-const uint16_t g_FX_MSWinHebrewUnicodes[128] = {
+const uint16_t kFX_MSWinHebrewUnicodes[128] = {
     0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6,
     0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2018,
     0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0000,
@@ -120,7 +127,7 @@
     0x200F, 0x0000,
 };
 
-const uint16_t g_FX_MSWinArabicUnicodes[128] = {
+const uint16_t kFX_MSWinArabicUnicodes[128] = {
     0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6,
     0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688, 0x06AF, 0x2018,
     0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x06A9, 0x2122, 0x0691,
@@ -138,7 +145,7 @@
     0x200F, 0x06D2,
 };
 
-const uint16_t g_FX_MSWinBalticUnicodes[128] = {
+const uint16_t kFX_MSWinBalticUnicodes[128] = {
     0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000,
     0x2030, 0x0000, 0x2039, 0x0000, 0x00A8, 0x02C7, 0x00B8, 0x0000, 0x2018,
     0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0000,
@@ -157,81 +164,170 @@
 };
 
 struct FX_CHARSET_MAP {
-  uint16_t charset;
-  uint16_t codepage;
+  FX_Charset charset;
+  FX_CodePage codepage;
 };
 
-const FX_CHARSET_MAP g_FXCharset2CodePageTable[] = {
-    {FX_CHARSET_ANSI, FX_CODEPAGE_MSWin_WesternEuropean},
-    {FX_CHARSET_Default, FX_CODEPAGE_DefANSI},
-    {FX_CHARSET_Symbol, FX_CODEPAGE_Symbol},
-    {FX_CHARSET_MAC_Roman, FX_CODEPAGE_MAC_Roman},
-    {FX_CHARSET_MAC_ShiftJIS, FX_CODEPAGE_MAC_ShiftJIS},
-    {FX_CHARSET_MAC_Korean, FX_CODEPAGE_MAC_Korean},
-    {FX_CHARSET_MAC_ChineseSimplified, FX_CODEPAGE_MAC_ChineseSimplified},
-    {FX_CHARSET_MAC_ChineseTraditional, FX_CODEPAGE_MAC_ChineseTraditional},
-    {FX_CHARSET_MAC_Hebrew, FX_CODEPAGE_MAC_Hebrew},
-    {FX_CHARSET_MAC_Arabic, FX_CODEPAGE_MAC_Arabic},
-    {FX_CHARSET_MAC_Greek, FX_CODEPAGE_MAC_Greek},
-    {FX_CHARSET_MAC_Turkish, FX_CODEPAGE_MAC_Turkish},
-    {FX_CHARSET_MAC_Thai, FX_CODEPAGE_MAC_Thai},
-    {FX_CHARSET_MAC_EasternEuropean, FX_CODEPAGE_MAC_EasternEuropean},
-    {FX_CHARSET_MAC_Cyrillic, FX_CODEPAGE_MAC_Cyrillic},
-    {FX_CHARSET_ShiftJIS, FX_CODEPAGE_ShiftJIS},
-    {FX_CHARSET_Hangul, FX_CODEPAGE_Hangul},
-    {FX_CHARSET_Johab, FX_CODEPAGE_Johab},
-    {FX_CHARSET_ChineseSimplified, FX_CODEPAGE_ChineseSimplified},
-    {FX_CHARSET_ChineseTraditional, FX_CODEPAGE_ChineseTraditional},
-    {FX_CHARSET_MSWin_Greek, FX_CODEPAGE_MSWin_Greek},
-    {FX_CHARSET_MSWin_Turkish, FX_CODEPAGE_MSWin_Turkish},
-    {FX_CHARSET_MSWin_Vietnamese, FX_CODEPAGE_MSWin_Vietnamese},
-    {FX_CHARSET_MSWin_Hebrew, FX_CODEPAGE_MSWin_Hebrew},
-    {FX_CHARSET_MSWin_Arabic, FX_CODEPAGE_MSWin_Arabic},
-    {FX_CHARSET_MSWin_Baltic, FX_CODEPAGE_MSWin_Baltic},
-    {FX_CHARSET_MSWin_Cyrillic, FX_CODEPAGE_MSWin_Cyrillic},
-    {FX_CHARSET_Thai, FX_CODEPAGE_MSDOS_Thai},
-    {FX_CHARSET_MSWin_EasternEuropean, FX_CODEPAGE_MSWin_EasternEuropean},
-    {FX_CHARSET_US, FX_CODEPAGE_MSDOS_US},
-    {FX_CHARSET_OEM, FX_CODEPAGE_MSDOS_WesternEuropean},
+const FX_CHARSET_MAP kFXCharset2CodePageTable[] = {
+    {FX_Charset::kANSI, FX_CodePage::kMSWin_WesternEuropean},
+    {FX_Charset::kDefault, FX_CodePage::kDefANSI},
+    {FX_Charset::kSymbol, FX_CodePage::kSymbol},
+    {FX_Charset::kMAC_Roman, FX_CodePage::kMAC_Roman},
+    {FX_Charset::kMAC_ShiftJIS, FX_CodePage::kMAC_ShiftJIS},
+    {FX_Charset::kMAC_Korean, FX_CodePage::kMAC_Korean},
+    {FX_Charset::kMAC_ChineseSimplified, FX_CodePage::kMAC_ChineseSimplified},
+    {FX_Charset::kMAC_ChineseTraditional, FX_CodePage::kMAC_ChineseTraditional},
+    {FX_Charset::kMAC_Hebrew, FX_CodePage::kMAC_Hebrew},
+    {FX_Charset::kMAC_Arabic, FX_CodePage::kMAC_Arabic},
+    {FX_Charset::kMAC_Greek, FX_CodePage::kMAC_Greek},
+    {FX_Charset::kMAC_Turkish, FX_CodePage::kMAC_Turkish},
+    {FX_Charset::kMAC_Thai, FX_CodePage::kMAC_Thai},
+    {FX_Charset::kMAC_EasternEuropean, FX_CodePage::kMAC_EasternEuropean},
+    {FX_Charset::kMAC_Cyrillic, FX_CodePage::kMAC_Cyrillic},
+    {FX_Charset::kShiftJIS, FX_CodePage::kShiftJIS},
+    {FX_Charset::kHangul, FX_CodePage::kHangul},
+    {FX_Charset::kJohab, FX_CodePage::kJohab},
+    {FX_Charset::kChineseSimplified, FX_CodePage::kChineseSimplified},
+    {FX_Charset::kChineseTraditional, FX_CodePage::kChineseTraditional},
+    {FX_Charset::kMSWin_Greek, FX_CodePage::kMSWin_Greek},
+    {FX_Charset::kMSWin_Turkish, FX_CodePage::kMSWin_Turkish},
+    {FX_Charset::kMSWin_Vietnamese, FX_CodePage::kMSWin_Vietnamese},
+    {FX_Charset::kMSWin_Hebrew, FX_CodePage::kMSWin_Hebrew},
+    {FX_Charset::kMSWin_Arabic, FX_CodePage::kMSWin_Arabic},
+    {FX_Charset::kMSWin_Baltic, FX_CodePage::kMSWin_Baltic},
+    {FX_Charset::kMSWin_Cyrillic, FX_CodePage::kMSWin_Cyrillic},
+    {FX_Charset::kThai, FX_CodePage::kMSDOS_Thai},
+    {FX_Charset::kMSWin_EasternEuropean, FX_CodePage::kMSWin_EasternEuropean},
+    {FX_Charset::kUS, FX_CodePage::kMSDOS_US},
+    {FX_Charset::kOEM, FX_CodePage::kMSDOS_WesternEuropean},
 };
 
 }  // namespace
 
-const FX_CharsetUnicodes g_FX_CharsetUnicodes[8] = {
-    {FX_CHARSET_Thai, g_FX_MSDOSThaiUnicodes},
-    {FX_CHARSET_MSWin_EasternEuropean, g_FX_MSWinEasternEuropeanUnicodes},
-    {FX_CHARSET_MSWin_Cyrillic, g_FX_MSWinCyrillicUnicodes},
-    {FX_CHARSET_MSWin_Greek, g_FX_MSWinGreekUnicodes},
-    {FX_CHARSET_MSWin_Turkish, g_FX_MSWinTurkishUnicodes},
-    {FX_CHARSET_MSWin_Hebrew, g_FX_MSWinHebrewUnicodes},
-    {FX_CHARSET_MSWin_Arabic, g_FX_MSWinArabicUnicodes},
-    {FX_CHARSET_MSWin_Baltic, g_FX_MSWinBalticUnicodes},
+const FX_CharsetUnicodes kFX_CharsetUnicodes[8] = {
+    {FX_Charset::kThai, kFX_MSDOSThaiUnicodes},
+    {FX_Charset::kMSWin_EasternEuropean, kFX_MSWinEasternEuropeanUnicodes},
+    {FX_Charset::kMSWin_Cyrillic, kFX_MSWinCyrillicUnicodes},
+    {FX_Charset::kMSWin_Greek, kFX_MSWinGreekUnicodes},
+    {FX_Charset::kMSWin_Turkish, kFX_MSWinTurkishUnicodes},
+    {FX_Charset::kMSWin_Hebrew, kFX_MSWinHebrewUnicodes},
+    {FX_Charset::kMSWin_Arabic, kFX_MSWinArabicUnicodes},
+    {FX_Charset::kMSWin_Baltic, kFX_MSWinBalticUnicodes},
 };
 
-uint16_t FX_GetCodePageFromCharset(uint8_t charset) {
-  auto* result =
-      std::lower_bound(std::begin(g_FXCharset2CodePageTable),
-                       std::end(g_FXCharset2CodePageTable), charset,
-                       [](const FX_CHARSET_MAP& iter, const uint16_t& charset) {
-                         return iter.charset < charset;
-                       });
-  if (result != std::end(g_FXCharset2CodePageTable) &&
+FX_CodePage FX_GetACP() {
+#if BUILDFLAG(IS_WIN)
+  return static_cast<FX_CodePage>(GetACP());
+#else
+  return FX_CodePage::kDefANSI;
+#endif
+}
+
+FX_CodePage FX_GetCodePageFromCharset(FX_Charset charset) {
+  auto* result = std::lower_bound(
+      std::begin(kFXCharset2CodePageTable), std::end(kFXCharset2CodePageTable),
+      charset, [](const FX_CHARSET_MAP& iter, const FX_Charset& charset) {
+        return iter.charset < charset;
+      });
+  if (result != std::end(kFXCharset2CodePageTable) &&
       result->charset == charset) {
     return result->codepage;
   }
-  return 0xFFFF;
+  return FX_CodePage::kFailure;
 }
 
-uint8_t FX_GetCharsetFromCodePage(uint16_t codepage) {
-  for (const auto& it : g_FXCharset2CodePageTable) {
+FX_Charset FX_GetCharsetFromCodePage(FX_CodePage codepage) {
+  for (const auto& it : kFXCharset2CodePageTable) {
     if (it.codepage == codepage)
       return it.charset;
   }
-  return FX_CHARSET_ANSI;
+  return FX_Charset::kANSI;
 }
 
-bool FX_CharSetIsCJK(uint8_t uCharset) {
-  return (uCharset == FX_CHARSET_ChineseSimplified) ||
-         (uCharset == FX_CHARSET_ChineseTraditional) ||
-         (uCharset == FX_CHARSET_Hangul) || (uCharset == FX_CHARSET_ShiftJIS);
+FX_Charset FX_GetCharsetFromInt(int value) {
+  switch (value) {
+    case static_cast<int>(FX_Charset::kANSI):
+    case static_cast<int>(FX_Charset::kDefault):
+    case static_cast<int>(FX_Charset::kSymbol):
+    case static_cast<int>(FX_Charset::kMAC_Roman):
+    case static_cast<int>(FX_Charset::kMAC_ShiftJIS):
+    case static_cast<int>(FX_Charset::kMAC_Korean):
+    case static_cast<int>(FX_Charset::kMAC_ChineseSimplified):
+    case static_cast<int>(FX_Charset::kMAC_ChineseTraditional):
+    case static_cast<int>(FX_Charset::kMAC_Hebrew):
+    case static_cast<int>(FX_Charset::kMAC_Arabic):
+    case static_cast<int>(FX_Charset::kMAC_Greek):
+    case static_cast<int>(FX_Charset::kMAC_Turkish):
+    case static_cast<int>(FX_Charset::kMAC_Thai):
+    case static_cast<int>(FX_Charset::kMAC_EasternEuropean):
+    case static_cast<int>(FX_Charset::kMAC_Cyrillic):
+    case static_cast<int>(FX_Charset::kShiftJIS):
+    case static_cast<int>(FX_Charset::kHangul):
+    case static_cast<int>(FX_Charset::kJohab):
+    case static_cast<int>(FX_Charset::kChineseSimplified):
+    case static_cast<int>(FX_Charset::kChineseTraditional):
+    case static_cast<int>(FX_Charset::kMSWin_Greek):
+    case static_cast<int>(FX_Charset::kMSWin_Turkish):
+    case static_cast<int>(FX_Charset::kMSWin_Vietnamese):
+    case static_cast<int>(FX_Charset::kMSWin_Hebrew):
+    case static_cast<int>(FX_Charset::kMSWin_Arabic):
+    case static_cast<int>(FX_Charset::kMSWin_Baltic):
+    case static_cast<int>(FX_Charset::kMSWin_Cyrillic):
+    case static_cast<int>(FX_Charset::kThai):
+    case static_cast<int>(FX_Charset::kMSWin_EasternEuropean):
+    case static_cast<int>(FX_Charset::kUS):
+    case static_cast<int>(FX_Charset::kOEM):
+      return static_cast<FX_Charset>(value);
+    default:
+      return FX_Charset::kANSI;
+  }
+}
+
+bool FX_CharSetIsCJK(FX_Charset uCharset) {
+  return (uCharset == FX_Charset::kChineseSimplified) ||
+         (uCharset == FX_Charset::kChineseTraditional) ||
+         (uCharset == FX_Charset::kHangul) ||
+         (uCharset == FX_Charset::kShiftJIS);
+}
+
+size_t FX_WideCharToMultiByte(FX_CodePage codepage,
+                              WideStringView wstr,
+                              pdfium::span<char> buf) {
+#if BUILDFLAG(IS_WIN)
+  int input_len = pdfium::base::checked_cast<int>(wstr.GetLength());
+  int output_len = pdfium::base::checked_cast<int>(buf.size());
+  return WideCharToMultiByte(static_cast<UINT>(codepage), 0,
+                             wstr.unterminated_c_str(), input_len, buf.data(),
+                             output_len, nullptr, nullptr);
+#else
+  size_t len = 0;
+  for (size_t i = 0; i < wstr.GetLength(); i++) {
+    if (wstr[i] < 0x100) {
+      if (len < buf.size())
+        buf[len] = static_cast<char>(wstr[i]);
+      len++;
+    }
+  }
+  return len;
+#endif
+}
+
+size_t FX_MultiByteToWideChar(FX_CodePage codepage,
+                              ByteStringView bstr,
+                              pdfium::span<wchar_t> buf) {
+#if BUILDFLAG(IS_WIN)
+  const int input_len = pdfium::base::checked_cast<int>(bstr.GetLength());
+  const int output_len = pdfium::base::checked_cast<int>(buf.size());
+  return MultiByteToWideChar(static_cast<UINT>(codepage), 0,
+                             bstr.unterminated_c_str(), input_len, buf.data(),
+                             output_len);
+#else
+  size_t wlen = 0;
+  for (size_t i = 0; i < bstr.GetLength(); i++) {
+    if (wlen < buf.size())
+      buf[wlen] = reinterpret_cast<uint8_t>(bstr[i]);
+    wlen++;
+  }
+  return wlen;
+#endif
 }
diff --git a/core/fxcrt/fx_codepage.h b/core/fxcrt/fx_codepage.h
index 4a6c6d8..2aa7741 100644
--- a/core/fxcrt/fx_codepage.h
+++ b/core/fxcrt/fx_codepage.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,97 +9,116 @@
 
 #include <stdint.h>
 
-#define FX_CODEPAGE_DefANSI 0
-#define FX_CODEPAGE_Symbol 42
-#define FX_CODEPAGE_MSDOS_US 437
-#define FX_CODEPAGE_Arabic_ASMO708 708
-#define FX_CODEPAGE_MSDOS_Greek1 737
-#define FX_CODEPAGE_MSDOS_Baltic 775
-#define FX_CODEPAGE_MSDOS_WesternEuropean 850
-#define FX_CODEPAGE_MSDOS_EasternEuropean 852
-#define FX_CODEPAGE_MSDOS_Cyrillic 855
-#define FX_CODEPAGE_MSDOS_Turkish 857
-#define FX_CODEPAGE_MSDOS_Portuguese 860
-#define FX_CODEPAGE_MSDOS_Icelandic 861
-#define FX_CODEPAGE_MSDOS_Hebrew 862
-#define FX_CODEPAGE_MSDOS_FrenchCanadian 863
-#define FX_CODEPAGE_MSDOS_Arabic 864
-#define FX_CODEPAGE_MSDOS_Norwegian 865
-#define FX_CODEPAGE_MSDOS_Russian 866
-#define FX_CODEPAGE_MSDOS_Greek2 869
-#define FX_CODEPAGE_MSDOS_Thai 874
-#define FX_CODEPAGE_ShiftJIS 932
-#define FX_CODEPAGE_ChineseSimplified 936
-#define FX_CODEPAGE_Hangul 949
-#define FX_CODEPAGE_ChineseTraditional 950
-#define FX_CODEPAGE_UTF16LE 1200
-#define FX_CODEPAGE_UTF16BE 1201
-#define FX_CODEPAGE_MSWin_EasternEuropean 1250
-#define FX_CODEPAGE_MSWin_Cyrillic 1251
-#define FX_CODEPAGE_MSWin_WesternEuropean 1252
-#define FX_CODEPAGE_MSWin_Greek 1253
-#define FX_CODEPAGE_MSWin_Turkish 1254
-#define FX_CODEPAGE_MSWin_Hebrew 1255
-#define FX_CODEPAGE_MSWin_Arabic 1256
-#define FX_CODEPAGE_MSWin_Baltic 1257
-#define FX_CODEPAGE_MSWin_Vietnamese 1258
-#define FX_CODEPAGE_Johab 1361
-#define FX_CODEPAGE_MAC_Roman 10000
-#define FX_CODEPAGE_MAC_ShiftJIS 10001
-#define FX_CODEPAGE_MAC_ChineseTraditional 10002
-#define FX_CODEPAGE_MAC_Korean 10003
-#define FX_CODEPAGE_MAC_Arabic 10004
-#define FX_CODEPAGE_MAC_Hebrew 10005
-#define FX_CODEPAGE_MAC_Greek 10006
-#define FX_CODEPAGE_MAC_Cyrillic 10007
-#define FX_CODEPAGE_MAC_ChineseSimplified 10008
-#define FX_CODEPAGE_MAC_Thai 10021
-#define FX_CODEPAGE_MAC_EasternEuropean 10029
-#define FX_CODEPAGE_MAC_Turkish 10081
-#define FX_CODEPAGE_UTF8 65001
+// Prove consistency with incomplete forward definitions.
+#include "core/fxcrt/fx_codepage_forward.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
+#include "third_party/base/containers/span.h"
 
-#define FX_CHARSET_ANSI 0
-#define FX_CHARSET_Default 1
-#define FX_CHARSET_Symbol 2
-#define FX_CHARSET_MAC_Roman 77
-#define FX_CHARSET_MAC_ShiftJIS 78
-#define FX_CHARSET_MAC_Korean 79
-#define FX_CHARSET_MAC_ChineseSimplified 80
-#define FX_CHARSET_MAC_ChineseTraditional 81
-#define FX_CHARSET_MAC_Hebrew 83
-#define FX_CHARSET_MAC_Arabic 84
-#define FX_CHARSET_MAC_Greek 85
-#define FX_CHARSET_MAC_Turkish 86
-#define FX_CHARSET_MAC_Thai 87
-#define FX_CHARSET_MAC_EasternEuropean 88
-#define FX_CHARSET_MAC_Cyrillic 89
-#define FX_CHARSET_ShiftJIS 128
-#define FX_CHARSET_Hangul 129
-#define FX_CHARSET_Johab 130
-#define FX_CHARSET_ChineseSimplified 134
-#define FX_CHARSET_ChineseTraditional 136
-#define FX_CHARSET_MSWin_Greek 161
-#define FX_CHARSET_MSWin_Turkish 162
-#define FX_CHARSET_MSWin_Vietnamese 163
-#define FX_CHARSET_MSWin_Hebrew 177
-#define FX_CHARSET_MSWin_Arabic 178
-#define FX_CHARSET_MSWin_Baltic 186
-#define FX_CHARSET_MSWin_Cyrillic 204
-#define FX_CHARSET_Thai 222
-#define FX_CHARSET_MSWin_EasternEuropean 238
-#define FX_CHARSET_US 254
-#define FX_CHARSET_OEM 255
+enum class FX_CodePage : uint16_t {
+  kDefANSI = 0,
+  kSymbol = 42,
+  kMSDOS_US = 437,
+  kArabic_ASMO708 = 708,
+  kMSDOS_Greek1 = 737,
+  kMSDOS_Baltic = 775,
+  kMSDOS_WesternEuropean = 850,
+  kMSDOS_EasternEuropean = 852,
+  kMSDOS_Cyrillic = 855,
+  kMSDOS_Turkish = 857,
+  kMSDOS_Portuguese = 860,
+  kMSDOS_Icelandic = 861,
+  kMSDOS_Hebrew = 862,
+  kMSDOS_FrenchCanadian = 863,
+  kMSDOS_Arabic = 864,
+  kMSDOS_Norwegian = 865,
+  kMSDOS_Russian = 866,
+  kMSDOS_Greek2 = 869,
+  kMSDOS_Thai = 874,
+  kShiftJIS = 932,
+  kChineseSimplified = 936,
+  kHangul = 949,
+  kChineseTraditional = 950,
+  kUTF16LE = 1200,
+  kUTF16BE = 1201,
+  kMSWin_EasternEuropean = 1250,
+  kMSWin_Cyrillic = 1251,
+  kMSWin_WesternEuropean = 1252,
+  kMSWin_Greek = 1253,
+  kMSWin_Turkish = 1254,
+  kMSWin_Hebrew = 1255,
+  kMSWin_Arabic = 1256,
+  kMSWin_Baltic = 1257,
+  kMSWin_Vietnamese = 1258,
+  kJohab = 1361,
+  kMAC_Roman = 10000,
+  kMAC_ShiftJIS = 10001,
+  kMAC_ChineseTraditional = 10002,
+  kMAC_Korean = 10003,
+  kMAC_Arabic = 10004,
+  kMAC_Hebrew = 10005,
+  kMAC_Greek = 10006,
+  kMAC_Cyrillic = 10007,
+  kMAC_ChineseSimplified = 10008,
+  kMAC_Thai = 10021,
+  kMAC_EasternEuropean = 10029,
+  kMAC_Turkish = 10081,
+  kUTF8 = 65001,
+  kFailure = 65535,
+};
+
+enum class FX_Charset : uint8_t {
+  kANSI = 0,
+  kDefault = 1,
+  kSymbol = 2,
+  kMAC_Roman = 77,
+  kMAC_ShiftJIS = 78,
+  kMAC_Korean = 79,
+  kMAC_ChineseSimplified = 80,
+  kMAC_ChineseTraditional = 81,
+  kMAC_Hebrew = 83,
+  kMAC_Arabic = 84,
+  kMAC_Greek = 85,
+  kMAC_Turkish = 86,
+  kMAC_Thai = 87,
+  kMAC_EasternEuropean = 88,
+  kMAC_Cyrillic = 89,
+  kShiftJIS = 128,
+  kHangul = 129,
+  kJohab = 130,
+  kChineseSimplified = 134,
+  kChineseTraditional = 136,
+  kMSWin_Greek = 161,
+  kMSWin_Turkish = 162,
+  kMSWin_Vietnamese = 163,
+  kMSWin_Hebrew = 177,
+  kMSWin_Arabic = 178,
+  kMSWin_Baltic = 186,
+  kMSWin_Cyrillic = 204,
+  kThai = 222,
+  kMSWin_EasternEuropean = 238,
+  kUS = 254,
+  kOEM = 255,
+};
 
 // Hi-bytes to unicode codepoint mapping for various code pages.
 struct FX_CharsetUnicodes {
-  uint8_t m_Charset;
-  const uint16_t* m_pUnicodes;
+  FX_Charset m_Charset;
+  UNOWNED_PTR_EXCLUSION const uint16_t* m_pUnicodes;  // POD struct.
 };
 
-extern const FX_CharsetUnicodes g_FX_CharsetUnicodes[8];
+extern const FX_CharsetUnicodes kFX_CharsetUnicodes[8];
 
-uint16_t FX_GetCodePageFromCharset(uint8_t charset);
-uint8_t FX_GetCharsetFromCodePage(uint16_t codepage);
-bool FX_CharSetIsCJK(uint8_t uCharset);
+FX_CodePage FX_GetACP();
+FX_CodePage FX_GetCodePageFromCharset(FX_Charset charset);
+FX_Charset FX_GetCharsetFromCodePage(FX_CodePage codepage);
+FX_Charset FX_GetCharsetFromInt(int value);
+bool FX_CharSetIsCJK(FX_Charset uCharset);
+size_t FX_WideCharToMultiByte(FX_CodePage codepage,
+                              WideStringView wstr,
+                              pdfium::span<char> buf);
+size_t FX_MultiByteToWideChar(FX_CodePage codepage,
+                              ByteStringView bstr,
+                              pdfium::span<wchar_t> buf);
 
 #endif  // CORE_FXCRT_FX_CODEPAGE_H_
diff --git a/core/fxcrt/fx_codepage_forward.h b/core/fxcrt/fx_codepage_forward.h
new file mode 100644
index 0000000..c67eaf2
--- /dev/null
+++ b/core/fxcrt/fx_codepage_forward.h
@@ -0,0 +1,17 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCRT_FX_CODEPAGE_FORWARD_H_
+#define CORE_FXCRT_FX_CODEPAGE_FORWARD_H_
+
+#include <stdint.h>
+
+// Incomplete definitions of large enumerated type for headers that don't
+// need the specifics.
+enum class FX_Charset : uint8_t;
+enum class FX_CodePage : uint16_t;
+
+#endif  // CORE_FXCRT_FX_CODEPAGE_FORWARD_H_
diff --git a/core/fxcrt/fx_coordinates.cpp b/core/fxcrt/fx_coordinates.cpp
index f76df5f..2cf87d6 100644
--- a/core/fxcrt/fx_coordinates.cpp
+++ b/core/fxcrt/fx_coordinates.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,12 +6,20 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 
+#include <math.h>
+
+#include <algorithm>
+#include <iterator>
 #include <utility>
 
 #include "build/build_config.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/numerics/safe_conversions.h"
+#include "core/fxcrt/fx_system.h"
+
+#ifndef NDEBUG
+#include <ostream>
+#endif
 
 namespace {
 
@@ -33,7 +41,7 @@
   }
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 static_assert(sizeof(FX_RECT) == sizeof(RECT), "FX_RECT vs. RECT mismatch");
 static_assert(offsetof(FX_RECT, left) == offsetof(RECT, left),
               "FX_RECT vs. RECT mismatch");
@@ -55,6 +63,29 @@
 
 }  // namespace
 
+template <>
+float CFX_VTemplate<float>::Length() const {
+  return FXSYS_sqrt2(x, y);
+}
+
+template <>
+void CFX_VTemplate<float>::Normalize() {
+  float fLen = Length();
+  if (fLen < 0.0001f)
+    return;
+
+  x /= fLen;
+  y /= fLen;
+}
+
+bool FX_RECT::Valid() const {
+  FX_SAFE_INT32 w = right;
+  FX_SAFE_INT32 h = bottom;
+  w -= left;
+  h -= top;
+  return w.IsValid() && h.IsValid();
+}
+
 void FX_RECT::Normalize() {
   if (left > right)
     std::swap(left, right);
@@ -75,24 +106,50 @@
   }
 }
 
+FX_RECT FX_RECT::SwappedClipBox(int width,
+                                int height,
+                                bool bFlipX,
+                                bool bFlipY) const {
+  FX_RECT rect;
+  if (bFlipY) {
+    rect.left = height - top;
+    rect.right = height - bottom;
+  } else {
+    rect.left = top;
+    rect.right = bottom;
+  }
+  if (bFlipX) {
+    rect.top = width - left;
+    rect.bottom = width - right;
+  } else {
+    rect.top = left;
+    rect.bottom = right;
+  }
+  rect.Normalize();
+  return rect;
+}
+
 // Y-axis runs the opposite way in FX_RECT.
 CFX_FloatRect::CFX_FloatRect(const FX_RECT& rect)
     : left(rect.left), bottom(rect.top), right(rect.right), top(rect.bottom) {}
 
+CFX_FloatRect::CFX_FloatRect(const CFX_PointF& point)
+    : left(point.x), bottom(point.y), right(point.x), top(point.y) {}
+
 // static
-CFX_FloatRect CFX_FloatRect::GetBBox(const CFX_PointF* pPoints, int nPoints) {
-  if (nPoints == 0)
+CFX_FloatRect CFX_FloatRect::GetBBox(pdfium::span<const CFX_PointF> pPoints) {
+  if (pPoints.empty())
     return CFX_FloatRect();
 
-  float min_x = pPoints->x;
-  float max_x = pPoints->x;
-  float min_y = pPoints->y;
-  float max_y = pPoints->y;
-  for (int i = 1; i < nPoints; i++) {
-    min_x = std::min(min_x, pPoints[i].x);
-    max_x = std::max(max_x, pPoints[i].x);
-    min_y = std::min(min_y, pPoints[i].y);
-    max_y = std::max(max_y, pPoints[i].y);
+  float min_x = pPoints.front().x;
+  float max_x = pPoints.front().x;
+  float min_y = pPoints.front().y;
+  float max_y = pPoints.front().y;
+  for (const auto& point : pPoints.subspan(1)) {
+    min_x = std::min(min_x, point.x);
+    max_x = std::max(max_x, point.x);
+    min_y = std::min(min_y, point.y);
+    max_y = std::max(max_y, point.y);
   }
   return CFX_FloatRect(min_x, min_y, max_x, max_y);
 }
@@ -269,6 +326,45 @@
                  FXSYS_roundf(bottom));
 }
 
+void CFX_RectF::Union(float x, float y) {
+  float r = right();
+  float b = bottom();
+
+  left = std::min(left, x);
+  top = std::min(top, y);
+  r = std::max(r, x);
+  b = std::max(b, y);
+
+  width = r - left;
+  height = b - top;
+}
+
+void CFX_RectF::Union(const CFX_RectF& rt) {
+  float r = right();
+  float b = bottom();
+
+  left = std::min(left, rt.left);
+  top = std::min(top, rt.top);
+  r = std::max(r, rt.right());
+  b = std::max(b, rt.bottom());
+
+  width = r - left;
+  height = b - top;
+}
+
+void CFX_RectF::Intersect(const CFX_RectF& rt) {
+  float r = right();
+  float b = bottom();
+
+  left = std::max(left, rt.left);
+  top = std::max(top, rt.top);
+  r = std::min(r, rt.right());
+  b = std::min(b, rt.bottom());
+
+  width = r - left;
+  height = b - top;
+}
+
 FX_RECT CFX_RectF::GetOuterRect() const {
   return FX_RECT(static_cast<int32_t>(floor(left)),
                  static_cast<int32_t>(floor(top)),
@@ -357,7 +453,7 @@
     return (a > 0 ? a : -a);
   if (a == 0)
     return (b > 0 ? b : -b);
-  return sqrt(a * a + b * b);
+  return FXSYS_sqrt2(a, b);
 }
 
 float CFX_Matrix::GetYUnit() const {
@@ -365,7 +461,7 @@
     return (d > 0 ? d : -d);
   if (d == 0)
     return (c > 0 ? c : -c);
-  return sqrt(c * c + d * d);
+  return FXSYS_sqrt2(c, d);
 }
 
 CFX_FloatRect CFX_Matrix::GetUnitRect() const {
@@ -375,7 +471,7 @@
 float CFX_Matrix::TransformXDistance(float dx) const {
   float fx = a * dx;
   float fy = b * dx;
-  return sqrt(fx * fx + fy * fy);
+  return FXSYS_sqrt2(fx, fy);
 }
 
 float CFX_Matrix::TransformDistance(float distance) const {
@@ -405,7 +501,7 @@
   float new_left = points[0].x;
   float new_top = points[0].y;
   float new_bottom = points[0].y;
-  for (size_t i = 1; i < FX_ArraySize(points); i++) {
+  for (size_t i = 1; i < std::size(points); i++) {
     new_right = std::max(new_right, points[i].x);
     new_left = std::min(new_left, points[i].x);
     new_top = std::max(new_top, points[i].y);
diff --git a/core/fxcrt/fx_coordinates.h b/core/fxcrt/fx_coordinates.h
index e8d1a8a..f6b2347 100644
--- a/core/fxcrt/fx_coordinates.h
+++ b/core/fxcrt/fx_coordinates.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,29 +7,23 @@
 #ifndef CORE_FXCRT_FX_COORDINATES_H_
 #define CORE_FXCRT_FX_COORDINATES_H_
 
-#include <algorithm>
-
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/numerics/safe_math.h"
+#include <stdint.h>
 
 #ifndef NDEBUG
-#include <ostream>
+#include <iosfwd>
 #endif
 
+#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;
   }
@@ -53,22 +47,21 @@
     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>;
 using CFX_PointF = CFX_PTemplate<float>;
 
 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>;
@@ -148,29 +134,8 @@
                 const CFX_PTemplate<BaseType>& point2)
       : CFX_PTemplate<BaseType>(point2.x - point1.x, point2.y - point1.y) {}
 
-  float Length() const { return sqrt(x * x + y * y); }
-  void Normalize() {
-    float fLen = Length();
-    if (fLen < 0.0001f)
-      return;
-
-    x /= fLen;
-    y /= fLen;
-  }
-  void Translate(BaseType dx, BaseType dy) {
-    x += dx;
-    y += dy;
-  }
-  void Scale(BaseType sx, BaseType sy) {
-    x *= sx;
-    y *= sy;
-  }
-  void Rotate(float fRadian) {
-    float cosValue = cos(fRadian);
-    float sinValue = sin(fRadian);
-    x = x * cosValue - y * sinValue;
-    y = x * sinValue + y * cosValue;
-  }
+  float Length() const;
+  void Normalize();
 };
 using CFX_Vector = CFX_VTemplate<int32_t>;
 using CFX_VectorF = CFX_VTemplate<float>;
@@ -181,24 +146,22 @@
 // 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; }
   bool IsEmpty() const { return right <= left || bottom <= top; }
 
-  bool Valid() const {
-    pdfium::base::CheckedNumeric<int> w = right;
-    pdfium::base::CheckedNumeric<int> h = bottom;
-    w -= left;
-    h -= top;
-    return w.IsValid() && h.IsValid();
-  }
+  bool Valid() const;
 
   void Normalize();
   void Intersect(const FX_RECT& src);
   void Intersect(int l, int t, int r, int b) { Intersect(FX_RECT(l, t, r, b)); }
+  FX_RECT SwappedClipBox(int width, int height, bool bFlipX, bool bFlipY) const;
 
   void Offset(int dx, int dy) {
     left += dx;
@@ -228,13 +191,13 @@
   constexpr CFX_FloatRect() = default;
   constexpr CFX_FloatRect(float l, float b, float r, float t)
       : left(l), bottom(b), right(r), top(t) {}
-
-  explicit CFX_FloatRect(const float* pArray)
-      : CFX_FloatRect(pArray[0], pArray[1], pArray[2], pArray[3]) {}
+  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);
 
-  static CFX_FloatRect GetBBox(const CFX_PointF* pPoints, int nPoints);
+  static CFX_FloatRect GetBBox(pdfium::span<const CFX_PointF> pPoints);
 
   void Normalize();
 
@@ -261,12 +224,6 @@
 
   CFX_FloatRect GetCenterSquare() const;
 
-  void InitRect(const CFX_PointF& point) {
-    left = point.x;
-    right = point.x;
-    bottom = point.y;
-    top = point.y;
-  }
   void UpdateRect(const CFX_PointF& point);
 
   float Width() const { return right - left; }
@@ -308,6 +265,11 @@
   // Rounds LBRT values.
   FX_RECT ToRoundedFxRect() const;
 
+  bool operator==(const CFX_FloatRect& other) const {
+    return left == other.left && right == other.right && top == other.top &&
+           bottom == other.bottom;
+  }
+
   float left = 0.0f;
   float bottom = 0.0f;
   float right = 0.0f;
@@ -324,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),
@@ -342,9 +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 PointType& p) {
     left += p.x;
     top += p.y;
@@ -436,43 +401,10 @@
   PointType Center() const {
     return PointType(left + width / 2, top + height / 2);
   }
-  void Union(float x, float y) {
-    float r = right();
-    float b = bottom();
-
-    left = std::min(left, x);
-    top = std::min(top, y);
-    r = std::max(r, x);
-    b = std::max(b, y);
-
-    width = r - left;
-    height = b - top;
-  }
+  void Union(float x, float y);
   void Union(const PointType& p) { Union(p.x, p.y); }
-  void Union(const CFX_RectF& rt) {
-    float r = right();
-    float b = bottom();
-
-    left = std::min(left, rt.left);
-    top = std::min(top, rt.top);
-    r = std::max(r, rt.right());
-    b = std::max(b, rt.bottom());
-
-    width = r - left;
-    height = b - top;
-  }
-  void Intersect(const CFX_RectF& rt) {
-    float r = right();
-    float b = bottom();
-
-    left = std::max(left, rt.left);
-    top = std::max(top, rt.top);
-    r = std::min(r, rt.right());
-    b = std::min(b, rt.bottom());
-
-    width = r - left;
-    height = b - top;
-  }
+  void Union(const CFX_RectF& rt);
+  void Intersect(const CFX_RectF& rt);
   bool IntersectWith(const CFX_RectF& rt) const {
     CFX_RectF rect = rt;
     rect.Intersect(*this);
diff --git a/core/fxcrt/fx_coordinates_unittest.cpp b/core/fxcrt/fx_coordinates_unittest.cpp
index 7539ab7..29c8028 100644
--- a/core/fxcrt/fx_coordinates_unittest.cpp
+++ b/core/fxcrt/fx_coordinates_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #include <limits>
 #include <vector>
 
+#include "core/fxcrt/fx_system.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -29,7 +30,7 @@
 }
 
 TEST(CFX_FloatRect, GetBBox) {
-  CFX_FloatRect rect = CFX_FloatRect::GetBBox(nullptr, 0);
+  CFX_FloatRect rect = CFX_FloatRect::GetBBox({nullptr, 0});
   EXPECT_FLOAT_EQ(0.0f, rect.left);
   EXPECT_FLOAT_EQ(0.0f, rect.bottom);
   EXPECT_FLOAT_EQ(0.0f, rect.right);
@@ -37,12 +38,12 @@
 
   std::vector<CFX_PointF> data;
   data.emplace_back(0.0f, 0.0f);
-  rect = CFX_FloatRect::GetBBox(data.data(), 0);
+  rect = CFX_FloatRect::GetBBox(pdfium::make_span(data).first(0));
   EXPECT_FLOAT_EQ(0.0f, rect.left);
   EXPECT_FLOAT_EQ(0.0f, rect.bottom);
   EXPECT_FLOAT_EQ(0.0f, rect.right);
   EXPECT_FLOAT_EQ(0.0f, rect.top);
-  rect = CFX_FloatRect::GetBBox(data.data(), data.size());
+  rect = CFX_FloatRect::GetBBox(data);
   EXPECT_FLOAT_EQ(0.0f, rect.left);
   EXPECT_FLOAT_EQ(0.0f, rect.bottom);
   EXPECT_FLOAT_EQ(0.0f, rect.right);
@@ -50,34 +51,34 @@
 
   data.emplace_back(2.5f, 6.2f);
   data.emplace_back(1.5f, 6.2f);
-  rect = CFX_FloatRect::GetBBox(data.data(), 2);
+  rect = CFX_FloatRect::GetBBox(pdfium::make_span(data).first(2));
   EXPECT_FLOAT_EQ(0.0f, rect.left);
   EXPECT_FLOAT_EQ(0.0f, rect.bottom);
   EXPECT_FLOAT_EQ(2.5f, rect.right);
   EXPECT_FLOAT_EQ(6.2f, rect.top);
 
-  rect = CFX_FloatRect::GetBBox(data.data(), data.size());
+  rect = CFX_FloatRect::GetBBox(data);
   EXPECT_FLOAT_EQ(0.0f, rect.left);
   EXPECT_FLOAT_EQ(0.0f, rect.bottom);
   EXPECT_FLOAT_EQ(2.5f, rect.right);
   EXPECT_FLOAT_EQ(6.2f, rect.top);
 
   data.emplace_back(2.5f, 6.3f);
-  rect = CFX_FloatRect::GetBBox(data.data(), data.size());
+  rect = CFX_FloatRect::GetBBox(data);
   EXPECT_FLOAT_EQ(0.0f, rect.left);
   EXPECT_FLOAT_EQ(0.0f, rect.bottom);
   EXPECT_FLOAT_EQ(2.5f, rect.right);
   EXPECT_FLOAT_EQ(6.3f, rect.top);
 
   data.emplace_back(-3.0f, 6.3f);
-  rect = CFX_FloatRect::GetBBox(data.data(), data.size());
+  rect = CFX_FloatRect::GetBBox(data);
   EXPECT_FLOAT_EQ(-3.0f, rect.left);
   EXPECT_FLOAT_EQ(0.0f, rect.bottom);
   EXPECT_FLOAT_EQ(2.5f, rect.right);
   EXPECT_FLOAT_EQ(6.3f, rect.top);
 
   data.emplace_back(4.0f, -8.0f);
-  rect = CFX_FloatRect::GetBBox(data.data(), data.size());
+  rect = CFX_FloatRect::GetBBox(data);
   EXPECT_FLOAT_EQ(-3.0f, rect.left);
   EXPECT_FLOAT_EQ(-8.0f, rect.bottom);
   EXPECT_FLOAT_EQ(4.0f, rect.right);
@@ -457,11 +458,11 @@
 #define EXPECT_NEAR_FIVE_PLACES(a, b) EXPECT_NEAR((a), (b), 1e-5)
 
 TEST(CFX_Matrix, ComposeTransformations) {
-  // sin(FX_PI/2) and cos(FX_PI/2) have a tiny error and are not exactly 1.0f
-  // and 0.0f. The rotation matrix is thus not perfect.
+  // sin(FXSYS_PI/2) and cos(FXSYS_PI/2) have a tiny error and are not
+  // exactly 1.0f and 0.0f. The rotation matrix is thus not perfect.
 
   CFX_Matrix rotate_90;
-  rotate_90.Rotate(FX_PI / 2);
+  rotate_90.Rotate(FXSYS_PI / 2);
   EXPECT_NEAR_FIVE_PLACES(0.0f, rotate_90.a);
   EXPECT_NEAR_FIVE_PLACES(1.0f, rotate_90.b);
   EXPECT_NEAR_FIVE_PLACES(-1.0f, rotate_90.c);
@@ -585,7 +586,7 @@
 
 TEST(CFX_Matrix, TransformRectForRectF) {
   CFX_Matrix rotate_90;
-  rotate_90.Rotate(FX_PI / 2);
+  rotate_90.Rotate(FXSYS_PI / 2);
 
   CFX_Matrix scale_5_13;
   scale_5_13.Scale(5, 13);
@@ -606,7 +607,7 @@
 
 TEST(CFX_Matrix, TransformRectForFloatRect) {
   CFX_Matrix rotate_90;
-  rotate_90.Rotate(FX_PI / 2);
+  rotate_90.Rotate(FXSYS_PI / 2);
 
   CFX_Matrix scale_5_13;
   scale_5_13.Scale(5, 13);
diff --git a/core/fxcrt/fx_extension.cpp b/core/fxcrt/fx_extension.cpp
index 8d76151..dc312f0 100644
--- a/core/fxcrt/fx_extension.cpp
+++ b/core/fxcrt/fx_extension.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,11 @@
 #include "core/fxcrt/fx_extension.h"
 
 #include <algorithm>
-#include <cwctype>
 #include <limits>
 
-#include "third_party/base/compiler_specific.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/utf16.h"
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -27,39 +28,36 @@
 
 }  // namespace
 
-float FXSYS_wcstof(const wchar_t* pwsStr, int32_t iLength, int32_t* pUsedLen) {
-  ASSERT(pwsStr);
-
-  if (iLength < 0)
-    iLength = static_cast<int32_t>(wcslen(pwsStr));
-  if (iLength == 0)
+float FXSYS_wcstof(const wchar_t* pwsStr, size_t nLength, size_t* pUsedLen) {
+  DCHECK(pwsStr);
+  if (nLength == 0)
     return 0.0f;
 
-  int32_t iUsedLen = 0;
+  size_t nUsedLen = 0;
   bool bNegtive = false;
-  switch (pwsStr[iUsedLen]) {
+  switch (pwsStr[nUsedLen]) {
     case '-':
       bNegtive = true;
-      FALLTHROUGH;
+      [[fallthrough]];
     case '+':
-      iUsedLen++;
+      nUsedLen++;
       break;
   }
 
   float fValue = 0.0f;
-  while (iUsedLen < iLength) {
-    wchar_t wch = pwsStr[iUsedLen];
+  while (nUsedLen < nLength) {
+    wchar_t wch = pwsStr[nUsedLen];
     if (!FXSYS_IsDecimalDigit(wch))
       break;
 
     fValue = fValue * 10.0f + (wch - L'0');
-    iUsedLen++;
+    nUsedLen++;
   }
 
-  if (iUsedLen < iLength && pwsStr[iUsedLen] == L'.') {
+  if (nUsedLen < nLength && pwsStr[nUsedLen] == L'.') {
     float fPrecise = 0.1f;
-    while (++iUsedLen < iLength) {
-      wchar_t wch = pwsStr[iUsedLen];
+    while (++nUsedLen < nLength) {
+      wchar_t wch = pwsStr[nUsedLen];
       if (!FXSYS_IsDecimalDigit(wch))
         break;
 
@@ -68,20 +66,20 @@
     }
   }
 
-  if (iUsedLen < iLength &&
-      (pwsStr[iUsedLen] == 'e' || pwsStr[iUsedLen] == 'E')) {
-    ++iUsedLen;
+  if (nUsedLen < nLength &&
+      (pwsStr[nUsedLen] == 'e' || pwsStr[nUsedLen] == 'E')) {
+    ++nUsedLen;
 
     bool negative_exponent = false;
-    if (iUsedLen < iLength &&
-        (pwsStr[iUsedLen] == '-' || pwsStr[iUsedLen] == '+')) {
-      negative_exponent = pwsStr[iUsedLen] == '-';
-      ++iUsedLen;
+    if (nUsedLen < nLength &&
+        (pwsStr[nUsedLen] == '-' || pwsStr[nUsedLen] == '+')) {
+      negative_exponent = pwsStr[nUsedLen] == '-';
+      ++nUsedLen;
     }
 
     int32_t exp_value = 0;
-    while (iUsedLen < iLength) {
-      wchar_t wch = pwsStr[iUsedLen];
+    while (nUsedLen < nLength) {
+      wchar_t wch = pwsStr[nUsedLen];
       if (!FXSYS_IsDecimalDigit(wch))
         break;
 
@@ -96,7 +94,7 @@
         return 0.0f;
       }
 
-      ++iUsedLen;
+      ++nUsedLen;
     }
 
     for (size_t i = exp_value; i > 0; --i) {
@@ -110,15 +108,15 @@
   }
 
   if (pUsedLen)
-    *pUsedLen = iUsedLen;
+    *pUsedLen = nUsedLen;
 
   return bNegtive ? -fValue : fValue;
 }
 
 wchar_t* FXSYS_wcsncpy(wchar_t* dstStr, const wchar_t* srcStr, size_t count) {
-  ASSERT(dstStr);
-  ASSERT(srcStr);
-  ASSERT(count > 0);
+  DCHECK(dstStr);
+  DCHECK(srcStr);
+  DCHECK(count > 0);
 
   for (size_t i = 0; i < count; ++i)
     if ((dstStr[i] = srcStr[i]) == L'\0')
@@ -127,9 +125,9 @@
 }
 
 int32_t FXSYS_wcsnicmp(const wchar_t* s1, const wchar_t* s2, size_t count) {
-  ASSERT(s1);
-  ASSERT(s2);
-  ASSERT(count > 0);
+  DCHECK(s1);
+  DCHECK(s2);
+  DCHECK(count > 0);
 
   wchar_t wch1 = 0, wch2 = 0;
   while (count-- > 0) {
@@ -153,16 +151,17 @@
 }
 
 size_t FXSYS_ToUTF16BE(uint32_t unicode, char* buf) {
-  ASSERT(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_extension.h b/core/fxcrt/fx_extension.h
index 28b23cf..ae94479 100644
--- a/core/fxcrt/fx_extension.h
+++ b/core/fxcrt/fx_extension.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,12 @@
 #ifndef CORE_FXCRT_FX_EXTENSION_H_
 #define CORE_FXCRT_FX_EXTENSION_H_
 
+#include <ctype.h>
+#include <math.h>
 #include <time.h>
+#include <wctype.h>
 
-#include <cctype>
-#include <cmath>
-#include <cwctype>
-#include <memory>
-
-#include "core/fxcrt/fx_string.h"
+#include "build/build_config.h"
 
 #if defined(USE_SYSTEM_ICUUC)
 #include <unicode/uchar.h>
@@ -24,11 +22,9 @@
 
 #define FX_INVALID_OFFSET static_cast<uint32_t>(-1)
 
-#ifdef PDF_ENABLE_XFA
 #define FX_IsOdd(a) ((a)&1)
-#endif  // PDF_ENABLE_XFA
 
-float FXSYS_wcstof(const wchar_t* pwsStr, int32_t iLength, int32_t* pUsedLen);
+float FXSYS_wcstof(const wchar_t* pwsStr, size_t nLength, size_t* pUsedLen);
 wchar_t* FXSYS_wcsncpy(wchar_t* dstStr, const wchar_t* srcStr, size_t count);
 int32_t FXSYS_wcsnicmp(const wchar_t* s1, const wchar_t* s2, size_t count);
 
@@ -48,8 +44,16 @@
   return u_toupper(c);
 }
 
+inline bool FXSYS_IsLowerASCII(int32_t c) {
+  return c >= 'a' && c <= 'z';
+}
+
+inline bool FXSYS_IsUpperASCII(int32_t c) {
+  return c >= 'A' && c <= 'Z';
+}
+
 inline char FXSYS_ToUpperASCII(char c) {
-  return (c >= 'a' && c <= 'z') ? (c + ('A' - 'a')) : c;
+  return FXSYS_IsLowerASCII(c) ? (c + ('A' - 'a')) : c;
 }
 
 inline bool FXSYS_iswalpha(wchar_t c) {
@@ -69,11 +73,11 @@
 }
 
 inline bool FXSYS_IsHexDigit(char c) {
-  return !((c & 0x80) || !std::isxdigit(c));
+  return !((c & 0x80) || !isxdigit(c));
 }
 
 inline bool FXSYS_IsWideHexDigit(wchar_t c) {
-  return !((c & 0xFFFFFF80) || !std::isxdigit(c));
+  return !((c & 0xFFFFFF80) || !isxdigit(c));
 }
 
 inline int FXSYS_HexCharToInt(char c) {
@@ -86,16 +90,16 @@
 inline int FXSYS_WideHexCharToInt(wchar_t c) {
   if (!FXSYS_IsWideHexDigit(c))
     return 0;
-  char upchar = std::toupper(static_cast<char>(c));
+  char upchar = toupper(static_cast<char>(c));
   return upchar > '9' ? upchar - 'A' + 10 : upchar - '0';
 }
 
 inline bool FXSYS_IsDecimalDigit(char c) {
-  return !((c & 0x80) || !std::isdigit(c));
+  return !((c & 0x80) || !isdigit(c));
 }
 
 inline bool FXSYS_IsDecimalDigit(wchar_t c) {
-  return !((c & 0xFFFFFF80) || !std::iswdigit(c));
+  return !((c & 0xFFFFFF80) || !iswdigit(c));
 }
 
 inline int FXSYS_DecimalCharToInt(char c) {
@@ -112,18 +116,19 @@
 size_t FXSYS_ToUTF16BE(uint32_t unicode, char* buf);
 
 // Strict order over floating types where NaNs may be present.
+// All NaNs are treated as equal to each other and greater than infinity.
 template <typename T>
 bool FXSYS_SafeEQ(const T& lhs, const T& rhs) {
-  return (std::isnan(lhs) && std::isnan(rhs)) ||
-         (!std::isnan(lhs) && !std::isnan(rhs) && lhs == rhs);
+  return (isnan(lhs) && isnan(rhs)) ||
+         (!isnan(lhs) && !isnan(rhs) && lhs == rhs);
 }
 
 template <typename T>
 bool FXSYS_SafeLT(const T& lhs, const T& rhs) {
-  if (std::isnan(lhs) && std::isnan(rhs))
+  if (isnan(lhs) && isnan(rhs))
     return false;
-  if (std::isnan(lhs) || std::isnan(rhs))
-    return std::isnan(lhs) < std::isnan(rhs);
+  if (isnan(lhs) || isnan(rhs))
+    return isnan(lhs) < isnan(rhs);
   return lhs < rhs;
 }
 
diff --git a/core/fxcrt/fx_extension_unittest.cpp b/core/fxcrt/fx_extension_unittest.cpp
index 81fc4f7..1ca94a7 100644
--- a/core/fxcrt/fx_extension_unittest.cpp
+++ b/core/fxcrt/fx_extension_unittest.cpp
@@ -1,13 +1,58 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxcrt/fx_extension.h"
 
+#include <math.h>
+
+#include <iterator>
 #include <limits>
 
 #include "testing/gtest/include/gtest/gtest.h"
 
+TEST(fxcrt, FXSYS_IsLowerASCII) {
+  EXPECT_TRUE(FXSYS_IsLowerASCII('a'));
+  EXPECT_TRUE(FXSYS_IsLowerASCII(L'a'));
+  EXPECT_TRUE(FXSYS_IsLowerASCII('b'));
+  EXPECT_TRUE(FXSYS_IsLowerASCII(L'b'));
+  EXPECT_TRUE(FXSYS_IsLowerASCII('y'));
+  EXPECT_TRUE(FXSYS_IsLowerASCII(L'y'));
+  EXPECT_TRUE(FXSYS_IsLowerASCII('z'));
+  EXPECT_TRUE(FXSYS_IsLowerASCII(L'z'));
+  EXPECT_FALSE(FXSYS_IsLowerASCII('`'));
+  EXPECT_FALSE(FXSYS_IsLowerASCII(L'`'));
+  EXPECT_FALSE(FXSYS_IsLowerASCII('{'));
+  EXPECT_FALSE(FXSYS_IsLowerASCII(L'{'));
+  EXPECT_FALSE(FXSYS_IsLowerASCII('Z'));
+  EXPECT_FALSE(FXSYS_IsLowerASCII(L'Z'));
+  EXPECT_FALSE(FXSYS_IsLowerASCII('7'));
+  EXPECT_FALSE(FXSYS_IsLowerASCII(L'7'));
+  EXPECT_FALSE(FXSYS_IsLowerASCII(static_cast<char>(-78)));
+  EXPECT_FALSE(FXSYS_IsLowerASCII(static_cast<wchar_t>(0xb2)));
+}
+
+TEST(fxcrt, FXSYS_IsUpperASCII) {
+  EXPECT_TRUE(FXSYS_IsUpperASCII('A'));
+  EXPECT_TRUE(FXSYS_IsUpperASCII(L'A'));
+  EXPECT_TRUE(FXSYS_IsUpperASCII('B'));
+  EXPECT_TRUE(FXSYS_IsUpperASCII(L'B'));
+  EXPECT_TRUE(FXSYS_IsUpperASCII('Y'));
+  EXPECT_TRUE(FXSYS_IsUpperASCII(L'Y'));
+  EXPECT_TRUE(FXSYS_IsUpperASCII('Z'));
+  EXPECT_TRUE(FXSYS_IsUpperASCII(L'Z'));
+  EXPECT_FALSE(FXSYS_IsUpperASCII('@'));
+  EXPECT_FALSE(FXSYS_IsUpperASCII(L'@'));
+  EXPECT_FALSE(FXSYS_IsUpperASCII('['));
+  EXPECT_FALSE(FXSYS_IsUpperASCII(L'['));
+  EXPECT_FALSE(FXSYS_IsUpperASCII('z'));
+  EXPECT_FALSE(FXSYS_IsUpperASCII(L'z'));
+  EXPECT_FALSE(FXSYS_IsUpperASCII('7'));
+  EXPECT_FALSE(FXSYS_IsUpperASCII(L'7'));
+  EXPECT_FALSE(FXSYS_IsUpperASCII(static_cast<char>(-78)));
+  EXPECT_FALSE(FXSYS_IsUpperASCII(static_cast<wchar_t>(0xb2)));
+}
+
 TEST(fxcrt, FXSYS_HexCharToInt) {
   EXPECT_EQ(10, FXSYS_HexCharToInt('a'));
   EXPECT_EQ(10, FXSYS_HexCharToInt('A'));
@@ -84,40 +129,40 @@
 }
 
 TEST(fxcrt, FXSYS_wcstof) {
-  int32_t used_len = 0;
+  size_t used_len = 0;
   EXPECT_FLOAT_EQ(-12.0f, FXSYS_wcstof(L"-12", 3, &used_len));
-  EXPECT_EQ(3, used_len);
+  EXPECT_EQ(3u, used_len);
 
   used_len = 0;
   EXPECT_FLOAT_EQ(1.5362f, FXSYS_wcstof(L"1.5362", 6, &used_len));
-  EXPECT_EQ(6, used_len);
+  EXPECT_EQ(6u, used_len);
 
   used_len = 0;
   EXPECT_FLOAT_EQ(0.875f, FXSYS_wcstof(L"0.875", 5, &used_len));
-  EXPECT_EQ(5, used_len);
+  EXPECT_EQ(5u, used_len);
 
   used_len = 0;
   EXPECT_FLOAT_EQ(5.56e-2f, FXSYS_wcstof(L"5.56e-2", 7, &used_len));
-  EXPECT_EQ(7, used_len);
+  EXPECT_EQ(7u, used_len);
 
   used_len = 0;
   EXPECT_FLOAT_EQ(1.234e10f, FXSYS_wcstof(L"1.234E10", 8, &used_len));
-  EXPECT_EQ(8, used_len);
+  EXPECT_EQ(8u, used_len);
 
   used_len = 0;
   EXPECT_FLOAT_EQ(0.0f, FXSYS_wcstof(L"1.234E100000000000000", 21, &used_len));
-  EXPECT_EQ(0, used_len);
+  EXPECT_EQ(0u, used_len);
 
   used_len = 0;
   EXPECT_FLOAT_EQ(0.0f, FXSYS_wcstof(L"1.234E-128", 21, &used_len));
-  EXPECT_EQ(0, used_len);
+  EXPECT_EQ(0u, used_len);
 
   // TODO(dsinclair): This should round as per IEEE 64-bit values.
   // EXPECT_EQ(L"123456789.01234567", FXSYS_wcstof(L"123456789.012345678"));
   used_len = 0;
   EXPECT_FLOAT_EQ(123456789.012345678f,
                   FXSYS_wcstof(L"123456789.012345678", 19, &used_len));
-  EXPECT_EQ(19, used_len);
+  EXPECT_EQ(19u, used_len);
 
   // TODO(dsinclair): This is spec'd as rounding when > 16 significant digits
   // prior to the exponent.
@@ -125,25 +170,25 @@
   used_len = 0;
   EXPECT_FLOAT_EQ(99999999999999999.0f,
                   FXSYS_wcstof(L"99999999999999999", 17, &used_len));
-  EXPECT_EQ(17, used_len);
+  EXPECT_EQ(17u, used_len);
 
   // For https://crbug.com/pdfium/1217
   EXPECT_FLOAT_EQ(0.0f, FXSYS_wcstof(L"e76", 3, nullptr));
 
   // Overflow to infinity.
   used_len = 0;
-  EXPECT_TRUE(std::isinf(FXSYS_wcstof(
+  EXPECT_TRUE(isinf(FXSYS_wcstof(
       L"88888888888888888888888888888888888888888888888888888888888888888888888"
       L"88888888888888888888888888888888888888888888888888888888888",
       130, &used_len)));
-  EXPECT_EQ(130, used_len);
+  EXPECT_EQ(130u, used_len);
 
   used_len = 0;
-  EXPECT_TRUE(std::isinf(FXSYS_wcstof(
+  EXPECT_TRUE(isinf(FXSYS_wcstof(
       L"-8888888888888888888888888888888888888888888888888888888888888888888888"
       L"888888888888888888888888888888888888888888888888888888888888",
       131, &used_len)));
-  EXPECT_EQ(131, used_len);
+  EXPECT_EQ(131u, used_len);
 }
 
 TEST(fxcrt, FXSYS_SafeOps) {
@@ -153,8 +198,8 @@
   const float fNan = std::numeric_limits<float>::quiet_NaN();
   const float ascending[] = {fMin, 1.0f, 2.0f, fMax, fInf, fNan};
 
-  for (size_t i = 0; i < FX_ArraySize(ascending); ++i) {
-    for (size_t j = 0; j < FX_ArraySize(ascending); ++j) {
+  for (size_t i = 0; i < std::size(ascending); ++i) {
+    for (size_t j = 0; j < std::size(ascending); ++j) {
       if (i == j) {
         EXPECT_TRUE(FXSYS_SafeEQ(ascending[i], ascending[j]))
             << " at " << i << " " << j;
diff --git a/core/fxcrt/fx_folder.h b/core/fxcrt/fx_folder.h
new file mode 100644
index 0000000..46ace07
--- /dev/null
+++ b/core/fxcrt/fx_folder.h
@@ -0,0 +1,24 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCRT_FX_FOLDER_H_
+#define CORE_FXCRT_FX_FOLDER_H_
+
+#include <memory>
+
+#include "core/fxcrt/bytestring.h"
+
+class FX_Folder {
+ public:
+  static std::unique_ptr<FX_Folder> OpenFolder(const ByteString& path);
+
+  virtual ~FX_Folder() = default;
+
+  // `filename` and `folder` are required out-parameters.
+  virtual bool GetNextFile(ByteString* filename, bool* bFolder) = 0;
+};
+
+#endif  // CORE_FXCRT_FX_FOLDER_H_
diff --git a/core/fxcrt/fx_folder_posix.cpp b/core/fxcrt/fx_folder_posix.cpp
new file mode 100644
index 0000000..fb873be
--- /dev/null
+++ b/core/fxcrt/fx_folder_posix.cpp
@@ -0,0 +1,66 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/fx_folder.h"
+
+#include <memory>
+
+#include "build/build_config.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/memory/ptr_util.h"
+
+#if BUILDFLAG(IS_WIN)
+#error "built on wrong platform"
+#endif
+
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+class FX_PosixFolder : public FX_Folder {
+ public:
+  ~FX_PosixFolder() override;
+
+  bool GetNextFile(ByteString* filename, bool* bFolder) override;
+
+ private:
+  friend class FX_Folder;
+  FX_PosixFolder(const ByteString& path, DIR* dir);
+
+  const ByteString m_Path;
+  UnownedPtr<DIR> m_Dir;
+};
+
+std::unique_ptr<FX_Folder> FX_Folder::OpenFolder(const ByteString& path) {
+  DIR* dir = opendir(path.c_str());
+  if (!dir)
+    return nullptr;
+
+  // Private ctor.
+  return pdfium::WrapUnique(new FX_PosixFolder(path, dir));
+}
+
+FX_PosixFolder::FX_PosixFolder(const ByteString& path, DIR* dir)
+    : m_Path(path), m_Dir(dir) {}
+
+FX_PosixFolder::~FX_PosixFolder() {
+  closedir(m_Dir.ExtractAsDangling());
+}
+
+bool FX_PosixFolder::GetNextFile(ByteString* filename, bool* bFolder) {
+  struct dirent* de = readdir(m_Dir);
+  if (!de)
+    return false;
+
+  ByteString fullpath = m_Path + "/" + de->d_name;
+  struct stat deStat;
+  if (stat(fullpath.c_str(), &deStat) < 0)
+    return false;
+
+  *filename = de->d_name;
+  *bFolder = S_ISDIR(deStat.st_mode);
+  return true;
+}
diff --git a/core/fxcrt/fx_folder_windows.cpp b/core/fxcrt/fx_folder_windows.cpp
new file mode 100644
index 0000000..6b19dde
--- /dev/null
+++ b/core/fxcrt/fx_folder_windows.cpp
@@ -0,0 +1,63 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/fx_folder.h"
+
+#include <memory>
+
+#include "build/build_config.h"
+#include "third_party/base/memory/ptr_util.h"
+
+#if !BUILDFLAG(IS_WIN)
+#error "built on wrong platform"
+#endif
+
+#include <direct.h>
+
+class FX_WindowsFolder : public FX_Folder {
+ public:
+  ~FX_WindowsFolder() override;
+  bool GetNextFile(ByteString* filename, bool* bFolder) override;
+
+ private:
+  friend class FX_Folder;
+  FX_WindowsFolder();
+
+  HANDLE m_Handle = INVALID_HANDLE_VALUE;
+  bool m_bReachedEnd = false;
+  WIN32_FIND_DATAA m_FindData;
+};
+
+std::unique_ptr<FX_Folder> FX_Folder::OpenFolder(const ByteString& path) {
+  // Private ctor.
+  auto handle = pdfium::WrapUnique(new FX_WindowsFolder());
+  ByteString search_path = path + "/*.*";
+  handle->m_Handle =
+      FindFirstFileExA(search_path.c_str(), FindExInfoStandard,
+                       &handle->m_FindData, FindExSearchNameMatch, nullptr, 0);
+  if (handle->m_Handle == INVALID_HANDLE_VALUE)
+    return nullptr;
+
+  return handle;
+}
+
+FX_WindowsFolder::FX_WindowsFolder() = default;
+
+FX_WindowsFolder::~FX_WindowsFolder() {
+  if (m_Handle != INVALID_HANDLE_VALUE)
+    FindClose(m_Handle);
+}
+
+bool FX_WindowsFolder::GetNextFile(ByteString* filename, bool* bFolder) {
+  if (m_bReachedEnd)
+    return false;
+
+  *filename = m_FindData.cFileName;
+  *bFolder = !!(m_FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
+  if (!FindNextFileA(m_Handle, &m_FindData))
+    m_bReachedEnd = true;
+  return true;
+}
diff --git a/core/fxcrt/fx_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.cpp b/core/fxcrt/fx_memory.cpp
index 1ed4949..7b6aa34 100644
--- a/core/fxcrt/fx_memory.cpp
+++ b/core/fxcrt/fx_memory.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,130 +8,96 @@
 
 #include <stdlib.h>  // For abort().
 
+#include <iterator>
 #include <limits>
 
 #include "build/build_config.h"
-#include "core/fxcrt/fx_safe_types.h"
 #include "third_party/base/debug/alias.h"
 
-pdfium::base::PartitionAllocatorGeneric& GetArrayBufferPartitionAllocator() {
-  static pdfium::base::PartitionAllocatorGeneric s_array_buffer_allocator;
-  return s_array_buffer_allocator;
-}
-
-pdfium::base::PartitionAllocatorGeneric& GetGeneralPartitionAllocator() {
-  static pdfium::base::PartitionAllocatorGeneric s_general_allocator;
-  return s_general_allocator;
-}
-
-pdfium::base::PartitionAllocatorGeneric& GetStringPartitionAllocator() {
-  static pdfium::base::PartitionAllocatorGeneric s_string_allocator;
-  return s_string_allocator;
-}
-
-void FXMEM_InitializePartitionAlloc() {
-  static bool s_partition_allocators_initialized = false;
-  if (!s_partition_allocators_initialized) {
-    pdfium::base::PartitionAllocGlobalInit(FX_OutOfMemoryTerminate);
-    GetArrayBufferPartitionAllocator().init();
-    GetGeneralPartitionAllocator().init();
-    GetStringPartitionAllocator().init();
-    s_partition_allocators_initialized = true;
-  }
-}
+#if BUILDFLAG(IS_WIN)
+#include <windows.h>
+#endif
 
 void* FXMEM_DefaultAlloc(size_t byte_size) {
-  return pdfium::base::PartitionAllocGenericFlags(
-      GetGeneralPartitionAllocator().root(),
-      pdfium::base::PartitionAllocReturnNull, byte_size, "GeneralPartition");
+  return pdfium::internal::Alloc(byte_size, 1);
 }
 
 void* FXMEM_DefaultCalloc(size_t num_elems, size_t byte_size) {
-  return FX_SafeAlloc(num_elems, byte_size);
+  return pdfium::internal::Calloc(num_elems, byte_size);
 }
 
 void* FXMEM_DefaultRealloc(void* pointer, size_t new_size) {
-  return pdfium::base::PartitionReallocGenericFlags(
-      GetGeneralPartitionAllocator().root(),
-      pdfium::base::PartitionAllocReturnNull, pointer, new_size,
-      "GeneralPartition");
+  return pdfium::internal::Realloc(pointer, new_size, 1);
 }
 
 void FXMEM_DefaultFree(void* pointer) {
-  pdfium::base::PartitionFree(pointer);
+  FX_Free(pointer);
 }
 
-NOINLINE void FX_OutOfMemoryTerminate() {
+NOINLINE void FX_OutOfMemoryTerminate(size_t size) {
   // Convince the linker this should not be folded with similar functions using
   // Identical Code Folding.
   static int make_this_function_aliased = 0xbd;
   pdfium::base::debug::Alias(&make_this_function_aliased);
 
-  // Termimate cleanly if we can, else crash at a specific address (0xbd).
-  abort();
-#if !defined(OS_WIN)
-  reinterpret_cast<void (*)()>(0xbd)();
+#if BUILDFLAG(IS_WIN)
+  // The same custom Windows exception code used in Chromium and Breakpad.
+  constexpr DWORD kOomExceptionCode = 0xe0000008;
+  ULONG_PTR exception_args[] = {size};
+  ::RaiseException(kOomExceptionCode, EXCEPTION_NONCONTINUABLE,
+                   std::size(exception_args), exception_args);
 #endif
+
+  // Terminate cleanly.
+  abort();
 }
 
-void* FX_SafeAlloc(size_t num_members, size_t member_size) {
-  FX_SAFE_SIZE_T total = member_size;
-  total *= num_members;
-  if (!total.IsValid())
-    return nullptr;
+namespace pdfium {
+namespace internal {
 
-  constexpr int kFlags = pdfium::base::PartitionAllocReturnNull |
-                         pdfium::base::PartitionAllocZeroFill;
-  return pdfium::base::PartitionAllocGenericFlags(
-      GetGeneralPartitionAllocator().root(), kFlags, total.ValueOrDie(),
-      "GeneralPartition");
-}
-
-void* FX_SafeRealloc(void* ptr, size_t num_members, size_t member_size) {
-  FX_SAFE_SIZE_T size = num_members;
-  size *= member_size;
-  if (!size.IsValid())
-    return nullptr;
-
-  return pdfium::base::PartitionReallocGenericFlags(
-      GetGeneralPartitionAllocator().root(),
-      pdfium::base::PartitionAllocReturnNull, ptr, size.ValueOrDie(),
-      "GeneralPartition");
-}
-
-void* FX_AllocOrDie(size_t num_members, size_t member_size) {
-  // TODO(tsepez): See if we can avoid the implicit memset(0).
-  void* result = FX_SafeAlloc(num_members, member_size);
+void* AllocOrDie(size_t num_members, size_t member_size) {
+  void* result = Alloc(num_members, member_size);
   if (!result)
-    FX_OutOfMemoryTerminate();  // Never returns.
+    FX_OutOfMemoryTerminate(0);  // Never returns.
 
   return result;
 }
 
-void* FX_AllocOrDie2D(size_t w, size_t h, size_t member_size) {
+void* AllocOrDie2D(size_t w, size_t h, size_t member_size) {
   if (w >= std::numeric_limits<size_t>::max() / h)
-    FX_OutOfMemoryTerminate();  // Never returns.
+    FX_OutOfMemoryTerminate(0);  // Never returns.
 
-  return FX_AllocOrDie(w * h, member_size);
+  return AllocOrDie(w * h, member_size);
 }
-
-void* FX_ReallocOrDie(void* ptr, size_t num_members, size_t member_size) {
-  void* result = FX_SafeRealloc(ptr, num_members, member_size);
+void* CallocOrDie(size_t num_members, size_t member_size) {
+  void* result = Calloc(num_members, member_size);
   if (!result)
-    FX_OutOfMemoryTerminate();  // Never returns.
+    FX_OutOfMemoryTerminate(0);  // Never returns.
 
   return result;
 }
 
-void FX_Free(void* ptr) {
-  // TODO(palmer): Removing this check exposes crashes when PDFium callers
-  // attempt to free |nullptr|. Although libc's |free| allows freeing |NULL|, no
-  // other Partition Alloc callers need this tolerant behavior. Additionally,
-  // checking for |nullptr| adds a branch to |PartitionFree|, and it's nice to
-  // not have to have that.
-  //
-  // So this check is hiding (what I consider to be) bugs, and we should try to
-  // fix them. https://bugs.chromium.org/p/pdfium/issues/detail?id=690
-  if (ptr)
-    pdfium::base::PartitionFree(ptr);
+void* CallocOrDie2D(size_t w, size_t h, size_t member_size) {
+  if (w >= std::numeric_limits<size_t>::max() / h)
+    FX_OutOfMemoryTerminate(0);  // Never returns.
+
+  return CallocOrDie(w * h, member_size);
 }
+
+void* ReallocOrDie(void* ptr, size_t num_members, size_t member_size) {
+  void* result = Realloc(ptr, num_members, member_size);
+  if (!result)
+    FX_OutOfMemoryTerminate(0);  // Never returns.
+
+  return result;
+}
+
+void* StringAllocOrDie(size_t num_members, size_t member_size) {
+  void* result = StringAlloc(num_members, member_size);
+  if (!result)
+    FX_OutOfMemoryTerminate(0);  // Never returns.
+
+  return result;
+}
+}  // namespace internal
+}  // namespace pdfium
diff --git a/core/fxcrt/fx_memory.h b/core/fxcrt/fx_memory.h
index 6cb5f2e..ac61146 100644
--- a/core/fxcrt/fx_memory.h
+++ b/core/fxcrt/fx_memory.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,50 +22,89 @@
 #ifdef __cplusplus
 }  // extern "C"
 
-#include "third_party/base/allocator/partition_allocator/partition_alloc.h"
+#include "third_party/base/compiler_specific.h"
 
-pdfium::base::PartitionAllocatorGeneric& GetArrayBufferPartitionAllocator();
-pdfium::base::PartitionAllocatorGeneric& GetGeneralPartitionAllocator();
-pdfium::base::PartitionAllocatorGeneric& GetStringPartitionAllocator();
+void FX_InitializeMemoryAllocators();
+NOINLINE void FX_OutOfMemoryTerminate(size_t size);
 
-void FXMEM_InitializePartitionAlloc();
-NOINLINE void FX_OutOfMemoryTerminate();
+// General Partition Allocators.
 
 // These never return nullptr, and must return cleared memory.
 #define FX_Alloc(type, size) \
-  static_cast<type*>(FX_AllocOrDie(size, sizeof(type)))
+  static_cast<type*>(pdfium::internal::CallocOrDie(size, sizeof(type)))
 #define FX_Alloc2D(type, w, h) \
-  static_cast<type*>(FX_AllocOrDie2D(w, h, sizeof(type)))
+  static_cast<type*>(pdfium::internal::CallocOrDie2D(w, h, sizeof(type)))
 #define FX_Realloc(type, ptr, size) \
-  static_cast<type*>(FX_ReallocOrDie(ptr, size, sizeof(type)))
+  static_cast<type*>(pdfium::internal::ReallocOrDie(ptr, size, sizeof(type)))
 
 // May return nullptr, but returns cleared memory otherwise.
 #define FX_TryAlloc(type, size) \
-  static_cast<type*>(FX_SafeAlloc(size, sizeof(type)))
+  static_cast<type*>(pdfium::internal::Calloc(size, sizeof(type)))
 #define FX_TryRealloc(type, ptr, size) \
-  static_cast<type*>(FX_SafeRealloc(ptr, size, sizeof(type)))
+  static_cast<type*>(pdfium::internal::Realloc(ptr, size, sizeof(type)))
 
-void* FX_SafeAlloc(size_t num_members, size_t member_size);
-void* FX_SafeRealloc(void* ptr, size_t num_members, size_t member_size);
-void* FX_AllocOrDie(size_t num_members, size_t member_size);
-void* FX_AllocOrDie2D(size_t w, size_t h, size_t member_size);
-void* FX_ReallocOrDie(void* ptr, size_t num_members, size_t member_size);
-void FX_Free(void* ptr);
+// These never return nullptr, but return uninitialized memory.
+// TODO(thestig): Add FX_TryAllocUninit() if there is a use case.
+#define FX_AllocUninit(type, size) \
+  static_cast<type*>(pdfium::internal::AllocOrDie(size, sizeof(type)))
+#define FX_AllocUninit2D(type, w, h) \
+  static_cast<type*>(pdfium::internal::AllocOrDie2D(w, h, sizeof(type)))
 
-// The FX_ArraySize(arr) macro returns the # of elements in an array arr.
-// The expression is a compile-time constant, and therefore can be
-// used in defining new arrays, for example.  If you use FX_ArraySize on
-// a pointer by mistake, you will get a compile-time error.
-//
-// One caveat is that FX_ArraySize() doesn't accept any array of an
-// anonymous type or a type defined inside a function.
-#define FX_ArraySize(array) (sizeof(ArraySizeHelper(array)))
+// FX_Free frees memory from the above.
+#define FX_Free(ptr) pdfium::internal::Dealloc(ptr)
 
-// This template function declaration is used in defining FX_ArraySize.
-// Note that the function doesn't need an implementation, as we only
-// use its type.
-template <typename T, size_t N>
-char (&ArraySizeHelper(T (&array)[N]))[N];
+// 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_StringFree frees memory from FX_StringAlloc.
+#define FX_StringFree(ptr) pdfium::internal::StringDealloc(ptr)
+
+#ifndef V8_ENABLE_SANDBOX
+// V8 Array Buffer Partition Allocators.
+
+// This never returns nullptr, and returns zeroed memory.
+void* FX_ArrayBufferAllocate(size_t length);
+
+// This never returns nullptr, but returns uninitialized memory.
+void* FX_ArrayBufferAllocateUninitialized(size_t length);
+
+// FX_ArrayBufferFree accepts memory from both of the above.
+void FX_ArrayBufferFree(void* data);
+#endif  // V8_ENABLE_SANDBOX
+
+namespace pdfium {
+namespace internal {
+
+// General partition.
+void* Alloc(size_t num_members, size_t member_size);
+void* AllocOrDie(size_t num_members, size_t member_size);
+void* AllocOrDie2D(size_t w, size_t h, size_t member_size);
+void* Calloc(size_t num_members, size_t member_size);
+void* Realloc(void* ptr, size_t num_members, size_t member_size);
+void* CallocOrDie(size_t num_members, size_t member_size);
+void* CallocOrDie2D(size_t w, size_t h, size_t member_size);
+void* ReallocOrDie(void* ptr, size_t num_members, size_t member_size);
+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
+
+// Force stack allocation of a class. Classes that do complex work in a
+// destructor, such as the flushing of buffers, should be declared as
+// stack-allocated as possible, since future memory allocation schemes
+// may not run destructors in a predictable manner if an instance is
+// heap-allocated.
+#define FX_STACK_ALLOCATED()           \
+  void* operator new(size_t) = delete; \
+  void* operator new(size_t, void*) = delete
 
 // Round up to the power-of-two boundary N.
 template <int N, typename T>
diff --git a/core/fxcrt/fx_memory_malloc.cpp b/core/fxcrt/fx_memory_malloc.cpp
new file mode 100644
index 0000000..e199d42
--- /dev/null
+++ b/core/fxcrt/fx_memory_malloc.cpp
@@ -0,0 +1,87 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/fx_memory.h"
+
+#include <stdlib.h>
+
+#include <limits>
+
+#include "build/build_config.h"
+#include "core/fxcrt/fx_safe_types.h"
+
+#if defined(PDF_USE_PARTITION_ALLOC)
+#error "File compiled under wrong build option."
+#endif
+
+namespace pdfium {
+namespace internal {
+
+// Slightly less than 2GB, typically.
+constexpr size_t kMallocSizeLimit = std::numeric_limits<int>::max() - (1 << 12);
+
+void* Alloc(size_t num_members, size_t member_size) {
+  FX_SAFE_SIZE_T total = member_size;
+  total *= num_members;
+  if (!total.IsValid() || total.ValueOrDie() >= kMallocSizeLimit)
+    return nullptr;
+  return malloc(total.ValueOrDie());
+}
+
+void* Calloc(size_t num_members, size_t member_size) {
+  FX_SAFE_SIZE_T total = member_size;
+  total *= num_members;
+  if (!total.IsValid() || total.ValueOrDie() >= kMallocSizeLimit)
+    return nullptr;
+  return calloc(num_members, member_size);
+}
+
+void* Realloc(void* ptr, size_t num_members, size_t member_size) {
+  FX_SAFE_SIZE_T total = num_members;
+  total *= member_size;
+  if (!total.IsValid() || total.ValueOrDie() >= kMallocSizeLimit)
+    return nullptr;
+  return realloc(ptr, total.ValueOrDie());
+}
+
+void 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;
+  if (!total.IsValid())
+    return nullptr;
+  return malloc(total.ValueOrDie());
+}
+
+void StringDealloc(void* ptr) {
+  free(ptr);
+}
+
+}  // namespace internal
+}  // namespace pdfium
+
+void FX_InitializeMemoryAllocators() {}
+
+void* FX_ArrayBufferAllocate(size_t length) {
+  void* result = calloc(length, 1);
+  if (!result)
+    FX_OutOfMemoryTerminate(length);
+  return result;
+}
+
+void* FX_ArrayBufferAllocateUninitialized(size_t length) {
+  void* result = malloc(length);
+  if (!result)
+    FX_OutOfMemoryTerminate(length);
+  return result;
+}
+
+void FX_ArrayBufferFree(void* data) {
+  free(data);
+}
diff --git a/core/fxcrt/fx_memory_pa.cpp b/core/fxcrt/fx_memory_pa.cpp
new file mode 100644
index 0000000..789f68c
--- /dev/null
+++ b/core/fxcrt/fx_memory_pa.cpp
@@ -0,0 +1,151 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/fx_memory.h"
+
+#include "base/allocator/partition_allocator/partition_alloc.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/no_destructor.h"
+
+#if !defined(PDF_USE_PARTITION_ALLOC)
+#error "File compiled under wrong build option."
+#endif
+
+namespace {
+
+constexpr partition_alloc::PartitionOptions kOptions = {};
+
+#ifndef V8_ENABLE_SANDBOX
+partition_alloc::PartitionAllocator& GetArrayBufferPartitionAllocator() {
+  static pdfium::base::NoDestructor<partition_alloc::PartitionAllocator>
+      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(kOptions);
+  return *s_general_allocator;
+}
+
+partition_alloc::PartitionAllocator& GetStringPartitionAllocator() {
+  static pdfium::base::NoDestructor<partition_alloc::PartitionAllocator>
+      s_string_allocator(kOptions);
+  return *s_string_allocator;
+}
+
+}  // namespace
+
+namespace pdfium {
+namespace internal {
+
+void* Alloc(size_t num_members, size_t member_size) {
+  FX_SAFE_SIZE_T total = member_size;
+  total *= num_members;
+  if (!total.IsValid())
+    return nullptr;
+
+  return GetGeneralPartitionAllocator().root()->AllocWithFlags(
+      partition_alloc::AllocFlags::kReturnNull, total.ValueOrDie(),
+      "GeneralPartition");
+}
+
+void* Calloc(size_t num_members, size_t member_size) {
+  FX_SAFE_SIZE_T total = member_size;
+  total *= num_members;
+  if (!total.IsValid())
+    return nullptr;
+
+  return GetGeneralPartitionAllocator().root()->AllocWithFlags(
+      partition_alloc::AllocFlags::kReturnNull |
+          partition_alloc::AllocFlags::kZeroFill,
+      total.ValueOrDie(), "GeneralPartition");
+}
+
+void* Realloc(void* ptr, size_t num_members, size_t member_size) {
+  FX_SAFE_SIZE_T size = num_members;
+  size *= member_size;
+  if (!size.IsValid())
+    return nullptr;
+
+  return GetGeneralPartitionAllocator().root()->ReallocWithFlags(
+      partition_alloc::AllocFlags::kReturnNull, ptr, size.ValueOrDie(),
+      "GeneralPartition");
+}
+
+void 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;
+  if (!total.IsValid())
+    return nullptr;
+
+  return GetStringPartitionAllocator().root()->AllocWithFlags(
+      partition_alloc::AllocFlags::kReturnNull, total.ValueOrDie(),
+      "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
+
+void FX_InitializeMemoryAllocators() {
+  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();
+#endif  // V8_ENABLE_SANDBOX
+    GetGeneralPartitionAllocator();
+    GetStringPartitionAllocator();
+    s_partition_allocators_initialized = true;
+  }
+}
+
+#ifndef V8_ENABLE_SANDBOX
+void* FX_ArrayBufferAllocate(size_t length) {
+  return GetArrayBufferPartitionAllocator().root()->AllocWithFlags(
+      partition_alloc::AllocFlags::kZeroFill, length, "FXArrayBuffer");
+}
+
+void* FX_ArrayBufferAllocateUninitialized(size_t length) {
+  return GetArrayBufferPartitionAllocator().root()->Alloc(length,
+                                                          "FXArrayBuffer");
+}
+
+void FX_ArrayBufferFree(void* data) {
+  GetArrayBufferPartitionAllocator().root()->Free(data);
+}
+#endif  // V8_ENABLE_SANDBOX
diff --git a/core/fxcrt/fx_memory_unittest.cpp b/core/fxcrt/fx_memory_unittest.cpp
index b3fd299..7d3fe4d 100644
--- a/core/fxcrt/fx_memory_unittest.cpp
+++ b/core/fxcrt/fx_memory_unittest.cpp
@@ -1,21 +1,28 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/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 {
 
-const size_t kMaxByteAlloc = std::numeric_limits<size_t>::max();
-const size_t kMaxIntAlloc = kMaxByteAlloc / sizeof(int);
-const size_t kOverflowIntAlloc = kMaxIntAlloc + 100;
-const size_t kWidth = 640;
-const size_t kOverflowIntAlloc2D = kMaxIntAlloc / kWidth + 10;
+constexpr size_t kMaxByteAlloc = std::numeric_limits<size_t>::max();
+constexpr size_t kMaxIntAlloc = kMaxByteAlloc / sizeof(int);
+constexpr size_t kOverflowIntAlloc = kMaxIntAlloc + 100;
+constexpr size_t kWidth = 640;
+constexpr size_t kOverflowIntAlloc2D = kMaxIntAlloc / kWidth + 10;
+constexpr size_t kCloseToMaxIntAlloc = kMaxIntAlloc - 100;
+constexpr size_t kCloseToMaxByteAlloc = kMaxByteAlloc - 100;
 
 }  // namespace
 
@@ -28,14 +35,13 @@
   FX_Free(ptr);
 }
 
-// TODO(tsepez): re-enable OOM tests if we can find a way to
-// prevent it from hosing the bots.
-TEST(fxcrt, DISABLED_FX_AllocOOM) {
-  EXPECT_DEATH_IF_SUPPORTED((void)FX_Alloc(int, kMaxIntAlloc), "");
+TEST(fxcrt, FXAllocOOM) {
+  EXPECT_DEATH_IF_SUPPORTED((void)FX_Alloc(int, kCloseToMaxIntAlloc), "");
 
   int* ptr = FX_Alloc(int, 1);
   EXPECT_TRUE(ptr);
-  EXPECT_DEATH_IF_SUPPORTED((void)FX_Realloc(int, ptr, kMaxIntAlloc), "");
+  EXPECT_DEATH_IF_SUPPORTED((void)FX_Realloc(int, ptr, kCloseToMaxIntAlloc),
+                            "");
   FX_Free(ptr);
 }
 
@@ -60,12 +66,12 @@
       << ptr;
 }
 
-TEST(fxcrt, DISABLED_FX_TryAllocOOM) {
-  EXPECT_FALSE(FX_TryAlloc(int, kMaxIntAlloc));
+TEST(fxcrt, FXTryAllocOOM) {
+  EXPECT_FALSE(FX_TryAlloc(int, kCloseToMaxIntAlloc));
 
   int* ptr = FX_Alloc(int, 1);
   EXPECT_TRUE(ptr);
-  EXPECT_FALSE(FX_TryRealloc(int, ptr, kMaxIntAlloc));
+  EXPECT_FALSE(FX_TryRealloc(int, ptr, kCloseToMaxIntAlloc));
   FX_Free(ptr);
 }
 
@@ -85,15 +91,23 @@
 }
 #endif
 
-TEST(fxcrt, DISABLED_FXMEM_DefaultOOM) {
-  EXPECT_FALSE(FXMEM_DefaultAlloc(kMaxByteAlloc));
+TEST(fxcrt, FXMEMDefaultOOM) {
+  EXPECT_FALSE(FXMEM_DefaultAlloc(kCloseToMaxByteAlloc));
 
   void* ptr = FXMEM_DefaultAlloc(1);
   EXPECT_TRUE(ptr);
-  EXPECT_FALSE(FXMEM_DefaultRealloc(ptr, kMaxByteAlloc));
+  EXPECT_FALSE(FXMEM_DefaultRealloc(ptr, kCloseToMaxByteAlloc));
   FXMEM_DefaultFree(ptr);
 }
 
+TEST(fxcrt, AllocZeroesMemory) {
+  uint8_t* ptr = FX_Alloc(uint8_t, 32);
+  ASSERT_TRUE(ptr);
+  for (size_t i = 0; i < 32; ++i)
+    EXPECT_EQ(0, ptr[i]);
+  FX_Free(ptr);
+}
+
 TEST(fxcrt, FXAlign) {
   static_assert(std::numeric_limits<size_t>::max() % 2 == 1,
                 "numeric limit must be odd for this test");
@@ -116,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 823fe76..f4f0b06 100644
--- a/core/fxcrt/fx_memory_wrappers.h
+++ b/core/fxcrt/fx_memory_wrappers.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,15 +16,28 @@
   inline void operator()(void* ptr) const { FX_Free(ptr); }
 };
 
-// Used with std::vector<> to put purely numeric vectors into
-// the same "general" parition used by FX_Alloc(). Otherwise,
-// replacing FX_Alloc/FX_Free pairs with std::vector<> may undo
-// some of the nice segregation that we get from partition alloc.
-template <class T>
-struct FxAllocAllocator {
+// Escape hatch mechanism to allow non_arithmetic types into data partition.
+template <typename T>
+struct IsFXDataPartitionException : std::false_type {};
+
+// Use with caution. No further checks are made to see if `T` is appropriate
+// for the Data Partition (e.g. no pointers, strings, vtables, etc.). This
+// declaration must occur in the top-level namespace.
+#define FX_DATA_PARTITION_EXCEPTION(T) \
+  template <>                          \
+  struct IsFXDataPartitionException<T> : std::true_type {}
+
+// Allocators for mapping STL containers onto Partition Alloc.
+// Otherwise, replacing e.g. the FX_AllocUninit/FX_Free pairs with STL may
+// undo some of the nice segregation that we get from PartitionAlloc.
+template <class T, void* Alloc(size_t, size_t), void Free(void*)>
+struct FxPartitionAllocAllocator {
  public:
-  static_assert(std::is_arithmetic<T>::value,
+#if !defined(COMPILER_MSVC) || defined(NDEBUG)
+  static_assert(std::is_arithmetic<T>::value || std::is_enum<T>::value ||
+                    IsFXDataPartitionException<T>::value,
                 "Only numeric types allowed in this partition");
+#endif
 
   using value_type = T;
   using pointer = T*;
@@ -36,22 +49,24 @@
 
   template <class U>
   struct rebind {
-    using other = FxAllocAllocator<U>;
+    using other = FxPartitionAllocAllocator<U, Alloc, Free>;
   };
 
-  FxAllocAllocator() noexcept = default;
-  FxAllocAllocator(const FxAllocAllocator& other) noexcept = default;
-  ~FxAllocAllocator() = default;
+  FxPartitionAllocAllocator() noexcept = default;
+  FxPartitionAllocAllocator(const FxPartitionAllocAllocator& other) noexcept =
+      default;
+  ~FxPartitionAllocAllocator() = default;
 
   template <typename U>
-  FxAllocAllocator(const FxAllocAllocator<U>& other) noexcept {}
+  FxPartitionAllocAllocator(
+      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>(FX_AllocOrDie(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);
   }
@@ -67,8 +82,26 @@
   }
 
   // There's no state, so they are all the same,
-  bool operator==(const FxAllocAllocator& that) { return true; }
-  bool operator!=(const FxAllocAllocator& that) { return false; }
+  bool operator==(const FxPartitionAllocAllocator& that) { return true; }
+  bool operator!=(const FxPartitionAllocAllocator& that) { return false; }
 };
 
+// Used to put backing store for std::vector<> and such into the
+// general partition, ensuring they contain data only.
+template <typename T,
+          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,
+                                                   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,
+                              pdfium::internal::StringDealloc>;
+
 #endif  // CORE_FXCRT_FX_MEMORY_WRAPPERS_H_
diff --git a/core/fxcrt/fx_memory_wrappers_unittest.cpp b/core/fxcrt/fx_memory_wrappers_unittest.cpp
index 0927683..7638361 100644
--- a/core/fxcrt/fx_memory_wrappers_unittest.cpp
+++ b/core/fxcrt/fx_memory_wrappers_unittest.cpp
@@ -1,15 +1,28 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxcrt/fx_memory_wrappers.h"
 
 #include <memory>
+#include <sstream>
+#include <string>
 #include <vector>
 
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace {
+
+struct OnlyNumbers {
+  int x;
+  int y;
+};
+
+}  // namespace
+
+FX_DATA_PARTITION_EXCEPTION(OnlyNumbers);
+
 TEST(fxcrt, FxFreeDeleter) {
   std::unique_ptr<int, FxFreeDeleter> empty(nullptr);
   std::unique_ptr<int, FxFreeDeleter> thing(FX_Alloc(int, 1));
@@ -20,6 +33,7 @@
 }
 
 TEST(fxcrt, FxAllocAllocator) {
+  // Let ASAN sanity check some simple operations.
   std::vector<int, FxAllocAllocator<int>> vec;
   vec.push_back(42);
   vec.reserve(100);
@@ -31,3 +45,22 @@
   vec2.resize(0);
   vec2.push_back(42);
 }
+
+TEST(fxcrt, FxStringAllocator) {
+  // Let ASAN sanity check some simple operations.
+  std::basic_ostringstream<char, std::char_traits<char>,
+                           FxStringAllocator<char>>
+      str;
+  str << 'B';
+  str << "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+  str << "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+  str << "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+  str << 42.0f;
+}
+
+TEST(fxcrt, FxAllocAllocatorStructException) {
+  std::vector<OnlyNumbers, FxAllocAllocator<OnlyNumbers>> vec;
+  vec.push_back({42, 73});
+  EXPECT_EQ(vec.back().x, 42);
+  EXPECT_EQ(vec.back().y, 73);
+}
diff --git a/core/fxcrt/fx_number.cpp b/core/fxcrt/fx_number.cpp
index 9935451..d577531 100644
--- a/core/fxcrt/fx_number.cpp
+++ b/core/fxcrt/fx_number.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,29 +6,32 @@
 
 #include "core/fxcrt/fx_number.h"
 
+#include <ctype.h>
+
 #include <limits>
 
 #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_bInteger(true), m_bSigned(false), m_UnsignedValue(0) {}
+    : m_bIsInteger(true), m_bIsSigned(false), m_UnsignedValue(0) {}
 
 FX_Number::FX_Number(int32_t value)
-    : m_bInteger(true), m_bSigned(true), m_SignedValue(value) {}
+    : m_bIsInteger(true), m_bIsSigned(true), m_SignedValue(value) {}
 
 FX_Number::FX_Number(float value)
-    : m_bInteger(false), m_bSigned(true), m_FloatValue(value) {}
+    : m_bIsInteger(false), m_bIsSigned(true), m_FloatValue(value) {}
 
 FX_Number::FX_Number(ByteStringView strc)
-    : m_bInteger(true), m_bSigned(false), m_UnsignedValue(0) {
+    : m_bIsInteger(true), m_bIsSigned(false), m_UnsignedValue(0) {
   if (strc.IsEmpty())
     return;
 
   if (strc.Contains('.')) {
-    m_bInteger = false;
-    m_bSigned = true;
+    m_bIsInteger = false;
+    m_bIsSigned = true;
     m_FloatValue = StringToFloat(strc);
     return;
   }
@@ -43,22 +46,22 @@
   size_t cc = 0;
   if (strc[0] == '+') {
     cc++;
-    m_bSigned = true;
+    m_bIsSigned = true;
   } else if (strc[0] == '-') {
     bNegative = true;
-    m_bSigned = true;
+    m_bIsSigned = true;
     cc++;
   }
 
-  while (cc < strc.GetLength() && std::isdigit(strc[cc])) {
-    unsigned_val = unsigned_val * 10 + FXSYS_DecimalCharToInt(strc.CharAt(cc));
-    if (!unsigned_val.IsValid())
-      break;
-    cc++;
+  for (; cc < strc.GetLength() && isdigit(strc[cc]); ++cc) {
+    // Deliberately not using FXSYS_DecimalCharToInt() in a tight loop to avoid
+    // a duplicate isdigit() call. Note that the order of operation is
+    // important to avoid unintentional overflows.
+    unsigned_val = unsigned_val * 10 + (strc[cc] - '0');
   }
 
   uint32_t uValue = unsigned_val.ValueOrDefault(0);
-  if (!m_bSigned) {
+  if (!m_bIsSigned) {
     m_UnsignedValue = uValue;
     return;
   }
@@ -86,13 +89,17 @@
 }
 
 int32_t FX_Number::GetSigned() const {
-  return m_bInteger ? 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 {
-  if (!m_bInteger)
+  if (!m_bIsInteger)
     return m_FloatValue;
 
-  return m_bSigned ? static_cast<float>(m_SignedValue)
-                   : static_cast<float>(m_UnsignedValue);
+  return m_bIsSigned ? static_cast<float>(m_SignedValue)
+                     : static_cast<float>(m_UnsignedValue);
 }
diff --git a/core/fxcrt/fx_number.h b/core/fxcrt/fx_number.h
index 0ee4744..3eda3eb 100644
--- a/core/fxcrt/fx_number.h
+++ b/core/fxcrt/fx_number.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -19,15 +19,15 @@
   explicit FX_Number(float value);
   explicit FX_Number(ByteStringView str);
 
-  bool IsInteger() const { return m_bInteger; }
-  bool IsSigned() const { return m_bSigned; }
+  bool IsInteger() const { return m_bIsInteger; }
+  bool IsSigned() const { return m_bIsSigned; }
 
   int32_t GetSigned() const;  // Underflow/Overflow possible.
   float GetFloat() const;
 
  private:
-  bool m_bInteger;  // One of the two integers vs. float type.
-  bool m_bSigned;   // Only valid if |m_bInteger|.
+  bool m_bIsInteger;  // One of the two integers vs. float type.
+  bool m_bIsSigned;   // Only valid if |m_bInteger|.
   union {
     uint32_t m_UnsignedValue;
     int32_t m_SignedValue;
diff --git a/core/fxcrt/fx_number_unittest.cpp b/core/fxcrt/fx_number_unittest.cpp
index a31dc55..9a1d2eb 100644
--- a/core/fxcrt/fx_number_unittest.cpp
+++ b/core/fxcrt/fx_number_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -43,42 +43,61 @@
   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) {
-  {
-    FX_Number number("");
-    EXPECT_TRUE(number.IsInteger());
-    EXPECT_FALSE(number.IsSigned());
-  }
-  {
-    FX_Number number("0");
-    EXPECT_TRUE(number.IsInteger());
-    EXPECT_FALSE(number.IsSigned());
-  }
-  {
-    FX_Number number("10");
-    EXPECT_TRUE(number.IsInteger());
-    EXPECT_FALSE(number.IsSigned());
-  }
-  {
-    FX_Number number("4294967295");
-    EXPECT_TRUE(number.IsInteger());
-    EXPECT_FALSE(number.IsSigned());
-  }
-  {
-    // Value overflows.
-    FX_Number number("4223423494965252");
-    EXPECT_TRUE(number.IsInteger());
-    EXPECT_FALSE(number.IsSigned());
-  }
-  {
-    // No explicit sign will allow the number to go negative if we retrieve
-    // it as a signed value. This is needed for things like the encryption
-    // Permissions flag (Table 3.20 PDF 1.7 spec)
-    FX_Number number("4294965252");
-    EXPECT_EQ(-2044, number.GetSigned());
-  }
+  struct TestCase {
+    const char* input;
+    int expected_output;
+  };
+
+  auto test_func = [](pdfium::span<const TestCase> test_cases) {
+    for (const auto& test : test_cases) {
+      FX_Number number(test.input);
+      EXPECT_TRUE(number.IsInteger());
+      EXPECT_FALSE(number.IsSigned());
+      EXPECT_EQ(test.expected_output, number.GetSigned());
+    }
+  };
+
+  static constexpr TestCase kNormalCases[] = {
+      {"", 0},
+      {"0", 0},
+      {"10", 10},
+  };
+  test_func(kNormalCases);
+
+  static constexpr TestCase kOverflowCases[] = {
+      {"4223423494965252", 0},
+      {"4294967296", 0},
+      {"4294967297", 0},
+      {"5000000000", 0},
+  };
+  test_func(kOverflowCases);
+
+  // No explicit sign will allow the number to go negative if retrieved as a
+  // signed value. This is needed for things like the encryption permissions
+  // flag (Table 3.20 PDF 1.7 spec)
+  static constexpr TestCase kNegativeCases[] = {
+      {"4294965252", -2044},
+      {"4294967247", -49},
+      {"4294967248", -48},
+      {"4294967292", -4},
+      {"4294967295", -1},
+  };
+  test_func(kNegativeCases);
 }
 
 TEST(fxnumber, FromStringSigned) {
diff --git a/core/fxcrt/fx_random.cpp b/core/fxcrt/fx_random.cpp
index ce796d9..cc40efc 100644
--- a/core/fxcrt/fx_random.cpp
+++ b/core/fxcrt/fx_random.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,7 +17,7 @@
 #define MT_Upper_Mask 0x80000000
 #define MT_Lower_Mask 0x7fffffff
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include <wincrypt.h>
 #else
 #include <sys/time.h>
@@ -34,7 +34,7 @@
 bool g_bHaveGlobalSeed = false;
 uint32_t g_nGlobalSeed = 0;
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 bool GenerateSeedFromCryptoRandom(uint32_t* pSeed) {
   HCRYPTPROV hCP = 0;
   if (!::CryptAcquireContext(&hCP, nullptr, nullptr, PROV_RSA_FULL, 0) ||
@@ -51,7 +51,7 @@
   char c;
   uintptr_t p = reinterpret_cast<uintptr_t>(&c);
   uint32_t seed = ~static_cast<uint32_t>(p >> 3);
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   SYSTEMTIME st;
   GetSystemTime(&st);
   seed ^= static_cast<uint32_t>(st.wSecond) * 1000000;
@@ -69,7 +69,7 @@
 
 void* ContextFromNextGlobalSeed() {
   if (!g_bHaveGlobalSeed) {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     if (!GenerateSeedFromCryptoRandom(&g_nGlobalSeed))
       g_nGlobalSeed = GenerateSeedFromEnvironment();
 #else
diff --git a/core/fxcrt/fx_random.h b/core/fxcrt/fx_random.h
index 52d514b..c6cbd5e 100644
--- a/core/fxcrt/fx_random.h
+++ b/core/fxcrt/fx_random.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxcrt/fx_random_unittest.cpp b/core/fxcrt/fx_random_unittest.cpp
index 607f140..1361e24 100644
--- a/core/fxcrt/fx_random_unittest.cpp
+++ b/core/fxcrt/fx_random_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxcrt/fx_safe_types.h b/core/fxcrt/fx_safe_types.h
index 6f5b967..d7b768b 100644
--- a/core/fxcrt/fx_safe_types.h
+++ b/core/fxcrt/fx_safe_types.h
@@ -1,13 +1,14 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef CORE_FXCRT_FX_SAFE_TYPES_H_
 #define CORE_FXCRT_FX_SAFE_TYPES_H_
 
-#include <stdlib.h>  // For size_t.
+#include <stddef.h>
+#include <stdint.h>
 
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/fx_types.h"
 #include "third_party/base/numerics/safe_math.h"
 
 using FX_SAFE_UINT32 = pdfium::base::CheckedNumeric<uint32_t>;
diff --git a/core/fxcrt/fx_safe_types_unittest.cpp b/core/fxcrt/fx_safe_types_unittest.cpp
new file mode 100644
index 0000000..bac9184
--- /dev/null
+++ b/core/fxcrt/fx_safe_types_unittest.cpp
@@ -0,0 +1,22 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <limits>
+
+#include "core/fxcrt/fx_safe_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// PDFium relies on safe types handling the --2147483648 boundary
+// condition without overflow.
+TEST(FXSafeTypes, UnaryMinus) {
+  FX_SAFE_INT32 safe_val = std::numeric_limits<int32_t>::min();
+  EXPECT_TRUE(safe_val.IsValid());
+  EXPECT_FALSE((-safe_val).IsValid());
+}
+
+TEST(FXSafeTypes, SubtractFromZero) {
+  FX_SAFE_INT32 safe_val = std::numeric_limits<int32_t>::min();
+  EXPECT_TRUE(safe_val.IsValid());
+  EXPECT_FALSE((0 - safe_val).IsValid());
+}
diff --git a/core/fxcrt/fx_stream.cpp b/core/fxcrt/fx_stream.cpp
index dd3c1fb..1ceb539 100644
--- a/core/fxcrt/fx_stream.cpp
+++ b/core/fxcrt/fx_stream.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,100 +6,75 @@
 
 #include "core/fxcrt/fx_stream.h"
 
-#include <algorithm>
 #include <memory>
 #include <utility>
-#include <vector>
 
-#include "build/build_config.h"
 #include "core/fxcrt/fileaccess_iface.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/ptr_util.h"
-
-#if defined(OS_WIN)
-#include <direct.h>
-
-struct FX_FolderHandle {
-  HANDLE m_Handle;
-  bool m_bEnd;
-  WIN32_FIND_DATAA m_FindData;
-};
-#else
-#include <dirent.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-struct FX_FolderHandle {
-  ByteString m_Path;
-  DIR* m_Dir;
-};
-#endif
 
 namespace {
 
 class CFX_CRTFileStream final : public IFX_SeekableStream {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // IFX_SeekableStream:
   FX_FILESIZE GetSize() override { return m_pFile->GetSize(); }
   bool IsEOF() override { return GetPosition() >= GetSize(); }
   FX_FILESIZE GetPosition() override { return m_pFile->GetPosition(); }
-  bool ReadBlockAtOffset(void* buffer,
-                         FX_FILESIZE offset,
-                         size_t size) override {
-    return m_pFile->ReadPos(buffer, size, offset) > 0;
+  bool ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                         FX_FILESIZE offset) override {
+    return m_pFile->ReadPos(buffer.data(), buffer.size(), offset) > 0;
   }
-  size_t ReadBlock(void* buffer, size_t size) override {
-    return m_pFile->Read(buffer, size);
+  size_t ReadBlock(pdfium::span<uint8_t> buffer) override {
+    return m_pFile->Read(buffer.data(), buffer.size());
   }
-  bool WriteBlockAtOffset(const void* buffer,
-                          FX_FILESIZE offset,
-                          size_t size) override {
-    return !!m_pFile->WritePos(buffer, size, offset);
+  bool WriteBlockAtOffset(pdfium::span<const uint8_t> buffer,
+                          FX_FILESIZE offset) override {
+    return !!m_pFile->WritePos(buffer.data(), buffer.size(), offset);
   }
   bool Flush() override { return m_pFile->Flush(); }
 
  private:
   explicit CFX_CRTFileStream(std::unique_ptr<FileAccessIface> pFA)
       : m_pFile(std::move(pFA)) {}
-  ~CFX_CRTFileStream() override {}
+  ~CFX_CRTFileStream() override = default;
 
   std::unique_ptr<FileAccessIface> m_pFile;
 };
 
 }  // namespace
 
-// static
-RetainPtr<IFX_SeekableStream> IFX_SeekableStream::CreateFromFilename(
-    const char* filename,
-    uint32_t dwModes) {
-  std::unique_ptr<FileAccessIface> pFA = FileAccessIface::Create();
-  if (!pFA->Open(filename, dwModes))
-    return nullptr;
-  return pdfium::MakeRetain<CFX_CRTFileStream>(std::move(pFA));
+bool IFX_WriteStream::WriteString(ByteStringView str) {
+  return WriteBlock(str.raw_span());
 }
 
-// static
-RetainPtr<IFX_SeekableStream> IFX_SeekableStream::CreateFromFilename(
-    const wchar_t* filename,
-    uint32_t dwModes) {
-  std::unique_ptr<FileAccessIface> pFA = FileAccessIface::Create();
-  if (!pFA->Open(filename, dwModes))
-    return nullptr;
-  return pdfium::MakeRetain<CFX_CRTFileStream>(std::move(pFA));
+bool IFX_WriteStream::WriteByte(uint8_t byte) {
+  return WriteBlock({&byte, 1});
+}
+
+bool IFX_WriteStream::WriteDWord(uint32_t i) {
+  char buf[20] = {};
+  FXSYS_itoa(i, buf, 10);
+  return WriteBlock({reinterpret_cast<uint8_t*>(buf), strlen(buf)});
+}
+
+bool IFX_WriteStream::WriteFilesize(FX_FILESIZE size) {
+  char buf[20] = {};
+  FXSYS_i64toa(size, buf, 10);
+  return WriteBlock({reinterpret_cast<uint8_t*>(buf), strlen(buf)});
 }
 
 // static
 RetainPtr<IFX_SeekableReadStream> IFX_SeekableReadStream::CreateFromFilename(
     const char* filename) {
-  return IFX_SeekableStream::CreateFromFilename(filename, FX_FILEMODE_ReadOnly);
+  std::unique_ptr<FileAccessIface> pFA = FileAccessIface::Create();
+  if (!pFA->Open(filename))
+    return nullptr;
+  return pdfium::MakeRetain<CFX_CRTFileStream>(std::move(pFA));
 }
 
-bool IFX_SeekableWriteStream::WriteBlock(const void* pData, size_t size) {
-  return WriteBlockAtOffset(pData, GetSize(), size);
+bool IFX_SeekableWriteStream::WriteBlock(pdfium::span<const uint8_t> buffer) {
+  return WriteBlockAtOffset(buffer, GetSize());
 }
 
 bool IFX_SeekableReadStream::IsEOF() {
@@ -110,78 +85,10 @@
   return 0;
 }
 
-size_t IFX_SeekableReadStream::ReadBlock(void* buffer, size_t size) {
+size_t IFX_SeekableReadStream::ReadBlock(pdfium::span<uint8_t> buffer) {
   return 0;
 }
 
-bool IFX_SeekableStream::WriteBlock(const void* buffer, size_t size) {
-  return WriteBlockAtOffset(buffer, GetSize(), size);
-}
-
-bool IFX_SeekableStream::WriteString(ByteStringView str) {
-  return WriteBlock(str.unterminated_c_str(), str.GetLength());
-}
-
-FX_FolderHandle* FX_OpenFolder(const char* path) {
-  auto handle = pdfium::MakeUnique<FX_FolderHandle>();
-#if defined(OS_WIN)
-  handle->m_Handle =
-      FindFirstFileExA((ByteString(path) + "/*.*").c_str(), FindExInfoStandard,
-                       &handle->m_FindData, FindExSearchNameMatch, nullptr, 0);
-  if (handle->m_Handle == INVALID_HANDLE_VALUE)
-    return nullptr;
-
-  handle->m_bEnd = false;
-#else
-  DIR* dir = opendir(path);
-  if (!dir)
-    return nullptr;
-
-  handle->m_Path = path;
-  handle->m_Dir = dir;
-#endif
-  return handle.release();
-}
-
-bool FX_GetNextFile(FX_FolderHandle* handle,
-                    ByteString* filename,
-                    bool* bFolder) {
-  if (!handle)
-    return false;
-
-#if defined(OS_WIN)
-  if (handle->m_bEnd)
-    return false;
-
-  *filename = handle->m_FindData.cFileName;
-  *bFolder =
-      (handle->m_FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
-  if (!FindNextFileA(handle->m_Handle, &handle->m_FindData))
-    handle->m_bEnd = true;
-  return true;
-#else
-  struct dirent* de = readdir(handle->m_Dir);
-  if (!de)
-    return false;
-  ByteString fullpath = handle->m_Path + "/" + de->d_name;
-  struct stat deStat;
-  if (stat(fullpath.c_str(), &deStat) < 0)
-    return false;
-
-  *filename = de->d_name;
-  *bFolder = S_ISDIR(deStat.st_mode);
-  return true;
-#endif
-}
-
-void FX_CloseFolder(FX_FolderHandle* handle) {
-  if (!handle)
-    return;
-
-#if defined(OS_WIN)
-  FindClose(handle->m_Handle);
-#else
-  closedir(handle->m_Dir);
-#endif
-  delete handle;
+bool IFX_SeekableStream::WriteBlock(pdfium::span<const uint8_t> buffer) {
+  return WriteBlockAtOffset(buffer, GetSize());
 }
diff --git a/core/fxcrt/fx_stream.h b/core/fxcrt/fx_stream.h
index ef58660..15e02d5 100644
--- a/core/fxcrt/fx_stream.h
+++ b/core/fxcrt/fx_stream.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,31 +7,23 @@
 #ifndef CORE_FXCRT_FX_STREAM_H_
 #define CORE_FXCRT_FX_STREAM_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include <stddef.h>
+#include <stdint.h>
+
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/fx_types.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/compiler_specific.h"
-
-struct FX_FolderHandle;
-
-FX_FolderHandle* FX_OpenFolder(const char* path);
-bool FX_GetNextFile(FX_FolderHandle* handle,
-                    ByteString* filename,
-                    bool* bFolder);
-void FX_CloseFolder(FX_FolderHandle* handle);
-
-// Used with std::unique_ptr to automatically call FX_CloseFolder().
-struct FxFolderHandleCloser {
-  inline void operator()(FX_FolderHandle* h) const { FX_CloseFolder(h); }
-};
-
-#define FX_FILEMODE_ReadOnly 1
-#define FX_FILEMODE_Truncate 2
+#include "third_party/base/containers/span.h"
 
 class IFX_WriteStream {
  public:
-  virtual bool WriteBlock(const void* pData, size_t size) = 0;
-  virtual bool WriteString(ByteStringView str) = 0;
+  // When `size` is 0, treat it as a no-op and return true.
+  virtual bool WriteBlock(pdfium::span<const uint8_t> data) = 0;
+
+  bool WriteString(ByteStringView str);
+  bool WriteByte(uint8_t byte);
+  bool WriteDWord(uint32_t i);
+  bool WriteFilesize(FX_FILESIZE size);
 
  protected:
   virtual ~IFX_WriteStream() = default;
@@ -39,8 +31,6 @@
 
 class IFX_ArchiveStream : public IFX_WriteStream {
  public:
-  virtual bool WriteByte(uint8_t byte) = 0;
-  virtual bool WriteDWord(uint32_t i) = 0;
   virtual FX_FILESIZE CurrentOffset() const = 0;
 };
 
@@ -56,12 +46,11 @@
                                 public IFX_RetainableWriteStream {
  public:
   // IFX_WriteStream:
-  bool WriteBlock(const void* pData, size_t size) override;
+  bool WriteBlock(pdfium::span<const uint8_t> buffer) override;
 
   virtual bool Flush() = 0;
-  virtual bool WriteBlockAtOffset(const void* pData,
-                                  FX_FILESIZE offset,
-                                  size_t size) = 0;
+  virtual bool WriteBlockAtOffset(pdfium::span<const uint8_t> data,
+                                  FX_FILESIZE offset) = 0;
 };
 
 class IFX_SeekableReadStream : virtual public Retainable,
@@ -72,26 +61,16 @@
 
   virtual bool IsEOF();
   virtual FX_FILESIZE GetPosition();
-  virtual size_t ReadBlock(void* buffer, size_t size);
-
-  virtual bool ReadBlockAtOffset(void* buffer,
-                                 FX_FILESIZE offset,
-                                 size_t size) WARN_UNUSED_RESULT = 0;
+  [[nodiscard]] virtual size_t ReadBlock(pdfium::span<uint8_t> buffer);
+  [[nodiscard]] virtual bool ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                                               FX_FILESIZE offset) = 0;
 };
 
 class IFX_SeekableStream : public IFX_SeekableReadStream,
                            public IFX_SeekableWriteStream {
  public:
-  static RetainPtr<IFX_SeekableStream> CreateFromFilename(const char* filename,
-                                                          uint32_t dwModes);
-
-  static RetainPtr<IFX_SeekableStream> CreateFromFilename(
-      const wchar_t* filename,
-      uint32_t dwModes);
-
   // IFX_SeekableWriteStream:
-  bool WriteBlock(const void* buffer, size_t size) override;
-  bool WriteString(ByteStringView str) override;
+  bool WriteBlock(pdfium::span<const uint8_t> buffer) override;
 };
 
 #endif  // CORE_FXCRT_FX_STREAM_H_
diff --git a/core/fxcrt/fx_string.cpp b/core/fxcrt/fx_string.cpp
index d4a6d38..62109cd 100644
--- a/core/fxcrt/fx_string.cpp
+++ b/core/fxcrt/fx_string.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,31 +6,125 @@
 
 #include "core/fxcrt/fx_string.h"
 
-#include <limits>
-#include <vector>
+#include <stdint.h>
 
-#include "core/fxcrt/cfx_utf8decoder.h"
-#include "core/fxcrt/cfx_utf8encoder.h"
+#include <iterator>
+
+#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/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) {
-  if (bsStr.IsEmpty())
-    return WideString();
+  WideString buffer;
 
-  CFX_UTF8Decoder decoder;
-  for (size_t i = 0; i < bsStr.GetLength(); i++)
-    decoder.Input(bsStr[i]);
+  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 WideString(decoder.GetResult());
+  return buffer;
 }
 
 namespace {
@@ -45,15 +139,13 @@
     0.0000001, 0.00000001, 0.000000001, 0.0000000001, 0.00000000001};
 
 template <class T>
-T StringTo(ByteStringView strc,
-           const T fractional_scales[],
-           size_t fractional_scales_size) {
+T StringTo(ByteStringView strc, pdfium::span<const T> fractional_scales) {
   if (strc.IsEmpty())
     return 0;
 
-  int cc = 0;
   bool bNegative = false;
-  int len = strc.GetLength();
+  size_t cc = 0;
+  size_t len = strc.GetLength();
   if (strc[0] == '+') {
     cc++;
   } else if (strc[0] == '-') {
@@ -79,7 +171,7 @@
       value +=
           fractional_scales[scale] * FXSYS_DecimalCharToInt(strc.CharAt(cc));
       scale++;
-      if (scale == fractional_scales_size)
+      if (scale == fractional_scales.size())
         break;
       cc++;
     }
@@ -88,7 +180,7 @@
 }
 
 template <class T>
-size_t ToString(T value, int (*round_func)(T), char* buf) {
+size_t ToString(T value, int (*round_func)(T), pdfium::span<char> buf) {
   buf[0] = '0';
   buf[1] = '\0';
   if (value == 0) {
@@ -119,7 +211,7 @@
   int i = scaled / scale;
   FXSYS_itoa(i, buf2, 10);
   size_t len = strlen(buf2);
-  memcpy(buf + buf_size, buf2, len);
+  fxcrt::spancpy(buf.subspan(buf_size), pdfium::make_span(buf2).first(len));
   buf_size += len;
   int fraction = scaled % scale;
   if (fraction == 0) {
@@ -138,27 +230,34 @@
 }  // namespace
 
 float StringToFloat(ByteStringView strc) {
-  return StringTo<float>(strc, kFractionScalesFloat,
-                         FX_ArraySize(kFractionScalesFloat));
+  return StringTo<float>(strc, kFractionScalesFloat);
 }
 
 float StringToFloat(WideStringView wsStr) {
-  return StringToFloat(FX_UTF8Encode(wsStr).c_str());
+  return StringToFloat(FX_UTF8Encode(wsStr).AsStringView());
 }
 
-size_t FloatToString(float f, char* buf) {
+size_t FloatToString(float f, pdfium::span<char> buf) {
   return ToString<float>(f, FXSYS_roundf, buf);
 }
 
 double StringToDouble(ByteStringView strc) {
-  return StringTo<double>(strc, kFractionScalesDouble,
-                          FX_ArraySize(kFractionScalesDouble));
+  return StringTo<double>(strc, kFractionScalesDouble);
 }
 
 double StringToDouble(WideStringView wsStr) {
-  return StringToDouble(FX_UTF8Encode(wsStr).c_str());
+  return StringToDouble(FX_UTF8Encode(wsStr).AsStringView());
 }
 
-size_t DoubleToString(double d, char* buf) {
+size_t DoubleToString(double d, pdfium::span<char> buf) {
   return ToString<double>(d, FXSYS_round, buf);
 }
+
+namespace fxcrt {
+
+template std::vector<ByteString> Split<ByteString>(const ByteString& that,
+                                                   ByteString::CharType ch);
+template std::vector<WideString> Split<WideString>(const WideString& that,
+                                                   WideString::CharType ch);
+
+}  // namespace fxcrt
diff --git a/core/fxcrt/fx_string.h b/core/fxcrt/fx_string.h
index e27f43a..49351c1 100644
--- a/core/fxcrt/fx_string.h
+++ b/core/fxcrt/fx_string.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,21 +13,23 @@
 
 #include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/widestring.h"
+#include "third_party/base/containers/span.h"
 
-#define FXBSTR_ID(c1, c2, c3, c4)                                      \
-  (((uint32_t)c1 << 24) | ((uint32_t)c2 << 16) | ((uint32_t)c3 << 8) | \
-   ((uint32_t)c4))
+constexpr uint32_t FXBSTR_ID(uint8_t c1, uint8_t c2, uint8_t c3, uint8_t c4) {
+  return static_cast<uint32_t>(c1) << 24 | static_cast<uint32_t>(c2) << 16 |
+         static_cast<uint32_t>(c3) << 8 | static_cast<uint32_t>(c4);
+}
 
 ByteString FX_UTF8Encode(WideStringView wsStr);
 WideString FX_UTF8Decode(ByteStringView bsStr);
 
 float StringToFloat(ByteStringView str);
 float StringToFloat(WideStringView wsStr);
-size_t FloatToString(float f, char* buf);
+size_t FloatToString(float f, pdfium::span<char> buf);
 
 double StringToDouble(ByteStringView str);
 double StringToDouble(WideStringView wsStr);
-size_t DoubleToString(double d, char* buf);
+size_t DoubleToString(double d, pdfium::span<char> buf);
 
 namespace fxcrt {
 
@@ -35,8 +37,8 @@
 std::vector<StrType> Split(const StrType& that, typename StrType::CharType ch) {
   std::vector<StrType> result;
   StringViewTemplate<typename StrType::CharType> remaining(that.span());
-  while (1) {
-    Optional<size_t> index = remaining.Find(ch);
+  while (true) {
+    absl::optional<size_t> index = remaining.Find(ch);
     if (!index.has_value())
       break;
     result.emplace_back(remaining.First(index.value()));
@@ -46,6 +48,13 @@
   return result;
 }
 
+extern template std::vector<ByteString> Split<ByteString>(
+    const ByteString& that,
+    ByteString::CharType ch);
+extern template std::vector<WideString> Split<WideString>(
+    const WideString& that,
+    WideString::CharType ch);
+
 }  // namespace fxcrt
 
 #endif  // CORE_FXCRT_FX_STRING_H_
diff --git a/core/fxcrt/fx_string_unittest.cpp b/core/fxcrt/fx_string_unittest.cpp
index 9556360..c671d22 100644
--- a/core/fxcrt/fx_string_unittest.cpp
+++ b/core/fxcrt/fx_string_unittest.cpp
@@ -1,70 +1,156 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include <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/containers/span.h"
 
-char* TerminatedFloatToString(float value, char* buf) {
+char* TerminatedFloatToString(float value, pdfium::span<char> buf) {
   size_t buflen = FloatToString(value, buf);
   buf[buflen] = '\0';
-  return buf;
+  return buf.data();
 }
 
-char* TerminatedDoubleToString(double value, char* buf) {
+char* TerminatedDoubleToString(double value, pdfium::span<char> buf) {
   size_t buflen = DoubleToString(value, buf);
   buf[buflen] = '\0';
-  return buf;
+  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_string_wrappers.h b/core/fxcrt/fx_string_wrappers.h
new file mode 100644
index 0000000..528236b
--- /dev/null
+++ b/core/fxcrt/fx_string_wrappers.h
@@ -0,0 +1,25 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_FX_STRING_WRAPPERS_H_
+#define CORE_FXCRT_FX_STRING_WRAPPERS_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "core/fxcrt/fx_memory_wrappers.h"
+
+namespace fxcrt {
+
+// String that uses partition alloc for backing store.
+using string =
+    std::basic_string<char, std::char_traits<char>, FxStringAllocator<char>>;
+
+// String stream that uses PartitionAlloc for backing store.
+using ostringstream = std::
+    basic_ostringstream<char, std::char_traits<char>, FxStringAllocator<char>>;
+
+}  // namespace fxcrt
+
+#endif  // CORE_FXCRT_FX_STRING_WRAPPERS_H_
diff --git a/core/fxcrt/fx_string_wrappers_unittest.cpp b/core/fxcrt/fx_string_wrappers_unittest.cpp
new file mode 100644
index 0000000..a7854e9
--- /dev/null
+++ b/core/fxcrt/fx_string_wrappers_unittest.cpp
@@ -0,0 +1,26 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/fx_string_wrappers.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(STLUtil, string) {
+  fxcrt::string str;
+  str += '2';
+  str += '2';
+  str += "C is ";
+  str += '7';
+  str += '1';
+  str += '.';
+  str += '6';
+  str += "F";
+  EXPECT_STREQ("22C is 71.6F", str.c_str());
+}
+
+TEST(STLUtil, OStringStream) {
+  fxcrt::ostringstream str;
+  str << 22 << "C is " << 71.6f << "F";
+  EXPECT_STREQ("22C is 71.6F", str.str().c_str());
+}
diff --git a/core/fxcrt/fx_system.cpp b/core/fxcrt/fx_system.cpp
index 7358b75..0286b07 100644
--- a/core/fxcrt/fx_system.cpp
+++ b/core/fxcrt/fx_system.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,8 @@
 
 #include "core/fxcrt/fx_system.h"
 
-#include <cmath>
+#include <math.h>
+
 #include <limits>
 
 #include "build/build_config.h"
@@ -14,7 +15,7 @@
 
 namespace {
 
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
 uint32_t g_last_error = 0;
 #endif
 
@@ -89,7 +90,7 @@
 }  // namespace
 
 int FXSYS_roundf(float f) {
-  if (std::isnan(f))
+  if (isnan(f))
     return 0;
   if (f < static_cast<float>(std::numeric_limits<int>::min()))
     return std::numeric_limits<int>::min();
@@ -99,7 +100,7 @@
 }
 
 int FXSYS_round(double d) {
-  if (std::isnan(d))
+  if (isnan(d))
     return 0;
   if (d < static_cast<double>(std::numeric_limits<int>::min()))
     return std::numeric_limits<int>::min();
@@ -124,7 +125,7 @@
   return FXSYS_IntToStr<int64_t, uint64_t, char*>(value, str, radix);
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 
 size_t FXSYS_wcsftime(wchar_t* strDest,
                       size_t maxsize,
@@ -144,11 +145,7 @@
   return wcsftime(strDest, maxsize, format, timeptr);
 }
 
-#else   // defined(OS_WIN)
-
-int FXSYS_GetACP() {
-  return 0;
-}
+#else   // BUILDFLAG(IS_WIN)
 
 char* FXSYS_strlwr(char* str) {
   if (!str) {
@@ -223,40 +220,6 @@
   return FXSYS_IntToStr<int32_t, uint32_t, char*>(value, str, radix);
 }
 
-int FXSYS_WideCharToMultiByte(uint32_t codepage,
-                              uint32_t dwFlags,
-                              const wchar_t* wstr,
-                              int wlen,
-                              char* buf,
-                              int buflen,
-                              const char* default_str,
-                              int* pUseDefault) {
-  int len = 0;
-  for (int i = 0; i < wlen; i++) {
-    if (wstr[i] < 0x100) {
-      if (buf && len < buflen)
-        buf[len] = static_cast<char>(wstr[i]);
-      len++;
-    }
-  }
-  return len;
-}
-
-int FXSYS_MultiByteToWideChar(uint32_t codepage,
-                              uint32_t dwFlags,
-                              const char* bstr,
-                              int blen,
-                              wchar_t* buf,
-                              int buflen) {
-  int wlen = 0;
-  for (int i = 0; i < blen; i++) {
-    if (buf && wlen < buflen)
-      buf[wlen] = reinterpret_cast<const uint8_t*>(bstr)[i];
-    wlen++;
-  }
-  return wlen;
-}
-
 void FXSYS_SetLastError(uint32_t err) {
   g_last_error = err;
 }
@@ -264,4 +227,8 @@
 uint32_t FXSYS_GetLastError() {
   return g_last_error;
 }
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
+
+float FXSYS_sqrt2(float a, float b) {
+  return sqrtf(a * a + b * b);
+}
diff --git a/core/fxcrt/fx_system.h b/core/fxcrt/fx_system.h
index 9963b90..ca910f8 100644
--- a/core/fxcrt/fx_system.h
+++ b/core/fxcrt/fx_system.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,32 +7,14 @@
 #ifndef CORE_FXCRT_FX_SYSTEM_H_
 #define CORE_FXCRT_FX_SYSTEM_H_
 
-#include <assert.h>
-#include <math.h>
-#include <stdarg.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 #include <wchar.h>
 
-// _FX_PLATFORM_ values;
-#define _FX_PLATFORM_WINDOWS_ 1
-#define _FX_PLATFORM_LINUX_ 2
-#define _FX_PLATFORM_APPLE_ 3
-
-#if defined(_WIN32)
-#define _FX_PLATFORM_ _FX_PLATFORM_WINDOWS_
-#elif defined(_WIN64)
-#define _FX_PLATFORM_ _FX_PLATFORM_WINDOWS_
-#elif defined(__linux__)
-#define _FX_PLATFORM_ _FX_PLATFORM_LINUX_
-#elif defined(__APPLE__)
-#define _FX_PLATFORM_ _FX_PLATFORM_APPLE_
-#elif defined(__asmjs__) || defined(__wasm__)
-#define _FX_PLATFORM_ _FX_PLATFORM_LINUX_
-#endif
+#include "build/build_config.h"
+#include "core/fxcrt/fx_types.h"
 
 #if defined(_MSC_VER) && _MSC_VER < 1900
 #error Sorry, VC++ 2015 or later is required to compile PDFium.
@@ -42,46 +24,24 @@
 #error Cannot compile v8 with wasm.
 #endif  // PDF_ENABLE_V8
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if BUILDFLAG(IS_WIN)
 #include <windows.h>
-#include <sal.h>
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-#include <Carbon/Carbon.h>
-#include <libkern/OSAtomic.h>
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#endif  // BUILDFLAG(IS_WIN)
 
 #ifdef __cplusplus
 extern "C" {
 #endif  // __cplusplus
 
-#define IsFloatZero(f) ((f) < 0.0001 && (f) > -0.0001)
-#define IsFloatBigger(fa, fb) ((fa) > (fb) && !IsFloatZero((fa) - (fb)))
-#define IsFloatSmaller(fa, fb) ((fa) < (fb) && !IsFloatZero((fa) - (fb)))
-#define IsFloatEqual(fa, fb) IsFloatZero((fa) - (fb))
-
-// PDFium file sizes match the platform, but PDFium itself does not support
-// files larger than 2GB even if the platform does. The value must be signed
-// to support -1 error returns.
-// TODO(tsepez): support larger files.
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-#define FX_FILESIZE int32_t
-#else  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-#define FX_FILESIZE off_t
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-
-#ifndef ASSERT
-#ifndef NDEBUG
-#define ASSERT assert
-#else
-#define ASSERT(a)
-#endif  // NDEBUG
-#endif  // ASSERT
+#define FXSYS_IsFloatZero(f) ((f) < 0.0001 && (f) > -0.0001)
+#define FXSYS_IsFloatBigger(fa, fb) \
+  ((fa) > (fb) && !FXSYS_IsFloatZero((fa) - (fb)))
+#define FXSYS_IsFloatSmaller(fa, fb) \
+  ((fa) < (fb) && !FXSYS_IsFloatZero((fa) - (fb)))
+#define FXSYS_IsFloatEqual(fa, fb) FXSYS_IsFloatZero((fa) - (fb))
 
 // M_PI not universally present on all platforms.
-#define FX_PI 3.1415926535897932384626433832795f
-#define FX_BEZIER 0.5522847498308f
+#define FXSYS_PI 3.1415926535897932384626433832795f
+#define FXSYS_BEZIER 0.5522847498308f
 
 // NOTE: prevent use of the return value from snprintf() since some platforms
 // have different return values.
@@ -90,98 +50,33 @@
 #define FXSYS_sprintf DO_NOT_USE_SPRINTF_DIE_DIE_DIE
 #define FXSYS_vsprintf DO_NOT_USE_VSPRINTF_DIE_DIE_DIE
 
-#ifdef __cplusplus
-}  // extern "C"
-
-#include "third_party/base/numerics/safe_conversions.h"
-
-// Overloaded functions for C++ templates
-inline size_t FXSYS_len(const char* ptr) {
-  return strlen(ptr);
-}
-
-inline size_t FXSYS_len(const wchar_t* ptr) {
-  return wcslen(ptr);
-}
-
-inline int FXSYS_cmp(const char* ptr1, const char* ptr2, size_t len) {
-  return memcmp(ptr1, ptr2, len);
-}
-
-inline int FXSYS_cmp(const wchar_t* ptr1, const wchar_t* ptr2, size_t len) {
-  return wmemcmp(ptr1, ptr2, len);
-}
-
-inline const char* FXSYS_chr(const char* ptr, char ch, size_t len) {
-  return reinterpret_cast<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);
-}
-
-extern "C" {
-#endif  // __cplusplus
-
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-#define FXSYS_GetACP GetACP
+#if BUILDFLAG(IS_WIN)
 #define FXSYS_itoa _itoa
-#define FXSYS_WideCharToMultiByte WideCharToMultiByte
-#define FXSYS_MultiByteToWideChar MultiByteToWideChar
 #define FXSYS_strlwr _strlwr
 #define FXSYS_strupr _strupr
 #define FXSYS_stricmp _stricmp
 #define FXSYS_wcsicmp _wcsicmp
 #define FXSYS_wcslwr _wcslwr
 #define FXSYS_wcsupr _wcsupr
-#define FXSYS_pow(a, b) (float)powf(a, b)
 size_t FXSYS_wcsftime(wchar_t* strDest,
                       size_t maxsize,
                       const wchar_t* format,
                       const struct tm* timeptr);
 #define FXSYS_SetLastError SetLastError
 #define FXSYS_GetLastError GetLastError
-#else  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-int FXSYS_GetACP();
+#else  // BUILDFLAG(IS_WIN)
 char* FXSYS_itoa(int value, char* str, int radix);
-int FXSYS_WideCharToMultiByte(uint32_t codepage,
-                              uint32_t dwFlags,
-                              const wchar_t* wstr,
-                              int wlen,
-                              char* buf,
-                              int buflen,
-                              const char* default_str,
-                              int* pUseDefault);
-int FXSYS_MultiByteToWideChar(uint32_t codepage,
-                              uint32_t dwFlags,
-                              const char* bstr,
-                              int blen,
-                              wchar_t* buf,
-                              int buflen);
 char* FXSYS_strlwr(char* str);
 char* FXSYS_strupr(char* str);
 int FXSYS_stricmp(const char* str1, const char* str2);
 int FXSYS_wcsicmp(const wchar_t* str1, const wchar_t* str2);
 wchar_t* FXSYS_wcslwr(wchar_t* str);
 wchar_t* FXSYS_wcsupr(wchar_t* str);
-#define FXSYS_pow(a, b) (float)pow(a, b)
 #define FXSYS_wcsftime wcsftime
 void FXSYS_SetLastError(uint32_t err);
 uint32_t FXSYS_GetLastError();
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#endif  // BUILDFLAG(IS_WIN)
 
-#define FXWORD_GET_LSBFIRST(p)                                \
-  (static_cast<uint16_t>((static_cast<uint16_t>(p[1]) << 8) | \
-                         (static_cast<uint16_t>(p[0]))))
-#define FXWORD_GET_MSBFIRST(p)                                \
-  (static_cast<uint16_t>((static_cast<uint16_t>(p[0]) << 8) | \
-                         (static_cast<uint16_t>(p[1]))))
-#define FXDWORD_GET_LSBFIRST(p)                                                \
-  ((static_cast<uint32_t>(p[3]) << 24) | (static_cast<uint32_t>(p[2]) << 16) | \
-   (static_cast<uint32_t>(p[1]) << 8) | (static_cast<uint32_t>(p[0])))
-#define FXDWORD_GET_MSBFIRST(p)                                                \
-  ((static_cast<uint32_t>(p[0]) << 24) | (static_cast<uint32_t>(p[1]) << 16) | \
-   (static_cast<uint32_t>(p[2]) << 8) | (static_cast<uint32_t>(p[3])))
 int32_t FXSYS_atoi(const char* str);
 uint32_t FXSYS_atoui(const char* str);
 int32_t FXSYS_wtoi(const wchar_t* str);
@@ -189,38 +84,32 @@
 const char* FXSYS_i64toa(int64_t value, char* str, int radix);
 int FXSYS_roundf(float f);
 int FXSYS_round(double d);
-#define FXSYS_sqrt2(a, b) (float)sqrt((a) * (a) + (b) * (b))
+float FXSYS_sqrt2(float a, float b);
+
 #ifdef __cplusplus
-}  // extern C
+}  // extern "C"
+
+// C++-only section
+
+// Could be C, but uses C++-style casting.
+#define FXSYS_UINT16_GET_LSBFIRST(p)                               \
+  (static_cast<uint16_t>(                                          \
+      (static_cast<uint32_t>(static_cast<uint8_t>((p)[1])) << 8) | \
+      (static_cast<uint32_t>(static_cast<uint8_t>((p)[0])))))
+#define FXSYS_UINT16_GET_MSBFIRST(p)                               \
+  (static_cast<uint16_t>(                                          \
+      (static_cast<uint32_t>(static_cast<uint8_t>((p)[0])) << 8) | \
+      (static_cast<uint32_t>(static_cast<uint8_t>((p)[1])))))
+#define FXSYS_UINT32_GET_LSBFIRST(p)                             \
+  ((static_cast<uint32_t>(static_cast<uint8_t>((p)[3])) << 24) | \
+   (static_cast<uint32_t>(static_cast<uint8_t>((p)[2])) << 16) | \
+   (static_cast<uint32_t>(static_cast<uint8_t>((p)[1])) << 8) |  \
+   (static_cast<uint32_t>(static_cast<uint8_t>((p)[0]))))
+#define FXSYS_UINT32_GET_MSBFIRST(p)                             \
+  ((static_cast<uint32_t>(static_cast<uint8_t>((p)[0])) << 24) | \
+   (static_cast<uint32_t>(static_cast<uint8_t>((p)[1])) << 16) | \
+   (static_cast<uint32_t>(static_cast<uint8_t>((p)[2])) << 8) |  \
+   (static_cast<uint32_t>(static_cast<uint8_t>((p)[3]))))
 #endif  // __cplusplus
 
-// To print a size_t value in a portable way:
-//   size_t size;
-//   printf("xyz: %" PRIuS, size);
-// The "u" in the macro corresponds to %u, and S is for "size".
-#if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
-
-#if (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64)
-#error "inttypes.h has already been included before this header file, but "
-#error "without __STDC_FORMAT_MACROS defined."
-#endif
-
-#if !defined(__STDC_FORMAT_MACROS)
-#define __STDC_FORMAT_MACROS
-#endif
-
-#include <inttypes.h>
-
-#if !defined(PRIuS)
-#define PRIuS "zu"
-#endif
-
-#else  // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
-
-#if !defined(PRIuS)
-#define PRIuS "Iu"
-#endif
-
-#endif  // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
-
 #endif  // CORE_FXCRT_FX_SYSTEM_H_
diff --git a/core/fxcrt/fx_system_unittest.cpp b/core/fxcrt/fx_system_unittest.cpp
index bc5dbba..63f1021 100644
--- a/core/fxcrt/fx_system_unittest.cpp
+++ b/core/fxcrt/fx_system_unittest.cpp
@@ -1,7 +1,10 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <math.h>
+
+#include <iterator>
 #include <limits>
 
 #include "build/build_config.h"
@@ -12,7 +15,7 @@
 // Unit test covering cases where PDFium replaces well-known library
 // functionality on any given platformn.
 
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
 
 namespace {
 
@@ -260,7 +263,7 @@
       "111111111111111111111111111111111111111111111111111111111111111");
 }
 
-#endif  // !defined(OS_WIN)
+#endif  // !BUILDFLAG(IS_WIN)
 
 TEST(fxcrt, FXSYS_wcsftime) {
   struct tm good_time = {};
@@ -272,7 +275,7 @@
   good_time.tm_sec = 59;
 
   wchar_t buf[100] = {};
-  EXPECT_EQ(19u, FXSYS_wcsftime(buf, FX_ArraySize(buf), L"%Y-%m-%dT%H:%M:%S",
+  EXPECT_EQ(19u, FXSYS_wcsftime(buf, std::size(buf), L"%Y-%m-%dT%H:%M:%S",
                                 &good_time));
   EXPECT_STREQ(L"1974-08-09T11:59:59", buf);
 
@@ -287,7 +290,7 @@
   for (int year = -2500; year <= 8500; ++year) {
     year_time.tm_year = year;
     wchar_t year_buf[100] = {};
-    FXSYS_wcsftime(year_buf, FX_ArraySize(year_buf), L"%Y-%m-%dT%H:%M:%S",
+    FXSYS_wcsftime(year_buf, std::size(year_buf), L"%Y-%m-%dT%H:%M:%S",
                    &year_time);
   }
 
@@ -300,7 +303,7 @@
   bad_time.tm_min = -1;
   bad_time.tm_sec = -1;
 
-  FXSYS_wcsftime(buf, FX_ArraySize(buf), L"%y-%m-%dT%H:%M:%S", &bad_time);
+  FXSYS_wcsftime(buf, std::size(buf), L"%y-%m-%dT%H:%M:%S", &bad_time);
 
   // Ensure wcsftime handles bad-ish day without crashing (Feb 30).
   struct tm feb_time = {};
@@ -311,7 +314,7 @@
   feb_time.tm_min = 00;
   feb_time.tm_sec = 00;
 
-  FXSYS_wcsftime(buf, FX_ArraySize(buf), L"%y-%m-%dT%H:%M:%S", &feb_time);
+  FXSYS_wcsftime(buf, std::size(buf), L"%y-%m-%dT%H:%M:%S", &feb_time);
 }
 
 TEST(fxcrt, FXSYS_atoi) {
diff --git a/core/fxcrt/fx_types.h b/core/fxcrt/fx_types.h
new file mode 100644
index 0000000..9838773
--- /dev/null
+++ b/core/fxcrt/fx_types.h
@@ -0,0 +1,22 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCRT_FX_TYPES_H_
+#define CORE_FXCRT_FX_TYPES_H_
+
+#include "build/build_config.h"
+
+// PDFium file sizes match the platform. The value must be signed to support -1
+// error returns.
+#if BUILDFLAG(IS_WIN)
+#include <stdint.h>
+#define FX_FILESIZE int64_t
+#else
+#include <sys/types.h>  // For off_t.
+#define FX_FILESIZE off_t
+#endif  // BUILDFLAG(IS_WIN)
+
+#endif  // CORE_FXCRT_FX_TYPES_H_
diff --git a/core/fxcrt/fx_unicode.cpp b/core/fxcrt/fx_unicode.cpp
index cbd4f0d..e3fb97b 100644
--- a/core/fxcrt/fx_unicode.cpp
+++ b/core/fxcrt/fx_unicode.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,11 @@
 
 #include "core/fxcrt/fx_unicode.h"
 
-#include "core/fxcrt/fx_memory.h"
+#include <stddef.h>
+
+#include <iterator>
+
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -25,12 +29,12 @@
   ((mirror << kMirrorBitPos) |           \
    (static_cast<uint16_t>(FX_BIDICLASS::bd) << kBidiClassBitPos)),
 constexpr uint16_t kTextLayoutCodeProperties[] = {
-#include "core/fxcrt/fx_ucddata.inc"
+#include "core/fxcrt/fx_ucddata.inc"  // NOLINT(build/include)
 };
 #undef CHARPROP____
 
 constexpr size_t kTextLayoutCodePropertiesSize =
-    FX_ArraySize(kTextLayoutCodeProperties);
+    std::size(kTextLayoutCodeProperties);
 
 static_assert(kTextLayoutCodePropertiesSize == 65536, "missing characters");
 
@@ -58,12 +62,12 @@
   ((static_cast<uint16_t>(FX_CHARTYPE::ct) << kCharTypeBitPos) | \
    (static_cast<uint16_t>(FX_BREAKPROPERTY::bt) << kBreakTypeBitPos)),
 constexpr uint16_t kExtendedTextLayoutCodeProperties[] = {
-#include "core/fxcrt/fx_ucddata.inc"
+#include "core/fxcrt/fx_ucddata.inc"  // NOLINT(build/include)
 };
 #undef CHARPROP____
 
 constexpr size_t kExtendedTextLayoutCodePropertiesSize =
-    FX_ArraySize(kExtendedTextLayoutCodeProperties);
+    std::size(kExtendedTextLayoutCodeProperties);
 
 static_assert(kExtendedTextLayoutCodePropertiesSize == 65536,
               "missing characters");
@@ -122,46 +126,52 @@
 };
 
 constexpr size_t kFXTextLayoutBidiMirrorSize =
-    FX_ArraySize(kFXTextLayoutBidiMirror);
+    std::size(kFXTextLayoutBidiMirror);
 
 // Check that the mirror indicies in the fx_ucddata.inc table are in bounds.
 #undef CHARPROP____
 #define CHARPROP____(mirror, ct, bd, bt)                                      \
   static_assert(mirror == kMirrorMax || mirror < kFXTextLayoutBidiMirrorSize, \
                 "Bad mirror index");
-#include "core/fxcrt/fx_ucddata.inc"
+#include "core/fxcrt/fx_ucddata.inc"  // NOLINT(build/include)
 #undef CHARPROP____
 
 }  // namespace
 
-wchar_t FX_GetMirrorChar(wchar_t wch) {
+namespace pdfium {
+namespace unicode {
+
+wchar_t GetMirrorChar(wchar_t wch) {
   uint16_t prop = GetUnicodeProperties(wch);
   size_t idx = prop >> kMirrorBitPos;
   if (idx == kMirrorMax)
     return wch;
-  ASSERT(idx < kFXTextLayoutBidiMirrorSize);
+  DCHECK(idx < kFXTextLayoutBidiMirrorSize);
   return kFXTextLayoutBidiMirror[idx];
 }
 
-FX_BIDICLASS FX_GetBidiClass(wchar_t wch) {
+FX_BIDICLASS GetBidiClass(wchar_t wch) {
   uint16_t prop = GetUnicodeProperties(wch);
   uint16_t result = (prop & kBidiClassBitMask) >> kBidiClassBitPos;
-  ASSERT(result <= static_cast<uint16_t>(FX_BIDICLASS::kPDF));
+  DCHECK(result <= static_cast<uint16_t>(FX_BIDICLASS::kPDF));
   return static_cast<FX_BIDICLASS>(result);
 }
 
 #ifdef PDF_ENABLE_XFA
-FX_CHARTYPE FX_GetCharType(wchar_t wch) {
+FX_CHARTYPE GetCharType(wchar_t wch) {
   uint16_t prop = GetExtendedUnicodeProperties(wch);
   uint16_t result = (prop & kCharTypeBitMask) >> kCharTypeBitPos;
-  ASSERT(result <= static_cast<uint16_t>(FX_CHARTYPE::kArabic));
+  DCHECK(result <= static_cast<uint16_t>(FX_CHARTYPE::kArabic));
   return static_cast<FX_CHARTYPE>(result);
 }
 
-FX_BREAKPROPERTY FX_GetBreakProperty(wchar_t wch) {
+FX_BREAKPROPERTY GetBreakProperty(wchar_t wch) {
   uint16_t prop = GetExtendedUnicodeProperties(wch);
   uint16_t result = (prop & kBreakTypeBitMask) >> kBreakTypeBitPos;
-  ASSERT(result <= static_cast<uint16_t>(FX_BREAKPROPERTY::kTB));
+  DCHECK(result <= static_cast<uint16_t>(FX_BREAKPROPERTY::kTB));
   return static_cast<FX_BREAKPROPERTY>(result);
 }
 #endif  // PDF_ENABLE_XFA
+
+}  // namespace unicode
+}  // namespace pdfium
diff --git a/core/fxcrt/fx_unicode.h b/core/fxcrt/fx_unicode.h
index a889304..5ad5ce0 100644
--- a/core/fxcrt/fx_unicode.h
+++ b/core/fxcrt/fx_unicode.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #ifndef CORE_FXCRT_FX_UNICODE_H_
 #define CORE_FXCRT_FX_UNICODE_H_
 
-#include "core/fxcrt/fx_system.h"
+#include <stdint.h>
 
 // NOTE: Order matters, less-than/greater-than comparisons are used.
 enum class FX_BIDICLASS : uint8_t {
@@ -33,9 +33,6 @@
   kN = kON,
 };
 
-wchar_t FX_GetMirrorChar(wchar_t wch);
-FX_BIDICLASS FX_GetBidiClass(wchar_t wch);
-
 #ifdef PDF_ENABLE_XFA
 // As defined in http://www.unicode.org/reports/tr14
 enum class FX_BREAKPROPERTY : uint8_t {
@@ -94,12 +91,29 @@
   kArabicForm,
   kArabic,
 };
+#endif  // PDF_ENABLE_XFA
 
-FX_CHARTYPE FX_GetCharType(wchar_t wch);
+namespace pdfium {
+namespace unicode {
+
+constexpr wchar_t kRightSingleQuotationMark = 0x2019;
+constexpr wchar_t kLineSeparator = 0x2028;
+constexpr wchar_t kParagraphSeparator = 0x2029;
+constexpr wchar_t kBoxDrawingsLightVerical = 0x2502;
+constexpr wchar_t kZeroWidthNoBreakSpace = 0xfeff;
+
+wchar_t GetMirrorChar(wchar_t wch);
+FX_BIDICLASS GetBidiClass(wchar_t wch);
+
+#ifdef PDF_ENABLE_XFA
+FX_CHARTYPE GetCharType(wchar_t wch);
 
 // Analagous to ULineBreak in icu's uchar.h, but permuted order, and a
 // subset lacking some more recent additions.
-FX_BREAKPROPERTY FX_GetBreakProperty(wchar_t wch);
+FX_BREAKPROPERTY GetBreakProperty(wchar_t wch);
 #endif  // PDF_ENABLE_XFA
 
+}  // namespace unicode
+}  // namespace pdfium
+
 #endif  // CORE_FXCRT_FX_UNICODE_H_
diff --git a/core/fxcrt/mask.h b/core/fxcrt/mask.h
new file mode 100644
index 0000000..67e7a3d
--- /dev/null
+++ b/core/fxcrt/mask.h
@@ -0,0 +1,118 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_MASK_H_
+#define CORE_FXCRT_MASK_H_
+
+#include <type_traits>
+
+namespace fxcrt {
+
+// Provides extremely strict type-checking on masks of enum class bitflags,
+// for code where flags may not be passed consistently.
+template <typename E>
+class Mask {
+ public:
+  using UnderlyingType = typename std::underlying_type<E>::type;
+
+  // Escape hatch for when value comes aross an API, say.
+  static Mask FromUnderlyingUnchecked(UnderlyingType val) { return Mask(val); }
+
+  constexpr Mask() = default;
+  constexpr Mask(const Mask& that) = default;
+
+  // NOLINTNEXTLINE(runtime/explicit)
+  constexpr Mask(E val) : val_(static_cast<UnderlyingType>(val)) {}
+
+  // Unfortunately, std::initializer_list<> can't be used in constexpr
+  // methods per C++ standards, and we need constexpr for a zero-cost
+  // abstraction.  Hence, expand out constructors of various arity.
+  constexpr Mask(E v1, E v2)
+      : val_(static_cast<UnderlyingType>(v1) |
+             static_cast<UnderlyingType>(v2)) {}
+
+  constexpr Mask(E v1, E v2, E v3)
+      : val_(static_cast<UnderlyingType>(v1) | static_cast<UnderlyingType>(v2) |
+             static_cast<UnderlyingType>(v3)) {}
+
+  constexpr Mask(E v1, E v2, E v3, E v4)
+      : val_(static_cast<UnderlyingType>(v1) | static_cast<UnderlyingType>(v2) |
+             static_cast<UnderlyingType>(v3) |
+             static_cast<UnderlyingType>(v4)) {}
+
+  constexpr Mask(E v1, E v2, E v3, E v4, E v5)
+      : val_(static_cast<UnderlyingType>(v1) | static_cast<UnderlyingType>(v2) |
+             static_cast<UnderlyingType>(v3) | static_cast<UnderlyingType>(v4) |
+             static_cast<UnderlyingType>(v5)) {}
+
+  constexpr Mask(E v1, E v2, E v3, E v4, E v5, E v6)
+      : val_(static_cast<UnderlyingType>(v1) | static_cast<UnderlyingType>(v2) |
+             static_cast<UnderlyingType>(v3) | static_cast<UnderlyingType>(v4) |
+             static_cast<UnderlyingType>(v5) |
+             static_cast<UnderlyingType>(v6)) {}
+
+  constexpr Mask(E v1, E v2, E v3, E v4, E v5, E v6, E v7)
+      : val_(static_cast<UnderlyingType>(v1) | static_cast<UnderlyingType>(v2) |
+             static_cast<UnderlyingType>(v3) | static_cast<UnderlyingType>(v4) |
+             static_cast<UnderlyingType>(v5) | static_cast<UnderlyingType>(v6) |
+             static_cast<UnderlyingType>(v7)) {}
+
+  constexpr Mask(E v1, E v2, E v3, E v4, E v5, E v6, E v7, E v8)
+      : val_(static_cast<UnderlyingType>(v1) | static_cast<UnderlyingType>(v2) |
+             static_cast<UnderlyingType>(v3) | static_cast<UnderlyingType>(v4) |
+             static_cast<UnderlyingType>(v5) | static_cast<UnderlyingType>(v6) |
+             static_cast<UnderlyingType>(v7) |
+             static_cast<UnderlyingType>(v8)) {}
+
+  explicit operator bool() const { return !!val_; }
+  Mask operator~() const { return Mask(~val_); }
+  constexpr Mask operator|(const Mask& that) const {
+    return Mask(val_ | that.val_);
+  }
+  constexpr Mask operator&(const Mask& that) const {
+    return Mask(val_ & that.val_);
+  }
+  constexpr Mask operator^(const Mask& that) const {
+    return Mask(val_ ^ that.val_);
+  }
+  Mask& operator=(const Mask& that) {
+    val_ = that.val_;
+    return *this;
+  }
+  Mask& operator|=(const Mask& that) {
+    val_ |= that.val_;
+    return *this;
+  }
+  Mask& operator&=(const Mask& that) {
+    val_ &= that.val_;
+    return *this;
+  }
+  Mask& operator^=(const Mask& that) {
+    val_ ^= that.val_;
+    return *this;
+  }
+  bool operator==(const Mask& that) const { return val_ == that.val_; }
+  bool operator!=(const Mask& that) const { return val_ != that.val_; }
+
+  bool TestAll(const Mask& that) const {
+    return (val_ & that.val_) == that.val_;
+  }
+
+  // Because ~ can't be applied to enum class without casting.
+  void Clear(const Mask& that) { val_ &= ~that.val_; }
+
+  // Escape hatch, usage should be minimized.
+  UnderlyingType UncheckedValue() const { return val_; }
+
+ private:
+  explicit constexpr Mask(UnderlyingType val) : val_(val) {}
+
+  UnderlyingType val_ = 0;
+};
+
+}  // namespace fxcrt
+
+using fxcrt::Mask;
+
+#endif  // CORE_FXCRT_MASK_H_
diff --git a/core/fxcrt/mask_unittest.cpp b/core/fxcrt/mask_unittest.cpp
new file mode 100644
index 0000000..be4e492
--- /dev/null
+++ b/core/fxcrt/mask_unittest.cpp
@@ -0,0 +1,170 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/mask.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace fxcrt {
+namespace {
+
+enum class Privilege : uint8_t {
+  kPriv1 = 1 << 0,
+  kPriv2 = 1 << 1,
+  kPriv4 = 1 << 2,
+  kPriv8 = 1 << 3,
+  kPriv16 = 1 << 4,
+  kPriv32 = 1 << 5,
+  kPriv64 = 1 << 6,
+  kPriv128 = 1 << 7,
+};
+
+constexpr Mask<Privilege> kAllMask = {
+    Privilege::kPriv1,  Privilege::kPriv2,   Privilege::kPriv4,
+    Privilege::kPriv8,  Privilege::kPriv16,  Privilege::kPriv32,
+    Privilege::kPriv64, Privilege::kPriv128,
+};
+
+}  // namespace
+
+static_assert(sizeof(Mask<Privilege>) == sizeof(Privilege),
+              "Mask size must be the same as enum");
+
+TEST(Mask, Empty) {
+  constexpr Mask<Privilege> privs;
+  EXPECT_EQ(0u, privs.UncheckedValue());
+  EXPECT_FALSE(privs & Privilege::kPriv1);
+  EXPECT_FALSE(privs & Privilege::kPriv4);
+  EXPECT_FALSE(privs & Privilege::kPriv8);
+  EXPECT_FALSE(privs & kAllMask);
+}
+
+TEST(Mask, FromOne) {
+  Mask<Privilege> privs = Privilege::kPriv1;
+  EXPECT_EQ(1u, privs.UncheckedValue());
+  EXPECT_TRUE(privs & Privilege::kPriv1);
+  EXPECT_FALSE(privs & Privilege::kPriv4);
+  EXPECT_FALSE(privs & Privilege::kPriv8);
+  EXPECT_TRUE(privs & kAllMask);
+}
+
+TEST(Mask, FromTwo) {
+  // Not adjacent bits, just because.
+  Mask<Privilege> privs = {Privilege::kPriv1, Privilege::kPriv8};
+  EXPECT_EQ(9u, privs.UncheckedValue());
+  EXPECT_TRUE(privs & Privilege::kPriv1);
+  EXPECT_FALSE(privs & Privilege::kPriv4);
+  EXPECT_TRUE(privs & Privilege::kPriv8);
+  EXPECT_TRUE(privs & kAllMask);
+}
+
+TEST(Mask, FromThree) {
+  Mask<Privilege> privs = {
+      Privilege::kPriv1,
+      Privilege::kPriv2,
+      Privilege::kPriv4,
+  };
+  EXPECT_EQ(7u, privs.UncheckedValue());
+}
+
+TEST(Mask, FromFour) {
+  Mask<Privilege> privs = {
+      Privilege::kPriv1,
+      Privilege::kPriv2,
+      Privilege::kPriv4,
+      Privilege::kPriv8,
+  };
+  EXPECT_EQ(15u, privs.UncheckedValue());
+}
+
+TEST(Mask, FromFive) {
+  Mask<Privilege> privs = {
+      Privilege::kPriv1, Privilege::kPriv2,  Privilege::kPriv4,
+      Privilege::kPriv8, Privilege::kPriv16,
+  };
+  EXPECT_EQ(31u, privs.UncheckedValue());
+}
+
+TEST(Mask, FromSix) {
+  Mask<Privilege> privs = {
+      Privilege::kPriv1, Privilege::kPriv2,  Privilege::kPriv4,
+      Privilege::kPriv8, Privilege::kPriv16, Privilege::kPriv32,
+  };
+  EXPECT_EQ(63u, privs.UncheckedValue());
+}
+
+TEST(Mask, FromSeven) {
+  Mask<Privilege> privs = {
+      Privilege::kPriv1,  Privilege::kPriv2,  Privilege::kPriv4,
+      Privilege::kPriv8,  Privilege::kPriv16, Privilege::kPriv32,
+      Privilege::kPriv64,
+  };
+  EXPECT_EQ(127u, privs.UncheckedValue());
+}
+
+TEST(Mask, FromEight) {
+  Mask<Privilege> privs = {
+      Privilege::kPriv1,  Privilege::kPriv2,   Privilege::kPriv4,
+      Privilege::kPriv8,  Privilege::kPriv16,  Privilege::kPriv32,
+      Privilege::kPriv64, Privilege::kPriv128,
+  };
+  EXPECT_EQ(255u, privs.UncheckedValue());
+}
+
+TEST(Mask, FromUnderlying) {
+  auto privs = Mask<Privilege>::FromUnderlyingUnchecked(5);
+  EXPECT_EQ(5u, privs.UncheckedValue());
+  EXPECT_TRUE(privs & Privilege::kPriv1);
+  EXPECT_TRUE(privs & Privilege::kPriv4);
+  EXPECT_FALSE(privs & Privilege::kPriv8);
+}
+
+TEST(Mask, AssignAndEQ) {
+  Mask<Privilege> source = {Privilege::kPriv1, Privilege::kPriv8};
+  Mask<Privilege> other = Privilege::kPriv1;
+  Mask<Privilege> dest;
+  dest = source;
+  EXPECT_EQ(9u, dest.UncheckedValue());
+  EXPECT_EQ(source, dest);
+  EXPECT_NE(other, dest);
+}
+
+TEST(Mask, OrAndAnd) {
+  Mask<Privilege> source = {Privilege::kPriv1, Privilege::kPriv8};
+  Mask<Privilege> or_result =
+      source | Mask<Privilege>{Privilege::kPriv1, Privilege::kPriv4};
+  Mask<Privilege> and_result =
+      source & Mask<Privilege>{Privilege::kPriv1, Privilege::kPriv4};
+  EXPECT_EQ(13u, or_result.UncheckedValue());
+  EXPECT_EQ(1u, and_result.UncheckedValue());
+}
+
+TEST(Mask, OrEqualsAndAndEquals) {
+  Mask<Privilege> source_or = {Privilege::kPriv1, Privilege::kPriv8};
+  Mask<Privilege> source_and = {Privilege::kPriv1, Privilege::kPriv8};
+  source_or |= {Privilege::kPriv1, Privilege::kPriv4};
+  source_and &= {Privilege::kPriv1, Privilege::kPriv4};
+  EXPECT_EQ(13u, source_or.UncheckedValue());
+  EXPECT_EQ(1u, source_and.UncheckedValue());
+}
+
+TEST(Mask, Clear) {
+  Mask<Privilege> source = kAllMask;
+  source.Clear({Privilege::kPriv1, Privilege::kPriv4});
+  EXPECT_EQ(250u, source.UncheckedValue());
+}
+
+TEST(Mask, TestAll) {
+  Mask<Privilege> source = {
+      Privilege::kPriv1,
+      Privilege::kPriv8,
+      Privilege::kPriv64,
+  };
+  Mask<Privilege> passes = {Privilege::kPriv1, Privilege::kPriv64};
+  Mask<Privilege> fails = {Privilege::kPriv1, Privilege::kPriv32};
+  EXPECT_TRUE(source.TestAll(passes));
+  EXPECT_FALSE(source.TestAll(fails));
+}
+
+}  // namespace fxcrt
diff --git a/core/fxcrt/maybe_owned.h b/core/fxcrt/maybe_owned.h
index bbdcd40..c599d39 100644
--- a/core/fxcrt/maybe_owned.h
+++ b/core/fxcrt/maybe_owned.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,8 +8,8 @@
 #include <memory>
 #include <utility>
 
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
 
 namespace fxcrt {
 
@@ -20,87 +20,83 @@
 template <typename T, typename D = std::default_delete<T>>
 class MaybeOwned {
  public:
+  using OwnedType = std::unique_ptr<T, D>;
+  using UnownedType = UnownedPtr<T>;
+
   MaybeOwned() = default;
-  explicit MaybeOwned(T* ptr) : m_pObj(ptr) {}
-  explicit MaybeOwned(const UnownedPtr<T>& ptr) : m_pObj(ptr.Get()) {}
-  explicit MaybeOwned(std::unique_ptr<T, D> ptr)
-      : m_pOwnedObj(std::move(ptr)), m_pObj(m_pOwnedObj.get()) {}
+  explicit MaybeOwned(T* ptr) : ptr_(UnownedType(ptr)) {}
+  explicit MaybeOwned(const UnownedType& ptr) : ptr_(ptr) {}
+  explicit MaybeOwned(OwnedType ptr) : ptr_(std::move(ptr)) {}
 
   MaybeOwned(const MaybeOwned& that) = delete;
-  MaybeOwned(MaybeOwned&& that) noexcept
-      : m_pOwnedObj(that.m_pOwnedObj.release()), m_pObj(that.m_pObj) {
-    that.m_pObj = nullptr;
-  }
+  MaybeOwned(MaybeOwned&& that) noexcept = default;
 
-  void Reset(std::unique_ptr<T, D> ptr) {
-    m_pObj = ptr.get();
-    m_pOwnedObj = std::move(ptr);
-  }
-  void Reset(T* ptr = nullptr) {
-    m_pObj = ptr;
-    m_pOwnedObj.reset();
-  }
+  MaybeOwned& operator=(const MaybeOwned& that) = delete;
+  MaybeOwned& operator=(MaybeOwned&& that) noexcept = default;
+
+  ~MaybeOwned() = default;
+
+  void Reset(T* ptr = nullptr) { ptr_ = UnownedType(ptr); }
+  void Reset(OwnedType ptr) { ptr_ = std::move(ptr); }
+
+  bool IsOwned() const { return absl::holds_alternative<OwnedType>(ptr_); }
+
   // Helpful for untangling a collection of intertwined MaybeOwned<>.
   void ResetIfUnowned() {
     if (!IsOwned())
       Reset();
   }
 
-  T* Get() const { return m_pObj.Get(); }
-  bool IsOwned() const { return !!m_pOwnedObj; }
+  T* Get() const& {
+    return absl::visit([](const auto& obj) { return obj.get(); }, ptr_);
+  }
+  T* Get() && {
+    auto local_variable_preventing_move_elision = std::move(ptr_);
+    return absl::visit([](const auto& obj) { return obj.get(); },
+                       local_variable_preventing_move_elision);
+  }
 
   // Downgrades to unowned, caller takes ownership.
-  std::unique_ptr<T, D> Release() {
-    ASSERT(IsOwned());
-    return std::move(m_pOwnedObj);
+  OwnedType Release() {
+    auto result = std::move(absl::get<OwnedType>(ptr_));
+    ptr_ = UnownedType(result.get());
+    return result;
   }
 
   // Downgrades to empty, caller takes ownership.
-  std::unique_ptr<T, D> ReleaseAndClear() {
-    ASSERT(IsOwned());
-    m_pObj = nullptr;
-    return std::move(m_pOwnedObj);
+  OwnedType ReleaseAndClear() {
+    auto result = std::move(absl::get<OwnedType>(ptr_));
+    ptr_ = UnownedType();
+    return result;
   }
 
-  MaybeOwned& operator=(const MaybeOwned& that) = delete;
-  MaybeOwned& operator=(MaybeOwned&& that) {
-    m_pObj = that.m_pObj;
-    m_pOwnedObj = std::move(that.m_pOwnedObj);
-    that.m_pObj = nullptr;
-    return *this;
-  }
   MaybeOwned& operator=(T* ptr) {
     Reset(ptr);
     return *this;
   }
-  MaybeOwned& operator=(const UnownedPtr<T>& ptr) {
-    Reset(ptr.Get());
+  MaybeOwned& operator=(const UnownedType& ptr) {
+    Reset(ptr);
     return *this;
   }
-  MaybeOwned& operator=(std::unique_ptr<T, D> ptr) {
+  MaybeOwned& operator=(OwnedType ptr) {
     Reset(std::move(ptr));
     return *this;
   }
 
   bool operator==(const MaybeOwned& that) const { return Get() == that.Get(); }
-  bool operator==(const std::unique_ptr<T, D>& ptr) const {
-    return Get() == ptr.get();
-  }
+  bool operator==(const OwnedType& ptr) const { return Get() == ptr.get(); }
   bool operator==(T* ptr) const { return Get() == ptr; }
 
   bool operator!=(const MaybeOwned& that) const { return !(*this == that); }
-  bool operator!=(const std::unique_ptr<T, D> ptr) const {
-    return !(*this == ptr);
-  }
+  bool operator!=(const OwnedType ptr) const { return !(*this == ptr); }
   bool operator!=(T* ptr) const { return !(*this == ptr); }
 
-  explicit operator bool() const { return !!m_pObj; }
-  T& operator*() const { return *m_pObj; }
-  T* operator->() const { return m_pObj.Get(); }
+  explicit operator bool() const { return !!Get(); }
+  T& operator*() const { return *Get(); }
+  T* operator->() const { return Get(); }
 
  private:
-  std::unique_ptr<T, D> m_pOwnedObj;
-  UnownedPtr<T> m_pObj;
+  absl::variant<UnownedType, OwnedType> ptr_;
 };
 
 }  // namespace fxcrt
diff --git a/core/fxcrt/maybe_owned_unittest.cpp b/core/fxcrt/maybe_owned_unittest.cpp
index 53cab6c..1785cf1 100644
--- a/core/fxcrt/maybe_owned_unittest.cpp
+++ b/core/fxcrt/maybe_owned_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,6 @@
 
 #include "core/fxcrt/unowned_ptr.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
 namespace fxcrt {
 namespace {
@@ -32,7 +31,7 @@
   MaybeOwned<PseudoDeletable> ptr1;
   EXPECT_FALSE(ptr1.IsOwned());
   EXPECT_FALSE(ptr1);
-  EXPECT_EQ(nullptr, ptr1.Get());
+  EXPECT_FALSE(ptr1.Get());
 
   MaybeOwned<PseudoDeletable> ptr2;
   EXPECT_TRUE(ptr1 == ptr2);
@@ -72,7 +71,7 @@
   {
     MaybeOwned<PseudoDeletable> ptr(&thing1);
     EXPECT_EQ(100, ptr->GetID());
-    ptr = pdfium::MakeUnique<PseudoDeletable>(300, &owned_delete_count);
+    ptr = std::make_unique<PseudoDeletable>(300, &owned_delete_count);
     EXPECT_TRUE(ptr.IsOwned());
     EXPECT_EQ(300, ptr->GetID());
   }
@@ -99,7 +98,7 @@
   int delete_count = 0;
   {
     MaybeOwned<PseudoDeletable> ptr(
-        pdfium::MakeUnique<PseudoDeletable>(100, &delete_count));
+        std::make_unique<PseudoDeletable>(100, &delete_count));
     EXPECT_TRUE(ptr.IsOwned());
     EXPECT_EQ(100, ptr->GetID());
 
@@ -112,8 +111,8 @@
   delete_count = 0;
   {
     MaybeOwned<PseudoDeletable> ptr(
-        pdfium::MakeUnique<PseudoDeletable>(200, &delete_count));
-    ptr = pdfium::MakeUnique<PseudoDeletable>(300, &delete_count);
+        std::make_unique<PseudoDeletable>(200, &delete_count));
+    ptr = std::make_unique<PseudoDeletable>(300, &delete_count);
     EXPECT_TRUE(ptr.IsOwned());
     EXPECT_EQ(300, ptr->GetID());
     EXPECT_EQ(1, delete_count);
@@ -125,7 +124,7 @@
   PseudoDeletable thing2(400, &unowned_delete_count);
   {
     MaybeOwned<PseudoDeletable> ptr(
-        pdfium::MakeUnique<PseudoDeletable>(500, &delete_count));
+        std::make_unique<PseudoDeletable>(500, &delete_count));
     ptr = &thing2;
     EXPECT_FALSE(ptr.IsOwned());
     EXPECT_EQ(400, ptr->GetID());
@@ -142,7 +141,7 @@
     std::unique_ptr<PseudoDeletable> stolen;
     {
       MaybeOwned<PseudoDeletable> ptr(
-          pdfium::MakeUnique<PseudoDeletable>(100, &delete_count));
+          std::make_unique<PseudoDeletable>(100, &delete_count));
       EXPECT_TRUE(ptr.IsOwned());
       stolen = ptr.Release();
       EXPECT_FALSE(ptr.IsOwned());
@@ -160,19 +159,19 @@
   {
     MaybeOwned<PseudoDeletable> ptr1(&thing1);
     MaybeOwned<PseudoDeletable> ptr2(
-        pdfium::MakeUnique<PseudoDeletable>(200, &delete_count));
+        std::make_unique<PseudoDeletable>(200, &delete_count));
     EXPECT_FALSE(ptr1.IsOwned());
     EXPECT_TRUE(ptr2.IsOwned());
 
     MaybeOwned<PseudoDeletable> ptr3(std::move(ptr1));
     MaybeOwned<PseudoDeletable> ptr4(std::move(ptr2));
-    EXPECT_FALSE(ptr1.IsOwned());
-    EXPECT_FALSE(ptr2.IsOwned());
+    EXPECT_FALSE(ptr1.IsOwned());  // Unowned and null.
+    EXPECT_FALSE(ptr1.Get());
+    EXPECT_TRUE(ptr2.IsOwned());  // Owned but null.
+    EXPECT_FALSE(ptr2.Get());
     EXPECT_FALSE(ptr3.IsOwned());
     EXPECT_TRUE(ptr4.IsOwned());
     EXPECT_EQ(0, delete_count);
-    EXPECT_EQ(nullptr, ptr1.Get());
-    EXPECT_EQ(nullptr, ptr2.Get());
     EXPECT_EQ(100, ptr3->GetID());
     EXPECT_EQ(200, ptr4->GetID());
 
@@ -180,17 +179,55 @@
     MaybeOwned<PseudoDeletable> ptr6;
     ptr5 = std::move(ptr3);
     ptr6 = std::move(ptr4);
-    EXPECT_FALSE(ptr3.IsOwned());
-    EXPECT_FALSE(ptr4.IsOwned());
+    EXPECT_FALSE(ptr3.IsOwned());  // Unowned and null.
+    EXPECT_FALSE(ptr3.Get());
+    EXPECT_TRUE(ptr4.IsOwned());  // Owned but null.
+    EXPECT_FALSE(ptr4.Get());
     EXPECT_FALSE(ptr5.IsOwned());
     EXPECT_TRUE(ptr6.IsOwned());
     EXPECT_EQ(0, delete_count);
-    EXPECT_EQ(nullptr, ptr3.Get());
-    EXPECT_EQ(nullptr, ptr4.Get());
     EXPECT_EQ(100, ptr5->GetID());
     EXPECT_EQ(200, ptr6->GetID());
   }
   EXPECT_EQ(1, delete_count);
 }
 
+namespace {
+
+class Thing {
+ public:
+  int x = 42;
+};
+
+class Owner {
+ public:
+  explicit Owner(std::unique_ptr<Thing> thing) : thing_(std::move(thing)) {}
+
+ private:
+  std::unique_ptr<Thing> thing_;
+};
+
+class Manager {
+ public:
+  Manager()
+      : transient_(std::make_unique<Thing>()),
+        owner_(std::make_unique<Owner>(transient_.Release())),
+        thing_(std::move(transient_).Get()) {}
+
+  bool has_transient() const { return !!transient_.Get(); }
+
+ private:
+  MaybeOwned<Thing> transient_;         // For initializng next two members.
+  const std::unique_ptr<Owner> owner_;  // Must outlive thing_.
+  const UnownedPtr<Thing> thing_;
+};
+
+}  // namespace
+
+TEST(MaybeOwned, MoveElisionThwarted) {
+  // Test fails if the std::move() in Manager::Manager() is elided.
+  Manager manager;
+  EXPECT_FALSE(manager.has_transient());
+}
+
 }  // namespace fxcrt
diff --git a/core/fxcrt/observed_ptr.cpp b/core/fxcrt/observed_ptr.cpp
index f192e69..611624a 100644
--- a/core/fxcrt/observed_ptr.cpp
+++ b/core/fxcrt/observed_ptr.cpp
@@ -1,9 +1,12 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxcrt/observed_ptr.h"
 
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+
 namespace fxcrt {
 
 Observable::Observable() = default;
@@ -12,4 +15,20 @@
   NotifyObservers();
 }
 
+void Observable::AddObserver(ObserverIface* pObserver) {
+  DCHECK(!pdfium::Contains(m_Observers, pObserver));
+  m_Observers.insert(pObserver);
+}
+
+void Observable::RemoveObserver(ObserverIface* pObserver) {
+  DCHECK(pdfium::Contains(m_Observers, pObserver));
+  m_Observers.erase(pObserver);
+}
+
+void Observable::NotifyObservers() {
+  for (auto* pObserver : m_Observers)
+    pObserver->OnObservableDestroyed();
+  m_Observers.clear();
+}
+
 }  // namespace fxcrt
diff --git a/core/fxcrt/observed_ptr.h b/core/fxcrt/observed_ptr.h
index 9568705..05b03d8 100644
--- a/core/fxcrt/observed_ptr.h
+++ b/core/fxcrt/observed_ptr.h
@@ -1,14 +1,16 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef CORE_FXCRT_OBSERVED_PTR_H_
 #define CORE_FXCRT_OBSERVED_PTR_H_
 
+#include <stddef.h>
+
 #include <set>
 
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
+#include "third_party/base/check.h"
 
 namespace fxcrt {
 
@@ -23,21 +25,12 @@
 
   Observable();
   Observable(const Observable& that) = delete;
-  ~Observable();
-  void AddObserver(ObserverIface* pObserver) {
-    ASSERT(!pdfium::ContainsKey(m_Observers, pObserver));
-    m_Observers.insert(pObserver);
-  }
-  void RemoveObserver(ObserverIface* pObserver) {
-    ASSERT(pdfium::ContainsKey(m_Observers, pObserver));
-    m_Observers.erase(pObserver);
-  }
-  void NotifyObservers() {
-    for (auto* pObserver : m_Observers)
-      pObserver->OnObservableDestroyed();
-    m_Observers.clear();
-  }
   Observable& operator=(const Observable& that) = delete;
+  ~Observable();
+
+  void AddObserver(ObserverIface* pObserver);
+  void RemoveObserver(ObserverIface* pObserver);
+  void NotifyObservers();
 
  protected:
   size_t ActiveObserversForTesting() const { return m_Observers.size(); }
@@ -47,6 +40,8 @@
 };
 
 // Simple case of a self-nulling pointer.
+// Generally, pass ObservedPtr<> by non-const reference since this saves
+// considerable work compared to pass by value.
 template <typename T>
 class ObservedPtr final : public Observable::ObserverIface {
  public:
@@ -68,7 +63,7 @@
       m_pObservable->AddObserver(this);
   }
   void OnObservableDestroyed() override {
-    ASSERT(m_pObservable);
+    DCHECK(m_pObservable);
     m_pObservable = nullptr;
   }
   bool HasObservable() const { return !!m_pObservable; }
@@ -97,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/observed_ptr_unittest.cpp b/core/fxcrt/observed_ptr_unittest.cpp
index 81e114f..c748f14 100644
--- a/core/fxcrt/observed_ptr_unittest.cpp
+++ b/core/fxcrt/observed_ptr_unittest.cpp
@@ -1,14 +1,14 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxcrt/observed_ptr.h"
 
+#include <memory>
 #include <utility>
 #include <vector>
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
 namespace fxcrt {
 namespace {
@@ -28,25 +28,25 @@
 
 TEST(ObservePtr, Null) {
   ObservedPtr<PseudoObservable> ptr;
-  EXPECT_EQ(nullptr, ptr.Get());
+  EXPECT_FALSE(ptr.Get());
 }
 
 TEST(ObservePtr, LivesLonger) {
   ObservedPtr<PseudoObservable> ptr;
   {
-    auto pObs = pdfium::MakeUnique<PseudoObservable>();
+    auto pObs = std::make_unique<PseudoObservable>();
     ptr.Reset(pObs.get());
-    EXPECT_NE(nullptr, ptr.Get());
+    EXPECT_TRUE(ptr.Get());
     EXPECT_EQ(1u, pObs->ActiveObservedPtrs());
   }
-  EXPECT_EQ(nullptr, ptr.Get());
+  EXPECT_FALSE(ptr.Get());
 }
 
 TEST(ObservePtr, LivesShorter) {
   PseudoObservable obs;
   {
     ObservedPtr<PseudoObservable> ptr(&obs);
-    EXPECT_NE(nullptr, ptr.Get());
+    EXPECT_TRUE(ptr.Get());
     EXPECT_EQ(1u, obs.ActiveObservedPtrs());
   }
   EXPECT_EQ(0u, obs.ActiveObservedPtrs());
@@ -56,11 +56,11 @@
   PseudoObservable obs;
   {
     ObservedPtr<PseudoObservable> ptr(&obs);
-    EXPECT_NE(nullptr, ptr.Get());
+    EXPECT_TRUE(ptr.Get());
     EXPECT_EQ(1u, obs.ActiveObservedPtrs());
     {
       ObservedPtr<PseudoObservable> ptr2(ptr);
-      EXPECT_NE(nullptr, ptr2.Get());
+      EXPECT_TRUE(ptr2.Get());
       EXPECT_EQ(2u, obs.ActiveObservedPtrs());
     }
     EXPECT_EQ(1u, obs.ActiveObservedPtrs());
@@ -72,12 +72,12 @@
   PseudoObservable obs;
   {
     ObservedPtr<PseudoObservable> ptr(&obs);
-    EXPECT_NE(nullptr, ptr.Get());
+    EXPECT_TRUE(ptr.Get());
     EXPECT_EQ(1u, obs.ActiveObservedPtrs());
     {
       ObservedPtr<PseudoObservable> ptr2;
       ptr2 = ptr;
-      EXPECT_NE(nullptr, ptr2.Get());
+      EXPECT_TRUE(ptr2.Get());
       EXPECT_EQ(2u, obs.ActiveObservedPtrs());
     }
     EXPECT_EQ(1u, obs.ActiveObservedPtrs());
@@ -92,12 +92,12 @@
     std::vector<ObservedPtr<PseudoObservable>> vec2;
     vec1.emplace_back(&obs);
     vec1.emplace_back(&obs);
-    EXPECT_NE(nullptr, vec1[0].Get());
-    EXPECT_NE(nullptr, vec1[1].Get());
+    EXPECT_TRUE(vec1[0].Get());
+    EXPECT_TRUE(vec1[1].Get());
     EXPECT_EQ(2u, obs.ActiveObservedPtrs());
     vec2 = vec1;
-    EXPECT_NE(nullptr, vec2[0].Get());
-    EXPECT_NE(nullptr, vec2[1].Get());
+    EXPECT_TRUE(vec2[0].Get());
+    EXPECT_TRUE(vec2[1].Get());
     EXPECT_EQ(4u, obs.ActiveObservedPtrs());
     vec1.clear();
     EXPECT_EQ(2u, obs.ActiveObservedPtrs());
@@ -115,12 +115,12 @@
     PseudoObservable obs;
     vec1.emplace_back(&obs);
     vec1.emplace_back(&obs);
-    EXPECT_NE(nullptr, vec1[0].Get());
-    EXPECT_NE(nullptr, vec1[1].Get());
+    EXPECT_TRUE(vec1[0].Get());
+    EXPECT_TRUE(vec1[1].Get());
     EXPECT_EQ(2u, obs.ActiveObservedPtrs());
   }
-  EXPECT_EQ(nullptr, vec1[0].Get());
-  EXPECT_EQ(nullptr, vec1[1].Get());
+  EXPECT_FALSE(vec1[0].Get());
+  EXPECT_FALSE(vec1[1].Get());
 }
 
 TEST(ObservePtr, ResetNull) {
@@ -216,7 +216,7 @@
     EXPECT_EQ(&thing2, thing1.m_pOther.Get());
     EXPECT_EQ(&thing1, thing2.m_pOther.Get());
   }
-  EXPECT_EQ(nullptr, thing1.m_pOther.Get());
+  EXPECT_FALSE(thing1.m_pOther.Get());
   // Must be no ASAN violations upon cleanup here.
 }
 
diff --git a/core/fxcrt/pauseindicator_iface.h b/core/fxcrt/pauseindicator_iface.h
index d5d856d..6aa7d0e 100644
--- a/core/fxcrt/pauseindicator_iface.h
+++ b/core/fxcrt/pauseindicator_iface.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxcrt/pdfium_span_unittest.cpp b/core/fxcrt/pdfium_span_unittest.cpp
index 6778bbb..e9da064 100644
--- a/core/fxcrt/pdfium_span_unittest.cpp
+++ b/core/fxcrt/pdfium_span_unittest.cpp
@@ -1,29 +1,71 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <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
 
 TEST(PdfiumSpan, EmptySpan) {
   int stuff[] = {1, 2, 3};
+  pdfium::span<int> null_span;
   pdfium::span<int> stuff_span(stuff);
   pdfium::span<int> empty_first_span = stuff_span.first(0);
   pdfium::span<int> empty_last_span = stuff_span.last(0);
   pdfium::span<int> empty_sub_span1 = stuff_span.subspan(0, 0);
   pdfium::span<int> empty_sub_span2 = stuff_span.subspan(3, 0);
+  EXPECT_TRUE(null_span.empty());
   EXPECT_TRUE(empty_first_span.empty());
   EXPECT_TRUE(empty_last_span.empty());
   EXPECT_TRUE(empty_sub_span1.empty());
   EXPECT_TRUE(empty_sub_span2.empty());
 }
 
-TEST(PdfiumSpan, EmptySpanDeath) {
+// Custom implementation of first()/last().
+TEST(PdfiumSpan, FirstLast) {
+  int one[] = {1};
   int stuff[] = {1, 2, 3};
+  pdfium::span<int> one_span(one);
   pdfium::span<int> stuff_span(stuff);
-  pdfium::span<int> empty_span = stuff_span.last(0);
+  EXPECT_EQ(one_span.front(), 1);
+  EXPECT_EQ(one_span.back(), 1);
+  EXPECT_EQ(stuff_span.front(), 1);
+  EXPECT_EQ(stuff_span.back(), 3);
+}
+
+TEST(PdfiumSpanDeathTest, EmptySpanIndex) {
+  pdfium::span<int> empty_span;
   EXPECT_DEATH(empty_span[0] += 1, ".*");
 }
+
+TEST(PdfiumSpanDeathTest, EmptySpanFront) {
+  pdfium::span<int> empty_span;
+  EXPECT_DEATH(empty_span.front() += 1, ".*");
+}
+
+TEST(PdfiumSpanDeathTest, EmptySpanBack) {
+  pdfium::span<int> empty_span;
+  EXPECT_DEATH(empty_span.back() += 1, ".*");
+}
+
+#if defined(ADDRESS_SANITIZER)
+namespace {
+
+void CreateDanglingSpan() {
+  pdfium::span<int> data_span;
+  {
+    std::vector<int> data(4);
+    data_span = pdfium::make_span(data);
+  }
+}
+
+}  // namespace
+
+TEST(PdfiumSpanDeathTest, DanglingReference) {
+  EXPECT_DEATH(CreateDanglingSpan(), ".*");
+}
+#endif  // defined(ADDRESS_SANITIZER)
diff --git a/core/fxcrt/retain_ptr.h b/core/fxcrt/retain_ptr.h
index 916a12e..26223b6 100644
--- a/core/fxcrt/retain_ptr.h
+++ b/core/fxcrt/retain_ptr.h
@@ -1,16 +1,20 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef CORE_FXCRT_RETAIN_PTR_H_
 #define CORE_FXCRT_RETAIN_PTR_H_
 
+#include <stdint.h>
+
 #include <functional>
 #include <memory>
+#include <type_traits>
 #include <utility>
 
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/check.h"
+#include "third_party/base/compiler_specific.h"
 
 namespace fxcrt {
 
@@ -22,29 +26,91 @@
 
 // Analogous to base's scoped_refptr.
 template <class T>
-class RetainPtr {
+class TRIVIAL_ABI RetainPtr {
  public:
-  explicit RetainPtr(T* pObj) : m_pObj(pObj) {
-    if (m_pObj)
-      m_pObj->Retain();
-  }
-
-  RetainPtr() = default;
-  RetainPtr(const RetainPtr& that) : RetainPtr(that.Get()) {}
-
-  // Move-construct a RetainPtr. After construction, |that| will be NULL.
-  RetainPtr(RetainPtr&& that) noexcept { Swap(that); }
+  RetainPtr() noexcept = default;
 
   // Deliberately implicit to allow returning nullptrs.
   // NOLINTNEXTLINE(runtime/explicit)
   RetainPtr(std::nullptr_t ptr) {}
 
-  template <class U>
+  explicit RetainPtr(T* pObj) noexcept : m_pObj(pObj) {
+    if (m_pObj)
+      m_pObj->Retain();
+  }
+
+  // Copy-construct a RetainPtr.
+  // Required in addition to copy conversion constructor below.
+  RetainPtr(const RetainPtr& that) noexcept : RetainPtr(that.Get()) {}
+
+  // Move-construct a RetainPtr. After construction, |that| will be NULL.
+  // Required in addition to move conversion constructor below.
+  RetainPtr(RetainPtr&& that) noexcept { Unleak(that.Leak()); }
+
+  // Copy conversion constructor.
+  template <class U,
+            typename = typename std::enable_if<
+                std::is_convertible<U*, T*>::value>::type>
   RetainPtr(const RetainPtr<U>& that) : RetainPtr(that.Get()) {}
 
+  // Move-conversion constructor.
+  template <class U,
+            typename = typename std::enable_if<
+                std::is_convertible<U*, T*>::value>::type>
+  RetainPtr(RetainPtr<U>&& that) noexcept {
+    Unleak(that.Leak());
+  }
+
+  // Assign a RetainPtr from nullptr;
+  RetainPtr& operator=(std::nullptr_t) noexcept {
+    Reset();
+    return *this;
+  }
+
+  // Copy-assign a RetainPtr.
+  // Required in addition to copy conversion assignment below.
+  RetainPtr& operator=(const RetainPtr& that) {
+    if (*this != that)
+      Reset(that.Get());
+    return *this;
+  }
+
+  // Move-assign a RetainPtr. After assignment, |that| will be NULL.
+  // Required in addition to move conversion assignment below.
+  RetainPtr& operator=(RetainPtr&& that) noexcept {
+    Unleak(that.Leak());
+    return *this;
+  }
+
+  // Copy-convert assign a RetainPtr.
+  template <class U,
+            typename = typename std::enable_if<
+                std::is_convertible<U*, T*>::value>::type>
+  RetainPtr& operator=(const RetainPtr<U>& that) {
+    if (*this != that)
+      Reset(that.Get());
+    return *this;
+  }
+
+  // Move-convert assign a RetainPtr. After assignment, |that| will be NULL.
+  template <class U,
+            typename = typename std::enable_if<
+                std::is_convertible<U*, T*>::value>::type>
+  RetainPtr& operator=(RetainPtr<U>&& that) noexcept {
+    Unleak(that.Leak());
+    return *this;
+  }
+
+  ~RetainPtr() = default;
+
+  template <class U>
+  U* AsRaw() const {
+    return static_cast<U*>(Get());
+  }
+
   template <class U>
   RetainPtr<U> As() const {
-    return RetainPtr<U>(static_cast<U*>(Get()));
+    return RetainPtr<U>(AsRaw<U>());
   }
 
   void Reset(T* obj = nullptr) {
@@ -53,7 +119,9 @@
     m_pObj.reset(obj);
   }
 
-  T* Get() const { return m_pObj.get(); }
+  operator T*() const noexcept { return Get(); }
+  T* Get() const noexcept { return m_pObj.get(); }
+
   UnownedPtr<T> BackPointer() const { return UnownedPtr<T>(Get()); }
   void Swap(RetainPtr& that) { m_pObj.swap(that.m_pObj); }
 
@@ -61,21 +129,6 @@
   T* Leak() { return m_pObj.release(); }
   void Unleak(T* ptr) { m_pObj.reset(ptr); }
 
-  RetainPtr& operator=(const RetainPtr& that) {
-    if (*this != that)
-      Reset(that.Get());
-    return *this;
-  }
-
-  // Move-assign a RetainPtr. After assignment, |that| will be NULL.
-  RetainPtr& operator=(RetainPtr&& that) {
-    m_pObj.reset(that.Leak());
-    return *this;
-  }
-
-  // Assigment from raw pointers is intentially not provided to make
-  // reference count churn more visible where possible.
-
   bool operator==(const RetainPtr& that) const { return Get() == that.Get(); }
   bool operator!=(const RetainPtr& that) const { return !(*this == that); }
 
@@ -121,26 +174,25 @@
   Retainable(const Retainable& that) = delete;
   Retainable& operator=(const Retainable& that) = delete;
 
-  void Retain() const { ++m_nRefCount; }
+  // These need to be const methods operating on a mutable member so that
+  // RetainPtr<const T> can be used for an object that is otherwise const
+  // apart from the internal ref-counting.
+  void Retain() const {
+    ++m_nRefCount;
+    CHECK(m_nRefCount > 0);
+  }
   void Release() const {
-    ASSERT(m_nRefCount > 0);
+    CHECK(m_nRefCount > 0);
     if (--m_nRefCount == 0)
       delete this;
   }
 
-  mutable intptr_t m_nRefCount = 0;
+  mutable uintptr_t m_nRefCount = 0;
+  static_assert(std::is_unsigned<decltype(m_nRefCount)>::value,
+                "m_nRefCount must be an unsigned type for overflow check"
+                "to work properly in Retain()");
 };
 
-template <typename T, typename U>
-inline bool operator==(const U* lhs, const RetainPtr<T>& rhs) {
-  return rhs == lhs;
-}
-
-template <typename T, typename U>
-inline bool operator!=(const U* lhs, const RetainPtr<T>& rhs) {
-  return rhs != lhs;
-}
-
 }  // namespace fxcrt
 
 using fxcrt::ReleaseDeleter;
@@ -149,16 +201,17 @@
 
 namespace pdfium {
 
-// Helper to make a RetainPtr along the lines of std::make_unique<>(),
-// or pdfium::MakeUnique<>(). Arguments are forwarded to T's constructor.
-// Classes managed by RetainPtr should have protected (or private)
-// constructors, and should friend this function.
+// Helper to make a RetainPtr along the lines of std::make_unique<>().
+// Arguments are forwarded to T's constructor. Classes managed by RetainPtr
+// should have protected (or private) constructors, and should friend this
+// function.
 template <typename T, typename... Args>
 RetainPtr<T> MakeRetain(Args&&... args) {
   return RetainPtr<T>(new T(std::forward<Args>(args)...));
 }
 
-// Type-deducing wrapper to make a RetainPtr from an ordinary pointer.
+// Type-deducing wrapper to make a RetainPtr from an ordinary pointer,
+// since equivalent constructor is explicit.
 template <typename T>
 RetainPtr<T> WrapRetain(T* that) {
   return RetainPtr<T>(that);
@@ -166,4 +219,10 @@
 
 }  // namespace pdfium
 
+// Macro to allow construction via MakeRetain<>() only, when used
+// with a private constructor in a class.
+#define CONSTRUCT_VIA_MAKE_RETAIN         \
+  template <typename T, typename... Args> \
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args)
+
 #endif  // CORE_FXCRT_RETAIN_PTR_H_
diff --git a/core/fxcrt/retain_ptr_unittest.cpp b/core/fxcrt/retain_ptr_unittest.cpp
index 47277df..b381e78 100644
--- a/core/fxcrt/retain_ptr_unittest.cpp
+++ b/core/fxcrt/retain_ptr_unittest.cpp
@@ -1,23 +1,52 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxcrt/retain_ptr.h"
 
+#include <functional>
+#include <set>
 #include <utility>
 #include <vector>
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/pseudo_retainable.h"
+#include "third_party/base/containers/contains.h"
+
+namespace {
+
+template <typename T, typename C = std::less<T>>
+class NoLinearSearchSet : public std::set<T, C> {
+ public:
+  typename std::set<T, C>::iterator begin() noexcept = delete;
+  typename std::set<T, C>::const_iterator cbegin() const noexcept = delete;
+};
+
+}  // namespace
 
 namespace fxcrt {
+namespace {
 
-TEST(RetainPtr, Null) {
+template <typename T, typename C = std::less<T>>
+class NoLinearSearchSet : public std::set<T, C> {
+ public:
+  typename std::set<T, C>::iterator begin() noexcept = delete;
+  typename std::set<T, C>::const_iterator cbegin() const noexcept = delete;
+};
+
+}  // namespace
+
+TEST(RetainPtr, DefaultCtor) {
   RetainPtr<PseudoRetainable> ptr;
-  EXPECT_EQ(nullptr, ptr.Get());
+  EXPECT_FALSE(ptr.Get());
 }
 
-TEST(RetainPtr, Normal) {
+TEST(RetainPtr, NullptrCtor) {
+  RetainPtr<PseudoRetainable> ptr(nullptr);
+  EXPECT_FALSE(ptr.Get());
+}
+
+TEST(RetainPtr, RawCtor) {
   PseudoRetainable obj;
   {
     RetainPtr<PseudoRetainable> ptr(&obj);
@@ -51,7 +80,7 @@
     RetainPtr<PseudoRetainable> ptr1(&obj);
     {
       RetainPtr<PseudoRetainable> ptr2(std::move(ptr1));
-      EXPECT_EQ(nullptr, ptr1.Get());
+      EXPECT_FALSE(ptr1.Get());
       EXPECT_EQ(&obj, ptr2.Get());
       EXPECT_EQ(1, obj.retain_count());
       EXPECT_EQ(0, obj.release_count());
@@ -63,6 +92,151 @@
   EXPECT_EQ(1, obj.release_count());
 }
 
+TEST(RetainPtr, CopyConversionCtor) {
+  PseudoRetainable obj;
+  {
+    RetainPtr<PseudoRetainable> ptr1(&obj);
+    {
+      RetainPtr<const PseudoRetainable> ptr2(ptr1);
+      EXPECT_EQ(2, obj.retain_count());
+      EXPECT_EQ(0, obj.release_count());
+    }
+    EXPECT_EQ(2, obj.retain_count());
+    EXPECT_EQ(1, obj.release_count());
+  }
+  EXPECT_EQ(2, obj.retain_count());
+  EXPECT_EQ(2, obj.release_count());
+}
+
+TEST(RetainPtr, MoveConversionCtor) {
+  PseudoRetainable obj;
+  {
+    RetainPtr<PseudoRetainable> ptr1(&obj);
+    {
+      RetainPtr<const PseudoRetainable> ptr2(std::move(ptr1));
+      EXPECT_FALSE(ptr1.Get());
+      EXPECT_EQ(&obj, ptr2.Get());
+      EXPECT_EQ(1, obj.retain_count());
+      EXPECT_EQ(0, obj.release_count());
+    }
+    EXPECT_EQ(1, obj.retain_count());
+    EXPECT_EQ(1, obj.release_count());
+  }
+  EXPECT_EQ(1, obj.retain_count());
+  EXPECT_EQ(1, obj.release_count());
+}
+
+TEST(RetainPtr, NullptrAssign) {
+  PseudoRetainable obj;
+  RetainPtr<PseudoRetainable> ptr(&obj);
+  ptr = nullptr;
+  EXPECT_FALSE(ptr);
+}
+
+TEST(RetainPtr, RawAssign) {
+  PseudoRetainable obj;
+  RetainPtr<PseudoRetainable> ptr;
+  ptr = pdfium::WrapRetain(&obj);
+  EXPECT_EQ(&obj, ptr);
+}
+
+TEST(RetainPtr, CopyAssign) {
+  PseudoRetainable obj;
+  {
+    RetainPtr<PseudoRetainable> ptr(&obj);
+    {
+      RetainPtr<PseudoRetainable> ptr2;
+      ptr2 = ptr;
+      EXPECT_EQ(2, obj.retain_count());
+      EXPECT_EQ(0, obj.release_count());
+    }
+    {
+      // Test assignment from wrapped underlying type.
+      RetainPtr<PseudoRetainable> ptr2;
+      ptr2 = pdfium::WrapRetain(ptr.Get());
+      EXPECT_EQ(3, obj.retain_count());
+      EXPECT_EQ(1, obj.release_count());
+    }
+    EXPECT_EQ(3, obj.retain_count());
+    EXPECT_EQ(2, obj.release_count());
+  }
+  EXPECT_EQ(3, obj.retain_count());
+  EXPECT_EQ(3, obj.release_count());
+}
+
+TEST(RetainPtr, MoveAssign) {
+  PseudoRetainable obj;
+  {
+    RetainPtr<PseudoRetainable> ptr1(&obj);
+    {
+      RetainPtr<PseudoRetainable> ptr2;
+      EXPECT_EQ(&obj, ptr1.Get());
+      EXPECT_FALSE(ptr2.Get());
+      ptr2 = std::move(ptr1);
+      EXPECT_FALSE(ptr1.Get());
+      EXPECT_EQ(&obj, ptr2.Get());
+      EXPECT_EQ(1, obj.retain_count());
+      EXPECT_EQ(0, obj.release_count());
+    }
+    EXPECT_EQ(1, obj.retain_count());
+    EXPECT_EQ(1, obj.release_count());
+  }
+  EXPECT_EQ(1, obj.retain_count());
+  EXPECT_EQ(1, obj.release_count());
+}
+
+TEST(RetainPtr, CopyConvertAssign) {
+  PseudoRetainable obj;
+  {
+    RetainPtr<PseudoRetainable> ptr(&obj);
+    {
+      RetainPtr<const PseudoRetainable> ptr2;
+      ptr2 = ptr;
+      EXPECT_EQ(2, obj.retain_count());
+      EXPECT_EQ(0, obj.release_count());
+    }
+    {
+      // Test assignment from wrapped underlying type.
+      RetainPtr<const PseudoRetainable> ptr2;
+      ptr2 = pdfium::WrapRetain(ptr.Get());
+      EXPECT_EQ(3, obj.retain_count());
+      EXPECT_EQ(1, obj.release_count());
+    }
+    EXPECT_EQ(3, obj.retain_count());
+    EXPECT_EQ(2, obj.release_count());
+  }
+  EXPECT_EQ(3, obj.retain_count());
+  EXPECT_EQ(3, obj.release_count());
+}
+
+TEST(RetainPtr, MoveConvertAssign) {
+  PseudoRetainable obj;
+  {
+    RetainPtr<PseudoRetainable> ptr1(&obj);
+    {
+      RetainPtr<const PseudoRetainable> ptr2;
+      ptr2 = std::move(ptr1);
+      EXPECT_FALSE(ptr1);
+      EXPECT_EQ(&obj, ptr2.Get());
+      EXPECT_EQ(1, obj.retain_count());
+      EXPECT_EQ(0, obj.release_count());
+    }
+    EXPECT_EQ(1, obj.retain_count());
+    EXPECT_EQ(1, obj.release_count());
+  }
+  EXPECT_EQ(1, obj.retain_count());
+  EXPECT_EQ(1, obj.release_count());
+}
+
+TEST(RetainPtr, AmbiguousExpression) {
+  class A : public Retainable {};
+  class B : public A {};
+
+  // Test passes if it compiles without error.
+  RetainPtr<A> var = (0) ? pdfium::MakeRetain<A>() : pdfium::MakeRetain<B>();
+  EXPECT_TRUE(var);
+}
+
 TEST(RetainPtr, ResetNull) {
   PseudoRetainable obj;
   {
@@ -156,44 +330,6 @@
   EXPECT_EQ(1, obj1.release_count());
 }
 
-TEST(RetainPtr, Assign) {
-  PseudoRetainable obj;
-  {
-    RetainPtr<PseudoRetainable> ptr(&obj);
-    {
-      RetainPtr<PseudoRetainable> ptr2;
-      ptr2 = ptr;
-      EXPECT_EQ(2, obj.retain_count());
-      EXPECT_EQ(0, obj.release_count());
-    }
-    EXPECT_EQ(2, obj.retain_count());
-    EXPECT_EQ(1, obj.release_count());
-  }
-  EXPECT_EQ(2, obj.retain_count());
-  EXPECT_EQ(2, obj.release_count());
-}
-
-TEST(RetainPtr, MoveAssign) {
-  PseudoRetainable obj;
-  {
-    RetainPtr<PseudoRetainable> ptr1(&obj);
-    {
-      RetainPtr<PseudoRetainable> ptr2;
-      EXPECT_EQ(&obj, ptr1.Get());
-      EXPECT_EQ(nullptr, ptr2.Get());
-      ptr2 = std::move(ptr1);
-      EXPECT_EQ(nullptr, ptr1.Get());
-      EXPECT_EQ(&obj, ptr2.Get());
-      EXPECT_EQ(1, obj.retain_count());
-      EXPECT_EQ(0, obj.release_count());
-    }
-    EXPECT_EQ(1, obj.retain_count());
-    EXPECT_EQ(1, obj.release_count());
-  }
-  EXPECT_EQ(1, obj.retain_count());
-  EXPECT_EQ(1, obj.release_count());
-}
-
 TEST(RetainPtr, Equals) {
   PseudoRetainable obj1;
   PseudoRetainable obj2;
@@ -309,4 +445,79 @@
   EXPECT_EQ(1, obj.release_count());
 }
 
+TEST(RetainPtr, SetContains) {
+  // Makes sure pdfium::Contains() works the same way with raw pointers and
+  // RetainPtrs for containers that use find().
+  PseudoRetainable obj1;
+  PseudoRetainable obj2;
+  RetainPtr<PseudoRetainable> ptr1(&obj1);
+  RetainPtr<PseudoRetainable> ptr2(&obj2);
+  RetainPtr<const PseudoRetainable> const_ptr1(&obj1);
+  RetainPtr<const PseudoRetainable> const_ptr2(&obj2);
+  NoLinearSearchSet<RetainPtr<const PseudoRetainable>, std::less<>> the_set;
+
+  // Intially, two smart pointers to each object.
+  EXPECT_EQ(2, obj1.retain_count());
+  EXPECT_EQ(0, obj1.release_count());
+  EXPECT_EQ(2, obj2.retain_count());
+  EXPECT_EQ(0, obj2.release_count());
+
+  // Passed by const-ref, increment on copy into set's data structure.
+  the_set.insert(const_ptr1);
+  EXPECT_EQ(3, obj1.retain_count());
+  EXPECT_EQ(0, obj1.release_count());
+  EXPECT_EQ(2, obj2.retain_count());
+  EXPECT_EQ(0, obj2.release_count());
+
+  // None of the following should cause any churn.
+  EXPECT_NE(the_set.end(), the_set.find(&obj1));
+  EXPECT_EQ(the_set.end(), the_set.find(&obj2));
+  EXPECT_TRUE(pdfium::Contains(the_set, &obj1));
+  EXPECT_FALSE(pdfium::Contains(the_set, &obj2));
+  EXPECT_EQ(3, obj1.retain_count());
+  EXPECT_EQ(0, obj1.release_count());
+  EXPECT_EQ(2, obj2.retain_count());
+  EXPECT_EQ(0, obj2.release_count());
+
+  EXPECT_NE(the_set.end(), the_set.find(const_ptr1));
+  EXPECT_EQ(the_set.end(), the_set.find(const_ptr2));
+  EXPECT_TRUE(pdfium::Contains(the_set, const_ptr1));
+  EXPECT_FALSE(pdfium::Contains(the_set, const_ptr2));
+  EXPECT_EQ(3, obj1.retain_count());
+  EXPECT_EQ(0, obj1.release_count());
+  EXPECT_EQ(2, obj2.retain_count());
+  EXPECT_EQ(0, obj2.release_count());
+
+  // These involve const-removing conversions which seem to churn.
+  EXPECT_NE(the_set.end(), the_set.find(ptr1));
+  EXPECT_EQ(the_set.end(), the_set.find(ptr2));
+  EXPECT_TRUE(pdfium::Contains(the_set, ptr1));
+  EXPECT_FALSE(pdfium::Contains(the_set, ptr2));
+  EXPECT_EQ(5, obj1.retain_count());
+  EXPECT_EQ(2, obj1.release_count());
+  EXPECT_EQ(4, obj2.retain_count());
+  EXPECT_EQ(2, obj2.release_count());
+}
+
+TEST(RetainPtr, VectorContains) {
+  // Makes sure pdfium::Contains() works the same way with raw pointers and
+  // RetainPtrs. for containers that use begin()/end().
+  PseudoRetainable obj1;
+  PseudoRetainable obj2;
+  std::vector<const PseudoRetainable*> vec;
+  vec.push_back(&obj1);
+  EXPECT_TRUE(pdfium::Contains(vec, &obj1));
+  EXPECT_FALSE(pdfium::Contains(vec, &obj2));
+
+  RetainPtr<PseudoRetainable> ptr1(&obj1);
+  RetainPtr<PseudoRetainable> ptr2(&obj2);
+  EXPECT_TRUE(pdfium::Contains(vec, ptr1));
+  EXPECT_FALSE(pdfium::Contains(vec, ptr2));
+
+  RetainPtr<const PseudoRetainable> const_ptr1(&obj1);
+  RetainPtr<const PseudoRetainable> const_ptr2(&obj2);
+  EXPECT_TRUE(pdfium::Contains(vec, const_ptr1));
+  EXPECT_FALSE(pdfium::Contains(vec, const_ptr2));
+}
+
 }  // namespace fxcrt
diff --git a/core/fxcrt/retained_tree_node.h b/core/fxcrt/retained_tree_node.h
deleted file mode 100644
index a859ae5..0000000
--- a/core/fxcrt/retained_tree_node.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CORE_FXCRT_RETAINED_TREE_NODE_H_
-#define CORE_FXCRT_RETAINED_TREE_NODE_H_
-
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/tree_node.h"
-#include "third_party/base/logging.h"
-
-namespace fxcrt {
-
-// For DOM/XML-ish trees, where references outside the tree are RetainPtr<T>,
-// and the parent node also "retains" its children but doesn't always have
-// a direct pointer to them.
-template <typename T>
-class RetainedTreeNode : public TreeNode<T> {
- public:
-  template <typename U, typename... Args>
-  friend RetainPtr<U> pdfium::MakeRetain(Args&&... args);
-
-  void AppendFirstChild(const RetainPtr<T>& child) {
-    TreeNode<T>::AppendFirstChild(child.Get());
-  }
-
-  void AppendLastChild(const RetainPtr<T>& child) {
-    TreeNode<T>::AppendLastChild(child.Get());
-  }
-
-  void InsertBefore(const RetainPtr<T>& child, T* other) {
-    TreeNode<T>::InsertBefore(child.Get(), other);
-  }
-
-  void InsertAfter(const RetainPtr<T>& child, T* other) {
-    TreeNode<T>::InsertAfter(child.Get(), other);
-  }
-
-  void RemoveChild(const RetainPtr<T>& child) {
-    TreeNode<T>::RemoveChild(child.Get());
-  }
-
-  void RemoveSelfIfParented() {
-    if (T* parent = TreeNode<T>::GetParent()) {
-      parent->TreeNode<T>::RemoveChild(
-          pdfium::WrapRetain(static_cast<T*>(this)).Get());
-    }
-  }
-
- protected:
-  RetainedTreeNode() = default;
-  ~RetainedTreeNode() override {
-    while (auto* pChild = TreeNode<T>::GetFirstChild())
-      RemoveChild(pdfium::WrapRetain(pChild));
-  }
-
- private:
-  template <typename U>
-  friend struct ReleaseDeleter;
-
-  template <typename U>
-  friend class RetainPtr;
-
-  RetainedTreeNode(const RetainedTreeNode& that) = delete;
-  RetainedTreeNode& operator=(const RetainedTreeNode& that) = delete;
-
-  void Retain() { ++m_nRefCount; }
-  void Release() {
-    ASSERT(m_nRefCount > 0);
-    if (--m_nRefCount == 0 && !TreeNode<T>::GetParent())
-      delete this;
-  }
-
-  intptr_t m_nRefCount = 0;
-};
-
-}  // namespace fxcrt
-
-using fxcrt::RetainedTreeNode;
-
-#endif  // CORE_FXCRT_RETAINED_TREE_NODE_H_
diff --git a/core/fxcrt/retained_tree_node_unittest.cpp b/core/fxcrt/retained_tree_node_unittest.cpp
deleted file mode 100644
index d469ab3..0000000
--- a/core/fxcrt/retained_tree_node_unittest.cpp
+++ /dev/null
@@ -1,188 +0,0 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "core/fxcrt/retained_tree_node.h"
-
-#include "core/fxcrt/observed_ptr.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace fxcrt {
-namespace {
-
-class ObservableRetainedTreeNodeForTest
-    : public RetainedTreeNode<ObservableRetainedTreeNodeForTest>,
-      public Observable {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
- private:
-  ObservableRetainedTreeNodeForTest() = default;
-};
-
-void AddClutterToFront(
-    const RetainPtr<ObservableRetainedTreeNodeForTest>& parent) {
-  for (int i = 0; i < 4; ++i) {
-    parent->AppendFirstChild(
-        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>());
-  }
-}
-
-void AddClutterToBack(
-    const RetainPtr<ObservableRetainedTreeNodeForTest>& parent) {
-  for (int i = 0; i < 4; ++i) {
-    parent->AppendLastChild(
-        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>());
-  }
-}
-
-}  // namespace
-
-TEST(RetainedTreeNode, NoParent) {
-  ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
-  {
-    RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
-        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
-    EXPECT_FALSE(ptr->HasChild(ptr.Get()));
-    watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
-    EXPECT_TRUE(watcher.Get());
-  }
-  EXPECT_FALSE(watcher.Get());
-}
-
-TEST(RetainedTreeNode, FirstHasParent) {
-  ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
-  RetainPtr<ObservableRetainedTreeNodeForTest> parent =
-      pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
-  {
-    RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
-        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
-    watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
-    parent->AppendFirstChild(ptr);
-    EXPECT_FALSE(parent->HasChild(parent.Get()));
-    EXPECT_TRUE(parent->HasChild(ptr.Get()));
-    EXPECT_TRUE(watcher.Get());
-  }
-  EXPECT_TRUE(watcher.Get());
-  parent->RemoveChild(pdfium::WrapRetain(watcher.Get()));
-  EXPECT_FALSE(watcher.Get());
-  // Now add some clutter.
-  {
-    RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
-        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
-    watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
-    parent->AppendFirstChild(ptr);
-    AddClutterToFront(parent);
-    AddClutterToBack(parent);
-    EXPECT_TRUE(watcher.Get());
-  }
-  EXPECT_TRUE(watcher.Get());
-  parent->RemoveChild(pdfium::WrapRetain(watcher.Get()));
-  EXPECT_FALSE(watcher.Get());
-}
-
-TEST(RetainedTreeNode, LastHasParent) {
-  ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
-  RetainPtr<ObservableRetainedTreeNodeForTest> parent =
-      pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
-  {
-    RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
-        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
-    watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
-    parent->AppendLastChild(ptr);
-    EXPECT_FALSE(parent->HasChild(parent.Get()));
-    EXPECT_TRUE(parent->HasChild(ptr.Get()));
-    EXPECT_TRUE(watcher.Get());
-  }
-  {
-    // Now add some clutter.
-    RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
-        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
-    watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
-    parent->AppendLastChild(ptr);
-    AddClutterToFront(parent);
-    AddClutterToBack(parent);
-    EXPECT_TRUE(watcher.Get());
-  }
-  EXPECT_TRUE(watcher.Get());
-  parent->RemoveChild(pdfium::WrapRetain(watcher.Get()));
-  EXPECT_FALSE(watcher.Get());
-}
-
-TEST(RetainedTreeNode, GrandChildCleanedUp) {
-  ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
-  RetainPtr<ObservableRetainedTreeNodeForTest> grandparent =
-      pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
-  {
-    RetainPtr<ObservableRetainedTreeNodeForTest> parent =
-        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
-    grandparent->AppendFirstChild(parent);
-    {
-      RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
-          pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
-      watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
-      parent->AppendFirstChild(ptr);
-      EXPECT_TRUE(watcher.Get());
-    }
-    grandparent->RemoveChild(parent);
-    EXPECT_TRUE(watcher.Get());
-  }
-  EXPECT_FALSE(watcher.Get());
-}
-
-TEST(RetainedTreeNode, RemoveSelf) {
-  ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
-  auto parent = pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
-  {
-    auto child = pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
-    watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(child.Get());
-    parent->AppendFirstChild(child);
-  }
-  EXPECT_TRUE(watcher.Get());
-  watcher->RemoveSelfIfParented();
-  EXPECT_FALSE(watcher.Get());
-}
-
-TEST(RetainedTreeNode, InsertBeforeAfter) {
-  ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
-  RetainPtr<ObservableRetainedTreeNodeForTest> parent =
-      pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
-
-  AddClutterToFront(parent);
-  {
-    RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
-        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
-    watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
-    parent->AppendFirstChild(ptr);
-    parent->InsertBefore(pdfium::WrapRetain(parent->GetFirstChild()),
-                         parent->GetLastChild());
-    parent->InsertAfter(pdfium::WrapRetain(parent->GetLastChild()),
-                        parent->GetFirstChild());
-    EXPECT_TRUE(watcher.Get());
-  }
-  parent->RemoveChild(pdfium::WrapRetain(watcher.Get()));
-  EXPECT_FALSE(watcher.Get());
-}
-
-TEST(RetainedTreeNode, Traversal) {
-  RetainPtr<ObservableRetainedTreeNodeForTest> parent =
-      pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
-
-  AddClutterToFront(parent);
-  int count = 0;
-  for (ObservableRetainedTreeNodeForTest* pNode = parent->GetFirstChild();
-       pNode; pNode = pNode->GetNextSibling()) {
-    ++count;
-  }
-  EXPECT_EQ(4, count);
-  count = 0;
-  for (ObservableRetainedTreeNodeForTest* pNode = parent->GetLastChild(); pNode;
-       pNode = pNode->GetPrevSibling()) {
-    ++count;
-  }
-  EXPECT_EQ(4, count);
-}
-
-}  // namespace fxcrt
diff --git a/core/fxcrt/scoped_set_insertion.h b/core/fxcrt/scoped_set_insertion.h
new file mode 100644
index 0000000..d930982
--- /dev/null
+++ b/core/fxcrt/scoped_set_insertion.h
@@ -0,0 +1,41 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_SCOPED_SET_INSERTION_H_
+#define CORE_FXCRT_SCOPED_SET_INSERTION_H_
+
+#include <set>
+#include <utility>
+
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/check.h"
+
+namespace fxcrt {
+
+// Track the addition of an object to a set, removing it automatically when
+// the ScopedSetInsertion goes out of scope.
+template <typename T>
+class ScopedSetInsertion {
+ public:
+  FX_STACK_ALLOCATED();
+
+  ScopedSetInsertion(std::set<T>* org_set, const T& elem)
+      : set_(org_set), insert_results_(set_->insert(elem)) {
+    CHECK(insert_results_.second);
+  }
+  ScopedSetInsertion(const ScopedSetInsertion&) = delete;
+  ScopedSetInsertion& operator=(const ScopedSetInsertion&) = delete;
+  ~ScopedSetInsertion() { set_->erase(insert_results_.first); }
+
+ private:
+  UnownedPtr<std::set<T>> const set_;
+  const std::pair<typename std::set<T>::iterator, bool> insert_results_;
+};
+
+}  // namespace fxcrt
+
+using fxcrt::ScopedSetInsertion;
+
+#endif  // CORE_FXCRT_SCOPED_SET_INSERTION_H_
diff --git a/core/fxcrt/scoped_set_insertion_unittest.cpp b/core/fxcrt/scoped_set_insertion_unittest.cpp
new file mode 100644
index 0000000..6a6b990
--- /dev/null
+++ b/core/fxcrt/scoped_set_insertion_unittest.cpp
@@ -0,0 +1,24 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/scoped_set_insertion.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(fxcrt, ScopedSetInsertion) {
+  std::set<int> container;
+  {
+    ScopedSetInsertion<int> insertion(&container, 5);
+    EXPECT_THAT(container, testing::UnorderedElementsAreArray({5}));
+
+    {
+      ScopedSetInsertion<int> insertion2(&container, 6);
+      EXPECT_THAT(container, testing::UnorderedElementsAreArray({5, 6}));
+    }
+
+    EXPECT_THAT(container, testing::UnorderedElementsAreArray({5}));
+  }
+  EXPECT_TRUE(container.empty());
+}
diff --git a/core/fxcrt/shared_copy_on_write.h b/core/fxcrt/shared_copy_on_write.h
index a672ddf..8466965 100644
--- a/core/fxcrt/shared_copy_on_write.h
+++ b/core/fxcrt/shared_copy_on_write.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -20,10 +20,10 @@
 template <class ObjClass>
 class SharedCopyOnWrite {
  public:
-  SharedCopyOnWrite() {}
+  SharedCopyOnWrite() = default;
   SharedCopyOnWrite(const SharedCopyOnWrite& other)
       : m_pObject(other.m_pObject) {}
-  ~SharedCopyOnWrite() {}
+  ~SharedCopyOnWrite() = default;
 
   template <typename... Args>
   ObjClass* Emplace(Args... params) {
diff --git a/core/fxcrt/shared_copy_on_write_unittest.cpp b/core/fxcrt/shared_copy_on_write_unittest.cpp
index f579358..2e8c9e8 100644
--- a/core/fxcrt/shared_copy_on_write_unittest.cpp
+++ b/core/fxcrt/shared_copy_on_write_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -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_;
 };
@@ -53,7 +55,7 @@
   Observer observer;
   {
     SharedCopyOnWrite<Object> ptr;
-    EXPECT_EQ(nullptr, ptr.GetObject());
+    EXPECT_FALSE(ptr.GetObject());
   }
 }
 
@@ -112,16 +114,16 @@
   Observer observer;
   {
     SharedCopyOnWrite<Object> ptr;
-    EXPECT_NE(nullptr, ptr.GetPrivateCopy(&observer, std::string("one")));
+    EXPECT_TRUE(ptr.GetPrivateCopy(&observer, std::string("one")));
     EXPECT_EQ(1, observer.GetConstructionCount("one"));
     EXPECT_EQ(0, observer.GetDestructionCount("one"));
 
-    EXPECT_NE(nullptr, ptr.GetPrivateCopy(&observer, std::string("one")));
+    EXPECT_TRUE(ptr.GetPrivateCopy(&observer, std::string("one")));
     EXPECT_EQ(1, observer.GetConstructionCount("one"));
     EXPECT_EQ(0, observer.GetDestructionCount("one"));
     {
       SharedCopyOnWrite<Object> other(ptr);
-      EXPECT_NE(nullptr, ptr.GetPrivateCopy(&observer, std::string("one")));
+      EXPECT_TRUE(ptr.GetPrivateCopy(&observer, std::string("one")));
       EXPECT_EQ(2, observer.GetConstructionCount("one"));
       EXPECT_EQ(0, observer.GetDestructionCount("one"));
     }
diff --git a/core/fxcrt/small_buffer.h b/core/fxcrt/small_buffer.h
new file mode 100644
index 0000000..a0fd5c2
--- /dev/null
+++ b/core/fxcrt/small_buffer.h
@@ -0,0 +1,49 @@
+// Copyright 2017 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCRT_SMALL_BUFFER_H_
+#define CORE_FXCRT_SMALL_BUFFER_H_
+
+#include <string.h>
+
+#include <array>
+#include <memory>
+
+#include "core/fxcrt/fx_memory_wrappers.h"
+
+namespace fxcrt {
+
+template <class T, size_t FixedSize>
+class SmallBuffer {
+ public:
+  explicit SmallBuffer(size_t actual_size) : m_pSize(actual_size) {
+    if (actual_size > FixedSize) {
+      m_pDynamicData.reset(FX_Alloc(T, actual_size));
+      return;
+    }
+    if (actual_size)
+      memset(m_FixedData.data(), 0, sizeof(T) * actual_size);
+  }
+  size_t size() const { return m_pSize; }
+  T* data() {
+    return m_pDynamicData ? m_pDynamicData.get() : m_FixedData.data();
+  }
+  T* begin() { return data(); }
+  T* end() { return begin() + size(); }
+
+  // Callers shouldn't need to know these details.
+  T* fixed_for_test() { return m_FixedData.data(); }
+  T* dynamic_for_test() { return m_pDynamicData.get(); }
+
+ private:
+  const size_t m_pSize;
+  std::unique_ptr<T, FxFreeDeleter> m_pDynamicData;
+  std::array<T, FixedSize> m_FixedData;
+};
+
+}  // namespace fxcrt
+
+#endif  // CORE_FXCRT_SMALL_BUFFER_H_
diff --git a/core/fxcrt/small_buffer_unittest.cpp b/core/fxcrt/small_buffer_unittest.cpp
new file mode 100644
index 0000000..cbf4bdf
--- /dev/null
+++ b/core/fxcrt/small_buffer_unittest.cpp
@@ -0,0 +1,56 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/small_buffer.h"
+
+#include <algorithm>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace fxcrt {
+
+TEST(SmallBuffer, Empty) {
+  SmallBuffer<int, 4> buffer(0);
+  EXPECT_EQ(buffer.data(), buffer.fixed_for_test());
+  EXPECT_EQ(buffer.begin(), buffer.end());
+}
+
+TEST(SmallBuffer, NoFixed) {
+  SmallBuffer<int, 0> buffer(4);
+  EXPECT_EQ(buffer.data(), buffer.dynamic_for_test());
+  std::fill(buffer.begin(), buffer.end(), 42);
+  int* ptr = buffer.data();
+  EXPECT_EQ(42, ptr[0]);
+  EXPECT_EQ(42, ptr[1]);
+  EXPECT_EQ(42, ptr[2]);
+  EXPECT_EQ(42, ptr[3]);
+}
+
+TEST(SmallBuffer, NoFixedEmpty) {
+  SmallBuffer<int, 0> buffer(0);
+  EXPECT_EQ(buffer.data(), buffer.fixed_for_test());
+  EXPECT_EQ(buffer.begin(), buffer.end());
+}
+
+TEST(SmallBuffer, Fixed) {
+  SmallBuffer<int, 4> buffer(2);
+  EXPECT_EQ(buffer.data(), buffer.fixed_for_test());
+  std::fill(buffer.begin(), buffer.end(), 42);
+  int* ptr = buffer.data();
+  EXPECT_EQ(42, ptr[0]);
+  EXPECT_EQ(42, ptr[1]);
+}
+
+TEST(SmallBuffer, Dynamic) {
+  SmallBuffer<int, 2> buffer(4);
+  EXPECT_EQ(buffer.data(), buffer.dynamic_for_test());
+  std::fill(buffer.begin(), buffer.end(), 42);
+  int* ptr = buffer.data();
+  EXPECT_EQ(42, ptr[0]);
+  EXPECT_EQ(42, ptr[1]);
+  EXPECT_EQ(42, ptr[2]);
+  EXPECT_EQ(42, ptr[3]);
+}
+
+}  // namespace fxcrt
diff --git a/core/fxcrt/span_util.h b/core/fxcrt/span_util.h
new file mode 100644
index 0000000..2a34980
--- /dev/null
+++ b/core/fxcrt/span_util.h
@@ -0,0 +1,46 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_SPAN_UTIL_H_
+#define CORE_FXCRT_SPAN_UTIL_H_
+
+#include "core/fxcrt/fx_memcpy_wrappers.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/span.h"
+
+namespace fxcrt {
+
+// Bounds-checked copies from spans into spans.
+template <typename T,
+          typename U,
+          typename = pdfium::internal::EnableIfLegalSpanConversion<T, U>>
+void spancpy(pdfium::span<T> dst, pdfium::span<U> src) {
+  CHECK_GE(dst.size_bytes(), src.size_bytes());
+  FXSYS_memcpy(dst.data(), src.data(), src.size_bytes());
+}
+
+// Bounds-checked moves from spans into spans.
+template <typename T,
+          typename U,
+          typename = pdfium::internal::EnableIfLegalSpanConversion<T, U>>
+void spanmove(pdfium::span<T> dst, pdfium::span<U> src) {
+  CHECK_GE(dst.size_bytes(), 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) {
+  FXSYS_memset(dst.data(), val, dst.size_bytes());
+}
+
+// Bounds-checked zeroing of spans.
+template <typename T>
+void spanclr(pdfium::span<T> dst) {
+  FXSYS_memset(dst.data(), 0, dst.size_bytes());
+}
+
+}  // namespace fxcrt
+
+#endif  // CORE_FXCRT_SPAN_UTIL_H_
diff --git a/core/fxcrt/span_util_unittest.cpp b/core/fxcrt/span_util_unittest.cpp
new file mode 100644
index 0000000..5718ed8
--- /dev/null
+++ b/core/fxcrt/span_util_unittest.cpp
@@ -0,0 +1,91 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/span_util.h"
+
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(Spanset, Fits) {
+  std::vector<char> dst(4, 'B');
+  fxcrt::spanset(pdfium::make_span(dst).first(2), 'A');
+  EXPECT_EQ(dst[0], 'A');
+  EXPECT_EQ(dst[1], 'A');
+  EXPECT_EQ(dst[2], 'B');
+  EXPECT_EQ(dst[3], 'B');
+}
+
+TEST(Spanset, Empty) {
+  std::vector<char> dst(4, 'B');
+  fxcrt::spanset(pdfium::make_span(dst).subspan(4), 'A');
+  EXPECT_EQ(dst[0], 'B');
+  EXPECT_EQ(dst[1], 'B');
+  EXPECT_EQ(dst[2], 'B');
+  EXPECT_EQ(dst[3], 'B');
+}
+
+TEST(Spancpy, FitsEntirely) {
+  std::vector<char> src(4, 'A');
+  std::vector<char> dst(4, 'B');
+  fxcrt::spancpy(pdfium::make_span(dst), pdfium::make_span(src));
+  EXPECT_EQ(dst[0], 'A');
+  EXPECT_EQ(dst[1], 'A');
+  EXPECT_EQ(dst[2], 'A');
+  EXPECT_EQ(dst[3], 'A');
+}
+
+TEST(Spancpy, FitsWithin) {
+  std::vector<char> src(2, 'A');
+  std::vector<char> dst(4, 'B');
+  // Also show that a const src argument is acceptable.
+  fxcrt::spancpy(pdfium::make_span(dst).subspan(1),
+                 pdfium::span<const char>(src));
+  EXPECT_EQ(dst[0], 'B');
+  EXPECT_EQ(dst[1], 'A');
+  EXPECT_EQ(dst[2], 'A');
+  EXPECT_EQ(dst[3], 'B');
+}
+
+TEST(Spancpy, EmptyCopyWithin) {
+  std::vector<char> src(2, 'A');
+  std::vector<char> dst(4, 'B');
+  fxcrt::spancpy(pdfium::make_span(dst).subspan(1),
+                 pdfium::make_span(src).subspan(2));
+  EXPECT_EQ(dst[0], 'B');
+  EXPECT_EQ(dst[1], 'B');
+  EXPECT_EQ(dst[2], 'B');
+  EXPECT_EQ(dst[3], 'B');
+}
+
+TEST(Spancpy, EmptyCopyToEmpty) {
+  std::vector<char> src(2, 'A');
+  std::vector<char> dst(4, 'B');
+  fxcrt::spancpy(pdfium::make_span(dst).subspan(4),
+                 pdfium::make_span(src).subspan(2));
+  EXPECT_EQ(dst[0], 'B');
+  EXPECT_EQ(dst[1], 'B');
+  EXPECT_EQ(dst[2], 'B');
+  EXPECT_EQ(dst[3], 'B');
+}
+
+TEST(Spanmove, FitsWithin) {
+  std::vector<char> src(2, 'A');
+  std::vector<char> dst(4, 'B');
+  // Also show that a const src argument is acceptable.
+  fxcrt::spanmove(pdfium::make_span(dst).subspan(1),
+                  pdfium::span<const char>(src));
+  EXPECT_EQ(dst[0], 'B');
+  EXPECT_EQ(dst[1], 'A');
+  EXPECT_EQ(dst[2], 'A');
+  EXPECT_EQ(dst[3], 'B');
+}
+
+TEST(Span, AssignOverOnePastEnd) {
+  std::vector<char> src(2, 'A');
+  pdfium::span<char> span = pdfium::make_span(src);
+  span = span.subspan(2);
+  span = pdfium::make_span(src);
+  EXPECT_EQ(span.size(), 2u);
+}
diff --git a/core/fxcrt/stl_util.h b/core/fxcrt/stl_util.h
new file mode 100644
index 0000000..c097300
--- /dev/null
+++ b/core/fxcrt/stl_util.h
@@ -0,0 +1,45 @@
+// Copyright 2015 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_STL_UTIL_H_
+#define CORE_FXCRT_STL_UTIL_H_
+
+#include <memory>
+
+#include "third_party/base/numerics/safe_conversions.h"
+
+namespace fxcrt {
+
+// Means of generating a key for searching STL collections of std::unique_ptr
+// that avoids the side effect of deleting the pointer.
+template <class T>
+class FakeUniquePtr : public std::unique_ptr<T> {
+ public:
+  using std::unique_ptr<T>::unique_ptr;
+  ~FakeUniquePtr() { std::unique_ptr<T>::release(); }
+};
+
+// Type-deducing wrapper for FakeUniquePtr<T>.
+template <class T>
+FakeUniquePtr<T> MakeFakeUniquePtr(T* arg) {
+  return FakeUniquePtr<T>(arg);
+}
+
+// Convenience routine for "int-fected" code, so that the stl collection
+// size_t size() method return values will be checked.
+template <typename ResultType, typename Collection>
+ResultType CollectionSize(const Collection& collection) {
+  return pdfium::base::checked_cast<ResultType>(collection.size());
+}
+
+// Convenience routine for "int-fected" code, to handle signed indicies. The
+// compiler can deduce the type, making this more convenient than the above.
+template <typename IndexType, typename Collection>
+bool IndexInBounds(const Collection& collection, IndexType index) {
+  return index >= 0 && index < CollectionSize<IndexType>(collection);
+}
+
+}  // namespace fxcrt
+
+#endif  // CORE_FXCRT_STL_UTIL_H_
diff --git a/core/fxcrt/string_data_template.cpp b/core/fxcrt/string_data_template.cpp
new file mode 100644
index 0000000..a659cc2
--- /dev/null
+++ b/core/fxcrt/string_data_template.cpp
@@ -0,0 +1,104 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/string_data_template.h"
+
+#include <string.h>
+
+#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"
+#include "third_party/base/check_op.h"
+
+namespace fxcrt {
+
+// static
+template <typename CharType>
+StringDataTemplate<CharType>* StringDataTemplate<CharType>::Create(
+    size_t nLen) {
+  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|.
+  int overhead = offsetof(StringDataTemplate, m_String) + sizeof(CharType);
+  FX_SAFE_SIZE_T nSize = nLen;
+  nSize *= sizeof(CharType);
+  nSize += overhead;
+
+  // Now round to an 16-byte boundary, assuming the underlying allocator is most
+  // likely PartitionAlloc, which has 16 byte chunks. This will help with cases
+  // where we can save a re-alloc when adding a few characters to a string by
+  // using this otherwise wasted space.
+  nSize += 15;
+  nSize &= ~15;
+  size_t totalSize = nSize.ValueOrDie();
+  size_t usableLen = (totalSize - overhead) / sizeof(CharType);
+  DCHECK(usableLen >= nLen);
+
+  void* pData = FX_StringAlloc(char, totalSize);
+  return new (pData) StringDataTemplate(nLen, usableLen);
+}
+
+// static
+template <typename CharType>
+StringDataTemplate<CharType>* StringDataTemplate<CharType>::Create(
+    const CharType* pStr,
+    size_t nLen) {
+  StringDataTemplate* result = Create(nLen);
+  result->CopyContents(pStr, nLen);
+  return result;
+}
+
+template <typename CharType>
+void StringDataTemplate<CharType>::Release() {
+  if (--m_nRefs <= 0)
+    FX_StringFree(this);
+}
+
+template <typename CharType>
+void StringDataTemplate<CharType>::CopyContents(
+    const StringDataTemplate& other) {
+  DCHECK(other.m_nDataLength <= m_nAllocLength);
+  memcpy(m_String, other.m_String,
+         (other.m_nDataLength + 1) * sizeof(CharType));
+}
+
+template <typename CharType>
+void StringDataTemplate<CharType>::CopyContents(const CharType* pStr,
+                                                size_t nLen) {
+  DCHECK_GE(nLen, 0u);
+  DCHECK_LE(nLen, m_nAllocLength);
+  FXSYS_memcpy(m_String, pStr, nLen * sizeof(CharType));
+  m_String[nLen] = 0;
+}
+
+template <typename CharType>
+void StringDataTemplate<CharType>::CopyContentsAt(size_t offset,
+                                                  const CharType* pStr,
+                                                  size_t nLen) {
+  DCHECK_GE(offset, 0u);
+  DCHECK_GE(nLen, 0u);
+  DCHECK_LE(offset + nLen, m_nAllocLength);
+  FXSYS_memcpy(m_String + offset, pStr, nLen * sizeof(CharType));
+  m_String[offset + nLen] = 0;
+}
+
+template <typename CharType>
+StringDataTemplate<CharType>::StringDataTemplate(size_t dataLen,
+                                                 size_t allocLen)
+    : m_nDataLength(dataLen), m_nAllocLength(allocLen) {
+  DCHECK_GE(dataLen, 0u);
+  DCHECK_LE(dataLen, allocLen);
+  m_String[dataLen] = 0;
+}
+
+template class StringDataTemplate<char>;
+template class StringDataTemplate<wchar_t>;
+
+}  // namespace fxcrt
diff --git a/core/fxcrt/string_data_template.h b/core/fxcrt/string_data_template.h
index cda41e0..bc5cac6 100644
--- a/core/fxcrt/string_data_template.h
+++ b/core/fxcrt/string_data_template.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,103 +7,46 @@
 #ifndef CORE_FXCRT_STRING_DATA_TEMPLATE_H_
 #define CORE_FXCRT_STRING_DATA_TEMPLATE_H_
 
-#include "core/fxcrt/fx_memory.h"
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/numerics/safe_math.h"
+#include <stddef.h>
+#include <stdint.h>
 
 namespace fxcrt {
 
 template <typename CharType>
 class StringDataTemplate {
  public:
-  static StringDataTemplate* Create(size_t nLen) {
-    ASSERT(nLen > 0);
-
-    // Calculate space needed for the fixed portion of the struct plus the
-    // NUL char that is not included in |m_nAllocLength|.
-    int overhead = offsetof(StringDataTemplate, m_String) + sizeof(CharType);
-    pdfium::base::CheckedNumeric<size_t> nSize = nLen;
-    nSize *= sizeof(CharType);
-    nSize += overhead;
-
-    // Now round to an 8-byte boundary. We'd expect that this is the minimum
-    // granularity of any of the underlying allocators, so there may be cases
-    // where we can save a re-alloc when adding a few characters to a string
-    // by using this otherwise wasted space.
-    nSize += 7;
-    nSize &= ~7;
-    size_t totalSize = nSize.ValueOrDie();
-    size_t usableLen = (totalSize - overhead) / sizeof(CharType);
-    ASSERT(usableLen >= nLen);
-
-    void* pData = GetStringPartitionAllocator().root()->Alloc(
-        totalSize, "StringDataTemplate");
-    return new (pData) StringDataTemplate(nLen, usableLen);
-  }
-
-  static StringDataTemplate* Create(const CharType* pStr, size_t nLen) {
-    StringDataTemplate* result = Create(nLen);
-    result->CopyContents(pStr, nLen);
-    return result;
-  }
+  static StringDataTemplate* Create(size_t nLen);
+  static StringDataTemplate* Create(const CharType* pStr, size_t nLen);
 
   void Retain() { ++m_nRefs; }
-  void Release() {
-    if (--m_nRefs <= 0)
-      GetStringPartitionAllocator().root()->Free(this);
-  }
+  void Release();
 
   bool CanOperateInPlace(size_t nTotalLen) const {
     return m_nRefs <= 1 && nTotalLen <= m_nAllocLength;
   }
 
-  void CopyContents(const StringDataTemplate& other) {
-    ASSERT(other.m_nDataLength <= m_nAllocLength);
-    memcpy(m_String, other.m_String,
-           (other.m_nDataLength + 1) * sizeof(CharType));
-  }
-
-  void CopyContents(const CharType* pStr, size_t nLen) {
-    ASSERT(nLen >= 0);
-    ASSERT(nLen <= m_nAllocLength);
-
-    memcpy(m_String, pStr, nLen * sizeof(CharType));
-    m_String[nLen] = 0;
-  }
-
-  void CopyContentsAt(size_t offset, const CharType* pStr, size_t nLen) {
-    ASSERT(offset >= 0);
-    ASSERT(nLen >= 0);
-    ASSERT(offset + nLen <= m_nAllocLength);
-
-    memcpy(m_String + offset, pStr, nLen * sizeof(CharType));
-    m_String[offset + nLen] = 0;
-  }
+  void CopyContents(const StringDataTemplate& other);
+  void CopyContents(const CharType* pStr, size_t nLen);
+  void CopyContentsAt(size_t offset, const CharType* pStr, size_t nLen);
 
   // To ensure ref counts do not overflow, consider the worst possible case:
   // the entire address space contains nothing but pointers to this object.
   // Since the count increments with each new pointer, the largest value is
   // the number of pointers that can fit into the address space. The size of
   // the address space itself is a good upper bound on it.
-  intptr_t m_nRefs;
+  intptr_t m_nRefs = 0;
 
   // These lengths are in terms of number of characters, not bytes, and do not
   // include the terminating NUL character, but the underlying buffer is sized
   // to be capable of holding it.
   size_t m_nDataLength;
-  size_t m_nAllocLength;
+  const size_t m_nAllocLength;
 
   // Not really 1, variable size.
   CharType m_String[1];
 
  private:
-  StringDataTemplate(size_t dataLen, size_t allocLen)
-      : m_nRefs(0), m_nDataLength(dataLen), m_nAllocLength(allocLen) {
-    ASSERT(dataLen >= 0);
-    ASSERT(dataLen <= allocLen);
-    m_String[dataLen] = 0;
-  }
-
+  StringDataTemplate(size_t dataLen, size_t allocLen);
   ~StringDataTemplate() = delete;
 };
 
diff --git a/core/fxcrt/string_pool_template.h b/core/fxcrt/string_pool_template.h
index 2f9fb09..7ccb7a5 100644
--- a/core/fxcrt/string_pool_template.h
+++ b/core/fxcrt/string_pool_template.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxcrt/string_pool_template_unittest.cpp b/core/fxcrt/string_pool_template_unittest.cpp
index 717e33d..2b9143c 100644
--- a/core/fxcrt/string_pool_template_unittest.cpp
+++ b/core/fxcrt/string_pool_template_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,8 +17,8 @@
   ByteString goats2("goats");
 
   // Underlying storage, if non-null, is not shared.
-  EXPECT_EQ(nullptr, null1.m_pData.Get());
-  EXPECT_EQ(nullptr, null2.m_pData.Get());
+  EXPECT_FALSE(null1.m_pData.Get());
+  EXPECT_FALSE(null2.m_pData.Get());
   EXPECT_NE(goats1.m_pData, goats2.m_pData);
 
   ByteString interned_null1 = pool.Intern(null1);
@@ -33,8 +33,8 @@
   EXPECT_EQ(goats2, interned_goats2);
 
   // Interned underlying storage, if non-null, belongs to first seen.
-  EXPECT_EQ(nullptr, interned_null1.m_pData.Get());
-  EXPECT_EQ(nullptr, interned_null2.m_pData.Get());
+  EXPECT_FALSE(interned_null1.m_pData.Get());
+  EXPECT_FALSE(interned_null2.m_pData.Get());
   EXPECT_EQ(goats1.m_pData, interned_goats1.m_pData);
   EXPECT_EQ(goats1.m_pData, interned_goats2.m_pData);
 
@@ -45,8 +45,8 @@
   ByteString reinterned_goats1 = pool.Intern(goats2);
 
   // After clearing pool, storage was re-interned using second strings.
-  EXPECT_EQ(nullptr, interned_null1.m_pData.Get());
-  EXPECT_EQ(nullptr, interned_null2.m_pData.Get());
+  EXPECT_FALSE(interned_null1.m_pData.Get());
+  EXPECT_FALSE(interned_null2.m_pData.Get());
   EXPECT_EQ(goats2.m_pData, reinterned_goats1.m_pData);
   EXPECT_EQ(goats2.m_pData, reinterned_goats2.m_pData);
 }
@@ -60,8 +60,8 @@
   WideString goats2(L"goats");
 
   // Underlying storage, if non-null, is not shared.
-  EXPECT_EQ(nullptr, null1.m_pData.Get());
-  EXPECT_EQ(nullptr, null2.m_pData.Get());
+  EXPECT_FALSE(null1.m_pData.Get());
+  EXPECT_FALSE(null2.m_pData.Get());
   EXPECT_NE(goats1.m_pData, goats2.m_pData);
 
   WideString interned_null1 = pool.Intern(null1);
@@ -76,8 +76,8 @@
   EXPECT_EQ(goats2, interned_goats2);
 
   // Interned underlying storage, if non-null, belongs to first seen.
-  EXPECT_EQ(nullptr, interned_null1.m_pData.Get());
-  EXPECT_EQ(nullptr, interned_null2.m_pData.Get());
+  EXPECT_FALSE(interned_null1.m_pData.Get());
+  EXPECT_FALSE(interned_null2.m_pData.Get());
   EXPECT_EQ(goats1.m_pData, interned_goats1.m_pData);
   EXPECT_EQ(goats1.m_pData, interned_goats2.m_pData);
 
@@ -88,8 +88,8 @@
   WideString reinterned_goats1 = pool.Intern(goats2);
 
   // After clearing pool, storage was re-interned using second strings.
-  EXPECT_EQ(nullptr, interned_null1.m_pData.Get());
-  EXPECT_EQ(nullptr, interned_null2.m_pData.Get());
+  EXPECT_FALSE(interned_null1.m_pData.Get());
+  EXPECT_FALSE(interned_null2.m_pData.Get());
   EXPECT_EQ(goats2.m_pData, reinterned_goats1.m_pData);
   EXPECT_EQ(goats2.m_pData, reinterned_goats2.m_pData);
 }
diff --git a/core/fxcrt/string_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 e50a71d..475490f 100644
--- a/core/fxcrt/string_view_template.h
+++ b/core/fxcrt/string_view_template.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,16 @@
 #ifndef CORE_FXCRT_STRING_VIEW_TEMPLATE_H_
 #define CORE_FXCRT_STRING_VIEW_TEMPLATE_H_
 
+#include <ctype.h>
+
 #include <algorithm>
 #include <iterator>
 #include <type_traits>
-#include <vector>
 
+#include "core/fxcrt/fx_memcpy_wrappers.h"
 #include "core/fxcrt/fx_system.h"
-#include "third_party/base/optional.h"
-#include "third_party/base/span.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcrt {
 
@@ -25,6 +26,11 @@
 //
 // String view arguments should be passed by value, since they are small,
 // rather than const-ref, even if they are not modified.
+//
+// Front() and Back() tolerate empty strings and must return NUL in those
+// cases. Substr(), First(), and Last() tolerate out-of-range indices and
+// must return an empty string view in those cases. The aim here is allowing
+// callers to avoid range-checking first.
 template <typename T>
 class StringViewTemplate {
  public:
@@ -43,41 +49,33 @@
       : m_Span(reinterpret_cast<const UnsignedType*>(ptr),
                ptr ? FXSYS_len(ptr) : 0) {}
 
-  constexpr StringViewTemplate(const CharType* ptr, size_t len) noexcept
-      : m_Span(reinterpret_cast<const UnsignedType*>(ptr), len) {}
+  constexpr StringViewTemplate(const CharType* ptr, size_t size) noexcept
+      : m_Span(reinterpret_cast<const UnsignedType*>(ptr), size) {}
+
+  template <typename E = typename std::enable_if<
+                !std::is_same<UnsignedType, CharType>::value>::type>
+  constexpr StringViewTemplate(const UnsignedType* ptr, size_t size) noexcept
+      : m_Span(ptr, size) {}
 
   explicit constexpr StringViewTemplate(
       const pdfium::span<const CharType>& other) noexcept
-      : m_Span(reinterpret_cast<const UnsignedType*>(other.data()),
+      : m_Span(!other.empty()
+                   ? reinterpret_cast<const UnsignedType*>(other.data())
+                   : nullptr,
                other.size()) {}
 
-  template <typename U = UnsignedType>
+  template <typename E = typename std::enable_if<
+                !std::is_same<UnsignedType, CharType>::value>::type>
   constexpr StringViewTemplate(
-      const UnsignedType* ptr,
-      size_t size,
-      typename std::enable_if<!std::is_same<U, CharType>::value>::type* =
-          0) noexcept
-      : m_Span(ptr, size) {}
+      const pdfium::span<const UnsignedType>& other) noexcept
+      : m_Span(!other.empty() ? other.data() : nullptr, other.size()) {}
 
-  template <typename U = UnsignedType>
-  StringViewTemplate(
-      const pdfium::span<U> other,
-      typename std::enable_if<!std::is_same<U, CharType>::value>::type* =
-          0) noexcept
-      : m_Span(other) {}
-
-  // Deliberately implicit to avoid calling on every string literal.
+  // Deliberately implicit to avoid calling on every char literal.
   // |ch| must be an lvalue that outlives the StringViewTemplate.
   // NOLINTNEXTLINE(runtime/explicit)
-  constexpr StringViewTemplate(CharType& ch) noexcept
+  constexpr StringViewTemplate(const CharType& ch) noexcept
       : m_Span(reinterpret_cast<const UnsignedType*>(&ch), 1) {}
 
-  // Any changes to |vec| invalidate the string.
-  template <typename AllocType>
-  explicit StringViewTemplate(
-      const std::vector<UnsignedType, AllocType>& vec) noexcept
-      : m_Span(!vec.empty() ? vec.data() : nullptr, vec.size()) {}
-
   StringViewTemplate& operator=(const CharType* src) {
     m_Span = pdfium::span<const UnsignedType>(
         reinterpret_cast<const UnsignedType*>(src), src ? FXSYS_len(src) : 0);
@@ -184,19 +182,26 @@
     return !m_Span.empty() ? m_Span[m_Span.size() - 1] : 0;
   }
 
-  const CharType CharAt(const size_t index) const {
+  CharType CharAt(const size_t index) const {
     return static_cast<CharType>(m_Span[index]);
   }
 
-  Optional<size_t> Find(CharType ch) const {
+  absl::optional<size_t> Find(CharType ch) const {
     const auto* found = reinterpret_cast<const UnsignedType*>(FXSYS_chr(
         reinterpret_cast<const CharType*>(m_Span.data()), ch, m_Span.size()));
 
-    return found ? Optional<size_t>(found - m_Span.data()) : Optional<size_t>();
+    return found ? absl::optional<size_t>(found - m_Span.data())
+                 : absl::nullopt;
   }
 
   bool Contains(CharType ch) const { return Find(ch).has_value(); }
 
+  StringViewTemplate Substr(size_t offset) const {
+    // Unsigned underflow is well-defined and out-of-range is handled by
+    // Substr().
+    return Substr(offset, GetLength() - offset);
+  }
+
   StringViewTemplate Substr(size_t first, size_t count) const {
     if (!m_Span.data())
       return StringViewTemplate();
@@ -210,18 +215,16 @@
     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 {
-    if (count == 0 || !IsValidLength(count))
-      return StringViewTemplate();
     return Substr(0, count);
   }
 
   StringViewTemplate Last(size_t count) const {
-    if (count == 0 || !IsValidLength(count))
-      return StringViewTemplate();
+    // Unsigned underflow is well-defined and out-of-range is handled by
+    // Substr().
     return Substr(GetLength() - count, count);
   }
 
@@ -275,12 +278,8 @@
   return rhs > lhs;
 }
 
-// Workaround for one of the cases external template classes are
-// failing in GCC before version 7 with -O0
-#if !defined(__GNUC__) || __GNUC__ >= 7
 extern template class StringViewTemplate<char>;
 extern template class StringViewTemplate<wchar_t>;
-#endif
 
 using ByteStringView = StringViewTemplate<char>;
 using WideStringView = StringViewTemplate<wchar_t>;
diff --git a/core/fxcrt/timerhandler_iface.h b/core/fxcrt/timerhandler_iface.h
deleted file mode 100644
index 04e781d..0000000
--- a/core/fxcrt/timerhandler_iface.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_TIMERHANDLER_IFACE_H_
-#define CORE_FXCRT_TIMERHANDLER_IFACE_H_
-
-#include "core/fxcrt/fx_system.h"
-
-namespace fxcrt {
-
-class TimerHandlerIface {
- public:
-  static constexpr int32_t kInvalidTimerID = 0;
-  using TimerCallback = void (*)(int32_t idEvent);
-
-  virtual ~TimerHandlerIface() = default;
-
-  virtual int32_t SetTimer(int32_t uElapse, TimerCallback lpTimerFunc) = 0;
-  virtual void KillTimer(int32_t nTimerID) = 0;
-};
-
-}  // namespace fxcrt
-
-using fxcrt::TimerHandlerIface;
-
-#endif  // CORE_FXCRT_TIMERHANDLER_IFACE_H_
diff --git a/core/fxcrt/tree_node.h b/core/fxcrt/tree_node.h
index 5b1d7df..b86d793 100644
--- a/core/fxcrt/tree_node.h
+++ b/core/fxcrt/tree_node.h
@@ -1,30 +1,43 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef CORE_FXCRT_TREE_NODE_H_
 #define CORE_FXCRT_TREE_NODE_H_
 
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/logging.h"
+#include <stdint.h>
+
+#include "core/fxcrt/unowned_ptr_exclusion.h"
+#include "third_party/base/check.h"
 
 namespace fxcrt {
 
-// Implements the usual DOM/XML-ish trees.
-template <typename T>
-class TreeNode {
- public:
-  TreeNode() = default;
-  virtual ~TreeNode() = default;
+// Implements the usual DOM/XML-ish trees allowing for a variety of
+// pointer types with which to connect the nodes. Public methods maintain
+// the invariants of the tree.
 
-  T* GetParent() const { return m_pParent; }
-  T* GetFirstChild() const { return m_pFirstChild; }
-  T* GetLastChild() const { return m_pLastChild; }
-  T* GetNextSibling() const { return m_pNextSibling; }
-  T* GetPrevSibling() const { return m_pPrevSibling; }
+template <typename T>
+class TreeNodeBase {
+ public:
+  TreeNodeBase() = default;
+  virtual ~TreeNodeBase() = default;
+
+  inline T* GetParent() const { return static_cast<const T*>(this)->m_pParent; }
+  inline T* GetFirstChild() const {
+    return static_cast<const T*>(this)->m_pFirstChild;
+  }
+  inline T* GetLastChild() const {
+    return static_cast<const T*>(this)->m_pLastChild;
+  }
+  inline T* GetNextSibling() const {
+    return static_cast<const T*>(this)->m_pNextSibling;
+  }
+  inline T* GetPrevSibling() const {
+    return static_cast<const T*>(this)->m_pPrevSibling;
+  }
 
   bool HasChild(const T* child) const {
-    return child != this && child->m_pParent == this;
+    return child != this && child->GetParent() == this;
   }
 
   T* GetNthChild(int32_t n) {
@@ -39,29 +52,29 @@
 
   void AppendFirstChild(T* child) {
     BecomeParent(child);
-    if (m_pFirstChild) {
-      CHECK(m_pLastChild);
-      m_pFirstChild->m_pPrevSibling = child;
-      child->m_pNextSibling = m_pFirstChild;
-      m_pFirstChild = child;
+    if (GetFirstChild()) {
+      CHECK(GetLastChild());
+      GetFirstChild()->SetPrevSibling(child);
+      child->SetNextSibling(GetFirstChild());
+      SetFirstChild(child);
     } else {
-      CHECK(!m_pLastChild);
-      m_pFirstChild = child;
-      m_pLastChild = child;
+      CHECK(!GetLastChild());
+      SetFirstChild(child);
+      SetLastChild(child);
     }
   }
 
   void AppendLastChild(T* child) {
     BecomeParent(child);
-    if (m_pLastChild) {
-      CHECK(m_pFirstChild);
-      m_pLastChild->m_pNextSibling = child;
-      child->m_pPrevSibling = m_pLastChild;
-      m_pLastChild = child;
+    if (GetLastChild()) {
+      CHECK(GetFirstChild());
+      GetLastChild()->SetNextSibling(child);
+      child->SetPrevSibling(GetLastChild());
+      SetLastChild(child);
     } else {
-      CHECK(!m_pFirstChild);
-      m_pFirstChild = child;
-      m_pLastChild = child;
+      CHECK(!GetFirstChild());
+      SetFirstChild(child);
+      SetLastChild(child);
     }
   }
 
@@ -72,13 +85,13 @@
     }
     BecomeParent(child);
     CHECK(HasChild(other));
-    child->m_pNextSibling = other;
-    child->m_pPrevSibling = other->m_pPrevSibling;
-    if (m_pFirstChild == other) {
-      CHECK(!other->m_pPrevSibling);
-      m_pFirstChild = child;
+    child->SetNextSibling(other);
+    child->SetPrevSibling(other->GetPrevSibling());
+    if (GetFirstChild() == other) {
+      CHECK(!other->GetPrevSibling());
+      SetFirstChild(child);
     } else {
-      other->m_pPrevSibling->m_pNextSibling = child;
+      other->GetPrevSibling()->SetNextSibling(child);
     }
     other->m_pPrevSibling = child;
   }
@@ -90,34 +103,34 @@
     }
     BecomeParent(child);
     CHECK(HasChild(other));
-    child->m_pNextSibling = other->m_pNextSibling;
-    child->m_pPrevSibling = other;
-    if (m_pLastChild == other) {
-      CHECK(!other->m_pNextSibling);
-      m_pLastChild = child;
+    child->SetNextSibling(other->GetNextSibling());
+    child->SetPrevSibling(other);
+    if (GetLastChild() == other) {
+      CHECK(!other->GetNextSibling());
+      SetLastChild(child);
     } else {
-      other->m_pNextSibling->m_pPrevSibling = child;
+      other->GetNextSibling()->SetPrevSibling(child);
     }
-    other->m_pNextSibling = child;
+    other->SetNextSibling(child);
   }
 
   void RemoveChild(T* child) {
     CHECK(HasChild(child));
-    if (m_pLastChild == child) {
-      CHECK(!child->m_pNextSibling);
-      m_pLastChild = child->m_pPrevSibling;
+    if (GetLastChild() == child) {
+      CHECK(!child->GetNextSibling());
+      SetLastChild(child->GetPrevSibling());
     } else {
-      child->m_pNextSibling->m_pPrevSibling = child->m_pPrevSibling;
+      child->GetNextSibling()->SetPrevSibling(child->GetPrevSibling());
     }
-    if (m_pFirstChild == child) {
-      CHECK(!child->m_pPrevSibling);
-      m_pFirstChild = child->m_pNextSibling;
+    if (GetFirstChild() == child) {
+      CHECK(!child->GetPrevSibling());
+      SetFirstChild(child->GetNextSibling());
     } else {
-      child->m_pPrevSibling->m_pNextSibling = child->m_pNextSibling;
+      child->GetPrevSibling()->SetNextSibling(child->GetNextSibling());
     }
-    child->m_pParent = nullptr;
-    child->m_pPrevSibling = nullptr;
-    child->m_pNextSibling = nullptr;
+    child->SetParent(nullptr);
+    child->SetPrevSibling(nullptr);
+    child->SetNextSibling(nullptr);
   }
 
   void RemoveAllChildren() {
@@ -131,21 +144,50 @@
   }
 
  private:
+  // These are private because they may leave the tree in an invalid state
+  // until subsequent operations restore it.
+  inline void SetParent(T* pParent) {
+    static_cast<T*>(this)->m_pParent = pParent;
+  }
+  inline void SetFirstChild(T* pChild) {
+    static_cast<T*>(this)->m_pFirstChild = pChild;
+  }
+  inline void SetLastChild(T* pChild) {
+    static_cast<T*>(this)->m_pLastChild = pChild;
+  }
+  inline void SetNextSibling(T* pSibling) {
+    static_cast<T*>(this)->m_pNextSibling = pSibling;
+  }
+  inline void SetPrevSibling(T* pSibling) {
+    static_cast<T*>(this)->m_pPrevSibling = pSibling;
+  }
+
   // Child left in state where sibling members need subsequent adjustment.
   void BecomeParent(T* child) {
     CHECK(child != this);  // Detect attempts at self-insertion.
     if (child->m_pParent)
-      child->m_pParent->TreeNode<T>::RemoveChild(child);
+      child->m_pParent->TreeNodeBase<T>::RemoveChild(child);
     child->m_pParent = static_cast<T*>(this);
     CHECK(!child->m_pNextSibling);
     CHECK(!child->m_pPrevSibling);
   }
+};
 
-  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
+// Tree connected using C-style pointers.
+template <typename T>
+class TreeNode : public TreeNodeBase<T> {
+ public:
+  TreeNode() = default;
+  virtual ~TreeNode() = default;
+
+ private:
+  friend class TreeNodeBase<T>;
+
+  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/tree_node_unittest.cpp b/core/fxcrt/tree_node_unittest.cpp
index 26dd610..ecd5b09 100644
--- a/core/fxcrt/tree_node_unittest.cpp
+++ b/core/fxcrt/tree_node_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
 namespace fxcrt {
 
@@ -17,114 +16,114 @@
 // These tests check that we trip CHECKS given bad calls.
 
 TEST(TreeNode, SelfAppendFirstChild) {
-  auto pNode = pdfium::MakeUnique<TestTreeNode>();
+  auto pNode = std::make_unique<TestTreeNode>();
   EXPECT_DEATH(pNode->AppendFirstChild(pNode.get()), "");
 }
 
 TEST(TreeNode, SelfAppendLastChild) {
-  auto pNode = pdfium::MakeUnique<TestTreeNode>();
+  auto pNode = std::make_unique<TestTreeNode>();
   EXPECT_DEATH(pNode->AppendLastChild(pNode.get()), "");
 }
 
 TEST(TreeNode, SelfInsertBeforeOther) {
-  auto pNode = pdfium::MakeUnique<TestTreeNode>();
-  auto pOther = pdfium::MakeUnique<TestTreeNode>();
+  auto pNode = std::make_unique<TestTreeNode>();
+  auto pOther = std::make_unique<TestTreeNode>();
   pNode->AppendFirstChild(pOther.get());
   EXPECT_DEATH(pNode->InsertBefore(pNode.get(), pOther.get()), "");
 }
 
 TEST(TreeNode, InsertOtherBeforeSelf) {
-  auto pNode = pdfium::MakeUnique<TestTreeNode>();
-  auto pOther = pdfium::MakeUnique<TestTreeNode>();
+  auto pNode = std::make_unique<TestTreeNode>();
+  auto pOther = std::make_unique<TestTreeNode>();
   pNode->AppendFirstChild(pOther.get());
   EXPECT_DEATH(pNode->InsertBefore(pOther.get(), pNode.get()), "");
 }
 
 TEST(TreeNode, SelfInsertAfterOther) {
-  auto pNode = pdfium::MakeUnique<TestTreeNode>();
-  auto pOther = pdfium::MakeUnique<TestTreeNode>();
+  auto pNode = std::make_unique<TestTreeNode>();
+  auto pOther = std::make_unique<TestTreeNode>();
   pNode->AppendFirstChild(pOther.get());
   EXPECT_DEATH(pNode->InsertBefore(pNode.get(), pOther.get()), "");
 }
 
 TEST(TreeNode, InsertOtherAfterSelf) {
-  auto pNode = pdfium::MakeUnique<TestTreeNode>();
-  auto pOther = pdfium::MakeUnique<TestTreeNode>();
+  auto pNode = std::make_unique<TestTreeNode>();
+  auto pOther = std::make_unique<TestTreeNode>();
   pNode->AppendFirstChild(pOther.get());
   EXPECT_DEATH(pNode->InsertBefore(pOther.get(), pNode.get()), "");
 }
 
 TEST(TreeNode, RemoveParentless) {
-  auto pNode = pdfium::MakeUnique<TestTreeNode>();
+  auto pNode = std::make_unique<TestTreeNode>();
   EXPECT_DEATH(pNode->GetParent()->RemoveChild(pNode.get()), "");
 }
 
 TEST(TreeNode, RemoveFromWrongParent) {
-  auto pGoodParent = pdfium::MakeUnique<TestTreeNode>();
-  auto pBadParent = pdfium::MakeUnique<TestTreeNode>();
-  auto pNode = pdfium::MakeUnique<TestTreeNode>();
+  auto pGoodParent = std::make_unique<TestTreeNode>();
+  auto pBadParent = std::make_unique<TestTreeNode>();
+  auto pNode = std::make_unique<TestTreeNode>();
   pGoodParent->AppendFirstChild(pNode.get());
   EXPECT_DEATH(pBadParent->RemoveChild(pNode.get()), "");
 }
 
 TEST(TreeNode, SafeRemove) {
-  auto pParent = pdfium::MakeUnique<TestTreeNode>();
-  auto pChild = pdfium::MakeUnique<TestTreeNode>();
+  auto pParent = std::make_unique<TestTreeNode>();
+  auto pChild = std::make_unique<TestTreeNode>();
   pParent->AppendFirstChild(pChild.get());
   pChild->RemoveSelfIfParented();
-  EXPECT_EQ(nullptr, pParent->GetFirstChild());
-  EXPECT_EQ(nullptr, pChild->GetParent());
+  EXPECT_FALSE(pParent->GetFirstChild());
+  EXPECT_FALSE(pChild->GetParent());
 }
 
 TEST(TreeNode, SafeRemoveParentless) {
-  auto pNode = pdfium::MakeUnique<TestTreeNode>();
+  auto pNode = std::make_unique<TestTreeNode>();
   pNode->RemoveSelfIfParented();
-  EXPECT_EQ(nullptr, pNode->GetParent());
+  EXPECT_FALSE(pNode->GetParent());
 }
 
 TEST(TreeNode, RemoveAllChildren) {
-  auto pParent = pdfium::MakeUnique<TestTreeNode>();
+  auto pParent = std::make_unique<TestTreeNode>();
   pParent->RemoveAllChildren();
-  EXPECT_EQ(nullptr, pParent->GetFirstChild());
+  EXPECT_FALSE(pParent->GetFirstChild());
 
-  auto p0 = pdfium::MakeUnique<TestTreeNode>();
-  auto p1 = pdfium::MakeUnique<TestTreeNode>();
-  auto p2 = pdfium::MakeUnique<TestTreeNode>();
-  auto p3 = pdfium::MakeUnique<TestTreeNode>();
+  auto p0 = std::make_unique<TestTreeNode>();
+  auto p1 = std::make_unique<TestTreeNode>();
+  auto p2 = std::make_unique<TestTreeNode>();
+  auto p3 = std::make_unique<TestTreeNode>();
   pParent->AppendLastChild(p0.get());
   pParent->AppendLastChild(p1.get());
   pParent->AppendLastChild(p2.get());
   pParent->AppendLastChild(p3.get());
   pParent->RemoveAllChildren();
-  EXPECT_EQ(nullptr, pParent->GetFirstChild());
+  EXPECT_FALSE(pParent->GetFirstChild());
 }
 
 TEST(TreeNode, NthChild) {
-  auto pParent = pdfium::MakeUnique<TestTreeNode>();
-  EXPECT_EQ(nullptr, pParent->GetNthChild(-1));
-  EXPECT_EQ(nullptr, pParent->GetNthChild(0));
+  auto pParent = std::make_unique<TestTreeNode>();
+  EXPECT_FALSE(pParent->GetNthChild(-1));
+  EXPECT_FALSE(pParent->GetNthChild(0));
 
-  auto p0 = pdfium::MakeUnique<TestTreeNode>();
-  auto p1 = pdfium::MakeUnique<TestTreeNode>();
-  auto p2 = pdfium::MakeUnique<TestTreeNode>();
-  auto p3 = pdfium::MakeUnique<TestTreeNode>();
+  auto p0 = std::make_unique<TestTreeNode>();
+  auto p1 = std::make_unique<TestTreeNode>();
+  auto p2 = std::make_unique<TestTreeNode>();
+  auto p3 = std::make_unique<TestTreeNode>();
   pParent->AppendLastChild(p0.get());
   pParent->AppendLastChild(p1.get());
   pParent->AppendLastChild(p2.get());
   pParent->AppendLastChild(p3.get());
-  EXPECT_EQ(nullptr, pParent->GetNthChild(-1));
+  EXPECT_FALSE(pParent->GetNthChild(-1));
   EXPECT_EQ(p0.get(), pParent->GetNthChild(0));
   EXPECT_EQ(p1.get(), pParent->GetNthChild(1));
   EXPECT_EQ(p2.get(), pParent->GetNthChild(2));
   EXPECT_EQ(p3.get(), pParent->GetNthChild(3));
-  EXPECT_EQ(nullptr, pParent->GetNthChild(4));
+  EXPECT_FALSE(pParent->GetNthChild(4));
   pParent->RemoveAllChildren();
 }
 
 TEST(TreeNode, AppendFirstChild) {
-  auto parent = pdfium::MakeUnique<TestTreeNode>();
-  auto child0 = pdfium::MakeUnique<TestTreeNode>();
-  auto child1 = pdfium::MakeUnique<TestTreeNode>();
+  auto parent = std::make_unique<TestTreeNode>();
+  auto child0 = std::make_unique<TestTreeNode>();
+  auto child1 = std::make_unique<TestTreeNode>();
   parent->AppendFirstChild(child0.get());
   EXPECT_EQ(child0.get(), parent->GetFirstChild());
   parent->AppendFirstChild(child1.get());
@@ -134,9 +133,9 @@
 }
 
 TEST(TreeNode, RemoveChild) {
-  auto parent = pdfium::MakeUnique<TestTreeNode>();
-  auto child0 = pdfium::MakeUnique<TestTreeNode>();
-  auto child1 = pdfium::MakeUnique<TestTreeNode>();
+  auto parent = std::make_unique<TestTreeNode>();
+  auto child0 = std::make_unique<TestTreeNode>();
+  auto child1 = std::make_unique<TestTreeNode>();
 
   parent->AppendFirstChild(child0.get());
   parent->AppendLastChild(child1.get());
@@ -146,8 +145,8 @@
   EXPECT_EQ(child1.get(), parent->GetFirstChild());
   EXPECT_EQ(child1.get(), parent->GetLastChild());
   parent->RemoveChild(child1.get());
-  EXPECT_EQ(nullptr, parent->GetFirstChild());
-  EXPECT_EQ(nullptr, parent->GetLastChild());
+  EXPECT_FALSE(parent->GetFirstChild());
+  EXPECT_FALSE(parent->GetLastChild());
 
   parent->AppendFirstChild(child0.get());
   parent->AppendLastChild(child1.get());
@@ -157,8 +156,8 @@
   EXPECT_EQ(child0.get(), parent->GetFirstChild());
   EXPECT_EQ(child0.get(), parent->GetLastChild());
   parent->RemoveChild(child0.get());
-  EXPECT_EQ(nullptr, parent->GetFirstChild());
-  EXPECT_EQ(nullptr, parent->GetLastChild());
+  EXPECT_FALSE(parent->GetFirstChild());
+  EXPECT_FALSE(parent->GetLastChild());
 }
 
 }  // namespace fxcrt
diff --git a/core/fxcrt/unowned_ptr.h b/core/fxcrt/unowned_ptr.h
index f7ff480..fa78beb 100644
--- a/core/fxcrt/unowned_ptr.h
+++ b/core/fxcrt/unowned_ptr.h
@@ -1,27 +1,28 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef CORE_FXCRT_UNOWNED_PTR_H_
 #define CORE_FXCRT_UNOWNED_PTR_H_
 
-#include <functional>
-#include <memory>
-#include <type_traits>
-#include <utility>
-
 // 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
@@ -35,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>
@@ -45,65 +82,105 @@
 namespace fxcrt {
 
 template <class T>
-class UnownedPtr {
+class TRIVIAL_ABI GSL_POINTER UnownedPtr {
  public:
   constexpr UnownedPtr() noexcept = default;
-  constexpr UnownedPtr(const UnownedPtr& that) noexcept = default;
-
-  // Move-construct an UnownedPtr. After construction, |that| will be NULL.
-  constexpr UnownedPtr(UnownedPtr&& that) noexcept : m_pObj(that.Release()) {}
-
-  template <typename U>
-  explicit constexpr UnownedPtr(U* pObj) noexcept : m_pObj(pObj) {}
 
   // Deliberately implicit to allow returning nullptrs.
   // NOLINTNEXTLINE(runtime/explicit)
-  constexpr UnownedPtr(std::nullptr_t ptr) noexcept {}
+  constexpr UnownedPtr(std::nullptr_t ptr) {}
 
-  ~UnownedPtr() { ProbeForLowSeverityLifetimeIssue(); }
+  explicit constexpr UnownedPtr(T* pObj) noexcept : m_pObj(pObj) {}
 
-  void Reset(T* obj = nullptr) {
-    ProbeForLowSeverityLifetimeIssue();
-    m_pObj = obj;
+  // Copy-construct an UnownedPtr.
+  // Required in addition to copy conversion constructor below.
+  constexpr UnownedPtr(const UnownedPtr& that) noexcept
+      : m_pObj(static_cast<T*>(that)) {}
+
+  // Move-construct an UnownedPtr. After construction, |that| will be NULL.
+  // Required in addition to move conversion constructor below.
+  constexpr UnownedPtr(UnownedPtr&& that) noexcept
+      : m_pObj(that.ExtractAsDangling()) {}
+
+  // Copy-conversion constructor.
+  template <class U,
+            typename = typename std::enable_if<
+                std::is_convertible<U*, T*>::value>::type>
+  UnownedPtr(const UnownedPtr<U>& that) : UnownedPtr(static_cast<U*>(that)) {}
+
+  // Move-conversion constructor.
+  template <class U,
+            typename = typename std::enable_if<
+                std::is_convertible<U*, T*>::value>::type>
+  UnownedPtr(UnownedPtr<U>&& that) noexcept {
+    Reset(that.ExtractAsDangling());
   }
 
+  // Assign an UnownedPtr from nullptr.
+  UnownedPtr& operator=(std::nullptr_t) noexcept {
+    Reset();
+    return *this;
+  }
+
+  // Assign an UnownedPtr from a raw ptr.
   UnownedPtr& operator=(T* that) noexcept {
     Reset(that);
     return *this;
   }
 
+  // Copy-assign an UnownedPtr.
+  // Required in addition to copy conversion assignment below.
   UnownedPtr& operator=(const UnownedPtr& that) noexcept {
     if (*this != that)
-      Reset(that.Get());
+      Reset(static_cast<T*>(that));
     return *this;
   }
 
   // Move-assign an UnownedPtr. After assignment, |that| will be NULL.
+  // Required in addition to move conversion assignment below.
   UnownedPtr& operator=(UnownedPtr&& that) noexcept {
     if (*this != that)
-      Reset(that.Release());
+      Reset(that.ExtractAsDangling());
     return *this;
   }
 
-  bool operator==(const UnownedPtr& that) const { return Get() == that.Get(); }
-  bool operator!=(const UnownedPtr& that) const { return !(*this == that); }
+  // Copy-convert assignment.
+  template <class U,
+            typename = typename std::enable_if<
+                std::is_convertible<U*, T*>::value>::type>
+  UnownedPtr& operator=(const UnownedPtr<U>& that) noexcept {
+    if (*this != that)
+      Reset(that);
+    return *this;
+  }
+
+  // Move-convert assignment. After assignment, |that| will be NULL.
+  template <class U,
+            typename = typename std::enable_if<
+                std::is_convertible<U*, T*>::value>::type>
+  UnownedPtr& operator=(UnownedPtr<U>&& that) noexcept {
+    if (*this != that)
+      Reset(that.ExtractAsDangling());
+    return *this;
+  }
+
+  ~UnownedPtr() {
+    ProbeForLowSeverityLifetimeIssue();
+    m_pObj = nullptr;
+  }
+
+  bool operator==(std::nullptr_t ptr) const { return m_pObj == nullptr; }
+  bool operator==(const UnownedPtr& that) const {
+    return m_pObj == static_cast<T*>(that);
+  }
   bool operator<(const UnownedPtr& that) const {
-    return std::less<T*>()(Get(), that.Get());
+    return std::less<T*>()(m_pObj, static_cast<T*>(that));
   }
 
-  template <typename U>
-  bool operator==(const U* that) const {
-    return Get() == that;
-  }
+  operator T*() const noexcept { return m_pObj; }
+  T* get() const noexcept { return m_pObj; }
 
-  template <typename U>
-  bool operator!=(const U* that) const {
-    return !(*this == that);
-  }
-
-  T* Get() const noexcept { return m_pObj; }
-
-  T* Release() {
+  T* ExtractAsDangling() {
     ProbeForLowSeverityLifetimeIssue();
     T* pTemp = nullptr;
     std::swap(pTemp, m_pObj);
@@ -117,6 +194,11 @@
  private:
   friend class pdfium::span<T>;
 
+  void Reset(T* obj = nullptr) {
+    ProbeForLowSeverityLifetimeIssue();
+    m_pObj = obj;
+  }
+
   inline void ProbeForLowSeverityLifetimeIssue() {
 #if defined(ADDRESS_SANITIZER)
     if (m_pObj)
@@ -130,21 +212,24 @@
 #endif
   }
 
-  T* m_pObj = nullptr;
+  UNOWNED_PTR_EXCLUSION T* m_pObj = nullptr;
 };
 
-template <typename T, typename U>
-inline bool operator==(const U* lhs, const UnownedPtr<T>& rhs) {
-  return rhs == lhs;
-}
-
-template <typename T, typename U>
-inline bool operator!=(const U* lhs, const UnownedPtr<T>& rhs) {
-  return rhs != lhs;
-}
-
 }  // 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,
+// since equivalent constructor is explicit.
+template <typename T>
+UnownedPtr<T> WrapUnowned(T* that) {
+  return UnownedPtr<T>(that);
+}
+
+}  // namespace pdfium
+
 #endif  // CORE_FXCRT_UNOWNED_PTR_H_
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 fa884d4..8c55108 100644
--- a/core/fxcrt/unowned_ptr_unittest.cpp
+++ b/core/fxcrt/unowned_ptr_unittest.cpp
@@ -1,104 +1,190 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxcrt/unowned_ptr.h"
 
+#include <atomic>
+#include <functional>
+#include <memory>
+#include <set>
 #include <utility>
-#include <vector>
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.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 {
 
+template <typename T, typename C = std::less<T>>
+class NoLinearSearchSet : public std::set<T, C> {
+ public:
+  typename std::set<T, C>::iterator begin() noexcept = delete;
+  typename std::set<T, C>::const_iterator cbegin() const noexcept = delete;
+};
+
 class Clink {
  public:
   UnownedPtr<Clink> next_ = nullptr;
 };
 
 void DeleteDangling() {
-  auto ptr2 = pdfium::MakeUnique<Clink>();
+  auto ptr2 = std::make_unique<Clink>();
   {
-    auto ptr1 = pdfium::MakeUnique<Clink>();
+    auto ptr1 = std::make_unique<Clink>();
     ptr2->next_ = ptr1.get();
   }
 }
 
-void ResetDangling() {
-  auto ptr2 = pdfium::MakeUnique<Clink>();
-  {
-    auto ptr1 = pdfium::MakeUnique<Clink>();
-    ptr2->next_.Reset(ptr1.get());
-  }
-  ptr2->next_.Reset();
-}
-
 void AssignDangling() {
-  auto ptr2 = pdfium::MakeUnique<Clink>();
+  auto ptr2 = std::make_unique<Clink>();
   {
-    auto ptr1 = pdfium::MakeUnique<Clink>();
+    auto ptr1 = std::make_unique<Clink>();
     ptr2->next_ = ptr1.get();
   }
   ptr2->next_ = nullptr;
 }
 
 void ReleaseDangling() {
-  auto ptr2 = pdfium::MakeUnique<Clink>();
+  auto ptr2 = std::make_unique<Clink>();
   {
-    auto ptr1 = pdfium::MakeUnique<Clink>();
+    auto ptr1 = std::make_unique<Clink>();
     ptr2->next_ = ptr1.get();
   }
-  ptr2->next_.Release();
+  ptr2->next_.ExtractAsDangling();
 }
 
 }  // namespace
 
+TEST(UnownedPtr, DefaultCtor) {
+  UnownedPtr<Clink> ptr;
+  EXPECT_FALSE(ptr);
+}
+
+TEST(UnownedPtr, NullptrCtor) {
+  UnownedPtr<Clink> ptr(nullptr);
+  EXPECT_FALSE(ptr);
+}
+
+TEST(UnownedPtr, RawCtor) {
+  auto obj = std::make_unique<Clink>();
+  UnownedPtr<Clink> ptr(obj.get());
+  EXPECT_EQ(obj.get(), ptr);
+}
+
+TEST(UnownedPtr, CopyCtor) {
+  std::unique_ptr<Clink> obj = std::make_unique<Clink>();
+  UnownedPtr<Clink> ptr1(obj.get());
+  UnownedPtr<Clink> ptr2(ptr1);
+  EXPECT_EQ(obj.get(), ptr2);
+  EXPECT_EQ(obj.get(), ptr1);
+}
+
+TEST(UnownedPtr, MoveCtor) {
+  std::unique_ptr<Clink> obj = std::make_unique<Clink>();
+  UnownedPtr<Clink> ptr1(obj.get());
+  UnownedPtr<Clink> ptr2(std::move(ptr1));
+  EXPECT_EQ(obj.get(), ptr2);
+  EXPECT_FALSE(ptr1);
+}
+
+TEST(UnownedPtr, CopyConversionCtor) {
+  std::unique_ptr<Clink> obj = std::make_unique<Clink>();
+  UnownedPtr<Clink> ptr1(obj.get());
+  UnownedPtr<const Clink> ptr2(ptr1);
+  EXPECT_EQ(obj.get(), ptr2);
+  EXPECT_EQ(obj.get(), ptr1);
+}
+
+TEST(UnownedPtr, MoveConversionCtor) {
+  std::unique_ptr<Clink> obj = std::make_unique<Clink>();
+  UnownedPtr<Clink> ptr1(obj.get());
+  UnownedPtr<const Clink> ptr2(std::move(ptr1));
+  EXPECT_EQ(obj.get(), ptr2);
+  EXPECT_FALSE(ptr1);
+}
+
+TEST(UnownedPtr, NullptrAssign) {
+  std::unique_ptr<Clink> obj = std::make_unique<Clink>();
+  UnownedPtr<Clink> ptr(obj.get());
+  ptr = nullptr;
+  EXPECT_FALSE(ptr);
+}
+
+TEST(UnownedPtr, RawAssign) {
+  std::unique_ptr<Clink> obj = std::make_unique<Clink>();
+  UnownedPtr<Clink> ptr;
+  ptr = obj.get();
+  EXPECT_EQ(obj.get(), ptr);
+}
+
+TEST(UnownedPtr, CopyAssign) {
+  std::unique_ptr<Clink> obj = std::make_unique<Clink>();
+  UnownedPtr<Clink> ptr1(obj.get());
+  UnownedPtr<Clink> ptr2;
+  ptr2 = ptr1;
+  EXPECT_EQ(obj.get(), ptr1);
+  EXPECT_EQ(obj.get(), ptr2);
+}
+
+TEST(UnownedPtr, MoveAssign) {
+  std::unique_ptr<Clink> obj = std::make_unique<Clink>();
+  UnownedPtr<Clink> ptr1(obj.get());
+  UnownedPtr<Clink> ptr2;
+  ptr2 = std::move(ptr1);
+  EXPECT_FALSE(ptr1);
+  EXPECT_EQ(obj.get(), ptr2);
+}
+
+TEST(UnownedPtr, CopyConversionAssign) {
+  std::unique_ptr<Clink> obj = std::make_unique<Clink>();
+  UnownedPtr<Clink> ptr1(obj.get());
+  UnownedPtr<const Clink> ptr2;
+  ptr2 = ptr1;
+  EXPECT_EQ(obj.get(), ptr1);
+  EXPECT_EQ(obj.get(), ptr2);
+}
+
+TEST(UnownedPtr, MoveConversionAssign) {
+  std::unique_ptr<Clink> obj = std::make_unique<Clink>();
+  UnownedPtr<Clink> ptr1(obj.get());
+  UnownedPtr<const Clink> ptr2;
+  ptr2 = std::move(ptr1);
+  EXPECT_FALSE(ptr1);
+  EXPECT_EQ(obj.get(), ptr2);
+}
+
 TEST(UnownedPtr, PtrOk) {
-  auto ptr1 = pdfium::MakeUnique<Clink>();
+  auto ptr1 = std::make_unique<Clink>();
   {
-    auto ptr2 = pdfium::MakeUnique<Clink>();
+    auto ptr2 = std::make_unique<Clink>();
     ptr2->next_ = ptr1.get();
   }
 }
 
 TEST(UnownedPtr, PtrNotOk) {
-#if defined(ADDRESS_SANITIZER)
+#if defined(UNOWNED_PTR_DANGLING_CHECKS)
   EXPECT_DEATH(DeleteDangling(), "");
 #else
   DeleteDangling();
 #endif
 }
 
-TEST(UnownedPtr, ResetOk) {
-  auto ptr1 = pdfium::MakeUnique<Clink>();
-  {
-    auto ptr2 = pdfium::MakeUnique<Clink>();
-    ptr2->next_.Reset(ptr1.get());
-    ptr2->next_.Reset(nullptr);
-  }
-}
-
-TEST(UnownedPtr, ResetNotOk) {
-#if defined(ADDRESS_SANITIZER)
-  EXPECT_DEATH(ResetDangling(), "");
-#else
-  ResetDangling();
-#endif
-}
-
 TEST(UnownedPtr, AssignOk) {
-  auto ptr1 = pdfium::MakeUnique<Clink>();
+  auto ptr1 = std::make_unique<Clink>();
   {
-    auto ptr2 = pdfium::MakeUnique<Clink>();
+    auto ptr2 = std::make_unique<Clink>();
     ptr2->next_ = ptr1.get();
     ptr2->next_ = nullptr;
   }
 }
 
 TEST(UnownedPtr, AssignNotOk) {
-#if defined(ADDRESS_SANITIZER)
+#if defined(UNOWNED_PTR_DANGLING_CHECKS)
   EXPECT_DEATH(AssignDangling(), "");
 #else
   AssignDangling();
@@ -106,37 +192,16 @@
 }
 
 TEST(UnownedPtr, ReleaseOk) {
-  auto ptr2 = pdfium::MakeUnique<Clink>();
+  auto ptr2 = std::make_unique<Clink>();
   {
-    auto ptr1 = pdfium::MakeUnique<Clink>();
+    auto ptr1 = std::make_unique<Clink>();
     ptr2->next_ = ptr1.get();
-    ptr2->next_.Release();
-  }
-}
-
-TEST(UnownedPtr, MoveCtorOk) {
-  UnownedPtr<Clink> outer;
-  {
-    auto owned = pdfium::MakeUnique<Clink>();
-    outer = owned.get();
-    UnownedPtr<Clink> inner(std::move(outer));
-    EXPECT_EQ(nullptr, outer.Get());
-  }
-}
-
-TEST(UnownedPtr, MoveAssignOk) {
-  UnownedPtr<Clink> outer;
-  {
-    auto owned = pdfium::MakeUnique<Clink>();
-    outer = owned.get();
-    UnownedPtr<Clink> inner;
-    inner = std::move(outer);
-    EXPECT_EQ(nullptr, outer.Get());
+    ptr2->next_.ExtractAsDangling();
   }
 }
 
 TEST(UnownedPtr, ReleaseNotOk) {
-#if defined(ADDRESS_SANITIZER)
+#if defined(UNOWNED_PTR_DANGLING_CHECKS)
   EXPECT_DEATH(ReleaseDangling(), "");
 #else
   ReleaseDangling();
@@ -187,4 +252,46 @@
   EXPECT_FALSE(ptr2 < ptr1);
 }
 
+TEST(UnownedPtr, TransparentCompare) {
+  int foos[2];
+  UnownedPtr<int> ptr1(&foos[0]);
+  UnownedPtr<int> ptr2(&foos[1]);
+  NoLinearSearchSet<UnownedPtr<int>, std::less<>> holder;
+  holder.insert(ptr1);
+  EXPECT_NE(holder.end(), holder.find(&foos[0]));
+  EXPECT_EQ(holder.end(), holder.find(&foos[1]));
+  EXPECT_TRUE(pdfium::Contains(holder, &foos[0]));
+  EXPECT_FALSE(pdfium::Contains(holder, &foos[1]));
+}
+
+#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/weak_ptr.h b/core/fxcrt/weak_ptr.h
index 7b345aa..fec4260 100644
--- a/core/fxcrt/weak_ptr.h
+++ b/core/fxcrt/weak_ptr.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,11 @@
 #ifndef CORE_FXCRT_WEAK_PTR_H_
 #define CORE_FXCRT_WEAK_PTR_H_
 
-#include <cstddef>
+#include <stdint.h>
+
 #include <memory>
 #include <utility>
 
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 
 namespace fxcrt {
diff --git a/core/fxcrt/weak_ptr_unittest.cpp b/core/fxcrt/weak_ptr_unittest.cpp
index f4026fb..468d840 100644
--- a/core/fxcrt/weak_ptr_unittest.cpp
+++ b/core/fxcrt/weak_ptr_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -19,7 +19,7 @@
 
 class PseudoDeletable {
  public:
-  PseudoDeletable() : delete_count_(0) {}
+  PseudoDeletable() = default;
   void Release() {
     ++delete_count_;
     next_.Reset();
@@ -28,7 +28,7 @@
   int delete_count() const { return delete_count_; }
 
  private:
-  int delete_count_;
+  int delete_count_ = 0;
   WeakTestPtr next_;
 };
 
@@ -88,7 +88,7 @@
     WeakTestPtr ptr2 = ptr1;
     ptr1.Reset();
     EXPECT_FALSE(ptr1);
-    EXPECT_EQ(nullptr, ptr1.Get());
+    EXPECT_FALSE(ptr1.Get());
     EXPECT_TRUE(ptr2);
     EXPECT_EQ(&thing, ptr2.Get());
     EXPECT_FALSE(ptr1 == ptr2);
@@ -128,9 +128,9 @@
     WeakTestPtr ptr2 = ptr1;
     ptr1.DeleteObject();
     EXPECT_FALSE(ptr1);
-    EXPECT_EQ(nullptr, ptr1.Get());
+    EXPECT_FALSE(ptr1.Get());
     EXPECT_FALSE(ptr2);
-    EXPECT_EQ(nullptr, ptr2.Get());
+    EXPECT_FALSE(ptr2.Get());
     EXPECT_FALSE(ptr1 == ptr2);
     EXPECT_TRUE(ptr1 != ptr2);
     EXPECT_EQ(1, thing.delete_count());
diff --git a/core/fxcrt/widestring.cpp b/core/fxcrt/widestring.cpp
index 4e51a85..241e544 100644
--- a/core/fxcrt/widestring.cpp
+++ b/core/fxcrt/widestring.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,17 +7,20 @@
 #include "core/fxcrt/widestring.h"
 
 #include <stddef.h>
+#include <string.h>
 
 #include <algorithm>
-#include <cctype>
-#include <cwctype>
+#include <sstream>
 
 #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/numerics/safe_math.h"
-#include "third_party/base/stl_util.h"
 
 template class fxcrt::StringDataTemplate<wchar_t>;
 template class fxcrt::StringViewTemplate<wchar_t>;
@@ -33,69 +36,69 @@
 constexpr wchar_t kWideTrimChars[] = L"\x09\x0a\x0b\x0c\x0d\x20";
 
 const wchar_t* FX_wcsstr(const wchar_t* haystack,
-                         int haystack_len,
+                         size_t haystack_len,
                          const wchar_t* needle,
-                         int needle_len) {
-  if (needle_len > haystack_len || needle_len == 0) {
+                         size_t needle_len) {
+  if (needle_len > haystack_len || needle_len == 0)
     return nullptr;
-  }
+
   const wchar_t* end_ptr = haystack + haystack_len - needle_len;
   while (haystack <= end_ptr) {
-    int i = 0;
-    while (1) {
-      if (haystack[i] != needle[i]) {
+    size_t i = 0;
+    while (true) {
+      if (haystack[i] != needle[i])
         break;
-      }
+
       i++;
-      if (i == needle_len) {
+      if (i == needle_len)
         return haystack;
-      }
     }
     haystack++;
   }
   return nullptr;
 }
 
-Optional<size_t> GuessSizeForVSWPrintf(const wchar_t* pFormat,
-                                       va_list argList) {
+absl::optional<size_t> GuessSizeForVSWPrintf(const wchar_t* pFormat,
+                                             va_list argList) {
   size_t nMaxLen = 0;
   for (const wchar_t* pStr = pFormat; *pStr != 0; pStr++) {
     if (*pStr != '%' || *(pStr = pStr + 1) == '%') {
       ++nMaxLen;
       continue;
     }
-    int nItemLen = 0;
-    int nWidth = 0;
+    int iWidth = 0;
     for (; *pStr != 0; pStr++) {
       if (*pStr == '#') {
         nMaxLen += 2;
       } else if (*pStr == '*') {
-        nWidth = va_arg(argList, int);
+        iWidth = va_arg(argList, int);
       } else if (*pStr != '-' && *pStr != '+' && *pStr != '0' && *pStr != ' ') {
         break;
       }
     }
-    if (nWidth == 0) {
-      nWidth = FXSYS_wtoi(pStr);
+    if (iWidth == 0) {
+      iWidth = FXSYS_wtoi(pStr);
       while (FXSYS_IsDecimalDigit(*pStr))
         ++pStr;
     }
-    if (nWidth < 0 || nWidth > 128 * 1024)
-      return pdfium::nullopt;
-    int nPrecision = 0;
+    if (iWidth < 0 || iWidth > 128 * 1024)
+      return absl::nullopt;
+    uint32_t nWidth = static_cast<uint32_t>(iWidth);
+    int iPrecision = 0;
     if (*pStr == '.') {
       pStr++;
       if (*pStr == '*') {
-        nPrecision = va_arg(argList, int);
+        iPrecision = va_arg(argList, int);
         pStr++;
       } else {
-        nPrecision = FXSYS_wtoi(pStr);
+        iPrecision = FXSYS_wtoi(pStr);
         while (FXSYS_IsDecimalDigit(*pStr))
           ++pStr;
       }
     }
-    if (nPrecision < 0 || nPrecision > 128 * 1024)
-      return pdfium::nullopt;
+    if (iPrecision < 0 || iPrecision > 128 * 1024)
+      return absl::nullopt;
+    uint32_t nPrecision = static_cast<uint32_t>(iPrecision);
     int nModifier = 0;
     if (*pStr == L'I' && *(pStr + 1) == L'6' && *(pStr + 2) == L'4') {
       pStr += 3;
@@ -117,6 +120,7 @@
           break;
       }
     }
+    size_t nItemLen = 0;
     switch (*pStr | nModifier) {
       case 'c':
       case 'C':
@@ -248,11 +252,11 @@
 }
 
 // Returns string unless we ran out of space.
-Optional<WideString> TryVSWPrintf(size_t size,
-                                  const wchar_t* pFormat,
-                                  va_list argList) {
+absl::optional<WideString> TryVSWPrintf(size_t size,
+                                        const wchar_t* pFormat,
+                                        va_list argList) {
   if (!size)
-    return {};
+    return absl::nullopt;
 
   WideString str;
   {
@@ -270,10 +274,10 @@
 
     bool bSufficientBuffer = ret >= 0 || buffer[size - 1] == 0;
     if (!bSufficientBuffer)
-      return {};
+      return absl::nullopt;
   }
   str.ReleaseBuffer(str.GetStringLength());
-  return {str};
+  return str;
 }
 
 }  // namespace
@@ -284,6 +288,13 @@
               "Strings must not require more space than pointers");
 
 // static
+WideString WideString::FormatInteger(int i) {
+  wchar_t wbuf[32];
+  swprintf(wbuf, std::size(wbuf), L"%d", i);
+  return WideString(wbuf);
+}
+
+// static
 WideString WideString::FormatV(const wchar_t* format, va_list argList) {
   va_list argListCopy;
   va_copy(argListCopy, argList);
@@ -297,12 +308,12 @@
 
   while (maxLen < 32 * 1024) {
     va_copy(argListCopy, argList);
-    Optional<WideString> ret =
+    absl::optional<WideString> ret =
         TryVSWPrintf(static_cast<size_t>(maxLen), format, argListCopy);
     va_end(argListCopy);
+    if (ret.has_value())
+      return ret.value();
 
-    if (ret)
-      return *ret;
     maxLen *= 2;
   }
   return WideString();
@@ -317,7 +328,7 @@
   return ret;
 }
 
-WideString::WideString() {}
+WideString::WideString() = default;
 
 WideString::WideString(const WideString& other) : m_pData(other.m_pData) {}
 
@@ -378,7 +389,15 @@
   }
 }
 
-WideString::~WideString() {}
+WideString::~WideString() = default;
+
+void WideString::clear() {
+  if (m_pData && m_pData->CanOperateInPlace(0)) {
+    m_pData->m_nDataLength = 0;
+    return;
+  }
+  m_pData.Reset();
+}
 
 WideString& WideString::operator=(const wchar_t* str) {
   if (!str || !str[0])
@@ -405,7 +424,7 @@
   return *this;
 }
 
-WideString& WideString::operator=(WideString&& that) {
+WideString& WideString::operator=(WideString&& that) noexcept {
   if (m_pData != that.m_pData)
     m_pData = std::move(that.m_pData);
 
@@ -446,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 {
@@ -454,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 {
@@ -485,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);
 }
 
@@ -543,7 +562,7 @@
     return;
   }
 
-  ASSERT(m_pData->m_nRefs == 1);
+  DCHECK_EQ(m_pData->m_nRefs, 1);
   m_pData->m_nDataLength = nNewLength;
   m_pData->m_String[nNewLength] = 0;
   if (m_pData->m_nAllocLength - nNewLength >= 32) {
@@ -588,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)
@@ -648,9 +668,8 @@
 }
 
 ByteString WideString::ToDefANSI() const {
-  int src_len = GetLength();
-  int dest_len = FXSYS_WideCharToMultiByte(
-      FX_CODEPAGE_DefANSI, 0, c_str(), src_len, nullptr, 0, nullptr, nullptr);
+  size_t dest_len =
+      FX_WideCharToMultiByte(FX_CodePage::kDefANSI, AsStringView(), {});
   if (!dest_len)
     return ByteString();
 
@@ -658,8 +677,7 @@
   {
     // Span's lifetime must end before ReleaseBuffer() below.
     pdfium::span<char> dest_buf = bstr.GetBuffer(dest_len);
-    FXSYS_WideCharToMultiByte(FX_CODEPAGE_DefANSI, 0, c_str(), src_len,
-                              dest_buf.data(), dest_len, nullptr, nullptr);
+    FX_WideCharToMultiByte(FX_CodePage::kDefANSI, AsStringView(), dest_buf);
   }
   bstr.ReleaseBuffer(dest_len);
   return bstr;
@@ -674,11 +692,11 @@
     return ByteString("\0\0", 2);
 
   ByteString result;
-  int len = m_pData->m_nDataLength;
+  size_t len = m_pData->m_nDataLength;
   {
     // Span's lifetime must end before ReleaseBuffer() below.
     pdfium::span<char> buffer = result.GetBuffer(len * 2 + 2);
-    for (int i = 0; i < len; i++) {
+    for (size_t i = 0; i < len; i++) {
       buffer[i * 2] = m_pData->m_String[i] & 0xff;
       buffer[i * 2 + 1] = m_pData->m_String[i] >> 8;
     }
@@ -689,6 +707,21 @@
   return result;
 }
 
+WideString WideString::EncodeEntities() const {
+  WideString ret = *this;
+  ret.Replace(L"&", L"&amp;");
+  ret.Replace(L"<", L"&lt;");
+  ret.Replace(L">", L"&gt;");
+  ret.Replace(L"\'", L"&apos;");
+  ret.Replace(L"\"", L"&quot;");
+  return ret;
+}
+
+WideString WideString::Substr(size_t offset) const {
+  // Unsigned underflow is well-defined and out-of-range is handled by Substr().
+  return Substr(offset, GetLength() - offset);
+}
+
 WideString WideString::Substr(size_t first, size_t count) const {
   if (!m_pData)
     return WideString();
@@ -711,14 +744,11 @@
 }
 
 WideString WideString::First(size_t count) const {
-  if (count == 0 || !IsValidLength(count))
-    return WideString();
   return Substr(0, count);
 }
 
 WideString WideString::Last(size_t count) const {
-  if (count == 0 || !IsValidLength(count))
-    return WideString();
+  // Unsigned underflow is well-defined and out-of-range is handled by Substr().
   return Substr(GetLength() - count, count);
 }
 
@@ -740,54 +770,57 @@
 
   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;
 }
 
-Optional<size_t> WideString::Find(wchar_t ch, size_t start) const {
+absl::optional<size_t> WideString::Find(wchar_t ch, size_t start) const {
   if (!m_pData)
-    return pdfium::nullopt;
+    return absl::nullopt;
 
   if (!IsValidIndex(start))
-    return pdfium::nullopt;
+    return absl::nullopt;
 
-  const wchar_t* pStr =
-      wmemchr(m_pData->m_String + start, ch, m_pData->m_nDataLength - start);
-  return pStr ? Optional<size_t>(static_cast<size_t>(pStr - m_pData->m_String))
-              : pdfium::nullopt;
+  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;
 }
 
-Optional<size_t> WideString::Find(WideStringView subStr, size_t start) const {
+absl::optional<size_t> WideString::Find(WideStringView subStr,
+                                        size_t start) const {
   if (!m_pData)
-    return pdfium::nullopt;
+    return absl::nullopt;
 
   if (!IsValidIndex(start))
-    return pdfium::nullopt;
+    return absl::nullopt;
 
   const wchar_t* pStr =
       FX_wcsstr(m_pData->m_String + start, m_pData->m_nDataLength - start,
                 subStr.unterminated_c_str(), subStr.GetLength());
-  return pStr ? Optional<size_t>(static_cast<size_t>(pStr - m_pData->m_String))
-              : pdfium::nullopt;
+  return pStr ? absl::optional<size_t>(
+                    static_cast<size_t>(pStr - m_pData->m_String))
+              : absl::nullopt;
 }
 
-Optional<size_t> WideString::ReverseFind(wchar_t ch) const {
+absl::optional<size_t> WideString::ReverseFind(wchar_t ch) const {
   if (!m_pData)
-    return pdfium::nullopt;
+    return absl::nullopt;
 
   size_t nLength = m_pData->m_nDataLength;
   while (nLength--) {
     if (m_pData->m_String[nLength] == ch)
       return nLength;
   }
-  return pdfium::nullopt;
+  return absl::nullopt;
 }
 
 void WideString::MakeLower() {
-  if (!m_pData)
+  if (IsEmpty())
     return;
 
   ReallocBeforeWrite(m_pData->m_nDataLength);
@@ -795,7 +828,7 @@
 }
 
 void WideString::MakeUpper() {
-  if (!m_pData)
+  if (IsEmpty())
     return;
 
   ReallocBeforeWrite(m_pData->m_nDataLength);
@@ -803,7 +836,7 @@
 }
 
 size_t WideString::Remove(wchar_t chRemove) {
-  if (!m_pData || m_pData->m_nDataLength == 0)
+  if (IsEmpty())
     return 0;
 
   wchar_t* pstrSource = m_pData->m_String;
@@ -845,7 +878,7 @@
   size_t count = 0;
   const wchar_t* pStart = m_pData->m_String;
   wchar_t* pEnd = m_pData->m_String + m_pData->m_nDataLength;
-  while (1) {
+  while (true) {
     const wchar_t* pTarget =
         FX_wcsstr(pStart, static_cast<size_t>(pEnd - pStart),
                   pOld.unterminated_c_str(), nSourceLen);
@@ -873,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;
 }
@@ -904,9 +937,7 @@
 
 // static
 WideString WideString::FromDefANSI(ByteStringView bstr) {
-  int src_len = bstr.GetLength();
-  int dest_len = FXSYS_MultiByteToWideChar(
-      FX_CODEPAGE_DefANSI, 0, bstr.unterminated_c_str(), src_len, nullptr, 0);
+  size_t dest_len = FX_MultiByteToWideChar(FX_CodePage::kDefANSI, bstr, {});
   if (!dest_len)
     return WideString();
 
@@ -914,8 +945,7 @@
   {
     // Span's lifetime must end before ReleaseBuffer() below.
     pdfium::span<wchar_t> dest_buf = wstr.GetBuffer(dest_len);
-    FXSYS_MultiByteToWideChar(FX_CODEPAGE_DefANSI, 0, bstr.unterminated_c_str(),
-                              src_len, dest_buf.data(), dest_len);
+    FX_MultiByteToWideChar(FX_CodePage::kDefANSI, bstr, dest_buf);
   }
   wstr.ReleaseBuffer(dest_len);
   return wstr;
@@ -961,7 +991,7 @@
 }
 
 void WideString::SetAt(size_t index, wchar_t c) {
-  ASSERT(IsValidIndex(index));
+  DCHECK(IsValidIndex(index));
   ReallocBeforeWrite(m_pData->m_nDataLength);
   m_pData->m_String[index] = c;
 }
@@ -981,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)
@@ -1105,14 +1135,16 @@
 
 }  // namespace fxcrt
 
-uint32_t FX_HashCode_GetW(WideStringView str, bool bIgnoreCase) {
+uint32_t FX_HashCode_GetW(WideStringView str) {
   uint32_t dwHashCode = 0;
-  if (bIgnoreCase) {
-    for (wchar_t c : str)  // match FXSYS_towlower() arg type.
-      dwHashCode = 1313 * dwHashCode + FXSYS_towlower(c);
-  } else {
-    for (WideStringView::UnsignedType c : str)
-      dwHashCode = 1313 * dwHashCode + c;
-  }
+  for (WideStringView::UnsignedType c : str)
+    dwHashCode = 1313 * dwHashCode + c;
+  return dwHashCode;
+}
+
+uint32_t FX_HashCode_GetLoweredW(WideStringView str) {
+  uint32_t dwHashCode = 0;
+  for (wchar_t c : str)  // match FXSYS_towlower() arg type.
+    dwHashCode = 1313 * dwHashCode + FXSYS_towlower(c);
   return dwHashCode;
 }
diff --git a/core/fxcrt/widestring.h b/core/fxcrt/widestring.h
index 58e7544..a01e960 100644
--- a/core/fxcrt/widestring.h
+++ b/core/fxcrt/widestring.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,18 +7,22 @@
 #ifndef CORE_FXCRT_WIDESTRING_H_
 #define CORE_FXCRT_WIDESTRING_H_
 
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <wchar.h>
+
 #include <functional>
+#include <iosfwd>
 #include <iterator>
-#include <ostream>
 #include <utility>
 
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/string_data_template.h"
 #include "core/fxcrt/string_view_template.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcrt {
 
@@ -28,13 +32,15 @@
 // 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>;
 
-  static WideString Format(const wchar_t* pFormat, ...) WARN_UNUSED_RESULT;
-  static WideString FormatV(const wchar_t* lpszFormat,
-                            va_list argList) WARN_UNUSED_RESULT;
+  [[nodiscard]] static WideString FormatInteger(int i);
+  [[nodiscard]] static WideString Format(const wchar_t* pFormat, ...);
+  [[nodiscard]] static WideString FormatV(const wchar_t* lpszFormat,
+                                          va_list argList);
 
   WideString();
   WideString(const WideString& other);
@@ -42,10 +48,11 @@
   // Move-construct a WideString. After construction, |other| is empty.
   WideString(WideString&& other) noexcept;
 
+  // Make a one-character string from one wide char.
+  explicit WideString(wchar_t ch);
+
   // Deliberately implicit to avoid calling on every string literal.
   // NOLINTNEXTLINE(runtime/explicit)
-  WideString(wchar_t ch);
-  // NOLINTNEXTLINE(runtime/explicit)
   WideString(const wchar_t* ptr);
 
   // No implicit conversions from byte strings.
@@ -60,16 +67,16 @@
 
   ~WideString();
 
-  static WideString FromASCII(ByteStringView str) WARN_UNUSED_RESULT;
-  static WideString FromLatin1(ByteStringView str) WARN_UNUSED_RESULT;
-  static WideString FromDefANSI(ByteStringView str) WARN_UNUSED_RESULT;
-  static WideString FromUTF8(ByteStringView str) WARN_UNUSED_RESULT;
-  static WideString FromUTF16LE(const unsigned short* str,
-                                size_t len) WARN_UNUSED_RESULT;
-  static WideString FromUTF16BE(const unsigned short* wstr,
-                                size_t wlen) WARN_UNUSED_RESULT;
+  [[nodiscard]] static WideString FromASCII(ByteStringView str);
+  [[nodiscard]] static WideString FromLatin1(ByteStringView str);
+  [[nodiscard]] static WideString FromDefANSI(ByteStringView str);
+  [[nodiscard]] static WideString FromUTF8(ByteStringView str);
+  [[nodiscard]] static WideString FromUTF16LE(const unsigned short* str,
+                                              size_t len);
+  [[nodiscard]] static WideString FromUTF16BE(const unsigned short* wstr,
+                                              size_t wlen);
 
-  static size_t WStringLength(const unsigned short* str) WARN_UNUSED_RESULT;
+  [[nodiscard]] static size_t WStringLength(const unsigned short* str);
 
   // Explicit conversion to C-style wide string.
   // Note: Any subsequent modification of |this| will invalidate the result.
@@ -102,7 +109,9 @@
     return const_reverse_iterator(begin());
   }
 
-  void clear() { m_pData.Reset(); }
+  // Holds on to buffer if possible for later re-use. Assign WideString()
+  // to force immediate release if desired.
+  void clear();
 
   size_t GetLength() const { return m_pData ? m_pData->m_nDataLength : 0; }
   size_t GetStringLength() const {
@@ -117,7 +126,7 @@
   WideString& operator=(const WideString& that);
 
   // Move-assign a WideString. After assignment, |that| is empty.
-  WideString& operator=(WideString&& that);
+  WideString& operator=(WideString&& that) noexcept;
 
   WideString& operator+=(const wchar_t* str);
   WideString& operator+=(wchar_t ch);
@@ -150,6 +159,7 @@
   int Compare(const WideString& str) const;
   int CompareNoCase(const wchar_t* str) const;
 
+  WideString Substr(size_t offset) const;
   WideString Substr(size_t first, size_t count) const;
   WideString First(size_t count) const;
   WideString Last(size_t count) const;
@@ -183,9 +193,9 @@
 
   int GetInteger() const;
 
-  Optional<size_t> Find(WideStringView subStr, size_t start = 0) const;
-  Optional<size_t> Find(wchar_t ch, size_t start = 0) const;
-  Optional<size_t> ReverseFind(wchar_t ch) const;
+  absl::optional<size_t> Find(WideStringView subStr, size_t start = 0) const;
+  absl::optional<size_t> Find(wchar_t ch, size_t start = 0) const;
+  absl::optional<size_t> ReverseFind(wchar_t ch) const;
 
   bool Contains(WideStringView lpszSub, size_t start = 0) const {
     return Find(lpszSub, start).has_value();
@@ -216,6 +226,9 @@
   // so GetLength() will include them.
   ByteString ToUTF16LE() const;
 
+  // Replace the characters &<>'" with HTML entities.
+  WideString EncodeEntities() const;
+
  protected:
   using StringData = StringDataTemplate<wchar_t>;
 
@@ -247,7 +260,7 @@
   return WideString(str1, WideStringView(ch));
 }
 inline WideString operator+(wchar_t ch, WideStringView str2) {
-  return WideString(ch, str2);
+  return WideString(WideStringView(ch), str2);
 }
 inline WideString operator+(const WideString& str1, const WideString& str2) {
   return WideString(str1.AsStringView(), str2.AsStringView());
@@ -256,7 +269,7 @@
   return WideString(str1.AsStringView(), WideStringView(ch));
 }
 inline WideString operator+(wchar_t ch, const WideString& str2) {
-  return WideString(ch, str2.AsStringView());
+  return WideString(WideStringView(ch), str2.AsStringView());
 }
 inline WideString operator+(const WideString& str1, const wchar_t* str2) {
   return WideString(str1.AsStringView(), str2);
@@ -291,18 +304,26 @@
 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;
 
-uint32_t FX_HashCode_GetW(WideStringView str, bool bIgnoreCase);
+uint32_t FX_HashCode_GetW(WideStringView str);
+uint32_t FX_HashCode_GetLoweredW(WideStringView str);
 
 namespace std {
 
 template <>
 struct hash<WideString> {
-  std::size_t operator()(const WideString& str) const {
-    return FX_HashCode_GetW(str.AsStringView(), false);
+  size_t operator()(const WideString& str) const {
+    return FX_HashCode_GetW(str.AsStringView());
   }
 };
 
diff --git a/core/fxcrt/widestring_unittest.cpp b/core/fxcrt/widestring_unittest.cpp
index 2445a47..947926c 100644
--- a/core/fxcrt/widestring_unittest.cpp
+++ b/core/fxcrt/widestring_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,8 @@
 #include "build/build_config.h"
 #include "core/fxcrt/fx_string.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/span.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcrt {
 
@@ -109,6 +109,26 @@
     }
     EXPECT_EQ(1, string1.ReferenceCountForTesting());
   }
+  {
+    // From wchar_t*.
+    WideString string1 = L"abc";
+    EXPECT_EQ(L"abc", string1);
+    string1 = nullptr;
+    EXPECT_TRUE(string1.IsEmpty());
+    string1 = L"def";
+    EXPECT_EQ(L"def", string1);
+    string1 = L"";
+    EXPECT_TRUE(string1.IsEmpty());
+  }
+  {
+    // From WideStringView.
+    WideString string1(WideStringView(L"abc"));
+    EXPECT_EQ(L"abc", string1);
+    string1 = WideStringView(L"");
+    EXPECT_TRUE(string1.IsEmpty());
+    string1 = WideStringView(L"def");
+    EXPECT_EQ(L"def", string1);
+  }
 }
 
 TEST(WideString, OperatorLT) {
@@ -514,7 +534,16 @@
 }
 
 TEST(WideString, Replace) {
+  WideString empty;
+  empty.Replace(L"", L"CLAMS");
+  empty.Replace(L"xx", L"CLAMS");
+  EXPECT_EQ(L"", empty);
+
   WideString fred(L"FRED");
+  fred.Replace(L"", L"");
+  EXPECT_EQ(L"FRED", fred);
+  fred.Replace(L"", L"CLAMS");
+  EXPECT_EQ(L"FRED", fred);
   fred.Replace(L"FR", L"BL");
   EXPECT_EQ(L"BLED", fred);
   fred.Replace(L"D", L"DDY");
@@ -525,10 +554,20 @@
   EXPECT_EQ(L"BY", fred);
   fred.Replace(L"BY", L"HI");
   EXPECT_EQ(L"HI", fred);
-  fred.Replace(L"", L"CLAMS");
-  EXPECT_EQ(L"HI", fred);
-  fred.Replace(L"HI", L"");
+  fred.Replace(L"I", L"IHIHI");
+  EXPECT_EQ(L"HIHIHI", fred);
+  fred.Replace(L"HI", L"HO");
+  EXPECT_EQ(L"HOHOHO", fred);
+  fred.Replace(L"HO", L"");
   EXPECT_EQ(L"", fred);
+
+  WideString five_xs(L"xxxxx");
+  five_xs.Replace(L"xx", L"xxx");
+  EXPECT_EQ(L"xxxxxxx", five_xs);
+
+  WideString five_ys(L"yyyyy");
+  five_ys.Replace(L"yy", L"y");
+  EXPECT_EQ(L"yyy", five_ys);
 }
 
 TEST(WideString, Insert) {
@@ -611,7 +650,20 @@
   EXPECT_EQ(L"", empty);
 }
 
-TEST(WideString, Substr) {
+TEST(WideString, OneArgSubstr) {
+  WideString fred(L"FRED");
+  EXPECT_EQ(L"FRED", fred.Substr(0));
+  EXPECT_EQ(L"RED", fred.Substr(1));
+  EXPECT_EQ(L"ED", fred.Substr(2));
+  EXPECT_EQ(L"D", fred.Substr(3));
+  EXPECT_EQ(L"", fred.Substr(4));
+
+  WideString empty;
+  EXPECT_EQ(L"", empty.Substr(0));
+  EXPECT_EQ(L"", empty.Substr(1));
+}
+
+TEST(WideString, TwoArgSubstr) {
   WideString fred(L"FRED");
   EXPECT_EQ(L"", fred.Substr(0, 0));
   EXPECT_EQ(L"", fred.Substr(3, 0));
@@ -673,9 +725,8 @@
   EXPECT_FALSE(empty_string.Find(L'a').has_value());
   EXPECT_FALSE(empty_string.Find(L'\0').has_value());
 
-  Optional<size_t> result;
   WideString single_string(L"a");
-  result = single_string.Find(L'a');
+  absl::optional<size_t> result = single_string.Find(L'a');
   ASSERT_TRUE(result.has_value());
   EXPECT_EQ(0u, result.value());
   EXPECT_FALSE(single_string.Find(L'b').has_value());
@@ -721,9 +772,8 @@
   EXPECT_FALSE(empty_string.ReverseFind(L'a').has_value());
   EXPECT_FALSE(empty_string.ReverseFind(L'\0').has_value());
 
-  Optional<size_t> result;
   WideString single_string(L"a");
-  result = single_string.ReverseFind(L'a');
+  absl::optional<size_t> result = single_string.ReverseFind(L'a');
   ASSERT_TRUE(result.has_value());
   EXPECT_EQ(0u, result.value());
   EXPECT_FALSE(single_string.ReverseFind(L'b').has_value());
@@ -758,6 +808,17 @@
   EXPECT_EQ(L"", empty);
   empty.MakeUpper();
   EXPECT_EQ(L"", empty);
+
+  WideString empty_with_buffer(L"x");
+  empty_with_buffer.Delete(0);
+
+  WideString additional_empty_with_buffer_ref = empty_with_buffer;
+  additional_empty_with_buffer_ref.MakeLower();
+  EXPECT_EQ(L"", additional_empty_with_buffer_ref);
+
+  additional_empty_with_buffer_ref = empty_with_buffer;
+  additional_empty_with_buffer_ref.MakeUpper();
+  EXPECT_EQ(L"", additional_empty_with_buffer_ref);
 }
 
 TEST(WideString, Trim) {
@@ -1088,13 +1149,18 @@
       {L"\x3132\x6162", ByteString("\x32\x31\x62\x61\0\0", 6)},
   };
 
-  for (size_t i = 0; i < FX_ArraySize(utf16le_encode_cases); ++i) {
+  for (size_t i = 0; i < std::size(utf16le_encode_cases); ++i) {
     EXPECT_EQ(utf16le_encode_cases[i].bs,
               utf16le_encode_cases[i].ws.ToUTF16LE())
         << " for case number " << i;
   }
 }
 
+TEST(WideString, EncodeEntities) {
+  EXPECT_EQ(WideString(L"Symbols &<>'\".").EncodeEntities(),
+            L"Symbols &amp;&lt;&gt;&apos;&quot;.");
+}
+
 TEST(WideString, IsASCII) {
   EXPECT_TRUE(WideString(L"xy\u007fz").IsASCII());
   EXPECT_FALSE(WideString(L"xy\u0080z").IsASCII());
@@ -1157,7 +1223,7 @@
 
 TEST(WideString, ToDefANSI) {
   EXPECT_EQ("", WideString().ToDefANSI());
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   const char* kResult =
       "x"
       "?"
@@ -1209,7 +1275,7 @@
 
 TEST(WideString, FromDefANSI) {
   EXPECT_EQ(L"", WideString::FromDefANSI(ByteStringView()));
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   const wchar_t* kResult =
       L"x"
       L"\u20ac"
@@ -1244,7 +1310,7 @@
   cleared_vec.pop_back();
   WideStringView cleared_string(cleared_vec);
   EXPECT_EQ(0u, cleared_string.GetLength());
-  EXPECT_EQ(nullptr, cleared_string.raw_str());
+  EXPECT_FALSE(cleared_string.raw_str());
 }
 
 TEST(WideStringView, ElementAccess) {
@@ -1430,9 +1496,8 @@
   EXPECT_FALSE(empty_string.Find(L'a').has_value());
   EXPECT_FALSE(empty_string.Find(L'\0').has_value());
 
-  Optional<size_t> result;
   WideStringView single_string(L"a");
-  result = single_string.Find(L'a');
+  absl::optional<size_t> result = single_string.Find(L'a');
   ASSERT_TRUE(result.has_value());
   EXPECT_EQ(0u, result.value());
   EXPECT_FALSE(single_string.Find(L'b').has_value());
@@ -1582,9 +1647,9 @@
   EXPECT_TRUE(std::any_of(str.begin(), str.end(),
                           [](const wchar_t& c) { return c == L'a'; }));
 
-  EXPECT_TRUE(pdfium::ContainsValue(str, L'a'));
-  EXPECT_TRUE(pdfium::ContainsValue(str, L'b'));
-  EXPECT_FALSE(pdfium::ContainsValue(str, L'z'));
+  EXPECT_TRUE(pdfium::Contains(str, L'a'));
+  EXPECT_TRUE(pdfium::Contains(str, L'b'));
+  EXPECT_FALSE(pdfium::Contains(str, L'z'));
 }
 
 TEST(WideStringView, TrimmedRight) {
@@ -1625,7 +1690,7 @@
   EXPECT_EQ(L"cla", WideString::Format(L"%.3ls", L"clams"));
   EXPECT_EQ(L"\u043e\u043f", WideString(L"\u043e\u043f"));
 
-#if !defined(OS_MACOSX)
+#if !BUILDFLAG(IS_APPLE)
   // See https://bugs.chromium.org/p/pdfium/issues/detail?id=1132
   EXPECT_EQ(L"\u043e\u043f", WideString::Format(L"\u043e\u043f"));
   EXPECT_EQ(L"\u043e\u043f", WideString::Format(L"%ls", L"\u043e\u043f"));
@@ -1639,12 +1704,12 @@
   EXPECT_EQ(0u, empty_str.GetLength());
 
   const wchar_t* cstr = empty_str.c_str();
-  EXPECT_NE(nullptr, cstr);
+  EXPECT_TRUE(cstr);
   EXPECT_EQ(0u, wcslen(cstr));
 
   pdfium::span<const wchar_t> cspan = empty_str.span();
   EXPECT_TRUE(cspan.empty());
-  EXPECT_EQ(nullptr, cspan.data());
+  EXPECT_FALSE(cspan.data());
 }
 
 TEST(CFX_WidString, InitializerList) {
@@ -1722,9 +1787,9 @@
   EXPECT_TRUE(std::any_of(str.begin(), str.end(),
                           [](const wchar_t& c) { return c == L'a'; }));
 
-  EXPECT_TRUE(pdfium::ContainsValue(str, L'a'));
-  EXPECT_TRUE(pdfium::ContainsValue(str, L'b'));
-  EXPECT_FALSE(pdfium::ContainsValue(str, L'z'));
+  EXPECT_TRUE(pdfium::Contains(str, L'a'));
+  EXPECT_TRUE(pdfium::Contains(str, L'b'));
+  EXPECT_FALSE(pdfium::Contains(str, L'z'));
 }
 
 TEST(WideString, OStreamOverload) {
@@ -1991,15 +2056,29 @@
   }
 }
 
+TEST(WideString, FormatInteger) {
+  // Base case of 0.
+  EXPECT_EQ(L"0", WideString::FormatInteger(0));
+
+  // Positive ordinary number.
+  EXPECT_EQ(L"123456", WideString::FormatInteger(123456));
+
+  // Negative ordinary number.
+  EXPECT_EQ(L"-123456", WideString::FormatInteger(-123456));
+
+  // int limits.
+  EXPECT_EQ(L"2147483647", WideString::FormatInteger(INT_MAX));
+  EXPECT_EQ(L"-2147483648", WideString::FormatInteger(INT_MIN));
+}
+
 TEST(WideString, FX_HashCode_Wide) {
-  EXPECT_EQ(0u, FX_HashCode_GetW(L"", false));
-  EXPECT_EQ(65u, FX_HashCode_GetW(L"A", false));
-  EXPECT_EQ(97u, FX_HashCode_GetW(L"A", true));
-  EXPECT_EQ(1313 * 65u + 66u, FX_HashCode_GetW(L"AB", false));
-  EXPECT_EQ(FX_HashCode_GetAsIfW("AB\xff", false),
-            FX_HashCode_GetW(L"AB\xff", false));
-  EXPECT_EQ(FX_HashCode_GetAsIfW("AB\xff", true),
-            FX_HashCode_GetW(L"AB\xff", true));
+  EXPECT_EQ(0u, FX_HashCode_GetW(L""));
+  EXPECT_EQ(65u, FX_HashCode_GetW(L"A"));
+  EXPECT_EQ(97u, FX_HashCode_GetLoweredW(L"A"));
+  EXPECT_EQ(1313 * 65u + 66u, FX_HashCode_GetW(L"AB"));
+  EXPECT_EQ(FX_HashCode_GetAsIfW("AB\xff"), FX_HashCode_GetW(L"AB\xff"));
+  EXPECT_EQ(FX_HashCode_GetLoweredAsIfW("AB\xff"),
+            FX_HashCode_GetLoweredW(L"AB\xff"));
 }
 
 }  // namespace fxcrt
diff --git a/core/fxcrt/widetext_buffer.cpp b/core/fxcrt/widetext_buffer.cpp
new file mode 100644
index 0000000..145c056
--- /dev/null
+++ b/core/fxcrt/widetext_buffer.cpp
@@ -0,0 +1,86 @@
+// Copyright 2017 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/widetext_buffer.h"
+
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_system.h"
+
+namespace fxcrt {
+
+size_t WideTextBuffer::GetLength() const {
+  return GetSize() / sizeof(wchar_t);
+}
+
+pdfium::span<wchar_t> WideTextBuffer::GetWideSpan() {
+  return pdfium::make_span(reinterpret_cast<wchar_t*>(m_buffer.data()),
+                           GetLength());
+}
+
+pdfium::span<const wchar_t> WideTextBuffer::GetWideSpan() const {
+  return pdfium::make_span(reinterpret_cast<const wchar_t*>(m_buffer.data()),
+                           GetLength());
+}
+
+WideStringView WideTextBuffer::AsStringView() const {
+  return WideStringView(GetWideSpan());
+}
+
+WideString WideTextBuffer::MakeString() const {
+  return WideString(AsStringView());
+}
+
+void WideTextBuffer::AppendChar(wchar_t ch) {
+  pdfium::span<wchar_t> new_span = ExpandWideBuf(1);
+  new_span[0] = ch;
+}
+
+void WideTextBuffer::Delete(size_t start_index, size_t count) {
+  DeleteBuf(start_index * sizeof(wchar_t), count * sizeof(wchar_t));
+}
+
+void WideTextBuffer::AppendWideString(WideStringView str) {
+  AppendSpan(pdfium::as_bytes(str.span()));
+}
+
+WideTextBuffer& WideTextBuffer::operator<<(ByteStringView ascii) {
+  pdfium::span<wchar_t> new_span = ExpandWideBuf(ascii.GetLength());
+  for (size_t i = 0; i < ascii.GetLength(); ++i)
+    new_span[i] = ascii[i];
+  return *this;
+}
+
+WideTextBuffer& WideTextBuffer::operator<<(WideStringView str) {
+  AppendWideString(str);
+  return *this;
+}
+
+WideTextBuffer& WideTextBuffer::operator<<(const WideString& str) {
+  AppendWideString(str.AsStringView());
+  return *this;
+}
+
+WideTextBuffer& WideTextBuffer::operator<<(const wchar_t* lpsz) {
+  AppendWideString(WideStringView(lpsz));
+  return *this;
+}
+
+WideTextBuffer& WideTextBuffer::operator<<(const WideTextBuffer& buf) {
+  AppendWideString(buf.AsStringView());
+  return *this;
+}
+
+pdfium::span<wchar_t> WideTextBuffer::ExpandWideBuf(size_t char_count) {
+  size_t original_count = GetLength();
+  FX_SAFE_SIZE_T safe_bytes = char_count;
+  safe_bytes *= sizeof(wchar_t);
+  size_t bytes = safe_bytes.ValueOrDie();
+  ExpandBuf(bytes);
+  m_DataSize += bytes;
+  return GetWideSpan().subspan(original_count);
+}
+
+}  // namespace fxcrt
diff --git a/core/fxcrt/widetext_buffer.h b/core/fxcrt/widetext_buffer.h
new file mode 100644
index 0000000..5c29a0b
--- /dev/null
+++ b/core/fxcrt/widetext_buffer.h
@@ -0,0 +1,48 @@
+// Copyright 2017 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCRT_WIDETEXT_BUFFER_H_
+#define CORE_FXCRT_WIDETEXT_BUFFER_H_
+
+#include <stddef.h>
+
+#include "core/fxcrt/binary_buffer.h"
+#include "core/fxcrt/fx_string.h"
+#include "third_party/base/containers/span.h"
+
+namespace fxcrt {
+
+class WideTextBuffer final : public BinaryBuffer {
+ public:
+  // BinaryBuffer:
+  size_t GetLength() const override;
+
+  pdfium::span<wchar_t> GetWideSpan();
+  pdfium::span<const wchar_t> GetWideSpan() const;
+  WideStringView AsStringView() const;
+  WideString MakeString() const;
+
+  void AppendChar(wchar_t wch);
+  void Delete(size_t start_index, size_t count);
+
+  WideTextBuffer& operator<<(ByteStringView ascii);
+  WideTextBuffer& operator<<(const wchar_t* lpsz);
+  WideTextBuffer& operator<<(WideStringView str);
+  WideTextBuffer& operator<<(const WideString& str);
+  WideTextBuffer& operator<<(const WideTextBuffer& buf);
+
+ private:
+  void AppendWideString(WideStringView str);
+
+  // Returned span is the newly-expanded space.
+  pdfium::span<wchar_t> ExpandWideBuf(size_t char_count);
+};
+
+}  // namespace fxcrt
+
+using fxcrt::WideTextBuffer;
+
+#endif  // CORE_FXCRT_WIDETEXT_BUFFER_H_
diff --git a/core/fxcrt/widetext_buffer_unittest.cpp b/core/fxcrt/widetext_buffer_unittest.cpp
new file mode 100644
index 0000000..81c889d
--- /dev/null
+++ b/core/fxcrt/widetext_buffer_unittest.cpp
@@ -0,0 +1,62 @@
+// Copyright 2018 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/widetext_buffer.h"
+
+#include <utility>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace fxcrt {
+
+TEST(WideTextBuffer, EmptyBuf) {
+  WideTextBuffer wtb;
+  EXPECT_TRUE(wtb.GetWideSpan().empty());
+  EXPECT_TRUE(wtb.AsStringView().IsEmpty());
+  EXPECT_TRUE(wtb.MakeString().IsEmpty());
+}
+
+TEST(WideTextBuffer, OperatorLtLt) {
+  WideTextBuffer wtb;
+  wtb << "clams" << L"\u208c\u208e";
+  EXPECT_EQ(wtb.MakeString(), L"clams\u208c\u208e");
+}
+
+TEST(WideTextBuffer, Deletion) {
+  WideTextBuffer wtb;
+  wtb << L"ABCDEFG";
+  EXPECT_TRUE(wtb.AsStringView().EqualsASCII("ABCDEFG"));
+
+  wtb.Delete(1, 3);
+  EXPECT_TRUE(wtb.AsStringView().EqualsASCII("AEFG"));
+
+  wtb.Delete(1, 0);
+  EXPECT_TRUE(wtb.AsStringView().EqualsASCII("AEFG"));
+
+  wtb.Delete(0, 2);
+  EXPECT_TRUE(wtb.AsStringView().EqualsASCII("FG"));
+
+  wtb.Delete(0, 2);
+  EXPECT_TRUE(wtb.AsStringView().EqualsASCII(""));
+
+  wtb.Delete(0, 0);
+  EXPECT_TRUE(wtb.AsStringView().EqualsASCII(""));
+}
+
+TEST(WideTextBuffer, Move) {
+  WideTextBuffer wtb;
+  wtb << "clams";
+  EXPECT_EQ(wtb.MakeString(), L"clams");
+
+  WideTextBuffer wtb2(std::move(wtb));
+  EXPECT_EQ(wtb.MakeString(), L"");
+  EXPECT_EQ(wtb2.MakeString(), L"clams");
+
+  WideTextBuffer wtb3;
+  wtb3 = std::move(wtb2);
+  EXPECT_EQ(wtb2.MakeString(), L"");
+  EXPECT_EQ(wtb3.MakeString(), L"clams");
+}
+
+}  // namespace fxcrt
diff --git a/core/fxcrt/xml/cfx_xmlchardata.cpp b/core/fxcrt/xml/cfx_xmlchardata.cpp
index 1d42bd0..88b8920 100644
--- a/core/fxcrt/xml/cfx_xmlchardata.cpp
+++ b/core/fxcrt/xml/cfx_xmlchardata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,7 +22,7 @@
 }
 
 void CFX_XMLCharData::Save(
-    const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) {
+    const RetainPtr<IFX_RetainableWriteStream>& pXMLStream) {
   pXMLStream->WriteString("<![CDATA[");
   pXMLStream->WriteString(GetText().ToUTF8().AsStringView());
   pXMLStream->WriteString("]]>");
diff --git a/core/fxcrt/xml/cfx_xmlchardata.h b/core/fxcrt/xml/cfx_xmlchardata.h
index 4d3a7f0..356d555 100644
--- a/core/fxcrt/xml/cfx_xmlchardata.h
+++ b/core/fxcrt/xml/cfx_xmlchardata.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #ifndef CORE_FXCRT_XML_CFX_XMLCHARDATA_H_
 #define CORE_FXCRT_XML_CFX_XMLCHARDATA_H_
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 #include "core/fxcrt/xml/cfx_xmltext.h"
 
 class CFX_XMLDocument;
@@ -20,7 +20,7 @@
   // CFX_XMLNode
   Type GetType() const override;
   CFX_XMLNode* Clone(CFX_XMLDocument* doc) override;
-  void Save(const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) override;
+  void Save(const RetainPtr<IFX_RetainableWriteStream>& pXMLStream) override;
 };
 
 inline CFX_XMLCharData* ToXMLCharData(CFX_XMLNode* pNode) {
diff --git a/core/fxcrt/xml/cfx_xmlchardata_unittest.cpp b/core/fxcrt/xml/cfx_xmlchardata_unittest.cpp
index 60798bd..8afa2ca 100644
--- a/core/fxcrt/xml/cfx_xmlchardata_unittest.cpp
+++ b/core/fxcrt/xml/cfx_xmlchardata_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxcrt/xml/cfx_xmldocument.cpp b/core/fxcrt/xml/cfx_xmldocument.cpp
index 1c6656d..f2c38da 100644
--- a/core/fxcrt/xml/cfx_xmldocument.cpp
+++ b/core/fxcrt/xml/cfx_xmldocument.cpp
@@ -1,15 +1,15 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxcrt/xml/cfx_xmldocument.h"
 
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlinstruction.h"
 
-CFX_XMLDocument::CFX_XMLDocument() {
-  root_ = CreateNode<CFX_XMLElement>(L"root");
-}
+CFX_XMLDocument::CFX_XMLDocument()
+    : root_(CreateNode<CFX_XMLElement>(L"root")) {}
 
 CFX_XMLDocument::~CFX_XMLDocument() = default;
 
diff --git a/core/fxcrt/xml/cfx_xmldocument.h b/core/fxcrt/xml/cfx_xmldocument.h
index 9931314..053cc61 100644
--- a/core/fxcrt/xml/cfx_xmldocument.h
+++ b/core/fxcrt/xml/cfx_xmldocument.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,9 +10,8 @@
 #include <vector>
 
 #include "core/fxcrt/unowned_ptr.h"
-#include "core/fxcrt/xml/cfx_xmlelement.h"
-#include "third_party/base/ptr_util.h"
 
+class CFX_XMLElement;
 class CFX_XMLNode;
 
 class CFX_XMLDocument {
@@ -20,11 +19,11 @@
   CFX_XMLDocument();
   ~CFX_XMLDocument();
 
-  CFX_XMLElement* GetRoot() const { return root_.Get(); }
+  CFX_XMLElement* GetRoot() const { return root_; }
 
   template <typename T, typename... Args>
   T* CreateNode(Args&&... args) {
-    nodes_.push_back(pdfium::MakeUnique<T>(std::forward<Args>(args)...));
+    nodes_.push_back(std::make_unique<T>(std::forward<Args>(args)...));
     return static_cast<T*>(nodes_.back().get());
   }
 
diff --git a/core/fxcrt/xml/cfx_xmldocument_unittest.cpp b/core/fxcrt/xml/cfx_xmldocument_unittest.cpp
index 8043cc6..7faabff 100644
--- a/core/fxcrt/xml/cfx_xmldocument_unittest.cpp
+++ b/core/fxcrt/xml/cfx_xmldocument_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxcrt/xml/cfx_xmlelement.cpp b/core/fxcrt/xml/cfx_xmlelement.cpp
index b999f5c..81b0865 100644
--- a/core/fxcrt/xml/cfx_xmlelement.cpp
+++ b/core/fxcrt/xml/cfx_xmlelement.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,16 +6,14 @@
 
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 
-#include <utility>
-
-#include "core/fxcrt/cfx_widetextbuf.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/xml/cfx_xmlchardata.h"
 #include "core/fxcrt/xml/cfx_xmldocument.h"
 #include "core/fxcrt/xml/cfx_xmltext.h"
+#include "third_party/base/check.h"
 
 CFX_XMLElement::CFX_XMLElement(const WideString& wsTag) : name_(wsTag) {
-  ASSERT(!name_.IsEmpty());
+  DCHECK(!name_.IsEmpty());
 }
 
 CFX_XMLElement::~CFX_XMLElement() = default;
@@ -69,24 +67,24 @@
 }
 
 WideString CFX_XMLElement::GetTextData() const {
-  CFX_WideTextBuf buffer;
+  WideString buffer;
   for (CFX_XMLNode* pChild = GetFirstChild(); pChild;
        pChild = pChild->GetNextSibling()) {
     CFX_XMLText* pText = ToXMLText(pChild);
     if (pText)
-      buffer << pText->GetText();
+      buffer += pText->GetText();
   }
-  return buffer.MakeString();
+  return buffer;
 }
 
 void CFX_XMLElement::Save(
-    const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) {
+    const RetainPtr<IFX_RetainableWriteStream>& pXMLStream) {
   ByteString bsNameEncoded = name_.ToUTF8();
 
   pXMLStream->WriteString("<");
   pXMLStream->WriteString(bsNameEncoded.AsStringView());
 
-  for (auto it : attrs_) {
+  for (const auto& it : attrs_) {
     // Note, the space between attributes is added by AttributeToString which
     // writes a blank as the first character.
     pXMLStream->WriteString(
@@ -150,7 +148,7 @@
   WideString ret = L" ";
   ret += name;
   ret += L"=\"";
-  ret += EncodeEntities(value);
+  ret += value.EncodeEntities();
   ret += L"\"";
   return ret;
 }
diff --git a/core/fxcrt/xml/cfx_xmlelement.h b/core/fxcrt/xml/cfx_xmlelement.h
index efc9fbb..d48bf9f 100644
--- a/core/fxcrt/xml/cfx_xmlelement.h
+++ b/core/fxcrt/xml/cfx_xmlelement.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 
 #include <map>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
 
 class CFX_XMLDocument;
@@ -22,7 +22,7 @@
   // CFX_XMLNode
   Type GetType() const override;
   CFX_XMLNode* Clone(CFX_XMLDocument* doc) override;
-  void Save(const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) override;
+  void Save(const RetainPtr<IFX_RetainableWriteStream>& pXMLStream) override;
 
   const WideString& GetName() const { return name_; }
 
@@ -32,7 +32,6 @@
   bool HasAttribute(const WideString& name) const;
   void SetAttribute(const WideString& name, const WideString& value);
   WideString GetAttribute(const WideString& name) const;
-
   void RemoveAttribute(const WideString& name);
 
   CFX_XMLElement* GetFirstChildNamed(WideStringView name) const;
diff --git a/core/fxcrt/xml/cfx_xmlelement_unittest.cpp b/core/fxcrt/xml/cfx_xmlelement_unittest.cpp
index 0053358..59d4b40 100644
--- a/core/fxcrt/xml/cfx_xmlelement_unittest.cpp
+++ b/core/fxcrt/xml/cfx_xmlelement_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxcrt/xml/cfx_xmlinstruction.cpp b/core/fxcrt/xml/cfx_xmlinstruction.cpp
index ac01f4e..74389bb 100644
--- a/core/fxcrt/xml/cfx_xmlinstruction.cpp
+++ b/core/fxcrt/xml/cfx_xmlinstruction.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,6 @@
 
 #include "core/fxcrt/xml/cfx_xmlinstruction.h"
 
-#include <utility>
-
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/xml/cfx_xmldocument.h"
@@ -40,7 +38,7 @@
 }
 
 void CFX_XMLInstruction::Save(
-    const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) {
+    const RetainPtr<IFX_RetainableWriteStream>& pXMLStream) {
   if (name_.EqualsASCIINoCase("xml")) {
     pXMLStream->WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
     return;
diff --git a/core/fxcrt/xml/cfx_xmlinstruction.h b/core/fxcrt/xml/cfx_xmlinstruction.h
index f4ba112..72cdbdf 100644
--- a/core/fxcrt/xml/cfx_xmlinstruction.h
+++ b/core/fxcrt/xml/cfx_xmlinstruction.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
 
 class CFX_XMLDocument;
@@ -22,7 +22,7 @@
   // CFX_XMLNode
   Type GetType() const override;
   CFX_XMLNode* Clone(CFX_XMLDocument* doc) override;
-  void Save(const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) override;
+  void Save(const RetainPtr<IFX_RetainableWriteStream>& pXMLStream) override;
 
   bool IsOriginalXFAVersion() const;
   bool IsAcrobat() const;
diff --git a/core/fxcrt/xml/cfx_xmlinstruction_unittest.cpp b/core/fxcrt/xml/cfx_xmlinstruction_unittest.cpp
index 97d9ddf..3b7f1d9 100644
--- a/core/fxcrt/xml/cfx_xmlinstruction_unittest.cpp
+++ b/core/fxcrt/xml/cfx_xmlinstruction_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
 #include "core/fxcrt/xml/cfx_xmldocument.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlparser.h"
@@ -87,7 +87,7 @@
       "<?acrobat http://www.xfa.org/schema/xfa-template/3.3/ Display:1 ?>\n"
       "<node></node>";
 
-  auto in_stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+  auto in_stream = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
       pdfium::as_bytes(pdfium::make_span(input)));
 
   CFX_XMLParser parser(in_stream);
@@ -120,7 +120,7 @@
       "<?acrobat http://www.xfa.org/schema/xfa-template/3.3/ Display:1 ?>\n"
       "</node>";
 
-  auto in_stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+  auto in_stream = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
       pdfium::as_bytes(pdfium::make_span(input)));
 
   CFX_XMLParser parser(in_stream);
diff --git a/core/fxcrt/xml/cfx_xmlnode.cpp b/core/fxcrt/xml/cfx_xmlnode.cpp
index 94b035b..4ce2a60 100644
--- a/core/fxcrt/xml/cfx_xmlnode.cpp
+++ b/core/fxcrt/xml/cfx_xmlnode.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -21,13 +21,3 @@
 
   return pParent;
 }
-
-WideString CFX_XMLNode::EncodeEntities(const WideString& value) {
-  WideString ret = value;
-  ret.Replace(L"&", L"&amp;");
-  ret.Replace(L"<", L"&lt;");
-  ret.Replace(L">", L"&gt;");
-  ret.Replace(L"\'", L"&apos;");
-  ret.Replace(L"\"", L"&quot;");
-  return ret;
-}
diff --git a/core/fxcrt/xml/cfx_xmlnode.h b/core/fxcrt/xml/cfx_xmlnode.h
index d4d48b1..5934bc7 100644
--- a/core/fxcrt/xml/cfx_xmlnode.h
+++ b/core/fxcrt/xml/cfx_xmlnode.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,6 @@
 #define CORE_FXCRT_XML_CFX_XMLNODE_H_
 
 #include "core/fxcrt/fx_stream.h"
-#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/tree_node.h"
 
@@ -28,13 +27,10 @@
 
   virtual Type GetType() const = 0;
   virtual CFX_XMLNode* Clone(CFX_XMLDocument* doc) = 0;
-  virtual void Save(const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) = 0;
+  virtual void Save(const RetainPtr<IFX_RetainableWriteStream>& pXMLStream) = 0;
 
   CFX_XMLNode* GetRoot();
   void InsertChildNode(CFX_XMLNode* pNode, int32_t index);
-
- protected:
-  WideString EncodeEntities(const WideString& value);
 };
 
 #endif  // CORE_FXCRT_XML_CFX_XMLNODE_H_
diff --git a/core/fxcrt/xml/cfx_xmlnode_unittest.cpp b/core/fxcrt/xml/cfx_xmlnode_unittest.cpp
index c879cfd..ad07579 100644
--- a/core/fxcrt/xml/cfx_xmlnode_unittest.cpp
+++ b/core/fxcrt/xml/cfx_xmlnode_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -36,7 +36,7 @@
   node1.AppendLastChild(&node2);
   node2.AppendLastChild(&node3);
 
-  EXPECT_EQ(nullptr, node1.GetParent());
+  EXPECT_FALSE(node1.GetParent());
   EXPECT_EQ(&node1, node2.GetParent());
   EXPECT_EQ(&node2, node3.GetParent());
 }
diff --git a/core/fxcrt/xml/cfx_xmlparser.cpp b/core/fxcrt/xml/cfx_xmlparser.cpp
index 9393bbd..6ca9ea4 100644
--- a/core/fxcrt/xml/cfx_xmlparser.cpp
+++ b/core/fxcrt/xml/cfx_xmlparser.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,13 +6,16 @@
 
 #include "core/fxcrt/xml/cfx_xmlparser.h"
 
+#include <stdint.h>
+
 #include <algorithm>
-#include <cwctype>
 #include <iterator>
 #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"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
@@ -22,7 +25,8 @@
 #include "core/fxcrt/xml/cfx_xmlinstruction.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
 #include "core/fxcrt/xml/cfx_xmltext.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
@@ -39,7 +43,7 @@
   bool bStartChar;
 };
 
-const FX_XMLNAMECHAR g_XMLNameChars[] = {
+constexpr FX_XMLNAMECHAR kXMLNameChars[] = {
     {L'-', L'.', false},    {L'0', L'9', false},     {L':', L':', false},
     {L'A', L'Z', true},     {L'_', L'_', true},      {L'a', L'z', true},
     {0xB7, 0xB7, false},    {0xC0, 0xD6, true},      {0xD8, 0xF6, true},
@@ -54,20 +58,20 @@
 // static
 bool CFX_XMLParser::IsXMLNameChar(wchar_t ch, bool bFirstChar) {
   auto* it = std::lower_bound(
-      std::begin(g_XMLNameChars), std::end(g_XMLNameChars), ch,
+      std::begin(kXMLNameChars), std::end(kXMLNameChars), ch,
       [](const FX_XMLNAMECHAR& arg, wchar_t ch) { return arg.wEnd < ch; });
-  return it != std::end(g_XMLNameChars) && ch >= it->wStart &&
+  return it != std::end(kXMLNameChars) && ch >= it->wStart &&
          (!bFirstChar || it->bStartChar);
 }
 
 CFX_XMLParser::CFX_XMLParser(const RetainPtr<IFX_SeekableReadStream>& pStream) {
-  ASSERT(pStream);
+  DCHECK(pStream);
 
   auto proxy = pdfium::MakeRetain<CFX_SeekableStreamProxy>(pStream);
-  uint16_t wCodePage = proxy->GetCodePage();
-  if (wCodePage != FX_CODEPAGE_UTF16LE && wCodePage != FX_CODEPAGE_UTF16BE &&
-      wCodePage != FX_CODEPAGE_UTF8) {
-    proxy->SetCodePage(FX_CODEPAGE_UTF8);
+  FX_CodePage wCodePage = proxy->GetCodePage();
+  if (wCodePage != FX_CodePage::kUTF16LE &&
+      wCodePage != FX_CodePage::kUTF16BE && wCodePage != FX_CodePage::kUTF8) {
+    proxy->SetCodePage(FX_CodePage::kUTF8);
   }
   stream_ = proxy;
 
@@ -80,9 +84,9 @@
 CFX_XMLParser::~CFX_XMLParser() = default;
 
 std::unique_ptr<CFX_XMLDocument> CFX_XMLParser::Parse() {
-  auto doc = pdfium::MakeUnique<CFX_XMLDocument>();
+  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;
 }
 
@@ -95,17 +99,16 @@
   if (!alloc_size_safe.IsValid())
     return false;
 
-  FX_FILESIZE current_buffer_idx = 0;
-  FX_FILESIZE buffer_size = 0;
+  size_t current_buffer_idx = 0;
+  size_t buffer_size = 0;
 
-  std::vector<wchar_t, FxAllocAllocator<wchar_t>> buffer;
+  DataVector<wchar_t> buffer;
   buffer.resize(alloc_size_safe.ValueOrDie());
 
   std::stack<wchar_t> character_to_skip_too_stack;
   std::stack<CFX_XMLNode::Type> node_type_stack;
   WideString current_attribute_name;
   FDE_XmlSyntaxState current_parser_state = FDE_XmlSyntaxState::Text;
-  int32_t iCount = 0;
   wchar_t current_quote_character = 0;
   wchar_t current_character_to_skip_to = 0;
 
@@ -262,7 +265,7 @@
           break;
         case FDE_XmlSyntaxState::AttriValue:
           if (ch == current_quote_character) {
-            if (entity_start_ > -1)
+            if (entity_start_.has_value())
               return false;
 
             current_quote_character = 0;
@@ -328,7 +331,6 @@
               }
 
               current_node_ = current_node_->GetParent();
-              iCount++;
             } else if (!IsXMLWhiteSpace(ch)) {
               return false;
             }
@@ -457,8 +459,6 @@
             current_buffer_idx++;
           }
           break;
-        default:
-          break;
       }
     }
   }
@@ -470,27 +470,27 @@
 void CFX_XMLParser::ProcessTextChar(wchar_t character) {
   current_text_.push_back(character);
 
-  if (entity_start_ > -1 && character == L';') {
+  if (entity_start_.has_value() && character == L';') {
     // Copy the entity out into a string and remove from the vector. When we
     // copy the entity we don't want to copy out the & or the ; so we start
     // shifted by one and want to copy 2 less characters in total.
-    WideString csEntity(current_text_.data() + entity_start_ + 1,
-                        current_text_.size() - entity_start_ - 2);
-    current_text_.erase(current_text_.begin() + entity_start_,
+    WideString csEntity(current_text_.data() + entity_start_.value() + 1,
+                        current_text_.size() - entity_start_.value() - 2);
+    current_text_.erase(current_text_.begin() + entity_start_.value(),
                         current_text_.end());
 
-    int32_t iLen = csEntity.GetLength();
+    size_t iLen = csEntity.GetLength();
     if (iLen > 0) {
       if (csEntity[0] == L'#') {
         uint32_t ch = 0;
         if (iLen > 1 && csEntity[1] == L'x') {
-          for (int32_t i = 2; i < iLen; i++) {
+          for (size_t i = 2; i < iLen; i++) {
             if (!FXSYS_IsHexDigit(csEntity[i]))
               break;
             ch = (ch << 4) + FXSYS_HexCharToInt(csEntity[i]);
           }
         } else {
-          for (int32_t i = 1; i < iLen; i++) {
+          for (size_t i = 1; i < iLen; i++) {
             if (!FXSYS_IsDecimalDigit(csEntity[i]))
               break;
             ch = ch * 10 + FXSYS_DecimalCharToInt(csEntity[i]);
@@ -503,22 +503,21 @@
         if (character != 0)
           current_text_.push_back(character);
       } else {
-        if (csEntity.Compare(L"amp") == 0) {
+        if (csEntity == L"amp") {
           current_text_.push_back(L'&');
-        } else if (csEntity.Compare(L"lt") == 0) {
+        } else if (csEntity == L"lt") {
           current_text_.push_back(L'<');
-        } else if (csEntity.Compare(L"gt") == 0) {
+        } else if (csEntity == L"gt") {
           current_text_.push_back(L'>');
-        } else if (csEntity.Compare(L"apos") == 0) {
+        } else if (csEntity == L"apos") {
           current_text_.push_back(L'\'');
-        } else if (csEntity.Compare(L"quot") == 0) {
+        } else if (csEntity == L"quot") {
           current_text_.push_back(L'"');
         }
       }
     }
-
-    entity_start_ = -1;
-  } else if (entity_start_ < 0 && character == L'&') {
+    entity_start_ = absl::nullopt;
+  } else if (!entity_start_.has_value() && character == L'&') {
     entity_start_ = current_text_.size() - 1;
   }
 }
@@ -535,7 +534,7 @@
 
 WideString CFX_XMLParser::GetTextData() {
   WideString ret(current_text_.data(), current_text_.size());
-  entity_start_ = -1;
+  entity_start_ = absl::nullopt;
   current_text_.clear();
   current_text_.reserve(kCurrentTextReserve);
   return ret;
diff --git a/core/fxcrt/xml/cfx_xmlparser.h b/core/fxcrt/xml/cfx_xmlparser.h
index ef171c6..268c9f2 100644
--- a/core/fxcrt/xml/cfx_xmlparser.h
+++ b/core/fxcrt/xml/cfx_xmlparser.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,15 +8,15 @@
 #define CORE_FXCRT_XML_CFX_XMLPARSER_H_
 
 #include <memory>
-#include <vector>
 
-#include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CFX_SeekableStreamProxy;
 class CFX_XMLDocument;
-class CFX_XMLElement;
 class CFX_XMLNode;
 class IFX_SeekableReadStream;
 
@@ -54,11 +54,11 @@
   void ProcessTextChar(wchar_t ch);
   void ProcessTargetData();
 
-  CFX_XMLNode* current_node_ = nullptr;
+  UnownedPtr<CFX_XMLNode> current_node_;
   RetainPtr<CFX_SeekableStreamProxy> stream_;
-  std::vector<wchar_t, FxAllocAllocator<wchar_t>> current_text_;
+  DataVector<wchar_t> current_text_;
   size_t xml_plane_size_ = 1024;
-  int32_t entity_start_ = -1;
+  absl::optional<size_t> entity_start_;
 };
 
 #endif  // CORE_FXCRT_XML_CFX_XMLPARSER_H_
diff --git a/core/fxcrt/xml/cfx_xmlparser_unittest.cpp b/core/fxcrt/xml/cfx_xmlparser_unittest.cpp
index a1fa8f3..c692a0b 100644
--- a/core/fxcrt/xml/cfx_xmlparser_unittest.cpp
+++ b/core/fxcrt/xml/cfx_xmlparser_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/xml/cfx_xmldocument.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
@@ -17,7 +17,7 @@
  public:
   std::unique_ptr<CFX_XMLDocument> Parse(pdfium::span<const char> input) {
     CFX_XMLParser parser(
-        pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(pdfium::as_bytes(input)));
+        pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::as_bytes(input)));
     return parser.Parse();
   }
 };
@@ -290,6 +290,27 @@
   EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L'-', true));
   EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(L'-', false));
 
+  EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L'.', true));
+  EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(L'.', false));
+
+  EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L'0', true));
+  EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(L'0', false));
+
+  EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(L'a', true));
+  EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(L'a', false));
+
+  EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(L'A', true));
+  EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(L'A', false));
+
+  EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L'(', false));
+  EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L'(', true));
+  EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L')', false));
+  EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L')', true));
+  EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L'[', false));
+  EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L'[', true));
+  EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L']', false));
+  EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L']', true));
+
   EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(0x2069, true));
   EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(0x2070, true));
   EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(0x2073, true));
diff --git a/core/fxcrt/xml/cfx_xmltext.cpp b/core/fxcrt/xml/cfx_xmltext.cpp
index 67c35a5..bfcf368 100644
--- a/core/fxcrt/xml/cfx_xmltext.cpp
+++ b/core/fxcrt/xml/cfx_xmltext.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -20,6 +20,6 @@
   return doc->CreateNode<CFX_XMLText>(text_);
 }
 
-void CFX_XMLText::Save(const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) {
-  pXMLStream->WriteString(EncodeEntities(GetText()).ToUTF8().AsStringView());
+void CFX_XMLText::Save(const RetainPtr<IFX_RetainableWriteStream>& pXMLStream) {
+  pXMLStream->WriteString(GetText().EncodeEntities().ToUTF8().AsStringView());
 }
diff --git a/core/fxcrt/xml/cfx_xmltext.h b/core/fxcrt/xml/cfx_xmltext.h
index 72ca242..096801a 100644
--- a/core/fxcrt/xml/cfx_xmltext.h
+++ b/core/fxcrt/xml/cfx_xmltext.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #ifndef CORE_FXCRT_XML_CFX_XMLTEXT_H_
 #define CORE_FXCRT_XML_CFX_XMLTEXT_H_
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
 
 class CFX_XMLDocument;
@@ -20,7 +20,7 @@
   // CFX_XMLNode
   Type GetType() const override;
   CFX_XMLNode* Clone(CFX_XMLDocument* doc) override;
-  void Save(const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) override;
+  void Save(const RetainPtr<IFX_RetainableWriteStream>& pXMLStream) override;
 
   const WideString& GetText() const { return text_; }
   void SetText(const WideString& wsText) { text_ = wsText; }
diff --git a/core/fxcrt/xml/cfx_xmltext_unittest.cpp b/core/fxcrt/xml/cfx_xmltext_unittest.cpp
index 0df003a..55e879c 100644
--- a/core/fxcrt/xml/cfx_xmltext_unittest.cpp
+++ b/core/fxcrt/xml/cfx_xmltext_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxge/Android.bp b/core/fxge/Android.bp
index 973ca03..3518bd6 100644
--- a/core/fxge/Android.bp
+++ b/core/fxge/Android.bp
@@ -23,6 +23,7 @@
         "cfx_unicodeencodingex.cpp",
         // is_win
         "dib/cfx_dibextractor.cpp",
+        "cfx_windowsrenderdevice.cpp",
         // is_linux
         "fx_ge_linux.cpp",
     ],
diff --git a/core/fxge/BUILD.gn b/core/fxge/BUILD.gn
index 04742d0..a3d2b0e 100644
--- a/core/fxge/BUILD.gn
+++ b/core/fxge/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -6,25 +6,23 @@
 import("../../pdfium.gni")
 import("../../testing/test.gni")
 
-config("fxge_warnings") {
-  visibility = [ ":*" ]
-  if (is_clang) {
-    cflags = [
-      # http://code.google.com/p/pdfium/issues/detail?id=188
-      "-Wno-switch",
-    ]
-  }
-}
-
 source_set("fxge") {
   sources = [
+    "agg/fx_agg_driver.cpp",
+    "agg/fx_agg_driver.h",
+    "calculate_pitch.cpp",
+    "calculate_pitch.h",
     "cfx_cliprgn.cpp",
     "cfx_cliprgn.h",
     "cfx_color.cpp",
     "cfx_color.h",
+    "cfx_defaultrenderdevice.cpp",
     "cfx_defaultrenderdevice.h",
+    "cfx_drawutils.cpp",
+    "cfx_drawutils.h",
     "cfx_face.cpp",
     "cfx_face.h",
+    "cfx_fillrenderoptions.h",
     "cfx_folderfontinfo.cpp",
     "cfx_folderfontinfo.h",
     "cfx_font.cpp",
@@ -45,12 +43,13 @@
     "cfx_graphstate.h",
     "cfx_graphstatedata.cpp",
     "cfx_graphstatedata.h",
-    "cfx_pathdata.cpp",
-    "cfx_pathdata.h",
+    "cfx_path.cpp",
+    "cfx_path.h",
     "cfx_renderdevice.cpp",
     "cfx_renderdevice.h",
     "cfx_substfont.cpp",
     "cfx_substfont.h",
+    "cfx_textrenderoptions.h",
     "cfx_unicodeencoding.cpp",
     "cfx_unicodeencoding.h",
     "dib/cfx_bitmapcomposer.cpp",
@@ -73,7 +72,8 @@
     "dib/cfx_scanlinecompositor.h",
     "dib/cstretchengine.cpp",
     "dib/cstretchengine.h",
-    "dib/fx_dib_main.cpp",
+    "dib/fx_dib.cpp",
+    "dib/fx_dib.h",
     "dib/scanlinecomposer_iface.h",
     "fontdata/chromefontdata/FoxitDingbats.cpp",
     "fontdata/chromefontdata/FoxitFixed.cpp",
@@ -93,17 +93,14 @@
     "fontdata/chromefontdata/FoxitSymbol.cpp",
     "fontdata/chromefontdata/chromefontdata.h",
     "freetype/fx_freetype.cpp",
-    "fx_dib.h",
+    "freetype/fx_freetype.h",
     "fx_font.cpp",
     "fx_font.h",
-    "fx_freetype.h",
-    "fx_ge_fontmap.cpp",
     "render_defines.h",
     "renderdevicedriver_iface.cpp",
     "renderdevicedriver_iface.h",
     "scoped_font_transform.cpp",
     "scoped_font_transform.h",
-    "scoped_font_transform.h",
     "systemfontinfo_iface.h",
     "text_char_pos.cpp",
     "text_char_pos.h",
@@ -112,11 +109,16 @@
   ]
 
   configs += [
-    ":fxge_warnings",
-    "../../:pdfium_core_config",
+    "../../:pdfium_strict_config",
+    "../../:pdfium_noshorten_config",
   ]
 
-  deps = [ "../fxcrt" ]
+  deps = [
+    "../../third_party:fx_agg",
+    "../fxcrt",
+  ]
+
+  public_deps = []
 
   if (is_component_build || use_system_freetype) {
     # ft_adobe_glyph_list is not exported from the Freetype shared library so we
@@ -131,15 +133,12 @@
     ]
   }
 
-  if (pdf_use_skia || pdf_use_skia_paths) {
-    sources += [ "skia/fx_skia_device.cpp" ]
-    deps += [ "//skia" ]
-  } else {
+  if (pdf_use_skia) {
     sources += [
-      "agg/fx_agg_driver.cpp",
-      "agg/fx_agg_driver.h",
+      "skia/cfx_dibbase_skia.cpp",
+      "skia/fx_skia_device.cpp",
     ]
-    deps += [ "../../third_party:fx_agg" ]
+    public_deps += [ "//skia" ]
   }
 
   if (is_android) {
@@ -154,39 +153,49 @@
       "android/cfpf_skiapathfont.h",
       "android/cfx_androidfontinfo.cpp",
       "android/cfx_androidfontinfo.h",
-      "android/fx_android_imp.cpp",
+      "android/fx_android_impl.cpp",
     ]
   }
 
-  if (is_linux) {
-    sources += [ "fx_ge_linux.cpp" ]
+  if (is_linux || is_chromeos || is_fuchsia) {
+    sources += [ "linux/fx_linux_impl.cpp" ]
   }
 
   if (is_mac) {
     sources += [
-      "apple/apple_int.h",
+      "apple/fx_apple_impl.cpp",
       "apple/fx_apple_platform.cpp",
-      "apple/fx_mac_imp.cpp",
+      "apple/fx_apple_platform.h",
       "apple/fx_quartz_device.cpp",
+      "apple/fx_quartz_device.h",
     ]
-    libs = [ "CoreGraphics.framework" ]
+    frameworks = [ "CoreGraphics.framework" ]
   }
 
   if (is_win) {
     sources += [
+      "cfx_windowsrenderdevice.cpp",
       "cfx_windowsrenderdevice.h",
-      "dib/cfx_dibextractor.cpp",
-      "dib/cfx_dibextractor.h",
+      "win32/cfx_psfonttracker.cpp",
+      "win32/cfx_psfonttracker.h",
       "win32/cfx_psrenderer.cpp",
       "win32/cfx_psrenderer.h",
-      "win32/cfx_windowsdib.h",
+      "win32/cgdi_device_driver.cpp",
+      "win32/cgdi_device_driver.h",
+      "win32/cgdi_display_driver.cpp",
+      "win32/cgdi_display_driver.h",
+      "win32/cgdi_plus_ext.cpp",
+      "win32/cgdi_plus_ext.h",
+      "win32/cgdi_printer_driver.cpp",
+      "win32/cgdi_printer_driver.h",
+      "win32/cps_printer_driver.cpp",
+      "win32/cps_printer_driver.h",
       "win32/cpsoutput.cpp",
       "win32/cpsoutput.h",
-      "win32/fx_win32_device.cpp",
-      "win32/fx_win32_dib.cpp",
-      "win32/fx_win32_gdipext.cpp",
-      "win32/fx_win32_print.cpp",
-      "win32/win32_int.h",
+      "win32/ctext_only_printer_driver.cpp",
+      "win32/ctext_only_printer_driver.h",
+      "win32/cwin32_platform.cpp",
+      "win32/cwin32_platform.h",
     ]
     configs -= [ "//build/config/win:lean_and_mean" ]
   }
@@ -196,8 +205,12 @@
 
 pdfium_unittest_source_set("unittests") {
   sources = [
+    "cfx_defaultrenderdevice_unittest.cpp",
+    "cfx_folderfontinfo_unittest.cpp",
     "cfx_fontmapper_unittest.cpp",
+    "cfx_path_unittest.cpp",
     "dib/cfx_cmyk_to_srgb_unittest.cpp",
+    "dib/cfx_dibbase_unittest.cpp",
     "dib/cfx_dibitmap_unittest.cpp",
     "dib/cstretchengine_unittest.cpp",
     "fx_font_unittest.cpp",
@@ -208,6 +221,10 @@
     "../fpdfapi/parser",
   ]
   pdfium_root_dir = "../../"
+
+  if (is_win) {
+    sources += [ "win32/cfx_psrenderer_unittest.cpp" ]
+  }
 }
 
 pdfium_embeddertest_source_set("embeddertests") {
@@ -215,17 +232,19 @@
   deps = []
   pdfium_root_dir = "../../"
 
-  if (pdf_use_skia || pdf_use_skia_paths) {
+  if (pdf_use_skia) {
     sources += [ "skia/fx_skia_device_embeddertest.cpp" ]
     deps += [
       ":fxge",
       "../../fpdfsdk",
+      "../fpdfapi/page",
+      "../fpdfapi/render",
       "//skia",
     ]
   }
 
   if (is_win) {
-    sources += [ "win32/fx_win32_device_embeddertest.cpp" ]
+    sources += [ "cfx_windowsrenderdevice_embeddertest.cpp" ]
     deps += [
       ":fxge",
       "../fxcodec",
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 b2a7986..e1fd404 100644
--- a/core/fxge/agg/fx_agg_driver.cpp
+++ b/core/fxge/agg/fx_agg_driver.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,25 +6,34 @@
 
 #include "core/fxge/agg/fx_agg_driver.h"
 
+#include <math.h>
+#include <stdint.h>
+
 #include <algorithm>
 #include <utility>
 
 #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"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/dib/cfx_imagerenderer.h"
 #include "core/fxge/dib/cfx_imagestretcher.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/span.h"
+#include "third_party/base/notreached.h"
 
 // Ignore fallthrough warnings in agg23 headers.
 #if defined(__clang__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
 #endif
+#include "third_party/agg23/agg_clip_liang_barsky.h"
 #include "third_party/agg23/agg_conv_dash.h"
 #include "third_party/agg23/agg_conv_stroke.h"
 #include "third_party/agg23/agg_curves.h"
@@ -37,33 +46,14 @@
 #pragma GCC diagnostic pop
 #endif
 
+namespace pdfium {
 namespace {
 
 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));
-}
-
-void RgbByteOrderSetPixel(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                          int x,
-                          int y,
-                          uint32_t argb) {
-  if (x < 0 || x >= pBitmap->GetWidth() || y < 0 || y >= pBitmap->GetHeight())
-    return;
-
-  uint8_t* pos = pBitmap->GetBuffer() + y * pBitmap->GetPitch() +
-                 x * pBitmap->GetBPP() / 8;
-  if (pBitmap->GetFormat() == FXDIB_Argb) {
-    FXARGB_SETRGBORDERDIB(pos, argb);
-    return;
-  }
-
-  int alpha = FXARGB_A(argb);
-  pos[0] = (FXARGB_R(argb) * alpha + pos[0] * (255 - alpha)) / 255;
-  pos[1] = (FXARGB_G(argb) * alpha + pos[1] * (255 - alpha)) / 255;
-  pos[2] = (FXARGB_B(argb) * alpha + pos[2] * (255 - alpha)) / 255;
+  return CFX_PointF(std::clamp(pos.x, -kMaxPos, kMaxPos),
+                    std::clamp(pos.y, -kMaxPos, kMaxPos));
 }
 
 void RgbByteOrderCompositeRect(const RetainPtr<CFX_DIBitmap>& pBitmap,
@@ -84,15 +74,13 @@
   int src_b = FXARGB_B(argb);
   int Bpp = pBitmap->GetBPP() / 8;
   int dib_argb = FXARGB_TOBGRORDERDIB(argb);
-  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 =
-          pBuffer + row * pBitmap->GetPitch() + rect.left * Bpp;
+          pBuffer.subspan(row * pBitmap->GetPitch() + rect.left * Bpp).data();
       if (Bpp == 4) {
-        uint32_t* scan = reinterpret_cast<uint32_t*>(dest_scan);
-        for (int col = 0; col < width; col++)
-          *scan++ = dib_argb;
+        std::fill_n(reinterpret_cast<uint32_t*>(dest_scan), width, dib_argb);
       } else {
         for (int col = 0; col < width; col++) {
           *dest_scan++ = src_r;
@@ -103,15 +91,15 @@
     }
     return;
   }
-  bool bAlpha = pBitmap->HasAlpha();
+  bool bAlpha = pBitmap->IsAlphaFormat();
   for (int row = rect.top; row < rect.bottom; row++) {
-    uint8_t* dest_scan = pBuffer + row * pBitmap->GetPitch() + rect.left * Bpp;
+    uint8_t* dest_scan =
+        pBuffer.subspan(row * pBitmap->GetPitch() + rect.left * Bpp).data();
     if (bAlpha) {
       for (int col = 0; col < width; col++) {
         uint8_t back_alpha = dest_scan[3];
         if (back_alpha == 0) {
-          FXARGB_SETRGBORDERDIB(dest_scan,
-                                ArgbEncode(src_alpha, src_r, src_g, src_b));
+          FXARGB_SETRGBORDERDIB(dest_scan, argb);
           dest_scan += 4;
           continue;
         }
@@ -142,113 +130,115 @@
 }
 
 void RgbByteOrderTransferBitmap(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                                int dest_left,
-                                int dest_top,
                                 int width,
                                 int height,
                                 const RetainPtr<CFX_DIBBase>& pSrcBitmap,
                                 int src_left,
                                 int src_top) {
-  if (!pBitmap)
-    return;
-
+  int dest_left = 0;
+  int dest_top = 0;
   if (!pBitmap->GetOverlapRect(dest_left, dest_top, width, height,
                                pSrcBitmap->GetWidth(), pSrcBitmap->GetHeight(),
                                src_left, src_top, nullptr)) {
     return;
   }
 
-  int Bpp = pBitmap->GetBPP() / 8;
-  FXDIB_Format dest_format = pBitmap->GetFormat();
-  FXDIB_Format src_format = pSrcBitmap->GetFormat();
-  int pitch = pBitmap->GetPitch();
-  uint8_t* buffer = pBitmap->GetBuffer();
+  const int Bpp = pBitmap->GetBPP() / 8;
+  const FXDIB_Format dest_format = pBitmap->GetFormat();
+  const FXDIB_Format src_format = pSrcBitmap->GetFormat();
+  const int dest_pitch = pBitmap->GetPitch();
+
+  const size_t dest_x_offset = Fx2DSizeOrDie(dest_left, Bpp);
+  const size_t dest_y_offset = Fx2DSizeOrDie(dest_top, dest_pitch);
+
+  pdfium::span<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++) {
-      uint8_t* dest_scan = buffer + (dest_top + row) * pitch + dest_left * Bpp;
+      uint8_t* dest_scan = dest_span.data();
       const uint8_t* src_scan =
-          pSrcBitmap->GetScanline(src_top + row) + src_left * Bpp;
+          pSrcBitmap->GetScanline(src_top + row).subspan(src_x_offset).data();
       if (Bpp == 4) {
         for (int col = 0; col < width; col++) {
-          FXARGB_SETDIB(dest_scan, ArgbEncode(src_scan[3], src_scan[0],
-                                              src_scan[1], src_scan[2]));
+          FXARGB_SETRGBORDERDIB(dest_scan,
+                                *reinterpret_cast<const uint32_t*>(src_scan));
           dest_scan += 4;
           src_scan += 4;
         }
-        continue;
+      } else {
+        for (int col = 0; col < width; col++) {
+          *dest_scan++ = src_scan[2];
+          *dest_scan++ = src_scan[1];
+          *dest_scan++ = src_scan[0];
+          src_scan += 3;
+        }
       }
-      for (int col = 0; col < width; col++) {
-        *dest_scan++ = src_scan[2];
-        *dest_scan++ = src_scan[1];
-        *dest_scan++ = src_scan[0];
-        src_scan += 3;
-      }
+      dest_span = dest_span.subspan(dest_pitch);
     }
     return;
   }
 
-  uint8_t* dest_buf = buffer + dest_top * pitch + dest_left * Bpp;
-  if (dest_format == FXDIB_Rgb) {
-    ASSERT(src_format == FXDIB_Rgb32);
+  if (dest_format == FXDIB_Format::kRgb) {
+    DCHECK_EQ(src_format, FXDIB_Format::kRgb32);
+    const size_t src_x_offset = Fx2DSizeOrDie(src_left, 4);
     for (int row = 0; row < height; row++) {
-      uint8_t* dest_scan = dest_buf + row * pitch;
+      uint8_t* dest_scan = dest_span.data();
       const uint8_t* src_scan =
-          pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
+          pSrcBitmap->GetScanline(src_top + row).subspan(src_x_offset).data();
       for (int col = 0; col < width; col++) {
         *dest_scan++ = src_scan[2];
         *dest_scan++ = src_scan[1];
         *dest_scan++ = src_scan[0];
         src_scan += 4;
       }
+      if (row < height - 1) {
+        // Since `dest_scan` was initialized in a way that takes `dest_x_offset`
+        // and `dest_y_offset` into account, it may go past the end of the span
+        // after processing the last row.
+        dest_span = dest_span.subspan(dest_pitch);
+      }
     }
     return;
   }
 
-  ASSERT(dest_format == FXDIB_Argb || dest_format == FXDIB_Rgb32);
-  if (src_format == FXDIB_Rgb) {
+  DCHECK(dest_format == FXDIB_Format::kArgb ||
+         dest_format == FXDIB_Format::kRgb32);
+  if (src_format == FXDIB_Format::kRgb) {
+    const size_t src_x_offset = Fx2DSizeOrDie(src_left, 3);
     for (int row = 0; row < height; row++) {
-      uint8_t* dest_scan = dest_buf + row * pitch;
+      uint8_t* dest_scan = dest_span.data();
       const uint8_t* src_scan =
-          pSrcBitmap->GetScanline(src_top + row) + src_left * 3;
+          pSrcBitmap->GetScanline(src_top + row).subspan(src_x_offset).data();
       for (int col = 0; col < width; col++) {
         FXARGB_SETDIB(dest_scan,
                       ArgbEncode(0xff, src_scan[0], src_scan[1], src_scan[2]));
         dest_scan += 4;
         src_scan += 3;
       }
+      dest_span = dest_span.subspan(dest_pitch);
     }
     return;
   }
-  if (src_format != FXDIB_Rgb32)
+  if (src_format != FXDIB_Format::kRgb32)
     return;
-  ASSERT(dest_format == FXDIB_Argb);
+  DCHECK_EQ(dest_format, FXDIB_Format::kArgb);
+  const size_t src_x_offset = Fx2DSizeOrDie(src_left, 4);
   for (int row = 0; row < height; row++) {
-    uint8_t* dest_scan = dest_buf + row * pitch;
+    uint8_t* dest_scan = dest_span.data();
     const uint8_t* src_scan =
-        pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
+        pSrcBitmap->GetScanline(src_top + row).subspan(src_x_offset).data();
     for (int col = 0; col < width; col++) {
       FXARGB_SETDIB(dest_scan,
                     ArgbEncode(0xff, src_scan[0], src_scan[1], src_scan[2]));
       src_scan += 4;
       dest_scan += 4;
     }
+    dest_span = dest_span.subspan(dest_pitch);
   }
 }
 
-bool DibSetPixel(const RetainPtr<CFX_DIBitmap>& pDevice,
-                 int x,
-                 int y,
-                 uint32_t color) {
-  int alpha = FXARGB_A(color);
-  if (pDevice->IsCmykImage())
-    return false;
-
-  pDevice->SetPixel(x, y, color);
-  if (pDevice->m_pAlphaMask)
-    pDevice->m_pAlphaMask->SetPixel(x, y, alpha << 24);
-  return true;
-}
-
 void RasterizeStroke(agg::rasterizer_scanline_aa* rasterizer,
                      agg::path_storage* path_data,
                      const CFX_Matrix* pObject2Device,
@@ -257,10 +247,10 @@
                      bool bTextMode) {
   agg::line_cap_e cap;
   switch (pGraphState->m_LineCap) {
-    case CFX_GraphStateData::LineCapRound:
+    case CFX_GraphStateData::LineCap::kRound:
       cap = agg::round_cap;
       break;
-    case CFX_GraphStateData::LineCapSquare:
+    case CFX_GraphStateData::LineCap::kSquare:
       cap = agg::square_cap;
       break;
     default:
@@ -269,10 +259,10 @@
   }
   agg::line_join_e join;
   switch (pGraphState->m_LineJoin) {
-    case CFX_GraphStateData::LineJoinRound:
+    case CFX_GraphStateData::LineJoin::kRound:
       join = agg::round_join;
       break;
-    case CFX_GraphStateData::LineJoinBevel:
+    case CFX_GraphStateData::LineJoin::kBevel:
       join = agg::bevel_join;
       break;
     default:
@@ -287,12 +277,12 @@
   }
   width = std::max(width, unit);
   if (!pGraphState->m_DashArray.empty()) {
-    typedef agg::conv_dash<agg::path_storage> dash_converter;
-    dash_converter dash(*path_data);
+    using DashConverter = agg::conv_dash<agg::path_storage>;
+    DashConverter dash(*path_data);
     for (size_t i = 0; i < (pGraphState->m_DashArray.size() + 1) / 2; i++) {
       float on = pGraphState->m_DashArray[i * 2];
       if (on <= 0.000001f)
-        on = 1.0f / 10;
+        on = 0.1f;
       float off = i * 2 + 1 == pGraphState->m_DashArray.size()
                       ? on
                       : pGraphState->m_DashArray[i * 2 + 1];
@@ -300,8 +290,8 @@
       dash.add_dash(on * scale, off * scale);
     }
     dash.dash_start(pGraphState->m_DashPhase * scale);
-    typedef agg::conv_stroke<dash_converter> dash_stroke;
-    dash_stroke stroke(dash);
+    using DashStroke = agg::conv_stroke<DashConverter>;
+    DashStroke stroke(dash);
     stroke.line_join(join);
     stroke.line_cap(cap);
     stroke.miter_limit(pGraphState->m_MiterLimit);
@@ -317,109 +307,95 @@
   rasterizer->add_path_transformed(stroke, pObject2Device);
 }
 
-constexpr int kAlternateOrWindingFillModeMask =
-    FXFILL_ALTERNATE | FXFILL_WINDING;
-
-int GetAlternateOrWindingFillMode(int fill_mode) {
-  return fill_mode & kAlternateOrWindingFillModeMask;
-}
-
-bool IsAlternateOrWindingFillMode(int fill_mode) {
-  return !!GetAlternateOrWindingFillMode(fill_mode);
-}
-
-agg::filling_rule_e GetAlternateOrWindingFillType(int fill_mode) {
-  return GetAlternateOrWindingFillMode(fill_mode) == FXFILL_WINDING
+agg::filling_rule_e GetAlternateOrWindingFillType(
+    const CFX_FillRenderOptions& fill_options) {
+  return fill_options.fill_type == CFX_FillRenderOptions::FillType::kWinding
              ? agg::fill_non_zero
              : agg::fill_even_odd;
 }
 
+RetainPtr<CFX_DIBitmap> GetClipMaskFromRegion(const CFX_ClipRgn* r) {
+  return (r && r->GetType() == CFX_ClipRgn::kMaskF) ? r->GetMask() : nullptr;
+}
+
+FX_RECT GetClipBoxFromRegion(const RetainPtr<CFX_DIBitmap>& device,
+                             const CFX_ClipRgn* region) {
+  if (region)
+    return region->GetBox();
+  return FX_RECT(0, 0, device->GetWidth(), device->GetHeight());
+}
+
 class CFX_Renderer {
  public:
+  CFX_Renderer(const RetainPtr<CFX_DIBitmap>& pDevice,
+               const RetainPtr<CFX_DIBitmap>& pBackdropDevice,
+               const CFX_ClipRgn* pClipRgn,
+               uint32_t color,
+               bool bFullCover,
+               bool bRgbByteOrder);
+
   // Needed for agg caller
   void prepare(unsigned) {}
 
-  void CompositeSpan(uint8_t* dest_scan,
-                     uint8_t* backdrop_scan,
-                     int Bpp,
-                     bool bDestAlpha,
-                     int span_left,
-                     int span_len,
-                     uint8_t* cover_scan,
-                     int clip_left,
-                     int clip_right,
-                     uint8_t* clip_scan);
-
-  void CompositeSpan1bpp(uint8_t* dest_scan,
-                         int Bpp,
-                         int span_left,
-                         int span_len,
-                         uint8_t* cover_scan,
-                         int clip_left,
-                         int clip_right,
-                         uint8_t* clip_scan,
-                         uint8_t* dest_extra_alpha_scan);
-
-  void CompositeSpanGray(uint8_t* dest_scan,
-                         int Bpp,
-                         int span_left,
-                         int span_len,
-                         uint8_t* cover_scan,
-                         int clip_left,
-                         int clip_right,
-                         uint8_t* clip_scan,
-                         uint8_t* dest_extra_alpha_scan);
-
-  void CompositeSpanARGB(uint8_t* dest_scan,
-                         int Bpp,
-                         int span_left,
-                         int span_len,
-                         uint8_t* cover_scan,
-                         int clip_left,
-                         int clip_right,
-                         uint8_t* clip_scan,
-                         uint8_t* dest_extra_alpha_scan);
-
-  void CompositeSpanRGB(uint8_t* dest_scan,
-                        int Bpp,
-                        int span_left,
-                        int span_len,
-                        uint8_t* cover_scan,
-                        int clip_left,
-                        int clip_right,
-                        uint8_t* clip_scan,
-                        uint8_t* dest_extra_alpha_scan);
-
-  void CompositeSpanCMYK(uint8_t* dest_scan,
-                         int Bpp,
-                         int span_left,
-                         int span_len,
-                         uint8_t* cover_scan,
-                         int clip_left,
-                         int clip_right,
-                         uint8_t* clip_scan,
-                         uint8_t* dest_extra_alpha_scan);
-
-  bool Init(const RetainPtr<CFX_DIBitmap>& pDevice,
-            const RetainPtr<CFX_DIBitmap>& pBackdropDevice,
-            const CFX_ClipRgn* pClipRgn,
-            uint32_t color,
-            bool bFullCover,
-            bool bRgbByteOrder);
-
   template <class Scanline>
   void render(const Scanline& sl);
 
  private:
-  void (CFX_Renderer::*composite_span)(uint8_t*,
-                                       int,
-                                       int,
-                                       int,
-                                       uint8_t*,
-                                       int,
-                                       int,
-                                       uint8_t*,
-                                       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,
+                     const uint8_t* backdrop_scan,
+                     int Bpp,
+                     bool bDestAlpha,
+                     int span_left,
+                     int span_len,
+                     const uint8_t* cover_scan,
+                     int clip_left,
+                     int clip_right,
+                     const uint8_t* clip_scan);
+
+  void CompositeSpan1bpp(uint8_t* dest_scan,
+                         int Bpp,
+                         int span_left,
+                         int span_len,
+                         const uint8_t* cover_scan,
+                         int clip_left,
+                         int clip_right,
+                         const uint8_t* clip_scan);
+
+  void CompositeSpanGray(uint8_t* dest_scan,
+                         int Bpp,
+                         int span_left,
+                         int span_len,
+                         const uint8_t* cover_scan,
+                         int clip_left,
+                         int clip_right,
+                         const uint8_t* clip_scan);
+
+  void CompositeSpanARGB(uint8_t* dest_scan,
+                         int Bpp,
+                         int span_left,
+                         int span_len,
+                         const uint8_t* cover_scan,
+                         int clip_left,
+                         int clip_right,
+                         const uint8_t* clip_scan);
+
+  void CompositeSpanRGB(uint8_t* dest_scan,
+                        int Bpp,
+                        int span_left,
+                        int span_len,
+                        const uint8_t* cover_scan,
+                        int clip_left,
+                        int clip_right,
+                        const uint8_t* clip_scan);
 
   void CompositeSpan1bppHelper(uint8_t* dest_scan,
                                int col_start,
@@ -428,6 +404,17 @@
                                const uint8_t* clip_scan,
                                int span_left);
 
+  static CompositeSpanFunc GetCompositeSpanFunc(
+      const RetainPtr<CFX_DIBitmap>& device) {
+    if (device->GetBPP() == 1)
+      return &CFX_Renderer::CompositeSpan1bpp;
+    if (device->GetBPP() == 8)
+      return &CFX_Renderer::CompositeSpanGray;
+    if (device->GetFormat() == FXDIB_Format::kArgb)
+      return &CFX_Renderer::CompositeSpanARGB;
+    return &CFX_Renderer::CompositeSpanRGB;
+  }
+
   inline int GetSrcAlpha(const uint8_t* clip_scan, int col) const {
     return clip_scan ? m_Alpha * clip_scan[col] / 255 : m_Alpha;
   }
@@ -453,27 +440,27 @@
   int m_Green;
   int m_Blue;
   int m_Gray;
-  uint32_t m_Color;
-  bool m_bFullCover;
-  bool m_bRgbByteOrder;
-  FX_RECT m_ClipBox;
-  RetainPtr<CFX_DIBitmap> m_pBackdropDevice;
-  RetainPtr<CFX_DIBitmap> m_pClipMask;
-  RetainPtr<CFX_DIBitmap> m_pDevice;
+  const uint32_t m_Color;
+  const bool m_bFullCover;
+  const bool m_bRgbByteOrder;
+  const FX_RECT m_ClipBox;
+  RetainPtr<CFX_DIBitmap> const m_pBackdropDevice;
+  RetainPtr<CFX_DIBitmap> const m_pClipMask;
+  RetainPtr<CFX_DIBitmap> const m_pDevice;
   UnownedPtr<const CFX_ClipRgn> m_pClipRgn;
+  const CompositeSpanFunc m_CompositeSpanFunc;
 };
 
 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) {
-  ASSERT(!m_pDevice->IsCmykImage());
+                                 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) {
@@ -612,13 +599,11 @@
                                      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,
-                                     uint8_t* dest_extra_alpha_scan) {
-  ASSERT(!m_bRgbByteOrder);
-  ASSERT(!m_pDevice->IsCmykImage());
+                                     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);
   dest_scan += col_start / 8;
@@ -630,38 +615,14 @@
                                      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,
-                                     uint8_t* dest_extra_alpha_scan) {
-  ASSERT(!m_bRgbByteOrder);
+                                     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);
   dest_scan += col_start;
-  if (dest_extra_alpha_scan) {
-    for (int col = col_start; col < col_end; col++) {
-      int src_alpha = m_bFullCover ? GetSrcAlpha(clip_scan, col)
-                                   : GetSourceAlpha(cover_scan, clip_scan, col);
-      if (src_alpha) {
-        if (src_alpha == 255) {
-          *dest_scan = m_Gray;
-          *dest_extra_alpha_scan = m_Alpha;
-        } else {
-          uint8_t dest_alpha = (*dest_extra_alpha_scan) + src_alpha -
-                               (*dest_extra_alpha_scan) * src_alpha / 255;
-          *dest_extra_alpha_scan++ = dest_alpha;
-          int alpha_ratio = src_alpha * 255 / dest_alpha;
-          *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Gray, alpha_ratio);
-          dest_scan++;
-          continue;
-        }
-      }
-      dest_extra_alpha_scan++;
-      dest_scan++;
-    }
-    return;
-  }
   for (int col = col_start; col < col_end; col++) {
     int src_alpha = GetSourceAlpha(cover_scan, clip_scan, col);
     if (src_alpha) {
@@ -678,11 +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,
-                                     uint8_t* dest_extra_alpha_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;
@@ -747,11 +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,
-                                    uint8_t* dest_extra_alpha_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;
@@ -782,35 +741,6 @@
     }
     return;
   }
-  if (Bpp == 3 && dest_extra_alpha_scan) {
-    for (int col = col_start; col < col_end; col++) {
-      int src_alpha = m_bFullCover ? GetSrcAlpha(clip_scan, col)
-                                   : GetSourceAlpha(cover_scan, clip_scan, col);
-      if (src_alpha) {
-        if (src_alpha == 255) {
-          *dest_scan++ = static_cast<uint8_t>(m_Blue);
-          *dest_scan++ = static_cast<uint8_t>(m_Green);
-          *dest_scan++ = static_cast<uint8_t>(m_Red);
-          *dest_extra_alpha_scan++ = static_cast<uint8_t>(m_Alpha);
-          continue;
-        }
-        uint8_t dest_alpha = (*dest_extra_alpha_scan) + src_alpha -
-                             (*dest_extra_alpha_scan) * src_alpha / 255;
-        *dest_extra_alpha_scan++ = dest_alpha;
-        int alpha_ratio = src_alpha * 255 / dest_alpha;
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Blue, alpha_ratio);
-        dest_scan++;
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Green, alpha_ratio);
-        dest_scan++;
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Red, alpha_ratio);
-        dest_scan++;
-        continue;
-      }
-      dest_extra_alpha_scan++;
-      dest_scan += Bpp;
-    }
-    return;
-  }
   for (int col = col_start; col < col_end; col++) {
     int src_alpha = m_bFullCover ? GetSrcAlpha(clip_scan, col)
                                  : GetSourceAlpha(cover_scan, clip_scan, col);
@@ -838,165 +768,70 @@
   }
 }
 
-void CFX_Renderer::CompositeSpanCMYK(uint8_t* dest_scan,
-                                     int Bpp,
-                                     int span_left,
-                                     int span_len,
-                                     uint8_t* cover_scan,
-                                     int clip_left,
-                                     int clip_right,
-                                     uint8_t* clip_scan,
-                                     uint8_t* dest_extra_alpha_scan) {
-  ASSERT(!m_bRgbByteOrder);
-  int col_start = GetColStart(span_left, clip_left);
-  int col_end = GetColEnd(span_left, span_len, clip_right);
-  dest_scan += col_start * 4;
-  if (dest_extra_alpha_scan) {
-    for (int col = col_start; col < col_end; col++) {
-      int src_alpha = m_bFullCover ? GetSrcAlpha(clip_scan, col)
-                                   : GetSourceAlpha(cover_scan, clip_scan, col);
-      if (src_alpha) {
-        if (src_alpha == 255) {
-          *(reinterpret_cast<FX_CMYK*>(dest_scan)) = m_Color;
-          *dest_extra_alpha_scan = static_cast<uint8_t>(m_Alpha);
-        } else {
-          uint8_t dest_alpha = (*dest_extra_alpha_scan) + src_alpha -
-                               (*dest_extra_alpha_scan) * src_alpha / 255;
-          *dest_extra_alpha_scan++ = dest_alpha;
-          int alpha_ratio = src_alpha * 255 / dest_alpha;
-          *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Red, alpha_ratio);
-          dest_scan++;
-          *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Green, alpha_ratio);
-          dest_scan++;
-          *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Blue, alpha_ratio);
-          dest_scan++;
-          *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Gray, alpha_ratio);
-          dest_scan++;
-          continue;
-        }
-      }
-      dest_extra_alpha_scan++;
-      dest_scan += 4;
-    }
-    return;
-  }
-  for (int col = col_start; col < col_end; col++) {
-    int src_alpha = GetSourceAlpha(cover_scan, clip_scan, col);
-    if (src_alpha) {
-      if (src_alpha == 255) {
-        *(reinterpret_cast<FX_CMYK*>(dest_scan)) = m_Color;
-      } else {
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Red, src_alpha);
-        dest_scan++;
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Green, src_alpha);
-        dest_scan++;
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Blue, src_alpha);
-        dest_scan++;
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Gray, src_alpha);
-        dest_scan++;
-        continue;
-      }
-    }
-    dest_scan += 4;
-  }
-}
-
-bool CFX_Renderer::Init(const RetainPtr<CFX_DIBitmap>& pDevice,
-                        const RetainPtr<CFX_DIBitmap>& pBackdropDevice,
-                        const CFX_ClipRgn* pClipRgn,
-                        uint32_t color,
-                        bool bFullCover,
-                        bool bRgbByteOrder) {
-  m_pDevice = pDevice;
-  m_pClipRgn = pClipRgn;
-  composite_span = nullptr;
-  m_bRgbByteOrder = bRgbByteOrder;
-  m_pBackdropDevice = pBackdropDevice;
-  if (m_pClipRgn) {
-    m_ClipBox = m_pClipRgn->GetBox();
-  } else {
-    m_ClipBox.left = m_ClipBox.top = 0;
-    m_ClipBox.right = m_pDevice->GetWidth();
-    m_ClipBox.bottom = m_pDevice->GetHeight();
-  }
-  m_pClipMask = nullptr;
-  if (m_pClipRgn && m_pClipRgn->GetType() == CFX_ClipRgn::MaskF)
-    m_pClipMask = m_pClipRgn->GetMask();
-  m_bFullCover = bFullCover;
-  bool bDeviceCMYK = pDevice->IsCmykImage();
-  m_Alpha = FXARGB_A(color);
+CFX_Renderer::CFX_Renderer(const RetainPtr<CFX_DIBitmap>& pDevice,
+                           const RetainPtr<CFX_DIBitmap>& pBackdropDevice,
+                           const CFX_ClipRgn* pClipRgn,
+                           uint32_t color,
+                           bool bFullCover,
+                           bool bRgbByteOrder)
+    : m_Alpha(FXARGB_A(color)),
+      m_Color(bRgbByteOrder ? FXARGB_TOBGRORDERDIB(color) : color),
+      m_bFullCover(bFullCover),
+      m_bRgbByteOrder(bRgbByteOrder),
+      m_ClipBox(GetClipBoxFromRegion(pDevice, pClipRgn)),
+      m_pBackdropDevice(pBackdropDevice),
+      m_pClipMask(GetClipMaskFromRegion(pClipRgn)),
+      m_pDevice(pDevice),
+      m_pClipRgn(pClipRgn),
+      m_CompositeSpanFunc(GetCompositeSpanFunc(m_pDevice)) {
   if (m_pDevice->GetBPP() == 8) {
-    ASSERT(!m_bRgbByteOrder);
-    composite_span = &CFX_Renderer::CompositeSpanGray;
-    if (m_pDevice->IsAlphaMask())
+    DCHECK(!m_bRgbByteOrder);
+    if (m_pDevice->IsMaskFormat())
       m_Gray = 255;
     else
       m_Gray = FXRGB2GRAY(FXARGB_R(color), FXARGB_G(color), FXARGB_B(color));
-    return true;
+    return;
   }
-  if (bDeviceCMYK) {
-    ASSERT(!m_bRgbByteOrder);
-    composite_span = &CFX_Renderer::CompositeSpanCMYK;
-    return false;
-  }
-  composite_span = (pDevice->GetFormat() == FXDIB_Argb)
-                       ? &CFX_Renderer::CompositeSpanARGB
-                       : &CFX_Renderer::CompositeSpanRGB;
-  if (m_bRgbByteOrder)
-    m_Color = FXARGB_TOBGRORDERDIB(color);
-  else
-    m_Color = FXARGB_TODIB(color);
+
   std::tie(m_Alpha, m_Red, m_Green, m_Blue) = ArgbDecode(color);
-  if (m_pDevice->GetBPP() == 1)
-    composite_span = &CFX_Renderer::CompositeSpan1bpp;
-  return true;
 }
 
 template <class Scanline>
 void CFX_Renderer::render(const Scanline& sl) {
-  if (!m_pBackdropDevice && !composite_span)
-    return;
-
   int y = sl.y();
   if (y < m_ClipBox.top || y >= m_ClipBox.bottom)
     return;
 
-  uint8_t* dest_scan = m_pDevice->GetBuffer() + m_pDevice->GetPitch() * y;
-  uint8_t* dest_scan_extra_alpha = nullptr;
-  RetainPtr<CFX_DIBitmap> pAlphaMask = m_pDevice->m_pAlphaMask;
-  if (pAlphaMask) {
-    dest_scan_extra_alpha =
-        pAlphaMask->GetBuffer() + pAlphaMask->GetPitch() * y;
-  }
-  uint8_t* backdrop_scan = nullptr;
+  uint8_t* dest_scan =
+      m_pDevice->GetWritableBuffer().subspan(m_pDevice->GetPitch() * y).data();
+  const uint8_t* backdrop_scan = nullptr;
   if (m_pBackdropDevice) {
-    backdrop_scan =
-        m_pBackdropDevice->GetBuffer() + m_pBackdropDevice->GetPitch() * y;
+    backdrop_scan = m_pBackdropDevice->GetBuffer()
+                        .subspan(m_pBackdropDevice->GetPitch() * y)
+                        .data();
   }
   int Bpp = m_pDevice->GetBPP() / 8;
-  bool bDestAlpha = m_pDevice->HasAlpha() || m_pDevice->IsAlphaMask();
+  bool bDestAlpha = m_pDevice->IsAlphaFormat() || m_pDevice->IsMaskFormat();
   unsigned num_spans = sl.num_spans();
   typename Scanline::const_iterator span = sl.begin();
-  while (1) {
+  while (true) {
     if (span->len <= 0)
       break;
 
     int x = span->x;
     uint8_t* dest_pos = nullptr;
-    uint8_t* dest_extra_alpha_pos = nullptr;
-    uint8_t* backdrop_pos = nullptr;
+    const uint8_t* backdrop_pos = nullptr;
     if (Bpp) {
       backdrop_pos = backdrop_scan ? backdrop_scan + x * Bpp : nullptr;
       dest_pos = dest_scan + x * Bpp;
-      dest_extra_alpha_pos =
-          dest_scan_extra_alpha ? dest_scan_extra_alpha + x : nullptr;
     } else {
       dest_pos = dest_scan + x / 8;
       backdrop_pos = backdrop_scan ? backdrop_scan + x / 8 : nullptr;
     }
-    uint8_t* clip_pos = nullptr;
+    const uint8_t* clip_pos = nullptr;
     if (m_pClipMask) {
-      clip_pos = m_pClipMask->GetBuffer() +
+      // TODO(crbug.com/1382604): use subspan arithmetic.
+      clip_pos = m_pClipMask->GetBuffer().data() +
                  (y - m_ClipBox.top) * m_pClipMask->GetPitch() + x -
                  m_ClipBox.left;
     }
@@ -1004,9 +839,8 @@
       CompositeSpan(dest_pos, backdrop_pos, Bpp, bDestAlpha, x, span->len,
                     span->covers, m_ClipBox.left, m_ClipBox.right, clip_pos);
     } else {
-      (this->*composite_span)(dest_pos, Bpp, x, span->len, span->covers,
-                              m_ClipBox.left, m_ClipBox.right, clip_pos,
-                              dest_extra_alpha_pos);
+      (this->*m_CompositeSpanFunc)(dest_pos, Bpp, x, span->len, span->covers,
+                                   m_ClipBox.left, m_ClipBox.right, clip_pos);
     }
     if (--num_spans == 0)
       break;
@@ -1022,9 +856,9 @@
                                            const uint8_t* clip_scan,
                                            int span_left) {
   int index = 0;
-  if (m_pDevice->GetPalette()) {
+  if (m_pDevice->HasPalette()) {
     for (int i = 0; i < 2; i++) {
-      if (FXARGB_TODIB(m_pDevice->GetPalette()[i]) == m_Color)
+      if (m_pDevice->GetPaletteSpan()[i] == m_Color)
         index = i;
     }
   } else {
@@ -1043,16 +877,12 @@
   }
 }
 
-}  // namespace
-
-namespace agg {
-
 template <class BaseRenderer>
-class renderer_scanline_aa_offset {
+class RendererScanLineAaOffset {
  public:
   typedef BaseRenderer base_ren_type;
   typedef typename base_ren_type::color_type color_type;
-  renderer_scanline_aa_offset(base_ren_type& ren, unsigned left, unsigned top)
+  RendererScanLineAaOffset(base_ren_type& ren, unsigned left, unsigned top)
       : m_ren(&ren), m_left(left), m_top(top) {}
   void color(const color_type& c) { m_color = c; }
   const color_type& color() const { return m_color; }
@@ -1062,7 +892,7 @@
     int y = sl.y();
     unsigned num_spans = sl.num_spans();
     typename Scanline::const_iterator span = sl.begin();
-    while (1) {
+    while (true) {
       int x = span->x;
       if (span->len > 0) {
         m_ren->blend_solid_hspan(x - m_left, y - m_top, (unsigned)span->len,
@@ -1079,38 +909,38 @@
   }
 
  private:
-  base_ren_type* m_ren;
+  UNOWNED_PTR_EXCLUSION base_ren_type* m_ren;
   color_type m_color;
-  unsigned m_left, m_top;
+  unsigned m_left;
+  unsigned m_top;
 };
 
-}  // namespace agg
-
-void CAgg_PathData::BuildPath(const CFX_PathData* pPathData,
-                              const CFX_Matrix* pObject2Device) {
-  const std::vector<FX_PATHPOINT>& pPoints = pPathData->GetPoints();
-  for (size_t i = 0; i < pPoints.size(); i++) {
-    CFX_PointF pos = pPoints[i].m_Point;
+agg::path_storage BuildAggPath(const CFX_Path& path,
+                               const CFX_Matrix* pObject2Device) {
+  agg::path_storage agg_path;
+  pdfium::span<const CFX_Path::Point> points = path.GetPoints();
+  for (size_t i = 0; i < points.size(); ++i) {
+    CFX_PointF pos = points[i].m_Point;
     if (pObject2Device)
       pos = pObject2Device->Transform(pos);
 
     pos = HardClip(pos);
-    FXPT_TYPE point_type = pPoints[i].m_Type;
-    if (point_type == FXPT_TYPE::MoveTo) {
-      m_PathData.move_to(pos.x, pos.y);
-    } else if (point_type == FXPT_TYPE::LineTo) {
-      if (i > 0 && pPoints[i - 1].IsTypeAndOpen(FXPT_TYPE::MoveTo) &&
-          (i == pPoints.size() - 1 ||
-           pPoints[i + 1].IsTypeAndOpen(FXPT_TYPE::MoveTo)) &&
-          pPoints[i].m_Point == pPoints[i - 1].m_Point) {
+    CFX_Path::Point::Type point_type = points[i].m_Type;
+    if (point_type == CFX_Path::Point::Type::kMove) {
+      agg_path.move_to(pos.x, pos.y);
+    } else if (point_type == CFX_Path::Point::Type::kLine) {
+      if (i > 0 && points[i - 1].IsTypeAndOpen(CFX_Path::Point::Type::kMove) &&
+          (i == points.size() - 1 ||
+           points[i + 1].IsTypeAndOpen(CFX_Path::Point::Type::kMove)) &&
+          points[i].m_Point == points[i - 1].m_Point) {
         pos.x += 1;
       }
-      m_PathData.line_to(pos.x, pos.y);
-    } else if (point_type == FXPT_TYPE::BezierTo) {
-      if (i > 0 && i + 2 < pPoints.size()) {
-        CFX_PointF pos0 = pPoints[i - 1].m_Point;
-        CFX_PointF pos2 = pPoints[i + 1].m_Point;
-        CFX_PointF pos3 = pPoints[i + 2].m_Point;
+      agg_path.line_to(pos.x, pos.y);
+    } else if (point_type == CFX_Path::Point::Type::kBezier) {
+      if (i > 0 && i + 2 < points.size()) {
+        CFX_PointF pos0 = points[i - 1].m_Point;
+        CFX_PointF pos2 = points[i + 1].m_Point;
+        CFX_PointF pos3 = points[i + 2].m_Point;
         if (pObject2Device) {
           pos0 = pObject2Device->Transform(pos0);
           pos2 = pObject2Device->Transform(pos2);
@@ -1122,24 +952,29 @@
         agg::curve4 curve(pos0.x, pos0.y, pos.x, pos.y, pos2.x, pos2.y, pos3.x,
                           pos3.y);
         i += 2;
-        m_PathData.add_path_curve(curve);
+        agg_path.add_path(curve);
       }
     }
-    if (pPoints[i].m_CloseFigure)
-      m_PathData.end_poly();
+    if (points[i].m_CloseFigure)
+      agg_path.end_poly();
   }
+  return agg_path;
 }
 
+}  // namespace
+
 CFX_AggDeviceDriver::CFX_AggDeviceDriver(
-    const RetainPtr<CFX_DIBitmap>& pBitmap,
+    RetainPtr<CFX_DIBitmap> pBitmap,
     bool bRgbByteOrder,
-    const RetainPtr<CFX_DIBitmap>& pBackdropBitmap,
+    RetainPtr<CFX_DIBitmap> pBackdropBitmap,
     bool bGroupKnockout)
-    : m_pBitmap(pBitmap),
+    : m_pBitmap(std::move(pBitmap)),
       m_bRgbByteOrder(bRgbByteOrder),
       m_bGroupKnockout(bGroupKnockout),
-      m_pBackdropBitmap(pBackdropBitmap) {
-  ASSERT(m_pBitmap);
+      m_pBackdropBitmap(std::move(pBackdropBitmap)) {
+  DCHECK(m_pBitmap);
+  DCHECK_NE(m_pBitmap->GetFormat(), FXDIB_Format::k1bppMask);
+  DCHECK_NE(m_pBitmap->GetFormat(), FXDIB_Format::k1bppRgb);
   InitPlatform();
 }
 
@@ -1147,24 +982,21 @@
   DestroyPlatform();
 }
 
-uint8_t* CFX_AggDeviceDriver::GetBuffer() const {
-  return m_pBitmap->GetBuffer();
-}
-
-#if !defined(OS_MACOSX)
+#if !BUILDFLAG(IS_APPLE)
 void CFX_AggDeviceDriver::InitPlatform() {}
 
 void CFX_AggDeviceDriver::DestroyPlatform() {}
 
-bool CFX_AggDeviceDriver::DrawDeviceText(int nChars,
-                                         const TextCharPos* pCharPos,
-                                         CFX_Font* pFont,
-                                         const CFX_Matrix& mtObject2Device,
-                                         float font_size,
-                                         uint32_t color) {
+bool CFX_AggDeviceDriver::DrawDeviceText(
+    pdfium::span<const TextCharPos> pCharPos,
+    CFX_Font* pFont,
+    const CFX_Matrix& mtObject2Device,
+    float font_size,
+    uint32_t color,
+    const CFX_TextRenderOptions& options) {
   return false;
 }
-#endif  // !defined(OS_MACOSX)
+#endif  // !BUILDFLAG(IS_APPLE)
 
 DeviceType CFX_AggDeviceDriver::GetDeviceType() const {
   return DeviceType::kDisplay;
@@ -1184,28 +1016,25 @@
     case FXDC_RENDER_CAPS: {
       int flags = FXRC_GET_BITS | FXRC_ALPHA_PATH | FXRC_ALPHA_IMAGE |
                   FXRC_BLEND_MODE | FXRC_SOFT_CLIP;
-      if (m_pBitmap->HasAlpha()) {
+      if (m_pBitmap->IsAlphaFormat()) {
         flags |= FXRC_ALPHA_OUTPUT;
-      } else if (m_pBitmap->IsAlphaMask()) {
+      } else if (m_pBitmap->IsMaskFormat()) {
         if (m_pBitmap->GetBPP() == 1)
           flags |= FXRC_BITMASK_OUTPUT;
         else
           flags |= FXRC_BYTEMASK_OUTPUT;
       }
-      if (m_pBitmap->IsCmykImage())
-        flags |= FXRC_CMYK_OUTPUT;
       return flags;
     }
     default:
-      NOTREACHED();
-      return 0;
+      NOTREACHED_NORETURN();
   }
 }
 
 void CFX_AggDeviceDriver::SaveState() {
   std::unique_ptr<CFX_ClipRgn> pClip;
   if (m_pClipRgn)
-    pClip = pdfium::MakeUnique<CFX_ClipRgn>(*m_pClipRgn);
+    pClip = std::make_unique<CFX_ClipRgn>(*m_pClipRgn);
   m_StateStack.push_back(std::move(pClip));
 }
 
@@ -1217,7 +1046,7 @@
 
   if (bKeepSaved) {
     if (m_StateStack.back())
-      m_pClipRgn = pdfium::MakeUnique<CFX_ClipRgn>(*m_StateStack.back());
+      m_pClipRgn = std::make_unique<CFX_ClipRgn>(*m_StateStack.back());
   } else {
     m_pClipRgn = std::move(m_StateStack.back());
     m_StateStack.pop_back();
@@ -1229,75 +1058,71 @@
                     rasterizer.max_x() + 1, rasterizer.max_y() + 1);
   path_rect.Intersect(m_pClipRgn->GetBox());
   auto pThisLayer = pdfium::MakeRetain<CFX_DIBitmap>();
-  pThisLayer->Create(path_rect.Width(), path_rect.Height(), FXDIB_8bppMask);
-  pThisLayer->Clear(0);
-  agg::rendering_buffer raw_buf(pThisLayer->GetBuffer(), pThisLayer->GetWidth(),
-                                pThisLayer->GetHeight(),
+  pThisLayer->Create(path_rect.Width(), path_rect.Height(),
+                     FXDIB_Format::k8bppMask);
+  agg::rendering_buffer raw_buf(pThisLayer->GetWritableBuffer().data(),
+                                pThisLayer->GetWidth(), pThisLayer->GetHeight(),
                                 pThisLayer->GetPitch());
   agg::pixfmt_gray8 pixel_buf(raw_buf);
   agg::renderer_base<agg::pixfmt_gray8> base_buf(pixel_buf);
-  agg::renderer_scanline_aa_offset<agg::renderer_base<agg::pixfmt_gray8> >
-      final_render(base_buf, path_rect.left, path_rect.top);
+  RendererScanLineAaOffset<agg::renderer_base<agg::pixfmt_gray8>> final_render(
+      base_buf, path_rect.left, path_rect.top);
   final_render.color(agg::gray8(255));
   agg::scanline_u8 scanline;
   agg::render_scanlines(rasterizer, scanline, final_render,
-                        (m_FillFlags & FXFILL_NOPATHSMOOTH) != 0);
-  m_pClipRgn->IntersectMaskF(path_rect.left, path_rect.top, pThisLayer);
+                        m_FillOptions.aliased_path);
+  m_pClipRgn->IntersectMaskF(path_rect.left, path_rect.top,
+                             std::move(pThisLayer));
 }
 
-bool CFX_AggDeviceDriver::SetClip_PathFill(const CFX_PathData* pPathData,
-                                           const CFX_Matrix* pObject2Device,
-                                           int fill_mode) {
-  ASSERT(IsAlternateOrWindingFillMode(fill_mode));
-  ASSERT(GetAlternateOrWindingFillMode(fill_mode) !=
-         kAlternateOrWindingFillModeMask);
+bool CFX_AggDeviceDriver::SetClip_PathFill(
+    const CFX_Path& path,
+    const CFX_Matrix* pObject2Device,
+    const CFX_FillRenderOptions& fill_options) {
+  DCHECK(fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill);
 
-  m_FillFlags = fill_mode;
+  m_FillOptions = fill_options;
   if (!m_pClipRgn) {
-    m_pClipRgn = pdfium::MakeUnique<CFX_ClipRgn>(
+    m_pClipRgn = std::make_unique<CFX_ClipRgn>(
         GetDeviceCaps(FXDC_PIXEL_WIDTH), GetDeviceCaps(FXDC_PIXEL_HEIGHT));
   }
-  size_t size = pPathData->GetPoints().size();
-  if (size == 5 || size == 4) {
-    CFX_FloatRect rectf;
-    if (pPathData->IsRect(pObject2Device, &rectf)) {
-      rectf.Intersect(CFX_FloatRect(
-          0, 0, static_cast<float>(GetDeviceCaps(FXDC_PIXEL_WIDTH)),
-          static_cast<float>(GetDeviceCaps(FXDC_PIXEL_HEIGHT))));
-      FX_RECT rect = rectf.GetOuterRect();
-      m_pClipRgn->IntersectRect(rect);
-      return true;
-    }
+  absl::optional<CFX_FloatRect> maybe_rectf = path.GetRect(pObject2Device);
+  if (maybe_rectf.has_value()) {
+    CFX_FloatRect& rectf = maybe_rectf.value();
+    rectf.Intersect(
+        CFX_FloatRect(0, 0, static_cast<float>(GetDeviceCaps(FXDC_PIXEL_WIDTH)),
+                      static_cast<float>(GetDeviceCaps(FXDC_PIXEL_HEIGHT))));
+    FX_RECT rect = rectf.GetOuterRect();
+    m_pClipRgn->IntersectRect(rect);
+    return true;
   }
-  CAgg_PathData path_data;
-  path_data.BuildPath(pPathData, pObject2Device);
-  path_data.m_PathData.end_poly();
+  agg::path_storage path_data = BuildAggPath(path, pObject2Device);
+  path_data.end_poly();
   agg::rasterizer_scanline_aa rasterizer;
   rasterizer.clip_box(0.0f, 0.0f,
                       static_cast<float>(GetDeviceCaps(FXDC_PIXEL_WIDTH)),
                       static_cast<float>(GetDeviceCaps(FXDC_PIXEL_HEIGHT)));
-  rasterizer.add_path(path_data.m_PathData);
-  rasterizer.filling_rule(GetAlternateOrWindingFillType(fill_mode));
+  rasterizer.add_path(path_data);
+  rasterizer.filling_rule(GetAlternateOrWindingFillType(fill_options));
   SetClipMask(rasterizer);
   return true;
 }
 
 bool CFX_AggDeviceDriver::SetClip_PathStroke(
-    const CFX_PathData* pPathData,
+    const CFX_Path& path,
     const CFX_Matrix* pObject2Device,
     const CFX_GraphStateData* pGraphState) {
   if (!m_pClipRgn) {
-    m_pClipRgn = pdfium::MakeUnique<CFX_ClipRgn>(
+    m_pClipRgn = std::make_unique<CFX_ClipRgn>(
         GetDeviceCaps(FXDC_PIXEL_WIDTH), GetDeviceCaps(FXDC_PIXEL_HEIGHT));
   }
-  CAgg_PathData path_data;
-  path_data.BuildPath(pPathData, nullptr);
+  agg::path_storage path_data = BuildAggPath(path, nullptr);
   agg::rasterizer_scanline_aa rasterizer;
   rasterizer.clip_box(0.0f, 0.0f,
                       static_cast<float>(GetDeviceCaps(FXDC_PIXEL_WIDTH)),
                       static_cast<float>(GetDeviceCaps(FXDC_PIXEL_HEIGHT)));
-  RasterizeStroke(&rasterizer, &path_data.m_PathData, pObject2Device,
-                  pGraphState, 1.0f, false);
+  RasterizeStroke(&rasterizer, &path_data, pObject2Device, pGraphState, 1.0f,
+                  false);
   rasterizer.filling_rule(agg::fill_non_zero);
   SetClipMask(rasterizer);
   return true;
@@ -1307,69 +1132,68 @@
   return 1;
 }
 
-bool CFX_AggDeviceDriver::RenderRasterizer(
+bool CFX_AggDeviceDriver::MultiplyAlpha(float alpha) {
+  return m_pBitmap->MultiplyAlpha(static_cast<int32_t>(alpha * 255));
+}
+
+bool CFX_AggDeviceDriver::MultiplyAlpha(const RetainPtr<CFX_DIBBase>& mask) {
+  return m_pBitmap->MultiplyAlpha(mask);
+}
+
+void CFX_AggDeviceDriver::RenderRasterizer(
     agg::rasterizer_scanline_aa& rasterizer,
     uint32_t color,
     bool bFullCover,
     bool bGroupKnockout) {
   RetainPtr<CFX_DIBitmap> pt = bGroupKnockout ? m_pBackdropBitmap : nullptr;
-  CFX_Renderer render;
-  if (!render.Init(m_pBitmap, pt, m_pClipRgn.get(), color, bFullCover,
-                   m_bRgbByteOrder)) {
-    return false;
-  }
+  CFX_Renderer render(m_pBitmap, pt, m_pClipRgn.get(), color, bFullCover,
+                      m_bRgbByteOrder);
   agg::scanline_u8 scanline;
   agg::render_scanlines(rasterizer, scanline, render,
-                        (m_FillFlags & FXFILL_NOPATHSMOOTH) != 0);
-  return true;
+                        m_FillOptions.aliased_path);
 }
 
-bool CFX_AggDeviceDriver::DrawPath(const CFX_PathData* pPathData,
+bool CFX_AggDeviceDriver::DrawPath(const CFX_Path& path,
                                    const CFX_Matrix* pObject2Device,
                                    const CFX_GraphStateData* pGraphState,
                                    uint32_t fill_color,
                                    uint32_t stroke_color,
-                                   int fill_mode,
+                                   const CFX_FillRenderOptions& fill_options,
                                    BlendMode blend_type) {
-  ASSERT(GetAlternateOrWindingFillMode(fill_mode) !=
-         kAlternateOrWindingFillModeMask);
-
   if (blend_type != BlendMode::kNormal)
     return false;
 
-  if (!GetBuffer())
+  if (m_pBitmap->GetBuffer().empty())
     return true;
 
-  m_FillFlags = fill_mode;
-  if (IsAlternateOrWindingFillMode(fill_mode) && fill_color) {
-    CAgg_PathData path_data;
-    path_data.BuildPath(pPathData, pObject2Device);
+  m_FillOptions = fill_options;
+  if (fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill &&
+      fill_color) {
+    agg::path_storage path_data = BuildAggPath(path, pObject2Device);
     agg::rasterizer_scanline_aa rasterizer;
     rasterizer.clip_box(0.0f, 0.0f,
                         static_cast<float>(GetDeviceCaps(FXDC_PIXEL_WIDTH)),
                         static_cast<float>(GetDeviceCaps(FXDC_PIXEL_HEIGHT)));
-    rasterizer.add_path(path_data.m_PathData);
-    rasterizer.filling_rule(GetAlternateOrWindingFillType(fill_mode));
-    if (!RenderRasterizer(rasterizer, fill_color,
-                          !!(fill_mode & FXFILL_FULLCOVER), false)) {
-      return false;
-    }
+    rasterizer.add_path(path_data);
+    rasterizer.filling_rule(GetAlternateOrWindingFillType(fill_options));
+    RenderRasterizer(rasterizer, fill_color, fill_options.full_cover,
+                     /*bGroupKnockout=*/false);
   }
   int stroke_alpha = FXARGB_A(stroke_color);
   if (!pGraphState || !stroke_alpha)
     return true;
 
-  if (fill_mode & FX_ZEROAREA_FILL) {
-    CAgg_PathData path_data;
-    path_data.BuildPath(pPathData, pObject2Device);
+  if (fill_options.zero_area) {
+    agg::path_storage path_data = BuildAggPath(path, pObject2Device);
     agg::rasterizer_scanline_aa rasterizer;
     rasterizer.clip_box(0.0f, 0.0f,
                         static_cast<float>(GetDeviceCaps(FXDC_PIXEL_WIDTH)),
                         static_cast<float>(GetDeviceCaps(FXDC_PIXEL_HEIGHT)));
-    RasterizeStroke(&rasterizer, &path_data.m_PathData, nullptr, pGraphState, 1,
-                    !!(fill_mode & FX_STROKE_TEXT_MODE));
-    return RenderRasterizer(rasterizer, stroke_color,
-                            !!(fill_mode & FXFILL_FULLCOVER), m_bGroupKnockout);
+    RasterizeStroke(&rasterizer, &path_data, nullptr, pGraphState, 1,
+                    fill_options.stroke_text_mode);
+    RenderRasterizer(rasterizer, stroke_color, fill_options.full_cover,
+                     m_bGroupKnockout);
+    return true;
   }
   CFX_Matrix matrix1;
   CFX_Matrix matrix2;
@@ -1383,48 +1207,16 @@
     matrix1 = *pObject2Device * matrix2.GetInverse();
   }
 
-  CAgg_PathData path_data;
-  path_data.BuildPath(pPathData, &matrix1);
+  agg::path_storage path_data = BuildAggPath(path, &matrix1);
   agg::rasterizer_scanline_aa rasterizer;
   rasterizer.clip_box(0.0f, 0.0f,
                       static_cast<float>(GetDeviceCaps(FXDC_PIXEL_WIDTH)),
                       static_cast<float>(GetDeviceCaps(FXDC_PIXEL_HEIGHT)));
-  RasterizeStroke(&rasterizer, &path_data.m_PathData, &matrix2, pGraphState,
-                  matrix1.a, !!(fill_mode & FX_STROKE_TEXT_MODE));
-  return RenderRasterizer(rasterizer, stroke_color,
-                          !!(fill_mode & FXFILL_FULLCOVER), m_bGroupKnockout);
-}
-
-bool CFX_AggDeviceDriver::SetPixel(int x, int y, uint32_t color) {
-  if (!m_pBitmap->GetBuffer())
-    return true;
-
-  if (!m_pClipRgn) {
-    if (!m_bRgbByteOrder)
-      return DibSetPixel(m_pBitmap, x, y, color);
-    RgbByteOrderSetPixel(m_pBitmap, x, y, color);
-    return true;
-  }
-  if (!m_pClipRgn->GetBox().Contains(x, y))
-    return true;
-
-  if (m_pClipRgn->GetType() == CFX_ClipRgn::RectI) {
-    if (!m_bRgbByteOrder)
-      return DibSetPixel(m_pBitmap, x, y, color);
-    RgbByteOrderSetPixel(m_pBitmap, x, y, color);
-    return true;
-  }
-  if (m_pClipRgn->GetType() != CFX_ClipRgn::MaskF)
-    return true;
-
-  int new_alpha =
-      FXARGB_A(color) * m_pClipRgn->GetMask()->GetScanline(y)[x] / 255;
-  color = (color & 0xffffff) | (new_alpha << 24);
-  if (m_bRgbByteOrder) {
-    RgbByteOrderSetPixel(m_pBitmap, x, y, color);
-    return true;
-  }
-  return DibSetPixel(m_pBitmap, x, y, color);
+  RasterizeStroke(&rasterizer, &path_data, &matrix2, pGraphState, matrix1.a,
+                  fill_options.stroke_text_mode);
+  RenderRasterizer(rasterizer, stroke_color, fill_options.full_cover,
+                   m_bGroupKnockout);
+  return true;
 }
 
 bool CFX_AggDeviceDriver::FillRectWithBlend(const FX_RECT& rect,
@@ -1433,7 +1225,7 @@
   if (blend_type != BlendMode::kNormal)
     return false;
 
-  if (!m_pBitmap->GetBuffer())
+  if (m_pBitmap->GetBuffer().empty())
     return true;
 
   FX_RECT clip_rect;
@@ -1443,14 +1235,14 @@
   if (draw_rect.IsEmpty())
     return true;
 
-  if (!m_pClipRgn || m_pClipRgn->GetType() == CFX_ClipRgn::RectI) {
+  if (!m_pClipRgn || m_pClipRgn->GetType() == CFX_ClipRgn::kRectI) {
     if (m_bRgbByteOrder) {
       RgbByteOrderCompositeRect(m_pBitmap, draw_rect.left, draw_rect.top,
                                 draw_rect.Width(), draw_rect.Height(),
                                 fill_color);
     } else {
       m_pBitmap->CompositeRect(draw_rect.left, draw_rect.top, draw_rect.Width(),
-                               draw_rect.Height(), fill_color, 0);
+                               draw_rect.Height(), fill_color);
     }
     return true;
   }
@@ -1476,21 +1268,21 @@
 bool CFX_AggDeviceDriver::GetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
                                     int left,
                                     int top) {
-  if (!m_pBitmap->GetBuffer())
+  if (m_pBitmap->GetBuffer().empty())
     return true;
 
   FX_RECT rect(left, top, left + pBitmap->GetWidth(),
                top + pBitmap->GetHeight());
   RetainPtr<CFX_DIBitmap> pBack;
   if (m_pBackdropBitmap) {
-    pBack = m_pBackdropBitmap->Clone(&rect);
+    pBack = m_pBackdropBitmap->ClipTo(rect);
     if (!pBack)
       return true;
 
     pBack->CompositeBitmap(0, 0, pBack->GetWidth(), pBack->GetHeight(),
                            m_pBitmap, 0, 0, BlendMode::kNormal, nullptr, false);
   } else {
-    pBack = m_pBitmap->Clone(&rect);
+    pBack = m_pBitmap->ClipTo(rect);
     if (!pBack)
       return true;
   }
@@ -1498,8 +1290,8 @@
   left = std::min(left, 0);
   top = std::min(top, 0);
   if (m_bRgbByteOrder) {
-    RgbByteOrderTransferBitmap(pBitmap, 0, 0, rect.Width(), rect.Height(),
-                               pBack, left, top);
+    RgbByteOrderTransferBitmap(pBitmap, rect.Width(), rect.Height(), pBack,
+                               left, top);
     return true;
   }
   return pBitmap->TransferBitmap(0, 0, rect.Width(), rect.Height(), pBack, left,
@@ -1516,10 +1308,10 @@
                                     int left,
                                     int top,
                                     BlendMode blend_type) {
-  if (!m_pBitmap->GetBuffer())
+  if (m_pBitmap->GetBuffer().empty())
     return true;
 
-  if (pBitmap->IsAlphaMask()) {
+  if (pBitmap->IsMaskFormat()) {
     return m_pBitmap->CompositeMask(left, top, src_rect.Width(),
                                     src_rect.Height(), pBitmap, argb,
                                     src_rect.left, src_rect.top, blend_type,
@@ -1539,7 +1331,7 @@
                                         const FX_RECT* pClipRect,
                                         const FXDIB_ResampleOptions& options,
                                         BlendMode blend_type) {
-  if (!m_pBitmap->GetBuffer())
+  if (m_pBitmap->GetBuffer().empty())
     return true;
 
   if (dest_width == pSource->GetWidth() &&
@@ -1571,10 +1363,10 @@
     const FXDIB_ResampleOptions& options,
     std::unique_ptr<CFX_ImageRenderer>* handle,
     BlendMode blend_type) {
-  if (!m_pBitmap->GetBuffer())
+  if (m_pBitmap->GetBuffer().empty())
     return true;
 
-  *handle = pdfium::MakeUnique<CFX_ImageRenderer>(
+  *handle = std::make_unique<CFX_ImageRenderer>(
       m_pBitmap, m_pClipRgn.get(), pSource, bitmap_alpha, argb, matrix, options,
       m_bRgbByteOrder);
   return true;
@@ -1582,41 +1374,37 @@
 
 bool CFX_AggDeviceDriver::ContinueDIBits(CFX_ImageRenderer* pHandle,
                                          PauseIndicatorIface* pPause) {
-  return !m_pBitmap->GetBuffer() || pHandle->Continue(pPause);
+  return m_pBitmap->GetBuffer().empty() || pHandle->Continue(pPause);
 }
 
-#ifndef _SKIA_SUPPORT_
-CFX_DefaultRenderDevice::CFX_DefaultRenderDevice() {}
+}  // namespace pdfium
 
-CFX_DefaultRenderDevice::~CFX_DefaultRenderDevice() {}
-
-bool CFX_DefaultRenderDevice::Attach(
-    const RetainPtr<CFX_DIBitmap>& pBitmap,
+bool CFX_DefaultRenderDevice::AttachAggImpl(
+    RetainPtr<CFX_DIBitmap> pBitmap,
     bool bRgbByteOrder,
-    const RetainPtr<CFX_DIBitmap>& pBackdropBitmap,
+    RetainPtr<CFX_DIBitmap> pBackdropBitmap,
     bool bGroupKnockout) {
   if (!pBitmap)
     return false;
 
   SetBitmap(pBitmap);
-  SetDeviceDriver(pdfium::MakeUnique<CFX_AggDeviceDriver>(
-      pBitmap, bRgbByteOrder, pBackdropBitmap, bGroupKnockout));
+  SetDeviceDriver(std::make_unique<pdfium::CFX_AggDeviceDriver>(
+      std::move(pBitmap), bRgbByteOrder, std::move(pBackdropBitmap),
+      bGroupKnockout));
   return true;
 }
 
-bool CFX_DefaultRenderDevice::Create(
+bool CFX_DefaultRenderDevice::CreateAgg(
     int width,
     int height,
     FXDIB_Format format,
-    const RetainPtr<CFX_DIBitmap>& pBackdropBitmap) {
+    RetainPtr<CFX_DIBitmap> pBackdropBitmap) {
   auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
   if (!pBitmap->Create(width, height, format))
     return false;
 
   SetBitmap(pBitmap);
-  SetDeviceDriver(pdfium::MakeUnique<CFX_AggDeviceDriver>(
-      pBitmap, false, pBackdropBitmap, false));
+  SetDeviceDriver(std::make_unique<pdfium::CFX_AggDeviceDriver>(
+      std::move(pBitmap), false, std::move(pBackdropBitmap), false));
   return true;
 }
-
-#endif
diff --git a/core/fxge/agg/fx_agg_driver.h b/core/fxge/agg/fx_agg_driver.h
index 0578421..523bb0c 100644
--- a/core/fxge/agg/fx_agg_driver.h
+++ b/core/fxge/agg/fx_agg_driver.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,31 +11,30 @@
 #include <vector>
 
 #include "build/build_config.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/renderdevicedriver_iface.h"
-#include "third_party/agg23/agg_clip_liang_barsky.h"
-#include "third_party/agg23/agg_path_storage.h"
-#include "third_party/agg23/agg_rasterizer_scanline_aa.h"
+
+#if BUILDFLAG(IS_APPLE)
+#include "core/fxcrt/unowned_ptr_exclusion.h"
+#endif
 
 class CFX_ClipRgn;
 class CFX_GraphStateData;
 class CFX_Matrix;
-class CFX_PathData;
+class CFX_Path;
 
-class CAgg_PathData {
- public:
-  CAgg_PathData() {}
-  ~CAgg_PathData() {}
-  void BuildPath(const CFX_PathData* pPathData,
-                 const CFX_Matrix* pObject2Device);
+namespace pdfium {
 
-  agg::path_storage m_PathData;
-};
+namespace agg {
+class rasterizer_scanline_aa;
+}  // namespace agg
 
 class CFX_AggDeviceDriver final : public RenderDeviceDriverIface {
  public:
-  CFX_AggDeviceDriver(const RetainPtr<CFX_DIBitmap>& pBitmap,
+  CFX_AggDeviceDriver(RetainPtr<CFX_DIBitmap> pBitmap,
                       bool bRgbByteOrder,
-                      const RetainPtr<CFX_DIBitmap>& pBackdropBitmap,
+                      RetainPtr<CFX_DIBitmap> pBackdropBitmap,
                       bool bGroupKnockout);
   ~CFX_AggDeviceDriver() override;
 
@@ -47,20 +46,19 @@
   int GetDeviceCaps(int caps_id) const override;
   void SaveState() override;
   void RestoreState(bool bKeepSaved) override;
-  bool SetClip_PathFill(const CFX_PathData* pPathData,
+  bool SetClip_PathFill(const CFX_Path& path,
                         const CFX_Matrix* pObject2Device,
-                        int fill_mode) override;
-  bool SetClip_PathStroke(const CFX_PathData* pPathData,
+                        const CFX_FillRenderOptions& fill_options) override;
+  bool SetClip_PathStroke(const CFX_Path& path,
                           const CFX_Matrix* pObject2Device,
                           const CFX_GraphStateData* pGraphState) override;
-  bool DrawPath(const CFX_PathData* pPathData,
+  bool DrawPath(const CFX_Path& path,
                 const CFX_Matrix* pObject2Device,
                 const CFX_GraphStateData* pGraphState,
                 uint32_t fill_color,
                 uint32_t stroke_color,
-                int fill_mode,
+                const CFX_FillRenderOptions& fill_options,
                 BlendMode blend_type) override;
-  bool SetPixel(int x, int y, uint32_t color) override;
   bool FillRectWithBlend(const FX_RECT& rect,
                          uint32_t fill_color,
                          BlendMode blend_type) override;
@@ -93,34 +91,36 @@
                    BlendMode blend_type) override;
   bool ContinueDIBits(CFX_ImageRenderer* handle,
                       PauseIndicatorIface* pPause) override;
-  bool DrawDeviceText(int nChars,
-                      const TextCharPos* pCharPos,
+  bool DrawDeviceText(pdfium::span<const TextCharPos> pCharPos,
                       CFX_Font* pFont,
                       const CFX_Matrix& mtObject2Device,
                       float font_size,
-                      uint32_t color) override;
+                      uint32_t color,
+                      const CFX_TextRenderOptions& options) override;
   int GetDriverType() const override;
+  bool MultiplyAlpha(float alpha) override;
+  bool MultiplyAlpha(const RetainPtr<CFX_DIBBase>& mask) override;
 
-  bool RenderRasterizer(agg::rasterizer_scanline_aa& rasterizer,
+ private:
+  void RenderRasterizer(pdfium::agg::rasterizer_scanline_aa& rasterizer,
                         uint32_t color,
                         bool bFullCover,
                         bool bGroupKnockout);
 
-  void SetClipMask(agg::rasterizer_scanline_aa& rasterizer);
+  void SetClipMask(pdfium::agg::rasterizer_scanline_aa& rasterizer);
 
-  virtual uint8_t* GetBuffer() const;
-
- private:
   RetainPtr<CFX_DIBitmap> const m_pBitmap;
   std::unique_ptr<CFX_ClipRgn> m_pClipRgn;
   std::vector<std::unique_ptr<CFX_ClipRgn>> m_StateStack;
-#if defined(OS_MACOSX)
-  void* m_pPlatformGraphics = nullptr;
+#if BUILDFLAG(IS_APPLE)
+  UNOWNED_PTR_EXCLUSION void* m_pPlatformGraphics = nullptr;
 #endif
-  int m_FillFlags = 0;
+  CFX_FillRenderOptions m_FillOptions;
   const bool m_bRgbByteOrder;
   const bool m_bGroupKnockout;
   RetainPtr<CFX_DIBitmap> m_pBackdropBitmap;
 };
 
+}  // namespace pdfium
+
 #endif  // CORE_FXGE_AGG_FX_AGG_DRIVER_H_
diff --git a/core/fxge/android/cfpf_skiadevicemodule.cpp b/core/fxge/android/cfpf_skiadevicemodule.cpp
index bb3f70a..1170bb6 100644
--- a/core/fxge/android/cfpf_skiadevicemodule.cpp
+++ b/core/fxge/android/cfpf_skiadevicemodule.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,6 @@
 #include <utility>
 
 #include "core/fxge/android/cfpf_skiafontmgr.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -23,9 +22,9 @@
   return gs_pPFModule;
 }
 
-CFPF_SkiaDeviceModule::CFPF_SkiaDeviceModule() {}
+CFPF_SkiaDeviceModule::CFPF_SkiaDeviceModule() = default;
 
-CFPF_SkiaDeviceModule::~CFPF_SkiaDeviceModule() {}
+CFPF_SkiaDeviceModule::~CFPF_SkiaDeviceModule() = default;
 
 void CFPF_SkiaDeviceModule::Destroy() {
   delete gs_pPFModule;
@@ -34,7 +33,7 @@
 
 CFPF_SkiaFontMgr* CFPF_SkiaDeviceModule::GetFontMgr() {
   if (!m_pFontMgr) {
-    auto pNewMgr = pdfium::MakeUnique<CFPF_SkiaFontMgr>();
+    auto pNewMgr = std::make_unique<CFPF_SkiaFontMgr>();
     if (!pNewMgr->InitFTLibrary())
       return nullptr;
     m_pFontMgr = std::move(pNewMgr);
diff --git a/core/fxge/android/cfpf_skiadevicemodule.h b/core/fxge/android/cfpf_skiadevicemodule.h
index 558b084..2a51f4d 100644
--- a/core/fxge/android/cfpf_skiadevicemodule.h
+++ b/core/fxge/android/cfpf_skiadevicemodule.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxge/android/cfpf_skiafont.cpp b/core/fxge/android/cfpf_skiafont.cpp
index 1c4eece..3039ac7 100644
--- a/core/fxge/android/cfpf_skiafont.cpp
+++ b/core/fxge/android/cfpf_skiafont.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,18 +8,20 @@
 
 #include <algorithm>
 
+#include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxge/android/cfpf_skiafontmgr.h"
 #include "core/fxge/android/cfpf_skiapathfont.h"
-#include "core/fxge/fx_freetype.h"
+#include "core/fxge/freetype/fx_freetype.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 #define FPF_EM_ADJUST(em, a) (em == 0 ? (a) : (a)*1000 / em)
 
 CFPF_SkiaFont::CFPF_SkiaFont(CFPF_SkiaFontMgr* pFontMgr,
                              const CFPF_SkiaPathFont* pFont,
                              uint32_t dwStyle,
-                             uint8_t uCharset)
+                             FX_Charset uCharset)
     : m_pFontMgr(pFontMgr),
       m_pFont(pFont),
       m_Face(m_pFontMgr->GetFontFace(m_pFont->path(), m_pFont->face_index())),
@@ -55,8 +57,9 @@
                     FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH)) {
     return 0;
   }
-  return FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
-                       FXFT_Get_Glyph_HoriAdvance(GetFaceRec()));
+  return static_cast<int32_t>(
+      FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                    FXFT_Get_Glyph_HoriAdvance(GetFaceRec())));
 }
 
 int32_t CFPF_SkiaFont::GetAscent() const {
@@ -93,10 +96,10 @@
     FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &cbox);
     int32_t x_ppem = GetFaceRec()->size->metrics.x_ppem;
     int32_t y_ppem = GetFaceRec()->size->metrics.y_ppem;
-    rtBBox.left = FPF_EM_ADJUST(x_ppem, cbox.xMin);
-    rtBBox.right = FPF_EM_ADJUST(x_ppem, cbox.xMax);
-    rtBBox.top = FPF_EM_ADJUST(y_ppem, cbox.yMax);
-    rtBBox.bottom = FPF_EM_ADJUST(y_ppem, cbox.yMin);
+    rtBBox.left = static_cast<int32_t>(FPF_EM_ADJUST(x_ppem, cbox.xMin));
+    rtBBox.right = static_cast<int32_t>(FPF_EM_ADJUST(x_ppem, cbox.xMax));
+    rtBBox.top = static_cast<int32_t>(FPF_EM_ADJUST(y_ppem, cbox.yMax));
+    rtBBox.bottom = static_cast<int32_t>(FPF_EM_ADJUST(y_ppem, cbox.yMin));
     rtBBox.top = std::min(rtBBox.top, GetAscent());
     rtBBox.bottom = std::max(rtBBox.bottom, GetDescent());
     FT_Done_Glyph(glyph);
@@ -106,16 +109,20 @@
                     FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH)) {
     return false;
   }
-  rtBBox.left = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
-                              FXFT_Get_Glyph_HoriBearingX(GetFaceRec()));
-  rtBBox.bottom = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
-                                FXFT_Get_Glyph_HoriBearingY(GetFaceRec()));
-  rtBBox.right = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
-                               FXFT_Get_Glyph_HoriBearingX(GetFaceRec()) +
-                                   FXFT_Get_Glyph_Width(GetFaceRec()));
-  rtBBox.top = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
-                             FXFT_Get_Glyph_HoriBearingY(GetFaceRec()) -
-                                 FXFT_Get_Glyph_Height(GetFaceRec()));
+  rtBBox.left = static_cast<int32_t>(
+      FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                    FXFT_Get_Glyph_HoriBearingX(GetFaceRec())));
+  rtBBox.bottom = static_cast<int32_t>(
+      FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                    FXFT_Get_Glyph_HoriBearingY(GetFaceRec())));
+  rtBBox.right = static_cast<int32_t>(
+      FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                    FXFT_Get_Glyph_HoriBearingX(GetFaceRec()) +
+                        FXFT_Get_Glyph_Width(GetFaceRec())));
+  rtBBox.top = static_cast<int32_t>(
+      FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                    FXFT_Get_Glyph_HoriBearingY(GetFaceRec()) -
+                        FXFT_Get_Glyph_Height(GetFaceRec())));
   return true;
 }
 
@@ -123,14 +130,18 @@
   if (!m_Face) {
     return false;
   }
-  rtBBox.left = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
-                              FXFT_Get_Face_xMin(GetFaceRec()));
-  rtBBox.top = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
-                             FXFT_Get_Face_yMin(GetFaceRec()));
-  rtBBox.right = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
-                               FXFT_Get_Face_xMax(GetFaceRec()));
-  rtBBox.bottom = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
-                                FXFT_Get_Face_yMax(GetFaceRec()));
+  rtBBox.left =
+      static_cast<int32_t>(FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                                         FXFT_Get_Face_xMin(GetFaceRec())));
+  rtBBox.top =
+      static_cast<int32_t>(FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                                         FXFT_Get_Face_yMin(GetFaceRec())));
+  rtBBox.right =
+      static_cast<int32_t>(FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                                         FXFT_Get_Face_xMax(GetFaceRec())));
+  rtBBox.bottom =
+      static_cast<int32_t>(FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                                         FXFT_Get_Face_yMax(GetFaceRec())));
   return true;
 }
 
@@ -147,7 +158,7 @@
 
   auto* info = static_cast<TT_Postscript*>(
       FT_Get_Sfnt_Table(GetFaceRec(), ft_sfnt_post));
-  return info ? info->italicAngle : 0;
+  return info ? static_cast<int32_t>(info->italicAngle) : 0;
 }
 
 uint32_t CFPF_SkiaFont::GetFontData(uint32_t dwTable,
diff --git a/core/fxge/android/cfpf_skiafont.h b/core/fxge/android/cfpf_skiafont.h
index 702a38e..c320806 100644
--- a/core/fxge/android/cfpf_skiafont.h
+++ b/core/fxge/android/cfpf_skiafont.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,15 @@
 #ifndef CORE_FXGE_ANDROID_CFPF_SKIAFONT_H_
 #define CORE_FXGE_ANDROID_CFPF_SKIAFONT_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include <stdint.h>
+
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/fx_codepage_forward.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_face.h"
-#include "core/fxge/fx_freetype.h"
-#include "third_party/base/span.h"
+#include "core/fxge/freetype/fx_freetype.h"
+#include "third_party/base/containers/span.h"
 
 class CFPF_SkiaFontMgr;
 class CFPF_SkiaPathFont;
@@ -23,7 +26,7 @@
   CFPF_SkiaFont(CFPF_SkiaFontMgr* pFontMgr,
                 const CFPF_SkiaPathFont* pFont,
                 uint32_t dwStyle,
-                uint8_t uCharset);
+                FX_Charset uCharset);
   ~CFPF_SkiaFont();
 
   bool IsValid() const { return !!m_Face; }
@@ -31,7 +34,7 @@
   ByteString GetFamilyName();
   ByteString GetPsName();
   uint32_t GetFontStyle() const { return m_dwStyle; }
-  uint8_t GetCharset() const { return m_uCharset; }
+  FX_Charset GetCharset() const { return m_uCharset; }
   int32_t GetGlyphIndex(wchar_t wUnicode);
   int32_t GetGlyphWidth(int32_t iGlyphIndex);
   int32_t GetAscent() const;
@@ -48,7 +51,7 @@
   UnownedPtr<const CFPF_SkiaPathFont> const m_pFont;
   RetainPtr<CFX_Face> const m_Face;
   const uint32_t m_dwStyle;
-  const uint8_t m_uCharset;
+  const FX_Charset m_uCharset;
 };
 
 #endif  // CORE_FXGE_ANDROID_CFPF_SKIAFONT_H_
diff --git a/core/fxge/android/cfpf_skiafontmgr.cpp b/core/fxge/android/cfpf_skiafontmgr.cpp
index b5e7a20..036f812 100644
--- a/core/fxge/android/cfpf_skiafontmgr.cpp
+++ b/core/fxge/android/cfpf_skiafontmgr.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,18 +7,18 @@
 #include "core/fxge/android/cfpf_skiafontmgr.h"
 
 #include <algorithm>
+#include <iterator>
 #include <utility>
 
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/fx_memory.h"
-#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/fx_folder.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxge/android/cfpf_skiafont.h"
 #include "core/fxge/android/cfpf_skiapathfont.h"
+#include "core/fxge/freetype/fx_freetype.h"
 #include "core/fxge/fx_font.h"
-#include "core/fxge/fx_freetype.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/containers/adapters.h"
 
 namespace {
 
@@ -32,7 +32,7 @@
   uint32_t dwSubSt;
 };
 
-const FPF_SKIAFONTMAP g_SkiaFontmap[] = {
+const FPF_SKIAFONTMAP kSkiaFontmap[] = {
     {0x58c5083, 0xc8d2e345},  {0x5dfade2, 0xe1633081},
     {0x684317d, 0xe1633081},  {0x14ee2d13, 0xc8d2e345},
     {0x3918fe2d, 0xbbeeec72}, {0x3b98b31c, 0xe1633081},
@@ -46,7 +46,7 @@
     {0xfb4ce0de, 0xe1633081},
 };
 
-const FPF_SKIAFONTMAP g_SkiaSansFontMap[] = {
+const FPF_SKIAFONTMAP kSkiaSansFontMap[] = {
     {0x58c5083, 0xd5b8d10f},  {0x14ee2d13, 0xd5b8d10f},
     {0x779ce19d, 0xd5b8d10f}, {0xcb7a04c8, 0xd5b8d10f},
     {0xfb4ce0de, 0xd5b8d10f},
@@ -65,18 +65,6 @@
   return 0;
 }
 
-uint32_t FPF_GetHashCode_StringA(const char* pStr, int32_t iLength) {
-  if (!pStr)
-    return 0;
-  if (iLength < 0)
-    iLength = strlen(pStr);
-  const char* pStrEnd = pStr + iLength;
-  uint32_t uHashCode = 0;
-  while (pStr < pStrEnd)
-    uHashCode = 31 * uHashCode + tolower(*pStr++);
-  return uHashCode;
-}
-
 enum FPF_SKIACHARSET {
   FPF_SKIACHARSET_Ansi = 1 << 0,
   FPF_SKIACHARSET_Default = 1 << 1,
@@ -99,58 +87,56 @@
   FPF_SKIACHARSET_OEM = 1 << 18,
 };
 
-uint32_t FPF_SkiaGetCharset(uint8_t uCharset) {
+uint32_t FPF_SkiaGetCharset(FX_Charset uCharset) {
   switch (uCharset) {
-    case FX_CHARSET_ANSI:
+    case FX_Charset::kANSI:
       return FPF_SKIACHARSET_Ansi;
-    case FX_CHARSET_Default:
+    case FX_Charset::kDefault:
       return FPF_SKIACHARSET_Default;
-    case FX_CHARSET_Symbol:
+    case FX_Charset::kSymbol:
       return FPF_SKIACHARSET_Symbol;
-    case FX_CHARSET_ShiftJIS:
+    case FX_Charset::kShiftJIS:
       return FPF_SKIACHARSET_ShiftJIS;
-    case FX_CHARSET_Hangul:
+    case FX_Charset::kHangul:
       return FPF_SKIACHARSET_Korean;
-    case FX_CHARSET_ChineseSimplified:
+    case FX_Charset::kChineseSimplified:
       return FPF_SKIACHARSET_GB2312;
-    case FX_CHARSET_ChineseTraditional:
+    case FX_Charset::kChineseTraditional:
       return FPF_SKIACHARSET_BIG5;
-    case FX_CHARSET_MSWin_Greek:
+    case FX_Charset::kMSWin_Greek:
       return FPF_SKIACHARSET_Greek;
-    case FX_CHARSET_MSWin_Turkish:
+    case FX_Charset::kMSWin_Turkish:
       return FPF_SKIACHARSET_Turkish;
-    case FX_CHARSET_MSWin_Hebrew:
+    case FX_Charset::kMSWin_Hebrew:
       return FPF_SKIACHARSET_Hebrew;
-    case FX_CHARSET_MSWin_Arabic:
+    case FX_Charset::kMSWin_Arabic:
       return FPF_SKIACHARSET_Arabic;
-    case FX_CHARSET_MSWin_Baltic:
+    case FX_Charset::kMSWin_Baltic:
       return FPF_SKIACHARSET_Baltic;
-    case FX_CHARSET_MSWin_Cyrillic:
+    case FX_Charset::kMSWin_Cyrillic:
       return FPF_SKIACHARSET_Cyrillic;
-    case FX_CHARSET_Thai:
+    case FX_Charset::kThai:
       return FPF_SKIACHARSET_Thai;
-    case FX_CHARSET_MSWin_EasternEuropean:
+    case FX_Charset::kMSWin_EasternEuropean:
       return FPF_SKIACHARSET_EeasternEuropean;
+    default:
+      return FPF_SKIACHARSET_Default;
   }
-  return FPF_SKIACHARSET_Default;
 }
 
-uint32_t FPF_SKIANormalizeFontName(ByteStringView bsfamily) {
-  uint32_t dwHash = 0;
-  int32_t iLength = bsfamily.GetLength();
-  const char* pBuffer = bsfamily.unterminated_c_str();
-  for (int32_t i = 0; i < iLength; i++) {
-    char ch = pBuffer[i];
+uint32_t FPF_SKIANormalizeFontName(ByteStringView bsFamily) {
+  uint32_t uHashCode = 0;
+  for (unsigned char ch : bsFamily) {
     if (ch == ' ' || ch == '-' || ch == ',')
       continue;
-    dwHash = 31 * dwHash + tolower(ch);
+    uHashCode = 31 * uHashCode + tolower(ch);
   }
-  return dwHash;
+  return uHashCode;
 }
 
 uint32_t FPF_SKIAGetFamilyHash(ByteStringView bsFamily,
                                uint32_t dwStyle,
-                               uint8_t uCharset) {
+                               FX_Charset uCharset) {
   ByteString bsFont(bsFamily);
   if (FontStyleIsForceBold(dwStyle))
     bsFont += "Bold";
@@ -158,11 +144,11 @@
     bsFont += "Italic";
   if (FontStyleIsSerif(dwStyle))
     bsFont += "Serif";
-  bsFont += uCharset;
-  return FPF_GetHashCode_StringA(bsFont.c_str(), bsFont.GetLength());
+  bsFont += static_cast<uint8_t>(uCharset);
+  return FX_HashCode_GetA(bsFont.AsStringView());
 }
 
-bool FPF_SkiaIsCJK(uint8_t uCharset) {
+bool FPF_SkiaIsCJK(FX_Charset uCharset) {
   return FX_CharSetIsCJK(uCharset);
 }
 
@@ -178,7 +164,7 @@
   return bsName.Contains("arabic");
 }
 
-const uint32_t g_FPFSkiaFontCharsets[] = {
+const uint32_t kFPFSkiaFontCharsets[] = {
     FPF_SKIACHARSET_Ansi,
     FPF_SKIACHARSET_EeasternEuropean,
     FPF_SKIACHARSET_Cyrillic,
@@ -218,7 +204,7 @@
   if (pOS2) {
     for (int32_t i = 0; i < 32; i++) {
       if (pOS2->ulCodePageRange1 & (1 << i))
-        dwCharset |= g_FPFSkiaFontCharsets[i];
+        dwCharset |= kFPFSkiaFontCharsets[i];
     }
   }
   dwCharset |= FPF_SKIACHARSET_Default;
@@ -255,7 +241,7 @@
 }
 
 CFPF_SkiaFont* CFPF_SkiaFontMgr::CreateFont(ByteStringView bsFamilyname,
-                                            uint8_t uCharset,
+                                            FX_Charset uCharset,
                                             uint32_t dwStyle) {
   uint32_t dwHash = FPF_SKIAGetFamilyHash(bsFamilyname, dwStyle, uCharset);
   auto family_iter = m_FamilyFonts.find(dwHash);
@@ -263,78 +249,77 @@
     return family_iter->second.get();
 
   uint32_t dwFaceName = FPF_SKIANormalizeFontName(bsFamilyname);
-  uint32_t dwSubst = FPF_SkiaGetSubstFont(dwFaceName, g_SkiaFontmap,
-                                          FX_ArraySize(g_SkiaFontmap));
-  uint32_t dwSubstSans = FPF_SkiaGetSubstFont(dwFaceName, g_SkiaSansFontMap,
-                                              FX_ArraySize(g_SkiaSansFontMap));
+  uint32_t dwSubst =
+      FPF_SkiaGetSubstFont(dwFaceName, kSkiaFontmap, std::size(kSkiaFontmap));
+  uint32_t dwSubstSans = FPF_SkiaGetSubstFont(dwFaceName, kSkiaSansFontMap,
+                                              std::size(kSkiaSansFontMap));
   bool bMaybeSymbol = FPF_SkiaMaybeSymbol(bsFamilyname);
-  if (uCharset != FX_CHARSET_MSWin_Arabic &&
+  if (uCharset != FX_Charset::kMSWin_Arabic &&
       FPF_SkiaMaybeArabic(bsFamilyname)) {
-    uCharset = FX_CHARSET_MSWin_Arabic;
-  } else if (uCharset == FX_CHARSET_ANSI) {
-    uCharset = FX_CHARSET_Default;
+    uCharset = FX_Charset::kMSWin_Arabic;
+  } else if (uCharset == FX_Charset::kANSI) {
+    uCharset = FX_Charset::kDefault;
   }
   int32_t nExpectVal = FPF_SKIAMATCHWEIGHT_NAME1 + FPF_SKIAMATCHWEIGHT_1 * 3 +
                        FPF_SKIAMATCHWEIGHT_2 * 2;
   const CFPF_SkiaPathFont* pBestFont = nullptr;
   int32_t nMax = -1;
   int32_t nGlyphNum = 0;
-  for (auto face_iter = m_FontFaces.rbegin(); face_iter != m_FontFaces.rend();
-       ++face_iter) {
-    const CFPF_SkiaPathFont* pFont = face_iter->get();
-    if (!(pFont->charsets() & FPF_SkiaGetCharset(uCharset)))
+  for (const std::unique_ptr<CFPF_SkiaPathFont>& font :
+       pdfium::base::Reversed(m_FontFaces)) {
+    if (!(font->charsets() & FPF_SkiaGetCharset(uCharset)))
       continue;
     int32_t nFind = 0;
-    uint32_t dwSysFontName = FPF_SKIANormalizeFontName(pFont->family());
+    uint32_t dwSysFontName = FPF_SKIANormalizeFontName(font->family());
     if (dwFaceName == dwSysFontName)
       nFind += FPF_SKIAMATCHWEIGHT_NAME1;
     bool bMatchedName = (nFind == FPF_SKIAMATCHWEIGHT_NAME1);
-    if (FontStyleIsForceBold(dwStyle) == FontStyleIsForceBold(pFont->style()))
+    if (FontStyleIsForceBold(dwStyle) == FontStyleIsForceBold(font->style()))
       nFind += FPF_SKIAMATCHWEIGHT_1;
-    if (FontStyleIsItalic(dwStyle) == FontStyleIsItalic(pFont->style()))
+    if (FontStyleIsItalic(dwStyle) == FontStyleIsItalic(font->style()))
       nFind += FPF_SKIAMATCHWEIGHT_1;
     if (FontStyleIsFixedPitch(dwStyle) ==
-        FontStyleIsFixedPitch(pFont->style())) {
+        FontStyleIsFixedPitch(font->style())) {
       nFind += FPF_SKIAMATCHWEIGHT_2;
     }
-    if (FontStyleIsSerif(dwStyle) == FontStyleIsSerif(pFont->style()))
+    if (FontStyleIsSerif(dwStyle) == FontStyleIsSerif(font->style()))
       nFind += FPF_SKIAMATCHWEIGHT_1;
-    if (FontStyleIsScript(dwStyle) == FontStyleIsScript(pFont->style()))
+    if (FontStyleIsScript(dwStyle) == FontStyleIsScript(font->style()))
       nFind += FPF_SKIAMATCHWEIGHT_2;
     if (dwSubst == dwSysFontName || dwSubstSans == dwSysFontName) {
       nFind += FPF_SKIAMATCHWEIGHT_NAME2;
       bMatchedName = true;
     }
-    if (uCharset == FX_CHARSET_Default || bMaybeSymbol) {
+    if (uCharset == FX_Charset::kDefault || bMaybeSymbol) {
       if (nFind > nMax && bMatchedName) {
         nMax = nFind;
-        pBestFont = face_iter->get();
+        pBestFont = font.get();
       }
     } else if (FPF_SkiaIsCJK(uCharset)) {
-      if (bMatchedName || pFont->glyph_num() > nGlyphNum) {
-        pBestFont = face_iter->get();
-        nGlyphNum = pFont->glyph_num();
+      if (bMatchedName || font->glyph_num() > nGlyphNum) {
+        pBestFont = font.get();
+        nGlyphNum = font->glyph_num();
       }
     } else if (nFind > nMax) {
       nMax = nFind;
-      pBestFont = face_iter->get();
+      pBestFont = font.get();
     }
     if (nExpectVal <= nFind) {
-      pBestFont = face_iter->get();
+      pBestFont = font.get();
       break;
     }
   }
   if (!pBestFont)
     return nullptr;
 
-  auto pFont =
-      pdfium::MakeUnique<CFPF_SkiaFont>(this, pBestFont, dwStyle, uCharset);
-  if (!pFont->IsValid())
+  auto font =
+      std::make_unique<CFPF_SkiaFont>(this, pBestFont, dwStyle, uCharset);
+  if (!font->IsValid())
     return nullptr;
 
-  CFPF_SkiaFont* pRet = pFont.get();
-  m_FamilyFonts[dwHash] = std::move(pFont);
-  return pRet;
+  CFPF_SkiaFont* ret = font.get();
+  m_FamilyFonts[dwHash] = std::move(font);
+  return ret;
 }
 
 RetainPtr<CFX_Face> CFPF_SkiaFontMgr::GetFontFace(ByteStringView bsFile,
@@ -358,14 +343,13 @@
 }
 
 void CFPF_SkiaFontMgr::ScanPath(const ByteString& path) {
-  std::unique_ptr<FX_FolderHandle, FxFolderHandleCloser> handle(
-      FX_OpenFolder(path.c_str()));
+  std::unique_ptr<FX_Folder> handle = FX_Folder::OpenFolder(path);
   if (!handle)
     return;
 
   ByteString filename;
   bool bFolder = false;
-  while (FX_GetNextFile(handle.get(), &filename, &bFolder)) {
+  while (handle->GetNextFile(&filename, &bFolder)) {
     if (bFolder) {
       if (filename == "." || filename == "..")
         continue;
@@ -417,7 +401,7 @@
   if (pOS2 && (pOS2->ulCodePageRange1 & (1 << 31)))
     dwStyle |= FXFONT_SYMBOLIC;
 
-  return pdfium::MakeUnique<CFPF_SkiaPathFont>(
+  return std::make_unique<CFPF_SkiaPathFont>(
       file, FXFT_Get_Face_Family_Name(face->GetRec()), dwStyle,
       face->GetRec()->face_index, FPF_SkiaGetFaceCharset(pOS2),
       face->GetRec()->num_glyphs);
diff --git a/core/fxge/android/cfpf_skiafontmgr.h b/core/fxge/android/cfpf_skiafontmgr.h
index 4d5c788..0095854 100644
--- a/core/fxge/android/cfpf_skiafontmgr.h
+++ b/core/fxge/android/cfpf_skiafontmgr.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,10 +11,11 @@
 #include <memory>
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/fx_codepage_forward.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/cfx_face.h"
-#include "core/fxge/fx_freetype.h"
+#include "core/fxge/freetype/fx_freetype.h"
 
 class CFPF_SkiaFont;
 class CFPF_SkiaPathFont;
@@ -26,7 +27,7 @@
 
   void LoadSystemFonts();
   CFPF_SkiaFont* CreateFont(ByteStringView bsFamilyname,
-                            uint8_t uCharset,
+                            FX_Charset uCharset,
                             uint32_t dwStyle);
 
   bool InitFTLibrary();
diff --git a/core/fxge/android/cfpf_skiapathfont.cpp b/core/fxge/android/cfpf_skiapathfont.cpp
index a9f96ac..a074b72 100644
--- a/core/fxge/android/cfpf_skiapathfont.cpp
+++ b/core/fxge/android/cfpf_skiapathfont.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxge/android/cfpf_skiapathfont.h b/core/fxge/android/cfpf_skiapathfont.h
index d091dc9..cad3545 100644
--- a/core/fxge/android/cfpf_skiapathfont.h
+++ b/core/fxge/android/cfpf_skiapathfont.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,9 @@
 #ifndef CORE_FXGE_ANDROID_CFPF_SKIAPATHFONT_H_
 #define CORE_FXGE_ANDROID_CFPF_SKIAPATHFONT_H_
 
+#include <stdint.h>
+
 #include "core/fxcrt/bytestring.h"
-#include "core/fxcrt/fx_system.h"
 
 class CFPF_SkiaPathFont {
  public:
diff --git a/core/fxge/android/cfx_androidfontinfo.cpp b/core/fxge/android/cfx_androidfontinfo.cpp
index 206003d..2799dcd 100644
--- a/core/fxge/android/cfx_androidfontinfo.cpp
+++ b/core/fxge/android/cfx_androidfontinfo.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,8 +12,10 @@
 #include "core/fxge/cfx_fontmapper.h"
 #include "core/fxge/fx_font.h"
 
-CFX_AndroidFontInfo::CFX_AndroidFontInfo() : m_pFontMgr(nullptr) {}
-CFX_AndroidFontInfo::~CFX_AndroidFontInfo() {}
+CFX_AndroidFontInfo::CFX_AndroidFontInfo() = default;
+
+CFX_AndroidFontInfo::~CFX_AndroidFontInfo() = default;
+
 bool CFX_AndroidFontInfo::Init(CFPF_SkiaFontMgr* pFontMgr) {
   if (!pFontMgr)
     return false;
@@ -29,9 +31,9 @@
 
 void* CFX_AndroidFontInfo::MapFont(int weight,
                                    bool bItalic,
-                                   int charset,
+                                   FX_Charset charset,
                                    int pitch_family,
-                                   const char* face) {
+                                   const ByteString& face) {
   if (!m_pFontMgr)
     return nullptr;
 
@@ -46,16 +48,16 @@
     dwStyle |= FXFONT_SCRIPT;
   if (FontFamilyIsRoman(pitch_family))
     dwStyle |= FXFONT_SERIF;
-  return m_pFontMgr->CreateFont(face, charset, dwStyle);
+  return m_pFontMgr->CreateFont(face.AsStringView(), charset, dwStyle);
 }
 
-void* CFX_AndroidFontInfo::GetFont(const char* face) {
+void* CFX_AndroidFontInfo::GetFont(const ByteString& face) {
   return nullptr;
 }
 
-uint32_t CFX_AndroidFontInfo::GetFontData(void* hFont,
-                                          uint32_t table,
-                                          pdfium::span<uint8_t> buffer) {
+size_t CFX_AndroidFontInfo::GetFontData(void* hFont,
+                                        uint32_t table,
+                                        pdfium::span<uint8_t> buffer) {
   if (!hFont)
     return 0;
   return static_cast<CFPF_SkiaFont*>(hFont)->GetFontData(table, buffer);
@@ -69,7 +71,7 @@
   return true;
 }
 
-bool CFX_AndroidFontInfo::GetFontCharset(void* hFont, int* charset) {
+bool CFX_AndroidFontInfo::GetFontCharset(void* hFont, FX_Charset* charset) {
   if (!hFont)
     return false;
 
diff --git a/core/fxge/android/cfx_androidfontinfo.h b/core/fxge/android/cfx_androidfontinfo.h
index c8b6d24..54500ba 100644
--- a/core/fxge/android/cfx_androidfontinfo.h
+++ b/core/fxge/android/cfx_androidfontinfo.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,12 @@
 #ifndef CORE_FXGE_ANDROID_CFX_ANDROIDFONTINFO_H_
 #define CORE_FXGE_ANDROID_CFX_ANDROIDFONTINFO_H_
 
-#include "core/fxcrt/fx_system.h"
+#include <stdint.h>
+
 #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;
 
@@ -26,15 +27,15 @@
   bool EnumFontList(CFX_FontMapper* pMapper) override;
   void* MapFont(int weight,
                 bool bItalic,
-                int charset,
+                FX_Charset charset,
                 int pitch_family,
-                const char* face) override;
-  void* GetFont(const char* face) override;
-  uint32_t GetFontData(void* hFont,
-                       uint32_t table,
-                       pdfium::span<uint8_t> buffer) override;
+                const ByteString& face) override;
+  void* GetFont(const ByteString& face) override;
+  size_t GetFontData(void* hFont,
+                     uint32_t table,
+                     pdfium::span<uint8_t> buffer) override;
   bool GetFaceName(void* hFont, ByteString* name) override;
-  bool GetFontCharset(void* hFont, int* charset) override;
+  bool GetFontCharset(void* hFont, FX_Charset* charset) override;
   void DeleteFont(void* hFont) override;
 
  private:
diff --git a/core/fxge/android/fx_android_imp.cpp b/core/fxge/android/fx_android_imp.cpp
deleted file mode 100644
index 147011c..0000000
--- a/core/fxge/android/fx_android_imp.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxge/cfx_gemodule.h"
-
-#include <memory>
-#include <utility>
-
-#include "core/fxge/android/cfpf_skiadevicemodule.h"
-#include "core/fxge/android/cfx_androidfontinfo.h"
-#include "core/fxge/cfx_fontmgr.h"
-#include "third_party/base/ptr_util.h"
-
-class CAndroidPlatform : public CFX_GEModule::PlatformIface {
- public:
-  CAndroidPlatform() = default;
-  ~CAndroidPlatform() override {
-    if (m_pDeviceModule)
-      m_pDeviceModule->Destroy();
-  }
-
-  void Init() override {
-    m_pDeviceModule = CFPF_GetSkiaDeviceModule();
-    CFPF_SkiaFontMgr* pFontMgr = m_pDeviceModule->GetFontMgr();
-    if (!pFontMgr)
-      return;
-
-    auto pFontInfo = pdfium::MakeUnique<CFX_AndroidFontInfo>();
-    pFontInfo->Init(pFontMgr);
-    CFX_GEModule::Get()->GetFontMgr()->SetSystemFontInfo(std::move(pFontInfo));
-  }
-
- private:
-  CFPF_SkiaDeviceModule* m_pDeviceModule = nullptr;
-};
-
-// static
-std::unique_ptr<CFX_GEModule::PlatformIface>
-CFX_GEModule::PlatformIface::Create() {
-  return pdfium::MakeUnique<CAndroidPlatform>();
-}
diff --git a/core/fxge/android/fx_android_impl.cpp b/core/fxge/android/fx_android_impl.cpp
new file mode 100644
index 0000000..12783f5
--- /dev/null
+++ b/core/fxge/android/fx_android_impl.cpp
@@ -0,0 +1,44 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include <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"
+#include "core/fxge/cfx_gemodule.h"
+
+class CAndroidPlatform : public CFX_GEModule::PlatformIface {
+ public:
+  CAndroidPlatform() = default;
+  ~CAndroidPlatform() override {
+    if (m_pDeviceModule)
+      m_pDeviceModule->Destroy();
+  }
+
+  void Init() override { m_pDeviceModule = CFPF_GetSkiaDeviceModule(); }
+
+  std::unique_ptr<SystemFontInfoIface> CreateDefaultSystemFontInfo() override {
+    CFPF_SkiaFontMgr* pFontMgr = m_pDeviceModule->GetFontMgr();
+    if (!pFontMgr)
+      return nullptr;
+
+    auto pFontInfo = std::make_unique<CFX_AndroidFontInfo>();
+    pFontInfo->Init(pFontMgr);
+    return pFontInfo;
+  }
+
+ private:
+  UnownedPtr<CFPF_SkiaDeviceModule> m_pDeviceModule;
+};
+
+// static
+std::unique_ptr<CFX_GEModule::PlatformIface>
+CFX_GEModule::PlatformIface::Create() {
+  return std::make_unique<CAndroidPlatform>();
+}
diff --git a/core/fxge/apple/apple_int.h b/core/fxge/apple/apple_int.h
deleted file mode 100644
index c58e75c..0000000
--- a/core/fxge/apple/apple_int.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXGE_APPLE_APPLE_INT_H_
-#define CORE_FXGE_APPLE_APPLE_INT_H_
-
-#include "core/fxcrt/fx_system.h"
-
-#include <Carbon/Carbon.h>
-
-#include "core/fxge/cfx_gemodule.h"
-#include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
-#include "core/fxge/fx_dib.h"
-#include "core/fxge/renderdevicedriver_iface.h"
-
-class CQuartz2D {
- public:
-  void* CreateGraphics(const RetainPtr<CFX_DIBitmap>& bitmap);
-  void DestroyGraphics(void* graphics);
-
-  void* CreateFont(const uint8_t* pFontData, uint32_t dwFontSize);
-  void DestroyFont(void* pFont);
-  void SetGraphicsTextMatrix(void* graphics, const CFX_Matrix& matrix);
-  bool DrawGraphicsString(void* graphics,
-                          void* font,
-                          float fontSize,
-                          uint16_t* glyphIndices,
-                          CGPoint* glyphPositions,
-                          int32_t chars,
-                          FX_ARGB argb);
-};
-
-class CApplePlatform : public CFX_GEModule::PlatformIface {
- public:
-  CApplePlatform();
-  ~CApplePlatform() override;
-
-  // CFX_GEModule::PlatformIface:
-  void Init() override;
-
-  CQuartz2D m_quartz2d;
-};
-
-#endif  // CORE_FXGE_APPLE_APPLE_INT_H_
diff --git a/core/fxge/apple/fx_apple_impl.cpp b/core/fxge/apple/fx_apple_impl.cpp
new file mode 100644
index 0000000..fa397c0
--- /dev/null
+++ b/core/fxge/apple/fx_apple_impl.cpp
@@ -0,0 +1,181 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxge/agg/fx_agg_driver.h"
+#include "core/fxge/apple/fx_apple_platform.h"
+#include "core/fxge/cfx_cliprgn.h"
+#include "core/fxge/cfx_font.h"
+#include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/cfx_glyphbitmap.h"
+#include "core/fxge/cfx_glyphcache.h"
+#include "core/fxge/cfx_renderdevice.h"
+#include "core/fxge/cfx_substfont.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/freetype/fx_freetype.h"
+#include "core/fxge/text_char_pos.h"
+#include "third_party/base/containers/span.h"
+
+namespace {
+
+void DoNothing(void* info, const void* data, size_t size) {}
+
+bool CGDrawGlyphRun(CGContextRef pContext,
+                    pdfium::span<const TextCharPos> pCharPos,
+                    CFX_Font* pFont,
+                    const CFX_Matrix& mtObject2Device,
+                    float font_size,
+                    uint32_t argb) {
+  if (pCharPos.empty())
+    return true;
+
+  bool bNegSize = font_size < 0;
+  if (bNegSize)
+    font_size = -font_size;
+
+  CFX_Matrix new_matrix = mtObject2Device;
+  CQuartz2D& quartz2d =
+      static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatform())
+          ->m_quartz2d;
+  if (!pFont->GetPlatformFont()) {
+    if (pFont->GetPsName() == "DFHeiStd-W5")
+      return false;
+
+    pFont->SetPlatformFont(quartz2d.CreateFont(pFont->GetFontSpan()));
+    if (!pFont->GetPlatformFont())
+      return false;
+  }
+  DataVector<uint16_t> glyph_indices(pCharPos.size());
+  std::vector<CGPoint> glyph_positions(pCharPos.size());
+  for (size_t i = 0; i < pCharPos.size(); i++) {
+    glyph_indices[i] =
+        pCharPos[i].m_ExtGID ? pCharPos[i].m_ExtGID : pCharPos[i].m_GlyphIndex;
+    if (bNegSize)
+      glyph_positions[i].x = -pCharPos[i].m_Origin.x;
+    else
+      glyph_positions[i].x = pCharPos[i].m_Origin.x;
+    glyph_positions[i].y = pCharPos[i].m_Origin.y;
+  }
+  if (bNegSize) {
+    new_matrix.a = -new_matrix.a;
+    new_matrix.c = -new_matrix.c;
+  } else {
+    new_matrix.b = -new_matrix.b;
+    new_matrix.d = -new_matrix.d;
+  }
+  quartz2d.SetGraphicsTextMatrix(pContext, new_matrix);
+  return quartz2d.DrawGraphicsString(pContext, pFont->GetPlatformFont(),
+                                     font_size, glyph_indices, glyph_positions,
+                                     argb);
+}
+
+}  // namespace
+
+namespace pdfium {
+
+void CFX_AggDeviceDriver::InitPlatform() {
+  CQuartz2D& quartz2d =
+      static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatform())
+          ->m_quartz2d;
+  m_pPlatformGraphics = quartz2d.CreateGraphics(m_pBitmap);
+}
+
+void CFX_AggDeviceDriver::DestroyPlatform() {
+  CQuartz2D& quartz2d =
+      static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatform())
+          ->m_quartz2d;
+  if (m_pPlatformGraphics) {
+    quartz2d.DestroyGraphics(m_pPlatformGraphics);
+    m_pPlatformGraphics = nullptr;
+  }
+}
+
+bool CFX_AggDeviceDriver::DrawDeviceText(
+    pdfium::span<const TextCharPos> pCharPos,
+    CFX_Font* pFont,
+    const CFX_Matrix& mtObject2Device,
+    float font_size,
+    uint32_t argb,
+    const CFX_TextRenderOptions& /*options*/) {
+  if (!pFont)
+    return false;
+
+  bool bBold = pFont->IsBold();
+  if (!bBold && pFont->GetSubstFont() &&
+      pFont->GetSubstFont()->m_Weight >= 500 &&
+      pFont->GetSubstFont()->m_Weight <= 600) {
+    return false;
+  }
+  for (const auto& cp : pCharPos) {
+    if (cp.m_bGlyphAdjust)
+      return false;
+  }
+  CGContextRef ctx = CGContextRef(m_pPlatformGraphics);
+  if (!ctx)
+    return false;
+
+  CGContextSaveGState(ctx);
+  CGContextSetTextDrawingMode(ctx, kCGTextFillClip);
+  CGRect rect_cg;
+  CGImageRef pImageCG = nullptr;
+  if (m_pClipRgn) {
+    rect_cg =
+        CGRectMake(m_pClipRgn->GetBox().left, m_pClipRgn->GetBox().top,
+                   m_pClipRgn->GetBox().Width(), m_pClipRgn->GetBox().Height());
+    RetainPtr<CFX_DIBitmap> pClipMask = m_pClipRgn->GetMask();
+    if (pClipMask) {
+      CGDataProviderRef pClipMaskDataProvider = CGDataProviderCreateWithData(
+          nullptr, pClipMask->GetBuffer().data(),
+          pClipMask->GetPitch() * pClipMask->GetHeight(), DoNothing);
+      CGFloat decode_f[2] = {255.f, 0.f};
+      pImageCG = CGImageMaskCreate(
+          pClipMask->GetWidth(), pClipMask->GetHeight(), 8, 8,
+          pClipMask->GetPitch(), pClipMaskDataProvider, decode_f, false);
+      CGDataProviderRelease(pClipMaskDataProvider);
+    }
+  } else {
+    rect_cg = CGRectMake(0, 0, m_pBitmap->GetWidth(), m_pBitmap->GetHeight());
+  }
+  rect_cg = CGContextConvertRectToDeviceSpace(ctx, rect_cg);
+  if (pImageCG)
+    CGContextClipToMask(ctx, rect_cg, pImageCG);
+  else
+    CGContextClipToRect(ctx, rect_cg);
+
+  bool ret =
+      CGDrawGlyphRun(ctx, pCharPos, pFont, mtObject2Device, font_size, argb);
+  if (pImageCG)
+    CGImageRelease(pImageCG);
+  CGContextRestoreGState(ctx);
+  return ret;
+}
+
+}  // namespace pdfium
+
+std::unique_ptr<CFX_GlyphBitmap> CFX_GlyphCache::RenderGlyph_Nativetext(
+    const CFX_Font* pFont,
+    uint32_t glyph_index,
+    const CFX_Matrix& matrix,
+    int dest_width,
+    int anti_alias) {
+  return nullptr;
+}
+
+void CFX_Font::ReleasePlatformResource() {
+  if (m_pPlatformFont) {
+    CQuartz2D& quartz2d =
+        static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatform())
+            ->m_quartz2d;
+    quartz2d.DestroyFont(m_pPlatformFont);
+    m_pPlatformFont = nullptr;
+  }
+}
diff --git a/core/fxge/apple/fx_apple_platform.cpp b/core/fxge/apple/fx_apple_platform.cpp
index 96adc88..492adc4 100644
--- a/core/fxge/apple/fx_apple_platform.cpp
+++ b/core/fxge/apple/fx_apple_platform.cpp
@@ -1,187 +1,164 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
+#include "core/fxge/apple/fx_apple_platform.h"
+
 #include <memory>
-#include <vector>
+#include <utility>
 
-#include "core/fxcrt/fx_system.h"
-
-#include "core/fxge/apple/apple_int.h"
-#include "core/fxge/cfx_cliprgn.h"
-#include "core/fxge/cfx_font.h"
-#include "core/fxge/cfx_gemodule.h"
-#include "core/fxge/cfx_glyphbitmap.h"
-#include "core/fxge/cfx_glyphcache.h"
-#include "core/fxge/cfx_renderdevice.h"
-#include "core/fxge/cfx_substfont.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_freetype.h"
-#include "core/fxge/text_char_pos.h"
-#include "third_party/base/span.h"
-
-#ifndef _SKIA_SUPPORT_
-#include "core/fxge/agg/fx_agg_driver.h"
-#endif
-
-#ifndef _SKIA_SUPPORT_
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxge/cfx_folderfontinfo.h"
+#include "core/fxge/cfx_fontmgr.h"
+#include "core/fxge/fx_font.h"
+#include "core/fxge/systemfontinfo_iface.h"
 
 namespace {
 
-void DoNothing(void* info, const void* data, size_t size) {}
+struct Substs {
+  const char* m_pName;
+  const char* m_pSubstName;
+};
 
-bool CGDrawGlyphRun(CGContextRef pContext,
-                    int nChars,
-                    const TextCharPos* pCharPos,
-                    CFX_Font* pFont,
-                    const CFX_Matrix& mtObject2Device,
-                    float font_size,
-                    uint32_t argb) {
-  if (nChars == 0)
-    return true;
+constexpr Substs kBase14Substs[] = {
+    {"Courier", "Courier New"},
+    {"Courier-Bold", "Courier New Bold"},
+    {"Courier-BoldOblique", "Courier New Bold Italic"},
+    {"Courier-Oblique", "Courier New Italic"},
+    {"Helvetica", "Arial"},
+    {"Helvetica-Bold", "Arial Bold"},
+    {"Helvetica-BoldOblique", "Arial Bold Italic"},
+    {"Helvetica-Oblique", "Arial Italic"},
+    {"Times-Roman", "Times New Roman"},
+    {"Times-Bold", "Times New Roman Bold"},
+    {"Times-BoldItalic", "Times New Roman Bold Italic"},
+    {"Times-Italic", "Times New Roman Italic"},
+};
 
-  bool bNegSize = font_size < 0;
-  if (bNegSize)
-    font_size = -font_size;
+class CFX_MacFontInfo final : public CFX_FolderFontInfo {
+ public:
+  CFX_MacFontInfo() = default;
+  ~CFX_MacFontInfo() override = default;
 
-  CFX_Matrix new_matrix = mtObject2Device;
-  CQuartz2D& quartz2d =
-      static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatform())
-          ->m_quartz2d;
-  if (!pFont->GetPlatformFont()) {
-    if (pFont->GetPsName() == "DFHeiStd-W5")
-      return false;
+  // CFX_FolderFontInfo
+  void* MapFont(int weight,
+                bool bItalic,
+                FX_Charset charset,
+                int pitch_family,
+                const ByteString& face) override;
 
-    pdfium::span<const uint8_t> span = pFont->GetFontSpan();
-    pFont->SetPlatformFont(quartz2d.CreateFont(span.data(), span.size()));
-    if (!pFont->GetPlatformFont())
-      return false;
-  }
-  std::vector<uint16_t> glyph_indices(nChars);
-  std::vector<CGPoint> glyph_positions(nChars);
-  for (int i = 0; i < nChars; i++) {
-    glyph_indices[i] =
-        pCharPos[i].m_ExtGID ? pCharPos[i].m_ExtGID : pCharPos[i].m_GlyphIndex;
-    if (bNegSize)
-      glyph_positions[i].x = -pCharPos[i].m_Origin.x;
-    else
-      glyph_positions[i].x = pCharPos[i].m_Origin.x;
-    glyph_positions[i].y = pCharPos[i].m_Origin.y;
-  }
-  if (bNegSize) {
-    new_matrix.a = -new_matrix.a;
-    new_matrix.c = -new_matrix.c;
-  } else {
-    new_matrix.b = -new_matrix.b;
-    new_matrix.d = -new_matrix.d;
-  }
-  quartz2d.SetGraphicsTextMatrix(pContext, new_matrix);
-  return quartz2d.DrawGraphicsString(pContext, pFont->GetPlatformFont(),
-                                     font_size, glyph_indices.data(),
-                                     glyph_positions.data(), nChars, argb);
+  bool ParseFontCfg(const char** pUserPaths);
+};
+
+constexpr char kJapanGothic[] = "Hiragino Kaku Gothic Pro W6";
+constexpr char kJapanMincho[] = "Hiragino Mincho Pro W6";
+
+ByteString GetJapanesePreference(const ByteString& face,
+                                 int weight,
+                                 int pitch_family) {
+  if (face.Contains("Gothic"))
+    return kJapanGothic;
+  if (FontFamilyIsRoman(pitch_family) || weight <= 400)
+    return kJapanMincho;
+  return kJapanGothic;
 }
 
+void* CFX_MacFontInfo::MapFont(int weight,
+                               bool bItalic,
+                               FX_Charset charset,
+                               int pitch_family,
+                               const ByteString& face) {
+  for (const auto& sub : kBase14Substs) {
+    if (face == ByteStringView(sub.m_pName))
+      return GetFont(sub.m_pSubstName);
+  }
+
+  // The request may not ask for the bold and/or italic version of a font by
+  // name. So try to construct the appropriate name. This is not 100% foolproof
+  // as there are fonts that have "Oblique" or "BoldOblique" or "Heavy" in their
+  // names instead. But this at least works for common fonts like Arial and
+  // Times New Roman. A more sophisticated approach would be to find all the
+  // fonts in |m_FontList| with |face| in the name, and examine the fonts to
+  // see which best matches the requested characteristics.
+  if (!face.Contains("Bold") && !face.Contains("Italic")) {
+    ByteString new_face = face;
+    if (weight > 400)
+      new_face += " Bold";
+    if (bItalic)
+      new_face += " Italic";
+    auto it = m_FontList.find(new_face);
+    if (it != m_FontList.end())
+      return it->second.get();
+  }
+
+  auto it = m_FontList.find(face);
+  if (it != m_FontList.end())
+    return it->second.get();
+
+  if (charset == FX_Charset::kANSI && FontFamilyIsFixedPitch(pitch_family))
+    return GetFont("Courier New");
+
+  if (charset == FX_Charset::kANSI || charset == FX_Charset::kSymbol)
+    return nullptr;
+
+  ByteString other_face;
+  switch (charset) {
+    case FX_Charset::kShiftJIS:
+      other_face = GetJapanesePreference(face, weight, pitch_family);
+      break;
+    case FX_Charset::kChineseSimplified:
+      other_face = "STSong";
+      break;
+    case FX_Charset::kHangul:
+      other_face = "AppleMyungjo";
+      break;
+    case FX_Charset::kChineseTraditional:
+      other_face = "LiSong Pro Light";
+      break;
+    default:
+      other_face = face;
+      break;
+  }
+  it = m_FontList.find(other_face);
+  return it != m_FontList.end() ? it->second.get() : nullptr;
+}
+
+bool CFX_MacFontInfo::ParseFontCfg(const char** pUserPaths) {
+  if (!pUserPaths)
+    return false;
+
+  for (const char** pPath = pUserPaths; *pPath; ++pPath)
+    AddPath(*pPath);
+  return true;
+}
 }  // namespace
 
-void CFX_AggDeviceDriver::InitPlatform() {
-  CQuartz2D& quartz2d =
-      static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatform())
-          ->m_quartz2d;
-  m_pPlatformGraphics = quartz2d.CreateGraphics(m_pBitmap);
+CApplePlatform::CApplePlatform() = default;
+
+CApplePlatform::~CApplePlatform() = default;
+
+void CApplePlatform::Init() {}
+
+std::unique_ptr<SystemFontInfoIface>
+CApplePlatform::CreateDefaultSystemFontInfo() {
+  auto pInfo = std::make_unique<CFX_MacFontInfo>();
+  if (!pInfo->ParseFontCfg(CFX_GEModule::Get()->GetUserFontPaths())) {
+    pInfo->AddPath("~/Library/Fonts");
+    pInfo->AddPath("/Library/Fonts");
+    pInfo->AddPath("/System/Library/Fonts");
+  }
+  return std::move(pInfo);
 }
 
-void CFX_AggDeviceDriver::DestroyPlatform() {
-  CQuartz2D& quartz2d =
-      static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatform())
-          ->m_quartz2d;
-  if (m_pPlatformGraphics) {
-    quartz2d.DestroyGraphics(m_pPlatformGraphics);
-    m_pPlatformGraphics = nullptr;
-  }
+void* CApplePlatform::CreatePlatformFont(
+    pdfium::span<const uint8_t> font_span) {
+  return m_quartz2d.CreateFont(font_span);
 }
 
-bool CFX_AggDeviceDriver::DrawDeviceText(int nChars,
-                                         const TextCharPos* pCharPos,
-                                         CFX_Font* pFont,
-                                         const CFX_Matrix& mtObject2Device,
-                                         float font_size,
-                                         uint32_t argb) {
-  if (!pFont)
-    return false;
-
-  bool bBold = pFont->IsBold();
-  if (!bBold && pFont->GetSubstFont() &&
-      pFont->GetSubstFont()->m_Weight >= 500 &&
-      pFont->GetSubstFont()->m_Weight <= 600) {
-    return false;
-  }
-  for (int i = 0; i < nChars; i++) {
-    if (pCharPos[i].m_bGlyphAdjust)
-      return false;
-  }
-  CGContextRef ctx = CGContextRef(m_pPlatformGraphics);
-  if (!ctx)
-    return false;
-
-  CGContextSaveGState(ctx);
-  CGContextSetTextDrawingMode(ctx, kCGTextFillClip);
-  CGRect rect_cg;
-  CGImageRef pImageCG = nullptr;
-  if (m_pClipRgn) {
-    rect_cg =
-        CGRectMake(m_pClipRgn->GetBox().left, m_pClipRgn->GetBox().top,
-                   m_pClipRgn->GetBox().Width(), m_pClipRgn->GetBox().Height());
-    RetainPtr<CFX_DIBitmap> pClipMask = m_pClipRgn->GetMask();
-    if (pClipMask) {
-      CGDataProviderRef pClipMaskDataProvider = CGDataProviderCreateWithData(
-          nullptr, pClipMask->GetBuffer(),
-          pClipMask->GetPitch() * pClipMask->GetHeight(), DoNothing);
-      CGFloat decode_f[2] = {255.f, 0.f};
-      pImageCG = CGImageMaskCreate(
-          pClipMask->GetWidth(), pClipMask->GetHeight(), 8, 8,
-          pClipMask->GetPitch(), pClipMaskDataProvider, decode_f, false);
-      CGDataProviderRelease(pClipMaskDataProvider);
-    }
-  } else {
-    rect_cg = CGRectMake(0, 0, m_pBitmap->GetWidth(), m_pBitmap->GetHeight());
-  }
-  rect_cg = CGContextConvertRectToDeviceSpace(ctx, rect_cg);
-  if (pImageCG)
-    CGContextClipToMask(ctx, rect_cg, pImageCG);
-  else
-    CGContextClipToRect(ctx, rect_cg);
-
-  bool ret = CGDrawGlyphRun(ctx, nChars, pCharPos, pFont, mtObject2Device,
-                            font_size, argb);
-  if (pImageCG)
-    CGImageRelease(pImageCG);
-  CGContextRestoreGState(ctx);
-  return ret;
-}
-
-#endif  // _SKIA_SUPPORT_
-
-void CFX_GlyphCache::InitPlatform() {}
-
-void CFX_GlyphCache::DestroyPlatform() {}
-
-std::unique_ptr<CFX_GlyphBitmap> CFX_GlyphCache::RenderGlyph_Nativetext(
-    const CFX_Font* pFont,
-    uint32_t glyph_index,
-    const CFX_Matrix& matrix,
-    uint32_t dest_width,
-    int anti_alias) {
-  return nullptr;
-}
-
-void CFX_Font::ReleasePlatformResource() {
-  if (m_pPlatformFont) {
-    CQuartz2D& quartz2d =
-        static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatform())
-            ->m_quartz2d;
-    quartz2d.DestroyFont(m_pPlatformFont);
-    m_pPlatformFont = nullptr;
-  }
+// static
+std::unique_ptr<CFX_GEModule::PlatformIface>
+CFX_GEModule::PlatformIface::Create() {
+  return std::make_unique<CApplePlatform>();
 }
diff --git a/core/fxge/apple/fx_apple_platform.h b/core/fxge/apple/fx_apple_platform.h
new file mode 100644
index 0000000..fef27e6
--- /dev/null
+++ b/core/fxge/apple/fx_apple_platform.h
@@ -0,0 +1,28 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_APPLE_FX_APPLE_PLATFORM_H_
+#define CORE_FXGE_APPLE_FX_APPLE_PLATFORM_H_
+
+#include <memory>
+
+#include "core/fxge/apple/fx_quartz_device.h"
+#include "core/fxge/cfx_gemodule.h"
+
+class CApplePlatform final : public CFX_GEModule::PlatformIface {
+ public:
+  CApplePlatform();
+  ~CApplePlatform() override;
+
+  // CFX_GEModule::PlatformIface:
+  void Init() override;
+  std::unique_ptr<SystemFontInfoIface> CreateDefaultSystemFontInfo() override;
+  void* CreatePlatformFont(pdfium::span<const uint8_t> font_span) override;
+
+  CQuartz2D m_quartz2d;
+};
+
+#endif  // CORE_FXGE_APPLE_FX_APPLE_PLATFORM_H_
diff --git a/core/fxge/apple/fx_mac_imp.cpp b/core/fxge/apple/fx_mac_imp.cpp
deleted file mode 100644
index 64e50cc..0000000
--- a/core/fxge/apple/fx_mac_imp.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include <memory>
-#include <utility>
-
-#include "core/fxcrt/fx_codepage.h"
-#include "core/fxge/apple/apple_int.h"
-#include "core/fxge/cfx_folderfontinfo.h"
-#include "core/fxge/cfx_fontmgr.h"
-#include "core/fxge/cfx_gemodule.h"
-#include "core/fxge/fx_font.h"
-#include "core/fxge/systemfontinfo_iface.h"
-#include "third_party/base/ptr_util.h"
-
-namespace {
-
-const struct {
-  const char* m_pName;
-  const char* m_pSubstName;
-} g_Base14Substs[] = {
-    {"Courier", "Courier New"},
-    {"Courier-Bold", "Courier New Bold"},
-    {"Courier-BoldOblique", "Courier New Bold Italic"},
-    {"Courier-Oblique", "Courier New Italic"},
-    {"Helvetica", "Arial"},
-    {"Helvetica-Bold", "Arial Bold"},
-    {"Helvetica-BoldOblique", "Arial Bold Italic"},
-    {"Helvetica-Oblique", "Arial Italic"},
-    {"Times-Roman", "Times New Roman"},
-    {"Times-Bold", "Times New Roman Bold"},
-    {"Times-BoldItalic", "Times New Roman Bold Italic"},
-    {"Times-Italic", "Times New Roman Italic"},
-};
-
-class CFX_MacFontInfo final : public CFX_FolderFontInfo {
- public:
-  CFX_MacFontInfo() {}
-  ~CFX_MacFontInfo() override {}
-
-  // CFX_FolderFontInfo
-  void* MapFont(int weight,
-                bool bItalic,
-                int charset,
-                int pitch_family,
-                const char* family) override;
-
-  bool ParseFontCfg(const char** pUserPaths);
-};
-
-const char JAPAN_GOTHIC[] = "Hiragino Kaku Gothic Pro W6";
-const char JAPAN_MINCHO[] = "Hiragino Mincho Pro W6";
-
-void GetJapanesePreference(ByteString* face, int weight, int pitch_family) {
-  if (face->Contains("Gothic")) {
-    *face = JAPAN_GOTHIC;
-    return;
-  }
-  *face = (FontFamilyIsRoman(pitch_family) || weight <= 400) ? JAPAN_MINCHO
-                                                             : JAPAN_GOTHIC;
-}
-
-void* CFX_MacFontInfo::MapFont(int weight,
-                               bool bItalic,
-                               int charset,
-                               int pitch_family,
-                               const char* cstr_face) {
-  ByteString face = cstr_face;
-  for (const auto& sub : g_Base14Substs) {
-    if (face == ByteStringView(sub.m_pName)) {
-      face = sub.m_pSubstName;
-      return GetFont(face.c_str());
-    }
-  }
-
-  // The request may not ask for the bold and/or italic version of a font by
-  // name. So try to construct the appropriate name. This is not 100% foolproof
-  // as there are fonts that have "Oblique" or "BoldOblique" or "Heavy" in their
-  // names instead. But this at least works for common fonts like Arial and
-  // Times New Roman. A more sophisticated approach would be to find all the
-  // fonts in |m_FontList| with |face| in the name, and examine the fonts to
-  // see which best matches the requested characteristics.
-  if (!face.Contains("Bold") && !face.Contains("Italic")) {
-    ByteString new_face = face;
-    if (weight > 400)
-      new_face += " Bold";
-    if (bItalic)
-      new_face += " Italic";
-    auto it = m_FontList.find(new_face);
-    if (it != m_FontList.end())
-      return it->second.get();
-  }
-
-  auto it = m_FontList.find(face);
-  if (it != m_FontList.end())
-    return it->second.get();
-
-  if (charset == FX_CHARSET_ANSI && FontFamilyIsFixedPitch(pitch_family))
-    return GetFont("Courier New");
-
-  if (charset == FX_CHARSET_ANSI || charset == FX_CHARSET_Symbol)
-    return nullptr;
-
-  switch (charset) {
-    case FX_CHARSET_ShiftJIS:
-      GetJapanesePreference(&face, weight, pitch_family);
-      break;
-    case FX_CHARSET_ChineseSimplified:
-      face = "STSong";
-      break;
-    case FX_CHARSET_Hangul:
-      face = "AppleMyungjo";
-      break;
-    case FX_CHARSET_ChineseTraditional:
-      face = "LiSong Pro Light";
-  }
-  it = m_FontList.find(face);
-  return it != m_FontList.end() ? it->second.get() : nullptr;
-}
-
-bool CFX_MacFontInfo::ParseFontCfg(const char** pUserPaths) {
-  if (!pUserPaths)
-    return false;
-
-  for (const char** pPath = pUserPaths; *pPath; ++pPath)
-    AddPath(*pPath);
-  return true;
-}
-}  // namespace
-
-std::unique_ptr<SystemFontInfoIface> SystemFontInfoIface::CreateDefault(
-    const char** pUserPaths) {
-  auto pInfo = pdfium::MakeUnique<CFX_MacFontInfo>();
-  if (!pInfo->ParseFontCfg(pUserPaths)) {
-    pInfo->AddPath("~/Library/Fonts");
-    pInfo->AddPath("/Library/Fonts");
-    pInfo->AddPath("/System/Library/Fonts");
-  }
-  return std::move(pInfo);
-}
-
-CApplePlatform::CApplePlatform() = default;
-
-CApplePlatform::~CApplePlatform() = default;
-
-void CApplePlatform::Init() {
-  CFX_GEModule* pModule = CFX_GEModule::Get();
-  pModule->GetFontMgr()->SetSystemFontInfo(
-      SystemFontInfoIface::CreateDefault(pModule->GetUserFontPaths()));
-}
-
-// static
-std::unique_ptr<CFX_GEModule::PlatformIface>
-CFX_GEModule::PlatformIface::Create() {
-  return pdfium::MakeUnique<CApplePlatform>();
-}
diff --git a/core/fxge/apple/fx_quartz_device.cpp b/core/fxge/apple/fx_quartz_device.cpp
index 2281ba5..24e0c9c 100644
--- a/core/fxge/apple/fx_quartz_device.cpp
+++ b/core/fxge/apple/fx_quartz_device.cpp
@@ -1,22 +1,20 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
+#include "core/fxge/apple/fx_quartz_device.h"
+
+#include <CoreGraphics/CoreGraphics.h>
+
 #include "core/fxcrt/fx_extension.h"
-
-#if !defined _SKIA_SUPPORT_ && !defined _SKIA_SUPPORT_PATHS_
-#include "core/fxge/agg/fx_agg_driver.h"
-#endif
-
 #include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_freetype.h"
+#include "core/fxge/freetype/fx_freetype.h"
 
-#include "core/fxge/apple/apple_int.h"
 #ifndef CGFLOAT_IS_DOUBLE
 #error Expected CGFLOAT_IS_DOUBLE to be defined by CoreGraphics headers
 #endif
@@ -26,17 +24,17 @@
     return nullptr;
   CGBitmapInfo bmpInfo = kCGBitmapByteOrder32Little;
   switch (pBitmap->GetFormat()) {
-    case FXDIB_Rgb32:
+    case FXDIB_Format::kRgb32:
       bmpInfo |= kCGImageAlphaNoneSkipFirst;
       break;
-    case FXDIB_Argb:
+    case FXDIB_Format::kArgb:
     default:
       return nullptr;
   }
   CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
   CGContextRef context = CGBitmapContextCreate(
-      pBitmap->GetBuffer(), pBitmap->GetWidth(), pBitmap->GetHeight(), 8,
-      pBitmap->GetPitch(), colorSpace, bmpInfo);
+      pBitmap->GetWritableBuffer().data(), pBitmap->GetWidth(),
+      pBitmap->GetHeight(), 8, pBitmap->GetPitch(), colorSpace, bmpInfo);
   CGColorSpaceRelease(colorSpace);
   return context;
 }
@@ -46,9 +44,9 @@
     CGContextRelease((CGContextRef)graphics);
 }
 
-void* CQuartz2D::CreateFont(const uint8_t* pFontData, uint32_t dwFontSize) {
+void* CQuartz2D::CreateFont(pdfium::span<const uint8_t> pFontData) {
   CGDataProviderRef pDataProvider = CGDataProviderCreateWithData(
-      nullptr, pFontData, static_cast<size_t>(dwFontSize), nullptr);
+      nullptr, pFontData.data(), pFontData.size(), nullptr);
   if (!pDataProvider)
     return nullptr;
 
@@ -75,9 +73,8 @@
 bool CQuartz2D::DrawGraphicsString(void* graphics,
                                    void* font,
                                    float fontSize,
-                                   uint16_t* glyphIndices,
-                                   CGPoint* glyphPositions,
-                                   int32_t charsCount,
+                                   pdfium::span<uint16_t> glyphIndices,
+                                   pdfium::span<CGPoint> glyphPositions,
                                    FX_ARGB argb) {
   if (!graphics)
     return false;
@@ -94,17 +91,17 @@
   CGContextSetRGBFillColor(context, r / 255.f, g / 255.f, b / 255.f, a / 255.f);
   CGContextSaveGState(context);
 #if CGFLOAT_IS_DOUBLE
-  CGPoint* glyphPositionsCG = new CGPoint[charsCount];
-  for (int index = 0; index < charsCount; ++index) {
+  CGPoint* glyphPositionsCG = new CGPoint[glyphPositions.size()];
+  for (size_t index = 0; index < glyphPositions.size(); ++index) {
     glyphPositionsCG[index].x = glyphPositions[index].x;
     glyphPositionsCG[index].y = glyphPositions[index].y;
   }
 #else
-  CGPoint* glyphPositionsCG = glyphPositions;
+  CGPoint* glyphPositionsCG = glyphPositions.data();
 #endif
-  CGContextShowGlyphsAtPositions(context,
-                                 reinterpret_cast<CGGlyph*>(glyphIndices),
-                                 glyphPositionsCG, charsCount);
+  CGContextShowGlyphsAtPositions(
+      context, reinterpret_cast<CGGlyph*>(glyphIndices.data()),
+      glyphPositionsCG, glyphPositions.size());
 #if CGFLOAT_IS_DOUBLE
   delete[] glyphPositionsCG;
 #endif
diff --git a/core/fxge/apple/fx_quartz_device.h b/core/fxge/apple/fx_quartz_device.h
new file mode 100644
index 0000000..97080e3
--- /dev/null
+++ b/core/fxge/apple/fx_quartz_device.h
@@ -0,0 +1,36 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_APPLE_FX_QUARTZ_DEVICE_H_
+#define CORE_FXGE_APPLE_FX_QUARTZ_DEVICE_H_
+
+#include <CoreGraphics/CoreGraphics.h>
+#include <stdint.h>
+
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/containers/span.h"
+
+class CFX_DIBitmap;
+class CFX_Matrix;
+
+class CQuartz2D {
+ public:
+  void* CreateGraphics(const RetainPtr<CFX_DIBitmap>& bitmap);
+  void DestroyGraphics(void* graphics);
+
+  void* CreateFont(pdfium::span<const uint8_t> pFontData);
+  void DestroyFont(void* pFont);
+  void SetGraphicsTextMatrix(void* graphics, const CFX_Matrix& matrix);
+  bool DrawGraphicsString(void* graphics,
+                          void* font,
+                          float fontSize,
+                          pdfium::span<uint16_t> glyphIndices,
+                          pdfium::span<CGPoint> glyphPositions,
+                          FX_ARGB argb);
+};
+
+#endif  // CORE_FXGE_APPLE_FX_QUARTZ_DEVICE_H_
diff --git a/core/fxge/calculate_pitch.cpp b/core/fxge/calculate_pitch.cpp
new file mode 100644
index 0000000..799e10e
--- /dev/null
+++ b/core/fxge/calculate_pitch.cpp
@@ -0,0 +1,59 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/calculate_pitch.h"
+
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxge/dib/fx_dib.h"
+
+namespace fxge {
+namespace {
+
+FX_SAFE_UINT32 CalculatePitch8Safely(uint32_t bpc,
+                                     uint32_t components,
+                                     int width) {
+  FX_SAFE_UINT32 pitch = bpc;
+  pitch *= components;
+  pitch *= width;
+  pitch += 7;
+  pitch /= 8;
+  return pitch;
+}
+
+FX_SAFE_UINT32 CalculatePitch32Safely(int bpp, int width) {
+  FX_SAFE_UINT32 pitch = bpp;
+  pitch *= width;
+  pitch += 31;
+  pitch /= 32;  // quantized to number of 32-bit words.
+  pitch *= 4;   // and then back to bytes, (not just /8 in one step).
+  return pitch;
+}
+
+}  // namespace
+
+uint32_t CalculatePitch8OrDie(uint32_t bpc, uint32_t components, int width) {
+  return CalculatePitch8Safely(bpc, components, width).ValueOrDie();
+}
+
+uint32_t CalculatePitch32OrDie(int bpp, int width) {
+  return CalculatePitch32Safely(bpp, width).ValueOrDie();
+}
+
+absl::optional<uint32_t> CalculatePitch8(uint32_t bpc,
+                                         uint32_t components,
+                                         int width) {
+  FX_SAFE_UINT32 pitch = CalculatePitch8Safely(bpc, components, width);
+  if (!pitch.IsValid())
+    return absl::nullopt;
+  return pitch.ValueOrDie();
+}
+
+absl::optional<uint32_t> CalculatePitch32(int bpp, int width) {
+  FX_SAFE_UINT32 pitch = CalculatePitch32Safely(bpp, width);
+  if (!pitch.IsValid())
+    return absl::nullopt;
+  return pitch.ValueOrDie();
+}
+
+}  // namespace fxge
diff --git a/core/fxge/calculate_pitch.h b/core/fxge/calculate_pitch.h
new file mode 100644
index 0000000..1de7fdd
--- /dev/null
+++ b/core/fxge/calculate_pitch.h
@@ -0,0 +1,23 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXGE_CALCULATE_PITCH_H_
+#define CORE_FXGE_CALCULATE_PITCH_H_
+
+#include <stdint.h>
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace fxge {
+
+uint32_t CalculatePitch8OrDie(uint32_t bpc, uint32_t components, int width);
+uint32_t CalculatePitch32OrDie(int bpp, int width);
+absl::optional<uint32_t> CalculatePitch8(uint32_t bpc,
+                                         uint32_t components,
+                                         int width);
+absl::optional<uint32_t> CalculatePitch32(int bpp, int width);
+
+}  // namespace fxge
+
+#endif  // CORE_FXGE_CALCULATE_PITCH_H_
diff --git a/core/fxge/cfx_cliprgn.cpp b/core/fxge/cfx_cliprgn.cpp
index 8a3f946..33eb14b 100644
--- a/core/fxge/cfx_cliprgn.cpp
+++ b/core/fxge/cfx_cliprgn.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,91 +6,85 @@
 
 #include "core/fxge/cfx_cliprgn.h"
 
+#include <string.h>
+
 #include <utility>
 
+#include "core/fxcrt/span_util.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "third_party/base/logging.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/notreached.h"
 
-CFX_ClipRgn::CFX_ClipRgn(int width, int height)
-    : m_Type(RectI), m_Box(0, 0, width, height) {}
+CFX_ClipRgn::CFX_ClipRgn(int width, int height) : m_Box(0, 0, width, height) {}
 
 CFX_ClipRgn::CFX_ClipRgn(const CFX_ClipRgn& src) = default;
 
 CFX_ClipRgn::~CFX_ClipRgn() = default;
 
 void CFX_ClipRgn::IntersectRect(const FX_RECT& rect) {
-  if (m_Type == RectI) {
+  if (m_Type == kRectI) {
     m_Box.Intersect(rect);
     return;
   }
-  if (m_Type == MaskF) {
-    IntersectMaskRect(rect, m_Box, m_Mask);
-    return;
-  }
+  IntersectMaskRect(rect, m_Box, m_Mask);
 }
 
 void CFX_ClipRgn::IntersectMaskRect(FX_RECT rect,
                                     FX_RECT mask_rect,
-                                    const RetainPtr<CFX_DIBitmap>& pMask) {
-  m_Type = MaskF;
+                                    RetainPtr<CFX_DIBitmap> pOldMask) {
+  m_Type = kMaskF;
   m_Box = rect;
   m_Box.Intersect(mask_rect);
   if (m_Box.IsEmpty()) {
-    m_Type = RectI;
+    m_Type = kRectI;
     return;
   }
   if (m_Box == mask_rect) {
-    m_Mask = pMask;
+    m_Mask = std::move(pOldMask);
     return;
   }
-  RetainPtr<CFX_DIBitmap> pOldMask(pMask);
   m_Mask = pdfium::MakeRetain<CFX_DIBitmap>();
-  m_Mask->Create(m_Box.Width(), m_Box.Height(), FXDIB_8bppMask);
+  m_Mask->Create(m_Box.Width(), m_Box.Height(), FXDIB_Format::k8bppMask);
+  const int offset = m_Box.left - mask_rect.left;
   for (int row = m_Box.top; row < m_Box.bottom; row++) {
-    uint8_t* dest_scan =
-        m_Mask->GetBuffer() + m_Mask->GetPitch() * (row - m_Box.top);
-    uint8_t* src_scan =
-        pOldMask->GetBuffer() + pOldMask->GetPitch() * (row - mask_rect.top);
-    for (int col = m_Box.left; col < m_Box.right; col++)
-      dest_scan[col - m_Box.left] = src_scan[col - mask_rect.left];
+    pdfium::span<uint8_t> dest_scan =
+        m_Mask->GetWritableScanline(row - m_Box.top);
+    pdfium::span<const uint8_t> src_scan =
+        pOldMask->GetScanline(row - mask_rect.top);
+    fxcrt::spancpy(dest_scan, src_scan.subspan(offset, m_Box.Width()));
   }
 }
 
 void CFX_ClipRgn::IntersectMaskF(int left,
                                  int top,
-                                 const RetainPtr<CFX_DIBitmap>& pMask) {
-  ASSERT(pMask->GetFormat() == FXDIB_8bppMask);
+                                 RetainPtr<CFX_DIBitmap> pMask) {
+  DCHECK_EQ(pMask->GetFormat(), FXDIB_Format::k8bppMask);
   FX_RECT mask_box(left, top, left + pMask->GetWidth(),
                    top + pMask->GetHeight());
-  if (m_Type == RectI) {
-    IntersectMaskRect(m_Box, mask_box, pMask);
+  if (m_Type == kRectI) {
+    IntersectMaskRect(m_Box, mask_box, std::move(pMask));
     return;
   }
-  if (m_Type == MaskF) {
-    FX_RECT new_box = m_Box;
-    new_box.Intersect(mask_box);
-    if (new_box.IsEmpty()) {
-      m_Type = RectI;
-      m_Mask = nullptr;
-      m_Box = new_box;
-      return;
-    }
-    auto new_dib = pdfium::MakeRetain<CFX_DIBitmap>();
-    new_dib->Create(new_box.Width(), new_box.Height(), FXDIB_8bppMask);
-    for (int row = new_box.top; row < new_box.bottom; row++) {
-      uint8_t* old_scan =
-          m_Mask->GetBuffer() + (row - m_Box.top) * m_Mask->GetPitch();
-      uint8_t* mask_scan = pMask->GetBuffer() + (row - top) * pMask->GetPitch();
-      uint8_t* new_scan =
-          new_dib->GetBuffer() + (row - new_box.top) * new_dib->GetPitch();
-      for (int col = new_box.left; col < new_box.right; col++) {
-        new_scan[col - new_box.left] =
-            old_scan[col - m_Box.left] * mask_scan[col - left] / 255;
-      }
-    }
+
+  FX_RECT new_box = m_Box;
+  new_box.Intersect(mask_box);
+  if (new_box.IsEmpty()) {
+    m_Type = kRectI;
+    m_Mask = nullptr;
     m_Box = new_box;
-    m_Mask = std::move(new_dib);
     return;
   }
-  NOTREACHED();
+  auto new_dib = pdfium::MakeRetain<CFX_DIBitmap>();
+  new_dib->Create(new_box.Width(), new_box.Height(), FXDIB_Format::k8bppMask);
+  for (int row = new_box.top; row < new_box.bottom; row++) {
+    pdfium::span<const uint8_t> old_scan = m_Mask->GetScanline(row - m_Box.top);
+    pdfium::span<const uint8_t> mask_scan = pMask->GetScanline(row - top);
+    uint8_t* new_scan = new_dib->GetWritableScanline(row - new_box.top).data();
+    for (int col = new_box.left; col < new_box.right; col++) {
+      new_scan[col - new_box.left] =
+          old_scan[col - m_Box.left] * mask_scan[col - left] / 255;
+    }
+  }
+  m_Box = new_box;
+  m_Mask = std::move(new_dib);
 }
diff --git a/core/fxge/cfx_cliprgn.h b/core/fxge/cfx_cliprgn.h
index e1f9e22..2884ac7 100644
--- a/core/fxge/cfx_cliprgn.h
+++ b/core/fxge/cfx_cliprgn.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CFX_ClipRgn {
  public:
-  enum ClipType { RectI, MaskF };
+  enum ClipType : bool { kRectI, kMaskF };
 
   CFX_ClipRgn(int device_width, int device_height);
   CFX_ClipRgn(const CFX_ClipRgn& src);
@@ -25,14 +25,14 @@
   RetainPtr<CFX_DIBitmap> GetMask() const { return m_Mask; }
 
   void IntersectRect(const FX_RECT& rect);
-  void IntersectMaskF(int left, int top, const RetainPtr<CFX_DIBitmap>& Mask);
+  void IntersectMaskF(int left, int top, RetainPtr<CFX_DIBitmap> Mask);
 
  private:
   void IntersectMaskRect(FX_RECT rect,
                          FX_RECT mask_rect,
-                         const RetainPtr<CFX_DIBitmap>& Mask);
+                         RetainPtr<CFX_DIBitmap> pOldMask);
 
-  ClipType m_Type;
+  ClipType m_Type = kRectI;
   FX_RECT m_Box;
   RetainPtr<CFX_DIBitmap> m_Mask;
 };
diff --git a/core/fxge/cfx_color.cpp b/core/fxge/cfx_color.cpp
index 99330bc..35e03a5 100644
--- a/core/fxge/cfx_color.cpp
+++ b/core/fxge/cfx_color.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,13 +8,15 @@
 
 #include <algorithm>
 
-// Color types are orded by increasing number of components so we can
-// choose a best color type during some conversions.
-static_assert(CFX_Color::kTransparent < CFX_Color::kGray,
+#include "third_party/base/notreached.h"
+
+// Color types are ordered by increasing number of components so we can choose
+// a best color type during some conversions.
+static_assert(CFX_Color::Type::kTransparent < CFX_Color::Type::kGray,
               "color type values must be ordered");
-static_assert(CFX_Color::kGray < CFX_Color::kRGB,
+static_assert(CFX_Color::Type::kGray < CFX_Color::Type::kRGB,
               "color type values must be ordered");
-static_assert(CFX_Color::kRGB < CFX_Color::kCMYK,
+static_assert(CFX_Color::Type::kRGB < CFX_Color::Type::kCMYK,
               "color type values must be ordered");
 
 namespace {
@@ -25,88 +27,101 @@
 
 CFX_Color ConvertCMYK2GRAY(float dC, float dM, float dY, float dK) {
   if (!InRange(dC) || !InRange(dM) || !InRange(dY) || !InRange(dK))
-    return CFX_Color(CFX_Color::kGray);
+    return CFX_Color(CFX_Color::Type::kGray);
   return CFX_Color(
-      CFX_Color::kGray,
+      CFX_Color::Type::kGray,
       1.0f - std::min(1.0f, 0.3f * dC + 0.59f * dM + 0.11f * dY + dK));
 }
 
 CFX_Color ConvertGRAY2CMYK(float dGray) {
   if (!InRange(dGray))
-    return CFX_Color(CFX_Color::kCMYK);
-  return CFX_Color(CFX_Color::kCMYK, 0.0f, 0.0f, 0.0f, 1.0f - dGray);
+    return CFX_Color(CFX_Color::Type::kCMYK);
+  return CFX_Color(CFX_Color::Type::kCMYK, 0.0f, 0.0f, 0.0f, 1.0f - dGray);
 }
 
 CFX_Color ConvertGRAY2RGB(float dGray) {
   if (!InRange(dGray))
-    return CFX_Color(CFX_Color::kRGB);
-  return CFX_Color(CFX_Color::kRGB, dGray, dGray, dGray);
+    return CFX_Color(CFX_Color::Type::kRGB);
+  return CFX_Color(CFX_Color::Type::kRGB, dGray, dGray, dGray);
 }
 
 CFX_Color ConvertRGB2GRAY(float dR, float dG, float dB) {
   if (!InRange(dR) || !InRange(dG) || !InRange(dB))
-    return CFX_Color(CFX_Color::kGray);
-  return CFX_Color(CFX_Color::kGray, 0.3f * dR + 0.59f * dG + 0.11f * dB);
+    return CFX_Color(CFX_Color::Type::kGray);
+  return CFX_Color(CFX_Color::Type::kGray, 0.3f * dR + 0.59f * dG + 0.11f * dB);
 }
 
 CFX_Color ConvertCMYK2RGB(float dC, float dM, float dY, float dK) {
   if (!InRange(dC) || !InRange(dM) || !InRange(dY) || !InRange(dK))
-    return CFX_Color(CFX_Color::kRGB);
-  return CFX_Color(CFX_Color::kRGB, 1.0f - std::min(1.0f, dC + dK),
+    return CFX_Color(CFX_Color::Type::kRGB);
+  return CFX_Color(CFX_Color::Type::kRGB, 1.0f - std::min(1.0f, dC + dK),
                    1.0f - std::min(1.0f, dM + dK),
                    1.0f - std::min(1.0f, dY + dK));
 }
 
 CFX_Color ConvertRGB2CMYK(float dR, float dG, float dB) {
   if (!InRange(dR) || !InRange(dG) || !InRange(dB))
-    return CFX_Color(CFX_Color::kCMYK);
+    return CFX_Color(CFX_Color::Type::kCMYK);
 
   float c = 1.0f - dR;
   float m = 1.0f - dG;
   float y = 1.0f - dB;
-  return CFX_Color(CFX_Color::kCMYK, c, m, y, std::min(c, std::min(m, y)));
+  return CFX_Color(CFX_Color::Type::kCMYK, c, m, y,
+                   std::min(c, std::min(m, y)));
 }
 
 }  // namespace
 
-CFX_Color CFX_Color::ConvertColorType(int32_t nConvertColorType) const {
+CFX_Color CFX_Color::ConvertColorType(Type nConvertColorType) const {
   if (nColorType == nConvertColorType)
     return *this;
 
   CFX_Color ret;
   switch (nColorType) {
-    case CFX_Color::kTransparent:
+    case CFX_Color::Type::kTransparent:
       ret = *this;
-      ret.nColorType = CFX_Color::kTransparent;
+      ret.nColorType = CFX_Color::Type::kTransparent;
       break;
-    case CFX_Color::kGray:
+    case CFX_Color::Type::kGray:
       switch (nConvertColorType) {
-        case CFX_Color::kRGB:
+        case CFX_Color::Type::kTransparent:
+          break;
+        case CFX_Color::Type::kGray:
+          NOTREACHED_NORETURN();
+        case CFX_Color::Type::kRGB:
           ret = ConvertGRAY2RGB(fColor1);
           break;
-        case CFX_Color::kCMYK:
+        case CFX_Color::Type::kCMYK:
           ret = ConvertGRAY2CMYK(fColor1);
           break;
       }
       break;
-    case CFX_Color::kRGB:
+    case CFX_Color::Type::kRGB:
       switch (nConvertColorType) {
-        case CFX_Color::kGray:
+        case CFX_Color::Type::kTransparent:
+          break;
+        case CFX_Color::Type::kGray:
           ret = ConvertRGB2GRAY(fColor1, fColor2, fColor3);
           break;
-        case CFX_Color::kCMYK:
+        case CFX_Color::Type::kRGB:
+          NOTREACHED_NORETURN();
+        case CFX_Color::Type::kCMYK:
           ret = ConvertRGB2CMYK(fColor1, fColor2, fColor3);
           break;
       }
       break;
-    case CFX_Color::kCMYK:
+    case CFX_Color::Type::kCMYK:
       switch (nConvertColorType) {
-        case CFX_Color::kGray:
+        case CFX_Color::Type::kTransparent:
+          break;
+        case CFX_Color::Type::kGray:
           ret = ConvertCMYK2GRAY(fColor1, fColor2, fColor3, fColor4);
           break;
-        case CFX_Color::kRGB:
+        case CFX_Color::Type::kRGB:
           ret = ConvertCMYK2RGB(fColor1, fColor2, fColor3, fColor4);
           break;
+        case CFX_Color::Type::kCMYK:
+          NOTREACHED_NORETURN();
       }
       break;
   }
@@ -116,21 +131,21 @@
 FX_COLORREF CFX_Color::ToFXColor(int32_t nTransparency) const {
   CFX_Color ret;
   switch (nColorType) {
-    case CFX_Color::kTransparent: {
-      ret = CFX_Color(CFX_Color::kTransparent, 0, 0, 0, 0);
+    case CFX_Color::Type::kTransparent: {
+      ret = CFX_Color(CFX_Color::Type::kTransparent, 0, 0, 0, 0);
       break;
     }
-    case CFX_Color::kGray: {
+    case CFX_Color::Type::kGray: {
       ret = ConvertGRAY2RGB(fColor1);
       ret.fColor4 = nTransparency;
       break;
     }
-    case CFX_Color::kRGB: {
-      ret = CFX_Color(CFX_Color::kRGB, fColor1, fColor2, fColor3);
+    case CFX_Color::Type::kRGB: {
+      ret = CFX_Color(CFX_Color::Type::kRGB, fColor1, fColor2, fColor3);
       ret.fColor4 = nTransparency;
       break;
     }
-    case CFX_Color::kCMYK: {
+    case CFX_Color::Type::kCMYK: {
       ret = ConvertCMYK2RGB(fColor1, fColor2, fColor3, fColor4);
       ret.fColor4 = nTransparency;
       break;
@@ -144,15 +159,15 @@
 CFX_Color CFX_Color::operator-(float fColorSub) const {
   CFX_Color sRet(nColorType);
   switch (nColorType) {
-    case CFX_Color::kTransparent:
-      sRet.nColorType = CFX_Color::kRGB;
+    case CFX_Color::Type::kTransparent:
+      sRet.nColorType = CFX_Color::Type::kRGB;
       sRet.fColor1 = std::max(1.0f - fColorSub, 0.0f);
       sRet.fColor2 = std::max(1.0f - fColorSub, 0.0f);
       sRet.fColor3 = std::max(1.0f - fColorSub, 0.0f);
       break;
-    case CFX_Color::kRGB:
-    case CFX_Color::kGray:
-    case CFX_Color::kCMYK:
+    case CFX_Color::Type::kRGB:
+    case CFX_Color::Type::kGray:
+    case CFX_Color::Type::kCMYK:
       sRet.fColor1 = std::max(fColor1 - fColorSub, 0.0f);
       sRet.fColor2 = std::max(fColor2 - fColorSub, 0.0f);
       sRet.fColor3 = std::max(fColor3 - fColorSub, 0.0f);
@@ -165,15 +180,15 @@
 CFX_Color CFX_Color::operator/(float fColorDivide) const {
   CFX_Color sRet(nColorType);
   switch (nColorType) {
-    case CFX_Color::kTransparent:
-      sRet.nColorType = CFX_Color::kRGB;
+    case CFX_Color::Type::kTransparent:
+      sRet.nColorType = CFX_Color::Type::kRGB;
       sRet.fColor1 = 1.0f / fColorDivide;
       sRet.fColor2 = 1.0f / fColorDivide;
       sRet.fColor3 = 1.0f / fColorDivide;
       break;
-    case CFX_Color::kRGB:
-    case CFX_Color::kGray:
-    case CFX_Color::kCMYK:
+    case CFX_Color::Type::kRGB:
+    case CFX_Color::Type::kGray:
+    case CFX_Color::Type::kCMYK:
       sRet = *this;
       sRet.fColor1 /= fColorDivide;
       sRet.fColor2 /= fColorDivide;
diff --git a/core/fxge/cfx_color.h b/core/fxge/cfx_color.h
index fc941db..1719129 100644
--- a/core/fxge/cfx_color.h
+++ b/core/fxge/cfx_color.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,51 +7,51 @@
 #ifndef CORE_FXGE_CFX_COLOR_H_
 #define CORE_FXGE_CFX_COLOR_H_
 
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
 
 struct CFX_Color {
   // Ordered by increasing number of components.
-  enum Type { kTransparent = 0, kGray, kRGB, kCMYK };
+  enum class Type { kTransparent = 0, kGray, kRGB, kCMYK };
 
-  explicit CFX_Color(FX_COLORREF ref)
+  struct TypeAndARGB {
+    TypeAndARGB(CFX_Color::Type type_in, FX_ARGB argb_in)
+        : color_type(type_in), argb(argb_in) {}
+
+    CFX_Color::Type color_type;
+    FX_ARGB argb;
+  };
+
+  explicit constexpr CFX_Color(FX_COLORREF ref)
       : CFX_Color(FXARGB_R(ref), FXARGB_G(ref), FXARGB_B(ref)) {}
 
-  CFX_Color(int32_t type = CFX_Color::kTransparent,
-            float color1 = 0.0f,
-            float color2 = 0.0f,
-            float color3 = 0.0f,
-            float color4 = 0.0f)
+  constexpr CFX_Color(Type type = CFX_Color::Type::kTransparent,
+                      float color1 = 0.0f,
+                      float color2 = 0.0f,
+                      float color3 = 0.0f,
+                      float color4 = 0.0f)
       : nColorType(type),
         fColor1(color1),
         fColor2(color2),
         fColor3(color3),
         fColor4(color4) {}
 
-  CFX_Color(int32_t r, int32_t g, int32_t b)
-      : nColorType(CFX_Color::kRGB),
+  constexpr CFX_Color(int32_t r, int32_t g, int32_t b)
+      : nColorType(CFX_Color::Type::kRGB),
         fColor1(r / 255.0f),
         fColor2(g / 255.0f),
         fColor3(b / 255.0f),
         fColor4(0) {}
 
-  CFX_Color(const CFX_Color&) = default;
+  CFX_Color(const CFX_Color& that) = default;
+  CFX_Color& operator=(const CFX_Color& that) = default;
 
   CFX_Color operator/(float fColorDivide) const;
   CFX_Color operator-(float fColorSub) const;
 
-  CFX_Color ConvertColorType(int32_t nConvertColorType) const;
-
+  CFX_Color ConvertColorType(Type nConvertColorType) const;
   FX_COLORREF ToFXColor(int32_t nTransparency) const;
 
-  void Reset() {
-    nColorType = CFX_Color::kTransparent;
-    fColor1 = 0.0f;
-    fColor2 = 0.0f;
-    fColor3 = 0.0f;
-    fColor4 = 0.0f;
-  }
-
-  int32_t nColorType;
+  Type nColorType;
   float fColor1;
   float fColor2;
   float fColor3;
diff --git a/core/fxge/cfx_defaultrenderdevice.cpp b/core/fxge/cfx_defaultrenderdevice.cpp
new file mode 100644
index 0000000..819be4e
--- /dev/null
+++ b/core/fxge/cfx_defaultrenderdevice.cpp
@@ -0,0 +1,84 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/cfx_defaultrenderdevice.h"
+
+#include <utility>
+
+#include "core/fxge/dib/cfx_dibitmap.h"
+
+namespace {
+
+// When build variant is Skia then it is assumed as the default, but might be
+// overridden at runtime.
+#if defined(_SKIA_SUPPORT_)
+CFX_DefaultRenderDevice::RendererType g_default_renderer_type =
+    CFX_DefaultRenderDevice::RendererType::kSkia;
+#endif
+
+}  // namespace
+
+// static
+bool CFX_DefaultRenderDevice::SkiaIsDefaultRenderer() {
+#if defined(_SKIA_SUPPORT_)
+  return g_default_renderer_type == RendererType::kSkia;
+#else
+  return false;
+#endif
+}
+
+#if defined(_SKIA_SUPPORT_)
+// static
+void CFX_DefaultRenderDevice::SetDefaultRenderer(RendererType renderer_type) {
+  g_default_renderer_type = renderer_type;
+}
+#endif
+
+CFX_DefaultRenderDevice::CFX_DefaultRenderDevice() = default;
+
+CFX_DefaultRenderDevice::~CFX_DefaultRenderDevice() = default;
+
+bool CFX_DefaultRenderDevice::Attach(RetainPtr<CFX_DIBitmap> pBitmap) {
+  return AttachWithRgbByteOrder(std::move(pBitmap), false);
+}
+
+bool CFX_DefaultRenderDevice::AttachWithRgbByteOrder(
+    RetainPtr<CFX_DIBitmap> pBitmap,
+    bool bRgbByteOrder) {
+  return AttachImpl(std::move(pBitmap), bRgbByteOrder, nullptr, false);
+}
+
+bool CFX_DefaultRenderDevice::AttachWithBackdropAndGroupKnockout(
+    RetainPtr<CFX_DIBitmap> pBitmap,
+    RetainPtr<CFX_DIBitmap> pBackdropBitmap,
+    bool bGroupKnockout) {
+  return AttachImpl(std::move(pBitmap), false, std::move(pBackdropBitmap),
+                    bGroupKnockout);
+}
+
+bool CFX_DefaultRenderDevice::CFX_DefaultRenderDevice::AttachImpl(
+    RetainPtr<CFX_DIBitmap> pBitmap,
+    bool bRgbByteOrder,
+    RetainPtr<CFX_DIBitmap> pBackdropBitmap,
+    bool bGroupKnockout) {
+#if defined(_SKIA_SUPPORT_)
+  if (SkiaIsDefaultRenderer()) {
+    return AttachSkiaImpl(std::move(pBitmap), bRgbByteOrder,
+                          std::move(pBackdropBitmap), bGroupKnockout);
+  }
+#endif
+  return AttachAggImpl(std::move(pBitmap), bRgbByteOrder,
+                       std::move(pBackdropBitmap), bGroupKnockout);
+}
+
+bool CFX_DefaultRenderDevice::Create(int width,
+                                     int height,
+                                     FXDIB_Format format,
+                                     RetainPtr<CFX_DIBitmap> pBackdropBitmap) {
+#if defined(_SKIA_SUPPORT_)
+  if (SkiaIsDefaultRenderer())
+    return CreateSkia(width, height, format, pBackdropBitmap);
+#endif
+  return CreateAgg(width, height, format, pBackdropBitmap);
+}
diff --git a/core/fxge/cfx_defaultrenderdevice.h b/core/fxge/cfx_defaultrenderdevice.h
index d001775..4d94b7a 100644
--- a/core/fxge/cfx_defaultrenderdevice.h
+++ b/core/fxge/cfx_defaultrenderdevice.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,36 +7,79 @@
 #ifndef CORE_FXGE_CFX_DEFAULTRENDERDEVICE_H_
 #define CORE_FXGE_CFX_DEFAULTRENDERDEVICE_H_
 
-#include "core/fxge/cfx_renderdevice.h"
-#include "core/fxge/fx_dib.h"
+#include <memory>
 
-class SkPictureRecorder;
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/cfx_renderdevice.h"
+#include "core/fxge/dib/fx_dib.h"
+
+class SkCanvas;
 
 class CFX_DefaultRenderDevice final : public CFX_RenderDevice {
  public:
   CFX_DefaultRenderDevice();
   ~CFX_DefaultRenderDevice() override;
 
-  bool Attach(const RetainPtr<CFX_DIBitmap>& pBitmap,
-              bool bRgbByteOrder,
-              const RetainPtr<CFX_DIBitmap>& pBackdropBitmap,
-              bool bGroupKnockout);
+  bool Attach(RetainPtr<CFX_DIBitmap> pBitmap);
+  bool AttachWithRgbByteOrder(RetainPtr<CFX_DIBitmap> pBitmap,
+                              bool bRgbByteOrder);
+  bool AttachWithBackdropAndGroupKnockout(
+      RetainPtr<CFX_DIBitmap> pBitmap,
+      RetainPtr<CFX_DIBitmap> pBackdropBitmap,
+      bool bGroupKnockout);
   bool Create(int width,
               int height,
               FXDIB_Format format,
-              const RetainPtr<CFX_DIBitmap>& pBackdropBitmap);
+              RetainPtr<CFX_DIBitmap> pBackdropBitmap);
 
-#ifdef _SKIA_SUPPORT_
-  bool AttachRecorder(SkPictureRecorder* recorder);
+#if defined(_SKIA_SUPPORT_)
+  bool AttachCanvas(SkCanvas* canvas);
   void Clear(uint32_t color);
-  SkPictureRecorder* CreateRecorder(int size_x, int size_y);
-  void DebugVerifyBitmapIsPreMultiplied() const override;
-  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.
+  static bool SkiaIsDefaultRenderer();
+
+#if defined(_SKIA_SUPPORT_)
+  // This internal definition of renderer types must stay updated with respect
+  // to the public definition of `FPDF_RENDERER_TYPE`, so that all public
+  // definition values can be mapped to a value in
+  // `CFX_DefaultRenderDevice::RendererType`.
+  enum class RendererType {
+    kAgg = 0,
+    kSkia = 1,
+  };
+
+  // Update default renderer.
+  static void SetDefaultRenderer(RendererType renderer_type);
+#endif  // defined(_SKIA_SUPPORT_)
+
+ private:
+  bool AttachImpl(RetainPtr<CFX_DIBitmap> pBitmap,
+                  bool bRgbByteOrder,
+                  RetainPtr<CFX_DIBitmap> pBackdropBitmap,
+                  bool bGroupKnockout);
+
+  bool AttachAggImpl(RetainPtr<CFX_DIBitmap> pBitmap,
+                     bool bRgbByteOrder,
+                     RetainPtr<CFX_DIBitmap> pBackdropBitmap,
+                     bool bGroupKnockout);
+
+  bool CreateAgg(int width,
+                 int height,
+                 FXDIB_Format format,
+                 RetainPtr<CFX_DIBitmap> pBackdropBitmap);
+
+#if defined(_SKIA_SUPPORT_)
+  bool AttachSkiaImpl(RetainPtr<CFX_DIBitmap> pBitmap,
+                      bool bRgbByteOrder,
+                      RetainPtr<CFX_DIBitmap> pBackdropBitmap,
+                      bool bGroupKnockout);
+
+  bool CreateSkia(int width,
+                  int height,
+                  FXDIB_Format format,
+                  RetainPtr<CFX_DIBitmap> pBackdropBitmap);
 #endif
 };
 
diff --git a/core/fxge/cfx_defaultrenderdevice_unittest.cpp b/core/fxge/cfx_defaultrenderdevice_unittest.cpp
new file mode 100644
index 0000000..9304a8c
--- /dev/null
+++ b/core/fxge/cfx_defaultrenderdevice_unittest.cpp
@@ -0,0 +1,80 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/cfx_defaultrenderdevice.h"
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "core/fxge/cfx_graphstatedata.h"
+#include "core/fxge/cfx_path.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(CFX_DefaultRenderDeviceTest, GetClipBox_Default) {
+  CFX_DefaultRenderDevice device;
+  ASSERT_TRUE(device.Create(/*width=*/16, /*height=*/16, FXDIB_Format::kArgb,
+                            /*pBackdropBitmap=*/nullptr));
+
+  EXPECT_EQ(FX_RECT(0, 0, 16, 16), device.GetClipBox());
+}
+
+TEST(CFX_DefaultRenderDeviceTest, GetClipBox_PathFill) {
+  // Matrix that transposes and translates by 1 unit on each axis.
+  const CFX_Matrix object_to_device(0, 1, 1, 0, 1, -1);
+
+  // Fill type cannot be none.
+  const CFX_FillRenderOptions fill_options(
+      CFX_FillRenderOptions::FillType::kEvenOdd);
+
+  CFX_DefaultRenderDevice device;
+  ASSERT_TRUE(device.Create(/*width=*/16, /*height=*/16, FXDIB_Format::kArgb,
+                            /*pBackdropBitmap=*/nullptr));
+
+  CFX_Path path;
+  path.AppendRect(2, 4, 14, 12);
+  EXPECT_TRUE(device.SetClip_PathFill(path, &object_to_device, fill_options));
+
+  EXPECT_EQ(FX_RECT(5, 1, 13, 13), device.GetClipBox());
+}
+
+TEST(CFX_DefaultRenderDeviceTest, GetClipBox_PathStroke) {
+  // Matrix that transposes and translates by 1 unit on each axis.
+  const CFX_Matrix object_to_device(0, 1, 1, 0, 1, -1);
+
+  // Default line width is 1.
+  const CFX_GraphStateData graphics_state;
+
+  CFX_DefaultRenderDevice device;
+  ASSERT_TRUE(device.Create(/*width=*/16, /*height=*/16, FXDIB_Format::kArgb,
+                            /*pBackdropBitmap=*/nullptr));
+
+  CFX_Path path;
+  path.AppendRect(2, 4, 14, 12);
+  EXPECT_TRUE(
+      device.SetClip_PathStroke(path, &object_to_device, &graphics_state));
+
+  EXPECT_EQ(FX_RECT(4, 0, 14, 14), device.GetClipBox());
+}
+
+TEST(CFX_DefaultRenderDeviceTest, GetClipBox_Rect) {
+  CFX_DefaultRenderDevice device;
+  ASSERT_TRUE(device.Create(/*width=*/16, /*height=*/16, FXDIB_Format::kArgb,
+                            /*pBackdropBitmap=*/nullptr));
+
+  EXPECT_TRUE(device.SetClip_Rect({2, 4, 14, 12}));
+
+  EXPECT_EQ(FX_RECT(2, 4, 14, 12), device.GetClipBox());
+}
+
+TEST(CFX_DefaultRenderDeviceTest, GetClipBox_Empty) {
+  CFX_DefaultRenderDevice device;
+  ASSERT_TRUE(device.Create(/*width=*/16, /*height=*/16, FXDIB_Format::kArgb,
+                            /*pBackdropBitmap=*/nullptr));
+
+  EXPECT_TRUE(device.SetClip_Rect({2, 8, 14, 8}));
+
+  EXPECT_TRUE(device.GetClipBox().IsEmpty());
+}
diff --git a/core/fxge/cfx_drawutils.cpp b/core/fxge/cfx_drawutils.cpp
new file mode 100644
index 0000000..3591471
--- /dev/null
+++ b/core/fxge/cfx_drawutils.cpp
@@ -0,0 +1,41 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxge/cfx_drawutils.h"
+
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "core/fxge/cfx_graphstatedata.h"
+#include "core/fxge/cfx_path.h"
+#include "core/fxge/cfx_renderdevice.h"
+#include "third_party/base/check.h"
+
+// static
+void CFX_DrawUtils::DrawFocusRect(CFX_RenderDevice* render_device,
+                                  const CFX_Matrix& user_to_device,
+                                  const CFX_FloatRect& view_bounding_box) {
+  DCHECK(render_device);
+  CFX_Path path;
+  path.AppendPoint(CFX_PointF(view_bounding_box.left, view_bounding_box.top),
+                   CFX_Path::Point::Type::kMove);
+  path.AppendPoint(CFX_PointF(view_bounding_box.left, view_bounding_box.bottom),
+                   CFX_Path::Point::Type::kLine);
+  path.AppendPoint(
+      CFX_PointF(view_bounding_box.right, view_bounding_box.bottom),
+      CFX_Path::Point::Type::kLine);
+  path.AppendPoint(CFX_PointF(view_bounding_box.right, view_bounding_box.top),
+                   CFX_Path::Point::Type::kLine);
+  path.AppendPoint(CFX_PointF(view_bounding_box.left, view_bounding_box.top),
+                   CFX_Path::Point::Type::kLine);
+
+  CFX_GraphStateData graph_state_data;
+  graph_state_data.m_DashArray = {1.0f};
+  graph_state_data.m_DashPhase = 0;
+  graph_state_data.m_LineWidth = 1.0f;
+
+  render_device->DrawPath(path, &user_to_device, &graph_state_data, 0,
+                          ArgbEncode(255, 0, 0, 0),
+                          CFX_FillRenderOptions::EvenOddOptions());
+}
diff --git a/core/fxge/cfx_drawutils.h b/core/fxge/cfx_drawutils.h
new file mode 100644
index 0000000..5e4cd9e
--- /dev/null
+++ b/core/fxge/cfx_drawutils.h
@@ -0,0 +1,24 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_CFX_DRAWUTILS_H_
+#define CORE_FXGE_CFX_DRAWUTILS_H_
+
+class CFX_FloatRect;
+class CFX_Matrix;
+class CFX_RenderDevice;
+
+class CFX_DrawUtils {
+ public:
+  CFX_DrawUtils() = delete;
+  CFX_DrawUtils(const CFX_DrawUtils&) = delete;
+  CFX_DrawUtils& operator=(const CFX_DrawUtils&) = delete;
+
+  static void DrawFocusRect(CFX_RenderDevice* render_device,
+                            const CFX_Matrix& user_to_device,
+                            const CFX_FloatRect& view_bounding_box);
+};
+#endif  // CORE_FXGE_CFX_DRAWUTILS_H_
diff --git a/core/fxge/cfx_face.cpp b/core/fxge/cfx_face.cpp
index 6a41376..90b11fe 100644
--- a/core/fxge/cfx_face.cpp
+++ b/core/fxge/cfx_face.cpp
@@ -1,22 +1,27 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxge/cfx_face.h"
 
-#include "third_party/base/ptr_util.h"
+#include <utility>
+
+#include "third_party/base/check.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 // static
 RetainPtr<CFX_Face> CFX_Face::New(FT_Library library,
-                                  const RetainPtr<Retainable>& pDesc,
+                                  RetainPtr<Retainable> pDesc,
                                   pdfium::span<const FT_Byte> data,
                                   FT_Long face_index) {
   FXFT_FaceRec* pRec = nullptr;
-  if (FT_New_Memory_Face(library, data.data(), data.size(), face_index,
-                         &pRec) != 0) {
+  if (FT_New_Memory_Face(library, data.data(),
+                         pdfium::base::checked_cast<FT_Long>(data.size()),
+                         face_index, &pRec) != 0) {
     return nullptr;
   }
-  return pdfium::WrapRetain(new CFX_Face(pRec, pDesc));
+  // Private ctor.
+  return pdfium::WrapRetain(new CFX_Face(pRec, std::move(pDesc)));
 }
 
 // static
@@ -27,12 +32,13 @@
   if (FT_Open_Face(library, args, face_index, &pRec) != 0)
     return nullptr;
 
+  // Private ctor.
   return pdfium::WrapRetain(new CFX_Face(pRec, nullptr));
 }
 
-CFX_Face::CFX_Face(FXFT_FaceRec* rec, const RetainPtr<Retainable>& pDesc)
-    : m_pRec(rec), m_pDesc(pDesc) {
-  ASSERT(m_pRec);
+CFX_Face::CFX_Face(FXFT_FaceRec* rec, RetainPtr<Retainable> pDesc)
+    : m_pRec(rec), m_pDesc(std::move(pDesc)) {
+  DCHECK(m_pRec);
 }
 
 CFX_Face::~CFX_Face() = default;
diff --git a/core/fxge/cfx_face.h b/core/fxge/cfx_face.h
index 2412043..6c74eae 100644
--- a/core/fxge/cfx_face.h
+++ b/core/fxge/cfx_face.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/fx_freetype.h"
-#include "third_party/base/span.h"
+#include "core/fxge/freetype/fx_freetype.h"
+#include "third_party/base/containers/span.h"
 
-class CFX_Face : public Retainable, public Observable {
+class CFX_Face final : public Retainable, public Observable {
  public:
   static RetainPtr<CFX_Face> New(FT_Library library,
-                                 const RetainPtr<Retainable>& pDesc,
+                                 RetainPtr<Retainable> pDesc,
                                  pdfium::span<const FT_Byte> data,
                                  FT_Long face_index);
 
@@ -26,7 +26,7 @@
   FXFT_FaceRec* GetRec() { return m_pRec.get(); }
 
  private:
-  CFX_Face(FXFT_FaceRec* pRec, const RetainPtr<Retainable>& pDesc);
+  CFX_Face(FXFT_FaceRec* pRec, RetainPtr<Retainable> pDesc);
 
   ScopedFXFTFaceRec const m_pRec;
   RetainPtr<Retainable> const m_pDesc;
diff --git a/core/fxge/cfx_fillrenderoptions.h b/core/fxge/cfx_fillrenderoptions.h
new file mode 100644
index 0000000..d123f72
--- /dev/null
+++ b/core/fxge/cfx_fillrenderoptions.h
@@ -0,0 +1,91 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXGE_CFX_FILLRENDEROPTIONS_H_
+#define CORE_FXGE_CFX_FILLRENDEROPTIONS_H_
+
+#include <stdint.h>
+
+// Represents the options for filling paths.
+struct CFX_FillRenderOptions {
+  // FillType defines how path is filled.
+  enum class FillType : uint8_t {
+    // No filling needed.
+    kNoFill = 0,
+
+    // Use even-odd or inverse even-odd algorithms to decide if the area needs
+    // to be filled.
+    kEvenOdd = 1,
+
+    // Use winding or inverse winding algorithms to decide whether the area
+    // needs to be filled.
+    kWinding = 2,
+  };
+
+  static constexpr CFX_FillRenderOptions EvenOddOptions() {
+    return CFX_FillRenderOptions(FillType::kEvenOdd);
+  }
+  static constexpr CFX_FillRenderOptions WindingOptions() {
+    return CFX_FillRenderOptions(FillType::kWinding);
+  }
+
+  constexpr CFX_FillRenderOptions()
+      : CFX_FillRenderOptions(FillType::kNoFill) {}
+
+  // TODO(thestig): Switch to default member initializer for bit-fields when
+  // C++20 is available.
+  constexpr explicit CFX_FillRenderOptions(FillType fill_type)
+      : fill_type(fill_type),
+        adjust_stroke(false),
+        aliased_path(false),
+        full_cover(false),
+        rect_aa(false),
+        stroke(false),
+        stroke_text_mode(false),
+        text_mode(false),
+        zero_area(false) {}
+
+  bool operator==(const CFX_FillRenderOptions& other) const {
+    return fill_type == other.fill_type &&
+           adjust_stroke == other.adjust_stroke &&
+           aliased_path == other.aliased_path &&
+           full_cover == other.full_cover && rect_aa == other.rect_aa &&
+           stroke == other.stroke &&
+           stroke_text_mode == other.stroke_text_mode &&
+           text_mode == other.text_mode && zero_area == other.zero_area;
+  }
+
+  bool operator!=(const CFX_FillRenderOptions& other) const {
+    return !(*this == other);
+  }
+
+  // Fill type.
+  FillType fill_type;
+
+  // Adjusted stroke rendering is enabled.
+  bool adjust_stroke : 1;
+
+  // Whether anti aliasing is enabled for path rendering.
+  bool aliased_path : 1;
+
+  // Fills with the sum of colors from both cover and source.
+  bool full_cover : 1;
+
+  // Rect paths use anti-aliasing.
+  bool rect_aa : 1;
+
+  // Path is stroke.
+  bool stroke : 1;
+
+  // Renders text by filling strokes.
+  bool stroke_text_mode : 1;
+
+  // Path is text.
+  bool text_mode : 1;
+
+  // Path encloses zero area.
+  bool zero_area : 1;
+};
+
+#endif  // CORE_FXGE_CFX_FILLRENDEROPTIONS_H_
diff --git a/core/fxge/cfx_folderfontinfo.cpp b/core/fxge/cfx_folderfontinfo.cpp
index c93b1f0..af8691a 100644
--- a/core/fxge/cfx_folderfontinfo.cpp
+++ b/core/fxge/cfx_folderfontinfo.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,25 +6,20 @@
 
 #include "core/fxge/cfx_folderfontinfo.h"
 
+#include <iterator>
 #include <limits>
 #include <utility>
 
 #include "build/build_config.h"
 #include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_folder.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/fx_system.h"
 #include "core/fxge/cfx_fontmapper.h"
 #include "core/fxge/fx_font.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-#define CHARSET_FLAG_ANSI (1 << 0)
-#define CHARSET_FLAG_SYMBOL (1 << 1)
-#define CHARSET_FLAG_SHIFTJIS (1 << 2)
-#define CHARSET_FLAG_BIG5 (1 << 3)
-#define CHARSET_FLAG_GB (1 << 4)
-#define CHARSET_FLAG_KOREAN (1 << 5)
+#include "third_party/base/containers/contains.h"
 
 namespace {
 
@@ -54,6 +49,25 @@
   }
 };
 
+bool FindFamilyNameMatch(ByteStringView family_name,
+                         const ByteString& installed_font_name) {
+  absl::optional<size_t> result = installed_font_name.Find(family_name, 0);
+  if (!result.has_value())
+    return false;
+
+  size_t next_index = result.value() + family_name.GetLength();
+  // Rule out the case that |family_name| is a substring of
+  // |installed_font_name| but their family names are actually different words.
+  // For example: "Univers" and "Universal" are not a match because they have
+  // different family names, but "Univers" and "Univers Bold" are a match.
+  if (installed_font_name.IsValidIndex(next_index) &&
+      FXSYS_IsLowerASCII(installed_font_name[next_index])) {
+    return false;
+  }
+
+  return true;
+}
+
 ByteString ReadStringFromFile(FILE* pFile, uint32_t size) {
   ByteString result;
   {
@@ -70,14 +84,15 @@
                            const uint8_t* pTables,
                            uint32_t nTables,
                            uint32_t tag,
-                           uint32_t fileSize) {
+                           FX_FILESIZE fileSize) {
   for (uint32_t i = 0; i < nTables; i++) {
     const uint8_t* p = pTables + i * 16;
-    if (GET_TT_LONG(p) == tag) {
-      uint32_t offset = GET_TT_LONG(p + 8);
-      uint32_t size = GET_TT_LONG(p + 12);
+    if (FXSYS_UINT32_GET_MSBFIRST(p) == tag) {
+      uint32_t offset = FXSYS_UINT32_GET_MSBFIRST(p + 8);
+      uint32_t size = FXSYS_UINT32_GET_MSBFIRST(p + 12);
       if (offset > std::numeric_limits<uint32_t>::max() - size ||
-          offset + size > fileSize || fseek(pFile, offset, SEEK_SET) < 0) {
+          static_cast<FX_FILESIZE>(offset + size) > fileSize ||
+          fseek(pFile, offset, SEEK_SET) < 0) {
         return ByteString();
       }
       return ReadStringFromFile(pFile, size);
@@ -86,19 +101,19 @@
   return ByteString();
 }
 
-uint32_t GetCharset(int charset) {
+uint32_t GetCharset(FX_Charset charset) {
   switch (charset) {
-    case FX_CHARSET_ShiftJIS:
+    case FX_Charset::kShiftJIS:
       return CHARSET_FLAG_SHIFTJIS;
-    case FX_CHARSET_ChineseSimplified:
+    case FX_Charset::kChineseSimplified:
       return CHARSET_FLAG_GB;
-    case FX_CHARSET_ChineseTraditional:
+    case FX_Charset::kChineseTraditional:
       return CHARSET_FLAG_BIG5;
-    case FX_CHARSET_Hangul:
+    case FX_Charset::kHangul:
       return CHARSET_FLAG_KOREAN;
-    case FX_CHARSET_Symbol:
+    case FX_Charset::kSymbol:
       return CHARSET_FLAG_SYMBOL;
-    case FX_CHARSET_ANSI:
+    case FX_Charset::kANSI:
       return CHARSET_FLAG_ANSI;
     default:
       break;
@@ -109,8 +124,13 @@
 int32_t GetSimilarValue(int weight,
                         bool bItalic,
                         int pitch_family,
-                        uint32_t style) {
+                        uint32_t style,
+                        bool bMatchName,
+                        size_t familyNameLength,
+                        size_t bsNameLength) {
   int32_t iSimilarValue = 0;
+  if (bMatchName && (familyNameLength == bsNameLength))
+    iSimilarValue += 4;
   if (FontStyleIsForceBold(style) == (weight > 400))
     iSimilarValue += 16;
   if (FontStyleIsItalic(style) == bItalic)
@@ -142,14 +162,13 @@
 }
 
 void CFX_FolderFontInfo::ScanPath(const ByteString& path) {
-  std::unique_ptr<FX_FolderHandle, FxFolderHandleCloser> handle(
-      FX_OpenFolder(path.c_str()));
+  std::unique_ptr<FX_Folder> handle = FX_Folder::OpenFolder(path);
   if (!handle)
     return;
 
   ByteString filename;
   bool bFolder;
-  while (FX_GetNextFile(handle.get(), &filename, &bFolder)) {
+  while (handle->GetNextFile(&filename, &bFolder)) {
     if (bFolder) {
       if (filename == "." || filename == "..")
         continue;
@@ -161,7 +180,7 @@
     }
 
     ByteString fullpath = path;
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     fullpath += "\\";
 #else
     fullpath += "/";
@@ -179,7 +198,7 @@
 
   fseek(pFile.get(), 0, SEEK_END);
 
-  uint32_t filesize = ftell(pFile.get());
+  FX_FILESIZE filesize = ftell(pFile.get());
   uint8_t buffer[16];
   fseek(pFile.get(), 0, SEEK_SET);
 
@@ -187,12 +206,12 @@
   if (readCnt != 1)
     return;
 
-  if (GET_TT_LONG(buffer) != kTableTTCF) {
+  if (FXSYS_UINT32_GET_MSBFIRST(buffer) != kTableTTCF) {
     ReportFace(path, pFile.get(), filesize, 0);
     return;
   }
 
-  uint32_t nFaces = GET_TT_LONG(buffer + 8);
+  uint32_t nFaces = FXSYS_UINT32_GET_MSBFIRST(buffer + 8);
   FX_SAFE_SIZE_T safe_face_bytes = nFaces;
   safe_face_bytes *= 4;
   if (!safe_face_bytes.IsValid())
@@ -206,25 +225,29 @@
     return;
 
   auto offsets_span = pdfium::make_span(offsets.get(), face_bytes);
-  for (uint32_t i = 0; i < nFaces; i++)
-    ReportFace(path, pFile.get(), filesize, GET_TT_LONG(&offsets_span[i * 4]));
+  for (uint32_t i = 0; i < nFaces; i++) {
+    ReportFace(path, pFile.get(), filesize,
+               FXSYS_UINT32_GET_MSBFIRST(&offsets_span[i * 4]));
+  }
 }
 
 void CFX_FolderFontInfo::ReportFace(const ByteString& path,
                                     FILE* pFile,
-                                    uint32_t filesize,
+                                    FX_FILESIZE filesize,
                                     uint32_t offset) {
   char buffer[16];
   if (fseek(pFile, offset, SEEK_SET) < 0 || !fread(buffer, 12, 1, pFile))
     return;
 
-  uint32_t nTables = GET_TT_SHORT(buffer + 4);
+  uint32_t nTables = FXSYS_UINT16_GET_MSBFIRST(buffer + 4);
   ByteString tables = ReadStringFromFile(pFile, nTables * 16);
   if (tables.IsEmpty())
     return;
 
+  static constexpr uint32_t kNameTag =
+      CFX_FontMapper::MakeTag('n', 'a', 'm', 'e');
   ByteString names =
-      LoadTableFromTT(pFile, tables.raw_str(), nTables, 0x6e616d65, filesize);
+      LoadTableFromTT(pFile, tables.raw_str(), nTables, kNameTag, filesize);
   if (names.IsEmpty())
     return;
 
@@ -236,38 +259,40 @@
   if (style != "Regular")
     facename += " " + style;
 
-  if (pdfium::ContainsKey(m_FontList, facename))
+  if (pdfium::Contains(m_FontList, facename))
     return;
 
-  auto pInfo = pdfium::MakeUnique<FontFaceInfo>(path, facename, tables, offset,
-                                                filesize);
+  auto pInfo =
+      std::make_unique<FontFaceInfo>(path, facename, tables, offset, filesize);
+  static constexpr uint32_t kOs2Tag =
+      CFX_FontMapper::MakeTag('O', 'S', '/', '2');
   ByteString os2 =
-      LoadTableFromTT(pFile, tables.raw_str(), nTables, 0x4f532f32, filesize);
+      LoadTableFromTT(pFile, tables.raw_str(), nTables, kOs2Tag, filesize);
   if (os2.GetLength() >= 86) {
     const uint8_t* p = os2.raw_str() + 78;
-    uint32_t codepages = GET_TT_LONG(p);
+    uint32_t codepages = FXSYS_UINT32_GET_MSBFIRST(p);
     if (codepages & (1U << 17)) {
-      m_pMapper->AddInstalledFont(facename, FX_CHARSET_ShiftJIS);
+      m_pMapper->AddInstalledFont(facename, FX_Charset::kShiftJIS);
       pInfo->m_Charsets |= CHARSET_FLAG_SHIFTJIS;
     }
     if (codepages & (1U << 18)) {
-      m_pMapper->AddInstalledFont(facename, FX_CHARSET_ChineseSimplified);
+      m_pMapper->AddInstalledFont(facename, FX_Charset::kChineseSimplified);
       pInfo->m_Charsets |= CHARSET_FLAG_GB;
     }
     if (codepages & (1U << 20)) {
-      m_pMapper->AddInstalledFont(facename, FX_CHARSET_ChineseTraditional);
+      m_pMapper->AddInstalledFont(facename, FX_Charset::kChineseTraditional);
       pInfo->m_Charsets |= CHARSET_FLAG_BIG5;
     }
     if ((codepages & (1U << 19)) || (codepages & (1U << 21))) {
-      m_pMapper->AddInstalledFont(facename, FX_CHARSET_Hangul);
+      m_pMapper->AddInstalledFont(facename, FX_Charset::kHangul);
       pInfo->m_Charsets |= CHARSET_FLAG_KOREAN;
     }
     if (codepages & (1U << 31)) {
-      m_pMapper->AddInstalledFont(facename, FX_CHARSET_Symbol);
+      m_pMapper->AddInstalledFont(facename, FX_Charset::kSymbol);
       pInfo->m_Charsets |= CHARSET_FLAG_SYMBOL;
     }
   }
-  m_pMapper->AddInstalledFont(facename, FX_CHARSET_ANSI);
+  m_pMapper->AddInstalledFont(facename, FX_Charset::kANSI);
   pInfo->m_Charsets |= CHARSET_FLAG_ANSI;
   pInfo->m_Styles = 0;
   if (style.Contains("Bold"))
@@ -281,8 +306,7 @@
 }
 
 void* CFX_FolderFontInfo::GetSubstFont(const ByteString& face) {
-  for (size_t iBaseFont = 0; iBaseFont < FX_ArraySize(Base14Substs);
-       iBaseFont++) {
+  for (size_t iBaseFont = 0; iBaseFont < std::size(Base14Substs); iBaseFont++) {
     if (face == Base14Substs[iBaseFont].m_pName)
       return GetFont(Base14Substs[iBaseFont].m_pSubstName);
   }
@@ -291,52 +315,62 @@
 
 void* CFX_FolderFontInfo::FindFont(int weight,
                                    bool bItalic,
-                                   int charset,
+                                   FX_Charset charset,
                                    int pitch_family,
-                                   const char* family,
+                                   const ByteString& family,
                                    bool bMatchName) {
   FontFaceInfo* pFind = nullptr;
-  if (charset == FX_CHARSET_ANSI && FontFamilyIsFixedPitch(pitch_family))
-    return GetFont("Courier New");
 
-  ByteStringView bsFamily(family);
+  ByteStringView bsFamily = family.AsStringView();
   uint32_t charset_flag = GetCharset(charset);
   int32_t iBestSimilar = 0;
   for (const auto& it : m_FontList) {
     const ByteString& bsName = it.first;
     FontFaceInfo* pFont = it.second.get();
-    if (!(pFont->m_Charsets & charset_flag) && charset != FX_CHARSET_Default)
+    if (!(pFont->m_Charsets & charset_flag) && charset != FX_Charset::kDefault)
       continue;
 
-    if (bMatchName && !bsName.Contains(bsFamily))
+    if (bMatchName && !FindFamilyNameMatch(bsFamily, bsName))
       continue;
 
     int32_t iSimilarValue =
-        GetSimilarValue(weight, bItalic, pitch_family, pFont->m_Styles);
+        GetSimilarValue(weight, bItalic, pitch_family, pFont->m_Styles,
+                        bMatchName, bsFamily.GetLength(), bsName.GetLength());
     if (iSimilarValue > iBestSimilar) {
       iBestSimilar = iSimilarValue;
       pFind = pFont;
     }
   }
-  return pFind;
+
+  if (pFind) {
+    return pFind;
+  }
+
+  if (charset == FX_Charset::kANSI && FontFamilyIsFixedPitch(pitch_family)) {
+    auto* courier_new = GetFont("Courier New");
+    if (courier_new)
+      return courier_new;
+  }
+
+  return nullptr;
 }
 
 void* CFX_FolderFontInfo::MapFont(int weight,
                                   bool bItalic,
-                                  int charset,
+                                  FX_Charset charset,
                                   int pitch_family,
-                                  const char* family) {
+                                  const ByteString& face) {
   return nullptr;
 }
 
-void* CFX_FolderFontInfo::GetFont(const char* face) {
+void* CFX_FolderFontInfo::GetFont(const ByteString& face) {
   auto it = m_FontList.find(face);
   return it != m_FontList.end() ? it->second.get() : nullptr;
 }
 
-uint32_t CFX_FolderFontInfo::GetFontData(void* hFont,
-                                         uint32_t table,
-                                         pdfium::span<uint8_t> buffer) {
+size_t CFX_FolderFontInfo::GetFontData(void* hFont,
+                                       uint32_t table,
+                                       pdfium::span<uint8_t> buffer) {
   if (!hFont)
     return 0;
 
@@ -348,12 +382,12 @@
   } else if (table == kTableTTCF) {
     datasize = pFont->m_FontOffset ? pFont->m_FileSize : 0;
   } else {
-    uint32_t nTables = pFont->m_FontTables.GetLength() / 16;
-    for (uint32_t i = 0; i < nTables; i++) {
+    size_t nTables = pFont->m_FontTables.GetLength() / 16;
+    for (size_t i = 0; i < nTables; i++) {
       const uint8_t* p = pFont->m_FontTables.raw_str() + i * 16;
-      if (GET_TT_LONG(p) == table) {
-        offset = GET_TT_LONG(p + 8);
-        datasize = GET_TT_LONG(p + 12);
+      if (FXSYS_UINT32_GET_MSBFIRST(p) == table) {
+        offset = FXSYS_UINT32_GET_MSBFIRST(p + 8);
+        datasize = FXSYS_UINT32_GET_MSBFIRST(p + 12);
       }
     }
   }
@@ -382,7 +416,7 @@
   return true;
 }
 
-bool CFX_FolderFontInfo::GetFontCharset(void* hFont, int* charset) {
+bool CFX_FolderFontInfo::GetFontCharset(void* hFont, FX_Charset* charset) {
   return false;
 }
 
@@ -395,6 +429,4 @@
       m_FaceName(faceName),
       m_FontTables(fontTables),
       m_FontOffset(fontOffset),
-      m_FileSize(fileSize),
-      m_Styles(0),
-      m_Charsets(0) {}
+      m_FileSize(fileSize) {}
diff --git a/core/fxge/cfx_folderfontinfo.h b/core/fxge/cfx_folderfontinfo.h
index 4fd516d..63d73c3 100644
--- a/core/fxge/cfx_folderfontinfo.h
+++ b/core/fxge/cfx_folderfontinfo.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,10 +11,18 @@
 #include <memory>
 #include <vector>
 
+#include "core/fxcrt/fx_codepage_forward.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_fontmapper.h"
 #include "core/fxge/systemfontinfo_iface.h"
 
+#define CHARSET_FLAG_ANSI (1 << 0)
+#define CHARSET_FLAG_SYMBOL (1 << 1)
+#define CHARSET_FLAG_SHIFTJIS (1 << 2)
+#define CHARSET_FLAG_BIG5 (1 << 3)
+#define CHARSET_FLAG_GB (1 << 4)
+#define CHARSET_FLAG_KOREAN (1 << 5)
+
 class CFX_FolderFontInfo : public SystemFontInfoIface {
  public:
   CFX_FolderFontInfo();
@@ -22,22 +30,24 @@
 
   void AddPath(const ByteString& path);
 
-  // IFX_SytemFontInfo:
+  // SystemFontInfoIface:
   bool EnumFontList(CFX_FontMapper* pMapper) override;
   void* MapFont(int weight,
                 bool bItalic,
-                int charset,
+                FX_Charset charset,
                 int pitch_family,
-                const char* family) override;
-  void* GetFont(const char* face) override;
-  uint32_t GetFontData(void* hFont,
-                       uint32_t table,
-                       pdfium::span<uint8_t> buffer) override;
+                const ByteString& face) override;
+  void* GetFont(const ByteString& face) override;
+  size_t GetFontData(void* hFont,
+                     uint32_t table,
+                     pdfium::span<uint8_t> buffer) override;
   void DeleteFont(void* hFont) override;
   bool GetFaceName(void* hFont, ByteString* name) override;
-  bool GetFontCharset(void* hFont, int* charset) override;
+  bool GetFontCharset(void* hFont, FX_Charset* charset) override;
 
  protected:
+  friend class CFX_FolderFontInfoTest;
+
   class FontFaceInfo {
    public:
     FontFaceInfo(ByteString filePath,
@@ -51,22 +61,22 @@
     const ByteString m_FontTables;
     const uint32_t m_FontOffset;
     const uint32_t m_FileSize;
-    uint32_t m_Styles;
-    uint32_t m_Charsets;
+    uint32_t m_Styles = 0;
+    uint32_t m_Charsets = 0;
   };
 
   void ScanPath(const ByteString& path);
   void ScanFile(const ByteString& path);
   void ReportFace(const ByteString& path,
                   FILE* pFile,
-                  uint32_t filesize,
+                  FX_FILESIZE filesize,
                   uint32_t offset);
   void* GetSubstFont(const ByteString& face);
   void* FindFont(int weight,
                  bool bItalic,
-                 int charset,
+                 FX_Charset charset,
                  int pitch_family,
-                 const char* family,
+                 const ByteString& family,
                  bool bMatchName);
 
   std::map<ByteString, std::unique_ptr<FontFaceInfo>> m_FontList;
diff --git a/core/fxge/cfx_folderfontinfo_unittest.cpp b/core/fxge/cfx_folderfontinfo_unittest.cpp
new file mode 100644
index 0000000..8cdb8b9
--- /dev/null
+++ b/core/fxge/cfx_folderfontinfo_unittest.cpp
@@ -0,0 +1,135 @@
+// Copyright 2019 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/cfx_folderfontinfo.h"
+
+#include <utility>
+
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxge/fx_font.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+constexpr char kArial[] = "Arial";
+constexpr char kCourierNew[] = "Courier New";
+constexpr char kTimesNewRoman[] = "TimesNewRoman";
+constexpr char kSymbol[] = "Symbol";
+constexpr char kBookshelfSymbol7[] = "Bookshelf Symbol 7";
+constexpr char kCalibri[] = "Calibri";
+constexpr char kBookshelf[] = "Bookshelf";
+constexpr char kBook[] = "Book";
+constexpr char kTofuBold[] = "Tofu, Bold Italic";
+constexpr char kTofu[] = "Tofu";
+constexpr char kLatoUltraBold[] = "Lato Ultra-Bold";
+constexpr char kLato[] = "Lato";
+constexpr char kOxygenSansSansBold[] = "Oxygen-Sans Sans-Bold";
+constexpr char kOxygenSans[] = "Oxygen-Sans";
+constexpr char kOxygen[] = "Oxygen";
+constexpr char kComicSansMS[] = "Comic Sans MS";
+
+}  // namespace
+
+class CFX_FolderFontInfoTest : public ::testing::Test {
+ public:
+  CFX_FolderFontInfoTest() {
+    AddDummyFont(kArial, CHARSET_FLAG_ANSI);
+    AddDummyFont(kCourierNew, CHARSET_FLAG_ANSI);
+    AddDummyFont(kTimesNewRoman, 0);
+    AddDummyFont(kBookshelfSymbol7, CHARSET_FLAG_SYMBOL);
+    AddDummyFont(kSymbol, CHARSET_FLAG_SYMBOL);
+    AddDummyFont(kTofuBold, CHARSET_FLAG_SYMBOL);
+    AddDummyFont(kLatoUltraBold, CHARSET_FLAG_ANSI);
+    AddDummyFont(kOxygenSansSansBold, CHARSET_FLAG_ANSI);
+    AddDummyFont(kComicSansMS, CHARSET_FLAG_ANSI);
+  }
+
+  void* FindFont(int weight,
+                 bool bItalic,
+                 FX_Charset charset,
+                 int pitch_family,
+                 const char* family,
+                 bool bMatchName) {
+    return font_info_.FindFont(weight, bItalic, charset, pitch_family, family,
+                               bMatchName);
+  }
+
+  ByteString GetFaceName(void* font) {
+    return static_cast<CFX_FolderFontInfo::FontFaceInfo*>(font)->m_FaceName;
+  }
+
+ private:
+  void AddDummyFont(const char* font_name, uint32_t charsets) {
+    auto info = std::make_unique<CFX_FolderFontInfo::FontFaceInfo>(
+        /*filePath=*/"", font_name, /*fontTables=*/"",
+        /*fontOffset=*/0, /*fileSize=*/0);
+    info->m_Charsets = charsets;
+    font_info_.m_FontList[font_name] = std::move(info);
+  }
+
+  CFX_FolderFontInfo font_info_;
+};
+
+TEST_F(CFX_FolderFontInfoTest, TestFindFont) {
+  // Find "Symbol" font
+  void* font = FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kSymbol,
+                        FXFONT_FF_ROMAN, kSymbol, /*bMatchName=*/true);
+  ASSERT_TRUE(font);
+  EXPECT_EQ(GetFaceName(font), kSymbol);
+
+  // Find "Calibri" font that is not present in the installed fonts
+  EXPECT_FALSE(FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kSymbol,
+                        FXFONT_FF_ROMAN, kCalibri, /*bMatchName=*/true));
+
+  // Find the closest matching font to "Bookshelf" font that is present in the
+  // installed fonts
+  font = FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kSymbol,
+                  FXFONT_FF_ROMAN, kBookshelf, /*bMatchName=*/true);
+  ASSERT_TRUE(font);
+  EXPECT_EQ(GetFaceName(font), kBookshelfSymbol7);
+
+  // Find "Book" font is expected to fail, because none of the installed fonts
+  // is in the same font family.
+  EXPECT_FALSE(FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kSymbol,
+                        FXFONT_FF_ROMAN, kBook, /*bMatchName=*/true));
+
+  // Find the closest matching font for "Tofu" in the installed fonts, which
+  // has "," following the string "Tofu".
+  font = FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kSymbol,
+                  FXFONT_FF_ROMAN, kTofu, /*bMatchName=*/true);
+  ASSERT_TRUE(font);
+  EXPECT_EQ(GetFaceName(font), kTofuBold);
+
+  // Find the closest matching font for "Lato" in the installed fonts, which
+  // has a space character following the string "Lato".
+  font = FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kANSI,
+                  FXFONT_FF_ROMAN, kLato, /*bMatchName=*/true);
+  ASSERT_TRUE(font);
+  EXPECT_EQ(GetFaceName(font), kLatoUltraBold);
+
+  // Find the closest matching font for "Oxygen" in the installed fonts,
+  // which has "-" following the string "Oxygen".
+  font = FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kANSI,
+                  FXFONT_FF_ROMAN, kOxygen, /*bMatchName=*/true);
+  ASSERT_TRUE(font);
+  EXPECT_EQ(GetFaceName(font), kOxygenSansSansBold);
+
+  // Find the closest matching font for "Oxygen-Sans" in the installed fonts,
+  // to test matching a family name with "-".
+  font = FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kANSI,
+                  FXFONT_FF_ROMAN, kOxygenSans, /*bMatchName=*/true);
+  ASSERT_TRUE(font);
+  EXPECT_EQ(GetFaceName(font), kOxygenSansSansBold);
+
+  // Find "Symbol" font when name matching is false
+  font = FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kSymbol,
+                  FXFONT_FF_ROMAN, kSymbol, /*bMatchName=*/false);
+  ASSERT_TRUE(font);
+  EXPECT_EQ(GetFaceName(font), kBookshelfSymbol7);
+
+  font = FindFont(700, false, FX_Charset::kANSI, FXFONT_FF_FIXEDPITCH,
+                  kComicSansMS, true);
+  ASSERT_TRUE(font);
+  EXPECT_EQ(GetFaceName(font), kComicSansMS);
+}
diff --git a/core/fxge/cfx_font.cpp b/core/fxge/cfx_font.cpp
index 5a51b6d..0330386 100644
--- a/core/fxge/cfx_font.cpp
+++ b/core/fxge/cfx_font.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,24 +6,30 @@
 
 #include "core/fxge/cfx_font.h"
 
+#include <stdint.h>
+
 #include <algorithm>
 #include <limits>
 #include <memory>
 #include <utility>
-#include <vector>
 
 #include "build/build_config.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_fontcache.h"
+#include "core/fxge/cfx_fontmapper.h"
 #include "core/fxge/cfx_fontmgr.h"
 #include "core/fxge/cfx_gemodule.h"
 #include "core/fxge/cfx_glyphcache.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_substfont.h"
 #include "core/fxge/fx_font.h"
 #include "core/fxge/scoped_font_transform.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/span.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 #define EM_ADJUST(em, a) (em == 0 ? (a) : (a)*1000 / em)
 
@@ -33,14 +39,33 @@
 constexpr int kThousandthMaxInt = std::numeric_limits<int>::max() / 1000;
 
 struct OUTLINE_PARAMS {
-  CFX_PathData* m_pPath;
-  int m_CurX;
-  int m_CurY;
+  UnownedPtr<CFX_Path> m_pPath;
+  FT_Pos m_CurX;
+  FT_Pos m_CurY;
   float m_CoordUnit;
 };
 
-#ifdef PDF_ENABLE_XFA
+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),
+                 pdfium::base::checked_cast<int32_t>(right),
+                 pdfium::base::checked_cast<int32_t>(bottom));
+}
 
+FX_RECT ScaledFXRectFromFTPos(FT_Pos left,
+                              FT_Pos top,
+                              FT_Pos right,
+                              FT_Pos bottom,
+                              int x_scale,
+                              int y_scale) {
+  if (x_scale == 0 || y_scale == 0)
+    return FXRectFromFTPos(left, top, right, bottom);
+
+  return FXRectFromFTPos(left * 1000 / x_scale, top * 1000 / y_scale,
+                         right * 1000 / x_scale, bottom * 1000 / y_scale);
+}
+
+#ifdef PDF_ENABLE_XFA
 unsigned long FTStreamRead(FXFT_StreamRec* stream,
                            unsigned long offset,
                            unsigned char* buffer,
@@ -50,52 +75,34 @@
 
   IFX_SeekableReadStream* pFile =
       static_cast<IFX_SeekableReadStream*>(stream->descriptor.pointer);
-  return pFile && pFile->ReadBlockAtOffset(buffer, offset, count) ? count : 0;
+  return pFile && pFile->ReadBlockAtOffset({buffer, count}, offset) ? count : 0;
 }
 
 void FTStreamClose(FXFT_StreamRec* stream) {}
-
-RetainPtr<CFX_Face> LoadFileImp(FXFT_LibraryRec* library,
-                                const RetainPtr<IFX_SeekableReadStream>& pFile,
-                                int32_t faceIndex,
-                                std::unique_ptr<FXFT_StreamRec>* stream) {
-  auto stream1 = pdfium::MakeUnique<FXFT_StreamRec>();
-  stream1->base = nullptr;
-  stream1->size = static_cast<unsigned long>(pFile->GetSize());
-  stream1->pos = 0;
-  stream1->descriptor.pointer = static_cast<void*>(pFile.Get());
-  stream1->close = FTStreamClose;
-  stream1->read = FTStreamRead;
-
-  FT_Open_Args args;
-  args.flags = FT_OPEN_STREAM;
-  args.stream = stream1.get();
-
-  RetainPtr<CFX_Face> face = CFX_Face::Open(library, &args, faceIndex);
-  if (!face)
-    return nullptr;
-
-  *stream = std::move(stream1);
-  return face;
-}
 #endif  // PDF_ENABLE_XFA
 
 void Outline_CheckEmptyContour(OUTLINE_PARAMS* param) {
-  std::vector<FX_PATHPOINT>& points = param->m_pPath->GetPoints();
-  size_t size = points.size();
+  size_t size;
+  {
+    pdfium::span<const CFX_Path::Point> points = param->m_pPath->GetPoints();
+    size = points.size();
 
-  if (size >= 2 && points[size - 2].IsTypeAndOpen(FXPT_TYPE::MoveTo) &&
-      points[size - 2].m_Point == points[size - 1].m_Point) {
-    size -= 2;
+    if (size >= 2 &&
+        points[size - 2].IsTypeAndOpen(CFX_Path::Point::Type::kMove) &&
+        points[size - 2].m_Point == points[size - 1].m_Point) {
+      size -= 2;
+    }
+    if (size >= 4 &&
+        points[size - 4].IsTypeAndOpen(CFX_Path::Point::Type::kMove) &&
+        points[size - 3].IsTypeAndOpen(CFX_Path::Point::Type::kBezier) &&
+        points[size - 3].m_Point == points[size - 4].m_Point &&
+        points[size - 2].m_Point == points[size - 4].m_Point &&
+        points[size - 1].m_Point == points[size - 4].m_Point) {
+      size -= 4;
+    }
   }
-  if (size >= 4 && points[size - 4].IsTypeAndOpen(FXPT_TYPE::MoveTo) &&
-      points[size - 3].IsTypeAndOpen(FXPT_TYPE::BezierTo) &&
-      points[size - 3].m_Point == points[size - 4].m_Point &&
-      points[size - 2].m_Point == points[size - 4].m_Point &&
-      points[size - 1].m_Point == points[size - 4].m_Point) {
-    size -= 4;
-  }
-  points.resize(size);
+  // Only safe after |points| has been destroyed.
+  param->m_pPath->GetPoints().resize(size);
 }
 
 int Outline_MoveTo(const FT_Vector* to, void* user) {
@@ -106,7 +113,7 @@
   param->m_pPath->ClosePath();
   param->m_pPath->AppendPoint(
       CFX_PointF(to->x / param->m_CoordUnit, to->y / param->m_CoordUnit),
-      FXPT_TYPE::MoveTo, false);
+      CFX_Path::Point::Type::kMove);
 
   param->m_CurX = to->x;
   param->m_CurY = to->y;
@@ -118,7 +125,7 @@
 
   param->m_pPath->AppendPoint(
       CFX_PointF(to->x / param->m_CoordUnit, to->y / param->m_CoordUnit),
-      FXPT_TYPE::LineTo, false);
+      CFX_Path::Point::Type::kLine);
 
   param->m_CurX = to->x;
   param->m_CurY = to->y;
@@ -133,16 +140,16 @@
                      param->m_CoordUnit,
                  (param->m_CurY + (control->y - param->m_CurY) * 2 / 3) /
                      param->m_CoordUnit),
-      FXPT_TYPE::BezierTo, false);
+      CFX_Path::Point::Type::kBezier);
 
   param->m_pPath->AppendPoint(
       CFX_PointF((control->x + (to->x - control->x) / 3) / param->m_CoordUnit,
                  (control->y + (to->y - control->y) / 3) / param->m_CoordUnit),
-      FXPT_TYPE::BezierTo, false);
+      CFX_Path::Point::Type::kBezier);
 
   param->m_pPath->AppendPoint(
       CFX_PointF(to->x / param->m_CoordUnit, to->y / param->m_CoordUnit),
-      FXPT_TYPE::BezierTo, false);
+      CFX_Path::Point::Type::kBezier);
 
   param->m_CurX = to->x;
   param->m_CurY = to->y;
@@ -157,15 +164,15 @@
 
   param->m_pPath->AppendPoint(CFX_PointF(control1->x / param->m_CoordUnit,
                                          control1->y / param->m_CoordUnit),
-                              FXPT_TYPE::BezierTo, false);
+                              CFX_Path::Point::Type::kBezier);
 
   param->m_pPath->AppendPoint(CFX_PointF(control2->x / param->m_CoordUnit,
                                          control2->y / param->m_CoordUnit),
-                              FXPT_TYPE::BezierTo, false);
+                              CFX_Path::Point::Type::kBezier);
 
   param->m_pPath->AppendPoint(
       CFX_PointF(to->x / param->m_CoordUnit, to->y / param->m_CoordUnit),
-      FXPT_TYPE::BezierTo, false);
+      CFX_Path::Point::Type::kBezier);
 
   param->m_CurX = to->x;
   param->m_CurY = to->y;
@@ -176,23 +183,22 @@
   return !style.IsEmpty() && style != "Regular";
 }
 
-}  // namespace
-
-const char CFX_Font::s_AngleSkew[] = {
-    0,  2,  3,  5,  7,  9,  11, 12, 14, 16, 18, 19, 21, 23, 25,
-    27, 29, 31, 32, 34, 36, 38, 40, 42, 45, 47, 49, 51, 53, 55,
+constexpr int8_t kAngleSkew[] = {
+    -0,  -2,  -3,  -5,  -7,  -9,  -11, -12, -14, -16, -18, -19, -21, -23, -25,
+    -27, -29, -31, -32, -34, -36, -38, -40, -42, -45, -47, -49, -51, -53, -55,
 };
 
-const uint8_t CFX_Font::s_WeightPow[] = {
-    0,  3,  6,  7,  8,  9,  11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22,
-    23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 35, 36, 36, 37,
-    37, 37, 38, 38, 38, 39, 39, 39, 40, 40, 40, 41, 41, 41, 42, 42, 42,
-    42, 43, 43, 43, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47,
-    47, 47, 47, 48, 48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 50,
-    51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53,
+constexpr uint8_t kWeightPow[] = {
+    0,   6,   12,  14,  16,  18,  22,  24,  28,  30,  32,  34,  36,  38,  40,
+    42,  44,  46,  48,  50,  52,  54,  56,  58,  60,  62,  64,  66,  68,  70,
+    70,  72,  72,  74,  74,  74,  76,  76,  76,  78,  78,  78,  80,  80,  80,
+    82,  82,  82,  84,  84,  84,  84,  86,  86,  86,  88,  88,  88,  88,  90,
+    90,  90,  90,  92,  92,  92,  92,  94,  94,  94,  94,  96,  96,  96,  96,
+    96,  98,  98,  98,  98,  100, 100, 100, 100, 100, 102, 102, 102, 102, 102,
+    104, 104, 104, 104, 104, 106, 106, 106, 106, 106,
 };
 
-const uint8_t CFX_Font::s_WeightPow_11[] = {
+constexpr uint8_t kWeightPow11[] = {
     0,  4,  7,  8,  9,  10, 12, 13, 15, 17, 18, 19, 20, 21, 22, 23, 24,
     25, 26, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 39, 39, 40, 40, 41,
     41, 41, 42, 42, 42, 43, 43, 43, 44, 44, 44, 45, 45, 45, 46, 46, 46,
@@ -201,28 +207,37 @@
     56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58,
 };
 
-const uint8_t CFX_Font::s_WeightPow_SHIFTJIS[] = {
-    0,  0,  1,  2,  3,  4,  5,  7,  8,  10, 11, 13, 14, 16, 17, 19, 21,
-    22, 24, 26, 28, 30, 32, 33, 35, 37, 39, 41, 43, 45, 48, 48, 48, 48,
-    49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 52, 52, 53,
-    53, 53, 53, 53, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 56, 56, 56,
-    56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 58,
-    59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 60, 60, 60,
+constexpr uint8_t kWeightPowShiftJis[] = {
+    0,   0,   2,   4,   6,   8,   10,  14,  16,  20,  22,  26,  28,  32,  34,
+    38,  42,  44,  48,  52,  56,  60,  64,  66,  70,  74,  78,  82,  86,  90,
+    96,  96,  96,  96,  98,  98,  98,  100, 100, 100, 100, 102, 102, 102, 102,
+    104, 104, 104, 104, 104, 106, 106, 106, 106, 106, 108, 108, 108, 108, 108,
+    110, 110, 110, 110, 110, 112, 112, 112, 112, 112, 112, 114, 114, 114, 114,
+    114, 114, 114, 116, 116, 116, 116, 116, 116, 116, 118, 118, 118, 118, 118,
+    118, 118, 120, 120, 120, 120, 120, 120, 120, 120,
 };
 
-const CFX_Font::CharsetFontMap CFX_Font::defaultTTFMap[] = {
-    {FX_CHARSET_ANSI, kDefaultAnsiFontName},
-    {FX_CHARSET_ChineseSimplified, "SimSun"},
-    {FX_CHARSET_ChineseTraditional, "MingLiU"},
-    {FX_CHARSET_ShiftJIS, "MS Gothic"},
-    {FX_CHARSET_Hangul, "Batang"},
-    {FX_CHARSET_MSWin_Cyrillic, "Arial"},
-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_ || defined(OS_MACOSX)
-    {FX_CHARSET_MSWin_EasternEuropean, "Arial"},
+constexpr size_t kWeightPowArraySize = 100;
+static_assert(kWeightPowArraySize == std::size(kWeightPow), "Wrong size");
+static_assert(kWeightPowArraySize == std::size(kWeightPow11), "Wrong size");
+static_assert(kWeightPowArraySize == std::size(kWeightPowShiftJis),
+              "Wrong size");
+
+}  // namespace
+
+const CFX_Font::CharsetFontMap CFX_Font::kDefaultTTFMap[] = {
+    {static_cast<int>(FX_Charset::kANSI), kDefaultAnsiFontName},
+    {static_cast<int>(FX_Charset::kChineseSimplified), "SimSun"},
+    {static_cast<int>(FX_Charset::kChineseTraditional), "MingLiU"},
+    {static_cast<int>(FX_Charset::kShiftJIS), "MS Gothic"},
+    {static_cast<int>(FX_Charset::kHangul), "Batang"},
+    {static_cast<int>(FX_Charset::kMSWin_Cyrillic), "Arial"},
+#if BUILDFLAG(IS_WIN)
+    {static_cast<int>(FX_Charset::kMSWin_EasternEuropean), "Tahoma"},
 #else
-    {FX_CHARSET_MSWin_EasternEuropean, "Tahoma"},
+    {static_cast<int>(FX_Charset::kMSWin_EasternEuropean), "Arial"},
 #endif
-    {FX_CHARSET_MSWin_Arabic, "Arial"},
+    {static_cast<int>(FX_Charset::kMSWin_Arabic), "Arial"},
     {-1, nullptr}};
 
 // static
@@ -235,65 +250,63 @@
 const char CFX_Font::kUniversalDefaultFontName[] = "Arial Unicode MS";
 
 // static
-ByteString CFX_Font::GetDefaultFontNameByCharset(uint8_t nCharset) {
-  int i = 0;
-  while (defaultTTFMap[i].charset != -1) {
-    if (nCharset == static_cast<uint8_t>(defaultTTFMap[i].charset))
-      return defaultTTFMap[i].fontname;
-    ++i;
+ByteString CFX_Font::GetDefaultFontNameByCharset(FX_Charset nCharset) {
+  for (size_t i = 0; i < std::size(kDefaultTTFMap) - 1; ++i) {
+    if (static_cast<int>(nCharset) == kDefaultTTFMap[i].charset)
+      return kDefaultTTFMap[i].fontname;
   }
   return kUniversalDefaultFontName;
 }
 
 // static
-uint8_t CFX_Font::GetCharSetFromUnicode(uint16_t word) {
+FX_Charset CFX_Font::GetCharSetFromUnicode(uint16_t word) {
   // to avoid CJK Font to show ASCII
   if (word < 0x7F)
-    return FX_CHARSET_ANSI;
+    return FX_Charset::kANSI;
 
   // find new charset
   if ((word >= 0x4E00 && word <= 0x9FA5) ||
       (word >= 0xE7C7 && word <= 0xE7F3) ||
       (word >= 0x3000 && word <= 0x303F) ||
       (word >= 0x2000 && word <= 0x206F)) {
-    return FX_CHARSET_ChineseSimplified;
+    return FX_Charset::kChineseSimplified;
   }
 
   if (((word >= 0x3040) && (word <= 0x309F)) ||
       ((word >= 0x30A0) && (word <= 0x30FF)) ||
       ((word >= 0x31F0) && (word <= 0x31FF)) ||
       ((word >= 0xFF00) && (word <= 0xFFEF))) {
-    return FX_CHARSET_ShiftJIS;
+    return FX_Charset::kShiftJIS;
   }
 
   if (((word >= 0xAC00) && (word <= 0xD7AF)) ||
       ((word >= 0x1100) && (word <= 0x11FF)) ||
       ((word >= 0x3130) && (word <= 0x318F))) {
-    return FX_CHARSET_Hangul;
+    return FX_Charset::kHangul;
   }
 
   if (word >= 0x0E00 && word <= 0x0E7F)
-    return FX_CHARSET_Thai;
+    return FX_Charset::kThai;
 
   if ((word >= 0x0370 && word <= 0x03FF) || (word >= 0x1F00 && word <= 0x1FFF))
-    return FX_CHARSET_MSWin_Greek;
+    return FX_Charset::kMSWin_Greek;
 
   if ((word >= 0x0600 && word <= 0x06FF) || (word >= 0xFB50 && word <= 0xFEFC))
-    return FX_CHARSET_MSWin_Arabic;
+    return FX_Charset::kMSWin_Arabic;
 
   if (word >= 0x0590 && word <= 0x05FF)
-    return FX_CHARSET_MSWin_Hebrew;
+    return FX_Charset::kMSWin_Hebrew;
 
   if (word >= 0x0400 && word <= 0x04FF)
-    return FX_CHARSET_MSWin_Cyrillic;
+    return FX_Charset::kMSWin_Cyrillic;
 
   if (word >= 0x0100 && word <= 0x024F)
-    return FX_CHARSET_MSWin_EasternEuropean;
+    return FX_Charset::kMSWin_EasternEuropean;
 
   if (word >= 0x1E00 && word <= 0x1EFF)
-    return FX_CHARSET_MSWin_Vietnamese;
+    return FX_Charset::kMSWin_Vietnamese;
 
-  return FX_CHARSET_ANSI;
+  return FX_Charset::kANSI;
 }
 
 CFX_Font::CFX_Font() = default;
@@ -304,38 +317,52 @@
 }
 
 #ifdef PDF_ENABLE_XFA
-bool CFX_Font::LoadFile(const RetainPtr<IFX_SeekableReadStream>& pFile,
+bool CFX_Font::LoadFile(RetainPtr<IFX_SeekableReadStream> pFile,
                         int nFaceIndex) {
   m_bEmbedded = false;
+  m_ObjectTag = 0;
 
-  CFX_FontMgr* pFontMgr = CFX_GEModule::Get()->GetFontMgr();
-  std::unique_ptr<FXFT_StreamRec> stream;
-  m_Face = LoadFileImp(pFontMgr->GetFTLibrary(), pFile, nFaceIndex, &stream);
+  auto pStreamRec = std::make_unique<FXFT_StreamRec>();
+  pStreamRec->base = nullptr;
+  pStreamRec->size = static_cast<unsigned long>(pFile->GetSize());
+  pStreamRec->pos = 0;
+  pStreamRec->descriptor.pointer = static_cast<void*>(pFile.Get());
+  pStreamRec->close = FTStreamClose;
+  pStreamRec->read = FTStreamRead;
+
+  FT_Open_Args args;
+  args.flags = FT_OPEN_STREAM;
+  args.stream = pStreamRec.get();
+
+  m_Face = CFX_Face::Open(CFX_GEModule::Get()->GetFontMgr()->GetFTLibrary(),
+                          &args, nFaceIndex);
   if (!m_Face)
     return false;
 
-  m_pOwnedStream = std::move(stream);
+  m_pOwnedFile = std::move(pFile);
+  m_pOwnedStreamRec = std::move(pStreamRec);
   FT_Set_Pixel_Sizes(m_Face->GetRec(), 0, 64);
   return true;
 }
 
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
 void CFX_Font::SetFace(RetainPtr<CFX_Face> face) {
   ClearGlyphCache();
+  m_ObjectTag = 0;
   m_Face = face;
 }
 
 void CFX_Font::SetSubstFont(std::unique_ptr<CFX_SubstFont> subst) {
   m_pSubstFont = std::move(subst);
 }
-#endif  // !defined(OS_WIN)
+#endif  // !BUILDFLAG(IS_WIN)
 #endif  // PDF_ENABLE_XFA
 
 CFX_Font::~CFX_Font() {
   m_FontData = {};  // m_FontData can't outive m_Face.
   m_Face.Reset();
 
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
   ReleasePlatformResource();
 #endif
 }
@@ -345,13 +372,14 @@
                          uint32_t flags,
                          int weight,
                          int italic_angle,
-                         int CharsetCP,
+                         FX_CodePage code_page,
                          bool bVertical) {
   m_bEmbedded = false;
   m_bVertical = bVertical;
-  m_pSubstFont = pdfium::MakeUnique<CFX_SubstFont>();
-  m_Face = CFX_GEModule::Get()->GetFontMgr()->FindSubstFont(
-      face_name, bTrueType, flags, weight, italic_angle, CharsetCP,
+  m_ObjectTag = 0;
+  m_pSubstFont = std::make_unique<CFX_SubstFont>();
+  m_Face = CFX_GEModule::Get()->GetFontMgr()->GetBuiltinMapper()->FindSubstFont(
+      face_name, bTrueType, flags, weight, italic_angle, code_page,
       m_pSubstFont.get());
   if (m_Face) {
     m_FontData = {FXFT_Get_Face_Stream_Base(m_Face->GetRec()),
@@ -359,30 +387,44 @@
   }
 }
 
-uint32_t CFX_Font::GetGlyphWidth(uint32_t glyph_index) {
+int CFX_Font::GetGlyphWidth(uint32_t glyph_index) const {
+  return GetGlyphWidth(glyph_index, 0, 0);
+}
+
+int CFX_Font::GetGlyphWidth(uint32_t glyph_index,
+                            int dest_width,
+                            int weight) const {
+  return GetOrCreateGlyphCache()->GetGlyphWidth(this, glyph_index, dest_width,
+                                                weight);
+}
+
+int CFX_Font::GetGlyphWidthImpl(uint32_t glyph_index,
+                                int dest_width,
+                                int weight) const {
   if (!m_Face)
     return 0;
-  if (m_pSubstFont && m_pSubstFont->m_bFlagMM)
-    AdjustMMParams(glyph_index, 0, 0);
+  if (m_pSubstFont && m_pSubstFont->IsBuiltInGenericFont())
+    AdjustMMParams(glyph_index, dest_width, weight);
   int err =
       FT_Load_Glyph(m_Face->GetRec(), glyph_index,
                     FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
   if (err)
     return 0;
 
-  int horiAdvance = FXFT_Get_Glyph_HoriAdvance(m_Face->GetRec());
-  if (horiAdvance < 0 || horiAdvance > kThousandthMaxInt)
+  FT_Pos horiAdvance = FXFT_Get_Glyph_HoriAdvance(m_Face->GetRec());
+  if (horiAdvance < kThousandthMinInt || horiAdvance > kThousandthMaxInt)
     return 0;
 
-  return EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face->GetRec()), horiAdvance);
+  return static_cast<int>(
+      EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face->GetRec()), horiAdvance));
 }
 
 bool CFX_Font::LoadEmbedded(pdfium::span<const uint8_t> src_span,
-                            bool bForceAsVertical) {
-  if (bForceAsVertical)
-    m_bVertical = true;
-  m_FontDataAllocation = std::vector<uint8_t, FxAllocAllocator<uint8_t>>(
-      src_span.begin(), src_span.end());
+                            bool force_vertical,
+                            uint64_t object_tag) {
+  m_bVertical = force_vertical;
+  m_ObjectTag = object_tag;
+  m_FontDataAllocation = DataVector<uint8_t>(src_span.begin(), src_span.end());
   m_Face = CFX_GEModule::Get()->GetFontMgr()->NewFixedFace(
       nullptr, m_FontDataAllocation, 0);
   m_bEmbedded = true;
@@ -416,70 +458,53 @@
   return EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face->GetRec()), descender);
 }
 
-bool CFX_Font::GetGlyphBBox(uint32_t glyph_index, FX_RECT* pBBox) {
+absl::optional<FX_RECT> CFX_Font::GetGlyphBBox(uint32_t glyph_index) {
   if (!m_Face)
-    return false;
+    return absl::nullopt;
 
   if (FXFT_Is_Face_Tricky(m_Face->GetRec())) {
     int error = FT_Set_Char_Size(m_Face->GetRec(), 0, 1000 * 64, 72, 72);
     if (error)
-      return false;
+      return absl::nullopt;
 
     error = FT_Load_Glyph(m_Face->GetRec(), glyph_index,
                           FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
     if (error)
-      return false;
+      return absl::nullopt;
 
-    FT_BBox cbox;
     FT_Glyph glyph;
     error = FT_Get_Glyph(m_Face->GetRec()->glyph, &glyph);
     if (error)
-      return false;
+      return absl::nullopt;
 
+    FT_BBox cbox;
     FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &cbox);
     int pixel_size_x = m_Face->GetRec()->size->metrics.x_ppem;
     int pixel_size_y = m_Face->GetRec()->size->metrics.y_ppem;
-    if (pixel_size_x == 0 || pixel_size_y == 0) {
-      pBBox->left = cbox.xMin;
-      pBBox->right = cbox.xMax;
-      pBBox->top = cbox.yMax;
-      pBBox->bottom = cbox.yMin;
-    } else {
-      pBBox->left = cbox.xMin * 1000 / pixel_size_x;
-      pBBox->right = cbox.xMax * 1000 / pixel_size_x;
-      pBBox->top = cbox.yMax * 1000 / pixel_size_y;
-      pBBox->bottom = cbox.yMin * 1000 / pixel_size_y;
-    }
-    pBBox->top = std::min(
-        pBBox->top,
-        static_cast<int32_t>(FXFT_Get_Face_Ascender(m_Face->GetRec())));
-    pBBox->bottom = std::max(
-        pBBox->bottom,
-        static_cast<int32_t>(FXFT_Get_Face_Descender(m_Face->GetRec())));
+    FX_RECT result = ScaledFXRectFromFTPos(
+        cbox.xMin, cbox.yMax, cbox.xMax, cbox.yMin, pixel_size_x, pixel_size_y);
+    result.top =
+        std::min(result.top, pdfium::base::checked_cast<int32_t>(
+                                 FXFT_Get_Face_Ascender(m_Face->GetRec())));
+    result.bottom =
+        std::max(result.bottom, pdfium::base::checked_cast<int32_t>(
+                                    FXFT_Get_Face_Descender(m_Face->GetRec())));
     FT_Done_Glyph(glyph);
-    return FT_Set_Pixel_Sizes(m_Face->GetRec(), 0, 64) == 0;
+    if (FT_Set_Pixel_Sizes(m_Face->GetRec(), 0, 64) != 0)
+      return absl::nullopt;
+    return result;
   }
-  if (FT_Load_Glyph(m_Face->GetRec(), glyph_index,
-                    FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH)) {
-    return false;
-  }
+  constexpr int kFlag = FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
+  if (FT_Load_Glyph(m_Face->GetRec(), glyph_index, kFlag) != 0)
+    return absl::nullopt;
   int em = FXFT_Get_Face_UnitsPerEM(m_Face->GetRec());
-  if (em == 0) {
-    pBBox->left = FXFT_Get_Glyph_HoriBearingX(m_Face->GetRec());
-    pBBox->bottom = FXFT_Get_Glyph_HoriBearingY(m_Face->GetRec());
-    pBBox->top = pBBox->bottom - FXFT_Get_Glyph_Height(m_Face->GetRec());
-    pBBox->right = pBBox->left + FXFT_Get_Glyph_Width(m_Face->GetRec());
-  } else {
-    pBBox->left = FXFT_Get_Glyph_HoriBearingX(m_Face->GetRec()) * 1000 / em;
-    pBBox->top = (FXFT_Get_Glyph_HoriBearingY(m_Face->GetRec()) -
-                  FXFT_Get_Glyph_Height(m_Face->GetRec())) *
-                 1000 / em;
-    pBBox->right = (FXFT_Get_Glyph_HoriBearingX(m_Face->GetRec()) +
-                    FXFT_Get_Glyph_Width(m_Face->GetRec())) *
-                   1000 / em;
-    pBBox->bottom = (FXFT_Get_Glyph_HoriBearingY(m_Face->GetRec())) * 1000 / em;
-  }
-  return true;
+  return ScaledFXRectFromFTPos(FXFT_Get_Glyph_HoriBearingX(m_Face->GetRec()),
+                               FXFT_Get_Glyph_HoriBearingY(m_Face->GetRec()) -
+                                   FXFT_Get_Glyph_Height(m_Face->GetRec()),
+                               FXFT_Get_Glyph_HoriBearingX(m_Face->GetRec()) +
+                                   FXFT_Get_Glyph_Width(m_Face->GetRec()),
+                               FXFT_Get_Glyph_HoriBearingY(m_Face->GetRec()),
+                               em, em);
 }
 
 bool CFX_Font::IsItalic() const {
@@ -501,6 +526,13 @@
   return m_Face && FXFT_Is_Face_fixedwidth(m_Face->GetRec()) != 0;
 }
 
+#if defined(_SKIA_SUPPORT_)
+bool CFX_Font::IsSubstFontBold() const {
+  CFX_SubstFont* subst_font = GetSubstFont();
+  return subst_font && subst_font->GetOriginalWeight() >= FXFONT_FW_BOLD;
+}
+#endif
+
 ByteString CFX_Font::GetPsName() const {
   if (!m_Face)
     return ByteString();
@@ -537,12 +569,10 @@
   return m_pSubstFont->m_Family;
 }
 
-ByteString CFX_Font::GetBaseFontName(bool restrict_to_psname) const {
+ByteString CFX_Font::GetBaseFontName() const {
   ByteString psname = GetPsName();
-  if (restrict_to_psname || (!psname.IsEmpty() && psname != kUntitledFontName))
+  if (!psname.IsEmpty() && psname != kUntitledFontName)
     return psname;
-  if (!m_Face && !m_pSubstFont)
-    return ByteString();
   if (m_Face) {
     ByteString style = ByteString(FXFT_Get_Face_Style_Name(m_Face->GetRec()));
     ByteString facename = GetFamilyNameOrUntitled();
@@ -552,26 +582,35 @@
       facename += (IsTTFont() ? "," : " ") + style;
     return facename;
   }
-  return m_pSubstFont->m_Family;
+  if (m_pSubstFont)
+    return m_pSubstFont->m_Family;
+  return ByteString();
 }
 
-bool CFX_Font::GetBBox(FX_RECT* pBBox) {
+absl::optional<FX_RECT> CFX_Font::GetRawBBox() const {
   if (!m_Face)
-    return false;
+    return absl::nullopt;
+
+  return FXRectFromFTPos(FXFT_Get_Face_xMin(m_Face->GetRec()),
+                         FXFT_Get_Face_yMin(m_Face->GetRec()),
+                         FXFT_Get_Face_xMax(m_Face->GetRec()),
+                         FXFT_Get_Face_yMax(m_Face->GetRec()));
+}
+
+absl::optional<FX_RECT> CFX_Font::GetBBox() const {
+  absl::optional<FX_RECT> result = GetRawBBox();
+  if (!result.has_value())
+    return result;
 
   int em = FXFT_Get_Face_UnitsPerEM(m_Face->GetRec());
-  if (em == 0) {
-    pBBox->left = FXFT_Get_Face_xMin(m_Face->GetRec());
-    pBBox->bottom = FXFT_Get_Face_yMax(m_Face->GetRec());
-    pBBox->top = FXFT_Get_Face_yMin(m_Face->GetRec());
-    pBBox->right = FXFT_Get_Face_xMax(m_Face->GetRec());
-  } else {
-    pBBox->left = FXFT_Get_Face_xMin(m_Face->GetRec()) * 1000 / em;
-    pBBox->top = FXFT_Get_Face_yMin(m_Face->GetRec()) * 1000 / em;
-    pBBox->right = FXFT_Get_Face_xMax(m_Face->GetRec()) * 1000 / em;
-    pBBox->bottom = FXFT_Get_Face_yMax(m_Face->GetRec()) * 1000 / em;
+  if (em != 0) {
+    FX_RECT& bbox = result.value();
+    bbox.left = (bbox.left * 1000) / em;
+    bbox.top = (bbox.top * 1000) / em;
+    bbox.right = (bbox.right * 1000) / em;
+    bbox.bottom = (bbox.bottom * 1000) / em;
   }
-  return true;
+  return result;
 }
 
 RetainPtr<CFX_GlyphCache> CFX_Font::GetOrCreateGlyphCache() const {
@@ -587,49 +626,49 @@
 void CFX_Font::AdjustMMParams(int glyph_index,
                               int dest_width,
                               int weight) const {
-  ASSERT(dest_width >= 0);
-  FXFT_MM_VarPtr pMasters = nullptr;
-  FT_Get_MM_Var(m_Face->GetRec(), &pMasters);
-  if (!pMasters)
+  DCHECK(dest_width >= 0);
+  ScopedFXFTMMVar variation_desc(m_Face->GetRec());
+  if (!variation_desc) {
     return;
+  }
 
-  long coords[2];
-  if (weight == 0)
-    coords[0] = FXFT_Get_MM_Axis_Def(FXFT_Get_MM_Axis(pMasters, 0)) / 65536;
-  else
+  FT_Pos coords[2];
+  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 {
-    int min_param = FXFT_Get_MM_Axis_Min(FXFT_Get_MM_Axis(pMasters, 1)) / 65536;
-    int max_param = FXFT_Get_MM_Axis_Max(FXFT_Get_MM_Axis(pMasters, 1)) / 65536;
+    FT_Long min_param = 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,
                   FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
-    int min_width = FXFT_Get_Glyph_HoriAdvance(m_Face->GetRec()) * 1000 /
-                    FXFT_Get_Face_UnitsPerEM(m_Face->GetRec());
+    FT_Pos min_width = FXFT_Get_Glyph_HoriAdvance(m_Face->GetRec()) * 1000 /
+                       FXFT_Get_Face_UnitsPerEM(m_Face->GetRec());
     coords[1] = max_param;
     FT_Set_MM_Design_Coordinates(m_Face->GetRec(), 2, coords);
     FT_Load_Glyph(m_Face->GetRec(), glyph_index,
                   FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
-    int max_width = FXFT_Get_Glyph_HoriAdvance(m_Face->GetRec()) * 1000 /
-                    FXFT_Get_Face_UnitsPerEM(m_Face->GetRec());
+    FT_Pos max_width = FXFT_Get_Glyph_HoriAdvance(m_Face->GetRec()) * 1000 /
+                       FXFT_Get_Face_UnitsPerEM(m_Face->GetRec());
     if (max_width == min_width) {
-      FXFT_Free(m_Face->GetRec(), pMasters);
       return;
     }
-    int param = min_param + (max_param - min_param) * (dest_width - min_width) /
-                                (max_width - min_width);
+    FT_Pos param = min_param + (max_param - min_param) *
+                                   (dest_width - min_width) /
+                                   (max_width - min_width);
     coords[1] = param;
   }
-  FXFT_Free(m_Face->GetRec(), pMasters);
   FT_Set_MM_Design_Coordinates(m_Face->GetRec(), 2, coords);
 }
 
-CFX_PathData* CFX_Font::LoadGlyphPathImpl(uint32_t glyph_index,
-                                          uint32_t dest_width) const {
+std::unique_ptr<CFX_Path> CFX_Font::LoadGlyphPathImpl(uint32_t glyph_index,
+                                                      int dest_width) const {
   if (!m_Face)
     return nullptr;
 
@@ -637,21 +676,13 @@
   FT_Matrix ft_matrix = {65536, 0, 0, 65536};
   if (m_pSubstFont) {
     if (m_pSubstFont->m_ItalicAngle) {
-      int skew = m_pSubstFont->m_ItalicAngle;
-      // |skew| is nonpositive so |-skew| is used as the index. We need to make
-      // sure |skew| != INT_MIN since -INT_MIN is undefined.
-      if (skew <= 0 && skew != std::numeric_limits<int>::min() &&
-          static_cast<size_t>(-skew) < kAngleSkewArraySize) {
-        skew = -s_AngleSkew[-skew];
-      } else {
-        skew = -58;
-      }
+      int skew = GetSkewFromAngle(m_pSubstFont->m_ItalicAngle);
       if (m_bVertical)
         ft_matrix.yx += ft_matrix.yy * skew / 100;
       else
         ft_matrix.xy -= ft_matrix.xx * skew / 100;
     }
-    if (m_pSubstFont->m_bFlagMM)
+    if (m_pSubstFont->IsBuiltInGenericFont())
       AdjustMMParams(glyph_index, dest_width, m_pSubstFont->m_Weight);
   }
   ScopedFontTransform scoped_transform(m_Face, &ft_matrix);
@@ -661,15 +692,15 @@
     load_flags |= FT_LOAD_NO_HINTING;
   if (FT_Load_Glyph(m_Face->GetRec(), glyph_index, load_flags))
     return nullptr;
-  if (m_pSubstFont && !m_pSubstFont->m_bFlagMM &&
+  if (m_pSubstFont && !m_pSubstFont->IsBuiltInGenericFont() &&
       m_pSubstFont->m_Weight > 400) {
-    uint32_t index = (m_pSubstFont->m_Weight - 400) / 10;
-    index = std::min(index, static_cast<uint32_t>(kWeightPowArraySize - 1));
-    int level = 0;
-    if (m_pSubstFont->m_Charset == FX_CHARSET_ShiftJIS)
-      level = s_WeightPow_SHIFTJIS[index] * 2 * 65536 / 36655;
+    uint32_t index = std::min<uint32_t>((m_pSubstFont->m_Weight - 400) / 10,
+                                        kWeightPowArraySize - 1);
+    int level;
+    if (m_pSubstFont->m_Charset == FX_Charset::kShiftJIS)
+      level = kWeightPowShiftJis[index] * 65536 / 36655;
     else
-      level = s_WeightPow[index] * 2;
+      level = kWeightPow[index];
     FT_Outline_Embolden(FXFT_Get_Glyph_Outline(m_Face->GetRec()), level);
   }
 
@@ -681,8 +712,8 @@
   funcs.shift = 0;
   funcs.delta = 0;
 
+  auto pPath = std::make_unique<CFX_Path>();
   OUTLINE_PARAMS params;
-  auto pPath = pdfium::MakeUnique<CFX_PathData>();
   params.m_pPath = pPath.get();
   params.m_CurX = params.m_CurY = 0;
   params.m_CoordUnit = 64 * 64.0;
@@ -694,27 +725,48 @@
 
   Outline_CheckEmptyContour(&params);
   pPath->ClosePath();
-
-  return pPath.release();
+  return pPath;
 }
 
-const CFX_GlyphBitmap* CFX_Font::LoadGlyphBitmap(uint32_t glyph_index,
-                                                 bool bFontStyle,
-                                                 const CFX_Matrix& matrix,
-                                                 uint32_t dest_width,
-                                                 int anti_alias,
-                                                 int* pTextFlags) const {
+const CFX_GlyphBitmap* CFX_Font::LoadGlyphBitmap(
+    uint32_t glyph_index,
+    bool bFontStyle,
+    const CFX_Matrix& matrix,
+    int dest_width,
+    int anti_alias,
+    CFX_TextRenderOptions* text_options) const {
   return GetOrCreateGlyphCache()->LoadGlyphBitmap(this, glyph_index, bFontStyle,
                                                   matrix, dest_width,
-                                                  anti_alias, pTextFlags);
+                                                  anti_alias, text_options);
 }
 
-const CFX_PathData* CFX_Font::LoadGlyphPath(uint32_t glyph_index,
-                                            uint32_t dest_width) const {
+const CFX_Path* CFX_Font::LoadGlyphPath(uint32_t glyph_index,
+                                        int dest_width) const {
   return GetOrCreateGlyphCache()->LoadGlyphPath(this, glyph_index, dest_width);
 }
 
-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
+// static
+int CFX_Font::GetWeightLevel(FX_Charset charset, size_t index) {
+  if (index >= kWeightPowArraySize)
+    return -1;
+
+  if (charset == FX_Charset::kShiftJIS)
+    return kWeightPowShiftJis[index];
+  return kWeightPow11[index];
+}
+
+// static
+int CFX_Font::GetSkewFromAngle(int angle) {
+  // |angle| is non-positive so |-angle| is used as the index. Need to make sure
+  // |angle| != INT_MIN since -INT_MIN is undefined.
+  if (angle > 0 || angle == std::numeric_limits<int>::min() ||
+      static_cast<size_t>(-angle) >= std::size(kAngleSkew)) {
+    return -58;
+  }
+  return kAngleSkew[-angle];
+}
+
+#if defined(_SKIA_SUPPORT_)
 CFX_TypeFace* CFX_Font::GetDeviceCache() const {
   return GetOrCreateGlyphCache()->GetDeviceCache(this);
 }
diff --git a/core/fxge/cfx_font.h b/core/fxge/cfx_font.h
index ead60f7..d1cc83d 100644
--- a/core/fxge/cfx_font.h
+++ b/core/fxge/cfx_font.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,50 +7,80 @@
 #ifndef CORE_FXGE_CFX_FONT_H_
 #define CORE_FXGE_CFX_FONT_H_
 
+#include <stdint.h>
+
 #include <memory>
-#include <vector>
 
 #include "build/build_config.h"
 #include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_codepage_forward.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 #include "core/fxge/cfx_face.h"
-#include "core/fxge/fx_freetype.h"
-#include "third_party/base/span.h"
+#include "core/fxge/freetype/fx_freetype.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/containers/span.h"
 
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
+#if defined(_SKIA_SUPPORT_)
 #include "core/fxge/fx_font.h"
 #endif
 
-class CFX_GlyphCache;
 class CFX_GlyphBitmap;
-class CFX_PathData;
+class CFX_GlyphCache;
+class CFX_Path;
 class CFX_SubstFont;
 class IFX_SeekableReadStream;
+struct CFX_TextRenderOptions;
 
 class CFX_Font {
  public:
-  CFX_Font();
-  ~CFX_Font();
+  // This struct should be the same as FPDF_CharsetFontMap.
+  struct CharsetFontMap {
+    int charset;           // Character Set Enum value, see FX_Charset::kXXX.
+    const char* fontname;  // Name of default font to use with that charset.
+  };
+
+  enum class FontType {
+    kUnknown,
+    kCIDTrueType,
+  };
+
+  // Pointer to the default character set to TT Font name map. The map is an
+  // array of CharsetFontMap structs, with its end indicated by a {-1, nullptr}
+  // entry.
+  static const CharsetFontMap kDefaultTTFMap[];
 
   // Used when the font name is empty.
   static const char kUntitledFontName[];
 
   static const char kDefaultAnsiFontName[];
   static const char kUniversalDefaultFontName[];
-  static ByteString GetDefaultFontNameByCharset(uint8_t nCharset);
-  static uint8_t GetCharSetFromUnicode(uint16_t word);
+
+  // Returns negative values on failure.
+  static int GetWeightLevel(FX_Charset charset, size_t index);
+
+  // |angle| is typically negative.
+  static int GetSkewFromAngle(int angle);
+
+  static ByteString GetDefaultFontNameByCharset(FX_Charset nCharset);
+  static FX_Charset GetCharSetFromUnicode(uint16_t word);
+
+  CFX_Font();
+  ~CFX_Font();
 
   void LoadSubst(const ByteString& face_name,
                  bool bTrueType,
                  uint32_t flags,
                  int weight,
                  int italic_angle,
-                 int CharsetCP,
+                 FX_CodePage code_page,
                  bool bVertical);
 
   bool LoadEmbedded(pdfium::span<const uint8_t> src_span,
-                    bool bForceAsVertical);
+                    bool force_vertical,
+                    uint64_t object_tag);
   RetainPtr<CFX_Face> GetFace() const { return m_Face; }
   FXFT_FaceRec* GetFaceRec() const {
     return m_Face ? m_Face->GetRec() : nullptr;
@@ -59,32 +89,28 @@
   int GetSubstFontItalicAngle() const;
 
 #if defined(PDF_ENABLE_XFA)
-  bool LoadFile(const RetainPtr<IFX_SeekableReadStream>& pFile, int nFaceIndex);
+  bool LoadFile(RetainPtr<IFX_SeekableReadStream> pFile, int nFaceIndex);
 
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
   void SetFace(RetainPtr<CFX_Face> face);
   void SetFontSpan(pdfium::span<uint8_t> pSpan) { m_FontData = pSpan; }
   void SetSubstFont(std::unique_ptr<CFX_SubstFont> subst);
-#endif  // !defined(OS_WIN)
+#endif  // !BUILDFLAG(IS_WIN)
 #endif  // defined(PDF_ENABLE_XFA)
 
-  const CFX_GlyphBitmap* LoadGlyphBitmap(uint32_t glyph_index,
-                                         bool bFontStyle,
-                                         const CFX_Matrix& matrix,
-                                         uint32_t dest_width,
-                                         int anti_alias,
-                                         int* pTextFlags) const;
-  const CFX_PathData* LoadGlyphPath(uint32_t glyph_index,
-                                    uint32_t dest_width) const;
-
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
-  CFX_TypeFace* GetDeviceCache() const;
-#endif
-
-  uint32_t GetGlyphWidth(uint32_t glyph_index);
+  const CFX_GlyphBitmap* LoadGlyphBitmap(
+      uint32_t glyph_index,
+      bool bFontStyle,
+      const CFX_Matrix& matrix,
+      int dest_width,
+      int anti_alias,
+      CFX_TextRenderOptions* text_options) const;
+  const CFX_Path* LoadGlyphPath(uint32_t glyph_index, int dest_width) const;
+  int GetGlyphWidth(uint32_t glyph_index) const;
+  int GetGlyphWidth(uint32_t glyph_index, int dest_width, int weight) const;
   int GetAscent() const;
   int GetDescent() const;
-  bool GetGlyphBBox(uint32_t glyph_index, FX_RECT* pBBox);
+  absl::optional<FX_RECT> GetGlyphBBox(uint32_t glyph_index);
   bool IsItalic() const;
   bool IsBold() const;
   bool IsFixedWidth() const;
@@ -92,62 +118,60 @@
   ByteString GetPsName() const;
   ByteString GetFamilyName() const;
   ByteString GetFaceName() const;
-  ByteString GetBaseFontName(bool restrict_to_psname) const;
+  ByteString GetBaseFontName() const;
   bool IsTTFont() const;
-  bool GetBBox(FX_RECT* pBBox);
+
+  // Raw bounding box.
+  absl::optional<FX_RECT> GetRawBBox() const;
+
+  // Bounding box adjusted for font units.
+  absl::optional<FX_RECT> GetBBox() const;
+
   bool IsEmbedded() const { return m_bEmbedded; }
-  uint8_t* GetSubData() const { return m_pGsubData.get(); }
-  void SetSubData(uint8_t* data) { m_pGsubData.reset(data); }
+  FontType GetFontType() const { return m_FontType; }
+  void SetFontType(FontType type) { m_FontType = type; }
+  uint64_t GetObjectTag() const { return m_ObjectTag; }
   pdfium::span<uint8_t> GetFontSpan() const { return m_FontData; }
   void AdjustMMParams(int glyph_index, int dest_width, int weight) const;
-  CFX_PathData* LoadGlyphPathImpl(uint32_t glyph_index,
-                                  uint32_t dest_width) const;
-#if defined(OS_MACOSX)
+  std::unique_ptr<CFX_Path> LoadGlyphPathImpl(uint32_t glyph_index,
+                                              int dest_width) const;
+  int GetGlyphWidthImpl(uint32_t glyph_index, int dest_width, int weight) const;
+
+#if defined(_SKIA_SUPPORT_)
+  CFX_TypeFace* GetDeviceCache() const;
+  bool IsSubstFontBold() const;
+#endif
+
+#if BUILDFLAG(IS_APPLE)
   void* GetPlatformFont() const { return m_pPlatformFont; }
   void SetPlatformFont(void* font) { m_pPlatformFont = font; }
 #endif
 
-  static const size_t kAngleSkewArraySize = 30;
-  static const char s_AngleSkew[kAngleSkewArraySize];
-  static const size_t kWeightPowArraySize = 100;
-  static const uint8_t s_WeightPow[kWeightPowArraySize];
-  static const uint8_t s_WeightPow_11[kWeightPowArraySize];
-  static const uint8_t s_WeightPow_SHIFTJIS[kWeightPowArraySize];
-
-  // This struct should be the same as FPDF_CharsetFontMap.
-  struct CharsetFontMap {
-    int charset;           // Character Set Enum value, see FX_CHARSET_XXX.
-    const char* fontname;  // Name of default font to use with that charset.
-  };
-
-  /**
-   *    Pointer to the default character set to TT Font name map. The
-   *    map is an array of CharsetFontMap structs, with its end indicated
-   *    by a { -1, NULL } entry.
-   **/
-  static const CharsetFontMap defaultTTFMap[];
-
  private:
   RetainPtr<CFX_GlyphCache> GetOrCreateGlyphCache() const;
   void ClearGlyphCache();
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
   void ReleasePlatformResource();
 #endif
   ByteString GetFamilyNameOrUntitled() const;
 
 #if defined(PDF_ENABLE_XFA)
-  std::unique_ptr<FXFT_StreamRec> m_pOwnedStream;  // Must outlive |m_Face|.
+  // |m_pOwnedFile| must outlive |m_pOwnedStreamRec|.
+  RetainPtr<IFX_SeekableReadStream> m_pOwnedFile;
+  std::unique_ptr<FXFT_StreamRec> m_pOwnedStreamRec;  // Must outlive |m_Face|.
 #endif
+
   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_pGsubData;
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_FontDataAllocation;
+  DataVector<uint8_t> m_FontDataAllocation;
   pdfium::span<uint8_t> m_FontData;
+  FontType m_FontType = FontType::kUnknown;
+  uint64_t m_ObjectTag = 0;
   bool m_bEmbedded = false;
   bool m_bVertical = false;
-#if defined(OS_MACOSX)
-  void* m_pPlatformFont = nullptr;
+#if BUILDFLAG(IS_APPLE)
+  UNOWNED_PTR_EXCLUSION void* m_pPlatformFont = nullptr;
 #endif
 };
 
diff --git a/core/fxge/cfx_fontcache.cpp b/core/fxge/cfx_fontcache.cpp
index 7289026..5ce96c9 100644
--- a/core/fxge/cfx_fontcache.cpp
+++ b/core/fxge/cfx_fontcache.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,14 +6,10 @@
 
 #include "core/fxge/cfx_fontcache.h"
 
-#include <memory>
-#include <utility>
-
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_glyphcache.h"
+#include "core/fxge/freetype/fx_freetype.h"
 #include "core/fxge/fx_font.h"
-#include "core/fxge/fx_freetype.h"
-#include "third_party/base/ptr_util.h"
 
 CFX_FontCache::CFX_FontCache() = default;
 
@@ -32,7 +28,7 @@
   return new_cache;
 }
 
-#ifdef _SKIA_SUPPORT_
+#if defined(_SKIA_SUPPORT_)
 CFX_TypeFace* CFX_FontCache::GetDeviceCache(const CFX_Font* pFont) {
   return GetGlyphCache(pFont)->GetDeviceCache(pFont);
 }
diff --git a/core/fxge/cfx_fontcache.h b/core/fxge/cfx_fontcache.h
index 515811b..9b7a426 100644
--- a/core/fxge/cfx_fontcache.h
+++ b/core/fxge/cfx_fontcache.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,11 +8,11 @@
 #define CORE_FXGE_CFX_FONTCACHE_H_
 
 #include <map>
-#include <memory>
 
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/cfx_glyphcache.h"
-#include "core/fxge/fx_freetype.h"
+#include "core/fxge/freetype/fx_freetype.h"
 
 class CFX_Font;
 
@@ -22,7 +22,7 @@
   ~CFX_FontCache();
 
   RetainPtr<CFX_GlyphCache> GetGlyphCache(const CFX_Font* pFont);
-#ifdef _SKIA_SUPPORT_
+#if defined(_SKIA_SUPPORT_)
   CFX_TypeFace* GetDeviceCache(const CFX_Font* pFont);
 #endif
 
diff --git a/core/fxge/cfx_fontmapper.cpp b/core/fxge/cfx_fontmapper.cpp
index 3b0bcae..63173b6 100644
--- a/core/fxge/cfx_fontmapper.cpp
+++ b/core/fxge/cfx_fontmapper.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,29 +6,33 @@
 
 #include "core/fxge/cfx_fontmapper.h"
 
+#include <stdint.h>
+
 #include <algorithm>
 #include <memory>
-#include <sstream>
-#include <tuple>
 #include <utility>
-#include <vector>
 
 #include "build/build_config.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/stl_util.h"
+#include "core/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/stl_util.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/contains.h"
 
 namespace {
 
-const int kNumStandardFonts = 14;
-static_assert(CFX_FontMapper::kDingbats + 1 == kNumStandardFonts,
+static_assert(CFX_FontMapper::kLast + 1 == CFX_FontMapper::kNumStandardFonts,
               "StandardFont enum count mismatch");
 
-const char* const g_Base14FontNames[kNumStandardFonts] = {
+const char* const kBase14FontNames[CFX_FontMapper::kNumStandardFonts] = {
     "Courier",
     "Courier-Bold",
     "Courier-BoldOblique",
@@ -50,7 +54,7 @@
   CFX_FontMapper::StandardFont m_Index;
 };
 
-const AltFontName g_AltFontNames[] = {
+constexpr AltFontName kAltFontNames[] = {
     {"Arial", CFX_FontMapper::kHelvetica},
     {"Arial,Bold", CFX_FontMapper::kHelveticaBold},
     {"Arial,BoldItalic", CFX_FontMapper::kHelveticaBoldOblique},
@@ -147,22 +151,21 @@
   const char* m_pFontFamily;  // Raw, POD struct.
 };
 
-const AltFontFamily g_AltFontFamilies[] = {
+constexpr AltFontFamily kAltFontFamilies[] = {
     {"AGaramondPro", "Adobe Garamond Pro"},
     {"BankGothicBT-Medium", "BankGothic Md BT"},
     {"ForteMT", "Forte"},
 };
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || defined(OS_ASMJS)
 const char kNarrowFamily[] = "LiberationSansNarrow";
-#elif defined(OS_ANDROID)
+#elif BUILDFLAG(IS_ANDROID)
 const char kNarrowFamily[] = "RobotoCondensed";
 #else
 const char kNarrowFamily[] = "ArialNarrow";
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || defined(OS_ASMJS)
 
-ByteString TT_NormalizeName(const char* family) {
-  ByteString norm(family);
+ByteString TT_NormalizeName(ByteString norm) {
   norm.Remove(' ');
   norm.Remove('-');
   norm.Remove(',');
@@ -185,7 +188,7 @@
       *fontName = "FreeStyleScript";
     return;
   }
-  for (const auto& alternate : g_AltFontFamilies) {
+  for (const auto& alternate : kAltFontFamilies) {
     if (fontName->Contains(alternate.m_pFontName)) {
       *fontName = alternate.m_pFontFamily;
       return;
@@ -193,69 +196,203 @@
   }
 }
 
-ByteString ParseStyle(const char* pStyle, int iLen, int iIndex) {
-  std::ostringstream buf;
-  if (!iLen || iLen <= iIndex)
-    return ByteString(buf);
-  while (iIndex < iLen) {
-    if (pStyle[iIndex] == ',')
-      break;
-    buf << pStyle[iIndex];
-    ++iIndex;
-  }
-  return ByteString(buf);
+ByteString ParseStyle(const ByteString& bsStyle, size_t iStart) {
+  ByteStringView bsRegion = bsStyle.AsStringView().Substr(iStart);
+  size_t iIndex = bsRegion.Find(',').value_or(bsRegion.GetLength());
+  return ByteString(bsRegion.First(iIndex));
 }
 
-const struct FX_FontStyle {
+struct FX_FontStyle {
   const char* name;
   size_t len;
   uint32_t style;
-} g_FontStyles[] = {
-    {"Bold", 4, FXFONT_FORCE_BOLD},
-    {"Italic", 6, FXFONT_ITALIC},
-    {"BoldItalic", 10, FXFONT_FORCE_BOLD | FXFONT_ITALIC},
-    {"Reg", 3, FXFONT_NORMAL},
-    {"Regular", 7, FXFONT_NORMAL},
 };
 
-// <exists, index, length>
-std::tuple<bool, uint32_t, size_t> GetStyleType(const ByteString& bsStyle,
-                                                bool bReverse) {
-  if (bsStyle.IsEmpty())
-    return std::make_tuple(false, FXFONT_NORMAL, 0);
+constexpr FX_FontStyle kFontStyles[] = {
+    {"Regular", 7, FXFONT_NORMAL},
+    {"Reg", 3, FXFONT_NORMAL},
+    {"BoldItalic", 10, FXFONT_FORCE_BOLD | FXFONT_ITALIC},
+    {"Italic", 6, FXFONT_ITALIC},
+    {"Bold", 4, FXFONT_FORCE_BOLD},
+};
 
-  for (int i = FX_ArraySize(g_FontStyles) - 1; i >= 0; --i) {
-    const FX_FontStyle* pStyle = g_FontStyles + i;
-    if (!pStyle || pStyle->len > bsStyle.GetLength())
+const FX_FontStyle* GetStyleType(ByteStringView font_name,
+                                 bool reverse_search) {
+  if (font_name.IsEmpty())
+    return nullptr;
+
+  for (const FX_FontStyle& style : kFontStyles) {
+    if (style.len > font_name.GetLength())
       continue;
 
-    if (bReverse) {
-      if (bsStyle.Last(pStyle->len).Compare(pStyle->name) == 0)
-        return std::make_tuple(true, pStyle->style, pStyle->len);
-    } else {
-      if (bsStyle.First(pStyle->len).Compare(pStyle->name) == 0)
-        return std::make_tuple(true, pStyle->style, pStyle->len);
-    }
+    ByteStringView style_view =
+        reverse_search ? font_name.Last(style.len) : font_name.First(style.len);
+    if (style_view == style.name)
+      return &style;
   }
-  return std::make_tuple(false, FXFONT_NORMAL, 0);
+  return nullptr;
 }
 
-bool CheckSupportThirdPartFont(const ByteString& name, int* PitchFamily) {
+bool ParseStyles(const ByteString& style_str,
+                 bool* is_style_available,
+                 int* weight,
+                 uint32_t* style) {
+  if (style_str.IsEmpty())
+    return false;
+
+  size_t i = 0;
+  bool is_first_item = true;
+  while (i < style_str.GetLength()) {
+    ByteString buf = ParseStyle(style_str, i);
+    const FX_FontStyle* style_result =
+        GetStyleType(buf.AsStringView(), /*reverse_search=*/false);
+    if ((i && !*is_style_available) || (!i && !style_result))
+      return true;
+
+    uint32_t parsed_style;
+    if (style_result) {
+      *is_style_available = true;
+      parsed_style = style_result->style;
+    } else {
+      parsed_style = FXFONT_NORMAL;
+    }
+
+    if (FontStyleIsForceBold(parsed_style)) {
+      // If we're already bold, then we're double bold, use special weight.
+      if (FontStyleIsForceBold(*style)) {
+        *weight = FXFONT_FW_BOLD_BOLD;
+      } else {
+        *weight = FXFONT_FW_BOLD;
+        *style |= FXFONT_FORCE_BOLD;
+      }
+
+      is_first_item = false;
+    }
+    if (FontStyleIsItalic(parsed_style) && FontStyleIsForceBold(parsed_style)) {
+      *style |= FXFONT_ITALIC;
+    } else if (FontStyleIsItalic(parsed_style)) {
+      if (!is_first_item)
+        return true;
+
+      *style |= FXFONT_ITALIC;
+      break;
+    }
+    i += buf.GetLength() + 1;
+  }
+  return false;
+}
+
+bool CheckSupportThirdPartFont(const ByteString& name, int* pitch_family) {
   if (name != "MyriadPro")
     return false;
-  *PitchFamily &= ~FXFONT_FF_ROMAN;
+  *pitch_family &= ~FXFONT_FF_ROMAN;
   return true;
 }
 
-void UpdatePitchFamily(uint32_t flags, int* PitchFamily) {
-  if (FontStyleIsSerif(flags))
-    *PitchFamily |= FXFONT_FF_ROMAN;
-  if (FontStyleIsScript(flags))
-    *PitchFamily |= FXFONT_FF_SCRIPT;
-  if (FontStyleIsFixedPitch(flags))
-    *PitchFamily |= FXFONT_FF_FIXEDPITCH;
+uint32_t GetStyleFromBaseFont(int base_font) {
+  int pos = base_font % 4;
+  uint32_t style = FXFONT_NORMAL;
+  if (pos == 1 || pos == 2)
+    style |= FXFONT_FORCE_BOLD;
+  if (pos / 2)
+    style |= FXFONT_ITALIC;
+  return style;
 }
 
+int GetPitchFamilyFromBaseFont(int base_font) {
+  if (base_font < 4)
+    return FXFONT_FF_FIXEDPITCH;
+  if (base_font >= 8)
+    return FXFONT_FF_ROMAN;
+  return 0;
+}
+
+int GetPitchFamilyFromFlags(uint32_t flags) {
+  int pitch_family = 0;
+  if (FontStyleIsSerif(flags))
+    pitch_family |= FXFONT_FF_ROMAN;
+  if (FontStyleIsScript(flags))
+    pitch_family |= FXFONT_FF_SCRIPT;
+  if (FontStyleIsFixedPitch(flags))
+    pitch_family |= FXFONT_FF_FIXEDPITCH;
+  return pitch_family;
+}
+
+int AdjustBaseFontForStyle(int base_font, uint32_t style) {
+  if (!style || (base_font % 4))
+    return base_font;
+
+  if (FontStyleIsForceBold(style) && FontStyleIsItalic(style))
+    base_font += 2;
+  else if (FontStyleIsForceBold(style))
+    base_font += 1;
+  else if (FontStyleIsItalic(style))
+    base_font += 3;
+  return base_font;
+}
+
+FX_Charset GetCharset(FX_CodePage code_page, int base_font, uint32_t flags) {
+  if (code_page != FX_CodePage::kDefANSI)
+    return FX_GetCharsetFromCodePage(code_page);
+  if (FontStyleIsSymbolic(flags) &&
+      base_font == CFX_FontMapper::kNumStandardFonts) {
+    return FX_Charset::kSymbol;
+  }
+  return FX_Charset::kANSI;
+}
+
+bool IsStrUpper(const ByteString& str) {
+  for (size_t i = 0; i < str.GetLength(); ++i) {
+    if (!FXSYS_IsUpperASCII(str[i]))
+      return false;
+  }
+  return true;
+}
+
+void RemoveSubsettedFontPrefix(ByteString* subst_name) {
+  constexpr size_t kPrefixLength = 6;
+  if (subst_name->GetLength() > kPrefixLength &&
+      (*subst_name)[kPrefixLength] == '+' &&
+      IsStrUpper(subst_name->First(kPrefixLength))) {
+    *subst_name =
+        subst_name->Last(subst_name->GetLength() - (kPrefixLength + 1));
+  }
+}
+
+ByteString GetSubstName(const ByteString& name, bool is_truetype) {
+  ByteString subst_name = name;
+  if (is_truetype && name.Front() == '@')
+    subst_name.Delete(0);
+  else
+    subst_name.Remove(' ');
+  RemoveSubsettedFontPrefix(&subst_name);
+  CFX_FontMapper::GetStandardFontName(&subst_name);
+  return subst_name;
+}
+
+bool IsNarrowFontName(const ByteString& name) {
+  static const char kNarrowFonts[][10] = {"Narrow", "Condensed"};
+  for (const char* font : kNarrowFonts) {
+    absl::optional<size_t> pos = name.Find(font);
+    if (pos.has_value() && pos.value() != 0)
+      return true;
+  }
+  return false;
+}
+
+class ScopedFontDeleter {
+ public:
+  FX_STACK_ALLOCATED();
+
+  ScopedFontDeleter(SystemFontInfoIface* font_info, void* font)
+      : font_info_(font_info), font_(font) {}
+  ~ScopedFontDeleter() { font_info_->DeleteFont(font_); }
+
+ private:
+  UnownedPtr<SystemFontInfoIface> const font_info_;
+  UNOWNED_PTR_EXCLUSION void* const font_;  // void type incompatible.
+};
+
 }  // namespace
 
 CFX_FontMapper::CFX_FontMapper(CFX_FontMgr* mgr) : m_pFontMgr(mgr) {}
@@ -267,13 +404,19 @@
   if (!pFontInfo)
     return;
 
+  m_bListLoaded = false;
   m_pFontInfo = std::move(pFontInfo);
 }
 
-uint32_t CFX_FontMapper::GetChecksumFromTT(void* hFont) {
+std::unique_ptr<SystemFontInfoIface> CFX_FontMapper::TakeSystemFontInfo() {
+  return std::move(m_pFontInfo);
+}
+
+uint32_t CFX_FontMapper::GetChecksumFromTT(void* font_handle) {
   uint32_t buffer[256];
   m_pFontInfo->GetFontData(
-      hFont, kTableTTCF, pdfium::as_writable_bytes(pdfium::make_span(buffer)));
+      font_handle, kTableTTCF,
+      pdfium::as_writable_bytes(pdfium::make_span(buffer)));
 
   uint32_t checksum = 0;
   for (auto x : buffer)
@@ -282,17 +425,18 @@
   return checksum;
 }
 
-ByteString CFX_FontMapper::GetPSNameFromTT(void* hFont) {
-  uint32_t size = m_pFontInfo->GetFontData(hFont, kTableNAME, {});
+ByteString CFX_FontMapper::GetPSNameFromTT(void* font_handle) {
+  size_t size = m_pFontInfo->GetFontData(font_handle, kTableNAME, {});
   if (!size)
     return ByteString();
 
-  std::vector<uint8_t> buffer(size);
-  uint32_t bytes_read = m_pFontInfo->GetFontData(hFont, kTableNAME, buffer);
+  DataVector<uint8_t> buffer(size);
+  size_t bytes_read = m_pFontInfo->GetFontData(font_handle, kTableNAME, buffer);
   return bytes_read == size ? GetNameFromTT(buffer, 6) : ByteString();
 }
 
-void CFX_FontMapper::AddInstalledFont(const ByteString& name, int charset) {
+void CFX_FontMapper::AddInstalledFont(const ByteString& name,
+                                      FX_Charset charset) {
   if (!m_pFontInfo)
     return;
 
@@ -300,22 +444,23 @@
   if (name == m_LastFamily)
     return;
 
-  bool bLocalized = std::any_of(name.begin(), name.end(), [](const char& c) {
+  bool is_localized = std::any_of(name.begin(), name.end(), [](const char& c) {
     return static_cast<uint8_t>(c) > 0x80;
   });
 
-  if (bLocalized) {
-    void* hFont = m_pFontInfo->GetFont(name.c_str());
-    if (!hFont) {
-      hFont = m_pFontInfo->MapFont(0, 0, FX_CHARSET_Default, 0, name.c_str());
-      if (!hFont)
+  if (is_localized) {
+    void* font_handle = m_pFontInfo->GetFont(name);
+    if (!font_handle) {
+      font_handle =
+          m_pFontInfo->MapFont(0, false, FX_Charset::kDefault, 0, name);
+      if (!font_handle)
         return;
     }
 
-    ByteString new_name = GetPSNameFromTT(hFont);
+    ScopedFontDeleter scoped_font(m_pFontInfo.get(), font_handle);
+    ByteString new_name = GetPSNameFromTT(font_handle);
     if (!new_name.IsEmpty())
       m_LocalizedTTFonts.push_back(std::make_pair(new_name, name));
-    m_pFontInfo->DeleteFont(hFont);
   }
   m_InstalledTTFonts.push_back(name);
   m_LastFamily = name;
@@ -332,63 +477,100 @@
 ByteString CFX_FontMapper::MatchInstalledFonts(const ByteString& norm_name) {
   LoadInstalledFonts();
   int i;
-  for (i = pdfium::CollectionSize<int>(m_InstalledTTFonts) - 1; i >= 0; i--) {
-    ByteString norm1 = TT_NormalizeName(m_InstalledTTFonts[i].c_str());
-    if (norm1 == norm_name)
+  for (i = fxcrt::CollectionSize<int>(m_InstalledTTFonts) - 1; i >= 0; i--) {
+    if (TT_NormalizeName(m_InstalledTTFonts[i]) == norm_name)
       return m_InstalledTTFonts[i];
   }
-  for (i = pdfium::CollectionSize<int>(m_LocalizedTTFonts) - 1; i >= 0; i--) {
-    ByteString norm1 = TT_NormalizeName(m_LocalizedTTFonts[i].first.c_str());
-    if (norm1 == norm_name)
+  for (i = fxcrt::CollectionSize<int>(m_LocalizedTTFonts) - 1; i >= 0; i--) {
+    if (TT_NormalizeName(m_LocalizedTTFonts[i].first) == norm_name)
       return m_LocalizedTTFonts[i].second;
   }
   return ByteString();
 }
 
-RetainPtr<CFX_Face> CFX_FontMapper::UseInternalSubst(CFX_SubstFont* pSubstFont,
-                                                     int iBaseFont,
-                                                     int italic_angle,
-                                                     int weight,
-                                                     int pitch_family) {
-  if (iBaseFont < kNumStandardFonts) {
-    if (m_FoxitFaces[iBaseFont])
-      return m_FoxitFaces[iBaseFont];
-    Optional<pdfium::span<const uint8_t>> font_data =
-        m_pFontMgr->GetBuiltinFont(iBaseFont);
-    if (font_data.has_value()) {
-      m_FoxitFaces[iBaseFont] =
-          m_pFontMgr->NewFixedFace(nullptr, font_data.value(), 0);
-      return m_FoxitFaces[iBaseFont];
+RetainPtr<CFX_Face> CFX_FontMapper::UseInternalSubst(
+    int base_font,
+    int weight,
+    int italic_angle,
+    int pitch_family,
+    CFX_SubstFont* subst_font) {
+  if (base_font < kNumStandardFonts) {
+    if (!m_StandardFaces[base_font]) {
+      m_StandardFaces[base_font] = m_pFontMgr->NewFixedFace(
+          nullptr, m_pFontMgr->GetStandardFont(base_font), 0);
     }
+    return m_StandardFaces[base_font];
   }
-  pSubstFont->m_bFlagMM = true;
-  pSubstFont->m_ItalicAngle = italic_angle;
+
+  subst_font->SetIsBuiltInGenericFont();
+  subst_font->m_ItalicAngle = italic_angle;
   if (weight)
-    pSubstFont->m_Weight = weight;
+    subst_font->m_Weight = weight;
   if (FontFamilyIsRoman(pitch_family)) {
-    pSubstFont->m_Weight = pSubstFont->m_Weight * 4 / 5;
-    pSubstFont->m_Family = "Chrome Serif";
-    if (!m_MMFaces[1]) {
-      m_MMFaces[1] = m_pFontMgr->NewFixedFace(
-          nullptr, m_pFontMgr->GetBuiltinFont(14).value(), 0);
+    subst_font->UseChromeSerif();
+    if (!m_GenericSerifFace) {
+      m_GenericSerifFace = m_pFontMgr->NewFixedFace(
+          nullptr, m_pFontMgr->GetGenericSerifFont(), 0);
     }
-    return m_MMFaces[1];
+    return m_GenericSerifFace;
   }
-  pSubstFont->m_Family = "Chrome Sans";
-  if (!m_MMFaces[0]) {
-    m_MMFaces[0] = m_pFontMgr->NewFixedFace(
-        nullptr, m_pFontMgr->GetBuiltinFont(15).value(), 0);
+  subst_font->m_Family = "Chrome Sans";
+  if (!m_GenericSansFace) {
+    m_GenericSansFace =
+        m_pFontMgr->NewFixedFace(nullptr, m_pFontMgr->GetGenericSansFont(), 0);
   }
-  return m_MMFaces[0];
+  return m_GenericSansFace;
+}
+
+RetainPtr<CFX_Face> CFX_FontMapper::UseExternalSubst(
+    void* font_handle,
+    ByteString face_name,
+    int weight,
+    bool is_italic,
+    int italic_angle,
+    FX_Charset charset,
+    CFX_SubstFont* subst_font) {
+  DCHECK(font_handle);
+
+  ScopedFontDeleter scoped_font(m_pFontInfo.get(), font_handle);
+  m_pFontInfo->GetFaceName(font_handle, &face_name);
+  if (charset == FX_Charset::kDefault)
+    m_pFontInfo->GetFontCharset(font_handle, &charset);
+  size_t ttc_size = m_pFontInfo->GetFontData(font_handle, kTableTTCF, {});
+  size_t font_size = m_pFontInfo->GetFontData(font_handle, 0, {});
+  if (font_size == 0 && ttc_size == 0)
+    return nullptr;
+
+  RetainPtr<CFX_Face> face =
+      ttc_size
+          ? GetCachedTTCFace(font_handle, ttc_size, font_size)
+          : GetCachedFace(font_handle, face_name, weight, is_italic, font_size);
+  if (!face)
+    return nullptr;
+
+  subst_font->m_Family = face_name;
+  subst_font->m_Charset = charset;
+  int face_weight =
+      FXFT_Is_Face_Bold(face->GetRec()) ? FXFONT_FW_BOLD : FXFONT_FW_NORMAL;
+  if (weight != face_weight)
+    subst_font->m_Weight = weight;
+  if (is_italic && !FXFT_Is_Face_Italic(face->GetRec())) {
+    if (italic_angle == 0)
+      italic_angle = -12;
+    else if (abs(italic_angle) < 5)
+      italic_angle = 0;
+    subst_font->m_ItalicAngle = italic_angle;
+  }
+  return face;
 }
 
 RetainPtr<CFX_Face> CFX_FontMapper::FindSubstFont(const ByteString& name,
-                                                  bool bTrueType,
+                                                  bool is_truetype,
                                                   uint32_t flags,
                                                   int weight,
                                                   int italic_angle,
-                                                  int CharsetCP,
-                                                  CFX_SubstFont* pSubstFont) {
+                                                  FX_CodePage code_page,
+                                                  CFX_SubstFont* subst_font) {
   if (weight == 0)
     weight = FXFONT_FW_NORMAL;
 
@@ -396,165 +578,104 @@
     weight = FXFONT_FW_NORMAL;
     italic_angle = 0;
   }
-  ByteString SubstName = name;
-  SubstName.Remove(' ');
-  if (bTrueType && name.GetLength() > 0 && name[0] == '@')
-    SubstName = name.Last(name.GetLength() - 1);
-  GetStandardFontName(&SubstName);
-  if (SubstName == "Symbol" && !bTrueType) {
-    pSubstFont->m_Family = "Chrome Symbol";
-    pSubstFont->m_Charset = FX_CHARSET_Symbol;
-    return UseInternalSubst(pSubstFont, 12, italic_angle, weight, 0);
+  const ByteString subst_name = GetSubstName(name, is_truetype);
+  if (subst_name == "Symbol" && !is_truetype) {
+    subst_font->m_Family = "Chrome Symbol";
+    subst_font->m_Charset = FX_Charset::kSymbol;
+    return UseInternalSubst(kSymbol, weight, italic_angle, 0, subst_font);
   }
-  if (SubstName == "ZapfDingbats") {
-    pSubstFont->m_Family = "Chrome Dingbats";
-    pSubstFont->m_Charset = FX_CHARSET_Symbol;
-    return UseInternalSubst(pSubstFont, 13, italic_angle, weight, 0);
+  if (subst_name == "ZapfDingbats") {
+    subst_font->m_Family = "Chrome Dingbats";
+    subst_font->m_Charset = FX_Charset::kSymbol;
+    return UseInternalSubst(kDingbats, weight, italic_angle, 0, subst_font);
   }
-  int iBaseFont = 0;
+  int base_font = 0;
   ByteString family;
   ByteString style;
-  bool bHasComma = false;
-  bool bHasHyphen = false;
+  bool has_comma = false;
+  bool has_hyphen = false;
   {
-    Optional<size_t> pos = SubstName.Find(",", 0);
+    absl::optional<size_t> pos = subst_name.Find(",");
     if (pos.has_value()) {
-      family = SubstName.First(pos.value());
+      family = subst_name.First(pos.value());
       GetStandardFontName(&family);
-      style = SubstName.Last(SubstName.GetLength() - (pos.value() + 1));
-      bHasComma = true;
+      style = subst_name.Last(subst_name.GetLength() - (pos.value() + 1));
+      has_comma = true;
     } else {
-      family = SubstName;
+      family = subst_name;
     }
   }
-  for (; iBaseFont < 12; iBaseFont++) {
-    if (family == ByteStringView(g_Base14FontNames[iBaseFont]))
+  for (; base_font < kSymbol; base_font++) {
+    if (family == kBase14FontNames[base_font])
       break;
   }
-  int PitchFamily = 0;
-  uint32_t nStyle = FXFONT_NORMAL;
-  bool bStyleAvail = false;
-  if (iBaseFont < 12) {
-    if ((iBaseFont % 4) == 1 || (iBaseFont % 4) == 2)
-      nStyle |= FXFONT_FORCE_BOLD;
-    if ((iBaseFont % 4) / 2)
-      nStyle |= FXFONT_ITALIC;
-    if (iBaseFont < 4)
-      PitchFamily |= FXFONT_FF_FIXEDPITCH;
-    if (iBaseFont >= 8)
-      PitchFamily |= FXFONT_FF_ROMAN;
+  int pitch_family;
+  uint32_t nStyle;
+  bool is_style_available = false;
+  if (base_font < kSymbol) {
+    nStyle = GetStyleFromBaseFont(base_font);
+    pitch_family = GetPitchFamilyFromBaseFont(base_font);
   } else {
-    iBaseFont = kNumStandardFonts;
-    if (!bHasComma) {
-      Optional<size_t> pos = family.ReverseFind('-');
+    base_font = kNumStandardFonts;
+    nStyle = FXFONT_NORMAL;
+    if (!has_comma) {
+      absl::optional<size_t> pos = family.ReverseFind('-');
       if (pos.has_value()) {
         style = family.Last(family.GetLength() - (pos.value() + 1));
         family = family.First(pos.value());
-        bHasHyphen = true;
+        has_hyphen = true;
       }
     }
-    if (!bHasHyphen) {
-      int nLen = family.GetLength();
-      bool hasStyleType;
-      uint32_t styleType;
-      size_t len;
-      std::tie(hasStyleType, styleType, len) = GetStyleType(family, true);
-      if (hasStyleType) {
-        family = family.First(nLen - len);
-        nStyle |= styleType;
+    if (!has_hyphen) {
+      size_t nLen = family.GetLength();
+      const FX_FontStyle* style_result =
+          GetStyleType(family.AsStringView(), /*reverse_search=*/true);
+      if (style_result) {
+        family = family.First(nLen - style_result->len);
+        nStyle |= style_result->style;
       }
     }
-    UpdatePitchFamily(flags, &PitchFamily);
+    pitch_family = GetPitchFamilyFromFlags(flags);
   }
 
   const int old_weight = weight;
   if (FontStyleIsForceBold(nStyle))
     weight = FXFONT_FW_BOLD;
 
-  if (!style.IsEmpty()) {
-    int nLen = style.GetLength();
-    const char* pStyle = style.c_str();
-    int i = 0;
-    bool bFirstItem = true;
-    ByteString buf;
-    while (i < nLen) {
-      buf = ParseStyle(pStyle, nLen, i);
-
-      bool hasStyleType;
-      uint32_t styleType;
-      size_t len;
-      std::tie(hasStyleType, styleType, len) = GetStyleType(buf, false);
-      if ((i && !bStyleAvail) || (!i && !hasStyleType)) {
-        family = SubstName;
-        iBaseFont = kNumStandardFonts;
-        break;
-      }
-      if (hasStyleType)
-        bStyleAvail = true;
-
-      if (FontStyleIsForceBold(styleType)) {
-        // If we're already bold, then we're double bold, use special weight.
-        if (FontStyleIsForceBold(nStyle)) {
-          weight = FXFONT_FW_BOLD_BOLD;
-        } else {
-          weight = FXFONT_FW_BOLD;
-          nStyle |= FXFONT_FORCE_BOLD;
-        }
-
-        bFirstItem = false;
-      }
-      if (FontStyleIsItalic(styleType) && FontStyleIsForceBold(styleType)) {
-        nStyle |= FXFONT_ITALIC;
-      } else if (FontStyleIsItalic(styleType)) {
-        if (bFirstItem) {
-          nStyle |= FXFONT_ITALIC;
-        } else {
-          family = SubstName;
-          iBaseFont = kNumStandardFonts;
-        }
-        break;
-      }
-      i += buf.GetLength() + 1;
-    }
+  if (ParseStyles(style, &is_style_available, &weight, &nStyle)) {
+    family = subst_name;
+    base_font = kNumStandardFonts;
   }
 
   if (!m_pFontInfo) {
-    return UseInternalSubst(pSubstFont, iBaseFont, italic_angle, old_weight,
-                            PitchFamily);
+    return UseInternalSubst(base_font, old_weight, italic_angle, pitch_family,
+                            subst_font);
   }
 
-  int Charset = FX_CHARSET_ANSI;
-  if (CharsetCP)
-    Charset = FX_GetCharsetFromCodePage(CharsetCP);
-  else if (iBaseFont == kNumStandardFonts && FontStyleIsSymbolic(flags))
-    Charset = FX_CHARSET_Symbol;
-  const bool bCJK = FX_CharSetIsCJK(Charset);
-  bool bItalic = FontStyleIsItalic(nStyle);
+  const FX_Charset Charset = GetCharset(code_page, base_font, flags);
+  const bool is_cjk = FX_CharSetIsCJK(Charset);
+  bool is_italic = FontStyleIsItalic(nStyle);
 
   GetFontFamily(nStyle, &family);
-  ByteString match = MatchInstalledFonts(TT_NormalizeName(family.c_str()));
-  if (match.IsEmpty() && family != SubstName &&
-      (!bHasComma && (!bHasHyphen || (bHasHyphen && !bStyleAvail)))) {
-    match = MatchInstalledFonts(TT_NormalizeName(SubstName.c_str()));
+  ByteString match = MatchInstalledFonts(TT_NormalizeName(family));
+  if (match.IsEmpty() && family != subst_name &&
+      (!has_comma && (!has_hyphen || (has_hyphen && !is_style_available)))) {
+    match = MatchInstalledFonts(TT_NormalizeName(subst_name));
   }
-  if (match.IsEmpty() && iBaseFont >= kNumStandardFonts) {
-    if (!bCJK) {
-      if (!CheckSupportThirdPartFont(family, &PitchFamily)) {
-        bItalic = italic_angle != 0;
+  if (match.IsEmpty() && base_font >= kNumStandardFonts) {
+    if (!is_cjk) {
+      if (!CheckSupportThirdPartFont(family, &pitch_family)) {
+        is_italic = italic_angle != 0;
         weight = old_weight;
       }
-      Optional<size_t> pos = SubstName.Find("Narrow");
-      if (pos.has_value() && pos.value() != 0)
-        family = kNarrowFamily;
-      pos = SubstName.Find("Condensed");
-      if (pos.has_value() && pos.value() != 0)
+      if (IsNarrowFontName(subst_name))
         family = kNarrowFamily;
     } else {
-      pSubstFont->m_bSubstCJK = true;
+      subst_font->m_bSubstCJK = true;
       if (nStyle)
-        pSubstFont->m_WeightCJK = nStyle ? weight : FXFONT_FW_NORMAL;
+        subst_font->m_WeightCJK = nStyle ? weight : FXFONT_FW_NORMAL;
       if (FontStyleIsItalic(nStyle))
-        pSubstFont->m_bItalicCJK = true;
+        subst_font->m_bItalicCJK = true;
     }
   } else {
     italic_angle = 0;
@@ -562,163 +683,158 @@
       weight = FXFONT_FW_NORMAL;
   }
 
-  if (!match.IsEmpty() || iBaseFont < kNumStandardFonts) {
+  if (!match.IsEmpty() || base_font < kNumStandardFonts) {
     if (!match.IsEmpty())
       family = match;
-    if (iBaseFont < kNumStandardFonts) {
-      if (nStyle && !(iBaseFont % 4)) {
-        if (FontStyleIsForceBold(nStyle) && FontStyleIsItalic(nStyle))
-          iBaseFont += 2;
-        else if (FontStyleIsForceBold(nStyle))
-          iBaseFont += 1;
-        else if (FontStyleIsItalic(nStyle))
-          iBaseFont += 3;
-      }
-      family = g_Base14FontNames[iBaseFont];
+    if (base_font < kNumStandardFonts) {
+      base_font = AdjustBaseFontForStyle(base_font, nStyle);
+      family = kBase14FontNames[base_font];
     }
   } else if (FontStyleIsItalic(flags)) {
-    bItalic = true;
+    is_italic = true;
   }
-  void* hFont = m_pFontInfo->MapFont(weight, bItalic, Charset, PitchFamily,
-                                     family.c_str());
-  if (!hFont) {
-    if (bCJK) {
-      bItalic = italic_angle != 0;
-      weight = old_weight;
+  void* font_handle =
+      m_pFontInfo->MapFont(weight, is_italic, Charset, pitch_family, family);
+  if (font_handle) {
+    return UseExternalSubst(font_handle, subst_name, weight, is_italic,
+                            italic_angle, Charset, subst_font);
+  }
+
+  if (is_cjk) {
+    is_italic = italic_angle != 0;
+    weight = old_weight;
+  }
+  if (!match.IsEmpty()) {
+    font_handle = m_pFontInfo->GetFont(match);
+    if (!font_handle) {
+      return UseInternalSubst(base_font, old_weight, italic_angle, pitch_family,
+                              subst_font);
     }
-    if (!match.IsEmpty()) {
-      hFont = m_pFontInfo->GetFont(match.c_str());
-      if (!hFont) {
-        return UseInternalSubst(pSubstFont, iBaseFont, italic_angle, old_weight,
-                                PitchFamily);
-      }
-    } else {
-      if (Charset == FX_CHARSET_Symbol) {
-#if defined(OS_MACOSX) || defined(OS_ANDROID)
-        if (SubstName == "Symbol") {
-          pSubstFont->m_Family = "Chrome Symbol";
-          pSubstFont->m_Charset = FX_CHARSET_Symbol;
-          return UseInternalSubst(pSubstFont, 12, italic_angle, old_weight,
-                                  PitchFamily);
-        }
+    return UseExternalSubst(font_handle, subst_name, weight, is_italic,
+                            italic_angle, Charset, subst_font);
+  }
+
+  if (Charset == FX_Charset::kSymbol) {
+#if !BUILDFLAG(IS_WIN)
+    if (subst_name == "Symbol") {
+      subst_font->m_Family = "Chrome Symbol";
+      subst_font->m_Charset = FX_Charset::kSymbol;
+      return UseInternalSubst(kSymbol, old_weight, italic_angle, pitch_family,
+                              subst_font);
+    }
 #endif
-        return FindSubstFont(family, bTrueType, flags & ~FXFONT_SYMBOLIC,
-                             weight, italic_angle, 0, pSubstFont);
-      }
-      if (Charset == FX_CHARSET_ANSI) {
-        return UseInternalSubst(pSubstFont, iBaseFont, italic_angle, old_weight,
-                                PitchFamily);
-      }
+    return FindSubstFont(family, is_truetype, flags & ~FXFONT_SYMBOLIC, weight,
+                         italic_angle, FX_CodePage::kDefANSI, subst_font);
+  }
 
-      auto it =
-          std::find_if(m_FaceArray.begin(), m_FaceArray.end(),
-                       [Charset](const FaceData& face) {
-                         return face.charset == static_cast<uint32_t>(Charset);
-                       });
-      if (it == m_FaceArray.end()) {
-        return UseInternalSubst(pSubstFont, iBaseFont, italic_angle, old_weight,
-                                PitchFamily);
-      }
-      hFont = m_pFontInfo->GetFont(it->name.c_str());
-    }
+  if (Charset == FX_Charset::kANSI) {
+    return UseInternalSubst(base_font, old_weight, italic_angle, pitch_family,
+                            subst_font);
   }
-  if (!hFont)
-    return nullptr;
 
-  m_pFontInfo->GetFaceName(hFont, &SubstName);
-  if (Charset == FX_CHARSET_Default)
-    m_pFontInfo->GetFontCharset(hFont, &Charset);
-  uint32_t ttc_size = m_pFontInfo->GetFontData(hFont, kTableTTCF, {});
-  uint32_t font_size = m_pFontInfo->GetFontData(hFont, 0, {});
-  if (font_size == 0 && ttc_size == 0) {
-    m_pFontInfo->DeleteFont(hFont);
+  auto it = std::find_if(
+      m_FaceArray.begin(), m_FaceArray.end(), [Charset](const FaceData& face) {
+        return face.charset == static_cast<uint32_t>(Charset);
+      });
+  if (it == m_FaceArray.end()) {
+    return UseInternalSubst(base_font, old_weight, italic_angle, pitch_family,
+                            subst_font);
+  }
+  font_handle = m_pFontInfo->GetFont(it->name);
+  if (!font_handle)
     return nullptr;
-  }
-  RetainPtr<CFX_Face> face;
-  if (ttc_size)
-    face = GetCachedTTCFace(hFont, ttc_size, font_size);
-  else
-    face = GetCachedFace(hFont, SubstName, weight, bItalic, font_size);
-  if (!face) {
-    m_pFontInfo->DeleteFont(hFont);
-    return nullptr;
-  }
-  pSubstFont->m_Family = SubstName;
-  pSubstFont->m_Charset = Charset;
-  bool bNeedUpdateWeight = false;
-  if (FXFT_Is_Face_Bold(face->GetRec()))
-    bNeedUpdateWeight = weight != FXFONT_FW_BOLD;
-  else
-    bNeedUpdateWeight = weight != FXFONT_FW_NORMAL;
-  if (bNeedUpdateWeight)
-    pSubstFont->m_Weight = weight;
-  if (bItalic && !FXFT_Is_Face_Italic(face->GetRec())) {
-    if (italic_angle == 0)
-      italic_angle = -12;
-    else if (abs(italic_angle) < 5)
-      italic_angle = 0;
-    pSubstFont->m_ItalicAngle = italic_angle;
-  }
-  m_pFontInfo->DeleteFont(hFont);
-  return face;
+  return UseExternalSubst(font_handle, subst_name, weight, is_italic,
+                          italic_angle, Charset, subst_font);
 }
 
-int CFX_FontMapper::GetFaceSize() const {
-  return pdfium::CollectionSize<int>(m_FaceArray);
+size_t CFX_FontMapper::GetFaceSize() const {
+  return m_FaceArray.size();
 }
 
-#ifdef PDF_ENABLE_XFA
-std::unique_ptr<uint8_t, FxFreeDeleter> CFX_FontMapper::RawBytesForIndex(
-    uint32_t index,
-    size_t* returned_length) {
-  if (!m_pFontInfo)
-    return nullptr;
-
-  void* hFont = m_pFontInfo->MapFont(0, 0, FX_CHARSET_Default, 0,
-                                     GetFaceName(index).c_str());
-  if (!hFont)
-    return nullptr;
-
-  uint32_t required_size = m_pFontInfo->GetFontData(hFont, 0, {});
-  if (required_size == 0)
-    return nullptr;
-
-  std::unique_ptr<uint8_t, FxFreeDeleter> pBuffer(
-      FX_Alloc(uint8_t, required_size + 1));
-  *returned_length =
-      m_pFontInfo->GetFontData(hFont, 0, {pBuffer.get(), required_size});
-  return pBuffer;
+ByteString CFX_FontMapper::GetFaceName(size_t index) const {
+  CHECK_LT(index, m_FaceArray.size());
+  return m_FaceArray[index].name;
 }
-#endif  // PDF_ENABLE_XFA
 
-bool CFX_FontMapper::IsBuiltinFace(const RetainPtr<CFX_Face>& face) const {
-  for (size_t i = 0; i < MM_FACE_COUNT; ++i) {
-    if (m_MMFaces[i] == face)
-      return true;
-  }
-  for (size_t i = 0; i < FOXIT_FACE_COUNT; ++i) {
-    if (m_FoxitFaces[i] == face)
+bool CFX_FontMapper::HasInstalledFont(ByteStringView name) const {
+  for (const auto& font : m_InstalledTTFonts) {
+    if (font == name)
       return true;
   }
   return false;
 }
 
-RetainPtr<CFX_Face> CFX_FontMapper::GetCachedTTCFace(void* hFont,
-                                                     uint32_t ttc_size,
-                                                     uint32_t font_size) {
-  uint32_t checksum = GetChecksumFromTT(hFont);
+bool CFX_FontMapper::HasLocalizedFont(ByteStringView name) const {
+  for (const auto& fontPair : m_LocalizedTTFonts) {
+    if (fontPair.first == name)
+      return true;
+  }
+  return false;
+}
+
+#if BUILDFLAG(IS_WIN)
+absl::optional<ByteString> CFX_FontMapper::InstalledFontNameStartingWith(
+    const ByteString& name) const {
+  for (const auto& thisname : m_InstalledTTFonts) {
+    if (thisname.First(name.GetLength()) == name)
+      return thisname;
+  }
+  return absl::nullopt;
+}
+
+absl::optional<ByteString> CFX_FontMapper::LocalizedFontNameStartingWith(
+    const ByteString& name) const {
+  for (const auto& thispair : m_LocalizedTTFonts) {
+    if (thispair.first.First(name.GetLength()) == name)
+      return thispair.second;
+  }
+  return absl::nullopt;
+}
+#endif  // BUILDFLAG(IS_WIN)
+
+#ifdef PDF_ENABLE_XFA
+FixedUninitDataVector<uint8_t> CFX_FontMapper::RawBytesForIndex(size_t index) {
+  CHECK_LT(index, m_FaceArray.size());
+
+  void* font_handle = m_pFontInfo->MapFont(0, false, FX_Charset::kDefault, 0,
+                                           GetFaceName(index));
+  if (!font_handle)
+    return FixedUninitDataVector<uint8_t>();
+
+  ScopedFontDeleter scoped_font(m_pFontInfo.get(), font_handle);
+  size_t required_size = m_pFontInfo->GetFontData(font_handle, 0, {});
+  if (required_size == 0)
+    return FixedUninitDataVector<uint8_t>();
+
+  FixedUninitDataVector<uint8_t> result(required_size);
+  size_t actual_size =
+      m_pFontInfo->GetFontData(font_handle, 0, result.writable_span());
+  if (actual_size != required_size)
+    return FixedUninitDataVector<uint8_t>();
+
+  return result;
+}
+#endif  // PDF_ENABLE_XFA
+
+RetainPtr<CFX_Face> CFX_FontMapper::GetCachedTTCFace(void* font_handle,
+                                                     size_t ttc_size,
+                                                     size_t data_size) {
+  CHECK_GE(ttc_size, data_size);
+  uint32_t checksum = GetChecksumFromTT(font_handle);
   RetainPtr<CFX_FontMgr::FontDesc> pFontDesc =
       m_pFontMgr->GetCachedTTCFontDesc(ttc_size, checksum);
   if (!pFontDesc) {
-    std::unique_ptr<uint8_t, FxFreeDeleter> pFontData(
-        FX_Alloc(uint8_t, ttc_size));
-    m_pFontInfo->GetFontData(hFont, kTableTTCF, {pFontData.get(), ttc_size});
-    pFontDesc = m_pFontMgr->AddCachedTTCFontDesc(
-        ttc_size, checksum, std::move(pFontData), ttc_size);
+    FixedUninitDataVector<uint8_t> font_data(ttc_size);
+    size_t size = m_pFontInfo->GetFontData(font_handle, kTableTTCF,
+                                           font_data.writable_span());
+    if (size != ttc_size)
+      return nullptr;
+
+    pFontDesc = m_pFontMgr->AddCachedTTCFontDesc(ttc_size, checksum,
+                                                 std::move(font_data));
   }
-  ASSERT(ttc_size >= font_size);
-  uint32_t font_offset = ttc_size - font_size;
-  int face_index =
+  size_t font_offset = ttc_size - data_size;
+  size_t face_index =
       GetTTCIndex(pFontDesc->FontData().first(ttc_size), font_offset);
   RetainPtr<CFX_Face> pFace(pFontDesc->GetFace(face_index));
   if (pFace)
@@ -733,27 +849,29 @@
   return pFace;
 }
 
-RetainPtr<CFX_Face> CFX_FontMapper::GetCachedFace(void* hFont,
-                                                  ByteString SubstName,
+RetainPtr<CFX_Face> CFX_FontMapper::GetCachedFace(void* font_handle,
+                                                  ByteString subst_name,
                                                   int weight,
-                                                  bool bItalic,
-                                                  uint32_t font_size) {
+                                                  bool is_italic,
+                                                  size_t data_size) {
   RetainPtr<CFX_FontMgr::FontDesc> pFontDesc =
-      m_pFontMgr->GetCachedFontDesc(SubstName, weight, bItalic);
+      m_pFontMgr->GetCachedFontDesc(subst_name, weight, is_italic);
   if (!pFontDesc) {
-    std::unique_ptr<uint8_t, FxFreeDeleter> pFontData(
-        FX_Alloc(uint8_t, font_size));
-    m_pFontInfo->GetFontData(hFont, 0, {pFontData.get(), font_size});
-    pFontDesc = m_pFontMgr->AddCachedFontDesc(SubstName, weight, bItalic,
-                                              std::move(pFontData), font_size);
+    FixedUninitDataVector<uint8_t> font_data(data_size);
+    size_t size =
+        m_pFontInfo->GetFontData(font_handle, 0, font_data.writable_span());
+    if (size != data_size)
+      return nullptr;
+
+    pFontDesc = m_pFontMgr->AddCachedFontDesc(subst_name, weight, is_italic,
+                                              std::move(font_data));
   }
   RetainPtr<CFX_Face> pFace(pFontDesc->GetFace(0));
   if (pFace)
     return pFace;
 
   pFace = m_pFontMgr->NewFixedFace(pFontDesc,
-                                   pFontDesc->FontData().first(font_size),
-                                   m_pFontInfo->GetFaceIndex(hFont));
+                                   pFontDesc->FontData().first(data_size), 0);
   if (!pFace)
     return nullptr;
 
@@ -762,22 +880,27 @@
 }
 
 // static
-Optional<CFX_FontMapper::StandardFont> CFX_FontMapper::GetStandardFontName(
-    ByteString* name) {
-  const auto* end = std::end(g_AltFontNames);
+absl::optional<CFX_FontMapper::StandardFont>
+CFX_FontMapper::GetStandardFontName(ByteString* name) {
+  const auto* end = std::end(kAltFontNames);
   const auto* found =
-      std::lower_bound(std::begin(g_AltFontNames), end, name->c_str(),
+      std::lower_bound(std::begin(kAltFontNames), end, name->c_str(),
                        [](const AltFontName& element, const char* name) {
                          return FXSYS_stricmp(element.m_pName, name) < 0;
                        });
   if (found == end || FXSYS_stricmp(found->m_pName, name->c_str()))
-    return {};
+    return absl::nullopt;
 
-  *name = g_Base14FontNames[static_cast<size_t>(found->m_Index)];
+  *name = kBase14FontNames[static_cast<size_t>(found->m_Index)];
   return found->m_Index;
 }
 
 // static
+bool CFX_FontMapper::IsStandardFontName(const ByteString& name) {
+  return pdfium::Contains(kBase14FontNames, name);
+}
+
+// static
 bool CFX_FontMapper::IsSymbolicFont(StandardFont font) {
   return font == StandardFont::kSymbol || font == StandardFont::kDingbats;
 }
diff --git a/core/fxge/cfx_fontmapper.h b/core/fxge/cfx_fontmapper.h
index c21c6cb..aa8f671 100644
--- a/core/fxge/cfx_fontmapper.h
+++ b/core/fxge/cfx_fontmapper.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,10 +11,17 @@
 #include <utility>
 #include <vector>
 
-#include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxcrt/fx_string.h"
+#include "build/build_config.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/fx_codepage_forward.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_face.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+#ifdef PDF_ENABLE_XFA
+#include "core/fxcrt/fixed_uninit_data_vector.h"
+#endif
 
 class CFX_FontMgr;
 class CFX_SubstFont;
@@ -22,7 +29,7 @@
 
 class CFX_FontMapper {
  public:
-  enum StandardFont {
+  enum StandardFont : uint8_t {
     kCourier = 0,
     kCourierBold,
     kCourierBoldOblique,
@@ -37,12 +44,15 @@
     kTimesOblique,
     kSymbol,
     kDingbats,
+    kLast = kDingbats
   };
+  static constexpr int kNumStandardFonts = 14;
 
   explicit CFX_FontMapper(CFX_FontMgr* mgr);
   ~CFX_FontMapper();
 
-  static Optional<StandardFont> GetStandardFontName(ByteString* name);
+  static absl::optional<StandardFont> GetStandardFontName(ByteString* name);
+  static bool IsStandardFontName(const ByteString& name);
   static bool IsSymbolicFont(StandardFont font);
   static bool IsFixedFont(StandardFont font);
   static constexpr uint32_t MakeTag(char c1, char c2, char c3, char c4) {
@@ -51,50 +61,62 @@
   }
 
   void SetSystemFontInfo(std::unique_ptr<SystemFontInfoIface> pFontInfo);
-  void AddInstalledFont(const ByteString& name, int charset);
+  std::unique_ptr<SystemFontInfoIface> TakeSystemFontInfo();
+  void AddInstalledFont(const ByteString& name, FX_Charset charset);
   void LoadInstalledFonts();
 
   RetainPtr<CFX_Face> FindSubstFont(const ByteString& face_name,
-                                    bool bTrueType,
+                                    bool is_truetype,
                                     uint32_t flags,
                                     int weight,
                                     int italic_angle,
-                                    int CharsetCP,
-                                    CFX_SubstFont* pSubstFont);
+                                    FX_CodePage code_page,
+                                    CFX_SubstFont* subst_font);
 
-  bool IsBuiltinFace(const RetainPtr<CFX_Face>& face) const;
-  int GetFaceSize() const;
-  ByteString GetFaceName(int index) const { return m_FaceArray[index].name; }
+  size_t GetFaceSize() const;
+  // `index` must be less than GetFaceSize().
+  ByteString GetFaceName(size_t index) const;
+  bool HasInstalledFont(ByteStringView name) const;
+  bool HasLocalizedFont(ByteStringView name) const;
+
+#if BUILDFLAG(IS_WIN)
+  absl::optional<ByteString> InstalledFontNameStartingWith(
+      const ByteString& name) const;
+  absl::optional<ByteString> LocalizedFontNameStartingWith(
+      const ByteString& name) const;
+#endif  // BUILDFLAG(IS_WIN)
 
 #ifdef PDF_ENABLE_XFA
-  std::unique_ptr<uint8_t, FxFreeDeleter> RawBytesForIndex(
-      uint32_t index,
-      size_t* returned_length);
+  // `index` must be less than GetFaceSize().
+  FixedUninitDataVector<uint8_t> RawBytesForIndex(size_t index);
 #endif  // PDF_ENABLE_XFA
 
-  std::vector<ByteString> m_InstalledTTFonts;
-  std::vector<std::pair<ByteString, ByteString>> m_LocalizedTTFonts;
-
  private:
-  static const size_t MM_FACE_COUNT = 2;
-  static const size_t FOXIT_FACE_COUNT = 14;
+  friend class TestFontMapper;
 
-  uint32_t GetChecksumFromTT(void* hFont);
-  ByteString GetPSNameFromTT(void* hFont);
+  uint32_t GetChecksumFromTT(void* font_handle);
+  ByteString GetPSNameFromTT(void* font_handle);
   ByteString MatchInstalledFonts(const ByteString& norm_name);
-  RetainPtr<CFX_Face> UseInternalSubst(CFX_SubstFont* pSubstFont,
-                                       int iBaseFont,
-                                       int italic_angle,
+  RetainPtr<CFX_Face> UseInternalSubst(int base_font,
                                        int weight,
-                                       int pitch_family);
-  RetainPtr<CFX_Face> GetCachedTTCFace(void* hFont,
-                                       uint32_t ttc_size,
-                                       uint32_t font_size);
-  RetainPtr<CFX_Face> GetCachedFace(void* hFont,
-                                    ByteString SubstName,
+                                       int italic_angle,
+                                       int pitch_family,
+                                       CFX_SubstFont* subst_font);
+  RetainPtr<CFX_Face> UseExternalSubst(void* font_handle,
+                                       ByteString face_name,
+                                       int weight,
+                                       bool is_italic,
+                                       int italic_angle,
+                                       FX_Charset charset,
+                                       CFX_SubstFont* subst_font);
+  RetainPtr<CFX_Face> GetCachedTTCFace(void* font_handle,
+                                       size_t ttc_size,
+                                       size_t data_size);
+  RetainPtr<CFX_Face> GetCachedFace(void* font_handle,
+                                    ByteString subst_name,
                                     int weight,
-                                    bool bItalic,
-                                    uint32_t font_size);
+                                    bool is_italic,
+                                    size_t data_size);
 
   struct FaceData {
     ByteString name;
@@ -106,8 +128,11 @@
   std::vector<FaceData> m_FaceArray;
   std::unique_ptr<SystemFontInfoIface> m_pFontInfo;
   UnownedPtr<CFX_FontMgr> const m_pFontMgr;
-  RetainPtr<CFX_Face> m_MMFaces[MM_FACE_COUNT];
-  RetainPtr<CFX_Face> m_FoxitFaces[FOXIT_FACE_COUNT];
+  std::vector<ByteString> m_InstalledTTFonts;
+  std::vector<std::pair<ByteString, ByteString>> m_LocalizedTTFonts;
+  RetainPtr<CFX_Face> m_StandardFaces[kNumStandardFonts];
+  RetainPtr<CFX_Face> m_GenericSansFace;
+  RetainPtr<CFX_Face> m_GenericSerifFace;
 };
 
 #endif  // CORE_FXGE_CFX_FONTMAPPER_H_
diff --git a/core/fxge/cfx_fontmapper_unittest.cpp b/core/fxge/cfx_fontmapper_unittest.cpp
index e69387d..ab0c858 100644
--- a/core/fxge/cfx_fontmapper_unittest.cpp
+++ b/core/fxge/cfx_fontmapper_unittest.cpp
@@ -1,14 +1,116 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxge/cfx_fontmapper.h"
 
+#include <memory>
+#include <numeric>
+#include <utility>
+
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/systemfontinfo_iface.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::_;
+using testing::DoAll;
+using testing::ElementsAre;
+using testing::InSequence;
+using testing::Invoke;
+using testing::Return;
+using testing::WithArg;
+
+class MockSystemFontInfo : public SystemFontInfoIface {
+ public:
+  MockSystemFontInfo() = default;
+  ~MockSystemFontInfo() override = default;
+
+  // SystemFontInfoIface:
+  MOCK_METHOD(bool, EnumFontList, (CFX_FontMapper*), (override));
+  MOCK_METHOD(void*,
+              MapFont,
+              (int, bool, FX_Charset, int, const ByteString&),
+              (override));
+  MOCK_METHOD(void*, GetFont, (const ByteString&), (override));
+  MOCK_METHOD(size_t,
+              GetFontData,
+              (void*, uint32_t, pdfium::span<uint8_t>),
+              (override));
+  MOCK_METHOD(bool, GetFaceName, (void*, ByteString*), (override));
+  MOCK_METHOD(bool, GetFontCharset, (void*, FX_Charset*), (override));
+  MOCK_METHOD(void, DeleteFont, (void*), (override));
+};
+
+// Class that exposes private CFX_FontMapper methods.
+class TestFontMapper : public CFX_FontMapper {
+ public:
+  TestFontMapper() : CFX_FontMapper(CFX_GEModule::Get()->GetFontMgr()) {}
+
+  RetainPtr<CFX_Face> GetCachedTTCFace(void* font_handle,
+                                       size_t ttc_size,
+                                       size_t data_size) {
+    return CFX_FontMapper::GetCachedTTCFace(font_handle, ttc_size, data_size);
+  }
+
+  RetainPtr<CFX_Face> GetCachedFace(void* font_handle,
+                                    ByteString subst_name,
+                                    int weight,
+                                    bool is_italic,
+                                    size_t data_size) {
+    return CFX_FontMapper::GetCachedFace(font_handle, subst_name, weight,
+                                         is_italic, data_size);
+  }
+};
+
+class CFXFontMapperSystemFontInfoTest : public testing::Test {
+ protected:
+  CFXFontMapperSystemFontInfoTest() = default;
+  ~CFXFontMapperSystemFontInfoTest() override = default;
+
+  void SetUp() override {
+    font_mapper_ = std::make_unique<TestFontMapper>();
+    auto system_font_info = std::make_unique<MockSystemFontInfo>();
+    system_font_info_ = system_font_info.get();
+    font_mapper_->SetSystemFontInfo(std::move(system_font_info));
+    font_mapper_->AddInstalledFont("dummy", FX_Charset::kANSI);
+  }
+
+  TestFontMapper& font_mapper() { return *font_mapper_; }
+  MockSystemFontInfo& system_font_info() { return *system_font_info_; }
+
+ private:
+  // Must outlive `system_font_info_`.
+  std::unique_ptr<TestFontMapper> font_mapper_;
+  UnownedPtr<MockSystemFontInfo> system_font_info_;
+};
+
 // Deliberately give this global variable external linkage.
 char g_maybe_changes = '\xff';
 
+TEST(CFX_FontMapper, IsStandardFontName) {
+  EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Courier"));
+  EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Courier-Bold"));
+  EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Courier-BoldOblique"));
+  EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Courier-Oblique"));
+  EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Helvetica"));
+  EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Helvetica-Bold"));
+  EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Helvetica-BoldOblique"));
+  EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Helvetica-Oblique"));
+  EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Times-Roman"));
+  EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Times-Bold"));
+  EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Times-BoldItalic"));
+  EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Times-Italic"));
+  EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Symbol"));
+  EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("ZapfDingbats"));
+
+  EXPECT_FALSE(CFX_FontMapper::IsStandardFontName("Courie"));
+  EXPECT_FALSE(CFX_FontMapper::IsStandardFontName("Courier-"));
+  EXPECT_FALSE(CFX_FontMapper::IsStandardFontName("Helvetica+Bold"));
+  EXPECT_FALSE(CFX_FontMapper::IsStandardFontName("YapfDingbats"));
+}
+
 TEST(CFX_FontMapper, MakeTag) {
   EXPECT_EQ(0x61626364u, CFX_FontMapper::MakeTag('a', 'b', 'c', 'd'));
   EXPECT_EQ(0x00000000u, CFX_FontMapper::MakeTag('\0', '\0', '\0', '\0'));
@@ -17,4 +119,117 @@
             CFX_FontMapper::MakeTag('\xff', '\xff', '\xff', '\xff'));
   EXPECT_EQ(0xffffffffu,
             CFX_FontMapper::MakeTag(g_maybe_changes, '\xff', '\xff', '\xff'));
+  EXPECT_EQ(0x6e616d65u, CFX_FontMapper::MakeTag('n', 'a', 'm', 'e'));
+  EXPECT_EQ(0x4f532f32u, CFX_FontMapper::MakeTag('O', 'S', '/', '2'));
+  EXPECT_EQ(FT_MAKE_TAG('G', 'S', 'U', 'B'),
+            CFX_FontMapper::MakeTag('G', 'S', 'U', 'B'));
+}
+
+TEST(CFX_FontMapper, AddInstalledFontBasic) {
+  const char kFontName[] = "dummy";
+  CFX_FontMapper font_mapper(nullptr);
+  font_mapper.SetSystemFontInfo(std::make_unique<MockSystemFontInfo>());
+
+  font_mapper.AddInstalledFont(kFontName, FX_Charset::kANSI);
+  EXPECT_EQ(1u, font_mapper.GetFaceSize());
+  EXPECT_EQ(kFontName, font_mapper.GetFaceName(0));
+}
+
+#ifdef PDF_ENABLE_XFA
+TEST_F(CFXFontMapperSystemFontInfoTest, RawBytesForIndex) {
+  {
+    void* const kFontHandle = reinterpret_cast<void*>(12345);
+
+    InSequence s;
+    EXPECT_CALL(system_font_info(), MapFont).WillOnce(Return(kFontHandle));
+    EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
+        .WillOnce(Return(2));
+    EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
+        .WillOnce(DoAll(WithArg<2>(Invoke([](pdfium::span<uint8_t> buffer) {
+                          buffer[0] = '0';
+                          buffer[1] = '1';
+                        })),
+                        Return(2)));
+    EXPECT_CALL(system_font_info(), DeleteFont(kFontHandle));
+  }
+
+  FixedUninitDataVector<uint8_t> data = font_mapper().RawBytesForIndex(0);
+  EXPECT_THAT(data.span(), ElementsAre('0', '1'));
+}
+
+TEST_F(CFXFontMapperSystemFontInfoTest, RawBytesForIndexFailToMap) {
+  EXPECT_CALL(system_font_info(), MapFont).WillOnce(Return(nullptr));
+
+  FixedUninitDataVector<uint8_t> data = font_mapper().RawBytesForIndex(0);
+  EXPECT_TRUE(data.empty());
+}
+
+TEST_F(CFXFontMapperSystemFontInfoTest, RawBytesForIndexFailToGetDataSize) {
+  {
+    void* const kFontHandle = reinterpret_cast<void*>(12345);
+
+    InSequence s;
+    EXPECT_CALL(system_font_info(), MapFont).WillOnce(Return(kFontHandle));
+    EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
+        .WillOnce(Return(0));
+    EXPECT_CALL(system_font_info(), DeleteFont(kFontHandle));
+  }
+
+  FixedUninitDataVector<uint8_t> data = font_mapper().RawBytesForIndex(0);
+  EXPECT_TRUE(data.empty());
+}
+
+TEST_F(CFXFontMapperSystemFontInfoTest, RawBytesForIndexFailToGetData) {
+  {
+    void* const kFontHandle = reinterpret_cast<void*>(12345);
+
+    InSequence s;
+    EXPECT_CALL(system_font_info(), MapFont).WillOnce(Return(kFontHandle));
+    EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
+        .WillOnce(Return(2));
+    EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
+        .WillOnce(Return(0));
+    EXPECT_CALL(system_font_info(), DeleteFont(kFontHandle));
+  }
+
+  FixedUninitDataVector<uint8_t> data = font_mapper().RawBytesForIndex(0);
+  EXPECT_TRUE(data.empty());
+}
+#endif  // PDF_ENABLE_XFA
+
+// Regression test for crbug.com/1372234 - should not crash.
+TEST_F(CFXFontMapperSystemFontInfoTest, GetCachedTTCFaceFailToGetData) {
+  void* const kFontHandle = reinterpret_cast<void*>(12345);
+  constexpr size_t kTtcSize = 1024;
+  constexpr size_t kDataSize = 2;
+
+  {
+    InSequence s;
+    EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, kTableTTCF, _))
+        .WillOnce(DoAll(WithArg<2>(Invoke([&](pdfium::span<uint8_t> buffer) {
+                          EXPECT_EQ(kTtcSize, buffer.size());
+                          std::iota(buffer.begin(), buffer.end(), 0);
+                        })),
+                        Return(kTtcSize)));
+    EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, kTableTTCF, _))
+        .WillOnce(Return(0));
+  }
+
+  EXPECT_FALSE(
+      font_mapper().GetCachedTTCFace(kFontHandle, kTtcSize, kDataSize));
+}
+
+// Regression test for crbug.com/1372234 - should not crash.
+TEST_F(CFXFontMapperSystemFontInfoTest, GetCachedFaceFailToGetData) {
+  void* const kFontHandle = reinterpret_cast<void*>(12345);
+  constexpr char kSubstName[] = "dummy_font";
+  constexpr int kWeight = 400;
+  constexpr bool kItalic = false;
+  constexpr size_t kDataSize = 2;
+
+  EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
+      .WillOnce(Return(0));
+
+  EXPECT_FALSE(font_mapper().GetCachedFace(kFontHandle, kSubstName, kWeight,
+                                           kItalic, kDataSize));
 }
diff --git a/core/fxge/cfx_fontmgr.cpp b/core/fxge/cfx_fontmgr.cpp
index 7a08d0f..e52979e 100644
--- a/core/fxge/cfx_fontmgr.cpp
+++ b/core/fxge/cfx_fontmgr.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,16 +6,17 @@
 
 #include "core/fxge/cfx_fontmgr.h"
 
+#include <iterator>
 #include <memory>
 #include <utility>
 
-#include "core/fxge/cfx_face.h"
+#include "core/fxcrt/fixed_uninit_data_vector.h"
 #include "core/fxge/cfx_fontmapper.h"
 #include "core/fxge/cfx_substfont.h"
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 #include "core/fxge/fx_font.h"
 #include "core/fxge/systemfontinfo_iface.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check_op.h"
 
 namespace {
 
@@ -24,41 +25,27 @@
   uint32_t m_dwSize;
 };
 
-const BuiltinFont g_FoxitFonts[14] = {
-    {g_FoxitFixedFontData, 17597},
-    {g_FoxitFixedBoldFontData, 18055},
-    {g_FoxitFixedBoldItalicFontData, 19151},
-    {g_FoxitFixedItalicFontData, 18746},
-    {g_FoxitSansFontData, 15025},
-    {g_FoxitSansBoldFontData, 16344},
-    {g_FoxitSansBoldItalicFontData, 16418},
-    {g_FoxitSansItalicFontData, 16339},
-    {g_FoxitSerifFontData, 19469},
-    {g_FoxitSerifBoldFontData, 19395},
-    {g_FoxitSerifBoldItalicFontData, 20733},
-    {g_FoxitSerifItalicFontData, 21227},
-    {g_FoxitSymbolFontData, 16729},
-    {g_FoxitDingbatsFontData, 29513},
+constexpr BuiltinFont kFoxitFonts[] = {
+    {kFoxitFixedFontData, 17597},
+    {kFoxitFixedBoldFontData, 18055},
+    {kFoxitFixedBoldItalicFontData, 19151},
+    {kFoxitFixedItalicFontData, 18746},
+    {kFoxitSansFontData, 15025},
+    {kFoxitSansBoldFontData, 16344},
+    {kFoxitSansBoldItalicFontData, 16418},
+    {kFoxitSansItalicFontData, 16339},
+    {kFoxitSerifFontData, 19469},
+    {kFoxitSerifBoldFontData, 19395},
+    {kFoxitSerifBoldItalicFontData, 20733},
+    {kFoxitSerifItalicFontData, 21227},
+    {kFoxitSymbolFontData, 16729},
+    {kFoxitDingbatsFontData, 29513},
 };
+static_assert(std::size(kFoxitFonts) == CFX_FontMapper::kNumStandardFonts,
+              "Wrong font count");
 
-const BuiltinFont g_MMFonts[2] = {
-    {g_FoxitSerifMMFontData, 113417},
-    {g_FoxitSansMMFontData, 66919},
-};
-
-ByteString KeyNameFromFace(const ByteString& face_name,
-                           int weight,
-                           bool bItalic) {
-  ByteString key(face_name);
-  key += ',';
-  key += ByteString::FormatInteger(weight);
-  key += bItalic ? 'I' : 'N';
-  return key;
-}
-
-ByteString KeyNameFromSize(int ttc_size, uint32_t checksum) {
-  return ByteString::Format("%d:%d", ttc_size, checksum);
-}
+constexpr BuiltinFont kGenericSansFont = {kFoxitSansMMFontData, 66919};
+constexpr BuiltinFont kGenericSerifFont = {kFoxitSerifMMFontData, 113417};
 
 FXFT_LibraryRec* FTLibraryInitHelper() {
   FXFT_LibraryRec* pLibrary = nullptr;
@@ -68,51 +55,34 @@
 
 }  // namespace
 
-CFX_FontMgr::FontDesc::FontDesc(std::unique_ptr<uint8_t, FxFreeDeleter> pData,
-                                size_t size)
-    : m_Size(size), m_pFontData(std::move(pData)) {}
+CFX_FontMgr::FontDesc::FontDesc(FixedUninitDataVector<uint8_t> data)
+    : m_pFontData(std::move(data)) {}
 
 CFX_FontMgr::FontDesc::~FontDesc() = default;
 
 void CFX_FontMgr::FontDesc::SetFace(size_t index, CFX_Face* face) {
-  ASSERT(index < FX_ArraySize(m_TTCFaces));
+  CHECK_LT(index, std::size(m_TTCFaces));
   m_TTCFaces[index].Reset(face);
 }
 
 CFX_Face* CFX_FontMgr::FontDesc::GetFace(size_t index) const {
-  ASSERT(index < FX_ArraySize(m_TTCFaces));
+  CHECK_LT(index, std::size(m_TTCFaces));
   return m_TTCFaces[index].Get();
 }
 
 CFX_FontMgr::CFX_FontMgr()
     : m_FTLibrary(FTLibraryInitHelper()),
-      m_pBuiltinMapper(pdfium::MakeUnique<CFX_FontMapper>(this)),
+      m_pBuiltinMapper(std::make_unique<CFX_FontMapper>(this)),
       m_FTLibrarySupportsHinting(SetLcdFilterMode() ||
                                  FreeTypeVersionSupportsHinting()) {}
 
 CFX_FontMgr::~CFX_FontMgr() = default;
 
-void CFX_FontMgr::SetSystemFontInfo(
-    std::unique_ptr<SystemFontInfoIface> pFontInfo) {
-  m_pBuiltinMapper->SetSystemFontInfo(std::move(pFontInfo));
-}
-
-RetainPtr<CFX_Face> CFX_FontMgr::FindSubstFont(const ByteString& face_name,
-                                               bool bTrueType,
-                                               uint32_t flags,
-                                               int weight,
-                                               int italic_angle,
-                                               int CharsetCP,
-                                               CFX_SubstFont* pSubstFont) {
-  return m_pBuiltinMapper->FindSubstFont(face_name, bTrueType, flags, weight,
-                                         italic_angle, CharsetCP, pSubstFont);
-}
-
 RetainPtr<CFX_FontMgr::FontDesc> CFX_FontMgr::GetCachedFontDesc(
     const ByteString& face_name,
     int weight,
     bool bItalic) {
-  auto it = m_FaceMap.find(KeyNameFromFace(face_name, weight, bItalic));
+  auto it = m_FaceMap.find({face_name, weight, bItalic});
   return it != m_FaceMap.end() ? pdfium::WrapRetain(it->second.Get()) : nullptr;
 }
 
@@ -120,35 +90,35 @@
     const ByteString& face_name,
     int weight,
     bool bItalic,
-    std::unique_ptr<uint8_t, FxFreeDeleter> pData,
-    uint32_t size) {
-  auto pFontDesc = pdfium::MakeRetain<FontDesc>(std::move(pData), size);
-  m_FaceMap[KeyNameFromFace(face_name, weight, bItalic)].Reset(pFontDesc.Get());
+    FixedUninitDataVector<uint8_t> data) {
+  auto pFontDesc = pdfium::MakeRetain<FontDesc>(std::move(data));
+  m_FaceMap[{face_name, weight, bItalic}].Reset(pFontDesc.Get());
   return pFontDesc;
 }
 
 RetainPtr<CFX_FontMgr::FontDesc> CFX_FontMgr::GetCachedTTCFontDesc(
-    int ttc_size,
+    size_t ttc_size,
     uint32_t checksum) {
-  auto it = m_FaceMap.find(KeyNameFromSize(ttc_size, checksum));
-  return it != m_FaceMap.end() ? pdfium::WrapRetain(it->second.Get()) : nullptr;
+  auto it = m_TTCFaceMap.find({ttc_size, checksum});
+  return it != m_TTCFaceMap.end() ? pdfium::WrapRetain(it->second.Get())
+                                  : nullptr;
 }
 
 RetainPtr<CFX_FontMgr::FontDesc> CFX_FontMgr::AddCachedTTCFontDesc(
-    int ttc_size,
+    size_t ttc_size,
     uint32_t checksum,
-    std::unique_ptr<uint8_t, FxFreeDeleter> pData,
-    uint32_t size) {
-  auto pNewDesc = pdfium::MakeRetain<FontDesc>(std::move(pData), size);
-  m_FaceMap[KeyNameFromSize(ttc_size, checksum)].Reset(pNewDesc.Get());
+    FixedUninitDataVector<uint8_t> data) {
+  auto pNewDesc = pdfium::MakeRetain<FontDesc>(std::move(data));
+  m_TTCFaceMap[{ttc_size, checksum}].Reset(pNewDesc.Get());
   return pNewDesc;
 }
 
-RetainPtr<CFX_Face> CFX_FontMgr::NewFixedFace(const RetainPtr<FontDesc>& pDesc,
+RetainPtr<CFX_Face> CFX_FontMgr::NewFixedFace(RetainPtr<FontDesc> pDesc,
                                               pdfium::span<const uint8_t> span,
-                                              int face_index) {
+                                              size_t face_index) {
   RetainPtr<CFX_Face> face =
-      CFX_Face::New(m_FTLibrary.get(), pDesc, span, face_index);
+      CFX_Face::New(m_FTLibrary.get(), std::move(pDesc), span,
+                    static_cast<FT_Long>(face_index));
   if (!face)
     return nullptr;
 
@@ -159,18 +129,22 @@
 }
 
 // static
-Optional<pdfium::span<const uint8_t>> CFX_FontMgr::GetBuiltinFont(
-    size_t index) {
-  if (index < FX_ArraySize(g_FoxitFonts)) {
-    return pdfium::make_span(g_FoxitFonts[index].m_pFontData,
-                             g_FoxitFonts[index].m_dwSize);
-  }
-  size_t mm_index = index - FX_ArraySize(g_FoxitFonts);
-  if (mm_index < FX_ArraySize(g_MMFonts)) {
-    return pdfium::make_span(g_MMFonts[mm_index].m_pFontData,
-                             g_MMFonts[mm_index].m_dwSize);
-  }
-  return {};
+pdfium::span<const uint8_t> CFX_FontMgr::GetStandardFont(size_t index) {
+  CHECK_LT(index, std::size(kFoxitFonts));
+  return pdfium::make_span(kFoxitFonts[index].m_pFontData,
+                           kFoxitFonts[index].m_dwSize);
+}
+
+// static
+pdfium::span<const uint8_t> CFX_FontMgr::GetGenericSansFont() {
+  return pdfium::make_span(kGenericSansFont.m_pFontData,
+                           kGenericSansFont.m_dwSize);
+}
+
+// static
+pdfium::span<const uint8_t> CFX_FontMgr::GetGenericSerifFont() {
+  return pdfium::make_span(kGenericSerifFont.m_pFontData,
+                           kGenericSerifFont.m_dwSize);
 }
 
 bool CFX_FontMgr::FreeTypeVersionSupportsHinting() const {
diff --git a/core/fxge/cfx_fontmgr.h b/core/fxge/cfx_fontmgr.h
index 144961e..2936e26 100644
--- a/core/fxge/cfx_fontmgr.h
+++ b/core/fxge/cfx_fontmgr.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,46 +7,45 @@
 #ifndef CORE_FXGE_CFX_FONTMGR_H_
 #define CORE_FXGE_CFX_FONTMGR_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <map>
 #include <memory>
+#include <tuple>
 
-#include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/fixed_uninit_data_vector.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/fx_freetype.h"
-#include "third_party/base/optional.h"
-#include "third_party/base/span.h"
+#include "core/fxge/cfx_face.h"
+#include "core/fxge/freetype/fx_freetype.h"
+#include "third_party/base/containers/span.h"
 
-class CFX_Face;
 class CFX_FontMapper;
-class CFX_SubstFont;
-class SystemFontInfoIface;
 
 class CFX_FontMgr {
  public:
   class FontDesc final : public Retainable, public Observable {
    public:
-    template <typename T, typename... Args>
-    friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+    CONSTRUCT_VIA_MAKE_RETAIN;
 
-    ~FontDesc() override;
-
-    pdfium::span<uint8_t> FontData() const {
-      return {m_pFontData.get(), m_Size};
-    }
+    pdfium::span<const uint8_t> FontData() const { return m_pFontData; }
     void SetFace(size_t index, CFX_Face* face);
     CFX_Face* GetFace(size_t index) const;
 
    private:
-    FontDesc(std::unique_ptr<uint8_t, FxFreeDeleter> pData, size_t size);
+    explicit FontDesc(FixedUninitDataVector<uint8_t> data);
+    ~FontDesc() override;
 
-    const size_t m_Size;
-    std::unique_ptr<uint8_t, FxFreeDeleter> const m_pFontData;
+    const FixedUninitDataVector<uint8_t> m_pFontData;
     ObservedPtr<CFX_Face> m_TTCFaces[16];
   };
 
-  static Optional<pdfium::span<const uint8_t>> GetBuiltinFont(size_t index);
+  // `index` must be less than `CFX_FontMapper::kNumStandardFonts`.
+  static pdfium::span<const uint8_t> GetStandardFont(size_t index);
+  static pdfium::span<const uint8_t> GetGenericSansFont();
+  static pdfium::span<const uint8_t> GetGenericSerifFont();
 
   CFX_FontMgr();
   ~CFX_FontMgr();
@@ -54,32 +53,19 @@
   RetainPtr<FontDesc> GetCachedFontDesc(const ByteString& face_name,
                                         int weight,
                                         bool bItalic);
-  RetainPtr<FontDesc> AddCachedFontDesc(
-      const ByteString& face_name,
-      int weight,
-      bool bItalic,
-      std::unique_ptr<uint8_t, FxFreeDeleter> pData,
-      uint32_t size);
+  RetainPtr<FontDesc> AddCachedFontDesc(const ByteString& face_name,
+                                        int weight,
+                                        bool bItalic,
+                                        FixedUninitDataVector<uint8_t> data);
 
-  RetainPtr<FontDesc> GetCachedTTCFontDesc(int ttc_size, uint32_t checksum);
-  RetainPtr<FontDesc> AddCachedTTCFontDesc(
-      int ttc_size,
-      uint32_t checksum,
-      std::unique_ptr<uint8_t, FxFreeDeleter> pData,
-      uint32_t size);
+  RetainPtr<FontDesc> GetCachedTTCFontDesc(size_t ttc_size, uint32_t checksum);
+  RetainPtr<FontDesc> AddCachedTTCFontDesc(size_t ttc_size,
+                                           uint32_t checksum,
+                                           FixedUninitDataVector<uint8_t> data);
 
-  RetainPtr<CFX_Face> NewFixedFace(const RetainPtr<FontDesc>& pDesc,
+  RetainPtr<CFX_Face> NewFixedFace(RetainPtr<FontDesc> pDesc,
                                    pdfium::span<const uint8_t> span,
-                                   int face_index);
-  RetainPtr<CFX_Face> FindSubstFont(const ByteString& face_name,
-                                    bool bTrueType,
-                                    uint32_t flags,
-                                    int weight,
-                                    int italic_angle,
-                                    int CharsetCP,
-                                    CFX_SubstFont* pSubstFont);
-
-  void SetSystemFontInfo(std::unique_ptr<SystemFontInfoIface> pFontInfo);
+                                   size_t face_index);
 
   // Always present.
   CFX_FontMapper* GetBuiltinMapper() const { return m_pBuiltinMapper.get(); }
@@ -94,7 +80,8 @@
   // Must come before |m_pBuiltinMapper| and |m_FaceMap|.
   ScopedFXFTLibraryRec const m_FTLibrary;
   std::unique_ptr<CFX_FontMapper> m_pBuiltinMapper;
-  std::map<ByteString, ObservedPtr<FontDesc>> m_FaceMap;
+  std::map<std::tuple<ByteString, int, bool>, ObservedPtr<FontDesc>> m_FaceMap;
+  std::map<std::tuple<size_t, uint32_t>, ObservedPtr<FontDesc>> m_TTCFaceMap;
   const bool m_FTLibrarySupportsHinting;
 };
 
diff --git a/core/fxge/cfx_gemodule.cpp b/core/fxge/cfx_gemodule.cpp
index 5d268bf..dc06b9d 100644
--- a/core/fxge/cfx_gemodule.cpp
+++ b/core/fxge/cfx_gemodule.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 #include "core/fxge/cfx_folderfontinfo.h"
 #include "core/fxge/cfx_fontcache.h"
 #include "core/fxge/cfx_fontmgr.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -19,28 +19,30 @@
 
 CFX_GEModule::CFX_GEModule(const char** pUserFontPaths)
     : m_pPlatform(PlatformIface::Create()),
-      m_pFontMgr(pdfium::MakeUnique<CFX_FontMgr>()),
-      m_pFontCache(pdfium::MakeUnique<CFX_FontCache>()),
+      m_pFontMgr(std::make_unique<CFX_FontMgr>()),
+      m_pFontCache(std::make_unique<CFX_FontCache>()),
       m_pUserFontPaths(pUserFontPaths) {}
 
 CFX_GEModule::~CFX_GEModule() = default;
 
 // static
 void CFX_GEModule::Create(const char** pUserFontPaths) {
-  ASSERT(!g_pGEModule);
+  DCHECK(!g_pGEModule);
   g_pGEModule = new CFX_GEModule(pUserFontPaths);
   g_pGEModule->m_pPlatform->Init();
+  g_pGEModule->GetFontMgr()->GetBuiltinMapper()->SetSystemFontInfo(
+      g_pGEModule->m_pPlatform->CreateDefaultSystemFontInfo());
 }
 
 // static
 void CFX_GEModule::Destroy() {
-  ASSERT(g_pGEModule);
+  DCHECK(g_pGEModule);
   delete g_pGEModule;
   g_pGEModule = nullptr;
 }
 
 // static
 CFX_GEModule* CFX_GEModule::Get() {
-  ASSERT(g_pGEModule);
+  DCHECK(g_pGEModule);
   return g_pGEModule;
 }
diff --git a/core/fxge/cfx_gemodule.h b/core/fxge/cfx_gemodule.h
index 47e2667..f6846ce 100644
--- a/core/fxge/cfx_gemodule.h
+++ b/core/fxge/cfx_gemodule.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,34 @@
 #ifndef CORE_FXGE_CFX_GEMODULE_H_
 #define CORE_FXGE_CFX_GEMODULE_H_
 
+#include <stdint.h>
+
 #include <memory>
 
+#include "build/build_config.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
+
+#if BUILDFLAG(IS_APPLE)
+#include "third_party/base/containers/span.h"
+#endif
+
 class CFX_FontCache;
 class CFX_FontMgr;
+class SystemFontInfoIface;
 
 class CFX_GEModule {
  public:
   class PlatformIface {
    public:
     static std::unique_ptr<PlatformIface> Create();
-    virtual ~PlatformIface() {}
+    virtual ~PlatformIface() = default;
 
     virtual void Init() = 0;
+    virtual std::unique_ptr<SystemFontInfoIface>
+    CreateDefaultSystemFontInfo() = 0;
+#if BUILDFLAG(IS_APPLE)
+    virtual void* CreatePlatformFont(pdfium::span<const uint8_t> font_span) = 0;
+#endif
   };
 
   static void Create(const char** pUserFontPaths);
@@ -38,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_glyphbitmap.cpp b/core/fxge/cfx_glyphbitmap.cpp
index cc28d51..f9898ba 100644
--- a/core/fxge/cfx_glyphbitmap.cpp
+++ b/core/fxge/cfx_glyphbitmap.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxge/cfx_glyphbitmap.h b/core/fxge/cfx_glyphbitmap.h
index 81a0547..cea62d2 100644
--- a/core/fxge/cfx_glyphbitmap.h
+++ b/core/fxge/cfx_glyphbitmap.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,6 @@
 #ifndef CORE_FXGE_CFX_GLYPHBITMAP_H_
 #define CORE_FXGE_CFX_GLYPHBITMAP_H_
 
-#include <vector>
-
 #include "core/fxcrt/retain_ptr.h"
 
 class CFX_DIBitmap;
diff --git a/core/fxge/cfx_glyphcache.cpp b/core/fxge/cfx_glyphcache.cpp
index ec9c886..ba308d4 100644
--- a/core/fxge/cfx_glyphcache.cpp
+++ b/core/fxge/cfx_glyphcache.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include "core/fxge/cfx_glyphcache.h"
 
+#include <stdarg.h>
+
 #include <algorithm>
 #include <limits>
 #include <memory>
@@ -13,29 +15,32 @@
 
 #include "build/build_config.h"
 #include "core/fxcrt/fx_codepage.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_fontmgr.h"
 #include "core/fxge/cfx_gemodule.h"
 #include "core/fxge/cfx_glyphbitmap.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_substfont.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_freetype.h"
-#include "core/fxge/render_defines.h"
+#include "core/fxge/freetype/fx_freetype.h"
 #include "core/fxge/scoped_font_transform.h"
 #include "third_party/base/numerics/safe_math.h"
-#include "third_party/base/ptr_util.h"
 
-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
-#include "third_party/skia/include/core/SkStream.h"
-#include "third_party/skia/include/core/SkTypeface.h"
+#if defined(_SKIA_SUPPORT_)
+#include "third_party/skia/include/core/SkStream.h"  // nogncheck
+#include "third_party/skia/include/core/SkTypeface.h"  // nogncheck
 
-#if defined(OS_WIN)
-#include "third_party/skia/include/core/SkFontMgr.h"
-#include "third_party/skia/include/ports/SkFontMgr_empty.h"
+#if BUILDFLAG(IS_WIN)
+#include "third_party/skia/include/core/SkFontMgr.h"  // nogncheck
+#include "third_party/skia/include/ports/SkFontMgr_empty.h"  // nogncheck
 #endif
 #endif
 
+#if BUILDFLAG(IS_APPLE)
+#include "core/fxge/cfx_textrenderoptions.h"
+#endif
+
 namespace {
 
 constexpr uint32_t kInvalidGlyphIndex = static_cast<uint32_t>(-1);
@@ -63,7 +68,7 @@
 void GenKey(UniqueKeyGen* pKeyGen,
             const CFX_Font* pFont,
             const CFX_Matrix& matrix,
-            uint32_t dest_width,
+            int dest_width,
             int anti_alias,
             bool bNative) {
   int nMatrixA = static_cast<int>(matrix.a * 10000);
@@ -71,6 +76,7 @@
   int nMatrixC = static_cast<int>(matrix.c * 10000);
   int nMatrixD = static_cast<int>(matrix.d * 10000);
 
+#if BUILDFLAG(IS_APPLE)
   if (bNative) {
     if (pFont->GetSubstFont()) {
       pKeyGen->Generate(10, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
@@ -81,22 +87,27 @@
       pKeyGen->Generate(7, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
                         anti_alias, 3);
     }
+    return;
+  }
+#else
+  CHECK(!bNative);
+#endif
+
+  if (pFont->GetSubstFont()) {
+    pKeyGen->Generate(9, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
+                      anti_alias, pFont->GetSubstFont()->m_Weight,
+                      pFont->GetSubstFont()->m_ItalicAngle,
+                      pFont->IsVertical());
   } else {
-    if (pFont->GetSubstFont()) {
-      pKeyGen->Generate(9, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
-                        anti_alias, pFont->GetSubstFont()->m_Weight,
-                        pFont->GetSubstFont()->m_ItalicAngle,
-                        pFont->IsVertical());
-    } else {
-      pKeyGen->Generate(6, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
-                        anti_alias);
-    }
+    pKeyGen->Generate(6, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
+                      anti_alias);
   }
 }
 
 }  // namespace
 
-CFX_GlyphCache::CFX_GlyphCache(RetainPtr<CFX_Face> face) : m_Face(face) {}
+CFX_GlyphCache::CFX_GlyphCache(RetainPtr<CFX_Face> face)
+    : m_Face(std::move(face)) {}
 
 CFX_GlyphCache::~CFX_GlyphCache() = default;
 
@@ -105,7 +116,7 @@
     uint32_t glyph_index,
     bool bFontStyle,
     const CFX_Matrix& matrix,
-    uint32_t dest_width,
+    int dest_width,
     int anti_alias) {
   if (!GetFaceRec())
     return nullptr;
@@ -119,59 +130,52 @@
   const CFX_SubstFont* pSubstFont = pFont->GetSubstFont();
   if (pSubstFont) {
     bUseCJKSubFont = pSubstFont->m_bSubstCJK && bFontStyle;
-    int skew = 0;
+    int angle;
     if (bUseCJKSubFont)
-      skew = pSubstFont->m_bItalicCJK ? -15 : 0;
+      angle = pSubstFont->m_bItalicCJK ? -15 : 0;
     else
-      skew = pSubstFont->m_ItalicAngle;
-    if (skew) {
-      // |skew| is nonpositive so |-skew| is used as the index. We need to make
-      // sure |skew| != INT_MIN since -INT_MIN is undefined.
-      if (skew <= 0 && skew != std::numeric_limits<int>::min() &&
-          static_cast<size_t>(-skew) < CFX_Font::kAngleSkewArraySize) {
-        skew = -CFX_Font::s_AngleSkew[-skew];
-      } else {
-        skew = -58;
-      }
+      angle = pSubstFont->m_ItalicAngle;
+    if (angle) {
+      int skew = CFX_Font::GetSkewFromAngle(angle);
       if (pFont->IsVertical())
         ft_matrix.yx += ft_matrix.yy * skew / 100;
       else
         ft_matrix.xy -= ft_matrix.xx * skew / 100;
     }
-    if (pSubstFont->m_bFlagMM) {
+    if (pSubstFont->IsBuiltInGenericFont()) {
       pFont->AdjustMMParams(glyph_index, dest_width,
                             pFont->GetSubstFont()->m_Weight);
     }
   }
+
   ScopedFontTransform scoped_transform(GetFace(), &ft_matrix);
-  int load_flags = (GetFaceRec()->face_flags & FT_FACE_FLAG_SFNT)
-                       ? FT_LOAD_NO_BITMAP
-                       : (FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING);
+  int load_flags = FT_LOAD_NO_BITMAP | FT_LOAD_PEDANTIC;
+  if (!(GetFaceRec()->face_flags & FT_FACE_FLAG_SFNT))
+    load_flags |= FT_LOAD_NO_HINTING;
   int error = FT_Load_Glyph(GetFaceRec(), glyph_index, load_flags);
   if (error) {
     // if an error is returned, try to reload glyphs without hinting.
-    if (load_flags & FT_LOAD_NO_HINTING || load_flags & FT_LOAD_NO_SCALE)
+    if (load_flags & FT_LOAD_NO_HINTING)
       return nullptr;
 
     load_flags |= FT_LOAD_NO_HINTING;
+    load_flags &= ~FT_LOAD_PEDANTIC;
     error = FT_Load_Glyph(GetFaceRec(), glyph_index, load_flags);
     if (error)
       return nullptr;
   }
-  int weight = 0;
+
+  int weight;
   if (bUseCJKSubFont)
     weight = pSubstFont->m_WeightCJK;
   else
     weight = pSubstFont ? pSubstFont->m_Weight : 0;
-  if (pSubstFont && !pSubstFont->m_bFlagMM && weight > 400) {
+  if (pSubstFont && !pSubstFont->IsBuiltInGenericFont() && weight > 400) {
     uint32_t index = (weight - 400) / 10;
-    if (index >= CFX_Font::kWeightPowArraySize)
+    pdfium::base::CheckedNumeric<signed long> level =
+        CFX_Font::GetWeightLevel(pSubstFont->m_Charset, index);
+    if (level.ValueOrDefault(-1) < 0)
       return nullptr;
-    pdfium::base::CheckedNumeric<signed long> level = 0;
-    if (pSubstFont->m_Charset == FX_CHARSET_ShiftJIS)
-      level = CFX_Font::s_WeightPow_SHIFTJIS[index] * 2;
-    else
-      level = CFX_Font::s_WeightPow_11[index];
 
     level = level *
             (abs(static_cast<int>(ft_matrix.xx)) +
@@ -191,15 +195,16 @@
   if (bmwidth > kMaxGlyphDimension || bmheight > kMaxGlyphDimension)
     return nullptr;
   int dib_width = bmwidth;
-  auto pGlyphBitmap = pdfium::MakeUnique<CFX_GlyphBitmap>(
-      FXFT_Get_Glyph_BitmapLeft(GetFaceRec()),
-      FXFT_Get_Glyph_BitmapTop(GetFaceRec()));
-  pGlyphBitmap->GetBitmap()->Create(
-      dib_width, bmheight,
-      anti_alias == FT_RENDER_MODE_MONO ? FXDIB_1bppMask : FXDIB_8bppMask);
+  auto pGlyphBitmap =
+      std::make_unique<CFX_GlyphBitmap>(FXFT_Get_Glyph_BitmapLeft(GetFaceRec()),
+                                        FXFT_Get_Glyph_BitmapTop(GetFaceRec()));
+  pGlyphBitmap->GetBitmap()->Create(dib_width, bmheight,
+                                    anti_alias == FT_RENDER_MODE_MONO
+                                        ? FXDIB_Format::k1bppMask
+                                        : FXDIB_Format::k8bppMask);
   int dest_pitch = pGlyphBitmap->GetBitmap()->GetPitch();
   int src_pitch = FXFT_Get_Bitmap_Pitch(FXFT_Get_Glyph_Bitmap(GetFaceRec()));
-  uint8_t* pDestBuf = pGlyphBitmap->GetBitmap()->GetBuffer();
+  uint8_t* pDestBuf = pGlyphBitmap->GetBitmap()->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 &&
@@ -223,9 +228,9 @@
   return pGlyphBitmap;
 }
 
-const CFX_PathData* CFX_GlyphCache::LoadGlyphPath(const CFX_Font* pFont,
-                                                  uint32_t glyph_index,
-                                                  uint32_t dest_width) {
+const CFX_Path* CFX_GlyphCache::LoadGlyphPath(const CFX_Font* pFont,
+                                              uint32_t glyph_index,
+                                              int dest_width) {
   if (!GetFaceRec() || glyph_index == kInvalidGlyphIndex)
     return nullptr;
 
@@ -239,33 +244,33 @@
   if (it != m_PathMap.end())
     return it->second.get();
 
-  CFX_PathData* pGlyphPath = pFont->LoadGlyphPathImpl(glyph_index, dest_width);
-  m_PathMap[key] = std::unique_ptr<CFX_PathData>(pGlyphPath);
-  return pGlyphPath;
+  m_PathMap[key] = pFont->LoadGlyphPathImpl(glyph_index, dest_width);
+  return m_PathMap[key].get();
 }
 
-const CFX_GlyphBitmap* CFX_GlyphCache::LoadGlyphBitmap(const CFX_Font* pFont,
-                                                       uint32_t glyph_index,
-                                                       bool bFontStyle,
-                                                       const CFX_Matrix& matrix,
-                                                       uint32_t dest_width,
-                                                       int anti_alias,
-                                                       int* pTextFlags) {
+const CFX_GlyphBitmap* CFX_GlyphCache::LoadGlyphBitmap(
+    const CFX_Font* pFont,
+    uint32_t glyph_index,
+    bool bFontStyle,
+    const CFX_Matrix& matrix,
+    int dest_width,
+    int anti_alias,
+    CFX_TextRenderOptions* text_options) {
   if (glyph_index == kInvalidGlyphIndex)
     return nullptr;
 
   UniqueKeyGen keygen;
-#if defined(OS_MACOSX)
-  const bool bNative = !(*pTextFlags & FXTEXT_NO_NATIVETEXT);
+#if BUILDFLAG(IS_APPLE)
+  const bool bNative = text_options->native_text;
 #else
   const bool bNative = false;
 #endif
   GenKey(&keygen, pFont, matrix, dest_width, anti_alias, bNative);
   ByteString FaceGlyphsKey(keygen.key_, keygen.key_len_);
 
-#if defined(OS_MACOSX) && !defined _SKIA_SUPPORT_ && \
-    !defined _SKIA_SUPPORT_PATHS_
-  const bool bDoLookUp = !!(*pTextFlags & FXTEXT_NO_NATIVETEXT);
+#if BUILDFLAG(IS_APPLE)
+  const bool bDoLookUp = !text_options->native_text ||
+                         CFX_DefaultRenderDevice::SkiaIsDefaultRenderer();
 #else
   const bool bDoLookUp = true;
 #endif
@@ -274,8 +279,9 @@
                              bFontStyle, dest_width, anti_alias);
   }
 
-#if defined(OS_MACOSX) && !defined _SKIA_SUPPORT_ && \
-    !defined _SKIA_SUPPORT_PATHS_
+#if BUILDFLAG(IS_APPLE)
+  DCHECK(!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer());
+
   std::unique_ptr<CFX_GlyphBitmap> pGlyphBitmap;
   auto it = m_SizeMap.find(FaceGlyphsKey);
   if (it != m_SizeMap.end()) {
@@ -306,34 +312,44 @@
   }
   GenKey(&keygen, pFont, matrix, dest_width, anti_alias, /*bNative=*/false);
   ByteString FaceGlyphsKey2(keygen.key_, keygen.key_len_);
-  *pTextFlags |= FXTEXT_NO_NATIVETEXT;
+  text_options->native_text = false;
   return LookUpGlyphBitmap(pFont, matrix, FaceGlyphsKey2, glyph_index,
                            bFontStyle, dest_width, anti_alias);
-#endif
+#endif  // BUILDFLAG(IS_APPLE)
 }
 
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
+int CFX_GlyphCache::GetGlyphWidth(const CFX_Font* font,
+                                  uint32_t glyph_index,
+                                  int dest_width,
+                                  int weight) {
+  const WidthMapKey key = std::make_tuple(glyph_index, dest_width, weight);
+  auto it = m_WidthMap.find(key);
+  if (it != m_WidthMap.end()) {
+    return it->second;
+  }
+
+  m_WidthMap[key] = font->GetGlyphWidthImpl(glyph_index, dest_width, weight);
+  return m_WidthMap[key];
+}
+
+#if defined(_SKIA_SUPPORT_)
 CFX_TypeFace* CFX_GlyphCache::GetDeviceCache(const CFX_Font* pFont) {
   if (!m_pTypeface) {
     pdfium::span<const uint8_t> span = pFont->GetFontSpan();
     m_pTypeface = SkTypeface::MakeFromStream(
-        pdfium::MakeUnique<SkMemoryStream>(span.data(), span.size()));
+        std::make_unique<SkMemoryStream>(span.data(), span.size()));
   }
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   if (!m_pTypeface) {
     sk_sp<SkFontMgr> customMgr(SkFontMgr_New_Custom_Empty());
     pdfium::span<const uint8_t> span = pFont->GetFontSpan();
     m_pTypeface = customMgr->makeFromStream(
-        pdfium::MakeUnique<SkMemoryStream>(span.data(), span.size()));
+        std::make_unique<SkMemoryStream>(span.data(), span.size()));
   }
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
   return m_pTypeface.get();
 }
-#endif  // defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
-
-#if !defined(OS_MACOSX)
-void CFX_GlyphCache::InitPlatform() {}
-#endif
+#endif  // defined(_SKIA_SUPPORT_)
 
 CFX_GlyphBitmap* CFX_GlyphCache::LookUpGlyphBitmap(
     const CFX_Font* pFont,
@@ -341,7 +357,7 @@
     const ByteString& FaceGlyphsKey,
     uint32_t glyph_index,
     bool bFontStyle,
-    uint32_t dest_width,
+    int dest_width,
     int anti_alias) {
   SizeGlyphCache* pSizeCache;
   auto it = m_SizeMap.find(FaceGlyphsKey);
diff --git a/core/fxge/cfx_glyphcache.h b/core/fxge/cfx_glyphcache.h
index 5a24424..c71ae3c 100644
--- a/core/fxge/cfx_glyphcache.h
+++ b/core/fxge/cfx_glyphcache.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,79 +11,82 @@
 #include <memory>
 #include <tuple>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/cfx_face.h"
 
-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
+#if defined(_SKIA_SUPPORT_)
 #include "core/fxge/fx_font.h"
-#include "third_party/skia/include/core/SkTypeface.h"
+#include "third_party/skia/include/core/SkRefCnt.h"  // nogncheck
 #endif
 
 class CFX_Font;
 class CFX_GlyphBitmap;
 class CFX_Matrix;
-class CFX_PathData;
+class CFX_Path;
+struct CFX_TextRenderOptions;
 
-class CFX_GlyphCache : public Retainable, public Observable {
+class CFX_GlyphCache final : public Retainable, public Observable {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  ~CFX_GlyphCache() override;
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   const CFX_GlyphBitmap* LoadGlyphBitmap(const CFX_Font* pFont,
                                          uint32_t glyph_index,
                                          bool bFontStyle,
                                          const CFX_Matrix& matrix,
-                                         uint32_t dest_width,
+                                         int dest_width,
                                          int anti_alias,
-                                         int* pTextFlags);
-  const CFX_PathData* LoadGlyphPath(const CFX_Font* pFont,
-                                    uint32_t glyph_index,
-                                    uint32_t dest_width);
+                                         CFX_TextRenderOptions* text_options);
+  const CFX_Path* LoadGlyphPath(const CFX_Font* pFont,
+                                uint32_t glyph_index,
+                                int dest_width);
+  int GetGlyphWidth(const CFX_Font* font,
+                    uint32_t glyph_index,
+                    int dest_width,
+                    int weight);
 
   RetainPtr<CFX_Face> GetFace() { return m_Face; }
   FXFT_FaceRec* GetFaceRec() { return m_Face ? m_Face->GetRec() : nullptr; }
 
-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
+#if defined(_SKIA_SUPPORT_)
   CFX_TypeFace* GetDeviceCache(const CFX_Font* pFont);
 #endif
 
  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>
-  using PathMapKey = std::tuple<uint32_t, uint32_t, int, int, bool>;
+  using PathMapKey = std::tuple<uint32_t, int, int, int, bool>;
+  // <glyph_index, dest_width, weight>
+  using WidthMapKey = std::tuple<uint32_t, int, int>;
 
   std::unique_ptr<CFX_GlyphBitmap> RenderGlyph(const CFX_Font* pFont,
                                                uint32_t glyph_index,
                                                bool bFontStyle,
                                                const CFX_Matrix& matrix,
-                                               uint32_t dest_width,
+                                               int dest_width,
                                                int anti_alias);
   std::unique_ptr<CFX_GlyphBitmap> RenderGlyph_Nativetext(
       const CFX_Font* pFont,
       uint32_t glyph_index,
       const CFX_Matrix& matrix,
-      uint32_t dest_width,
+      int dest_width,
       int anti_alias);
   CFX_GlyphBitmap* LookUpGlyphBitmap(const CFX_Font* pFont,
                                      const CFX_Matrix& matrix,
                                      const ByteString& FaceGlyphsKey,
                                      uint32_t glyph_index,
                                      bool bFontStyle,
-                                     uint32_t dest_width,
+                                     int dest_width,
                                      int anti_alias);
-  void InitPlatform();
-  void DestroyPlatform();
-
   RetainPtr<CFX_Face> const m_Face;
   std::map<ByteString, SizeGlyphCache> m_SizeMap;
-  std::map<PathMapKey, std::unique_ptr<CFX_PathData>> m_PathMap;
-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
+  std::map<PathMapKey, std::unique_ptr<CFX_Path>> m_PathMap;
+  std::map<WidthMapKey, int> m_WidthMap;
+#if defined(_SKIA_SUPPORT_)
   sk_sp<SkTypeface> m_pTypeface;
 #endif
 };
diff --git a/core/fxge/cfx_graphstate.cpp b/core/fxge/cfx_graphstate.cpp
index 726c0c6..18f32a3 100644
--- a/core/fxge/cfx_graphstate.cpp
+++ b/core/fxge/cfx_graphstate.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -29,8 +29,30 @@
   pData->m_DashArray = std::move(dashes);
 }
 
+void CFX_GraphState::SetLineDashPhase(float phase) {
+  CFX_GraphStateData* pData = m_Ref.GetPrivateCopy();
+  pData->m_DashPhase = phase;
+}
+
+std::vector<float> CFX_GraphState::GetLineDashArray() const {
+  std::vector<float> ret;
+
+  if (m_Ref.GetObject())
+    ret = m_Ref.GetObject()->m_DashArray;
+
+  return ret;
+}
+
+size_t CFX_GraphState::GetLineDashSize() const {
+  return m_Ref.GetObject() ? m_Ref.GetObject()->m_DashArray.size() : 0;
+}
+
+float CFX_GraphState::GetLineDashPhase() const {
+  return m_Ref.GetObject() ? m_Ref.GetObject()->m_DashPhase : 1.0f;
+}
+
 float CFX_GraphState::GetLineWidth() const {
-  return m_Ref.GetObject() ? m_Ref.GetObject()->m_LineWidth : 1.f;
+  return m_Ref.GetObject() ? m_Ref.GetObject()->m_LineWidth : 1.0f;
 }
 
 void CFX_GraphState::SetLineWidth(float width) {
@@ -39,7 +61,7 @@
 
 CFX_GraphStateData::LineCap CFX_GraphState::GetLineCap() const {
   return m_Ref.GetObject() ? m_Ref.GetObject()->m_LineCap
-                           : CFX_GraphStateData::LineCapButt;
+                           : CFX_GraphStateData::LineCap::kButt;
 }
 void CFX_GraphState::SetLineCap(CFX_GraphStateData::LineCap cap) {
   m_Ref.GetPrivateCopy()->m_LineCap = cap;
@@ -47,7 +69,7 @@
 
 CFX_GraphStateData::LineJoin CFX_GraphState::GetLineJoin() const {
   return m_Ref.GetObject() ? m_Ref.GetObject()->m_LineJoin
-                           : CFX_GraphStateData::LineJoinMiter;
+                           : CFX_GraphStateData::LineJoin::kMiter;
 }
 
 void CFX_GraphState::SetLineJoin(CFX_GraphStateData::LineJoin join) {
diff --git a/core/fxge/cfx_graphstate.h b/core/fxge/cfx_graphstate.h
index 20955f8..ed53479 100644
--- a/core/fxge/cfx_graphstate.h
+++ b/core/fxge/cfx_graphstate.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,8 @@
 #ifndef CORE_FXGE_CFX_GRAPHSTATE_H_
 #define CORE_FXGE_CFX_GRAPHSTATE_H_
 
+#include <stddef.h>
+
 #include <vector>
 
 #include "core/fxcrt/shared_copy_on_write.h"
@@ -21,6 +23,10 @@
   void Emplace();
 
   void SetLineDash(std::vector<float> dashes, float phase, float scale);
+  void SetLineDashPhase(float phase);
+  std::vector<float> GetLineDashArray() const;
+  size_t GetLineDashSize() const;
+  float GetLineDashPhase() const;
 
   float GetLineWidth() const;
   void SetLineWidth(float width);
diff --git a/core/fxge/cfx_graphstatedata.cpp b/core/fxge/cfx_graphstatedata.cpp
index 43f6254..5bfa32e 100644
--- a/core/fxge/cfx_graphstatedata.cpp
+++ b/core/fxge/cfx_graphstatedata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,15 +10,16 @@
 
 CFX_GraphStateData::CFX_GraphStateData(const CFX_GraphStateData& src) = default;
 
-CFX_GraphStateData::CFX_GraphStateData(CFX_GraphStateData&& src) = default;
+CFX_GraphStateData::CFX_GraphStateData(CFX_GraphStateData&& src) noexcept =
+    default;
 
 CFX_GraphStateData::~CFX_GraphStateData() = default;
 
 CFX_GraphStateData& CFX_GraphStateData::operator=(
     const CFX_GraphStateData& that) = default;
 
-CFX_GraphStateData& CFX_GraphStateData::operator=(CFX_GraphStateData&& that) =
-    default;
+CFX_GraphStateData& CFX_GraphStateData::operator=(
+    CFX_GraphStateData&& that) noexcept = default;
 
 CFX_RetainableGraphStateData::CFX_RetainableGraphStateData() = default;
 
diff --git a/core/fxge/cfx_graphstatedata.h b/core/fxge/cfx_graphstatedata.h
index e8ece9f..e7a958e 100644
--- a/core/fxge/cfx_graphstatedata.h
+++ b/core/fxge/cfx_graphstatedata.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,46 +7,38 @@
 #ifndef CORE_FXGE_CFX_GRAPHSTATEDATA_H_
 #define CORE_FXGE_CFX_GRAPHSTATEDATA_H_
 
+#include <stdint.h>
+
 #include <vector>
 
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 
 class CFX_GraphStateData {
  public:
-  enum LineCap : uint8_t {
-    LineCapButt = 0,
-    LineCapRound = 1,
-    LineCapSquare = 2
-  };
+  enum class LineCap : uint8_t { kButt = 0, kRound = 1, kSquare = 2 };
 
-  enum LineJoin : uint8_t {
-    LineJoinMiter = 0,
-    LineJoinRound = 1,
-    LineJoinBevel = 2
-  };
+  enum class LineJoin : uint8_t { kMiter = 0, kRound = 1, kBevel = 2 };
 
   CFX_GraphStateData();
   CFX_GraphStateData(const CFX_GraphStateData& src);
-  CFX_GraphStateData(CFX_GraphStateData&& src);
+  CFX_GraphStateData(CFX_GraphStateData&& src) noexcept;
   ~CFX_GraphStateData();
 
   CFX_GraphStateData& operator=(const CFX_GraphStateData& that);
-  CFX_GraphStateData& operator=(CFX_GraphStateData&& that);
+  CFX_GraphStateData& operator=(CFX_GraphStateData&& that) noexcept;
 
-  LineCap m_LineCap = LineCapButt;
-  LineJoin m_LineJoin = LineJoinMiter;
+  LineCap m_LineCap = LineCap::kButt;
+  LineJoin m_LineJoin = LineJoin::kMiter;
   float m_DashPhase = 0.0f;
   float m_MiterLimit = 10.0f;
   float m_LineWidth = 1.0f;
   std::vector<float> m_DashArray;
 };
 
-class CFX_RetainableGraphStateData : public Retainable,
-                                     public CFX_GraphStateData {
+class CFX_RetainableGraphStateData final : public Retainable,
+                                           public CFX_GraphStateData {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   RetainPtr<CFX_RetainableGraphStateData> Clone() const;
 
diff --git a/core/fxge/cfx_path.cpp b/core/fxge/cfx_path.cpp
new file mode 100644
index 0000000..d52c21e
--- /dev/null
+++ b/core/fxge/cfx_path.cpp
@@ -0,0 +1,452 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxge/cfx_path.h"
+
+#include <math.h>
+
+#include <algorithm>
+#include <iterator>
+
+#include "core/fxcrt/fx_system.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/numerics/safe_math.h"
+
+namespace {
+
+bool IsRectPreTransform(const std::vector<CFX_Path::Point>& points) {
+  if (points.size() != 5 && points.size() != 4)
+    return false;
+
+  if (points.size() == 5 && points[0].m_Point != points[4].m_Point)
+    return false;
+
+  if (points[0].m_Point == points[2].m_Point ||
+      points[1].m_Point == points[3].m_Point) {
+    return false;
+  }
+
+  for (size_t i = 1; i < points.size(); ++i) {
+    if (points[i].m_Type != CFX_Path::Point::Type::kLine)
+      return false;
+  }
+  return true;
+}
+
+bool XYBothNotEqual(const CFX_PointF& p1, const CFX_PointF& p2) {
+  return p1.x != p2.x && p1.y != p2.y;
+}
+
+bool IsRectImpl(const std::vector<CFX_Path::Point>& points) {
+  if (!IsRectPreTransform(points))
+    return false;
+
+  for (int i = 1; i < 4; i++) {
+    if (XYBothNotEqual(points[i].m_Point, points[i - 1].m_Point))
+      return false;
+  }
+
+  if (XYBothNotEqual(points[0].m_Point, points[3].m_Point))
+    return false;
+
+  return true;
+}
+
+CFX_FloatRect CreateRectFromPoints(const CFX_PointF& p1, const CFX_PointF& p2) {
+  CFX_FloatRect rect(p1.x, p1.y, p2.x, p2.y);
+  rect.Normalize();
+  return rect;
+}
+
+bool PathPointsNeedNormalization(const std::vector<CFX_Path::Point>& points) {
+  return points.size() > 5;
+}
+
+std::vector<CFX_Path::Point> GetNormalizedPoints(
+    const std::vector<CFX_Path::Point>& points) {
+  DCHECK(PathPointsNeedNormalization(points));
+
+  if (points[0].m_Point != points.back().m_Point)
+    return {};
+
+  std::vector<CFX_Path::Point> normalized;
+  normalized.reserve(6);
+  normalized.push_back(points[0]);
+  for (auto it = points.begin() + 1; it != points.end(); ++it) {
+    // Exactly 5 points left. Stop normalizing and take what is left.
+    if (normalized.size() + std::distance(it, points.end()) == 5) {
+      std::copy(it, points.end(), std::back_inserter(normalized));
+      break;
+    }
+
+    // If the line does not move, skip this point.
+    const auto& point = *it;
+    if (point.m_Type == CFX_Path::Point::Type::kLine && !point.m_CloseFigure &&
+        !normalized.back().m_CloseFigure &&
+        point.m_Point == normalized.back().m_Point) {
+      continue;
+    }
+
+    normalized.push_back(point);
+
+    // Too many points. Not considered as a rectangle.
+    if (normalized.size() > 5)
+      return {};
+  }
+
+  DCHECK_EQ(5u, normalized.size());
+  return normalized;
+}
+
+void UpdateLineEndPoints(CFX_FloatRect* rect,
+                         const CFX_PointF& start_pos,
+                         const CFX_PointF& end_pos,
+                         float hw) {
+  if (start_pos.x == end_pos.x) {
+    if (start_pos.y == end_pos.y) {
+      rect->UpdateRect(end_pos + CFX_PointF(hw, hw));
+      rect->UpdateRect(end_pos - CFX_PointF(hw, hw));
+      return;
+    }
+
+    float point_y;
+    if (end_pos.y < start_pos.y)
+      point_y = end_pos.y - hw;
+    else
+      point_y = end_pos.y + hw;
+
+    rect->UpdateRect(CFX_PointF(end_pos.x + hw, point_y));
+    rect->UpdateRect(CFX_PointF(end_pos.x - hw, point_y));
+    return;
+  }
+
+  if (start_pos.y == end_pos.y) {
+    float point_x;
+    if (end_pos.x < start_pos.x)
+      point_x = end_pos.x - hw;
+    else
+      point_x = end_pos.x + hw;
+
+    rect->UpdateRect(CFX_PointF(point_x, end_pos.y + hw));
+    rect->UpdateRect(CFX_PointF(point_x, end_pos.y - hw));
+    return;
+  }
+
+  CFX_PointF diff = end_pos - start_pos;
+  float ll = FXSYS_sqrt2(diff.x, diff.y);
+  float mx = end_pos.x + hw * diff.x / ll;
+  float my = end_pos.y + hw * diff.y / ll;
+  float dx1 = hw * diff.y / ll;
+  float dy1 = hw * diff.x / ll;
+  rect->UpdateRect(CFX_PointF(mx - dx1, my + dy1));
+  rect->UpdateRect(CFX_PointF(mx + dx1, my - dy1));
+}
+
+void UpdateLineJoinPoints(CFX_FloatRect* rect,
+                          const CFX_PointF& start_pos,
+                          const CFX_PointF& mid_pos,
+                          const CFX_PointF& end_pos,
+                          float half_width,
+                          float miter_limit) {
+  float start_k = 0;
+  float start_c = 0;
+  float end_k = 0;
+  float end_c = 0;
+  float start_len = 0;
+  float start_dc = 0;
+  float end_len = 0;
+  float end_dc = 0;
+  float one_twentieth = 1.0f / 20;
+
+  bool bStartVert = fabs(start_pos.x - mid_pos.x) < one_twentieth;
+  bool bEndVert = fabs(mid_pos.x - end_pos.x) < one_twentieth;
+  if (bStartVert && bEndVert) {
+    int start_dir = mid_pos.y > start_pos.y ? 1 : -1;
+    float point_y = mid_pos.y + half_width * start_dir;
+    rect->UpdateRect(CFX_PointF(mid_pos.x + half_width, point_y));
+    rect->UpdateRect(CFX_PointF(mid_pos.x - half_width, point_y));
+    return;
+  }
+
+  if (!bStartVert) {
+    CFX_PointF start_to_mid = start_pos - mid_pos;
+    start_k = (mid_pos.y - start_pos.y) / (mid_pos.x - start_pos.x);
+    start_c = mid_pos.y - (start_k * mid_pos.x);
+    start_len = FXSYS_sqrt2(start_to_mid.x, start_to_mid.y);
+    start_dc = fabsf(half_width * start_len / start_to_mid.x);
+  }
+  if (!bEndVert) {
+    CFX_PointF end_to_mid = end_pos - mid_pos;
+    end_k = end_to_mid.y / end_to_mid.x;
+    end_c = mid_pos.y - (end_k * mid_pos.x);
+    end_len = FXSYS_sqrt2(end_to_mid.x, end_to_mid.y);
+    end_dc = fabs(half_width * end_len / end_to_mid.x);
+  }
+  if (bStartVert) {
+    CFX_PointF outside(start_pos.x, 0);
+    if (end_pos.x < start_pos.x)
+      outside.x += half_width;
+    else
+      outside.x -= half_width;
+
+    if (start_pos.y < (end_k * start_pos.x) + end_c)
+      outside.y = (end_k * outside.x) + end_c + end_dc;
+    else
+      outside.y = (end_k * outside.x) + end_c - end_dc;
+
+    rect->UpdateRect(outside);
+    return;
+  }
+
+  if (bEndVert) {
+    CFX_PointF outside(end_pos.x, 0);
+    if (start_pos.x < end_pos.x)
+      outside.x += half_width;
+    else
+      outside.x -= half_width;
+
+    if (end_pos.y < (start_k * end_pos.x) + start_c)
+      outside.y = (start_k * outside.x) + start_c + start_dc;
+    else
+      outside.y = (start_k * outside.x) + start_c - start_dc;
+
+    rect->UpdateRect(outside);
+    return;
+  }
+
+  if (fabs(start_k - end_k) < one_twentieth) {
+    int start_dir = mid_pos.x > start_pos.x ? 1 : -1;
+    int end_dir = end_pos.x > mid_pos.x ? 1 : -1;
+    if (start_dir == end_dir)
+      UpdateLineEndPoints(rect, mid_pos, end_pos, half_width);
+    else
+      UpdateLineEndPoints(rect, start_pos, mid_pos, half_width);
+    return;
+  }
+
+  float start_outside_c = start_c;
+  if (end_pos.y < (start_k * end_pos.x) + start_c)
+    start_outside_c += start_dc;
+  else
+    start_outside_c -= start_dc;
+
+  float end_outside_c = end_c;
+  if (start_pos.y < (end_k * start_pos.x) + end_c)
+    end_outside_c += end_dc;
+  else
+    end_outside_c -= end_dc;
+
+  float join_x = (end_outside_c - start_outside_c) / (start_k - end_k);
+  float join_y = start_k * join_x + start_outside_c;
+  rect->UpdateRect(CFX_PointF(join_x, join_y));
+}
+
+}  // namespace
+
+CFX_Path::Point::Point() = default;
+
+CFX_Path::Point::Point(const CFX_PointF& point, Type type, bool close)
+    : m_Point(point), m_Type(type), m_CloseFigure(close) {}
+
+CFX_Path::Point::Point(const Point& other) = default;
+
+CFX_Path::Point::~Point() = default;
+
+CFX_Path::CFX_Path() = default;
+
+CFX_Path::CFX_Path(const CFX_Path& src) = default;
+
+CFX_Path::CFX_Path(CFX_Path&& src) noexcept = default;
+
+CFX_Path::~CFX_Path() = default;
+
+void CFX_Path::Clear() {
+  m_Points.clear();
+}
+
+void CFX_Path::ClosePath() {
+  if (m_Points.empty())
+    return;
+  m_Points.back().m_CloseFigure = true;
+}
+
+void CFX_Path::Append(const CFX_Path& src, const CFX_Matrix* matrix) {
+  if (src.m_Points.empty())
+    return;
+
+  size_t cur_size = m_Points.size();
+  m_Points.insert(m_Points.end(), src.m_Points.begin(), src.m_Points.end());
+
+  if (!matrix)
+    return;
+
+  for (size_t i = cur_size; i < m_Points.size(); i++)
+    m_Points[i].m_Point = matrix->Transform(m_Points[i].m_Point);
+}
+
+void CFX_Path::AppendPoint(const CFX_PointF& point, Point::Type type) {
+  m_Points.push_back(Point(point, type, /*close=*/false));
+}
+
+void CFX_Path::AppendPointAndClose(const CFX_PointF& point, Point::Type type) {
+  m_Points.push_back(Point(point, type, /*close=*/true));
+}
+
+void CFX_Path::AppendLine(const CFX_PointF& pt1, const CFX_PointF& pt2) {
+  if (m_Points.empty() || fabs(m_Points.back().m_Point.x - pt1.x) > 0.001 ||
+      fabs(m_Points.back().m_Point.y - pt1.y) > 0.001) {
+    AppendPoint(pt1, CFX_Path::Point::Type::kMove);
+  }
+  AppendPoint(pt2, CFX_Path::Point::Type::kLine);
+}
+
+void CFX_Path::AppendFloatRect(const CFX_FloatRect& rect) {
+  return AppendRect(rect.left, rect.bottom, rect.right, rect.top);
+}
+
+void CFX_Path::AppendRect(float left, float bottom, float right, float top) {
+  CFX_PointF left_bottom(left, bottom);
+  CFX_PointF left_top(left, top);
+  CFX_PointF right_top(right, top);
+  CFX_PointF right_bottom(right, bottom);
+
+  AppendLine(left_bottom, left_top);
+  AppendLine(left_top, right_top);
+  AppendLine(right_top, right_bottom);
+  AppendLine(right_bottom, left_bottom);
+  ClosePath();
+}
+
+CFX_FloatRect CFX_Path::GetBoundingBox() const {
+  if (m_Points.empty())
+    return CFX_FloatRect();
+
+  CFX_FloatRect rect(m_Points[0].m_Point);
+  for (size_t i = 1; i < m_Points.size(); ++i)
+    rect.UpdateRect(m_Points[i].m_Point);
+  return rect;
+}
+
+CFX_FloatRect CFX_Path::GetBoundingBoxForStrokePath(float line_width,
+                                                    float miter_limit) const {
+  CFX_FloatRect rect(100000.0f, 100000.0f, -100000.0f, -100000.0f);
+  size_t iPoint = 0;
+  float half_width = line_width;
+  size_t iStartPoint = 0;
+  size_t iEndPoint = 0;
+  size_t iMiddlePoint = 0;
+  bool bJoin;
+  while (iPoint < m_Points.size()) {
+    if (m_Points[iPoint].m_Type == CFX_Path::Point::Type::kMove) {
+      if (iPoint + 1 == m_Points.size()) {
+        if (m_Points[iPoint].m_CloseFigure) {
+          // Update `rect` right away since this is the final point to be drawn.
+          rect.UpdateRect(m_Points[iPoint].m_Point);
+        }
+        break;
+      }
+
+      iStartPoint = iPoint + 1;
+      iEndPoint = iPoint;
+      bJoin = false;
+    } else {
+      if (m_Points[iPoint].IsTypeAndOpen(CFX_Path::Point::Type::kBezier)) {
+        // Callers are responsible for adding Beziers in sets of 3.
+        CHECK_LT(iPoint + 2, m_Points.size());
+        DCHECK_EQ(m_Points[iPoint + 1].m_Type, CFX_Path::Point::Type::kBezier);
+        DCHECK_EQ(m_Points[iPoint + 2].m_Type, CFX_Path::Point::Type::kBezier);
+        rect.UpdateRect(m_Points[iPoint].m_Point);
+        rect.UpdateRect(m_Points[iPoint + 1].m_Point);
+        iPoint += 2;
+      }
+      if (iPoint == m_Points.size() - 1 ||
+          m_Points[iPoint + 1].m_Type == CFX_Path::Point::Type::kMove) {
+        iStartPoint = iPoint - 1;
+        iEndPoint = iPoint;
+        bJoin = false;
+      } else {
+        iStartPoint = iPoint - 1;
+        iMiddlePoint = iPoint;
+        iEndPoint = iPoint + 1;
+        bJoin = true;
+      }
+    }
+    CHECK_LT(iStartPoint, m_Points.size());
+    CHECK_LT(iEndPoint, m_Points.size());
+    if (bJoin) {
+      CHECK_LT(iMiddlePoint, m_Points.size());
+      UpdateLineJoinPoints(
+          &rect, m_Points[iStartPoint].m_Point, m_Points[iMiddlePoint].m_Point,
+          m_Points[iEndPoint].m_Point, half_width, miter_limit);
+    } else {
+      UpdateLineEndPoints(&rect, m_Points[iStartPoint].m_Point,
+                          m_Points[iEndPoint].m_Point, half_width);
+    }
+    ++iPoint;
+  }
+  return rect;
+}
+
+void CFX_Path::Transform(const CFX_Matrix& matrix) {
+  for (auto& point : m_Points)
+    point.m_Point = matrix.Transform(point.m_Point);
+}
+
+bool CFX_Path::IsRect() const {
+  if (PathPointsNeedNormalization(m_Points))
+    return IsRectImpl(GetNormalizedPoints(m_Points));
+  return IsRectImpl(m_Points);
+}
+
+absl::optional<CFX_FloatRect> CFX_Path::GetRect(
+    const CFX_Matrix* matrix) const {
+  bool do_normalize = PathPointsNeedNormalization(m_Points);
+  std::vector<Point> normalized;
+  if (do_normalize)
+    normalized = GetNormalizedPoints(m_Points);
+  const std::vector<Point>& path_points = do_normalize ? normalized : m_Points;
+
+  if (!matrix) {
+    if (!IsRectImpl(path_points))
+      return absl::nullopt;
+
+    return CreateRectFromPoints(path_points[0].m_Point, path_points[2].m_Point);
+  }
+
+  if (!IsRectPreTransform(path_points))
+    return absl::nullopt;
+
+  CFX_PointF points[5];
+  for (size_t i = 0; i < path_points.size(); ++i) {
+    points[i] = matrix->Transform(path_points[i].m_Point);
+
+    if (i == 0)
+      continue;
+    if (XYBothNotEqual(points[i], points[i - 1]))
+      return absl::nullopt;
+  }
+
+  if (XYBothNotEqual(points[0], points[3]))
+    return absl::nullopt;
+
+  return CreateRectFromPoints(points[0], points[2]);
+}
+
+CFX_RetainablePath::CFX_RetainablePath() = default;
+
+// Note: can't default the copy constructor since Retainable<> has a deleted
+// copy constructor (as it should). Instead, we want the default Retainable<>
+// constructor to be invoked so as to create a copy with a ref-count of 1 as
+// of the time it is created, then populate the remainder of the members from
+// the |src| object.
+CFX_RetainablePath::CFX_RetainablePath(const CFX_RetainablePath& src)
+    : CFX_Path(src) {}
+
+CFX_RetainablePath::~CFX_RetainablePath() = default;
+
+RetainPtr<CFX_RetainablePath> CFX_RetainablePath::Clone() const {
+  return pdfium::MakeRetain<CFX_RetainablePath>(*this);
+}
diff --git a/core/fxge/cfx_path.h b/core/fxge/cfx_path.h
new file mode 100644
index 0000000..5c6199c
--- /dev/null
+++ b/core/fxge/cfx_path.h
@@ -0,0 +1,85 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_CFX_PATH_H_
+#define CORE_FXGE_CFX_PATH_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+class CFX_Path {
+ public:
+  class Point {
+   public:
+    enum class Type : uint8_t { kLine, kBezier, kMove };
+
+    Point();
+    Point(const CFX_PointF& point, Type type, bool close);
+    Point(const Point& other);
+    ~Point();
+
+    bool IsTypeAndOpen(Type type) const {
+      return m_Type == type && !m_CloseFigure;
+    }
+
+    CFX_PointF m_Point;
+    Type m_Type = Type::kLine;
+    bool m_CloseFigure = false;
+  };
+
+  CFX_Path();
+  CFX_Path(const CFX_Path& src);
+  CFX_Path(CFX_Path&& src) noexcept;
+  ~CFX_Path();
+
+  void Clear();
+
+  Point::Type GetType(size_t index) const { return m_Points[index].m_Type; }
+  bool IsClosingFigure(size_t index) const {
+    return m_Points[index].m_CloseFigure;
+  }
+  CFX_PointF GetPoint(size_t index) const { return m_Points[index].m_Point; }
+  const std::vector<Point>& GetPoints() const { return m_Points; }
+  std::vector<Point>& GetPoints() { return m_Points; }
+
+  CFX_FloatRect GetBoundingBox() const;
+  CFX_FloatRect GetBoundingBoxForStrokePath(float line_width,
+                                            float miter_limit) const;
+
+  void Transform(const CFX_Matrix& matrix);
+  bool IsRect() const;
+  absl::optional<CFX_FloatRect> GetRect(const CFX_Matrix* matrix) const;
+
+  void Append(const CFX_Path& src, const CFX_Matrix* matrix);
+  void AppendFloatRect(const CFX_FloatRect& rect);
+  void AppendRect(float left, float bottom, float right, float top);
+  void AppendLine(const CFX_PointF& pt1, const CFX_PointF& pt2);
+  void AppendPoint(const CFX_PointF& point, Point::Type type);
+  void AppendPointAndClose(const CFX_PointF& point, Point::Type type);
+  void ClosePath();
+
+ private:
+  std::vector<Point> m_Points;
+};
+
+class CFX_RetainablePath final : public Retainable, public CFX_Path {
+ public:
+  CONSTRUCT_VIA_MAKE_RETAIN;
+
+  RetainPtr<CFX_RetainablePath> Clone() const;
+
+ private:
+  CFX_RetainablePath();
+  CFX_RetainablePath(const CFX_RetainablePath& src);
+  ~CFX_RetainablePath() override;
+};
+
+#endif  // CORE_FXGE_CFX_PATH_H_
diff --git a/core/fxge/cfx_path_unittest.cpp b/core/fxge/cfx_path_unittest.cpp
new file mode 100644
index 0000000..4ccb1c2
--- /dev/null
+++ b/core/fxge/cfx_path_unittest.cpp
@@ -0,0 +1,407 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/cfx_path.h"
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(CFX_Path, BasicTest) {
+  CFX_Path path;
+  path.AppendRect(/*left=*/1, /*bottom=*/2, /*right=*/3, /*top=*/5);
+  EXPECT_EQ(5u, path.GetPoints().size());
+  EXPECT_TRUE(path.IsRect());
+  absl::optional<CFX_FloatRect> rect = path.GetRect(nullptr);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), rect.value());
+  EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), path.GetBoundingBox());
+
+  const CFX_Matrix kScaleMatrix(1, 0, 0, 2, 60, 70);
+  rect = path.GetRect(&kScaleMatrix);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(61, 74, 63, 80), rect.value());
+  EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), path.GetBoundingBox());
+
+  path.Clear();
+  EXPECT_EQ(0u, path.GetPoints().size());
+  EXPECT_FALSE(path.IsRect());
+  EXPECT_EQ(CFX_FloatRect(), path.GetBoundingBox());
+
+  // 4 points without a closed path makes a rect.
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove);
+  path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({1, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({1, 0}, CFX_Path::Point::Type::kLine);
+  EXPECT_EQ(4u, path.GetPoints().size());
+  EXPECT_TRUE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), rect.value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), path.GetBoundingBox());
+
+  // 4 points with a closed path also makes a rect.
+  path.ClosePath();
+  EXPECT_EQ(4u, path.GetPoints().size());
+  EXPECT_TRUE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), rect.value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), path.GetBoundingBox());
+
+  path.Transform(kScaleMatrix);
+  EXPECT_TRUE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(60, 70, 61, 72), rect.value());
+  EXPECT_EQ(CFX_FloatRect(60, 70, 61, 72), path.GetBoundingBox());
+
+  path.Clear();
+  path.AppendFloatRect({1, 2, 3, 5});
+  EXPECT_TRUE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), rect.value());
+  EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), path.GetBoundingBox());
+}
+
+TEST(CFX_Path, ShearTransform) {
+  CFX_Path path;
+  path.AppendRect(/*left=*/1, /*bottom=*/2, /*right=*/3, /*top=*/5);
+
+  const CFX_Matrix kShearMatrix(1, 2, 0, 1, 0, 0);
+  EXPECT_TRUE(path.IsRect());
+  absl::optional<CFX_FloatRect> rect = path.GetRect(&kShearMatrix);
+  EXPECT_FALSE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), path.GetBoundingBox());
+
+  path.Transform(kShearMatrix);
+  EXPECT_FALSE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  EXPECT_FALSE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(1, 4, 3, 11), path.GetBoundingBox());
+
+  const CFX_Matrix shear_inverse_matrix = kShearMatrix.GetInverse();
+  rect = path.GetRect(&shear_inverse_matrix);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), rect.value());
+  EXPECT_EQ(CFX_FloatRect(1, 4, 3, 11), path.GetBoundingBox());
+
+  path.Transform(shear_inverse_matrix);
+  EXPECT_TRUE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), rect.value());
+  EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), path.GetBoundingBox());
+}
+
+TEST(CFX_Path, Hexagon) {
+  CFX_Path path;
+  path.AppendPoint({1, 0}, CFX_Path::Point::Type::kMove);
+  path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({3, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({2, 2}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({1, 2}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine);
+  ASSERT_EQ(6u, path.GetPoints().size());
+  EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(5));
+  EXPECT_FALSE(path.IsClosingFigure(5));
+  EXPECT_FALSE(path.IsRect());
+  EXPECT_FALSE(path.GetRect(nullptr).has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 3, 2), path.GetBoundingBox());
+
+  path.ClosePath();
+  ASSERT_EQ(6u, path.GetPoints().size());
+  EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(5));
+  EXPECT_TRUE(path.IsClosingFigure(5));
+  EXPECT_FALSE(path.IsRect());
+  EXPECT_FALSE(path.GetRect(nullptr).has_value());
+
+  // Calling ClosePath() repeatedly makes no difference.
+  path.ClosePath();
+  ASSERT_EQ(6u, path.GetPoints().size());
+  EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(5));
+  EXPECT_TRUE(path.IsClosingFigure(5));
+  EXPECT_FALSE(path.IsRect());
+  EXPECT_FALSE(path.GetRect(nullptr).has_value());
+
+  // A hexagon with the same start/end point is still not a rectangle.
+  path.Clear();
+  path.AppendPoint({1, 0}, CFX_Path::Point::Type::kMove);
+  path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({3, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({2, 2}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({1, 2}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({1, 0}, CFX_Path::Point::Type::kLine);
+  EXPECT_FALSE(path.IsRect());
+  EXPECT_FALSE(path.GetRect(nullptr).has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 3, 2), path.GetBoundingBox());
+}
+
+TEST(CFX_Path, ClosePath) {
+  CFX_Path path;
+  path.AppendLine({0, 0}, {0, 1});
+  path.AppendLine({0, 1}, {1, 1});
+  path.AppendLine({1, 1}, {1, 0});
+  ASSERT_EQ(4u, path.GetPoints().size());
+  EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(3));
+  EXPECT_FALSE(path.IsClosingFigure(3));
+  EXPECT_TRUE(path.IsRect());
+  absl::optional<CFX_FloatRect> rect = path.GetRect(nullptr);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), rect.value());
+
+  const CFX_Matrix kIdentityMatrix;
+  ASSERT_TRUE(kIdentityMatrix.IsIdentity());
+  rect = path.GetRect(&kIdentityMatrix);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), rect.value());
+
+  path.ClosePath();
+  ASSERT_EQ(4u, path.GetPoints().size());
+  EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(3));
+  EXPECT_TRUE(path.IsClosingFigure(3));
+  EXPECT_TRUE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), rect.value());
+
+  // Calling ClosePath() repeatedly makes no difference.
+  path.ClosePath();
+  ASSERT_EQ(4u, path.GetPoints().size());
+  EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(3));
+  EXPECT_TRUE(path.IsClosingFigure(3));
+  EXPECT_TRUE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), rect.value());
+
+  path.AppendPointAndClose({0, 0}, CFX_Path::Point::Type::kLine);
+  ASSERT_EQ(5u, path.GetPoints().size());
+  EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(3));
+  EXPECT_TRUE(path.IsClosingFigure(3));
+  EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(4));
+  EXPECT_TRUE(path.IsClosingFigure(4));
+  EXPECT_TRUE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), rect.value());
+}
+
+TEST(CFX_Path, FivePointRect) {
+  CFX_Path path;
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove);
+  path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine);
+  ASSERT_EQ(5u, path.GetPoints().size());
+  EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(4));
+  EXPECT_FALSE(path.IsClosingFigure(4));
+  EXPECT_TRUE(path.IsRect());
+  absl::optional<CFX_FloatRect> rect = path.GetRect(nullptr);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), rect.value());
+
+  path.ClosePath();
+  ASSERT_EQ(5u, path.GetPoints().size());
+  EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(4));
+  EXPECT_TRUE(path.IsClosingFigure(4));
+  EXPECT_TRUE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), rect.value());
+}
+
+TEST(CFX_Path, SixPlusPointRect) {
+  CFX_Path path;
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove);
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine);
+  EXPECT_TRUE(path.IsRect());
+  absl::optional<CFX_FloatRect> rect = path.GetRect(nullptr);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), rect.value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), path.GetBoundingBox());
+
+  path.Clear();
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove);
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine);
+  EXPECT_TRUE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), rect.value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), path.GetBoundingBox());
+}
+
+TEST(CFX_Path, NotRect) {
+  CFX_Path path;
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove);
+  path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 0.1f}, CFX_Path::Point::Type::kLine);
+  EXPECT_FALSE(path.IsRect());
+  absl::optional<CFX_FloatRect> rect = path.GetRect(nullptr);
+  EXPECT_FALSE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), path.GetBoundingBox());
+
+  path.ClosePath();
+  EXPECT_FALSE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  EXPECT_FALSE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), path.GetBoundingBox());
+
+  path.Clear();
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove);
+  path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({3, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPointAndClose({0, 1}, CFX_Path::Point::Type::kLine);
+  EXPECT_FALSE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  EXPECT_FALSE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 3, 1), path.GetBoundingBox());
+
+  path.Clear();
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove);
+  path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPointAndClose({0, 1}, CFX_Path::Point::Type::kMove);
+  EXPECT_FALSE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  EXPECT_FALSE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), path.GetBoundingBox());
+
+  path.Clear();
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove);
+  path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({3, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPointAndClose({0, 1}, CFX_Path::Point::Type::kLine);
+  EXPECT_FALSE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  EXPECT_FALSE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 3, 1), path.GetBoundingBox());
+
+  path.Clear();
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove);
+  path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine);
+  EXPECT_FALSE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  EXPECT_FALSE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), path.GetBoundingBox());
+
+  path.Clear();
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove);
+  path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine);
+  EXPECT_FALSE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  EXPECT_FALSE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), path.GetBoundingBox());
+
+  path.Clear();
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove);
+  path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({2, 2}, CFX_Path::Point::Type::kLine);
+  EXPECT_FALSE(path.IsRect());
+  rect = path.GetRect(nullptr);
+  EXPECT_FALSE(rect.has_value());
+  const CFX_Matrix kScaleMatrix(1, 0, 0, 2, 60, 70);
+  rect = path.GetRect(&kScaleMatrix);
+  EXPECT_FALSE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 2, 2), path.GetBoundingBox());
+}
+
+TEST(CFX_Path, EmptyRect) {
+  // Document existing behavior where an empty rect is still considered a rect.
+  CFX_Path path;
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove);
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine);
+  path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine);
+  EXPECT_TRUE(path.IsRect());
+  absl::optional<CFX_FloatRect> rect = path.GetRect(nullptr);
+  ASSERT_TRUE(rect.has_value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 0, 1), rect.value());
+  EXPECT_EQ(CFX_FloatRect(0, 0, 0, 1), path.GetBoundingBox());
+}
+
+TEST(CFX_Path, Append) {
+  CFX_Path path;
+  path.AppendPoint({5, 6}, CFX_Path::Point::Type::kMove);
+  ASSERT_EQ(1u, path.GetPoints().size());
+  EXPECT_EQ(CFX_PointF(5, 6), path.GetPoint(0));
+
+  CFX_Path empty_path;
+  path.Append(empty_path, nullptr);
+  ASSERT_EQ(1u, path.GetPoints().size());
+  EXPECT_EQ(CFX_PointF(5, 6), path.GetPoint(0));
+
+  path.Append(path, nullptr);
+  ASSERT_EQ(2u, path.GetPoints().size());
+  EXPECT_EQ(CFX_PointF(5, 6), path.GetPoint(0));
+  EXPECT_EQ(CFX_PointF(5, 6), path.GetPoint(1));
+
+  const CFX_Matrix kScaleMatrix(1, 0, 0, 2, 60, 70);
+  path.Append(path, &kScaleMatrix);
+  ASSERT_EQ(4u, path.GetPoints().size());
+  EXPECT_EQ(CFX_PointF(5, 6), path.GetPoint(0));
+  EXPECT_EQ(CFX_PointF(5, 6), path.GetPoint(1));
+  EXPECT_EQ(CFX_PointF(65, 82), path.GetPoint(2));
+  EXPECT_EQ(CFX_PointF(65, 82), path.GetPoint(3));
+}
+
+TEST(CFX_Path, GetBoundingBoxForStrokePath) {
+  static constexpr float kLineWidth = 1.0f;
+  static constexpr float kMiterLimit = 1.0f;
+
+  {
+    // Test the case that the first/last point is "move" and it closes the
+    // paths.
+    CFX_Path path;
+    path.AppendPoint({2, 0}, CFX_Path::Point::Type::kMove);
+    path.ClosePath();
+    EXPECT_EQ(CFX_FloatRect(2, 0, 2, 0),
+              path.GetBoundingBoxForStrokePath(kLineWidth, kMiterLimit));
+  }
+
+  {
+    // Test on a regular rect path.
+    CFX_Path path;
+    path.AppendPoint({2, 0}, CFX_Path::Point::Type::kMove);
+    path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine);
+    path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine);
+    path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine);
+    path.ClosePath();
+    EXPECT_EQ(CFX_FloatRect(-1, -1, 3, 2),
+              path.GetBoundingBoxForStrokePath(kLineWidth, kMiterLimit));
+
+    // If the final point is "move" and the path remains open, it should not
+    // affect the bounding rect.
+    path.AppendPoint({20, 20}, CFX_Path::Point::Type::kMove);
+    EXPECT_EQ(CFX_FloatRect(-1, -1, 3, 2),
+              path.GetBoundingBoxForStrokePath(kLineWidth, kMiterLimit));
+  }
+}
diff --git a/core/fxge/cfx_pathdata.cpp b/core/fxge/cfx_pathdata.cpp
deleted file mode 100644
index 55ce854..0000000
--- a/core/fxge/cfx_pathdata.cpp
+++ /dev/null
@@ -1,522 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxge/cfx_pathdata.h"
-
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/numerics/safe_math.h"
-
-namespace {
-
-bool IsFoldingVerticalLine(const CFX_PointF& a,
-                           const CFX_PointF& b,
-                           const CFX_PointF& c) {
-  return a.x == b.x && b.x == c.x && (b.y - a.y) * (b.y - c.y) > 0;
-}
-
-bool IsFoldingHorizontalLine(const CFX_PointF& a,
-                             const CFX_PointF& b,
-                             const CFX_PointF& c) {
-  return a.y == b.y && b.y == c.y && (b.x - a.x) * (b.x - c.x) > 0;
-}
-
-bool IsFoldingDiagonalLine(const CFX_PointF& a,
-                           const CFX_PointF& b,
-                           const CFX_PointF& c) {
-  return a.x != b.x && c.x != b.x && a.y != b.y && c.y != b.y &&
-         (a.y - b.y) * (c.x - b.x) == (c.y - b.y) * (a.x - b.x);
-}
-
-void UpdateLineEndPoints(CFX_FloatRect* rect,
-                         const CFX_PointF& start_pos,
-                         const CFX_PointF& end_pos,
-                         float hw) {
-  if (start_pos.x == end_pos.x) {
-    if (start_pos.y == end_pos.y) {
-      rect->UpdateRect(end_pos + CFX_PointF(hw, hw));
-      rect->UpdateRect(end_pos - CFX_PointF(hw, hw));
-      return;
-    }
-
-    float point_y;
-    if (end_pos.y < start_pos.y)
-      point_y = end_pos.y - hw;
-    else
-      point_y = end_pos.y + hw;
-
-    rect->UpdateRect(CFX_PointF(end_pos.x + hw, point_y));
-    rect->UpdateRect(CFX_PointF(end_pos.x - hw, point_y));
-    return;
-  }
-
-  if (start_pos.y == end_pos.y) {
-    float point_x;
-    if (end_pos.x < start_pos.x)
-      point_x = end_pos.x - hw;
-    else
-      point_x = end_pos.x + hw;
-
-    rect->UpdateRect(CFX_PointF(point_x, end_pos.y + hw));
-    rect->UpdateRect(CFX_PointF(point_x, end_pos.y - hw));
-    return;
-  }
-
-  CFX_PointF diff = end_pos - start_pos;
-  float ll = FXSYS_sqrt2(diff.x, diff.y);
-  float mx = end_pos.x + hw * diff.x / ll;
-  float my = end_pos.y + hw * diff.y / ll;
-  float dx1 = hw * diff.y / ll;
-  float dy1 = hw * diff.x / ll;
-  rect->UpdateRect(CFX_PointF(mx - dx1, my + dy1));
-  rect->UpdateRect(CFX_PointF(mx + dx1, my - dy1));
-}
-
-void UpdateLineJoinPoints(CFX_FloatRect* rect,
-                          const CFX_PointF& start_pos,
-                          const CFX_PointF& mid_pos,
-                          const CFX_PointF& end_pos,
-                          float half_width,
-                          float miter_limit) {
-  float start_k = 0;
-  float start_c = 0;
-  float end_k = 0;
-  float end_c = 0;
-  float start_len = 0;
-  float start_dc = 0;
-  float end_len = 0;
-  float end_dc = 0;
-  float one_twentieth = 1.0f / 20;
-
-  bool bStartVert = fabs(start_pos.x - mid_pos.x) < one_twentieth;
-  bool bEndVert = fabs(mid_pos.x - end_pos.x) < one_twentieth;
-  if (bStartVert && bEndVert) {
-    int start_dir = mid_pos.y > start_pos.y ? 1 : -1;
-    float point_y = mid_pos.y + half_width * start_dir;
-    rect->UpdateRect(CFX_PointF(mid_pos.x + half_width, point_y));
-    rect->UpdateRect(CFX_PointF(mid_pos.x - half_width, point_y));
-    return;
-  }
-
-  if (!bStartVert) {
-    CFX_PointF start_to_mid = start_pos - mid_pos;
-    start_k = (mid_pos.y - start_pos.y) / (mid_pos.x - start_pos.x);
-    start_c = mid_pos.y - (start_k * mid_pos.x);
-    start_len = FXSYS_sqrt2(start_to_mid.x, start_to_mid.y);
-    start_dc =
-        static_cast<float>(fabs(half_width * start_len / start_to_mid.x));
-  }
-  if (!bEndVert) {
-    CFX_PointF end_to_mid = end_pos - mid_pos;
-    end_k = end_to_mid.y / end_to_mid.x;
-    end_c = mid_pos.y - (end_k * mid_pos.x);
-    end_len = FXSYS_sqrt2(end_to_mid.x, end_to_mid.y);
-    end_dc = static_cast<float>(fabs(half_width * end_len / end_to_mid.x));
-  }
-  if (bStartVert) {
-    CFX_PointF outside(start_pos.x, 0);
-    if (end_pos.x < start_pos.x)
-      outside.x += half_width;
-    else
-      outside.x -= half_width;
-
-    if (start_pos.y < (end_k * start_pos.x) + end_c)
-      outside.y = (end_k * outside.x) + end_c + end_dc;
-    else
-      outside.y = (end_k * outside.x) + end_c - end_dc;
-
-    rect->UpdateRect(outside);
-    return;
-  }
-
-  if (bEndVert) {
-    CFX_PointF outside(end_pos.x, 0);
-    if (start_pos.x < end_pos.x)
-      outside.x += half_width;
-    else
-      outside.x -= half_width;
-
-    if (end_pos.y < (start_k * end_pos.x) + start_c)
-      outside.y = (start_k * outside.x) + start_c + start_dc;
-    else
-      outside.y = (start_k * outside.x) + start_c - start_dc;
-
-    rect->UpdateRect(outside);
-    return;
-  }
-
-  if (fabs(start_k - end_k) < one_twentieth) {
-    int start_dir = mid_pos.x > start_pos.x ? 1 : -1;
-    int end_dir = end_pos.x > mid_pos.x ? 1 : -1;
-    if (start_dir == end_dir)
-      UpdateLineEndPoints(rect, mid_pos, end_pos, half_width);
-    else
-      UpdateLineEndPoints(rect, start_pos, mid_pos, half_width);
-    return;
-  }
-
-  float start_outside_c = start_c;
-  if (end_pos.y < (start_k * end_pos.x) + start_c)
-    start_outside_c += start_dc;
-  else
-    start_outside_c -= start_dc;
-
-  float end_outside_c = end_c;
-  if (start_pos.y < (end_k * start_pos.x) + end_c)
-    end_outside_c += end_dc;
-  else
-    end_outside_c -= end_dc;
-
-  float join_x = (end_outside_c - start_outside_c) / (start_k - end_k);
-  float join_y = start_k * join_x + start_outside_c;
-  rect->UpdateRect(CFX_PointF(join_x, join_y));
-}
-
-}  // namespace
-
-FX_PATHPOINT::FX_PATHPOINT() = default;
-
-FX_PATHPOINT::FX_PATHPOINT(const CFX_PointF& point, FXPT_TYPE type, bool close)
-    : m_Point(point), m_Type(type), m_CloseFigure(close) {}
-
-FX_PATHPOINT::FX_PATHPOINT(const FX_PATHPOINT& other) = default;
-
-FX_PATHPOINT::~FX_PATHPOINT() = default;
-
-CFX_PathData::CFX_PathData() = default;
-
-CFX_PathData::CFX_PathData(const CFX_PathData& src) = default;
-
-CFX_PathData::CFX_PathData(CFX_PathData&& src) = default;
-
-CFX_PathData::~CFX_PathData() = default;
-
-void CFX_PathData::Clear() {
-  m_Points.clear();
-}
-
-void CFX_PathData::ClosePath() {
-  if (m_Points.empty())
-    return;
-  m_Points.back().m_CloseFigure = true;
-}
-
-void CFX_PathData::Append(const CFX_PathData* pSrc, const CFX_Matrix* pMatrix) {
-  if (pSrc->m_Points.empty())
-    return;
-
-  size_t cur_size = m_Points.size();
-  m_Points.insert(m_Points.end(), pSrc->m_Points.begin(), pSrc->m_Points.end());
-
-  if (!pMatrix)
-    return;
-
-  for (size_t i = cur_size; i < m_Points.size(); i++)
-    m_Points[i].m_Point = pMatrix->Transform(m_Points[i].m_Point);
-}
-
-void CFX_PathData::AppendPoint(const CFX_PointF& point,
-                               FXPT_TYPE type,
-                               bool closeFigure) {
-  m_Points.push_back(FX_PATHPOINT(point, type, closeFigure));
-}
-
-void CFX_PathData::AppendLine(const CFX_PointF& pt1, const CFX_PointF& pt2) {
-  if (m_Points.empty() || fabs(m_Points.back().m_Point.x - pt1.x) > 0.001 ||
-      fabs(m_Points.back().m_Point.y - pt1.y) > 0.001) {
-    AppendPoint(pt1, FXPT_TYPE::MoveTo, false);
-  }
-  AppendPoint(pt2, FXPT_TYPE::LineTo, false);
-}
-
-void CFX_PathData::AppendFloatRect(const CFX_FloatRect& rect) {
-  return AppendRect(rect.left, rect.bottom, rect.right, rect.top);
-}
-
-void CFX_PathData::AppendRect(float left,
-                              float bottom,
-                              float right,
-                              float top) {
-  CFX_PointF left_bottom(left, bottom);
-  CFX_PointF left_top(left, top);
-  CFX_PointF right_top(right, top);
-  CFX_PointF right_bottom(right, bottom);
-
-  AppendLine(left_bottom, left_top);
-  AppendLine(left_top, right_top);
-  AppendLine(right_top, right_bottom);
-  AppendLine(right_bottom, left_bottom);
-  ClosePath();
-}
-
-CFX_FloatRect CFX_PathData::GetBoundingBox() const {
-  if (m_Points.empty())
-    return CFX_FloatRect();
-
-  CFX_FloatRect rect;
-  rect.InitRect(m_Points[0].m_Point);
-  for (size_t i = 1; i < m_Points.size(); i++)
-    rect.UpdateRect(m_Points[i].m_Point);
-  return rect;
-}
-
-CFX_FloatRect CFX_PathData::GetBoundingBox(float line_width,
-                                           float miter_limit) const {
-  CFX_FloatRect rect(100000.0f, 100000.0f, -100000.0f, -100000.0f);
-  size_t iPoint = 0;
-  float half_width = line_width;
-  int iStartPoint = 0;
-  int iEndPoint = 0;
-  int iMiddlePoint = 0;
-  bool bJoin;
-  while (iPoint < m_Points.size()) {
-    if (m_Points[iPoint].IsTypeAndOpen(FXPT_TYPE::MoveTo)) {
-      if (iPoint + 1 == m_Points.size())
-        break;
-
-      iStartPoint = iPoint + 1;
-      iEndPoint = iPoint;
-      bJoin = false;
-    } else {
-      if (m_Points[iPoint].IsTypeAndOpen(FXPT_TYPE::BezierTo)) {
-        rect.UpdateRect(m_Points[iPoint].m_Point);
-        rect.UpdateRect(m_Points[iPoint + 1].m_Point);
-        iPoint += 2;
-      }
-      if (iPoint == m_Points.size() - 1 ||
-          m_Points[iPoint + 1].IsTypeAndOpen(FXPT_TYPE::MoveTo)) {
-        iStartPoint = iPoint - 1;
-        iEndPoint = iPoint;
-        bJoin = false;
-      } else {
-        iStartPoint = iPoint - 1;
-        iMiddlePoint = iPoint;
-        iEndPoint = iPoint + 1;
-        bJoin = true;
-      }
-    }
-
-    CFX_PointF start_pos = m_Points[iStartPoint].m_Point;
-    CFX_PointF end_pos = m_Points[iEndPoint].m_Point;
-    if (bJoin) {
-      CFX_PointF mid_pos = m_Points[iMiddlePoint].m_Point;
-      UpdateLineJoinPoints(&rect, start_pos, mid_pos, end_pos, half_width,
-                           miter_limit);
-    } else {
-      UpdateLineEndPoints(&rect, start_pos, end_pos, half_width);
-    }
-    iPoint++;
-  }
-  return rect;
-}
-
-void CFX_PathData::Transform(const CFX_Matrix& matrix) {
-  for (auto& point : m_Points)
-    point.m_Point = matrix.Transform(point.m_Point);
-}
-
-bool CFX_PathData::GetZeroAreaPath(const CFX_Matrix* pMatrix,
-                                   bool bAdjust,
-                                   CFX_PathData* NewPath,
-                                   bool* bThin,
-                                   bool* setIdentity) const {
-  *setIdentity = false;
-  if (m_Points.size() < 3)
-    return false;
-
-  if (m_Points.size() == 3 && m_Points[0].m_Type == FXPT_TYPE::MoveTo &&
-      m_Points[1].m_Type == FXPT_TYPE::LineTo &&
-      m_Points[2].m_Type == FXPT_TYPE::LineTo &&
-      m_Points[0].m_Point == m_Points[2].m_Point) {
-    for (size_t i = 0; i < 2; i++) {
-      CFX_PointF point = m_Points[i].m_Point;
-      if (bAdjust) {
-        if (pMatrix)
-          point = pMatrix->Transform(point);
-
-        point = CFX_PointF(static_cast<int>(point.x) + 0.5f,
-                           static_cast<int>(point.y) + 0.5f);
-      }
-      NewPath->AppendPoint(
-          point, i == 0 ? FXPT_TYPE::MoveTo : FXPT_TYPE::LineTo, false);
-    }
-    if (bAdjust && pMatrix)
-      *setIdentity = true;
-
-    // Note, they both have to be not equal.
-    if (m_Points[0].m_Point.x != m_Points[1].m_Point.x &&
-        m_Points[0].m_Point.y != m_Points[1].m_Point.y) {
-      *bThin = true;
-    }
-    return true;
-  }
-
-  if (((m_Points.size() > 3) && (m_Points.size() % 2))) {
-    int mid = m_Points.size() / 2;
-    bool bZeroArea = false;
-    CFX_PathData t_path;
-    for (int i = 0; i < mid; i++) {
-      if (!(m_Points[mid - i - 1].m_Point == m_Points[mid + i + 1].m_Point &&
-            m_Points[mid - i - 1].m_Type != FXPT_TYPE::BezierTo &&
-            m_Points[mid + i + 1].m_Type != FXPT_TYPE::BezierTo)) {
-        bZeroArea = true;
-        break;
-      }
-
-      t_path.AppendPoint(m_Points[mid - i].m_Point, FXPT_TYPE::MoveTo, false);
-      t_path.AppendPoint(m_Points[mid - i - 1].m_Point, FXPT_TYPE::LineTo,
-                         false);
-    }
-    if (!bZeroArea) {
-      NewPath->Append(&t_path, nullptr);
-      *bThin = true;
-      return true;
-    }
-  }
-
-  int startPoint = 0;
-  for (size_t i = 0; i < m_Points.size(); i++) {
-    FXPT_TYPE point_type = m_Points[i].m_Type;
-    if (point_type == FXPT_TYPE::MoveTo) {
-      startPoint = i;
-      continue;
-    }
-
-    if (point_type == FXPT_TYPE::BezierTo) {
-      i += 2;
-      continue;
-    }
-
-    ASSERT(point_type == FXPT_TYPE::LineTo);
-    int next_index =
-        (i + 1 - startPoint) % (m_Points.size() - startPoint) + startPoint;
-    const FX_PATHPOINT& next = m_Points[next_index];
-    if (next.m_Type == FXPT_TYPE::BezierTo || next.m_Type == FXPT_TYPE::MoveTo)
-      continue;
-
-    const FX_PATHPOINT& prev = m_Points[i - 1];
-    const FX_PATHPOINT& cur = m_Points[i];
-    if (IsFoldingVerticalLine(prev.m_Point, cur.m_Point, next.m_Point)) {
-      bool use_prev = fabs(cur.m_Point.y - prev.m_Point.y) <
-                      fabs(cur.m_Point.y - next.m_Point.y);
-      const FX_PATHPOINT& start = use_prev ? prev : cur;
-      const FX_PATHPOINT& end = use_prev ? m_Points[next_index - 1] : next;
-      NewPath->AppendPoint(start.m_Point, FXPT_TYPE::MoveTo, false);
-      NewPath->AppendPoint(end.m_Point, FXPT_TYPE::LineTo, false);
-      continue;
-    }
-
-    if (IsFoldingHorizontalLine(prev.m_Point, cur.m_Point, next.m_Point) ||
-        IsFoldingDiagonalLine(prev.m_Point, cur.m_Point, next.m_Point)) {
-      bool use_prev = fabs(cur.m_Point.x - prev.m_Point.x) <
-                      fabs(cur.m_Point.x - next.m_Point.x);
-      const FX_PATHPOINT& start = use_prev ? prev : cur;
-      const FX_PATHPOINT& end = use_prev ? m_Points[next_index - 1] : next;
-      NewPath->AppendPoint(start.m_Point, FXPT_TYPE::MoveTo, false);
-      NewPath->AppendPoint(end.m_Point, FXPT_TYPE::LineTo, false);
-      continue;
-    }
-  }
-
-  size_t new_path_size = NewPath->GetPoints().size();
-  if (m_Points.size() > 3 && new_path_size > 0)
-    *bThin = true;
-  return new_path_size != 0;
-}
-
-bool CFX_PathData::IsRect() const {
-  if (m_Points.size() != 5 && m_Points.size() != 4)
-    return false;
-
-  if ((m_Points.size() == 5 && m_Points[0].m_Point != m_Points[4].m_Point) ||
-      m_Points[0].m_Point == m_Points[2].m_Point ||
-      m_Points[1].m_Point == m_Points[3].m_Point) {
-    return false;
-  }
-  // Note, both x,y have to not equal.
-  if (m_Points[0].m_Point.x != m_Points[3].m_Point.x &&
-      m_Points[0].m_Point.y != m_Points[3].m_Point.y) {
-    return false;
-  }
-
-  for (int i = 1; i < 4; i++) {
-    if (m_Points[i].m_Type != FXPT_TYPE::LineTo)
-      return false;
-    // Note, both x,y have to not equal.
-    if (m_Points[i].m_Point.x != m_Points[i - 1].m_Point.x &&
-        m_Points[i].m_Point.y != m_Points[i - 1].m_Point.y) {
-      return false;
-    }
-  }
-  return m_Points.size() == 5 || m_Points[3].m_CloseFigure;
-}
-
-bool CFX_PathData::IsRect(const CFX_Matrix* pMatrix,
-                          CFX_FloatRect* pRect) const {
-  if (!pMatrix) {
-    if (!IsRect())
-      return false;
-
-    if (pRect) {
-      pRect->left = m_Points[0].m_Point.x;
-      pRect->right = m_Points[2].m_Point.x;
-      pRect->bottom = m_Points[0].m_Point.y;
-      pRect->top = m_Points[2].m_Point.y;
-      pRect->Normalize();
-    }
-    return true;
-  }
-
-  if (m_Points.size() != 5 && m_Points.size() != 4)
-    return false;
-
-  if ((m_Points.size() == 5 && m_Points[0].m_Point != m_Points[4].m_Point) ||
-      m_Points[1].m_Point == m_Points[3].m_Point) {
-    return false;
-  }
-  // Note, both x,y not equal.
-  if (m_Points.size() == 4 && m_Points[0].m_Point.x != m_Points[3].m_Point.x &&
-      m_Points[0].m_Point.y != m_Points[3].m_Point.y) {
-    return false;
-  }
-
-  CFX_PointF points[5];
-  for (size_t i = 0; i < m_Points.size(); i++) {
-    points[i] = pMatrix->Transform(m_Points[i].m_Point);
-
-    if (i == 0)
-      continue;
-    if (m_Points[i].m_Type != FXPT_TYPE::LineTo)
-      return false;
-    if (points[i].x != points[i - 1].x && points[i].y != points[i - 1].y)
-      return false;
-  }
-
-  if (pRect) {
-    pRect->left = points[0].x;
-    pRect->right = points[2].x;
-    pRect->bottom = points[0].y;
-    pRect->top = points[2].y;
-    pRect->Normalize();
-  }
-  return true;
-}
-
-CFX_RetainablePathData::CFX_RetainablePathData() = default;
-
-// Note: can't default the copy constructor since Retainable<> has a deleted
-// copy constructor (as it should). Instead, we want the default Retainable<>
-// constructor to be invoked so as to create a copy with a ref-count of 1 as
-// of the time it is created, then populate the remainder of the members from
-// the |src| object.
-CFX_RetainablePathData::CFX_RetainablePathData(
-    const CFX_RetainablePathData& src)
-    : CFX_PathData(src) {}
-
-CFX_RetainablePathData::~CFX_RetainablePathData() = default;
-
-RetainPtr<CFX_RetainablePathData> CFX_RetainablePathData::Clone() const {
-  return pdfium::MakeRetain<CFX_RetainablePathData>(*this);
-}
diff --git a/core/fxge/cfx_pathdata.h b/core/fxge/cfx_pathdata.h
deleted file mode 100644
index 3fd57e2..0000000
--- a/core/fxge/cfx_pathdata.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXGE_CFX_PATHDATA_H_
-#define CORE_FXGE_CFX_PATHDATA_H_
-
-#include <vector>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/retain_ptr.h"
-
-enum class FXPT_TYPE : uint8_t { LineTo, BezierTo, MoveTo };
-
-class FX_PATHPOINT {
- public:
-  FX_PATHPOINT();
-  FX_PATHPOINT(const CFX_PointF& point, FXPT_TYPE type, bool close);
-  FX_PATHPOINT(const FX_PATHPOINT& other);
-  ~FX_PATHPOINT();
-
-  bool IsTypeAndOpen(FXPT_TYPE type) const {
-    return m_Type == type && !m_CloseFigure;
-  }
-
-  CFX_PointF m_Point;
-  FXPT_TYPE m_Type;
-  bool m_CloseFigure;
-};
-
-class CFX_PathData {
- public:
-  CFX_PathData();
-  CFX_PathData(const CFX_PathData& src);
-  CFX_PathData(CFX_PathData&& src);
-  ~CFX_PathData();
-
-  void Clear();
-
-  FXPT_TYPE GetType(int index) const { return m_Points[index].m_Type; }
-  bool IsClosingFigure(int index) const {
-    return m_Points[index].m_CloseFigure;
-  }
-
-  CFX_PointF GetPoint(int index) const { return m_Points[index].m_Point; }
-  const std::vector<FX_PATHPOINT>& GetPoints() const { return m_Points; }
-  std::vector<FX_PATHPOINT>& GetPoints() { return m_Points; }
-
-  CFX_FloatRect GetBoundingBox() const;
-  CFX_FloatRect GetBoundingBox(float line_width, float miter_limit) const;
-
-  void Transform(const CFX_Matrix& matrix);
-  bool IsRect() const;
-  bool GetZeroAreaPath(const CFX_Matrix* pMatrix,
-                       bool bAdjust,
-                       CFX_PathData* NewPath,
-                       bool* bThin,
-                       bool* setIdentity) const;
-  bool IsRect(const CFX_Matrix* pMatrix, CFX_FloatRect* rect) const;
-
-  void Append(const CFX_PathData* pSrc, const CFX_Matrix* pMatrix);
-  void AppendFloatRect(const CFX_FloatRect& rect);
-  void AppendRect(float left, float bottom, float right, float top);
-  void AppendLine(const CFX_PointF& pt1, const CFX_PointF& pt2);
-  void AppendPoint(const CFX_PointF& point, FXPT_TYPE type, bool closeFigure);
-  void ClosePath();
-
- private:
-  std::vector<FX_PATHPOINT> m_Points;
-};
-
-class CFX_RetainablePathData final : public Retainable, public CFX_PathData {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  RetainPtr<CFX_RetainablePathData> Clone() const;
-
- private:
-  CFX_RetainablePathData();
-  CFX_RetainablePathData(const CFX_RetainablePathData& src);
-  ~CFX_RetainablePathData() override;
-};
-
-#endif  // CORE_FXGE_CFX_PATHDATA_H_
diff --git a/core/fxge/cfx_renderdevice.cpp b/core/fxge/cfx_renderdevice.cpp
index b1474c5..2280f6f 100644
--- a/core/fxge/cfx_renderdevice.cpp
+++ b/core/fxge/cfx_renderdevice.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include "core/fxge/cfx_renderdevice.h"
 
+#include <math.h>
+
 #include <algorithm>
 #include <memory>
 #include <utility>
@@ -14,28 +16,33 @@
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxge/cfx_color.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_fontmgr.h"
 #include "core/fxge/cfx_gemodule.h"
 #include "core/fxge/cfx_glyphbitmap.h"
 #include "core/fxge/cfx_glyphcache.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
+#include "core/fxge/cfx_textrenderoptions.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/dib/cfx_imagerenderer.h"
 #include "core/fxge/fx_font.h"
 #include "core/fxge/renderdevicedriver_iface.h"
 #include "core/fxge/text_char_pos.h"
 #include "core/fxge/text_glyph_pos.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/span.h"
 
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
-#include "third_party/skia/include/core/SkTypes.h"
+#if defined(_SKIA_SUPPORT_)
+#include "third_party/skia/include/core/SkTypes.h"  // nogncheck
 #endif
 
 namespace {
 
 void AdjustGlyphSpace(std::vector<TextGlyphPos>* pGlyphAndPos) {
-  ASSERT(pGlyphAndPos->size() > 1);
+  DCHECK_GT(pGlyphAndPos->size(), 1u);
   std::vector<TextGlyphPos>& glyphs = *pGlyphAndPos;
   bool bVertical = glyphs.back().m_Origin.x == glyphs.front().m_Origin.x;
   if (!bVertical && (glyphs.back().m_Origin.y != glyphs.front().m_Origin.y))
@@ -44,12 +51,13 @@
   for (size_t i = glyphs.size() - 1; i > 1; --i) {
     const TextGlyphPos& next = glyphs[i];
     int next_origin = bVertical ? next.m_Origin.y : next.m_Origin.x;
-    float next_origin_f = bVertical ? next.m_fOrigin.y : next.m_fOrigin.x;
+    float next_origin_f =
+        bVertical ? next.m_fDeviceOrigin.y : next.m_fDeviceOrigin.x;
 
     TextGlyphPos& current = glyphs[i - 1];
     int& current_origin = bVertical ? current.m_Origin.y : current.m_Origin.x;
     float current_origin_f =
-        bVertical ? current.m_fOrigin.y : current.m_fOrigin.x;
+        bVertical ? current.m_fDeviceOrigin.y : current.m_fDeviceOrigin.x;
 
     FX_SAFE_INT32 safe_space = next_origin;
     safe_space -= current_origin;
@@ -71,7 +79,7 @@
   }
 }
 
-const uint8_t g_TextGammaAdjust[256] = {
+constexpr uint8_t kTextGammaAdjust[256] = {
     0,   2,   3,   4,   6,   7,   8,   10,  11,  12,  13,  15,  16,  17,  18,
     19,  21,  22,  23,  24,  25,  26,  27,  29,  30,  31,  32,  33,  34,  35,
     36,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  51,  52,
@@ -93,35 +101,20 @@
 };
 
 int TextGammaAdjust(int value) {
-  ASSERT(value >= 0);
-  ASSERT(value <= 255);
-  return g_TextGammaAdjust[value];
+  DCHECK_GE(value, 0);
+  DCHECK_LE(value, 255);
+  return kTextGammaAdjust[value];
 }
 
 int CalcAlpha(int src, int alpha) {
   return src * alpha / 255;
 }
 
-void Merge(uint8_t src, int channel, int alpha, uint8_t* dest) {
-  *dest = FXDIB_ALPHA_MERGE(*dest, channel, CalcAlpha(src, alpha));
-}
-
 void MergeGammaAdjust(uint8_t src, int channel, int alpha, uint8_t* dest) {
   *dest =
       FXDIB_ALPHA_MERGE(*dest, channel, CalcAlpha(TextGammaAdjust(src), alpha));
 }
 
-void MergeGammaAdjustBgr(const uint8_t* src,
-                         int r,
-                         int g,
-                         int b,
-                         int a,
-                         uint8_t* dest) {
-  MergeGammaAdjust(src[0], b, a, &dest[0]);
-  MergeGammaAdjust(src[1], g, a, &dest[1]);
-  MergeGammaAdjust(src[2], r, a, &dest[2]);
-}
-
 void MergeGammaAdjustRgb(const uint8_t* src,
                          int r,
                          int g,
@@ -207,7 +200,7 @@
     NormalizeArgb(src_value, r, g, b, a, dest, src_alpha);
 }
 
-void NextPixel(uint8_t** src_scan, uint8_t** dst_scan, int bpp) {
+void NextPixel(const uint8_t** src_scan, uint8_t** dst_scan, int bpp) {
   *src_scan += 3;
   *dst_scan += bpp;
 }
@@ -224,72 +217,26 @@
                           int top,
                           int start_col,
                           int end_col,
-                          bool bNormal,
-                          bool bBGRStripe,
+                          bool normalize,
                           int x_subpixel,
                           int a,
                           int r,
                           int g,
                           int b) {
-  const bool has_alpha = bitmap->GetFormat() == FXDIB_Argb;
-  uint8_t* src_buf = pGlyph->GetBuffer();
-  int src_pitch = pGlyph->GetPitch();
-  uint8_t* dest_buf = bitmap->GetBuffer();
-  int dest_pitch = bitmap->GetPitch();
+  const bool has_alpha = bitmap->GetFormat() == FXDIB_Format::kArgb;
   const int Bpp = has_alpha ? 4 : bitmap->GetBPP() / 8;
   for (int row = 0; row < nrows; ++row) {
     int dest_row = row + top;
     if (dest_row < 0 || dest_row >= bitmap->GetHeight())
       continue;
 
-    uint8_t* src_scan = src_buf + row * src_pitch + (start_col - left) * 3;
-    uint8_t* dest_scan = dest_buf + dest_row * dest_pitch + start_col * Bpp;
-    if (bBGRStripe) {
-      if (x_subpixel == 0) {
-        for (int col = start_col; col < end_col; ++col) {
-          if (has_alpha) {
-            Merge(src_scan[2], r, a, &dest_scan[2]);
-            Merge(src_scan[1], g, a, &dest_scan[1]);
-            Merge(src_scan[0], b, a, &dest_scan[0]);
-          } else {
-            MergeGammaAdjustBgr(&src_scan[0], r, g, b, a, &dest_scan[0]);
-          }
-          SetAlpha(has_alpha, dest_scan);
-          NextPixel(&src_scan, &dest_scan, Bpp);
-        }
-        continue;
-      }
-      if (x_subpixel == 1) {
-        MergeGammaAdjust(src_scan[1], r, a, &dest_scan[2]);
-        MergeGammaAdjust(src_scan[0], g, a, &dest_scan[1]);
-        if (start_col > left)
-          MergeGammaAdjust(src_scan[-1], b, a, &dest_scan[0]);
-        SetAlpha(has_alpha, dest_scan);
-        NextPixel(&src_scan, &dest_scan, Bpp);
-        for (int col = start_col + 1; col < end_col - 1; ++col) {
-          MergeGammaAdjustBgr(&src_scan[-1], r, g, b, a, &dest_scan[0]);
-          SetAlpha(has_alpha, dest_scan);
-          NextPixel(&src_scan, &dest_scan, Bpp);
-        }
-        continue;
-      }
-      MergeGammaAdjust(src_scan[0], r, a, &dest_scan[2]);
-      if (start_col > left) {
-        MergeGammaAdjust(src_scan[-1], g, a, &dest_scan[1]);
-        MergeGammaAdjust(src_scan[-2], b, a, &dest_scan[0]);
-      }
-      SetAlpha(has_alpha, dest_scan);
-      NextPixel(&src_scan, &dest_scan, Bpp);
-      for (int col = start_col + 1; col < end_col - 1; ++col) {
-        MergeGammaAdjustBgr(&src_scan[-2], r, g, b, a, &dest_scan[0]);
-        SetAlpha(has_alpha, dest_scan);
-        NextPixel(&src_scan, &dest_scan, Bpp);
-      }
-      continue;
-    }
+    const uint8_t* src_scan =
+        pGlyph->GetScanline(row).subspan((start_col - left) * 3).data();
+    uint8_t* dest_scan =
+        bitmap->GetWritableScanline(dest_row).subspan(start_col * Bpp).data();
     if (x_subpixel == 0) {
       for (int col = start_col; col < end_col; ++col) {
-        if (bNormal) {
+        if (normalize) {
           int src_value = AverageRgb(&src_scan[0]);
           NormalizeDest(has_alpha, src_value, r, g, b, a, dest_scan);
         } else {
@@ -301,7 +248,7 @@
       continue;
     }
     if (x_subpixel == 1) {
-      if (bNormal) {
+      if (normalize) {
         int src_value = start_col > left ? AverageRgb(&src_scan[-1])
                                          : (src_scan[0] + src_scan[1]) / 3;
         NormalizeSrc(has_alpha, src_value, r, g, b, a, dest_scan);
@@ -314,7 +261,7 @@
       }
       NextPixel(&src_scan, &dest_scan, Bpp);
       for (int col = start_col + 1; col < end_col; ++col) {
-        if (bNormal) {
+        if (normalize) {
           int src_value = AverageRgb(&src_scan[-1]);
           NormalizeDest(has_alpha, src_value, r, g, b, a, dest_scan);
         } else {
@@ -325,7 +272,7 @@
       }
       continue;
     }
-    if (bNormal) {
+    if (normalize) {
       int src_value =
           start_col > left ? AverageRgb(&src_scan[-2]) : src_scan[0] / 3;
       NormalizeSrc(has_alpha, src_value, r, g, b, a, dest_scan);
@@ -339,7 +286,7 @@
     }
     NextPixel(&src_scan, &dest_scan, Bpp);
     for (int col = start_col + 1; col < end_col; ++col) {
-      if (bNormal) {
+      if (normalize) {
         int src_value = AverageRgb(&src_scan[-2]);
         NormalizeDest(has_alpha, src_value, r, g, b, a, dest_scan);
       } else {
@@ -351,9 +298,10 @@
   }
 }
 
-bool ShouldDrawDeviceText(const CFX_Font* pFont, uint32_t text_flags) {
-#if defined(OS_MACOSX)
-  if (text_flags & FXFONT_CIDFONT)
+bool ShouldDrawDeviceText(const CFX_Font* pFont,
+                          const CFX_TextRenderOptions& options) {
+#if BUILDFLAG(IS_APPLE)
+  if (options.font_is_cid)
     return false;
 
   const ByteString bsPsName = pFont->GetPsName();
@@ -366,15 +314,178 @@
   return true;
 }
 
+// Returns true if the path is a 3-point path that draws A->B->A and forms a
+// zero area, or a 2-point path which draws A->B.
+bool CheckSimpleLinePath(pdfium::span<const CFX_Path::Point> points,
+                         const CFX_Matrix* matrix,
+                         bool adjust,
+                         CFX_Path* new_path,
+                         bool* thin,
+                         bool* set_identity) {
+  if (points.size() != 2 && points.size() != 3)
+    return false;
+
+  if (points[0].m_Type != CFX_Path::Point::Type::kMove ||
+      points[1].m_Type != CFX_Path::Point::Type::kLine ||
+      (points.size() == 3 &&
+       (points[2].m_Type != CFX_Path::Point::Type::kLine ||
+        points[0].m_Point != points[2].m_Point))) {
+    return false;
+  }
+
+  // A special case that all points are identical, zero area is formed and no
+  // thin line needs to be drawn.
+  if (points[0].m_Point == points[1].m_Point)
+    return true;
+
+  for (size_t i = 0; i < 2; i++) {
+    CFX_PointF point = points[i].m_Point;
+    if (adjust) {
+      if (matrix)
+        point = matrix->Transform(point);
+
+      point = CFX_PointF(static_cast<int>(point.x) + 0.5f,
+                         static_cast<int>(point.y) + 0.5f);
+    }
+    new_path->AppendPoint(point, points[i].m_Type);
+  }
+  if (adjust && matrix)
+    *set_identity = true;
+
+  *thin = true;
+  return true;
+}
+
+// Returns true if `points` is palindromic and forms zero area. Otherwise,
+// returns false.
+bool CheckPalindromicPath(pdfium::span<const CFX_Path::Point> points,
+                          CFX_Path* new_path,
+                          bool* thin) {
+  if (points.size() <= 3 || !(points.size() % 2))
+    return false;
+
+  const size_t mid = points.size() / 2;
+  CFX_Path temp_path;
+  for (size_t i = 0; i < mid; i++) {
+    const CFX_Path::Point& left = points[mid - i - 1];
+    const CFX_Path::Point& right = points[mid + i + 1];
+    bool zero_area = left.m_Point == right.m_Point &&
+                     left.m_Type != CFX_Path::Point::Type::kBezier &&
+                     right.m_Type != CFX_Path::Point::Type::kBezier;
+    if (!zero_area)
+      return false;
+
+    temp_path.AppendPoint(points[mid - i].m_Point,
+                          CFX_Path::Point::Type::kMove);
+    temp_path.AppendPoint(left.m_Point, CFX_Path::Point::Type::kLine);
+  }
+
+  new_path->Append(temp_path, nullptr);
+  *thin = true;
+  return true;
+}
+
+bool IsFoldingVerticalLine(const CFX_PointF& a,
+                           const CFX_PointF& b,
+                           const CFX_PointF& c) {
+  return a.x == b.x && b.x == c.x && (b.y - a.y) * (b.y - c.y) > 0;
+}
+
+bool IsFoldingHorizontalLine(const CFX_PointF& a,
+                             const CFX_PointF& b,
+                             const CFX_PointF& c) {
+  return a.y == b.y && b.y == c.y && (b.x - a.x) * (b.x - c.x) > 0;
+}
+
+bool IsFoldingDiagonalLine(const CFX_PointF& a,
+                           const CFX_PointF& b,
+                           const CFX_PointF& c) {
+  return a.x != b.x && c.x != b.x && a.y != b.y && c.y != b.y &&
+         (a.y - b.y) * (c.x - b.x) == (c.y - b.y) * (a.x - b.x);
+}
+
+bool GetZeroAreaPath(pdfium::span<const CFX_Path::Point> points,
+                     const CFX_Matrix* matrix,
+                     bool adjust,
+                     CFX_Path* new_path,
+                     bool* thin,
+                     bool* set_identity) {
+  *set_identity = false;
+
+  if (points.size() < 2)
+    return false;
+
+  if (CheckSimpleLinePath(points, matrix, adjust, new_path, thin,
+                          set_identity)) {
+    return true;
+  }
+
+  if (CheckPalindromicPath(points, new_path, thin))
+    return true;
+
+  for (size_t i = 0; i < points.size(); i++) {
+    CFX_Path::Point::Type point_type = points[i].m_Type;
+    if (point_type == CFX_Path::Point::Type::kMove) {
+      DCHECK_EQ(0u, i);
+      continue;
+    }
+
+    if (point_type == CFX_Path::Point::Type::kBezier) {
+      i += 2;
+      DCHECK_LT(i, points.size());
+      continue;
+    }
+
+    DCHECK_EQ(point_type, CFX_Path::Point::Type::kLine);
+    size_t next_index = (i + 1) % (points.size());
+    const CFX_Path::Point& next = points[next_index];
+    if (next.m_Type != CFX_Path::Point::Type::kLine)
+      continue;
+
+    const CFX_Path::Point& prev = points[i - 1];
+    const CFX_Path::Point& cur = points[i];
+    if (IsFoldingVerticalLine(prev.m_Point, cur.m_Point, next.m_Point)) {
+      bool use_prev = fabs(cur.m_Point.y - prev.m_Point.y) <
+                      fabs(cur.m_Point.y - next.m_Point.y);
+      const CFX_Path::Point& start = use_prev ? prev : cur;
+      const CFX_Path::Point& end = use_prev ? cur : next;
+      new_path->AppendPoint(start.m_Point, CFX_Path::Point::Type::kMove);
+      new_path->AppendPoint(end.m_Point, CFX_Path::Point::Type::kLine);
+      continue;
+    }
+
+    if (IsFoldingHorizontalLine(prev.m_Point, cur.m_Point, next.m_Point) ||
+        IsFoldingDiagonalLine(prev.m_Point, cur.m_Point, next.m_Point)) {
+      bool use_prev = fabs(cur.m_Point.x - prev.m_Point.x) <
+                      fabs(cur.m_Point.x - next.m_Point.x);
+      const CFX_Path::Point& start = use_prev ? prev : cur;
+      const CFX_Path::Point& end = use_prev ? cur : next;
+      new_path->AppendPoint(start.m_Point, CFX_Path::Point::Type::kMove);
+      new_path->AppendPoint(end.m_Point, CFX_Path::Point::Type::kLine);
+      continue;
+    }
+  }
+
+  size_t new_path_size = new_path->GetPoints().size();
+  if (points.size() > 3 && new_path_size > 0)
+    *thin = true;
+  return new_path_size != 0;
+}
+
+FXDIB_Format GetCreateCompatibleBitmapFormat(int render_caps) {
+  if (render_caps & FXRC_BYTEMASK_OUTPUT)
+    return FXDIB_Format::k8bppMask;
+  if (render_caps & FXRC_ALPHA_OUTPUT)
+    return FXDIB_Format::kArgb;
+  return CFX_DIBBase::kPlatformRGBFormat;
+}
+
 }  // namespace
 
 CFX_RenderDevice::CFX_RenderDevice() = default;
 
 CFX_RenderDevice::~CFX_RenderDevice() {
   RestoreState(false);
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
-  Flush(true);
-#endif
 }
 
 // static
@@ -385,17 +496,10 @@
   return CFX_Matrix(width, 0, 0, -height, left, top + height);
 }
 
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
-void CFX_RenderDevice::Flush(bool release) {
-  if (release)
-    m_pDeviceDriver.reset();
-  else
-    m_pDeviceDriver->Flush();
-}
-#endif
-
 void CFX_RenderDevice::SetDeviceDriver(
     std::unique_ptr<RenderDeviceDriverIface> pDriver) {
+  DCHECK(pDriver);
+  DCHECK(!m_pDeviceDriver);
   m_pDeviceDriver = std::move(pDriver);
   InitDeviceInfo();
 }
@@ -441,54 +545,40 @@
     const RetainPtr<CFX_DIBitmap>& pDIB,
     int width,
     int height) const {
-  if (m_RenderCaps & FXRC_CMYK_OUTPUT) {
-    return pDIB->Create(
-        width, height,
-        m_RenderCaps & FXRC_ALPHA_OUTPUT ? FXDIB_Cmyka : FXDIB_Cmyk);
-  }
-  if (m_RenderCaps & FXRC_BYTEMASK_OUTPUT)
-    return pDIB->Create(width, height, FXDIB_8bppMask);
-#if defined(OS_MACOSX) || defined _SKIA_SUPPORT_PATHS_
-  constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb32;
-#else
-  constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb;
-#endif
-  return pDIB->Create(
-      width, height,
-      m_RenderCaps & FXRC_ALPHA_OUTPUT ? FXDIB_Argb : kPlatformFormat);
+  return pDIB->Create(width, height,
+                      GetCreateCompatibleBitmapFormat(m_RenderCaps));
 }
 
 void CFX_RenderDevice::SetBaseClip(const FX_RECT& rect) {
   m_pDeviceDriver->SetBaseClip(rect);
 }
 
-bool CFX_RenderDevice::SetClip_PathFill(const CFX_PathData* pPathData,
-                                        const CFX_Matrix* pObject2Device,
-                                        int fill_mode) {
-  if (!m_pDeviceDriver->SetClip_PathFill(pPathData, pObject2Device,
-                                         fill_mode)) {
+bool CFX_RenderDevice::SetClip_PathFill(
+    const CFX_Path& path,
+    const CFX_Matrix* pObject2Device,
+    const CFX_FillRenderOptions& fill_options) {
+  if (!m_pDeviceDriver->SetClip_PathFill(path, pObject2Device, fill_options))
     return false;
-  }
+
   UpdateClipBox();
   return true;
 }
 
 bool CFX_RenderDevice::SetClip_PathStroke(
-    const CFX_PathData* pPathData,
+    const CFX_Path& path,
     const CFX_Matrix* pObject2Device,
     const CFX_GraphStateData* pGraphState) {
-  if (!m_pDeviceDriver->SetClip_PathStroke(pPathData, pObject2Device,
-                                           pGraphState)) {
+  if (!m_pDeviceDriver->SetClip_PathStroke(path, pObject2Device, pGraphState))
     return false;
-  }
+
   UpdateClipBox();
   return true;
 }
 
 bool CFX_RenderDevice::SetClip_Rect(const FX_RECT& rect) {
-  CFX_PathData path;
+  CFX_Path path;
   path.AppendRect(rect.left, rect.bottom, rect.right, rect.top);
-  if (!SetClip_PathFill(&path, nullptr, FXFILL_WINDING))
+  if (!SetClip_PathFill(path, nullptr, CFX_FillRenderOptions::WindingOptions()))
     return false;
 
   UpdateClipBox();
@@ -504,31 +594,44 @@
   m_ClipBox.bottom = m_Height;
 }
 
-bool CFX_RenderDevice::DrawPathWithBlend(const CFX_PathData* pPathData,
-                                         const CFX_Matrix* pObject2Device,
-                                         const CFX_GraphStateData* pGraphState,
-                                         uint32_t fill_color,
-                                         uint32_t stroke_color,
-                                         int fill_mode,
-                                         BlendMode blend_type) {
+bool CFX_RenderDevice::DrawPath(const CFX_Path& path,
+                                const CFX_Matrix* pObject2Device,
+                                const CFX_GraphStateData* pGraphState,
+                                uint32_t fill_color,
+                                uint32_t stroke_color,
+                                const CFX_FillRenderOptions& fill_options) {
+  return DrawPathWithBlend(path, pObject2Device, pGraphState, fill_color,
+                           stroke_color, fill_options, BlendMode::kNormal);
+}
+
+bool CFX_RenderDevice::DrawPathWithBlend(
+    const CFX_Path& path,
+    const CFX_Matrix* pObject2Device,
+    const CFX_GraphStateData* pGraphState,
+    uint32_t fill_color,
+    uint32_t stroke_color,
+    const CFX_FillRenderOptions& fill_options,
+    BlendMode blend_type) {
+  const bool fill =
+      fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill;
+  uint8_t fill_alpha = fill ? FXARGB_A(fill_color) : 0;
   uint8_t stroke_alpha = pGraphState ? FXARGB_A(stroke_color) : 0;
-  uint8_t fill_alpha = (fill_mode & 3) ? FXARGB_A(fill_color) : 0;
-  const std::vector<FX_PATHPOINT>& pPoints = pPathData->GetPoints();
-  if (stroke_alpha == 0 && pPoints.size() == 2) {
-    CFX_PointF pos1 = pPoints[0].m_Point;
-    CFX_PointF pos2 = pPoints[1].m_Point;
+  pdfium::span<const CFX_Path::Point> points = path.GetPoints();
+  if (stroke_alpha == 0 && points.size() == 2) {
+    CFX_PointF pos1 = points[0].m_Point;
+    CFX_PointF pos2 = points[1].m_Point;
     if (pObject2Device) {
       pos1 = pObject2Device->Transform(pos1);
       pos2 = pObject2Device->Transform(pos2);
     }
-    DrawCosmeticLine(pos1, pos2, fill_color, fill_mode, blend_type);
+    DrawCosmeticLine(pos1, pos2, fill_color, fill_options, blend_type);
     return true;
   }
 
-  if ((pPoints.size() == 5 || pPoints.size() == 4) && stroke_alpha == 0) {
-    CFX_FloatRect rect_f;
-    if (!(fill_mode & FXFILL_RECT_AA) &&
-        pPathData->IsRect(pObject2Device, &rect_f)) {
+  if (stroke_alpha == 0 && !fill_options.rect_aa) {
+    absl::optional<CFX_FloatRect> maybe_rect_f = path.GetRect(pObject2Device);
+    if (maybe_rect_f.has_value()) {
+      const CFX_FloatRect& rect_f = maybe_rect_f.value();
       FX_RECT rect_i = rect_f.GetOuterRect();
 
       // Depending on the top/bottom, left/right values of the rect it's
@@ -569,64 +672,87 @@
         return true;
     }
   }
-  if ((fill_mode & 3) && stroke_alpha == 0 && !(fill_mode & FX_FILL_STROKE) &&
-      !(fill_mode & FX_FILL_TEXT_MODE)) {
-    CFX_PathData newPath;
-    bool bThin = false;
-    bool setIdentity = false;
-    if (pPathData->GetZeroAreaPath(pObject2Device,
-                                   !!m_pDeviceDriver->GetDriverType(), &newPath,
-                                   &bThin, &setIdentity)) {
-      CFX_GraphStateData graphState;
-      graphState.m_LineWidth = 0.0f;
 
-      uint32_t strokecolor = fill_color;
-      if (bThin)
-        strokecolor = (((fill_alpha >> 2) << 24) | (strokecolor & 0x00ffffff));
+  if (fill && stroke_alpha == 0 && !fill_options.stroke &&
+      !fill_options.text_mode) {
+    bool adjust = !!m_pDeviceDriver->GetDriverType();
+    std::vector<CFX_Path::Point> sub_path;
+    for (size_t i = 0; i < points.size(); i++) {
+      CFX_Path::Point::Type point_type = points[i].m_Type;
+      if (point_type == CFX_Path::Point::Type::kMove) {
+        // Process the existing sub path.
+        DrawZeroAreaPath(sub_path, pObject2Device, adjust,
+                         fill_options.aliased_path, fill_color, fill_alpha,
+                         blend_type);
+        sub_path.clear();
 
-      const CFX_Matrix* pMatrix = nullptr;
-      if (pObject2Device && !pObject2Device->IsIdentity() && !setIdentity)
-        pMatrix = pObject2Device;
+        // Start forming the next sub path.
+        sub_path.push_back(points[i]);
+        continue;
+      }
 
-      int smooth_path = FX_ZEROAREA_FILL;
-      if (fill_mode & FXFILL_NOPATHSMOOTH)
-        smooth_path |= FXFILL_NOPATHSMOOTH;
+      if (point_type == CFX_Path::Point::Type::kBezier) {
+        sub_path.push_back(points[i]);
+        sub_path.push_back(points[i + 1]);
+        sub_path.push_back(points[i + 2]);
+        i += 2;
+        continue;
+      }
 
-      m_pDeviceDriver->DrawPath(&newPath, pMatrix, &graphState, 0, strokecolor,
-                                smooth_path, blend_type);
+      DCHECK_EQ(point_type, CFX_Path::Point::Type::kLine);
+      sub_path.push_back(points[i]);
     }
+    // Process the last sub paths.
+    DrawZeroAreaPath(sub_path, pObject2Device, adjust,
+                     fill_options.aliased_path, fill_color, fill_alpha,
+                     blend_type);
   }
-  if ((fill_mode & 3) && fill_alpha && stroke_alpha < 0xff &&
-      (fill_mode & FX_FILL_STROKE)) {
+
+  if (fill && fill_alpha && stroke_alpha < 0xff && fill_options.stroke) {
     if (m_RenderCaps & FXRC_FILLSTROKE_PATH) {
-      return m_pDeviceDriver->DrawPath(pPathData, pObject2Device, pGraphState,
-                                       fill_color, stroke_color, fill_mode,
-                                       blend_type);
+#if defined(_SKIA_SUPPORT_)
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+        m_pDeviceDriver->SetGroupKnockout(true);
+      }
+#endif
+      bool draw_fillstroke_path_result = m_pDeviceDriver->DrawPath(
+          path, pObject2Device, pGraphState, fill_color, stroke_color,
+          fill_options, blend_type);
+
+#if defined(_SKIA_SUPPORT_)
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+        // Restore the group knockout status for `m_pDeviceDriver` after
+        // finishing painting a fill-and-stroke path.
+        m_pDeviceDriver->SetGroupKnockout(false);
+      }
+#endif
+      return draw_fillstroke_path_result;
     }
-    return DrawFillStrokePath(pPathData, pObject2Device, pGraphState,
-                              fill_color, stroke_color, fill_mode, blend_type);
+    return DrawFillStrokePath(path, pObject2Device, pGraphState, fill_color,
+                              stroke_color, fill_options, blend_type);
   }
-  return m_pDeviceDriver->DrawPath(pPathData, pObject2Device, pGraphState,
-                                   fill_color, stroke_color, fill_mode,
+  return m_pDeviceDriver->DrawPath(path, pObject2Device, pGraphState,
+                                   fill_color, stroke_color, fill_options,
                                    blend_type);
 }
 
 // This can be removed once PDFium entirely relies on Skia
-bool CFX_RenderDevice::DrawFillStrokePath(const CFX_PathData* pPathData,
-                                          const CFX_Matrix* pObject2Device,
-                                          const CFX_GraphStateData* pGraphState,
-                                          uint32_t fill_color,
-                                          uint32_t stroke_color,
-                                          int fill_mode,
-                                          BlendMode blend_type) {
+bool CFX_RenderDevice::DrawFillStrokePath(
+    const CFX_Path& path,
+    const CFX_Matrix* pObject2Device,
+    const CFX_GraphStateData* pGraphState,
+    uint32_t fill_color,
+    uint32_t stroke_color,
+    const CFX_FillRenderOptions& fill_options,
+    BlendMode blend_type) {
   if (!(m_RenderCaps & FXRC_GET_BITS))
     return false;
   CFX_FloatRect bbox;
   if (pGraphState) {
-    bbox = pPathData->GetBoundingBox(pGraphState->m_LineWidth,
-                                     pGraphState->m_MiterLimit);
+    bbox = path.GetBoundingBoxForStrokePath(pGraphState->m_LineWidth,
+                                            pGraphState->m_MiterLimit);
   } else {
-    bbox = pPathData->GetBoundingBox();
+    bbox = path.GetBoundingBox();
   }
   if (pObject2Device)
     bbox = pObject2Device->TransformRect(bbox);
@@ -640,8 +766,7 @@
   if (!CreateCompatibleBitmap(bitmap, rect.Width(), rect.Height()))
     return false;
 
-  if (bitmap->HasAlpha()) {
-    bitmap->Clear(0);
+  if (bitmap->IsAlphaFormat()) {
     backdrop->Copy(bitmap);
   } else {
     if (!m_pDeviceDriver->GetDIBits(bitmap, rect.left, rect.top))
@@ -649,20 +774,18 @@
     backdrop->Copy(bitmap);
   }
   CFX_DefaultRenderDevice bitmap_device;
-  bitmap_device.Attach(bitmap, false, backdrop, true);
+  bitmap_device.AttachWithBackdropAndGroupKnockout(bitmap, std::move(backdrop),
+                                                   /*bGroupKnockout=*/true);
 
   CFX_Matrix matrix;
   if (pObject2Device)
     matrix = *pObject2Device;
   matrix.Translate(-rect.left, -rect.top);
-  if (!bitmap_device.GetDeviceDriver()->DrawPath(
-          pPathData, &matrix, pGraphState, fill_color, stroke_color, fill_mode,
-          blend_type)) {
+  if (!bitmap_device.GetDeviceDriver()->DrawPath(path, &matrix, pGraphState,
+                                                 fill_color, stroke_color,
+                                                 fill_options, blend_type)) {
     return false;
   }
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
-  bitmap_device.GetDeviceDriver()->Flush();
-#endif
   FX_RECT src_rect(0, 0, rect.Width(), rect.Height());
   return m_pDeviceDriver->SetDIBits(bitmap, 0, src_rect, rect.left, rect.top,
                                     BlendMode::kNormal);
@@ -684,31 +807,68 @@
   if (!m_pDeviceDriver->GetDIBits(bitmap, rect.left, rect.top))
     return false;
 
-  if (!bitmap->CompositeRect(0, 0, rect.Width(), rect.Height(), fill_color,
-                             0)) {
+  if (!bitmap->CompositeRect(0, 0, rect.Width(), rect.Height(), fill_color))
     return false;
-  }
+
   FX_RECT src_rect(0, 0, rect.Width(), rect.Height());
   m_pDeviceDriver->SetDIBits(bitmap, 0, src_rect, rect.left, rect.top,
                              BlendMode::kNormal);
   return true;
 }
 
-bool CFX_RenderDevice::DrawCosmeticLine(const CFX_PointF& ptMoveTo,
-                                        const CFX_PointF& ptLineTo,
-                                        uint32_t color,
-                                        int fill_mode,
-                                        BlendMode blend_type) {
+bool CFX_RenderDevice::DrawCosmeticLine(
+    const CFX_PointF& ptMoveTo,
+    const CFX_PointF& ptLineTo,
+    uint32_t color,
+    const CFX_FillRenderOptions& fill_options,
+    BlendMode blend_type) {
   if ((color >= 0xff000000) && m_pDeviceDriver->DrawCosmeticLine(
                                    ptMoveTo, ptLineTo, color, blend_type)) {
     return true;
   }
   CFX_GraphStateData graph_state;
-  CFX_PathData path;
-  path.AppendPoint(ptMoveTo, FXPT_TYPE::MoveTo, false);
-  path.AppendPoint(ptLineTo, FXPT_TYPE::LineTo, false);
-  return m_pDeviceDriver->DrawPath(&path, nullptr, &graph_state, 0, color,
-                                   fill_mode, blend_type);
+  CFX_Path path;
+  path.AppendPoint(ptMoveTo, CFX_Path::Point::Type::kMove);
+  path.AppendPoint(ptLineTo, CFX_Path::Point::Type::kLine);
+  return m_pDeviceDriver->DrawPath(path, nullptr, &graph_state, 0, color,
+                                   fill_options, blend_type);
+}
+
+void CFX_RenderDevice::DrawZeroAreaPath(
+    const std::vector<CFX_Path::Point>& path,
+    const CFX_Matrix* matrix,
+    bool adjust,
+    bool aliased_path,
+    uint32_t fill_color,
+    uint8_t fill_alpha,
+    BlendMode blend_type) {
+  if (path.empty())
+    return;
+
+  CFX_Path new_path;
+  bool thin = false;
+  bool set_identity = false;
+
+  if (!GetZeroAreaPath(path, matrix, adjust, &new_path, &thin, &set_identity))
+    return;
+
+  CFX_GraphStateData graph_state;
+  graph_state.m_LineWidth = 0.0f;
+
+  uint32_t stroke_color = fill_color;
+  if (thin)
+    stroke_color = (((fill_alpha >> 2) << 24) | (stroke_color & 0x00ffffff));
+
+  const CFX_Matrix* new_matrix = nullptr;
+  if (matrix && !matrix->IsIdentity() && !set_identity)
+    new_matrix = matrix;
+
+  CFX_FillRenderOptions path_options;
+  path_options.zero_area = true;
+  path_options.aliased_path = aliased_path;
+
+  m_pDeviceDriver->DrawPath(new_path, new_matrix, &graph_state, 0, stroke_color,
+                            path_options, blend_type);
 }
 
 bool CFX_RenderDevice::GetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
@@ -726,7 +886,7 @@
                                           int left,
                                           int top,
                                           BlendMode blend_mode) {
-  ASSERT(!pBitmap->IsAlphaMask());
+  DCHECK(!pBitmap->IsMaskFormat());
   FX_RECT dest_rect(left, top, left + pBitmap->GetWidth(),
                     top + pBitmap->GetHeight());
   dest_rect.Intersect(m_ClipBox);
@@ -737,7 +897,7 @@
                    dest_rect.left - left + dest_rect.Width(),
                    dest_rect.top - top + dest_rect.Height());
   if ((blend_mode == BlendMode::kNormal || (m_RenderCaps & FXRC_BLEND_MODE)) &&
-      (!pBitmap->HasAlpha() || (m_RenderCaps & FXRC_ALPHA_IMAGE))) {
+      (!pBitmap->IsAlphaFormat() || (m_RenderCaps & FXRC_ALPHA_IMAGE))) {
     return m_pDeviceDriver->SetDIBits(pBitmap, 0, src_rect, dest_rect.left,
                                       dest_rect.top, blend_mode);
   }
@@ -747,14 +907,13 @@
   int bg_pixel_width = dest_rect.Width();
   int bg_pixel_height = dest_rect.Height();
   auto background = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!background->Create(
-          bg_pixel_width, bg_pixel_height,
-          (m_RenderCaps & FXRC_CMYK_OUTPUT) ? FXDIB_Cmyk : FXDIB_Rgb32)) {
+  if (!background->Create(bg_pixel_width, bg_pixel_height,
+                          FXDIB_Format::kRgb32)) {
     return false;
   }
-  if (!m_pDeviceDriver->GetDIBits(background, dest_rect.left, dest_rect.top)) {
+  if (!m_pDeviceDriver->GetDIBits(background, dest_rect.left, dest_rect.top))
     return false;
-  }
+
   if (!background->CompositeBitmap(0, 0, bg_pixel_width, bg_pixel_height,
                                    pBitmap, src_rect.left, src_rect.top,
                                    blend_mode, nullptr, false)) {
@@ -833,11 +992,7 @@
   return m_pDeviceDriver->ContinueDIBits(handle, pPause);
 }
 
-#ifdef _SKIA_SUPPORT_
-void CFX_RenderDevice::DebugVerifyBitmapIsPreMultiplied() const {
-  NOTREACHED();
-}
-
+#if defined(_SKIA_SUPPORT_)
 bool CFX_RenderDevice::SetBitsWithMask(const RetainPtr<CFX_DIBBase>& pBitmap,
                                        const RetainPtr<CFX_DIBBase>& pMask,
                                        int left,
@@ -849,97 +1004,99 @@
 }
 #endif
 
-bool CFX_RenderDevice::DrawNormalText(int nChars,
-                                      const TextCharPos* pCharPos,
+bool CFX_RenderDevice::DrawNormalText(pdfium::span<const TextCharPos> pCharPos,
                                       CFX_Font* pFont,
                                       float font_size,
                                       const CFX_Matrix& mtText2Device,
                                       uint32_t fill_color,
-                                      uint32_t text_flags) {
-  int nativetext_flags = text_flags;
-  if (m_DeviceType != DeviceType::kDisplay) {
-    if (!(text_flags & FXTEXT_PRINTGRAPHICTEXT)) {
-      if (ShouldDrawDeviceText(pFont, text_flags) &&
-          m_pDeviceDriver->DrawDeviceText(
-              nChars, pCharPos, pFont, mtText2Device, font_size, fill_color)) {
-        return true;
-      }
-    }
-    if (FXARGB_A(fill_color) < 255)
-      return false;
-  } else if (!(text_flags & FXTEXT_NO_NATIVETEXT)) {
-    if (ShouldDrawDeviceText(pFont, text_flags) &&
-        m_pDeviceDriver->DrawDeviceText(nChars, pCharPos, pFont, mtText2Device,
-                                        font_size, fill_color)) {
-      return true;
-    }
-  }
-  CFX_Matrix char2device = mtText2Device;
-  CFX_Matrix text2Device = mtText2Device;
-  char2device.Scale(font_size, -font_size);
-  if (fabs(char2device.a) + fabs(char2device.b) > 50 * 1.0f ||
-      (m_DeviceType == DeviceType::kPrinter &&
-       !(text_flags & FXTEXT_PRINTIMAGETEXT))) {
-    if (pFont->GetFaceRec()) {
-      int nPathFlags =
-          (text_flags & FXTEXT_NOSMOOTH) == 0 ? 0 : FXFILL_NOPATHSMOOTH;
-      return DrawTextPath(nChars, pCharPos, pFont, font_size, mtText2Device,
-                          nullptr, nullptr, fill_color, 0, nullptr, nPathFlags);
-    }
-  }
+                                      const CFX_TextRenderOptions& options) {
+  // `anti_alias` and `normalize` don't affect Skia rendering.
   int anti_alias = FT_RENDER_MODE_MONO;
-  bool bNormal = false;
-  if ((text_flags & FXTEXT_NOSMOOTH) == 0) {
-    if (m_DeviceType == DeviceType::kDisplay && m_bpp > 1) {
+  bool normalize = false;
+  const bool is_text_smooth = options.IsSmooth();
+  // |text_options| has the potential to affect all derived classes of
+  // RenderDeviceDriverIface. But now it only affects Skia rendering.
+  CFX_TextRenderOptions text_options(options);
+  if (is_text_smooth) {
+    if (GetDeviceType() == DeviceType::kDisplay && m_bpp > 1) {
       if (!CFX_GEModule::Get()->GetFontMgr()->FTLibrarySupportsHinting()) {
         // Some Freetype implementations (like the one packaged with Fedora) do
         // not support hinting due to patents 6219025, 6239783, 6307566,
         // 6225973, 6243070, 6393145, 6421054, 6282327, and 6624828; the latest
-        // one expires 10/7/19.  This makes LCD antialiasing very ugly, so we
-        // instead fall back on NORMAL antialiasing.
+        // one expires 10/7/19.  This makes LCD anti-aliasing very ugly, so we
+        // instead fall back on NORMAL anti-aliasing.
         anti_alias = FT_RENDER_MODE_NORMAL;
-      } else if ((m_RenderCaps & (FXRC_ALPHA_OUTPUT | FXRC_CMYK_OUTPUT))) {
+        if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+          // Since |anti_alias| doesn't affect Skia rendering, and Skia only
+          // follows strictly to the options provided by |text_options|, we need
+          // to update |text_options| so that Skia falls back on normal
+          // anti-aliasing as well.
+          text_options.aliasing_type = CFX_TextRenderOptions::kAntiAliasing;
+        }
+      } else if ((m_RenderCaps & FXRC_ALPHA_OUTPUT)) {
+        // Whether Skia uses LCD optimization should strictly follow the
+        // rendering options provided by |text_options|. No change needs to be
+        // done for |text_options| here.
         anti_alias = FT_RENDER_MODE_LCD;
-        bNormal = true;
+        normalize = true;
       } else if (m_bpp < 16) {
+        // This case doesn't apply to Skia since Skia always have |m_bpp| = 32.
         anti_alias = FT_RENDER_MODE_NORMAL;
       } else {
+        // Whether Skia uses LCD optimization should strictly follow the
+        // rendering options provided by |text_options|. No change needs to be
+        // done for |text_options| here.
         anti_alias = FT_RENDER_MODE_LCD;
-
-        bool bClearType = false;
-        if (pFont->GetFaceRec())
-          bClearType = !!(text_flags & FXTEXT_CLEARTYPE);
-        bNormal = !bClearType;
+        normalize = !pFont->GetFaceRec() ||
+                    options.aliasing_type != CFX_TextRenderOptions::kLcd;
       }
     }
   }
-  std::vector<TextGlyphPos> glyphs(nChars);
-  CFX_Matrix deviceCtm = char2device;
 
+  if (GetDeviceType() != DeviceType::kDisplay) {
+    if (ShouldDrawDeviceText(pFont, options) &&
+        m_pDeviceDriver->DrawDeviceText(pCharPos, pFont, mtText2Device,
+                                        font_size, fill_color, text_options)) {
+      return true;
+    }
+    if (FXARGB_A(fill_color) < 255)
+      return false;
+  } else if (options.native_text) {
+    if (ShouldDrawDeviceText(pFont, options) &&
+        m_pDeviceDriver->DrawDeviceText(pCharPos, pFont, mtText2Device,
+                                        font_size, fill_color, text_options)) {
+      return true;
+    }
+  }
+
+  CFX_Matrix char2device = mtText2Device;
+  CFX_Matrix text2Device = mtText2Device;
+  char2device.Scale(font_size, -font_size);
+  if (fabs(char2device.a) + fabs(char2device.b) > 50 * 1.0f ||
+      GetDeviceType() == DeviceType::kPrinter) {
+    if (pFont->GetFaceRec()) {
+      CFX_FillRenderOptions path_options;
+      path_options.aliased_path = !is_text_smooth;
+      return DrawTextPath(pCharPos, pFont, font_size, mtText2Device, nullptr,
+                          nullptr, fill_color, 0, nullptr, path_options);
+    }
+  }
+  std::vector<TextGlyphPos> glyphs(pCharPos.size());
   for (size_t i = 0; i < glyphs.size(); ++i) {
     TextGlyphPos& glyph = glyphs[i];
     const TextCharPos& charpos = pCharPos[i];
 
-    glyph.m_fOrigin = text2Device.Transform(charpos.m_Origin);
+    glyph.m_fDeviceOrigin = text2Device.Transform(charpos.m_Origin);
     if (anti_alias < FT_RENDER_MODE_LCD)
-      glyph.m_Origin.x = FXSYS_roundf(glyph.m_fOrigin.x);
+      glyph.m_Origin.x = FXSYS_roundf(glyph.m_fDeviceOrigin.x);
     else
-      glyph.m_Origin.x = static_cast<int>(floor(glyph.m_fOrigin.x));
-    glyph.m_Origin.y = FXSYS_roundf(glyph.m_fOrigin.y);
+      glyph.m_Origin.x = static_cast<int>(floor(glyph.m_fDeviceOrigin.x));
+    glyph.m_Origin.y = FXSYS_roundf(glyph.m_fDeviceOrigin.y);
 
-    if (charpos.m_bGlyphAdjust) {
-      CFX_Matrix new_matrix(
-          charpos.m_AdjustMatrix[0], charpos.m_AdjustMatrix[1],
-          charpos.m_AdjustMatrix[2], charpos.m_AdjustMatrix[3], 0, 0);
-      new_matrix.Concat(deviceCtm);
-      glyph.m_pGlyph = pFont->LoadGlyphBitmap(
-          charpos.m_GlyphIndex, charpos.m_bFontStyle, new_matrix,
-          charpos.m_FontCharWidth, anti_alias, &nativetext_flags);
-    } else {
-      glyph.m_pGlyph = pFont->LoadGlyphBitmap(
-          charpos.m_GlyphIndex, charpos.m_bFontStyle, deviceCtm,
-          charpos.m_FontCharWidth, anti_alias, &nativetext_flags);
-    }
+    CFX_Matrix matrix = charpos.GetEffectiveMatrix(char2device);
+    glyph.m_pGlyph = pFont->LoadGlyphBitmap(
+        charpos.m_GlyphIndex, charpos.m_bFontStyle, matrix,
+        charpos.m_FontCharWidth, anti_alias, &text_options);
   }
   if (anti_alias < FT_RENDER_MODE_LCD && glyphs.size() > 1)
     AdjustGlyphSpace(&glyphs);
@@ -955,40 +1112,36 @@
   int pixel_top = bmp_rect.top;
   if (anti_alias == FT_RENDER_MODE_MONO) {
     auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-    if (!bitmap->Create(pixel_width, pixel_height, FXDIB_1bppMask))
+    if (!bitmap->Create(pixel_width, pixel_height, FXDIB_Format::k1bppMask))
       return false;
-    bitmap->Clear(0);
     for (const TextGlyphPos& glyph : glyphs) {
       if (!glyph.m_pGlyph)
         continue;
 
-      Optional<CFX_Point> point = glyph.GetOrigin({pixel_left, pixel_top});
+      absl::optional<CFX_Point> point =
+          glyph.GetOrigin({pixel_left, pixel_top});
       if (!point.has_value())
         continue;
 
       const RetainPtr<CFX_DIBitmap>& pGlyph = glyph.m_pGlyph->GetBitmap();
-      bitmap->TransferBitmap(point.value().x, point.value().y,
-                             pGlyph->GetWidth(), pGlyph->GetHeight(), pGlyph, 0,
-                             0);
+      bitmap->CompositeOneBPPMask(point.value().x, point.value().y,
+                                  pGlyph->GetWidth(), pGlyph->GetHeight(),
+                                  pGlyph, 0, 0);
     }
     return SetBitMask(bitmap, bmp_rect.left, bmp_rect.top, fill_color);
   }
   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
   if (m_bpp == 8) {
-    if (!bitmap->Create(pixel_width, pixel_height, FXDIB_8bppMask))
+    if (!bitmap->Create(pixel_width, pixel_height, FXDIB_Format::k8bppMask))
       return false;
   } else {
     if (!CreateCompatibleBitmap(bitmap, pixel_width, pixel_height))
       return false;
   }
-  if (!bitmap->HasAlpha() && !bitmap->IsAlphaMask()) {
+  if (!bitmap->IsAlphaFormat() && !bitmap->IsMaskFormat()) {
     bitmap->Clear(0xFFFFFFFF);
     if (!GetDIBits(bitmap, bmp_rect.left, bmp_rect.top))
       return false;
-  } else {
-    bitmap->Clear(0);
-    if (bitmap->m_pAlphaMask)
-      bitmap->m_pAlphaMask->Clear(0);
   }
   int dest_width = pixel_width;
   int a = 0;
@@ -1002,7 +1155,7 @@
     if (!glyph.m_pGlyph)
       continue;
 
-    Optional<CFX_Point> point = glyph.GetOrigin({pixel_left, pixel_top});
+    absl::optional<CFX_Point> point = glyph.GetOrigin({pixel_left, pixel_top});
     if (!point.has_value())
       continue;
 
@@ -1017,9 +1170,8 @@
       }
       continue;
     }
-    bool bBGRStripe = !!(text_flags & FXTEXT_BGR_STRIPE);
     ncols /= 3;
-    int x_subpixel = static_cast<int>(glyph.m_fOrigin.x * 3) % 3;
+    int x_subpixel = static_cast<int>(glyph.m_fDeviceOrigin.x * 3) % 3;
     int start_col = std::max(point->x, 0);
     FX_SAFE_INT32 end_col_safe = point->x;
     end_col_safe += ncols;
@@ -1031,17 +1183,17 @@
       continue;
 
     DrawNormalTextHelper(bitmap, pGlyph, nrows, point->x, point->y, start_col,
-                         end_col, bNormal, bBGRStripe, x_subpixel, a, r, g, b);
+                         end_col, normalize, x_subpixel, a, r, g, b);
   }
-  if (bitmap->IsAlphaMask())
+
+  if (bitmap->IsMaskFormat())
     SetBitMask(bitmap, bmp_rect.left, bmp_rect.top, fill_color);
   else
     SetDIBits(bitmap, bmp_rect.left, bmp_rect.top);
   return true;
 }
 
-bool CFX_RenderDevice::DrawTextPath(int nChars,
-                                    const TextCharPos* pCharPos,
+bool CFX_RenderDevice::DrawTextPath(pdfium::span<const TextCharPos> pCharPos,
                                     CFX_Font* pFont,
                                     float font_size,
                                     const CFX_Matrix& mtText2User,
@@ -1049,40 +1201,34 @@
                                     const CFX_GraphStateData* pGraphState,
                                     uint32_t fill_color,
                                     FX_ARGB stroke_color,
-                                    CFX_PathData* pClippingPath,
-                                    int nFlag) {
-  for (int iChar = 0; iChar < nChars; ++iChar) {
-    const TextCharPos& charpos = pCharPos[iChar];
-    CFX_Matrix matrix;
-    if (charpos.m_bGlyphAdjust) {
-      matrix = CFX_Matrix(charpos.m_AdjustMatrix[0], charpos.m_AdjustMatrix[1],
-                          charpos.m_AdjustMatrix[2], charpos.m_AdjustMatrix[3],
-                          0, 0);
-    }
-    matrix.Concat(CFX_Matrix(font_size, 0, 0, font_size, charpos.m_Origin.x,
-                             charpos.m_Origin.y));
-    const CFX_PathData* pPath =
+                                    CFX_Path* pClippingPath,
+                                    const CFX_FillRenderOptions& fill_options) {
+  for (const auto& charpos : pCharPos) {
+    const CFX_Path* pPath =
         pFont->LoadGlyphPath(charpos.m_GlyphIndex, charpos.m_FontCharWidth);
     if (!pPath)
       continue;
 
+    CFX_Matrix matrix(font_size, 0, 0, font_size, charpos.m_Origin.x,
+                      charpos.m_Origin.y);
+    matrix = charpos.GetEffectiveMatrix(matrix);
     matrix.Concat(mtText2User);
 
-    CFX_PathData TransformedPath(*pPath);
-    TransformedPath.Transform(matrix);
+    CFX_Path transformed_path(*pPath);
+    transformed_path.Transform(matrix);
     if (fill_color || stroke_color) {
-      int fill_mode = nFlag;
+      CFX_FillRenderOptions options(fill_options);
       if (fill_color)
-        fill_mode |= FXFILL_WINDING;
-      fill_mode |= FX_FILL_TEXT_MODE;
-      if (!DrawPathWithBlend(&TransformedPath, pUser2Device, pGraphState,
-                             fill_color, stroke_color, fill_mode,
+        options.fill_type = CFX_FillRenderOptions::FillType::kWinding;
+      options.text_mode = true;
+      if (!DrawPathWithBlend(transformed_path, pUser2Device, pGraphState,
+                             fill_color, stroke_color, options,
                              BlendMode::kNormal)) {
         return false;
       }
     }
     if (pClippingPath)
-      pClippingPath->Append(&TransformedPath, pUser2Device);
+      pClippingPath->Append(transformed_path, pUser2Device);
   }
   return true;
 }
@@ -1090,21 +1236,23 @@
 void CFX_RenderDevice::DrawFillRect(const CFX_Matrix* pUser2Device,
                                     const CFX_FloatRect& rect,
                                     const FX_COLORREF& color) {
-  CFX_PathData path;
+  CFX_Path path;
   path.AppendFloatRect(rect);
-  DrawPath(&path, pUser2Device, nullptr, color, 0, FXFILL_WINDING);
+  DrawPath(path, pUser2Device, nullptr, color, 0,
+           CFX_FillRenderOptions::WindingOptions());
 }
 
 void CFX_RenderDevice::DrawFillArea(const CFX_Matrix& mtUser2Device,
                                     const std::vector<CFX_PointF>& points,
                                     const FX_COLORREF& color) {
-  ASSERT(!points.empty());
-  CFX_PathData path;
-  path.AppendPoint(points[0], FXPT_TYPE::MoveTo, false);
+  DCHECK(!points.empty());
+  CFX_Path path;
+  path.AppendPoint(points[0], CFX_Path::Point::Type::kMove);
   for (size_t i = 1; i < points.size(); ++i)
-    path.AppendPoint(points[i], FXPT_TYPE::LineTo, false);
+    path.AppendPoint(points[i], CFX_Path::Point::Type::kLine);
 
-  DrawPath(&path, &mtUser2Device, nullptr, color, 0, FXFILL_ALTERNATE);
+  DrawPath(path, &mtUser2Device, nullptr, color, 0,
+           CFX_FillRenderOptions::EvenOddOptions());
 }
 
 void CFX_RenderDevice::DrawStrokeRect(const CFX_Matrix& mtUser2Device,
@@ -1114,9 +1262,10 @@
   CFX_GraphStateData gsd;
   gsd.m_LineWidth = fWidth;
 
-  CFX_PathData path;
+  CFX_Path path;
   path.AppendFloatRect(rect);
-  DrawPath(&path, &mtUser2Device, &gsd, 0, color, FXFILL_ALTERNATE);
+  DrawPath(path, &mtUser2Device, &gsd, 0, color,
+           CFX_FillRenderOptions::EvenOddOptions());
 }
 
 void CFX_RenderDevice::DrawStrokeLine(const CFX_Matrix* pUser2Device,
@@ -1124,14 +1273,15 @@
                                       const CFX_PointF& ptLineTo,
                                       const FX_COLORREF& color,
                                       float fWidth) {
-  CFX_PathData path;
-  path.AppendPoint(ptMoveTo, FXPT_TYPE::MoveTo, false);
-  path.AppendPoint(ptLineTo, FXPT_TYPE::LineTo, false);
+  CFX_Path path;
+  path.AppendPoint(ptMoveTo, CFX_Path::Point::Type::kMove);
+  path.AppendPoint(ptLineTo, CFX_Path::Point::Type::kLine);
 
   CFX_GraphStateData gsd;
   gsd.m_LineWidth = fWidth;
 
-  DrawPath(&path, pUser2Device, &gsd, 0, color, FXFILL_ALTERNATE);
+  DrawPath(path, pUser2Device, &gsd, 0, color,
+           CFX_FillRenderOptions::EvenOddOptions());
 }
 
 void CFX_RenderDevice::DrawFillRect(const CFX_Matrix* pUser2Device,
@@ -1142,35 +1292,35 @@
 }
 
 void CFX_RenderDevice::DrawShadow(const CFX_Matrix& mtUser2Device,
-                                  bool bVertical,
-                                  bool bHorizontal,
                                   const CFX_FloatRect& rect,
                                   int32_t nTransparency,
                                   int32_t nStartGray,
                                   int32_t nEndGray) {
-  float fStepGray = 1.0f;
+  constexpr float kBorder = 0.5f;
+  constexpr float kSegmentWidth = 1.0f;
+  constexpr float kLineWidth = 1.5f;
 
-  if (bVertical) {
-    fStepGray = (nEndGray - nStartGray) / rect.Height();
+  float fStepGray = (nEndGray - nStartGray) / rect.Height();
+  CFX_PointF start(rect.left, 0);
+  CFX_PointF end(rect.right, 0);
 
-    for (float fy = rect.bottom + 0.5f; fy <= rect.top - 0.5f; fy += 1.0f) {
-      int32_t nGray = nStartGray + (int32_t)(fStepGray * (fy - rect.bottom));
-      DrawStrokeLine(&mtUser2Device, CFX_PointF(rect.left, fy),
-                     CFX_PointF(rect.right, fy),
-                     ArgbEncode(nTransparency, nGray, nGray, nGray), 1.5f);
-    }
+  for (float fy = rect.bottom + kBorder; fy <= rect.top - kBorder;
+       fy += kSegmentWidth) {
+    start.y = fy;
+    end.y = fy;
+    int nGray = nStartGray + static_cast<int>(fStepGray * (fy - rect.bottom));
+    FX_ARGB color = ArgbEncode(nTransparency, nGray, nGray, nGray);
+    DrawStrokeLine(&mtUser2Device, start, end, color, kLineWidth);
   }
+}
 
-  if (bHorizontal) {
-    fStepGray = (nEndGray - nStartGray) / rect.Width();
-
-    for (float fx = rect.left + 0.5f; fx <= rect.right - 0.5f; fx += 1.0f) {
-      int32_t nGray = nStartGray + (int32_t)(fStepGray * (fx - rect.left));
-      DrawStrokeLine(&mtUser2Device, CFX_PointF(fx, rect.bottom),
-                     CFX_PointF(fx, rect.top),
-                     ArgbEncode(nTransparency, nGray, nGray, nGray), 1.5f);
-    }
-  }
+bool CFX_RenderDevice::DrawShading(const CPDF_ShadingPattern* pPattern,
+                                   const CFX_Matrix* pMatrix,
+                                   const FX_RECT& clip_rect,
+                                   int alpha,
+                                   bool bAlphaMode) {
+  return m_pDeviceDriver->DrawShading(pPattern, pMatrix, clip_rect, alpha,
+                                      bAlphaMode);
 }
 
 void CFX_RenderDevice::DrawBorder(const CFX_Matrix* pUser2Device,
@@ -1181,130 +1331,131 @@
                                   const CFX_Color& crRightBottom,
                                   BorderStyle nStyle,
                                   int32_t nTransparency) {
-  float fLeft = rect.left;
-  float fRight = rect.right;
-  float fTop = rect.top;
-  float fBottom = rect.bottom;
+  if (fWidth <= 0.0f)
+    return;
 
-  if (fWidth > 0.0f) {
-    float fHalfWidth = fWidth / 2.0f;
+  const float fLeft = rect.left;
+  const float fRight = rect.right;
+  const float fTop = rect.top;
+  const float fBottom = rect.bottom;
+  const float fHalfWidth = fWidth / 2.0f;
 
-    switch (nStyle) {
-      default:
-      case BorderStyle::SOLID: {
-        CFX_PathData path;
-        path.AppendRect(fLeft, fBottom, fRight, fTop);
-        path.AppendRect(fLeft + fWidth, fBottom + fWidth, fRight - fWidth,
-                        fTop - fWidth);
-        DrawPath(&path, pUser2Device, nullptr, color.ToFXColor(nTransparency),
-                 0, FXFILL_ALTERNATE);
-        break;
-      }
-      case BorderStyle::DASH: {
-        CFX_PathData path;
-        path.AppendPoint(
-            CFX_PointF(fLeft + fWidth / 2.0f, fBottom + fWidth / 2.0f),
-            FXPT_TYPE::MoveTo, false);
-        path.AppendPoint(
-            CFX_PointF(fLeft + fWidth / 2.0f, fTop - fWidth / 2.0f),
-            FXPT_TYPE::LineTo, false);
-        path.AppendPoint(
-            CFX_PointF(fRight - fWidth / 2.0f, fTop - fWidth / 2.0f),
-            FXPT_TYPE::LineTo, false);
-        path.AppendPoint(
-            CFX_PointF(fRight - fWidth / 2.0f, fBottom + fWidth / 2.0f),
-            FXPT_TYPE::LineTo, false);
-        path.AppendPoint(
-            CFX_PointF(fLeft + fWidth / 2.0f, fBottom + fWidth / 2.0f),
-            FXPT_TYPE::LineTo, false);
+  switch (nStyle) {
+    case BorderStyle::kSolid: {
+      CFX_Path path;
+      path.AppendRect(fLeft, fBottom, fRight, fTop);
+      path.AppendRect(fLeft + fWidth, fBottom + fWidth, fRight - fWidth,
+                      fTop - fWidth);
+      DrawPath(path, pUser2Device, nullptr, color.ToFXColor(nTransparency), 0,
+               CFX_FillRenderOptions::EvenOddOptions());
+      break;
+    }
+    case BorderStyle::kDash: {
+      CFX_GraphStateData gsd;
+      gsd.m_DashArray = {3.0f, 3.0f};
+      gsd.m_DashPhase = 0;
+      gsd.m_LineWidth = fWidth;
 
-        CFX_GraphStateData gsd;
-        gsd.m_DashArray = {3.0f, 3.0f};
-        gsd.m_DashPhase = 0;
-        gsd.m_LineWidth = fWidth;
-        DrawPath(&path, pUser2Device, &gsd, 0, color.ToFXColor(nTransparency),
-                 FXFILL_WINDING);
-        break;
-      }
-      case BorderStyle::BEVELED:
-      case BorderStyle::INSET: {
-        CFX_GraphStateData gsd;
-        gsd.m_LineWidth = fHalfWidth;
+      CFX_Path path;
+      path.AppendPoint(CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth),
+                       CFX_Path::Point::Type::kMove);
+      path.AppendPoint(CFX_PointF(fLeft + fHalfWidth, fTop - fHalfWidth),
+                       CFX_Path::Point::Type::kLine);
+      path.AppendPoint(CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth),
+                       CFX_Path::Point::Type::kLine);
+      path.AppendPoint(CFX_PointF(fRight - fHalfWidth, fBottom + fHalfWidth),
+                       CFX_Path::Point::Type::kLine);
+      path.AppendPoint(CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth),
+                       CFX_Path::Point::Type::kLine);
+      DrawPath(path, pUser2Device, &gsd, 0, color.ToFXColor(nTransparency),
+               CFX_FillRenderOptions::WindingOptions());
+      break;
+    }
+    case BorderStyle::kBeveled:
+    case BorderStyle::kInset: {
+      CFX_GraphStateData gsd;
+      gsd.m_LineWidth = fHalfWidth;
 
-        CFX_PathData pathLT;
+      CFX_Path path_left_top;
+      path_left_top.AppendPoint(
+          CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth),
+          CFX_Path::Point::Type::kMove);
+      path_left_top.AppendPoint(
+          CFX_PointF(fLeft + fHalfWidth, fTop - fHalfWidth),
+          CFX_Path::Point::Type::kLine);
+      path_left_top.AppendPoint(
+          CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth),
+          CFX_Path::Point::Type::kLine);
+      path_left_top.AppendPoint(CFX_PointF(fRight - fWidth, fTop - fWidth),
+                                CFX_Path::Point::Type::kLine);
+      path_left_top.AppendPoint(CFX_PointF(fLeft + fWidth, fTop - fWidth),
+                                CFX_Path::Point::Type::kLine);
+      path_left_top.AppendPoint(CFX_PointF(fLeft + fWidth, fBottom + fWidth),
+                                CFX_Path::Point::Type::kLine);
+      path_left_top.AppendPoint(
+          CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth),
+          CFX_Path::Point::Type::kLine);
+      DrawPath(path_left_top, pUser2Device, &gsd,
+               crLeftTop.ToFXColor(nTransparency), 0,
+               CFX_FillRenderOptions::EvenOddOptions());
 
-        pathLT.AppendPoint(CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth),
-                           FXPT_TYPE::MoveTo, false);
-        pathLT.AppendPoint(CFX_PointF(fLeft + fHalfWidth, fTop - fHalfWidth),
-                           FXPT_TYPE::LineTo, false);
-        pathLT.AppendPoint(CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth),
-                           FXPT_TYPE::LineTo, false);
-        pathLT.AppendPoint(
-            CFX_PointF(fRight - fHalfWidth * 2, fTop - fHalfWidth * 2),
-            FXPT_TYPE::LineTo, false);
-        pathLT.AppendPoint(
-            CFX_PointF(fLeft + fHalfWidth * 2, fTop - fHalfWidth * 2),
-            FXPT_TYPE::LineTo, false);
-        pathLT.AppendPoint(
-            CFX_PointF(fLeft + fHalfWidth * 2, fBottom + fHalfWidth * 2),
-            FXPT_TYPE::LineTo, false);
-        pathLT.AppendPoint(CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth),
-                           FXPT_TYPE::LineTo, false);
+      CFX_Path path_right_bottom;
+      path_right_bottom.AppendPoint(
+          CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth),
+          CFX_Path::Point::Type::kMove);
+      path_right_bottom.AppendPoint(
+          CFX_PointF(fRight - fHalfWidth, fBottom + fHalfWidth),
+          CFX_Path::Point::Type::kLine);
+      path_right_bottom.AppendPoint(
+          CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth),
+          CFX_Path::Point::Type::kLine);
+      path_right_bottom.AppendPoint(
+          CFX_PointF(fLeft + fWidth, fBottom + fWidth),
+          CFX_Path::Point::Type::kLine);
+      path_right_bottom.AppendPoint(
+          CFX_PointF(fRight - fWidth, fBottom + fWidth),
+          CFX_Path::Point::Type::kLine);
+      path_right_bottom.AppendPoint(CFX_PointF(fRight - fWidth, fTop - fWidth),
+                                    CFX_Path::Point::Type::kLine);
+      path_right_bottom.AppendPoint(
+          CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth),
+          CFX_Path::Point::Type::kLine);
+      DrawPath(path_right_bottom, pUser2Device, &gsd,
+               crRightBottom.ToFXColor(nTransparency), 0,
+               CFX_FillRenderOptions::EvenOddOptions());
 
-        DrawPath(&pathLT, pUser2Device, &gsd,
-                 crLeftTop.ToFXColor(nTransparency), 0, FXFILL_ALTERNATE);
+      CFX_Path path;
+      path.AppendRect(fLeft, fBottom, fRight, fTop);
+      path.AppendRect(fLeft + fHalfWidth, fBottom + fHalfWidth,
+                      fRight - fHalfWidth, fTop - fHalfWidth);
+      DrawPath(path, pUser2Device, &gsd, color.ToFXColor(nTransparency), 0,
+               CFX_FillRenderOptions::EvenOddOptions());
+      break;
+    }
+    case BorderStyle::kUnderline: {
+      CFX_GraphStateData gsd;
+      gsd.m_LineWidth = fWidth;
 
-        CFX_PathData pathRB;
-        pathRB.AppendPoint(CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth),
-                           FXPT_TYPE::MoveTo, false);
-        pathRB.AppendPoint(
-            CFX_PointF(fRight - fHalfWidth, fBottom + fHalfWidth),
-            FXPT_TYPE::LineTo, false);
-        pathRB.AppendPoint(CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth),
-                           FXPT_TYPE::LineTo, false);
-        pathRB.AppendPoint(
-            CFX_PointF(fLeft + fHalfWidth * 2, fBottom + fHalfWidth * 2),
-            FXPT_TYPE::LineTo, false);
-        pathRB.AppendPoint(
-            CFX_PointF(fRight - fHalfWidth * 2, fBottom + fHalfWidth * 2),
-            FXPT_TYPE::LineTo, false);
-        pathRB.AppendPoint(
-            CFX_PointF(fRight - fHalfWidth * 2, fTop - fHalfWidth * 2),
-            FXPT_TYPE::LineTo, false);
-        pathRB.AppendPoint(CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth),
-                           FXPT_TYPE::LineTo, false);
-
-        DrawPath(&pathRB, pUser2Device, &gsd,
-                 crRightBottom.ToFXColor(nTransparency), 0, FXFILL_ALTERNATE);
-
-        CFX_PathData path;
-
-        path.AppendRect(fLeft, fBottom, fRight, fTop);
-        path.AppendRect(fLeft + fHalfWidth, fBottom + fHalfWidth,
-                        fRight - fHalfWidth, fTop - fHalfWidth);
-
-        DrawPath(&path, pUser2Device, &gsd, color.ToFXColor(nTransparency), 0,
-                 FXFILL_ALTERNATE);
-        break;
-      }
-      case BorderStyle::UNDERLINE: {
-        CFX_PathData path;
-        path.AppendPoint(CFX_PointF(fLeft, fBottom + fWidth / 2),
-                         FXPT_TYPE::MoveTo, false);
-        path.AppendPoint(CFX_PointF(fRight, fBottom + fWidth / 2),
-                         FXPT_TYPE::LineTo, false);
-
-        CFX_GraphStateData gsd;
-        gsd.m_LineWidth = fWidth;
-
-        DrawPath(&path, pUser2Device, &gsd, 0, color.ToFXColor(nTransparency),
-                 FXFILL_ALTERNATE);
-        break;
-      }
+      CFX_Path path;
+      path.AppendPoint(CFX_PointF(fLeft, fBottom + fHalfWidth),
+                       CFX_Path::Point::Type::kMove);
+      path.AppendPoint(CFX_PointF(fRight, fBottom + fHalfWidth),
+                       CFX_Path::Point::Type::kLine);
+      DrawPath(path, pUser2Device, &gsd, 0, color.ToFXColor(nTransparency),
+               CFX_FillRenderOptions::EvenOddOptions());
+      break;
     }
   }
 }
 
+bool CFX_RenderDevice::MultiplyAlpha(float alpha) {
+  return m_pDeviceDriver->MultiplyAlpha(alpha);
+}
+
+bool CFX_RenderDevice::MultiplyAlpha(const RetainPtr<CFX_DIBBase>& mask) {
+  return m_pDeviceDriver->MultiplyAlpha(mask);
+}
+
 CFX_RenderDevice::StateRestorer::StateRestorer(CFX_RenderDevice* pDevice)
     : m_pDevice(pDevice) {
   m_pDevice->SaveState();
diff --git a/core/fxge/cfx_renderdevice.h b/core/fxge/cfx_renderdevice.h
index 755f445..8e6e380 100644
--- a/core/fxge/cfx_renderdevice.h
+++ b/core/fxge/cfx_renderdevice.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,23 +12,30 @@
 
 #include "build/build_config.h"
 #include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/cfx_path.h"
+#include "core/fxge/dib/fx_dib.h"
 #include "core/fxge/render_defines.h"
 #include "core/fxge/renderdevicedriver_iface.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_DIBBase;
 class CFX_DIBitmap;
 class CFX_Font;
 class CFX_GraphStateData;
 class CFX_ImageRenderer;
-class CFX_PathData;
 class PauseIndicatorIface;
 class TextCharPos;
 struct CFX_Color;
+struct CFX_FillRenderOptions;
+struct CFX_TextRenderOptions;
 
-enum class BorderStyle { SOLID, DASH, BEVELED, INSET, UNDERLINE };
+enum class BorderStyle { kSolid, kDash, kBeveled, kInset, kUnderline };
 
+// Base class for all render devices. Derived classes must call
+// SetDeviceDriver() to fully initialize the class. Until then, class methods
+// are not safe to call, or may return invalid results.
 class CFX_RenderDevice {
  public:
   class StateRestorer {
@@ -40,7 +47,6 @@
     UnownedPtr<CFX_RenderDevice> m_pDevice;
   };
 
-  CFX_RenderDevice();
   virtual ~CFX_RenderDevice();
 
   static CFX_Matrix GetFlipMatrix(float width,
@@ -48,11 +54,6 @@
                                   float left,
                                   float top);
 
-  void SetDeviceDriver(std::unique_ptr<RenderDeviceDriverIface> pDriver);
-  RenderDeviceDriverIface* GetDeviceDriver() const {
-    return m_pDeviceDriver.get();
-  }
-
   void SaveState();
   void RestoreState(bool bKeepSaved);
 
@@ -68,28 +69,25 @@
                               int height) const;
   const FX_RECT& GetClipBox() const { return m_ClipBox; }
   void SetBaseClip(const FX_RECT& rect);
-  bool SetClip_PathFill(const CFX_PathData* pPathData,
+  bool SetClip_PathFill(const CFX_Path& path,
                         const CFX_Matrix* pObject2Device,
-                        int fill_mode);
-  bool SetClip_PathStroke(const CFX_PathData* pPathData,
+                        const CFX_FillRenderOptions& fill_options);
+  bool SetClip_PathStroke(const CFX_Path& path,
                           const CFX_Matrix* pObject2Device,
                           const CFX_GraphStateData* pGraphState);
   bool SetClip_Rect(const FX_RECT& pRect);
-  bool DrawPath(const CFX_PathData* pPathData,
+  bool DrawPath(const CFX_Path& path,
                 const CFX_Matrix* pObject2Device,
                 const CFX_GraphStateData* pGraphState,
                 uint32_t fill_color,
                 uint32_t stroke_color,
-                int fill_mode) {
-    return DrawPathWithBlend(pPathData, pObject2Device, pGraphState, fill_color,
-                             stroke_color, fill_mode, BlendMode::kNormal);
-  }
-  bool DrawPathWithBlend(const CFX_PathData* pPathData,
+                const CFX_FillRenderOptions& fill_options);
+  bool DrawPathWithBlend(const CFX_Path& path,
                          const CFX_Matrix* pObject2Device,
                          const CFX_GraphStateData* pGraphState,
                          uint32_t fill_color,
                          uint32_t stroke_color,
-                         int fill_mode,
+                         const CFX_FillRenderOptions& fill_options,
                          BlendMode blend_type);
   bool FillRect(const FX_RECT& rect, uint32_t color) {
     return FillRectWithBlend(rect, color, BlendMode::kNormal);
@@ -155,15 +153,13 @@
                             BlendMode blend_mode);
   bool ContinueDIBits(CFX_ImageRenderer* handle, PauseIndicatorIface* pPause);
 
-  bool DrawNormalText(int nChars,
-                      const TextCharPos* pCharPos,
+  bool DrawNormalText(pdfium::span<const TextCharPos> pCharPos,
                       CFX_Font* pFont,
                       float font_size,
                       const CFX_Matrix& mtText2Device,
                       uint32_t fill_color,
-                      uint32_t text_flags);
-  bool DrawTextPath(int nChars,
-                    const TextCharPos* pCharPos,
+                      const CFX_TextRenderOptions& options);
+  bool DrawTextPath(pdfium::span<const TextCharPos> pCharPos,
                     CFX_Font* pFont,
                     float font_size,
                     const CFX_Matrix& mtText2User,
@@ -171,8 +167,8 @@
                     const CFX_GraphStateData* pGraphState,
                     uint32_t fill_color,
                     uint32_t stroke_color,
-                    CFX_PathData* pClippingPath,
-                    int nFlag);
+                    CFX_Path* pClippingPath,
+                    const CFX_FillRenderOptions& fill_options);
 
   void DrawFillRect(const CFX_Matrix* pUser2Device,
                     const CFX_FloatRect& rect,
@@ -202,40 +198,60 @@
                     const std::vector<CFX_PointF>& points,
                     const FX_COLORREF& color);
   void DrawShadow(const CFX_Matrix& mtUser2Device,
-                  bool bVertical,
-                  bool bHorizontal,
                   const CFX_FloatRect& rect,
                   int32_t nTransparency,
                   int32_t nStartGray,
                   int32_t nEndGray);
+  bool DrawShading(const CPDF_ShadingPattern* pPattern,
+                   const CFX_Matrix* pMatrix,
+                   const FX_RECT& clip_rect,
+                   int alpha,
+                   bool bAlphaMode);
 
-#ifdef _SKIA_SUPPORT_
-  virtual void DebugVerifyBitmapIsPreMultiplied() const;
-  virtual bool SetBitsWithMask(const RetainPtr<CFX_DIBBase>& pBitmap,
-                               const RetainPtr<CFX_DIBBase>& pMask,
-                               int left,
-                               int top,
-                               int bitmap_alpha,
-                               BlendMode blend_type);
+  // Multiplies the device by a constant alpha, returning `true` on success.
+  bool MultiplyAlpha(float alpha);
+
+  // Multiplies the device by an alpha mask, returning `true` on success.
+  bool MultiplyAlpha(const RetainPtr<CFX_DIBBase>& mask);
+
+#if defined(_SKIA_SUPPORT_)
+  bool SetBitsWithMask(const RetainPtr<CFX_DIBBase>& pBitmap,
+                       const RetainPtr<CFX_DIBBase>& pMask,
+                       int left,
+                       int top,
+                       int bitmap_alpha,
+                       BlendMode blend_type);
 #endif
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
-  void Flush(bool release);
-#endif
+
+ protected:
+  CFX_RenderDevice();
+
+  void SetDeviceDriver(std::unique_ptr<RenderDeviceDriverIface> pDriver);
+  RenderDeviceDriverIface* GetDeviceDriver() const {
+    return m_pDeviceDriver.get();
+  }
 
  private:
   void InitDeviceInfo();
   void UpdateClipBox();
-  bool DrawFillStrokePath(const CFX_PathData* pPathData,
+  bool DrawFillStrokePath(const CFX_Path& path,
                           const CFX_Matrix* pObject2Device,
                           const CFX_GraphStateData* pGraphState,
                           uint32_t fill_color,
                           uint32_t stroke_color,
-                          int fill_mode,
+                          const CFX_FillRenderOptions& fill_options,
                           BlendMode blend_type);
   bool DrawCosmeticLine(const CFX_PointF& ptMoveTo,
                         const CFX_PointF& ptLineTo,
                         uint32_t color,
-                        int fill_mode,
+                        const CFX_FillRenderOptions& fill_options,
+                        BlendMode blend_type);
+  void DrawZeroAreaPath(const std::vector<CFX_Path::Point>& path,
+                        const CFX_Matrix* matrix,
+                        bool adjust,
+                        bool aliased_path,
+                        uint32_t fill_color,
+                        uint8_t fill_alpha,
                         BlendMode blend_type);
   bool FillRectWithBlend(const FX_RECT& rect,
                          uint32_t color,
@@ -246,7 +262,7 @@
   int m_Height = 0;
   int m_bpp = 0;
   int m_RenderCaps = 0;
-  DeviceType m_DeviceType = DeviceType::kUnknown;
+  DeviceType m_DeviceType = DeviceType::kDisplay;
   FX_RECT m_ClipBox;
   std::unique_ptr<RenderDeviceDriverIface> m_pDeviceDriver;
 };
diff --git a/core/fxge/cfx_substfont.cpp b/core/fxge/cfx_substfont.cpp
index b9936df..9fb243f 100644
--- a/core/fxge/cfx_substfont.cpp
+++ b/core/fxge/cfx_substfont.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,3 +9,20 @@
 CFX_SubstFont::CFX_SubstFont() = default;
 
 CFX_SubstFont::~CFX_SubstFont() = default;
+
+#if defined(_SKIA_SUPPORT_)
+int CFX_SubstFont::GetOriginalWeight() const {
+  int weight = m_Weight;
+
+  // Perform the inverse weight adjustment of UseChromeSerif() to get the
+  // original font weight.
+  if (m_Family == "Chrome Serif")
+    weight = weight * 5 / 4;
+  return weight;
+}
+#endif
+
+void CFX_SubstFont::UseChromeSerif() {
+  m_Weight = m_Weight * 4 / 5;
+  m_Family = "Chrome Serif";
+}
diff --git a/core/fxge/cfx_substfont.h b/core/fxge/cfx_substfont.h
index c9ffa40..c989e37 100644
--- a/core/fxge/cfx_substfont.h
+++ b/core/fxge/cfx_substfont.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,21 +7,32 @@
 #ifndef CORE_FXGE_CFX_SUBSTFONT_H_
 #define CORE_FXGE_CFX_SUBSTFONT_H_
 
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_codepage.h"
-#include "core/fxcrt/fx_string.h"
 
 class CFX_SubstFont {
  public:
   CFX_SubstFont();
   ~CFX_SubstFont();
 
+#if defined(_SKIA_SUPPORT_)
+  int GetOriginalWeight() const;
+#endif
+  void UseChromeSerif();
+
+  void SetIsBuiltInGenericFont() { m_bFlagMM = true; }
+  bool IsBuiltInGenericFont() const { return m_bFlagMM; }
+
   ByteString m_Family;
-  int m_Charset = FX_CHARSET_ANSI;
+  FX_Charset m_Charset = FX_Charset::kANSI;
   int m_Weight = 0;
   int m_ItalicAngle = 0;
   int m_WeightCJK = 0;
+
   bool m_bSubstCJK = false;
   bool m_bItalicCJK = false;
+
+ private:
   bool m_bFlagMM = false;
 };
 
diff --git a/core/fxge/cfx_textrenderoptions.h b/core/fxge/cfx_textrenderoptions.h
new file mode 100644
index 0000000..7e7c482
--- /dev/null
+++ b/core/fxge/cfx_textrenderoptions.h
@@ -0,0 +1,57 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXGE_CFX_TEXTRENDEROPTIONS_H_
+#define CORE_FXGE_CFX_TEXTRENDEROPTIONS_H_
+
+struct CFX_TextRenderOptions {
+  // AliasingType defines the options for drawing pixels on the edges of the
+  // text. The values are defined in an incrementing order due to the latter
+  // aliasing type's dependency on the previous one.
+  enum AliasingType {
+    // No transparent pixels on glyph edges.
+    kAliasing,
+
+    // May have transparent pixels on glyph edges.
+    kAntiAliasing,
+
+    // LCD optimization, can be enabled when anti-aliasing is allowed.
+    kLcd,
+  };
+
+  constexpr CFX_TextRenderOptions() = default;
+  constexpr explicit CFX_TextRenderOptions(AliasingType type)
+      : aliasing_type(type) {}
+  constexpr CFX_TextRenderOptions(const CFX_TextRenderOptions& other) = default;
+  CFX_TextRenderOptions& operator=(const CFX_TextRenderOptions& other) =
+      default;
+
+  // Indicates whether anti-aliasing is enabled.
+  bool IsSmooth() const {
+    return aliasing_type == kAntiAliasing || aliasing_type == kLcd;
+  }
+
+  // Aliasing option for fonts.
+  AliasingType aliasing_type = kAntiAliasing;
+
+  // Font is CID font.
+  bool font_is_cid = false;
+
+  // Using the native text output available on some platforms.
+  bool native_text = true;
+};
+
+inline bool operator==(const CFX_TextRenderOptions& lhs,
+                       const CFX_TextRenderOptions& rhs) {
+  return lhs.aliasing_type == rhs.aliasing_type &&
+         lhs.font_is_cid == rhs.font_is_cid &&
+         lhs.native_text == rhs.native_text;
+}
+
+inline bool operator!=(const CFX_TextRenderOptions& lhs,
+                       const CFX_TextRenderOptions& rhs) {
+  return !(lhs == rhs);
+}
+
+#endif  // CORE_FXGE_CFX_TEXTRENDEROPTIONS_H_
diff --git a/core/fxge/cfx_unicodeencoding.cpp b/core/fxge/cfx_unicodeencoding.cpp
index 6673fca..b8f818a 100644
--- a/core/fxge/cfx_unicodeencoding.cpp
+++ b/core/fxge/cfx_unicodeencoding.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,12 +9,13 @@
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_substfont.h"
+#include "core/fxge/freetype/fx_freetype.h"
 #include "core/fxge/fx_font.h"
-#include "core/fxge/fx_freetype.h"
 
-CFX_UnicodeEncoding::CFX_UnicodeEncoding(CFX_Font* pFont) : m_pFont(pFont) {}
+CFX_UnicodeEncoding::CFX_UnicodeEncoding(const CFX_Font* pFont)
+    : m_pFont(pFont) {}
 
-CFX_UnicodeEncoding::~CFX_UnicodeEncoding() {}
+CFX_UnicodeEncoding::~CFX_UnicodeEncoding() = default;
 
 uint32_t CFX_UnicodeEncoding::GlyphFromCharCode(uint32_t charcode) {
   FXFT_FaceRec* face = m_pFont->GetFaceRec();
@@ -25,7 +26,7 @@
     return FT_Get_Char_Index(face, charcode);
 
   if (m_pFont->GetSubstFont() &&
-      m_pFont->GetSubstFont()->m_Charset == FX_CHARSET_Symbol) {
+      m_pFont->GetSubstFont()->m_Charset == FX_Charset::kSymbol) {
     uint32_t index = 0;
     if (FT_Select_Charmap(face, FT_ENCODING_MS_SYMBOL) == 0)
       index = FT_Get_Char_Index(face, charcode);
diff --git a/core/fxge/cfx_unicodeencoding.h b/core/fxge/cfx_unicodeencoding.h
index a730dc9..6ba80a7 100644
--- a/core/fxge/cfx_unicodeencoding.h
+++ b/core/fxge/cfx_unicodeencoding.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,13 +15,13 @@
 
 class CFX_UnicodeEncoding {
  public:
-  explicit CFX_UnicodeEncoding(CFX_Font* pFont);
+  explicit CFX_UnicodeEncoding(const CFX_Font* pFont);
   virtual ~CFX_UnicodeEncoding();
 
   virtual uint32_t GlyphFromCharCode(uint32_t charcode);
 
  protected:
-  UnownedPtr<CFX_Font> const m_pFont;
+  UnownedPtr<const CFX_Font> const m_pFont;
 };
 
 #endif  // CORE_FXGE_CFX_UNICODEENCODING_H_
diff --git a/core/fxge/cfx_unicodeencodingex.cpp b/core/fxge/cfx_unicodeencodingex.cpp
index a2fa24a..683e2d0 100644
--- a/core/fxge/cfx_unicodeencodingex.cpp
+++ b/core/fxge/cfx_unicodeencodingex.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,37 +9,35 @@
 #include <memory>
 
 #include "core/fxge/cfx_font.h"
+#include "core/fxge/freetype/fx_freetype.h"
 #include "core/fxge/fx_font.h"
-#include "core/fxge/fx_freetype.h"
-#include "third_party/base/ptr_util.h"
 
-#define FXFM_ENC_TAG(a, b, c, d)                                          \
+#define ENC_TAG(a, b, c, d)                                               \
   (((uint32_t)(a) << 24) | ((uint32_t)(b) << 16) | ((uint32_t)(c) << 8) | \
    (uint32_t)(d))
-#define FXFM_ENCODING_MS_SYMBOL FXFM_ENC_TAG('s', 'y', 'm', 'b')
-#define FXFM_ENCODING_UNICODE FXFM_ENC_TAG('u', 'n', 'i', 'c')
-#define FXFM_ENCODING_MS_SJIS FXFM_ENC_TAG('s', 'j', 'i', 's')
-#define FXFM_ENCODING_MS_GB2312 FXFM_ENC_TAG('g', 'b', ' ', ' ')
-#define FXFM_ENCODING_MS_BIG5 FXFM_ENC_TAG('b', 'i', 'g', '5')
-#define FXFM_ENCODING_MS_WANSUNG FXFM_ENC_TAG('w', 'a', 'n', 's')
-#define FXFM_ENCODING_MS_JOHAB FXFM_ENC_TAG('j', 'o', 'h', 'a')
-#define FXFM_ENCODING_ADOBE_STANDARD FXFM_ENC_TAG('A', 'D', 'O', 'B')
-#define FXFM_ENCODING_ADOBE_EXPERT FXFM_ENC_TAG('A', 'D', 'B', 'E')
-#define FXFM_ENCODING_ADOBE_CUSTOM FXFM_ENC_TAG('A', 'D', 'B', 'C')
-#define FXFM_ENCODING_ADOBE_LATIN_1 FXFM_ENC_TAG('l', 'a', 't', '1')
-#define FXFM_ENCODING_OLD_LATIN_2 FXFM_ENC_TAG('l', 'a', 't', '2')
-#define FXFM_ENCODING_APPLE_ROMAN FXFM_ENC_TAG('a', 'r', 'm', 'n')
 
 namespace {
 
-const uint32_t g_EncodingID[] = {
-    FXFM_ENCODING_MS_SYMBOL,     FXFM_ENCODING_UNICODE,
-    FXFM_ENCODING_MS_SJIS,       FXFM_ENCODING_MS_GB2312,
-    FXFM_ENCODING_MS_BIG5,       FXFM_ENCODING_MS_WANSUNG,
-    FXFM_ENCODING_MS_JOHAB,      FXFM_ENCODING_ADOBE_STANDARD,
-    FXFM_ENCODING_ADOBE_EXPERT,  FXFM_ENCODING_ADOBE_CUSTOM,
-    FXFM_ENCODING_ADOBE_LATIN_1, FXFM_ENCODING_OLD_LATIN_2,
-    FXFM_ENCODING_APPLE_ROMAN,
+constexpr uint32_t kEncodingExSymbol = ENC_TAG('s', 'y', 'm', 'b');
+constexpr uint32_t kEncodingExUnicode = ENC_TAG('u', 'n', 'i', 'c');
+constexpr uint32_t kEncodingExSjis = ENC_TAG('s', 'j', 'i', 's');
+constexpr uint32_t kEncodingExGB2312 = ENC_TAG('g', 'b', ' ', ' ');
+constexpr uint32_t kEncodingExBig5 = ENC_TAG('b', 'i', 'g', '5');
+constexpr uint32_t kEncodingExWansung = ENC_TAG('w', 'a', 'n', 's');
+constexpr uint32_t kEncodingExJohab = ENC_TAG('j', 'o', 'h', 'a');
+constexpr uint32_t kEncodingExAdobeStandard = ENC_TAG('A', 'D', 'O', 'B');
+constexpr uint32_t kEncodingExAdobeExpert = ENC_TAG('A', 'D', 'B', 'E');
+constexpr uint32_t kEncodingExAdobeCustom = ENC_TAG('A', 'D', 'B', 'C');
+constexpr uint32_t kEncodingExLatin1 = ENC_TAG('l', 'a', 't', '1');
+constexpr uint32_t kEncodingExOldLatin2 = ENC_TAG('l', 'a', 't', '2');
+constexpr uint32_t kEncodingExAppleRoman = ENC_TAG('a', 'r', 'm', 'n');
+
+constexpr uint32_t kEncodingID[] = {
+    kEncodingExSymbol,      kEncodingExUnicode,       kEncodingExSjis,
+    kEncodingExGB2312,      kEncodingExBig5,          kEncodingExWansung,
+    kEncodingExJohab,       kEncodingExAdobeStandard, kEncodingExAdobeExpert,
+    kEncodingExAdobeCustom, kEncodingExLatin1,        kEncodingExOldLatin2,
+    kEncodingExAppleRoman,
 };
 
 std::unique_ptr<CFX_UnicodeEncodingEx> FXFM_CreateFontEncoding(
@@ -47,7 +45,7 @@
     uint32_t nEncodingID) {
   if (FXFT_Select_Charmap(pFont->GetFaceRec(), nEncodingID))
     return nullptr;
-  return pdfium::MakeUnique<CFX_UnicodeEncodingEx>(pFont, nEncodingID);
+  return std::make_unique<CFX_UnicodeEncodingEx>(pFont, nEncodingID);
 }
 
 }  // namespace
@@ -56,18 +54,16 @@
                                              uint32_t EncodingID)
     : CFX_UnicodeEncoding(pFont), m_nEncodingID(EncodingID) {}
 
-CFX_UnicodeEncodingEx::~CFX_UnicodeEncodingEx() {}
+CFX_UnicodeEncodingEx::~CFX_UnicodeEncodingEx() = default;
 
 uint32_t CFX_UnicodeEncodingEx::GlyphFromCharCode(uint32_t charcode) {
   FXFT_FaceRec* face = m_pFont->GetFaceRec();
   FT_UInt nIndex = FT_Get_Char_Index(face, charcode);
   if (nIndex > 0)
     return nIndex;
-  int nmaps = FXFT_Get_Face_CharmapCount(face);
   int m = 0;
-  while (m < nmaps) {
-    uint32_t nEncodingID =
-        FXFT_Get_Charmap_Encoding(FXFT_Get_Face_Charmaps(face)[m++]);
+  while (m < face->num_charmaps) {
+    uint32_t nEncodingID = FXFT_Get_Charmap_Encoding(face->charmaps[m++]);
     if (m_nEncodingID == nEncodingID)
       continue;
     int error = FXFT_Select_Charmap(face, nEncodingID);
@@ -84,17 +80,14 @@
 }
 
 uint32_t CFX_UnicodeEncodingEx::CharCodeFromUnicode(wchar_t Unicode) const {
-  if (m_nEncodingID == FXFM_ENCODING_UNICODE ||
-      m_nEncodingID == FXFM_ENCODING_MS_SYMBOL) {
+  if (m_nEncodingID == kEncodingExUnicode ||
+      m_nEncodingID == kEncodingExSymbol) {
     return Unicode;
   }
   FXFT_FaceRec* face = m_pFont->GetFaceRec();
-  int nmaps = FXFT_Get_Face_CharmapCount(face);
-  for (int i = 0; i < nmaps; i++) {
-    int nEncodingID =
-        FXFT_Get_Charmap_Encoding(FXFT_Get_Face_Charmaps(face)[i]);
-    if (nEncodingID == FXFM_ENCODING_UNICODE ||
-        nEncodingID == FXFM_ENCODING_MS_SYMBOL) {
+  for (int i = 0; i < face->num_charmaps; i++) {
+    int nEncodingID = FXFT_Get_Charmap_Encoding(face->charmaps[i]);
+    if (nEncodingID == kEncodingExUnicode || nEncodingID == kEncodingExSymbol) {
       return Unicode;
     }
   }
@@ -106,7 +99,7 @@
   if (!pFont || !pFont->GetFaceRec())
     return nullptr;
 
-  for (uint32_t id : g_EncodingID) {
+  for (uint32_t id : kEncodingID) {
     auto pFontEncoding = FXFM_CreateFontEncoding(pFont, id);
     if (pFontEncoding)
       return pFontEncoding;
diff --git a/core/fxge/cfx_unicodeencodingex.h b/core/fxge/cfx_unicodeencodingex.h
index a9c1f58..da87d7e 100644
--- a/core/fxge/cfx_unicodeencodingex.h
+++ b/core/fxge/cfx_unicodeencodingex.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,9 +7,10 @@
 #ifndef CORE_FXGE_CFX_UNICODEENCODINGEX_H_
 #define CORE_FXGE_CFX_UNICODEENCODINGEX_H_
 
+#include <stdint.h>
+
 #include <memory>
 
-#include "core/fxcrt/fx_system.h"
 #include "core/fxge/cfx_unicodeencoding.h"
 
 class CFX_UnicodeEncodingEx final : public CFX_UnicodeEncoding {
diff --git a/core/fxge/cfx_windowsrenderdevice.cpp b/core/fxge/cfx_windowsrenderdevice.cpp
new file mode 100644
index 0000000..dd26019
--- /dev/null
+++ b/core/fxge/cfx_windowsrenderdevice.cpp
@@ -0,0 +1,55 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxge/cfx_windowsrenderdevice.h"
+
+#include <memory>
+
+#include "core/fxge/renderdevicedriver_iface.h"
+#include "core/fxge/win32/cgdi_display_driver.h"
+#include "core/fxge/win32/cgdi_printer_driver.h"
+#include "core/fxge/win32/cps_printer_driver.h"
+#include "core/fxge/win32/ctext_only_printer_driver.h"
+
+namespace {
+
+std::unique_ptr<RenderDeviceDriverIface> CreateDriver(
+    HDC hDC,
+    CFX_PSFontTracker* ps_font_tracker,
+    const EncoderIface* encoder_iface) {
+  int device_type = ::GetDeviceCaps(hDC, TECHNOLOGY);
+  int obj_type = ::GetObjectType(hDC);
+  bool use_printer = device_type == DT_RASPRINTER ||
+                     device_type == DT_PLOTTER ||
+                     device_type == DT_CHARSTREAM || obj_type == OBJ_ENHMETADC;
+
+  if (!use_printer)
+    return std::make_unique<CGdiDisplayDriver>(hDC);
+
+  if (g_pdfium_print_mode == WindowsPrintMode::kEmf ||
+      g_pdfium_print_mode == WindowsPrintMode::kEmfImageMasks) {
+    return std::make_unique<CGdiPrinterDriver>(hDC);
+  }
+
+  if (g_pdfium_print_mode == WindowsPrintMode::kTextOnly)
+    return std::make_unique<CTextOnlyPrinterDriver>(hDC);
+
+  return std::make_unique<CPSPrinterDriver>(hDC, g_pdfium_print_mode,
+                                            ps_font_tracker, encoder_iface);
+}
+
+}  // namespace
+
+WindowsPrintMode g_pdfium_print_mode = WindowsPrintMode::kEmf;
+
+CFX_WindowsRenderDevice::CFX_WindowsRenderDevice(
+    HDC hDC,
+    CFX_PSFontTracker* ps_font_tracker,
+    const EncoderIface* encoder_iface) {
+  SetDeviceDriver(CreateDriver(hDC, ps_font_tracker, encoder_iface));
+}
+
+CFX_WindowsRenderDevice::~CFX_WindowsRenderDevice() = default;
diff --git a/core/fxge/cfx_windowsrenderdevice.h b/core/fxge/cfx_windowsrenderdevice.h
index 3d96207..7684fe3 100644
--- a/core/fxge/cfx_windowsrenderdevice.h
+++ b/core/fxge/cfx_windowsrenderdevice.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,38 +11,29 @@
 
 #include "core/fxge/cfx_renderdevice.h"
 
-enum WindowsPrintMode {
-  kModeEmf = 0,
-  kModeTextOnly = 1,
-  kModePostScript2 = 2,
-  kModePostScript3 = 3,
-  kModePostScript2PassThrough = 4,
-  kModePostScript3PassThrough = 5,
+enum class WindowsPrintMode {
+  kEmf = 0,
+  kTextOnly = 1,
+  kPostScript2 = 2,
+  kPostScript3 = 3,
+  kPostScript2PassThrough = 4,
+  kPostScript3PassThrough = 5,
+  kEmfImageMasks = 6,
+  kPostScript3Type42 = 7,
+  kPostScript3Type42PassThrough = 8,
 };
 
-class RenderDeviceDriverIface;
+class CFX_PSFontTracker;
 struct EncoderIface;
 
-#if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
-typedef void (*PDFiumEnsureTypefaceCharactersAccessible)(const LOGFONT* font,
-                                                         const wchar_t* text,
-                                                         size_t text_length);
-
-extern bool g_pdfium_print_text_with_gdi;
-extern PDFiumEnsureTypefaceCharactersAccessible
-    g_pdfium_typeface_accessible_func;
-#endif
 extern WindowsPrintMode g_pdfium_print_mode;
 
 class CFX_WindowsRenderDevice : public CFX_RenderDevice {
  public:
-  CFX_WindowsRenderDevice(HDC hDC, const EncoderIface* pEncoderIface);
+  CFX_WindowsRenderDevice(HDC hDC,
+                          CFX_PSFontTracker* ps_font_tracker,
+                          const EncoderIface* encoder_iface);
   ~CFX_WindowsRenderDevice() override;
-
- private:
-  static RenderDeviceDriverIface* CreateDriver(
-      HDC hDC,
-      const EncoderIface* pEncoderIface);
 };
 
 #endif  // CORE_FXGE_CFX_WINDOWSRENDERDEVICE_H_
diff --git a/core/fxge/cfx_windowsrenderdevice_embeddertest.cpp b/core/fxge/cfx_windowsrenderdevice_embeddertest.cpp
new file mode 100644
index 0000000..b637b46
--- /dev/null
+++ b/core/fxge/cfx_windowsrenderdevice_embeddertest.cpp
@@ -0,0 +1,98 @@
+// Copyright 2019 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/cfx_windowsrenderdevice.h"
+
+#include <windows.h>
+
+#include <memory>
+
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "core/fxge/cfx_path.h"
+#include "core/fxge/win32/cfx_psfonttracker.h"
+#include "testing/embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+constexpr CFX_Matrix kIdentityMatrix;
+
+}  // namespace
+
+class CFX_WindowsRenderDeviceTest : public EmbedderTest {
+ public:
+  void SetUp() override {
+    EmbedderTest::SetUp();
+
+    // Get a device context with Windows GDI.
+    m_hDC = CreateCompatibleDC(nullptr);
+    ASSERT_TRUE(m_hDC);
+    m_driver = std::make_unique<CFX_WindowsRenderDevice>(
+        m_hDC, &m_PSFontTracker, /*encoder_iface=*/nullptr);
+    m_driver->SaveState();
+  }
+
+  void TearDown() override {
+    m_driver->RestoreState(false);
+    m_driver.reset();
+    DeleteDC(m_hDC);
+    EmbedderTest::TearDown();
+  }
+
+ protected:
+  HDC m_hDC;
+  CFX_PSFontTracker m_PSFontTracker;
+  std::unique_ptr<CFX_WindowsRenderDevice> m_driver;
+};
+
+TEST_F(CFX_WindowsRenderDeviceTest, SimpleClipTriangle) {
+  CFX_Path path_data;
+  CFX_PointF p1(0.0f, 0.0f);
+  CFX_PointF p2(0.0f, 100.0f);
+  CFX_PointF p3(100.0f, 100.0f);
+
+  path_data.AppendLine(p1, p2);
+  path_data.AppendLine(p2, p3);
+  path_data.AppendLine(p3, p1);
+  path_data.ClosePath();
+  EXPECT_TRUE(m_driver->SetClip_PathFill(
+      path_data, &kIdentityMatrix, CFX_FillRenderOptions::WindingOptions()));
+}
+
+TEST_F(CFX_WindowsRenderDeviceTest, SimpleClipRect) {
+  CFX_Path path_data;
+
+  path_data.AppendRect(0.0f, 100.0f, 200.0f, 0.0f);
+  path_data.ClosePath();
+  EXPECT_TRUE(m_driver->SetClip_PathFill(
+      path_data, &kIdentityMatrix, CFX_FillRenderOptions::WindingOptions()));
+}
+
+TEST_F(CFX_WindowsRenderDeviceTest, GargantuanClipRect) {
+  CFX_Path path_data;
+
+  path_data.AppendRect(-257698020.0f, -257697252.0f, 257698044.0f,
+                       257698812.0f);
+  path_data.ClosePath();
+  // These coordinates for a clip path are valid, just very large. Using these
+  // for a clip path should allow IntersectClipRect() to return success;
+  // however they do not because the GDI API IntersectClipRect() errors out and
+  // affect subsequent imaging.  crbug.com/1019026
+  EXPECT_FALSE(m_driver->SetClip_PathFill(
+      path_data, &kIdentityMatrix, CFX_FillRenderOptions::WindingOptions()));
+}
+
+TEST_F(CFX_WindowsRenderDeviceTest, GargantuanClipRectWithBaseClip) {
+  CFX_Path path_data;
+  const FX_RECT kBaseClip(0, 0, 5100, 6600);
+
+  m_driver->SetBaseClip(kBaseClip);
+  path_data.AppendRect(-257698020.0f, -257697252.0f, 257698044.0f,
+                       257698812.0f);
+  path_data.ClosePath();
+  // Use of a reasonable base clip ensures that we avoid getting an error back
+  // from GDI API IntersectClipRect().
+  EXPECT_TRUE(m_driver->SetClip_PathFill(
+      path_data, &kIdentityMatrix, CFX_FillRenderOptions::WindingOptions()));
+}
diff --git a/core/fxge/dib/cfx_bitmapcomposer.cpp b/core/fxge/dib/cfx_bitmapcomposer.cpp
index beb02b7..e924034 100644
--- a/core/fxge/dib/cfx_bitmapcomposer.cpp
+++ b/core/fxge/dib/cfx_bitmapcomposer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,15 @@
 
 #include "core/fxge/dib/cfx_bitmapcomposer.h"
 
+#include <string.h>
+
+#include "core/fxcrt/fx_2d_size.h"
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/span_util.h"
 #include "core/fxge/cfx_cliprgn.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
+#include "third_party/base/check_op.h"
 
 CFX_BitmapComposer::CFX_BitmapComposer() = default;
 
@@ -22,7 +29,7 @@
                                  bool bFlipX,
                                  bool bFlipY,
                                  bool bRgbByteOrder,
-                                 BlendMode blend_type) {
+                                 BlendMode blend_mode) {
   m_pBitmap = pDest;
   m_pClipRgn = pClipRgn;
   m_DestLeft = dest_rect.left;
@@ -32,22 +39,24 @@
   m_BitmapAlpha = bitmap_alpha;
   m_MaskColor = mask_color;
   m_pClipMask = nullptr;
-  if (pClipRgn && pClipRgn->GetType() != CFX_ClipRgn::RectI)
+  if (pClipRgn && pClipRgn->GetType() != CFX_ClipRgn::kRectI)
     m_pClipMask = pClipRgn->GetMask();
   m_bVertical = bVertical;
   m_bFlipX = bFlipX;
   m_bFlipY = bFlipY;
   m_bRgbByteOrder = bRgbByteOrder;
-  m_BlendType = blend_type;
+  m_BlendMode = blend_mode;
 }
 
 bool CFX_BitmapComposer::SetInfo(int width,
                                  int height,
                                  FXDIB_Format src_format,
-                                 uint32_t* pSrcPalette) {
+                                 pdfium::span<const uint32_t> src_palette) {
+  DCHECK_NE(src_format, FXDIB_Format::k1bppMask);
+  DCHECK_NE(src_format, FXDIB_Format::k1bppRgb);
   m_SrcFormat = src_format;
-  if (!m_Compositor.Init(m_pBitmap->GetFormat(), src_format, width, pSrcPalette,
-                         m_MaskColor, BlendMode::kNormal,
+  if (!m_Compositor.Init(m_pBitmap->GetFormat(), src_format, src_palette,
+                         m_MaskColor, m_BlendMode,
                          m_pClipMask != nullptr || (m_BitmapAlpha < 255),
                          m_bRgbByteOrder)) {
     return false;
@@ -55,8 +64,6 @@
   if (m_bVertical) {
     m_pScanlineV.resize(m_pBitmap->GetBPP() / 8 * width + 4);
     m_pClipScanV.resize(m_pBitmap->GetHeight());
-    if (m_pBitmap->m_pAlphaMask)
-      m_pScanlineAlphaV.resize(width + 4);
   }
   if (m_BitmapAlpha < 255) {
     m_pAddClipScan.resize(m_bVertical ? m_pBitmap->GetHeight()
@@ -65,85 +72,78 @@
   return true;
 }
 
-void CFX_BitmapComposer::DoCompose(uint8_t* dest_scan,
-                                   const uint8_t* src_scan,
+void CFX_BitmapComposer::DoCompose(pdfium::span<uint8_t> dest_scan,
+                                   pdfium::span<const uint8_t> src_scan,
                                    int dest_width,
-                                   const uint8_t* clip_scan,
-                                   const uint8_t* src_extra_alpha,
-                                   uint8_t* dst_extra_alpha) {
-  uint8_t* pAddClipScan = m_pAddClipScan.data();
+                                   pdfium::span<const uint8_t> clip_scan) {
   if (m_BitmapAlpha < 255) {
-    if (clip_scan) {
+    if (!clip_scan.empty()) {
       for (int i = 0; i < dest_width; ++i)
-        pAddClipScan[i] = clip_scan[i] * m_BitmapAlpha / 255;
+        m_pAddClipScan[i] = clip_scan[i] * m_BitmapAlpha / 255;
     } else {
-      memset(pAddClipScan, m_BitmapAlpha, dest_width);
+      fxcrt::spanset(pdfium::make_span(m_pAddClipScan).first(dest_width),
+                     m_BitmapAlpha);
     }
-    clip_scan = pAddClipScan;
+    clip_scan = m_pAddClipScan;
   }
-  if (m_SrcFormat == FXDIB_8bppMask) {
+  if (m_SrcFormat == FXDIB_Format::k8bppMask) {
     m_Compositor.CompositeByteMaskLine(dest_scan, src_scan, dest_width,
-                                       clip_scan, dst_extra_alpha);
-  } else if (GetBppFromFormat(m_SrcFormat) == 8) {
+                                       clip_scan);
+  } else if (m_SrcFormat == FXDIB_Format::k8bppRgb) {
     m_Compositor.CompositePalBitmapLine(dest_scan, src_scan, 0, dest_width,
-                                        clip_scan, src_extra_alpha,
-                                        dst_extra_alpha);
+                                        clip_scan);
   } else {
     m_Compositor.CompositeRgbBitmapLine(dest_scan, src_scan, dest_width,
-                                        clip_scan, src_extra_alpha,
-                                        dst_extra_alpha);
+                                        clip_scan);
   }
 }
 
 void CFX_BitmapComposer::ComposeScanline(int line,
-                                         const uint8_t* scanline,
-                                         const uint8_t* scan_extra_alpha) {
+                                         pdfium::span<const uint8_t> scanline) {
   if (m_bVertical) {
-    ComposeScanlineV(line, scanline, scan_extra_alpha);
+    ComposeScanlineV(line, scanline);
     return;
   }
-  const uint8_t* clip_scan = nullptr;
+  pdfium::span<const uint8_t> clip_scan;
   if (m_pClipMask) {
-    clip_scan = m_pClipMask->GetBuffer() +
-                (m_DestTop + line - m_pClipRgn->GetBox().top) *
-                    m_pClipMask->GetPitch() +
-                (m_DestLeft - m_pClipRgn->GetBox().left);
+    clip_scan =
+        m_pClipMask
+            ->GetWritableScanline(m_DestTop + line - m_pClipRgn->GetBox().top)
+            .subspan(m_DestLeft - m_pClipRgn->GetBox().left);
   }
-  uint8_t* dest_scan = m_pBitmap->GetWritableScanline(line + m_DestTop) +
-                       m_DestLeft * m_pBitmap->GetBPP() / 8;
-  uint8_t* dest_alpha_scan =
-      m_pBitmap->m_pAlphaMask
-          ? m_pBitmap->m_pAlphaMask->GetWritableScanline(line + m_DestTop) +
-                m_DestLeft
-          : nullptr;
-  DoCompose(dest_scan, scanline, m_DestWidth, clip_scan, scan_extra_alpha,
-            dest_alpha_scan);
+  pdfium::span<uint8_t> dest_scan =
+      m_pBitmap->GetWritableScanline(line + m_DestTop);
+  if (!dest_scan.empty()) {
+    FX_SAFE_UINT32 offset = m_DestLeft;
+    offset *= m_pBitmap->GetBPP();
+    offset /= 8;
+    if (!offset.IsValid())
+      return;
+
+    dest_scan = dest_scan.subspan(offset.ValueOrDie());
+  }
+  DoCompose(dest_scan, scanline, m_DestWidth, clip_scan);
 }
 
-void CFX_BitmapComposer::ComposeScanlineV(int line,
-                                          const uint8_t* scanline,
-                                          const uint8_t* scan_extra_alpha) {
+void CFX_BitmapComposer::ComposeScanlineV(
+    int line,
+    pdfium::span<const uint8_t> scanline) {
   int Bpp = m_pBitmap->GetBPP() / 8;
   int dest_pitch = m_pBitmap->GetPitch();
-  int dest_alpha_pitch =
-      m_pBitmap->m_pAlphaMask ? m_pBitmap->m_pAlphaMask->GetPitch() : 0;
   int dest_x = m_DestLeft + (m_bFlipX ? (m_DestWidth - line - 1) : line);
-  uint8_t* dest_buf =
-      m_pBitmap->GetBuffer() + dest_x * Bpp + m_DestTop * dest_pitch;
-  uint8_t* dest_alpha_buf = m_pBitmap->m_pAlphaMask
-                                ? m_pBitmap->m_pAlphaMask->GetBuffer() +
-                                      dest_x + m_DestTop * dest_alpha_pitch
-                                : nullptr;
-  if (m_bFlipY) {
-    dest_buf += dest_pitch * (m_DestHeight - 1);
-    dest_alpha_buf += dest_alpha_pitch * (m_DestHeight - 1);
+  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);
+    dest_span = dest_span.subspan(dest_y_offset).subspan(dest_x_offset);
+    if (m_bFlipY) {
+      const size_t dest_flip_offset =
+          Fx2DSizeOrDie(dest_pitch, m_DestHeight - 1);
+      dest_span = dest_span.subspan(dest_flip_offset);
+    }
   }
-  int y_step = dest_pitch;
-  int y_alpha_step = dest_alpha_pitch;
-  if (m_bFlipY) {
-    y_step = -y_step;
-    y_alpha_step = -y_alpha_step;
-  }
+  uint8_t* dest_buf = dest_span.data();
+  const int y_step = m_bFlipY ? -dest_pitch : dest_pitch;
   uint8_t* src_scan = m_pScanlineV.data();
   uint8_t* dest_scan = dest_buf;
   for (int i = 0; i < m_DestHeight; ++i) {
@@ -151,24 +151,16 @@
       *src_scan++ = dest_scan[j];
     dest_scan += y_step;
   }
-  uint8_t* src_alpha_scan = m_pScanlineAlphaV.data();
-  uint8_t* dest_alpha_scan = dest_alpha_buf;
-  if (dest_alpha_scan) {
-    for (int i = 0; i < m_DestHeight; ++i) {
-      *src_alpha_scan++ = *dest_alpha_scan;
-      dest_alpha_scan += y_alpha_step;
-    }
-  }
-  uint8_t* clip_scan = nullptr;
+  pdfium::span<uint8_t> clip_scan;
   if (m_pClipMask) {
-    clip_scan = m_pClipScanV.data();
+    clip_scan = m_pClipScanV;
     int clip_pitch = m_pClipMask->GetPitch();
     const uint8_t* src_clip =
-        m_pClipMask->GetBuffer() +
-        (m_DestTop - m_pClipRgn->GetBox().top) * clip_pitch +
-        (dest_x - m_pClipRgn->GetBox().left);
+        m_pClipMask->GetScanline(m_DestTop - m_pClipRgn->GetBox().top)
+            .subspan(dest_x - m_pClipRgn->GetBox().left)
+            .data();
     if (m_bFlipY) {
-      src_clip += clip_pitch * (m_DestHeight - 1);
+      src_clip += Fx2DSizeOrDie(clip_pitch, m_DestHeight - 1);
       clip_pitch = -clip_pitch;
     }
     for (int i = 0; i < m_DestHeight; ++i) {
@@ -176,8 +168,7 @@
       src_clip += clip_pitch;
     }
   }
-  DoCompose(m_pScanlineV.data(), scanline, m_DestHeight, clip_scan,
-            scan_extra_alpha, m_pScanlineAlphaV.data());
+  DoCompose(m_pScanlineV, scanline, m_DestHeight, clip_scan);
   src_scan = m_pScanlineV.data();
   dest_scan = dest_buf;
   for (int i = 0; i < m_DestHeight; ++i) {
@@ -185,12 +176,4 @@
       dest_scan[j] = *src_scan++;
     dest_scan += y_step;
   }
-  src_alpha_scan = m_pScanlineAlphaV.data();
-  dest_alpha_scan = dest_alpha_buf;
-  if (!dest_alpha_scan)
-    return;
-  for (int i = 0; i < m_DestHeight; ++i) {
-    *dest_alpha_scan = *src_alpha_scan++;
-    dest_alpha_scan += y_alpha_step;
-  }
 }
diff --git a/core/fxge/dib/cfx_bitmapcomposer.h b/core/fxge/dib/cfx_bitmapcomposer.h
index 74a7efa..c4cc07d 100644
--- a/core/fxge/dib/cfx_bitmapcomposer.h
+++ b/core/fxge/dib/cfx_bitmapcomposer.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,17 +7,18 @@
 #ifndef CORE_FXGE_DIB_CFX_BITMAPCOMPOSER_H_
 #define CORE_FXGE_DIB_CFX_BITMAPCOMPOSER_H_
 
-#include <vector>
+#include <stdint.h>
 
-#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/dib/cfx_scanlinecompositor.h"
+#include "core/fxge/dib/fx_dib.h"
 #include "core/fxge/dib/scanlinecomposer_iface.h"
-#include "core/fxge/fx_dib.h"
 
 class CFX_ClipRgn;
 class CFX_DIBitmap;
+struct FX_RECT;
 
 class CFX_BitmapComposer final : public ScanlineComposerIface {
  public:
@@ -33,28 +34,21 @@
                bool bFlipX,
                bool bFlipY,
                bool bRgbByteOrder,
-               BlendMode blend_type);
+               BlendMode blend_mode);
 
-  // ScanlineComposerIface
+  // ScanlineComposerIface:
   bool SetInfo(int width,
                int height,
                FXDIB_Format src_format,
-               uint32_t* pSrcPalette) override;
-
-  void ComposeScanline(int line,
-                       const uint8_t* scanline,
-                       const uint8_t* scan_extra_alpha) override;
+               pdfium::span<const uint32_t> src_palette) override;
+  void ComposeScanline(int line, pdfium::span<const uint8_t> scanline) override;
 
  private:
-  void DoCompose(uint8_t* dest_scan,
-                 const uint8_t* src_scan,
+  void DoCompose(pdfium::span<uint8_t> dest_scan,
+                 pdfium::span<const uint8_t> src_scan,
                  int dest_width,
-                 const uint8_t* clip_scan,
-                 const uint8_t* src_extra_alpha,
-                 uint8_t* dst_extra_alpha);
-  void ComposeScanlineV(int line,
-                        const uint8_t* scanline,
-                        const uint8_t* scan_extra_alpha);
+                 pdfium::span<const uint8_t> clip_scan);
+  void ComposeScanlineV(int line, pdfium::span<const uint8_t> scanline);
 
   RetainPtr<CFX_DIBitmap> m_pBitmap;
   UnownedPtr<const CFX_ClipRgn> m_pClipRgn;
@@ -71,11 +65,10 @@
   bool m_bFlipX;
   bool m_bFlipY;
   bool m_bRgbByteOrder = false;
-  BlendMode m_BlendType = BlendMode::kNormal;
-  std::vector<uint8_t> m_pScanlineV;
-  std::vector<uint8_t> m_pClipScanV;
-  std::vector<uint8_t> m_pAddClipScan;
-  std::vector<uint8_t> m_pScanlineAlphaV;
+  BlendMode m_BlendMode = BlendMode::kNormal;
+  DataVector<uint8_t> m_pScanlineV;
+  DataVector<uint8_t> m_pClipScanV;
+  DataVector<uint8_t> m_pAddClipScan;
 };
 
 #endif  // CORE_FXGE_DIB_CFX_BITMAPCOMPOSER_H_
diff --git a/core/fxge/dib/cfx_bitmapstorer.cpp b/core/fxge/dib/cfx_bitmapstorer.cpp
index 97ddf6f..607b0cb 100644
--- a/core/fxge/dib/cfx_bitmapstorer.cpp
+++ b/core/fxge/dib/cfx_bitmapstorer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,13 +6,17 @@
 
 #include "core/fxge/dib/cfx_bitmapstorer.h"
 
+#include <string.h>
+
 #include <utility>
 
+#include "core/fxcrt/span_util.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
+#include "third_party/base/check_op.h"
 
-CFX_BitmapStorer::CFX_BitmapStorer() {}
+CFX_BitmapStorer::CFX_BitmapStorer() = default;
 
-CFX_BitmapStorer::~CFX_BitmapStorer() {}
+CFX_BitmapStorer::~CFX_BitmapStorer() = default;
 
 RetainPtr<CFX_DIBitmap> CFX_BitmapStorer::Detach() {
   return std::move(m_pBitmap);
@@ -23,32 +27,24 @@
 }
 
 void CFX_BitmapStorer::ComposeScanline(int line,
-                                       const uint8_t* scanline,
-                                       const uint8_t* scan_extra_alpha) {
-  uint8_t* dest_buf = m_pBitmap->GetWritableScanline(line);
-  uint8_t* dest_alpha_buf =
-      m_pBitmap->m_pAlphaMask
-          ? m_pBitmap->m_pAlphaMask->GetWritableScanline(line)
-          : nullptr;
-  if (dest_buf)
-    memcpy(dest_buf, scanline, m_pBitmap->GetPitch());
-
-  if (dest_alpha_buf) {
-    memcpy(dest_alpha_buf, scan_extra_alpha,
-           m_pBitmap->m_pAlphaMask->GetPitch());
-  }
+                                       pdfium::span<const uint8_t> scanline) {
+  pdfium::span<uint8_t> dest_buf = m_pBitmap->GetWritableScanline(line);
+  if (!dest_buf.empty())
+    fxcrt::spancpy(dest_buf, scanline);
 }
 
 bool CFX_BitmapStorer::SetInfo(int width,
                                int height,
                                FXDIB_Format src_format,
-                               uint32_t* pSrcPalette) {
+                               pdfium::span<const uint32_t> src_palette) {
+  DCHECK_NE(src_format, FXDIB_Format::k1bppMask);
+  DCHECK_NE(src_format, FXDIB_Format::k1bppRgb);
   auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
   if (!pBitmap->Create(width, height, src_format))
     return false;
 
-  if (pSrcPalette)
-    pBitmap->SetPalette(pSrcPalette);
+  if (!src_palette.empty())
+    pBitmap->SetPalette(src_palette);
 
   m_pBitmap = std::move(pBitmap);
   return true;
diff --git a/core/fxge/dib/cfx_bitmapstorer.h b/core/fxge/dib/cfx_bitmapstorer.h
index c8158a3..7a5e3ff 100644
--- a/core/fxge/dib/cfx_bitmapstorer.h
+++ b/core/fxge/dib/cfx_bitmapstorer.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,9 +7,9 @@
 #ifndef CORE_FXGE_DIB_CFX_BITMAPSTORER_H_
 #define CORE_FXGE_DIB_CFX_BITMAPSTORER_H_
 
-#include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/dib/scanlinecomposer_iface.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_DIBitmap;
 
@@ -18,14 +18,12 @@
   CFX_BitmapStorer();
   ~CFX_BitmapStorer() override;
 
-  // ScanlineComposerIface
-  void ComposeScanline(int line,
-                       const uint8_t* scanline,
-                       const uint8_t* scan_extra_alpha) override;
+  // ScanlineComposerIface:
+  void ComposeScanline(int line, pdfium::span<const uint8_t> scanline) override;
   bool SetInfo(int width,
                int height,
                FXDIB_Format src_format,
-               uint32_t* pSrcPalette) override;
+               pdfium::span<const uint32_t> src_palette) override;
 
   RetainPtr<CFX_DIBitmap> GetBitmap() { return m_pBitmap; }
   RetainPtr<CFX_DIBitmap> Detach();
diff --git a/core/fxge/dib/cfx_cmyk_to_srgb.cpp b/core/fxge/dib/cfx_cmyk_to_srgb.cpp
index d75987c..655a6c0 100644
--- a/core/fxge/dib/cfx_cmyk_to_srgb.cpp
+++ b/core/fxge/dib/cfx_cmyk_to_srgb.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,6 +10,7 @@
 #include <tuple>
 
 #include "core/fxcrt/fx_system.h"
+#include "third_party/base/check_op.h"
 
 namespace fxge {
 
@@ -1740,10 +1741,10 @@
   uint8_t y1 = static_cast<int>(y * 255.f + rounding_offset);
   uint8_t k1 = static_cast<int>(k * 255.f + rounding_offset);
 
-  ASSERT(c1 == FXSYS_roundf(c * 255));
-  ASSERT(m1 == FXSYS_roundf(m * 255));
-  ASSERT(y1 == FXSYS_roundf(y * 255));
-  ASSERT(k1 == FXSYS_roundf(k * 255));
+  DCHECK_EQ(c1, FXSYS_roundf(c * 255));
+  DCHECK_EQ(m1, FXSYS_roundf(m * 255));
+  DCHECK_EQ(y1, FXSYS_roundf(y * 255));
+  DCHECK_EQ(k1, FXSYS_roundf(k * 255));
 
   uint8_t r;
   uint8_t g;
diff --git a/core/fxge/dib/cfx_cmyk_to_srgb.h b/core/fxge/dib/cfx_cmyk_to_srgb.h
index aebd27a..e38e57c 100644
--- a/core/fxge/dib/cfx_cmyk_to_srgb.h
+++ b/core/fxge/dib/cfx_cmyk_to_srgb.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxge/dib/cfx_cmyk_to_srgb_unittest.cpp b/core/fxge/dib/cfx_cmyk_to_srgb_unittest.cpp
index e1c0ffd..d28f9cd 100644
--- a/core/fxge/dib/cfx_cmyk_to_srgb_unittest.cpp
+++ b/core/fxge/dib/cfx_cmyk_to_srgb_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxge/dib/cfx_dibbase.cpp b/core/fxge/dib/cfx_dibbase.cpp
index 4a4223a..97f8440 100644
--- a/core/fxge/dib/cfx_dibbase.cpp
+++ b/core/fxge/dib/cfx_dibbase.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,18 +6,28 @@
 
 #include "core/fxge/dib/cfx_dibbase.h"
 
+#include <stdint.h>
+#include <string.h>
+
 #include <algorithm>
-#include <memory>
 #include <utility>
 #include <vector>
 
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_2d_size.h"
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/span_util.h"
 #include "core/fxge/cfx_cliprgn.h"
 #include "core/fxge/dib/cfx_bitmapstorer.h"
-#include "core/fxge/dib/cfx_cmyk_to_srgb.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/dib/cfx_imagestretcher.h"
 #include "core/fxge/dib/cfx_imagetransformer.h"
-#include "third_party/base/logging.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/span.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
@@ -48,7 +58,7 @@
 
 class CFX_Palette {
  public:
-  explicit CFX_Palette(const RetainPtr<CFX_DIBBase>& pBitmap);
+  explicit CFX_Palette(const RetainPtr<const CFX_DIBBase>& pBitmap);
   ~CFX_Palette();
 
   const uint32_t* GetPalette() { return m_Palette.data(); }
@@ -60,18 +70,19 @@
   std::vector<uint32_t> m_Palette;
   // (Amount, Color) pairs
   std::vector<std::pair<uint32_t, uint32_t>> m_Luts;
-  int m_lut;
+  int m_lut = 0;
 };
 
-CFX_Palette::CFX_Palette(const RetainPtr<CFX_DIBBase>& pBitmap)
-    : m_Palette(256), m_Luts(4096), m_lut(0) {
+CFX_Palette::CFX_Palette(const RetainPtr<const CFX_DIBBase>& pBitmap)
+    : m_Palette(256), m_Luts(4096) {
   int bpp = pBitmap->GetBPP() / 8;
   int width = pBitmap->GetWidth();
   int height = pBitmap->GetHeight();
   for (int row = 0; row < height; ++row) {
-    const uint8_t* scan_line = pBitmap->GetScanline(row);
+    pdfium::span<const uint8_t> scan_line = pBitmap->GetScanline(row);
     for (int col = 0; col < width; ++col) {
-      const uint8_t* src_port = scan_line + col * bpp;
+      const uint8_t* src_port =
+          scan_line.subspan(Fx2DSizeOrDie(col, bpp)).data();
       uint32_t b = src_port[0] & 0xf0;
       uint32_t g = src_port[1] & 0xf0;
       uint32_t r = src_port[2] & 0xf0;
@@ -95,21 +106,25 @@
   Obtain_Pal(m_Luts.data(), m_Palette.data(), m_lut);
 }
 
-CFX_Palette::~CFX_Palette() {}
+CFX_Palette::~CFX_Palette() = default;
 
-void ConvertBuffer_1bppMask2Gray(uint8_t* dest_buf,
+void ConvertBuffer_1bppMask2Gray(pdfium::span<uint8_t> dest_buf,
                                  int dest_pitch,
                                  int width,
                                  int height,
-                                 const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                 const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
                                  int src_left,
                                  int src_top) {
   static constexpr uint8_t kSetGray = 0xff;
   static constexpr uint8_t kResetGray = 0x00;
   for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    memset(dest_scan, kResetGray, width);
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
+    pdfium::span<uint8_t> dest_span =
+        dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch));
+    pdfium::span<const uint8_t> src_span =
+        pSrcBitmap->GetScanline(src_top + row);
+    fxcrt::spanset(dest_span.first(width), kResetGray);
+    uint8_t* dest_scan = dest_span.data();
+    const uint8_t* src_scan = src_span.data();
     for (int col = src_left; col < src_left + width; ++col) {
       if (src_scan[col / 8] & (1 << (7 - col % 8)))
         *dest_scan = kSetGray;
@@ -118,151 +133,111 @@
   }
 }
 
-void ConvertBuffer_8bppMask2Gray(uint8_t* dest_buf,
+void ConvertBuffer_8bppMask2Gray(pdfium::span<uint8_t> dest_buf,
                                  int dest_pitch,
                                  int width,
                                  int height,
-                                 const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                 const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
                                  int src_left,
                                  int src_top) {
   for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left;
-    memcpy(dest_scan, src_scan, width);
+    fxcrt::spancpy(
+        dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)),
+        pSrcBitmap->GetScanline(src_top + row).subspan(src_left, width));
   }
 }
 
-void ConvertBuffer_1bppPlt2Gray(uint8_t* dest_buf,
+void ConvertBuffer_1bppPlt2Gray(pdfium::span<uint8_t> dest_buf,
                                 int dest_pitch,
                                 int width,
                                 int height,
-                                const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
                                 int src_left,
                                 int src_top) {
-  uint32_t* src_plt = pSrcBitmap->GetPalette();
-  uint8_t gray[2];
-  uint8_t reset_r;
-  uint8_t reset_g;
-  uint8_t reset_b;
-  uint8_t set_r;
-  uint8_t set_g;
-  uint8_t set_b;
-  if (pSrcBitmap->IsCmykImage()) {
-    std::tie(reset_r, reset_g, reset_b) = AdobeCMYK_to_sRGB1(
-        FXSYS_GetCValue(src_plt[0]), FXSYS_GetMValue(src_plt[0]),
-        FXSYS_GetYValue(src_plt[0]), FXSYS_GetKValue(src_plt[0]));
-    std::tie(set_r, set_g, set_b) = AdobeCMYK_to_sRGB1(
-        FXSYS_GetCValue(src_plt[1]), FXSYS_GetMValue(src_plt[1]),
-        FXSYS_GetYValue(src_plt[1]), FXSYS_GetKValue(src_plt[1]));
-  } else {
-    reset_r = FXARGB_R(src_plt[0]);
-    reset_g = FXARGB_G(src_plt[0]);
-    reset_b = FXARGB_B(src_plt[0]);
-    set_r = FXARGB_R(src_plt[1]);
-    set_g = FXARGB_G(src_plt[1]);
-    set_b = FXARGB_B(src_plt[1]);
-  }
-  gray[0] = FXRGB2GRAY(reset_r, reset_g, reset_b);
-  gray[1] = FXRGB2GRAY(set_r, set_g, set_b);
+  pdfium::span<const uint32_t> src_palette = pSrcBitmap->GetPaletteSpan();
+  const uint8_t reset_r = FXARGB_R(src_palette[0]);
+  const uint8_t reset_g = FXARGB_G(src_palette[0]);
+  const uint8_t reset_b = FXARGB_B(src_palette[0]);
+  const uint8_t set_r = FXARGB_R(src_palette[1]);
+  const uint8_t set_g = FXARGB_G(src_palette[1]);
+  const uint8_t set_b = FXARGB_B(src_palette[1]);
+  const uint8_t gray0 = FXRGB2GRAY(reset_r, reset_g, reset_b);
+  const uint8_t gray1 = FXRGB2GRAY(set_r, set_g, set_b);
 
   for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    memset(dest_scan, gray[0], width);
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
+    pdfium::span<uint8_t> dest_span =
+        dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch));
+    fxcrt::spanset(dest_span.first(width), gray0);
+    uint8_t* dest_scan = dest_span.data();
+    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row).data();
     for (int col = src_left; col < src_left + width; ++col) {
       if (src_scan[col / 8] & (1 << (7 - col % 8)))
-        *dest_scan = gray[1];
+        *dest_scan = gray1;
       ++dest_scan;
     }
   }
 }
 
-void ConvertBuffer_8bppPlt2Gray(uint8_t* dest_buf,
+void ConvertBuffer_8bppPlt2Gray(pdfium::span<uint8_t> dest_buf,
                                 int dest_pitch,
                                 int width,
                                 int height,
-                                const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
                                 int src_left,
                                 int src_top) {
-  uint32_t* src_plt = pSrcBitmap->GetPalette();
+  pdfium::span<const uint32_t> src_palette = pSrcBitmap->GetPaletteSpan();
   uint8_t gray[256];
-  if (pSrcBitmap->IsCmykImage()) {
-    uint8_t r;
-    uint8_t g;
-    uint8_t b;
-    for (size_t i = 0; i < FX_ArraySize(gray); ++i) {
-      std::tie(r, g, b) = AdobeCMYK_to_sRGB1(
-          FXSYS_GetCValue(src_plt[i]), FXSYS_GetMValue(src_plt[i]),
-          FXSYS_GetYValue(src_plt[i]), FXSYS_GetKValue(src_plt[i]));
-      gray[i] = FXRGB2GRAY(r, g, b);
-    }
-  } else {
-    for (size_t i = 0; i < FX_ArraySize(gray); ++i) {
-      gray[i] = FXRGB2GRAY(FXARGB_R(src_plt[i]), FXARGB_G(src_plt[i]),
-                           FXARGB_B(src_plt[i]));
-    }
+  for (size_t i = 0; i < std::size(gray); ++i) {
+    gray[i] = FXRGB2GRAY(FXARGB_R(src_palette[i]), FXARGB_G(src_palette[i]),
+                         FXARGB_B(src_palette[i]));
   }
 
   for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left;
+    uint8_t* dest_scan =
+        dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data();
+    const uint8_t* src_scan =
+        pSrcBitmap->GetScanline(src_top + row).subspan(src_left).data();
     for (int col = 0; col < width; ++col)
       *dest_scan++ = gray[*src_scan++];
   }
 }
 
-void ConvertBuffer_RgbOrCmyk2Gray(uint8_t* dest_buf,
-                                  int dest_pitch,
-                                  int width,
-                                  int height,
-                                  const RetainPtr<CFX_DIBBase>& pSrcBitmap,
-                                  int src_left,
-                                  int src_top) {
-  int Bpp = pSrcBitmap->GetBPP() / 8;
-  if (pSrcBitmap->IsCmykImage()) {
-    for (int row = 0; row < height; ++row) {
-      uint8_t* dest_scan = dest_buf + row * dest_pitch;
-      const uint8_t* src_scan =
-          pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
-      for (int col = 0; col < width; ++col) {
-        uint8_t r;
-        uint8_t g;
-        uint8_t b;
-        std::tie(r, g, b) = AdobeCMYK_to_sRGB1(
-            FXSYS_GetCValue(static_cast<uint32_t>(src_scan[0])),
-            FXSYS_GetMValue(static_cast<uint32_t>(src_scan[1])),
-            FXSYS_GetYValue(static_cast<uint32_t>(src_scan[2])),
-            FXSYS_GetKValue(static_cast<uint32_t>(src_scan[3])));
-        *dest_scan++ = FXRGB2GRAY(r, g, b);
-        src_scan += 4;
-      }
-    }
-  } else {
-    for (int row = 0; row < height; ++row) {
-      uint8_t* dest_scan = dest_buf + row * dest_pitch;
-      const uint8_t* src_scan =
-          pSrcBitmap->GetScanline(src_top + row) + src_left * Bpp;
-      for (int col = 0; col < width; ++col) {
-        *dest_scan++ = FXRGB2GRAY(src_scan[2], src_scan[1], src_scan[0]);
-        src_scan += Bpp;
-      }
+void ConvertBuffer_Rgb2Gray(pdfium::span<uint8_t> dest_buf,
+                            int dest_pitch,
+                            int width,
+                            int height,
+                            const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
+                            int src_left,
+                            int src_top) {
+  const int Bpp = pSrcBitmap->GetBPP() / 8;
+  const size_t x_offset = Fx2DSizeOrDie(src_left, Bpp);
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan =
+        dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data();
+    const uint8_t* src_scan =
+        pSrcBitmap->GetScanline(src_top + row).subspan(x_offset).data();
+    for (int col = 0; col < width; ++col) {
+      *dest_scan++ = FXRGB2GRAY(src_scan[2], src_scan[1], src_scan[0]);
+      src_scan += Bpp;
     }
   }
 }
 
-void ConvertBuffer_IndexCopy(uint8_t* dest_buf,
+void ConvertBuffer_IndexCopy(pdfium::span<uint8_t> dest_buf,
                              int dest_pitch,
                              int width,
                              int height,
-                             const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                             const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
                              int src_left,
                              int src_top) {
   if (pSrcBitmap->GetBPP() == 1) {
     for (int row = 0; row < height; ++row) {
-      uint8_t* dest_scan = dest_buf + row * dest_pitch;
+      pdfium::span<uint8_t> dest_span =
+          dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch));
       // Set all destination pixels to be white initially.
-      memset(dest_scan, 255, width);
-      const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
+      fxcrt::spanset(dest_span.first(width), 255);
+      uint8_t* dest_scan = dest_span.data();
+      const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row).data();
       for (int col = src_left; col < src_left + width; ++col) {
         // If the source bit is set, then set the destination pixel to be black.
         if (src_scan[col / 8] & (1 << (7 - col % 8)))
@@ -273,49 +248,40 @@
     }
   } else {
     for (int row = 0; row < height; ++row) {
-      uint8_t* dest_scan = dest_buf + row * dest_pitch;
-      const uint8_t* src_scan =
-          pSrcBitmap->GetScanline(src_top + row) + src_left;
-      memcpy(dest_scan, src_scan, width);
+      fxcrt::spancpy(
+          dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)),
+          pSrcBitmap->GetScanline(src_top + row).subspan(src_left, width));
     }
   }
 }
 
-void ConvertBuffer_Plt2PltRgb8(uint8_t* dest_buf,
+void ConvertBuffer_Plt2PltRgb8(pdfium::span<uint8_t> dest_buf,
                                int dest_pitch,
                                int width,
                                int height,
-                               const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                               const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
                                int src_left,
                                int src_top,
-                               uint32_t* dst_plt) {
+                               pdfium::span<uint32_t> dst_plt) {
   ConvertBuffer_IndexCopy(dest_buf, dest_pitch, width, height, pSrcBitmap,
                           src_left, src_top);
-  uint32_t* src_plt = pSrcBitmap->GetPalette();
-  size_t plt_size = pSrcBitmap->GetPaletteSize();
-  if (pSrcBitmap->IsCmykImage()) {
-    for (size_t i = 0; i < plt_size; ++i) {
-      uint8_t r;
-      uint8_t g;
-      uint8_t b;
-      std::tie(r, g, b) = AdobeCMYK_to_sRGB1(
-          FXSYS_GetCValue(src_plt[i]), FXSYS_GetMValue(src_plt[i]),
-          FXSYS_GetYValue(src_plt[i]), FXSYS_GetKValue(src_plt[i]));
-      dst_plt[i] = ArgbEncode(0xff, r, g, b);
-    }
-  } else {
-    memcpy(dst_plt, src_plt, plt_size * 4);
-  }
+  const size_t plt_size = pSrcBitmap->GetRequiredPaletteSize();
+  pdfium::span<const uint32_t> src_span = pSrcBitmap->GetPaletteSpan();
+  CHECK_LE(plt_size, src_span.size());
+
+  const uint32_t* src_plt = src_span.data();
+  for (size_t i = 0; i < plt_size; ++i)
+    dst_plt[i] = src_plt[i];
 }
 
-void ConvertBuffer_Rgb2PltRgb8(uint8_t* dest_buf,
+void ConvertBuffer_Rgb2PltRgb8(pdfium::span<uint8_t> dest_buf,
                                int dest_pitch,
                                int width,
                                int height,
-                               const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                               const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
                                int src_left,
                                int src_top,
-                               uint32_t* dst_plt) {
+                               pdfium::span<uint32_t> dst_plt) {
   int bpp = pSrcBitmap->GetBPP() / 8;
   CFX_Palette palette(pSrcBitmap);
   const std::pair<uint32_t, uint32_t>* Luts = palette.GetLuts();
@@ -348,10 +314,13 @@
   }
   int32_t lut_1 = lut - 1;
   for (int row = 0; row < height; ++row) {
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left;
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
+    pdfium::span<const uint8_t> src_span =
+        pSrcBitmap->GetScanline(src_top + row).subspan(src_left);
+    uint8_t* dest_scan =
+        dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data();
     for (int col = 0; col < width; ++col) {
-      const uint8_t* src_port = src_scan + col * bpp;
+      const uint8_t* src_port =
+          src_span.subspan(Fx2DSizeOrDie(col, bpp)).data();
       int r = src_port[2] & 0xf0;
       int g = src_port[1] & 0xf0;
       int b = src_port[0] & 0xf0;
@@ -363,244 +332,183 @@
         }
     }
   }
-  memcpy(dst_plt, pal, sizeof(uint32_t) * 256);
+  for (size_t i = 0; i < 256; ++i)
+    dst_plt[i] = pal[i];
 }
 
 void ConvertBuffer_1bppMask2Rgb(FXDIB_Format dest_format,
-                                uint8_t* dest_buf,
+                                pdfium::span<uint8_t> dest_buf,
                                 int dest_pitch,
                                 int width,
                                 int height,
-                                const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
                                 int src_left,
                                 int src_top) {
   int comps = GetCompsFromFormat(dest_format);
   static constexpr uint8_t kSetGray = 0xff;
   static constexpr uint8_t kResetGray = 0x00;
   for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
+    uint8_t* dest_scan =
+        dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data();
+    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row).data();
     for (int col = src_left; col < src_left + width; ++col) {
-      if (src_scan[col / 8] & (1 << (7 - col % 8))) {
-        dest_scan[0] = kSetGray;
-        dest_scan[1] = kSetGray;
-        dest_scan[2] = kSetGray;
-      } else {
-        dest_scan[0] = kResetGray;
-        dest_scan[1] = kResetGray;
-        dest_scan[2] = kResetGray;
-      }
+      uint8_t value =
+          (src_scan[col / 8] & (1 << (7 - col % 8))) ? kSetGray : kResetGray;
+      memset(dest_scan, value, 3);
       dest_scan += comps;
     }
   }
 }
 
 void ConvertBuffer_8bppMask2Rgb(FXDIB_Format dest_format,
-                                uint8_t* dest_buf,
+                                pdfium::span<uint8_t> dest_buf,
                                 int dest_pitch,
                                 int width,
                                 int height,
-                                const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
                                 int src_left,
                                 int src_top) {
   int comps = GetCompsFromFormat(dest_format);
   for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left;
-    uint8_t src_pixel;
-    for (int col = 0; col < width; ++col) {
-      src_pixel = *src_scan++;
-      *dest_scan++ = src_pixel;
-      *dest_scan++ = src_pixel;
-      *dest_scan = src_pixel;
-      dest_scan += comps - 2;
-    }
-  }
-}
-
-void ConvertBuffer_1bppPlt2Rgb(FXDIB_Format dest_format,
-                               uint8_t* dest_buf,
-                               int dest_pitch,
-                               int width,
-                               int height,
-                               const RetainPtr<CFX_DIBBase>& pSrcBitmap,
-                               int src_left,
-                               int src_top) {
-  int comps = GetCompsFromFormat(dest_format);
-  uint32_t* src_plt = pSrcBitmap->GetPalette();
-  uint32_t plt[2];
-  uint8_t* bgr_ptr = reinterpret_cast<uint8_t*>(plt);
-  if (pSrcBitmap->IsCmykImage()) {
-    plt[0] = FXCMYK_TODIB(src_plt[0]);
-    plt[1] = FXCMYK_TODIB(src_plt[1]);
-  } else {
-    bgr_ptr[0] = FXARGB_B(src_plt[0]);
-    bgr_ptr[1] = FXARGB_G(src_plt[0]);
-    bgr_ptr[2] = FXARGB_R(src_plt[0]);
-    bgr_ptr[3] = FXARGB_B(src_plt[1]);
-    bgr_ptr[4] = FXARGB_G(src_plt[1]);
-    bgr_ptr[5] = FXARGB_R(src_plt[1]);
-  }
-
-  if (pSrcBitmap->IsCmykImage()) {
-    std::tie(bgr_ptr[2], bgr_ptr[1], bgr_ptr[0]) = AdobeCMYK_to_sRGB1(
-        FXSYS_GetCValue(src_plt[0]), FXSYS_GetMValue(src_plt[0]),
-        FXSYS_GetYValue(src_plt[0]), FXSYS_GetKValue(src_plt[0]));
-    std::tie(bgr_ptr[5], bgr_ptr[4], bgr_ptr[3]) = AdobeCMYK_to_sRGB1(
-        FXSYS_GetCValue(src_plt[1]), FXSYS_GetMValue(src_plt[1]),
-        FXSYS_GetYValue(src_plt[1]), FXSYS_GetKValue(src_plt[1]));
-  }
-
-  for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
-    for (int col = src_left; col < src_left + width; ++col) {
-      if (src_scan[col / 8] & (1 << (7 - col % 8))) {
-        *dest_scan++ = bgr_ptr[3];
-        *dest_scan++ = bgr_ptr[4];
-        *dest_scan = bgr_ptr[5];
-      } else {
-        *dest_scan++ = bgr_ptr[0];
-        *dest_scan++ = bgr_ptr[1];
-        *dest_scan = bgr_ptr[2];
-      }
-      dest_scan += comps - 2;
-    }
-  }
-}
-
-void ConvertBuffer_8bppPlt2Rgb(FXDIB_Format dest_format,
-                               uint8_t* dest_buf,
-                               int dest_pitch,
-                               int width,
-                               int height,
-                               const RetainPtr<CFX_DIBBase>& pSrcBitmap,
-                               int src_left,
-                               int src_top) {
-  int comps = GetCompsFromFormat(dest_format);
-  uint32_t* src_plt = pSrcBitmap->GetPalette();
-  uint32_t plt[256];
-  uint8_t* bgr_ptr = reinterpret_cast<uint8_t*>(plt);
-  if (!pSrcBitmap->IsCmykImage()) {
-    for (int i = 0; i < 256; ++i) {
-      *bgr_ptr++ = FXARGB_B(src_plt[i]);
-      *bgr_ptr++ = FXARGB_G(src_plt[i]);
-      *bgr_ptr++ = FXARGB_R(src_plt[i]);
-    }
-    bgr_ptr = reinterpret_cast<uint8_t*>(plt);
-  }
-
-  if (pSrcBitmap->IsCmykImage()) {
-    for (int i = 0; i < 256; ++i) {
-      std::tie(bgr_ptr[2], bgr_ptr[1], bgr_ptr[0]) = AdobeCMYK_to_sRGB1(
-          FXSYS_GetCValue(src_plt[i]), FXSYS_GetMValue(src_plt[i]),
-          FXSYS_GetYValue(src_plt[i]), FXSYS_GetKValue(src_plt[i]));
-      bgr_ptr += 3;
-    }
-    bgr_ptr = reinterpret_cast<uint8_t*>(plt);
-  }
-
-  for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left;
-    for (int col = 0; col < width; ++col) {
-      uint8_t* src_pixel = bgr_ptr + 3 * (*src_scan++);
-      *dest_scan++ = *src_pixel++;
-      *dest_scan++ = *src_pixel++;
-      *dest_scan = *src_pixel++;
-      dest_scan += comps - 2;
-    }
-  }
-}
-
-void ConvertBuffer_24bppRgb2Rgb24(uint8_t* dest_buf,
-                                  int dest_pitch,
-                                  int width,
-                                  int height,
-                                  const RetainPtr<CFX_DIBBase>& pSrcBitmap,
-                                  int src_left,
-                                  int src_top) {
-  for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
+    uint8_t* dest_scan =
+        dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data();
     const uint8_t* src_scan =
-        pSrcBitmap->GetScanline(src_top + row) + src_left * 3;
-    memcpy(dest_scan, src_scan, width * 3);
-  }
-}
-
-void ConvertBuffer_32bppRgb2Rgb24(uint8_t* dest_buf,
-                                  int dest_pitch,
-                                  int width,
-                                  int height,
-                                  const RetainPtr<CFX_DIBBase>& pSrcBitmap,
-                                  int src_left,
-                                  int src_top) {
-  for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan =
-        pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
+        pSrcBitmap->GetScanline(src_top + row).subspan(src_left).data();
     for (int col = 0; col < width; ++col) {
-      *dest_scan++ = *src_scan++;
-      *dest_scan++ = *src_scan++;
-      *dest_scan++ = *src_scan++;
+      memset(dest_scan, *src_scan, 3);
+      dest_scan += comps;
       ++src_scan;
     }
   }
 }
 
-void ConvertBuffer_Rgb2Rgb32(uint8_t* dest_buf,
-                             int dest_pitch,
-                             int width,
-                             int height,
-                             const RetainPtr<CFX_DIBBase>& pSrcBitmap,
-                             int src_left,
-                             int src_top) {
-  int comps = pSrcBitmap->GetBPP() / 8;
+void ConvertBuffer_1bppPlt2Rgb(FXDIB_Format dest_format,
+                               pdfium::span<uint8_t> dest_buf,
+                               int dest_pitch,
+                               int width,
+                               int height,
+                               const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
+                               int src_left,
+                               int src_top) {
+  pdfium::span<const uint32_t> src_palette = pSrcBitmap->GetPaletteSpan();
+  const uint8_t dst_palette[6] = {
+      FXARGB_B(src_palette[0]), FXARGB_G(src_palette[0]),
+      FXARGB_R(src_palette[0]), FXARGB_B(src_palette[1]),
+      FXARGB_G(src_palette[1]), FXARGB_R(src_palette[1])};
+  int comps = GetCompsFromFormat(dest_format);
   for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan =
-        pSrcBitmap->GetScanline(src_top + row) + src_left * comps;
-    for (int col = 0; col < width; ++col) {
-      *dest_scan++ = *src_scan++;
-      *dest_scan++ = *src_scan++;
-      *dest_scan++ = *src_scan++;
-      ++dest_scan;
-      src_scan += comps - 3;
+    uint8_t* dest_scan =
+        dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data();
+    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row).data();
+    for (int col = src_left; col < src_left + width; ++col) {
+      size_t offset = (src_scan[col / 8] & (1 << (7 - col % 8))) ? 3 : 0;
+      memcpy(dest_scan, dst_palette + offset, 3);
+      dest_scan += comps;
     }
   }
 }
 
-void ConvertBuffer_32bppCmyk2Rgb32(uint8_t* dest_buf,
-                                   int dest_pitch,
-                                   int width,
-                                   int height,
-                                   const RetainPtr<CFX_DIBBase>& pSrcBitmap,
-                                   int src_left,
-                                   int src_top) {
+void ConvertBuffer_8bppPlt2Rgb(FXDIB_Format dest_format,
+                               pdfium::span<uint8_t> dest_buf,
+                               int dest_pitch,
+                               int width,
+                               int height,
+                               const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
+                               int src_left,
+                               int src_top) {
+  pdfium::span<const uint32_t> src_palette = pSrcBitmap->GetPaletteSpan();
+  uint8_t dst_palette[768];
+  for (int i = 0; i < 256; ++i) {
+    dst_palette[3 * i] = FXARGB_B(src_palette[i]);
+    dst_palette[3 * i + 1] = FXARGB_G(src_palette[i]);
+    dst_palette[3 * i + 2] = FXARGB_R(src_palette[i]);
+  }
+  int comps = GetCompsFromFormat(dest_format);
   for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
+    uint8_t* dest_scan =
+        dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data();
     const uint8_t* src_scan =
-        pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
+        pSrcBitmap->GetScanline(src_top + row).subspan(src_left).data();
     for (int col = 0; col < width; ++col) {
-      std::tie(dest_scan[2], dest_scan[1], dest_scan[0]) = AdobeCMYK_to_sRGB1(
-          src_scan[0], src_scan[1], src_scan[2], src_scan[3]);
-      dest_scan += 4;
+      uint8_t* src_pixel = dst_palette + 3 * (*src_scan++);
+      memcpy(dest_scan, src_pixel, 3);
+      dest_scan += comps;
+    }
+  }
+}
+
+void ConvertBuffer_24bppRgb2Rgb24(
+    pdfium::span<uint8_t> dest_buf,
+    int dest_pitch,
+    int width,
+    int height,
+    const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
+    int src_left,
+    int src_top) {
+  const size_t x_offset = Fx2DSizeOrDie(src_left, 3);
+  const size_t byte_count = Fx2DSizeOrDie(width, 3);
+  for (int row = 0; row < height; ++row) {
+    fxcrt::spancpy(
+        dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)),
+        pSrcBitmap->GetScanline(src_top + row).subspan(x_offset, byte_count));
+  }
+}
+
+void ConvertBuffer_32bppRgb2Rgb24(
+    pdfium::span<uint8_t> dest_buf,
+    int dest_pitch,
+    int width,
+    int height,
+    const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
+    int src_left,
+    int src_top) {
+  const size_t x_offset = Fx2DSizeOrDie(src_left, 4);
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan =
+        dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data();
+    const uint8_t* src_scan =
+        pSrcBitmap->GetScanline(src_top + row).subspan(x_offset).data();
+    for (int col = 0; col < width; ++col) {
+      memcpy(dest_scan, src_scan, 3);
+      dest_scan += 3;
       src_scan += 4;
     }
   }
 }
 
+void ConvertBuffer_Rgb2Rgb32(pdfium::span<uint8_t> dest_buf,
+                             int dest_pitch,
+                             int width,
+                             int height,
+                             const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
+                             int src_left,
+                             int src_top) {
+  const int comps = pSrcBitmap->GetBPP() / 8;
+  const size_t x_offset = Fx2DSizeOrDie(src_left, comps);
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan =
+        dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data();
+    const uint8_t* src_scan =
+        pSrcBitmap->GetScanline(src_top + row).subspan(x_offset).data();
+    for (int col = 0; col < width; ++col) {
+      memcpy(dest_scan, src_scan, 3);
+      dest_scan += 4;
+      src_scan += comps;
+    }
+  }
+}
+
 bool ConvertBuffer_8bppMask(int bpp,
-                            uint8_t* dest_buf,
+                            pdfium::span<uint8_t> dest_buf,
                             int dest_pitch,
                             int width,
                             int height,
-                            const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                            const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
                             int src_left,
                             int src_top) {
   switch (bpp) {
     case 1:
-      if (pSrcBitmap->GetPalette()) {
+      if (pSrcBitmap->HasPalette()) {
         ConvertBuffer_1bppPlt2Gray(dest_buf, dest_pitch, width, height,
                                    pSrcBitmap, src_left, src_top);
       } else {
@@ -609,7 +517,7 @@
       }
       return true;
     case 8:
-      if (pSrcBitmap->GetPalette()) {
+      if (pSrcBitmap->HasPalette()) {
         ConvertBuffer_8bppPlt2Gray(dest_buf, dest_pitch, width, height,
                                    pSrcBitmap, src_left, src_top);
       } else {
@@ -619,8 +527,8 @@
       return true;
     case 24:
     case 32:
-      ConvertBuffer_RgbOrCmyk2Gray(dest_buf, dest_pitch, width, height,
-                                   pSrcBitmap, src_left, src_top);
+      ConvertBuffer_Rgb2Gray(dest_buf, dest_pitch, width, height, pSrcBitmap,
+                             src_left, src_top);
       return true;
     default:
       return false;
@@ -629,16 +537,16 @@
 
 bool ConvertBuffer_Rgb(int bpp,
                        FXDIB_Format dest_format,
-                       uint8_t* dest_buf,
+                       pdfium::span<uint8_t> dest_buf,
                        int dest_pitch,
                        int width,
                        int height,
-                       const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                       const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
                        int src_left,
                        int src_top) {
   switch (bpp) {
     case 1:
-      if (pSrcBitmap->GetPalette()) {
+      if (pSrcBitmap->HasPalette()) {
         ConvertBuffer_1bppPlt2Rgb(dest_format, dest_buf, dest_pitch, width,
                                   height, pSrcBitmap, src_left, src_top);
       } else {
@@ -647,7 +555,7 @@
       }
       return true;
     case 8:
-      if (pSrcBitmap->GetPalette()) {
+      if (pSrcBitmap->HasPalette()) {
         ConvertBuffer_8bppPlt2Rgb(dest_format, dest_buf, dest_pitch, width,
                                   height, pSrcBitmap, src_left, src_top);
       } else {
@@ -669,18 +577,17 @@
 }
 
 bool ConvertBuffer_Argb(int bpp,
-                        bool cmyk,
                         FXDIB_Format dest_format,
-                        uint8_t* dest_buf,
+                        pdfium::span<uint8_t> dest_buf,
                         int dest_pitch,
                         int width,
                         int height,
-                        const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                        const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
                         int src_left,
                         int src_top) {
   switch (bpp) {
     case 1:
-      if (pSrcBitmap->GetPalette()) {
+      if (pSrcBitmap->HasPalette()) {
         ConvertBuffer_1bppPlt2Rgb(dest_format, dest_buf, dest_pitch, width,
                                   height, pSrcBitmap, src_left, src_top);
       } else {
@@ -689,7 +596,7 @@
       }
       return true;
     case 8:
-      if (pSrcBitmap->GetPalette()) {
+      if (pSrcBitmap->HasPalette()) {
         ConvertBuffer_8bppPlt2Rgb(dest_format, dest_buf, dest_pitch, width,
                                   height, pSrcBitmap, src_left, src_top);
       } else {
@@ -699,13 +606,8 @@
       return true;
     case 24:
     case 32:
-      if (cmyk) {
-        ConvertBuffer_32bppCmyk2Rgb32(dest_buf, dest_pitch, width, height,
-                                      pSrcBitmap, src_left, src_top);
-      } else {
-        ConvertBuffer_Rgb2Rgb32(dest_buf, dest_pitch, width, height, pSrcBitmap,
-                                src_left, src_top);
-      }
+      ConvertBuffer_Rgb2Rgb32(dest_buf, dest_pitch, width, height, pSrcBitmap,
+                              src_left, src_top);
       return true;
     default:
       return false;
@@ -714,20 +616,32 @@
 
 }  // namespace
 
-CFX_DIBBase::CFX_DIBBase()
-    : m_Width(0), m_Height(0), m_bpp(0), m_AlphaFlag(0), m_Pitch(0) {}
+CFX_DIBBase::CFX_DIBBase() = default;
 
-CFX_DIBBase::~CFX_DIBBase() {}
+CFX_DIBBase::~CFX_DIBBase() = default;
 
-uint8_t* CFX_DIBBase::GetBuffer() const {
-  return nullptr;
+pdfium::span<const uint8_t> CFX_DIBBase::GetBuffer() const {
+  return pdfium::span<const uint8_t>();
 }
 
 bool CFX_DIBBase::SkipToScanline(int line, PauseIndicatorIface* pPause) const {
   return false;
 }
 
-RetainPtr<CFX_DIBitmap> CFX_DIBBase::Clone(const FX_RECT* pClip) const {
+size_t CFX_DIBBase::GetEstimatedImageMemoryBurden() const {
+  return GetRequiredPaletteSize() * sizeof(uint32_t);
+}
+
+RetainPtr<CFX_DIBitmap> CFX_DIBBase::Realize() const {
+  return ClipToInternal(nullptr);
+}
+
+RetainPtr<CFX_DIBitmap> CFX_DIBBase::ClipTo(const FX_RECT& rect) const {
+  return ClipToInternal(&rect);
+}
+
+RetainPtr<CFX_DIBitmap> CFX_DIBBase::ClipToInternal(
+    const FX_RECT* pClip) const {
   FX_RECT rect(0, 0, m_Width, m_Height);
   if (pClip) {
     rect.Intersect(*pClip);
@@ -738,80 +652,67 @@
   if (!pNewBitmap->Create(rect.Width(), rect.Height(), GetFormat()))
     return nullptr;
 
-  pNewBitmap->SetPalette(m_pPalette.get());
-  pNewBitmap->SetAlphaMask(m_pAlphaMask, pClip);
+  pNewBitmap->SetPalette(GetPaletteSpan());
   if (GetBPP() == 1 && rect.left % 8 != 0) {
     int left_shift = rect.left % 32;
     int right_shift = 32 - left_shift;
     int dword_count = pNewBitmap->m_Pitch / 4;
     for (int row = rect.top; row < rect.bottom; ++row) {
       const uint32_t* src_scan =
-          reinterpret_cast<const uint32_t*>(GetScanline(row)) + rect.left / 32;
+          reinterpret_cast<const uint32_t*>(GetScanline(row).data()) +
+          rect.left / 32;
       uint32_t* dest_scan = reinterpret_cast<uint32_t*>(
-          pNewBitmap->GetWritableScanline(row - rect.top));
+          pNewBitmap->GetWritableScanline(row - rect.top).data());
       for (int i = 0; i < dword_count; ++i) {
         dest_scan[i] =
             (src_scan[i] << left_shift) | (src_scan[i + 1] >> right_shift);
       }
     }
   } else {
-    int copy_len = (pNewBitmap->GetWidth() * pNewBitmap->GetBPP() + 7) / 8;
-    if (m_Pitch < static_cast<uint32_t>(copy_len))
-      copy_len = m_Pitch;
+    FX_SAFE_UINT32 copy_len = pNewBitmap->GetWidth();
+    copy_len *= pNewBitmap->GetBPP();
+    copy_len += 7;
+    copy_len /= 8;
+    if (!copy_len.IsValid())
+      return nullptr;
+
+    copy_len = std::min<uint32_t>(m_Pitch, copy_len.ValueOrDie());
+
+    FX_SAFE_UINT32 offset = rect.left;
+    offset *= GetBppFromFormat(m_Format);
+    offset /= 8;
+    if (!offset.IsValid())
+      return nullptr;
 
     for (int row = rect.top; row < rect.bottom; ++row) {
-      const uint8_t* src_scan = GetScanline(row) + rect.left * m_bpp / 8;
-      uint8_t* dest_scan = pNewBitmap->GetWritableScanline(row - rect.top);
-      memcpy(dest_scan, src_scan, copy_len);
+      const uint8_t* src_scan =
+          GetScanline(row).subspan(offset.ValueOrDie()).data();
+      uint8_t* dest_scan =
+          pNewBitmap->GetWritableScanline(row - rect.top).data();
+      memcpy(dest_scan, src_scan, copy_len.ValueOrDie());
     }
   }
   return pNewBitmap;
 }
 
 void CFX_DIBBase::BuildPalette() {
-  if (m_pPalette)
+  if (HasPalette())
     return;
 
   if (GetBPP() == 1) {
-    m_pPalette.reset(FX_Alloc(uint32_t, 2));
-    if (IsCmykImage()) {
-      m_pPalette.get()[0] = 0xff;
-      m_pPalette.get()[1] = 0;
-    } else {
-      m_pPalette.get()[0] = 0xff000000;
-      m_pPalette.get()[1] = 0xffffffff;
-    }
+    m_palette = {0xff000000, 0xffffffff};
   } else if (GetBPP() == 8) {
-    m_pPalette.reset(FX_Alloc(uint32_t, 256));
-    if (IsCmykImage()) {
-      for (int i = 0; i < 256; ++i)
-        m_pPalette.get()[i] = 0xff - i;
-    } else {
-      for (int i = 0; i < 256; ++i)
-        m_pPalette.get()[i] = 0xff000000 | (i * 0x10101);
-    }
+    m_palette.resize(256);
+    for (int i = 0; i < 256; ++i)
+      m_palette[i] = ArgbEncode(0xff, i, i, i);
   }
 }
 
-bool CFX_DIBBase::BuildAlphaMask() {
-  if (m_pAlphaMask)
-    return true;
-
-  m_pAlphaMask = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!m_pAlphaMask->Create(m_Width, m_Height, FXDIB_8bppMask)) {
-    m_pAlphaMask = nullptr;
-    return false;
-  }
-  memset(m_pAlphaMask->GetBuffer(), 0xff,
-         m_pAlphaMask->GetHeight() * m_pAlphaMask->GetPitch());
-  return true;
-}
-
-size_t CFX_DIBBase::GetPaletteSize() const {
-  if (IsAlphaMask())
+size_t CFX_DIBBase::GetRequiredPaletteSize() const {
+  if (IsMaskFormat())
     return 0;
 
-  switch (m_bpp) {
+  switch (GetBppFromFormat(m_Format)) {
     case 1:
       return 2;
     case 8:
@@ -822,50 +723,37 @@
 }
 
 uint32_t CFX_DIBBase::GetPaletteArgb(int index) const {
-  ASSERT((GetBPP() == 1 || GetBPP() == 8) && !IsAlphaMask());
-  if (m_pPalette)
-    return m_pPalette.get()[index];
+  DCHECK((GetBPP() == 1 || GetBPP() == 8) && !IsMaskFormat());
+  if (HasPalette())
+    return GetPaletteSpan()[index];
 
-  if (IsCmykImage()) {
-    if (GetBPP() == 1)
-      return index ? 0 : 0xff;
-
-    return 0xff - index;
-  }
   if (GetBPP() == 1)
     return index ? 0xffffffff : 0xff000000;
 
-  return index * 0x10101 | 0xff000000;
+  return ArgbEncode(0xff, index, index, index);
 }
 
 void CFX_DIBBase::SetPaletteArgb(int index, uint32_t color) {
-  ASSERT((GetBPP() == 1 || GetBPP() == 8) && !IsAlphaMask());
-  if (!m_pPalette) {
-    BuildPalette();
-  }
-  m_pPalette.get()[index] = color;
+  DCHECK((GetBPP() == 1 || GetBPP() == 8) && !IsMaskFormat());
+  BuildPalette();
+  m_palette[index] = color;
 }
 
 int CFX_DIBBase::FindPalette(uint32_t color) const {
-  ASSERT((GetBPP() == 1 || GetBPP() == 8) && !IsAlphaMask());
-  if (!m_pPalette) {
-    if (IsCmykImage()) {
-      if (GetBPP() == 1)
-        return (static_cast<uint8_t>(color) == 0xff) ? 0 : 1;
-
-      return 0xff - static_cast<uint8_t>(color);
+  DCHECK((GetBPP() == 1 || GetBPP() == 8) && !IsMaskFormat());
+  if (HasPalette()) {
+    int palsize = (1 << GetBPP());
+    pdfium::span<const uint32_t> palette = GetPaletteSpan();
+    for (int i = 0; i < palsize; ++i) {
+      if (palette[i] == color)
+        return i;
     }
-    if (GetBPP() == 1)
-      return (static_cast<uint8_t>(color) == 0xff) ? 1 : 0;
+    return -1;
+  }
 
-    return static_cast<uint8_t>(color);
-  }
-  int palsize = (1 << GetBPP());
-  for (int i = 0; i < palsize; ++i) {
-    if (m_pPalette.get()[i] == color)
-      return i;
-  }
-  return -1;
+  if (GetBPP() == 1)
+    return (static_cast<uint8_t>(color) == 0xff) ? 1 : 0;
+  return static_cast<uint8_t>(color);
 }
 
 bool CFX_DIBBase::GetOverlapRect(int& dest_left,
@@ -876,93 +764,115 @@
                                  int src_height,
                                  int& src_left,
                                  int& src_top,
-                                 const CFX_ClipRgn* pClipRgn) {
+                                 const CFX_ClipRgn* pClipRgn) const {
   if (width == 0 || height == 0)
     return false;
 
-  ASSERT(width > 0);
-  ASSERT(height > 0);
+  DCHECK_GT(width, 0);
+  DCHECK_GT(height, 0);
 
-  if (dest_left > m_Width || dest_top > m_Height) {
-    width = 0;
-    height = 0;
+  if (dest_left > m_Width || dest_top > m_Height)
     return false;
-  }
-  int x_offset = dest_left - src_left;
-  int y_offset = dest_top - src_top;
-  FX_RECT src_rect(src_left, src_top, src_left + width, src_top + height);
+
+  FX_SAFE_INT32 safe_src_width = src_left;
+  safe_src_width += width;
+  if (!safe_src_width.IsValid())
+    return false;
+
+  FX_SAFE_INT32 safe_src_height = src_top;
+  safe_src_height += height;
+  if (!safe_src_height.IsValid())
+    return false;
+
+  FX_RECT src_rect(src_left, src_top, safe_src_width.ValueOrDie(),
+                   safe_src_height.ValueOrDie());
   FX_RECT src_bound(0, 0, src_width, src_height);
   src_rect.Intersect(src_bound);
-  FX_RECT dest_rect(src_rect.left + x_offset, src_rect.top + y_offset,
-                    src_rect.right + x_offset, src_rect.bottom + y_offset);
+
+  FX_SAFE_INT32 safe_x_offset = dest_left;
+  safe_x_offset -= src_left;
+  if (!safe_x_offset.IsValid())
+    return false;
+
+  FX_SAFE_INT32 safe_y_offset = dest_top;
+  safe_y_offset -= src_top;
+  if (!safe_y_offset.IsValid())
+    return false;
+
+  FX_SAFE_INT32 safe_dest_left = safe_x_offset;
+  safe_dest_left += src_rect.left;
+  if (!safe_dest_left.IsValid())
+    return false;
+
+  FX_SAFE_INT32 safe_dest_top = safe_y_offset;
+  safe_dest_top += src_rect.top;
+  if (!safe_dest_top.IsValid())
+    return false;
+
+  FX_SAFE_INT32 safe_dest_right = safe_x_offset;
+  safe_dest_right += src_rect.right;
+  if (!safe_dest_right.IsValid())
+    return false;
+
+  FX_SAFE_INT32 safe_dest_bottom = safe_y_offset;
+  safe_dest_bottom += src_rect.bottom;
+  if (!safe_dest_bottom.IsValid())
+    return false;
+
+  FX_RECT dest_rect(safe_dest_left.ValueOrDie(), safe_dest_top.ValueOrDie(),
+                    safe_dest_right.ValueOrDie(),
+                    safe_dest_bottom.ValueOrDie());
   FX_RECT dest_bound(0, 0, m_Width, m_Height);
   dest_rect.Intersect(dest_bound);
+
   if (pClipRgn)
     dest_rect.Intersect(pClipRgn->GetBox());
   dest_left = dest_rect.left;
   dest_top = dest_rect.top;
 
-  pdfium::base::CheckedNumeric<int> safe_src_left = dest_left;
-  safe_src_left -= x_offset;
-  if (!safe_src_left.IsValid())
+  FX_SAFE_INT32 safe_new_src_left = dest_left;
+  safe_new_src_left -= safe_x_offset;
+  if (!safe_new_src_left.IsValid())
     return false;
-  src_left = safe_src_left.ValueOrDie();
+  src_left = safe_new_src_left.ValueOrDie();
 
-  pdfium::base::CheckedNumeric<int> safe_src_top = dest_top;
-  safe_src_top -= y_offset;
-  if (!safe_src_top.IsValid())
+  FX_SAFE_INT32 safe_new_src_top = dest_top;
+  safe_new_src_top -= safe_y_offset;
+  if (!safe_new_src_top.IsValid())
     return false;
-  src_top = safe_src_top.ValueOrDie();
+  src_top = safe_new_src_top.ValueOrDie();
 
-  width = dest_rect.right - dest_rect.left;
-  height = dest_rect.bottom - dest_rect.top;
-  return width != 0 && height != 0;
+  if (dest_rect.IsEmpty())
+    return false;
+
+  width = dest_rect.Width();
+  height = dest_rect.Height();
+  return true;
 }
 
-void CFX_DIBBase::SetPalette(const uint32_t* pSrc) {
-  static const uint32_t kPaletteSize = 256;
-  if (!pSrc || GetBPP() > 8) {
-    m_pPalette.reset();
+void CFX_DIBBase::SetPalette(pdfium::span<const uint32_t> src_palette) {
+  if (src_palette.empty() || GetBPP() > 8) {
+    m_palette.clear();
     return;
   }
   uint32_t pal_size = 1 << GetBPP();
-  if (!m_pPalette)
-    m_pPalette.reset(FX_Alloc(uint32_t, pal_size));
+  if (m_palette.empty())
+    m_palette.resize(pal_size);
   pal_size = std::min(pal_size, kPaletteSize);
-  memcpy(m_pPalette.get(), pSrc, pal_size * sizeof(uint32_t));
-}
-
-void CFX_DIBBase::GetPalette(uint32_t* pal, int alpha) const {
-  ASSERT(GetBPP() <= 8);
-  ASSERT(!IsCmykImage());
-
-  if (GetBPP() == 1) {
-    pal[0] = ((m_pPalette ? m_pPalette.get()[0] : 0xff000000) & 0xffffff) |
-             (alpha << 24);
-    pal[1] = ((m_pPalette ? m_pPalette.get()[1] : 0xffffffff) & 0xffffff) |
-             (alpha << 24);
-    return;
-  }
-  if (m_pPalette) {
-    for (int i = 0; i < 256; ++i)
-      pal[i] = (m_pPalette.get()[i] & 0x00ffffff) | (alpha << 24);
-  } else {
-    for (int i = 0; i < 256; ++i)
-      pal[i] = (i * 0x10101) | (alpha << 24);
-  }
+  for (size_t i = 0; i < pal_size; ++i)
+    m_palette[i] = src_palette[i];
 }
 
 RetainPtr<CFX_DIBitmap> CFX_DIBBase::CloneAlphaMask() const {
-  ASSERT(GetFormat() == FXDIB_Argb);
-  FX_RECT rect(0, 0, m_Width, m_Height);
+  DCHECK_EQ(GetFormat(), FXDIB_Format::kArgb);
   auto pMask = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pMask->Create(rect.Width(), rect.Height(), FXDIB_8bppMask))
+  if (!pMask->Create(m_Width, m_Height, FXDIB_Format::k8bppMask))
     return nullptr;
 
-  for (int row = rect.top; row < rect.bottom; ++row) {
-    const uint8_t* src_scan = GetScanline(row) + rect.left * 4 + 3;
-    uint8_t* dest_scan = pMask->GetWritableScanline(row - rect.top);
-    for (int col = rect.left; col < rect.right; ++col) {
+  for (int row = 0; row < m_Height; ++row) {
+    const uint8_t* src_scan = GetScanline(row).subspan(3).data();
+    uint8_t* dest_scan = pMask->GetWritableScanline(row).data();
+    for (int col = 0; col < m_Width; ++col) {
       *dest_scan++ = *src_scan;
       src_scan += 4;
     }
@@ -970,51 +880,22 @@
   return pMask;
 }
 
-bool CFX_DIBBase::SetAlphaMask(const RetainPtr<CFX_DIBBase>& pAlphaMask,
-                               const FX_RECT* pClip) {
-  if (!HasAlpha() || GetFormat() == FXDIB_Argb)
-    return false;
-
-  if (!pAlphaMask) {
-    m_pAlphaMask->Clear(0xff000000);
-    return true;
-  }
-  FX_RECT rect(0, 0, pAlphaMask->m_Width, pAlphaMask->m_Height);
-  if (pClip) {
-    rect.Intersect(*pClip);
-    if (rect.IsEmpty() || rect.Width() != m_Width ||
-        rect.Height() != m_Height) {
-      return false;
-    }
-  } else {
-    if (pAlphaMask->m_Width != m_Width || pAlphaMask->m_Height != m_Height)
-      return false;
-  }
-  for (int row = 0; row < m_Height; ++row) {
-    memcpy(m_pAlphaMask->GetWritableScanline(row),
-           pAlphaMask->GetScanline(row + rect.top) + rect.left,
-           m_pAlphaMask->m_Pitch);
-  }
-  return true;
-}
-
 RetainPtr<CFX_DIBitmap> CFX_DIBBase::FlipImage(bool bXFlip, bool bYFlip) const {
   auto pFlipped = pdfium::MakeRetain<CFX_DIBitmap>();
   if (!pFlipped->Create(m_Width, m_Height, GetFormat()))
     return nullptr;
 
-  pFlipped->SetPalette(m_pPalette.get());
-  uint8_t* pDestBuffer = pFlipped->GetBuffer();
-  int Bpp = m_bpp / 8;
+  pFlipped->SetPalette(GetPaletteSpan());
+  int Bpp = GetBppFromFormat(m_Format) / 8;
   for (int row = 0; row < m_Height; ++row) {
-    const uint8_t* src_scan = GetScanline(row);
+    const uint8_t* src_scan = GetScanline(row).data();
     uint8_t* dest_scan =
-        pDestBuffer + m_Pitch * (bYFlip ? (m_Height - row - 1) : row);
+        pFlipped->GetWritableScanline(bYFlip ? m_Height - row - 1 : row).data();
     if (!bXFlip) {
       memcpy(dest_scan, src_scan, m_Pitch);
       continue;
     }
-    if (m_bpp == 1) {
+    if (GetBppFromFormat(m_Format) == 1) {
       memset(dest_scan, 0, m_Pitch);
       for (int col = 0; col < m_Width; ++col) {
         if (src_scan[col / 8] & (1 << (7 - col % 8))) {
@@ -1034,14 +915,12 @@
       }
     } else if (Bpp == 3) {
       for (int col = 0; col < m_Width; ++col) {
-        dest_scan[0] = src_scan[0];
-        dest_scan[1] = src_scan[1];
-        dest_scan[2] = src_scan[2];
+        memcpy(dest_scan, src_scan, 3);
         dest_scan -= 3;
         src_scan += 3;
       }
     } else {
-      ASSERT(Bpp == 4);
+      DCHECK_EQ(Bpp, 4);
       for (int col = 0; col < m_Width; ++col) {
         const auto* src_scan32 = reinterpret_cast<const uint32_t*>(src_scan);
         uint32_t* dest_scan32 = reinterpret_cast<uint32_t*>(dest_scan);
@@ -1051,62 +930,39 @@
       }
     }
   }
-  if (m_pAlphaMask) {
-    pDestBuffer = pFlipped->m_pAlphaMask->GetBuffer();
-    uint32_t dest_pitch = pFlipped->m_pAlphaMask->GetPitch();
-    for (int row = 0; row < m_Height; ++row) {
-      const uint8_t* src_scan = m_pAlphaMask->GetScanline(row);
-      uint8_t* dest_scan =
-          pDestBuffer + dest_pitch * (bYFlip ? (m_Height - row - 1) : row);
-      if (!bXFlip) {
-        memcpy(dest_scan, src_scan, dest_pitch);
-        continue;
-      }
-      dest_scan += (m_Width - 1);
-      for (int col = 0; col < m_Width; ++col) {
-        *dest_scan = *src_scan;
-        --dest_scan;
-        ++src_scan;
-      }
-    }
-  }
   return pFlipped;
 }
 
-RetainPtr<CFX_DIBitmap> CFX_DIBBase::CloneConvert(FXDIB_Format dest_format) {
+RetainPtr<CFX_DIBitmap> CFX_DIBBase::ConvertTo(FXDIB_Format dest_format) const {
   if (dest_format == GetFormat())
-    return Clone(nullptr);
+    return Realize();
 
   auto pClone = pdfium::MakeRetain<CFX_DIBitmap>();
   if (!pClone->Create(m_Width, m_Height, dest_format))
     return nullptr;
 
   RetainPtr<CFX_DIBitmap> pSrcAlpha;
-  if (HasAlpha()) {
-    pSrcAlpha = (GetFormat() == FXDIB_Argb) ? CloneAlphaMask() : m_pAlphaMask;
+  if (IsAlphaFormat()) {
+    pSrcAlpha = CloneAlphaMask();
     if (!pSrcAlpha)
       return nullptr;
   }
-  if (GetIsAlphaFromFormat(dest_format)) {
-    bool ret;
-    if (dest_format == FXDIB_Argb) {
-      ret = pSrcAlpha ? pClone->LoadChannelFromAlpha(FXDIB_Alpha, pSrcAlpha)
-                      : pClone->LoadChannel(FXDIB_Alpha, 0xff);
-    } else {
-      ret = pClone->SetAlphaMask(pSrcAlpha, nullptr);
-    }
+  if (dest_format == FXDIB_Format::kArgb) {
+    bool ret = pSrcAlpha ? pClone->SetAlphaFromBitmap(pSrcAlpha)
+                         : pClone->SetUniformOpaqueAlpha();
     if (!ret)
       return nullptr;
   }
 
-  RetainPtr<CFX_DIBBase> holder(this);
-  std::unique_ptr<uint32_t, FxFreeDeleter> pal_8bpp;
-  if (!ConvertBuffer(dest_format, pClone->GetBuffer(), pClone->GetPitch(),
-                     m_Width, m_Height, holder, 0, 0, &pal_8bpp)) {
+  RetainPtr<const CFX_DIBBase> holder(this);
+  DataVector<uint32_t> pal_8bpp;
+  if (!ConvertBuffer(dest_format, pClone->GetWritableBuffer(),
+                     pClone->GetPitch(), m_Width, m_Height, holder, 0, 0,
+                     &pal_8bpp)) {
     return nullptr;
   }
-  if (pal_8bpp)
-    pClone->SetPalette(pal_8bpp.get());
+  if (!pal_8bpp.empty())
+    pClone->SetPalette(pal_8bpp);
 
   return pClone;
 }
@@ -1117,28 +973,31 @@
     return nullptr;
 
   auto pTransBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  int result_height = dest_clip.Height();
-  int result_width = dest_clip.Width();
+  const int result_height = dest_clip.Height();
+  const int result_width = dest_clip.Width();
   if (!pTransBitmap->Create(result_width, result_height, GetFormat()))
     return nullptr;
 
-  pTransBitmap->SetPalette(m_pPalette.get());
-  int dest_pitch = pTransBitmap->GetPitch();
-  uint8_t* dest_buf = pTransBitmap->GetBuffer();
-  int row_start = bXFlip ? m_Height - dest_clip.right : dest_clip.left;
-  int row_end = bXFlip ? m_Height - dest_clip.left : dest_clip.right;
-  int col_start = bYFlip ? m_Width - dest_clip.bottom : dest_clip.top;
-  int col_end = bYFlip ? m_Width - dest_clip.top : dest_clip.bottom;
+  pTransBitmap->SetPalette(GetPaletteSpan());
+  const int dest_pitch = pTransBitmap->GetPitch();
+  pdfium::span<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;
+  const int row_end = bXFlip ? m_Height - dest_clip.left : dest_clip.right;
+  const int col_start = bYFlip ? m_Width - dest_clip.bottom : dest_clip.top;
+  const int col_end = bYFlip ? m_Width - dest_clip.top : dest_clip.bottom;
   if (GetBPP() == 1) {
-    memset(dest_buf, 0xff, dest_pitch * result_height);
+    fxcrt::spanset(dest_span, 0xff);
+    if (bYFlip)
+      dest_span = dest_span.subspan(dest_last_row_offset);
+    const int dest_step = bYFlip ? -dest_pitch : dest_pitch;
     for (int row = row_start; row < row_end; ++row) {
-      const uint8_t* src_scan = GetScanline(row);
+      const uint8_t* src_scan = GetScanline(row).data();
       int dest_col = (bXFlip ? dest_clip.right - (row - row_start) - 1 : row) -
                      dest_clip.left;
-      uint8_t* dest_scan = dest_buf;
-      if (bYFlip)
-        dest_scan += (result_height - 1) * dest_pitch;
-      int dest_step = bYFlip ? -dest_pitch : dest_pitch;
+      uint8_t* dest_scan = dest_span.data();
       for (int col = col_start; col < col_end; ++col) {
         if (!(src_scan[col / 8] & (1 << (7 - col % 8))))
           dest_scan[dest_col / 8] &= ~(1 << (7 - dest_col % 8));
@@ -1150,22 +1009,25 @@
     int dest_step = bYFlip ? -dest_pitch : dest_pitch;
     if (nBytes == 3)
       dest_step -= 2;
+    if (bYFlip)
+      dest_span = dest_span.subspan(dest_last_row_offset);
     for (int row = row_start; row < row_end; ++row) {
       int dest_col = (bXFlip ? dest_clip.right - (row - row_start) - 1 : row) -
                      dest_clip.left;
-      uint8_t* dest_scan = dest_buf + dest_col * nBytes;
-      if (bYFlip)
-        dest_scan += (result_height - 1) * dest_pitch;
+      size_t dest_offset = Fx2DSizeOrDie(dest_col, nBytes);
+      uint8_t* dest_scan = dest_span.subspan(dest_offset).data();
       if (nBytes == 4) {
         const uint32_t* src_scan =
-            reinterpret_cast<const uint32_t*>(GetScanline(row)) + col_start;
+            reinterpret_cast<const uint32_t*>(GetScanline(row).data()) +
+            col_start;
         for (int col = col_start; col < col_end; ++col) {
           uint32_t* dest_scan32 = reinterpret_cast<uint32_t*>(dest_scan);
           *dest_scan32 = *src_scan++;
           dest_scan += dest_step;
         }
       } else {
-        const uint8_t* src_scan = GetScanline(row) + col_start * nBytes;
+        const uint8_t* src_scan =
+            GetScanline(row).subspan(col_start * nBytes).data();
         if (nBytes == 1) {
           for (int col = col_start; col < col_end; ++col) {
             *dest_scan = *src_scan++;
@@ -1173,39 +1035,21 @@
           }
         } else {
           for (int col = col_start; col < col_end; ++col) {
-            *dest_scan++ = *src_scan++;
-            *dest_scan++ = *src_scan++;
-            *dest_scan = *src_scan++;
-            dest_scan += dest_step;
+            memcpy(dest_scan, src_scan, 3);
+            dest_scan += 2 + dest_step;
+            src_scan += 3;
           }
         }
       }
     }
   }
-  if (m_pAlphaMask) {
-    dest_pitch = pTransBitmap->m_pAlphaMask->GetPitch();
-    dest_buf = pTransBitmap->m_pAlphaMask->GetBuffer();
-    int dest_step = bYFlip ? -dest_pitch : dest_pitch;
-    for (int row = row_start; row < row_end; ++row) {
-      int dest_col = (bXFlip ? dest_clip.right - (row - row_start) - 1 : row) -
-                     dest_clip.left;
-      uint8_t* dest_scan = dest_buf + dest_col;
-      if (bYFlip)
-        dest_scan += (result_height - 1) * dest_pitch;
-      const uint8_t* src_scan = m_pAlphaMask->GetScanline(row) + col_start;
-      for (int col = col_start; col < col_end; ++col) {
-        *dest_scan = *src_scan++;
-        dest_scan += dest_step;
-      }
-    }
-  }
   return pTransBitmap;
 }
 
 RetainPtr<CFX_DIBitmap> CFX_DIBBase::TransformTo(const CFX_Matrix& mtDest,
                                                  int* result_left,
-                                                 int* result_top) {
-  RetainPtr<CFX_DIBBase> holder(this);
+                                                 int* result_top) const {
+  RetainPtr<const CFX_DIBBase> holder(this);
   CFX_ImageTransformer transformer(holder, mtDest, FXDIB_ResampleOptions(),
                                    nullptr);
   transformer.Continue(nullptr);
@@ -1218,8 +1062,8 @@
     int dest_width,
     int dest_height,
     const FXDIB_ResampleOptions& options,
-    const FX_RECT* pClip) {
-  RetainPtr<CFX_DIBBase> holder(this);
+    const FX_RECT* pClip) const {
+  RetainPtr<const CFX_DIBBase> holder(this);
   FX_RECT clip_rect(0, 0, abs(dest_width), abs(dest_height));
   if (pClip)
     clip_rect.Intersect(*pClip);
@@ -1228,7 +1072,7 @@
     return nullptr;
 
   if (dest_width == m_Width && dest_height == m_Height)
-    return Clone(&clip_rect);
+    return ClipTo(clip_rect);
 
   CFX_BitmapStorer storer;
   CFX_ImageStretcher stretcher(&storer, holder, dest_width, dest_height,
@@ -1240,56 +1084,51 @@
 }
 
 // static
-bool CFX_DIBBase::ConvertBuffer(
-    FXDIB_Format dest_format,
-    uint8_t* dest_buf,
-    int dest_pitch,
-    int width,
-    int height,
-    const RetainPtr<CFX_DIBBase>& pSrcBitmap,
-    int src_left,
-    int src_top,
-    std::unique_ptr<uint32_t, FxFreeDeleter>* p_pal) {
+bool CFX_DIBBase::ConvertBuffer(FXDIB_Format dest_format,
+                                pdfium::span<uint8_t> dest_buf,
+                                int dest_pitch,
+                                int width,
+                                int height,
+                                const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
+                                int src_left,
+                                int src_top,
+                                DataVector<uint32_t>* pal) {
   FXDIB_Format src_format = pSrcBitmap->GetFormat();
   const int bpp = GetBppFromFormat(src_format);
   switch (dest_format) {
-    case FXDIB_8bppMask: {
+    case FXDIB_Format::k8bppMask: {
       return ConvertBuffer_8bppMask(bpp, dest_buf, dest_pitch, width, height,
                                     pSrcBitmap, src_left, src_top);
     }
-    case FXDIB_8bppRgb:
-    case FXDIB_8bppRgba: {
+    case FXDIB_Format::k8bppRgb: {
       const bool bpp_1_or_8 = (bpp == 1 || bpp == 8);
-      if (bpp_1_or_8 && !pSrcBitmap->GetPalette()) {
-        return ConvertBuffer(FXDIB_8bppMask, dest_buf, dest_pitch, width,
-                             height, pSrcBitmap, src_left, src_top, p_pal);
+      if (bpp_1_or_8 && !pSrcBitmap->HasPalette()) {
+        return ConvertBuffer(FXDIB_Format::k8bppMask, dest_buf, dest_pitch,
+                             width, height, pSrcBitmap, src_left, src_top, pal);
       }
-      p_pal->reset(FX_Alloc(uint32_t, 256));
-      if (bpp_1_or_8 && pSrcBitmap->GetPalette()) {
+      pal->resize(256);
+      if (bpp_1_or_8 && pSrcBitmap->HasPalette()) {
         ConvertBuffer_Plt2PltRgb8(dest_buf, dest_pitch, width, height,
-                                  pSrcBitmap, src_left, src_top, p_pal->get());
+                                  pSrcBitmap, src_left, src_top, *pal);
         return true;
       }
       if (bpp >= 24) {
         ConvertBuffer_Rgb2PltRgb8(dest_buf, dest_pitch, width, height,
-                                  pSrcBitmap, src_left, src_top, p_pal->get());
+                                  pSrcBitmap, src_left, src_top, *pal);
         return true;
       }
       return false;
     }
-    case FXDIB_Rgb:
-    case FXDIB_Rgba: {
+    case FXDIB_Format::kRgb: {
       return ConvertBuffer_Rgb(bpp, dest_format, dest_buf, dest_pitch, width,
                                height, pSrcBitmap, src_left, src_top);
     }
-    case FXDIB_Argb:
-    case FXDIB_Rgb32: {
-      return ConvertBuffer_Argb(bpp, GetIsCmykFromFormat(src_format),
-                                dest_format, dest_buf, dest_pitch, width,
+    case FXDIB_Format::kArgb:
+    case FXDIB_Format::kRgb32: {
+      return ConvertBuffer_Argb(bpp, dest_format, dest_buf, dest_pitch, width,
                                 height, pSrcBitmap, src_left, src_top);
     }
     default:
-      NOTREACHED();
-      return false;
+      NOTREACHED_NORETURN();
   }
 }
diff --git a/core/fxge/dib/cfx_dibbase.h b/core/fxge/dib/cfx_dibbase.h
index 15d37b7..ebf1eac 100644
--- a/core/fxge/dib/cfx_dibbase.h
+++ b/core/fxge/dib/cfx_dibbase.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,88 +7,78 @@
 #ifndef CORE_FXGE_DIB_CFX_DIBBASE_H_
 #define CORE_FXGE_DIB_CFX_DIBBASE_H_
 
-#include <memory>
+#include <stdint.h>
 
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/containers/span.h"
 
-enum FXDIB_Channel {
-  FXDIB_Red = 1,
-  FXDIB_Green,
-  FXDIB_Blue,
-  FXDIB_Cyan,
-  FXDIB_Magenta,
-  FXDIB_Yellow,
-  FXDIB_Black,
-  FXDIB_Alpha
-};
+#if defined(_SKIA_SUPPORT_)
+#include "third_party/skia/include/core/SkRefCnt.h"  // nogncheck
+#endif
 
 class CFX_ClipRgn;
 class CFX_DIBitmap;
+class CFX_Matrix;
 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:
-  ~CFX_DIBBase() override;
+#if BUILDFLAG(IS_APPLE)
+  // Matches Apple's kCGBitmapByteOrder32Little in fx_quartz_device.cpp.
+  static constexpr FXDIB_Format kPlatformRGBFormat = FXDIB_Format::kRgb32;
+#else   // BUILDFLAG(IS_APPLE)
+  static constexpr FXDIB_Format kPlatformRGBFormat = FXDIB_Format::kRgb;
+#endif  // BUILDFLAG(IS_APPLE)
 
-  virtual uint8_t* GetBuffer() const;
-  virtual const uint8_t* GetScanline(int line) const = 0;
+  static constexpr uint32_t kPaletteSize = 256;
+
+  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 void DownSampleScanline(int line,
-                                  uint8_t* dest_scan,
-                                  int dest_bpp,
-                                  int dest_width,
-                                  bool bFlipX,
-                                  int clip_left,
-                                  int clip_width) const = 0;
+  virtual size_t GetEstimatedImageMemoryBurden() const;
 
-  uint8_t* GetWritableScanline(int line) {
-    return const_cast<uint8_t*>(GetScanline(line));
-  }
   int GetWidth() const { return m_Width; }
   int GetHeight() const { return m_Height; }
-
-  FXDIB_Format GetFormat() const {
-    return static_cast<FXDIB_Format>(m_AlphaFlag * 0x100 + m_bpp);
-  }
   uint32_t GetPitch() const { return m_Pitch; }
-  uint32_t* GetPalette() const { return m_pPalette.get(); }
-  int GetBPP() const { return m_bpp; }
 
-  bool IsAlphaMask() const { return !!(m_AlphaFlag & 1); }
-  bool HasAlpha() const { return !!(m_AlphaFlag & 2); }
-  bool IsCmykImage() const { return !!(m_AlphaFlag & 4); }
-  bool IsOpaqueImage() const { return !IsAlphaMask() && !HasAlpha(); }
+  FXDIB_Format GetFormat() const { return m_Format; }
+  int GetBPP() const { return GetBppFromFormat(m_Format); }
+  bool IsMaskFormat() const { return GetIsMaskFromFormat(m_Format); }
+  bool IsAlphaFormat() const { return m_Format == FXDIB_Format::kArgb; }
+  bool IsOpaqueImage() const { return !IsMaskFormat() && !IsAlphaFormat(); }
 
-  size_t GetPaletteSize() const;
-
+  bool HasPalette() const { return !m_palette.empty(); }
+  pdfium::span<const uint32_t> GetPaletteSpan() const { return m_palette; }
+  size_t GetRequiredPaletteSize() const;
   uint32_t GetPaletteArgb(int index) const;
   void SetPaletteArgb(int index, uint32_t color);
 
   // Copies into internally-owned palette.
-  void SetPalette(const uint32_t* pSrcPal);
+  void SetPalette(pdfium::span<const uint32_t> src_palette);
 
-  RetainPtr<CFX_DIBitmap> Clone(const FX_RECT* pClip) const;
-  RetainPtr<CFX_DIBitmap> CloneConvert(FXDIB_Format format);
+  RetainPtr<CFX_DIBitmap> Realize() const;
+  RetainPtr<CFX_DIBitmap> ClipTo(const FX_RECT& rect) const;
+  RetainPtr<CFX_DIBitmap> ConvertTo(FXDIB_Format format) const;
   RetainPtr<CFX_DIBitmap> StretchTo(int dest_width,
                                     int dest_height,
                                     const FXDIB_ResampleOptions& options,
-                                    const FX_RECT* pClip);
+                                    const FX_RECT* pClip) const;
   RetainPtr<CFX_DIBitmap> TransformTo(const CFX_Matrix& mtDest,
                                       int* left,
-                                      int* top);
+                                      int* top) const;
   RetainPtr<CFX_DIBitmap> SwapXY(bool bXFlip, bool bYFlip) const;
   RetainPtr<CFX_DIBitmap> FlipImage(bool bXFlip, bool bYFlip) const;
 
   RetainPtr<CFX_DIBitmap> CloneAlphaMask() const;
 
-  // Copies into internally-owned mask.
-  bool SetAlphaMask(const RetainPtr<CFX_DIBBase>& pAlphaMask,
-                    const FX_RECT* pClip);
-
   bool GetOverlapRect(int& dest_left,
                       int& dest_top,
                       int& width,
@@ -97,39 +87,44 @@
                       int src_height,
                       int& src_left,
                       int& src_top,
-                      const CFX_ClipRgn* pClipRgn);
+                      const CFX_ClipRgn* pClipRgn) const;
 
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
-  void DebugVerifyBitmapIsPreMultiplied(void* buffer) const;
-#endif
-
-  RetainPtr<CFX_DIBitmap> m_pAlphaMask;
+#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,
-                            uint8_t* dest_buf,
+                            pdfium::span<uint8_t> dest_buf,
                             int dest_pitch,
                             int width,
                             int height,
-                            const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                            const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
                             int src_left,
                             int src_top,
-                            std::unique_ptr<uint32_t, FxFreeDeleter>* pal);
+                            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();
-  bool BuildAlphaMask();
   int FindPalette(uint32_t color) const;
-  void GetPalette(uint32_t* pal, int alpha) const;
 
-  int m_Width;
-  int m_Height;
-  int m_bpp;
-  uint32_t m_AlphaFlag;
-  uint32_t m_Pitch;
-  // TODO(weili): Use std::vector for this.
-  std::unique_ptr<uint32_t, FxFreeDeleter> m_pPalette;
+  FXDIB_Format m_Format = FXDIB_Format::kInvalid;
+  int m_Width = 0;
+  int m_Height = 0;
+  uint32_t m_Pitch = 0;
+  DataVector<uint32_t> m_palette;
 };
 
 #endif  // CORE_FXGE_DIB_CFX_DIBBASE_H_
diff --git a/core/fxge/dib/cfx_dibbase_unittest.cpp b/core/fxge/dib/cfx_dibbase_unittest.cpp
new file mode 100644
index 0000000..ac05c09
--- /dev/null
+++ b/core/fxge/dib/cfx_dibbase_unittest.cpp
@@ -0,0 +1,163 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/dib/cfx_dibbase.h"
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+struct Input {
+  CFX_Point src_top_left;
+  CFX_Size src_size;
+  CFX_Point dest_top_left;
+  CFX_Size overlap_size;
+};
+
+struct Output {
+  CFX_Point src_top_left;
+  CFX_Point dest_top_left;
+  CFX_Size overlap_size;
+};
+
+void RunOverlapRectTest(const CFX_DIBitmap* bitmap,
+                        const Input& input,
+                        const Output* expected_output) {
+  // Initialize in-out parameters.
+  int src_left = input.src_top_left.x;
+  int src_top = input.src_top_left.y;
+  int dest_left = input.dest_top_left.x;
+  int dest_top = input.dest_top_left.y;
+  int overlap_width = input.overlap_size.width;
+  int overlap_height = input.overlap_size.height;
+
+  bool success = bitmap->GetOverlapRect(
+      dest_left, dest_top, overlap_width, overlap_height, input.src_size.width,
+      input.src_size.height, src_left, src_top,
+      /*pClipRgn=*/nullptr);
+  if (success == !expected_output) {
+    ADD_FAILURE();
+    return;
+  }
+
+  if (expected_output) {
+    EXPECT_EQ(expected_output->src_top_left.x, src_left);
+    EXPECT_EQ(expected_output->src_top_left.y, src_top);
+    EXPECT_EQ(expected_output->dest_top_left.x, dest_left);
+    EXPECT_EQ(expected_output->dest_top_left.y, dest_top);
+    EXPECT_EQ(expected_output->overlap_size.width, overlap_width);
+    EXPECT_EQ(expected_output->overlap_size.height, overlap_height);
+  }
+}
+
+}  // namespace
+
+TEST(CFX_DIBBaseTest, GetOverlapRectTrivialOverlap) {
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  EXPECT_TRUE(bitmap->Create(400, 300, FXDIB_Format::k1bppRgb));
+
+  const Input kInput = {/*src_top_left=*/{0, 0}, /*src_size=*/{400, 300},
+                        /*dest_top_left=*/{0, 0},
+                        /*overlap_size=*/{400, 300}};
+  const Output kExpectedOutput = {/*src_top_left=*/{0, 0},
+                                  /*dest_top_left=*/{0, 0},
+                                  /*overlap_size=*/{400, 300}};
+  RunOverlapRectTest(bitmap.Get(), kInput, &kExpectedOutput);
+}
+
+TEST(CFX_DIBBaseTest, GetOverlapRectOverlapNoLimit) {
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  EXPECT_TRUE(bitmap->Create(400, 300, FXDIB_Format::k1bppRgb));
+
+  const Input kInput = {/*src_top_left=*/{35, 41}, /*src_size=*/{400, 300},
+                        /*dest_top_left=*/{123, 137},
+                        /*overlap_size=*/{200, 100}};
+  const Output kExpectedOutput = {/*src_top_left=*/{35, 41},
+                                  /*dest_top_left=*/{123, 137},
+                                  /*overlap_size=*/{200, 100}};
+  RunOverlapRectTest(bitmap.Get(), kInput, &kExpectedOutput);
+}
+
+TEST(CFX_DIBBaseTest, GetOverlapRectOverlapLimitedBySource) {
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  EXPECT_TRUE(bitmap->Create(400, 300, FXDIB_Format::k1bppRgb));
+
+  const Input kInput = {/*src_top_left=*/{141, 154}, /*src_size=*/{400, 300},
+                        /*dest_top_left=*/{35, 41},
+                        /*overlap_size=*/{270, 160}};
+  const Output kExpectedOutput = {/*src_top_left=*/{141, 154},
+                                  /*dest_top_left=*/{35, 41},
+                                  /*overlap_size=*/{259, 146}};
+  RunOverlapRectTest(bitmap.Get(), kInput, &kExpectedOutput);
+}
+
+TEST(CFX_DIBBaseTest, GetOverlapRectOverlapLimitedByDestination) {
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  EXPECT_TRUE(bitmap->Create(400, 300, FXDIB_Format::k1bppRgb));
+
+  const Input kInput = {/*src_top_left=*/{35, 41}, /*src_size=*/{400, 300},
+                        /*dest_top_left=*/{123, 137},
+                        /*overlap_size=*/{280, 170}};
+  const Output kExpectedOutput = {/*src_top_left=*/{35, 41},
+                                  /*dest_top_left=*/{123, 137},
+                                  /*overlap_size=*/{277, 163}};
+  RunOverlapRectTest(bitmap.Get(), kInput, &kExpectedOutput);
+}
+
+TEST(CFX_DIBBaseTest, GetOverlapRectBadInputs) {
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  EXPECT_TRUE(bitmap->Create(400, 300, FXDIB_Format::k1bppRgb));
+
+  const Input kEmptyInputs[] = {
+      // Empty source rect.
+      {/*src_top_left=*/{0, 0}, /*src_size=*/{0, 0},
+       /*dest_top_left=*/{0, 0},
+       /*overlap_size=*/{400, 300}},
+      // Empty overlap size.
+      {/*src_top_left=*/{0, 0}, /*src_size=*/{400, 300},
+       /*dest_top_left=*/{0, 0},
+       /*overlap_size=*/{0, 0}},
+      // Source out of bounds on x-axis.
+      {/*src_top_left=*/{-400, 0}, /*src_size=*/{400, 300},
+       /*dest_top_left=*/{0, 0},
+       /*overlap_size=*/{400, 300}},
+  };
+  for (const Input& input : kEmptyInputs)
+    RunOverlapRectTest(bitmap.Get(), input, /*expected_output=*/nullptr);
+
+  const Input kOutOfBoundInputs[] = {
+      // Source out of bounds on x-axis.
+      {/*src_top_left=*/{400, 0}, /*src_size=*/{400, 300},
+       /*dest_top_left=*/{0, 0},
+       /*overlap_size=*/{400, 300}},
+      // Source out of bounds on y-axis.
+      {/*src_top_left=*/{0, 300}, /*src_size=*/{400, 300},
+       /*dest_top_left=*/{0, 0},
+       /*overlap_size=*/{400, 300}},
+      // Source out of bounds on y-axis.
+      {/*src_top_left=*/{0, -300}, /*src_size=*/{400, 300},
+       /*dest_top_left=*/{0, 0},
+       /*overlap_size=*/{400, 300}},
+      // Destination out of bounds on x-axis.
+      {/*src_top_left=*/{0, 0}, /*src_size=*/{400, 300},
+       /*dest_top_left=*/{-400, 0},
+       /*overlap_size=*/{400, 300}},
+      // Destination out of bounds on x-axis.
+      {/*src_top_left=*/{0, 0}, /*src_size=*/{400, 300},
+       /*dest_top_left=*/{400, 0},
+       /*overlap_size=*/{400, 300}},
+      // Destination out of bounds on y-axis.
+      {/*src_top_left=*/{0, 0}, /*src_size=*/{400, 300},
+       /*dest_top_left=*/{0, -300},
+       /*overlap_size=*/{400, 300}},
+      // Destination out of bounds on y-axis.
+      {/*src_top_left=*/{0, 0}, /*src_size=*/{400, 300},
+       /*dest_top_left=*/{0, 300},
+       /*overlap_size=*/{400, 300}},
+  };
+  for (const Input& input : kOutOfBoundInputs)
+    RunOverlapRectTest(bitmap.Get(), input, /*expected_output=*/nullptr);
+}
diff --git a/core/fxge/dib/cfx_dibextractor.cpp b/core/fxge/dib/cfx_dibextractor.cpp
deleted file mode 100644
index f6f31e8..0000000
--- a/core/fxge/dib/cfx_dibextractor.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/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()) {
-    m_pBitmap = pSrc->Clone(nullptr);
-    return;
-  }
-  RetainPtr<CFX_DIBBase> pOldSrc(pSrc);
-  m_pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!m_pBitmap->Create(pOldSrc->GetWidth(), pOldSrc->GetHeight(),
-                         pOldSrc->GetFormat(), pOldSrc->GetBuffer(), 0)) {
-    m_pBitmap.Reset();
-    return;
-  }
-  m_pBitmap->SetPalette(pOldSrc->GetPalette());
-  m_pBitmap->SetAlphaMask(pOldSrc->m_pAlphaMask, nullptr);
-}
-
-CFX_DIBExtractor::~CFX_DIBExtractor() {}
diff --git a/core/fxge/dib/cfx_dibextractor.h b/core/fxge/dib/cfx_dibextractor.h
deleted file mode 100644
index eff96b1..0000000
--- a/core/fxge/dib/cfx_dibextractor.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_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 c5ee2c5..6df1eee 100644
--- a/core/fxge/dib/cfx_dibitmap.cpp
+++ b/core/fxge/dib/cfx_dibitmap.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,28 +6,28 @@
 
 #include "core/fxge/dib/cfx_dibitmap.h"
 
+#include <stdint.h>
+#include <string.h>
+
 #include <limits>
 #include <memory>
 #include <utility>
 
 #include "build/build_config.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxge/calculate_pitch.h"
 #include "core/fxge/cfx_cliprgn.h"
-#include "core/fxge/dib/cfx_cmyk_to_srgb.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/dib/cfx_scanlinecompositor.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/notreached.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
-namespace {
-
-constexpr size_t kMaxOOMLimit = 12000000;
-constexpr int8_t kChannelOffset[] = {0, 2, 1, 0, 0, 1, 2, 3, 3};
-
-}  // namespace
-
-CFX_DIBitmap::CFX_DIBitmap() {
-  m_pPalette = nullptr;
-#ifdef _SKIA_SUPPORT_PATHS_
-  m_nFormat = Format::kCleared;
-#endif
-}
+CFX_DIBitmap::CFX_DIBitmap() = default;
 
 bool CFX_DIBitmap::Create(int width, int height, FXDIB_Format format) {
   return Create(width, height, format, nullptr, 0);
@@ -39,47 +39,33 @@
                           uint8_t* pBuffer,
                           uint32_t pitch) {
   m_pBuffer = nullptr;
-  m_bpp = GetBppFromFormat(format);
-  m_AlphaFlag = GetAlphaFlagFromFormat(format);
+  m_Format = format;
   m_Width = 0;
   m_Height = 0;
   m_Pitch = 0;
 
-  uint32_t calculatedSize;
-  if (!CalculatePitchAndSize(height, width, format, &pitch, &calculatedSize))
+  absl::optional<PitchAndSize> pitch_size =
+      CalculatePitchAndSize(width, height, format, pitch);
+  if (!pitch_size.has_value())
     return false;
 
   if (pBuffer) {
     m_pBuffer.Reset(pBuffer);
   } else {
-    size_t bufferSize = calculatedSize + 4;
-    if (bufferSize >= kMaxOOMLimit) {
-      m_pBuffer = std::unique_ptr<uint8_t, FxFreeDeleter>(
-          FX_TryAlloc(uint8_t, bufferSize));
-      if (!m_pBuffer)
-        return false;
-    } else {
-      m_pBuffer = std::unique_ptr<uint8_t, FxFreeDeleter>(
-          FX_Alloc(uint8_t, bufferSize));
-    }
+    FX_SAFE_SIZE_T safe_buffer_size = pitch_size.value().size;
+    safe_buffer_size += 4;
+    if (!safe_buffer_size.IsValid())
+      return false;
+
+    m_pBuffer = std::unique_ptr<uint8_t, FxFreeDeleter>(
+        FX_TryAlloc(uint8_t, safe_buffer_size.ValueOrDie()));
+    if (!m_pBuffer)
+      return false;
   }
   m_Width = width;
   m_Height = height;
-  m_Pitch = pitch;
-  if (!HasAlpha() || format == FXDIB_Argb)
-    return true;
-
-  if (BuildAlphaMask())
-    return true;
-
-  if (pBuffer)
-    return true;
-
-  m_pBuffer = nullptr;
-  m_Width = 0;
-  m_Height = 0;
-  m_Pitch = 0;
-  return false;
+  m_Pitch = pitch_size.value().pitch;
+  return true;
 }
 
 bool CFX_DIBitmap::Copy(const RetainPtr<CFX_DIBBase>& pSrc) {
@@ -89,31 +75,46 @@
   if (!Create(pSrc->GetWidth(), pSrc->GetHeight(), pSrc->GetFormat()))
     return false;
 
-  SetPalette(pSrc->GetPalette());
-  SetAlphaMask(pSrc->m_pAlphaMask, nullptr);
-  for (int row = 0; row < pSrc->GetHeight(); row++)
-    memcpy(m_pBuffer.Get() + row * m_Pitch, pSrc->GetScanline(row), m_Pitch);
+  SetPalette(pSrc->GetPaletteSpan());
+  for (int row = 0; row < pSrc->GetHeight(); row++) {
+    memcpy(m_pBuffer.Get() + row * m_Pitch, pSrc->GetScanline(row).data(),
+           m_Pitch);
+  }
   return true;
 }
 
-CFX_DIBitmap::~CFX_DIBitmap() {}
+CFX_DIBitmap::~CFX_DIBitmap() = default;
 
-uint8_t* CFX_DIBitmap::GetBuffer() const {
-  return m_pBuffer.Get();
+pdfium::span<const uint8_t> CFX_DIBitmap::GetBuffer() const {
+  if (!m_pBuffer)
+    return pdfium::span<const uint8_t>();
+
+  return {m_pBuffer.Get(), m_Height * m_Pitch};
 }
 
-const uint8_t* CFX_DIBitmap::GetScanline(int line) const {
-  return m_pBuffer.Get() ? m_pBuffer.Get() + line * m_Pitch : nullptr;
+pdfium::span<const uint8_t> CFX_DIBitmap::GetScanline(int line) const {
+  auto buffer_span = GetBuffer();
+  if (buffer_span.empty())
+    return pdfium::span<const uint8_t>();
+
+  return buffer_span.subspan(line * m_Pitch, m_Pitch);
+}
+
+size_t CFX_DIBitmap::GetEstimatedImageMemoryBurden() const {
+  size_t result = CFX_DIBBase::GetEstimatedImageMemoryBurden();
+  if (!GetBuffer().empty()) {
+    int height = GetHeight();
+    CHECK(pdfium::base::IsValueInRangeForNumericType<size_t>(height));
+    result += static_cast<size_t>(height) * GetPitch();
+  }
+  return result;
 }
 
 void CFX_DIBitmap::TakeOver(RetainPtr<CFX_DIBitmap>&& pSrcBitmap) {
   m_pBuffer = std::move(pSrcBitmap->m_pBuffer);
-  m_pPalette = std::move(pSrcBitmap->m_pPalette);
-  m_pAlphaMask = pSrcBitmap->m_pAlphaMask;
+  m_palette = std::move(pSrcBitmap->m_palette);
   pSrcBitmap->m_pBuffer = nullptr;
-  pSrcBitmap->m_pAlphaMask = nullptr;
-  m_bpp = pSrcBitmap->m_bpp;
-  m_AlphaFlag = pSrcBitmap->m_AlphaFlag;
+  m_Format = pSrcBitmap->m_Format;
   m_Width = pSrcBitmap->m_Width;
   m_Height = pSrcBitmap->m_Height;
   m_Pitch = pSrcBitmap->m_Pitch;
@@ -125,24 +126,23 @@
 
   uint8_t* pBuffer = m_pBuffer.Get();
   switch (GetFormat()) {
-    case FXDIB_1bppMask:
+    case FXDIB_Format::k1bppMask:
       memset(pBuffer, (color & 0xff000000) ? 0xff : 0, m_Pitch * m_Height);
       break;
-    case FXDIB_1bppRgb: {
+    case FXDIB_Format::k1bppRgb: {
       int index = FindPalette(color);
       memset(pBuffer, index ? 0xff : 0, m_Pitch * m_Height);
       break;
     }
-    case FXDIB_8bppMask:
+    case FXDIB_Format::k8bppMask:
       memset(pBuffer, color >> 24, m_Pitch * m_Height);
       break;
-    case FXDIB_8bppRgb: {
+    case FXDIB_Format::k8bppRgb: {
       int index = FindPalette(color);
       memset(pBuffer, index, m_Pitch * m_Height);
       break;
     }
-    case FXDIB_Rgb:
-    case FXDIB_Rgba: {
+    case FXDIB_Format::kRgb: {
       int a;
       int r;
       int g;
@@ -163,13 +163,14 @@
       }
       break;
     }
-    case FXDIB_Rgb32:
-    case FXDIB_Argb: {
-      color = IsCmykImage() ? FXCMYK_TODIB(color) : FXARGB_TODIB(color);
-#ifdef _SKIA_SUPPORT_
-      if (FXDIB_Rgb32 == GetFormat() && !IsCmykImage())
+    case FXDIB_Format::kRgb32:
+    case FXDIB_Format::kArgb: {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer() &&
+          FXDIB_Format::kRgb32 == GetFormat()) {
+        // TODO(crbug.com/pdfium/2016): This is not reliable because alpha may
+        // be modified outside of this operation.
         color |= 0xFF000000;
-#endif
+      }
       for (int i = 0; i < m_Width; i++)
         reinterpret_cast<uint32_t*>(pBuffer)[i] = color;
       for (int row = 1; row < m_Height; row++)
@@ -224,20 +225,23 @@
     const RetainPtr<CFX_DIBBase>& pSrcBitmap,
     int src_left,
     int src_top) {
-  if (m_pPalette)
+  if (HasPalette())
     return false;
 
-  if (m_bpp == 8)
-    dest_format = FXDIB_8bppMask;
+  if (GetBppFromFormat(m_Format) == 8)
+    dest_format = FXDIB_Format::k8bppMask;
 
-  uint8_t* dest_buf =
-      m_pBuffer.Get() + dest_top * m_Pitch + dest_left * GetBPP() / 8;
-  std::unique_ptr<uint32_t, FxFreeDeleter> d_plt;
-  if (!ConvertBuffer(dest_format, dest_buf, m_Pitch, width, height, pSrcBitmap,
-                     src_left, src_top, &d_plt)) {
+  FX_SAFE_UINT32 offset = dest_left;
+  offset *= GetBPP();
+  offset /= 8;
+  if (!offset.IsValid())
     return false;
-  }
-  return true;
+
+  pdfium::span<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,
+                       pSrcBitmap, src_left, src_top, &d_plt);
 }
 
 void CFX_DIBitmap::TransferWithMultipleBPP(
@@ -253,7 +257,7 @@
     uint8_t* dest_scan =
         m_pBuffer.Get() + (dest_top + row) * m_Pitch + dest_left * Bpp;
     const uint8_t* src_scan =
-        pSrcBitmap->GetScanline(src_top + row) + src_left * Bpp;
+        pSrcBitmap->GetScanline(src_top + row).subspan(src_left * Bpp).data();
     memcpy(dest_scan, src_scan, width * Bpp);
   }
 }
@@ -268,7 +272,7 @@
     int src_top) {
   for (int row = 0; row < height; ++row) {
     uint8_t* dest_scan = m_pBuffer.Get() + (dest_top + row) * m_Pitch;
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
+    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row).data();
     for (int col = 0; col < width; ++col) {
       int src_idx = src_left + col;
       int dest_idx = dest_left + col;
@@ -280,69 +284,50 @@
   }
 }
 
-bool CFX_DIBitmap::LoadChannelFromAlpha(
-    FXDIB_Channel destChannel,
+bool CFX_DIBitmap::SetChannelFromBitmap(
+    Channel destChannel,
     const RetainPtr<CFX_DIBBase>& pSrcBitmap) {
   if (!m_pBuffer)
     return false;
 
   RetainPtr<CFX_DIBBase> pSrcClone = pSrcBitmap;
-  if (!pSrcBitmap->HasAlpha() && !pSrcBitmap->IsAlphaMask())
+  if (!pSrcBitmap->IsAlphaFormat() && !pSrcBitmap->IsMaskFormat())
     return false;
 
   if (pSrcBitmap->GetBPP() == 1) {
-    pSrcClone = pSrcBitmap->CloneConvert(FXDIB_8bppMask);
+    pSrcClone = pSrcBitmap->ConvertTo(FXDIB_Format::k8bppMask);
     if (!pSrcClone)
       return false;
   }
-  int srcOffset = pSrcBitmap->GetFormat() == FXDIB_Argb ? 3 : 0;
+  const int srcOffset = pSrcBitmap->GetFormat() == FXDIB_Format::kArgb ? 3 : 0;
   int destOffset = 0;
-  if (destChannel == FXDIB_Alpha) {
-    if (IsAlphaMask()) {
-      if (!ConvertFormat(FXDIB_8bppMask))
+  if (destChannel == Channel::kAlpha) {
+    if (IsMaskFormat()) {
+      if (!ConvertFormat(FXDIB_Format::k8bppMask))
         return false;
     } else {
-      if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyka : FXDIB_Argb))
+      if (!ConvertFormat(FXDIB_Format::kArgb))
         return false;
 
-      if (GetFormat() == FXDIB_Argb)
-        destOffset = 3;
+      destOffset = 3;
     }
   } else {
-    if (IsAlphaMask())
+    DCHECK_EQ(destChannel, Channel::kRed);
+    if (IsMaskFormat())
       return false;
 
     if (GetBPP() < 24) {
-      if (HasAlpha()) {
-        if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyka : FXDIB_Argb))
+      if (IsAlphaFormat()) {
+        if (!ConvertFormat(FXDIB_Format::kArgb))
           return false;
       } else {
-#if defined(OS_MACOSX)
-        constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb32;
-#else
-        constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb;
-#endif
-        if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyk : kPlatformFormat))
+        if (!ConvertFormat(kPlatformRGBFormat))
           return false;
       }
     }
-    destOffset = kChannelOffset[destChannel];
+    destOffset = 2;
   }
-  if (pSrcClone->m_pAlphaMask) {
-    RetainPtr<CFX_DIBBase> pAlphaMask = pSrcClone->m_pAlphaMask;
-    if (pSrcClone->GetWidth() != m_Width ||
-        pSrcClone->GetHeight() != m_Height) {
-      if (pAlphaMask) {
-        pAlphaMask = pAlphaMask->StretchTo(m_Width, m_Height,
-                                           FXDIB_ResampleOptions(), nullptr);
-        if (!pAlphaMask)
-          return false;
-      }
-    }
-    pSrcClone = std::move(pAlphaMask);
-    srcOffset = 0;
-  } else if (pSrcClone->GetWidth() != m_Width ||
-             pSrcClone->GetHeight() != m_Height) {
+  if (pSrcClone->GetWidth() != m_Width || pSrcClone->GetHeight() != m_Height) {
     RetainPtr<CFX_DIBitmap> pSrcMatched = pSrcClone->StretchTo(
         m_Width, m_Height, FXDIB_ResampleOptions(), nullptr);
     if (!pSrcMatched)
@@ -351,15 +336,13 @@
     pSrcClone = pSrcMatched;
   }
   RetainPtr<CFX_DIBitmap> pDst(this);
-  if (destChannel == FXDIB_Alpha && m_pAlphaMask) {
-    pDst = m_pAlphaMask;
-    destOffset = 0;
-  }
   int srcBytes = pSrcClone->GetBPP() / 8;
   int destBytes = pDst->GetBPP() / 8;
   for (int row = 0; row < m_Height; row++) {
-    uint8_t* dest_pos = pDst->GetWritableScanline(row) + destOffset;
-    const uint8_t* src_pos = pSrcClone->GetScanline(row) + srcOffset;
+    uint8_t* dest_pos =
+        pDst->GetWritableScanline(row).subspan(destOffset).data();
+    const uint8_t* src_pos =
+        pSrcClone->GetScanline(row).subspan(srcOffset).data();
     for (int col = 0; col < m_Width; col++) {
       *dest_pos = *src_pos;
       dest_pos += destBytes;
@@ -369,61 +352,36 @@
   return true;
 }
 
-bool CFX_DIBitmap::LoadChannel(FXDIB_Channel destChannel, int value) {
+bool CFX_DIBitmap::SetAlphaFromBitmap(
+    const RetainPtr<CFX_DIBBase>& pSrcBitmap) {
+  return SetChannelFromBitmap(Channel::kAlpha, pSrcBitmap);
+}
+
+bool CFX_DIBitmap::SetRedFromBitmap(const RetainPtr<CFX_DIBBase>& pSrcBitmap) {
+  return SetChannelFromBitmap(Channel::kRed, pSrcBitmap);
+}
+
+bool CFX_DIBitmap::SetUniformOpaqueAlpha() {
   if (!m_pBuffer)
     return false;
 
-  int destOffset;
-  if (destChannel == FXDIB_Alpha) {
-    if (IsAlphaMask()) {
-      if (!ConvertFormat(FXDIB_8bppMask)) {
-        return false;
-      }
-      destOffset = 0;
-    } else {
-      destOffset = 0;
-      if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyka : FXDIB_Argb)) {
-        return false;
-      }
-      if (GetFormat() == FXDIB_Argb) {
-        destOffset = 3;
-      }
-    }
-  } else {
-    if (IsAlphaMask()) {
+  if (IsMaskFormat()) {
+    if (!ConvertFormat(FXDIB_Format::k8bppMask))
       return false;
-    }
-    if (GetBPP() < 24) {
-      if (HasAlpha()) {
-        if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyka : FXDIB_Argb)) {
-          return false;
-        }
-      } else {
-#if defined(OS_MACOSX)
-        constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb;
-#else
-        constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb32;
-#endif
-        if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyk : kPlatformFormat))
-          return false;
-      }
-    }
-    destOffset = kChannelOffset[destChannel];
+  } else {
+    if (!ConvertFormat(FXDIB_Format::kArgb))
+      return false;
   }
-  int Bpp = GetBPP() / 8;
+  const int Bpp = GetBPP() / 8;
   if (Bpp == 1) {
-    memset(m_pBuffer.Get(), value, m_Height * m_Pitch);
+    memset(m_pBuffer.Get(), 0xff, m_Height * m_Pitch);
     return true;
   }
-  if (destChannel == FXDIB_Alpha && m_pAlphaMask) {
-    memset(m_pAlphaMask->GetBuffer(), value,
-           m_pAlphaMask->GetHeight() * m_pAlphaMask->GetPitch());
-    return true;
-  }
+  const int destOffset = GetFormat() == FXDIB_Format::kArgb ? 3 : 0;
   for (int row = 0; row < m_Height; row++) {
     uint8_t* scan_line = m_pBuffer.Get() + row * m_Pitch + destOffset;
     for (int col = 0; col < m_Width; col++) {
-      *scan_line = value;
+      *scan_line = 0xff;
       scan_line += Bpp;
     }
   }
@@ -431,16 +389,14 @@
 }
 
 bool CFX_DIBitmap::MultiplyAlpha(const RetainPtr<CFX_DIBBase>& pSrcBitmap) {
-  if (!m_pBuffer)
-    return false;
+  CHECK(pSrcBitmap->IsMaskFormat());
 
-  if (!pSrcBitmap->IsAlphaMask()) {
-    NOTREACHED();
+  if (!m_pBuffer) {
     return false;
   }
 
-  if (!IsAlphaMask() && !HasAlpha())
-    return LoadChannelFromAlpha(FXDIB_Alpha, pSrcBitmap);
+  if (IsOpaqueImage())
+    return SetAlphaFromBitmap(pSrcBitmap);
 
   RetainPtr<CFX_DIBitmap> pSrcClone = pSrcBitmap.As<CFX_DIBitmap>();
   if (pSrcBitmap->GetWidth() != m_Width ||
@@ -450,8 +406,8 @@
     if (!pSrcClone)
       return false;
   }
-  if (IsAlphaMask()) {
-    if (!ConvertFormat(FXDIB_8bppMask))
+  if (IsMaskFormat()) {
+    if (!ConvertFormat(FXDIB_Format::k8bppMask))
       return false;
 
     for (int row = 0; row < m_Height; row++) {
@@ -469,22 +425,19 @@
         }
       }
     }
-  } else {
-    if (GetFormat() == FXDIB_Argb) {
-      if (pSrcClone->GetBPP() == 1)
-        return false;
+    return true;
+  }
 
-      for (int row = 0; row < m_Height; row++) {
-        uint8_t* dest_scan = m_pBuffer.Get() + m_Pitch * row + 3;
-        uint8_t* src_scan =
-            pSrcClone->m_pBuffer.Get() + pSrcClone->m_Pitch * row;
-        for (int col = 0; col < m_Width; col++) {
-          *dest_scan = (*dest_scan) * src_scan[col] / 255;
-          dest_scan += 4;
-        }
-      }
-    } else {
-      m_pAlphaMask->MultiplyAlpha(pSrcClone);
+  DCHECK_EQ(GetFormat(), FXDIB_Format::kArgb);
+  if (pSrcClone->GetBPP() == 1)
+    return false;
+
+  for (int row = 0; row < m_Height; row++) {
+    uint8_t* dest_scan = m_pBuffer.Get() + m_Pitch * row + 3;
+    uint8_t* src_scan = pSrcClone->m_pBuffer.Get() + pSrcClone->m_Pitch * row;
+    for (int col = 0; col < m_Width; col++) {
+      *dest_scan = (*dest_scan) * src_scan[col] / 255;
+      dest_scan += 4;
     }
   }
   return true;
@@ -495,13 +448,13 @@
     return false;
 
   switch (GetFormat()) {
-    case FXDIB_1bppMask:
-      if (!ConvertFormat(FXDIB_8bppMask)) {
+    case FXDIB_Format::k1bppMask:
+      if (!ConvertFormat(FXDIB_Format::k8bppMask)) {
         return false;
       }
       MultiplyAlpha(alpha);
       break;
-    case FXDIB_8bppMask: {
+    case FXDIB_Format::k8bppMask: {
       for (int row = 0; row < m_Height; row++) {
         uint8_t* scan_line = m_pBuffer.Get() + row * m_Pitch;
         for (int col = 0; col < m_Width; col++) {
@@ -510,7 +463,7 @@
       }
       break;
     }
-    case FXDIB_Argb: {
+    case FXDIB_Format::kArgb: {
       for (int row = 0; row < m_Height; row++) {
         uint8_t* scan_line = m_pBuffer.Get() + row * m_Pitch + 3;
         for (int col = 0; col < m_Width; col++) {
@@ -521,52 +474,50 @@
       break;
     }
     default:
-      if (HasAlpha()) {
-        m_pAlphaMask->MultiplyAlpha(alpha);
-      } else if (IsCmykImage()) {
-        if (!ConvertFormat((FXDIB_Format)(GetFormat() | 0x0200))) {
-          return false;
-        }
-        m_pAlphaMask->MultiplyAlpha(alpha);
-      } else {
-        if (!ConvertFormat(FXDIB_Argb)) {
-          return false;
-        }
-        MultiplyAlpha(alpha);
+      DCHECK(!IsAlphaFormat());
+      if (!ConvertFormat(FXDIB_Format::kArgb)) {
+        return false;
       }
+      MultiplyAlpha(alpha);
       break;
   }
   return true;
 }
 
+#if defined(_SKIA_SUPPORT_)
 uint32_t CFX_DIBitmap::GetPixel(int x, int y) const {
   if (!m_pBuffer)
     return 0;
 
-  uint8_t* pos = m_pBuffer.Get() + y * m_Pitch + x * GetBPP() / 8;
+  FX_SAFE_UINT32 offset = x;
+  offset *= GetBPP();
+  offset /= 8;
+  if (!offset.IsValid())
+    return 0;
+
+  uint8_t* pos = m_pBuffer.Get() + y * m_Pitch + offset.ValueOrDie();
   switch (GetFormat()) {
-    case FXDIB_1bppMask: {
+    case FXDIB_Format::k1bppMask: {
       if ((*pos) & (1 << (7 - x % 8))) {
         return 0xff000000;
       }
       return 0;
     }
-    case FXDIB_1bppRgb: {
+    case FXDIB_Format::k1bppRgb: {
       if ((*pos) & (1 << (7 - x % 8))) {
-        return m_pPalette ? m_pPalette.get()[1] : 0xffffffff;
+        return HasPalette() ? GetPaletteSpan()[1] : 0xffffffff;
       }
-      return m_pPalette ? m_pPalette.get()[0] : 0xff000000;
+      return HasPalette() ? GetPaletteSpan()[0] : 0xff000000;
     }
-    case FXDIB_8bppMask:
+    case FXDIB_Format::k8bppMask:
       return (*pos) << 24;
-    case FXDIB_8bppRgb:
-      return m_pPalette ? m_pPalette.get()[*pos]
-                        : (0xff000000 | ((*pos) * 0x10101));
-    case FXDIB_Rgb:
-    case FXDIB_Rgba:
-    case FXDIB_Rgb32:
+    case FXDIB_Format::k8bppRgb:
+      return HasPalette() ? GetPaletteSpan()[*pos]
+                          : ArgbEncode(0xff, *pos, *pos, *pos);
+    case FXDIB_Format::kRgb:
+    case FXDIB_Format::kRgb32:
       return FXARGB_GETDIB(pos) | 0xff000000;
-    case FXDIB_Argb:
+    case FXDIB_Format::kArgb:
       return FXARGB_GETDIB(pos);
     default:
       break;
@@ -581,18 +532,24 @@
   if (x < 0 || x >= m_Width || y < 0 || y >= m_Height)
     return;
 
-  uint8_t* pos = m_pBuffer.Get() + y * m_Pitch + x * GetBPP() / 8;
+  FX_SAFE_UINT32 offset = x;
+  offset *= GetBPP();
+  offset /= 8;
+  if (!offset.IsValid())
+    return;
+
+  uint8_t* pos = m_pBuffer.Get() + y * m_Pitch + offset.ValueOrDie();
   switch (GetFormat()) {
-    case FXDIB_1bppMask:
+    case FXDIB_Format::k1bppMask:
       if (color >> 24) {
         *pos |= 1 << (7 - x % 8);
       } else {
         *pos &= ~(1 << (7 - x % 8));
       }
       break;
-    case FXDIB_1bppRgb:
-      if (m_pPalette) {
-        if (color == m_pPalette.get()[1]) {
+    case FXDIB_Format::k1bppRgb:
+      if (HasPalette()) {
+        if (color == GetPaletteSpan()[1]) {
           *pos |= 1 << (7 - x % 8);
         } else {
           *pos &= ~(1 << (7 - x % 8));
@@ -605,13 +562,14 @@
         }
       }
       break;
-    case FXDIB_8bppMask:
+    case FXDIB_Format::k8bppMask:
       *pos = (uint8_t)(color >> 24);
       break;
-    case FXDIB_8bppRgb: {
-      if (m_pPalette) {
+    case FXDIB_Format::k8bppRgb: {
+      if (HasPalette()) {
+        pdfium::span<const uint32_t> palette = GetPaletteSpan();
         for (int i = 0; i < 256; i++) {
-          if (m_pPalette.get()[i] == color) {
+          if (palette[i] == color) {
             *pos = (uint8_t)i;
             return;
           }
@@ -622,92 +580,22 @@
       }
       break;
     }
-    case FXDIB_Rgb:
-    case FXDIB_Rgb32: {
+    case FXDIB_Format::kRgb:
+    case FXDIB_Format::kRgb32: {
       int alpha = FXARGB_A(color);
       pos[0] = (FXARGB_B(color) * alpha + pos[0] * (255 - alpha)) / 255;
       pos[1] = (FXARGB_G(color) * alpha + pos[1] * (255 - alpha)) / 255;
       pos[2] = (FXARGB_R(color) * alpha + pos[2] * (255 - alpha)) / 255;
       break;
     }
-    case FXDIB_Rgba: {
-      pos[0] = FXARGB_B(color);
-      pos[1] = FXARGB_G(color);
-      pos[2] = FXARGB_R(color);
-      break;
-    }
-    case FXDIB_Argb:
+    case FXDIB_Format::kArgb:
       FXARGB_SETDIB(pos, color);
       break;
     default:
       break;
   }
 }
-
-void CFX_DIBitmap::DownSampleScanline(int line,
-                                      uint8_t* dest_scan,
-                                      int dest_bpp,
-                                      int dest_width,
-                                      bool bFlipX,
-                                      int clip_left,
-                                      int clip_width) const {
-  if (!m_pBuffer)
-    return;
-
-  int src_Bpp = m_bpp / 8;
-  uint8_t* scanline = m_pBuffer.Get() + line * m_Pitch;
-  if (src_Bpp == 0) {
-    for (int i = 0; i < clip_width; i++) {
-      uint32_t dest_x = clip_left + i;
-      uint32_t src_x = dest_x * m_Width / dest_width;
-      if (bFlipX) {
-        src_x = m_Width - src_x - 1;
-      }
-      src_x %= m_Width;
-      dest_scan[i] = (scanline[src_x / 8] & (1 << (7 - src_x % 8))) ? 255 : 0;
-    }
-  } else if (src_Bpp == 1) {
-    for (int i = 0; i < clip_width; i++) {
-      uint32_t dest_x = clip_left + i;
-      uint32_t src_x = dest_x * m_Width / dest_width;
-      if (bFlipX) {
-        src_x = m_Width - src_x - 1;
-      }
-      src_x %= m_Width;
-      int dest_pos = i;
-      if (m_pPalette) {
-        if (!IsCmykImage()) {
-          dest_pos *= 3;
-          FX_ARGB argb = m_pPalette.get()[scanline[src_x]];
-          dest_scan[dest_pos] = FXARGB_B(argb);
-          dest_scan[dest_pos + 1] = FXARGB_G(argb);
-          dest_scan[dest_pos + 2] = FXARGB_R(argb);
-        } else {
-          dest_pos *= 4;
-          FX_CMYK cmyk = m_pPalette.get()[scanline[src_x]];
-          dest_scan[dest_pos] = FXSYS_GetCValue(cmyk);
-          dest_scan[dest_pos + 1] = FXSYS_GetMValue(cmyk);
-          dest_scan[dest_pos + 2] = FXSYS_GetYValue(cmyk);
-          dest_scan[dest_pos + 3] = FXSYS_GetKValue(cmyk);
-        }
-      } else {
-        dest_scan[dest_pos] = scanline[src_x];
-      }
-    }
-  } else {
-    for (int i = 0; i < clip_width; i++) {
-      uint32_t dest_x = clip_left + i;
-      uint32_t src_x =
-          bFlipX ? (m_Width - dest_x * m_Width / dest_width - 1) * src_Bpp
-                 : (dest_x * m_Width / dest_width) * src_Bpp;
-      src_x %= m_Width * src_Bpp;
-      int dest_pos = i * src_Bpp;
-      for (int b = 0; b < src_Bpp; b++) {
-        dest_scan[dest_pos + b] = scanline[src_x + b];
-      }
-    }
-  }
-}
+#endif  // defined(_SKIA_SUPPORT_)
 
 void CFX_DIBitmap::ConvertBGRColorScale(uint32_t forecolor,
                                         uint32_t backcolor) {
@@ -717,17 +605,16 @@
   int br = FXSYS_GetRValue(backcolor);
   int bg = FXSYS_GetGValue(backcolor);
   int bb = FXSYS_GetBValue(backcolor);
-  if (m_bpp <= 8) {
-    if (forecolor == 0 && backcolor == 0xffffff && !m_pPalette)
+  if (GetBppFromFormat(m_Format) <= 8) {
+    if (forecolor == 0 && backcolor == 0xffffff && !HasPalette())
       return;
-    if (!m_pPalette)
-      BuildPalette();
-    int size = 1 << m_bpp;
+
+    BuildPalette();
+    int size = 1 << GetBppFromFormat(m_Format);
     for (int i = 0; i < size; ++i) {
-      int gray = FXRGB2GRAY(FXARGB_R(m_pPalette.get()[i]),
-                            FXARGB_G(m_pPalette.get()[i]),
-                            FXARGB_B(m_pPalette.get()[i]));
-      m_pPalette.get()[i] =
+      int gray = FXRGB2GRAY(FXARGB_R(m_palette[i]), FXARGB_G(m_palette[i]),
+                            FXARGB_B(m_palette[i]));
+      m_palette[i] =
           ArgbEncode(0xff, br + (fr - br) * gray / 255,
                      bg + (fg - bg) * gray / 255, bb + (fb - bb) * gray / 255);
     }
@@ -736,7 +623,7 @@
   if (forecolor == 0 && backcolor == 0xffffff) {
     for (int row = 0; row < m_Height; ++row) {
       uint8_t* scanline = m_pBuffer.Get() + row * m_Pitch;
-      int gap = m_bpp / 8 - 2;
+      int gap = GetBppFromFormat(m_Format) / 8 - 2;
       for (int col = 0; col < m_Width; ++col) {
         int gray = FXRGB2GRAY(scanline[2], scanline[1], scanline[0]);
         *scanline++ = gray;
@@ -749,7 +636,7 @@
   }
   for (int row = 0; row < m_Height; ++row) {
     uint8_t* scanline = m_pBuffer.Get() + row * m_Pitch;
-    int gap = m_bpp / 8 - 2;
+    int gap = GetBppFromFormat(m_Format) / 8 - 2;
     for (int col = 0; col < m_Width; ++col) {
       int gray = FXRGB2GRAY(scanline[2], scanline[1], scanline[0]);
       *scanline++ = bb + (fb - bb) * gray / 255;
@@ -760,107 +647,43 @@
   }
 }
 
-void CFX_DIBitmap::ConvertCMYKColorScale(uint32_t forecolor,
-                                         uint32_t backcolor) {
-  int fc = FXSYS_GetCValue(forecolor);
-  int fm = FXSYS_GetMValue(forecolor);
-  int fy = FXSYS_GetYValue(forecolor);
-  int fk = FXSYS_GetKValue(forecolor);
-  int bc = FXSYS_GetCValue(backcolor);
-  int bm = FXSYS_GetMValue(backcolor);
-  int by = FXSYS_GetYValue(backcolor);
-  int bk = FXSYS_GetKValue(backcolor);
-  if (m_bpp <= 8) {
-    if (forecolor == 0xff && backcolor == 0 && !m_pPalette)
-      return;
-    if (!m_pPalette)
-      BuildPalette();
-    int size = 1 << m_bpp;
-    for (int i = 0; i < size; ++i) {
-      uint8_t r;
-      uint8_t g;
-      uint8_t b;
-      std::tie(r, g, b) =
-          AdobeCMYK_to_sRGB1(FXSYS_GetCValue(m_pPalette.get()[i]),
-                             FXSYS_GetMValue(m_pPalette.get()[i]),
-                             FXSYS_GetYValue(m_pPalette.get()[i]),
-                             FXSYS_GetKValue(m_pPalette.get()[i]));
-      int gray = 255 - FXRGB2GRAY(r, g, b);
-      m_pPalette.get()[i] =
-          CmykEncode(bc + (fc - bc) * gray / 255, bm + (fm - bm) * gray / 255,
-                     by + (fy - by) * gray / 255, bk + (fk - bk) * gray / 255);
-    }
-    return;
-  }
-  if (forecolor == 0xff && backcolor == 0x00) {
-    for (int row = 0; row < m_Height; ++row) {
-      uint8_t* scanline = m_pBuffer.Get() + row * m_Pitch;
-      for (int col = 0; col < m_Width; ++col) {
-        uint8_t r;
-        uint8_t g;
-        uint8_t b;
-        std::tie(r, g, b) = AdobeCMYK_to_sRGB1(scanline[0], scanline[1],
-                                               scanline[2], scanline[3]);
-        *scanline++ = 0;
-        *scanline++ = 0;
-        *scanline++ = 0;
-        *scanline++ = 255 - FXRGB2GRAY(r, g, b);
-      }
-    }
-    return;
-  }
-  for (int row = 0; row < m_Height; ++row) {
-    uint8_t* scanline = m_pBuffer.Get() + row * m_Pitch;
-    for (int col = 0; col < m_Width; ++col) {
-      uint8_t r;
-      uint8_t g;
-      uint8_t b;
-      std::tie(r, g, b) = AdobeCMYK_to_sRGB1(scanline[0], scanline[1],
-                                             scanline[2], scanline[3]);
-      int gray = 255 - FXRGB2GRAY(r, g, b);
-      *scanline++ = bc + (fc - bc) * gray / 255;
-      *scanline++ = bm + (fm - bm) * gray / 255;
-      *scanline++ = by + (fy - by) * gray / 255;
-      *scanline++ = bk + (fk - bk) * gray / 255;
-    }
-  }
-}
-
 bool CFX_DIBitmap::ConvertColorScale(uint32_t forecolor, uint32_t backcolor) {
-  if (!m_pBuffer || IsAlphaMask())
+  if (!m_pBuffer || IsMaskFormat())
     return false;
 
-  if (IsCmykImage())
-    ConvertCMYKColorScale(forecolor, backcolor);
-  else
-    ConvertBGRColorScale(forecolor, backcolor);
+  ConvertBGRColorScale(forecolor, backcolor);
   return true;
 }
 
 // static
-bool CFX_DIBitmap::CalculatePitchAndSize(int height,
-                                         int width,
-                                         FXDIB_Format format,
-                                         uint32_t* pitch,
-                                         uint32_t* size) {
+absl::optional<CFX_DIBitmap::PitchAndSize> CFX_DIBitmap::CalculatePitchAndSize(
+    int width,
+    int height,
+    FXDIB_Format format,
+    uint32_t pitch) {
   if (width <= 0 || height <= 0)
-    return false;
+    return absl::nullopt;
 
   int bpp = GetBppFromFormat(format);
   if (!bpp)
-    return false;
+    return absl::nullopt;
 
-  if ((INT_MAX - 31) / width < bpp)
-    return false;
+  uint32_t actual_pitch = pitch;
+  if (actual_pitch == 0) {
+    absl::optional<uint32_t> pitch32 = fxge::CalculatePitch32(bpp, width);
+    if (!pitch32.has_value()) {
+      return absl::nullopt;
+    }
 
-  if (!*pitch)
-    *pitch = static_cast<uint32_t>((width * bpp + 31) / 32 * 4);
+    actual_pitch = pitch32.value();
+  }
 
-  if ((1 << 30) / *pitch < static_cast<uint32_t>(height))
-    return false;
+  FX_SAFE_UINT32 safe_size = actual_pitch;
+  safe_size *= height;
+  if (!safe_size.IsValid())
+    return absl::nullopt;
 
-  *size = *pitch * static_cast<uint32_t>(height);
-  return true;
+  return PitchAndSize{actual_pitch, safe_size.ValueOrDie()};
 }
 
 bool CFX_DIBitmap::CompositeBitmap(int dest_left,
@@ -873,13 +696,14 @@
                                    BlendMode blend_type,
                                    const CFX_ClipRgn* pClipRgn,
                                    bool bRgbByteOrder) {
+  // Should have called CompositeMask().
+  CHECK(!pSrcBitmap->IsMaskFormat());
+
   if (!m_pBuffer)
     return false;
 
-  if (pSrcBitmap->IsAlphaMask() || m_bpp < 8) {
-    NOTREACHED();
+  if (GetBppFromFormat(m_Format) < 8)
     return false;
-  }
 
   if (!GetOverlapRect(dest_left, dest_top, width, height,
                       pSrcBitmap->GetWidth(), pSrcBitmap->GetHeight(), src_left,
@@ -889,47 +713,37 @@
 
   RetainPtr<CFX_DIBitmap> pClipMask;
   FX_RECT clip_box;
-  if (pClipRgn && pClipRgn->GetType() != CFX_ClipRgn::RectI) {
-    ASSERT(pClipRgn->GetType() == CFX_ClipRgn::MaskF);
+  if (pClipRgn && pClipRgn->GetType() != CFX_ClipRgn::kRectI) {
     pClipMask = pClipRgn->GetMask();
     clip_box = pClipRgn->GetBox();
   }
   CFX_ScanlineCompositor compositor;
-  if (!compositor.Init(GetFormat(), pSrcBitmap->GetFormat(), width,
-                       pSrcBitmap->GetPalette(), 0, blend_type,
+  if (!compositor.Init(GetFormat(), pSrcBitmap->GetFormat(),
+                       pSrcBitmap->GetPaletteSpan(), 0, blend_type,
                        pClipMask != nullptr, bRgbByteOrder)) {
     return false;
   }
-  int dest_Bpp = m_bpp / 8;
-  int src_Bpp = pSrcBitmap->GetBPP() / 8;
-  bool bRgb = src_Bpp > 1 && !pSrcBitmap->IsCmykImage();
-  RetainPtr<CFX_DIBitmap> pSrcAlphaMask = pSrcBitmap->m_pAlphaMask;
+  const int dest_Bpp = GetBppFromFormat(m_Format) / 8;
+  const int src_Bpp = pSrcBitmap->GetBPP() / 8;
+  const bool bRgb = src_Bpp > 1;
+  if (!bRgb && !pSrcBitmap->HasPalette())
+    return false;
+
   for (int row = 0; row < height; row++) {
-    uint8_t* dest_scan =
-        m_pBuffer.Get() + (dest_top + row) * m_Pitch + dest_left * dest_Bpp;
-    const uint8_t* src_scan =
-        pSrcBitmap->GetScanline(src_top + row) + src_left * src_Bpp;
-    const uint8_t* src_scan_extra_alpha =
-        pSrcAlphaMask ? pSrcAlphaMask->GetScanline(src_top + row) + src_left
-                      : nullptr;
-    uint8_t* dst_scan_extra_alpha =
-        m_pAlphaMask
-            ? m_pAlphaMask->GetWritableScanline(dest_top + row) + dest_left
-            : nullptr;
-    const uint8_t* clip_scan = nullptr;
+    pdfium::span<uint8_t> dest_scan =
+        GetWritableScanline(dest_top + row).subspan(dest_left * dest_Bpp);
+    pdfium::span<const uint8_t> src_scan =
+        pSrcBitmap->GetScanline(src_top + row).subspan(src_left * src_Bpp);
+    pdfium::span<const uint8_t> clip_scan;
     if (pClipMask) {
-      clip_scan = pClipMask->m_pBuffer.Get() +
-                  (dest_top + row - clip_box.top) * pClipMask->m_Pitch +
-                  (dest_left - clip_box.left);
+      clip_scan = pClipMask->GetWritableScanline(dest_top + row - clip_box.top)
+                      .subspan(dest_left - clip_box.left);
     }
     if (bRgb) {
-      compositor.CompositeRgbBitmapLine(dest_scan, src_scan, width, clip_scan,
-                                        src_scan_extra_alpha,
-                                        dst_scan_extra_alpha);
+      compositor.CompositeRgbBitmapLine(dest_scan, src_scan, width, clip_scan);
     } else {
       compositor.CompositePalBitmapLine(dest_scan, src_scan, src_left, width,
-                                        clip_scan, src_scan_extra_alpha,
-                                        dst_scan_extra_alpha);
+                                        clip_scan);
     }
   }
   return true;
@@ -946,13 +760,14 @@
                                  BlendMode blend_type,
                                  const CFX_ClipRgn* pClipRgn,
                                  bool bRgbByteOrder) {
+  // Should have called CompositeBitmap().
+  CHECK(pMask->IsMaskFormat());
+
   if (!m_pBuffer)
     return false;
 
-  if (!pMask->IsAlphaMask() || m_bpp < 8) {
-    NOTREACHED();
+  if (GetBppFromFormat(m_Format) < 8)
     return false;
-  }
 
   if (!GetOverlapRect(dest_left, dest_top, width, height, pMask->GetWidth(),
                       pMask->GetHeight(), src_left, src_top, pClipRgn)) {
@@ -965,53 +780,76 @@
 
   RetainPtr<CFX_DIBitmap> pClipMask;
   FX_RECT clip_box;
-  if (pClipRgn && pClipRgn->GetType() != CFX_ClipRgn::RectI) {
-    ASSERT(pClipRgn->GetType() == CFX_ClipRgn::MaskF);
+  if (pClipRgn && pClipRgn->GetType() != CFX_ClipRgn::kRectI) {
     pClipMask = pClipRgn->GetMask();
     clip_box = pClipRgn->GetBox();
   }
   int src_bpp = pMask->GetBPP();
   int Bpp = GetBPP() / 8;
   CFX_ScanlineCompositor compositor;
-  if (!compositor.Init(GetFormat(), pMask->GetFormat(), width, nullptr, color,
-                       blend_type, pClipMask != nullptr, bRgbByteOrder)) {
+  if (!compositor.Init(GetFormat(), pMask->GetFormat(), {}, color, blend_type,
+                       pClipMask != nullptr, bRgbByteOrder)) {
     return false;
   }
   for (int row = 0; row < height; row++) {
-    uint8_t* dest_scan =
-        m_pBuffer.Get() + (dest_top + row) * m_Pitch + dest_left * Bpp;
-    const uint8_t* src_scan = pMask->GetScanline(src_top + row);
-    uint8_t* dst_scan_extra_alpha =
-        m_pAlphaMask
-            ? m_pAlphaMask->GetWritableScanline(dest_top + row) + dest_left
-            : nullptr;
-    const uint8_t* clip_scan = nullptr;
+    pdfium::span<uint8_t> dest_scan =
+        GetWritableScanline(dest_top + row).subspan(dest_left * Bpp);
+    pdfium::span<const uint8_t> src_scan = pMask->GetScanline(src_top + row);
+    pdfium::span<const uint8_t> clip_scan;
     if (pClipMask) {
-      clip_scan = pClipMask->m_pBuffer.Get() +
-                  (dest_top + row - clip_box.top) * pClipMask->m_Pitch +
-                  (dest_left - clip_box.left);
+      clip_scan = pClipMask->GetScanline(dest_top + row - clip_box.top)
+                      .subspan(dest_left - clip_box.left);
     }
     if (src_bpp == 1) {
       compositor.CompositeBitMaskLine(dest_scan, src_scan, src_left, width,
-                                      clip_scan, dst_scan_extra_alpha);
+                                      clip_scan);
     } else {
-      compositor.CompositeByteMaskLine(dest_scan, src_scan + src_left, width,
-                                       clip_scan, dst_scan_extra_alpha);
+      compositor.CompositeByteMaskLine(dest_scan, src_scan.subspan(src_left),
+                                       width, clip_scan);
     }
   }
   return true;
 }
 
+void CFX_DIBitmap::CompositeOneBPPMask(int dest_left,
+                                       int dest_top,
+                                       int width,
+                                       int height,
+                                       const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                       int src_left,
+                                       int src_top) {
+  if (GetBPP() != 1) {
+    return;
+  }
+
+  if (!GetOverlapRect(dest_left, dest_top, width, height,
+                      pSrcBitmap->GetWidth(), pSrcBitmap->GetHeight(), src_left,
+                      src_top, nullptr)) {
+    return;
+  }
+
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan = m_pBuffer.Get() + (dest_top + row) * m_Pitch;
+    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row).data();
+    for (int col = 0; col < width; ++col) {
+      int src_idx = src_left + col;
+      int dest_idx = dest_left + col;
+      if (src_scan[src_idx / 8] & (1 << (7 - src_idx % 8))) {
+        dest_scan[dest_idx / 8] |= 1 << (7 - dest_idx % 8);
+      }
+    }
+  }
+}
+
 bool CFX_DIBitmap::CompositeRect(int left,
                                  int top,
                                  int width,
                                  int height,
-                                 uint32_t color,
-                                 int alpha_flag) {
+                                 uint32_t color) {
   if (!m_pBuffer)
     return false;
 
-  int src_alpha = (alpha_flag >> 8) ? (alpha_flag & 0xff) : FXARGB_A(color);
+  int src_alpha = FXARGB_A(color);
   if (src_alpha == 0)
     return true;
 
@@ -1021,29 +859,12 @@
     return true;
 
   width = rect.Width();
-  uint32_t dst_color;
-  if (alpha_flag >> 8)
-    dst_color = FXCMYK_TODIB(color);
-  else
-    dst_color = FXARGB_TODIB(color);
+  uint32_t dst_color = color;
   uint8_t* color_p = reinterpret_cast<uint8_t*>(&dst_color);
-  if (m_bpp == 8) {
-    uint8_t gray = 255;
-    if (!IsAlphaMask()) {
-      if (alpha_flag >> 8) {
-        uint8_t r;
-        uint8_t g;
-        uint8_t b;
-        std::tie(r, g, b) =
-            AdobeCMYK_to_sRGB1(color_p[0], color_p[1], color_p[2], color_p[3]);
-        gray = FXRGB2GRAY(r, g, b);
-      } else {
-        gray = (uint8_t)FXRGB2GRAY((int)color_p[2], color_p[1], color_p[0]);
-      }
-      if (IsCmykImage()) {
-        gray = ~gray;
-      }
-    }
+  if (GetBppFromFormat(m_Format) == 8) {
+    uint8_t gray = IsMaskFormat() ? 255
+                                  : (uint8_t)FXRGB2GRAY((int)color_p[2],
+                                                        color_p[1], color_p[0]);
     for (int row = rect.top; row < rect.bottom; row++) {
       uint8_t* dest_scan = m_pBuffer.Get() + row * m_Pitch + rect.left;
       if (src_alpha == 255) {
@@ -1057,25 +878,24 @@
     }
     return true;
   }
-  if (m_bpp == 1) {
-    ASSERT(!IsCmykImage());
-    ASSERT(static_cast<uint8_t>(alpha_flag >> 8) == 0);
-
+  if (GetBppFromFormat(m_Format) == 1) {
     int left_shift = rect.left % 8;
     int right_shift = rect.right % 8;
     int new_width = rect.right / 8 - rect.left / 8;
     int index = 0;
-    if (m_pPalette) {
+    if (HasPalette()) {
       for (int i = 0; i < 2; i++) {
-        if (m_pPalette.get()[i] == color)
+        if (GetPaletteSpan()[i] == color)
           index = i;
       }
     } else {
       index = (static_cast<uint8_t>(color) == 0xff) ? 1 : 0;
     }
     for (int row = rect.top; row < rect.bottom; row++) {
-      uint8_t* dest_scan_top = GetWritableScanline(row) + rect.left / 8;
-      uint8_t* dest_scan_top_r = GetWritableScanline(row) + rect.right / 8;
+      uint8_t* dest_scan_top =
+          GetWritableScanline(row).subspan(rect.left / 8).data();
+      uint8_t* dest_scan_top_r =
+          GetWritableScanline(row).subspan(rect.right / 8).data();
       uint8_t left_flag = *dest_scan_top & (255 << (8 - left_shift));
       uint8_t right_flag = *dest_scan_top_r & (255 >> right_shift);
       if (new_width) {
@@ -1098,33 +918,17 @@
     return true;
   }
 
-  if (m_bpp < 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();
+  if (bAlpha) {
+    // Other formats with alpha have already been handled above.
+    DCHECK_EQ(GetFormat(), FXDIB_Format::kArgb);
   }
-
-  if (!(alpha_flag >> 8) && IsCmykImage())
-    return false;
-
-  if (alpha_flag >> 8 && !IsCmykImage()) {
-    std::tie(color_p[2], color_p[1], color_p[0]) =
-        AdobeCMYK_to_sRGB1(FXSYS_GetCValue(color), FXSYS_GetMValue(color),
-                           FXSYS_GetYValue(color), FXSYS_GetKValue(color));
-  }
-  if (!IsCmykImage())
-    color_p[3] = static_cast<uint8_t>(src_alpha);
-  int Bpp = m_bpp / 8;
-  bool bAlpha = HasAlpha();
-  bool bArgb = GetFormat() == FXDIB_Argb;
   if (src_alpha == 255) {
     for (int row = rect.top; row < rect.bottom; row++) {
       uint8_t* dest_scan = m_pBuffer.Get() + row * m_Pitch + rect.left * Bpp;
-      uint8_t* dest_scan_alpha =
-          m_pAlphaMask ? m_pAlphaMask->GetWritableScanline(row) + rect.left
-                       : nullptr;
-      if (dest_scan_alpha)
-        memset(dest_scan_alpha, 0xff, width);
-
       if (Bpp == 4) {
         uint32_t* scan = reinterpret_cast<uint32_t*>(dest_scan);
         for (int col = 0; col < width; col++)
@@ -1142,47 +946,24 @@
   for (int row = rect.top; row < rect.bottom; row++) {
     uint8_t* dest_scan = m_pBuffer.Get() + row * m_Pitch + rect.left * Bpp;
     if (bAlpha) {
-      if (bArgb) {
-        for (int col = 0; col < width; col++) {
-          uint8_t back_alpha = dest_scan[3];
-          if (back_alpha == 0) {
-            FXARGB_SETDIB(dest_scan, ArgbEncode(src_alpha, color_p[2],
-                                                color_p[1], color_p[0]));
-            dest_scan += 4;
-            continue;
-          }
-          uint8_t dest_alpha =
-              back_alpha + src_alpha - back_alpha * src_alpha / 255;
-          int alpha_ratio = src_alpha * 255 / dest_alpha;
-          *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[0], alpha_ratio);
-          dest_scan++;
-          *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[1], alpha_ratio);
-          dest_scan++;
-          *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[2], alpha_ratio);
-          dest_scan++;
-          *dest_scan++ = dest_alpha;
+      for (int col = 0; col < width; col++) {
+        uint8_t back_alpha = dest_scan[3];
+        if (back_alpha == 0) {
+          FXARGB_SETDIB(dest_scan, ArgbEncode(src_alpha, color_p[2], color_p[1],
+                                              color_p[0]));
+          dest_scan += 4;
+          continue;
         }
-      } else {
-        uint8_t* dest_scan_alpha =
-            m_pAlphaMask->GetWritableScanline(row) + rect.left;
-        for (int col = 0; col < width; col++) {
-          uint8_t back_alpha = *dest_scan_alpha;
-          if (back_alpha == 0) {
-            *dest_scan_alpha++ = src_alpha;
-            memcpy(dest_scan, color_p, Bpp);
-            dest_scan += Bpp;
-            continue;
-          }
-          uint8_t dest_alpha =
-              back_alpha + src_alpha - back_alpha * src_alpha / 255;
-          *dest_scan_alpha++ = dest_alpha;
-          int alpha_ratio = src_alpha * 255 / dest_alpha;
-          for (int comps = 0; comps < Bpp; comps++) {
-            *dest_scan =
-                FXDIB_ALPHA_MERGE(*dest_scan, color_p[comps], alpha_ratio);
-            dest_scan++;
-          }
-        }
+        uint8_t dest_alpha =
+            back_alpha + src_alpha - back_alpha * src_alpha / 255;
+        int alpha_ratio = src_alpha * 255 / dest_alpha;
+        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[0], alpha_ratio);
+        dest_scan++;
+        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[1], alpha_ratio);
+        dest_scan++;
+        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[2], alpha_ratio);
+        dest_scan++;
+        *dest_scan++ = dest_alpha;
       }
     } else {
       for (int col = 0; col < width; col++) {
@@ -1201,17 +982,21 @@
 }
 
 bool CFX_DIBitmap::ConvertFormat(FXDIB_Format dest_format) {
-  FXDIB_Format src_format = GetFormat();
-  if (dest_format == src_format)
+  DCHECK(dest_format == FXDIB_Format::k8bppMask ||
+         dest_format == FXDIB_Format::kArgb ||
+         dest_format == FXDIB_Format::kRgb32 ||
+         dest_format == FXDIB_Format::kRgb);
+
+  if (dest_format == m_Format)
     return true;
 
-  if (dest_format == FXDIB_8bppMask && src_format == FXDIB_8bppRgb &&
-      !m_pPalette) {
-    m_AlphaFlag = 1;
+  if (dest_format == FXDIB_Format::k8bppMask &&
+      m_Format == FXDIB_Format::k8bppRgb && !HasPalette()) {
+    m_Format = FXDIB_Format::k8bppMask;
     return true;
   }
-  if (dest_format == FXDIB_Argb && src_format == FXDIB_Rgb32) {
-    m_AlphaFlag = 2;
+  if (dest_format == FXDIB_Format::kArgb && m_Format == FXDIB_Format::kRgb32) {
+    m_Format = FXDIB_Format::kArgb;
     for (int row = 0; row < m_Height; row++) {
       uint8_t* scanline = m_pBuffer.Get() + row * m_Pitch + 3;
       for (int col = 0; col < m_Width; col++) {
@@ -1222,53 +1007,26 @@
     return true;
   }
   int dest_bpp = GetBppFromFormat(dest_format);
-  int dest_pitch = (dest_bpp * m_Width + 31) / 32 * 4;
+  int dest_pitch = fxge::CalculatePitch32OrDie(dest_bpp, m_Width);
+  const size_t dest_buf_size = dest_pitch * m_Height + 4;
   std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf(
-      FX_TryAlloc(uint8_t, dest_pitch * m_Height + 4));
+      FX_TryAlloc(uint8_t, dest_buf_size));
   if (!dest_buf)
     return false;
 
-  RetainPtr<CFX_DIBitmap> pAlphaMask;
-  if (dest_format == FXDIB_Argb) {
-    memset(dest_buf.get(), 0xff, dest_pitch * m_Height + 4);
-    if (m_pAlphaMask) {
-      for (int row = 0; row < m_Height; row++) {
-        uint8_t* pDstScanline = dest_buf.get() + row * dest_pitch + 3;
-        const uint8_t* pSrcScanline = m_pAlphaMask->GetScanline(row);
-        for (int col = 0; col < m_Width; col++) {
-          *pDstScanline = *pSrcScanline++;
-          pDstScanline += 4;
-        }
-      }
-    }
-  } else if (GetIsAlphaFromFormat(dest_format)) {
-    if (src_format == FXDIB_Argb) {
-      pAlphaMask = CloneAlphaMask();
-      if (!pAlphaMask)
-        return false;
-    } else {
-      if (!m_pAlphaMask) {
-        if (!BuildAlphaMask())
-          return false;
-        pAlphaMask = std::move(m_pAlphaMask);
-      } else {
-        pAlphaMask = m_pAlphaMask;
-      }
-    }
+  if (dest_format == FXDIB_Format::kArgb) {
+    memset(dest_buf.get(), 0xff, dest_buf_size);
   }
-  bool ret = false;
   RetainPtr<CFX_DIBBase> holder(this);
-  std::unique_ptr<uint32_t, FxFreeDeleter> pal_8bpp;
-  ret = ConvertBuffer(dest_format, dest_buf.get(), dest_pitch, m_Width,
-                      m_Height, holder, 0, 0, &pal_8bpp);
-  if (!ret)
+  DataVector<uint32_t> pal_8bpp;
+  if (!ConvertBuffer(dest_format, {dest_buf.get(), dest_buf_size}, dest_pitch,
+                     m_Width, m_Height, holder, 0, 0, &pal_8bpp)) {
     return false;
+  }
 
-  m_pAlphaMask = pAlphaMask;
-  m_pPalette = std::move(pal_8bpp);
+  m_palette = std::move(pal_8bpp);
   m_pBuffer = std::move(dest_buf);
-  m_bpp = GetBppFromFormat(dest_format);
-  m_AlphaFlag = GetAlphaFlagFromFormat(dest_format);
+  m_Format = dest_format;
   m_Pitch = dest_pitch;
   return true;
 }
diff --git a/core/fxge/dib/cfx_dibitmap.h b/core/fxge/dib/cfx_dibitmap.h
index ce53df8..34f5887 100644
--- a/core/fxge/dib/cfx_dibitmap.h
+++ b/core/fxge/dib/cfx_dibitmap.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,20 +7,24 @@
 #ifndef CORE_FXGE_DIB_CFX_DIBITMAP_H_
 #define CORE_FXGE_DIB_CFX_DIBITMAP_H_
 
-#include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/maybe_owned.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/dib/cfx_dibbase.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/containers/span.h"
 
-class CFX_DIBitmap : public CFX_DIBBase {
+class CFX_DIBitmap final : public CFX_DIBBase {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  struct PitchAndSize {
+    uint32_t pitch;
+    uint32_t size;
+  };
+
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   bool Create(int width, int height, FXDIB_Format format);
-
   bool Create(int width,
               int height,
               FXDIB_Format format,
@@ -30,27 +34,34 @@
   bool Copy(const RetainPtr<CFX_DIBBase>& pSrc);
 
   // CFX_DIBBase
-  uint8_t* GetBuffer() const override;
-  const uint8_t* GetScanline(int line) const override;
-  void DownSampleScanline(int line,
-                          uint8_t* dest_scan,
-                          int dest_bpp,
-                          int dest_width,
-                          bool bFlipX,
-                          int clip_left,
-                          int clip_width) const override;
+  pdfium::span<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);
 
+#if defined(_SKIA_SUPPORT_)
   uint32_t GetPixel(int x, int y) const;
   void SetPixel(int x, int y, uint32_t color);
+#endif  // defined(_SKIA_SUPPORT_)
 
-  bool LoadChannelFromAlpha(FXDIB_Channel destChannel,
-                            const RetainPtr<CFX_DIBBase>& pSrcBitmap);
-  bool LoadChannel(FXDIB_Channel destChannel, int value);
+  bool SetRedFromBitmap(const RetainPtr<CFX_DIBBase>& pSrcBitmap);
+  bool SetAlphaFromBitmap(const RetainPtr<CFX_DIBBase>& pSrcBitmap);
+  bool SetUniformOpaqueAlpha();
 
+  // TODO(crbug.com/pdfium/2007): Migrate callers to `CFX_RenderDevice`.
   bool MultiplyAlpha(int alpha);
   bool MultiplyAlpha(const RetainPtr<CFX_DIBBase>& pSrcBitmap);
 
@@ -85,45 +96,62 @@
                      const CFX_ClipRgn* pClipRgn,
                      bool bRgbByteOrder);
 
+  void CompositeOneBPPMask(int dest_left,
+                           int dest_top,
+                           int width,
+                           int height,
+                           const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                           int src_left,
+                           int src_top);
+
   bool CompositeRect(int dest_left,
                      int dest_top,
                      int width,
                      int height,
-                     uint32_t color,
-                     int alpha_flag);
+                     uint32_t color);
 
   bool ConvertColorScale(uint32_t forecolor, uint32_t backcolor);
 
-  static bool CalculatePitchAndSize(int height,
-                                    int width,
-                                    FXDIB_Format format,
-                                    uint32_t* pitch,
-                                    uint32_t* size);
+  // |width| and |height| must be greater than 0.
+  // |format| must have a valid bits per pixel count.
+  // If |pitch| is zero, then the actual pitch will be calculated based on
+  // |width| and |format|.
+  // If |pitch| is non-zero, then that be used as the actual pitch.
+  // The actual pitch will be used to calculate the size.
+  // Returns the calculated pitch and size on success, or nullopt on failure.
+  static absl::optional<PitchAndSize> CalculatePitchAndSize(int width,
+                                                            int height,
+                                                            FXDIB_Format format,
+                                                            uint32_t pitch);
 
-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
-  void PreMultiply();
-#endif
-#if defined _SKIA_SUPPORT_PATHS_
+#if defined(_SKIA_SUPPORT_)
+  // 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();
 #endif
 
  protected:
+#if defined(_SKIA_SUPPORT_)
+  bool IsPremultiplied() const override;
+#endif  // defined(_SKIA_SUPPORT_)
+
+ private:
+  enum class Channel : uint8_t { kRed, kAlpha };
+
+#if defined(_SKIA_SUPPORT_)
+  enum class Format { kCleared, kPreMultiplied, kUnPreMultiplied };
+#endif
+
   CFX_DIBitmap();
   CFX_DIBitmap(const CFX_DIBitmap& src);
   ~CFX_DIBitmap() override;
 
-#if defined _SKIA_SUPPORT_PATHS_
-  enum class Format { kCleared, kPreMultiplied, kUnPreMultiplied };
-#endif
-
-  MaybeOwned<uint8_t, FxFreeDeleter> m_pBuffer;
-#if defined _SKIA_SUPPORT_PATHS_
-  Format m_nFormat;
-#endif
-
- private:
+  bool SetChannelFromBitmap(Channel destChannel,
+                            const RetainPtr<CFX_DIBBase>& pSrcBitmap);
   void ConvertBGRColorScale(uint32_t forecolor, uint32_t backcolor);
-  void ConvertCMYKColorScale(uint32_t forecolor, uint32_t backcolor);
   bool TransferWithUnequalFormats(FXDIB_Format dest_format,
                                   int dest_left,
                                   int dest_top,
@@ -146,6 +174,11 @@
                                   const RetainPtr<CFX_DIBBase>& pSrcBitmap,
                                   int src_left,
                                   int src_top);
+
+  MaybeOwned<uint8_t, FxFreeDeleter> m_pBuffer;
+#if defined(_SKIA_SUPPORT_)
+  Format m_nFormat = Format::kCleared;
+#endif
 };
 
 #endif  // CORE_FXGE_DIB_CFX_DIBITMAP_H_
diff --git a/core/fxge/dib/cfx_dibitmap_unittest.cpp b/core/fxge/dib/cfx_dibitmap_unittest.cpp
index 67ca705..ef448c2 100644
--- a/core/fxge/dib/cfx_dibitmap_unittest.cpp
+++ b/core/fxge/dib/cfx_dibitmap_unittest.cpp
@@ -1,15 +1,148 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxge/dib/cfx_dibitmap.h"
 
+#include <stdint.h>
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/containers/span.h"
+
+namespace {
+
+using ::testing::ElementsAre;
+
+}  // namespace
 
 TEST(CFX_DIBitmap, Create) {
   auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  EXPECT_FALSE(pBitmap->Create(400, 300, FXDIB_Invalid));
+  EXPECT_FALSE(pBitmap->Create(400, 300, FXDIB_Format::kInvalid));
 
   pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  EXPECT_TRUE(pBitmap->Create(400, 300, FXDIB_1bppRgb));
+  EXPECT_TRUE(pBitmap->Create(400, 300, FXDIB_Format::k1bppRgb));
 }
+
+TEST(CFX_DIBitmap, CalculatePitchAndSizeGood) {
+  // Simple case with no provided pitch.
+  absl::optional<CFX_DIBitmap::PitchAndSize> result =
+      CFX_DIBitmap::CalculatePitchAndSize(100, 200, FXDIB_Format::kArgb, 0);
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(400u, result.value().pitch);
+  EXPECT_EQ(80000u, result.value().size);
+
+  // Simple case with no provided pitch and different format.
+  result =
+      CFX_DIBitmap::CalculatePitchAndSize(100, 200, FXDIB_Format::k8bppRgb, 0);
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(100u, result.value().pitch);
+  EXPECT_EQ(20000u, result.value().size);
+
+  // Simple case with provided pitch.
+  result =
+      CFX_DIBitmap::CalculatePitchAndSize(100, 200, FXDIB_Format::kArgb, 400);
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(400u, result.value().pitch);
+  EXPECT_EQ(80000u, result.value().size);
+
+  // Simple case with provided pitch, but pitch does not match width * bpp.
+  result =
+      CFX_DIBitmap::CalculatePitchAndSize(100, 200, FXDIB_Format::kArgb, 355);
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(355u, result.value().pitch);
+  EXPECT_EQ(71000u, result.value().size);
+}
+
+TEST(CFX_DIBitmap, CalculatePitchAndSizeBad) {
+  // Bad width / height.
+  static const CFX_Size kBadDimensions[] = {
+      {0, 0},   {-1, -1}, {-1, 0},   {0, -1},
+      {0, 200}, {100, 0}, {-1, 200}, {100, -1},
+  };
+  for (const auto& dimension : kBadDimensions) {
+    EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize(
+        dimension.width, dimension.height, FXDIB_Format::kArgb, 0));
+    EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize(
+        dimension.width, dimension.height, FXDIB_Format::kArgb, 1));
+  }
+
+  // Bad format.
+  EXPECT_FALSE(
+      CFX_DIBitmap::CalculatePitchAndSize(100, 200, FXDIB_Format::kInvalid, 0));
+  EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize(
+      100, 200, FXDIB_Format::kInvalid, 800));
+
+  // Overflow cases with calculated pitch.
+  EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize(1073747000, 1,
+                                                   FXDIB_Format::kArgb, 0));
+  EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize(1048576, 1024,
+                                                   FXDIB_Format::kArgb, 0));
+  EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize(4194304, 1024,
+                                                   FXDIB_Format::k8bppRgb, 0));
+
+  // Overflow cases with provided pitch.
+  EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize(
+      2147484000u, 1, FXDIB_Format::kArgb, 2147484000u));
+  EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize(
+      1048576, 1024, FXDIB_Format::kArgb, 4194304));
+  EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize(
+      4194304, 1024, FXDIB_Format::k8bppRgb, 4194304));
+}
+
+TEST(CFX_DIBitmap, CalculatePitchAndSizeBoundary) {
+  // Test boundary condition for pitch overflow.
+  absl::optional<CFX_DIBitmap::PitchAndSize> result =
+      CFX_DIBitmap::CalculatePitchAndSize(536870908, 4, FXDIB_Format::k8bppRgb,
+                                          0);
+  ASSERT_TRUE(result);
+  EXPECT_EQ(536870908u, result.value().pitch);
+  EXPECT_EQ(2147483632u, result.value().size);
+  EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize(536870909, 4,
+                                                   FXDIB_Format::k8bppRgb, 0));
+
+  // Test boundary condition for size overflow.
+  result = CFX_DIBitmap::CalculatePitchAndSize(68174084, 63,
+                                               FXDIB_Format::k8bppRgb, 0);
+  ASSERT_TRUE(result);
+  EXPECT_EQ(68174084u, result.value().pitch);
+  EXPECT_EQ(4294967292u, result.value().size);
+  EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize(68174085, 63,
+                                                   FXDIB_Format::k8bppRgb, 0));
+}
+
+#if defined(_SKIA_SUPPORT_)
+TEST(CFX_DIBitmap, UnPreMultiply_FromCleared) {
+  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->UnPreMultiply();
+
+  EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0xff, 0xff, 0xff, 0x7f));
+}
+
+TEST(CFX_DIBitmap, UnPreMultiply_FromPreMultiplied) {
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  ASSERT_TRUE(bitmap->Create(1, 1, FXDIB_Format::kArgb));
+  bitmap->ForcePreMultiply();
+  FXARGB_SETDIB(bitmap->GetBuffer().data(), 0x7f'7f'7f'7f);
+
+  bitmap->UnPreMultiply();
+
+  EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0xff, 0xff, 0xff, 0x7f));
+}
+
+TEST(CFX_DIBitmap, UnPreMultiply_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->UnPreMultiply();
+
+  EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0xff, 0xff, 0xff, 0x7f));
+}
+#endif  // defined(_SKIA_SUPPORT_)
diff --git a/core/fxge/dib/cfx_imagerenderer.cpp b/core/fxge/dib/cfx_imagerenderer.cpp
index 5c15a9c..2e7f496 100644
--- a/core/fxge/dib/cfx_imagerenderer.cpp
+++ b/core/fxge/dib/cfx_imagerenderer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,13 +6,14 @@
 
 #include "core/fxge/dib/cfx_imagerenderer.h"
 
+#include <math.h>
+
 #include <memory>
 
 #include "core/fxge/cfx_cliprgn.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/dib/cfx_imagestretcher.h"
 #include "core/fxge/dib/cfx_imagetransformer.h"
-#include "third_party/base/ptr_util.h"
 
 CFX_ImageRenderer::CFX_ImageRenderer(const RetainPtr<CFX_DIBitmap>& pDevice,
                                      const CFX_ClipRgn* pClipRgn,
@@ -45,19 +46,19 @@
       int dest_height = image_rect.Height();
       FX_RECT bitmap_clip = m_ClipBox;
       bitmap_clip.Offset(-image_rect.left, -image_rect.top);
-      bitmap_clip = FXDIB_SwapClipBox(bitmap_clip, dest_width, dest_height,
-                                      m_Matrix.c > 0, m_Matrix.b < 0);
+      bitmap_clip = bitmap_clip.SwappedClipBox(dest_width, dest_height,
+                                               m_Matrix.c > 0, m_Matrix.b < 0);
       m_Composer.Compose(pDevice, pClipRgn, bitmap_alpha, mask_color, m_ClipBox,
                          true, m_Matrix.c > 0, m_Matrix.b < 0, m_bRgbByteOrder,
                          BlendMode::kNormal);
-      m_Stretcher = pdfium::MakeUnique<CFX_ImageStretcher>(
+      m_Stretcher = std::make_unique<CFX_ImageStretcher>(
           &m_Composer, pSource, dest_height, dest_width, bitmap_clip, options);
       if (m_Stretcher->Start())
-        m_Status = 1;
+        m_State = State::kStretching;
       return;
     }
-    m_Status = 2;
-    m_pTransformer = pdfium::MakeUnique<CFX_ImageTransformer>(
+    m_State = State::kTransforming;
+    m_pTransformer = std::make_unique<CFX_ImageTransformer>(
         pSource, m_Matrix, options, &m_ClipBox);
     return;
   }
@@ -77,40 +78,40 @@
   bitmap_clip.Offset(-image_rect.left, -image_rect.top);
   m_Composer.Compose(pDevice, pClipRgn, bitmap_alpha, mask_color, m_ClipBox,
                      false, false, false, m_bRgbByteOrder, BlendMode::kNormal);
-  m_Status = 1;
-  m_Stretcher = pdfium::MakeUnique<CFX_ImageStretcher>(
+  m_State = State::kStretching;
+  m_Stretcher = std::make_unique<CFX_ImageStretcher>(
       &m_Composer, pSource, dest_width, dest_height, bitmap_clip, options);
   m_Stretcher->Start();
 }
 
-CFX_ImageRenderer::~CFX_ImageRenderer() {}
+CFX_ImageRenderer::~CFX_ImageRenderer() = default;
 
 bool CFX_ImageRenderer::Continue(PauseIndicatorIface* pPause) {
-  if (m_Status == 1)
+  if (m_State == State::kStretching)
     return m_Stretcher->Continue(pPause);
-  if (m_Status != 2)
+  if (m_State != State::kTransforming)
     return false;
   if (m_pTransformer->Continue(pPause))
     return true;
 
   RetainPtr<CFX_DIBitmap> pBitmap = m_pTransformer->DetachBitmap();
-  if (!pBitmap || !pBitmap->GetBuffer())
+  if (!pBitmap || pBitmap->GetBuffer().empty())
     return false;
 
-  if (pBitmap->IsAlphaMask()) {
+  if (pBitmap->IsMaskFormat()) {
     if (m_BitmapAlpha != 255)
       m_MaskColor = FXARGB_MUL_ALPHA(m_MaskColor, m_BitmapAlpha);
-    m_pDevice->CompositeMask(
-        m_pTransformer->result().left, m_pTransformer->result().top,
-        pBitmap->GetWidth(), pBitmap->GetHeight(), pBitmap, m_MaskColor, 0, 0,
-        BlendMode::kNormal, m_pClipRgn.Get(), m_bRgbByteOrder);
+    m_pDevice->CompositeMask(m_pTransformer->result().left,
+                             m_pTransformer->result().top, pBitmap->GetWidth(),
+                             pBitmap->GetHeight(), pBitmap, m_MaskColor, 0, 0,
+                             BlendMode::kNormal, m_pClipRgn, m_bRgbByteOrder);
   } else {
     if (m_BitmapAlpha != 255)
       pBitmap->MultiplyAlpha(m_BitmapAlpha);
     m_pDevice->CompositeBitmap(
         m_pTransformer->result().left, m_pTransformer->result().top,
         pBitmap->GetWidth(), pBitmap->GetHeight(), pBitmap, 0, 0,
-        BlendMode::kNormal, m_pClipRgn.Get(), m_bRgbByteOrder);
+        BlendMode::kNormal, m_pClipRgn, m_bRgbByteOrder);
   }
   return false;
 }
diff --git a/core/fxge/dib/cfx_imagerenderer.h b/core/fxge/dib/cfx_imagerenderer.h
index a64fd4a..9c31e05 100644
--- a/core/fxge/dib/cfx_imagerenderer.h
+++ b/core/fxge/dib/cfx_imagerenderer.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -35,6 +35,8 @@
   bool Continue(PauseIndicatorIface* pPause);
 
  private:
+  enum class State : uint8_t { kInitial = 0, kStretching, kTransforming };
+
   RetainPtr<CFX_DIBitmap> const m_pDevice;
   UnownedPtr<const CFX_ClipRgn> const m_pClipRgn;
   const CFX_Matrix m_Matrix;
@@ -43,8 +45,8 @@
   CFX_BitmapComposer m_Composer;
   FX_RECT m_ClipBox;
   const int m_BitmapAlpha;
-  int m_Status = 0;
   uint32_t m_MaskColor;
+  State m_State = State::kInitial;
   const bool m_bRgbByteOrder;
 };
 
diff --git a/core/fxge/dib/cfx_imagestretcher.cpp b/core/fxge/dib/cfx_imagestretcher.cpp
index 1499e8a..1eafa40 100644
--- a/core/fxge/dib/cfx_imagestretcher.cpp
+++ b/core/fxge/dib/cfx_imagestretcher.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,16 +6,14 @@
 
 #include "core/fxge/dib/cfx_imagestretcher.h"
 
-#include <climits>
-#include <tuple>
-
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxge/dib/cfx_dibbase.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/dib/cstretchengine.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/span.h"
 
 namespace {
 
@@ -27,95 +25,85 @@
 
 FXDIB_Format GetStretchedFormat(const CFX_DIBBase& src) {
   FXDIB_Format format = src.GetFormat();
-  if (format == FXDIB_1bppMask)
-    return FXDIB_8bppMask;
-  if (format == FXDIB_1bppRgb)
-    return FXDIB_8bppRgb;
-  if (format == FXDIB_8bppRgb && src.GetPalette())
-    return FXDIB_Rgb;
+  if (format == FXDIB_Format::k1bppMask)
+    return FXDIB_Format::k8bppMask;
+  if (format == FXDIB_Format::k1bppRgb)
+    return FXDIB_Format::k8bppRgb;
+  if (format == FXDIB_Format::k8bppRgb && src.HasPalette())
+    return FXDIB_Format::kRgb;
   return format;
 }
 
-// Returns tuple c, m, y, k
-std::tuple<int, int, int, int> CmykDecode(const uint32_t cmyk) {
-  return std::make_tuple(FXSYS_GetCValue(cmyk), FXSYS_GetMValue(cmyk),
-                         FXSYS_GetYValue(cmyk), FXSYS_GetKValue(cmyk));
+// Builds a new palette with a size of `CFX_DIBBase::kPaletteSize` from the
+// existing palette in `source`. Note: The caller must make sure that the
+// parameters meet the following conditions:
+//   source       - The format must be `FXDIB_Format::k1bppRgb` and it must
+//                  have a palette.
+//   palette_span - The size must be `CFX_DIBBase::kPaletteSize` to be able
+//                  to hold the new palette.
+
+void BuildPaletteFrom1BppSource(const RetainPtr<const CFX_DIBBase>& source,
+                                pdfium::span<FX_ARGB> palette_span) {
+  DCHECK_EQ(FXDIB_Format::k1bppRgb, source->GetFormat());
+  DCHECK(source->HasPalette());
+  DCHECK_EQ(CFX_DIBBase::kPaletteSize, palette_span.size());
+
+  int a0;
+  int r0;
+  int g0;
+  int b0;
+  std::tie(a0, r0, g0, b0) = ArgbDecode(source->GetPaletteArgb(0));
+  int a1;
+  int r1;
+  int g1;
+  int b1;
+  std::tie(a1, r1, g1, b1) = ArgbDecode(source->GetPaletteArgb(1));
+  DCHECK_EQ(255, a0);
+  DCHECK_EQ(255, a1);
+
+  for (int i = 0; i < static_cast<int>(CFX_DIBBase::kPaletteSize); ++i) {
+    int r = r0 + (r1 - r0) * i / 255;
+    int g = g0 + (g1 - g0) * i / 255;
+    int b = b0 + (b1 - b0) * i / 255;
+    palette_span[i] = ArgbEncode(255, r, g, b);
+  }
 }
 
 }  // namespace
 
-CFX_ImageStretcher::CFX_ImageStretcher(ScanlineComposerIface* pDest,
-                                       const RetainPtr<CFX_DIBBase>& pSource,
-                                       int dest_width,
-                                       int dest_height,
-                                       const FX_RECT& bitmap_rect,
-                                       const FXDIB_ResampleOptions& options)
+CFX_ImageStretcher::CFX_ImageStretcher(
+    ScanlineComposerIface* pDest,
+    const RetainPtr<const CFX_DIBBase>& pSource,
+    int dest_width,
+    int dest_height,
+    const FX_RECT& bitmap_rect,
+    const FXDIB_ResampleOptions& options)
     : m_pDest(pDest),
       m_pSource(pSource),
       m_ResampleOptions(options),
       m_DestWidth(dest_width),
       m_DestHeight(dest_height),
       m_ClipRect(bitmap_rect),
-      m_DestFormat(GetStretchedFormat(*pSource)),
-      m_DestBPP(GetBppFromFormat(m_DestFormat)) {
-  ASSERT(m_ClipRect.Valid());
+      m_DestFormat(GetStretchedFormat(*pSource)) {
+  DCHECK(m_ClipRect.Valid());
 }
 
-CFX_ImageStretcher::~CFX_ImageStretcher() {}
+CFX_ImageStretcher::~CFX_ImageStretcher() = default;
 
 bool CFX_ImageStretcher::Start() {
   if (m_DestWidth == 0 || m_DestHeight == 0)
     return false;
 
-  if (m_pSource->GetFormat() == FXDIB_1bppRgb && m_pSource->GetPalette()) {
-    FX_ARGB pal[256];
-    int a0;
-    int r0;
-    int g0;
-    int b0;
-    std::tie(a0, r0, g0, b0) = ArgbDecode(m_pSource->GetPaletteArgb(0));
-    int a1;
-    int r1;
-    int g1;
-    int b1;
-    std::tie(a1, r1, g1, b1) = ArgbDecode(m_pSource->GetPaletteArgb(1));
-    for (int i = 0; i < 256; ++i) {
-      int a = a0 + (a1 - a0) * i / 255;
-      int r = r0 + (r1 - r0) * i / 255;
-      int g = g0 + (g1 - g0) * i / 255;
-      int b = b0 + (b1 - b0) * i / 255;
-      pal[i] = ArgbEncode(a, r, g, b);
-    }
-    if (!m_pDest->SetInfo(m_ClipRect.Width(), m_ClipRect.Height(), m_DestFormat,
-                          pal)) {
-      return false;
-    }
-  } else if (m_pSource->GetFormat() == FXDIB_1bppCmyk &&
-             m_pSource->GetPalette()) {
-    FX_CMYK pal[256];
-    int c0;
-    int m0;
-    int y0;
-    int k0;
-    std::tie(c0, m0, y0, k0) = CmykDecode(m_pSource->GetPaletteArgb(0));
-    int c1;
-    int m1;
-    int y1;
-    int k1;
-    std::tie(c1, m1, y1, k1) = CmykDecode(m_pSource->GetPaletteArgb(1));
-    for (int i = 0; i < 256; ++i) {
-      int c = c0 + (c1 - c0) * i / 255;
-      int m = m0 + (m1 - m0) * i / 255;
-      int y = y0 + (y1 - y0) * i / 255;
-      int k = k0 + (k1 - k0) * i / 255;
-      pal[i] = CmykEncode(c, m, y, k);
-    }
+  if (m_pSource->GetFormat() == FXDIB_Format::k1bppRgb &&
+      m_pSource->HasPalette()) {
+    FX_ARGB pal[CFX_DIBBase::kPaletteSize];
+    BuildPaletteFrom1BppSource(m_pSource, pal);
     if (!m_pDest->SetInfo(m_ClipRect.Width(), m_ClipRect.Height(), m_DestFormat,
                           pal)) {
       return false;
     }
   } else if (!m_pDest->SetInfo(m_ClipRect.Width(), m_ClipRect.Height(),
-                               m_DestFormat, nullptr)) {
+                               m_DestFormat, {})) {
     return false;
   }
   return StartStretch();
@@ -125,14 +113,14 @@
   return ContinueStretch(pPause);
 }
 
-RetainPtr<CFX_DIBBase> CFX_ImageStretcher::source() {
+RetainPtr<const CFX_DIBBase> CFX_ImageStretcher::source() {
   return m_pSource;
 }
 
 bool CFX_ImageStretcher::StartStretch() {
-  m_pStretchEngine = pdfium::MakeUnique<CStretchEngine>(
-      m_pDest.Get(), m_DestFormat, m_DestWidth, m_DestHeight, m_ClipRect,
-      m_pSource, m_ResampleOptions);
+  m_pStretchEngine = std::make_unique<CStretchEngine>(
+      m_pDest, m_DestFormat, m_DestWidth, m_DestHeight, m_ClipRect, m_pSource,
+      m_ResampleOptions);
   m_pStretchEngine->StartStretchHorz();
   if (SourceSizeWithinLimit(m_pSource->GetWidth(), m_pSource->GetHeight())) {
     m_pStretchEngine->Continue(nullptr);
diff --git a/core/fxge/dib/cfx_imagestretcher.h b/core/fxge/dib/cfx_imagestretcher.h
index 1ed9695..7e51dc7 100644
--- a/core/fxge/dib/cfx_imagestretcher.h
+++ b/core/fxge/dib/cfx_imagestretcher.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,11 +10,10 @@
 #include <memory>
 
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxge/dib/fx_dib.h"
 #include "core/fxge/dib/scanlinecomposer_iface.h"
-#include "core/fxge/fx_dib.h"
 
 class CFX_DIBBase;
 class CStretchEngine;
@@ -23,7 +22,7 @@
 class CFX_ImageStretcher {
  public:
   CFX_ImageStretcher(ScanlineComposerIface* pDest,
-                     const RetainPtr<CFX_DIBBase>& pSource,
+                     const RetainPtr<const CFX_DIBBase>& pSource,
                      int dest_width,
                      int dest_height,
                      const FX_RECT& bitmap_rect,
@@ -33,23 +32,20 @@
   bool Start();
   bool Continue(PauseIndicatorIface* pPause);
 
-  RetainPtr<CFX_DIBBase> source();
+  RetainPtr<const CFX_DIBBase> source();
 
  private:
   bool StartStretch();
   bool ContinueStretch(PauseIndicatorIface* pPause);
 
   UnownedPtr<ScanlineComposerIface> const m_pDest;
-  RetainPtr<CFX_DIBBase> m_pSource;
+  RetainPtr<const CFX_DIBBase> const m_pSource;
   std::unique_ptr<CStretchEngine> m_pStretchEngine;
-  std::unique_ptr<uint8_t, FxFreeDeleter> m_pScanline;
-  std::unique_ptr<uint8_t, FxFreeDeleter> m_pMaskScanline;
   const FXDIB_ResampleOptions m_ResampleOptions;
-  int m_DestWidth;
-  int m_DestHeight;
+  const int m_DestWidth;
+  const int m_DestHeight;
   const FX_RECT m_ClipRect;
   const FXDIB_Format m_DestFormat;
-  const int m_DestBPP;
 };
 
 #endif  // CORE_FXGE_DIB_CFX_IMAGESTRETCHER_H_
diff --git a/core/fxge/dib/cfx_imagetransformer.cpp b/core/fxge/dib/cfx_imagetransformer.cpp
index 3afc48c..12bc14d 100644
--- a/core/fxge/dib/cfx_imagetransformer.cpp
+++ b/core/fxge/dib/cfx_imagetransformer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,17 +6,20 @@
 
 #include "core/fxge/dib/cfx_imagetransformer.h"
 
-#include <cmath>
+#include <math.h>
+
+#include <iterator>
 #include <memory>
 #include <utility>
 
+#include "core/fxcrt/fx_system.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/dib/cfx_imagestretcher.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/check.h"
 #include "third_party/base/compiler_specific.h"
+#include "third_party/base/notreached.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
 
 namespace {
 
@@ -24,140 +27,27 @@
 constexpr float kFix16 = 0.05f;
 constexpr uint8_t kOpaqueAlpha = 0xff;
 
-uint8_t bilinear_interpol(const uint8_t* buf,
-                          int row_offset_l,
-                          int row_offset_r,
-                          int src_col_l,
-                          int src_col_r,
-                          int res_x,
-                          int res_y,
-                          int bpp,
-                          int c_offset) {
-  int i_resx = 255 - res_x;
-  int col_bpp_l = src_col_l * bpp;
-  int col_bpp_r = src_col_r * bpp;
-  const uint8_t* buf_u = buf + row_offset_l + c_offset;
-  const uint8_t* buf_d = buf + row_offset_r + c_offset;
+uint8_t BilinearInterpolate(const uint8_t* buf,
+                            const CFX_ImageTransformer::BilinearData& data,
+                            int bpp,
+                            int c_offset) {
+  int i_resx = 255 - data.res_x;
+  int col_bpp_l = data.src_col_l * bpp;
+  int col_bpp_r = data.src_col_r * bpp;
+  const uint8_t* buf_u = buf + data.row_offset_l + c_offset;
+  const uint8_t* buf_d = buf + data.row_offset_r + c_offset;
   const uint8_t* src_pos0 = buf_u + col_bpp_l;
   const uint8_t* src_pos1 = buf_u + col_bpp_r;
   const uint8_t* src_pos2 = buf_d + col_bpp_l;
   const uint8_t* src_pos3 = buf_d + col_bpp_r;
-  uint8_t r_pos_0 = (*src_pos0 * i_resx + *src_pos1 * res_x) >> 8;
-  uint8_t r_pos_1 = (*src_pos2 * i_resx + *src_pos3 * res_x) >> 8;
-  return (r_pos_0 * (255 - res_y) + r_pos_1 * res_y) >> 8;
+  uint8_t r_pos_0 = (*src_pos0 * i_resx + *src_pos1 * data.res_x) >> 8;
+  uint8_t r_pos_1 = (*src_pos2 * i_resx + *src_pos3 * data.res_x) >> 8;
+  return (r_pos_0 * (255 - data.res_y) + r_pos_1 * data.res_y) >> 8;
 }
 
-uint8_t bicubic_interpol(const uint8_t* buf,
-                         uint32_t pitch,
-                         const int pos_pixel[],
-                         const int u_w[],
-                         const int v_w[],
-                         int res_x,
-                         int res_y,
-                         int bpp,
-                         int c_offset) {
-  int s_result = 0;
-  for (int i = 0; i < 4; i++) {
-    int a_result = 0;
-    for (int j = 0; j < 4; j++) {
-      uint8_t val =
-          *(buf + pos_pixel[i + 4] * pitch + pos_pixel[j] * bpp + c_offset);
-      a_result += u_w[j] * val;
-    }
-    s_result += a_result * v_w[i];
-  }
-  s_result >>= 16;
-  return static_cast<uint8_t>(pdfium::clamp(s_result, 0, 255));
-}
-
-void bicubic_get_pos_weight(int pos_pixel[],
-                            int u_w[],
-                            int v_w[],
-                            int src_col_l,
-                            int src_row_l,
-                            int res_x,
-                            int res_y,
-                            int stretch_width,
-                            int stretch_height) {
-  pos_pixel[0] = src_col_l - 1;
-  pos_pixel[1] = src_col_l;
-  pos_pixel[2] = src_col_l + 1;
-  pos_pixel[3] = src_col_l + 2;
-  pos_pixel[4] = src_row_l - 1;
-  pos_pixel[5] = src_row_l;
-  pos_pixel[6] = src_row_l + 1;
-  pos_pixel[7] = src_row_l + 2;
-  for (int i = 0; i < 4; i++) {
-    pos_pixel[i] = pdfium::clamp(pos_pixel[i], 0, stretch_width - 1);
-    pos_pixel[i + 4] = pdfium::clamp(pos_pixel[i + 4], 0, stretch_height - 1);
-  }
-  u_w[0] = SDP_Table[256 + res_x];
-  u_w[1] = SDP_Table[res_x];
-  u_w[2] = SDP_Table[256 - res_x];
-  u_w[3] = SDP_Table[512 - res_x];
-  v_w[0] = SDP_Table[256 + res_y];
-  v_w[1] = SDP_Table[res_y];
-  v_w[2] = SDP_Table[256 - res_y];
-  v_w[3] = SDP_Table[512 - res_y];
-}
-
-FXDIB_Format GetTransformedFormat(const RetainPtr<CFX_DIBBase>& pDrc) {
-  if (pDrc->IsAlphaMask())
-    return FXDIB_8bppMask;
-
-  FXDIB_Format format = pDrc->GetFormat();
-  if (format >= 1025)
-    return FXDIB_Cmyka;
-  if (format <= 32 || format == FXDIB_Argb)
-    return FXDIB_Argb;
-  return FXDIB_Rgba;
-}
-
-void WriteMonoResult(uint32_t r_bgra_cmyk, FXDIB_Format format, uint8_t* dest) {
-  if (format == FXDIB_Rgba) {
-    dest[0] = static_cast<uint8_t>(r_bgra_cmyk >> 24);
-    dest[1] = static_cast<uint8_t>(r_bgra_cmyk >> 16);
-    dest[2] = static_cast<uint8_t>(r_bgra_cmyk >> 8);
-  } else {
-    *reinterpret_cast<uint32_t*>(dest) = r_bgra_cmyk;
-  }
-}
-
-// Let the compiler deduce the type for |func|, which cheaper than specifying it
-// with std::function.
-template <typename F>
-void WriteColorResult(const F& func,
-                      bool bHasAlpha,
-                      FXDIB_Format format,
-                      uint8_t* dest) {
-  uint8_t blue_c = func(0);
-  uint8_t green_m = func(1);
-  uint8_t red_y = func(2);
-
-  uint32_t* dest32 = reinterpret_cast<uint32_t*>(dest);
-  if (bHasAlpha) {
-    if (format == FXDIB_Argb) {
-      *dest32 = FXARGB_TODIB(ArgbEncode(func(3), red_y, green_m, blue_c));
-    } else if (format == FXDIB_Rgba) {
-      dest[0] = blue_c;
-      dest[1] = green_m;
-      dest[2] = red_y;
-    } else {
-      *dest32 = FXCMYK_TODIB(CmykEncode(blue_c, green_m, red_y, func(3)));
-    }
-    return;
-  }
-
-  if (format == FXDIB_Cmyka) {
-    *dest32 = FXCMYK_TODIB(CmykEncode(blue_c, green_m, red_y, func(3)));
-  } else {
-    *dest32 = FXARGB_TODIB(ArgbEncode(kOpaqueAlpha, red_y, green_m, blue_c));
-  }
-}
-
-class CPDF_FixedMatrix {
+class CFX_BilinearMatrix {
  public:
-  explicit CPDF_FixedMatrix(const CFX_Matrix& src)
+  explicit CFX_BilinearMatrix(const CFX_Matrix& src)
       : a(FXSYS_roundf(src.a * kBase)),
         b(FXSYS_roundf(src.b * kBase)),
         c(FXSYS_roundf(src.c * kBase)),
@@ -165,16 +55,22 @@
         e(FXSYS_roundf(src.e * kBase)),
         f(FXSYS_roundf(src.f * kBase)) {}
 
-  void Transform(int x, int y, int* x1, int* y1) const {
-    std::pair<float, float> val = TransformInternal(x, y);
-    *x1 = pdfium::base::saturated_cast<int>(val.first / kBase);
-    *y1 = pdfium::base::saturated_cast<int>(val.second / kBase);
+  void Transform(int x, int y, int* x1, int* y1, int* res_x, int* res_y) const {
+    CFX_PointF val = TransformInternal(CFX_PointF(x, y));
+    *x1 = pdfium::base::saturated_cast<int>(val.x / kBase);
+    *y1 = pdfium::base::saturated_cast<int>(val.y / kBase);
+    *res_x = static_cast<int>(val.x) % kBase;
+    *res_y = static_cast<int>(val.y) % kBase;
+    if (*res_x < 0 && *res_x > -kBase)
+      *res_x = kBase + *res_x;
+    if (*res_y < 0 && *res_y > -kBase)
+      *res_y = kBase + *res_y;
   }
 
- protected:
-  std::pair<float, float> TransformInternal(float x, float y) const {
-    return std::make_pair(a * x + c * y + e + kBase / 2,
-                          b * x + d * y + f + kBase / 2);
+ private:
+  CFX_PointF TransformInternal(CFX_PointF pt) const {
+    return CFX_PointF(a * pt.x + c * pt.y + e + kBase / 2,
+                      b * pt.x + d * pt.y + f + kBase / 2);
   }
 
   const int a;
@@ -185,24 +81,6 @@
   const int f;
 };
 
-class CFX_BilinearMatrix final : public CPDF_FixedMatrix {
- public:
-  explicit CFX_BilinearMatrix(const CFX_Matrix& src) : CPDF_FixedMatrix(src) {}
-
-  void Transform(int x, int y, int* x1, int* y1, int* res_x, int* res_y) const {
-    std::pair<float, float> val = TransformInternal(x, y);
-    *x1 = pdfium::base::saturated_cast<int>(val.first / kBase);
-    *y1 = pdfium::base::saturated_cast<int>(val.second / kBase);
-
-    *res_x = static_cast<int>(val.first) % kBase;
-    *res_y = static_cast<int>(val.second) % kBase;
-    if (*res_x < 0 && *res_x > -kBase)
-      *res_x = kBase + *res_x;
-    if (*res_y < 0 && *res_y > -kBase)
-      *res_y = kBase + *res_y;
-  }
-};
-
 bool InStretchBounds(const FX_RECT& clip_rect, int col, int row) {
   return col >= 0 && col <= clip_rect.Width() && row >= 0 &&
          row <= clip_rect.Height();
@@ -220,14 +98,14 @@
 // Let the compiler deduce the type for |func|, which cheaper than specifying it
 // with std::function.
 template <typename F>
-void DoBilinearLoop(const CFX_ImageTransformer::CalcData& cdata,
+void DoBilinearLoop(const CFX_ImageTransformer::CalcData& calc_data,
                     const FX_RECT& result_rect,
                     const FX_RECT& clip_rect,
                     int increment,
                     const F& func) {
-  CFX_BilinearMatrix matrix_fix(cdata.matrix);
+  CFX_BilinearMatrix matrix_fix(calc_data.matrix);
   for (int row = 0; row < result_rect.Height(); row++) {
-    uint8_t* dest = cdata.bitmap->GetWritableScanline(row);
+    uint8_t* dest = calc_data.bitmap->GetWritableScanline(row).data();
     for (int col = 0; col < result_rect.Width(); col++) {
       CFX_ImageTransformer::BilinearData d;
       d.res_x = 0;
@@ -241,64 +119,8 @@
         d.src_col_r = d.src_col_l + 1;
         d.src_row_r = d.src_row_l + 1;
         AdjustCoords(clip_rect, &d.src_col_r, &d.src_row_r);
-        d.row_offset_l = d.src_row_l * cdata.pitch;
-        d.row_offset_r = d.src_row_r * cdata.pitch;
-        func(d, dest);
-      }
-      dest += increment;
-    }
-  }
-}
-
-// Let the compiler deduce the type for |func|, which cheaper than specifying it
-// with std::function.
-template <typename F>
-void DoBicubicLoop(const CFX_ImageTransformer::CalcData& cdata,
-                   const FX_RECT& result_rect,
-                   const FX_RECT& clip_rect,
-                   int increment,
-                   const F& func) {
-  CFX_BilinearMatrix matrix_fix(cdata.matrix);
-  for (int row = 0; row < result_rect.Height(); row++) {
-    uint8_t* dest = cdata.bitmap->GetWritableScanline(row);
-    for (int col = 0; col < result_rect.Width(); col++) {
-      CFX_ImageTransformer::BicubicData d;
-      d.res_x = 0;
-      d.res_y = 0;
-      d.src_col_l = 0;
-      d.src_row_l = 0;
-      matrix_fix.Transform(col, row, &d.src_col_l, &d.src_row_l, &d.res_x,
-                           &d.res_y);
-      if (LIKELY(InStretchBounds(clip_rect, d.src_col_l, d.src_row_l))) {
-        AdjustCoords(clip_rect, &d.src_col_l, &d.src_row_l);
-        bicubic_get_pos_weight(d.pos_pixel, d.u_w, d.v_w, d.src_col_l,
-                               d.src_row_l, d.res_x, d.res_y, clip_rect.Width(),
-                               clip_rect.Height());
-        func(d, dest);
-      }
-      dest += increment;
-    }
-  }
-}
-
-// Let the compiler deduce the type for |func|, which cheaper than specifying it
-// with std::function.
-template <typename F>
-void DoDownSampleLoop(const CFX_ImageTransformer::CalcData& cdata,
-                      const FX_RECT& result_rect,
-                      const FX_RECT& clip_rect,
-                      int increment,
-                      const F& func) {
-  CPDF_FixedMatrix matrix_fix(cdata.matrix);
-  for (int row = 0; row < result_rect.Height(); row++) {
-    uint8_t* dest = cdata.bitmap->GetWritableScanline(row);
-    for (int col = 0; col < result_rect.Width(); col++) {
-      CFX_ImageTransformer::DownSampleData d;
-      d.src_col = 0;
-      d.src_row = 0;
-      matrix_fix.Transform(col, row, &d.src_col, &d.src_row);
-      if (LIKELY(InStretchBounds(clip_rect, d.src_col, d.src_row))) {
-        AdjustCoords(clip_rect, &d.src_col, &d.src_row);
+        d.row_offset_l = d.src_row_l * calc_data.pitch;
+        d.row_offset_r = d.src_row_r * calc_data.pitch;
         func(d, dest);
       }
       dest += increment;
@@ -308,10 +130,11 @@
 
 }  // namespace
 
-CFX_ImageTransformer::CFX_ImageTransformer(const RetainPtr<CFX_DIBBase>& pSrc,
-                                           const CFX_Matrix& matrix,
-                                           const FXDIB_ResampleOptions& options,
-                                           const FX_RECT* pClip)
+CFX_ImageTransformer::CFX_ImageTransformer(
+    const RetainPtr<const CFX_DIBBase>& pSrc,
+    const CFX_Matrix& matrix,
+    const FXDIB_ResampleOptions& options,
+    const FX_RECT* pClip)
     : m_pSrc(pSrc), m_matrix(matrix), m_ResampleOptions(options) {
   FX_RECT result_rect = m_matrix.GetUnitRect().GetClosestRect();
   FX_RECT result_clip = result_rect;
@@ -328,13 +151,13 @@
     int dest_width = result_rect.Width();
     int dest_height = result_rect.Height();
     result_clip.Offset(-result_rect.left, -result_rect.top);
-    result_clip = FXDIB_SwapClipBox(result_clip, dest_width, dest_height,
-                                    m_matrix.c > 0, m_matrix.b < 0);
-    m_Stretcher = pdfium::MakeUnique<CFX_ImageStretcher>(
+    result_clip = result_clip.SwappedClipBox(dest_width, dest_height,
+                                             m_matrix.c > 0, m_matrix.b < 0);
+    m_Stretcher = std::make_unique<CFX_ImageStretcher>(
         &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) {
@@ -343,11 +166,11 @@
     int dest_height = static_cast<int>(m_matrix.d > 0 ? -ceil(m_matrix.d)
                                                       : -floor(m_matrix.d));
     result_clip.Offset(-result_rect.left, -result_rect.top);
-    m_Stretcher = pdfium::MakeUnique<CFX_ImageStretcher>(
+    m_Stretcher = std::make_unique<CFX_ImageStretcher>(
         &m_Storer, m_pSrc, dest_width, dest_height, result_clip,
         m_ResampleOptions);
     m_Stretcher->Start();
-    m_type = kNormal;
+    m_type = StretchType::kNormal;
     return;
   }
 
@@ -373,36 +196,36 @@
 
   m_dest2stretch = dest_to_strech;
   m_StretchClip = stretch_clip;
-  m_Stretcher = pdfium::MakeUnique<CFX_ImageStretcher>(
+  m_Stretcher = std::make_unique<CFX_ImageStretcher>(
       &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) {
@@ -417,45 +240,28 @@
     return;
 
   auto pTransformed = pdfium::MakeRetain<CFX_DIBitmap>();
-  FXDIB_Format format = GetTransformedFormat(m_Stretcher->source());
+  FXDIB_Format format = m_Stretcher->source()->IsMaskFormat()
+                            ? FXDIB_Format::k8bppMask
+                            : FXDIB_Format::kArgb;
   if (!pTransformed->Create(m_result.Width(), m_result.Height(), format))
     return;
 
-  const auto& pSrcMask = m_Storer.GetBitmap()->m_pAlphaMask;
-  const uint8_t* pSrcMaskBuf = pSrcMask ? pSrcMask->GetBuffer() : nullptr;
-
-  pTransformed->Clear(0);
-  auto& pDestMask = pTransformed->m_pAlphaMask;
-  if (pDestMask)
-    pDestMask->Clear(0);
-
   CFX_Matrix result2stretch(1.0f, 0.0f, 0.0f, 1.0f, m_result.left,
                             m_result.top);
   result2stretch.Concat(m_dest2stretch);
   result2stretch.Translate(-m_StretchClip.left, -m_StretchClip.top);
-  if (!pSrcMaskBuf && pDestMask) {
-    pDestMask->Clear(0xff000000);
-  } else if (pDestMask) {
-    CalcData cdata = {
-        pDestMask.Get(),
-        result2stretch,
-        pSrcMaskBuf,
-        m_Storer.GetBitmap()->m_pAlphaMask->GetPitch(),
-    };
-    CalcMask(cdata);
-  }
 
-  CalcData cdata = {pTransformed.Get(), result2stretch,
-                    m_Storer.GetBitmap()->GetBuffer(),
-                    m_Storer.GetBitmap()->GetPitch()};
-  if (m_Storer.GetBitmap()->IsAlphaMask()) {
-    CalcAlpha(cdata);
+  CalcData calc_data = {pTransformed.Get(), result2stretch,
+                        m_Storer.GetBitmap()->GetBuffer().data(),
+                        m_Storer.GetBitmap()->GetPitch()};
+  if (m_Storer.GetBitmap()->IsMaskFormat()) {
+    CalcAlpha(calc_data);
   } else {
     int Bpp = m_Storer.GetBitmap()->GetBPP() / 8;
     if (Bpp == 1)
-      CalcMono(cdata, format);
+      CalcMono(calc_data);
     else
-      CalcColor(cdata, format, Bpp);
+      CalcColor(calc_data, format, Bpp);
   }
   m_Storer.Replace(std::move(pTransformed));
 }
@@ -464,140 +270,68 @@
   return m_Storer.Detach();
 }
 
-void CFX_ImageTransformer::CalcMask(const CalcData& cdata) {
-  if (IsBilinear()) {
-    auto func = [&cdata](const BilinearData& data, uint8_t* dest) {
-      *dest = bilinear_interpol(cdata.buf, data.row_offset_l, data.row_offset_r,
-                                data.src_col_l, data.src_col_r, data.res_x,
-                                data.res_y, 1, 0);
-    };
-    DoBilinearLoop(cdata, m_result, m_StretchClip, 1, func);
-  } else if (IsBiCubic()) {
-    auto func = [&cdata](const BicubicData& data, uint8_t* dest) {
-      *dest = bicubic_interpol(cdata.buf, cdata.pitch, data.pos_pixel, data.u_w,
-                               data.v_w, data.res_x, data.res_y, 1, 0);
-    };
-    DoBicubicLoop(cdata, m_result, m_StretchClip, 1, func);
-  } else {
-    auto func = [&cdata](const DownSampleData& data, uint8_t* dest) {
-      *dest = cdata.buf[data.src_row * cdata.pitch + data.src_col];
-    };
-    DoDownSampleLoop(cdata, m_result, m_StretchClip, 1, func);
-  }
+void CFX_ImageTransformer::CalcAlpha(const CalcData& calc_data) {
+  auto func = [&calc_data](const BilinearData& data, uint8_t* dest) {
+    *dest = BilinearInterpolate(calc_data.buf, data, 1, 0);
+  };
+  DoBilinearLoop(calc_data, m_result, m_StretchClip, 1, func);
 }
 
-void CFX_ImageTransformer::CalcAlpha(const CalcData& cdata) {
-  if (IsBilinear()) {
-    auto func = [&cdata](const BilinearData& data, uint8_t* dest) {
-      *dest = bilinear_interpol(cdata.buf, data.row_offset_l, data.row_offset_r,
-                                data.src_col_l, data.src_col_r, data.res_x,
-                                data.res_y, 1, 0);
-    };
-    DoBilinearLoop(cdata, m_result, m_StretchClip, 1, func);
-  } else if (IsBiCubic()) {
-    auto func = [&cdata](const BicubicData& data, uint8_t* dest) {
-      *dest = bicubic_interpol(cdata.buf, cdata.pitch, data.pos_pixel, data.u_w,
-                               data.v_w, data.res_x, data.res_y, 1, 0);
-    };
-    DoBicubicLoop(cdata, m_result, m_StretchClip, 1, func);
-  } else {
-    auto func = [&cdata](const DownSampleData& data, uint8_t* dest) {
-      const uint8_t* src_pixel =
-          cdata.buf + cdata.pitch * data.src_row + data.src_col;
-      *dest = *src_pixel;
-    };
-    DoDownSampleLoop(cdata, m_result, m_StretchClip, 1, func);
-  }
-}
-
-void CFX_ImageTransformer::CalcMono(const CalcData& cdata,
-                                    FXDIB_Format format) {
+void CFX_ImageTransformer::CalcMono(const CalcData& calc_data) {
   uint32_t argb[256];
-  FX_ARGB* pPal = m_Storer.GetBitmap()->GetPalette();
-  if (pPal) {
-    for (size_t i = 0; i < FX_ArraySize(argb); i++)
-      argb[i] = pPal[i];
-  } else if (m_Storer.GetBitmap()->IsCmykImage()) {
-    for (size_t i = 0; i < FX_ArraySize(argb); i++)
-      argb[i] = 255 - i;
+  if (m_Storer.GetBitmap()->HasPalette()) {
+    pdfium::span<const uint32_t> palette =
+        m_Storer.GetBitmap()->GetPaletteSpan();
+    for (size_t i = 0; i < std::size(argb); i++)
+      argb[i] = palette[i];
   } else {
-    for (size_t i = 0; i < FX_ArraySize(argb); i++)
-      argb[i] = 0xff000000 | (i * 0x010101);
+    for (size_t i = 0; i < std::size(argb); i++) {
+      uint32_t v = static_cast<uint32_t>(i);
+      argb[i] = ArgbEncode(0xff, v, v, v);
+    }
   }
-  int destBpp = cdata.bitmap->GetBPP() / 8;
-  if (IsBilinear()) {
-    auto func = [&cdata, format, &argb](const BilinearData& data,
-                                        uint8_t* dest) {
-      uint8_t idx = bilinear_interpol(
-          cdata.buf, data.row_offset_l, data.row_offset_r, data.src_col_l,
-          data.src_col_r, data.res_x, data.res_y, 1, 0);
-      uint32_t r_bgra_cmyk = argb[idx];
-      WriteMonoResult(r_bgra_cmyk, format, dest);
-    };
-    DoBilinearLoop(cdata, m_result, m_StretchClip, destBpp, func);
-  } else if (IsBiCubic()) {
-    auto func = [&cdata, format, &argb](const BicubicData& data,
-                                        uint8_t* dest) {
-      uint32_t r_bgra_cmyk = argb[bicubic_interpol(
-          cdata.buf, cdata.pitch, data.pos_pixel, data.u_w, data.v_w,
-          data.res_x, data.res_y, 1, 0)];
-      WriteMonoResult(r_bgra_cmyk, format, dest);
-    };
-    DoBicubicLoop(cdata, m_result, m_StretchClip, destBpp, func);
-  } else {
-    auto func = [&cdata, format, &argb](const DownSampleData& data,
-                                        uint8_t* dest) {
-      uint32_t r_bgra_cmyk =
-          argb[cdata.buf[data.src_row * cdata.pitch + data.src_col]];
-      WriteMonoResult(r_bgra_cmyk, format, dest);
-    };
-    DoDownSampleLoop(cdata, m_result, m_StretchClip, destBpp, func);
-  }
+  int destBpp = calc_data.bitmap->GetBPP() / 8;
+  auto func = [&calc_data, &argb](const BilinearData& data, uint8_t* dest) {
+    uint8_t idx = BilinearInterpolate(calc_data.buf, data, 1, 0);
+    *reinterpret_cast<uint32_t*>(dest) = argb[idx];
+  };
+  DoBilinearLoop(calc_data, m_result, m_StretchClip, destBpp, func);
 }
 
-void CFX_ImageTransformer::CalcColor(const CalcData& cdata,
+void CFX_ImageTransformer::CalcColor(const CalcData& calc_data,
                                      FXDIB_Format format,
                                      int Bpp) {
-  bool bHasAlpha = m_Storer.GetBitmap()->HasAlpha();
-  int destBpp = cdata.bitmap->GetBPP() / 8;
-  if (IsBilinear()) {
-    auto func = [&cdata, format, Bpp, bHasAlpha](const BilinearData& data,
-                                                 uint8_t* dest) {
-      auto bilinear_interpol_func = [&cdata, &data, Bpp](int offset) {
-        return bilinear_interpol(
-            cdata.buf, data.row_offset_l, data.row_offset_r, data.src_col_l,
-            data.src_col_r, data.res_x, data.res_y, Bpp, offset);
-      };
-      WriteColorResult(bilinear_interpol_func, bHasAlpha, format, dest);
+  DCHECK(format == FXDIB_Format::k8bppMask || format == FXDIB_Format::kArgb);
+  const int destBpp = calc_data.bitmap->GetBPP() / 8;
+  if (!m_Storer.GetBitmap()->IsAlphaFormat()) {
+    auto func = [&calc_data, Bpp](const BilinearData& data, uint8_t* dest) {
+      uint8_t b = BilinearInterpolate(calc_data.buf, data, Bpp, 0);
+      uint8_t g = BilinearInterpolate(calc_data.buf, data, Bpp, 1);
+      uint8_t r = BilinearInterpolate(calc_data.buf, data, Bpp, 2);
+      *reinterpret_cast<uint32_t*>(dest) = ArgbEncode(kOpaqueAlpha, r, g, b);
     };
-    DoBilinearLoop(cdata, m_result, m_StretchClip, destBpp, func);
-  } else if (IsBiCubic()) {
-    auto func = [&cdata, format, Bpp, bHasAlpha](const BicubicData& data,
-                                                 uint8_t* dest) {
-      auto bicubic_interpol_func = [&cdata, &data, Bpp](int offset) {
-        return bicubic_interpol(cdata.buf, cdata.pitch, data.pos_pixel,
-                                data.u_w, data.v_w, data.res_x, data.res_y, Bpp,
-                                offset);
-      };
-      WriteColorResult(bicubic_interpol_func, bHasAlpha, format, dest);
-    };
-    DoBicubicLoop(cdata, m_result, m_StretchClip, destBpp, func);
-  } else {
-    auto func = [&cdata, format, bHasAlpha, Bpp](const DownSampleData& data,
-                                                 uint8_t* dest) {
-      const uint8_t* src_pos =
-          cdata.buf + data.src_row * cdata.pitch + data.src_col * Bpp;
-      auto sample_func = [src_pos](int offset) { return src_pos[offset]; };
-      WriteColorResult(sample_func, bHasAlpha, format, dest);
-    };
-    DoDownSampleLoop(cdata, m_result, m_StretchClip, destBpp, func);
+    DoBilinearLoop(calc_data, m_result, m_StretchClip, destBpp, func);
+    return;
   }
-}
 
-bool CFX_ImageTransformer::IsBilinear() const {
-  return !IsBiCubic();
-}
+  if (format == FXDIB_Format::kArgb) {
+    auto func = [&calc_data, Bpp](const BilinearData& data, uint8_t* dest) {
+      uint8_t b = BilinearInterpolate(calc_data.buf, data, Bpp, 0);
+      uint8_t g = BilinearInterpolate(calc_data.buf, data, Bpp, 1);
+      uint8_t r = BilinearInterpolate(calc_data.buf, data, Bpp, 2);
+      uint8_t alpha = BilinearInterpolate(calc_data.buf, data, Bpp, 3);
+      *reinterpret_cast<uint32_t*>(dest) = ArgbEncode(alpha, r, g, b);
+    };
+    DoBilinearLoop(calc_data, m_result, m_StretchClip, destBpp, func);
+    return;
+  }
 
-bool CFX_ImageTransformer::IsBiCubic() const {
-  return m_ResampleOptions.bInterpolateBicubic;
+  auto func = [&calc_data, Bpp](const BilinearData& data, uint8_t* dest) {
+    uint8_t c = BilinearInterpolate(calc_data.buf, data, Bpp, 0);
+    uint8_t m = BilinearInterpolate(calc_data.buf, data, Bpp, 1);
+    uint8_t y = BilinearInterpolate(calc_data.buf, data, Bpp, 2);
+    uint8_t k = BilinearInterpolate(calc_data.buf, data, Bpp, 3);
+    *reinterpret_cast<uint32_t*>(dest) = FXCMYK_TODIB(CmykEncode(c, m, y, k));
+  };
+  DoBilinearLoop(calc_data, m_result, m_StretchClip, destBpp, func);
 }
diff --git a/core/fxge/dib/cfx_imagetransformer.h b/core/fxge/dib/cfx_imagetransformer.h
index 3372003..2329e9b 100644
--- a/core/fxge/dib/cfx_imagetransformer.h
+++ b/core/fxge/dib/cfx_imagetransformer.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -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;
@@ -31,31 +32,14 @@
     int row_offset_r;
   };
 
-  struct BicubicData {
-    int res_x;
-    int res_y;
-    int src_col_l;
-    int src_row_l;
-    int src_col_r;
-    int src_row_r;
-    int pos_pixel[8];
-    int u_w[4];
-    int v_w[4];
-  };
-
-  struct DownSampleData {
-    int src_col;
-    int src_row;
-  };
-
   struct CalcData {
-    CFX_DIBitmap* bitmap;
+    UNOWNED_PTR_EXCLUSION CFX_DIBitmap* bitmap;  // POD struct.
     const CFX_Matrix& matrix;
     const uint8_t* buf;
     uint32_t pitch;
   };
 
-  CFX_ImageTransformer(const RetainPtr<CFX_DIBBase>& pSrc,
+  CFX_ImageTransformer(const RetainPtr<const CFX_DIBBase>& pSrc,
                        const CFX_Matrix& matrix,
                        const FXDIB_ResampleOptions& options,
                        const FX_RECT* pClip);
@@ -67,7 +51,7 @@
   RetainPtr<CFX_DIBitmap> DetachBitmap();
 
  private:
-  enum StretchType {
+  enum class StretchType {
     kNone,
     kNormal,
     kRotate,
@@ -77,15 +61,11 @@
   void ContinueRotate(PauseIndicatorIface* pPause);
   void ContinueOther(PauseIndicatorIface* pPause);
 
-  void CalcMask(const CalcData& cdata);
-  void CalcAlpha(const CalcData& cdata);
-  void CalcMono(const CalcData& cdata, FXDIB_Format format);
-  void CalcColor(const CalcData& cdata, FXDIB_Format format, int Bpp);
+  void CalcAlpha(const CalcData& calc_data);
+  void CalcMono(const CalcData& calc_data);
+  void CalcColor(const CalcData& calc_data, FXDIB_Format format, int Bpp);
 
-  bool IsBilinear() const;
-  bool IsBiCubic() const;
-
-  RetainPtr<CFX_DIBBase> const m_pSrc;
+  RetainPtr<const CFX_DIBBase> const m_pSrc;
   const CFX_Matrix m_matrix;
   FX_RECT m_StretchClip;
   FX_RECT m_result;
@@ -93,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.cpp b/core/fxge/dib/cfx_scanlinecompositor.cpp
index 3c90d96..e9086b7 100644
--- a/core/fxge/dib/cfx_scanlinecompositor.cpp
+++ b/core/fxge/dib/cfx_scanlinecompositor.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,15 +6,15 @@
 
 #include "core/fxge/dib/cfx_scanlinecompositor.h"
 
+#include <string.h>
+
 #include <algorithm>
 
-#include "core/fxge/dib/cfx_cmyk_to_srgb.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 
-#define FX_CCOLOR(val) (255 - (val))
 #define FXDIB_ALPHA_UNION(dest, src) ((dest) + (src) - (dest) * (src) / 255)
-#define FXARGB_COPY(dest, src)                    \
-  *(dest) = *(src), *((dest) + 1) = *((src) + 1), \
-  *((dest) + 2) = *((src) + 2), *((dest) + 3) = *((src) + 3)
 #define FXARGB_RGBORDERCOPY(dest, src)                  \
   *((dest) + 3) = *((src) + 3), *(dest) = *((src) + 2), \
              *((dest) + 1) = *((src) + 1), *((dest) + 2) = *((src))
@@ -196,11 +196,14 @@
   return result / 255;
 }
 
-void CompositeRow_AlphaToMask(uint8_t* dest_scan,
-                              const uint8_t* src_scan,
+void CompositeRow_AlphaToMask(pdfium::span<uint8_t> dest_span,
+                              pdfium::span<const uint8_t> src_span,
                               int pixel_count,
-                              const uint8_t* clip_scan,
+                              pdfium::span<const uint8_t> clip_span,
                               uint8_t stride) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   src_scan += stride - 1;
   for (int col = 0; col < pixel_count; ++col) {
     int src_alpha = GetAlpha(*src_scan, clip_scan, col);
@@ -214,10 +217,11 @@
   }
 }
 
-void CompositeRow_Rgb2Mask(uint8_t* dest_scan,
-                           const uint8_t* src_scan,
+void CompositeRow_Rgb2Mask(pdfium::span<uint8_t> dest_span,
                            int width,
-                           const uint8_t* clip_scan) {
+                           pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   if (!clip_scan) {
     memset(dest_scan, 0xff, width);
     return;
@@ -256,83 +260,37 @@
   return gray;
 }
 
-void CompositeRow_Argb2Graya(uint8_t* dest_scan,
-                             const uint8_t* src_scan,
-                             int pixel_count,
-                             BlendMode blend_type,
-                             const uint8_t* clip_scan,
-                             const uint8_t* src_alpha_scan,
-                             uint8_t* dst_alpha_scan) {
-  uint8_t offset = src_alpha_scan ? 3 : 4;
-  for (int col = 0; col < pixel_count; ++col) {
-    const uint8_t* alpha_scan =
-        src_alpha_scan ? src_alpha_scan++ : &src_scan[3];
-    uint8_t back_alpha = *dst_alpha_scan;
-    if (back_alpha == 0) {
-      int src_alpha = GetAlpha(*alpha_scan, clip_scan, col);
-      if (src_alpha) {
-        *dest_scan = GetGray(src_scan);
-        *dst_alpha_scan = src_alpha;
-      }
-      ++dest_scan;
-      ++dst_alpha_scan;
-      src_scan += offset;
-      continue;
-    }
-    uint8_t src_alpha = GetAlpha(*alpha_scan, clip_scan, col);
-    if (src_alpha == 0) {
-      ++dest_scan;
-      ++dst_alpha_scan;
-      src_scan += offset;
-      continue;
-    }
-    *dst_alpha_scan = FXDIB_ALPHA_UNION(back_alpha, src_alpha);
-    int alpha_ratio = src_alpha * 255 / (*dst_alpha_scan);
-    uint8_t gray = GetGray(src_scan);
-    // TODO(npm): Does this if really need src_alpha_scan or was that a bug?
-    if (blend_type != BlendMode::kNormal && src_alpha_scan) {
-      if (IsNonSeparableBlendMode(blend_type))
-        gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan;
-      else
-        gray = Blend(blend_type, *dest_scan, gray);
-    }
-    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio);
-    ++dest_scan;
-    ++dst_alpha_scan;
-    src_scan += offset;
-  }
-}
-
-void CompositeRow_Argb2Gray(uint8_t* dest_scan,
-                            const uint8_t* src_scan,
+void CompositeRow_Argb2Gray(pdfium::span<uint8_t> dest_span,
+                            pdfium::span<const uint8_t> src_span,
                             int pixel_count,
                             BlendMode blend_type,
-                            const uint8_t* clip_scan,
-                            const uint8_t* src_alpha_scan) {
-  uint8_t gray;
-  uint8_t offset = src_alpha_scan ? 3 : 4;
+                            pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
+  constexpr size_t kOffset = 4;
   for (int col = 0; col < pixel_count; ++col) {
-    const uint8_t* alpha_scan =
-        src_alpha_scan ? src_alpha_scan++ : &src_scan[3];
-    int src_alpha = GetAlpha(*alpha_scan, clip_scan, col);
+    int src_alpha = GetAlpha(src_scan[3], clip_scan, col);
     if (src_alpha) {
-      gray = GetGrayWithBlend(src_scan, dest_scan, blend_type);
+      uint8_t gray = GetGrayWithBlend(src_scan, dest_scan, blend_type);
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, src_alpha);
     }
     ++dest_scan;
-    src_scan += offset;
+    src_scan += kOffset;
   }
 }
 
-void CompositeRow_Rgb2Gray(uint8_t* dest_scan,
-                           const uint8_t* src_scan,
+void CompositeRow_Rgb2Gray(pdfium::span<uint8_t> dest_span,
+                           pdfium::span<const uint8_t> src_span,
                            int src_Bpp,
                            int pixel_count,
                            BlendMode blend_type,
-                           const uint8_t* clip_scan) {
-  uint8_t gray;
+                           pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   for (int col = 0; col < pixel_count; ++col) {
-    gray = GetGrayWithBlend(src_scan, dest_scan, blend_type);
+    uint8_t gray = GetGrayWithBlend(src_scan, dest_scan, blend_type);
     if (clip_scan && clip_scan[col] < 255)
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, clip_scan[col]);
     else
@@ -342,106 +300,38 @@
   }
 }
 
-void CompositeRow_Rgb2Graya(uint8_t* dest_scan,
-                            const uint8_t* src_scan,
-                            int src_Bpp,
+void CompositeRow_Argb2Argb(pdfium::span<uint8_t> dest_span,
+                            pdfium::span<const uint8_t> src_span,
                             int pixel_count,
                             BlendMode blend_type,
-                            const uint8_t* clip_scan,
-                            uint8_t* dest_alpha_scan) {
-  for (int col = 0; col < pixel_count; ++col) {
-    if (blend_type != BlendMode::kNormal && *dest_alpha_scan == 0) {
-      *dest_scan = GetGray(src_scan);
-      ++dest_scan;
-      ++dest_alpha_scan;
-      src_scan += src_Bpp;
-      continue;
-    }
-    int src_alpha = clip_scan ? clip_scan[col] : 255;
-    if (src_alpha == 255) {
-      *dest_scan = GetGrayWithBlend(src_scan, dest_scan, blend_type);
-      ++dest_scan;
-      *dest_alpha_scan = 255;
-      ++dest_alpha_scan;
-      src_scan += src_Bpp;
-      continue;
-    }
-    if (src_alpha == 0) {
-      ++dest_scan;
-      ++dest_alpha_scan;
-      src_scan += src_Bpp;
-      continue;
-    }
-    int back_alpha = *dest_alpha_scan;
-    uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255;
-    *dest_alpha_scan = dest_alpha;
-    ++dest_alpha_scan;
-    int alpha_ratio = src_alpha * 255 / dest_alpha;
-    uint8_t gray = GetGrayWithBlend(src_scan, dest_scan, blend_type);
-    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio);
-    ++dest_scan;
-    src_scan += src_Bpp;
-  }
-}
-
-void CompositeRow_Argb2Argb(uint8_t* dest_scan,
-                            const uint8_t* src_scan,
-                            int pixel_count,
-                            BlendMode blend_type,
-                            const uint8_t* clip_scan,
-                            uint8_t* dest_alpha_scan,
-                            const uint8_t* src_alpha_scan) {
+                            pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   int blended_colors[3];
-  uint8_t dest_offset = dest_alpha_scan ? 3 : 4;
-  uint8_t src_offset = src_alpha_scan ? 3 : 4;
+  constexpr size_t kOffset = 4;
   bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
-  bool has_src = !!src_alpha_scan;
-  bool has_dest = !!dest_alpha_scan;
   for (int col = 0; col < pixel_count; ++col) {
-    uint8_t back_alpha = has_dest ? *dest_alpha_scan : dest_scan[3];
-    const uint8_t* alpha_source = has_src ? src_alpha_scan++ : &src_scan[3];
-    uint8_t src_alpha = GetAlpha(*alpha_source, clip_scan, col);
+    uint8_t back_alpha = dest_scan[3];
+    uint8_t src_alpha = GetAlpha(src_scan[3], clip_scan, col);
     if (back_alpha == 0) {
-      if (!has_dest && !has_src) {
-        if (clip_scan) {
-          FXARGB_SETDIB(dest_scan, (FXARGB_GETDIB(src_scan) & 0xffffff) |
-                                       (src_alpha << 24));
-        } else {
-          FXARGB_COPY(dest_scan, src_scan);
-        }
-      } else if (has_dest) {
-        *dest_alpha_scan = src_alpha;
-        for (int i = 0; i < 3; ++i) {
-          *dest_scan = *src_scan++;
-          ++dest_scan;
-        }
-        ++dest_alpha_scan;
-        if (!has_src)
-          ++src_scan;
+      if (clip_scan) {
+        FXARGB_SETDIB(dest_scan,
+                      (FXARGB_GETDIB(src_scan) & 0xffffff) | (src_alpha << 24));
       } else {
-        FXARGB_SETDIB(dest_scan, ArgbEncode((src_alpha << 24), src_scan[2],
-                                            src_scan[1], *src_scan));
+        memcpy(dest_scan, src_scan, 4);
       }
-      if (!has_dest) {
-        dest_scan += dest_offset;
-        src_scan += src_offset;
-      }
+      dest_scan += kOffset;
+      src_scan += kOffset;
       continue;
     }
     if (src_alpha == 0) {
-      dest_scan += dest_offset;
-      src_scan += src_offset;
-      if (has_dest)
-        ++dest_alpha_scan;
+      dest_scan += kOffset;
+      src_scan += kOffset;
       continue;
     }
     uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255;
-    if (has_dest) {
-      *dest_alpha_scan = dest_alpha;
-      ++dest_alpha_scan;
-    } else {
-      dest_scan[3] = dest_alpha;
-    }
+    dest_scan[3] = dest_alpha;
     int alpha_ratio = src_alpha * 255 / dest_alpha;
     if (bNonseparableBlend)
       RGB_Blend(blend_type, src_scan, dest_scan, blended_colors);
@@ -458,42 +348,32 @@
       ++dest_scan;
       ++src_scan;
     }
-    if (!has_dest)
-      ++dest_scan;
-    if (!has_src)
-      ++src_scan;
+    ++dest_scan;
+    ++src_scan;
   }
 }
 
-void CompositeRow_Rgb2Argb_Blend_NoClip(uint8_t* dest_scan,
-                                        const uint8_t* src_scan,
+void CompositeRow_Rgb2Argb_Blend_NoClip(pdfium::span<uint8_t> dest_span,
+                                        pdfium::span<const uint8_t> src_span,
                                         int width,
                                         BlendMode blend_type,
-                                        int src_Bpp,
-                                        uint8_t* dest_alpha_scan) {
+                                        int src_Bpp) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
   int blended_colors[3];
   bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   int src_gap = src_Bpp - 3;
   for (int col = 0; col < width; ++col) {
-    uint8_t* dest_alpha = dest_alpha_scan ? dest_alpha_scan : &dest_scan[3];
+    uint8_t* dest_alpha = &dest_scan[3];
     uint8_t back_alpha = *dest_alpha;
     if (back_alpha == 0) {
-      if (dest_alpha_scan) {
-        for (int i = 0; i < 3; ++i) {
-          *dest_scan = *src_scan++;
-          ++dest_scan;
-        }
-        *dest_alpha_scan = 0xff;
-        ++dest_alpha_scan;
+      if (src_Bpp == 4) {
+        FXARGB_SETDIB(dest_scan, 0xff000000 | FXARGB_GETDIB(src_scan));
       } else {
-        if (src_Bpp == 4) {
-          FXARGB_SETDIB(dest_scan, 0xff000000 | FXARGB_GETDIB(src_scan));
-        } else {
-          FXARGB_SETDIB(dest_scan, ArgbEncode(0xff, src_scan[2], src_scan[1],
-                                              src_scan[0]));
-        }
-        dest_scan += 4;
+        FXARGB_SETDIB(dest_scan,
+                      ArgbEncode(0xff, src_scan[2], src_scan[1], src_scan[0]));
       }
+      dest_scan += 4;
       src_scan += src_Bpp;
       continue;
     }
@@ -509,52 +389,40 @@
       ++dest_scan;
       ++src_scan;
     }
-    if (dest_alpha_scan)
-      ++dest_alpha_scan;
-    else
-      ++dest_scan;
+    ++dest_scan;
     src_scan += src_gap;
   }
 }
 
-void CompositeRow_Rgb2Argb_Blend_Clip(uint8_t* dest_scan,
-                                      const uint8_t* src_scan,
+void CompositeRow_Rgb2Argb_Blend_Clip(pdfium::span<uint8_t> dest_span,
+                                      pdfium::span<const uint8_t> src_span,
                                       int width,
                                       BlendMode blend_type,
                                       int src_Bpp,
-                                      const uint8_t* clip_scan,
-                                      uint8_t* dest_alpha_scan) {
+                                      pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   int blended_colors[3];
   bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   int src_gap = src_Bpp - 3;
-  bool has_dest = !!dest_alpha_scan;
   for (int col = 0; col < width; ++col) {
     int src_alpha = *clip_scan++;
-    uint8_t back_alpha = has_dest ? *dest_alpha_scan : dest_scan[3];
+    uint8_t back_alpha = dest_scan[3];
     if (back_alpha == 0) {
-      for (int i = 0; i < 3; ++i) {
-        *dest_scan = *src_scan++;
-        ++dest_scan;
-      }
-      src_scan += src_gap;
-      if (has_dest)
-        dest_alpha_scan++;
-      else
-        dest_scan++;
+      memcpy(dest_scan, src_scan, 3);
+      dest_scan += 3;
+      src_scan += src_Bpp;
+      dest_scan++;
       continue;
     }
     if (src_alpha == 0) {
-      dest_scan += has_dest ? 3 : 4;
-      if (has_dest)
-        dest_alpha_scan++;
+      dest_scan += 4;
       src_scan += src_Bpp;
       continue;
     }
     uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255;
-    if (has_dest)
-      *dest_alpha_scan++ = dest_alpha;
-    else
-      dest_scan[3] = dest_alpha;
+    dest_scan[3] = dest_alpha;
     int alpha_ratio = src_alpha * 255 / dest_alpha;
     if (bNonseparableBlend)
       RGB_Blend(blend_type, src_scan, dest_scan, blended_colors);
@@ -569,247 +437,151 @@
       src_scan++;
     }
     src_scan += src_gap;
-    if (!has_dest)
-      dest_scan++;
+    dest_scan++;
   }
 }
 
-void CompositeRow_Rgb2Argb_NoBlend_Clip(uint8_t* dest_scan,
-                                        const uint8_t* src_scan,
+void CompositeRow_Rgb2Argb_NoBlend_Clip(pdfium::span<uint8_t> dest_span,
+                                        pdfium::span<const uint8_t> src_span,
                                         int width,
                                         int src_Bpp,
-                                        const uint8_t* clip_scan,
-                                        uint8_t* dest_alpha_scan) {
+                                        pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   int src_gap = src_Bpp - 3;
-  if (dest_alpha_scan) {
-    for (int col = 0; col < width; col++) {
-      int src_alpha = clip_scan[col];
-      if (src_alpha == 255) {
-        *dest_scan++ = *src_scan++;
-        *dest_scan++ = *src_scan++;
-        *dest_scan++ = *src_scan++;
-        *dest_alpha_scan++ = 255;
-        src_scan += src_gap;
-        continue;
-      }
-      if (src_alpha == 0) {
-        dest_scan += 3;
-        dest_alpha_scan++;
-        src_scan += src_Bpp;
-        continue;
-      }
-      int back_alpha = *dest_alpha_scan;
-      uint8_t dest_alpha =
-          back_alpha + src_alpha - back_alpha * src_alpha / 255;
-      *dest_alpha_scan++ = dest_alpha;
-      int alpha_ratio = src_alpha * 255 / dest_alpha;
-      for (int color = 0; color < 3; color++) {
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, *src_scan, alpha_ratio);
-        dest_scan++;
-        src_scan++;
-      }
-      src_scan += src_gap;
+  for (int col = 0; col < width; col++) {
+    int src_alpha = clip_scan[col];
+    if (src_alpha == 255) {
+      memcpy(dest_scan, src_scan, 3);
+      dest_scan += 3;
+      *dest_scan++ = 255;
+      src_scan += src_Bpp;
+      continue;
     }
-  } else {
-    for (int col = 0; col < width; col++) {
-      int src_alpha = clip_scan[col];
-      if (src_alpha == 255) {
-        *dest_scan++ = *src_scan++;
-        *dest_scan++ = *src_scan++;
-        *dest_scan++ = *src_scan++;
-        *dest_scan++ = 255;
-        src_scan += src_gap;
-        continue;
-      }
-      if (src_alpha == 0) {
-        dest_scan += 4;
-        src_scan += src_Bpp;
-        continue;
-      }
-      int back_alpha = dest_scan[3];
-      uint8_t dest_alpha =
-          back_alpha + src_alpha - back_alpha * src_alpha / 255;
-      dest_scan[3] = dest_alpha;
-      int alpha_ratio = src_alpha * 255 / dest_alpha;
-      for (int color = 0; color < 3; color++) {
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, *src_scan, alpha_ratio);
-        dest_scan++;
-        src_scan++;
-      }
-      dest_scan++;
-      src_scan += src_gap;
-    }
-  }
-}
-
-void CompositeRow_Rgb2Argb_NoBlend_NoClip(uint8_t* dest_scan,
-                                          const uint8_t* src_scan,
-                                          int width,
-                                          int src_Bpp,
-                                          uint8_t* dest_alpha_scan) {
-  if (dest_alpha_scan) {
-    int src_gap = src_Bpp - 3;
-    for (int col = 0; col < width; col++) {
-      *dest_scan++ = *src_scan++;
-      *dest_scan++ = *src_scan++;
-      *dest_scan++ = *src_scan++;
-      *dest_alpha_scan++ = 0xff;
-      src_scan += src_gap;
-    }
-  } else {
-    for (int col = 0; col < width; col++) {
-      if (src_Bpp == 4) {
-        FXARGB_SETDIB(dest_scan, 0xff000000 | FXARGB_GETDIB(src_scan));
-      } else {
-        FXARGB_SETDIB(dest_scan,
-                      ArgbEncode(0xff, src_scan[2], src_scan[1], src_scan[0]));
-      }
+    if (src_alpha == 0) {
       dest_scan += 4;
       src_scan += src_Bpp;
+      continue;
     }
+    int back_alpha = dest_scan[3];
+    uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255;
+    dest_scan[3] = dest_alpha;
+    int alpha_ratio = src_alpha * 255 / dest_alpha;
+    for (int color = 0; color < 3; color++) {
+      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, *src_scan, alpha_ratio);
+      dest_scan++;
+      src_scan++;
+    }
+    dest_scan++;
+    src_scan += src_gap;
   }
 }
 
-void CompositeRow_Argb2Rgb_Blend(uint8_t* dest_scan,
-                                 const uint8_t* src_scan,
+void CompositeRow_Rgb2Argb_NoBlend_NoClip(pdfium::span<uint8_t> dest_span,
+                                          pdfium::span<const uint8_t> src_span,
+                                          int width,
+                                          int src_Bpp) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  for (int col = 0; col < width; col++) {
+    if (src_Bpp == 4) {
+      FXARGB_SETDIB(dest_scan, 0xff000000 | FXARGB_GETDIB(src_scan));
+    } else {
+      FXARGB_SETDIB(dest_scan,
+                    ArgbEncode(0xff, src_scan[2], src_scan[1], src_scan[0]));
+    }
+    dest_scan += 4;
+    src_scan += src_Bpp;
+  }
+}
+
+void CompositeRow_Argb2Rgb_Blend(pdfium::span<uint8_t> dest_span,
+                                 pdfium::span<const uint8_t> src_span,
                                  int width,
                                  BlendMode blend_type,
                                  int dest_Bpp,
-                                 const uint8_t* clip_scan,
-                                 const uint8_t* src_alpha_scan) {
+                                 pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   int blended_colors[3];
   bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   int dest_gap = dest_Bpp - 3;
-  if (src_alpha_scan) {
-    for (int col = 0; col < width; col++) {
-      uint8_t src_alpha;
-      if (clip_scan) {
-        src_alpha = (*src_alpha_scan++) * (*clip_scan++) / 255;
-      } else {
-        src_alpha = *src_alpha_scan++;
-      }
-      if (src_alpha == 0) {
-        dest_scan += dest_Bpp;
-        src_scan += 3;
-        continue;
-      }
-      if (bNonseparableBlend) {
-        RGB_Blend(blend_type, src_scan, dest_scan, blended_colors);
-      }
-      for (int color = 0; color < 3; color++) {
-        int back_color = *dest_scan;
-        int blended = bNonseparableBlend
-                          ? blended_colors[color]
-                          : Blend(blend_type, back_color, *src_scan);
-        *dest_scan = FXDIB_ALPHA_MERGE(back_color, blended, src_alpha);
-        dest_scan++;
-        src_scan++;
-      }
-      dest_scan += dest_gap;
+  for (int col = 0; col < width; col++) {
+    uint8_t src_alpha;
+    if (clip_scan) {
+      src_alpha = src_scan[3] * (*clip_scan++) / 255;
+    } else {
+      src_alpha = src_scan[3];
     }
-  } else {
-    for (int col = 0; col < width; col++) {
-      uint8_t src_alpha;
-      if (clip_scan) {
-        src_alpha = src_scan[3] * (*clip_scan++) / 255;
-      } else {
-        src_alpha = src_scan[3];
-      }
-      if (src_alpha == 0) {
-        dest_scan += dest_Bpp;
-        src_scan += 4;
-        continue;
-      }
-      if (bNonseparableBlend) {
-        RGB_Blend(blend_type, src_scan, dest_scan, blended_colors);
-      }
-      for (int color = 0; color < 3; color++) {
-        int back_color = *dest_scan;
-        int blended = bNonseparableBlend
-                          ? blended_colors[color]
-                          : Blend(blend_type, back_color, *src_scan);
-        *dest_scan = FXDIB_ALPHA_MERGE(back_color, blended, src_alpha);
-        dest_scan++;
-        src_scan++;
-      }
-      dest_scan += dest_gap;
+    if (src_alpha == 0) {
+      dest_scan += dest_Bpp;
+      src_scan += 4;
+      continue;
+    }
+    if (bNonseparableBlend) {
+      RGB_Blend(blend_type, src_scan, dest_scan, blended_colors);
+    }
+    for (int color = 0; color < 3; color++) {
+      int back_color = *dest_scan;
+      int blended = bNonseparableBlend
+                        ? blended_colors[color]
+                        : Blend(blend_type, back_color, *src_scan);
+      *dest_scan = FXDIB_ALPHA_MERGE(back_color, blended, src_alpha);
+      dest_scan++;
       src_scan++;
     }
+    dest_scan += dest_gap;
+    src_scan++;
   }
 }
 
-void CompositeRow_Argb2Rgb_NoBlend(uint8_t* dest_scan,
-                                   const uint8_t* src_scan,
+void CompositeRow_Argb2Rgb_NoBlend(pdfium::span<uint8_t> dest_span,
+                                   pdfium::span<const uint8_t> src_span,
                                    int width,
                                    int dest_Bpp,
-                                   const uint8_t* clip_scan,
-                                   const uint8_t* src_alpha_scan) {
+                                   pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   int dest_gap = dest_Bpp - 3;
-  if (src_alpha_scan) {
-    for (int col = 0; col < width; col++) {
-      uint8_t src_alpha;
-      if (clip_scan) {
-        src_alpha = (*src_alpha_scan++) * (*clip_scan++) / 255;
-      } else {
-        src_alpha = *src_alpha_scan++;
-      }
-      if (src_alpha == 255) {
-        *dest_scan++ = *src_scan++;
-        *dest_scan++ = *src_scan++;
-        *dest_scan++ = *src_scan++;
-        dest_scan += dest_gap;
-        continue;
-      }
-      if (src_alpha == 0) {
-        dest_scan += dest_Bpp;
-        src_scan += 3;
-        continue;
-      }
-      for (int color = 0; color < 3; color++) {
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, *src_scan, src_alpha);
-        dest_scan++;
-        src_scan++;
-      }
-      dest_scan += dest_gap;
+  for (int col = 0; col < width; col++) {
+    uint8_t src_alpha;
+    if (clip_scan) {
+      src_alpha = src_scan[3] * (*clip_scan++) / 255;
+    } else {
+      src_alpha = src_scan[3];
     }
-  } else {
-    for (int col = 0; col < width; col++) {
-      uint8_t src_alpha;
-      if (clip_scan) {
-        src_alpha = src_scan[3] * (*clip_scan++) / 255;
-      } else {
-        src_alpha = src_scan[3];
-      }
-      if (src_alpha == 255) {
-        *dest_scan++ = *src_scan++;
-        *dest_scan++ = *src_scan++;
-        *dest_scan++ = *src_scan++;
-        dest_scan += dest_gap;
-        src_scan++;
-        continue;
-      }
-      if (src_alpha == 0) {
-        dest_scan += dest_Bpp;
-        src_scan += 4;
-        continue;
-      }
-      for (int color = 0; color < 3; color++) {
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, *src_scan, src_alpha);
-        dest_scan++;
-        src_scan++;
-      }
-      dest_scan += dest_gap;
+    if (src_alpha == 255) {
+      memcpy(dest_scan, src_scan, 3);
+      dest_scan += dest_Bpp;
+      src_scan += 4;
+      continue;
+    }
+    if (src_alpha == 0) {
+      dest_scan += dest_Bpp;
+      src_scan += 4;
+      continue;
+    }
+    for (int color = 0; color < 3; color++) {
+      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, *src_scan, src_alpha);
+      dest_scan++;
       src_scan++;
     }
+    dest_scan += dest_gap;
+    src_scan++;
   }
 }
 
-void CompositeRow_Rgb2Rgb_Blend_NoClip(uint8_t* dest_scan,
-                                       const uint8_t* src_scan,
+void CompositeRow_Rgb2Rgb_Blend_NoClip(pdfium::span<uint8_t> dest_span,
+                                       pdfium::span<const uint8_t> src_span,
                                        int width,
                                        BlendMode blend_type,
                                        int dest_Bpp,
                                        int src_Bpp) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
   int blended_colors[3];
   bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   int dest_gap = dest_Bpp - 3;
@@ -833,13 +605,16 @@
   }
 }
 
-void CompositeRow_Rgb2Rgb_Blend_Clip(uint8_t* dest_scan,
-                                     const uint8_t* src_scan,
+void CompositeRow_Rgb2Rgb_Blend_Clip(pdfium::span<uint8_t> dest_span,
+                                     pdfium::span<const uint8_t> src_span,
                                      int width,
                                      BlendMode blend_type,
                                      int dest_Bpp,
                                      int src_Bpp,
-                                     const uint8_t* clip_scan) {
+                                     pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   int blended_colors[3];
   bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   int dest_gap = dest_Bpp - 3;
@@ -869,36 +644,37 @@
   }
 }
 
-void CompositeRow_Rgb2Rgb_NoBlend_NoClip(uint8_t* dest_scan,
-                                         const uint8_t* src_scan,
+void CompositeRow_Rgb2Rgb_NoBlend_NoClip(pdfium::span<uint8_t> dest_span,
+                                         pdfium::span<const uint8_t> src_span,
                                          int width,
                                          int dest_Bpp,
                                          int src_Bpp) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
   if (dest_Bpp == src_Bpp) {
     memcpy(dest_scan, src_scan, width * dest_Bpp);
     return;
   }
   for (int col = 0; col < width; col++) {
-    dest_scan[0] = src_scan[0];
-    dest_scan[1] = src_scan[1];
-    dest_scan[2] = src_scan[2];
+    memcpy(dest_scan, src_scan, 3);
     dest_scan += dest_Bpp;
     src_scan += src_Bpp;
   }
 }
 
-void CompositeRow_Rgb2Rgb_NoBlend_Clip(uint8_t* dest_scan,
-                                       const uint8_t* src_scan,
+void CompositeRow_Rgb2Rgb_NoBlend_Clip(pdfium::span<uint8_t> dest_span,
+                                       pdfium::span<const uint8_t> src_span,
                                        int width,
                                        int dest_Bpp,
                                        int src_Bpp,
-                                       const uint8_t* clip_scan) {
+                                       pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   for (int col = 0; col < width; col++) {
     int src_alpha = clip_scan[col];
     if (src_alpha == 255) {
-      dest_scan[0] = src_scan[0];
-      dest_scan[1] = src_scan[1];
-      dest_scan[2] = src_scan[2];
+      memcpy(dest_scan, src_scan, 3);
     } else if (src_alpha) {
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, *src_scan, src_alpha);
       dest_scan++;
@@ -916,62 +692,24 @@
   }
 }
 
-void CompositeRow_8bppPal2Gray(uint8_t* dest_scan,
-                               const uint8_t* src_scan,
-                               const uint8_t* pPalette,
+void CompositeRow_8bppPal2Gray(pdfium::span<uint8_t> dest_span,
+                               pdfium::span<const uint8_t> src_span,
+                               pdfium::span<const uint8_t> palette_span,
                                int pixel_count,
                                BlendMode blend_type,
-                               const uint8_t* clip_scan,
-                               const uint8_t* src_alpha_scan) {
-  if (src_alpha_scan) {
-    if (blend_type != BlendMode::kNormal) {
-      bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
-      for (int col = 0; col < pixel_count; col++) {
-        uint8_t gray = pPalette[*src_scan];
-        int src_alpha = GetAlpha(*src_alpha_scan++, clip_scan, col);
-        if (bNonseparableBlend)
-          gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan;
-        else
-          gray = Blend(blend_type, *dest_scan, gray);
-        if (src_alpha)
-          *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, src_alpha);
-        else
-          *dest_scan = gray;
-        dest_scan++;
-        src_scan++;
-      }
-      return;
-    }
+                               pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
+  const uint8_t* pPalette = palette_span.data();
+  if (blend_type != BlendMode::kNormal) {
+    bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
     for (int col = 0; col < pixel_count; col++) {
       uint8_t gray = pPalette[*src_scan];
-      int src_alpha = GetAlpha(*src_alpha_scan++, clip_scan, col);
-      if (src_alpha)
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, src_alpha);
+      if (bNonseparableBlend)
+        gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan;
       else
-        *dest_scan = gray;
-      dest_scan++;
-      src_scan++;
-    }
-  } else {
-    if (blend_type != BlendMode::kNormal) {
-      bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
-      for (int col = 0; col < pixel_count; col++) {
-        uint8_t gray = pPalette[*src_scan];
-        if (bNonseparableBlend)
-          gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan;
-        else
-          gray = Blend(blend_type, *dest_scan, gray);
-        if (clip_scan && clip_scan[col] < 255)
-          *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, clip_scan[col]);
-        else
-          *dest_scan = gray;
-        dest_scan++;
-        src_scan++;
-      }
-      return;
-    }
-    for (int col = 0; col < pixel_count; col++) {
-      uint8_t gray = pPalette[*src_scan];
+        gray = Blend(blend_type, *dest_scan, gray);
       if (clip_scan && clip_scan[col] < 255)
         *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, clip_scan[col]);
       else
@@ -979,144 +717,31 @@
       dest_scan++;
       src_scan++;
     }
+    return;
+  }
+  for (int col = 0; col < pixel_count; col++) {
+    uint8_t gray = pPalette[*src_scan];
+    if (clip_scan && clip_scan[col] < 255)
+      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, clip_scan[col]);
+    else
+      *dest_scan = gray;
+    dest_scan++;
+    src_scan++;
   }
 }
 
-void CompositeRow_8bppPal2Graya(uint8_t* dest_scan,
-                                const uint8_t* src_scan,
-                                const uint8_t* pPalette,
-                                int pixel_count,
-                                BlendMode blend_type,
-                                const uint8_t* clip_scan,
-                                uint8_t* dest_alpha_scan,
-                                const uint8_t* src_alpha_scan) {
-  if (src_alpha_scan) {
-    if (blend_type != BlendMode::kNormal) {
-      bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
-      for (int col = 0; col < pixel_count; col++) {
-        uint8_t gray = pPalette[*src_scan];
-        src_scan++;
-        uint8_t back_alpha = *dest_alpha_scan;
-        if (back_alpha == 0) {
-          int src_alpha = GetAlpha(*src_alpha_scan++, clip_scan, col);
-          if (src_alpha) {
-            *dest_scan = gray;
-            *dest_alpha_scan = src_alpha;
-          }
-          dest_scan++;
-          dest_alpha_scan++;
-          continue;
-        }
-        uint8_t src_alpha = GetAlpha(*src_alpha_scan++, clip_scan, col);
-        if (src_alpha == 0) {
-          dest_scan++;
-          dest_alpha_scan++;
-          continue;
-        }
-        *dest_alpha_scan =
-            back_alpha + src_alpha - back_alpha * src_alpha / 255;
-        int alpha_ratio = src_alpha * 255 / (*dest_alpha_scan);
-        if (bNonseparableBlend)
-          gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan;
-        else
-          gray = Blend(blend_type, *dest_scan, gray);
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio);
-        dest_alpha_scan++;
-        dest_scan++;
-      }
-      return;
-    }
-    for (int col = 0; col < pixel_count; col++) {
-      uint8_t gray = pPalette[*src_scan];
-      src_scan++;
-      uint8_t back_alpha = *dest_alpha_scan;
-      if (back_alpha == 0) {
-        int src_alpha = GetAlpha(*src_alpha_scan++, clip_scan, col);
-        if (src_alpha) {
-          *dest_scan = gray;
-          *dest_alpha_scan = src_alpha;
-        }
-        dest_scan++;
-        dest_alpha_scan++;
-        continue;
-      }
-      uint8_t src_alpha = GetAlpha(*src_alpha_scan++, clip_scan, col);
-      if (src_alpha == 0) {
-        dest_scan++;
-        dest_alpha_scan++;
-        continue;
-      }
-      *dest_alpha_scan = back_alpha + src_alpha - back_alpha * src_alpha / 255;
-      int alpha_ratio = src_alpha * 255 / (*dest_alpha_scan);
-      dest_alpha_scan++;
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio);
-      dest_scan++;
-    }
-  } else {
-    if (blend_type != BlendMode::kNormal) {
-      bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
-      for (int col = 0; col < pixel_count; col++) {
-        uint8_t gray = pPalette[*src_scan];
-        src_scan++;
-        if (!clip_scan || clip_scan[col] == 255) {
-          *dest_scan++ = gray;
-          *dest_alpha_scan++ = 255;
-          continue;
-        }
-        int src_alpha = clip_scan[col];
-        if (src_alpha == 0) {
-          dest_scan++;
-          dest_alpha_scan++;
-          continue;
-        }
-        int back_alpha = *dest_alpha_scan;
-        uint8_t dest_alpha =
-            back_alpha + src_alpha - back_alpha * src_alpha / 255;
-        *dest_alpha_scan++ = dest_alpha;
-        int alpha_ratio = src_alpha * 255 / dest_alpha;
-        if (bNonseparableBlend)
-          gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan;
-        else
-          gray = Blend(blend_type, *dest_scan, gray);
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio);
-        dest_scan++;
-      }
-      return;
-    }
-    for (int col = 0; col < pixel_count; col++) {
-      uint8_t gray = pPalette[*src_scan];
-      src_scan++;
-      if (!clip_scan || clip_scan[col] == 255) {
-        *dest_scan++ = gray;
-        *dest_alpha_scan++ = 255;
-        continue;
-      }
-      int src_alpha = clip_scan[col];
-      if (src_alpha == 0) {
-        dest_scan++;
-        dest_alpha_scan++;
-        continue;
-      }
-      int back_alpha = *dest_alpha_scan;
-      uint8_t dest_alpha =
-          back_alpha + src_alpha - back_alpha * src_alpha / 255;
-      *dest_alpha_scan++ = dest_alpha;
-      int alpha_ratio = src_alpha * 255 / dest_alpha;
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio);
-      dest_scan++;
-    }
-  }
-}
-
-void CompositeRow_1bppPal2Gray(uint8_t* dest_scan,
-                               const uint8_t* src_scan,
+void CompositeRow_1bppPal2Gray(pdfium::span<uint8_t> dest_span,
+                               pdfium::span<const uint8_t> src_span,
                                int src_left,
-                               const uint8_t* pPalette,
+                               pdfium::span<const uint8_t> src_palette,
                                int pixel_count,
                                BlendMode blend_type,
-                               const uint8_t* clip_scan) {
-  int reset_gray = pPalette[0];
-  int set_gray = pPalette[1];
+                               pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
+  int reset_gray = src_palette[0];
+  int set_gray = src_palette[1];
   if (blend_type != BlendMode::kNormal) {
     bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
     for (int col = 0; col < pixel_count; col++) {
@@ -1151,158 +776,61 @@
   }
 }
 
-void CompositeRow_1bppPal2Graya(uint8_t* dest_scan,
-                                const uint8_t* src_scan,
-                                int src_left,
-                                const uint8_t* pPalette,
-                                int pixel_count,
-                                BlendMode blend_type,
-                                const uint8_t* clip_scan,
-                                uint8_t* dest_alpha_scan) {
-  int reset_gray = pPalette[0];
-  int set_gray = pPalette[1];
-  if (blend_type != BlendMode::kNormal) {
-    bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
-    for (int col = 0; col < pixel_count; col++) {
-      uint8_t gray =
-          (src_scan[(col + src_left) / 8] & (1 << (7 - (col + src_left) % 8)))
-              ? set_gray
-              : reset_gray;
-      if (!clip_scan || clip_scan[col] == 255) {
-        *dest_scan++ = gray;
-        *dest_alpha_scan++ = 255;
-        continue;
-      }
-      int src_alpha = clip_scan[col];
-      if (src_alpha == 0) {
-        dest_scan++;
-        dest_alpha_scan++;
-        continue;
-      }
-      int back_alpha = *dest_alpha_scan;
-      uint8_t dest_alpha =
-          back_alpha + src_alpha - back_alpha * src_alpha / 255;
-      *dest_alpha_scan++ = dest_alpha;
-      int alpha_ratio = src_alpha * 255 / dest_alpha;
-      if (bNonseparableBlend)
-        gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan;
-      else
-        gray = Blend(blend_type, *dest_scan, gray);
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio);
-      dest_scan++;
-    }
-    return;
-  }
-  for (int col = 0; col < pixel_count; col++) {
-    uint8_t gray =
-        (src_scan[(col + src_left) / 8] & (1 << (7 - (col + src_left) % 8)))
-            ? set_gray
-            : reset_gray;
-    if (!clip_scan || clip_scan[col] == 255) {
-      *dest_scan++ = gray;
-      *dest_alpha_scan++ = 255;
-      continue;
-    }
-    int src_alpha = clip_scan[col];
-    if (src_alpha == 0) {
-      dest_scan++;
-      dest_alpha_scan++;
-      continue;
-    }
-    int back_alpha = *dest_alpha_scan;
-    uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255;
-    *dest_alpha_scan++ = dest_alpha;
-    int alpha_ratio = src_alpha * 255 / dest_alpha;
-    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio);
-    dest_scan++;
-  }
-}
-
-void CompositeRow_8bppRgb2Rgb_NoBlend(uint8_t* dest_scan,
-                                      const uint8_t* src_scan,
-                                      uint32_t* pPalette,
+void CompositeRow_8bppRgb2Rgb_NoBlend(pdfium::span<uint8_t> dest_span,
+                                      pdfium::span<const uint8_t> src_span,
+                                      pdfium::span<const uint32_t> palette_span,
                                       int pixel_count,
                                       int DestBpp,
-                                      const uint8_t* clip_scan,
-                                      const uint8_t* src_alpha_scan) {
-  if (src_alpha_scan) {
-    int dest_gap = DestBpp - 3;
-    FX_ARGB argb = 0;
-    for (int col = 0; col < pixel_count; col++) {
-      argb = pPalette[*src_scan];
-      int src_r = FXARGB_R(argb);
-      int src_g = FXARGB_G(argb);
-      int src_b = FXARGB_B(argb);
-      src_scan++;
-      uint8_t src_alpha = 0;
-      if (clip_scan) {
-        src_alpha = (*src_alpha_scan++) * (*clip_scan++) / 255;
-      } else {
-        src_alpha = *src_alpha_scan++;
-      }
-      if (src_alpha == 255) {
-        *dest_scan++ = src_b;
-        *dest_scan++ = src_g;
-        *dest_scan++ = src_r;
-        dest_scan += dest_gap;
-        continue;
-      }
-      if (src_alpha == 0) {
-        dest_scan += DestBpp;
-        continue;
-      }
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, src_alpha);
+                                      pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
+  const uint32_t* pPalette = palette_span.data();
+  FX_ARGB argb = 0;
+  for (int col = 0; col < pixel_count; col++) {
+    argb = pPalette[*src_scan];
+    int src_r = FXARGB_R(argb);
+    int src_g = FXARGB_G(argb);
+    int src_b = FXARGB_B(argb);
+    if (clip_scan && clip_scan[col] < 255) {
+      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, clip_scan[col]);
       dest_scan++;
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, src_alpha);
+      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, clip_scan[col]);
       dest_scan++;
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, src_alpha);
+      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, clip_scan[col]);
       dest_scan++;
-      dest_scan += dest_gap;
+    } else {
+      *dest_scan++ = src_b;
+      *dest_scan++ = src_g;
+      *dest_scan++ = src_r;
     }
-  } else {
-    FX_ARGB argb = 0;
-    for (int col = 0; col < pixel_count; col++) {
-      argb = pPalette[*src_scan];
-      int src_r = FXARGB_R(argb);
-      int src_g = FXARGB_G(argb);
-      int src_b = FXARGB_B(argb);
-      if (clip_scan && clip_scan[col] < 255) {
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, clip_scan[col]);
-        dest_scan++;
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, clip_scan[col]);
-        dest_scan++;
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, clip_scan[col]);
-        dest_scan++;
-      } else {
-        *dest_scan++ = src_b;
-        *dest_scan++ = src_g;
-        *dest_scan++ = src_r;
-      }
-      if (DestBpp == 4) {
-        dest_scan++;
-      }
-      src_scan++;
+    if (DestBpp == 4) {
+      dest_scan++;
     }
+    src_scan++;
   }
 }
 
-void CompositeRow_1bppRgb2Rgb_NoBlend(uint8_t* dest_scan,
-                                      const uint8_t* src_scan,
+void CompositeRow_1bppRgb2Rgb_NoBlend(pdfium::span<uint8_t> dest_span,
+                                      pdfium::span<const uint8_t> src_span,
                                       int src_left,
-                                      uint32_t* pPalette,
+                                      pdfium::span<const uint32_t> src_palette,
                                       int pixel_count,
                                       int DestBpp,
-                                      const uint8_t* clip_scan) {
-  int reset_r, reset_g, reset_b;
-  int set_r, set_g, set_b;
-  reset_r = FXARGB_R(pPalette[0]);
-  reset_g = FXARGB_G(pPalette[0]);
-  reset_b = FXARGB_B(pPalette[0]);
-  set_r = FXARGB_R(pPalette[1]);
-  set_g = FXARGB_G(pPalette[1]);
-  set_b = FXARGB_B(pPalette[1]);
+                                      pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
+  int reset_r = FXARGB_R(src_palette[0]);
+  int reset_g = FXARGB_G(src_palette[0]);
+  int reset_b = FXARGB_B(src_palette[0]);
+  int set_r = FXARGB_R(src_palette[1]);
+  int set_g = FXARGB_G(src_palette[1]);
+  int set_b = FXARGB_B(src_palette[1]);
   for (int col = 0; col < pixel_count; col++) {
-    int src_r, src_g, src_b;
+    int src_r;
+    int src_g;
+    int src_b;
     if (src_scan[(col + src_left) / 8] & (1 << (7 - (col + src_left) % 8))) {
       src_r = set_r;
       src_g = set_g;
@@ -1330,103 +858,69 @@
   }
 }
 
-void CompositeRow_8bppRgb2Argb_NoBlend(uint8_t* dest_scan,
-                                       const uint8_t* src_scan,
-                                       int width,
-                                       uint32_t* pPalette,
-                                       const uint8_t* clip_scan,
-                                       const uint8_t* src_alpha_scan) {
-  if (src_alpha_scan) {
-    for (int col = 0; col < width; col++) {
-      FX_ARGB argb = pPalette[*src_scan];
+void CompositeRow_8bppRgb2Argb_NoBlend(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int width,
+    pdfium::span<const uint32_t> palette_span,
+    pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
+  const uint32_t* pPalette = palette_span.data();
+  for (int col = 0; col < width; col++) {
+    FX_ARGB argb = pPalette[*src_scan];
+    int src_r = FXARGB_R(argb);
+    int src_g = FXARGB_G(argb);
+    int src_b = FXARGB_B(argb);
+    if (!clip_scan || clip_scan[col] == 255) {
+      *dest_scan++ = src_b;
+      *dest_scan++ = src_g;
+      *dest_scan++ = src_r;
+      *dest_scan++ = 255;
       src_scan++;
-      int src_r = FXARGB_R(argb);
-      int src_g = FXARGB_G(argb);
-      int src_b = FXARGB_B(argb);
-      uint8_t back_alpha = dest_scan[3];
-      if (back_alpha == 0) {
-        if (clip_scan) {
-          int src_alpha = clip_scan[col] * (*src_alpha_scan) / 255;
-          FXARGB_SETDIB(dest_scan, ArgbEncode(src_alpha, src_r, src_g, src_b));
-        } else {
-          FXARGB_SETDIB(dest_scan,
-                        ArgbEncode(*src_alpha_scan, src_r, src_g, src_b));
-        }
-        dest_scan += 4;
-        src_alpha_scan++;
-        continue;
-      }
-      uint8_t src_alpha = GetAlpha(*src_alpha_scan, clip_scan, col);
-      ++src_alpha_scan;
-      if (src_alpha == 0) {
-        dest_scan += 4;
-        continue;
-      }
-      uint8_t dest_alpha =
-          back_alpha + src_alpha - back_alpha * src_alpha / 255;
-      dest_scan[3] = dest_alpha;
-      int alpha_ratio = src_alpha * 255 / dest_alpha;
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, alpha_ratio);
-      dest_scan++;
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, alpha_ratio);
-      dest_scan++;
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, alpha_ratio);
-      dest_scan++;
-      dest_scan++;
+      continue;
     }
-  } else {
-    for (int col = 0; col < width; col++) {
-      FX_ARGB argb = pPalette[*src_scan];
-      int src_r = FXARGB_R(argb);
-      int src_g = FXARGB_G(argb);
-      int src_b = FXARGB_B(argb);
-      if (!clip_scan || clip_scan[col] == 255) {
-        *dest_scan++ = src_b;
-        *dest_scan++ = src_g;
-        *dest_scan++ = src_r;
-        *dest_scan++ = 255;
-        src_scan++;
-        continue;
-      }
-      int src_alpha = clip_scan[col];
-      if (src_alpha == 0) {
-        dest_scan += 4;
-        src_scan++;
-        continue;
-      }
-      int back_alpha = dest_scan[3];
-      uint8_t dest_alpha =
-          back_alpha + src_alpha - back_alpha * src_alpha / 255;
-      dest_scan[3] = dest_alpha;
-      int alpha_ratio = src_alpha * 255 / dest_alpha;
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, alpha_ratio);
-      dest_scan++;
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, alpha_ratio);
-      dest_scan++;
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, alpha_ratio);
-      dest_scan++;
-      dest_scan++;
+    int src_alpha = clip_scan[col];
+    if (src_alpha == 0) {
+      dest_scan += 4;
       src_scan++;
+      continue;
     }
+    int back_alpha = dest_scan[3];
+    uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255;
+    dest_scan[3] = dest_alpha;
+    int alpha_ratio = src_alpha * 255 / dest_alpha;
+    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, alpha_ratio);
+    dest_scan++;
+    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, alpha_ratio);
+    dest_scan++;
+    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, alpha_ratio);
+    dest_scan++;
+    dest_scan++;
+    src_scan++;
   }
 }
 
-void CompositeRow_1bppRgb2Argb_NoBlend(uint8_t* dest_scan,
-                                       const uint8_t* src_scan,
+void CompositeRow_1bppRgb2Argb_NoBlend(pdfium::span<uint8_t> dest_span,
+                                       pdfium::span<const uint8_t> src_span,
                                        int src_left,
                                        int width,
-                                       uint32_t* pPalette,
-                                       const uint8_t* clip_scan) {
-  int reset_r, reset_g, reset_b;
-  int set_r, set_g, set_b;
-  reset_r = FXARGB_R(pPalette[0]);
-  reset_g = FXARGB_G(pPalette[0]);
-  reset_b = FXARGB_B(pPalette[0]);
-  set_r = FXARGB_R(pPalette[1]);
-  set_g = FXARGB_G(pPalette[1]);
-  set_b = FXARGB_B(pPalette[1]);
+                                       pdfium::span<const uint32_t> src_palette,
+                                       pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
+  int reset_r = FXARGB_R(src_palette[0]);
+  int reset_g = FXARGB_G(src_palette[0]);
+  int reset_b = FXARGB_B(src_palette[0]);
+  int set_r = FXARGB_R(src_palette[1]);
+  int set_g = FXARGB_G(src_palette[1]);
+  int set_b = FXARGB_B(src_palette[1]);
   for (int col = 0; col < width; col++) {
-    int src_r, src_g, src_b;
+    int src_r;
+    int src_g;
+    int src_b;
     if (src_scan[(col + src_left) / 8] & (1 << (7 - (col + src_left) % 8))) {
       src_r = set_r;
       src_g = set_g;
@@ -1462,67 +956,18 @@
   }
 }
 
-void CompositeRow_1bppRgb2Rgba_NoBlend(uint8_t* dest_scan,
-                                       const uint8_t* src_scan,
-                                       int src_left,
-                                       int width,
-                                       uint32_t* pPalette,
-                                       const uint8_t* clip_scan,
-                                       uint8_t* dest_alpha_scan) {
-  int reset_r, reset_g, reset_b;
-  int set_r, set_g, set_b;
-  reset_r = FXARGB_R(pPalette[0]);
-  reset_g = FXARGB_G(pPalette[0]);
-  reset_b = FXARGB_B(pPalette[0]);
-  set_r = FXARGB_R(pPalette[1]);
-  set_g = FXARGB_G(pPalette[1]);
-  set_b = FXARGB_B(pPalette[1]);
-  for (int col = 0; col < width; col++) {
-    int src_r, src_g, src_b;
-    if (src_scan[(col + src_left) / 8] & (1 << (7 - (col + src_left) % 8))) {
-      src_r = set_r;
-      src_g = set_g;
-      src_b = set_b;
-    } else {
-      src_r = reset_r;
-      src_g = reset_g;
-      src_b = reset_b;
-    }
-    if (!clip_scan || clip_scan[col] == 255) {
-      *dest_scan++ = src_b;
-      *dest_scan++ = src_g;
-      *dest_scan++ = src_r;
-      *dest_alpha_scan++ = 255;
-      continue;
-    }
-    int src_alpha = clip_scan[col];
-    if (src_alpha == 0) {
-      dest_scan += 3;
-      dest_alpha_scan++;
-      continue;
-    }
-    int back_alpha = *dest_alpha_scan;
-    uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255;
-    *dest_alpha_scan++ = dest_alpha;
-    int alpha_ratio = src_alpha * 255 / dest_alpha;
-    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, alpha_ratio);
-    dest_scan++;
-    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, alpha_ratio);
-    dest_scan++;
-    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, alpha_ratio);
-    dest_scan++;
-  }
-}
-
-void CompositeRow_ByteMask2Argb(uint8_t* dest_scan,
-                                const uint8_t* src_scan,
+void CompositeRow_ByteMask2Argb(pdfium::span<uint8_t> dest_span,
+                                pdfium::span<const uint8_t> src_span,
                                 int mask_alpha,
                                 int src_r,
                                 int src_g,
                                 int src_b,
                                 int pixel_count,
                                 BlendMode blend_type,
-                                const uint8_t* clip_scan) {
+                                pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   for (int col = 0; col < pixel_count; col++) {
     int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col);
     uint8_t back_alpha = dest_scan[3];
@@ -1575,75 +1020,8 @@
   }
 }
 
-void CompositeRow_ByteMask2Rgba(uint8_t* dest_scan,
-                                const uint8_t* src_scan,
-                                int mask_alpha,
-                                int src_r,
-                                int src_g,
-                                int src_b,
-                                int pixel_count,
-                                BlendMode blend_type,
-                                const uint8_t* clip_scan,
-                                uint8_t* dest_alpha_scan) {
-  for (int col = 0; col < pixel_count; col++) {
-    int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col);
-    uint8_t back_alpha = *dest_alpha_scan;
-    if (back_alpha == 0) {
-      *dest_scan++ = src_b;
-      *dest_scan++ = src_g;
-      *dest_scan++ = src_r;
-      *dest_alpha_scan++ = src_alpha;
-      continue;
-    }
-    if (src_alpha == 0) {
-      dest_scan += 3;
-      dest_alpha_scan++;
-      continue;
-    }
-    uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255;
-    *dest_alpha_scan++ = dest_alpha;
-    int alpha_ratio = src_alpha * 255 / dest_alpha;
-    if (IsNonSeparableBlendMode(blend_type)) {
-      int blended_colors[3];
-      uint8_t scan[3] = {static_cast<uint8_t>(src_b),
-                         static_cast<uint8_t>(src_g),
-                         static_cast<uint8_t>(src_r)};
-      RGB_Blend(blend_type, scan, dest_scan, blended_colors);
-      *dest_scan =
-          FXDIB_ALPHA_MERGE(*dest_scan, blended_colors[0], alpha_ratio);
-      dest_scan++;
-      *dest_scan =
-          FXDIB_ALPHA_MERGE(*dest_scan, blended_colors[1], alpha_ratio);
-      dest_scan++;
-      *dest_scan =
-          FXDIB_ALPHA_MERGE(*dest_scan, blended_colors[2], alpha_ratio);
-      dest_scan++;
-    } else if (blend_type != BlendMode::kNormal) {
-      int blended = Blend(blend_type, *dest_scan, src_b);
-      blended = FXDIB_ALPHA_MERGE(src_b, blended, back_alpha);
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, blended, alpha_ratio);
-      dest_scan++;
-      blended = Blend(blend_type, *dest_scan, src_g);
-      blended = FXDIB_ALPHA_MERGE(src_g, blended, back_alpha);
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, blended, alpha_ratio);
-      dest_scan++;
-      blended = Blend(blend_type, *dest_scan, src_r);
-      blended = FXDIB_ALPHA_MERGE(src_r, blended, back_alpha);
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, blended, alpha_ratio);
-      dest_scan++;
-    } else {
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, alpha_ratio);
-      dest_scan++;
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, alpha_ratio);
-      dest_scan++;
-      *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, alpha_ratio);
-      dest_scan++;
-    }
-  }
-}
-
-void CompositeRow_ByteMask2Rgb(uint8_t* dest_scan,
-                               const uint8_t* src_scan,
+void CompositeRow_ByteMask2Rgb(pdfium::span<uint8_t> dest_span,
+                               pdfium::span<const uint8_t> src_span,
                                int mask_alpha,
                                int src_r,
                                int src_g,
@@ -1651,7 +1029,10 @@
                                int pixel_count,
                                BlendMode blend_type,
                                int Bpp,
-                               const uint8_t* clip_scan) {
+                               pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   for (int col = 0; col < pixel_count; col++) {
     int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col);
     if (src_alpha == 0) {
@@ -1689,11 +1070,14 @@
   }
 }
 
-void CompositeRow_ByteMask2Mask(uint8_t* dest_scan,
-                                const uint8_t* src_scan,
+void CompositeRow_ByteMask2Mask(pdfium::span<uint8_t> dest_span,
+                                pdfium::span<const uint8_t> src_span,
                                 int mask_alpha,
                                 int pixel_count,
-                                const uint8_t* clip_scan) {
+                                pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   for (int col = 0; col < pixel_count; col++) {
     int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col);
     uint8_t back_alpha = *dest_scan;
@@ -1706,12 +1090,15 @@
   }
 }
 
-void CompositeRow_ByteMask2Gray(uint8_t* dest_scan,
-                                const uint8_t* src_scan,
+void CompositeRow_ByteMask2Gray(pdfium::span<uint8_t> dest_span,
+                                pdfium::span<const uint8_t> src_span,
                                 int mask_alpha,
                                 int src_gray,
                                 int pixel_count,
-                                const uint8_t* clip_scan) {
+                                pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   for (int col = 0; col < pixel_count; col++) {
     int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col);
     if (src_alpha) {
@@ -1721,36 +1108,8 @@
   }
 }
 
-void CompositeRow_ByteMask2Graya(uint8_t* dest_scan,
-                                 const uint8_t* src_scan,
-                                 int mask_alpha,
-                                 int src_gray,
-                                 int pixel_count,
-                                 const uint8_t* clip_scan,
-                                 uint8_t* dest_alpha_scan) {
-  for (int col = 0; col < pixel_count; col++) {
-    int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col);
-    uint8_t back_alpha = *dest_alpha_scan;
-    if (back_alpha == 0) {
-      *dest_scan++ = src_gray;
-      *dest_alpha_scan++ = src_alpha;
-      continue;
-    }
-    if (src_alpha == 0) {
-      dest_scan++;
-      dest_alpha_scan++;
-      continue;
-    }
-    uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255;
-    *dest_alpha_scan++ = dest_alpha;
-    int alpha_ratio = src_alpha * 255 / dest_alpha;
-    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_gray, alpha_ratio);
-    dest_scan++;
-  }
-}
-
-void CompositeRow_BitMask2Argb(uint8_t* dest_scan,
-                               const uint8_t* src_scan,
+void CompositeRow_BitMask2Argb(pdfium::span<uint8_t> dest_span,
+                               pdfium::span<const uint8_t> src_span,
                                int mask_alpha,
                                int src_r,
                                int src_g,
@@ -1758,7 +1117,10 @@
                                int src_left,
                                int pixel_count,
                                BlendMode blend_type,
-                               const uint8_t* clip_scan) {
+                               pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   if (blend_type == BlendMode::kNormal && !clip_scan && mask_alpha == 255) {
     FX_ARGB argb = ArgbEncode(0xff, src_r, src_g, src_b);
     for (int col = 0; col < pixel_count; col++) {
@@ -1821,8 +1183,8 @@
   }
 }
 
-void CompositeRow_BitMask2Rgb(uint8_t* dest_scan,
-                              const uint8_t* src_scan,
+void CompositeRow_BitMask2Rgb(pdfium::span<uint8_t> dest_span,
+                              pdfium::span<const uint8_t> src_span,
                               int mask_alpha,
                               int src_r,
                               int src_g,
@@ -1831,7 +1193,10 @@
                               int pixel_count,
                               BlendMode blend_type,
                               int Bpp,
-                              const uint8_t* clip_scan) {
+                              pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   if (blend_type == BlendMode::kNormal && !clip_scan && mask_alpha == 255) {
     for (int col = 0; col < pixel_count; col++) {
       if (src_scan[(src_left + col) / 8] & (1 << (7 - (src_left + col) % 8))) {
@@ -1884,12 +1249,15 @@
   }
 }
 
-void CompositeRow_BitMask2Mask(uint8_t* dest_scan,
-                               const uint8_t* src_scan,
+void CompositeRow_BitMask2Mask(pdfium::span<uint8_t> dest_span,
+                               pdfium::span<const uint8_t> src_span,
                                int mask_alpha,
                                int src_left,
                                int pixel_count,
-                               const uint8_t* clip_scan) {
+                               pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   for (int col = 0; col < pixel_count; col++) {
     if (!(src_scan[(src_left + col) / 8] & (1 << (7 - (src_left + col) % 8)))) {
       dest_scan++;
@@ -1906,13 +1274,16 @@
   }
 }
 
-void CompositeRow_BitMask2Gray(uint8_t* dest_scan,
-                               const uint8_t* src_scan,
+void CompositeRow_BitMask2Gray(pdfium::span<uint8_t> dest_span,
+                               pdfium::span<const uint8_t> src_span,
                                int mask_alpha,
                                int src_gray,
                                int src_left,
                                int pixel_count,
-                               const uint8_t* clip_scan) {
+                               pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   for (int col = 0; col < pixel_count; col++) {
     if (!(src_scan[(src_left + col) / 8] & (1 << (7 - (src_left + col) % 8)))) {
       dest_scan++;
@@ -1926,56 +1297,24 @@
   }
 }
 
-void CompositeRow_BitMask2Graya(uint8_t* dest_scan,
-                                const uint8_t* src_scan,
-                                int mask_alpha,
-                                int src_gray,
-                                int src_left,
-                                int pixel_count,
-                                const uint8_t* clip_scan,
-                                uint8_t* dest_alpha_scan) {
-  for (int col = 0; col < pixel_count; col++) {
-    if (!(src_scan[(src_left + col) / 8] & (1 << (7 - (src_left + col) % 8)))) {
-      dest_scan++;
-      dest_alpha_scan++;
-      continue;
-    }
-    int src_alpha = GetAlpha(mask_alpha, clip_scan, col);
-    uint8_t back_alpha = *dest_alpha_scan;
-    if (back_alpha == 0) {
-      *dest_scan++ = src_gray;
-      *dest_alpha_scan++ = src_alpha;
-      continue;
-    }
-    if (src_alpha == 0) {
-      dest_scan++;
-      dest_alpha_scan++;
-      continue;
-    }
-    uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255;
-    *dest_alpha_scan++ = dest_alpha;
-    int alpha_ratio = src_alpha * 255 / dest_alpha;
-    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_gray, alpha_ratio);
-    dest_scan++;
-  }
-}
-
-void CompositeRow_Argb2Argb_RgbByteOrder(uint8_t* dest_scan,
-                                         const uint8_t* src_scan,
-                                         int pixel_count,
-                                         BlendMode blend_type,
-                                         const uint8_t* clip_scan) {
-  int blended_colors[3];
+void CompositeRow_Argb2Argb_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int pixel_count,
+    BlendMode blend_type,
+    pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
+  int blended_colors[3];
   for (int col = 0; col < pixel_count; col++) {
     uint8_t back_alpha = dest_scan[3];
     if (back_alpha == 0) {
       if (clip_scan) {
         int src_alpha = clip_scan[col] * src_scan[3] / 255;
+        ReverseCopy3Bytes(dest_scan, src_scan);
         dest_scan[3] = src_alpha;
-        dest_scan[0] = src_scan[2];
-        dest_scan[1] = src_scan[1];
-        dest_scan[2] = src_scan[0];
       } else {
         FXARGB_RGBORDERCOPY(dest_scan, src_scan);
       }
@@ -1994,9 +1333,7 @@
     int alpha_ratio = src_alpha * 255 / dest_alpha;
     if (bNonseparableBlend) {
       uint8_t dest_scan_o[3];
-      dest_scan_o[0] = dest_scan[2];
-      dest_scan_o[1] = dest_scan[1];
-      dest_scan_o[2] = dest_scan[0];
+      ReverseCopy3Bytes(dest_scan_o, dest_scan);
       RGB_Blend(blend_type, src_scan, dest_scan_o, blended_colors);
     }
     for (int color = 0; color < 3; color++) {
@@ -2019,14 +1356,17 @@
   }
 }
 
-void CompositeRow_Rgb2Argb_Blend_NoClip_RgbByteOrder(uint8_t* dest_scan,
-                                                     const uint8_t* src_scan,
-                                                     int width,
-                                                     BlendMode blend_type,
-                                                     int src_Bpp) {
-  int blended_colors[3];
+void CompositeRow_Rgb2Argb_Blend_NoClip_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int width,
+    BlendMode blend_type,
+    int src_Bpp) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
   bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   int src_gap = src_Bpp - 3;
+  int blended_colors[3];
   for (int col = 0; col < width; col++) {
     uint8_t back_alpha = dest_scan[3];
     if (back_alpha == 0) {
@@ -2043,9 +1383,7 @@
     dest_scan[3] = 0xff;
     if (bNonseparableBlend) {
       uint8_t dest_scan_o[3];
-      dest_scan_o[0] = dest_scan[2];
-      dest_scan_o[1] = dest_scan[1];
-      dest_scan_o[2] = dest_scan[0];
+      ReverseCopy3Bytes(dest_scan_o, dest_scan);
       RGB_Blend(blend_type, src_scan, dest_scan_o, blended_colors);
     }
     for (int color = 0; color < 3; color++) {
@@ -2062,14 +1400,18 @@
   }
 }
 
-void CompositeRow_Argb2Rgb_Blend_RgbByteOrder(uint8_t* dest_scan,
-                                              const uint8_t* src_scan,
-                                              int width,
-                                              BlendMode blend_type,
-                                              int dest_Bpp,
-                                              const uint8_t* clip_scan) {
+void CompositeRow_Argb2Rgb_Blend_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int width,
+    BlendMode blend_type,
+    int dest_Bpp,
+    pdfium::span<const uint8_t> clip_span) {
   int blended_colors[3];
   bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   for (int col = 0; col < width; col++) {
     uint8_t src_alpha;
     if (clip_scan) {
@@ -2084,9 +1426,7 @@
     }
     if (bNonseparableBlend) {
       uint8_t dest_scan_o[3];
-      dest_scan_o[0] = dest_scan[2];
-      dest_scan_o[1] = dest_scan[1];
-      dest_scan_o[2] = dest_scan[0];
+      ReverseCopy3Bytes(dest_scan_o, dest_scan);
       RGB_Blend(blend_type, src_scan, dest_scan_o, blended_colors);
     }
     for (int color = 0; color < 3; color++) {
@@ -2103,10 +1443,13 @@
   }
 }
 
-void CompositeRow_Rgb2Argb_NoBlend_NoClip_RgbByteOrder(uint8_t* dest_scan,
-                                                       const uint8_t* src_scan,
-                                                       int width,
-                                                       int src_Bpp) {
+void CompositeRow_Rgb2Argb_NoBlend_NoClip_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int width,
+    int src_Bpp) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
   for (int col = 0; col < width; col++) {
     if (src_Bpp == 4) {
       FXARGB_SETRGBORDERDIB(dest_scan, 0xff000000 | FXARGB_GETDIB(src_scan));
@@ -2119,21 +1462,22 @@
   }
 }
 
-void CompositeRow_Rgb2Rgb_Blend_NoClip_RgbByteOrder(uint8_t* dest_scan,
-                                                    const uint8_t* src_scan,
-                                                    int width,
-                                                    BlendMode blend_type,
-                                                    int dest_Bpp,
-                                                    int src_Bpp) {
+void CompositeRow_Rgb2Rgb_Blend_NoClip_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int width,
+    BlendMode blend_type,
+    int dest_Bpp,
+    int src_Bpp) {
   int blended_colors[3];
   bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
   int src_gap = src_Bpp - 3;
   for (int col = 0; col < width; col++) {
     if (bNonseparableBlend) {
       uint8_t dest_scan_o[3];
-      dest_scan_o[0] = dest_scan[2];
-      dest_scan_o[1] = dest_scan[1];
-      dest_scan_o[2] = dest_scan[0];
+      ReverseCopy3Bytes(dest_scan_o, dest_scan);
       RGB_Blend(blend_type, src_scan, dest_scan_o, blended_colors);
     }
     for (int color = 0; color < 3; color++) {
@@ -2151,11 +1495,15 @@
   }
 }
 
-void CompositeRow_Argb2Rgb_NoBlend_RgbByteOrder(uint8_t* dest_scan,
-                                                const uint8_t* src_scan,
-                                                int width,
-                                                int dest_Bpp,
-                                                const uint8_t* clip_scan) {
+void CompositeRow_Argb2Rgb_NoBlend_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int width,
+    int dest_Bpp,
+    pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   for (int col = 0; col < width; col++) {
     uint8_t src_alpha;
     if (clip_scan) {
@@ -2164,11 +1512,9 @@
       src_alpha = src_scan[3];
     }
     if (src_alpha == 255) {
-      dest_scan[2] = *src_scan++;
-      dest_scan[1] = *src_scan++;
-      dest_scan[0] = *src_scan++;
+      ReverseCopy3Bytes(dest_scan, src_scan);
       dest_scan += dest_Bpp;
-      src_scan++;
+      src_scan += 4;
       continue;
     }
     if (src_alpha == 0) {
@@ -2187,26 +1533,31 @@
   }
 }
 
-void CompositeRow_Rgb2Rgb_NoBlend_NoClip_RgbByteOrder(uint8_t* dest_scan,
-                                                      const uint8_t* src_scan,
-                                                      int width,
-                                                      int dest_Bpp,
-                                                      int src_Bpp) {
+void CompositeRow_Rgb2Rgb_NoBlend_NoClip_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int width,
+    int dest_Bpp,
+    int src_Bpp) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
   for (int col = 0; col < width; col++) {
-    dest_scan[2] = src_scan[0];
-    dest_scan[1] = src_scan[1];
-    dest_scan[0] = src_scan[2];
+    ReverseCopy3Bytes(dest_scan, src_scan);
     dest_scan += dest_Bpp;
     src_scan += src_Bpp;
   }
 }
 
-void CompositeRow_Rgb2Argb_Blend_Clip_RgbByteOrder(uint8_t* dest_scan,
-                                                   const uint8_t* src_scan,
-                                                   int width,
-                                                   BlendMode blend_type,
-                                                   int src_Bpp,
-                                                   const uint8_t* clip_scan) {
+void CompositeRow_Rgb2Argb_Blend_Clip_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int width,
+    BlendMode blend_type,
+    int src_Bpp,
+    pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   int blended_colors[3];
   bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   int src_gap = src_Bpp - 3;
@@ -2214,10 +1565,8 @@
     int src_alpha = *clip_scan++;
     uint8_t back_alpha = dest_scan[3];
     if (back_alpha == 0) {
-      dest_scan[2] = *src_scan++;
-      dest_scan[1] = *src_scan++;
-      dest_scan[0] = *src_scan++;
-      src_scan += src_gap;
+      ReverseCopy3Bytes(dest_scan, src_scan);
+      src_scan += src_Bpp;
       dest_scan += 4;
       continue;
     }
@@ -2231,9 +1580,7 @@
     int alpha_ratio = src_alpha * 255 / dest_alpha;
     if (bNonseparableBlend) {
       uint8_t dest_scan_o[3];
-      dest_scan_o[0] = dest_scan[2];
-      dest_scan_o[1] = dest_scan[1];
-      dest_scan_o[2] = dest_scan[0];
+      ReverseCopy3Bytes(dest_scan_o, dest_scan);
       RGB_Blend(blend_type, src_scan, dest_scan_o, blended_colors);
     }
     for (int color = 0; color < 3; color++) {
@@ -2252,13 +1599,17 @@
   }
 }
 
-void CompositeRow_Rgb2Rgb_Blend_Clip_RgbByteOrder(uint8_t* dest_scan,
-                                                  const uint8_t* src_scan,
-                                                  int width,
-                                                  BlendMode blend_type,
-                                                  int dest_Bpp,
-                                                  int src_Bpp,
-                                                  const uint8_t* clip_scan) {
+void CompositeRow_Rgb2Rgb_Blend_Clip_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int width,
+    BlendMode blend_type,
+    int dest_Bpp,
+    int src_Bpp,
+    pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   int blended_colors[3];
   bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   int src_gap = src_Bpp - 3;
@@ -2271,9 +1622,7 @@
     }
     if (bNonseparableBlend) {
       uint8_t dest_scan_o[3];
-      dest_scan_o[0] = dest_scan[2];
-      dest_scan_o[1] = dest_scan[1];
-      dest_scan_o[2] = dest_scan[0];
+      ReverseCopy3Bytes(dest_scan_o, dest_scan);
       RGB_Blend(blend_type, src_scan, dest_scan_o, blended_colors);
     }
     for (int color = 0; color < 3; color++) {
@@ -2291,21 +1640,23 @@
   }
 }
 
-void CompositeRow_Rgb2Argb_NoBlend_Clip_RgbByteOrder(uint8_t* dest_scan,
-                                                     const uint8_t* src_scan,
-                                                     int width,
-                                                     int src_Bpp,
-                                                     const uint8_t* clip_scan) {
+void CompositeRow_Rgb2Argb_NoBlend_Clip_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int width,
+    int src_Bpp,
+    pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   int src_gap = src_Bpp - 3;
   for (int col = 0; col < width; col++) {
     int src_alpha = clip_scan[col];
     if (src_alpha == 255) {
-      dest_scan[2] = *src_scan++;
-      dest_scan[1] = *src_scan++;
-      dest_scan[0] = *src_scan++;
+      ReverseCopy3Bytes(dest_scan, src_scan);
       dest_scan[3] = 255;
       dest_scan += 4;
-      src_scan += src_gap;
+      src_scan += src_Bpp;
       continue;
     }
     if (src_alpha == 0) {
@@ -2328,18 +1679,20 @@
   }
 }
 
-void CompositeRow_Rgb2Rgb_NoBlend_Clip_RgbByteOrder(uint8_t* dest_scan,
-                                                    const uint8_t* src_scan,
-                                                    int width,
-                                                    int dest_Bpp,
-                                                    int src_Bpp,
-                                                    const uint8_t* clip_scan) {
+void CompositeRow_Rgb2Rgb_NoBlend_Clip_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int width,
+    int dest_Bpp,
+    int src_Bpp,
+    pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   for (int col = 0; col < width; col++) {
     int src_alpha = clip_scan[col];
     if (src_alpha == 255) {
-      dest_scan[2] = src_scan[0];
-      dest_scan[1] = src_scan[1];
-      dest_scan[0] = src_scan[2];
+      ReverseCopy3Bytes(dest_scan, src_scan);
     } else if (src_alpha) {
       dest_scan[2] = FXDIB_ALPHA_MERGE(dest_scan[2], *src_scan, src_alpha);
       src_scan++;
@@ -2355,14 +1708,19 @@
   }
 }
 
-void CompositeRow_8bppRgb2Rgb_NoBlend_RgbByteOrder(uint8_t* dest_scan,
-                                                   const uint8_t* src_scan,
-                                                   FX_ARGB* pPalette,
-                                                   int pixel_count,
-                                                   int DestBpp,
-                                                   const uint8_t* clip_scan) {
+void CompositeRow_8bppRgb2Rgb_NoBlend_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    const FX_ARGB* pPalette,
+    int pixel_count,
+    int DestBpp,
+    pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   for (int col = 0; col < pixel_count; col++) {
-    FX_ARGB argb = pPalette ? pPalette[*src_scan] : (*src_scan) * 0x010101;
+    FX_ARGB argb = pPalette ? pPalette[*src_scan]
+                            : ArgbEncode(0, *src_scan, *src_scan, *src_scan);
     int src_r = FXARGB_R(argb);
     int src_g = FXARGB_G(argb);
     int src_b = FXARGB_B(argb);
@@ -2380,28 +1738,38 @@
   }
 }
 
-void CompositeRow_1bppRgb2Rgb_NoBlend_RgbByteOrder(uint8_t* dest_scan,
-                                                   const uint8_t* src_scan,
-                                                   int src_left,
-                                                   FX_ARGB* pPalette,
-                                                   int pixel_count,
-                                                   int DestBpp,
-                                                   const uint8_t* clip_scan) {
-  int reset_r, reset_g, reset_b;
-  int set_r, set_g, set_b;
-  if (pPalette) {
-    reset_r = FXARGB_R(pPalette[0]);
-    reset_g = FXARGB_G(pPalette[0]);
-    reset_b = FXARGB_B(pPalette[0]);
-    set_r = FXARGB_R(pPalette[1]);
-    set_g = FXARGB_G(pPalette[1]);
-    set_b = FXARGB_B(pPalette[1]);
+void CompositeRow_1bppRgb2Rgb_NoBlend_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int src_left,
+    pdfium::span<const FX_ARGB> src_palette,
+    int pixel_count,
+    int DestBpp,
+    pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
+  int reset_r;
+  int reset_g;
+  int reset_b;
+  int set_r;
+  int set_g;
+  int set_b;
+  if (!src_palette.empty()) {
+    reset_r = FXARGB_R(src_palette[0]);
+    reset_g = FXARGB_G(src_palette[0]);
+    reset_b = FXARGB_B(src_palette[0]);
+    set_r = FXARGB_R(src_palette[1]);
+    set_g = FXARGB_G(src_palette[1]);
+    set_b = FXARGB_B(src_palette[1]);
   } else {
     reset_r = reset_g = reset_b = 0;
     set_r = set_g = set_b = 255;
   }
   for (int col = 0; col < pixel_count; col++) {
-    int src_r, src_g, src_b;
+    int src_r;
+    int src_g;
+    int src_b;
     if (src_scan[(col + src_left) / 8] & (1 << (7 - (col + src_left) % 8))) {
       src_r = set_r;
       src_g = set_g;
@@ -2424,13 +1792,19 @@
   }
 }
 
-void CompositeRow_8bppRgb2Argb_NoBlend_RgbByteOrder(uint8_t* dest_scan,
-                                                    const uint8_t* src_scan,
-                                                    int width,
-                                                    FX_ARGB* pPalette,
-                                                    const uint8_t* clip_scan) {
+void CompositeRow_8bppRgb2Argb_NoBlend_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int width,
+    const FX_ARGB* pPalette,
+    pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   for (int col = 0; col < width; col++) {
-    int src_r, src_g, src_b;
+    int src_r;
+    int src_g;
+    int src_b;
     if (pPalette) {
       FX_ARGB argb = pPalette[*src_scan];
       src_r = FXARGB_R(argb);
@@ -2466,27 +1840,37 @@
   }
 }
 
-void CompositeRow_1bppRgb2Argb_NoBlend_RgbByteOrder(uint8_t* dest_scan,
-                                                    const uint8_t* src_scan,
-                                                    int src_left,
-                                                    int width,
-                                                    FX_ARGB* pPalette,
-                                                    const uint8_t* clip_scan) {
-  int reset_r, reset_g, reset_b;
-  int set_r, set_g, set_b;
-  if (pPalette) {
-    reset_r = FXARGB_R(pPalette[0]);
-    reset_g = FXARGB_G(pPalette[0]);
-    reset_b = FXARGB_B(pPalette[0]);
-    set_r = FXARGB_R(pPalette[1]);
-    set_g = FXARGB_G(pPalette[1]);
-    set_b = FXARGB_B(pPalette[1]);
+void CompositeRow_1bppRgb2Argb_NoBlend_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int src_left,
+    int width,
+    pdfium::span<const FX_ARGB> src_palette,
+    pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
+  int reset_r;
+  int reset_g;
+  int reset_b;
+  int set_r;
+  int set_g;
+  int set_b;
+  if (!src_palette.empty()) {
+    reset_r = FXARGB_R(src_palette[0]);
+    reset_g = FXARGB_G(src_palette[0]);
+    reset_b = FXARGB_B(src_palette[0]);
+    set_r = FXARGB_R(src_palette[1]);
+    set_g = FXARGB_G(src_palette[1]);
+    set_b = FXARGB_B(src_palette[1]);
   } else {
     reset_r = reset_g = reset_b = 0;
     set_r = set_g = set_b = 255;
   }
   for (int col = 0; col < width; col++) {
-    int src_r, src_g, src_b;
+    int src_r;
+    int src_g;
+    int src_b;
     if (src_scan[(col + src_left) / 8] & (1 << (7 - (col + src_left) % 8))) {
       src_r = set_r;
       src_g = set_g;
@@ -2520,15 +1904,19 @@
   }
 }
 
-void CompositeRow_ByteMask2Argb_RgbByteOrder(uint8_t* dest_scan,
-                                             const uint8_t* src_scan,
-                                             int mask_alpha,
-                                             int src_r,
-                                             int src_g,
-                                             int src_b,
-                                             int pixel_count,
-                                             BlendMode blend_type,
-                                             const uint8_t* clip_scan) {
+void CompositeRow_ByteMask2Argb_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int mask_alpha,
+    int src_r,
+    int src_g,
+    int src_b,
+    int pixel_count,
+    BlendMode blend_type,
+    pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   for (int col = 0; col < pixel_count; col++) {
     int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col);
     uint8_t back_alpha = dest_scan[3];
@@ -2550,7 +1938,8 @@
       uint8_t scan[3] = {static_cast<uint8_t>(src_b),
                          static_cast<uint8_t>(src_g),
                          static_cast<uint8_t>(src_r)};
-      uint8_t dest_scan_o[3] = {dest_scan[2], dest_scan[1], dest_scan[0]};
+      uint8_t dest_scan_o[3];
+      ReverseCopy3Bytes(dest_scan_o, dest_scan);
       RGB_Blend(blend_type, scan, dest_scan_o, blended_colors);
       dest_scan[2] =
           FXDIB_ALPHA_MERGE(dest_scan[2], blended_colors[0], alpha_ratio);
@@ -2577,16 +1966,20 @@
   }
 }
 
-void CompositeRow_ByteMask2Rgb_RgbByteOrder(uint8_t* dest_scan,
-                                            const uint8_t* src_scan,
-                                            int mask_alpha,
-                                            int src_r,
-                                            int src_g,
-                                            int src_b,
-                                            int pixel_count,
-                                            BlendMode blend_type,
-                                            int Bpp,
-                                            const uint8_t* clip_scan) {
+void CompositeRow_ByteMask2Rgb_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int mask_alpha,
+    int src_r,
+    int src_g,
+    int src_b,
+    int pixel_count,
+    BlendMode blend_type,
+    int Bpp,
+    pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   for (int col = 0; col < pixel_count; col++) {
     int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col);
     if (src_alpha == 0) {
@@ -2598,7 +1991,8 @@
       uint8_t scan[3] = {static_cast<uint8_t>(src_b),
                          static_cast<uint8_t>(src_g),
                          static_cast<uint8_t>(src_r)};
-      uint8_t dest_scan_o[3] = {dest_scan[2], dest_scan[1], dest_scan[0]};
+      uint8_t dest_scan_o[3];
+      ReverseCopy3Bytes(dest_scan_o, dest_scan);
       RGB_Blend(blend_type, scan, dest_scan_o, blended_colors);
       dest_scan[2] =
           FXDIB_ALPHA_MERGE(dest_scan[2], blended_colors[0], src_alpha);
@@ -2622,16 +2016,20 @@
   }
 }
 
-void CompositeRow_BitMask2Argb_RgbByteOrder(uint8_t* dest_scan,
-                                            const uint8_t* src_scan,
-                                            int mask_alpha,
-                                            int src_r,
-                                            int src_g,
-                                            int src_b,
-                                            int src_left,
-                                            int pixel_count,
-                                            BlendMode blend_type,
-                                            const uint8_t* clip_scan) {
+void CompositeRow_BitMask2Argb_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int mask_alpha,
+    int src_r,
+    int src_g,
+    int src_b,
+    int src_left,
+    int pixel_count,
+    BlendMode blend_type,
+    pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   if (blend_type == BlendMode::kNormal && !clip_scan && mask_alpha == 255) {
     FX_ARGB argb = ArgbEncode(0xff, src_r, src_g, src_b);
     for (int col = 0; col < pixel_count; col++) {
@@ -2663,7 +2061,8 @@
       uint8_t scan[3] = {static_cast<uint8_t>(src_b),
                          static_cast<uint8_t>(src_g),
                          static_cast<uint8_t>(src_r)};
-      uint8_t dest_scan_o[3] = {dest_scan[2], dest_scan[1], dest_scan[0]};
+      uint8_t dest_scan_o[3];
+      ReverseCopy3Bytes(dest_scan_o, dest_scan);
       RGB_Blend(blend_type, scan, dest_scan_o, blended_colors);
       dest_scan[2] =
           FXDIB_ALPHA_MERGE(dest_scan[2], blended_colors[0], alpha_ratio);
@@ -2690,17 +2089,21 @@
   }
 }
 
-void CompositeRow_BitMask2Rgb_RgbByteOrder(uint8_t* dest_scan,
-                                           const uint8_t* src_scan,
-                                           int mask_alpha,
-                                           int src_r,
-                                           int src_g,
-                                           int src_b,
-                                           int src_left,
-                                           int pixel_count,
-                                           BlendMode blend_type,
-                                           int Bpp,
-                                           const uint8_t* clip_scan) {
+void CompositeRow_BitMask2Rgb_RgbByteOrder(
+    pdfium::span<uint8_t> dest_span,
+    pdfium::span<const uint8_t> src_span,
+    int mask_alpha,
+    int src_r,
+    int src_g,
+    int src_b,
+    int src_left,
+    int pixel_count,
+    BlendMode blend_type,
+    int Bpp,
+    pdfium::span<const uint8_t> clip_span) {
+  uint8_t* dest_scan = dest_span.data();
+  const uint8_t* src_scan = src_span.data();
+  const uint8_t* clip_scan = clip_span.data();
   if (blend_type == BlendMode::kNormal && !clip_scan && mask_alpha == 255) {
     for (int col = 0; col < pixel_count; col++) {
       if (src_scan[(src_left + col) / 8] & (1 << (7 - (src_left + col) % 8))) {
@@ -2727,7 +2130,8 @@
       uint8_t scan[3] = {static_cast<uint8_t>(src_b),
                          static_cast<uint8_t>(src_g),
                          static_cast<uint8_t>(src_r)};
-      uint8_t dest_scan_o[3] = {dest_scan[2], dest_scan[1], dest_scan[0]};
+      uint8_t dest_scan_o[3];
+      ReverseCopy3Bytes(dest_scan_o, dest_scan);
       RGB_Blend(blend_type, scan, dest_scan_o, blended_colors);
       dest_scan[2] =
           FXDIB_ALPHA_MERGE(dest_scan[2], blended_colors[0], src_alpha);
@@ -2762,8 +2166,7 @@
 
 bool CFX_ScanlineCompositor::Init(FXDIB_Format dest_format,
                                   FXDIB_Format src_format,
-                                  int32_t width,
-                                  uint32_t* pSrcPalette,
+                                  pdfium::span<const uint32_t> src_palette,
                                   uint32_t mask_color,
                                   BlendMode blend_type,
                                   bool bClip,
@@ -2772,31 +2175,27 @@
   m_DestFormat = dest_format;
   m_BlendType = blend_type;
   m_bRgbByteOrder = bRgbByteOrder;
-  if (GetBppFromFormat(dest_format) == 1)
+  m_bClip = bClip;
+  if (m_DestFormat == FXDIB_Format::k1bppMask ||
+      m_DestFormat == FXDIB_Format::k1bppRgb) {
     return false;
-  if (m_SrcFormat == FXDIB_1bppMask || m_SrcFormat == FXDIB_8bppMask) {
+  }
+
+  if (m_bRgbByteOrder && (m_DestFormat == FXDIB_Format::k8bppMask ||
+                          m_DestFormat == FXDIB_Format::k8bppRgb)) {
+    return false;
+  }
+
+  if (m_SrcFormat == FXDIB_Format::k1bppMask ||
+      m_SrcFormat == FXDIB_Format::k8bppMask) {
     InitSourceMask(mask_color);
     return true;
   }
-  if (!GetIsCmykFromFormat(src_format) && GetIsCmykFromFormat(dest_format))
-    return false;
-  if (GetBppFromFormat(m_SrcFormat) <= 8) {
-    if (dest_format == FXDIB_8bppMask)
-      return true;
-
-    InitSourcePalette(src_format, dest_format, pSrcPalette);
-    m_iTransparency = (dest_format == FXDIB_Argb ? 1 : 0) +
-                      (GetIsAlphaFromFormat(dest_format) ? 2 : 0) +
-                      (GetIsCmykFromFormat(dest_format) ? 4 : 0) +
-                      (GetBppFromFormat(src_format) == 1 ? 8 : 0);
-    return true;
+  if ((m_SrcFormat == FXDIB_Format::k1bppRgb ||
+       m_SrcFormat == FXDIB_Format::k8bppRgb) &&
+      m_DestFormat != FXDIB_Format::k8bppMask) {
+    InitSourcePalette(src_palette);
   }
-  m_iTransparency = (GetIsAlphaFromFormat(src_format) ? 0 : 1) +
-                    (GetIsAlphaFromFormat(dest_format) ? 0 : 2) +
-                    (blend_type == BlendMode::kNormal ? 4 : 0) +
-                    (bClip ? 8 : 0) +
-                    (GetIsCmykFromFormat(src_format) ? 16 : 0) +
-                    (GetIsCmykFromFormat(dest_format) ? 32 : 0);
   return true;
 }
 
@@ -2805,382 +2204,314 @@
   m_MaskRed = FXARGB_R(mask_color);
   m_MaskGreen = FXARGB_G(mask_color);
   m_MaskBlue = FXARGB_B(mask_color);
-  if (m_DestFormat == FXDIB_8bppMask)
+  if (m_DestFormat == FXDIB_Format::k8bppMask)
     return;
 
-  if (GetBppFromFormat(m_DestFormat) == 8) {
+  if (m_DestFormat == FXDIB_Format::k8bppRgb)
     m_MaskRed = FXRGB2GRAY(m_MaskRed, m_MaskGreen, m_MaskBlue);
-    if (GetIsCmykFromFormat(m_DestFormat))
-      m_MaskRed = FX_CCOLOR(m_MaskRed);
-  }
 }
 
-void CFX_ScanlineCompositor::InitSourcePalette(FXDIB_Format src_format,
-                                               FXDIB_Format dest_format,
-                                               const uint32_t* pSrcPalette) {
-  bool bIsSrcCmyk = GetIsCmykFromFormat(src_format);
-  bool bIsDstCmyk = GetIsCmykFromFormat(dest_format);
-  bool bIsDestBpp8 = GetBppFromFormat(dest_format) == 8;
-  int pal_count = 1 << GetBppFromFormat(src_format);
-  m_pSrcPalette = nullptr;
-  if (pSrcPalette) {
+void CFX_ScanlineCompositor::InitSourcePalette(
+    pdfium::span<const uint32_t> src_palette) {
+  DCHECK_NE(m_DestFormat, FXDIB_Format::k8bppMask);
+
+  m_SrcPalette.Reset();
+  const bool bIsDestBpp8 = m_DestFormat == FXDIB_Format::k8bppRgb;
+  const size_t pal_count = static_cast<size_t>(1)
+                           << GetBppFromFormat(m_SrcFormat);
+
+  if (!src_palette.empty()) {
     if (bIsDestBpp8) {
-      uint8_t* gray_pal = FX_Alloc(uint8_t, pal_count);
-      m_pSrcPalette.reset(reinterpret_cast<uint32_t*>(gray_pal));
-      if (bIsSrcCmyk) {
-        for (int i = 0; i < pal_count; ++i) {
-          FX_CMYK cmyk = pSrcPalette[i];
-          uint8_t r;
-          uint8_t g;
-          uint8_t b;
-          std::tie(r, g, b) =
-              AdobeCMYK_to_sRGB1(FXSYS_GetCValue(cmyk), FXSYS_GetMValue(cmyk),
-                                 FXSYS_GetYValue(cmyk), FXSYS_GetKValue(cmyk));
-          *gray_pal++ = FXRGB2GRAY(r, g, b);
-        }
-      } else {
-        for (int i = 0; i < pal_count; ++i) {
-          FX_ARGB argb = pSrcPalette[i];
-          *gray_pal++ =
-              FXRGB2GRAY(FXARGB_R(argb), FXARGB_G(argb), FXARGB_B(argb));
-        }
+      pdfium::span<uint8_t> gray_pal = m_SrcPalette.Make8BitPalette(pal_count);
+      for (size_t i = 0; i < pal_count; ++i) {
+        FX_ARGB argb = src_palette[i];
+        gray_pal[i] =
+            FXRGB2GRAY(FXARGB_R(argb), FXARGB_G(argb), FXARGB_B(argb));
       }
       return;
     }
-    m_pSrcPalette.reset(FX_Alloc(uint32_t, pal_count));
-    uint32_t* pPalette = m_pSrcPalette.get();
-    if (bIsDstCmyk == bIsSrcCmyk) {
-      memcpy(pPalette, pSrcPalette, pal_count * sizeof(uint32_t));
-    } else {
-      for (int i = 0; i < pal_count; ++i) {
-        FX_CMYK cmyk = pSrcPalette[i];
-        uint8_t r;
-        uint8_t g;
-        uint8_t b;
-        std::tie(r, g, b) =
-            AdobeCMYK_to_sRGB1(FXSYS_GetCValue(cmyk), FXSYS_GetMValue(cmyk),
-                               FXSYS_GetYValue(cmyk), FXSYS_GetKValue(cmyk));
-        pPalette[i] = ArgbEncode(0xff, r, g, b);
-      }
-    }
+    pdfium::span<uint32_t> pPalette = m_SrcPalette.Make32BitPalette(pal_count);
+    for (size_t i = 0; i < pal_count; ++i)
+      pPalette[i] = src_palette[i];
     return;
   }
   if (bIsDestBpp8) {
-    uint8_t* gray_pal = FX_Alloc(uint8_t, pal_count);
+    pdfium::span<uint8_t> gray_pal = m_SrcPalette.Make8BitPalette(pal_count);
     if (pal_count == 2) {
       gray_pal[0] = 0;
       gray_pal[1] = 255;
     } else {
-      for (int i = 0; i < pal_count; ++i)
+      for (size_t i = 0; i < pal_count; ++i)
         gray_pal[i] = i;
     }
-    m_pSrcPalette.reset(reinterpret_cast<uint32_t*>(gray_pal));
     return;
   }
-  m_pSrcPalette.reset(FX_Alloc(uint32_t, pal_count));
-  uint32_t* pPalette = m_pSrcPalette.get();
+  pdfium::span<uint32_t> pPalette = m_SrcPalette.Make32BitPalette(pal_count);
   if (pal_count == 2) {
-    pPalette[0] = bIsSrcCmyk ? 255 : 0xff000000;
-    pPalette[1] = bIsSrcCmyk ? 0 : 0xffffffff;
+    pPalette[0] = 0xff000000;
+    pPalette[1] = 0xffffffff;
   } else {
-    for (int i = 0; i < pal_count; ++i)
-      pPalette[i] = bIsSrcCmyk ? FX_CCOLOR(i) : (i * 0x10101);
-  }
-  if (bIsSrcCmyk != bIsDstCmyk) {
-    for (int i = 0; i < pal_count; ++i) {
-      FX_CMYK cmyk = pPalette[i];
-      uint8_t r;
-      uint8_t g;
-      uint8_t b;
-      std::tie(r, g, b) =
-          AdobeCMYK_to_sRGB1(FXSYS_GetCValue(cmyk), FXSYS_GetMValue(cmyk),
-                             FXSYS_GetYValue(cmyk), FXSYS_GetKValue(cmyk));
-      pPalette[i] = ArgbEncode(0xff, r, g, b);
+    for (size_t i = 0; i < pal_count; ++i) {
+      uint32_t v = static_cast<uint32_t>(i);
+      pPalette[i] = ArgbEncode(0, v, v, v);
     }
   }
 }
 
 void CFX_ScanlineCompositor::CompositeRgbBitmapLine(
-    uint8_t* dest_scan,
-    const uint8_t* src_scan,
+    pdfium::span<uint8_t> dest_scan,
+    pdfium::span<const uint8_t> src_scan,
     int width,
-    const uint8_t* clip_scan,
-    const uint8_t* src_extra_alpha,
-    uint8_t* dst_extra_alpha) {
+    pdfium::span<const uint8_t> clip_scan) const {
+  DCHECK(m_SrcFormat == FXDIB_Format::kRgb ||
+         m_SrcFormat == FXDIB_Format::kRgb32 ||
+         m_SrcFormat == FXDIB_Format::kArgb);
+
   int src_Bpp = GetCompsFromFormat(m_SrcFormat);
   int dest_Bpp = GetCompsFromFormat(m_DestFormat);
   if (m_bRgbByteOrder) {
-    switch (m_iTransparency) {
-      case 0:
-      case 4:
-      case 8:
-      case 12:
+    if (m_SrcFormat == FXDIB_Format::kArgb) {
+      if (m_DestFormat == FXDIB_Format::kArgb) {
         CompositeRow_Argb2Argb_RgbByteOrder(dest_scan, src_scan, width,
                                             m_BlendType, clip_scan);
-        break;
-      case 1:
-        CompositeRow_Rgb2Argb_Blend_NoClip_RgbByteOrder(
-            dest_scan, src_scan, width, m_BlendType, src_Bpp);
-        break;
-      case 2:
-      case 10:
-        CompositeRow_Argb2Rgb_Blend_RgbByteOrder(
-            dest_scan, src_scan, width, m_BlendType, dest_Bpp, clip_scan);
-        break;
-      case 3:
-        CompositeRow_Rgb2Rgb_Blend_NoClip_RgbByteOrder(
-            dest_scan, src_scan, width, m_BlendType, dest_Bpp, src_Bpp);
-        break;
-      case 5:
-        CompositeRow_Rgb2Argb_NoBlend_NoClip_RgbByteOrder(dest_scan, src_scan,
-                                                          width, src_Bpp);
-        break;
-      case 6:
-      case 14:
+        return;
+      }
+      if (m_BlendType == BlendMode::kNormal) {
         CompositeRow_Argb2Rgb_NoBlend_RgbByteOrder(dest_scan, src_scan, width,
                                                    dest_Bpp, clip_scan);
-        break;
-      case 7:
-        CompositeRow_Rgb2Rgb_NoBlend_NoClip_RgbByteOrder(
-            dest_scan, src_scan, width, dest_Bpp, src_Bpp);
-        break;
-      case 9:
+        return;
+      }
+      CompositeRow_Argb2Rgb_Blend_RgbByteOrder(
+          dest_scan, src_scan, width, m_BlendType, dest_Bpp, clip_scan);
+      return;
+    }
+
+    if (m_DestFormat == FXDIB_Format::kArgb) {
+      if (m_BlendType == BlendMode::kNormal) {
+        if (m_bClip) {
+          CompositeRow_Rgb2Argb_NoBlend_Clip_RgbByteOrder(
+              dest_scan, src_scan, width, src_Bpp, clip_scan);
+          return;
+        }
+        CompositeRow_Rgb2Argb_NoBlend_NoClip_RgbByteOrder(dest_scan, src_scan,
+                                                          width, src_Bpp);
+        return;
+      }
+      if (m_bClip) {
         CompositeRow_Rgb2Argb_Blend_Clip_RgbByteOrder(
             dest_scan, src_scan, width, m_BlendType, src_Bpp, clip_scan);
-        break;
-      case 11:
-        CompositeRow_Rgb2Rgb_Blend_Clip_RgbByteOrder(dest_scan, src_scan, width,
-                                                     m_BlendType, dest_Bpp,
-                                                     src_Bpp, clip_scan);
-        break;
-      case 13:
-        CompositeRow_Rgb2Argb_NoBlend_Clip_RgbByteOrder(
-            dest_scan, src_scan, width, src_Bpp, clip_scan);
-        break;
-      case 15:
+        return;
+      }
+      CompositeRow_Rgb2Argb_Blend_NoClip_RgbByteOrder(
+          dest_scan, src_scan, width, m_BlendType, src_Bpp);
+      return;
+    }
+
+    if (m_BlendType == BlendMode::kNormal) {
+      if (m_bClip) {
         CompositeRow_Rgb2Rgb_NoBlend_Clip_RgbByteOrder(
             dest_scan, src_scan, width, dest_Bpp, src_Bpp, clip_scan);
-        break;
+        return;
+      }
+      CompositeRow_Rgb2Rgb_NoBlend_NoClip_RgbByteOrder(
+          dest_scan, src_scan, width, dest_Bpp, src_Bpp);
+      return;
+    }
+    if (m_bClip) {
+      CompositeRow_Rgb2Rgb_Blend_Clip_RgbByteOrder(dest_scan, src_scan, width,
+                                                   m_BlendType, dest_Bpp,
+                                                   src_Bpp, clip_scan);
+      return;
+    }
+    CompositeRow_Rgb2Rgb_Blend_NoClip_RgbByteOrder(
+        dest_scan, src_scan, width, m_BlendType, dest_Bpp, src_Bpp);
+    return;
+  }
+
+  if (m_DestFormat == FXDIB_Format::k8bppMask) {
+    if (m_SrcFormat == FXDIB_Format::kArgb) {
+      CompositeRow_AlphaToMask(dest_scan, src_scan, width, clip_scan, 4);
+    } else {
+      CompositeRow_Rgb2Mask(dest_scan, width, clip_scan);
     }
     return;
   }
-  if (m_DestFormat == FXDIB_8bppMask) {
-    if (GetIsAlphaFromFormat(m_SrcFormat)) {
-      if (m_SrcFormat == FXDIB_Argb) {
-        CompositeRow_AlphaToMask(dest_scan, src_scan, width, clip_scan, 4);
-      } else {
-        CompositeRow_AlphaToMask(dest_scan, src_extra_alpha, width, clip_scan,
-                                 1);
-      }
+
+  if (m_DestFormat == FXDIB_Format::k8bppRgb) {
+    if (m_SrcFormat == FXDIB_Format::kArgb) {
+      CompositeRow_Argb2Gray(dest_scan, src_scan, width, m_BlendType,
+                             clip_scan);
     } else {
-      CompositeRow_Rgb2Mask(dest_scan, src_scan, width, clip_scan);
+      CompositeRow_Rgb2Gray(dest_scan, src_scan, src_Bpp, width, m_BlendType,
+                            clip_scan);
     }
-  } else if (GetBppFromFormat(m_DestFormat) == 8) {
-    if (GetIsCmykFromFormat(m_DestFormat)) {
-      for (int i = 0; i < width; ++i) {
-        *dest_scan = ~*dest_scan;
-        dest_scan++;
-      }
-    }
-    if (GetIsAlphaFromFormat(m_SrcFormat)) {
-      if (GetIsAlphaFromFormat(m_DestFormat)) {
-        CompositeRow_Argb2Graya(dest_scan, src_scan, width, m_BlendType,
-                                clip_scan, src_extra_alpha, dst_extra_alpha);
-      } else {
-        CompositeRow_Argb2Gray(dest_scan, src_scan, width, m_BlendType,
-                               clip_scan, src_extra_alpha);
-      }
-    } else {
-      if (GetIsAlphaFromFormat(m_DestFormat)) {
-        CompositeRow_Rgb2Graya(dest_scan, src_scan, src_Bpp, width, m_BlendType,
-                               clip_scan, dst_extra_alpha);
-      } else {
-        CompositeRow_Rgb2Gray(dest_scan, src_scan, src_Bpp, width, m_BlendType,
-                              clip_scan);
-      }
-    }
-    if (GetIsCmykFromFormat(m_DestFormat)) {
-      for (int i = 0; i < width; ++i) {
-        *dest_scan = ~*dest_scan;
-        dest_scan++;
-      }
-    }
-  } else {
-    switch (m_iTransparency) {
-      case 0:
-      case 4:
-      case 8:
-      case 4 + 8: {
-        CompositeRow_Argb2Argb(dest_scan, src_scan, width, m_BlendType,
-                               clip_scan, dst_extra_alpha, src_extra_alpha);
-      } break;
-      case 1:
-        CompositeRow_Rgb2Argb_Blend_NoClip(
-            dest_scan, src_scan, width, m_BlendType, src_Bpp, dst_extra_alpha);
-        break;
-      case 1 + 8:
-        CompositeRow_Rgb2Argb_Blend_Clip(dest_scan, src_scan, width,
-                                         m_BlendType, src_Bpp, clip_scan,
-                                         dst_extra_alpha);
-        break;
-      case 1 + 4:
-        CompositeRow_Rgb2Argb_NoBlend_NoClip(dest_scan, src_scan, width,
-                                             src_Bpp, dst_extra_alpha);
-        break;
-      case 1 + 4 + 8:
-        CompositeRow_Rgb2Argb_NoBlend_Clip(dest_scan, src_scan, width, src_Bpp,
-                                           clip_scan, dst_extra_alpha);
-        break;
-      case 2:
-      case 2 + 8:
-        CompositeRow_Argb2Rgb_Blend(dest_scan, src_scan, width, m_BlendType,
-                                    dest_Bpp, clip_scan, src_extra_alpha);
-        break;
-      case 2 + 4:
-      case 2 + 4 + 8:
-        CompositeRow_Argb2Rgb_NoBlend(dest_scan, src_scan, width, dest_Bpp,
-                                      clip_scan, src_extra_alpha);
-        break;
-      case 1 + 2:
-        CompositeRow_Rgb2Rgb_Blend_NoClip(dest_scan, src_scan, width,
-                                          m_BlendType, dest_Bpp, src_Bpp);
-        break;
-      case 1 + 2 + 8:
-        CompositeRow_Rgb2Rgb_Blend_Clip(dest_scan, src_scan, width, m_BlendType,
-                                        dest_Bpp, src_Bpp, clip_scan);
-        break;
-      case 1 + 2 + 4:
-        CompositeRow_Rgb2Rgb_NoBlend_NoClip(dest_scan, src_scan, width,
-                                            dest_Bpp, src_Bpp);
-        break;
-      case 1 + 2 + 4 + 8:
-        CompositeRow_Rgb2Rgb_NoBlend_Clip(dest_scan, src_scan, width, dest_Bpp,
-                                          src_Bpp, clip_scan);
-        break;
-    }
+    return;
   }
+
+  // TODO(thestig): Tighten this check.
+  DCHECK_NE(GetBppFromFormat(m_DestFormat), 8);
+
+  if (m_SrcFormat == FXDIB_Format::kArgb) {
+    if (m_DestFormat == FXDIB_Format::kArgb) {
+      CompositeRow_Argb2Argb(dest_scan, src_scan, width, m_BlendType,
+                             clip_scan);
+      return;
+    }
+    if (m_BlendType == BlendMode::kNormal) {
+      CompositeRow_Argb2Rgb_NoBlend(dest_scan, src_scan, width, dest_Bpp,
+                                    clip_scan);
+      return;
+    }
+    CompositeRow_Argb2Rgb_Blend(dest_scan, src_scan, width, m_BlendType,
+                                dest_Bpp, clip_scan);
+    return;
+  }
+
+  if (m_DestFormat == FXDIB_Format::kArgb) {
+    if (m_BlendType == BlendMode::kNormal) {
+      if (m_bClip) {
+        CompositeRow_Rgb2Argb_NoBlend_Clip(dest_scan, src_scan, width, src_Bpp,
+                                           clip_scan);
+        return;
+      }
+      CompositeRow_Rgb2Argb_NoBlend_NoClip(dest_scan, src_scan, width, src_Bpp);
+      return;
+    }
+    if (m_bClip) {
+      CompositeRow_Rgb2Argb_Blend_Clip(dest_scan, src_scan, width, m_BlendType,
+                                       src_Bpp, clip_scan);
+      return;
+    }
+    CompositeRow_Rgb2Argb_Blend_NoClip(dest_scan, src_scan, width, m_BlendType,
+                                       src_Bpp);
+    return;
+  }
+
+  if (m_BlendType == BlendMode::kNormal) {
+    if (m_bClip) {
+      CompositeRow_Rgb2Rgb_NoBlend_Clip(dest_scan, src_scan, width, dest_Bpp,
+                                        src_Bpp, clip_scan);
+      return;
+    }
+    CompositeRow_Rgb2Rgb_NoBlend_NoClip(dest_scan, src_scan, width, dest_Bpp,
+                                        src_Bpp);
+    return;
+  }
+  if (m_bClip) {
+    CompositeRow_Rgb2Rgb_Blend_Clip(dest_scan, src_scan, width, m_BlendType,
+                                    dest_Bpp, src_Bpp, clip_scan);
+    return;
+  }
+  CompositeRow_Rgb2Rgb_Blend_NoClip(dest_scan, src_scan, width, m_BlendType,
+                                    dest_Bpp, src_Bpp);
 }
 
 void CFX_ScanlineCompositor::CompositePalBitmapLine(
-    uint8_t* dest_scan,
-    const uint8_t* src_scan,
+    pdfium::span<uint8_t> dest_scan,
+    pdfium::span<const uint8_t> src_scan,
     int src_left,
     int width,
-    const uint8_t* clip_scan,
-    const uint8_t* src_extra_alpha,
-    uint8_t* dst_extra_alpha) {
+    pdfium::span<const uint8_t> clip_scan) const {
+  DCHECK(m_SrcFormat == FXDIB_Format::k1bppRgb ||
+         m_SrcFormat == FXDIB_Format::k8bppRgb);
+
   if (m_bRgbByteOrder) {
-    if (m_SrcFormat == FXDIB_1bppRgb) {
-      if (m_DestFormat == FXDIB_8bppRgb) {
+    if (m_SrcFormat == FXDIB_Format::k1bppRgb) {
+      if (m_DestFormat == FXDIB_Format::k8bppRgb) {
         return;
       }
-      if (m_DestFormat == FXDIB_Argb) {
+      if (m_DestFormat == FXDIB_Format::kArgb) {
         CompositeRow_1bppRgb2Argb_NoBlend_RgbByteOrder(
-            dest_scan, src_scan, src_left, width, m_pSrcPalette.get(),
-            clip_scan);
+            dest_scan, src_scan, src_left, width,
+            m_SrcPalette.Get32BitPalette(), clip_scan);
       } else {
         CompositeRow_1bppRgb2Rgb_NoBlend_RgbByteOrder(
-            dest_scan, src_scan, src_left, m_pSrcPalette.get(), width,
-            GetCompsFromFormat(m_DestFormat), clip_scan);
+            dest_scan, src_scan, src_left, m_SrcPalette.Get32BitPalette(),
+            width, GetCompsFromFormat(m_DestFormat), clip_scan);
       }
     } else {
-      if (m_DestFormat == FXDIB_8bppRgb) {
+      if (m_DestFormat == FXDIB_Format::k8bppRgb) {
         return;
       }
-      if (m_DestFormat == FXDIB_Argb) {
+      if (m_DestFormat == FXDIB_Format::kArgb) {
         CompositeRow_8bppRgb2Argb_NoBlend_RgbByteOrder(
-            dest_scan, src_scan, width, m_pSrcPalette.get(), clip_scan);
+            dest_scan, src_scan, width, m_SrcPalette.Get32BitPalette().data(),
+            clip_scan);
       } else {
         CompositeRow_8bppRgb2Rgb_NoBlend_RgbByteOrder(
-            dest_scan, src_scan, m_pSrcPalette.get(), width,
+            dest_scan, src_scan, m_SrcPalette.Get32BitPalette().data(), width,
             GetCompsFromFormat(m_DestFormat), clip_scan);
       }
     }
     return;
   }
-  if (m_DestFormat == FXDIB_8bppMask) {
-    CompositeRow_Rgb2Mask(dest_scan, src_scan, width, clip_scan);
+
+  if (m_DestFormat == FXDIB_Format::k8bppMask) {
+    CompositeRow_Rgb2Mask(dest_scan, width, clip_scan);
     return;
   }
-  if (GetBppFromFormat(m_DestFormat) == 8) {
-    if (m_iTransparency & 8) {
-      if (GetIsAlphaFromFormat(m_DestFormat)) {
-        CompositeRow_1bppPal2Graya(
-            dest_scan, src_scan, src_left,
-            reinterpret_cast<const uint8_t*>(m_pSrcPalette.get()), width,
-            m_BlendType, clip_scan, dst_extra_alpha);
-      } else {
-        CompositeRow_1bppPal2Gray(
-            dest_scan, src_scan, src_left,
-            reinterpret_cast<const uint8_t*>(m_pSrcPalette.get()), width,
-            m_BlendType, clip_scan);
-      }
-    } else {
-      if (GetIsAlphaFromFormat(m_DestFormat)) {
-        CompositeRow_8bppPal2Graya(
-            dest_scan, src_scan,
-            reinterpret_cast<const uint8_t*>(m_pSrcPalette.get()), width,
-            m_BlendType, clip_scan, dst_extra_alpha, src_extra_alpha);
-      } else {
-        CompositeRow_8bppPal2Gray(
-            dest_scan, src_scan,
-            reinterpret_cast<const uint8_t*>(m_pSrcPalette.get()), width,
-            m_BlendType, clip_scan, src_extra_alpha);
-      }
+
+  if (m_DestFormat == FXDIB_Format::k8bppRgb) {
+    if (m_SrcFormat == FXDIB_Format::k1bppRgb) {
+      CompositeRow_1bppPal2Gray(dest_scan, src_scan, src_left,
+                                m_SrcPalette.Get8BitPalette(), width,
+                                m_BlendType, clip_scan);
+      return;
     }
-  } else {
-    switch (m_iTransparency) {
-      case 1 + 2:
-        CompositeRow_8bppRgb2Argb_NoBlend(dest_scan, src_scan, width,
-                                          m_pSrcPalette.get(), clip_scan,
-                                          src_extra_alpha);
-        break;
-      case 1 + 2 + 8:
-        CompositeRow_1bppRgb2Argb_NoBlend(dest_scan, src_scan, src_left, width,
-                                          m_pSrcPalette.get(), clip_scan);
-        break;
-      case 0:
-        CompositeRow_8bppRgb2Rgb_NoBlend(
-            dest_scan, src_scan, m_pSrcPalette.get(), width,
-            GetCompsFromFormat(m_DestFormat), clip_scan, src_extra_alpha);
-        break;
-      case 0 + 8:
-        CompositeRow_1bppRgb2Rgb_NoBlend(
-            dest_scan, src_scan, src_left, m_pSrcPalette.get(), width,
-            GetCompsFromFormat(m_DestFormat), clip_scan);
-        break;
-      case 0 + 2:
-        CompositeRow_8bppRgb2Rgb_NoBlend(
-            dest_scan, src_scan, m_pSrcPalette.get(), width,
-            GetCompsFromFormat(m_DestFormat), clip_scan, src_extra_alpha);
-        break;
-      case 0 + 2 + 8:
-        CompositeRow_1bppRgb2Rgba_NoBlend(dest_scan, src_scan, src_left, width,
-                                          m_pSrcPalette.get(), clip_scan,
-                                          dst_extra_alpha);
-        break;
-    }
+    CompositeRow_8bppPal2Gray(dest_scan, src_scan,
+                              m_SrcPalette.Get8BitPalette(), width, m_BlendType,
+                              clip_scan);
+    return;
   }
+
+  // TODO(thestig): Tighten this check.
+  DCHECK_NE(GetBppFromFormat(m_DestFormat), 8);
+
+  if (m_DestFormat == FXDIB_Format::kArgb) {
+    if (m_SrcFormat == FXDIB_Format::k1bppRgb) {
+      CompositeRow_1bppRgb2Argb_NoBlend(dest_scan, src_scan, src_left, width,
+                                        m_SrcPalette.Get32BitPalette(),
+                                        clip_scan);
+      return;
+    }
+    CompositeRow_8bppRgb2Argb_NoBlend(
+        dest_scan, src_scan, width, m_SrcPalette.Get32BitPalette(), clip_scan);
+    return;
+  }
+
+  if (m_SrcFormat == FXDIB_Format::k8bppRgb) {
+    CompositeRow_8bppRgb2Rgb_NoBlend(
+        dest_scan, src_scan, m_SrcPalette.Get32BitPalette(), width,
+        GetCompsFromFormat(m_DestFormat), clip_scan);
+    return;
+  }
+
+  CompositeRow_1bppRgb2Rgb_NoBlend(dest_scan, src_scan, src_left,
+                                   m_SrcPalette.Get32BitPalette(), width,
+                                   GetCompsFromFormat(m_DestFormat), clip_scan);
 }
 
-void CFX_ScanlineCompositor::CompositeByteMaskLine(uint8_t* dest_scan,
-                                                   const uint8_t* src_scan,
-                                                   int width,
-                                                   const uint8_t* clip_scan,
-                                                   uint8_t* dst_extra_alpha) {
-  if (m_DestFormat == FXDIB_8bppMask) {
+void CFX_ScanlineCompositor::CompositeByteMaskLine(
+    pdfium::span<uint8_t> dest_scan,
+    pdfium::span<const uint8_t> src_scan,
+    int width,
+    pdfium::span<const uint8_t> clip_scan) const {
+  if (m_DestFormat == FXDIB_Format::k8bppMask) {
     CompositeRow_ByteMask2Mask(dest_scan, src_scan, m_MaskAlpha, width,
                                clip_scan);
-  } else if (GetBppFromFormat(m_DestFormat) == 8) {
-    if (GetIsAlphaFromFormat(m_DestFormat)) {
-      CompositeRow_ByteMask2Graya(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
-                                  width, clip_scan, dst_extra_alpha);
-    } else {
-      CompositeRow_ByteMask2Gray(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
-                                 width, clip_scan);
-    }
-  } else if (m_bRgbByteOrder) {
-    if (m_DestFormat == FXDIB_Argb) {
+    return;
+  }
+  if (m_DestFormat == FXDIB_Format::k8bppRgb) {
+    CompositeRow_ByteMask2Gray(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
+                               width, clip_scan);
+    return;
+  }
+
+  // TODO(thestig): Tighten this check.
+  DCHECK_NE(GetBppFromFormat(m_DestFormat), 8);
+
+  if (m_bRgbByteOrder) {
+    if (m_DestFormat == FXDIB_Format::kArgb) {
       CompositeRow_ByteMask2Argb_RgbByteOrder(
           dest_scan, src_scan, m_MaskAlpha, m_MaskRed, m_MaskGreen, m_MaskBlue,
           width, m_BlendType, clip_scan);
@@ -3189,40 +2520,50 @@
           dest_scan, src_scan, m_MaskAlpha, m_MaskRed, m_MaskGreen, m_MaskBlue,
           width, m_BlendType, GetCompsFromFormat(m_DestFormat), clip_scan);
     }
-  } else if (m_DestFormat == FXDIB_Argb) {
+    return;
+  }
+
+  if (m_DestFormat == FXDIB_Format::kArgb) {
     CompositeRow_ByteMask2Argb(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
                                m_MaskGreen, m_MaskBlue, width, m_BlendType,
                                clip_scan);
-  } else if (m_DestFormat == FXDIB_Rgb || m_DestFormat == FXDIB_Rgb32) {
+    return;
+  }
+
+  if (m_DestFormat == FXDIB_Format::kRgb ||
+      m_DestFormat == FXDIB_Format::kRgb32) {
     CompositeRow_ByteMask2Rgb(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
                               m_MaskGreen, m_MaskBlue, width, m_BlendType,
                               GetCompsFromFormat(m_DestFormat), clip_scan);
-  } else if (m_DestFormat == FXDIB_Rgba) {
-    CompositeRow_ByteMask2Rgba(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
-                               m_MaskGreen, m_MaskBlue, width, m_BlendType,
-                               clip_scan, dst_extra_alpha);
+    return;
   }
+
+  // TODO(thestig): Is this line reachable?
 }
 
-void CFX_ScanlineCompositor::CompositeBitMaskLine(uint8_t* dest_scan,
-                                                  const uint8_t* src_scan,
-                                                  int src_left,
-                                                  int width,
-                                                  const uint8_t* clip_scan,
-                                                  uint8_t* dst_extra_alpha) {
-  if (m_DestFormat == FXDIB_8bppMask) {
+void CFX_ScanlineCompositor::CompositeBitMaskLine(
+    pdfium::span<uint8_t> dest_scan,
+    pdfium::span<const uint8_t> src_scan,
+    int src_left,
+    int width,
+    pdfium::span<const uint8_t> clip_scan) const {
+  if (m_DestFormat == FXDIB_Format::k8bppMask) {
     CompositeRow_BitMask2Mask(dest_scan, src_scan, m_MaskAlpha, src_left, width,
                               clip_scan);
-  } else if (GetBppFromFormat(m_DestFormat) == 8) {
-    if (GetIsAlphaFromFormat(m_DestFormat)) {
-      CompositeRow_BitMask2Graya(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
-                                 src_left, width, clip_scan, dst_extra_alpha);
-    } else {
-      CompositeRow_BitMask2Gray(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
-                                src_left, width, clip_scan);
-    }
-  } else if (m_bRgbByteOrder) {
-    if (m_DestFormat == FXDIB_Argb) {
+    return;
+  }
+
+  if (m_DestFormat == FXDIB_Format::k8bppRgb) {
+    CompositeRow_BitMask2Gray(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
+                              src_left, width, clip_scan);
+    return;
+  }
+
+  // TODO(thestig): Tighten this check.
+  DCHECK_NE(GetBppFromFormat(m_DestFormat), 8);
+
+  if (m_bRgbByteOrder) {
+    if (m_DestFormat == FXDIB_Format::kArgb) {
       CompositeRow_BitMask2Argb_RgbByteOrder(
           dest_scan, src_scan, m_MaskAlpha, m_MaskRed, m_MaskGreen, m_MaskBlue,
           src_left, width, m_BlendType, clip_scan);
@@ -3232,14 +2573,62 @@
           src_left, width, m_BlendType, GetCompsFromFormat(m_DestFormat),
           clip_scan);
     }
-  } else if (m_DestFormat == FXDIB_Argb) {
+    return;
+  }
+
+  if (m_DestFormat == FXDIB_Format::kArgb) {
     CompositeRow_BitMask2Argb(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
                               m_MaskGreen, m_MaskBlue, src_left, width,
                               m_BlendType, clip_scan);
-  } else if (m_DestFormat == FXDIB_Rgb || m_DestFormat == FXDIB_Rgb32) {
+    return;
+  }
+
+  if (m_DestFormat == FXDIB_Format::kRgb ||
+      m_DestFormat == FXDIB_Format::kRgb32) {
     CompositeRow_BitMask2Rgb(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
                              m_MaskGreen, m_MaskBlue, src_left, width,
                              m_BlendType, GetCompsFromFormat(m_DestFormat),
                              clip_scan);
+    return;
   }
+
+  // TODO(thestig): Is this line reachable?
+}
+
+CFX_ScanlineCompositor::Palette::Palette() = default;
+
+CFX_ScanlineCompositor::Palette::~Palette() = default;
+
+void CFX_ScanlineCompositor::Palette::Reset() {
+  m_Width = 0;
+  m_nElements = 0;
+  m_pData.reset();
+}
+
+pdfium::span<uint8_t> CFX_ScanlineCompositor::Palette::Make8BitPalette(
+    size_t nElements) {
+  m_Width = sizeof(uint8_t);
+  m_nElements = nElements;
+  m_pData.reset(reinterpret_cast<uint32_t*>(FX_Alloc(uint8_t, m_nElements)));
+  return {reinterpret_cast<uint8_t*>(m_pData.get()), m_nElements};
+}
+
+pdfium::span<uint32_t> CFX_ScanlineCompositor::Palette::Make32BitPalette(
+    size_t nElements) {
+  m_Width = sizeof(uint32_t);
+  m_nElements = nElements;
+  m_pData.reset(FX_Alloc(uint32_t, m_nElements));
+  return {m_pData.get(), m_nElements};
+}
+
+pdfium::span<const uint8_t> CFX_ScanlineCompositor::Palette::Get8BitPalette()
+    const {
+  CHECK(!m_pData || m_Width == sizeof(uint8_t));
+  return {reinterpret_cast<const uint8_t*>(m_pData.get()), m_nElements};
+}
+
+pdfium::span<const uint32_t> CFX_ScanlineCompositor::Palette::Get32BitPalette()
+    const {
+  CHECK(!m_pData || m_Width == sizeof(uint32_t));
+  return {m_pData.get(), m_nElements};
 }
diff --git a/core/fxge/dib/cfx_scanlinecompositor.h b/core/fxge/dib/cfx_scanlinecompositor.h
index 0ec842d..c7e95fd 100644
--- a/core/fxge/dib/cfx_scanlinecompositor.h
+++ b/core/fxge/dib/cfx_scanlinecompositor.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,8 @@
 #include <memory>
 
 #include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_ScanlineCompositor {
  public:
@@ -19,58 +20,71 @@
 
   bool Init(FXDIB_Format dest_format,
             FXDIB_Format src_format,
-            int32_t width,
-            uint32_t* pSrcPalette,
+            pdfium::span<const uint32_t> src_palette,
             uint32_t mask_color,
             BlendMode blend_type,
             bool bClip,
             bool bRgbByteOrder);
 
-  void CompositeRgbBitmapLine(uint8_t* dest_scan,
-                              const uint8_t* src_scan,
+  void CompositeRgbBitmapLine(pdfium::span<uint8_t> dest_scan,
+                              pdfium::span<const uint8_t> src_scan,
                               int width,
-                              const uint8_t* clip_scan,
-                              const uint8_t* src_extra_alpha,
-                              uint8_t* dst_extra_alpha);
+                              pdfium::span<const uint8_t> clip_scan) const;
 
-  void CompositePalBitmapLine(uint8_t* dest_scan,
-                              const uint8_t* src_scan,
+  void CompositePalBitmapLine(pdfium::span<uint8_t> dest_scan,
+                              pdfium::span<const uint8_t> src_scan,
                               int src_left,
                               int width,
-                              const uint8_t* clip_scan,
-                              const uint8_t* src_extra_alpha,
-                              uint8_t* dst_extra_alpha);
+                              pdfium::span<const uint8_t> clip_scan) const;
 
-  void CompositeByteMaskLine(uint8_t* dest_scan,
-                             const uint8_t* src_scan,
+  void CompositeByteMaskLine(pdfium::span<uint8_t> dest_scan,
+                             pdfium::span<const uint8_t> src_scan,
                              int width,
-                             const uint8_t* clip_scan,
-                             uint8_t* dst_extra_alpha);
+                             pdfium::span<const uint8_t> clip_scan) const;
 
-  void CompositeBitMaskLine(uint8_t* dest_scan,
-                            const uint8_t* src_scan,
+  void CompositeBitMaskLine(pdfium::span<uint8_t> dest_scan,
+                            pdfium::span<const uint8_t> src_scan,
                             int src_left,
                             int width,
-                            const uint8_t* clip_scan,
-                            uint8_t* dst_extra_alpha);
+                            pdfium::span<const uint8_t> clip_scan) const;
 
  private:
-  void InitSourcePalette(FXDIB_Format src_format,
-                         FXDIB_Format dest_format,
-                         const uint32_t* pSrcPalette);
+  class Palette {
+   public:
+    Palette();
+    ~Palette();
+
+    void Reset();
+    pdfium::span<uint8_t> Make8BitPalette(size_t nElements);
+    pdfium::span<uint32_t> Make32BitPalette(size_t nElements);
+
+    // Hard CHECK() if mismatch between created and requested widths.
+    pdfium::span<const uint8_t> Get8BitPalette() const;
+    pdfium::span<const uint32_t> Get32BitPalette() const;
+
+   private:
+    // If 0, then no |m_pData|.
+    // If 1, then |m_pData| is really uint8_t* instead.
+    // If 4, then |m_pData| is uint32_t* as expected.
+    size_t m_Width = 0;
+    size_t m_nElements = 0;
+    std::unique_ptr<uint32_t, FxFreeDeleter> m_pData;
+  };
+
+  void InitSourcePalette(pdfium::span<const uint32_t> src_palette);
 
   void InitSourceMask(uint32_t mask_color);
 
-  int m_iTransparency;
   FXDIB_Format m_SrcFormat;
   FXDIB_Format m_DestFormat;
-  std::unique_ptr<uint32_t, FxFreeDeleter> m_pSrcPalette;
+  Palette m_SrcPalette;
   int m_MaskAlpha;
   int m_MaskRed;
   int m_MaskGreen;
   int m_MaskBlue;
   BlendMode m_BlendType = BlendMode::kNormal;
   bool m_bRgbByteOrder = false;
+  bool m_bClip = false;
 };
 
 #endif  // CORE_FXGE_DIB_CFX_SCANLINECOMPOSITOR_H_
diff --git a/core/fxge/dib/cstretchengine.cpp b/core/fxge/dib/cstretchengine.cpp
index 8636430..920645a 100644
--- a/core/fxge/dib/cstretchengine.cpp
+++ b/core/fxge/dib/cstretchengine.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,161 +6,119 @@
 
 #include "core/fxge/dib/cstretchengine.h"
 
+#include <math.h>
+
 #include <algorithm>
+#include <type_traits>
 #include <utility>
 
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/pauseindicator_iface.h"
+#include "core/fxge/calculate_pitch.h"
 #include "core/fxge/dib/cfx_dibbase.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/dib/fx_dib.h"
 #include "core/fxge/dib/scanlinecomposer_iface.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
 
-namespace {
+static_assert(
+    std::is_trivially_destructible<CStretchEngine::PixelWeight>::value,
+    "PixelWeight storage may be re-used without invoking its destructor");
 
-constexpr int kMaxDestValue = 16711680;
-
-int GetPitchRoundUpTo4Bytes(int bits_per_pixel) {
-  return (bits_per_pixel + 31) / 32 * 4;
+// static
+bool CStretchEngine::UseInterpolateBilinear(
+    const FXDIB_ResampleOptions& options,
+    int dest_width,
+    int dest_height,
+    int src_width,
+    int src_height) {
+  return !options.bInterpolateBilinear && !options.bNoSmoothing &&
+         abs(dest_width) != 0 &&
+         abs(dest_height) / 8 <
+             static_cast<long long>(src_width) * src_height / abs(dest_width);
 }
 
-}  // namespace
-
-CStretchEngine::CWeightTable::CWeightTable() = default;
-
-CStretchEngine::CWeightTable::~CWeightTable() = default;
-
-size_t CStretchEngine::CWeightTable::GetPixelWeightSize() const {
-  return m_ItemSize / sizeof(int) - 2;
+// static
+size_t CStretchEngine::PixelWeight::TotalBytesForWeightCount(
+    size_t weight_count) {
+  // Always room for one weight even for empty ranges due to declaration
+  // of m_Weights[1] in the header. Don't shrink below this since
+  // CalculateWeights() relies on this later.
+  const size_t extra_weights = weight_count > 0 ? weight_count - 1 : 0;
+  FX_SAFE_SIZE_T total_bytes = extra_weights;
+  total_bytes *= sizeof(m_Weights[0]);
+  total_bytes += sizeof(PixelWeight);
+  return total_bytes.ValueOrDie();
 }
 
-bool CStretchEngine::CWeightTable::Calc(int dest_len,
-                                        int dest_min,
-                                        int dest_max,
-                                        int src_len,
-                                        int src_min,
-                                        int src_max,
-                                        const FXDIB_ResampleOptions& options) {
+CStretchEngine::WeightTable::WeightTable() = default;
+
+CStretchEngine::WeightTable::~WeightTable() = default;
+
+bool CStretchEngine::WeightTable::CalculateWeights(
+    int dest_len,
+    int dest_min,
+    int dest_max,
+    int src_len,
+    int src_min,
+    int src_max,
+    const FXDIB_ResampleOptions& options) {
+  // 512MB should be large enough for this while preventing OOM.
+  static constexpr size_t kMaxTableBytesAllowed = 512 * 1024 * 1024;
+
+  // Help the compiler realize that these can't change during a loop iteration:
+  const bool bilinear = options.bInterpolateBilinear;
+
+  m_DestMin = 0;
+  m_ItemSizeBytes = 0;
+  m_WeightTablesSizeBytes = 0;
   m_WeightTables.clear();
-  m_dwWeightTablesSize = 0;
-  const double scale = static_cast<float>(src_len) / dest_len;
-  const double base = dest_len < 0 ? src_len : 0;
-  const int ext_size = options.bInterpolateBicubic ? 3 : 1;
-  m_ItemSize =
-      sizeof(int) * 2 +
-      static_cast<int>(sizeof(int) *
-                       (ceil(fabs(static_cast<float>(scale))) + ext_size));
+  if (dest_len == 0)
+    return true;
 
-  m_DestMin = dest_min;
-  if (dest_max - dest_min > static_cast<int>((1U << 30) - 4) / m_ItemSize)
+  if (dest_min > dest_max)
     return false;
 
-  m_dwWeightTablesSize = (dest_max - dest_min) * m_ItemSize + 4;
-  m_WeightTables.resize(m_dwWeightTablesSize);
-  if (options.bNoSmoothing || fabs(static_cast<float>(scale)) < 1.0f) {
+  m_DestMin = dest_min;
+
+  const double scale = static_cast<double>(src_len) / dest_len;
+  const double base = dest_len < 0 ? src_len : 0;
+  const size_t weight_count = static_cast<size_t>(ceil(fabs(scale))) + 1;
+  m_ItemSizeBytes = PixelWeight::TotalBytesForWeightCount(weight_count);
+
+  const size_t dest_range = static_cast<size_t>(dest_max - dest_min);
+  const size_t kMaxTableItemsAllowed = kMaxTableBytesAllowed / m_ItemSizeBytes;
+  if (dest_range > kMaxTableItemsAllowed)
+    return false;
+
+  m_WeightTablesSizeBytes = dest_range * m_ItemSizeBytes;
+  m_WeightTables.resize(m_WeightTablesSizeBytes);
+  if (options.bNoSmoothing || fabs(scale) < 1.0f) {
     for (int dest_pixel = dest_min; dest_pixel < dest_max; ++dest_pixel) {
       PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel);
       double src_pos = dest_pixel * scale + scale / 2 + base;
-      if (options.bInterpolateBilinear) {
-        pixel_weights.m_SrcStart =
-            static_cast<int>(floor(static_cast<float>(src_pos) - 1.0f / 2));
-        pixel_weights.m_SrcEnd =
-            static_cast<int>(floor(static_cast<float>(src_pos) + 1.0f / 2));
-        pixel_weights.m_SrcStart = std::max(pixel_weights.m_SrcStart, src_min);
-        pixel_weights.m_SrcEnd = std::min(pixel_weights.m_SrcEnd, src_max - 1);
-        if (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd) {
-          pixel_weights.m_Weights[0] = 65536;
+      if (bilinear) {
+        int src_start = static_cast<int>(floor(src_pos - 0.5));
+        int src_end = static_cast<int>(floor(src_pos + 0.5));
+        src_start = std::max(src_start, src_min);
+        src_end = std::min(src_end, src_max - 1);
+        pixel_weights.SetStartEnd(src_start, src_end, weight_count);
+        if (pixel_weights.m_SrcStart >= pixel_weights.m_SrcEnd) {
+          // Always room for one weight per size calculation.
+          pixel_weights.m_Weights[0] = kFixedPointOne;
         } else {
           pixel_weights.m_Weights[1] =
-              FXSYS_roundf(static_cast<float>(
-                               src_pos - pixel_weights.m_SrcStart - 1.0f / 2) *
-                           65536);
-          pixel_weights.m_Weights[0] = 65536 - pixel_weights.m_Weights[1];
-        }
-      } else if (options.bInterpolateBicubic) {
-        pixel_weights.m_SrcStart =
-            static_cast<int>(floor(static_cast<float>(src_pos) - 1.0f / 2));
-        pixel_weights.m_SrcEnd =
-            static_cast<int>(floor(static_cast<float>(src_pos) + 1.0f / 2));
-        int start = pixel_weights.m_SrcStart - 1;
-        int end = pixel_weights.m_SrcEnd + 1;
-        start = std::max(start, src_min);
-        end = std::min(end, src_max - 1);
-        if (pixel_weights.m_SrcStart < src_min) {
-          src_pos += src_min - pixel_weights.m_SrcStart;
-          pixel_weights.m_SrcStart = src_min;
-        }
-        pixel_weights.m_SrcEnd = std::min(pixel_weights.m_SrcEnd, src_max - 1);
-        int weight = FXSYS_roundf(
-            static_cast<float>(src_pos - pixel_weights.m_SrcStart - 1.0f / 2) *
-            256);
-        if (start == end) {
+              FixedFromDouble(src_pos - pixel_weights.m_SrcStart - 0.5f);
           pixel_weights.m_Weights[0] =
-              (SDP_Table[256 + weight] + SDP_Table[weight] +
-               SDP_Table[256 - weight] + SDP_Table[512 - weight])
-              << 8;
-        } else if ((start == pixel_weights.m_SrcStart &&
-                    (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd ||
-                     end == pixel_weights.m_SrcEnd) &&
-                    start < end) ||
-                   (start < pixel_weights.m_SrcStart &&
-                    pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd &&
-                    end == pixel_weights.m_SrcEnd)) {
-          if (start < pixel_weights.m_SrcStart) {
-            pixel_weights.m_Weights[0] = SDP_Table[256 + weight] << 8;
-            pixel_weights.m_Weights[1] =
-                (SDP_Table[weight] + SDP_Table[256 - weight] +
-                 SDP_Table[512 - weight])
-                << 8;
-          } else {
-            if (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd) {
-              pixel_weights.m_Weights[0] =
-                  (SDP_Table[256 + weight] + SDP_Table[weight] +
-                   SDP_Table[256 - weight])
-                  << 8;
-              pixel_weights.m_Weights[1] = SDP_Table[512 - weight] << 8;
-            } else {
-              pixel_weights.m_Weights[0] =
-                  (SDP_Table[256 + weight] + SDP_Table[weight]) << 8;
-              pixel_weights.m_Weights[1] =
-                  (SDP_Table[256 - weight] + SDP_Table[512 - weight]) << 8;
-            }
-          }
-          if (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd) {
-            pixel_weights.m_SrcEnd = end;
-          }
-          if (start < pixel_weights.m_SrcStart) {
-            pixel_weights.m_SrcStart = start;
-          }
-        } else if (start == pixel_weights.m_SrcStart &&
-                   start < pixel_weights.m_SrcEnd &&
-                   pixel_weights.m_SrcEnd < end) {
-          pixel_weights.m_Weights[0] =
-              (SDP_Table[256 + weight] + SDP_Table[weight]) << 8;
-          pixel_weights.m_Weights[1] = SDP_Table[256 - weight] << 8;
-          pixel_weights.m_Weights[2] = SDP_Table[512 - weight] << 8;
-          pixel_weights.m_SrcEnd = end;
-        } else if (start < pixel_weights.m_SrcStart &&
-                   pixel_weights.m_SrcStart < pixel_weights.m_SrcEnd &&
-                   pixel_weights.m_SrcEnd == end) {
-          pixel_weights.m_Weights[0] = SDP_Table[256 + weight] << 8;
-          pixel_weights.m_Weights[1] = SDP_Table[weight] << 8;
-          pixel_weights.m_Weights[2] =
-              (SDP_Table[256 - weight] + SDP_Table[512 - weight]) << 8;
-          pixel_weights.m_SrcStart = start;
-        } else {
-          pixel_weights.m_Weights[0] = SDP_Table[256 + weight] << 8;
-          pixel_weights.m_Weights[1] = SDP_Table[weight] << 8;
-          pixel_weights.m_Weights[2] = SDP_Table[256 - weight] << 8;
-          pixel_weights.m_Weights[3] = SDP_Table[512 - weight] << 8;
-          pixel_weights.m_SrcStart = start;
-          pixel_weights.m_SrcEnd = end;
+              kFixedPointOne - pixel_weights.m_Weights[1];
         }
       } else {
-        int pixel_pos = static_cast<int>(floor(static_cast<float>(src_pos)));
-        pixel_weights.m_SrcStart = std::max(pixel_pos, src_min);
-        pixel_weights.m_SrcEnd = std::min(pixel_pos, src_max - 1);
-        pixel_weights.m_Weights[0] = 65536;
+        int pixel_pos = static_cast<int>(floor(src_pos));
+        int src_start = std::max(pixel_pos, src_min);
+        int src_end = std::min(pixel_pos, src_max - 1);
+        pixel_weights.SetStartEnd(src_start, src_end, weight_count);
+        pixel_weights.m_Weights[0] = kFixedPointOne;
       }
     }
     return true;
@@ -176,13 +134,13 @@
     end_i = std::min(end_i, src_max - 1);
     if (start_i > end_i) {
       start_i = std::min(start_i, src_max - 1);
-      pixel_weights.m_SrcStart = start_i;
-      pixel_weights.m_SrcEnd = start_i;
+      pixel_weights.SetStartEnd(start_i, start_i, weight_count);
       continue;
     }
-    pixel_weights.m_SrcStart = start_i;
-    pixel_weights.m_SrcEnd = end_i;
-    for (int j = start_i; j <= end_i; ++j) {
+    pixel_weights.SetStartEnd(start_i, end_i, weight_count);
+    uint32_t remaining = kFixedPointOne;
+    double rounding_error = 0.0;
+    for (int j = start_i; j < end_i; ++j) {
       double dest_start = (j - base) / scale;
       double dest_end = (j + 1 - base) / scale;
       if (dest_start > dest_end)
@@ -190,34 +148,33 @@
       double area_start = std::max(dest_start, static_cast<double>(dest_pixel));
       double area_end = std::min(dest_end, static_cast<double>(dest_pixel + 1));
       double weight = std::max(0.0, area_end - area_start);
-      if (weight == 0 && j == end_i) {
-        --pixel_weights.m_SrcEnd;
-        break;
-      }
-      size_t idx = j - start_i;
-      if (idx >= GetPixelWeightSize())
-        return false;
-
-      pixel_weights.m_Weights[idx] = FXSYS_roundf(weight * 65536);
+      uint32_t fixed_weight = FixedFromDouble(weight + rounding_error);
+      pixel_weights.SetWeightForPosition(j, fixed_weight);
+      remaining -= fixed_weight;
+      rounding_error =
+          weight - static_cast<double>(fixed_weight) / kFixedPointOne;
+    }
+    // Note: underflow is defined behaviour for unsigned types and will
+    // result in an out-of-range value.
+    if (remaining && remaining <= kFixedPointOne) {
+      pixel_weights.SetWeightForPosition(end_i, remaining);
+    } else {
+      pixel_weights.RemoveLastWeightAndAdjust(remaining);
     }
   }
   return true;
 }
 
-const PixelWeight* CStretchEngine::CWeightTable::GetPixelWeight(
+const CStretchEngine::PixelWeight* CStretchEngine::WeightTable::GetPixelWeight(
     int pixel) const {
-  ASSERT(pixel >= m_DestMin);
+  DCHECK(pixel >= m_DestMin);
   return reinterpret_cast<const PixelWeight*>(
-      &m_WeightTables[(pixel - m_DestMin) * m_ItemSize]);
+      &m_WeightTables[(pixel - m_DestMin) * m_ItemSizeBytes]);
 }
 
-int* CStretchEngine::CWeightTable::GetValueFromPixelWeight(PixelWeight* pWeight,
-                                                           int index) const {
-  if (index < pWeight->m_SrcStart)
-    return nullptr;
-
-  size_t idx = index - pWeight->m_SrcStart;
-  return idx < GetPixelWeightSize() ? &pWeight->m_Weights[idx] : nullptr;
+CStretchEngine::PixelWeight* CStretchEngine::WeightTable::GetPixelWeight(
+    int pixel) {
+  return const_cast<PixelWeight*>(std::as_const(*this).GetPixelWeight(pixel));
 }
 
 CStretchEngine::CStretchEngine(ScanlineComposerIface* pDestBitmap,
@@ -225,42 +182,42 @@
                                int dest_width,
                                int dest_height,
                                const FX_RECT& clip_rect,
-                               const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                               const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
                                const FXDIB_ResampleOptions& options)
     : m_DestFormat(dest_format),
       m_DestBpp(GetBppFromFormat(dest_format)),
-      m_SrcBpp(GetBppFromFormat(pSrcBitmap->GetFormat())),
-      m_bHasAlpha(GetIsAlphaFromFormat(pSrcBitmap->GetFormat())),
+      m_SrcBpp(pSrcBitmap->GetBPP()),
+      m_bHasAlpha(pSrcBitmap->IsAlphaFormat()),
       m_pSource(pSrcBitmap),
-      m_pSrcPalette(pSrcBitmap->GetPalette()),
+      m_pSrcPalette(pSrcBitmap->GetPaletteSpan()),
       m_SrcWidth(pSrcBitmap->GetWidth()),
       m_SrcHeight(pSrcBitmap->GetHeight()),
       m_pDestBitmap(pDestBitmap),
       m_DestWidth(dest_width),
       m_DestHeight(dest_height),
       m_DestClip(clip_rect) {
-  uint32_t size = clip_rect.Width();
-  if (size && m_DestBpp > static_cast<int>(INT_MAX / size))
+  if (m_bHasAlpha) {
+    DCHECK_EQ(m_DestFormat, FXDIB_Format::kArgb);
+    DCHECK_EQ(m_DestBpp, GetBppFromFormat(FXDIB_Format::kArgb));
+    DCHECK_EQ(m_pSource->GetFormat(), FXDIB_Format::kArgb);
+    DCHECK_EQ(m_SrcBpp, GetBppFromFormat(FXDIB_Format::kArgb));
+  }
+
+  absl::optional<uint32_t> maybe_size =
+      fxge::CalculatePitch32(m_DestBpp, clip_rect.Width());
+  if (!maybe_size.has_value())
     return;
 
-  size *= m_DestBpp;
-  if (size > INT_MAX - 31)
-    return;
-
-  size = GetPitchRoundUpTo4Bytes(size);
-  m_DestScanline.resize(size);
-  if (dest_format == FXDIB_Rgb32)
+  m_DestScanline.resize(maybe_size.value());
+  if (dest_format == FXDIB_Format::kRgb32)
     std::fill(m_DestScanline.begin(), m_DestScanline.end(), 255);
-  m_InterPitch = GetPitchRoundUpTo4Bytes(m_DestClip.Width() * m_DestBpp);
-  m_ExtraMaskPitch = GetPitchRoundUpTo4Bytes(m_DestClip.Width() * 8);
+  m_InterPitch = fxge::CalculatePitch32OrDie(m_DestBpp, m_DestClip.Width());
+  m_ExtraMaskPitch = fxge::CalculatePitch32OrDie(8, m_DestClip.Width());
   if (options.bNoSmoothing) {
     m_ResampleOptions.bNoSmoothing = true;
   } else {
-    bool bInterpol =
-        options.bInterpolateBilinear || options.bInterpolateBicubic;
-    if (!bInterpol && abs(dest_width) != 0 &&
-        abs(dest_height) / 8 < static_cast<long long>(m_SrcWidth) *
-                                   m_SrcHeight / abs(dest_width)) {
+    if (UseInterpolateBilinear(options, dest_width, dest_height, m_SrcWidth,
+                               m_SrcHeight)) {
       m_ResampleOptions.bInterpolateBilinear = true;
     } else {
       m_ResampleOptions = options;
@@ -291,13 +248,8 @@
                                      : TransformMethod::k1BppToManyBpp;
       break;
     case 8:
-      if (m_DestBpp == 8) {
-        m_TransMethod = m_bHasAlpha ? TransformMethod::k8BppTo8BppWithAlpha
-                                    : TransformMethod::k8BppTo8Bpp;
-      } else {
-        m_TransMethod = m_bHasAlpha ? TransformMethod::k8BppToManyBppWithAlpha
-                                    : TransformMethod::k8BppToManyBpp;
-      }
+      m_TransMethod = m_DestBpp == 8 ? TransformMethod::k8BppTo8Bpp
+                                     : TransformMethod::k8BppToManyBpp;
       break;
     default:
       m_TransMethod = m_bHasAlpha ? TransformMethod::kManyBpptoManyBppWithAlpha
@@ -306,7 +258,7 @@
   }
 }
 
-CStretchEngine::~CStretchEngine() {}
+CStretchEngine::~CStretchEngine() = default;
 
 bool CStretchEngine::Continue(PauseIndicatorIface* pPause) {
   while (m_State == State::kHorizontal) {
@@ -323,22 +275,21 @@
   if (m_DestWidth == 0 || m_InterPitch == 0 || m_DestScanline.empty())
     return false;
 
-  if (m_SrcClip.Height() == 0 ||
-      m_SrcClip.Height() > (1 << 29) / m_InterPitch) {
-    return false;
-  }
-
-  m_InterBuf.resize(m_SrcClip.Height() * m_InterPitch);
-  if (m_pSource && m_bHasAlpha && m_pSource->m_pAlphaMask) {
-    m_ExtraAlphaBuf.resize(m_SrcClip.Height(), m_ExtraMaskPitch);
-    m_DestMaskScanline.resize(m_ExtraMaskPitch);
-  }
-  bool ret = m_WeightTable.Calc(m_DestWidth, m_DestClip.left, m_DestClip.right,
-                                m_SrcWidth, m_SrcClip.left, m_SrcClip.right,
-                                m_ResampleOptions);
-  if (!ret)
+  FX_SAFE_SIZE_T safe_size = m_SrcClip.Height();
+  safe_size *= m_InterPitch;
+  const size_t size = safe_size.ValueOrDefault(0);
+  if (size == 0)
     return false;
 
+  m_InterBuf = FixedTryAllocZeroedDataVector<uint8_t>(size);
+  if (m_InterBuf.empty())
+    return false;
+
+  if (!m_WeightTable.CalculateWeights(
+          m_DestWidth, m_DestClip.left, m_DestClip.right, m_SrcWidth,
+          m_SrcClip.left, m_SrcClip.right, m_ResampleOptions)) {
+    return false;
+  }
   m_CurRow = m_SrcClip.top;
   m_State = State::kHorizontal;
   return true;
@@ -361,220 +312,105 @@
       rows_to_go = kStrechPauseRows;
     }
 
-    const uint8_t* src_scan = m_pSource->GetScanline(m_CurRow);
-    uint8_t* dest_scan =
-        m_InterBuf.data() + (m_CurRow - m_SrcClip.top) * m_InterPitch;
-    const uint8_t* src_scan_mask = nullptr;
-    uint8_t* dest_scan_mask = nullptr;
-    if (!m_ExtraAlphaBuf.empty()) {
-      src_scan_mask = m_pSource->m_pAlphaMask->GetScanline(m_CurRow);
-      dest_scan_mask = m_ExtraAlphaBuf.data() +
-                       (m_CurRow - m_SrcClip.top) * m_ExtraMaskPitch;
-    }
+    const uint8_t* src_scan = m_pSource->GetScanline(m_CurRow).data();
+    pdfium::span<uint8_t> dest_span = m_InterBuf.writable_span().subspan(
+        (m_CurRow - m_SrcClip.top) * m_InterPitch, m_InterPitch);
+    size_t dest_span_index = 0;
     // TODO(npm): reduce duplicated code here
     switch (m_TransMethod) {
       case TransformMethod::k1BppTo8Bpp:
       case TransformMethod::k1BppToManyBpp: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
-          int dest_a = 0;
+          uint32_t dest_a = 0;
           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
-            int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j);
-            if (!pWeight)
-              return false;
-
-            int pixel_weight = *pWeight;
+            uint32_t pixel_weight = pWeights->GetWeightForPosition(j);
             if (src_scan[j / 8] & (1 << (7 - j % 8)))
               dest_a += pixel_weight * 255;
           }
-          if (m_ResampleOptions.bInterpolateBicubic)
-            dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue);
-          *dest_scan++ = static_cast<uint8_t>(dest_a >> 16);
+          dest_span[dest_span_index++] = PixelFromFixed(dest_a);
         }
         break;
       }
       case TransformMethod::k8BppTo8Bpp: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
-          int dest_a = 0;
+          uint32_t dest_a = 0;
           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
-            int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j);
-            if (!pWeight)
-              return false;
-
-            int pixel_weight = *pWeight;
+            uint32_t pixel_weight = pWeights->GetWeightForPosition(j);
             dest_a += pixel_weight * src_scan[j];
           }
-          if (m_ResampleOptions.bInterpolateBicubic)
-            dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue);
-          *dest_scan++ = static_cast<uint8_t>(dest_a >> 16);
-        }
-        break;
-      }
-      case TransformMethod::k8BppTo8BppWithAlpha: {
-        for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
-          PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
-          int dest_a = 0;
-          int dest_r = 0;
-          for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
-            int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j);
-            if (!pWeight)
-              return false;
-
-            int pixel_weight = *pWeight;
-            pixel_weight = pixel_weight * src_scan_mask[j] / 255;
-            dest_r += pixel_weight * src_scan[j];
-            dest_a += pixel_weight;
-          }
-          if (m_ResampleOptions.bInterpolateBicubic) {
-            dest_r = pdfium::clamp(dest_r, 0, kMaxDestValue);
-            dest_a = pdfium::clamp(dest_a, 0, 65536);
-          }
-          *dest_scan++ = static_cast<uint8_t>(dest_r >> 16);
-          *dest_scan_mask++ = static_cast<uint8_t>((dest_a * 255) >> 16);
+          dest_span[dest_span_index++] = PixelFromFixed(dest_a);
         }
         break;
       }
       case TransformMethod::k8BppToManyBpp: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
-          int dest_r_y = 0;
-          int dest_g_m = 0;
-          int dest_b_c = 0;
+          uint32_t dest_r = 0;
+          uint32_t dest_g = 0;
+          uint32_t dest_b = 0;
           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
-            int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j);
-            if (!pWeight)
-              return false;
-
-            int pixel_weight = *pWeight;
-            unsigned long argb_cmyk = m_pSrcPalette[src_scan[j]];
-            if (m_DestFormat == FXDIB_Rgb) {
-              dest_r_y += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 16);
-              dest_g_m += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 8);
-              dest_b_c += pixel_weight * static_cast<uint8_t>(argb_cmyk);
+            uint32_t pixel_weight = pWeights->GetWeightForPosition(j);
+            unsigned long argb = m_pSrcPalette[src_scan[j]];
+            if (m_DestFormat == FXDIB_Format::kRgb) {
+              dest_r += pixel_weight * static_cast<uint8_t>(argb >> 16);
+              dest_g += pixel_weight * static_cast<uint8_t>(argb >> 8);
+              dest_b += pixel_weight * static_cast<uint8_t>(argb);
             } else {
-              dest_b_c += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 24);
-              dest_g_m += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 16);
-              dest_r_y += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 8);
+              dest_b += pixel_weight * static_cast<uint8_t>(argb >> 24);
+              dest_g += pixel_weight * static_cast<uint8_t>(argb >> 16);
+              dest_r += pixel_weight * static_cast<uint8_t>(argb >> 8);
             }
           }
-          if (m_ResampleOptions.bInterpolateBicubic) {
-            dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
-            dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
-            dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
-          }
-          *dest_scan++ = static_cast<uint8_t>(dest_b_c >> 16);
-          *dest_scan++ = static_cast<uint8_t>(dest_g_m >> 16);
-          *dest_scan++ = static_cast<uint8_t>(dest_r_y >> 16);
-        }
-        break;
-      }
-      case TransformMethod::k8BppToManyBppWithAlpha: {
-        for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
-          PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
-          int dest_a = 0;
-          int dest_r_y = 0;
-          int dest_g_m = 0;
-          int dest_b_c = 0;
-          for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
-            int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j);
-            if (!pWeight)
-              return false;
-
-            int pixel_weight = *pWeight;
-            pixel_weight = pixel_weight * src_scan_mask[j] / 255;
-            unsigned long argb_cmyk = m_pSrcPalette[src_scan[j]];
-            if (m_DestFormat == FXDIB_Rgba) {
-              dest_r_y += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 16);
-              dest_g_m += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 8);
-              dest_b_c += pixel_weight * static_cast<uint8_t>(argb_cmyk);
-            } else {
-              dest_b_c += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 24);
-              dest_g_m += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 16);
-              dest_r_y += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 8);
-            }
-            dest_a += pixel_weight;
-          }
-          if (m_ResampleOptions.bInterpolateBicubic) {
-            dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
-            dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
-            dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
-            dest_a = pdfium::clamp(dest_a, 0, 65536);
-          }
-          *dest_scan++ = static_cast<uint8_t>(dest_b_c >> 16);
-          *dest_scan++ = static_cast<uint8_t>(dest_g_m >> 16);
-          *dest_scan++ = static_cast<uint8_t>(dest_r_y >> 16);
-          *dest_scan_mask++ = static_cast<uint8_t>((dest_a * 255) >> 16);
+          dest_span[dest_span_index++] = PixelFromFixed(dest_b);
+          dest_span[dest_span_index++] = PixelFromFixed(dest_g);
+          dest_span[dest_span_index++] = PixelFromFixed(dest_r);
         }
         break;
       }
       case TransformMethod::kManyBpptoManyBpp: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
-          int dest_r_y = 0;
-          int dest_g_m = 0;
-          int dest_b_c = 0;
+          uint32_t dest_r = 0;
+          uint32_t dest_g = 0;
+          uint32_t dest_b = 0;
           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
-            int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j);
-            if (!pWeight)
-              return false;
-
-            int pixel_weight = *pWeight;
+            uint32_t pixel_weight = pWeights->GetWeightForPosition(j);
             const uint8_t* src_pixel = src_scan + j * Bpp;
-            dest_b_c += pixel_weight * (*src_pixel++);
-            dest_g_m += pixel_weight * (*src_pixel++);
-            dest_r_y += pixel_weight * (*src_pixel);
+            dest_b += pixel_weight * (*src_pixel++);
+            dest_g += pixel_weight * (*src_pixel++);
+            dest_r += pixel_weight * (*src_pixel);
           }
-          if (m_ResampleOptions.bInterpolateBicubic) {
-            dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
-            dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
-            dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
-          }
-          *dest_scan++ = static_cast<uint8_t>((dest_b_c) >> 16);
-          *dest_scan++ = static_cast<uint8_t>((dest_g_m) >> 16);
-          *dest_scan++ = static_cast<uint8_t>((dest_r_y) >> 16);
-          dest_scan += Bpp - 3;
+          dest_span[dest_span_index++] = PixelFromFixed(dest_b);
+          dest_span[dest_span_index++] = PixelFromFixed(dest_g);
+          dest_span[dest_span_index++] = PixelFromFixed(dest_r);
+          dest_span_index += Bpp - 3;
         }
         break;
       }
       case TransformMethod::kManyBpptoManyBppWithAlpha: {
+        DCHECK(m_bHasAlpha);
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
-          int dest_a = 0;
-          int dest_r_y = 0;
-          int dest_g_m = 0;
-          int dest_b_c = 0;
+          uint32_t dest_a = 0;
+          uint32_t dest_r = 0;
+          uint32_t dest_g = 0;
+          uint32_t dest_b = 0;
           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
-            int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j);
-            if (!pWeight)
-              return false;
-
-            int pixel_weight = *pWeight;
             const uint8_t* src_pixel = src_scan + j * Bpp;
-            if (m_DestFormat == FXDIB_Argb) {
-              pixel_weight = pixel_weight * src_pixel[3] / 255;
-            } else {
-              pixel_weight = pixel_weight * src_scan_mask[j] / 255;
-            }
-            dest_b_c += pixel_weight * (*src_pixel++);
-            dest_g_m += pixel_weight * (*src_pixel++);
-            dest_r_y += pixel_weight * (*src_pixel);
+            uint32_t pixel_weight =
+                pWeights->GetWeightForPosition(j) * src_pixel[3] / 255;
+            dest_b += pixel_weight * (*src_pixel++);
+            dest_g += pixel_weight * (*src_pixel++);
+            dest_r += pixel_weight * (*src_pixel);
             dest_a += pixel_weight;
           }
-          if (m_ResampleOptions.bInterpolateBicubic) {
-            dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
-            dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
-            dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
-            dest_a = pdfium::clamp(dest_a, 0, 65536);
-          }
-          *dest_scan++ = static_cast<uint8_t>((dest_b_c) >> 16);
-          *dest_scan++ = static_cast<uint8_t>((dest_g_m) >> 16);
-          *dest_scan++ = static_cast<uint8_t>((dest_r_y) >> 16);
-          if (m_DestFormat == FXDIB_Argb)
-            *dest_scan = static_cast<uint8_t>((dest_a * 255) >> 16);
-          if (dest_scan_mask)
-            *dest_scan_mask++ = static_cast<uint8_t>((dest_a * 255) >> 16);
-          dest_scan += Bpp - 3;
+          dest_span[dest_span_index++] = PixelFromFixed(dest_b);
+          dest_span[dest_span_index++] = PixelFromFixed(dest_g);
+          dest_span[dest_span_index++] = PixelFromFixed(dest_r);
+          dest_span[dest_span_index] = PixelFromFixed(255 * dest_a);
+          dest_span_index += Bpp - 3;
         }
         break;
       }
@@ -588,160 +424,91 @@
   if (m_DestHeight == 0)
     return;
 
-  CWeightTable table;
-  bool ret =
-      table.Calc(m_DestHeight, m_DestClip.top, m_DestClip.bottom, m_SrcHeight,
-                 m_SrcClip.top, m_SrcClip.bottom, m_ResampleOptions);
-  if (!ret)
+  WeightTable table;
+  if (!table.CalculateWeights(m_DestHeight, m_DestClip.top, m_DestClip.bottom,
+                              m_SrcHeight, m_SrcClip.top, m_SrcClip.bottom,
+                              m_ResampleOptions)) {
     return;
+  }
 
   const int DestBpp = m_DestBpp / 8;
   for (int row = m_DestClip.top; row < m_DestClip.bottom; ++row) {
     unsigned char* dest_scan = m_DestScanline.data();
-    unsigned char* dest_scan_mask = m_DestMaskScanline.data();
     PixelWeight* pWeights = table.GetPixelWeight(row);
     switch (m_TransMethod) {
       case TransformMethod::k1BppTo8Bpp:
       case TransformMethod::k1BppToManyBpp:
       case TransformMethod::k8BppTo8Bpp: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
-          unsigned char* src_scan =
-              m_InterBuf.data() + (col - m_DestClip.left) * DestBpp;
-          int dest_a = 0;
+          pdfium::span<const uint8_t> src_span =
+              m_InterBuf.span().subspan((col - m_DestClip.left) * DestBpp);
+          uint32_t dest_a = 0;
           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
-            int* pWeight = table.GetValueFromPixelWeight(pWeights, j);
-            if (!pWeight)
-              return;
-
-            int pixel_weight = *pWeight;
+            uint32_t pixel_weight = pWeights->GetWeightForPosition(j);
             dest_a +=
-                pixel_weight * src_scan[(j - m_SrcClip.top) * m_InterPitch];
+                pixel_weight * src_span[(j - m_SrcClip.top) * m_InterPitch];
           }
-          if (m_ResampleOptions.bInterpolateBicubic)
-            dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue);
-          *dest_scan = static_cast<uint8_t>(dest_a >> 16);
+          *dest_scan = PixelFromFixed(dest_a);
           dest_scan += DestBpp;
         }
         break;
       }
-      case TransformMethod::k8BppTo8BppWithAlpha: {
-        for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
-          unsigned char* src_scan =
-              m_InterBuf.data() + (col - m_DestClip.left) * DestBpp;
-          unsigned char* src_scan_mask =
-              m_ExtraAlphaBuf.data() + (col - m_DestClip.left);
-          int dest_a = 0;
-          int dest_k = 0;
-          for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
-            int* pWeight = table.GetValueFromPixelWeight(pWeights, j);
-            if (!pWeight)
-              return;
-
-            int pixel_weight = *pWeight;
-            dest_k +=
-                pixel_weight * src_scan[(j - m_SrcClip.top) * m_InterPitch];
-            dest_a += pixel_weight *
-                      src_scan_mask[(j - m_SrcClip.top) * m_ExtraMaskPitch];
-          }
-          if (m_ResampleOptions.bInterpolateBicubic) {
-            dest_k = pdfium::clamp(dest_k, 0, kMaxDestValue);
-            dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue);
-          }
-          *dest_scan = static_cast<uint8_t>(dest_k >> 16);
-          dest_scan += DestBpp;
-          *dest_scan_mask++ = static_cast<uint8_t>(dest_a >> 16);
-        }
-        break;
-      }
       case TransformMethod::k8BppToManyBpp:
       case TransformMethod::kManyBpptoManyBpp: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
-          unsigned char* src_scan =
-              m_InterBuf.data() + (col - m_DestClip.left) * DestBpp;
-          int dest_r_y = 0;
-          int dest_g_m = 0;
-          int dest_b_c = 0;
+          pdfium::span<const uint8_t> src_span =
+              m_InterBuf.span().subspan((col - m_DestClip.left) * DestBpp);
+          uint32_t dest_r = 0;
+          uint32_t dest_g = 0;
+          uint32_t dest_b = 0;
           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
-            int* pWeight = table.GetValueFromPixelWeight(pWeights, j);
-            if (!pWeight)
-              return;
-
-            int pixel_weight = *pWeight;
-            const uint8_t* src_pixel =
-                src_scan + (j - m_SrcClip.top) * m_InterPitch;
-            dest_b_c += pixel_weight * (*src_pixel++);
-            dest_g_m += pixel_weight * (*src_pixel++);
-            dest_r_y += pixel_weight * (*src_pixel);
+            uint32_t pixel_weight = pWeights->GetWeightForPosition(j);
+            pdfium::span<const uint8_t> src_pixel =
+                src_span.subspan((j - m_SrcClip.top) * m_InterPitch, 3);
+            dest_b += pixel_weight * src_pixel[0];
+            dest_g += pixel_weight * src_pixel[1];
+            dest_r += pixel_weight * src_pixel[2];
           }
-          if (m_ResampleOptions.bInterpolateBicubic) {
-            dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
-            dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
-            dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
-          }
-          dest_scan[0] = static_cast<uint8_t>((dest_b_c) >> 16);
-          dest_scan[1] = static_cast<uint8_t>((dest_g_m) >> 16);
-          dest_scan[2] = static_cast<uint8_t>((dest_r_y) >> 16);
+          dest_scan[0] = PixelFromFixed(dest_b);
+          dest_scan[1] = PixelFromFixed(dest_g);
+          dest_scan[2] = PixelFromFixed(dest_r);
           dest_scan += DestBpp;
         }
         break;
       }
-      case TransformMethod::k8BppToManyBppWithAlpha:
       case TransformMethod::kManyBpptoManyBppWithAlpha: {
+        DCHECK(m_bHasAlpha);
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
-          unsigned char* src_scan =
-              m_InterBuf.data() + (col - m_DestClip.left) * DestBpp;
-          unsigned char* src_scan_mask = nullptr;
-          if (m_DestFormat != FXDIB_Argb)
-            src_scan_mask = m_ExtraAlphaBuf.data() + (col - m_DestClip.left);
-          int dest_a = 0;
-          int dest_r_y = 0;
-          int dest_g_m = 0;
-          int dest_b_c = 0;
+          pdfium::span<const uint8_t> src_span =
+              m_InterBuf.span().subspan((col - m_DestClip.left) * DestBpp);
+          uint32_t dest_a = 0;
+          uint32_t dest_r = 0;
+          uint32_t dest_g = 0;
+          uint32_t dest_b = 0;
+          constexpr size_t kPixelBytes = 4;
           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
-            int* pWeight = table.GetValueFromPixelWeight(pWeights, j);
-            if (!pWeight)
-              return;
-
-            int pixel_weight = *pWeight;
-            const uint8_t* src_pixel =
-                src_scan + (j - m_SrcClip.top) * m_InterPitch;
-            int mask_v = 255;
-            if (src_scan_mask)
-              mask_v = src_scan_mask[(j - m_SrcClip.top) * m_ExtraMaskPitch];
-            dest_b_c += pixel_weight * (*src_pixel++);
-            dest_g_m += pixel_weight * (*src_pixel++);
-            dest_r_y += pixel_weight * (*src_pixel);
-            if (m_DestFormat == FXDIB_Argb)
-              dest_a += pixel_weight * (*(src_pixel + 1));
-            else
-              dest_a += pixel_weight * mask_v;
-          }
-          if (m_ResampleOptions.bInterpolateBicubic) {
-            dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
-            dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
-            dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
-            dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue);
+            uint32_t pixel_weight = pWeights->GetWeightForPosition(j);
+            pdfium::span<const uint8_t> src_pixel = src_span.subspan(
+                (j - m_SrcClip.top) * m_InterPitch, kPixelBytes);
+            dest_b += pixel_weight * src_pixel[0];
+            dest_g += pixel_weight * src_pixel[1];
+            dest_r += pixel_weight * src_pixel[2];
+            dest_a += pixel_weight * src_pixel[3];
           }
           if (dest_a) {
-            int r = static_cast<uint32_t>(dest_r_y) * 255 / dest_a;
-            int g = static_cast<uint32_t>(dest_g_m) * 255 / dest_a;
-            int b = static_cast<uint32_t>(dest_b_c) * 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);
+            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] = std::clamp(b, 0, 255);
+            dest_scan[1] = std::clamp(g, 0, 255);
+            dest_scan[2] = std::clamp(r, 0, 255);
           }
-          if (m_DestFormat == FXDIB_Argb)
-            dest_scan[3] = static_cast<uint8_t>((dest_a) >> 16);
-          else
-            *dest_scan_mask = static_cast<uint8_t>((dest_a) >> 16);
+          dest_scan[3] = PixelFromFixed(dest_a);
           dest_scan += DestBpp;
-          if (dest_scan_mask)
-            dest_scan_mask++;
         }
         break;
       }
     }
-    m_pDestBitmap->ComposeScanline(row - m_DestClip.top, m_DestScanline.data(),
-                                   m_DestMaskScanline.data());
+    m_pDestBitmap->ComposeScanline(row - m_DestClip.top, m_DestScanline);
   }
 }
diff --git a/core/fxge/dib/cstretchengine.h b/core/fxge/dib/cstretchengine.h
index 1792963..4f0d71f 100644
--- a/core/fxge/dib/cstretchengine.h
+++ b/core/fxge/dib/cstretchengine.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,17 @@
 #ifndef CORE_FXGE_DIB_CSTRETCHENGINE_H_
 #define CORE_FXGE_DIB_CSTRETCHENGINE_H_
 
-#include <vector>
+#include <stdint.h>
 
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fixed_try_alloc_zeroed_data_vector.h"
 #include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_DIBBase;
 class PauseIndicatorIface;
@@ -20,59 +25,114 @@
 
 class CStretchEngine {
  public:
+  static constexpr uint32_t kFixedPointBits = 16;
+  static constexpr uint32_t kFixedPointOne = 1 << kFixedPointBits;
+
+  static inline uint32_t FixedFromDouble(double d) {
+    return static_cast<uint32_t>(FXSYS_round(d * kFixedPointOne));
+  }
+
+  static inline uint32_t FixedFromFloat(float f) {
+    return static_cast<uint32_t>(FXSYS_roundf(f * kFixedPointOne));
+  }
+
+  static inline uint8_t PixelFromFixed(uint32_t fixed) {
+    return static_cast<uint8_t>(fixed >> kFixedPointBits);
+  }
+
+  // Indicates whether to manually set interpolate bilinear option to true to
+  // achieve a smoother rendering results.
+  static bool UseInterpolateBilinear(const FXDIB_ResampleOptions& options,
+                                     int dest_width,
+                                     int dest_height,
+                                     int src_width,
+                                     int src_height);
+
+  struct PixelWeight {
+    static size_t TotalBytesForWeightCount(size_t weight_count);
+
+    void SetStartEnd(int src_start, int src_end, size_t weight_count) {
+      CHECK_LT(src_end - src_start, static_cast<int>(weight_count));
+      m_SrcStart = src_start;
+      m_SrcEnd = src_end;
+    }
+
+    uint32_t GetWeightForPosition(int position) const {
+      CHECK_GE(position, m_SrcStart);
+      CHECK_LE(position, m_SrcEnd);
+      return m_Weights[position - m_SrcStart];
+    }
+
+    void SetWeightForPosition(int position, uint32_t weight) {
+      CHECK_GE(position, m_SrcStart);
+      CHECK_LE(position, m_SrcEnd);
+      m_Weights[position - m_SrcStart] = weight;
+    }
+
+    // NOTE: relies on defined behaviour for unsigned overflow to
+    // decrement the previous position, as needed.
+    void RemoveLastWeightAndAdjust(uint32_t weight_change) {
+      CHECK_GT(m_SrcEnd, m_SrcStart);
+      --m_SrcEnd;
+      m_Weights[m_SrcEnd - m_SrcStart] += weight_change;
+    }
+
+    int m_SrcStart;
+    int m_SrcEnd;           // Note: inclusive, [0, -1] for empty range at 0.
+    uint32_t m_Weights[1];  // Not really 1, variable size.
+  };
+
+  class WeightTable {
+   public:
+    WeightTable();
+    ~WeightTable();
+
+    // Accepts a negative `dest_len` argument, producing a "mirror
+    // image" of the result if `dest_len` is negative.
+    bool CalculateWeights(int dest_len,
+                          int dest_min,
+                          int dest_max,
+                          int src_len,
+                          int src_min,
+                          int src_max,
+                          const FXDIB_ResampleOptions& options);
+
+    const PixelWeight* GetPixelWeight(int pixel) const;
+    PixelWeight* GetPixelWeight(int pixel);
+
+   private:
+    int m_DestMin = 0;
+    size_t m_ItemSizeBytes = 0;
+    size_t m_WeightTablesSizeBytes = 0;
+    DataVector<uint8_t> m_WeightTables;
+  };
+
   CStretchEngine(ScanlineComposerIface* pDestBitmap,
                  FXDIB_Format dest_format,
                  int dest_width,
                  int dest_height,
                  const FX_RECT& clip_rect,
-                 const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                 const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
                  const FXDIB_ResampleOptions& options);
   ~CStretchEngine();
 
   bool Continue(PauseIndicatorIface* pPause);
-
   bool StartStretchHorz();
   bool ContinueStretchHorz(PauseIndicatorIface* pPause);
   void StretchVert();
 
-  class CWeightTable {
-   public:
-    CWeightTable();
-    ~CWeightTable();
+  const FXDIB_ResampleOptions& GetResampleOptionsForTest() const {
+    return m_ResampleOptions;
+  }
 
-    bool Calc(int dest_len,
-              int dest_min,
-              int dest_max,
-              int src_len,
-              int src_min,
-              int src_max,
-              const FXDIB_ResampleOptions& options);
-
-    const PixelWeight* GetPixelWeight(int pixel) const;
-    PixelWeight* GetPixelWeight(int pixel) {
-      return const_cast<PixelWeight*>(
-          static_cast<const CWeightTable*>(this)->GetPixelWeight(pixel));
-    }
-
-    int* GetValueFromPixelWeight(PixelWeight* pWeight, int index) const;
-    size_t GetPixelWeightSize() const;
-
-   private:
-    int m_DestMin = 0;
-    int m_ItemSize = 0;
-    size_t m_dwWeightTablesSize = 0;
-    std::vector<uint8_t> m_WeightTables;
-  };
-
+ private:
   enum class State : uint8_t { kInitial, kHorizontal, kVertical };
 
   enum class TransformMethod : uint8_t {
     k1BppTo8Bpp,
     k1BppToManyBpp,
     k8BppTo8Bpp,
-    k8BppTo8BppWithAlpha,
     k8BppToManyBpp,
-    k8BppToManyBppWithAlpha,
     kManyBpptoManyBpp,
     kManyBpptoManyBppWithAlpha
   };
@@ -80,27 +140,25 @@
   const FXDIB_Format m_DestFormat;
   const int m_DestBpp;
   const int m_SrcBpp;
-  const int m_bHasAlpha;
-  RetainPtr<CFX_DIBBase> const m_pSource;
-  const uint32_t* m_pSrcPalette;
+  const bool m_bHasAlpha;
+  RetainPtr<const CFX_DIBBase> const m_pSource;
+  pdfium::span<const uint32_t> m_pSrcPalette;
   const int m_SrcWidth;
   const int m_SrcHeight;
   UnownedPtr<ScanlineComposerIface> const m_pDestBitmap;
   const int m_DestWidth;
   const int m_DestHeight;
   const FX_RECT m_DestClip;
-  std::vector<uint8_t> m_DestScanline;
-  std::vector<uint8_t> m_DestMaskScanline;
-  std::vector<uint8_t> m_InterBuf;
-  std::vector<uint8_t> m_ExtraAlphaBuf;
+  DataVector<uint8_t> m_DestScanline;
+  FixedTryAllocZeroedDataVector<uint8_t> m_InterBuf;
   FX_RECT m_SrcClip;
   int m_InterPitch;
   int m_ExtraMaskPitch;
   FXDIB_ResampleOptions m_ResampleOptions;
   TransformMethod m_TransMethod;
   State m_State = State::kInitial;
-  int m_CurRow;
-  CWeightTable m_WeightTable;
+  int m_CurRow = 0;
+  WeightTable m_WeightTable;
 };
 
 #endif  // CORE_FXGE_DIB_CSTRETCHENGINE_H_
diff --git a/core/fxge/dib/cstretchengine_unittest.cpp b/core/fxge/dib/cstretchengine_unittest.cpp
index 8c360e7..d11ac9d 100644
--- a/core/fxge/dib/cstretchengine_unittest.cpp
+++ b/core/fxge/dib/cstretchengine_unittest.cpp
@@ -1,19 +1,72 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxge/dib/cstretchengine.h"
 
-#include <memory>
 #include <utility>
 
 #include "core/fpdfapi/page/cpdf_dib.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
+
+namespace {
+
+// Discovered experimentally
+constexpr uint32_t kTooBigSrcLen = 20;
+constexpr uint32_t kTooBigDestLen = 32 * 1024 * 1024 + 1;
+
+uint32_t PixelWeightSum(const CStretchEngine::PixelWeight* weights) {
+  uint32_t sum = 0;
+  for (int i = weights->m_SrcStart; i <= weights->m_SrcEnd; ++i) {
+    sum += weights->GetWeightForPosition(i);
+  }
+  return sum;
+}
+
+void ExecuteOneStretchTest(int32_t dest_width,
+                           int32_t src_width,
+                           const FXDIB_ResampleOptions& options) {
+  constexpr uint32_t kExpectedSum = CStretchEngine::kFixedPointOne;
+  CStretchEngine::WeightTable table;
+  ASSERT_TRUE(table.CalculateWeights(dest_width, 0, dest_width, src_width, 0,
+                                     src_width, options));
+  for (int32_t i = 0; i < dest_width; ++i) {
+    EXPECT_EQ(kExpectedSum, PixelWeightSum(table.GetPixelWeight(i)))
+        << "for { " << src_width << ", " << dest_width << " } at " << i;
+  }
+}
+
+void ExecuteOneReversedStretchTest(int32_t dest_width,
+                                   int32_t src_width,
+                                   const FXDIB_ResampleOptions& options) {
+  constexpr uint32_t kExpectedSum = CStretchEngine::kFixedPointOne;
+  CStretchEngine::WeightTable table;
+  ASSERT_TRUE(table.CalculateWeights(-dest_width, 0, dest_width, src_width, 0,
+                                     src_width, options));
+  for (int32_t i = 0; i < dest_width; ++i) {
+    EXPECT_EQ(kExpectedSum, PixelWeightSum(table.GetPixelWeight(i)))
+        << "for { " << src_width << ", " << dest_width << " } at " << i
+        << " (reversed)";
+  }
+}
+
+void ExecuteStretchTests(const FXDIB_ResampleOptions& options) {
+  // Can't test everything, few random values chosen.
+  constexpr int32_t kDestWidths[] = {1, 2, 337, 512, 808, 2550};
+  constexpr int32_t kSrcWidths[] = {1, 2, 187, 256, 809, 1110};
+  for (int32_t src_width : kSrcWidths) {
+    for (int32_t dest_width : kDestWidths) {
+      ExecuteOneStretchTest(dest_width, src_width, options);
+      ExecuteOneReversedStretchTest(dest_width, src_width, options);
+    }
+  }
+}
+
+}  // namespace
 
 TEST(CStretchEngine, OverflowInCtor) {
   FX_RECT clip_rect;
@@ -21,14 +74,79 @@
   dict_obj->SetNewFor<CPDF_Number>("Width", 71000);
   dict_obj->SetNewFor<CPDF_Number>("Height", 12500);
   RetainPtr<CPDF_Stream> stream =
-      pdfium::MakeRetain<CPDF_Stream>(nullptr, 0, std::move(dict_obj));
-  auto dib_source = pdfium::MakeRetain<CPDF_DIB>();
-  dib_source->Load(nullptr, stream.Get());
-  CStretchEngine engine(nullptr, FXDIB_8bppRgb, 500, 500, clip_rect, dib_source,
-                        FXDIB_ResampleOptions());
-  EXPECT_TRUE(engine.m_ResampleOptions.bInterpolateBilinear);
-  EXPECT_FALSE(engine.m_ResampleOptions.bInterpolateBicubic);
-  EXPECT_FALSE(engine.m_ResampleOptions.bHalftone);
-  EXPECT_FALSE(engine.m_ResampleOptions.bNoSmoothing);
-  EXPECT_FALSE(engine.m_ResampleOptions.bLossy);
+      pdfium::MakeRetain<CPDF_Stream>(std::move(dict_obj));
+  auto dib_source = pdfium::MakeRetain<CPDF_DIB>(nullptr, stream);
+  dib_source->Load();
+  CStretchEngine engine(nullptr, FXDIB_Format::k8bppRgb, 500, 500, clip_rect,
+                        dib_source, FXDIB_ResampleOptions());
+  EXPECT_TRUE(engine.GetResampleOptionsForTest().bInterpolateBilinear);
+  EXPECT_FALSE(engine.GetResampleOptionsForTest().bHalftone);
+  EXPECT_FALSE(engine.GetResampleOptionsForTest().bNoSmoothing);
+  EXPECT_FALSE(engine.GetResampleOptionsForTest().bLossy);
+}
+
+TEST(CStretchEngine, WeightRounding) {
+  FXDIB_ResampleOptions options;
+  ExecuteStretchTests(options);
+}
+
+TEST(CStretchEngine, WeightRoundingNoSmoothing) {
+  FXDIB_ResampleOptions options;
+  options.bNoSmoothing = true;
+  ExecuteStretchTests(options);
+}
+
+TEST(CStretchEngine, WeightRoundingBilinear) {
+  FXDIB_ResampleOptions options;
+  options.bInterpolateBilinear = true;
+  ExecuteStretchTests(options);
+}
+
+TEST(CStretchEngine, WeightRoundingNoSmoothingBilinear) {
+  FXDIB_ResampleOptions options;
+  options.bNoSmoothing = true;
+  options.bInterpolateBilinear = true;
+  ExecuteStretchTests(options);
+}
+
+TEST(CStretchEngine, ZeroLengthSrc) {
+  FXDIB_ResampleOptions options;
+  CStretchEngine::WeightTable table;
+  ASSERT_TRUE(table.CalculateWeights(100, 0, 100, 0, 0, 0, options));
+}
+
+TEST(CStretchEngine, ZeroLengthSrcNoSmoothing) {
+  FXDIB_ResampleOptions options;
+  options.bNoSmoothing = true;
+  CStretchEngine::WeightTable table;
+  ASSERT_TRUE(table.CalculateWeights(100, 0, 100, 0, 0, 0, options));
+}
+
+TEST(CStretchEngine, ZeroLengthSrcBilinear) {
+  FXDIB_ResampleOptions options;
+  options.bInterpolateBilinear = true;
+  CStretchEngine::WeightTable table;
+  ASSERT_TRUE(table.CalculateWeights(100, 0, 100, 0, 0, 0, options));
+}
+
+TEST(CStretchEngine, ZeroLengthSrcNoSmoothingBilinear) {
+  FXDIB_ResampleOptions options;
+  options.bNoSmoothing = true;
+  options.bInterpolateBilinear = true;
+  CStretchEngine::WeightTable table;
+  ASSERT_TRUE(table.CalculateWeights(100, 0, 100, 0, 0, 0, options));
+}
+
+TEST(CStretchEngine, ZeroLengthDest) {
+  FXDIB_ResampleOptions options;
+  CStretchEngine::WeightTable table;
+  ASSERT_TRUE(table.CalculateWeights(0, 0, 0, 100, 0, 100, options));
+}
+
+TEST(CStretchEngine, TooManyWeights) {
+  FXDIB_ResampleOptions options;
+  CStretchEngine::WeightTable table;
+  ASSERT_FALSE(table.CalculateWeights(kTooBigDestLen, 0, kTooBigDestLen,
+                                      kTooBigSrcLen, 0, kTooBigSrcLen,
+                                      options));
 }
diff --git a/core/fxge/dib/fx_dib.cpp b/core/fxge/dib/fx_dib.cpp
new file mode 100644
index 0000000..c8f9833
--- /dev/null
+++ b/core/fxge/dib/fx_dib.cpp
@@ -0,0 +1,60 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxge/dib/fx_dib.h"
+
+#include <tuple>
+#include <utility>
+
+#include "build/build_config.h"
+
+#if BUILDFLAG(IS_WIN)
+#include <windows.h>
+#endif
+
+#if BUILDFLAG(IS_WIN)
+static_assert(sizeof(FX_COLORREF) == sizeof(COLORREF),
+              "FX_COLORREF vs. COLORREF mismatch");
+#endif
+
+FXDIB_Format MakeRGBFormat(int bpp) {
+  switch (bpp) {
+    case 1:
+      return FXDIB_Format::k1bppRgb;
+    case 8:
+      return FXDIB_Format::k8bppRgb;
+    case 24:
+      return FXDIB_Format::kRgb;
+    case 32:
+      return FXDIB_Format::kRgb32;
+    default:
+      return FXDIB_Format::kInvalid;
+  }
+}
+
+FXDIB_ResampleOptions::FXDIB_ResampleOptions() = default;
+
+bool FXDIB_ResampleOptions::HasAnyOptions() const {
+  return bInterpolateBilinear || bHalftone || bNoSmoothing || bLossy;
+}
+
+std::tuple<int, int, int, int> ArgbDecode(FX_ARGB argb) {
+  return std::make_tuple(FXARGB_A(argb), FXARGB_R(argb), FXARGB_G(argb),
+                         FXARGB_B(argb));
+}
+
+std::pair<int, FX_COLORREF> ArgbToAlphaAndColorRef(FX_ARGB argb) {
+  return {FXARGB_A(argb), ArgbToColorRef(argb)};
+}
+
+FX_COLORREF ArgbToColorRef(FX_ARGB argb) {
+  return FXSYS_BGR(FXARGB_B(argb), FXARGB_G(argb), FXARGB_R(argb));
+}
+
+FX_ARGB AlphaAndColorRefToArgb(int a, FX_COLORREF colorref) {
+  return ArgbEncode(a, FXSYS_GetRValue(colorref), FXSYS_GetGValue(colorref),
+                    FXSYS_GetBValue(colorref));
+}
diff --git a/core/fxge/dib/fx_dib.h b/core/fxge/dib/fx_dib.h
new file mode 100644
index 0000000..bd458f9
--- /dev/null
+++ b/core/fxge/dib/fx_dib.h
@@ -0,0 +1,160 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_DIB_FX_DIB_H_
+#define CORE_FXGE_DIB_FX_DIB_H_
+
+#include <stdint.h>
+
+#include <tuple>
+#include <utility>
+
+// Encoding:
+// - Bits-per-pixel: value & 0xFF
+// - Is mask: value & 0x100
+// - Has alpha: value & 0x200
+enum class FXDIB_Format : uint16_t {
+  kInvalid = 0,
+  k1bppRgb = 0x001,
+  k8bppRgb = 0x008,
+  kRgb = 0x018,
+  kRgb32 = 0x020,
+  k1bppMask = 0x101,
+  k8bppMask = 0x108,
+  kArgb = 0x220,
+};
+
+using FX_ARGB = uint32_t;
+using FX_CMYK = uint32_t;
+
+// FX_COLORREF, like win32 COLORREF, is BGR.
+using FX_COLORREF = uint32_t;
+
+struct FXDIB_ResampleOptions {
+  FXDIB_ResampleOptions();
+
+  bool HasAnyOptions() const;
+
+  bool bInterpolateBilinear = false;
+  bool bHalftone = false;
+  bool bNoSmoothing = false;
+  bool bLossy = false;
+};
+
+// See PDF 1.7 spec, table 7.2 and 7.3. The enum values need to be in the same
+// order as listed in the spec.
+enum class BlendMode {
+  kNormal = 0,
+  kMultiply,
+  kScreen,
+  kOverlay,
+  kDarken,
+  kLighten,
+  kColorDodge,
+  kColorBurn,
+  kHardLight,
+  kSoftLight,
+  kDifference,
+  kExclusion,
+  kHue,
+  kSaturation,
+  kColor,
+  kLuminosity,
+  kLast = kLuminosity,
+};
+
+constexpr uint32_t FXSYS_BGR(uint8_t b, uint8_t g, uint8_t r) {
+  return (b << 16) | (g << 8) | r;
+}
+
+constexpr uint8_t FXSYS_GetRValue(uint32_t bgr) {
+  return bgr & 0xff;
+}
+
+constexpr uint8_t FXSYS_GetGValue(uint32_t bgr) {
+  return (bgr >> 8) & 0xff;
+}
+
+constexpr uint8_t FXSYS_GetBValue(uint32_t bgr) {
+  return (bgr >> 16) & 0xff;
+}
+
+constexpr unsigned int FXSYS_GetUnsignedAlpha(float alpha) {
+  return static_cast<unsigned int>(alpha * 255.f + 0.5f);
+}
+
+// Bits per pixel, not bytes.
+inline int GetBppFromFormat(FXDIB_Format format) {
+  return static_cast<uint16_t>(format) & 0xff;
+}
+
+// AKA bytes per pixel, assuming 8-bits per component.
+inline int GetCompsFromFormat(FXDIB_Format format) {
+  return (static_cast<uint16_t>(format) & 0xff) / 8;
+}
+
+inline bool GetIsMaskFromFormat(FXDIB_Format format) {
+  return !!(static_cast<uint16_t>(format) & 0x100);
+}
+
+FXDIB_Format MakeRGBFormat(int bpp);
+
+constexpr FX_CMYK CmykEncode(uint32_t c, uint32_t m, uint32_t y, uint32_t k) {
+  return (c << 24) | (m << 16) | (y << 8) | k;
+}
+
+// Returns (a, r, g, b)
+std::tuple<int, int, int, int> ArgbDecode(FX_ARGB argb);
+
+// Returns (a, FX_COLORREF)
+std::pair<int, FX_COLORREF> ArgbToAlphaAndColorRef(FX_ARGB argb);
+
+// Returns FX_COLORREF.
+FX_COLORREF ArgbToColorRef(FX_ARGB argb);
+
+constexpr FX_ARGB ArgbEncode(uint32_t a, uint32_t r, uint32_t g, uint32_t b) {
+  return (a << 24) | (r << 16) | (g << 8) | b;
+}
+
+FX_ARGB AlphaAndColorRefToArgb(int a, FX_COLORREF colorref);
+
+#define FXARGB_A(argb) ((uint8_t)((argb) >> 24))
+#define FXARGB_R(argb) ((uint8_t)((argb) >> 16))
+#define FXARGB_G(argb) ((uint8_t)((argb) >> 8))
+#define FXARGB_B(argb) ((uint8_t)(argb))
+#define FXARGB_MUL_ALPHA(argb, alpha) \
+  (((((argb) >> 24) * (alpha) / 255) << 24) | ((argb)&0xffffff))
+
+#define FXRGB2GRAY(r, g, b) (((b)*11 + (g)*59 + (r)*30) / 100)
+#define FXDIB_ALPHA_MERGE(backdrop, source, source_alpha) \
+  (((backdrop) * (255 - (source_alpha)) + (source) * (source_alpha)) / 255)
+#define FXARGB_GETDIB(p)                              \
+  ((((uint8_t*)(p))[0]) | (((uint8_t*)(p))[1] << 8) | \
+   (((uint8_t*)(p))[2] << 16) | (((uint8_t*)(p))[3] << 24))
+#define FXARGB_SETDIB(p, argb)                  \
+  ((uint8_t*)(p))[0] = (uint8_t)(argb),         \
+  ((uint8_t*)(p))[1] = (uint8_t)((argb) >> 8),  \
+  ((uint8_t*)(p))[2] = (uint8_t)((argb) >> 16), \
+  ((uint8_t*)(p))[3] = (uint8_t)((argb) >> 24)
+#define FXARGB_SETRGBORDERDIB(p, argb)          \
+  ((uint8_t*)(p))[3] = (uint8_t)(argb >> 24),   \
+  ((uint8_t*)(p))[0] = (uint8_t)((argb) >> 16), \
+  ((uint8_t*)(p))[1] = (uint8_t)((argb) >> 8),  \
+  ((uint8_t*)(p))[2] = (uint8_t)(argb)
+#define FXCMYK_TODIB(cmyk)                                    \
+  ((uint8_t)((cmyk) >> 24) | ((uint8_t)((cmyk) >> 16)) << 8 | \
+   ((uint8_t)((cmyk) >> 8)) << 16 | ((uint8_t)(cmyk) << 24))
+#define FXARGB_TOBGRORDERDIB(argb)                       \
+  ((uint8_t)(argb >> 16) | ((uint8_t)(argb >> 8)) << 8 | \
+   ((uint8_t)(argb)) << 16 | ((uint8_t)(argb >> 24) << 24))
+
+inline void ReverseCopy3Bytes(uint8_t* dest, const uint8_t* src) {
+  dest[2] = src[0];
+  dest[1] = src[1];
+  dest[0] = src[2];
+}
+
+#endif  // CORE_FXGE_DIB_FX_DIB_H_
diff --git a/core/fxge/dib/fx_dib_main.cpp b/core/fxge/dib/fx_dib_main.cpp
deleted file mode 100644
index 31e5919..0000000
--- a/core/fxge/dib/fx_dib_main.cpp
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxge/fx_dib.h"
-
-#include <tuple>
-#include <utility>
-
-#include "build/build_config.h"
-#include "core/fxcrt/fx_extension.h"
-
-#if defined(OS_WIN)
-static_assert(sizeof(FX_COLORREF) == sizeof(COLORREF),
-              "FX_COLORREF vs. COLORREF mismatch");
-#endif
-
-const int16_t SDP_Table[513] = {
-    256, 256, 256, 256, 256, 256, 256, 256, 256, 255, 255, 255, 255, 255, 255,
-    254, 254, 254, 254, 253, 253, 253, 252, 252, 252, 251, 251, 251, 250, 250,
-    249, 249, 249, 248, 248, 247, 247, 246, 246, 245, 244, 244, 243, 243, 242,
-    242, 241, 240, 240, 239, 238, 238, 237, 236, 236, 235, 234, 233, 233, 232,
-    231, 230, 230, 229, 228, 227, 226, 226, 225, 224, 223, 222, 221, 220, 219,
-    218, 218, 217, 216, 215, 214, 213, 212, 211, 210, 209, 208, 207, 206, 205,
-    204, 203, 202, 201, 200, 199, 198, 196, 195, 194, 193, 192, 191, 190, 189,
-    188, 186, 185, 184, 183, 182, 181, 179, 178, 177, 176, 175, 173, 172, 171,
-    170, 169, 167, 166, 165, 164, 162, 161, 160, 159, 157, 156, 155, 154, 152,
-    151, 150, 149, 147, 146, 145, 143, 142, 141, 140, 138, 137, 136, 134, 133,
-    132, 130, 129, 128, 126, 125, 124, 122, 121, 120, 119, 117, 116, 115, 113,
-    112, 111, 109, 108, 107, 105, 104, 103, 101, 100, 99,  97,  96,  95,  93,
-    92,  91,  89,  88,  87,  85,  84,  83,  81,  80,  79,  77,  76,  75,  73,
-    72,  71,  69,  68,  67,  66,  64,  63,  62,  60,  59,  58,  57,  55,  54,
-    53,  52,  50,  49,  48,  47,  45,  44,  43,  42,  40,  39,  38,  37,  36,
-    34,  33,  32,  31,  30,  28,  27,  26,  25,  24,  23,  21,  20,  19,  18,
-    17,  16,  15,  14,  13,  11,  10,  9,   8,   7,   6,   5,   4,   3,   2,
-    1,   0,   0,   -1,  -2,  -3,  -4,  -5,  -6,  -7,  -7,  -8,  -9,  -10, -11,
-    -12, -12, -13, -14, -15, -15, -16, -17, -17, -18, -19, -19, -20, -21, -21,
-    -22, -22, -23, -24, -24, -25, -25, -26, -26, -27, -27, -27, -28, -28, -29,
-    -29, -30, -30, -30, -31, -31, -31, -32, -32, -32, -33, -33, -33, -33, -34,
-    -34, -34, -34, -35, -35, -35, -35, -35, -36, -36, -36, -36, -36, -36, -36,
-    -36, -36, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37,
-    -37, -37, -37, -37, -37, -37, -37, -37, -36, -36, -36, -36, -36, -36, -36,
-    -36, -36, -35, -35, -35, -35, -35, -35, -34, -34, -34, -34, -34, -33, -33,
-    -33, -33, -33, -32, -32, -32, -32, -31, -31, -31, -31, -30, -30, -30, -30,
-    -29, -29, -29, -29, -28, -28, -28, -27, -27, -27, -27, -26, -26, -26, -25,
-    -25, -25, -24, -24, -24, -23, -23, -23, -22, -22, -22, -22, -21, -21, -21,
-    -20, -20, -20, -19, -19, -19, -18, -18, -18, -17, -17, -17, -16, -16, -16,
-    -15, -15, -15, -14, -14, -14, -13, -13, -13, -12, -12, -12, -11, -11, -11,
-    -10, -10, -10, -9,  -9,  -9,  -9,  -8,  -8,  -8,  -7,  -7,  -7,  -7,  -6,
-    -6,  -6,  -6,  -5,  -5,  -5,  -5,  -4,  -4,  -4,  -4,  -3,  -3,  -3,  -3,
-    -3,  -2,  -2,  -2,  -2,  -2,  -1,  -1,  -1,  -1,  -1,  -1,  0,   0,   0,
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-    0,   0,   0,
-};
-
-FXDIB_ResampleOptions::FXDIB_ResampleOptions() = default;
-
-bool FXDIB_ResampleOptions::HasAnyOptions() const {
-  return bInterpolateBilinear || bInterpolateBicubic || bHalftone ||
-         bNoSmoothing || bLossy;
-}
-
-FX_RECT FXDIB_SwapClipBox(const FX_RECT& clip,
-                          int width,
-                          int height,
-                          bool bFlipX,
-                          bool bFlipY) {
-  FX_RECT rect;
-  if (bFlipY) {
-    rect.left = height - clip.top;
-    rect.right = height - clip.bottom;
-  } else {
-    rect.left = clip.top;
-    rect.right = clip.bottom;
-  }
-  if (bFlipX) {
-    rect.top = width - clip.left;
-    rect.bottom = width - clip.right;
-  } else {
-    rect.top = clip.left;
-    rect.bottom = clip.right;
-  }
-  rect.Normalize();
-  return rect;
-}
-
-std::tuple<int, int, int, int> ArgbDecode(FX_ARGB argb) {
-  return std::make_tuple(FXARGB_A(argb), FXARGB_R(argb), FXARGB_G(argb),
-                         FXARGB_B(argb));
-}
-
-std::pair<int, FX_COLORREF> ArgbToAlphaAndColorRef(FX_ARGB argb) {
-  return {FXARGB_A(argb), ArgbToColorRef(argb)};
-}
-
-FX_COLORREF ArgbToColorRef(FX_ARGB argb) {
-  return FXSYS_BGR(FXARGB_B(argb), FXARGB_G(argb), FXARGB_R(argb));
-}
-
-FX_ARGB AlphaAndColorRefToArgb(int a, FX_COLORREF colorref) {
-  return ArgbEncode(a, FXSYS_GetRValue(colorref), FXSYS_GetGValue(colorref),
-                    FXSYS_GetBValue(colorref));
-}
-
-FX_ARGB StringToFXARGB(WideStringView view) {
-  static constexpr FX_ARGB kDefaultValue = 0xff000000;
-  if (view.IsEmpty())
-    return kDefaultValue;
-
-  int cc = 0;
-  const wchar_t* str = view.unterminated_c_str();
-  int len = view.GetLength();
-  while (cc < len && FXSYS_iswspace(str[cc]))
-    cc++;
-
-  if (cc >= len)
-    return kDefaultValue;
-
-  uint8_t r = 0;
-  uint8_t g = 0;
-  uint8_t b = 0;
-  while (cc < len) {
-    if (str[cc] == ',' || !FXSYS_IsDecimalDigit(str[cc]))
-      break;
-
-    r = r * 10 + str[cc] - '0';
-    cc++;
-  }
-  if (cc < len && str[cc] == ',') {
-    cc++;
-    while (cc < len && FXSYS_iswspace(str[cc]))
-      cc++;
-
-    while (cc < len) {
-      if (str[cc] == ',' || !FXSYS_IsDecimalDigit(str[cc]))
-        break;
-
-      g = g * 10 + str[cc] - '0';
-      cc++;
-    }
-    if (cc < len && str[cc] == ',') {
-      cc++;
-      while (cc < len && FXSYS_iswspace(str[cc]))
-        cc++;
-
-      while (cc < len) {
-        if (str[cc] == ',' || !FXSYS_IsDecimalDigit(str[cc]))
-          break;
-
-        b = b * 10 + str[cc] - '0';
-        cc++;
-      }
-    }
-  }
-  return (0xffU << 24) | (r << 16) | (g << 8) | b;
-}
diff --git a/core/fxge/dib/scanlinecomposer_iface.h b/core/fxge/dib/scanlinecomposer_iface.h
index 316736f..ef997cf 100644
--- a/core/fxge/dib/scanlinecomposer_iface.h
+++ b/core/fxge/dib/scanlinecomposer_iface.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,20 +7,22 @@
 #ifndef CORE_FXGE_DIB_SCANLINECOMPOSER_IFACE_H_
 #define CORE_FXGE_DIB_SCANLINECOMPOSER_IFACE_H_
 
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/containers/span.h"
 
 class ScanlineComposerIface {
  public:
   virtual ~ScanlineComposerIface() = default;
 
   virtual void ComposeScanline(int line,
-                               const uint8_t* scanline,
-                               const uint8_t* scan_extra_alpha) = 0;
+                               pdfium::span<const uint8_t> scanline) = 0;
 
+  // `src_format` cannot be `FXDIB_Format::k1bppMask` or
+  // `FXDIB_Format::k1bppRgb`.
   virtual bool SetInfo(int width,
                        int height,
                        FXDIB_Format src_format,
-                       uint32_t* pSrcPalette) = 0;
+                       pdfium::span<const uint32_t> src_palette) = 0;
 };
 
 #endif  // CORE_FXGE_DIB_SCANLINECOMPOSER_IFACE_H_
diff --git a/core/fxge/fontdata/chromefontdata/FoxitDingbats.cpp b/core/fxge/fontdata/chromefontdata/FoxitDingbats.cpp
index 75088ce..3c61db0 100644
--- a/core/fxge/fontdata/chromefontdata/FoxitDingbats.cpp
+++ b/core/fxge/fontdata/chromefontdata/FoxitDingbats.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 
-const unsigned char g_FoxitDingbatsFontData[29513] = {
+const unsigned char kFoxitDingbatsFontData[29513] = {
     0x1,  0x0,  0x4,  0x2,  0x0,  0x1,  0x1,  0x1,  0x11, 0x43, 0x68, 0x72,
     0x6f, 0x6d, 0x44, 0x69, 0x6e, 0x67, 0x62, 0x61, 0x74, 0x73, 0x4f, 0x54,
     0x46, 0x0,  0x1,  0x1,  0x1,  0x25, 0xf8, 0x10, 0x0,  0xf8, 0xe4, 0x1,
diff --git a/core/fxge/fontdata/chromefontdata/FoxitFixed.cpp b/core/fxge/fontdata/chromefontdata/FoxitFixed.cpp
index 61c0857..1ec59bf 100644
--- a/core/fxge/fontdata/chromefontdata/FoxitFixed.cpp
+++ b/core/fxge/fontdata/chromefontdata/FoxitFixed.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 
-const unsigned char g_FoxitFixedFontData[17597] = {
+const unsigned char kFoxitFixedFontData[17597] = {
     0x1,  0x0,  0x4,  0x2,  0x0,  0x1,  0x1,  0x1,  0xe,  0x43, 0x68, 0x72,
     0x6f, 0x6d, 0x46, 0x69, 0x78, 0x65, 0x64, 0x4f, 0x54, 0x46, 0x0,  0x1,
     0x1,  0x1,  0x23, 0xf8, 0x10, 0x0,  0xf8, 0x2a, 0x1,  0xf8, 0x2b, 0x2,
diff --git a/core/fxge/fontdata/chromefontdata/FoxitFixedBold.cpp b/core/fxge/fontdata/chromefontdata/FoxitFixedBold.cpp
index c1494f5..84a6768 100644
--- a/core/fxge/fontdata/chromefontdata/FoxitFixedBold.cpp
+++ b/core/fxge/fontdata/chromefontdata/FoxitFixedBold.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 
-const unsigned char g_FoxitFixedBoldFontData[18055] = {
+const unsigned char kFoxitFixedBoldFontData[18055] = {
     0x1,  0x0,  0x4,  0x2,  0x0,  0x1,  0x1,  0x1,  0x13, 0x43, 0x68, 0x72,
     0x6f, 0x6d, 0x46, 0x69, 0x78, 0x65, 0x64, 0x4f, 0x54, 0x46, 0x2d, 0x42,
     0x6f, 0x6c, 0x64, 0x0,  0x1,  0x1,  0x1,  0x24, 0xf8, 0x10, 0x0,  0xf8,
diff --git a/core/fxge/fontdata/chromefontdata/FoxitFixedBoldItalic.cpp b/core/fxge/fontdata/chromefontdata/FoxitFixedBoldItalic.cpp
index c9509e6..63da1ce 100644
--- a/core/fxge/fontdata/chromefontdata/FoxitFixedBoldItalic.cpp
+++ b/core/fxge/fontdata/chromefontdata/FoxitFixedBoldItalic.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 
-const unsigned char g_FoxitFixedBoldItalicFontData[19151] = {
+const unsigned char kFoxitFixedBoldItalicFontData[19151] = {
     0x1,  0x0,  0x4,  0x2,  0x0,  0x1,  0x1,  0x1,  0x19, 0x43, 0x68, 0x72,
     0x6f, 0x6d, 0x46, 0x69, 0x78, 0x65, 0x64, 0x4f, 0x54, 0x46, 0x2d, 0x42,
     0x6f, 0x6c, 0x64, 0x49, 0x74, 0x61, 0x6c, 0x69, 0x63, 0x0,  0x1,  0x1,
diff --git a/core/fxge/fontdata/chromefontdata/FoxitFixedItalic.cpp b/core/fxge/fontdata/chromefontdata/FoxitFixedItalic.cpp
index 7fe9f63..ba56707 100644
--- a/core/fxge/fontdata/chromefontdata/FoxitFixedItalic.cpp
+++ b/core/fxge/fontdata/chromefontdata/FoxitFixedItalic.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 
-const unsigned char g_FoxitFixedItalicFontData[18746] = {
+const unsigned char kFoxitFixedItalicFontData[18746] = {
     0x1,  0x0,  0x4,  0x2,  0x0,  0x1,  0x1,  0x1,  0x15, 0x43, 0x68, 0x72,
     0x6f, 0x6d, 0x46, 0x69, 0x78, 0x65, 0x64, 0x4f, 0x54, 0x46, 0x2d, 0x49,
     0x74, 0x61, 0x6c, 0x69, 0x63, 0x0,  0x1,  0x1,  0x1,  0x23, 0xf8, 0x10,
diff --git a/core/fxge/fontdata/chromefontdata/FoxitSans.cpp b/core/fxge/fontdata/chromefontdata/FoxitSans.cpp
index 16da03e..79d194c 100644
--- a/core/fxge/fontdata/chromefontdata/FoxitSans.cpp
+++ b/core/fxge/fontdata/chromefontdata/FoxitSans.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 
-const unsigned char g_FoxitSansFontData[15025] = {
+const unsigned char kFoxitSansFontData[15025] = {
     0x1,  0x0,  0x4,  0x2,  0x0,  0x1,  0x1,  0x1,  0xd,  0x43, 0x68, 0x72,
     0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x4f, 0x54, 0x46, 0x0,  0x1,  0x1,
     0x1,  0x24, 0xf8, 0x10, 0x0,  0xf8, 0x1c, 0x1,  0xf8, 0x1d, 0x2,  0xf8,
diff --git a/core/fxge/fontdata/chromefontdata/FoxitSansBold.cpp b/core/fxge/fontdata/chromefontdata/FoxitSansBold.cpp
index 10a63a5..5a54eae 100644
--- a/core/fxge/fontdata/chromefontdata/FoxitSansBold.cpp
+++ b/core/fxge/fontdata/chromefontdata/FoxitSansBold.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 
-const unsigned char g_FoxitSansBoldFontData[16344] = {
+const unsigned char kFoxitSansBoldFontData[16344] = {
     0x1,  0x0,  0x4,  0x2,  0x0,  0x1,  0x1,  0x1,  0x12, 0x43, 0x68, 0x72,
     0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x4f, 0x54, 0x46, 0x2d, 0x42, 0x6f,
     0x6c, 0x64, 0x0,  0x1,  0x1,  0x1,  0x24, 0xf8, 0x10, 0x0,  0xf8, 0x1c,
diff --git a/core/fxge/fontdata/chromefontdata/FoxitSansBoldItalic.cpp b/core/fxge/fontdata/chromefontdata/FoxitSansBoldItalic.cpp
index 054976c..475f875 100644
--- a/core/fxge/fontdata/chromefontdata/FoxitSansBoldItalic.cpp
+++ b/core/fxge/fontdata/chromefontdata/FoxitSansBoldItalic.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 
-const unsigned char g_FoxitSansBoldItalicFontData[16418] = {
+const unsigned char kFoxitSansBoldItalicFontData[16418] = {
     0x1,  0x0,  0x4,  0x2,  0x0,  0x1,  0x1,  0x1,  0x17, 0x43, 0x68, 0x72,
     0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x50, 0x53, 0x2d, 0x42, 0x6f, 0x6c,
     0x64, 0x49, 0x74, 0x61, 0x6c, 0x69, 0x63, 0x0,  0x1,  0x1,  0x1,  0x27,
diff --git a/core/fxge/fontdata/chromefontdata/FoxitSansItalic.cpp b/core/fxge/fontdata/chromefontdata/FoxitSansItalic.cpp
index 2912e16..5af176d 100644
--- a/core/fxge/fontdata/chromefontdata/FoxitSansItalic.cpp
+++ b/core/fxge/fontdata/chromefontdata/FoxitSansItalic.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 
-const unsigned char g_FoxitSansItalicFontData[16339] = {
+const unsigned char kFoxitSansItalicFontData[16339] = {
     0x1,  0x0,  0x4,  0x2,  0x0,  0x1,  0x1,  0x1,  0x14, 0x43, 0x68, 0x72,
     0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x4f, 0x54, 0x46, 0x2d, 0x49, 0x74,
     0x61, 0x6c, 0x69, 0x63, 0x0,  0x1,  0x1,  0x1,  0x27, 0xf8, 0x10, 0x0,
diff --git a/core/fxge/fontdata/chromefontdata/FoxitSansMM.cpp b/core/fxge/fontdata/chromefontdata/FoxitSansMM.cpp
index 7f540ae..985a6bb 100644
--- a/core/fxge/fontdata/chromefontdata/FoxitSansMM.cpp
+++ b/core/fxge/fontdata/chromefontdata/FoxitSansMM.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 
-const unsigned char g_FoxitSansMMFontData[66919] = {
+const unsigned char kFoxitSansMMFontData[66919] = {
     0x80, 0x01, 0xD2, 0x29, 0x00, 0x00, 0x25, 0x21, 0x50, 0x53, 0x2D, 0x41,
     0x64, 0x6F, 0x62, 0x65, 0x46, 0x6F, 0x6E, 0x74, 0x2D, 0x31, 0x2E, 0x30,
     0x3A, 0x20, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x53, 0x61, 0x6E, 0x73,
diff --git a/core/fxge/fontdata/chromefontdata/FoxitSerif.cpp b/core/fxge/fontdata/chromefontdata/FoxitSerif.cpp
index 5fa276d..9ec8721 100644
--- a/core/fxge/fontdata/chromefontdata/FoxitSerif.cpp
+++ b/core/fxge/fontdata/chromefontdata/FoxitSerif.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 
-const unsigned char g_FoxitSerifFontData[19469] = {
+const unsigned char kFoxitSerifFontData[19469] = {
     0x1,  0x0,  0x4,  0x2,  0x0,  0x1,  0x1,  0x1,  0xe,  0x43, 0x68, 0x72,
     0x6f, 0x6d, 0x53, 0x65, 0x72, 0x69, 0x66, 0x4f, 0x54, 0x46, 0x0,  0x1,
     0x1,  0x1,  0x26, 0xf8, 0x10, 0x0,  0xf8, 0x1c, 0x1,  0xf8, 0x1d, 0x2,
diff --git a/core/fxge/fontdata/chromefontdata/FoxitSerifBold.cpp b/core/fxge/fontdata/chromefontdata/FoxitSerifBold.cpp
index 2875863..d886483 100644
--- a/core/fxge/fontdata/chromefontdata/FoxitSerifBold.cpp
+++ b/core/fxge/fontdata/chromefontdata/FoxitSerifBold.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 
-const unsigned char g_FoxitSerifBoldFontData[19395] = {
+const unsigned char kFoxitSerifBoldFontData[19395] = {
     0x1,  0x0,  0x4,  0x2,  0x0,  0x1,  0x1,  0x1,  0x13, 0x43, 0x68, 0x72,
     0x6f, 0x6d, 0x53, 0x65, 0x72, 0x69, 0x66, 0x4f, 0x54, 0x46, 0x2d, 0x42,
     0x6f, 0x6c, 0x64, 0x0,  0x1,  0x1,  0x1,  0x26, 0xf8, 0x10, 0x0,  0xf8,
diff --git a/core/fxge/fontdata/chromefontdata/FoxitSerifBoldItalic.cpp b/core/fxge/fontdata/chromefontdata/FoxitSerifBoldItalic.cpp
index 159e535..724537a 100644
--- a/core/fxge/fontdata/chromefontdata/FoxitSerifBoldItalic.cpp
+++ b/core/fxge/fontdata/chromefontdata/FoxitSerifBoldItalic.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 
-const unsigned char g_FoxitSerifBoldItalicFontData[20733] = {
+const unsigned char kFoxitSerifBoldItalicFontData[20733] = {
     0x1,  0x0,  0x4,  0x2,  0x0,  0x1,  0x1,  0x1,  0x19, 0x43, 0x68, 0x72,
     0x6f, 0x6d, 0x53, 0x65, 0x72, 0x69, 0x66, 0x4f, 0x54, 0x46, 0x2d, 0x42,
     0x6f, 0x6c, 0x64, 0x49, 0x74, 0x61, 0x6c, 0x69, 0x63, 0x0,  0x1,  0x1,
diff --git a/core/fxge/fontdata/chromefontdata/FoxitSerifItalic.cpp b/core/fxge/fontdata/chromefontdata/FoxitSerifItalic.cpp
index eec32d7..8fcda85 100644
--- a/core/fxge/fontdata/chromefontdata/FoxitSerifItalic.cpp
+++ b/core/fxge/fontdata/chromefontdata/FoxitSerifItalic.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 
-const unsigned char g_FoxitSerifItalicFontData[21227] = {
+const unsigned char kFoxitSerifItalicFontData[21227] = {
     0x1,  0x0,  0x4,  0x2,  0x0,  0x1,  0x1,  0x1,  0x15, 0x43, 0x68, 0x72,
     0x6f, 0x6d, 0x53, 0x65, 0x72, 0x69, 0x66, 0x4f, 0x54, 0x46, 0x2d, 0x49,
     0x74, 0x61, 0x6c, 0x69, 0x63, 0x0,  0x1,  0x1,  0x1,  0x2a, 0xf8, 0x10,
diff --git a/core/fxge/fontdata/chromefontdata/FoxitSerifMM.cpp b/core/fxge/fontdata/chromefontdata/FoxitSerifMM.cpp
index 4195e9c..6389782 100644
--- a/core/fxge/fontdata/chromefontdata/FoxitSerifMM.cpp
+++ b/core/fxge/fontdata/chromefontdata/FoxitSerifMM.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 
-const unsigned char g_FoxitSerifMMFontData[113417] = {
+const unsigned char kFoxitSerifMMFontData[113417] = {
     0x80, 0x01, 0xD6, 0x29, 0x00, 0x00, 0x25, 0x21, 0x50, 0x53, 0x2D, 0x41,
     0x64, 0x6F, 0x62, 0x65, 0x46, 0x6F, 0x6E, 0x74, 0x2D, 0x31, 0x2E, 0x30,
     0x3A, 0x20, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69,
diff --git a/core/fxge/fontdata/chromefontdata/FoxitSymbol.cpp b/core/fxge/fontdata/chromefontdata/FoxitSymbol.cpp
index 37ce309..2a460e6 100644
--- a/core/fxge/fontdata/chromefontdata/FoxitSymbol.cpp
+++ b/core/fxge/fontdata/chromefontdata/FoxitSymbol.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 
-const unsigned char g_FoxitSymbolFontData[16729] = {
+const unsigned char kFoxitSymbolFontData[16729] = {
     0x1,  0x0,  0x4,  0x2,  0x0,  0x1,  0x1,  0x1,  0xf,  0x43, 0x68, 0x72,
     0x6f, 0x6d, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x4f, 0x54, 0x46, 0x0,
     0x1,  0x1,  0x1,  0x26, 0xf8, 0x10, 0x0,  0xf8, 0xad, 0x1,  0xf8, 0xae,
diff --git a/core/fxge/fontdata/chromefontdata/chromefontdata.h b/core/fxge/fontdata/chromefontdata/chromefontdata.h
index cbd1ad2..7d6fb78 100644
--- a/core/fxge/fontdata/chromefontdata/chromefontdata.h
+++ b/core/fxge/fontdata/chromefontdata/chromefontdata.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,21 +7,21 @@
 #ifndef CORE_FXGE_FONTDATA_CHROMEFONTDATA_CHROMEFONTDATA_H_
 #define CORE_FXGE_FONTDATA_CHROMEFONTDATA_CHROMEFONTDATA_H_
 
-extern const unsigned char g_FoxitFixedItalicFontData[18746];
-extern const unsigned char g_FoxitFixedFontData[17597];
-extern const unsigned char g_FoxitSansItalicFontData[16339];
-extern const unsigned char g_FoxitSansFontData[15025];
-extern const unsigned char g_FoxitSerifItalicFontData[21227];
-extern const unsigned char g_FoxitSerifFontData[19469];
-extern const unsigned char g_FoxitFixedBoldItalicFontData[19151];
-extern const unsigned char g_FoxitFixedBoldFontData[18055];
-extern const unsigned char g_FoxitSansBoldItalicFontData[16418];
-extern const unsigned char g_FoxitSansBoldFontData[16344];
-extern const unsigned char g_FoxitSerifBoldItalicFontData[20733];
-extern const unsigned char g_FoxitSerifBoldFontData[19395];
-extern const unsigned char g_FoxitSymbolFontData[16729];
-extern const unsigned char g_FoxitDingbatsFontData[29513];
-extern const unsigned char g_FoxitSerifMMFontData[113417];
-extern const unsigned char g_FoxitSansMMFontData[66919];
+extern const unsigned char kFoxitFixedItalicFontData[18746];
+extern const unsigned char kFoxitFixedFontData[17597];
+extern const unsigned char kFoxitSansItalicFontData[16339];
+extern const unsigned char kFoxitSansFontData[15025];
+extern const unsigned char kFoxitSerifItalicFontData[21227];
+extern const unsigned char kFoxitSerifFontData[19469];
+extern const unsigned char kFoxitFixedBoldItalicFontData[19151];
+extern const unsigned char kFoxitFixedBoldFontData[18055];
+extern const unsigned char kFoxitSansBoldItalicFontData[16418];
+extern const unsigned char kFoxitSansBoldFontData[16344];
+extern const unsigned char kFoxitSerifBoldItalicFontData[20733];
+extern const unsigned char kFoxitSerifBoldFontData[19395];
+extern const unsigned char kFoxitSymbolFontData[16729];
+extern const unsigned char kFoxitDingbatsFontData[29513];
+extern const unsigned char kFoxitSerifMMFontData[113417];
+extern const unsigned char kFoxitSansMMFontData[66919];
 
 #endif  // CORE_FXGE_FONTDATA_CHROMEFONTDATA_CHROMEFONTDATA_H_
diff --git a/core/fxge/freetype/fx_freetype.cpp b/core/fxge/freetype/fx_freetype.cpp
index fb1c29a..effd160 100644
--- a/core/fxge/freetype/fx_freetype.cpp
+++ b/core/fxge/freetype/fx_freetype.cpp
@@ -1,22 +1,29 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
-#include "core/fxge/fx_freetype.h"
+#include "core/fxge/freetype/fx_freetype.h"
+
+#include <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"
 
-static int xyq_search_node(char* glyph_name,
-                           int name_offset,
-                           int table_offset,
-                           wchar_t unicode) {
-  int i, count;
+namespace {
 
+constexpr uint32_t kVariantBit = 0x80000000;
+
+int xyq_search_node(char* glyph_name,
+                    int name_offset,
+                    int table_offset,
+                    wchar_t unicode) {
   // copy letters
-  while (1) {
+  while (true) {
     glyph_name[name_offset] = ft_adobe_glyph_list[table_offset] & 0x7f;
     name_offset++;
     table_offset++;
@@ -26,7 +33,7 @@
   glyph_name[name_offset] = 0;
 
   // get child count
-  count = ft_adobe_glyph_list[table_offset] & 0x7f;
+  int count = ft_adobe_glyph_list[table_offset] & 0x7f;
 
   // check if we have value for this node
   if (ft_adobe_glyph_list[table_offset] & 0x80) {
@@ -42,7 +49,8 @@
   // now search in sub-nodes
   if (count == 0)
     return 0;
-  for (i = 0; i < count; i++) {
+
+  for (int i = 0; i < count; i++) {
     int child_offset = ft_adobe_glyph_list[table_offset + i * 2] * 256 +
                        ft_adobe_glyph_list[table_offset + i * 2 + 1];
     if (xyq_search_node(glyph_name, name_offset, child_offset, unicode))
@@ -52,7 +60,35 @@
   return 0;
 }
 
-#define VARIANT_BIT 0x80000000UL
+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 */
@@ -70,9 +106,7 @@
 
     for (count = 4; count > 0; count--, p++) {
       char c = *p;
-      unsigned int d;
-
-      d = (unsigned char)c - '0';
+      unsigned int d = (unsigned char)c - '0';
       if (d >= 10) {
         d = (unsigned char)c - 'A';
         if (d >= 6)
@@ -95,7 +129,7 @@
       if (*p == '\0')
         return value;
       if (*p == '.')
-        return (FT_UInt32)(value | VARIANT_BIT);
+        return (FT_UInt32)(value | kVariantBit);
     }
   }
 
@@ -108,9 +142,7 @@
 
     for (count = 6; count > 0; count--, p++) {
       char c = *p;
-      unsigned int d;
-
-      d = (unsigned char)c - '0';
+      unsigned int d = (unsigned char)c - '0';
       if (d >= 10) {
         d = (unsigned char)c - 'A';
         if (d >= 6)
@@ -129,7 +161,7 @@
       if (*p == '\0')
         return value;
       if (*p == '.')
-        return (FT_UInt32)(value | VARIANT_BIT);
+        return (FT_UInt32)(value | kVariantBit);
     }
   }
 
@@ -149,18 +181,15 @@
     /* now look up the glyph in the Adobe Glyph List */
     if (!dot)
       return (FT_UInt32)ft_get_adobe_glyph_index(glyph_name, p);
-    else
-      return (FT_UInt32)(ft_get_adobe_glyph_index(glyph_name, dot) |
-                         VARIANT_BIT);
+
+    return (FT_UInt32)(ft_get_adobe_glyph_index(glyph_name, dot) | kVariantBit);
   }
 }
 
 void FXFT_adobe_name_from_unicode(char* glyph_name, wchar_t unicode) {
-  int i, count;
-
   // start from top level node
-  count = ft_adobe_glyph_list[1];
-  for (i = 0; i < count; i++) {
+  int count = ft_adobe_glyph_list[1];
+  for (int i = 0; i < count; i++) {
     int child_offset =
         ft_adobe_glyph_list[i * 2 + 2] * 256 + ft_adobe_glyph_list[i * 2 + 3];
     if (xyq_search_node(glyph_name, 0, child_offset, unicode))
diff --git a/core/fxge/freetype/fx_freetype.h b/core/fxge/freetype/fx_freetype.h
new file mode 100644
index 0000000..96c3d2f
--- /dev/null
+++ b/core/fxge/freetype/fx_freetype.h
@@ -0,0 +1,106 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_FREETYPE_FX_FREETYPE_H_
+#define CORE_FXGE_FREETYPE_FX_FREETYPE_H_
+
+#include <ft2build.h>
+
+#include <memory>
+
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+#include FT_LCD_FILTER_H
+#include FT_MULTIPLE_MASTERS_H
+#include FT_OUTLINE_H
+#include FT_TRUETYPE_TABLES_H
+
+using FXFT_LibraryRec = struct FT_LibraryRec_;
+using FXFT_FaceRec = struct FT_FaceRec_;
+using FXFT_StreamRec = struct FT_StreamRec_;
+
+struct FXFTFaceRecDeleter {
+  inline void operator()(FXFT_FaceRec* pRec) { FT_Done_Face(pRec); }
+};
+
+struct FXFTLibraryRecDeleter {
+  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) \
+  FT_Render_Glyph((face)->glyph, static_cast<enum FT_Render_Mode_>(mode))
+
+#define FXFT_Has_Glyph_Names(face) \
+  (((face)->face_flags) & FT_FACE_FLAG_GLYPH_NAMES)
+#define FXFT_Clear_Face_External_Stream(face) \
+  ((face)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM)
+#define FXFT_Get_Face_External_Stream(face) \
+  (((face)->face_flags) & FT_FACE_FLAG_EXTERNAL_STREAM)
+#define FXFT_Is_Face_TT_OT(face) (((face)->face_flags) & FT_FACE_FLAG_SFNT)
+#define FXFT_Is_Face_Tricky(face) (((face)->face_flags) & FT_FACE_FLAG_TRICKY)
+#define FXFT_Is_Face_fixedwidth(face) \
+  (((face)->face_flags) & FT_FACE_FLAG_FIXED_WIDTH)
+#define FXFT_Get_Face_Stream_Base(face) (face)->stream->base
+#define FXFT_Get_Face_Stream_Size(face) (face)->stream->size
+#define FXFT_Get_Face_Family_Name(face) (face)->family_name
+#define FXFT_Get_Face_Style_Name(face) (face)->style_name
+#define FXFT_Is_Face_Italic(face) (((face)->style_flags) & FT_STYLE_FLAG_ITALIC)
+#define FXFT_Is_Face_Bold(face) (((face)->style_flags) & FT_STYLE_FLAG_BOLD)
+#define FXFT_Get_Glyph_HoriBearingX(face) (face)->glyph->metrics.horiBearingX
+#define FXFT_Get_Glyph_HoriBearingY(face) (face)->glyph->metrics.horiBearingY
+#define FXFT_Get_Glyph_Width(face) (face)->glyph->metrics.width
+#define FXFT_Get_Glyph_Height(face) (face)->glyph->metrics.height
+#define FXFT_Get_Charmap_Encoding(charmap) (charmap)->encoding
+#define FXFT_Get_Charmap_PlatformID(charmap) (charmap)->platform_id
+#define FXFT_Get_Charmap_EncodingID(charmap) (charmap)->encoding_id
+#define FXFT_Get_Face_UnitsPerEM(face) (face)->units_per_EM
+#define FXFT_Get_Face_xMin(face) (face)->bbox.xMin
+#define FXFT_Get_Face_xMax(face) (face)->bbox.xMax
+#define FXFT_Get_Face_yMin(face) (face)->bbox.yMin
+#define FXFT_Get_Face_yMax(face) (face)->bbox.yMax
+#define FXFT_Get_Face_Height(face) (face)->height
+#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_Glyph_Outline(face) &((face)->glyph->outline)
+#define FXFT_Get_Glyph_Bitmap(face) (face)->glyph->bitmap
+#define FXFT_Get_Bitmap_Width(bitmap) (bitmap).width
+#define FXFT_Get_Bitmap_Rows(bitmap) (bitmap).rows
+#define FXFT_Get_Bitmap_PixelMode(bitmap) (bitmap).pixel_mode
+#define FXFT_Get_Bitmap_Pitch(bitmap) (bitmap).pitch
+#define FXFT_Get_Bitmap_Buffer(bitmap) (bitmap).buffer
+#define FXFT_Get_Glyph_BitmapLeft(face) (face)->glyph->bitmap_left
+#define FXFT_Get_Glyph_BitmapTop(face) (face)->glyph->bitmap_top
+
+int FXFT_unicode_from_adobe_name(const char* glyph_name);
+void FXFT_adobe_name_from_unicode(char* name, wchar_t unicode);
+
+#endif  // CORE_FXGE_FREETYPE_FX_FREETYPE_H_
diff --git a/core/fxge/fx_dib.h b/core/fxge/fx_dib.h
deleted file mode 100644
index b2dbe8a..0000000
--- a/core/fxge/fx_dib.h
+++ /dev/null
@@ -1,188 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXGE_FX_DIB_H_
-#define CORE_FXGE_FX_DIB_H_
-
-#include <tuple>
-#include <utility>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/widestring.h"
-
-enum FXDIB_Format {
-  FXDIB_Invalid = 0,
-  FXDIB_1bppRgb = 0x001,
-  FXDIB_8bppRgb = 0x008,
-  FXDIB_Rgb = 0x018,
-  FXDIB_Rgb32 = 0x020,
-  FXDIB_1bppMask = 0x101,
-  FXDIB_8bppMask = 0x108,
-  FXDIB_8bppRgba = 0x208,
-  FXDIB_Rgba = 0x218,
-  FXDIB_Argb = 0x220,
-  FXDIB_1bppCmyk = 0x401,
-  FXDIB_8bppCmyk = 0x408,
-  FXDIB_Cmyk = 0x420,
-  FXDIB_8bppCmyka = 0x608,
-  FXDIB_Cmyka = 0x620,
-};
-
-struct PixelWeight {
-  int m_SrcStart;
-  int m_SrcEnd;
-  int m_Weights[1];
-};
-
-using FX_ARGB = uint32_t;
-
-// FX_COLORREF, like win32 COLORREF, is BGR.
-using FX_COLORREF = uint32_t;
-
-using FX_CMYK = uint32_t;
-
-extern const int16_t SDP_Table[513];
-
-struct FXDIB_ResampleOptions {
-  FXDIB_ResampleOptions();
-
-  bool HasAnyOptions() const;
-
-  bool bInterpolateBilinear = false;
-  bool bInterpolateBicubic = false;
-  bool bHalftone = false;
-  bool bNoSmoothing = false;
-  bool bLossy = false;
-};
-
-// See PDF 1.7 spec, table 7.2 and 7.3. The enum values need to be in the same
-// order as listed in the spec.
-enum class BlendMode {
-  kNormal = 0,
-  kMultiply,
-  kScreen,
-  kOverlay,
-  kDarken,
-  kLighten,
-  kColorDodge,
-  kColorBurn,
-  kHardLight,
-  kSoftLight,
-  kDifference,
-  kExclusion,
-  kHue,
-  kSaturation,
-  kColor,
-  kLuminosity,
-  kLast = kLuminosity,
-};
-
-constexpr uint32_t FXSYS_BGR(uint8_t b, uint8_t g, uint8_t r) {
-  return (b << 16) | (g << 8) | r;
-}
-
-constexpr uint8_t FXSYS_GetRValue(uint32_t bgr) {
-  return bgr & 0xff;
-}
-
-constexpr uint8_t FXSYS_GetGValue(uint32_t bgr) {
-  return (bgr >> 8) & 0xff;
-}
-
-constexpr uint8_t FXSYS_GetBValue(uint32_t bgr) {
-  return (bgr >> 16) & 0xff;
-}
-
-constexpr unsigned int FXSYS_GetUnsignedAlpha(float alpha) {
-  return static_cast<unsigned int>(alpha * 255.f + 0.5f);
-}
-
-#define FXSYS_GetCValue(cmyk) ((uint8_t)((cmyk) >> 24) & 0xff)
-#define FXSYS_GetMValue(cmyk) ((uint8_t)((cmyk) >> 16) & 0xff)
-#define FXSYS_GetYValue(cmyk) ((uint8_t)((cmyk) >> 8) & 0xff)
-#define FXSYS_GetKValue(cmyk) ((uint8_t)(cmyk)&0xff)
-
-// Bits per pixel, not bytes.
-inline int GetBppFromFormat(FXDIB_Format format) {
-  return format & 0xff;
-}
-
-// AKA bytes per pixel, assuming 8-bits per component.
-inline int GetCompsFromFormat(FXDIB_Format format) {
-  return (format & 0xff) / 8;
-}
-
-inline uint32_t GetAlphaFlagFromFormat(FXDIB_Format format) {
-  return (format >> 8) & 0xff;
-}
-
-inline bool GetIsAlphaFromFormat(FXDIB_Format format) {
-  return format & 0x200;
-}
-
-inline bool GetIsCmykFromFormat(FXDIB_Format format) {
-  return format & 0x400;
-}
-
-inline FX_CMYK CmykEncode(int c, int m, int y, int k) {
-  return (c << 24) | (m << 16) | (y << 8) | k;
-}
-
-// Returns (a, r, g, b)
-std::tuple<int, int, int, int> ArgbDecode(FX_ARGB argb);
-
-// Returns (a, FX_COLORREF)
-std::pair<int, FX_COLORREF> ArgbToAlphaAndColorRef(FX_ARGB argb);
-
-// Returns FX_COLORREF.
-FX_COLORREF ArgbToColorRef(FX_ARGB argb);
-
-constexpr FX_ARGB ArgbEncode(int a, int r, int g, int b) {
-  return (a << 24) | (r << 16) | (g << 8) | b;
-}
-
-FX_ARGB AlphaAndColorRefToArgb(int a, FX_COLORREF colorref);
-
-FX_ARGB StringToFXARGB(WideStringView view);
-
-#define FXARGB_A(argb) ((uint8_t)((argb) >> 24))
-#define FXARGB_R(argb) ((uint8_t)((argb) >> 16))
-#define FXARGB_G(argb) ((uint8_t)((argb) >> 8))
-#define FXARGB_B(argb) ((uint8_t)(argb))
-#define FXARGB_MUL_ALPHA(argb, alpha) \
-  (((((argb) >> 24) * (alpha) / 255) << 24) | ((argb)&0xffffff))
-
-#define FXRGB2GRAY(r, g, b) (((b)*11 + (g)*59 + (r)*30) / 100)
-#define FXDIB_ALPHA_MERGE(backdrop, source, source_alpha) \
-  (((backdrop) * (255 - (source_alpha)) + (source) * (source_alpha)) / 255)
-#define FXARGB_GETDIB(p)                              \
-  ((((uint8_t*)(p))[0]) | (((uint8_t*)(p))[1] << 8) | \
-   (((uint8_t*)(p))[2] << 16) | (((uint8_t*)(p))[3] << 24))
-#define FXARGB_SETDIB(p, argb)                  \
-  ((uint8_t*)(p))[0] = (uint8_t)(argb),         \
-  ((uint8_t*)(p))[1] = (uint8_t)((argb) >> 8),  \
-  ((uint8_t*)(p))[2] = (uint8_t)((argb) >> 16), \
-  ((uint8_t*)(p))[3] = (uint8_t)((argb) >> 24)
-#define FXARGB_SETRGBORDERDIB(p, argb)          \
-  ((uint8_t*)(p))[3] = (uint8_t)(argb >> 24),   \
-  ((uint8_t*)(p))[0] = (uint8_t)((argb) >> 16), \
-  ((uint8_t*)(p))[1] = (uint8_t)((argb) >> 8),  \
-  ((uint8_t*)(p))[2] = (uint8_t)(argb)
-#define FXARGB_TODIB(argb) (argb)
-#define FXCMYK_TODIB(cmyk)                                    \
-  ((uint8_t)((cmyk) >> 24) | ((uint8_t)((cmyk) >> 16)) << 8 | \
-   ((uint8_t)((cmyk) >> 8)) << 16 | ((uint8_t)(cmyk) << 24))
-#define FXARGB_TOBGRORDERDIB(argb)                       \
-  ((uint8_t)(argb >> 16) | ((uint8_t)(argb >> 8)) << 8 | \
-   ((uint8_t)(argb)) << 16 | ((uint8_t)(argb >> 24) << 24))
-
-FX_RECT FXDIB_SwapClipBox(const FX_RECT& clip,
-                          int width,
-                          int height,
-                          bool bFlipX,
-                          bool bFlipY);
-
-#endif  // CORE_FXGE_FX_DIB_H_
diff --git a/core/fxge/fx_font.cpp b/core/fxge/fx_font.cpp
index fe2d6b1..a3bf344 100644
--- a/core/fxge/fx_font.cpp
+++ b/core/fxge/fx_font.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,17 @@
 #include <algorithm>
 
 #include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/widestring.h"
 #include "core/fxge/cfx_glyphbitmap.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/freetype/fx_freetype.h"
 #include "core/fxge/text_glyph_pos.h"
 
 namespace {
 
 // These numbers come from the OpenType name table specification.
-constexpr uint16_t kNamePlatformMac = 1;
 constexpr uint16_t kNameMacEncodingRoman = 0;
-constexpr uint16_t kNamePlatformWindows = 3;
 constexpr uint16_t kNameWindowsEncodingUnicode = 1;
 
 ByteString GetStringFromTable(pdfium::span<const uint8_t> string_span,
@@ -38,7 +39,7 @@
     if (!glyph.m_pGlyph)
       continue;
 
-    Optional<CFX_Point> point = glyph.GetOrigin({0, 0});
+    absl::optional<CFX_Point> point = glyph.GetOrigin({0, 0});
     if (!point.has_value())
       continue;
 
@@ -80,8 +81,8 @@
   if (name_table.size() < 6)
     return ByteString();
 
-  uint32_t name_count = GET_TT_SHORT(&name_table[2]);
-  uint32_t string_offset = GET_TT_SHORT(&name_table[4]);
+  uint32_t name_count = FXSYS_UINT16_GET_MSBFIRST(&name_table[2]);
+  uint32_t string_offset = FXSYS_UINT16_GET_MSBFIRST(&name_table[4]);
   // We will ignore the possibility of overlap of structures and
   // string table as if it's all corrupt there's not a lot we can do.
   if (name_table.size() < string_offset)
@@ -94,21 +95,24 @@
 
   for (uint32_t i = 0; i < name_count;
        i++, name_table = name_table.subspan(12)) {
-    if (GET_TT_SHORT(&name_table[6]) == name_id) {
-      const uint16_t platform_identifier = GET_TT_SHORT(name_table);
-      const uint16_t platform_encoding = GET_TT_SHORT(&name_table[2]);
+    if (FXSYS_UINT16_GET_MSBFIRST(&name_table[6]) == name_id) {
+      const uint16_t platform_identifier =
+          FXSYS_UINT16_GET_MSBFIRST(name_table);
+      const uint16_t platform_encoding =
+          FXSYS_UINT16_GET_MSBFIRST(&name_table[2]);
 
       if (platform_identifier == kNamePlatformMac &&
           platform_encoding == kNameMacEncodingRoman) {
-        return GetStringFromTable(string_span, GET_TT_SHORT(&name_table[10]),
-                                  GET_TT_SHORT(&name_table[8]));
+        return GetStringFromTable(string_span,
+                                  FXSYS_UINT16_GET_MSBFIRST(&name_table[10]),
+                                  FXSYS_UINT16_GET_MSBFIRST(&name_table[8]));
       }
       if (platform_identifier == kNamePlatformWindows &&
           platform_encoding == kNameWindowsEncodingUnicode) {
         // This name is always UTF16-BE and we have to convert it to UTF8.
-        ByteString utf16_be =
-            GetStringFromTable(string_span, GET_TT_SHORT(&name_table[10]),
-                               GET_TT_SHORT(&name_table[8]));
+        ByteString utf16_be = GetStringFromTable(
+            string_span, FXSYS_UINT16_GET_MSBFIRST(&name_table[10]),
+            FXSYS_UINT16_GET_MSBFIRST(&name_table[8]));
         if (utf16_be.IsEmpty() || utf16_be.GetLength() % 2 != 0) {
           return ByteString();
         }
@@ -124,23 +128,22 @@
   return ByteString();
 }
 
-int GetTTCIndex(pdfium::span<const uint8_t> pFontData, uint32_t font_offset) {
-  const uint8_t* p = pFontData.data() + 8;
-  uint32_t nfont = GET_TT_LONG(p);
-  uint32_t index;
-  for (index = 0; index < nfont; index++) {
-    p = pFontData.data() + 12 + index * 4;
-    if (GET_TT_LONG(p) == font_offset)
+size_t GetTTCIndex(pdfium::span<const uint8_t> pFontData, size_t font_offset) {
+  pdfium::span<const uint8_t> p = pFontData.subspan(8);
+  size_t nfont = FXSYS_UINT32_GET_MSBFIRST(p.data());
+  for (size_t index = 0; index < nfont; index++) {
+    p = pFontData.subspan(12 + index * 4);
+    if (FXSYS_UINT32_GET_MSBFIRST(p.data()) == font_offset)
       return index;
   }
   return 0;
 }
 
-wchar_t PDF_UnicodeFromAdobeName(const char* name) {
+wchar_t UnicodeFromAdobeName(const char* name) {
   return (wchar_t)(FXFT_unicode_from_adobe_name(name) & 0x7FFFFFFF);
 }
 
-ByteString PDF_AdobeNameFromUnicode(wchar_t unicode) {
+ByteString AdobeNameFromUnicode(wchar_t unicode) {
   char glyph_name[64];
   FXFT_adobe_name_from_unicode(glyph_name, unicode);
   return ByteString(glyph_name);
diff --git a/core/fxge/fx_font.h b/core/fxge/fx_font.h
index b748d55..7770f7f 100644
--- a/core/fxge/fx_font.h
+++ b/core/fxge/fx_font.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,11 +9,9 @@
 
 #include <vector>
 
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxge/fx_freetype.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 /* Font pitch and family flags */
 #define FXFONT_FF_FIXEDPITCH (1 << 0)
@@ -39,13 +37,13 @@
 
 /* Other font flags */
 #define FXFONT_USEEXTERNATTR 0x80000
-#define FXFONT_CIDFONT 0x100000
 
-#define GET_TT_SHORT(w) (uint16_t)(((w)[0] << 8) | (w)[1])
-#define GET_TT_LONG(w) \
-  (uint32_t)(((w)[0] << 24) | ((w)[1] << 16) | ((w)[2] << 8) | (w)[3])
+// These numbers come from the OpenType name table specification.
+constexpr uint16_t kNamePlatformAppleUnicode = 0;
+constexpr uint16_t kNamePlatformMac = 1;
+constexpr uint16_t kNamePlatformWindows = 3;
 
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
+#if defined(_SKIA_SUPPORT_)
 class SkTypeface;
 
 using CFX_TypeFace = SkTypeface;
@@ -56,7 +54,7 @@
 FX_RECT GetGlyphsBBox(const std::vector<TextGlyphPos>& glyphs, int anti_alias);
 
 ByteString GetNameFromTT(pdfium::span<const uint8_t> name_table, uint32_t name);
-int GetTTCIndex(pdfium::span<const uint8_t> pFontData, uint32_t font_offset);
+size_t GetTTCIndex(pdfium::span<const uint8_t> pFontData, size_t font_offset);
 
 inline bool FontStyleIsForceBold(uint32_t style) {
   return !!(style & FXFONT_FORCE_BOLD);
@@ -93,7 +91,7 @@
   return !!(family & FXFONT_FF_SCRIPT);
 }
 
-wchar_t PDF_UnicodeFromAdobeName(const char* name);
-ByteString PDF_AdobeNameFromUnicode(wchar_t unicode);
+wchar_t UnicodeFromAdobeName(const char* name);
+ByteString AdobeNameFromUnicode(wchar_t unicode);
 
 #endif  // CORE_FXGE_FX_FONT_H_
diff --git a/core/fxge/fx_font_unittest.cpp b/core/fxge/fx_font_unittest.cpp
index e550f0a..cc795d5 100644
--- a/core/fxge/fx_font_unittest.cpp
+++ b/core/fxge/fx_font_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,25 +6,36 @@
 
 #include "core/fxge/cfx_folderfontinfo.h"
 #include "core/fxge/cfx_fontmapper.h"
+#include "core/fxge/cfx_gemodule.h"
 #include "core/fxge/fx_font.h"
-
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/utils/path_service.h"
+#include "third_party/base/check.h"
 
-TEST(FXFontTest, PDF_AdobeNameFromUnicode) {
-  EXPECT_STREQ("", PDF_AdobeNameFromUnicode(0x0000).c_str());
-  EXPECT_STREQ("divide", PDF_AdobeNameFromUnicode(0x00f7).c_str());
-  EXPECT_STREQ("Lslash", PDF_AdobeNameFromUnicode(0x0141).c_str());
-  EXPECT_STREQ("tonos", PDF_AdobeNameFromUnicode(0x0384).c_str());
-  EXPECT_STREQ("afii57513", PDF_AdobeNameFromUnicode(0x0691).c_str());
-  EXPECT_STREQ("angkhankhuthai", PDF_AdobeNameFromUnicode(0x0e5a).c_str());
-  EXPECT_STREQ("Euro", PDF_AdobeNameFromUnicode(0x20ac).c_str());
+TEST(FXFontTest, UnicodeFromAdobeName) {
+  EXPECT_EQ(static_cast<wchar_t>(0x0000), UnicodeFromAdobeName("nonesuch"));
+  EXPECT_EQ(static_cast<wchar_t>(0x0000), UnicodeFromAdobeName(""));
+  EXPECT_EQ(static_cast<wchar_t>(0x00b6), UnicodeFromAdobeName("paragraph"));
+  EXPECT_EQ(static_cast<wchar_t>(0x00d3), UnicodeFromAdobeName("Oacute"));
+  EXPECT_EQ(static_cast<wchar_t>(0x00fe), UnicodeFromAdobeName("thorn"));
+  EXPECT_EQ(static_cast<wchar_t>(0x0384), UnicodeFromAdobeName("tonos"));
+  EXPECT_EQ(static_cast<wchar_t>(0x2022), UnicodeFromAdobeName("bullet"));
+}
+
+TEST(FXFontTest, AdobeNameFromUnicode) {
+  EXPECT_STREQ("", AdobeNameFromUnicode(0x0000).c_str());
+  EXPECT_STREQ("divide", AdobeNameFromUnicode(0x00f7).c_str());
+  EXPECT_STREQ("Lslash", AdobeNameFromUnicode(0x0141).c_str());
+  EXPECT_STREQ("tonos", AdobeNameFromUnicode(0x0384).c_str());
+  EXPECT_STREQ("afii57513", AdobeNameFromUnicode(0x0691).c_str());
+  EXPECT_STREQ("angkhankhuthai", AdobeNameFromUnicode(0x0e5a).c_str());
+  EXPECT_STREQ("Euro", AdobeNameFromUnicode(0x20ac).c_str());
 }
 
 TEST(FXFontTest, ReadFontNameFromMicrosoftEntries) {
   std::string test_data_dir;
   PathService::GetTestDataDir(&test_data_dir);
-  ASSERT(!test_data_dir.empty());
+  DCHECK(!test_data_dir.empty());
 
   CFX_FontMapper font_mapper(nullptr);
 
@@ -35,10 +46,11 @@
     folder_font_info.AddPath(
         (test_data_dir + PATH_SEPARATOR + "font_tests").c_str());
 
-    font_mapper.SetSystemFontInfo(SystemFontInfoIface::CreateDefault(nullptr));
+    font_mapper.SetSystemFontInfo(
+        CFX_GEModule::Get()->GetPlatform()->CreateDefaultSystemFontInfo());
     ASSERT_TRUE(folder_font_info.EnumFontList(&font_mapper));
   }
 
-  ASSERT_EQ(1, font_mapper.GetFaceSize());
+  ASSERT_EQ(1u, font_mapper.GetFaceSize());
   ASSERT_EQ("Test", font_mapper.GetFaceName(0));
 }
diff --git a/core/fxge/fx_freetype.h b/core/fxge/fx_freetype.h
deleted file mode 100644
index 2dd379a..0000000
--- a/core/fxge/fx_freetype.h
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXGE_FX_FREETYPE_H_
-#define CORE_FXGE_FX_FREETYPE_H_
-
-#include <ft2build.h>
-
-#include <memory>
-
-#include FT_FREETYPE_H
-#include FT_GLYPH_H
-#include FT_LCD_FILTER_H
-#include FT_MULTIPLE_MASTERS_H
-#include FT_OUTLINE_H
-#include FT_TRUETYPE_TABLES_H
-
-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) {
-    if (pRec)
-      FT_Done_Face(pRec);
-  }
-};
-
-struct FXFTLibraryRecDeleter {
-  inline void operator()(FXFT_LibraryRec* pRec) {
-    if (pRec)
-      FT_Done_FreeType(pRec);
-  }
-};
-
-using ScopedFXFTFaceRec = std::unique_ptr<FXFT_FaceRec, FXFTFaceRecDeleter>;
-using ScopedFXFTLibraryRec =
-    std::unique_ptr<FXFT_LibraryRec, FXFTLibraryRecDeleter>;
-
-#define FXFT_Select_Charmap(face, encoding) \
-  FT_Select_Charmap(face, static_cast<FT_Encoding>(encoding))
-#define FXFT_Get_Name_Index(face, name) \
-  FT_Get_Name_Index(face, const_cast<char*>(name))
-#define FXFT_Get_Glyph_Outline(face) &((face)->glyph->outline)
-#define FXFT_Render_Glyph(face, mode) \
-  FT_Render_Glyph((face)->glyph, static_cast<enum FT_Render_Mode_>(mode))
-
-#define FXFT_Has_Glyph_Names(face) \
-  (((face)->face_flags) & FT_FACE_FLAG_GLYPH_NAMES)
-#define FXFT_Clear_Face_External_Stream(face) \
-  ((face)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM)
-#define FXFT_Get_Face_External_Stream(face) \
-  (((face)->face_flags) & FT_FACE_FLAG_EXTERNAL_STREAM)
-#define FXFT_Is_Face_TT_OT(face) (((face)->face_flags) & FT_FACE_FLAG_SFNT)
-#define FXFT_Is_Face_Tricky(face) (((face)->face_flags) & FT_FACE_FLAG_TRICKY)
-#define FXFT_Is_Face_fixedwidth(face) \
-  (((face)->face_flags) & FT_FACE_FLAG_FIXED_WIDTH)
-#define FXFT_Get_Face_Stream_Base(face) (face)->stream->base
-#define FXFT_Get_Face_Stream_Size(face) (face)->stream->size
-#define FXFT_Get_Face_Family_Name(face) (face)->family_name
-#define FXFT_Get_Face_Style_Name(face) (face)->style_name
-#define FXFT_Is_Face_Italic(face) (((face)->style_flags) & FT_STYLE_FLAG_ITALIC)
-#define FXFT_Is_Face_Bold(face) (((face)->style_flags) & FT_STYLE_FLAG_BOLD)
-#define FXFT_Get_Face_Charmaps(face) (face)->charmaps
-#define FXFT_Get_Glyph_HoriBearingX(face) (face)->glyph->metrics.horiBearingX
-#define FXFT_Get_Glyph_HoriBearingY(face) (face)->glyph->metrics.horiBearingY
-#define FXFT_Get_Glyph_Width(face) (face)->glyph->metrics.width
-#define FXFT_Get_Glyph_Height(face) (face)->glyph->metrics.height
-#define FXFT_Get_Face_CharmapCount(face) (face)->num_charmaps
-#define FXFT_Get_Charmap_Encoding(charmap) (charmap)->encoding
-#define FXFT_Get_Face_Charmap(face) (face)->charmap
-#define FXFT_Get_Charmap_PlatformID(charmap) (charmap)->platform_id
-#define FXFT_Get_Charmap_EncodingID(charmap) (charmap)->encoding_id
-#define FXFT_Get_Face_UnitsPerEM(face) (face)->units_per_EM
-#define FXFT_Get_Face_xMin(face) (face)->bbox.xMin
-#define FXFT_Get_Face_xMax(face) (face)->bbox.xMax
-#define FXFT_Get_Face_yMin(face) (face)->bbox.yMin
-#define FXFT_Get_Face_yMax(face) (face)->bbox.yMax
-#define FXFT_Get_Face_Height(face) (face)->height
-#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
-#define FXFT_Get_Bitmap_Rows(bitmap) (bitmap).rows
-#define FXFT_Get_Bitmap_PixelMode(bitmap) (bitmap).pixel_mode
-#define FXFT_Get_Bitmap_Pitch(bitmap) (bitmap).pitch
-#define FXFT_Get_Bitmap_Buffer(bitmap) (bitmap).buffer
-#define FXFT_Get_Glyph_BitmapLeft(face) (face)->glyph->bitmap_left
-#define FXFT_Get_Glyph_BitmapTop(face) (face)->glyph->bitmap_top
-
-int FXFT_unicode_from_adobe_name(const char* glyph_name);
-void FXFT_adobe_name_from_unicode(char* name, wchar_t unicode);
-
-#endif  // CORE_FXGE_FX_FREETYPE_H_
diff --git a/core/fxge/fx_ge_fontmap.cpp b/core/fxge/fx_ge_fontmap.cpp
deleted file mode 100644
index c99d08d..0000000
--- a/core/fxge/fx_ge_fontmap.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include <memory>
-
-#include "build/build_config.h"
-#include "core/fxge/systemfontinfo_iface.h"
-
-int SystemFontInfoIface::GetFaceIndex(void* hFont) {
-  return 0;
-}
-
-#if defined(OS_ANDROID)
-std::unique_ptr<SystemFontInfoIface> SystemFontInfoIface::CreateDefault(
-    const char** pUnused) {
-  return nullptr;
-}
-#endif
diff --git a/core/fxge/fx_ge_linux.cpp b/core/fxge/fx_ge_linux.cpp
deleted file mode 100644
index d76d39d..0000000
--- a/core/fxge/fx_ge_linux.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include <memory>
-#include <utility>
-
-#include "core/fxcrt/fx_codepage.h"
-#include "core/fxge/cfx_folderfontinfo.h"
-#include "core/fxge/cfx_fontmgr.h"
-#include "core/fxge/cfx_gemodule.h"
-#include "core/fxge/fx_font.h"
-#include "core/fxge/systemfontinfo_iface.h"
-#include "third_party/base/ptr_util.h"
-
-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
-namespace {
-
-enum JpFontFamily : uint8_t {
-  kJpFontPGothic,
-  kJpFontGothic,
-  kJpFontPMincho,
-  kJpFontMincho,
-  kCount
-};
-
-const char* const g_LinuxJpFontList[][JpFontFamily::kCount] = {
-    {"TakaoPGothic", "VL PGothic", "IPAPGothic", "VL Gothic"},
-    {"TakaoGothic", "VL Gothic", "IPAGothic", "Kochi Gothic"},
-    {"TakaoPMincho", "IPAPMincho", "VL Gothic", "Kochi Mincho"},
-    {"TakaoMincho", "IPAMincho", "VL Gothic", "Kochi Mincho"},
-};
-
-const char* const g_LinuxGbFontList[] = {
-    "AR PL UMing CN Light",
-    "WenQuanYi Micro Hei",
-    "AR PL UKai CN",
-};
-
-const char* const g_LinuxB5FontList[] = {
-    "AR PL UMing TW Light",
-    "WenQuanYi Micro Hei",
-    "AR PL UKai TW",
-};
-
-const char* const g_LinuxHGFontList[] = {
-    "UnDotum",
-};
-
-uint8_t GetJapanesePreference(const char* facearr,
-                              int weight,
-                              int pitch_family) {
-  ByteString face = facearr;
-  if (face.Contains("Gothic") ||
-      face.Contains("\x83\x53\x83\x56\x83\x62\x83\x4e")) {
-    if (face.Contains("PGothic") ||
-        face.Contains("\x82\x6f\x83\x53\x83\x56\x83\x62\x83\x4e")) {
-      return kJpFontPGothic;
-    }
-    return kJpFontGothic;
-  }
-  if (face.Contains("Mincho") || face.Contains("\x96\xbe\x92\xa9")) {
-    if (face.Contains("PMincho") || face.Contains("\x82\x6f\x96\xbe\x92\xa9")) {
-      return kJpFontPMincho;
-    }
-    return kJpFontMincho;
-  }
-  if (!FontFamilyIsRoman(pitch_family) && weight > 400)
-    return kJpFontPGothic;
-
-  return kJpFontPMincho;
-}
-
-class CFX_LinuxFontInfo final : public CFX_FolderFontInfo {
- public:
-  CFX_LinuxFontInfo() = default;
-  ~CFX_LinuxFontInfo() override = default;
-
-  // CFX_LinuxFontInfo:
-  void* MapFont(int weight,
-                bool bItalic,
-                int charset,
-                int pitch_family,
-                const char* family) override;
-
-  bool ParseFontCfg(const char** pUserPaths);
-};
-
-void* CFX_LinuxFontInfo::MapFont(int weight,
-                                 bool bItalic,
-                                 int charset,
-                                 int pitch_family,
-                                 const char* family) {
-  void* font = GetSubstFont(family);
-  if (font)
-    return font;
-
-  bool bCJK = true;
-  switch (charset) {
-    case FX_CHARSET_ShiftJIS: {
-      uint8_t index = GetJapanesePreference(family, weight, pitch_family);
-      ASSERT(index < FX_ArraySize(g_LinuxJpFontList));
-      for (const char* name : g_LinuxJpFontList[index]) {
-        auto it = m_FontList.find(name);
-        if (it != m_FontList.end())
-          return it->second.get();
-      }
-      break;
-    }
-    case FX_CHARSET_ChineseSimplified: {
-      for (const char* name : g_LinuxGbFontList) {
-        auto it = m_FontList.find(name);
-        if (it != m_FontList.end())
-          return it->second.get();
-      }
-      break;
-    }
-    case FX_CHARSET_ChineseTraditional: {
-      for (const char* name : g_LinuxB5FontList) {
-        auto it = m_FontList.find(name);
-        if (it != m_FontList.end())
-          return it->second.get();
-      }
-      break;
-    }
-    case FX_CHARSET_Hangul: {
-      for (const char* name : g_LinuxHGFontList) {
-        auto it = m_FontList.find(name);
-        if (it != m_FontList.end())
-          return it->second.get();
-      }
-      break;
-    }
-    default:
-      bCJK = false;
-      break;
-  }
-  return FindFont(weight, bItalic, charset, pitch_family, family, !bCJK);
-}
-
-bool CFX_LinuxFontInfo::ParseFontCfg(const char** pUserPaths) {
-  if (!pUserPaths)
-    return false;
-
-  for (const char** pPath = pUserPaths; *pPath; ++pPath)
-    AddPath(*pPath);
-  return true;
-}
-
-}  // namespace
-
-std::unique_ptr<SystemFontInfoIface> SystemFontInfoIface::CreateDefault(
-    const char** pUserPaths) {
-  auto pInfo = pdfium::MakeUnique<CFX_LinuxFontInfo>();
-  if (!pInfo->ParseFontCfg(pUserPaths)) {
-    pInfo->AddPath("/usr/share/fonts");
-    pInfo->AddPath("/usr/share/X11/fonts/Type1");
-    pInfo->AddPath("/usr/share/X11/fonts/TTF");
-    pInfo->AddPath("/usr/local/share/fonts");
-  }
-  return std::move(pInfo);
-}
-
-class CLinuxPlatform : public CFX_GEModule::PlatformIface {
- public:
-  CLinuxPlatform() = default;
-  ~CLinuxPlatform() override = default;
-
-  void Init() override {
-    CFX_GEModule* pModule = CFX_GEModule::Get();
-    pModule->GetFontMgr()->SetSystemFontInfo(
-        SystemFontInfoIface::CreateDefault(pModule->GetUserFontPaths()));
-  }
-};
-
-// static
-std::unique_ptr<CFX_GEModule::PlatformIface>
-CFX_GEModule::PlatformIface::Create() {
-  return pdfium::MakeUnique<CLinuxPlatform>();
-}
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
diff --git a/core/fxge/fx_ge_text_embeddertest.cpp b/core/fxge/fx_ge_text_embeddertest.cpp
index e47ed5f..9a79620 100644
--- a/core/fxge/fx_ge_text_embeddertest.cpp
+++ b/core/fxge/fx_ge_text_embeddertest.cpp
@@ -1,18 +1,28 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
-
 #include "public/cpp/fpdf_scopers.h"
 #include "testing/embedder_test.h"
+#include "testing/embedder_test_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-class FXGETextEmbedderTest : public EmbedderTest {};
+class FXGETextEmbedderTest : public EmbedderTest {
+ public:
+  void TearDown() override {
+    EmbedderTest::TearDown();
+
+    // TODO(tsepez): determine how this is changing the environment,
+    // such that FPDFAnnotEmbedderTest.BUG_1206 will diff if run
+    // after this.
+    EmbedderTestEnvironment::GetInstance()->TearDown();
+    EmbedderTestEnvironment::GetInstance()->SetUp();
+  }
+};
 
 TEST_F(FXGETextEmbedderTest, BadItalic) {
   // Shouldn't crash.
-  EXPECT_TRUE(OpenDocument("bug_601362.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_601362.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
diff --git a/core/fxge/linux/fx_linux_impl.cpp b/core/fxge/linux/fx_linux_impl.cpp
new file mode 100644
index 0000000..c24043c
--- /dev/null
+++ b/core/fxge/linux/fx_linux_impl.cpp
@@ -0,0 +1,182 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include <iterator>
+#include <memory>
+#include <utility>
+
+#include "build/build_config.h"
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxge/cfx_folderfontinfo.h"
+#include "core/fxge/cfx_fontmgr.h"
+#include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/fx_font.h"
+#include "core/fxge/systemfontinfo_iface.h"
+#include "third_party/base/check.h"
+
+#if !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS) && !defined(OS_FUCHSIA) && \
+    !defined(OS_ASMJS)
+#error "Included on the wrong platform"
+#endif
+
+namespace {
+
+enum JpFontFamily : uint8_t {
+  kJpFontPGothic,
+  kJpFontGothic,
+  kJpFontPMincho,
+  kJpFontMincho,
+  kCount
+};
+
+const char* const kLinuxJpFontList[][JpFontFamily::kCount] = {
+    {"TakaoPGothic", "VL PGothic", "IPAPGothic", "VL Gothic"},
+    {"TakaoGothic", "VL Gothic", "IPAGothic", "Kochi Gothic"},
+    {"TakaoPMincho", "IPAPMincho", "VL Gothic", "Kochi Mincho"},
+    {"TakaoMincho", "IPAMincho", "VL Gothic", "Kochi Mincho"},
+};
+
+const char* const kLinuxGbFontList[] = {
+    "AR PL UMing CN Light",
+    "WenQuanYi Micro Hei",
+    "AR PL UKai CN",
+};
+
+const char* const kLinuxB5FontList[] = {
+    "AR PL UMing TW Light",
+    "WenQuanYi Micro Hei",
+    "AR PL UKai TW",
+};
+
+const char* const kLinuxHGFontList[] = {
+    "UnDotum",
+};
+
+JpFontFamily GetJapanesePreference(const ByteString& face,
+                                   int weight,
+                                   int pitch_family) {
+  if (face.Contains("Gothic") ||
+      face.Contains("\x83\x53\x83\x56\x83\x62\x83\x4e")) {
+    if (face.Contains("PGothic") ||
+        face.Contains("\x82\x6f\x83\x53\x83\x56\x83\x62\x83\x4e")) {
+      return kJpFontPGothic;
+    }
+    return kJpFontGothic;
+  }
+  if (face.Contains("Mincho") || face.Contains("\x96\xbe\x92\xa9")) {
+    if (face.Contains("PMincho") || face.Contains("\x82\x6f\x96\xbe\x92\xa9")) {
+      return kJpFontPMincho;
+    }
+    return kJpFontMincho;
+  }
+  if (!FontFamilyIsRoman(pitch_family) && weight > 400)
+    return kJpFontPGothic;
+
+  return kJpFontPMincho;
+}
+
+class CFX_LinuxFontInfo final : public CFX_FolderFontInfo {
+ public:
+  CFX_LinuxFontInfo() = default;
+  ~CFX_LinuxFontInfo() override = default;
+
+  // CFX_FolderFontInfo:
+  void* MapFont(int weight,
+                bool bItalic,
+                FX_Charset charset,
+                int pitch_family,
+                const ByteString& face) override;
+
+  bool ParseFontCfg(const char** pUserPaths);
+};
+
+void* CFX_LinuxFontInfo::MapFont(int weight,
+                                 bool bItalic,
+                                 FX_Charset charset,
+                                 int pitch_family,
+                                 const ByteString& face) {
+  void* font = GetSubstFont(face);
+  if (font)
+    return font;
+
+  bool bCJK = true;
+  switch (charset) {
+    case FX_Charset::kShiftJIS: {
+      JpFontFamily index = GetJapanesePreference(face, weight, pitch_family);
+      DCHECK(index < std::size(kLinuxJpFontList));
+      for (const char* name : kLinuxJpFontList[index]) {
+        auto it = m_FontList.find(name);
+        if (it != m_FontList.end())
+          return it->second.get();
+      }
+      break;
+    }
+    case FX_Charset::kChineseSimplified: {
+      for (const char* name : kLinuxGbFontList) {
+        auto it = m_FontList.find(name);
+        if (it != m_FontList.end())
+          return it->second.get();
+      }
+      break;
+    }
+    case FX_Charset::kChineseTraditional: {
+      for (const char* name : kLinuxB5FontList) {
+        auto it = m_FontList.find(name);
+        if (it != m_FontList.end())
+          return it->second.get();
+      }
+      break;
+    }
+    case FX_Charset::kHangul: {
+      for (const char* name : kLinuxHGFontList) {
+        auto it = m_FontList.find(name);
+        if (it != m_FontList.end())
+          return it->second.get();
+      }
+      break;
+    }
+    default:
+      bCJK = false;
+      break;
+  }
+  return FindFont(weight, bItalic, charset, pitch_family, face, !bCJK);
+}
+
+bool CFX_LinuxFontInfo::ParseFontCfg(const char** pUserPaths) {
+  if (!pUserPaths)
+    return false;
+
+  for (const char** pPath = pUserPaths; *pPath; ++pPath)
+    AddPath(*pPath);
+  return true;
+}
+
+}  // namespace
+
+class CLinuxPlatform : public CFX_GEModule::PlatformIface {
+ public:
+  CLinuxPlatform() = default;
+  ~CLinuxPlatform() override = default;
+
+  void Init() override {}
+
+  std::unique_ptr<SystemFontInfoIface> CreateDefaultSystemFontInfo() override {
+    auto pInfo = std::make_unique<CFX_LinuxFontInfo>();
+    if (!pInfo->ParseFontCfg(CFX_GEModule::Get()->GetUserFontPaths())) {
+      pInfo->AddPath("/usr/share/fonts");
+      pInfo->AddPath("/usr/share/X11/fonts/Type1");
+      pInfo->AddPath("/usr/share/X11/fonts/TTF");
+      pInfo->AddPath("/usr/local/share/fonts");
+    }
+    return pInfo;
+  }
+};
+
+// static
+std::unique_ptr<CFX_GEModule::PlatformIface>
+CFX_GEModule::PlatformIface::Create() {
+  return std::make_unique<CLinuxPlatform>();
+}
diff --git a/core/fxge/render_defines.h b/core/fxge/render_defines.h
index ff52b4e..0507e78 100644
--- a/core/fxge/render_defines.h
+++ b/core/fxge/render_defines.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -21,29 +21,9 @@
 #define FXRC_ALPHA_OUTPUT 0x40
 #define FXRC_BLEND_MODE 0x80
 #define FXRC_SOFT_CLIP 0x100
-#define FXRC_CMYK_OUTPUT 0x200
 #define FXRC_BITMASK_OUTPUT 0x400
 #define FXRC_BYTEMASK_OUTPUT 0x800
-#define FXRENDER_IMAGE_LOSSY 0x1000
 #define FXRC_FILLSTROKE_PATH 0x2000
 #define FXRC_SHADING 0x4000
 
-#define FXFILL_ALTERNATE 1
-#define FXFILL_WINDING 2
-#define FXFILL_FULLCOVER 4
-#define FXFILL_RECT_AA 8
-#define FX_FILL_STROKE 16
-#define FX_STROKE_ADJUST 32
-#define FX_STROKE_TEXT_MODE 64
-#define FX_FILL_TEXT_MODE 128
-#define FX_ZEROAREA_FILL 256
-#define FXFILL_NOPATHSMOOTH 512
-
-#define FXTEXT_CLEARTYPE 0x01
-#define FXTEXT_BGR_STRIPE 0x02
-#define FXTEXT_PRINTGRAPHICTEXT 0x04
-#define FXTEXT_NO_NATIVETEXT 0x08
-#define FXTEXT_PRINTIMAGETEXT 0x10
-#define FXTEXT_NOSMOOTH 0x20
-
 #endif  // CORE_FXGE_RENDER_DEFINES_H_
diff --git a/core/fxge/renderdevicedriver_iface.cpp b/core/fxge/renderdevicedriver_iface.cpp
index f8fcccf..b549ea2 100644
--- a/core/fxge/renderdevicedriver_iface.cpp
+++ b/core/fxge/renderdevicedriver_iface.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,13 @@
 #include "core/fxge/renderdevicedriver_iface.h"
 
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 
-RenderDeviceDriverIface::~RenderDeviceDriverIface() {}
-
-bool RenderDeviceDriverIface::StartRendering() {
-  return true;
-}
-
-void RenderDeviceDriverIface::EndRendering() {}
+RenderDeviceDriverIface::~RenderDeviceDriverIface() = default;
 
 bool RenderDeviceDriverIface::SetClip_PathStroke(
-    const CFX_PathData* pPathData,
+    const CFX_Path& path,
     const CFX_Matrix* pObject2Device,
     const CFX_GraphStateData* pGraphState) {
   return false;
@@ -27,10 +21,6 @@
 
 void RenderDeviceDriverIface::SetBaseClip(const FX_RECT& rect) {}
 
-bool RenderDeviceDriverIface::SetPixel(int x, int y, uint32_t color) {
-  return false;
-}
-
 bool RenderDeviceDriverIface::FillRectWithBlend(const FX_RECT& rect,
                                                 uint32_t fill_color,
                                                 BlendMode blend_type) {
@@ -59,12 +49,13 @@
   return false;
 }
 
-bool RenderDeviceDriverIface::DrawDeviceText(int nChars,
-                                             const TextCharPos* pCharPos,
-                                             CFX_Font* pFont,
-                                             const CFX_Matrix& mtObject2Device,
-                                             float font_size,
-                                             uint32_t color) {
+bool RenderDeviceDriverIface::DrawDeviceText(
+    pdfium::span<const TextCharPos> pCharPos,
+    CFX_Font* pFont,
+    const CFX_Matrix& mtObject2Device,
+    float font_size,
+    uint32_t color,
+    const CFX_TextRenderOptions& options) {
   return false;
 }
 
@@ -72,8 +63,6 @@
   return 0;
 }
 
-void RenderDeviceDriverIface::ClearDriver() {}
-
 bool RenderDeviceDriverIface::DrawShading(const CPDF_ShadingPattern* pPattern,
                                           const CFX_Matrix* pMatrix,
                                           const FX_RECT& clip_rect,
@@ -82,6 +71,7 @@
   return false;
 }
 
+#if defined(_SKIA_SUPPORT_)
 bool RenderDeviceDriverIface::SetBitsWithMask(
     const RetainPtr<CFX_DIBBase>& pBitmap,
     const RetainPtr<CFX_DIBBase>& pMask,
@@ -92,6 +82,5 @@
   return false;
 }
 
-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
-void RenderDeviceDriverIface::Flush() {}
+void RenderDeviceDriverIface::SetGroupKnockout(bool group_knockout) {}
 #endif
diff --git a/core/fxge/renderdevicedriver_iface.h b/core/fxge/renderdevicedriver_iface.h
index 69e404b..09dd93c 100644
--- a/core/fxge/renderdevicedriver_iface.h
+++ b/core/fxge/renderdevicedriver_iface.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,14 @@
 #ifndef CORE_FXGE_RENDERDEVICEDRIVER_IFACE_H_
 #define CORE_FXGE_RENDERDEVICEDRIVER_IFACE_H_
 
+#include <stdint.h>
+
 #include <memory>
 
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_DIBBase;
 class CFX_DIBitmap;
@@ -20,14 +22,15 @@
 class CFX_GraphStateData;
 class CFX_ImageRenderer;
 class CFX_Matrix;
-class CFX_PathData;
+class CFX_Path;
 class CPDF_ShadingPattern;
 class PauseIndicatorIface;
 class TextCharPos;
+struct CFX_FillRenderOptions;
+struct CFX_TextRenderOptions;
 struct FX_RECT;
 
-enum class DeviceType : uint8_t {
-  kUnknown,
+enum class DeviceType : bool {
   kDisplay,
   kPrinter,
 };
@@ -39,26 +42,23 @@
   virtual DeviceType GetDeviceType() const = 0;
   virtual int GetDeviceCaps(int caps_id) const = 0;
 
-  virtual bool StartRendering();
-  virtual void EndRendering();
   virtual void SaveState() = 0;
   virtual void RestoreState(bool bKeepSaved) = 0;
 
   virtual void SetBaseClip(const FX_RECT& rect);
-  virtual bool SetClip_PathFill(const CFX_PathData* pPathData,
+  virtual bool SetClip_PathFill(const CFX_Path& path,
                                 const CFX_Matrix* pObject2Device,
-                                int fill_mode) = 0;
-  virtual bool SetClip_PathStroke(const CFX_PathData* pPathData,
+                                const CFX_FillRenderOptions& fill_options) = 0;
+  virtual bool SetClip_PathStroke(const CFX_Path& path,
                                   const CFX_Matrix* pObject2Device,
                                   const CFX_GraphStateData* pGraphState);
-  virtual bool DrawPath(const CFX_PathData* pPathData,
+  virtual bool DrawPath(const CFX_Path& path,
                         const CFX_Matrix* pObject2Device,
                         const CFX_GraphStateData* pGraphState,
                         uint32_t fill_color,
                         uint32_t stroke_color,
-                        int fill_mode,
+                        const CFX_FillRenderOptions& fill_options,
                         BlendMode blend_type) = 0;
-  virtual bool SetPixel(int x, int y, uint32_t color);
   virtual bool FillRectWithBlend(const FX_RECT& rect,
                                  uint32_t fill_color,
                                  BlendMode blend_type);
@@ -96,28 +96,33 @@
                            BlendMode blend_type) = 0;
   virtual bool ContinueDIBits(CFX_ImageRenderer* handle,
                               PauseIndicatorIface* pPause);
-  virtual bool DrawDeviceText(int nChars,
-                              const TextCharPos* pCharPos,
+  virtual bool DrawDeviceText(pdfium::span<const TextCharPos> pCharPos,
                               CFX_Font* pFont,
                               const CFX_Matrix& mtObject2Device,
                               float font_size,
-                              uint32_t color);
+                              uint32_t color,
+                              const CFX_TextRenderOptions& options);
   virtual int GetDriverType() const;
-  virtual void ClearDriver();
   virtual bool DrawShading(const CPDF_ShadingPattern* pPattern,
                            const CFX_Matrix* pMatrix,
                            const FX_RECT& clip_rect,
                            int alpha,
                            bool bAlphaMode);
+#if defined(_SKIA_SUPPORT_)
   virtual bool SetBitsWithMask(const RetainPtr<CFX_DIBBase>& pBitmap,
                                const RetainPtr<CFX_DIBBase>& pMask,
                                int left,
                                int top,
                                int bitmap_alpha,
                                BlendMode blend_type);
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
-  virtual void Flush();
+  virtual void SetGroupKnockout(bool group_knockout);
 #endif
+
+  // Multiplies the device by a constant alpha, returning `true` on success.
+  virtual bool MultiplyAlpha(float alpha) = 0;
+
+  // Multiplies the device by an alpha mask, returning `true` on success.
+  virtual bool MultiplyAlpha(const RetainPtr<CFX_DIBBase>& mask) = 0;
 };
 
 #endif  // CORE_FXGE_RENDERDEVICEDRIVER_IFACE_H_
diff --git a/core/fxge/scoped_font_transform.cpp b/core/fxge/scoped_font_transform.cpp
index 2bd5396..76626b3 100644
--- a/core/fxge/scoped_font_transform.cpp
+++ b/core/fxge/scoped_font_transform.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fxge/scoped_font_transform.h b/core/fxge/scoped_font_transform.h
index bcbbb50..7530e17 100644
--- a/core/fxge/scoped_font_transform.h
+++ b/core/fxge/scoped_font_transform.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,17 @@
 #ifndef CORE_FXGE_SCOPED_FONT_TRANSFORM_H_
 #define CORE_FXGE_SCOPED_FONT_TRANSFORM_H_
 
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/cfx_face.h"
-#include "core/fxge/fx_freetype.h"
+#include "core/fxge/freetype/fx_freetype.h"
 
 // Sets the given transform on the font, and resets it to the identity when it
 // goes out of scope.
 class ScopedFontTransform {
  public:
+  FX_STACK_ALLOCATED();
+
   ScopedFontTransform(RetainPtr<CFX_Face> face, FT_Matrix* matrix);
   ~ScopedFontTransform();
 
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 f29022f..6bff316 100644
--- a/core/fxge/skia/fx_skia_device.cpp
+++ b/core/fxge/skia/fx_skia_device.cpp
@@ -1,10 +1,16 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxge/skia/fx_skia_device.h"
 
+#include <math.h>
+#include <stddef.h>
+#include <stdint.h>
+
 #include <algorithm>
+#include <limits>
+#include <memory>
 #include <utility>
 #include <vector>
 
@@ -20,155 +26,65 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fxcrt/cfx_bitstream.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_2d_size.h"
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/stl_util.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
-#include "core/fxge/dib/cfx_bitmapcomposer.h"
+#include "core/fxge/cfx_substfont.h"
+#include "core/fxge/cfx_textrenderoptions.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/dib/cfx_imagerenderer.h"
-#include "core/fxge/dib/cfx_imagestretcher.h"
+#include "core/fxge/dib/cstretchengine.h"
+#include "core/fxge/dib/fx_dib.h"
 #include "core/fxge/text_char_pos.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.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/skia/include/core/SkBlendMode.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkClipOp.h"
+#include "third_party/skia/include/core/SkColorFilter.h"
 #include "third_party/skia/include/core/SkColorPriv.h"
+#include "third_party/skia/include/core/SkColorType.h"
 #include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkMaskFilter.h"
 #include "third_party/skia/include/core/SkPaint.h"
 #include "third_party/skia/include/core/SkPath.h"
+#include "third_party/skia/include/core/SkPathEffect.h"
+#include "third_party/skia/include/core/SkPathUtils.h"
+#include "third_party/skia/include/core/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"
 #include "third_party/skia/include/pathops/SkPathOps.h"
 
-#ifdef _SKIA_SUPPORT_PATHS_
-#include "core/fxge/cfx_cliprgn.h"
-#endif  // _SKIA_SUPPORT_PATHS_
-
-#ifdef _SKIA_SUPPORT_
-#include "third_party/skia/include/core/SkColorFilter.h"
-#include "third_party/skia/include/core/SkMaskFilter.h"
-#include "third_party/skia/include/core/SkPictureRecorder.h"
-#endif  // _SKIA_SUPPORT_
-
 namespace {
 
-#ifdef _SKIA_SUPPORT_PATHS_
-void RgbByteOrderTransferBitmap(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                                int dest_left,
-                                int dest_top,
-                                int width,
-                                int height,
-                                const RetainPtr<CFX_DIBBase>& pSrcBitmap,
-                                int src_left,
-                                int src_top) {
-  if (!pBitmap)
-    return;
-
-  if (!pBitmap->GetOverlapRect(dest_left, dest_top, width, height,
-                               pSrcBitmap->GetWidth(), pSrcBitmap->GetHeight(),
-                               src_left, src_top, nullptr)) {
-    return;
-  }
-
-  int Bpp = pBitmap->GetBPP() / 8;
-  FXDIB_Format dest_format = pBitmap->GetFormat();
-  FXDIB_Format src_format = pSrcBitmap->GetFormat();
-  int pitch = pBitmap->GetPitch();
-  uint8_t* buffer = pBitmap->GetBuffer();
-  if (dest_format == src_format) {
-    for (int row = 0; row < height; row++) {
-      uint8_t* dest_scan = buffer + (dest_top + row) * pitch + dest_left * Bpp;
-      const uint8_t* src_scan =
-          pSrcBitmap->GetScanline(src_top + row) + src_left * Bpp;
-      if (Bpp == 4) {
-        for (int col = 0; col < width; col++) {
-          FXARGB_SETDIB(dest_scan, ArgbEncode(src_scan[3], src_scan[0],
-                                              src_scan[1], src_scan[2]));
-          dest_scan += 4;
-          src_scan += 4;
-        }
-      } else {
-        for (int col = 0; col < width; col++) {
-          *dest_scan++ = src_scan[2];
-          *dest_scan++ = src_scan[1];
-          *dest_scan++ = src_scan[0];
-          src_scan += 3;
-        }
-      }
-    }
-    return;
-  }
-
-  uint8_t* dest_buf = buffer + dest_top * pitch + dest_left * Bpp;
-  if (dest_format == FXDIB_Rgb) {
-    if (src_format == FXDIB_Rgb32) {
-      for (int row = 0; row < height; row++) {
-        uint8_t* dest_scan = dest_buf + row * pitch;
-        const uint8_t* src_scan =
-            pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
-        for (int col = 0; col < width; col++) {
-          *dest_scan++ = src_scan[2];
-          *dest_scan++ = src_scan[1];
-          *dest_scan++ = src_scan[0];
-          src_scan += 4;
-        }
-      }
-    } else {
-      NOTREACHED();
-    }
-    return;
-  }
-
-  if (dest_format == FXDIB_Argb || dest_format == FXDIB_Rgb32) {
-    if (src_format == FXDIB_Rgb) {
-      for (int row = 0; row < height; row++) {
-        uint8_t* dest_scan = (uint8_t*)(dest_buf + row * pitch);
-        const uint8_t* src_scan =
-            pSrcBitmap->GetScanline(src_top + row) + src_left * 3;
-        for (int col = 0; col < width; col++) {
-          FXARGB_SETDIB(dest_scan, ArgbEncode(0xff, src_scan[0], src_scan[1],
-                                              src_scan[2]));
-          dest_scan += 4;
-          src_scan += 3;
-        }
-      }
-    } else if (src_format == FXDIB_Rgb32) {
-      ASSERT(dest_format == FXDIB_Argb);
-      for (int row = 0; row < height; row++) {
-        uint8_t* dest_scan = dest_buf + row * pitch;
-        const uint8_t* src_scan =
-            pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
-        for (int col = 0; col < width; col++) {
-          FXARGB_SETDIB(dest_scan, ArgbEncode(0xff, src_scan[0], src_scan[1],
-                                              src_scan[2]));
-          src_scan += 4;
-          dest_scan += 4;
-        }
-      }
-    }
-    return;
-  }
-
-  NOTREACHED();
-}
-
-#endif  // _SKIA_SUPPORT_PATHS_
-
 #define SHOW_SKIA_PATH 0  // set to 1 to print the path contents
 #if SHOW_SKIA_PATH
 #define SHOW_SKIA_PATH_SHORTHAND 0  // set to 1 for abbreviated path contents
 #endif
-#define DRAW_SKIA_CLIP 0  // set to 1 to draw a green rectangle around the clip
-#define SHOW_TEXT_GLYPHS 0  // set to 1 to print unichar equivalent of glyph
 
 #if SHOW_SKIA_PATH
 void DebugShowSkiaPaint(const SkPaint& paint) {
@@ -187,11 +103,10 @@
   printf(" **\n");
 #else
   SkDynamicMemoryWStream stream;
-  path.dump(&stream, false, false);
-  std::unique_ptr<char, FxFreeDeleter> storage;
-  storage.reset(FX_Alloc(char, stream.bytesWritten()));
-  stream.copyTo(storage.get());
-  printf("%.*s", (int)stream.bytesWritten(), storage.get());
+  path.dump(&stream, false);
+  DataVector<char> storage(stream.bytesWritten());
+  stream.copyTo(storage.data());
+  printf("%.*s", static_cast<int>(storage.size()), storage.data());
 #endif  // SHOW_SKIA_PATH_SHORTHAND
 #endif  // SHOW_SKIA_PATH
 }
@@ -241,96 +156,74 @@
 #endif  // SHOW_SKIA_PATH
 }
 
-#if DRAW_SKIA_CLIP
-
-SkPaint DebugClipPaint() {
-  SkPaint paint;
-  paint.setAntiAlias(true);
-  paint.setColor(SK_ColorGREEN);
-  paint.setStyle(SkPaint::kStroke_Style);
-  return paint;
-}
-
-void DebugDrawSkiaClipRect(SkCanvas* canvas, const SkRect& rect) {
-  SkPaint paint = DebugClipPaint();
-  canvas->drawRect(rect, paint);
-}
-
-void DebugDrawSkiaClipPath(SkCanvas* canvas, const SkPath& path) {
-  SkPaint paint = DebugClipPaint();
-  canvas->drawPath(path, paint);
-}
-
-#else  // DRAW_SKIA_CLIP
-
-void DebugDrawSkiaClipRect(SkCanvas* canvas, const SkRect& rect) {}
-
-void DebugDrawSkiaClipPath(SkCanvas* canvas, const SkPath& path) {}
-
-#endif  // DRAW_SKIA_CLIP
-
-#ifdef _SKIA_SUPPORT_
 static void DebugValidate(const RetainPtr<CFX_DIBitmap>& bitmap,
                           const RetainPtr<CFX_DIBitmap>& device) {
   if (bitmap) {
-    ASSERT(bitmap->GetBPP() == 8 || bitmap->GetBPP() == 32);
-    if (bitmap->GetBPP() == 32) {
-      bitmap->DebugVerifyBitmapIsPreMultiplied(nullptr);
-    }
+    DCHECK(bitmap->GetBPP() == 8 || bitmap->GetBPP() == 32);
   }
   if (device) {
-    ASSERT(device->GetBPP() == 8 || device->GetBPP() == 32);
-    if (device->GetBPP() == 32) {
-      device->DebugVerifyBitmapIsPreMultiplied(nullptr);
-    }
+    DCHECK(device->GetBPP() == 8 || device->GetBPP() == 32);
   }
 }
-#endif  // _SKIA_SUPPORT_
 
-constexpr int kAlternateOrWindingFillModeMask =
-    FXFILL_ALTERNATE | FXFILL_WINDING;
-
-int GetAlternateOrWindingFillMode(int fill_mode) {
-  return fill_mode & kAlternateOrWindingFillModeMask;
+SkColorType Get32BitSkColorType(bool is_rgb_byte_order) {
+  return is_rgb_byte_order ? kRGBA_8888_SkColorType : kBGRA_8888_SkColorType;
 }
 
-bool IsAlternateFillMode(int fill_mode) {
+SkPathFillType GetAlternateOrWindingFillType(
+    const CFX_FillRenderOptions& fill_options) {
   // TODO(thestig): This function should be able to assert
-  // GetAlternateOrWindingFillMode(fill_mode) != 0.
-  return GetAlternateOrWindingFillMode(fill_mode) == FXFILL_ALTERNATE;
+  // fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill.
+  return fill_options.fill_type == CFX_FillRenderOptions::FillType::kEvenOdd
+             ? SkPathFillType::kEvenOdd
+             : SkPathFillType::kWinding;
 }
 
-SkPathFillType GetAlternateOrWindingFillType(int fill_mode) {
-  return IsAlternateFillMode(fill_mode) ? SkPathFillType::kEvenOdd
-                                        : SkPathFillType::kWinding;
+SkFont::Edging GetFontEdgingType(const CFX_TextRenderOptions& text_options) {
+  if (text_options.aliasing_type == CFX_TextRenderOptions::kAliasing)
+    return SkFont::Edging::kAlias;
+
+  if (text_options.aliasing_type == CFX_TextRenderOptions::kAntiAliasing)
+    return SkFont::Edging::kAntiAlias;
+
+  DCHECK_EQ(text_options.aliasing_type, CFX_TextRenderOptions::kLcd);
+  return SkFont::Edging::kSubpixelAntiAlias;
 }
 
-bool IsEvenOddFillType(SkPathFillType fill) {
-  return fill == SkPathFillType::kEvenOdd ||
-         fill == SkPathFillType::kInverseEvenOdd;
+bool IsPathAPoint(const SkPath& path) {
+  if (path.isEmpty())
+    return false;
+
+  if (path.countPoints() == 1)
+    return true;
+
+  for (int i = 0; i < path.countPoints() - 1; ++i) {
+    if (path.getPoint(i) != path.getPoint(i + 1))
+      return false;
+  }
+  return true;
 }
 
-SkPath BuildPath(const CFX_PathData* pPathData) {
-  SkPath skPath;
-  const CFX_PathData* pFPath = pPathData;
-  const std::vector<FX_PATHPOINT>& pPoints = pFPath->GetPoints();
-  for (size_t i = 0; i < pPoints.size(); i++) {
-    CFX_PointF point = pPoints[i].m_Point;
-    FXPT_TYPE point_type = pPoints[i].m_Type;
-    if (point_type == FXPT_TYPE::MoveTo) {
-      skPath.moveTo(point.x, point.y);
-    } else if (point_type == FXPT_TYPE::LineTo) {
-      skPath.lineTo(point.x, point.y);
-    } else if (point_type == FXPT_TYPE::BezierTo) {
-      CFX_PointF point2 = pPoints[i + 1].m_Point;
-      CFX_PointF point3 = pPoints[i + 2].m_Point;
-      skPath.cubicTo(point.x, point.y, point2.x, point2.y, point3.x, point3.y);
+SkPath BuildPath(const CFX_Path& path) {
+  SkPath sk_path;
+  pdfium::span<const CFX_Path::Point> points = path.GetPoints();
+  for (size_t i = 0; i < points.size(); ++i) {
+    const CFX_PointF& point = points[i].m_Point;
+    CFX_Path::Point::Type point_type = points[i].m_Type;
+    if (point_type == CFX_Path::Point::Type::kMove) {
+      sk_path.moveTo(point.x, point.y);
+    } else if (point_type == CFX_Path::Point::Type::kLine) {
+      sk_path.lineTo(point.x, point.y);
+    } else if (point_type == CFX_Path::Point::Type::kBezier) {
+      const CFX_PointF& point2 = points[i + 1].m_Point;
+      const CFX_PointF& point3 = points[i + 2].m_Point;
+      sk_path.cubicTo(point.x, point.y, point2.x, point2.y, point3.x, point3.y);
       i += 2;
     }
-    if (pPoints[i].m_CloseFigure)
-      skPath.close();
+    if (points[i].m_CloseFigure)
+      sk_path.close();
   }
-  return skPath;
+  return sk_path;
 }
 
 SkMatrix ToSkMatrix(const CFX_Matrix& m) {
@@ -380,115 +273,124 @@
     case BlendMode::kLuminosity:
       return SkBlendMode::kLuminosity;
     case BlendMode::kNormal:
-    default:
       return SkBlendMode::kSrcOver;
   }
 }
 
-// Add begin & end colors into |skColors| array for each gradient transition.
+// Add begin & end colors into `colors` array for each gradient transition.
 //
-// |is_encode_reversed| must be set to true when the parent function of |pFunc|
-// has an Encode array, and the matching pair of encode values for |pFunc| are
+// `is_encode_reversed` must be set to true when the parent function of `func`
+// has an Encode array, and the matching pair of encode values for `func` are
 // in decreasing order.
-bool AddColors(const CPDF_ExpIntFunc* pFunc,
-               SkTDArray<SkColor>* skColors,
+bool AddColors(const CPDF_ExpIntFunc* func,
+               DataVector<SkColor>& colors,
                bool is_encode_reversed) {
-  if (pFunc->CountInputs() != 1)
+  if (func->CountInputs() != 1) {
     return false;
-  if (pFunc->m_Exponent != 1)
+  }
+  if (func->GetExponent() != 1) {
     return false;
-  if (pFunc->m_nOrigOutputs != 3)
+  }
+  if (func->GetOrigOutputs() != 3) {
     return false;
+  }
 
-  auto begin_values = pFunc->m_BeginValues.begin();
-  auto end_values = pFunc->m_EndValues.begin();
+  pdfium::span<const float> begin_values = func->GetBeginValues();
+  pdfium::span<const float> end_values = func->GetEndValues();
   if (is_encode_reversed)
     std::swap(begin_values, end_values);
 
-  skColors->push_back(SkColorSetARGB(0xFF,
-                                     SkUnitScalarClampToByte(begin_values[0]),
-                                     SkUnitScalarClampToByte(begin_values[1]),
-                                     SkUnitScalarClampToByte(begin_values[2])));
-  skColors->push_back(SkColorSetARGB(0xFF,
-                                     SkUnitScalarClampToByte(end_values[0]),
-                                     SkUnitScalarClampToByte(end_values[1]),
-                                     SkUnitScalarClampToByte(end_values[2])));
+  colors.push_back(SkColorSetARGB(0xFF,
+                                  SkUnitScalarClampToByte(begin_values[0]),
+                                  SkUnitScalarClampToByte(begin_values[1]),
+                                  SkUnitScalarClampToByte(begin_values[2])));
+  colors.push_back(SkColorSetARGB(0xFF, SkUnitScalarClampToByte(end_values[0]),
+                                  SkUnitScalarClampToByte(end_values[1]),
+                                  SkUnitScalarClampToByte(end_values[2])));
   return true;
 }
 
 uint8_t FloatToByte(float f) {
-  ASSERT(f >= 0);
-  ASSERT(f <= 1);
+  DCHECK(f >= 0);
+  DCHECK(f <= 1);
   return (uint8_t)(f * 255.99f);
 }
 
-bool AddSamples(const CPDF_SampledFunc* pFunc,
-                SkTDArray<SkColor>* skColors,
-                SkTDArray<SkScalar>* skPos) {
-  if (pFunc->CountInputs() != 1)
+bool AddSamples(const CPDF_SampledFunc* func,
+                DataVector<SkColor>& colors,
+                DataVector<SkScalar>& pos) {
+  if (func->CountInputs() != 1) {
     return false;
-  if (pFunc->CountOutputs() != 3)  // expect rgb
-    return false;
-  if (pFunc->GetEncodeInfo().empty())
-    return false;
-  const CPDF_SampledFunc::SampleEncodeInfo& encodeInfo =
-      pFunc->GetEncodeInfo()[0];
-  if (encodeInfo.encode_min != 0)
-    return false;
-  if (encodeInfo.encode_max != encodeInfo.sizes - 1)
-    return false;
-  uint32_t sampleSize = pFunc->GetBitsPerSample();
-  uint32_t sampleCount = encodeInfo.sizes;
-  if (sampleCount != 1U << sampleSize)
-    return false;
-  if (pFunc->GetSampleStream()->GetSize() < sampleCount * 3 * sampleSize / 8)
-    return false;
-
-  float colorsMin[3];
-  float colorsMax[3];
-  for (int i = 0; i < 3; ++i) {
-    colorsMin[i] = pFunc->GetRange(i * 2);
-    colorsMax[i] = pFunc->GetRange(i * 2 + 1);
   }
-  pdfium::span<const uint8_t> pSampleData = pFunc->GetSampleStream()->GetSpan();
-  CFX_BitStream bitstream(pSampleData);
-  for (uint32_t i = 0; i < sampleCount; ++i) {
-    float floatColors[3];
+  if (func->CountOutputs() != 3) {  // expect rgb
+    return false;
+  }
+  if (func->GetEncodeInfo().empty()) {
+    return false;
+  }
+  const CPDF_SampledFunc::SampleEncodeInfo& encode_info =
+      func->GetEncodeInfo()[0];
+  if (encode_info.encode_min != 0) {
+    return false;
+  }
+  if (encode_info.encode_max != encode_info.sizes - 1) {
+    return false;
+  }
+  uint32_t sample_size = func->GetBitsPerSample();
+  uint32_t sample_count = encode_info.sizes;
+  if (sample_count != 1U << sample_size) {
+    return false;
+  }
+  if (func->GetSampleStream()->GetSize() < sample_count * 3 * sample_size / 8) {
+    return false;
+  }
+
+  float colors_min[3];
+  float colors_max[3];
+  for (int i = 0; i < 3; ++i) {
+    colors_min[i] = func->GetRange(i * 2);
+    colors_max[i] = func->GetRange(i * 2 + 1);
+  }
+  pdfium::span<const uint8_t> sample_data = func->GetSampleStream()->GetSpan();
+  CFX_BitStream bitstream(sample_data);
+  for (uint32_t i = 0; i < sample_count; ++i) {
+    float float_colors[3];
     for (uint32_t j = 0; j < 3; ++j) {
-      float sample = static_cast<float>(bitstream.GetBits(sampleSize));
-      float interp = sample / (sampleCount - 1);
-      floatColors[j] = colorsMin[j] + (colorsMax[j] - colorsMin[j]) * interp;
+      float sample = static_cast<float>(bitstream.GetBits(sample_size));
+      float interp = sample / (sample_count - 1);
+      float_colors[j] =
+          colors_min[j] + (colors_max[j] - colors_min[j]) * interp;
     }
-    SkColor color =
-        SkPackARGB32(0xFF, FloatToByte(floatColors[0]),
-                     FloatToByte(floatColors[1]), FloatToByte(floatColors[2]));
-    skColors->push_back(color);
-    skPos->push_back((float)i / (sampleCount - 1));
+    colors.push_back(SkPackARGB32NoCheck(0xFF, FloatToByte(float_colors[0]),
+                                         FloatToByte(float_colors[1]),
+                                         FloatToByte(float_colors[2])));
+    pos.push_back(static_cast<float>(i) / (sample_count - 1));
   }
   return true;
 }
 
-bool AddStitching(const CPDF_StitchFunc* pFunc,
-                  SkTDArray<SkColor>* skColors,
-                  SkTDArray<SkScalar>* skPos) {
-  float boundsStart = pFunc->GetDomain(0);
+bool AddStitching(const CPDF_StitchFunc* func,
+                  DataVector<SkColor>& colors,
+                  DataVector<SkScalar>& pos) {
+  float bounds_start = func->GetDomain(0);
 
-  const auto& subFunctions = pFunc->GetSubFunctions();
-  int subFunctionCount = subFunctions.size();
-  for (int i = 0; i < subFunctionCount; ++i) {
-    const CPDF_ExpIntFunc* pSubFunc = subFunctions[i]->ToExpIntFunc();
-    if (!pSubFunc)
+  const auto& sub_functions = func->GetSubFunctions();
+  const size_t sub_function_count = sub_functions.size();
+  for (size_t i = 0; i < sub_function_count; ++i) {
+    const CPDF_ExpIntFunc* sub_func = sub_functions[i]->ToExpIntFunc();
+    if (!sub_func)
       return false;
     // Check if the matching encode values are reversed
     bool is_encode_reversed =
-        pFunc->GetEncode(2 * i) > pFunc->GetEncode(2 * i + 1);
-    if (!AddColors(pSubFunc, skColors, is_encode_reversed))
+        func->GetEncode(2 * i) > func->GetEncode(2 * i + 1);
+    if (!AddColors(sub_func, colors, is_encode_reversed)) {
       return false;
-    float boundsEnd =
-        i < subFunctionCount - 1 ? pFunc->GetBound(i + 1) : pFunc->GetDomain(1);
-    skPos->push_back(boundsStart);
-    skPos->push_back(boundsEnd);
-    boundsStart = boundsEnd;
+    }
+    float bounds_end =
+        i < sub_function_count - 1 ? func->GetBound(i + 1) : func->GetDomain(1);
+    pos.push_back(bounds_start);
+    pos.push_back(bounds_end);
+    bounds_start = bounds_end;
   }
   return true;
 }
@@ -535,13 +437,13 @@
     if (sDist * eDist <= 0)  // if the signs are different,
       continue;              // the point is inside the gradient
     if (sDist < 0) {
-      SkScalar smaller = SkTMin(sDist, eDist);
+      SkScalar smaller = std::min(sDist, eDist);
       if (minPerpDist > smaller) {
         minPerpDist = smaller;
         minPerpPtIndex = i;
       }
     } else {
-      SkScalar larger = SkTMax(sDist, eDist);
+      SkScalar larger = std::max(sDist, eDist);
       if (maxPerpDist < larger) {
         maxPerpDist = larger;
         maxPerpPtIndex = i;
@@ -596,7 +498,77 @@
   clip->lineTo(IntersectSides(rectPts[maxBounds], slope, startEdgePt));
 }
 
-#ifdef _SKIA_SUPPORT_
+// Converts a stroking path to scanlines
+void PaintStroke(SkPaint* spaint,
+                 const CFX_GraphStateData* graph_state,
+                 const SkMatrix& matrix,
+                 const CFX_FillRenderOptions& fill_options) {
+  SkPaint::Cap cap;
+  switch (graph_state->m_LineCap) {
+    case CFX_GraphStateData::LineCap::kRound:
+      cap = SkPaint::kRound_Cap;
+      break;
+    case CFX_GraphStateData::LineCap::kSquare:
+      cap = SkPaint::kSquare_Cap;
+      break;
+    default:
+      cap = SkPaint::kButt_Cap;
+      break;
+  }
+  SkPaint::Join join;
+  switch (graph_state->m_LineJoin) {
+    case CFX_GraphStateData::LineJoin::kRound:
+      join = SkPaint::kRound_Join;
+      break;
+    case CFX_GraphStateData::LineJoin::kBevel:
+      join = SkPaint::kBevel_Join;
+      break;
+    default:
+      join = SkPaint::kMiter_Join;
+      break;
+  }
+  SkMatrix inverse;
+  if (!matrix.invert(&inverse)) {
+    return;  // give up if the matrix is degenerate, and not invertable
+  }
+  inverse.set(SkMatrix::kMTransX, 0);
+  inverse.set(SkMatrix::kMTransY, 0);
+  SkVector deviceUnits[2] = {{0, 1}, {1, 0}};
+  inverse.mapPoints(deviceUnits, std::size(deviceUnits));
+
+  float width = fill_options.zero_area
+                    ? 0.0f
+                    : std::max(graph_state->m_LineWidth,
+                               std::min(deviceUnits[0].length(),
+                                        deviceUnits[1].length()));
+  if (!graph_state->m_DashArray.empty()) {
+    size_t count = (graph_state->m_DashArray.size() + 1) / 2;
+    DataVector<SkScalar> intervals(count * 2);
+    // Set dash pattern
+    for (size_t i = 0; i < count; i++) {
+      float on = graph_state->m_DashArray[i * 2];
+      if (on <= 0.000001f) {
+        on = 0.1f;
+      }
+      float off = i * 2 + 1 == graph_state->m_DashArray.size()
+                      ? on
+                      : graph_state->m_DashArray[i * 2 + 1];
+      off = std::max(off, 0.0f);
+      intervals[i * 2] = on;
+      intervals[i * 2 + 1] = off;
+    }
+    spaint->setPathEffect(SkDashPathEffect::Make(
+        intervals.data(), pdfium::base::checked_cast<int>(intervals.size()),
+        graph_state->m_DashPhase));
+  }
+  spaint->setStyle(SkPaint::kStroke_Style);
+  spaint->setAntiAlias(!fill_options.aliased_path);
+  spaint->setStrokeWidth(width);
+  spaint->setStrokeMiter(graph_state->m_MiterLimit);
+  spaint->setStrokeCap(cap);
+  spaint->setStrokeJoin(join);
+}
+
 void SetBitmapMatrix(const CFX_Matrix& m,
                      int width,
                      int height,
@@ -605,1061 +577,184 @@
                    -m.d / height, m.d + m.f, 0, 0, 1);
 }
 
-void SetBitmapPaint(bool isAlphaMask,
-                    uint32_t argb,
+void SetBitmapPaint(bool is_mask,
+                    bool anti_alias,
                     int bitmap_alpha,
+                    uint32_t argb,
                     BlendMode blend_type,
                     SkPaint* paint) {
-  paint->setAntiAlias(true);
-  if (isAlphaMask)
+  DCHECK_GE(bitmap_alpha, 0);
+  DCHECK_LE(bitmap_alpha, 255);
+
+  if (is_mask)
+    paint->setColor(argb);
+  else if (bitmap_alpha != 255)
+    paint->setAlpha(bitmap_alpha);
+
+  paint->setAntiAlias(anti_alias);
+  paint->setBlendMode(GetSkiaBlendMode(blend_type));
+}
+
+void SetBitmapPaintForMerge(bool is_mask,
+                            bool anti_alias,
+                            uint32_t argb,
+                            int bitmap_alpha,
+                            BlendMode blend_type,
+                            SkPaint* paint) {
+  if (is_mask)
     paint->setColorFilter(SkColorFilters::Blend(argb, SkBlendMode::kSrc));
 
-  // paint->setFilterQuality(kHigh_SkFilterQuality);
-  paint->setBlendMode(GetSkiaBlendMode(blend_type));
   paint->setAlpha(bitmap_alpha);
+  paint->setAntiAlias(anti_alias);
+  paint->setBlendMode(GetSkiaBlendMode(blend_type));
 }
 
-bool Upsample(const RetainPtr<CFX_DIBBase>& pSource,
-              std::unique_ptr<uint8_t, FxFreeDeleter>& dst8Storage,
-              std::unique_ptr<uint32_t, FxFreeDeleter>& dst32Storage,
-              SkBitmap* skBitmap,
-              int* widthPtr,
-              int* heightPtr,
-              bool forceAlpha) {
-  void* buffer = pSource->GetBuffer();
-  if (!buffer)
-    return false;
-  SkColorType colorType = forceAlpha || pSource->IsAlphaMask()
-                              ? SkColorType::kAlpha_8_SkColorType
-                              : SkColorType::kGray_8_SkColorType;
-  SkAlphaType alphaType =
-      pSource->IsAlphaMask() ? kPremul_SkAlphaType : kOpaque_SkAlphaType;
-  int width = pSource->GetWidth();
-  int height = pSource->GetHeight();
-  int rowBytes = pSource->GetPitch();
-  switch (pSource->GetBPP()) {
-    case 1: {
-      dst8Storage.reset(FX_Alloc2D(uint8_t, width, height));
-      uint8_t* dst8Pixels = dst8Storage.get();
-      for (int y = 0; y < height; ++y) {
-        const uint8_t* srcRow =
-            static_cast<const uint8_t*>(buffer) + y * rowBytes;
-        uint8_t* dstRow = dst8Pixels + y * width;
-        for (int x = 0; x < width; ++x)
-          dstRow[x] = srcRow[x >> 3] & (1 << (~x & 0x07)) ? 0xFF : 0x00;
-      }
-      buffer = dst8Storage.get();
-      rowBytes = width;
-      break;
-    }
-    case 8:
-      // we upscale ctables to 32bit.
-      if (pSource->GetPalette()) {
-        dst32Storage.reset(FX_Alloc2D(uint32_t, width, height));
-        SkPMColor* dst32Pixels = dst32Storage.get();
-        const SkPMColor* ctable = pSource->GetPalette();
-        const unsigned ctableSize = pSource->GetPaletteSize();
-        for (int y = 0; y < height; ++y) {
-          const uint8_t* srcRow =
-              static_cast<const uint8_t*>(buffer) + y * rowBytes;
-          uint32_t* dstRow = dst32Pixels + y * width;
-          for (int x = 0; x < width; ++x) {
-            unsigned index = srcRow[x];
-            if (index >= ctableSize) {
-              index = 0;
-            }
-            dstRow[x] = ctable[index];
-          }
-        }
-        buffer = dst32Storage.get();
-        rowBytes = width * sizeof(uint32_t);
-        colorType = SkColorType::kN32_SkColorType;
-      }
-      break;
-    case 24: {
-      dst32Storage.reset(FX_Alloc2D(uint32_t, width, height));
-      uint32_t* dst32Pixels = dst32Storage.get();
-      for (int y = 0; y < height; ++y) {
-        const uint8_t* srcRow =
-            static_cast<const uint8_t*>(buffer) + y * rowBytes;
-        uint32_t* dstRow = dst32Pixels + y * width;
-        for (int x = 0; x < width; ++x) {
-          dstRow[x] = SkPackARGB32(0xFF, srcRow[x * 3 + 2], srcRow[x * 3 + 1],
-                                   srcRow[x * 3 + 0]);
-        }
-      }
-      buffer = dst32Storage.get();
-      rowBytes = width * sizeof(uint32_t);
-      colorType = SkColorType::kN32_SkColorType;
-      alphaType = kOpaque_SkAlphaType;
-      break;
-    }
-    case 32:
-      colorType = SkColorType::kN32_SkColorType;
-      alphaType = kPremul_SkAlphaType;
-      pSource->DebugVerifyBitmapIsPreMultiplied(buffer);
-      break;
-    default:
-      NOTREACHED();  // TODO(bug_11) ensure that all cases are covered
-      colorType = SkColorType::kUnknown_SkColorType;
-  }
-  SkImageInfo imageInfo =
-      SkImageInfo::Make(width, height, colorType, alphaType);
-  skBitmap->installPixels(imageInfo, buffer, rowBytes);
-  *widthPtr = width;
-  *heightPtr = height;
-  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>();
+  if (!bitmap->Create(width, height, FXDIB_Format::kArgb))
+    return nullptr;
+
+  bitmap->Clear(color);
+  return bitmap;
 }
-#endif  // _SKIA_SUPPORT_
+
+bool HasRSX(pdfium::span<const TextCharPos> char_pos,
+            float* scaleXPtr,
+            bool* oneAtATimePtr) {
+  bool useRSXform = false;
+  bool oneAtATime = false;
+  float scaleX = 1;
+  for (const TextCharPos& cp : char_pos) {
+    if (!cp.m_bGlyphAdjust) {
+      continue;
+    }
+    bool upright = 0 == cp.m_AdjustMatrix[1] && 0 == cp.m_AdjustMatrix[2];
+    if (cp.m_AdjustMatrix[0] != cp.m_AdjustMatrix[3]) {
+      if (upright && 1 == cp.m_AdjustMatrix[3]) {
+        if (1 == scaleX) {
+          scaleX = cp.m_AdjustMatrix[0];
+        } else if (scaleX != cp.m_AdjustMatrix[0]) {
+          oneAtATime = true;
+        }
+      } else {
+        oneAtATime = true;
+      }
+    } else if (cp.m_AdjustMatrix[1] != -cp.m_AdjustMatrix[2]) {
+      oneAtATime = true;
+    } else {
+      useRSXform = true;
+    }
+  }
+  *oneAtATimePtr = oneAtATime;
+  *scaleXPtr = oneAtATime ? 1 : scaleX;
+  return oneAtATime ? false : useRSXform;
+}
 
 }  // namespace
 
-// Encapsulate the state used for successive text and path draws so that
-// they can be combined.
-class SkiaState {
- public:
-  enum class Clip {
-    kSave,
-    kPath,
-  };
-
-  enum class Accumulator {
-    kNone,
-    kPath,
-    kText,
-    kOther,
-  };
-
-  // mark all cached state as uninitialized
-  explicit SkiaState(CFX_SkiaDeviceDriver* pDriver) : m_pDriver(pDriver) {}
-
-  bool DrawPath(const CFX_PathData* pPathData,
-                const CFX_Matrix* pMatrix,
-                const CFX_GraphStateData* pDrawState,
-                uint32_t fill_color,
-                uint32_t stroke_color,
-                int fill_mode,
-                BlendMode blend_type) {
-    if (m_debugDisable)
-      return false;
-    Dump(__func__);
-    int drawIndex = SkTMin(m_drawIndex, m_commands.count());
-    if (Accumulator::kText == m_type || drawIndex != m_commandIndex ||
-        (Accumulator::kPath == m_type &&
-         DrawChanged(pMatrix, pDrawState, fill_color, stroke_color, fill_mode,
-                     blend_type, m_pDriver->GetGroupKnockout()))) {
-      Flush();
-    }
-    if (Accumulator::kPath != m_type) {
-      m_skPath.reset();
-      m_fillFullCover = !!(fill_mode & FXFILL_FULLCOVER);
-      m_fillPath = GetAlternateOrWindingFillMode(fill_mode) && fill_color;
-      m_skPath.setFillType(GetAlternateOrWindingFillType(fill_mode));
-      if (pDrawState)
-        m_drawState = *pDrawState;
-      m_fillColor = fill_color;
-      m_strokeColor = stroke_color;
-      m_blendType = blend_type;
-      m_groupKnockout = m_pDriver->GetGroupKnockout();
-      if (pMatrix)
-        m_drawMatrix = *pMatrix;
-      m_drawIndex = m_commandIndex;
-      m_type = Accumulator::kPath;
-    }
-    SkPath skPath = BuildPath(pPathData);
-    SkPoint delta;
-    if (MatrixOffset(pMatrix, &delta))
-      skPath.offset(delta.fX, delta.fY);
-    m_skPath.addPath(skPath);
-    return true;
+// static
+std::unique_ptr<CFX_SkiaDeviceDriver> CFX_SkiaDeviceDriver::Create(
+    RetainPtr<CFX_DIBitmap> pBitmap,
+    bool bRgbByteOrder,
+    RetainPtr<CFX_DIBitmap> pBackdropBitmap,
+    bool bGroupKnockout) {
+  auto driver = pdfium::WrapUnique(
+      new CFX_SkiaDeviceDriver(std::move(pBitmap), bRgbByteOrder,
+                               std::move(pBackdropBitmap), bGroupKnockout));
+  if (!driver->m_pCanvas) {
+    return nullptr;
   }
 
-  void FlushPath() {
-    Dump(__func__);
-    SkMatrix skMatrix = ToSkMatrix(m_drawMatrix);
-    SkPaint skPaint;
-    skPaint.setAntiAlias(true);
-    if (m_fillFullCover)
-      skPaint.setBlendMode(SkBlendMode::kPlus);
-    int stroke_alpha = FXARGB_A(m_strokeColor);
-    if (stroke_alpha)
-      m_pDriver->PaintStroke(&skPaint, &m_drawState, skMatrix);
-    SkCanvas* skCanvas = m_pDriver->SkiaCanvas();
-    SkAutoCanvasRestore scoped_save_restore(skCanvas, /*doSave=*/true);
-    skCanvas->concat(skMatrix);
-    bool do_stroke = true;
-    if (m_fillPath) {
-      SkPath strokePath;
-      const SkPath* fillPath = &m_skPath;
-      if (stroke_alpha) {
-        if (m_groupKnockout) {
-          skPaint.getFillPath(m_skPath, &strokePath);
-          if (m_strokeColor == m_fillColor &&
-              Op(m_skPath, strokePath, SkPathOp::kUnion_SkPathOp,
-                 &strokePath)) {
-            fillPath = &strokePath;
-            do_stroke = false;
-          } else if (Op(m_skPath, strokePath, SkPathOp::kDifference_SkPathOp,
-                        &strokePath)) {
-            fillPath = &strokePath;
-          }
-        }
-      }
-      skPaint.setStyle(SkPaint::kFill_Style);
-      skPaint.setColor(m_fillColor);
-#ifdef _SKIA_SUPPORT_PATHS_
-      m_pDriver->PreMultiply();
-#endif  // _SKIA_SUPPORT_PATHS_
-      DebugShowSkiaDrawPath(m_pDriver.Get(), skCanvas, skPaint, *fillPath);
-      skCanvas->drawPath(*fillPath, skPaint);
-    }
-    if (stroke_alpha && do_stroke) {
-      skPaint.setStyle(SkPaint::kStroke_Style);
-      skPaint.setColor(m_strokeColor);
-#ifdef _SKIA_SUPPORT_PATHS_
-      m_pDriver->PreMultiply();
-#endif  // _SKIA_SUPPORT_PATHS_
-      DebugShowSkiaDrawPath(m_pDriver.Get(), skCanvas, skPaint, m_skPath);
-      skCanvas->drawPath(m_skPath, skPaint);
-    }
-    m_drawIndex = INT_MAX;
-    m_type = Accumulator::kNone;
-    m_drawMatrix = CFX_Matrix();
-  }
-
-  bool HasRSX(int nChars,
-              const TextCharPos* pCharPos,
-              float* scaleXPtr,
-              bool* oneAtATimePtr) const {
-    bool useRSXform = false;
-    bool oneAtATime = false;
-    float scaleX = 1;
-    for (int index = 0; index < nChars; ++index) {
-      const TextCharPos& cp = pCharPos[index];
-      if (!cp.m_bGlyphAdjust)
-        continue;
-      bool upright = 0 == cp.m_AdjustMatrix[1] && 0 == cp.m_AdjustMatrix[2];
-      if (cp.m_AdjustMatrix[0] != cp.m_AdjustMatrix[3]) {
-        if (upright && 1 == cp.m_AdjustMatrix[3]) {
-          if (1 == scaleX)
-            scaleX = cp.m_AdjustMatrix[0];
-          else if (scaleX != cp.m_AdjustMatrix[0])
-            oneAtATime = true;
-        } else {
-          oneAtATime = true;
-        }
-      } else if (cp.m_AdjustMatrix[1] != -cp.m_AdjustMatrix[2]) {
-        oneAtATime = true;
-      } else {
-        useRSXform = true;
-      }
-    }
-    *oneAtATimePtr = oneAtATime;
-    *scaleXPtr = oneAtATime ? 1 : scaleX;
-    return oneAtATime ? false : useRSXform;
-  }
-
-  bool DrawText(int nChars,
-                const TextCharPos* pCharPos,
-                CFX_Font* pFont,
-                const CFX_Matrix& matrix,
-                float font_size,
-                uint32_t color) {
-    if (m_debugDisable)
-      return false;
-    Dump(__func__);
-    float scaleX = 1;
-    bool oneAtATime = false;
-    bool hasRSX = HasRSX(nChars, pCharPos, &scaleX, &oneAtATime);
-    if (oneAtATime) {
-      Flush();
-      return false;
-    }
-    int drawIndex = SkTMin(m_drawIndex, m_commands.count());
-    if (Accumulator::kPath == m_type || drawIndex != m_commandIndex ||
-        (Accumulator::kText == m_type &&
-         (FontChanged(pFont, matrix, font_size, scaleX, color) ||
-          hasRSX == m_rsxform.isEmpty()))) {
-      Flush();
-    }
-    if (Accumulator::kText != m_type) {
-      m_italicAngle = pFont->GetSubstFontItalicAngle();
-      m_charDetails.SetCount(0);
-      m_rsxform.setCount(0);
-      if (pFont->GetFaceRec())
-        m_pTypeFace.reset(SkSafeRef(pFont->GetDeviceCache()));
-      else
-        m_pTypeFace.reset();
-      m_fontSize = font_size;
-      m_scaleX = scaleX;
-      m_fillColor = color;
-      m_drawMatrix = matrix;
-      m_drawIndex = m_commandIndex;
-      m_type = Accumulator::kText;
-      m_pFont = pFont;
-    }
-    if (!hasRSX && !m_rsxform.isEmpty())
-      FlushText();
-
-    int count = m_charDetails.Count();
-    m_charDetails.SetCount(nChars + count);
-    if (hasRSX)
-      m_rsxform.setCount(nChars + count);
-
-    SkScalar flip = m_fontSize < 0 ? -1 : 1;
-    SkScalar vFlip = flip;
-    if (pFont->IsVertical())
-      vFlip *= -1;
-    for (int index = 0; index < nChars; ++index) {
-      const TextCharPos& cp = pCharPos[index];
-      int cur_index = index + count;
-      m_charDetails.SetPositionAt(
-          cur_index, {cp.m_Origin.x * flip, cp.m_Origin.y * vFlip});
-      m_charDetails.SetGlyphAt(cur_index,
-                               static_cast<uint16_t>(cp.m_GlyphIndex));
-      m_charDetails.SetFontCharWidthAt(cur_index, cp.m_FontCharWidth);
-#if defined(OS_MACOSX)
-      if (cp.m_ExtGID) {
-        m_charDetails.SetGlyphAt(cur_index, static_cast<uint16_t>(cp.m_ExtGID));
-      }
-#endif
-    }
-    SkPoint delta;
-    if (MatrixOffset(&matrix, &delta)) {
-      for (int index = 0; index < nChars; ++index) {
-        m_charDetails.OffsetPositionAt(index + count, delta.fX * flip,
-                                       -delta.fY * flip);
-      }
-    }
-    if (hasRSX) {
-      const SkTDArray<SkPoint>& positions = m_charDetails.GetPositions();
-      for (int index = 0; index < nChars; ++index) {
-        const TextCharPos& cp = pCharPos[index];
-        SkRSXform* rsxform = &m_rsxform[index + count];
-        if (cp.m_bGlyphAdjust) {
-          rsxform->fSCos = cp.m_AdjustMatrix[0];
-          rsxform->fSSin = cp.m_AdjustMatrix[1];
-          rsxform->fTx = cp.m_AdjustMatrix[0] * positions[index].fX;
-          rsxform->fTy = cp.m_AdjustMatrix[1] * positions[index].fY;
-        } else {
-          rsxform->fSCos = 1;
-          rsxform->fSSin = 0;
-          rsxform->fTx = positions[index].fX;
-          rsxform->fTy = positions[index].fY;
-        }
-      }
-    }
-    return true;
-  }
-
-  void FlushText() {
-    Dump(__func__);
-    SkPaint skPaint;
-    skPaint.setAntiAlias(true);
-    skPaint.setColor(m_fillColor);
-
-    SkFont font;
-    if (m_pTypeFace) {  // exclude placeholder test fonts
-      font.setTypeface(m_pTypeFace);
-    }
-    font.setHinting(SkFontHinting::kNone);
-    font.setScaleX(m_scaleX);
-    font.setSkewX(tanf(m_italicAngle * FX_PI / 180.0));
-    font.setSize(SkTAbs(m_fontSize));
-    font.setSubpixel(true);
-
-    SkCanvas* skCanvas = m_pDriver->SkiaCanvas();
-    SkAutoCanvasRestore scoped_save_restore(skCanvas, /*doSave=*/true);
-    SkScalar flip = m_fontSize < 0 ? -1 : 1;
-    SkMatrix skMatrix = ToFlippedSkMatrix(m_drawMatrix, flip);
-    skCanvas->concat(skMatrix);
-    const SkTDArray<uint16_t>& glyphs = m_charDetails.GetGlyphs();
-#ifdef _SKIA_SUPPORT_PATHS_
-    m_pDriver->PreMultiply();
-#endif  // _SKIA_SUPPORT_PATHS_
-#if SHOW_TEXT_GLYPHS
-    SkTDArray<SkUnichar> text;
-    // TODO(nigi): |m_glyphs| are deprecated and glyphToUnichars() takes 4
-    // parameters now.
-    text.setCount(m_glyphs.count());
-    skPaint.glyphsToUnichars(m_glyphs.begin(), m_glyphs.count(), text.begin());
-    for (int i = 0; i < m_glyphs.count(); ++i)
-      printf("%lc", m_glyphs[i]);
-    printf("\n");
-#endif
-
-    if (m_rsxform.count()) {
-      sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromRSXform(
-          glyphs.begin(), glyphs.bytes(), m_rsxform.begin(), font,
-          SkTextEncoding::kGlyphID);
-      skCanvas->drawTextBlob(blob, 0, 0, skPaint);
-    } else {
-      const SkTDArray<SkPoint>& positions = m_charDetails.GetPositions();
-      const SkTDArray<uint32_t>& widths = m_charDetails.GetFontCharWidths();
-      for (int i = 0; i < m_charDetails.Count(); ++i) {
-        uint32_t font_glyph_width =
-            m_pFont ? m_pFont->GetGlyphWidth(glyphs[i]) : 0;
-        uint32_t pdf_glyph_width = widths[i];
-        if (font_glyph_width && pdf_glyph_width &&
-            font_glyph_width > pdf_glyph_width) {
-          font.setScaleX(SkIntToScalar(pdf_glyph_width) / font_glyph_width);
-        } else {
-          font.setScaleX(SkIntToScalar(1));
-        }
-        sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromText(
-            &glyphs[i], sizeof(glyphs[i]), font, SkTextEncoding::kGlyphID);
-        skCanvas->drawTextBlob(blob, positions[i].fX, positions[i].fY, skPaint);
-      }
-    }
-
-    m_drawIndex = INT_MAX;
-    m_type = Accumulator::kNone;
-    m_drawMatrix = CFX_Matrix();
-    m_pFont = nullptr;
-    m_italicAngle = 0;
-  }
-
-  bool IsEmpty() const { return !m_commands.count(); }
-
-  bool SetClipFill(const CFX_PathData* pPathData,
-                   const CFX_Matrix* pMatrix,
-                   int fill_mode) {
-    if (m_debugDisable)
-      return false;
-    Dump(__func__);
-    SkPath skClipPath;
-    if (pPathData->GetPoints().size() == 5 ||
-        pPathData->GetPoints().size() == 4) {
-      CFX_FloatRect rectf;
-      if (pPathData->IsRect(pMatrix, &rectf)) {
-        rectf.Intersect(CFX_FloatRect(
-            0, 0,
-            static_cast<float>(m_pDriver->GetDeviceCaps(FXDC_PIXEL_WIDTH)),
-            static_cast<float>(m_pDriver->GetDeviceCaps(FXDC_PIXEL_HEIGHT))));
-        FX_RECT outer = rectf.GetOuterRect();
-        // note that PDF's y-axis goes up; Skia's y-axis goes down
-        skClipPath.addRect({(float)outer.left, (float)outer.bottom,
-                            (float)outer.right, (float)outer.top});
-      }
-    }
-    if (skClipPath.isEmpty()) {
-      skClipPath = BuildPath(pPathData);
-      skClipPath.setFillType(GetAlternateOrWindingFillType(fill_mode));
-      SkMatrix skMatrix = ToSkMatrix(*pMatrix);
-      skClipPath.transform(skMatrix);
-    }
-    return SetClip(skClipPath);
-  }
-
-  bool SetClip(const SkPath& skClipPath) {
-    // if a pending draw depends on clip state that is cached, flush it and draw
-    if (m_commandIndex < m_commands.count()) {
-      if (m_commands[m_commandIndex] == Clip::kPath &&
-          m_clips[m_commandIndex] == skClipPath) {
-        ++m_commandIndex;
-        return true;
-      }
-      Flush();
-    }
-    while (m_clipIndex > m_commandIndex) {
-      do {
-        --m_clipIndex;
-        ASSERT(m_clipIndex >= 0);
-      } while (m_commands[m_clipIndex] != Clip::kSave);
-      m_pDriver->SkiaCanvas()->restore();
-    }
-    if (m_commandIndex < m_commands.count()) {
-      m_commands[m_commandIndex] = Clip::kPath;
-      m_clips[m_commandIndex] = skClipPath;
-    } else {
-      m_commands.push_back(Clip::kPath);
-      m_clips.push_back(skClipPath);
-    }
-    ++m_commandIndex;
-    return true;
-  }
-
-  bool SetClipStroke(const CFX_PathData* pPathData,
-                     const CFX_Matrix* pMatrix,
-                     const CFX_GraphStateData* pGraphState) {
-    if (m_debugDisable)
-      return false;
-    Dump(__func__);
-    SkPath skPath = BuildPath(pPathData);
-    SkMatrix skMatrix = ToSkMatrix(*pMatrix);
-    SkPaint skPaint;
-    m_pDriver->PaintStroke(&skPaint, pGraphState, skMatrix);
-    SkPath dst_path;
-    skPaint.getFillPath(skPath, &dst_path);
-    dst_path.transform(skMatrix);
-    return SetClip(dst_path);
-  }
-
-  bool MatrixOffset(const CFX_Matrix* pMatrix, SkPoint* delta) {
-    CFX_Matrix identityMatrix;
-    if (!pMatrix)
-      pMatrix = &identityMatrix;
-    delta->set(pMatrix->e - m_drawMatrix.e, pMatrix->f - m_drawMatrix.f);
-    if (!delta->fX && !delta->fY)
-      return true;
-    SkMatrix drawMatrix = ToSkMatrix(m_drawMatrix);
-    if (!(drawMatrix.getType() & ~SkMatrix::kTranslate_Mask))
-      return true;
-    SkMatrix invDrawMatrix;
-    if (!drawMatrix.invert(&invDrawMatrix))
-      return false;
-    SkMatrix invNewMatrix;
-    SkMatrix newMatrix = ToSkMatrix(*pMatrix);
-    if (!newMatrix.invert(&invNewMatrix))
-      return false;
-    delta->set(invDrawMatrix.getTranslateX() - invNewMatrix.getTranslateX(),
-               invDrawMatrix.getTranslateY() - invNewMatrix.getTranslateY());
-    return true;
-  }
-
-  // returns true if caller should apply command to skia canvas
-  bool ClipSave() {
-    if (m_debugDisable)
-      return false;
-    Dump(__func__);
-    int count = m_commands.count();
-    if (m_commandIndex < count) {
-      if (Clip::kSave == m_commands[m_commandIndex]) {
-        ++m_commandIndex;
-        return true;
-      }
-      Flush();
-      AdjustClip(m_commandIndex);
-      m_commands[m_commandIndex] = Clip::kSave;
-      m_clips[m_commandIndex] = m_skEmptyPath;
-    } else {
-      AdjustClip(m_commandIndex);
-      m_commands.push_back(Clip::kSave);
-      m_clips.push_back(m_skEmptyPath);
-    }
-    ++m_commandIndex;
-    return true;
-  }
-
-  bool ClipRestore() {
-    if (m_debugDisable)
-      return false;
-    Dump(__func__);
-    while (Clip::kSave != m_commands[--m_commandIndex]) {
-      ASSERT(m_commandIndex > 0);
-    }
-    return true;
-  }
-
-  bool DrawChanged(const CFX_Matrix* pMatrix,
-                   const CFX_GraphStateData* pState,
-                   uint32_t fill_color,
-                   uint32_t stroke_color,
-                   int fill_mode,
-                   BlendMode blend_type,
-                   bool group_knockout) const {
-    return MatrixChanged(pMatrix) || StateChanged(pState, m_drawState) ||
-           fill_color != m_fillColor || stroke_color != m_strokeColor ||
-           IsEvenOddFillType(m_skPath.getFillType()) ||
-           IsAlternateFillMode(fill_mode) || blend_type != m_blendType ||
-           group_knockout != m_groupKnockout;
-  }
-
-  bool FontChanged(CFX_Font* pFont,
-                   const CFX_Matrix& matrix,
-                   float font_size,
-                   float scaleX,
-                   uint32_t color) const {
-    CFX_TypeFace* typeface =
-        pFont->GetFaceRec() ? pFont->GetDeviceCache() : nullptr;
-    return typeface != m_pTypeFace.get() || MatrixChanged(&matrix) ||
-           font_size != m_fontSize || scaleX != m_scaleX ||
-           color != m_fillColor ||
-           pFont->GetSubstFontItalicAngle() != m_italicAngle;
-  }
-
-  bool MatrixChanged(const CFX_Matrix* pMatrix) const {
-    return pMatrix ? *pMatrix != m_drawMatrix : m_drawMatrix.IsIdentity();
-  }
-
-  bool StateChanged(const CFX_GraphStateData* pState,
-                    const CFX_GraphStateData& refState) const {
-    CFX_GraphStateData identityState;
-    if (!pState)
-      pState = &identityState;
-    return pState->m_LineWidth != refState.m_LineWidth ||
-           pState->m_LineCap != refState.m_LineCap ||
-           pState->m_LineJoin != refState.m_LineJoin ||
-           pState->m_MiterLimit != refState.m_MiterLimit ||
-           DashChanged(pState, refState);
-  }
-
-  bool DashChanged(const CFX_GraphStateData* pState,
-                   const CFX_GraphStateData& refState) const {
-    bool dashArray = pState && !pState->m_DashArray.empty();
-    if (!dashArray && refState.m_DashArray.empty())
-      return false;
-    if (!dashArray || refState.m_DashArray.empty())
-      return true;
-    return pState->m_DashPhase != refState.m_DashPhase ||
-           pState->m_DashArray != refState.m_DashArray;
-  }
-
-  void AdjustClip(int limit) {
-    while (m_clipIndex > limit) {
-      do {
-        --m_clipIndex;
-        ASSERT(m_clipIndex >= 0);
-      } while (m_commands[m_clipIndex] != Clip::kSave);
-      m_pDriver->SkiaCanvas()->restore();
-    }
-    while (m_clipIndex < limit) {
-      if (Clip::kSave == m_commands[m_clipIndex]) {
-        m_pDriver->SkiaCanvas()->save();
-      } else {
-        ASSERT(Clip::kPath == m_commands[m_clipIndex]);
-        m_pDriver->SkiaCanvas()->clipPath(m_clips[m_clipIndex],
-                                          SkClipOp::kIntersect, true);
-      }
-      ++m_clipIndex;
-    }
-  }
-
-  void Flush() {
-    if (m_debugDisable)
-      return;
-    Dump(__func__);
-    if (Accumulator::kPath == m_type || Accumulator::kText == m_type) {
-      AdjustClip(SkTMin(m_drawIndex, m_commands.count()));
-      Accumulator::kPath == m_type ? FlushPath() : FlushText();
-    }
-  }
-
-  void FlushForDraw() {
-    if (m_debugDisable)
-      return;
-    Flush();                     // draw any pending text or path
-    AdjustClip(m_commandIndex);  // set up clip stack with any pending state
-  }
-
-#if SHOW_SKIA_PATH
-  void DumpPrefix(int index) const {
-    if (index != m_commandIndex && index != m_drawIndex &&
-        index != m_clipIndex) {
-      printf("     ");
-      return;
-    }
-    printf("%c%c%c> ", index == m_commandIndex ? 'x' : '-',
-           index == m_drawIndex ? 'd' : '-', index == m_clipIndex ? 'c' : '-');
-  }
-
-  void DumpEndPrefix() const {
-    int index = m_commands.count();
-    if (index != m_commandIndex && index > m_drawIndex && index != m_clipIndex)
-      return;
-    printf("%c%c%c>\n", index == m_commandIndex ? 'x' : '-',
-           index <= m_drawIndex ? 'd' : '-', index == m_clipIndex ? 'c' : '-');
-  }
-#endif  // SHOW_SKIA_PATH
-
-  void Dump(const char* where) const {
-#if SHOW_SKIA_PATH
-    if (m_debugDisable)
-      return;
-    printf(
-        "\n%s\nSkia Save Count %d  Agg Save Stack/Count %d/%d"
-        "  Cache Save Index/Count %d/%d\n",
-        where, m_pDriver->SkiaCanvas()->getSaveCount(),
-        (int)m_pDriver->stack().size(), AggSaveCount(m_pDriver), m_commandIndex,
-        CacheSaveCount(m_commands, m_commandIndex));
-    printf("Cache:\n");
-#if SHOW_SKIA_PATH_SHORTHAND
-    bool dumpedPath = false;
-#endif
-    for (int index = 0; index < m_commands.count(); ++index) {
-#if SHOW_SKIA_PATH_SHORTHAND
-      if (Clip::kSave == m_commands[index] && dumpedPath) {
-        printf("\n");
-        dumpedPath = false;
-      }
-#endif
-      DumpPrefix(index);
-      switch (m_commands[index]) {
-        case Clip::kSave:
-          printf("Save %d\n", ++m_debugSaveCounter);
-          break;
-        case Clip::kPath:
-#if SHOW_SKIA_PATH_SHORTHAND
-          printf("*");
-          dumpedPath = true;
-#else
-          m_clips[index].dump();
-#endif
-          break;
-        default:
-          printf("unknown\n");
-      }
-    }
-#if SHOW_SKIA_PATH_SHORTHAND
-    if (dumpedPath)
-      printf("\n");
-#endif
-    DumpEndPrefix();
-    int skCanvasSaveCount = m_pDriver->SkiaCanvas()->getSaveCount();
-    int cacheSaveCount = 1;
-    ASSERT(m_clipIndex <= m_commands.count());
-    for (int index = 0; index < m_clipIndex; ++index)
-      cacheSaveCount += Clip::kSave == m_commands[index];
-    ASSERT(skCanvasSaveCount == cacheSaveCount);
-#endif  // SHOW_SKIA_PATH
-  }
-
-#if SHOW_SKIA_PATH
-  static int AggSaveCount(const UnownedPtr<CFX_SkiaDeviceDriver> driver) {
-    FX_RECT last;
-    int aggSaveCount = 0;
-    bool foundLast = false;
-    for (int index = 0; index < (int)driver->stack().size(); ++index) {
-      if (!driver->stack()[index]) {
-        continue;
-      }
-      if (driver->stack()[index]->GetType() != CFX_ClipRgn::RectI) {
-        aggSaveCount += 1;
-        foundLast = false;
-        continue;
-      }
-      if (!foundLast ||
-          memcmp(&last, &driver->stack()[index]->GetBox(), sizeof(FX_RECT))) {
-        aggSaveCount += 1;
-        foundLast = true;
-        last = driver->stack()[index]->GetBox();
-      }
-    }
-    if (driver->clip_region()) {
-      CFX_ClipRgn::ClipType clipType = driver->clip_region()->GetType();
-      if (clipType != CFX_ClipRgn::RectI || !foundLast ||
-          memcmp(&last, &driver->clip_region()->GetBox(), sizeof(FX_RECT))) {
-        aggSaveCount += 1;
-      }
-    }
-    return aggSaveCount;
-  }
-
-  static int CacheSaveCount(const SkTDArray<SkiaState::Clip>& commands,
-                            int commandIndex) {
-    int cacheSaveCount = 0;
-    bool newPath = false;
-    for (int index = 0; index < commandIndex; ++index) {
-      if (Clip::kSave == commands[index]) {
-        newPath = true;
-      } else if (newPath) {
-        ++cacheSaveCount;
-        newPath = false;
-      }
-    }
-    return cacheSaveCount;
-  }
-#endif
-
-  void DebugCheckClip() {
-#if SHOW_SKIA_PATH
-    if (m_debugDisable)
-      return;
-    int aggSaveCount = AggSaveCount(m_pDriver);
-    int cacheSaveCount = CacheSaveCount(m_commands, m_commandIndex);
-    ASSERT(m_clipIndex <= m_commands.count());
-    if (aggSaveCount != cacheSaveCount) {
-      // may not signify a bug if counts don't match
-      printf("aggSaveCount %d != cacheSaveCount %d\n", aggSaveCount,
-             cacheSaveCount);
-      DumpClipStacks();
-    }
-    for (int aggIndex = 0; aggIndex < (int)m_pDriver->stack().size();
-         ++aggIndex) {
-      if (!m_pDriver->stack()[aggIndex])
-        continue;
-      if (m_pDriver->stack()[aggIndex]->GetType() != CFX_ClipRgn::RectI)
-        continue;
-      const FX_RECT& aggRect = m_pDriver->stack()[aggIndex]->GetBox();
-      SkRect skRect = SkRect::MakeLTRB(aggRect.left, aggRect.top, aggRect.right,
-                                       aggRect.bottom);
-      bool foundMatch = false;
-      for (int skIndex = 0; skIndex < m_commandIndex; ++skIndex) {
-        if (Clip::kPath != m_commands[skIndex])
-          continue;
-        const SkPath& clip = m_clips[skIndex];
-        SkRect bounds;
-        if (!clip.isRect(&bounds))
-          continue;
-        bounds.roundOut(&bounds);
-        if (skRect == bounds) {
-          foundMatch = true;
-          break;
-        }
-      }
-      if (!foundMatch) {
-        DumpClipStacks();
-        NOTREACHED();
-      }
-    }
-#endif  // SHOW_SKIA_PATH
-  }
-
-#if SHOW_SKIA_PATH
-  void DumpClipStacks() const {
-    if (m_debugDisable)
-      return;
-    printf("\ncache\n");
-    for (int index = 0; index < m_commandIndex; ++index) {
-      DumpPrefix(index);
-      switch (m_commands[index]) {
-        case Clip::kSave:
-          printf("Save\n");
-          break;
-        case Clip::kPath:
-          m_clips[index].dump();
-          break;
-        default:
-          printf("unknown\n");
-      }
-    }
-    printf("\nagg\n");
-    for (int index = 0; index < (int)m_pDriver->stack().size(); ++index) {
-      if (!m_pDriver->stack()[index]) {
-        printf("null\n");
-        continue;
-      }
-      CFX_ClipRgn::ClipType clipType = m_pDriver->stack()[index]->GetType();
-      const FX_RECT& box = m_pDriver->stack()[index]->GetBox();
-      printf("stack rect: %d,%d,%d,%d mask=%s\n", box.left, box.top, box.right,
-             box.bottom,
-             CFX_ClipRgn::MaskF == clipType
-                 ? "1"
-                 : CFX_ClipRgn::RectI == clipType ? "0" : "?");
-    }
-    if (m_pDriver->clip_region()) {
-      const FX_RECT& box = m_pDriver->clip_region()->GetBox();
-      CFX_ClipRgn::ClipType clipType = m_pDriver->clip_region()->GetType();
-      printf("clip rect: %d,%d,%d,%d mask=%s\n", box.left, box.top, box.right,
-             box.bottom,
-             CFX_ClipRgn::MaskF == clipType
-                 ? "1"
-                 : CFX_ClipRgn::RectI == clipType ? "0" : "?");
-    }
-  }
-#endif  // SHOW_SKIA_PATH
-
- private:
-  class CharDetail {
-   public:
-    CharDetail() = default;
-    ~CharDetail() = default;
-
-    const SkTDArray<SkPoint>& GetPositions() const { return m_positions; }
-    void SetPositionAt(int index, const SkPoint& position) {
-      m_positions[index] = position;
-    }
-    void OffsetPositionAt(int index, SkScalar dx, SkScalar dy) {
-      m_positions[index].offset(dx, dy);
-    }
-    const SkTDArray<uint16_t>& GetGlyphs() const { return m_glyphs; }
-    void SetGlyphAt(int index, uint16_t glyph) { m_glyphs[index] = glyph; }
-    const SkTDArray<uint32_t>& GetFontCharWidths() const {
-      return m_fontCharWidths;
-    }
-    void SetFontCharWidthAt(int index, uint32_t width) {
-      m_fontCharWidths[index] = width;
-    }
-    int Count() const {
-      ASSERT(m_positions.count() == m_glyphs.count());
-      return m_glyphs.count();
-    }
-    void SetCount(int count) {
-      ASSERT(count >= 0);
-      m_positions.setCount(count);
-      m_glyphs.setCount(count);
-      m_fontCharWidths.setCount(count);
-    }
-
-   private:
-    SkTDArray<SkPoint> m_positions;  // accumulator for text positions
-    SkTDArray<uint16_t> m_glyphs;    // accumulator for text glyphs
-    // accumulator for glyphs' width defined in pdf
-    SkTDArray<uint32_t> m_fontCharWidths;
-  };
-
-  SkTArray<SkPath> m_clips;        // stack of clips that may be reused
-  SkTDArray<Clip> m_commands;      // stack of clip-related commands
-  CharDetail m_charDetails;
-  SkTDArray<SkRSXform> m_rsxform;  // accumulator for txt rotate/scale/translate
-  SkPath m_skPath;                 // accumulator for path contours
-  SkPath m_skEmptyPath;            // used as placehold in the clips array
-  UnownedPtr<CFX_Font> m_pFont;
-  CFX_Matrix m_drawMatrix;
-  CFX_GraphStateData m_clipState;
-  CFX_GraphStateData m_drawState;
-  CFX_Matrix m_clipMatrix;
-  UnownedPtr<CFX_SkiaDeviceDriver> const m_pDriver;
-  sk_sp<CFX_TypeFace> m_pTypeFace;
-  float m_fontSize = 0;
-  float m_scaleX = 0;
-  uint32_t m_fillColor = 0;
-  uint32_t m_strokeColor = 0;
-  BlendMode m_blendType = BlendMode::kNormal;
-  int m_commandIndex = 0;     // active position in clip command stack
-  int m_drawIndex = INT_MAX;  // position of the pending path or text draw
-  int m_clipIndex = 0;        // position reflecting depth of canvas clip stacck
-  int m_italicAngle = 0;
-  Accumulator m_type = Accumulator::kNone;  // type of pending draw
-  bool m_fillFullCover = false;
-  bool m_fillPath = false;
-  bool m_groupKnockout = false;
-  bool m_debugDisable = false;  // turn off cache for debugging
-#if SHOW_SKIA_PATH
- public:
-  mutable int m_debugSaveCounter = 0;
-  static int m_debugInitCounter;
-#endif
-};
-
-#if SHOW_SKIA_PATH
-int SkiaState::m_debugInitCounter;
-#endif
-
-// convert a stroking path to scanlines
-void CFX_SkiaDeviceDriver::PaintStroke(SkPaint* spaint,
-                                       const CFX_GraphStateData* pGraphState,
-                                       const SkMatrix& matrix) {
-  SkPaint::Cap cap;
-  switch (pGraphState->m_LineCap) {
-    case CFX_GraphStateData::LineCapRound:
-      cap = SkPaint::kRound_Cap;
-      break;
-    case CFX_GraphStateData::LineCapSquare:
-      cap = SkPaint::kSquare_Cap;
-      break;
-    default:
-      cap = SkPaint::kButt_Cap;
-      break;
-  }
-  SkPaint::Join join;
-  switch (pGraphState->m_LineJoin) {
-    case CFX_GraphStateData::LineJoinRound:
-      join = SkPaint::kRound_Join;
-      break;
-    case CFX_GraphStateData::LineJoinBevel:
-      join = SkPaint::kBevel_Join;
-      break;
-    default:
-      join = SkPaint::kMiter_Join;
-      break;
-  }
-  SkMatrix inverse;
-  if (!matrix.invert(&inverse))
-    return;  // give up if the matrix is degenerate, and not invertable
-  inverse.set(SkMatrix::kMTransX, 0);
-  inverse.set(SkMatrix::kMTransY, 0);
-  SkVector deviceUnits[2] = {{0, 1}, {1, 0}};
-  inverse.mapPoints(deviceUnits, SK_ARRAY_COUNT(deviceUnits));
-  float width =
-      SkTMax(pGraphState->m_LineWidth,
-             SkTMin(deviceUnits[0].length(), deviceUnits[1].length()));
-  if (!pGraphState->m_DashArray.empty()) {
-    size_t count = (pGraphState->m_DashArray.size() + 1) / 2;
-    std::unique_ptr<SkScalar, FxFreeDeleter> intervals(
-        FX_Alloc2D(SkScalar, count, sizeof(SkScalar)));
-    // Set dash pattern
-    for (size_t i = 0; i < count; i++) {
-      float on = pGraphState->m_DashArray[i * 2];
-      if (on <= 0.000001f)
-        on = 1.f / 10;
-      float off = i * 2 + 1 == pGraphState->m_DashArray.size()
-                      ? on
-                      : pGraphState->m_DashArray[i * 2 + 1];
-      if (off < 0)
-        off = 0;
-      intervals.get()[i * 2] = on;
-      intervals.get()[i * 2 + 1] = off;
-    }
-    spaint->setPathEffect(SkDashPathEffect::Make(intervals.get(), count * 2,
-                                                 pGraphState->m_DashPhase));
-  }
-  spaint->setStyle(SkPaint::kStroke_Style);
-  spaint->setAntiAlias(true);
-  spaint->setStrokeWidth(width);
-  spaint->setStrokeMiter(pGraphState->m_MiterLimit);
-  spaint->setStrokeCap(cap);
-  spaint->setStrokeJoin(join);
+  return driver;
 }
 
 CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(
-    const RetainPtr<CFX_DIBitmap>& pBitmap,
+    RetainPtr<CFX_DIBitmap> pBitmap,
     bool bRgbByteOrder,
-    const RetainPtr<CFX_DIBitmap>& pBackdropBitmap,
+    RetainPtr<CFX_DIBitmap> pBackdropBitmap,
     bool bGroupKnockout)
-    : m_pBitmap(pBitmap),
+    : m_pBitmap(std::move(pBitmap)),
       m_pBackdropBitmap(pBackdropBitmap),
-      m_pRecorder(nullptr),
-      m_pCache(new SkiaState(this)),
-#ifdef _SKIA_SUPPORT_PATHS_
-      m_pClipRgn(nullptr),
-      m_FillFlags(0),
       m_bRgbByteOrder(bRgbByteOrder),
-#endif  // _SKIA_SUPPORT_PATHS_
       m_bGroupKnockout(bGroupKnockout) {
-  SkBitmap skBitmap;
-  ASSERT(pBitmap->GetBPP() == 8 || pBitmap->GetBPP() == 32);
-  SkImageInfo imageInfo = SkImageInfo::Make(
-      pBitmap->GetWidth(), pBitmap->GetHeight(),
-      pBitmap->GetBPP() == 8 ? kAlpha_8_SkColorType : kN32_SkColorType,
-      kOpaque_SkAlphaType);
-  skBitmap.installPixels(imageInfo, pBitmap->GetBuffer(), pBitmap->GetPitch());
-  m_pCanvas = new SkCanvas(skBitmap);
+  SkColorType color_type;
+  const int bpp = m_pBitmap->GetBPP();
+  if (bpp == 8) {
+    color_type = m_pBitmap->IsAlphaFormat() || m_pBitmap->IsMaskFormat()
+                     ? kAlpha_8_SkColorType
+                     : kGray_8_SkColorType;
+  } else if (bpp == 24) {
+    DCHECK_EQ(m_pBitmap->GetFormat(), FXDIB_Format::kRgb);
+
+    // Save the input bitmap as `m_pOriginalBitmap` and save its 32 bpp
+    // equivalent at `m_pBitmap` for Skia's internal process.
+    m_pOriginalBitmap = std::move(m_pBitmap);
+    m_pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+    if (!m_pBitmap->Copy(m_pOriginalBitmap) ||
+        !m_pBitmap->ConvertFormat(FXDIB_Format::kArgb)) {
+      // Skip creating SkCanvas if we fail to create the 32 bpp bitmap to back
+      // it.
+      return;
+    }
+
+    color_type = Get32BitSkColorType(bRgbByteOrder);
+  } else {
+    DCHECK_EQ(bpp, 32);
+    color_type = Get32BitSkColorType(bRgbByteOrder);
+  }
+
+  SkImageInfo imageInfo =
+      SkImageInfo::Make(m_pBitmap->GetWidth(), m_pBitmap->GetHeight(),
+                        color_type, kPremul_SkAlphaType);
+  surface_ = SkSurfaces::WrapPixels(
+      imageInfo, m_pBitmap->GetWritableBuffer().data(), m_pBitmap->GetPitch());
+  m_pCanvas = surface_->getCanvas();
 }
 
-#ifdef _SKIA_SUPPORT_
-CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(int size_x, int size_y)
-    : m_pBitmap(nullptr),
-      m_pBackdropBitmap(nullptr),
-      m_pRecorder(new SkPictureRecorder),
-      m_pCache(new SkiaState(this)),
-      m_bGroupKnockout(false) {
-  m_pRecorder->beginRecording(SkIntToScalar(size_x), SkIntToScalar(size_y));
-  m_pCanvas = m_pRecorder->getRecordingCanvas();
-}
+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());
 
-CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(SkPictureRecorder* recorder)
-    : m_pBitmap(nullptr),
-      m_pBackdropBitmap(nullptr),
-      m_pRecorder(recorder),
-      m_pCache(new SkiaState(this)),
-      m_bGroupKnockout(false) {
-  m_pCanvas = m_pRecorder->getRecordingCanvas();
+  constexpr uint32_t kMagenta = 0xffff00ff;
+  constexpr uint32_t kGreen = 0xff00ff00;
+  m_pBitmap = MakeDebugBitmap(width, height, kMagenta);
+  m_pBackdropBitmap = MakeDebugBitmap(width, height, kGreen);
 }
-#endif  // _SKIA_SUPPORT_
 
 CFX_SkiaDeviceDriver::~CFX_SkiaDeviceDriver() {
-  Flush();
-  if (!m_pRecorder)
-    delete m_pCanvas;
+  // Convert and transfer the internal processed result to the original 24 bpp
+  // bitmap provided by the render device.
+  if (m_pOriginalBitmap && m_pBitmap->ConvertFormat(FXDIB_Format::kRgb)) {
+    int width = m_pOriginalBitmap->GetWidth();
+    int height = m_pOriginalBitmap->GetHeight();
+    DCHECK_EQ(width, m_pBitmap->GetWidth());
+    DCHECK_EQ(height, m_pBitmap->GetHeight());
+    DCHECK_EQ(FXDIB_Format::kRgb, m_pOriginalBitmap->GetFormat());
+    m_pOriginalBitmap->TransferBitmap(/*dest_left=*/0, /*dest_top=*/0, width,
+                                      height, m_pBitmap, /*src_left=*/0,
+                                      /*src_top=*/0);
+  }
 }
 
-void CFX_SkiaDeviceDriver::Flush() {
-  m_pCache->Flush();
-}
+bool CFX_SkiaDeviceDriver::DrawDeviceText(
+    pdfium::span<const TextCharPos> pCharPos,
+    CFX_Font* pFont,
+    const CFX_Matrix& mtObject2Device,
+    float font_size,
+    uint32_t color,
+    const CFX_TextRenderOptions& options) {
+  // `SkTextBlob` is built from `pFont`'s font data. If `pFont` doesn't contain
+  // any font data, each text blob will have zero area to be drawn and the
+  // drawing command will be rejected. In this case, we fall back to drawing
+  // characters by their glyph bitmaps.
+  if (pFont->GetFontSpan().empty())
+    return false;
 
-void CFX_SkiaDeviceDriver::PreMultiply() {
-  m_pBitmap->PreMultiply();
-}
-
-bool CFX_SkiaDeviceDriver::DrawDeviceText(int nChars,
-                                          const TextCharPos* pCharPos,
-                                          CFX_Font* pFont,
-                                          const CFX_Matrix& mtObject2Device,
-                                          float font_size,
-                                          uint32_t color) {
-  if (m_pCache->DrawText(nChars, pCharPos, pFont, mtObject2Device, font_size,
-                         color)) {
+  if (TryDrawText(pCharPos, pFont, mtObject2Device, font_size, color,
+                  options)) {
     return true;
   }
   sk_sp<SkTypeface> typeface(SkSafeRef(pFont->GetDeviceCache()));
@@ -1669,123 +764,58 @@
 
   SkFont font;
   font.setTypeface(typeface);
+  font.setEmbolden(pFont->IsSubstFontBold());
   font.setHinting(SkFontHinting::kNone);
   font.setSize(SkTAbs(font_size));
   font.setSubpixel(true);
-  font.setSkewX(tanf(pFont->GetSubstFontItalicAngle() * FX_PI / 180.0));
+  font.setSkewX(tanf(pFont->GetSubstFontItalicAngle() * FXSYS_PI / 180.0));
+  font.setEdging(GetFontEdgingType(options));
 
   SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
-  SkScalar flip = font_size < 0 ? -1 : 1;
-  SkScalar vFlip = flip;
-  if (pFont->IsVertical())
-    vFlip *= -1;
+  const SkScalar flip = font_size < 0 ? -1 : 1;
+  const SkScalar vFlip = pFont->IsVertical() ? -1 : 1;
   SkMatrix skMatrix = ToFlippedSkMatrix(mtObject2Device, flip);
   m_pCanvas->concat(skMatrix);
-  SkTDArray<SkPoint> positions;
-  positions.setCount(nChars);
-  SkTDArray<uint16_t> glyphs;
-  glyphs.setCount(nChars);
-  bool useRSXform = false;
-  bool oneAtATime = false;
-  for (int index = 0; index < nChars; ++index) {
+  DataVector<SkPoint> positions(pCharPos.size());
+  DataVector<uint16_t> glyphs(pCharPos.size());
+
+  for (size_t index = 0; index < pCharPos.size(); ++index) {
     const TextCharPos& cp = pCharPos[index];
     positions[index] = {cp.m_Origin.x * flip, cp.m_Origin.y * vFlip};
-    if (cp.m_bGlyphAdjust) {
-      useRSXform = true;
-      if (cp.m_AdjustMatrix[0] != cp.m_AdjustMatrix[3] ||
-          cp.m_AdjustMatrix[1] != -cp.m_AdjustMatrix[2]) {
-        oneAtATime = true;
-      }
-    }
     glyphs[index] = static_cast<uint16_t>(cp.m_GlyphIndex);
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
     if (cp.m_ExtGID)
       glyphs[index] = static_cast<uint16_t>(cp.m_ExtGID);
 #endif
   }
-  if (oneAtATime)
-    useRSXform = false;
-#if SHOW_TEXT_GLYPHS
-  SkTDArray<SkUnichar> text;
-  text.setCount(glyphs.count());
-  paint.glyphsToUnichars(glyphs.begin(), glyphs.count(), text.begin());
-  for (int i = 0; i < glyphs.count(); ++i)
-    printf("%lc", text[i]);
-  printf("\n");
-#endif
-#ifdef _SKIA_SUPPORT_PATHS_
-  m_pBitmap->PreMultiply();
-#endif  // _SKIA_SUPPORT_PATHS_
-  if (useRSXform) {
-    SkTDArray<SkRSXform> xforms;
-    xforms.setCount(nChars);
-    for (int index = 0; index < nChars; ++index) {
-      const TextCharPos& cp = pCharPos[index];
-      SkRSXform* rsxform = &xforms[index];
-      if (cp.m_bGlyphAdjust) {
-        rsxform->fSCos = cp.m_AdjustMatrix[0];
-        rsxform->fSSin = cp.m_AdjustMatrix[1];
-        rsxform->fTx = cp.m_AdjustMatrix[0] * positions[index].fX;
-        rsxform->fTy = cp.m_AdjustMatrix[1] * positions[index].fY;
-      } else {
-        rsxform->fSCos = 1;
-        rsxform->fSSin = 0;
-        rsxform->fTx = positions[index].fX;
-        rsxform->fTy = positions[index].fY;
-      }
-    }
-    m_pCanvas->drawTextBlob(
-        SkTextBlob::MakeFromRSXform(glyphs.begin(), nChars * 2, xforms.begin(),
-                                    font, SkTextEncoding::kGlyphID),
-        0, 0, paint);
-  } else if (oneAtATime) {
-    for (int index = 0; index < nChars; ++index) {
-      const TextCharPos& cp = pCharPos[index];
-      if (cp.m_bGlyphAdjust) {
-        if (0 == cp.m_AdjustMatrix[1] && 0 == cp.m_AdjustMatrix[2] &&
-            1 == cp.m_AdjustMatrix[3]) {
-          font.setScaleX(cp.m_AdjustMatrix[0]);
-          auto blob =
-              SkTextBlob::MakeFromText(&glyphs[index], sizeof(glyphs[index]),
-                                       font, SkTextEncoding::kGlyphID);
-          m_pCanvas->drawTextBlob(blob, positions[index].fX,
-                                  positions[index].fY, paint);
-          font.setScaleX(SkIntToScalar(1));
-        } else {
-          SkAutoCanvasRestore scoped_save_restore2(m_pCanvas, /*doSave=*/true);
-          SkMatrix adjust;
-          adjust.reset();
-          adjust.setScaleX(cp.m_AdjustMatrix[0]);
-          adjust.setSkewX(cp.m_AdjustMatrix[1]);
-          adjust.setSkewY(cp.m_AdjustMatrix[2]);
-          adjust.setScaleY(cp.m_AdjustMatrix[3]);
-          adjust.preTranslate(positions[index].fX, positions[index].fY);
-          m_pCanvas->concat(adjust);
-          auto blob =
-              SkTextBlob::MakeFromText(&glyphs[index], sizeof(glyphs[index]),
-                                       font, SkTextEncoding::kGlyphID);
-          m_pCanvas->drawTextBlob(blob, 0, 0, paint);
-        }
-      } else {
+
+  for (size_t index = 0; index < pCharPos.size(); ++index) {
+    const TextCharPos& cp = pCharPos[index];
+    if (cp.m_bGlyphAdjust) {
+      if (0 == cp.m_AdjustMatrix[1] && 0 == cp.m_AdjustMatrix[2] &&
+          1 == cp.m_AdjustMatrix[3]) {
+        font.setScaleX(cp.m_AdjustMatrix[0]);
         auto blob =
             SkTextBlob::MakeFromText(&glyphs[index], sizeof(glyphs[index]),
                                      font, SkTextEncoding::kGlyphID);
         m_pCanvas->drawTextBlob(blob, positions[index].fX, positions[index].fY,
                                 paint);
-      }
-    }
-  } else {
-    for (int index = 0; index < nChars; ++index) {
-      const TextCharPos& cp = pCharPos[index];
-      uint32_t font_glyph_width =
-          pFont ? pFont->GetGlyphWidth(cp.m_GlyphIndex) : 0;
-      uint32_t pdf_glyph_width = cp.m_FontCharWidth;
-      if (font_glyph_width && pdf_glyph_width &&
-          font_glyph_width > pdf_glyph_width) {
-        font.setScaleX(SkIntToScalar(pdf_glyph_width) / font_glyph_width);
-      } else {
         font.setScaleX(SkIntToScalar(1));
+      } else {
+        SkAutoCanvasRestore scoped_save_restore2(m_pCanvas, /*doSave=*/true);
+        SkMatrix adjust;
+        adjust.preTranslate(positions[index].fX, -positions[index].fY);
+        adjust.setScaleX(cp.m_AdjustMatrix[0]);
+        adjust.setSkewX(cp.m_AdjustMatrix[1]);
+        adjust.setSkewY(cp.m_AdjustMatrix[2]);
+        adjust.setScaleY(cp.m_AdjustMatrix[3]);
+        m_pCanvas->concat(adjust);
+        auto blob =
+            SkTextBlob::MakeFromText(&glyphs[index], sizeof(glyphs[index]),
+                                     font, SkTextEncoding::kGlyphID);
+        m_pCanvas->drawTextBlob(blob, 0, 0, paint);
       }
+    } else {
       auto blob =
           SkTextBlob::MakeFromText(&glyphs[index], sizeof(glyphs[index]), font,
                                    SkTextEncoding::kGlyphID);
@@ -1793,7 +823,118 @@
                               paint);
     }
   }
+  return true;
+}
 
+// 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,
+                                       float font_size,
+                                       uint32_t color,
+                                       const CFX_TextRenderOptions& options) {
+  float scaleX = 1;
+  bool oneAtATime = false;
+  bool hasRSX = HasRSX(char_pos, &scaleX, &oneAtATime);
+  if (oneAtATime) {
+    return false;
+  }
+
+  m_charDetails.SetCount(0);
+  m_rsxform.resize(0);
+
+  const size_t original_count = m_charDetails.Count();
+  FX_SAFE_SIZE_T safe_count = original_count;
+  safe_count += char_pos.size();
+  const size_t total_count = safe_count.ValueOrDie();
+  m_charDetails.SetCount(total_count);
+  if (hasRSX) {
+    m_rsxform.resize(total_count);
+  }
+
+  const SkScalar flip = font_size < 0 ? -1 : 1;
+  const SkScalar vFlip = pFont->IsVertical() ? -1 : 1;
+  for (size_t index = 0; index < char_pos.size(); ++index) {
+    const TextCharPos& cp = char_pos[index];
+    size_t cur_index = index + original_count;
+    m_charDetails.SetPositionAt(cur_index,
+                                {cp.m_Origin.x * flip, cp.m_Origin.y * vFlip});
+    m_charDetails.SetGlyphAt(cur_index, static_cast<uint16_t>(cp.m_GlyphIndex));
+    m_charDetails.SetFontCharWidthAt(cur_index, cp.m_FontCharWidth);
+#if BUILDFLAG(IS_APPLE)
+    if (cp.m_ExtGID) {
+      m_charDetails.SetGlyphAt(cur_index, static_cast<uint16_t>(cp.m_ExtGID));
+    }
+#endif
+  }
+  if (hasRSX) {
+    const DataVector<SkPoint>& positions = m_charDetails.GetPositions();
+    for (size_t index = 0; index < char_pos.size(); ++index) {
+      const TextCharPos& cp = char_pos[index];
+      SkRSXform& rsxform = m_rsxform[index + original_count];
+      if (cp.m_bGlyphAdjust) {
+        rsxform.fSCos = cp.m_AdjustMatrix[0];
+        rsxform.fSSin = cp.m_AdjustMatrix[1];
+        rsxform.fTx = cp.m_AdjustMatrix[0] * positions[index].fX;
+        rsxform.fTy = -cp.m_AdjustMatrix[3] * positions[index].fY;
+      } else {
+        rsxform.fSCos = 1;
+        rsxform.fSSin = 0;
+        rsxform.fTx = positions[index].fX;
+        rsxform.fTy = positions[index].fY;
+      }
+    }
+  }
+
+  SkPaint skPaint;
+  skPaint.setAntiAlias(true);
+  skPaint.setColor(color);
+
+  SkFont font;
+  if (pFont->GetFaceRec()) {  // exclude placeholder test fonts
+    font.setTypeface(sk_ref_sp(pFont->GetDeviceCache()));
+  }
+  font.setEmbolden(pFont->IsSubstFontBold());
+  font.setHinting(SkFontHinting::kNone);
+  font.setScaleX(scaleX);
+  font.setSkewX(tanf(pFont->GetSubstFontItalicAngle() * FXSYS_PI / 180.0));
+  font.setSize(SkTAbs(font_size));
+  font.setSubpixel(true);
+  font.setEdging(GetFontEdgingType(options));
+
+  SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
+  m_pCanvas->concat(ToFlippedSkMatrix(matrix, flip));
+
+  const DataVector<uint16_t>& glyphs = m_charDetails.GetGlyphs();
+  if (m_rsxform.size()) {
+    sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromRSXform(
+        glyphs.data(), glyphs.size() * sizeof(uint16_t), m_rsxform.data(), font,
+        SkTextEncoding::kGlyphID);
+    m_pCanvas->drawTextBlob(blob, 0, 0, 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;
 }
 
@@ -1801,13 +942,37 @@
   return 1;
 }
 
+bool CFX_SkiaDeviceDriver::MultiplyAlpha(float alpha) {
+  SkPaint paint;
+  paint.setAlphaf(alpha);
+  paint.setBlendMode(SkBlendMode::kDstIn);
+  m_pCanvas->drawPaint(paint);
+  return true;
+}
+
+bool CFX_SkiaDeviceDriver::MultiplyAlpha(const RetainPtr<CFX_DIBBase>& mask) {
+  CHECK(mask->IsMaskFormat());
+
+  sk_sp<SkImage> skia_mask = mask->RealizeSkImage();
+  if (!skia_mask) {
+    return false;
+  }
+  DCHECK_EQ(skia_mask->colorType(), kAlpha_8_SkColorType);
+
+  SkPaint paint;
+  paint.setBlendMode(SkBlendMode::kDstIn);
+  m_pCanvas->drawImageRect(skia_mask,
+                           SkRect::Make(m_pCanvas->imageInfo().bounds()),
+                           SkSamplingOptions(), &paint);
+  return true;
+}
+
 DeviceType CFX_SkiaDeviceDriver::GetDeviceType() const {
   return DeviceType::kDisplay;
 }
 
 int CFX_SkiaDeviceDriver::GetDeviceCaps(int caps_id) const {
   switch (caps_id) {
-#ifdef _SKIA_SUPPORT_
     case FXDC_PIXEL_WIDTH:
       return m_pCanvas->imageInfo().width();
     case FXDC_PIXEL_HEIGHT:
@@ -1821,250 +986,114 @@
       return FXRC_GET_BITS | FXRC_ALPHA_PATH | FXRC_ALPHA_IMAGE |
              FXRC_BLEND_MODE | FXRC_SOFT_CLIP | FXRC_ALPHA_OUTPUT |
              FXRC_FILLSTROKE_PATH | FXRC_SHADING;
-#endif  // _SKIA_SUPPORT_
-
-#ifdef _SKIA_SUPPORT_PATHS_
-    case FXDC_PIXEL_WIDTH:
-      return m_pBitmap->GetWidth();
-    case FXDC_PIXEL_HEIGHT:
-      return m_pBitmap->GetHeight();
-    case FXDC_BITS_PIXEL:
-      return m_pBitmap->GetBPP();
-    case FXDC_HORZ_SIZE:
-    case FXDC_VERT_SIZE:
-      return 0;
-    case FXDC_RENDER_CAPS: {
-      int flags = FXRC_GET_BITS | FXRC_ALPHA_PATH | FXRC_ALPHA_IMAGE |
-                  FXRC_BLEND_MODE | FXRC_SOFT_CLIP | FXRC_SHADING;
-      if (m_pBitmap->HasAlpha()) {
-        flags |= FXRC_ALPHA_OUTPUT;
-      } else if (m_pBitmap->IsAlphaMask()) {
-        if (m_pBitmap->GetBPP() == 1) {
-          flags |= FXRC_BITMASK_OUTPUT;
-        } else {
-          flags |= FXRC_BYTEMASK_OUTPUT;
-        }
-      }
-      if (m_pBitmap->IsCmykImage()) {
-        flags |= FXRC_CMYK_OUTPUT;
-      }
-      return flags;
-    }
-#endif  // _SKIA_SUPPORT_PATHS_
-
     default:
-      NOTREACHED();
-      return 0;
+      NOTREACHED_NORETURN();
   }
 }
 
 void CFX_SkiaDeviceDriver::SaveState() {
-  m_pCache->DebugCheckClip();
-  if (!m_pCache->ClipSave())
-    m_pCanvas->save();
-
-#ifdef _SKIA_SUPPORT_PATHS_
-#if SHOW_SKIA_PATH
-  printf("SaveState %zd\n", stack().size());
-#endif
-  std::unique_ptr<CFX_ClipRgn> pClip;
-  if (m_pClipRgn)
-    pClip = pdfium::MakeUnique<CFX_ClipRgn>(*m_pClipRgn);
-  m_StateStack.push_back(std::move(pClip));
-#endif  // _SKIA_SUPPORT_PATHS_
+  m_pCanvas->save();
 }
 
 void CFX_SkiaDeviceDriver::RestoreState(bool bKeepSaved) {
-#ifdef _SKIA_SUPPORT_PATHS_
-  m_pClipRgn.reset();
-
-  if (m_StateStack.empty())
-    return;
-#else
-  if (m_pCache->IsEmpty())
-    return;
-#endif
-  if (!m_pCache->ClipRestore())
-    m_pCanvas->restore();
-  if (bKeepSaved && !m_pCache->ClipSave())
-    m_pCanvas->save();
-#ifdef _SKIA_SUPPORT_PATHS_
-#if SHOW_SKIA_PATH
-  printf("RestoreState %zd %s\n", m_StateStack.size(),
-         bKeepSaved ? "bKeepSaved" : "");
-#endif
+  m_pCanvas->restore();
   if (bKeepSaved) {
-    if (m_StateStack.back())
-      m_pClipRgn = pdfium::MakeUnique<CFX_ClipRgn>(*m_StateStack.back());
-  } else {
-    m_pClipRgn = std::move(m_StateStack.back());
-    m_StateStack.pop_back();
+    m_pCanvas->save();
   }
-  m_pCache->DebugCheckClip();
-#endif  // _SKIA_SUPPORT_PATHS_
 }
 
-#ifdef _SKIA_SUPPORT_PATHS_
-void CFX_SkiaDeviceDriver::SetClipMask(const FX_RECT& clipBox,
-                                       const SkPath& path) {
-  FX_RECT path_rect(clipBox.left, clipBox.top, clipBox.right + 1,
-                    clipBox.bottom + 1);
-  path_rect.Intersect(m_pClipRgn->GetBox());
-  auto pThisLayer = pdfium::MakeRetain<CFX_DIBitmap>();
-  pThisLayer->Create(path_rect.Width(), path_rect.Height(), FXDIB_8bppMask);
-  pThisLayer->Clear(0);
-
-  SkImageInfo imageInfo =
-      SkImageInfo::Make(pThisLayer->GetWidth(), pThisLayer->GetHeight(),
-                        SkColorType::kAlpha_8_SkColorType, kOpaque_SkAlphaType);
-  SkBitmap bitmap;
-  bitmap.installPixels(imageInfo, pThisLayer->GetBuffer(),
-                       pThisLayer->GetPitch());
-  auto canvas = pdfium::MakeUnique<SkCanvas>(bitmap);
-  canvas->translate(
-      -path_rect.left,
-      -path_rect.top);  // FIXME(caryclark) wrong sign(s)? upside down?
-  SkPaint paint;
-  paint.setAntiAlias((m_FillFlags & FXFILL_NOPATHSMOOTH) == 0);
-  canvas->drawPath(path, paint);
-  m_pClipRgn->IntersectMaskF(path_rect.left, path_rect.top, pThisLayer);
-}
-#endif  // _SKIA_SUPPORT_PATHS_
-
 bool CFX_SkiaDeviceDriver::SetClip_PathFill(
-    const CFX_PathData* pPathData,     // path info
+    const CFX_Path& path,              // path info
     const CFX_Matrix* pObject2Device,  // flips object's y-axis
-    int fill_mode                      // fill mode, WINDING or ALTERNATE
-    ) {
-  CFX_Matrix identity;
-  const CFX_Matrix* deviceMatrix = pObject2Device ? pObject2Device : &identity;
-  bool cached = m_pCache->SetClipFill(pPathData, deviceMatrix, fill_mode);
+    const CFX_FillRenderOptions& fill_options) {
+  m_FillOptions = fill_options;
+  const CFX_Matrix& deviceMatrix =
+      pObject2Device ? *pObject2Device : CFX_Matrix();
 
-#ifdef _SKIA_SUPPORT_PATHS_
-  m_FillFlags = fill_mode;
-  if (!m_pClipRgn) {
-    m_pClipRgn = pdfium::MakeUnique<CFX_ClipRgn>(
-        GetDeviceCaps(FXDC_PIXEL_WIDTH), GetDeviceCaps(FXDC_PIXEL_HEIGHT));
-  }
-#endif  // _SKIA_SUPPORT_PATHS_
-  if (pPathData->GetPoints().size() == 5 ||
-      pPathData->GetPoints().size() == 4) {
-    CFX_FloatRect rectf;
-    if (pPathData->IsRect(deviceMatrix, &rectf)) {
+  SkPath skClipPath;
+  if (path.GetPoints().size() == 5 || path.GetPoints().size() == 4) {
+    absl::optional<CFX_FloatRect> maybe_rectf = path.GetRect(&deviceMatrix);
+    if (maybe_rectf.has_value()) {
+      CFX_FloatRect& rectf = maybe_rectf.value();
       rectf.Intersect(CFX_FloatRect(0, 0,
                                     (float)GetDeviceCaps(FXDC_PIXEL_WIDTH),
                                     (float)GetDeviceCaps(FXDC_PIXEL_HEIGHT)));
+      FX_RECT outer = rectf.GetOuterRect();
       // note that PDF's y-axis goes up; Skia's y-axis goes down
-      if (!cached) {
-        SkRect skClipRect =
-            SkRect::MakeLTRB(rectf.left, rectf.bottom, rectf.right, rectf.top);
-        DebugDrawSkiaClipRect(m_pCanvas, skClipRect);
-        m_pCanvas->clipRect(skClipRect, SkClipOp::kIntersect, true);
-      }
-
-#ifdef _SKIA_SUPPORT_PATHS_
-      FX_RECT rect = rectf.GetOuterRect();
-      m_pClipRgn->IntersectRect(rect);
-#endif  // _SKIA_SUPPORT_PATHS_
-      DebugShowCanvasClip(this, m_pCanvas);
-      return true;
+      skClipPath.addRect({(float)outer.left, (float)outer.bottom,
+                          (float)outer.right, (float)outer.top});
     }
   }
-  SkPath skClipPath = BuildPath(pPathData);
-  skClipPath.setFillType(GetAlternateOrWindingFillType(fill_mode));
-  SkMatrix skMatrix = ToSkMatrix(*deviceMatrix);
-  skClipPath.transform(skMatrix);
-  DebugShowSkiaPath(skClipPath);
-  if (!cached) {
-    DebugDrawSkiaClipPath(m_pCanvas, skClipPath);
-    m_pCanvas->clipPath(skClipPath, SkClipOp::kIntersect, true);
+  if (skClipPath.isEmpty()) {
+    skClipPath = BuildPath(path);
+    skClipPath.setFillType(GetAlternateOrWindingFillType(fill_options));
+    skClipPath.transform(ToSkMatrix(deviceMatrix));
+    DebugShowSkiaPath(skClipPath);
   }
-#ifdef _SKIA_SUPPORT_PATHS_
-  FX_RECT clipBox(0, 0, GetDeviceCaps(FXDC_PIXEL_WIDTH),
-                  GetDeviceCaps(FXDC_PIXEL_HEIGHT));
-  SetClipMask(clipBox, skClipPath);
-#endif  // _SKIA_SUPPORT_PATHS_
+  m_pCanvas->clipPath(skClipPath, SkClipOp::kIntersect, true);
   DebugShowCanvasClip(this, m_pCanvas);
   return true;
 }
 
 bool CFX_SkiaDeviceDriver::SetClip_PathStroke(
-    const CFX_PathData* pPathData,         // path info
+    const CFX_Path& path,                  // path info
     const CFX_Matrix* pObject2Device,      // required transformation
     const CFX_GraphStateData* pGraphState  // graphic state, for pen attributes
-    ) {
-  bool cached = m_pCache->SetClipStroke(pPathData, pObject2Device, pGraphState);
-
-#ifdef _SKIA_SUPPORT_PATHS_
-  if (!m_pClipRgn) {
-    m_pClipRgn = pdfium::MakeUnique<CFX_ClipRgn>(
-        GetDeviceCaps(FXDC_PIXEL_WIDTH), GetDeviceCaps(FXDC_PIXEL_HEIGHT));
-  }
-#endif  // _SKIA_SUPPORT_PATHS_
-  // build path data
-  SkPath skPath = BuildPath(pPathData);
+) {
+  SkPath skPath = BuildPath(path);
   SkMatrix skMatrix = ToSkMatrix(*pObject2Device);
   SkPaint skPaint;
-  PaintStroke(&skPaint, pGraphState, skMatrix);
+  PaintStroke(&skPaint, pGraphState, skMatrix, CFX_FillRenderOptions());
   SkPath dst_path;
-  skPaint.getFillPath(skPath, &dst_path);
+  skpathutils::FillPathWithPaint(skPath, skPaint, &dst_path);
   dst_path.transform(skMatrix);
-  if (!cached) {
-    DebugDrawSkiaClipPath(m_pCanvas, dst_path);
-    m_pCanvas->clipPath(dst_path, SkClipOp::kIntersect, true);
-  }
-#ifdef _SKIA_SUPPORT_PATHS_
-  FX_RECT clipBox(0, 0, GetDeviceCaps(FXDC_PIXEL_WIDTH),
-                  GetDeviceCaps(FXDC_PIXEL_HEIGHT));
-  SetClipMask(clipBox, dst_path);
-#endif  // _SKIA_SUPPORT_PATHS_
+  m_pCanvas->clipPath(dst_path, SkClipOp::kIntersect, true);
   DebugShowCanvasClip(this, m_pCanvas);
   return true;
 }
 
+// TODO(crbug.com/pdfium/1963): `blend_type` isn't used?
 bool CFX_SkiaDeviceDriver::DrawPath(
-    const CFX_PathData* pPathData,          // path info
+    const CFX_Path& path,                   // path info
     const CFX_Matrix* pObject2Device,       // optional transformation
     const CFX_GraphStateData* pGraphState,  // graphic state, for pen attributes
     uint32_t fill_color,                    // fill color
     uint32_t stroke_color,                  // stroke color
-    int fill_mode,  // fill mode, WINDING or ALTERNATE. 0 for not filled
+    const CFX_FillRenderOptions& fill_options,
     BlendMode blend_type) {
-  ASSERT(GetAlternateOrWindingFillMode(fill_mode) !=
-         kAlternateOrWindingFillModeMask);
-  if (m_pCache->DrawPath(pPathData, pObject2Device, pGraphState, fill_color,
-                         stroke_color, fill_mode, blend_type)) {
-    return true;
-  }
-  SkMatrix skMatrix;
-  if (pObject2Device)
-    skMatrix = ToSkMatrix(*pObject2Device);
-  else
-    skMatrix.setIdentity();
+  m_FillOptions = fill_options;
+
+  SkPath skia_path = BuildPath(path);
+  skia_path.setFillType(GetAlternateOrWindingFillType(fill_options));
+
+  SkMatrix skMatrix = pObject2Device ? ToSkMatrix(*pObject2Device) : SkMatrix();
   SkPaint skPaint;
-  skPaint.setAntiAlias(true);
-  if (fill_mode & FXFILL_FULLCOVER)
+  skPaint.setAntiAlias(!fill_options.aliased_path);
+  if (fill_options.full_cover) {
     skPaint.setBlendMode(SkBlendMode::kPlus);
+  }
   int stroke_alpha = FXARGB_A(stroke_color);
-  bool is_paint_stroke = pGraphState && stroke_alpha;
-  if (is_paint_stroke)
-    PaintStroke(&skPaint, pGraphState, skMatrix);
-  SkPath skPath = BuildPath(pPathData);
+  if (stroke_alpha) {
+    const CFX_GraphStateData& graph_state =
+        pGraphState ? *pGraphState : CFX_GraphStateData();
+    PaintStroke(&skPaint, &graph_state, skMatrix, fill_options);
+  }
+
   SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
   m_pCanvas->concat(skMatrix);
   bool do_stroke = true;
-  if (GetAlternateOrWindingFillMode(fill_mode) && fill_color) {
-    skPath.setFillType(GetAlternateOrWindingFillType(fill_mode));
+  if (fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill &&
+      fill_color) {
     SkPath strokePath;
-    const SkPath* fillPath = &skPath;
-    if (is_paint_stroke) {
+    const SkPath* fillPath = &skia_path;
+    if (stroke_alpha) {
       if (m_bGroupKnockout) {
-        skPaint.getFillPath(skPath, &strokePath);
+        skpathutils::FillPathWithPaint(skia_path, skPaint, &strokePath);
         if (stroke_color == fill_color &&
-            Op(skPath, strokePath, SkPathOp::kUnion_SkPathOp, &strokePath)) {
+            Op(skia_path, strokePath, SkPathOp::kUnion_SkPathOp, &strokePath)) {
           fillPath = &strokePath;
           do_stroke = false;
-        } else if (Op(skPath, strokePath, SkPathOp::kDifference_SkPathOp,
+        } else if (Op(skia_path, strokePath, SkPathOp::kDifference_SkPathOp,
                       &strokePath)) {
           fillPath = &strokePath;
         }
@@ -2072,20 +1101,23 @@
     }
     skPaint.setStyle(SkPaint::kFill_Style);
     skPaint.setColor(fill_color);
-#ifdef _SKIA_SUPPORT_PATHS_
-    m_pBitmap->PreMultiply();
-#endif  // _SKIA_SUPPORT_PATHS_
     DebugShowSkiaDrawPath(this, m_pCanvas, skPaint, *fillPath);
     m_pCanvas->drawPath(*fillPath, skPaint);
   }
-  if (is_paint_stroke && do_stroke) {
+  if (stroke_alpha && do_stroke) {
     skPaint.setStyle(SkPaint::kStroke_Style);
     skPaint.setColor(stroke_color);
-#ifdef _SKIA_SUPPORT_PATHS_
-    m_pBitmap->PreMultiply();
-#endif  // _SKIA_SUPPORT_PATHS_
-    DebugShowSkiaDrawPath(this, m_pCanvas, skPaint, skPath);
-    m_pCanvas->drawPath(skPath, skPaint);
+    if (!skia_path.isLastContourClosed() && IsPathAPoint(skia_path)) {
+      DCHECK_GE(skia_path.countPoints(), 1);
+      m_pCanvas->drawPoint(skia_path.getPoint(0), skPaint);
+    } else if (IsPathAPoint(skia_path) &&
+               skPaint.getStrokeCap() != SkPaint::kRound_Cap) {
+      // Do nothing. A closed 0-length closed path can be rendered only if
+      // its line cap type is round.
+    } else {
+      DebugShowSkiaDrawPath(this, m_pCanvas, skPaint, skia_path);
+      m_pCanvas->drawPath(skia_path, skPaint);
+    }
   }
   return true;
 }
@@ -2100,13 +1132,12 @@
 bool CFX_SkiaDeviceDriver::FillRectWithBlend(const FX_RECT& rect,
                                              uint32_t fill_color,
                                              BlendMode blend_type) {
-  m_pCache->FlushForDraw();
   SkPaint spaint;
   spaint.setAntiAlias(true);
   spaint.setColor(fill_color);
   spaint.setBlendMode(GetSkiaBlendMode(blend_type));
-  SkRect srect = SkRect::MakeLTRB(rect.left, SkTMin(rect.top, rect.bottom),
-                                  rect.right, SkTMax(rect.bottom, rect.top));
+  SkRect srect = SkRect::MakeLTRB(rect.left, std::min(rect.top, rect.bottom),
+                                  rect.right, std::max(rect.bottom, rect.top));
   DebugShowSkiaDrawRect(this, m_pCanvas, spaint, srect);
   m_pCanvas->drawRect(srect, spaint);
   return true;
@@ -2117,30 +1148,32 @@
                                        const FX_RECT& clip_rect,
                                        int alpha,
                                        bool bAlphaMode) {
-  m_pCache->FlushForDraw();
   ShadingType shadingType = pPattern->GetShadingType();
   if (kAxialShading != shadingType && kRadialShading != shadingType &&
       kCoonsPatchMeshShading != shadingType) {
     // TODO(caryclark) more types
     return false;
   }
-  int csFamily = pPattern->GetCS()->GetFamily();
-  if (PDFCS_DEVICERGB != csFamily && PDFCS_DEVICEGRAY != csFamily)
+  CPDF_ColorSpace::Family csFamily = pPattern->GetCS()->GetFamily();
+  if (CPDF_ColorSpace::Family::kDeviceRGB != csFamily &&
+      CPDF_ColorSpace::Family::kDeviceGray != csFamily) {
     return false;
+  }
   const std::vector<std::unique_ptr<CPDF_Function>>& pFuncs =
       pPattern->GetFuncs();
-  int nFuncs = pFuncs.size();
+  size_t nFuncs = pFuncs.size();
   if (nFuncs > 1)  // TODO(caryclark) remove this restriction
     return false;
-  const CPDF_Dictionary* pDict = pPattern->GetShadingObject()->GetDict();
-  const CPDF_Array* pCoords = pDict->GetArrayFor("Coords");
+  RetainPtr<const CPDF_Dictionary> pDict =
+      pPattern->GetShadingObject()->GetDict();
+  RetainPtr<const CPDF_Array> pCoords = pDict->GetArrayFor("Coords");
   if (!pCoords && kCoonsPatchMeshShading != shadingType)
     return false;
   // TODO(caryclark) Respect Domain[0], Domain[1]. (Don't know what they do
   // yet.)
-  SkTDArray<SkColor> skColors;
-  SkTDArray<SkScalar> skPos;
-  for (int j = 0; j < nFuncs; j++) {
+  DataVector<SkColor> sk_colors;
+  DataVector<SkScalar> sk_pos;
+  for (size_t j = 0; j < nFuncs; j++) {
     if (!pFuncs[j])
       continue;
 
@@ -2149,21 +1182,24 @@
          Type 0 Sampled Functions in PostScript can also have an Order integer
          in the dictionary. PDFium doesn't appear to check for this anywhere.
        */
-      if (!AddSamples(pSampledFunc, &skColors, &skPos))
+      if (!AddSamples(pSampledFunc, sk_colors, sk_pos)) {
         return false;
+      }
     } else if (const CPDF_ExpIntFunc* pExpIntFuc = pFuncs[j]->ToExpIntFunc()) {
-      if (!AddColors(pExpIntFuc, &skColors, /*is_encode_reversed=*/false))
+      if (!AddColors(pExpIntFuc, sk_colors, /*is_encode_reversed=*/false)) {
         return false;
-      skPos.push_back(0);
-      skPos.push_back(1);
+      }
+      sk_pos.push_back(0);
+      sk_pos.push_back(1);
     } else if (const CPDF_StitchFunc* pStitchFunc = pFuncs[j]->ToStitchFunc()) {
-      if (!AddStitching(pStitchFunc, &skColors, &skPos))
+      if (!AddStitching(pStitchFunc, sk_colors, sk_pos)) {
         return false;
+      }
     } else {
       return false;
     }
   }
-  const CPDF_Array* pArray = pDict->GetArrayFor("Extend");
+  RetainPtr<const CPDF_Array> pArray = pDict->GetArrayFor("Extend");
   bool clipStart = !pArray || !pArray->GetIntegerAt(0);
   bool clipEnd = !pArray || !pArray->GetIntegerAt(1);
   SkPaint paint;
@@ -2175,15 +1211,15 @@
   SkPath skClip;
   SkPath skPath;
   if (kAxialShading == shadingType) {
-    float start_x = pCoords->GetNumberAt(0);
-    float start_y = pCoords->GetNumberAt(1);
-    float end_x = pCoords->GetNumberAt(2);
-    float end_y = pCoords->GetNumberAt(3);
+    float start_x = pCoords->GetFloatAt(0);
+    float start_y = pCoords->GetFloatAt(1);
+    float end_x = pCoords->GetFloatAt(2);
+    float end_y = pCoords->GetFloatAt(3);
     SkPoint pts[] = {{start_x, start_y}, {end_x, end_y}};
-    skMatrix.mapPoints(pts, SK_ARRAY_COUNT(pts));
-    paint.setShader(
-        SkGradientShader::MakeLinear(pts, skColors.begin(), skPos.begin(),
-                                     skColors.count(), SkTileMode::kClamp));
+    skMatrix.mapPoints(pts, std::size(pts));
+    paint.setShader(SkGradientShader::MakeLinear(
+        pts, sk_colors.data(), sk_pos.data(),
+        fxcrt::CollectionSize<int>(sk_colors), SkTileMode::kClamp));
     if (clipStart || clipEnd) {
       // if the gradient is horizontal or vertical, modify the draw rectangle
       if (pts[0].fX == pts[1].fX) {  // vertical
@@ -2192,18 +1228,18 @@
           std::swap(clipStart, clipEnd);
         }
         if (clipStart)
-          skRect.fTop = SkTMax(skRect.fTop, pts[0].fY);
+          skRect.fTop = std::max(skRect.fTop, pts[0].fY);
         if (clipEnd)
-          skRect.fBottom = SkTMin(skRect.fBottom, pts[1].fY);
+          skRect.fBottom = std::min(skRect.fBottom, pts[1].fY);
       } else if (pts[0].fY == pts[1].fY) {  // horizontal
         if (pts[0].fX > pts[1].fX) {
           std::swap(pts[0].fX, pts[1].fX);
           std::swap(clipStart, clipEnd);
         }
         if (clipStart)
-          skRect.fLeft = SkTMax(skRect.fLeft, pts[0].fX);
+          skRect.fLeft = std::max(skRect.fLeft, pts[0].fX);
         if (clipEnd)
-          skRect.fRight = SkTMin(skRect.fRight, pts[1].fX);
+          skRect.fRight = std::min(skRect.fRight, pts[1].fX);
       } else {  // if the gradient is angled and contained by the rect, clip
         SkPoint rectPts[4] = {{skRect.fLeft, skRect.fTop},
                               {skRect.fRight, skRect.fTop},
@@ -2215,17 +1251,17 @@
     skPath.addRect(skRect);
     skMatrix.setIdentity();
   } else if (kRadialShading == shadingType) {
-    float start_x = pCoords->GetNumberAt(0);
-    float start_y = pCoords->GetNumberAt(1);
-    float start_r = pCoords->GetNumberAt(2);
-    float end_x = pCoords->GetNumberAt(3);
-    float end_y = pCoords->GetNumberAt(4);
-    float end_r = pCoords->GetNumberAt(5);
+    float start_x = pCoords->GetFloatAt(0);
+    float start_y = pCoords->GetFloatAt(1);
+    float start_r = pCoords->GetFloatAt(2);
+    float end_x = pCoords->GetFloatAt(3);
+    float end_y = pCoords->GetFloatAt(4);
+    float end_r = pCoords->GetFloatAt(5);
     SkPoint pts[] = {{start_x, start_y}, {end_x, end_y}};
 
     paint.setShader(SkGradientShader::MakeTwoPointConical(
-        pts[0], start_r, pts[1], end_r, skColors.begin(), skPos.begin(),
-        skColors.count(), SkTileMode::kClamp));
+        pts[0], start_r, pts[1], end_r, sk_colors.data(), sk_pos.data(),
+        fxcrt::CollectionSize<int>(sk_colors), SkTileMode::kClamp));
     if (clipStart || clipEnd) {
       if (clipStart && start_r)
         skClip.addCircle(pts[0].fX, pts[0].fY, start_r);
@@ -2241,12 +1277,13 @@
     skPath.addRect(skRect);
     skPath.transform(inverse);
   } else {
-    ASSERT(kCoonsPatchMeshShading == shadingType);
-    const CPDF_Stream* pStream = ToStream(pPattern->GetShadingObject());
+    DCHECK_EQ(kCoonsPatchMeshShading, shadingType);
+    RetainPtr<const CPDF_Stream> pStream =
+        ToStream(pPattern->GetShadingObject());
     if (!pStream)
       return false;
-    CPDF_MeshStream stream(shadingType, pPattern->GetFuncs(), pStream,
-                           pPattern->GetCS());
+    CPDF_MeshStream stream(shadingType, pPattern->GetFuncs(),
+                           std::move(pStream), pPattern->GetCS());
     if (!stream.Load())
       return false;
     SkPoint cubics[12];
@@ -2255,26 +1292,27 @@
     if (!skClip.isEmpty())
       m_pCanvas->clipPath(skClip, SkClipOp::kIntersect, true);
     m_pCanvas->concat(skMatrix);
-    while (!stream.BitStream()->IsEOF()) {
+    while (!stream.IsEOF()) {
       uint32_t flag = stream.ReadFlag();
-      int iStartPoint = flag ? 4 : 0;
-      int iStartColor = flag ? 2 : 0;
+      size_t start_point = flag ? 4 : 0;
+      size_t start_color = flag ? 2 : 0;
       if (flag) {
-        SkPoint tempCubics[4];
-        for (int i = 0; i < (int)SK_ARRAY_COUNT(tempCubics); i++)
-          tempCubics[i] = cubics[(flag * 3 + i) % 12];
-        memcpy(cubics, tempCubics, sizeof(tempCubics));
-        SkColor tempColors[2];
-        tempColors[0] = colors[flag];
-        tempColors[1] = colors[(flag + 1) % 4];
-        memcpy(colors, tempColors, sizeof(tempColors));
+        SkPoint temp_cubics[4];
+        for (size_t i = 0; i < std::size(temp_cubics); ++i) {
+          temp_cubics[i] = cubics[(flag * 3 + i) % 12];
+        }
+        std::copy(std::begin(temp_cubics), std::end(temp_cubics),
+                  std::begin(cubics));
+        SkColor temp_colors[2] = {colors[flag % 4], colors[(flag + 1) % 4]};
+        std::copy(std::begin(temp_colors), std::end(temp_colors),
+                  std::begin(colors));
       }
-      for (int i = iStartPoint; i < (int)SK_ARRAY_COUNT(cubics); i++) {
+      for (size_t i = start_point; i < std::size(cubics); ++i) {
         CFX_PointF point = stream.ReadCoords();
         cubics[i].fX = point.x;
         cubics[i].fY = point.y;
       }
-      for (int i = iStartColor; i < (int)SK_ARRAY_COUNT(colors); i++) {
+      for (size_t i = start_color; i < std::size(colors); ++i) {
         float r;
         float g;
         float b;
@@ -2282,7 +1320,8 @@
         colors[i] = SkColorSetARGB(0xFF, (U8CPU)(r * 255), (U8CPU)(g * 255),
                                    (U8CPU)(b * 255));
       }
-      m_pCanvas->drawPatch(cubics, colors, nullptr, paint);
+      m_pCanvas->drawPatch(cubics, colors, /*textCoords=*/nullptr,
+                           SkBlendMode::kDst, paint);
     }
     return true;
   }
@@ -2294,27 +1333,12 @@
   return true;
 }
 
-uint8_t* CFX_SkiaDeviceDriver::GetBuffer() const {
-  return m_pBitmap->GetBuffer();
-}
-
 bool CFX_SkiaDeviceDriver::GetClipBox(FX_RECT* pRect) {
-#ifdef _SKIA_SUPPORT_PATHS_
-  if (!m_pClipRgn) {
-    pRect->left = pRect->top = 0;
-    pRect->right = GetDeviceCaps(FXDC_PIXEL_WIDTH);
-    pRect->bottom = GetDeviceCaps(FXDC_PIXEL_HEIGHT);
-    return true;
-  }
-  *pRect = m_pClipRgn->GetBox();
-#else
-  // TODO(caryclark) call m_canvas->getClipDeviceBounds() instead
-  pRect->left = 0;
-  pRect->top = 0;
-  const SkImageInfo& canvasSize = m_pCanvas->imageInfo();
-  pRect->right = canvasSize.width();
-  pRect->bottom = canvasSize.height();
-#endif
+  SkIRect clip = m_pCanvas->getDeviceClipBounds();
+  pRect->left = clip.fLeft;
+  pRect->top = clip.fTop;
+  pRect->right = clip.fRight;
+  pRect->bottom = clip.fBottom;
   return true;
 }
 
@@ -2323,63 +1347,30 @@
                                      int top) {
   if (!m_pBitmap)
     return true;
-  uint8_t* srcBuffer = m_pBitmap->GetBuffer();
-  if (!srcBuffer)
+
+  const uint8_t* input_buffer = m_pBitmap->GetBuffer().data();
+  if (!input_buffer) {
     return true;
-#ifdef _SKIA_SUPPORT_
-  m_pCache->FlushForDraw();
-  int srcWidth = m_pBitmap->GetWidth();
-  int srcHeight = m_pBitmap->GetHeight();
-  int srcRowBytes = srcWidth * sizeof(uint32_t);
-  SkImageInfo srcImageInfo = SkImageInfo::Make(
-      srcWidth, srcHeight, SkColorType::kN32_SkColorType, kPremul_SkAlphaType);
-  SkBitmap skSrcBitmap;
-  skSrcBitmap.installPixels(srcImageInfo, srcBuffer, srcRowBytes);
-  uint8_t* dstBuffer = pBitmap->GetBuffer();
-  ASSERT(dstBuffer);
-  int dstWidth = pBitmap->GetWidth();
-  int dstHeight = pBitmap->GetHeight();
-  int dstRowBytes = dstWidth * sizeof(uint32_t);
-  SkImageInfo dstImageInfo = SkImageInfo::Make(
-      dstWidth, dstHeight, SkColorType::kN32_SkColorType, kPremul_SkAlphaType);
-  SkBitmap skDstBitmap;
-  skDstBitmap.installPixels(dstImageInfo, dstBuffer, dstRowBytes);
-  SkCanvas canvas(skDstBitmap);
-  canvas.drawBitmap(skSrcBitmap, left, top, nullptr);
+  }
+
+  uint8_t* output_buffer = pBitmap->GetWritableBuffer().data();
+  DCHECK(output_buffer);
+
+  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);
+
+  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());
+
+  output->getCanvas()->drawImage(input, left, top, SkSamplingOptions());
   return true;
-#endif  // _SKIA_SUPPORT_
-
-#ifdef _SKIA_SUPPORT_PATHS_
-  Flush();
-  m_pBitmap->UnPreMultiply();
-  FX_RECT rect(left, top, left + pBitmap->GetWidth(),
-               top + pBitmap->GetHeight());
-  RetainPtr<CFX_DIBitmap> pBack;
-  if (m_pBackdropBitmap) {
-    pBack = m_pBackdropBitmap->Clone(&rect);
-    if (!pBack)
-      return true;
-
-    pBack->CompositeBitmap(0, 0, pBack->GetWidth(), pBack->GetHeight(),
-                           m_pBitmap, 0, 0, BlendMode::kNormal, nullptr, false);
-  } else {
-    pBack = m_pBitmap->Clone(&rect);
-    if (!pBack)
-      return true;
-  }
-
-  bool bRet = true;
-  left = std::min(left, 0);
-  top = std::min(top, 0);
-  if (m_bRgbByteOrder) {
-    RgbByteOrderTransferBitmap(pBitmap, 0, 0, rect.Width(), rect.Height(),
-                               pBack, left, top);
-  } else {
-    bRet = pBitmap->TransferBitmap(0, 0, rect.Width(), rect.Height(), pBack,
-                                   left, top);
-  }
-  return bRet;
-#endif  // _SKIA_SUPPORT_PATHS_
 }
 
 RetainPtr<CFX_DIBitmap> CFX_SkiaDeviceDriver::GetBackDrop() {
@@ -2392,29 +1383,18 @@
                                      int left,
                                      int top,
                                      BlendMode blend_type) {
-  if (!m_pBitmap || !m_pBitmap->GetBuffer())
+  if (!m_pBitmap || m_pBitmap->GetBuffer().empty())
     return true;
 
-#ifdef _SKIA_SUPPORT_
   CFX_Matrix m = CFX_RenderDevice::GetFlipMatrix(
       pBitmap->GetWidth(), pBitmap->GetHeight(), left, top);
-  std::unique_ptr<CFX_ImageRenderer> dummy;
-  return StartDIBits(pBitmap, 0xFF, argb, m, FXDIB_ResampleOptions(), &dummy,
-                     blend_type);
-#endif  // _SKIA_SUPPORT_
 
-#ifdef _SKIA_SUPPORT_PATHS_
-  Flush();
-  if (pBitmap->IsAlphaMask()) {
-    return m_pBitmap->CompositeMask(left, top, src_rect.Width(),
-                                    src_rect.Height(), pBitmap, argb,
-                                    src_rect.left, src_rect.top, blend_type,
-                                    m_pClipRgn.get(), m_bRgbByteOrder);
-  }
-  return m_pBitmap->CompositeBitmap(
-      left, top, src_rect.Width(), src_rect.Height(), pBitmap, src_rect.left,
-      src_rect.top, blend_type, m_pClipRgn.get(), m_bRgbByteOrder);
-#endif  // _SKIA_SUPPORT_PATHS_
+  // `bNoSmoothing` prevents linear sampling when rendering bitmaps.
+  FXDIB_ResampleOptions sampling_options;
+  sampling_options.bNoSmoothing = true;
+
+  return StartDIBitsSkia(pBitmap, src_rect, 0xFF, argb, m, sampling_options,
+                         blend_type);
 }
 
 bool CFX_SkiaDeviceDriver::StretchDIBits(const RetainPtr<CFX_DIBBase>& pSource,
@@ -2426,9 +1406,7 @@
                                          const FX_RECT* pClipRect,
                                          const FXDIB_ResampleOptions& options,
                                          BlendMode blend_type) {
-#ifdef _SKIA_SUPPORT_
-  m_pCache->FlushForDraw();
-  if (!m_pBitmap->GetBuffer())
+  if (m_pBitmap->GetBuffer().empty())
     return true;
 
   CFX_Matrix m = CFX_RenderDevice::GetFlipMatrix(dest_width, dest_height,
@@ -2437,33 +1415,14 @@
   SkRect skClipRect = SkRect::MakeLTRB(pClipRect->left, pClipRect->bottom,
                                        pClipRect->right, pClipRect->top);
   m_pCanvas->clipRect(skClipRect, SkClipOp::kIntersect, true);
-  std::unique_ptr<CFX_ImageRenderer> dummy;
-  return StartDIBits(pSource, 0xFF, argb, m, FXDIB_ResampleOptions(), &dummy,
-                     blend_type);
-#endif  // _SKIA_SUPPORT_
 
-#ifdef _SKIA_SUPPORT_PATHS_
-  if (dest_width == pSource->GetWidth() &&
-      dest_height == pSource->GetHeight()) {
-    FX_RECT rect(0, 0, dest_width, dest_height);
-    return SetDIBits(pSource, argb, rect, dest_left, dest_top, blend_type);
-  }
-  Flush();
-  FX_RECT dest_rect(dest_left, dest_top, dest_left + dest_width,
-                    dest_top + dest_height);
-  dest_rect.Normalize();
-  FX_RECT dest_clip = dest_rect;
-  dest_clip.Intersect(*pClipRect);
-  CFX_BitmapComposer composer;
-  composer.Compose(m_pBitmap, m_pClipRgn.get(), 255, argb, dest_clip, false,
-                   false, false, m_bRgbByteOrder, blend_type);
-  dest_clip.Offset(-dest_rect.left, -dest_rect.top);
-  CFX_ImageStretcher stretcher(&composer, pSource, dest_width, dest_height,
-                               dest_clip, options);
-  if (stretcher.Start())
-    stretcher.Continue(nullptr);
-  return true;
-#endif  // _SKIA_SUPPORT_PATHS_
+  // `bNoSmoothing` prevents linear sampling when rendering bitmaps.
+  FXDIB_ResampleOptions sampling_options;
+  sampling_options.bNoSmoothing = true;
+
+  return StartDIBitsSkia(
+      pSource, FX_RECT(0, 0, pSource->GetWidth(), pSource->GetHeight()), 0xFF,
+      argb, m, sampling_options, blend_type);
 }
 
 bool CFX_SkiaDeviceDriver::StartDIBits(
@@ -2474,132 +1433,49 @@
     const FXDIB_ResampleOptions& options,
     std::unique_ptr<CFX_ImageRenderer>* handle,
     BlendMode blend_type) {
-#ifdef _SKIA_SUPPORT_
-  m_pCache->FlushForDraw();
-  DebugValidate(m_pBitmap, m_pBackdropBitmap);
-  std::unique_ptr<uint8_t, FxFreeDeleter> dst8Storage;
-  std::unique_ptr<uint32_t, FxFreeDeleter> dst32Storage;
-  SkBitmap skBitmap;
-  int width, height;
-  if (!Upsample(pSource, dst8Storage, dst32Storage, &skBitmap, &width, &height,
-                false)) {
-    return false;
-  }
-  {
-    SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
-    SkMatrix skMatrix;
-    SetBitmapMatrix(matrix, width, height, &skMatrix);
-    m_pCanvas->concat(skMatrix);
-    SkPaint paint;
-    SetBitmapPaint(pSource->IsAlphaMask(), argb, bitmap_alpha, blend_type,
-                   &paint);
-    // TODO(caryclark) Once Skia supports 8 bit src to 8 bit dst remove this
-    if (m_pBitmap && m_pBitmap->GetBPP() == 8 && pSource->GetBPP() == 8) {
-      SkMatrix inv;
-      SkAssertResult(skMatrix.invert(&inv));
-      for (int y = 0; y < m_pBitmap->GetHeight(); ++y) {
-        for (int x = 0; x < m_pBitmap->GetWidth(); ++x) {
-          SkPoint src = {x + 0.5f, y + 0.5f};
-          inv.mapPoints(&src, 1);
-          // TODO(caryclark) Why does the matrix map require clamping?
-          src.fX = SkTMax(0.5f, SkTMin(src.fX, width - 0.5f));
-          src.fY = SkTMax(0.5f, SkTMin(src.fY, height - 0.5f));
-          m_pBitmap->SetPixel(x, y, skBitmap.getColor(src.fX, src.fY));
-        }
-      }
-    } else {
-      m_pCanvas->drawBitmap(skBitmap, 0, 0, &paint);
-    }
-  }
-  DebugValidate(m_pBitmap, m_pBackdropBitmap);
-#endif  // _SKIA_SUPPORT_
-
-#ifdef _SKIA_SUPPORT_PATHS_
-  Flush();
-  if (!m_pBitmap->GetBuffer())
-    return true;
-  m_pBitmap->UnPreMultiply();
-  *handle = pdfium::MakeUnique<CFX_ImageRenderer>(
-      m_pBitmap, m_pClipRgn.get(), pSource, bitmap_alpha, argb, matrix, options,
-      m_bRgbByteOrder);
-#endif  // _SKIA_SUPPORT_PATHS_
-  return true;
+  return StartDIBitsSkia(
+      pSource, FX_RECT(0, 0, pSource->GetWidth(), pSource->GetHeight()),
+      bitmap_alpha, argb, matrix, options, blend_type);
 }
 
 bool CFX_SkiaDeviceDriver::ContinueDIBits(CFX_ImageRenderer* handle,
                                           PauseIndicatorIface* pPause) {
-#ifdef _SKIA_SUPPORT_
-  m_pCache->FlushForDraw();
   return false;
-#endif  // _SKIA_SUPPORT_
-
-#ifdef _SKIA_SUPPORT_PATHS_
-  Flush();
-  if (!m_pBitmap->GetBuffer()) {
-    return true;
-  }
-  return handle->Continue(pPause);
-#endif  // _SKIA_SUPPORT_PATHS_
 }
 
-#if defined _SKIA_SUPPORT_
-void CFX_SkiaDeviceDriver::PreMultiply(
-    const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
-  pDIBitmap->PreMultiply();
-}
-#endif  // _SKIA_SUPPORT_
-
-void CFX_DIBitmap::PreMultiply() {
-  if (this->GetBPP() != 32)
-    return;
-  void* buffer = this->GetBuffer();
-  if (!buffer)
-    return;
-#if defined _SKIA_SUPPORT_PATHS_
-  Format priorFormat = m_nFormat;
-  m_nFormat = Format::kPreMultiplied;
-  if (priorFormat != Format::kUnPreMultiplied)
-    return;
-#endif
-  int height = this->GetHeight();
-  int width = this->GetWidth();
-  int rowBytes = this->GetPitch();
-  SkImageInfo unpremultipliedInfo =
-      SkImageInfo::Make(width, height, kN32_SkColorType, kUnpremul_SkAlphaType);
-  SkPixmap unpremultiplied(unpremultipliedInfo, buffer, rowBytes);
-  SkImageInfo premultipliedInfo =
-      SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType);
-  SkPixmap premultiplied(premultipliedInfo, buffer, rowBytes);
-  unpremultiplied.readPixels(premultiplied);
-  this->DebugVerifyBitmapIsPreMultiplied(nullptr);
-}
-
-#ifdef _SKIA_SUPPORT_PATHS_
 void CFX_DIBitmap::UnPreMultiply() {
-  if (this->GetBPP() != 32)
+  if (GetBPP() != 32)
     return;
-  void* buffer = this->GetBuffer();
+
+  void* buffer = GetWritableBuffer().data();
   if (!buffer)
     return;
-  Format priorFormat = m_nFormat;
+
+  Format prior_format = m_nFormat;
   m_nFormat = Format::kUnPreMultiplied;
-  if (priorFormat != Format::kPreMultiplied)
+  if (prior_format == Format::kUnPreMultiplied)
     return;
-  this->DebugVerifyBitmapIsPreMultiplied(nullptr);
-  int height = this->GetHeight();
-  int width = this->GetWidth();
-  int rowBytes = this->GetPitch();
-  SkImageInfo premultipliedInfo =
+
+  int height = GetHeight();
+  int width = GetWidth();
+  int row_bytes = GetPitch();
+  SkImageInfo premultiplied_info =
       SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType);
-  SkPixmap premultiplied(premultipliedInfo, buffer, rowBytes);
-  SkImageInfo unpremultipliedInfo =
+  SkPixmap premultiplied(premultiplied_info, buffer, row_bytes);
+  SkImageInfo unpremultiplied_info =
       SkImageInfo::Make(width, height, kN32_SkColorType, kUnpremul_SkAlphaType);
-  SkPixmap unpremultiplied(unpremultipliedInfo, buffer, rowBytes);
+  SkPixmap unpremultiplied(unpremultiplied_info, buffer, row_bytes);
   premultiplied.readPixels(unpremultiplied);
 }
-#endif  // _SKIA_SUPPORT_PATHS_
 
-#ifdef _SKIA_SUPPORT_
+void CFX_DIBitmap::ForcePreMultiply() {
+  m_nFormat = Format::kPreMultiplied;
+}
+
+bool CFX_DIBitmap::IsPremultiplied() const {
+  return m_nFormat == Format::kPreMultiplied;
+}
+
 bool CFX_SkiaDeviceDriver::DrawBitsWithMask(
     const RetainPtr<CFX_DIBBase>& pSource,
     const RetainPtr<CFX_DIBBase>& pMask,
@@ -2607,37 +1483,41 @@
     const CFX_Matrix& matrix,
     BlendMode blend_type) {
   DebugValidate(m_pBitmap, m_pBackdropBitmap);
-  std::unique_ptr<uint8_t, FxFreeDeleter> src8Storage, mask8Storage;
-  std::unique_ptr<uint32_t, FxFreeDeleter> src32Storage, mask32Storage;
-  SkBitmap skBitmap, skMask;
-  int srcWidth, srcHeight, maskWidth, maskHeight;
-  if (!Upsample(pSource, src8Storage, src32Storage, &skBitmap, &srcWidth,
-                &srcHeight, false)) {
+
+  sk_sp<SkImage> skia_source = pSource->RealizeSkImage();
+  if (!skia_source) {
     return false;
   }
-  if (!Upsample(pMask, mask8Storage, mask32Storage, &skMask, &maskWidth,
-                &maskHeight, 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);
+
+    const int src_width = pSource->GetWidth();
+    const int src_height = pSource->GetHeight();
     SkMatrix skMatrix;
-    SetBitmapMatrix(matrix, srcWidth, srcHeight, &skMatrix);
+    SetBitmapMatrix(matrix, src_width, src_height, &skMatrix);
     m_pCanvas->concat(skMatrix);
     SkPaint paint;
-    SetBitmapPaint(pSource->IsAlphaMask(), 0xFFFFFFFF, bitmap_alpha, blend_type,
-                   &paint);
-    sk_sp<SkImage> skSrc = SkImage::MakeFromBitmap(skBitmap);
-    sk_sp<SkShader> skSrcShader =
-        skSrc->makeShader(SkTileMode::kClamp, SkTileMode::kClamp);
-    sk_sp<SkImage> skMaskImage = SkImage::MakeFromBitmap(skMask);
-    sk_sp<SkShader> skMaskShader =
-        skMaskImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp);
-    paint.setShader(
-        SkShaders::Blend(SkBlendMode::kSrcIn, skMaskShader, skSrcShader));
-    SkRect r = {0, 0, SkIntToScalar(srcWidth), SkIntToScalar(srcHeight)};
-    m_pCanvas->drawRect(r, paint);
+    SetBitmapPaintForMerge(pSource->IsMaskFormat(), !m_FillOptions.aliased_path,
+                           0xFFFFFFFF, bitmap_alpha, blend_type, &paint);
+    sk_sp<SkShader> source_shader = skia_source->makeShader(
+        SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions());
+    sk_sp<SkShader> mask_shader = skia_mask->makeShader(
+        SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions());
+    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;
 }
@@ -2649,7 +1529,7 @@
     int dest_top,
     int bitmap_alpha,
     BlendMode blend_type) {
-  if (!m_pBitmap || !m_pBitmap->GetBuffer())
+  if (!m_pBitmap || m_pBitmap->GetBuffer().empty())
     return true;
 
   CFX_Matrix m = CFX_RenderDevice::GetFlipMatrix(
@@ -2657,128 +1537,118 @@
   return DrawBitsWithMask(pBitmap, pMask, bitmap_alpha, m, blend_type);
 }
 
+void CFX_SkiaDeviceDriver::SetGroupKnockout(bool group_knockout) {
+  m_bGroupKnockout = group_knockout;
+}
+
 void CFX_SkiaDeviceDriver::Clear(uint32_t color) {
   m_pCanvas->clear(color);
 }
-#endif  // _SKIA_SUPPORT_
 
-void CFX_SkiaDeviceDriver::Dump() const {
-#if SHOW_SKIA_PATH && defined _SKIA_SUPPORT_
-  if (m_pCache)
-    m_pCache->Dump(this);
-#endif  // SHOW_SKIA_PATH && defined _SKIA_SUPPORT_
+bool CFX_SkiaDeviceDriver::StartDIBitsSkia(
+    const RetainPtr<CFX_DIBBase>& pSource,
+    const FX_RECT& src_rect,
+    int bitmap_alpha,
+    uint32_t argb,
+    const CFX_Matrix& matrix,
+    const FXDIB_ResampleOptions& options,
+    BlendMode blend_type) {
+  DebugValidate(m_pBitmap, m_pBackdropBitmap);
+
+  sk_sp<SkImage> skia_source = pSource->RealizeSkImage();
+  if (!skia_source) {
+    return false;
+  }
+
+  {
+    SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
+
+    const int width = pSource->GetWidth();
+    const int height = pSource->GetHeight();
+    SkMatrix skMatrix;
+    SetBitmapMatrix(matrix, width, height, &skMatrix);
+    m_pCanvas->concat(skMatrix);
+    SkPaint paint;
+    SetBitmapPaint(pSource->IsMaskFormat(), !m_FillOptions.aliased_path,
+                   bitmap_alpha, argb, blend_type, &paint);
+
+    bool use_interpolate_bilinear = options.bInterpolateBilinear;
+    if (!use_interpolate_bilinear) {
+      float dest_width = ceilf(matrix.GetXUnit());
+      float dest_height = ceilf(matrix.GetYUnit());
+      if (pdfium::base::IsValueInRangeForNumericType<int>(dest_width) &&
+          pdfium::base::IsValueInRangeForNumericType<int>(dest_height)) {
+        use_interpolate_bilinear = CStretchEngine::UseInterpolateBilinear(
+            options, static_cast<int>(dest_width),
+            static_cast<int>(dest_height), width, height);
+      }
+    }
+    SkSamplingOptions sampling_options;
+    if (use_interpolate_bilinear) {
+      sampling_options =
+          SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear);
+    }
+
+    m_pCanvas->drawImageRect(
+        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;
 }
 
-#ifdef _SKIA_SUPPORT_
-void CFX_SkiaDeviceDriver::DebugVerifyBitmapIsPreMultiplied() const {
-  if (m_pBackdropBitmap)
-    m_pBackdropBitmap->DebugVerifyBitmapIsPreMultiplied(nullptr);
-}
-#endif  // _SKIA_SUPPORT_
+CFX_SkiaDeviceDriver::CharDetail::CharDetail() = default;
+CFX_SkiaDeviceDriver::CharDetail::~CharDetail() = default;
 
-CFX_DefaultRenderDevice::CFX_DefaultRenderDevice() {}
-
-#ifdef _SKIA_SUPPORT_
 void CFX_DefaultRenderDevice::Clear(uint32_t color) {
-  CFX_SkiaDeviceDriver* skDriver =
-      static_cast<CFX_SkiaDeviceDriver*>(GetDeviceDriver());
-  skDriver->Clear(color);
+  static_cast<CFX_SkiaDeviceDriver*>(GetDeviceDriver())->Clear(color);
 }
 
-SkPictureRecorder* CFX_DefaultRenderDevice::CreateRecorder(int size_x,
-                                                           int size_y) {
-  CFX_SkiaDeviceDriver* skDriver = new CFX_SkiaDeviceDriver(size_x, size_y);
-  SetDeviceDriver(pdfium::WrapUnique(skDriver));
-  return skDriver->GetRecorder();
-}
-#endif  // _SKIA_SUPPORT_
-
-bool CFX_DefaultRenderDevice::Attach(
-    const RetainPtr<CFX_DIBitmap>& pBitmap,
+bool CFX_DefaultRenderDevice::AttachSkiaImpl(
+    RetainPtr<CFX_DIBitmap> pBitmap,
     bool bRgbByteOrder,
-    const RetainPtr<CFX_DIBitmap>& pBackdropBitmap,
+    RetainPtr<CFX_DIBitmap> pBackdropBitmap,
     bool bGroupKnockout) {
   if (!pBitmap)
     return false;
   SetBitmap(pBitmap);
-  SetDeviceDriver(pdfium::MakeUnique<CFX_SkiaDeviceDriver>(
-      pBitmap, bRgbByteOrder, pBackdropBitmap, bGroupKnockout));
-  return true;
-}
-
-#ifdef _SKIA_SUPPORT_
-bool CFX_DefaultRenderDevice::AttachRecorder(SkPictureRecorder* recorder) {
-  if (!recorder)
+  auto driver =
+      CFX_SkiaDeviceDriver::Create(std::move(pBitmap), bRgbByteOrder,
+                                   std::move(pBackdropBitmap), bGroupKnockout);
+  if (!driver)
     return false;
-  SetDeviceDriver(pdfium::MakeUnique<CFX_SkiaDeviceDriver>(recorder));
+
+  SetDeviceDriver(std::move(driver));
   return true;
 }
-#endif  // _SKIA_SUPPORT_
 
-bool CFX_DefaultRenderDevice::Create(
+bool CFX_DefaultRenderDevice::AttachCanvas(SkCanvas* canvas) {
+  if (!canvas) {
+    return false;
+  }
+  SetDeviceDriver(std::make_unique<CFX_SkiaDeviceDriver>(canvas));
+  return true;
+}
+
+bool CFX_DefaultRenderDevice::CreateSkia(
     int width,
     int height,
     FXDIB_Format format,
-    const RetainPtr<CFX_DIBitmap>& pBackdropBitmap) {
+    RetainPtr<CFX_DIBitmap> pBackdropBitmap) {
   auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pBitmap->Create(width, height, format)) {
+  if (!pBitmap->Create(width, height, format))
     return false;
-  }
+
   SetBitmap(pBitmap);
-  SetDeviceDriver(pdfium::MakeUnique<CFX_SkiaDeviceDriver>(
-      pBitmap, false, pBackdropBitmap, false));
+  auto driver = CFX_SkiaDeviceDriver::Create(std::move(pBitmap), false,
+                                             std::move(pBackdropBitmap), false);
+  if (!driver)
+    return false;
+
+  SetDeviceDriver(std::move(driver));
   return true;
 }
-
-CFX_DefaultRenderDevice::~CFX_DefaultRenderDevice() {
-  Flush(true);
-}
-
-#ifdef _SKIA_SUPPORT_
-void CFX_DefaultRenderDevice::DebugVerifyBitmapIsPreMultiplied() const {
-#ifdef SK_DEBUG
-  CFX_SkiaDeviceDriver* skDriver =
-      static_cast<CFX_SkiaDeviceDriver*>(GetDeviceDriver());
-  if (skDriver)
-    skDriver->DebugVerifyBitmapIsPreMultiplied();
-#endif  // SK_DEBUG
-}
-
-bool CFX_DefaultRenderDevice::SetBitsWithMask(
-    const RetainPtr<CFX_DIBBase>& pBitmap,
-    const RetainPtr<CFX_DIBBase>& pMask,
-    int left,
-    int top,
-    int bitmap_alpha,
-    BlendMode blend_type) {
-  CFX_SkiaDeviceDriver* skDriver =
-      static_cast<CFX_SkiaDeviceDriver*>(GetDeviceDriver());
-  if (skDriver)
-    return skDriver->SetBitsWithMask(pBitmap, pMask, left, top, bitmap_alpha,
-                                     blend_type);
-  return false;
-}
-#endif  // _SKIA_SUPPORT_
-
-void CFX_DIBBase::DebugVerifyBitmapIsPreMultiplied(void* opt) const {
-#ifdef SK_DEBUG
-  ASSERT(GetBPP() == 32);
-  const uint32_t* buffer = (const uint32_t*)(opt ? opt : GetBuffer());
-  int width = GetWidth();
-  int height = GetHeight();
-  // verify that input is really premultiplied
-  for (int y = 0; y < height; ++y) {
-    const uint32_t* srcRow = buffer + y * width;
-    for (int x = 0; x < width; ++x) {
-      uint8_t a = SkGetPackedA32(srcRow[x]);
-      uint8_t r = SkGetPackedR32(srcRow[x]);
-      uint8_t g = SkGetPackedG32(srcRow[x]);
-      uint8_t b = SkGetPackedB32(srcRow[x]);
-      SkA32Assert(a);
-      ASSERT(r <= a);
-      ASSERT(g <= a);
-      ASSERT(b <= a);
-    }
-  }
-#endif  // SK_DEBUG
-}
diff --git a/core/fxge/skia/fx_skia_device.h b/core/fxge/skia/fx_skia_device.h
index 2571769..3285f8f 100644
--- a/core/fxge/skia/fx_skia_device.h
+++ b/core/fxge/skia/fx_skia_device.h
@@ -1,38 +1,47 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef CORE_FXGE_SKIA_FX_SKIA_DEVICE_H_
 #define CORE_FXGE_SKIA_FX_SKIA_DEVICE_H_
 
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
+#include <stdint.h>
 
 #include <memory>
-#include <vector>
 
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/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/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_ClipRgn;
+class CFX_Font;
+class CFX_Matrix;
 class SkCanvas;
-class SkMatrix;
-class SkPaint;
-class SkPath;
-class SkPictureRecorder;
-class SkiaState;
+class SkSurface;
 class TextCharPos;
-struct SkIRect;
+struct CFX_TextRenderOptions;
+
+// Assumes Skia is not going to add non-data members to its fundamental types.
+FX_DATA_PARTITION_EXCEPTION(SkPoint);
+FX_DATA_PARTITION_EXCEPTION(SkRSXform);
 
 class CFX_SkiaDeviceDriver final : public RenderDeviceDriverIface {
  public:
-  CFX_SkiaDeviceDriver(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                       bool bRgbByteOrder,
-                       const RetainPtr<CFX_DIBitmap>& pBackdropBitmap,
-                       bool bGroupKnockout);
-#ifdef _SKIA_SUPPORT_
-  explicit CFX_SkiaDeviceDriver(SkPictureRecorder* recorder);
-  CFX_SkiaDeviceDriver(int size_x, int size_y);
-#endif
+  static std::unique_ptr<CFX_SkiaDeviceDriver> Create(
+      RetainPtr<CFX_DIBitmap> pBitmap,
+      bool bRgbByteOrder,
+      RetainPtr<CFX_DIBitmap> pBackdropBitmap,
+      bool bGroupKnockout);
+
+  explicit CFX_SkiaDeviceDriver(SkCanvas* canvas);
   ~CFX_SkiaDeviceDriver() override;
 
   /** Options */
@@ -45,25 +54,26 @@
 
   /** Set clipping path using filled region */
   bool SetClip_PathFill(
-      const CFX_PathData* pPathData,     // path info
-      const CFX_Matrix* pObject2Device,  // optional transformation
-      int fill_mode) override;           // fill mode, WINDING or ALTERNATE
+      const CFX_Path& path,                       // path info
+      const CFX_Matrix* pObject2Device,           // optional transformation
+      const CFX_FillRenderOptions& fill_options)  // fill options
+      override;
 
   /** Set clipping path using stroked region */
   bool SetClip_PathStroke(
-      const CFX_PathData* pPathData,     // path info
+      const CFX_Path& path,              // path info
       const CFX_Matrix* pObject2Device,  // required transformation
       const CFX_GraphStateData*
           pGraphState)  // graphic state, for pen attributes
       override;
 
   /** Draw a path */
-  bool DrawPath(const CFX_PathData* pPathData,
+  bool DrawPath(const CFX_Path& path,
                 const CFX_Matrix* pObject2Device,
                 const CFX_GraphStateData* pGraphState,
                 uint32_t fill_color,
                 uint32_t stroke_color,
-                int fill_mode,
+                const CFX_FillRenderOptions& fill_options,
                 BlendMode blend_type) override;
 
   bool FillRectWithBlend(const FX_RECT& rect,
@@ -91,18 +101,13 @@
                  int dest_left,
                  int dest_top,
                  BlendMode blend_type) override;
-#ifdef _SKIA_SUPPORT_
   bool SetBitsWithMask(const RetainPtr<CFX_DIBBase>& pBitmap,
                        const RetainPtr<CFX_DIBBase>& pMask,
                        int dest_left,
                        int dest_top,
                        int bitmap_alpha,
                        BlendMode blend_type) override;
-#endif
-
-#ifdef _SKIA_SUPPORT_PATHS_
-  void SetClipMask(const FX_RECT& clipBox, const SkPath& skClipPath);
-#endif
+  void SetGroupKnockout(bool group_knockout) override;
 
   bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                      uint32_t color,
@@ -131,12 +136,12 @@
                         const CFX_Matrix& matrix,
                         BlendMode blend_type);
 
-  bool DrawDeviceText(int nChars,
-                      const TextCharPos* pCharPos,
+  bool DrawDeviceText(pdfium::span<const TextCharPos> pCharPos,
                       CFX_Font* pFont,
                       const CFX_Matrix& mtObject2Device,
                       float font_size,
-                      uint32_t color) override;
+                      uint32_t color,
+                      const CFX_TextRenderOptions& options) override;
 
   int GetDriverType() const override;
 
@@ -146,43 +151,83 @@
                    int alpha,
                    bool bAlphaMode) override;
 
-  virtual uint8_t* GetBuffer() const;
+  bool MultiplyAlpha(float alpha) override;
+  bool MultiplyAlpha(const RetainPtr<CFX_DIBBase>& mask) override;
 
-  void PaintStroke(SkPaint* spaint,
-                   const CFX_GraphStateData* pGraphState,
-                   const SkMatrix& matrix);
   void Clear(uint32_t color);
-  void Flush() override;
-  SkPictureRecorder* GetRecorder() const { return m_pRecorder; }
-  void PreMultiply();
-  static void PreMultiply(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
-  SkCanvas* SkiaCanvas() { return m_pCanvas; }
-  void DebugVerifyBitmapIsPreMultiplied() const;
   void Dump() const;
 
-  bool GetGroupKnockout() const { return m_bGroupKnockout; }
-
-#ifdef _SKIA_SUPPORT_PATHS_
-  const CFX_ClipRgn* clip_region() const { return m_pClipRgn.get(); }
-  const std::vector<std::unique_ptr<CFX_ClipRgn>>& stack() const {
-    return m_StateStack;
-  }
-#endif
-
  private:
+  class CharDetail {
+   public:
+    CharDetail();
+    ~CharDetail();
+
+    const DataVector<SkPoint>& GetPositions() const { return m_positions; }
+    void SetPositionAt(size_t index, const SkPoint& position) {
+      m_positions[index] = position;
+    }
+    const DataVector<uint16_t>& GetGlyphs() const { return m_glyphs; }
+    void SetGlyphAt(size_t index, uint16_t glyph) { m_glyphs[index] = glyph; }
+    const DataVector<uint32_t>& GetFontCharWidths() const {
+      return m_fontCharWidths;
+    }
+    void SetFontCharWidthAt(size_t index, uint32_t width) {
+      m_fontCharWidths[index] = width;
+    }
+    size_t Count() const {
+      DCHECK_EQ(m_positions.size(), m_glyphs.size());
+      return m_glyphs.size();
+    }
+    void SetCount(size_t count) {
+      m_positions.resize(count);
+      m_glyphs.resize(count);
+      m_fontCharWidths.resize(count);
+    }
+
+   private:
+    DataVector<SkPoint> m_positions;  // accumulator for text positions
+    DataVector<uint16_t> m_glyphs;    // accumulator for text glyphs
+    // accumulator for glyphs' width defined in pdf
+    DataVector<uint32_t> m_fontCharWidths;
+  };
+
+  CFX_SkiaDeviceDriver(RetainPtr<CFX_DIBitmap> pBitmap,
+                       bool bRgbByteOrder,
+                       RetainPtr<CFX_DIBitmap> pBackdropBitmap,
+                       bool bGroupKnockout);
+
+  bool TryDrawText(pdfium::span<const TextCharPos> char_pos,
+                   const CFX_Font* pFont,
+                   const CFX_Matrix& matrix,
+                   float font_size,
+                   uint32_t color,
+                   const CFX_TextRenderOptions& options);
+
+  bool StartDIBitsSkia(const RetainPtr<CFX_DIBBase>& pBitmap,
+                       const FX_RECT& src_rect,
+                       int bitmap_alpha,
+                       uint32_t color,
+                       const CFX_Matrix& matrix,
+                       const FXDIB_ResampleOptions& options,
+                       BlendMode blend_type);
+
   RetainPtr<CFX_DIBitmap> m_pBitmap;
   RetainPtr<CFX_DIBitmap> m_pBackdropBitmap;
-  SkCanvas* m_pCanvas;
-  SkPictureRecorder* const m_pRecorder;
-  std::unique_ptr<SkiaState> m_pCache;
-#ifdef _SKIA_SUPPORT_PATHS_
-  std::unique_ptr<CFX_ClipRgn> m_pClipRgn;
-  std::vector<std::unique_ptr<CFX_ClipRgn>> m_StateStack;
-  int m_FillFlags;
+
+  // The input bitmap passed by the render device. Only used when the input
+  // bitmap is 24 bpp and cannot be directly used as the back of a SkCanvas.
+  RetainPtr<CFX_DIBitmap> m_pOriginalBitmap;
+
+  sk_sp<SkSurface> surface_;
+  UnownedPtr<SkCanvas> m_pCanvas;
+  CFX_FillRenderOptions m_FillOptions;
   bool m_bRgbByteOrder;
-#endif
   bool m_bGroupKnockout;
+
+  CharDetail m_charDetails;
+  // accumulator for txt rotate/scale/translate
+  DataVector<SkRSXform> m_rsxform;
 };
-#endif  // defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
 
 #endif  // CORE_FXGE_SKIA_FX_SKIA_DEVICE_H_
diff --git a/core/fxge/skia/fx_skia_device_embeddertest.cpp b/core/fxge/skia/fx_skia_device_embeddertest.cpp
index 6631e4d..ca38a1e 100644
--- a/core/fxge/skia/fx_skia_device_embeddertest.cpp
+++ b/core/fxge/skia/fx_skia_device_embeddertest.cpp
@@ -1,22 +1,43 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "core/fxge/skia/fx_skia_device.h"
+
+#include <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"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
-#include "core/fxge/skia/fx_skia_device.h"
+#include "core/fxge/cfx_textrenderoptions.h"
+#include "core/fxge/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 };
@@ -39,57 +60,67 @@
 void CommonTest(CFX_SkiaDeviceDriver* driver, const State& state) {
   TextCharPos charPos[1];
   charPos[0].m_Origin = CFX_PointF(0, 1);
-  charPos[0].m_GlyphIndex = 1;
-  charPos[0].m_FontCharWidth = 4u;
+  charPos[0].m_GlyphIndex = 0;
+  charPos[0].m_FontCharWidth = 4;
 
   CFX_Font font;
-  float fontSize = 1;
-  CFX_PathData clipPath, clipPath2;
+  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);
   clipPath2.AppendRect(0, 0, 2, 1);
   CFX_Matrix clipMatrix;
   CFX_Matrix clipMatrix2(1, 0, 0, 1, 0, 1);
   driver->SaveState();
-  CFX_PathData path1;
+  CFX_Path path1;
   path1.AppendRect(0, 0, 1, 2);
 
   CFX_Matrix matrix;
   CFX_Matrix matrix2;
   matrix2.Translate(1, 0);
   CFX_GraphStateData graphState;
+  // 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)
-    driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
+    driver->SetClip_PathFill(clipPath, &clipMatrix, CFX_FillRenderOptions());
   if (state.m_graphic == State::Graphic::kPath) {
-    driver->DrawPath(&path1, &matrix, &graphState, 0xFF112233, 0,
-                     FXFILL_WINDING, BlendMode::kNormal);
+    driver->DrawPath(path1, &matrix, &graphState, 0xFF112233, 0,
+                     CFX_FillRenderOptions::WindingOptions(),
+                     BlendMode::kNormal);
   } else if (state.m_graphic == State::Graphic::kText) {
-    driver->DrawDeviceText(SK_ARRAY_COUNT(charPos), charPos, &font, matrix,
-                           fontSize, 0xFF445566);
+    driver->DrawDeviceText(charPos, &font, matrix, fontSize, 0xFF445566,
+                           kTextOptions);
   }
   if (state.m_save == State::Save::kYes)
     driver->RestoreState(true);
-  CFX_PathData path2;
+  CFX_Path path2;
   path2.AppendRect(0, 0, 2, 2);
   if (state.m_change == State::Change::kYes) {
     if (state.m_graphic == State::Graphic::kPath)
-      graphState.m_LineCap = CFX_GraphStateData::LineCapRound;
+      graphState.m_LineCap = CFX_GraphStateData::LineCap::kRound;
     else if (state.m_graphic == State::Graphic::kText)
       fontSize = 2;
   }
   if (state.m_clip == State::Clip::kSame)
-    driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
+    driver->SetClip_PathFill(clipPath, &clipMatrix, CFX_FillRenderOptions());
   else if (state.m_clip == State::Clip::kDifferentPath)
-    driver->SetClip_PathFill(&clipPath2, &clipMatrix, 0);
+    driver->SetClip_PathFill(clipPath2, &clipMatrix, CFX_FillRenderOptions());
   else if (state.m_clip == State::Clip::kDifferentMatrix)
-    driver->SetClip_PathFill(&clipPath, &clipMatrix2, 0);
+    driver->SetClip_PathFill(clipPath, &clipMatrix2, CFX_FillRenderOptions());
   if (state.m_graphic == State::Graphic::kPath) {
-    driver->DrawPath(&path2, &matrix2, &graphState, 0xFF112233, 0,
-                     FXFILL_WINDING, BlendMode::kNormal);
+    driver->DrawPath(path2, &matrix2, &graphState, 0xFF112233, 0,
+                     CFX_FillRenderOptions::WindingOptions(),
+                     BlendMode::kNormal);
   } else if (state.m_graphic == State::Graphic::kText) {
-    driver->DrawDeviceText(SK_ARRAY_COUNT(charPos), charPos, &font, matrix2,
-                           fontSize, 0xFF445566);
+    driver->DrawDeviceText(charPos, &font, matrix2, fontSize, 0xFF445566,
+                           kTextOptions);
   }
   if (state.m_save == State::Save::kYes)
     driver->RestoreState(false);
@@ -97,22 +128,22 @@
 }
 
 void OutOfSequenceClipTest(CFX_SkiaDeviceDriver* driver, const State&) {
-  CFX_PathData clipPath;
+  CFX_Path clipPath;
   clipPath.AppendRect(1, 0, 3, 1);
   CFX_Matrix clipMatrix;
   driver->SaveState();
-  driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
+  driver->SetClip_PathFill(clipPath, &clipMatrix, CFX_FillRenderOptions());
   driver->RestoreState(true);
   driver->SaveState();
-  driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
+  driver->SetClip_PathFill(clipPath, &clipMatrix, CFX_FillRenderOptions());
   driver->RestoreState(false);
   driver->RestoreState(false);
 
   driver->SaveState();
   driver->SaveState();
-  driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
+  driver->SetClip_PathFill(clipPath, &clipMatrix, CFX_FillRenderOptions());
   driver->RestoreState(true);
-  driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
+  driver->SetClip_PathFill(clipPath, &clipMatrix, CFX_FillRenderOptions());
   driver->RestoreState(false);
   driver->RestoreState(false);
 }
@@ -124,27 +155,66 @@
   ScopedFPDFBitmap bitmap(FPDFBitmap_Create(kWidth, kHeight, 1));
   ASSERT_TRUE(bitmap);
   FPDFBitmap_FillRect(bitmap.get(), 0, 0, kWidth, kHeight, 0x00000000);
-  CFX_DefaultRenderDevice device;
   RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap.get()));
-  device.Attach(pBitmap, false, nullptr, false);
-  auto* driver = static_cast<CFX_SkiaDeviceDriver*>(device.GetDeviceDriver());
-  (*Test)(driver, state);
-  driver->Flush();
+  auto driver = CFX_SkiaDeviceDriver::Create(pBitmap, false, nullptr, false);
+  ASSERT_TRUE(driver);
+  (*Test)(driver.get(), state);
   uint32_t pixel = pBitmap->GetPixel(0, 0);
   EXPECT_EQ(state.m_pixel, pixel);
-#ifdef SK_DEBUG
-  if (!driver)  // force dump to be linked in so it can be called from debugger
-    driver->Dump();
-#endif
 }
 
+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) {
+  if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    return;
   Harness(&EmptyTest, {});
 }
 
 TEST(fxge, SkiaStatePath) {
+  if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    return;
   Harness(&CommonTest, {State::Change::kNo, State::Save::kYes,
                         State::Clip::kSame, State::Graphic::kPath, 0xFF112233});
   Harness(&CommonTest,
@@ -158,17 +228,64 @@
                         State::Graphic::kPath, 0xFF112233});
 }
 
-#ifdef _SKIA_SUPPORT_
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-TEST(fxge, DISABLED_SkiaStateText) {
+TEST(fxge, SkiaStateText) {
+  if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    return;
+
   Harness(&CommonTest,
           {State::Change::kNo, State::Save::kYes, State::Clip::kDifferentMatrix,
            State::Graphic::kText, 0xFF445566});
   Harness(&CommonTest, {State::Change::kNo, State::Save::kYes,
                         State::Clip::kSame, State::Graphic::kText, 0xFF445566});
 }
-#endif
 
 TEST(fxge, SkiaStateOOSClip) {
+  if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    return;
   Harness(&OutOfSequenceClipTest, {});
 }
+
+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 9a6fbc7..90cdb19 100644
--- a/core/fxge/systemfontinfo_iface.h
+++ b/core/fxge/systemfontinfo_iface.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,34 +7,33 @@
 #ifndef CORE_FXGE_SYSTEMFONTINFO_IFACE_H_
 #define CORE_FXGE_SYSTEMFONTINFO_IFACE_H_
 
-#include <memory>
+#include <stddef.h>
+#include <stdint.h>
 
+#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');
 
 class SystemFontInfoIface {
  public:
-  static std::unique_ptr<SystemFontInfoIface> CreateDefault(
-      const char** pUserPaths);
-
   virtual ~SystemFontInfoIface() = default;
 
   virtual bool EnumFontList(CFX_FontMapper* pMapper) = 0;
   virtual void* MapFont(int weight,
                         bool bItalic,
-                        int charset,
+                        FX_Charset charset,
                         int pitch_family,
-                        const char* face) = 0;
-  virtual void* GetFont(const char* face) = 0;
-  virtual uint32_t GetFontData(void* hFont,
-                               uint32_t table,
-                               pdfium::span<uint8_t> buffer) = 0;
+                        const ByteString& face) = 0;
+  virtual void* GetFont(const ByteString& face) = 0;
+  virtual size_t GetFontData(void* hFont,
+                             uint32_t table,
+                             pdfium::span<uint8_t> buffer) = 0;
   virtual bool GetFaceName(void* hFont, ByteString* name) = 0;
-  virtual bool GetFontCharset(void* hFont, int* charset) = 0;
-  virtual int GetFaceIndex(void* hFont);
+  virtual bool GetFontCharset(void* hFont, FX_Charset* charset) = 0;
   virtual void DeleteFont(void* hFont) = 0;
 };
 
diff --git a/core/fxge/text_char_pos.cpp b/core/fxge/text_char_pos.cpp
index cf88b96..52ad4c1 100644
--- a/core/fxge/text_char_pos.cpp
+++ b/core/fxge/text_char_pos.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,3 +11,13 @@
 TextCharPos::TextCharPos(const TextCharPos&) = default;
 
 TextCharPos::~TextCharPos() = default;
+
+CFX_Matrix TextCharPos::GetEffectiveMatrix(const CFX_Matrix& matrix) const {
+  CFX_Matrix new_matrix;
+  if (m_bGlyphAdjust) {
+    new_matrix = CFX_Matrix(m_AdjustMatrix[0], m_AdjustMatrix[1],
+                            m_AdjustMatrix[2], m_AdjustMatrix[3], 0, 0);
+  }
+  new_matrix.Concat(matrix);
+  return new_matrix;
+}
diff --git a/core/fxge/text_char_pos.h b/core/fxge/text_char_pos.h
index c211be8..0877a26 100644
--- a/core/fxge/text_char_pos.h
+++ b/core/fxge/text_char_pos.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #ifndef CORE_FXGE_TEXT_CHAR_POS_H_
 #define CORE_FXGE_TEXT_CHAR_POS_H_
 
+#include "build/build_config.h"
 #include "core/fxcrt/fx_coordinates.h"
 
 class TextCharPos {
@@ -15,17 +16,19 @@
   TextCharPos(const TextCharPos&);
   ~TextCharPos();
 
+  CFX_Matrix GetEffectiveMatrix(const CFX_Matrix& matrix) const;
+
   CFX_PointF m_Origin;
   uint32_t m_Unicode = 0;
   uint32_t m_GlyphIndex = 0;
-  uint32_t m_FontCharWidth = 0;
-#if defined(OS_MACOSX)
+  int m_FontCharWidth = 0;
+#if BUILDFLAG(IS_APPLE)
   uint32_t m_ExtGID = 0;
 #endif
   int32_t m_FallbackFontPosition = 0;
   bool m_bGlyphAdjust = false;
   bool m_bFontStyle = false;
-  float m_AdjustMatrix[4];
+  float m_AdjustMatrix[4] = {};
 };
 
 #endif  // CORE_FXGE_TEXT_CHAR_POS_H_
diff --git a/core/fxge/text_glyph_pos.cpp b/core/fxge/text_glyph_pos.cpp
index 3e3c4ad..d887d08 100644
--- a/core/fxge/text_glyph_pos.cpp
+++ b/core/fxge/text_glyph_pos.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,18 +15,19 @@
 
 TextGlyphPos::~TextGlyphPos() = default;
 
-Optional<CFX_Point> TextGlyphPos::GetOrigin(const CFX_Point& offset) const {
+absl::optional<CFX_Point> TextGlyphPos::GetOrigin(
+    const CFX_Point& offset) const {
   FX_SAFE_INT32 left = m_Origin.x;
   left += m_pGlyph->left();
   left -= offset.x;
   if (!left.IsValid())
-    return {};
+    return absl::nullopt;
 
   FX_SAFE_INT32 top = m_Origin.y;
   top -= m_pGlyph->top();
   top -= offset.y;
   if (!top.IsValid())
-    return {};
+    return absl::nullopt;
 
   return CFX_Point(left.ValueOrDie(), top.ValueOrDie());
 }
diff --git a/core/fxge/text_glyph_pos.h b/core/fxge/text_glyph_pos.h
index b786432..36935d8 100644
--- a/core/fxge/text_glyph_pos.h
+++ b/core/fxge/text_glyph_pos.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,7 @@
 #include "core/fxcrt/fx_coordinates.h"
 
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CFX_GlyphBitmap;
 
@@ -20,11 +20,11 @@
   TextGlyphPos(const TextGlyphPos&);
   ~TextGlyphPos();
 
-  Optional<CFX_Point> GetOrigin(const CFX_Point& offset) const;
+  absl::optional<CFX_Point> GetOrigin(const CFX_Point& offset) const;
 
   UnownedPtr<const CFX_GlyphBitmap> m_pGlyph;
   CFX_Point m_Origin;
-  CFX_PointF m_fOrigin;
+  CFX_PointF m_fDeviceOrigin;
 };
 
 #endif  // CORE_FXGE_TEXT_GLYPH_POS_H_
diff --git a/core/fxge/win32/DEPS b/core/fxge/win32/DEPS
new file mode 100644
index 0000000..fa830d1
--- /dev/null
+++ b/core/fxge/win32/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  'cgdi_device_driver.cpp': [
+    '+third_party/agg23/agg_clip_liang_barsky.h',
+  ]
+}
diff --git a/core/fxge/win32/cfx_psfonttracker.cpp b/core/fxge/win32/cfx_psfonttracker.cpp
new file mode 100644
index 0000000..103ff6d
--- /dev/null
+++ b/core/fxge/win32/cfx_psfonttracker.cpp
@@ -0,0 +1,31 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/win32/cfx_psfonttracker.h"
+
+#include "core/fxge/cfx_font.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+
+CFX_PSFontTracker::CFX_PSFontTracker() = default;
+
+CFX_PSFontTracker::~CFX_PSFontTracker() = default;
+
+void CFX_PSFontTracker::AddFontObject(const CFX_Font* font) {
+  uint64_t tag = font->GetObjectTag();
+  [[maybe_unused]] bool inserted;
+  if (tag != 0) {
+    inserted = seen_font_tags_.insert(tag).second;
+  } else {
+    inserted = seen_font_ptrs_.insert(UnownedPtr<const CFX_Font>(font)).second;
+  }
+  DCHECK(inserted);
+}
+
+bool CFX_PSFontTracker::SeenFontObject(const CFX_Font* font) const {
+  uint64_t tag = font->GetObjectTag();
+  if (tag != 0)
+    return pdfium::Contains(seen_font_tags_, tag);
+  return pdfium::Contains(seen_font_ptrs_, font);
+}
diff --git a/core/fxge/win32/cfx_psfonttracker.h b/core/fxge/win32/cfx_psfonttracker.h
new file mode 100644
index 0000000..e015f58
--- /dev/null
+++ b/core/fxge/win32/cfx_psfonttracker.h
@@ -0,0 +1,35 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXGE_WIN32_CFX_PSFONTTRACKER_H_
+#define CORE_FXGE_WIN32_CFX_PSFONTTRACKER_H_
+
+#include <stdint.h>
+
+#include <functional>
+#include <set>
+
+#include "core/fxcrt/unowned_ptr.h"
+
+class CFX_Font;
+
+class CFX_PSFontTracker {
+ public:
+  CFX_PSFontTracker();
+  ~CFX_PSFontTracker();
+
+  void AddFontObject(const CFX_Font* font);
+  bool SeenFontObject(const CFX_Font* font) const;
+
+ private:
+  // Tracks font objects via tags, so if two CFX_Font instances are for the same
+  // PDF object, then they are deduplicated.
+  std::set<uint64_t> seen_font_tags_;
+
+  // For fonts without valid tags, e.g. ones created in-memory, track them by
+  // pointer.
+  std::set<UnownedPtr<const CFX_Font>, std::less<>> seen_font_ptrs_;
+};
+
+#endif  // CORE_FXGE_WIN32_CFX_PSFONTTRACKER_H_
diff --git a/core/fxge/win32/cfx_psrenderer.cpp b/core/fxge/win32/cfx_psrenderer.cpp
index f9f7de2..c2da036 100644
--- a/core/fxge/win32/cfx_psrenderer.cpp
+++ b/core/fxge/win32/cfx_psrenderer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,61 +6,249 @@
 
 #include "core/fxge/win32/cfx_psrenderer.h"
 
+#include <math.h>
+#include <string.h>
+
 #include <algorithm>
+#include <array>
 #include <memory>
-#include <sstream>
+#include <string>
 #include <utility>
 
-#include "core/fxcrt/maybe_owned.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_fontcache.h"
 #include "core/fxge/cfx_gemodule.h"
 #include "core/fxge/cfx_glyphcache.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
-#include "core/fxge/dib/cfx_dibextractor.h"
+#include "core/fxge/dib/cfx_dibbase.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "core/fxge/freetype/fx_freetype.h"
 #include "core/fxge/text_char_pos.h"
-#include "core/fxge/win32/cpsoutput.h"
-#include "third_party/base/ptr_util.h"
+#include "core/fxge/win32/cfx_psfonttracker.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
-struct PSGlyph {
-  UnownedPtr<CFX_Font> m_pFont;
-  uint32_t m_GlyphIndex;
-  bool m_bGlyphAdjust;
-  float m_AdjustMatrix[4];
+namespace {
+
+bool CanEmbed(CFX_Font* font) {
+  FT_UShort fstype = FT_Get_FSType_Flags(font->GetFaceRec());
+  return (fstype & (FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING |
+                    FT_FSTYPE_BITMAP_EMBEDDING_ONLY)) == 0;
+}
+
+absl::optional<ByteString> GenerateType42SfntData(
+    const ByteString& psname,
+    pdfium::span<const uint8_t> font_data) {
+  if (font_data.empty())
+    return absl::nullopt;
+
+  // Per Type 42 font spec.
+  constexpr size_t kMaxSfntStringSize = 65535;
+  if (font_data.size() > kMaxSfntStringSize) {
+    // TODO(thestig): Fonts that are too big need to be written out in sections.
+    return absl::nullopt;
+  }
+
+  // Each byte is written as 2 ASCIIHex characters, so really 64 chars per line.
+  constexpr size_t kMaxBytesPerLine = 32;
+  fxcrt::ostringstream output;
+  output << "/" << psname << "_sfnts [\n<\n";
+  size_t bytes_per_line = 0;
+  char buf[2];
+  for (uint8_t datum : font_data) {
+    FXSYS_IntToTwoHexChars(datum, buf);
+    output << buf[0];
+    output << buf[1];
+    bytes_per_line++;
+    if (bytes_per_line == kMaxBytesPerLine) {
+      output << "\n";
+      bytes_per_line = 0;
+    }
+  }
+
+  // Pad with ASCIIHex NUL character per Type 42 font spec if needed.
+  if (!FX_IsOdd(font_data.size()))
+    output << "00";
+
+  output << "\n>\n] def\n";
+  return ByteString(output);
+}
+
+// The value to use with GenerateType42FontDictionary() below, and the max
+// number of entries supported for non-CID fonts.
+// Also used to avoid buggy fonts by writing out at least this many entries,
+// per note in Poppler's Type 42 generation code.
+constexpr size_t kGlyphsPerDescendantFont = 256;
+
+ByteString GenerateType42FontDictionary(const ByteString& psname,
+                                        const FX_RECT& bbox,
+                                        size_t num_glyphs,
+                                        size_t glyphs_per_descendant_font) {
+  DCHECK_LE(glyphs_per_descendant_font, kGlyphsPerDescendantFont);
+  CHECK_GT(glyphs_per_descendant_font, 0u);
+
+  const size_t descendant_font_count =
+      (num_glyphs + glyphs_per_descendant_font - 1) /
+      glyphs_per_descendant_font;
+
+  fxcrt::ostringstream output;
+  for (size_t i = 0; i < descendant_font_count; ++i) {
+    output << "8 dict begin\n";
+    output << "/FontType 42 def\n";
+    output << "/FontMatrix [1 0 0 1 0 0] def\n";
+    output << "/FontName /" << psname << "_" << i << " def\n";
+
+    output << "/Encoding " << glyphs_per_descendant_font << " array\n";
+    for (size_t j = 0, pos = i * glyphs_per_descendant_font;
+         j < glyphs_per_descendant_font; ++j, ++pos) {
+      if (pos >= num_glyphs)
+        break;
+
+      output << ByteString::Format("dup %d /c%02x put\n", j, j);
+    }
+    output << "readonly def\n";
+
+    // Note: `bbox` is LTRB, while /FontBBox is LBRT. Writing it out as LTRB
+    // gets the correct values.
+    output << "/FontBBox [" << bbox.left << " " << bbox.top << " " << bbox.right
+           << " " << bbox.bottom << "] def\n";
+
+    output << "/PaintType 0 def\n";
+
+    output << "/CharStrings " << glyphs_per_descendant_font + 1
+           << " dict dup begin\n";
+    output << "/.notdef 0 def\n";
+    for (size_t j = 0, pos = i * glyphs_per_descendant_font;
+         j < glyphs_per_descendant_font; ++j, ++pos) {
+      if (pos >= num_glyphs)
+        break;
+
+      output << ByteString::Format("/c%02x %d def\n", j, pos);
+    }
+    output << "end readonly def\n";
+
+    output << "/sfnts " << psname << "_sfnts def\n";
+    output << "FontName currentdict end definefont pop\n";
+  }
+
+  output << "6 dict begin\n";
+  output << "/FontName /" << psname << " def\n";
+  output << "/FontType 0 def\n";
+  output << "/FontMatrix [1 0 0 1 0 0] def\n";
+  output << "/FMapType 2 def\n";
+
+  output << "/Encoding [\n";
+  for (size_t i = 0; i < descendant_font_count; ++i)
+    output << i << "\n";
+  output << "] def\n";
+
+  output << "/FDepVector [\n";
+  for (size_t i = 0; i < descendant_font_count; ++i)
+    output << "/" << psname << "_" << i << " findfont\n";
+  output << "] def\n";
+
+  output << "FontName currentdict end definefont pop\n";
+  output << "%%EndResource\n";
+
+  return ByteString(output);
+}
+
+ByteString GenerateType42FontData(const CFX_Font* font) {
+  const FXFT_FaceRec* font_face_rec = font->GetFaceRec();
+  if (!font_face_rec)
+    return ByteString();
+
+  const ByteString psname = font->GetPsName();
+  DCHECK(!psname.IsEmpty());
+
+  absl::optional<ByteString> sfnt_data =
+      GenerateType42SfntData(psname, font->GetFontSpan());
+  if (!sfnt_data.has_value())
+    return ByteString();
+
+  ByteString output = "%%BeginResource: font ";
+  output += psname;
+  output += "\n";
+  output += sfnt_data.value();
+  output += GenerateType42FontDictionary(psname, font->GetRawBBox().value(),
+                                         font_face_rec->num_glyphs,
+                                         kGlyphsPerDescendantFont);
+  return output;
+}
+
+}  // namespace
+
+struct CFX_PSRenderer::Glyph {
+  Glyph(CFX_Font* font, uint32_t glyph_index)
+      : font(font), glyph_index(glyph_index) {}
+  Glyph(const Glyph& other) = delete;
+  Glyph& operator=(const Glyph&) = delete;
+  ~Glyph() = default;
+
+  UnownedPtr<CFX_Font> const font;
+  const uint32_t glyph_index;
+  absl::optional<std::array<float, 4>> adjust_matrix;
 };
 
-class CPSFont {
- public:
-  int m_nGlyphs;
-  PSGlyph m_Glyphs[256];
-};
+CFX_PSRenderer::FaxCompressResult::FaxCompressResult() = default;
 
-CFX_PSRenderer::CFX_PSRenderer(const EncoderIface* pEncoderIface)
-    : m_pEncoderIface(pEncoderIface) {}
+CFX_PSRenderer::FaxCompressResult::FaxCompressResult(
+    FaxCompressResult&&) noexcept = default;
 
-CFX_PSRenderer::~CFX_PSRenderer() = default;
+CFX_PSRenderer::FaxCompressResult& CFX_PSRenderer::FaxCompressResult::operator=(
+    FaxCompressResult&&) noexcept = default;
+
+CFX_PSRenderer::FaxCompressResult::~FaxCompressResult() = default;
+
+CFX_PSRenderer::PSCompressResult::PSCompressResult() = default;
+
+CFX_PSRenderer::PSCompressResult::PSCompressResult(
+    PSCompressResult&&) noexcept = default;
+
+CFX_PSRenderer::PSCompressResult& CFX_PSRenderer::PSCompressResult::operator=(
+    PSCompressResult&&) noexcept = default;
+
+CFX_PSRenderer::PSCompressResult::~PSCompressResult() = default;
+
+CFX_PSRenderer::CFX_PSRenderer(CFX_PSFontTracker* font_tracker,
+                               const EncoderIface* encoder_iface)
+    : m_pFontTracker(font_tracker), m_pEncoderIface(encoder_iface) {
+  DCHECK(m_pFontTracker);
+}
+
+CFX_PSRenderer::~CFX_PSRenderer() {
+  EndRendering();
+}
 
 void CFX_PSRenderer::Init(const RetainPtr<IFX_RetainableWriteStream>& pStream,
-                          int pslevel,
+                          RenderingLevel level,
                           int width,
-                          int height,
-                          bool bCmykOutput) {
-  m_PSLevel = pslevel;
+                          int height) {
+  DCHECK(pStream);
+
+  m_Level = level;
   m_pStream = pStream;
   m_ClipBox.left = 0;
   m_ClipBox.top = 0;
   m_ClipBox.right = width;
   m_ClipBox.bottom = height;
-  m_bCmykOutput = bCmykOutput;
 }
 
-bool CFX_PSRenderer::StartRendering() {
+void CFX_PSRenderer::StartRendering() {
   if (m_bInited)
-    return true;
+    return;
 
-  static const char init_str[] =
+  static const char kInitStr[] =
       "\nsave\n/im/initmatrix load def\n"
       "/n/newpath load def/m/moveto load def/l/lineto load def/c/curveto load "
       "def/h/closepath load def\n"
@@ -74,30 +262,44 @@
       "load def\n"
       "/cm/concat load def/Cm/currentmatrix load def/mx/matrix load "
       "def/sm/setmatrix load def\n";
-  m_pStream->WriteString(init_str);
+  WriteString(kInitStr);
   m_bInited = true;
-  return true;
 }
 
 void CFX_PSRenderer::EndRendering() {
   if (!m_bInited)
     return;
 
-  m_pStream->WriteString("\nrestore\n");
+  WriteString("\nrestore\n");
   m_bInited = false;
+
+  // Flush `m_PreambleOutput` if it is not empty.
+  std::streamoff preamble_pos = m_PreambleOutput.tellp();
+  if (preamble_pos > 0) {
+    m_pStream->WriteBlock(
+        {reinterpret_cast<const uint8_t*>(m_PreambleOutput.str().c_str()),
+         pdfium::base::checked_cast<size_t>(preamble_pos)});
+    m_PreambleOutput.str("");
+  }
+
+  // Flush `m_Output`. It's never empty because of the WriteString() call above.
+  m_pStream->WriteBlock(
+      {reinterpret_cast<const uint8_t*>(m_Output.str().c_str()),
+       pdfium::base::checked_cast<size_t>(std::streamoff(m_Output.tellp()))});
+  m_Output.str("");
 }
 
 void CFX_PSRenderer::SaveState() {
   StartRendering();
-  m_pStream->WriteString("q\n");
+  WriteString("q\n");
   m_ClipBoxStack.push_back(m_ClipBox);
 }
 
 void CFX_PSRenderer::RestoreState(bool bKeepSaved) {
   StartRendering();
-  m_pStream->WriteString("Q\n");
+  WriteString("Q\n");
   if (bKeepSaved)
-    m_pStream->WriteString("q\n");
+    WriteString("q\n");
 
   m_bColorSet = false;
   m_bGraphStateSet = false;
@@ -109,31 +311,31 @@
     m_ClipBoxStack.pop_back();
 }
 
-void CFX_PSRenderer::OutputPath(const CFX_PathData* pPathData,
+void CFX_PSRenderer::OutputPath(const CFX_Path& path,
                                 const CFX_Matrix* pObject2Device) {
-  std::ostringstream buf;
-  size_t size = pPathData->GetPoints().size();
+  fxcrt::ostringstream buf;
+  size_t size = path.GetPoints().size();
 
   for (size_t i = 0; i < size; i++) {
-    FXPT_TYPE type = pPathData->GetType(i);
-    bool closing = pPathData->IsClosingFigure(i);
-    CFX_PointF pos = pPathData->GetPoint(i);
+    CFX_Path::Point::Type type = path.GetType(i);
+    bool closing = path.IsClosingFigure(i);
+    CFX_PointF pos = path.GetPoint(i);
     if (pObject2Device)
       pos = pObject2Device->Transform(pos);
 
     buf << pos.x << " " << pos.y;
     switch (type) {
-      case FXPT_TYPE::MoveTo:
+      case CFX_Path::Point::Type::kMove:
         buf << " m ";
         break;
-      case FXPT_TYPE::LineTo:
+      case CFX_Path::Point::Type::kLine:
         buf << " l ";
         if (closing)
           buf << "h ";
         break;
-      case FXPT_TYPE::BezierTo: {
-        CFX_PointF pos1 = pPathData->GetPoint(i + 1);
-        CFX_PointF pos2 = pPathData->GetPoint(i + 2);
+      case CFX_Path::Point::Type::kBezier: {
+        CFX_PointF pos1 = path.GetPoint(i + 1);
+        CFX_PointF pos2 = path.GetPoint(i + 2);
         if (pObject2Device) {
           pos1 = pObject2Device->Transform(pos1);
           pos2 = pObject2Device->Transform(pos2);
@@ -148,15 +350,16 @@
       }
     }
   }
-  WriteToStream(&buf);
+  WriteStream(buf);
 }
 
-void CFX_PSRenderer::SetClip_PathFill(const CFX_PathData* pPathData,
-                                      const CFX_Matrix* pObject2Device,
-                                      int fill_mode) {
+void CFX_PSRenderer::SetClip_PathFill(
+    const CFX_Path& path,
+    const CFX_Matrix* pObject2Device,
+    const CFX_FillRenderOptions& fill_options) {
   StartRendering();
-  OutputPath(pPathData, pObject2Device);
-  CFX_FloatRect rect = pPathData->GetBoundingBox();
+  OutputPath(path, pObject2Device);
+  CFX_FloatRect rect = path.GetBoundingBox();
   if (pObject2Device)
     rect = pObject2Device->TransformRect(rect);
 
@@ -165,38 +368,38 @@
   m_ClipBox.top = static_cast<int>(rect.top + rect.bottom);
   m_ClipBox.bottom = static_cast<int>(rect.bottom);
 
-  m_pStream->WriteString("W");
-  if ((fill_mode & 3) != FXFILL_WINDING)
-    m_pStream->WriteString("*");
-  m_pStream->WriteString(" n\n");
+  WriteString("W");
+  if (fill_options.fill_type != CFX_FillRenderOptions::FillType::kWinding)
+    WriteString("*");
+  WriteString(" n\n");
 }
 
-void CFX_PSRenderer::SetClip_PathStroke(const CFX_PathData* pPathData,
+void CFX_PSRenderer::SetClip_PathStroke(const CFX_Path& path,
                                         const CFX_Matrix* pObject2Device,
                                         const CFX_GraphStateData* pGraphState) {
   StartRendering();
   SetGraphState(pGraphState);
 
-  std::ostringstream buf;
+  fxcrt::ostringstream buf;
   buf << "mx Cm [" << pObject2Device->a << " " << pObject2Device->b << " "
       << pObject2Device->c << " " << pObject2Device->d << " "
       << pObject2Device->e << " " << pObject2Device->f << "]cm ";
-  WriteToStream(&buf);
+  WriteStream(buf);
 
-  OutputPath(pPathData, nullptr);
-  CFX_FloatRect rect = pPathData->GetBoundingBox(pGraphState->m_LineWidth,
-                                                 pGraphState->m_MiterLimit);
+  OutputPath(path, nullptr);
+  CFX_FloatRect rect = path.GetBoundingBoxForStrokePath(
+      pGraphState->m_LineWidth, pGraphState->m_MiterLimit);
   m_ClipBox.Intersect(pObject2Device->TransformRect(rect).GetOuterRect());
 
-  m_pStream->WriteString("strokepath W n sm\n");
+  WriteString("strokepath W n sm\n");
 }
 
-bool CFX_PSRenderer::DrawPath(const CFX_PathData* pPathData,
+bool CFX_PSRenderer::DrawPath(const CFX_Path& path,
                               const CFX_Matrix* pObject2Device,
                               const CFX_GraphStateData* pGraphState,
                               uint32_t fill_color,
                               uint32_t stroke_color,
-                              int fill_mode) {
+                              const CFX_FillRenderOptions& fill_options) {
   StartRendering();
   int fill_alpha = FXARGB_A(fill_color);
   int stroke_alpha = FXARGB_A(stroke_color);
@@ -210,43 +413,45 @@
   if (stroke_alpha) {
     SetGraphState(pGraphState);
     if (pObject2Device) {
-      std::ostringstream buf;
+      fxcrt::ostringstream buf;
       buf << "mx Cm [" << pObject2Device->a << " " << pObject2Device->b << " "
           << pObject2Device->c << " " << pObject2Device->d << " "
           << pObject2Device->e << " " << pObject2Device->f << "]cm ";
-      WriteToStream(&buf);
+      WriteStream(buf);
     }
   }
 
-  OutputPath(pPathData, stroke_alpha ? nullptr : pObject2Device);
-  if (fill_mode && fill_alpha) {
+  OutputPath(path, stroke_alpha ? nullptr : pObject2Device);
+  if (fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill &&
+      fill_alpha) {
     SetColor(fill_color);
-    if ((fill_mode & 3) == FXFILL_WINDING) {
+    if (fill_options.fill_type == CFX_FillRenderOptions::FillType::kWinding) {
       if (stroke_alpha)
-        m_pStream->WriteString("q f Q ");
+        WriteString("q f Q ");
       else
-        m_pStream->WriteString("f");
-    } else if ((fill_mode & 3) == FXFILL_ALTERNATE) {
+        WriteString("f");
+    } else if (fill_options.fill_type ==
+               CFX_FillRenderOptions::FillType::kEvenOdd) {
       if (stroke_alpha)
-        m_pStream->WriteString("q F Q ");
+        WriteString("q F Q ");
       else
-        m_pStream->WriteString("F");
+        WriteString("F");
     }
   }
 
   if (stroke_alpha) {
     SetColor(stroke_color);
-    m_pStream->WriteString("s");
+    WriteString("s");
     if (pObject2Device)
-      m_pStream->WriteString(" sm");
+      WriteString(" sm");
   }
 
-  m_pStream->WriteString("\n");
+  WriteString("\n");
   return true;
 }
 
 void CFX_PSRenderer::SetGraphState(const CFX_GraphStateData* pGraphState) {
-  std::ostringstream buf;
+  fxcrt::ostringstream buf;
   if (!m_bGraphStateSet ||
       m_CurGraphState.m_LineCap != pGraphState->m_LineCap) {
     buf << static_cast<int>(pGraphState->m_LineCap) << " J\n";
@@ -272,7 +477,7 @@
   }
   m_CurGraphState = *pGraphState;
   m_bGraphStateSet = true;
-  WriteToStream(&buf);
+  WriteStream(buf);
 }
 
 bool CFX_PSRenderer::SetDIBits(const RetainPtr<CFX_DIBBase>& pSource,
@@ -306,38 +511,29 @@
   if ((matrix.a == 0 && matrix.b == 0) || (matrix.c == 0 && matrix.d == 0))
     return true;
 
-  if (pSource->HasAlpha())
+  if (pSource->IsAlphaFormat())
     return false;
 
   int alpha = FXARGB_A(color);
-  if (pSource->IsAlphaMask() && (alpha < 255 || pSource->GetBPP() != 1))
+  if (pSource->IsMaskFormat() && (alpha < 255 || pSource->GetBPP() != 1))
     return false;
 
-  m_pStream->WriteString("q\n");
+  WriteString("q\n");
 
-  std::ostringstream buf;
+  fxcrt::ostringstream buf;
   buf << "[" << matrix.a << " " << matrix.b << " " << matrix.c << " "
       << matrix.d << " " << matrix.e << " " << matrix.f << "]cm ";
 
-  int width = pSource->GetWidth();
-  int height = pSource->GetHeight();
+  const int width = pSource->GetWidth();
+  const int height = pSource->GetHeight();
   buf << width << " " << height;
 
-  if (pSource->GetBPP() == 1 && !pSource->GetPalette()) {
-    int pitch = (width + 7) / 8;
-    uint32_t src_size = height * pitch;
-    std::unique_ptr<uint8_t, FxFreeDeleter> src_buf(
-        FX_Alloc(uint8_t, src_size));
-    for (int row = 0; row < height; row++) {
-      const uint8_t* src_scan = pSource->GetScanline(row);
-      memcpy(src_buf.get() + row * pitch, src_scan, pitch);
-    }
+  if (pSource->GetBPP() == 1 && !pSource->HasPalette()) {
+    FaxCompressResult compress_result = FaxCompressData(pSource);
+    if (compress_result.data.empty())
+      return false;
 
-    std::unique_ptr<uint8_t, FxFreeDeleter> output_buf;
-    uint32_t output_size;
-    bool compressed = FaxCompressData(std::move(src_buf), width, height,
-                                      &output_buf, &output_size);
-    if (pSource->IsAlphaMask()) {
+    if (pSource->IsMaskFormat()) {
       SetColor(color);
       m_bColorSet = false;
       buf << " true[";
@@ -347,63 +543,52 @@
     buf << width << " 0 0 -" << height << " 0 " << height
         << "]currentfile/ASCII85Decode filter ";
 
-    if (compressed) {
+    if (compress_result.compressed) {
       buf << "<</K -1/EndOfBlock false/Columns " << width << "/Rows " << height
           << ">>/CCITTFaxDecode filter ";
     }
-    if (pSource->IsAlphaMask())
+    if (pSource->IsMaskFormat())
       buf << "iM\n";
     else
       buf << "false 1 colorimage\n";
 
-    WriteToStream(&buf);
-    WritePSBinary(output_buf.get(), output_size);
+    WriteStream(buf);
+    WritePSBinary(compress_result.data);
   } else {
-    CFX_DIBExtractor source_extractor(pSource);
-    RetainPtr<CFX_DIBBase> pConverted = source_extractor.GetBitmap();
-    if (!pConverted)
-      return false;
+    RetainPtr<CFX_DIBBase> pConverted = pSource;
     switch (pSource->GetFormat()) {
-      case FXDIB_1bppRgb:
-      case FXDIB_Rgb32:
-        pConverted = pConverted->CloneConvert(FXDIB_Rgb);
+      case FXDIB_Format::k1bppRgb:
+      case FXDIB_Format::kRgb32:
+        pConverted = pConverted->ConvertTo(FXDIB_Format::kRgb);
         break;
-      case FXDIB_8bppRgb:
-        if (pSource->GetPalette()) {
-          pConverted = pConverted->CloneConvert(FXDIB_Rgb);
-        }
-        break;
-      case FXDIB_1bppCmyk:
-        pConverted = pConverted->CloneConvert(FXDIB_Cmyk);
-        break;
-      case FXDIB_8bppCmyk:
-        if (pSource->GetPalette()) {
-          pConverted = pConverted->CloneConvert(FXDIB_Cmyk);
-        }
+      case FXDIB_Format::k8bppRgb:
+        if (pSource->HasPalette())
+          pConverted = pConverted->ConvertTo(FXDIB_Format::kRgb);
         break;
       default:
         break;
     }
     if (!pConverted) {
-      m_pStream->WriteString("\nQ\n");
+      WriteString("\nQ\n");
       return false;
     }
 
     int bpp = pConverted->GetBPP() / 8;
     uint8_t* output_buf = nullptr;
     size_t output_size = 0;
-    const char* filter = nullptr;
-    if ((m_PSLevel == 2 || options.bLossy) &&
+    bool output_buf_is_owned = true;
+    absl::optional<PSCompressResult> compress_result;
+    ByteString filter;
+    if ((m_Level.value() == RenderingLevel::kLevel2 || options.bLossy) &&
         m_pEncoderIface->pJpegEncodeFunc(pConverted, &output_buf,
                                          &output_size)) {
       filter = "/DCTDecode filter ";
-    }
-    if (!filter) {
+    } else {
       int src_pitch = width * bpp;
       output_size = height * src_pitch;
       output_buf = FX_Alloc(uint8_t, output_size);
       for (int row = 0; row < height; row++) {
-        const uint8_t* src_scan = pConverted->GetScanline(row);
+        const uint8_t* src_scan = pConverted->GetScanline(row).data();
         uint8_t* dest_scan = output_buf + row * src_pitch;
         if (bpp == 3) {
           for (int col = 0; col < width; col++) {
@@ -416,52 +601,43 @@
           memcpy(dest_scan, src_scan, src_pitch);
         }
       }
-      uint8_t* compressed_buf;
-      uint32_t compressed_size;
-      PSCompressData(output_buf, output_size, &compressed_buf, &compressed_size,
-                     &filter);
-      if (output_buf != compressed_buf)
+      compress_result = PSCompressData({output_buf, output_size});
+      if (compress_result.has_value()) {
         FX_Free(output_buf);
-
-      output_buf = compressed_buf;
-      output_size = compressed_size;
+        output_buf_is_owned = false;
+        output_buf = compress_result.value().data.data();
+        output_size = compress_result.value().data.size();
+        filter = compress_result.value().filter;
+      }
     }
     buf << " 8[";
     buf << width << " 0 0 -" << height << " 0 " << height << "]";
     buf << "currentfile/ASCII85Decode filter ";
-    if (filter)
+    if (!filter.IsEmpty())
       buf << filter;
 
     buf << "false " << bpp;
     buf << " colorimage\n";
-    WriteToStream(&buf);
+    WriteStream(buf);
 
-    WritePSBinary(output_buf, output_size);
-    FX_Free(output_buf);
+    WritePSBinary({output_buf, output_size});
+    if (output_buf_is_owned)
+      FX_Free(output_buf);
   }
-  m_pStream->WriteString("\nQ\n");
+  WriteString("\nQ\n");
   return true;
 }
 
 void CFX_PSRenderer::SetColor(uint32_t color) {
-  bool bCMYK = false;
-  if (bCMYK != m_bCmykOutput || !m_bColorSet || m_LastColor != color) {
-    std::ostringstream buf;
-    if (bCMYK) {
-      buf << FXSYS_GetCValue(color) / 255.0 << " "
-          << FXSYS_GetMValue(color) / 255.0 << " "
-          << FXSYS_GetYValue(color) / 255.0 << " "
-          << FXSYS_GetKValue(color) / 255.0 << " k\n";
-    } else {
-      buf << FXARGB_R(color) / 255.0 << " " << FXARGB_G(color) / 255.0 << " "
-          << FXARGB_B(color) / 255.0 << " rg\n";
-    }
-    if (bCMYK == m_bCmykOutput) {
-      m_bColorSet = true;
-      m_LastColor = color;
-    }
-    WriteToStream(&buf);
-  }
+  if (m_bColorSet && m_LastColor == color)
+    return;
+
+  fxcrt::ostringstream buf;
+  buf << FXARGB_R(color) / 255.0 << " " << FXARGB_G(color) / 255.0 << " "
+      << FXARGB_B(color) / 255.0 << " rg\n";
+  m_bColorSet = true;
+  m_LastColor = color;
+  WriteStream(buf);
 }
 
 void CFX_PSRenderer::FindPSFontGlyph(CFX_GlyphCache* pGlyphCache,
@@ -469,33 +645,35 @@
                                      const TextCharPos& charpos,
                                      int* ps_fontnum,
                                      int* ps_glyphindex) {
-  int i = 0;
-  for (const auto& pPSFont : m_PSFontList) {
-    for (int j = 0; j < pPSFont->m_nGlyphs; j++) {
-      if (pPSFont->m_Glyphs[j].m_pFont == pFont &&
-          pPSFont->m_Glyphs[j].m_GlyphIndex == charpos.m_GlyphIndex &&
-          ((!pPSFont->m_Glyphs[j].m_bGlyphAdjust && !charpos.m_bGlyphAdjust) ||
-           (pPSFont->m_Glyphs[j].m_bGlyphAdjust && charpos.m_bGlyphAdjust &&
-            (fabs(pPSFont->m_Glyphs[j].m_AdjustMatrix[0] -
-                  charpos.m_AdjustMatrix[0]) < 0.01 &&
-             fabs(pPSFont->m_Glyphs[j].m_AdjustMatrix[1] -
-                  charpos.m_AdjustMatrix[1]) < 0.01 &&
-             fabs(pPSFont->m_Glyphs[j].m_AdjustMatrix[2] -
-                  charpos.m_AdjustMatrix[2]) < 0.01 &&
-             fabs(pPSFont->m_Glyphs[j].m_AdjustMatrix[3] -
-                  charpos.m_AdjustMatrix[3]) < 0.01)))) {
-        *ps_fontnum = i;
-        *ps_glyphindex = j;
+  for (size_t i = 0; i < m_PSFontList.size(); ++i) {
+    const Glyph& glyph = *m_PSFontList[i];
+    if (glyph.font == pFont && glyph.glyph_index == charpos.m_GlyphIndex &&
+        glyph.adjust_matrix.has_value() == charpos.m_bGlyphAdjust) {
+      bool found;
+      if (glyph.adjust_matrix.has_value()) {
+        constexpr float kEpsilon = 0.01f;
+        const auto& adjust_matrix = glyph.adjust_matrix.value();
+        found = fabs(adjust_matrix[0] - charpos.m_AdjustMatrix[0]) < kEpsilon &&
+                fabs(adjust_matrix[1] - charpos.m_AdjustMatrix[1]) < kEpsilon &&
+                fabs(adjust_matrix[2] - charpos.m_AdjustMatrix[2]) < kEpsilon &&
+                fabs(adjust_matrix[3] - charpos.m_AdjustMatrix[3]) < kEpsilon;
+      } else {
+        found = true;
+      }
+      if (found) {
+        *ps_fontnum = pdfium::base::checked_cast<int>(i / 256);
+        *ps_glyphindex = i % 256;
         return;
       }
     }
-    ++i;
   }
 
-  if (m_PSFontList.empty() || m_PSFontList.back()->m_nGlyphs == 256) {
-    m_PSFontList.push_back(pdfium::MakeUnique<CPSFont>());
-    m_PSFontList.back()->m_nGlyphs = 0;
-    std::ostringstream buf;
+  m_PSFontList.push_back(std::make_unique<Glyph>(pFont, charpos.m_GlyphIndex));
+  *ps_fontnum =
+      pdfium::base::checked_cast<int>((m_PSFontList.size() - 1) / 256);
+  *ps_glyphindex = (m_PSFontList.size() - 1) % 256;
+  if (*ps_glyphindex == 0) {
+    fxcrt::ostringstream buf;
     buf << "8 dict begin/FontType 3 def/FontMatrix[1 0 0 1 0 0]def\n"
            "/FontBBox[0 0 0 0]def/Encoding 256 array def 0 1 255{Encoding "
            "exch/.notdef put}for\n"
@@ -505,26 +683,15 @@
            "/BuildChar{1 index/Encoding get exch get 1 index/BuildGlyph get "
            "exec}bind def\n"
            "currentdict end\n";
-    buf << "/X" << static_cast<uint32_t>(m_PSFontList.size() - 1)
-        << " exch definefont pop\n";
-    WriteToStream(&buf);
-    buf.str("");
+    buf << "/X" << *ps_fontnum << " exch definefont pop\n";
+    WriteStream(buf);
   }
 
-  *ps_fontnum = m_PSFontList.size() - 1;
-  CPSFont* pPSFont = m_PSFontList[*ps_fontnum].get();
-  int glyphindex = pPSFont->m_nGlyphs;
-  *ps_glyphindex = glyphindex;
-  pPSFont->m_Glyphs[glyphindex].m_GlyphIndex = charpos.m_GlyphIndex;
-  pPSFont->m_Glyphs[glyphindex].m_pFont = pFont;
-  pPSFont->m_Glyphs[glyphindex].m_bGlyphAdjust = charpos.m_bGlyphAdjust;
   if (charpos.m_bGlyphAdjust) {
-    pPSFont->m_Glyphs[glyphindex].m_AdjustMatrix[0] = charpos.m_AdjustMatrix[0];
-    pPSFont->m_Glyphs[glyphindex].m_AdjustMatrix[1] = charpos.m_AdjustMatrix[1];
-    pPSFont->m_Glyphs[glyphindex].m_AdjustMatrix[2] = charpos.m_AdjustMatrix[2];
-    pPSFont->m_Glyphs[glyphindex].m_AdjustMatrix[3] = charpos.m_AdjustMatrix[3];
+    m_PSFontList.back()->adjust_matrix = std::array<float, 4>{
+        charpos.m_AdjustMatrix[0], charpos.m_AdjustMatrix[1],
+        charpos.m_AdjustMatrix[2], charpos.m_AdjustMatrix[3]};
   }
-  pPSFont->m_nGlyphs++;
 
   CFX_Matrix matrix;
   if (charpos.m_bGlyphAdjust) {
@@ -532,30 +699,30 @@
         CFX_Matrix(charpos.m_AdjustMatrix[0], charpos.m_AdjustMatrix[1],
                    charpos.m_AdjustMatrix[2], charpos.m_AdjustMatrix[3], 0, 0);
   }
-  const CFX_PathData* pPathData = pGlyphCache->LoadGlyphPath(
+  const CFX_Path* pPath = pGlyphCache->LoadGlyphPath(
       pFont, charpos.m_GlyphIndex, charpos.m_FontCharWidth);
-  if (!pPathData)
+  if (!pPath)
     return;
 
-  CFX_PathData TransformedPath(*pPathData);
+  CFX_Path TransformedPath(*pPath);
   if (charpos.m_bGlyphAdjust)
     TransformedPath.Transform(matrix);
 
-  std::ostringstream buf;
-  buf << "/X" << *ps_fontnum << " Ff/CharProcs get begin/" << glyphindex
+  fxcrt::ostringstream buf;
+  buf << "/X" << *ps_fontnum << " Ff/CharProcs get begin/" << *ps_glyphindex
       << "{n ";
   for (size_t p = 0; p < TransformedPath.GetPoints().size(); p++) {
     CFX_PointF point = TransformedPath.GetPoint(p);
     switch (TransformedPath.GetType(p)) {
-      case FXPT_TYPE::MoveTo: {
+      case CFX_Path::Point::Type::kMove: {
         buf << point.x << " " << point.y << " m\n";
         break;
       }
-      case FXPT_TYPE::LineTo: {
+      case CFX_Path::Point::Type::kLine: {
         buf << point.x << " " << point.y << " l\n";
         break;
       }
-      case FXPT_TYPE::BezierTo: {
+      case CFX_Path::Point::Type::kBezier: {
         CFX_PointF point1 = TransformedPath.GetPoint(p + 1);
         CFX_PointF point2 = TransformedPath.GetPoint(p + 2);
         buf << point.x << " " << point.y << " " << point1.x << " " << point1.y
@@ -566,9 +733,64 @@
     }
   }
   buf << "f}bind def end\n";
-  buf << "/X" << *ps_fontnum << " Ff/Encoding get " << glyphindex << "/"
-      << glyphindex << " put\n";
-  WriteToStream(&buf);
+  buf << "/X" << *ps_fontnum << " Ff/Encoding get " << *ps_glyphindex << "/"
+      << *ps_glyphindex << " put\n";
+  WriteStream(buf);
+}
+
+void CFX_PSRenderer::DrawTextAsType3Font(int char_count,
+                                         const TextCharPos* char_pos,
+                                         CFX_Font* font,
+                                         float font_size,
+                                         fxcrt::ostringstream& buf) {
+  CFX_FontCache* pCache = CFX_GEModule::Get()->GetFontCache();
+  RetainPtr<CFX_GlyphCache> pGlyphCache = pCache->GetGlyphCache(font);
+  int last_fontnum = -1;
+  for (int i = 0; i < char_count; i++) {
+    int ps_fontnum;
+    int ps_glyphindex;
+    FindPSFontGlyph(pGlyphCache.Get(), font, char_pos[i], &ps_fontnum,
+                    &ps_glyphindex);
+    if (last_fontnum != ps_fontnum) {
+      buf << "/X" << ps_fontnum << " Ff " << font_size << " Fs Sf ";
+      last_fontnum = ps_fontnum;
+    }
+    buf << char_pos[i].m_Origin.x << " " << char_pos[i].m_Origin.y << " m";
+    ByteString hex = ByteString::Format("<%02X>", ps_glyphindex);
+    buf << hex.AsStringView() << "Tj\n";
+  }
+}
+
+bool CFX_PSRenderer::DrawTextAsType42Font(int char_count,
+                                          const TextCharPos* char_pos,
+                                          CFX_Font* font,
+                                          float font_size,
+                                          fxcrt::ostringstream& buf) {
+  if (m_Level != RenderingLevel::kLevel3Type42 || !CanEmbed(font))
+    return false;
+
+  if (font->GetFontType() != CFX_Font::FontType::kCIDTrueType)
+    return false;
+
+  bool is_existing_font = m_pFontTracker->SeenFontObject(font);
+  if (!is_existing_font) {
+    ByteString font_data = GenerateType42FontData(font);
+    if (font_data.IsEmpty())
+      return false;
+
+    m_pFontTracker->AddFontObject(font);
+    WritePreambleString(font_data.AsStringView());
+  }
+
+  buf << "/" << font->GetPsName() << " " << font_size << " selectfont\n";
+  for (int i = 0; i < char_count; ++i) {
+    buf << char_pos[i].m_Origin.x << " " << char_pos[i].m_Origin.y << " m";
+    uint8_t hi = char_pos[i].m_GlyphIndex / 256;
+    uint8_t lo = char_pos[i].m_GlyphIndex % 256;
+    ByteString hex = ByteString::Format("<%02X%02X>", hi, lo);
+    buf << hex.AsStringView() << "Tj\n";
+  }
+  return true;
 }
 
 bool CFX_PSRenderer::DrawText(int nChars,
@@ -587,7 +809,7 @@
   float scale =
       std::min(mtObject2Device.GetXUnit(), mtObject2Device.GetYUnit());
   static constexpr float kEpsilon = 0.01f;
-  if (std::fabs(font_size * scale) < kEpsilon)
+  if (fabsf(font_size * scale) < kEpsilon)
     return true;
 
   StartRendering();
@@ -596,97 +818,114 @@
     return false;
 
   SetColor(color);
-  std::ostringstream buf;
+  fxcrt::ostringstream buf;
   buf << "q[" << mtObject2Device.a << " " << mtObject2Device.b << " "
       << mtObject2Device.c << " " << mtObject2Device.d << " "
       << mtObject2Device.e << " " << mtObject2Device.f << "]cm\n";
 
-  CFX_FontCache* pCache = CFX_GEModule::Get()->GetFontCache();
-  RetainPtr<CFX_GlyphCache> pGlyphCache = pCache->GetGlyphCache(pFont);
-  int last_fontnum = -1;
-  for (int i = 0; i < nChars; i++) {
-    int ps_fontnum, ps_glyphindex;
-    FindPSFontGlyph(pGlyphCache.Get(), pFont, pCharPos[i], &ps_fontnum,
-                    &ps_glyphindex);
-    if (last_fontnum != ps_fontnum) {
-      buf << "/X" << ps_fontnum << " Ff " << font_size << " Fs Sf ";
-      last_fontnum = ps_fontnum;
-    }
-    buf << pCharPos[i].m_Origin.x << " " << pCharPos[i].m_Origin.y << " m";
-    ByteString hex = ByteString::Format("<%02X>", ps_glyphindex);
-    buf << hex.AsStringView() << "Tj\n";
+  if (!DrawTextAsType42Font(nChars, pCharPos, pFont, font_size, buf)) {
+    DrawTextAsType3Font(nChars, pCharPos, pFont, font_size, buf);
   }
+
   buf << "Q\n";
-  WriteToStream(&buf);
+  WriteStream(buf);
   return true;
 }
 
-bool CFX_PSRenderer::FaxCompressData(
-    std::unique_ptr<uint8_t, FxFreeDeleter> src_buf,
-    int width,
-    int height,
-    std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-    uint32_t* dest_size) const {
-  if (width * height <= 128) {
-    *dest_buf = std::move(src_buf);
-    *dest_size = (width + 7) / 8 * height;
-    return false;
+CFX_PSRenderer::FaxCompressResult CFX_PSRenderer::FaxCompressData(
+    RetainPtr<CFX_DIBBase> src) const {
+  DCHECK_EQ(1, src->GetBPP());
+
+  FaxCompressResult result;
+  const int width = src->GetWidth();
+  const int height = src->GetHeight();
+  const int pitch = src->GetPitch();
+  DCHECK_GE(width, pitch);
+
+  FX_SAFE_UINT32 safe_pixel_count = width;
+  safe_pixel_count *= height;
+  if (!safe_pixel_count.IsValid())
+    return result;
+
+  if (safe_pixel_count.ValueOrDie() > 128) {
+    result.data = m_pEncoderIface->pFaxEncodeFunc(std::move(src));
+    result.compressed = true;
+    return result;
   }
 
-  m_pEncoderIface->pFaxEncodeFunc(src_buf.get(), width, height, (width + 7) / 8,
-                                  dest_buf, dest_size);
-  return true;
+  FX_SAFE_UINT32 safe_size = pitch;
+  safe_size *= height;
+  result.data.resize(safe_size.ValueOrDie());
+  auto dest_span = pdfium::make_span(result.data);
+  for (int row = 0; row < height; row++) {
+    pdfium::span<const uint8_t> src_scan = src->GetScanline(row);
+    fxcrt::spancpy(dest_span.subspan(row * pitch, pitch), src_scan);
+  }
+  return result;
 }
 
-void CFX_PSRenderer::PSCompressData(uint8_t* src_buf,
-                                    uint32_t src_size,
-                                    uint8_t** output_buf,
-                                    uint32_t* output_size,
-                                    const char** filter) const {
-  *output_buf = src_buf;
-  *output_size = src_size;
-  *filter = "";
-  if (src_size < 1024)
-    return;
+absl::optional<CFX_PSRenderer::PSCompressResult> CFX_PSRenderer::PSCompressData(
+    pdfium::span<const uint8_t> src_span) const {
+  if (src_span.size() < 1024)
+    return absl::nullopt;
 
-  uint8_t* dest_buf = nullptr;
-  uint32_t dest_size = src_size;
-  if (m_PSLevel >= 3) {
-    std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf_unique;
-    if (m_pEncoderIface->pFlateEncodeFunc(src_buf, src_size, &dest_buf_unique,
-                                          &dest_size)) {
-      dest_buf = dest_buf_unique.release();
-      *filter = "/FlateDecode filter ";
-    }
+  DataVector<uint8_t> (*encode_func)(pdfium::span<const uint8_t> src_span);
+  ByteString filter;
+  if (m_Level.value() == RenderingLevel::kLevel3 ||
+      m_Level.value() == RenderingLevel::kLevel3Type42) {
+    encode_func = m_pEncoderIface->pFlateEncodeFunc;
+    filter = "/FlateDecode filter ";
   } else {
-    std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf_unique;
-    if (m_pEncoderIface->pRunLengthEncodeFunc({src_buf, src_size},
-                                              &dest_buf_unique, &dest_size)) {
-      dest_buf = dest_buf_unique.release();
-      *filter = "/RunLengthDecode filter ";
-    }
+    encode_func = m_pEncoderIface->pRunLengthEncodeFunc;
+    filter = "/RunLengthDecode filter ";
   }
-  if (dest_size < src_size) {
-    *output_buf = dest_buf;
-    *output_size = dest_size;
-  } else {
-    *filter = nullptr;
-    FX_Free(dest_buf);
+
+  DataVector<uint8_t> decode_result = encode_func(src_span);
+  if (decode_result.size() == 0 || decode_result.size() >= src_span.size())
+    return absl::nullopt;
+
+  PSCompressResult result;
+  result.data = std::move(decode_result);
+  result.filter = filter;
+  return result;
+}
+
+void CFX_PSRenderer::WritePreambleString(ByteStringView str) {
+  m_PreambleOutput << str;
+}
+
+void CFX_PSRenderer::WritePSBinary(pdfium::span<const uint8_t> data) {
+  DataVector<uint8_t> encoded_data = m_pEncoderIface->pA85EncodeFunc(data);
+  pdfium::span<const uint8_t> result =
+      encoded_data.empty() ? data : encoded_data;
+  m_Output.write(reinterpret_cast<const char*>(result.data()), result.size());
+}
+
+void CFX_PSRenderer::WriteStream(fxcrt::ostringstream& stream) {
+  std::streamoff output_pos = stream.tellp();
+  if (output_pos > 0) {
+    m_Output.write(stream.str().c_str(),
+                   pdfium::base::checked_cast<size_t>(output_pos));
   }
 }
 
-void CFX_PSRenderer::WritePSBinary(const uint8_t* data, int len) {
-  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
-  uint32_t dest_size;
-  if (m_pEncoderIface->pA85EncodeFunc({data, static_cast<size_t>(len)},
-                                      &dest_buf, &dest_size)) {
-    m_pStream->WriteBlock(dest_buf.get(), dest_size);
-  } else {
-    m_pStream->WriteBlock(data, len);
-  }
+void CFX_PSRenderer::WriteString(ByteStringView str) {
+  m_Output << str;
 }
 
-void CFX_PSRenderer::WriteToStream(std::ostringstream* stringStream) {
-  if (stringStream->tellp() > 0)
-    m_pStream->WriteBlock(stringStream->str().c_str(), stringStream->tellp());
+// static
+absl::optional<ByteString> CFX_PSRenderer::GenerateType42SfntDataForTesting(
+    const ByteString& psname,
+    pdfium::span<const uint8_t> font_data) {
+  return GenerateType42SfntData(psname, font_data);
+}
+
+// static
+ByteString CFX_PSRenderer::GenerateType42FontDictionaryForTesting(
+    const ByteString& psname,
+    const FX_RECT& bbox,
+    size_t num_glyphs,
+    size_t glyphs_per_descendant_font) {
+  return GenerateType42FontDictionary(psname, bbox, num_glyphs,
+                                      glyphs_per_descendant_font);
 }
diff --git a/core/fxge/win32/cfx_psrenderer.h b/core/fxge/win32/cfx_psrenderer.h
index 50e98a4..ad914be 100644
--- a/core/fxge/win32/cfx_psrenderer.h
+++ b/core/fxge/win32/cfx_psrenderer.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,74 +7,76 @@
 #ifndef CORE_FXGE_WIN32_CFX_PSRENDERER_H_
 #define CORE_FXGE_WIN32_CFX_PSRENDERER_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <memory>
+#include <sstream>
 #include <vector>
 
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_stream.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/fx_string_wrappers.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_graphstatedata.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_DIBBase;
-class CFX_GlyphCache;
 class CFX_Font;
-class CFX_PathData;
-class CPSFont;
+class CFX_GlyphCache;
+class CFX_PSFontTracker;
+class CFX_Path;
 class TextCharPos;
+struct CFX_FillRenderOptions;
 struct FXDIB_ResampleOptions;
 
 struct EncoderIface {
-  bool (*pA85EncodeFunc)(pdfium::span<const uint8_t> src_buf,
-                         std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-                         uint32_t* dest_size);
-  void (*pFaxEncodeFunc)(const uint8_t* src_buf,
-                         int width,
-                         int height,
-                         int pitch,
-                         std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-                         uint32_t* dest_size);
-  bool (*pFlateEncodeFunc)(const uint8_t* src_buf,
-                           uint32_t src_size,
-                           std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-                           uint32_t* dest_size);
+  DataVector<uint8_t> (*pA85EncodeFunc)(pdfium::span<const uint8_t> src_span);
+  DataVector<uint8_t> (*pFaxEncodeFunc)(RetainPtr<CFX_DIBBase> src);
+  DataVector<uint8_t> (*pFlateEncodeFunc)(pdfium::span<const uint8_t> src_span);
   bool (*pJpegEncodeFunc)(const RetainPtr<CFX_DIBBase>& pSource,
                           uint8_t** dest_buf,
                           size_t* dest_size);
-  bool (*pRunLengthEncodeFunc)(
-      pdfium::span<const uint8_t> src_buf,
-      std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-      uint32_t* dest_size);
+  DataVector<uint8_t> (*pRunLengthEncodeFunc)(
+      pdfium::span<const uint8_t> src_span);
 };
 
 class CFX_PSRenderer {
  public:
-  explicit CFX_PSRenderer(const EncoderIface* pEncoderIface);
+  enum class RenderingLevel {
+    kLevel2,
+    kLevel3,
+    kLevel3Type42,
+  };
+
+  CFX_PSRenderer(CFX_PSFontTracker* font_tracker,
+                 const EncoderIface* encoder_iface);
   ~CFX_PSRenderer();
 
   void Init(const RetainPtr<IFX_RetainableWriteStream>& stream,
-            int pslevel,
+            RenderingLevel level,
             int width,
-            int height,
-            bool bCmykOutput);
-  bool StartRendering();
-  void EndRendering();
+            int height);
   void SaveState();
   void RestoreState(bool bKeepSaved);
-  void SetClip_PathFill(const CFX_PathData* pPathData,
+  void SetClip_PathFill(const CFX_Path& path,
                         const CFX_Matrix* pObject2Device,
-                        int fill_mode);
-  void SetClip_PathStroke(const CFX_PathData* pPathData,
+                        const CFX_FillRenderOptions& fill_options);
+  void SetClip_PathStroke(const CFX_Path& path,
                           const CFX_Matrix* pObject2Device,
                           const CFX_GraphStateData* pGraphState);
   FX_RECT GetClipBox() { return m_ClipBox; }
-  bool DrawPath(const CFX_PathData* pPathData,
+  bool DrawPath(const CFX_Path& path,
                 const CFX_Matrix* pObject2Device,
                 const CFX_GraphStateData* pGraphState,
                 uint32_t fill_color,
                 uint32_t stroke_color,
-                int fill_mode);
+                const CFX_FillRenderOptions& fill_options);
   bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                  uint32_t color,
                  int dest_left,
@@ -97,9 +99,46 @@
                 float font_size,
                 uint32_t color);
 
+  static absl::optional<ByteString> GenerateType42SfntDataForTesting(
+      const ByteString& psname,
+      pdfium::span<const uint8_t> font_data);
+
+  static ByteString GenerateType42FontDictionaryForTesting(
+      const ByteString& psname,
+      const FX_RECT& bbox,
+      size_t num_glyphs,
+      size_t glyphs_per_descendant_font);
+
  private:
-  void OutputPath(const CFX_PathData* pPathData,
-                  const CFX_Matrix* pObject2Device);
+  struct Glyph;
+
+  struct FaxCompressResult {
+    FaxCompressResult();
+    FaxCompressResult(const FaxCompressResult&) = delete;
+    FaxCompressResult& operator=(const FaxCompressResult&) = delete;
+    FaxCompressResult(FaxCompressResult&&) noexcept;
+    FaxCompressResult& operator=(FaxCompressResult&&) noexcept;
+    ~FaxCompressResult();
+
+    DataVector<uint8_t> data;
+    bool compressed = false;
+  };
+
+  struct PSCompressResult {
+    PSCompressResult();
+    PSCompressResult(const PSCompressResult&) = delete;
+    PSCompressResult& operator=(const PSCompressResult&) = delete;
+    PSCompressResult(PSCompressResult&&) noexcept;
+    PSCompressResult& operator=(PSCompressResult&&) noexcept;
+    ~PSCompressResult();
+
+    DataVector<uint8_t> data;
+    ByteString filter;
+  };
+
+  void StartRendering();
+  void EndRendering();
+  void OutputPath(const CFX_Path& path, const CFX_Matrix* pObject2Device);
   void SetGraphState(const CFX_GraphStateData* pGraphState);
   void SetColor(uint32_t color);
   void FindPSFontGlyph(CFX_GlyphCache* pGlyphCache,
@@ -107,30 +146,37 @@
                        const TextCharPos& charpos,
                        int* ps_fontnum,
                        int* ps_glyphindex);
-  bool FaxCompressData(std::unique_ptr<uint8_t, FxFreeDeleter> src_buf,
-                       int width,
-                       int height,
-                       std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-                       uint32_t* dest_size) const;
-  void PSCompressData(uint8_t* src_buf,
-                      uint32_t src_size,
-                      uint8_t** output_buf,
-                      uint32_t* output_size,
-                      const char** filter) const;
-  void WritePSBinary(const uint8_t* data, int len);
-  void WriteToStream(std::ostringstream* stringStream);
+  void DrawTextAsType3Font(int char_count,
+                           const TextCharPos* char_pos,
+                           CFX_Font* font,
+                           float font_size,
+                           fxcrt::ostringstream& buf);
+  bool DrawTextAsType42Font(int char_count,
+                            const TextCharPos* char_pos,
+                            CFX_Font* font,
+                            float font_size,
+                            fxcrt::ostringstream& buf);
+  FaxCompressResult FaxCompressData(RetainPtr<CFX_DIBBase> src) const;
+  absl::optional<PSCompressResult> PSCompressData(
+      pdfium::span<const uint8_t> src_span) const;
+  void WritePreambleString(ByteStringView str);
+  void WritePSBinary(pdfium::span<const uint8_t> data);
+  void WriteStream(fxcrt::ostringstream& stream);
+  void WriteString(ByteStringView str);
 
   bool m_bInited = false;
   bool m_bGraphStateSet = false;
-  bool m_bCmykOutput;
   bool m_bColorSet = false;
-  int m_PSLevel = 0;
+  absl::optional<RenderingLevel> m_Level;
   uint32_t m_LastColor = 0;
   FX_RECT m_ClipBox;
   CFX_GraphStateData m_CurGraphState;
-  const EncoderIface* const m_pEncoderIface;
+  UnownedPtr<CFX_PSFontTracker> const m_pFontTracker;
+  UnownedPtr<const EncoderIface> const m_pEncoderIface;
   RetainPtr<IFX_RetainableWriteStream> m_pStream;
-  std::vector<std::unique_ptr<CPSFont>> m_PSFontList;
+  std::vector<std::unique_ptr<Glyph>> m_PSFontList;
+  fxcrt::ostringstream m_PreambleOutput;
+  fxcrt::ostringstream m_Output;
   std::vector<FX_RECT> m_ClipBoxStack;
 };
 
diff --git a/core/fxge/win32/cfx_psrenderer_unittest.cpp b/core/fxge/win32/cfx_psrenderer_unittest.cpp
new file mode 100644
index 0000000..9927692
--- /dev/null
+++ b/core/fxge/win32/cfx_psrenderer_unittest.cpp
@@ -0,0 +1,213 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/win32/cfx_psrenderer.h"
+
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "core/fxge/win32/cfx_psfonttracker.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/containers/span.h"
+
+namespace {
+
+DataVector<uint8_t> FakeA85Encode(pdfium::span<const uint8_t> src_span) {
+  return DataVector<uint8_t>({'d', 'u', 'm', 'm', 'y', 'a', '8', '5'});
+}
+
+class TestWriteStream final : public IFX_RetainableWriteStream {
+ public:
+  CONSTRUCT_VIA_MAKE_RETAIN;
+
+  // IFX_RetainableWriteStream:
+  bool WriteBlock(pdfium::span<const uint8_t> buffer) override {
+    data_.insert(data_.end(), buffer.begin(), buffer.end());
+    return true;
+  }
+
+  pdfium::span<const uint8_t> GetSpan() const { return data_; }
+
+ private:
+  DataVector<uint8_t> data_;
+};
+
+}  // namespace
+
+TEST(PSRendererTest, GenerateType42SfntData) {
+  absl::optional<ByteString> result;
+
+  result = CFX_PSRenderer::GenerateType42SfntDataForTesting("empty", {});
+  EXPECT_FALSE(result.has_value());
+
+  constexpr uint8_t kOddByteCountTestData[] = {0, 32, 55};
+  static constexpr char kExpectedOddByteCountResult[] = R"(/odd_sfnts [
+<
+002037
+>
+] def
+)";
+  result = CFX_PSRenderer::GenerateType42SfntDataForTesting(
+      "odd", kOddByteCountTestData);
+  ASSERT_TRUE(result.has_value());
+  EXPECT_STREQ(kExpectedOddByteCountResult, result.value().c_str());
+
+  // Requires padding.
+  constexpr uint8_t kEvenByteCountTestData[] = {0, 32, 66, 77};
+  static constexpr char kExpectedEvenByteCountResult[] = R"(/even_sfnts [
+<
+0020424D00
+>
+] def
+)";
+  result = CFX_PSRenderer::GenerateType42SfntDataForTesting(
+      "even", kEvenByteCountTestData);
+  ASSERT_TRUE(result.has_value());
+  EXPECT_STREQ(kExpectedEvenByteCountResult, result.value().c_str());
+}
+
+TEST(PSRendererTest, GenerateType42FontDictionary) {
+  ByteString result;
+
+  static constexpr char kExpected1DescendantFontResult[] = R"(8 dict begin
+/FontType 42 def
+/FontMatrix [1 0 0 1 0 0] def
+/FontName /1descendant_0 def
+/Encoding 3 array
+dup 0 /c00 put
+dup 1 /c01 put
+dup 2 /c02 put
+readonly def
+/FontBBox [1 2 3 4] def
+/PaintType 0 def
+/CharStrings 4 dict dup begin
+/.notdef 0 def
+/c00 0 def
+/c01 1 def
+/c02 2 def
+end readonly def
+/sfnts 1descendant_sfnts def
+FontName currentdict end definefont pop
+6 dict begin
+/FontName /1descendant def
+/FontType 0 def
+/FontMatrix [1 0 0 1 0 0] def
+/FMapType 2 def
+/Encoding [
+0
+] def
+/FDepVector [
+/1descendant_0 findfont
+] def
+FontName currentdict end definefont pop
+%%EndResource
+)";
+  result = CFX_PSRenderer::GenerateType42FontDictionaryForTesting(
+      "1descendant", FX_RECT(1, 2, 3, 4), /*num_glyphs=*/3,
+      /*glyphs_per_descendant_font=*/3);
+  EXPECT_STREQ(kExpected1DescendantFontResult, result.c_str());
+
+  static constexpr char kExpected2DescendantFontResult[] = R"(8 dict begin
+/FontType 42 def
+/FontMatrix [1 0 0 1 0 0] def
+/FontName /2descendant_0 def
+/Encoding 3 array
+dup 0 /c00 put
+dup 1 /c01 put
+dup 2 /c02 put
+readonly def
+/FontBBox [12 -5 34 199] def
+/PaintType 0 def
+/CharStrings 4 dict dup begin
+/.notdef 0 def
+/c00 0 def
+/c01 1 def
+/c02 2 def
+end readonly def
+/sfnts 2descendant_sfnts def
+FontName currentdict end definefont pop
+8 dict begin
+/FontType 42 def
+/FontMatrix [1 0 0 1 0 0] def
+/FontName /2descendant_1 def
+/Encoding 3 array
+dup 0 /c00 put
+dup 1 /c01 put
+readonly def
+/FontBBox [12 -5 34 199] def
+/PaintType 0 def
+/CharStrings 4 dict dup begin
+/.notdef 0 def
+/c00 3 def
+/c01 4 def
+end readonly def
+/sfnts 2descendant_sfnts def
+FontName currentdict end definefont pop
+6 dict begin
+/FontName /2descendant def
+/FontType 0 def
+/FontMatrix [1 0 0 1 0 0] def
+/FMapType 2 def
+/Encoding [
+0
+1
+] def
+/FDepVector [
+/2descendant_0 findfont
+/2descendant_1 findfont
+] def
+FontName currentdict end definefont pop
+%%EndResource
+)";
+  result = CFX_PSRenderer::GenerateType42FontDictionaryForTesting(
+      "2descendant", FX_RECT(12, -5, 34, 199), /*num_glyphs=*/5,
+      /*glyphs_per_descendant_font=*/3);
+  EXPECT_STREQ(kExpected2DescendantFontResult, result.c_str());
+}
+
+TEST(PSRendererTest, DrawDIBits) {
+  static constexpr char kExpectedOutput[] = R"(
+save
+/im/initmatrix load def
+/n/newpath load def/m/moveto load def/l/lineto load def/c/curveto load def/h/closepath load def
+/f/fill load def/F/eofill load def/s/stroke load def/W/clip load def/W*/eoclip load def
+/rg/setrgbcolor load def/k/setcmykcolor load def
+/J/setlinecap load def/j/setlinejoin load def/w/setlinewidth load def/M/setmiterlimit load def/d/setdash load def
+/q/gsave load def/Q/grestore load def/iM/imagemask load def
+/Tj/show load def/Ff/findfont load def/Fs/scalefont load def/Sf/setfont load def
+/cm/concat load def/Cm/currentmatrix load def/mx/matrix load def/sm/setmatrix load def
+q
+[1 0 0 1 0 0]cm 10 2 1[10 0 0 -2 0 2]currentfile/ASCII85Decode filter false 1 colorimage
+dummya85
+Q
+
+restore
+)";
+  auto output_stream = pdfium::MakeRetain<TestWriteStream>();
+
+  {
+    constexpr int kWidth = 10;
+    constexpr int kHeight = 2;
+    CFX_PSFontTracker font_tracker;
+    const EncoderIface encoder_interface{&FakeA85Encode, nullptr, nullptr,
+                                         nullptr, nullptr};
+    CFX_PSRenderer renderer(&font_tracker, &encoder_interface);
+    renderer.Init(output_stream, CFX_PSRenderer::RenderingLevel::kLevel2,
+                  kWidth, kHeight);
+
+    auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+    bool result = bitmap->Create(kWidth, kHeight, FXDIB_Format::k1bppRgb);
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(renderer.DrawDIBits(bitmap, /*color=*/0, CFX_Matrix(),
+                                    FXDIB_ResampleOptions()));
+  }
+
+  ByteString output(output_stream->GetSpan());
+  EXPECT_STREQ(output.c_str(), kExpectedOutput);
+}
diff --git a/core/fxge/win32/cfx_windowsdib.h b/core/fxge/win32/cfx_windowsdib.h
deleted file mode 100644
index ef6127b..0000000
--- a/core/fxge/win32/cfx_windowsdib.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXGE_WIN32_CFX_WINDOWSDIB_H_
-#define CORE_FXGE_WIN32_CFX_WINDOWSDIB_H_
-
-#include <windows.h>
-
-#include "core/fxcrt/bytestring.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-
-#define WINDIB_OPEN_MEMORY 0x1
-#define WINDIB_OPEN_PATHNAME 0x2
-
-struct WINDIB_Open_Args_ {
-  int flags;
-  const uint8_t* memory_base;
-  size_t memory_size;
-  const wchar_t* path_name;
-};
-
-class CFX_WindowsDIB final : public CFX_DIBitmap {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  static ByteString GetBitmapInfo(const RetainPtr<CFX_DIBitmap>& pBitmap);
-  static HBITMAP GetDDBitmap(const RetainPtr<CFX_DIBitmap>& pBitmap, HDC hDC);
-
-  static RetainPtr<CFX_DIBitmap> LoadFromBuf(BITMAPINFO* pbmi, void* pData);
-  static RetainPtr<CFX_DIBitmap> LoadFromFile(const wchar_t* filename);
-  static RetainPtr<CFX_DIBitmap> LoadFromFile(const char* filename);
-  static RetainPtr<CFX_DIBitmap> LoadDIBitmap(WINDIB_Open_Args_ args);
-
-  HBITMAP GetWindowsBitmap() const { return m_hBitmap; }
-
-  void LoadFromDevice(HDC hDC, int left, int top);
-  void SetToDevice(HDC hDC, int left, int top);
-
- private:
-  CFX_WindowsDIB(HDC hDC, int width, int height);
-  ~CFX_WindowsDIB() override;
-
-  HDC m_hMemDC;
-  HBITMAP m_hBitmap;
-  HBITMAP m_hOldBitmap;
-};
-
-#endif  // CORE_FXGE_WIN32_CFX_WINDOWSDIB_H_
diff --git a/core/fxge/win32/cgdi_device_driver.cpp b/core/fxge/win32/cgdi_device_driver.cpp
new file mode 100644
index 0000000..6ae582e
--- /dev/null
+++ b/core/fxge/win32/cgdi_device_driver.cpp
@@ -0,0 +1,754 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxge/win32/cgdi_device_driver.h"
+
+#include <math.h>
+#include <windows.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "core/fxcrt/fx_string.h"
+#include "core/fxge/agg/fx_agg_driver.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "core/fxge/cfx_graphstatedata.h"
+#include "core/fxge/cfx_path.h"
+#include "core/fxge/dib/cfx_dibbase.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/render_defines.h"
+#include "core/fxge/win32/cwin32_platform.h"
+#include "third_party/agg23/agg_clip_liang_barsky.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/notreached.h"
+#include "third_party/base/numerics/safe_conversions.h"
+
+namespace {
+
+constexpr int FillTypeToGdiFillType(CFX_FillRenderOptions::FillType fill_type) {
+  return static_cast<int>(fill_type);
+}
+
+static_assert(FillTypeToGdiFillType(
+                  CFX_FillRenderOptions::FillType::kEvenOdd) == ALTERNATE,
+              "CFX_FillRenderOptions::FillType::kEvenOdd value mismatch");
+
+static_assert(
+    FillTypeToGdiFillType(CFX_FillRenderOptions::FillType::kWinding) == WINDING,
+    "CFX_FillRenderOptions::FillType::kWinding value mismatch");
+
+HPEN CreateExtPen(const CFX_GraphStateData* pGraphState,
+                  const CFX_Matrix* pMatrix,
+                  uint32_t argb) {
+  DCHECK(pGraphState);
+
+  float scale = 1.0f;
+  if (pMatrix) {
+    scale = fabs(pMatrix->a) > fabs(pMatrix->b) ? fabs(pMatrix->a)
+                                                : fabs(pMatrix->b);
+  }
+  float width = std::max(scale * pGraphState->m_LineWidth, 1.0f);
+
+  uint32_t PenStyle = PS_GEOMETRIC;
+  if (!pGraphState->m_DashArray.empty())
+    PenStyle |= PS_USERSTYLE;
+  else
+    PenStyle |= PS_SOLID;
+
+  switch (pGraphState->m_LineCap) {
+    case CFX_GraphStateData::LineCap::kButt:
+      PenStyle |= PS_ENDCAP_FLAT;
+      break;
+    case CFX_GraphStateData::LineCap::kRound:
+      PenStyle |= PS_ENDCAP_ROUND;
+      break;
+    case CFX_GraphStateData::LineCap::kSquare:
+      PenStyle |= PS_ENDCAP_SQUARE;
+      break;
+  }
+  switch (pGraphState->m_LineJoin) {
+    case CFX_GraphStateData::LineJoin::kMiter:
+      PenStyle |= PS_JOIN_MITER;
+      break;
+    case CFX_GraphStateData::LineJoin::kRound:
+      PenStyle |= PS_JOIN_ROUND;
+      break;
+    case CFX_GraphStateData::LineJoin::kBevel:
+      PenStyle |= PS_JOIN_BEVEL;
+      break;
+  }
+
+  FX_COLORREF colorref = ArgbToColorRef(argb);
+  LOGBRUSH lb;
+  lb.lbColor = colorref;
+  lb.lbStyle = BS_SOLID;
+  lb.lbHatch = 0;
+  std::vector<uint32_t> dashes;
+  if (!pGraphState->m_DashArray.empty()) {
+    dashes.resize(pGraphState->m_DashArray.size());
+    for (size_t i = 0; i < pGraphState->m_DashArray.size(); i++) {
+      dashes[i] = FXSYS_roundf(
+          pMatrix ? pMatrix->TransformDistance(pGraphState->m_DashArray[i])
+                  : pGraphState->m_DashArray[i]);
+      dashes[i] = std::max(dashes[i], 1U);
+    }
+  }
+  return ExtCreatePen(
+      PenStyle, (DWORD)ceil(width), &lb,
+      pdfium::base::checked_cast<DWORD>(pGraphState->m_DashArray.size()),
+      reinterpret_cast<const DWORD*>(dashes.data()));
+}
+
+HBRUSH CreateBrush(uint32_t argb) {
+  return CreateSolidBrush(ArgbToColorRef(argb));
+}
+
+void SetPathToDC(HDC hDC, const CFX_Path& path, const CFX_Matrix* pMatrix) {
+  BeginPath(hDC);
+
+  pdfium::span<const CFX_Path::Point> points = path.GetPoints();
+  for (size_t i = 0; i < points.size(); ++i) {
+    CFX_PointF pos = points[i].m_Point;
+    if (pMatrix)
+      pos = pMatrix->Transform(pos);
+
+    CFX_Point screen(FXSYS_roundf(pos.x), FXSYS_roundf(pos.y));
+    CFX_Path::Point::Type point_type = points[i].m_Type;
+    if (point_type == CFX_Path::Point::Type::kMove) {
+      MoveToEx(hDC, screen.x, screen.y, nullptr);
+    } else if (point_type == CFX_Path::Point::Type::kLine) {
+      if (points[i].m_Point == points[i - 1].m_Point)
+        screen.x++;
+
+      LineTo(hDC, screen.x, screen.y);
+    } else if (point_type == CFX_Path::Point::Type::kBezier) {
+      POINT lppt[3];
+      lppt[0].x = screen.x;
+      lppt[0].y = screen.y;
+
+      pos = points[i + 1].m_Point;
+      if (pMatrix)
+        pos = pMatrix->Transform(pos);
+
+      lppt[1].x = FXSYS_roundf(pos.x);
+      lppt[1].y = FXSYS_roundf(pos.y);
+
+      pos = points[i + 2].m_Point;
+      if (pMatrix)
+        pos = pMatrix->Transform(pos);
+
+      lppt[2].x = FXSYS_roundf(pos.x);
+      lppt[2].y = FXSYS_roundf(pos.y);
+      PolyBezierTo(hDC, lppt, 3);
+      i += 2;
+    }
+    if (points[i].m_CloseFigure)
+      CloseFigure(hDC);
+  }
+  EndPath(hDC);
+}
+
+ByteString GetBitmapInfo(const RetainPtr<CFX_DIBBase>& source) {
+  int len = sizeof(BITMAPINFOHEADER);
+  if (source->GetBPP() == 1 || source->GetBPP() == 8) {
+    len += sizeof(DWORD) * (int)(1 << source->GetBPP());
+  }
+
+  ByteString result;
+  {
+    // Span's lifetime must end before ReleaseBuffer() below.
+    pdfium::span<char> cspan = result.GetBuffer(len);
+    BITMAPINFOHEADER* pbmih = reinterpret_cast<BITMAPINFOHEADER*>(cspan.data());
+    memset(pbmih, 0, sizeof(BITMAPINFOHEADER));
+    pbmih->biSize = sizeof(BITMAPINFOHEADER);
+    pbmih->biBitCount = source->GetBPP();
+    pbmih->biCompression = BI_RGB;
+    pbmih->biHeight = -(int)source->GetHeight();
+    pbmih->biPlanes = 1;
+    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++) {
+          palette[i] = ArgbEncode(0, i, i, i);
+        }
+      }
+    }
+    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 {
+        palette[0] = 0;
+        palette[1] = 0xffffff;
+      }
+    }
+  }
+  result.ReleaseBuffer(len);
+  return result;
+}
+
+#if defined(_SKIA_SUPPORT_)
+// TODO(caryclark)  This antigrain function is duplicated here to permit
+// removing the last remaining dependency. Eventually, this will be elminiated
+// altogether and replace by Skia code.
+
+struct rect_base {
+  float x1;
+  float y1;
+  float x2;
+  float y2;
+};
+
+unsigned clip_liang_barsky(float x1,
+                           float y1,
+                           float x2,
+                           float y2,
+                           const rect_base& clip_box,
+                           float* x,
+                           float* y) {
+  const float nearzero = 1e-30f;
+  float deltax = x2 - x1;
+  float deltay = y2 - y1;
+  unsigned np = 0;
+  if (deltax == 0)
+    deltax = (x1 > clip_box.x1) ? -nearzero : nearzero;
+  float xin;
+  float xout;
+  if (deltax > 0) {
+    xin = clip_box.x1;
+    xout = clip_box.x2;
+  } else {
+    xin = clip_box.x2;
+    xout = clip_box.x1;
+  }
+  float tinx = (xin - x1) / deltax;
+  if (deltay == 0)
+    deltay = (y1 > clip_box.y1) ? -nearzero : nearzero;
+  float yin;
+  float yout;
+  if (deltay > 0) {
+    yin = clip_box.y1;
+    yout = clip_box.y2;
+  } else {
+    yin = clip_box.y2;
+    yout = clip_box.y1;
+  }
+  float tiny = (yin - y1) / deltay;
+  float tin1;
+  float tin2;
+  if (tinx < tiny) {
+    tin1 = tinx;
+    tin2 = tiny;
+  } else {
+    tin1 = tiny;
+    tin2 = tinx;
+  }
+  if (tin1 <= 1.0f) {
+    if (0 < tin1) {
+      *x++ = xin;
+      *y++ = yin;
+      ++np;
+    }
+    if (tin2 <= 1.0f) {
+      float toutx = (xout - x1) / deltax;
+      float touty = (yout - y1) / deltay;
+      float tout1 = (toutx < touty) ? toutx : touty;
+      if (tin2 > 0 || tout1 > 0) {
+        if (tin2 <= tout1) {
+          if (tin2 > 0) {
+            if (tinx > tiny) {
+              *x++ = xin;
+              *y++ = y1 + (deltay * tinx);
+            } else {
+              *x++ = x1 + (deltax * tiny);
+              *y++ = yin;
+            }
+            ++np;
+          }
+          if (tout1 < 1.0f) {
+            if (toutx < touty) {
+              *x++ = xout;
+              *y++ = y1 + (deltay * toutx);
+            } else {
+              *x++ = x1 + (deltax * touty);
+              *y++ = yout;
+            }
+          } else {
+            *x++ = x2;
+            *y++ = y2;
+          }
+          ++np;
+        } else {
+          if (tinx > tiny) {
+            *x++ = xin;
+            *y++ = yout;
+          } else {
+            *x++ = xout;
+            *y++ = yin;
+          }
+          ++np;
+        }
+      }
+    }
+  }
+  return np;
+}
+#endif  //  defined(_SKIA_SUPPORT_)
+
+unsigned LineClip(float w,
+                  float h,
+                  float x1,
+                  float y1,
+                  float x2,
+                  float y2,
+                  float* x,
+                  float* y) {
+#if defined(_SKIA_SUPPORT_)
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    // TODO(caryclark) temporary replacement of antigrain in line function to
+    // permit removing antigrain altogether
+    rect_base rect = {0.0f, 0.0f, w, h};
+    return clip_liang_barsky(x1, y1, x2, y2, rect, x, y);
+  }
+#endif
+  pdfium::agg::rect_base<float> rect(0.0f, 0.0f, w, h);
+  return pdfium::agg::clip_liang_barsky<float>(x1, y1, x2, y2, rect, x, y);
+}
+
+}  // namespace
+
+CGdiDeviceDriver::CGdiDeviceDriver(HDC hDC, DeviceType device_type)
+    : m_hDC(hDC), m_DeviceType(device_type) {
+  SetStretchBltMode(m_hDC, HALFTONE);
+  DWORD obj_type = GetObjectType(m_hDC);
+  m_bMetafileDCType = obj_type == OBJ_ENHMETADC || obj_type == OBJ_ENHMETAFILE;
+  if (obj_type == OBJ_MEMDC) {
+    HBITMAP hBitmap = CreateBitmap(1, 1, 1, 1, nullptr);
+    hBitmap = (HBITMAP)SelectObject(m_hDC, hBitmap);
+    BITMAP bitmap;
+    GetObject(hBitmap, sizeof bitmap, &bitmap);
+    m_nBitsPerPixel = bitmap.bmBitsPixel;
+    m_Width = bitmap.bmWidth;
+    m_Height = abs(bitmap.bmHeight);
+    hBitmap = (HBITMAP)SelectObject(m_hDC, hBitmap);
+    DeleteObject(hBitmap);
+  } else {
+    m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL);
+    m_Width = ::GetDeviceCaps(m_hDC, HORZRES);
+    m_Height = ::GetDeviceCaps(m_hDC, VERTRES);
+  }
+  if (m_DeviceType != DeviceType::kDisplay) {
+    m_RenderCaps = FXRC_BIT_MASK;
+  } else {
+    m_RenderCaps = FXRC_GET_BITS | FXRC_BIT_MASK;
+  }
+}
+
+CGdiDeviceDriver::~CGdiDeviceDriver() = default;
+
+DeviceType CGdiDeviceDriver::GetDeviceType() const {
+  return m_DeviceType;
+}
+
+int CGdiDeviceDriver::GetDeviceCaps(int caps_id) const {
+  switch (caps_id) {
+    case FXDC_PIXEL_WIDTH:
+      return m_Width;
+    case FXDC_PIXEL_HEIGHT:
+      return m_Height;
+    case FXDC_BITS_PIXEL:
+      return m_nBitsPerPixel;
+    case FXDC_RENDER_CAPS:
+      return m_RenderCaps;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
+void CGdiDeviceDriver::SaveState() {
+  SaveDC(m_hDC);
+}
+
+void CGdiDeviceDriver::RestoreState(bool bKeepSaved) {
+  RestoreDC(m_hDC, -1);
+  if (bKeepSaved)
+    SaveDC(m_hDC);
+}
+
+bool CGdiDeviceDriver::GDI_SetDIBits(const RetainPtr<CFX_DIBBase>& source,
+                                     const FX_RECT& src_rect,
+                                     int left,
+                                     int top) {
+  if (m_DeviceType == DeviceType::kPrinter) {
+    RetainPtr<CFX_DIBBase> flipped_source = source->FlipImage(false, true);
+    if (!flipped_source) {
+      return false;
+    }
+
+    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, 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, flipped_source->GetBuffer().data(),
+                    (BITMAPINFO*)info.c_str(), DIB_RGB_COLORS, SRCCOPY);
+    return true;
+  }
+
+  ByteString info = GetBitmapInfo(source);
+  ::SetDIBitsToDevice(m_hDC, left, top, src_rect.Width(), src_rect.Height(),
+                      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_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(source);
+  if ((int64_t)abs(dest_width) * abs(dest_height) <
+          (int64_t)source->GetWidth() * source->GetHeight() * 4 ||
+      options.bInterpolateBilinear) {
+    SetStretchBltMode(m_hDC, HALFTONE);
+  } else {
+    SetStretchBltMode(m_hDC, COLORONCOLOR);
+  }
+  RetainPtr<CFX_DIBBase> stretch_source = source;
+  if (m_DeviceType == DeviceType::kPrinter &&
+      ((int64_t)source->GetWidth() * source->GetHeight() >
+       (int64_t)abs(dest_width) * abs(dest_height))) {
+    stretch_source = source->StretchTo(dest_width, dest_height,
+                                       FXDIB_ResampleOptions(), nullptr);
+    if (!stretch_source) {
+      return false;
+    }
+    info = GetBitmapInfo(stretch_source);
+  }
+  ::StretchDIBits(m_hDC, dest_left, dest_top, dest_width, dest_height, 0, 0,
+                  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_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 = source->GetWidth();
+  int height = source->GetHeight();
+  struct {
+    BITMAPINFOHEADER bmiHeader;
+    uint32_t bmiColors[2];
+  } bmi;
+  memset(&bmi.bmiHeader, 0, sizeof(BITMAPINFOHEADER));
+  bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+  bmi.bmiHeader.biBitCount = 1;
+  bmi.bmiHeader.biCompression = BI_RGB;
+  bmi.bmiHeader.biHeight = -height;
+  bmi.bmiHeader.biPlanes = 1;
+  bmi.bmiHeader.biWidth = width;
+  if (m_nBitsPerPixel != 1) {
+    SetStretchBltMode(m_hDC, HALFTONE);
+  }
+  bmi.bmiColors[0] = 0xffffff;
+  bmi.bmiColors[1] = 0;
+
+  HBRUSH hPattern = CreateBrush(bitmap_color);
+  HBRUSH hOld = (HBRUSH)SelectObject(m_hDC, hPattern);
+
+  // In PDF, when image mask is 1, use device bitmap; when mask is 0, use brush
+  // bitmap.
+  // A complete list of the boolen operations is as follows:
+
+  /* P(bitmap_color)    S(ImageMask)    D(DeviceBitmap)    Result
+   *        0                 0                0              0
+   *        0                 0                1              0
+   *        0                 1                0              0
+   *        0                 1                1              1
+   *        1                 0                0              1
+   *        1                 0                1              1
+   *        1                 1                0              0
+   *        1                 1                1              1
+   */
+  // The boolen codes is B8. Based on
+  // http://msdn.microsoft.com/en-us/library/aa932106.aspx, the ROP3 code is
+  // 0xB8074A
+
+  ::StretchDIBits(m_hDC, dest_left, dest_top, dest_width, dest_height, 0, 0,
+                  width, height, source->GetBuffer().data(), (BITMAPINFO*)&bmi,
+                  DIB_RGB_COLORS, 0xB8074A);
+
+  SelectObject(m_hDC, hOld);
+  DeleteObject(hPattern);
+
+  return true;
+}
+
+bool CGdiDeviceDriver::GetClipBox(FX_RECT* pRect) {
+  return !!(::GetClipBox(m_hDC, (RECT*)pRect));
+}
+
+bool CGdiDeviceDriver::MultiplyAlpha(float alpha) {
+  // Not implemented. All callers are using `CFX_DIBitmap`-backed raster devices
+  // anyway.
+  NOTREACHED();
+  return false;
+}
+
+bool CGdiDeviceDriver::MultiplyAlpha(const RetainPtr<CFX_DIBBase>& mask) {
+  // Not implemented. All callers are using `CFX_DIBitmap`-backed raster devices
+  // anyway.
+  NOTREACHED();
+  return false;
+}
+
+void CGdiDeviceDriver::DrawLine(float x1, float y1, float x2, float y2) {
+  if (!m_bMetafileDCType) {  // EMF drawing is not bound to the DC.
+    int startOutOfBoundsFlag = (x1 < 0) | ((x1 > m_Width) << 1) |
+                               ((y1 < 0) << 2) | ((y1 > m_Height) << 3);
+    int endOutOfBoundsFlag = (x2 < 0) | ((x2 > m_Width) << 1) |
+                             ((y2 < 0) << 2) | ((y2 > m_Height) << 3);
+    if (startOutOfBoundsFlag & endOutOfBoundsFlag)
+      return;
+
+    if (startOutOfBoundsFlag || endOutOfBoundsFlag) {
+      float x[2];
+      float y[2];
+      unsigned np = LineClip(m_Width, m_Height, x1, y1, x2, y2, x, y);
+      if (np == 0)
+        return;
+
+      if (np == 1) {
+        x2 = x[0];
+        y2 = y[0];
+      } else {
+        DCHECK_EQ(np, 2);
+        x1 = x[0];
+        y1 = y[0];
+        x2 = x[1];
+        y2 = y[1];
+      }
+    }
+  }
+
+  MoveToEx(m_hDC, FXSYS_roundf(x1), FXSYS_roundf(y1), nullptr);
+  LineTo(m_hDC, FXSYS_roundf(x2), FXSYS_roundf(y2));
+}
+
+bool CGdiDeviceDriver::DrawPath(const CFX_Path& path,
+                                const CFX_Matrix* pMatrix,
+                                const CFX_GraphStateData* pGraphState,
+                                uint32_t fill_color,
+                                uint32_t stroke_color,
+                                const CFX_FillRenderOptions& fill_options,
+                                BlendMode blend_type) {
+  if (blend_type != BlendMode::kNormal)
+    return false;
+
+  auto* pPlatform =
+      static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
+  if (!(pGraphState || stroke_color == 0) &&
+      !pPlatform->m_GdiplusExt.IsAvailable()) {
+    CFX_FloatRect bbox_f = path.GetBoundingBox();
+    if (pMatrix)
+      bbox_f = pMatrix->TransformRect(bbox_f);
+
+    FX_RECT bbox = bbox_f.GetInnerRect();
+    if (bbox.Width() <= 0) {
+      return DrawCosmeticLine(CFX_PointF(bbox.left, bbox.top),
+                              CFX_PointF(bbox.left, bbox.bottom + 1),
+                              fill_color, BlendMode::kNormal);
+    }
+    if (bbox.Height() <= 0) {
+      return DrawCosmeticLine(CFX_PointF(bbox.left, bbox.top),
+                              CFX_PointF(bbox.right + 1, bbox.top), fill_color,
+                              BlendMode::kNormal);
+    }
+  }
+  int fill_alpha = FXARGB_A(fill_color);
+  int stroke_alpha = FXARGB_A(stroke_color);
+  bool bDrawAlpha = (fill_alpha > 0 && fill_alpha < 255) ||
+                    (stroke_alpha > 0 && stroke_alpha < 255 && pGraphState);
+  if (!pPlatform->m_GdiplusExt.IsAvailable() && bDrawAlpha)
+    return false;
+
+  if (pPlatform->m_GdiplusExt.IsAvailable()) {
+    if (bDrawAlpha ||
+        ((m_DeviceType != DeviceType::kPrinter && !fill_options.full_cover) ||
+         (pGraphState && !pGraphState->m_DashArray.empty()))) {
+      if (!((!pMatrix || !pMatrix->WillScale()) && pGraphState &&
+            pGraphState->m_LineWidth == 1.0f && path.IsRect())) {
+        if (pPlatform->m_GdiplusExt.DrawPath(m_hDC, path, pMatrix, pGraphState,
+                                             fill_color, stroke_color,
+                                             fill_options)) {
+          return true;
+        }
+      }
+    }
+  }
+  const bool fill =
+      fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill;
+  HPEN hPen = nullptr;
+  HBRUSH hBrush = nullptr;
+  if (pGraphState && stroke_alpha) {
+    SetMiterLimit(m_hDC, pGraphState->m_MiterLimit, nullptr);
+    hPen = CreateExtPen(pGraphState, pMatrix, stroke_color);
+    hPen = (HPEN)SelectObject(m_hDC, hPen);
+  }
+  if (fill && fill_alpha) {
+    SetPolyFillMode(m_hDC, FillTypeToGdiFillType(fill_options.fill_type));
+    hBrush = CreateBrush(fill_color);
+    hBrush = (HBRUSH)SelectObject(m_hDC, hBrush);
+  }
+  if (path.GetPoints().size() == 2 && pGraphState &&
+      !pGraphState->m_DashArray.empty()) {
+    CFX_PointF pos1 = path.GetPoint(0);
+    CFX_PointF pos2 = path.GetPoint(1);
+    if (pMatrix) {
+      pos1 = pMatrix->Transform(pos1);
+      pos2 = pMatrix->Transform(pos2);
+    }
+    DrawLine(pos1.x, pos1.y, pos2.x, pos2.y);
+  } else {
+    SetPathToDC(m_hDC, path, pMatrix);
+    if (pGraphState && stroke_alpha) {
+      if (fill && fill_alpha) {
+        if (fill_options.text_mode) {
+          StrokeAndFillPath(m_hDC);
+        } else {
+          FillPath(m_hDC);
+          SetPathToDC(m_hDC, path, pMatrix);
+          StrokePath(m_hDC);
+        }
+      } else {
+        StrokePath(m_hDC);
+      }
+    } else if (fill && fill_alpha) {
+      FillPath(m_hDC);
+    }
+  }
+  if (hPen) {
+    hPen = (HPEN)SelectObject(m_hDC, hPen);
+    DeleteObject(hPen);
+  }
+  if (hBrush) {
+    hBrush = (HBRUSH)SelectObject(m_hDC, hBrush);
+    DeleteObject(hBrush);
+  }
+  return true;
+}
+
+bool CGdiDeviceDriver::FillRectWithBlend(const FX_RECT& rect,
+                                         uint32_t fill_color,
+                                         BlendMode blend_type) {
+  if (blend_type != BlendMode::kNormal)
+    return false;
+
+  int alpha;
+  FX_COLORREF colorref;
+  std::tie(alpha, colorref) = ArgbToAlphaAndColorRef(fill_color);
+  if (alpha == 0)
+    return true;
+
+  if (alpha < 255)
+    return false;
+
+  HBRUSH hBrush = CreateSolidBrush(colorref);
+  const RECT* pRect = reinterpret_cast<const RECT*>(&rect);
+  ::FillRect(m_hDC, pRect, hBrush);
+  DeleteObject(hBrush);
+  return true;
+}
+
+void CGdiDeviceDriver::SetBaseClip(const FX_RECT& rect) {
+  m_BaseClipBox = rect;
+}
+
+bool CGdiDeviceDriver::SetClip_PathFill(
+    const CFX_Path& path,
+    const CFX_Matrix* pMatrix,
+    const CFX_FillRenderOptions& fill_options) {
+  absl::optional<CFX_FloatRect> maybe_rectf = path.GetRect(pMatrix);
+  if (maybe_rectf.has_value()) {
+    FX_RECT rect = maybe_rectf.value().GetOuterRect();
+    // Can easily apply base clip to protect against wildly large rectangular
+    // clips. crbug.com/1019026
+    if (m_BaseClipBox.has_value())
+      rect.Intersect(m_BaseClipBox.value());
+    return IntersectClipRect(m_hDC, rect.left, rect.top, rect.right,
+                             rect.bottom) != ERROR;
+  }
+  SetPathToDC(m_hDC, path, pMatrix);
+  SetPolyFillMode(m_hDC, FillTypeToGdiFillType(fill_options.fill_type));
+  SelectClipPath(m_hDC, RGN_AND);
+  return true;
+}
+
+bool CGdiDeviceDriver::SetClip_PathStroke(
+    const CFX_Path& path,
+    const CFX_Matrix* pMatrix,
+    const CFX_GraphStateData* pGraphState) {
+  HPEN hPen = CreateExtPen(pGraphState, pMatrix, 0xff000000);
+  hPen = (HPEN)SelectObject(m_hDC, hPen);
+  SetPathToDC(m_hDC, path, pMatrix);
+  WidenPath(m_hDC);
+  SetPolyFillMode(m_hDC, WINDING);
+  bool ret = !!SelectClipPath(m_hDC, RGN_AND);
+  hPen = (HPEN)SelectObject(m_hDC, hPen);
+  DeleteObject(hPen);
+  return ret;
+}
+
+bool CGdiDeviceDriver::DrawCosmeticLine(const CFX_PointF& ptMoveTo,
+                                        const CFX_PointF& ptLineTo,
+                                        uint32_t color,
+                                        BlendMode blend_type) {
+  if (blend_type != BlendMode::kNormal)
+    return false;
+
+  int alpha;
+  FX_COLORREF colorref;
+  std::tie(alpha, colorref) = ArgbToAlphaAndColorRef(color);
+  if (alpha == 0)
+    return true;
+
+  HPEN hPen = CreatePen(PS_SOLID, 1, colorref);
+  hPen = (HPEN)SelectObject(m_hDC, hPen);
+  MoveToEx(m_hDC, FXSYS_roundf(ptMoveTo.x), FXSYS_roundf(ptMoveTo.y), nullptr);
+  LineTo(m_hDC, FXSYS_roundf(ptLineTo.x), FXSYS_roundf(ptLineTo.y));
+  hPen = (HPEN)SelectObject(m_hDC, hPen);
+  DeleteObject(hPen);
+  return true;
+}
diff --git a/core/fxge/win32/cgdi_device_driver.h b/core/fxge/win32/cgdi_device_driver.h
new file mode 100644
index 0000000..de8c41e
--- /dev/null
+++ b/core/fxge/win32/cgdi_device_driver.h
@@ -0,0 +1,82 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_WIN32_CGDI_DEVICE_DRIVER_H_
+#define CORE_FXGE_WIN32_CGDI_DEVICE_DRIVER_H_
+
+#include <windows.h>
+
+#include "core/fxcrt/retain_ptr.h"
+#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);
+  ~CGdiDeviceDriver() override;
+
+  // RenderDeviceDriverIface:
+  DeviceType GetDeviceType() const override;
+  int GetDeviceCaps(int caps_id) const override;
+  void SaveState() override;
+  void RestoreState(bool bKeepSaved) override;
+  void SetBaseClip(const FX_RECT& rect) override;
+  bool SetClip_PathFill(const CFX_Path& path,
+                        const CFX_Matrix* pObject2Device,
+                        const CFX_FillRenderOptions& fill_options) override;
+  bool SetClip_PathStroke(const CFX_Path& path,
+                          const CFX_Matrix* pObject2Device,
+                          const CFX_GraphStateData* pGraphState) override;
+  bool DrawPath(const CFX_Path& path,
+                const CFX_Matrix* pObject2Device,
+                const CFX_GraphStateData* pGraphState,
+                uint32_t fill_color,
+                uint32_t stroke_color,
+                const CFX_FillRenderOptions& fill_options,
+                BlendMode blend_type) override;
+  bool FillRectWithBlend(const FX_RECT& rect,
+                         uint32_t fill_color,
+                         BlendMode blend_type) override;
+  bool DrawCosmeticLine(const CFX_PointF& ptMoveTo,
+                        const CFX_PointF& ptLineTo,
+                        uint32_t color,
+                        BlendMode blend_type) override;
+  bool GetClipBox(FX_RECT* pRect) override;
+  bool MultiplyAlpha(float alpha) override;
+  bool MultiplyAlpha(const RetainPtr<CFX_DIBBase>& mask) override;
+
+  void DrawLine(float x1, float y1, float x2, float y2);
+
+  bool GDI_SetDIBits(const RetainPtr<CFX_DIBBase>& source,
+                     const FX_RECT& src_rect,
+                     int left,
+                     int top);
+  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_DIBBase>& source,
+                          int dest_left,
+                          int dest_top,
+                          int dest_width,
+                          int dest_height,
+                          uint32_t bitmap_color);
+
+  const HDC m_hDC;
+  bool m_bMetafileDCType;
+  int m_Width;
+  int m_Height;
+  int m_nBitsPerPixel;
+  const DeviceType m_DeviceType;
+  int m_RenderCaps;
+  absl::optional<FX_RECT> m_BaseClipBox;
+};
+
+#endif  // CORE_FXGE_WIN32_CGDI_DEVICE_DRIVER_H_
diff --git a/core/fxge/win32/cgdi_display_driver.cpp b/core/fxge/win32/cgdi_display_driver.cpp
new file mode 100644
index 0000000..763e300
--- /dev/null
+++ b/core/fxge/win32/cgdi_display_driver.cpp
@@ -0,0 +1,218 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxge/win32/cgdi_display_driver.h"
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxge/dib/cfx_dibbase.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/render_defines.h"
+#include "core/fxge/win32/cwin32_platform.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+
+CGdiDisplayDriver::CGdiDisplayDriver(HDC hDC)
+    : CGdiDeviceDriver(hDC, DeviceType::kDisplay) {
+  auto* pPlatform =
+      static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
+  if (pPlatform->m_GdiplusExt.IsAvailable()) {
+    m_RenderCaps |= FXRC_ALPHA_PATH | FXRC_ALPHA_IMAGE;
+  }
+}
+
+CGdiDisplayDriver::~CGdiDisplayDriver() = default;
+
+int CGdiDisplayDriver::GetDeviceCaps(int caps_id) const {
+  if (caps_id == FXDC_HORZ_SIZE || caps_id == FXDC_VERT_SIZE)
+    return 0;
+  return CGdiDeviceDriver::GetDeviceCaps(caps_id);
+}
+
+bool CGdiDisplayDriver::GetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
+                                  int left,
+                                  int top) {
+  bool ret = false;
+  int width = pBitmap->GetWidth();
+  int height = pBitmap->GetHeight();
+  HBITMAP hbmp = CreateCompatibleBitmap(m_hDC, width, height);
+  HDC hDCMemory = CreateCompatibleDC(m_hDC);
+  HBITMAP holdbmp = (HBITMAP)SelectObject(hDCMemory, hbmp);
+  BitBlt(hDCMemory, 0, 0, width, height, m_hDC, left, top, SRCCOPY);
+  SelectObject(hDCMemory, holdbmp);
+  BITMAPINFO bmi;
+  memset(&bmi, 0, sizeof bmi);
+  bmi.bmiHeader.biSize = sizeof bmi.bmiHeader;
+  bmi.bmiHeader.biBitCount = pBitmap->GetBPP();
+  bmi.bmiHeader.biHeight = -height;
+  bmi.bmiHeader.biPlanes = 1;
+  bmi.bmiHeader.biWidth = width;
+  if (pBitmap->GetBPP() > 8) {
+    ret = ::GetDIBits(hDCMemory, hbmp, 0, height,
+                      pBitmap->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->GetWritableBuffer().data(), &bmi, DIB_RGB_COLORS);
+      ret = pBitmap->TransferBitmap(0, 0, width, height, bitmap, 0, 0);
+    } else {
+      ret = false;
+    }
+  }
+  if (ret && pBitmap->IsAlphaFormat())
+    pBitmap->SetUniformOpaqueAlpha();
+
+  DeleteObject(hbmp);
+  DeleteObject(hDCMemory);
+  return ret;
+}
+
+bool CGdiDisplayDriver::SetDIBits(const RetainPtr<CFX_DIBBase>& pSource,
+                                  uint32_t color,
+                                  const FX_RECT& src_rect,
+                                  int left,
+                                  int top,
+                                  BlendMode blend_type) {
+  DCHECK_EQ(blend_type, BlendMode::kNormal);
+  if (pSource->IsMaskFormat()) {
+    int width = pSource->GetWidth(), height = pSource->GetHeight();
+    int alpha = FXARGB_A(color);
+    if (pSource->GetBPP() != 1 || alpha != 255) {
+      auto background = pdfium::MakeRetain<CFX_DIBitmap>();
+      if (!background->Create(width, height, FXDIB_Format::kRgb32) ||
+          !GetDIBits(background, left, top) ||
+          !background->CompositeMask(0, 0, width, height, pSource, color, 0, 0,
+                                     BlendMode::kNormal, nullptr, false)) {
+        return false;
+      }
+      FX_RECT alpha_src_rect(0, 0, width, height);
+      return SetDIBits(background, 0, alpha_src_rect, left, top,
+                       BlendMode::kNormal);
+    }
+    FX_RECT clip_rect(left, top, left + src_rect.Width(),
+                      top + src_rect.Height());
+    return StretchDIBits(pSource, color, left - src_rect.left,
+                         top - src_rect.top, width, height, &clip_rect,
+                         FXDIB_ResampleOptions(), BlendMode::kNormal);
+  }
+  int width = src_rect.Width();
+  int height = src_rect.Height();
+  if (pSource->IsAlphaFormat()) {
+    auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+    if (!bitmap->Create(width, height, FXDIB_Format::kRgb) ||
+        !GetDIBits(bitmap, left, top) ||
+        !bitmap->CompositeBitmap(0, 0, width, height, pSource, src_rect.left,
+                                 src_rect.top, BlendMode::kNormal, nullptr,
+                                 false)) {
+      return false;
+    }
+    FX_RECT alpha_src_rect(0, 0, width, height);
+    return SetDIBits(bitmap, 0, alpha_src_rect, left, top, BlendMode::kNormal);
+  }
+  return GDI_SetDIBits(pSource, src_rect, left, top);
+}
+
+bool CGdiDisplayDriver::UseFoxitStretchEngine(
+    const RetainPtr<CFX_DIBBase>& pSource,
+    uint32_t color,
+    int dest_left,
+    int dest_top,
+    int dest_width,
+    int dest_height,
+    const FX_RECT* pClipRect,
+    const FXDIB_ResampleOptions& options) {
+  FX_RECT bitmap_clip = *pClipRect;
+  if (dest_width < 0)
+    dest_left += dest_width;
+
+  if (dest_height < 0)
+    dest_top += dest_height;
+
+  bitmap_clip.Offset(-dest_left, -dest_top);
+  RetainPtr<CFX_DIBBase> pStretched =
+      pSource->StretchTo(dest_width, dest_height, options, &bitmap_clip);
+  if (!pStretched)
+    return true;
+
+  FX_RECT src_rect(0, 0, pStretched->GetWidth(), pStretched->GetHeight());
+  return SetDIBits(pStretched, color, src_rect, pClipRect->left, pClipRect->top,
+                   BlendMode::kNormal);
+}
+
+bool CGdiDisplayDriver::StretchDIBits(const RetainPtr<CFX_DIBBase>& pSource,
+                                      uint32_t color,
+                                      int dest_left,
+                                      int dest_top,
+                                      int dest_width,
+                                      int dest_height,
+                                      const FX_RECT* pClipRect,
+                                      const FXDIB_ResampleOptions& options,
+                                      BlendMode blend_type) {
+  DCHECK(pSource);
+  DCHECK(pClipRect);
+
+  if (options.HasAnyOptions() || dest_width > 10000 || dest_width < -10000 ||
+      dest_height > 10000 || dest_height < -10000) {
+    return UseFoxitStretchEngine(pSource, color, dest_left, dest_top,
+                                 dest_width, dest_height, pClipRect, options);
+  }
+  if (pSource->IsMaskFormat()) {
+    FX_RECT image_rect;
+    image_rect.left = dest_width > 0 ? dest_left : dest_left + dest_width;
+    image_rect.right = dest_width > 0 ? dest_left + dest_width : dest_left;
+    image_rect.top = dest_height > 0 ? dest_top : dest_top + dest_height;
+    image_rect.bottom = dest_height > 0 ? dest_top + dest_height : dest_top;
+    FX_RECT clip_rect = image_rect;
+    clip_rect.Intersect(*pClipRect);
+    clip_rect.Offset(-image_rect.left, -image_rect.top);
+    int clip_width = clip_rect.Width(), clip_height = clip_rect.Height();
+    RetainPtr<CFX_DIBitmap> pStretched(pSource->StretchTo(
+        dest_width, dest_height, FXDIB_ResampleOptions(), &clip_rect));
+    if (!pStretched)
+      return true;
+
+    auto background = pdfium::MakeRetain<CFX_DIBitmap>();
+    if (!background->Create(clip_width, clip_height, FXDIB_Format::kRgb32) ||
+        !GetDIBits(background, image_rect.left + clip_rect.left,
+                   image_rect.top + clip_rect.top) ||
+        !background->CompositeMask(0, 0, clip_width, clip_height, pStretched,
+                                   color, 0, 0, BlendMode::kNormal, nullptr,
+                                   false)) {
+      return false;
+    }
+
+    FX_RECT src_rect(0, 0, clip_width, clip_height);
+    return SetDIBits(background, 0, src_rect, image_rect.left + clip_rect.left,
+                     image_rect.top + clip_rect.top, BlendMode::kNormal);
+  }
+  if (pSource->IsAlphaFormat()) {
+    auto* pPlatform =
+        static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
+    if (pPlatform->m_GdiplusExt.IsAvailable()) {
+      return pPlatform->m_GdiplusExt.StretchDIBits(
+          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());
+  }
+  return GDI_StretchDIBits(pSource, dest_left, dest_top, dest_width,
+                           dest_height, FXDIB_ResampleOptions());
+}
+
+bool CGdiDisplayDriver::StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                                    int bitmap_alpha,
+                                    uint32_t color,
+                                    const CFX_Matrix& matrix,
+                                    const FXDIB_ResampleOptions& options,
+                                    std::unique_ptr<CFX_ImageRenderer>* handle,
+                                    BlendMode blend_type) {
+  return false;
+}
diff --git a/core/fxge/win32/cgdi_display_driver.h b/core/fxge/win32/cgdi_display_driver.h
new file mode 100644
index 0000000..2e43194
--- /dev/null
+++ b/core/fxge/win32/cgdi_display_driver.h
@@ -0,0 +1,66 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_WIN32_CGDI_DISPLAY_DRIVER_H_
+#define CORE_FXGE_WIN32_CGDI_DISPLAY_DRIVER_H_
+
+#include <stdint.h>
+#include <windows.h>
+
+#include <memory>
+
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/win32/cgdi_device_driver.h"
+
+class CFX_DIBBase;
+struct FXDIB_ResampleOptions;
+struct FX_RECT;
+
+class CGdiDisplayDriver final : public CGdiDeviceDriver {
+ public:
+  explicit CGdiDisplayDriver(HDC hDC);
+  ~CGdiDisplayDriver() override;
+
+ private:
+  // CGdiDisplayDriver:
+  int GetDeviceCaps(int caps_id) const override;
+  bool GetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
+                 int left,
+                 int top) override;
+  bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                 uint32_t color,
+                 const FX_RECT& src_rect,
+                 int left,
+                 int top,
+                 BlendMode blend_type) override;
+  bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                     uint32_t color,
+                     int dest_left,
+                     int dest_top,
+                     int dest_width,
+                     int dest_height,
+                     const FX_RECT* pClipRect,
+                     const FXDIB_ResampleOptions& options,
+                     BlendMode blend_type) override;
+  bool StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                   int bitmap_alpha,
+                   uint32_t color,
+                   const CFX_Matrix& matrix,
+                   const FXDIB_ResampleOptions& options,
+                   std::unique_ptr<CFX_ImageRenderer>* handle,
+                   BlendMode blend_type) override;
+
+  bool UseFoxitStretchEngine(const RetainPtr<CFX_DIBBase>& pSource,
+                             uint32_t color,
+                             int dest_left,
+                             int dest_top,
+                             int dest_width,
+                             int dest_height,
+                             const FX_RECT* pClipRect,
+                             const FXDIB_ResampleOptions& options);
+};
+
+#endif  // CORE_FXGE_WIN32_CGDI_DISPLAY_DRIVER_H_
diff --git a/core/fxge/win32/cgdi_plus_ext.cpp b/core/fxge/win32/cgdi_plus_ext.cpp
new file mode 100644
index 0000000..42cf505
--- /dev/null
+++ b/core/fxge/win32/cgdi_plus_ext.cpp
@@ -0,0 +1,776 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxge/win32/cgdi_plus_ext.h"
+
+#include <windows.h>
+
+#include <objidl.h>
+
+#include <algorithm>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/fx_string_wrappers.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/cfx_graphstatedata.h"
+#include "core/fxge/cfx_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"
+
+// Has to come before gdiplus.h
+namespace Gdiplus {
+using std::max;
+using std::min;
+}  // namespace Gdiplus
+
+#include <gdiplus.h>  // NOLINT
+
+namespace {
+
+enum {
+  FuncId_GdipCreatePath2,
+  FuncId_GdipSetPenDashArray,
+  FuncId_GdipSetPenLineJoin,
+  FuncId_GdipCreateFromHDC,
+  FuncId_GdipSetPageUnit,
+  FuncId_GdipSetSmoothingMode,
+  FuncId_GdipCreateSolidFill,
+  FuncId_GdipFillPath,
+  FuncId_GdipDeleteBrush,
+  FuncId_GdipCreatePen1,
+  FuncId_GdipSetPenMiterLimit,
+  FuncId_GdipDrawPath,
+  FuncId_GdipDeletePen,
+  FuncId_GdipDeletePath,
+  FuncId_GdipDeleteGraphics,
+  FuncId_GdipDisposeImage,
+  FuncId_GdipCreateBitmapFromScan0,
+  FuncId_GdipSetImagePalette,
+  FuncId_GdipSetInterpolationMode,
+  FuncId_GdipDrawImagePointsI,
+  FuncId_GdiplusStartup,
+  FuncId_GdipDrawLineI,
+  FuncId_GdipCreatePath,
+  FuncId_GdipSetPathFillMode,
+  FuncId_GdipSetClipRegion,
+  FuncId_GdipWidenPath,
+  FuncId_GdipAddPathLine,
+  FuncId_GdipAddPathRectangle,
+  FuncId_GdipDeleteRegion,
+  FuncId_GdipSetPenLineCap197819,
+  FuncId_GdipSetPenDashOffset,
+  FuncId_GdipCreateMatrix2,
+  FuncId_GdipDeleteMatrix,
+  FuncId_GdipSetWorldTransform,
+  FuncId_GdipSetPixelOffsetMode,
+};
+
+LPCSTR g_GdipFuncNames[] = {
+    "GdipCreatePath2",
+    "GdipSetPenDashArray",
+    "GdipSetPenLineJoin",
+    "GdipCreateFromHDC",
+    "GdipSetPageUnit",
+    "GdipSetSmoothingMode",
+    "GdipCreateSolidFill",
+    "GdipFillPath",
+    "GdipDeleteBrush",
+    "GdipCreatePen1",
+    "GdipSetPenMiterLimit",
+    "GdipDrawPath",
+    "GdipDeletePen",
+    "GdipDeletePath",
+    "GdipDeleteGraphics",
+    "GdipDisposeImage",
+    "GdipCreateBitmapFromScan0",
+    "GdipSetImagePalette",
+    "GdipSetInterpolationMode",
+    "GdipDrawImagePointsI",
+    "GdiplusStartup",
+    "GdipDrawLineI",
+    "GdipCreatePath",
+    "GdipSetPathFillMode",
+    "GdipSetClipRegion",
+    "GdipWidenPath",
+    "GdipAddPathLine",
+    "GdipAddPathRectangle",
+    "GdipDeleteRegion",
+    "GdipSetPenLineCap197819",
+    "GdipSetPenDashOffset",
+    "GdipCreateMatrix2",
+    "GdipDeleteMatrix",
+    "GdipSetWorldTransform",
+    "GdipSetPixelOffsetMode",
+};
+static_assert(std::size(g_GdipFuncNames) ==
+                  static_cast<size_t>(FuncId_GdipSetPixelOffsetMode) + 1,
+              "g_GdipFuncNames has wrong size");
+
+using FuncType_GdipCreatePath2 =
+    decltype(&Gdiplus::DllExports::GdipCreatePath2);
+using FuncType_GdipSetPenDashArray =
+    decltype(&Gdiplus::DllExports::GdipSetPenDashArray);
+using FuncType_GdipSetPenLineJoin =
+    decltype(&Gdiplus::DllExports::GdipSetPenLineJoin);
+using FuncType_GdipCreateFromHDC =
+    decltype(&Gdiplus::DllExports::GdipCreateFromHDC);
+using FuncType_GdipSetPageUnit =
+    decltype(&Gdiplus::DllExports::GdipSetPageUnit);
+using FuncType_GdipSetSmoothingMode =
+    decltype(&Gdiplus::DllExports::GdipSetSmoothingMode);
+using FuncType_GdipCreateSolidFill =
+    decltype(&Gdiplus::DllExports::GdipCreateSolidFill);
+using FuncType_GdipFillPath = decltype(&Gdiplus::DllExports::GdipFillPath);
+using FuncType_GdipDeleteBrush =
+    decltype(&Gdiplus::DllExports::GdipDeleteBrush);
+using FuncType_GdipCreatePen1 = decltype(&Gdiplus::DllExports::GdipCreatePen1);
+using FuncType_GdipSetPenMiterLimit =
+    decltype(&Gdiplus::DllExports::GdipSetPenMiterLimit);
+using FuncType_GdipDrawPath = decltype(&Gdiplus::DllExports::GdipDrawPath);
+using FuncType_GdipDeletePen = decltype(&Gdiplus::DllExports::GdipDeletePen);
+using FuncType_GdipDeletePath = decltype(&Gdiplus::DllExports::GdipDeletePath);
+using FuncType_GdipDeleteGraphics =
+    decltype(&Gdiplus::DllExports::GdipDeleteGraphics);
+using FuncType_GdipDisposeImage =
+    decltype(&Gdiplus::DllExports::GdipDisposeImage);
+using FuncType_GdipCreateBitmapFromScan0 =
+    decltype(&Gdiplus::DllExports::GdipCreateBitmapFromScan0);
+using FuncType_GdipSetImagePalette =
+    decltype(&Gdiplus::DllExports::GdipSetImagePalette);
+using FuncType_GdipSetInterpolationMode =
+    decltype(&Gdiplus::DllExports::GdipSetInterpolationMode);
+using FuncType_GdipDrawImagePointsI =
+    decltype(&Gdiplus::DllExports::GdipDrawImagePointsI);
+using FuncType_GdiplusStartup = decltype(&Gdiplus::GdiplusStartup);
+using FuncType_GdipDrawLineI = decltype(&Gdiplus::DllExports::GdipDrawLineI);
+using FuncType_GdipCreatePath = decltype(&Gdiplus::DllExports::GdipCreatePath);
+using FuncType_GdipSetPathFillMode =
+    decltype(&Gdiplus::DllExports::GdipSetPathFillMode);
+using FuncType_GdipSetClipRegion =
+    decltype(&Gdiplus::DllExports::GdipSetClipRegion);
+using FuncType_GdipWidenPath = decltype(&Gdiplus::DllExports::GdipWidenPath);
+using FuncType_GdipAddPathLine =
+    decltype(&Gdiplus::DllExports::GdipAddPathLine);
+using FuncType_GdipAddPathRectangle =
+    decltype(&Gdiplus::DllExports::GdipAddPathRectangle);
+using FuncType_GdipDeleteRegion =
+    decltype(&Gdiplus::DllExports::GdipDeleteRegion);
+using FuncType_GdipSetPenLineCap197819 =
+    decltype(&Gdiplus::DllExports::GdipSetPenLineCap197819);
+using FuncType_GdipSetPenDashOffset =
+    decltype(&Gdiplus::DllExports::GdipSetPenDashOffset);
+using FuncType_GdipCreateMatrix2 =
+    decltype(&Gdiplus::DllExports::GdipCreateMatrix2);
+using FuncType_GdipDeleteMatrix =
+    decltype(&Gdiplus::DllExports::GdipDeleteMatrix);
+using FuncType_GdipSetWorldTransform =
+    decltype(&Gdiplus::DllExports::GdipSetWorldTransform);
+using FuncType_GdipSetPixelOffsetMode =
+    decltype(&Gdiplus::DllExports::GdipSetPixelOffsetMode);
+#define CallFunc(funcname)               \
+  reinterpret_cast<FuncType_##funcname>( \
+      GdiplusExt.m_Functions[FuncId_##funcname])
+
+Gdiplus::GpFillMode FillType2Gdip(CFX_FillRenderOptions::FillType fill_type) {
+  return fill_type == CFX_FillRenderOptions::FillType::kEvenOdd
+             ? Gdiplus::FillModeAlternate
+             : Gdiplus::FillModeWinding;
+}
+
+const CGdiplusExt& GetGdiplusExt() {
+  auto* pData =
+      static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
+  return pData->m_GdiplusExt;
+}
+
+Gdiplus::GpBrush* GdipCreateBrushImpl(DWORD argb) {
+  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
+  Gdiplus::GpSolidFill* solidBrush = nullptr;
+  CallFunc(GdipCreateSolidFill)((Gdiplus::ARGB)argb, &solidBrush);
+  return solidBrush;
+}
+
+void OutputImage(Gdiplus::GpGraphics* pGraphics,
+                 const RetainPtr<CFX_DIBBase>& source,
+                 const FX_RECT& src_rect,
+                 int dest_left,
+                 int dest_top,
+                 int dest_width,
+                 int dest_height) {
+  int src_width = src_rect.Width();
+  int src_height = src_rect.Height();
+  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
+  if (source->GetBPP() == 1 && (src_rect.left % 8)) {
+    FX_RECT new_rect(0, 0, src_width, src_height);
+    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 = 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 (source->GetFormat()) {
+    case FXDIB_Format::kArgb:
+      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
+                                          PixelFormat32bppARGB, scan0, &bitmap);
+      break;
+    case FXDIB_Format::kRgb32:
+      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
+                                          PixelFormat32bppRGB, scan0, &bitmap);
+      break;
+    case FXDIB_Format::kRgb:
+      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
+                                          PixelFormat24bppRGB, scan0, &bitmap);
+      break;
+    case FXDIB_Format::k8bppRgb: {
+      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
+                                          PixelFormat8bppIndexed, scan0,
+                                          &bitmap);
+      UINT pal[258];
+      pal[0] = 0;
+      pal[1] = 256;
+      for (int i = 0; i < 256; i++)
+        pal[i + 2] = source->GetPaletteArgb(i);
+      CallFunc(GdipSetImagePalette)(bitmap, (Gdiplus::ColorPalette*)pal);
+      break;
+    }
+    case FXDIB_Format::k1bppRgb: {
+      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
+                                          PixelFormat1bppIndexed, scan0,
+                                          &bitmap);
+      break;
+    }
+    case FXDIB_Format::kInvalid:
+    case FXDIB_Format::k1bppMask:
+    case FXDIB_Format::k8bppMask:
+      NOTREACHED_NORETURN();
+  }
+  if (dest_height < 0) {
+    dest_height--;
+  }
+  if (dest_width < 0) {
+    dest_width--;
+  }
+  Gdiplus::Point destinationPoints[] = {
+      Gdiplus::Point(dest_left, dest_top),
+      Gdiplus::Point(dest_left + dest_width, dest_top),
+      Gdiplus::Point(dest_left, dest_top + dest_height)};
+  CallFunc(GdipDrawImagePointsI)(pGraphics, bitmap, destinationPoints, 3);
+  CallFunc(GdipDisposeImage)(bitmap);
+}
+
+Gdiplus::GpPen* GdipCreatePenImpl(const CFX_GraphStateData* pGraphState,
+                                  const CFX_Matrix* pMatrix,
+                                  DWORD argb,
+                                  bool bTextMode) {
+  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
+  float width = pGraphState->m_LineWidth;
+  if (!bTextMode) {
+    float unit = pMatrix
+                     ? 1.0f / ((pMatrix->GetXUnit() + pMatrix->GetYUnit()) / 2)
+                     : 1.0f;
+    width = std::max(width, unit);
+  }
+  Gdiplus::GpPen* pPen = nullptr;
+  CallFunc(GdipCreatePen1)((Gdiplus::ARGB)argb, width, Gdiplus::UnitWorld,
+                           &pPen);
+  Gdiplus::LineCap lineCap = Gdiplus::LineCapFlat;
+  Gdiplus::DashCap dashCap = Gdiplus::DashCapFlat;
+  bool bDashExtend = false;
+  switch (pGraphState->m_LineCap) {
+    case CFX_GraphStateData::LineCap::kButt:
+      lineCap = Gdiplus::LineCapFlat;
+      break;
+    case CFX_GraphStateData::LineCap::kRound:
+      lineCap = Gdiplus::LineCapRound;
+      dashCap = Gdiplus::DashCapRound;
+      bDashExtend = true;
+      break;
+    case CFX_GraphStateData::LineCap::kSquare:
+      lineCap = Gdiplus::LineCapSquare;
+      bDashExtend = true;
+      break;
+  }
+  CallFunc(GdipSetPenLineCap197819)(pPen, lineCap, lineCap, dashCap);
+  Gdiplus::LineJoin lineJoin = Gdiplus::LineJoinMiterClipped;
+  switch (pGraphState->m_LineJoin) {
+    case CFX_GraphStateData::LineJoin::kMiter:
+      lineJoin = Gdiplus::LineJoinMiterClipped;
+      break;
+    case CFX_GraphStateData::LineJoin::kRound:
+      lineJoin = Gdiplus::LineJoinRound;
+      break;
+    case CFX_GraphStateData::LineJoin::kBevel:
+      lineJoin = Gdiplus::LineJoinBevel;
+      break;
+  }
+  CallFunc(GdipSetPenLineJoin)(pPen, lineJoin);
+  if (!pGraphState->m_DashArray.empty()) {
+    float* pDashArray =
+        FX_Alloc(float, FxAlignToBoundary<2>(pGraphState->m_DashArray.size()));
+    int nCount = 0;
+    float on_leftover = 0;
+    float off_leftover = 0;
+    for (size_t i = 0; i < pGraphState->m_DashArray.size(); i += 2) {
+      float on_phase = pGraphState->m_DashArray[i];
+      float off_phase;
+      if (i == pGraphState->m_DashArray.size() - 1)
+        off_phase = on_phase;
+      else
+        off_phase = pGraphState->m_DashArray[i + 1];
+      on_phase /= width;
+      off_phase /= width;
+      if (on_phase + off_phase <= 0.00002f) {
+        on_phase = 0.1f;
+        off_phase = 0.1f;
+      }
+      if (bDashExtend) {
+        if (off_phase < 1)
+          off_phase = 0;
+        else
+          --off_phase;
+        ++on_phase;
+      }
+      if (on_phase == 0 || off_phase == 0) {
+        if (nCount == 0) {
+          on_leftover += on_phase;
+          off_leftover += off_phase;
+        } else {
+          pDashArray[nCount - 2] += on_phase;
+          pDashArray[nCount - 1] += off_phase;
+        }
+      } else {
+        pDashArray[nCount++] = on_phase + on_leftover;
+        on_leftover = 0;
+        pDashArray[nCount++] = off_phase + off_leftover;
+        off_leftover = 0;
+      }
+    }
+    CallFunc(GdipSetPenDashArray)(pPen, pDashArray, nCount);
+    float phase = pGraphState->m_DashPhase;
+    if (bDashExtend) {
+      if (phase < 0.5f)
+        phase = 0;
+      else
+        phase -= 0.5f;
+    }
+    CallFunc(GdipSetPenDashOffset)(pPen, phase);
+    FX_Free(pDashArray);
+    pDashArray = nullptr;
+  }
+  CallFunc(GdipSetPenMiterLimit)(pPen, pGraphState->m_MiterLimit);
+  return pPen;
+}
+
+absl::optional<std::pair<size_t, size_t>> IsSmallTriangle(
+    pdfium::span<const Gdiplus::PointF> points,
+    const CFX_Matrix* pMatrix) {
+  static constexpr size_t kPairs[3][2] = {{1, 2}, {0, 2}, {0, 1}};
+  for (size_t i = 0; i < std::size(kPairs); ++i) {
+    size_t pair1 = kPairs[i][0];
+    size_t pair2 = kPairs[i][1];
+
+    CFX_PointF p1(points[pair1].X, points[pair1].Y);
+    CFX_PointF p2(points[pair2].X, points[pair2].Y);
+    if (pMatrix) {
+      p1 = pMatrix->Transform(p1);
+      p2 = pMatrix->Transform(p2);
+    }
+
+    CFX_PointF diff = p1 - p2;
+    float distance_square = (diff.x * diff.x) + (diff.y * diff.y);
+    if (distance_square < 2.25f)
+      return std::make_pair(i, pair1);
+  }
+  return absl::nullopt;
+}
+
+class GpStream final : public IStream {
+ public:
+  GpStream() = default;
+  ~GpStream() = default;
+
+  // IUnknown
+  HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
+                                           void** ppvObject) override {
+    if (iid == __uuidof(IUnknown) || iid == __uuidof(IStream) ||
+        iid == __uuidof(ISequentialStream)) {
+      *ppvObject = static_cast<IStream*>(this);
+      AddRef();
+      return S_OK;
+    }
+    return E_NOINTERFACE;
+  }
+  ULONG STDMETHODCALLTYPE AddRef() override {
+    return (ULONG)InterlockedIncrement(&m_RefCount);
+  }
+  ULONG STDMETHODCALLTYPE Release() override {
+    ULONG res = (ULONG)InterlockedDecrement(&m_RefCount);
+    if (res == 0) {
+      delete this;
+    }
+    return res;
+  }
+
+  // ISequentialStream
+  HRESULT STDMETHODCALLTYPE Read(void* output,
+                                 ULONG cb,
+                                 ULONG* pcbRead) override {
+    if (pcbRead)
+      *pcbRead = 0;
+
+    if (m_ReadPos >= m_InterStream.tellp())
+      return HRESULT_FROM_WIN32(ERROR_END_OF_MEDIA);
+
+    size_t bytes_left = pdfium::base::checked_cast<size_t>(
+        std::streamoff(m_InterStream.tellp()) - m_ReadPos);
+    size_t bytes_out =
+        std::min(pdfium::base::checked_cast<size_t>(cb), bytes_left);
+    memcpy(output, m_InterStream.str().c_str() + m_ReadPos, bytes_out);
+    m_ReadPos += bytes_out;
+    if (pcbRead)
+      *pcbRead = (ULONG)bytes_out;
+
+    return S_OK;
+  }
+  HRESULT STDMETHODCALLTYPE Write(const void* input,
+                                  ULONG cb,
+                                  ULONG* pcbWritten) override {
+    if (cb <= 0) {
+      if (pcbWritten)
+        *pcbWritten = 0;
+      return S_OK;
+    }
+    m_InterStream.write(reinterpret_cast<const char*>(input), cb);
+    if (pcbWritten)
+      *pcbWritten = cb;
+    return S_OK;
+  }
+
+  // IStream
+  HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER) override {
+    return E_NOTIMPL;
+  }
+  HRESULT STDMETHODCALLTYPE CopyTo(IStream*,
+                                   ULARGE_INTEGER,
+                                   ULARGE_INTEGER*,
+                                   ULARGE_INTEGER*) override {
+    return E_NOTIMPL;
+  }
+  HRESULT STDMETHODCALLTYPE Commit(DWORD) override { return E_NOTIMPL; }
+  HRESULT STDMETHODCALLTYPE Revert() override { return E_NOTIMPL; }
+  HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER,
+                                       ULARGE_INTEGER,
+                                       DWORD) override {
+    return E_NOTIMPL;
+  }
+  HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER,
+                                         ULARGE_INTEGER,
+                                         DWORD) override {
+    return E_NOTIMPL;
+  }
+  HRESULT STDMETHODCALLTYPE Clone(IStream** stream) override {
+    return E_NOTIMPL;
+  }
+  HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove,
+                                 DWORD dwOrigin,
+                                 ULARGE_INTEGER* lpNewFilePointer) override {
+    std::streamoff start;
+    std::streamoff new_read_position;
+    switch (dwOrigin) {
+      case STREAM_SEEK_SET:
+        start = 0;
+        break;
+      case STREAM_SEEK_CUR:
+        start = m_ReadPos;
+        break;
+      case STREAM_SEEK_END:
+        if (m_InterStream.tellp() < 0)
+          return STG_E_SEEKERROR;
+        start = m_InterStream.tellp();
+        break;
+      default:
+        return STG_E_INVALIDFUNCTION;
+    }
+    new_read_position = start + liDistanceToMove.QuadPart;
+    if (new_read_position > m_InterStream.tellp())
+      return STG_E_SEEKERROR;
+
+    m_ReadPos = new_read_position;
+    if (lpNewFilePointer)
+      lpNewFilePointer->QuadPart = m_ReadPos;
+
+    return S_OK;
+  }
+  HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg,
+                                 DWORD grfStatFlag) override {
+    if (!pStatstg)
+      return STG_E_INVALIDFUNCTION;
+
+    ZeroMemory(pStatstg, sizeof(STATSTG));
+
+    if (m_InterStream.tellp() < 0)
+      return STG_E_SEEKERROR;
+
+    pStatstg->cbSize.QuadPart = m_InterStream.tellp();
+    return S_OK;
+  }
+
+ private:
+  LONG m_RefCount = 1;
+  std::streamoff m_ReadPos = 0;
+  fxcrt::ostringstream m_InterStream;
+};
+
+}  // namespace
+
+CGdiplusExt::CGdiplusExt() = default;
+
+CGdiplusExt::~CGdiplusExt() {
+  FreeLibrary(m_GdiModule);
+  FreeLibrary(m_hModule);
+}
+
+void CGdiplusExt::Load() {
+  char buf[MAX_PATH];
+  GetSystemDirectoryA(buf, MAX_PATH);
+  ByteString dllpath = buf;
+  dllpath += "\\GDIPLUS.DLL";
+  m_hModule = LoadLibraryA(dllpath.c_str());
+  if (!m_hModule)
+    return;
+
+  m_Functions.resize(std::size(g_GdipFuncNames));
+  for (size_t i = 0; i < std::size(g_GdipFuncNames); ++i) {
+    m_Functions[i] = GetProcAddress(m_hModule, g_GdipFuncNames[i]);
+    if (!m_Functions[i]) {
+      m_hModule = nullptr;
+      return;
+    }
+  }
+
+  ULONG_PTR gdiplus_token;
+  Gdiplus::GdiplusStartupInput gdiplus_startup_input;
+  ((FuncType_GdiplusStartup)m_Functions[FuncId_GdiplusStartup])(
+      &gdiplus_token, &gdiplus_startup_input, nullptr);
+  m_GdiModule = LoadLibraryA("GDI32.DLL");
+}
+
+bool CGdiplusExt::StretchDIBits(HDC hDC,
+                                const RetainPtr<CFX_DIBBase>& source,
+                                int dest_left,
+                                int dest_top,
+                                int dest_width,
+                                int dest_height,
+                                const FX_RECT* pClipRect,
+                                const FXDIB_ResampleOptions& options) {
+  Gdiplus::GpGraphics* pGraphics;
+  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
+  CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
+  CallFunc(GdipSetPageUnit)(pGraphics, Gdiplus::UnitPixel);
+  if (options.bNoSmoothing) {
+    CallFunc(GdipSetInterpolationMode)(
+        pGraphics, Gdiplus::InterpolationModeNearestNeighbor);
+  } 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, source->GetWidth(), source->GetHeight());
+  OutputImage(pGraphics, source, src_rect, dest_left, dest_top, dest_width,
+              dest_height);
+  CallFunc(GdipDeleteGraphics)(pGraphics);
+  CallFunc(GdipDeleteGraphics)(pGraphics);
+  return true;
+}
+
+bool CGdiplusExt::DrawPath(HDC hDC,
+                           const CFX_Path& path,
+                           const CFX_Matrix* pObject2Device,
+                           const CFX_GraphStateData* pGraphState,
+                           uint32_t fill_argb,
+                           uint32_t stroke_argb,
+                           const CFX_FillRenderOptions& fill_options) {
+  pdfium::span<const CFX_Path::Point> points = path.GetPoints();
+  if (points.empty())
+    return true;
+
+  Gdiplus::GpGraphics* pGraphics = nullptr;
+  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
+  CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
+  CallFunc(GdipSetPageUnit)(pGraphics, Gdiplus::UnitPixel);
+  CallFunc(GdipSetPixelOffsetMode)(pGraphics, Gdiplus::PixelOffsetModeHalf);
+  Gdiplus::GpMatrix* pMatrix = nullptr;
+  if (pObject2Device) {
+    CallFunc(GdipCreateMatrix2)(pObject2Device->a, pObject2Device->b,
+                                pObject2Device->c, pObject2Device->d,
+                                pObject2Device->e, pObject2Device->f, &pMatrix);
+    CallFunc(GdipSetWorldTransform)(pGraphics, pMatrix);
+  }
+  std::vector<Gdiplus::PointF> gp_points(points.size());
+  std::vector<BYTE> gp_types(points.size());
+  int nSubPathes = 0;
+  bool bSubClose = false;
+  bool bSmooth = false;
+  size_t pos_subclose = 0;
+  size_t startpoint = 0;
+  for (size_t i = 0; i < points.size(); ++i) {
+    gp_points[i].X = points[i].m_Point.x;
+    gp_points[i].Y = points[i].m_Point.y;
+
+    CFX_PointF pos = points[i].m_Point;
+    if (pObject2Device)
+      pos = pObject2Device->Transform(pos);
+
+    if (pos.x > 50000.0f)
+      gp_points[i].X = 50000.0f;
+    if (pos.x < -50000.0f)
+      gp_points[i].X = -50000.0f;
+    if (pos.y > 50000.0f)
+      gp_points[i].Y = 50000.0f;
+    if (pos.y < -50000.0f)
+      gp_points[i].Y = -50000.0f;
+
+    CFX_Path::Point::Type point_type = points[i].m_Type;
+    if (point_type == CFX_Path::Point::Type::kMove) {
+      gp_types[i] = Gdiplus::PathPointTypeStart;
+      nSubPathes++;
+      bSubClose = false;
+      startpoint = i;
+    } else if (point_type == CFX_Path::Point::Type::kLine) {
+      gp_types[i] = Gdiplus::PathPointTypeLine;
+      if (points[i - 1].IsTypeAndOpen(CFX_Path::Point::Type::kMove) &&
+          (i == points.size() - 1 ||
+           points[i + 1].IsTypeAndOpen(CFX_Path::Point::Type::kMove)) &&
+          gp_points[i].Y == gp_points[i - 1].Y &&
+          gp_points[i].X == gp_points[i - 1].X) {
+        gp_points[i].X += 0.01f;
+        continue;
+      }
+      if (!bSmooth && gp_points[i].X != gp_points[i - 1].X &&
+          gp_points[i].Y != gp_points[i - 1].Y) {
+        bSmooth = true;
+      }
+    } else if (point_type == CFX_Path::Point::Type::kBezier) {
+      gp_types[i] = Gdiplus::PathPointTypeBezier;
+      bSmooth = true;
+    }
+    if (points[i].m_CloseFigure) {
+      if (bSubClose)
+        gp_types[pos_subclose] &= ~Gdiplus::PathPointTypeCloseSubpath;
+      else
+        bSubClose = true;
+      pos_subclose = i;
+      gp_types[i] |= Gdiplus::PathPointTypeCloseSubpath;
+      if (!bSmooth && gp_points[i].X != gp_points[startpoint].X &&
+          gp_points[i].Y != gp_points[startpoint].Y) {
+        bSmooth = true;
+      }
+    }
+  }
+  const bool fill =
+      fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill;
+  if (fill_options.aliased_path) {
+    bSmooth = false;
+    CallFunc(GdipSetSmoothingMode)(pGraphics, Gdiplus::SmoothingModeNone);
+  } else if (!fill_options.full_cover) {
+    if (!bSmooth && fill)
+      bSmooth = true;
+
+    if (bSmooth || (pGraphState && pGraphState->m_LineWidth > 2)) {
+      CallFunc(GdipSetSmoothingMode)(pGraphics,
+                                     Gdiplus::SmoothingModeAntiAlias);
+    }
+  }
+  if (points.size() == 4 && !pGraphState) {
+    auto indices = IsSmallTriangle(gp_points, pObject2Device);
+    if (indices.has_value()) {
+      size_t v1;
+      size_t v2;
+      std::tie(v1, v2) = indices.value();
+      Gdiplus::GpPen* pPen = nullptr;
+      CallFunc(GdipCreatePen1)(fill_argb, 1.0f, Gdiplus::UnitPixel, &pPen);
+      CallFunc(GdipDrawLineI)(pGraphics, pPen, FXSYS_roundf(gp_points[v1].X),
+                              FXSYS_roundf(gp_points[v1].Y),
+                              FXSYS_roundf(gp_points[v2].X),
+                              FXSYS_roundf(gp_points[v2].Y));
+      CallFunc(GdipDeletePen)(pPen);
+      return true;
+    }
+  }
+  Gdiplus::GpPath* pGpPath = nullptr;
+  const Gdiplus::GpFillMode gp_fill_mode =
+      FillType2Gdip(fill_options.fill_type);
+  CallFunc(GdipCreatePath2)(gp_points.data(), gp_types.data(),
+                            pdfium::base::checked_cast<int>(points.size()),
+                            gp_fill_mode, &pGpPath);
+  if (!pGpPath) {
+    if (pMatrix)
+      CallFunc(GdipDeleteMatrix)(pMatrix);
+
+    CallFunc(GdipDeleteGraphics)(pGraphics);
+    return false;
+  }
+  if (fill) {
+    Gdiplus::GpBrush* pBrush = GdipCreateBrushImpl(fill_argb);
+    CallFunc(GdipSetPathFillMode)(pGpPath, gp_fill_mode);
+    CallFunc(GdipFillPath)(pGraphics, pBrush, pGpPath);
+    CallFunc(GdipDeleteBrush)(pBrush);
+  }
+  if (pGraphState && stroke_argb) {
+    Gdiplus::GpPen* pPen =
+        GdipCreatePenImpl(pGraphState, pObject2Device, stroke_argb,
+                          fill_options.stroke_text_mode);
+    if (nSubPathes == 1) {
+      CallFunc(GdipDrawPath)(pGraphics, pPen, pGpPath);
+    } else {
+      size_t iStart = 0;
+      for (size_t i = 0; i < points.size(); ++i) {
+        if (i == points.size() - 1 ||
+            gp_types[i + 1] == Gdiplus::PathPointTypeStart) {
+          Gdiplus::GpPath* pSubPath;
+          CallFunc(GdipCreatePath2)(
+              &gp_points[iStart], &gp_types[iStart],
+              pdfium::base::checked_cast<int>(i - iStart + 1), gp_fill_mode,
+              &pSubPath);
+          iStart = i + 1;
+          CallFunc(GdipDrawPath)(pGraphics, pPen, pSubPath);
+          CallFunc(GdipDeletePath)(pSubPath);
+        }
+      }
+    }
+    CallFunc(GdipDeletePen)(pPen);
+  }
+  if (pMatrix)
+    CallFunc(GdipDeleteMatrix)(pMatrix);
+  CallFunc(GdipDeletePath)(pGpPath);
+  CallFunc(GdipDeleteGraphics)(pGraphics);
+  return true;
+}
diff --git a/core/fxge/win32/cgdi_plus_ext.h b/core/fxge/win32/cgdi_plus_ext.h
new file mode 100644
index 0000000..ced36c9
--- /dev/null
+++ b/core/fxge/win32/cgdi_plus_ext.h
@@ -0,0 +1,55 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_WIN32_CGDI_PLUS_EXT_H_
+#define CORE_FXGE_WIN32_CGDI_PLUS_EXT_H_
+
+#include <stdint.h>
+#include <windows.h>
+
+#include <vector>
+
+#include "core/fxcrt/retain_ptr.h"
+
+class CFX_DIBBase;
+class CFX_GraphStateData;
+class CFX_Matrix;
+class CFX_Path;
+struct CFX_FillRenderOptions;
+struct FXDIB_ResampleOptions;
+struct FX_RECT;
+
+class CGdiplusExt {
+ public:
+  CGdiplusExt();
+  ~CGdiplusExt();
+
+  void Load();
+  bool IsAvailable() { return !!m_hModule; }
+  bool StretchDIBits(HDC hDC,
+                     const RetainPtr<CFX_DIBBase>& source,
+                     int dest_left,
+                     int dest_top,
+                     int dest_width,
+                     int dest_height,
+                     const FX_RECT* pClipRect,
+                     const FXDIB_ResampleOptions& options);
+  bool DrawPath(HDC hDC,
+                const CFX_Path& path,
+                const CFX_Matrix* pObject2Device,
+                const CFX_GraphStateData* pGraphState,
+                uint32_t fill_argb,
+                uint32_t stroke_argb,
+                const CFX_FillRenderOptions& fill_options);
+
+  std::vector<FARPROC> m_Functions;
+
+ private:
+  HMODULE m_hModule = nullptr;
+  HMODULE m_GdiModule = nullptr;
+};
+
+#endif  // CORE_FXGE_WIN32_CGDI_PLUS_EXT_H_
diff --git a/core/fxge/win32/cgdi_printer_driver.cpp b/core/fxge/win32/cgdi_printer_driver.cpp
new file mode 100644
index 0000000..28bc516
--- /dev/null
+++ b/core/fxge/win32/cgdi_printer_driver.cpp
@@ -0,0 +1,165 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxge/win32/cgdi_printer_driver.h"
+
+#include <math.h>
+#include <windows.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/cfx_font.h"
+#include "core/fxge/cfx_windowsrenderdevice.h"
+#include "core/fxge/dib/cfx_dibbase.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/render_defines.h"
+#include "core/fxge/text_char_pos.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+
+CGdiPrinterDriver::CGdiPrinterDriver(HDC hDC)
+    : CGdiDeviceDriver(hDC, DeviceType::kPrinter),
+      m_HorzSize(::GetDeviceCaps(m_hDC, HORZSIZE)),
+      m_VertSize(::GetDeviceCaps(m_hDC, VERTSIZE)) {}
+
+CGdiPrinterDriver::~CGdiPrinterDriver() = default;
+
+int CGdiPrinterDriver::GetDeviceCaps(int caps_id) const {
+  if (caps_id == FXDC_HORZ_SIZE)
+    return m_HorzSize;
+  if (caps_id == FXDC_VERT_SIZE)
+    return m_VertSize;
+  return CGdiDeviceDriver::GetDeviceCaps(caps_id);
+}
+
+bool CGdiPrinterDriver::SetDIBits(const RetainPtr<CFX_DIBBase>& pSource,
+                                  uint32_t color,
+                                  const FX_RECT& src_rect,
+                                  int left,
+                                  int top,
+                                  BlendMode blend_type) {
+  if (pSource->IsMaskFormat()) {
+    FX_RECT clip_rect(left, top, left + src_rect.Width(),
+                      top + src_rect.Height());
+    return StretchDIBits(pSource, color, left - src_rect.left,
+                         top - src_rect.top, pSource->GetWidth(),
+                         pSource->GetHeight(), &clip_rect,
+                         FXDIB_ResampleOptions(), BlendMode::kNormal);
+  }
+  DCHECK(pSource);
+  DCHECK(!pSource->IsMaskFormat());
+  DCHECK_EQ(blend_type, BlendMode::kNormal);
+  if (pSource->IsAlphaFormat())
+    return false;
+
+  return GDI_SetDIBits(pSource, src_rect, left, top);
+}
+
+bool CGdiPrinterDriver::StretchDIBits(const RetainPtr<CFX_DIBBase>& pSource,
+                                      uint32_t color,
+                                      int dest_left,
+                                      int dest_top,
+                                      int dest_width,
+                                      int dest_height,
+                                      const FX_RECT* pClipRect,
+                                      const FXDIB_ResampleOptions& options,
+                                      BlendMode blend_type) {
+  if (pSource->IsMaskFormat()) {
+    int alpha = FXARGB_A(color);
+    if (pSource->GetBPP() != 1 || alpha != 255)
+      return false;
+
+    if (dest_width < 0 || dest_height < 0) {
+      RetainPtr<CFX_DIBBase> pFlipped =
+          pSource->FlipImage(dest_width < 0, dest_height < 0);
+      if (!pFlipped)
+        return false;
+
+      if (dest_width < 0)
+        dest_left += dest_width;
+      if (dest_height < 0)
+        dest_top += dest_height;
+
+      return GDI_StretchBitMask(pFlipped, dest_left, dest_top, abs(dest_width),
+                                abs(dest_height), color);
+    }
+
+    return GDI_StretchBitMask(pSource, dest_left, dest_top, dest_width,
+                              dest_height, color);
+  }
+
+  if (pSource->IsAlphaFormat())
+    return false;
+
+  if (dest_width < 0 || dest_height < 0) {
+    RetainPtr<CFX_DIBBase> pFlipped =
+        pSource->FlipImage(dest_width < 0, dest_height < 0);
+    if (!pFlipped)
+      return false;
+
+    if (dest_width < 0)
+      dest_left += dest_width;
+    if (dest_height < 0)
+      dest_top += dest_height;
+
+    return GDI_StretchDIBits(pFlipped, dest_left, dest_top, abs(dest_width),
+                             abs(dest_height), options);
+  }
+
+  return GDI_StretchDIBits(pSource, dest_left, dest_top, dest_width,
+                           dest_height, options);
+}
+
+bool CGdiPrinterDriver::StartDIBits(const RetainPtr<CFX_DIBBase>& pSource,
+                                    int bitmap_alpha,
+                                    uint32_t color,
+                                    const CFX_Matrix& matrix,
+                                    const FXDIB_ResampleOptions& options,
+                                    std::unique_ptr<CFX_ImageRenderer>* handle,
+                                    BlendMode blend_type) {
+  if (bitmap_alpha < 255 || pSource->IsAlphaFormat() ||
+      (pSource->IsMaskFormat() && (pSource->GetBPP() != 1))) {
+    return false;
+  }
+  CFX_FloatRect unit_rect = matrix.GetUnitRect();
+  FX_RECT full_rect = unit_rect.GetOuterRect();
+  if (fabs(matrix.b) < 0.5f && matrix.a != 0 && fabs(matrix.c) < 0.5f &&
+      matrix.d != 0) {
+    bool bFlipX = matrix.a < 0;
+    bool bFlipY = matrix.d > 0;
+    return StretchDIBits(pSource, color,
+                         bFlipX ? full_rect.right : full_rect.left,
+                         bFlipY ? full_rect.bottom : full_rect.top,
+                         bFlipX ? -full_rect.Width() : full_rect.Width(),
+                         bFlipY ? -full_rect.Height() : full_rect.Height(),
+                         nullptr, FXDIB_ResampleOptions(), blend_type);
+  }
+  if (fabs(matrix.a) >= 0.5f || fabs(matrix.d) >= 0.5f)
+    return false;
+
+  RetainPtr<CFX_DIBBase> pTransformed =
+      pSource->SwapXY(matrix.c > 0, matrix.b < 0);
+  if (!pTransformed)
+    return false;
+
+  return StretchDIBits(pTransformed, color, full_rect.left, full_rect.top,
+                       full_rect.Width(), full_rect.Height(), nullptr,
+                       FXDIB_ResampleOptions(), blend_type);
+}
+
+bool CGdiPrinterDriver::DrawDeviceText(
+    pdfium::span<const TextCharPos> pCharPos,
+    CFX_Font* pFont,
+    const CFX_Matrix& mtObject2Device,
+    float font_size,
+    uint32_t color,
+    const CFX_TextRenderOptions& /*options*/) {
+  return false;
+}
diff --git a/core/fxge/win32/cgdi_printer_driver.h b/core/fxge/win32/cgdi_printer_driver.h
new file mode 100644
index 0000000..5c4cfc0
--- /dev/null
+++ b/core/fxge/win32/cgdi_printer_driver.h
@@ -0,0 +1,57 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_WIN32_CGDI_PRINTER_DRIVER_H_
+#define CORE_FXGE_WIN32_CGDI_PRINTER_DRIVER_H_
+
+#include <windows.h>
+
+#include <memory>
+
+#include "core/fxge/win32/cgdi_device_driver.h"
+
+class CGdiPrinterDriver final : public CGdiDeviceDriver {
+ public:
+  explicit CGdiPrinterDriver(HDC hDC);
+  ~CGdiPrinterDriver() override;
+
+ private:
+  // CGdiPrinterDriver:
+  int GetDeviceCaps(int caps_id) const override;
+  bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                 uint32_t color,
+                 const FX_RECT& src_rect,
+                 int left,
+                 int top,
+                 BlendMode blend_type) override;
+  bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                     uint32_t color,
+                     int dest_left,
+                     int dest_top,
+                     int dest_width,
+                     int dest_height,
+                     const FX_RECT* pClipRect,
+                     const FXDIB_ResampleOptions& options,
+                     BlendMode blend_type) override;
+  bool StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                   int bitmap_alpha,
+                   uint32_t color,
+                   const CFX_Matrix& matrix,
+                   const FXDIB_ResampleOptions& options,
+                   std::unique_ptr<CFX_ImageRenderer>* handle,
+                   BlendMode blend_type) override;
+  bool DrawDeviceText(pdfium::span<const TextCharPos> pCharPos,
+                      CFX_Font* pFont,
+                      const CFX_Matrix& mtObject2Device,
+                      float font_size,
+                      uint32_t color,
+                      const CFX_TextRenderOptions& options) override;
+
+  const int m_HorzSize;
+  const int m_VertSize;
+};
+
+#endif  // CORE_FXGE_WIN32_CGDI_PRINTER_DRIVER_H_
diff --git a/core/fxge/win32/cps_printer_driver.cpp b/core/fxge/win32/cps_printer_driver.cpp
new file mode 100644
index 0000000..f30b80d
--- /dev/null
+++ b/core/fxge/win32/cps_printer_driver.cpp
@@ -0,0 +1,226 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxge/win32/cps_printer_driver.h"
+
+#include <stdint.h>
+
+#include <sstream>
+
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "core/fxge/cfx_path.h"
+#include "core/fxge/dib/cfx_imagerenderer.h"
+#include "core/fxge/win32/cpsoutput.h"
+#include "third_party/base/check.h"
+#include "third_party/base/notreached.h"
+
+namespace {
+
+CFX_PSRenderer::RenderingLevel RenderingLevelFromWindowsPrintMode(
+    WindowsPrintMode mode) {
+  switch (mode) {
+    case WindowsPrintMode::kPostScript2:
+    case WindowsPrintMode::kPostScript2PassThrough:
+      return CFX_PSRenderer::RenderingLevel::kLevel2;
+    case WindowsPrintMode::kPostScript3:
+    case WindowsPrintMode::kPostScript3PassThrough:
+      return CFX_PSRenderer::RenderingLevel::kLevel3;
+    case WindowsPrintMode::kPostScript3Type42:
+    case WindowsPrintMode::kPostScript3Type42PassThrough:
+      return CFX_PSRenderer::RenderingLevel::kLevel3Type42;
+    default:
+      // |mode| should be PostScript.
+      NOTREACHED();
+      return CFX_PSRenderer::RenderingLevel::kLevel2;
+  }
+}
+
+}  // namespace
+
+CPSPrinterDriver::CPSPrinterDriver(HDC hDC,
+                                   WindowsPrintMode mode,
+                                   CFX_PSFontTracker* ps_font_tracker,
+                                   const EncoderIface* encoder_iface)
+    : m_hDC(hDC), m_PSRenderer(ps_font_tracker, encoder_iface) {
+  CFX_PSRenderer::RenderingLevel level =
+      RenderingLevelFromWindowsPrintMode(mode);
+  CPSOutput::OutputMode output_mode =
+      (mode == WindowsPrintMode::kPostScript2 ||
+       mode == WindowsPrintMode::kPostScript3 ||
+       mode == WindowsPrintMode::kPostScript3Type42)
+          ? CPSOutput::OutputMode::kGdiComment
+          : CPSOutput::OutputMode::kExtEscape;
+
+  m_HorzSize = ::GetDeviceCaps(m_hDC, HORZSIZE);
+  m_VertSize = ::GetDeviceCaps(m_hDC, VERTSIZE);
+  m_Width = ::GetDeviceCaps(m_hDC, HORZRES);
+  m_Height = ::GetDeviceCaps(m_hDC, VERTRES);
+  m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL);
+
+  m_PSRenderer.Init(pdfium::MakeRetain<CPSOutput>(m_hDC, output_mode), level,
+                    m_Width, m_Height);
+  HRGN hRgn = ::CreateRectRgn(0, 0, 1, 1);
+  if (::GetClipRgn(m_hDC, hRgn) == 1) {
+    DWORD dwCount = ::GetRegionData(hRgn, 0, nullptr);
+    if (dwCount) {
+      DataVector<uint8_t> buffer(dwCount);
+      RGNDATA* pData = reinterpret_cast<RGNDATA*>(buffer.data());
+      if (::GetRegionData(hRgn, dwCount, pData)) {
+        CFX_Path path;
+        for (uint32_t i = 0; i < pData->rdh.nCount; i++) {
+          RECT* pRect =
+              reinterpret_cast<RECT*>(pData->Buffer + pData->rdh.nRgnSize * i);
+          path.AppendRect(static_cast<float>(pRect->left),
+                          static_cast<float>(pRect->bottom),
+                          static_cast<float>(pRect->right),
+                          static_cast<float>(pRect->top));
+        }
+        m_PSRenderer.SetClip_PathFill(path, nullptr,
+                                      CFX_FillRenderOptions::WindingOptions());
+      }
+    }
+  }
+  ::DeleteObject(hRgn);
+}
+
+CPSPrinterDriver::~CPSPrinterDriver() = default;
+
+DeviceType CPSPrinterDriver::GetDeviceType() const {
+  return DeviceType::kPrinter;
+}
+
+int CPSPrinterDriver::GetDeviceCaps(int caps_id) const {
+  switch (caps_id) {
+    case FXDC_PIXEL_WIDTH:
+      return m_Width;
+    case FXDC_PIXEL_HEIGHT:
+      return m_Height;
+    case FXDC_BITS_PIXEL:
+      return m_nBitsPerPixel;
+    case FXDC_RENDER_CAPS:
+      return FXRC_BIT_MASK;
+    case FXDC_HORZ_SIZE:
+      return m_HorzSize;
+    case FXDC_VERT_SIZE:
+      return m_VertSize;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
+void CPSPrinterDriver::SaveState() {
+  m_PSRenderer.SaveState();
+}
+
+void CPSPrinterDriver::RestoreState(bool bKeepSaved) {
+  m_PSRenderer.RestoreState(bKeepSaved);
+}
+
+bool CPSPrinterDriver::SetClip_PathFill(
+    const CFX_Path& path,
+    const CFX_Matrix* pObject2Device,
+    const CFX_FillRenderOptions& fill_options) {
+  m_PSRenderer.SetClip_PathFill(path, pObject2Device, fill_options);
+  return true;
+}
+
+bool CPSPrinterDriver::SetClip_PathStroke(
+    const CFX_Path& path,
+    const CFX_Matrix* pObject2Device,
+    const CFX_GraphStateData* pGraphState) {
+  m_PSRenderer.SetClip_PathStroke(path, pObject2Device, pGraphState);
+  return true;
+}
+
+bool CPSPrinterDriver::DrawPath(const CFX_Path& path,
+                                const CFX_Matrix* pObject2Device,
+                                const CFX_GraphStateData* pGraphState,
+                                FX_ARGB fill_color,
+                                FX_ARGB stroke_color,
+                                const CFX_FillRenderOptions& fill_options,
+                                BlendMode blend_type) {
+  if (blend_type != BlendMode::kNormal)
+    return false;
+  return m_PSRenderer.DrawPath(path, pObject2Device, pGraphState, fill_color,
+                               stroke_color, fill_options);
+}
+
+bool CPSPrinterDriver::GetClipBox(FX_RECT* pRect) {
+  *pRect = m_PSRenderer.GetClipBox();
+  return true;
+}
+
+bool CPSPrinterDriver::SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                                 uint32_t color,
+                                 const FX_RECT& src_rect,
+                                 int left,
+                                 int top,
+                                 BlendMode blend_type) {
+  if (blend_type != BlendMode::kNormal)
+    return false;
+  return m_PSRenderer.SetDIBits(pBitmap, color, left, top);
+}
+
+bool CPSPrinterDriver::StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                                     uint32_t color,
+                                     int dest_left,
+                                     int dest_top,
+                                     int dest_width,
+                                     int dest_height,
+                                     const FX_RECT* pClipRect,
+                                     const FXDIB_ResampleOptions& options,
+                                     BlendMode blend_type) {
+  if (blend_type != BlendMode::kNormal)
+    return false;
+  return m_PSRenderer.StretchDIBits(pBitmap, color, dest_left, dest_top,
+                                    dest_width, dest_height, options);
+}
+
+bool CPSPrinterDriver::StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                                   int bitmap_alpha,
+                                   uint32_t color,
+                                   const CFX_Matrix& matrix,
+                                   const FXDIB_ResampleOptions& options,
+                                   std::unique_ptr<CFX_ImageRenderer>* handle,
+                                   BlendMode blend_type) {
+  if (blend_type != BlendMode::kNormal)
+    return false;
+
+  if (bitmap_alpha < 255)
+    return false;
+
+  *handle = nullptr;
+  return m_PSRenderer.DrawDIBits(pBitmap, color, matrix, options);
+}
+
+bool CPSPrinterDriver::DrawDeviceText(
+    pdfium::span<const TextCharPos> pCharPos,
+    CFX_Font* pFont,
+    const CFX_Matrix& mtObject2Device,
+    float font_size,
+    uint32_t color,
+    const CFX_TextRenderOptions& /*options*/) {
+  return m_PSRenderer.DrawText(pCharPos.size(), pCharPos.data(), pFont,
+                               mtObject2Device, font_size, color);
+}
+
+bool CPSPrinterDriver::MultiplyAlpha(float alpha) {
+  // PostScript doesn't support transparency. All callers are using
+  // `CFX_DIBitmap`-backed raster devices anyway.
+  NOTREACHED();
+  return false;
+}
+
+bool CPSPrinterDriver::MultiplyAlpha(const RetainPtr<CFX_DIBBase>& mask) {
+  // PostScript doesn't support transparency. All callers are using
+  // `CFX_DIBitmap`-backed raster devices anyway.
+  NOTREACHED();
+  return false;
+}
diff --git a/core/fxge/win32/cps_printer_driver.h b/core/fxge/win32/cps_printer_driver.h
new file mode 100644
index 0000000..4e28497
--- /dev/null
+++ b/core/fxge/win32/cps_printer_driver.h
@@ -0,0 +1,88 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_WIN32_CPS_PRINTER_DRIVER_H_
+#define CORE_FXGE_WIN32_CPS_PRINTER_DRIVER_H_
+
+#include <windows.h>
+
+#include <memory>
+
+#include "core/fxge/cfx_windowsrenderdevice.h"
+#include "core/fxge/renderdevicedriver_iface.h"
+#include "core/fxge/win32/cfx_psrenderer.h"
+
+class CFX_PSFontTracker;
+
+class CPSPrinterDriver final : public RenderDeviceDriverIface {
+ public:
+  CPSPrinterDriver(HDC hDC,
+                   WindowsPrintMode mode,
+                   CFX_PSFontTracker* ps_font_tracker,
+                   const EncoderIface* encoder_iface);
+  ~CPSPrinterDriver() override;
+
+ private:
+  // RenderDeviceDriverIface:
+  DeviceType GetDeviceType() const override;
+  int GetDeviceCaps(int caps_id) const override;
+  void SaveState() override;
+  void RestoreState(bool bKeepSaved) override;
+  bool SetClip_PathFill(const CFX_Path& paath,
+                        const CFX_Matrix* pObject2Device,
+                        const CFX_FillRenderOptions& fill_options) override;
+  bool SetClip_PathStroke(const CFX_Path& path,
+                          const CFX_Matrix* pObject2Device,
+                          const CFX_GraphStateData* pGraphState) override;
+  bool DrawPath(const CFX_Path& path,
+                const CFX_Matrix* pObject2Device,
+                const CFX_GraphStateData* pGraphState,
+                uint32_t fill_color,
+                uint32_t stroke_color,
+                const CFX_FillRenderOptions& fill_options,
+                BlendMode blend_type) override;
+  bool GetClipBox(FX_RECT* pRect) override;
+  bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                 uint32_t color,
+                 const FX_RECT& src_rect,
+                 int left,
+                 int top,
+                 BlendMode blend_type) override;
+  bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                     uint32_t color,
+                     int dest_left,
+                     int dest_top,
+                     int dest_width,
+                     int dest_height,
+                     const FX_RECT* pClipRect,
+                     const FXDIB_ResampleOptions& options,
+                     BlendMode blend_type) override;
+  bool StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                   int bitmap_alpha,
+                   uint32_t color,
+                   const CFX_Matrix& matrix,
+                   const FXDIB_ResampleOptions& options,
+                   std::unique_ptr<CFX_ImageRenderer>* handle,
+                   BlendMode blend_type) override;
+  bool DrawDeviceText(pdfium::span<const TextCharPos> pCharPos,
+                      CFX_Font* pFont,
+                      const CFX_Matrix& mtObject2Device,
+                      float font_size,
+                      uint32_t color,
+                      const CFX_TextRenderOptions& options) override;
+  bool MultiplyAlpha(float alpha) override;
+  bool MultiplyAlpha(const RetainPtr<CFX_DIBBase>& mask) override;
+
+  HDC m_hDC;
+  int m_Width;
+  int m_Height;
+  int m_nBitsPerPixel;
+  int m_HorzSize;
+  int m_VertSize;
+  CFX_PSRenderer m_PSRenderer;
+};
+
+#endif  // CORE_FXGE_WIN32_CPS_PRINTER_DRIVER_H_
diff --git a/core/fxge/win32/cpsoutput.cpp b/core/fxge/win32/cpsoutput.cpp
index 4170db1..0b391ee 100644
--- a/core/fxge/win32/cpsoutput.cpp
+++ b/core/fxge/win32/cpsoutput.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,34 +9,28 @@
 #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) {}
 
 CPSOutput::~CPSOutput() = default;
 
-bool CPSOutput::WriteBlock(const void* str, size_t len) {
-  pdfium::span<const uint8_t> input(static_cast<const uint8_t*>(str), len);
+bool CPSOutput::WriteBlock(pdfium::span<const uint8_t> input) {
   while (!input.empty()) {
     uint8_t buffer[1026];
     size_t send_len = std::min<size_t>(input.size(), 1024);
     *(reinterpret_cast<uint16_t*>(buffer)) = static_cast<uint16_t>(send_len);
     memcpy(buffer + 2, input.data(), send_len);
-
     switch (m_mode) {
       case OutputMode::kExtEscape:
-        ExtEscape(m_hDC, PASSTHROUGH, send_len + 2,
+        ExtEscape(m_hDC, PASSTHROUGH, static_cast<int>(send_len + 2),
                   reinterpret_cast<const char*>(buffer), 0, nullptr);
         break;
       case OutputMode::kGdiComment:
-        GdiComment(m_hDC, send_len + 2, buffer);
+        GdiComment(m_hDC, static_cast<UINT>(send_len + 2), buffer);
         break;
     }
     input = input.subspan(send_len);
   }
   return true;
 }
-
-bool CPSOutput::WriteString(ByteStringView str) {
-  return WriteBlock(str.unterminated_c_str(), str.GetLength());
-}
diff --git a/core/fxge/win32/cpsoutput.h b/core/fxge/win32/cpsoutput.h
index 939f4cc..efb93ce 100644
--- a/core/fxge/win32/cpsoutput.h
+++ b/core/fxge/win32/cpsoutput.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,10 @@
 #ifndef CORE_FXGE_WIN32_CPSOUTPUT_H_
 #define CORE_FXGE_WIN32_CPSOUTPUT_H_
 
+#include <stddef.h>
 #include <windows.h>
 
 #include "core/fxcrt/fx_stream.h"
-#include "core/fxcrt/fx_system.h"
 
 class CPSOutput final : public IFX_RetainableWriteStream {
  public:
@@ -19,9 +19,8 @@
   CPSOutput(HDC hDC, OutputMode mode);
   ~CPSOutput() override;
 
-  // IFX_Writestream
-  bool WriteBlock(const void* str, size_t len) override;
-  bool WriteString(ByteStringView str) override;
+  // IFX_Writestream:
+  bool WriteBlock(pdfium::span<const uint8_t> input) override;
 
  private:
   const HDC m_hDC;
diff --git a/core/fxge/win32/ctext_only_printer_driver.cpp b/core/fxge/win32/ctext_only_printer_driver.cpp
new file mode 100644
index 0000000..7689f3f
--- /dev/null
+++ b/core/fxge/win32/ctext_only_printer_driver.cpp
@@ -0,0 +1,194 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/win32/ctext_only_printer_driver.h"
+
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <algorithm>
+
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxge/cfx_font.h"
+#include "core/fxge/text_char_pos.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/notreached.h"
+
+CTextOnlyPrinterDriver::CTextOnlyPrinterDriver(HDC hDC)
+    : m_hDC(hDC),
+      m_Width(INT_MAX),
+      m_Height(INT_MAX),
+      m_HorzSize(INT_MAX),
+      m_VertSize(INT_MAX),
+      m_OriginY(0.0f),
+      m_SetOrigin(false) {
+  m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL);
+}
+
+CTextOnlyPrinterDriver::~CTextOnlyPrinterDriver() = default;
+
+DeviceType CTextOnlyPrinterDriver::GetDeviceType() const {
+  return DeviceType::kPrinter;
+}
+
+int CTextOnlyPrinterDriver::GetDeviceCaps(int caps_id) const {
+  switch (caps_id) {
+    case FXDC_PIXEL_WIDTH:
+      return m_Width;
+    case FXDC_PIXEL_HEIGHT:
+      return m_Height;
+    case FXDC_BITS_PIXEL:
+      return m_nBitsPerPixel;
+    case FXDC_RENDER_CAPS:
+      return 0;
+    case FXDC_HORZ_SIZE:
+      return m_HorzSize;
+    case FXDC_VERT_SIZE:
+      return m_VertSize;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
+void CTextOnlyPrinterDriver::SaveState() {}
+
+void CTextOnlyPrinterDriver::RestoreState(bool bKeepSaved) {}
+
+bool CTextOnlyPrinterDriver::SetClip_PathFill(
+    const CFX_Path& path,
+    const CFX_Matrix* pObject2Device,
+    const CFX_FillRenderOptions& fill_options) {
+  return true;
+}
+
+bool CTextOnlyPrinterDriver::SetClip_PathStroke(
+    const CFX_Path& path,
+    const CFX_Matrix* pObject2Device,
+    const CFX_GraphStateData* pGraphState) {
+  return false;
+}
+
+bool CTextOnlyPrinterDriver::DrawPath(const CFX_Path& path,
+                                      const CFX_Matrix* pObject2Device,
+                                      const CFX_GraphStateData* pGraphState,
+                                      uint32_t fill_color,
+                                      uint32_t stroke_color,
+                                      const CFX_FillRenderOptions& fill_options,
+                                      BlendMode blend_type) {
+  return false;
+}
+
+bool CTextOnlyPrinterDriver::SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                                       uint32_t color,
+                                       const FX_RECT& src_rect,
+                                       int left,
+                                       int top,
+                                       BlendMode blend_type) {
+  return false;
+}
+
+bool CTextOnlyPrinterDriver::GetClipBox(FX_RECT* pRect) {
+  pRect->left = 0;
+  pRect->right = m_Width;
+  pRect->top = 0;
+  pRect->bottom = m_Height;
+  return true;
+}
+
+bool CTextOnlyPrinterDriver::StretchDIBits(
+    const RetainPtr<CFX_DIBBase>& pBitmap,
+    uint32_t color,
+    int dest_left,
+    int dest_top,
+    int dest_width,
+    int dest_height,
+    const FX_RECT* pClipRect,
+    const FXDIB_ResampleOptions& options,
+    BlendMode blend_type) {
+  return false;
+}
+
+bool CTextOnlyPrinterDriver::StartDIBits(
+    const RetainPtr<CFX_DIBBase>& pBitmap,
+    int bitmap_alpha,
+    uint32_t color,
+    const CFX_Matrix& matrix,
+    const FXDIB_ResampleOptions& options,
+    std::unique_ptr<CFX_ImageRenderer>* handle,
+    BlendMode blend_type) {
+  return false;
+}
+
+bool CTextOnlyPrinterDriver::DrawDeviceText(
+    pdfium::span<const TextCharPos> pCharPos,
+    CFX_Font* pFont,
+    const CFX_Matrix& mtObject2Device,
+    float font_size,
+    uint32_t color,
+    const CFX_TextRenderOptions& /*options*/) {
+  if (g_pdfium_print_mode != WindowsPrintMode::kTextOnly)
+    return false;
+  if (pCharPos.empty() || !pFont || !pFont->IsEmbedded() || !pFont->IsTTFont())
+    return false;
+
+  // Scale factor used to minimize the kerning problems caused by rounding
+  // errors below. Value chosen based on the title of https://crbug.com/18383
+  const double kScaleFactor = 10;
+
+  // Detect new lines and add clrf characters (since this is Windows only).
+  // These characters are removed by SkPDF, but the new line information is
+  // preserved in the text location. clrf characters seem to be ignored by
+  // label printers that use this driver.
+  WideString wsText;
+  size_t len = pCharPos.size();
+  float fOffsetY = mtObject2Device.f * kScaleFactor;
+  if (m_SetOrigin && FXSYS_roundf(m_OriginY) != FXSYS_roundf(fOffsetY)) {
+    wsText += L"\r\n";
+    len += 2;
+  }
+  wsText.Reserve(len);
+  m_OriginY = fOffsetY;
+  m_SetOrigin = true;
+
+  // Text
+  for (const auto& charpos : pCharPos) {
+    // Only works with PDFs from Skia's PDF generator. Cannot handle arbitrary
+    // values from PDFs.
+    DCHECK_EQ(charpos.m_AdjustMatrix[0], 0);
+    DCHECK_EQ(charpos.m_AdjustMatrix[1], 0);
+    DCHECK_EQ(charpos.m_AdjustMatrix[2], 0);
+    DCHECK_EQ(charpos.m_AdjustMatrix[3], 0);
+    DCHECK_EQ(charpos.m_Origin.y, 0);
+    wsText += charpos.m_Unicode;
+  }
+  ByteString text = wsText.ToDefANSI();
+  auto text_span = text.span();
+  while (!text_span.empty()) {
+    uint8_t buffer[1026];
+    size_t send_len = std::min<size_t>(text_span.size(), 1024);
+    *(reinterpret_cast<uint16_t*>(buffer)) = static_cast<uint16_t>(send_len);
+    memcpy(buffer + 2, text_span.data(), send_len);
+    ::GdiComment(m_hDC, static_cast<UINT>(send_len + 2), buffer);
+    text_span = text_span.subspan(send_len);
+  }
+  return true;
+}
+
+bool CTextOnlyPrinterDriver::MultiplyAlpha(float alpha) {
+  // Not needed. All callers are using `CFX_DIBitmap`-backed raster devices
+  // anyway.
+  NOTREACHED();
+  return false;
+}
+
+bool CTextOnlyPrinterDriver::MultiplyAlpha(const RetainPtr<CFX_DIBBase>& mask) {
+  // Not needed. All callers are using `CFX_DIBitmap`-backed raster devices
+  // anyway.
+  NOTREACHED();
+  return false;
+}
diff --git a/core/fxge/win32/ctext_only_printer_driver.h b/core/fxge/win32/ctext_only_printer_driver.h
new file mode 100644
index 0000000..e029a7c
--- /dev/null
+++ b/core/fxge/win32/ctext_only_printer_driver.h
@@ -0,0 +1,80 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXGE_WIN32_CTEXT_ONLY_PRINTER_DRIVER_H_
+#define CORE_FXGE_WIN32_CTEXT_ONLY_PRINTER_DRIVER_H_
+
+#include <windows.h>
+
+#include <memory>
+
+#include "core/fxge/cfx_windowsrenderdevice.h"
+
+class CTextOnlyPrinterDriver final : public RenderDeviceDriverIface {
+ public:
+  explicit CTextOnlyPrinterDriver(HDC hDC);
+  ~CTextOnlyPrinterDriver() override;
+
+ private:
+  // RenderDeviceDriverIface:
+  DeviceType GetDeviceType() const override;
+  int GetDeviceCaps(int caps_id) const override;
+  void SaveState() override;
+  void RestoreState(bool bKeepSaved) override;
+  bool SetClip_PathFill(const CFX_Path& path,
+                        const CFX_Matrix* pObject2Device,
+                        const CFX_FillRenderOptions& fill_options) override;
+  bool SetClip_PathStroke(const CFX_Path& path,
+                          const CFX_Matrix* pObject2Device,
+                          const CFX_GraphStateData* pGraphState) override;
+  bool DrawPath(const CFX_Path& path,
+                const CFX_Matrix* pObject2Device,
+                const CFX_GraphStateData* pGraphState,
+                uint32_t fill_color,
+                uint32_t stroke_color,
+                const CFX_FillRenderOptions& fill_options,
+                BlendMode blend_type) override;
+  bool GetClipBox(FX_RECT* pRect) override;
+  bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                 uint32_t color,
+                 const FX_RECT& src_rect,
+                 int left,
+                 int top,
+                 BlendMode blend_type) override;
+  bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                     uint32_t color,
+                     int dest_left,
+                     int dest_top,
+                     int dest_width,
+                     int dest_height,
+                     const FX_RECT* pClipRect,
+                     const FXDIB_ResampleOptions& options,
+                     BlendMode blend_type) override;
+  bool StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                   int bitmap_alpha,
+                   uint32_t color,
+                   const CFX_Matrix& matrix,
+                   const FXDIB_ResampleOptions& options,
+                   std::unique_ptr<CFX_ImageRenderer>* handle,
+                   BlendMode blend_type) override;
+  bool DrawDeviceText(pdfium::span<const TextCharPos> pCharPos,
+                      CFX_Font* pFont,
+                      const CFX_Matrix& mtObject2Device,
+                      float font_size,
+                      uint32_t color,
+                      const CFX_TextRenderOptions& options) override;
+  bool MultiplyAlpha(float alpha) override;
+  bool MultiplyAlpha(const RetainPtr<CFX_DIBBase>& mask) override;
+
+  HDC m_hDC;
+  const int m_Width;
+  const int m_Height;
+  int m_nBitsPerPixel;
+  const int m_HorzSize;
+  const int m_VertSize;
+  float m_OriginY;
+  bool m_SetOrigin;
+};
+
+#endif  // CORE_FXGE_WIN32_CTEXT_ONLY_PRINTER_DRIVER_H_
diff --git a/core/fxge/win32/cwin32_platform.cpp b/core/fxge/win32/cwin32_platform.cpp
new file mode 100644
index 0000000..428f132
--- /dev/null
+++ b/core/fxge/win32/cwin32_platform.cpp
@@ -0,0 +1,490 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxge/win32/cwin32_platform.h"
+
+#include <iterator>
+#include <memory>
+#include <utility>
+
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxge/cfx_folderfontinfo.h"
+#include "core/fxge/cfx_gemodule.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/span.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "third_party/base/win/scoped_select_object.h"
+#include "third_party/base/win/win_util.h"
+
+namespace {
+
+using ScopedSelectObject = pdfium::base::win::ScopedSelectObject;
+
+struct Variant {
+  const char* m_pFaceName;
+  const char* m_pVariantName;  // Note: UTF-16LE terminator required.
+};
+
+constexpr Variant kVariantNames[] = {
+    {"DFKai-SB", "\x19\x6A\x77\x69\xD4\x9A\x00\x00"},
+};
+
+struct Substs {
+  const char* m_pName;
+  const char* m_pWinName;
+  bool m_bBold;
+  bool m_bItalic;
+};
+
+constexpr Substs kBase14Substs[] = {
+    {"Courier", "Courier New", false, false},
+    {"Courier-Bold", "Courier New", true, false},
+    {"Courier-BoldOblique", "Courier New", true, true},
+    {"Courier-Oblique", "Courier New", false, true},
+    {"Helvetica", "Arial", false, false},
+    {"Helvetica-Bold", "Arial", true, false},
+    {"Helvetica-BoldOblique", "Arial", true, true},
+    {"Helvetica-Oblique", "Arial", false, true},
+    {"Times-Roman", "Times New Roman", false, false},
+    {"Times-Bold", "Times New Roman", true, false},
+    {"Times-BoldItalic", "Times New Roman", true, true},
+    {"Times-Italic", "Times New Roman", false, true},
+};
+
+struct FontNameMap {
+  const char* m_pSubFontName;
+  const char* m_pSrcFontName;
+};
+
+constexpr FontNameMap kJpFontNameMap[] = {
+    {"MS Mincho", "Heiseimin-W3"},
+    {"MS Gothic", "Jun101-Light"},
+};
+
+bool GetSubFontName(ByteString* name) {
+  for (size_t i = 0; i < std::size(kJpFontNameMap); ++i) {
+    if (!FXSYS_stricmp(name->c_str(), kJpFontNameMap[i].m_pSrcFontName)) {
+      *name = kJpFontNameMap[i].m_pSubFontName;
+      return true;
+    }
+  }
+  return false;
+}
+
+// Wraps CreateFontA() so callers don't have to specify all the arguments.
+HFONT Win32CreateFont(int weight,
+                      bool italic,
+                      FX_Charset charset,
+                      int pitch_family,
+                      const char* face) {
+  return ::CreateFontA(-10, 0, 0, 0, weight, italic, 0, 0,
+                       static_cast<int>(charset), OUT_TT_ONLY_PRECIS, 0, 0,
+                       pitch_family, face);
+}
+
+class CFX_Win32FallbackFontInfo final : public CFX_FolderFontInfo {
+ public:
+  CFX_Win32FallbackFontInfo() = default;
+  ~CFX_Win32FallbackFontInfo() override = default;
+
+  // CFX_FolderFontInfo:
+  void* MapFont(int weight,
+                bool bItalic,
+                FX_Charset charset,
+                int pitch_family,
+                const ByteString& face) override;
+};
+
+class CFX_Win32FontInfo final : public SystemFontInfoIface {
+ public:
+  CFX_Win32FontInfo();
+  ~CFX_Win32FontInfo() override;
+
+  // SystemFontInfoIface:
+  bool EnumFontList(CFX_FontMapper* pMapper) override;
+  void* MapFont(int weight,
+                bool bItalic,
+                FX_Charset charset,
+                int pitch_family,
+                const ByteString& face) override;
+  void* GetFont(const ByteString& face) override { return nullptr; }
+  size_t GetFontData(void* hFont,
+                     uint32_t table,
+                     pdfium::span<uint8_t> buffer) override;
+  bool GetFaceName(void* hFont, ByteString* name) override;
+  bool GetFontCharset(void* hFont, FX_Charset* charset) override;
+  void DeleteFont(void* hFont) override;
+
+  void AddInstalledFont(const LOGFONTA* plf, uint32_t font_type);
+
+ private:
+  bool IsSupportedFont(const LOGFONTA* plf);
+  void GetGBPreference(ByteString& face, int weight, int pitch_family);
+  void GetJapanesePreference(ByteString& face, int weight, int pitch_family);
+  ByteString FindFont(const ByteString& name);
+  void* GetFontFromList(int weight,
+                        bool italic,
+                        FX_Charset charset,
+                        int pitch_family,
+                        pdfium::span<const char* const> font_faces);
+
+  const HDC m_hDC;
+  UnownedPtr<CFX_FontMapper> m_pMapper;
+  ByteString m_LastFamily;
+  ByteString m_KaiTi;
+  ByteString m_FangSong;
+};
+
+int CALLBACK FontEnumProc(const LOGFONTA* plf,
+                          const TEXTMETRICA* lpntme,
+                          uint32_t font_type,
+                          LPARAM lParam) {
+  CFX_Win32FontInfo* pFontInfo = reinterpret_cast<CFX_Win32FontInfo*>(lParam);
+  pFontInfo->AddInstalledFont(plf, font_type);
+  return 1;
+}
+
+CFX_Win32FontInfo::CFX_Win32FontInfo() : m_hDC(CreateCompatibleDC(nullptr)) {}
+
+CFX_Win32FontInfo::~CFX_Win32FontInfo() {
+  DeleteDC(m_hDC);
+}
+
+bool CFX_Win32FontInfo::IsSupportedFont(const LOGFONTA* plf) {
+  HFONT hFont = CreateFontIndirectA(plf);
+  bool ret = false;
+  size_t font_size = GetFontData(hFont, 0, {});
+  if (font_size != GDI_ERROR && font_size >= sizeof(uint32_t)) {
+    uint32_t header;
+    auto span = pdfium::as_writable_bytes(pdfium::make_span(&header, 1));
+    GetFontData(hFont, 0, span);
+    header = FXSYS_UINT32_GET_MSBFIRST(span);
+    ret = header == FXBSTR_ID('O', 'T', 'T', 'O') ||
+          header == FXBSTR_ID('t', 't', 'c', 'f') ||
+          header == FXBSTR_ID('t', 'r', 'u', 'e') || header == 0x00010000 ||
+          header == 0x00020000 ||
+          (header & 0xFFFF0000) == FXBSTR_ID(0x80, 0x01, 0x00, 0x00) ||
+          (header & 0xFFFF0000) == FXBSTR_ID('%', '!', 0, 0);
+  }
+  DeleteFont(hFont);
+  return ret;
+}
+
+void CFX_Win32FontInfo::AddInstalledFont(const LOGFONTA* plf,
+                                         uint32_t font_type) {
+  ByteString name(plf->lfFaceName);
+  if (name.GetLength() > 0 && name[0] == '@')
+    return;
+
+  if (name == m_LastFamily) {
+    m_pMapper->AddInstalledFont(name, FX_GetCharsetFromInt(plf->lfCharSet));
+    return;
+  }
+  if (!(font_type & TRUETYPE_FONTTYPE)) {
+    if (!(font_type & DEVICE_FONTTYPE) || !IsSupportedFont(plf))
+      return;
+  }
+
+  m_pMapper->AddInstalledFont(name, FX_GetCharsetFromInt(plf->lfCharSet));
+  m_LastFamily = name;
+}
+
+bool CFX_Win32FontInfo::EnumFontList(CFX_FontMapper* pMapper) {
+  m_pMapper = pMapper;
+  LOGFONTA lf;
+  memset(&lf, 0, sizeof(LOGFONTA));
+  lf.lfCharSet = static_cast<int>(FX_Charset::kDefault);
+  lf.lfFaceName[0] = 0;
+  lf.lfPitchAndFamily = 0;
+  EnumFontFamiliesExA(m_hDC, &lf, reinterpret_cast<FONTENUMPROCA>(FontEnumProc),
+                      reinterpret_cast<uintptr_t>(this), 0);
+  return true;
+}
+
+ByteString CFX_Win32FontInfo::FindFont(const ByteString& name) {
+  if (!m_pMapper)
+    return name;
+
+  absl::optional<ByteString> maybe_installed =
+      m_pMapper->InstalledFontNameStartingWith(name);
+  if (maybe_installed.has_value())
+    return maybe_installed.value();
+
+  absl::optional<ByteString> maybe_localized =
+      m_pMapper->LocalizedFontNameStartingWith(name);
+  if (maybe_localized.has_value())
+    return maybe_localized.value();
+
+  return ByteString();
+}
+
+void* CFX_Win32FontInfo::GetFontFromList(
+    int weight,
+    bool italic,
+    FX_Charset charset,
+    int pitch_family,
+    pdfium::span<const char* const> font_faces) {
+  DCHECK(!font_faces.empty());
+
+  // Initialization not needed because of DCHECK() above and the assignment in
+  // the for-loop below.
+  HFONT font;
+  for (const char* face : font_faces) {
+    font = Win32CreateFont(weight, italic, charset, pitch_family, face);
+    ByteString actual_face;
+    if (GetFaceName(font, &actual_face) && actual_face.EqualNoCase(face))
+      break;
+  }
+  return font;
+}
+
+void* CFX_Win32FallbackFontInfo::MapFont(int weight,
+                                         bool bItalic,
+                                         FX_Charset charset,
+                                         int pitch_family,
+                                         const ByteString& face) {
+  void* font = GetSubstFont(face);
+  if (font)
+    return font;
+
+  bool bCJK = true;
+  switch (charset) {
+    case FX_Charset::kShiftJIS:
+    case FX_Charset::kChineseSimplified:
+    case FX_Charset::kChineseTraditional:
+    case FX_Charset::kHangul:
+      break;
+    default:
+      bCJK = false;
+      break;
+  }
+  return FindFont(weight, bItalic, charset, pitch_family, face, !bCJK);
+}
+
+void CFX_Win32FontInfo::GetGBPreference(ByteString& face,
+                                        int weight,
+                                        int pitch_family) {
+  if (face.Contains("KaiTi") || face.Contains("\xbf\xac")) {
+    if (m_KaiTi.IsEmpty()) {
+      m_KaiTi = FindFont("KaiTi");
+      if (m_KaiTi.IsEmpty()) {
+        m_KaiTi = "SimSun";
+      }
+    }
+    face = m_KaiTi;
+  } else if (face.Contains("FangSong") || face.Contains("\xb7\xc2\xcb\xce")) {
+    if (m_FangSong.IsEmpty()) {
+      m_FangSong = FindFont("FangSong");
+      if (m_FangSong.IsEmpty()) {
+        m_FangSong = "SimSun";
+      }
+    }
+    face = m_FangSong;
+  } else if (face.Contains("SimSun") || face.Contains("\xcb\xce")) {
+    face = "SimSun";
+  } else if (face.Contains("SimHei") || face.Contains("\xba\xda")) {
+    face = "SimHei";
+  } else if (!(pitch_family & FF_ROMAN) && weight > 550) {
+    face = "SimHei";
+  } else {
+    face = "SimSun";
+  }
+}
+
+void CFX_Win32FontInfo::GetJapanesePreference(ByteString& face,
+                                              int weight,
+                                              int pitch_family) {
+  if (face.Contains("Gothic") ||
+      face.Contains("\x83\x53\x83\x56\x83\x62\x83\x4e")) {
+    if (face.Contains("PGothic") ||
+        face.Contains("\x82\x6f\x83\x53\x83\x56\x83\x62\x83\x4e")) {
+      face = "MS PGothic";
+    } else if (face.Contains("UI Gothic")) {
+      face = "MS UI Gothic";
+    } else {
+      if (face.Contains("HGSGothicM") || face.Contains("HGMaruGothicMPRO")) {
+        face = "MS PGothic";
+      } else {
+        face = "MS Gothic";
+      }
+    }
+    return;
+  }
+  if (face.Contains("Mincho") || face.Contains("\x96\xbe\x92\xa9")) {
+    if (face.Contains("PMincho") || face.Contains("\x82\x6f\x96\xbe\x92\xa9")) {
+      face = "MS PMincho";
+    } else {
+      face = "MS Mincho";
+    }
+    return;
+  }
+  if (GetSubFontName(&face))
+    return;
+
+  if (!(pitch_family & FF_ROMAN) && weight > 400) {
+    face = "MS PGothic";
+  } else {
+    face = "MS PMincho";
+  }
+}
+
+void* CFX_Win32FontInfo::MapFont(int weight,
+                                 bool bItalic,
+                                 FX_Charset charset,
+                                 int pitch_family,
+                                 const ByteString& face) {
+  ByteString new_face = face;
+  for (int iBaseFont = 0; iBaseFont < 12; iBaseFont++) {
+    if (new_face == ByteStringView(kBase14Substs[iBaseFont].m_pName)) {
+      new_face = kBase14Substs[iBaseFont].m_pWinName;
+      weight = kBase14Substs[iBaseFont].m_bBold ? FW_BOLD : FW_NORMAL;
+      bItalic = kBase14Substs[iBaseFont].m_bItalic;
+      break;
+    }
+  }
+  if (charset == FX_Charset::kANSI || charset == FX_Charset::kSymbol)
+    charset = FX_Charset::kDefault;
+
+  int subst_pitch_family;
+  switch (charset) {
+    case FX_Charset::kShiftJIS:
+      subst_pitch_family = FF_ROMAN;
+      break;
+    case FX_Charset::kChineseTraditional:
+    case FX_Charset::kHangul:
+    case FX_Charset::kChineseSimplified:
+      subst_pitch_family = 0;
+      break;
+    default:
+      subst_pitch_family = pitch_family;
+      break;
+  }
+  HFONT hFont = Win32CreateFont(weight, bItalic, charset, subst_pitch_family,
+                                new_face.c_str());
+  ByteString actual_new_face;
+  if (GetFaceName(hFont, &actual_new_face) &&
+      new_face.EqualNoCase(actual_new_face.AsStringView())) {
+    return hFont;
+  }
+
+  WideString wsFace = WideString::FromDefANSI(actual_new_face.AsStringView());
+  for (const Variant& variant : kVariantNames) {
+    if (new_face != variant.m_pFaceName)
+      continue;
+
+    const auto* pName =
+        reinterpret_cast<const unsigned short*>(variant.m_pVariantName);
+    size_t len = WideString::WStringLength(pName);
+    WideString wsName = WideString::FromUTF16LE(pName, len);
+    if (wsFace == wsName)
+      return hFont;
+  }
+  ::DeleteObject(hFont);
+  if (charset == FX_Charset::kDefault)
+    return nullptr;
+
+  switch (charset) {
+    case FX_Charset::kShiftJIS:
+      GetJapanesePreference(new_face, weight, pitch_family);
+      break;
+    case FX_Charset::kChineseSimplified:
+      GetGBPreference(new_face, weight, pitch_family);
+      break;
+    case FX_Charset::kHangul:
+      new_face = "Gulim";
+      break;
+    case FX_Charset::kChineseTraditional: {
+      static const char* const kMonospaceFonts[] = {"Microsoft YaHei",
+                                                    "MingLiU"};
+      static const char* const kProportionalFonts[] = {"Microsoft JHengHei",
+                                                       "PMingLiU"};
+      pdfium::span<const char* const> candidate_fonts =
+          new_face.Contains("MSung") ? kMonospaceFonts : kProportionalFonts;
+      return GetFontFromList(weight, bItalic, charset, subst_pitch_family,
+                             candidate_fonts);
+    }
+    default:
+      break;
+  }
+  return Win32CreateFont(weight, bItalic, charset, subst_pitch_family,
+                         new_face.c_str());
+}
+
+void CFX_Win32FontInfo::DeleteFont(void* hFont) {
+  ::DeleteObject(hFont);
+}
+
+size_t CFX_Win32FontInfo::GetFontData(void* hFont,
+                                      uint32_t table,
+                                      pdfium::span<uint8_t> buffer) {
+  ScopedSelectObject select_object(m_hDC, static_cast<HFONT>(hFont));
+  table = FXSYS_UINT32_GET_MSBFIRST(reinterpret_cast<uint8_t*>(&table));
+  size_t size = ::GetFontData(m_hDC, table, 0, buffer.data(),
+                              pdfium::base::checked_cast<DWORD>(buffer.size()));
+  return size != GDI_ERROR ? size : 0;
+}
+
+bool CFX_Win32FontInfo::GetFaceName(void* hFont, ByteString* name) {
+  ScopedSelectObject select_object(m_hDC, static_cast<HFONT>(hFont));
+  char facebuf[100];
+  if (::GetTextFaceA(m_hDC, std::size(facebuf), facebuf) == 0)
+    return false;
+
+  *name = facebuf;
+  return true;
+}
+
+bool CFX_Win32FontInfo::GetFontCharset(void* hFont, FX_Charset* charset) {
+  ScopedSelectObject select_object(m_hDC, static_cast<HFONT>(hFont));
+  TEXTMETRIC tm;
+  ::GetTextMetrics(m_hDC, &tm);
+  *charset = FX_GetCharsetFromInt(tm.tmCharSet);
+  return true;
+}
+
+}  // namespace
+
+CWin32Platform::CWin32Platform() = default;
+
+CWin32Platform::~CWin32Platform() = default;
+
+void CWin32Platform::Init() {
+  if (pdfium::base::win::IsUser32AndGdi32Available())
+    m_GdiplusExt.Load();
+}
+
+std::unique_ptr<SystemFontInfoIface>
+CWin32Platform::CreateDefaultSystemFontInfo() {
+  auto** user_paths = CFX_GEModule::Get()->GetUserFontPaths();
+  if (user_paths) {
+    auto font_info = std::make_unique<CFX_Win32FallbackFontInfo>();
+    for (; *user_paths; user_paths++)
+      font_info->AddPath(*user_paths);
+    return std::move(font_info);
+  }
+
+  if (pdfium::base::win::IsUser32AndGdi32Available())
+    return std::make_unique<CFX_Win32FontInfo>();
+
+  // Select the fallback font information class if GDI is disabled.
+  auto fallback_info = std::make_unique<CFX_Win32FallbackFontInfo>();
+  // Construct the font path manually, SHGetKnownFolderPath won't work under
+  // a restrictive sandbox.
+  CHAR windows_path[MAX_PATH] = {};
+  DWORD path_len = ::GetWindowsDirectoryA(windows_path, MAX_PATH);
+  if (path_len > 0 && path_len < MAX_PATH) {
+    ByteString fonts_path(windows_path);
+    fonts_path += "\\Fonts";
+    fallback_info->AddPath(fonts_path);
+  }
+  return fallback_info;
+}
+
+// static
+std::unique_ptr<CFX_GEModule::PlatformIface>
+CFX_GEModule::PlatformIface::Create() {
+  return std::make_unique<CWin32Platform>();
+}
diff --git a/core/fxge/win32/cwin32_platform.h b/core/fxge/win32/cwin32_platform.h
new file mode 100644
index 0000000..a6c54e9
--- /dev/null
+++ b/core/fxge/win32/cwin32_platform.h
@@ -0,0 +1,27 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_WIN32_CWIN32_PLATFORM_H_
+#define CORE_FXGE_WIN32_CWIN32_PLATFORM_H_
+
+#include <memory>
+
+#include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/win32/cgdi_plus_ext.h"
+
+class CWin32Platform final : public CFX_GEModule::PlatformIface {
+ public:
+  CWin32Platform();
+  ~CWin32Platform() override;
+
+  // CFX_GEModule::PlatformIface:
+  void Init() override;
+  std::unique_ptr<SystemFontInfoIface> CreateDefaultSystemFontInfo() override;
+
+  CGdiplusExt m_GdiplusExt;
+};
+
+#endif  // CORE_FXGE_WIN32_CWIN32_PLATFORM_H_
diff --git a/core/fxge/win32/fx_win32_device.cpp b/core/fxge/win32/fx_win32_device.cpp
deleted file mode 100644
index 58cb504..0000000
--- a/core/fxge/win32/fx_win32_device.cpp
+++ /dev/null
@@ -1,1385 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include <crtdbg.h>
-
-#include <algorithm>
-#include <memory>
-#include <vector>
-
-#include "core/fxcrt/fx_codepage.h"
-#include "core/fxcrt/fx_memory.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/maybe_owned.h"
-#include "core/fxge/cfx_folderfontinfo.h"
-#include "core/fxge/cfx_fontmgr.h"
-#include "core/fxge/cfx_gemodule.h"
-#include "core/fxge/cfx_windowsrenderdevice.h"
-#include "core/fxge/dib/cfx_dibextractor.h"
-#include "core/fxge/dib/cfx_imagerenderer.h"
-#include "core/fxge/dib/cstretchengine.h"
-#include "core/fxge/fx_font.h"
-#include "core/fxge/fx_freetype.h"
-#include "core/fxge/systemfontinfo_iface.h"
-#include "core/fxge/win32/cfx_windowsdib.h"
-#include "core/fxge/win32/win32_int.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/span.h"
-#include "third_party/base/win/win_util.h"
-
-#ifndef _SKIA_SUPPORT_
-#include "core/fxge/agg/fx_agg_driver.h"
-#endif
-
-namespace {
-
-const struct {
-  const char* m_pFaceName;
-  const char* m_pVariantName;  // Note: UTF-16LE terminator required.
-} g_VariantNames[] = {
-    {"DFKai-SB", "\x19\x6A\x77\x69\xD4\x9A\x00\x00"},
-};
-
-const struct {
-  const char* m_pName;
-  const char* m_pWinName;
-  bool m_bBold;
-  bool m_bItalic;
-} g_Base14Substs[] = {
-    {"Courier", "Courier New", false, false},
-    {"Courier-Bold", "Courier New", true, false},
-    {"Courier-BoldOblique", "Courier New", true, true},
-    {"Courier-Oblique", "Courier New", false, true},
-    {"Helvetica", "Arial", false, false},
-    {"Helvetica-Bold", "Arial", true, false},
-    {"Helvetica-BoldOblique", "Arial", true, true},
-    {"Helvetica-Oblique", "Arial", false, true},
-    {"Times-Roman", "Times New Roman", false, false},
-    {"Times-Bold", "Times New Roman", true, false},
-    {"Times-BoldItalic", "Times New Roman", true, true},
-    {"Times-Italic", "Times New Roman", false, true},
-};
-
-struct FontNameMap {
-  const char* m_pSubFontName;
-  const char* m_pSrcFontName;
-};
-const FontNameMap g_JpFontNameMap[] = {
-    {"MS Mincho", "Heiseimin-W3"},
-    {"MS Gothic", "Jun101-Light"},
-};
-
-bool GetSubFontName(ByteString* name) {
-  for (size_t i = 0; i < FX_ArraySize(g_JpFontNameMap); ++i) {
-    if (!FXSYS_stricmp(name->c_str(), g_JpFontNameMap[i].m_pSrcFontName)) {
-      *name = g_JpFontNameMap[i].m_pSubFontName;
-      return true;
-    }
-  }
-  return false;
-}
-
-HPEN CreateExtPen(const CFX_GraphStateData* pGraphState,
-                  const CFX_Matrix* pMatrix,
-                  uint32_t argb) {
-  ASSERT(pGraphState);
-
-  float scale = 1.0f;
-  if (pMatrix) {
-    scale = fabs(pMatrix->a) > fabs(pMatrix->b) ? fabs(pMatrix->a)
-                                                : fabs(pMatrix->b);
-  }
-  float width = std::max(scale * pGraphState->m_LineWidth, 1.0f);
-
-  uint32_t PenStyle = PS_GEOMETRIC;
-  if (!pGraphState->m_DashArray.empty())
-    PenStyle |= PS_USERSTYLE;
-  else
-    PenStyle |= PS_SOLID;
-
-  switch (pGraphState->m_LineCap) {
-    case CFX_GraphStateData::LineCapButt:
-      PenStyle |= PS_ENDCAP_FLAT;
-      break;
-    case CFX_GraphStateData::LineCapRound:
-      PenStyle |= PS_ENDCAP_ROUND;
-      break;
-    case CFX_GraphStateData::LineCapSquare:
-      PenStyle |= PS_ENDCAP_SQUARE;
-      break;
-  }
-  switch (pGraphState->m_LineJoin) {
-    case CFX_GraphStateData::LineJoinMiter:
-      PenStyle |= PS_JOIN_MITER;
-      break;
-    case CFX_GraphStateData::LineJoinRound:
-      PenStyle |= PS_JOIN_ROUND;
-      break;
-    case CFX_GraphStateData::LineJoinBevel:
-      PenStyle |= PS_JOIN_BEVEL;
-      break;
-  }
-
-  FX_COLORREF colorref = ArgbToColorRef(argb);
-  LOGBRUSH lb;
-  lb.lbColor = colorref;
-  lb.lbStyle = BS_SOLID;
-  lb.lbHatch = 0;
-  std::vector<uint32_t> dashes;
-  if (!pGraphState->m_DashArray.empty()) {
-    dashes.resize(pGraphState->m_DashArray.size());
-    for (size_t i = 0; i < pGraphState->m_DashArray.size(); i++) {
-      dashes[i] = FXSYS_roundf(
-          pMatrix ? pMatrix->TransformDistance(pGraphState->m_DashArray[i])
-                  : pGraphState->m_DashArray[i]);
-      dashes[i] = std::max(dashes[i], 1U);
-    }
-  }
-  return ExtCreatePen(PenStyle, (DWORD)ceil(width), &lb,
-                      pGraphState->m_DashArray.size(),
-                      reinterpret_cast<const DWORD*>(dashes.data()));
-}
-
-HBRUSH CreateBrush(uint32_t argb) {
-  return CreateSolidBrush(ArgbToColorRef(argb));
-}
-
-void SetPathToDC(HDC hDC,
-                 const CFX_PathData* pPathData,
-                 const CFX_Matrix* pMatrix) {
-  BeginPath(hDC);
-
-  const std::vector<FX_PATHPOINT>& pPoints = pPathData->GetPoints();
-  for (size_t i = 0; i < pPoints.size(); i++) {
-    CFX_PointF pos = pPoints[i].m_Point;
-    if (pMatrix)
-      pos = pMatrix->Transform(pos);
-
-    CFX_Point screen(FXSYS_roundf(pos.x), FXSYS_roundf(pos.y));
-    FXPT_TYPE point_type = pPoints[i].m_Type;
-    if (point_type == FXPT_TYPE::MoveTo) {
-      MoveToEx(hDC, screen.x, screen.y, nullptr);
-    } else if (point_type == FXPT_TYPE::LineTo) {
-      if (pPoints[i].m_Point == pPoints[i - 1].m_Point)
-        screen.x++;
-
-      LineTo(hDC, screen.x, screen.y);
-    } else if (point_type == FXPT_TYPE::BezierTo) {
-      POINT lppt[3];
-      lppt[0].x = screen.x;
-      lppt[0].y = screen.y;
-
-      pos = pPoints[i + 1].m_Point;
-      if (pMatrix)
-        pos = pMatrix->Transform(pos);
-
-      lppt[1].x = FXSYS_roundf(pos.x);
-      lppt[1].y = FXSYS_roundf(pos.y);
-
-      pos = pPoints[i + 2].m_Point;
-      if (pMatrix)
-        pos = pMatrix->Transform(pos);
-
-      lppt[2].x = FXSYS_roundf(pos.x);
-      lppt[2].y = FXSYS_roundf(pos.y);
-      PolyBezierTo(hDC, lppt, 3);
-      i += 2;
-    }
-    if (pPoints[i].m_CloseFigure)
-      CloseFigure(hDC);
-  }
-  EndPath(hDC);
-}
-
-#ifdef _SKIA_SUPPORT_
-// TODO(caryclark)  This antigrain function is duplicated here to permit
-// removing the last remaining dependency. Eventually, this will be elminiated
-// altogether and replace by Skia code.
-
-struct rect_base {
-  float x1;
-  float y1;
-  float x2;
-  float y2;
-};
-
-unsigned clip_liang_barsky(float x1,
-                           float y1,
-                           float x2,
-                           float y2,
-                           const rect_base& clip_box,
-                           float* x,
-                           float* y) {
-  const float nearzero = 1e-30f;
-  float deltax = x2 - x1;
-  float deltay = y2 - y1;
-  unsigned np = 0;
-  if (deltax == 0)
-    deltax = (x1 > clip_box.x1) ? -nearzero : nearzero;
-  float xin, xout;
-  if (deltax > 0) {
-    xin = clip_box.x1;
-    xout = clip_box.x2;
-  } else {
-    xin = clip_box.x2;
-    xout = clip_box.x1;
-  }
-  float tinx = (xin - x1) / deltax;
-  if (deltay == 0)
-    deltay = (y1 > clip_box.y1) ? -nearzero : nearzero;
-  float yin, yout;
-  if (deltay > 0) {
-    yin = clip_box.y1;
-    yout = clip_box.y2;
-  } else {
-    yin = clip_box.y2;
-    yout = clip_box.y1;
-  }
-  float tiny = (yin - y1) / deltay;
-  float tin1, tin2;
-  if (tinx < tiny) {
-    tin1 = tinx;
-    tin2 = tiny;
-  } else {
-    tin1 = tiny;
-    tin2 = tinx;
-  }
-  if (tin1 <= 1.0f) {
-    if (0 < tin1) {
-      *x++ = xin;
-      *y++ = yin;
-      ++np;
-    }
-    if (tin2 <= 1.0f) {
-      float toutx = (xout - x1) / deltax;
-      float touty = (yout - y1) / deltay;
-      float tout1 = (toutx < touty) ? toutx : touty;
-      if (tin2 > 0 || tout1 > 0) {
-        if (tin2 <= tout1) {
-          if (tin2 > 0) {
-            if (tinx > tiny) {
-              *x++ = xin;
-              *y++ = y1 + (deltay * tinx);
-            } else {
-              *x++ = x1 + (deltax * tiny);
-              *y++ = yin;
-            }
-            ++np;
-          }
-          if (tout1 < 1.0f) {
-            if (toutx < touty) {
-              *x++ = xout;
-              *y++ = y1 + (deltay * toutx);
-            } else {
-              *x++ = x1 + (deltax * touty);
-              *y++ = yout;
-            }
-          } else {
-            *x++ = x2;
-            *y++ = y2;
-          }
-          ++np;
-        } else {
-          if (tinx > tiny) {
-            *x++ = xin;
-            *y++ = yout;
-          } else {
-            *x++ = xout;
-            *y++ = yin;
-          }
-          ++np;
-        }
-      }
-    }
-  }
-  return np;
-}
-#endif  // _SKIA_SUPPORT_
-
-class CFX_Win32FallbackFontInfo final : public CFX_FolderFontInfo {
- public:
-  CFX_Win32FallbackFontInfo() = default;
-  ~CFX_Win32FallbackFontInfo() override = default;
-
-  // CFX_FolderFontInfo:
-  void* MapFont(int weight,
-                bool bItalic,
-                int charset,
-                int pitch_family,
-                const char* family) override;
-};
-
-class CFX_Win32FontInfo final : public SystemFontInfoIface {
- public:
-  CFX_Win32FontInfo();
-  ~CFX_Win32FontInfo() override;
-
-  // SystemFontInfoIface
-  bool EnumFontList(CFX_FontMapper* pMapper) override;
-  void* MapFont(int weight,
-                bool bItalic,
-                int charset,
-                int pitch_family,
-                const char* face) override;
-  void* GetFont(const char* face) override { return nullptr; }
-  uint32_t GetFontData(void* hFont,
-                       uint32_t table,
-                       pdfium::span<uint8_t> buffer) override;
-  bool GetFaceName(void* hFont, ByteString* name) override;
-  bool GetFontCharset(void* hFont, int* charset) override;
-  void DeleteFont(void* hFont) override;
-
-  bool IsOpenTypeFromDiv(const LOGFONTA* plf);
-  bool IsSupportFontFormDiv(const LOGFONTA* plf);
-  void AddInstalledFont(const LOGFONTA* plf, uint32_t FontType);
-  void GetGBPreference(ByteString& face, int weight, int picth_family);
-  void GetJapanesePreference(ByteString& face, int weight, int picth_family);
-  ByteString FindFont(const ByteString& name);
-
-  const HDC m_hDC;
-  UnownedPtr<CFX_FontMapper> m_pMapper;
-  ByteString m_LastFamily;
-  ByteString m_KaiTi;
-  ByteString m_FangSong;
-};
-
-int CALLBACK FontEnumProc(const LOGFONTA* plf,
-                          const TEXTMETRICA* lpntme,
-                          uint32_t FontType,
-                          LPARAM lParam) {
-  CFX_Win32FontInfo* pFontInfo = reinterpret_cast<CFX_Win32FontInfo*>(lParam);
-  pFontInfo->AddInstalledFont(plf, FontType);
-  return 1;
-}
-
-CFX_Win32FontInfo::CFX_Win32FontInfo() : m_hDC(CreateCompatibleDC(nullptr)) {}
-
-CFX_Win32FontInfo::~CFX_Win32FontInfo() {
-  DeleteDC(m_hDC);
-}
-
-bool CFX_Win32FontInfo::IsOpenTypeFromDiv(const LOGFONTA* plf) {
-  HFONT hFont = CreateFontIndirectA(plf);
-  bool ret = false;
-  uint32_t font_size = GetFontData(hFont, 0, {});
-  if (font_size != GDI_ERROR && font_size >= sizeof(uint32_t)) {
-    uint32_t lVersion = 0;
-    GetFontData(hFont, 0, {(uint8_t*)(&lVersion), sizeof(uint32_t)});
-    lVersion = (((uint32_t)(uint8_t)(lVersion)) << 24) |
-               ((uint32_t)((uint8_t)(lVersion >> 8))) << 16 |
-               ((uint32_t)((uint8_t)(lVersion >> 16))) << 8 |
-               ((uint8_t)(lVersion >> 24));
-    if (lVersion == FXBSTR_ID('O', 'T', 'T', 'O') || lVersion == 0x00010000 ||
-        lVersion == FXBSTR_ID('t', 't', 'c', 'f') ||
-        lVersion == FXBSTR_ID('t', 'r', 'u', 'e') || lVersion == 0x00020000) {
-      ret = true;
-    }
-  }
-  DeleteFont(hFont);
-  return ret;
-}
-
-bool CFX_Win32FontInfo::IsSupportFontFormDiv(const LOGFONTA* plf) {
-  HFONT hFont = CreateFontIndirectA(plf);
-  bool ret = false;
-  uint32_t font_size = GetFontData(hFont, 0, {});
-  if (font_size != GDI_ERROR && font_size >= sizeof(uint32_t)) {
-    uint32_t lVersion = 0;
-    GetFontData(hFont, 0, {(uint8_t*)(&lVersion), sizeof(lVersion)});
-    lVersion = (((uint32_t)(uint8_t)(lVersion)) << 24) |
-               ((uint32_t)((uint8_t)(lVersion >> 8))) << 16 |
-               ((uint32_t)((uint8_t)(lVersion >> 16))) << 8 |
-               ((uint8_t)(lVersion >> 24));
-    if (lVersion == FXBSTR_ID('O', 'T', 'T', 'O') || lVersion == 0x00010000 ||
-        lVersion == FXBSTR_ID('t', 't', 'c', 'f') ||
-        lVersion == FXBSTR_ID('t', 'r', 'u', 'e') || lVersion == 0x00020000 ||
-        (lVersion & 0xFFFF0000) == FXBSTR_ID(0x80, 0x01, 0x00, 0x00) ||
-        (lVersion & 0xFFFF0000) == FXBSTR_ID('%', '!', 0, 0)) {
-      ret = true;
-    }
-  }
-  DeleteFont(hFont);
-  return ret;
-}
-
-void CFX_Win32FontInfo::AddInstalledFont(const LOGFONTA* plf,
-                                         uint32_t FontType) {
-  ByteString name(plf->lfFaceName);
-  if (name.GetLength() > 0 && name[0] == '@')
-    return;
-
-  if (name == m_LastFamily) {
-    m_pMapper->AddInstalledFont(name, plf->lfCharSet);
-    return;
-  }
-  if (!(FontType & TRUETYPE_FONTTYPE)) {
-    if (!(FontType & DEVICE_FONTTYPE) || !IsSupportFontFormDiv(plf))
-      return;
-  }
-
-  m_pMapper->AddInstalledFont(name, plf->lfCharSet);
-  m_LastFamily = name;
-}
-
-bool CFX_Win32FontInfo::EnumFontList(CFX_FontMapper* pMapper) {
-  m_pMapper = pMapper;
-  LOGFONTA lf;
-  memset(&lf, 0, sizeof(LOGFONTA));
-  lf.lfCharSet = FX_CHARSET_Default;
-  lf.lfFaceName[0] = 0;
-  lf.lfPitchAndFamily = 0;
-  EnumFontFamiliesExA(m_hDC, &lf, (FONTENUMPROCA)FontEnumProc, (uintptr_t)this,
-                      0);
-  return true;
-}
-
-ByteString CFX_Win32FontInfo::FindFont(const ByteString& name) {
-  if (!m_pMapper)
-    return name;
-
-  for (size_t i = 0; i < m_pMapper->m_InstalledTTFonts.size(); ++i) {
-    ByteString thisname = m_pMapper->m_InstalledTTFonts[i];
-    if (thisname.First(name.GetLength()) == name)
-      return m_pMapper->m_InstalledTTFonts[i];
-  }
-  for (size_t i = 0; i < m_pMapper->m_LocalizedTTFonts.size(); ++i) {
-    ByteString thisname = m_pMapper->m_LocalizedTTFonts[i].first;
-    if (thisname.First(name.GetLength()) == name)
-      return m_pMapper->m_LocalizedTTFonts[i].second;
-  }
-  return ByteString();
-}
-
-void* CFX_Win32FallbackFontInfo::MapFont(int weight,
-                                         bool bItalic,
-                                         int charset,
-                                         int pitch_family,
-                                         const char* cstr_face) {
-  void* font = GetSubstFont(cstr_face);
-  if (font)
-    return font;
-
-  bool bCJK = true;
-  switch (charset) {
-    case FX_CHARSET_ShiftJIS:
-    case FX_CHARSET_ChineseSimplified:
-    case FX_CHARSET_ChineseTraditional:
-    case FX_CHARSET_Hangul:
-      break;
-    default:
-      bCJK = false;
-      break;
-  }
-  return FindFont(weight, bItalic, charset, pitch_family, cstr_face, !bCJK);
-}
-
-void CFX_Win32FontInfo::GetGBPreference(ByteString& face,
-                                        int weight,
-                                        int picth_family) {
-  if (face.Contains("KaiTi") || face.Contains("\xbf\xac")) {
-    if (m_KaiTi.IsEmpty()) {
-      m_KaiTi = FindFont("KaiTi");
-      if (m_KaiTi.IsEmpty()) {
-        m_KaiTi = "SimSun";
-      }
-    }
-    face = m_KaiTi;
-  } else if (face.Contains("FangSong") || face.Contains("\xb7\xc2\xcb\xce")) {
-    if (m_FangSong.IsEmpty()) {
-      m_FangSong = FindFont("FangSong");
-      if (m_FangSong.IsEmpty()) {
-        m_FangSong = "SimSun";
-      }
-    }
-    face = m_FangSong;
-  } else if (face.Contains("SimSun") || face.Contains("\xcb\xce")) {
-    face = "SimSun";
-  } else if (face.Contains("SimHei") || face.Contains("\xba\xda")) {
-    face = "SimHei";
-  } else if (!(picth_family & FF_ROMAN) && weight > 550) {
-    face = "SimHei";
-  } else {
-    face = "SimSun";
-  }
-}
-
-void CFX_Win32FontInfo::GetJapanesePreference(ByteString& face,
-                                              int weight,
-                                              int picth_family) {
-  if (face.Contains("Gothic") ||
-      face.Contains("\x83\x53\x83\x56\x83\x62\x83\x4e")) {
-    if (face.Contains("PGothic") ||
-        face.Contains("\x82\x6f\x83\x53\x83\x56\x83\x62\x83\x4e")) {
-      face = "MS PGothic";
-    } else if (face.Contains("UI Gothic")) {
-      face = "MS UI Gothic";
-    } else {
-      if (face.Contains("HGSGothicM") || face.Contains("HGMaruGothicMPRO")) {
-        face = "MS PGothic";
-      } else {
-        face = "MS Gothic";
-      }
-    }
-    return;
-  }
-  if (face.Contains("Mincho") || face.Contains("\x96\xbe\x92\xa9")) {
-    if (face.Contains("PMincho") || face.Contains("\x82\x6f\x96\xbe\x92\xa9")) {
-      face = "MS PMincho";
-    } else {
-      face = "MS Mincho";
-    }
-    return;
-  }
-  if (GetSubFontName(&face))
-    return;
-
-  if (!(picth_family & FF_ROMAN) && weight > 400) {
-    face = "MS PGothic";
-  } else {
-    face = "MS PMincho";
-  }
-}
-
-void* CFX_Win32FontInfo::MapFont(int weight,
-                                 bool bItalic,
-                                 int charset,
-                                 int pitch_family,
-                                 const char* cstr_face) {
-  ByteString face = cstr_face;
-  int iBaseFont;
-  for (iBaseFont = 0; iBaseFont < 12; iBaseFont++) {
-    if (face == ByteStringView(g_Base14Substs[iBaseFont].m_pName)) {
-      face = g_Base14Substs[iBaseFont].m_pWinName;
-      weight = g_Base14Substs[iBaseFont].m_bBold ? FW_BOLD : FW_NORMAL;
-      bItalic = g_Base14Substs[iBaseFont].m_bItalic;
-      break;
-    }
-  }
-  if (charset == FX_CHARSET_ANSI || charset == FX_CHARSET_Symbol)
-    charset = FX_CHARSET_Default;
-
-  int subst_pitch_family = pitch_family;
-  switch (charset) {
-    case FX_CHARSET_ShiftJIS:
-      subst_pitch_family = FF_ROMAN;
-      break;
-    case FX_CHARSET_ChineseTraditional:
-    case FX_CHARSET_Hangul:
-    case FX_CHARSET_ChineseSimplified:
-      subst_pitch_family = 0;
-      break;
-  }
-  HFONT hFont =
-      ::CreateFontA(-10, 0, 0, 0, weight, bItalic, 0, 0, charset,
-                    OUT_TT_ONLY_PRECIS, 0, 0, subst_pitch_family, face.c_str());
-  char facebuf[100];
-  HFONT hOldFont = (HFONT)::SelectObject(m_hDC, hFont);
-  ::GetTextFaceA(m_hDC, 100, facebuf);
-  ::SelectObject(m_hDC, hOldFont);
-  if (face.EqualNoCase(facebuf))
-    return hFont;
-
-  WideString wsFace = WideString::FromDefANSI(facebuf);
-  for (size_t i = 0; i < FX_ArraySize(g_VariantNames); ++i) {
-    if (face != g_VariantNames[i].m_pFaceName)
-      continue;
-
-    const unsigned short* pName = reinterpret_cast<const unsigned short*>(
-        g_VariantNames[i].m_pVariantName);
-    size_t len = WideString::WStringLength(pName);
-    WideString wsName = WideString::FromUTF16LE(pName, len);
-    if (wsFace == wsName)
-      return hFont;
-  }
-  ::DeleteObject(hFont);
-  if (charset == FX_CHARSET_Default)
-    return nullptr;
-
-  switch (charset) {
-    case FX_CHARSET_ShiftJIS:
-      GetJapanesePreference(face, weight, pitch_family);
-      break;
-    case FX_CHARSET_ChineseSimplified:
-      GetGBPreference(face, weight, pitch_family);
-      break;
-    case FX_CHARSET_Hangul:
-      face = "Gulim";
-      break;
-    case FX_CHARSET_ChineseTraditional:
-      if (face.Contains("MSung")) {
-        face = "MingLiU";
-      } else {
-        face = "PMingLiU";
-      }
-      break;
-  }
-  hFont =
-      ::CreateFontA(-10, 0, 0, 0, weight, bItalic, 0, 0, charset,
-                    OUT_TT_ONLY_PRECIS, 0, 0, subst_pitch_family, face.c_str());
-  return hFont;
-}
-
-void CFX_Win32FontInfo::DeleteFont(void* hFont) {
-  ::DeleteObject(hFont);
-}
-
-uint32_t CFX_Win32FontInfo::GetFontData(void* hFont,
-                                        uint32_t table,
-                                        pdfium::span<uint8_t> buffer) {
-  HFONT hOldFont = (HFONT)::SelectObject(m_hDC, (HFONT)hFont);
-  table = FXDWORD_GET_MSBFIRST(reinterpret_cast<uint8_t*>(&table));
-  uint32_t size = ::GetFontData(m_hDC, table, 0, buffer.data(), buffer.size());
-  ::SelectObject(m_hDC, hOldFont);
-  if (size == GDI_ERROR) {
-    return 0;
-  }
-  return size;
-}
-
-bool CFX_Win32FontInfo::GetFaceName(void* hFont, ByteString* name) {
-  char facebuf[100];
-  HFONT hOldFont = (HFONT)::SelectObject(m_hDC, (HFONT)hFont);
-  int ret = ::GetTextFaceA(m_hDC, 100, facebuf);
-  ::SelectObject(m_hDC, hOldFont);
-  if (ret == 0) {
-    return false;
-  }
-  *name = facebuf;
-  return true;
-}
-
-bool CFX_Win32FontInfo::GetFontCharset(void* hFont, int* charset) {
-  TEXTMETRIC tm;
-  HFONT hOldFont = (HFONT)::SelectObject(m_hDC, (HFONT)hFont);
-  ::GetTextMetrics(m_hDC, &tm);
-  ::SelectObject(m_hDC, hOldFont);
-  *charset = tm.tmCharSet;
-  return true;
-}
-
-}  // namespace
-
-WindowsPrintMode g_pdfium_print_mode = WindowsPrintMode::kModeEmf;
-
-std::unique_ptr<SystemFontInfoIface> SystemFontInfoIface::CreateDefault(
-    const char** pUnused) {
-  if (pdfium::base::win::IsUser32AndGdi32Available())
-    return std::unique_ptr<SystemFontInfoIface>(new CFX_Win32FontInfo);
-
-  // Select the fallback font information class if GDI is disabled.
-  CFX_Win32FallbackFontInfo* pInfoFallback = new CFX_Win32FallbackFontInfo;
-  // Construct the font path manually, SHGetKnownFolderPath won't work under
-  // a restrictive sandbox.
-  CHAR windows_path[MAX_PATH] = {};
-  DWORD path_len = ::GetWindowsDirectoryA(windows_path, MAX_PATH);
-  if (path_len > 0 && path_len < MAX_PATH) {
-    ByteString fonts_path(windows_path);
-    fonts_path += "\\Fonts";
-    pInfoFallback->AddPath(fonts_path);
-  }
-  return std::unique_ptr<SystemFontInfoIface>(pInfoFallback);
-}
-
-CWin32Platform::CWin32Platform() = default;
-
-CWin32Platform::~CWin32Platform() = default;
-
-void CWin32Platform::Init() {
-  OSVERSIONINFO ver;
-  ver.dwOSVersionInfoSize = sizeof(ver);
-  GetVersionEx(&ver);
-  m_bHalfTone = ver.dwMajorVersion >= 5;
-  if (pdfium::base::win::IsUser32AndGdi32Available())
-    m_GdiplusExt.Load();
-  CFX_GEModule::Get()->GetFontMgr()->SetSystemFontInfo(
-      SystemFontInfoIface::CreateDefault(nullptr));
-}
-
-// static
-std::unique_ptr<CFX_GEModule::PlatformIface>
-CFX_GEModule::PlatformIface::Create() {
-  return pdfium::MakeUnique<CWin32Platform>();
-}
-
-CGdiDeviceDriver::CGdiDeviceDriver(HDC hDC, DeviceType device_type)
-    : m_hDC(hDC), m_DeviceType(device_type) {
-  auto* pPlatform =
-      static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
-  SetStretchBltMode(m_hDC, pPlatform->m_bHalfTone ? HALFTONE : COLORONCOLOR);
-  DWORD obj_type = GetObjectType(m_hDC);
-  m_bMetafileDCType = obj_type == OBJ_ENHMETADC || obj_type == OBJ_ENHMETAFILE;
-  if (obj_type == OBJ_MEMDC) {
-    HBITMAP hBitmap = CreateBitmap(1, 1, 1, 1, nullptr);
-    hBitmap = (HBITMAP)SelectObject(m_hDC, hBitmap);
-    BITMAP bitmap;
-    GetObject(hBitmap, sizeof bitmap, &bitmap);
-    m_nBitsPerPixel = bitmap.bmBitsPixel;
-    m_Width = bitmap.bmWidth;
-    m_Height = abs(bitmap.bmHeight);
-    hBitmap = (HBITMAP)SelectObject(m_hDC, hBitmap);
-    DeleteObject(hBitmap);
-  } else {
-    m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL);
-    m_Width = ::GetDeviceCaps(m_hDC, HORZRES);
-    m_Height = ::GetDeviceCaps(m_hDC, VERTRES);
-  }
-  if (m_DeviceType != DeviceType::kDisplay) {
-    m_RenderCaps = FXRC_BIT_MASK;
-  } else {
-    m_RenderCaps = FXRC_GET_BITS | FXRC_BIT_MASK;
-  }
-}
-
-CGdiDeviceDriver::~CGdiDeviceDriver() = default;
-
-DeviceType CGdiDeviceDriver::GetDeviceType() const {
-  return m_DeviceType;
-}
-
-int CGdiDeviceDriver::GetDeviceCaps(int caps_id) const {
-  switch (caps_id) {
-    case FXDC_PIXEL_WIDTH:
-      return m_Width;
-    case FXDC_PIXEL_HEIGHT:
-      return m_Height;
-    case FXDC_BITS_PIXEL:
-      return m_nBitsPerPixel;
-    case FXDC_RENDER_CAPS:
-      return m_RenderCaps;
-    default:
-      NOTREACHED();
-      return 0;
-  }
-}
-
-void CGdiDeviceDriver::SaveState() {
-  SaveDC(m_hDC);
-}
-
-void CGdiDeviceDriver::RestoreState(bool bKeepSaved) {
-  RestoreDC(m_hDC, -1);
-  if (bKeepSaved)
-    SaveDC(m_hDC);
-}
-
-bool CGdiDeviceDriver::GDI_SetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap1,
-                                     const FX_RECT& src_rect,
-                                     int left,
-                                     int top) {
-  if (m_DeviceType == DeviceType::kPrinter) {
-    RetainPtr<CFX_DIBitmap> pBitmap = pBitmap1->FlipImage(false, true);
-    if (!pBitmap)
-      return false;
-
-    if (pBitmap->IsCmykImage() && !pBitmap->ConvertFormat(FXDIB_Rgb))
-      return false;
-
-    LPBYTE pBuffer = pBitmap->GetBuffer();
-    ByteString info = CFX_WindowsDIB::GetBitmapInfo(pBitmap);
-    ((BITMAPINFOHEADER*)info.c_str())->biHeight *= -1;
-    FX_RECT dst_rect(0, 0, src_rect.Width(), src_rect.Height());
-    dst_rect.Intersect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight());
-    int dst_width = dst_rect.Width();
-    int dst_height = dst_rect.Height();
-    ::StretchDIBits(m_hDC, left, top, dst_width, dst_height, 0, 0, dst_width,
-                    dst_height, pBuffer, (BITMAPINFO*)info.c_str(),
-                    DIB_RGB_COLORS, SRCCOPY);
-    return true;
-  }
-
-  RetainPtr<CFX_DIBitmap> pBitmap = pBitmap1;
-  if (pBitmap->IsCmykImage()) {
-    pBitmap = pBitmap->CloneConvert(FXDIB_Rgb);
-    if (!pBitmap)
-      return false;
-  }
-  LPBYTE pBuffer = pBitmap->GetBuffer();
-  ByteString info = CFX_WindowsDIB::GetBitmapInfo(pBitmap);
-  ::SetDIBitsToDevice(m_hDC, left, top, src_rect.Width(), src_rect.Height(),
-                      src_rect.left, pBitmap->GetHeight() - src_rect.bottom, 0,
-                      pBitmap->GetHeight(), pBuffer, (BITMAPINFO*)info.c_str(),
-                      DIB_RGB_COLORS);
-  return true;
-}
-
-bool CGdiDeviceDriver::GDI_StretchDIBits(
-    const RetainPtr<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)
-    return false;
-
-  if (pBitmap->IsCmykImage() && !pBitmap->ConvertFormat(FXDIB_Rgb))
-    return false;
-
-  ByteString info = CFX_WindowsDIB::GetBitmapInfo(pBitmap);
-  if ((int64_t)abs(dest_width) * abs(dest_height) <
-          (int64_t)pBitmap1->GetWidth() * pBitmap1->GetHeight() * 4 ||
-      options.bInterpolateBilinear || options.bInterpolateBicubic) {
-    SetStretchBltMode(m_hDC, HALFTONE);
-  } else {
-    SetStretchBltMode(m_hDC, COLORONCOLOR);
-  }
-  RetainPtr<CFX_DIBitmap> pToStrechBitmap = pBitmap;
-  if (m_DeviceType == DeviceType::kPrinter &&
-      ((int64_t)pBitmap->GetWidth() * pBitmap->GetHeight() >
-       (int64_t)abs(dest_width) * abs(dest_height))) {
-    pToStrechBitmap = pBitmap->StretchTo(dest_width, dest_height,
-                                         FXDIB_ResampleOptions(), nullptr);
-  }
-  ByteString toStrechBitmapInfo =
-      CFX_WindowsDIB::GetBitmapInfo(pToStrechBitmap);
-  ::StretchDIBits(m_hDC, dest_left, dest_top, dest_width, dest_height, 0, 0,
-                  pToStrechBitmap->GetWidth(), pToStrechBitmap->GetHeight(),
-                  pToStrechBitmap->GetBuffer(),
-                  (BITMAPINFO*)toStrechBitmapInfo.c_str(), DIB_RGB_COLORS,
-                  SRCCOPY);
-  return true;
-}
-
-bool CGdiDeviceDriver::GDI_StretchBitMask(
-    const RetainPtr<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)
-    return false;
-
-  int width = pBitmap->GetWidth(), height = pBitmap->GetHeight();
-  struct {
-    BITMAPINFOHEADER bmiHeader;
-    uint32_t bmiColors[2];
-  } bmi;
-  memset(&bmi.bmiHeader, 0, sizeof(BITMAPINFOHEADER));
-  bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
-  bmi.bmiHeader.biBitCount = 1;
-  bmi.bmiHeader.biCompression = BI_RGB;
-  bmi.bmiHeader.biHeight = -height;
-  bmi.bmiHeader.biPlanes = 1;
-  bmi.bmiHeader.biWidth = width;
-  if (m_nBitsPerPixel != 1) {
-    SetStretchBltMode(m_hDC, HALFTONE);
-  }
-  bmi.bmiColors[0] = 0xffffff;
-  bmi.bmiColors[1] = 0;
-
-  HBRUSH hPattern = CreateBrush(bitmap_color);
-  HBRUSH hOld = (HBRUSH)SelectObject(m_hDC, hPattern);
-
-  // In PDF, when image mask is 1, use device bitmap; when mask is 0, use brush
-  // bitmap.
-  // A complete list of the boolen operations is as follows:
-
-  /* P(bitmap_color)    S(ImageMask)    D(DeviceBitmap)    Result
-   *        0                 0                0              0
-   *        0                 0                1              0
-   *        0                 1                0              0
-   *        0                 1                1              1
-   *        1                 0                0              1
-   *        1                 0                1              1
-   *        1                 1                0              0
-   *        1                 1                1              1
-   */
-  // The boolen codes is B8. Based on
-  // http://msdn.microsoft.com/en-us/library/aa932106.aspx, the ROP3 code is
-  // 0xB8074A
-
-  ::StretchDIBits(m_hDC, dest_left, dest_top, dest_width, dest_height, 0, 0,
-                  width, height, pBitmap->GetBuffer(), (BITMAPINFO*)&bmi,
-                  DIB_RGB_COLORS, 0xB8074A);
-
-  SelectObject(m_hDC, hOld);
-  DeleteObject(hPattern);
-
-  return true;
-}
-
-bool CGdiDeviceDriver::GetClipBox(FX_RECT* pRect) {
-  return !!(::GetClipBox(m_hDC, (RECT*)pRect));
-}
-
-void CGdiDeviceDriver::DrawLine(float x1, float y1, float x2, float y2) {
-  if (!m_bMetafileDCType) {  // EMF drawing is not bound to the DC.
-    int startOutOfBoundsFlag = (x1 < 0) | ((x1 > m_Width) << 1) |
-                               ((y1 < 0) << 2) | ((y1 > m_Height) << 3);
-    int endOutOfBoundsFlag = (x2 < 0) | ((x2 > m_Width) << 1) |
-                             ((y2 < 0) << 2) | ((y2 > m_Height) << 3);
-    if (startOutOfBoundsFlag & endOutOfBoundsFlag)
-      return;
-
-    if (startOutOfBoundsFlag || endOutOfBoundsFlag) {
-      float x[2];
-      float y[2];
-      int np;
-#ifdef _SKIA_SUPPORT_
-      // TODO(caryclark) temporary replacement of antigrain in line function
-      // to permit removing antigrain altogether
-      rect_base rect = {0.0f, 0.0f, (float)(m_Width), (float)(m_Height)};
-      np = clip_liang_barsky(x1, y1, x2, y2, rect, x, y);
-#else
-      agg::rect_base<float> rect(0.0f, 0.0f, (float)(m_Width),
-                                 (float)(m_Height));
-      np = agg::clip_liang_barsky<float>(x1, y1, x2, y2, rect, x, y);
-#endif
-      if (np == 0)
-        return;
-
-      if (np == 1) {
-        x2 = x[0];
-        y2 = y[0];
-      } else {
-        ASSERT(np == 2);
-        x1 = x[0];
-        y1 = y[0];
-        x2 = x[1];
-        y2 = y[1];
-      }
-    }
-  }
-
-  MoveToEx(m_hDC, FXSYS_roundf(x1), FXSYS_roundf(y1), nullptr);
-  LineTo(m_hDC, FXSYS_roundf(x2), FXSYS_roundf(y2));
-}
-
-bool CGdiDeviceDriver::DrawPath(const CFX_PathData* pPathData,
-                                const CFX_Matrix* pMatrix,
-                                const CFX_GraphStateData* pGraphState,
-                                uint32_t fill_color,
-                                uint32_t stroke_color,
-                                int fill_mode,
-                                BlendMode blend_type) {
-  if (blend_type != BlendMode::kNormal)
-    return false;
-
-  auto* pPlatform =
-      static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
-  if (!(pGraphState || stroke_color == 0) &&
-      !pPlatform->m_GdiplusExt.IsAvailable()) {
-    CFX_FloatRect bbox_f = pPathData->GetBoundingBox();
-    if (pMatrix)
-      bbox_f = pMatrix->TransformRect(bbox_f);
-
-    FX_RECT bbox = bbox_f.GetInnerRect();
-    if (bbox.Width() <= 0) {
-      return DrawCosmeticLine(CFX_PointF(bbox.left, bbox.top),
-                              CFX_PointF(bbox.left, bbox.bottom + 1),
-                              fill_color, BlendMode::kNormal);
-    }
-    if (bbox.Height() <= 0) {
-      return DrawCosmeticLine(CFX_PointF(bbox.left, bbox.top),
-                              CFX_PointF(bbox.right + 1, bbox.top), fill_color,
-                              BlendMode::kNormal);
-    }
-  }
-  int fill_alpha = FXARGB_A(fill_color);
-  int stroke_alpha = FXARGB_A(stroke_color);
-  bool bDrawAlpha = (fill_alpha > 0 && fill_alpha < 255) ||
-                    (stroke_alpha > 0 && stroke_alpha < 255 && pGraphState);
-  if (!pPlatform->m_GdiplusExt.IsAvailable() && bDrawAlpha)
-    return false;
-
-  if (pPlatform->m_GdiplusExt.IsAvailable()) {
-    if (bDrawAlpha || ((m_DeviceType != DeviceType::kPrinter &&
-                        !(fill_mode & FXFILL_FULLCOVER)) ||
-                       (pGraphState && !pGraphState->m_DashArray.empty()))) {
-      if (!((!pMatrix || !pMatrix->WillScale()) && pGraphState &&
-            pGraphState->m_LineWidth == 1.0f &&
-            (pPathData->GetPoints().size() == 5 ||
-             pPathData->GetPoints().size() == 4) &&
-            pPathData->IsRect())) {
-        if (pPlatform->m_GdiplusExt.DrawPath(m_hDC, pPathData, pMatrix,
-                                             pGraphState, fill_color,
-                                             stroke_color, fill_mode)) {
-          return true;
-        }
-      }
-    }
-  }
-  int old_fill_mode = fill_mode;
-  fill_mode &= 3;
-  HPEN hPen = nullptr;
-  HBRUSH hBrush = nullptr;
-  if (pGraphState && stroke_alpha) {
-    SetMiterLimit(m_hDC, pGraphState->m_MiterLimit, nullptr);
-    hPen = CreateExtPen(pGraphState, pMatrix, stroke_color);
-    hPen = (HPEN)SelectObject(m_hDC, hPen);
-  }
-  if (fill_mode && fill_alpha) {
-    SetPolyFillMode(m_hDC, fill_mode);
-    hBrush = CreateBrush(fill_color);
-    hBrush = (HBRUSH)SelectObject(m_hDC, hBrush);
-  }
-  if (pPathData->GetPoints().size() == 2 && pGraphState &&
-      !pGraphState->m_DashArray.empty()) {
-    CFX_PointF pos1 = pPathData->GetPoint(0);
-    CFX_PointF pos2 = pPathData->GetPoint(1);
-    if (pMatrix) {
-      pos1 = pMatrix->Transform(pos1);
-      pos2 = pMatrix->Transform(pos2);
-    }
-    DrawLine(pos1.x, pos1.y, pos2.x, pos2.y);
-  } else {
-    SetPathToDC(m_hDC, pPathData, pMatrix);
-    if (pGraphState && stroke_alpha) {
-      if (fill_mode && fill_alpha) {
-        if (old_fill_mode & FX_FILL_TEXT_MODE) {
-          StrokeAndFillPath(m_hDC);
-        } else {
-          FillPath(m_hDC);
-          SetPathToDC(m_hDC, pPathData, pMatrix);
-          StrokePath(m_hDC);
-        }
-      } else {
-        StrokePath(m_hDC);
-      }
-    } else if (fill_mode && fill_alpha) {
-      FillPath(m_hDC);
-    }
-  }
-  if (hPen) {
-    hPen = (HPEN)SelectObject(m_hDC, hPen);
-    DeleteObject(hPen);
-  }
-  if (hBrush) {
-    hBrush = (HBRUSH)SelectObject(m_hDC, hBrush);
-    DeleteObject(hBrush);
-  }
-  return true;
-}
-
-bool CGdiDeviceDriver::FillRectWithBlend(const FX_RECT& rect,
-                                         uint32_t fill_color,
-                                         BlendMode blend_type) {
-  if (blend_type != BlendMode::kNormal)
-    return false;
-
-  int alpha;
-  FX_COLORREF colorref;
-  std::tie(alpha, colorref) = ArgbToAlphaAndColorRef(fill_color);
-  if (alpha == 0)
-    return true;
-
-  if (alpha < 255)
-    return false;
-
-  HBRUSH hBrush = CreateSolidBrush(colorref);
-  const RECT* pRect = reinterpret_cast<const RECT*>(&rect);
-  ::FillRect(m_hDC, pRect, hBrush);
-  DeleteObject(hBrush);
-  return true;
-}
-
-void CGdiDeviceDriver::SetBaseClip(const FX_RECT& rect) {
-  m_BaseClipBox = rect;
-}
-
-bool CGdiDeviceDriver::SetClip_PathFill(const CFX_PathData* pPathData,
-                                        const CFX_Matrix* pMatrix,
-                                        int fill_mode) {
-  if (pPathData->GetPoints().size() == 5) {
-    CFX_FloatRect rectf;
-    if (pPathData->IsRect(pMatrix, &rectf)) {
-      FX_RECT rect = rectf.GetOuterRect();
-      // Can easily apply base clip to protect against wildly large rectangular
-      // clips. crbug.com/1019026
-      if (m_BaseClipBox.has_value())
-        rect.Intersect(m_BaseClipBox.value());
-      return IntersectClipRect(m_hDC, rect.left, rect.top, rect.right,
-                               rect.bottom) != ERROR;
-    }
-  }
-  SetPathToDC(m_hDC, pPathData, pMatrix);
-  SetPolyFillMode(m_hDC, fill_mode & 3);
-  SelectClipPath(m_hDC, RGN_AND);
-  return true;
-}
-
-bool CGdiDeviceDriver::SetClip_PathStroke(
-    const CFX_PathData* pPathData,
-    const CFX_Matrix* pMatrix,
-    const CFX_GraphStateData* pGraphState) {
-  HPEN hPen = CreateExtPen(pGraphState, pMatrix, 0xff000000);
-  hPen = (HPEN)SelectObject(m_hDC, hPen);
-  SetPathToDC(m_hDC, pPathData, pMatrix);
-  WidenPath(m_hDC);
-  SetPolyFillMode(m_hDC, WINDING);
-  bool ret = !!SelectClipPath(m_hDC, RGN_AND);
-  hPen = (HPEN)SelectObject(m_hDC, hPen);
-  DeleteObject(hPen);
-  return ret;
-}
-
-bool CGdiDeviceDriver::DrawCosmeticLine(const CFX_PointF& ptMoveTo,
-                                        const CFX_PointF& ptLineTo,
-                                        uint32_t color,
-                                        BlendMode blend_type) {
-  if (blend_type != BlendMode::kNormal)
-    return false;
-
-  int alpha;
-  FX_COLORREF colorref;
-  std::tie(alpha, colorref) = ArgbToAlphaAndColorRef(color);
-  if (alpha == 0)
-    return true;
-
-  HPEN hPen = CreatePen(PS_SOLID, 1, colorref);
-  hPen = (HPEN)SelectObject(m_hDC, hPen);
-  MoveToEx(m_hDC, FXSYS_roundf(ptMoveTo.x), FXSYS_roundf(ptMoveTo.y), nullptr);
-  LineTo(m_hDC, FXSYS_roundf(ptLineTo.x), FXSYS_roundf(ptLineTo.y));
-  hPen = (HPEN)SelectObject(m_hDC, hPen);
-  DeleteObject(hPen);
-  return true;
-}
-
-CGdiDisplayDriver::CGdiDisplayDriver(HDC hDC)
-    : CGdiDeviceDriver(hDC, DeviceType::kDisplay) {
-  auto* pPlatform =
-      static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
-  if (pPlatform->m_GdiplusExt.IsAvailable()) {
-    m_RenderCaps |= FXRC_ALPHA_PATH | FXRC_ALPHA_IMAGE;
-  }
-}
-
-CGdiDisplayDriver::~CGdiDisplayDriver() = default;
-
-int CGdiDisplayDriver::GetDeviceCaps(int caps_id) const {
-  if (caps_id == FXDC_HORZ_SIZE || caps_id == FXDC_VERT_SIZE)
-    return 0;
-  return CGdiDeviceDriver::GetDeviceCaps(caps_id);
-}
-
-bool CGdiDisplayDriver::GetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                                  int left,
-                                  int top) {
-  bool ret = false;
-  int width = pBitmap->GetWidth();
-  int height = pBitmap->GetHeight();
-  HBITMAP hbmp = CreateCompatibleBitmap(m_hDC, width, height);
-  HDC hDCMemory = CreateCompatibleDC(m_hDC);
-  HBITMAP holdbmp = (HBITMAP)SelectObject(hDCMemory, hbmp);
-  BitBlt(hDCMemory, 0, 0, width, height, m_hDC, left, top, SRCCOPY);
-  SelectObject(hDCMemory, holdbmp);
-  BITMAPINFO bmi;
-  memset(&bmi, 0, sizeof bmi);
-  bmi.bmiHeader.biSize = sizeof bmi.bmiHeader;
-  bmi.bmiHeader.biBitCount = pBitmap->GetBPP();
-  bmi.bmiHeader.biHeight = -height;
-  bmi.bmiHeader.biPlanes = 1;
-  bmi.bmiHeader.biWidth = width;
-  if (pBitmap->GetBPP() > 8 && !pBitmap->IsCmykImage()) {
-    ret = ::GetDIBits(hDCMemory, hbmp, 0, height, pBitmap->GetBuffer(), &bmi,
-                      DIB_RGB_COLORS) == height;
-  } else {
-    auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-    if (bitmap->Create(width, height, FXDIB_Rgb)) {
-      bmi.bmiHeader.biBitCount = 24;
-      ::GetDIBits(hDCMemory, hbmp, 0, height, bitmap->GetBuffer(), &bmi,
-                  DIB_RGB_COLORS);
-      ret = pBitmap->TransferBitmap(0, 0, width, height, bitmap, 0, 0);
-    } else {
-      ret = false;
-    }
-  }
-  if (pBitmap->HasAlpha() && ret)
-    pBitmap->LoadChannel(FXDIB_Alpha, 0xff);
-
-  DeleteObject(hbmp);
-  DeleteObject(hDCMemory);
-  return ret;
-}
-
-bool CGdiDisplayDriver::SetDIBits(const RetainPtr<CFX_DIBBase>& pSource,
-                                  uint32_t color,
-                                  const FX_RECT& src_rect,
-                                  int left,
-                                  int top,
-                                  BlendMode blend_type) {
-  ASSERT(blend_type == BlendMode::kNormal);
-  if (pSource->IsAlphaMask()) {
-    int width = pSource->GetWidth(), height = pSource->GetHeight();
-    int alpha = FXARGB_A(color);
-    if (pSource->GetBPP() != 1 || alpha != 255) {
-      auto background = pdfium::MakeRetain<CFX_DIBitmap>();
-      if (!background->Create(width, height, FXDIB_Rgb32) ||
-          !GetDIBits(background, left, top) ||
-          !background->CompositeMask(0, 0, width, height, pSource, color, 0, 0,
-                                     BlendMode::kNormal, nullptr, false)) {
-        return false;
-      }
-      FX_RECT alpha_src_rect(0, 0, width, height);
-      return SetDIBits(background, 0, alpha_src_rect, left, top,
-                       BlendMode::kNormal);
-    }
-    FX_RECT clip_rect(left, top, left + src_rect.Width(),
-                      top + src_rect.Height());
-    return StretchDIBits(pSource, color, left - src_rect.left,
-                         top - src_rect.top, width, height, &clip_rect,
-                         FXDIB_ResampleOptions(), BlendMode::kNormal);
-  }
-  int width = src_rect.Width();
-  int height = src_rect.Height();
-  if (pSource->HasAlpha()) {
-    auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-    if (!bitmap->Create(width, height, FXDIB_Rgb) ||
-        !GetDIBits(bitmap, left, top) ||
-        !bitmap->CompositeBitmap(0, 0, width, height, pSource, src_rect.left,
-                                 src_rect.top, BlendMode::kNormal, nullptr,
-                                 false)) {
-      return false;
-    }
-    FX_RECT alpha_src_rect(0, 0, width, height);
-    return SetDIBits(bitmap, 0, alpha_src_rect, left, top, BlendMode::kNormal);
-  }
-  CFX_DIBExtractor temp(pSource);
-  RetainPtr<CFX_DIBitmap> pBitmap = temp.GetBitmap();
-  if (!pBitmap)
-    return false;
-  return GDI_SetDIBits(pBitmap, src_rect, left, top);
-}
-
-bool CGdiDisplayDriver::UseFoxitStretchEngine(
-    const RetainPtr<CFX_DIBBase>& pSource,
-    uint32_t color,
-    int dest_left,
-    int dest_top,
-    int dest_width,
-    int dest_height,
-    const FX_RECT* pClipRect,
-    const FXDIB_ResampleOptions& options) {
-  FX_RECT bitmap_clip = *pClipRect;
-  if (dest_width < 0)
-    dest_left += dest_width;
-
-  if (dest_height < 0)
-    dest_top += dest_height;
-
-  bitmap_clip.Offset(-dest_left, -dest_top);
-  RetainPtr<CFX_DIBitmap> pStretched =
-      pSource->StretchTo(dest_width, dest_height, options, &bitmap_clip);
-  if (!pStretched)
-    return true;
-
-  FX_RECT src_rect(0, 0, pStretched->GetWidth(), pStretched->GetHeight());
-  return SetDIBits(pStretched, color, src_rect, pClipRect->left, pClipRect->top,
-                   BlendMode::kNormal);
-}
-
-bool CGdiDisplayDriver::StretchDIBits(const RetainPtr<CFX_DIBBase>& pSource,
-                                      uint32_t color,
-                                      int dest_left,
-                                      int dest_top,
-                                      int dest_width,
-                                      int dest_height,
-                                      const FX_RECT* pClipRect,
-                                      const FXDIB_ResampleOptions& options,
-                                      BlendMode blend_type) {
-  ASSERT(pSource);
-  ASSERT(pClipRect);
-
-  if (options.HasAnyOptions() || dest_width > 10000 || dest_width < -10000 ||
-      dest_height > 10000 || dest_height < -10000) {
-    return UseFoxitStretchEngine(pSource, color, dest_left, dest_top,
-                                 dest_width, dest_height, pClipRect, options);
-  }
-  if (pSource->IsAlphaMask()) {
-    FX_RECT image_rect;
-    image_rect.left = dest_width > 0 ? dest_left : dest_left + dest_width;
-    image_rect.right = dest_width > 0 ? dest_left + dest_width : dest_left;
-    image_rect.top = dest_height > 0 ? dest_top : dest_top + dest_height;
-    image_rect.bottom = dest_height > 0 ? dest_top + dest_height : dest_top;
-    FX_RECT clip_rect = image_rect;
-    clip_rect.Intersect(*pClipRect);
-    clip_rect.Offset(-image_rect.left, -image_rect.top);
-    int clip_width = clip_rect.Width(), clip_height = clip_rect.Height();
-    RetainPtr<CFX_DIBitmap> pStretched(pSource->StretchTo(
-        dest_width, dest_height, FXDIB_ResampleOptions(), &clip_rect));
-    if (!pStretched)
-      return true;
-
-    auto background = pdfium::MakeRetain<CFX_DIBitmap>();
-    if (!background->Create(clip_width, clip_height, FXDIB_Rgb32) ||
-        !GetDIBits(background, image_rect.left + clip_rect.left,
-                   image_rect.top + clip_rect.top) ||
-        !background->CompositeMask(0, 0, clip_width, clip_height, pStretched,
-                                   color, 0, 0, BlendMode::kNormal, nullptr,
-                                   false)) {
-      return false;
-    }
-
-    FX_RECT src_rect(0, 0, clip_width, clip_height);
-    return SetDIBits(background, 0, src_rect, image_rect.left + clip_rect.left,
-                     image_rect.top + clip_rect.top, BlendMode::kNormal);
-  }
-  if (pSource->HasAlpha()) {
-    auto* pPlatform =
-        static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
-    if (pPlatform->m_GdiplusExt.IsAvailable() && !pSource->IsCmykImage()) {
-      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,
-          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,
-                           dest_height, FXDIB_ResampleOptions());
-}
-
-bool CGdiDisplayDriver::StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
-                                    int bitmap_alpha,
-                                    uint32_t color,
-                                    const CFX_Matrix& matrix,
-                                    const FXDIB_ResampleOptions& options,
-                                    std::unique_ptr<CFX_ImageRenderer>* handle,
-                                    BlendMode blend_type) {
-  return false;
-}
-
-CFX_WindowsRenderDevice::CFX_WindowsRenderDevice(
-    HDC hDC,
-    const EncoderIface* pEncoderIface) {
-  SetDeviceDriver(pdfium::WrapUnique(CreateDriver(hDC, pEncoderIface)));
-}
-
-CFX_WindowsRenderDevice::~CFX_WindowsRenderDevice() = default;
-
-// static
-RenderDeviceDriverIface* CFX_WindowsRenderDevice::CreateDriver(
-    HDC hDC,
-    const EncoderIface* pEncoderIface) {
-  int device_type = ::GetDeviceCaps(hDC, TECHNOLOGY);
-  int obj_type = ::GetObjectType(hDC);
-  bool use_printer = device_type == DT_RASPRINTER ||
-                     device_type == DT_PLOTTER ||
-                     device_type == DT_CHARSTREAM || obj_type == OBJ_ENHMETADC;
-
-  if (!use_printer)
-    return new CGdiDisplayDriver(hDC);
-
-  if (g_pdfium_print_mode == WindowsPrintMode::kModeEmf)
-    return new CGdiPrinterDriver(hDC);
-
-  if (g_pdfium_print_mode == WindowsPrintMode::kModeTextOnly)
-    return new CTextOnlyPrinterDriver(hDC);
-
-  return new CPSPrinterDriver(hDC, g_pdfium_print_mode, false, pEncoderIface);
-}
diff --git a/core/fxge/win32/fx_win32_device_embeddertest.cpp b/core/fxge/win32/fx_win32_device_embeddertest.cpp
deleted file mode 100644
index cb088e7..0000000
--- a/core/fxge/win32/fx_win32_device_embeddertest.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "core/fxge/win32/win32_int.h"
-
-#include <windows.h>
-
-#include <memory>
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
-
-namespace {
-
-constexpr CFX_Matrix kIdentityMatrix;
-
-}  // namespace
-
-class CFX_WindowsRenderDeviceTest : public testing::Test {
- public:
-  void SetUp() override {
-    // Get a device context with Windows GDI.
-    m_hDC = CreateCompatibleDC(nullptr);
-    ASSERT_TRUE(m_hDC);
-    CFX_GEModule::Create(nullptr);
-    m_driver = pdfium::MakeUnique<CFX_WindowsRenderDevice>(m_hDC, nullptr);
-    m_driver->SaveState();
-  }
-
-  void TearDown() override {
-    m_driver->RestoreState(false);
-    m_driver.reset();
-    CFX_GEModule::Destroy();
-    DeleteDC(m_hDC);
-  }
-
- protected:
-  HDC m_hDC;
-  std::unique_ptr<CFX_WindowsRenderDevice> m_driver;
-};
-
-TEST_F(CFX_WindowsRenderDeviceTest, SimpleClipTriangle) {
-  CFX_PathData path_data;
-  CFX_PointF p1(0.0f, 0.0f);
-  CFX_PointF p2(0.0f, 100.0f);
-  CFX_PointF p3(100.0f, 100.0f);
-
-  path_data.AppendLine(p1, p2);
-  path_data.AppendLine(p2, p3);
-  path_data.AppendLine(p3, p1);
-  path_data.ClosePath();
-  EXPECT_TRUE(
-      m_driver->SetClip_PathFill(&path_data, &kIdentityMatrix, FXFILL_WINDING));
-}
-
-TEST_F(CFX_WindowsRenderDeviceTest, SimpleClipRect) {
-  CFX_PathData path_data;
-
-  path_data.AppendRect(0.0f, 100.0f, 200.0f, 0.0f);
-  path_data.ClosePath();
-  EXPECT_TRUE(
-      m_driver->SetClip_PathFill(&path_data, &kIdentityMatrix, FXFILL_WINDING));
-}
-
-TEST_F(CFX_WindowsRenderDeviceTest, GargantuanClipRect) {
-  CFX_PathData path_data;
-
-  path_data.AppendRect(-257698020.0f, -257697252.0f, 257698044.0f,
-                       257698812.0f);
-  path_data.ClosePath();
-  // These coordinates for a clip path are valid, just very large. Using these
-  // for a clip path should allow IntersectClipRect() to return success;
-  // however they do not because the GDI API IntersectClipRect() errors out and
-  // affect subsequent imaging.  crbug.com/1019026
-  EXPECT_FALSE(
-      m_driver->SetClip_PathFill(&path_data, &kIdentityMatrix, FXFILL_WINDING));
-}
-
-TEST_F(CFX_WindowsRenderDeviceTest, GargantuanClipRectWithBaseClip) {
-  CFX_PathData path_data;
-  const FX_RECT kBaseClip(0, 0, 5100, 6600);
-
-  m_driver->SetBaseClip(kBaseClip);
-  path_data.AppendRect(-257698020.0f, -257697252.0f, 257698044.0f,
-                       257698812.0f);
-  path_data.ClosePath();
-  // Use of a reasonable base clip ensures that we avoid getting an error back
-  // from GDI API IntersectClipRect().
-  EXPECT_TRUE(
-      m_driver->SetClip_PathFill(&path_data, &kIdentityMatrix, FXFILL_WINDING));
-}
diff --git a/core/fxge/win32/fx_win32_dib.cpp b/core/fxge/win32/fx_win32_dib.cpp
deleted file mode 100644
index 5f9d42c..0000000
--- a/core/fxge/win32/fx_win32_dib.cpp
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include <windows.h>
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxge/cfx_gemodule.h"
-#include "core/fxge/win32/cfx_windowsdib.h"
-#include "core/fxge/win32/win32_int.h"
-
-ByteString CFX_WindowsDIB::GetBitmapInfo(
-    const RetainPtr<CFX_DIBitmap>& pBitmap) {
-  int len = sizeof(BITMAPINFOHEADER);
-  if (pBitmap->GetBPP() == 1 || pBitmap->GetBPP() == 8)
-    len += sizeof(DWORD) * (int)(1 << pBitmap->GetBPP());
-
-  ByteString result;
-  {
-    // Span's lifetime must end before ReleaseBuffer() below.
-    pdfium::span<char> cspan = result.GetBuffer(len);
-    BITMAPINFOHEADER* pbmih = reinterpret_cast<BITMAPINFOHEADER*>(cspan.data());
-    memset(pbmih, 0, sizeof(BITMAPINFOHEADER));
-    pbmih->biSize = sizeof(BITMAPINFOHEADER);
-    pbmih->biBitCount = pBitmap->GetBPP();
-    pbmih->biCompression = BI_RGB;
-    pbmih->biHeight = -(int)pBitmap->GetHeight();
-    pbmih->biPlanes = 1;
-    pbmih->biWidth = pBitmap->GetWidth();
-    if (pBitmap->GetBPP() == 8) {
-      uint32_t* pPalette = (uint32_t*)(pbmih + 1);
-      if (pBitmap->GetPalette()) {
-        for (int i = 0; i < 256; i++) {
-          pPalette[i] = pBitmap->GetPalette()[i];
-        }
-      } else {
-        for (int i = 0; i < 256; i++) {
-          pPalette[i] = i * 0x010101;
-        }
-      }
-    }
-    if (pBitmap->GetBPP() == 1) {
-      uint32_t* pPalette = (uint32_t*)(pbmih + 1);
-      if (pBitmap->GetPalette()) {
-        pPalette[0] = pBitmap->GetPalette()[0];
-        pPalette[1] = pBitmap->GetPalette()[1];
-      } else {
-        pPalette[0] = 0;
-        pPalette[1] = 0xffffff;
-      }
-    }
-  }
-  result.ReleaseBuffer(len);
-  return result;
-}
-
-RetainPtr<CFX_DIBitmap> FX_WindowsDIB_LoadFromBuf(BITMAPINFO* pbmi,
-                                                  LPVOID pData,
-                                                  bool bAlpha) {
-  int width = pbmi->bmiHeader.biWidth;
-  int height = pbmi->bmiHeader.biHeight;
-  BOOL bBottomUp = true;
-  if (height < 0) {
-    height = -height;
-    bBottomUp = false;
-  }
-  int pitch = (width * pbmi->bmiHeader.biBitCount + 31) / 32 * 4;
-  auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  FXDIB_Format format = bAlpha
-                            ? (FXDIB_Format)(pbmi->bmiHeader.biBitCount + 0x200)
-                            : (FXDIB_Format)pbmi->bmiHeader.biBitCount;
-  if (!pBitmap->Create(width, height, format))
-    return nullptr;
-
-  memcpy(pBitmap->GetBuffer(), pData, pitch * height);
-  if (bBottomUp) {
-    std::vector<uint8_t> temp_buf(pitch);
-    int top = 0;
-    int bottom = height - 1;
-    while (top < bottom) {
-      uint8_t* top_ptr = pBitmap->GetBuffer() + top * pitch;
-      uint8_t* bottom_ptr = pBitmap->GetBuffer() + bottom * pitch;
-      memcpy(temp_buf.data(), top_ptr, pitch);
-      memcpy(top_ptr, bottom_ptr, pitch);
-      memcpy(bottom_ptr, temp_buf.data(), pitch);
-      top++;
-      bottom--;
-    }
-  }
-  if (pbmi->bmiHeader.biBitCount == 1) {
-    for (int i = 0; i < 2; i++)
-      pBitmap->SetPaletteArgb(i, ((uint32_t*)pbmi->bmiColors)[i] | 0xff000000);
-  } else if (pbmi->bmiHeader.biBitCount == 8) {
-    for (int i = 0; i < 256; i++)
-      pBitmap->SetPaletteArgb(i, ((uint32_t*)pbmi->bmiColors)[i] | 0xff000000);
-  }
-  return pBitmap;
-}
-
-RetainPtr<CFX_DIBitmap> CFX_WindowsDIB::LoadFromBuf(BITMAPINFO* pbmi,
-                                                    LPVOID pData) {
-  return FX_WindowsDIB_LoadFromBuf(pbmi, pData, false);
-}
-
-HBITMAP CFX_WindowsDIB::GetDDBitmap(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                                    HDC hDC) {
-  ByteString info = GetBitmapInfo(pBitmap);
-  return CreateDIBitmap(hDC, (BITMAPINFOHEADER*)info.c_str(), CBM_INIT,
-                        pBitmap->GetBuffer(), (BITMAPINFO*)info.c_str(),
-                        DIB_RGB_COLORS);
-}
-
-void GetBitmapSize(HBITMAP hBitmap, int& w, int& h) {
-  BITMAP bmp;
-  GetObject(hBitmap, sizeof bmp, &bmp);
-  w = bmp.bmWidth;
-  h = bmp.bmHeight;
-}
-
-RetainPtr<CFX_DIBitmap> CFX_WindowsDIB::LoadFromFile(const wchar_t* filename) {
-  auto* pPlatform =
-      static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
-  if (pPlatform->m_GdiplusExt.IsAvailable()) {
-    WINDIB_Open_Args_ args;
-    args.flags = WINDIB_OPEN_PATHNAME;
-    args.path_name = filename;
-    return pPlatform->m_GdiplusExt.LoadDIBitmap(args);
-  }
-  HBITMAP hBitmap = (HBITMAP)LoadImageW(nullptr, (wchar_t*)filename,
-                                        IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
-  if (!hBitmap) {
-    return nullptr;
-  }
-  HDC hDC = CreateCompatibleDC(nullptr);
-  int width;
-  int height;
-  GetBitmapSize(hBitmap, width, height);
-  auto pDIBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pDIBitmap->Create(width, height, FXDIB_Rgb)) {
-    DeleteDC(hDC);
-    return nullptr;
-  }
-  ByteString info = GetBitmapInfo(pDIBitmap);
-  int ret = GetDIBits(hDC, hBitmap, 0, height, pDIBitmap->GetBuffer(),
-                      (BITMAPINFO*)info.c_str(), DIB_RGB_COLORS);
-  DeleteDC(hDC);
-  if (!ret)
-    return nullptr;
-  return pDIBitmap;
-}
-
-RetainPtr<CFX_DIBitmap> CFX_WindowsDIB::LoadFromFile(const char* filename) {
-  return LoadFromFile(WideString::FromDefANSI(filename).c_str());
-}
-
-RetainPtr<CFX_DIBitmap> CFX_WindowsDIB::LoadDIBitmap(WINDIB_Open_Args_ args) {
-  auto* pPlatform =
-      static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
-  if (pPlatform->m_GdiplusExt.IsAvailable()) {
-    return pPlatform->m_GdiplusExt.LoadDIBitmap(args);
-  }
-  if (args.flags == WINDIB_OPEN_MEMORY) {
-    return nullptr;
-  }
-  HBITMAP hBitmap = (HBITMAP)LoadImageW(nullptr, (wchar_t*)args.path_name,
-                                        IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
-  if (!hBitmap) {
-    return nullptr;
-  }
-  HDC hDC = CreateCompatibleDC(nullptr);
-  int width, height;
-  GetBitmapSize(hBitmap, width, height);
-  auto pDIBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pDIBitmap->Create(width, height, FXDIB_Rgb)) {
-    DeleteDC(hDC);
-    return nullptr;
-  }
-  ByteString info = GetBitmapInfo(pDIBitmap);
-  int ret = GetDIBits(hDC, hBitmap, 0, height, pDIBitmap->GetBuffer(),
-                      (BITMAPINFO*)info.c_str(), DIB_RGB_COLORS);
-  DeleteDC(hDC);
-  if (!ret)
-    return nullptr;
-  return pDIBitmap;
-}
-
-CFX_WindowsDIB::CFX_WindowsDIB(HDC hDC, int width, int height) {
-  Create(width, height, FXDIB_Rgb, (uint8_t*)1, 0);
-  BITMAPINFOHEADER bmih;
-  memset(&bmih, 0, sizeof bmih);
-  bmih.biSize = sizeof bmih;
-  bmih.biBitCount = 24;
-  bmih.biHeight = -height;
-  bmih.biPlanes = 1;
-  bmih.biWidth = width;
-  LPVOID pData = nullptr;
-  m_hBitmap = CreateDIBSection(hDC, (BITMAPINFO*)&bmih, DIB_RGB_COLORS, &pData,
-                               nullptr, 0);
-  m_pBuffer.Reset(static_cast<uint8_t*>(pData));
-  m_hMemDC = CreateCompatibleDC(hDC);
-  m_hOldBitmap = (HBITMAP)SelectObject(m_hMemDC, m_hBitmap);
-}
-
-CFX_WindowsDIB::~CFX_WindowsDIB() {
-  SelectObject(m_hMemDC, m_hOldBitmap);
-  DeleteDC(m_hMemDC);
-  DeleteObject(m_hBitmap);
-}
-
-void CFX_WindowsDIB::LoadFromDevice(HDC hDC, int left, int top) {
-  ::BitBlt(m_hMemDC, 0, 0, m_Width, m_Height, hDC, left, top, SRCCOPY);
-}
-
-void CFX_WindowsDIB::SetToDevice(HDC hDC, int left, int top) {
-  ::BitBlt(hDC, left, top, m_Width, m_Height, m_hMemDC, 0, 0, SRCCOPY);
-}
diff --git a/core/fxge/win32/fx_win32_gdipext.cpp b/core/fxge/win32/fx_win32_gdipext.cpp
deleted file mode 100644
index 272e408..0000000
--- a/core/fxge/win32/fx_win32_gdipext.cpp
+++ /dev/null
@@ -1,989 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include <windows.h>
-
-#include <objidl.h>
-
-#include <algorithm>
-#include <memory>
-#include <sstream>
-#include <utility>
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxge/cfx_gemodule.h"
-#include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
-#include "core/fxge/win32/cfx_windowsdib.h"
-#include "core/fxge/win32/win32_int.h"
-
-// Has to come before gdiplus.h
-namespace Gdiplus {
-using std::max;
-using std::min;
-}  // namespace Gdiplus
-
-#include <gdiplus.h>  // NOLINT
-
-namespace {
-
-enum {
-  FuncId_GdipCreatePath2,
-  FuncId_GdipSetPenDashArray,
-  FuncId_GdipSetPenLineJoin,
-  FuncId_GdipCreateFromHDC,
-  FuncId_GdipSetPageUnit,
-  FuncId_GdipSetSmoothingMode,
-  FuncId_GdipCreateSolidFill,
-  FuncId_GdipFillPath,
-  FuncId_GdipDeleteBrush,
-  FuncId_GdipCreatePen1,
-  FuncId_GdipSetPenMiterLimit,
-  FuncId_GdipDrawPath,
-  FuncId_GdipDeletePen,
-  FuncId_GdipDeletePath,
-  FuncId_GdipDeleteGraphics,
-  FuncId_GdipCreateBitmapFromFileICM,
-  FuncId_GdipCreateBitmapFromStreamICM,
-  FuncId_GdipGetImageHeight,
-  FuncId_GdipGetImageWidth,
-  FuncId_GdipGetImagePixelFormat,
-  FuncId_GdipBitmapLockBits,
-  FuncId_GdipGetImagePaletteSize,
-  FuncId_GdipGetImagePalette,
-  FuncId_GdipBitmapUnlockBits,
-  FuncId_GdipDisposeImage,
-  FuncId_GdipCreateBitmapFromScan0,
-  FuncId_GdipSetImagePalette,
-  FuncId_GdipSetInterpolationMode,
-  FuncId_GdipDrawImagePointsI,
-  FuncId_GdiplusStartup,
-  FuncId_GdipDrawLineI,
-  FuncId_GdipCreatePath,
-  FuncId_GdipSetPathFillMode,
-  FuncId_GdipSetClipRegion,
-  FuncId_GdipWidenPath,
-  FuncId_GdipAddPathLine,
-  FuncId_GdipAddPathRectangle,
-  FuncId_GdipDeleteRegion,
-  FuncId_GdipSetPenLineCap197819,
-  FuncId_GdipSetPenDashOffset,
-  FuncId_GdipCreateMatrix2,
-  FuncId_GdipDeleteMatrix,
-  FuncId_GdipSetWorldTransform,
-  FuncId_GdipSetPixelOffsetMode,
-};
-
-LPCSTR g_GdipFuncNames[] = {
-    "GdipCreatePath2",
-    "GdipSetPenDashArray",
-    "GdipSetPenLineJoin",
-    "GdipCreateFromHDC",
-    "GdipSetPageUnit",
-    "GdipSetSmoothingMode",
-    "GdipCreateSolidFill",
-    "GdipFillPath",
-    "GdipDeleteBrush",
-    "GdipCreatePen1",
-    "GdipSetPenMiterLimit",
-    "GdipDrawPath",
-    "GdipDeletePen",
-    "GdipDeletePath",
-    "GdipDeleteGraphics",
-    "GdipCreateBitmapFromFileICM",
-    "GdipCreateBitmapFromStreamICM",
-    "GdipGetImageHeight",
-    "GdipGetImageWidth",
-    "GdipGetImagePixelFormat",
-    "GdipBitmapLockBits",
-    "GdipGetImagePaletteSize",
-    "GdipGetImagePalette",
-    "GdipBitmapUnlockBits",
-    "GdipDisposeImage",
-    "GdipCreateBitmapFromScan0",
-    "GdipSetImagePalette",
-    "GdipSetInterpolationMode",
-    "GdipDrawImagePointsI",
-    "GdiplusStartup",
-    "GdipDrawLineI",
-    "GdipCreatePath",
-    "GdipSetPathFillMode",
-    "GdipSetClipRegion",
-    "GdipWidenPath",
-    "GdipAddPathLine",
-    "GdipAddPathRectangle",
-    "GdipDeleteRegion",
-    "GdipSetPenLineCap197819",
-    "GdipSetPenDashOffset",
-    "GdipCreateMatrix2",
-    "GdipDeleteMatrix",
-    "GdipSetWorldTransform",
-    "GdipSetPixelOffsetMode",
-};
-static_assert(FX_ArraySize(g_GdipFuncNames) ==
-                  static_cast<size_t>(FuncId_GdipSetPixelOffsetMode) + 1,
-              "g_GdipFuncNames has wrong size");
-
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreatePath2)(
-    GDIPCONST Gdiplus::GpPointF*,
-    GDIPCONST BYTE*,
-    INT,
-    Gdiplus::GpFillMode,
-    Gdiplus::GpPath** path);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashArray)(
-    Gdiplus::GpPen* pen,
-    GDIPCONST Gdiplus::REAL* dash,
-    INT count);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenLineJoin)(
-    Gdiplus::GpPen* pen,
-    Gdiplus::GpLineJoin lineJoin);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateFromHDC)(
-    HDC hdc,
-    Gdiplus::GpGraphics** graphics);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPageUnit)(
-    Gdiplus::GpGraphics* graphics,
-    Gdiplus::GpUnit unit);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetSmoothingMode)(
-    Gdiplus::GpGraphics* graphics,
-    Gdiplus::SmoothingMode smoothingMode);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateSolidFill)(
-    Gdiplus::ARGB color,
-    Gdiplus::GpSolidFill** brush);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipFillPath)(
-    Gdiplus::GpGraphics* graphics,
-    Gdiplus::GpBrush* brush,
-    Gdiplus::GpPath* path);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeleteBrush)(
-    Gdiplus::GpBrush* brush);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreatePen1)(
-    Gdiplus::ARGB color,
-    Gdiplus::REAL width,
-    Gdiplus::GpUnit unit,
-    Gdiplus::GpPen** pen);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenMiterLimit)(
-    Gdiplus::GpPen* pen,
-    Gdiplus::REAL miterLimit);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDrawPath)(
-    Gdiplus::GpGraphics* graphics,
-    Gdiplus::GpPen* pen,
-    Gdiplus::GpPath* path);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeletePen)(
-    Gdiplus::GpPen* pen);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeletePath)(
-    Gdiplus::GpPath* path);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeleteGraphics)(
-    Gdiplus::GpGraphics* graphics);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromFileICM)(
-    GDIPCONST WCHAR* filename,
-    Gdiplus::GpBitmap** bitmap);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromStreamICM)(
-    IStream* stream,
-    Gdiplus::GpBitmap** bitmap);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImageWidth)(
-    Gdiplus::GpImage* image,
-    UINT* width);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImageHeight)(
-    Gdiplus::GpImage* image,
-    UINT* height);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImagePixelFormat)(
-    Gdiplus::GpImage* image,
-    Gdiplus::PixelFormat* format);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipBitmapLockBits)(
-    Gdiplus::GpBitmap* bitmap,
-    GDIPCONST Gdiplus::GpRect* rect,
-    UINT flags,
-    Gdiplus::PixelFormat format,
-    Gdiplus::BitmapData* lockedBitmapData);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImagePalette)(
-    Gdiplus::GpImage* image,
-    Gdiplus::ColorPalette* palette,
-    INT size);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImagePaletteSize)(
-    Gdiplus::GpImage* image,
-    INT* size);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipBitmapUnlockBits)(
-    Gdiplus::GpBitmap* bitmap,
-    Gdiplus::BitmapData* lockedBitmapData);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDisposeImage)(
-    Gdiplus::GpImage* image);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromScan0)(
-    INT width,
-    INT height,
-    INT stride,
-    Gdiplus::PixelFormat format,
-    BYTE* scan0,
-    Gdiplus::GpBitmap** bitmap);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetImagePalette)(
-    Gdiplus::GpImage* image,
-    GDIPCONST Gdiplus::ColorPalette* palette);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetInterpolationMode)(
-    Gdiplus::GpGraphics* graphics,
-    Gdiplus::InterpolationMode interpolationMode);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDrawImagePointsI)(
-    Gdiplus::GpGraphics* graphics,
-    Gdiplus::GpImage* image,
-    GDIPCONST Gdiplus::GpPoint* dstpoints,
-    INT count);
-typedef Gdiplus::Status(WINAPI* FuncType_GdiplusStartup)(
-    OUT uintptr_t* token,
-    const Gdiplus::GdiplusStartupInput* input,
-    OUT Gdiplus::GdiplusStartupOutput* output);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDrawLineI)(
-    Gdiplus::GpGraphics* graphics,
-    Gdiplus::GpPen* pen,
-    int x1,
-    int y1,
-    int x2,
-    int y2);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreatePath)(
-    Gdiplus::GpFillMode brushMode,
-    Gdiplus::GpPath** path);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPathFillMode)(
-    Gdiplus::GpPath* path,
-    Gdiplus::GpFillMode fillmode);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetClipRegion)(
-    Gdiplus::GpGraphics* graphics,
-    Gdiplus::GpRegion* region,
-    Gdiplus::CombineMode combineMode);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipWidenPath)(
-    Gdiplus::GpPath* nativePath,
-    Gdiplus::GpPen* pen,
-    Gdiplus::GpMatrix* matrix,
-    Gdiplus::REAL flatness);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipAddPathLine)(
-    Gdiplus::GpPath* path,
-    Gdiplus::REAL x1,
-    Gdiplus::REAL y1,
-    Gdiplus::REAL x2,
-    Gdiplus::REAL y2);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipAddPathRectangle)(
-    Gdiplus::GpPath* path,
-    Gdiplus::REAL x,
-    Gdiplus::REAL y,
-    Gdiplus::REAL width,
-    Gdiplus::REAL height);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeleteRegion)(
-    Gdiplus::GpRegion* region);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenLineCap197819)(
-    Gdiplus::GpPen* pen,
-    Gdiplus::GpLineCap startCap,
-    Gdiplus::GpLineCap endCap,
-    Gdiplus::GpDashCap dashCap);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashOffset)(
-    Gdiplus::GpPen* pen,
-    Gdiplus::REAL offset);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateMatrix2)(
-    Gdiplus::REAL m11,
-    Gdiplus::REAL m12,
-    Gdiplus::REAL m21,
-    Gdiplus::REAL m22,
-    Gdiplus::REAL dx,
-    Gdiplus::REAL dy,
-    Gdiplus::GpMatrix** matrix);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeleteMatrix)(
-    Gdiplus::GpMatrix* matrix);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetWorldTransform)(
-    Gdiplus::GpGraphics* graphics,
-    Gdiplus::GpMatrix* matrix);
-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPixelOffsetMode)(
-    Gdiplus::GpGraphics* graphics,
-    Gdiplus::PixelOffsetMode pixelOffsetMode);
-#define CallFunc(funcname)               \
-  reinterpret_cast<FuncType_##funcname>( \
-      GdiplusExt.m_Functions[FuncId_##funcname])
-
-Gdiplus::GpFillMode GdiFillType2Gdip(int fill_type) {
-  return fill_type == ALTERNATE ? Gdiplus::FillModeAlternate
-                                : Gdiplus::FillModeWinding;
-}
-
-const CGdiplusExt& GetGdiplusExt() {
-  auto* pData =
-      static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
-  return pData->m_GdiplusExt;
-}
-
-Gdiplus::GpBrush* GdipCreateBrushImpl(DWORD argb) {
-  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
-  Gdiplus::GpSolidFill* solidBrush = nullptr;
-  CallFunc(GdipCreateSolidFill)((Gdiplus::ARGB)argb, &solidBrush);
-  return solidBrush;
-}
-
-void OutputImage(Gdiplus::GpGraphics* pGraphics,
-                 const RetainPtr<CFX_DIBitmap>& pBitmap,
-                 const FX_RECT* pSrcRect,
-                 int dest_left,
-                 int dest_top,
-                 int dest_width,
-                 int dest_height) {
-  int src_width = pSrcRect->Width(), src_height = pSrcRect->Height();
-  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
-  if (pBitmap->GetBPP() == 1 && (pSrcRect->left % 8)) {
-    FX_RECT new_rect(0, 0, src_width, src_height);
-    RetainPtr<CFX_DIBitmap> pCloned = pBitmap->Clone(pSrcRect);
-    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() + pSrcRect->top * src_pitch +
-                   pBitmap->GetBPP() * pSrcRect->left / 8;
-  Gdiplus::GpBitmap* bitmap = nullptr;
-  switch (pBitmap->GetFormat()) {
-    case FXDIB_Argb:
-      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
-                                          PixelFormat32bppARGB, scan0, &bitmap);
-      break;
-    case FXDIB_Rgb32:
-      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
-                                          PixelFormat32bppRGB, scan0, &bitmap);
-      break;
-    case FXDIB_Rgb:
-      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
-                                          PixelFormat24bppRGB, scan0, &bitmap);
-      break;
-    case FXDIB_8bppRgb: {
-      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
-                                          PixelFormat8bppIndexed, scan0,
-                                          &bitmap);
-      UINT pal[258];
-      pal[0] = 0;
-      pal[1] = 256;
-      for (int i = 0; i < 256; i++)
-        pal[i + 2] = pBitmap->GetPaletteArgb(i);
-      CallFunc(GdipSetImagePalette)(bitmap, (Gdiplus::ColorPalette*)pal);
-      break;
-    }
-    case FXDIB_1bppRgb: {
-      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
-                                          PixelFormat1bppIndexed, scan0,
-                                          &bitmap);
-      break;
-    }
-  }
-  if (dest_height < 0) {
-    dest_height--;
-  }
-  if (dest_width < 0) {
-    dest_width--;
-  }
-  Gdiplus::Point destinationPoints[] = {
-      Gdiplus::Point(dest_left, dest_top),
-      Gdiplus::Point(dest_left + dest_width, dest_top),
-      Gdiplus::Point(dest_left, dest_top + dest_height)};
-  CallFunc(GdipDrawImagePointsI)(pGraphics, bitmap, destinationPoints, 3);
-  CallFunc(GdipDisposeImage)(bitmap);
-}
-
-Gdiplus::GpPen* GdipCreatePenImpl(const CFX_GraphStateData* pGraphState,
-                                  const CFX_Matrix* pMatrix,
-                                  DWORD argb,
-                                  bool bTextMode) {
-  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
-  float width = pGraphState->m_LineWidth;
-  if (!bTextMode) {
-    float unit = pMatrix
-                     ? 1.0f / ((pMatrix->GetXUnit() + pMatrix->GetYUnit()) / 2)
-                     : 1.0f;
-    width = std::max(width, unit);
-  }
-  Gdiplus::GpPen* pPen = nullptr;
-  CallFunc(GdipCreatePen1)((Gdiplus::ARGB)argb, width, Gdiplus::UnitWorld,
-                           &pPen);
-  Gdiplus::LineCap lineCap = Gdiplus::LineCapFlat;
-  Gdiplus::DashCap dashCap = Gdiplus::DashCapFlat;
-  bool bDashExtend = false;
-  switch (pGraphState->m_LineCap) {
-    case CFX_GraphStateData::LineCapButt:
-      lineCap = Gdiplus::LineCapFlat;
-      break;
-    case CFX_GraphStateData::LineCapRound:
-      lineCap = Gdiplus::LineCapRound;
-      dashCap = Gdiplus::DashCapRound;
-      bDashExtend = true;
-      break;
-    case CFX_GraphStateData::LineCapSquare:
-      lineCap = Gdiplus::LineCapSquare;
-      bDashExtend = true;
-      break;
-  }
-  CallFunc(GdipSetPenLineCap197819)(pPen, lineCap, lineCap, dashCap);
-  Gdiplus::LineJoin lineJoin = Gdiplus::LineJoinMiterClipped;
-  switch (pGraphState->m_LineJoin) {
-    case CFX_GraphStateData::LineJoinMiter:
-      lineJoin = Gdiplus::LineJoinMiterClipped;
-      break;
-    case CFX_GraphStateData::LineJoinRound:
-      lineJoin = Gdiplus::LineJoinRound;
-      break;
-    case CFX_GraphStateData::LineJoinBevel:
-      lineJoin = Gdiplus::LineJoinBevel;
-      break;
-  }
-  CallFunc(GdipSetPenLineJoin)(pPen, lineJoin);
-  if (!pGraphState->m_DashArray.empty()) {
-    float* pDashArray =
-        FX_Alloc(float, FxAlignToBoundary<2>(pGraphState->m_DashArray.size()));
-    int nCount = 0;
-    float on_leftover = 0, off_leftover = 0;
-    for (size_t i = 0; i < pGraphState->m_DashArray.size(); i += 2) {
-      float on_phase = pGraphState->m_DashArray[i];
-      float off_phase;
-      if (i == pGraphState->m_DashArray.size() - 1)
-        off_phase = on_phase;
-      else
-        off_phase = pGraphState->m_DashArray[i + 1];
-      on_phase /= width;
-      off_phase /= width;
-      if (on_phase + off_phase <= 0.00002f) {
-        on_phase = 1.0f / 10;
-        off_phase = 1.0f / 10;
-      }
-      if (bDashExtend) {
-        if (off_phase < 1)
-          off_phase = 0;
-        else
-          off_phase -= 1;
-        on_phase += 1;
-      }
-      if (on_phase == 0 || off_phase == 0) {
-        if (nCount == 0) {
-          on_leftover += on_phase;
-          off_leftover += off_phase;
-        } else {
-          pDashArray[nCount - 2] += on_phase;
-          pDashArray[nCount - 1] += off_phase;
-        }
-      } else {
-        pDashArray[nCount++] = on_phase + on_leftover;
-        on_leftover = 0;
-        pDashArray[nCount++] = off_phase + off_leftover;
-        off_leftover = 0;
-      }
-    }
-    CallFunc(GdipSetPenDashArray)(pPen, pDashArray, nCount);
-    float phase = pGraphState->m_DashPhase;
-    if (bDashExtend) {
-      if (phase < 0.5f)
-        phase = 0;
-      else
-        phase -= 0.5f;
-    }
-    CallFunc(GdipSetPenDashOffset)(pPen, phase);
-    FX_Free(pDashArray);
-    pDashArray = nullptr;
-  }
-  CallFunc(GdipSetPenMiterLimit)(pPen, pGraphState->m_MiterLimit);
-  return pPen;
-}
-
-Optional<std::pair<size_t, size_t>> IsSmallTriangle(Gdiplus::PointF* points,
-                                                    const CFX_Matrix* pMatrix) {
-  size_t pairs[] = {1, 2, 0, 2, 0, 1};
-  for (size_t i = 0; i < FX_ArraySize(pairs) / 2; i++) {
-    size_t pair1 = pairs[i * 2];
-    size_t pair2 = pairs[i * 2 + 1];
-
-    CFX_PointF p1(points[pair1].X, points[pair1].Y);
-    CFX_PointF p2(points[pair2].X, points[pair2].Y);
-    if (pMatrix) {
-      p1 = pMatrix->Transform(p1);
-      p2 = pMatrix->Transform(p2);
-    }
-
-    CFX_PointF diff = p1 - p2;
-    float distance_square = (diff.x * diff.x) + (diff.y * diff.y);
-    if (distance_square < (1.0f * 2 + 1.0f / 4))
-      return std::make_pair(i, pair1);
-  }
-  return {};
-}
-
-class GpStream final : public IStream {
- public:
-  GpStream() : m_RefCount(1), m_ReadPos(0) {}
-  ~GpStream() = default;
-
-  // IUnknown
-  HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
-                                           void** ppvObject) override {
-    if (iid == __uuidof(IUnknown) || iid == __uuidof(IStream) ||
-        iid == __uuidof(ISequentialStream)) {
-      *ppvObject = static_cast<IStream*>(this);
-      AddRef();
-      return S_OK;
-    }
-    return E_NOINTERFACE;
-  }
-  ULONG STDMETHODCALLTYPE AddRef() override {
-    return (ULONG)InterlockedIncrement(&m_RefCount);
-  }
-  ULONG STDMETHODCALLTYPE Release() override {
-    ULONG res = (ULONG)InterlockedDecrement(&m_RefCount);
-    if (res == 0) {
-      delete this;
-    }
-    return res;
-  }
-
-  // ISequentialStream
-  HRESULT STDMETHODCALLTYPE Read(void* output,
-                                 ULONG cb,
-                                 ULONG* pcbRead) override {
-    if (pcbRead)
-      *pcbRead = 0;
-
-    if (m_ReadPos >= m_InterStream.tellp())
-      return HRESULT_FROM_WIN32(ERROR_END_OF_MEDIA);
-
-    size_t bytes_left = m_InterStream.tellp() - m_ReadPos;
-    size_t bytes_out =
-        std::min(pdfium::base::checked_cast<size_t>(cb), bytes_left);
-    memcpy(output, m_InterStream.str().c_str() + m_ReadPos, bytes_out);
-    m_ReadPos += bytes_out;
-    if (pcbRead)
-      *pcbRead = (ULONG)bytes_out;
-
-    return S_OK;
-  }
-  HRESULT STDMETHODCALLTYPE Write(const void* input,
-                                  ULONG cb,
-                                  ULONG* pcbWritten) override {
-    if (cb <= 0) {
-      if (pcbWritten)
-        *pcbWritten = 0;
-      return S_OK;
-    }
-    m_InterStream.write(reinterpret_cast<const char*>(input), cb);
-    if (pcbWritten)
-      *pcbWritten = cb;
-    return S_OK;
-  }
-
-  // IStream
-  HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER) override {
-    return E_NOTIMPL;
-  }
-  HRESULT STDMETHODCALLTYPE CopyTo(IStream*,
-                                   ULARGE_INTEGER,
-                                   ULARGE_INTEGER*,
-                                   ULARGE_INTEGER*) override {
-    return E_NOTIMPL;
-  }
-  HRESULT STDMETHODCALLTYPE Commit(DWORD) override { return E_NOTIMPL; }
-  HRESULT STDMETHODCALLTYPE Revert() override { return E_NOTIMPL; }
-  HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER,
-                                       ULARGE_INTEGER,
-                                       DWORD) override {
-    return E_NOTIMPL;
-  }
-  HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER,
-                                         ULARGE_INTEGER,
-                                         DWORD) override {
-    return E_NOTIMPL;
-  }
-  HRESULT STDMETHODCALLTYPE Clone(IStream** stream) override {
-    return E_NOTIMPL;
-  }
-  HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove,
-                                 DWORD dwOrigin,
-                                 ULARGE_INTEGER* lpNewFilePointer) override {
-    std::streamoff start;
-    std::streamoff new_read_position;
-    switch (dwOrigin) {
-      case STREAM_SEEK_SET:
-        start = 0;
-        break;
-      case STREAM_SEEK_CUR:
-        start = m_ReadPos;
-        break;
-      case STREAM_SEEK_END:
-        if (m_InterStream.tellp() < 0)
-          return STG_E_SEEKERROR;
-        start = m_InterStream.tellp();
-        break;
-      default:
-        return STG_E_INVALIDFUNCTION;
-    }
-    new_read_position = start + liDistanceToMove.QuadPart;
-    if (new_read_position > m_InterStream.tellp())
-      return STG_E_SEEKERROR;
-
-    m_ReadPos = new_read_position;
-    if (lpNewFilePointer)
-      lpNewFilePointer->QuadPart = m_ReadPos;
-
-    return S_OK;
-  }
-  HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg,
-                                 DWORD grfStatFlag) override {
-    if (!pStatstg)
-      return STG_E_INVALIDFUNCTION;
-
-    ZeroMemory(pStatstg, sizeof(STATSTG));
-
-    if (m_InterStream.tellp() < 0)
-      return STG_E_SEEKERROR;
-
-    pStatstg->cbSize.QuadPart = m_InterStream.tellp();
-    return S_OK;
-  }
-
- private:
-  LONG m_RefCount;
-  std::streamoff m_ReadPos;
-  std::ostringstream m_InterStream;
-};
-
-struct PREVIEW3_DIBITMAP {
-  BITMAPINFO* pbmi;
-  int Stride;
-  LPBYTE pScan0;
-  Gdiplus::GpBitmap* pBitmap;
-  Gdiplus::BitmapData* pBitmapData;
-  GpStream* pStream;
-};
-
-PREVIEW3_DIBITMAP* LoadDIBitmap(WINDIB_Open_Args_ args) {
-  Gdiplus::GpBitmap* pBitmap;
-  GpStream* pStream = nullptr;
-  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
-  Gdiplus::Status status = Gdiplus::Ok;
-  if (args.flags == WINDIB_OPEN_PATHNAME) {
-    status = CallFunc(GdipCreateBitmapFromFileICM)(args.path_name, &pBitmap);
-  } else {
-    if (args.memory_size == 0 || !args.memory_base)
-      return nullptr;
-
-    pStream = new GpStream;
-    pStream->Write(args.memory_base, (ULONG)args.memory_size, nullptr);
-    status = CallFunc(GdipCreateBitmapFromStreamICM)(pStream, &pBitmap);
-  }
-  if (status != Gdiplus::Ok) {
-    if (pStream)
-      pStream->Release();
-
-    return nullptr;
-  }
-  UINT height, width;
-  CallFunc(GdipGetImageHeight)(pBitmap, &height);
-  CallFunc(GdipGetImageWidth)(pBitmap, &width);
-  Gdiplus::PixelFormat pixel_format;
-  CallFunc(GdipGetImagePixelFormat)(pBitmap, &pixel_format);
-  int info_size = sizeof(BITMAPINFOHEADER);
-  int bpp = 24;
-  int dest_pixel_format = PixelFormat24bppRGB;
-  if (pixel_format == PixelFormat1bppIndexed) {
-    info_size += 8;
-    bpp = 1;
-    dest_pixel_format = PixelFormat1bppIndexed;
-  } else if (pixel_format == PixelFormat8bppIndexed) {
-    info_size += 1024;
-    bpp = 8;
-    dest_pixel_format = PixelFormat8bppIndexed;
-  } else if (pixel_format == PixelFormat32bppARGB) {
-    bpp = 32;
-    dest_pixel_format = PixelFormat32bppARGB;
-  }
-  LPBYTE buf = FX_Alloc(BYTE, info_size);
-  BITMAPINFOHEADER* pbmih = (BITMAPINFOHEADER*)buf;
-  pbmih->biBitCount = bpp;
-  pbmih->biCompression = BI_RGB;
-  pbmih->biHeight = -(int)height;
-  pbmih->biPlanes = 1;
-  pbmih->biWidth = width;
-  Gdiplus::Rect rect(0, 0, width, height);
-  Gdiplus::BitmapData* pBitmapData = FX_Alloc(Gdiplus::BitmapData, 1);
-  CallFunc(GdipBitmapLockBits)(pBitmap, &rect, Gdiplus::ImageLockModeRead,
-                               dest_pixel_format, pBitmapData);
-  if (pixel_format == PixelFormat1bppIndexed ||
-      pixel_format == PixelFormat8bppIndexed) {
-    DWORD* ppal = (DWORD*)(buf + sizeof(BITMAPINFOHEADER));
-    struct {
-      UINT flags;
-      UINT Count;
-      DWORD Entries[256];
-    } pal;
-    int size = 0;
-    CallFunc(GdipGetImagePaletteSize)(pBitmap, &size);
-    CallFunc(GdipGetImagePalette)(pBitmap, (Gdiplus::ColorPalette*)&pal, size);
-    int entries = pixel_format == PixelFormat1bppIndexed ? 2 : 256;
-    for (int i = 0; i < entries; i++) {
-      ppal[i] = pal.Entries[i] & 0x00ffffff;
-    }
-  }
-  PREVIEW3_DIBITMAP* pInfo = FX_Alloc(PREVIEW3_DIBITMAP, 1);
-  pInfo->pbmi = (BITMAPINFO*)buf;
-  pInfo->pScan0 = (LPBYTE)pBitmapData->Scan0;
-  pInfo->Stride = pBitmapData->Stride;
-  pInfo->pBitmap = pBitmap;
-  pInfo->pBitmapData = pBitmapData;
-  pInfo->pStream = pStream;
-  return pInfo;
-}
-
-void FreeDIBitmap(PREVIEW3_DIBITMAP* pInfo) {
-  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
-  CallFunc(GdipBitmapUnlockBits)(pInfo->pBitmap, pInfo->pBitmapData);
-  CallFunc(GdipDisposeImage)(pInfo->pBitmap);
-  FX_Free(pInfo->pBitmapData);
-  FX_Free((LPBYTE)pInfo->pbmi);
-  if (pInfo->pStream)
-    pInfo->pStream->Release();
-  FX_Free(pInfo);
-}
-
-}  // namespace
-
-CGdiplusExt::CGdiplusExt() {}
-
-CGdiplusExt::~CGdiplusExt() {
-  FreeLibrary(m_GdiModule);
-  FreeLibrary(m_hModule);
-}
-
-void CGdiplusExt::Load() {
-  char buf[MAX_PATH];
-  GetSystemDirectoryA(buf, MAX_PATH);
-  ByteString dllpath = buf;
-  dllpath += "\\GDIPLUS.DLL";
-  m_hModule = LoadLibraryA(dllpath.c_str());
-  if (!m_hModule)
-    return;
-
-  m_Functions.resize(FX_ArraySize(g_GdipFuncNames));
-  for (size_t i = 0; i < FX_ArraySize(g_GdipFuncNames); ++i) {
-    m_Functions[i] = GetProcAddress(m_hModule, g_GdipFuncNames[i]);
-    if (!m_Functions[i]) {
-      m_hModule = nullptr;
-      return;
-    }
-  }
-
-  uintptr_t gdiplusToken;
-  Gdiplus::GdiplusStartupInput gdiplusStartupInput;
-  ((FuncType_GdiplusStartup)m_Functions[FuncId_GdiplusStartup])(
-      &gdiplusToken, &gdiplusStartupInput, nullptr);
-  m_GdiModule = LoadLibraryA("GDI32.DLL");
-}
-
-bool CGdiplusExt::StretchDIBits(HDC hDC,
-                                const RetainPtr<CFX_DIBitmap>& pBitmap,
-                                int dest_left,
-                                int dest_top,
-                                int dest_width,
-                                int dest_height,
-                                const FX_RECT* pClipRect,
-                                const FXDIB_ResampleOptions& options) {
-  Gdiplus::GpGraphics* pGraphics;
-  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
-  CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
-  CallFunc(GdipSetPageUnit)(pGraphics, Gdiplus::UnitPixel);
-  if (options.bNoSmoothing) {
-    CallFunc(GdipSetInterpolationMode)(
-        pGraphics, Gdiplus::InterpolationModeNearestNeighbor);
-  } else if (pBitmap->GetWidth() > abs(dest_width) / 2 ||
-             pBitmap->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,
-              dest_height);
-  CallFunc(GdipDeleteGraphics)(pGraphics);
-  CallFunc(GdipDeleteGraphics)(pGraphics);
-  return true;
-}
-
-bool CGdiplusExt::DrawPath(HDC hDC,
-                           const CFX_PathData* pPathData,
-                           const CFX_Matrix* pObject2Device,
-                           const CFX_GraphStateData* pGraphState,
-                           uint32_t fill_argb,
-                           uint32_t stroke_argb,
-                           int fill_mode) {
-  auto& pPoints = pPathData->GetPoints();
-  if (pPoints.empty())
-    return true;
-
-  Gdiplus::GpGraphics* pGraphics = nullptr;
-  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
-  CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
-  CallFunc(GdipSetPageUnit)(pGraphics, Gdiplus::UnitPixel);
-  CallFunc(GdipSetPixelOffsetMode)(pGraphics, Gdiplus::PixelOffsetModeHalf);
-  Gdiplus::GpMatrix* pMatrix = nullptr;
-  if (pObject2Device) {
-    CallFunc(GdipCreateMatrix2)(pObject2Device->a, pObject2Device->b,
-                                pObject2Device->c, pObject2Device->d,
-                                pObject2Device->e, pObject2Device->f, &pMatrix);
-    CallFunc(GdipSetWorldTransform)(pGraphics, pMatrix);
-  }
-  Gdiplus::PointF* points = FX_Alloc(Gdiplus::PointF, pPoints.size());
-  BYTE* types = FX_Alloc(BYTE, pPoints.size());
-  int nSubPathes = 0;
-  bool bSubClose = false;
-  int pos_subclose = 0;
-  bool bSmooth = false;
-  int startpoint = 0;
-  for (size_t i = 0; i < pPoints.size(); i++) {
-    points[i].X = pPoints[i].m_Point.x;
-    points[i].Y = pPoints[i].m_Point.y;
-
-    CFX_PointF pos = pPoints[i].m_Point;
-    if (pObject2Device)
-      pos = pObject2Device->Transform(pos);
-
-    if (pos.x > 50000 * 1.0f)
-      points[i].X = 50000 * 1.0f;
-    if (pos.x < -50000 * 1.0f)
-      points[i].X = -50000 * 1.0f;
-    if (pos.y > 50000 * 1.0f)
-      points[i].Y = 50000 * 1.0f;
-    if (pos.y < -50000 * 1.0f)
-      points[i].Y = -50000 * 1.0f;
-
-    FXPT_TYPE point_type = pPoints[i].m_Type;
-    if (point_type == FXPT_TYPE::MoveTo) {
-      types[i] = Gdiplus::PathPointTypeStart;
-      nSubPathes++;
-      bSubClose = false;
-      startpoint = i;
-    } else if (point_type == FXPT_TYPE::LineTo) {
-      types[i] = Gdiplus::PathPointTypeLine;
-      if (pPoints[i - 1].IsTypeAndOpen(FXPT_TYPE::MoveTo) &&
-          (i == pPoints.size() - 1 ||
-           pPoints[i + 1].IsTypeAndOpen(FXPT_TYPE::MoveTo)) &&
-          points[i].Y == points[i - 1].Y && points[i].X == points[i - 1].X) {
-        points[i].X += 0.01f;
-        continue;
-      }
-      if (!bSmooth && points[i].X != points[i - 1].X &&
-          points[i].Y != points[i - 1].Y)
-        bSmooth = true;
-    } else if (point_type == FXPT_TYPE::BezierTo) {
-      types[i] = Gdiplus::PathPointTypeBezier;
-      bSmooth = true;
-    }
-    if (pPoints[i].m_CloseFigure) {
-      if (bSubClose)
-        types[pos_subclose] &= ~Gdiplus::PathPointTypeCloseSubpath;
-      else
-        bSubClose = true;
-      pos_subclose = i;
-      types[i] |= Gdiplus::PathPointTypeCloseSubpath;
-      if (!bSmooth && points[i].X != points[startpoint].X &&
-          points[i].Y != points[startpoint].Y)
-        bSmooth = true;
-    }
-  }
-  if (fill_mode & FXFILL_NOPATHSMOOTH) {
-    bSmooth = false;
-    CallFunc(GdipSetSmoothingMode)(pGraphics, Gdiplus::SmoothingModeNone);
-  } else if (!(fill_mode & FXFILL_FULLCOVER)) {
-    if (!bSmooth && (fill_mode & 3))
-      bSmooth = true;
-
-    if (bSmooth || (pGraphState && pGraphState->m_LineWidth > 2)) {
-      CallFunc(GdipSetSmoothingMode)(pGraphics,
-                                     Gdiplus::SmoothingModeAntiAlias);
-    }
-  }
-  int new_fill_mode = fill_mode & 3;
-  if (pPoints.size() == 4 && !pGraphState) {
-    auto indices = IsSmallTriangle(points, pObject2Device);
-    if (indices.has_value()) {
-      size_t v1;
-      size_t v2;
-      std::tie(v1, v2) = indices.value();
-      Gdiplus::GpPen* pPen = nullptr;
-      CallFunc(GdipCreatePen1)(fill_argb, 1.0f, Gdiplus::UnitPixel, &pPen);
-      CallFunc(GdipDrawLineI)(pGraphics, pPen, FXSYS_roundf(points[v1].X),
-                              FXSYS_roundf(points[v1].Y),
-                              FXSYS_roundf(points[v2].X),
-                              FXSYS_roundf(points[v2].Y));
-      CallFunc(GdipDeletePen)(pPen);
-      return true;
-    }
-  }
-  Gdiplus::GpPath* pGpPath = nullptr;
-  CallFunc(GdipCreatePath2)(points, types, pPoints.size(),
-                            GdiFillType2Gdip(new_fill_mode), &pGpPath);
-  if (!pGpPath) {
-    if (pMatrix)
-      CallFunc(GdipDeleteMatrix)(pMatrix);
-
-    FX_Free(points);
-    FX_Free(types);
-    CallFunc(GdipDeleteGraphics)(pGraphics);
-    return false;
-  }
-  if (new_fill_mode) {
-    Gdiplus::GpBrush* pBrush = GdipCreateBrushImpl(fill_argb);
-    CallFunc(GdipSetPathFillMode)(pGpPath, GdiFillType2Gdip(new_fill_mode));
-    CallFunc(GdipFillPath)(pGraphics, pBrush, pGpPath);
-    CallFunc(GdipDeleteBrush)(pBrush);
-  }
-  if (pGraphState && stroke_argb) {
-    Gdiplus::GpPen* pPen =
-        GdipCreatePenImpl(pGraphState, pObject2Device, stroke_argb,
-                          !!(fill_mode & FX_STROKE_TEXT_MODE));
-    if (nSubPathes == 1) {
-      CallFunc(GdipDrawPath)(pGraphics, pPen, pGpPath);
-    } else {
-      int iStart = 0;
-      for (size_t i = 0; i < pPoints.size(); i++) {
-        if (i == pPoints.size() - 1 ||
-            types[i + 1] == Gdiplus::PathPointTypeStart) {
-          Gdiplus::GpPath* pSubPath;
-          CallFunc(GdipCreatePath2)(points + iStart, types + iStart,
-                                    i - iStart + 1,
-                                    GdiFillType2Gdip(new_fill_mode), &pSubPath);
-          iStart = i + 1;
-          CallFunc(GdipDrawPath)(pGraphics, pPen, pSubPath);
-          CallFunc(GdipDeletePath)(pSubPath);
-        }
-      }
-    }
-    CallFunc(GdipDeletePen)(pPen);
-  }
-  if (pMatrix)
-    CallFunc(GdipDeleteMatrix)(pMatrix);
-  FX_Free(points);
-  FX_Free(types);
-  CallFunc(GdipDeletePath)(pGpPath);
-  CallFunc(GdipDeleteGraphics)(pGraphics);
-  return true;
-}
-
-RetainPtr<CFX_DIBitmap> CGdiplusExt::LoadDIBitmap(WINDIB_Open_Args_ args) {
-  PREVIEW3_DIBITMAP* pInfo = ::LoadDIBitmap(args);
-  if (!pInfo)
-    return nullptr;
-
-  int height = abs(pInfo->pbmi->bmiHeader.biHeight);
-  int width = pInfo->pbmi->bmiHeader.biWidth;
-  int dest_pitch = (width * pInfo->pbmi->bmiHeader.biBitCount + 31) / 32 * 4;
-  LPBYTE pData = FX_Alloc2D(BYTE, dest_pitch, height);
-  if (dest_pitch == pInfo->Stride) {
-    memcpy(pData, pInfo->pScan0, dest_pitch * height);
-  } else {
-    for (int i = 0; i < height; i++) {
-      memcpy(pData + dest_pitch * i, pInfo->pScan0 + pInfo->Stride * i,
-             dest_pitch);
-    }
-  }
-  RetainPtr<CFX_DIBitmap> pDIBitmap = FX_WindowsDIB_LoadFromBuf(
-      pInfo->pbmi, pData, pInfo->pbmi->bmiHeader.biBitCount == 32);
-  FX_Free(pData);
-  FreeDIBitmap(pInfo);
-  return pDIBitmap;
-}
diff --git a/core/fxge/win32/fx_win32_print.cpp b/core/fxge/win32/fx_win32_print.cpp
deleted file mode 100644
index 9ae092a..0000000
--- a/core/fxge/win32/fx_win32_print.cpp
+++ /dev/null
@@ -1,673 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include <windows.h>
-
-#include <algorithm>
-#include <memory>
-#include <vector>
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxge/cfx_font.h"
-#include "core/fxge/cfx_windowsrenderdevice.h"
-#include "core/fxge/dib/cfx_dibextractor.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/dib/cfx_imagerenderer.h"
-#include "core/fxge/dib/cstretchengine.h"
-#include "core/fxge/fx_freetype.h"
-#include "core/fxge/text_char_pos.h"
-#include "core/fxge/win32/cpsoutput.h"
-#include "core/fxge/win32/win32_int.h"
-#include "third_party/base/span.h"
-
-#if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
-namespace {
-
-class ScopedState {
- public:
-  ScopedState(HDC hDC, HFONT hFont)
-      : m_hDC(hDC),
-        m_iState(SaveDC(m_hDC)),
-        m_hFont(SelectObject(m_hDC, hFont)) {}
-
-  ScopedState(const ScopedState&) = delete;
-  ScopedState& operator=(const ScopedState&) = delete;
-
-  ~ScopedState() {
-    HGDIOBJ hFont = SelectObject(m_hDC, m_hFont);
-    DeleteObject(hFont);
-    RestoreDC(m_hDC, m_iState);
-  }
-
- private:
-  const HDC m_hDC;
-  const int m_iState;
-  const HGDIOBJ m_hFont;
-};
-
-}  // namespace
-
-bool g_pdfium_print_text_with_gdi = false;
-
-PDFiumEnsureTypefaceCharactersAccessible g_pdfium_typeface_accessible_func =
-    nullptr;
-#endif
-
-CGdiPrinterDriver::CGdiPrinterDriver(HDC hDC)
-    : CGdiDeviceDriver(hDC, DeviceType::kPrinter),
-      m_HorzSize(::GetDeviceCaps(m_hDC, HORZSIZE)),
-      m_VertSize(::GetDeviceCaps(m_hDC, VERTSIZE)) {}
-
-CGdiPrinterDriver::~CGdiPrinterDriver() = default;
-
-int CGdiPrinterDriver::GetDeviceCaps(int caps_id) const {
-  if (caps_id == FXDC_HORZ_SIZE)
-    return m_HorzSize;
-  if (caps_id == FXDC_VERT_SIZE)
-    return m_VertSize;
-  return CGdiDeviceDriver::GetDeviceCaps(caps_id);
-}
-
-bool CGdiPrinterDriver::SetDIBits(const RetainPtr<CFX_DIBBase>& pSource,
-                                  uint32_t color,
-                                  const FX_RECT& src_rect,
-                                  int left,
-                                  int top,
-                                  BlendMode blend_type) {
-  if (pSource->IsAlphaMask()) {
-    FX_RECT clip_rect(left, top, left + src_rect.Width(),
-                      top + src_rect.Height());
-    return StretchDIBits(pSource, color, left - src_rect.left,
-                         top - src_rect.top, pSource->GetWidth(),
-                         pSource->GetHeight(), &clip_rect,
-                         FXDIB_ResampleOptions(), BlendMode::kNormal);
-  }
-  ASSERT(pSource);
-  ASSERT(!pSource->IsAlphaMask());
-  ASSERT(blend_type == BlendMode::kNormal);
-  if (pSource->HasAlpha())
-    return false;
-
-  CFX_DIBExtractor temp(pSource);
-  RetainPtr<CFX_DIBitmap> pBitmap = temp.GetBitmap();
-  if (!pBitmap)
-    return false;
-
-  return GDI_SetDIBits(pBitmap, src_rect, left, top);
-}
-
-bool CGdiPrinterDriver::StretchDIBits(const RetainPtr<CFX_DIBBase>& pSource,
-                                      uint32_t color,
-                                      int dest_left,
-                                      int dest_top,
-                                      int dest_width,
-                                      int dest_height,
-                                      const FX_RECT* pClipRect,
-                                      const FXDIB_ResampleOptions& options,
-                                      BlendMode blend_type) {
-  if (pSource->IsAlphaMask()) {
-    int alpha = FXARGB_A(color);
-    if (pSource->GetBPP() != 1 || alpha != 255)
-      return false;
-
-    if (dest_width < 0 || dest_height < 0) {
-      RetainPtr<CFX_DIBitmap> pFlipped =
-          pSource->FlipImage(dest_width < 0, dest_height < 0);
-      if (!pFlipped)
-        return false;
-
-      if (dest_width < 0)
-        dest_left += dest_width;
-      if (dest_height < 0)
-        dest_top += dest_height;
-
-      return GDI_StretchBitMask(pFlipped, dest_left, dest_top, abs(dest_width),
-                                abs(dest_height), color);
-    }
-
-    CFX_DIBExtractor temp(pSource);
-    RetainPtr<CFX_DIBitmap> pBitmap = temp.GetBitmap();
-    if (!pBitmap)
-      return false;
-    return GDI_StretchBitMask(pBitmap, dest_left, dest_top, dest_width,
-                              dest_height, color);
-  }
-
-  if (pSource->HasAlpha())
-    return false;
-
-  if (dest_width < 0 || dest_height < 0) {
-    RetainPtr<CFX_DIBitmap> pFlipped =
-        pSource->FlipImage(dest_width < 0, dest_height < 0);
-    if (!pFlipped)
-      return false;
-
-    if (dest_width < 0)
-      dest_left += dest_width;
-    if (dest_height < 0)
-      dest_top += dest_height;
-
-    return GDI_StretchDIBits(pFlipped, dest_left, dest_top, abs(dest_width),
-                             abs(dest_height), options);
-  }
-
-  CFX_DIBExtractor temp(pSource);
-  RetainPtr<CFX_DIBitmap> pBitmap = temp.GetBitmap();
-  if (!pBitmap)
-    return false;
-  return GDI_StretchDIBits(pBitmap, dest_left, dest_top, dest_width,
-                           dest_height, options);
-}
-
-bool CGdiPrinterDriver::StartDIBits(const RetainPtr<CFX_DIBBase>& pSource,
-                                    int bitmap_alpha,
-                                    uint32_t color,
-                                    const CFX_Matrix& matrix,
-                                    const FXDIB_ResampleOptions& options,
-                                    std::unique_ptr<CFX_ImageRenderer>* handle,
-                                    BlendMode blend_type) {
-  if (bitmap_alpha < 255 || pSource->HasAlpha() ||
-      (pSource->IsAlphaMask() && (pSource->GetBPP() != 1))) {
-    return false;
-  }
-  CFX_FloatRect unit_rect = matrix.GetUnitRect();
-  FX_RECT full_rect = unit_rect.GetOuterRect();
-  if (fabs(matrix.b) < 0.5f && matrix.a != 0 && fabs(matrix.c) < 0.5f &&
-      matrix.d != 0) {
-    bool bFlipX = matrix.a < 0;
-    bool bFlipY = matrix.d > 0;
-    return StretchDIBits(pSource, color,
-                         bFlipX ? full_rect.right : full_rect.left,
-                         bFlipY ? full_rect.bottom : full_rect.top,
-                         bFlipX ? -full_rect.Width() : full_rect.Width(),
-                         bFlipY ? -full_rect.Height() : full_rect.Height(),
-                         nullptr, FXDIB_ResampleOptions(), blend_type);
-  }
-  if (fabs(matrix.a) >= 0.5f || fabs(matrix.d) >= 0.5f)
-    return false;
-
-  RetainPtr<CFX_DIBitmap> pTransformed =
-      pSource->SwapXY(matrix.c > 0, matrix.b < 0);
-  if (!pTransformed)
-    return false;
-
-  return StretchDIBits(pTransformed, color, full_rect.left, full_rect.top,
-                       full_rect.Width(), full_rect.Height(), nullptr,
-                       FXDIB_ResampleOptions(), blend_type);
-}
-
-bool CGdiPrinterDriver::DrawDeviceText(int nChars,
-                                       const TextCharPos* pCharPos,
-                                       CFX_Font* pFont,
-                                       const CFX_Matrix& mtObject2Device,
-                                       float font_size,
-                                       uint32_t color) {
-#if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
-  if (!g_pdfium_print_text_with_gdi)
-    return false;
-
-  if (nChars < 1 || !pFont || !pFont->IsEmbedded() || !pFont->IsTTFont())
-    return false;
-
-  // Scale factor used to minimize the kerning problems caused by rounding
-  // errors below. Value chosen based on the title of https://crbug.com/18383
-  const double kScaleFactor = 10;
-
-  // Font
-  //
-  // Note that |pFont| has the actual font to render with embedded within, but
-  // but unfortunately AddFontMemResourceEx() does not seem to cooperate.
-  // Loading font data to memory seems to work, but then enumerating the fonts
-  // fails to find it. This requires more investigation. In the meanwhile,
-  // assume the printing is happening on the machine that generated the PDF, so
-  // the embedded font, if not a web font, is available through GDI anyway.
-  // TODO(thestig): Figure out why AddFontMemResourceEx() does not work.
-  // Generalize this method to work for all PDFs with embedded fonts.
-  // In sandboxed environments, font loading may not work at all, so this may be
-  // the best possible effort.
-  LOGFONT lf = {};
-  lf.lfHeight = -font_size * kScaleFactor;
-  lf.lfWeight = pFont->IsBold() ? FW_BOLD : FW_NORMAL;
-  lf.lfItalic = pFont->IsItalic();
-  lf.lfCharSet = DEFAULT_CHARSET;
-
-  const WideString wsName =
-      WideString::FromUTF8(pFont->GetFaceName().AsStringView());
-  size_t iNameLen =
-      std::min(wsName.GetLength(), static_cast<size_t>(LF_FACESIZE - 1));
-  memcpy(lf.lfFaceName, wsName.c_str(), sizeof(lf.lfFaceName[0]) * iNameLen);
-  lf.lfFaceName[iNameLen] = 0;
-
-  HFONT hFont = CreateFontIndirect(&lf);
-  if (!hFont)
-    return false;
-
-  ScopedState state(m_hDC, hFont);
-  size_t nTextMetricSize = GetOutlineTextMetrics(m_hDC, 0, nullptr);
-  if (nTextMetricSize == 0) {
-    // Give up and fail if there is no way to get the font to try again.
-    if (!g_pdfium_typeface_accessible_func)
-      return false;
-
-    // Try to get the font. Any letter will do.
-    g_pdfium_typeface_accessible_func(&lf, L"A", 1);
-    nTextMetricSize = GetOutlineTextMetrics(m_hDC, 0, nullptr);
-    if (nTextMetricSize == 0)
-      return false;
-  }
-
-  std::vector<BYTE> buf(nTextMetricSize);
-  OUTLINETEXTMETRIC* pTextMetric =
-      reinterpret_cast<OUTLINETEXTMETRIC*>(buf.data());
-  if (GetOutlineTextMetrics(m_hDC, nTextMetricSize, pTextMetric) == 0)
-    return false;
-
-  // If the selected font is not the requested font, then bail out. This can
-  // happen with web fonts, for example.
-  wchar_t* wsSelectedName = reinterpret_cast<wchar_t*>(
-      buf.data() + reinterpret_cast<size_t>(pTextMetric->otmpFaceName));
-  if (wsName != wsSelectedName)
-    return false;
-
-  // Transforms
-  SetGraphicsMode(m_hDC, GM_ADVANCED);
-  XFORM xform;
-  xform.eM11 = mtObject2Device.a / kScaleFactor;
-  xform.eM12 = mtObject2Device.b / kScaleFactor;
-  xform.eM21 = -mtObject2Device.c / kScaleFactor;
-  xform.eM22 = -mtObject2Device.d / kScaleFactor;
-  xform.eDx = mtObject2Device.e;
-  xform.eDy = mtObject2Device.f;
-  ModifyWorldTransform(m_hDC, &xform, MWT_LEFTMULTIPLY);
-
-  // Color
-  FX_COLORREF colorref = ArgbToColorRef(color);
-  SetTextColor(m_hDC, colorref);
-  SetBkMode(m_hDC, TRANSPARENT);
-
-  // Text
-  WideString wsText;
-  std::vector<INT> spacing(nChars);
-  float fPreviousOriginX = 0;
-  for (int i = 0; i < nChars; ++i) {
-    // Only works with PDFs from Skia's PDF generator. Cannot handle arbitrary
-    // values from PDFs.
-    const TextCharPos& charpos = pCharPos[i];
-    ASSERT(charpos.m_AdjustMatrix[0] == 0);
-    ASSERT(charpos.m_AdjustMatrix[1] == 0);
-    ASSERT(charpos.m_AdjustMatrix[2] == 0);
-    ASSERT(charpos.m_AdjustMatrix[3] == 0);
-    ASSERT(charpos.m_Origin.y == 0);
-
-    // Round the spacing to the nearest integer, but keep track of the rounding
-    // error for calculating the next spacing value.
-    float fOriginX = charpos.m_Origin.x * kScaleFactor;
-    float fPixelSpacing = fOriginX - fPreviousOriginX;
-    spacing[i] = FXSYS_roundf(fPixelSpacing);
-    fPreviousOriginX = fOriginX - (fPixelSpacing - spacing[i]);
-
-    wsText += charpos.m_GlyphIndex;
-  }
-
-  // Draw
-  SetTextAlign(m_hDC, TA_LEFT | TA_BASELINE);
-  if (ExtTextOutW(m_hDC, 0, 0, ETO_GLYPH_INDEX, nullptr, wsText.c_str(), nChars,
-                  nChars > 1 ? &spacing[1] : nullptr)) {
-    return true;
-  }
-
-  // Give up and fail if there is no way to get the font to try again.
-  if (!g_pdfium_typeface_accessible_func)
-    return false;
-
-  // Try to get the font and draw again.
-  g_pdfium_typeface_accessible_func(&lf, wsText.c_str(), nChars);
-  return !!ExtTextOutW(m_hDC, 0, 0, ETO_GLYPH_INDEX, nullptr, wsText.c_str(),
-                       nChars, nChars > 1 ? &spacing[1] : nullptr);
-#else
-  return false;
-#endif
-}
-
-CPSPrinterDriver::CPSPrinterDriver(HDC hDC,
-                                   WindowsPrintMode mode,
-                                   bool bCmykOutput,
-                                   const EncoderIface* pEncoderIface)
-    : m_hDC(hDC), m_bCmykOutput(bCmykOutput), m_PSRenderer(pEncoderIface) {
-  // |mode| should be PostScript.
-  ASSERT(mode == WindowsPrintMode::kModePostScript2 ||
-         mode == WindowsPrintMode::kModePostScript3 ||
-         mode == WindowsPrintMode::kModePostScript2PassThrough ||
-         mode == WindowsPrintMode::kModePostScript3PassThrough);
-  int pslevel = (mode == WindowsPrintMode::kModePostScript2 ||
-                 mode == WindowsPrintMode::kModePostScript2PassThrough)
-                    ? 2
-                    : 3;
-  CPSOutput::OutputMode output_mode =
-      (mode == WindowsPrintMode::kModePostScript2 ||
-       mode == WindowsPrintMode::kModePostScript3)
-          ? CPSOutput::OutputMode::kGdiComment
-          : CPSOutput::OutputMode::kExtEscape;
-
-  m_HorzSize = ::GetDeviceCaps(m_hDC, HORZSIZE);
-  m_VertSize = ::GetDeviceCaps(m_hDC, VERTSIZE);
-  m_Width = ::GetDeviceCaps(m_hDC, HORZRES);
-  m_Height = ::GetDeviceCaps(m_hDC, VERTRES);
-  m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL);
-
-  m_PSRenderer.Init(pdfium::MakeRetain<CPSOutput>(m_hDC, output_mode), pslevel,
-                    m_Width, m_Height, bCmykOutput);
-  HRGN hRgn = ::CreateRectRgn(0, 0, 1, 1);
-  if (::GetClipRgn(m_hDC, hRgn) == 1) {
-    DWORD dwCount = ::GetRegionData(hRgn, 0, nullptr);
-    if (dwCount) {
-      std::vector<uint8_t> buffer(dwCount);
-      RGNDATA* pData = reinterpret_cast<RGNDATA*>(buffer.data());
-      if (::GetRegionData(hRgn, dwCount, pData)) {
-        CFX_PathData path;
-        for (uint32_t i = 0; i < pData->rdh.nCount; i++) {
-          RECT* pRect =
-              reinterpret_cast<RECT*>(pData->Buffer + pData->rdh.nRgnSize * i);
-          path.AppendRect(static_cast<float>(pRect->left),
-                          static_cast<float>(pRect->bottom),
-                          static_cast<float>(pRect->right),
-                          static_cast<float>(pRect->top));
-        }
-        m_PSRenderer.SetClip_PathFill(&path, nullptr, FXFILL_WINDING);
-      }
-    }
-  }
-  ::DeleteObject(hRgn);
-}
-
-CPSPrinterDriver::~CPSPrinterDriver() {
-  EndRendering();
-}
-
-DeviceType CPSPrinterDriver::GetDeviceType() const {
-  return DeviceType::kPrinter;
-}
-
-int CPSPrinterDriver::GetDeviceCaps(int caps_id) const {
-  switch (caps_id) {
-    case FXDC_PIXEL_WIDTH:
-      return m_Width;
-    case FXDC_PIXEL_HEIGHT:
-      return m_Height;
-    case FXDC_BITS_PIXEL:
-      return m_nBitsPerPixel;
-    case FXDC_RENDER_CAPS:
-      return m_bCmykOutput ? FXRC_BIT_MASK | FXRC_CMYK_OUTPUT : FXRC_BIT_MASK;
-    case FXDC_HORZ_SIZE:
-      return m_HorzSize;
-    case FXDC_VERT_SIZE:
-      return m_VertSize;
-    default:
-      NOTREACHED();
-      return 0;
-  }
-}
-
-bool CPSPrinterDriver::StartRendering() {
-  return m_PSRenderer.StartRendering();
-}
-
-void CPSPrinterDriver::EndRendering() {
-  m_PSRenderer.EndRendering();
-}
-
-void CPSPrinterDriver::SaveState() {
-  m_PSRenderer.SaveState();
-}
-
-void CPSPrinterDriver::RestoreState(bool bKeepSaved) {
-  m_PSRenderer.RestoreState(bKeepSaved);
-}
-
-bool CPSPrinterDriver::SetClip_PathFill(const CFX_PathData* pPathData,
-                                        const CFX_Matrix* pObject2Device,
-                                        int fill_mode) {
-  m_PSRenderer.SetClip_PathFill(pPathData, pObject2Device, fill_mode);
-  return true;
-}
-
-bool CPSPrinterDriver::SetClip_PathStroke(
-    const CFX_PathData* pPathData,
-    const CFX_Matrix* pObject2Device,
-    const CFX_GraphStateData* pGraphState) {
-  m_PSRenderer.SetClip_PathStroke(pPathData, pObject2Device, pGraphState);
-  return true;
-}
-
-bool CPSPrinterDriver::DrawPath(const CFX_PathData* pPathData,
-                                const CFX_Matrix* pObject2Device,
-                                const CFX_GraphStateData* pGraphState,
-                                FX_ARGB fill_color,
-                                FX_ARGB stroke_color,
-                                int fill_mode,
-                                BlendMode blend_type) {
-  if (blend_type != BlendMode::kNormal)
-    return false;
-  return m_PSRenderer.DrawPath(pPathData, pObject2Device, pGraphState,
-                               fill_color, stroke_color, fill_mode & 3);
-}
-
-bool CPSPrinterDriver::GetClipBox(FX_RECT* pRect) {
-  *pRect = m_PSRenderer.GetClipBox();
-  return true;
-}
-
-bool CPSPrinterDriver::SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
-                                 uint32_t color,
-                                 const FX_RECT& src_rect,
-                                 int left,
-                                 int top,
-                                 BlendMode blend_type) {
-  if (blend_type != BlendMode::kNormal)
-    return false;
-  return m_PSRenderer.SetDIBits(pBitmap, color, left, top);
-}
-
-bool CPSPrinterDriver::StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
-                                     uint32_t color,
-                                     int dest_left,
-                                     int dest_top,
-                                     int dest_width,
-                                     int dest_height,
-                                     const FX_RECT* pClipRect,
-                                     const FXDIB_ResampleOptions& options,
-                                     BlendMode blend_type) {
-  if (blend_type != BlendMode::kNormal)
-    return false;
-  return m_PSRenderer.StretchDIBits(pBitmap, color, dest_left, dest_top,
-                                    dest_width, dest_height, options);
-}
-
-bool CPSPrinterDriver::StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
-                                   int bitmap_alpha,
-                                   uint32_t color,
-                                   const CFX_Matrix& matrix,
-                                   const FXDIB_ResampleOptions& options,
-                                   std::unique_ptr<CFX_ImageRenderer>* handle,
-                                   BlendMode blend_type) {
-  if (blend_type != BlendMode::kNormal)
-    return false;
-
-  if (bitmap_alpha < 255)
-    return false;
-
-  *handle = nullptr;
-  return m_PSRenderer.DrawDIBits(pBitmap, color, matrix, options);
-}
-
-bool CPSPrinterDriver::DrawDeviceText(int nChars,
-                                      const TextCharPos* pCharPos,
-                                      CFX_Font* pFont,
-                                      const CFX_Matrix& mtObject2Device,
-                                      float font_size,
-                                      uint32_t color) {
-  return m_PSRenderer.DrawText(nChars, pCharPos, pFont, mtObject2Device,
-                               font_size, color);
-}
-
-CTextOnlyPrinterDriver::CTextOnlyPrinterDriver(HDC hDC)
-    : m_hDC(hDC),
-      m_Width(INT_MAX),
-      m_Height(INT_MAX),
-      m_HorzSize(INT_MAX),
-      m_VertSize(INT_MAX),
-      m_OriginY(0.0f),
-      m_SetOrigin(false) {
-  m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL);
-}
-
-CTextOnlyPrinterDriver::~CTextOnlyPrinterDriver() {
-  EndRendering();
-}
-
-DeviceType CTextOnlyPrinterDriver::GetDeviceType() const {
-  return DeviceType::kPrinter;
-}
-
-int CTextOnlyPrinterDriver::GetDeviceCaps(int caps_id) const {
-  switch (caps_id) {
-    case FXDC_PIXEL_WIDTH:
-      return m_Width;
-    case FXDC_PIXEL_HEIGHT:
-      return m_Height;
-    case FXDC_BITS_PIXEL:
-      return m_nBitsPerPixel;
-    case FXDC_RENDER_CAPS:
-      return 0;
-    case FXDC_HORZ_SIZE:
-      return m_HorzSize;
-    case FXDC_VERT_SIZE:
-      return m_VertSize;
-    default:
-      NOTREACHED();
-      return 0;
-  }
-}
-
-bool CTextOnlyPrinterDriver::SetClip_PathFill(const CFX_PathData* pPathData,
-                                              const CFX_Matrix* pObject2Device,
-                                              int fill_mode) {
-  return true;
-}
-
-bool CTextOnlyPrinterDriver::SetClip_PathStroke(
-    const CFX_PathData* pPathData,
-    const CFX_Matrix* pObject2Device,
-    const CFX_GraphStateData* pGraphState) {
-  return false;
-}
-
-bool CTextOnlyPrinterDriver::DrawPath(const CFX_PathData* pPathData,
-                                      const CFX_Matrix* pObject2Device,
-                                      const CFX_GraphStateData* pGraphState,
-                                      uint32_t fill_color,
-                                      uint32_t stroke_color,
-                                      int fill_mode,
-                                      BlendMode blend_type) {
-  return false;
-}
-
-bool CTextOnlyPrinterDriver::SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
-                                       uint32_t color,
-                                       const FX_RECT& src_rect,
-                                       int left,
-                                       int top,
-                                       BlendMode blend_type) {
-  return false;
-}
-
-bool CTextOnlyPrinterDriver::GetClipBox(FX_RECT* pRect) {
-  pRect->left = 0;
-  pRect->right = m_Width;
-  pRect->top = 0;
-  pRect->bottom = m_Height;
-  return true;
-}
-
-bool CTextOnlyPrinterDriver::StretchDIBits(
-    const RetainPtr<CFX_DIBBase>& pBitmap,
-    uint32_t color,
-    int dest_left,
-    int dest_top,
-    int dest_width,
-    int dest_height,
-    const FX_RECT* pClipRect,
-    const FXDIB_ResampleOptions& options,
-    BlendMode blend_type) {
-  return false;
-}
-
-bool CTextOnlyPrinterDriver::StartDIBits(
-    const RetainPtr<CFX_DIBBase>& pBitmap,
-    int bitmap_alpha,
-    uint32_t color,
-    const CFX_Matrix& matrix,
-    const FXDIB_ResampleOptions& options,
-    std::unique_ptr<CFX_ImageRenderer>* handle,
-    BlendMode blend_type) {
-  return false;
-}
-
-bool CTextOnlyPrinterDriver::DrawDeviceText(int nChars,
-                                            const TextCharPos* pCharPos,
-                                            CFX_Font* pFont,
-                                            const CFX_Matrix& mtObject2Device,
-                                            float font_size,
-                                            uint32_t color) {
-  if (g_pdfium_print_mode != 1)
-    return false;
-  if (nChars < 1 || !pFont || !pFont->IsEmbedded() || !pFont->IsTTFont())
-    return false;
-
-  // Scale factor used to minimize the kerning problems caused by rounding
-  // errors below. Value chosen based on the title of https://crbug.com/18383
-  const double kScaleFactor = 10;
-
-  // Detect new lines and add clrf characters (since this is Windows only).
-  // These characters are removed by SkPDF, but the new line information is
-  // preserved in the text location. clrf characters seem to be ignored by
-  // label printers that use this driver.
-  WideString wsText;
-  size_t len = nChars;
-  float fOffsetY = mtObject2Device.f * kScaleFactor;
-  if (m_SetOrigin && FXSYS_roundf(m_OriginY) != FXSYS_roundf(fOffsetY)) {
-    wsText += L"\r\n";
-    len += 2;
-  }
-  wsText.Reserve(len);
-  m_OriginY = fOffsetY;
-  m_SetOrigin = true;
-
-  // Text
-  for (int i = 0; i < nChars; ++i) {
-    // Only works with PDFs from Skia's PDF generator. Cannot handle arbitrary
-    // values from PDFs.
-    const TextCharPos& charpos = pCharPos[i];
-    ASSERT(charpos.m_AdjustMatrix[0] == 0);
-    ASSERT(charpos.m_AdjustMatrix[1] == 0);
-    ASSERT(charpos.m_AdjustMatrix[2] == 0);
-    ASSERT(charpos.m_AdjustMatrix[3] == 0);
-    ASSERT(charpos.m_Origin.y == 0);
-
-    wsText += charpos.m_Unicode;
-  }
-  ByteString text = wsText.ToDefANSI();
-  auto text_span = text.span();
-  while (!text_span.empty()) {
-    uint8_t buffer[1026];
-    size_t send_len = std::min<size_t>(text_span.size(), 1024);
-    *(reinterpret_cast<uint16_t*>(buffer)) = static_cast<uint16_t>(send_len);
-    memcpy(buffer + 2, text_span.data(), send_len);
-    ::GdiComment(m_hDC, send_len + 2, buffer);
-    text_span = text_span.subspan(send_len);
-  }
-  return true;
-}
diff --git a/core/fxge/win32/win32_int.h b/core/fxge/win32/win32_int.h
deleted file mode 100644
index 7f299ef..0000000
--- a/core/fxge/win32/win32_int.h
+++ /dev/null
@@ -1,353 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXGE_WIN32_WIN32_INT_H_
-#define CORE_FXGE_WIN32_WIN32_INT_H_
-
-#include <windows.h>
-
-#include <memory>
-#include <vector>
-
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/cfx_gemodule.h"
-#include "core/fxge/cfx_pathdata.h"
-#include "core/fxge/cfx_windowsrenderdevice.h"
-#include "core/fxge/renderdevicedriver_iface.h"
-#include "core/fxge/win32/cfx_psrenderer.h"
-#include "core/fxge/win32/cpsoutput.h"
-#include "third_party/base/optional.h"
-
-class CFX_ImageRenderer;
-class TextCharPos;
-struct WINDIB_Open_Args_;
-
-RetainPtr<CFX_DIBitmap> FX_WindowsDIB_LoadFromBuf(BITMAPINFO* pbmi,
-                                                  LPVOID pData,
-                                                  bool bAlpha);
-class CGdiplusExt {
- public:
-  CGdiplusExt();
-  ~CGdiplusExt();
-
-  void Load();
-  bool IsAvailable() { return !!m_hModule; }
-  bool StretchDIBits(HDC hDC,
-                     const RetainPtr<CFX_DIBitmap>& pBitmap,
-                     int dest_left,
-                     int dest_top,
-                     int dest_width,
-                     int dest_height,
-                     const FX_RECT* pClipRect,
-                     const FXDIB_ResampleOptions& options);
-  bool DrawPath(HDC hDC,
-                const CFX_PathData* pPathData,
-                const CFX_Matrix* pObject2Device,
-                const CFX_GraphStateData* pGraphState,
-                uint32_t fill_argb,
-                uint32_t stroke_argb,
-                int fill_mode);
-
-  RetainPtr<CFX_DIBitmap> LoadDIBitmap(WINDIB_Open_Args_ args);
-
-  std::vector<FARPROC> m_Functions;
-
- protected:
-  HMODULE m_hModule = nullptr;
-  HMODULE m_GdiModule = nullptr;
-};
-
-class CWin32Platform : public CFX_GEModule::PlatformIface {
- public:
-  CWin32Platform();
-  ~CWin32Platform() override;
-
-  // CFX_GEModule::PlatformIface:
-  void Init() override;
-
-  bool m_bHalfTone = false;
-  CGdiplusExt m_GdiplusExt;
-};
-
-class CGdiDeviceDriver : public RenderDeviceDriverIface {
- protected:
-  CGdiDeviceDriver(HDC hDC, DeviceType device_type);
-  ~CGdiDeviceDriver() override;
-
-  // RenderDeviceDriverIface:
-  DeviceType GetDeviceType() const override;
-  int GetDeviceCaps(int caps_id) const override;
-  void SaveState() override;
-  void RestoreState(bool bKeepSaved) override;
-  void SetBaseClip(const FX_RECT& rect) override;
-  bool SetClip_PathFill(const CFX_PathData* pPathData,
-                        const CFX_Matrix* pObject2Device,
-                        int fill_mode) override;
-  bool SetClip_PathStroke(const CFX_PathData* pPathData,
-                          const CFX_Matrix* pObject2Device,
-                          const CFX_GraphStateData* pGraphState) override;
-  bool DrawPath(const CFX_PathData* pPathData,
-                const CFX_Matrix* pObject2Device,
-                const CFX_GraphStateData* pGraphState,
-                uint32_t fill_color,
-                uint32_t stroke_color,
-                int fill_mode,
-                BlendMode blend_type) override;
-  bool FillRectWithBlend(const FX_RECT& rect,
-                         uint32_t fill_color,
-                         BlendMode blend_type) override;
-  bool DrawCosmeticLine(const CFX_PointF& ptMoveTo,
-                        const CFX_PointF& ptLineTo,
-                        uint32_t color,
-                        BlendMode blend_type) override;
-  bool GetClipBox(FX_RECT* pRect) override;
-
-  void DrawLine(float x1, float y1, float x2, float y2);
-
-  bool GDI_SetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                     const FX_RECT& src_rect,
-                     int left,
-                     int top);
-  bool GDI_StretchDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                         int dest_left,
-                         int dest_top,
-                         int dest_width,
-                         int dest_height,
-                         const FXDIB_ResampleOptions& options);
-  bool GDI_StretchBitMask(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                          int dest_left,
-                          int dest_top,
-                          int dest_width,
-                          int dest_height,
-                          uint32_t bitmap_color);
-
-  const HDC m_hDC;
-  bool m_bMetafileDCType;
-  int m_Width;
-  int m_Height;
-  int m_nBitsPerPixel;
-  const DeviceType m_DeviceType;
-  int m_RenderCaps;
-  pdfium::Optional<FX_RECT> m_BaseClipBox;
-};
-
-class CGdiDisplayDriver final : public CGdiDeviceDriver {
- public:
-  explicit CGdiDisplayDriver(HDC hDC);
-  ~CGdiDisplayDriver() override;
-
- private:
-  // CGdiDisplayDriver:
-  int GetDeviceCaps(int caps_id) const override;
-  bool GetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                 int left,
-                 int top) override;
-  bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
-                 uint32_t color,
-                 const FX_RECT& src_rect,
-                 int left,
-                 int top,
-                 BlendMode blend_type) override;
-  bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
-                     uint32_t color,
-                     int dest_left,
-                     int dest_top,
-                     int dest_width,
-                     int dest_height,
-                     const FX_RECT* pClipRect,
-                     const FXDIB_ResampleOptions& options,
-                     BlendMode blend_type) override;
-  bool StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
-                   int bitmap_alpha,
-                   uint32_t color,
-                   const CFX_Matrix& matrix,
-                   const FXDIB_ResampleOptions& options,
-                   std::unique_ptr<CFX_ImageRenderer>* handle,
-                   BlendMode blend_type) override;
-  bool UseFoxitStretchEngine(const RetainPtr<CFX_DIBBase>& pSource,
-                             uint32_t color,
-                             int dest_left,
-                             int dest_top,
-                             int dest_width,
-                             int dest_height,
-                             const FX_RECT* pClipRect,
-                             const FXDIB_ResampleOptions& options);
-};
-
-class CGdiPrinterDriver final : public CGdiDeviceDriver {
- public:
-  explicit CGdiPrinterDriver(HDC hDC);
-  ~CGdiPrinterDriver() override;
-
- private:
-  // CGdiPrinterDriver:
-  int GetDeviceCaps(int caps_id) const override;
-  bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
-                 uint32_t color,
-                 const FX_RECT& src_rect,
-                 int left,
-                 int top,
-                 BlendMode blend_type) override;
-  bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
-                     uint32_t color,
-                     int dest_left,
-                     int dest_top,
-                     int dest_width,
-                     int dest_height,
-                     const FX_RECT* pClipRect,
-                     const FXDIB_ResampleOptions& options,
-                     BlendMode blend_type) override;
-  bool StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
-                   int bitmap_alpha,
-                   uint32_t color,
-                   const CFX_Matrix& matrix,
-                   const FXDIB_ResampleOptions& options,
-                   std::unique_ptr<CFX_ImageRenderer>* handle,
-                   BlendMode blend_type) override;
-  bool DrawDeviceText(int nChars,
-                      const TextCharPos* pCharPos,
-                      CFX_Font* pFont,
-                      const CFX_Matrix& mtObject2Device,
-                      float font_size,
-                      uint32_t color) override;
-
-  const int m_HorzSize;
-  const int m_VertSize;
-};
-
-class CPSPrinterDriver final : public RenderDeviceDriverIface {
- public:
-  CPSPrinterDriver(HDC hDC,
-                   WindowsPrintMode mode,
-                   bool bCmykOutput,
-                   const EncoderIface* pEncoderIface);
-  ~CPSPrinterDriver() override;
-
- private:
-  // RenderDeviceDriverIface:
-  DeviceType GetDeviceType() const override;
-  int GetDeviceCaps(int caps_id) const override;
-  bool StartRendering() override;
-  void EndRendering() override;
-  void SaveState() override;
-  void RestoreState(bool bKeepSaved) override;
-  bool SetClip_PathFill(const CFX_PathData* pPathData,
-                        const CFX_Matrix* pObject2Device,
-                        int fill_mode) override;
-  bool SetClip_PathStroke(const CFX_PathData* pPathData,
-                          const CFX_Matrix* pObject2Device,
-                          const CFX_GraphStateData* pGraphState) override;
-  bool DrawPath(const CFX_PathData* pPathData,
-                const CFX_Matrix* pObject2Device,
-                const CFX_GraphStateData* pGraphState,
-                uint32_t fill_color,
-                uint32_t stroke_color,
-                int fill_mode,
-                BlendMode blend_type) override;
-  bool GetClipBox(FX_RECT* pRect) override;
-  bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
-                 uint32_t color,
-                 const FX_RECT& src_rect,
-                 int left,
-                 int top,
-                 BlendMode blend_type) override;
-  bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
-                     uint32_t color,
-                     int dest_left,
-                     int dest_top,
-                     int dest_width,
-                     int dest_height,
-                     const FX_RECT* pClipRect,
-                     const FXDIB_ResampleOptions& options,
-                     BlendMode blend_type) override;
-  bool StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
-                   int bitmap_alpha,
-                   uint32_t color,
-                   const CFX_Matrix& matrix,
-                   const FXDIB_ResampleOptions& options,
-                   std::unique_ptr<CFX_ImageRenderer>* handle,
-                   BlendMode blend_type) override;
-  bool DrawDeviceText(int nChars,
-                      const TextCharPos* pCharPos,
-                      CFX_Font* pFont,
-                      const CFX_Matrix& mtObject2Device,
-                      float font_size,
-                      uint32_t color) override;
-
-  HDC m_hDC;
-  const bool m_bCmykOutput;
-  int m_Width;
-  int m_Height;
-  int m_nBitsPerPixel;
-  int m_HorzSize;
-  int m_VertSize;
-  CFX_PSRenderer m_PSRenderer;
-};
-
-class CTextOnlyPrinterDriver final : public RenderDeviceDriverIface {
- public:
-  explicit CTextOnlyPrinterDriver(HDC hDC);
-  ~CTextOnlyPrinterDriver() override;
-
- private:
-  // RenderDeviceDriverIface:
-  DeviceType GetDeviceType() const override;
-  int GetDeviceCaps(int caps_id) const override;
-  void SaveState() override {}
-  void RestoreState(bool bKeepSaved) override {}
-  bool SetClip_PathFill(const CFX_PathData* pPathData,
-                        const CFX_Matrix* pObject2Device,
-                        int fill_mode) override;
-  bool SetClip_PathStroke(const CFX_PathData* pPathData,
-                          const CFX_Matrix* pObject2Device,
-                          const CFX_GraphStateData* pGraphState) override;
-  bool DrawPath(const CFX_PathData* pPathData,
-                const CFX_Matrix* pObject2Device,
-                const CFX_GraphStateData* pGraphState,
-                uint32_t fill_color,
-                uint32_t stroke_color,
-                int fill_mode,
-                BlendMode blend_type) override;
-  bool GetClipBox(FX_RECT* pRect) override;
-  bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
-                 uint32_t color,
-                 const FX_RECT& src_rect,
-                 int left,
-                 int top,
-                 BlendMode blend_type) override;
-  bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
-                     uint32_t color,
-                     int dest_left,
-                     int dest_top,
-                     int dest_width,
-                     int dest_height,
-                     const FX_RECT* pClipRect,
-                     const FXDIB_ResampleOptions& options,
-                     BlendMode blend_type) override;
-  bool StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
-                   int bitmap_alpha,
-                   uint32_t color,
-                   const CFX_Matrix& matrix,
-                   const FXDIB_ResampleOptions& options,
-                   std::unique_ptr<CFX_ImageRenderer>* handle,
-                   BlendMode blend_type) override;
-  bool DrawDeviceText(int nChars,
-                      const TextCharPos* pCharPos,
-                      CFX_Font* pFont,
-                      const CFX_Matrix& mtObject2Device,
-                      float font_size,
-                      uint32_t color) override;
-
-  HDC m_hDC;
-  const int m_Width;
-  const int m_Height;
-  int m_nBitsPerPixel;
-  const int m_HorzSize;
-  const int m_VertSize;
-  float m_OriginY;
-  bool m_SetOrigin;
-};
-#endif  // CORE_FXGE_WIN32_WIN32_INT_H_
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 6077d21..dbba9a1 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -75,7 +75,7 @@
 
 ```
 PDF_LIBS="-lpdfium -lfpdfapi -lfxge -lfpdfdoc -lfxcrt -lfx_agg \
--lfxcodec -lfx_lpng -lfx_libopenjpeg -lfx_lcms2 -lfx_freetype -ljpeg \
+-lfxcodec -lpng -lfx_libopenjpeg -lfx_lcms2 -lfx_freetype -ljpeg \
 -lfdrm -lpwl -lbigint -lformfiller -ljavascript -lfxedit"
 PDF_DIR=<path/to/pdfium>
 
@@ -181,7 +181,7 @@
 
 
 
-[chrome-plugin]: https://chromium.googlesource.com/chromium/src/+/master/pdf/
-[pdfium-public]: https://pdfium.googlesource.com/pdfium/+/master/public/
+[chrome-plugin]: https://chromium.googlesource.com/chromium/src/+/main/pdf/
+[pdfium-public]: https://pdfium.googlesource.com/pdfium/+/main/public/
 [pdfium-v8]: /docs/v8-getting-started.md
 [pdfium-edit-guide]: /docs/pdfium-edit-guide.md
diff --git a/docs/safetynet.md b/docs/safetynet.md
index bd0a791..8121bbc 100644
--- a/docs/safetynet.md
+++ b/docs/safetynet.md
@@ -106,9 +106,6 @@
 $ valgrind
 ```
 
-Add `ro_segment_workaround_for_valgrind=true` to `args.gn` for symbols to appear
-correctly.
-
 This is a slow and accurate profiler. Expect variations of around 100
 instructions. However, this takes about 50 times longer to run than perf stat.
 
diff --git a/fpdfsdk/Android.bp b/fpdfsdk/Android.bp
index f9c3861..f17a63b 100644
--- a/fpdfsdk/Android.bp
+++ b/fpdfsdk/Android.bp
@@ -13,11 +13,8 @@
 
     visibility: ["//external/pdfium:__subpackages__"],
 
-    header_libs: [
-        "libpdfium-constants",
-    ],
-
     static_libs: [
+        "libpdfium-constants",
         "libpdfium-fdrm",
         "libpdfium-edit",
         "libpdfium-font",
diff --git a/fpdfsdk/BUILD.gn b/fpdfsdk/BUILD.gn
index 9eb9562..c8aa54f 100644
--- a/fpdfsdk/BUILD.gn
+++ b/fpdfsdk/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -7,12 +7,8 @@
 
 source_set("fpdfsdk") {
   sources = [
-    "cpdfsdk_actionhandler.cpp",
-    "cpdfsdk_actionhandler.h",
     "cpdfsdk_annot.cpp",
     "cpdfsdk_annot.h",
-    "cpdfsdk_annothandlermgr.cpp",
-    "cpdfsdk_annothandlermgr.h",
     "cpdfsdk_annotiteration.cpp",
     "cpdfsdk_annotiteration.h",
     "cpdfsdk_annotiterator.cpp",
@@ -21,12 +17,8 @@
     "cpdfsdk_appstream.h",
     "cpdfsdk_baannot.cpp",
     "cpdfsdk_baannot.h",
-    "cpdfsdk_baannothandler.cpp",
-    "cpdfsdk_baannothandler.h",
     "cpdfsdk_customaccess.cpp",
     "cpdfsdk_customaccess.h",
-    "cpdfsdk_fieldaction.cpp",
-    "cpdfsdk_fieldaction.h",
     "cpdfsdk_filewriteadapter.cpp",
     "cpdfsdk_filewriteadapter.h",
     "cpdfsdk_formfillenvironment.cpp",
@@ -43,8 +35,6 @@
     "cpdfsdk_renderpage.h",
     "cpdfsdk_widget.cpp",
     "cpdfsdk_widget.h",
-    "cpdfsdk_widgethandler.cpp",
-    "cpdfsdk_widgethandler.h",
     "fpdf_annot.cpp",
     "fpdf_attachment.cpp",
     "fpdf_catalog.cpp",
@@ -62,16 +52,19 @@
     "fpdf_progressive.cpp",
     "fpdf_save.cpp",
     "fpdf_searchex.cpp",
+    "fpdf_signature.cpp",
     "fpdf_structtree.cpp",
     "fpdf_sysfontinfo.cpp",
     "fpdf_text.cpp",
     "fpdf_thumbnail.cpp",
     "fpdf_transformpage.cpp",
     "fpdf_view.cpp",
-    "ipdfsdk_annothandler.h",
   ]
 
-  configs += [ "../:pdfium_core_config" ]
+  configs += [
+    "../:pdfium_strict_config",
+    "../:pdfium_noshorten_config",
+  ]
   deps = [
     "../:pdfium_public_headers",
     "../constants",
@@ -97,7 +90,7 @@
 
   if (pdf_enable_xfa) {
     deps += [
-      "../fxbarcode",
+      "../xfa/fgas/font",
       "../xfa/fxfa",
       "../xfa/fxfa/parser",
       "fpdfxfa",
@@ -108,19 +101,19 @@
 
 pdfium_unittest_source_set("unittests") {
   sources = [
-    "fpdf_annot_unittest.cpp",
+    "cpdfsdk_helpers_unittest.cpp",
     "fpdf_catalog_unittest.cpp",
     "fpdf_doc_unittest.cpp",
     "fpdf_edit_unittest.cpp",
-    "fpdf_editimg_unittest.cpp",
     "fpdf_view_unittest.cpp",
   ]
   deps = [
     ":fpdfsdk",
     "../:pdfium_public_headers",
     "../constants",
-    "../core/fpdfapi/page",
+    "../core/fpdfapi/page:unit_test_support",
     "../core/fpdfapi/parser",
+    "../core/fpdfapi/parser:unit_test_support",
     "../core/fpdfapi/render",
     "../core/fpdfdoc",
   ]
@@ -130,12 +123,13 @@
 pdfium_embeddertest_source_set("embeddertests") {
   sources = [
     "cpdfsdk_annotiterator_embeddertest.cpp",
-    "cpdfsdk_baannothandler_embeddertest.cpp",
+    "cpdfsdk_baannot_embeddertest.cpp",
     "fpdf_annot_embeddertest.cpp",
     "fpdf_attachment_embeddertest.cpp",
     "fpdf_dataavail_embeddertest.cpp",
     "fpdf_doc_embeddertest.cpp",
     "fpdf_edit_embeddertest.cpp",
+    "fpdf_editimg_embeddertest.cpp",
     "fpdf_editpage_embeddertest.cpp",
     "fpdf_editpath_embeddertest.cpp",
     "fpdf_ext_embeddertest.cpp",
@@ -145,6 +139,7 @@
     "fpdf_ppo_embeddertest.cpp",
     "fpdf_save_embeddertest.cpp",
     "fpdf_searchex_embeddertest.cpp",
+    "fpdf_signature_embeddertest.cpp",
     "fpdf_structtree_embeddertest.cpp",
     "fpdf_sysfontinfo_embeddertest.cpp",
     "fpdf_text_embeddertest.cpp",
@@ -160,7 +155,12 @@
     "../core/fpdfapi/font",
     "../core/fpdfapi/page",
     "../core/fpdfapi/parser",
+    "../core/fxcrt:test_support",
     "../core/fxge",
   ]
   pdfium_root_dir = "../"
+
+  if (pdf_use_skia) {
+    deps += [ "//skia" ]
+  }
 }
diff --git a/fpdfsdk/DEPS b/fpdfsdk/DEPS
index 5edee08..1e52515 100644
--- a/fpdfsdk/DEPS
+++ b/fpdfsdk/DEPS
@@ -3,8 +3,14 @@
   '+fxjs',
   '+public',
   '+v8',
+  '+xfa/fgas/font',
+  '+xfa/fgas/graphics',
   '+xfa/fwl',
-  '+fxbarcode',
   '+xfa/fxfa',
-  '+xfa/fxgraphics',
 ]
+
+specific_include_rules = {
+  'fpdf_view_embeddertest\.cpp': [
+    '+third_party/skia/include',
+  ],
+}
diff --git a/fpdfsdk/PRESUBMIT.py b/fpdfsdk/PRESUBMIT.py
index 17f7c6a..9e59329 100644
--- a/fpdfsdk/PRESUBMIT.py
+++ b/fpdfsdk/PRESUBMIT.py
@@ -1,4 +1,4 @@
-# Copyright 2019 The PDFium Authors. All rights reserved.
+# Copyright 2019 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -8,6 +8,9 @@
 for more details about the presubmit API built into depot_tools.
 """
 
+USE_PYTHON3 = True
+
+
 def _CheckApiTestFile(input_api, output_api):
   """Checks that the public headers match the API tests."""
   api_test_file = input_api.os_path.normpath('fpdfsdk/fpdf_view_c_api_test.c')
@@ -21,7 +24,7 @@
   src_path = input_api.os_path.dirname(input_api.PresubmitLocalPath())
   check_script = input_api.os_path.join(
       src_path, 'testing' , 'tools' , 'api_check.py')
-  cmd = [input_api.python_executable, check_script]
+  cmd = [input_api.python3_executable, check_script]
   try:
     input_api.subprocess.check_output(cmd)
     return []
@@ -32,11 +35,11 @@
 
 def CheckChangeOnUpload(input_api, output_api):
   results = []
-  results += _CheckApiTestFile(input_api, output_api)
+  results.extend(_CheckApiTestFile(input_api, output_api))
   return results
 
 
 def CheckChangeOnCommit(input_api, output_api):
   results = []
-  results += _CheckApiTestFile(input_api, output_api)
+  results.extend(_CheckApiTestFile(input_api, output_api))
   return results
diff --git a/fpdfsdk/cpdfsdk_actionhandler.cpp b/fpdfsdk/cpdfsdk_actionhandler.cpp
deleted file mode 100644
index 6b2b4b9..0000000
--- a/fpdfsdk/cpdfsdk_actionhandler.cpp
+++ /dev/null
@@ -1,426 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/cpdfsdk_actionhandler.h"
-
-#include <set>
-#include <vector>
-
-#include "core/fpdfapi/parser/cpdf_array.h"
-#include "core/fpdfdoc/cpdf_formfield.h"
-#include "core/fpdfdoc/cpdf_interactiveform.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_interactiveform.h"
-#include "fxjs/ijs_event_context.h"
-#include "fxjs/ijs_runtime.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/stl_util.h"
-
-bool CPDFSDK_ActionHandler::DoAction_DocOpen(
-    const CPDF_Action& action,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  std::set<const CPDF_Dictionary*> visited;
-  return ExecuteDocumentOpenAction(action, pFormFillEnv, &visited);
-}
-
-bool CPDFSDK_ActionHandler::DoAction_JavaScript(
-    const CPDF_Action& JsAction,
-    WideString csJSName,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  if (JsAction.GetType() == CPDF_Action::JavaScript) {
-    WideString swJS = JsAction.GetJavaScript();
-    if (!swJS.IsEmpty()) {
-      RunDocumentOpenJavaScript(pFormFillEnv, csJSName, swJS);
-      return true;
-    }
-  }
-
-  return false;
-}
-
-bool CPDFSDK_ActionHandler::DoAction_FieldJavaScript(
-    const CPDF_Action& JsAction,
-    CPDF_AAction::AActionType type,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    CPDF_FormField* pFormField,
-    CPDFSDK_FieldAction* data) {
-  ASSERT(pFormFillEnv);
-  if (pFormFillEnv->IsJSPlatformPresent() &&
-      JsAction.GetType() == CPDF_Action::JavaScript) {
-    WideString swJS = JsAction.GetJavaScript();
-    if (!swJS.IsEmpty()) {
-      RunFieldJavaScript(pFormFillEnv, pFormField, type, data, swJS);
-      return true;
-    }
-  }
-  return false;
-}
-
-bool CPDFSDK_ActionHandler::DoAction_Page(
-    const CPDF_Action& action,
-    enum CPDF_AAction::AActionType eType,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  std::set<const CPDF_Dictionary*> visited;
-  return ExecuteDocumentPageAction(action, eType, pFormFillEnv, &visited);
-}
-
-bool CPDFSDK_ActionHandler::DoAction_Document(
-    const CPDF_Action& action,
-    enum CPDF_AAction::AActionType eType,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  std::set<const CPDF_Dictionary*> visited;
-  return ExecuteDocumentPageAction(action, eType, pFormFillEnv, &visited);
-}
-
-bool CPDFSDK_ActionHandler::DoAction_Field(
-    const CPDF_Action& action,
-    CPDF_AAction::AActionType type,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    CPDF_FormField* pFormField,
-    CPDFSDK_FieldAction* data) {
-  std::set<const CPDF_Dictionary*> visited;
-  return ExecuteFieldAction(action, type, pFormFillEnv, pFormField, data,
-                            &visited);
-}
-
-bool CPDFSDK_ActionHandler::ExecuteDocumentOpenAction(
-    const CPDF_Action& action,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    std::set<const CPDF_Dictionary*>* visited) {
-  const CPDF_Dictionary* pDict = action.GetDict();
-  if (pdfium::ContainsKey(*visited, pDict))
-    return false;
-
-  visited->insert(pDict);
-
-  ASSERT(pFormFillEnv);
-  if (action.GetType() == CPDF_Action::JavaScript) {
-    if (pFormFillEnv->IsJSPlatformPresent()) {
-      WideString swJS = action.GetJavaScript();
-      if (!swJS.IsEmpty())
-        RunDocumentOpenJavaScript(pFormFillEnv, WideString(), swJS);
-    }
-  } else {
-    DoAction_NoJs(action, CPDF_AAction::AActionType::kDocumentOpen,
-                  pFormFillEnv);
-  }
-
-  for (int32_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) {
-    CPDF_Action subaction = action.GetSubAction(i);
-    if (!ExecuteDocumentOpenAction(subaction, pFormFillEnv, visited))
-      return false;
-  }
-
-  return true;
-}
-
-bool CPDFSDK_ActionHandler::ExecuteDocumentPageAction(
-    const CPDF_Action& action,
-    CPDF_AAction::AActionType type,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    std::set<const CPDF_Dictionary*>* visited) {
-  const CPDF_Dictionary* pDict = action.GetDict();
-  if (pdfium::ContainsKey(*visited, pDict))
-    return false;
-
-  visited->insert(pDict);
-
-  ASSERT(pFormFillEnv);
-  if (action.GetType() == CPDF_Action::JavaScript) {
-    if (pFormFillEnv->IsJSPlatformPresent()) {
-      WideString swJS = action.GetJavaScript();
-      if (!swJS.IsEmpty())
-        RunDocumentPageJavaScript(pFormFillEnv, type, swJS);
-    }
-  } else {
-    DoAction_NoJs(action, type, pFormFillEnv);
-  }
-
-  ASSERT(pFormFillEnv);
-
-  for (int32_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) {
-    CPDF_Action subaction = action.GetSubAction(i);
-    if (!ExecuteDocumentPageAction(subaction, type, pFormFillEnv, visited))
-      return false;
-  }
-
-  return true;
-}
-
-bool CPDFSDK_ActionHandler::IsValidField(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    CPDF_Dictionary* pFieldDict) {
-  ASSERT(pFieldDict);
-
-  CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
-  CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
-  return !!pPDFForm->GetFieldByDict(pFieldDict);
-}
-
-bool CPDFSDK_ActionHandler::ExecuteFieldAction(
-    const CPDF_Action& action,
-    CPDF_AAction::AActionType type,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    CPDF_FormField* pFormField,
-    CPDFSDK_FieldAction* data,
-    std::set<const CPDF_Dictionary*>* visited) {
-  const CPDF_Dictionary* pDict = action.GetDict();
-  if (pdfium::ContainsKey(*visited, pDict))
-    return false;
-
-  visited->insert(pDict);
-
-  ASSERT(pFormFillEnv);
-  if (action.GetType() == CPDF_Action::JavaScript) {
-    if (pFormFillEnv->IsJSPlatformPresent()) {
-      WideString swJS = action.GetJavaScript();
-      if (!swJS.IsEmpty()) {
-        RunFieldJavaScript(pFormFillEnv, pFormField, type, data, swJS);
-        if (!IsValidField(pFormFillEnv, pFormField->GetFieldDict()))
-          return false;
-      }
-    }
-  } else {
-    DoAction_NoJs(action, type, pFormFillEnv);
-  }
-
-  for (int32_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) {
-    CPDF_Action subaction = action.GetSubAction(i);
-    if (!ExecuteFieldAction(subaction, type, pFormFillEnv, pFormField, data,
-                            visited))
-      return false;
-  }
-
-  return true;
-}
-
-void CPDFSDK_ActionHandler::DoAction_NoJs(
-    const CPDF_Action& action,
-    CPDF_AAction::AActionType type,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  ASSERT(pFormFillEnv);
-
-  switch (action.GetType()) {
-    case CPDF_Action::GoTo:
-      DoAction_GoTo(pFormFillEnv, action);
-      break;
-    case CPDF_Action::URI:
-      if (CPDF_AAction::IsUserClick(type))
-        DoAction_URI(pFormFillEnv, action);
-      break;
-    case CPDF_Action::Hide:
-      DoAction_Hide(action, pFormFillEnv);
-      break;
-    case CPDF_Action::Named:
-      DoAction_Named(pFormFillEnv, action);
-      break;
-    case CPDF_Action::SubmitForm:
-      if (CPDF_AAction::IsUserClick(type))
-        DoAction_SubmitForm(action, pFormFillEnv);
-      break;
-    case CPDF_Action::ResetForm:
-      DoAction_ResetForm(action, pFormFillEnv);
-      break;
-    case CPDF_Action::JavaScript:
-      NOTREACHED();
-      break;
-    case CPDF_Action::SetOCGState:
-    case CPDF_Action::Thread:
-    case CPDF_Action::Sound:
-    case CPDF_Action::Movie:
-    case CPDF_Action::Rendition:
-    case CPDF_Action::Trans:
-    case CPDF_Action::GoTo3DView:
-    case CPDF_Action::GoToR:
-    case CPDF_Action::GoToE:
-    case CPDF_Action::Launch:
-    case CPDF_Action::ImportData:
-      // Unimplemented
-      break;
-    default:
-      break;
-  }
-}
-
-void CPDFSDK_ActionHandler::DoAction_GoTo(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    const CPDF_Action& action) {
-  ASSERT(action.GetDict());
-
-  CPDF_Document* pPDFDocument = pFormFillEnv->GetPDFDocument();
-  ASSERT(pPDFDocument);
-
-  CPDF_Dest MyDest = action.GetDest(pPDFDocument);
-  int nPageIndex = MyDest.GetDestPageIndex(pPDFDocument);
-  int nFitType = MyDest.GetZoomMode();
-  const CPDF_Array* pMyArray = MyDest.GetArray();
-  std::vector<float> posArray;
-  if (pMyArray) {
-    for (size_t i = 2; i < pMyArray->size(); i++)
-      posArray.push_back(pMyArray->GetNumberAt(i));
-  }
-  pFormFillEnv->DoGoToAction(nPageIndex, nFitType, posArray.data(),
-                             posArray.size());
-}
-
-void CPDFSDK_ActionHandler::DoAction_URI(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    const CPDF_Action& action) {
-  ASSERT(action.GetDict());
-
-  ByteString sURI = action.GetURI(pFormFillEnv->GetPDFDocument());
-  pFormFillEnv->DoURIAction(sURI.c_str());
-}
-
-void CPDFSDK_ActionHandler::DoAction_Named(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    const CPDF_Action& action) {
-  ASSERT(action.GetDict());
-
-  ByteString csName = action.GetNamedAction();
-  pFormFillEnv->ExecuteNamedAction(csName.c_str());
-}
-
-void CPDFSDK_ActionHandler::RunFieldJavaScript(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    CPDF_FormField* pFormField,
-    CPDF_AAction::AActionType type,
-    CPDFSDK_FieldAction* data,
-    const WideString& script) {
-  ASSERT(type != CPDF_AAction::kCalculate);
-  ASSERT(type != CPDF_AAction::kFormat);
-
-  RunScript(pFormFillEnv, script,
-            [type, data, pFormField](IJS_EventContext* context) {
-              switch (type) {
-                case CPDF_AAction::kCursorEnter:
-                  context->OnField_MouseEnter(data->bModifier, data->bShift,
-                                              pFormField);
-                  break;
-                case CPDF_AAction::kCursorExit:
-                  context->OnField_MouseExit(data->bModifier, data->bShift,
-                                             pFormField);
-                  break;
-                case CPDF_AAction::kButtonDown:
-                  context->OnField_MouseDown(data->bModifier, data->bShift,
-                                             pFormField);
-                  break;
-                case CPDF_AAction::kButtonUp:
-                  context->OnField_MouseUp(data->bModifier, data->bShift,
-                                           pFormField);
-                  break;
-                case CPDF_AAction::kGetFocus:
-                  context->OnField_Focus(data->bModifier, data->bShift,
-                                         pFormField, &data->sValue);
-                  break;
-                case CPDF_AAction::kLoseFocus:
-                  context->OnField_Blur(data->bModifier, data->bShift,
-                                        pFormField, &data->sValue);
-                  break;
-                case CPDF_AAction::kKeyStroke:
-                  context->OnField_Keystroke(
-                      &data->sChange, data->sChangeEx, data->bKeyDown,
-                      data->bModifier, &data->nSelEnd, &data->nSelStart,
-                      data->bShift, pFormField, &data->sValue,
-                      data->bWillCommit, data->bFieldFull, &data->bRC);
-                  break;
-                case CPDF_AAction::kValidate:
-                  context->OnField_Validate(&data->sChange, data->sChangeEx,
-                                            data->bKeyDown, data->bModifier,
-                                            data->bShift, pFormField,
-                                            &data->sValue, &data->bRC);
-                  break;
-                default:
-                  NOTREACHED();
-                  break;
-              }
-            });
-}
-
-void CPDFSDK_ActionHandler::RunDocumentOpenJavaScript(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    const WideString& sScriptName,
-    const WideString& script) {
-  RunScript(pFormFillEnv, script,
-            [pFormFillEnv, sScriptName](IJS_EventContext* context) {
-              context->OnDoc_Open(pFormFillEnv, sScriptName);
-            });
-}
-
-void CPDFSDK_ActionHandler::RunDocumentPageJavaScript(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    CPDF_AAction::AActionType type,
-    const WideString& script) {
-  RunScript(pFormFillEnv, script,
-            [type, pFormFillEnv](IJS_EventContext* context) {
-              switch (type) {
-                case CPDF_AAction::kOpenPage:
-                  context->OnPage_Open(pFormFillEnv);
-                  break;
-                case CPDF_AAction::kClosePage:
-                  context->OnPage_Close(pFormFillEnv);
-                  break;
-                case CPDF_AAction::kCloseDocument:
-                  context->OnDoc_WillClose(pFormFillEnv);
-                  break;
-                case CPDF_AAction::kSaveDocument:
-                  context->OnDoc_WillSave(pFormFillEnv);
-                  break;
-                case CPDF_AAction::kDocumentSaved:
-                  context->OnDoc_DidSave(pFormFillEnv);
-                  break;
-                case CPDF_AAction::kPrintDocument:
-                  context->OnDoc_WillPrint(pFormFillEnv);
-                  break;
-                case CPDF_AAction::kDocumentPrinted:
-                  context->OnDoc_DidPrint(pFormFillEnv);
-                  break;
-                case CPDF_AAction::kPageVisible:
-                  context->OnPage_InView(pFormFillEnv);
-                  break;
-                case CPDF_AAction::kPageInvisible:
-                  context->OnPage_OutView(pFormFillEnv);
-                  break;
-                default:
-                  NOTREACHED();
-                  break;
-              }
-            });
-}
-
-bool CPDFSDK_ActionHandler::DoAction_Hide(
-    const CPDF_Action& action,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
-  if (pForm->DoAction_Hide(action)) {
-    pFormFillEnv->SetChangeMark();
-    return true;
-  }
-  return false;
-}
-
-bool CPDFSDK_ActionHandler::DoAction_SubmitForm(
-    const CPDF_Action& action,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
-  return pForm->DoAction_SubmitForm(action);
-}
-
-void CPDFSDK_ActionHandler::DoAction_ResetForm(
-    const CPDF_Action& action,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
-  pForm->DoAction_ResetForm(action);
-}
-
-void CPDFSDK_ActionHandler::RunScript(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                      const WideString& script,
-                                      const RunScriptCallback& cb) {
-  IJS_Runtime::ScopedEventContext pContext(pFormFillEnv->GetIJSRuntime());
-  cb(pContext.Get());
-  pContext->RunScript(script);
-  // TODO(dsinclair): Return error if RunScript returns a IJS_Runtime::JS_Error.
-}
diff --git a/fpdfsdk/cpdfsdk_actionhandler.h b/fpdfsdk/cpdfsdk_actionhandler.h
deleted file mode 100644
index a8bd9cf..0000000
--- a/fpdfsdk/cpdfsdk_actionhandler.h
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_CPDFSDK_ACTIONHANDLER_H_
-#define FPDFSDK_CPDFSDK_ACTIONHANDLER_H_
-
-#include <set>
-#include <utility>
-
-#include "core/fpdfdoc/cpdf_aaction.h"
-#include "core/fpdfdoc/cpdf_action.h"
-#include "core/fxcrt/fx_string.h"
-#include "fpdfsdk/cpdfsdk_fieldaction.h"
-
-class CPDFSDK_Annot;
-class CPDFSDK_FormFillEnvironment;
-class CPDF_Dictionary;
-class CPDF_FormField;
-class IJS_EventContext;
-
-class CPDFSDK_ActionHandler {
- public:
-  bool DoAction_DocOpen(const CPDF_Action& action,
-                        CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  bool DoAction_JavaScript(const CPDF_Action& JsAction,
-                           WideString csJSName,
-                           CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  bool DoAction_Page(const CPDF_Action& action,
-                     enum CPDF_AAction::AActionType eType,
-                     CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  bool DoAction_Document(const CPDF_Action& action,
-                         enum CPDF_AAction::AActionType eType,
-                         CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  bool DoAction_Field(const CPDF_Action& action,
-                      CPDF_AAction::AActionType type,
-                      CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                      CPDF_FormField* pFormField,
-                      CPDFSDK_FieldAction* data);
-  bool DoAction_FieldJavaScript(const CPDF_Action& JsAction,
-                                CPDF_AAction::AActionType type,
-                                CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                CPDF_FormField* pFormField,
-                                CPDFSDK_FieldAction* data);
-
- private:
-  using RunScriptCallback = std::function<void(IJS_EventContext* context)>;
-
-  void RunScript(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                 const WideString& script,
-                 const RunScriptCallback& cb);
-
-  bool ExecuteDocumentOpenAction(const CPDF_Action& action,
-                                 CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                 std::set<const CPDF_Dictionary*>* visited);
-  bool ExecuteDocumentPageAction(const CPDF_Action& action,
-                                 CPDF_AAction::AActionType type,
-                                 CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                 std::set<const CPDF_Dictionary*>* visited);
-  bool ExecuteFieldAction(const CPDF_Action& action,
-                          CPDF_AAction::AActionType type,
-                          CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                          CPDF_FormField* pFormField,
-                          CPDFSDK_FieldAction* data,
-                          std::set<const CPDF_Dictionary*>* visited);
-
-  void DoAction_NoJs(const CPDF_Action& action,
-                     CPDF_AAction::AActionType type,
-                     CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void RunDocumentPageJavaScript(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                 CPDF_AAction::AActionType type,
-                                 const WideString& script);
-  void RunDocumentOpenJavaScript(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                 const WideString& sScriptName,
-                                 const WideString& script);
-  void RunFieldJavaScript(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                          CPDF_FormField* pFormField,
-                          CPDF_AAction::AActionType type,
-                          CPDFSDK_FieldAction* data,
-                          const WideString& script);
-
-  bool IsValidField(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                    CPDF_Dictionary* pFieldDict);
-
-  void DoAction_GoTo(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                     const CPDF_Action& action);
-  void DoAction_Launch(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                       const CPDF_Action& action);
-  void DoAction_URI(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                    const CPDF_Action& action);
-  void DoAction_Named(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                      const CPDF_Action& action);
-
-  bool DoAction_Hide(const CPDF_Action& action,
-                     CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  bool DoAction_SubmitForm(const CPDF_Action& action,
-                           CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void DoAction_ResetForm(const CPDF_Action& action,
-                          CPDFSDK_FormFillEnvironment* pFormFillEnv);
-};
-
-#endif  // FPDFSDK_CPDFSDK_ACTIONHANDLER_H_
diff --git a/fpdfsdk/cpdfsdk_annot.cpp b/fpdfsdk/cpdfsdk_annot.cpp
index d6712d0..4845e67 100644
--- a/fpdfsdk/cpdfsdk_annot.cpp
+++ b/fpdfsdk/cpdfsdk_annot.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,14 +6,15 @@
 
 #include "fpdfsdk/cpdfsdk_annot.h"
 
-#include <algorithm>
-
 #include "fpdfsdk/cpdfsdk_pageview.h"
+#include "third_party/base/check.h"
 
 CPDFSDK_Annot::CPDFSDK_Annot(CPDFSDK_PageView* pPageView)
-    : m_pPageView(pPageView) {}
+    : m_pPageView(pPageView) {
+  DCHECK(m_pPageView);
+}
 
-CPDFSDK_Annot::~CPDFSDK_Annot() {}
+CPDFSDK_Annot::~CPDFSDK_Annot() = default;
 
 CPDFSDK_BAAnnot* CPDFSDK_Annot::AsBAAnnot() {
   return nullptr;
@@ -23,12 +24,100 @@
   return nullptr;
 }
 
+// static
+void CPDFSDK_Annot::OnMouseEnter(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                                 Mask<FWL_EVENTFLAG> nFlags) {
+  pAnnot->GetUnsafeInputHandlers()->OnMouseEnter(nFlags);
+}
+
+// static
+void CPDFSDK_Annot::OnMouseExit(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                                Mask<FWL_EVENTFLAG> nFlags) {
+  pAnnot->GetUnsafeInputHandlers()->OnMouseExit(nFlags);
+}
+
+// static
+bool CPDFSDK_Annot::OnLButtonDown(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                                  Mask<FWL_EVENTFLAG> nFlags,
+                                  const CFX_PointF& point) {
+  return pAnnot->GetUnsafeInputHandlers()->OnLButtonDown(nFlags, point);
+}
+
+// static
+bool CPDFSDK_Annot::OnLButtonUp(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                                Mask<FWL_EVENTFLAG> nFlags,
+                                const CFX_PointF& point) {
+  return pAnnot->GetUnsafeInputHandlers()->OnLButtonUp(nFlags, point);
+}
+
+// static
+bool CPDFSDK_Annot::OnLButtonDblClk(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                                    Mask<FWL_EVENTFLAG> nFlags,
+                                    const CFX_PointF& point) {
+  return pAnnot->GetUnsafeInputHandlers()->OnLButtonDblClk(nFlags, point);
+}
+
+// static
+bool CPDFSDK_Annot::OnMouseMove(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                                Mask<FWL_EVENTFLAG> nFlags,
+                                const CFX_PointF& point) {
+  return pAnnot->GetUnsafeInputHandlers()->OnMouseMove(nFlags, point);
+}
+
+// static
+bool CPDFSDK_Annot::OnMouseWheel(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                                 Mask<FWL_EVENTFLAG> nFlags,
+                                 const CFX_PointF& point,
+                                 const CFX_Vector& delta) {
+  return pAnnot->GetUnsafeInputHandlers()->OnMouseWheel(nFlags, point, delta);
+}
+
+// static
+bool CPDFSDK_Annot::OnRButtonDown(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                                  Mask<FWL_EVENTFLAG> nFlags,
+                                  const CFX_PointF& point) {
+  return pAnnot->GetUnsafeInputHandlers()->OnRButtonDown(nFlags, point);
+}
+
+// static
+bool CPDFSDK_Annot::OnRButtonUp(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                                Mask<FWL_EVENTFLAG> nFlags,
+                                const CFX_PointF& point) {
+  return pAnnot->GetUnsafeInputHandlers()->OnRButtonUp(nFlags, point);
+}
+
+// static
+bool CPDFSDK_Annot::OnChar(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                           uint32_t nChar,
+                           Mask<FWL_EVENTFLAG> nFlags) {
+  return pAnnot->GetUnsafeInputHandlers()->OnChar(nChar, nFlags);
+}
+
+// static
+bool CPDFSDK_Annot::OnKeyDown(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                              FWL_VKEYCODE nKeyCode,
+                              Mask<FWL_EVENTFLAG> nFlags) {
+  return pAnnot->GetUnsafeInputHandlers()->OnKeyDown(nKeyCode, nFlags);
+}
+
+// static
+bool CPDFSDK_Annot::OnSetFocus(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                               Mask<FWL_EVENTFLAG> nFlags) {
+  return pAnnot->GetUnsafeInputHandlers()->OnSetFocus(nFlags);
+}
+
+// static
+bool CPDFSDK_Annot::OnKillFocus(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                                Mask<FWL_EVENTFLAG> nFlags) {
+  return pAnnot->GetUnsafeInputHandlers()->OnKillFocus(nFlags);
+}
+
 IPDF_Page* CPDFSDK_Annot::GetXFAPage() {
 #ifdef PDF_ENABLE_XFA
-  if (m_pPageView)
-    return m_pPageView->GetXFAPage();
-#endif
+  return m_pPageView->GetXFAPage();
+#else
   return nullptr;
+#endif
 }
 
 int CPDFSDK_Annot::GetLayoutOrder() const {
@@ -39,20 +128,6 @@
   return nullptr;
 }
 
-CPDF_Annot::Subtype CPDFSDK_Annot::GetAnnotSubtype() const {
-  return CPDF_Annot::Subtype::UNKNOWN;
-}
-
-bool CPDFSDK_Annot::IsSignatureWidget() const {
-  return false;
-}
-
-void CPDFSDK_Annot::SetRect(const CFX_FloatRect& rect) {}
-
-CFX_FloatRect CPDFSDK_Annot::GetRect() const {
-  return CFX_FloatRect();
-}
-
 IPDF_Page* CPDFSDK_Annot::GetPage() {
 #ifdef PDF_ENABLE_XFA
   IPDF_Page* pXFAPage = GetXFAPage();
@@ -63,5 +138,5 @@
 }
 
 CPDF_Page* CPDFSDK_Annot::GetPDFPage() {
-  return m_pPageView ? m_pPageView->GetPDFPage() : nullptr;
+  return m_pPageView->GetPDFPage();
 }
diff --git a/fpdfsdk/cpdfsdk_annot.h b/fpdfsdk/cpdfsdk_annot.h
index e722de1..1357594 100644
--- a/fpdfsdk/cpdfsdk_annot.h
+++ b/fpdfsdk/cpdfsdk_annot.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,15 @@
 #ifndef FPDFSDK_CPDFSDK_ANNOT_H_
 #define FPDFSDK_CPDFSDK_ANNOT_H_
 
-#include "core/fpdfdoc/cpdf_aaction.h"
 #include "core/fpdfdoc/cpdf_annot.h"
-#include "core/fpdfdoc/cpdf_defaultappearance.h"
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/mask.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "public/fpdf_fwlevent.h"
 
-class CFX_Matrix;
-class CFX_RenderDevice;
 class CPDF_Page;
-class CPDF_RenderOptions;
 class CPDFSDK_BAAnnot;
 class CPDFSDK_PageView;
 class CPDFXFA_Widget;
@@ -24,34 +23,118 @@
 
 class CPDFSDK_Annot : public Observable {
  public:
-  explicit CPDFSDK_Annot(CPDFSDK_PageView* pPageView);
+  // These methods may destroy the class that implements them when called.
+  // Access through the static methods below of the same name.
+  class UnsafeInputHandlers {
+   public:
+    virtual void OnMouseEnter(Mask<FWL_EVENTFLAG> nFlags) = 0;
+    virtual void OnMouseExit(Mask<FWL_EVENTFLAG> nFlags) = 0;
+    virtual bool OnLButtonDown(Mask<FWL_EVENTFLAG> nFlags,
+                               const CFX_PointF& point) = 0;
+    virtual bool OnLButtonUp(Mask<FWL_EVENTFLAG> nFlags,
+                             const CFX_PointF& point) = 0;
+    virtual bool OnLButtonDblClk(Mask<FWL_EVENTFLAG> nFlags,
+                                 const CFX_PointF& point) = 0;
+    virtual bool OnMouseMove(Mask<FWL_EVENTFLAG> nFlags,
+                             const CFX_PointF& point) = 0;
+    virtual bool OnMouseWheel(Mask<FWL_EVENTFLAG> nFlags,
+                              const CFX_PointF& point,
+                              const CFX_Vector& delta) = 0;
+    virtual bool OnRButtonDown(Mask<FWL_EVENTFLAG> nFlags,
+                               const CFX_PointF& point) = 0;
+    virtual bool OnRButtonUp(Mask<FWL_EVENTFLAG> nFlags,
+                             const CFX_PointF& point) = 0;
+    virtual bool OnChar(uint32_t nChar, Mask<FWL_EVENTFLAG> nFlags) = 0;
+    virtual bool OnKeyDown(FWL_VKEYCODE nKeyCode,
+                           Mask<FWL_EVENTFLAG> nFlags) = 0;
+    virtual bool OnSetFocus(Mask<FWL_EVENTFLAG> nFlags) = 0;
+    virtual bool OnKillFocus(Mask<FWL_EVENTFLAG> nFlags) = 0;
+  };
+
   virtual ~CPDFSDK_Annot();
 
   virtual CPDFSDK_BAAnnot* AsBAAnnot();
   virtual CPDFXFA_Widget* AsXFAWidget();
 
+  // Never returns nullptr.
+  virtual UnsafeInputHandlers* GetUnsafeInputHandlers() = 0;
+
+  virtual void OnLoad() {}
   virtual int GetLayoutOrder() const;
   virtual CPDF_Annot* GetPDFAnnot() const;
-  virtual CPDF_Annot::Subtype GetAnnotSubtype() const;
-  virtual bool IsSignatureWidget() const;
-  virtual CFX_FloatRect GetRect() const;
-  virtual void SetRect(const CFX_FloatRect& rect);
+  virtual CPDF_Annot::Subtype GetAnnotSubtype() const = 0;
+  virtual CFX_FloatRect GetRect() const = 0;
+  virtual void OnDraw(CFX_RenderDevice* pDevice,
+                      const CFX_Matrix& mtUser2Device,
+                      bool bDrawAnnots) = 0;
+  virtual bool DoHitTest(const CFX_PointF& point) = 0;
+  virtual CFX_FloatRect GetViewBBox() = 0;
+  virtual bool CanUndo() = 0;
+  virtual bool CanRedo() = 0;
+  virtual bool Undo() = 0;
+  virtual bool Redo() = 0;
+  virtual WideString GetText() = 0;
+  virtual WideString GetSelectedText() = 0;
+  virtual void ReplaceAndKeepSelection(const WideString& text) = 0;
+  virtual void ReplaceSelection(const WideString& text) = 0;
+  virtual bool SelectAllText() = 0;
+  virtual bool SetIndexSelected(int index, bool selected) = 0;
+  virtual bool IsIndexSelected(int index) = 0;
+
+  // Callers must check if `pAnnot` is still valid after calling these methods,
+  // before accessing them again.
+  static void OnMouseEnter(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                           Mask<FWL_EVENTFLAG> nFlags);
+  static void OnMouseExit(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                          Mask<FWL_EVENTFLAG> nFlags);
+  static bool OnLButtonDown(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                            Mask<FWL_EVENTFLAG> nFlags,
+                            const CFX_PointF& point);
+  static bool OnLButtonUp(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                          Mask<FWL_EVENTFLAG> nFlags,
+                          const CFX_PointF& point);
+  static bool OnLButtonDblClk(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                              Mask<FWL_EVENTFLAG> nFlags,
+                              const CFX_PointF& point);
+  static bool OnMouseMove(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                          Mask<FWL_EVENTFLAG> nFlags,
+                          const CFX_PointF& point);
+  static bool OnMouseWheel(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                           Mask<FWL_EVENTFLAG> nFlags,
+                           const CFX_PointF& point,
+                           const CFX_Vector& delta);
+  static bool OnRButtonDown(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                            Mask<FWL_EVENTFLAG> nFlags,
+                            const CFX_PointF& point);
+  static bool OnRButtonUp(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                          Mask<FWL_EVENTFLAG> nFlags,
+                          const CFX_PointF& point);
+  static bool OnChar(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                     uint32_t nChar,
+                     Mask<FWL_EVENTFLAG> nFlags);
+  static bool OnKeyDown(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                        FWL_VKEYCODE nKeyCode,
+                        Mask<FWL_EVENTFLAG> nFlags);
+  static bool OnSetFocus(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                         Mask<FWL_EVENTFLAG> nFlags);
+  static bool OnKillFocus(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                          Mask<FWL_EVENTFLAG> nFlags);
 
   // Three cases: PDF page only, XFA page only, or XFA page backed by PDF page.
   IPDF_Page* GetPage();     // Returns XFA Page if possible, else PDF page.
   CPDF_Page* GetPDFPage();  // Returns PDF page or nullptr.
   IPDF_Page* GetXFAPage();  // Returns XFA page or nullptr.
 
-  CPDFSDK_PageView* GetPageView() const { return m_pPageView.Get(); }
+  // Never returns nullptr.
+  CPDFSDK_PageView* GetPageView() const { return m_pPageView; }
 
  protected:
+  explicit CPDFSDK_Annot(CPDFSDK_PageView* pPageView);
+
+ private:
   UnownedPtr<CPDFSDK_PageView> const m_pPageView;
 };
 
-inline CPDFSDK_BAAnnot* ToBAAnnot(CPDFSDK_Annot* pAnnot) {
-  return pAnnot ? pAnnot->AsBAAnnot() : nullptr;
-}
-
 inline CPDFXFA_Widget* ToXFAWidget(CPDFSDK_Annot* pAnnot) {
   return pAnnot ? pAnnot->AsXFAWidget() : nullptr;
 }
diff --git a/fpdfsdk/cpdfsdk_annothandlermgr.cpp b/fpdfsdk/cpdfsdk_annothandlermgr.cpp
deleted file mode 100644
index 2a45b0d..0000000
--- a/fpdfsdk/cpdfsdk_annothandlermgr.cpp
+++ /dev/null
@@ -1,332 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/cpdfsdk_annothandlermgr.h"
-
-#include <utility>
-
-#include "core/fpdfapi/parser/cpdf_number.h"
-#include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfdoc/cpdf_annot.h"
-#include "fpdfsdk/cpdfsdk_annot.h"
-#include "fpdfsdk/cpdfsdk_annotiterator.h"
-#include "fpdfsdk/cpdfsdk_baannot.h"
-#include "fpdfsdk/cpdfsdk_baannothandler.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_pageview.h"
-#include "fpdfsdk/cpdfsdk_widget.h"
-#include "fpdfsdk/cpdfsdk_widgethandler.h"
-#include "fpdfsdk/pwl/cpwl_wnd.h"
-#include "public/fpdf_fwlevent.h"
-#include "third_party/base/ptr_util.h"
-
-#ifdef PDF_ENABLE_XFA
-#include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
-#include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h"
-#include "fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h"
-#endif  // PDF_ENABLE_XFA
-
-CPDFSDK_AnnotHandlerMgr::CPDFSDK_AnnotHandlerMgr(
-    std::unique_ptr<CPDFSDK_BAAnnotHandler> pBAAnnotHandler,
-    std::unique_ptr<CPDFSDK_WidgetHandler> pWidgetHandler,
-    std::unique_ptr<IPDFSDK_AnnotHandler> pXFAWidgetHandler)
-    : m_pBAAnnotHandler(std::move(pBAAnnotHandler)),
-      m_pWidgetHandler(std::move(pWidgetHandler)),
-      m_pXFAWidgetHandler(std::move(pXFAWidgetHandler)) {
-  ASSERT(m_pBAAnnotHandler);
-  ASSERT(m_pWidgetHandler);
-}
-
-CPDFSDK_AnnotHandlerMgr::~CPDFSDK_AnnotHandlerMgr() = default;
-
-void CPDFSDK_AnnotHandlerMgr::SetFormFillEnv(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pBAAnnotHandler->SetFormFillEnvironment(pFormFillEnv);
-  m_pWidgetHandler->SetFormFillEnvironment(pFormFillEnv);
-  if (m_pXFAWidgetHandler)
-    m_pXFAWidgetHandler->SetFormFillEnvironment(pFormFillEnv);
-}
-
-CPDFSDK_Annot* CPDFSDK_AnnotHandlerMgr::NewAnnot(CPDF_Annot* pAnnot,
-                                                 CPDFSDK_PageView* pPageView) {
-  ASSERT(pPageView);
-  return GetAnnotHandlerOfType(pAnnot->GetSubtype())
-      ->NewAnnot(pAnnot, pPageView);
-}
-
-#ifdef PDF_ENABLE_XFA
-std::unique_ptr<CPDFSDK_Annot> CPDFSDK_AnnotHandlerMgr::NewXFAAnnot(
-    CXFA_FFWidget* pAnnot,
-    CPDFSDK_PageView* pPageView) {
-  ASSERT(pAnnot);
-  ASSERT(pPageView);
-  return static_cast<CPDFXFA_WidgetHandler*>(m_pXFAWidgetHandler.get())
-      ->NewAnnotForXFA(pAnnot, pPageView);
-}
-#endif  // PDF_ENABLE_XFA
-
-void CPDFSDK_AnnotHandlerMgr::ReleaseAnnot(
-    std::unique_ptr<CPDFSDK_Annot> pAnnot) {
-  IPDFSDK_AnnotHandler* pAnnotHandler = GetAnnotHandler(pAnnot.get());
-  pAnnotHandler->ReleaseAnnot(std::move(pAnnot));
-}
-
-void CPDFSDK_AnnotHandlerMgr::Annot_OnLoad(CPDFSDK_Annot* pAnnot) {
-  ASSERT(pAnnot);
-  GetAnnotHandler(pAnnot)->OnLoad(pAnnot);
-}
-
-WideString CPDFSDK_AnnotHandlerMgr::Annot_GetText(CPDFSDK_Annot* pAnnot) {
-  return GetAnnotHandler(pAnnot)->GetText(pAnnot);
-}
-
-WideString CPDFSDK_AnnotHandlerMgr::Annot_GetSelectedText(
-    CPDFSDK_Annot* pAnnot) {
-  return GetAnnotHandler(pAnnot)->GetSelectedText(pAnnot);
-}
-
-void CPDFSDK_AnnotHandlerMgr::Annot_ReplaceSelection(CPDFSDK_Annot* pAnnot,
-                                                     const WideString& text) {
-  GetAnnotHandler(pAnnot)->ReplaceSelection(pAnnot, text);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_CanUndo(CPDFSDK_Annot* pAnnot) {
-  return GetAnnotHandler(pAnnot)->CanUndo(pAnnot);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_CanRedo(CPDFSDK_Annot* pAnnot) {
-  return GetAnnotHandler(pAnnot)->CanRedo(pAnnot);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_Undo(CPDFSDK_Annot* pAnnot) {
-  return GetAnnotHandler(pAnnot)->Undo(pAnnot);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_Redo(CPDFSDK_Annot* pAnnot) {
-  return GetAnnotHandler(pAnnot)->Redo(pAnnot);
-}
-
-IPDFSDK_AnnotHandler* CPDFSDK_AnnotHandlerMgr::GetAnnotHandler(
-    CPDFSDK_Annot* pAnnot) const {
-  return GetAnnotHandlerOfType(pAnnot->GetAnnotSubtype());
-}
-
-IPDFSDK_AnnotHandler* CPDFSDK_AnnotHandlerMgr::GetAnnotHandlerOfType(
-    CPDF_Annot::Subtype nAnnotSubtype) const {
-  if (nAnnotSubtype == CPDF_Annot::Subtype::WIDGET)
-    return m_pWidgetHandler.get();
-
-#ifdef PDF_ENABLE_XFA
-  if (nAnnotSubtype == CPDF_Annot::Subtype::XFAWIDGET)
-    return m_pXFAWidgetHandler.get();
-#endif  // PDF_ENABLE_XFA
-
-  return m_pBAAnnotHandler.get();
-}
-
-void CPDFSDK_AnnotHandlerMgr::Annot_OnDraw(CPDFSDK_PageView* pPageView,
-                                           CPDFSDK_Annot* pAnnot,
-                                           CFX_RenderDevice* pDevice,
-                                           const CFX_Matrix& mtUser2Device,
-                                           bool bDrawAnnots) {
-  ASSERT(pAnnot);
-  GetAnnotHandler(pAnnot)->OnDraw(pPageView, pAnnot, pDevice, mtUser2Device,
-                                  bDrawAnnots);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_OnLButtonDown(
-    CPDFSDK_PageView* pPageView,
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    uint32_t nFlags,
-    const CFX_PointF& point) {
-  ASSERT(pAnnot->HasObservable());
-  return GetAnnotHandler(pAnnot->Get())
-      ->OnLButtonDown(pPageView, pAnnot, nFlags, point);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_OnLButtonUp(
-    CPDFSDK_PageView* pPageView,
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    uint32_t nFlags,
-    const CFX_PointF& point) {
-  ASSERT(pAnnot->HasObservable());
-  return GetAnnotHandler(pAnnot->Get())
-      ->OnLButtonUp(pPageView, pAnnot, nFlags, point);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_OnLButtonDblClk(
-    CPDFSDK_PageView* pPageView,
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    uint32_t nFlags,
-    const CFX_PointF& point) {
-  ASSERT(pAnnot->HasObservable());
-  return GetAnnotHandler(pAnnot->Get())
-      ->OnLButtonDblClk(pPageView, pAnnot, nFlags, point);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_OnMouseMove(
-    CPDFSDK_PageView* pPageView,
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    uint32_t nFlags,
-    const CFX_PointF& point) {
-  ASSERT(pAnnot->HasObservable());
-  return GetAnnotHandler(pAnnot->Get())
-      ->OnMouseMove(pPageView, pAnnot, nFlags, point);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_OnMouseWheel(
-    CPDFSDK_PageView* pPageView,
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    uint32_t nFlags,
-    short zDelta,
-    const CFX_PointF& point) {
-  ASSERT(pAnnot->HasObservable());
-  return GetAnnotHandler(pAnnot->Get())
-      ->OnMouseWheel(pPageView, pAnnot, nFlags, zDelta, point);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_OnRButtonDown(
-    CPDFSDK_PageView* pPageView,
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    uint32_t nFlags,
-    const CFX_PointF& point) {
-  ASSERT(pAnnot->HasObservable());
-  return GetAnnotHandler(pAnnot->Get())
-      ->OnRButtonDown(pPageView, pAnnot, nFlags, point);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_OnRButtonUp(
-    CPDFSDK_PageView* pPageView,
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    uint32_t nFlags,
-    const CFX_PointF& point) {
-  ASSERT(pAnnot->HasObservable());
-  return GetAnnotHandler(pAnnot->Get())
-      ->OnRButtonUp(pPageView, pAnnot, nFlags, point);
-}
-
-void CPDFSDK_AnnotHandlerMgr::Annot_OnMouseEnter(
-    CPDFSDK_PageView* pPageView,
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    uint32_t nFlag) {
-  ASSERT(pAnnot->HasObservable());
-  GetAnnotHandler(pAnnot->Get())->OnMouseEnter(pPageView, pAnnot, nFlag);
-}
-
-void CPDFSDK_AnnotHandlerMgr::Annot_OnMouseExit(
-    CPDFSDK_PageView* pPageView,
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    uint32_t nFlag) {
-  ASSERT(pAnnot->HasObservable());
-  GetAnnotHandler(pAnnot->Get())->OnMouseExit(pPageView, pAnnot, nFlag);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_OnChar(CPDFSDK_Annot* pAnnot,
-                                           uint32_t nChar,
-                                           uint32_t nFlags) {
-  return GetAnnotHandler(pAnnot)->OnChar(pAnnot, nChar, nFlags);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_OnKeyDown(CPDFSDK_Annot* pAnnot,
-                                              int nKeyCode,
-                                              int nFlag) {
-  if (CPWL_Wnd::IsCTRLKeyDown(nFlag) || CPWL_Wnd::IsALTKeyDown(nFlag)) {
-    return GetAnnotHandler(pAnnot)->OnKeyDown(pAnnot, nKeyCode, nFlag);
-  }
-  ObservedPtr<CPDFSDK_Annot> pObservedAnnot(pAnnot);
-  CPDFSDK_PageView* pPage = pAnnot->GetPageView();
-  CPDFSDK_Annot* pFocusAnnot = pPage->GetFocusAnnot();
-  if (pFocusAnnot && (nKeyCode == FWL_VKEY_Tab)) {
-    ObservedPtr<CPDFSDK_Annot> pNext(
-        GetNextAnnot(pFocusAnnot, !CPWL_Wnd::IsSHIFTKeyDown(nFlag)));
-    if (pNext && pNext.Get() != pFocusAnnot) {
-      pPage->GetFormFillEnv()->SetFocusAnnot(&pNext);
-      return true;
-    }
-  }
-
-  // Check |pAnnot| again because JS may have destroyed it in |GetNextAnnot|
-  if (!pObservedAnnot)
-    return false;
-
-  return GetAnnotHandler(pAnnot)->OnKeyDown(pAnnot, nKeyCode, nFlag);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_OnSetFocus(
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    uint32_t nFlag) {
-  ASSERT(pAnnot->HasObservable());
-  return GetAnnotHandler(pAnnot->Get())->OnSetFocus(pAnnot, nFlag);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_OnKillFocus(
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    uint32_t nFlag) {
-  ASSERT(pAnnot->HasObservable());
-  return GetAnnotHandler(pAnnot->Get())->OnKillFocus(pAnnot, nFlag);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_SetIndexSelected(
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    int index,
-    bool selected) {
-  return GetAnnotHandler(pAnnot->Get())
-      ->SetIndexSelected(pAnnot, index, selected);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_IsIndexSelected(
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    int index) {
-  return GetAnnotHandler(pAnnot->Get())->IsIndexSelected(pAnnot, index);
-}
-
-#ifdef PDF_ENABLE_XFA
-bool CPDFSDK_AnnotHandlerMgr::Annot_OnChangeFocus(
-    ObservedPtr<CPDFSDK_Annot>* pSetAnnot,
-    ObservedPtr<CPDFSDK_Annot>* pKillAnnot) {
-  CPDFXFA_Widget* pSetXFAWidget = ToXFAWidget(pSetAnnot->Get());
-  CPDFXFA_Widget* pKillXFAWidget = ToXFAWidget(pKillAnnot->Get());
-  bool bXFA = (pSetXFAWidget && pSetXFAWidget->GetXFAFFWidget()) ||
-              (pKillXFAWidget && pKillXFAWidget->GetXFAFFWidget());
-
-  return !bXFA || static_cast<CPDFXFA_WidgetHandler*>(m_pXFAWidgetHandler.get())
-                      ->OnXFAChangedFocus(pKillAnnot, pSetAnnot);
-}
-#endif  // PDF_ENABLE_XFA
-
-CFX_FloatRect CPDFSDK_AnnotHandlerMgr::Annot_OnGetViewBBox(
-    CPDFSDK_PageView* pPageView,
-    CPDFSDK_Annot* pAnnot) {
-  ASSERT(pAnnot);
-  return GetAnnotHandler(pAnnot)->GetViewBBox(pPageView, pAnnot);
-}
-
-bool CPDFSDK_AnnotHandlerMgr::Annot_OnHitTest(CPDFSDK_PageView* pPageView,
-                                              CPDFSDK_Annot* pAnnot,
-                                              const CFX_PointF& point) {
-  ASSERT(pAnnot);
-  IPDFSDK_AnnotHandler* pAnnotHandler = GetAnnotHandler(pAnnot);
-  if (pAnnotHandler->CanAnswer(pAnnot))
-    return pAnnotHandler->HitTest(pPageView, pAnnot, point);
-
-  return false;
-}
-
-CPDFSDK_Annot* CPDFSDK_AnnotHandlerMgr::GetNextAnnot(CPDFSDK_Annot* pSDKAnnot,
-                                                     bool bNext) {
-#ifdef PDF_ENABLE_XFA
-  IPDF_Page* pPage = pSDKAnnot->GetPageView()->GetXFAPage();
-  if (pPage && !pPage->AsPDFPage()) {
-    // For xfa annots in XFA pages not backed by PDF pages.
-    return static_cast<CPDFXFA_Page*>(pPage)->GetNextXFAAnnot(pSDKAnnot, bNext);
-  }
-#endif  // PDF_ENABLE_XFA
-
-  // For PDF annots.
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pSDKAnnot);
-  CPDFSDK_AnnotIterator ai(pWidget->GetPageView(), pWidget->GetAnnotSubtype());
-  return bNext ? ai.GetNextAnnot(pWidget) : ai.GetPrevAnnot(pWidget);
-}
diff --git a/fpdfsdk/cpdfsdk_annothandlermgr.h b/fpdfsdk/cpdfsdk_annothandlermgr.h
deleted file mode 100644
index b88f613..0000000
--- a/fpdfsdk/cpdfsdk_annothandlermgr.h
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_CPDFSDK_ANNOTHANDLERMGR_H_
-#define FPDFSDK_CPDFSDK_ANNOTHANDLERMGR_H_
-
-#include <memory>
-
-#include "core/fpdfdoc/cpdf_annot.h"
-#include "core/fxcrt/fx_coordinates.h"
-#include "fpdfsdk/cpdfsdk_annot.h"
-
-class CFX_Matrix;
-class CFX_RenderDevice;
-class CPDFSDK_FormFillEnvironment;
-class CPDFSDK_BAAnnotHandler;
-class CPDFSDK_WidgetHandler;
-class CPDFSDK_PageView;
-class IPDFSDK_AnnotHandler;
-
-#ifdef PDF_ENABLE_XFA
-class CXFA_FFWidget;
-#endif  // PDF_ENABLE_XFA
-
-class CPDFSDK_AnnotHandlerMgr {
- public:
-  CPDFSDK_AnnotHandlerMgr(
-      std::unique_ptr<CPDFSDK_BAAnnotHandler> pBAAnnotHandler,
-      std::unique_ptr<CPDFSDK_WidgetHandler> pWidgetHandler,
-      std::unique_ptr<IPDFSDK_AnnotHandler> pXFAWidgetHandler);
-
-  ~CPDFSDK_AnnotHandlerMgr();
-
-  void SetFormFillEnv(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-
-  CPDFSDK_Annot* NewAnnot(CPDF_Annot* pAnnot, CPDFSDK_PageView* pPageView);
-#ifdef PDF_ENABLE_XFA
-  std::unique_ptr<CPDFSDK_Annot> NewXFAAnnot(CXFA_FFWidget* pAnnot,
-                                             CPDFSDK_PageView* pPageView);
-#endif  // PDF_ENABLE_XFA
-  void ReleaseAnnot(std::unique_ptr<CPDFSDK_Annot> pAnnot);
-
-  void Annot_OnLoad(CPDFSDK_Annot* pAnnot);
-
-  WideString Annot_GetText(CPDFSDK_Annot* pAnnot);
-  WideString Annot_GetSelectedText(CPDFSDK_Annot* pAnnot);
-  void Annot_ReplaceSelection(CPDFSDK_Annot* pAnnot, const WideString& text);
-
-  bool Annot_CanUndo(CPDFSDK_Annot* pAnnot);
-  bool Annot_CanRedo(CPDFSDK_Annot* pAnnot);
-  bool Annot_Undo(CPDFSDK_Annot* pAnnot);
-  bool Annot_Redo(CPDFSDK_Annot* pAnnot);
-
-  void Annot_OnDraw(CPDFSDK_PageView* pPageView,
-                    CPDFSDK_Annot* pAnnot,
-                    CFX_RenderDevice* pDevice,
-                    const CFX_Matrix& mtUser2Device,
-                    bool bDrawAnnots);
-
-  void Annot_OnMouseEnter(CPDFSDK_PageView* pPageView,
-                          ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                          uint32_t nFlags);
-  void Annot_OnMouseExit(CPDFSDK_PageView* pPageView,
-                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                         uint32_t nFlags);
-  bool Annot_OnLButtonDown(CPDFSDK_PageView* pPageView,
-                           ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                           uint32_t nFlags,
-                           const CFX_PointF& point);
-  bool Annot_OnLButtonUp(CPDFSDK_PageView* pPageView,
-                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                         uint32_t nFlags,
-                         const CFX_PointF& point);
-  bool Annot_OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                             uint32_t nFlags,
-                             const CFX_PointF& point);
-  bool Annot_OnMouseMove(CPDFSDK_PageView* pPageView,
-                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                         uint32_t nFlags,
-                         const CFX_PointF& point);
-  bool Annot_OnMouseWheel(CPDFSDK_PageView* pPageView,
-                          ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                          uint32_t nFlags,
-                          short zDelta,
-                          const CFX_PointF& point);
-  bool Annot_OnRButtonDown(CPDFSDK_PageView* pPageView,
-                           ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                           uint32_t nFlags,
-                           const CFX_PointF& point);
-  bool Annot_OnRButtonUp(CPDFSDK_PageView* pPageView,
-                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                         uint32_t nFlags,
-                         const CFX_PointF& point);
-  bool Annot_OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags);
-  bool Annot_OnKeyDown(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag);
-  bool Annot_OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag);
-  bool Annot_OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag);
-  bool Annot_SetIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                              int index,
-                              bool selected);
-  bool Annot_IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot, int index);
-
-#ifdef PDF_ENABLE_XFA
-  bool Annot_OnChangeFocus(ObservedPtr<CPDFSDK_Annot>* pSetAnnot,
-                           ObservedPtr<CPDFSDK_Annot>* pKillAnnot);
-#endif  // PDF_ENABLE_XFA
-
-  CFX_FloatRect Annot_OnGetViewBBox(CPDFSDK_PageView* pPageView,
-                                    CPDFSDK_Annot* pAnnot);
-  bool Annot_OnHitTest(CPDFSDK_PageView* pPageView,
-                       CPDFSDK_Annot* pAnnot,
-                       const CFX_PointF& point);
-
- private:
-  friend class CPDFSDK_BAAnnotHandlerTest;
-
-  IPDFSDK_AnnotHandler* GetAnnotHandler(CPDFSDK_Annot* pAnnot) const;
-  IPDFSDK_AnnotHandler* GetAnnotHandlerOfType(
-      CPDF_Annot::Subtype nAnnotSubtype) const;
-  CPDFSDK_Annot* GetNextAnnot(CPDFSDK_Annot* pSDKAnnot, bool bNext);
-
-  // |m_pBAAnnotHandler| and |m_pWidgetHandler| are always present, but
-  // |m_pXFAWidgetHandler| is only present in XFA mode.
-  std::unique_ptr<CPDFSDK_BAAnnotHandler> const m_pBAAnnotHandler;
-  std::unique_ptr<CPDFSDK_WidgetHandler> const m_pWidgetHandler;
-  std::unique_ptr<IPDFSDK_AnnotHandler> const m_pXFAWidgetHandler;
-};
-
-#endif  // FPDFSDK_CPDFSDK_ANNOTHANDLERMGR_H_
diff --git a/fpdfsdk/cpdfsdk_annotiteration.cpp b/fpdfsdk/cpdfsdk_annotiteration.cpp
index d256950..0ea37cc 100644
--- a/fpdfsdk/cpdfsdk_annotiteration.cpp
+++ b/fpdfsdk/cpdfsdk_annotiteration.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,34 +7,43 @@
 #include "fpdfsdk/cpdfsdk_annotiteration.h"
 
 #include <algorithm>
-#include <utility>
 
 #include "fpdfsdk/cpdfsdk_annot.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 
-CPDFSDK_AnnotIteration::CPDFSDK_AnnotIteration(CPDFSDK_PageView* pPageView,
-                                               bool bReverse) {
-  // Copying/sorting ObservedPtrs is expensive, so do it once at the end.
-  std::vector<CPDFSDK_Annot*> copiedList = pPageView->GetAnnotList();
-  std::stable_sort(copiedList.begin(), copiedList.end(),
+// static
+CPDFSDK_AnnotIteration CPDFSDK_AnnotIteration::CreateForDrawing(
+    CPDFSDK_PageView* page_view) {
+  CPDFSDK_AnnotIteration result(page_view);
+  return CPDFSDK_AnnotIteration(page_view, /*put_focused_annot_at_end=*/true);
+}
+
+CPDFSDK_AnnotIteration::CPDFSDK_AnnotIteration(CPDFSDK_PageView* page_view)
+    : CPDFSDK_AnnotIteration(page_view, /*put_focused_annot_at_end=*/false) {}
+
+CPDFSDK_AnnotIteration::CPDFSDK_AnnotIteration(CPDFSDK_PageView* page_view,
+                                               bool put_focused_annot_at_end) {
+  // Copying ObservedPtrs is expensive, so do it once at the end.
+  std::vector<CPDFSDK_Annot*> copied_list = page_view->GetAnnotList();
+  std::stable_sort(copied_list.begin(), copied_list.end(),
                    [](const CPDFSDK_Annot* p1, const CPDFSDK_Annot* p2) {
                      return p1->GetLayoutOrder() < p2->GetLayoutOrder();
                    });
 
-  CPDFSDK_Annot* pTopMostAnnot = pPageView->GetFocusAnnot();
+  CPDFSDK_Annot* pTopMostAnnot = page_view->GetFocusAnnot();
   if (pTopMostAnnot) {
-    auto it = std::find(copiedList.begin(), copiedList.end(), pTopMostAnnot);
-    if (it != copiedList.end()) {
-      copiedList.erase(it);
-      copiedList.insert(copiedList.begin(), pTopMostAnnot);
+    auto it = std::find(copied_list.begin(), copied_list.end(), pTopMostAnnot);
+    if (it != copied_list.end()) {
+      copied_list.erase(it);
+      auto insert_it =
+          put_focused_annot_at_end ? copied_list.end() : copied_list.begin();
+      copied_list.insert(insert_it, pTopMostAnnot);
     }
   }
-  if (bReverse)
-    std::reverse(copiedList.begin(), copiedList.end());
 
-  m_List.reserve(copiedList.size());
-  for (auto* pAnnot : copiedList)
-    m_List.emplace_back(pAnnot);
+  list_.reserve(copied_list.size());
+  for (auto* pAnnot : copied_list)
+    list_.emplace_back(pAnnot);
 }
 
-CPDFSDK_AnnotIteration::~CPDFSDK_AnnotIteration() {}
+CPDFSDK_AnnotIteration::~CPDFSDK_AnnotIteration() = default;
diff --git a/fpdfsdk/cpdfsdk_annotiteration.h b/fpdfsdk/cpdfsdk_annotiteration.h
index d3a0f39..ff6c2d7 100644
--- a/fpdfsdk/cpdfsdk_annotiteration.h
+++ b/fpdfsdk/cpdfsdk_annotiteration.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,14 +18,21 @@
   using const_iterator =
       std::vector<ObservedPtr<CPDFSDK_Annot>>::const_iterator;
 
-  CPDFSDK_AnnotIteration(CPDFSDK_PageView* pPageView, bool bReverse);
+  static CPDFSDK_AnnotIteration CreateForDrawing(CPDFSDK_PageView* page_view);
+
+  explicit CPDFSDK_AnnotIteration(CPDFSDK_PageView* page_view);
+  CPDFSDK_AnnotIteration(const CPDFSDK_AnnotIteration&) = delete;
+  CPDFSDK_AnnotIteration& operator=(const CPDFSDK_AnnotIteration&) = delete;
   ~CPDFSDK_AnnotIteration();
 
-  const_iterator begin() const { return m_List.begin(); }
-  const_iterator end() const { return m_List.end(); }
+  const_iterator begin() const { return list_.begin(); }
+  const_iterator end() const { return list_.end(); }
 
  private:
-  std::vector<ObservedPtr<CPDFSDK_Annot>> m_List;
+  CPDFSDK_AnnotIteration(CPDFSDK_PageView* page_view,
+                         bool put_focused_annot_at_end);
+
+  std::vector<ObservedPtr<CPDFSDK_Annot>> list_;
 };
 
 #endif  // FPDFSDK_CPDFSDK_ANNOTITERATION_H_
diff --git a/fpdfsdk/cpdfsdk_annotiterator.cpp b/fpdfsdk/cpdfsdk_annotiterator.cpp
index f1919ab..58ec083 100644
--- a/fpdfsdk/cpdfsdk_annotiterator.cpp
+++ b/fpdfsdk/cpdfsdk_annotiterator.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,8 +10,11 @@
 
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fxcrt/stl_util.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
+#include "fpdfsdk/cpdfsdk_widget.h"
+#include "third_party/base/containers/contains.h"
 
 namespace {
 
@@ -27,22 +30,13 @@
   return GetAnnotRect(p1).top > GetAnnotRect(p2).top;
 }
 
-CPDFSDK_AnnotIterator::TabOrder GetTabOrder(CPDFSDK_PageView* pPageView) {
-  CPDF_Page* pPDFPage = pPageView->GetPDFPage();
-  ByteString sTabs = pPDFPage->GetDict()->GetStringFor("Tabs");
-  if (sTabs == "R")
-    return CPDFSDK_AnnotIterator::ROW;
-  if (sTabs == "C")
-    return CPDFSDK_AnnotIterator::COLUMN;
-  return CPDFSDK_AnnotIterator::STRUCTURE;
-}
-
 }  // namespace
 
-CPDFSDK_AnnotIterator::CPDFSDK_AnnotIterator(CPDFSDK_PageView* pPageView,
-                                             CPDF_Annot::Subtype nAnnotSubtype)
+CPDFSDK_AnnotIterator::CPDFSDK_AnnotIterator(
+    CPDFSDK_PageView* pPageView,
+    const std::vector<CPDF_Annot::Subtype>& subtypes_to_iterate)
     : m_pPageView(pPageView),
-      m_nAnnotSubtype(nAnnotSubtype),
+      m_subtypes(subtypes_to_iterate),
       m_eTabOrder(GetTabOrder(pPageView)) {
   GenerateResults();
 }
@@ -63,62 +57,75 @@
     return nullptr;
   ++iter;
   if (iter == m_Annots.end())
-    iter = m_Annots.begin();
+    return nullptr;
   return *iter;
 }
 
 CPDFSDK_Annot* CPDFSDK_AnnotIterator::GetPrevAnnot(CPDFSDK_Annot* pAnnot) {
   auto iter = std::find(m_Annots.begin(), m_Annots.end(), pAnnot);
-  if (iter == m_Annots.end())
+  if (iter == m_Annots.begin() || iter == m_Annots.end())
     return nullptr;
-  if (iter == m_Annots.begin())
-    iter = m_Annots.end();
   return *(--iter);
 }
 
-void CPDFSDK_AnnotIterator::CollectAnnots(std::vector<CPDFSDK_Annot*>* pArray) {
+void CPDFSDK_AnnotIterator::CollectAnnots(
+    std::vector<UnownedPtr<CPDFSDK_Annot>>* pArray) {
   for (auto* pAnnot : m_pPageView->GetAnnotList()) {
-    if (pAnnot->GetAnnotSubtype() == m_nAnnotSubtype &&
-        !pAnnot->IsSignatureWidget()) {
-      pArray->push_back(pAnnot);
+    if (pdfium::Contains(m_subtypes, pAnnot->GetAnnotSubtype())) {
+      CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
+      if (!pWidget || !pWidget->IsSignatureWidget())
+        pArray->emplace_back(pAnnot);
     }
   }
 }
 
 CFX_FloatRect CPDFSDK_AnnotIterator::AddToAnnotsList(
-    std::vector<CPDFSDK_Annot*>* sa,
+    std::vector<UnownedPtr<CPDFSDK_Annot>>* sa,
     size_t idx) {
   CPDFSDK_Annot* pLeftTopAnnot = sa->at(idx);
   CFX_FloatRect rcLeftTop = GetAnnotRect(pLeftTopAnnot);
-  m_Annots.push_back(pLeftTopAnnot);
+  m_Annots.emplace_back(pLeftTopAnnot);
   sa->erase(sa->begin() + idx);
   return rcLeftTop;
 }
 
-void CPDFSDK_AnnotIterator::AddSelectedToAnnots(std::vector<CPDFSDK_Annot*>* sa,
-                                                std::vector<size_t>* aSelect) {
+void CPDFSDK_AnnotIterator::AddSelectedToAnnots(
+    std::vector<UnownedPtr<CPDFSDK_Annot>>* sa,
+    std::vector<size_t>* aSelect) {
   for (size_t i = 0; i < aSelect->size(); ++i)
-    m_Annots.push_back(sa->at(aSelect->at(i)));
+    m_Annots.emplace_back(sa->at(aSelect->at(i)));
 
-  for (int i = aSelect->size() - 1; i >= 0; --i)
-    sa->erase(sa->begin() + aSelect->at(i));
+  for (size_t i = aSelect->size(); i > 0; --i)
+    sa->erase(sa->begin() + aSelect->at(i - 1));
+}
+
+// static
+CPDFSDK_AnnotIterator::TabOrder CPDFSDK_AnnotIterator::GetTabOrder(
+    CPDFSDK_PageView* pPageView) {
+  CPDF_Page* pPDFPage = pPageView->GetPDFPage();
+  ByteString sTabs = pPDFPage->GetDict()->GetByteStringFor("Tabs");
+  if (sTabs == "R")
+    return kRow;
+  if (sTabs == "C")
+    return kColumn;
+  return kStructure;
 }
 
 void CPDFSDK_AnnotIterator::GenerateResults() {
   switch (m_eTabOrder) {
-    case STRUCTURE:
+    case kStructure:
       CollectAnnots(&m_Annots);
       break;
 
-    case ROW: {
-      std::vector<CPDFSDK_Annot*> sa;
+    case kRow: {
+      std::vector<UnownedPtr<CPDFSDK_Annot>> sa;
       CollectAnnots(&sa);
       std::sort(sa.begin(), sa.end(), CompareByLeftAscending);
 
       while (!sa.empty()) {
         int nLeftTopIndex = -1;
         float fTop = 0.0f;
-        for (int i = sa.size() - 1; i >= 0; i--) {
+        for (int i = fxcrt::CollectionSize<int>(sa) - 1; i >= 0; i--) {
           CFX_FloatRect rcAnnot = GetAnnotRect(sa[i]);
           if (rcAnnot.top > fTop) {
             nLeftTopIndex = i;
@@ -142,15 +149,15 @@
       break;
     }
 
-    case COLUMN: {
-      std::vector<CPDFSDK_Annot*> sa;
+    case kColumn: {
+      std::vector<UnownedPtr<CPDFSDK_Annot>> sa;
       CollectAnnots(&sa);
       std::sort(sa.begin(), sa.end(), CompareByTopDescending);
 
       while (!sa.empty()) {
         int nLeftTopIndex = -1;
         float fLeft = -1.0f;
-        for (int i = sa.size() - 1; i >= 0; --i) {
+        for (int i = fxcrt::CollectionSize<int>(sa) - 1; i >= 0; --i) {
           CFX_FloatRect rcAnnot = GetAnnotRect(sa[i]);
           if (fLeft < 0) {
             nLeftTopIndex = 0;
diff --git a/fpdfsdk/cpdfsdk_annotiterator.h b/fpdfsdk/cpdfsdk_annotiterator.h
index fd7cbdc..74f93a4 100644
--- a/fpdfsdk/cpdfsdk_annotiterator.h
+++ b/fpdfsdk/cpdfsdk_annotiterator.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,6 @@
 
 #include "core/fpdfdoc/cpdf_annot.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CPDFSDK_Annot;
@@ -19,10 +18,9 @@
 
 class CPDFSDK_AnnotIterator {
  public:
-  enum TabOrder : uint8_t { STRUCTURE = 0, ROW, COLUMN };
-
-  CPDFSDK_AnnotIterator(CPDFSDK_PageView* pPageView,
-                        CPDF_Annot::Subtype nAnnotSubtype);
+  CPDFSDK_AnnotIterator(
+      CPDFSDK_PageView* pPageView,
+      const std::vector<CPDF_Annot::Subtype>& subtypes_to_iterate);
   ~CPDFSDK_AnnotIterator();
 
   CPDFSDK_Annot* GetFirstAnnot();
@@ -31,16 +29,21 @@
   CPDFSDK_Annot* GetPrevAnnot(CPDFSDK_Annot* pAnnot);
 
  private:
+  enum TabOrder : uint8_t { kStructure = 0, kRow, kColumn };
+
+  static TabOrder GetTabOrder(CPDFSDK_PageView* pPageView);
+
   void GenerateResults();
-  void CollectAnnots(std::vector<CPDFSDK_Annot*>* pArray);
-  CFX_FloatRect AddToAnnotsList(std::vector<CPDFSDK_Annot*>* sa, size_t idx);
-  void AddSelectedToAnnots(std::vector<CPDFSDK_Annot*>* sa,
+  void CollectAnnots(std::vector<UnownedPtr<CPDFSDK_Annot>>* pArray);
+  CFX_FloatRect AddToAnnotsList(std::vector<UnownedPtr<CPDFSDK_Annot>>* sa,
+                                size_t idx);
+  void AddSelectedToAnnots(std::vector<UnownedPtr<CPDFSDK_Annot>>* sa,
                            std::vector<size_t>* aSelect);
 
   UnownedPtr<CPDFSDK_PageView> const m_pPageView;
-  CPDF_Annot::Subtype m_nAnnotSubtype;
+  const std::vector<CPDF_Annot::Subtype> m_subtypes;
   const TabOrder m_eTabOrder;
-  std::vector<CPDFSDK_Annot*> m_Annots;
+  std::vector<UnownedPtr<CPDFSDK_Annot>> m_Annots;
 };
 
 #endif  // FPDFSDK_CPDFSDK_ANNOTITERATOR_H_
diff --git a/fpdfsdk/cpdfsdk_annotiterator_embeddertest.cpp b/fpdfsdk/cpdfsdk_annotiterator_embeddertest.cpp
index 161ae95..1bb785f 100644
--- a/fpdfsdk/cpdfsdk_annotiterator_embeddertest.cpp
+++ b/fpdfsdk/cpdfsdk_annotiterator_embeddertest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -26,7 +26,7 @@
 class CPDFSDK_AnnotIteratorTest : public EmbedderTest {};
 
 TEST_F(CPDFSDK_AnnotIteratorTest, CPDFSDK_AnnotIterator) {
-  EXPECT_TRUE(OpenDocument("annotiter.pdf"));
+  ASSERT_TRUE(OpenDocument("annotiter.pdf"));
   FPDF_PAGE page0 = LoadPage(0);
   FPDF_PAGE page1 = LoadPage(1);
   FPDF_PAGE page2 = LoadPage(2);
@@ -44,8 +44,8 @@
 
   {
     // Page 0 specifies "row order".
-    CPDFSDK_AnnotIterator iter(pFormFillEnv->GetPageView(0),
-                               CPDF_Annot::Subtype::WIDGET);
+    CPDFSDK_AnnotIterator iter(pFormFillEnv->GetPageViewAtIndex(0),
+                               {CPDF_Annot::Subtype::WIDGET});
     CPDFSDK_Annot* pAnnot = iter.GetFirstAnnot();
     CheckRect(pAnnot->GetRect(), RightTop);
     pAnnot = iter.GetNextAnnot(pAnnot);
@@ -55,7 +55,7 @@
     pAnnot = iter.GetNextAnnot(pAnnot);
     CheckRect(pAnnot->GetRect(), LeftBottom);
     pAnnot = iter.GetNextAnnot(pAnnot);
-    EXPECT_EQ(iter.GetFirstAnnot(), pAnnot);
+    EXPECT_FALSE(pAnnot);
 
     pAnnot = iter.GetLastAnnot();
     CheckRect(pAnnot->GetRect(), LeftBottom);
@@ -66,12 +66,12 @@
     pAnnot = iter.GetPrevAnnot(pAnnot);
     CheckRect(pAnnot->GetRect(), RightTop);
     pAnnot = iter.GetPrevAnnot(pAnnot);
-    EXPECT_EQ(iter.GetLastAnnot(), pAnnot);
+    EXPECT_FALSE(pAnnot);
   }
   {
     // Page 1 specifies "column order"
-    CPDFSDK_AnnotIterator iter(pFormFillEnv->GetPageView(1),
-                               CPDF_Annot::Subtype::WIDGET);
+    CPDFSDK_AnnotIterator iter(pFormFillEnv->GetPageViewAtIndex(1),
+                               {CPDF_Annot::Subtype::WIDGET});
     CPDFSDK_Annot* pAnnot = iter.GetFirstAnnot();
     CheckRect(pAnnot->GetRect(), RightTop);
     pAnnot = iter.GetNextAnnot(pAnnot);
@@ -81,7 +81,7 @@
     pAnnot = iter.GetNextAnnot(pAnnot);
     CheckRect(pAnnot->GetRect(), LeftBottom);
     pAnnot = iter.GetNextAnnot(pAnnot);
-    EXPECT_EQ(iter.GetFirstAnnot(), pAnnot);
+    EXPECT_FALSE(pAnnot);
 
     pAnnot = iter.GetLastAnnot();
     CheckRect(pAnnot->GetRect(), LeftBottom);
@@ -92,12 +92,12 @@
     pAnnot = iter.GetPrevAnnot(pAnnot);
     CheckRect(pAnnot->GetRect(), RightTop);
     pAnnot = iter.GetPrevAnnot(pAnnot);
-    EXPECT_EQ(iter.GetLastAnnot(), pAnnot);
+    EXPECT_FALSE(pAnnot);
   }
   {
     // Page 2 specifies "struct order"
-    CPDFSDK_AnnotIterator iter(pFormFillEnv->GetPageView(2),
-                               CPDF_Annot::Subtype::WIDGET);
+    CPDFSDK_AnnotIterator iter(pFormFillEnv->GetPageViewAtIndex(2),
+                               {CPDF_Annot::Subtype::WIDGET});
     CPDFSDK_Annot* pAnnot = iter.GetFirstAnnot();
     CheckRect(pAnnot->GetRect(), LeftBottom);
     pAnnot = iter.GetNextAnnot(pAnnot);
@@ -107,7 +107,7 @@
     pAnnot = iter.GetNextAnnot(pAnnot);
     CheckRect(pAnnot->GetRect(), RightBottom);
     pAnnot = iter.GetNextAnnot(pAnnot);
-    EXPECT_EQ(iter.GetFirstAnnot(), pAnnot);
+    EXPECT_FALSE(pAnnot);
 
     pAnnot = iter.GetLastAnnot();
     CheckRect(pAnnot->GetRect(), RightBottom);
@@ -118,7 +118,7 @@
     pAnnot = iter.GetPrevAnnot(pAnnot);
     CheckRect(pAnnot->GetRect(), LeftBottom);
     pAnnot = iter.GetPrevAnnot(pAnnot);
-    EXPECT_EQ(iter.GetLastAnnot(), pAnnot);
+    EXPECT_FALSE(pAnnot);
   }
   UnloadPage(page2);
   UnloadPage(page1);
diff --git a/fpdfsdk/cpdfsdk_appstream.cpp b/fpdfsdk/cpdfsdk_appstream.cpp
index b6600d2..55c93bf 100644
--- a/fpdfsdk/cpdfsdk_appstream.cpp
+++ b/fpdfsdk/cpdfsdk_appstream.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,16 @@
 
 #include "fpdfsdk/cpdfsdk_appstream.h"
 
+#include <math.h>
+
+#include <iterator>
+#include <memory>
+#include <sstream>
 #include <utility>
 
+#include "constants/appearance.h"
 #include "constants/form_flags.h"
+#include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
@@ -18,19 +25,21 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
-#include "core/fpdfdoc/cba_fontmap.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fpdfdoc/cpdf_bafontmap.h"
 #include "core/fpdfdoc/cpdf_formcontrol.h"
 #include "core/fpdfdoc/cpdf_icon.h"
 #include "core/fpdfdoc/cpvt_word.h"
+#include "core/fxcrt/fx_string_wrappers.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
 #include "fpdfsdk/pwl/cpwl_edit.h"
 #include "fpdfsdk/pwl/cpwl_edit_impl.h"
-#include "fpdfsdk/pwl/cpwl_icon.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/containers/span.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
 
@@ -60,7 +69,6 @@
 const char kMarkedSequenceEndOperator[] = "EMC";
 const char kMoveTextPositionOperator[] = "Td";
 const char kMoveToOperator[] = "m";
-const char kSetCharacterSpacingOperator[] = "Tc";
 const char kSetCMYKOperator[] = "k";
 const char kSetCMKYStrokedOperator[] = "K";
 const char kSetDashOperator[] = "d";
@@ -82,7 +90,7 @@
 
 class AutoClosedCommand {
  public:
-  AutoClosedCommand(std::ostringstream* stream,
+  AutoClosedCommand(fxcrt::ostringstream* stream,
                     ByteString open,
                     ByteString close)
       : stream_(stream), close_(close) {
@@ -92,43 +100,85 @@
   virtual ~AutoClosedCommand() { *stream_ << close_ << "\n"; }
 
  private:
-  std::ostringstream* stream_;
+  UnownedPtr<fxcrt::ostringstream> const stream_;
   ByteString close_;
 };
 
 class AutoClosedQCommand final : public AutoClosedCommand {
  public:
-  explicit AutoClosedQCommand(std::ostringstream* stream)
+  explicit AutoClosedQCommand(fxcrt::ostringstream* stream)
       : AutoClosedCommand(stream, kStateSaveOperator, kStateRestoreOperator) {}
-  ~AutoClosedQCommand() override {}
+  ~AutoClosedQCommand() override = default;
 };
 
-ByteString GetColorAppStream(const CFX_Color& color,
-                             const bool& bFillOrStroke) {
-  std::ostringstream sColorStream;
+void WriteMove(fxcrt::ostringstream& stream, const CFX_PointF& point) {
+  WritePoint(stream, point) << " " << kMoveToOperator << "\n";
+}
 
+void WriteLine(fxcrt::ostringstream& stream, const CFX_PointF& point) {
+  WritePoint(stream, point) << " " << kLineToOperator << "\n";
+}
+
+void WriteClosedLoop(fxcrt::ostringstream& stream,
+                     pdfium::span<const CFX_PointF> points) {
+  WriteMove(stream, points[0]);
+  for (const auto& point : points.subspan(1))
+    WriteLine(stream, point);
+  WriteLine(stream, points[0]);
+}
+
+void WriteBezierCurve(fxcrt::ostringstream& stream,
+                      const CFX_PointF& point1,
+                      const CFX_PointF& point2,
+                      const CFX_PointF& point3) {
+  WritePoint(stream, point1) << " ";
+  WritePoint(stream, point2) << " ";
+  WritePoint(stream, point3) << " " << kCurveToOperator << "\n";
+}
+
+void WriteAppendRect(fxcrt::ostringstream& stream, const CFX_FloatRect& rect) {
+  WriteRect(stream, rect) << " " << kAppendRectOperator << "\n";
+}
+
+ByteString GetStrokeColorAppStream(const CFX_Color& color) {
+  fxcrt::ostringstream sColorStream;
   switch (color.nColorType) {
-    case CFX_Color::kRGB:
+    case CFX_Color::Type::kTransparent:
+      break;
+    case CFX_Color::Type::kGray:
+      sColorStream << color.fColor1 << " " << kSetGrayStrokedOperator << "\n";
+      break;
+    case CFX_Color::Type::kRGB:
       sColorStream << color.fColor1 << " " << color.fColor2 << " "
-                   << color.fColor3 << " "
-                   << (bFillOrStroke ? kSetRGBOperator : kSetRGBStrokedOperator)
-                   << "\n";
+                   << color.fColor3 << " " << kSetRGBStrokedOperator << "\n";
       break;
-    case CFX_Color::kGray:
-      sColorStream << color.fColor1 << " "
-                   << (bFillOrStroke ? kSetGrayOperator
-                                     : kSetGrayStrokedOperator)
-                   << "\n";
-      break;
-    case CFX_Color::kCMYK:
+    case CFX_Color::Type::kCMYK:
       sColorStream << color.fColor1 << " " << color.fColor2 << " "
                    << color.fColor3 << " " << color.fColor4 << " "
-                   << (bFillOrStroke ? kSetCMYKOperator
-                                     : kSetCMKYStrokedOperator)
-                   << "\n";
+                   << kSetCMKYStrokedOperator << "\n";
       break;
   }
+  return ByteString(sColorStream);
+}
 
+ByteString GetFillColorAppStream(const CFX_Color& color) {
+  fxcrt::ostringstream sColorStream;
+  switch (color.nColorType) {
+    case CFX_Color::Type::kTransparent:
+      break;
+    case CFX_Color::Type::kGray:
+      sColorStream << color.fColor1 << " " << kSetGrayOperator << "\n";
+      break;
+    case CFX_Color::Type::kRGB:
+      sColorStream << color.fColor1 << " " << color.fColor2 << " "
+                   << color.fColor3 << " " << kSetRGBOperator << "\n";
+      break;
+    case CFX_Color::Type::kCMYK:
+      sColorStream << color.fColor1 << " " << color.fColor2 << " "
+                   << color.fColor3 << " " << color.fColor4 << " "
+                   << kSetCMYKOperator << "\n";
+      break;
+  }
   return ByteString(sColorStream);
 }
 
@@ -153,36 +203,37 @@
                           {CFX_PointF(0.40f, 0.60f), CFX_PointF(0.28f, 0.66f),
                            CFX_PointF(0.30f, 0.56f)}};
 
-  for (size_t i = 0; i < FX_ArraySize(pts); ++i) {
-    for (size_t j = 0; j < FX_ArraySize(pts[0]); ++j) {
+  for (size_t i = 0; i < std::size(pts); ++i) {
+    for (size_t j = 0; j < std::size(pts[0]); ++j) {
       pts[i][j].x = pts[i][j].x * fWidth + crBBox.left;
       pts[i][j].y *= pts[i][j].y * fHeight + crBBox.bottom;
     }
   }
 
-  std::ostringstream csAP;
-  csAP << pts[0][0].x << " " << pts[0][0].y << " " << kMoveToOperator << "\n";
+  fxcrt::ostringstream csAP;
+  WriteMove(csAP, pts[0][0]);
 
-  for (size_t i = 0; i < FX_ArraySize(pts); ++i) {
-    size_t nNext = i < FX_ArraySize(pts) - 1 ? i + 1 : 0;
+  for (size_t i = 0; i < std::size(pts); ++i) {
+    size_t nNext = i < std::size(pts) - 1 ? i + 1 : 0;
+    const CFX_PointF& pt_next = pts[nNext][0];
 
     float px1 = pts[i][1].x - pts[i][0].x;
     float py1 = pts[i][1].y - pts[i][0].y;
-    float px2 = pts[i][2].x - pts[nNext][0].x;
-    float py2 = pts[i][2].y - pts[nNext][0].y;
+    float px2 = pts[i][2].x - pt_next.x;
+    float py2 = pts[i][2].y - pt_next.y;
 
-    csAP << pts[i][0].x + px1 * FX_BEZIER << " "
-         << pts[i][0].y + py1 * FX_BEZIER << " "
-         << pts[nNext][0].x + px2 * FX_BEZIER << " "
-         << pts[nNext][0].y + py2 * FX_BEZIER << " " << pts[nNext][0].x << " "
-         << pts[nNext][0].y << " " << kCurveToOperator << "\n";
+    WriteBezierCurve(
+        csAP,
+        {pts[i][0].x + px1 * FXSYS_BEZIER, pts[i][0].y + py1 * FXSYS_BEZIER},
+        {pt_next.x + px2 * FXSYS_BEZIER, pt_next.y + py2 * FXSYS_BEZIER},
+        pt_next);
   }
 
   return ByteString(csAP);
 }
 
 ByteString GetAP_Circle(const CFX_FloatRect& crBBox) {
-  std::ostringstream csAP;
+  fxcrt::ostringstream csAP;
 
   float fWidth = crBBox.Width();
   float fHeight = crBBox.Height();
@@ -192,115 +243,101 @@
   CFX_PointF pt3(crBBox.right, crBBox.bottom + fHeight / 2);
   CFX_PointF pt4(crBBox.left + fWidth / 2, crBBox.bottom);
 
-  csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n";
+  WriteMove(csAP, pt1);
 
   float px = pt2.x - pt1.x;
   float py = pt2.y - pt1.y;
 
-  csAP << pt1.x << " " << pt1.y + py * FX_BEZIER << " "
-       << pt2.x - px * FX_BEZIER << " " << pt2.y << " " << pt2.x << " " << pt2.y
-       << " " << kCurveToOperator << "\n";
+  WriteBezierCurve(csAP, {pt1.x, pt1.y + py * FXSYS_BEZIER},
+                   {pt2.x - px * FXSYS_BEZIER, pt2.y}, pt2);
 
   px = pt3.x - pt2.x;
   py = pt2.y - pt3.y;
 
-  csAP << pt2.x + px * FX_BEZIER << " " << pt2.y << " " << pt3.x << " "
-       << pt3.y + py * FX_BEZIER << " " << pt3.x << " " << pt3.y << " "
-       << kCurveToOperator << "\n";
+  WriteBezierCurve(csAP, {pt2.x + px * FXSYS_BEZIER, pt2.y},
+                   {pt3.x, pt3.y + py * FXSYS_BEZIER}, pt3);
 
   px = pt3.x - pt4.x;
   py = pt3.y - pt4.y;
 
-  csAP << pt3.x << " " << pt3.y - py * FX_BEZIER << " "
-       << pt4.x + px * FX_BEZIER << " " << pt4.y << " " << pt4.x << " " << pt4.y
-       << " " << kCurveToOperator << "\n";
+  WriteBezierCurve(csAP, {pt3.x, pt3.y - py * FXSYS_BEZIER},
+                   {pt4.x + px * FXSYS_BEZIER, pt4.y}, pt4);
 
   px = pt4.x - pt1.x;
   py = pt1.y - pt4.y;
 
-  csAP << pt4.x - px * FX_BEZIER << " " << pt4.y << " " << pt1.x << " "
-       << pt1.y - py * FX_BEZIER << " " << pt1.x << " " << pt1.y << " "
-       << kCurveToOperator << "\n";
+  WriteBezierCurve(csAP, {pt4.x - px * FXSYS_BEZIER, pt4.y},
+                   {pt1.x, pt1.y - py * FXSYS_BEZIER}, pt1);
 
   return ByteString(csAP);
 }
 
 ByteString GetAP_Cross(const CFX_FloatRect& crBBox) {
-  std::ostringstream csAP;
+  fxcrt::ostringstream csAP;
 
-  csAP << crBBox.left << " " << crBBox.top << " " << kMoveToOperator << "\n";
-  csAP << crBBox.right << " " << crBBox.bottom << " " << kLineToOperator
-       << "\n";
-  csAP << crBBox.left << " " << crBBox.bottom << " " << kMoveToOperator << "\n";
-  csAP << crBBox.right << " " << crBBox.top << " " << kLineToOperator << "\n";
+  WriteMove(csAP, {crBBox.left, crBBox.top});
+  WriteLine(csAP, {crBBox.right, crBBox.bottom});
+  WriteMove(csAP, {crBBox.left, crBBox.bottom});
+  WriteLine(csAP, {crBBox.right, crBBox.top});
 
   return ByteString(csAP);
 }
 
 ByteString GetAP_Diamond(const CFX_FloatRect& crBBox) {
-  std::ostringstream csAP;
+  fxcrt::ostringstream csAP;
 
   float fWidth = crBBox.Width();
   float fHeight = crBBox.Height();
 
-  CFX_PointF pt1(crBBox.left, crBBox.bottom + fHeight / 2);
-  CFX_PointF pt2(crBBox.left + fWidth / 2, crBBox.top);
-  CFX_PointF pt3(crBBox.right, crBBox.bottom + fHeight / 2);
-  CFX_PointF pt4(crBBox.left + fWidth / 2, crBBox.bottom);
-
-  csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n";
-  csAP << pt2.x << " " << pt2.y << " " << kLineToOperator << "\n";
-  csAP << pt3.x << " " << pt3.y << " " << kLineToOperator << "\n";
-  csAP << pt4.x << " " << pt4.y << " " << kLineToOperator << "\n";
-  csAP << pt1.x << " " << pt1.y << " " << kLineToOperator << "\n";
+  const CFX_PointF points[] = {{crBBox.left, crBBox.bottom + fHeight / 2},
+                               {crBBox.left + fWidth / 2, crBBox.top},
+                               {crBBox.right, crBBox.bottom + fHeight / 2},
+                               {crBBox.left + fWidth / 2, crBBox.bottom}};
+  WriteClosedLoop(csAP, points);
 
   return ByteString(csAP);
 }
 
 ByteString GetAP_Square(const CFX_FloatRect& crBBox) {
-  std::ostringstream csAP;
+  fxcrt::ostringstream csAP;
 
-  csAP << crBBox.left << " " << crBBox.top << " " << kMoveToOperator << "\n";
-  csAP << crBBox.right << " " << crBBox.top << " " << kLineToOperator << "\n";
-  csAP << crBBox.right << " " << crBBox.bottom << " " << kLineToOperator
-       << "\n";
-  csAP << crBBox.left << " " << crBBox.bottom << " " << kLineToOperator << "\n";
-  csAP << crBBox.left << " " << crBBox.top << " " << kLineToOperator << "\n";
+  const CFX_PointF points[] = {{crBBox.left, crBBox.top},
+                               {crBBox.right, crBBox.top},
+                               {crBBox.right, crBBox.bottom},
+                               {crBBox.left, crBBox.bottom}};
+  WriteClosedLoop(csAP, points);
 
   return ByteString(csAP);
 }
 
 ByteString GetAP_Star(const CFX_FloatRect& crBBox) {
-  std::ostringstream csAP;
+  fxcrt::ostringstream csAP;
 
-  float fRadius = (crBBox.top - crBBox.bottom) / (1 + (float)cos(FX_PI / 5.0f));
+  float fRadius = (crBBox.top - crBBox.bottom) / (1 + cosf(FXSYS_PI / 5.0f));
   CFX_PointF ptCenter = CFX_PointF((crBBox.left + crBBox.right) / 2.0f,
                                    (crBBox.top + crBBox.bottom) / 2.0f);
 
-  float px[5];
-  float py[5];
-  float fAngel = FX_PI / 10.0f;
-  for (int32_t i = 0; i < 5; i++) {
-    px[i] = ptCenter.x + fRadius * (float)cos(fAngel);
-    py[i] = ptCenter.y + fRadius * (float)sin(fAngel);
-    fAngel += FX_PI * 2 / 5.0f;
+  CFX_PointF points[5];
+  float fAngle = FXSYS_PI / 10.0f;
+  for (auto& point : points) {
+    point =
+        ptCenter + CFX_PointF(fRadius * cosf(fAngle), fRadius * sinf(fAngle));
+    fAngle += FXSYS_PI * 2 / 5.0f;
   }
 
-  csAP << px[0] << " " << py[0] << " " << kMoveToOperator << "\n";
+  WriteMove(csAP, points[0]);
 
-  int32_t nNext = 0;
-  for (int32_t j = 0; j < 5; j++) {
-    nNext += 2;
-    if (nNext >= 5)
-      nNext -= 5;
-    csAP << px[nNext] << " " << py[nNext] << " " << kLineToOperator << "\n";
+  int next = 0;
+  for (size_t i = 0; i < std::size(points); ++i) {
+    next = (next + 2) % std::size(points);
+    WriteLine(csAP, points[next]);
   }
 
   return ByteString(csAP);
 }
 
 ByteString GetAP_HalfCircle(const CFX_FloatRect& crBBox, float fRotate) {
-  std::ostringstream csAP;
+  fxcrt::ostringstream csAP;
 
   float fWidth = crBBox.Width();
   float fHeight = crBBox.Height();
@@ -309,49 +346,45 @@
   CFX_PointF pt2(0, fHeight / 2);
   CFX_PointF pt3(fWidth / 2, 0);
 
-  float px;
-  float py;
+  CFX_Matrix rotate_matrix(cos(fRotate), sin(fRotate), -sin(fRotate),
+                           cos(fRotate), crBBox.left + fWidth / 2,
+                           crBBox.bottom + fHeight / 2);
+  WriteMatrix(csAP, rotate_matrix) << " " << kConcatMatrixOperator << "\n";
 
-  csAP << cos(fRotate) << " " << sin(fRotate) << " " << -sin(fRotate) << " "
-       << cos(fRotate) << " " << crBBox.left + fWidth / 2 << " "
-       << crBBox.bottom + fHeight / 2 << " " << kConcatMatrixOperator << "\n";
+  WriteMove(csAP, pt1);
 
-  csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n";
+  float px = pt2.x - pt1.x;
+  float py = pt2.y - pt1.y;
 
-  px = pt2.x - pt1.x;
-  py = pt2.y - pt1.y;
-
-  csAP << pt1.x << " " << pt1.y + py * FX_BEZIER << " "
-       << pt2.x - px * FX_BEZIER << " " << pt2.y << " " << pt2.x << " " << pt2.y
-       << " " << kCurveToOperator << "\n";
+  WriteBezierCurve(csAP, {pt1.x, pt1.y + py * FXSYS_BEZIER},
+                   {pt2.x - px * FXSYS_BEZIER, pt2.y}, pt2);
 
   px = pt3.x - pt2.x;
   py = pt2.y - pt3.y;
 
-  csAP << pt2.x + px * FX_BEZIER << " " << pt2.y << " " << pt3.x << " "
-       << pt3.y + py * FX_BEZIER << " " << pt3.x << " " << pt3.y << " "
-       << kCurveToOperator << "\n";
+  WriteBezierCurve(csAP, {pt2.x + px * FXSYS_BEZIER, pt2.y},
+                   {pt3.x, pt3.y + py * FXSYS_BEZIER}, pt3);
 
   return ByteString(csAP);
 }
 
 ByteString GetAppStream_Check(const CFX_FloatRect& rcBBox,
                               const CFX_Color& crText) {
-  std::ostringstream sAP;
+  fxcrt::ostringstream sAP;
   {
     AutoClosedQCommand q(&sAP);
-    sAP << GetColorAppStream(crText, true) << GetAP_Check(rcBBox)
-        << kFillOperator << "\n";
+    sAP << GetFillColorAppStream(crText) << GetAP_Check(rcBBox) << kFillOperator
+        << "\n";
   }
   return ByteString(sAP);
 }
 
 ByteString GetAppStream_Circle(const CFX_FloatRect& rcBBox,
                                const CFX_Color& crText) {
-  std::ostringstream sAP;
+  fxcrt::ostringstream sAP;
   {
     AutoClosedQCommand q(&sAP);
-    sAP << GetColorAppStream(crText, true) << GetAP_Circle(rcBBox)
+    sAP << GetFillColorAppStream(crText) << GetAP_Circle(rcBBox)
         << kFillOperator << "\n";
   }
   return ByteString(sAP);
@@ -359,10 +392,10 @@
 
 ByteString GetAppStream_Cross(const CFX_FloatRect& rcBBox,
                               const CFX_Color& crText) {
-  std::ostringstream sAP;
+  fxcrt::ostringstream sAP;
   {
     AutoClosedQCommand q(&sAP);
-    sAP << GetColorAppStream(crText, false) << GetAP_Cross(rcBBox)
+    sAP << GetStrokeColorAppStream(crText) << GetAP_Cross(rcBBox)
         << kStrokeOperator << "\n";
   }
   return ByteString(sAP);
@@ -370,11 +403,11 @@
 
 ByteString GetAppStream_Diamond(const CFX_FloatRect& rcBBox,
                                 const CFX_Color& crText) {
-  std::ostringstream sAP;
+  fxcrt::ostringstream sAP;
   {
     AutoClosedQCommand q(&sAP);
     sAP << "1 " << kSetLineWidthOperator << "\n"
-        << GetColorAppStream(crText, true) << GetAP_Diamond(rcBBox)
+        << GetFillColorAppStream(crText) << GetAP_Diamond(rcBBox)
         << kFillOperator << "\n";
   }
   return ByteString(sAP);
@@ -382,10 +415,10 @@
 
 ByteString GetAppStream_Square(const CFX_FloatRect& rcBBox,
                                const CFX_Color& crText) {
-  std::ostringstream sAP;
+  fxcrt::ostringstream sAP;
   {
     AutoClosedQCommand q(&sAP);
-    sAP << GetColorAppStream(crText, true) << GetAP_Square(rcBBox)
+    sAP << GetFillColorAppStream(crText) << GetAP_Square(rcBBox)
         << kFillOperator << "\n";
   }
   return ByteString(sAP);
@@ -393,19 +426,19 @@
 
 ByteString GetAppStream_Star(const CFX_FloatRect& rcBBox,
                              const CFX_Color& crText) {
-  std::ostringstream sAP;
+  fxcrt::ostringstream sAP;
   {
     AutoClosedQCommand q(&sAP);
-    sAP << GetColorAppStream(crText, true) << GetAP_Star(rcBBox)
-        << kFillOperator << "\n";
+    sAP << GetFillColorAppStream(crText) << GetAP_Star(rcBBox) << kFillOperator
+        << "\n";
   }
   return ByteString(sAP);
 }
 
 ByteString GetCircleFillAppStream(const CFX_FloatRect& rect,
                                   const CFX_Color& color) {
-  std::ostringstream sAppStream;
-  ByteString sColor = GetColorAppStream(color, true);
+  fxcrt::ostringstream sAppStream;
+  ByteString sColor = GetFillColorAppStream(color);
   if (sColor.GetLength() > 0) {
     AutoClosedQCommand q(&sAppStream);
     sAppStream << sColor << GetAP_Circle(rect) << kFillOperator << "\n";
@@ -420,7 +453,7 @@
                                     const CFX_Color& crRightBottom,
                                     BorderStyle nStyle,
                                     const CPWL_Dash& dash) {
-  std::ostringstream sAppStream;
+  fxcrt::ostringstream sAppStream;
   ByteString sColor;
 
   if (fWidth > 0.0f) {
@@ -432,10 +465,9 @@
     float div = fHalfWidth * 0.75f;
     CFX_FloatRect rect_by_75 = rect.GetDeflated(div, div);
     switch (nStyle) {
-      default:
-      case BorderStyle::SOLID:
-      case BorderStyle::UNDERLINE: {
-        sColor = GetColorAppStream(color, false);
+      case BorderStyle::kSolid:
+      case BorderStyle::kUnderline: {
+        sColor = GetStrokeColorAppStream(color);
         if (sColor.GetLength() > 0) {
           AutoClosedQCommand q2(&sAppStream);
           sAppStream << fWidth << " " << kSetLineWidthOperator << "\n"
@@ -443,8 +475,8 @@
                      << kStrokeOperator << "\n";
         }
       } break;
-      case BorderStyle::DASH: {
-        sColor = GetColorAppStream(color, false);
+      case BorderStyle::kDash: {
+        sColor = GetStrokeColorAppStream(color);
         if (sColor.GetLength() > 0) {
           AutoClosedQCommand q2(&sAppStream);
           sAppStream << fWidth << " " << kSetLineWidthOperator << "\n"
@@ -454,54 +486,52 @@
                      << kStrokeOperator << "\n";
         }
       } break;
-      case BorderStyle::BEVELED: {
-        sColor = GetColorAppStream(color, false);
+      case BorderStyle::kBeveled: {
+        sColor = GetStrokeColorAppStream(color);
         if (sColor.GetLength() > 0) {
           AutoClosedQCommand q2(&sAppStream);
           sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
                      << sColor << GetAP_Circle(rect) << " " << kStrokeOperator
                      << "\n";
         }
-
-        sColor = GetColorAppStream(crLeftTop, false);
+        sColor = GetStrokeColorAppStream(crLeftTop);
         if (sColor.GetLength() > 0) {
           AutoClosedQCommand q2(&sAppStream);
           sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
-                     << sColor << GetAP_HalfCircle(rect_by_75, FX_PI / 4.0f)
+                     << sColor << GetAP_HalfCircle(rect_by_75, FXSYS_PI / 4.0f)
                      << " " << kStrokeOperator << "\n";
         }
-
-        sColor = GetColorAppStream(crRightBottom, false);
+        sColor = GetStrokeColorAppStream(crRightBottom);
         if (sColor.GetLength() > 0) {
           AutoClosedQCommand q2(&sAppStream);
           sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
-                     << sColor << GetAP_HalfCircle(rect_by_75, FX_PI * 5 / 4.0f)
-                     << " " << kStrokeOperator << "\n";
+                     << sColor
+                     << GetAP_HalfCircle(rect_by_75, FXSYS_PI * 5 / 4.0f) << " "
+                     << kStrokeOperator << "\n";
         }
       } break;
-      case BorderStyle::INSET: {
-        sColor = GetColorAppStream(color, false);
+      case BorderStyle::kInset: {
+        sColor = GetStrokeColorAppStream(color);
         if (sColor.GetLength() > 0) {
           AutoClosedQCommand q2(&sAppStream);
           sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
                      << sColor << GetAP_Circle(rect) << " " << kStrokeOperator
                      << "\n";
         }
-
-        sColor = GetColorAppStream(crLeftTop, false);
+        sColor = GetStrokeColorAppStream(crLeftTop);
         if (sColor.GetLength() > 0) {
           AutoClosedQCommand q2(&sAppStream);
           sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
-                     << sColor << GetAP_HalfCircle(rect_by_75, FX_PI / 4.0f)
+                     << sColor << GetAP_HalfCircle(rect_by_75, FXSYS_PI / 4.0f)
                      << " " << kStrokeOperator << "\n";
         }
-
-        sColor = GetColorAppStream(crRightBottom, false);
+        sColor = GetStrokeColorAppStream(crRightBottom);
         if (sColor.GetLength() > 0) {
           AutoClosedQCommand q2(&sAppStream);
           sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
-                     << sColor << GetAP_HalfCircle(rect_by_75, FX_PI * 5 / 4.0f)
-                     << " " << kStrokeOperator << "\n";
+                     << sColor
+                     << GetAP_HalfCircle(rect_by_75, FXSYS_PI * 5 / 4.0f) << " "
+                     << kStrokeOperator << "\n";
         }
       } break;
     }
@@ -514,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:
@@ -539,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:
@@ -569,40 +597,39 @@
   if (sFontAlias.GetLength() <= 0 || fFontSize <= 0)
     return ByteString();
 
-  std::ostringstream sRet;
+  fxcrt::ostringstream sRet;
   sRet << "/" << sFontAlias << " " << fFontSize << " "
        << kSetTextFontAndSizeOperator << "\n";
   return ByteString(sRet);
 }
 
-ByteString GetWordRenderString(const ByteString& strWords) {
-  if (strWords.GetLength() > 0) {
-    return PDF_EncodeString(strWords, false) + " " + kShowTextOperator + "\n";
-  }
-  return ByteString();
+ByteString GetWordRenderString(ByteStringView strWords) {
+  if (strWords.IsEmpty())
+    return ByteString();
+  return PDF_EncodeString(strWords) + " " + kShowTextOperator + "\n";
 }
 
 ByteString GetEditAppStream(CPWL_EditImpl* pEdit,
                             const CFX_PointF& ptOffset,
                             bool bContinuous,
                             uint16_t SubWord) {
-  CPWL_EditImpl_Iterator* pIterator = pEdit->GetIterator();
+  CPWL_EditImpl::Iterator* pIterator = pEdit->GetIterator();
   pIterator->SetAt(0);
 
-  std::ostringstream sEditStream;
-  std::ostringstream sWords;
+  fxcrt::ostringstream sEditStream;
   int32_t nCurFontIndex = -1;
   CFX_PointF ptOld;
   CFX_PointF ptNew;
   CPVT_WordPlace oldplace;
+  ByteString sWords;
 
   while (pIterator->NextWord()) {
     CPVT_WordPlace place = pIterator->GetAt();
     if (bContinuous) {
       if (place.LineCmp(oldplace) != 0) {
-        if (sWords.tellp() > 0) {
-          sEditStream << GetWordRenderString(ByteString(sWords));
-          sWords.str("");
+        if (!sWords.IsEmpty()) {
+          sEditStream << GetWordRenderString(sWords.AsStringView());
+          sWords.clear();
         }
 
         CPVT_Word word;
@@ -617,8 +644,8 @@
         }
 
         if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) {
-          sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y << " "
-                      << kMoveTextPositionOperator << "\n";
+          WritePoint(sEditStream, {ptNew.x - ptOld.x, ptNew.y - ptOld.y})
+              << " " << kMoveTextPositionOperator << "\n";
 
           ptOld = ptNew;
         }
@@ -627,18 +654,17 @@
       CPVT_Word word;
       if (pIterator->GetWord(word)) {
         if (word.nFontIndex != nCurFontIndex) {
-          if (sWords.tellp() > 0) {
-            sEditStream << GetWordRenderString(ByteString(sWords));
-            sWords.str("");
+          if (!sWords.IsEmpty()) {
+            sEditStream << GetWordRenderString(sWords.AsStringView());
+            sWords.clear();
           }
           sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex,
                                           word.fFontSize);
           nCurFontIndex = word.nFontIndex;
         }
 
-        sWords << pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord);
+        sWords += pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord);
       }
-
       oldplace = place;
     } else {
       CPVT_Word word;
@@ -647,94 +673,79 @@
             CFX_PointF(word.ptWord.x + ptOffset.x, word.ptWord.y + ptOffset.y);
 
         if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) {
-          sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y << " "
-                      << kMoveTextPositionOperator << "\n";
+          WritePoint(sEditStream, {ptNew.x - ptOld.x, ptNew.y - ptOld.y})
+              << " " << kMoveTextPositionOperator << "\n";
           ptOld = ptNew;
         }
-
         if (word.nFontIndex != nCurFontIndex) {
           sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex,
                                           word.fFontSize);
           nCurFontIndex = word.nFontIndex;
         }
-
         sEditStream << GetWordRenderString(
-            pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord));
+            pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord)
+                .AsStringView());
       }
     }
   }
 
-  if (sWords.tellp() > 0) {
-    sEditStream << GetWordRenderString(ByteString(sWords));
-    sWords.str("");
-  }
+  if (!sWords.IsEmpty())
+    sEditStream << GetWordRenderString(sWords.AsStringView());
 
-  std::ostringstream sAppStream;
+  fxcrt::ostringstream sAppStream;
   if (sEditStream.tellp() > 0) {
-    float fCharSpace = pEdit->GetCharSpace();
-    if (!IsFloatZero(fCharSpace))
-      sAppStream << fCharSpace << " " << kSetCharacterSpacingOperator << "\n";
-
     sAppStream << sEditStream.str();
   }
-
   return ByteString(sAppStream);
 }
 
 ByteString GenerateIconAppStream(CPDF_IconFit& fit,
-                                 CPDF_Stream* pIconStream,
+                                 RetainPtr<CPDF_Stream> pIconStream,
                                  const CFX_FloatRect& rcIcon) {
   if (rcIcon.IsEmpty() || !pIconStream)
     return ByteString();
 
-  CPWL_Wnd::CreateParams cp;
+  CPWL_Wnd::CreateParams cp(nullptr, nullptr, nullptr);
   cp.dwFlags = PWS_VISIBLE;
-
-  CPWL_Icon icon(cp, pdfium::MakeUnique<CPDF_Icon>(pIconStream), &fit);
-  icon.Realize();
-  if (!icon.Move(rcIcon, false, false))
+  auto pWnd = std::make_unique<CPWL_Wnd>(cp, nullptr);
+  pWnd->Realize();
+  if (!pWnd->Move(rcIcon, false, false))
     return ByteString();
 
-  ByteString sAlias = icon.GetImageAlias();
+  auto pPDFIcon = std::make_unique<CPDF_Icon>(std::move(pIconStream));
+  ByteString sAlias = pPDFIcon->GetImageAlias();
   if (sAlias.GetLength() <= 0)
     return ByteString();
 
-  CFX_FloatRect rcPlate = icon.GetClientRect();
-  CFX_Matrix mt = icon.GetImageMatrix().GetInverse();
+  const CFX_FloatRect rcPlate = pWnd->GetClientRect();
+  const CFX_SizeF image_size = pPDFIcon->GetImageSize();
+  const CFX_Matrix mt = pPDFIcon->GetImageMatrix().GetInverse();
+  const CFX_VectorF scale = fit.GetScale(image_size, rcPlate);
+  const CFX_VectorF offset = fit.GetImageOffset(image_size, scale, rcPlate);
 
-  float fHScale;
-  float fVScale;
-  std::tie(fHScale, fVScale) = icon.GetScale();
-
-  float fx;
-  float fy;
-  std::tie(fx, fy) = icon.GetImageOffset();
-
-  std::ostringstream str;
+  fxcrt::ostringstream str;
   {
     AutoClosedQCommand q(&str);
-    str << rcPlate.left << " " << rcPlate.bottom << " "
-        << rcPlate.right - rcPlate.left << " " << rcPlate.top - rcPlate.bottom
-        << " " << kAppendRectOperator << " " << kSetNonZeroWindingClipOperator
-        << " " << kEndPathNoFillOrStrokeOperator << "\n";
+    WriteAppendRect(str, rcPlate);
+    str << kSetNonZeroWindingClipOperator << " "
+        << kEndPathNoFillOrStrokeOperator << "\n";
 
-    str << fHScale << " 0 0 " << fVScale << " " << rcPlate.left + fx << " "
-        << rcPlate.bottom + fy << " " << kConcatMatrixOperator << "\n";
-    str << mt.a << " " << mt.b << " " << mt.c << " " << mt.d << " " << mt.e
-        << " " << mt.f << " " << kConcatMatrixOperator << "\n";
+    CFX_Matrix scale_matrix(scale.x, 0, 0, scale.y, rcPlate.left + offset.x,
+                            rcPlate.bottom + offset.y);
+    WriteMatrix(str, scale_matrix) << " " << kConcatMatrixOperator << "\n";
+    WriteMatrix(str, mt) << " " << kConcatMatrixOperator << "\n";
 
     str << "0 " << kSetGrayOperator << " 0 " << kSetGrayStrokedOperator << " 1 "
         << kSetLineWidthOperator << " /" << sAlias << " "
         << kInvokeNamedXObjectOperator << "\n";
   }
-  icon.Destroy();
-
+  pWnd->Destroy();
   return ByteString(str);
 }
 
 ByteString GetPushButtonAppStream(const CFX_FloatRect& rcBBox,
                                   IPVT_FontMap* pFontMap,
-                                  CPDF_Stream* pIconStream,
+                                  RetainPtr<CPDF_Stream> pIconStream,
                                   CPDF_IconFit& IconFit,
                                   const WideString& sLabel,
                                   const CFX_Color& crText,
@@ -742,19 +753,20 @@
                                   ButtonStyle nLayOut) {
   const float fAutoFontScale = 1.0f / 3.0f;
 
-  auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>();
+  auto pEdit = std::make_unique<CPWL_EditImpl>();
   pEdit->SetFontMap(pFontMap);
-  pEdit->SetAlignmentH(1, true);
-  pEdit->SetAlignmentV(1, true);
-  pEdit->SetMultiLine(false, true);
-  pEdit->SetAutoReturn(false, true);
-  if (IsFloatZero(fFontSize))
-    pEdit->SetAutoFontSize(true, true);
+  pEdit->SetAlignmentH(1);
+  pEdit->SetAlignmentV(1);
+  pEdit->SetMultiLine(false);
+  pEdit->SetAutoReturn(false);
+  if (FXSYS_IsFloatZero(fFontSize))
+    pEdit->SetAutoFontSize(true);
   else
     pEdit->SetFontSize(fFontSize);
 
   pEdit->Initialize();
   pEdit->SetText(sLabel);
+  pEdit->Paint();
 
   CFX_FloatRect rcLabelContent = pEdit->GetContentRect();
   CFX_FloatRect rcLabel;
@@ -771,7 +783,7 @@
       break;
     case ButtonStyle::kIconTopLabelBottom:
       if (pIconStream) {
-        if (IsFloatZero(fFontSize)) {
+        if (FXSYS_IsFloatZero(fFontSize)) {
           fHeight = rcBBox.Height();
           rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right,
                                   rcBBox.bottom + fHeight * fAutoFontScale);
@@ -795,7 +807,7 @@
       break;
     case ButtonStyle::kIconBottomLabelTop:
       if (pIconStream) {
-        if (IsFloatZero(fFontSize)) {
+        if (FXSYS_IsFloatZero(fFontSize)) {
           fHeight = rcBBox.Height();
           rcLabel =
               CFX_FloatRect(rcBBox.left, rcBBox.top - fHeight * fAutoFontScale,
@@ -820,7 +832,7 @@
       break;
     case ButtonStyle::kIconLeftLabelRight:
       if (pIconStream) {
-        if (IsFloatZero(fFontSize)) {
+        if (FXSYS_IsFloatZero(fFontSize)) {
           fWidth = rcBBox.right - rcBBox.left;
           if (rcLabelContent.Width() < fWidth * fAutoFontScale) {
             rcLabel = CFX_FloatRect(rcBBox.right - fWidth * fAutoFontScale,
@@ -854,7 +866,7 @@
       break;
     case ButtonStyle::kIconRightLabelLeft:
       if (pIconStream) {
-        if (IsFloatZero(fFontSize)) {
+        if (FXSYS_IsFloatZero(fFontSize)) {
           fWidth = rcBBox.right - rcBBox.left;
           if (rcLabelContent.Width() < fWidth * fAutoFontScale) {
             rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom,
@@ -894,29 +906,28 @@
       break;
   }
 
-  std::ostringstream sTemp;
-  sTemp << GenerateIconAppStream(IconFit, pIconStream, rcIcon);
+  fxcrt::ostringstream sTemp;
+  sTemp << GenerateIconAppStream(IconFit, std::move(pIconStream), rcIcon);
 
   if (!rcLabel.IsEmpty()) {
     pEdit->SetPlateRect(rcLabel);
+    pEdit->Paint();
     ByteString sEdit =
         GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, 0.0f), true, 0);
     if (sEdit.GetLength() > 0) {
       AutoClosedCommand bt(&sTemp, kTextBeginOperator, kTextEndOperator);
-      sTemp << GetColorAppStream(crText, true) << sEdit;
+      sTemp << GetFillColorAppStream(crText) << sEdit;
     }
   }
 
   if (sTemp.tellp() <= 0)
     return ByteString();
 
-  std::ostringstream sAppStream;
+  fxcrt::ostringstream sAppStream;
   {
     AutoClosedQCommand q(&sAppStream);
-    sAppStream << rcBBox.left << " " << rcBBox.bottom << " "
-               << rcBBox.right - rcBBox.left << " "
-               << rcBBox.top - rcBBox.bottom << " " << kAppendRectOperator
-               << " " << kSetNonZeroWindingClipOperator << " "
+    WriteAppendRect(sAppStream, rcBBox);
+    sAppStream << kSetNonZeroWindingClipOperator << " "
                << kEndPathNoFillOrStrokeOperator << "\n";
     sAppStream << sTemp.str().c_str();
   }
@@ -930,7 +941,7 @@
                                       const CFX_Color& crRightBottom,
                                       BorderStyle nStyle,
                                       const CPWL_Dash& dash) {
-  std::ostringstream sAppStream;
+  fxcrt::ostringstream sAppStream;
   ByteString sColor;
 
   float fLeft = rect.left;
@@ -943,104 +954,83 @@
     AutoClosedQCommand q(&sAppStream);
 
     switch (nStyle) {
-      default:
-      case BorderStyle::SOLID:
-        sColor = GetColorAppStream(color, true);
+      case BorderStyle::kSolid:
+        sColor = GetFillColorAppStream(color);
         if (sColor.GetLength() > 0) {
           sAppStream << sColor;
-          sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " "
-                     << fTop - fBottom << " " << kAppendRectOperator << "\n";
-          sAppStream << fLeft + fWidth << " " << fBottom + fWidth << " "
-                     << fRight - fLeft - fWidth * 2 << " "
-                     << fTop - fBottom - fWidth * 2 << " "
-                     << kAppendRectOperator << "\n";
+          WriteAppendRect(sAppStream, {fLeft, fBottom, fRight, fTop});
+          WriteAppendRect(sAppStream, {fLeft + fWidth, fBottom + fWidth,
+                                       fRight - fWidth, fTop - fWidth});
           sAppStream << kFillEvenOddOperator << "\n";
         }
         break;
-      case BorderStyle::DASH:
-        sColor = GetColorAppStream(color, false);
+      case BorderStyle::kDash:
+        sColor = GetStrokeColorAppStream(color);
         if (sColor.GetLength() > 0) {
           sAppStream << sColor;
           sAppStream << fWidth << " " << kSetLineWidthOperator << " ["
                      << dash.nDash << " " << dash.nGap << "] " << dash.nPhase
                      << " " << kSetDashOperator << "\n";
-          sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2 << " "
-                     << kMoveToOperator << "\n";
-          sAppStream << fLeft + fWidth / 2 << " " << fTop - fWidth / 2 << " "
-                     << kLineToOperator << "\n";
-          sAppStream << fRight - fWidth / 2 << " " << fTop - fWidth / 2 << " "
-                     << kLineToOperator << "\n";
-          sAppStream << fRight - fWidth / 2 << " " << fBottom + fWidth / 2
-                     << " " << kLineToOperator << "\n";
-          sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2 << " "
-                     << kLineToOperator << " " << kStrokeOperator << "\n";
+          const CFX_PointF points[] = {
+              {fLeft + fWidth / 2, fBottom + fWidth / 2},
+              {fLeft + fWidth / 2, fTop - fWidth / 2},
+              {fRight - fWidth / 2, fTop - fWidth / 2},
+              {fRight - fWidth / 2, fBottom + fWidth / 2}};
+          WriteClosedLoop(sAppStream, points);
+          sAppStream << kStrokeOperator << "\n";
         }
         break;
-      case BorderStyle::BEVELED:
-      case BorderStyle::INSET:
-        sColor = GetColorAppStream(crLeftTop, true);
+      case BorderStyle::kBeveled:
+      case BorderStyle::kInset:
+        sColor = GetFillColorAppStream(crLeftTop);
         if (sColor.GetLength() > 0) {
           sAppStream << sColor;
-          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " "
-                     << kMoveToOperator << "\n";
-          sAppStream << fLeft + fHalfWidth << " " << fTop - fHalfWidth << " "
-                     << kLineToOperator << "\n";
-          sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth << " "
-                     << kLineToOperator << "\n";
-          sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
-                     << " " << kLineToOperator << "\n";
-          sAppStream << fLeft + fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
-                     << " " << kLineToOperator << "\n";
-          sAppStream << fLeft + fHalfWidth * 2 << " "
-                     << fBottom + fHalfWidth * 2 << " " << kLineToOperator
-                     << " " << kFillOperator << "\n";
+          WriteMove(sAppStream, {fLeft + fHalfWidth, fBottom + fHalfWidth});
+          WriteLine(sAppStream, {fLeft + fHalfWidth, fTop - fHalfWidth});
+          WriteLine(sAppStream, {fRight - fHalfWidth, fTop - fHalfWidth});
+          WriteLine(sAppStream,
+                    {fRight - fHalfWidth * 2, fTop - fHalfWidth * 2});
+          WriteLine(sAppStream,
+                    {fLeft + fHalfWidth * 2, fTop - fHalfWidth * 2});
+          WriteLine(sAppStream,
+                    {fLeft + fHalfWidth * 2, fBottom + fHalfWidth * 2});
+          sAppStream << kFillOperator << "\n";
         }
-
-        sColor = GetColorAppStream(crRightBottom, true);
+        sColor = GetFillColorAppStream(crRightBottom);
         if (sColor.GetLength() > 0) {
           sAppStream << sColor;
-          sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth << " "
-                     << kMoveToOperator << "\n";
-          sAppStream << fRight - fHalfWidth << " " << fBottom + fHalfWidth
-                     << " " << kLineToOperator << "\n";
-          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " "
-                     << kLineToOperator << "\n";
-          sAppStream << fLeft + fHalfWidth * 2 << " "
-                     << fBottom + fHalfWidth * 2 << " " << kLineToOperator
-                     << "\n";
-          sAppStream << fRight - fHalfWidth * 2 << " "
-                     << fBottom + fHalfWidth * 2 << " " << kLineToOperator
-                     << "\n";
-          sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
-                     << " " << kLineToOperator << " " << kFillOperator << "\n";
+          WriteMove(sAppStream, {fRight - fHalfWidth, fTop - fHalfWidth});
+          WriteLine(sAppStream, {fRight - fHalfWidth, fBottom + fHalfWidth});
+          WriteLine(sAppStream, {fLeft + fHalfWidth, fBottom + fHalfWidth});
+          WriteLine(sAppStream,
+                    {fLeft + fHalfWidth * 2, fBottom + fHalfWidth * 2});
+          WriteLine(sAppStream,
+                    {fRight - fHalfWidth * 2, fBottom + fHalfWidth * 2});
+          WriteLine(sAppStream,
+                    {fRight - fHalfWidth * 2, fTop - fHalfWidth * 2});
+          sAppStream << kFillOperator << "\n";
         }
-
-        sColor = GetColorAppStream(color, true);
+        sColor = GetFillColorAppStream(color);
         if (sColor.GetLength() > 0) {
           sAppStream << sColor;
-          sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " "
-                     << fTop - fBottom << " " << kAppendRectOperator << "\n";
-          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " "
-                     << fRight - fLeft - fHalfWidth * 2 << " "
-                     << fTop - fBottom - fHalfWidth * 2 << " "
-                     << kAppendRectOperator << " " << kFillEvenOddOperator
-                     << "\n";
+          WriteAppendRect(sAppStream, {fLeft, fBottom, fRight, fTop});
+          WriteAppendRect(sAppStream, {fLeft + fHalfWidth, fBottom + fHalfWidth,
+                                       fRight - fHalfWidth, fTop - fHalfWidth});
+          sAppStream << kFillEvenOddOperator << "\n";
         }
         break;
-      case BorderStyle::UNDERLINE:
-        sColor = GetColorAppStream(color, false);
+      case BorderStyle::kUnderline:
+        sColor = GetStrokeColorAppStream(color);
         if (sColor.GetLength() > 0) {
           sAppStream << sColor;
           sAppStream << fWidth << " " << kSetLineWidthOperator << "\n";
-          sAppStream << fLeft << " " << fBottom + fWidth / 2 << " "
-                     << kMoveToOperator << "\n";
-          sAppStream << fRight << " " << fBottom + fWidth / 2 << " "
-                     << kLineToOperator << " " << kStrokeOperator << "\n";
+          WriteMove(sAppStream, {fLeft, fBottom + fWidth / 2});
+          WriteLine(sAppStream, {fRight, fBottom + fWidth / 2});
+          sAppStream << kStrokeOperator << "\n";
         }
         break;
     }
   }
-
   return ByteString(sAppStream);
 }
 
@@ -1048,40 +1038,36 @@
   if (rcBBox.IsEmpty())
     return ByteString();
 
-  std::ostringstream sAppStream;
+  fxcrt::ostringstream sAppStream;
   {
     AutoClosedQCommand q(&sAppStream);
-    sAppStream << GetColorAppStream(CFX_Color(CFX_Color::kRGB, 220.0f / 255.0f,
-                                              220.0f / 255.0f, 220.0f / 255.0f),
-                                    true)
-               << rcBBox.left << " " << rcBBox.bottom << " "
-               << rcBBox.right - rcBBox.left << " "
-               << rcBBox.top - rcBBox.bottom << " " << kAppendRectOperator
-               << " " << kFillOperator << "\n";
+    sAppStream << GetFillColorAppStream(
+        CFX_Color(CFX_Color::Type::kRGB, 220.0f / 255.0f, 220.0f / 255.0f,
+                  220.0f / 255.0f));
+    WriteAppendRect(sAppStream, rcBBox);
+    sAppStream << kFillOperator << "\n";
   }
 
   {
     AutoClosedQCommand q(&sAppStream);
     sAppStream << GetBorderAppStreamInternal(
-        rcBBox, 2, CFX_Color(CFX_Color::kGray, 0),
-        CFX_Color(CFX_Color::kGray, 1), CFX_Color(CFX_Color::kGray, 0.5),
-        BorderStyle::BEVELED, CPWL_Dash(3, 0, 0));
+        rcBBox, 2, CFX_Color(CFX_Color::Type::kGray, 0),
+        CFX_Color(CFX_Color::Type::kGray, 1),
+        CFX_Color(CFX_Color::Type::kGray, 0.5), BorderStyle::kBeveled,
+        CPWL_Dash(3, 0, 0));
   }
 
   CFX_PointF ptCenter = CFX_PointF((rcBBox.left + rcBBox.right) / 2,
                                    (rcBBox.top + rcBBox.bottom) / 2);
-  if (IsFloatBigger(rcBBox.right - rcBBox.left, 6) &&
-      IsFloatBigger(rcBBox.top - rcBBox.bottom, 6)) {
+  if (FXSYS_IsFloatBigger(rcBBox.right - rcBBox.left, 6) &&
+      FXSYS_IsFloatBigger(rcBBox.top - rcBBox.bottom, 6)) {
     AutoClosedQCommand q(&sAppStream);
-    sAppStream << " 0 " << kSetGrayOperator << "\n"
-               << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " "
-               << kMoveToOperator << "\n"
-               << ptCenter.x + 3 << " " << ptCenter.y + 1.5f << " "
-               << kLineToOperator << "\n"
-               << ptCenter.x << " " << ptCenter.y - 1.5f << " "
-               << kLineToOperator << "\n"
-               << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " "
-               << kLineToOperator << " " << kFillOperator << "\n";
+    const CFX_PointF points[] = {{ptCenter.x - 3, ptCenter.y + 1.5f},
+                                 {ptCenter.x + 3, ptCenter.y + 1.5f},
+                                 {ptCenter.x, ptCenter.y - 1.5f}};
+    sAppStream << " 0 " << kSetGrayOperator << "\n";
+    WriteClosedLoop(sAppStream, points);
+    sAppStream << kFillOperator << "\n";
   }
 
   return ByteString(sAppStream);
@@ -1089,13 +1075,13 @@
 
 ByteString GetRectFillAppStream(const CFX_FloatRect& rect,
                                 const CFX_Color& color) {
-  std::ostringstream sAppStream;
-  ByteString sColor = GetColorAppStream(color, true);
+  fxcrt::ostringstream sAppStream;
+  ByteString sColor = GetFillColorAppStream(color);
   if (sColor.GetLength() > 0) {
     AutoClosedQCommand q(&sAppStream);
-    sAppStream << sColor << rect.left << " " << rect.bottom << " "
-               << rect.right - rect.left << " " << rect.top - rect.bottom << " "
-               << kAppendRectOperator << " " << kFillOperator << "\n";
+    sAppStream << sColor;
+    WriteAppendRect(sAppStream, rect);
+    sAppStream << kFillOperator << "\n";
   }
 
   return ByteString(sAppStream);
@@ -1105,7 +1091,7 @@
   if (!pIcon)
     return;
 
-  CPDF_Dictionary* pImageDict = pIcon->GetDict();
+  RetainPtr<CPDF_Dictionary> pImageDict = pIcon->GetMutableDict();
   if (!pImageDict)
     return;
 
@@ -1115,13 +1101,36 @@
   pImageDict->SetNewFor<CPDF_String>("Name", name, false);
 }
 
+absl::optional<CheckStyle> CheckStyleFromCaption(const WideString& caption) {
+  if (caption.IsEmpty())
+    return absl::nullopt;
+
+  // Character values are ZapfDingbats encodings of named glyphs.
+  switch (caption[0]) {
+    case L'4':
+      return CheckStyle::kCheck;
+    case L'8':
+      return CheckStyle::kCross;
+    case L'H':
+      return CheckStyle::kStar;
+    case L'l':
+      return CheckStyle::kCircle;
+    case L'n':
+      return CheckStyle::kSquare;
+    case L'u':
+      return CheckStyle::kDiamond;
+    default:
+      return absl::nullopt;
+  }
+}
+
 }  // namespace
 
 CPDFSDK_AppStream::CPDFSDK_AppStream(CPDFSDK_Widget* widget,
                                      CPDF_Dictionary* dict)
     : widget_(widget), dict_(dict) {}
 
-CPDFSDK_AppStream::~CPDFSDK_AppStream() {}
+CPDFSDK_AppStream::~CPDFSDK_AppStream() = default;
 
 void CPDFSDK_AppStream::SetAsPushButton() {
   CPDF_FormControl* pControl = widget_->GetFormControl();
@@ -1151,17 +1160,8 @@
       break;
   }
 
-  CFX_Color crBackground;
-  CFX_Color crBorder;
-  int iColorType;
-  float fc[4];
-  pControl->GetOriginalBackgroundColor(iColorType, fc);
-  if (iColorType > 0)
-    crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-
-  pControl->GetOriginalBorderColor(iColorType, fc);
-  if (iColorType > 0)
-    crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
+  CFX_Color crBackground = pControl->GetOriginalBackgroundColor();
+  CFX_Color crBorder = pControl->GetOriginalBorderColor();
 
   float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
   CPWL_Dash dsBorder(3, 0, 0);
@@ -1170,37 +1170,33 @@
 
   BorderStyle nBorderStyle = widget_->GetBorderStyle();
   switch (nBorderStyle) {
-    case BorderStyle::DASH:
+    case BorderStyle::kDash:
       dsBorder = CPWL_Dash(3, 3, 0);
       break;
-    case BorderStyle::BEVELED:
+    case BorderStyle::kBeveled:
       fBorderWidth *= 2;
-      crLeftTop = CFX_Color(CFX_Color::kGray, 1);
+      crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
       crRightBottom = crBackground / 2.0f;
       break;
-    case BorderStyle::INSET:
+    case BorderStyle::kInset:
       fBorderWidth *= 2;
-      crLeftTop = CFX_Color(CFX_Color::kGray, 0.5);
-      crRightBottom = CFX_Color(CFX_Color::kGray, 0.75);
+      crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5);
+      crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75);
       break;
     default:
       break;
   }
 
   CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth);
-  CFX_Color crText(CFX_Color::kGray, 0);
-  ByteString csNameTag;
   CPDF_DefaultAppearance da = pControl->GetDefaultAppearance();
-  Optional<CFX_Color::Type> color = da.GetColor(fc);
-  if (color) {
-    iColorType = *color;
-    crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-  }
+  absl::optional<CFX_Color> color = da.GetColor();
+  CFX_Color crText = color.value_or(CFX_Color(CFX_Color::Type::kGray, 0));
 
   float fFontSize;
-  Optional<ByteString> font = da.GetFont(&fFontSize);
-  if (font)
-    csNameTag = *font;
+  ByteString csNameTag;
+  absl::optional<ByteString> font = da.GetFont(&fFontSize);
+  if (font.has_value())
+    csNameTag = font.value();
   else
     fFontSize = 12.0f;
 
@@ -1208,58 +1204,63 @@
   WideString csNormalCaption;
   WideString csRolloverCaption;
   WideString csDownCaption;
-  if (pControl->HasMKEntry("CA"))
+  if (pControl->HasMKEntry(pdfium::appearance::kCA))
     csNormalCaption = pControl->GetNormalCaption();
 
-  if (pControl->HasMKEntry("RC"))
+  if (pControl->HasMKEntry(pdfium::appearance::kRC))
     csRolloverCaption = pControl->GetRolloverCaption();
 
-  if (pControl->HasMKEntry("AC"))
+  if (pControl->HasMKEntry(pdfium::appearance::kAC))
     csDownCaption = pControl->GetDownCaption();
 
-  CPDF_Stream* pNormalIcon = nullptr;
-  CPDF_Stream* pRolloverIcon = nullptr;
-  CPDF_Stream* pDownIcon = nullptr;
-  if (pControl->HasMKEntry("I"))
+  RetainPtr<CPDF_Stream> pNormalIcon;
+  RetainPtr<CPDF_Stream> pRolloverIcon;
+  RetainPtr<CPDF_Stream> pDownIcon;
+  if (pControl->HasMKEntry(pdfium::appearance::kI))
     pNormalIcon = pControl->GetNormalIcon();
 
-  if (pControl->HasMKEntry("RI"))
+  if (pControl->HasMKEntry(pdfium::appearance::kRI))
     pRolloverIcon = pControl->GetRolloverIcon();
 
-  if (pControl->HasMKEntry("IX"))
+  if (pControl->HasMKEntry(pdfium::appearance::kIX))
     pDownIcon = pControl->GetDownIcon();
 
-  SetDefaultIconName(pNormalIcon, "ImgA");
-  SetDefaultIconName(pRolloverIcon, "ImgB");
-  SetDefaultIconName(pDownIcon, "ImgC");
-
-  CBA_FontMap font_map(widget_->GetPDFPage()->GetDocument(),
-                       widget_->GetPDFAnnot()->GetAnnotDict());
-  font_map.SetAPType("N");
+  SetDefaultIconName(pNormalIcon.Get(), "ImgA");
+  SetDefaultIconName(pRolloverIcon.Get(), "ImgB");
+  SetDefaultIconName(pDownIcon.Get(), "ImgC");
 
   CPDF_IconFit iconFit = pControl->GetIconFit();
-  ByteString csAP =
-      GetRectFillAppStream(rcWindow, crBackground) +
-      GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
-                                 crRightBottom, nBorderStyle, dsBorder) +
-      GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient,
-                             &font_map, pNormalIcon, iconFit, csNormalCaption,
-                             crText, fFontSize, nLayout);
+  {
+    CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
+                            widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N");
+    ByteString csAP =
+        GetRectFillAppStream(rcWindow, crBackground) +
+        GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
+                                   crRightBottom, nBorderStyle, dsBorder) +
+        GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient,
+                               &font_map, pNormalIcon, iconFit, csNormalCaption,
+                               crText, fFontSize, nLayout);
 
-  Write("N", csAP, ByteString());
-  if (pNormalIcon)
-    AddImage("N", pNormalIcon);
+    Write("N", csAP, ByteString());
+    if (pNormalIcon)
+      AddImage("N", pNormalIcon.Get());
 
-  CPDF_FormControl::HighlightingMode eHLM = pControl->GetHighlightingMode();
-  if (eHLM == CPDF_FormControl::Push || eHLM == CPDF_FormControl::Toggle) {
+    CPDF_FormControl::HighlightingMode eHLM = pControl->GetHighlightingMode();
+    if (eHLM != CPDF_FormControl::kPush && eHLM != CPDF_FormControl::kToggle) {
+      Remove("D");
+      Remove("R");
+      return;
+    }
+
     if (csRolloverCaption.IsEmpty() && !pRolloverIcon) {
       csRolloverCaption = csNormalCaption;
       pRolloverIcon = pNormalIcon;
     }
-
-    font_map.SetAPType("R");
-
-    csAP =
+  }
+  {
+    CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
+                            widget_->GetPDFAnnot()->GetMutableAnnotDict(), "R");
+    ByteString csAP =
         GetRectFillAppStream(rcWindow, crBackground) +
         GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
                                    crRightBottom, nBorderStyle, dsBorder) +
@@ -1269,7 +1270,7 @@
 
     Write("R", csAP, ByteString());
     if (pRolloverIcon)
-      AddImage("R", pRolloverIcon);
+      AddImage("R", pRolloverIcon.Get());
 
     if (csDownCaption.IsEmpty() && !pDownIcon) {
       csDownCaption = csNormalCaption;
@@ -1277,24 +1278,25 @@
     }
 
     switch (nBorderStyle) {
-      case BorderStyle::BEVELED: {
+      case BorderStyle::kBeveled: {
         CFX_Color crTemp = crLeftTop;
         crLeftTop = crRightBottom;
         crRightBottom = crTemp;
         break;
       }
-      case BorderStyle::INSET: {
-        crLeftTop = CFX_Color(CFX_Color::kGray, 0);
-        crRightBottom = CFX_Color(CFX_Color::kGray, 1);
+      case BorderStyle::kInset: {
+        crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0);
+        crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1);
         break;
       }
       default:
         break;
     }
-
-    font_map.SetAPType("D");
-
-    csAP =
+  }
+  {
+    CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
+                            widget_->GetPDFAnnot()->GetMutableAnnotDict(), "D");
+    ByteString csAP =
         GetRectFillAppStream(rcWindow, crBackground - 0.25f) +
         GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
                                    crRightBottom, nBorderStyle, dsBorder) +
@@ -1304,45 +1306,33 @@
 
     Write("D", csAP, ByteString());
     if (pDownIcon)
-      AddImage("D", pDownIcon);
-  } else {
-    Remove("D");
-    Remove("R");
+      AddImage("D", pDownIcon.Get());
   }
 }
 
 void CPDFSDK_AppStream::SetAsCheckBox() {
   CPDF_FormControl* pControl = widget_->GetFormControl();
-  CFX_Color crBackground, crBorder, crText;
-  int iColorType;
-  float fc[4];
-
-  pControl->GetOriginalBackgroundColor(iColorType, fc);
-  if (iColorType > 0)
-    crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-
-  pControl->GetOriginalBorderColor(iColorType, fc);
-  if (iColorType > 0)
-    crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-
+  CFX_Color crBackground = pControl->GetOriginalBackgroundColor();
+  CFX_Color crBorder = pControl->GetOriginalBorderColor();
   float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
   CPWL_Dash dsBorder(3, 0, 0);
-  CFX_Color crLeftTop, crRightBottom;
+  CFX_Color crLeftTop;
+  CFX_Color crRightBottom;
 
   BorderStyle nBorderStyle = widget_->GetBorderStyle();
   switch (nBorderStyle) {
-    case BorderStyle::DASH:
+    case BorderStyle::kDash:
       dsBorder = CPWL_Dash(3, 3, 0);
       break;
-    case BorderStyle::BEVELED:
+    case BorderStyle::kBeveled:
       fBorderWidth *= 2;
-      crLeftTop = CFX_Color(CFX_Color::kGray, 1);
+      crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
       crRightBottom = crBackground / 2.0f;
       break;
-    case BorderStyle::INSET:
+    case BorderStyle::kInset:
       fBorderWidth *= 2;
-      crLeftTop = CFX_Color(CFX_Color::kGray, 0.5);
-      crRightBottom = CFX_Color(CFX_Color::kGray, 0.75);
+      crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5);
+      crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75);
       break;
     default:
       break;
@@ -1350,38 +1340,11 @@
 
   CFX_FloatRect rcWindow = widget_->GetRotatedRect();
   CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth);
-  CPDF_DefaultAppearance da = pControl->GetDefaultAppearance();
-  Optional<CFX_Color::Type> color = da.GetColor(fc);
-  if (color) {
-    iColorType = *color;
-    crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-  }
+  absl::optional<CFX_Color> color = pControl->GetDefaultAppearance().GetColor();
+  CFX_Color crText = color.value_or(CFX_Color());
 
-  CheckStyle nStyle = CheckStyle::kCheck;
-  WideString csWCaption = pControl->GetNormalCaption();
-  if (csWCaption.GetLength() > 0) {
-    switch (csWCaption[0]) {
-      case L'l':
-        nStyle = CheckStyle::kCircle;
-        break;
-      case L'8':
-        nStyle = CheckStyle::kCross;
-        break;
-      case L'u':
-        nStyle = CheckStyle::kDiamond;
-        break;
-      case L'n':
-        nStyle = CheckStyle::kSquare;
-        break;
-      case L'H':
-        nStyle = CheckStyle::kStar;
-        break;
-      case L'4':
-      default:
-        nStyle = CheckStyle::kCheck;
-    }
-  }
-
+  CheckStyle nStyle = CheckStyleFromCaption(pControl->GetNormalCaption())
+                          .value_or(CheckStyle::kCheck);
   ByteString csAP_N_ON =
       GetRectFillAppStream(rcWindow, crBackground) +
       GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
@@ -1390,15 +1353,15 @@
   ByteString csAP_N_OFF = csAP_N_ON;
 
   switch (nBorderStyle) {
-    case BorderStyle::BEVELED: {
+    case BorderStyle::kBeveled: {
       CFX_Color crTemp = crLeftTop;
       crLeftTop = crRightBottom;
       crRightBottom = crTemp;
       break;
     }
-    case BorderStyle::INSET: {
-      crLeftTop = CFX_Color(CFX_Color::kGray, 0);
-      crRightBottom = CFX_Color(CFX_Color::kGray, 1);
+    case BorderStyle::kInset: {
+      crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0);
+      crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1);
       break;
     }
     default:
@@ -1423,43 +1386,32 @@
 
   ByteString csAS = widget_->GetAppState();
   if (csAS.IsEmpty())
-    widget_->SetAppState("Off");
+    widget_->SetAppStateOff();
 }
 
 void CPDFSDK_AppStream::SetAsRadioButton() {
   CPDF_FormControl* pControl = widget_->GetFormControl();
-  CFX_Color crBackground;
-  CFX_Color crBorder;
-  CFX_Color crText;
-  int iColorType;
-  float fc[4];
-
-  pControl->GetOriginalBackgroundColor(iColorType, fc);
-  if (iColorType > 0)
-    crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-
-  pControl->GetOriginalBorderColor(iColorType, fc);
-  if (iColorType > 0)
-    crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-
+  CFX_Color crBackground = pControl->GetOriginalBackgroundColor();
+  CFX_Color crBorder = pControl->GetOriginalBorderColor();
   float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
   CPWL_Dash dsBorder(3, 0, 0);
   CFX_Color crLeftTop;
   CFX_Color crRightBottom;
+
   BorderStyle nBorderStyle = widget_->GetBorderStyle();
   switch (nBorderStyle) {
-    case BorderStyle::DASH:
+    case BorderStyle::kDash:
       dsBorder = CPWL_Dash(3, 3, 0);
       break;
-    case BorderStyle::BEVELED:
+    case BorderStyle::kBeveled:
       fBorderWidth *= 2;
-      crLeftTop = CFX_Color(CFX_Color::kGray, 1);
+      crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
       crRightBottom = crBackground / 2.0f;
       break;
-    case BorderStyle::INSET:
+    case BorderStyle::kInset:
       fBorderWidth *= 2;
-      crLeftTop = CFX_Color(CFX_Color::kGray, 0.5);
-      crRightBottom = CFX_Color(CFX_Color::kGray, 0.75);
+      crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5);
+      crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75);
       break;
     default:
       break;
@@ -1467,47 +1419,20 @@
 
   CFX_FloatRect rcWindow = widget_->GetRotatedRect();
   CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth);
-  CPDF_DefaultAppearance da = pControl->GetDefaultAppearance();
-  Optional<CFX_Color::Type> color = da.GetColor(fc);
-  if (color) {
-    iColorType = *color;
-    crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-  }
-
-  CheckStyle nStyle = CheckStyle::kCircle;
-  WideString csWCaption = pControl->GetNormalCaption();
-  if (csWCaption.GetLength() > 0) {
-    switch (csWCaption[0]) {
-      case L'8':
-        nStyle = CheckStyle::kCross;
-        break;
-      case L'u':
-        nStyle = CheckStyle::kDiamond;
-        break;
-      case L'n':
-        nStyle = CheckStyle::kSquare;
-        break;
-      case L'H':
-        nStyle = CheckStyle::kStar;
-        break;
-      case L'4':
-        nStyle = CheckStyle::kCheck;
-        break;
-      case L'l':
-      default:
-        nStyle = CheckStyle::kCircle;
-    }
-  }
+  absl::optional<CFX_Color> color = pControl->GetDefaultAppearance().GetColor();
+  CFX_Color crText = color.value_or(CFX_Color());
+  CheckStyle nStyle = CheckStyleFromCaption(pControl->GetNormalCaption())
+                          .value_or(CheckStyle::kCircle);
 
   ByteString csAP_N_ON;
   CFX_FloatRect rcCenter = rcWindow.GetCenterSquare().GetDeflated(1.0f, 1.0f);
   if (nStyle == CheckStyle::kCircle) {
-    if (nBorderStyle == BorderStyle::BEVELED) {
-      crLeftTop = CFX_Color(CFX_Color::kGray, 1);
+    if (nBorderStyle == BorderStyle::kBeveled) {
+      crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
       crRightBottom = crBackground - 0.25f;
-    } else if (nBorderStyle == BorderStyle::INSET) {
-      crLeftTop = CFX_Color(CFX_Color::kGray, 0.5f);
-      crRightBottom = CFX_Color(CFX_Color::kGray, 0.75f);
+    } else if (nBorderStyle == BorderStyle::kInset) {
+      crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5f);
+      crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75f);
     }
 
     csAP_N_ON =
@@ -1524,15 +1449,15 @@
   ByteString csAP_N_OFF = csAP_N_ON;
 
   switch (nBorderStyle) {
-    case BorderStyle::BEVELED: {
+    case BorderStyle::kBeveled: {
       CFX_Color crTemp = crLeftTop;
       crLeftTop = crRightBottom;
       crRightBottom = crTemp;
       break;
     }
-    case BorderStyle::INSET: {
-      crLeftTop = CFX_Color(CFX_Color::kGray, 0);
-      crRightBottom = CFX_Color(CFX_Color::kGray, 1);
+    case BorderStyle::kInset: {
+      crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0);
+      crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1);
       break;
     }
     default:
@@ -1543,13 +1468,13 @@
 
   if (nStyle == CheckStyle::kCircle) {
     CFX_Color crBK = crBackground - 0.25f;
-    if (nBorderStyle == BorderStyle::BEVELED) {
+    if (nBorderStyle == BorderStyle::kBeveled) {
       crLeftTop = crBackground - 0.25f;
-      crRightBottom = CFX_Color(CFX_Color::kGray, 1);
+      crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1);
       crBK = crBackground;
-    } else if (nBorderStyle == BorderStyle::INSET) {
-      crLeftTop = CFX_Color(CFX_Color::kGray, 0);
-      crRightBottom = CFX_Color(CFX_Color::kGray, 1);
+    } else if (nBorderStyle == BorderStyle::kInset) {
+      crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0);
+      crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1);
     }
 
     csAP_D_ON =
@@ -1565,8 +1490,9 @@
 
   ByteString csAP_D_OFF = csAP_D_ON;
 
-  csAP_N_ON += GetRadioButtonAppStream(rcClient, nStyle, crText);
-  csAP_D_ON += GetRadioButtonAppStream(rcClient, nStyle, crText);
+  ByteString app_stream = GetRadioButtonAppStream(rcClient, nStyle, crText);
+  csAP_N_ON += app_stream;
+  csAP_D_ON += app_stream;
 
   Write("N", csAP_N_ON, pControl->GetCheckedAPState());
   Write("N", csAP_N_OFF, "Off");
@@ -1576,13 +1502,13 @@
 
   ByteString csAS = widget_->GetAppState();
   if (csAS.IsEmpty())
-    widget_->SetAppState("Off");
+    widget_->SetAppStateOff();
 }
 
-void CPDFSDK_AppStream::SetAsComboBox(Optional<WideString> sValue) {
+void CPDFSDK_AppStream::SetAsComboBox(absl::optional<WideString> sValue) {
   CPDF_FormControl* pControl = widget_->GetFormControl();
   CPDF_FormField* pField = pControl->GetField();
-  std::ostringstream sBody;
+  fxcrt::ostringstream sBody;
 
   CFX_FloatRect rcClient = widget_->GetClientRect();
   CFX_FloatRect rcButton = rcClient;
@@ -1590,10 +1516,10 @@
   rcButton.Normalize();
 
   // Font map must outlive |pEdit|.
-  CBA_FontMap font_map(widget_->GetPDFPage()->GetDocument(),
-                       widget_->GetPDFAnnot()->GetAnnotDict());
+  CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
+                          widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N");
 
-  auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>();
+  auto pEdit = std::make_unique<CPWL_EditImpl>();
   pEdit->EnableRefresh(false);
   pEdit->SetFontMap(&font_map);
 
@@ -1602,25 +1528,26 @@
   rcEdit.Normalize();
 
   pEdit->SetPlateRect(rcEdit);
-  pEdit->SetAlignmentV(1, true);
+  pEdit->SetAlignmentV(1);
 
   float fFontSize = widget_->GetFontSize();
-  if (IsFloatZero(fFontSize))
-    pEdit->SetAutoFontSize(true, true);
+  if (FXSYS_IsFloatZero(fFontSize))
+    pEdit->SetAutoFontSize(true);
   else
     pEdit->SetFontSize(fFontSize);
 
   pEdit->Initialize();
-
   if (sValue.has_value()) {
     pEdit->SetText(sValue.value());
   } else {
     int32_t nCurSel = pField->GetSelectedIndex(0);
-    if (nCurSel < 0)
+    if (nCurSel < 0) {
       pEdit->SetText(pField->GetValue());
-    else
+    } else {
       pEdit->SetText(pField->GetOptionLabel(nCurSel));
+    }
   }
+  pEdit->Paint();
 
   CFX_FloatRect rcContent = pEdit->GetContentRect();
   ByteString sEdit = GetEditAppStream(pEdit.get(), CFX_PointF(), true, 0);
@@ -1632,15 +1559,14 @@
 
     if (rcContent.Width() > rcEdit.Width() ||
         rcContent.Height() > rcEdit.Height()) {
-      sBody << rcEdit.left << " " << rcEdit.bottom << " " << rcEdit.Width()
-            << " " << rcEdit.Height() << " " << kAppendRectOperator << "\n"
-            << kSetNonZeroWindingClipOperator << "\n"
+      WriteAppendRect(sBody, rcEdit);
+      sBody << kSetNonZeroWindingClipOperator << "\n"
             << kEndPathNoFillOrStrokeOperator << "\n";
     }
 
     CFX_Color crText = widget_->GetTextPWLColor();
     AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator);
-    sBody << GetColorAppStream(crText, true) << sEdit;
+    sBody << GetFillColorAppStream(crText) << sEdit;
   }
 
   sBody << GetDropButtonAppStream(rcButton);
@@ -1653,22 +1579,22 @@
   CPDF_FormControl* pControl = widget_->GetFormControl();
   CPDF_FormField* pField = pControl->GetField();
   CFX_FloatRect rcClient = widget_->GetClientRect();
-  std::ostringstream sBody;
+  fxcrt::ostringstream sBody;
 
   // Font map must outlive |pEdit|.
-  CBA_FontMap font_map(widget_->GetPDFPage()->GetDocument(),
-                       widget_->GetPDFAnnot()->GetAnnotDict());
+  CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
+                          widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N");
 
-  auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>();
+  auto pEdit = std::make_unique<CPWL_EditImpl>();
   pEdit->EnableRefresh(false);
   pEdit->SetFontMap(&font_map);
   pEdit->SetPlateRect(CFX_FloatRect(rcClient.left, 0.0f, rcClient.right, 0.0f));
 
   float fFontSize = widget_->GetFontSize();
-  pEdit->SetFontSize(IsFloatZero(fFontSize) ? 12.0f : fFontSize);
+  pEdit->SetFontSize(FXSYS_IsFloatZero(fFontSize) ? 12.0f : fFontSize);
   pEdit->Initialize();
 
-  std::ostringstream sList;
+  fxcrt::ostringstream sList;
   float fy = rcClient.top;
 
   int32_t nTop = pField->GetTopVisibleIndex();
@@ -1685,6 +1611,7 @@
     }
 
     pEdit->SetText(pField->GetOptionLabel(i));
+    pEdit->Paint();
 
     CFX_FloatRect rcContent = pEdit->GetContentRect();
     float fItemHeight = rcContent.Height();
@@ -1694,22 +1621,20 @@
           CFX_FloatRect(rcClient.left, fy - fItemHeight, rcClient.right, fy);
       {
         AutoClosedQCommand q(&sList);
-        sList << GetColorAppStream(CFX_Color(CFX_Color::kRGB, 0, 51.0f / 255.0f,
-                                             113.0f / 255.0f),
-                                   true)
-              << rcItem.left << " " << rcItem.bottom << " " << rcItem.Width()
-              << " " << rcItem.Height() << " " << kAppendRectOperator << " "
-              << kFillOperator << "\n";
+        sList << GetFillColorAppStream(CFX_Color(
+            CFX_Color::Type::kRGB, 0, 51.0f / 255.0f, 113.0f / 255.0f));
+        WriteAppendRect(sList, rcItem);
+        sList << kFillOperator << "\n";
       }
 
       AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator);
-      sList << GetColorAppStream(CFX_Color(CFX_Color::kGray, 1), true)
+      sList << GetFillColorAppStream(CFX_Color(CFX_Color::Type::kGray, 1))
             << GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, fy), true, 0);
     } else {
       CFX_Color crText = widget_->GetTextPWLColor();
 
       AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator);
-      sList << GetColorAppStream(crText, true)
+      sList << GetFillColorAppStream(crText)
             << GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, fy), true, 0);
     }
 
@@ -1722,9 +1647,8 @@
                           kMarkedSequenceEndOperator);
     AutoClosedQCommand q(&sBody);
 
-    sBody << rcClient.left << " " << rcClient.bottom << " " << rcClient.Width()
-          << " " << rcClient.Height() << " " << kAppendRectOperator << "\n"
-          << kSetNonZeroWindingClipOperator << "\n"
+    WriteAppendRect(sBody, rcClient);
+    sBody << kSetNonZeroWindingClipOperator << "\n"
           << kEndPathNoFillOrStrokeOperator << "\n"
           << sList.str();
   }
@@ -1733,37 +1657,37 @@
         ByteString());
 }
 
-void CPDFSDK_AppStream::SetAsTextField(Optional<WideString> sValue) {
+void CPDFSDK_AppStream::SetAsTextField(absl::optional<WideString> sValue) {
   CPDF_FormControl* pControl = widget_->GetFormControl();
   CPDF_FormField* pField = pControl->GetField();
-  std::ostringstream sBody;
-  std::ostringstream sLines;
+  fxcrt::ostringstream sBody;
+  fxcrt::ostringstream sLines;
 
   // Font map must outlive |pEdit|.
-  CBA_FontMap font_map(widget_->GetPDFPage()->GetDocument(),
-                       widget_->GetPDFAnnot()->GetAnnotDict());
+  CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
+                          widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N");
 
-  auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>();
+  auto pEdit = std::make_unique<CPWL_EditImpl>();
   pEdit->EnableRefresh(false);
   pEdit->SetFontMap(&font_map);
 
   CFX_FloatRect rcClient = widget_->GetClientRect();
   pEdit->SetPlateRect(rcClient);
-  pEdit->SetAlignmentH(pControl->GetControlAlignment(), true);
+  pEdit->SetAlignmentH(pControl->GetControlAlignment());
 
   uint32_t dwFieldFlags = pField->GetFieldFlags();
   bool bMultiLine = dwFieldFlags & pdfium::form_flags::kTextMultiline;
   if (bMultiLine) {
-    pEdit->SetMultiLine(true, true);
-    pEdit->SetAutoReturn(true, true);
+    pEdit->SetMultiLine(true);
+    pEdit->SetAutoReturn(true);
   } else {
-    pEdit->SetAlignmentV(1, true);
+    pEdit->SetAlignmentV(1);
   }
 
   uint16_t subWord = 0;
   if (dwFieldFlags & pdfium::form_flags::kTextPassword) {
     subWord = '*';
-    pEdit->SetPasswordChar(subWord, true);
+    pEdit->SetPasswordChar(subWord);
   }
 
   int nMaxLen = pField->GetMaxLen();
@@ -1778,24 +1702,25 @@
   if (nMaxLen > 0) {
     if (bCharArray) {
       pEdit->SetCharArray(nMaxLen);
-      if (IsFloatZero(fFontSize)) {
+      if (FXSYS_IsFloatZero(fFontSize)) {
         fFontSize = CPWL_Edit::GetCharArrayAutoFontSize(
             font_map.GetPDFFont(0).Get(), rcClient, nMaxLen);
       }
     } else {
       if (sValue.has_value())
-        nMaxLen = sValue.value().GetLength();
+        nMaxLen = pdfium::base::checked_cast<int>(sValue.value().GetLength());
       pEdit->SetLimitChar(nMaxLen);
     }
   }
 
-  if (IsFloatZero(fFontSize))
-    pEdit->SetAutoFontSize(true, true);
+  if (FXSYS_IsFloatZero(fFontSize))
+    pEdit->SetAutoFontSize(true);
   else
     pEdit->SetFontSize(fFontSize);
 
   pEdit->Initialize();
   pEdit->SetText(sValue.value_or(pField->GetValue()));
+  pEdit->Paint();
 
   CFX_FloatRect rcContent = pEdit->GetContentRect();
   ByteString sEdit =
@@ -1809,63 +1734,57 @@
 
     if (rcContent.Width() > rcClient.Width() ||
         rcContent.Height() > rcClient.Height()) {
-      sBody << rcClient.left << " " << rcClient.bottom << " "
-            << rcClient.Width() << " " << rcClient.Height() << " "
-            << kAppendRectOperator << "\n"
-            << kSetNonZeroWindingClipOperator << "\n"
+      WriteAppendRect(sBody, rcClient);
+      sBody << kSetNonZeroWindingClipOperator << "\n"
             << kEndPathNoFillOrStrokeOperator << "\n";
     }
     CFX_Color crText = widget_->GetTextPWLColor();
 
     AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator);
-    sBody << GetColorAppStream(crText, true) << sEdit;
+    sBody << GetFillColorAppStream(crText) << sEdit;
   }
 
   if (bCharArray) {
     switch (widget_->GetBorderStyle()) {
-      case BorderStyle::SOLID: {
+      case BorderStyle::kSolid: {
         ByteString sColor =
-            GetColorAppStream(widget_->GetBorderPWLColor(), false);
+            GetStrokeColorAppStream(widget_->GetBorderPWLColor());
         if (sColor.GetLength() > 0) {
           AutoClosedQCommand q(&sLines);
           sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator
                  << "\n"
-                 << GetColorAppStream(widget_->GetBorderPWLColor(), false)
+                 << GetStrokeColorAppStream(widget_->GetBorderPWLColor())
                  << " 2 " << kSetLineCapStyleOperator << " 0 "
                  << kSetLineJoinStyleOperator << "\n";
 
+          const float width = rcClient.right - rcClient.left;
           for (int32_t i = 1; i < nMaxLen; ++i) {
-            sLines << rcClient.left +
-                          ((rcClient.right - rcClient.left) / nMaxLen) * i
-                   << " " << rcClient.bottom << " " << kMoveToOperator << "\n"
-                   << rcClient.left +
-                          ((rcClient.right - rcClient.left) / nMaxLen) * i
-                   << " " << rcClient.top << " " << kLineToOperator << " "
-                   << kStrokeOperator << "\n";
+            const float left = rcClient.left + (width / nMaxLen) * i;
+            WriteMove(sLines, {left, rcClient.bottom});
+            WriteLine(sLines, {left, rcClient.top});
+            sLines << kStrokeOperator << "\n";
           }
         }
         break;
       }
-      case BorderStyle::DASH: {
+      case BorderStyle::kDash: {
         ByteString sColor =
-            GetColorAppStream(widget_->GetBorderPWLColor(), false);
+            GetStrokeColorAppStream(widget_->GetBorderPWLColor());
         if (sColor.GetLength() > 0) {
           CPWL_Dash dsBorder = CPWL_Dash(3, 3, 0);
           AutoClosedQCommand q(&sLines);
           sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator
                  << "\n"
-                 << GetColorAppStream(widget_->GetBorderPWLColor(), false)
-                 << "[" << dsBorder.nDash << " " << dsBorder.nGap << "] "
+                 << GetStrokeColorAppStream(widget_->GetBorderPWLColor()) << "["
+                 << dsBorder.nDash << " " << dsBorder.nGap << "] "
                  << dsBorder.nPhase << " " << kSetDashOperator << "\n";
 
+          const float width = rcClient.right - rcClient.left;
           for (int32_t i = 1; i < nMaxLen; ++i) {
-            sLines << rcClient.left +
-                          ((rcClient.right - rcClient.left) / nMaxLen) * i
-                   << " " << rcClient.bottom << " " << kMoveToOperator << "\n"
-                   << rcClient.left +
-                          ((rcClient.right - rcClient.left) / nMaxLen) * i
-                   << " " << rcClient.top << " " << kLineToOperator << " "
-                   << kStrokeOperator << "\n";
+            const float left = rcClient.left + (width / nMaxLen) * i;
+            WriteMove(sLines, {left, rcClient.bottom});
+            WriteLine(sLines, {left, rcClient.top});
+            sLines << kStrokeOperator << "\n";
           }
         }
         break;
@@ -1883,22 +1802,17 @@
 
 void CPDFSDK_AppStream::AddImage(const ByteString& sAPType,
                                  CPDF_Stream* pImage) {
-  CPDF_Stream* pStream = dict_->GetStreamFor(sAPType);
-  CPDF_Dictionary* pStreamDict = pStream->GetDict();
+  RetainPtr<CPDF_Stream> pStream = dict_->GetMutableStreamFor(sAPType);
+  RetainPtr<CPDF_Dictionary> pStreamDict = pStream->GetMutableDict();
   ByteString sImageAlias = "IMG";
 
-  if (CPDF_Dictionary* pImageDict = pImage->GetDict()) {
-    sImageAlias = pImageDict->GetStringFor("Name");
-    if (sImageAlias.IsEmpty())
-      sImageAlias = "IMG";
-  }
+  RetainPtr<const CPDF_Dictionary> pImageDict = pImage->GetDict();
+  if (pImageDict)
+    sImageAlias = pImageDict->GetByteStringFor("Name");
 
-  CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources");
-  if (!pStreamResList)
-    pStreamResList = pStreamDict->SetNewFor<CPDF_Dictionary>("Resources");
-
-  CPDF_Dictionary* pXObject =
-      pStreamResList->SetNewFor<CPDF_Dictionary>("XObject");
+  RetainPtr<CPDF_Dictionary> pStreamResList =
+      pStreamDict->GetOrCreateDictFor("Resources");
+  auto pXObject = pStreamResList->SetNewFor<CPDF_Dictionary>("XObject");
   pXObject->SetNewFor<CPDF_Reference>(sImageAlias,
                                       widget_->GetPageView()->GetPDFDocument(),
                                       pImage->GetObjNum());
@@ -1907,48 +1821,57 @@
 void CPDFSDK_AppStream::Write(const ByteString& sAPType,
                               const ByteString& sContents,
                               const ByteString& sAPState) {
-  CPDF_Stream* pStream = nullptr;
-  CPDF_Dictionary* pParentDict = nullptr;
+  RetainPtr<CPDF_Dictionary> pParentDict;
+  ByteString key;
   if (sAPState.IsEmpty()) {
-    pParentDict = dict_.Get();
-    pStream = dict_->GetStreamFor(sAPType);
+    pParentDict = dict_;
+    key = sAPType;
   } else {
-    CPDF_Dictionary* pAPTypeDict = dict_->GetDictFor(sAPType);
-    if (!pAPTypeDict)
-      pAPTypeDict = dict_->SetNewFor<CPDF_Dictionary>(sAPType);
-
-    pParentDict = pAPTypeDict;
-    pStream = pAPTypeDict->GetStreamFor(sAPState);
+    pParentDict = dict_->GetOrCreateDictFor(sAPType);
+    key = sAPState;
   }
 
-  if (!pStream) {
-    CPDF_Document* doc = widget_->GetPageView()->GetPDFDocument();
-    pStream = doc->NewIndirect<CPDF_Stream>();
-    pParentDict->SetNewFor<CPDF_Reference>(sAPType, doc, pStream->GetObjNum());
+  RetainPtr<CPDF_Dictionary> pOrigStreamDict;
+
+  // If `pStream` is created by CreateModifiedAPStream(), then it is safe to
+  // edit, as it is not shared.
+  RetainPtr<CPDF_Stream> pStream = pParentDict->GetMutableStreamFor(key);
+  CPDF_Document* doc = widget_->GetPageView()->GetPDFDocument();
+  if (!doc->IsModifiedAPStream(pStream.Get())) {
+    if (pStream)
+      pOrigStreamDict = pStream->GetMutableDict();
+    pStream.Reset(doc->CreateModifiedAPStream());
+    pParentDict->SetNewFor<CPDF_Reference>(key, doc, pStream->GetObjNum());
   }
 
-  CPDF_Dictionary* pStreamDict = pStream->GetDict();
+  RetainPtr<CPDF_Dictionary> pStreamDict = pStream->GetMutableDict();
   if (!pStreamDict) {
-    auto pNewDict =
-        widget_->GetPDFAnnot()->GetDocument()->New<CPDF_Dictionary>();
-    pStreamDict = pNewDict.Get();
+    pStreamDict = doc->New<CPDF_Dictionary>();
     pStreamDict->SetNewFor<CPDF_Name>("Type", "XObject");
     pStreamDict->SetNewFor<CPDF_Name>("Subtype", "Form");
     pStreamDict->SetNewFor<CPDF_Number>("FormType", 1);
-    pStream->InitStream({}, std::move(pNewDict));
+
+    if (pOrigStreamDict) {
+      RetainPtr<const CPDF_Dictionary> pResources =
+          pOrigStreamDict->GetDictFor("Resources");
+      if (pResources)
+        pStreamDict->SetFor("Resources", pResources->Clone());
+    }
+
+    pStream->InitStreamWithEmptyData(pStreamDict);
   }
   pStreamDict->SetMatrixFor("Matrix", widget_->GetMatrix());
   pStreamDict->SetRectFor("BBox", widget_->GetRotatedRect());
   pStream->SetDataAndRemoveFilter(sContents.raw_span());
 }
 
-void CPDFSDK_AppStream::Remove(const ByteString& sAPType) {
+void CPDFSDK_AppStream::Remove(ByteStringView sAPType) {
   dict_->RemoveFor(sAPType);
 }
 
 ByteString CPDFSDK_AppStream::GetBackgroundAppStream() const {
   CFX_Color crBackground = widget_->GetFillPWLColor();
-  if (crBackground.nColorType != CFX_Color::kTransparent)
+  if (crBackground.nColorType != CFX_Color::Type::kTransparent)
     return GetRectFillAppStream(widget_->GetRotatedRect(), crBackground);
 
   return ByteString();
@@ -1966,18 +1889,18 @@
 
   BorderStyle nBorderStyle = widget_->GetBorderStyle();
   switch (nBorderStyle) {
-    case BorderStyle::DASH:
+    case BorderStyle::kDash:
       dsBorder = CPWL_Dash(3, 3, 0);
       break;
-    case BorderStyle::BEVELED:
+    case BorderStyle::kBeveled:
       fBorderWidth *= 2;
-      crLeftTop = CFX_Color(CFX_Color::kGray, 1);
+      crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
       crRightBottom = crBackground / 2.0f;
       break;
-    case BorderStyle::INSET:
+    case BorderStyle::kInset:
       fBorderWidth *= 2;
-      crLeftTop = CFX_Color(CFX_Color::kGray, 0.5);
-      crRightBottom = CFX_Color(CFX_Color::kGray, 0.75);
+      crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5);
+      crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75);
       break;
     default:
       break;
diff --git a/fpdfsdk/cpdfsdk_appstream.h b/fpdfsdk/cpdfsdk_appstream.h
index 8a38a86..aabef24 100644
--- a/fpdfsdk/cpdfsdk_appstream.h
+++ b/fpdfsdk/cpdfsdk_appstream.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,8 +8,9 @@
 #define FPDFSDK_CPDFSDK_APPSTREAM_H_
 
 #include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CPDFSDK_Widget;
 class CPDF_Dictionary;
@@ -23,16 +24,16 @@
   void SetAsPushButton();
   void SetAsCheckBox();
   void SetAsRadioButton();
-  void SetAsComboBox(Optional<WideString> sValue);
+  void SetAsComboBox(absl::optional<WideString> sValue);
   void SetAsListBox();
-  void SetAsTextField(Optional<WideString> sValue);
+  void SetAsTextField(absl::optional<WideString> sValue);
 
  private:
   void AddImage(const ByteString& sAPType, CPDF_Stream* pImage);
   void Write(const ByteString& sAPType,
              const ByteString& sContents,
              const ByteString& sAPState);
-  void Remove(const ByteString& sAPType);
+  void Remove(ByteStringView sAPType);
 
   ByteString GetBackgroundAppStream() const;
   ByteString GetBorderAppStream() const;
diff --git a/fpdfsdk/cpdfsdk_baannot.cpp b/fpdfsdk/cpdfsdk_baannot.cpp
index e214163..c5d33d6 100644
--- a/fpdfsdk/cpdfsdk_baannot.cpp
+++ b/fpdfsdk/cpdfsdk_baannot.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,56 +6,65 @@
 
 #include "fpdfsdk/cpdfsdk_baannot.h"
 
-#include <algorithm>
-#include <utility>
+#include <vector>
 
 #include "constants/annotation_common.h"
 #include "constants/annotation_flags.h"
+#include "constants/form_fields.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fxge/cfx_drawutils.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
 
 CPDFSDK_BAAnnot::CPDFSDK_BAAnnot(CPDF_Annot* pAnnot,
                                  CPDFSDK_PageView* pPageView)
     : CPDFSDK_Annot(pPageView), m_pAnnot(pAnnot) {}
 
-CPDFSDK_BAAnnot::~CPDFSDK_BAAnnot() {}
+CPDFSDK_BAAnnot::~CPDFSDK_BAAnnot() = default;
 
 CPDFSDK_BAAnnot* CPDFSDK_BAAnnot::AsBAAnnot() {
   return this;
 }
 
+CPDFSDK_Annot::UnsafeInputHandlers* CPDFSDK_BAAnnot::GetUnsafeInputHandlers() {
+  return this;
+}
+
 CPDF_Annot* CPDFSDK_BAAnnot::GetPDFAnnot() const {
-  return m_pAnnot.Get();
+  return m_pAnnot;
 }
 
-CPDF_Annot* CPDFSDK_BAAnnot::GetPDFPopupAnnot() const {
-  return m_pAnnot->GetPopupAnnot();
-}
-
-CPDF_Dictionary* CPDFSDK_BAAnnot::GetAnnotDict() const {
+const CPDF_Dictionary* CPDFSDK_BAAnnot::GetAnnotDict() const {
   return m_pAnnot->GetAnnotDict();
 }
 
-CPDF_Dictionary* CPDFSDK_BAAnnot::GetAPDict() const {
-  CPDF_Dictionary* pAPDict =
-      GetAnnotDict()->GetDictFor(pdfium::annotation::kAP);
-  if (pAPDict)
-    return pAPDict;
-  return GetAnnotDict()->SetNewFor<CPDF_Dictionary>(pdfium::annotation::kAP);
+RetainPtr<CPDF_Dictionary> CPDFSDK_BAAnnot::GetMutableAnnotDict() {
+  return m_pAnnot->GetMutableAnnotDict();
 }
 
-void CPDFSDK_BAAnnot::SetRect(const CFX_FloatRect& rect) {
-  ASSERT(rect.right - rect.left >= 1.0f);
-  ASSERT(rect.top - rect.bottom >= 1.0f);
-  GetAnnotDict()->SetRectFor(pdfium::annotation::kRect, rect);
+RetainPtr<CPDF_Dictionary> CPDFSDK_BAAnnot::GetAPDict() {
+  return GetMutableAnnotDict()->GetOrCreateDictFor(pdfium::annotation::kAP);
+}
+
+void CPDFSDK_BAAnnot::ClearCachedAnnotAP() {
+  m_pAnnot->ClearCachedAP();
+}
+
+bool CPDFSDK_BAAnnot::IsFocusableAnnot(
+    const CPDF_Annot::Subtype& annot_type) const {
+  return pdfium::Contains(
+      GetPageView()->GetFormFillEnv()->GetFocusableAnnotSubtypes(), annot_type);
 }
 
 CFX_FloatRect CPDFSDK_BAAnnot::GetRect() const {
@@ -68,10 +77,9 @@
 
 void CPDFSDK_BAAnnot::DrawAppearance(CFX_RenderDevice* pDevice,
                                      const CFX_Matrix& mtUser2Device,
-                                     CPDF_Annot::AppearanceMode mode,
-                                     const CPDF_RenderOptions* pOptions) {
-  m_pAnnot->DrawAppearance(m_pPageView->GetPDFPage(), pDevice, mtUser2Device,
-                           mode, pOptions);
+                                     CPDF_Annot::AppearanceMode mode) {
+  m_pAnnot->DrawAppearance(GetPageView()->GetPDFPage(), pDevice, mtUser2Device,
+                           mode);
 }
 
 bool CPDFSDK_BAAnnot::IsAppearanceValid() {
@@ -79,11 +87,12 @@
 }
 
 void CPDFSDK_BAAnnot::SetAnnotName(const WideString& sName) {
-  CPDF_Dictionary* pDict = GetAnnotDict();
-  if (sName.IsEmpty())
+  RetainPtr<CPDF_Dictionary> pDict = GetMutableAnnotDict();
+  if (sName.IsEmpty()) {
     pDict->RemoveFor(pdfium::annotation::kNM);
-  else
-    pDict->SetNewFor<CPDF_String>(pdfium::annotation::kNM, sName);
+    return;
+  }
+  pDict->SetNewFor<CPDF_String>(pdfium::annotation::kNM, sName.AsStringView());
 }
 
 WideString CPDFSDK_BAAnnot::GetAnnotName() const {
@@ -91,106 +100,98 @@
 }
 
 void CPDFSDK_BAAnnot::SetFlags(uint32_t nFlags) {
-  GetAnnotDict()->SetNewFor<CPDF_Number>(pdfium::annotation::kF,
-                                         static_cast<int>(nFlags));
+  GetMutableAnnotDict()->SetNewFor<CPDF_Number>(pdfium::annotation::kF,
+                                                static_cast<int>(nFlags));
 }
 
 uint32_t CPDFSDK_BAAnnot::GetFlags() const {
   return GetAnnotDict()->GetIntegerFor(pdfium::annotation::kF);
 }
 
-void CPDFSDK_BAAnnot::SetAppState(const ByteString& str) {
-  CPDF_Dictionary* pDict = GetAnnotDict();
-  if (str.IsEmpty())
-    pDict->RemoveFor(pdfium::annotation::kAS);
-  else
-    pDict->SetNewFor<CPDF_String>(pdfium::annotation::kAS, str, false);
+void CPDFSDK_BAAnnot::SetAppStateOff() {
+  RetainPtr<CPDF_Dictionary> pDict = GetMutableAnnotDict();
+  pDict->SetNewFor<CPDF_String>(pdfium::annotation::kAS, "Off", false);
 }
 
 ByteString CPDFSDK_BAAnnot::GetAppState() const {
-  return GetAnnotDict()->GetStringFor(pdfium::annotation::kAS);
+  return GetAnnotDict()->GetByteStringFor(pdfium::annotation::kAS);
 }
 
 void CPDFSDK_BAAnnot::SetBorderWidth(int nWidth) {
-  CPDF_Array* pBorder =
-      GetAnnotDict()->GetArrayFor(pdfium::annotation::kBorder);
+  RetainPtr<CPDF_Dictionary> pAnnotDict = GetMutableAnnotDict();
+  RetainPtr<CPDF_Array> pBorder =
+      pAnnotDict->GetMutableArrayFor(pdfium::annotation::kBorder);
   if (pBorder) {
     pBorder->SetNewAt<CPDF_Number>(2, nWidth);
-  } else {
-    CPDF_Dictionary* pBSDict = GetAnnotDict()->GetDictFor("BS");
-    if (!pBSDict)
-      pBSDict = GetAnnotDict()->SetNewFor<CPDF_Dictionary>("BS");
-    pBSDict->SetNewFor<CPDF_Number>("W", nWidth);
+    return;
   }
+  pAnnotDict->GetOrCreateDictFor("BS")->SetNewFor<CPDF_Number>("W", nWidth);
 }
 
 int CPDFSDK_BAAnnot::GetBorderWidth() const {
-  if (const CPDF_Array* pBorder =
-          GetAnnotDict()->GetArrayFor(pdfium::annotation::kBorder)) {
+  RetainPtr<const CPDF_Array> pBorder =
+      GetAnnotDict()->GetArrayFor(pdfium::annotation::kBorder);
+  if (pBorder)
     return pBorder->GetIntegerAt(2);
-  }
 
-  if (CPDF_Dictionary* pBSDict = GetAnnotDict()->GetDictFor("BS"))
+  RetainPtr<const CPDF_Dictionary> pBSDict = GetAnnotDict()->GetDictFor("BS");
+  if (pBSDict)
     return pBSDict->GetIntegerFor("W", 1);
 
   return 1;
 }
 
 void CPDFSDK_BAAnnot::SetBorderStyle(BorderStyle nStyle) {
-  CPDF_Dictionary* pBSDict = GetAnnotDict()->GetDictFor("BS");
-  if (!pBSDict)
-    pBSDict = GetAnnotDict()->SetNewFor<CPDF_Dictionary>("BS");
-
+  RetainPtr<CPDF_Dictionary> pBSDict =
+      GetMutableAnnotDict()->GetOrCreateDictFor("BS");
   const char* name = nullptr;
   switch (nStyle) {
-    case BorderStyle::SOLID:
+    case BorderStyle::kSolid:
       name = "S";
       break;
-    case BorderStyle::DASH:
+    case BorderStyle::kDash:
       name = "D";
       break;
-    case BorderStyle::BEVELED:
+    case BorderStyle::kBeveled:
       name = "B";
       break;
-    case BorderStyle::INSET:
+    case BorderStyle::kInset:
       name = "I";
       break;
-    case BorderStyle::UNDERLINE:
+    case BorderStyle::kUnderline:
       name = "U";
       break;
-    default:
-      return;
   }
   pBSDict->SetNewFor<CPDF_Name>("S", name);
 }
 
 BorderStyle CPDFSDK_BAAnnot::GetBorderStyle() const {
-  CPDF_Dictionary* pBSDict = GetAnnotDict()->GetDictFor("BS");
+  RetainPtr<const CPDF_Dictionary> pBSDict = GetAnnotDict()->GetDictFor("BS");
   if (pBSDict) {
-    ByteString sBorderStyle = pBSDict->GetStringFor("S", "S");
+    ByteString sBorderStyle = pBSDict->GetByteStringFor("S", "S");
     if (sBorderStyle == "S")
-      return BorderStyle::SOLID;
+      return BorderStyle::kSolid;
     if (sBorderStyle == "D")
-      return BorderStyle::DASH;
+      return BorderStyle::kDash;
     if (sBorderStyle == "B")
-      return BorderStyle::BEVELED;
+      return BorderStyle::kBeveled;
     if (sBorderStyle == "I")
-      return BorderStyle::INSET;
+      return BorderStyle::kInset;
     if (sBorderStyle == "U")
-      return BorderStyle::UNDERLINE;
+      return BorderStyle::kUnderline;
   }
 
-  const CPDF_Array* pBorder =
+  RetainPtr<const CPDF_Array> pBorder =
       GetAnnotDict()->GetArrayFor(pdfium::annotation::kBorder);
   if (pBorder) {
     if (pBorder->size() >= 4) {
-      const CPDF_Array* pDP = pBorder->GetArrayAt(3);
+      RetainPtr<const CPDF_Array> pDP = pBorder->GetArrayAt(3);
       if (pDP && pDP->size() > 0)
-        return BorderStyle::DASH;
+        return BorderStyle::kDash;
     }
   }
 
-  return BorderStyle::SOLID;
+  return BorderStyle::kSolid;
 }
 
 bool CPDFSDK_BAAnnot::IsVisible() const {
@@ -205,7 +206,7 @@
 }
 
 CPDF_AAction CPDFSDK_BAAnnot::GetAAction() const {
-  return CPDF_AAction(GetAnnotDict()->GetDictFor("AA"));
+  return CPDF_AAction(GetAnnotDict()->GetDictFor(pdfium::form_fields::kAA));
 }
 
 CPDF_Action CPDFSDK_BAAnnot::GetAAction(CPDF_AAction::AActionType eAAT) {
@@ -213,15 +214,40 @@
   if (AAction.ActionExist(eAAT))
     return AAction.GetAction(eAAT);
 
-  if (eAAT == CPDF_AAction::kButtonUp)
+  if (eAAT == CPDF_AAction::kButtonUp || eAAT == CPDF_AAction::kKeyStroke)
     return GetAction();
 
   return CPDF_Action(nullptr);
 }
 
 void CPDFSDK_BAAnnot::SetOpenState(bool bOpenState) {
-  if (CPDF_Annot* pAnnot = m_pAnnot->GetPopupAnnot())
-    pAnnot->SetOpenState(bOpenState);
+  m_pAnnot->SetPopupAnnotOpenState(bOpenState);
+}
+
+void CPDFSDK_BAAnnot::UpdateAnnotRects() {
+  std::vector<CFX_FloatRect> rects;
+  rects.push_back(GetRect());
+
+  absl::optional<CFX_FloatRect> annot_rect = m_pAnnot->GetPopupAnnotRect();
+  if (annot_rect.has_value())
+    rects.push_back(annot_rect.value());
+
+  // Make the rects round up to avoid https://crbug.com/662804
+  for (CFX_FloatRect& rect : rects)
+    rect.Inflate(1, 1);
+
+  GetPageView()->UpdateRects(rects);
+}
+
+void CPDFSDK_BAAnnot::InvalidateRect() {
+  CFX_FloatRect view_bounding_box = GetViewBBox();
+  if (view_bounding_box.IsEmpty())
+    return;
+
+  view_bounding_box.Inflate(1, 1);
+  view_bounding_box.Normalize();
+  FX_RECT rect = view_bounding_box.GetOuterRect();
+  GetPageView()->GetFormFillEnv()->Invalidate(GetPage(), rect);
 }
 
 int CPDFSDK_BAAnnot::GetLayoutOrder() const {
@@ -230,3 +256,173 @@
 
   return CPDFSDK_Annot::GetLayoutOrder();
 }
+
+void CPDFSDK_BAAnnot::OnDraw(CFX_RenderDevice* pDevice,
+                             const CFX_Matrix& mtUser2Device,
+                             bool bDrawAnnots) {
+  if (!IsVisible())
+    return;
+
+  const CPDF_Annot::Subtype annot_type = GetAnnotSubtype();
+  if (bDrawAnnots && annot_type == CPDF_Annot::Subtype::POPUP) {
+    DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::AppearanceMode::kNormal);
+    return;
+  }
+
+  if (!is_focused_ || !IsFocusableAnnot(annot_type) ||
+      this != GetPageView()->GetFormFillEnv()->GetFocusAnnot()) {
+    return;
+  }
+
+  CFX_FloatRect view_bounding_box = GetViewBBox();
+  if (view_bounding_box.IsEmpty())
+    return;
+
+  view_bounding_box.Normalize();
+  CFX_DrawUtils::DrawFocusRect(pDevice, mtUser2Device, view_bounding_box);
+}
+
+bool CPDFSDK_BAAnnot::DoHitTest(const CFX_PointF& point) {
+  return false;
+}
+
+CFX_FloatRect CPDFSDK_BAAnnot::GetViewBBox() {
+  return GetRect();
+}
+
+void CPDFSDK_BAAnnot::OnMouseEnter(Mask<FWL_EVENTFLAG> nFlags) {
+  SetOpenState(true);
+  UpdateAnnotRects();
+}
+
+void CPDFSDK_BAAnnot::OnMouseExit(Mask<FWL_EVENTFLAG> nFlags) {
+  SetOpenState(false);
+  UpdateAnnotRects();
+}
+
+bool CPDFSDK_BAAnnot::OnLButtonDown(Mask<FWL_EVENTFLAG> nFlags,
+                                    const CFX_PointF& point) {
+  return false;
+}
+
+bool CPDFSDK_BAAnnot::OnLButtonUp(Mask<FWL_EVENTFLAG> nFlags,
+                                  const CFX_PointF& point) {
+  return false;
+}
+
+bool CPDFSDK_BAAnnot::OnLButtonDblClk(Mask<FWL_EVENTFLAG> nFlags,
+                                      const CFX_PointF& point) {
+  return false;
+}
+
+bool CPDFSDK_BAAnnot::OnMouseMove(Mask<FWL_EVENTFLAG> nFlags,
+                                  const CFX_PointF& point) {
+  return false;
+}
+
+bool CPDFSDK_BAAnnot::OnMouseWheel(Mask<FWL_EVENTFLAG> nFlags,
+                                   const CFX_PointF& point,
+                                   const CFX_Vector& delta) {
+  return false;
+}
+
+bool CPDFSDK_BAAnnot::OnRButtonDown(Mask<FWL_EVENTFLAG> nFlags,
+                                    const CFX_PointF& point) {
+  return false;
+}
+
+bool CPDFSDK_BAAnnot::OnRButtonUp(Mask<FWL_EVENTFLAG> nFlags,
+                                  const CFX_PointF& point) {
+  return false;
+}
+
+bool CPDFSDK_BAAnnot::OnChar(uint32_t nChar, Mask<FWL_EVENTFLAG> nFlags) {
+  return false;
+}
+
+bool CPDFSDK_BAAnnot::OnKeyDown(FWL_VKEYCODE nKeyCode,
+                                Mask<FWL_EVENTFLAG> nFlags) {
+  // OnKeyDown() is implemented only for link annotations for now. As
+  // OnKeyDown() is implemented for other subtypes, following check should be
+  // modified.
+  if (nKeyCode != FWL_VKEY_Return ||
+      GetAnnotSubtype() != CPDF_Annot::Subtype::LINK) {
+    return false;
+  }
+
+  CPDF_Action action = GetAAction(CPDF_AAction::kKeyStroke);
+  CPDFSDK_FormFillEnvironment* env = GetPageView()->GetFormFillEnv();
+  if (action.HasDict()) {
+    return env->DoActionLink(action, CPDF_AAction::kKeyStroke, nFlags);
+  }
+
+  return env->DoActionDestination(GetDestination());
+}
+
+bool CPDFSDK_BAAnnot::OnSetFocus(Mask<FWL_EVENTFLAG> nFlags) {
+  if (!IsFocusableAnnot(GetAnnotSubtype()))
+    return false;
+
+  is_focused_ = true;
+  InvalidateRect();
+  return true;
+}
+
+bool CPDFSDK_BAAnnot::OnKillFocus(Mask<FWL_EVENTFLAG> nFlags) {
+  if (!IsFocusableAnnot(GetAnnotSubtype()))
+    return false;
+
+  is_focused_ = false;
+  InvalidateRect();
+  return true;
+}
+
+bool CPDFSDK_BAAnnot::CanUndo() {
+  return false;
+}
+
+bool CPDFSDK_BAAnnot::CanRedo() {
+  return false;
+}
+
+bool CPDFSDK_BAAnnot::Undo() {
+  return false;
+}
+
+bool CPDFSDK_BAAnnot::Redo() {
+  return false;
+}
+
+WideString CPDFSDK_BAAnnot::GetText() {
+  return WideString();
+}
+
+WideString CPDFSDK_BAAnnot::GetSelectedText() {
+  return WideString();
+}
+
+void CPDFSDK_BAAnnot::ReplaceAndKeepSelection(const WideString& text) {}
+
+void CPDFSDK_BAAnnot::ReplaceSelection(const WideString& text) {}
+
+bool CPDFSDK_BAAnnot::SelectAllText() {
+  return false;
+}
+
+bool CPDFSDK_BAAnnot::SetIndexSelected(int index, bool selected) {
+  return false;
+}
+
+bool CPDFSDK_BAAnnot::IsIndexSelected(int index) {
+  return false;
+}
+
+CPDF_Dest CPDFSDK_BAAnnot::GetDestination() const {
+  if (m_pAnnot->GetSubtype() != CPDF_Annot::Subtype::LINK)
+    return CPDF_Dest(nullptr);
+
+  // Link annotations can have "Dest" entry defined as an explicit array.
+  // See ISO 32000-1:2008 spec, section 12.3.2.1.
+  return CPDF_Dest::Create(GetPageView()->GetPDFDocument(),
+                           GetAnnotDict()->GetDirectObjectFor("Dest"));
+}
diff --git a/fpdfsdk/cpdfsdk_baannot.h b/fpdfsdk/cpdfsdk_baannot.h
index 482aa0a..0da13d8 100644
--- a/fpdfsdk/cpdfsdk_baannot.h
+++ b/fpdfsdk/cpdfsdk_baannot.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,36 +12,49 @@
 #include "core/fpdfdoc/cpdf_annot.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
 
 class CFX_Matrix;
 class CPDF_Dictionary;
-class CPDF_RenderOptions;
 class CPDFSDK_PageView;
 
-class CPDFSDK_BAAnnot : public CPDFSDK_Annot {
+class CPDFSDK_BAAnnot : public CPDFSDK_Annot,
+                        CPDFSDK_Annot::UnsafeInputHandlers {
  public:
   CPDFSDK_BAAnnot(CPDF_Annot* pAnnot, CPDFSDK_PageView* pPageView);
   ~CPDFSDK_BAAnnot() override;
 
-  // CPDFSDK_Annot
+  // CPDFSDK_Annot:
   CPDFSDK_BAAnnot* AsBAAnnot() override;
+  CPDFSDK_Annot::UnsafeInputHandlers* GetUnsafeInputHandlers() override;
   CPDF_Annot::Subtype GetAnnotSubtype() const override;
-  void SetRect(const CFX_FloatRect& rect) override;
   CFX_FloatRect GetRect() const override;
   CPDF_Annot* GetPDFAnnot() const override;
   int GetLayoutOrder() const override;
+  void OnDraw(CFX_RenderDevice* pDevice,
+              const CFX_Matrix& mtUser2Device,
+              bool bDrawAnnots) override;
+  bool DoHitTest(const CFX_PointF& point) override;
+  CFX_FloatRect GetViewBBox() override;
+  bool CanUndo() override;
+  bool CanRedo() override;
+  bool Undo() override;
+  bool Redo() override;
+  WideString GetText() override;
+  WideString GetSelectedText() override;
+  void ReplaceAndKeepSelection(const WideString& text) override;
+  void ReplaceSelection(const WideString& text) override;
+  bool SelectAllText() override;
+  bool SetIndexSelected(int index, bool selected) override;
+  bool IsIndexSelected(int index) override;
 
   virtual CPDF_Action GetAAction(CPDF_AAction::AActionType eAAT);
   virtual bool IsAppearanceValid();
   virtual void DrawAppearance(CFX_RenderDevice* pDevice,
                               const CFX_Matrix& mtUser2Device,
-                              CPDF_Annot::AppearanceMode mode,
-                              const CPDF_RenderOptions* pOptions);
-
-  CPDF_Dictionary* GetAnnotDict() const;
-  CPDF_Annot* GetPDFPopupAnnot() const;
+                              CPDF_Annot::AppearanceMode mode);
 
   void SetAnnotName(const WideString& sName);
   WideString GetAnnotName() const;
@@ -49,7 +62,7 @@
   void SetFlags(uint32_t nFlags);
   uint32_t GetFlags() const;
 
-  void SetAppState(const ByteString& str);
+  void SetAppStateOff();
   ByteString GetAppState() const;
 
   void SetBorderWidth(int nWidth);
@@ -61,14 +74,45 @@
   bool IsVisible() const;
 
   CPDF_Action GetAction() const;
-
   CPDF_AAction GetAAction() const;
-
-  void SetOpenState(bool bOpenState);
+  CPDF_Dest GetDestination() const;
 
  protected:
-  CPDF_Dictionary* GetAPDict() const;
+  const CPDF_Dictionary* GetAnnotDict() const;
+  RetainPtr<CPDF_Dictionary> GetMutableAnnotDict();
+  RetainPtr<CPDF_Dictionary> GetAPDict();
+  void ClearCachedAnnotAP();
+  bool IsFocusableAnnot(const CPDF_Annot::Subtype& annot_type) const;
 
+ private:
+  // CPDFSDK_Annot::UnsafeInputHandlers:
+  void OnMouseEnter(Mask<FWL_EVENTFLAG> nFlags) override;
+  void OnMouseExit(Mask<FWL_EVENTFLAG> nFlags) override;
+  bool OnLButtonDown(Mask<FWL_EVENTFLAG> nFlags,
+                     const CFX_PointF& point) override;
+  bool OnLButtonUp(Mask<FWL_EVENTFLAG> nFlags,
+                   const CFX_PointF& point) override;
+  bool OnLButtonDblClk(Mask<FWL_EVENTFLAG> nFlags,
+                       const CFX_PointF& point) override;
+  bool OnMouseMove(Mask<FWL_EVENTFLAG> nFlags,
+                   const CFX_PointF& point) override;
+  bool OnMouseWheel(Mask<FWL_EVENTFLAG> nFlags,
+                    const CFX_PointF& point,
+                    const CFX_Vector& delta) override;
+  bool OnRButtonDown(Mask<FWL_EVENTFLAG> nFlags,
+                     const CFX_PointF& point) override;
+  bool OnRButtonUp(Mask<FWL_EVENTFLAG> nFlags,
+                   const CFX_PointF& point) override;
+  bool OnChar(uint32_t nChar, Mask<FWL_EVENTFLAG> nFlags) override;
+  bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlags) override;
+  bool OnSetFocus(Mask<FWL_EVENTFLAG> nFlags) override;
+  bool OnKillFocus(Mask<FWL_EVENTFLAG> nFlags) override;
+
+  void SetOpenState(bool bOpenState);
+  void UpdateAnnotRects();
+  void InvalidateRect();
+
+  bool is_focused_ = false;
   UnownedPtr<CPDF_Annot> const m_pAnnot;
 };
 
diff --git a/fpdfsdk/cpdfsdk_baannot_embeddertest.cpp b/fpdfsdk/cpdfsdk_baannot_embeddertest.cpp
new file mode 100644
index 0000000..14362b0
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_baannot_embeddertest.cpp
@@ -0,0 +1,93 @@
+// Copyright 2019 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "fpdfsdk/cpdfsdk_annotiterator.h"
+#include "fpdfsdk/cpdfsdk_baannot.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/cpdfsdk_pageview.h"
+#include "testing/embedder_test.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+
+class CPDFSDK_BAAnnotTest : public EmbedderTest {
+ public:
+  void SetUp() override {
+    EmbedderTest::SetUp();
+    SetUpBAAnnot();
+  }
+
+  void TearDown() override {
+    UnloadPage(m_page);
+    EmbedderTest::TearDown();
+  }
+
+  void SetUpBAAnnot() {
+    ASSERT_TRUE(OpenDocument("links_highlights_annots.pdf"));
+    m_page = LoadPage(0);
+    ASSERT_TRUE(m_page);
+
+    m_pFormFillEnv =
+        CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle());
+    ASSERT_TRUE(m_pFormFillEnv);
+    m_pPageView =
+        m_pFormFillEnv->GetOrCreatePageView(IPDFPageFromFPDFPage(m_page));
+    ASSERT_TRUE(m_pPageView);
+  }
+
+  CPDFSDK_FormFillEnvironment* GetFormFillEnv() const { return m_pFormFillEnv; }
+  CPDFSDK_PageView* GetPageView() const { return m_pPageView; }
+
+  CPDFSDK_Annot* GetNthFocusableAnnot(size_t n) {
+    DCHECK_NE(n, 0);
+    CPDFSDK_AnnotIterator ai(GetPageView(),
+                             m_pFormFillEnv->GetFocusableAnnotSubtypes());
+    CPDFSDK_Annot* pAnnot = ai.GetFirstAnnot();
+    DCHECK(pAnnot);
+
+    for (size_t i = 1; i < n; i++) {
+      pAnnot = ai.GetNextAnnot(pAnnot);
+      DCHECK(pAnnot);
+    }
+
+    return pAnnot;
+  }
+
+ private:
+  FPDF_PAGE m_page = nullptr;
+  CPDFSDK_PageView* m_pPageView = nullptr;
+  CPDFSDK_FormFillEnvironment* m_pFormFillEnv = nullptr;
+};
+
+TEST_F(CPDFSDK_BAAnnotTest, TabToLinkOrHighlightAnnot) {
+  std::vector<CPDF_Annot::Subtype> focusable_annot_types = {
+      CPDF_Annot::Subtype::WIDGET, CPDF_Annot::Subtype::LINK,
+      CPDF_Annot::Subtype::HIGHLIGHT};
+
+  GetFormFillEnv()->SetFocusableAnnotSubtypes(focusable_annot_types);
+
+  // Get link annot.
+  CPDFSDK_Annot* pAnnot = GetNthFocusableAnnot(2);
+  ASSERT_TRUE(pAnnot);
+  EXPECT_EQ(pAnnot->GetAnnotSubtype(), CPDF_Annot::Subtype::LINK);
+
+  {
+    ObservedPtr<CPDFSDK_Annot> observer(pAnnot);
+    EXPECT_TRUE(CPDFSDK_Annot::OnSetFocus(observer, {}));
+    EXPECT_TRUE(CPDFSDK_Annot::OnKillFocus(observer, {}));
+  }
+
+  // Get highlight annot.
+  pAnnot = GetNthFocusableAnnot(4);
+  ASSERT_TRUE(pAnnot);
+  EXPECT_EQ(pAnnot->GetAnnotSubtype(), CPDF_Annot::Subtype::HIGHLIGHT);
+
+  {
+    ObservedPtr<CPDFSDK_Annot> observer(pAnnot);
+    EXPECT_TRUE(CPDFSDK_Annot::OnSetFocus(observer, {}));
+    EXPECT_TRUE(CPDFSDK_Annot::OnKillFocus(observer, {}));
+  }
+}
diff --git a/fpdfsdk/cpdfsdk_baannothandler.cpp b/fpdfsdk/cpdfsdk_baannothandler.cpp
deleted file mode 100644
index 6aa6de7..0000000
--- a/fpdfsdk/cpdfsdk_baannothandler.cpp
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/cpdfsdk_baannothandler.h"
-
-#include <memory>
-#include <vector>
-
-#include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfdoc/cpdf_interactiveform.h"
-#include "fpdfsdk/cpdfsdk_annot.h"
-#include "fpdfsdk/cpdfsdk_baannot.h"
-#include "fpdfsdk/cpdfsdk_pageview.h"
-#include "fpdfsdk/formfiller/cffl_formfiller.h"
-
-namespace {
-
-void UpdateAnnotRects(CPDFSDK_PageView* pPageView, CPDFSDK_BAAnnot* pBAAnnot) {
-  std::vector<CFX_FloatRect> rects;
-  rects.push_back(pBAAnnot->GetRect());
-  if (CPDF_Annot* pPopupAnnot = pBAAnnot->GetPDFPopupAnnot())
-    rects.push_back(pPopupAnnot->GetRect());
-
-  // Make the rects round up to avoid https://crbug.com/662804
-  for (CFX_FloatRect& rect : rects)
-    rect.Inflate(1, 1);
-
-  pPageView->UpdateRects(rects);
-}
-
-}  // namespace
-
-CPDFSDK_BAAnnotHandler::CPDFSDK_BAAnnotHandler() {}
-
-CPDFSDK_BAAnnotHandler::~CPDFSDK_BAAnnotHandler() {}
-
-void CPDFSDK_BAAnnotHandler::SetFormFillEnvironment(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  // CPDFSDK_BAAnnotHandler does not need it.
-}
-
-bool CPDFSDK_BAAnnotHandler::CanAnswer(CPDFSDK_Annot* pAnnot) {
-  return false;
-}
-
-CPDFSDK_Annot* CPDFSDK_BAAnnotHandler::NewAnnot(CPDF_Annot* pAnnot,
-                                                CPDFSDK_PageView* pPage) {
-  return new CPDFSDK_BAAnnot(pAnnot, pPage);
-}
-
-void CPDFSDK_BAAnnotHandler::ReleaseAnnot(
-    std::unique_ptr<CPDFSDK_Annot> pAnnot) {
-  // pAnnot deleted by unique_ptr going out of scope.
-}
-
-void CPDFSDK_BAAnnotHandler::OnDraw(CPDFSDK_PageView* pPageView,
-                                    CPDFSDK_Annot* pAnnot,
-                                    CFX_RenderDevice* pDevice,
-                                    const CFX_Matrix& mtUser2Device,
-                                    bool bDrawAnnots) {
-  if (pAnnot->AsXFAWidget())
-    return;
-
-  if (bDrawAnnots && pAnnot->GetAnnotSubtype() == CPDF_Annot::Subtype::POPUP) {
-    pAnnot->AsBAAnnot()->DrawAppearance(pDevice, mtUser2Device,
-                                        CPDF_Annot::Normal, nullptr);
-  }
-}
-
-void CPDFSDK_BAAnnotHandler::OnMouseEnter(CPDFSDK_PageView* pPageView,
-                                          ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                          uint32_t nFlag) {
-  CPDFSDK_BAAnnot* pBAAnnot = (*pAnnot)->AsBAAnnot();
-  pBAAnnot->SetOpenState(true);
-  UpdateAnnotRects(pPageView, pBAAnnot);
-}
-
-void CPDFSDK_BAAnnotHandler::OnMouseExit(CPDFSDK_PageView* pPageView,
-                                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                         uint32_t nFlag) {
-  CPDFSDK_BAAnnot* pBAAnnot = (*pAnnot)->AsBAAnnot();
-  pBAAnnot->SetOpenState(false);
-  UpdateAnnotRects(pPageView, pBAAnnot);
-}
-
-bool CPDFSDK_BAAnnotHandler::OnLButtonDown(CPDFSDK_PageView* pPageView,
-                                           ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                           uint32_t nFlags,
-                                           const CFX_PointF& point) {
-  return false;
-}
-
-bool CPDFSDK_BAAnnotHandler::OnLButtonUp(CPDFSDK_PageView* pPageView,
-                                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                         uint32_t nFlags,
-                                         const CFX_PointF& point) {
-  return false;
-}
-
-bool CPDFSDK_BAAnnotHandler::OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                             uint32_t nFlags,
-                                             const CFX_PointF& point) {
-  return false;
-}
-
-bool CPDFSDK_BAAnnotHandler::OnMouseMove(CPDFSDK_PageView* pPageView,
-                                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                         uint32_t nFlags,
-                                         const CFX_PointF& point) {
-  return false;
-}
-
-bool CPDFSDK_BAAnnotHandler::OnMouseWheel(CPDFSDK_PageView* pPageView,
-                                          ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                          uint32_t nFlags,
-                                          short zDelta,
-                                          const CFX_PointF& point) {
-  return false;
-}
-
-bool CPDFSDK_BAAnnotHandler::OnRButtonDown(CPDFSDK_PageView* pPageView,
-                                           ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                           uint32_t nFlags,
-                                           const CFX_PointF& point) {
-  return false;
-}
-
-bool CPDFSDK_BAAnnotHandler::OnRButtonUp(CPDFSDK_PageView* pPageView,
-                                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                         uint32_t nFlags,
-                                         const CFX_PointF& point) {
-  return false;
-}
-
-bool CPDFSDK_BAAnnotHandler::OnRButtonDblClk(CPDFSDK_PageView* pPageView,
-                                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                             uint32_t nFlags,
-                                             const CFX_PointF& point) {
-  return false;
-}
-
-bool CPDFSDK_BAAnnotHandler::OnChar(CPDFSDK_Annot* pAnnot,
-                                    uint32_t nChar,
-                                    uint32_t nFlags) {
-  return false;
-}
-
-bool CPDFSDK_BAAnnotHandler::OnKeyDown(CPDFSDK_Annot* pAnnot,
-                                       int nKeyCode,
-                                       int nFlag) {
-  return false;
-}
-
-bool CPDFSDK_BAAnnotHandler::OnKeyUp(CPDFSDK_Annot* pAnnot,
-                                     int nKeyCode,
-                                     int nFlag) {
-  return false;
-}
-
-void CPDFSDK_BAAnnotHandler::OnLoad(CPDFSDK_Annot* pAnnot) {}
-
-bool CPDFSDK_BAAnnotHandler::OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                        uint32_t nFlag) {
-  return false;
-}
-
-bool CPDFSDK_BAAnnotHandler::OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                         uint32_t nFlag) {
-  return false;
-}
-
-bool CPDFSDK_BAAnnotHandler::SetIndexSelected(
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    int index,
-    bool selected) {
-  return false;
-}
-
-bool CPDFSDK_BAAnnotHandler::IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                             int index) {
-  return false;
-}
-
-CFX_FloatRect CPDFSDK_BAAnnotHandler::GetViewBBox(CPDFSDK_PageView* pPageView,
-                                                  CPDFSDK_Annot* pAnnot) {
-  return pAnnot->GetRect();
-}
-
-WideString CPDFSDK_BAAnnotHandler::GetText(CPDFSDK_Annot* pAnnot) {
-  return WideString();
-}
-
-WideString CPDFSDK_BAAnnotHandler::GetSelectedText(CPDFSDK_Annot* pAnnot) {
-  return WideString();
-}
-
-void CPDFSDK_BAAnnotHandler::ReplaceSelection(CPDFSDK_Annot* pAnnot,
-                                              const WideString& text) {}
-
-bool CPDFSDK_BAAnnotHandler::CanUndo(CPDFSDK_Annot* pAnnot) {
-  return false;
-}
-
-bool CPDFSDK_BAAnnotHandler::CanRedo(CPDFSDK_Annot* pAnnot) {
-  return false;
-}
-
-bool CPDFSDK_BAAnnotHandler::Undo(CPDFSDK_Annot* pAnnot) {
-  return false;
-}
-
-bool CPDFSDK_BAAnnotHandler::Redo(CPDFSDK_Annot* pAnnot) {
-  return false;
-}
-
-bool CPDFSDK_BAAnnotHandler::HitTest(CPDFSDK_PageView* pPageView,
-                                     CPDFSDK_Annot* pAnnot,
-                                     const CFX_PointF& point) {
-  ASSERT(pPageView);
-  ASSERT(pAnnot);
-  return GetViewBBox(pPageView, pAnnot).Contains(point);
-}
diff --git a/fpdfsdk/cpdfsdk_baannothandler.h b/fpdfsdk/cpdfsdk_baannothandler.h
deleted file mode 100644
index 295bf0d..0000000
--- a/fpdfsdk/cpdfsdk_baannothandler.h
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_CPDFSDK_BAANNOTHANDLER_H_
-#define FPDFSDK_CPDFSDK_BAANNOTHANDLER_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "fpdfsdk/ipdfsdk_annothandler.h"
-
-class CFFL_InteractiveFormFiller;
-class CFX_Matrix;
-class CFX_RenderDevice;
-class CPDF_Annot;
-class CPDFSDK_FormFillEnvironment;
-class CPDFSDK_Annot;
-class CPDFSDK_PageView;
-
-class CPDFSDK_BAAnnotHandler final : public IPDFSDK_AnnotHandler {
- public:
-  CPDFSDK_BAAnnotHandler();
-  ~CPDFSDK_BAAnnotHandler() override;
-
-  // IPDFSDK_AnnotHandler:
-  void SetFormFillEnvironment(
-      CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  bool CanAnswer(CPDFSDK_Annot* pAnnot) override;
-  CPDFSDK_Annot* NewAnnot(CPDF_Annot* pAnnot, CPDFSDK_PageView* pPage) override;
-  void ReleaseAnnot(std::unique_ptr<CPDFSDK_Annot> pAnnot) override;
-  CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView,
-                            CPDFSDK_Annot* pAnnot) override;
-  WideString GetText(CPDFSDK_Annot* pAnnot) override;
-  WideString GetSelectedText(CPDFSDK_Annot* pAnnot) override;
-  void ReplaceSelection(CPDFSDK_Annot* pAnnot, const WideString& text) override;
-  bool CanUndo(CPDFSDK_Annot* pAnnot) override;
-  bool CanRedo(CPDFSDK_Annot* pAnnot) override;
-  bool Undo(CPDFSDK_Annot* pAnnot) override;
-  bool Redo(CPDFSDK_Annot* pAnnot) override;
-  bool HitTest(CPDFSDK_PageView* pPageView,
-               CPDFSDK_Annot* pAnnot,
-               const CFX_PointF& point) override;
-  void OnDraw(CPDFSDK_PageView* pPageView,
-              CPDFSDK_Annot* pAnnot,
-              CFX_RenderDevice* pDevice,
-              const CFX_Matrix& mtUser2Device,
-              bool bDrawAnnots) override;
-  void OnLoad(CPDFSDK_Annot* pAnnot) override;
-
-  void OnMouseEnter(CPDFSDK_PageView* pPageView,
-                    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                    uint32_t nFlag) override;
-  void OnMouseExit(CPDFSDK_PageView* pPageView,
-                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   uint32_t nFlag) override;
-  bool OnLButtonDown(CPDFSDK_PageView* pPageView,
-                     ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                     uint32_t nFlags,
-                     const CFX_PointF& point) override;
-  bool OnLButtonUp(CPDFSDK_PageView* pPageView,
-                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   uint32_t nFlags,
-                   const CFX_PointF& point) override;
-  bool OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                       ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                       uint32_t nFlags,
-                       const CFX_PointF& point) override;
-  bool OnMouseMove(CPDFSDK_PageView* pPageView,
-                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   uint32_t nFlags,
-                   const CFX_PointF& point) override;
-  bool OnMouseWheel(CPDFSDK_PageView* pPageView,
-                    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                    uint32_t nFlags,
-                    short zDelta,
-                    const CFX_PointF& point) override;
-  bool OnRButtonDown(CPDFSDK_PageView* pPageView,
-                     ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                     uint32_t nFlags,
-                     const CFX_PointF& point) override;
-  bool OnRButtonUp(CPDFSDK_PageView* pPageView,
-                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   uint32_t nFlags,
-                   const CFX_PointF& point) override;
-  bool OnRButtonDblClk(CPDFSDK_PageView* pPageView,
-                       ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                       uint32_t nFlags,
-                       const CFX_PointF& point) override;
-  bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override;
-  bool OnKeyDown(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override;
-  bool OnKeyUp(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override;
-  bool OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag) override;
-  bool OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag) override;
-  bool SetIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                        int index,
-                        bool selected) override;
-  bool IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot, int index) override;
-};
-
-#endif  // FPDFSDK_CPDFSDK_BAANNOTHANDLER_H_
diff --git a/fpdfsdk/cpdfsdk_baannothandler_embeddertest.cpp b/fpdfsdk/cpdfsdk_baannothandler_embeddertest.cpp
deleted file mode 100644
index 9a26e83..0000000
--- a/fpdfsdk/cpdfsdk_baannothandler_embeddertest.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2019 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "fpdfsdk/cpdfsdk_annotiterator.h"
-#include "fpdfsdk/cpdfsdk_baannothandler.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_helpers.h"
-#include "fpdfsdk/cpdfsdk_pageview.h"
-#include "public/fpdf_annot.h"
-#include "testing/embedder_test.h"
-
-class CPDFSDK_BAAnnotHandlerTest : public EmbedderTest {
- public:
-  void SetUp() override {
-    // Test behaviour with currently supported annot i.e. Widget.
-    // TODO(crbug.com/994500): Add an API that can set list of focusable
-    // subtypes once other annots(links & highlights) are also supported.
-    EmbedderTest::SetUp();
-    SetUpBAAnnotHandler();
-  }
-
-  void TearDown() override {
-    UnloadPage(m_page);
-    EmbedderTest::TearDown();
-  }
-
-  void SetUpBAAnnotHandler() {
-    EXPECT_TRUE(OpenDocument("links_highlights_annots.pdf"));
-    m_page = LoadPage(0);
-    ASSERT_TRUE(m_page);
-
-    CPDFSDK_FormFillEnvironment* pFormFillEnv =
-        CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle());
-    ASSERT_TRUE(pFormFillEnv);
-    m_pPageView = pFormFillEnv->GetPageView(IPDFPageFromFPDFPage(m_page), true);
-    ASSERT_TRUE(m_pPageView);
-
-    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-        pFormFillEnv->GetAnnotHandlerMgr();
-    ASSERT_TRUE(pAnnotHandlerMgr);
-    m_pBAAnnotHandler = pAnnotHandlerMgr->m_pBAAnnotHandler.get();
-    ASSERT_TRUE(m_pBAAnnotHandler);
-  }
-
-  CPDFSDK_PageView* GetPageView() const { return m_pPageView; }
-  CPDFSDK_BAAnnotHandler* GetBAAnnotHandler() const {
-    return m_pBAAnnotHandler;
-  }
-
- private:
-  FPDF_PAGE m_page = nullptr;
-  CPDFSDK_PageView* m_pPageView = nullptr;
-  CPDFSDK_BAAnnotHandler* m_pBAAnnotHandler = nullptr;
-};
-
-TEST_F(CPDFSDK_BAAnnotHandlerTest, TabToLinkOrHighlightAnnot) {
-  // TODO(crbug.com/994500): Create annot iterator with list of supported
-  // focusable subtypes as provided by host.
-  CPDFSDK_AnnotIterator ai(GetPageView(), CPDF_Annot::Subtype::LINK);
-  CPDFSDK_Annot* pAnnot = ai.GetFirstAnnot();
-  ASSERT_TRUE(pAnnot);
-  EXPECT_EQ(pAnnot->GetAnnotSubtype(), CPDF_Annot::Subtype::LINK);
-
-  ObservedPtr<CPDFSDK_Annot> pNonWidgetAnnot(pAnnot);
-
-  // TODO(crbug.com/994500): Change expected value as true once
-  // links & highlights are supported.
-  EXPECT_FALSE(GetBAAnnotHandler()->OnSetFocus(&pNonWidgetAnnot, 0));
-
-  EXPECT_FALSE(GetBAAnnotHandler()->OnKillFocus(&pNonWidgetAnnot, 0));
-}
-
-TEST_F(CPDFSDK_BAAnnotHandlerTest, TabToInvalidAnnot) {
-  // TODO(crbug.com/994500): Create annot iterator with list of supported
-  // focusable subtypes as provided by host.
-  CPDFSDK_AnnotIterator ai(GetPageView(), CPDF_Annot::Subtype::WIDGET);
-  CPDFSDK_Annot* pAnnot = ai.GetFirstAnnot();
-  ASSERT_TRUE(pAnnot);
-  EXPECT_EQ(pAnnot->GetAnnotSubtype(), CPDF_Annot::Subtype::WIDGET);
-
-  ObservedPtr<CPDFSDK_Annot> pWidgetAnnot(pAnnot);
-
-  // Passing wrong subtype to BAAnnotHandler, API should return false.
-  EXPECT_FALSE(GetBAAnnotHandler()->OnSetFocus(&pWidgetAnnot, 0));
-
-  EXPECT_FALSE(GetBAAnnotHandler()->OnKillFocus(&pWidgetAnnot, 0));
-}
diff --git a/fpdfsdk/cpdfsdk_customaccess.cpp b/fpdfsdk/cpdfsdk_customaccess.cpp
index 108ffbc..4f82823 100644
--- a/fpdfsdk/cpdfsdk_customaccess.cpp
+++ b/fpdfsdk/cpdfsdk_customaccess.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #include "fpdfsdk/cpdfsdk_customaccess.h"
 
 #include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 CPDFSDK_CustomAccess::CPDFSDK_CustomAccess(FPDF_FILEACCESS* pFileAccess)
     : m_FileAccess(*pFileAccess) {}
@@ -17,18 +18,19 @@
   return m_FileAccess.m_FileLen;
 }
 
-bool CPDFSDK_CustomAccess::ReadBlockAtOffset(void* buffer,
-                                             FX_FILESIZE offset,
-                                             size_t size) {
-  if (!buffer || offset < 0 || !size)
+bool CPDFSDK_CustomAccess::ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                                             FX_FILESIZE offset) {
+  if (buffer.empty() || offset < 0)
     return false;
 
-  if (!pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(size))
+  if (!pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(buffer.size()))
     return false;
 
-  FX_SAFE_FILESIZE new_pos = size;
+  FX_SAFE_FILESIZE new_pos = buffer.size();
   new_pos += offset;
   return new_pos.IsValid() && new_pos.ValueOrDie() <= GetSize() &&
-         m_FileAccess.m_GetBlock(m_FileAccess.m_Param, offset,
-                                 static_cast<uint8_t*>(buffer), size);
+         m_FileAccess.m_GetBlock(
+             m_FileAccess.m_Param,
+             pdfium::base::checked_cast<unsigned long>(offset), buffer.data(),
+             pdfium::base::checked_cast<unsigned long>(buffer.size()));
 }
diff --git a/fpdfsdk/cpdfsdk_customaccess.h b/fpdfsdk/cpdfsdk_customaccess.h
index 76940ce..734baeb 100644
--- a/fpdfsdk/cpdfsdk_customaccess.h
+++ b/fpdfsdk/cpdfsdk_customaccess.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,18 +8,17 @@
 #define FPDFSDK_CPDFSDK_CUSTOMACCESS_H_
 
 #include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "public/fpdfview.h"
 
 class CPDFSDK_CustomAccess final : public IFX_SeekableReadStream {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // IFX_SeekableReadStream
   FX_FILESIZE GetSize() override;
-  bool ReadBlockAtOffset(void* buffer,
-                         FX_FILESIZE offset,
-                         size_t size) override;
+  bool ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                         FX_FILESIZE offset) override;
 
  private:
   explicit CPDFSDK_CustomAccess(FPDF_FILEACCESS* pFileAccess);
diff --git a/fpdfsdk/cpdfsdk_fieldaction.cpp b/fpdfsdk/cpdfsdk_fieldaction.cpp
deleted file mode 100644
index 57fa1b6..0000000
--- a/fpdfsdk/cpdfsdk_fieldaction.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/cpdfsdk_fieldaction.h"
-
-CPDFSDK_FieldAction::CPDFSDK_FieldAction() = default;
-
-CPDFSDK_FieldAction::~CPDFSDK_FieldAction() = default;
diff --git a/fpdfsdk/cpdfsdk_fieldaction.h b/fpdfsdk/cpdfsdk_fieldaction.h
deleted file mode 100644
index e287911..0000000
--- a/fpdfsdk/cpdfsdk_fieldaction.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_CPDFSDK_FIELDACTION_H_
-#define FPDFSDK_CPDFSDK_FIELDACTION_H_
-
-#include "core/fxcrt/fx_string.h"
-
-struct CPDFSDK_FieldAction {
-  CPDFSDK_FieldAction();
-  CPDFSDK_FieldAction(const CPDFSDK_FieldAction& other) = delete;
-  ~CPDFSDK_FieldAction();
-
-  bool bModifier = false;
-  bool bShift = false;
-  bool bKeyDown = false;
-  bool bWillCommit = false;
-  bool bFieldFull = false;
-  bool bRC = true;
-  int nSelEnd = 0;
-  int nSelStart = 0;
-  WideString sChange;
-  WideString sChangeEx;
-  WideString sValue;
-};
-
-#endif  // FPDFSDK_CPDFSDK_FIELDACTION_H_
diff --git a/fpdfsdk/cpdfsdk_filewriteadapter.cpp b/fpdfsdk/cpdfsdk_filewriteadapter.cpp
index 39a69a5..776c8de 100644
--- a/fpdfsdk/cpdfsdk_filewriteadapter.cpp
+++ b/fpdfsdk/cpdfsdk_filewriteadapter.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,17 +6,18 @@
 
 #include "fpdfsdk/cpdfsdk_filewriteadapter.h"
 
+#include "third_party/base/check.h"
+#include "third_party/base/numerics/safe_conversions.h"
+
 CPDFSDK_FileWriteAdapter::CPDFSDK_FileWriteAdapter(FPDF_FILEWRITE* file_write)
     : file_write_(file_write) {
-  ASSERT(file_write_);
+  DCHECK(file_write_);
 }
 
-CPDFSDK_FileWriteAdapter::~CPDFSDK_FileWriteAdapter() {}
+CPDFSDK_FileWriteAdapter::~CPDFSDK_FileWriteAdapter() = default;
 
-bool CPDFSDK_FileWriteAdapter::WriteBlock(const void* data, size_t size) {
-  return file_write_->WriteBlock(file_write_.Get(), data, size) != 0;
-}
-
-bool CPDFSDK_FileWriteAdapter::WriteString(ByteStringView str) {
-  return WriteBlock(str.unterminated_c_str(), str.GetLength());
+bool CPDFSDK_FileWriteAdapter::WriteBlock(pdfium::span<const uint8_t> buffer) {
+  return file_write_->WriteBlock(
+             file_write_, buffer.data(),
+             pdfium::base::checked_cast<unsigned long>(buffer.size())) != 0;
 }
diff --git a/fpdfsdk/cpdfsdk_filewriteadapter.h b/fpdfsdk/cpdfsdk_filewriteadapter.h
index fc42b7f..477ec90 100644
--- a/fpdfsdk/cpdfsdk_filewriteadapter.h
+++ b/fpdfsdk/cpdfsdk_filewriteadapter.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,12 +14,10 @@
 
 class CPDFSDK_FileWriteAdapter final : public IFX_RetainableWriteStream {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // IFX_WriteStream:
-  bool WriteBlock(const void* data, size_t size) override;
-  bool WriteString(ByteStringView str) override;
+  bool WriteBlock(pdfium::span<const uint8_t> buffer) override;
 
  private:
   explicit CPDFSDK_FileWriteAdapter(FPDF_FILEWRITE* file_write);
diff --git a/fpdfsdk/cpdfsdk_formfillenvironment.cpp b/fpdfsdk/cpdfsdk_formfillenvironment.cpp
index 021ec60..9aade3a 100644
--- a/fpdfsdk/cpdfsdk_formfillenvironment.cpp
+++ b/fpdfsdk/cpdfsdk_formfillenvironment.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,25 +6,53 @@
 
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 
+#include <stdint.h>
+
 #include <memory>
 #include <utility>
 #include <vector>
 
+#include "core/fpdfapi/page/cpdf_annotcontext.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfdoc/cpdf_nametree.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
-#include "fpdfsdk/cpdfsdk_actionhandler.h"
-#include "fpdfsdk/cpdfsdk_annothandlermgr.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/stl_util.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
-#include "fpdfsdk/formfiller/cffl_formfiller.h"
+#include "fpdfsdk/formfiller/cffl_formfield.h"
 #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
+#include "fxjs/ijs_event_context.h"
 #include "fxjs/ijs_runtime.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/notreached.h"
+#include "third_party/base/numerics/safe_conversions.h"
+
+#ifdef PDF_ENABLE_XFA
+#include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h"
+#endif
+
+static_assert(FXCT_ARROW ==
+                  static_cast<int>(IPWL_FillerNotify::CursorStyle::kArrow),
+              "kArrow value mismatch");
+static_assert(FXCT_NESW ==
+                  static_cast<int>(IPWL_FillerNotify::CursorStyle::kNESW),
+              "kNEWS value mismatch");
+static_assert(FXCT_NWSE ==
+                  static_cast<int>(IPWL_FillerNotify::CursorStyle::kNWSE),
+              "kNWSE value mismatch");
+static_assert(FXCT_VBEAM ==
+                  static_cast<int>(IPWL_FillerNotify::CursorStyle::kVBeam),
+              "kVBeam value mismatch");
+static_assert(FXCT_HBEAM ==
+                  static_cast<int>(IPWL_FillerNotify::CursorStyle::kHBeam),
+              "HBeam value mismatch");
+static_assert(FXCT_HAND ==
+                  static_cast<int>(IPWL_FillerNotify::CursorStyle::kHand),
+              "kHand value mismatch");
 
 FPDF_WIDESTRING AsFPDFWideString(ByteString* bsUTF16LE) {
   // Force a private version of the string, since we're about to hand it off
@@ -36,13 +64,12 @@
 
 CPDFSDK_FormFillEnvironment::CPDFSDK_FormFillEnvironment(
     CPDF_Document* pDoc,
-    FPDF_FORMFILLINFO* pFFinfo,
-    std::unique_ptr<CPDFSDK_AnnotHandlerMgr> pHandlerMgr)
+    FPDF_FORMFILLINFO* pFFinfo)
     : m_pInfo(pFFinfo),
       m_pCPDFDoc(pDoc),
-      m_pAnnotHandlerMgr(std::move(pHandlerMgr)) {
-  ASSERT(m_pCPDFDoc);
-  m_pAnnotHandlerMgr->SetFormFillEnv(this);
+      m_pInteractiveFormFiller(
+          std::make_unique<CFFL_InteractiveFormFiller>(this)) {
+  DCHECK(m_pCPDFDoc);
 }
 
 CPDFSDK_FormFillEnvironment::~CPDFSDK_FormFillEnvironment() {
@@ -53,32 +80,23 @@
   // up. Make sure it is deleted before |m_pInteractiveForm|.
   m_PageMap.clear();
 
-  // |m_pAnnotHandlerMgr| will try to access |m_pFormFiller| when it cleans
-  // itself up. Make sure it is deleted before |m_pFormFiller|.
-  m_pAnnotHandlerMgr.reset();
-
-  // Must destroy the |m_pFormFiller| before the environment (|this|)
+  // Must destroy the |m_pInteractiveFormFiller| before the environment (|this|)
   // because any created form widgets hold a pointer to the environment.
   // Those widgets may call things like KillTimer() as they are shutdown.
-  m_pFormFiller.reset();
+  m_pInteractiveFormFiller.reset();
 
   if (m_pInfo && m_pInfo->Release)
     m_pInfo->Release(m_pInfo);
 }
 
-void CPDFSDK_FormFillEnvironment::InvalidateRect(PerWindowData* pWidgetData,
+void CPDFSDK_FormFillEnvironment::InvalidateRect(CPDFSDK_Widget* widget,
                                                  const CFX_FloatRect& rect) {
-  auto* pPrivateData = static_cast<CFFL_PrivateData*>(pWidgetData);
-  CPDFSDK_Widget* widget = pPrivateData->pWidget.Get();
-  if (!widget)
-    return;
-
-  CPDFSDK_PageView* pPageView = widget->GetPageView();
   IPDF_Page* pPage = widget->GetPage();
-  if (!pPage || !pPageView)
+  if (!pPage)
     return;
 
-  CFX_Matrix device2page = pPageView->GetCurrentMatrix().GetInverse();
+  CFX_Matrix device2page =
+      widget->GetPageView()->GetCurrentMatrix().GetInverse();
   CFX_PointF left_top = device2page.Transform(CFX_PointF(rect.left, rect.top));
   CFX_PointF right_bottom =
       device2page.Transform(CFX_PointF(rect.right, rect.bottom));
@@ -89,16 +107,16 @@
 }
 
 void CPDFSDK_FormFillEnvironment::OutputSelectedRect(
-    CFFL_FormFiller* pFormFiller,
+    CFFL_FormField* pFormField,
     const CFX_FloatRect& rect) {
-  if (!pFormFiller || !m_pInfo || !m_pInfo->FFI_OutputSelectedRect)
+  if (!m_pInfo || !m_pInfo->FFI_OutputSelectedRect)
     return;
 
-  auto* pPage = FPDFPageFromIPDFPage(pFormFiller->GetSDKAnnot()->GetPage());
-  ASSERT(pPage);
+  auto* pPage = FPDFPageFromIPDFPage(pFormField->GetSDKWidget()->GetPage());
+  DCHECK(pPage);
 
-  CFX_PointF ptA = pFormFiller->PWLtoFFL(CFX_PointF(rect.left, rect.bottom));
-  CFX_PointF ptB = pFormFiller->PWLtoFFL(CFX_PointF(rect.right, rect.top));
+  CFX_PointF ptA = pFormField->PWLtoFFL(CFX_PointF(rect.left, rect.bottom));
+  CFX_PointF ptB = pFormField->PWLtoFFL(CFX_PointF(rect.right, rect.top));
   m_pInfo->FFI_OutputSelectedRect(m_pInfo, pPage, ptA.x, ptB.y, ptB.x, ptA.y);
 }
 
@@ -109,14 +127,14 @@
 
 #ifdef PDF_ENABLE_V8
 CPDFSDK_PageView* CPDFSDK_FormFillEnvironment::GetCurrentView() {
-  IPDF_Page* pPage = IPDFPageFromFPDFPage(GetCurrentPage());
-  return pPage ? GetPageView(pPage, true) : nullptr;
+  IPDF_Page* pPage = GetCurrentPage();
+  return pPage ? GetOrCreatePageView(pPage) : nullptr;
 }
 
-FPDF_PAGE CPDFSDK_FormFillEnvironment::GetCurrentPage() const {
+IPDF_Page* CPDFSDK_FormFillEnvironment::GetCurrentPage() const {
   if (m_pInfo && m_pInfo->FFI_GetCurrentPage) {
-    return m_pInfo->FFI_GetCurrentPage(
-        m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc.Get()));
+    return IPDFPageFromFPDFPage(m_pInfo->FFI_GetCurrentPage(
+        m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc)));
   }
   return nullptr;
 }
@@ -130,7 +148,7 @@
   if (nRequiredLen <= 0)
     return WideString();
 
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> pBuff(nRequiredLen);
+  DataVector<uint8_t> pBuff(nRequiredLen);
   int nActualLen =
       m_pInfo->FFI_GetLanguage(m_pInfo, pBuff.data(), nRequiredLen);
   if (nActualLen <= 0 || nActualLen > nRequiredLen)
@@ -152,7 +170,7 @@
   if (nRequiredLen <= 0)
     return WideString();
 
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> pBuff(nRequiredLen);
+  DataVector<uint8_t> pBuff(nRequiredLen);
   int nActualLen =
       m_pInfo->FFI_GetPlatform(m_pInfo, pBuff.data(), nRequiredLen);
   if (nActualLen <= 0 || nActualLen > nRequiredLen)
@@ -169,89 +187,89 @@
                                              const WideString& Title,
                                              int Type,
                                              int Icon) {
-  if (!m_pInfo || !m_pInfo->m_pJsPlatform ||
-      !m_pInfo->m_pJsPlatform->app_alert) {
+  IPDF_JSPLATFORM* js_platform = GetJSPlatform();
+  if (!js_platform || !js_platform->app_alert)
     return -1;
-  }
 
   ByteString bsMsg = Msg.ToUTF16LE();
   ByteString bsTitle = Title.ToUTF16LE();
-  return m_pInfo->m_pJsPlatform->app_alert(
-      m_pInfo->m_pJsPlatform, AsFPDFWideString(&bsMsg),
-      AsFPDFWideString(&bsTitle), Type, Icon);
+  return js_platform->app_alert(js_platform, AsFPDFWideString(&bsMsg),
+                                AsFPDFWideString(&bsTitle), Type, Icon);
 }
 
-int CPDFSDK_FormFillEnvironment::JS_appResponse(const WideString& Question,
-                                                const WideString& Title,
-                                                const WideString& Default,
-                                                const WideString& Label,
-                                                FPDF_BOOL bPassword,
-                                                void* response,
-                                                int length) {
-  if (!m_pInfo || !m_pInfo->m_pJsPlatform ||
-      !m_pInfo->m_pJsPlatform->app_response) {
+int CPDFSDK_FormFillEnvironment::JS_appResponse(
+    const WideString& Question,
+    const WideString& Title,
+    const WideString& Default,
+    const WideString& Label,
+    FPDF_BOOL bPassword,
+    pdfium::span<uint8_t> response) {
+  IPDF_JSPLATFORM* js_platform = GetJSPlatform();
+  if (!js_platform || !js_platform->app_response)
     return -1;
-  }
+
   ByteString bsQuestion = Question.ToUTF16LE();
   ByteString bsTitle = Title.ToUTF16LE();
   ByteString bsDefault = Default.ToUTF16LE();
   ByteString bsLabel = Label.ToUTF16LE();
-  return m_pInfo->m_pJsPlatform->app_response(
-      m_pInfo->m_pJsPlatform, AsFPDFWideString(&bsQuestion),
-      AsFPDFWideString(&bsTitle), AsFPDFWideString(&bsDefault),
-      AsFPDFWideString(&bsLabel), bPassword, response, length);
+  return js_platform->app_response(
+      js_platform, AsFPDFWideString(&bsQuestion), AsFPDFWideString(&bsTitle),
+      AsFPDFWideString(&bsDefault), AsFPDFWideString(&bsLabel), bPassword,
+      response.data(), pdfium::base::checked_cast<int>(response.size()));
 }
 
 void CPDFSDK_FormFillEnvironment::JS_appBeep(int nType) {
-  if (!m_pInfo || !m_pInfo->m_pJsPlatform ||
-      !m_pInfo->m_pJsPlatform->app_beep) {
+  IPDF_JSPLATFORM* js_platform = GetJSPlatform();
+  if (!js_platform || !js_platform->app_beep)
     return;
-  }
-  m_pInfo->m_pJsPlatform->app_beep(m_pInfo->m_pJsPlatform, nType);
+
+  js_platform->app_beep(js_platform, nType);
 }
 
 WideString CPDFSDK_FormFillEnvironment::JS_fieldBrowse() {
-  if (!m_pInfo || !m_pInfo->m_pJsPlatform ||
-      !m_pInfo->m_pJsPlatform->Field_browse) {
+  IPDF_JSPLATFORM* js_platform = GetJSPlatform();
+  if (!js_platform || !js_platform->Field_browse)
     return WideString();
-  }
-  const int nRequiredLen =
-      m_pInfo->m_pJsPlatform->Field_browse(m_pInfo->m_pJsPlatform, nullptr, 0);
+
+  const int nRequiredLen = js_platform->Field_browse(js_platform, nullptr, 0);
   if (nRequiredLen <= 0)
     return WideString();
 
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> pBuff(nRequiredLen);
-  const int nActualLen = m_pInfo->m_pJsPlatform->Field_browse(
-      m_pInfo->m_pJsPlatform, pBuff.data(), nRequiredLen);
+  DataVector<uint8_t> pBuff(nRequiredLen);
+  const int nActualLen =
+      js_platform->Field_browse(js_platform, pBuff.data(), nRequiredLen);
   if (nActualLen <= 0 || nActualLen > nRequiredLen)
     return WideString();
 
   // Don't include trailing NUL.
   pBuff.resize(nActualLen - 1);
+
+  // Use FromDefANSI() per "local encoding" comment in fpdf_formfill.h.
   return WideString::FromDefANSI(ByteStringView(pBuff));
 }
 
-void CPDFSDK_FormFillEnvironment::JS_docmailForm(void* mailData,
-                                                 int length,
-                                                 FPDF_BOOL bUI,
-                                                 const WideString& To,
-                                                 const WideString& Subject,
-                                                 const WideString& CC,
-                                                 const WideString& BCC,
-                                                 const WideString& Msg) {
-  if (!m_pInfo || !m_pInfo->m_pJsPlatform ||
-      !m_pInfo->m_pJsPlatform->Doc_mail) {
+void CPDFSDK_FormFillEnvironment::JS_docmailForm(
+    pdfium::span<const uint8_t> mailData,
+    FPDF_BOOL bUI,
+    const WideString& To,
+    const WideString& Subject,
+    const WideString& CC,
+    const WideString& BCC,
+    const WideString& Msg) {
+  IPDF_JSPLATFORM* js_platform = GetJSPlatform();
+  if (!js_platform || !js_platform->Doc_mail)
     return;
-  }
+
   ByteString bsTo = To.ToUTF16LE();
   ByteString bsSubject = Subject.ToUTF16LE();
   ByteString bsCC = CC.ToUTF16LE();
   ByteString bsBcc = BCC.ToUTF16LE();
   ByteString bsMsg = Msg.ToUTF16LE();
-  m_pInfo->m_pJsPlatform->Doc_mail(
-      m_pInfo->m_pJsPlatform, mailData, length, bUI, AsFPDFWideString(&bsTo),
-      AsFPDFWideString(&bsSubject), AsFPDFWideString(&bsCC),
-      AsFPDFWideString(&bsBcc), AsFPDFWideString(&bsMsg));
+  js_platform->Doc_mail(js_platform, const_cast<uint8_t*>(mailData.data()),
+                        pdfium::base::checked_cast<int>(mailData.size()), bUI,
+                        AsFPDFWideString(&bsTo), AsFPDFWideString(&bsSubject),
+                        AsFPDFWideString(&bsCC), AsFPDFWideString(&bsBcc),
+                        AsFPDFWideString(&bsMsg));
 }
 
 void CPDFSDK_FormFillEnvironment::JS_docprint(FPDF_BOOL bUI,
@@ -262,21 +280,20 @@
                                               FPDF_BOOL bPrintAsImage,
                                               FPDF_BOOL bReverse,
                                               FPDF_BOOL bAnnotations) {
-  if (!m_pInfo || !m_pInfo->m_pJsPlatform ||
-      !m_pInfo->m_pJsPlatform->Doc_print) {
+  IPDF_JSPLATFORM* js_platform = GetJSPlatform();
+  if (!js_platform || !js_platform->Doc_print)
     return;
-  }
-  m_pInfo->m_pJsPlatform->Doc_print(m_pInfo->m_pJsPlatform, bUI, nStart, nEnd,
-                                    bSilent, bShrinkToFit, bPrintAsImage,
-                                    bReverse, bAnnotations);
+
+  js_platform->Doc_print(js_platform, bUI, nStart, nEnd, bSilent, bShrinkToFit,
+                         bPrintAsImage, bReverse, bAnnotations);
 }
 
 void CPDFSDK_FormFillEnvironment::JS_docgotoPage(int nPageNum) {
-  if (!m_pInfo || !m_pInfo->m_pJsPlatform ||
-      !m_pInfo->m_pJsPlatform->Doc_gotoPage) {
+  IPDF_JSPLATFORM* js_platform = GetJSPlatform();
+  if (!js_platform || !js_platform->Doc_gotoPage)
     return;
-  }
-  m_pInfo->m_pJsPlatform->Doc_gotoPage(m_pInfo->m_pJsPlatform, nPageNum);
+
+  js_platform->Doc_gotoPage(js_platform, nPageNum);
 }
 
 WideString CPDFSDK_FormFillEnvironment::JS_docGetFilePath() {
@@ -285,36 +302,39 @@
 #endif  // PDF_ENABLE_V8
 
 WideString CPDFSDK_FormFillEnvironment::GetFilePath() const {
-  if (!m_pInfo || !m_pInfo->m_pJsPlatform ||
-      !m_pInfo->m_pJsPlatform->Doc_getFilePath) {
+  IPDF_JSPLATFORM* js_platform = GetJSPlatform();
+  if (!js_platform || !js_platform->Doc_getFilePath)
     return WideString();
-  }
-  const int nRequiredLen = m_pInfo->m_pJsPlatform->Doc_getFilePath(
-      m_pInfo->m_pJsPlatform, nullptr, 0);
+
+  const int nRequiredLen =
+      js_platform->Doc_getFilePath(js_platform, nullptr, 0);
   if (nRequiredLen <= 0)
     return WideString();
 
-  std::vector<uint8_t, FxAllocAllocator<uint8_t>> pBuff(nRequiredLen);
-  const int nActualLen = m_pInfo->m_pJsPlatform->Doc_getFilePath(
-      m_pInfo->m_pJsPlatform, pBuff.data(), nRequiredLen);
+  DataVector<uint8_t> pBuff(nRequiredLen);
+  const int nActualLen =
+      js_platform->Doc_getFilePath(js_platform, pBuff.data(), nRequiredLen);
   if (nActualLen <= 0 || nActualLen > nRequiredLen)
     return WideString();
 
   // Don't include trailing NUL.
   pBuff.resize(nActualLen - 1);
+
+  // Use FromDefANSI() per "local encoding" comment in fpdf_formfill.h.
   return WideString::FromDefANSI(ByteStringView(pBuff));
 }
 
-void CPDFSDK_FormFillEnvironment::SubmitForm(pdfium::span<uint8_t> form_data,
-                                             const WideString& URL) {
-  if (!m_pInfo || !m_pInfo->m_pJsPlatform ||
-      !m_pInfo->m_pJsPlatform->Doc_submitForm) {
+void CPDFSDK_FormFillEnvironment::SubmitForm(
+    pdfium::span<const uint8_t> form_data,
+    const WideString& URL) {
+  IPDF_JSPLATFORM* js_platform = GetJSPlatform();
+  if (!js_platform || !js_platform->Doc_submitForm)
     return;
-  }
+
   ByteString bsUrl = URL.ToUTF16LE();
-  m_pInfo->m_pJsPlatform->Doc_submitForm(m_pInfo->m_pJsPlatform,
-                                         form_data.data(), form_data.size(),
-                                         AsFPDFWideString(&bsUrl));
+  js_platform->Doc_submitForm(
+      js_platform, const_cast<uint8_t*>(form_data.data()),
+      fxcrt::CollectionSize<int>(form_data), AsFPDFWideString(&bsUrl));
 }
 
 IJS_Runtime* CPDFSDK_FormFillEnvironment::GetIJSRuntime() {
@@ -323,23 +343,6 @@
   return m_pIJSRuntime.get();
 }
 
-CPDFSDK_AnnotHandlerMgr* CPDFSDK_FormFillEnvironment::GetAnnotHandlerMgr() {
-  return m_pAnnotHandlerMgr.get();
-}
-
-CPDFSDK_ActionHandler* CPDFSDK_FormFillEnvironment::GetActionHandler() {
-  if (!m_pActionHandler)
-    m_pActionHandler = pdfium::MakeUnique<CPDFSDK_ActionHandler>();
-  return m_pActionHandler.get();
-}
-
-CFFL_InteractiveFormFiller*
-CPDFSDK_FormFillEnvironment::GetInteractiveFormFiller() {
-  if (!m_pFormFiller)
-    m_pFormFiller = pdfium::MakeUnique<CFFL_InteractiveFormFiller>(this);
-  return m_pFormFiller.get();
-}
-
 void CPDFSDK_FormFillEnvironment::Invalidate(IPDF_Page* page,
                                              const FX_RECT& rect) {
   if (m_pInfo && m_pInfo->FFI_Invalidate) {
@@ -348,16 +351,17 @@
   }
 }
 
-void CPDFSDK_FormFillEnvironment::SetCursor(int nCursorType) {
+void CPDFSDK_FormFillEnvironment::SetCursor(
+    IPWL_FillerNotify::CursorStyle nCursorType) {
   if (m_pInfo && m_pInfo->FFI_SetCursor)
-    m_pInfo->FFI_SetCursor(m_pInfo, nCursorType);
+    m_pInfo->FFI_SetCursor(m_pInfo, static_cast<int>(nCursorType));
 }
 
 int CPDFSDK_FormFillEnvironment::SetTimer(int uElapse,
                                           TimerCallback lpTimerFunc) {
   if (m_pInfo && m_pInfo->FFI_SetTimer)
     return m_pInfo->FFI_SetTimer(m_pInfo, uElapse, lpTimerFunc);
-  return TimerHandlerIface::kInvalidTimerID;
+  return CFX_Timer::HandlerIface::kInvalidTimerID;
 }
 
 void CPDFSDK_FormFillEnvironment::KillTimer(int nTimerID) {
@@ -370,37 +374,78 @@
     m_pInfo->FFI_OnChange(m_pInfo);
 }
 
-void CPDFSDK_FormFillEnvironment::ExecuteNamedAction(const char* namedAction) {
+void CPDFSDK_FormFillEnvironment::ExecuteNamedAction(
+    const ByteString& namedAction) {
   if (m_pInfo && m_pInfo->FFI_ExecuteNamedAction)
-    m_pInfo->FFI_ExecuteNamedAction(m_pInfo, namedAction);
+    m_pInfo->FFI_ExecuteNamedAction(m_pInfo, namedAction.c_str());
 }
 
-void CPDFSDK_FormFillEnvironment::OnSetFieldInputFocus(
-    FPDF_WIDESTRING focusText,
-    FPDF_DWORD nTextLen,
+void CPDFSDK_FormFillEnvironment::OnSetFieldInputFocus(const WideString& text) {
+  OnSetFieldInputFocusInternal(text, true);
+}
+
+void CPDFSDK_FormFillEnvironment::OnSetFieldInputFocusInternal(
+    const WideString& text,
     bool bFocus) {
-  if (m_pInfo && m_pInfo->FFI_SetTextFieldFocus)
-    m_pInfo->FFI_SetTextFieldFocus(m_pInfo, focusText, nTextLen, bFocus);
+  if (m_pInfo && m_pInfo->FFI_SetTextFieldFocus) {
+    size_t nCharacters = text.GetLength();
+    ByteString bsUTFText = text.ToUTF16LE();
+    auto* pBuffer = reinterpret_cast<const unsigned short*>(bsUTFText.c_str());
+    m_pInfo->FFI_SetTextFieldFocus(
+        m_pInfo, pBuffer, pdfium::base::checked_cast<FPDF_DWORD>(nCharacters),
+        bFocus);
+  }
 }
 
-void CPDFSDK_FormFillEnvironment::DoURIAction(const char* bsURI) {
-  if (m_pInfo && m_pInfo->FFI_DoURIAction)
-    m_pInfo->FFI_DoURIAction(m_pInfo, bsURI);
+void CPDFSDK_FormFillEnvironment::OnCalculate(
+    ObservedPtr<CPDFSDK_Annot>& pAnnot) {
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot.Get());
+  if (pWidget)
+    m_pInteractiveForm->OnCalculate(pWidget->GetFormField());
+}
+
+void CPDFSDK_FormFillEnvironment::OnFormat(ObservedPtr<CPDFSDK_Annot>& pAnnot) {
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot.Get());
+  DCHECK(pWidget);
+
+  absl::optional<WideString> sValue =
+      m_pInteractiveForm->OnFormat(pWidget->GetFormField());
+  if (!pAnnot)
+    return;
+
+  if (sValue.has_value()) {
+    m_pInteractiveForm->ResetFieldAppearance(pWidget->GetFormField(), sValue);
+    m_pInteractiveForm->UpdateField(pWidget->GetFormField());
+  }
+}
+
+void CPDFSDK_FormFillEnvironment::DoURIAction(const ByteString& bsURI,
+                                              Mask<FWL_EVENTFLAG> modifiers) {
+  if (!m_pInfo)
+    return;
+
+  if (m_pInfo->version >= 2 && m_pInfo->FFI_DoURIActionWithKeyboardModifier) {
+    m_pInfo->FFI_DoURIActionWithKeyboardModifier(m_pInfo, bsURI.c_str(),
+                                                 modifiers.UncheckedValue());
+    return;
+  }
+
+  if (m_pInfo->FFI_DoURIAction)
+    m_pInfo->FFI_DoURIAction(m_pInfo, bsURI.c_str());
 }
 
 void CPDFSDK_FormFillEnvironment::DoGoToAction(int nPageIndex,
                                                int zoomMode,
-                                               float* fPosArray,
-                                               int sizeOfArray) {
+                                               pdfium::span<float> fPosArray) {
   if (m_pInfo && m_pInfo->FFI_DoGoToAction) {
-    m_pInfo->FFI_DoGoToAction(m_pInfo, nPageIndex, zoomMode, fPosArray,
-                              sizeOfArray);
+    m_pInfo->FFI_DoGoToAction(m_pInfo, nPageIndex, zoomMode, fPosArray.data(),
+                              fxcrt::CollectionSize<int>(fPosArray));
   }
 }
 
 #ifdef PDF_ENABLE_XFA
 int CPDFSDK_FormFillEnvironment::GetPageViewCount() const {
-  return pdfium::CollectionSize<int>(m_PageMap);
+  return fxcrt::CollectionSize<int>(m_PageMap);
 }
 
 void CPDFSDK_FormFillEnvironment::DisplayCaret(IPDF_Page* page,
@@ -419,14 +464,14 @@
   if (!m_pInfo || m_pInfo->version < 2 || !m_pInfo->FFI_GetCurrentPageIndex)
     return -1;
   return m_pInfo->FFI_GetCurrentPageIndex(
-      m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc.Get()));
+      m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc));
 }
 
 void CPDFSDK_FormFillEnvironment::SetCurrentPage(int iCurPage) {
   if (!m_pInfo || m_pInfo->version < 2 || !m_pInfo->FFI_SetCurrentPage)
     return;
-  m_pInfo->FFI_SetCurrentPage(
-      m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc.Get()), iCurPage);
+  m_pInfo->FFI_SetCurrentPage(m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc),
+                              iCurPage);
 }
 
 void CPDFSDK_FormFillEnvironment::GotoURL(const WideString& wsURL) {
@@ -434,7 +479,7 @@
     return;
 
   ByteString bsTo = wsURL.ToUTF16LE();
-  m_pInfo->FFI_GotoURL(m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc.Get()),
+  m_pInfo->FFI_GotoURL(m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc),
                        AsFPDFWideString(&bsTo));
 }
 
@@ -458,11 +503,10 @@
 }
 
 bool CPDFSDK_FormFillEnvironment::PopupMenu(IPDF_Page* page,
-                                            FPDF_WIDGET hWidget,
                                             int menuFlag,
                                             const CFX_PointF& pt) {
   return m_pInfo && m_pInfo->version >= 2 && m_pInfo->FFI_PopupMenu &&
-         m_pInfo->FFI_PopupMenu(m_pInfo, FPDFPageFromIPDFPage(page), hWidget,
+         m_pInfo->FFI_PopupMenu(m_pInfo, FPDFPageFromIPDFPage(page), nullptr,
                                 menuFlag, pt.x, pt.y);
 }
 
@@ -527,7 +571,7 @@
 
   WideString wsRet =
       WideString::FromUTF16LE(reinterpret_cast<FPDF_WIDESTRING>(response.str),
-                              response.len / sizeof(FPDF_WIDESTRING));
+                              response.len / sizeof(FPDF_WCHAR));
 
   FPDF_BStr_Clear(&response);
   return wsRet;
@@ -558,22 +602,22 @@
 
 void CPDFSDK_FormFillEnvironment::ClearAllFocusedAnnots() {
   for (auto& it : m_PageMap) {
-    if (it.second->IsValidSDKAnnot(GetFocusAnnot()))
-      KillFocusAnnot(0);
+    if (it.second->IsValidSDKAnnot(GetFocusAnnot())) {
+      ObservedPtr<CPDFSDK_PageView> pObserved(it.second.get());
+      KillFocusAnnot({});
+      if (!pObserved)
+        break;
+    }
   }
 }
 
-CPDFSDK_PageView* CPDFSDK_FormFillEnvironment::GetPageView(
-    IPDF_Page* pUnderlyingPage,
-    bool renew) {
-  auto it = m_PageMap.find(pUnderlyingPage);
-  if (it != m_PageMap.end())
-    return it->second.get();
+CPDFSDK_PageView* CPDFSDK_FormFillEnvironment::GetOrCreatePageView(
+    IPDF_Page* pUnderlyingPage) {
+  CPDFSDK_PageView* pExisting = GetPageView(pUnderlyingPage);
+  if (pExisting)
+    return pExisting;
 
-  if (!renew)
-    return nullptr;
-
-  auto pNew = pdfium::MakeUnique<CPDFSDK_PageView>(this, pUnderlyingPage);
+  auto pNew = std::make_unique<CPDFSDK_PageView>(this, pUnderlyingPage);
   CPDFSDK_PageView* pPageView = pNew.get();
   m_PageMap[pUnderlyingPage] = std::move(pNew);
 
@@ -582,31 +626,40 @@
   return pPageView;
 }
 
-CPDFSDK_PageView* CPDFSDK_FormFillEnvironment::GetPageView(int nIndex) {
-  IPDF_Page* pTempPage = GetPage(nIndex);
-  if (!pTempPage)
-    return nullptr;
-
-  auto it = m_PageMap.find(pTempPage);
+CPDFSDK_PageView* CPDFSDK_FormFillEnvironment::GetPageView(
+    IPDF_Page* pUnderlyingPage) {
+  auto it = m_PageMap.find(pUnderlyingPage);
   return it != m_PageMap.end() ? it->second.get() : nullptr;
 }
 
+CFX_Timer::HandlerIface* CPDFSDK_FormFillEnvironment::GetTimerHandler() {
+  return this;
+}
+
+CPDFSDK_PageView* CPDFSDK_FormFillEnvironment::GetPageViewAtIndex(int nIndex) {
+  IPDF_Page* pTempPage = GetPage(nIndex);
+  return pTempPage ? GetPageView(pTempPage) : nullptr;
+}
+
 void CPDFSDK_FormFillEnvironment::ProcJavascriptAction() {
-  CPDF_NameTree docJS(m_pCPDFDoc.Get(), "JavaScript");
-  int iCount = docJS.GetCount();
-  for (int i = 0; i < iCount; i++) {
+  auto name_tree = CPDF_NameTree::Create(m_pCPDFDoc, "JavaScript");
+  if (!name_tree)
+    return;
+
+  size_t count = name_tree->GetCount();
+  for (size_t i = 0; i < count; ++i) {
     WideString name;
-    CPDF_Action action(ToDictionary(docJS.LookupValueAndName(i, &name)));
-    GetActionHandler()->DoAction_JavaScript(action, name, this);
+    CPDF_Action action(ToDictionary(name_tree->LookupValueAndName(i, &name)));
+    DoActionJavaScript(action, name);
   }
 }
 
 bool CPDFSDK_FormFillEnvironment::ProcOpenAction() {
-  CPDF_Dictionary* pRoot = m_pCPDFDoc->GetRoot();
+  const CPDF_Dictionary* pRoot = m_pCPDFDoc->GetRoot();
   if (!pRoot)
     return false;
 
-  CPDF_Object* pOpenAction = pRoot->GetDictFor("OpenAction");
+  RetainPtr<const CPDF_Object> pOpenAction(pRoot->GetDictFor("OpenAction"));
   if (!pOpenAction)
     pOpenAction = pRoot->GetArrayFor("OpenAction");
   if (!pOpenAction)
@@ -615,12 +668,11 @@
   if (pOpenAction->IsArray())
     return true;
 
-  CPDF_Dictionary* pDict = pOpenAction->AsDictionary();
+  RetainPtr<const CPDF_Dictionary> pDict = ToDictionary(pOpenAction);
   if (!pDict)
     return false;
 
-  CPDF_Action action(pDict);
-  GetActionHandler()->DoAction_DocOpen(action, this);
+  DoActionDocOpen(CPDF_Action(std::move(pDict)));
   return true;
 }
 
@@ -643,97 +695,102 @@
   // be created. We then have two page views pointing to the same page and
   // bad things happen.
   if (pPageView->IsValidSDKAnnot(GetFocusAnnot()))
-    KillFocusAnnot(0);
+    KillFocusAnnot({});
 
   // Remove the page from the map to make sure we don't accidentally attempt
   // to use the |pPageView| while we're cleaning it up.
   m_PageMap.erase(it);
 }
 
-IPDF_Page* CPDFSDK_FormFillEnvironment::GetPage(int nIndex) {
+IPDF_Page* CPDFSDK_FormFillEnvironment::GetPage(int nIndex) const {
   if (!m_pInfo || !m_pInfo->FFI_GetPage)
     return nullptr;
   return IPDFPageFromFPDFPage(m_pInfo->FFI_GetPage(
-      m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc.Get()), nIndex));
+      m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc), nIndex));
 }
 
 CPDFSDK_InteractiveForm* CPDFSDK_FormFillEnvironment::GetInteractiveForm() {
   if (!m_pInteractiveForm)
-    m_pInteractiveForm = pdfium::MakeUnique<CPDFSDK_InteractiveForm>(this);
+    m_pInteractiveForm = std::make_unique<CPDFSDK_InteractiveForm>(this);
   return m_pInteractiveForm.get();
 }
 
-void CPDFSDK_FormFillEnvironment::UpdateAllViews(CPDFSDK_PageView* pSender,
-                                                 CPDFSDK_Annot* pAnnot) {
+void CPDFSDK_FormFillEnvironment::UpdateAllViews(CPDFSDK_Annot* pAnnot) {
   for (const auto& it : m_PageMap) {
-    CPDFSDK_PageView* pPageView = it.second.get();
-    if (pPageView != pSender)
-      pPageView->UpdateView(pAnnot);
+    ObservedPtr<CPDFSDK_PageView> pObserved(it.second.get());
+    if (pObserved) {
+      pObserved->UpdateView(pAnnot);
+      if (!pObserved)
+        break;
+    }
   }
 }
 
+CPDFSDK_Annot* CPDFSDK_FormFillEnvironment::GetFocusAnnot() const {
+  return m_pFocusAnnot.Get();
+}
+
 bool CPDFSDK_FormFillEnvironment::SetFocusAnnot(
-    ObservedPtr<CPDFSDK_Annot>* pAnnot) {
+    ObservedPtr<CPDFSDK_Annot>& pAnnot) {
   if (m_bBeingDestroyed)
     return false;
-  if (m_pFocusAnnot == *pAnnot)
+  if (m_pFocusAnnot == pAnnot)
     return true;
-  if (m_pFocusAnnot && !KillFocusAnnot(0))
+  if (m_pFocusAnnot && !KillFocusAnnot({}))
     return false;
-  if (!pAnnot->HasObservable())
+  if (!pAnnot)
+    return false;
+  if (!pAnnot->GetPageView()->IsValid())
     return false;
 
-  CPDFSDK_PageView* pPageView = (*pAnnot)->GetPageView();
-  if (!pPageView || !pPageView->IsValid())
-    return false;
-
-  CPDFSDK_AnnotHandlerMgr* pAnnotHandler = GetAnnotHandlerMgr();
   if (m_pFocusAnnot)
     return false;
 
 #ifdef PDF_ENABLE_XFA
-  ObservedPtr<CPDFSDK_Annot> pLastFocusAnnot(m_pFocusAnnot.Get());
-  if (!pAnnotHandler->Annot_OnChangeFocus(pAnnot, &pLastFocusAnnot))
+  CPDFXFA_Widget* pXFAWidget = pAnnot->AsXFAWidget();
+  if (pXFAWidget && pXFAWidget->OnChangedFocus())
     return false;
 
-  // |pAnnot| may be destroyed in |Annot_OnChangeFocus|.
-  if (!pAnnot->HasObservable())
+  // `pAnnot` may be destroyed in `OnChangedFocus()`.
+  if (!pAnnot)
     return false;
 #endif  // PDF_ENABLE_XFA
-  if (!pAnnotHandler->Annot_OnSetFocus(pAnnot, 0))
+
+  if (!CPDFSDK_Annot::OnSetFocus(pAnnot, {}))
     return false;
   if (m_pFocusAnnot)
     return false;
 
-  m_pFocusAnnot.Reset(pAnnot->Get());
+  m_pFocusAnnot.Reset(pAnnot.Get());
+
+  // If we are not able to inform the client about the focus change, it
+  // shouldn't be considered as failure.
+  SendOnFocusChange(pAnnot);
   return true;
 }
 
-bool CPDFSDK_FormFillEnvironment::KillFocusAnnot(uint32_t nFlag) {
+bool CPDFSDK_FormFillEnvironment::KillFocusAnnot(Mask<FWL_EVENTFLAG> nFlags) {
   if (!m_pFocusAnnot)
     return false;
 
-  CPDFSDK_AnnotHandlerMgr* pAnnotHandler = GetAnnotHandlerMgr();
   ObservedPtr<CPDFSDK_Annot> pFocusAnnot(m_pFocusAnnot.Get());
   m_pFocusAnnot.Reset();
 
-#ifdef PDF_ENABLE_XFA
-  ObservedPtr<CPDFSDK_Annot> pNull;
-  if (!pAnnotHandler->Annot_OnChangeFocus(&pNull, &pFocusAnnot))
-    return false;
-#endif  // PDF_ENABLE_XFA
-
-  if (!pAnnotHandler->Annot_OnKillFocus(&pFocusAnnot, nFlag)) {
+  if (!CPDFSDK_Annot::OnKillFocus(pFocusAnnot, nFlags)) {
     m_pFocusAnnot.Reset(pFocusAnnot.Get());
     return false;
   }
 
+  // Might have been destroyed by OnKillFocus().
+  if (!pFocusAnnot)
+    return false;
+
   if (pFocusAnnot->GetAnnotSubtype() == CPDF_Annot::Subtype::WIDGET) {
     CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pFocusAnnot.Get());
     FormFieldType fieldType = pWidget->GetFieldType();
     if (fieldType == FormFieldType::kTextField ||
         fieldType == FormFieldType::kComboBox) {
-      OnSetFieldInputFocus(nullptr, 0, false);
+      OnSetFieldInputFocusInternal(WideString(), false);
     }
   }
   return !m_pFocusAnnot;
@@ -744,6 +801,401 @@
   return pExtension ? pExtension->GetPageCount() : m_pCPDFDoc->GetPageCount();
 }
 
-bool CPDFSDK_FormFillEnvironment::GetPermissions(int nFlag) const {
-  return !!(m_pCPDFDoc->GetUserPermissions() & nFlag);
+bool CPDFSDK_FormFillEnvironment::HasPermissions(uint32_t flags) const {
+  return !!(m_pCPDFDoc->GetUserPermissions() & flags);
+}
+
+void CPDFSDK_FormFillEnvironment::SendOnFocusChange(
+    ObservedPtr<CPDFSDK_Annot>& pAnnot) {
+  if (!m_pInfo || m_pInfo->version < 2 || !m_pInfo->FFI_OnFocusChange)
+    return;
+
+  // TODO(crbug.com/pdfium/1482): Handle XFA case.
+  if (pAnnot->AsXFAWidget())
+    return;
+
+  CPDFSDK_PageView* pPageView = pAnnot->GetPageView();
+  if (!pPageView->IsValid())
+    return;
+
+  IPDF_Page* page = pAnnot->GetPage();
+  if (!page)
+    return;
+
+  RetainPtr<CPDF_Dictionary> annot_dict =
+      pAnnot->GetPDFAnnot()->GetMutableAnnotDict();
+  auto focused_annot = std::make_unique<CPDF_AnnotContext>(annot_dict, page);
+  FPDF_ANNOTATION fpdf_annot =
+      FPDFAnnotationFromCPDFAnnotContext(focused_annot.get());
+
+  m_pInfo->FFI_OnFocusChange(m_pInfo, fpdf_annot, pPageView->GetPageIndex());
+}
+
+bool CPDFSDK_FormFillEnvironment::DoActionDocOpen(const CPDF_Action& action) {
+  std::set<const CPDF_Dictionary*> visited;
+  return ExecuteDocumentOpenAction(action, &visited);
+}
+
+bool CPDFSDK_FormFillEnvironment::DoActionJavaScript(
+    const CPDF_Action& JsAction,
+    WideString csJSName) {
+  if (JsAction.GetType() == CPDF_Action::Type::kJavaScript) {
+    WideString swJS = JsAction.GetJavaScript();
+    if (!swJS.IsEmpty()) {
+      RunDocumentOpenJavaScript(csJSName, swJS);
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool CPDFSDK_FormFillEnvironment::DoActionFieldJavaScript(
+    const CPDF_Action& JsAction,
+    CPDF_AAction::AActionType type,
+    CPDF_FormField* pFormField,
+    CFFL_FieldAction* data) {
+  if (IsJSPlatformPresent() &&
+      JsAction.GetType() == CPDF_Action::Type::kJavaScript) {
+    WideString swJS = JsAction.GetJavaScript();
+    if (!swJS.IsEmpty()) {
+      RunFieldJavaScript(pFormField, type, data, swJS);
+      return true;
+    }
+  }
+  return false;
+}
+
+bool CPDFSDK_FormFillEnvironment::DoActionLink(const CPDF_Action& action,
+                                               CPDF_AAction::AActionType type,
+                                               Mask<FWL_EVENTFLAG> modifiers) {
+  if (!CPDF_AAction::IsUserInput(type))
+    return false;
+
+  switch (action.GetType()) {
+    case CPDF_Action::Type::kGoTo:
+      DoActionGoTo(action);
+      return true;
+    case CPDF_Action::Type::kURI:
+      DoActionURI(action, modifiers);
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool CPDFSDK_FormFillEnvironment::DoActionDestination(const CPDF_Dest& dest) {
+  CPDF_Document* document = GetPDFDocument();
+  DCHECK(document);
+
+  std::vector<float> positions = dest.GetScrollPositionArray();
+  DoGoToAction(dest.GetDestPageIndex(document), dest.GetZoomMode(), positions);
+  return true;
+}
+
+bool CPDFSDK_FormFillEnvironment::DoActionPage(
+    const CPDF_Action& action,
+    CPDF_AAction::AActionType eType) {
+  std::set<const CPDF_Dictionary*> visited;
+  return ExecuteDocumentPageAction(action, eType, &visited);
+}
+
+bool CPDFSDK_FormFillEnvironment::DoActionDocument(
+    const CPDF_Action& action,
+    CPDF_AAction::AActionType eType) {
+  std::set<const CPDF_Dictionary*> visited;
+  return ExecuteDocumentPageAction(action, eType, &visited);
+}
+
+bool CPDFSDK_FormFillEnvironment::DoActionField(const CPDF_Action& action,
+                                                CPDF_AAction::AActionType type,
+                                                CPDF_FormField* pFormField,
+                                                CFFL_FieldAction* data) {
+  std::set<const CPDF_Dictionary*> visited;
+  return ExecuteFieldAction(action, type, pFormField, data, &visited);
+}
+
+bool CPDFSDK_FormFillEnvironment::ExecuteDocumentOpenAction(
+    const CPDF_Action& action,
+    std::set<const CPDF_Dictionary*>* visited) {
+  const CPDF_Dictionary* pDict = action.GetDict();
+  if (pdfium::Contains(*visited, pDict))
+    return false;
+
+  visited->insert(pDict);
+
+  if (action.GetType() == CPDF_Action::Type::kJavaScript) {
+    if (IsJSPlatformPresent()) {
+      WideString swJS = action.GetJavaScript();
+      if (!swJS.IsEmpty())
+        RunDocumentOpenJavaScript(WideString(), swJS);
+    }
+  } else {
+    DoActionNoJs(action, CPDF_AAction::AActionType::kDocumentOpen);
+  }
+
+  for (size_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) {
+    CPDF_Action subaction = action.GetSubAction(i);
+    if (!ExecuteDocumentOpenAction(subaction, visited))
+      return false;
+  }
+
+  return true;
+}
+
+bool CPDFSDK_FormFillEnvironment::ExecuteDocumentPageAction(
+    const CPDF_Action& action,
+    CPDF_AAction::AActionType type,
+    std::set<const CPDF_Dictionary*>* visited) {
+  const CPDF_Dictionary* pDict = action.GetDict();
+  if (pdfium::Contains(*visited, pDict))
+    return false;
+
+  visited->insert(pDict);
+
+  if (action.GetType() == CPDF_Action::Type::kJavaScript) {
+    if (IsJSPlatformPresent()) {
+      WideString swJS = action.GetJavaScript();
+      if (!swJS.IsEmpty())
+        RunDocumentPageJavaScript(type, swJS);
+    }
+  } else {
+    DoActionNoJs(action, type);
+  }
+
+  for (size_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) {
+    CPDF_Action subaction = action.GetSubAction(i);
+    if (!ExecuteDocumentPageAction(subaction, type, visited))
+      return false;
+  }
+
+  return true;
+}
+
+bool CPDFSDK_FormFillEnvironment::IsValidField(
+    const CPDF_Dictionary* pFieldDict) {
+  DCHECK(pFieldDict);
+
+  CPDFSDK_InteractiveForm* pForm = GetInteractiveForm();
+  CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
+  return !!pPDFForm->GetFieldByDict(pFieldDict);
+}
+
+bool CPDFSDK_FormFillEnvironment::ExecuteFieldAction(
+    const CPDF_Action& action,
+    CPDF_AAction::AActionType type,
+    CPDF_FormField* pFormField,
+    CFFL_FieldAction* data,
+    std::set<const CPDF_Dictionary*>* visited) {
+  const CPDF_Dictionary* pDict = action.GetDict();
+  if (pdfium::Contains(*visited, pDict))
+    return false;
+
+  visited->insert(pDict);
+
+  if (action.GetType() == CPDF_Action::Type::kJavaScript) {
+    if (IsJSPlatformPresent()) {
+      WideString swJS = action.GetJavaScript();
+      if (!swJS.IsEmpty()) {
+        RunFieldJavaScript(pFormField, type, data, swJS);
+        if (!IsValidField(pFormField->GetFieldDict()))
+          return false;
+      }
+    }
+  } else {
+    DoActionNoJs(action, type);
+  }
+
+  for (size_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) {
+    CPDF_Action subaction = action.GetSubAction(i);
+    if (!ExecuteFieldAction(subaction, type, pFormField, data, visited))
+      return false;
+  }
+
+  return true;
+}
+
+void CPDFSDK_FormFillEnvironment::DoActionNoJs(const CPDF_Action& action,
+                                               CPDF_AAction::AActionType type) {
+  switch (action.GetType()) {
+    case CPDF_Action::Type::kGoTo:
+      DoActionGoTo(action);
+      break;
+    case CPDF_Action::Type::kURI:
+      if (CPDF_AAction::IsUserInput(type))
+        DoActionURI(action, Mask<FWL_EVENTFLAG>{});
+      break;
+    case CPDF_Action::Type::kHide:
+      DoActionHide(action);
+      break;
+    case CPDF_Action::Type::kNamed:
+      DoActionNamed(action);
+      break;
+    case CPDF_Action::Type::kSubmitForm:
+      if (CPDF_AAction::IsUserInput(type))
+        DoActionSubmitForm(action);
+      break;
+    case CPDF_Action::Type::kResetForm:
+      DoActionResetForm(action);
+      break;
+    case CPDF_Action::Type::kJavaScript:
+      NOTREACHED_NORETURN();
+    case CPDF_Action::Type::kSetOCGState:
+    case CPDF_Action::Type::kThread:
+    case CPDF_Action::Type::kSound:
+    case CPDF_Action::Type::kMovie:
+    case CPDF_Action::Type::kRendition:
+    case CPDF_Action::Type::kTrans:
+    case CPDF_Action::Type::kGoTo3DView:
+    case CPDF_Action::Type::kGoToR:
+    case CPDF_Action::Type::kGoToE:
+    case CPDF_Action::Type::kLaunch:
+    case CPDF_Action::Type::kImportData:
+      // Unimplemented
+      break;
+    default:
+      break;
+  }
+}
+
+void CPDFSDK_FormFillEnvironment::DoActionGoTo(const CPDF_Action& action) {
+  DCHECK(action.GetDict());
+
+  CPDF_Document* pPDFDocument = GetPDFDocument();
+  DCHECK(pPDFDocument);
+
+  CPDF_Dest MyDest = action.GetDest(pPDFDocument);
+  DoActionDestination(MyDest);
+}
+
+void CPDFSDK_FormFillEnvironment::DoActionURI(const CPDF_Action& action,
+                                              Mask<FWL_EVENTFLAG> modifiers) {
+  DCHECK(action.GetDict());
+  DoURIAction(action.GetURI(GetPDFDocument()), modifiers);
+}
+
+void CPDFSDK_FormFillEnvironment::DoActionNamed(const CPDF_Action& action) {
+  DCHECK(action.GetDict());
+  ExecuteNamedAction(action.GetNamedAction());
+}
+
+void CPDFSDK_FormFillEnvironment::RunFieldJavaScript(
+    CPDF_FormField* pFormField,
+    CPDF_AAction::AActionType type,
+    CFFL_FieldAction* data,
+    const WideString& script) {
+  DCHECK(type != CPDF_AAction::kCalculate);
+  DCHECK(type != CPDF_AAction::kFormat);
+
+  RunScript(script, [type, data, pFormField](IJS_EventContext* context) {
+    switch (type) {
+      case CPDF_AAction::kCursorEnter:
+        context->OnField_MouseEnter(data->bModifier, data->bShift, pFormField);
+        break;
+      case CPDF_AAction::kCursorExit:
+        context->OnField_MouseExit(data->bModifier, data->bShift, pFormField);
+        break;
+      case CPDF_AAction::kButtonDown:
+        context->OnField_MouseDown(data->bModifier, data->bShift, pFormField);
+        break;
+      case CPDF_AAction::kButtonUp:
+        context->OnField_MouseUp(data->bModifier, data->bShift, pFormField);
+        break;
+      case CPDF_AAction::kGetFocus:
+        context->OnField_Focus(data->bModifier, data->bShift, pFormField,
+                               &data->sValue);
+        break;
+      case CPDF_AAction::kLoseFocus:
+        context->OnField_Blur(data->bModifier, data->bShift, pFormField,
+                              &data->sValue);
+        break;
+      case CPDF_AAction::kKeyStroke:
+        context->OnField_Keystroke(
+            &data->sChange, data->sChangeEx, data->bKeyDown, data->bModifier,
+            &data->nSelEnd, &data->nSelStart, data->bShift, pFormField,
+            &data->sValue, data->bWillCommit, data->bFieldFull, &data->bRC);
+        break;
+      case CPDF_AAction::kValidate:
+        context->OnField_Validate(&data->sChange, data->sChangeEx,
+                                  data->bKeyDown, data->bModifier, data->bShift,
+                                  pFormField, &data->sValue, &data->bRC);
+        break;
+      default:
+        NOTREACHED_NORETURN();
+    }
+  });
+}
+
+void CPDFSDK_FormFillEnvironment::RunDocumentOpenJavaScript(
+    const WideString& sScriptName,
+    const WideString& script) {
+  RunScript(script, [sScriptName](IJS_EventContext* context) {
+    context->OnDoc_Open(sScriptName);
+  });
+}
+
+void CPDFSDK_FormFillEnvironment::RunDocumentPageJavaScript(
+    CPDF_AAction::AActionType type,
+    const WideString& script) {
+  RunScript(script, [type](IJS_EventContext* context) {
+    switch (type) {
+      case CPDF_AAction::kOpenPage:
+        context->OnPage_Open();
+        break;
+      case CPDF_AAction::kClosePage:
+        context->OnPage_Close();
+        break;
+      case CPDF_AAction::kCloseDocument:
+        context->OnDoc_WillClose();
+        break;
+      case CPDF_AAction::kSaveDocument:
+        context->OnDoc_WillSave();
+        break;
+      case CPDF_AAction::kDocumentSaved:
+        context->OnDoc_DidSave();
+        break;
+      case CPDF_AAction::kPrintDocument:
+        context->OnDoc_WillPrint();
+        break;
+      case CPDF_AAction::kDocumentPrinted:
+        context->OnDoc_DidPrint();
+        break;
+      case CPDF_AAction::kPageVisible:
+        context->OnPage_InView();
+        break;
+      case CPDF_AAction::kPageInvisible:
+        context->OnPage_OutView();
+        break;
+      default:
+        NOTREACHED_NORETURN();
+    }
+  });
+}
+
+bool CPDFSDK_FormFillEnvironment::DoActionHide(const CPDF_Action& action) {
+  CPDFSDK_InteractiveForm* pForm = GetInteractiveForm();
+  if (pForm->DoAction_Hide(action)) {
+    SetChangeMark();
+    return true;
+  }
+  return false;
+}
+
+bool CPDFSDK_FormFillEnvironment::DoActionSubmitForm(
+    const CPDF_Action& action) {
+  CPDFSDK_InteractiveForm* pForm = GetInteractiveForm();
+  return pForm->DoAction_SubmitForm(action);
+}
+
+void CPDFSDK_FormFillEnvironment::DoActionResetForm(const CPDF_Action& action) {
+  CPDFSDK_InteractiveForm* pForm = GetInteractiveForm();
+  pForm->DoAction_ResetForm(action);
+}
+
+void CPDFSDK_FormFillEnvironment::RunScript(const WideString& script,
+                                            const RunScriptCallback& cb) {
+  IJS_Runtime::ScopedEventContext pContext(GetIJSRuntime());
+  cb(pContext.Get());
+  pContext->RunScript(script);
+  // TODO(dsinclair): Return error if RunScript returns a IJS_Runtime::JS_Error.
 }
diff --git a/fpdfsdk/cpdfsdk_formfillenvironment.h b/fpdfsdk/cpdfsdk_formfillenvironment.h
index ea67394..3be1962 100644
--- a/fpdfsdk/cpdfsdk_formfillenvironment.h
+++ b/fpdfsdk/cpdfsdk_formfillenvironment.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,24 +7,37 @@
 #ifndef FPDFSDK_CPDFSDK_FORMFILLENVIRONMENT_H_
 #define FPDFSDK_CPDFSDK_FORMFILLENVIRONMENT_H_
 
+#include <stdint.h>
+
 #include <map>
 #include <memory>
+#include <set>
+#include <utility>
+#include <vector>
 
 #include "core/fpdfapi/page/cpdf_occontext.h"
-#include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfdoc/cpdf_aaction.h"
+#include "core/fxcrt/cfx_timer.h"
+#include "core/fxcrt/mask.h"
 #include "core/fxcrt/observed_ptr.h"
-#include "core/fxcrt/timerhandler_iface.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
-#include "fpdfsdk/pwl/ipwl_systemhandler.h"
+#include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
+#include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "fpdfsdk/pwl/ipwl_fillernotify.h"
 #include "public/fpdf_formfill.h"
+#include "third_party/base/containers/span.h"
 
-class CFFL_InteractiveFormFiller;
-class CPDFSDK_ActionHandler;
-class CPDFSDK_AnnotHandlerMgr;
+class CPDF_Action;
+class CPDF_FormField;
 class CPDFSDK_InteractiveForm;
 class CPDFSDK_PageView;
+class IJS_EventContext;
 class IJS_Runtime;
+class IPDF_Page;
+struct CFFL_FieldAction;
 
 // NOTE: |bsUTF16LE| must outlive the use of the result. Care must be taken
 // since modifying the result would impact |bsUTF16LE|.
@@ -41,14 +54,11 @@
 // hierarcy back to the form fill environment itself, so as to flag any
 // lingering lifetime issues via the memory tools.
 
-class CPDFSDK_FormFillEnvironment final : public Observable,
-                                          public TimerHandlerIface,
-                                          public IPWL_SystemHandler {
+class CPDFSDK_FormFillEnvironment final
+    : public CFX_Timer::HandlerIface,
+      public CFFL_InteractiveFormFiller::CallbackIface {
  public:
-  CPDFSDK_FormFillEnvironment(
-      CPDF_Document* pDoc,
-      FPDF_FORMFILLINFO* pFFinfo,
-      std::unique_ptr<CPDFSDK_AnnotHandlerMgr> pHandlerMgr);
+  CPDFSDK_FormFillEnvironment(CPDF_Document* pDoc, FPDF_FORMFILLINFO* pFFinfo);
 
   ~CPDFSDK_FormFillEnvironment() override;
 
@@ -56,27 +66,33 @@
   int32_t SetTimer(int32_t uElapse, TimerCallback lpTimerFunc) override;
   void KillTimer(int32_t nTimerID) override;
 
-  // IPWL_SystemHandler:
-  void InvalidateRect(PerWindowData* pWidgetData,
+  // CFFL_InteractiveFormFiller::CallbackIface:
+  void InvalidateRect(CPDFSDK_Widget* widget,
                       const CFX_FloatRect& rect) override;
-  void OutputSelectedRect(CFFL_FormFiller* pFormFiller,
+  void OutputSelectedRect(CFFL_FormField* pFormField,
                           const CFX_FloatRect& rect) override;
   bool IsSelectionImplemented() const override;
-  void SetCursor(int32_t nCursorType) override;
+  void SetCursor(IPWL_FillerNotify::CursorStyle nCursorType) override;
+  void OnSetFieldInputFocus(const WideString& text) override;
+  void OnCalculate(ObservedPtr<CPDFSDK_Annot>& pAnnot) override;
+  void OnFormat(ObservedPtr<CPDFSDK_Annot>& pAnnot) override;
+  void Invalidate(IPDF_Page* page, const FX_RECT& rect) override;
+  CPDFSDK_PageView* GetOrCreatePageView(IPDF_Page* pUnderlyingPage) override;
+  CPDFSDK_PageView* GetPageView(IPDF_Page* pUnderlyingPage) override;
+  CFX_Timer::HandlerIface* GetTimerHandler() override;
+  CPDFSDK_Annot* GetFocusAnnot() const override;
+  bool SetFocusAnnot(ObservedPtr<CPDFSDK_Annot>& pAnnot) override;
+  bool HasPermissions(uint32_t flags) const override;
+  void OnChange() override;
 
-  CPDFSDK_PageView* GetPageView(IPDF_Page* pUnderlyingPage, bool renew);
-  CPDFSDK_PageView* GetPageView(int nIndex);
-
+  CPDFSDK_PageView* GetPageViewAtIndex(int nIndex);
   void RemovePageView(IPDF_Page* pUnderlyingPage);
-  void UpdateAllViews(CPDFSDK_PageView* pSender, CPDFSDK_Annot* pAnnot);
+  void UpdateAllViews(CPDFSDK_Annot* pAnnot);
 
-  CPDFSDK_Annot* GetFocusAnnot() const { return m_pFocusAnnot.Get(); }
-  bool SetFocusAnnot(ObservedPtr<CPDFSDK_Annot>* pAnnot);
-  bool KillFocusAnnot(uint32_t nFlag);
+  bool KillFocusAnnot(Mask<FWL_EVENTFLAG> nFlags);
   void ClearAllFocusedAnnots();
 
   int GetPageCount() const;
-  bool GetPermissions(int nFlag) const;
 
   bool GetChangeMark() const { return m_bChangeMask; }
   void SetChangeMark() { m_bChangeMask = true; }
@@ -84,29 +100,53 @@
 
   void ProcJavascriptAction();
   bool ProcOpenAction();
-  void Invalidate(IPDF_Page* page, const FX_RECT& rect);
 
-  void OnChange();
-  void ExecuteNamedAction(const char* namedAction);
-  void OnSetFieldInputFocus(FPDF_WIDESTRING focusText,
-                            FPDF_DWORD nTextLen,
-                            bool bFocus);
-  void DoURIAction(const char* bsURI);
+  void ExecuteNamedAction(const ByteString& namedAction);
+  void DoURIAction(const ByteString& bsURI, Mask<FWL_EVENTFLAG> modifiers);
   void DoGoToAction(int nPageIndex,
                     int zoomMode,
-                    float* fPosArray,
-                    int sizeOfArray);
+                    pdfium::span<float> fPosArray);
 
-  CPDF_Document* GetPDFDocument() const { return m_pCPDFDoc.Get(); }
+  CPDF_Document* GetPDFDocument() const { return m_pCPDFDoc; }
   CPDF_Document::Extension* GetDocExtension() const {
     return m_pCPDFDoc->GetExtension();
   }
 
   bool IsJSPlatformPresent() const { return m_pInfo && m_pInfo->m_pJsPlatform; }
+  IPDF_JSPLATFORM* GetJSPlatform() const {
+    return m_pInfo ? m_pInfo->m_pJsPlatform : nullptr;
+  }
+
+  // Actions.
+  bool DoActionDocOpen(const CPDF_Action& action);
+  bool DoActionJavaScript(const CPDF_Action& JsAction, WideString csJSName);
+  bool DoActionPage(const CPDF_Action& action, CPDF_AAction::AActionType eType);
+  bool DoActionDocument(const CPDF_Action& action,
+                        CPDF_AAction::AActionType eType);
+  bool DoActionField(const CPDF_Action& action,
+                     CPDF_AAction::AActionType type,
+                     CPDF_FormField* pFormField,
+                     CFFL_FieldAction* data);
+  bool DoActionFieldJavaScript(const CPDF_Action& JsAction,
+                               CPDF_AAction::AActionType type,
+                               CPDF_FormField* pFormField,
+                               CFFL_FieldAction* data);
+  bool DoActionLink(const CPDF_Action& action,
+                    CPDF_AAction::AActionType type,
+                    Mask<FWL_EVENTFLAG> modifiers);
+  bool DoActionDestination(const CPDF_Dest& dest);
+  void DoActionNoJs(const CPDF_Action& action, CPDF_AAction::AActionType type);
+  void DoActionGoTo(const CPDF_Action& action);
+  void DoActionLaunch(const CPDF_Action& action);
+  void DoActionURI(const CPDF_Action& action, Mask<FWL_EVENTFLAG> modifiers);
+  void DoActionNamed(const CPDF_Action& action);
+  bool DoActionHide(const CPDF_Action& action);
+  bool DoActionSubmitForm(const CPDF_Action& action);
+  void DoActionResetForm(const CPDF_Action& action);
 
 #ifdef PDF_ENABLE_V8
   CPDFSDK_PageView* GetCurrentView();
-  FPDF_PAGE GetCurrentPage() const;
+  IPDF_Page* GetCurrentPage() const;
 
   WideString GetLanguage();
   WideString GetPlatform();
@@ -120,12 +160,10 @@
                      const WideString& Default,
                      const WideString& cLabel,
                      FPDF_BOOL bPassword,
-                     void* response,
-                     int length);
+                     pdfium::span<uint8_t> response);
   void JS_appBeep(int nType);
   WideString JS_fieldBrowse();
-  void JS_docmailForm(void* mailData,
-                      int length,
+  void JS_docmailForm(pdfium::span<const uint8_t> mailData,
                       FPDF_BOOL bUI,
                       const WideString& To,
                       const WideString& Subject,
@@ -159,10 +197,7 @@
 
   void GotoURL(const WideString& wsURL);
   FS_RECTF GetPageViewRect(IPDF_Page* page);
-  bool PopupMenu(IPDF_Page* page,
-                 FPDF_WIDGET hWidget,
-                 int menuFlag,
-                 const CFX_PointF& pt);
+  bool PopupMenu(IPDF_Page* page, int menuFlag, const CFX_PointF& pt);
   void EmailTo(FPDF_FILEHANDLER* fileHandler,
                FPDF_WIDESTRING pTo,
                FPDF_WIDESTRING pSubject,
@@ -191,33 +226,71 @@
 
   WideString GetFilePath() const;
   ByteString GetAppName() const { return ByteString(); }
-  TimerHandlerIface* GetTimerHandler() { return this; }
-  IPWL_SystemHandler* GetSysHandler() { return this; }
   FPDF_FORMFILLINFO* GetFormFillInfo() const { return m_pInfo; }
-  void SubmitForm(pdfium::span<uint8_t> form_data, const WideString& URL);
+  void SubmitForm(pdfium::span<const uint8_t> form_data, const WideString& URL);
 
-  CPDFSDK_AnnotHandlerMgr* GetAnnotHandlerMgr();  // Always present.
+  void SetFocusableAnnotSubtypes(
+      const std::vector<CPDF_Annot::Subtype>& focusableAnnotTypes) {
+    m_FocusableAnnotTypes = focusableAnnotTypes;
+  }
+  const std::vector<CPDF_Annot::Subtype>& GetFocusableAnnotSubtypes() const {
+    return m_FocusableAnnotTypes;
+  }
 
-  // Creates if not present.
-  CFFL_InteractiveFormFiller* GetInteractiveFormFiller();
+  // Never returns null.
+  CFFL_InteractiveFormFiller* GetInteractiveFormFiller() {
+    return m_pInteractiveFormFiller.get();
+  }
+
   IJS_Runtime* GetIJSRuntime();                   // Creates if not present.
-  CPDFSDK_ActionHandler* GetActionHandler();      // Creates if not present.
   CPDFSDK_InteractiveForm* GetInteractiveForm();  // Creates if not present.
 
  private:
-  IPDF_Page* GetPage(int nIndex);
+  using RunScriptCallback = std::function<void(IJS_EventContext* context)>;
 
-  FPDF_FORMFILLINFO* const m_pInfo;
-  std::unique_ptr<CPDFSDK_ActionHandler> m_pActionHandler;
+  IPDF_Page* GetPage(int nIndex) const;
+  void OnSetFieldInputFocusInternal(const WideString& text, bool bFocus);
+  void SendOnFocusChange(ObservedPtr<CPDFSDK_Annot>& pAnnot);
+
+  // Support methods for Actions.
+  void RunScript(const WideString& script, const RunScriptCallback& cb);
+  bool ExecuteDocumentOpenAction(const CPDF_Action& action,
+                                 std::set<const CPDF_Dictionary*>* visited);
+  bool ExecuteDocumentPageAction(const CPDF_Action& action,
+                                 CPDF_AAction::AActionType type,
+                                 std::set<const CPDF_Dictionary*>* visited);
+  bool ExecuteFieldAction(const CPDF_Action& action,
+                          CPDF_AAction::AActionType type,
+                          CPDF_FormField* pFormField,
+                          CFFL_FieldAction* data,
+                          std::set<const CPDF_Dictionary*>* visited);
+  void RunDocumentPageJavaScript(CPDF_AAction::AActionType type,
+                                 const WideString& script);
+  void RunDocumentOpenJavaScript(const WideString& sScriptName,
+                                 const WideString& script);
+  void RunFieldJavaScript(CPDF_FormField* pFormField,
+                          CPDF_AAction::AActionType type,
+                          CFFL_FieldAction* data,
+                          const WideString& script);
+  bool IsValidField(const CPDF_Dictionary* pFieldDict);
+
+  UnownedPtr<FPDF_FORMFILLINFO> const m_pInfo;
   std::unique_ptr<IJS_Runtime> m_pIJSRuntime;
+
+  // Iterator stability guarantees as provided by std::map<> required.
   std::map<IPDF_Page*, std::unique_ptr<CPDFSDK_PageView>> m_PageMap;
+
   std::unique_ptr<CPDFSDK_InteractiveForm> m_pInteractiveForm;
   ObservedPtr<CPDFSDK_Annot> m_pFocusAnnot;
   UnownedPtr<CPDF_Document> const m_pCPDFDoc;
-  std::unique_ptr<CPDFSDK_AnnotHandlerMgr> m_pAnnotHandlerMgr;
-  std::unique_ptr<CFFL_InteractiveFormFiller> m_pFormFiller;
+  std::unique_ptr<CFFL_InteractiveFormFiller> m_pInteractiveFormFiller;
   bool m_bChangeMask = false;
   bool m_bBeingDestroyed = false;
+
+  // Holds the list of focusable annot types.
+  // Annotations of type WIDGET are by default focusable.
+  std::vector<CPDF_Annot::Subtype> m_FocusableAnnotTypes = {
+      CPDF_Annot::Subtype::WIDGET};
 };
 
 #endif  // FPDFSDK_CPDFSDK_FORMFILLENVIRONMENT_H_
diff --git a/fpdfsdk/cpdfsdk_helpers.cpp b/fpdfsdk/cpdfsdk_helpers.cpp
index 144149d..c548420 100644
--- a/fpdfsdk/cpdfsdk_helpers.cpp
+++ b/fpdfsdk/cpdfsdk_helpers.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include "fpdfsdk/cpdfsdk_helpers.h"
 
+#include <utility>
+
 #include "build/build_config.h"
 #include "constants/form_fields.h"
 #include "constants/stream_dict_common.h"
@@ -14,10 +16,15 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fpdfdoc/cpdf_annot.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "core/fpdfdoc/cpdf_metadata.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "third_party/base/check.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
 
@@ -37,43 +44,48 @@
   return true;
 }
 
-unsigned long GetStreamMaybeCopyAndReturnLengthImpl(const CPDF_Stream* stream,
-                                                    void* buffer,
-                                                    unsigned long buflen,
-                                                    bool decode) {
-  ASSERT(stream);
-  auto stream_acc = pdfium::MakeRetain<CPDF_StreamAcc>(stream);
+// Use the existence of the XFA array as a signal for XFA forms.
+bool DocHasXFA(const CPDF_Document* doc) {
+  const CPDF_Dictionary* root = doc->GetRoot();
+  if (!root)
+    return false;
 
+  RetainPtr<const CPDF_Dictionary> form = root->GetDictFor("AcroForm");
+  return form && form->GetArrayFor("XFA");
+}
+
+unsigned long GetStreamMaybeCopyAndReturnLengthImpl(
+    RetainPtr<const CPDF_Stream> stream,
+    pdfium::span<uint8_t> buffer,
+    bool decode) {
+  DCHECK(stream);
+  auto stream_acc = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(stream));
   if (decode)
     stream_acc->LoadAllDataFiltered();
   else
     stream_acc->LoadAllDataRaw();
 
-  const auto stream_data_size = stream_acc->GetSize();
-  if (!buffer || buflen < stream_data_size)
-    return stream_data_size;
+  pdfium::span<const uint8_t> stream_data_span = stream_acc->GetSpan();
+  if (!buffer.empty() && buffer.size() <= stream_data_span.size())
+    fxcrt::spancpy(buffer, stream_data_span);
 
-  memcpy(buffer, stream_acc->GetData(), stream_data_size);
-  return stream_data_size;
+  return pdfium::base::checked_cast<unsigned long>(stream_data_span.size());
 }
 
 #ifdef PDF_ENABLE_XFA
 class FPDF_FileHandlerContext final : public IFX_SeekableStream {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // IFX_SeekableStream:
   FX_FILESIZE GetSize() override;
-  bool IsEOF() override;
   FX_FILESIZE GetPosition() override;
-  bool ReadBlockAtOffset(void* buffer,
-                         FX_FILESIZE offset,
-                         size_t size) override;
-  size_t ReadBlock(void* buffer, size_t size) override;
-  bool WriteBlockAtOffset(const void* buffer,
-                          FX_FILESIZE offset,
-                          size_t size) override;
+  bool IsEOF() override;
+  size_t ReadBlock(pdfium::span<uint8_t> buffer) override;
+  bool ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                         FX_FILESIZE offset) override;
+  bool WriteBlockAtOffset(pdfium::span<const uint8_t> buffer,
+                          FX_FILESIZE offset) override;
   bool Flush() override;
 
   void SetPosition(FX_FILESIZE pos) { m_nCurPos = pos; }
@@ -82,14 +94,12 @@
   explicit FPDF_FileHandlerContext(FPDF_FILEHANDLER* pFS);
   ~FPDF_FileHandlerContext() override;
 
-  FPDF_FILEHANDLER* m_pFS;
-  FX_FILESIZE m_nCurPos;
+  UnownedPtr<FPDF_FILEHANDLER> const m_pFS;
+  FX_FILESIZE m_nCurPos = 0;
 };
 
-FPDF_FileHandlerContext::FPDF_FileHandlerContext(FPDF_FILEHANDLER* pFS) {
-  m_pFS = pFS;
-  m_nCurPos = 0;
-}
+FPDF_FileHandlerContext::FPDF_FileHandlerContext(FPDF_FILEHANDLER* pFS)
+    : m_pFS(pFS) {}
 
 FPDF_FileHandlerContext::~FPDF_FileHandlerContext() {
   if (m_pFS && m_pFS->Release)
@@ -98,7 +108,7 @@
 
 FX_FILESIZE FPDF_FileHandlerContext::GetSize() {
   if (m_pFS && m_pFS->GetSize)
-    return (FX_FILESIZE)m_pFS->GetSize(m_pFS->clientData);
+    return static_cast<FX_FILESIZE>(m_pFS->GetSize(m_pFS->clientData));
   return 0;
 }
 
@@ -110,48 +120,50 @@
   return m_nCurPos;
 }
 
-bool FPDF_FileHandlerContext::ReadBlockAtOffset(void* buffer,
-                                                FX_FILESIZE offset,
-                                                size_t size) {
-  if (!buffer || !size || !m_pFS->ReadBlock)
+bool FPDF_FileHandlerContext::ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                                                FX_FILESIZE offset) {
+  if (buffer.empty() || !m_pFS->ReadBlock)
     return false;
 
-  if (m_pFS->ReadBlock(m_pFS->clientData, (FPDF_DWORD)offset, buffer,
-                       (FPDF_DWORD)size) == 0) {
-    m_nCurPos = offset + size;
+  if (m_pFS->ReadBlock(m_pFS->clientData, static_cast<FPDF_DWORD>(offset),
+                       buffer.data(),
+                       static_cast<FPDF_DWORD>(buffer.size())) == 0) {
+    m_nCurPos = offset + buffer.size();
     return true;
   }
   return false;
 }
 
-size_t FPDF_FileHandlerContext::ReadBlock(void* buffer, size_t size) {
-  if (!buffer || !size || !m_pFS->ReadBlock)
+size_t FPDF_FileHandlerContext::ReadBlock(pdfium::span<uint8_t> buffer) {
+  if (buffer.empty() || !m_pFS->ReadBlock)
     return 0;
 
   FX_FILESIZE nSize = GetSize();
   if (m_nCurPos >= nSize)
     return 0;
   FX_FILESIZE dwAvail = nSize - m_nCurPos;
-  if (dwAvail < (FX_FILESIZE)size)
-    size = static_cast<size_t>(dwAvail);
-  if (m_pFS->ReadBlock(m_pFS->clientData, (FPDF_DWORD)m_nCurPos, buffer,
-                       (FPDF_DWORD)size) == 0) {
-    m_nCurPos += size;
-    return size;
+  if (dwAvail < (FX_FILESIZE)buffer.size())
+    buffer = buffer.first(static_cast<size_t>(dwAvail));
+  if (m_pFS->ReadBlock(m_pFS->clientData, static_cast<FPDF_DWORD>(m_nCurPos),
+                       buffer.data(),
+                       static_cast<FPDF_DWORD>(buffer.size())) == 0) {
+    m_nCurPos += buffer.size();
+    return buffer.size();
   }
 
   return 0;
 }
 
-bool FPDF_FileHandlerContext::WriteBlockAtOffset(const void* buffer,
-                                                 FX_FILESIZE offset,
-                                                 size_t size) {
+bool FPDF_FileHandlerContext::WriteBlockAtOffset(
+    pdfium::span<const uint8_t> buffer,
+    FX_FILESIZE offset) {
   if (!m_pFS || !m_pFS->WriteBlock)
     return false;
 
-  if (m_pFS->WriteBlock(m_pFS->clientData, (FPDF_DWORD)offset, buffer,
-                        (FPDF_DWORD)size) == 0) {
-    m_nCurPos = offset + size;
+  if (m_pFS->WriteBlock(m_pFS->clientData, static_cast<FPDF_DWORD>(offset),
+                        buffer.data(),
+                        static_cast<FPDF_DWORD>(buffer.size())) == 0) {
+    m_nCurPos = offset + buffer.size();
     return true;
   }
   return false;
@@ -209,16 +221,18 @@
 }
 #endif  // PDF_ENABLE_XFA
 
-const CPDF_Array* GetQuadPointsArrayFromDictionary(
+RetainPtr<const CPDF_Array> GetQuadPointsArrayFromDictionary(
     const CPDF_Dictionary* dict) {
   return dict->GetArrayFor("QuadPoints");
 }
 
-CPDF_Array* GetQuadPointsArrayFromDictionary(CPDF_Dictionary* dict) {
-  return dict->GetArrayFor("QuadPoints");
+RetainPtr<CPDF_Array> GetMutableQuadPointsArrayFromDictionary(
+    CPDF_Dictionary* dict) {
+  return pdfium::WrapRetain(
+      const_cast<CPDF_Array*>(GetQuadPointsArrayFromDictionary(dict).Get()));
 }
 
-CPDF_Array* AddQuadPointsArrayToDictionary(CPDF_Dictionary* dict) {
+RetainPtr<CPDF_Array> AddQuadPointsArrayToDictionary(CPDF_Dictionary* dict) {
   return dict->SetNewFor<CPDF_Array>(kQuadPoints);
 }
 
@@ -226,24 +240,24 @@
   return array && index < array->size() / 8;
 }
 
-bool GetQuadPointsAtIndex(const CPDF_Array* array,
+bool GetQuadPointsAtIndex(RetainPtr<const CPDF_Array> array,
                           size_t quad_index,
                           FS_QUADPOINTSF* quad_points) {
-  ASSERT(quad_points);
-  ASSERT(array);
+  DCHECK(quad_points);
+  DCHECK(array);
 
   if (!IsValidQuadPointsIndex(array, quad_index))
     return false;
 
   quad_index *= 8;
-  quad_points->x1 = array->GetNumberAt(quad_index);
-  quad_points->y1 = array->GetNumberAt(quad_index + 1);
-  quad_points->x2 = array->GetNumberAt(quad_index + 2);
-  quad_points->y2 = array->GetNumberAt(quad_index + 3);
-  quad_points->x3 = array->GetNumberAt(quad_index + 4);
-  quad_points->y3 = array->GetNumberAt(quad_index + 5);
-  quad_points->x4 = array->GetNumberAt(quad_index + 6);
-  quad_points->y4 = array->GetNumberAt(quad_index + 7);
+  quad_points->x1 = array->GetFloatAt(quad_index);
+  quad_points->y1 = array->GetFloatAt(quad_index + 1);
+  quad_points->x2 = array->GetFloatAt(quad_index + 2);
+  quad_points->y2 = array->GetFloatAt(quad_index + 3);
+  quad_points->x3 = array->GetFloatAt(quad_index + 4);
+  quad_points->y3 = array->GetFloatAt(quad_index + 5);
+  quad_points->x4 = array->GetFloatAt(quad_index + 6);
+  quad_points->y4 = array->GetFloatAt(quad_index + 7);
   return true;
 }
 
@@ -267,27 +281,38 @@
   return {matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f};
 }
 
+unsigned long NulTerminateMaybeCopyAndReturnLength(const ByteString& text,
+                                                   void* buffer,
+                                                   unsigned long buflen) {
+  const unsigned long len =
+      pdfium::base::checked_cast<unsigned long>(text.GetLength() + 1);
+  if (buffer && len <= buflen)
+    memcpy(buffer, text.c_str(), len);
+  return len;
+}
+
 unsigned long Utf16EncodeMaybeCopyAndReturnLength(const WideString& text,
                                                   void* buffer,
                                                   unsigned long buflen) {
   ByteString encoded_text = text.ToUTF16LE();
-  unsigned long len = encoded_text.GetLength();
+  const unsigned long len =
+      pdfium::base::checked_cast<unsigned long>(encoded_text.GetLength());
   if (buffer && len <= buflen)
     memcpy(buffer, encoded_text.c_str(), len);
   return len;
 }
 
-unsigned long GetRawStreamMaybeCopyAndReturnLength(const CPDF_Stream* stream,
-                                                   void* buffer,
-                                                   unsigned long buflen) {
-  return GetStreamMaybeCopyAndReturnLengthImpl(stream, buffer, buflen,
+unsigned long GetRawStreamMaybeCopyAndReturnLength(
+    RetainPtr<const CPDF_Stream> stream,
+    pdfium::span<uint8_t> buffer) {
+  return GetStreamMaybeCopyAndReturnLengthImpl(std::move(stream), buffer,
                                                /*decode=*/false);
 }
 
-unsigned long DecodeStreamMaybeCopyAndReturnLength(const CPDF_Stream* stream,
-                                                   void* buffer,
-                                                   unsigned long buflen) {
-  return GetStreamMaybeCopyAndReturnLengthImpl(stream, buffer, buflen,
+unsigned long DecodeStreamMaybeCopyAndReturnLength(
+    RetainPtr<const CPDF_Stream> stream,
+    pdfium::span<uint8_t> buffer) {
+  return GetStreamMaybeCopyAndReturnLengthImpl(std::move(stream), buffer,
                                                /*decode=*/true);
 }
 
@@ -320,53 +345,47 @@
   g_unsupport_info = unsp_info;
 }
 
-UNSUPPORT_INFO* GetPDFUnssuportInto() {
-  return g_unsupport_info;
-}
-
-void ReportUnsupportedFeatures(CPDF_Document* pDoc) {
+void ReportUnsupportedFeatures(const CPDF_Document* pDoc) {
   const CPDF_Dictionary* pRootDict = pDoc->GetRoot();
-  if (pRootDict) {
-    // Portfolios and Packages
-    if (pRootDict->KeyExist("Collection")) {
-      RaiseUnsupportedError(FPDF_UNSP_DOC_PORTABLECOLLECTION);
-      return;
-    }
-    if (pRootDict->KeyExist("Names")) {
-      const CPDF_Dictionary* pNameDict = pRootDict->GetDictFor("Names");
-      if (pNameDict && pNameDict->KeyExist("EmbeddedFiles")) {
-        RaiseUnsupportedError(FPDF_UNSP_DOC_ATTACHMENT);
-        return;
-      }
-      if (pNameDict && pNameDict->KeyExist("JavaScript")) {
-        const CPDF_Dictionary* pJSDict = pNameDict->GetDictFor("JavaScript");
-        const CPDF_Array* pArray =
-            pJSDict ? pJSDict->GetArrayFor("Names") : nullptr;
-        if (pArray) {
-          for (size_t i = 0; i < pArray->size(); i++) {
-            ByteString cbStr = pArray->GetStringAt(i);
-            if (cbStr.Compare("com.adobe.acrobat.SharedReview.Register") == 0) {
-              RaiseUnsupportedError(FPDF_UNSP_DOC_SHAREDREVIEW);
-              return;
-            }
+  if (!pRootDict)
+    return;
+
+  // Portfolios and Packages
+  if (pRootDict->KeyExist("Collection"))
+    RaiseUnsupportedError(FPDF_UNSP_DOC_PORTABLECOLLECTION);
+
+  RetainPtr<const CPDF_Dictionary> pNameDict = pRootDict->GetDictFor("Names");
+  if (pNameDict) {
+    if (pNameDict->KeyExist("EmbeddedFiles"))
+      RaiseUnsupportedError(FPDF_UNSP_DOC_ATTACHMENT);
+
+    RetainPtr<const CPDF_Dictionary> pJSDict =
+        pNameDict->GetDictFor("JavaScript");
+    if (pJSDict) {
+      RetainPtr<const CPDF_Array> pArray = pJSDict->GetArrayFor("Names");
+      if (pArray) {
+        for (size_t i = 0; i < pArray->size(); i++) {
+          ByteString cbStr = pArray->GetByteStringAt(i);
+          if (cbStr == "com.adobe.acrobat.SharedReview.Register") {
+            RaiseUnsupportedError(FPDF_UNSP_DOC_SHAREDREVIEW);
+            break;
           }
         }
       }
     }
+  }
 
-    // SharedForm
-    const CPDF_Stream* pStream = pRootDict->GetStreamFor("Metadata");
-    if (pStream) {
-      CPDF_Metadata metaData(pStream);
-      for (const auto& err : metaData.CheckForSharedForm())
-        RaiseUnsupportedError(static_cast<int>(err));
-    }
+  // SharedForm
+  RetainPtr<const CPDF_Stream> pStream = pRootDict->GetStreamFor("Metadata");
+  if (pStream) {
+    CPDF_Metadata metadata(std::move(pStream));
+    for (const UnsupportedFeature& feature : metadata.CheckForSharedForm())
+      RaiseUnsupportedError(static_cast<int>(feature));
   }
 }
 
-void ReportUnsupportedXFA(CPDF_Document* pDoc) {
-  // XFA Forms
-  if (!pDoc->GetExtension() && CPDF_InteractiveForm(pDoc).HasXFAForm())
+void ReportUnsupportedXFA(const CPDF_Document* pDoc) {
+  if (!pDoc->GetExtension() && DocHasXFA(pDoc))
     RaiseUnsupportedError(FPDF_UNSP_DOC_XFAFORM);
 }
 
@@ -383,7 +402,7 @@
       break;
     case CPDF_Annot::Subtype::SCREEN: {
       const CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
-      ByteString cbString = pAnnotDict->GetStringFor("IT");
+      ByteString cbString = pAnnotDict->GetByteStringFor("IT");
       if (cbString != "Img")
         RaiseUnsupportedError(FPDF_UNSP_ANNOT_SCREEN_MEDIA);
       break;
@@ -396,7 +415,8 @@
       break;
     case CPDF_Annot::Subtype::WIDGET: {
       const CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
-      ByteString cbString = pAnnotDict->GetStringFor(pdfium::form_fields::kFT);
+      ByteString cbString =
+          pAnnotDict->GetByteStringFor(pdfium::form_fields::kFT);
       if (cbString == pdfium::form_fields::kSig)
         RaiseUnsupportedError(FPDF_UNSP_ANNOT_SIG);
       break;
@@ -428,3 +448,55 @@
   }
   FXSYS_SetLastError(err_code);
 }
+
+void SetColorFromScheme(const FPDF_COLORSCHEME* pColorScheme,
+                        CPDF_RenderOptions* pRenderOptions) {
+  CPDF_RenderOptions::ColorScheme color_scheme;
+  color_scheme.path_fill_color =
+      static_cast<FX_ARGB>(pColorScheme->path_fill_color);
+  color_scheme.path_stroke_color =
+      static_cast<FX_ARGB>(pColorScheme->path_stroke_color);
+  color_scheme.text_fill_color =
+      static_cast<FX_ARGB>(pColorScheme->text_fill_color);
+  color_scheme.text_stroke_color =
+      static_cast<FX_ARGB>(pColorScheme->text_stroke_color);
+  pRenderOptions->SetColorScheme(color_scheme);
+}
+
+std::vector<uint32_t> ParsePageRangeString(const ByteString& bsPageRange,
+                                           uint32_t nCount) {
+  ByteStringView alphabet(" 0123456789-,");
+  for (const auto& ch : bsPageRange) {
+    if (!alphabet.Contains(ch))
+      return std::vector<uint32_t>();
+  }
+
+  ByteString bsStrippedPageRange = bsPageRange;
+  bsStrippedPageRange.Remove(' ');
+
+  std::vector<uint32_t> results;
+  for (const auto& entry : fxcrt::Split(bsStrippedPageRange, ',')) {
+    std::vector<ByteString> args = fxcrt::Split(entry, '-');
+    if (args.size() == 1) {
+      uint32_t page_num =
+          pdfium::base::checked_cast<uint32_t>(atoi(args[0].c_str()));
+      if (page_num == 0 || page_num > nCount)
+        return std::vector<uint32_t>();
+      results.push_back(page_num - 1);
+    } else if (args.size() == 2) {
+      uint32_t first_num =
+          pdfium::base::checked_cast<uint32_t>(atoi(args[0].c_str()));
+      if (first_num == 0)
+        return std::vector<uint32_t>();
+      uint32_t last_num =
+          pdfium::base::checked_cast<uint32_t>(atoi(args[1].c_str()));
+      if (last_num == 0 || first_num > last_num || last_num > nCount)
+        return std::vector<uint32_t>();
+      for (uint32_t i = first_num; i <= last_num; ++i)
+        results.push_back(i - 1);
+    } else {
+      return std::vector<uint32_t>();
+    }
+  }
+  return results;
+}
diff --git a/fpdfsdk/cpdfsdk_helpers.h b/fpdfsdk/cpdfsdk_helpers.h
index 327ec4c..f66adbf 100644
--- a/fpdfsdk/cpdfsdk_helpers.h
+++ b/fpdfsdk/cpdfsdk_helpers.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,13 @@
 #ifndef FPDFSDK_CPDFSDK_HELPERS_H_
 #define FPDFSDK_CPDFSDK_HELPERS_H_
 
+#include <vector>
+
 #include "build/build_config.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/parser/cpdf_parser.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/cfx_path.h"
 #include "public/fpdf_doc.h"
 #include "public/fpdf_ext.h"
 #include "public/fpdfview.h"
@@ -19,11 +22,6 @@
 #include "core/fxcrt/fx_stream.h"
 #endif  // PDF_ENABLE_XFA
 
-#if defined(OS_WIN)
-#include <math.h>
-#include <tchar.h>
-#endif
-
 class CPDF_Annot;
 class CPDF_AnnotContext;
 class CPDF_ClipPath;
@@ -32,6 +30,7 @@
 class CPDF_Font;
 class CPDF_LinkExtract;
 class CPDF_PageObject;
+class CPDF_RenderOptions;
 class CPDF_Stream;
 class CPDF_StructElement;
 class CPDF_StructTree;
@@ -39,8 +38,9 @@
 class CPDF_TextPageFind;
 class CPDFSDK_FormFillEnvironment;
 class CPDFSDK_InteractiveForm;
-class FX_PATHPOINT;
+class CFX_DIBitmap;
 struct CPDF_JavaScript;
+struct XObjectContext;
 
 // Conversions to/from underlying types.
 IPDF_Page* IPDFPageFromFPDFPage(FPDF_PAGE page);
@@ -151,20 +151,20 @@
   return reinterpret_cast<CPDF_ContentMarkItem*>(mark);
 }
 
-inline FPDF_PAGERANGE FPDFPageRangeFromCPDFArray(CPDF_Array* range) {
+inline FPDF_PAGERANGE FPDFPageRangeFromCPDFArray(const CPDF_Array* range) {
   return reinterpret_cast<FPDF_PAGERANGE>(range);
 }
-inline CPDF_Array* CPDFArrayFromFPDFPageRange(FPDF_PAGERANGE range) {
-  return reinterpret_cast<CPDF_Array*>(range);
+inline const CPDF_Array* CPDFArrayFromFPDFPageRange(FPDF_PAGERANGE range) {
+  return reinterpret_cast<const CPDF_Array*>(range);
 }
 
 inline FPDF_PATHSEGMENT FPDFPathSegmentFromFXPathPoint(
-    const FX_PATHPOINT* segment) {
+    const CFX_Path::Point* segment) {
   return reinterpret_cast<FPDF_PATHSEGMENT>(segment);
 }
-inline const FX_PATHPOINT* FXPathPointFromFPDFPathSegment(
+inline const CFX_Path::Point* FXPathPointFromFPDFPathSegment(
     FPDF_PATHSEGMENT segment) {
-  return reinterpret_cast<const FX_PATHPOINT*>(segment);
+  return reinterpret_cast<const CFX_Path::Point*>(segment);
 }
 
 inline FPDF_STRUCTTREE FPDFStructTreeFromCPDFStructTree(
@@ -185,6 +185,15 @@
   return reinterpret_cast<CPDF_StructElement*>(struct_element);
 }
 
+inline FPDF_STRUCTELEMENT_ATTR FPDFStructElementAttrFromCPDFDictionary(
+    const CPDF_Dictionary* dictionary) {
+  return reinterpret_cast<FPDF_STRUCTELEMENT_ATTR>(dictionary);
+}
+inline const CPDF_Dictionary* CPDFDictionaryFromFPDFStructElementAttr(
+    FPDF_STRUCTELEMENT_ATTR struct_element_attr) {
+  return reinterpret_cast<const CPDF_Dictionary*>(struct_element_attr);
+}
+
 inline FPDF_TEXTPAGE FPDFTextPageFromCPDFTextPage(CPDF_TextPage* page) {
   return reinterpret_cast<FPDF_TEXTPAGE>(page);
 }
@@ -210,6 +219,23 @@
   return reinterpret_cast<CPDFSDK_FormFillEnvironment*>(handle);
 }
 
+inline FPDF_SIGNATURE FPDFSignatureFromCPDFDictionary(
+    const CPDF_Dictionary* dictionary) {
+  return reinterpret_cast<FPDF_SIGNATURE>(dictionary);
+}
+inline const CPDF_Dictionary* CPDFDictionaryFromFPDFSignature(
+    FPDF_SIGNATURE signature) {
+  return reinterpret_cast<const CPDF_Dictionary*>(signature);
+}
+
+inline FPDF_XOBJECT FPDFXObjectFromXObjectContext(XObjectContext* xobject) {
+  return reinterpret_cast<FPDF_XOBJECT>(xobject);
+}
+
+inline XObjectContext* XObjectContextFromFPDFXObject(FPDF_XOBJECT xobject) {
+  return reinterpret_cast<XObjectContext*>(xobject);
+}
+
 CPDFSDK_InteractiveForm* FormHandleToInteractiveForm(FPDF_FORMHANDLE hHandle);
 
 ByteString ByteStringFromFPDFWideString(FPDF_WIDESTRING wide_string);
@@ -222,11 +248,13 @@
     FPDF_FILEHANDLER* pFileHandler);
 #endif  // PDF_ENABLE_XFA
 
-const CPDF_Array* GetQuadPointsArrayFromDictionary(const CPDF_Dictionary* dict);
-CPDF_Array* GetQuadPointsArrayFromDictionary(CPDF_Dictionary* dict);
-CPDF_Array* AddQuadPointsArrayToDictionary(CPDF_Dictionary* dict);
+RetainPtr<const CPDF_Array> GetQuadPointsArrayFromDictionary(
+    const CPDF_Dictionary* dict);
+RetainPtr<CPDF_Array> GetMutableQuadPointsArrayFromDictionary(
+    CPDF_Dictionary* dict);
+RetainPtr<CPDF_Array> AddQuadPointsArrayToDictionary(CPDF_Dictionary* dict);
 bool IsValidQuadPointsIndex(const CPDF_Array* array, size_t index);
-bool GetQuadPointsAtIndex(const CPDF_Array* array,
+bool GetQuadPointsAtIndex(RetainPtr<const CPDF_Array> array,
                           size_t quad_index,
                           FS_QUADPOINTSF* quad_points);
 
@@ -238,34 +266,43 @@
 CFX_Matrix CFXMatrixFromFSMatrix(const FS_MATRIX& matrix);
 FS_MATRIX FSMatrixFromCFXMatrix(const CFX_Matrix& matrix);
 
+unsigned long NulTerminateMaybeCopyAndReturnLength(const ByteString& text,
+                                                   void* buffer,
+                                                   unsigned long buflen);
+
 unsigned long Utf16EncodeMaybeCopyAndReturnLength(const WideString& text,
                                                   void* buffer,
                                                   unsigned long buflen);
 
 // Returns the length of the raw stream data from |stream|. The raw data is the
 // stream's data as stored in the PDF without applying any filters. If |buffer|
-// is non-nullptr and |buflen| is large enough to contain the raw data, then
+// is non-empty and its length is large enough to contain the raw data, then
 // the raw data is copied into |buffer|.
-unsigned long GetRawStreamMaybeCopyAndReturnLength(const CPDF_Stream* stream,
-                                                   void* buffer,
-                                                   unsigned long buflen);
+unsigned long GetRawStreamMaybeCopyAndReturnLength(
+    RetainPtr<const CPDF_Stream> stream,
+    pdfium::span<uint8_t> buffer);
 
 // Return the length of the decoded stream data of |stream|. The decoded data is
 // the uncompressed stream data, i.e. the raw stream data after having all
-// filters applied. If |buffer| is non-nullptr and |buflen| is large enough to
+// filters applied. If |buffer| is non-empty and its length is large enough to
 // contain the decoded data, then the decoded data is copied into |buffer|.
-unsigned long DecodeStreamMaybeCopyAndReturnLength(const CPDF_Stream* stream,
-                                                   void* buffer,
-                                                   unsigned long buflen);
+unsigned long DecodeStreamMaybeCopyAndReturnLength(
+    RetainPtr<const CPDF_Stream> stream,
+    pdfium::span<uint8_t> buffer);
 
 void SetPDFSandboxPolicy(FPDF_DWORD policy, FPDF_BOOL enable);
 FPDF_BOOL IsPDFSandboxPolicyEnabled(FPDF_DWORD policy);
 
 void SetPDFUnsupportInfo(UNSUPPORT_INFO* unsp_info);
-UNSUPPORT_INFO* GetPDFUnssuportInto();
-void ReportUnsupportedFeatures(CPDF_Document* pDoc);
-void ReportUnsupportedXFA(CPDF_Document* pDoc);
+void ReportUnsupportedFeatures(const CPDF_Document* pDoc);
+void ReportUnsupportedXFA(const CPDF_Document* pDoc);
 void CheckForUnsupportedAnnot(const CPDF_Annot* pAnnot);
 void ProcessParseError(CPDF_Parser::Error err);
+void SetColorFromScheme(const FPDF_COLORSCHEME* pColorScheme,
+                        CPDF_RenderOptions* pRenderOptions);
+
+// Returns a vector of page indices given a page range string.
+std::vector<uint32_t> ParsePageRangeString(const ByteString& bsPageRange,
+                                           uint32_t nCount);
 
 #endif  // FPDFSDK_CPDFSDK_HELPERS_H_
diff --git a/fpdfsdk/cpdfsdk_helpers_unittest.cpp b/fpdfsdk/cpdfsdk_helpers_unittest.cpp
new file mode 100644
index 0000000..ac1320e
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_helpers_unittest.cpp
@@ -0,0 +1,88 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fpdfsdk/cpdfsdk_helpers.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+
+TEST(CPDFSDK_HelpersTest, NulTerminateMaybeCopyAndReturnLength) {
+  {
+    const ByteString to_be_copied("toBeCopied");
+    constexpr size_t kExpectedToBeCopiedLen = 10;
+    ASSERT_EQ(kExpectedToBeCopiedLen, to_be_copied.GetLength());
+
+    EXPECT_EQ(kExpectedToBeCopiedLen + 1,
+              NulTerminateMaybeCopyAndReturnLength(to_be_copied, nullptr, 0));
+
+    // Buffer should not change if declared length is too short.
+    char buf[kExpectedToBeCopiedLen + 1];
+    memset(buf, 0x42, kExpectedToBeCopiedLen + 1);
+    ASSERT_EQ(kExpectedToBeCopiedLen + 1,
+              NulTerminateMaybeCopyAndReturnLength(to_be_copied, buf,
+                                                   kExpectedToBeCopiedLen));
+    for (char c : buf)
+      EXPECT_EQ(0x42, c);
+
+    // Buffer should copy over if long enough.
+    ASSERT_EQ(kExpectedToBeCopiedLen + 1,
+              NulTerminateMaybeCopyAndReturnLength(to_be_copied, buf,
+                                                   kExpectedToBeCopiedLen + 1));
+    EXPECT_EQ(to_be_copied, ByteString(buf));
+  }
+  {
+    // Empty ByteString should still copy NUL terminator.
+    const ByteString empty;
+    char buf[1];
+    ASSERT_EQ(1u, NulTerminateMaybeCopyAndReturnLength(empty, buf, 1));
+    EXPECT_EQ(empty, ByteString(buf));
+  }
+}
+
+TEST(CPDFSDK_HelpersTest, ParsePageRangeString) {
+  EXPECT_THAT(ParsePageRangeString("", 1), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString(" ", 1), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("clams", 1), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("0", 0), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("1", 0), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString(",1", 10), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("1,", 10), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("1,clams", 1), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("clams,1", 1), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("0-1", 10), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("1-0", 10), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("1-5", 4), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("1-11,", 10), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString(",1-1", 10), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("1-", 10), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("1-,", 10), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("-2,", 10), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("1-clams", 10), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("clams-1,", 10), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("1-2clams", 10), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("0,1", 10), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("1,0", 10), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("1-2,,,,3-4", 10), IsEmpty());
+  EXPECT_THAT(ParsePageRangeString("1-2-", 10), IsEmpty());
+
+  EXPECT_THAT(ParsePageRangeString("1-1", 10), ElementsAre(0));
+  EXPECT_THAT(ParsePageRangeString("1", 1), ElementsAre(0));
+  EXPECT_THAT(ParsePageRangeString("1-4", 4), ElementsAre(0, 1, 2, 3));
+  EXPECT_THAT(ParsePageRangeString("1- 4", 4), ElementsAre(0, 1, 2, 3));
+  EXPECT_THAT(ParsePageRangeString("1 -4", 4), ElementsAre(0, 1, 2, 3));
+  EXPECT_THAT(ParsePageRangeString("1,2", 10), ElementsAre(0, 1));
+  EXPECT_THAT(ParsePageRangeString("2,1", 10), ElementsAre(1, 0));
+  EXPECT_THAT(ParsePageRangeString("1,50,2", 100), ElementsAre(0, 49, 1));
+  EXPECT_THAT(ParsePageRangeString("1-4,50", 100), ElementsAre(0, 1, 2, 3, 49));
+  EXPECT_THAT(ParsePageRangeString("50,1-2", 100), ElementsAre(49, 0, 1));
+  EXPECT_THAT(ParsePageRangeString("5  0, 1-2 ", 100),
+              ElementsAre(49, 0, 1));  // ???
+  EXPECT_THAT(ParsePageRangeString("1-3,4-6", 10),
+              ElementsAre(0, 1, 2, 3, 4, 5));
+  EXPECT_THAT(ParsePageRangeString("1-4,3-6", 10),
+              ElementsAre(0, 1, 2, 3, 2, 3, 4, 5));
+}
diff --git a/fpdfsdk/cpdfsdk_interactiveform.cpp b/fpdfsdk/cpdfsdk_interactiveform.cpp
index 51145b9..021124f 100644
--- a/fpdfsdk/cpdfsdk_interactiveform.cpp
+++ b/fpdfsdk/cpdfsdk_interactiveform.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,11 @@
 
 #include "fpdfsdk/cpdfsdk_interactiveform.h"
 
+#include <stdint.h>
+
 #include <algorithm>
 #include <memory>
 #include <sstream>
-#include <string>
 #include <utility>
 #include <vector>
 
@@ -24,19 +25,19 @@
 #include "core/fpdfdoc/cpdf_formcontrol.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "core/fxcrt/autorestorer.h"
+#include "core/fxcrt/fx_string_wrappers.h"
+#include "core/fxcrt/stl_util.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
-#include "fpdfsdk/cpdfsdk_actionhandler.h"
+#include "core/fxge/cfx_path.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
 #include "fpdfsdk/cpdfsdk_annotiterator.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
-#include "fpdfsdk/formfiller/cffl_formfiller.h"
-#include "fpdfsdk/ipdfsdk_annothandler.h"
+#include "fpdfsdk/formfiller/cffl_formfield.h"
 #include "fxjs/ijs_event_context.h"
 #include "fxjs/ijs_runtime.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -70,42 +71,37 @@
 }
 #endif  // PDF_ENABLE_XFA
 
-bool FDFToURLEncodedData(std::vector<uint8_t>* pBuffer) {
-  std::unique_ptr<CFDF_Document> pFDF = CFDF_Document::ParseMemory(*pBuffer);
+ByteString FDFToURLEncodedData(ByteString buffer) {
+  std::unique_ptr<CFDF_Document> pFDF =
+      CFDF_Document::ParseMemory(buffer.raw_span());
   if (!pFDF)
-    return true;
+    return buffer;
 
-  CPDF_Dictionary* pMainDict = pFDF->GetRoot()->GetDictFor("FDF");
+  RetainPtr<const CPDF_Dictionary> pMainDict =
+      pFDF->GetRoot()->GetDictFor("FDF");
   if (!pMainDict)
-    return false;
+    return ByteString();
 
-  CPDF_Array* pFields = pMainDict->GetArrayFor("Fields");
+  RetainPtr<const CPDF_Array> pFields = pMainDict->GetArrayFor("Fields");
   if (!pFields)
-    return false;
+    return ByteString();
 
-  std::ostringstream fdfEncodedData;
+  fxcrt::ostringstream encoded_data;
   for (uint32_t i = 0; i < pFields->size(); i++) {
-    CPDF_Dictionary* pField = pFields->GetDictAt(i);
+    RetainPtr<const CPDF_Dictionary> pField = pFields->GetDictAt(i);
     if (!pField)
       continue;
-    WideString name;
-    name = pField->GetUnicodeTextFor("T");
+    WideString name = pField->GetUnicodeTextFor("T");
     ByteString name_b = name.ToDefANSI();
-    ByteString csBValue = pField->GetStringFor("V");
+    ByteString csBValue = pField->GetByteStringFor("V");
     WideString csWValue = PDF_DecodeText(csBValue.raw_span());
     ByteString csValue_b = csWValue.ToDefANSI();
-    fdfEncodedData << name_b << "=" << csValue_b;
+    encoded_data << name_b << "=" << csValue_b;
     if (i != pFields->size() - 1)
-      fdfEncodedData << "&";
+      encoded_data << "&";
   }
 
-  size_t nBufSize = fdfEncodedData.tellp();
-  if (nBufSize <= 0)
-    return false;
-
-  pBuffer->resize(nBufSize);
-  memcpy(pBuffer->data(), fdfEncodedData.str().c_str(), nBufSize);
-  return true;
+  return ByteString(encoded_data);
 }
 
 }  // namespace
@@ -113,7 +109,7 @@
 CPDFSDK_InteractiveForm::CPDFSDK_InteractiveForm(
     CPDFSDK_FormFillEnvironment* pFormFillEnv)
     : m_pFormFillEnv(pFormFillEnv),
-      m_pInteractiveForm(pdfium::MakeUnique<CPDF_InteractiveForm>(
+      m_pInteractiveForm(std::make_unique<CPDF_InteractiveForm>(
           m_pFormFillEnv->GetPDFDocument())) {
   m_pInteractiveForm->SetNotifierIface(this);
   RemoveAllHighLights();
@@ -133,20 +129,20 @@
   if (pWidget)
     return pWidget;
 
-  CPDF_Dictionary* pControlDict = pControl->GetWidget();
   CPDF_Document* pDocument = m_pFormFillEnv->GetPDFDocument();
   CPDFSDK_PageView* pPage = nullptr;
-
-  if (CPDF_Dictionary* pPageDict = pControlDict->GetDictFor("P")) {
+  RetainPtr<const CPDF_Dictionary> pControlDict = pControl->GetWidgetDict();
+  RetainPtr<const CPDF_Dictionary> pPageDict = pControlDict->GetDictFor("P");
+  if (pPageDict) {
     int nPageIndex = pDocument->GetPageIndex(pPageDict->GetObjNum());
     if (nPageIndex >= 0)
-      pPage = m_pFormFillEnv->GetPageView(nPageIndex);
+      pPage = m_pFormFillEnv->GetPageViewAtIndex(nPageIndex);
   }
 
   if (!pPage) {
     int nPageIndex = GetPageIndexByAnnotDict(pDocument, pControlDict);
     if (nPageIndex >= 0)
-      pPage = m_pFormFillEnv->GetPageView(nPageIndex);
+      pPage = m_pFormFillEnv->GetPageViewAtIndex(nPageIndex);
   }
 
   return pPage ? ToCPDFSDKWidget(pPage->GetAnnotByDict(pControlDict)) : nullptr;
@@ -154,21 +150,21 @@
 
 void CPDFSDK_InteractiveForm::GetWidgets(
     const WideString& sFieldName,
-    std::vector<ObservedPtr<CPDFSDK_Annot>>* widgets) const {
-  for (int i = 0, sz = m_pInteractiveForm->CountFields(sFieldName); i < sz;
+    std::vector<ObservedPtr<CPDFSDK_Widget>>* widgets) const {
+  for (size_t i = 0, sz = m_pInteractiveForm->CountFields(sFieldName); i < sz;
        ++i) {
     CPDF_FormField* pFormField = m_pInteractiveForm->GetField(i, sFieldName);
-    ASSERT(pFormField);
+    DCHECK(pFormField);
     GetWidgets(pFormField, widgets);
   }
 }
 
 void CPDFSDK_InteractiveForm::GetWidgets(
     CPDF_FormField* pField,
-    std::vector<ObservedPtr<CPDFSDK_Annot>>* widgets) const {
+    std::vector<ObservedPtr<CPDFSDK_Widget>>* widgets) const {
   for (int i = 0, sz = pField->CountControls(); i < sz; ++i) {
     CPDF_FormControl* pFormCtrl = pField->GetControl(i);
-    ASSERT(pFormCtrl);
+    DCHECK(pFormCtrl);
     CPDFSDK_Widget* pWidget = GetWidget(pFormCtrl);
     if (pWidget)
       widgets->emplace_back(pWidget);
@@ -177,31 +173,37 @@
 
 int CPDFSDK_InteractiveForm::GetPageIndexByAnnotDict(
     CPDF_Document* pDocument,
-    CPDF_Dictionary* pAnnotDict) const {
-  ASSERT(pAnnotDict);
+    const CPDF_Dictionary* pAnnotDict) const {
+  DCHECK(pAnnotDict);
 
   for (int i = 0, sz = pDocument->GetPageCount(); i < sz; i++) {
-    if (CPDF_Dictionary* pPageDict = pDocument->GetPageDictionary(i)) {
-      if (CPDF_Array* pAnnots = pPageDict->GetArrayFor("Annots")) {
-        for (int j = 0, jsz = pAnnots->size(); j < jsz; j++) {
-          CPDF_Object* pDict = pAnnots->GetDirectObjectAt(j);
-          if (pAnnotDict == pDict)
-            return i;
-        }
-      }
+    RetainPtr<const CPDF_Dictionary> pPageDict =
+        pDocument->GetPageDictionary(i);
+    if (!pPageDict)
+      continue;
+
+    RetainPtr<const CPDF_Array> pAnnots = pPageDict->GetArrayFor("Annots");
+    if (!pAnnots)
+      continue;
+
+    for (size_t j = 0, jsz = pAnnots->size(); j < jsz; j++) {
+      RetainPtr<const CPDF_Object> pDict = pAnnots->GetDirectObjectAt(j);
+      if (pAnnotDict == pDict)
+        return i;
     }
   }
-
   return -1;
 }
 
 void CPDFSDK_InteractiveForm::AddMap(CPDF_FormControl* pControl,
                                      CPDFSDK_Widget* pWidget) {
-  m_Map[pControl] = pWidget;
+  m_Map[pdfium::WrapUnowned(pControl)] = pWidget;
 }
 
 void CPDFSDK_InteractiveForm::RemoveMap(CPDF_FormControl* pControl) {
-  m_Map.erase(pControl);
+  auto it = m_Map.find(pControl);
+  if (it != m_Map.end())
+    m_Map.erase(it);
 }
 
 void CPDFSDK_InteractiveForm::EnableCalculate(bool bEnabled) {
@@ -262,11 +264,11 @@
       continue;
 
     CPDF_AAction aAction = pField->GetAdditionalAction();
-    if (!aAction.GetDict() || !aAction.ActionExist(CPDF_AAction::kCalculate))
+    if (!aAction.ActionExist(CPDF_AAction::kCalculate))
       continue;
 
     CPDF_Action action = aAction.GetAction(CPDF_AAction::kCalculate);
-    if (!action.GetDict())
+    if (!action.HasDict())
       continue;
 
     WideString csJS = action.GetJavaScript();
@@ -279,16 +281,16 @@
     IJS_Runtime::ScopedEventContext pContext(pRuntime);
     pContext->OnField_Calculate(pFormField, pField, &sValue, &bRC);
 
-    Optional<IJS_Runtime::JS_Error> err = pContext->RunScript(csJS);
-    if (!err && bRC && sValue.Compare(sOldValue) != 0)
+    absl::optional<IJS_Runtime::JS_Error> err = pContext->RunScript(csJS);
+    if (!err.has_value() && bRC && sValue != sOldValue)
       pField->SetValue(sValue, NotificationOption::kNotify);
   }
 }
 
-Optional<WideString> CPDFSDK_InteractiveForm::OnFormat(
+absl::optional<WideString> CPDFSDK_InteractiveForm::OnFormat(
     CPDF_FormField* pFormField) {
   if (!m_pFormFillEnv->IsJSPlatformPresent())
-    return {};
+    return absl::nullopt;
 
   WideString sValue = pFormField->GetValue();
   IJS_Runtime* pRuntime = m_pFormFillEnv->GetIJSRuntime();
@@ -300,30 +302,30 @@
   }
 
   CPDF_AAction aAction = pFormField->GetAdditionalAction();
-  if (aAction.GetDict() && aAction.ActionExist(CPDF_AAction::kFormat)) {
+  if (aAction.ActionExist(CPDF_AAction::kFormat)) {
     CPDF_Action action = aAction.GetAction(CPDF_AAction::kFormat);
-    if (action.GetDict()) {
+    if (action.HasDict()) {
       WideString script = action.GetJavaScript();
       if (!script.IsEmpty()) {
         IJS_Runtime::ScopedEventContext pContext(pRuntime);
         pContext->OnField_Format(pFormField, &sValue);
-        Optional<IJS_Runtime::JS_Error> err = pContext->RunScript(script);
-        if (!err)
+        absl::optional<IJS_Runtime::JS_Error> err = pContext->RunScript(script);
+        if (!err.has_value())
           return sValue;
       }
     }
   }
-  return {};
+  return absl::nullopt;
 }
 
 void CPDFSDK_InteractiveForm::ResetFieldAppearance(
     CPDF_FormField* pFormField,
-    Optional<WideString> sValue) {
+    absl::optional<WideString> sValue) {
   for (int i = 0, sz = pFormField->CountControls(); i < sz; i++) {
     CPDF_FormControl* pFormCtrl = pFormField->GetControl(i);
-    ASSERT(pFormCtrl);
+    DCHECK(pFormCtrl);
     if (CPDFSDK_Widget* pWidget = GetWidget(pFormCtrl))
-      pWidget->ResetAppearance(sValue, true);
+      pWidget->ResetAppearance(sValue, CPDFSDK_Widget::kValueChanged);
   }
 }
 
@@ -331,15 +333,15 @@
   auto* formfiller = m_pFormFillEnv->GetInteractiveFormFiller();
   for (int i = 0, sz = pFormField->CountControls(); i < sz; i++) {
     CPDF_FormControl* pFormCtrl = pFormField->GetControl(i);
-    ASSERT(pFormCtrl);
+    DCHECK(pFormCtrl);
 
     CPDFSDK_Widget* pWidget = GetWidget(pFormCtrl);
     if (!pWidget)
       continue;
 
     IPDF_Page* pPage = pWidget->GetPage();
-    FX_RECT rect = formfiller->GetViewBBox(
-        m_pFormFillEnv->GetPageView(pPage, false), pWidget);
+    FX_RECT rect =
+        formfiller->GetViewBBox(m_pFormFillEnv->GetPageView(pPage), pWidget);
     m_pFormFillEnv->Invalidate(pPage, rect);
   }
 }
@@ -347,43 +349,43 @@
 bool CPDFSDK_InteractiveForm::OnKeyStrokeCommit(CPDF_FormField* pFormField,
                                                 const WideString& csValue) {
   CPDF_AAction aAction = pFormField->GetAdditionalAction();
-  if (!aAction.GetDict() || !aAction.ActionExist(CPDF_AAction::kKeyStroke))
+  if (!aAction.ActionExist(CPDF_AAction::kKeyStroke))
     return true;
 
   CPDF_Action action = aAction.GetAction(CPDF_AAction::kKeyStroke);
-  if (!action.GetDict())
+  if (!action.HasDict())
     return true;
 
-  CPDFSDK_FieldAction fa;
+  CFFL_FieldAction fa;
   fa.bModifier = false;
   fa.bShift = false;
   fa.sValue = csValue;
-  m_pFormFillEnv->GetActionHandler()->DoAction_FieldJavaScript(
-      action, CPDF_AAction::kKeyStroke, m_pFormFillEnv.Get(), pFormField, &fa);
+  m_pFormFillEnv->DoActionFieldJavaScript(action, CPDF_AAction::kKeyStroke,
+                                          pFormField, &fa);
   return fa.bRC;
 }
 
 bool CPDFSDK_InteractiveForm::OnValidate(CPDF_FormField* pFormField,
                                          const WideString& csValue) {
   CPDF_AAction aAction = pFormField->GetAdditionalAction();
-  if (!aAction.GetDict() || !aAction.ActionExist(CPDF_AAction::kValidate))
+  if (!aAction.ActionExist(CPDF_AAction::kValidate))
     return true;
 
   CPDF_Action action = aAction.GetAction(CPDF_AAction::kValidate);
-  if (!action.GetDict())
+  if (!action.HasDict())
     return true;
 
-  CPDFSDK_FieldAction fa;
+  CFFL_FieldAction fa;
   fa.bModifier = false;
   fa.bShift = false;
   fa.sValue = csValue;
-  m_pFormFillEnv->GetActionHandler()->DoAction_FieldJavaScript(
-      action, CPDF_AAction::kValidate, m_pFormFillEnv.Get(), pFormField, &fa);
+  m_pFormFillEnv->DoActionFieldJavaScript(action, CPDF_AAction::kValidate,
+                                          pFormField, &fa);
   return fa.bRC;
 }
 
 bool CPDFSDK_InteractiveForm::DoAction_Hide(const CPDF_Action& action) {
-  ASSERT(action.GetDict());
+  DCHECK(action.GetDict());
   std::vector<CPDF_FormField*> fields =
       GetFieldFromObjects(action.GetAllFields());
   bool bHide = action.GetHideStatus();
@@ -392,7 +394,7 @@
   for (CPDF_FormField* pField : fields) {
     for (int i = 0, sz = pField->CountControls(); i < sz; ++i) {
       CPDF_FormControl* pControl = pField->GetControl(i);
-      ASSERT(pControl);
+      DCHECK(pControl);
 
       if (CPDFSDK_Widget* pWidget = GetWidget(pControl)) {
         uint32_t nFlags = pWidget->GetFlags();
@@ -417,8 +419,7 @@
   if (sDestination.IsEmpty())
     return false;
 
-  const CPDF_Dictionary* pActionDict = action.GetDict();
-  if (pActionDict->KeyExist("Fields")) {
+  if (action.HasFields()) {
     uint32_t dwFlags = action.GetFlags();
     std::vector<CPDF_FormField*> fields =
         GetFieldFromObjects(action.GetAllFields());
@@ -433,7 +434,7 @@
   if (!m_pInteractiveForm->CheckRequiredFields(nullptr, true))
     return false;
 
-  return SubmitForm(sDestination, false);
+  return SubmitForm(sDestination);
 }
 
 bool CPDFSDK_InteractiveForm::SubmitFields(
@@ -441,15 +442,17 @@
     const std::vector<CPDF_FormField*>& fields,
     bool bIncludeOrExclude,
     bool bUrlEncoded) {
-  ByteString textBuf = ExportFieldsToFDFTextBuf(fields, bIncludeOrExclude);
-  if (textBuf.IsEmpty())
+  ByteString text_buf = ExportFieldsToFDFTextBuf(fields, bIncludeOrExclude);
+  if (text_buf.IsEmpty())
     return false;
 
-  std::vector<uint8_t> buffer(textBuf.begin(), textBuf.end());
-  if (bUrlEncoded && !FDFToURLEncodedData(&buffer))
-    return false;
+  if (bUrlEncoded) {
+    text_buf = FDFToURLEncodedData(text_buf);
+    if (text_buf.IsEmpty())
+      return false;
+  }
 
-  m_pFormFillEnv->SubmitForm(buffer, csDestination);
+  m_pFormFillEnv->SubmitForm(text_buf.raw_span(), csDestination);
   return true;
 }
 
@@ -457,56 +460,49 @@
     const std::vector<CPDF_FormField*>& fields,
     bool bIncludeOrExclude) {
   std::unique_ptr<CFDF_Document> pFDF = m_pInteractiveForm->ExportToFDF(
-      m_pFormFillEnv->GetFilePath(), fields, bIncludeOrExclude, false);
+      m_pFormFillEnv->GetFilePath(), fields, bIncludeOrExclude);
 
   return pFDF ? pFDF->WriteToString() : ByteString();
 }
 
-bool CPDFSDK_InteractiveForm::SubmitForm(const WideString& sDestination,
-                                         bool bUrlEncoded) {
+bool CPDFSDK_InteractiveForm::SubmitForm(const WideString& sDestination) {
   if (sDestination.IsEmpty())
     return false;
 
   std::unique_ptr<CFDF_Document> pFDFDoc =
-      m_pInteractiveForm->ExportToFDF(m_pFormFillEnv->GetFilePath(), false);
+      m_pInteractiveForm->ExportToFDF(m_pFormFillEnv->GetFilePath());
   if (!pFDFDoc)
     return false;
 
-  ByteString fdfBuffer = pFDFDoc->WriteToString();
-  if (fdfBuffer.IsEmpty())
+  ByteString fdf_buffer = pFDFDoc->WriteToString();
+  if (fdf_buffer.IsEmpty())
     return false;
 
-  std::vector<uint8_t> buffer(fdfBuffer.begin(), fdfBuffer.end());
-  if (bUrlEncoded && !FDFToURLEncodedData(&buffer))
-    return false;
-
-  m_pFormFillEnv->SubmitForm(buffer, sDestination);
+  m_pFormFillEnv->SubmitForm(fdf_buffer.raw_span(), sDestination);
   return true;
 }
 
 ByteString CPDFSDK_InteractiveForm::ExportFormToFDFTextBuf() {
   std::unique_ptr<CFDF_Document> pFDF =
-      m_pInteractiveForm->ExportToFDF(m_pFormFillEnv->GetFilePath(), false);
+      m_pInteractiveForm->ExportToFDF(m_pFormFillEnv->GetFilePath());
 
   return pFDF ? pFDF->WriteToString() : ByteString();
 }
 
 void CPDFSDK_InteractiveForm::DoAction_ResetForm(const CPDF_Action& action) {
-  ASSERT(action.GetDict());
-  const CPDF_Dictionary* pActionDict = action.GetDict();
-  if (!pActionDict->KeyExist("Fields")) {
-    m_pInteractiveForm->ResetForm(NotificationOption::kNotify);
+  DCHECK(action.GetDict());
+  if (!action.HasFields()) {
+    m_pInteractiveForm->ResetForm();
     return;
   }
   uint32_t dwFlags = action.GetFlags();
   std::vector<CPDF_FormField*> fields =
       GetFieldFromObjects(action.GetAllFields());
-  m_pInteractiveForm->ResetForm(fields, !(dwFlags & 0x01),
-                                NotificationOption::kNotify);
+  m_pInteractiveForm->ResetForm(fields, !(dwFlags & 0x01));
 }
 
 std::vector<CPDF_FormField*> CPDFSDK_InteractiveForm::GetFieldFromObjects(
-    const std::vector<const CPDF_Object*>& objects) const {
+    const std::vector<RetainPtr<const CPDF_Object>>& objects) const {
   std::vector<CPDF_FormField*> fields;
   for (const CPDF_Object* pObject : objects) {
     if (!pObject || !pObject->IsString())
@@ -558,7 +554,7 @@
     return;
 
   OnCalculate(pField);
-  ResetFieldAppearance(pField, pdfium::nullopt);
+  ResetFieldAppearance(pField, absl::nullopt);
   UpdateField(pField);
 }
 
@@ -592,9 +588,9 @@
 }
 
 void CPDFSDK_InteractiveForm::RemoveAllHighLights() {
-  std::fill(m_HighlightColor, m_HighlightColor + kFormFieldTypeCount,
+  std::fill(std::begin(m_HighlightColor), std::end(m_HighlightColor),
             kWhiteBGR);
-  std::fill(m_NeedsHighlight, m_NeedsHighlight + kFormFieldTypeCount, false);
+  std::fill(std::begin(m_NeedsHighlight), std::end(m_NeedsHighlight), false);
 }
 
 void CPDFSDK_InteractiveForm::SetHighlightColor(FX_COLORREF clr,
diff --git a/fpdfsdk/cpdfsdk_interactiveform.h b/fpdfsdk/cpdfsdk_interactiveform.h
index df931d1..223e3d4 100644
--- a/fpdfsdk/cpdfsdk_interactiveform.h
+++ b/fpdfsdk/cpdfsdk_interactiveform.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #ifndef FPDFSDK_CPDFSDK_INTERACTIVEFORM_H_
 #define FPDFSDK_CPDFSDK_INTERACTIVEFORM_H_
 
+#include <functional>
 #include <map>
 #include <memory>
 #include <vector>
@@ -14,9 +15,9 @@
 #include "core/fpdfdoc/cpdf_action.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CPDF_Dictionary;
 class CPDF_FormControl;
@@ -33,15 +34,12 @@
   CPDF_InteractiveForm* GetInteractiveForm() const {
     return m_pInteractiveForm.get();
   }
-  CPDFSDK_FormFillEnvironment* GetFormFillEnv() const {
-    return m_pFormFillEnv.Get();
-  }
 
   CPDFSDK_Widget* GetWidget(CPDF_FormControl* pControl) const;
   void GetWidgets(const WideString& sFieldName,
-                  std::vector<ObservedPtr<CPDFSDK_Annot>>* widgets) const;
+                  std::vector<ObservedPtr<CPDFSDK_Widget>>* widgets) const;
   void GetWidgets(CPDF_FormField* pField,
-                  std::vector<ObservedPtr<CPDFSDK_Annot>>* widgets) const;
+                  std::vector<ObservedPtr<CPDFSDK_Widget>>* widgets) const;
 
   void AddMap(CPDF_FormControl* pControl, CPDFSDK_Widget* pWidget);
   void RemoveMap(CPDF_FormControl* pControl);
@@ -60,10 +58,10 @@
   bool OnKeyStrokeCommit(CPDF_FormField* pFormField, const WideString& csValue);
   bool OnValidate(CPDF_FormField* pFormField, const WideString& csValue);
   void OnCalculate(CPDF_FormField* pFormField);
-  Optional<WideString> OnFormat(CPDF_FormField* pFormField);
+  absl::optional<WideString> OnFormat(CPDF_FormField* pFormField);
 
   void ResetFieldAppearance(CPDF_FormField* pFormField,
-                            Optional<WideString> sValue);
+                            absl::optional<WideString> sValue);
   void UpdateField(CPDF_FormField* pFormField);
 
   bool DoAction_Hide(const CPDF_Action& action);
@@ -71,12 +69,12 @@
   void DoAction_ResetForm(const CPDF_Action& action);
 
   std::vector<CPDF_FormField*> GetFieldFromObjects(
-      const std::vector<const CPDF_Object*>& objects) const;
+      const std::vector<RetainPtr<const CPDF_Object>>& objects) const;
   bool SubmitFields(const WideString& csDestination,
                     const std::vector<CPDF_FormField*>& fields,
                     bool bIncludeOrExclude,
                     bool bUrlEncoded);
-  bool SubmitForm(const WideString& sDestination, bool bUrlEncoded);
+  bool SubmitForm(const WideString& sDestination);
   ByteString ExportFormToFDFTextBuf();
   ByteString ExportFieldsToFDFTextBuf(
       const std::vector<CPDF_FormField*>& fields,
@@ -102,11 +100,14 @@
   void AfterFormReset(CPDF_InteractiveForm* pForm) override;
 
   int GetPageIndexByAnnotDict(CPDF_Document* pDocument,
-                              CPDF_Dictionary* pAnnotDict) const;
+                              const CPDF_Dictionary* pAnnotDict) const;
 
   UnownedPtr<CPDFSDK_FormFillEnvironment> const m_pFormFillEnv;
   std::unique_ptr<CPDF_InteractiveForm> const m_pInteractiveForm;
-  std::map<CPDF_FormControl*, CPDFSDK_Widget*> m_Map;
+  std::map<UnownedPtr<const CPDF_FormControl>,
+           UnownedPtr<CPDFSDK_Widget>,
+           std::less<>>
+      m_Map;
 #ifdef PDF_ENABLE_XFA
   bool m_bXfaCalculate = true;
   bool m_bXfaValidationsEnabled = true;
diff --git a/fpdfsdk/cpdfsdk_pageview.cpp b/fpdfsdk/cpdfsdk_pageview.cpp
index 9e4adc8..1bbaeeb 100644
--- a/fpdfsdk/cpdfsdk_pageview.cpp
+++ b/fpdfsdk/cpdfsdk_pageview.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #include "fpdfsdk/cpdfsdk_pageview.h"
 
 #include <memory>
+#include <utility>
 #include <vector>
 
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
@@ -15,25 +16,26 @@
 #include "core/fpdfdoc/cpdf_annotlist.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "core/fxcrt/autorestorer.h"
+#include "core/fxcrt/stl_util.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
 #include "fpdfsdk/cpdfsdk_annotiteration.h"
+#include "fpdfsdk/cpdfsdk_annotiterator.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "fpdfsdk/cpdfsdk_interactiveform.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
 
 #ifdef PDF_ENABLE_XFA
 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h"
-#include "xfa/fxfa/cxfa_ffdocview.h"
 #include "xfa/fxfa/cxfa_ffpageview.h"
 #endif  // PDF_ENABLE_XFA
 
 CPDFSDK_PageView::CPDFSDK_PageView(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                                    IPDF_Page* page)
     : m_page(page), m_pFormFillEnv(pFormFillEnv) {
-  ASSERT(m_page);
+  DCHECK(m_page);
   CPDF_Page* pPDFPage = ToPDFPage(page);
   if (pPDFPage) {
     CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
@@ -46,22 +48,26 @@
 
 CPDFSDK_PageView::~CPDFSDK_PageView() {
   if (!m_page->AsXFAPage()) {
-    // The call to |ReleaseAnnot| can cause the page pointed to by |m_page| to
-    // be freed, which will cause issues if we try to cleanup the pageview
-    // pointer in |m_page|. So, reset the pageview pointer before doing anything
-    // else.
+    // Deleting from `m_SDKAnnotArray` below can cause the page pointed to by
+    // `m_page` to be freed, which will cause issues if we try to cleanup the
+    // pageview pointer in `m_page`. So, reset the pageview pointer before doing
+    // anything else.
     m_page->AsPDFPage()->SetView(nullptr);
   }
 
-  CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-      m_pFormFillEnv->GetAnnotHandlerMgr();
-  for (CPDFSDK_Annot* pAnnot : m_SDKAnnotArray)
-    pAnnotHandlerMgr->ReleaseAnnot(pdfium::WrapUnique(pAnnot));
+  // Manually reset elements to ensure they are deleted in order.
+  for (std::unique_ptr<CPDFSDK_Annot>& pAnnot : m_SDKAnnotArray)
+    pAnnot.reset();
 
   m_SDKAnnotArray.clear();
   m_pAnnotList.reset();
 }
 
+void CPDFSDK_PageView::ClearPage(CPDF_Page* pPage) {
+  if (!IsBeingDestroyed())
+    GetFormFillEnv()->RemovePageView(pPage);
+}
+
 void CPDFSDK_PageView::PageView_OnDraw(CFX_RenderDevice* pDevice,
                                        const CFX_Matrix& mtUser2Device,
                                        CPDF_RenderOptions* pOptions,
@@ -80,19 +86,41 @@
 #endif  // PDF_ENABLE_XFA
 
   // for pdf/static xfa.
-  CPDFSDK_AnnotIteration annotIteration(this, true);
-  for (const auto& pSDKAnnot : annotIteration) {
-    m_pFormFillEnv->GetAnnotHandlerMgr()->Annot_OnDraw(
-        this, pSDKAnnot.Get(), pDevice, mtUser2Device,
-        pOptions->GetDrawAnnots());
+  auto annot_iteration = CPDFSDK_AnnotIteration::CreateForDrawing(this);
+  for (const auto& pSDKAnnot : annot_iteration) {
+    pSDKAnnot->OnDraw(pDevice, mtUser2Device, pOptions->GetDrawAnnots());
   }
 }
 
+std::unique_ptr<CPDFSDK_Annot> CPDFSDK_PageView::NewAnnot(CPDF_Annot* annot) {
+  const CPDF_Annot::Subtype sub_type = annot->GetSubtype();
+  if (sub_type == CPDF_Annot::Subtype::WIDGET) {
+    CPDFSDK_InteractiveForm* form = m_pFormFillEnv->GetInteractiveForm();
+    CPDF_InteractiveForm* pdf_form = form->GetInteractiveForm();
+    CPDF_FormControl* form_control =
+        pdf_form->GetControlByDict(annot->GetAnnotDict());
+    if (!form_control)
+      return nullptr;
+
+    auto ret = std::make_unique<CPDFSDK_Widget>(annot, this, form);
+    form->AddMap(form_control, ret.get());
+    if (pdf_form->NeedConstructAP())
+      ret->ResetAppearance(absl::nullopt, CPDFSDK_Widget::kValueUnchanged);
+    return ret;
+  }
+
+#ifdef PDF_ENABLE_XFA
+  if (sub_type == CPDF_Annot::Subtype::XFAWIDGET)
+    return nullptr;
+#endif  // PDF_ENABLE_XFA
+
+  return std::make_unique<CPDFSDK_BAAnnot>(annot, this);
+}
+
 CPDFSDK_Annot* CPDFSDK_PageView::GetFXAnnotAtPoint(const CFX_PointF& point) {
-  CPDFSDK_AnnotHandlerMgr* pAnnotMgr = m_pFormFillEnv->GetAnnotHandlerMgr();
-  CPDFSDK_AnnotIteration annotIteration(this, false);
-  for (const auto& pSDKAnnot : annotIteration) {
-    CFX_FloatRect rc = pAnnotMgr->Annot_OnGetViewBBox(this, pSDKAnnot.Get());
+  CPDFSDK_AnnotIteration annot_iteration(this);
+  for (const auto& pSDKAnnot : annot_iteration) {
+    CFX_FloatRect rc = pSDKAnnot->GetViewBBox();
     if (pSDKAnnot->GetAnnotSubtype() == CPDF_Annot::Subtype::POPUP)
       continue;
     if (rc.Contains(point))
@@ -102,67 +130,60 @@
 }
 
 CPDFSDK_Annot* CPDFSDK_PageView::GetFXWidgetAtPoint(const CFX_PointF& point) {
-  CPDFSDK_AnnotHandlerMgr* pAnnotMgr = m_pFormFillEnv->GetAnnotHandlerMgr();
-  CPDFSDK_AnnotIteration annotIteration(this, false);
-  for (const auto& pSDKAnnot : annotIteration) {
-    bool bHitTest = pSDKAnnot->GetAnnotSubtype() == CPDF_Annot::Subtype::WIDGET;
+  CPDFSDK_AnnotIteration annot_iteration(this);
+  for (const auto& pSDKAnnot : annot_iteration) {
+    const CPDF_Annot::Subtype sub_type = pSDKAnnot->GetAnnotSubtype();
+    bool do_hit_test = sub_type == CPDF_Annot::Subtype::WIDGET;
 #ifdef PDF_ENABLE_XFA
-    bHitTest = bHitTest ||
-               pSDKAnnot->GetAnnotSubtype() == CPDF_Annot::Subtype::XFAWIDGET;
+    do_hit_test = do_hit_test || sub_type == CPDF_Annot::Subtype::XFAWIDGET;
 #endif  // PDF_ENABLE_XFA
-    if (bHitTest) {
-      pAnnotMgr->Annot_OnGetViewBBox(this, pSDKAnnot.Get());
-      if (pAnnotMgr->Annot_OnHitTest(this, pSDKAnnot.Get(), point))
-        return pSDKAnnot.Get();
-    }
+    if (do_hit_test && pSDKAnnot->DoHitTest(point))
+      return pSDKAnnot.Get();
   }
   return nullptr;
 }
 
 #ifdef PDF_ENABLE_XFA
-CPDFSDK_Annot* CPDFSDK_PageView::AddAnnot(CXFA_FFWidget* pPDFAnnot) {
-  CPDFSDK_Annot* pSDKAnnot = GetAnnotByXFAWidget(pPDFAnnot);
+CPDFSDK_Annot* CPDFSDK_PageView::AddAnnotForFFWidget(CXFA_FFWidget* pWidget) {
+  CPDFSDK_Annot* pSDKAnnot = GetAnnotForFFWidget(pWidget);
   if (pSDKAnnot)
     return pSDKAnnot;
 
-  CPDFSDK_AnnotHandlerMgr* pAnnotHandler = m_pFormFillEnv->GetAnnotHandlerMgr();
-  std::unique_ptr<CPDFSDK_Annot> pNewAnnot =
-      pAnnotHandler->NewXFAAnnot(pPDFAnnot, this);
-  ASSERT(pNewAnnot);
-  pSDKAnnot = pNewAnnot.get();
-  // TODO(thestig): See if |m_SDKAnnotArray|, which takes ownership of
-  // |pNewAnnot|, can hold std::unique_ptrs instead of raw pointers.
-  m_SDKAnnotArray.push_back(pNewAnnot.release());
-  return pSDKAnnot;
+  m_SDKAnnotArray.push_back(std::make_unique<CPDFXFA_Widget>(pWidget, this));
+  return m_SDKAnnotArray.back().get();
 }
 
-bool CPDFSDK_PageView::DeleteAnnot(CPDFSDK_Annot* pAnnot) {
+void CPDFSDK_PageView::DeleteAnnotForFFWidget(CXFA_FFWidget* pWidget) {
+  CPDFSDK_Annot* pAnnot = GetAnnotForFFWidget(pWidget);
+  if (!pAnnot)
+    return;
+
   IPDF_Page* pPage = pAnnot->GetXFAPage();
   if (!pPage)
-    return false;
+    return;
 
   CPDF_Document::Extension* pContext = pPage->GetDocument()->GetExtension();
   if (pContext && !pContext->ContainsExtensionForm())
-    return false;
+    return;
 
   ObservedPtr<CPDFSDK_Annot> pObserved(pAnnot);
   if (GetFocusAnnot() == pAnnot)
-    m_pFormFillEnv->KillFocusAnnot(0);  // May invoke JS, invalidating pAnnot.
+    m_pFormFillEnv->KillFocusAnnot({});  // May invoke JS, invalidating pAnnot.
 
   if (pObserved) {
-    CPDFSDK_AnnotHandlerMgr* pAnnotHandler =
-        m_pFormFillEnv->GetAnnotHandlerMgr();
-    if (pAnnotHandler)
-      pAnnotHandler->ReleaseAnnot(pdfium::WrapUnique(pObserved.Get()));
+    auto it = std::find(m_SDKAnnotArray.begin(), m_SDKAnnotArray.end(),
+                        fxcrt::MakeFakeUniquePtr(pAnnot));
+    if (it != m_SDKAnnotArray.end())
+      m_SDKAnnotArray.erase(it);
   }
 
-  auto it = std::find(m_SDKAnnotArray.begin(), m_SDKAnnotArray.end(), pAnnot);
-  if (it != m_SDKAnnotArray.end())
-    m_SDKAnnotArray.erase(it);
   if (m_pCaptureWidget.Get() == pAnnot)
     m_pCaptureWidget.Reset();
+}
 
-  return true;
+CPDFXFA_Page* CPDFSDK_PageView::XFAPageIfNotBackedByPDFPage() {
+  auto* pPage = static_cast<CPDFXFA_Page*>(GetXFAPage());
+  return pPage && !pPage->AsPDFPage() ? pPage : nullptr;
 }
 #endif  // PDF_ENABLE_XFA
 
@@ -174,22 +195,36 @@
   return ToPDFPage(m_page);
 }
 
-CPDFSDK_Annot* CPDFSDK_PageView::GetAnnotByDict(CPDF_Dictionary* pDict) {
-  for (CPDFSDK_Annot* pAnnot : m_SDKAnnotArray) {
-    if (pAnnot->GetPDFAnnot()->GetAnnotDict() == pDict)
-      return pAnnot;
+CPDFSDK_InteractiveForm* CPDFSDK_PageView::GetInteractiveForm() const {
+  return m_pFormFillEnv->GetInteractiveForm();
+}
+
+std::vector<CPDFSDK_Annot*> CPDFSDK_PageView::GetAnnotList() const {
+  std::vector<CPDFSDK_Annot*> list;
+  list.reserve(m_SDKAnnotArray.size());
+  for (const std::unique_ptr<CPDFSDK_Annot>& elem : m_SDKAnnotArray)
+    list.push_back(elem.get());
+  return list;
+}
+
+CPDFSDK_Annot* CPDFSDK_PageView::GetAnnotByDict(const CPDF_Dictionary* pDict) {
+  for (std::unique_ptr<CPDFSDK_Annot>& pAnnot : m_SDKAnnotArray) {
+    CPDF_Annot* pPDFAnnot = pAnnot->GetPDFAnnot();
+    if (pPDFAnnot && pPDFAnnot->GetAnnotDict() == pDict)
+      return pAnnot.get();
   }
   return nullptr;
 }
 
 #ifdef PDF_ENABLE_XFA
-CPDFSDK_Annot* CPDFSDK_PageView::GetAnnotByXFAWidget(CXFA_FFWidget* hWidget) {
-  if (!hWidget)
+CPDFSDK_Annot* CPDFSDK_PageView::GetAnnotForFFWidget(CXFA_FFWidget* pWidget) {
+  if (!pWidget)
     return nullptr;
 
-  for (CPDFSDK_Annot* pAnnot : m_SDKAnnotArray) {
-    if (ToXFAWidget(pAnnot)->GetXFAFFWidget() == hWidget)
-      return pAnnot;
+  for (std::unique_ptr<CPDFSDK_Annot>& pAnnot : m_SDKAnnotArray) {
+    CPDFXFA_Widget* pCurrentWidget = pAnnot->AsXFAWidget();
+    if (pCurrentWidget && pCurrentWidget->GetXFAFFWidget() == pWidget)
+      return pAnnot.get();
   }
   return nullptr;
 }
@@ -200,281 +235,306 @@
 #endif  // PDF_ENABLE_XFA
 
 WideString CPDFSDK_PageView::GetFocusedFormText() {
-  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
-    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-        m_pFormFillEnv->GetAnnotHandlerMgr();
-    return pAnnotHandlerMgr->Annot_GetText(pAnnot);
-  }
+  CPDFSDK_Annot* annot = GetFocusAnnot();
+  return annot ? annot->GetText() : WideString();
+}
 
-  return WideString();
+CPDFSDK_Annot* CPDFSDK_PageView::GetNextAnnot(CPDFSDK_Annot* pAnnot) {
+#ifdef PDF_ENABLE_XFA
+  CPDFXFA_Page* pXFAPage = XFAPageIfNotBackedByPDFPage();
+  if (pXFAPage)
+    return pXFAPage->GetNextXFAAnnot(pAnnot);
+#endif  // PDF_ENABLE_XFA
+  CPDFSDK_AnnotIterator ai(this, GetFormFillEnv()->GetFocusableAnnotSubtypes());
+  return ai.GetNextAnnot(pAnnot);
+}
+
+CPDFSDK_Annot* CPDFSDK_PageView::GetPrevAnnot(CPDFSDK_Annot* pAnnot) {
+#ifdef PDF_ENABLE_XFA
+  CPDFXFA_Page* pXFAPage = XFAPageIfNotBackedByPDFPage();
+  if (pXFAPage)
+    return pXFAPage->GetPrevXFAAnnot(pAnnot);
+#endif  // PDF_ENABLE_XFA
+  CPDFSDK_AnnotIterator ai(this, GetFormFillEnv()->GetFocusableAnnotSubtypes());
+  return ai.GetPrevAnnot(pAnnot);
+}
+
+CPDFSDK_Annot* CPDFSDK_PageView::GetFirstFocusableAnnot() {
+#ifdef PDF_ENABLE_XFA
+  CPDFXFA_Page* pXFAPage = XFAPageIfNotBackedByPDFPage();
+  if (pXFAPage)
+    return pXFAPage->GetFirstXFAAnnot(this);
+#endif  // PDF_ENABLE_XFA
+  CPDFSDK_AnnotIterator ai(this, GetFormFillEnv()->GetFocusableAnnotSubtypes());
+  return ai.GetFirstAnnot();
+}
+
+CPDFSDK_Annot* CPDFSDK_PageView::GetLastFocusableAnnot() {
+#ifdef PDF_ENABLE_XFA
+  CPDFXFA_Page* pXFAPage = XFAPageIfNotBackedByPDFPage();
+  if (pXFAPage)
+    return pXFAPage->GetLastXFAAnnot(this);
+#endif  // PDF_ENABLE_XFA
+  CPDFSDK_AnnotIterator ai(this, GetFormFillEnv()->GetFocusableAnnotSubtypes());
+  return ai.GetLastAnnot();
 }
 
 WideString CPDFSDK_PageView::GetSelectedText() {
-  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
-    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-        m_pFormFillEnv->GetAnnotHandlerMgr();
-    return pAnnotHandlerMgr->Annot_GetSelectedText(pAnnot);
-  }
+  CPDFSDK_Annot* annot = GetFocusAnnot();
+  if (!annot)
+    return WideString();
+  return annot->GetSelectedText();
+}
 
-  return WideString();
+void CPDFSDK_PageView::ReplaceAndKeepSelection(const WideString& text) {
+  CPDFSDK_Annot* annot = GetFocusAnnot();
+  if (annot)
+    annot->ReplaceAndKeepSelection(text);
 }
 
 void CPDFSDK_PageView::ReplaceSelection(const WideString& text) {
-  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
-    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-        m_pFormFillEnv->GetAnnotHandlerMgr();
-    pAnnotHandlerMgr->Annot_ReplaceSelection(pAnnot, text);
-  }
+  CPDFSDK_Annot* annot = GetFocusAnnot();
+  if (annot)
+    annot->ReplaceSelection(text);
+}
+
+bool CPDFSDK_PageView::SelectAllText() {
+  CPDFSDK_Annot* annot = GetFocusAnnot();
+  return annot && annot->SelectAllText();
 }
 
 bool CPDFSDK_PageView::CanUndo() {
-  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
-    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-        m_pFormFillEnv->GetAnnotHandlerMgr();
-    return pAnnotHandlerMgr->Annot_CanUndo(pAnnot);
-  }
-  return false;
+  CPDFSDK_Annot* annot = GetFocusAnnot();
+  return annot && annot->CanUndo();
 }
 
 bool CPDFSDK_PageView::CanRedo() {
-  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
-    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-        m_pFormFillEnv->GetAnnotHandlerMgr();
-    return pAnnotHandlerMgr->Annot_CanRedo(pAnnot);
-  }
-  return false;
+  CPDFSDK_Annot* annot = GetFocusAnnot();
+  return annot && annot->CanRedo();
 }
 
 bool CPDFSDK_PageView::Undo() {
-  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
-    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-        m_pFormFillEnv->GetAnnotHandlerMgr();
-    return pAnnotHandlerMgr->Annot_Undo(pAnnot);
-  }
-  return false;
+  CPDFSDK_Annot* annot = GetFocusAnnot();
+  return annot && annot->Undo();
 }
 
 bool CPDFSDK_PageView::Redo() {
-  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
-    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-        m_pFormFillEnv->GetAnnotHandlerMgr();
-    return pAnnotHandlerMgr->Annot_Redo(pAnnot);
-  }
-  return false;
+  CPDFSDK_Annot* annot = GetFocusAnnot();
+  return annot && annot->Redo();
 }
 
-bool CPDFSDK_PageView::OnFocus(const CFX_PointF& point, uint32_t nFlag) {
+bool CPDFSDK_PageView::OnFocus(Mask<FWL_EVENTFLAG> nFlags,
+                               const CFX_PointF& point) {
   ObservedPtr<CPDFSDK_Annot> pAnnot(GetFXWidgetAtPoint(point));
   if (!pAnnot) {
-    m_pFormFillEnv->KillFocusAnnot(nFlag);
+    m_pFormFillEnv->KillFocusAnnot(nFlags);
     return false;
   }
 
-  m_pFormFillEnv->SetFocusAnnot(&pAnnot);
+  m_pFormFillEnv->SetFocusAnnot(pAnnot);
   return true;
 }
 
-bool CPDFSDK_PageView::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
+bool CPDFSDK_PageView::OnLButtonDown(Mask<FWL_EVENTFLAG> nFlags,
+                                     const CFX_PointF& point) {
   ObservedPtr<CPDFSDK_Annot> pAnnot(GetFXWidgetAtPoint(point));
   if (!pAnnot) {
-    m_pFormFillEnv->KillFocusAnnot(nFlag);
+    m_pFormFillEnv->KillFocusAnnot(nFlags);
     return false;
   }
 
-  CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-      m_pFormFillEnv->GetAnnotHandlerMgr();
-  if (!pAnnotHandlerMgr->Annot_OnLButtonDown(this, &pAnnot, nFlag, point))
+  if (!CPDFSDK_Annot::OnLButtonDown(pAnnot, nFlags, point))
     return false;
 
   if (!pAnnot)
     return false;
 
-  m_pFormFillEnv->SetFocusAnnot(&pAnnot);
+  m_pFormFillEnv->SetFocusAnnot(pAnnot);
   return true;
 }
 
-bool CPDFSDK_PageView::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
-  CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-      m_pFormFillEnv->GetAnnotHandlerMgr();
+bool CPDFSDK_PageView::OnLButtonUp(Mask<FWL_EVENTFLAG> nFlags,
+                                   const CFX_PointF& point) {
   ObservedPtr<CPDFSDK_Annot> pFXAnnot(GetFXWidgetAtPoint(point));
   ObservedPtr<CPDFSDK_Annot> pFocusAnnot(GetFocusAnnot());
   if (pFocusAnnot && pFocusAnnot != pFXAnnot) {
     // Last focus Annot gets a chance to handle the event.
-    if (pAnnotHandlerMgr->Annot_OnLButtonUp(this, &pFocusAnnot, nFlag, point))
+    if (CPDFSDK_Annot::OnLButtonUp(pFocusAnnot, nFlags, point))
       return true;
   }
-  return pFXAnnot &&
-         pAnnotHandlerMgr->Annot_OnLButtonUp(this, &pFXAnnot, nFlag, point);
+  return pFXAnnot && CPDFSDK_Annot::OnLButtonUp(pFXAnnot, nFlags, point);
 }
 
-bool CPDFSDK_PageView::OnLButtonDblClk(const CFX_PointF& point,
-                                       uint32_t nFlag) {
+bool CPDFSDK_PageView::OnLButtonDblClk(Mask<FWL_EVENTFLAG> nFlags,
+                                       const CFX_PointF& point) {
   ObservedPtr<CPDFSDK_Annot> pAnnot(GetFXWidgetAtPoint(point));
   if (!pAnnot) {
-    m_pFormFillEnv->KillFocusAnnot(nFlag);
+    m_pFormFillEnv->KillFocusAnnot(nFlags);
     return false;
   }
 
-  CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-      m_pFormFillEnv->GetAnnotHandlerMgr();
-  if (!pAnnotHandlerMgr->Annot_OnLButtonDblClk(this, &pAnnot, nFlag, point))
+  if (!CPDFSDK_Annot::OnLButtonDblClk(pAnnot, nFlags, point))
     return false;
 
   if (!pAnnot)
     return false;
 
-  m_pFormFillEnv->SetFocusAnnot(&pAnnot);
+  m_pFormFillEnv->SetFocusAnnot(pAnnot);
   return true;
 }
 
-bool CPDFSDK_PageView::OnRButtonDown(const CFX_PointF& point, uint32_t nFlag) {
+bool CPDFSDK_PageView::OnRButtonDown(Mask<FWL_EVENTFLAG> nFlags,
+                                     const CFX_PointF& point) {
   ObservedPtr<CPDFSDK_Annot> pAnnot(GetFXWidgetAtPoint(point));
   if (!pAnnot)
     return false;
 
-  CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-      m_pFormFillEnv->GetAnnotHandlerMgr();
-  bool ok = pAnnotHandlerMgr->Annot_OnRButtonDown(this, &pAnnot, nFlag, point);
+  bool ok = CPDFSDK_Annot::OnRButtonDown(pAnnot, nFlags, point);
   if (!pAnnot)
     return false;
 
   if (ok)
-    m_pFormFillEnv->SetFocusAnnot(&pAnnot);
+    m_pFormFillEnv->SetFocusAnnot(pAnnot);
 
   return true;
 }
 
-bool CPDFSDK_PageView::OnRButtonUp(const CFX_PointF& point, uint32_t nFlag) {
+bool CPDFSDK_PageView::OnRButtonUp(Mask<FWL_EVENTFLAG> nFlags,
+                                   const CFX_PointF& point) {
   ObservedPtr<CPDFSDK_Annot> pAnnot(GetFXWidgetAtPoint(point));
   if (!pAnnot)
     return false;
 
-  CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-      m_pFormFillEnv->GetAnnotHandlerMgr();
-  bool ok = pAnnotHandlerMgr->Annot_OnRButtonUp(this, &pAnnot, nFlag, point);
+  bool ok = CPDFSDK_Annot::OnRButtonUp(pAnnot, nFlags, point);
   if (!pAnnot)
     return false;
 
   if (ok)
-    m_pFormFillEnv->SetFocusAnnot(&pAnnot);
+    m_pFormFillEnv->SetFocusAnnot(pAnnot);
 
   return true;
 }
 
-bool CPDFSDK_PageView::OnMouseMove(const CFX_PointF& point, int nFlag) {
-  CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-      m_pFormFillEnv->GetAnnotHandlerMgr();
-
+bool CPDFSDK_PageView::OnMouseMove(Mask<FWL_EVENTFLAG> nFlags,
+                                   const CFX_PointF& point) {
   ObservedPtr<CPDFSDK_Annot> pFXAnnot(GetFXAnnotAtPoint(point));
   ObservedPtr<CPDFSDK_PageView> pThis(this);
 
   if (m_bOnWidget && m_pCaptureWidget != pFXAnnot)
-    ExitWidget(pAnnotHandlerMgr, true, nFlag);
+    ExitWidget(true, nFlags);
 
   // ExitWidget() may have invalidated objects.
   if (!pThis || !pFXAnnot)
     return false;
 
   if (!m_bOnWidget) {
-    EnterWidget(pAnnotHandlerMgr, &pFXAnnot, nFlag);
+    EnterWidget(pFXAnnot, nFlags);
 
     // EnterWidget() may have invalidated objects.
     if (!pThis)
       return false;
 
     if (!pFXAnnot) {
-      ExitWidget(pAnnotHandlerMgr, false, nFlag);
+      ExitWidget(false, nFlags);
       return true;
     }
   }
-  pAnnotHandlerMgr->Annot_OnMouseMove(this, &pFXAnnot, nFlag, point);
+  CPDFSDK_Annot::OnMouseMove(pFXAnnot, nFlags, point);
   return true;
 }
 
-void CPDFSDK_PageView::EnterWidget(CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr,
-                                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                   uint32_t nFlag) {
+void CPDFSDK_PageView::EnterWidget(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                                   Mask<FWL_EVENTFLAG> nFlags) {
   m_bOnWidget = true;
-  m_pCaptureWidget.Reset(pAnnot->Get());
-  pAnnotHandlerMgr->Annot_OnMouseEnter(this, pAnnot, nFlag);
+  m_pCaptureWidget.Reset(pAnnot.Get());
+  CPDFSDK_Annot::OnMouseEnter(m_pCaptureWidget, nFlags);
 }
 
-void CPDFSDK_PageView::ExitWidget(CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr,
-                                  bool callExitCallback,
-                                  uint32_t nFlag) {
+void CPDFSDK_PageView::ExitWidget(bool callExitCallback,
+                                  Mask<FWL_EVENTFLAG> nFlags) {
   m_bOnWidget = false;
-  if (m_pCaptureWidget) {
-    if (callExitCallback)
-      pAnnotHandlerMgr->Annot_OnMouseExit(this, &m_pCaptureWidget, nFlag);
+  if (!m_pCaptureWidget)
+    return;
 
-    m_pCaptureWidget.Reset();
+  if (callExitCallback) {
+    ObservedPtr<CPDFSDK_PageView> pThis(this);
+    CPDFSDK_Annot::OnMouseExit(m_pCaptureWidget, nFlags);
+
+    // OnMouseExit() may have invalidated |this|.
+    if (!pThis)
+      return;
   }
+
+  m_pCaptureWidget.Reset();
 }
 
-bool CPDFSDK_PageView::OnMouseWheel(double deltaX,
-                                    double deltaY,
+bool CPDFSDK_PageView::OnMouseWheel(Mask<FWL_EVENTFLAG> nFlags,
                                     const CFX_PointF& point,
-                                    int nFlag) {
+                                    const CFX_Vector& delta) {
   ObservedPtr<CPDFSDK_Annot> pAnnot(GetFXWidgetAtPoint(point));
   if (!pAnnot)
     return false;
 
-  CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-      m_pFormFillEnv->GetAnnotHandlerMgr();
-  return pAnnotHandlerMgr->Annot_OnMouseWheel(this, &pAnnot, nFlag,
-                                              static_cast<int>(deltaY), point);
+  return CPDFSDK_Annot::OnMouseWheel(pAnnot, nFlags, point, delta);
 }
 
 bool CPDFSDK_PageView::SetIndexSelected(int index, bool selected) {
-  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
-    ObservedPtr<CPDFSDK_Annot> pAnnotObserved(pAnnot);
-    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-        m_pFormFillEnv->GetAnnotHandlerMgr();
-    return pAnnotHandlerMgr->Annot_SetIndexSelected(&pAnnotObserved, index,
-                                                    selected);
-  }
-
-  return false;
+  CPDFSDK_Annot* annot = GetFocusAnnot();
+  return annot && annot->SetIndexSelected(index, selected);
 }
 
 bool CPDFSDK_PageView::IsIndexSelected(int index) {
-  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
-    ObservedPtr<CPDFSDK_Annot> pAnnotObserved(pAnnot);
-    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-        m_pFormFillEnv->GetAnnotHandlerMgr();
-    return pAnnotHandlerMgr->Annot_IsIndexSelected(&pAnnotObserved, index);
-  }
-
-  return false;
+  CPDFSDK_Annot* annot = GetFocusAnnot();
+  return annot && annot->IsIndexSelected(index);
 }
 
-bool CPDFSDK_PageView::OnChar(int nChar, uint32_t nFlag) {
-  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
-    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-        m_pFormFillEnv->GetAnnotHandlerMgr();
-    return pAnnotHandlerMgr->Annot_OnChar(pAnnot, nChar, nFlag);
-  }
-
-  return false;
+bool CPDFSDK_PageView::OnChar(uint32_t nChar, Mask<FWL_EVENTFLAG> nFlags) {
+  ObservedPtr<CPDFSDK_Annot> pAnnot(GetFocusAnnot());
+  return pAnnot && CPDFSDK_Annot::OnChar(pAnnot, nChar, nFlags);
 }
 
-bool CPDFSDK_PageView::OnKeyDown(int nKeyCode, int nFlag) {
-  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
-    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-        m_pFormFillEnv->GetAnnotHandlerMgr();
-    return pAnnotHandlerMgr->Annot_OnKeyDown(pAnnot, nKeyCode, nFlag);
-  }
-  return false;
-}
+bool CPDFSDK_PageView::OnKeyDown(FWL_VKEYCODE nKeyCode,
+                                 Mask<FWL_EVENTFLAG> nFlags) {
+  ObservedPtr<CPDFSDK_Annot> pAnnot(GetFocusAnnot());
+  if (!pAnnot) {
+    // If pressed key is not tab then no action is needed.
+    if (nKeyCode != FWL_VKEY_Tab)
+      return false;
 
-bool CPDFSDK_PageView::OnKeyUp(int nKeyCode, int nFlag) {
-  return false;
+    // If ctrl key or alt key is pressed, then no action is needed.
+    if (CPWL_Wnd::IsCTRLKeyDown(nFlags) || CPWL_Wnd::IsALTKeyDown(nFlags))
+      return false;
+
+    ObservedPtr<CPDFSDK_Annot> end_annot(CPWL_Wnd::IsSHIFTKeyDown(nFlags)
+                                             ? GetLastFocusableAnnot()
+                                             : GetFirstFocusableAnnot());
+    return end_annot && m_pFormFillEnv->SetFocusAnnot(end_annot);
+  }
+
+  if (CPWL_Wnd::IsCTRLKeyDown(nFlags) || CPWL_Wnd::IsALTKeyDown(nFlags))
+    return CPDFSDK_Annot::OnKeyDown(pAnnot, nKeyCode, nFlags);
+
+  CPDFSDK_Annot* pFocusAnnot = GetFocusAnnot();
+  if (pFocusAnnot && (nKeyCode == FWL_VKEY_Tab)) {
+    ObservedPtr<CPDFSDK_Annot> pNext(CPWL_Wnd::IsSHIFTKeyDown(nFlags)
+                                         ? GetPrevAnnot(pFocusAnnot)
+                                         : GetNextAnnot(pFocusAnnot));
+    if (!pNext)
+      return false;
+    if (pNext.Get() != pFocusAnnot) {
+      GetFormFillEnv()->SetFocusAnnot(pNext);
+      return true;
+    }
+  }
+
+  // Check |pAnnot| again because JS may have destroyed it in GetNextAnnot().
+  if (!pAnnot)
+    return false;
+
+  return CPDFSDK_Annot::OnKeyDown(pAnnot, nKeyCode, nFlags);
 }
 
 void CPDFSDK_PageView::LoadFXAnnots() {
-  CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-      m_pFormFillEnv->GetAnnotHandlerMgr();
-
   AutoRestorer<bool> lock(&m_bLocked);
   m_bLocked = true;
 
@@ -483,40 +543,36 @@
   CPDF_Document::Extension* pContext = m_pFormFillEnv->GetDocExtension();
   if (pContext && pContext->ContainsExtensionFullForm()) {
     CXFA_FFPageView* pageView = protector->GetXFAPageView();
-    std::unique_ptr<IXFA_WidgetIterator> pWidgetHandler =
-        pageView->CreateFormWidgetIterator(XFA_WidgetStatus_Visible |
-                                           XFA_WidgetStatus_Viewable);
+    CXFA_FFPageWidgetIterator pWidgetHandler(
+        pageView, Mask<XFA_WidgetStatus>{XFA_WidgetStatus::kVisible,
+                                         XFA_WidgetStatus::kViewable});
 
-    while (CXFA_FFWidget* pXFAAnnot = pWidgetHandler->MoveToNext()) {
-      std::unique_ptr<CPDFSDK_Annot> pNewAnnot =
-          pAnnotHandlerMgr->NewXFAAnnot(pXFAAnnot, this);
-      ASSERT(pNewAnnot);
-      CPDFSDK_Annot* pAnnot = pNewAnnot.get();
-      m_SDKAnnotArray.push_back(pNewAnnot.release());
-      pAnnotHandlerMgr->Annot_OnLoad(pAnnot);
+    while (CXFA_FFWidget* pXFAAnnot = pWidgetHandler.MoveToNext()) {
+      m_SDKAnnotArray.push_back(
+          std::make_unique<CPDFXFA_Widget>(pXFAAnnot, this));
+      m_SDKAnnotArray.back()->OnLoad();
     }
-
     return;
   }
 #endif  // PDF_ENABLE_XFA
 
   CPDF_Page* pPage = GetPDFPage();
-  ASSERT(pPage);
+  DCHECK(pPage);
   bool bUpdateAP = CPDF_InteractiveForm::IsUpdateAPEnabled();
   // Disable the default AP construction.
   CPDF_InteractiveForm::SetUpdateAP(false);
-  m_pAnnotList = pdfium::MakeUnique<CPDF_AnnotList>(pPage);
+  m_pAnnotList = std::make_unique<CPDF_AnnotList>(pPage);
   CPDF_InteractiveForm::SetUpdateAP(bUpdateAP);
 
   const size_t nCount = m_pAnnotList->Count();
   for (size_t i = 0; i < nCount; ++i) {
     CPDF_Annot* pPDFAnnot = m_pAnnotList->GetAt(i);
     CheckForUnsupportedAnnot(pPDFAnnot);
-    CPDFSDK_Annot* pAnnot = pAnnotHandlerMgr->NewAnnot(pPDFAnnot, this);
+    std::unique_ptr<CPDFSDK_Annot> pAnnot = NewAnnot(pPDFAnnot);
     if (!pAnnot)
       continue;
-    m_SDKAnnotArray.push_back(pAnnot);
-    pAnnotHandlerMgr->Annot_OnLoad(pAnnot);
+    m_SDKAnnotArray.push_back(std::move(pAnnot));
+    m_SDKAnnotArray.back()->OnLoad();
   }
 }
 
@@ -542,21 +598,11 @@
 }
 
 bool CPDFSDK_PageView::IsValidAnnot(const CPDF_Annot* p) const {
-  if (!p)
-    return false;
-
-  const auto& annots = m_pAnnotList->All();
-  auto it = std::find_if(annots.begin(), annots.end(),
-                         [p](const std::unique_ptr<CPDF_Annot>& annot) {
-                           return annot.get() == p;
-                         });
-  return it != annots.end();
+  return p && m_pAnnotList->Contains(p);
 }
 
 bool CPDFSDK_PageView::IsValidSDKAnnot(const CPDFSDK_Annot* p) const {
-  if (!p)
-    return false;
-  return pdfium::ContainsValue(m_SDKAnnotArray, p);
+  return p && pdfium::Contains(m_SDKAnnotArray, fxcrt::MakeFakeUniquePtr(p));
 }
 
 CPDFSDK_Annot* CPDFSDK_PageView::GetFocusAnnot() {
@@ -565,7 +611,6 @@
 }
 
 int CPDFSDK_PageView::GetPageIndexForStaticPDF() const {
-  const CPDF_Dictionary* pDict = GetPDFPage()->GetDict();
   CPDF_Document* pDoc = m_pFormFillEnv->GetPDFDocument();
-  return pDoc->GetPageIndex(pDict->GetObjNum());
+  return pDoc->GetPageIndex(GetPDFPage()->GetDict()->GetObjNum());
 }
diff --git a/fpdfsdk/cpdfsdk_pageview.h b/fpdfsdk/cpdfsdk_pageview.h
index 9b040db..b95f844 100644
--- a/fpdfsdk/cpdfsdk_pageview.h
+++ b/fpdfsdk/cpdfsdk_pageview.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,25 +7,35 @@
 #ifndef FPDFSDK_CPDFSDK_PAGEVIEW_H_
 #define FPDFSDK_CPDFSDK_PAGEVIEW_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/mask.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
-#include "fpdfsdk/cpdfsdk_annothandlermgr.h"
 
 class CFX_RenderDevice;
 class CPDF_AnnotList;
 class CPDF_RenderOptions;
 class CPDFSDK_FormFillEnvironment;
+class CPDFSDK_InteractiveForm;
+
+#ifdef PDF_ENABLE_XFA
+class CPDFXFA_Page;
+class CXFA_FFWidget;
+#endif  // PDF_ENABLE_XFA
 
 class CPDFSDK_PageView final : public CPDF_Page::View {
  public:
   CPDFSDK_PageView(CPDFSDK_FormFillEnvironment* pFormFillEnv, IPDF_Page* page);
   ~CPDFSDK_PageView();
 
+  // CPDF_Page::View:
+  void ClearPage(CPDF_Page* pPage) override;
+
   void PageView_OnDraw(CFX_RenderDevice* pDevice,
                        const CFX_Matrix& mtUser2Device,
                        CPDF_RenderOptions* pOptions,
@@ -33,50 +43,50 @@
 
   void LoadFXAnnots();
   CPDFSDK_Annot* GetFocusAnnot();
+  CPDFSDK_Annot* GetNextAnnot(CPDFSDK_Annot* pAnnot);
+  CPDFSDK_Annot* GetPrevAnnot(CPDFSDK_Annot* pAnnot);
+  CPDFSDK_Annot* GetFirstFocusableAnnot();
+  CPDFSDK_Annot* GetLastFocusableAnnot();
   bool IsValidAnnot(const CPDF_Annot* p) const;
   bool IsValidSDKAnnot(const CPDFSDK_Annot* p) const;
 
-  const std::vector<CPDFSDK_Annot*>& GetAnnotList() const {
-    return m_SDKAnnotArray;
-  }
-  CPDFSDK_Annot* GetAnnotByDict(CPDF_Dictionary* pDict);
+  std::vector<CPDFSDK_Annot*> GetAnnotList() const;
+  CPDFSDK_Annot* GetAnnotByDict(const CPDF_Dictionary* pDict);
 
 #ifdef PDF_ENABLE_XFA
-  bool DeleteAnnot(CPDFSDK_Annot* pAnnot);
-  CPDFSDK_Annot* AddAnnot(CXFA_FFWidget* pPDFAnnot);
-  CPDFSDK_Annot* GetAnnotByXFAWidget(CXFA_FFWidget* hWidget);
+  CPDFSDK_Annot* AddAnnotForFFWidget(CXFA_FFWidget* pWidget);
+  void DeleteAnnotForFFWidget(CXFA_FFWidget* pWidget);
+  CPDFSDK_Annot* GetAnnotForFFWidget(CXFA_FFWidget* pWidget);
   IPDF_Page* GetXFAPage();
 #endif  // PDF_ENABLE_XFA
 
   CPDF_Page* GetPDFPage() const;
   CPDF_Document* GetPDFDocument();
-  CPDFSDK_FormFillEnvironment* GetFormFillEnv() const {
-    return m_pFormFillEnv.Get();
-  }
+  CPDFSDK_FormFillEnvironment* GetFormFillEnv() const { return m_pFormFillEnv; }
 
   WideString GetFocusedFormText();
   WideString GetSelectedText();
+  void ReplaceAndKeepSelection(const WideString& text);
   void ReplaceSelection(const WideString& text);
+  bool SelectAllText();
 
   bool CanUndo();
   bool CanRedo();
   bool Undo();
   bool Redo();
 
-  bool OnFocus(const CFX_PointF& point, uint32_t nFlag);
-  bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag);
-  bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag);
-  bool OnLButtonDblClk(const CFX_PointF& point, uint32_t nFlag);
-  bool OnRButtonDown(const CFX_PointF& point, uint32_t nFlag);
-  bool OnRButtonUp(const CFX_PointF& point, uint32_t nFlag);
-  bool OnChar(int nChar, uint32_t nFlag);
-  bool OnKeyDown(int nKeyCode, int nFlag);
-  bool OnKeyUp(int nKeyCode, int nFlag);
-  bool OnMouseMove(const CFX_PointF& point, int nFlag);
-  bool OnMouseWheel(double deltaX,
-                    double deltaY,
+  bool OnFocus(Mask<FWL_EVENTFLAG> nFlags, const CFX_PointF& point);
+  bool OnLButtonDown(Mask<FWL_EVENTFLAG> nFlags, const CFX_PointF& point);
+  bool OnLButtonUp(Mask<FWL_EVENTFLAG> nFlags, const CFX_PointF& point);
+  bool OnLButtonDblClk(Mask<FWL_EVENTFLAG> nFlags, const CFX_PointF& point);
+  bool OnRButtonDown(Mask<FWL_EVENTFLAG> nFlags, const CFX_PointF& point);
+  bool OnRButtonUp(Mask<FWL_EVENTFLAG> nFlags, const CFX_PointF& point);
+  bool OnChar(uint32_t nChar, Mask<FWL_EVENTFLAG> nFlags);
+  bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlags);
+  bool OnMouseMove(Mask<FWL_EVENTFLAG> nFlags, const CFX_PointF& point);
+  bool OnMouseWheel(Mask<FWL_EVENTFLAG> nFlags,
                     const CFX_PointF& point,
-                    int nFlag);
+                    const CFX_Vector& delta);
 
   bool SetIndexSelected(int index, bool selected);
   bool IsIndexSelected(int index);
@@ -92,28 +102,30 @@
   bool IsLocked() const { return m_bLocked; }
   void SetBeingDestroyed() { m_bBeingDestroyed = true; }
   bool IsBeingDestroyed() const { return m_bBeingDestroyed; }
-  void TakePageOwnership() { m_pOwnsPage.Reset(ToPDFPage(m_page)); }
 
  private:
+#ifdef PDF_ENABLE_XFA
+  CPDFXFA_Page* XFAPageIfNotBackedByPDFPage();
+#endif
+
+  std::unique_ptr<CPDFSDK_Annot> NewAnnot(CPDF_Annot* annot);
+
+  CPDFSDK_InteractiveForm* GetInteractiveForm() const;
   CPDFSDK_Annot* GetFXAnnotAtPoint(const CFX_PointF& point);
   CPDFSDK_Annot* GetFXWidgetAtPoint(const CFX_PointF& point);
 
   int GetPageIndexForStaticPDF() const;
 
-  void EnterWidget(CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr,
-                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   uint32_t nFlag);
-  void ExitWidget(CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr,
-                  bool callExitCallback,
-                  uint32_t nFlag);
+  void EnterWidget(ObservedPtr<CPDFSDK_Annot>& pAnnot,
+                   Mask<FWL_EVENTFLAG> nFlags);
+  void ExitWidget(bool callExitCallback, Mask<FWL_EVENTFLAG> nFlags);
 
   CFX_Matrix m_curMatrix;
-  IPDF_Page* const m_page;
+  UnownedPtr<IPDF_Page> const m_page;
   std::unique_ptr<CPDF_AnnotList> m_pAnnotList;
-  std::vector<CPDFSDK_Annot*> m_SDKAnnotArray;
+  std::vector<std::unique_ptr<CPDFSDK_Annot>> m_SDKAnnotArray;
   UnownedPtr<CPDFSDK_FormFillEnvironment> const m_pFormFillEnv;
   ObservedPtr<CPDFSDK_Annot> m_pCaptureWidget;
-  RetainPtr<CPDF_Page> m_pOwnsPage;
   bool m_bOnWidget = false;
   bool m_bValid = false;
   bool m_bLocked = false;
diff --git a/fpdfsdk/cpdfsdk_pauseadapter.cpp b/fpdfsdk/cpdfsdk_pauseadapter.cpp
index bf3f1c7..dfefbcf 100644
--- a/fpdfsdk/cpdfsdk_pauseadapter.cpp
+++ b/fpdfsdk/cpdfsdk_pauseadapter.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,5 +12,5 @@
 CPDFSDK_PauseAdapter::~CPDFSDK_PauseAdapter() = default;
 
 bool CPDFSDK_PauseAdapter::NeedToPauseNow() {
-  return m_IPause->NeedToPauseNow && m_IPause->NeedToPauseNow(m_IPause.Get());
+  return m_IPause->NeedToPauseNow && m_IPause->NeedToPauseNow(m_IPause);
 }
diff --git a/fpdfsdk/cpdfsdk_pauseadapter.h b/fpdfsdk/cpdfsdk_pauseadapter.h
index dfbc2b4..e44eef1 100644
--- a/fpdfsdk/cpdfsdk_pauseadapter.h
+++ b/fpdfsdk/cpdfsdk_pauseadapter.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,6 @@
 #ifndef FPDFSDK_CPDFSDK_PAUSEADAPTER_H_
 #define FPDFSDK_CPDFSDK_PAUSEADAPTER_H_
 
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/pauseindicator_iface.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "public/fpdf_progressive.h"
diff --git a/fpdfsdk/cpdfsdk_renderpage.cpp b/fpdfsdk/cpdfsdk_renderpage.cpp
index ce492e6..757aa1f 100644
--- a/fpdfsdk/cpdfsdk_renderpage.cpp
+++ b/fpdfsdk/cpdfsdk_renderpage.cpp
@@ -1,4 +1,4 @@
-// Copyright 2020 PDFium Authors. All rights reserved.
+// Copyright 2020 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,17 +6,17 @@
 
 #include "fpdfsdk/cpdfsdk_renderpage.h"
 
+#include <memory>
 #include <utility>
 
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
 #include "core/fpdfapi/render/cpdf_pagerendercontext.h"
 #include "core/fpdfapi/render/cpdf_progressiverenderer.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fpdfdoc/cpdf_annotlist.h"
 #include "core/fxge/cfx_renderdevice.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
 #include "fpdfsdk/cpdfsdk_pauseadapter.h"
-#include "public/fpdfview.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -25,10 +25,11 @@
                     const CFX_Matrix& matrix,
                     const FX_RECT& clipping_rect,
                     int flags,
+                    const FPDF_COLORSCHEME* color_scheme,
                     bool need_to_restore,
                     CPDFSDK_PauseAdapter* pause) {
   if (!pContext->m_pOptions)
-    pContext->m_pOptions = pdfium::MakeUnique<CPDF_RenderOptions>();
+    pContext->m_pOptions = std::make_unique<CPDF_RenderOptions>();
 
   auto& options = pContext->m_pOptions->GetOptions();
   options.bClearType = !!(flags & FPDF_LCD_TEXT);
@@ -43,31 +44,42 @@
   if (flags & FPDF_GRAYSCALE)
     pContext->m_pOptions->SetColorMode(CPDF_RenderOptions::kGray);
 
+  if (color_scheme) {
+    pContext->m_pOptions->SetColorMode(CPDF_RenderOptions::kForcedColor);
+    SetColorFromScheme(color_scheme, pContext->m_pOptions.get());
+    options.bConvertFillToStroke = !!(flags & FPDF_CONVERT_FILL_TO_STROKE);
+  }
+
   const CPDF_OCContext::UsageType usage =
-      (flags & FPDF_PRINTING) ? CPDF_OCContext::Print : CPDF_OCContext::View;
+      (flags & FPDF_PRINTING) ? CPDF_OCContext::kPrint : CPDF_OCContext::kView;
   pContext->m_pOptions->SetOCContext(
       pdfium::MakeRetain<CPDF_OCContext>(pPage->GetDocument(), usage));
 
   pContext->m_pDevice->SaveState();
   pContext->m_pDevice->SetBaseClip(clipping_rect);
   pContext->m_pDevice->SetClip_Rect(clipping_rect);
-  pContext->m_pContext = pdfium::MakeUnique<CPDF_RenderContext>(
-      pPage->GetDocument(), pPage->m_pPageResources.Get(),
-      static_cast<CPDF_PageRenderCache*>(pPage->GetRenderCache()));
+  pContext->m_pContext = std::make_unique<CPDF_RenderContext>(
+      pPage->GetDocument(), pPage->GetMutablePageResources(),
+      pPage->GetPageImageCache());
 
-  pContext->m_pContext->AppendLayer(pPage, &matrix);
+  pContext->m_pContext->AppendLayer(pPage, matrix);
 
   if (flags & FPDF_ANNOT) {
-    auto pOwnedList = pdfium::MakeUnique<CPDF_AnnotList>(pPage);
+    auto pOwnedList = std::make_unique<CPDF_AnnotList>(pPage);
     CPDF_AnnotList* pList = pOwnedList.get();
     pContext->m_pAnnots = std::move(pOwnedList);
     bool bPrinting =
+        (flags & FPDF_PRINTING) ||
         pContext->m_pDevice->GetDeviceType() != DeviceType::kDisplay;
-    pList->DisplayAnnots(pPage, pContext->m_pContext.get(), bPrinting, &matrix,
-                         false, nullptr);
+
+    // TODO(https://crbug.com/pdfium/993) - maybe pass true here.
+    const bool bShowWidget = false;
+    pList->DisplayAnnots(pPage, pContext->m_pDevice.get(),
+                         pContext->m_pContext.get(), bPrinting, matrix,
+                         bShowWidget);
   }
 
-  pContext->m_pRenderer = pdfium::MakeUnique<CPDF_ProgressiveRenderer>(
+  pContext->m_pRenderer = std::make_unique<CPDF_ProgressiveRenderer>(
       pContext->m_pContext.get(), pContext->m_pDevice.get(),
       pContext->m_pOptions.get());
   pContext->m_pRenderer->Start(pause);
@@ -81,8 +93,9 @@
                         CPDF_Page* pPage,
                         const CFX_Matrix& matrix,
                         const FX_RECT& clipping_rect,
-                        int flags) {
-  RenderPageImpl(pContext, pPage, matrix, clipping_rect, flags,
+                        int flags,
+                        const FPDF_COLORSCHEME* color_scheme) {
+  RenderPageImpl(pContext, pPage, matrix, clipping_rect, flags, color_scheme,
                  /*need_to_restore=*/true, /*pause=*/nullptr);
 }
 
@@ -94,9 +107,10 @@
                                    int size_y,
                                    int rotate,
                                    int flags,
+                                   const FPDF_COLORSCHEME* color_scheme,
                                    bool need_to_restore,
                                    CPDFSDK_PauseAdapter* pause) {
   const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y);
   RenderPageImpl(pContext, pPage, pPage->GetDisplayMatrix(rect, rotate), rect,
-                 flags, need_to_restore, pause);
+                 flags, color_scheme, need_to_restore, pause);
 }
diff --git a/fpdfsdk/cpdfsdk_renderpage.h b/fpdfsdk/cpdfsdk_renderpage.h
index cb7a600..9b33a81 100644
--- a/fpdfsdk/cpdfsdk_renderpage.h
+++ b/fpdfsdk/cpdfsdk_renderpage.h
@@ -1,4 +1,4 @@
-// Copyright 2020 PDFium Authors. All rights reserved.
+// Copyright 2020 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,8 @@
 #ifndef FPDFSDK_CPDFSDK_RENDERPAGE_H_
 #define FPDFSDK_CPDFSDK_RENDERPAGE_H_
 
+#include "public/fpdfview.h"
+
 class CFX_Matrix;
 class CPDFSDK_PauseAdapter;
 class CPDF_Page;
@@ -17,7 +19,8 @@
                         CPDF_Page* pPage,
                         const CFX_Matrix& matrix,
                         const FX_RECT& clipping_rect,
-                        int flags);
+                        int flags,
+                        const FPDF_COLORSCHEME* color_scheme);
 
 // TODO(thestig): Consider giving this a better name, and make its parameters
 // more similar to those of CPDFSDK_RenderPage().
@@ -29,6 +32,7 @@
                                    int size_y,
                                    int rotate,
                                    int flags,
+                                   const FPDF_COLORSCHEME* color_scheme,
                                    bool need_to_restore,
                                    CPDFSDK_PauseAdapter* pause);
 
diff --git a/fpdfsdk/cpdfsdk_widget.cpp b/fpdfsdk/cpdfsdk_widget.cpp
index 6d92b74..0b8083e 100644
--- a/fpdfsdk/cpdfsdk_widget.cpp
+++ b/fpdfsdk/cpdfsdk_widget.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,32 +6,34 @@
 
 #include "fpdfsdk/cpdfsdk_widget.h"
 
-#include <memory>
-#include <sstream>
-
+#include "constants/access_permissions.h"
 #include "constants/annotation_common.h"
+#include "constants/appearance.h"
+#include "constants/form_flags.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfdoc/cba_fontmap.h"
+#include "core/fpdfdoc/cpdf_bafontmap.h"
 #include "core/fpdfdoc/cpdf_defaultappearance.h"
 #include "core/fpdfdoc/cpdf_formcontrol.h"
 #include "core/fpdfdoc/cpdf_formfield.h"
 #include "core/fpdfdoc/cpdf_iconfit.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
-#include "fpdfsdk/cpdfsdk_actionhandler.h"
 #include "fpdfsdk/cpdfsdk_appstream.h"
-#include "fpdfsdk/cpdfsdk_fieldaction.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
+#include "fpdfsdk/formfiller/cffl_fieldaction.h"
 #include "fpdfsdk/pwl/cpwl_edit.h"
+#include "third_party/base/check.h"
+#include "third_party/base/notreached.h"
 
 #ifdef PDF_ENABLE_XFA
 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
@@ -48,12 +50,15 @@
     : CPDFSDK_BAAnnot(pAnnot, pPageView),
       m_pInteractiveForm(pInteractiveForm) {}
 
-CPDFSDK_Widget::~CPDFSDK_Widget() = default;
+CPDFSDK_Widget::~CPDFSDK_Widget() {
+  GetInteractiveFormFiller()->OnDelete(this);
+  m_pInteractiveForm->RemoveMap(GetFormControl());
+}
 
 #ifdef PDF_ENABLE_XFA
 CXFA_FFWidget* CPDFSDK_Widget::GetMixXFAWidget() const {
   CPDF_Document::Extension* pContext =
-      m_pPageView->GetFormFillEnv()->GetDocExtension();
+      GetPageView()->GetFormFillEnv()->GetDocExtension();
   if (!pContext || !pContext->ContainsExtensionForegroundForm())
     return nullptr;
 
@@ -79,7 +84,7 @@
 
 CXFA_FFWidget* CPDFSDK_Widget::GetGroupMixXFAWidget() const {
   CPDF_Document::Extension* pContext =
-      m_pPageView->GetFormFillEnv()->GetDocExtension();
+      GetPageView()->GetFormFillEnv()->GetDocExtension();
   if (!pContext || !pContext->ContainsExtensionForegroundForm())
     return nullptr;
 
@@ -94,17 +99,13 @@
 
 CXFA_FFWidgetHandler* CPDFSDK_Widget::GetXFAWidgetHandler() const {
   CPDF_Document::Extension* pContext =
-      m_pPageView->GetFormFillEnv()->GetDocExtension();
+      GetPageView()->GetFormFillEnv()->GetDocExtension();
   if (!pContext || !pContext->ContainsExtensionForegroundForm())
     return nullptr;
 
-  if (!m_pWidgetHandler) {
-    CXFA_FFDocView* pDocView =
-        static_cast<CPDFXFA_Context*>(pContext)->GetXFADocView();
-    if (pDocView)
-      m_pWidgetHandler = pDocView->GetWidgetHandler();
-  }
-  return m_pWidgetHandler.Get();
+  CXFA_FFDocView* pDocView =
+      static_cast<CPDFXFA_Context*>(pContext)->GetXFADocView();
+  return pDocView ? pDocView->GetWidgetHandler() : nullptr;
 }
 
 static XFA_EVENTTYPE GetXFAEventType(PDFSDK_XFAAActionType eXFAAAT) {
@@ -175,15 +176,14 @@
       break;
     case CPDF_AAction::kDocumentOpen:
     case CPDF_AAction::kNumberOfActions:
-      NOTREACHED();
-      break;
+      NOTREACHED_NORETURN();
   }
 
   return eEventType;
 }
 
 bool CPDFSDK_Widget::HasXFAAAction(PDFSDK_XFAAActionType eXFAAAT) const {
-  ObservedPtr<CXFA_FFWidget> pWidget(GetMixXFAWidget());
+  CXFA_FFWidget* pWidget = GetMixXFAWidget();
   if (!pWidget)
     return false;
 
@@ -201,20 +201,18 @@
     }
   }
 
-  // Check |pWidget| again because JS may have destroyed it in the block above.
-  return pWidget &&
-         pWidget->HasEventUnderHandler(eEventType, pXFAWidgetHandler);
+  return pWidget->HasEventUnderHandler(eEventType, pXFAWidgetHandler);
 }
 
 bool CPDFSDK_Widget::OnXFAAAction(PDFSDK_XFAAActionType eXFAAAT,
-                                  CPDFSDK_FieldAction* data,
-                                  CPDFSDK_PageView* pPageView) {
+                                  CFFL_FieldAction* data,
+                                  const CPDFSDK_PageView* pPageView) {
   auto* pContext = static_cast<CPDFXFA_Context*>(
-      m_pPageView->GetFormFillEnv()->GetDocExtension());
+      GetPageView()->GetFormFillEnv()->GetDocExtension());
   if (!pContext)
     return false;
 
-  ObservedPtr<CXFA_FFWidget> pWidget(GetMixXFAWidget());
+  CXFA_FFWidget* pWidget = GetMixXFAWidget();
   if (!pWidget)
     return false;
 
@@ -246,12 +244,9 @@
     }
   }
 
-  // Check |pWidget| again because JS may have destroyed it in the block above.
-  if (!pWidget)
-    return false;
-
   bool ret = pWidget->ProcessEventUnderHandler(&param, pXFAWidgetHandler);
-  if (CXFA_FFDocView* pDocView = pContext->GetXFADocView())
+  CXFA_FFDocView* pDocView = pContext->GetXFADocView();
+  if (pDocView)
     pDocView->UpdateDocView();
 
   return ret;
@@ -271,25 +266,26 @@
     case FormFieldType::kCheckBox:
     case FormFieldType::kRadioButton: {
       CPDF_FormControl* pFormCtrl = GetFormControl();
-      XFA_CHECKSTATE eCheckState =
-          pFormCtrl->IsChecked() ? XFA_CHECKSTATE_On : XFA_CHECKSTATE_Off;
-      node->SetCheckState(eCheckState, true);
+      XFA_CheckState eCheckState =
+          pFormCtrl->IsChecked() ? XFA_CheckState::kOn : XFA_CheckState::kOff;
+      node->SetCheckState(eCheckState);
       break;
     }
     case FormFieldType::kTextField:
-      node->SetValue(XFA_VALUEPICTURE_Edit, pFormField->GetValue());
+      node->SetValue(XFA_ValuePicture::kEdit, pFormField->GetValue());
       break;
     case FormFieldType::kComboBox:
     case FormFieldType::kListBox: {
       node->ClearAllSelections();
-
       for (int i = 0; i < pFormField->CountSelectedItems(); ++i) {
         int nIndex = pFormField->GetSelectedIndex(i);
-        if (nIndex > -1 && nIndex < node->CountChoiceListItems(false))
-          node->SetItemState(nIndex, true, false, false, true);
+        if (nIndex > -1 &&
+            static_cast<size_t>(nIndex) < node->CountChoiceListItems(false)) {
+          node->SetItemState(nIndex, true, false, false);
+        }
       }
       if (GetFieldType() == FormFieldType::kComboBox)
-        node->SetValue(XFA_VALUEPICTURE_Edit, pFormField->GetValue());
+        node->SetValue(XFA_ValuePicture::kEdit, pFormField->GetValue());
       break;
     }
     default:
@@ -298,29 +294,70 @@
 
   if (bSynchronizeElse) {
     auto* context = static_cast<CPDFXFA_Context*>(
-        m_pPageView->GetFormFillEnv()->GetDocExtension());
+        GetPageView()->GetFormFillEnv()->GetDocExtension());
     context->GetXFADocView()->ProcessValueChanged(node);
   }
 }
+
+bool CPDFSDK_Widget::HandleXFAAAction(
+    CPDF_AAction::AActionType type,
+    CFFL_FieldAction* data,
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  auto* pContext =
+      static_cast<CPDFXFA_Context*>(pFormFillEnv->GetDocExtension());
+  if (!pContext)
+    return false;
+
+  CXFA_FFWidget* hWidget = GetMixXFAWidget();
+  if (!hWidget)
+    return false;
+
+  XFA_EVENTTYPE eEventType = GetXFAEventType(type, data->bWillCommit);
+  if (eEventType == XFA_EVENT_Unknown)
+    return false;
+
+  CXFA_FFWidgetHandler* pXFAWidgetHandler = GetXFAWidgetHandler();
+  if (!pXFAWidgetHandler)
+    return false;
+
+  CXFA_EventParam param;
+  param.m_eType = eEventType;
+  param.m_wsChange = data->sChange;
+  param.m_iCommitKey = 0;
+  param.m_bShift = data->bShift;
+  param.m_iSelStart = data->nSelStart;
+  param.m_iSelEnd = data->nSelEnd;
+  param.m_wsFullText = data->sValue;
+  param.m_bKeyDown = data->bKeyDown;
+  param.m_bModifier = data->bModifier;
+  param.m_wsPrevText = data->sValue;
+  bool ret = hWidget->ProcessEventUnderHandler(&param, pXFAWidgetHandler);
+  CXFA_FFDocView* pDocView = pContext->GetXFADocView();
+  if (pDocView)
+    pDocView->UpdateDocView();
+
+  return ret;
+}
 #endif  // PDF_ENABLE_XFA
 
-bool CPDFSDK_Widget::IsWidgetAppearanceValid(CPDF_Annot::AppearanceMode mode) {
-  const CPDF_Dictionary* pAP =
+bool CPDFSDK_Widget::IsWidgetAppearanceValid(
+    CPDF_Annot::AppearanceMode mode) const {
+  RetainPtr<const CPDF_Dictionary> pAP =
       GetAnnotDict()->GetDictFor(pdfium::annotation::kAP);
   if (!pAP)
     return false;
 
   // Choose the right sub-ap
   const char* ap_entry = "N";
-  if (mode == CPDF_Annot::Down)
+  if (mode == CPDF_Annot::AppearanceMode::kDown)
     ap_entry = "D";
-  else if (mode == CPDF_Annot::Rollover)
+  else if (mode == CPDF_Annot::AppearanceMode::kRollover)
     ap_entry = "R";
   if (!pAP->KeyExist(ap_entry))
     ap_entry = "N";
 
   // Get the AP stream or subdirectory
-  const CPDF_Object* pSub = pAP->GetDirectObjectFor(ap_entry);
+  RetainPtr<const CPDF_Object> pSub = pAP->GetDirectObjectFor(ap_entry);
   if (!pSub)
     return false;
 
@@ -343,15 +380,25 @@
   }
 }
 
+bool CPDFSDK_Widget::IsPushHighlighted() const {
+  return GetFormControl()->GetHighlightingMode() == CPDF_FormControl::kPush;
+}
+
 FormFieldType CPDFSDK_Widget::GetFieldType() const {
   CPDF_FormField* pField = GetFormField();
   return pField ? pField->GetFieldType() : FormFieldType::kUnknown;
 }
 
+void CPDFSDK_Widget::SetRect(const CFX_FloatRect& rect) {
+  DCHECK(rect.right - rect.left >= 1.0f);
+  DCHECK(rect.top - rect.bottom >= 1.0f);
+  GetMutableAnnotDict()->SetRectFor(pdfium::annotation::kRect, rect);
+}
+
 bool CPDFSDK_Widget::IsAppearanceValid() {
 #ifdef PDF_ENABLE_XFA
   CPDF_Document::Extension* pContext =
-      m_pPageView->GetFormFillEnv()->GetDocExtension();
+      GetPageView()->GetFormFillEnv()->GetDocExtension();
   if (pContext && pContext->ContainsExtensionFullForm())
     return true;
 #endif  // PDF_ENABLE_XFA
@@ -392,37 +439,37 @@
 }
 #endif  // PDF_ENABLE_XFA
 
-Optional<FX_COLORREF> CPDFSDK_Widget::GetFillColor() const {
-  CPDF_FormControl* pFormCtrl = GetFormControl();
-  int iColorType = 0;
-  FX_COLORREF color = ArgbToColorRef(pFormCtrl->GetBackgroundColor(iColorType));
-  if (iColorType == CFX_Color::kTransparent)
-    return {};
-  return color;
+absl::optional<FX_COLORREF> CPDFSDK_Widget::GetFillColor() const {
+  CFX_Color::TypeAndARGB type_argb_pair =
+      GetFormControl()->GetColorARGB(pdfium::appearance::kBG);
+
+  if (type_argb_pair.color_type == CFX_Color::Type::kTransparent)
+    return absl::nullopt;
+
+  return ArgbToColorRef(type_argb_pair.argb);
 }
 
-Optional<FX_COLORREF> CPDFSDK_Widget::GetBorderColor() const {
-  CPDF_FormControl* pFormCtrl = GetFormControl();
-  int iColorType = 0;
-  FX_COLORREF color = ArgbToColorRef(pFormCtrl->GetBorderColor(iColorType));
-  if (iColorType == CFX_Color::kTransparent)
-    return {};
-  return color;
+absl::optional<FX_COLORREF> CPDFSDK_Widget::GetBorderColor() const {
+  CFX_Color::TypeAndARGB type_argb_pair =
+      GetFormControl()->GetColorARGB(pdfium::appearance::kBC);
+  if (type_argb_pair.color_type == CFX_Color::Type::kTransparent)
+    return absl::nullopt;
+
+  return ArgbToColorRef(type_argb_pair.argb);
 }
 
-Optional<FX_COLORREF> CPDFSDK_Widget::GetTextColor() const {
-  CPDF_FormControl* pFormCtrl = GetFormControl();
-  CPDF_DefaultAppearance da = pFormCtrl->GetDefaultAppearance();
-  FX_ARGB argb;
-  Optional<CFX_Color::Type> iColorType;
-  std::tie(iColorType, argb) = da.GetColor();
-  if (!iColorType.has_value())
-    return {};
+absl::optional<FX_COLORREF> CPDFSDK_Widget::GetTextColor() const {
+  CPDF_DefaultAppearance da = GetFormControl()->GetDefaultAppearance();
+  absl::optional<CFX_Color::TypeAndARGB> maybe_type_argb_pair =
+      da.GetColorARGB();
 
-  FX_COLORREF color = ArgbToColorRef(argb);
-  if (iColorType.value() == CFX_Color::kTransparent)
-    return {};
-  return color;
+  if (!maybe_type_argb_pair.has_value())
+    return absl::nullopt;
+
+  if (maybe_type_argb_pair.value().color_type == CFX_Color::Type::kTransparent)
+    return absl::nullopt;
+
+  return ArgbToColorRef(maybe_type_argb_pair.value().argb);
 }
 
 float CPDFSDK_Widget::GetFontSize() const {
@@ -452,16 +499,16 @@
   if (CXFA_FFWidget* hWidget = GetMixXFAWidget()) {
     CXFA_Node* node = hWidget->GetNode();
     if (node->IsWidgetReady())
-      return node->GetValue(XFA_VALUEPICTURE_Display);
+      return node->GetValue(XFA_ValuePicture::kDisplay);
   }
 #endif  // PDF_ENABLE_XFA
   CPDF_FormField* pFormField = GetFormField();
   return pFormField->GetValue();
 }
 
-WideString CPDFSDK_Widget::GetDefaultValue() const {
-  CPDF_FormField* pFormField = GetFormField();
-  return pFormField->GetDefaultValue();
+WideString CPDFSDK_Widget::GetExportValue() const {
+  CPDF_FormControl* pFormCtrl = GetFormControl();
+  return pFormCtrl->GetExportValue();
 }
 
 WideString CPDFSDK_Widget::GetOptionLabel(int nIndex) const {
@@ -469,6 +516,21 @@
   return pFormField->GetOptionLabel(nIndex);
 }
 
+WideString CPDFSDK_Widget::GetSelectExportText(int nIndex) const {
+  if (nIndex < 0)
+    return WideString();
+
+  CPDF_FormField* pFormField = GetFormField();
+  if (!pFormField)
+    return WideString();
+
+  WideString swRet = pFormField->GetOptionValue(nIndex);
+  if (!swRet.IsEmpty())
+    return swRet;
+
+  return pFormField->GetOptionLabel(nIndex);
+}
+
 int CPDFSDK_Widget::CountOptions() const {
   CPDF_FormField* pFormField = GetFormField();
   return pFormField->CountOptions();
@@ -479,9 +541,10 @@
   if (CXFA_FFWidget* hWidget = GetMixXFAWidget()) {
     CXFA_Node* node = hWidget->GetNode();
     if (node->IsWidgetReady()) {
-      if (nIndex > -1 && nIndex < node->CountChoiceListItems(false))
+      if (nIndex > -1 &&
+          static_cast<size_t>(nIndex) < node->CountChoiceListItems(false)) {
         return node->GetItemState(nIndex);
-
+      }
       return false;
     }
   }
@@ -500,7 +563,7 @@
   if (CXFA_FFWidget* hWidget = GetMixXFAWidget()) {
     CXFA_Node* node = hWidget->GetNode();
     if (node->IsWidgetReady())
-      return node->GetCheckState() == XFA_CHECKSTATE_On;
+      return node->GetCheckState() == XFA_CheckState::kOn;
   }
 #endif  // PDF_ENABLE_XFA
   CPDF_FormControl* pFormCtrl = GetFormControl();
@@ -517,46 +580,39 @@
   return pFormField->GetMaxLen();
 }
 
-void CPDFSDK_Widget::SetCheck(bool bChecked, NotificationOption notify) {
+void CPDFSDK_Widget::SetCheck(bool bChecked) {
   CPDF_FormControl* pFormCtrl = GetFormControl();
   CPDF_FormField* pFormField = pFormCtrl->GetField();
   pFormField->CheckControl(pFormField->GetControlIndex(pFormCtrl), bChecked,
-                           notify);
+                           NotificationOption::kDoNotNotify);
 #ifdef PDF_ENABLE_XFA
-  if (!IsWidgetAppearanceValid(CPDF_Annot::Normal))
-    ResetXFAAppearance(true);
-  if (notify == NotificationOption::kDoNotNotify)
-    Synchronize(true);
+  if (!IsWidgetAppearanceValid(CPDF_Annot::AppearanceMode::kNormal))
+    ResetXFAAppearance(CPDFSDK_Widget::kValueChanged);
+  Synchronize(true);
 #endif  // PDF_ENABLE_XFA
 }
 
-void CPDFSDK_Widget::SetValue(const WideString& sValue,
-                              NotificationOption notify) {
+void CPDFSDK_Widget::SetValue(const WideString& sValue) {
   CPDF_FormField* pFormField = GetFormField();
-  pFormField->SetValue(sValue, notify);
+  pFormField->SetValue(sValue, NotificationOption::kDoNotNotify);
 #ifdef PDF_ENABLE_XFA
-  if (notify == NotificationOption::kDoNotNotify)
-    Synchronize(true);
+  Synchronize(true);
 #endif  // PDF_ENABLE_XFA
 }
 
-void CPDFSDK_Widget::SetOptionSelection(int index,
-                                        bool bSelected,
-                                        NotificationOption notify) {
+void CPDFSDK_Widget::SetOptionSelection(int index) {
   CPDF_FormField* pFormField = GetFormField();
-  pFormField->SetItemSelection(index, bSelected, notify);
+  pFormField->SetItemSelection(index, NotificationOption::kDoNotNotify);
 #ifdef PDF_ENABLE_XFA
-  if (notify == NotificationOption::kDoNotNotify)
-    Synchronize(true);
+  Synchronize(true);
 #endif  // PDF_ENABLE_XFA
 }
 
-void CPDFSDK_Widget::ClearSelection(NotificationOption notify) {
+void CPDFSDK_Widget::ClearSelection() {
   CPDF_FormField* pFormField = GetFormField();
-  pFormField->ClearSelection(notify);
+  pFormField->ClearSelection(NotificationOption::kDoNotNotify);
 #ifdef PDF_ENABLE_XFA
-  if (notify == NotificationOption::kDoNotNotify)
-    Synchronize(true);
+  Synchronize(true);
 #endif  // PDF_ENABLE_XFA
 }
 
@@ -575,29 +631,29 @@
 }
 
 #ifdef PDF_ENABLE_XFA
-void CPDFSDK_Widget::ResetXFAAppearance(bool bValueChanged) {
+void CPDFSDK_Widget::ResetXFAAppearance(ValueChanged bValueChanged) {
   switch (GetFieldType()) {
     case FormFieldType::kTextField:
     case FormFieldType::kComboBox: {
-      ResetAppearance(OnFormat(), true);
+      ResetAppearance(OnFormat(), kValueChanged);
       break;
     }
     default:
-      ResetAppearance(pdfium::nullopt, false);
+      ResetAppearance(absl::nullopt, kValueUnchanged);
       break;
   }
 }
 #endif  // PDF_ENABLE_XFA
 
-void CPDFSDK_Widget::ResetAppearance(Optional<WideString> sValue,
-                                     bool bValueChanged) {
+void CPDFSDK_Widget::ResetAppearance(absl::optional<WideString> sValue,
+                                     ValueChanged bValueChanged) {
   SetAppModified();
 
   m_nAppearanceAge++;
-  if (bValueChanged)
+  if (bValueChanged == kValueChanged)
     m_nValueAge++;
 
-  CPDFSDK_AppStream appStream(this, GetAPDict());
+  CPDFSDK_AppStream appStream(this, GetAPDict().Get());
   switch (GetFieldType()) {
     case FormFieldType::kPushButton:
       appStream.SetAsPushButton();
@@ -621,46 +677,260 @@
       break;
   }
 
-  m_pAnnot->ClearCachedAP();
+  ClearCachedAnnotAP();
 }
 
-Optional<WideString> CPDFSDK_Widget::OnFormat() {
+absl::optional<WideString> CPDFSDK_Widget::OnFormat() {
   CPDF_FormField* pFormField = GetFormField();
-  ASSERT(pFormField);
+  DCHECK(pFormField);
   return m_pInteractiveForm->OnFormat(pFormField);
 }
 
 void CPDFSDK_Widget::ResetFieldAppearance() {
   CPDF_FormField* pFormField = GetFormField();
-  ASSERT(pFormField);
-  m_pInteractiveForm->ResetFieldAppearance(pFormField, pdfium::nullopt);
+  DCHECK(pFormField);
+  m_pInteractiveForm->ResetFieldAppearance(pFormField, absl::nullopt);
+}
+
+void CPDFSDK_Widget::OnDraw(CFX_RenderDevice* pDevice,
+                            const CFX_Matrix& mtUser2Device,
+                            bool bDrawAnnots) {
+  if (IsSignatureWidget()) {
+    DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::AppearanceMode::kNormal);
+    return;
+  }
+
+  GetInteractiveFormFiller()->OnDraw(GetPageView(), this, pDevice,
+                                     mtUser2Device);
+}
+
+bool CPDFSDK_Widget::DoHitTest(const CFX_PointF& point) {
+  if (IsSignatureWidget() || !IsVisible())
+    return false;
+
+  if (GetFieldFlags() & pdfium::form_flags::kReadOnly)
+    return false;
+
+  bool do_hit_test = GetFieldType() == FormFieldType::kPushButton;
+  if (!do_hit_test) {
+    uint32_t perms = GetPDFPage()->GetDocument()->GetUserPermissions();
+    do_hit_test = (perms & pdfium::access_permissions::kFillForm) ||
+                  (perms & pdfium::access_permissions::kModifyAnnotation);
+  }
+  return do_hit_test && GetViewBBox().Contains(point);
+}
+
+CFX_FloatRect CPDFSDK_Widget::GetViewBBox() {
+  if (IsSignatureWidget())
+    return CFX_FloatRect();
+
+  auto* form_filler = GetInteractiveFormFiller();
+  return CFX_FloatRect(form_filler->GetViewBBox(GetPageView(), this));
+}
+
+void CPDFSDK_Widget::OnMouseEnter(Mask<FWL_EVENTFLAG> nFlags) {
+  if (IsSignatureWidget())
+    return;
+
+  ObservedPtr<CPDFSDK_Widget> observer(this);
+  GetInteractiveFormFiller()->OnMouseEnter(GetPageView(), observer, nFlags);
+}
+
+void CPDFSDK_Widget::OnMouseExit(Mask<FWL_EVENTFLAG> nFlags) {
+  if (IsSignatureWidget())
+    return;
+
+  ObservedPtr<CPDFSDK_Widget> observer(this);
+  GetInteractiveFormFiller()->OnMouseExit(GetPageView(), observer, nFlags);
+}
+
+bool CPDFSDK_Widget::OnLButtonDown(Mask<FWL_EVENTFLAG> nFlags,
+                                   const CFX_PointF& point) {
+  if (IsSignatureWidget())
+    return false;
+
+  ObservedPtr<CPDFSDK_Widget> observer(this);
+  return GetInteractiveFormFiller()->OnLButtonDown(GetPageView(), observer,
+                                                   nFlags, point);
+}
+
+bool CPDFSDK_Widget::OnLButtonUp(Mask<FWL_EVENTFLAG> nFlags,
+                                 const CFX_PointF& point) {
+  if (IsSignatureWidget())
+    return false;
+
+  ObservedPtr<CPDFSDK_Widget> observer(this);
+  return GetInteractiveFormFiller()->OnLButtonUp(GetPageView(), observer,
+                                                 nFlags, point);
+}
+
+bool CPDFSDK_Widget::OnLButtonDblClk(Mask<FWL_EVENTFLAG> nFlags,
+                                     const CFX_PointF& point) {
+  if (IsSignatureWidget())
+    return false;
+
+  ObservedPtr<CPDFSDK_Widget> observer(this);
+  return GetInteractiveFormFiller()->OnLButtonDblClk(GetPageView(), observer,
+                                                     nFlags, point);
+}
+
+bool CPDFSDK_Widget::OnMouseMove(Mask<FWL_EVENTFLAG> nFlags,
+                                 const CFX_PointF& point) {
+  if (IsSignatureWidget())
+    return false;
+
+  ObservedPtr<CPDFSDK_Widget> observer(this);
+  return GetInteractiveFormFiller()->OnMouseMove(GetPageView(), observer,
+                                                 nFlags, point);
+}
+
+bool CPDFSDK_Widget::OnMouseWheel(Mask<FWL_EVENTFLAG> nFlags,
+                                  const CFX_PointF& point,
+                                  const CFX_Vector& delta) {
+  if (IsSignatureWidget())
+    return false;
+
+  ObservedPtr<CPDFSDK_Widget> observer(this);
+  return GetInteractiveFormFiller()->OnMouseWheel(GetPageView(), observer,
+                                                  nFlags, point, delta);
+}
+
+bool CPDFSDK_Widget::OnRButtonDown(Mask<FWL_EVENTFLAG> nFlags,
+                                   const CFX_PointF& point) {
+  if (IsSignatureWidget())
+    return false;
+
+  ObservedPtr<CPDFSDK_Widget> observer(this);
+  return GetInteractiveFormFiller()->OnRButtonDown(GetPageView(), observer,
+                                                   nFlags, point);
+}
+
+bool CPDFSDK_Widget::OnRButtonUp(Mask<FWL_EVENTFLAG> nFlags,
+                                 const CFX_PointF& point) {
+  if (IsSignatureWidget())
+    return false;
+
+  ObservedPtr<CPDFSDK_Widget> observer(this);
+  return GetInteractiveFormFiller()->OnRButtonUp(GetPageView(), observer,
+                                                 nFlags, point);
+}
+
+bool CPDFSDK_Widget::OnChar(uint32_t nChar, Mask<FWL_EVENTFLAG> nFlags) {
+  return !IsSignatureWidget() &&
+         GetInteractiveFormFiller()->OnChar(this, nChar, nFlags);
+}
+
+bool CPDFSDK_Widget::OnKeyDown(FWL_VKEYCODE nKeyCode,
+                               Mask<FWL_EVENTFLAG> nFlags) {
+  return !IsSignatureWidget() &&
+         GetInteractiveFormFiller()->OnKeyDown(this, nKeyCode, nFlags);
+}
+
+bool CPDFSDK_Widget::OnSetFocus(Mask<FWL_EVENTFLAG> nFlags) {
+  if (!IsFocusableAnnot(GetPDFAnnot()->GetSubtype()))
+    return false;
+
+  if (IsSignatureWidget())
+    return true;
+
+  ObservedPtr<CPDFSDK_Widget> observer(this);
+  return GetInteractiveFormFiller()->OnSetFocus(observer, nFlags);
+}
+
+bool CPDFSDK_Widget::OnKillFocus(Mask<FWL_EVENTFLAG> nFlags) {
+  if (!IsFocusableAnnot(GetPDFAnnot()->GetSubtype()))
+    return false;
+
+  if (IsSignatureWidget())
+    return true;
+
+  ObservedPtr<CPDFSDK_Widget> observer(this);
+  return GetInteractiveFormFiller()->OnKillFocus(observer, nFlags);
+}
+
+bool CPDFSDK_Widget::CanUndo() {
+  return !IsSignatureWidget() && GetInteractiveFormFiller()->CanUndo(this);
+}
+
+bool CPDFSDK_Widget::CanRedo() {
+  return !IsSignatureWidget() && GetInteractiveFormFiller()->CanRedo(this);
+}
+
+bool CPDFSDK_Widget::Undo() {
+  return !IsSignatureWidget() && GetInteractiveFormFiller()->Undo(this);
+}
+
+bool CPDFSDK_Widget::Redo() {
+  return !IsSignatureWidget() && GetInteractiveFormFiller()->Redo(this);
+}
+
+WideString CPDFSDK_Widget::GetText() {
+  if (IsSignatureWidget())
+    return WideString();
+  return GetInteractiveFormFiller()->GetText(this);
+}
+
+WideString CPDFSDK_Widget::GetSelectedText() {
+  if (IsSignatureWidget())
+    return WideString();
+  return GetInteractiveFormFiller()->GetSelectedText(this);
+}
+
+void CPDFSDK_Widget::ReplaceAndKeepSelection(const WideString& text) {
+  if (IsSignatureWidget())
+    return;
+
+  GetInteractiveFormFiller()->ReplaceAndKeepSelection(this, text);
+}
+
+void CPDFSDK_Widget::ReplaceSelection(const WideString& text) {
+  if (IsSignatureWidget())
+    return;
+
+  GetInteractiveFormFiller()->ReplaceSelection(this, text);
+}
+
+bool CPDFSDK_Widget::SelectAllText() {
+  return !IsSignatureWidget() &&
+         GetInteractiveFormFiller()->SelectAllText(this);
+}
+
+bool CPDFSDK_Widget::SetIndexSelected(int index, bool selected) {
+  ObservedPtr<CPDFSDK_Widget> observer(this);
+  return !IsSignatureWidget() && GetInteractiveFormFiller()->SetIndexSelected(
+                                     observer, index, selected);
+}
+
+bool CPDFSDK_Widget::IsIndexSelected(int index) {
+  ObservedPtr<CPDFSDK_Widget> observer(this);
+  return !IsSignatureWidget() &&
+         GetInteractiveFormFiller()->IsIndexSelected(observer, index);
 }
 
 void CPDFSDK_Widget::DrawAppearance(CFX_RenderDevice* pDevice,
                                     const CFX_Matrix& mtUser2Device,
-                                    CPDF_Annot::AppearanceMode mode,
-                                    const CPDF_RenderOptions* pOptions) {
+                                    CPDF_Annot::AppearanceMode mode) {
   FormFieldType fieldType = GetFieldType();
 
   if ((fieldType == FormFieldType::kCheckBox ||
        fieldType == FormFieldType::kRadioButton) &&
-      mode == CPDF_Annot::Normal &&
-      !IsWidgetAppearanceValid(CPDF_Annot::Normal)) {
+      mode == CPDF_Annot::AppearanceMode::kNormal &&
+      !IsWidgetAppearanceValid(CPDF_Annot::AppearanceMode::kNormal)) {
     CFX_GraphStateData gsd;
     gsd.m_LineWidth = 0.0f;
 
-    CFX_PathData pathData;
-    pathData.AppendFloatRect(GetRect());
-    pDevice->DrawPath(&pathData, &mtUser2Device, &gsd, 0, 0xFFAAAAAA,
-                      FXFILL_ALTERNATE);
+    CFX_Path path;
+    path.AppendFloatRect(GetRect());
+    pDevice->DrawPath(path, &mtUser2Device, &gsd, 0, 0xFFAAAAAA,
+                      CFX_FillRenderOptions::EvenOddOptions());
   } else {
-    CPDFSDK_BAAnnot::DrawAppearance(pDevice, mtUser2Device, mode, pOptions);
+    CPDFSDK_BAAnnot::DrawAppearance(pDevice, mtUser2Device, mode);
   }
 }
 
 void CPDFSDK_Widget::UpdateField() {
   CPDF_FormField* pFormField = GetFormField();
-  ASSERT(pFormField);
+  DCHECK(pFormField);
   m_pInteractiveForm->UpdateField(pFormField);
 }
 
@@ -693,8 +963,8 @@
   CFX_FloatRect rcWindow = GetRotatedRect();
   float fBorderWidth = GetBorderWidth();
   switch (GetBorderStyle()) {
-    case BorderStyle::BEVELED:
-    case BorderStyle::INSET:
+    case BorderStyle::kBeveled:
+    case BorderStyle::kInset:
       fBorderWidth *= 2.0f;
       break;
     default:
@@ -751,90 +1021,67 @@
 }
 
 CFX_Color CPDFSDK_Widget::GetTextPWLColor() const {
-  CFX_Color crText = CFX_Color(CFX_Color::kGray, 0);
-
   CPDF_FormControl* pFormCtrl = GetFormControl();
-  CPDF_DefaultAppearance da = pFormCtrl->GetDefaultAppearance();
-
-  float fc[4];
-  Optional<CFX_Color::Type> iColorType = da.GetColor(fc);
-  if (iColorType)
-    crText = CFX_Color(*iColorType, fc[0], fc[1], fc[2], fc[3]);
-
-  return crText;
+  absl::optional<CFX_Color> crText =
+      pFormCtrl->GetDefaultAppearance().GetColor();
+  return crText.value_or(CFX_Color(CFX_Color::Type::kGray, 0));
 }
 
 CFX_Color CPDFSDK_Widget::GetBorderPWLColor() const {
-  CFX_Color crBorder;
-
   CPDF_FormControl* pFormCtrl = GetFormControl();
-  int32_t iColorType;
-  float fc[4];
-  pFormCtrl->GetOriginalBorderColor(iColorType, fc);
-  if (iColorType > 0)
-    crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-
-  return crBorder;
+  return pFormCtrl->GetOriginalBorderColor();
 }
 
 CFX_Color CPDFSDK_Widget::GetFillPWLColor() const {
-  CFX_Color crFill;
-
   CPDF_FormControl* pFormCtrl = GetFormControl();
-  int32_t iColorType;
-  float fc[4];
-  pFormCtrl->GetOriginalBackgroundColor(iColorType, fc);
-  if (iColorType > 0)
-    crFill = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-
-  return crFill;
+  return pFormCtrl->GetOriginalBackgroundColor();
 }
 
 bool CPDFSDK_Widget::OnAAction(CPDF_AAction::AActionType type,
-                               CPDFSDK_FieldAction* data,
-                               CPDFSDK_PageView* pPageView) {
+                               CFFL_FieldAction* data,
+                               const CPDFSDK_PageView* pPageView) {
   CPDFSDK_FormFillEnvironment* pFormFillEnv = pPageView->GetFormFillEnv();
 
 #ifdef PDF_ENABLE_XFA
-  auto* pContext =
-      static_cast<CPDFXFA_Context*>(pFormFillEnv->GetDocExtension());
-  if (pContext) {
-    CXFA_FFWidget* hWidget = GetMixXFAWidget();
-    if (hWidget) {
-      XFA_EVENTTYPE eEventType = GetXFAEventType(type, data->bWillCommit);
-      if (eEventType != XFA_EVENT_Unknown) {
-        if (CXFA_FFWidgetHandler* pXFAWidgetHandler = GetXFAWidgetHandler()) {
-          CXFA_EventParam param;
-          param.m_eType = eEventType;
-          param.m_wsChange = data->sChange;
-          param.m_iCommitKey = 0;
-          param.m_bShift = data->bShift;
-          param.m_iSelStart = data->nSelStart;
-          param.m_iSelEnd = data->nSelEnd;
-          param.m_wsFullText = data->sValue;
-          param.m_bKeyDown = data->bKeyDown;
-          param.m_bModifier = data->bModifier;
-          param.m_wsPrevText = data->sValue;
-          bool ret =
-              hWidget->ProcessEventUnderHandler(&param, pXFAWidgetHandler);
-          if (CXFA_FFDocView* pDocView = pContext->GetXFADocView())
-            pDocView->UpdateDocView();
-          if (ret)
-            return true;
-        }
-      }
-    }
-  }
+  if (HandleXFAAAction(type, data, pFormFillEnv))
+    return true;
 #endif  // PDF_ENABLE_XFA
 
   CPDF_Action action = GetAAction(type);
-  if (action.GetType() != CPDF_Action::Unknown) {
-    pFormFillEnv->GetActionHandler()->DoAction_Field(action, type, pFormFillEnv,
-                                                     GetFormField(), data);
+  if (action.GetType() != CPDF_Action::Type::kUnknown) {
+    pFormFillEnv->DoActionField(action, type, GetFormField(), data);
   }
   return false;
 }
 
+void CPDFSDK_Widget::OnLoad() {
+  if (IsSignatureWidget())
+    return;
+
+  if (!IsAppearanceValid())
+    ResetAppearance(absl::nullopt, CPDFSDK_Widget::kValueUnchanged);
+
+  FormFieldType field_type = GetFieldType();
+  if (field_type == FormFieldType::kTextField ||
+      field_type == FormFieldType::kComboBox) {
+    ObservedPtr<CPDFSDK_Annot> pObserved(this);
+    absl::optional<WideString> sValue = OnFormat();
+    if (!pObserved)
+      return;
+
+    if (sValue.has_value() && field_type == FormFieldType::kComboBox)
+      ResetAppearance(sValue, CPDFSDK_Widget::kValueUnchanged);
+  }
+
+#ifdef PDF_ENABLE_XFA
+  auto* pContext = GetPageView()->GetFormFillEnv()->GetDocExtension();
+  if (pContext && pContext->ContainsExtensionForegroundForm()) {
+    if (!IsAppearanceValid() && !GetValue().IsEmpty())
+      ResetXFAAppearance(CPDFSDK_Widget::kValueUnchanged);
+  }
+#endif  // PDF_ENABLE_XFA
+}
+
 CPDF_Action CPDFSDK_Widget::GetAAction(CPDF_AAction::AActionType eAAT) {
   switch (eAAT) {
     case CPDF_AAction::kCursorEnter:
@@ -854,7 +1101,7 @@
     case CPDF_AAction::kValidate:
     case CPDF_AAction::kCalculate: {
       CPDF_FormField* pField = GetFormField();
-      if (pField->GetAdditionalAction().GetDict())
+      if (pField->GetAdditionalAction().HasDict())
         return pField->GetAdditionalAction().GetAction(eAAT);
       return CPDFSDK_BAAnnot::GetAAction(eAAT);
     }
@@ -865,7 +1112,6 @@
   return CPDF_Action(nullptr);
 }
 
-WideString CPDFSDK_Widget::GetAlternateName() const {
-  CPDF_FormField* pFormField = GetFormField();
-  return pFormField->GetAlternateName();
+CFFL_InteractiveFormFiller* CPDFSDK_Widget::GetInteractiveFormFiller() {
+  return GetPageView()->GetFormFillEnv()->GetInteractiveFormFiller();
 }
diff --git a/fpdfsdk/cpdfsdk_widget.h b/fpdfsdk/cpdfsdk_widget.h
index 3037a16..ce68cfb 100644
--- a/fpdfsdk/cpdfsdk_widget.h
+++ b/fpdfsdk/cpdfsdk_widget.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,22 +12,21 @@
 #include "core/fpdfdoc/cpdf_annot.h"
 #include "core/fpdfdoc/cpdf_formfield.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
 #include "core/fxge/cfx_color.h"
 #include "fpdfsdk/cpdfsdk_baannot.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
+class CFFL_InteractiveFormFiller;
 class CFX_RenderDevice;
 class CPDF_Annot;
-class CPDF_Dictionary;
 class CPDF_FormControl;
 class CPDF_FormField;
-class CPDF_RenderOptions;
-class CPDF_Stream;
+class CPDFSDK_FormFillEnvironment;
 class CPDFSDK_InteractiveForm;
 class CPDFSDK_PageView;
-struct CPDFSDK_FieldAction;
+struct CFFL_FieldAction;
 
 #ifdef PDF_ENABLE_XFA
 class CXFA_FFWidget;
@@ -43,72 +42,89 @@
 
 class CPDFSDK_Widget final : public CPDFSDK_BAAnnot {
  public:
-#ifdef PDF_ENABLE_XFA
-  CXFA_FFWidget* GetMixXFAWidget() const;
-  CXFA_FFWidgetHandler* GetXFAWidgetHandler() const;
-
-  bool HasXFAAAction(PDFSDK_XFAAActionType eXFAAAT) const;
-  bool OnXFAAAction(PDFSDK_XFAAActionType eXFAAAT,
-                    CPDFSDK_FieldAction* data,
-                    CPDFSDK_PageView* pPageView);
-
-  void Synchronize(bool bSynchronizeElse);
-#endif  // PDF_ENABLE_XFA
+  enum ValueChanged : bool { kValueUnchanged = false, kValueChanged = true };
 
   CPDFSDK_Widget(CPDF_Annot* pAnnot,
                  CPDFSDK_PageView* pPageView,
                  CPDFSDK_InteractiveForm* pInteractiveForm);
   ~CPDFSDK_Widget() override;
 
-  bool IsSignatureWidget() const override;
+  // CPDFSDK_BAAnnot:
+  void OnLoad() override;
   CPDF_Action GetAAction(CPDF_AAction::AActionType eAAT) override;
   bool IsAppearanceValid() override;
-
   int GetLayoutOrder() const override;
+  void OnDraw(CFX_RenderDevice* pDevice,
+              const CFX_Matrix& mtUser2Device,
+              bool bDrawAnnots) override;
+  bool DoHitTest(const CFX_PointF& point) override;
+  CFX_FloatRect GetViewBBox() override;
+  bool CanUndo() override;
+  bool CanRedo() override;
+  bool Undo() override;
+  bool Redo() override;
+  WideString GetText() override;
+  WideString GetSelectedText() override;
+  void ReplaceAndKeepSelection(const WideString& text) override;
+  void ReplaceSelection(const WideString& text) override;
+  bool SelectAllText() override;
+  bool SetIndexSelected(int index, bool selected) override;
+  bool IsIndexSelected(int index) override;
+  void DrawAppearance(CFX_RenderDevice* pDevice,
+                      const CFX_Matrix& mtUser2Device,
+                      CPDF_Annot::AppearanceMode mode) override;
 
+  bool IsSignatureWidget() const;
+  void SetRect(const CFX_FloatRect& rect);
   FormFieldType GetFieldType() const;
   int GetFieldFlags() const;
   int GetRotate() const;
 
-  Optional<FX_COLORREF> GetFillColor() const;
-  Optional<FX_COLORREF> GetBorderColor() const;
-  Optional<FX_COLORREF> GetTextColor() const;
+  absl::optional<FX_COLORREF> GetFillColor() const;
+  absl::optional<FX_COLORREF> GetBorderColor() const;
+  absl::optional<FX_COLORREF> GetTextColor() const;
   float GetFontSize() const;
 
   int GetSelectedIndex(int nIndex) const;
   WideString GetValue() const;
-  WideString GetDefaultValue() const;
+  WideString GetExportValue() const;
   WideString GetOptionLabel(int nIndex) const;
+  WideString GetSelectExportText(int nIndex) const;
+
   int CountOptions() const;
   bool IsOptionSelected(int nIndex) const;
   int GetTopVisibleIndex() const;
   bool IsChecked() const;
   int GetAlignment() const;
   int GetMaxLen() const;
-  WideString GetAlternateName() const;
 
-  void SetCheck(bool bChecked, NotificationOption notify);
-  void SetValue(const WideString& sValue, NotificationOption notify);
-  void SetOptionSelection(int index, bool bSelected, NotificationOption notify);
-  void ClearSelection(NotificationOption notify);
+  void SetCheck(bool bChecked);
+  void SetValue(const WideString& sValue);
+  void SetOptionSelection(int index);
+  void ClearSelection();
   void SetTopVisibleIndex(int index);
 
 #ifdef PDF_ENABLE_XFA
+  CXFA_FFWidget* GetMixXFAWidget() const;
+  bool HasXFAAAction(PDFSDK_XFAAActionType eXFAAAT) const;
+  bool OnXFAAAction(PDFSDK_XFAAActionType eXFAAAT,
+                    CFFL_FieldAction* data,
+                    const CPDFSDK_PageView* pPageView);
+  void Synchronize(bool bSynchronizeElse);
   // TODO(thestig): Figure out if the parameter should be used or removed.
-  void ResetXFAAppearance(bool bValueChanged);
+  void ResetXFAAppearance(ValueChanged bValueChanged);
 #endif  // PDF_ENABLE_XFA
-  void ResetAppearance(Optional<WideString> sValue, bool bValueChanged);
+
+  void ResetAppearance(absl::optional<WideString> sValue,
+                       ValueChanged bValueChanged);
   void ResetFieldAppearance();
   void UpdateField();
-  Optional<WideString> OnFormat();
+  absl::optional<WideString> OnFormat();
 
   bool OnAAction(CPDF_AAction::AActionType type,
-                 CPDFSDK_FieldAction* data,
-                 CPDFSDK_PageView* pPageView);
+                 CFFL_FieldAction* data,
+                 const CPDFSDK_PageView* pPageView);
 
-  CPDFSDK_InteractiveForm* GetInteractiveForm() const {
-    return m_pInteractiveForm.Get();
-  }
   CPDF_FormField* GetFormField() const;
   CPDF_FormControl* GetFormControl() const;
 
@@ -121,12 +137,8 @@
   uint32_t GetAppearanceAge() const { return m_nAppearanceAge; }
   uint32_t GetValueAge() const { return m_nValueAge; }
 
-  bool IsWidgetAppearanceValid(CPDF_Annot::AppearanceMode mode);
-  void DrawAppearance(CFX_RenderDevice* pDevice,
-                      const CFX_Matrix& mtUser2Device,
-                      CPDF_Annot::AppearanceMode mode,
-                      const CPDF_RenderOptions* pOptions) override;
-
+  bool IsWidgetAppearanceValid(CPDF_Annot::AppearanceMode mode) const;
+  bool IsPushHighlighted() const;
   CFX_Matrix GetMatrix() const;
   CFX_FloatRect GetClientRect() const;
   CFX_FloatRect GetRotatedRect() const;
@@ -135,19 +147,44 @@
   CFX_Color GetFillPWLColor() const;
 
  private:
+  // CPDFSDK_Annot::UnsafeInputHandlers:
+  void OnMouseEnter(Mask<FWL_EVENTFLAG> nFlags) override;
+  void OnMouseExit(Mask<FWL_EVENTFLAG> nFlags) override;
+  bool OnLButtonDown(Mask<FWL_EVENTFLAG> nFlags,
+                     const CFX_PointF& point) override;
+  bool OnLButtonUp(Mask<FWL_EVENTFLAG> nFlags,
+                   const CFX_PointF& point) override;
+  bool OnLButtonDblClk(Mask<FWL_EVENTFLAG> nFlags,
+                       const CFX_PointF& point) override;
+  bool OnMouseMove(Mask<FWL_EVENTFLAG> nFlags,
+                   const CFX_PointF& point) override;
+  bool OnMouseWheel(Mask<FWL_EVENTFLAG> nFlags,
+                    const CFX_PointF& point,
+                    const CFX_Vector& delta) override;
+  bool OnRButtonDown(Mask<FWL_EVENTFLAG> nFlags,
+                     const CFX_PointF& point) override;
+  bool OnRButtonUp(Mask<FWL_EVENTFLAG> nFlags,
+                   const CFX_PointF& point) override;
+  bool OnChar(uint32_t nChar, Mask<FWL_EVENTFLAG> nFlags) override;
+  bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlags) override;
+  bool OnSetFocus(Mask<FWL_EVENTFLAG> nFlags) override;
+  bool OnKillFocus(Mask<FWL_EVENTFLAG> nFlags) override;
+
+  CFFL_InteractiveFormFiller* GetInteractiveFormFiller();
+
 #ifdef PDF_ENABLE_XFA
+  CXFA_FFWidgetHandler* GetXFAWidgetHandler() const;
   CXFA_FFWidget* GetGroupMixXFAWidget() const;
   WideString GetName() const;
+  bool HandleXFAAAction(CPDF_AAction::AActionType type,
+                        CFFL_FieldAction* data,
+                        CPDFSDK_FormFillEnvironment* pFormFillEnv);
 #endif  // PDF_ENABLE_XFA
 
   UnownedPtr<CPDFSDK_InteractiveForm> const m_pInteractiveForm;
   bool m_bAppModified = false;
   uint32_t m_nAppearanceAge = 0;
   uint32_t m_nValueAge = 0;
-
-#ifdef PDF_ENABLE_XFA
-  mutable UnownedPtr<CXFA_FFWidgetHandler> m_pWidgetHandler;
-#endif  // PDF_ENABLE_XFA
 };
 
 inline CPDFSDK_Widget* ToCPDFSDKWidget(CPDFSDK_Annot* pAnnot) {
diff --git a/fpdfsdk/cpdfsdk_widgethandler.cpp b/fpdfsdk/cpdfsdk_widgethandler.cpp
deleted file mode 100644
index a3b2394..0000000
--- a/fpdfsdk/cpdfsdk_widgethandler.cpp
+++ /dev/null
@@ -1,293 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/cpdfsdk_widgethandler.h"
-
-#include <memory>
-#include <vector>
-
-#include "constants/form_flags.h"
-#include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfdoc/cpdf_interactiveform.h"
-#include "fpdfsdk/cpdfsdk_annot.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_interactiveform.h"
-#include "fpdfsdk/cpdfsdk_pageview.h"
-#include "fpdfsdk/cpdfsdk_widget.h"
-#include "fpdfsdk/formfiller/cffl_formfiller.h"
-
-CPDFSDK_WidgetHandler::CPDFSDK_WidgetHandler() = default;
-
-CPDFSDK_WidgetHandler::~CPDFSDK_WidgetHandler() = default;
-
-void CPDFSDK_WidgetHandler::SetFormFillEnvironment(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pFormFillEnv = pFormFillEnv;
-  m_pFormFiller = m_pFormFillEnv->GetInteractiveFormFiller();
-}
-
-bool CPDFSDK_WidgetHandler::CanAnswer(CPDFSDK_Annot* pAnnot) {
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
-  if (pWidget->IsSignatureWidget())
-    return false;
-
-  if (!pWidget->IsVisible())
-    return false;
-
-  int nFieldFlags = pWidget->GetFieldFlags();
-  if (nFieldFlags & pdfium::form_flags::kReadOnly)
-    return false;
-
-  if (pWidget->GetFieldType() == FormFieldType::kPushButton)
-    return true;
-
-  CPDF_Page* pPage = pWidget->GetPDFPage();
-  uint32_t dwPermissions = pPage->GetDocument()->GetUserPermissions();
-  return (dwPermissions & FPDFPERM_FILL_FORM) ||
-         (dwPermissions & FPDFPERM_ANNOT_FORM);
-}
-
-CPDFSDK_Annot* CPDFSDK_WidgetHandler::NewAnnot(CPDF_Annot* pAnnot,
-                                               CPDFSDK_PageView* pPage) {
-  CPDFSDK_InteractiveForm* pForm = m_pFormFillEnv->GetInteractiveForm();
-  CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
-  CPDF_FormControl* pCtrl = pPDFForm->GetControlByDict(pAnnot->GetAnnotDict());
-  if (!pCtrl)
-    return nullptr;
-
-  CPDFSDK_Widget* pWidget = new CPDFSDK_Widget(pAnnot, pPage, pForm);
-  pForm->AddMap(pCtrl, pWidget);
-  if (pPDFForm->NeedConstructAP())
-    pWidget->ResetAppearance(pdfium::nullopt, false);
-  return pWidget;
-}
-
-void CPDFSDK_WidgetHandler::ReleaseAnnot(
-    std::unique_ptr<CPDFSDK_Annot> pAnnot) {
-  ASSERT(pAnnot);
-  m_pFormFiller->OnDelete(pAnnot.get());
-
-  std::unique_ptr<CPDFSDK_Widget> pWidget(ToCPDFSDKWidget(pAnnot.release()));
-  CPDFSDK_InteractiveForm* pForm = pWidget->GetInteractiveForm();
-  CPDF_FormControl* pControl = pWidget->GetFormControl();
-  pForm->RemoveMap(pControl);
-}
-
-void CPDFSDK_WidgetHandler::OnDraw(CPDFSDK_PageView* pPageView,
-                                   CPDFSDK_Annot* pAnnot,
-                                   CFX_RenderDevice* pDevice,
-                                   const CFX_Matrix& mtUser2Device,
-                                   bool bDrawAnnots) {
-  if (pAnnot->IsSignatureWidget()) {
-    pAnnot->AsBAAnnot()->DrawAppearance(pDevice, mtUser2Device,
-                                        CPDF_Annot::Normal, nullptr);
-  } else {
-    m_pFormFiller->OnDraw(pPageView, pAnnot, pDevice, mtUser2Device);
-  }
-}
-
-void CPDFSDK_WidgetHandler::OnMouseEnter(CPDFSDK_PageView* pPageView,
-                                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                         uint32_t nFlag) {
-  if (!(*pAnnot)->IsSignatureWidget())
-    m_pFormFiller->OnMouseEnter(pPageView, pAnnot, nFlag);
-}
-
-void CPDFSDK_WidgetHandler::OnMouseExit(CPDFSDK_PageView* pPageView,
-                                        ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                        uint32_t nFlag) {
-  if (!(*pAnnot)->IsSignatureWidget())
-    m_pFormFiller->OnMouseExit(pPageView, pAnnot, nFlag);
-}
-
-bool CPDFSDK_WidgetHandler::OnLButtonDown(CPDFSDK_PageView* pPageView,
-                                          ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                          uint32_t nFlags,
-                                          const CFX_PointF& point) {
-  return !(*pAnnot)->IsSignatureWidget() &&
-         m_pFormFiller->OnLButtonDown(pPageView, pAnnot, nFlags, point);
-}
-
-bool CPDFSDK_WidgetHandler::OnLButtonUp(CPDFSDK_PageView* pPageView,
-                                        ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                        uint32_t nFlags,
-                                        const CFX_PointF& point) {
-  return !(*pAnnot)->IsSignatureWidget() &&
-         m_pFormFiller->OnLButtonUp(pPageView, pAnnot, nFlags, point);
-}
-
-bool CPDFSDK_WidgetHandler::OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                                            ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                            uint32_t nFlags,
-                                            const CFX_PointF& point) {
-  return !(*pAnnot)->IsSignatureWidget() &&
-         m_pFormFiller->OnLButtonDblClk(pPageView, pAnnot, nFlags, point);
-}
-
-bool CPDFSDK_WidgetHandler::OnMouseMove(CPDFSDK_PageView* pPageView,
-                                        ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                        uint32_t nFlags,
-                                        const CFX_PointF& point) {
-  return !(*pAnnot)->IsSignatureWidget() &&
-         m_pFormFiller->OnMouseMove(pPageView, pAnnot, nFlags, point);
-}
-
-bool CPDFSDK_WidgetHandler::OnMouseWheel(CPDFSDK_PageView* pPageView,
-                                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                         uint32_t nFlags,
-                                         short zDelta,
-                                         const CFX_PointF& point) {
-  return !(*pAnnot)->IsSignatureWidget() &&
-         m_pFormFiller->OnMouseWheel(pPageView, pAnnot, nFlags, zDelta, point);
-}
-
-bool CPDFSDK_WidgetHandler::OnRButtonDown(CPDFSDK_PageView* pPageView,
-                                          ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                          uint32_t nFlags,
-                                          const CFX_PointF& point) {
-  return !(*pAnnot)->IsSignatureWidget() &&
-         m_pFormFiller->OnRButtonDown(pPageView, pAnnot, nFlags, point);
-}
-
-bool CPDFSDK_WidgetHandler::OnRButtonUp(CPDFSDK_PageView* pPageView,
-                                        ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                        uint32_t nFlags,
-                                        const CFX_PointF& point) {
-  return !(*pAnnot)->IsSignatureWidget() &&
-         m_pFormFiller->OnRButtonUp(pPageView, pAnnot, nFlags, point);
-}
-
-bool CPDFSDK_WidgetHandler::OnRButtonDblClk(CPDFSDK_PageView* pPageView,
-                                            ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                            uint32_t nFlags,
-                                            const CFX_PointF& point) {
-  return false;
-}
-
-bool CPDFSDK_WidgetHandler::OnChar(CPDFSDK_Annot* pAnnot,
-                                   uint32_t nChar,
-                                   uint32_t nFlags) {
-  return !pAnnot->IsSignatureWidget() &&
-         m_pFormFiller->OnChar(pAnnot, nChar, nFlags);
-}
-
-bool CPDFSDK_WidgetHandler::OnKeyDown(CPDFSDK_Annot* pAnnot,
-                                      int nKeyCode,
-                                      int nFlag) {
-  return !pAnnot->IsSignatureWidget() &&
-         m_pFormFiller->OnKeyDown(pAnnot, nKeyCode, nFlag);
-}
-
-bool CPDFSDK_WidgetHandler::OnKeyUp(CPDFSDK_Annot* pAnnot,
-                                    int nKeyCode,
-                                    int nFlag) {
-  return false;
-}
-
-void CPDFSDK_WidgetHandler::OnLoad(CPDFSDK_Annot* pAnnot) {
-  if (pAnnot->IsSignatureWidget())
-    return;
-
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
-  if (!pWidget->IsAppearanceValid())
-    pWidget->ResetAppearance(pdfium::nullopt, false);
-
-  FormFieldType fieldType = pWidget->GetFieldType();
-  if (fieldType == FormFieldType::kTextField ||
-      fieldType == FormFieldType::kComboBox) {
-    ObservedPtr<CPDFSDK_Annot> pObserved(pWidget);
-    Optional<WideString> sValue = pWidget->OnFormat();
-    if (!pObserved)
-      return;
-
-    if (sValue.has_value() && fieldType == FormFieldType::kComboBox)
-      pWidget->ResetAppearance(sValue, false);
-  }
-
-#ifdef PDF_ENABLE_XFA
-  CPDFSDK_PageView* pPageView = pAnnot->GetPageView();
-  auto* pContext = pPageView->GetFormFillEnv()->GetDocExtension();
-  if (pContext && pContext->ContainsExtensionForegroundForm()) {
-    if (!pWidget->IsAppearanceValid() && !pWidget->GetValue().IsEmpty())
-      pWidget->ResetXFAAppearance(false);
-  }
-#endif  // PDF_ENABLE_XFA
-}
-
-bool CPDFSDK_WidgetHandler::OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                       uint32_t nFlag) {
-  return (*pAnnot)->IsSignatureWidget() ||
-         m_pFormFiller->OnSetFocus(pAnnot, nFlag);
-}
-
-bool CPDFSDK_WidgetHandler::OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                        uint32_t nFlag) {
-  return (*pAnnot)->IsSignatureWidget() ||
-         m_pFormFiller->OnKillFocus(pAnnot, nFlag);
-}
-
-bool CPDFSDK_WidgetHandler::SetIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                             int index,
-                                             bool selected) {
-  return !(*pAnnot)->IsSignatureWidget() &&
-         m_pFormFiller->SetIndexSelected(pAnnot, index, selected);
-}
-
-bool CPDFSDK_WidgetHandler::IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                            int index) {
-  return !(*pAnnot)->IsSignatureWidget() &&
-         m_pFormFiller->IsIndexSelected(pAnnot, index);
-}
-
-CFX_FloatRect CPDFSDK_WidgetHandler::GetViewBBox(CPDFSDK_PageView* pPageView,
-                                                 CPDFSDK_Annot* pAnnot) {
-  if (!pAnnot->IsSignatureWidget())
-    return CFX_FloatRect(m_pFormFiller->GetViewBBox(pPageView, pAnnot));
-  return CFX_FloatRect();
-}
-
-WideString CPDFSDK_WidgetHandler::GetText(CPDFSDK_Annot* pAnnot) {
-  if (!pAnnot->IsSignatureWidget())
-    return m_pFormFiller->GetText(pAnnot);
-  return WideString();
-}
-
-WideString CPDFSDK_WidgetHandler::GetSelectedText(CPDFSDK_Annot* pAnnot) {
-  if (!pAnnot->IsSignatureWidget())
-    return m_pFormFiller->GetSelectedText(pAnnot);
-  return WideString();
-}
-
-void CPDFSDK_WidgetHandler::ReplaceSelection(CPDFSDK_Annot* pAnnot,
-                                             const WideString& text) {
-  if (!pAnnot->IsSignatureWidget())
-    m_pFormFiller->ReplaceSelection(pAnnot, text);
-}
-
-bool CPDFSDK_WidgetHandler::CanUndo(CPDFSDK_Annot* pAnnot) {
-  return !pAnnot->IsSignatureWidget() && m_pFormFiller->CanUndo(pAnnot);
-}
-
-bool CPDFSDK_WidgetHandler::CanRedo(CPDFSDK_Annot* pAnnot) {
-  return !pAnnot->IsSignatureWidget() && m_pFormFiller->CanRedo(pAnnot);
-}
-
-bool CPDFSDK_WidgetHandler::Undo(CPDFSDK_Annot* pAnnot) {
-  return !pAnnot->IsSignatureWidget() && m_pFormFiller->Undo(pAnnot);
-}
-
-bool CPDFSDK_WidgetHandler::Redo(CPDFSDK_Annot* pAnnot) {
-  return !pAnnot->IsSignatureWidget() && m_pFormFiller->Redo(pAnnot);
-}
-
-bool CPDFSDK_WidgetHandler::HitTest(CPDFSDK_PageView* pPageView,
-                                    CPDFSDK_Annot* pAnnot,
-                                    const CFX_PointF& point) {
-  ASSERT(pPageView);
-  ASSERT(pAnnot);
-  return GetViewBBox(pPageView, pAnnot).Contains(point);
-}
diff --git a/fpdfsdk/cpdfsdk_widgethandler.h b/fpdfsdk/cpdfsdk_widgethandler.h
deleted file mode 100644
index fa2ac94..0000000
--- a/fpdfsdk/cpdfsdk_widgethandler.h
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_CPDFSDK_WIDGETHANDLER_H_
-#define FPDFSDK_CPDFSDK_WIDGETHANDLER_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "fpdfsdk/ipdfsdk_annothandler.h"
-
-class CFFL_InteractiveFormFiller;
-class CFX_Matrix;
-class CFX_RenderDevice;
-class CPDF_Annot;
-class CPDFSDK_Annot;
-class CPDFSDK_FormFillEnvironment;
-class CPDFSDK_PageView;
-
-class CPDFSDK_WidgetHandler final : public IPDFSDK_AnnotHandler {
- public:
-  CPDFSDK_WidgetHandler();
-  ~CPDFSDK_WidgetHandler() override;
-
-  void SetFormFillEnvironment(
-      CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  bool CanAnswer(CPDFSDK_Annot* pAnnot) override;
-  CPDFSDK_Annot* NewAnnot(CPDF_Annot* pAnnot, CPDFSDK_PageView* pPage) override;
-  void ReleaseAnnot(std::unique_ptr<CPDFSDK_Annot> pAnnot) override;
-  CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView,
-                            CPDFSDK_Annot* pAnnot) override;
-  WideString GetText(CPDFSDK_Annot* pAnnot) override;
-  WideString GetSelectedText(CPDFSDK_Annot* pAnnot) override;
-  void ReplaceSelection(CPDFSDK_Annot* pAnnot, const WideString& text) override;
-  bool CanUndo(CPDFSDK_Annot* pAnnot) override;
-  bool CanRedo(CPDFSDK_Annot* pAnnot) override;
-  bool Undo(CPDFSDK_Annot* pAnnot) override;
-  bool Redo(CPDFSDK_Annot* pAnnot) override;
-  bool HitTest(CPDFSDK_PageView* pPageView,
-               CPDFSDK_Annot* pAnnot,
-               const CFX_PointF& point) override;
-  void OnDraw(CPDFSDK_PageView* pPageView,
-              CPDFSDK_Annot* pAnnot,
-              CFX_RenderDevice* pDevice,
-              const CFX_Matrix& mtUser2Device,
-              bool bDrawAnnots) override;
-  void OnLoad(CPDFSDK_Annot* pAnnot) override;
-
-  void OnMouseEnter(CPDFSDK_PageView* pPageView,
-                    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                    uint32_t nFlag) override;
-  void OnMouseExit(CPDFSDK_PageView* pPageView,
-                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   uint32_t nFlag) override;
-  bool OnLButtonDown(CPDFSDK_PageView* pPageView,
-                     ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                     uint32_t nFlags,
-                     const CFX_PointF& point) override;
-  bool OnLButtonUp(CPDFSDK_PageView* pPageView,
-                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   uint32_t nFlags,
-                   const CFX_PointF& point) override;
-  bool OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                       ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                       uint32_t nFlags,
-                       const CFX_PointF& point) override;
-  bool OnMouseMove(CPDFSDK_PageView* pPageView,
-                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   uint32_t nFlags,
-                   const CFX_PointF& point) override;
-  bool OnMouseWheel(CPDFSDK_PageView* pPageView,
-                    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                    uint32_t nFlags,
-                    short zDelta,
-                    const CFX_PointF& point) override;
-  bool OnRButtonDown(CPDFSDK_PageView* pPageView,
-                     ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                     uint32_t nFlags,
-                     const CFX_PointF& point) override;
-  bool OnRButtonUp(CPDFSDK_PageView* pPageView,
-                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   uint32_t nFlags,
-                   const CFX_PointF& point) override;
-  bool OnRButtonDblClk(CPDFSDK_PageView* pPageView,
-                       ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                       uint32_t nFlags,
-                       const CFX_PointF& point) override;
-  bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override;
-  bool OnKeyDown(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override;
-  bool OnKeyUp(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override;
-  bool OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag) override;
-  bool OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag) override;
-  bool SetIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                        int index,
-                        bool selected) override;
-  bool IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot, int index) override;
-
- private:
-  UnownedPtr<CPDFSDK_FormFillEnvironment> m_pFormFillEnv;
-  UnownedPtr<CFFL_InteractiveFormFiller> m_pFormFiller;
-};
-
-#endif  // FPDFSDK_CPDFSDK_WIDGETHANDLER_H_
diff --git a/fpdfsdk/formfiller/Android.bp b/fpdfsdk/formfiller/Android.bp
index 960992a..8ed217e 100644
--- a/fpdfsdk/formfiller/Android.bp
+++ b/fpdfsdk/formfiller/Android.bp
@@ -13,11 +13,8 @@
 
     visibility: ["//external/pdfium:__subpackages__"],
 
-    header_libs: [
-        "libpdfium-constants",
-    ],
-
     static_libs: [
+        "libpdfium-constants",
         "libpdfium-page",
         "libpdfium-fpdfdoc",
         "libpdfium-fxcrt",
diff --git a/fpdfsdk/formfiller/BUILD.gn b/fpdfsdk/formfiller/BUILD.gn
index e612ef4..857bb5a 100644
--- a/fpdfsdk/formfiller/BUILD.gn
+++ b/fpdfsdk/formfiller/BUILD.gn
@@ -1,8 +1,9 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
 import("../../pdfium.gni")
+import("../../testing/test.gni")
 
 source_set("formfiller") {
   sources = [
@@ -12,12 +13,16 @@
     "cffl_checkbox.h",
     "cffl_combobox.cpp",
     "cffl_combobox.h",
-    "cffl_formfiller.cpp",
-    "cffl_formfiller.h",
+    "cffl_fieldaction.cpp",
+    "cffl_fieldaction.h",
+    "cffl_formfield.cpp",
+    "cffl_formfield.h",
     "cffl_interactiveformfiller.cpp",
     "cffl_interactiveformfiller.h",
     "cffl_listbox.cpp",
     "cffl_listbox.h",
+    "cffl_perwindowdata.cpp",
+    "cffl_perwindowdata.h",
     "cffl_pushbutton.cpp",
     "cffl_pushbutton.h",
     "cffl_radiobutton.cpp",
@@ -27,7 +32,10 @@
     "cffl_textobject.cpp",
     "cffl_textobject.h",
   ]
-  configs += [ "../../:pdfium_core_config" ]
+  configs += [
+    "../../:pdfium_strict_config",
+    "../../:pdfium_noshorten_config",
+  ]
   deps = [
     "../../:pdfium_public_headers",
     "../../constants",
@@ -39,3 +47,12 @@
   ]
   visibility = [ "../../*" ]
 }
+
+pdfium_embeddertest_source_set("embeddertests") {
+  sources = [ "cffl_combobox_embeddertest.cpp" ]
+  deps = [
+    ":formfiller",
+    "../pwl:embedder_test_support",
+  ]
+  pdfium_root_dir = "../../"
+}
diff --git a/fpdfsdk/formfiller/cffl_button.cpp b/fpdfsdk/formfiller/cffl_button.cpp
index 3520927..6c466d5 100644
--- a/fpdfsdk/formfiller/cffl_button.cpp
+++ b/fpdfsdk/formfiller/cffl_button.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,15 +6,14 @@
 
 #include "fpdfsdk/formfiller/cffl_button.h"
 
-#include "core/fpdfdoc/cpdf_formcontrol.h"
+#include "fpdfsdk/cpdfsdk_widget.h"
+#include "third_party/base/check.h"
 
-CFFL_Button::CFFL_Button(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+CFFL_Button::CFFL_Button(CFFL_InteractiveFormFiller* pFormFiller,
                          CPDFSDK_Widget* pWidget)
-    : CFFL_FormFiller(pFormFillEnv, pWidget),
-      m_bMouseIn(false),
-      m_bMouseDown(false) {}
+    : CFFL_FormField(pFormFiller, pWidget) {}
 
-CFFL_Button::~CFFL_Button() {}
+CFFL_Button::~CFFL_Button() = default;
 
 void CFFL_Button::OnMouseEnter(CPDFSDK_PageView* pPageView) {
   m_bMouseIn = true;
@@ -25,14 +24,14 @@
   m_bMouseIn = false;
   InvalidateRect(GetViewBBox(pPageView));
   m_pTimer.reset();
-  ASSERT(m_pWidget);
+  DCHECK(m_pWidget);
 }
 
 bool CFFL_Button::OnLButtonDown(CPDFSDK_PageView* pPageView,
-                                CPDFSDK_Annot* pAnnot,
-                                uint32_t nFlags,
+                                CPDFSDK_Widget* pWidget,
+                                Mask<FWL_EVENTFLAG> nFlags,
                                 const CFX_PointF& point) {
-  if (!pAnnot->GetRect().Contains(point))
+  if (!pWidget->GetRect().Contains(point))
     return false;
 
   m_bMouseDown = true;
@@ -42,63 +41,62 @@
 }
 
 bool CFFL_Button::OnLButtonUp(CPDFSDK_PageView* pPageView,
-                              CPDFSDK_Annot* pAnnot,
-                              uint32_t nFlags,
+                              CPDFSDK_Widget* pWidget,
+                              Mask<FWL_EVENTFLAG> nFlags,
                               const CFX_PointF& point) {
-  if (!pAnnot->GetRect().Contains(point))
+  if (!pWidget->GetRect().Contains(point))
     return false;
 
   m_bMouseDown = false;
-  m_pWidget->GetPDFPage();
   InvalidateRect(GetViewBBox(pPageView));
   return true;
 }
 
 bool CFFL_Button::OnMouseMove(CPDFSDK_PageView* pPageView,
-                              uint32_t nFlags,
+                              Mask<FWL_EVENTFLAG> nFlags,
                               const CFX_PointF& point) {
   return true;
 }
 
 void CFFL_Button::OnDraw(CPDFSDK_PageView* pPageView,
-                         CPDFSDK_Annot* pAnnot,
+                         CPDFSDK_Widget* pWidget,
                          CFX_RenderDevice* pDevice,
                          const CFX_Matrix& mtUser2Device) {
-  ASSERT(pPageView);
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
-  CPDF_FormControl* pCtrl = pWidget->GetFormControl();
-  if (pCtrl->GetHighlightingMode() != CPDF_FormControl::Push) {
-    pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Normal,
-                            nullptr);
+  DCHECK(pPageView);
+  if (!pWidget->IsPushHighlighted()) {
+    pWidget->DrawAppearance(pDevice, mtUser2Device,
+                            CPDF_Annot::AppearanceMode::kNormal);
     return;
   }
   if (m_bMouseDown) {
-    if (pWidget->IsWidgetAppearanceValid(CPDF_Annot::Down)) {
-      pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Down,
-                              nullptr);
+    if (pWidget->IsWidgetAppearanceValid(CPDF_Annot::AppearanceMode::kDown)) {
+      pWidget->DrawAppearance(pDevice, mtUser2Device,
+                              CPDF_Annot::AppearanceMode::kDown);
     } else {
-      pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Normal,
-                              nullptr);
+      pWidget->DrawAppearance(pDevice, mtUser2Device,
+                              CPDF_Annot::AppearanceMode::kNormal);
     }
     return;
   }
   if (m_bMouseIn) {
-    if (pWidget->IsWidgetAppearanceValid(CPDF_Annot::Rollover)) {
-      pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Rollover,
-                              nullptr);
+    if (pWidget->IsWidgetAppearanceValid(
+            CPDF_Annot::AppearanceMode::kRollover)) {
+      pWidget->DrawAppearance(pDevice, mtUser2Device,
+                              CPDF_Annot::AppearanceMode::kRollover);
     } else {
-      pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Normal,
-                              nullptr);
+      pWidget->DrawAppearance(pDevice, mtUser2Device,
+                              CPDF_Annot::AppearanceMode::kNormal);
     }
     return;
   }
 
-  pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Normal, nullptr);
+  pWidget->DrawAppearance(pDevice, mtUser2Device,
+                          CPDF_Annot::AppearanceMode::kNormal);
 }
 
 void CFFL_Button::OnDrawDeactive(CPDFSDK_PageView* pPageView,
-                                 CPDFSDK_Annot* pAnnot,
+                                 CPDFSDK_Widget* pWidget,
                                  CFX_RenderDevice* pDevice,
                                  const CFX_Matrix& mtUser2Device) {
-  OnDraw(pPageView, pAnnot, pDevice, mtUser2Device);
+  OnDraw(pPageView, pWidget, pDevice, mtUser2Device);
 }
diff --git a/fpdfsdk/formfiller/cffl_button.h b/fpdfsdk/formfiller/cffl_button.h
index 768c144..eaa9ad6 100644
--- a/fpdfsdk/formfiller/cffl_button.h
+++ b/fpdfsdk/formfiller/cffl_button.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,47 +8,44 @@
 #define FPDFSDK_FORMFILLER_CFFL_BUTTON_H_
 
 #include "core/fxcrt/fx_coordinates.h"
-#include "fpdfsdk/formfiller/cffl_formfiller.h"
+#include "fpdfsdk/formfiller/cffl_formfield.h"
 
 class CFX_RenderDevice;
 class CFX_Matrix;
-class CPDFSDK_Annot;
-class CPDFSDK_FormFillEnvironment;
 class CPDFSDK_PageView;
 class CPDFSDK_Widget;
 
-class CFFL_Button : public CFFL_FormFiller {
+class CFFL_Button : public CFFL_FormField {
  public:
-  CFFL_Button(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-              CPDFSDK_Widget* pWidget);
+  CFFL_Button(CFFL_InteractiveFormFiller* pFormFiller, CPDFSDK_Widget* pWidget);
   ~CFFL_Button() override;
 
-  // CFFL_FormFiller
+  // CFFL_FormField
   void OnMouseEnter(CPDFSDK_PageView* pPageView) override;
   void OnMouseExit(CPDFSDK_PageView* pPageView) override;
   bool OnLButtonDown(CPDFSDK_PageView* pPageView,
-                     CPDFSDK_Annot* pAnnot,
-                     uint32_t nFlags,
+                     CPDFSDK_Widget* pWidget,
+                     Mask<FWL_EVENTFLAG> nFlags,
                      const CFX_PointF& point) override;
   bool OnLButtonUp(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot* pAnnot,
-                   uint32_t nFlags,
+                   CPDFSDK_Widget* pWidget,
+                   Mask<FWL_EVENTFLAG> nFlags,
                    const CFX_PointF& point) override;
   bool OnMouseMove(CPDFSDK_PageView* pPageView,
-                   uint32_t nFlags,
+                   Mask<FWL_EVENTFLAG> nFlags,
                    const CFX_PointF& point) override;
   void OnDraw(CPDFSDK_PageView* pPageView,
-              CPDFSDK_Annot* pAnnot,
+              CPDFSDK_Widget* pWidget,
               CFX_RenderDevice* pDevice,
               const CFX_Matrix& mtUser2Device) override;
   void OnDrawDeactive(CPDFSDK_PageView* pPageView,
-                      CPDFSDK_Annot* pAnnot,
+                      CPDFSDK_Widget* pWidget,
                       CFX_RenderDevice* pDevice,
                       const CFX_Matrix& mtUser2Device) override;
 
  private:
-  bool m_bMouseIn;
-  bool m_bMouseDown;
+  bool m_bMouseIn = false;
+  bool m_bMouseDown = false;
 };
 
 #endif  // FPDFSDK_FORMFILLER_CFFL_BUTTON_H_
diff --git a/fpdfsdk/formfiller/cffl_checkbox.cpp b/fpdfsdk/formfiller/cffl_checkbox.cpp
index b0e60e2..527720e 100644
--- a/fpdfsdk/formfiller/cffl_checkbox.cpp
+++ b/fpdfsdk/formfiller/cffl_checkbox.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,50 +8,51 @@
 
 #include <utility>
 
+#include "constants/ascii.h"
 #include "core/fpdfdoc/cpdf_formcontrol.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
-#include "fpdfsdk/formfiller/cffl_formfiller.h"
+#include "fpdfsdk/formfiller/cffl_formfield.h"
 #include "fpdfsdk/pwl/cpwl_special_button.h"
 #include "public/fpdf_fwlevent.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
-CFFL_CheckBox::CFFL_CheckBox(CPDFSDK_FormFillEnvironment* pApp,
+CFFL_CheckBox::CFFL_CheckBox(CFFL_InteractiveFormFiller* pFormFiller,
                              CPDFSDK_Widget* pWidget)
-    : CFFL_Button(pApp, pWidget) {}
+    : CFFL_Button(pFormFiller, pWidget) {}
 
-CFFL_CheckBox::~CFFL_CheckBox() {}
+CFFL_CheckBox::~CFFL_CheckBox() = default;
 
 std::unique_ptr<CPWL_Wnd> CFFL_CheckBox::NewPWLWindow(
     const CPWL_Wnd::CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData) {
-  auto pWnd = pdfium::MakeUnique<CPWL_CheckBox>(cp, std::move(pAttachedData));
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData) {
+  auto pWnd = std::make_unique<CPWL_CheckBox>(cp, std::move(pAttachedData));
   pWnd->Realize();
   pWnd->SetCheck(m_pWidget->IsChecked());
   return std::move(pWnd);
 }
 
-bool CFFL_CheckBox::OnKeyDown(uint32_t nKeyCode, uint32_t nFlags) {
+bool CFFL_CheckBox::OnKeyDown(FWL_VKEYCODE nKeyCode,
+                              Mask<FWL_EVENTFLAG> nFlags) {
   switch (nKeyCode) {
     case FWL_VKEY_Return:
     case FWL_VKEY_Space:
       return true;
     default:
-      return CFFL_FormFiller::OnKeyDown(nKeyCode, nFlags);
+      return CFFL_FormField::OnKeyDown(nKeyCode, nFlags);
   }
 }
-bool CFFL_CheckBox::OnChar(CPDFSDK_Annot* pAnnot,
-                           uint32_t nChar,
-                           uint32_t nFlags) {
-  switch (nChar) {
-    case FWL_VKEY_Return:
-    case FWL_VKEY_Space: {
-      CPDFSDK_PageView* pPageView = pAnnot->GetPageView();
-      ASSERT(pPageView);
 
-      ObservedPtr<CPDFSDK_Annot> pObserved(m_pWidget.Get());
-      if (m_pFormFillEnv->GetInteractiveFormFiller()->OnButtonUp(
-              &pObserved, pPageView, nFlags)) {
+bool CFFL_CheckBox::OnChar(CPDFSDK_Widget* pWidget,
+                           uint32_t nChar,
+                           Mask<FWL_EVENTFLAG> nFlags) {
+  switch (nChar) {
+    case pdfium::ascii::kReturn:
+    case pdfium::ascii::kSpace: {
+      CPDFSDK_PageView* pPageView = pWidget->GetPageView();
+      DCHECK(pPageView);
+
+      ObservedPtr<CPDFSDK_Widget> pObserved(m_pWidget);
+      if (m_pFormFiller->OnButtonUp(pObserved, pPageView, nFlags)) {
         if (!pObserved)
           m_pWidget = nullptr;
         return true;
@@ -61,63 +62,56 @@
         return true;
       }
 
-      CFFL_FormFiller::OnChar(pAnnot, nChar, nFlags);
+      CFFL_FormField::OnChar(pWidget, nChar, nFlags);
 
-      CPWL_CheckBox* pWnd = GetCheckBox(pPageView, true);
-      if (pWnd) {
-        CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
-        pWnd->SetCheck(!pWidget->IsChecked());
+      CPWL_CheckBox* pWnd = CreateOrUpdatePWLCheckBox(pPageView);
+      if (pWnd && !pWnd->IsReadOnly()) {
+        ObservedPtr<CPWL_CheckBox> pObservedBox(pWnd);
+        const bool is_checked = pWidget->IsChecked();
+        if (pObservedBox) {
+          pObservedBox->SetCheck(!is_checked);
+        }
       }
-
       return CommitData(pPageView, nFlags);
     }
     default:
-      return CFFL_FormFiller::OnChar(pAnnot, nChar, nFlags);
+      return CFFL_FormField::OnChar(pWidget, nChar, nFlags);
   }
 }
 
 bool CFFL_CheckBox::OnLButtonUp(CPDFSDK_PageView* pPageView,
-                                CPDFSDK_Annot* pAnnot,
-                                uint32_t nFlags,
+                                CPDFSDK_Widget* pWidget,
+                                Mask<FWL_EVENTFLAG> nFlags,
                                 const CFX_PointF& point) {
-  CFFL_Button::OnLButtonUp(pPageView, pAnnot, nFlags, point);
-
-  if (!IsValid())
+  CFFL_Button::OnLButtonUp(pPageView, pWidget, nFlags, point);
+  if (!IsValid()) {
     return true;
-
-  CPWL_CheckBox* pWnd = GetCheckBox(pPageView, true);
-  if (pWnd) {
-    CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
-    pWnd->SetCheck(!pWidget->IsChecked());
   }
-
+  CPWL_CheckBox* pWnd = CreateOrUpdatePWLCheckBox(pPageView);
+  if (pWnd) {
+    ObservedPtr<CPWL_CheckBox> pObservedBox(pWnd);
+    const bool is_checked = pWidget->IsChecked();
+    if (pObservedBox) {
+      pObservedBox->SetCheck(!is_checked);
+    }
+  }
   return CommitData(pPageView, nFlags);
 }
 
-bool CFFL_CheckBox::IsDataChanged(CPDFSDK_PageView* pPageView) {
-  CPWL_CheckBox* pWnd = GetCheckBox(pPageView, false);
+bool CFFL_CheckBox::IsDataChanged(const CPDFSDK_PageView* pPageView) {
+  CPWL_CheckBox* pWnd = GetPWLCheckBox(pPageView);
   return pWnd && pWnd->IsChecked() != m_pWidget->IsChecked();
 }
 
-void CFFL_CheckBox::SaveData(CPDFSDK_PageView* pPageView) {
-  CPWL_CheckBox* pWnd = GetCheckBox(pPageView, false);
+void CFFL_CheckBox::SaveData(const CPDFSDK_PageView* pPageView) {
+  CPWL_CheckBox* pWnd = GetPWLCheckBox(pPageView);
   if (!pWnd)
     return;
 
   bool bNewChecked = pWnd->IsChecked();
-  if (bNewChecked) {
-    CPDF_FormField* pField = m_pWidget->GetFormField();
-    for (int32_t i = 0, sz = pField->CountControls(); i < sz; i++) {
-      if (CPDF_FormControl* pCtrl = pField->GetControl(i)) {
-        if (pCtrl->IsChecked()) {
-          break;
-        }
-      }
-    }
-  }
-  ObservedPtr<CPDFSDK_Widget> observed_widget(m_pWidget.Get());
+  ObservedPtr<CPDFSDK_Widget> observed_widget(m_pWidget);
   ObservedPtr<CFFL_CheckBox> observed_this(this);
-  m_pWidget->SetCheck(bNewChecked, NotificationOption::kDoNotNotify);
+  m_pWidget->SetCheck(bNewChecked);
   if (!observed_widget)
     return;
 
@@ -128,7 +122,12 @@
   SetChangeMark();
 }
 
-CPWL_CheckBox* CFFL_CheckBox::GetCheckBox(CPDFSDK_PageView* pPageView,
-                                          bool bNew) {
-  return static_cast<CPWL_CheckBox*>(GetPWLWindow(pPageView, bNew));
+CPWL_CheckBox* CFFL_CheckBox::GetPWLCheckBox(
+    const CPDFSDK_PageView* pPageView) const {
+  return static_cast<CPWL_CheckBox*>(GetPWLWindow(pPageView));
+}
+
+CPWL_CheckBox* CFFL_CheckBox::CreateOrUpdatePWLCheckBox(
+    const CPDFSDK_PageView* pPageView) {
+  return static_cast<CPWL_CheckBox*>(CreateOrUpdatePWLWindow(pPageView));
 }
diff --git a/fpdfsdk/formfiller/cffl_checkbox.h b/fpdfsdk/formfiller/cffl_checkbox.h
index aff2762..5669429 100644
--- a/fpdfsdk/formfiller/cffl_checkbox.h
+++ b/fpdfsdk/formfiller/cffl_checkbox.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,25 +15,28 @@
 
 class CFFL_CheckBox final : public CFFL_Button {
  public:
-  CFFL_CheckBox(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget);
+  CFFL_CheckBox(CFFL_InteractiveFormFiller* pFormFiller,
+                CPDFSDK_Widget* pWidget);
   ~CFFL_CheckBox() override;
 
   // CFFL_Button:
   std::unique_ptr<CPWL_Wnd> NewPWLWindow(
       const CPWL_Wnd::CreateParams& cp,
-      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
-      override;
-  bool OnKeyDown(uint32_t nKeyCode, uint32_t nFlags) override;
-  bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override;
+      std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData) override;
+  bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlags) override;
+  bool OnChar(CPDFSDK_Widget* pWidget,
+              uint32_t nChar,
+              Mask<FWL_EVENTFLAG> nFlags) override;
   bool OnLButtonUp(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot* pAnnot,
-                   uint32_t nFlags,
+                   CPDFSDK_Widget* pWidget,
+                   Mask<FWL_EVENTFLAG> nFlags,
                    const CFX_PointF& point) override;
-  bool IsDataChanged(CPDFSDK_PageView* pPageView) override;
-  void SaveData(CPDFSDK_PageView* pPageView) override;
+  bool IsDataChanged(const CPDFSDK_PageView* pPageView) override;
+  void SaveData(const CPDFSDK_PageView* pPageView) override;
 
  private:
-  CPWL_CheckBox* GetCheckBox(CPDFSDK_PageView* pPageView, bool bNew);
+  CPWL_CheckBox* GetPWLCheckBox(const CPDFSDK_PageView* pPageView) const;
+  CPWL_CheckBox* CreateOrUpdatePWLCheckBox(const CPDFSDK_PageView* pPageView);
 };
 
 #endif  // FPDFSDK_FORMFILLER_CFFL_CHECKBOX_H_
diff --git a/fpdfsdk/formfiller/cffl_combobox.cpp b/fpdfsdk/formfiller/cffl_combobox.cpp
index 588c50c..7364135 100644
--- a/fpdfsdk/formfiller/cffl_combobox.cpp
+++ b/fpdfsdk/formfiller/cffl_combobox.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,22 +9,18 @@
 #include <utility>
 
 #include "constants/form_flags.h"
-#include "core/fpdfdoc/cba_fontmap.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "core/fpdfdoc/cpdf_bafontmap.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
 #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
+#include "fpdfsdk/formfiller/cffl_perwindowdata.h"
 #include "fpdfsdk/pwl/cpwl_combo_box.h"
-#include "third_party/base/ptr_util.h"
+#include "fpdfsdk/pwl/cpwl_edit.h"
 
-CFFL_ComboBox::CFFL_ComboBox(CPDFSDK_FormFillEnvironment* pApp,
+CFFL_ComboBox::CFFL_ComboBox(CFFL_InteractiveFormFiller* pFormFiller,
                              CPDFSDK_Widget* pWidget)
-    : CFFL_TextObject(pApp, pWidget) {
-}
+    : CFFL_TextObject(pFormFiller, pWidget) {}
 
 CFFL_ComboBox::~CFFL_ComboBox() {
-  for (const auto& it : m_Maps)
-    it.second->InvalidateFocusHandler(this);
-
   // See comment in cffl_formfiller.h.
   // The font map should be stored somewhere more appropriate so it will live
   // until the PWL_Edit is done with it. pdfium:566
@@ -36,22 +32,17 @@
   if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceEdit)
     cp.dwFlags |= PCBS_ALLOWCUSTOMTEXT;
 
-  cp.pFontMap = MaybeCreateFontMap();
-  cp.pFocusHandler = this;
+  cp.pFontMap = GetOrCreateFontMap();
   return cp;
 }
 
 std::unique_ptr<CPWL_Wnd> CFFL_ComboBox::NewPWLWindow(
     const CPWL_Wnd::CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData) {
-  auto pWnd = pdfium::MakeUnique<CPWL_ComboBox>(cp, std::move(pAttachedData));
-  pWnd->AttachFFLData(this);
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData) {
+  static_cast<CFFL_PerWindowData*>(pAttachedData.get())->SetFormField(this);
+  auto pWnd = std::make_unique<CPWL_ComboBox>(cp, std::move(pAttachedData));
   pWnd->Realize();
 
-  CFFL_InteractiveFormFiller* pFormFiller =
-      m_pFormFillEnv->GetInteractiveFormFiller();
-  pWnd->SetFillerNotify(pFormFiller);
-
   int32_t nCurSel = m_pWidget->GetSelectedIndex(0);
   WideString swText;
   if (nCurSel < 0)
@@ -67,14 +58,14 @@
   return std::move(pWnd);
 }
 
-bool CFFL_ComboBox::OnChar(CPDFSDK_Annot* pAnnot,
+bool CFFL_ComboBox::OnChar(CPDFSDK_Widget* pWidget,
                            uint32_t nChar,
-                           uint32_t nFlags) {
-  return CFFL_TextObject::OnChar(pAnnot, nChar, nFlags);
+                           Mask<FWL_EVENTFLAG> nFlags) {
+  return CFFL_TextObject::OnChar(pWidget, nChar, nFlags);
 }
 
-bool CFFL_ComboBox::IsDataChanged(CPDFSDK_PageView* pPageView) {
-  auto* pWnd = GetComboBox(pPageView, false);
+bool CFFL_ComboBox::IsDataChanged(const CPDFSDK_PageView* pPageView) {
+  auto* pWnd = GetPWLComboBox(pPageView);
   if (!pWnd)
     return false;
 
@@ -88,8 +79,8 @@
   return pWnd->GetText() != m_pWidget->GetValue();
 }
 
-void CFFL_ComboBox::SaveData(CPDFSDK_PageView* pPageView) {
-  CPWL_ComboBox* pWnd = GetComboBox(pPageView, false);
+void CFFL_ComboBox::SaveData(const CPDFSDK_PageView* pPageView) {
+  CPWL_ComboBox* pWnd = GetPWLComboBox(pPageView);
   if (!pWnd)
     return;
 
@@ -100,13 +91,12 @@
     bSetValue = (nCurSel < 0) || (swText != m_pWidget->GetOptionLabel(nCurSel));
 
   if (bSetValue) {
-    m_pWidget->SetValue(swText, NotificationOption::kDoNotNotify);
+    m_pWidget->SetValue(swText);
   } else {
     m_pWidget->GetSelectedIndex(0);
-    m_pWidget->SetOptionSelection(nCurSel, true,
-                                  NotificationOption::kDoNotNotify);
+    m_pWidget->SetOptionSelection(nCurSel);
   }
-  ObservedPtr<CPDFSDK_Widget> observed_widget(m_pWidget.Get());
+  ObservedPtr<CPDFSDK_Widget> observed_widget(m_pWidget);
   ObservedPtr<CFFL_ComboBox> observed_this(this);
   m_pWidget->ResetFieldAppearance();
   if (!observed_widget)
@@ -117,22 +107,17 @@
     return;
 
   SetChangeMark();
-  m_pWidget->GetPDFPage();
 }
 
-void CFFL_ComboBox::GetActionData(CPDFSDK_PageView* pPageView,
+void CFFL_ComboBox::GetActionData(const CPDFSDK_PageView* pPageView,
                                   CPDF_AAction::AActionType type,
-                                  CPDFSDK_FieldAction& fa) {
+                                  CFFL_FieldAction& fa) {
   switch (type) {
     case CPDF_AAction::kKeyStroke:
-      if (CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false)) {
+      if (CPWL_ComboBox* pComboBox = GetPWLComboBox(pPageView)) {
         if (CPWL_Edit* pEdit = pComboBox->GetEdit()) {
           fa.bFieldFull = pEdit->IsTextFull();
-          int nSelStart = 0;
-          int nSelEnd = 0;
-          pEdit->GetSelection(nSelStart, nSelEnd);
-          fa.nSelEnd = nSelEnd;
-          fa.nSelStart = nSelStart;
+          std::tie(fa.nSelStart, fa.nSelEnd) = pEdit->GetSelection();
           fa.sValue = pEdit->GetText();
           fa.sChangeEx = GetSelectExportText();
 
@@ -144,7 +129,7 @@
       }
       break;
     case CPDF_AAction::kValidate:
-      if (CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false)) {
+      if (CPWL_ComboBox* pComboBox = GetPWLComboBox(pPageView)) {
         if (CPWL_Edit* pEdit = pComboBox->GetEdit()) {
           fa.sValue = pEdit->GetText();
         }
@@ -159,12 +144,12 @@
   }
 }
 
-void CFFL_ComboBox::SetActionData(CPDFSDK_PageView* pPageView,
+void CFFL_ComboBox::SetActionData(const CPDFSDK_PageView* pPageView,
                                   CPDF_AAction::AActionType type,
-                                  const CPDFSDK_FieldAction& fa) {
+                                  const CFFL_FieldAction& fa) {
   switch (type) {
     case CPDF_AAction::kKeyStroke:
-      if (CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false)) {
+      if (CPWL_ComboBox* pComboBox = GetPWLComboBox(pPageView)) {
         if (CPWL_Edit* pEdit = pComboBox->GetEdit()) {
           pEdit->SetSelection(fa.nSelStart, fa.nSelEnd);
           pEdit->ReplaceSelection(fa.sChange);
@@ -176,8 +161,8 @@
   }
 }
 
-void CFFL_ComboBox::SaveState(CPDFSDK_PageView* pPageView) {
-  CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false);
+void CFFL_ComboBox::SavePWLWindowState(const CPDFSDK_PageView* pPageView) {
+  CPWL_ComboBox* pComboBox = GetPWLComboBox(pPageView);
   if (!pComboBox)
     return;
 
@@ -187,12 +172,13 @@
   if (!pEdit)
     return;
 
-  pEdit->GetSelection(m_State.nStart, m_State.nEnd);
+  std::tie(m_State.nStart, m_State.nEnd) = pEdit->GetSelection();
   m_State.sValue = pEdit->GetText();
 }
 
-void CFFL_ComboBox::RestoreState(CPDFSDK_PageView* pPageView) {
-  CPWL_ComboBox* pComboBox = GetComboBox(pPageView, true);
+void CFFL_ComboBox::RecreatePWLWindowFromSavedState(
+    const CPDFSDK_PageView* pPageView) {
+  CPWL_ComboBox* pComboBox = CreateOrUpdatePWLComboBox(pPageView);
   if (!pComboBox)
     return;
 
@@ -216,7 +202,7 @@
   if (index < 0 || index >= m_pWidget->CountOptions())
     return false;
 
-  CPWL_ComboBox* pWnd = GetComboBox(GetCurPageView(true), false);
+  CPWL_ComboBox* pWnd = GetPWLComboBox(GetCurPageView());
   if (!pWnd)
     return false;
 
@@ -231,13 +217,13 @@
   if (index < 0 || index >= m_pWidget->CountOptions())
     return false;
 
-  CPWL_ComboBox* pWnd = GetComboBox(GetCurPageView(true), false);
+  CPWL_ComboBox* pWnd = GetPWLComboBox(GetCurPageView());
   return pWnd && index == pWnd->GetSelect();
 }
 
 #ifdef PDF_ENABLE_XFA
-bool CFFL_ComboBox::IsFieldFull(CPDFSDK_PageView* pPageView) {
-  CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false);
+bool CFFL_ComboBox::IsFieldFull(const CPDFSDK_PageView* pPageView) {
+  CPWL_ComboBox* pComboBox = GetPWLComboBox(pPageView);
   if (!pComboBox)
     return false;
 
@@ -246,35 +232,24 @@
 }
 #endif  // PDF_ENABLE_XFA
 
-void CFFL_ComboBox::OnSetFocus(CPWL_Edit* pEdit) {
-  pEdit->SetCharSet(FX_CHARSET_ChineseSimplified);
+void CFFL_ComboBox::OnSetFocusForEdit(CPWL_Edit* pEdit) {
+  pEdit->SetCharSet(FX_Charset::kChineseSimplified);
   pEdit->SetReadyToInput();
-
-  WideString wsText = pEdit->GetText();
-  int nCharacters = wsText.GetLength();
-  ByteString bsUTFText = wsText.ToUTF16LE();
-  auto* pBuffer = reinterpret_cast<const unsigned short*>(bsUTFText.c_str());
-  m_pFormFillEnv->OnSetFieldInputFocus(pBuffer, nCharacters, true);
+  m_pFormFiller->OnSetFieldInputFocus(pEdit->GetText());
 }
 
 WideString CFFL_ComboBox::GetSelectExportText() {
-  WideString swRet;
-
-  CPWL_ComboBox* pComboBox = GetComboBox(GetCurPageView(true), false);
+  CPWL_ComboBox* pComboBox = GetPWLComboBox(GetCurPageView());
   int nExport = pComboBox ? pComboBox->GetSelect() : -1;
-
-  if (nExport >= 0) {
-    if (CPDF_FormField* pFormField = m_pWidget->GetFormField()) {
-      swRet = pFormField->GetOptionValue(nExport);
-      if (swRet.IsEmpty())
-        swRet = pFormField->GetOptionLabel(nExport);
-    }
-  }
-
-  return swRet;
+  return m_pWidget->GetSelectExportText(nExport);
 }
 
-CPWL_ComboBox* CFFL_ComboBox::GetComboBox(CPDFSDK_PageView* pPageView,
-                                          bool bNew) {
-  return static_cast<CPWL_ComboBox*>(GetPWLWindow(pPageView, bNew));
+CPWL_ComboBox* CFFL_ComboBox::GetPWLComboBox(
+    const CPDFSDK_PageView* pPageView) const {
+  return static_cast<CPWL_ComboBox*>(GetPWLWindow(pPageView));
+}
+
+CPWL_ComboBox* CFFL_ComboBox::CreateOrUpdatePWLComboBox(
+    const CPDFSDK_PageView* pPageView) {
+  return static_cast<CPWL_ComboBox*>(CreateOrUpdatePWLWindow(pPageView));
 }
diff --git a/fpdfsdk/formfiller/cffl_combobox.h b/fpdfsdk/formfiller/cffl_combobox.h
index 250dba7..e2cc51e 100644
--- a/fpdfsdk/formfiller/cffl_combobox.h
+++ b/fpdfsdk/formfiller/cffl_combobox.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 
 #include <memory>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 #include "fpdfsdk/formfiller/cffl_textobject.h"
 
 class CPWL_ComboBox;
@@ -21,41 +21,44 @@
   WideString sValue;
 };
 
-class CFFL_ComboBox final : public CFFL_TextObject,
-                            public CPWL_Wnd::FocusHandlerIface {
+class CFFL_ComboBox final : public CFFL_TextObject {
  public:
-  CFFL_ComboBox(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget);
+  CFFL_ComboBox(CFFL_InteractiveFormFiller* pFormFiller,
+                CPDFSDK_Widget* pWidget);
   ~CFFL_ComboBox() override;
 
   // CFFL_TextObject:
   CPWL_Wnd::CreateParams GetCreateParam() override;
   std::unique_ptr<CPWL_Wnd> NewPWLWindow(
       const CPWL_Wnd::CreateParams& cp,
-      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
-      override;
-  bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override;
-  bool IsDataChanged(CPDFSDK_PageView* pPageView) override;
-  void SaveData(CPDFSDK_PageView* pPageView) override;
-  void GetActionData(CPDFSDK_PageView* pPageView,
+      std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData) override;
+  bool OnChar(CPDFSDK_Widget* pWidget,
+              uint32_t nChar,
+              Mask<FWL_EVENTFLAG> nFlags) override;
+  bool IsDataChanged(const CPDFSDK_PageView* pPageView) override;
+  void SaveData(const CPDFSDK_PageView* pPageView) override;
+  void GetActionData(const CPDFSDK_PageView* pPageView,
                      CPDF_AAction::AActionType type,
-                     CPDFSDK_FieldAction& fa) override;
-  void SetActionData(CPDFSDK_PageView* pPageView,
+                     CFFL_FieldAction& fa) override;
+  void SetActionData(const CPDFSDK_PageView* pPageView,
                      CPDF_AAction::AActionType type,
-                     const CPDFSDK_FieldAction& fa) override;
-  void SaveState(CPDFSDK_PageView* pPageView) override;
-  void RestoreState(CPDFSDK_PageView* pPageView) override;
+                     const CFFL_FieldAction& fa) override;
+  void SavePWLWindowState(const CPDFSDK_PageView* pPageView) override;
+  void RecreatePWLWindowFromSavedState(
+      const CPDFSDK_PageView* pPageView) override;
   bool SetIndexSelected(int index, bool selected) override;
   bool IsIndexSelected(int index) override;
 #ifdef PDF_ENABLE_XFA
-  bool IsFieldFull(CPDFSDK_PageView* pPageView) override;
+  bool IsFieldFull(const CPDFSDK_PageView* pPageView) override;
 #endif
 
-  // CPWL_Wnd::FocusHandlerIface:
-  void OnSetFocus(CPWL_Edit* pEdit) override;
+  // CPWL_Wnd::ProviderIface:
+  void OnSetFocusForEdit(CPWL_Edit* pEdit) override;
 
  private:
   WideString GetSelectExportText();
-  CPWL_ComboBox* GetComboBox(CPDFSDK_PageView* pPageView, bool bNew);
+  CPWL_ComboBox* GetPWLComboBox(const CPDFSDK_PageView* pPageView) const;
+  CPWL_ComboBox* CreateOrUpdatePWLComboBox(const CPDFSDK_PageView* pPageView);
 
   FFL_ComboBoxState m_State;
 };
diff --git a/fpdfsdk/formfiller/cffl_combobox_embeddertest.cpp b/fpdfsdk/formfiller/cffl_combobox_embeddertest.cpp
new file mode 100644
index 0000000..dbde075
--- /dev/null
+++ b/fpdfsdk/formfiller/cffl_combobox_embeddertest.cpp
@@ -0,0 +1,50 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fpdfsdk/formfiller/cffl_combobox.h"
+
+#include "fpdfsdk/pwl/cpwl_combo_box_embeddertest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class CFFLComboBoxEmbedderTest : public CPWLComboBoxEmbedderTest {};
+
+TEST_F(CFFLComboBoxEmbedderTest, GetActionData) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotNormal());
+  {
+    CFFL_FieldAction result;
+    GetCFFLFormField()->GetActionData(GetPageView(), CPDF_AAction::kKeyStroke,
+                                      result);
+    EXPECT_EQ(L"Banana", result.sValue);
+    EXPECT_EQ(L"Banana", result.sChangeEx);
+  }
+  {
+    CFFL_FieldAction result;
+    GetCFFLFormField()->GetActionData(GetPageView(), CPDF_AAction::kValidate,
+                                      result);
+    EXPECT_EQ(L"Banana", result.sValue);
+    EXPECT_EQ(L"", result.sChangeEx);
+  }
+  {
+    CFFL_FieldAction result;
+    GetCFFLFormField()->GetActionData(GetPageView(), CPDF_AAction::kGetFocus,
+                                      result);
+    EXPECT_EQ(L"Banana", result.sValue);
+    EXPECT_EQ(L"", result.sChangeEx);
+  }
+}
+
+TEST_F(CFFLComboBoxEmbedderTest, SetActionData) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotNormal());
+  CFFL_FieldAction input_fa;
+  input_fa.nSelStart = 2;
+  input_fa.nSelEnd = 4;
+  input_fa.sChange = L"Hamster";
+  GetCFFLFormField()->SetActionData(GetPageView(), CPDF_AAction::kKeyStroke,
+                                    input_fa);
+
+  CFFL_FieldAction output_fa;
+  GetCFFLFormField()->GetActionData(GetPageView(), CPDF_AAction::kKeyStroke,
+                                    output_fa);
+  EXPECT_EQ(L"BaHamsterna", output_fa.sValue);
+}
diff --git a/fpdfsdk/formfiller/cffl_fieldaction.cpp b/fpdfsdk/formfiller/cffl_fieldaction.cpp
new file mode 100644
index 0000000..8ee28cb
--- /dev/null
+++ b/fpdfsdk/formfiller/cffl_fieldaction.cpp
@@ -0,0 +1,11 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fpdfsdk/formfiller/cffl_fieldaction.h"
+
+CFFL_FieldAction::CFFL_FieldAction() = default;
+
+CFFL_FieldAction::~CFFL_FieldAction() = default;
diff --git a/fpdfsdk/formfiller/cffl_fieldaction.h b/fpdfsdk/formfiller/cffl_fieldaction.h
new file mode 100644
index 0000000..0bd6824
--- /dev/null
+++ b/fpdfsdk/formfiller/cffl_fieldaction.h
@@ -0,0 +1,30 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_FORMFILLER_CFFL_FIELDACTION_H_
+#define FPDFSDK_FORMFILLER_CFFL_FIELDACTION_H_
+
+#include "core/fxcrt/widestring.h"
+
+struct CFFL_FieldAction {
+  CFFL_FieldAction();
+  CFFL_FieldAction(const CFFL_FieldAction& other) = delete;
+  ~CFFL_FieldAction();
+
+  bool bModifier = false;
+  bool bShift = false;
+  bool bKeyDown = false;
+  bool bWillCommit = false;
+  bool bFieldFull = false;
+  bool bRC = true;
+  int nSelEnd = 0;
+  int nSelStart = 0;
+  WideString sChange;
+  WideString sChangeEx;
+  WideString sValue;
+};
+
+#endif  // FPDFSDK_FORMFILLER_CFFL_FIELDACTION_H_
diff --git a/fpdfsdk/formfiller/cffl_formfield.cpp b/fpdfsdk/formfiller/cffl_formfield.cpp
new file mode 100644
index 0000000..88fb16a
--- /dev/null
+++ b/fpdfsdk/formfiller/cffl_formfield.cpp
@@ -0,0 +1,590 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fpdfsdk/formfiller/cffl_formfield.h"
+
+#include <utility>
+
+#include "constants/form_flags.h"
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fxge/cfx_renderdevice.h"
+#include "fpdfsdk/cpdfsdk_pageview.h"
+#include "fpdfsdk/cpdfsdk_widget.h"
+#include "fpdfsdk/formfiller/cffl_perwindowdata.h"
+#include "third_party/base/check.h"
+
+CFFL_FormField::CFFL_FormField(CFFL_InteractiveFormFiller* pFormFiller,
+                               CPDFSDK_Widget* pWidget)
+    : m_pFormFiller(pFormFiller), m_pWidget(pWidget) {
+  DCHECK(m_pFormFiller);
+}
+
+CFFL_FormField::~CFFL_FormField() {
+  DestroyWindows();
+}
+
+void CFFL_FormField::DestroyWindows() {
+  while (!m_Maps.empty()) {
+    auto it = m_Maps.begin();
+    std::unique_ptr<CPWL_Wnd> pWnd = std::move(it->second);
+    m_Maps.erase(it);
+    pWnd->InvalidateProvider(this);
+    pWnd->Destroy();
+  }
+}
+
+FX_RECT CFFL_FormField::GetViewBBox(const CPDFSDK_PageView* pPageView) {
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView);
+  CFX_FloatRect rcAnnot =
+      pWnd ? PWLtoFFL(pWnd->GetWindowRect()) : m_pWidget->GetRect();
+  CFX_FloatRect rcFocus = GetFocusBox(pPageView);
+
+  CFX_FloatRect rcWin = rcAnnot;
+  if (!rcFocus.IsEmpty())
+    rcWin.Union(rcFocus);
+  if (!rcWin.IsEmpty()) {
+    rcWin.Inflate(1, 1);
+    rcWin.Normalize();
+  }
+
+  return rcWin.GetOuterRect();
+}
+
+void CFFL_FormField::OnDraw(CPDFSDK_PageView* pPageView,
+                            CPDFSDK_Widget* pWidget,
+                            CFX_RenderDevice* pDevice,
+                            const CFX_Matrix& mtUser2Device) {
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView);
+  if (pWnd) {
+    pWnd->DrawAppearance(pDevice, GetCurMatrix() * mtUser2Device);
+    return;
+  }
+  if (!CFFL_InteractiveFormFiller::IsVisible(pWidget))
+    return;
+
+  pWidget->DrawAppearance(pDevice, mtUser2Device,
+                          CPDF_Annot::AppearanceMode::kNormal);
+}
+
+void CFFL_FormField::OnDrawDeactive(CPDFSDK_PageView* pPageView,
+                                    CPDFSDK_Widget* pWidget,
+                                    CFX_RenderDevice* pDevice,
+                                    const CFX_Matrix& mtUser2Device) {
+  pWidget->DrawAppearance(pDevice, mtUser2Device,
+                          CPDF_Annot::AppearanceMode::kNormal);
+}
+
+void CFFL_FormField::OnMouseEnter(CPDFSDK_PageView* pPageView) {}
+
+void CFFL_FormField::OnMouseExit(CPDFSDK_PageView* pPageView) {
+  m_pTimer.reset();
+  DCHECK(m_pWidget);
+}
+
+bool CFFL_FormField::OnLButtonDown(CPDFSDK_PageView* pPageView,
+                                   CPDFSDK_Widget* pWidget,
+                                   Mask<FWL_EVENTFLAG> nFlags,
+                                   const CFX_PointF& point) {
+  CPWL_Wnd* pWnd = CreateOrUpdatePWLWindow(pPageView);
+  if (!pWnd)
+    return false;
+
+  m_bValid = true;
+  FX_RECT rect = GetViewBBox(pPageView);
+  InvalidateRect(rect);
+  if (!rect.Contains(static_cast<int>(point.x), static_cast<int>(point.y)))
+    return false;
+  return pWnd->OnLButtonDown(nFlags, FFLtoPWL(point));
+}
+
+bool CFFL_FormField::OnLButtonUp(CPDFSDK_PageView* pPageView,
+                                 CPDFSDK_Widget* pWidget,
+                                 Mask<FWL_EVENTFLAG> nFlags,
+                                 const CFX_PointF& point) {
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView);
+  if (!pWnd)
+    return false;
+
+  InvalidateRect(GetViewBBox(pPageView));
+  pWnd->OnLButtonUp(nFlags, FFLtoPWL(point));
+  return true;
+}
+
+bool CFFL_FormField::OnLButtonDblClk(CPDFSDK_PageView* pPageView,
+                                     Mask<FWL_EVENTFLAG> nFlags,
+                                     const CFX_PointF& point) {
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView);
+  if (!pWnd)
+    return false;
+
+  pWnd->OnLButtonDblClk(nFlags, FFLtoPWL(point));
+  return true;
+}
+
+bool CFFL_FormField::OnMouseMove(CPDFSDK_PageView* pPageView,
+                                 Mask<FWL_EVENTFLAG> nFlags,
+                                 const CFX_PointF& point) {
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView);
+  if (!pWnd)
+    return false;
+
+  pWnd->OnMouseMove(nFlags, FFLtoPWL(point));
+  return true;
+}
+
+bool CFFL_FormField::OnMouseWheel(CPDFSDK_PageView* pPageView,
+                                  Mask<FWL_EVENTFLAG> nFlags,
+                                  const CFX_PointF& point,
+                                  const CFX_Vector& delta) {
+  if (!IsValid())
+    return false;
+
+  CPWL_Wnd* pWnd = CreateOrUpdatePWLWindow(pPageView);
+  return pWnd && pWnd->OnMouseWheel(nFlags, FFLtoPWL(point), delta);
+}
+
+bool CFFL_FormField::OnRButtonDown(CPDFSDK_PageView* pPageView,
+                                   Mask<FWL_EVENTFLAG> nFlags,
+                                   const CFX_PointF& point) {
+  CPWL_Wnd* pWnd = CreateOrUpdatePWLWindow(pPageView);
+  return pWnd && pWnd->OnRButtonDown(nFlags, FFLtoPWL(point));
+}
+
+bool CFFL_FormField::OnRButtonUp(CPDFSDK_PageView* pPageView,
+                                 Mask<FWL_EVENTFLAG> nFlags,
+                                 const CFX_PointF& point) {
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView);
+  return pWnd && pWnd->OnRButtonUp(nFlags, FFLtoPWL(point));
+}
+
+bool CFFL_FormField::OnKeyDown(FWL_VKEYCODE nKeyCode,
+                               Mask<FWL_EVENTFLAG> nFlags) {
+  if (!IsValid())
+    return false;
+
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView());
+  return pWnd && pWnd->OnKeyDown(nKeyCode, nFlags);
+}
+
+bool CFFL_FormField::OnChar(CPDFSDK_Widget* pWidget,
+                            uint32_t nChar,
+                            Mask<FWL_EVENTFLAG> nFlags) {
+  if (!IsValid())
+    return false;
+
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView());
+  return pWnd && pWnd->OnChar(nChar, nFlags);
+}
+
+bool CFFL_FormField::SetIndexSelected(int index, bool selected) {
+  return false;
+}
+
+bool CFFL_FormField::IsIndexSelected(int index) {
+  return false;
+}
+
+WideString CFFL_FormField::GetText() {
+  if (!IsValid())
+    return WideString();
+
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView());
+  return pWnd ? pWnd->GetText() : WideString();
+}
+
+WideString CFFL_FormField::GetSelectedText() {
+  if (!IsValid())
+    return WideString();
+
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView());
+  return pWnd ? pWnd->GetSelectedText() : WideString();
+}
+
+void CFFL_FormField::ReplaceAndKeepSelection(const WideString& text) {
+  if (!IsValid())
+    return;
+
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView());
+  if (!pWnd)
+    return;
+
+  pWnd->ReplaceAndKeepSelection(text);
+}
+
+void CFFL_FormField::ReplaceSelection(const WideString& text) {
+  if (!IsValid())
+    return;
+
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView());
+  if (!pWnd)
+    return;
+
+  pWnd->ReplaceSelection(text);
+}
+
+bool CFFL_FormField::SelectAllText() {
+  if (!IsValid())
+    return false;
+
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView());
+  return pWnd && pWnd->SelectAllText();
+}
+
+bool CFFL_FormField::CanUndo() {
+  if (!IsValid())
+    return false;
+
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView());
+  return pWnd && pWnd->CanUndo();
+}
+
+bool CFFL_FormField::CanRedo() {
+  if (!IsValid())
+    return false;
+
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView());
+  return pWnd && pWnd->CanRedo();
+}
+
+bool CFFL_FormField::Undo() {
+  if (!IsValid())
+    return false;
+
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView());
+  return pWnd && pWnd->Undo();
+}
+
+bool CFFL_FormField::Redo() {
+  if (!IsValid())
+    return false;
+
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView());
+  return pWnd && pWnd->Redo();
+}
+
+void CFFL_FormField::SetFocusForAnnot(CPDFSDK_Widget* pWidget,
+                                      Mask<FWL_EVENTFLAG> nFlag) {
+  CPDFSDK_PageView* pPageView =
+      m_pFormFiller->GetOrCreatePageView(pWidget->GetPage());
+  CPWL_Wnd* pWnd = CreateOrUpdatePWLWindow(pPageView);
+  if (pWnd)
+    pWnd->SetFocus();
+
+  m_bValid = true;
+  InvalidateRect(GetViewBBox(pPageView));
+}
+
+void CFFL_FormField::KillFocusForAnnot(Mask<FWL_EVENTFLAG> nFlag) {
+  if (!IsValid())
+    return;
+
+  CPDFSDK_PageView* pPageView =
+      m_pFormFiller->GetPageView(m_pWidget->GetPage());
+  if (!pPageView || !CommitData(pPageView, nFlag))
+    return;
+  if (CPWL_Wnd* pWnd = GetPWLWindow(pPageView))
+    pWnd->KillFocus();
+
+  bool bDestroyPWLWindow;
+  switch (m_pWidget->GetFieldType()) {
+    case FormFieldType::kPushButton:
+    case FormFieldType::kCheckBox:
+    case FormFieldType::kRadioButton:
+      bDestroyPWLWindow = true;
+      break;
+    default:
+      bDestroyPWLWindow = false;
+      break;
+  }
+  EscapeFiller(pPageView, bDestroyPWLWindow);
+}
+
+bool CFFL_FormField::IsValid() const {
+  return m_bValid;
+}
+
+CPWL_Wnd::CreateParams CFFL_FormField::GetCreateParam() {
+  CPWL_Wnd::CreateParams cp(m_pFormFiller->GetTimerHandler(), m_pFormFiller,
+                            this);
+
+  cp.rcRectWnd = GetPDFAnnotRect();
+
+  uint32_t dwCreateFlags = PWS_BORDER | PWS_BACKGROUND | PWS_VISIBLE;
+  uint32_t dwFieldFlag = m_pWidget->GetFieldFlags();
+  if (dwFieldFlag & pdfium::form_flags::kReadOnly)
+    dwCreateFlags |= PWS_READONLY;
+
+  absl::optional<FX_COLORREF> color = m_pWidget->GetFillColor();
+  if (color.has_value())
+    cp.sBackgroundColor = CFX_Color(color.value());
+  color = m_pWidget->GetBorderColor();
+  if (color.has_value())
+    cp.sBorderColor = CFX_Color(color.value());
+
+  cp.sTextColor = CFX_Color(CFX_Color::Type::kGray, 0);
+
+  color = m_pWidget->GetTextColor();
+  if (color.has_value())
+    cp.sTextColor = CFX_Color(color.value());
+
+  cp.fFontSize = m_pWidget->GetFontSize();
+  cp.dwBorderWidth = m_pWidget->GetBorderWidth();
+
+  cp.nBorderStyle = m_pWidget->GetBorderStyle();
+  switch (cp.nBorderStyle) {
+    case BorderStyle::kDash:
+      cp.sDash = CPWL_Dash(3, 3, 0);
+      break;
+    case BorderStyle::kBeveled:
+    case BorderStyle::kInset:
+      cp.dwBorderWidth *= 2;
+      break;
+    default:
+      break;
+  }
+
+  if (cp.fFontSize <= 0)
+    dwCreateFlags |= PWS_AUTOFONTSIZE;
+
+  cp.dwFlags = dwCreateFlags;
+  return cp;
+}
+
+CPWL_Wnd* CFFL_FormField::GetPWLWindow(
+    const CPDFSDK_PageView* pPageView) const {
+  DCHECK(pPageView);
+  auto it = m_Maps.find(pPageView);
+  return it != m_Maps.end() ? it->second.get() : nullptr;
+}
+
+CPWL_Wnd* CFFL_FormField::CreateOrUpdatePWLWindow(
+    const CPDFSDK_PageView* pPageView) {
+  DCHECK(pPageView);
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView);
+  if (!pWnd) {
+    CPWL_Wnd::CreateParams cp = GetCreateParam();
+    // TODO(tsepez): maybe pass widget's value age as 4th arg.
+    auto pPrivateData = std::make_unique<CFFL_PerWindowData>(
+        m_pWidget, pPageView, m_pWidget->GetAppearanceAge(), 0);
+    m_Maps[pPageView] = NewPWLWindow(cp, std::move(pPrivateData));
+    return m_Maps[pPageView].get();
+  }
+  const auto* pPrivateData =
+      static_cast<const CFFL_PerWindowData*>(pWnd->GetAttachedData());
+  if (pPrivateData->AppearanceAgeEquals(m_pWidget->GetAppearanceAge()))
+    return pWnd;
+
+  return ResetPWLWindowForValueAgeInternal(pPageView, m_pWidget,
+                                           pPrivateData->GetValueAge());
+}
+
+void CFFL_FormField::DestroyPWLWindow(const CPDFSDK_PageView* pPageView) {
+  auto it = m_Maps.find(pPageView);
+  if (it == m_Maps.end())
+    return;
+
+  std::unique_ptr<CPWL_Wnd> pWnd = std::move(it->second);
+  m_Maps.erase(it);
+  pWnd->Destroy();
+}
+
+CFX_Matrix CFFL_FormField::GetWindowMatrix(
+    const IPWL_FillerNotify::PerWindowData* pAttached) {
+  const auto* pPrivateData = static_cast<const CFFL_PerWindowData*>(pAttached);
+  if (!pPrivateData)
+    return CFX_Matrix();
+
+  const CPDFSDK_PageView* pPageView = pPrivateData->GetPageView();
+  if (!pPageView)
+    return CFX_Matrix();
+
+  return GetCurMatrix() * pPageView->GetCurrentMatrix();
+}
+
+void CFFL_FormField::OnSetFocusForEdit(CPWL_Edit* pEdit) {
+  // Only sub-classes might have a subordinate edit to focus.
+}
+
+CFX_Matrix CFFL_FormField::GetCurMatrix() {
+  CFX_Matrix mt;
+  CFX_FloatRect rcDA = m_pWidget->GetPDFAnnot()->GetRect();
+  switch (m_pWidget->GetRotate()) {
+    case 90:
+      mt = CFX_Matrix(0, 1, -1, 0, rcDA.right - rcDA.left, 0);
+      break;
+    case 180:
+      mt = CFX_Matrix(-1, 0, 0, -1, rcDA.right - rcDA.left,
+                      rcDA.top - rcDA.bottom);
+      break;
+    case 270:
+      mt = CFX_Matrix(0, -1, 1, 0, 0, rcDA.top - rcDA.bottom);
+      break;
+    case 0:
+    default:
+      break;
+  }
+  mt.e += rcDA.left;
+  mt.f += rcDA.bottom;
+
+  return mt;
+}
+
+CFX_FloatRect CFFL_FormField::GetPDFAnnotRect() const {
+  CFX_FloatRect rectAnnot = m_pWidget->GetPDFAnnot()->GetRect();
+  float fWidth = rectAnnot.Width();
+  float fHeight = rectAnnot.Height();
+  if ((m_pWidget->GetRotate() / 90) & 0x01)
+    std::swap(fWidth, fHeight);
+  return CFX_FloatRect(0, 0, fWidth, fHeight);
+}
+
+CPDFSDK_PageView* CFFL_FormField::GetCurPageView() {
+  return m_pFormFiller->GetOrCreatePageView(m_pWidget->GetPage());
+}
+
+CFX_FloatRect CFFL_FormField::GetFocusBox(const CPDFSDK_PageView* pPageView) {
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView);
+  if (!pWnd)
+    return CFX_FloatRect();
+
+  CFX_FloatRect rcFocus = PWLtoFFL(pWnd->GetFocusRect());
+  return pPageView->GetPDFPage()->GetBBox().Contains(rcFocus) ? rcFocus
+                                                              : CFX_FloatRect();
+}
+
+CFX_FloatRect CFFL_FormField::FFLtoPWL(const CFX_FloatRect& rect) {
+  return GetCurMatrix().GetInverse().TransformRect(rect);
+}
+
+CFX_FloatRect CFFL_FormField::PWLtoFFL(const CFX_FloatRect& rect) {
+  return GetCurMatrix().TransformRect(rect);
+}
+
+CFX_PointF CFFL_FormField::FFLtoPWL(const CFX_PointF& point) {
+  return GetCurMatrix().GetInverse().Transform(point);
+}
+
+CFX_PointF CFFL_FormField::PWLtoFFL(const CFX_PointF& point) {
+  return GetCurMatrix().Transform(point);
+}
+
+bool CFFL_FormField::CommitData(const CPDFSDK_PageView* pPageView,
+                                Mask<FWL_EVENTFLAG> nFlag) {
+  if (!IsDataChanged(pPageView))
+    return true;
+
+  ObservedPtr<CPDFSDK_Widget> pObserved(m_pWidget);
+  if (!m_pFormFiller->OnKeyStrokeCommit(pObserved, pPageView, nFlag)) {
+    if (!pObserved)
+      return false;
+    ResetPWLWindow(pPageView);
+    return true;
+  }
+  if (!pObserved)
+    return false;
+
+  if (!m_pFormFiller->OnValidate(pObserved, pPageView, nFlag)) {
+    if (!pObserved)
+      return false;
+    ResetPWLWindow(pPageView);
+    return true;
+  }
+  if (!pObserved)
+    return false;
+
+  SaveData(pPageView);  // may invoking JS to delete this widget.
+  if (!pObserved)
+    return false;
+
+  m_pFormFiller->OnCalculate(pObserved);
+  if (!pObserved)
+    return false;
+
+  m_pFormFiller->OnFormat(pObserved);
+  if (!pObserved)
+    return false;
+
+  return true;
+}
+
+bool CFFL_FormField::IsDataChanged(const CPDFSDK_PageView* pPageView) {
+  return false;
+}
+
+void CFFL_FormField::SaveData(const CPDFSDK_PageView* pPageView) {}
+
+#ifdef PDF_ENABLE_XFA
+bool CFFL_FormField::IsFieldFull(const CPDFSDK_PageView* pPageView) {
+  return false;
+}
+#endif  // PDF_ENABLE_XFA
+
+void CFFL_FormField::SetChangeMark() {
+  m_pFormFiller->OnChange();
+}
+
+void CFFL_FormField::GetActionData(const CPDFSDK_PageView* pPageView,
+                                   CPDF_AAction::AActionType type,
+                                   CFFL_FieldAction& fa) {
+  fa.sValue = m_pWidget->GetValue();
+}
+
+void CFFL_FormField::SetActionData(const CPDFSDK_PageView* pPageView,
+                                   CPDF_AAction::AActionType type,
+                                   const CFFL_FieldAction& fa) {}
+
+void CFFL_FormField::SavePWLWindowState(const CPDFSDK_PageView* pPageView) {}
+
+void CFFL_FormField::RecreatePWLWindowFromSavedState(
+    const CPDFSDK_PageView* pPageView) {}
+
+CFFL_PerWindowData* CFFL_FormField::GetPerPWLWindowData(
+    const CPDFSDK_PageView* pPageView) {
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView);
+  if (!pWnd)
+    return nullptr;
+
+  return static_cast<CFFL_PerWindowData*>(pWnd->GetAttachedData());
+}
+
+void CFFL_FormField::ResetPWLWindowForValueAge(
+    const CPDFSDK_PageView* pPageView,
+    CPDFSDK_Widget* pWidget,
+    uint32_t nValueAge) {
+  // Don't leak PWL_Wnd result to public callers.
+  ResetPWLWindowForValueAgeInternal(pPageView, pWidget, nValueAge);
+}
+
+CPWL_Wnd* CFFL_FormField::ResetPWLWindowForValueAgeInternal(
+    const CPDFSDK_PageView* pPageView,
+    CPDFSDK_Widget* pWidget,
+    uint32_t nValueAge) {
+  return nValueAge == pWidget->GetValueAge() ? RestorePWLWindow(pPageView)
+                                             : ResetPWLWindow(pPageView);
+}
+
+CPWL_Wnd* CFFL_FormField::ResetPWLWindow(const CPDFSDK_PageView* pPageView) {
+  return GetPWLWindow(pPageView);
+}
+
+CPWL_Wnd* CFFL_FormField::RestorePWLWindow(const CPDFSDK_PageView* pPageView) {
+  return GetPWLWindow(pPageView);
+}
+
+void CFFL_FormField::OnTimerFired() {}
+
+void CFFL_FormField::EscapeFiller(CPDFSDK_PageView* pPageView,
+                                  bool bDestroyPWLWindow) {
+  m_bValid = false;
+
+  InvalidateRect(GetViewBBox(pPageView));
+  if (bDestroyPWLWindow)
+    DestroyPWLWindow(pPageView);
+}
+
+void CFFL_FormField::InvalidateRect(const FX_RECT& rect) {
+  m_pFormFiller->Invalidate(m_pWidget->GetPage(), rect);
+}
diff --git a/fpdfsdk/formfiller/cffl_formfield.h b/fpdfsdk/formfiller/cffl_formfield.h
new file mode 100644
index 0000000..1ddf730
--- /dev/null
+++ b/fpdfsdk/formfiller/cffl_formfield.h
@@ -0,0 +1,174 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_FORMFILLER_CFFL_FORMFIELD_H_
+#define FPDFSDK_FORMFILLER_CFFL_FORMFIELD_H_
+
+#include <map>
+#include <memory>
+
+#include "core/fpdfdoc/cpdf_aaction.h"
+#include "core/fxcrt/cfx_timer.h"
+#include "core/fxcrt/mask.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "fpdfsdk/formfiller/cffl_fieldaction.h"
+#include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
+#include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "fpdfsdk/pwl/ipwl_fillernotify.h"
+
+class CFFL_PerWindowData;
+class CPDFSDK_PageView;
+class CPDFSDK_Widget;
+
+class CFFL_FormField : public CPWL_Wnd::ProviderIface,
+                       public CFX_Timer::CallbackIface {
+ public:
+  CFFL_FormField(CFFL_InteractiveFormFiller* pFormFiller,
+                 CPDFSDK_Widget* pWidget);
+  ~CFFL_FormField() override;
+
+  virtual void OnDraw(CPDFSDK_PageView* pPageView,
+                      CPDFSDK_Widget* pWidget,
+                      CFX_RenderDevice* pDevice,
+                      const CFX_Matrix& mtUser2Device);
+  virtual void OnDrawDeactive(CPDFSDK_PageView* pPageView,
+                              CPDFSDK_Widget* pWidget,
+                              CFX_RenderDevice* pDevice,
+                              const CFX_Matrix& mtUser2Device);
+
+  virtual void OnMouseEnter(CPDFSDK_PageView* pPageView);
+  virtual void OnMouseExit(CPDFSDK_PageView* pPageView);
+
+  virtual bool OnLButtonDown(CPDFSDK_PageView* pPageView,
+                             CPDFSDK_Widget* pAnnot,
+                             Mask<FWL_EVENTFLAG> nFlags,
+                             const CFX_PointF& point);
+  virtual bool OnLButtonUp(CPDFSDK_PageView* pPageView,
+                           CPDFSDK_Widget* pAnnot,
+                           Mask<FWL_EVENTFLAG> nFlags,
+                           const CFX_PointF& point);
+  virtual bool OnLButtonDblClk(CPDFSDK_PageView* pPageView,
+                               Mask<FWL_EVENTFLAG> nFlags,
+                               const CFX_PointF& point);
+  virtual bool OnMouseMove(CPDFSDK_PageView* pPageView,
+                           Mask<FWL_EVENTFLAG> nFlags,
+                           const CFX_PointF& point);
+  virtual bool OnMouseWheel(CPDFSDK_PageView* pPageView,
+                            Mask<FWL_EVENTFLAG> nFlags,
+                            const CFX_PointF& point,
+                            const CFX_Vector& delta);
+  virtual bool OnRButtonDown(CPDFSDK_PageView* pPageView,
+                             Mask<FWL_EVENTFLAG> nFlags,
+                             const CFX_PointF& point);
+  virtual bool OnRButtonUp(CPDFSDK_PageView* pPageView,
+                           Mask<FWL_EVENTFLAG> nFlags,
+                           const CFX_PointF& point);
+
+  virtual bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlags);
+  virtual bool OnChar(CPDFSDK_Widget* pAnnot,
+                      uint32_t nChar,
+                      Mask<FWL_EVENTFLAG> nFlags);
+  virtual bool SetIndexSelected(int index, bool selected);
+  virtual bool IsIndexSelected(int index);
+
+  FX_RECT GetViewBBox(const CPDFSDK_PageView* pPageView);
+
+  WideString GetText();
+  WideString GetSelectedText();
+  void ReplaceAndKeepSelection(const WideString& text);
+  void ReplaceSelection(const WideString& text);
+  bool SelectAllText();
+
+  bool CanUndo();
+  bool CanRedo();
+  bool Undo();
+  bool Redo();
+
+  void SetFocusForAnnot(CPDFSDK_Widget* pWidget, Mask<FWL_EVENTFLAG> nFlag);
+  void KillFocusForAnnot(Mask<FWL_EVENTFLAG> nFlag);
+
+  // CFX_Timer::CallbackIface:
+  void OnTimerFired() override;
+
+  // CPWL_Wnd::ProviderIface:
+  CFX_Matrix GetWindowMatrix(
+      const IPWL_FillerNotify::PerWindowData* pAttached) override;
+  void OnSetFocusForEdit(CPWL_Edit* pEdit) override;
+
+  virtual void GetActionData(const CPDFSDK_PageView* pPageView,
+                             CPDF_AAction::AActionType type,
+                             CFFL_FieldAction& fa);
+  virtual void SetActionData(const CPDFSDK_PageView* pPageView,
+                             CPDF_AAction::AActionType type,
+                             const CFFL_FieldAction& fa);
+  virtual CPWL_Wnd::CreateParams GetCreateParam();
+  virtual std::unique_ptr<CPWL_Wnd> NewPWLWindow(
+      const CPWL_Wnd::CreateParams& cp,
+      std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData) = 0;
+  virtual void SavePWLWindowState(const CPDFSDK_PageView* pPageView);
+  virtual void RecreatePWLWindowFromSavedState(
+      const CPDFSDK_PageView* pPageView);
+  virtual bool IsDataChanged(const CPDFSDK_PageView* pPageView);
+  virtual void SaveData(const CPDFSDK_PageView* pPageView);
+#ifdef PDF_ENABLE_XFA
+  virtual bool IsFieldFull(const CPDFSDK_PageView* pPageView);
+#endif  // PDF_ENABLE_XFA
+
+  CFX_Matrix GetCurMatrix();
+  CFX_FloatRect GetFocusBox(const CPDFSDK_PageView* pPageView);
+  CFX_FloatRect FFLtoPWL(const CFX_FloatRect& rect);
+  CFX_FloatRect PWLtoFFL(const CFX_FloatRect& rect);
+  CFX_PointF FFLtoPWL(const CFX_PointF& point);
+  CFX_PointF PWLtoFFL(const CFX_PointF& point);
+  bool CommitData(const CPDFSDK_PageView* pPageView, Mask<FWL_EVENTFLAG> nFlag);
+  void DestroyPWLWindow(const CPDFSDK_PageView* pPageView);
+  void EscapeFiller(CPDFSDK_PageView* pPageView, bool bDestroyPWLWindow);
+
+  bool IsValid() const;
+  CFX_FloatRect GetPDFAnnotRect() const;
+
+  CPDFSDK_PageView* GetCurPageView();
+  void SetChangeMark();
+
+  CPDFSDK_Widget* GetSDKWidget() const { return m_pWidget; }
+
+  CFFL_PerWindowData* GetPerPWLWindowData(const CPDFSDK_PageView* pPageView);
+  void ResetPWLWindowForValueAge(const CPDFSDK_PageView* pPageView,
+                                 CPDFSDK_Widget* pWidget,
+                                 uint32_t nValueAge);
+
+ protected:
+  friend class CPWLComboBoxEmbedderTest;
+  friend class CPWLEditEmbedderTest;
+  friend class CPWLSpecialButtonEmbedderTest;
+
+  virtual CPWL_Wnd* ResetPWLWindow(const CPDFSDK_PageView* pPageView);
+  virtual CPWL_Wnd* RestorePWLWindow(const CPDFSDK_PageView* pPageView);
+
+  CPWL_Wnd* GetPWLWindow(const CPDFSDK_PageView* pPageView) const;
+  CPWL_Wnd* CreateOrUpdatePWLWindow(const CPDFSDK_PageView* pPageView);
+  CPWL_Wnd* ResetPWLWindowForValueAgeInternal(const CPDFSDK_PageView* pPageView,
+                                              CPDFSDK_Widget* pWidget,
+                                              uint32_t nValueAge);
+
+  // If the inheriting widget has its own fontmap and a PWL_Edit widget that
+  // access that fontmap then you have to call DestroyWindows before destroying
+  // the font map in order to not get a use-after-free.
+  //
+  // The font map should be stored somewhere more appropriate so it will live
+  // until the PWL_Edit is done with it. pdfium:566
+  void DestroyWindows();
+
+  void InvalidateRect(const FX_RECT& rect);
+
+  bool m_bValid = false;
+  UnownedPtr<CFFL_InteractiveFormFiller> const m_pFormFiller;
+  UnownedPtr<CPDFSDK_Widget> m_pWidget;
+  std::unique_ptr<CFX_Timer> m_pTimer;
+  std::map<const CPDFSDK_PageView*, std::unique_ptr<CPWL_Wnd>> m_Maps;
+};
+
+#endif  // FPDFSDK_FORMFILLER_CFFL_FORMFIELD_H_
diff --git a/fpdfsdk/formfiller/cffl_formfiller.cpp b/fpdfsdk/formfiller/cffl_formfiller.cpp
deleted file mode 100644
index a214920..0000000
--- a/fpdfsdk/formfiller/cffl_formfiller.cpp
+++ /dev/null
@@ -1,547 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/formfiller/cffl_formfiller.h"
-
-#include <utility>
-
-#include "constants/form_flags.h"
-#include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fxge/cfx_renderdevice.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_pageview.h"
-#include "fpdfsdk/cpdfsdk_widget.h"
-
-CFFL_FormFiller::CFFL_FormFiller(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                 CPDFSDK_Widget* pWidget)
-    : m_pFormFillEnv(pFormFillEnv), m_pWidget(pWidget) {
-  ASSERT(m_pFormFillEnv);
-}
-
-CFFL_FormFiller::~CFFL_FormFiller() {
-  DestroyWindows();
-}
-
-void CFFL_FormFiller::DestroyWindows() {
-  while (!m_Maps.empty()) {
-    auto it = m_Maps.begin();
-    std::unique_ptr<CPWL_Wnd> pWnd = std::move(it->second);
-    m_Maps.erase(it);
-    pWnd->InvalidateProvider(this);
-    pWnd->Destroy();
-  }
-}
-
-FX_RECT CFFL_FormFiller::GetViewBBox(CPDFSDK_PageView* pPageView) {
-  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false);
-  CFX_FloatRect rcAnnot =
-      pWnd ? PWLtoFFL(pWnd->GetWindowRect()) : m_pWidget->GetRect();
-  CFX_FloatRect rcFocus = GetFocusBox(pPageView);
-
-  CFX_FloatRect rcWin = rcAnnot;
-  if (!rcFocus.IsEmpty())
-    rcWin.Union(rcFocus);
-  if (!rcWin.IsEmpty()) {
-    rcWin.Inflate(1, 1);
-    rcWin.Normalize();
-  }
-
-  return rcWin.GetOuterRect();
-}
-
-void CFFL_FormFiller::OnDraw(CPDFSDK_PageView* pPageView,
-                             CPDFSDK_Annot* pAnnot,
-                             CFX_RenderDevice* pDevice,
-                             const CFX_Matrix& mtUser2Device) {
-  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false);
-  if (pWnd) {
-    pWnd->DrawAppearance(pDevice, GetCurMatrix() * mtUser2Device);
-    return;
-  }
-
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
-  if (!CFFL_InteractiveFormFiller::IsVisible(pWidget))
-    return;
-
-  pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Normal, nullptr);
-}
-
-void CFFL_FormFiller::OnDrawDeactive(CPDFSDK_PageView* pPageView,
-                                     CPDFSDK_Annot* pAnnot,
-                                     CFX_RenderDevice* pDevice,
-                                     const CFX_Matrix& mtUser2Device) {
-  ToCPDFSDKWidget(pAnnot)->DrawAppearance(pDevice, mtUser2Device,
-                                          CPDF_Annot::Normal, nullptr);
-}
-
-void CFFL_FormFiller::OnMouseEnter(CPDFSDK_PageView* pPageView) {}
-
-void CFFL_FormFiller::OnMouseExit(CPDFSDK_PageView* pPageView) {
-  m_pTimer.reset();
-  ASSERT(m_pWidget);
-}
-
-bool CFFL_FormFiller::OnLButtonDown(CPDFSDK_PageView* pPageView,
-                                    CPDFSDK_Annot* pAnnot,
-                                    uint32_t nFlags,
-                                    const CFX_PointF& point) {
-  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, true);
-  if (!pWnd)
-    return false;
-
-  m_bValid = true;
-  FX_RECT rect = GetViewBBox(pPageView);
-  InvalidateRect(rect);
-  if (!rect.Contains(static_cast<int>(point.x), static_cast<int>(point.y)))
-    return false;
-  return pWnd->OnLButtonDown(WndtoPWL(point), nFlags);
-}
-
-bool CFFL_FormFiller::OnLButtonUp(CPDFSDK_PageView* pPageView,
-                                  CPDFSDK_Annot* pAnnot,
-                                  uint32_t nFlags,
-                                  const CFX_PointF& point) {
-  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false);
-  if (!pWnd)
-    return false;
-
-  InvalidateRect(GetViewBBox(pPageView));
-  pWnd->OnLButtonUp(WndtoPWL(point), nFlags);
-  return true;
-}
-
-bool CFFL_FormFiller::OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                                      uint32_t nFlags,
-                                      const CFX_PointF& point) {
-  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false);
-  if (!pWnd)
-    return false;
-
-  pWnd->OnLButtonDblClk(WndtoPWL(point), nFlags);
-  return true;
-}
-
-bool CFFL_FormFiller::OnMouseMove(CPDFSDK_PageView* pPageView,
-                                  uint32_t nFlags,
-                                  const CFX_PointF& point) {
-  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false);
-  if (!pWnd)
-    return false;
-
-  pWnd->OnMouseMove(WndtoPWL(point), nFlags);
-  return true;
-}
-
-bool CFFL_FormFiller::OnMouseWheel(CPDFSDK_PageView* pPageView,
-                                   uint32_t nFlags,
-                                   short zDelta,
-                                   const CFX_PointF& point) {
-  if (!IsValid())
-    return false;
-
-  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, true);
-  return pWnd && pWnd->OnMouseWheel(zDelta, WndtoPWL(point), nFlags);
-}
-
-bool CFFL_FormFiller::OnRButtonDown(CPDFSDK_PageView* pPageView,
-                                    uint32_t nFlags,
-                                    const CFX_PointF& point) {
-  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, true);
-  return pWnd && pWnd->OnRButtonDown(WndtoPWL(point), nFlags);
-}
-
-bool CFFL_FormFiller::OnRButtonUp(CPDFSDK_PageView* pPageView,
-                                  uint32_t nFlags,
-                                  const CFX_PointF& point) {
-  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false);
-  return pWnd && pWnd->OnRButtonUp(WndtoPWL(point), nFlags);
-}
-
-bool CFFL_FormFiller::OnKeyDown(uint32_t nKeyCode, uint32_t nFlags) {
-  if (!IsValid())
-    return false;
-
-  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
-  return pWnd && pWnd->OnKeyDown(nKeyCode, nFlags);
-}
-
-bool CFFL_FormFiller::OnChar(CPDFSDK_Annot* pAnnot,
-                             uint32_t nChar,
-                             uint32_t nFlags) {
-  if (!IsValid())
-    return false;
-
-  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
-  return pWnd && pWnd->OnChar(nChar, nFlags);
-}
-
-bool CFFL_FormFiller::SetIndexSelected(int index, bool selected) {
-  return false;
-}
-
-bool CFFL_FormFiller::IsIndexSelected(int index) {
-  return false;
-}
-
-WideString CFFL_FormFiller::GetText() {
-  if (!IsValid())
-    return WideString();
-
-  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
-  return pWnd ? pWnd->GetText() : WideString();
-}
-
-WideString CFFL_FormFiller::GetSelectedText() {
-  if (!IsValid())
-    return WideString();
-
-  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
-  return pWnd ? pWnd->GetSelectedText() : WideString();
-}
-
-void CFFL_FormFiller::ReplaceSelection(const WideString& text) {
-  if (!IsValid())
-    return;
-
-  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
-  if (!pWnd)
-    return;
-
-  pWnd->ReplaceSelection(text);
-}
-
-bool CFFL_FormFiller::CanUndo() {
-  if (!IsValid())
-    return false;
-
-  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
-  return pWnd && pWnd->CanUndo();
-}
-
-bool CFFL_FormFiller::CanRedo() {
-  if (!IsValid())
-    return false;
-
-  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
-  return pWnd && pWnd->CanRedo();
-}
-
-bool CFFL_FormFiller::Undo() {
-  if (!IsValid())
-    return false;
-
-  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
-  return pWnd && pWnd->Undo();
-}
-
-bool CFFL_FormFiller::Redo() {
-  if (!IsValid())
-    return false;
-
-  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
-  return pWnd && pWnd->Redo();
-}
-
-void CFFL_FormFiller::SetFocusForAnnot(CPDFSDK_Annot* pAnnot, uint32_t nFlag) {
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
-  IPDF_Page* pPage = pWidget->GetPage();
-  CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(pPage, true);
-  if (CPWL_Wnd* pWnd = GetPWLWindow(pPageView, true))
-    pWnd->SetFocus();
-
-  m_bValid = true;
-  InvalidateRect(GetViewBBox(pPageView));
-}
-
-void CFFL_FormFiller::KillFocusForAnnot(uint32_t nFlag) {
-  if (!IsValid())
-    return;
-
-  CPDFSDK_PageView* pPageView = GetCurPageView(false);
-  if (!pPageView || !CommitData(pPageView, nFlag))
-    return;
-  if (CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false))
-    pWnd->KillFocus();
-
-  bool bDestroyPWLWindow;
-  switch (m_pWidget->GetFieldType()) {
-    case FormFieldType::kPushButton:
-    case FormFieldType::kCheckBox:
-    case FormFieldType::kRadioButton:
-      bDestroyPWLWindow = true;
-      break;
-    default:
-      bDestroyPWLWindow = false;
-      break;
-  }
-  EscapeFiller(pPageView, bDestroyPWLWindow);
-}
-
-bool CFFL_FormFiller::IsValid() const {
-  return m_bValid;
-}
-
-CPWL_Wnd::CreateParams CFFL_FormFiller::GetCreateParam() {
-  CPWL_Wnd::CreateParams cp;
-  cp.pProvider.Reset(this);
-  cp.rcRectWnd = GetPDFAnnotRect();
-
-  uint32_t dwCreateFlags = PWS_BORDER | PWS_BACKGROUND | PWS_VISIBLE;
-  uint32_t dwFieldFlag = m_pWidget->GetFieldFlags();
-  if (dwFieldFlag & pdfium::form_flags::kReadOnly)
-    dwCreateFlags |= PWS_READONLY;
-
-  Optional<FX_COLORREF> color = m_pWidget->GetFillColor();
-  if (color.has_value())
-    cp.sBackgroundColor = CFX_Color(color.value());
-  color = m_pWidget->GetBorderColor();
-  if (color.has_value())
-    cp.sBorderColor = CFX_Color(color.value());
-
-  cp.sTextColor = CFX_Color(CFX_Color::kGray, 0);
-
-  color = m_pWidget->GetTextColor();
-  if (color.has_value())
-    cp.sTextColor = CFX_Color(color.value());
-
-  cp.fFontSize = m_pWidget->GetFontSize();
-  cp.dwBorderWidth = m_pWidget->GetBorderWidth();
-
-  cp.nBorderStyle = m_pWidget->GetBorderStyle();
-  switch (cp.nBorderStyle) {
-    case BorderStyle::DASH:
-      cp.sDash = CPWL_Dash(3, 3, 0);
-      break;
-    case BorderStyle::BEVELED:
-    case BorderStyle::INSET:
-      cp.dwBorderWidth *= 2;
-      break;
-    default:
-      break;
-  }
-
-  if (cp.fFontSize <= 0)
-    dwCreateFlags |= PWS_AUTOFONTSIZE;
-
-  cp.dwFlags = dwCreateFlags;
-  cp.pTimerHandler = m_pFormFillEnv->GetTimerHandler();
-  cp.pSystemHandler = m_pFormFillEnv->GetSysHandler();
-  return cp;
-}
-
-CPWL_Wnd* CFFL_FormFiller::GetPWLWindow(CPDFSDK_PageView* pPageView,
-                                        bool bNew) {
-  ASSERT(pPageView);
-  auto it = m_Maps.find(pPageView);
-  if (it == m_Maps.end()) {
-    if (!bNew)
-      return nullptr;
-
-    CPWL_Wnd::CreateParams cp = GetCreateParam();
-    auto pPrivateData = pdfium::MakeUnique<CFFL_PrivateData>();
-    pPrivateData->pWidget.Reset(m_pWidget.Get());
-    pPrivateData->pPageView = pPageView;
-    pPrivateData->nWidgetAppearanceAge = m_pWidget->GetAppearanceAge();
-    pPrivateData->nWidgetValueAge = 0;
-    m_Maps[pPageView] = NewPWLWindow(cp, std::move(pPrivateData));
-    return m_Maps[pPageView].get();
-  }
-
-  CPWL_Wnd* pWnd = it->second.get();
-  if (!bNew)
-    return pWnd;
-
-  const auto* pPrivateData =
-      static_cast<const CFFL_PrivateData*>(pWnd->GetAttachedData());
-  if (pPrivateData->nWidgetAppearanceAge == m_pWidget->GetAppearanceAge())
-    return pWnd;
-
-  return ResetPWLWindow(
-      pPageView, pPrivateData->nWidgetValueAge == m_pWidget->GetValueAge());
-}
-
-void CFFL_FormFiller::DestroyPWLWindow(CPDFSDK_PageView* pPageView) {
-  auto it = m_Maps.find(pPageView);
-  if (it == m_Maps.end())
-    return;
-
-  std::unique_ptr<CPWL_Wnd> pWnd = std::move(it->second);
-  m_Maps.erase(it);
-  pWnd->Destroy();
-}
-
-CFX_Matrix CFFL_FormFiller::GetWindowMatrix(
-    const IPWL_SystemHandler::PerWindowData* pAttached) {
-  const auto* pPrivateData = static_cast<const CFFL_PrivateData*>(pAttached);
-  if (!pPrivateData || !pPrivateData->pPageView)
-    return CFX_Matrix();
-
-  return GetCurMatrix() * pPrivateData->pPageView->GetCurrentMatrix();
-}
-
-CFX_Matrix CFFL_FormFiller::GetCurMatrix() {
-  CFX_Matrix mt;
-  CFX_FloatRect rcDA = m_pWidget->GetPDFAnnot()->GetRect();
-  switch (m_pWidget->GetRotate()) {
-    case 90:
-      mt = CFX_Matrix(0, 1, -1, 0, rcDA.right - rcDA.left, 0);
-      break;
-    case 180:
-      mt = CFX_Matrix(-1, 0, 0, -1, rcDA.right - rcDA.left,
-                      rcDA.top - rcDA.bottom);
-      break;
-    case 270:
-      mt = CFX_Matrix(0, -1, 1, 0, 0, rcDA.top - rcDA.bottom);
-      break;
-    case 0:
-    default:
-      break;
-  }
-  mt.e += rcDA.left;
-  mt.f += rcDA.bottom;
-
-  return mt;
-}
-
-CFX_FloatRect CFFL_FormFiller::GetPDFAnnotRect() const {
-  CFX_FloatRect rectAnnot = m_pWidget->GetPDFAnnot()->GetRect();
-
-  float fWidth = rectAnnot.Width();
-  float fHeight = rectAnnot.Height();
-  if ((m_pWidget->GetRotate() / 90) & 0x01)
-    std::swap(fWidth, fHeight);
-  return CFX_FloatRect(0, 0, fWidth, fHeight);
-}
-
-CPDFSDK_PageView* CFFL_FormFiller::GetCurPageView(bool renew) {
-  IPDF_Page* pPage = m_pWidget->GetPage();
-  return m_pFormFillEnv->GetPageView(pPage, renew);
-}
-
-CFX_FloatRect CFFL_FormFiller::GetFocusBox(CPDFSDK_PageView* pPageView) {
-  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false);
-  if (!pWnd)
-    return CFX_FloatRect();
-
-  CFX_FloatRect rcFocus = FFLtoWnd(PWLtoFFL(pWnd->GetFocusRect()));
-  return pPageView->GetPDFPage()->GetBBox().Contains(rcFocus) ? rcFocus
-                                                              : CFX_FloatRect();
-}
-
-CFX_FloatRect CFFL_FormFiller::FFLtoPWL(const CFX_FloatRect& rect) {
-  return GetCurMatrix().GetInverse().TransformRect(rect);
-}
-
-CFX_FloatRect CFFL_FormFiller::PWLtoFFL(const CFX_FloatRect& rect) {
-  return GetCurMatrix().TransformRect(rect);
-}
-
-CFX_PointF CFFL_FormFiller::FFLtoPWL(const CFX_PointF& point) {
-  return GetCurMatrix().GetInverse().Transform(point);
-}
-
-CFX_PointF CFFL_FormFiller::PWLtoFFL(const CFX_PointF& point) {
-  return GetCurMatrix().Transform(point);
-}
-
-CFX_PointF CFFL_FormFiller::WndtoPWL(const CFX_PointF& pt) {
-  return FFLtoPWL(pt);
-}
-
-CFX_FloatRect CFFL_FormFiller::FFLtoWnd(const CFX_FloatRect& rect) {
-  return rect;
-}
-
-bool CFFL_FormFiller::CommitData(CPDFSDK_PageView* pPageView, uint32_t nFlag) {
-  if (!IsDataChanged(pPageView))
-    return true;
-
-  CFFL_InteractiveFormFiller* pFormFiller =
-      m_pFormFillEnv->GetInteractiveFormFiller();
-  ObservedPtr<CPDFSDK_Annot> pObserved(m_pWidget.Get());
-
-  if (!pFormFiller->OnKeyStrokeCommit(&pObserved, pPageView, nFlag)) {
-    if (!pObserved)
-      return false;
-    ResetPWLWindow(pPageView, false);
-    return true;
-  }
-  if (!pObserved)
-    return false;
-
-  if (!pFormFiller->OnValidate(&pObserved, pPageView, nFlag)) {
-    if (!pObserved)
-      return false;
-    ResetPWLWindow(pPageView, false);
-    return true;
-  }
-  if (!pObserved)
-    return false;
-
-  SaveData(pPageView);  // may invoking JS to delete this widget.
-  if (!pObserved)
-    return false;
-
-  pFormFiller->OnCalculate(&pObserved, pPageView, nFlag);
-  if (!pObserved)
-    return false;
-
-  pFormFiller->OnFormat(&pObserved, pPageView, nFlag);
-  if (!pObserved)
-    return false;
-
-  return true;
-}
-
-bool CFFL_FormFiller::IsDataChanged(CPDFSDK_PageView* pPageView) {
-  return false;
-}
-
-void CFFL_FormFiller::SaveData(CPDFSDK_PageView* pPageView) {}
-
-#ifdef PDF_ENABLE_XFA
-bool CFFL_FormFiller::IsFieldFull(CPDFSDK_PageView* pPageView) {
-  return false;
-}
-#endif  // PDF_ENABLE_XFA
-
-void CFFL_FormFiller::SetChangeMark() {
-  m_pFormFillEnv->OnChange();
-}
-
-void CFFL_FormFiller::GetActionData(CPDFSDK_PageView* pPageView,
-                                    CPDF_AAction::AActionType type,
-                                    CPDFSDK_FieldAction& fa) {
-  fa.sValue = m_pWidget->GetValue();
-}
-
-void CFFL_FormFiller::SetActionData(CPDFSDK_PageView* pPageView,
-                                    CPDF_AAction::AActionType type,
-                                    const CPDFSDK_FieldAction& fa) {}
-
-void CFFL_FormFiller::SaveState(CPDFSDK_PageView* pPageView) {}
-
-void CFFL_FormFiller::RestoreState(CPDFSDK_PageView* pPageView) {}
-
-CPWL_Wnd* CFFL_FormFiller::ResetPWLWindow(CPDFSDK_PageView* pPageView,
-                                          bool bRestoreValue) {
-  return GetPWLWindow(pPageView, false);
-}
-
-void CFFL_FormFiller::OnTimerFired() {}
-
-void CFFL_FormFiller::EscapeFiller(CPDFSDK_PageView* pPageView,
-                                   bool bDestroyPWLWindow) {
-  m_bValid = false;
-
-  InvalidateRect(GetViewBBox(pPageView));
-  if (bDestroyPWLWindow)
-    DestroyPWLWindow(pPageView);
-}
-
-void CFFL_FormFiller::InvalidateRect(const FX_RECT& rect) {
-  m_pFormFillEnv->Invalidate(m_pWidget->GetPage(), rect);
-}
diff --git a/fpdfsdk/formfiller/cffl_formfiller.h b/fpdfsdk/formfiller/cffl_formfiller.h
deleted file mode 100644
index 05bacaf..0000000
--- a/fpdfsdk/formfiller/cffl_formfiller.h
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_FORMFILLER_CFFL_FORMFILLER_H_
-#define FPDFSDK_FORMFILLER_CFFL_FORMFILLER_H_
-
-#include <map>
-#include <memory>
-
-#include "core/fxcrt/cfx_timer.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "fpdfsdk/cpdfsdk_fieldaction.h"
-#include "fpdfsdk/cpdfsdk_widget.h"
-#include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
-#include "fpdfsdk/pwl/cpwl_wnd.h"
-#include "fpdfsdk/pwl/ipwl_systemhandler.h"
-
-class CPDFSDK_Annot;
-class CPDFSDK_FormFillEnvironment;
-class CPDFSDK_PageView;
-
-class CFFL_FormFiller : public CPWL_Wnd::ProviderIface,
-                        public CFX_Timer::CallbackIface {
- public:
-  CFFL_FormFiller(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                  CPDFSDK_Widget* pWidget);
-  ~CFFL_FormFiller() override;
-
-  virtual void OnDraw(CPDFSDK_PageView* pPageView,
-                      CPDFSDK_Annot* pAnnot,
-                      CFX_RenderDevice* pDevice,
-                      const CFX_Matrix& mtUser2Device);
-  virtual void OnDrawDeactive(CPDFSDK_PageView* pPageView,
-                              CPDFSDK_Annot* pAnnot,
-                              CFX_RenderDevice* pDevice,
-                              const CFX_Matrix& mtUser2Device);
-
-  virtual void OnMouseEnter(CPDFSDK_PageView* pPageView);
-  virtual void OnMouseExit(CPDFSDK_PageView* pPageView);
-
-  virtual bool OnLButtonDown(CPDFSDK_PageView* pPageView,
-                             CPDFSDK_Annot* pAnnot,
-                             uint32_t nFlags,
-                             const CFX_PointF& point);
-  virtual bool OnLButtonUp(CPDFSDK_PageView* pPageView,
-                           CPDFSDK_Annot* pAnnot,
-                           uint32_t nFlags,
-                           const CFX_PointF& point);
-  virtual bool OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                               uint32_t nFlags,
-                               const CFX_PointF& point);
-  virtual bool OnMouseMove(CPDFSDK_PageView* pPageView,
-                           uint32_t nFlags,
-                           const CFX_PointF& point);
-  virtual bool OnMouseWheel(CPDFSDK_PageView* pPageView,
-                            uint32_t nFlags,
-                            short zDelta,
-                            const CFX_PointF& point);
-  virtual bool OnRButtonDown(CPDFSDK_PageView* pPageView,
-                             uint32_t nFlags,
-                             const CFX_PointF& point);
-  virtual bool OnRButtonUp(CPDFSDK_PageView* pPageView,
-                           uint32_t nFlags,
-                           const CFX_PointF& point);
-
-  virtual bool OnKeyDown(uint32_t nKeyCode, uint32_t nFlags);
-  virtual bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags);
-  virtual bool SetIndexSelected(int index, bool selected);
-  virtual bool IsIndexSelected(int index);
-
-  FX_RECT GetViewBBox(CPDFSDK_PageView* pPageView);
-
-  WideString GetText();
-  WideString GetSelectedText();
-  void ReplaceSelection(const WideString& text);
-
-  bool CanUndo();
-  bool CanRedo();
-  bool Undo();
-  bool Redo();
-
-  void SetFocusForAnnot(CPDFSDK_Annot* pAnnot, uint32_t nFlag);
-  void KillFocusForAnnot(uint32_t nFlag);
-
-  // CFX_Timer::CallbackIface:
-  void OnTimerFired() override;
-
-  // CPWL_Wnd::ProviderIface:
-  CFX_Matrix GetWindowMatrix(
-      const IPWL_SystemHandler::PerWindowData* pAttached) override;
-
-  virtual void GetActionData(CPDFSDK_PageView* pPageView,
-                             CPDF_AAction::AActionType type,
-                             CPDFSDK_FieldAction& fa);
-  virtual void SetActionData(CPDFSDK_PageView* pPageView,
-                             CPDF_AAction::AActionType type,
-                             const CPDFSDK_FieldAction& fa);
-  virtual CPWL_Wnd::CreateParams GetCreateParam();
-  virtual std::unique_ptr<CPWL_Wnd> NewPWLWindow(
-      const CPWL_Wnd::CreateParams& cp,
-      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData) = 0;
-  virtual CPWL_Wnd* ResetPWLWindow(CPDFSDK_PageView* pPageView,
-                                   bool bRestoreValue);
-  virtual void SaveState(CPDFSDK_PageView* pPageView);
-  virtual void RestoreState(CPDFSDK_PageView* pPageView);
-
-  CFX_Matrix GetCurMatrix();
-  CFX_FloatRect GetFocusBox(CPDFSDK_PageView* pPageView);
-  CFX_FloatRect FFLtoPWL(const CFX_FloatRect& rect);
-  CFX_FloatRect PWLtoFFL(const CFX_FloatRect& rect);
-  CFX_PointF FFLtoPWL(const CFX_PointF& point);
-  CFX_PointF PWLtoFFL(const CFX_PointF& point);
-  CFX_PointF WndtoPWL(const CFX_PointF& pt);
-  CFX_FloatRect FFLtoWnd(const CFX_FloatRect& rect);
-
-  bool CommitData(CPDFSDK_PageView* pPageView, uint32_t nFlag);
-  virtual bool IsDataChanged(CPDFSDK_PageView* pPageView);
-  virtual void SaveData(CPDFSDK_PageView* pPageView);
-
-#ifdef PDF_ENABLE_XFA
-  virtual bool IsFieldFull(CPDFSDK_PageView* pPageView);
-#endif  // PDF_ENABLE_XFA
-
-  CPWL_Wnd* GetPWLWindow(CPDFSDK_PageView* pPageView, bool bNew);
-  void DestroyPWLWindow(CPDFSDK_PageView* pPageView);
-  void EscapeFiller(CPDFSDK_PageView* pPageView, bool bDestroyPWLWindow);
-
-  bool IsValid() const;
-  CFX_FloatRect GetPDFAnnotRect() const;
-
-  CPDFSDK_PageView* GetCurPageView(bool renew);
-  void SetChangeMark();
-
-  CPDFSDK_Annot* GetSDKAnnot() const { return m_pWidget.Get(); }
-
- protected:
-  // If the inheriting widget has its own fontmap and a PWL_Edit widget that
-  // access that fontmap then you have to call DestroyWindows before destroying
-  // the font map in order to not get a use-after-free.
-  //
-  // The font map should be stored somewhere more appropriate so it will live
-  // until the PWL_Edit is done with it. pdfium:566
-  void DestroyWindows();
-
-  void InvalidateRect(const FX_RECT& rect);
-
-  bool m_bValid = false;
-  UnownedPtr<CPDFSDK_FormFillEnvironment> const m_pFormFillEnv;
-  UnownedPtr<CPDFSDK_Widget> m_pWidget;
-  std::unique_ptr<CFX_Timer> m_pTimer;
-  std::map<CPDFSDK_PageView*, std::unique_ptr<CPWL_Wnd>> m_Maps;
-};
-
-#endif  // FPDFSDK_FORMFILLER_CFFL_FORMFILLER_H_
diff --git a/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp b/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp
index 6766e1c..843af8c 100644
--- a/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp
+++ b/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,47 +6,47 @@
 
 #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
 
+#include <algorithm>
+
+#include "constants/access_permissions.h"
+#include "constants/ascii.h"
 #include "constants/form_flags.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fxcrt/autorestorer.h"
-#include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
-#include "core/fxge/cfx_renderdevice.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_interactiveform.h"
+#include "core/fxge/cfx_drawutils.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
 #include "fpdfsdk/formfiller/cffl_checkbox.h"
 #include "fpdfsdk/formfiller/cffl_combobox.h"
-#include "fpdfsdk/formfiller/cffl_formfiller.h"
+#include "fpdfsdk/formfiller/cffl_formfield.h"
 #include "fpdfsdk/formfiller/cffl_listbox.h"
+#include "fpdfsdk/formfiller/cffl_perwindowdata.h"
 #include "fpdfsdk/formfiller/cffl_pushbutton.h"
 #include "fpdfsdk/formfiller/cffl_radiobutton.h"
 #include "fpdfsdk/formfiller/cffl_textfield.h"
 #include "public/fpdf_fwlevent.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
 
 CFFL_InteractiveFormFiller::CFFL_InteractiveFormFiller(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv)
-    : m_pFormFillEnv(pFormFillEnv) {}
+    CallbackIface* pCallbackIface)
+    : m_pCallbackIface(pCallbackIface) {}
 
 CFFL_InteractiveFormFiller::~CFFL_InteractiveFormFiller() = default;
 
-bool CFFL_InteractiveFormFiller::Annot_HitTest(CPDFSDK_PageView* pPageView,
-                                               CPDFSDK_Annot* pAnnot,
+bool CFFL_InteractiveFormFiller::Annot_HitTest(const CPDFSDK_Widget* pWidget,
                                                const CFX_PointF& point) {
-  return pAnnot->GetRect().Contains(point);
+  return pWidget->GetRect().Contains(point);
 }
 
-FX_RECT CFFL_InteractiveFormFiller::GetViewBBox(CPDFSDK_PageView* pPageView,
-                                                CPDFSDK_Annot* pAnnot) {
-  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot))
-    return pFormFiller->GetViewBBox(pPageView);
+FX_RECT CFFL_InteractiveFormFiller::GetViewBBox(
+    const CPDFSDK_PageView* pPageView,
+    CPDFSDK_Widget* pWidget) {
+  if (CFFL_FormField* pFormField = GetFormField(pWidget))
+    return pFormField->GetViewBBox(pPageView);
 
-  ASSERT(pPageView);
+  DCHECK(pPageView);
 
-  CPDF_Annot* pPDFAnnot = pAnnot->GetPDFAnnot();
+  CPDF_Annot* pPDFAnnot = pWidget->GetPDFAnnot();
   CFX_FloatRect rcWin = pPDFAnnot->GetRect();
   if (!rcWin.IsEmpty()) {
     rcWin.Inflate(1, 1);
@@ -56,181 +56,161 @@
 }
 
 void CFFL_InteractiveFormFiller::OnDraw(CPDFSDK_PageView* pPageView,
-                                        CPDFSDK_Annot* pAnnot,
+                                        CPDFSDK_Widget* pWidget,
                                         CFX_RenderDevice* pDevice,
                                         const CFX_Matrix& mtUser2Device) {
-  ASSERT(pPageView);
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
+  DCHECK(pPageView);
   if (!IsVisible(pWidget))
     return;
 
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
-  if (pFormFiller && pFormFiller->IsValid()) {
-    pFormFiller->OnDraw(pPageView, pAnnot, pDevice, mtUser2Device);
-    pAnnot->GetPDFPage();
-    if (m_pFormFillEnv->GetFocusAnnot() != pAnnot)
+  CFFL_FormField* pFormField = GetFormField(pWidget);
+  if (pFormField && pFormField->IsValid()) {
+    pFormField->OnDraw(pPageView, pWidget, pDevice, mtUser2Device);
+    if (m_pCallbackIface->GetFocusAnnot() != pWidget)
       return;
 
-    CFX_FloatRect rcFocus = pFormFiller->GetFocusBox(pPageView);
+    CFX_FloatRect rcFocus = pFormField->GetFocusBox(pPageView);
     if (rcFocus.IsEmpty())
       return;
 
-    CFX_PathData path;
-    path.AppendPoint(CFX_PointF(rcFocus.left, rcFocus.top), FXPT_TYPE::MoveTo,
-                     false);
-    path.AppendPoint(CFX_PointF(rcFocus.left, rcFocus.bottom),
-                     FXPT_TYPE::LineTo, false);
-    path.AppendPoint(CFX_PointF(rcFocus.right, rcFocus.bottom),
-                     FXPT_TYPE::LineTo, false);
-    path.AppendPoint(CFX_PointF(rcFocus.right, rcFocus.top), FXPT_TYPE::LineTo,
-                     false);
-    path.AppendPoint(CFX_PointF(rcFocus.left, rcFocus.top), FXPT_TYPE::LineTo,
-                     false);
+    CFX_DrawUtils::DrawFocusRect(pDevice, mtUser2Device, rcFocus);
 
-    CFX_GraphStateData gsd;
-    gsd.m_DashArray = {1.0f};
-    gsd.m_DashPhase = 0;
-    gsd.m_LineWidth = 1.0f;
-    pDevice->DrawPath(&path, &mtUser2Device, &gsd, 0, ArgbEncode(255, 0, 0, 0),
-                      FXFILL_ALTERNATE);
     return;
   }
 
-  if (pFormFiller) {
-    pFormFiller->OnDrawDeactive(pPageView, pAnnot, pDevice, mtUser2Device);
+  if (pFormField) {
+    pFormField->OnDrawDeactive(pPageView, pWidget, pDevice, mtUser2Device);
   } else {
-    pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Normal,
-                            nullptr);
+    pWidget->DrawAppearance(pDevice, mtUser2Device,
+                            CPDF_Annot::AppearanceMode::kNormal);
   }
 
   if (!IsReadOnly(pWidget) && IsFillingAllowed(pWidget))
     pWidget->DrawShadow(pDevice, pPageView);
 }
 
-void CFFL_InteractiveFormFiller::OnDelete(CPDFSDK_Annot* pAnnot) {
-  UnRegisterFormFiller(pAnnot);
+void CFFL_InteractiveFormFiller::OnDelete(CPDFSDK_Widget* pWidget) {
+  UnregisterFormField(pWidget);
 }
 
 void CFFL_InteractiveFormFiller::OnMouseEnter(
     CPDFSDK_PageView* pPageView,
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    uint32_t nFlag) {
-  ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
+    ObservedPtr<CPDFSDK_Widget>& pWidget,
+    Mask<FWL_EVENTFLAG> nFlag) {
   if (!m_bNotifying) {
-    CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
-    if (pWidget->GetAAction(CPDF_AAction::kCursorEnter).GetDict()) {
-      m_bNotifying = true;
-
+    if (pWidget->GetAAction(CPDF_AAction::kCursorEnter).HasDict()) {
       uint32_t nValueAge = pWidget->GetValueAge();
       pWidget->ClearAppModified();
-      ASSERT(pPageView);
+      DCHECK(pPageView);
+      {
+        AutoRestorer<bool> restorer(&m_bNotifying);
+        m_bNotifying = true;
 
-      CPDFSDK_FieldAction fa;
-      fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
-      fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
-      pWidget->OnAAction(CPDF_AAction::kCursorEnter, &fa, pPageView);
-      m_bNotifying = false;
-      if (!pAnnot->HasObservable())
+        CFFL_FieldAction fa;
+        fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+        fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
+        pWidget->OnAAction(CPDF_AAction::kCursorEnter, &fa, pPageView);
+      }
+      if (!pWidget)
         return;
 
       if (pWidget->IsAppModified()) {
-        if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget)) {
-          pFormFiller->ResetPWLWindow(pPageView,
-                                      pWidget->GetValueAge() == nValueAge);
-        }
+        CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+        if (pFormField)
+          pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(),
+                                                nValueAge);
       }
     }
   }
-  if (CFFL_FormFiller* pFormFiller = GetOrCreateFormFiller(pAnnot->Get()))
-    pFormFiller->OnMouseEnter(pPageView);
+  if (CFFL_FormField* pFormField = GetOrCreateFormField(pWidget.Get()))
+    pFormField->OnMouseEnter(pPageView);
 }
 
-void CFFL_InteractiveFormFiller::OnMouseExit(CPDFSDK_PageView* pPageView,
-                                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                             uint32_t nFlag) {
-  ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
+void CFFL_InteractiveFormFiller::OnMouseExit(
+    CPDFSDK_PageView* pPageView,
+    ObservedPtr<CPDFSDK_Widget>& pWidget,
+    Mask<FWL_EVENTFLAG> nFlag) {
   if (!m_bNotifying) {
-    CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
-    if (pWidget->GetAAction(CPDF_AAction::kCursorExit).GetDict()) {
-      m_bNotifying = true;
-
+    if (pWidget->GetAAction(CPDF_AAction::kCursorExit).HasDict()) {
       uint32_t nValueAge = pWidget->GetValueAge();
       pWidget->ClearAppModified();
-      ASSERT(pPageView);
+      DCHECK(pPageView);
+      {
+        AutoRestorer<bool> restorer(&m_bNotifying);
+        m_bNotifying = true;
 
-      CPDFSDK_FieldAction fa;
-      fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
-      fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
-      pWidget->OnAAction(CPDF_AAction::kCursorExit, &fa, pPageView);
-      m_bNotifying = false;
-      if (!pAnnot->HasObservable())
+        CFFL_FieldAction fa;
+        fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+        fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
+        pWidget->OnAAction(CPDF_AAction::kCursorExit, &fa, pPageView);
+      }
+      if (!pWidget)
         return;
 
       if (pWidget->IsAppModified()) {
-        if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget)) {
-          pFormFiller->ResetPWLWindow(pPageView,
-                                      nValueAge == pWidget->GetValueAge());
+        CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+        if (pFormField) {
+          pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(),
+                                                nValueAge);
         }
       }
     }
   }
-  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get()))
-    pFormFiller->OnMouseExit(pPageView);
+  if (CFFL_FormField* pFormField = GetFormField(pWidget.Get()))
+    pFormField->OnMouseExit(pPageView);
 }
 
 bool CFFL_InteractiveFormFiller::OnLButtonDown(
     CPDFSDK_PageView* pPageView,
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    uint32_t nFlags,
+    ObservedPtr<CPDFSDK_Widget>& pWidget,
+    Mask<FWL_EVENTFLAG> nFlags,
     const CFX_PointF& point) {
-  ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
   if (!m_bNotifying) {
-    CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
-    if (Annot_HitTest(pPageView, pAnnot->Get(), point) &&
-        pWidget->GetAAction(CPDF_AAction::kButtonDown).GetDict()) {
-      m_bNotifying = true;
-
+    if (Annot_HitTest(pWidget.Get(), point) &&
+        pWidget->GetAAction(CPDF_AAction::kButtonDown).HasDict()) {
       uint32_t nValueAge = pWidget->GetValueAge();
       pWidget->ClearAppModified();
-      ASSERT(pPageView);
+      DCHECK(pPageView);
+      {
+        AutoRestorer<bool> restorer(&m_bNotifying);
+        m_bNotifying = true;
 
-      CPDFSDK_FieldAction fa;
-      fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlags);
-      fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlags);
-      pWidget->OnAAction(CPDF_AAction::kButtonDown, &fa, pPageView);
-      m_bNotifying = false;
-      if (!pAnnot->HasObservable())
+        CFFL_FieldAction fa;
+        fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlags);
+        fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlags);
+        pWidget->OnAAction(CPDF_AAction::kButtonDown, &fa, pPageView);
+      }
+      if (!pWidget)
         return true;
 
-      if (!IsValidAnnot(pPageView, pAnnot->Get()))
+      if (!IsValidAnnot(pPageView, pWidget.Get()))
         return true;
 
       if (pWidget->IsAppModified()) {
-        if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget)) {
-          pFormFiller->ResetPWLWindow(pPageView,
-                                      nValueAge == pWidget->GetValueAge());
+        CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+        if (pFormField) {
+          pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(),
+                                                nValueAge);
         }
       }
     }
   }
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
-  return pFormFiller &&
-         pFormFiller->OnLButtonDown(pPageView, pAnnot->Get(), nFlags, point);
+  CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+  return pFormField &&
+         pFormField->OnLButtonDown(pPageView, pWidget.Get(), nFlags, point);
 }
 
-bool CFFL_InteractiveFormFiller::OnLButtonUp(CPDFSDK_PageView* pPageView,
-                                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                             uint32_t nFlags,
-                                             const CFX_PointF& point) {
-  ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
-
+bool CFFL_InteractiveFormFiller::OnLButtonUp(
+    CPDFSDK_PageView* pPageView,
+    ObservedPtr<CPDFSDK_Widget>& pWidget,
+    Mask<FWL_EVENTFLAG> nFlags,
+    const CFX_PointF& point) {
   bool bSetFocus;
   switch (pWidget->GetFieldType()) {
     case FormFieldType::kPushButton:
     case FormFieldType::kCheckBox:
     case FormFieldType::kRadioButton: {
-      FX_RECT bbox = GetViewBBox(pPageView, pAnnot->Get());
+      FX_RECT bbox = GetViewBBox(pPageView, pWidget.Get());
       bSetFocus =
           bbox.Contains(static_cast<int>(point.x), static_cast<int>(point.y));
       break;
@@ -239,223 +219,244 @@
       bSetFocus = true;
       break;
   }
-  if (bSetFocus)
-    m_pFormFillEnv->SetFocusAnnot(pAnnot);
+  if (bSetFocus) {
+    ObservedPtr<CPDFSDK_Annot> pObserved(pWidget.Get());
+    m_pCallbackIface->SetFocusAnnot(pObserved);
+  }
 
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
-  bool bRet = pFormFiller &&
-              pFormFiller->OnLButtonUp(pPageView, pAnnot->Get(), nFlags, point);
-  if (m_pFormFillEnv->GetFocusAnnot() != pAnnot->Get())
+  CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+  bool bRet = pFormField &&
+              pFormField->OnLButtonUp(pPageView, pWidget.Get(), nFlags, point);
+  if (m_pCallbackIface->GetFocusAnnot() != pWidget.Get())
     return bRet;
-  if (OnButtonUp(pAnnot, pPageView, nFlags) || !pAnnot)
+  if (OnButtonUp(pWidget, pPageView, nFlags) || !pWidget)
     return true;
 #ifdef PDF_ENABLE_XFA
-  if (OnClick(pAnnot, pPageView, nFlags) || !pAnnot)
+  if (OnClick(pWidget, pPageView, nFlags) || !pWidget)
     return true;
 #endif  // PDF_ENABLE_XFA
   return bRet;
 }
 
-bool CFFL_InteractiveFormFiller::OnButtonUp(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                            CPDFSDK_PageView* pPageView,
-                                            uint32_t nFlag) {
+bool CFFL_InteractiveFormFiller::OnButtonUp(
+    ObservedPtr<CPDFSDK_Widget>& pWidget,
+    const CPDFSDK_PageView* pPageView,
+    Mask<FWL_EVENTFLAG> nFlag) {
   if (m_bNotifying)
     return false;
 
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
-  if (!pWidget->GetAAction(CPDF_AAction::kButtonUp).GetDict())
+  if (!pWidget->GetAAction(CPDF_AAction::kButtonUp).HasDict())
     return false;
 
-  m_bNotifying = true;
-
   uint32_t nAge = pWidget->GetAppearanceAge();
   uint32_t nValueAge = pWidget->GetValueAge();
-  ASSERT(pPageView);
+  DCHECK(pPageView);
+  {
+    AutoRestorer<bool> restorer(&m_bNotifying);
+    m_bNotifying = true;
 
-  CPDFSDK_FieldAction fa;
-  fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
-  fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
-  pWidget->OnAAction(CPDF_AAction::kButtonUp, &fa, pPageView);
-  m_bNotifying = false;
-  if (!pAnnot->HasObservable() || !IsValidAnnot(pPageView, pWidget))
+    CFFL_FieldAction fa;
+    fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+    fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
+    pWidget->OnAAction(CPDF_AAction::kButtonUp, &fa, pPageView);
+  }
+  if (!pWidget || !IsValidAnnot(pPageView, pWidget.Get()))
     return true;
   if (nAge == pWidget->GetAppearanceAge())
     return false;
 
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget);
-  if (pFormFiller)
-    pFormFiller->ResetPWLWindow(pPageView, nValueAge == pWidget->GetValueAge());
+  CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+  if (pFormField)
+    pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(), nValueAge);
   return true;
 }
 
 bool CFFL_InteractiveFormFiller::SetIndexSelected(
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
+    ObservedPtr<CPDFSDK_Widget>& pWidget,
     int index,
     bool selected) {
-  ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
-  return pFormFiller && pFormFiller->SetIndexSelected(index, selected);
+  CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+  return pFormField && pFormField->SetIndexSelected(index, selected);
 }
 
 bool CFFL_InteractiveFormFiller::IsIndexSelected(
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
+    ObservedPtr<CPDFSDK_Widget>& pWidget,
     int index) {
-  ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
-  return pFormFiller && pFormFiller->IsIndexSelected(index);
+  CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+  return pFormField && pFormField->IsIndexSelected(index);
 }
 
 bool CFFL_InteractiveFormFiller::OnLButtonDblClk(
     CPDFSDK_PageView* pPageView,
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    uint32_t nFlags,
+    ObservedPtr<CPDFSDK_Widget>& pWidget,
+    Mask<FWL_EVENTFLAG> nFlags,
     const CFX_PointF& point) {
-  ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
-  return pFormFiller && pFormFiller->OnLButtonDblClk(pPageView, nFlags, point);
+  CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+  return pFormField && pFormField->OnLButtonDblClk(pPageView, nFlags, point);
 }
 
-bool CFFL_InteractiveFormFiller::OnMouseMove(CPDFSDK_PageView* pPageView,
-                                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                             uint32_t nFlags,
-                                             const CFX_PointF& point) {
-  ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetOrCreateFormFiller(pAnnot->Get());
-  return pFormFiller && pFormFiller->OnMouseMove(pPageView, nFlags, point);
+bool CFFL_InteractiveFormFiller::OnMouseMove(
+    CPDFSDK_PageView* pPageView,
+    ObservedPtr<CPDFSDK_Widget>& pWidget,
+    Mask<FWL_EVENTFLAG> nFlags,
+    const CFX_PointF& point) {
+  CFFL_FormField* pFormField = GetOrCreateFormField(pWidget.Get());
+  return pFormField && pFormField->OnMouseMove(pPageView, nFlags, point);
 }
 
 bool CFFL_InteractiveFormFiller::OnMouseWheel(
     CPDFSDK_PageView* pPageView,
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    uint32_t nFlags,
-    short zDelta,
-    const CFX_PointF& point) {
-  ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
-  return pFormFiller &&
-         pFormFiller->OnMouseWheel(pPageView, nFlags, zDelta, point);
+    ObservedPtr<CPDFSDK_Widget>& pWidget,
+    Mask<FWL_EVENTFLAG> nFlags,
+    const CFX_PointF& point,
+    const CFX_Vector& delta) {
+  CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+  return pFormField &&
+         pFormField->OnMouseWheel(pPageView, nFlags, point, delta);
 }
 
 bool CFFL_InteractiveFormFiller::OnRButtonDown(
     CPDFSDK_PageView* pPageView,
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    uint32_t nFlags,
+    ObservedPtr<CPDFSDK_Widget>& pWidget,
+    Mask<FWL_EVENTFLAG> nFlags,
     const CFX_PointF& point) {
-  ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
-  return pFormFiller && pFormFiller->OnRButtonDown(pPageView, nFlags, point);
+  CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+  return pFormField && pFormField->OnRButtonDown(pPageView, nFlags, point);
 }
 
-bool CFFL_InteractiveFormFiller::OnRButtonUp(CPDFSDK_PageView* pPageView,
-                                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                             uint32_t nFlags,
-                                             const CFX_PointF& point) {
-  ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
-  return pFormFiller && pFormFiller->OnRButtonUp(pPageView, nFlags, point);
+bool CFFL_InteractiveFormFiller::OnRButtonUp(
+    CPDFSDK_PageView* pPageView,
+    ObservedPtr<CPDFSDK_Widget>& pWidget,
+    Mask<FWL_EVENTFLAG> nFlags,
+    const CFX_PointF& point) {
+  CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+  return pFormField && pFormField->OnRButtonUp(pPageView, nFlags, point);
 }
 
-bool CFFL_InteractiveFormFiller::OnKeyDown(CPDFSDK_Annot* pAnnot,
-                                           uint32_t nKeyCode,
-                                           uint32_t nFlags) {
-  ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
-  return pFormFiller && pFormFiller->OnKeyDown(nKeyCode, nFlags);
+bool CFFL_InteractiveFormFiller::OnKeyDown(CPDFSDK_Widget* pWidget,
+                                           FWL_VKEYCODE nKeyCode,
+                                           Mask<FWL_EVENTFLAG> nFlags) {
+  CFFL_FormField* pFormField = GetFormField(pWidget);
+  return pFormField && pFormField->OnKeyDown(nKeyCode, nFlags);
 }
 
-bool CFFL_InteractiveFormFiller::OnChar(CPDFSDK_Annot* pAnnot,
+bool CFFL_InteractiveFormFiller::OnChar(CPDFSDK_Widget* pWidget,
                                         uint32_t nChar,
-                                        uint32_t nFlags) {
-  ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  if (nChar == FWL_VKEY_Tab)
+                                        Mask<FWL_EVENTFLAG> nFlags) {
+  if (nChar == pdfium::ascii::kTab)
     return true;
 
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
-  return pFormFiller && pFormFiller->OnChar(pAnnot, nChar, nFlags);
+  CFFL_FormField* pFormField = GetFormField(pWidget);
+  return pFormField && pFormField->OnChar(pWidget, nChar, nFlags);
 }
 
-bool CFFL_InteractiveFormFiller::OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                            uint32_t nFlag) {
-  if (!pAnnot->HasObservable())
+bool CFFL_InteractiveFormFiller::OnSetFocus(
+    ObservedPtr<CPDFSDK_Widget>& pWidget,
+    Mask<FWL_EVENTFLAG> nFlag) {
+  if (!pWidget)
     return false;
 
-  ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
   if (!m_bNotifying) {
-    CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
-    if (pWidget->GetAAction(CPDF_AAction::kGetFocus).GetDict()) {
-      m_bNotifying = true;
-
+    if (pWidget->GetAAction(CPDF_AAction::kGetFocus).HasDict()) {
       uint32_t nValueAge = pWidget->GetValueAge();
       pWidget->ClearAppModified();
 
-      CFFL_FormFiller* pFormFiller = GetOrCreateFormFiller(pWidget);
-      if (!pFormFiller)
+      CFFL_FormField* pFormField = GetOrCreateFormField(pWidget.Get());
+      if (!pFormField)
         return false;
 
-      CPDFSDK_PageView* pPageView = (*pAnnot)->GetPageView();
-      ASSERT(pPageView);
+      CPDFSDK_PageView* pPageView = pWidget->GetPageView();
+      DCHECK(pPageView);
+      {
+        AutoRestorer<bool> restorer(&m_bNotifying);
+        m_bNotifying = true;
 
-      CPDFSDK_FieldAction fa;
-      fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
-      fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
-      pFormFiller->GetActionData(pPageView, CPDF_AAction::kGetFocus, fa);
-      pWidget->OnAAction(CPDF_AAction::kGetFocus, &fa, pPageView);
-      m_bNotifying = false;
-      if (!pAnnot->HasObservable())
+        CFFL_FieldAction fa;
+        fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+        fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
+        pFormField->GetActionData(pPageView, CPDF_AAction::kGetFocus, fa);
+        pWidget->OnAAction(CPDF_AAction::kGetFocus, &fa, pPageView);
+      }
+      if (!pWidget)
         return false;
 
       if (pWidget->IsAppModified()) {
-        if (CFFL_FormFiller* pFiller = GetFormFiller(pWidget)) {
-          pFiller->ResetPWLWindow(pPageView,
-                                  nValueAge == pWidget->GetValueAge());
+        CFFL_FormField* pFiller = GetFormField(pWidget.Get());
+        if (pFiller) {
+          pFiller->ResetPWLWindowForValueAge(pPageView, pWidget.Get(),
+                                             nValueAge);
         }
       }
     }
   }
 
-  if (CFFL_FormFiller* pFormFiller = GetOrCreateFormFiller(pAnnot->Get()))
-    pFormFiller->SetFocusForAnnot(pAnnot->Get(), nFlag);
+  if (CFFL_FormField* pFormField = GetOrCreateFormField(pWidget.Get()))
+    pFormField->SetFocusForAnnot(pWidget.Get(), nFlag);
 
   return true;
 }
 
-bool CFFL_InteractiveFormFiller::OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                             uint32_t nFlag) {
-  if (!pAnnot->HasObservable())
+bool CFFL_InteractiveFormFiller::OnKillFocus(
+    ObservedPtr<CPDFSDK_Widget>& pWidget,
+    Mask<FWL_EVENTFLAG> nFlag) {
+  if (!pWidget)
     return false;
 
-  ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
-  if (!pFormFiller)
+  CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+  if (!pFormField)
     return true;
 
-  pFormFiller->KillFocusForAnnot(nFlag);
-  if (!pAnnot->HasObservable())
+  pFormField->KillFocusForAnnot(nFlag);
+  if (!pWidget)
     return false;
 
   if (m_bNotifying)
     return true;
 
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
-  if (!pWidget->GetAAction(CPDF_AAction::kLoseFocus).GetDict())
+  if (!pWidget->GetAAction(CPDF_AAction::kLoseFocus).HasDict())
     return true;
 
-  m_bNotifying = true;
   pWidget->ClearAppModified();
 
   CPDFSDK_PageView* pPageView = pWidget->GetPageView();
-  ASSERT(pPageView);
+  DCHECK(pPageView);
+  {
+    AutoRestorer<bool> restorer(&m_bNotifying);
+    m_bNotifying = true;
 
-  CPDFSDK_FieldAction fa;
-  fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
-  fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
-  pFormFiller->GetActionData(pPageView, CPDF_AAction::kLoseFocus, fa);
-  pWidget->OnAAction(CPDF_AAction::kLoseFocus, &fa, pPageView);
-  m_bNotifying = false;
-  return pAnnot->HasObservable();
+    CFFL_FieldAction fa;
+    fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+    fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
+    pFormField->GetActionData(pPageView, CPDF_AAction::kLoseFocus, fa);
+    pWidget->OnAAction(CPDF_AAction::kLoseFocus, &fa, pPageView);
+  }
+  return !!pWidget;
+}
+
+void CFFL_InteractiveFormFiller::OnSetFieldInputFocus(const WideString& text) {
+  m_pCallbackIface->OnSetFieldInputFocus(text);
+}
+
+void CFFL_InteractiveFormFiller::Invalidate(IPDF_Page* pPage,
+                                            const FX_RECT& rect) {
+  m_pCallbackIface->Invalidate(pPage, rect);
+}
+
+CPDFSDK_PageView* CFFL_InteractiveFormFiller::GetOrCreatePageView(
+    IPDF_Page* pPage) {
+  return m_pCallbackIface->GetOrCreatePageView(pPage);
+}
+
+CPDFSDK_PageView* CFFL_InteractiveFormFiller::GetPageView(IPDF_Page* pPage) {
+  return m_pCallbackIface->GetPageView(pPage);
+}
+
+CFX_Timer::HandlerIface* CFFL_InteractiveFormFiller::GetTimerHandler() {
+  return m_pCallbackIface->GetTimerHandler();
+}
+
+void CFFL_InteractiveFormFiller::OnChange() {
+  m_pCallbackIface->OnChange();
 }
 
 bool CFFL_InteractiveFormFiller::IsVisible(CPDFSDK_Widget* pWidget) {
@@ -472,122 +473,156 @@
   if (pWidget->GetFieldType() == FormFieldType::kPushButton)
     return false;
 
-  return m_pFormFillEnv->GetPermissions(FPDFPERM_FILL_FORM) ||
-         m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM) ||
-         m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY);
+  return m_pCallbackIface->HasPermissions(
+      pdfium::access_permissions::kFillForm |
+      pdfium::access_permissions::kModifyAnnotation |
+      pdfium::access_permissions::kModifyContent);
 }
 
-CFFL_FormFiller* CFFL_InteractiveFormFiller::GetFormFiller(
-    CPDFSDK_Annot* pAnnot) {
-  auto it = m_Map.find(pAnnot);
+CFFL_FormField* CFFL_InteractiveFormFiller::GetFormField(
+    CPDFSDK_Widget* pWidget) {
+  auto it = m_Map.find(pWidget);
   return it != m_Map.end() ? it->second.get() : nullptr;
 }
 
-CFFL_FormFiller* CFFL_InteractiveFormFiller::GetOrCreateFormFiller(
-    CPDFSDK_Annot* pAnnot) {
-  CFFL_FormFiller* result = GetFormFiller(pAnnot);
+CFFL_FormField* CFFL_InteractiveFormFiller::GetOrCreateFormField(
+    CPDFSDK_Widget* pWidget) {
+  CFFL_FormField* result = GetFormField(pWidget);
   if (result)
     return result;
 
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
-  std::unique_ptr<CFFL_FormFiller> pFormFiller;
+  std::unique_ptr<CFFL_FormField> pFormField;
   switch (pWidget->GetFieldType()) {
     case FormFieldType::kPushButton:
-      pFormFiller =
-          pdfium::MakeUnique<CFFL_PushButton>(m_pFormFillEnv.Get(), pWidget);
+      pFormField = std::make_unique<CFFL_PushButton>(this, pWidget);
       break;
     case FormFieldType::kCheckBox:
-      pFormFiller =
-          pdfium::MakeUnique<CFFL_CheckBox>(m_pFormFillEnv.Get(), pWidget);
+      pFormField = std::make_unique<CFFL_CheckBox>(this, pWidget);
       break;
     case FormFieldType::kRadioButton:
-      pFormFiller =
-          pdfium::MakeUnique<CFFL_RadioButton>(m_pFormFillEnv.Get(), pWidget);
+      pFormField = std::make_unique<CFFL_RadioButton>(this, pWidget);
       break;
     case FormFieldType::kTextField:
-      pFormFiller =
-          pdfium::MakeUnique<CFFL_TextField>(m_pFormFillEnv.Get(), pWidget);
+      pFormField = std::make_unique<CFFL_TextField>(this, pWidget);
       break;
     case FormFieldType::kListBox:
-      pFormFiller =
-          pdfium::MakeUnique<CFFL_ListBox>(m_pFormFillEnv.Get(), pWidget);
+      pFormField = std::make_unique<CFFL_ListBox>(this, pWidget);
       break;
     case FormFieldType::kComboBox:
-      pFormFiller =
-          pdfium::MakeUnique<CFFL_ComboBox>(m_pFormFillEnv.Get(), pWidget);
+      pFormField = std::make_unique<CFFL_ComboBox>(this, pWidget);
       break;
     case FormFieldType::kUnknown:
     default:
       return nullptr;
   }
 
-  result = pFormFiller.get();
-  m_Map[pAnnot] = std::move(pFormFiller);
+  result = pFormField.get();
+  m_Map[pWidget] = std::move(pFormField);
   return result;
 }
 
-WideString CFFL_InteractiveFormFiller::GetText(CPDFSDK_Annot* pAnnot) {
-  ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
-  return pFormFiller ? pFormFiller->GetText() : WideString();
+WideString CFFL_InteractiveFormFiller::GetText(CPDFSDK_Widget* pWidget) {
+  CFFL_FormField* pFormField = GetFormField(pWidget);
+  return pFormField ? pFormField->GetText() : WideString();
 }
 
-WideString CFFL_InteractiveFormFiller::GetSelectedText(CPDFSDK_Annot* pAnnot) {
-  ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
-  return pFormFiller ? pFormFiller->GetSelectedText() : WideString();
+WideString CFFL_InteractiveFormFiller::GetSelectedText(
+    CPDFSDK_Widget* pWidget) {
+  CFFL_FormField* pFormField = GetFormField(pWidget);
+  return pFormField ? pFormField->GetSelectedText() : WideString();
 }
 
-void CFFL_InteractiveFormFiller::ReplaceSelection(CPDFSDK_Annot* pAnnot,
-                                                  const WideString& text) {
-  ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
-  if (!pFormFiller)
+void CFFL_InteractiveFormFiller::ReplaceAndKeepSelection(
+    CPDFSDK_Widget* pWidget,
+    const WideString& text) {
+  CFFL_FormField* pFormField = GetFormField(pWidget);
+  if (!pFormField)
     return;
 
-  pFormFiller->ReplaceSelection(text);
+  pFormField->ReplaceAndKeepSelection(text);
 }
 
-bool CFFL_InteractiveFormFiller::CanUndo(CPDFSDK_Annot* pAnnot) {
-  ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
-  return pFormFiller && pFormFiller->CanUndo();
+void CFFL_InteractiveFormFiller::ReplaceSelection(CPDFSDK_Widget* pWidget,
+                                                  const WideString& text) {
+  CFFL_FormField* pFormField = GetFormField(pWidget);
+  if (!pFormField)
+    return;
+
+  pFormField->ReplaceSelection(text);
 }
 
-bool CFFL_InteractiveFormFiller::CanRedo(CPDFSDK_Annot* pAnnot) {
-  ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
-  return pFormFiller && pFormFiller->CanRedo();
+bool CFFL_InteractiveFormFiller::SelectAllText(CPDFSDK_Widget* pWidget) {
+  CFFL_FormField* pFormField = GetFormField(pWidget);
+  return pFormField && pFormField->SelectAllText();
 }
 
-bool CFFL_InteractiveFormFiller::Undo(CPDFSDK_Annot* pAnnot) {
-  ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
-  return pFormFiller && pFormFiller->Undo();
+bool CFFL_InteractiveFormFiller::CanUndo(CPDFSDK_Widget* pWidget) {
+  CFFL_FormField* pFormField = GetFormField(pWidget);
+  return pFormField && pFormField->CanUndo();
 }
 
-bool CFFL_InteractiveFormFiller::Redo(CPDFSDK_Annot* pAnnot) {
-  ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
-  return pFormFiller && pFormFiller->Redo();
+bool CFFL_InteractiveFormFiller::CanRedo(CPDFSDK_Widget* pWidget) {
+  CFFL_FormField* pFormField = GetFormField(pWidget);
+  return pFormField && pFormField->CanRedo();
 }
 
-void CFFL_InteractiveFormFiller::UnRegisterFormFiller(CPDFSDK_Annot* pAnnot) {
-  auto it = m_Map.find(pAnnot);
+bool CFFL_InteractiveFormFiller::Undo(CPDFSDK_Widget* pWidget) {
+  CFFL_FormField* pFormField = GetFormField(pWidget);
+  return pFormField && pFormField->Undo();
+}
+
+bool CFFL_InteractiveFormFiller::Redo(CPDFSDK_Widget* pWidget) {
+  CFFL_FormField* pFormField = GetFormField(pWidget);
+  return pFormField && pFormField->Redo();
+}
+
+void CFFL_InteractiveFormFiller::UnregisterFormField(CPDFSDK_Widget* pWidget) {
+  auto it = m_Map.find(pWidget);
   if (it == m_Map.end())
     return;
 
   m_Map.erase(it);
 }
 
+void CFFL_InteractiveFormFiller::InvalidateRect(PerWindowData* pWidgetData,
+                                                const CFX_FloatRect& rect) {
+  auto* pPrivateData = static_cast<CFFL_PerWindowData*>(pWidgetData);
+  CPDFSDK_Widget* pWidget = pPrivateData->GetWidget();
+  if (!pWidget)
+    return;
+
+  m_pCallbackIface->InvalidateRect(pWidget, rect);
+}
+
+void CFFL_InteractiveFormFiller::OutputSelectedRect(PerWindowData* pWidgetData,
+                                                    const CFX_FloatRect& rect) {
+  auto* pPrivateData = static_cast<CFFL_PerWindowData*>(pWidgetData);
+  if (!pPrivateData)
+    return;
+
+  CFFL_FormField* pFormField = pPrivateData->GetFormField();
+  if (!pFormField)
+    return;
+
+  m_pCallbackIface->OutputSelectedRect(pFormField, rect);
+}
+
+bool CFFL_InteractiveFormFiller::IsSelectionImplemented() const {
+  return m_pCallbackIface->IsSelectionImplemented();
+}
+
+void CFFL_InteractiveFormFiller::SetCursor(CursorStyle nCursorStyle) {
+  m_pCallbackIface->SetCursor(nCursorStyle);
+}
+
 void CFFL_InteractiveFormFiller::QueryWherePopup(
-    const IPWL_SystemHandler::PerWindowData* pAttached,
+    const IPWL_FillerNotify::PerWindowData* pAttached,
     float fPopupMin,
     float fPopupMax,
     bool* bBottom,
     float* fPopupRet) {
-  auto* pData = static_cast<const CFFL_PrivateData*>(pAttached);
-  CPDFSDK_Widget* pWidget = pData->pWidget.Get();
+  auto* pData = static_cast<const CFFL_PerWindowData*>(pAttached);
+  CPDFSDK_Widget* pWidget = pData->GetWidget();
   CPDF_Page* pPage = pWidget->GetPDFPage();
 
   CFX_FloatRect rcPageView(0, pPage->GetPageHeight(), pPage->GetPageWidth(), 0);
@@ -618,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;
@@ -642,274 +677,260 @@
 }
 
 bool CFFL_InteractiveFormFiller::OnKeyStrokeCommit(
-    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-    CPDFSDK_PageView* pPageView,
-    uint32_t nFlag) {
+    ObservedPtr<CPDFSDK_Widget>& pWidget,
+    const CPDFSDK_PageView* pPageView,
+    Mask<FWL_EVENTFLAG> nFlag) {
   if (m_bNotifying)
     return true;
 
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
-  if (!pWidget->GetAAction(CPDF_AAction::kKeyStroke).GetDict())
+  if (!pWidget->GetAAction(CPDF_AAction::kKeyStroke).HasDict())
     return true;
 
-  ASSERT(pPageView);
-  m_bNotifying = true;
+  DCHECK(pPageView);
   pWidget->ClearAppModified();
 
-  CPDFSDK_FieldAction fa;
+  AutoRestorer<bool> restorer(&m_bNotifying);
+  m_bNotifying = true;
+
+  CFFL_FieldAction fa;
   fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
   fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
   fa.bWillCommit = true;
   fa.bKeyDown = true;
   fa.bRC = true;
 
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget);
-  pFormFiller->GetActionData(pPageView, CPDF_AAction::kKeyStroke, fa);
-  pFormFiller->SaveState(pPageView);
+  CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+  pFormField->GetActionData(pPageView, CPDF_AAction::kKeyStroke, fa);
+  pFormField->SavePWLWindowState(pPageView);
   pWidget->OnAAction(CPDF_AAction::kKeyStroke, &fa, pPageView);
-  if (!pAnnot->HasObservable())
+
+  if (!pWidget)
     return true;
 
-  m_bNotifying = false;
   return fa.bRC;
 }
 
-bool CFFL_InteractiveFormFiller::OnValidate(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                            CPDFSDK_PageView* pPageView,
-                                            uint32_t nFlag) {
+bool CFFL_InteractiveFormFiller::OnValidate(
+    ObservedPtr<CPDFSDK_Widget>& pWidget,
+    const CPDFSDK_PageView* pPageView,
+    Mask<FWL_EVENTFLAG> nFlag) {
   if (m_bNotifying)
     return true;
 
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
-  if (!pWidget->GetAAction(CPDF_AAction::kValidate).GetDict())
+  if (!pWidget->GetAAction(CPDF_AAction::kValidate).HasDict())
     return true;
 
-  ASSERT(pPageView);
-  m_bNotifying = true;
+  DCHECK(pPageView);
   pWidget->ClearAppModified();
 
-  CPDFSDK_FieldAction fa;
+  AutoRestorer<bool> restorer(&m_bNotifying);
+  m_bNotifying = true;
+
+  CFFL_FieldAction fa;
   fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
   fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
   fa.bKeyDown = true;
   fa.bRC = true;
 
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget);
-  pFormFiller->GetActionData(pPageView, CPDF_AAction::kValidate, fa);
-  pFormFiller->SaveState(pPageView);
+  CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+  pFormField->GetActionData(pPageView, CPDF_AAction::kValidate, fa);
+  pFormField->SavePWLWindowState(pPageView);
   pWidget->OnAAction(CPDF_AAction::kValidate, &fa, pPageView);
-  if (!pAnnot->HasObservable())
+
+  if (!pWidget)
     return true;
 
-  m_bNotifying = false;
   return fa.bRC;
 }
 
-void CFFL_InteractiveFormFiller::OnCalculate(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                             CPDFSDK_PageView* pPageView,
-                                             uint32_t nFlag) {
+void CFFL_InteractiveFormFiller::OnCalculate(
+    ObservedPtr<CPDFSDK_Widget>& pWidget) {
   if (m_bNotifying)
     return;
 
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
-  if (pWidget) {
-    CPDFSDK_InteractiveForm* pForm =
-        pPageView->GetFormFillEnv()->GetInteractiveForm();
-    pForm->OnCalculate(pWidget->GetFormField());
-  }
-  m_bNotifying = false;
+  ObservedPtr<CPDFSDK_Annot> pObserved(pWidget.Get());
+  m_pCallbackIface->OnCalculate(pObserved);
 }
 
-void CFFL_InteractiveFormFiller::OnFormat(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                          CPDFSDK_PageView* pPageView,
-                                          uint32_t nFlag) {
+void CFFL_InteractiveFormFiller::OnFormat(
+    ObservedPtr<CPDFSDK_Widget>& pWidget) {
   if (m_bNotifying)
     return;
 
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
-  ASSERT(pWidget);
-  CPDFSDK_InteractiveForm* pForm =
-      pPageView->GetFormFillEnv()->GetInteractiveForm();
-
-  Optional<WideString> sValue = pForm->OnFormat(pWidget->GetFormField());
-  if (!pAnnot->HasObservable())
-    return;
-
-  if (sValue.has_value()) {
-    pForm->ResetFieldAppearance(pWidget->GetFormField(), sValue);
-    pForm->UpdateField(pWidget->GetFormField());
-  }
-
-  m_bNotifying = false;
+  ObservedPtr<CPDFSDK_Annot> pObserved(pWidget.Get());
+  m_pCallbackIface->OnFormat(pObserved);
 }
 
 #ifdef PDF_ENABLE_XFA
-bool CFFL_InteractiveFormFiller::OnClick(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                         CPDFSDK_PageView* pPageView,
-                                         uint32_t nFlag) {
+bool CFFL_InteractiveFormFiller::OnClick(ObservedPtr<CPDFSDK_Widget>& pWidget,
+                                         const CPDFSDK_PageView* pPageView,
+                                         Mask<FWL_EVENTFLAG> nFlag) {
   if (m_bNotifying)
     return false;
 
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
   if (!pWidget->HasXFAAAction(PDFSDK_XFA_Click))
     return false;
 
-  m_bNotifying = true;
   uint32_t nAge = pWidget->GetAppearanceAge();
   uint32_t nValueAge = pWidget->GetValueAge();
+  {
+    AutoRestorer<bool> restorer(&m_bNotifying);
+    m_bNotifying = true;
 
-  CPDFSDK_FieldAction fa;
-  fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
-  fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
+    CFFL_FieldAction fa;
+    fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+    fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
 
-  pWidget->OnXFAAAction(PDFSDK_XFA_Click, &fa, pPageView);
-  m_bNotifying = false;
-  if (!pAnnot->HasObservable() || !IsValidAnnot(pPageView, pWidget))
+    pWidget->OnXFAAAction(PDFSDK_XFA_Click, &fa, pPageView);
+  }
+  if (!pWidget || !IsValidAnnot(pPageView, pWidget.Get()))
     return true;
   if (nAge == pWidget->GetAppearanceAge())
     return false;
 
-  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget))
-    pFormFiller->ResetPWLWindow(pPageView, nValueAge == pWidget->GetValueAge());
+  CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+  if (pFormField)
+    pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(), nValueAge);
   return false;
 }
 
-bool CFFL_InteractiveFormFiller::OnFull(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                        CPDFSDK_PageView* pPageView,
-                                        uint32_t nFlag) {
+bool CFFL_InteractiveFormFiller::OnFull(ObservedPtr<CPDFSDK_Widget>& pWidget,
+                                        const CPDFSDK_PageView* pPageView,
+                                        Mask<FWL_EVENTFLAG> nFlag) {
   if (m_bNotifying)
     return false;
 
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
   if (!pWidget->HasXFAAAction(PDFSDK_XFA_Full))
     return false;
 
-  m_bNotifying = true;
   uint32_t nAge = pWidget->GetAppearanceAge();
   uint32_t nValueAge = pWidget->GetValueAge();
+  {
+    AutoRestorer<bool> restorer(&m_bNotifying);
+    m_bNotifying = true;
 
-  CPDFSDK_FieldAction fa;
-  fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
-  fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
-
-  pWidget->OnXFAAAction(PDFSDK_XFA_Full, &fa, pPageView);
-  m_bNotifying = false;
-  if (!pAnnot->HasObservable() || !IsValidAnnot(pPageView, pWidget))
+    CFFL_FieldAction fa;
+    fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+    fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
+    pWidget->OnXFAAAction(PDFSDK_XFA_Full, &fa, pPageView);
+  }
+  if (!pWidget || !IsValidAnnot(pPageView, pWidget.Get()))
     return true;
   if (nAge == pWidget->GetAppearanceAge())
     return false;
 
-  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget))
-    pFormFiller->ResetPWLWindow(pPageView, nValueAge == pWidget->GetValueAge());
-
+  CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+  if (pFormField)
+    pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(), nValueAge);
   return true;
 }
 
-bool CFFL_InteractiveFormFiller::OnPreOpen(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                           CPDFSDK_PageView* pPageView,
-                                           uint32_t nFlag) {
+bool CFFL_InteractiveFormFiller::OnPreOpen(ObservedPtr<CPDFSDK_Widget>& pWidget,
+                                           const CPDFSDK_PageView* pPageView,
+                                           Mask<FWL_EVENTFLAG> nFlag) {
   if (m_bNotifying)
     return false;
 
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
   if (!pWidget->HasXFAAAction(PDFSDK_XFA_PreOpen))
     return false;
 
-  m_bNotifying = true;
   uint32_t nAge = pWidget->GetAppearanceAge();
   uint32_t nValueAge = pWidget->GetValueAge();
+  {
+    AutoRestorer<bool> restorer(&m_bNotifying);
+    m_bNotifying = true;
 
-  CPDFSDK_FieldAction fa;
-  fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
-  fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
-
-  pWidget->OnXFAAAction(PDFSDK_XFA_PreOpen, &fa, pPageView);
-  m_bNotifying = false;
-  if (!pAnnot->HasObservable() || !IsValidAnnot(pPageView, pWidget))
+    CFFL_FieldAction fa;
+    fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+    fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
+    pWidget->OnXFAAAction(PDFSDK_XFA_PreOpen, &fa, pPageView);
+  }
+  if (!pWidget || !IsValidAnnot(pPageView, pWidget.Get()))
     return true;
   if (nAge == pWidget->GetAppearanceAge())
     return false;
 
-  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget))
-    pFormFiller->ResetPWLWindow(pPageView, nValueAge == pWidget->GetValueAge());
-
+  CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+  if (pFormField)
+    pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(), nValueAge);
   return true;
 }
 
-bool CFFL_InteractiveFormFiller::OnPostOpen(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                            CPDFSDK_PageView* pPageView,
-                                            uint32_t nFlag) {
+bool CFFL_InteractiveFormFiller::OnPostOpen(
+    ObservedPtr<CPDFSDK_Widget>& pWidget,
+    const CPDFSDK_PageView* pPageView,
+    Mask<FWL_EVENTFLAG> nFlag) {
   if (m_bNotifying)
     return false;
 
-  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
   if (!pWidget->HasXFAAAction(PDFSDK_XFA_PostOpen))
     return false;
 
-  m_bNotifying = true;
   uint32_t nAge = pWidget->GetAppearanceAge();
   uint32_t nValueAge = pWidget->GetValueAge();
+  {
+    AutoRestorer<bool> restorer(&m_bNotifying);
+    m_bNotifying = true;
 
-  CPDFSDK_FieldAction fa;
-  fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
-  fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
-
-  pWidget->OnXFAAAction(PDFSDK_XFA_PostOpen, &fa, pPageView);
-  m_bNotifying = false;
-  if (!pAnnot->HasObservable() || !IsValidAnnot(pPageView, pWidget))
+    CFFL_FieldAction fa;
+    fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+    fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
+    pWidget->OnXFAAAction(PDFSDK_XFA_PostOpen, &fa, pPageView);
+  }
+  if (!pWidget || !IsValidAnnot(pPageView, pWidget.Get()))
     return true;
+
   if (nAge == pWidget->GetAppearanceAge())
     return false;
 
-  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget))
-    pFormFiller->ResetPWLWindow(pPageView, nValueAge == pWidget->GetValueAge());
-
+  CFFL_FormField* pFormField = GetFormField(pWidget.Get());
+  if (pFormField)
+    pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(), nValueAge);
   return true;
 }
 #endif  // PDF_ENABLE_XFA
 
-bool CFFL_InteractiveFormFiller::IsValidAnnot(CPDFSDK_PageView* pPageView,
-                                              CPDFSDK_Annot* pAnnot) {
-  return pPageView && pPageView->IsValidAnnot(pAnnot->GetPDFAnnot());
+// static
+bool CFFL_InteractiveFormFiller::IsValidAnnot(const CPDFSDK_PageView* pPageView,
+                                              CPDFSDK_Widget* pWidget) {
+  return pPageView && pPageView->IsValidAnnot(pWidget->GetPDFAnnot());
 }
 
 std::pair<bool, bool> CFFL_InteractiveFormFiller::OnBeforeKeyStroke(
-    const IPWL_SystemHandler::PerWindowData* pAttached,
+    const IPWL_FillerNotify::PerWindowData* pAttached,
     WideString& strChange,
     const WideString& strChangeEx,
     int nSelStart,
     int nSelEnd,
     bool bKeyDown,
-    uint32_t nFlag) {
-  // Copy the private data since the window owning it may not survive.
-  CFFL_PrivateData privateData =
-      *static_cast<const CFFL_PrivateData*>(pAttached);
-  ASSERT(privateData.pWidget);
+    Mask<FWL_EVENTFLAG> nFlag) {
+  // Copy out of private data since the window owning it may not survive.
+  auto* pPrivateData = static_cast<const CFFL_PerWindowData*>(pAttached);
+  const CPDFSDK_PageView* pPageView = pPrivateData->GetPageView();
+  ObservedPtr<CPDFSDK_Widget> pWidget(pPrivateData->GetWidget());
+  DCHECK(pWidget);
 
-  CFFL_FormFiller* pFormFiller = GetFormFiller(privateData.GetWidget());
+  CFFL_FormField* pFormField = GetFormField(pWidget.Get());
 
 #ifdef PDF_ENABLE_XFA
-  if (pFormFiller->IsFieldFull(privateData.pPageView)) {
-    ObservedPtr<CPDFSDK_Annot> pObserved(privateData.GetWidget());
-    if (OnFull(&pObserved, privateData.pPageView, nFlag) || !pObserved)
+  if (pFormField->IsFieldFull(pPageView)) {
+    if (OnFull(pWidget, pPageView, nFlag) || !pWidget)
       return {true, true};
   }
 #endif  // PDF_ENABLE_XFA
 
   if (m_bNotifying ||
-      !privateData.pWidget->GetAAction(CPDF_AAction::kKeyStroke).GetDict()) {
+      !pWidget->GetAAction(CPDF_AAction::kKeyStroke).HasDict()) {
     return {true, false};
   }
 
   AutoRestorer<bool> restorer(&m_bNotifying);
   m_bNotifying = true;
 
-  uint32_t nAge = privateData.pWidget->GetAppearanceAge();
-  uint32_t nValueAge = privateData.pWidget->GetValueAge();
-  CPDFSDK_FormFillEnvironment* pFormFillEnv =
-      privateData.pPageView->GetFormFillEnv();
+  uint32_t nAge = pWidget->GetAppearanceAge();
+  uint32_t nValueAge = pWidget->GetValueAge();
 
-  CPDFSDK_FieldAction fa;
+  CFFL_FieldAction fa;
   fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
   fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
   fa.sChange = strChange;
@@ -919,79 +940,65 @@
   fa.bRC = true;
   fa.nSelStart = nSelStart;
   fa.nSelEnd = nSelEnd;
-  pFormFiller->GetActionData(privateData.pPageView, CPDF_AAction::kKeyStroke,
-                             fa);
-  pFormFiller->SaveState(privateData.pPageView);
+  pFormField->GetActionData(pPageView, CPDF_AAction::kKeyStroke, fa);
+  pFormField->SavePWLWindowState(pPageView);
 
-  ObservedPtr<CPDFSDK_Annot> pObserved(privateData.GetWidget());
-  bool action_status = privateData.pWidget->OnAAction(
-      CPDF_AAction::kKeyStroke, &fa, privateData.pPageView);
+  bool action_status =
+      pWidget->OnAAction(CPDF_AAction::kKeyStroke, &fa, pPageView);
 
-  if (!pObserved ||
-      !IsValidAnnot(privateData.pPageView, privateData.GetWidget())) {
+  if (!pWidget || !IsValidAnnot(pPageView, pWidget.Get())) {
     return {true, true};
   }
   if (!action_status)
     return {true, false};
 
   bool bExit = false;
-  if (nAge != privateData.pWidget->GetAppearanceAge()) {
-    CPWL_Wnd* pWnd = pFormFiller->ResetPWLWindow(
-        privateData.pPageView, nValueAge == privateData.pWidget->GetValueAge());
-    if (!pWnd)
+  if (nAge != pWidget->GetAppearanceAge()) {
+    pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(), nValueAge);
+    pPrivateData = pFormField->GetPerPWLWindowData(pPageView);
+    if (!pPrivateData)
       return {true, true};
-    privateData =
-        *static_cast<const CFFL_PrivateData*>(pWnd->GetAttachedData());
+
+    pWidget.Reset(pPrivateData->GetWidget());
+    pPageView = pPrivateData->GetPageView();
     bExit = true;
   }
   if (fa.bRC) {
-    pFormFiller->SetActionData(privateData.pPageView, CPDF_AAction::kKeyStroke,
-                               fa);
+    pFormField->SetActionData(pPageView, CPDF_AAction::kKeyStroke, fa);
   } else {
-    pFormFiller->RestoreState(privateData.pPageView);
+    pFormField->RecreatePWLWindowFromSavedState(pPageView);
   }
-  if (pFormFillEnv->GetFocusAnnot() == privateData.GetWidget())
+  if (m_pCallbackIface->GetFocusAnnot() == pWidget)
     return {false, bExit};
 
-  pFormFiller->CommitData(privateData.pPageView, nFlag);
+  pFormField->CommitData(pPageView, nFlag);
   return {false, true};
 }
 
 bool CFFL_InteractiveFormFiller::OnPopupPreOpen(
-    const IPWL_SystemHandler::PerWindowData* pAttached,
-    uint32_t nFlag) {
+    const IPWL_FillerNotify::PerWindowData* pAttached,
+    Mask<FWL_EVENTFLAG> nFlag) {
 #ifdef PDF_ENABLE_XFA
-  auto* pData = static_cast<const CFFL_PrivateData*>(pAttached);
-  ASSERT(pData->pWidget);
+  auto* pData = static_cast<const CFFL_PerWindowData*>(pAttached);
+  DCHECK(pData->GetWidget());
 
-  ObservedPtr<CPDFSDK_Annot> pObserved(pData->GetWidget());
-  return OnPreOpen(&pObserved, pData->pPageView, nFlag) || !pObserved;
+  ObservedPtr<CPDFSDK_Widget> pObserved(pData->GetWidget());
+  return OnPreOpen(pObserved, pData->GetPageView(), nFlag) || !pObserved;
 #else
   return false;
 #endif
 }
 
 bool CFFL_InteractiveFormFiller::OnPopupPostOpen(
-    const IPWL_SystemHandler::PerWindowData* pAttached,
-    uint32_t nFlag) {
+    const IPWL_FillerNotify::PerWindowData* pAttached,
+    Mask<FWL_EVENTFLAG> nFlag) {
 #ifdef PDF_ENABLE_XFA
-  auto* pData = static_cast<const CFFL_PrivateData*>(pAttached);
-  ASSERT(pData->pWidget);
+  auto* pData = static_cast<const CFFL_PerWindowData*>(pAttached);
+  DCHECK(pData->GetWidget());
 
-  ObservedPtr<CPDFSDK_Annot> pObserved(pData->GetWidget());
-  return OnPostOpen(&pObserved, pData->pPageView, nFlag) || !pObserved;
+  ObservedPtr<CPDFSDK_Widget> pObserved(pData->GetWidget());
+  return OnPostOpen(pObserved, pData->GetPageView(), nFlag) || !pObserved;
 #else
   return false;
 #endif
 }
-
-CFFL_PrivateData::CFFL_PrivateData() = default;
-
-CFFL_PrivateData::CFFL_PrivateData(const CFFL_PrivateData& that) = default;
-
-CFFL_PrivateData::~CFFL_PrivateData() = default;
-
-std::unique_ptr<IPWL_SystemHandler::PerWindowData> CFFL_PrivateData::Clone()
-    const {
-  return pdfium::MakeUnique<CFFL_PrivateData>(*this);
-}
diff --git a/fpdfsdk/formfiller/cffl_interactiveformfiller.h b/fpdfsdk/formfiller/cffl_interactiveformfiller.h
index 3adfe4e..bbf757d 100644
--- a/fpdfsdk/formfiller/cffl_interactiveformfiller.h
+++ b/fpdfsdk/formfiller/cffl_interactiveformfiller.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,180 +11,205 @@
 #include <memory>
 #include <utility>
 
+#include "core/fxcrt/cfx_timer.h"
+#include "core/fxcrt/mask.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
-#include "fpdfsdk/pwl/cpwl_edit.h"
-#include "fpdfsdk/pwl/cpwl_wnd.h"
-#include "fpdfsdk/pwl/ipwl_systemhandler.h"
+#include "fpdfsdk/pwl/ipwl_fillernotify.h"
+#include "public/fpdf_fwlevent.h"
 
-class CFFL_FormFiller;
-class CPDFSDK_FormFillEnvironment;
+class CFFL_FormField;
 class CPDFSDK_PageView;
 class CPDFSDK_Widget;
 
-class CFFL_InteractiveFormFiller final : public IPWL_Filler_Notify {
+class CFFL_InteractiveFormFiller final : public IPWL_FillerNotify {
  public:
-  explicit CFFL_InteractiveFormFiller(
-      CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  class CallbackIface {
+   public:
+    virtual ~CallbackIface() = default;
+
+    virtual void OnSetFieldInputFocus(const WideString& text) = 0;
+    virtual void OnCalculate(ObservedPtr<CPDFSDK_Annot>& pAnnot) = 0;
+    virtual void OnFormat(ObservedPtr<CPDFSDK_Annot>& pAnnot) = 0;
+    virtual void Invalidate(IPDF_Page* pPage, const FX_RECT& rect) = 0;
+    virtual CPDFSDK_PageView* GetOrCreatePageView(IPDF_Page* pPage) = 0;
+    virtual CPDFSDK_PageView* GetPageView(IPDF_Page* pPage) = 0;
+    virtual CFX_Timer::HandlerIface* GetTimerHandler() = 0;
+    virtual CPDFSDK_Annot* GetFocusAnnot() const = 0;
+    virtual bool SetFocusAnnot(ObservedPtr<CPDFSDK_Annot>& pAnnot) = 0;
+    virtual void InvalidateRect(CPDFSDK_Widget* pWidget,
+                                const CFX_FloatRect& rect) = 0;
+    virtual void OutputSelectedRect(CFFL_FormField* pFormField,
+                                    const CFX_FloatRect& rect) = 0;
+    virtual bool IsSelectionImplemented() const = 0;
+    virtual void SetCursor(CursorStyle nCursorStyle) = 0;
+
+    // See PDF Reference 1.7, table 3.20 for the permission bits. Returns true
+    // if any bit in |flags| is set.
+    virtual bool HasPermissions(uint32_t flags) const = 0;
+    virtual void OnChange() = 0;
+  };
+
+  explicit CFFL_InteractiveFormFiller(CallbackIface* pCallbackIface);
   ~CFFL_InteractiveFormFiller() override;
 
-  bool Annot_HitTest(CPDFSDK_PageView* pPageView,
-                     CPDFSDK_Annot* pAnnot,
-                     const CFX_PointF& point);
-  FX_RECT GetViewBBox(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot);
+  bool Annot_HitTest(const CPDFSDK_Widget* pWidget, const CFX_PointF& point);
+  FX_RECT GetViewBBox(const CPDFSDK_PageView* pPageView,
+                      CPDFSDK_Widget* pWidget);
+
   void OnDraw(CPDFSDK_PageView* pPageView,
-              CPDFSDK_Annot* pAnnot,
+              CPDFSDK_Widget* pWidget,
               CFX_RenderDevice* pDevice,
               const CFX_Matrix& mtUser2Device);
-
-  void OnDelete(CPDFSDK_Annot* pAnnot);
+  void OnDelete(CPDFSDK_Widget* pWidget);
 
   void OnMouseEnter(CPDFSDK_PageView* pPageView,
-                    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                    uint32_t nFlag);
+                    ObservedPtr<CPDFSDK_Widget>& pWidget,
+                    Mask<FWL_EVENTFLAG> nFlag);
   void OnMouseExit(CPDFSDK_PageView* pPageView,
-                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   uint32_t nFlag);
+                   ObservedPtr<CPDFSDK_Widget>& pWidget,
+                   Mask<FWL_EVENTFLAG> nFlag);
   bool OnLButtonDown(CPDFSDK_PageView* pPageView,
-                     ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                     uint32_t nFlags,
+                     ObservedPtr<CPDFSDK_Widget>& pWidget,
+                     Mask<FWL_EVENTFLAG> nFlags,
                      const CFX_PointF& point);
   bool OnLButtonUp(CPDFSDK_PageView* pPageView,
-                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   uint32_t nFlags,
+                   ObservedPtr<CPDFSDK_Widget>& pWidget,
+                   Mask<FWL_EVENTFLAG> nFlags,
                    const CFX_PointF& point);
   bool OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                       ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                       uint32_t nFlags,
+                       ObservedPtr<CPDFSDK_Widget>& pWidget,
+                       Mask<FWL_EVENTFLAG> nFlags,
                        const CFX_PointF& point);
   bool OnMouseMove(CPDFSDK_PageView* pPageView,
-                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   uint32_t nFlags,
+                   ObservedPtr<CPDFSDK_Widget>& pWidget,
+                   Mask<FWL_EVENTFLAG> nFlags,
                    const CFX_PointF& point);
   bool OnMouseWheel(CPDFSDK_PageView* pPageView,
-                    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                    uint32_t nFlags,
-                    short zDelta,
-                    const CFX_PointF& point);
+                    ObservedPtr<CPDFSDK_Widget>& pWidget,
+                    Mask<FWL_EVENTFLAG> nFlags,
+                    const CFX_PointF& point,
+                    const CFX_Vector& delta);
   bool OnRButtonDown(CPDFSDK_PageView* pPageView,
-                     ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                     uint32_t nFlags,
+                     ObservedPtr<CPDFSDK_Widget>& pWidget,
+                     Mask<FWL_EVENTFLAG> nFlags,
                      const CFX_PointF& point);
   bool OnRButtonUp(CPDFSDK_PageView* pPageView,
-                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   uint32_t nFlags,
+                   ObservedPtr<CPDFSDK_Widget>& pWidget,
+                   Mask<FWL_EVENTFLAG> nFlags,
                    const CFX_PointF& point);
 
-  bool OnKeyDown(CPDFSDK_Annot* pAnnot, uint32_t nKeyCode, uint32_t nFlags);
-  bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags);
+  bool OnKeyDown(CPDFSDK_Widget* pWidget,
+                 FWL_VKEYCODE nKeyCode,
+                 Mask<FWL_EVENTFLAG> nFlags);
+  bool OnChar(CPDFSDK_Widget* pWidget,
+              uint32_t nChar,
+              Mask<FWL_EVENTFLAG> nFlags);
 
-  bool OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag);
-  bool OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag);
+  bool OnSetFocus(ObservedPtr<CPDFSDK_Widget>& pWidget,
+                  Mask<FWL_EVENTFLAG> nFlag);
+  bool OnKillFocus(ObservedPtr<CPDFSDK_Widget>& pWidget,
+                   Mask<FWL_EVENTFLAG> nFlag);
 
-  CFFL_FormFiller* GetFormFillerForTesting(CPDFSDK_Annot* pAnnot) {
-    return GetFormFiller(pAnnot);
+  // Wrapper methods for CallbackIface
+  void OnSetFieldInputFocus(const WideString& text);
+  void Invalidate(IPDF_Page* pPage, const FX_RECT& rect);
+  CPDFSDK_PageView* GetOrCreatePageView(IPDF_Page* pPage);
+  CPDFSDK_PageView* GetPageView(IPDF_Page* pPage);
+  CFX_Timer::HandlerIface* GetTimerHandler();
+  void OnChange();
+
+  CFFL_FormField* GetFormFieldForTesting(CPDFSDK_Widget* pAnnot) {
+    return GetFormField(pAnnot);
   }
 
-  WideString GetText(CPDFSDK_Annot* pAnnot);
-  WideString GetSelectedText(CPDFSDK_Annot* pAnnot);
-  void ReplaceSelection(CPDFSDK_Annot* pAnnot, const WideString& text);
+  WideString GetText(CPDFSDK_Widget* pWidget);
+  WideString GetSelectedText(CPDFSDK_Widget* pWidget);
+  void ReplaceAndKeepSelection(CPDFSDK_Widget* pWidget, const WideString& text);
+  void ReplaceSelection(CPDFSDK_Widget* pWidget, const WideString& text);
+  bool SelectAllText(CPDFSDK_Widget* pWidget);
 
-  bool CanUndo(CPDFSDK_Annot* pAnnot);
-  bool CanRedo(CPDFSDK_Annot* pAnnot);
-  bool Undo(CPDFSDK_Annot* pAnnot);
-  bool Redo(CPDFSDK_Annot* pAnnot);
+  bool CanUndo(CPDFSDK_Widget* pWidget);
+  bool CanRedo(CPDFSDK_Widget* pWidget);
+  bool Undo(CPDFSDK_Widget* pWidget);
+  bool Redo(CPDFSDK_Widget* pWidget);
 
   static bool IsVisible(CPDFSDK_Widget* pWidget);
   static bool IsReadOnly(CPDFSDK_Widget* pWidget);
-  static bool IsValidAnnot(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot);
+  static bool IsValidAnnot(const CPDFSDK_PageView* pPageView,
+                           CPDFSDK_Widget* pWidget);
 
-  bool OnKeyStrokeCommit(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                         CPDFSDK_PageView* pPageView,
-                         uint32_t nFlag);
-  bool OnValidate(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                  CPDFSDK_PageView* pPageView,
-                  uint32_t nFlag);
-  void OnCalculate(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   CPDFSDK_PageView* pPageView,
-                   uint32_t nFlag);
-  void OnFormat(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                CPDFSDK_PageView* pPageView,
-                uint32_t nFlag);
-  bool OnButtonUp(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                  CPDFSDK_PageView* pPageView,
-                  uint32_t nFlag);
+  bool OnKeyStrokeCommit(ObservedPtr<CPDFSDK_Widget>& pWidget,
+                         const CPDFSDK_PageView* pPageView,
+                         Mask<FWL_EVENTFLAG> nFlag);
+  bool OnValidate(ObservedPtr<CPDFSDK_Widget>& pWidget,
+                  const CPDFSDK_PageView* pPageView,
+                  Mask<FWL_EVENTFLAG> nFlag);
+  void OnCalculate(ObservedPtr<CPDFSDK_Widget>& pWidget);
+  void OnFormat(ObservedPtr<CPDFSDK_Widget>& pWidget);
+  bool OnButtonUp(ObservedPtr<CPDFSDK_Widget>& pWidget,
+                  const CPDFSDK_PageView* pPageView,
+                  Mask<FWL_EVENTFLAG> nFlag);
 
-  bool SetIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+  bool SetIndexSelected(ObservedPtr<CPDFSDK_Widget>& pWidget,
                         int index,
                         bool selected);
-  bool IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot, int index);
+  bool IsIndexSelected(ObservedPtr<CPDFSDK_Widget>& pWidget, int index);
 
  private:
   using WidgetToFormFillerMap =
-      std::map<CPDFSDK_Annot*, std::unique_ptr<CFFL_FormFiller>>;
+      std::map<CPDFSDK_Widget*, std::unique_ptr<CFFL_FormField>>;
 
-  // IPWL_Filler_Notify:
-  void QueryWherePopup(const IPWL_SystemHandler::PerWindowData* pAttached,
+  // IPWL_FillerNotify:
+  void InvalidateRect(PerWindowData* pWidgetData,
+                      const CFX_FloatRect& rect) override;
+  void OutputSelectedRect(PerWindowData* pWidgetData,
+                          const CFX_FloatRect& rect) override;
+  bool IsSelectionImplemented() const override;
+  void SetCursor(CursorStyle nCursorStyle) override;
+  void QueryWherePopup(const PerWindowData* pAttached,
                        float fPopupMin,
                        float fPopupMax,
                        bool* bBottom,
                        float* fPopupRet) override;
   // Returns {bRC, bExit}.
-  std::pair<bool, bool> OnBeforeKeyStroke(
-      const IPWL_SystemHandler::PerWindowData* pAttached,
-      WideString& strChange,
-      const WideString& strChangeEx,
-      int nSelStart,
-      int nSelEnd,
-      bool bKeyDown,
-      uint32_t nFlag) override;
-  bool OnPopupPreOpen(const IPWL_SystemHandler::PerWindowData* pAttached,
-                      uint32_t nFlag) override;
-  bool OnPopupPostOpen(const IPWL_SystemHandler::PerWindowData* pAttached,
-                       uint32_t nFlag) override;
+  std::pair<bool, bool> OnBeforeKeyStroke(const PerWindowData* pAttached,
+                                          WideString& strChange,
+                                          const WideString& strChangeEx,
+                                          int nSelStart,
+                                          int nSelEnd,
+                                          bool bKeyDown,
+                                          Mask<FWL_EVENTFLAG> nFlag) override;
+  bool OnPopupPreOpen(const PerWindowData* pAttached,
+                      Mask<FWL_EVENTFLAG> nFlag) override;
+  bool OnPopupPostOpen(const PerWindowData* pAttached,
+                       Mask<FWL_EVENTFLAG> nFlag) override;
 
 #ifdef PDF_ENABLE_XFA
-  void SetFocusAnnotTab(CPDFSDK_Annot* pWidget, bool bSameField, bool bNext);
-  bool OnClick(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-               CPDFSDK_PageView* pPageView,
-               uint32_t nFlag);
-  bool OnFull(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-              CPDFSDK_PageView* pPageView,
-              uint32_t nFlag);
-  bool OnPreOpen(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                 CPDFSDK_PageView* pPageView,
-                 uint32_t nFlag);
-  bool OnPostOpen(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                  CPDFSDK_PageView* pPageView,
-                  uint32_t nFlag);
+  void SetFocusAnnotTab(CPDFSDK_Widget* pWidget, bool bSameField, bool bNext);
+  bool OnClick(ObservedPtr<CPDFSDK_Widget>& pWidget,
+               const CPDFSDK_PageView* pPageView,
+               Mask<FWL_EVENTFLAG> nFlag);
+  bool OnFull(ObservedPtr<CPDFSDK_Widget>& pAnnot,
+              const CPDFSDK_PageView* pPageView,
+              Mask<FWL_EVENTFLAG> nFlag);
+  bool OnPreOpen(ObservedPtr<CPDFSDK_Widget>& pWidget,
+                 const CPDFSDK_PageView* pPageView,
+                 Mask<FWL_EVENTFLAG> nFlag);
+  bool OnPostOpen(ObservedPtr<CPDFSDK_Widget>& pWidget,
+                  const CPDFSDK_PageView* pPageView,
+                  Mask<FWL_EVENTFLAG> nFlag);
 #endif  // PDF_ENABLE_XFA
 
   bool IsFillingAllowed(CPDFSDK_Widget* pWidget) const;
-  CFFL_FormFiller* GetFormFiller(CPDFSDK_Annot* pAnnot);
-  CFFL_FormFiller* GetOrCreateFormFiller(CPDFSDK_Annot* pAnnot);
-  void UnRegisterFormFiller(CPDFSDK_Annot* pAnnot);
+  CFFL_FormField* GetFormField(CPDFSDK_Widget* pWidget);
+  CFFL_FormField* GetOrCreateFormField(CPDFSDK_Widget* pWidget);
+  void UnregisterFormField(CPDFSDK_Widget* pWidget);
 
-  UnownedPtr<CPDFSDK_FormFillEnvironment> const m_pFormFillEnv;
+  UnownedPtr<CallbackIface> const m_pCallbackIface;
   WidgetToFormFillerMap m_Map;
   bool m_bNotifying = false;
 };
 
-class CFFL_PrivateData final : public IPWL_SystemHandler::PerWindowData {
- public:
-  CFFL_PrivateData();
-  CFFL_PrivateData(const CFFL_PrivateData& that);
-  ~CFFL_PrivateData() override;
-
-  // CPWL_Wnd::PrivateData:
-  std::unique_ptr<IPWL_SystemHandler::PerWindowData> Clone() const override;
-
-  CPDFSDK_Widget* GetWidget() const { return pWidget.Get(); }
-
-  ObservedPtr<CPDFSDK_Widget> pWidget;
-  CPDFSDK_PageView* pPageView = nullptr;
-  uint32_t nWidgetAppearanceAge = 0;
-  uint32_t nWidgetValueAge = 0;
-};
-
 #endif  // FPDFSDK_FORMFILLER_CFFL_INTERACTIVEFORMFILLER_H_
diff --git a/fpdfsdk/formfiller/cffl_listbox.cpp b/fpdfsdk/formfiller/cffl_listbox.cpp
index 3d80255..31134bb 100644
--- a/fpdfsdk/formfiller/cffl_listbox.cpp
+++ b/fpdfsdk/formfiller/cffl_listbox.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,16 +9,16 @@
 #include <utility>
 
 #include "constants/form_flags.h"
-#include "core/fpdfdoc/cba_fontmap.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "core/fpdfdoc/cpdf_bafontmap.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
 #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
+#include "fpdfsdk/formfiller/cffl_perwindowdata.h"
 #include "fpdfsdk/pwl/cpwl_list_box.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/containers/contains.h"
 
-CFFL_ListBox::CFFL_ListBox(CPDFSDK_FormFillEnvironment* pApp,
+CFFL_ListBox::CFFL_ListBox(CFFL_InteractiveFormFiller* pFormFiller,
                            CPDFSDK_Widget* pWidget)
-    : CFFL_TextObject(pApp, pWidget) {}
+    : CFFL_TextObject(pFormFiller, pWidget) {}
 
 CFFL_ListBox::~CFFL_ListBox() = default;
 
@@ -35,17 +35,16 @@
     cp.fFontSize = kDefaultListBoxFontSize;
   }
 
-  cp.pFontMap = MaybeCreateFontMap();
+  cp.pFontMap = GetOrCreateFontMap();
   return cp;
 }
 
 std::unique_ptr<CPWL_Wnd> CFFL_ListBox::NewPWLWindow(
     const CPWL_Wnd::CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData) {
-  auto pWnd = pdfium::MakeUnique<CPWL_ListBox>(cp, std::move(pAttachedData));
-  pWnd->AttachFFLData(this);
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData) {
+  static_cast<CFFL_PerWindowData*>(pAttachedData.get())->SetFormField(this);
+  auto pWnd = std::make_unique<CPWL_ListBox>(cp, std::move(pAttachedData));
   pWnd->Realize();
-  pWnd->SetFillerNotify(m_pFormFillEnv->GetInteractiveFormFiller());
 
   for (int32_t i = 0, sz = m_pWidget->CountOptions(); i < sz; i++)
     pWnd->AddString(m_pWidget->GetOptionLabel(i));
@@ -77,14 +76,14 @@
   return std::move(pWnd);
 }
 
-bool CFFL_ListBox::OnChar(CPDFSDK_Annot* pAnnot,
+bool CFFL_ListBox::OnChar(CPDFSDK_Widget* pWidget,
                           uint32_t nChar,
-                          uint32_t nFlags) {
-  return CFFL_TextObject::OnChar(pAnnot, nChar, nFlags);
+                          Mask<FWL_EVENTFLAG> nFlags) {
+  return CFFL_TextObject::OnChar(pWidget, nChar, nFlags);
 }
 
-bool CFFL_ListBox::IsDataChanged(CPDFSDK_PageView* pPageView) {
-  CPWL_ListBox* pListBox = GetListBox(pPageView);
+bool CFFL_ListBox::IsDataChanged(const CPDFSDK_PageView* pPageView) {
+  CPWL_ListBox* pListBox = GetPWLListBox(pPageView);
   if (!pListBox)
     return false;
 
@@ -92,7 +91,7 @@
     size_t nSelCount = 0;
     for (int32_t i = 0, sz = pListBox->GetCount(); i < sz; ++i) {
       if (pListBox->IsItemSelected(i)) {
-        if (m_OriginSelections.count(i) == 0)
+        if (!pdfium::Contains(m_OriginSelections, i))
           return true;
 
         ++nSelCount;
@@ -104,50 +103,58 @@
   return pListBox->GetCurSel() != m_pWidget->GetSelectedIndex(0);
 }
 
-void CFFL_ListBox::SaveData(CPDFSDK_PageView* pPageView) {
-  CPWL_ListBox* pListBox = GetListBox(pPageView);
-  if (!pListBox)
+void CFFL_ListBox::SaveData(const CPDFSDK_PageView* pPageView) {
+  CPWL_ListBox* pListBox = GetPWLListBox(pPageView);
+  if (!pListBox) {
     return;
-
+  }
   int32_t nNewTopIndex = pListBox->GetTopVisibleIndex();
-  m_pWidget->ClearSelection(NotificationOption::kDoNotNotify);
+  ObservedPtr<CPWL_ListBox> observed_box(pListBox);
+  m_pWidget->ClearSelection();
+  if (!observed_box) {
+    return;
+  }
   if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceMultiSelect) {
     for (int32_t i = 0, sz = pListBox->GetCount(); i < sz; i++) {
       if (pListBox->IsItemSelected(i)) {
-        m_pWidget->SetOptionSelection(i, true,
-                                      NotificationOption::kDoNotNotify);
+        m_pWidget->SetOptionSelection(i);
+        if (!observed_box) {
+          return;
+        }
       }
     }
   } else {
-    m_pWidget->SetOptionSelection(pListBox->GetCurSel(), true,
-                                  NotificationOption::kDoNotNotify);
+    m_pWidget->SetOptionSelection(pListBox->GetCurSel());
+    if (!observed_box) {
+      return;
+    }
   }
-  ObservedPtr<CPDFSDK_Widget> observed_widget(m_pWidget.Get());
+  ObservedPtr<CPDFSDK_Widget> observed_widget(m_pWidget);
   ObservedPtr<CFFL_ListBox> observed_this(this);
   m_pWidget->SetTopVisibleIndex(nNewTopIndex);
-  if (!observed_widget)
+  if (!observed_widget) {
     return;
-
+  }
   m_pWidget->ResetFieldAppearance();
-  if (!observed_widget)
+  if (!observed_widget) {
     return;
-
+  }
   m_pWidget->UpdateField();
-  if (!observed_widget || !observed_this)
+  if (!observed_widget || !observed_this) {
     return;
-
+  }
   SetChangeMark();
 }
 
-void CFFL_ListBox::GetActionData(CPDFSDK_PageView* pPageView,
+void CFFL_ListBox::GetActionData(const CPDFSDK_PageView* pPageView,
                                  CPDF_AAction::AActionType type,
-                                 CPDFSDK_FieldAction& fa) {
+                                 CFFL_FieldAction& fa) {
   switch (type) {
     case CPDF_AAction::kValidate:
       if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceMultiSelect) {
         fa.sValue.clear();
       } else {
-        CPWL_ListBox* pListBox = GetListBox(pPageView);
+        CPWL_ListBox* pListBox = GetPWLListBox(pPageView);
         if (pListBox) {
           int32_t nCurSel = pListBox->GetCurSel();
           if (nCurSel >= 0)
@@ -170,8 +177,8 @@
   }
 }
 
-void CFFL_ListBox::SaveState(CPDFSDK_PageView* pPageView) {
-  CPWL_ListBox* pListBox = GetListBox(pPageView);
+void CFFL_ListBox::SavePWLWindowState(const CPDFSDK_PageView* pPageView) {
+  CPWL_ListBox* pListBox = GetPWLListBox(pPageView);
   if (!pListBox)
     return;
 
@@ -181,8 +188,9 @@
   }
 }
 
-void CFFL_ListBox::RestoreState(CPDFSDK_PageView* pPageView) {
-  CPWL_ListBox* pListBox = GetListBox(pPageView);
+void CFFL_ListBox::RecreatePWLWindowFromSavedState(
+    const CPDFSDK_PageView* pPageView) {
+  CPWL_ListBox* pListBox = CreateOrUpdatePWLListBox(pPageView);
   if (!pListBox)
     return;
 
@@ -197,7 +205,7 @@
   if (index < 0 || index >= m_pWidget->CountOptions())
     return false;
 
-  CPWL_ListBox* pListBox = GetListBox(GetCurPageView(true));
+  CPWL_ListBox* pListBox = GetPWLListBox(GetCurPageView());
   if (!pListBox)
     return false;
 
@@ -219,10 +227,16 @@
   if (index < 0 || index >= m_pWidget->CountOptions())
     return false;
 
-  CPWL_ListBox* pListBox = GetListBox(GetCurPageView(true));
+  CPWL_ListBox* pListBox = GetPWLListBox(GetCurPageView());
   return pListBox && pListBox->IsItemSelected(index);
 }
 
-CPWL_ListBox* CFFL_ListBox::GetListBox(CPDFSDK_PageView* pPageView) {
-  return static_cast<CPWL_ListBox*>(GetPWLWindow(pPageView, /*bNew=*/false));
+CPWL_ListBox* CFFL_ListBox::GetPWLListBox(
+    const CPDFSDK_PageView* pPageView) const {
+  return static_cast<CPWL_ListBox*>(GetPWLWindow(pPageView));
+}
+
+CPWL_ListBox* CFFL_ListBox::CreateOrUpdatePWLListBox(
+    const CPDFSDK_PageView* pPageView) {
+  return static_cast<CPWL_ListBox*>(CreateOrUpdatePWLWindow(pPageView));
 }
diff --git a/fpdfsdk/formfiller/cffl_listbox.h b/fpdfsdk/formfiller/cffl_listbox.h
index 7f39f5a..9e99946 100644
--- a/fpdfsdk/formfiller/cffl_listbox.h
+++ b/fpdfsdk/formfiller/cffl_listbox.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,28 +17,32 @@
 
 class CFFL_ListBox final : public CFFL_TextObject {
  public:
-  CFFL_ListBox(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget);
+  CFFL_ListBox(CFFL_InteractiveFormFiller* pFormFiller,
+               CPDFSDK_Widget* pWidget);
   ~CFFL_ListBox() override;
 
   // CFFL_TextObject:
   CPWL_Wnd::CreateParams GetCreateParam() override;
   std::unique_ptr<CPWL_Wnd> NewPWLWindow(
       const CPWL_Wnd::CreateParams& cp,
-      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
-      override;
-  bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override;
-  bool IsDataChanged(CPDFSDK_PageView* pPageView) override;
-  void SaveData(CPDFSDK_PageView* pPageView) override;
-  void GetActionData(CPDFSDK_PageView* pPageView,
+      std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData) override;
+  bool OnChar(CPDFSDK_Widget* pWidget,
+              uint32_t nChar,
+              Mask<FWL_EVENTFLAG> nFlags) override;
+  bool IsDataChanged(const CPDFSDK_PageView* pPageView) override;
+  void SaveData(const CPDFSDK_PageView* pPageView) override;
+  void GetActionData(const CPDFSDK_PageView* pPageView,
                      CPDF_AAction::AActionType type,
-                     CPDFSDK_FieldAction& fa) override;
-  void SaveState(CPDFSDK_PageView* pPageView) override;
-  void RestoreState(CPDFSDK_PageView* pPageView) override;
+                     CFFL_FieldAction& fa) override;
+  void SavePWLWindowState(const CPDFSDK_PageView* pPageView) override;
+  void RecreatePWLWindowFromSavedState(
+      const CPDFSDK_PageView* pPageView) override;
   bool SetIndexSelected(int index, bool selected) override;
   bool IsIndexSelected(int index) override;
 
  private:
-  CPWL_ListBox* GetListBox(CPDFSDK_PageView* pPageView);
+  CPWL_ListBox* GetPWLListBox(const CPDFSDK_PageView* pPageView) const;
+  CPWL_ListBox* CreateOrUpdatePWLListBox(const CPDFSDK_PageView* pPageView);
 
   std::set<int> m_OriginSelections;
   std::vector<int> m_State;
diff --git a/fpdfsdk/formfiller/cffl_perwindowdata.cpp b/fpdfsdk/formfiller/cffl_perwindowdata.cpp
new file mode 100644
index 0000000..85f6d03
--- /dev/null
+++ b/fpdfsdk/formfiller/cffl_perwindowdata.cpp
@@ -0,0 +1,30 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fpdfsdk/formfiller/cffl_perwindowdata.h"
+
+#include "fpdfsdk/cpdfsdk_widget.h"
+#include "third_party/base/memory/ptr_util.h"
+
+CFFL_PerWindowData::CFFL_PerWindowData(CPDFSDK_Widget* pWidget,
+                                       const CPDFSDK_PageView* pPageView,
+                                       uint32_t nAppearanceAge,
+                                       uint32_t nValueAge)
+    : m_pWidget(pWidget),
+      m_pPageView(pPageView),
+      m_nAppearanceAge(nAppearanceAge),
+      m_nValueAge(nValueAge) {}
+
+CFFL_PerWindowData::CFFL_PerWindowData(const CFFL_PerWindowData& that) =
+    default;
+
+CFFL_PerWindowData::~CFFL_PerWindowData() = default;
+
+std::unique_ptr<IPWL_FillerNotify::PerWindowData> CFFL_PerWindowData::Clone()
+    const {
+  // Private constructor.
+  return pdfium::WrapUnique(new CFFL_PerWindowData(*this));
+}
diff --git a/fpdfsdk/formfiller/cffl_perwindowdata.h b/fpdfsdk/formfiller/cffl_perwindowdata.h
new file mode 100644
index 0000000..b535027
--- /dev/null
+++ b/fpdfsdk/formfiller/cffl_perwindowdata.h
@@ -0,0 +1,52 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_FORMFILLER_CFFL_PERWINDOWDATA_H_
+#define FPDFSDK_FORMFILLER_CFFL_PERWINDOWDATA_H_
+
+#include <memory>
+
+#include "core/fxcrt/observed_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "fpdfsdk/pwl/ipwl_fillernotify.h"
+
+class CFFL_FormField;
+class CPDFSDK_PageView;
+class CPDFSDK_Widget;
+
+class CFFL_PerWindowData final : public IPWL_FillerNotify::PerWindowData {
+ public:
+  CFFL_PerWindowData(CPDFSDK_Widget* pWidget,
+                     const CPDFSDK_PageView* pPageView,
+                     uint32_t nAppearanceAge,
+                     uint32_t nValueAge);
+  CFFL_PerWindowData& operator=(const CFFL_PerWindowData& that) = delete;
+  ~CFFL_PerWindowData() override;
+
+  // IPWL_FillerNotify::PerWindowData:
+  std::unique_ptr<IPWL_FillerNotify::PerWindowData> Clone() const override;
+
+  CPDFSDK_Widget* GetWidget() const { return m_pWidget.Get(); }
+  const CPDFSDK_PageView* GetPageView() const { return m_pPageView; }
+  bool AppearanceAgeEquals(uint32_t age) const {
+    return age == m_nAppearanceAge;
+  }
+  uint32_t GetValueAge() const { return m_nValueAge; }
+
+  void SetFormField(CFFL_FormField* pFormField) { m_pFormField = pFormField; }
+  CFFL_FormField* GetFormField() { return m_pFormField; }
+
+ private:
+  CFFL_PerWindowData(const CFFL_PerWindowData& that);
+
+  ObservedPtr<CPDFSDK_Widget> m_pWidget;
+  UnownedPtr<const CPDFSDK_PageView> const m_pPageView;
+  UnownedPtr<CFFL_FormField> m_pFormField;
+  const uint32_t m_nAppearanceAge;
+  const uint32_t m_nValueAge;
+};
+
+#endif  // FPDFSDK_FORMFILLER_CFFL_PERWINDOWDATA_H_
diff --git a/fpdfsdk/formfiller/cffl_pushbutton.cpp b/fpdfsdk/formfiller/cffl_pushbutton.cpp
index e9872f6..28892b0 100644
--- a/fpdfsdk/formfiller/cffl_pushbutton.cpp
+++ b/fpdfsdk/formfiller/cffl_pushbutton.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,20 +8,19 @@
 
 #include <utility>
 
-#include "fpdfsdk/formfiller/cffl_formfiller.h"
+#include "fpdfsdk/formfiller/cffl_formfield.h"
 #include "fpdfsdk/pwl/cpwl_special_button.h"
-#include "third_party/base/ptr_util.h"
 
-CFFL_PushButton::CFFL_PushButton(CPDFSDK_FormFillEnvironment* pApp,
+CFFL_PushButton::CFFL_PushButton(CFFL_InteractiveFormFiller* pFormFiller,
                                  CPDFSDK_Widget* pWidget)
-    : CFFL_Button(pApp, pWidget) {}
+    : CFFL_Button(pFormFiller, pWidget) {}
 
 CFFL_PushButton::~CFFL_PushButton() = default;
 
 std::unique_ptr<CPWL_Wnd> CFFL_PushButton::NewPWLWindow(
     const CPWL_Wnd::CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData) {
-  auto pWnd = pdfium::MakeUnique<CPWL_PushButton>(cp, std::move(pAttachedData));
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData) {
+  auto pWnd = std::make_unique<CPWL_PushButton>(cp, std::move(pAttachedData));
   pWnd->Realize();
   return std::move(pWnd);
 }
diff --git a/fpdfsdk/formfiller/cffl_pushbutton.h b/fpdfsdk/formfiller/cffl_pushbutton.h
index 9ebaf60..a9ecc80 100644
--- a/fpdfsdk/formfiller/cffl_pushbutton.h
+++ b/fpdfsdk/formfiller/cffl_pushbutton.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,14 +13,14 @@
 
 class CFFL_PushButton final : public CFFL_Button {
  public:
-  CFFL_PushButton(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget);
+  CFFL_PushButton(CFFL_InteractiveFormFiller* pFormFiller,
+                  CPDFSDK_Widget* pWidget);
   ~CFFL_PushButton() override;
 
   // CFFL_Button:
   std::unique_ptr<CPWL_Wnd> NewPWLWindow(
       const CPWL_Wnd::CreateParams& cp,
-      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
-      override;
+      std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData) override;
 };
 
 #endif  // FPDFSDK_FORMFILLER_CFFL_PUSHBUTTON_H_
diff --git a/fpdfsdk/formfiller/cffl_radiobutton.cpp b/fpdfsdk/formfiller/cffl_radiobutton.cpp
index caa6349..9d11e86 100644
--- a/fpdfsdk/formfiller/cffl_radiobutton.cpp
+++ b/fpdfsdk/formfiller/cffl_radiobutton.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,105 +8,96 @@
 
 #include <utility>
 
+#include "constants/ascii.h"
 #include "core/fpdfdoc/cpdf_formcontrol.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
-#include "fpdfsdk/formfiller/cffl_formfiller.h"
+#include "fpdfsdk/formfiller/cffl_formfield.h"
 #include "fpdfsdk/pwl/cpwl_special_button.h"
 #include "public/fpdf_fwlevent.h"
+#include "third_party/base/check.h"
 
-CFFL_RadioButton::CFFL_RadioButton(CPDFSDK_FormFillEnvironment* pApp,
+CFFL_RadioButton::CFFL_RadioButton(CFFL_InteractiveFormFiller* pFormFiller,
                                    CPDFSDK_Widget* pWidget)
-    : CFFL_Button(pApp, pWidget) {}
+    : CFFL_Button(pFormFiller, pWidget) {}
 
-CFFL_RadioButton::~CFFL_RadioButton() {}
+CFFL_RadioButton::~CFFL_RadioButton() = default;
 
 std::unique_ptr<CPWL_Wnd> CFFL_RadioButton::NewPWLWindow(
     const CPWL_Wnd::CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData) {
-  auto pWnd =
-      pdfium::MakeUnique<CPWL_RadioButton>(cp, std::move(pAttachedData));
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData) {
+  auto pWnd = std::make_unique<CPWL_RadioButton>(cp, std::move(pAttachedData));
   pWnd->Realize();
   pWnd->SetCheck(m_pWidget->IsChecked());
   return std::move(pWnd);
 }
 
-bool CFFL_RadioButton::OnKeyDown(uint32_t nKeyCode, uint32_t nFlags) {
+bool CFFL_RadioButton::OnKeyDown(FWL_VKEYCODE nKeyCode,
+                                 Mask<FWL_EVENTFLAG> nFlags) {
   switch (nKeyCode) {
     case FWL_VKEY_Return:
     case FWL_VKEY_Space:
       return true;
     default:
-      return CFFL_FormFiller::OnKeyDown(nKeyCode, nFlags);
+      return CFFL_FormField::OnKeyDown(nKeyCode, nFlags);
   }
 }
 
-bool CFFL_RadioButton::OnChar(CPDFSDK_Annot* pAnnot,
+bool CFFL_RadioButton::OnChar(CPDFSDK_Widget* pWidget,
                               uint32_t nChar,
-                              uint32_t nFlags) {
+                              Mask<FWL_EVENTFLAG> nFlags) {
   switch (nChar) {
-    case FWL_VKEY_Return:
-    case FWL_VKEY_Space: {
-      CPDFSDK_PageView* pPageView = pAnnot->GetPageView();
-      ASSERT(pPageView);
+    case pdfium::ascii::kReturn:
+    case pdfium::ascii::kSpace: {
+      CPDFSDK_PageView* pPageView = pWidget->GetPageView();
+      DCHECK(pPageView);
 
-      ObservedPtr<CPDFSDK_Annot> pObserved(m_pWidget.Get());
-      if (m_pFormFillEnv->GetInteractiveFormFiller()->OnButtonUp(
-              &pObserved, pPageView, nFlags) ||
+      ObservedPtr<CPDFSDK_Widget> pObserved(m_pWidget);
+      if (m_pFormFiller->OnButtonUp(pObserved, pPageView, nFlags) ||
           !pObserved) {
         return true;
       }
 
-      CFFL_FormFiller::OnChar(pAnnot, nChar, nFlags);
-      CPWL_RadioButton* pWnd = GetRadioButton(pPageView, true);
-      if (pWnd)
+      CFFL_FormField::OnChar(pWidget, nChar, nFlags);
+      CPWL_RadioButton* pWnd = CreateOrUpdatePWLRadioButton(pPageView);
+      if (pWnd && !pWnd->IsReadOnly())
         pWnd->SetCheck(true);
       return CommitData(pPageView, nFlags);
     }
     default:
-      return CFFL_FormFiller::OnChar(pAnnot, nChar, nFlags);
+      return CFFL_FormField::OnChar(pWidget, nChar, nFlags);
   }
 }
 
 bool CFFL_RadioButton::OnLButtonUp(CPDFSDK_PageView* pPageView,
-                                   CPDFSDK_Annot* pAnnot,
-                                   uint32_t nFlags,
+                                   CPDFSDK_Widget* pWidget,
+                                   Mask<FWL_EVENTFLAG> nFlags,
                                    const CFX_PointF& point) {
-  CFFL_Button::OnLButtonUp(pPageView, pAnnot, nFlags, point);
+  CFFL_Button::OnLButtonUp(pPageView, pWidget, nFlags, point);
 
   if (!IsValid())
     return true;
 
-  CPWL_RadioButton* pWnd = GetRadioButton(pPageView, true);
+  CPWL_RadioButton* pWnd = CreateOrUpdatePWLRadioButton(pPageView);
   if (pWnd)
     pWnd->SetCheck(true);
 
   return CommitData(pPageView, nFlags);
 }
 
-bool CFFL_RadioButton::IsDataChanged(CPDFSDK_PageView* pPageView) {
-  CPWL_RadioButton* pWnd = GetRadioButton(pPageView, false);
+bool CFFL_RadioButton::IsDataChanged(const CPDFSDK_PageView* pPageView) {
+  CPWL_RadioButton* pWnd = GetPWLRadioButton(pPageView);
   return pWnd && pWnd->IsChecked() != m_pWidget->IsChecked();
 }
 
-void CFFL_RadioButton::SaveData(CPDFSDK_PageView* pPageView) {
-  CPWL_RadioButton* pWnd = GetRadioButton(pPageView, false);
+void CFFL_RadioButton::SaveData(const CPDFSDK_PageView* pPageView) {
+  CPWL_RadioButton* pWnd = GetPWLRadioButton(pPageView);
   if (!pWnd)
     return;
 
   bool bNewChecked = pWnd->IsChecked();
-  if (bNewChecked) {
-    CPDF_FormField* pField = m_pWidget->GetFormField();
-    for (int32_t i = 0, sz = pField->CountControls(); i < sz; i++) {
-      if (CPDF_FormControl* pCtrl = pField->GetControl(i)) {
-        if (pCtrl->IsChecked())
-          break;
-      }
-    }
-  }
-  ObservedPtr<CPDFSDK_Widget> observed_widget(m_pWidget.Get());
+  ObservedPtr<CPDFSDK_Widget> observed_widget(m_pWidget);
   ObservedPtr<CFFL_RadioButton> observed_this(this);
-  m_pWidget->SetCheck(bNewChecked, NotificationOption::kDoNotNotify);
+  m_pWidget->SetCheck(bNewChecked);
   if (!observed_widget)
     return;
 
@@ -117,7 +108,12 @@
   SetChangeMark();
 }
 
-CPWL_RadioButton* CFFL_RadioButton::GetRadioButton(CPDFSDK_PageView* pPageView,
-                                                   bool bNew) {
-  return static_cast<CPWL_RadioButton*>(GetPWLWindow(pPageView, bNew));
+CPWL_RadioButton* CFFL_RadioButton::GetPWLRadioButton(
+    const CPDFSDK_PageView* pPageView) const {
+  return static_cast<CPWL_RadioButton*>(GetPWLWindow(pPageView));
+}
+
+CPWL_RadioButton* CFFL_RadioButton::CreateOrUpdatePWLRadioButton(
+    const CPDFSDK_PageView* pPageView) {
+  return static_cast<CPWL_RadioButton*>(CreateOrUpdatePWLWindow(pPageView));
 }
diff --git a/fpdfsdk/formfiller/cffl_radiobutton.h b/fpdfsdk/formfiller/cffl_radiobutton.h
index 8ba2f28..016d282 100644
--- a/fpdfsdk/formfiller/cffl_radiobutton.h
+++ b/fpdfsdk/formfiller/cffl_radiobutton.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,25 +15,29 @@
 
 class CFFL_RadioButton final : public CFFL_Button {
  public:
-  CFFL_RadioButton(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget);
+  CFFL_RadioButton(CFFL_InteractiveFormFiller* pFormFiller,
+                   CPDFSDK_Widget* pWidget);
   ~CFFL_RadioButton() override;
 
   // CFFL_Button:
   std::unique_ptr<CPWL_Wnd> NewPWLWindow(
       const CPWL_Wnd::CreateParams& cp,
-      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
-      override;
-  bool OnKeyDown(uint32_t nKeyCode, uint32_t nFlags) override;
-  bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override;
+      std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData) override;
+  bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlags) override;
+  bool OnChar(CPDFSDK_Widget* pWidget,
+              uint32_t nChar,
+              Mask<FWL_EVENTFLAG> nFlags) override;
   bool OnLButtonUp(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot* pAnnot,
-                   uint32_t nFlags,
+                   CPDFSDK_Widget* pWidget,
+                   Mask<FWL_EVENTFLAG> nFlags,
                    const CFX_PointF& point) override;
-  bool IsDataChanged(CPDFSDK_PageView* pPageView) override;
-  void SaveData(CPDFSDK_PageView* pPageView) override;
+  bool IsDataChanged(const CPDFSDK_PageView* pPageView) override;
+  void SaveData(const CPDFSDK_PageView* pPageView) override;
 
  private:
-  CPWL_RadioButton* GetRadioButton(CPDFSDK_PageView* pPageView, bool bNew);
+  CPWL_RadioButton* GetPWLRadioButton(const CPDFSDK_PageView* pPageView) const;
+  CPWL_RadioButton* CreateOrUpdatePWLRadioButton(
+      const CPDFSDK_PageView* pPageView);
 };
 
 #endif  // FPDFSDK_FORMFILLER_CFFL_RADIOBUTTON_H_
diff --git a/fpdfsdk/formfiller/cffl_textfield.cpp b/fpdfsdk/formfiller/cffl_textfield.cpp
index c3cd365..8096c82 100644
--- a/fpdfsdk/formfiller/cffl_textfield.cpp
+++ b/fpdfsdk/formfiller/cffl_textfield.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,12 +8,14 @@
 
 #include <utility>
 
+#include "constants/ascii.h"
 #include "constants/form_flags.h"
-#include "core/fpdfdoc/cba_fontmap.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "core/fpdfdoc/cpdf_bafontmap.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
+#include "fpdfsdk/formfiller/cffl_perwindowdata.h"
+#include "fpdfsdk/pwl/cpwl_edit.h"
 #include "public/fpdf_fwlevent.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -26,14 +28,11 @@
 
 }  // namespace
 
-CFFL_TextField::CFFL_TextField(CPDFSDK_FormFillEnvironment* pApp,
+CFFL_TextField::CFFL_TextField(CFFL_InteractiveFormFiller* pFormFiller,
                                CPDFSDK_Widget* pWidget)
-    : CFFL_TextObject(pApp, pWidget) {}
+    : CFFL_TextObject(pFormFiller, pWidget) {}
 
 CFFL_TextField::~CFFL_TextField() {
-  for (const auto& it : m_Maps)
-    it.second->InvalidateFocusHandler(this);
-
   // See comment in cffl_formfiller.h.
   // The font map should be stored somewhere more appropriate so it will live
   // until the PWL_Edit is done with it. pdfium:566
@@ -76,18 +75,16 @@
       cp.dwFlags |= PES_RIGHT;
       break;
   }
-  cp.pFontMap = MaybeCreateFontMap();
-  cp.pFocusHandler = this;
+  cp.pFontMap = GetOrCreateFontMap();
   return cp;
 }
 
 std::unique_ptr<CPWL_Wnd> CFFL_TextField::NewPWLWindow(
     const CPWL_Wnd::CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData) {
-  auto pWnd = pdfium::MakeUnique<CPWL_Edit>(cp, std::move(pAttachedData));
-  pWnd->AttachFFLData(this);
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData) {
+  static_cast<CFFL_PerWindowData*>(pAttachedData.get())->SetFormField(this);
+  auto pWnd = std::make_unique<CPWL_Edit>(cp, std::move(pAttachedData));
   pWnd->Realize();
-  pWnd->SetFillerNotify(m_pFormFillEnv->GetInteractiveFormFiller());
 
   int32_t nMaxLen = m_pWidget->GetMaxLen();
   WideString swValue = m_pWidget->GetValue();
@@ -103,22 +100,21 @@
   return std::move(pWnd);
 }
 
-bool CFFL_TextField::OnChar(CPDFSDK_Annot* pAnnot,
+bool CFFL_TextField::OnChar(CPDFSDK_Widget* pWidget,
                             uint32_t nChar,
-                            uint32_t nFlags) {
+                            Mask<FWL_EVENTFLAG> nFlags) {
   switch (nChar) {
-    case FWL_VKEY_Return: {
+    case pdfium::ascii::kReturn: {
       if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kTextMultiline)
         break;
 
-      CPDFSDK_PageView* pPageView = GetCurPageView(true);
-      ASSERT(pPageView);
+      CPDFSDK_PageView* pPageView = GetCurPageView();
+      DCHECK(pPageView);
       m_bValid = !m_bValid;
-      m_pFormFillEnv->Invalidate(pAnnot->GetPage(),
-                                 pAnnot->GetRect().GetOuterRect());
-
+      m_pFormFiller->Invalidate(pWidget->GetPage(),
+                                pWidget->GetRect().GetOuterRect());
       if (m_bValid) {
-        if (CPWL_Wnd* pWnd = GetPWLWindow(pPageView, true))
+        if (CPWL_Wnd* pWnd = CreateOrUpdatePWLWindow(pPageView))
           pWnd->SetFocus();
         break;
       }
@@ -129,56 +125,57 @@
       DestroyPWLWindow(pPageView);
       return true;
     }
-    case FWL_VKEY_Escape: {
-      CPDFSDK_PageView* pPageView = GetCurPageView(true);
-      ASSERT(pPageView);
+    case pdfium::ascii::kEscape: {
+      CPDFSDK_PageView* pPageView = GetCurPageView();
+      DCHECK(pPageView);
       EscapeFiller(pPageView, true);
       return true;
     }
   }
 
-  return CFFL_TextObject::OnChar(pAnnot, nChar, nFlags);
+  return CFFL_TextObject::OnChar(pWidget, nChar, nFlags);
 }
 
-bool CFFL_TextField::IsDataChanged(CPDFSDK_PageView* pPageView) {
-  CPWL_Edit* pEdit = GetEdit(pPageView, false);
+bool CFFL_TextField::IsDataChanged(const CPDFSDK_PageView* pPageView) {
+  CPWL_Edit* pEdit = GetPWLEdit(pPageView);
   return pEdit && pEdit->GetText() != m_pWidget->GetValue();
 }
 
-void CFFL_TextField::SaveData(CPDFSDK_PageView* pPageView) {
-  CPWL_Edit* pWnd = GetEdit(pPageView, false);
-  if (!pWnd)
+void CFFL_TextField::SaveData(const CPDFSDK_PageView* pPageView) {
+  ObservedPtr<CPWL_Edit> observed_edit(GetPWLEdit(pPageView));
+  if (!observed_edit) {
     return;
-
+  }
   WideString sOldValue = m_pWidget->GetValue();
-  WideString sNewValue = pWnd->GetText();
-  ObservedPtr<CPDFSDK_Widget> observed_widget(m_pWidget.Get());
+  if (!observed_edit) {
+    return;
+  }
+  WideString sNewValue = observed_edit->GetText();
+  ObservedPtr<CPDFSDK_Widget> observed_widget(m_pWidget);
   ObservedPtr<CFFL_TextField> observed_this(this);
-  m_pWidget->SetValue(sNewValue, NotificationOption::kDoNotNotify);
-  if (!observed_widget)
+  m_pWidget->SetValue(sNewValue);
+  if (!observed_widget) {
     return;
-
+  }
   m_pWidget->ResetFieldAppearance();
-  if (!observed_widget)
+  if (!observed_widget) {
     return;
-
+  }
   m_pWidget->UpdateField();
-  if (!observed_widget || !observed_this)
+  if (!observed_widget || !observed_this) {
     return;
-
+  }
   SetChangeMark();
 }
 
-void CFFL_TextField::GetActionData(CPDFSDK_PageView* pPageView,
+void CFFL_TextField::GetActionData(const CPDFSDK_PageView* pPageView,
                                    CPDF_AAction::AActionType type,
-                                   CPDFSDK_FieldAction& fa) {
+                                   CFFL_FieldAction& fa) {
   switch (type) {
     case CPDF_AAction::kKeyStroke:
-      if (CPWL_Edit* pWnd = GetEdit(pPageView, false)) {
+      if (CPWL_Edit* pWnd = GetPWLEdit(pPageView)) {
         fa.bFieldFull = pWnd->IsTextFull();
-
         fa.sValue = pWnd->GetText();
-
         if (fa.bFieldFull) {
           fa.sChange.clear();
           fa.sChangeEx.clear();
@@ -186,7 +183,7 @@
       }
       break;
     case CPDF_AAction::kValidate:
-      if (CPWL_Edit* pWnd = GetEdit(pPageView, false)) {
+      if (CPWL_Edit* pWnd = GetPWLEdit(pPageView)) {
         fa.sValue = pWnd->GetText();
       }
       break;
@@ -199,12 +196,12 @@
   }
 }
 
-void CFFL_TextField::SetActionData(CPDFSDK_PageView* pPageView,
+void CFFL_TextField::SetActionData(const CPDFSDK_PageView* pPageView,
                                    CPDF_AAction::AActionType type,
-                                   const CPDFSDK_FieldAction& fa) {
+                                   const CFFL_FieldAction& fa) {
   switch (type) {
     case CPDF_AAction::kKeyStroke:
-      if (CPWL_Edit* pEdit = GetEdit(pPageView, false)) {
+      if (CPWL_Edit* pEdit = GetPWLEdit(pPageView)) {
         pEdit->SetFocus();
         pEdit->SetSelection(fa.nSelStart, fa.nSelEnd);
         pEdit->ReplaceSelection(fa.sChange);
@@ -215,17 +212,18 @@
   }
 }
 
-void CFFL_TextField::SaveState(CPDFSDK_PageView* pPageView) {
-  CPWL_Edit* pWnd = GetEdit(pPageView, false);
+void CFFL_TextField::SavePWLWindowState(const CPDFSDK_PageView* pPageView) {
+  CPWL_Edit* pWnd = GetPWLEdit(pPageView);
   if (!pWnd)
     return;
 
-  pWnd->GetSelection(m_State.nStart, m_State.nEnd);
+  std::tie(m_State.nStart, m_State.nEnd) = pWnd->GetSelection();
   m_State.sValue = pWnd->GetText();
 }
 
-void CFFL_TextField::RestoreState(CPDFSDK_PageView* pPageView) {
-  CPWL_Edit* pWnd = GetEdit(pPageView, true);
+void CFFL_TextField::RecreatePWLWindowFromSavedState(
+    const CPDFSDK_PageView* pPageView) {
+  CPWL_Edit* pWnd = CreateOrUpdatePWLEdit(pPageView);
   if (!pWnd)
     return;
 
@@ -234,23 +232,23 @@
 }
 
 #ifdef PDF_ENABLE_XFA
-bool CFFL_TextField::IsFieldFull(CPDFSDK_PageView* pPageView) {
-  CPWL_Edit* pWnd = GetEdit(pPageView, false);
+bool CFFL_TextField::IsFieldFull(const CPDFSDK_PageView* pPageView) {
+  CPWL_Edit* pWnd = GetPWLEdit(pPageView);
   return pWnd && pWnd->IsTextFull();
 }
 #endif  // PDF_ENABLE_XFA
 
-void CFFL_TextField::OnSetFocus(CPWL_Edit* pEdit) {
-  pEdit->SetCharSet(FX_CHARSET_ChineseSimplified);
+void CFFL_TextField::OnSetFocusForEdit(CPWL_Edit* pEdit) {
+  pEdit->SetCharSet(FX_Charset::kChineseSimplified);
   pEdit->SetReadyToInput();
-
-  WideString wsText = pEdit->GetText();
-  int nCharacters = wsText.GetLength();
-  ByteString bsUTFText = wsText.ToUTF16LE();
-  auto* pBuffer = reinterpret_cast<const unsigned short*>(bsUTFText.c_str());
-  m_pFormFillEnv->OnSetFieldInputFocus(pBuffer, nCharacters, true);
+  m_pFormFiller->OnSetFieldInputFocus(pEdit->GetText());
 }
 
-CPWL_Edit* CFFL_TextField::GetEdit(CPDFSDK_PageView* pPageView, bool bNew) {
-  return static_cast<CPWL_Edit*>(GetPWLWindow(pPageView, bNew));
+CPWL_Edit* CFFL_TextField::GetPWLEdit(const CPDFSDK_PageView* pPageView) const {
+  return static_cast<CPWL_Edit*>(GetPWLWindow(pPageView));
+}
+
+CPWL_Edit* CFFL_TextField::CreateOrUpdatePWLEdit(
+    const CPDFSDK_PageView* pPageView) {
+  return static_cast<CPWL_Edit*>(CreateOrUpdatePWLWindow(pPageView));
 }
diff --git a/fpdfsdk/formfiller/cffl_textfield.h b/fpdfsdk/formfiller/cffl_textfield.h
index 5941b42..f7f5d37 100644
--- a/fpdfsdk/formfiller/cffl_textfield.h
+++ b/fpdfsdk/formfiller/cffl_textfield.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -19,38 +19,41 @@
   WideString sValue;
 };
 
-class CFFL_TextField final : public CFFL_TextObject,
-                             public CPWL_Wnd::FocusHandlerIface {
+class CFFL_TextField final : public CFFL_TextObject {
  public:
-  CFFL_TextField(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget);
+  CFFL_TextField(CFFL_InteractiveFormFiller* pFormFiller,
+                 CPDFSDK_Widget* pWidget);
   ~CFFL_TextField() override;
 
   // CFFL_TextObject:
   CPWL_Wnd::CreateParams GetCreateParam() override;
   std::unique_ptr<CPWL_Wnd> NewPWLWindow(
       const CPWL_Wnd::CreateParams& cp,
-      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
-      override;
-  bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override;
-  bool IsDataChanged(CPDFSDK_PageView* pPageView) override;
-  void SaveData(CPDFSDK_PageView* pPageView) override;
-  void GetActionData(CPDFSDK_PageView* pPageView,
+      std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData) override;
+  bool OnChar(CPDFSDK_Widget* pWidget,
+              uint32_t nChar,
+              Mask<FWL_EVENTFLAG> nFlags) override;
+  bool IsDataChanged(const CPDFSDK_PageView* pPageView) override;
+  void SaveData(const CPDFSDK_PageView* pPageView) override;
+  void GetActionData(const CPDFSDK_PageView* pPageView,
                      CPDF_AAction::AActionType type,
-                     CPDFSDK_FieldAction& fa) override;
-  void SetActionData(CPDFSDK_PageView* pPageView,
+                     CFFL_FieldAction& fa) override;
+  void SetActionData(const CPDFSDK_PageView* pPageView,
                      CPDF_AAction::AActionType type,
-                     const CPDFSDK_FieldAction& fa) override;
-  void SaveState(CPDFSDK_PageView* pPageView) override;
-  void RestoreState(CPDFSDK_PageView* pPageView) override;
+                     const CFFL_FieldAction& fa) override;
+  void SavePWLWindowState(const CPDFSDK_PageView* pPageView) override;
+  void RecreatePWLWindowFromSavedState(
+      const CPDFSDK_PageView* pPageView) override;
 #ifdef PDF_ENABLE_XFA
-  bool IsFieldFull(CPDFSDK_PageView* pPageView) override;
+  bool IsFieldFull(const CPDFSDK_PageView* pPageView) override;
 #endif
 
-  // CPWL_Wnd::FocusHandlerIface:
-  void OnSetFocus(CPWL_Edit* pEdit) override;
+  // CPWL_Wnd::ProviderIface:
+  void OnSetFocusForEdit(CPWL_Edit* pEdit) override;
 
  private:
-  CPWL_Edit* GetEdit(CPDFSDK_PageView* pPageView, bool bNew);
+  CPWL_Edit* GetPWLEdit(const CPDFSDK_PageView* pPageView) const;
+  CPWL_Edit* CreateOrUpdatePWLEdit(const CPDFSDK_PageView* pPageView);
 
   FFL_TextFieldState m_State;
 };
diff --git a/fpdfsdk/formfiller/cffl_textobject.cpp b/fpdfsdk/formfiller/cffl_textobject.cpp
index 6d84960..a7b7cd9 100644
--- a/fpdfsdk/formfiller/cffl_textobject.cpp
+++ b/fpdfsdk/formfiller/cffl_textobject.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,26 +7,12 @@
 #include "fpdfsdk/formfiller/cffl_textobject.h"
 
 #include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fpdfdoc/cba_fontmap.h"
-#include "third_party/base/ptr_util.h"
+#include "core/fpdfdoc/cpdf_bafontmap.h"
+#include "fpdfsdk/cpdfsdk_widget.h"
 
-CPWL_Wnd* CFFL_TextObject::ResetPWLWindow(CPDFSDK_PageView* pPageView,
-                                          bool bRestoreValue) {
-  if (bRestoreValue)
-    SaveState(pPageView);
-
-  DestroyPWLWindow(pPageView);
-  if (bRestoreValue)
-    RestoreState(pPageView);
-
-  ObservedPtr<CPWL_Wnd> pRet(GetPWLWindow(pPageView, !bRestoreValue));
-  m_pWidget->UpdateField();  // May invoke JS, invalidating |pRet|.
-  return pRet.Get();
-}
-
-CFFL_TextObject::CFFL_TextObject(CPDFSDK_FormFillEnvironment* pApp,
+CFFL_TextObject::CFFL_TextObject(CFFL_InteractiveFormFiller* pFormFiller,
                                  CPDFSDK_Widget* pWidget)
-    : CFFL_FormFiller(pApp, pWidget) {}
+    : CFFL_FormField(pFormFiller, pWidget) {}
 
 CFFL_TextObject::~CFFL_TextObject() {
   // Destroy view classes before this object's members are destroyed since
@@ -34,11 +20,27 @@
   DestroyWindows();
 }
 
-CBA_FontMap* CFFL_TextObject::MaybeCreateFontMap() {
+CPWL_Wnd* CFFL_TextObject::ResetPWLWindow(const CPDFSDK_PageView* pPageView) {
+  DestroyPWLWindow(pPageView);
+  ObservedPtr<CPWL_Wnd> pRet(CreateOrUpdatePWLWindow(pPageView));
+  m_pWidget->UpdateField();  // May invoke JS, invalidating |pRet|.
+  return pRet.Get();
+}
+
+CPWL_Wnd* CFFL_TextObject::RestorePWLWindow(const CPDFSDK_PageView* pPageView) {
+  SavePWLWindowState(pPageView);
+  DestroyPWLWindow(pPageView);
+  RecreatePWLWindowFromSavedState(pPageView);
+  ObservedPtr<CPWL_Wnd> pRet(GetPWLWindow(pPageView));
+  m_pWidget->UpdateField();  // May invoke JS, invalidating |pRet|.
+  return pRet.Get();
+}
+
+CPDF_BAFontMap* CFFL_TextObject::GetOrCreateFontMap() {
   if (!m_pFontMap) {
-    m_pFontMap = pdfium::MakeUnique<CBA_FontMap>(
+    m_pFontMap = std::make_unique<CPDF_BAFontMap>(
         m_pWidget->GetPDFPage()->GetDocument(),
-        m_pWidget->GetPDFAnnot()->GetAnnotDict());
+        m_pWidget->GetPDFAnnot()->GetMutableAnnotDict(), "N");
   }
   return m_pFontMap.get();
 }
diff --git a/fpdfsdk/formfiller/cffl_textobject.h b/fpdfsdk/formfiller/cffl_textobject.h
index a2381af..f2dc1b1 100644
--- a/fpdfsdk/formfiller/cffl_textobject.h
+++ b/fpdfsdk/formfiller/cffl_textobject.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,26 +9,27 @@
 
 #include <memory>
 
-#include "fpdfsdk/formfiller/cffl_formfiller.h"
+#include "fpdfsdk/formfiller/cffl_formfield.h"
 
-class CBA_FontMap;
+class CPDF_BAFontMap;
 
-// Class to implement common functionality for CFFL_FormFiller sub-classes with
+// Class to implement common functionality for CFFL_FormField sub-classes with
 // text fields.
-class CFFL_TextObject : public CFFL_FormFiller {
+class CFFL_TextObject : public CFFL_FormField {
  public:
-  // CFFL_FormFiller:
-  CPWL_Wnd* ResetPWLWindow(CPDFSDK_PageView* pPageView,
-                           bool bRestoreValue) override;
+  // CFFL_FormField:
+  CPWL_Wnd* ResetPWLWindow(const CPDFSDK_PageView* pPageView) override;
+  CPWL_Wnd* RestorePWLWindow(const CPDFSDK_PageView* pPageView) override;
 
  protected:
-  CFFL_TextObject(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget);
+  CFFL_TextObject(CFFL_InteractiveFormFiller* pFormFiller,
+                  CPDFSDK_Widget* pWidget);
   ~CFFL_TextObject() override;
 
-  CBA_FontMap* MaybeCreateFontMap();
+  CPDF_BAFontMap* GetOrCreateFontMap();
 
  private:
-  std::unique_ptr<CBA_FontMap> m_pFontMap;
+  std::unique_ptr<CPDF_BAFontMap> m_pFontMap;
 };
 
 #endif  // FPDFSDK_FORMFILLER_CFFL_TEXTOBJECT_H_
diff --git a/fpdfsdk/fpdf_annot.cpp b/fpdfsdk/fpdf_annot.cpp
index c08ba5a..cb7c96d 100644
--- a/fpdfsdk/fpdf_annot.cpp
+++ b/fpdfsdk/fpdf_annot.cpp
@@ -1,11 +1,13 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "public/fpdf_annot.h"
 
 #include <memory>
+#include <sstream>
 #include <utility>
+#include <vector>
 
 #include "constants/annotation_common.h"
 #include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
@@ -22,16 +24,23 @@
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fpdfdoc/cpdf_annot.h"
 #include "core/fpdfdoc/cpdf_color_utils.h"
 #include "core/fpdfdoc/cpdf_formfield.h"
+#include "core/fpdfdoc/cpdf_generateap.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
-#include "core/fpdfdoc/cpvt_generateap.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_string_wrappers.h"
+#include "core/fxcrt/stl_util.h"
 #include "core/fxge/cfx_color.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "fpdfsdk/cpdfsdk_interactiveform.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/memory/ptr_util.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
 
@@ -112,16 +121,19 @@
 static_assert(static_cast<int>(CPDF_Annot::Subtype::XFAWIDGET) ==
                   FPDF_ANNOT_XFAWIDGET,
               "CPDF_Annot::XFAWIDGET value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::REDACT) ==
+                  FPDF_ANNOT_REDACT,
+              "CPDF_Annot::REDACT value mismatch");
 
 // These checks ensure the consistency of annotation appearance mode values
 // across core/ and public.
-static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::Normal) ==
+static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::kNormal) ==
                   FPDF_ANNOT_APPEARANCEMODE_NORMAL,
               "CPDF_Annot::AppearanceMode::Normal value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::Rollover) ==
+static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::kRollover) ==
                   FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
               "CPDF_Annot::AppearanceMode::Rollover value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::Down) ==
+static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::kDown) ==
                   FPDF_ANNOT_APPEARANCEMODE_DOWN,
               "CPDF_Annot::AppearanceMode::Down value mismatch");
 
@@ -153,16 +165,31 @@
                   FPDF_OBJECT_REFERENCE,
               "CPDF_Object::kReference value mismatch");
 
+// These checks ensure the consistency of annotation additional action event
+// values across core/ and public.
+static_assert(static_cast<int>(CPDF_AAction::kKeyStroke) ==
+                  FPDF_ANNOT_AACTION_KEY_STROKE,
+              "CPDF_AAction::kKeyStroke value mismatch");
+static_assert(static_cast<int>(CPDF_AAction::kFormat) ==
+                  FPDF_ANNOT_AACTION_FORMAT,
+              "CPDF_AAction::kFormat value mismatch");
+static_assert(static_cast<int>(CPDF_AAction::kValidate) ==
+                  FPDF_ANNOT_AACTION_VALIDATE,
+              "CPDF_AAction::kValidate value mismatch");
+static_assert(static_cast<int>(CPDF_AAction::kCalculate) ==
+                  FPDF_ANNOT_AACTION_CALCULATE,
+              "CPDF_AAction::kCalculate value mismatch");
+
 bool HasAPStream(CPDF_Dictionary* pAnnotDict) {
-  return !!GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal);
+  return !!GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::kNormal);
 }
 
 void UpdateContentStream(CPDF_Form* pForm, CPDF_Stream* pStream) {
-  ASSERT(pForm);
-  ASSERT(pStream);
+  DCHECK(pForm);
+  DCHECK(pStream);
 
   CPDF_PageContentGenerator generator(pForm);
-  std::ostringstream buf;
+  fxcrt::ostringstream buf;
   generator.ProcessPageObjects(&buf);
   pStream->SetDataFromStringstreamAndRemoveFilter(&buf);
 }
@@ -170,9 +197,9 @@
 void SetQuadPointsAtIndex(CPDF_Array* array,
                           size_t quad_index,
                           const FS_QUADPOINTSF* quad_points) {
-  ASSERT(array);
-  ASSERT(quad_points);
-  ASSERT(IsValidQuadPointsIndex(array, quad_index));
+  DCHECK(array);
+  DCHECK(quad_points);
+  DCHECK(IsValidQuadPointsIndex(array, quad_index));
 
   size_t nIndex = quad_index * 8;
   array->SetNewAt<CPDF_Number>(nIndex, quad_points->x1);
@@ -186,38 +213,45 @@
 }
 
 void AppendQuadPoints(CPDF_Array* array, const FS_QUADPOINTSF* quad_points) {
-  ASSERT(quad_points);
-  ASSERT(array);
+  DCHECK(quad_points);
+  DCHECK(array);
 
-  array->AddNew<CPDF_Number>(quad_points->x1);
-  array->AddNew<CPDF_Number>(quad_points->y1);
-  array->AddNew<CPDF_Number>(quad_points->x2);
-  array->AddNew<CPDF_Number>(quad_points->y2);
-  array->AddNew<CPDF_Number>(quad_points->x3);
-  array->AddNew<CPDF_Number>(quad_points->y3);
-  array->AddNew<CPDF_Number>(quad_points->x4);
-  array->AddNew<CPDF_Number>(quad_points->y4);
+  array->AppendNew<CPDF_Number>(quad_points->x1);
+  array->AppendNew<CPDF_Number>(quad_points->y1);
+  array->AppendNew<CPDF_Number>(quad_points->x2);
+  array->AppendNew<CPDF_Number>(quad_points->y2);
+  array->AppendNew<CPDF_Number>(quad_points->x3);
+  array->AppendNew<CPDF_Number>(quad_points->y3);
+  array->AppendNew<CPDF_Number>(quad_points->x4);
+  array->AppendNew<CPDF_Number>(quad_points->y4);
 }
 
 void UpdateBBox(CPDF_Dictionary* annot_dict) {
-  ASSERT(annot_dict);
+  DCHECK(annot_dict);
   // Update BBox entry in appearance stream based on the bounding rectangle
   // of the annotation's quadpoints.
-  CPDF_Stream* pStream =
-      GetAnnotAP(annot_dict, CPDF_Annot::AppearanceMode::Normal);
+  RetainPtr<CPDF_Stream> pStream =
+      GetAnnotAP(annot_dict, CPDF_Annot::AppearanceMode::kNormal);
   if (pStream) {
     CFX_FloatRect boundingRect =
         CPDF_Annot::BoundingRectFromQuadPoints(annot_dict);
     if (boundingRect.Contains(pStream->GetDict()->GetRectFor("BBox")))
-      pStream->GetDict()->SetRectFor("BBox", boundingRect);
+      pStream->GetMutableDict()->SetRectFor("BBox", boundingRect);
   }
 }
 
-CPDF_Dictionary* GetAnnotDictFromFPDFAnnotation(FPDF_ANNOTATION annot) {
+const CPDF_Dictionary* GetAnnotDictFromFPDFAnnotation(
+    const FPDF_ANNOTATION annot) {
   CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot);
   return context ? context->GetAnnotDict() : nullptr;
 }
 
+RetainPtr<CPDF_Dictionary> GetMutableAnnotDictFromFPDFAnnotation(
+    FPDF_ANNOTATION annot) {
+  CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot);
+  return context ? context->GetMutableAnnotDict() : nullptr;
+}
+
 RetainPtr<CPDF_Dictionary> SetExtGStateInResourceDict(
     CPDF_Document* pDoc,
     const CPDF_Dictionary* pAnnotDict,
@@ -230,7 +264,7 @@
 
   // CA respresents current stroking alpha specifying constant opacity
   // value that should be used in transparent imaging model.
-  float fOpacity = pAnnotDict->GetNumberFor("CA");
+  float fOpacity = pAnnotDict->GetFloatFor("CA");
 
   pGSDict->SetNewFor<CPDF_Number>("CA", fOpacity);
 
@@ -257,7 +291,7 @@
 }
 
 CPDF_FormField* GetFormField(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot) {
-  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
   if (!pAnnotDict)
     return nullptr;
 
@@ -269,17 +303,59 @@
   return pPDFForm->GetFieldByDict(pAnnotDict);
 }
 
+const CPDFSDK_Widget* GetRadioButtonOrCheckBoxWidget(FPDF_FORMHANDLE hHandle,
+                                                     FPDF_ANNOTATION annot) {
+  const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict)
+    return nullptr;
+
+  CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
+  if (!pForm)
+    return nullptr;
+
+  CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
+  CPDF_FormField* pFormField = pPDFForm->GetFieldByDict(pAnnotDict);
+  if (!pFormField || (pFormField->GetType() != CPDF_FormField::kCheckBox &&
+                      pFormField->GetType() != CPDF_FormField::kRadioButton)) {
+    return nullptr;
+  }
+
+  CPDF_FormControl* pFormControl = pPDFForm->GetControlByDict(pAnnotDict);
+  return pFormControl ? pForm->GetWidget(pFormControl) : nullptr;
+}
+
+RetainPtr<const CPDF_Array> GetInkList(FPDF_ANNOTATION annot) {
+  FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot);
+  if (subtype != FPDF_ANNOT_INK)
+    return nullptr;
+
+  const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot);
+  return annot_dict ? annot_dict->GetArrayFor(pdfium::annotation::kInkList)
+                    : nullptr;
+}
+
 }  // namespace
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFAnnot_IsSupportedSubtype(FPDF_ANNOTATION_SUBTYPE subtype) {
   // The supported subtypes must also be communicated in the user doc.
-  return subtype == FPDF_ANNOT_CIRCLE || subtype == FPDF_ANNOT_FREETEXT ||
-         subtype == FPDF_ANNOT_HIGHLIGHT || subtype == FPDF_ANNOT_INK ||
-         subtype == FPDF_ANNOT_POPUP || subtype == FPDF_ANNOT_SQUARE ||
-         subtype == FPDF_ANNOT_SQUIGGLY || subtype == FPDF_ANNOT_STAMP ||
-         subtype == FPDF_ANNOT_STRIKEOUT || subtype == FPDF_ANNOT_TEXT ||
-         subtype == FPDF_ANNOT_UNDERLINE;
+  switch (subtype) {
+    case FPDF_ANNOT_CIRCLE:
+    case FPDF_ANNOT_FREETEXT:
+    case FPDF_ANNOT_HIGHLIGHT:
+    case FPDF_ANNOT_INK:
+    case FPDF_ANNOT_LINK:
+    case FPDF_ANNOT_POPUP:
+    case FPDF_ANNOT_SQUARE:
+    case FPDF_ANNOT_SQUIGGLY:
+    case FPDF_ANNOT_STAMP:
+    case FPDF_ANNOT_STRIKEOUT:
+    case FPDF_ANNOT_TEXT:
+    case FPDF_ANNOT_UNDERLINE:
+      return true;
+    default:
+      return false;
+  }
 }
 
 FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
@@ -293,24 +369,23 @@
   pDict->SetNewFor<CPDF_Name>(pdfium::annotation::kSubtype,
                               CPDF_Annot::AnnotSubtypeToString(
                                   static_cast<CPDF_Annot::Subtype>(subtype)));
-  auto pNewAnnot = pdfium::MakeUnique<CPDF_AnnotContext>(pDict.Get(), pPage);
+  auto pNewAnnot =
+      std::make_unique<CPDF_AnnotContext>(pDict, IPDFPageFromFPDFPage(page));
 
-  CPDF_Array* pAnnotList = pPage->GetDict()->GetArrayFor("Annots");
-  if (!pAnnotList)
-    pAnnotList = pPage->GetDict()->SetNewFor<CPDF_Array>("Annots");
-  pAnnotList->Add(pDict);
+  RetainPtr<CPDF_Array> pAnnotList = pPage->GetOrCreateAnnotsArray();
+  pAnnotList->Append(pDict);
 
   // Caller takes ownership.
   return FPDFAnnotationFromCPDFAnnotContext(pNewAnnot.release());
 }
 
 FPDF_EXPORT int FPDF_CALLCONV FPDFPage_GetAnnotCount(FPDF_PAGE page) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  const CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
   if (!pPage)
     return 0;
 
-  CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots");
-  return pAnnots ? pAnnots->size() : 0;
+  RetainPtr<const CPDF_Array> pAnnots = pPage->GetAnnotsArray();
+  return pAnnots ? fxcrt::CollectionSize<int>(*pAnnots) : 0;
 }
 
 FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV FPDFPage_GetAnnot(FPDF_PAGE page,
@@ -319,15 +394,17 @@
   if (!pPage || index < 0)
     return nullptr;
 
-  CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots");
+  RetainPtr<CPDF_Array> pAnnots = pPage->GetMutableAnnotsArray();
   if (!pAnnots || static_cast<size_t>(index) >= pAnnots->size())
     return nullptr;
 
-  CPDF_Dictionary* pDict = ToDictionary(pAnnots->GetDirectObjectAt(index));
+  RetainPtr<CPDF_Dictionary> pDict =
+      ToDictionary(pAnnots->GetMutableDirectObjectAt(index));
   if (!pDict)
     return nullptr;
 
-  auto pNewAnnot = pdfium::MakeUnique<CPDF_AnnotContext>(pDict, pPage);
+  auto pNewAnnot = std::make_unique<CPDF_AnnotContext>(
+      std::move(pDict), IPDFPageFromFPDFPage(page));
 
   // Caller takes ownership.
   return FPDFAnnotationFromCPDFAnnotContext(pNewAnnot.release());
@@ -335,15 +412,15 @@
 
 FPDF_EXPORT int FPDF_CALLCONV FPDFPage_GetAnnotIndex(FPDF_PAGE page,
                                                      FPDF_ANNOTATION annot) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  const CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
   if (!pPage)
     return -1;
 
-  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
   if (!pAnnotDict)
     return -1;
 
-  CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots");
+  RetainPtr<const CPDF_Array> pAnnots = pPage->GetAnnotsArray();
   if (!pAnnots)
     return -1;
 
@@ -356,7 +433,7 @@
   if (it == locker.end())
     return -1;
 
-  return it - locker.begin();
+  return pdfium::base::checked_cast<int>(it - locker.begin());
 }
 
 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_CloseAnnot(FPDF_ANNOTATION annot) {
@@ -369,7 +446,7 @@
   if (!pPage || index < 0)
     return false;
 
-  CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots");
+  RetainPtr<CPDF_Array> pAnnots = pPage->GetMutableAnnotsArray();
   if (!pAnnots || static_cast<size_t>(index) >= pAnnots->size())
     return false;
 
@@ -379,12 +456,12 @@
 
 FPDF_EXPORT FPDF_ANNOTATION_SUBTYPE FPDF_CALLCONV
 FPDFAnnot_GetSubtype(FPDF_ANNOTATION annot) {
-  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
   if (!pAnnotDict)
     return FPDF_ANNOT_UNKNOWN;
 
   return static_cast<FPDF_ANNOTATION_SUBTYPE>(CPDF_Annot::StringToAnnotSubtype(
-      pAnnotDict->GetStringFor(pdfium::annotation::kSubtype)));
+      pAnnotDict->GetNameFor(pdfium::annotation::kSubtype)));
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -406,23 +483,55 @@
 
   // Check that the annotation already has an appearance stream, since an
   // existing object is to be updated.
-  CPDF_Stream* pStream =
-      GetAnnotAP(pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal);
+  RetainPtr<CPDF_Dictionary> pAnnotDict = pAnnot->GetMutableAnnotDict();
+  RetainPtr<CPDF_Stream> pStream =
+      GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal);
   if (!pStream)
     return false;
 
   // Check that the object is already in this annotation's object list.
   CPDF_Form* pForm = pAnnot->GetForm();
-  auto it =
-      std::find_if(pForm->begin(), pForm->end(),
-                   [pObj](const std::unique_ptr<CPDF_PageObject>& candidate) {
-                     return candidate.get() == pObj;
-                   });
-  if (it == pForm->end())
+  if (!pdfium::Contains(*pForm, fxcrt::MakeFakeUniquePtr(pObj)))
     return false;
 
   // Update the content stream data in the annotation's AP stream.
-  UpdateContentStream(pForm, pStream);
+  UpdateContentStream(pForm, pStream.Get());
+  return true;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_AddInkStroke(FPDF_ANNOTATION annot,
+                                                     const FS_POINTF* points,
+                                                     size_t point_count) {
+  if (FPDFAnnot_GetSubtype(annot) != FPDF_ANNOT_INK || !points ||
+      point_count == 0 ||
+      !pdfium::base::IsValueInRangeForNumericType<int32_t>(point_count)) {
+    return -1;
+  }
+
+  RetainPtr<CPDF_Dictionary> annot_dict =
+      GetMutableAnnotDictFromFPDFAnnotation(annot);
+  RetainPtr<CPDF_Array> inklist = annot_dict->GetOrCreateArrayFor("InkList");
+  FX_SAFE_SIZE_T safe_ink_size = inklist->size();
+  safe_ink_size += 1;
+  if (!safe_ink_size.IsValid<int32_t>())
+    return -1;
+
+  auto ink_coord_list = inklist->AppendNew<CPDF_Array>();
+  for (size_t i = 0; i < point_count; i++) {
+    ink_coord_list->AppendNew<CPDF_Number>(points[i].x);
+    ink_coord_list->AppendNew<CPDF_Number>(points[i].y);
+  }
+  return static_cast<int>(inklist->size() - 1);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_RemoveInkList(FPDF_ANNOTATION annot) {
+  if (FPDFAnnot_GetSubtype(annot) != FPDF_ANNOT_INK)
+    return false;
+
+  RetainPtr<CPDF_Dictionary> annot_dict =
+      CPDFAnnotContextFromFPDFAnnotation(annot)->GetMutableAnnotDict();
+  annot_dict->RemoveFor("InkList");
   return true;
 }
 
@@ -438,13 +547,13 @@
     return false;
 
   // If the annotation does not have an AP stream yet, generate and set it.
-  CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
-  CPDF_Stream* pStream =
-      GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal);
+  RetainPtr<CPDF_Dictionary> pAnnotDict = pAnnot->GetMutableAnnotDict();
+  RetainPtr<CPDF_Stream> pStream =
+      GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal);
   if (!pStream) {
-    CPVT_GenerateAP::GenerateEmptyAP(pAnnot->GetPage()->GetDocument(),
-                                     pAnnotDict);
-    pStream = GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal);
+    CPDF_GenerateAP::GenerateEmptyAP(pAnnot->GetPage()->GetDocument(),
+                                     pAnnotDict.Get());
+    pStream = GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal);
     if (!pStream)
       return false;
   }
@@ -459,19 +568,14 @@
   // Note that an object that came from a different annotation must not be
   // passed here, since an object cannot belong to more than one annotation.
   CPDF_Form* pForm = pAnnot->GetForm();
-  auto it =
-      std::find_if(pForm->begin(), pForm->end(),
-                   [pObj](const std::unique_ptr<CPDF_PageObject>& candidate) {
-                     return candidate.get() == pObj;
-                   });
-  if (it != pForm->end())
+  if (pdfium::Contains(*pForm, fxcrt::MakeFakeUniquePtr(pObj)))
     return false;
 
   // Append the object to the object list.
   pForm->AppendPageObject(pdfium::WrapUnique(pObj));
 
   // Set the content stream data in the annotation's AP stream.
-  UpdateContentStream(pForm, pStream);
+  UpdateContentStream(pForm, pStream.Get());
   return true;
 }
 
@@ -481,14 +585,16 @@
     return 0;
 
   if (!pAnnot->HasForm()) {
-    CPDF_Stream* pStream =
-        GetAnnotAP(pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal);
+    RetainPtr<CPDF_Dictionary> pDict = pAnnot->GetMutableAnnotDict();
+    RetainPtr<CPDF_Stream> pStream =
+        GetAnnotAP(pDict.Get(), CPDF_Annot::AppearanceMode::kNormal);
     if (!pStream)
       return 0;
 
-    pAnnot->SetForm(pStream);
+    pAnnot->SetForm(std::move(pStream));
   }
-  return pAnnot->GetForm()->GetPageObjectCount();
+  return pdfium::base::checked_cast<int>(
+      pAnnot->GetForm()->GetPageObjectCount());
 }
 
 FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
@@ -498,12 +604,13 @@
     return nullptr;
 
   if (!pAnnot->HasForm()) {
-    CPDF_Stream* pStream =
-        GetAnnotAP(pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal);
+    RetainPtr<CPDF_Dictionary> pAnnotDict = pAnnot->GetMutableAnnotDict();
+    RetainPtr<CPDF_Stream> pStream =
+        GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal);
     if (!pStream)
       return nullptr;
 
-    pAnnot->SetForm(pStream);
+    pAnnot->SetForm(std::move(pStream));
   }
 
   return FPDFPageObjectFromCPDFPageObject(
@@ -522,15 +629,16 @@
 
   // Check that the annotation already has an appearance stream, since an
   // existing object is to be deleted.
-  CPDF_Stream* pStream =
-      GetAnnotAP(pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal);
+  RetainPtr<CPDF_Dictionary> pAnnotDict = pAnnot->GetMutableAnnotDict();
+  RetainPtr<CPDF_Stream> pStream =
+      GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal);
   if (!pStream)
     return false;
 
   if (!pAnnot->GetForm()->ErasePageObjectAtIndex(index))
     return false;
 
-  UpdateContentStream(pAnnot->GetForm(), pStream);
+  UpdateContentStream(pAnnot->GetForm(), pStream.Get());
   return true;
 }
 
@@ -540,14 +648,16 @@
                                                        unsigned int G,
                                                        unsigned int B,
                                                        unsigned int A) {
-  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  RetainPtr<CPDF_Dictionary> pAnnotDict =
+      GetMutableAnnotDictFromFPDFAnnotation(annot);
+
   if (!pAnnotDict || R > 255 || G > 255 || B > 255 || A > 255)
     return false;
 
   // For annotations with their appearance streams already defined, the path
   // stream's own color definitions take priority over the annotation color
   // definitions set by this method, hence this method will simply fail.
-  if (HasAPStream(pAnnotDict))
+  if (HasAPStream(pAnnotDict.Get()))
     return false;
 
   // Set the opacity of the annotation.
@@ -555,15 +665,15 @@
 
   // Set the color of the annotation.
   ByteString key = type == FPDFANNOT_COLORTYPE_InteriorColor ? "IC" : "C";
-  CPDF_Array* pColor = pAnnotDict->GetArrayFor(key);
+  RetainPtr<CPDF_Array> pColor = pAnnotDict->GetMutableArrayFor(key);
   if (pColor)
     pColor->Clear();
   else
     pColor = pAnnotDict->SetNewFor<CPDF_Array>(key);
 
-  pColor->AddNew<CPDF_Number>(R / 255.f);
-  pColor->AddNew<CPDF_Number>(G / 255.f);
-  pColor->AddNew<CPDF_Number>(B / 255.f);
+  pColor->AppendNew<CPDF_Number>(R / 255.f);
+  pColor->AppendNew<CPDF_Number>(G / 255.f);
+  pColor->AppendNew<CPDF_Number>(B / 255.f);
 
   return true;
 }
@@ -574,25 +684,26 @@
                                                        unsigned int* G,
                                                        unsigned int* B,
                                                        unsigned int* A) {
-  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  RetainPtr<CPDF_Dictionary> pAnnotDict =
+      GetMutableAnnotDictFromFPDFAnnotation(annot);
+
   if (!pAnnotDict || !R || !G || !B || !A)
     return false;
 
   // For annotations with their appearance streams already defined, the path
   // stream's own color definitions take priority over the annotation color
   // definitions retrieved by this method, hence this method will simply fail.
-  if (HasAPStream(pAnnotDict))
+  if (HasAPStream(pAnnotDict.Get()))
     return false;
 
-  CPDF_Array* pColor = pAnnotDict->GetArrayFor(
+  RetainPtr<const CPDF_Array> pColor = pAnnotDict->GetArrayFor(
       type == FPDFANNOT_COLORTYPE_InteriorColor ? "IC" : "C");
-  *A =
-      (pAnnotDict->KeyExist("CA") ? pAnnotDict->GetNumberFor("CA") : 1) * 255.f;
+  *A = (pAnnotDict->KeyExist("CA") ? pAnnotDict->GetFloatFor("CA") : 1) * 255.f;
   if (!pColor) {
     // Use default color. The default colors must be consistent with the ones
     // used to generate AP. See calls to GetColorStringWithDefault() in
-    // CPVT_GenerateAP::Generate*AP().
-    if (pAnnotDict->GetStringFor(pdfium::annotation::kSubtype) == "Highlight") {
+    // CPDF_GenerateAP::Generate*AP().
+    if (pAnnotDict->GetNameFor(pdfium::annotation::kSubtype) == "Highlight") {
       *R = 255;
       *G = 255;
       *B = 0;
@@ -606,22 +717,22 @@
 
   CFX_Color color = fpdfdoc::CFXColorFromArray(*pColor);
   switch (color.nColorType) {
-    case CFX_Color::kRGB:
+    case CFX_Color::Type::kRGB:
       *R = color.fColor1 * 255.f;
       *G = color.fColor2 * 255.f;
       *B = color.fColor3 * 255.f;
       break;
-    case CFX_Color::kGray:
+    case CFX_Color::Type::kGray:
       *R = 255.f * color.fColor1;
       *G = 255.f * color.fColor1;
       *B = 255.f * color.fColor1;
       break;
-    case CFX_Color::kCMYK:
+    case CFX_Color::Type::kCMYK:
       *R = 255.f * (1 - color.fColor1) * (1 - color.fColor4);
       *G = 255.f * (1 - color.fColor2) * (1 - color.fColor4);
       *B = 255.f * (1 - color.fColor3) * (1 - color.fColor4);
       break;
-    case CFX_Color::kTransparent:
+    case CFX_Color::Type::kTransparent:
       *R = 0;
       *G = 0;
       *B = 0;
@@ -648,14 +759,15 @@
   if (!FPDFAnnot_HasAttachmentPoints(annot) || !quad_points)
     return false;
 
-  CPDF_Dictionary* pAnnotDict =
-      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  CPDF_Array* pQuadPointsArray = GetQuadPointsArrayFromDictionary(pAnnotDict);
-  if (!IsValidQuadPointsIndex(pQuadPointsArray, quad_index))
+  RetainPtr<CPDF_Dictionary> pAnnotDict =
+      CPDFAnnotContextFromFPDFAnnotation(annot)->GetMutableAnnotDict();
+  RetainPtr<CPDF_Array> pQuadPointsArray =
+      GetMutableQuadPointsArrayFromDictionary(pAnnotDict.Get());
+  if (!IsValidQuadPointsIndex(pQuadPointsArray.Get(), quad_index))
     return false;
 
-  SetQuadPointsAtIndex(pQuadPointsArray, quad_index, quad_points);
-  UpdateBBox(pAnnotDict);
+  SetQuadPointsAtIndex(pQuadPointsArray.Get(), quad_index, quad_points);
+  UpdateBBox(pAnnotDict.Get());
   return true;
 }
 
@@ -665,13 +777,14 @@
   if (!FPDFAnnot_HasAttachmentPoints(annot) || !quad_points)
     return false;
 
-  CPDF_Dictionary* pAnnotDict =
-      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  CPDF_Array* pQuadPointsArray = GetQuadPointsArrayFromDictionary(pAnnotDict);
+  RetainPtr<CPDF_Dictionary> pAnnotDict =
+      CPDFAnnotContextFromFPDFAnnotation(annot)->GetMutableAnnotDict();
+  RetainPtr<CPDF_Array> pQuadPointsArray =
+      GetMutableQuadPointsArrayFromDictionary(pAnnotDict.Get());
   if (!pQuadPointsArray)
-    pQuadPointsArray = AddQuadPointsArrayToDictionary(pAnnotDict);
-  AppendQuadPoints(pQuadPointsArray, quad_points);
-  UpdateBBox(pAnnotDict);
+    pQuadPointsArray = AddQuadPointsArrayToDictionary(pAnnotDict.Get());
+  AppendQuadPoints(pQuadPointsArray.Get(), quad_points);
+  UpdateBBox(pAnnotDict.Get());
   return true;
 }
 
@@ -682,7 +795,8 @@
 
   const CPDF_Dictionary* pAnnotDict =
       CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  const CPDF_Array* pArray = GetQuadPointsArrayFromDictionary(pAnnotDict);
+  RetainPtr<const CPDF_Array> pArray =
+      GetQuadPointsArrayFromDictionary(pAnnotDict);
   return pArray ? pArray->size() / 8 : 0;
 }
 
@@ -695,16 +809,18 @@
 
   const CPDF_Dictionary* pAnnotDict =
       CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  const CPDF_Array* pArray = GetQuadPointsArrayFromDictionary(pAnnotDict);
+  RetainPtr<const CPDF_Array> pArray =
+      GetQuadPointsArrayFromDictionary(pAnnotDict);
   if (!pArray)
     return false;
 
-  return GetQuadPointsAtIndex(pArray, quad_index, quad_points);
+  return GetQuadPointsAtIndex(std::move(pArray), quad_index, quad_points);
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetRect(FPDF_ANNOTATION annot,
                                                       const FS_RECTF* rect) {
-  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  RetainPtr<CPDF_Dictionary> pAnnotDict =
+      GetMutableAnnotDictFromFPDFAnnotation(annot);
   if (!pAnnotDict || !rect)
     return false;
 
@@ -721,16 +837,16 @@
   if (FPDFAnnot_HasAttachmentPoints(annot))
     return true;
 
-  CPDF_Stream* pStream =
-      GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal);
+  RetainPtr<CPDF_Stream> pStream =
+      GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal);
   if (pStream && newRect.Contains(pStream->GetDict()->GetRectFor("BBox")))
-    pStream->GetDict()->SetRectFor("BBox", newRect);
+    pStream->GetMutableDict()->SetRectFor("BBox", newRect);
   return true;
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetRect(FPDF_ANNOTATION annot,
                                                       FS_RECTF* rect) {
-  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
   if (!pAnnotDict || !rect)
     return false;
 
@@ -739,9 +855,138 @@
   return true;
 }
 
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetVertices(FPDF_ANNOTATION annot,
+                      FS_POINTF* buffer,
+                      unsigned long length) {
+  FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot);
+  if (subtype != FPDF_ANNOT_POLYGON && subtype != FPDF_ANNOT_POLYLINE)
+    return 0;
+
+  const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!annot_dict)
+    return 0;
+
+  RetainPtr<const CPDF_Array> vertices =
+      annot_dict->GetArrayFor(pdfium::annotation::kVertices);
+  if (!vertices)
+    return 0;
+
+  // Truncate to an even number.
+  const unsigned long points_len =
+      fxcrt::CollectionSize<unsigned long>(*vertices) / 2;
+  if (buffer && length >= points_len) {
+    for (unsigned long i = 0; i < points_len; ++i) {
+      buffer[i].x = vertices->GetFloatAt(i * 2);
+      buffer[i].y = vertices->GetFloatAt(i * 2 + 1);
+    }
+  }
+  return points_len;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetInkListCount(FPDF_ANNOTATION annot) {
+  RetainPtr<const CPDF_Array> ink_list = GetInkList(annot);
+  return ink_list ? fxcrt::CollectionSize<unsigned long>(*ink_list) : 0;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetInkListPath(FPDF_ANNOTATION annot,
+                         unsigned long path_index,
+                         FS_POINTF* buffer,
+                         unsigned long length) {
+  RetainPtr<const CPDF_Array> ink_list = GetInkList(annot);
+  if (!ink_list)
+    return 0;
+
+  RetainPtr<const CPDF_Array> path = ink_list->GetArrayAt(path_index);
+  if (!path)
+    return 0;
+
+  // Truncate to an even number.
+  const unsigned long points_len =
+      fxcrt::CollectionSize<unsigned long>(*path) / 2;
+  if (buffer && length >= points_len) {
+    for (unsigned long i = 0; i < points_len; ++i) {
+      buffer[i].x = path->GetFloatAt(i * 2);
+      buffer[i].y = path->GetFloatAt(i * 2 + 1);
+    }
+  }
+  return points_len;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetLine(FPDF_ANNOTATION annot,
+                                                      FS_POINTF* start,
+                                                      FS_POINTF* end) {
+  if (!start || !end)
+    return false;
+
+  FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot);
+  if (subtype != FPDF_ANNOT_LINE)
+    return false;
+
+  const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!annot_dict)
+    return false;
+
+  RetainPtr<const CPDF_Array> line =
+      annot_dict->GetArrayFor(pdfium::annotation::kL);
+  if (!line || line->size() < 4)
+    return false;
+
+  start->x = line->GetFloatAt(0);
+  start->y = line->GetFloatAt(1);
+  end->x = line->GetFloatAt(2);
+  end->y = line->GetFloatAt(3);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetBorder(FPDF_ANNOTATION annot,
+                                                        float horizontal_radius,
+                                                        float vertical_radius,
+                                                        float border_width) {
+  RetainPtr<CPDF_Dictionary> annot_dict =
+      GetMutableAnnotDictFromFPDFAnnotation(annot);
+  if (!annot_dict)
+    return false;
+
+  // Remove the appearance stream. Otherwise PDF viewers will render that and
+  // not use the border values.
+  annot_dict->RemoveFor(pdfium::annotation::kAP);
+
+  auto border = annot_dict->SetNewFor<CPDF_Array>(pdfium::annotation::kBorder);
+  border->AppendNew<CPDF_Number>(horizontal_radius);
+  border->AppendNew<CPDF_Number>(vertical_radius);
+  border->AppendNew<CPDF_Number>(border_width);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_GetBorder(FPDF_ANNOTATION annot,
+                    float* horizontal_radius,
+                    float* vertical_radius,
+                    float* border_width) {
+  if (!horizontal_radius || !vertical_radius || !border_width)
+    return false;
+
+  const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!annot_dict)
+    return false;
+
+  RetainPtr<const CPDF_Array> border =
+      annot_dict->GetArrayFor(pdfium::annotation::kBorder);
+  if (!border || border->size() < 3)
+    return false;
+
+  *horizontal_radius = border->GetFloatAt(0);
+  *vertical_radius = border->GetFloatAt(1);
+  *border_width = border->GetFloatAt(2);
+  return true;
+}
+
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_HasKey(FPDF_ANNOTATION annot,
                                                      FPDF_BYTESTRING key) {
-  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
   return pAnnotDict && pAnnotDict->KeyExist(key);
 }
 
@@ -750,8 +995,8 @@
   if (!FPDFAnnot_HasKey(annot, key))
     return FPDF_OBJECT_UNKNOWN;
 
-  auto* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
-  CPDF_Object* pObj = pAnnot->GetAnnotDict()->GetObjectFor(key);
+  const CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
+  RetainPtr<const CPDF_Object> pObj = pAnnot->GetAnnotDict()->GetObjectFor(key);
   return pObj ? pObj->GetType() : FPDF_OBJECT_UNKNOWN;
 }
 
@@ -759,11 +1004,13 @@
 FPDFAnnot_SetStringValue(FPDF_ANNOTATION annot,
                          FPDF_BYTESTRING key,
                          FPDF_WIDESTRING value) {
-  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  RetainPtr<CPDF_Dictionary> pAnnotDict =
+      GetMutableAnnotDictFromFPDFAnnotation(annot);
   if (!pAnnotDict)
     return false;
 
-  pAnnotDict->SetNewFor<CPDF_String>(key, WideStringFromFPDFWideString(value));
+  pAnnotDict->SetNewFor<CPDF_String>(
+      key, WideStringFromFPDFWideString(value).AsStringView());
   return true;
 }
 
@@ -772,7 +1019,7 @@
                          FPDF_BYTESTRING key,
                          FPDF_WCHAR* buffer,
                          unsigned long buflen) {
-  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
   if (!pAnnotDict)
     return 0;
 
@@ -787,11 +1034,11 @@
   if (!value)
     return false;
 
-  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
   if (!pAnnotDict)
     return false;
 
-  const CPDF_Object* p = pAnnotDict->GetObjectFor(key);
+  RetainPtr<const CPDF_Object> p = pAnnotDict->GetObjectFor(key);
   if (!p || p->GetType() != FPDF_OBJECT_NUMBER)
     return false;
 
@@ -803,7 +1050,8 @@
 FPDFAnnot_SetAP(FPDF_ANNOTATION annot,
                 FPDF_ANNOT_APPEARANCEMODE appearanceMode,
                 FPDF_WIDESTRING value) {
-  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  RetainPtr<CPDF_Dictionary> pAnnotDict =
+      GetMutableAnnotDictFromFPDFAnnotation(annot);
   if (!pAnnotDict)
     return false;
 
@@ -811,13 +1059,13 @@
     return false;
 
   static constexpr const char* kModeKeyForMode[] = {"N", "R", "D"};
-  static_assert(
-      FX_ArraySize(kModeKeyForMode) == FPDF_ANNOT_APPEARANCEMODE_COUNT,
-      "length of kModeKeyForMode should be equal to "
-      "FPDF_ANNOT_APPEARANCEMODE_COUNT");
+  static_assert(std::size(kModeKeyForMode) == FPDF_ANNOT_APPEARANCEMODE_COUNT,
+                "length of kModeKeyForMode should be equal to "
+                "FPDF_ANNOT_APPEARANCEMODE_COUNT");
   const char* modeKey = kModeKeyForMode[appearanceMode];
 
-  CPDF_Dictionary* pApDict = pAnnotDict->GetDictFor(pdfium::annotation::kAP);
+  RetainPtr<CPDF_Dictionary> pApDict =
+      pAnnotDict->GetMutableDictFor(pdfium::annotation::kAP);
 
   // If value is null, we're in remove mode. Otherwise, we're in add/update
   // mode.
@@ -836,13 +1084,13 @@
     if (!pDoc)
       return false;
 
-    CPDF_Stream* pNewIndirectStream = pDoc->NewIndirect<CPDF_Stream>();
-
+    auto pNewIndirectStream = pDoc->NewIndirect<CPDF_Stream>();
     ByteString newAPStream =
-        PDF_EncodeText(WideStringFromFPDFWideString(value));
+        PDF_EncodeText(WideStringFromFPDFWideString(value).AsStringView());
     pNewIndirectStream->SetData(newAPStream.raw_span());
 
-    CPDF_Dictionary* pStreamDict = pNewIndirectStream->GetDict();
+    RetainPtr<CPDF_Dictionary> pStreamDict =
+        pNewIndirectStream->GetMutableDict();
     pStreamDict->SetNewFor<CPDF_Name>(pdfium::annotation::kType, "XObject");
     pStreamDict->SetNewFor<CPDF_Name>(pdfium::annotation::kSubtype, "Form");
     pStreamDict->SetRectFor("BBox", rect);
@@ -850,16 +1098,16 @@
     // checking for value < 1 and not <= 1 so that the output PDF size does not
     // unnecessarily bloat up by creating a new dictionary in case of solid
     // color.
-    if (pAnnotDict->KeyExist("CA") && pAnnotDict->GetNumberFor("CA") < 1.0f) {
+    if (pAnnotDict->KeyExist("CA") && pAnnotDict->GetFloatFor("CA") < 1.0f) {
       RetainPtr<CPDF_Dictionary> pResourceDict =
-          SetExtGStateInResourceDict(pDoc, pAnnotDict, "Normal");
+          SetExtGStateInResourceDict(pDoc, pAnnotDict.Get(), "Normal");
       pStreamDict->SetFor("Resources", pResourceDict);
     }
 
     // Storing reference to indirect object in annotation's AP
-    if (!pApDict)
+    if (!pApDict) {
       pApDict = pAnnotDict->SetNewFor<CPDF_Dictionary>(pdfium::annotation::kAP);
-
+    }
     pApDict->SetNewFor<CPDF_Reference>(modeKey, pDoc,
                                        pNewIndirectStream->GetObjNum());
   } else {
@@ -879,7 +1127,8 @@
                 FPDF_ANNOT_APPEARANCEMODE appearanceMode,
                 FPDF_WCHAR* buffer,
                 unsigned long buflen) {
-  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  RetainPtr<CPDF_Dictionary> pAnnotDict =
+      GetMutableAnnotDictFromFPDFAnnotation(annot);
   if (!pAnnotDict)
     return 0;
 
@@ -889,7 +1138,7 @@
   CPDF_Annot::AppearanceMode mode =
       static_cast<CPDF_Annot::AppearanceMode>(appearanceMode);
 
-  CPDF_Stream* pStream = GetAnnotAPNoFallback(pAnnotDict, mode);
+  RetainPtr<CPDF_Stream> pStream = GetAnnotAPNoFallback(pAnnotDict.Get(), mode);
   return Utf16EncodeMaybeCopyAndReturnLength(
       pStream ? pStream->GetUnicodeText() : WideString(), buffer, buflen);
 }
@@ -900,26 +1149,28 @@
   if (!pAnnot)
     return nullptr;
 
-  CPDF_Dictionary* pLinkedDict = pAnnot->GetAnnotDict()->GetDictFor(key);
-  if (!pLinkedDict || pLinkedDict->GetStringFor("Type") != "Annot")
+  RetainPtr<CPDF_Dictionary> pLinkedDict =
+      pAnnot->GetMutableAnnotDict()->GetMutableDictFor(key);
+  if (!pLinkedDict || pLinkedDict->GetNameFor("Type") != "Annot")
     return nullptr;
 
-  auto pLinkedAnnot =
-      pdfium::MakeUnique<CPDF_AnnotContext>(pLinkedDict, pAnnot->GetPage());
+  auto pLinkedAnnot = std::make_unique<CPDF_AnnotContext>(
+      std::move(pLinkedDict), pAnnot->GetPage());
 
   // Caller takes ownership.
   return FPDFAnnotationFromCPDFAnnotContext(pLinkedAnnot.release());
 }
 
 FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_GetFlags(FPDF_ANNOTATION annot) {
-  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
   return pAnnotDict ? pAnnotDict->GetIntegerFor(pdfium::annotation::kF)
                     : FPDF_ANNOT_FLAG_NONE;
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetFlags(FPDF_ANNOTATION annot,
                                                        int flags) {
-  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  RetainPtr<CPDF_Dictionary> pAnnotDict =
+      GetMutableAnnotDictFromFPDFAnnotation(annot);
   if (!pAnnotDict)
     return false;
 
@@ -940,17 +1191,17 @@
   if (!point)
     return nullptr;
 
-  CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
+  const CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
   if (!pForm)
     return nullptr;
 
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  const CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
   if (!pPage)
     return nullptr;
 
-  CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
+  const CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
   int annot_index = -1;
-  CPDF_FormControl* pFormCtrl = pPDFForm->GetControlAtPoint(
+  const CPDF_FormControl* pFormCtrl = pPDFForm->GetControlAtPoint(
       pPage, CFXPointFFromFSPointF(*point), &annot_index);
   if (!pFormCtrl || annot_index == -1)
     return nullptr;
@@ -962,7 +1213,7 @@
                            FPDF_ANNOTATION annot,
                            FPDF_WCHAR* buffer,
                            unsigned long buflen) {
-  CPDF_FormField* pFormField = GetFormField(hHandle, annot);
+  const CPDF_FormField* pFormField = GetFormField(hHandle, annot);
   if (!pFormField)
     return 0;
   return Utf16EncodeMaybeCopyAndReturnLength(pFormField->GetFullName(), buffer,
@@ -971,16 +1222,51 @@
 
 FPDF_EXPORT int FPDF_CALLCONV
 FPDFAnnot_GetFormFieldType(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot) {
-  CPDF_FormField* pFormField = GetFormField(hHandle, annot);
+  const CPDF_FormField* pFormField = GetFormField(hHandle, annot);
   return pFormField ? static_cast<int>(pFormField->GetFieldType()) : -1;
 }
 
 FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetFormAdditionalActionJavaScript(FPDF_FORMHANDLE hHandle,
+                                            FPDF_ANNOTATION annot,
+                                            int event,
+                                            FPDF_WCHAR* buffer,
+                                            unsigned long buflen) {
+  const CPDF_FormField* pFormField = GetFormField(hHandle, annot);
+  if (!pFormField)
+    return 0;
+
+  if (event < FPDF_ANNOT_AACTION_KEY_STROKE ||
+      event > FPDF_ANNOT_AACTION_CALCULATE) {
+    return 0;
+  }
+
+  auto type = static_cast<CPDF_AAction::AActionType>(event);
+  CPDF_AAction additional_action = pFormField->GetAdditionalAction();
+  CPDF_Action action = additional_action.GetAction(type);
+  return Utf16EncodeMaybeCopyAndReturnLength(action.GetJavaScript(), buffer,
+                                             buflen);
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetFormFieldAlternateName(FPDF_FORMHANDLE hHandle,
+                                    FPDF_ANNOTATION annot,
+                                    FPDF_WCHAR* buffer,
+                                    unsigned long buflen) {
+  const CPDF_FormField* pFormField = GetFormField(hHandle, annot);
+  if (!pFormField)
+    return 0;
+
+  return Utf16EncodeMaybeCopyAndReturnLength(pFormField->GetAlternateName(),
+                                             buffer, buflen);
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDFAnnot_GetFormFieldValue(FPDF_FORMHANDLE hHandle,
                             FPDF_ANNOTATION annot,
                             FPDF_WCHAR* buffer,
                             unsigned long buflen) {
-  CPDF_FormField* pFormField = GetFormField(hHandle, annot);
+  const CPDF_FormField* pFormField = GetFormField(hHandle, annot);
   if (!pFormField)
     return 0;
   return Utf16EncodeMaybeCopyAndReturnLength(pFormField->GetValue(), buffer,
@@ -989,7 +1275,7 @@
 
 FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_GetOptionCount(FPDF_FORMHANDLE hHandle,
                                                        FPDF_ANNOTATION annot) {
-  CPDF_FormField* pFormField = GetFormField(hHandle, annot);
+  const CPDF_FormField* pFormField = GetFormField(hHandle, annot);
   return pFormField ? pFormField->CountOptions() : -1;
 }
 
@@ -1002,7 +1288,7 @@
   if (index < 0)
     return 0;
 
-  CPDF_FormField* pFormField = GetFormField(hHandle, annot);
+  const CPDF_FormField* pFormField = GetFormField(hHandle, annot);
   if (!pFormField || index >= pFormField->CountOptions())
     return 0;
 
@@ -1011,6 +1297,25 @@
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_IsOptionSelected(FPDF_FORMHANDLE handle,
+                           FPDF_ANNOTATION annot,
+                           int index) {
+  if (index < 0)
+    return false;
+
+  const CPDF_FormField* form_field = GetFormField(handle, annot);
+  if (!form_field || index >= form_field->CountOptions())
+    return false;
+
+  if (form_field->GetFieldType() != FormFieldType::kComboBox &&
+      form_field->GetFieldType() != FormFieldType::kListBox) {
+    return false;
+  }
+
+  return form_field->IsItemSelected(index);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFAnnot_GetFontSize(FPDF_FORMHANDLE hHandle,
                       FPDF_ANNOTATION annot,
                       float* value) {
@@ -1021,7 +1326,7 @@
   if (!pForm)
     return false;
 
-  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
   if (!pAnnotDict)
     return false;
 
@@ -1040,28 +1345,127 @@
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_IsChecked(FPDF_FORMHANDLE hHandle,
                                                         FPDF_ANNOTATION annot) {
-  CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
-  if (!pForm)
+  const CPDFSDK_Widget* pWidget =
+      GetRadioButtonOrCheckBoxWidget(hHandle, annot);
+  return pWidget && pWidget->IsChecked();
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_SetFocusableSubtypes(FPDF_FORMHANDLE hHandle,
+                               const FPDF_ANNOTATION_SUBTYPE* subtypes,
+                               size_t count) {
+  CPDFSDK_FormFillEnvironment* pFormFillEnv =
+      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
+  if (!pFormFillEnv)
     return false;
 
-  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
-  if (!pAnnotDict)
+  if (count > 0 && !subtypes)
     return false;
 
+  std::vector<CPDF_Annot::Subtype> focusable_annot_types;
+  focusable_annot_types.reserve(count);
+  for (size_t i = 0; i < count; ++i) {
+    focusable_annot_types.push_back(
+        static_cast<CPDF_Annot::Subtype>(subtypes[i]));
+  }
+
+  pFormFillEnv->SetFocusableAnnotSubtypes(focusable_annot_types);
+  return true;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFAnnot_GetFocusableSubtypesCount(FPDF_FORMHANDLE hHandle) {
+  CPDFSDK_FormFillEnvironment* pFormFillEnv =
+      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
+  if (!pFormFillEnv)
+    return -1;
+
+  return fxcrt::CollectionSize<int>(pFormFillEnv->GetFocusableAnnotSubtypes());
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_GetFocusableSubtypes(FPDF_FORMHANDLE hHandle,
+                               FPDF_ANNOTATION_SUBTYPE* subtypes,
+                               size_t count) {
+  CPDFSDK_FormFillEnvironment* pFormFillEnv =
+      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
+  if (!pFormFillEnv)
+    return false;
+
+  if (!subtypes)
+    return false;
+
+  const std::vector<CPDF_Annot::Subtype>& focusable_annot_types =
+      pFormFillEnv->GetFocusableAnnotSubtypes();
+
+  // Host should allocate enough memory to get the list of currently supported
+  // focusable subtypes.
+  if (count < focusable_annot_types.size())
+    return false;
+
+  for (size_t i = 0; i < focusable_annot_types.size(); ++i) {
+    subtypes[i] =
+        static_cast<FPDF_ANNOTATION_SUBTYPE>(focusable_annot_types[i]);
+  }
+
+  return true;
+}
+
+FPDF_EXPORT FPDF_LINK FPDF_CALLCONV FPDFAnnot_GetLink(FPDF_ANNOTATION annot) {
+  if (FPDFAnnot_GetSubtype(annot) != FPDF_ANNOT_LINK)
+    return nullptr;
+
+  // Unretained reference in public API. NOLINTNEXTLINE
+  return FPDFLinkFromCPDFDictionary(
+      CPDFAnnotContextFromFPDFAnnotation(annot)->GetMutableAnnotDict());
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFAnnot_GetFormControlCount(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot) {
+  CPDF_FormField* pFormField = GetFormField(hHandle, annot);
+  return pFormField ? pFormField->CountControls() : -1;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFAnnot_GetFormControlIndex(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot) {
+  const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict)
+    return -1;
+
+  CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
+  if (!pForm)
+    return -1;
+
   CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
   CPDF_FormField* pFormField = pPDFForm->GetFieldByDict(pAnnotDict);
-  if (!pFormField)
-    return false;
-
-  if (pFormField->GetType() != CPDF_FormField::kCheckBox &&
-      pFormField->GetType() != CPDF_FormField::kRadioButton) {
-    return false;
-  }
-
   CPDF_FormControl* pFormControl = pPDFForm->GetControlByDict(pAnnotDict);
-  if (!pFormControl)
+  return pFormField ? pFormField->GetControlIndex(pFormControl) : -1;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetFormFieldExportValue(FPDF_FORMHANDLE hHandle,
+                                  FPDF_ANNOTATION annot,
+                                  FPDF_WCHAR* buffer,
+                                  unsigned long buflen) {
+  const CPDFSDK_Widget* pWidget =
+      GetRadioButtonOrCheckBoxWidget(hHandle, annot);
+  if (!pWidget)
+    return 0;
+
+  return Utf16EncodeMaybeCopyAndReturnLength(pWidget->GetExportValue(), buffer,
+                                             buflen);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetURI(FPDF_ANNOTATION annot,
+                                                     const char* uri) {
+  if (!uri || FPDFAnnot_GetSubtype(annot) != FPDF_ANNOT_LINK)
     return false;
 
-  CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl);
-  return pWidget && pWidget->IsChecked();
+  RetainPtr<CPDF_Dictionary> annot_dict =
+      GetMutableAnnotDictFromFPDFAnnotation(annot);
+  auto action = annot_dict->SetNewFor<CPDF_Dictionary>("A");
+  action->SetNewFor<CPDF_Name>("Type", "Action");
+  action->SetNewFor<CPDF_Name>("S", "URI");
+  action->SetNewFor<CPDF_String>("URI", uri, /*bHex=*/false);
+  return true;
 }
diff --git a/fpdfsdk/fpdf_annot_embeddertest.cpp b/fpdfsdk/fpdf_annot_embeddertest.cpp
index 1e0ebd8..9769570 100644
--- a/fpdfsdk/fpdf_annot_embeddertest.cpp
+++ b/fpdfsdk/fpdf_annot_embeddertest.cpp
@@ -1,29 +1,345 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "public/fpdf_annot.h"
+
+#include <limits.h>
+
 #include <algorithm>
-#include <cwchar>
-#include <memory>
 #include <string>
 #include <vector>
 
 #include "build/build_config.h"
 #include "constants/annotation_common.h"
+#include "core/fpdfapi/page/cpdf_annotcontext.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
 #include "public/cpp/fpdf_scopers.h"
-#include "public/fpdf_annot.h"
 #include "public/fpdf_edit.h"
 #include "public/fpdf_formfill.h"
 #include "public/fpdfview.h"
 #include "testing/embedder_test.h"
+#include "testing/embedder_test_constants.h"
 #include "testing/fx_string_testhelpers.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/utils/hash.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/containers/span.h"
+
+using pdfium::AnnotationStampWithApChecksum;
+
+namespace {
+
+const wchar_t kStreamData[] =
+    L"/GS gs 0.0 0.0 0.0 RG 4 w 211.8 747.6 m 211.8 744.8 "
+    L"212.6 743.0 214.2 740.8 "
+    L"c 215.4 739.0 216.8 737.1 218.9 736.1 c 220.8 735.1 221.4 733.0 "
+    L"223.7 732.4 c 232.6 729.9 242.0 730.8 251.2 730.8 c 257.5 730.8 "
+    L"263.0 732.9 269.0 734.4 c S";
+
+void VerifyFocusableAnnotSubtypes(
+    FPDF_FORMHANDLE form_handle,
+    pdfium::span<const FPDF_ANNOTATION_SUBTYPE> expected_subtypes) {
+  ASSERT_EQ(static_cast<int>(expected_subtypes.size()),
+            FPDFAnnot_GetFocusableSubtypesCount(form_handle));
+
+  std::vector<FPDF_ANNOTATION_SUBTYPE> actual_subtypes(
+      expected_subtypes.size());
+  ASSERT_TRUE(FPDFAnnot_GetFocusableSubtypes(
+      form_handle, actual_subtypes.data(), actual_subtypes.size()));
+  for (size_t i = 0; i < expected_subtypes.size(); ++i)
+    ASSERT_EQ(expected_subtypes[i], actual_subtypes[i]);
+}
+
+void SetAndVerifyFocusableAnnotSubtypes(
+    FPDF_FORMHANDLE form_handle,
+    pdfium::span<const FPDF_ANNOTATION_SUBTYPE> subtypes) {
+  ASSERT_TRUE(FPDFAnnot_SetFocusableSubtypes(form_handle, subtypes.data(),
+                                             subtypes.size()));
+  VerifyFocusableAnnotSubtypes(form_handle, subtypes);
+}
+
+void VerifyAnnotationSubtypesAndFocusability(
+    FPDF_FORMHANDLE form_handle,
+    FPDF_PAGE page,
+    pdfium::span<const FPDF_ANNOTATION_SUBTYPE> expected_subtypes,
+    pdfium::span<const FPDF_ANNOTATION_SUBTYPE> expected_focusable_subtypes) {
+  ASSERT_EQ(static_cast<int>(expected_subtypes.size()),
+            FPDFPage_GetAnnotCount(page));
+  for (size_t i = 0; i < expected_subtypes.size(); ++i) {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, i));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(expected_subtypes[i], FPDFAnnot_GetSubtype(annot.get()));
+
+    bool expected_focusable =
+        pdfium::Contains(expected_focusable_subtypes, expected_subtypes[i]);
+    EXPECT_EQ(expected_focusable,
+              FORM_SetFocusedAnnot(form_handle, annot.get()));
+
+    // Kill the focus so the next test starts in an unfocused state.
+    FORM_ForceToKillFocus(form_handle);
+  }
+}
+
+void VerifyUriActionInLink(FPDF_DOCUMENT doc,
+                           FPDF_LINK link,
+                           const std::string& expected_uri) {
+  ASSERT_TRUE(link);
+
+  FPDF_ACTION action = FPDFLink_GetAction(link);
+  ASSERT_TRUE(action);
+  EXPECT_EQ(static_cast<unsigned long>(PDFACTION_URI),
+            FPDFAction_GetType(action));
+
+  unsigned long bufsize = FPDFAction_GetURIPath(doc, action, nullptr, 0);
+  ASSERT_EQ(expected_uri.size() + 1, bufsize);
+
+  std::vector<char> buffer(bufsize);
+  EXPECT_EQ(bufsize,
+            FPDFAction_GetURIPath(doc, action, buffer.data(), bufsize));
+  EXPECT_STREQ(expected_uri.c_str(), buffer.data());
+}
+
+}  // namespace
 
 class FPDFAnnotEmbedderTest : public EmbedderTest {};
 
+TEST_F(FPDFAnnotEmbedderTest, SetAP) {
+  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
+  ASSERT_TRUE(doc);
+  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100));
+  ASSERT_TRUE(page);
+  ScopedFPDFWideString ap_stream = GetFPDFWideString(kStreamData);
+  ASSERT_TRUE(ap_stream);
+
+  ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK));
+  ASSERT_TRUE(annot);
+
+  // Negative case: FPDFAnnot_SetAP() should fail if bounding rect is not yet
+  // set on the annotation.
+  EXPECT_FALSE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+                               ap_stream.get()));
+
+  const FS_RECTF bounding_rect{206.0f, 753.0f, 339.0f, 709.0f};
+  EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &bounding_rect));
+
+  ASSERT_TRUE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color,
+                                 /*R=*/255, /*G=*/0, /*B=*/0, /*A=*/255));
+
+  EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+                              ap_stream.get()));
+
+  // Verify that appearance stream is created as form XObject
+  CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot.get());
+  ASSERT_TRUE(context);
+  const CPDF_Dictionary* annot_dict = context->GetAnnotDict();
+  ASSERT_TRUE(annot_dict);
+  RetainPtr<const CPDF_Dictionary> ap_dict =
+      annot_dict->GetDictFor(pdfium::annotation::kAP);
+  ASSERT_TRUE(ap_dict);
+  RetainPtr<const CPDF_Dictionary> stream_dict = ap_dict->GetDictFor("N");
+  ASSERT_TRUE(stream_dict);
+  // Check for non-existence of resources dictionary in case of opaque color
+  RetainPtr<const CPDF_Dictionary> resources_dict =
+      stream_dict->GetDictFor("Resources");
+  ASSERT_FALSE(resources_dict);
+  ByteString type = stream_dict->GetByteStringFor(pdfium::annotation::kType);
+  EXPECT_EQ("XObject", type);
+  ByteString sub_type =
+      stream_dict->GetByteStringFor(pdfium::annotation::kSubtype);
+  EXPECT_EQ("Form", sub_type);
+
+  // Check that the appearance stream is same as we just set.
+  const uint32_t kStreamDataSize = std::size(kStreamData) * sizeof(FPDF_WCHAR);
+  unsigned long normal_length_bytes = FPDFAnnot_GetAP(
+      annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, nullptr, 0);
+  ASSERT_EQ(kStreamDataSize, normal_length_bytes);
+  std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(normal_length_bytes);
+  EXPECT_EQ(kStreamDataSize,
+            FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+                            buf.data(), normal_length_bytes));
+  EXPECT_EQ(kStreamData, GetPlatformWString(buf.data()));
+}
+
+TEST_F(FPDFAnnotEmbedderTest, SetAPWithOpacity) {
+  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
+  ASSERT_TRUE(doc);
+  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100));
+  ASSERT_TRUE(page);
+  ScopedFPDFWideString ap_stream = GetFPDFWideString(kStreamData);
+  ASSERT_TRUE(ap_stream);
+
+  ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK));
+  ASSERT_TRUE(annot);
+
+  ASSERT_TRUE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color,
+                                 /*R=*/255, /*G=*/0, /*B=*/0, /*A=*/102));
+
+  const FS_RECTF bounding_rect{206.0f, 753.0f, 339.0f, 709.0f};
+  EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &bounding_rect));
+
+  EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+                              ap_stream.get()));
+
+  CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot.get());
+  ASSERT_TRUE(context);
+  const CPDF_Dictionary* annot_dict = context->GetAnnotDict();
+  ASSERT_TRUE(annot_dict);
+  RetainPtr<const CPDF_Dictionary> ap_dict =
+      annot_dict->GetDictFor(pdfium::annotation::kAP);
+  ASSERT_TRUE(ap_dict);
+  RetainPtr<const CPDF_Dictionary> stream_dict = ap_dict->GetDictFor("N");
+  ASSERT_TRUE(stream_dict);
+  RetainPtr<const CPDF_Dictionary> resources_dict =
+      stream_dict->GetDictFor("Resources");
+  ASSERT_TRUE(stream_dict);
+  RetainPtr<const CPDF_Dictionary> extGState_dict =
+      resources_dict->GetDictFor("ExtGState");
+  ASSERT_TRUE(extGState_dict);
+  RetainPtr<const CPDF_Dictionary> gs_dict = extGState_dict->GetDictFor("GS");
+  ASSERT_TRUE(gs_dict);
+  ByteString type = gs_dict->GetByteStringFor(pdfium::annotation::kType);
+  EXPECT_EQ("ExtGState", type);
+  float opacity = gs_dict->GetFloatFor("CA");
+  // Opacity value of 102 is represented as 0.4f (=104/255) in /CA entry.
+  EXPECT_FLOAT_EQ(0.4f, opacity);
+  ByteString blend_mode = gs_dict->GetByteStringFor("BM");
+  EXPECT_EQ("Normal", blend_mode);
+  bool alpha_source_flag = gs_dict->GetBooleanFor("AIS", true);
+  EXPECT_FALSE(alpha_source_flag);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, InkListAPIValidations) {
+  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
+  ASSERT_TRUE(doc);
+  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100));
+  ASSERT_TRUE(page);
+
+  // Create a new ink annotation.
+  ScopedFPDFAnnotation ink_annot(
+      FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK));
+  ASSERT_TRUE(ink_annot);
+  CPDF_AnnotContext* context =
+      CPDFAnnotContextFromFPDFAnnotation(ink_annot.get());
+  ASSERT_TRUE(context);
+  const CPDF_Dictionary* annot_dict = context->GetAnnotDict();
+  ASSERT_TRUE(annot_dict);
+
+  static constexpr FS_POINTF kFirstInkStroke[] = {
+      {80.0f, 90.0f}, {81.0f, 91.0f}, {82.0f, 92.0f},
+      {83.0f, 93.0f}, {84.0f, 94.0f}, {85.0f, 95.0f}};
+  static constexpr size_t kFirstStrokePointCount = std::size(kFirstInkStroke);
+
+  static constexpr FS_POINTF kSecondInkStroke[] = {
+      {70.0f, 90.0f}, {71.0f, 91.0f}, {72.0f, 92.0f}};
+  static constexpr size_t kSecondStrokePointCount = std::size(kSecondInkStroke);
+
+  static constexpr FS_POINTF kThirdInkStroke[] = {{60.0f, 90.0f},
+                                                  {61.0f, 91.0f},
+                                                  {62.0f, 92.0f},
+                                                  {63.0f, 93.0f},
+                                                  {64.0f, 94.0f}};
+  static constexpr size_t kThirdStrokePointCount = std::size(kThirdInkStroke);
+
+  // Negative test: |annot| is passed as nullptr.
+  EXPECT_EQ(-1, FPDFAnnot_AddInkStroke(nullptr, kFirstInkStroke,
+                                       kFirstStrokePointCount));
+
+  // Negative test: |annot| is not ink annotation.
+  // Create a new highlight annotation.
+  ScopedFPDFAnnotation highlight_annot(
+      FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_HIGHLIGHT));
+  ASSERT_TRUE(highlight_annot);
+  EXPECT_EQ(-1, FPDFAnnot_AddInkStroke(highlight_annot.get(), kFirstInkStroke,
+                                       kFirstStrokePointCount));
+
+  // Negative test: passing |point_count| as  0.
+  EXPECT_EQ(-1, FPDFAnnot_AddInkStroke(ink_annot.get(), kFirstInkStroke, 0));
+
+  // Negative test: passing |points| array as nullptr.
+  EXPECT_EQ(-1, FPDFAnnot_AddInkStroke(ink_annot.get(), nullptr,
+                                       kFirstStrokePointCount));
+
+  // Negative test: passing |point_count| more than ULONG_MAX/2.
+  EXPECT_EQ(-1, FPDFAnnot_AddInkStroke(ink_annot.get(), kSecondInkStroke,
+                                       ULONG_MAX / 2 + 1));
+
+  // InkStroke should get added to ink annotation. Also inklist should get
+  // created.
+  EXPECT_EQ(0, FPDFAnnot_AddInkStroke(ink_annot.get(), kFirstInkStroke,
+                                      kFirstStrokePointCount));
+
+  RetainPtr<const CPDF_Array> inklist = annot_dict->GetArrayFor("InkList");
+  ASSERT_TRUE(inklist);
+  EXPECT_EQ(1u, inklist->size());
+  EXPECT_EQ(kFirstStrokePointCount * 2, inklist->GetArrayAt(0)->size());
+
+  // Adding another inkStroke to ink annotation with all valid paremeters.
+  // InkList already exists in ink_annot.
+  EXPECT_EQ(1, FPDFAnnot_AddInkStroke(ink_annot.get(), kSecondInkStroke,
+                                      kSecondStrokePointCount));
+  EXPECT_EQ(2u, inklist->size());
+  EXPECT_EQ(kSecondStrokePointCount * 2, inklist->GetArrayAt(1)->size());
+
+  // Adding one more InkStroke to the ink annotation. |point_count| passed is
+  // less than the data available in |buffer|.
+  EXPECT_EQ(2, FPDFAnnot_AddInkStroke(ink_annot.get(), kThirdInkStroke,
+                                      kThirdStrokePointCount - 1));
+  EXPECT_EQ(3u, inklist->size());
+  EXPECT_EQ((kThirdStrokePointCount - 1) * 2, inklist->GetArrayAt(2)->size());
+}
+
+TEST_F(FPDFAnnotEmbedderTest, RemoveInkList) {
+  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
+  ASSERT_TRUE(doc);
+  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100));
+  ASSERT_TRUE(page);
+
+  // Negative test: |annot| is passed as nullptr.
+  EXPECT_FALSE(FPDFAnnot_RemoveInkList(nullptr));
+
+  // Negative test: |annot| is not ink annotation.
+  // Create a new highlight annotation.
+  ScopedFPDFAnnotation highlight_annot(
+      FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_HIGHLIGHT));
+  ASSERT_TRUE(highlight_annot);
+  EXPECT_FALSE(FPDFAnnot_RemoveInkList(highlight_annot.get()));
+
+  // Create a new ink annotation.
+  ScopedFPDFAnnotation ink_annot(
+      FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK));
+  ASSERT_TRUE(ink_annot);
+  CPDF_AnnotContext* context =
+      CPDFAnnotContextFromFPDFAnnotation(ink_annot.get());
+  ASSERT_TRUE(context);
+  const CPDF_Dictionary* annot_dict = context->GetAnnotDict();
+  ASSERT_TRUE(annot_dict);
+
+  static constexpr FS_POINTF kInkStroke[] = {{80.0f, 90.0f}, {81.0f, 91.0f},
+                                             {82.0f, 92.0f}, {83.0f, 93.0f},
+                                             {84.0f, 94.0f}, {85.0f, 95.0f}};
+  static constexpr size_t kPointCount = std::size(kInkStroke);
+
+  // InkStroke should get added to ink annotation. Also inklist should get
+  // created.
+  EXPECT_EQ(0,
+            FPDFAnnot_AddInkStroke(ink_annot.get(), kInkStroke, kPointCount));
+
+  RetainPtr<const CPDF_Array> inklist = annot_dict->GetArrayFor("InkList");
+  ASSERT_TRUE(inklist);
+  ASSERT_EQ(1u, inklist->size());
+  EXPECT_EQ(kPointCount * 2, inklist->GetArrayAt(0)->size());
+
+  // Remove inklist.
+  EXPECT_TRUE(FPDFAnnot_RemoveInkList(ink_annot.get()));
+  EXPECT_FALSE(annot_dict->KeyExist("InkList"));
+}
+
 TEST_F(FPDFAnnotEmbedderTest, BadParams) {
   ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   FPDF_PAGE page = LoadPage(0);
@@ -87,23 +403,20 @@
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_RenderMultilineMarkupAnnotWithoutAP \
-  DISABLED_RenderMultilineMarkupAnnotWithoutAP
-#else
-#define MAYBE_RenderMultilineMarkupAnnotWithoutAP \
-  RenderMultilineMarkupAnnotWithoutAP
-#endif
-TEST_F(FPDFAnnotEmbedderTest, MAYBE_RenderMultilineMarkupAnnotWithoutAP) {
-  static const char kMd5[] = "76512832d88017668d9acc7aacd13dae";
+TEST_F(FPDFAnnotEmbedderTest, RenderMultilineMarkupAnnotWithoutAP) {
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "ec1f4ccbd0aecfdea6d53893387a0101";
+    return "76512832d88017668d9acc7aacd13dae";
+  }();
+
   // Open a file with multiline markup annotations.
   ASSERT_TRUE(OpenDocument("annotation_markup_multiline_no_ap.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
   ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-  CompareBitmap(bitmap.get(), 595, 842, kMd5);
+  CompareBitmap(bitmap.get(), 595, 842, checksum);
 
   UnloadPage(page);
 }
@@ -192,13 +505,7 @@
   UnloadPageNoEvents(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_ExtractInkMultiple DISABLED_ExtractInkMultiple
-#else
-#define MAYBE_ExtractInkMultiple ExtractInkMultiple
-#endif
-TEST_F(FPDFAnnotEmbedderTest, MAYBE_ExtractInkMultiple) {
+TEST_F(FPDFAnnotEmbedderTest, ExtractInkMultiple) {
   // Open a file with three annotations and load its first page.
   ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
   FPDF_PAGE page = LoadPageNoEvents(0);
@@ -239,8 +546,18 @@
     EXPECT_EQ(681.535034f, rect.top);
   }
   {
+    const char* expected_hash = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+#if BUILDFLAG(IS_APPLE)
+        return "6e00cc75639c5314c8273072915d8f92";
+#else
+        return "1fb0dd8dd5f0b9bb8d076e48eb59296d";
+#endif
+      }
+      return "354002e1c4386d38fdde29ef8d61074a";
+    }();
     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-    CompareBitmap(bitmap.get(), 612, 792, "354002e1c4386d38fdde29ef8d61074a");
+    CompareBitmap(bitmap.get(), 612, 792, expected_hash);
   }
   UnloadPageNoEvents(page);
 }
@@ -342,14 +659,85 @@
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_AddAndSaveUnderlineAnnotation \
-  DISABLED_AddAndSaveUnderlineAnnotation
-#else
-#define MAYBE_AddAndSaveUnderlineAnnotation AddAndSaveUnderlineAnnotation
-#endif
-TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndSaveUnderlineAnnotation) {
+TEST_F(FPDFAnnotEmbedderTest, AddAndSaveLinkAnnotation) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 200, 200, pdfium::HelloWorldChecksum());
+  }
+  EXPECT_EQ(0, FPDFPage_GetAnnotCount(page));
+
+  constexpr char kUri[] = "https://pdfium.org/";
+
+  {
+    // Add a link annotation to the page and set its URI.
+    ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_LINK));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
+    EXPECT_EQ(FPDF_ANNOT_LINK, FPDFAnnot_GetSubtype(annot.get()));
+    EXPECT_TRUE(FPDFAnnot_SetURI(annot.get(), kUri));
+    VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri);
+
+    // Negative tests:
+    EXPECT_FALSE(FPDFAnnot_SetURI(nullptr, nullptr));
+    VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri);
+    EXPECT_FALSE(FPDFAnnot_SetURI(annot.get(), nullptr));
+    VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri);
+    EXPECT_FALSE(FPDFAnnot_SetURI(nullptr, kUri));
+    VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri);
+
+    // Position the link on top of "Hello, world!" without a border.
+    const FS_RECTF kRect = {19.0f, 48.0f, 85.0f, 60.0f};
+    EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &kRect));
+    EXPECT_TRUE(FPDFAnnot_SetBorder(annot.get(), /*horizontal_radius=*/0.0f,
+                                    /*vertical_radius=*/0.0f,
+                                    /*border_width=*/0.0f));
+
+    VerifyUriActionInLink(document(), FPDFLink_GetLinkAtPoint(page, 40.0, 50.0),
+                          kUri);
+  }
+
+  {
+    // Add an ink annotation to the page. Trying to add a link to it fails.
+    ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_INK));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
+    EXPECT_EQ(FPDF_ANNOT_INK, FPDFAnnot_GetSubtype(annot.get()));
+    EXPECT_FALSE(FPDFAnnot_SetURI(annot.get(), kUri));
+  }
+
+  // Remove the ink annotation added above for negative testing.
+  EXPECT_TRUE(FPDFPage_RemoveAnnot(page, 1));
+  EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
+
+  // Save the document, closing the page.
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Reopen the document and make sure it still renders the same. Since the link
+  // does not have a border, it does not affect the rendering.
+  ASSERT_TRUE(OpenSavedDocument());
+  page = LoadSavedPage(0);
+  ASSERT_TRUE(page);
+  VerifySavedRendering(page, 200, 200, pdfium::HelloWorldChecksum());
+  EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(FPDF_ANNOT_LINK, FPDFAnnot_GetSubtype(annot.get()));
+    VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri);
+    VerifyUriActionInLink(document(), FPDFLink_GetLinkAtPoint(page, 40.0, 50.0),
+                          kUri);
+  }
+
+  CloseSavedPage(page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFAnnotEmbedderTest, AddAndSaveUnderlineAnnotation) {
   // Open a file with one annotation and load its first page.
   ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
   FPDF_PAGE page = LoadPage(0);
@@ -379,16 +767,26 @@
     ASSERT_TRUE(FPDFAnnot_AppendAttachmentPoints(annot.get(), &quadpoints));
   }
 
-  // Save the document, closing the page and document.
+  // Save the document and close the page.
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   UnloadPage(page);
 
   // Open the saved document.
-  static const char kMd5[] = "dba153419f67b7c0c0e3d22d3e8910d5";
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+#if BUILDFLAG(IS_APPLE)
+      return "24994ad69aa612a66d183eaf9a92aa06";
+#else
+      return "798fa41303381c9ba6d99092f5cd4d2b";
+#endif
+    }
+    return "dba153419f67b7c0c0e3d22d3e8910d5";
+  }();
 
   ASSERT_TRUE(OpenSavedDocument());
   page = LoadSavedPage(0);
-  VerifySavedRendering(page, 612, 792, kMd5);
+  ASSERT_TRUE(page);
+  VerifySavedRendering(page, 612, 792, checksum);
 
   // Check that the saved document has 2 annotations on the first page
   EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
@@ -496,29 +894,34 @@
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_ModifyRectQuadpointsWithAP DISABLED_ModifyRectQuadpointsWithAP
+TEST_F(FPDFAnnotEmbedderTest, ModifyRectQuadpointsWithAP) {
+  const char* md5_original = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "2a9d1df839d5ec81a49f982347d9656c";
+#if BUILDFLAG(IS_APPLE)
+    return "fc59468d154f397fd298c69f47ef565a";
 #else
-#define MAYBE_ModifyRectQuadpointsWithAP ModifyRectQuadpointsWithAP
+    return "0e27376094f11490f74c65f3dc3a42c5";
 #endif
-TEST_F(FPDFAnnotEmbedderTest, MAYBE_ModifyRectQuadpointsWithAP) {
-#if defined(OS_MACOSX)
-  static const char kMd5Original[] = "63af8432fab95a67cdebb7cd0e514941";
-  static const char kMd5ModifiedHighlight[] =
-      "aec26075011349dec9bace891856b5f2";
-  static const char kMd5ModifiedSquare[] = "057f57a32be95975775e5ec513fdcb56";
-#elif defined(OS_WIN)
-  static const char kMd5Original[] = "0e27376094f11490f74c65f3dc3a42c5";
-  static const char kMd5ModifiedHighlight[] =
-      "66f3caef3a7d488a4fa1ad37fc06310e";
-  static const char kMd5ModifiedSquare[] = "a456dad0bc6801ee2d6408a4394af563";
+  }();
+  const char* md5_modified_highlight = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "0fb1653db0e8e8f7ce5d726bb0074bb5";
+#if BUILDFLAG(IS_APPLE)
+    return "e64bf648f6e9354d1f3eedb47a2c9498";
 #else
-  static const char kMd5Original[] = "0e27376094f11490f74c65f3dc3a42c5";
-  static const char kMd5ModifiedHighlight[] =
-      "66f3caef3a7d488a4fa1ad37fc06310e";
-  static const char kMd5ModifiedSquare[] = "a456dad0bc6801ee2d6408a4394af563";
+    return "66f3caef3a7d488a4fa1ad37fc06310e";
 #endif
+  }();
+  const char* md5_modified_square = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "879c77a2cb9f79ba65ffe0bbdd720ce3";
+#if BUILDFLAG(IS_APPLE)
+    return "a66591662c8e7ad3c6059952e234bebf";
+#else
+    return "a456dad0bc6801ee2d6408a4394af563";
+#endif
+  }();
 
   // Open a file with four annotations and load its first page.
   ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
@@ -529,7 +932,7 @@
   // Check that the original file renders correctly.
   {
     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-    CompareBitmap(bitmap.get(), 612, 792, kMd5Original);
+    CompareBitmap(bitmap.get(), 612, 792, md5_original);
   }
 
   FS_RECTF rect;
@@ -569,7 +972,7 @@
     // Check that updating quadpoints does not change the annotation's position.
     {
       ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-      CompareBitmap(bitmap.get(), 612, 792, kMd5Original);
+      CompareBitmap(bitmap.get(), 612, 792, md5_original);
     }
 
     // Verify its annotation rectangle.
@@ -590,7 +993,7 @@
   // Check that updating the rectangle changes the annotation's position.
   {
     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-    CompareBitmap(bitmap.get(), 612, 792, kMd5ModifiedHighlight);
+    CompareBitmap(bitmap.get(), 612, 792, md5_modified_highlight);
   }
 
   {
@@ -610,7 +1013,7 @@
     // Check that updating the rectangle changes the square annotation's
     // position.
     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-    CompareBitmap(bitmap.get(), 612, 792, kMd5ModifiedSquare);
+    CompareBitmap(bitmap.get(), 612, 792, md5_modified_square);
   }
 
   UnloadPage(page);
@@ -673,7 +1076,7 @@
   EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
   EXPECT_FALSE(FPDFPage_GetAnnot(page, 2));
 
-  // Save the document, closing the page and document.
+  // Save the document and close the page.
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   UnloadPageNoEvents(page);
 
@@ -710,29 +1113,34 @@
   FPDF_CloseDocument(new_doc);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_AddAndModifyPath DISABLED_AddAndModifyPath
+TEST_F(FPDFAnnotEmbedderTest, AddAndModifyPath) {
+  const char* md5_modified_path = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "fb4d5fac05f7eb5d84a4100898c11197";
+#if BUILDFLAG(IS_APPLE)
+    return "34614087e04b729b7b8c37739dcf9af9";
 #else
-#define MAYBE_AddAndModifyPath AddAndModifyPath
+    return "31a94d22460171cd83169daf6a6956ee";
 #endif
-TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyPath) {
-#if defined(OS_MACOSX)
-  static const char kMd5Original[] = "c35408717759562d1f8bf33d317483d2";
-  static const char kMd5ModifiedPath[] = "9059723a045e17478753d2f0eb33bc03";
-  static const char kMd5TwoPaths[] = "7eed0cfba780f1d4dd8068f717d3a6bf";
-  static const char kMd5NewAnnot[] = "1de8212d43b7066a6df042095c2aca61";
-#elif defined(OS_WIN)
-  static const char kMd5Original[] = "6aa001a77ec05d0f1b0d1d22e28744d4";
-  static const char kMd5ModifiedPath[] = "a7a8d675a6ddbcbdfecee65a33ba19e1";
-  static const char kMd5TwoPaths[] = "7c0bdd4552329704c47a7cce47edbbd6";
-  static const char kMd5NewAnnot[] = "3c48d492b4f62941fed0fb62f729f31e";
+  }();
+  const char* md5_two_paths = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "fcf3e79b2a91d1294b9bbccff727d3c2";
+#if BUILDFLAG(IS_APPLE)
+    return "6cdaf6b3e5145f435d8ccae6db5cf9af";
 #else
-  static const char kMd5Original[] = "b42cef463483e668eaf4055a65e4f1f5";
-  static const char kMd5ModifiedPath[] = "6ff77d6d1fec4ea571fabe0c7a19b517";
-  static const char kMd5TwoPaths[] = "ca37ad549e74ac5b359a055708f3e7b6";
-  static const char kMd5NewAnnot[] = "0d7a0e33fbf41ff7fa5d732ab2c5edff";
+    return "ed49fefef45f14121f8150cde10006c4";
 #endif
+  }();
+  const char* md5_new_annot = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "7db6321c8ffe502f4e60622aa16d5417";
+#if BUILDFLAG(IS_APPLE)
+    return "55dab4f158fdc284e439b88c4306373c";
+#else
+    return "cc08493b1f079803930388ecc703be9d";
+#endif
+  }();
 
   // Open a file with two annotations and load its first page.
   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
@@ -743,7 +1151,7 @@
   // Check that the page renders correctly.
   {
     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-    CompareBitmap(bitmap.get(), 595, 842, kMd5Original);
+    CompareBitmap(bitmap.get(), 595, 842, AnnotationStampWithApChecksum());
   }
 
   {
@@ -767,7 +1175,7 @@
     // Check that the page with the modified annotation renders correctly.
     {
       ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-      CompareBitmap(bitmap.get(), 595, 842, kMd5ModifiedPath);
+      CompareBitmap(bitmap.get(), 595, 842, md5_modified_path);
     }
 
     // Add a second path object to the same annotation.
@@ -786,7 +1194,7 @@
     // Check that the page with an annotation with two paths renders correctly.
     {
       ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-      CompareBitmap(bitmap.get(), 595, 842, kMd5TwoPaths);
+      CompareBitmap(bitmap.get(), 595, 842, md5_two_paths);
     }
 
     // Delete the newly added path object.
@@ -798,7 +1206,7 @@
   // Check that the page renders the same as before.
   {
     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-    CompareBitmap(bitmap.get(), 595, 842, kMd5ModifiedPath);
+    CompareBitmap(bitmap.get(), 595, 842, md5_modified_path);
   }
 
   FS_RECTF rect;
@@ -834,14 +1242,15 @@
     EXPECT_EQ(rect.top, new_rect.top);
   }
 
-  // Save the document, closing the page and document.
+  // Save the document and close the page.
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   UnloadPage(page);
 
   // Open the saved document.
   ASSERT_TRUE(OpenSavedDocument());
   page = LoadSavedPage(0);
-  VerifySavedRendering(page, 595, 842, kMd5NewAnnot);
+  ASSERT_TRUE(page);
+  VerifySavedRendering(page, 595, 842, md5_new_annot);
 
   // Check that the document has a correct count of annotations and objects.
   EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
@@ -883,8 +1292,15 @@
 
     // Check that the original flag values are as expected.
     int flags = FPDFAnnot_GetFlags(annot.get());
+    EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_INVISIBLE);
     EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_HIDDEN);
     EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_PRINT);
+    EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_NOZOOM);
+    EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_NOROTATE);
+    EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_NOVIEW);
+    EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_READONLY);
+    EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_LOCKED);
+    EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_TOGGLENOVIEW);
 
     // Set the HIDDEN flag.
     flags |= FPDF_ANNOT_FLAG_HIDDEN;
@@ -896,7 +1312,7 @@
     // Check that the page renders correctly without rendering the annotation.
     {
       ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-      CompareBitmap(bitmap.get(), 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3");
+      CompareBitmap(bitmap.get(), 612, 792, pdfium::kBlankPage612By792Checksum);
     }
 
     // Unset the HIDDEN flag.
@@ -918,26 +1334,25 @@
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_AddAndModifyImage DISABLED_AddAndModifyImage
+TEST_F(FPDFAnnotEmbedderTest, AddAndModifyImage) {
+  const char* md5_new_image = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "476596330c0e7daa31f115005c1d36eb";
+#if BUILDFLAG(IS_APPLE)
+    return "17ac49518eabbb6a7632a547269c40a3";
 #else
-#define MAYBE_AddAndModifyImage AddAndModifyImage
+    return "e79446398d4508bc2cb47e6cf2a677ed";
 #endif
-TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyImage) {
-#if defined(OS_MACOSX)
-  static const char kMd5Original[] = "c35408717759562d1f8bf33d317483d2";
-  static const char kMd5NewImage[] = "ff012f5697436dfcaec25b32d1333596";
-  static const char kMd5ModifiedImage[] = "86cf8cb2755a7a2046a543e66d9c1e61";
-#elif defined(OS_WIN)
-  static const char kMd5Original[] = "6aa001a77ec05d0f1b0d1d22e28744d4";
-  static const char kMd5NewImage[] = "3d77d06a971bcb9fb54db082f1082c8b";
-  static const char kMd5ModifiedImage[] = "dc4f4afc26c345418330d31c065020e1";
+  }();
+  const char* md5_modified_image = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "0047c3e7ea7658e1a963fc339f1c587d";
+#if BUILDFLAG(IS_APPLE)
+    return "ce68959f74242d588af7fb82be5ba0ab";
 #else
-  static const char kMd5Original[] = "b42cef463483e668eaf4055a65e4f1f5";
-  static const char kMd5NewImage[] = "528e6243dc29d54f36b61e0d3287d935";
-  static const char kMd5ModifiedImage[] = "6d9e59f3e57a1ff82fb258356b7eb731";
+    return "425646a517a23104b9ef22881a19b3e2";
 #endif
+  }();
 
   // Open a file with two annotations and load its first page.
   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
@@ -948,7 +1363,7 @@
   // Check that the page renders correctly.
   {
     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-    CompareBitmap(bitmap.get(), 595, 842, kMd5Original);
+    CompareBitmap(bitmap.get(), 595, 842, AnnotationStampWithApChecksum());
   }
 
   constexpr int kBitmapSize = 200;
@@ -973,8 +1388,9 @@
     EXPECT_EQ(kBitmapSize, FPDFBitmap_GetHeight(image_bitmap));
     FPDF_PAGEOBJECT image_object = FPDFPageObj_NewImageObj(document());
     ASSERT_TRUE(FPDFImageObj_SetBitmap(&page, 0, image_object, image_bitmap));
-    ASSERT_TRUE(FPDFImageObj_SetMatrix(image_object, kBitmapSize, 0, 0,
-                                       kBitmapSize, 0, 0));
+    static constexpr FS_MATRIX kBitmapScaleMatrix{kBitmapSize, 0, 0,
+                                                  kBitmapSize, 0, 0};
+    ASSERT_TRUE(FPDFPageObj_SetMatrix(image_object, &kBitmapScaleMatrix));
     FPDFPageObj_Transform(image_object, 1, 0, 0, 1, 200, 600);
     EXPECT_TRUE(FPDFAnnot_AppendObject(annot.get(), image_object));
   }
@@ -982,7 +1398,7 @@
   // Check that the page renders correctly with the new image object.
   {
     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-    CompareBitmap(bitmap.get(), 595, 842, kMd5NewImage);
+    CompareBitmap(bitmap.get(), 595, 842, md5_new_image);
   }
 
   {
@@ -1000,35 +1416,40 @@
     EXPECT_TRUE(FPDFAnnot_UpdateObject(annot.get(), image_object));
   }
 
-  // Save the document, closing the page and document.
+  // Save the document and close the page.
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   UnloadPage(page);
   FPDFBitmap_Destroy(image_bitmap);
 
   // Test that the saved document renders the modified image object correctly.
-  VerifySavedDocument(595, 842, kMd5ModifiedImage);
+  VerifySavedDocument(595, 842, md5_modified_image);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_AddAndModifyText DISABLED_AddAndModifyText
+TEST_F(FPDFAnnotEmbedderTest, AddAndModifyText) {
+  const char* md5_new_text = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "1e7f98c18775d6e0f4f454747b77cc1a";
+    }
+#if BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_ARM64)
+    return "0c3448974a4e8da2395da917935e5de1";
+#elif BUILDFLAG(IS_APPLE) && !defined(ARCH_CPU_ARM64)
+    return "5d449d36926c9f212c6cdb6c276d18cc";
 #else
-#define MAYBE_AddAndModifyText AddAndModifyText
+    return "a9532f555aca2fd099e2107fa40b61e6";
 #endif
-TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyText) {
-#if defined(OS_MACOSX)
-  static const char kMd5Original[] = "c35408717759562d1f8bf33d317483d2";
-  static const char kMd5NewText[] = "60031c1b0330cf1e1575f7d46687d429";
-  static const char kMd5ModifiedText[] = "79f5cfb0b07caaf936f65f6a7a57ce77";
-#elif defined(OS_WIN)
-  static const char kMd5Original[] = "6aa001a77ec05d0f1b0d1d22e28744d4";
-  static const char kMd5NewText[] = "204cc01749a70b8afc246a4ca33c7eb6";
-  static const char kMd5ModifiedText[] = "641261a45e8dfd68c89b80bfd237660d";
+  }();
+  const char* md5_modified_text = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "37e35705946806f8f98c51e4e25647a2";
+    }
+#if BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_ARM64)
+    return "9cf1c024a9d2d356bcdd14cb71a32324";
+#elif BUILDFLAG(IS_APPLE) && !defined(ARCH_CPU_ARM64)
+    return "8c992808db99dbe3d74006358a671f05";
 #else
-  static const char kMd5Original[] = "b42cef463483e668eaf4055a65e4f1f5";
-  static const char kMd5NewText[] = "00197ad6206f763febad5719e5935306";
-  static const char kMd5ModifiedText[] = "85853bc0aaa5a4e3af04e58b9cbfff23";
+    return "03cae68322d6a6ba120e738ab325408c";
 #endif
+  }();
 
   // Open a file with two annotations and load its first page.
   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
@@ -1039,7 +1460,7 @@
   // Check that the page renders correctly.
   {
     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-    CompareBitmap(bitmap.get(), 595, 842, kMd5Original);
+    CompareBitmap(bitmap.get(), 595, 842, AnnotationStampWithApChecksum());
   }
 
   {
@@ -1068,7 +1489,7 @@
   // Check that the page renders correctly with the new text object.
   {
     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-    CompareBitmap(bitmap.get(), 595, 842, kMd5NewText);
+    CompareBitmap(bitmap.get(), 595, 842, md5_new_text);
   }
 
   {
@@ -1088,26 +1509,20 @@
   // Check that the page renders correctly with the modified text object.
   {
     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-    CompareBitmap(bitmap.get(), 595, 842, kMd5ModifiedText);
+    CompareBitmap(bitmap.get(), 595, 842, md5_modified_text);
   }
 
   // Remove the new annotation, and check that the page renders as before.
   EXPECT_TRUE(FPDFPage_RemoveAnnot(page, 2));
   {
     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-    CompareBitmap(bitmap.get(), 595, 842, kMd5Original);
+    CompareBitmap(bitmap.get(), 595, 842, AnnotationStampWithApChecksum());
   }
 
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_GetSetStringValue DISABLED_GetSetStringValue
-#else
-#define MAYBE_GetSetStringValue GetSetStringValue
-#endif
-TEST_F(FPDFAnnotEmbedderTest, MAYBE_GetSetStringValue) {
+TEST_F(FPDFAnnotEmbedderTest, GetSetStringValue) {
   // Open a file with four annotations and load its first page.
   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
   FPDF_PAGE page = LoadPage(0);
@@ -1158,22 +1573,25 @@
                                          text.get()));
   }
 
-  // Save the document, closing the page and document.
+  // Save the document and close the page.
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   UnloadPage(page);
 
-#if defined(OS_MACOSX)
-  static const char kMd5[] = "4d64e61c9c0f8c60ab3cc3234bb73b1c";
-#elif defined(OS_WIN)
-  static const char kMd5[] = "20b612ebd46babcb44c48c903e2c5a48";
+  const char* md5 = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "a95a65d109eda5671c793ff5f7d2a2df";
+#if BUILDFLAG(IS_APPLE)
+    return "52e93c54796f7f7167edf64e81d12bd7";
 #else
-  static const char kMd5[] = "1d7bea2042c6fea0558ff2aef05811b5";
+    return "5143f9a98beb7b00ff40b89110a1089f";
 #endif
+  }();
 
   // Open the saved annotation.
   ASSERT_TRUE(OpenSavedDocument());
   page = LoadSavedPage(0);
-  VerifySavedRendering(page, 595, 842, kMd5);
+  ASSERT_TRUE(page);
+  VerifySavedRendering(page, 595, 842, md5);
   {
     ScopedFPDFAnnotation new_annot(FPDFPage_GetAnnot(page, 0));
 
@@ -1269,8 +1687,8 @@
               FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
                               buf.data(), normal_length_bytes));
     EXPECT_EQ(kMd5NormalAP,
-              GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()),
-                                normal_length_bytes));
+              GenerateMD5Base16({reinterpret_cast<uint8_t*>(buf.data()),
+                                 normal_length_bytes}));
 
     // Check that the string value of an AP is returned through a buffer that is
     // larger than necessary.
@@ -1279,8 +1697,8 @@
               FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
                               buf.data(), normal_length_bytes + 2));
     EXPECT_EQ(kMd5NormalAP,
-              GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()),
-                                normal_length_bytes));
+              GenerateMD5Base16({reinterpret_cast<uint8_t*>(buf.data()),
+                                 normal_length_bytes}));
 
     // Check that getting an AP for a mode that does not have an AP returns an
     // empty string.
@@ -1322,8 +1740,8 @@
               FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
                               buf.data(), normal_length_bytes));
     EXPECT_EQ(kMd5NormalAP,
-              GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()),
-                                normal_length_bytes));
+              GenerateMD5Base16({reinterpret_cast<uint8_t*>(buf.data()),
+                                 normal_length_bytes}));
   }
 
   // Save the modified document, then reopen it.
@@ -1332,6 +1750,7 @@
 
   ASSERT_TRUE(OpenSavedDocument());
   page = LoadSavedPage(0);
+  ASSERT_TRUE(page);
   {
     ScopedFPDFAnnotation new_annot(FPDFPage_GetAnnot(page, 0));
 
@@ -1481,6 +1900,9 @@
     // Check that the flag values are as expected.
     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
     EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_TEXT_MULTILINE);
     EXPECT_FALSE(flags & FPDF_FORMFLAG_TEXT_PASSWORD);
   }
 
@@ -1492,6 +1914,9 @@
     // Check that the flag values are as expected.
     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
     EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_TEXT_MULTILINE);
     EXPECT_FALSE(flags & FPDF_FORMFLAG_TEXT_PASSWORD);
   }
 
@@ -1503,6 +1928,9 @@
     // Check that the flag values are as expected.
     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
     EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_TEXT_MULTILINE);
     EXPECT_TRUE(flags & FPDF_FORMFLAG_TEXT_PASSWORD);
   }
 
@@ -1523,8 +1951,11 @@
     // Check that the flag values are as expected.
     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
     EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
     EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
     EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT);
   }
 
   {
@@ -1535,8 +1966,11 @@
     // Check that the flag values are as expected.
     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
     EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
     EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
     EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT);
   }
 
   {
@@ -1547,8 +1981,11 @@
     // Check that the flag values are as expected.
     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
     EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
     EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
     EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT);
   }
 
   UnloadPage(page);
@@ -1556,7 +1993,7 @@
 
 TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotNull) {
   // Open file with form text fields.
-  EXPECT_TRUE(OpenDocument("text_form.pdf"));
+  ASSERT_TRUE(OpenDocument("text_form.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1584,7 +2021,7 @@
 
 TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotAndCheckFlagsTextField) {
   // Open file with form text fields.
-  EXPECT_TRUE(OpenDocument("text_form_multiple.pdf"));
+  ASSERT_TRUE(OpenDocument("text_form_multiple.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1597,6 +2034,8 @@
 
     // Check that interactive form annotation flag values are as expected.
     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
     EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
   }
 
@@ -1610,6 +2049,8 @@
     // Check that interactive form annotation flag values are as expected.
     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
     EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
   }
 
   UnloadPage(page);
@@ -1617,7 +2058,7 @@
 
 TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotAndCheckFlagsComboBox) {
   // Open file with form comboboxes.
-  EXPECT_TRUE(OpenDocument("combobox_form.pdf"));
+  ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1631,8 +2072,11 @@
     // Check that interactive form annotation flag values are as expected.
     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
     EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
     EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
     EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT);
   }
 
   {
@@ -1645,8 +2089,11 @@
     // Check that interactive form annotation flag values are as expected.
     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
     EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
     EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
     EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT);
   }
 
   {
@@ -1659,22 +2106,23 @@
     // Check that interactive form annotation flag values are as expected.
     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
     EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
     EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
     EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT);
   }
 
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_BUG_1206 DISABLED_BUG_1206
-#else
-#define MAYBE_BUG_1206 BUG_1206
-#endif
-TEST_F(FPDFAnnotEmbedderTest, MAYBE_BUG_1206) {
-  static constexpr size_t kExpectedSize = 1609;
-  static const char kExpectedBitmap[] = "0d9fc05c6762fd788bd23fd87a4967bc";
+TEST_F(FPDFAnnotEmbedderTest, BUG_1206) {
+  const char* expected_bitmap = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "a1ea1ceebb26922fae576cb79ce63af0";
+    return "0d9fc05c6762fd788bd23fd87a4967bc";
+  }();
+  static constexpr size_t kExpectedSize = 1590;
 
   ASSERT_TRUE(OpenDocument("bug_1206.pdf"));
 
@@ -1687,7 +2135,7 @@
 
   for (size_t i = 0; i < 10; ++i) {
     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-    CompareBitmap(bitmap.get(), 612, 792, kExpectedBitmap);
+    CompareBitmap(bitmap.get(), 612, 792, expected_bitmap);
 
     ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
     // TODO(https://crbug.com/pdfium/1206): This is wrong. The size should be
@@ -1995,6 +2443,147 @@
   UnloadPage(page);
 }
 
+TEST_F(FPDFAnnotEmbedderTest, IsOptionSelectedCombobox) {
+  // Open a file with combobox widget annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // Checks for Combobox with no Values (/V) or Selected Indices (/I) objects.
+    int count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
+    ASSERT_EQ(3, count);
+    for (int i = 0; i < count; i++) {
+      EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
+    }
+
+    annot.reset(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    // Checks for Combobox with Values (/V) object which is just a string.
+    count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
+    ASSERT_EQ(26, count);
+    for (int i = 0; i < count; i++) {
+      EXPECT_EQ(i == 1,
+                FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
+    }
+
+    // Checks for index outside bound i.e. (index >= CountOption()).
+    EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(),
+                                            /*index=*/26));
+    // Checks for negetive index.
+    EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(),
+                                            /*index=*/-1));
+
+    // Checks for bad form handle/annot.
+    EXPECT_FALSE(FPDFAnnot_IsOptionSelected(nullptr, nullptr, /*index=*/0));
+    EXPECT_FALSE(
+        FPDFAnnot_IsOptionSelected(form_handle(), nullptr, /*index=*/0));
+    EXPECT_FALSE(FPDFAnnot_IsOptionSelected(nullptr, annot.get(), /*index=*/0));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, IsOptionSelectedListbox) {
+  // Open a file with listbox widget annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("listbox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // Checks for Listbox with no Values (/V) or Selected Indices (/I) objects.
+    int count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
+    ASSERT_EQ(3, count);
+    for (int i = 0; i < count; i++) {
+      EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
+    }
+
+    annot.reset(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    // Checks for Listbox with Values (/V) object which is just a string.
+    count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
+    ASSERT_EQ(26, count);
+    for (int i = 0; i < count; i++) {
+      EXPECT_EQ(i == 1,
+                FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
+    }
+
+    annot.reset(FPDFPage_GetAnnot(page, 3));
+    ASSERT_TRUE(annot);
+
+    // Checks for Listbox with only Selected indices (/I) object which is an
+    // array with multiple objects.
+    count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
+    ASSERT_EQ(5, count);
+    for (int i = 0; i < count; i++) {
+      bool expected = (i == 1 || i == 3);
+      EXPECT_EQ(expected,
+                FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
+    }
+
+    annot.reset(FPDFPage_GetAnnot(page, 4));
+    ASSERT_TRUE(annot);
+
+    // Checks for Listbox with Values (/V) object which is an array with
+    // multiple objects.
+    count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
+    ASSERT_EQ(5, count);
+    for (int i = 0; i < count; i++) {
+      bool expected = (i == 2 || i == 4);
+      EXPECT_EQ(expected,
+                FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
+    }
+
+    annot.reset(FPDFPage_GetAnnot(page, 5));
+    ASSERT_TRUE(annot);
+
+    // Checks for Listbox with both Values (/V) and Selected Indices (/I)
+    // objects conflict with different lengths.
+    count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
+    ASSERT_EQ(5, count);
+    for (int i = 0; i < count; i++) {
+      bool expected = (i == 0 || i == 2);
+      EXPECT_EQ(expected,
+                FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
+    }
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, IsOptionSelectedInvalidAnnotations) {
+  // Open a file with multiple form field annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("multiple_form_types.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // Checks for link annotation.
+    EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(),
+                                            /*index=*/0));
+
+    annot.reset(FPDFPage_GetAnnot(page, 3));
+    ASSERT_TRUE(annot);
+
+    // Checks for text field annotation.
+    EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(),
+                                            /*index=*/0));
+  }
+
+  UnloadPage(page);
+}
+
 TEST_F(FPDFAnnotEmbedderTest, GetFontSizeCombobox) {
   // Open a file with combobox annotations and load its first page.
   ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
@@ -2238,34 +2827,30 @@
   UnloadPage(page);
 }
 
-TEST_F(FPDFAnnotEmbedderTest, GetFormFieldTypeTextField) {
-  ASSERT_TRUE(OpenDocument("text_form.pdf"));
+TEST_F(FPDFAnnotEmbedderTest, GetFormFieldType) {
+  ASSERT_TRUE(OpenDocument("multiple_form_types.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
+  EXPECT_EQ(-1, FPDFAnnot_GetFormFieldType(form_handle(), nullptr));
+
   {
-    EXPECT_EQ(-1, FPDFAnnot_GetFormFieldType(form_handle(), nullptr));
-
-    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
     ASSERT_TRUE(annot);
-
     EXPECT_EQ(-1, FPDFAnnot_GetFormFieldType(nullptr, annot.get()));
-
-    EXPECT_EQ(FPDF_FORMFIELD_TEXTFIELD,
-              FPDFAnnot_GetFormFieldType(form_handle(), annot.get()));
   }
-  UnloadPage(page);
-}
 
-TEST_F(FPDFAnnotEmbedderTest, GetFormFieldTypeComboBox) {
-  ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  ASSERT_TRUE(page);
+  constexpr int kExpectedAnnotTypes[] = {-1,
+                                         FPDF_FORMFIELD_COMBOBOX,
+                                         FPDF_FORMFIELD_LISTBOX,
+                                         FPDF_FORMFIELD_TEXTFIELD,
+                                         FPDF_FORMFIELD_CHECKBOX,
+                                         FPDF_FORMFIELD_RADIOBUTTON};
 
-  {
-    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+  for (size_t i = 0; i < std::size(kExpectedAnnotTypes); ++i) {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, i));
     ASSERT_TRUE(annot);
-    EXPECT_EQ(FPDF_FORMFIELD_COMBOBOX,
+    EXPECT_EQ(kExpectedAnnotTypes[i],
               FPDFAnnot_GetFormFieldType(form_handle(), annot.get()));
   }
   UnloadPage(page);
@@ -2385,3 +2970,750 @@
   }
   UnloadPage(page);
 }
+
+TEST_F(FPDFAnnotEmbedderTest, FocusableAnnotSubtypes) {
+  ASSERT_TRUE(OpenDocument("annots.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Verify widgets are by default focusable.
+  const FPDF_ANNOTATION_SUBTYPE kDefaultSubtypes[] = {FPDF_ANNOT_WIDGET};
+  VerifyFocusableAnnotSubtypes(form_handle(), kDefaultSubtypes);
+
+  // Expected annot subtypes for page 0 of annots.pdf.
+  const FPDF_ANNOTATION_SUBTYPE kExpectedAnnotSubtypes[] = {
+      FPDF_ANNOT_LINK,  FPDF_ANNOT_LINK,      FPDF_ANNOT_LINK,
+      FPDF_ANNOT_LINK,  FPDF_ANNOT_HIGHLIGHT, FPDF_ANNOT_HIGHLIGHT,
+      FPDF_ANNOT_POPUP, FPDF_ANNOT_HIGHLIGHT, FPDF_ANNOT_WIDGET,
+  };
+
+  const FPDF_ANNOTATION_SUBTYPE kExpectedDefaultFocusableSubtypes[] = {
+      FPDF_ANNOT_WIDGET};
+  VerifyAnnotationSubtypesAndFocusability(form_handle(), page,
+                                          kExpectedAnnotSubtypes,
+                                          kExpectedDefaultFocusableSubtypes);
+
+  // Make no annotation type focusable using the preferred method.
+  ASSERT_TRUE(FPDFAnnot_SetFocusableSubtypes(form_handle(), nullptr, 0));
+  ASSERT_EQ(0, FPDFAnnot_GetFocusableSubtypesCount(form_handle()));
+
+  // Restore the focusable type count back to 1, then set it back to 0 using a
+  // different method.
+  SetAndVerifyFocusableAnnotSubtypes(form_handle(), kDefaultSubtypes);
+  ASSERT_TRUE(
+      FPDFAnnot_SetFocusableSubtypes(form_handle(), kDefaultSubtypes, 0));
+  ASSERT_EQ(0, FPDFAnnot_GetFocusableSubtypesCount(form_handle()));
+
+  VerifyAnnotationSubtypesAndFocusability(form_handle(), page,
+                                          kExpectedAnnotSubtypes, {});
+
+  // Now make links focusable.
+  const FPDF_ANNOTATION_SUBTYPE kLinkSubtypes[] = {FPDF_ANNOT_LINK};
+  SetAndVerifyFocusableAnnotSubtypes(form_handle(), kLinkSubtypes);
+
+  const FPDF_ANNOTATION_SUBTYPE kExpectedLinkocusableSubtypes[] = {
+      FPDF_ANNOT_LINK};
+  VerifyAnnotationSubtypesAndFocusability(form_handle(), page,
+                                          kExpectedAnnotSubtypes,
+                                          kExpectedLinkocusableSubtypes);
+
+  // Test invalid parameters.
+  EXPECT_FALSE(FPDFAnnot_SetFocusableSubtypes(nullptr, kDefaultSubtypes,
+                                              std::size(kDefaultSubtypes)));
+  EXPECT_FALSE(FPDFAnnot_SetFocusableSubtypes(form_handle(), nullptr,
+                                              std::size(kDefaultSubtypes)));
+  EXPECT_EQ(-1, FPDFAnnot_GetFocusableSubtypesCount(nullptr));
+
+  std::vector<FPDF_ANNOTATION_SUBTYPE> subtypes(1);
+  EXPECT_FALSE(FPDFAnnot_GetFocusableSubtypes(nullptr, subtypes.data(),
+                                              subtypes.size()));
+  EXPECT_FALSE(
+      FPDFAnnot_GetFocusableSubtypes(form_handle(), nullptr, subtypes.size()));
+  EXPECT_FALSE(
+      FPDFAnnot_GetFocusableSubtypes(form_handle(), subtypes.data(), 0));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, FocusableAnnotRendering) {
+  ASSERT_TRUE(OpenDocument("annots.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    const char* md5_sum = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+        return "c09b129c071ec1569deb003676b617b0";
+#if BUILDFLAG(IS_APPLE)
+      return "108a46c517c4eaace9982ee83e8e3296";
+#else
+      return "5550d8dcb4d1af1f50e8b4bcaef2ee60";
+#endif
+    }();
+    // Check the initial rendering.
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 612, 792, md5_sum);
+  }
+
+  // Make links and highlights focusable.
+  static constexpr FPDF_ANNOTATION_SUBTYPE kSubTypes[] = {FPDF_ANNOT_LINK,
+                                                          FPDF_ANNOT_HIGHLIGHT};
+  constexpr int kSubTypesCount = std::size(kSubTypes);
+  ASSERT_TRUE(
+      FPDFAnnot_SetFocusableSubtypes(form_handle(), kSubTypes, kSubTypesCount));
+  ASSERT_EQ(kSubTypesCount, FPDFAnnot_GetFocusableSubtypesCount(form_handle()));
+  std::vector<FPDF_ANNOTATION_SUBTYPE> subtypes(kSubTypesCount);
+  ASSERT_TRUE(FPDFAnnot_GetFocusableSubtypes(form_handle(), subtypes.data(),
+                                             subtypes.size()));
+  ASSERT_EQ(FPDF_ANNOT_LINK, subtypes[0]);
+  ASSERT_EQ(FPDF_ANNOT_HIGHLIGHT, subtypes[1]);
+
+  {
+    const char* md5_sum = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+        return "277f1b9e70031539d034d22bc6064838";
+#if BUILDFLAG(IS_APPLE)
+      return "eb3869335e7a219e1b5f25c1c6037b97";
+#else
+      return "805fe7bb751ac4ed2b82bb66efe6db40";
+#endif
+    }();
+    // Focus the first link and check the rendering.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(FPDF_ANNOT_LINK, FPDFAnnot_GetSubtype(annot.get()));
+    EXPECT_TRUE(FORM_SetFocusedAnnot(form_handle(), annot.get()));
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 612, 792, md5_sum);
+  }
+
+  {
+    const char* md5_sum = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+        return "d980005939cd4ae0a199d8600a0abdf3";
+#if BUILDFLAG(IS_APPLE)
+      return "d20b1978da2362d3942ea0fc6d230997";
+#else
+      return "c5c5dcb462af3ef5f43b298ec048feef";
+#endif
+    }();
+    // Focus the first highlight and check the rendering.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 4));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot.get()));
+    EXPECT_TRUE(FORM_SetFocusedAnnot(form_handle(), annot.get()));
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 612, 792, md5_sum);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetLinkFromAnnotation) {
+  ASSERT_TRUE(OpenDocument("annots.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  {
+    constexpr char kExpectedResult[] =
+        "https://cs.chromium.org/chromium/src/third_party/pdfium/public/"
+        "fpdf_text.h";
+
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 3));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(FPDF_ANNOT_LINK, FPDFAnnot_GetSubtype(annot.get()));
+    VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()),
+                          kExpectedResult);
+  }
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 4));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot.get()));
+    EXPECT_FALSE(FPDFAnnot_GetLink(annot.get()));
+  }
+
+  EXPECT_FALSE(FPDFAnnot_GetLink(nullptr));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormControlCountRadioButton) {
+  // Open a file with radio button widget annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("click_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Checks for bad annot.
+    EXPECT_EQ(-1,
+              FPDFAnnot_GetFormControlCount(form_handle(), /*annot=*/nullptr));
+
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 3));
+    ASSERT_TRUE(annot);
+
+    // Checks for bad form handle.
+    EXPECT_EQ(-1,
+              FPDFAnnot_GetFormControlCount(/*hHandle=*/nullptr, annot.get()));
+
+    EXPECT_EQ(3, FPDFAnnot_GetFormControlCount(form_handle(), annot.get()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormControlCountCheckBox) {
+  // Open a file with checkbox widget annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("click_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(1, FPDFAnnot_GetFormControlCount(form_handle(), annot.get()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormControlCountInvalidAnnotation) {
+  // Open a file with ink annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(-1, FPDFAnnot_GetFormControlCount(form_handle(), annot.get()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormControlIndexRadioButton) {
+  // Open a file with radio button widget annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("click_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Checks for bad annot.
+    EXPECT_EQ(-1,
+              FPDFAnnot_GetFormControlIndex(form_handle(), /*annot=*/nullptr));
+
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 3));
+    ASSERT_TRUE(annot);
+
+    // Checks for bad form handle.
+    EXPECT_EQ(-1,
+              FPDFAnnot_GetFormControlIndex(/*hHandle=*/nullptr, annot.get()));
+
+    EXPECT_EQ(1, FPDFAnnot_GetFormControlIndex(form_handle(), annot.get()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormControlIndexCheckBox) {
+  // Open a file with checkbox widget annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("click_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(0, FPDFAnnot_GetFormControlIndex(form_handle(), annot.get()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormControlIndexInvalidAnnotation) {
+  // Open a file with ink annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(-1, FPDFAnnot_GetFormControlIndex(form_handle(), annot.get()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormFieldExportValueRadioButton) {
+  // Open a file with radio button widget annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("click_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Checks for bad annot.
+    EXPECT_EQ(0u, FPDFAnnot_GetFormFieldExportValue(
+                      form_handle(), /*annot=*/nullptr,
+                      /*buffer=*/nullptr, /*buflen=*/0));
+
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 6));
+    ASSERT_TRUE(annot);
+
+    // Checks for bad form handle.
+    EXPECT_EQ(0u, FPDFAnnot_GetFormFieldExportValue(
+                      /*hHandle=*/nullptr, annot.get(),
+                      /*buffer=*/nullptr, /*buflen=*/0));
+
+    unsigned long length_bytes =
+        FPDFAnnot_GetFormFieldExportValue(form_handle(), annot.get(),
+                                          /*buffer=*/nullptr, /*buflen=*/0);
+    ASSERT_EQ(14u, length_bytes);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(14u, FPDFAnnot_GetFormFieldExportValue(form_handle(), annot.get(),
+                                                     buf.data(), length_bytes));
+    EXPECT_EQ(L"value2", GetPlatformWString(buf.data()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormFieldExportValueCheckBox) {
+  // Open a file with checkbox widget annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("click_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    unsigned long length_bytes =
+        FPDFAnnot_GetFormFieldExportValue(form_handle(), annot.get(),
+                                          /*buffer=*/nullptr, /*buflen=*/0);
+    ASSERT_EQ(8u, length_bytes);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(8u, FPDFAnnot_GetFormFieldExportValue(form_handle(), annot.get(),
+                                                    buf.data(), length_bytes));
+    EXPECT_EQ(L"Yes", GetPlatformWString(buf.data()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormFieldExportValueInvalidAnnotation) {
+  // Open a file with ink annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(0u, FPDFAnnot_GetFormFieldExportValue(form_handle(), annot.get(),
+                                                    /*buffer=*/nullptr,
+                                                    /*buflen=*/0));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, Redactannotation) {
+  ASSERT_TRUE(OpenDocument("redact_annot.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(FPDF_ANNOT_REDACT, FPDFAnnot_GetSubtype(annot.get()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, PolygonAnnotation) {
+  ASSERT_TRUE(OpenDocument("polygon_annot.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // FPDFAnnot_GetVertices() positive testing.
+    unsigned long size = FPDFAnnot_GetVertices(annot.get(), nullptr, 0);
+    const size_t kExpectedSize = 3;
+    ASSERT_EQ(kExpectedSize, size);
+    std::vector<FS_POINTF> vertices_buffer(size);
+    EXPECT_EQ(size,
+              FPDFAnnot_GetVertices(annot.get(), vertices_buffer.data(), size));
+    EXPECT_FLOAT_EQ(159.0f, vertices_buffer[0].x);
+    EXPECT_FLOAT_EQ(296.0f, vertices_buffer[0].y);
+    EXPECT_FLOAT_EQ(350.0f, vertices_buffer[1].x);
+    EXPECT_FLOAT_EQ(411.0f, vertices_buffer[1].y);
+    EXPECT_FLOAT_EQ(472.0f, vertices_buffer[2].x);
+    EXPECT_FLOAT_EQ(243.42f, vertices_buffer[2].y);
+
+    // FPDFAnnot_GetVertices() negative testing.
+    EXPECT_EQ(0U, FPDFAnnot_GetVertices(nullptr, nullptr, 0));
+
+    // vertices_buffer is not overwritten if it is too small.
+    vertices_buffer.resize(1);
+    vertices_buffer[0].x = 42;
+    vertices_buffer[0].y = 43;
+    size = FPDFAnnot_GetVertices(annot.get(), vertices_buffer.data(),
+                                 vertices_buffer.size());
+    EXPECT_EQ(kExpectedSize, size);
+    EXPECT_FLOAT_EQ(42, vertices_buffer[0].x);
+    EXPECT_FLOAT_EQ(43, vertices_buffer[0].y);
+  }
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    // This has an odd number of elements in the vertices array, ignore the last
+    // element.
+    unsigned long size = FPDFAnnot_GetVertices(annot.get(), nullptr, 0);
+    const size_t kExpectedSize = 3;
+    ASSERT_EQ(kExpectedSize, size);
+    std::vector<FS_POINTF> vertices_buffer(size);
+    EXPECT_EQ(size,
+              FPDFAnnot_GetVertices(annot.get(), vertices_buffer.data(), size));
+    EXPECT_FLOAT_EQ(259.0f, vertices_buffer[0].x);
+    EXPECT_FLOAT_EQ(396.0f, vertices_buffer[0].y);
+    EXPECT_FLOAT_EQ(450.0f, vertices_buffer[1].x);
+    EXPECT_FLOAT_EQ(511.0f, vertices_buffer[1].y);
+    EXPECT_FLOAT_EQ(572.0f, vertices_buffer[2].x);
+    EXPECT_FLOAT_EQ(343.0f, vertices_buffer[2].y);
+  }
+
+  {
+    // Wrong annotation type.
+    ScopedFPDFAnnotation ink_annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_INK));
+    EXPECT_EQ(0U, FPDFAnnot_GetVertices(ink_annot.get(), nullptr, 0));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, InkAnnotation) {
+  ASSERT_TRUE(OpenDocument("ink_annot.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // FPDFAnnot_GetInkListCount() and FPDFAnnot_GetInkListPath() positive
+    // testing.
+    unsigned long size = FPDFAnnot_GetInkListCount(annot.get());
+    const size_t kExpectedSize = 1;
+    ASSERT_EQ(kExpectedSize, size);
+    const unsigned long kPathIndex = 0;
+    unsigned long path_size =
+        FPDFAnnot_GetInkListPath(annot.get(), kPathIndex, nullptr, 0);
+    const size_t kExpectedPathSize = 3;
+    ASSERT_EQ(kExpectedPathSize, path_size);
+    std::vector<FS_POINTF> path_buffer(path_size);
+    EXPECT_EQ(path_size,
+              FPDFAnnot_GetInkListPath(annot.get(), kPathIndex,
+                                       path_buffer.data(), path_size));
+    EXPECT_FLOAT_EQ(159.0f, path_buffer[0].x);
+    EXPECT_FLOAT_EQ(296.0f, path_buffer[0].y);
+    EXPECT_FLOAT_EQ(350.0f, path_buffer[1].x);
+    EXPECT_FLOAT_EQ(411.0f, path_buffer[1].y);
+    EXPECT_FLOAT_EQ(472.0f, path_buffer[2].x);
+    EXPECT_FLOAT_EQ(243.42f, path_buffer[2].y);
+
+    // FPDFAnnot_GetInkListCount() and FPDFAnnot_GetInkListPath() negative
+    // testing.
+    EXPECT_EQ(0U, FPDFAnnot_GetInkListCount(nullptr));
+    EXPECT_EQ(0U, FPDFAnnot_GetInkListPath(nullptr, 0, nullptr, 0));
+
+    // out of bounds path_index.
+    EXPECT_EQ(0U, FPDFAnnot_GetInkListPath(nullptr, 42, nullptr, 0));
+
+    // path_buffer is not overwritten if it is too small.
+    path_buffer.resize(1);
+    path_buffer[0].x = 42;
+    path_buffer[0].y = 43;
+    path_size = FPDFAnnot_GetInkListPath(
+        annot.get(), kPathIndex, path_buffer.data(), path_buffer.size());
+    EXPECT_EQ(kExpectedPathSize, path_size);
+    EXPECT_FLOAT_EQ(42, path_buffer[0].x);
+    EXPECT_FLOAT_EQ(43, path_buffer[0].y);
+  }
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    // This has an odd number of elements in the path array, ignore the last
+    // element.
+    unsigned long size = FPDFAnnot_GetInkListCount(annot.get());
+    const size_t kExpectedSize = 1;
+    ASSERT_EQ(kExpectedSize, size);
+    const unsigned long kPathIndex = 0;
+    unsigned long path_size =
+        FPDFAnnot_GetInkListPath(annot.get(), kPathIndex, nullptr, 0);
+    const size_t kExpectedPathSize = 3;
+    ASSERT_EQ(kExpectedPathSize, path_size);
+    std::vector<FS_POINTF> path_buffer(path_size);
+    EXPECT_EQ(path_size,
+              FPDFAnnot_GetInkListPath(annot.get(), kPathIndex,
+                                       path_buffer.data(), path_size));
+    EXPECT_FLOAT_EQ(259.0f, path_buffer[0].x);
+    EXPECT_FLOAT_EQ(396.0f, path_buffer[0].y);
+    EXPECT_FLOAT_EQ(450.0f, path_buffer[1].x);
+    EXPECT_FLOAT_EQ(511.0f, path_buffer[1].y);
+    EXPECT_FLOAT_EQ(572.0f, path_buffer[2].x);
+    EXPECT_FLOAT_EQ(343.0f, path_buffer[2].y);
+  }
+
+  {
+    // Wrong annotation type.
+    ScopedFPDFAnnotation polygon_annot(
+        FPDFPage_CreateAnnot(page, FPDF_ANNOT_POLYGON));
+    EXPECT_EQ(0U, FPDFAnnot_GetInkListCount(polygon_annot.get()));
+    const unsigned long kPathIndex = 0;
+    EXPECT_EQ(0U, FPDFAnnot_GetInkListPath(polygon_annot.get(), kPathIndex,
+                                           nullptr, 0));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, LineAnnotation) {
+  ASSERT_TRUE(OpenDocument("line_annot.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // FPDFAnnot_GetVertices() positive testing.
+    FS_POINTF start;
+    FS_POINTF end;
+    ASSERT_TRUE(FPDFAnnot_GetLine(annot.get(), &start, &end));
+    EXPECT_FLOAT_EQ(159.0f, start.x);
+    EXPECT_FLOAT_EQ(296.0f, start.y);
+    EXPECT_FLOAT_EQ(472.0f, end.x);
+    EXPECT_FLOAT_EQ(243.42f, end.y);
+
+    // FPDFAnnot_GetVertices() negative testing.
+    EXPECT_FALSE(FPDFAnnot_GetLine(nullptr, nullptr, nullptr));
+    EXPECT_FALSE(FPDFAnnot_GetLine(annot.get(), nullptr, nullptr));
+  }
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    // Too few elements in the line array.
+    FS_POINTF start;
+    FS_POINTF end;
+    EXPECT_FALSE(FPDFAnnot_GetLine(annot.get(), &start, &end));
+  }
+
+  {
+    // Wrong annotation type.
+    ScopedFPDFAnnotation ink_annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_INK));
+    FS_POINTF start;
+    FS_POINTF end;
+    EXPECT_FALSE(FPDFAnnot_GetLine(ink_annot.get(), &start, &end));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, AnnotationBorder) {
+  ASSERT_TRUE(OpenDocument("line_annot.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // FPDFAnnot_GetBorder() positive testing.
+    float horizontal_radius;
+    float vertical_radius;
+    float border_width;
+    ASSERT_TRUE(FPDFAnnot_GetBorder(annot.get(), &horizontal_radius,
+                                    &vertical_radius, &border_width));
+    EXPECT_FLOAT_EQ(0.25f, horizontal_radius);
+    EXPECT_FLOAT_EQ(0.5f, vertical_radius);
+    EXPECT_FLOAT_EQ(2.0f, border_width);
+
+    // FPDFAnnot_GetBorder() negative testing.
+    EXPECT_FALSE(FPDFAnnot_GetBorder(nullptr, nullptr, nullptr, nullptr));
+    EXPECT_FALSE(FPDFAnnot_GetBorder(annot.get(), nullptr, nullptr, nullptr));
+  }
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    // Too few elements in the border array.
+    float horizontal_radius;
+    float vertical_radius;
+    float border_width;
+    EXPECT_FALSE(FPDFAnnot_GetBorder(annot.get(), &horizontal_radius,
+                                     &vertical_radius, &border_width));
+
+    // FPDFAnnot_SetBorder() positive testing.
+    EXPECT_TRUE(FPDFAnnot_SetBorder(annot.get(), /*horizontal_radius=*/2.0f,
+                                    /*vertical_radius=*/3.5f,
+                                    /*border_width=*/4.0f));
+
+    EXPECT_TRUE(FPDFAnnot_GetBorder(annot.get(), &horizontal_radius,
+                                    &vertical_radius, &border_width));
+    EXPECT_FLOAT_EQ(2.0f, horizontal_radius);
+    EXPECT_FLOAT_EQ(3.5f, vertical_radius);
+    EXPECT_FLOAT_EQ(4.0f, border_width);
+
+    // FPDFAnnot_SetBorder() negative testing.
+    EXPECT_FALSE(FPDFAnnot_SetBorder(nullptr, /*horizontal_radius=*/1.0f,
+                                     /*vertical_radius=*/2.5f,
+                                     /*border_width=*/3.0f));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, AnnotationJavaScript) {
+  ASSERT_TRUE(OpenDocument("annot_javascript.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // FPDFAnnot_GetFormAdditionalActionJavaScript() positive testing.
+    unsigned long length_bytes = FPDFAnnot_GetFormAdditionalActionJavaScript(
+        form_handle(), annot.get(), FPDF_ANNOT_AACTION_FORMAT, nullptr, 0);
+    ASSERT_EQ(62u, length_bytes);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(62u, FPDFAnnot_GetFormAdditionalActionJavaScript(
+                       form_handle(), annot.get(), FPDF_ANNOT_AACTION_FORMAT,
+                       buf.data(), length_bytes));
+    EXPECT_EQ(L"AFDate_FormatEx(\"yyyy-mm-dd\");",
+              GetPlatformWString(buf.data()));
+
+    // FPDFAnnot_GetFormAdditionalActionJavaScript() negative testing.
+    EXPECT_EQ(0u, FPDFAnnot_GetFormAdditionalActionJavaScript(
+                      form_handle(), nullptr, 0, nullptr, 0));
+    EXPECT_EQ(0u, FPDFAnnot_GetFormAdditionalActionJavaScript(
+                      nullptr, annot.get(), 0, nullptr, 0));
+    EXPECT_EQ(0u, FPDFAnnot_GetFormAdditionalActionJavaScript(
+                      form_handle(), annot.get(), 0, nullptr, 0));
+    EXPECT_EQ(2u, FPDFAnnot_GetFormAdditionalActionJavaScript(
+                      form_handle(), annot.get(), FPDF_ANNOT_AACTION_KEY_STROKE,
+                      nullptr, 0));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, FormFieldAlternateName) {
+  ASSERT_TRUE(OpenDocument("click_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(8, FPDFPage_GetAnnotCount(page));
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // FPDFAnnot_GetFormFieldAlternateName() positive testing.
+    unsigned long length_bytes = FPDFAnnot_GetFormFieldAlternateName(
+        form_handle(), annot.get(), nullptr, 0);
+    ASSERT_EQ(34u, length_bytes);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(34u, FPDFAnnot_GetFormFieldAlternateName(
+                       form_handle(), annot.get(), buf.data(), length_bytes));
+    EXPECT_EQ(L"readOnlyCheckbox", GetPlatformWString(buf.data()));
+
+    // FPDFAnnot_GetFormFieldAlternateName() negative testing.
+    EXPECT_EQ(0u, FPDFAnnot_GetFormFieldAlternateName(form_handle(), nullptr,
+                                                      nullptr, 0));
+    EXPECT_EQ(0u, FPDFAnnot_GetFormFieldAlternateName(nullptr, annot.get(),
+                                                      nullptr, 0));
+  }
+
+  UnloadPage(page);
+}
+
+// Due to https://crbug.com/pdfium/570, the AnnotationBorder test above cannot
+// actually render the line annotations inside line_annot.pdf. For now, use a
+// square annotation in annots.pdf for testing.
+TEST_F(FPDFAnnotEmbedderTest, AnnotationBorderRendering) {
+  ASSERT_TRUE(OpenDocument("annots.pdf"));
+  FPDF_PAGE page = LoadPage(1);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
+
+  const char* original_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "238dccc7df0ac61ac580c28e1109da3c";
+    }
+#if BUILDFLAG(IS_APPLE)
+    return "522a4a6b6c7eab5bf95ded1f21ea372e";
+#else
+    return "12127303aecd80c6288460f7c0d79f3f";
+#endif
+  }();
+  const char* modified_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "0f326acb3eb583125ca584d703ccb13b";
+    }
+#if BUILDFLAG(IS_APPLE)
+    return "6844019e07b83cc01723415f58218d06";
+#else
+    return "73d06ff4c665fe85029acef30240dcca";
+#endif
+  }();
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(FPDF_ANNOT_SQUARE, FPDFAnnot_GetSubtype(annot.get()));
+
+    {
+      ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+      CompareBitmap(bitmap.get(), 612, 792, original_checksum);
+    }
+
+    EXPECT_TRUE(FPDFAnnot_SetBorder(annot.get(), /*horizontal_radius=*/2.0f,
+                                    /*vertical_radius=*/3.5f,
+                                    /*border_width=*/4.0f));
+
+    {
+      ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+      CompareBitmap(bitmap.get(), 612, 792, modified_checksum);
+    }
+  }
+
+  // Save the document and close the page.
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  ASSERT_TRUE(OpenSavedDocument());
+  page = LoadSavedPage(1);
+  ASSERT_TRUE(page);
+  VerifySavedRendering(page, 612, 792, modified_checksum);
+
+  CloseSavedPage(page);
+  CloseSavedDocument();
+}
diff --git a/fpdfsdk/fpdf_annot_unittest.cpp b/fpdfsdk/fpdf_annot_unittest.cpp
deleted file mode 100644
index a5f619e..0000000
--- a/fpdfsdk/fpdf_annot_unittest.cpp
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "public/fpdf_annot.h"
-
-#include <vector>
-
-#include "constants/annotation_common.h"
-#include "core/fpdfapi/page/cpdf_annotcontext.h"
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "fpdfsdk/cpdfsdk_helpers.h"
-#include "public/cpp/fpdf_scopers.h"
-#include "public/fpdf_edit.h"
-#include "testing/fx_string_testhelpers.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-const wchar_t kStreamData[] =
-    L"/GS gs 0.0 0.0 0.0 RG 4 w 211.8 747.6 m 211.8 744.8 "
-    L"212.6 743.0 214.2 740.8 "
-    L"c 215.4 739.0 216.8 737.1 218.9 736.1 c 220.8 735.1 221.4 733.0 "
-    L"223.7 732.4 c 232.6 729.9 242.0 730.8 251.2 730.8 c 257.5 730.8 "
-    L"263.0 732.9 269.0 734.4 c S";
-
-}  // namespace
-
-class PDFAnnotTest : public testing::Test {
- protected:
-  PDFAnnotTest() = default;
-  ~PDFAnnotTest() override = default;
-
-  void SetUp() override { CPDF_PageModule::Create(); }
-  void TearDown() override { CPDF_PageModule::Destroy(); }
-};
-
-TEST_F(PDFAnnotTest, SetAP) {
-  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
-  ASSERT_TRUE(doc);
-  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100));
-  ASSERT_TRUE(page);
-  ScopedFPDFWideString ap_stream = GetFPDFWideString(kStreamData);
-  ASSERT_TRUE(ap_stream);
-
-  ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK));
-  ASSERT_TRUE(annot);
-
-  // Negative case: FPDFAnnot_SetAP() should fail if bounding rect is not yet
-  // set on the annotation.
-  EXPECT_FALSE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
-                               ap_stream.get()));
-
-  const FS_RECTF bounding_rect{206.0f, 753.0f, 339.0f, 709.0f};
-  EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &bounding_rect));
-
-  ASSERT_TRUE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color,
-                                 /*R=*/255, /*G=*/0, /*B=*/0, /*A=*/255));
-
-  EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
-                              ap_stream.get()));
-
-  // Verify that appearance stream is created as form XObject
-  CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot.get());
-  ASSERT_TRUE(context);
-  CPDF_Dictionary* annot_dict = context->GetAnnotDict();
-  ASSERT_TRUE(annot_dict);
-  CPDF_Dictionary* ap_dict = annot_dict->GetDictFor(pdfium::annotation::kAP);
-  ASSERT_TRUE(ap_dict);
-  CPDF_Dictionary* stream_dict = ap_dict->GetDictFor("N");
-  ASSERT_TRUE(stream_dict);
-  // Check for non-existence of resources dictionary in case of opaque color
-  CPDF_Dictionary* resources_dict = stream_dict->GetDictFor("Resources");
-  ASSERT_FALSE(resources_dict);
-  ByteString type = stream_dict->GetStringFor(pdfium::annotation::kType);
-  EXPECT_EQ("XObject", type);
-  ByteString sub_type = stream_dict->GetStringFor(pdfium::annotation::kSubtype);
-  EXPECT_EQ("Form", sub_type);
-
-  // Check that the appearance stream is same as we just set.
-  const uint32_t kStreamDataSize =
-      FX_ArraySize(kStreamData) * sizeof(FPDF_WCHAR);
-  unsigned long normal_length_bytes = FPDFAnnot_GetAP(
-      annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, nullptr, 0);
-  ASSERT_EQ(kStreamDataSize, normal_length_bytes);
-  std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(normal_length_bytes);
-  EXPECT_EQ(kStreamDataSize,
-            FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
-                            buf.data(), normal_length_bytes));
-  EXPECT_EQ(kStreamData, GetPlatformWString(buf.data()));
-}
-
-TEST_F(PDFAnnotTest, SetAPWithOpacity) {
-  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
-  ASSERT_TRUE(doc);
-  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100));
-  ASSERT_TRUE(page);
-  ScopedFPDFWideString ap_stream = GetFPDFWideString(kStreamData);
-  ASSERT_TRUE(ap_stream);
-
-  ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK));
-  ASSERT_TRUE(annot);
-
-  ASSERT_TRUE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color,
-                                 /*R=*/255, /*G=*/0, /*B=*/0, /*A=*/102));
-
-  const FS_RECTF bounding_rect{206.0f, 753.0f, 339.0f, 709.0f};
-  EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &bounding_rect));
-
-  EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
-                              ap_stream.get()));
-
-  CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot.get());
-  ASSERT_TRUE(context);
-  CPDF_Dictionary* annot_dict = context->GetAnnotDict();
-  ASSERT_TRUE(annot_dict);
-  CPDF_Dictionary* ap_dict = annot_dict->GetDictFor(pdfium::annotation::kAP);
-  ASSERT_TRUE(ap_dict);
-  CPDF_Dictionary* stream_dict = ap_dict->GetDictFor("N");
-  ASSERT_TRUE(stream_dict);
-  CPDF_Dictionary* resources_dict = stream_dict->GetDictFor("Resources");
-  ASSERT_TRUE(stream_dict);
-  CPDF_Dictionary* extGState_dict = resources_dict->GetDictFor("ExtGState");
-  ASSERT_TRUE(extGState_dict);
-  CPDF_Dictionary* gs_dict = extGState_dict->GetDictFor("GS");
-  ASSERT_TRUE(gs_dict);
-  ByteString type = gs_dict->GetStringFor(pdfium::annotation::kType);
-  EXPECT_EQ("ExtGState", type);
-  float opacity = gs_dict->GetNumberFor("CA");
-  // Opacity value of 102 is represented as 0.4f (=104/255) in /CA entry.
-  EXPECT_FLOAT_EQ(0.4f, opacity);
-  ByteString blend_mode = gs_dict->GetStringFor("BM");
-  EXPECT_EQ("Normal", blend_mode);
-  bool alpha_source_flag = gs_dict->GetBooleanFor("AIS", true);
-  EXPECT_FALSE(alpha_source_flag);
-}
diff --git a/fpdfsdk/fpdf_attachment.cpp b/fpdfsdk/fpdf_attachment.cpp
index 7f55691..04aed02 100644
--- a/fpdfsdk/fpdf_attachment.cpp
+++ b/fpdfsdk/fpdf_attachment.cpp
@@ -1,9 +1,11 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "public/fpdf_attachment.h"
 
+#include <limits.h>
+
 #include <memory>
 #include <utility>
 
@@ -21,10 +23,11 @@
 #include "core/fpdfdoc/cpdf_filespec.h"
 #include "core/fpdfdoc/cpdf_nametree.h"
 #include "core/fxcrt/cfx_datetime.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
 
@@ -55,7 +58,8 @@
   if (!pDoc)
     return 0;
 
-  return CPDF_NameTree(pDoc, "EmbeddedFiles").GetCount();
+  auto name_tree = CPDF_NameTree::Create(pDoc, "EmbeddedFiles");
+  return name_tree ? pdfium::base::checked_cast<int>(name_tree->GetCount()) : 0;
 }
 
 FPDF_EXPORT FPDF_ATTACHMENT FPDF_CALLCONV
@@ -64,41 +68,26 @@
   if (!pDoc)
     return nullptr;
 
-  CPDF_Dictionary* pRoot = pDoc->GetRoot();
-  if (!pRoot)
-    return nullptr;
-
   WideString wsName = WideStringFromFPDFWideString(name);
   if (wsName.IsEmpty())
     return nullptr;
 
-  // Retrieve the document's Names dictionary; create it if missing.
-  CPDF_Dictionary* pNames = pRoot->GetDictFor("Names");
-  if (!pNames) {
-    pNames = pDoc->NewIndirect<CPDF_Dictionary>();
-    pRoot->SetNewFor<CPDF_Reference>("Names", pDoc, pNames->GetObjNum());
-  }
-
-  // Create the EmbeddedFiles dictionary if missing.
-  if (!pNames->GetDictFor("EmbeddedFiles")) {
-    CPDF_Dictionary* pFiles = pDoc->NewIndirect<CPDF_Dictionary>();
-    pFiles->SetNewFor<CPDF_Array>("Names");
-    pNames->SetNewFor<CPDF_Reference>("EmbeddedFiles", pDoc,
-                                      pFiles->GetObjNum());
-  }
+  auto name_tree =
+      CPDF_NameTree::CreateWithRootNameArray(pDoc, "EmbeddedFiles");
+  if (!name_tree)
+    return nullptr;
 
   // Set up the basic entries in the filespec dictionary.
-  CPDF_Dictionary* pFile = pDoc->NewIndirect<CPDF_Dictionary>();
+  auto pFile = pDoc->NewIndirect<CPDF_Dictionary>();
   pFile->SetNewFor<CPDF_Name>("Type", "Filespec");
-  pFile->SetNewFor<CPDF_String>("UF", wsName);
-  pFile->SetNewFor<CPDF_String>(pdfium::stream::kF, wsName);
+  pFile->SetNewFor<CPDF_String>("UF", wsName.AsStringView());
+  pFile->SetNewFor<CPDF_String>(pdfium::stream::kF, wsName.AsStringView());
 
   // Add the new attachment name and filespec into the document's EmbeddedFiles.
-  CPDF_NameTree nameTree(pDoc, "EmbeddedFiles");
-  if (!nameTree.AddValueAndName(pFile->MakeReference(pDoc), wsName)) {
+  if (!name_tree->AddValueAndName(pFile->MakeReference(pDoc), wsName))
     return nullptr;
-  }
 
+  // Unretained reference in public API. NOLINTNEXTLINE
   return FPDFAttachmentFromCPDFObject(pFile);
 }
 
@@ -108,13 +97,15 @@
   if (!pDoc || index < 0)
     return nullptr;
 
-  CPDF_NameTree nameTree(pDoc, "EmbeddedFiles");
-  if (static_cast<size_t>(index) >= nameTree.GetCount())
+  auto name_tree = CPDF_NameTree::Create(pDoc, "EmbeddedFiles");
+  if (!name_tree || static_cast<size_t>(index) >= name_tree->GetCount())
     return nullptr;
 
   WideString csName;
+
+  // Unretained reference in public API. NOLINTNEXTLINE
   return FPDFAttachmentFromCPDFObject(
-      nameTree.LookupValueAndName(index, &csName));
+      name_tree->LookupValueAndName(index, &csName));
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -123,11 +114,11 @@
   if (!pDoc || index < 0)
     return false;
 
-  CPDF_NameTree nameTree(pDoc, "EmbeddedFiles");
-  if (static_cast<size_t>(index) >= nameTree.GetCount())
+  auto name_tree = CPDF_NameTree::Create(pDoc, "EmbeddedFiles");
+  if (!name_tree || static_cast<size_t>(index) >= name_tree->GetCount())
     return false;
 
-  return nameTree.DeleteValueAndName(index);
+  return name_tree->DeleteValueAndName(index);
 }
 
 FPDF_EXPORT unsigned long FPDF_CALLCONV
@@ -138,8 +129,9 @@
   if (!pFile)
     return 0;
 
-  return Utf16EncodeMaybeCopyAndReturnLength(CPDF_FileSpec(pFile).GetFileName(),
-                                             buffer, buflen);
+  CPDF_FileSpec spec(pdfium::WrapRetain(pFile));
+  return Utf16EncodeMaybeCopyAndReturnLength(spec.GetFileName(), buffer,
+                                             buflen);
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -148,7 +140,8 @@
   if (!pFile)
     return 0;
 
-  CPDF_Dictionary* pParamsDict = CPDF_FileSpec(pFile).GetParamsDict();
+  CPDF_FileSpec spec(pdfium::WrapRetain(pFile));
+  RetainPtr<const CPDF_Dictionary> pParamsDict = spec.GetParamsDict();
   return pParamsDict ? pParamsDict->KeyExist(key) : 0;
 }
 
@@ -157,8 +150,9 @@
   if (!FPDFAttachment_HasKey(attachment, key))
     return FPDF_OBJECT_UNKNOWN;
 
-  CPDF_FileSpec spec(CPDFObjectFromFPDFAttachment(attachment));
-  CPDF_Object* pObj = spec.GetParamsDict()->GetObjectFor(key);
+  CPDF_FileSpec spec(
+      pdfium::WrapRetain(CPDFObjectFromFPDFAttachment(attachment)));
+  RetainPtr<const CPDF_Object> pObj = spec.GetParamsDict()->GetObjectFor(key);
   return pObj ? pObj->GetType() : FPDF_OBJECT_UNKNOWN;
 }
 
@@ -170,7 +164,8 @@
   if (!pFile)
     return false;
 
-  CPDF_Dictionary* pParamsDict = CPDF_FileSpec(pFile).GetParamsDict();
+  CPDF_FileSpec spec(pdfium::WrapRetain(pFile));
+  RetainPtr<CPDF_Dictionary> pParamsDict = spec.GetMutableParamsDict();
   if (!pParamsDict)
     return false;
 
@@ -193,16 +188,19 @@
   if (!pFile)
     return 0;
 
-  CPDF_Dictionary* pParamsDict = CPDF_FileSpec(pFile).GetParamsDict();
+  CPDF_FileSpec spec(pdfium::WrapRetain(pFile));
+  RetainPtr<const CPDF_Dictionary> pParamsDict = spec.GetParamsDict();
   if (!pParamsDict)
     return 0;
 
   ByteString bsKey = key;
   WideString value = pParamsDict->GetUnicodeTextFor(bsKey);
   if (bsKey == kChecksumKey && !value.IsEmpty()) {
-    CPDF_String* stringValue = pParamsDict->GetObjectFor(bsKey)->AsString();
+    const CPDF_String* stringValue =
+        pParamsDict->GetObjectFor(bsKey)->AsString();
     if (stringValue->IsHex()) {
-      ByteString encoded = PDF_EncodeString(stringValue->GetString(), true);
+      ByteString encoded =
+          PDF_HexEncodeString(stringValue->GetString().AsStringView());
       value = pdfium::MakeRetain<CPDF_String>(nullptr, encoded, false)
                   ->GetUnicodeText();
     }
@@ -215,7 +213,7 @@
 FPDFAttachment_SetFile(FPDF_ATTACHMENT attachment,
                        FPDF_DOCUMENT document,
                        const void* contents,
-                       const unsigned long len) {
+                       unsigned long len) {
   CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
   if (!pFile || !pFile->IsDictionary() || !pDoc || len > INT_MAX)
@@ -227,8 +225,7 @@
 
   // Create a dictionary for the new embedded file stream.
   auto pFileStreamDict = pdfium::MakeRetain<CPDF_Dictionary>();
-  CPDF_Dictionary* pParamsDict =
-      pFileStreamDict->SetNewFor<CPDF_Dictionary>("Params");
+  auto pParamsDict = pFileStreamDict->SetNewFor<CPDF_Dictionary>("Params");
 
   // Set the size of the new file in the dictionary.
   pFileStreamDict->SetNewFor<CPDF_Number>(pdfium::stream::kDL,
@@ -251,27 +248,34 @@
       true);
 
   // Create the file stream and have the filespec dictionary link to it.
-  std::unique_ptr<uint8_t, FxFreeDeleter> stream(FX_Alloc(uint8_t, len));
-  memcpy(stream.get(), contents, len);
-  CPDF_Stream* pFileStream = pDoc->NewIndirect<CPDF_Stream>(
-      std::move(stream), len, std::move(pFileStreamDict));
-  CPDF_Dictionary* pEFDict =
-      pFile->AsDictionary()->SetNewFor<CPDF_Dictionary>("EF");
+  const uint8_t* contents_as_bytes = static_cast<const uint8_t*>(contents);
+  auto pFileStream = pDoc->NewIndirect<CPDF_Stream>(
+      DataVector<uint8_t>(contents_as_bytes, contents_as_bytes + len),
+      std::move(pFileStreamDict));
+  auto pEFDict = pFile->AsMutableDictionary()->SetNewFor<CPDF_Dictionary>("EF");
   pEFDict->SetNewFor<CPDF_Reference>("F", pDoc, pFileStream->GetObjNum());
   return true;
 }
 
-FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFAttachment_GetFile(FPDF_ATTACHMENT attachment,
                        void* buffer,
-                       unsigned long buflen) {
+                       unsigned long buflen,
+                       unsigned long* out_buflen) {
+  if (!out_buflen)
+    return false;
+
   CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
   if (!pFile)
-    return 0;
+    return false;
 
-  CPDF_Stream* pFileStream = CPDF_FileSpec(pFile).GetFileStream();
+  CPDF_FileSpec spec(pdfium::WrapRetain(pFile));
+  RetainPtr<const CPDF_Stream> pFileStream = spec.GetFileStream();
   if (!pFileStream)
-    return 0;
+    return false;
 
-  return DecodeStreamMaybeCopyAndReturnLength(pFileStream, buffer, buflen);
+  *out_buflen = DecodeStreamMaybeCopyAndReturnLength(
+      std::move(pFileStream),
+      {static_cast<uint8_t*>(buffer), static_cast<size_t>(buflen)});
+  return true;
 }
diff --git a/fpdfsdk/fpdf_attachment_embeddertest.cpp b/fpdfsdk/fpdf_attachment_embeddertest.cpp
index 7ce6003..6c156d6 100644
--- a/fpdfsdk/fpdf_attachment_embeddertest.cpp
+++ b/fpdfsdk/fpdf_attachment_embeddertest.cpp
@@ -1,8 +1,7 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
 #include <string>
 #include <vector>
 
@@ -10,6 +9,7 @@
 #include "public/fpdfview.h"
 #include "testing/embedder_test.h"
 #include "testing/fx_string_testhelpers.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/utils/hash.h"
 
 static constexpr char kDateKey[] = "CreationDate";
@@ -22,6 +22,10 @@
   ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
   EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
 
+  // Try to retrieve attachments at bad indices.
+  EXPECT_FALSE(FPDFDoc_GetAttachment(document(), -1));
+  EXPECT_FALSE(FPDFDoc_GetAttachment(document(), 2));
+
   // Retrieve the first attachment.
   FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(document(), 0);
   ASSERT_TRUE(attachment);
@@ -33,12 +37,17 @@
   EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
   EXPECT_EQ(L"1.txt", GetPlatformWString(buf.data()));
 
+  // Check some unsuccessful cases of FPDFAttachment_GetFile.
+  EXPECT_FALSE(FPDFAttachment_GetFile(attachment, nullptr, 0, nullptr));
+  EXPECT_FALSE(FPDFAttachment_GetFile(nullptr, nullptr, 0, &length_bytes));
+
   // Check that the content of the first attachment is correct.
-  length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0);
-  std::vector<char> content_buf(length_bytes);
-  ASSERT_EQ(
-      4u, FPDFAttachment_GetFile(attachment, content_buf.data(), length_bytes));
-  EXPECT_EQ(std::string("test"), std::string(content_buf.data(), 4));
+  ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
+  std::vector<uint8_t> content_buf(length_bytes);
+  unsigned long actual_length_bytes;
+  ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(),
+                                     length_bytes, &actual_length_bytes));
+  ASSERT_THAT(content_buf, testing::ElementsAre('t', 'e', 's', 't'));
 
   // Check that a non-existent key does not exist.
   EXPECT_FALSE(FPDFAttachment_HasKey(attachment, "none"));
@@ -64,17 +73,17 @@
   ASSERT_TRUE(attachment);
 
   // Retrieve the second attachment file.
-  length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0);
+  ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
   content_buf.clear();
   content_buf.resize(length_bytes);
-  ASSERT_EQ(5869u, FPDFAttachment_GetFile(attachment, content_buf.data(),
-                                          length_bytes));
+  ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(),
+                                     length_bytes, &actual_length_bytes));
+  ASSERT_EQ(5869u, actual_length_bytes);
 
   // Check that the calculated checksum of the file data matches expectation.
   const char kCheckSum[] = "72afcddedf554dda63c0c88e06f1ce18";
   const wchar_t kCheckSumW[] = L"<72AFCDDEDF554DDA63C0C88E06F1CE18>";
-  const std::string generated_checksum = GenerateMD5Base16(
-      reinterpret_cast<uint8_t*>(content_buf.data()), length_bytes);
+  const std::string generated_checksum = GenerateMD5Base16(content_buf);
   EXPECT_EQ(kCheckSum, generated_checksum);
 
   // Check that the stored checksum matches expectation.
@@ -87,6 +96,40 @@
   EXPECT_EQ(kCheckSumW, GetPlatformWString(buf.data()));
 }
 
+TEST_F(FPDFAttachmentEmbedderTest, NoAttachmentToExtract) {
+  // Open a file with no attachments.
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_EQ(0, FPDFDoc_GetAttachmentCount(document()));
+
+  // Try to retrieve attachments at bad indices.
+  EXPECT_FALSE(FPDFDoc_GetAttachment(document(), -1));
+  EXPECT_FALSE(FPDFDoc_GetAttachment(document(), 0));
+}
+
+TEST_F(FPDFAttachmentEmbedderTest, InvalidAttachmentData) {
+  // Open a file with an attachment that is missing the embedded file (/EF).
+  ASSERT_TRUE(OpenDocument("embedded_attachments_invalid_data.pdf"));
+  ASSERT_EQ(1, FPDFDoc_GetAttachmentCount(document()));
+
+  // Retrieve the first attachment.
+  FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(document(), 0);
+  ASSERT_TRUE(attachment);
+
+  // Check that the name of the attachment is correct.
+  unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
+  ASSERT_EQ(12u, length_bytes);
+  std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+  EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
+  EXPECT_EQ("1.txt", GetPlatformString(buf.data()));
+
+  // Check that is is not possible to retrieve the file data.
+  EXPECT_FALSE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
+
+  // Check that the attachment can be deleted.
+  EXPECT_TRUE(FPDFDoc_DeleteAttachment(document(), 0));
+  EXPECT_EQ(0, FPDFDoc_GetAttachmentCount(document()));
+}
+
 TEST_F(FPDFAttachmentEmbedderTest, AddAttachments) {
   // Open a file with two attachments.
   ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
@@ -99,6 +142,7 @@
   ScopedFPDFWideString file_name = GetFPDFWideString(L"0.txt");
   FPDF_ATTACHMENT attachment =
       FPDFDoc_AddAttachment(document(), file_name.get());
+  ASSERT_TRUE(attachment);
 
   // Check that writing to a file with nullptr but non-zero bytes would fail.
   EXPECT_FALSE(FPDFAttachment_SetFile(attachment, document(), nullptr, 10));
@@ -107,6 +151,7 @@
   constexpr char kContents1[] = "Hello!";
   EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents1,
                                      strlen(kContents1)));
+  EXPECT_EQ(3, FPDFDoc_GetAttachmentCount(document()));
 
   // Verify the name of the new attachment (i.e. the first attachment).
   attachment = FPDFDoc_GetAttachment(document(), 0);
@@ -118,15 +163,18 @@
   EXPECT_EQ(L"0.txt", GetPlatformWString(buf.data()));
 
   // Verify the content of the new attachment (i.e. the first attachment).
-  length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0);
+  ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
   std::vector<char> content_buf(length_bytes);
-  ASSERT_EQ(
-      6u, FPDFAttachment_GetFile(attachment, content_buf.data(), length_bytes));
+  unsigned long actual_length_bytes;
+  ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(),
+                                     length_bytes, &actual_length_bytes));
+  ASSERT_EQ(6u, actual_length_bytes);
   EXPECT_EQ(std::string(kContents1), std::string(content_buf.data(), 6));
 
   // Add an attachment to the end of the embedded file list and set its file.
   file_name = GetFPDFWideString(L"z.txt");
   attachment = FPDFDoc_AddAttachment(document(), file_name.get());
+  ASSERT_TRUE(attachment);
   constexpr char kContents2[] = "World!";
   EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents2,
                                      strlen(kContents2)));
@@ -142,11 +190,12 @@
   EXPECT_EQ(L"z.txt", GetPlatformWString(buf.data()));
 
   // Verify the content of the new attachment (i.e. the fourth attachment).
-  length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0);
+  ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
   content_buf.clear();
   content_buf.resize(length_bytes);
-  ASSERT_EQ(
-      6u, FPDFAttachment_GetFile(attachment, content_buf.data(), length_bytes));
+  ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(),
+                                     length_bytes, &actual_length_bytes));
+  ASSERT_EQ(6u, actual_length_bytes);
   EXPECT_EQ(std::string(kContents2), std::string(content_buf.data(), 6));
 }
 
@@ -159,6 +208,7 @@
   ScopedFPDFWideString file_name = GetFPDFWideString(L"5.txt");
   FPDF_ATTACHMENT attachment =
       FPDFDoc_AddAttachment(document(), file_name.get());
+  ASSERT_TRUE(attachment);
   constexpr char kContents[] = "Hello World!";
   EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents,
                                      strlen(kContents)));
@@ -185,10 +235,12 @@
   EXPECT_EQ(L"5.txt", GetPlatformWString(buf.data()));
 
   // Verify the content of the new attachment.
-  length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0);
+  ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
   std::vector<char> content_buf(length_bytes);
-  ASSERT_EQ(12u, FPDFAttachment_GetFile(attachment, content_buf.data(),
-                                        length_bytes));
+  unsigned long actual_length_bytes;
+  ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(),
+                                     length_bytes, &actual_length_bytes));
+  ASSERT_EQ(12u, actual_length_bytes);
   EXPECT_EQ(std::string(kContents), std::string(content_buf.data(), 12));
 
   // Verify the creation date of the new attachment.
@@ -212,7 +264,8 @@
   // Overwrite the existing file with empty content, and check that the checksum
   // gets updated to the correct value.
   EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), nullptr, 0));
-  EXPECT_EQ(0u, FPDFAttachment_GetFile(attachment, nullptr, 0));
+  ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
+  EXPECT_EQ(0u, length_bytes);
   length_bytes =
       FPDFAttachment_GetStringValue(attachment, kChecksumKey, nullptr, 0);
   ASSERT_EQ(70u, length_bytes);
@@ -223,6 +276,69 @@
             GetPlatformWString(buf.data()));
 }
 
+TEST_F(FPDFAttachmentEmbedderTest, AddAttachmentsToFileWithNoAttachments) {
+  // Open a file with no attachments.
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_EQ(0, FPDFDoc_GetAttachmentCount(document()));
+
+  // Add an attachment to the beginning of the embedded file list.
+  ScopedFPDFWideString file_name = GetFPDFWideString(L"0.txt");
+  FPDF_ATTACHMENT attachment =
+      FPDFDoc_AddAttachment(document(), file_name.get());
+  ASSERT_TRUE(attachment);
+
+  // Set the new attachment's file.
+  constexpr char kContents1[] = "Hello!";
+  EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents1,
+                                     strlen(kContents1)));
+  EXPECT_EQ(1, FPDFDoc_GetAttachmentCount(document()));
+
+  // Verify the name of the new attachment (i.e. the first attachment).
+  attachment = FPDFDoc_GetAttachment(document(), 0);
+  ASSERT_TRUE(attachment);
+  unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
+  ASSERT_EQ(12u, length_bytes);
+  std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+  EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
+  EXPECT_EQ(L"0.txt", GetPlatformWString(buf.data()));
+
+  // Verify the content of the new attachment (i.e. the first attachment).
+  ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
+  std::vector<char> content_buf(length_bytes);
+  unsigned long actual_length_bytes;
+  ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(),
+                                     length_bytes, &actual_length_bytes));
+  ASSERT_EQ(6u, actual_length_bytes);
+  EXPECT_EQ(std::string(kContents1), std::string(content_buf.data(), 6));
+
+  // Add an attachment to the end of the embedded file list and set its file.
+  file_name = GetFPDFWideString(L"z.txt");
+  attachment = FPDFDoc_AddAttachment(document(), file_name.get());
+  ASSERT_TRUE(attachment);
+  constexpr char kContents2[] = "World!";
+  EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents2,
+                                     strlen(kContents2)));
+  EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
+
+  // Verify the name of the new attachment (i.e. the second attachment).
+  attachment = FPDFDoc_GetAttachment(document(), 1);
+  ASSERT_TRUE(attachment);
+  length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
+  ASSERT_EQ(12u, length_bytes);
+  buf = GetFPDFWideStringBuffer(length_bytes);
+  EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
+  EXPECT_EQ(L"z.txt", GetPlatformWString(buf.data()));
+
+  // Verify the content of the new attachment (i.e. the second attachment).
+  ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
+  content_buf.clear();
+  content_buf.resize(length_bytes);
+  ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(),
+                                     length_bytes, &actual_length_bytes));
+  ASSERT_EQ(6u, actual_length_bytes);
+  EXPECT_EQ(std::string(kContents2), std::string(content_buf.data(), 6));
+}
+
 TEST_F(FPDFAttachmentEmbedderTest, DeleteAttachment) {
   // Open a file with two attachments.
   ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
@@ -230,6 +346,7 @@
 
   // Verify the name of the first attachment.
   FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(document(), 0);
+  ASSERT_TRUE(attachment);
   unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
   ASSERT_EQ(12u, length_bytes);
   std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
@@ -242,6 +359,7 @@
 
   // Verify the name of the new first attachment.
   attachment = FPDFDoc_GetAttachment(document(), 0);
+  ASSERT_TRUE(attachment);
   length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
   ASSERT_EQ(26u, length_bytes);
   buf = GetFPDFWideStringBuffer(length_bytes);
diff --git a/fpdfsdk/fpdf_catalog.cpp b/fpdfsdk/fpdf_catalog.cpp
index 97bddde..6147de8 100644
--- a/fpdfsdk/fpdf_catalog.cpp
+++ b/fpdfsdk/fpdf_catalog.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,6 +18,6 @@
   if (!pCatalog)
     return false;
 
-  const CPDF_Dictionary* pMarkInfo = pCatalog->GetDictFor("MarkInfo");
+  RetainPtr<const CPDF_Dictionary> pMarkInfo = pCatalog->GetDictFor("MarkInfo");
   return pMarkInfo && pMarkInfo->GetIntegerFor("Marked") != 0;
 }
diff --git a/fpdfsdk/fpdf_catalog_unittest.cpp b/fpdfsdk/fpdf_catalog_unittest.cpp
index 68655da..7a45cb0 100644
--- a/fpdfsdk/fpdf_catalog_unittest.cpp
+++ b/fpdfsdk/fpdf_catalog_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,40 +6,29 @@
 
 #include <memory>
 
-#include "core/fpdfapi/page/cpdf_docpagedata.h"
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fpdfapi/page/test_with_page_module.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_parser.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfapi/render/cpdf_docrenderdata.h"
+#include "core/fpdfapi/parser/cpdf_test_document.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "public/cpp/fpdf_scopers.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
-class CPDF_TestDocument final : public CPDF_Document {
- public:
-  CPDF_TestDocument()
-      : CPDF_Document(pdfium::MakeUnique<CPDF_DocRenderData>(),
-                      pdfium::MakeUnique<CPDF_DocPageData>()) {}
-
-  void SetRoot(CPDF_Dictionary* root) { m_pRootDict.Reset(root); }
-};
-
-class PDFCatalogTest : public testing::Test {
+class PDFCatalogTest : public TestWithPageModule {
  public:
   void SetUp() override {
-    CPDF_PageModule::Create();
-    auto pTestDoc = pdfium::MakeUnique<CPDF_TestDocument>();
+    TestWithPageModule::SetUp();
+    auto pTestDoc = std::make_unique<CPDF_TestDocument>();
     m_pDoc.reset(FPDFDocumentFromCPDFDocument(pTestDoc.release()));
     m_pRootObj = pdfium::MakeRetain<CPDF_Dictionary>();
   }
 
   void TearDown() override {
     m_pDoc.reset();
-    CPDF_PageModule::Destroy();
+    TestWithPageModule::TearDown();
   }
 
  protected:
@@ -59,7 +48,7 @@
   EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get()));
 
   // Empty root
-  pTestDoc->SetRoot(m_pRootObj.Get());
+  pTestDoc->SetRoot(m_pRootObj);
   EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get()));
 
   // Root with other key
@@ -67,8 +56,7 @@
   EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get()));
 
   // Root with empty MarkInfo
-  CPDF_Dictionary* markInfoDict =
-      m_pRootObj->SetNewFor<CPDF_Dictionary>("MarkInfo");
+  auto markInfoDict = m_pRootObj->SetNewFor<CPDF_Dictionary>("MarkInfo");
   EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get()));
 
   // MarkInfo present but Marked is 0
diff --git a/fpdfsdk/fpdf_dataavail.cpp b/fpdfsdk/fpdf_dataavail.cpp
index 11abff3..f0ce101 100644
--- a/fpdfsdk/fpdf_dataavail.cpp
+++ b/fpdfsdk/fpdf_dataavail.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,37 +16,40 @@
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "public/fpdf_formfill.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 #ifdef PDF_ENABLE_XFA
 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
 #endif  // PDF_ENABLE_XFA
 
 // These checks are here because core/ and public/ cannot depend on each other.
-static_assert(CPDF_DataAvail::DataError == PDF_DATA_ERROR,
-              "CPDF_DataAvail::DataError value mismatch");
-static_assert(CPDF_DataAvail::DataNotAvailable == PDF_DATA_NOTAVAIL,
-              "CPDF_DataAvail::DataNotAvailable value mismatch");
-static_assert(CPDF_DataAvail::DataAvailable == PDF_DATA_AVAIL,
-              "CPDF_DataAvail::DataAvailable value mismatch");
+static_assert(CPDF_DataAvail::kDataError == PDF_DATA_ERROR,
+              "CPDF_DataAvail::kDataError value mismatch");
+static_assert(CPDF_DataAvail::kDataNotAvailable == PDF_DATA_NOTAVAIL,
+              "CPDF_DataAvail::kDataNotAvailable value mismatch");
+static_assert(CPDF_DataAvail::kDataAvailable == PDF_DATA_AVAIL,
+              "CPDF_DataAvail::kDataAvailable value mismatch");
 
-static_assert(CPDF_DataAvail::LinearizationUnknown == PDF_LINEARIZATION_UNKNOWN,
-              "CPDF_DataAvail::LinearizationUnknown value mismatch");
-static_assert(CPDF_DataAvail::NotLinearized == PDF_NOT_LINEARIZED,
-              "CPDF_DataAvail::NotLinearized value mismatch");
-static_assert(CPDF_DataAvail::Linearized == PDF_LINEARIZED,
-              "CPDF_DataAvail::Linearized value mismatch");
+static_assert(CPDF_DataAvail::kLinearizationUnknown ==
+                  PDF_LINEARIZATION_UNKNOWN,
+              "CPDF_DataAvail::kLinearizationUnknown value mismatch");
+static_assert(CPDF_DataAvail::kNotLinearized == PDF_NOT_LINEARIZED,
+              "CPDF_DataAvail::kNotLinearized value mismatch");
+static_assert(CPDF_DataAvail::kLinearized == PDF_LINEARIZED,
+              "CPDF_DataAvail::kLinearized value mismatch");
 
-static_assert(CPDF_DataAvail::FormError == PDF_FORM_ERROR,
-              "CPDF_DataAvail::FormError value mismatch");
-static_assert(CPDF_DataAvail::FormNotAvailable == PDF_FORM_NOTAVAIL,
-              "CPDF_DataAvail::FormNotAvailable value mismatch");
-static_assert(CPDF_DataAvail::FormAvailable == PDF_FORM_AVAIL,
-              "CPDF_DataAvail::FormAvailable value mismatch");
-static_assert(CPDF_DataAvail::FormNotExist == PDF_FORM_NOTEXIST,
-              "CPDF_DataAvail::FormNotExist value mismatch");
+static_assert(CPDF_DataAvail::kFormError == PDF_FORM_ERROR,
+              "CPDF_DataAvail::kFormError value mismatch");
+static_assert(CPDF_DataAvail::kFormNotAvailable == PDF_FORM_NOTAVAIL,
+              "CPDF_DataAvail::kFormNotAvailable value mismatch");
+static_assert(CPDF_DataAvail::kFormAvailable == PDF_FORM_AVAIL,
+              "CPDF_DataAvail::kFormAvailable value mismatch");
+static_assert(CPDF_DataAvail::kFormNotExist == PDF_FORM_NOTEXIST,
+              "CPDF_DataAvail::kFormNotExist value mismatch");
 
 namespace {
 
@@ -57,70 +60,72 @@
 
   // CPDF_DataAvail::FileAvail:
   bool IsDataAvail(FX_FILESIZE offset, size_t size) override {
-    return !!avail_->IsDataAvail(avail_, offset, size);
+    return !!avail_->IsDataAvail(
+        avail_, pdfium::base::checked_cast<size_t>(offset), size);
   }
 
  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 {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // IFX_SeekableReadStream:
   FX_FILESIZE GetSize() override { return file_->m_FileLen; }
 
-  bool ReadBlockAtOffset(void* buffer,
-                         FX_FILESIZE offset,
-                         size_t size) override {
-    if (!buffer || offset < 0 || !size)
+  bool ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                         FX_FILESIZE offset) override {
+    if (buffer.empty() || offset < 0)
       return false;
 
-    if (!pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(size))
+    if (!pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(buffer.size()))
       return false;
 
-    FX_SAFE_FILESIZE new_pos = size;
+    FX_SAFE_FILESIZE new_pos = buffer.size();
     new_pos += offset;
     return new_pos.IsValid() && new_pos.ValueOrDie() <= GetSize() &&
-           file_->m_GetBlock(file_->m_Param, offset,
-                             static_cast<uint8_t*>(buffer), size);
+           file_->m_GetBlock(
+               file_->m_Param,
+               pdfium::base::checked_cast<unsigned long>(offset), buffer.data(),
+               pdfium::base::checked_cast<unsigned long>(buffer.size()));
   }
 
  private:
   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 {
  public:
-  explicit FPDF_DownloadHintsContext(FX_DOWNLOADHINTS* pDownloadHints) {
-    m_pDownloadHints = pDownloadHints;
-  }
-  ~FPDF_DownloadHintsContext() override {}
+  explicit FPDF_DownloadHintsContext(FX_DOWNLOADHINTS* pDownloadHints)
+      : m_pDownloadHints(pDownloadHints) {}
+  ~FPDF_DownloadHintsContext() override = default;
 
- public:
   // IFX_DownloadHints
   void AddSegment(FX_FILESIZE offset, size_t size) override {
-    if (m_pDownloadHints)
-      m_pDownloadHints->AddSegment(m_pDownloadHints, offset, size);
+    if (m_pDownloadHints) {
+      m_pDownloadHints->AddSegment(m_pDownloadHints,
+                                   static_cast<size_t>(offset), size);
+    }
   }
 
  private:
-  FX_DOWNLOADHINTS* m_pDownloadHints;
+  UnownedPtr<FX_DOWNLOADHINTS> m_pDownloadHints;
 };
 
 class FPDF_AvailContext {
  public:
   FPDF_AvailContext(FX_FILEAVAIL* file_avail, FPDF_FILEACCESS* file)
-      : file_avail_(pdfium::MakeUnique<FPDF_FileAvailContext>(file_avail)),
+      : file_avail_(std::make_unique<FPDF_FileAvailContext>(file_avail)),
         file_read_(pdfium::MakeRetain<FPDF_FileAccessContext>(file)),
-        data_avail_(pdfium::MakeUnique<CPDF_DataAvail>(file_avail_.get(),
-                                                       file_read_,
-                                                       true)) {}
+        data_avail_(
+            std::make_unique<CPDF_DataAvail>(file_avail_.get(), file_read_)) {}
   ~FPDF_AvailContext() = default;
 
   CPDF_DataAvail* data_avail() { return data_avail_.get(); }
@@ -132,15 +137,21 @@
 };
 
 FPDF_AvailContext* FPDFAvailContextFromFPDFAvail(FPDF_AVAIL avail) {
-  return static_cast<FPDF_AvailContext*>(avail);
+  return reinterpret_cast<FPDF_AvailContext*>(avail);
+}
+
+FPDF_AVAIL FPDFAvailFromFPDFAvailContext(FPDF_AvailContext* pAvailContext) {
+  return reinterpret_cast<FPDF_AVAIL>(pAvailContext);
 }
 
 }  // namespace
 
 FPDF_EXPORT FPDF_AVAIL FPDF_CALLCONV FPDFAvail_Create(FX_FILEAVAIL* file_avail,
                                                       FPDF_FILEACCESS* file) {
-  auto pAvail = pdfium::MakeUnique<FPDF_AvailContext>(file_avail, file);
-  return pAvail.release();  // Caller takes ownership.
+  auto pAvail = std::make_unique<FPDF_AvailContext>(file_avail, file);
+
+  // Caller takes ownership.
+  return FPDFAvailFromFPDFAvailContext(pAvail.release());
 }
 
 FPDF_EXPORT void FPDF_CALLCONV FPDFAvail_Destroy(FPDF_AVAIL avail) {
@@ -165,17 +176,13 @@
   CPDF_Parser::Error error;
   std::unique_ptr<CPDF_Document> document;
   std::tie(error, document) = avail_context->data_avail()->ParseDocument(
-      pdfium::MakeUnique<CPDF_DocRenderData>(),
-      pdfium::MakeUnique<CPDF_DocPageData>(), password);
+      std::make_unique<CPDF_DocRenderData>(),
+      std::make_unique<CPDF_DocPageData>(), password);
   if (error != CPDF_Parser::SUCCESS) {
     ProcessParseError(error);
     return nullptr;
   }
 
-#ifdef PDF_ENABLE_XFA
-  document->SetExtension(pdfium::MakeUnique<CPDFXFA_Context>(document.get()));
-#endif  // PDF_ENABLE_XFA
-
   ReportUnsupportedFeatures(document.get());
   return FPDFDocumentFromCPDFDocument(document.release());
 }
diff --git a/fpdfsdk/fpdf_dataavail_embeddertest.cpp b/fpdfsdk/fpdf_dataavail_embeddertest.cpp
index 02b481c..e03f2ac 100644
--- a/fpdfsdk/fpdf_dataavail_embeddertest.cpp
+++ b/fpdfsdk/fpdf_dataavail_embeddertest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,9 +9,10 @@
 #include <vector>
 
 #include "core/fxcrt/bytestring.h"
-#include "core/fxcrt/widestring.h"
+#include "public/fpdf_doc.h"
 #include "public/fpdfview.h"
 #include "testing/embedder_test.h"
+#include "testing/fx_string_testhelpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/range_set.h"
 #include "testing/utils/file_util.h"
@@ -163,23 +164,23 @@
   // Document must load without crashing but is too malformed to be available.
   EXPECT_FALSE(OpenDocument("trailer_unterminated.pdf"));
   MockDownloadHints hints;
-  EXPECT_FALSE(FPDFAvail_IsDocAvail(avail_, &hints));
+  EXPECT_FALSE(FPDFAvail_IsDocAvail(avail(), &hints));
 }
 
 TEST_F(FPDFDataAvailEmbedderTest, TrailerAsHexstring) {
   // Document must load without crashing but is too malformed to be available.
   EXPECT_FALSE(OpenDocument("trailer_as_hexstring.pdf"));
   MockDownloadHints hints;
-  EXPECT_FALSE(FPDFAvail_IsDocAvail(avail_, &hints));
+  EXPECT_FALSE(FPDFAvail_IsDocAvail(avail(), &hints));
 }
 
 TEST_F(FPDFDataAvailEmbedderTest, LoadUsingHintTables) {
   TestAsyncLoader loader("feature_linearized_loading.pdf");
-  avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
-  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail_, loader.hints()));
-  document_ = FPDFAvail_GetDocument(avail_, nullptr);
-  ASSERT_TRUE(document_);
-  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail_, 1, loader.hints()));
+  CreateAvail(loader.file_avail(), loader.file_access());
+  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
+  SetDocumentFromAvail();
+  ASSERT_TRUE(document());
+  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail(), 1, loader.hints()));
 
   // No new data available, to prevent load "Pages" node.
   loader.set_is_new_data_available(false);
@@ -189,10 +190,10 @@
 
 TEST_F(FPDFDataAvailEmbedderTest, CheckFormAvailIfLinearized) {
   TestAsyncLoader loader("feature_linearized_loading.pdf");
-  avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
-  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail_, loader.hints()));
-  document_ = FPDFAvail_GetDocument(avail_, nullptr);
-  ASSERT_TRUE(document_);
+  CreateAvail(loader.file_avail(), loader.file_access());
+  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
+  SetDocumentFromAvail();
+  ASSERT_TRUE(document());
 
   // Prevent access to non-requested data to coerce the parser to send new
   // request for non available (non-requested before) data.
@@ -202,7 +203,7 @@
   int status = PDF_FORM_NOTAVAIL;
   while (status == PDF_FORM_NOTAVAIL) {
     loader.FlushRequestedData();
-    status = FPDFAvail_IsFormAvail(avail_, loader.hints());
+    status = FPDFAvail_IsFormAvail(avail(), loader.hints());
   }
   EXPECT_NE(PDF_FORM_ERROR, status);
 }
@@ -210,11 +211,11 @@
 TEST_F(FPDFDataAvailEmbedderTest,
        DoNotLoadMainCrossRefForFirstPageIfLinearized) {
   TestAsyncLoader loader("feature_linearized_loading.pdf");
-  avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
-  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail_, loader.hints()));
-  document_ = FPDFAvail_GetDocument(avail_, nullptr);
-  ASSERT_TRUE(document_);
-  const int first_page_num = FPDFAvail_GetFirstPageNum(document_);
+  CreateAvail(loader.file_avail(), loader.file_access());
+  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
+  SetDocumentFromAvail();
+  ASSERT_TRUE(document());
+  const int first_page_num = FPDFAvail_GetFirstPageNum(document());
 
   // The main cross ref table should not be processed.
   // (It is always at file end)
@@ -224,7 +225,7 @@
   // Prevent access to non-requested data to coerce the parser to send new
   // request for non available (non-requested before) data.
   loader.set_is_new_data_available(false);
-  FPDFAvail_IsPageAvail(avail_, first_page_num, loader.hints());
+  FPDFAvail_IsPageAvail(avail(), first_page_num, loader.hints());
 
   // The main cross ref table should not be requested.
   // (It is always at file end)
@@ -233,7 +234,7 @@
   // Allow parse page.
   loader.set_is_new_data_available(true);
   ASSERT_EQ(PDF_DATA_AVAIL,
-            FPDFAvail_IsPageAvail(avail_, first_page_num, loader.hints()));
+            FPDFAvail_IsPageAvail(avail(), first_page_num, loader.hints()));
 
   // The main cross ref table should not be processed.
   // (It is always at file end)
@@ -248,10 +249,10 @@
 
 TEST_F(FPDFDataAvailEmbedderTest, LoadSecondPageIfLinearizedWithHints) {
   TestAsyncLoader loader("feature_linearized_loading.pdf");
-  avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
-  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail_, loader.hints()));
-  document_ = FPDFAvail_GetDocument(avail_, nullptr);
-  ASSERT_TRUE(document_);
+  CreateAvail(loader.file_avail(), loader.file_access());
+  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
+  SetDocumentFromAvail();
+  ASSERT_TRUE(document());
 
   static constexpr uint32_t kSecondPageNum = 1;
 
@@ -263,7 +264,7 @@
   int status = PDF_DATA_NOTAVAIL;
   while (status == PDF_DATA_NOTAVAIL) {
     loader.FlushRequestedData();
-    status = FPDFAvail_IsPageAvail(avail_, kSecondPageNum, loader.hints());
+    status = FPDFAvail_IsPageAvail(avail(), kSecondPageNum, loader.hints());
   }
   EXPECT_EQ(PDF_DATA_AVAIL, status);
 
@@ -276,23 +277,23 @@
 TEST_F(FPDFDataAvailEmbedderTest, LoadInfoAfterReceivingWholeDocument) {
   TestAsyncLoader loader("linearized.pdf");
   loader.set_is_new_data_available(false);
-  avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
-  while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail_, loader.hints())) {
+  CreateAvail(loader.file_avail(), loader.file_access());
+  while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail(), loader.hints())) {
     loader.FlushRequestedData();
   }
 
-  document_ = FPDFAvail_GetDocument(avail_, nullptr);
-  ASSERT_TRUE(document_);
+  SetDocumentFromAvail();
+  ASSERT_TRUE(document());
 
   // The "info" dictionary should still be unavailable.
-  EXPECT_FALSE(FPDF_GetMetaText(document_, "CreationDate", nullptr, 0));
+  EXPECT_FALSE(FPDF_GetMetaText(document(), "CreationDate", nullptr, 0));
 
   // Simulate receiving whole file.
   loader.set_is_new_data_available(true);
   // Load second page, to parse additional crossref sections.
-  EXPECT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail_, 1, loader.hints()));
+  EXPECT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail(), 1, loader.hints()));
 
-  EXPECT_TRUE(FPDF_GetMetaText(document_, "CreationDate", nullptr, 0));
+  EXPECT_TRUE(FPDF_GetMetaText(document(), "CreationDate", nullptr, 0));
 }
 
 TEST_F(FPDFDataAvailEmbedderTest, LoadInfoAfterReceivingFirstPage) {
@@ -300,79 +301,77 @@
   // Map "Info" to an object within the first section without breaking
   // linearization.
   ByteString data(loader.file_contents(), loader.file_length());
-  Optional<size_t> index = data.Find("/Info 27 0 R");
+  absl::optional<size_t> index = data.Find("/Info 27 0 R");
   ASSERT_TRUE(index);
   memcpy(loader.file_contents() + *index, "/Info 29 0 R", 12);
 
   loader.set_is_new_data_available(false);
-  avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
-  while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail_, loader.hints())) {
+  CreateAvail(loader.file_avail(), loader.file_access());
+  while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail(), loader.hints())) {
     loader.FlushRequestedData();
   }
 
-  document_ = FPDFAvail_GetDocument(avail_, nullptr);
-  ASSERT_TRUE(document_);
+  SetDocumentFromAvail();
+  ASSERT_TRUE(document());
 
   // The "Info" dictionary should be available for the linearized document, if
   // it is located in the first page section.
   // Info was remapped to a dictionary with Type "Catalog"
   unsigned short buffer[100] = {0};
-  EXPECT_TRUE(FPDF_GetMetaText(document_, "Type", buffer, sizeof(buffer)));
-  constexpr wchar_t kExpectedValue[] = L"Catalog";
-  EXPECT_EQ(WideString(kExpectedValue),
-            WideString::FromUTF16LE(buffer, FXSYS_len(kExpectedValue)));
+  EXPECT_TRUE(FPDF_GetMetaText(document(), "Type", buffer, sizeof(buffer)));
+  EXPECT_EQ(L"Catalog", GetPlatformWString(buffer));
 }
 
 TEST_F(FPDFDataAvailEmbedderTest, TryLoadInvalidInfo) {
   TestAsyncLoader loader("linearized.pdf");
   // Map "Info" to an invalid object without breaking linearization.
   ByteString data(loader.file_contents(), loader.file_length());
-  Optional<size_t> index = data.Find("/Info 27 0 R");
+  absl::optional<size_t> index = data.Find("/Info 27 0 R");
   ASSERT_TRUE(index);
   memcpy(loader.file_contents() + *index, "/Info 99 0 R", 12);
 
   loader.set_is_new_data_available(false);
-  avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
-  while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail_, loader.hints())) {
+  CreateAvail(loader.file_avail(), loader.file_access());
+  while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail(), loader.hints())) {
     loader.FlushRequestedData();
   }
 
-  document_ = FPDFAvail_GetDocument(avail_, nullptr);
-  ASSERT_TRUE(document_);
+  SetDocumentFromAvail();
+  ASSERT_TRUE(document());
 
   // Set all data available.
   loader.set_is_new_data_available(true);
   // Check second page, to load additional crossrefs.
-  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail_, 0, loader.hints()));
+  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail(), 0, loader.hints()));
 
   // Test that api is robust enough to handle the bad case.
-  EXPECT_FALSE(FPDF_GetMetaText(document_, "Type", nullptr, 0));
+  EXPECT_FALSE(FPDF_GetMetaText(document(), "Type", nullptr, 0));
 }
 
 TEST_F(FPDFDataAvailEmbedderTest, TryLoadNonExistsInfo) {
   TestAsyncLoader loader("linearized.pdf");
   // Break the "Info" parameter without breaking linearization.
   ByteString data(loader.file_contents(), loader.file_length());
-  Optional<size_t> index = data.Find("/Info 27 0 R");
+  absl::optional<size_t> index = data.Find("/Info 27 0 R");
   ASSERT_TRUE(index);
   memcpy(loader.file_contents() + *index, "/I_fo 27 0 R", 12);
 
   loader.set_is_new_data_available(false);
-  avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
-  while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail_, loader.hints())) {
+  CreateAvail(loader.file_avail(), loader.file_access());
+  while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail(), loader.hints())) {
     loader.FlushRequestedData();
   }
 
-  document_ = FPDFAvail_GetDocument(avail_, nullptr);
-  ASSERT_TRUE(document_);
+  SetDocumentFromAvail();
+  ASSERT_TRUE(document());
 
   // Set all data available.
   loader.set_is_new_data_available(true);
   // Check second page, to load additional crossrefs.
-  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail_, 0, loader.hints()));
+  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail(), 0, loader.hints()));
 
   // Test that api is robust enough to handle the bad case.
-  EXPECT_FALSE(FPDF_GetMetaText(document_, "Type", nullptr, 0));
+  EXPECT_FALSE(FPDF_GetMetaText(document(), "Type", nullptr, 0));
 }
 
 TEST_F(FPDFDataAvailEmbedderTest, BadInputsToAPIs) {
@@ -386,8 +385,22 @@
 
 TEST_F(FPDFDataAvailEmbedderTest, NegativePageIndex) {
   TestAsyncLoader loader("linearized.pdf");
-  avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
-  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail_, loader.hints()));
+  CreateAvail(loader.file_avail(), loader.file_access());
+  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
   EXPECT_EQ(PDF_DATA_NOTAVAIL,
-            FPDFAvail_IsPageAvail(avail_, -1, loader.hints()));
+            FPDFAvail_IsPageAvail(avail(), -1, loader.hints()));
+}
+
+TEST_F(FPDFDataAvailEmbedderTest, Bug_1324189) {
+  // Test passes if it doesn't crash.
+  TestAsyncLoader loader("bug_1324189.pdf");
+  CreateAvail(loader.file_avail(), loader.file_access());
+  ASSERT_EQ(PDF_DATA_NOTAVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
+}
+
+TEST_F(FPDFDataAvailEmbedderTest, Bug_1324503) {
+  // Test passes if it doesn't crash.
+  TestAsyncLoader loader("bug_1324503.pdf");
+  CreateAvail(loader.file_avail(), loader.file_access());
+  ASSERT_EQ(PDF_DATA_NOTAVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
 }
diff --git a/fpdfsdk/fpdf_doc.cpp b/fpdfsdk/fpdf_doc.cpp
index b0a2324..4f57144 100644
--- a/fpdfsdk/fpdf_doc.cpp
+++ b/fpdfsdk/fpdf_doc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,19 +10,26 @@
 #include <set>
 #include <utility>
 
+#include "constants/form_fields.h"
+#include "core/fpdfapi/page/cpdf_annotcontext.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "core/fpdfdoc/cpdf_aaction.h"
 #include "core/fpdfdoc/cpdf_bookmark.h"
 #include "core/fpdfdoc/cpdf_bookmarktree.h"
 #include "core/fpdfdoc/cpdf_dest.h"
 #include "core/fpdfdoc/cpdf_linklist.h"
 #include "core/fpdfdoc/cpdf_pagelabel.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "public/fpdf_formfill.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
 
@@ -31,7 +38,7 @@
                            const WideString& title,
                            std::set<const CPDF_Dictionary*>* visited) {
   // Return if already checked to avoid circular calling.
-  if (pdfium::ContainsKey(*visited, bookmark.GetDict()))
+  if (pdfium::Contains(*visited, bookmark.GetDict()))
     return CPDF_Bookmark();
   visited->insert(bookmark.GetDict());
 
@@ -42,13 +49,13 @@
   }
 
   // Go into children items.
-  CPDF_Bookmark child = tree.GetFirstChild(&bookmark);
-  while (child.GetDict() && !pdfium::ContainsKey(*visited, child.GetDict())) {
+  CPDF_Bookmark child = tree.GetFirstChild(bookmark);
+  while (child.GetDict() && !pdfium::Contains(*visited, child.GetDict())) {
     // Check this item and its children.
     CPDF_Bookmark found = FindBookmark(tree, child, title, visited);
     if (found.GetDict())
       return found;
-    child = tree.GetNextSibling(&child);
+    child = tree.GetNextSibling(child);
   }
   return CPDF_Bookmark();
 }
@@ -59,7 +66,7 @@
   if (pList)
     return pList;
 
-  auto pNewList = pdfium::MakeUnique<CPDF_LinkList>();
+  auto pNewList = std::make_unique<CPDF_LinkList>();
   pList = pNewList.get();
   pDoc->SetLinksContext(std::move(pNewList));
   return pList;
@@ -73,9 +80,10 @@
   if (!pDoc)
     return nullptr;
   CPDF_BookmarkTree tree(pDoc);
-  CPDF_Bookmark cBookmark(CPDFDictionaryFromFPDFBookmark(bookmark));
+  CPDF_Bookmark cBookmark(
+      pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark)));
   return FPDFBookmarkFromCPDFDictionary(
-      tree.GetFirstChild(&cBookmark).GetDict());
+      tree.GetFirstChild(cBookmark).GetDict());
 }
 
 FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
@@ -88,9 +96,10 @@
     return nullptr;
 
   CPDF_BookmarkTree tree(pDoc);
-  CPDF_Bookmark cBookmark(CPDFDictionaryFromFPDFBookmark(bookmark));
+  CPDF_Bookmark cBookmark(
+      pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark)));
   return FPDFBookmarkFromCPDFDictionary(
-      tree.GetNextSibling(&cBookmark).GetDict());
+      tree.GetNextSibling(cBookmark).GetDict());
 }
 
 FPDF_EXPORT unsigned long FPDF_CALLCONV
@@ -99,11 +108,20 @@
                       unsigned long buflen) {
   if (!bookmark)
     return 0;
-  CPDF_Bookmark cBookmark(CPDFDictionaryFromFPDFBookmark(bookmark));
+  CPDF_Bookmark cBookmark(
+      pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark)));
   WideString title = cBookmark.GetTitle();
   return Utf16EncodeMaybeCopyAndReturnLength(title, buffer, buflen);
 }
 
+FPDF_EXPORT int FPDF_CALLCONV FPDFBookmark_GetCount(FPDF_BOOKMARK bookmark) {
+  if (!bookmark)
+    return 0;
+  CPDF_Bookmark cBookmark(
+      pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark)));
+  return cBookmark.GetCount();
+}
+
 FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
 FPDFBookmark_Find(FPDF_DOCUMENT document, FPDF_WIDESTRING title) {
   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
@@ -129,14 +147,15 @@
   if (!bookmark)
     return nullptr;
 
-  CPDF_Bookmark cBookmark(CPDFDictionaryFromFPDFBookmark(bookmark));
+  CPDF_Bookmark cBookmark(
+      pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark)));
   CPDF_Dest dest = cBookmark.GetDest(pDoc);
   if (dest.GetArray())
     return FPDFDestFromCPDFArray(dest.GetArray());
   // If this bookmark is not directly associated with a dest, we try to get
   // action
   CPDF_Action action = cBookmark.GetAction();
-  if (!action.GetDict())
+  if (!action.HasDict())
     return nullptr;
   return FPDFDestFromCPDFArray(action.GetDest(pDoc).GetArray());
 }
@@ -146,7 +165,8 @@
   if (!bookmark)
     return nullptr;
 
-  CPDF_Bookmark cBookmark(CPDFDictionaryFromFPDFBookmark(bookmark));
+  CPDF_Bookmark cBookmark(
+      pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark)));
   return FPDFActionFromCPDFDictionary(cBookmark.GetAction().GetDict());
 }
 
@@ -154,16 +174,17 @@
   if (!action)
     return PDFACTION_UNSUPPORTED;
 
-  CPDF_Action cAction(CPDFDictionaryFromFPDFAction(action));
-  CPDF_Action::ActionType type = cAction.GetType();
-  switch (type) {
-    case CPDF_Action::GoTo:
+  CPDF_Action cAction(pdfium::WrapRetain(CPDFDictionaryFromFPDFAction(action)));
+  switch (cAction.GetType()) {
+    case CPDF_Action::Type::kGoTo:
       return PDFACTION_GOTO;
-    case CPDF_Action::GoToR:
+    case CPDF_Action::Type::kGoToR:
       return PDFACTION_REMOTEGOTO;
-    case CPDF_Action::URI:
+    case CPDF_Action::Type::kGoToE:
+      return PDFACTION_EMBEDDEDGOTO;
+    case CPDF_Action::Type::kURI:
       return PDFACTION_URI;
-    case CPDF_Action::Launch:
+    case CPDF_Action::Type::kLaunch:
       return PDFACTION_LAUNCH;
     default:
       return PDFACTION_UNSUPPORTED;
@@ -177,25 +198,25 @@
     return nullptr;
 
   unsigned long type = FPDFAction_GetType(action);
-  if (type != PDFACTION_GOTO && type != PDFACTION_REMOTEGOTO)
+  if (type != PDFACTION_GOTO && type != PDFACTION_REMOTEGOTO &&
+      type != PDFACTION_EMBEDDEDGOTO) {
     return nullptr;
-
-  CPDF_Action cAction(CPDFDictionaryFromFPDFAction(action));
+  }
+  CPDF_Action cAction(pdfium::WrapRetain(CPDFDictionaryFromFPDFAction(action)));
   return FPDFDestFromCPDFArray(cAction.GetDest(pDoc).GetArray());
 }
 
 FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDFAction_GetFilePath(FPDF_ACTION action, void* buffer, unsigned long buflen) {
   unsigned long type = FPDFAction_GetType(action);
-  if (type != PDFACTION_REMOTEGOTO && type != PDFACTION_LAUNCH)
+  if (type != PDFACTION_REMOTEGOTO && type != PDFACTION_EMBEDDEDGOTO &&
+      type != PDFACTION_LAUNCH) {
     return 0;
+  }
 
-  CPDF_Action cAction(CPDFDictionaryFromFPDFAction(action));
+  CPDF_Action cAction(pdfium::WrapRetain(CPDFDictionaryFromFPDFAction(action)));
   ByteString path = cAction.GetFilePath().ToUTF8();
-  unsigned long len = path.GetLength() + 1;
-  if (buffer && len <= buflen)
-    memcpy(buffer, path.c_str(), len);
-  return len;
+  return NulTerminateMaybeCopyAndReturnLength(path, buffer, buflen);
 }
 
 FPDF_EXPORT unsigned long FPDF_CALLCONV
@@ -211,9 +232,11 @@
   if (type != PDFACTION_URI)
     return 0;
 
-  CPDF_Action cAction(CPDFDictionaryFromFPDFAction(action));
+  CPDF_Action cAction(pdfium::WrapRetain(CPDFDictionaryFromFPDFAction(action)));
   ByteString path = cAction.GetURI(pDoc);
-  unsigned long len = path.GetLength() + 1;
+
+  const unsigned long len =
+      pdfium::base::checked_cast<unsigned long>(path.GetLength() + 1);
   if (buffer && len <= buflen)
     memcpy(buffer, path.c_str(), len);
   return len;
@@ -228,7 +251,7 @@
   if (!dest)
     return -1;
 
-  CPDF_Dest destination(CPDFArrayFromFPDFDest(dest));
+  CPDF_Dest destination(pdfium::WrapRetain(CPDFArrayFromFPDFDest(dest)));
   return destination.GetDestPageIndex(pDoc);
 }
 
@@ -239,9 +262,10 @@
     return 0;
   }
 
-  CPDF_Dest destination(CPDFArrayFromFPDFDest(dest));
-  unsigned long nParams = destination.GetNumParams();
-  ASSERT(nParams <= 4);
+  CPDF_Dest destination(pdfium::WrapRetain(CPDFArrayFromFPDFDest(dest)));
+  const unsigned long nParams =
+      pdfium::base::checked_cast<unsigned long>(destination.GetNumParams());
+  DCHECK(nParams <= 4);
   *pNumParams = nParams;
   for (unsigned long i = 0; i < nParams; ++i)
     pParams[i] = destination.GetParam(i);
@@ -259,13 +283,13 @@
   if (!dest)
     return false;
 
-  auto destination = pdfium::MakeUnique<CPDF_Dest>(CPDFArrayFromFPDFDest(dest));
+  CPDF_Dest destination(pdfium::WrapRetain(CPDFArrayFromFPDFDest(dest)));
 
   // FPDF_BOOL is an int, GetXYZ expects bools.
   bool bHasX;
   bool bHasY;
   bool bHasZoom;
-  if (!destination->GetXYZ(&bHasX, &bHasY, &bHasZoom, x, y, zoom))
+  if (!destination.GetXYZ(&bHasX, &bHasY, &bHasZoom, x, y, zoom))
     return false;
 
   *hasXVal = bHasX;
@@ -288,7 +312,8 @@
   CPDF_Link link = pLinkList->GetLinkAtPoint(
       pPage, CFX_PointF(static_cast<float>(x), static_cast<float>(y)), nullptr);
 
-  return FPDFLinkFromCPDFDictionary(link.GetDict());
+  // Unretained reference in public API. NOLINTNEXTLINE
+  return FPDFLinkFromCPDFDictionary(link.GetMutableDict());
 }
 
 FPDF_EXPORT int FPDF_CALLCONV FPDFLink_GetLinkZOrderAtPoint(FPDF_PAGE page,
@@ -316,13 +341,13 @@
   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
   if (!pDoc)
     return nullptr;
-  CPDF_Link cLink(CPDFDictionaryFromFPDFLink(link));
+  CPDF_Link cLink(pdfium::WrapRetain(CPDFDictionaryFromFPDFLink(link)));
   FPDF_DEST dest = FPDFDestFromCPDFArray(cLink.GetDest(pDoc).GetArray());
   if (dest)
     return dest;
   // If this link is not directly associated with a dest, we try to get action
   CPDF_Action action = cLink.GetAction();
-  if (!action.GetDict())
+  if (!action.HasDict())
     return nullptr;
   return FPDFDestFromCPDFArray(action.GetDest(pDoc).GetArray());
 }
@@ -331,7 +356,7 @@
   if (!link)
     return nullptr;
 
-  CPDF_Link cLink(CPDFDictionaryFromFPDFLink(link));
+  CPDF_Link cLink(pdfium::WrapRetain(CPDFDictionaryFromFPDFLink(link)));
   return FPDFActionFromCPDFDictionary(cLink.GetAction().GetDict());
 }
 
@@ -343,22 +368,37 @@
   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
   if (!pPage)
     return false;
-  CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots");
+  RetainPtr<CPDF_Array> pAnnots = pPage->GetMutableAnnotsArray();
   if (!pAnnots)
     return false;
   for (size_t i = *start_pos; i < pAnnots->size(); i++) {
-    CPDF_Dictionary* pDict = ToDictionary(pAnnots->GetDirectObjectAt(i));
+    RetainPtr<CPDF_Dictionary> pDict =
+        ToDictionary(pAnnots->GetMutableDirectObjectAt(i));
     if (!pDict)
       continue;
-    if (pDict->GetStringFor("Subtype") == "Link") {
+    if (pDict->GetByteStringFor("Subtype") == "Link") {
       *start_pos = static_cast<int>(i + 1);
-      *link_annot = FPDFLinkFromCPDFDictionary(pDict);
+      *link_annot = FPDFLinkFromCPDFDictionary(pDict.Get());
       return true;
     }
   }
   return false;
 }
 
+FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
+FPDFLink_GetAnnot(FPDF_PAGE page, FPDF_LINK link_annot) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  RetainPtr<CPDF_Dictionary> pAnnotDict(CPDFDictionaryFromFPDFLink(link_annot));
+  if (!pPage || !pAnnotDict)
+    return nullptr;
+
+  auto pAnnotContext = std::make_unique<CPDF_AnnotContext>(
+      std::move(pAnnotDict), IPDFPageFromFPDFPage(page));
+
+  // Caller takes the ownership of the object.
+  return FPDFAnnotationFromCPDFAnnotContext(pAnnotContext.release());
+}
+
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetAnnotRect(FPDF_LINK link_annot,
                                                           FS_RECTF* rect) {
   if (!link_annot || !rect)
@@ -370,7 +410,7 @@
 }
 
 FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountQuadPoints(FPDF_LINK link_annot) {
-  const CPDF_Array* pArray =
+  RetainPtr<const CPDF_Array> pArray =
       GetQuadPointsArrayFromDictionary(CPDFDictionaryFromFPDFLink(link_annot));
   return pArray ? static_cast<int>(pArray->size() / 8) : 0;
 }
@@ -386,12 +426,62 @@
   if (!pLinkDict)
     return false;
 
-  const CPDF_Array* pArray = GetQuadPointsArrayFromDictionary(pLinkDict);
+  RetainPtr<const CPDF_Array> pArray =
+      GetQuadPointsArrayFromDictionary(pLinkDict);
   if (!pArray)
     return false;
 
-  return GetQuadPointsAtIndex(pArray, static_cast<size_t>(quad_index),
-                              quad_points);
+  return GetQuadPointsAtIndex(std::move(pArray),
+                              static_cast<size_t>(quad_index), quad_points);
+}
+
+FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV FPDF_GetPageAAction(FPDF_PAGE page,
+                                                          int aa_type) {
+  CPDF_Page* pdf_page = CPDFPageFromFPDFPage(page);
+  if (!pdf_page)
+    return nullptr;
+
+  CPDF_AAction aa(pdf_page->GetDict()->GetDictFor(pdfium::form_fields::kAA));
+  CPDF_AAction::AActionType type;
+  if (aa_type == FPDFPAGE_AACTION_OPEN)
+    type = CPDF_AAction::kOpenPage;
+  else if (aa_type == FPDFPAGE_AACTION_CLOSE)
+    type = CPDF_AAction::kClosePage;
+  else
+    return nullptr;
+
+  if (!aa.ActionExist(type))
+    return nullptr;
+
+  CPDF_Action action = aa.GetAction(type);
+  return FPDFActionFromCPDFDictionary(action.GetDict());
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_GetFileIdentifier(FPDF_DOCUMENT document,
+                       FPDF_FILEIDTYPE id_type,
+                       void* buffer,
+                       unsigned long buflen) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return 0;
+
+  // Check if |id_type| is valid.
+  if (id_type != FILEIDTYPE_PERMANENT && id_type != FILEIDTYPE_CHANGING)
+    return 0;
+
+  RetainPtr<const CPDF_Array> pFileId = pDoc->GetFileIdentifier();
+  if (!pFileId)
+    return 0;
+
+  size_t nIndex = id_type == FILEIDTYPE_PERMANENT ? 0 : 1;
+  RetainPtr<const CPDF_String> pValue =
+      ToString(pFileId->GetDirectObjectAt(nIndex));
+  if (!pValue)
+    return 0;
+
+  return NulTerminateMaybeCopyAndReturnLength(pValue->GetString(), buffer,
+                                              buflen);
 }
 
 FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetMetaText(FPDF_DOCUMENT document,
@@ -404,9 +494,10 @@
   if (!pDoc)
     return 0;
 
-  const CPDF_Dictionary* pInfo = pDoc->GetInfo();
+  RetainPtr<const CPDF_Dictionary> pInfo = pDoc->GetInfo();
   if (!pInfo)
     return 0;
+
   WideString text = pInfo->GetUnicodeTextFor(tag);
   return Utf16EncodeMaybeCopyAndReturnLength(text, buffer, buflen);
 }
@@ -421,7 +512,7 @@
 
   // CPDF_PageLabel can deal with NULL |document|.
   CPDF_PageLabel label(CPDFDocumentFromFPDFDocument(document));
-  Optional<WideString> str = label.GetLabel(page_index);
+  absl::optional<WideString> str = label.GetLabel(page_index);
   return str.has_value()
              ? Utf16EncodeMaybeCopyAndReturnLength(str.value(), buffer, buflen)
              : 0;
diff --git a/fpdfsdk/fpdf_doc_embeddertest.cpp b/fpdfsdk/fpdf_doc_embeddertest.cpp
index 84712ed..f06e596 100644
--- a/fpdfsdk/fpdf_doc_embeddertest.cpp
+++ b/fpdfsdk/fpdf_doc_embeddertest.cpp
@@ -1,14 +1,12 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
 #include <set>
-#include <string>
 #include <vector>
 
 #include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/bytestring.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "public/cpp/fpdf_scopers.h"
 #include "public/fpdf_doc.h"
@@ -21,7 +19,7 @@
 class FPDFDocEmbedderTest : public EmbedderTest {};
 
 TEST_F(FPDFDocEmbedderTest, MultipleSamePage) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document());
 
   std::set<FPDF_PAGE> unique_pages;
@@ -40,7 +38,7 @@
 }
 
 TEST_F(FPDFDocEmbedderTest, DestGetPageIndex) {
-  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
+  ASSERT_TRUE(OpenDocument("named_dests.pdf"));
 
   // NULL argument cases.
   EXPECT_EQ(-1, FPDFDest_GetDestPageIndex(nullptr, nullptr));
@@ -68,7 +66,7 @@
 }
 
 TEST_F(FPDFDocEmbedderTest, DestGetView) {
-  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
+  ASSERT_TRUE(OpenDocument("named_dests.pdf"));
 
   unsigned long numParams;
   FS_FLOAT params[4];
@@ -127,7 +125,7 @@
 }
 
 TEST_F(FPDFDocEmbedderTest, DestGetLocationInPage) {
-  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
+  ASSERT_TRUE(OpenDocument("named_dests.pdf"));
 
   FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First");
   EXPECT_TRUE(dest);
@@ -154,8 +152,46 @@
   EXPECT_EQ(1, zoom);
 }
 
+TEST_F(FPDFDocEmbedderTest, BUG_1506_1) {
+  ASSERT_TRUE(OpenDocument("bug_1506.pdf"));
+
+  FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First");
+  ASSERT_TRUE(dest);
+  EXPECT_EQ(3, FPDFDest_GetDestPageIndex(document(), dest));
+}
+
+TEST_F(FPDFDocEmbedderTest, BUG_1506_2) {
+  ASSERT_TRUE(OpenDocument("bug_1506.pdf"));
+
+  std::vector<FPDF_PAGE> pages;
+  for (int i : {0, 2})
+    pages.push_back(LoadPage(i));
+
+  FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First");
+  ASSERT_TRUE(dest);
+  EXPECT_EQ(3, FPDFDest_GetDestPageIndex(document(), dest));
+
+  for (FPDF_PAGE page : pages)
+    UnloadPage(page);
+}
+
+TEST_F(FPDFDocEmbedderTest, BUG_1506_3) {
+  ASSERT_TRUE(OpenDocument("bug_1506.pdf"));
+
+  std::vector<FPDF_PAGE> pages;
+  for (int i : {0, 1, 3})
+    pages.push_back(LoadPage(i));
+
+  FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First");
+  ASSERT_TRUE(dest);
+  EXPECT_EQ(3, FPDFDest_GetDestPageIndex(document(), dest));
+
+  for (FPDF_PAGE page : pages)
+    UnloadPage(page);
+}
+
 TEST_F(FPDFDocEmbedderTest, BUG_680376) {
-  EXPECT_TRUE(OpenDocument("bug_680376.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_680376.pdf"));
 
   // Page number directly in item from Dests NameTree.
   FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First");
@@ -164,12 +200,12 @@
 }
 
 TEST_F(FPDFDocEmbedderTest, BUG_821454) {
-  EXPECT_TRUE(OpenDocument("bug_821454.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_821454.pdf"));
 
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
-  // Cover some NULL arg cases while we're at it.
+  // Cover some invalid argument cases while we're at it.
   EXPECT_FALSE(FPDFLink_GetLinkAtPoint(nullptr, 150, 360));
   EXPECT_EQ(-1, FPDFLink_GetLinkZOrderAtPoint(nullptr, 150, 360));
 
@@ -186,6 +222,11 @@
   FPDF_DEST dest2 = FPDFLink_GetDest(document(), link2);
   ASSERT_TRUE(dest2);
 
+  // Cover more invalid argument cases while we're at it.
+  EXPECT_FALSE(FPDFLink_GetDest(nullptr, nullptr));
+  EXPECT_FALSE(FPDFLink_GetDest(nullptr, link1));
+  EXPECT_FALSE(FPDFLink_GetDest(document(), nullptr));
+
   EXPECT_EQ(0, FPDFDest_GetDestPageIndex(document(), dest1));
   EXPECT_EQ(0, FPDFDest_GetDestPageIndex(document(), dest2));
 
@@ -226,19 +267,19 @@
 }
 
 TEST_F(FPDFDocEmbedderTest, ActionBadArguments) {
-  EXPECT_TRUE(OpenDocument("launch_action.pdf"));
+  ASSERT_TRUE(OpenDocument("launch_action.pdf"));
   EXPECT_EQ(static_cast<unsigned long>(PDFACTION_UNSUPPORTED),
             FPDFAction_GetType(nullptr));
 
-  EXPECT_EQ(nullptr, FPDFAction_GetDest(nullptr, nullptr));
-  EXPECT_EQ(nullptr, FPDFAction_GetDest(document(), nullptr));
+  EXPECT_FALSE(FPDFAction_GetDest(nullptr, nullptr));
+  EXPECT_FALSE(FPDFAction_GetDest(document(), nullptr));
   EXPECT_EQ(0u, FPDFAction_GetFilePath(nullptr, nullptr, 0));
   EXPECT_EQ(0u, FPDFAction_GetURIPath(nullptr, nullptr, nullptr, 0));
   EXPECT_EQ(0u, FPDFAction_GetURIPath(document(), nullptr, nullptr, 0));
 }
 
 TEST_F(FPDFDocEmbedderTest, ActionLaunch) {
-  EXPECT_TRUE(OpenDocument("launch_action.pdf"));
+  ASSERT_TRUE(OpenDocument("launch_action.pdf"));
 
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
@@ -262,14 +303,14 @@
   EXPECT_STREQ(kExpectedResult, buf);
 
   // Other public methods are not appropriate for launch actions.
-  EXPECT_EQ(nullptr, FPDFAction_GetDest(document(), action));
+  EXPECT_FALSE(FPDFAction_GetDest(document(), action));
   EXPECT_EQ(0u, FPDFAction_GetURIPath(document(), action, buf, sizeof(buf)));
 
   UnloadPage(page);
 }
 
-TEST_F(FPDFDocEmbedderTest, ActionURI) {
-  EXPECT_TRUE(OpenDocument("uri_action.pdf"));
+TEST_F(FPDFDocEmbedderTest, ActionUri) {
+  ASSERT_TRUE(OpenDocument("uri_action.pdf"));
 
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
@@ -293,14 +334,67 @@
   EXPECT_STREQ(kExpectedResult, buf);
 
   // Other public methods are not appropriate for URI actions
-  EXPECT_EQ(nullptr, FPDFAction_GetDest(document(), action));
+  EXPECT_FALSE(FPDFAction_GetDest(document(), action));
   EXPECT_EQ(0u, FPDFAction_GetFilePath(action, buf, sizeof(buf)));
 
   UnloadPage(page);
 }
 
+TEST_F(FPDFDocEmbedderTest, ActionUriNonAscii) {
+  ASSERT_TRUE(OpenDocument("uri_action_nonascii.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // The target action is nearly the size of the whole page.
+  FPDF_LINK link = FPDFLink_GetLinkAtPoint(page, 100, 100);
+  ASSERT_TRUE(link);
+
+  FPDF_ACTION action = FPDFLink_GetAction(link);
+  ASSERT_TRUE(action);
+  EXPECT_EQ(static_cast<unsigned long>(PDFACTION_URI),
+            FPDFAction_GetType(action));
+
+  // FPDFAction_GetURIPath() may return data in any encoding, or even with bad
+  // encoding.
+  const char kExpectedResult[] =
+      "https://example.com/\xA5octal\xC7"
+      "chars";
+  const unsigned long kExpectedLength = sizeof(kExpectedResult);
+  unsigned long bufsize = FPDFAction_GetURIPath(document(), action, nullptr, 0);
+  ASSERT_EQ(kExpectedLength, bufsize);
+
+  char buf[1024];
+  EXPECT_EQ(bufsize, FPDFAction_GetURIPath(document(), action, buf, bufsize));
+  EXPECT_STREQ(kExpectedResult, buf);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFDocEmbedderTest, LinkToAnnotConversion) {
+  ASSERT_TRUE(OpenDocument("annots.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  {
+    FPDF_LINK first_link = FPDFLink_GetLinkAtPoint(page, 69.00, 653.00);
+    ScopedFPDFAnnotation first_annot(FPDFLink_GetAnnot(page, first_link));
+    EXPECT_EQ(0, FPDFPage_GetAnnotIndex(page, first_annot.get()));
+
+    FPDF_LINK second_link = FPDFLink_GetLinkAtPoint(page, 80.00, 633.00);
+    ScopedFPDFAnnotation second_annot(FPDFLink_GetAnnot(page, second_link));
+    EXPECT_EQ(1, FPDFPage_GetAnnotIndex(page, second_annot.get()));
+
+    // Also test invalid arguments.
+    EXPECT_FALSE(FPDFLink_GetAnnot(nullptr, nullptr));
+    EXPECT_FALSE(FPDFLink_GetAnnot(page, nullptr));
+    EXPECT_FALSE(FPDFLink_GetAnnot(nullptr, second_link));
+  }
+
+  UnloadPage(page);
+}
+
 TEST_F(FPDFDocEmbedderTest, ActionGoto) {
-  EXPECT_TRUE(OpenDocument("goto_action.pdf"));
+  ASSERT_TRUE(OpenDocument("goto_action.pdf"));
 
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
@@ -324,8 +418,45 @@
   UnloadPage(page);
 }
 
+TEST_F(FPDFDocEmbedderTest, ActionEmbeddedGoto) {
+  ASSERT_TRUE(OpenDocument("gotoe_action.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // The target action is nearly the size of the whole page.
+  FPDF_LINK link = FPDFLink_GetLinkAtPoint(page, 100, 100);
+  ASSERT_TRUE(link);
+
+  FPDF_ACTION action = FPDFLink_GetAction(link);
+  ASSERT_TRUE(action);
+  EXPECT_EQ(static_cast<unsigned long>(PDFACTION_EMBEDDEDGOTO),
+            FPDFAction_GetType(action));
+
+  FPDF_DEST dest = FPDFAction_GetDest(document(), action);
+  EXPECT_TRUE(dest);
+
+  unsigned long num_params = 42;
+  FS_FLOAT params[4];
+  std::fill_n(params, 4, 42.4242f);
+  EXPECT_EQ(static_cast<unsigned long>(PDFDEST_VIEW_FIT),
+            FPDFDest_GetView(dest, &num_params, params));
+  EXPECT_EQ(0u, num_params);
+  EXPECT_FLOAT_EQ(42.4242f, params[0]);
+
+  const char kExpectedResult[] = "ExampleFile.pdf";
+  const unsigned long kExpectedLength = sizeof(kExpectedResult);
+  char buf[1024];
+  unsigned long bufsize = FPDFAction_GetFilePath(action, nullptr, 0);
+  EXPECT_EQ(kExpectedLength, bufsize);
+  EXPECT_EQ(kExpectedLength, FPDFAction_GetFilePath(action, buf, bufsize));
+  EXPECT_STREQ(kExpectedResult, buf);
+
+  UnloadPage(page);
+}
+
 TEST_F(FPDFDocEmbedderTest, ActionNonesuch) {
-  EXPECT_TRUE(OpenDocument("nonesuch_action.pdf"));
+  ASSERT_TRUE(OpenDocument("nonesuch_action.pdf"));
 
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
@@ -352,54 +483,70 @@
   unsigned short buf[128];
 
   // Open a file with no bookmarks.
-  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
+  ASSERT_TRUE(OpenDocument("named_dests.pdf"));
 
   // NULL argument cases.
   EXPECT_EQ(0u, FPDFBookmark_GetTitle(nullptr, buf, sizeof(buf)));
-  EXPECT_EQ(nullptr, FPDFBookmark_GetFirstChild(nullptr, nullptr));
-  EXPECT_EQ(nullptr, FPDFBookmark_GetFirstChild(document(), nullptr));
-  EXPECT_EQ(nullptr, FPDFBookmark_GetNextSibling(nullptr, nullptr));
-  EXPECT_EQ(nullptr, FPDFBookmark_GetNextSibling(document(), nullptr));
-  EXPECT_EQ(nullptr, FPDFBookmark_Find(nullptr, nullptr));
-  EXPECT_EQ(nullptr, FPDFBookmark_Find(document(), nullptr));
-  EXPECT_EQ(nullptr, FPDFBookmark_GetDest(nullptr, nullptr));
-  EXPECT_EQ(nullptr, FPDFBookmark_GetDest(document(), nullptr));
-  EXPECT_EQ(nullptr, FPDFBookmark_GetAction(nullptr));
+  EXPECT_FALSE(FPDFBookmark_GetFirstChild(nullptr, nullptr));
+  EXPECT_FALSE(FPDFBookmark_GetFirstChild(document(), nullptr));
+  EXPECT_FALSE(FPDFBookmark_GetNextSibling(nullptr, nullptr));
+  EXPECT_FALSE(FPDFBookmark_GetNextSibling(document(), nullptr));
+  EXPECT_FALSE(FPDFBookmark_Find(nullptr, nullptr));
+  EXPECT_FALSE(FPDFBookmark_Find(document(), nullptr));
+  EXPECT_FALSE(FPDFBookmark_GetDest(nullptr, nullptr));
+  EXPECT_FALSE(FPDFBookmark_GetDest(document(), nullptr));
+  EXPECT_FALSE(FPDFBookmark_GetAction(nullptr));
 }
 
 TEST_F(FPDFDocEmbedderTest, Bookmarks) {
   unsigned short buf[128];
 
-  // Open a file with two bookmarks.
-  EXPECT_TRUE(OpenDocument("bookmarks.pdf"));
+  // Open a file with many bookmarks.
+  ASSERT_TRUE(OpenDocument("bookmarks.pdf"));
 
   FPDF_BOOKMARK child = FPDFBookmark_GetFirstChild(document(), nullptr);
   EXPECT_TRUE(child);
   EXPECT_EQ(34u, FPDFBookmark_GetTitle(child, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(L"A Good Beginning"), WideString::FromUTF16LE(buf, 16));
+  EXPECT_EQ(L"A Good Beginning", GetPlatformWString(buf));
+  EXPECT_EQ(0, FPDFBookmark_GetCount(child));
+  EXPECT_EQ(0, FPDFBookmark_GetCount(nullptr));
 
-  FPDF_DEST dest = FPDFBookmark_GetDest(document(), child);
-  EXPECT_FALSE(dest);  // TODO(tsepez): put real dest into bookmarks.pdf
-
-  FPDF_ACTION action = FPDFBookmark_GetAction(child);
-  EXPECT_FALSE(action);  // TODO(tsepez): put real action into bookmarks.pdf
+  EXPECT_FALSE(FPDFBookmark_GetDest(document(), child));
+  EXPECT_FALSE(FPDFBookmark_GetAction(child));
 
   FPDF_BOOKMARK grand_child = FPDFBookmark_GetFirstChild(document(), child);
   EXPECT_FALSE(grand_child);
 
   FPDF_BOOKMARK sibling = FPDFBookmark_GetNextSibling(document(), child);
   EXPECT_TRUE(sibling);
-  EXPECT_EQ(28u, FPDFBookmark_GetTitle(sibling, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(L"A Good Ending"), WideString::FromUTF16LE(buf, 13));
+  EXPECT_EQ(24u, FPDFBookmark_GetTitle(sibling, buf, sizeof(buf)));
+  EXPECT_EQ(L"Open Middle", GetPlatformWString(buf));
+  EXPECT_TRUE(FPDFBookmark_GetAction(sibling));
+  EXPECT_EQ(1, FPDFBookmark_GetCount(sibling));
 
-  EXPECT_EQ(nullptr, FPDFBookmark_GetNextSibling(document(), sibling));
+  FPDF_BOOKMARK sibling2 = FPDFBookmark_GetNextSibling(document(), sibling);
+  EXPECT_TRUE(sibling2);
+  EXPECT_EQ(42u, FPDFBookmark_GetTitle(sibling2, buf, sizeof(buf)));
+  EXPECT_EQ(L"A Good Closed Ending", GetPlatformWString(buf));
+  EXPECT_EQ(-2, FPDFBookmark_GetCount(sibling2));
+
+  EXPECT_FALSE(FPDFBookmark_GetNextSibling(document(), sibling2));
+
+  grand_child = FPDFBookmark_GetFirstChild(document(), sibling);
+  EXPECT_TRUE(grand_child);
+  EXPECT_EQ(46u, FPDFBookmark_GetTitle(grand_child, buf, sizeof(buf)));
+  EXPECT_EQ(L"Open Middle Descendant", GetPlatformWString(buf));
+  EXPECT_EQ(0, FPDFBookmark_GetCount(grand_child));
+  EXPECT_TRUE(FPDFBookmark_GetDest(document(), grand_child));
+
+  EXPECT_FALSE(FPDFBookmark_GetNextSibling(document(), grand_child));
 }
 
 TEST_F(FPDFDocEmbedderTest, FindBookmarks) {
   unsigned short buf[128];
 
-  // Open a file with two bookmarks.
-  EXPECT_TRUE(OpenDocument("bookmarks.pdf"));
+  // Open a file with many bookmarks.
+  ASSERT_TRUE(OpenDocument("bookmarks.pdf"));
 
   // Find the first one, based on its known title.
   ScopedFPDFWideString title = GetFPDFWideString(L"A Good Beginning");
@@ -408,28 +555,28 @@
 
   // Check that the string matches.
   EXPECT_EQ(34u, FPDFBookmark_GetTitle(child, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(L"A Good Beginning"), WideString::FromUTF16LE(buf, 16));
+  EXPECT_EQ(L"A Good Beginning", GetPlatformWString(buf));
 
   // Check that it is them same as the one returned by GetFirstChild.
   EXPECT_EQ(child, FPDFBookmark_GetFirstChild(document(), nullptr));
 
   // Try to find one using a non-existent title.
   ScopedFPDFWideString bad_title = GetFPDFWideString(L"A BAD Beginning");
-  EXPECT_EQ(nullptr, FPDFBookmark_Find(document(), bad_title.get()));
+  EXPECT_FALSE(FPDFBookmark_Find(document(), bad_title.get()));
 }
 
 // Check circular bookmarks will not cause infinite loop.
 TEST_F(FPDFDocEmbedderTest, FindBookmarks_bug420) {
   // Open a file with circular bookmarks.
-  EXPECT_TRUE(OpenDocument("bookmarks_circular.pdf"));
+  ASSERT_TRUE(OpenDocument("bookmarks_circular.pdf"));
 
   // Try to find a title.
   ScopedFPDFWideString title = GetFPDFWideString(L"anything");
-  EXPECT_EQ(nullptr, FPDFBookmark_Find(document(), title.get()));
+  EXPECT_FALSE(FPDFBookmark_Find(document(), title.get()));
 }
 
 TEST_F(FPDFDocEmbedderTest, DeletePage) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   EXPECT_EQ(1, FPDF_GetPageCount(document()));
 
   FPDFPage_Delete(nullptr, 0);
@@ -444,6 +591,57 @@
   EXPECT_EQ(0, FPDF_GetPageCount(document()));
 }
 
+TEST_F(FPDFDocEmbedderTest, GetFileIdentifier) {
+  ASSERT_TRUE(OpenDocument("split_streams.pdf"));
+  constexpr size_t kMd5Length = 17;
+  char buf[kMd5Length];
+  EXPECT_EQ(0u,
+            FPDF_GetFileIdentifier(document(), static_cast<FPDF_FILEIDTYPE>(-1),
+                                   buf, sizeof(buf)));
+  EXPECT_EQ(0u,
+            FPDF_GetFileIdentifier(document(), static_cast<FPDF_FILEIDTYPE>(2),
+                                   buf, sizeof(buf)));
+  EXPECT_EQ(0u, FPDF_GetFileIdentifier(nullptr, FILEIDTYPE_PERMANENT, buf,
+                                       sizeof(buf)));
+  EXPECT_EQ(kMd5Length, FPDF_GetFileIdentifier(document(), FILEIDTYPE_PERMANENT,
+                                               nullptr, 0));
+
+  constexpr char kExpectedPermanent[] =
+      "\xF3\x41\xAE\x65\x4A\x77\xAC\xD5\x06\x5A\x76\x45\xE5\x96\xE6\xE6";
+  ASSERT_EQ(kMd5Length, FPDF_GetFileIdentifier(document(), FILEIDTYPE_PERMANENT,
+                                               buf, sizeof(buf)));
+  EXPECT_EQ(kExpectedPermanent, ByteString(buf));
+
+  constexpr char kExpectedChanging[] =
+      "\xBC\x37\x29\x8A\x3F\x87\xF4\x79\x22\x9B\xCE\x99\x7C\xA7\x91\xF7";
+  ASSERT_EQ(kMd5Length, FPDF_GetFileIdentifier(document(), FILEIDTYPE_CHANGING,
+                                               buf, sizeof(buf)));
+  EXPECT_EQ(kExpectedChanging, ByteString(buf));
+}
+
+TEST_F(FPDFDocEmbedderTest, GetNonHexFileIdentifier) {
+  ASSERT_TRUE(OpenDocument("non_hex_file_id.pdf"));
+  char buf[18];
+
+  constexpr char kPermanentNonHex[] = "permanent non-hex";
+  ASSERT_EQ(18u, FPDF_GetFileIdentifier(document(), FILEIDTYPE_PERMANENT, buf,
+                                        sizeof(buf)));
+  EXPECT_EQ(kPermanentNonHex, ByteString(buf));
+
+  constexpr char kChangingNonHex[] = "changing non-hex";
+  ASSERT_EQ(17u, FPDF_GetFileIdentifier(document(), FILEIDTYPE_CHANGING, buf,
+                                        sizeof(buf)));
+  EXPECT_EQ(kChangingNonHex, ByteString(buf));
+}
+
+TEST_F(FPDFDocEmbedderTest, GetNonexistentFileIdentifier) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_EQ(
+      0u, FPDF_GetFileIdentifier(document(), FILEIDTYPE_PERMANENT, nullptr, 0));
+  EXPECT_EQ(
+      0u, FPDF_GetFileIdentifier(document(), FILEIDTYPE_CHANGING, nullptr, 0));
+}
+
 TEST_F(FPDFDocEmbedderTest, GetMetaText) {
   ASSERT_TRUE(OpenDocument("bug_601362.pdf"));
 
@@ -461,32 +659,24 @@
   ASSERT_EQ(2u, FPDF_GetMetaText(document(), "Keywords", buf, sizeof(buf)));
   ASSERT_EQ(2u, FPDF_GetMetaText(document(), "Producer", buf, sizeof(buf)));
 
-  constexpr wchar_t kExpectedCreator[] = L"Microsoft Word";
   ASSERT_EQ(30u, FPDF_GetMetaText(document(), "Creator", buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedCreator),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedCreator)));
+  EXPECT_EQ(L"Microsoft Word", GetPlatformWString(buf));
 
-  constexpr wchar_t kExpectedCreationDate[] = L"D:20160411190039+00'00'";
   ASSERT_EQ(48u,
             FPDF_GetMetaText(document(), "CreationDate", buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedCreationDate),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedCreationDate)));
+  EXPECT_EQ(L"D:20160411190039+00'00'", GetPlatformWString(buf));
 
-  constexpr wchar_t kExpectedModDate[] = L"D:20160411190039+00'00'";
   ASSERT_EQ(48u, FPDF_GetMetaText(document(), "ModDate", buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedModDate),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedModDate)));
+  EXPECT_EQ(L"D:20160411190039+00'00'", GetPlatformWString(buf));
 }
 
 TEST_F(FPDFDocEmbedderTest, Bug_182) {
   ASSERT_TRUE(OpenDocument("bug_182.pdf"));
 
   unsigned short buf[128];
-  constexpr wchar_t kExpectedTitle[] = L"Super Visual Formade 印刷";
 
   ASSERT_EQ(48u, FPDF_GetMetaText(document(), "Title", buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedTitle),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedTitle)));
+  EXPECT_EQ(L"Super Visual Formade 印刷", GetPlatformWString(buf));
 }
 
 TEST_F(FPDFDocEmbedderTest, GetMetaTextSameObjectNumber) {
@@ -496,10 +686,8 @@
   // (1 0). Both objects are /Info dictionaries, but contain different data.
   // Make sure ModDate is the date of the last modification.
   unsigned short buf[128];
-  constexpr wchar_t kExpectedModDate[] = L"D:20170612232940-04'00'";
   ASSERT_EQ(48u, FPDF_GetMetaText(document(), "ModDate", buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedModDate),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedModDate)));
+  EXPECT_EQ(L"D:20170612232940-04'00'", GetPlatformWString(buf));
 }
 
 TEST_F(FPDFDocEmbedderTest, GetMetaTextInAttachmentFile) {
@@ -507,10 +695,8 @@
 
   // Make sure this is the date from the PDF itself and not the attached PDF.
   unsigned short buf[128];
-  constexpr wchar_t kExpectedModDate[] = L"D:20170712214448-07'00'";
   ASSERT_EQ(48u, FPDF_GetMetaText(document(), "ModDate", buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedModDate),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedModDate)));
+  EXPECT_EQ(L"D:20170712214448-07'00'", GetPlatformWString(buf));
 }
 
 TEST_F(FPDFDocEmbedderTest, GetMetaTextFromNewDocument) {
@@ -520,15 +706,47 @@
   FPDF_CloseDocument(empty_doc);
 }
 
+TEST_F(FPDFDocEmbedderTest, GetPageAAction) {
+  ASSERT_TRUE(OpenDocument("get_page_aaction.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+
+  EXPECT_FALSE(FPDF_GetPageAAction(nullptr, FPDFPAGE_AACTION_OPEN));
+  EXPECT_FALSE(FPDF_GetPageAAction(page, FPDFPAGE_AACTION_CLOSE));
+  EXPECT_FALSE(FPDF_GetPageAAction(page, -1));
+  EXPECT_FALSE(FPDF_GetPageAAction(page, 999));
+
+  FPDF_ACTION action = FPDF_GetPageAAction(page, FPDFPAGE_AACTION_OPEN);
+  EXPECT_EQ(static_cast<unsigned long>(PDFACTION_EMBEDDEDGOTO),
+            FPDFAction_GetType(action));
+
+  const char kExpectedResult[] = "\\\\127.0.0.1\\c$\\Program Files\\test.exe";
+  const unsigned long kExpectedLength = sizeof(kExpectedResult);
+  char buf[1024];
+
+  unsigned long bufsize = FPDFAction_GetFilePath(action, nullptr, 0);
+  EXPECT_EQ(kExpectedLength, bufsize);
+  EXPECT_EQ(kExpectedLength, FPDFAction_GetFilePath(action, buf, bufsize));
+  EXPECT_STREQ(kExpectedResult, buf);
+
+  UnloadPage(page);
+
+  page = LoadPage(1);
+  EXPECT_TRUE(page);
+  EXPECT_FALSE(FPDF_GetPageAAction(page, -1));
+
+  UnloadPage(page);
+}
+
 TEST_F(FPDFDocEmbedderTest, NoPageLabels) {
-  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
+  ASSERT_TRUE(OpenDocument("about_blank.pdf"));
   EXPECT_EQ(1, FPDF_GetPageCount(document()));
 
   ASSERT_EQ(0u, FPDF_GetPageLabel(document(), 0, nullptr, 0));
 }
 
 TEST_F(FPDFDocEmbedderTest, GetPageLabels) {
-  EXPECT_TRUE(OpenDocument("page_labels.pdf"));
+  ASSERT_TRUE(OpenDocument("page_labels.pdf"));
   EXPECT_EQ(7, FPDF_GetPageCount(document()));
 
   // We do not request labels, when use FPDFAvail_IsXXXAvail.
@@ -539,40 +757,26 @@
   EXPECT_EQ(0u, FPDF_GetPageLabel(document(), -2, buf, sizeof(buf)));
   EXPECT_EQ(0u, FPDF_GetPageLabel(document(), -1, buf, sizeof(buf)));
 
-  const wchar_t kExpectedPageLabel0[] = L"i";
   ASSERT_EQ(4u, FPDF_GetPageLabel(document(), 0, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedPageLabel0),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel0)));
+  EXPECT_EQ(L"i", GetPlatformWString(buf));
 
-  const wchar_t kExpectedPageLabel1[] = L"ii";
   ASSERT_EQ(6u, FPDF_GetPageLabel(document(), 1, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedPageLabel1),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel1)));
+  EXPECT_EQ(L"ii", GetPlatformWString(buf));
 
-  const wchar_t kExpectedPageLabel2[] = L"1";
   ASSERT_EQ(4u, FPDF_GetPageLabel(document(), 2, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedPageLabel2),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel2)));
+  EXPECT_EQ(L"1", GetPlatformWString(buf));
 
-  const wchar_t kExpectedPageLabel3[] = L"2";
   ASSERT_EQ(4u, FPDF_GetPageLabel(document(), 3, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedPageLabel3),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel3)));
+  EXPECT_EQ(L"2", GetPlatformWString(buf));
 
-  const wchar_t kExpectedPageLabel4[] = L"zzA";
   ASSERT_EQ(8u, FPDF_GetPageLabel(document(), 4, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedPageLabel4),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel4)));
+  EXPECT_EQ(L"zzA", GetPlatformWString(buf));
 
-  const wchar_t kExpectedPageLabel5[] = L"zzB";
   ASSERT_EQ(8u, FPDF_GetPageLabel(document(), 5, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedPageLabel5),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel5)));
+  EXPECT_EQ(L"zzB", GetPlatformWString(buf));
 
-  const wchar_t kExpectedPageLabel6[] = L"";
   ASSERT_EQ(2u, FPDF_GetPageLabel(document(), 6, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedPageLabel6),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel6)));
+  EXPECT_EQ(L"", GetPlatformWString(buf));
 
   ASSERT_EQ(0u, FPDF_GetPageLabel(document(), 7, buf, sizeof(buf)));
   ASSERT_EQ(0u, FPDF_GetPageLabel(document(), 8, buf, sizeof(buf)));
@@ -580,7 +784,7 @@
 
 #ifdef PDF_ENABLE_XFA
 TEST_F(FPDFDocEmbedderTest, GetXFALinks) {
-  EXPECT_TRUE(OpenDocument("simple_xfa.pdf"));
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
   ScopedFPDFPage page(FPDF_LoadPage(document(), 0));
   ASSERT_TRUE(page);
diff --git a/fpdfsdk/fpdf_doc_unittest.cpp b/fpdfsdk/fpdf_doc_unittest.cpp
index 2beadd4..6d45629 100644
--- a/fpdfsdk/fpdf_doc_unittest.cpp
+++ b/fpdfsdk/fpdf_doc_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,7 @@
 #include <memory>
 #include <vector>
 
-#include "core/fpdfapi/page/cpdf_docpagedata.h"
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fpdfapi/page/test_with_page_module.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
@@ -18,37 +17,26 @@
 #include "core/fpdfapi/parser/cpdf_parser.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfapi/render/cpdf_docrenderdata.h"
+#include "core/fpdfapi/parser/cpdf_test_document.h"
 #include "core/fpdfdoc/cpdf_dest.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "public/cpp/fpdf_scopers.h"
 #include "testing/fx_string_testhelpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
-class CPDF_TestDocument final : public CPDF_Document {
- public:
-  CPDF_TestDocument()
-      : CPDF_Document(pdfium::MakeUnique<CPDF_DocRenderData>(),
-                      pdfium::MakeUnique<CPDF_DocPageData>()) {}
-
-  void SetRoot(CPDF_Dictionary* root) { m_pRootDict.Reset(root); }
-  CPDF_IndirectObjectHolder* GetHolder() { return this; }
-};
-
-class PDFDocTest : public testing::Test {
+class PDFDocTest : public TestWithPageModule {
  public:
   struct DictObjInfo {
     uint32_t num;
-    CPDF_Dictionary* obj;
+    RetainPtr<CPDF_Dictionary> obj;
   };
 
   void SetUp() override {
-    CPDF_PageModule::Create();
-    auto pTestDoc = pdfium::MakeUnique<CPDF_TestDocument>();
-    m_pIndirectObjs = pTestDoc->GetHolder();
-    m_pRootObj.Reset(m_pIndirectObjs->NewIndirect<CPDF_Dictionary>());
-    pTestDoc->SetRoot(m_pRootObj.Get());
+    TestWithPageModule::SetUp();
+    auto pTestDoc = std::make_unique<CPDF_TestDocument>();
+    m_pIndirectObjs = pTestDoc.get();
+    m_pRootObj = m_pIndirectObjs->NewIndirect<CPDF_Dictionary>();
+    pTestDoc->SetRoot(m_pRootObj);
     m_pDoc.reset(FPDFDocumentFromCPDFDocument(pTestDoc.release()));
   }
 
@@ -56,14 +44,13 @@
     m_pRootObj = nullptr;
     m_pIndirectObjs = nullptr;
     m_pDoc.reset();
-    CPDF_PageModule::Destroy();
+    TestWithPageModule::TearDown();
   }
 
   std::vector<DictObjInfo> CreateDictObjs(int num) {
     std::vector<DictObjInfo> info;
     for (int i = 0; i < num; ++i) {
-      // Objects created will be released by the document.
-      CPDF_Dictionary* obj = m_pIndirectObjs->NewIndirect<CPDF_Dictionary>();
+      auto obj = m_pIndirectObjs->NewIndirect<CPDF_Dictionary>();
       info.push_back({obj->GetObjNum(), obj});
     }
     return info;
@@ -79,62 +66,62 @@
   {
     // No bookmark information.
     ScopedFPDFWideString title = GetFPDFWideString(L"");
-    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
+    EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get()));
 
     title = GetFPDFWideString(L"Preface");
-    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
+    EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get()));
   }
   {
     // Empty bookmark tree.
     m_pRootObj->SetNewFor<CPDF_Dictionary>("Outlines");
     ScopedFPDFWideString title = GetFPDFWideString(L"");
-    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
+    EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get()));
 
     title = GetFPDFWideString(L"Preface");
-    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
+    EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get()));
   }
   {
     // Check on a regular bookmark tree.
     auto bookmarks = CreateDictObjs(3);
 
     bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
-    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
+    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
                                                 bookmarks[0].num);
-    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
+    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs,
                                                 bookmarks[2].num);
 
     bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
-    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
+    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
                                                 bookmarks[0].num);
-    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Prev", m_pIndirectObjs.Get(),
+    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Prev", m_pIndirectObjs,
                                                 bookmarks[1].num);
 
     bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
     bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
-    bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
+    bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
                                                 bookmarks[1].num);
-    bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs.Get(),
+    bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs,
                                                 bookmarks[2].num);
 
-    m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs.Get(),
+    m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs,
                                           bookmarks[0].num);
 
     // Title with no match.
     ScopedFPDFWideString title = GetFPDFWideString(L"Chapter 3");
-    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
+    EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get()));
 
     // Title with partial match only.
     title = GetFPDFWideString(L"Chapter");
-    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
+    EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get()));
 
     // Title with a match.
     title = GetFPDFWideString(L"Chapter 2");
-    EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj),
+    EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj.Get()),
               FPDFBookmark_Find(m_pDoc.get(), title.get()));
 
     // Title match is case insensitive.
     title = GetFPDFWideString(L"cHaPter 2");
-    EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj),
+    EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj.Get()),
               FPDFBookmark_Find(m_pDoc.get(), title.get()));
   }
   {
@@ -142,34 +129,34 @@
     auto bookmarks = CreateDictObjs(3);
 
     bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
-    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
+    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
                                                 bookmarks[0].num);
-    bookmarks[1].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
+    bookmarks[1].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
                                                 bookmarks[2].num);
 
     bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
-    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
+    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
                                                 bookmarks[1].num);
-    bookmarks[2].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
+    bookmarks[2].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
                                                 bookmarks[1].num);
 
     bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
     bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
-    bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
+    bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
                                                 bookmarks[1].num);
-    bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs.Get(),
+    bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs,
                                                 bookmarks[2].num);
 
-    m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs.Get(),
+    m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs,
                                           bookmarks[0].num);
 
     // Title with no match.
     ScopedFPDFWideString title = GetFPDFWideString(L"Chapter 3");
-    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
+    EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get()));
 
     // Title with a match.
     title = GetFPDFWideString(L"Chapter 2");
-    EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj),
+    EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj.Get()),
               FPDFBookmark_Find(m_pDoc.get(), title.get()));
   }
   {
@@ -177,51 +164,51 @@
     auto bookmarks = CreateDictObjs(4);
 
     bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
-    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
+    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
                                                 bookmarks[0].num);
-    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
+    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs,
                                                 bookmarks[2].num);
 
     bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
-    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
+    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
                                                 bookmarks[0].num);
-    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
+    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs,
                                                 bookmarks[3].num);
 
     bookmarks[3].obj->SetNewFor<CPDF_String>("Title", L"Chapter 3");
-    bookmarks[3].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
+    bookmarks[3].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
                                                 bookmarks[0].num);
-    bookmarks[3].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
+    bookmarks[3].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs,
                                                 bookmarks[1].num);
 
     bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
     bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
-    bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
+    bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
                                                 bookmarks[1].num);
-    bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs.Get(),
+    bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs,
                                                 bookmarks[2].num);
 
-    m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs.Get(),
+    m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs,
                                           bookmarks[0].num);
 
     // Title with no match.
     ScopedFPDFWideString title = GetFPDFWideString(L"Chapter 8");
-    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
+    EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get()));
 
     // Title with a match.
     title = GetFPDFWideString(L"Chapter 3");
-    EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[3].obj),
+    EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[3].obj.Get()),
               FPDFBookmark_Find(m_pDoc.get(), title.get()));
   }
 }
 
 TEST_F(PDFDocTest, GetLocationInPage) {
   auto array = pdfium::MakeRetain<CPDF_Array>();
-  array->AddNew<CPDF_Number>(0);  // Page Index.
-  array->AddNew<CPDF_Name>("XYZ");
-  array->AddNew<CPDF_Number>(4);  // X
-  array->AddNew<CPDF_Number>(5);  // Y
-  array->AddNew<CPDF_Number>(6);  // Zoom.
+  array->AppendNew<CPDF_Number>(0);  // Page Index.
+  array->AppendNew<CPDF_Name>("XYZ");
+  array->AppendNew<CPDF_Number>(4);  // X
+  array->AppendNew<CPDF_Number>(5);  // Y
+  array->AppendNew<CPDF_Number>(6);  // Zoom.
 
   FPDF_BOOL hasX;
   FPDF_BOOL hasY;
diff --git a/fpdfsdk/fpdf_edit_embeddertest.cpp b/fpdfsdk/fpdf_edit_embeddertest.cpp
index b6cc84c..19097d8 100644
--- a/fpdfsdk/fpdf_edit_embeddertest.cpp
+++ b/fpdfsdk/fpdf_edit_embeddertest.cpp
@@ -1,7 +1,8 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <limits>
 #include <memory>
 #include <string>
 #include <utility>
@@ -9,7 +10,6 @@
 
 #include "build/build_config.h"
 #include "core/fpdfapi/font/cpdf_font.h"
-#include "core/fpdfapi/page/cpdf_formobject.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
@@ -17,7 +17,9 @@
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/fx_font.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "public/cpp/fpdf_scopers.h"
@@ -25,17 +27,73 @@
 #include "public/fpdf_edit.h"
 #include "public/fpdfview.h"
 #include "testing/embedder_test.h"
+#include "testing/embedder_test_constants.h"
 #include "testing/fx_string_testhelpers.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/utils/file_util.h"
 #include "testing/utils/hash.h"
+#include "testing/utils/path_service.h"
+#include "third_party/base/check.h"
+
+using pdfium::HelloWorldChecksum;
+using testing::HasSubstr;
+using testing::Not;
+using testing::UnorderedElementsAreArray;
+
+namespace {
+
+const char kAllRemovedChecksum[] = "eee4600ac08b458ac7ac2320e225674c";
+
+const wchar_t kBottomText[] = L"I'm at the bottom of the page";
+
+const char* BottomTextChecksum() {
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    return "c62d315856a558d2666b80d474831efe";
+  }
+#if BUILDFLAG(IS_APPLE)
+  return "81636489006a31fcb00cf29efcdf7909";
+#else
+  return "891dcb6e914c8360998055f1f47c9727";
+#endif
+}
+
+const char* FirstRemovedChecksum() {
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    return "3006ab2b12d27246eae4faad509ac575";
+  }
+#if BUILDFLAG(IS_APPLE)
+  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 "fc2334c350cbd0d2ae6076689da09741";
+#if BUILDFLAG(IS_APPLE)
+  return "0f3e4a7d71f9e7eb8a1a0d69403b9848";
+#else
+  return "d58570cc045dfb818b92cbabbd1a364c";
+#endif
+}
+
+const char kRedRectangleChecksum[] = "66d02eaa6181e2c069ce2ea99beda497";
+
+// In embedded_images.pdf.
+const char kEmbeddedImage33Checksum[] = "cb3637934bb3b95a6e4ae1ea9eb9e56e";
+
+}  // namespace
 
 class FPDFEditEmbedderTest : public EmbedderTest {
  protected:
   FPDF_DOCUMENT CreateNewDocument() {
-    document_ = FPDF_CreateNewDocument();
-    cpdf_doc_ = CPDFDocumentFromFPDFDocument(document_);
-    return document_;
+    CreateEmptyDocumentWithoutFormFillEnvironment();
+    cpdf_doc_ = CPDFDocumentFromFPDFDocument(document());
+    return document();
   }
 
   void CheckFontDescriptor(const CPDF_Dictionary* font_dict,
@@ -43,11 +101,13 @@
                            bool bold,
                            bool italic,
                            pdfium::span<const uint8_t> span) {
-    const CPDF_Dictionary* font_desc = font_dict->GetDictFor("FontDescriptor");
+    RetainPtr<const CPDF_Dictionary> font_desc =
+        font_dict->GetDictFor("FontDescriptor");
     ASSERT_TRUE(font_desc);
-    EXPECT_EQ("FontDescriptor", font_desc->GetStringFor("Type"));
-    EXPECT_EQ(font_dict->GetStringFor("BaseFont"),
-              font_desc->GetStringFor("FontName"));
+    EXPECT_EQ("FontDescriptor", font_desc->GetNameFor("Type"));
+    ByteString font_name = font_desc->GetNameFor("FontName");
+    EXPECT_FALSE(font_name.IsEmpty());
+    EXPECT_EQ(font_dict->GetNameFor("BaseFont"), font_name);
 
     // Check that the font descriptor has the required keys according to spec
     // 1.7 Table 5.19
@@ -59,7 +119,7 @@
     EXPECT_TRUE(FontStyleIsNonSymbolic(font_flags));
     ASSERT_TRUE(font_desc->KeyExist("FontBBox"));
 
-    const CPDF_Array* fontBBox = font_desc->GetArrayFor("FontBBox");
+    RetainPtr<const CPDF_Array> fontBBox = font_desc->GetArrayFor("FontBBox");
     ASSERT_TRUE(fontBBox);
     EXPECT_EQ(4u, fontBBox->size());
     // Check that the coordinates are in the preferred order according to spec
@@ -86,11 +146,10 @@
     // Check that the font stream is the one that was provided
     ASSERT_EQ(span.size(), streamAcc->GetSize());
     if (font_type == FPDF_FONT_TRUETYPE) {
-      ASSERT_EQ(static_cast<int>(span.size()),
-                streamAcc->GetDict()->GetIntegerFor("Length1"));
+      ASSERT_EQ(static_cast<int>(span.size()), streamAcc->GetLength1ForTest());
     }
 
-    const uint8_t* stream_data = streamAcc->GetData();
+    pdfium::span<const uint8_t> stream_data = streamAcc->GetSpan();
     for (size_t j = 0; j < span.size(); j++)
       EXPECT_EQ(span[j], stream_data[j]) << " at byte " << j;
   }
@@ -104,17 +163,17 @@
     int num_cids_checked = 0;
     int cur_cid = 0;
     for (size_t idx = 0; idx < widths_array->size(); idx++) {
-      int cid = widths_array->GetNumberAt(idx);
+      int cid = widths_array->GetFloatAt(idx);
       EXPECT_GE(cid, cur_cid);
       ASSERT_FALSE(++idx == widths_array->size());
-      const CPDF_Object* next = widths_array->GetObjectAt(idx);
+      RetainPtr<const CPDF_Object> next = widths_array->GetObjectAt(idx);
       if (next->IsArray()) {
         // We are in the c [w1 w2 ...] case
         const CPDF_Array* arr = next->AsArray();
         int cnt = static_cast<int>(arr->size());
         size_t inner_idx = 0;
         for (cur_cid = cid; cur_cid < cid + cnt; cur_cid++) {
-          uint32_t width = arr->GetNumberAt(inner_idx++);
+          int width = arr->GetFloatAt(inner_idx++);
           EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid))
               << " at cid " << cur_cid;
         }
@@ -125,7 +184,7 @@
       ASSERT_TRUE(next->IsNumber());
       int last_cid = next->AsNumber()->GetInteger();
       ASSERT_FALSE(++idx == widths_array->size());
-      uint32_t width = widths_array->GetNumberAt(idx);
+      int width = widths_array->GetFloatAt(idx);
       for (cur_cid = cid; cur_cid <= last_cid; cur_cid++) {
         EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid))
             << " at cid " << cur_cid;
@@ -133,7 +192,7 @@
       num_cids_checked += last_cid - cid + 1;
     }
     // Make sure we have a good amount of cids described
-    EXPECT_GT(num_cids_checked, 900);
+    EXPECT_GT(num_cids_checked, 200);
   }
   CPDF_Document* cpdf_doc() { return cpdf_doc_; }
 
@@ -157,36 +216,123 @@
     "endobj\r\n"
     "4 0 obj\r\n"
     "<</MediaBox\\[ 0 0 640 480\\]/Parent 2 0 R "
-    "/Resources<</ExtGState<</FXE1 5 0 R >>>>"
+    "/Resources<<>>"
     "/Rotate 0/Type/Page"
     ">>\r\n"
     "endobj\r\n"
-    "5 0 obj\r\n"
-    "<</BM/Normal/CA 1/ca 1>>\r\n"
-    "endobj\r\n"
     "xref\r\n"
-    "0 6\r\n"
+    "0 5\r\n"
     "0000000000 65535 f\r\n"
     "0000000017 00000 n\r\n"
     "0000000066 00000 n\r\n"
     "0000000122 00000 n\r\n"
     "0000000192 00000 n\r\n"
-    "0000000311 00000 n\r\n"
     "trailer\r\n"
     "<<\r\n"
     "/Root 1 0 R\r\n"
     "/Info 3 0 R\r\n"
-    "/Size 6/ID\\[<.*><.*>\\]>>\r\n"
+    "/Size 5/ID\\[<.*><.*>\\]>>\r\n"
     "startxref\r\n"
-    "354\r\n"
+    "285\r\n"
     "%%EOF\r\n";
 
 }  // namespace
 
+TEST_F(FPDFEditEmbedderTest, EmbedNotoSansSCFont) {
+  CreateEmptyDocument();
+  ScopedFPDFPage page(FPDFPage_New(document(), 0, 400, 400));
+  std::string font_path;
+  ASSERT_TRUE(PathService::GetThirdPartyFilePath(
+      "NotoSansCJK/NotoSansSC-Regular.subset.otf", &font_path));
+
+  size_t file_length = 0;
+  std::unique_ptr<char, pdfium::FreeDeleter> font_data =
+      GetFileContents(font_path.c_str(), &file_length);
+  ASSERT_TRUE(font_data);
+
+  ScopedFPDFFont font(FPDFText_LoadFont(
+      document(), reinterpret_cast<const uint8_t*>(font_data.get()),
+      file_length, FPDF_FONT_TRUETYPE, /*cid=*/true));
+  FPDF_PAGEOBJECT text_object =
+      FPDFPageObj_CreateTextObj(document(), font.get(), 20.0f);
+  EXPECT_TRUE(text_object);
+
+  // Test the characters which are either mapped to one single unicode or
+  // multiple unicodes in the embedded font.
+  ScopedFPDFWideString text = GetFPDFWideString(L"这是第一句。 这是第二行。");
+  EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
+
+  FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 50, 200);
+  FPDFPage_InsertObject(page.get(), text_object);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
+
+  const char* 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, checksum);
+
+  ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedDocument(400, 400, checksum);
+}
+
+TEST_F(FPDFEditEmbedderTest, EmbedNotoSansSCFontWithCharcodes) {
+  CreateEmptyDocument();
+  ScopedFPDFPage page(FPDFPage_New(document(), 0, 400, 400));
+  std::string font_path;
+  ASSERT_TRUE(PathService::GetThirdPartyFilePath(
+      "NotoSansCJK/NotoSansSC-Regular.subset.otf", &font_path));
+
+  size_t file_length = 0;
+  std::unique_ptr<char, pdfium::FreeDeleter> font_data =
+      GetFileContents(font_path.c_str(), &file_length);
+  ASSERT_TRUE(font_data);
+
+  ScopedFPDFFont font(FPDFText_LoadFont(
+      document(), reinterpret_cast<const uint8_t*>(font_data.get()),
+      file_length, FPDF_FONT_TRUETYPE, /*cid=*/true));
+  FPDF_PAGEOBJECT text_object =
+      FPDFPageObj_CreateTextObj(document(), font.get(), 20.0f);
+  EXPECT_TRUE(text_object);
+
+  // Same as `text` in the EmbedNotoSansSCFont test case above.
+  const std::vector<uint32_t> charcodes = {9, 6, 7, 3, 5, 2, 1,
+                                           9, 6, 7, 4, 8, 2};
+  EXPECT_TRUE(
+      FPDFText_SetCharcodes(text_object, charcodes.data(), charcodes.size()));
+
+  FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 50, 200);
+  FPDFPage_InsertObject(page.get(), text_object);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
+
+  const char* 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, checksum);
+
+  ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedDocument(400, 400, checksum);
+}
+
 TEST_F(FPDFEditEmbedderTest, EmptyCreation) {
-  EXPECT_TRUE(CreateEmptyDocument());
+  CreateEmptyDocument();
   FPDF_PAGE page = FPDFPage_New(document(), 0, 640.0, 480.0);
-  EXPECT_NE(nullptr, page);
+  EXPECT_TRUE(page);
   // The FPDFPage_GenerateContent call should do nothing.
   EXPECT_TRUE(FPDFPage_GenerateContent(page));
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
@@ -198,16 +344,16 @@
 
 // Regression test for https://crbug.com/667012
 TEST_F(FPDFEditEmbedderTest, RasterizePDF) {
-  const char kAllBlackMd5sum[] = "5708fc5c4a8bd0abde99c8e8f0390615";
+  const char kAllBlackChecksum[] = "5708fc5c4a8bd0abde99c8e8f0390615";
 
   // Get the bitmap for the original document.
   ScopedFPDFBitmap orig_bitmap;
   {
-    EXPECT_TRUE(OpenDocument("black.pdf"));
+    ASSERT_TRUE(OpenDocument("black.pdf"));
     FPDF_PAGE orig_page = LoadPage(0);
     ASSERT_TRUE(orig_page);
     orig_bitmap = RenderLoadedPage(orig_page);
-    CompareBitmap(orig_bitmap.get(), 612, 792, kAllBlackMd5sum);
+    CompareBitmap(orig_bitmap.get(), 612, 792, kAllBlackChecksum);
     UnloadPage(orig_page);
   }
 
@@ -221,7 +367,8 @@
     FPDF_PAGEOBJECT temp_img = FPDFPageObj_NewImageObj(temp_doc);
     EXPECT_TRUE(
         FPDFImageObj_SetBitmap(&temp_page, 1, temp_img, orig_bitmap.get()));
-    EXPECT_TRUE(FPDFImageObj_SetMatrix(temp_img, 612, 0, 0, 792, 0, 0));
+    static constexpr FS_MATRIX kLetterScaleMatrix{612, 0, 0, 792, 0, 0};
+    EXPECT_TRUE(FPDFPageObj_SetMatrix(temp_img, &kLetterScaleMatrix));
     FPDFPage_InsertObject(temp_page, temp_img);
     EXPECT_TRUE(FPDFPage_GenerateContent(temp_page));
     EXPECT_TRUE(FPDF_SaveAsCopy(temp_doc, this, 0));
@@ -232,16 +379,10 @@
   // Get the generated content. Make sure it is at least as big as the original
   // PDF.
   EXPECT_GT(GetString().size(), 923u);
-  VerifySavedDocument(612, 792, kAllBlackMd5sum);
+  VerifySavedDocument(612, 792, kAllBlackChecksum);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_AddPaths DISABLED_AddPaths
-#else
-#define MAYBE_AddPaths AddPaths
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_AddPaths) {
+TEST_F(FPDFEditEmbedderTest, AddPaths) {
   // Start with a blank page
   FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
   ASSERT_TRUE(page);
@@ -263,13 +404,11 @@
   EXPECT_EQ(FPDF_FILLMODE_ALTERNATE, fillmode);
   EXPECT_FALSE(stroke);
 
-  static const FS_MATRIX kMatrix = {1, 2, 3, 4, 5, 6};
-  EXPECT_FALSE(FPDFPath_SetMatrix(nullptr, &kMatrix));
-  EXPECT_TRUE(FPDFPath_SetMatrix(red_rect, &kMatrix));
+  static constexpr FS_MATRIX kMatrix = {1, 2, 3, 4, 5, 6};
+  EXPECT_TRUE(FPDFPageObj_SetMatrix(red_rect, &kMatrix));
 
   FS_MATRIX matrix;
-  EXPECT_FALSE(FPDFPath_GetMatrix(nullptr, &matrix));
-  EXPECT_TRUE(FPDFPath_GetMatrix(red_rect, &matrix));
+  EXPECT_TRUE(FPDFPageObj_GetMatrix(red_rect, &matrix));
   EXPECT_FLOAT_EQ(1.0f, matrix.a);
   EXPECT_FLOAT_EQ(2.0f, matrix.b);
   EXPECT_FLOAT_EQ(3.0f, matrix.c);
@@ -279,13 +418,12 @@
 
   // Set back the identity matrix.
   matrix = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f};
-  EXPECT_TRUE(FPDFPath_SetMatrix(red_rect, &matrix));
+  EXPECT_TRUE(FPDFPageObj_SetMatrix(red_rect, &matrix));
 
   FPDFPage_InsertObject(page, red_rect);
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 612, 792,
-                  "66d02eaa6181e2c069ce2ea99beda497");
+    CompareBitmap(page_bitmap.get(), 612, 792, kRedRectangleChecksum);
   }
 
   // Now add to that a green rectangle with some medium alpha
@@ -306,8 +444,8 @@
   EXPECT_EQ(0u, B);
   EXPECT_EQ(128u, A);
 
-  // Make sure the path has 5 points (1 FXPT_TYPE::MoveTo and 4
-  // FXPT_TYPE::LineTo).
+  // Make sure the path has 5 points (1 CFX_Path::Point::Type::kMove and 4
+  // CFX_Path::Point::Type::kLine).
   ASSERT_EQ(5, FPDFPath_CountSegments(green_rect));
   // Verify actual coordinates.
   FPDF_PATHSEGMENT segment = FPDFPath_GetPathSegment(green_rect, 0);
@@ -359,8 +497,8 @@
   EXPECT_TRUE(FPDFPath_LineTo(black_path, 300, 100));
   EXPECT_TRUE(FPDFPath_Close(black_path));
 
-  // Make sure the path has 3 points (1 FXPT_TYPE::MoveTo and 2
-  // FXPT_TYPE::LineTo).
+  // Make sure the path has 3 points (1 CFX_Path::Point::Type::kMove and 2
+  // CFX_Path::Point::Type::kLine).
   ASSERT_EQ(3, FPDFPath_CountSegments(black_path));
   // Verify actual coordinates.
   segment = FPDFPath_GetPathSegment(black_path, 0);
@@ -382,7 +520,7 @@
   EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
   EXPECT_TRUE(FPDFPathSegment_GetClose(segment));
   // Make sure out of bounds index access fails properly.
-  EXPECT_EQ(nullptr, FPDFPath_GetPathSegment(black_path, 3));
+  EXPECT_FALSE(FPDFPath_GetPathSegment(black_path, 3));
 
   FPDFPage_InsertObject(page, black_path);
   {
@@ -402,10 +540,14 @@
   EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 375, 330, 390, 360, 400, 400));
   EXPECT_TRUE(FPDFPath_Close(blue_path));
   FPDFPage_InsertObject(page, blue_path);
-  const char kLastMD5[] = "9823e1a21bd9b72b6a442ba4f12af946";
+  const char* last_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "ed14c60702b1489c597c7d46ece7f86d";
+    return "9823e1a21bd9b72b6a442ba4f12af946";
+  }();
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 612, 792, kLastMD5);
+    CompareBitmap(page_bitmap.get(), 612, 792, last_checksum);
   }
 
   // Now save the result, closing the page and document
@@ -414,12 +556,12 @@
   FPDF_ClosePage(page);
 
   // Render the saved result
-  VerifySavedDocument(612, 792, kLastMD5);
+  VerifySavedDocument(612, 792, last_checksum);
 }
 
 TEST_F(FPDFEditEmbedderTest, ClipPath) {
   // Load document with a clipped rectangle.
-  EXPECT_TRUE(OpenDocument("clip_path.pdf"));
+  ASSERT_TRUE(OpenDocument("clip_path.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -515,7 +657,7 @@
 
 TEST_F(FPDFEditEmbedderTest, BUG_1399) {
   // Load document with a clipped rectangle.
-  EXPECT_TRUE(OpenDocument("bug_1399.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_1399.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -552,15 +694,39 @@
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_SetText DISABLED_SetText
-#else
-#define MAYBE_SetText SetText
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_SetText) {
+TEST_F(FPDFEditEmbedderTest, BUG_1549) {
+  static const char kOriginalChecksum[] = "126366fb95e6caf8ea196780075b22b2";
+  static const char kRemovedChecksum[] = "6ec2f27531927882624b37bc7d8e12f4";
+
+  ASSERT_TRUE(OpenDocument("bug_1549.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    CompareBitmap(bitmap.get(), 100, 150, kOriginalChecksum);
+
+    ScopedFPDFPageObject obj(FPDFPage_GetObject(page, 0));
+    ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj.get()));
+    ASSERT_TRUE(FPDFPage_RemoveObject(page, obj.get()));
+  }
+
+  ASSERT_TRUE(FPDFPage_GenerateContent(page));
+
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    CompareBitmap(bitmap.get(), 100, 150, kRemovedChecksum);
+  }
+
+  ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // TODO(crbug.com/pdfium/1549): Should be `kRemovedChecksum`.
+  VerifySavedDocument(100, 150, "4f9889cd5993db20f1ab37d677ac8d26");
+}
+
+TEST_F(FPDFEditEmbedderTest, SetText) {
   // Load document with some text.
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -574,16 +740,20 @@
   // Verify the "Hello, world!" text is gone and "Changed for SetText test" is
   // now displayed.
   ASSERT_EQ(2, FPDFPage_CountObjects(page));
-#if defined(OS_MACOSX)
-  const char kChangedMD5[] = "94c1e7a5af7dd9d77dc2223b1091acb7";
-#elif defined(OS_WIN)
-  const char kChangedMD5[] = "3137fdb27962671f5c3963a5e965eff5";
+
+  const char* changed_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "4a8345a139507932729e07d4831cbe2b";
+    }
+#if BUILDFLAG(IS_APPLE)
+    return "b720e83476fd6819d47c533f1f43c728";
 #else
-  const char kChangedMD5[] = "a0c4ea6620772991f66bf7130379b08a";
+    return "9a85b9354a69c61772ed24151c140f46";
 #endif
+  }();
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kChangedMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, changed_checksum);
   }
 
   // Now save the result.
@@ -595,63 +765,392 @@
   // Re-open the file and check the changes were kept in the saved .pdf.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
   EXPECT_EQ(2, FPDFPage_CountObjects(saved_page));
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kChangedMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, changed_checksum);
   }
 
   CloseSavedPage(saved_page);
   CloseSavedDocument();
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_RemovePageObject DISABLED_RemovePageObject
+TEST_F(FPDFEditEmbedderTest, SetCharcodesBadParams) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ASSERT_EQ(2, FPDFPage_CountObjects(page));
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(page_object);
+
+  const uint32_t kDummyValue = 42;
+  EXPECT_FALSE(FPDFText_SetCharcodes(nullptr, nullptr, 0));
+  EXPECT_FALSE(FPDFText_SetCharcodes(nullptr, nullptr, 1));
+  EXPECT_FALSE(FPDFText_SetCharcodes(nullptr, &kDummyValue, 0));
+  EXPECT_FALSE(FPDFText_SetCharcodes(nullptr, &kDummyValue, 1));
+  EXPECT_FALSE(FPDFText_SetCharcodes(page_object, nullptr, 1));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, SetTextKeepClippingPath) {
+  // Load document with some text, with parts clipped.
+  ASSERT_TRUE(OpenDocument("bug_1558.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  const char* original_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "3c04e3acc732faaf39fb0c19efd056ac";
+    }
+#if BUILDFLAG(IS_APPLE)
+    return "ae7a25c85e0e2dd0c5cb9dd5cd37f6df";
 #else
-#define MAYBE_RemovePageObject RemovePageObject
+    return "7af7fe5b281298261eb66ac2d22f5054";
 #endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_RemovePageObject) {
+  }();
+  {
+    // When opened before any editing and saving, the clipping path is rendered.
+    ScopedFPDFBitmap original_bitmap = RenderPage(page);
+    CompareBitmap(original_bitmap.get(), 200, 200, original_checksum);
+  }
+
+  // "Change" the text in the objects to their current values to force them to
+  // regenerate when saving.
+  {
+    ScopedFPDFTextPage text_page(FPDFText_LoadPage(page));
+    ASSERT_TRUE(text_page);
+    const int obj_count = FPDFPage_CountObjects(page);
+    ASSERT_EQ(2, obj_count);
+    for (int i = 0; i < obj_count; ++i) {
+      FPDF_PAGEOBJECT text_obj = FPDFPage_GetObject(page, i);
+      ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_obj));
+      unsigned long size =
+          FPDFTextObj_GetText(text_obj, text_page.get(),
+                              /*buffer=*/nullptr, /*length=*/0);
+      ASSERT_GT(size, 0u);
+      std::vector<FPDF_WCHAR> buffer = GetFPDFWideStringBuffer(size);
+      ASSERT_EQ(size, FPDFTextObj_GetText(text_obj, text_page.get(),
+                                          buffer.data(), size));
+      EXPECT_TRUE(FPDFText_SetText(text_obj, buffer.data()));
+    }
+  }
+
+  {
+    // After editing but before saving, the clipping path is retained.
+    ScopedFPDFBitmap edited_bitmap = RenderPage(page);
+    CompareBitmap(edited_bitmap.get(), 200, 200, original_checksum);
+  }
+
+  // Save the file.
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Open the saved copy and render it.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
+
+  {
+    ScopedFPDFBitmap saved_bitmap = RenderSavedPage(saved_page);
+    CompareBitmap(saved_bitmap.get(), 200, 200, original_checksum);
+  }
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFEditEmbedderTest, BUG_1574) {
+  // Load document with some text within a clipping path.
+  ASSERT_TRUE(OpenDocument("bug_1574.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  const char* original_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "d76a31d931a350f0809226a41029a9a4";
+    }
+#if BUILDFLAG(IS_APPLE)
+    return "1226bc2b8072622eb28f52321876e814";
+#else
+    return "c5241eef60b9eac68ed1f2a5fd002703";
+#endif
+  }();
+  {
+    // When opened before any editing and saving, the text object is rendered.
+    ScopedFPDFBitmap original_bitmap = RenderPage(page);
+    CompareBitmap(original_bitmap.get(), 200, 300, original_checksum);
+  }
+
+  // "Change" the text in the objects to their current values to force them to
+  // regenerate when saving.
+  {
+    ScopedFPDFTextPage text_page(FPDFText_LoadPage(page));
+    ASSERT_TRUE(text_page);
+
+    ASSERT_EQ(2, FPDFPage_CountObjects(page));
+    FPDF_PAGEOBJECT text_obj = FPDFPage_GetObject(page, 1);
+    ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_obj));
+
+    unsigned long size = FPDFTextObj_GetText(text_obj, text_page.get(),
+                                             /*buffer=*/nullptr, /*length=*/0);
+    ASSERT_GT(size, 0u);
+    std::vector<FPDF_WCHAR> buffer = GetFPDFWideStringBuffer(size);
+    ASSERT_EQ(size, FPDFTextObj_GetText(text_obj, text_page.get(),
+                                        buffer.data(), size));
+    EXPECT_TRUE(FPDFText_SetText(text_obj, buffer.data()));
+  }
+
+  // Save the file.
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Open the saved copy and render it.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
+
+  {
+    ScopedFPDFBitmap saved_bitmap = RenderSavedPage(saved_page);
+    CompareBitmap(saved_bitmap.get(), 200, 300, original_checksum);
+  }
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFEditEmbedderTest, RemoveTextObject) {
   // Load document with some text.
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
   // Show what the original file looks like.
   {
-#if defined(OS_MACOSX)
-    const char kOriginalMD5[] = "b90475ca64d1348c3bf5e2b77ad9187a";
-#elif defined(OS_WIN)
-    const char kOriginalMD5[] = "795b7ce1626931aa06af0fa23b7d80bb";
-#else
-    const char kOriginalMD5[] = "2baa4c0e1758deba1b9c908e1fbd04ed";
-#endif
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum());
   }
 
   // Get the "Hello, world!" text object and remove it.
   ASSERT_EQ(2, FPDFPage_CountObjects(page));
-  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
-  ASSERT_TRUE(page_object);
-  EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
-
-  // Verify the "Hello, world!" text is gone.
   {
-#if defined(OS_MACOSX)
-    const char kRemovedMD5[] = "af760c4702467cb1492a57fb8215efaa";
-#elif defined(OS_WIN)
-    const char kRemovedMD5[] = "aae6c5334721f90ec30d3d59f4ef7deb";
-#else
-    const char kRemovedMD5[] = "b76df015fe88009c3c342395df96abf1";
-#endif
-    ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kRemovedMD5);
+    ScopedFPDFPageObject page_object(FPDFPage_GetObject(page, 0));
+    ASSERT_TRUE(page_object);
+    ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(page_object.get()));
+    EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object.get()));
   }
   ASSERT_EQ(1, FPDFPage_CountObjects(page));
 
+  // Verify the "Hello, world!" text is gone.
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 200, FirstRemovedChecksum());
+  }
+
+  // Verify the rendering again after calling FPDFPage_GenerateContent().
+  ASSERT_TRUE(FPDFPage_GenerateContent(page));
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 200, FirstRemovedChecksum());
+  }
+
+  // Save the document and verify it after reloading.
+  ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedDocument(200, 200, FirstRemovedChecksum());
+
+  // Verify removed/renamed resources are no longer there.
+  EXPECT_THAT(GetString(), Not(HasSubstr("/F1")));
+  EXPECT_THAT(GetString(), Not(HasSubstr("/F2")));
+  EXPECT_THAT(GetString(), Not(HasSubstr("/Times-Roman")));
+
   UnloadPage(page);
-  FPDFPageObj_Destroy(page_object);
+}
+
+TEST_F(FPDFEditEmbedderTest,
+       RemoveTextObjectWithTwoPagesSharingContentStreamAndResources) {
+  // Load document with some text.
+  ASSERT_TRUE(OpenDocument("hello_world_2_pages.pdf"));
+  FPDF_PAGE page1 = LoadPage(0);
+  ASSERT_TRUE(page1);
+  FPDF_PAGE page2 = LoadPage(1);
+  ASSERT_TRUE(page2);
+
+  // Show what the original file looks like.
+  {
+    ScopedFPDFBitmap page1_bitmap = RenderPage(page1);
+    CompareBitmap(page1_bitmap.get(), 200, 200, HelloWorldChecksum());
+    ScopedFPDFBitmap page2_bitmap = RenderPage(page2);
+    CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
+  }
+
+  // Get the "Hello, world!" text object from page 1 and remove it.
+  ASSERT_EQ(2, FPDFPage_CountObjects(page1));
+  {
+    ScopedFPDFPageObject page_object(FPDFPage_GetObject(page1, 0));
+    ASSERT_TRUE(page_object);
+    ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(page_object.get()));
+    EXPECT_TRUE(FPDFPage_RemoveObject(page1, page_object.get()));
+  }
+  ASSERT_EQ(1, FPDFPage_CountObjects(page1));
+
+  // Verify the "Hello, world!" text is gone from page 1.
+  {
+    ScopedFPDFBitmap page1_bitmap = RenderPage(page1);
+    CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum());
+    ScopedFPDFBitmap page2_bitmap = RenderPage(page2);
+    CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
+  }
+
+  // Verify the rendering again after calling FPDFPage_GenerateContent().
+  ASSERT_TRUE(FPDFPage_GenerateContent(page1));
+  {
+    ScopedFPDFBitmap page1_bitmap = RenderPage(page1);
+    CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum());
+    ScopedFPDFBitmap page2_bitmap = RenderPage(page2);
+    CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
+  }
+
+  // Save the document and verify it after reloading.
+  ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page1 = LoadSavedPage(0);
+  VerifySavedRendering(saved_page1, 200, 200, FirstRemovedChecksum());
+  CloseSavedPage(saved_page1);
+  FPDF_PAGE saved_page2 = LoadSavedPage(1);
+  VerifySavedRendering(saved_page2, 200, 200, HelloWorldChecksum());
+  CloseSavedPage(saved_page2);
+  CloseSavedDocument();
+
+  std::vector<std::string> split_saved_data = StringSplit(GetString(), '\n');
+  // Verify removed/renamed resources are in the save PDF the correct number of
+  // times.
+  EXPECT_THAT(split_saved_data, Contains(HasSubstr("/F1")).Times(1));
+  EXPECT_THAT(split_saved_data, Contains(HasSubstr("/F2")).Times(1));
+  EXPECT_THAT(split_saved_data, Contains(HasSubstr("/Times-Roman")).Times(1));
+
+  UnloadPage(page1);
+  UnloadPage(page2);
+}
+
+TEST_F(FPDFEditEmbedderTest,
+       RemoveTextObjectWithTwoPagesSharingContentArrayAndResources) {
+  // Load document with some text.
+  ASSERT_TRUE(OpenDocument("hello_world_2_pages_split_streams.pdf"));
+  FPDF_PAGE page1 = LoadPage(0);
+  ASSERT_TRUE(page1);
+  FPDF_PAGE page2 = LoadPage(1);
+  ASSERT_TRUE(page2);
+
+  // Show what the original file looks like.
+  {
+    ScopedFPDFBitmap page1_bitmap = RenderPage(page1);
+    CompareBitmap(page1_bitmap.get(), 200, 200, HelloWorldChecksum());
+    ScopedFPDFBitmap page2_bitmap = RenderPage(page2);
+    CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
+  }
+
+  // Get the "Hello, world!" text object from page 1 and remove it.
+  ASSERT_EQ(2, FPDFPage_CountObjects(page1));
+  {
+    ScopedFPDFPageObject page_object(FPDFPage_GetObject(page1, 0));
+    ASSERT_TRUE(page_object);
+    ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(page_object.get()));
+    EXPECT_TRUE(FPDFPage_RemoveObject(page1, page_object.get()));
+  }
+  ASSERT_EQ(1, FPDFPage_CountObjects(page1));
+
+  // Verify the "Hello, world!" text is gone from page 1.
+  {
+    ScopedFPDFBitmap page1_bitmap = RenderPage(page1);
+    CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum());
+    ScopedFPDFBitmap page2_bitmap = RenderPage(page2);
+    CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
+  }
+
+  // Verify the rendering again after calling FPDFPage_GenerateContent().
+  ASSERT_TRUE(FPDFPage_GenerateContent(page1));
+  {
+    ScopedFPDFBitmap page1_bitmap = RenderPage(page1);
+    CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum());
+    ScopedFPDFBitmap page2_bitmap = RenderPage(page2);
+    CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
+  }
+
+  // Save the document and verify it after reloading.
+  ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page1 = LoadSavedPage(0);
+  VerifySavedRendering(saved_page1, 200, 200, FirstRemovedChecksum());
+  CloseSavedPage(saved_page1);
+  FPDF_PAGE saved_page2 = LoadSavedPage(1);
+  VerifySavedRendering(saved_page2, 200, 200, HelloWorldChecksum());
+  CloseSavedPage(saved_page2);
+  CloseSavedDocument();
+
+  UnloadPage(page1);
+  UnloadPage(page2);
+}
+
+TEST_F(FPDFEditEmbedderTest, RemoveTextObjectWithTwoPagesSharingResourcesDict) {
+  // Load document with some text.
+  ASSERT_TRUE(OpenDocument("hello_world_2_pages_shared_resources_dict.pdf"));
+  FPDF_PAGE page1 = LoadPage(0);
+  ASSERT_TRUE(page1);
+  FPDF_PAGE page2 = LoadPage(1);
+  ASSERT_TRUE(page2);
+
+  // Show what the original file looks like.
+  {
+    ScopedFPDFBitmap page1_bitmap = RenderPage(page1);
+    CompareBitmap(page1_bitmap.get(), 200, 200, HelloWorldChecksum());
+    ScopedFPDFBitmap page2_bitmap = RenderPage(page2);
+    CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
+  }
+
+  // Get the "Hello, world!" text object from page 1 and remove it.
+  ASSERT_EQ(2, FPDFPage_CountObjects(page1));
+  {
+    ScopedFPDFPageObject page_object(FPDFPage_GetObject(page1, 0));
+    ASSERT_TRUE(page_object);
+    ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(page_object.get()));
+    EXPECT_TRUE(FPDFPage_RemoveObject(page1, page_object.get()));
+  }
+  ASSERT_EQ(1, FPDFPage_CountObjects(page1));
+
+  // Verify the "Hello, world!" text is gone from page 1
+  {
+    ScopedFPDFBitmap page1_bitmap = RenderPage(page1);
+    CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum());
+    ScopedFPDFBitmap page2_bitmap = RenderPage(page2);
+    CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
+  }
+
+  // Verify the rendering again after calling FPDFPage_GenerateContent().
+  ASSERT_TRUE(FPDFPage_GenerateContent(page1));
+  {
+    ScopedFPDFBitmap page1_bitmap = RenderPage(page1);
+    CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum());
+    ScopedFPDFBitmap page2_bitmap = RenderPage(page2);
+    CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
+  }
+
+  // Save the document and verify it after reloading.
+  ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page1 = LoadSavedPage(0);
+  VerifySavedRendering(saved_page1, 200, 200, FirstRemovedChecksum());
+  CloseSavedPage(saved_page1);
+  FPDF_PAGE saved_page2 = LoadSavedPage(1);
+  VerifySavedRendering(saved_page2, 200, 200, HelloWorldChecksum());
+  CloseSavedPage(saved_page2);
+  CloseSavedDocument();
+
+  UnloadPage(page1);
+  UnloadPage(page2);
 }
 
 void CheckMarkCounts(FPDF_PAGE page,
@@ -758,7 +1257,7 @@
 
 TEST_F(FPDFEditEmbedderTest, ReadMarkedObjectsIndirectDict) {
   // Load document with some text marked with an indirect property.
-  EXPECT_TRUE(OpenDocument("text_in_page_marked_indirect.pdf"));
+  ASSERT_TRUE(OpenDocument("text_in_page_marked_indirect.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -767,29 +1266,29 @@
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_RemoveMarkedObjectsPrime DISABLED_RemoveMarkedObjectsPrime
-#else
-#define MAYBE_RemoveMarkedObjectsPrime RemoveMarkedObjectsPrime
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveMarkedObjectsPrime) {
+TEST_F(FPDFEditEmbedderTest, RemoveMarkedObjectsPrime) {
   // Load document with some text.
-  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
   // Show what the original file looks like.
   {
-#if defined(OS_MACOSX)
-    const char kOriginalMD5[] = "5a5eb63cb21cc15084fea1f14284b8df";
-#elif defined(OS_WIN)
-    const char kOriginalMD5[] = "00542ee435b37749c4453be63bf7bdb6";
+    const char* original_checksum = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+        return "efc2206b313fff03be8e701907322b06";
+#if BUILDFLAG(IS_APPLE)
+#ifdef ARCH_CPU_ARM64
+      return "cdc8e22cf1e7e06999dc456288672a3b";
 #else
-    const char kOriginalMD5[] = "41647268d5911d049801803b15c2dfb0";
-#endif
+      return "966579fb98206858ce2f0a1f94a74d05";
+#endif  // ARCH_CPU_ARM64
+#else
+      return "3d5a3de53d5866044c2b6bf339742c97";
+#endif  // BUILDFLAG(IS_APPLE)
+    }();
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, original_checksum);
   }
 
   constexpr int expected_object_count = 19;
@@ -825,20 +1324,35 @@
   }
 
   EXPECT_EQ(11, FPDFPage_CountObjects(page));
-
-#if defined(OS_MACOSX)
-  const char kNonPrimesMD5[] = "57e76dc7375d896704f0fd6d6d1b9e65";
-  const char kNonPrimesAfterSaveMD5[] = "6304512d0150bbd5578e8e22d3121103";
-#elif defined(OS_WIN)
-  const char kNonPrimesMD5[] = "86e371fdae30c2471f476631f3f93413";
-  const char kNonPrimesAfterSaveMD5[] = "86e371fdae30c2471f476631f3f93413";
+  const char* non_primes_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "10a6558c9e40ea837922e6f2882a2d57";
+#if BUILDFLAG(IS_APPLE)
+#ifdef ARCH_CPU_ARM64
+    return "23c4aec321547f51591fe7363a9ea2d6";
 #else
-  const char kNonPrimesMD5[] = "67ab13115d0cc34e99a1003c28047b40";
-  const char kNonPrimesAfterSaveMD5[] = "67ab13115d0cc34e99a1003c28047b40";
-#endif
+    return "6e19a4dd674b522cd39cf41956559bd6";
+#endif  // ARCH_CPU_ARM64
+#else
+    return "bc8623c052f12376c3d8dd09a6cd27df";
+#endif  // BUILDFLAG(IS_APPLE)
+  }();
+  const char* non_primes_after_save_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "10a6558c9e40ea837922e6f2882a2d57";
+#if BUILDFLAG(IS_APPLE)
+#ifdef ARCH_CPU_ARM64
+    return "6bb1ea0d0a512f29edabda33064a0725";
+#else
+    return "3cb35c681f8fb5a43a49146ac7caa818";
+#endif  // ARCH_CPU_ARM64
+#else
+    return "bc8623c052f12376c3d8dd09a6cd27df";
+#endif  // BUILDFLAG(IS_APPLE)
+  }();
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kNonPrimesMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, non_primes_checksum);
   }
 
   // Save the file.
@@ -849,11 +1363,12 @@
   // Re-open the file and check the prime marks are not there anymore.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
   EXPECT_EQ(11, FPDFPage_CountObjects(saved_page));
 
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kNonPrimesAfterSaveMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, non_primes_after_save_checksum);
   }
 
   CloseSavedPage(saved_page);
@@ -862,7 +1377,7 @@
 
 TEST_F(FPDFEditEmbedderTest, RemoveMarks) {
   // Load document with some text.
-  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -906,6 +1421,7 @@
   // Re-open the file and check the prime marks are not there anymore.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
 
   CheckMarkCounts(saved_page, 1, kExpectedObjectCount, 0, 4, 9, 1);
 
@@ -915,7 +1431,7 @@
 
 TEST_F(FPDFEditEmbedderTest, RemoveMarkParam) {
   // Load document with some text.
-  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -962,6 +1478,7 @@
   // Re-open the file and check the "Factor" parameters are still gone.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
 
   size_t square_count = 0;
   for (int i = 0; i < kExpectedObjectCount; ++i) {
@@ -999,7 +1516,7 @@
 
 TEST_F(FPDFEditEmbedderTest, MaintainMarkedObjects) {
   // Load document with some text.
-  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1021,6 +1538,7 @@
 
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
 
   CheckMarkCounts(saved_page, 2, 18, 8, 3, 9, 1);
 
@@ -1030,7 +1548,7 @@
 
 TEST_F(FPDFEditEmbedderTest, MaintainIndirectMarkedObjects) {
   // Load document with some text.
-  EXPECT_TRUE(OpenDocument("text_in_page_marked_indirect.pdf"));
+  ASSERT_TRUE(OpenDocument("text_in_page_marked_indirect.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1052,6 +1570,7 @@
 
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
 
   CheckMarkCounts(saved_page, 2, 18, 8, 3, 9, 1);
 
@@ -1061,7 +1580,7 @@
 
 TEST_F(FPDFEditEmbedderTest, RemoveExistingPageObject) {
   // Load document with some text.
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1083,23 +1602,15 @@
   // Re-open the file and check the page object count is still 1.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
   EXPECT_EQ(1, FPDFPage_CountObjects(saved_page));
   CloseSavedPage(saved_page);
   CloseSavedDocument();
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_RemoveExistingPageObjectSplitStreamsNotLonely \
-  DISABLED_RemoveExistingPageObjectSplitStreamsNotLonely
-#else
-#define MAYBE_RemoveExistingPageObjectSplitStreamsNotLonely \
-  RemoveExistingPageObjectSplitStreamsNotLonely
-#endif
-TEST_F(FPDFEditEmbedderTest,
-       MAYBE_RemoveExistingPageObjectSplitStreamsNotLonely) {
+TEST_F(FPDFEditEmbedderTest, RemoveExistingPageObjectSplitStreamsNotLonely) {
   // Load document with some text.
-  EXPECT_TRUE(OpenDocument("hello_world_split_streams.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world_split_streams.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1112,16 +1623,19 @@
 
   // Verify the "Hello, world!" text is gone.
   ASSERT_EQ(2, FPDFPage_CountObjects(page));
-#if defined(OS_MACOSX)
-  const char kHelloRemovedMD5[] = "e07a62d412728fc4d6e3ff42f2dd0e11";
-#elif defined(OS_WIN)
-  const char kHelloRemovedMD5[] = "a97d4c72c969ba373c2dce675d277e65";
+  const char* hello_removed_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "204c11472f5b93719487de7b9c1b1c93";
+    }
+#if BUILDFLAG(IS_APPLE)
+    return "5508c2f06d104050f74f655693e38c2c";
 #else
-  const char kHelloRemovedMD5[] = "95b92950647a2190e1230911e7a1a0e9";
+    return "a8cd82499cf744e0862ca468c9d4ceb8";
 #endif
+  }();
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kHelloRemovedMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, hello_removed_checksum);
   }
 
   // Save the file
@@ -1133,28 +1647,21 @@
   // Re-open the file and check the page object count is still 2.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
 
   EXPECT_EQ(2, FPDFPage_CountObjects(saved_page));
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kHelloRemovedMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, hello_removed_checksum);
   }
 
   CloseSavedPage(saved_page);
   CloseSavedDocument();
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_RemoveExistingPageObjectSplitStreamsLonely \
-  DISABLED_RemoveExistingPageObjectSplitStreamsLonely
-#else
-#define MAYBE_RemoveExistingPageObjectSplitStreamsLonely \
-  RemoveExistingPageObjectSplitStreamsLonely
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveExistingPageObjectSplitStreamsLonely) {
+TEST_F(FPDFEditEmbedderTest, RemoveExistingPageObjectSplitStreamsLonely) {
   // Load document with some text.
-  EXPECT_TRUE(OpenDocument("hello_world_split_streams.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world_split_streams.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1167,16 +1674,9 @@
 
   // Verify the "Greetings, world!" text is gone.
   ASSERT_EQ(2, FPDFPage_CountObjects(page));
-#if defined(OS_MACOSX)
-  const char kGreetingsRemovedMD5[] = "b90475ca64d1348c3bf5e2b77ad9187a";
-#elif defined(OS_WIN)
-  const char kGreetingsRemovedMD5[] = "795b7ce1626931aa06af0fa23b7d80bb";
-#else
-  const char kGreetingsRemovedMD5[] = "2baa4c0e1758deba1b9c908e1fbd04ed";
-#endif
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kGreetingsRemovedMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum());
   }
 
   // Save the file
@@ -1188,11 +1688,12 @@
   // Re-open the file and check the page object count is still 2.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
 
   EXPECT_EQ(2, FPDFPage_CountObjects(saved_page));
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kGreetingsRemovedMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum());
   }
 
   CloseSavedPage(saved_page);
@@ -1201,7 +1702,7 @@
 
 TEST_F(FPDFEditEmbedderTest, GetContentStream) {
   // Load document with some text split across streams.
-  EXPECT_TRUE(OpenDocument("split_streams.pdf"));
+  ASSERT_TRUE(OpenDocument("split_streams.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1225,15 +1726,9 @@
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_RemoveAllFromStream DISABLED_RemoveAllFromStream
-#else
-#define MAYBE_RemoveAllFromStream RemoveAllFromStream
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveAllFromStream) {
+TEST_F(FPDFEditEmbedderTest, RemoveAllFromStream) {
   // Load document with some text split across streams.
-  EXPECT_TRUE(OpenDocument("split_streams.pdf"));
+  ASSERT_TRUE(OpenDocument("split_streams.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1289,16 +1784,22 @@
       EXPECT_EQ(1, cpdf_page_object->GetContentStream()) << i;
   }
 
-#if defined(OS_MACOSX)
-  const char kStream1RemovedMD5[] = "d2e21fbd5a6de563f619feeeb6163331";
-#elif defined(OS_WIN)
-  const char kStream1RemovedMD5[] = "b4140f203523e38793283a5943d8075b";
+  const char* stream1_removed_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "0b3ef335b8d86a3f9d609368b9d075e0";
+#if BUILDFLAG(IS_APPLE)
+#if ARCH_CPU_ARM64
+    return "08505db7b598f7397a2260ecb1f6d86d";
 #else
-  const char kStream1RemovedMD5[] = "e86a3efc160ede6cfcb1f59bcacf1105";
-#endif
+    return "3cdc75af44c15bed80998facd6e674c9";
+#endif  // ARCH_CPU_ARM64
+#else
+    return "b474826df1acedb05c7b82e1e49e64a6";
+#endif  // BUILDFLAG(IS_APPLE)
+  }();
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kStream1RemovedMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, stream1_removed_checksum);
   }
 
   // Save the file
@@ -1309,6 +1810,7 @@
   // content stream 1 was removed.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
 
   // Content stream 0: page objects 0-14.
   // Content stream 1: page object 15.
@@ -1326,7 +1828,7 @@
 
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kStream1RemovedMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, stream1_removed_checksum);
   }
 
   CloseSavedPage(saved_page);
@@ -1335,7 +1837,7 @@
 
 TEST_F(FPDFEditEmbedderTest, RemoveAllFromSingleStream) {
   // Load document with a single stream.
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1363,10 +1865,9 @@
 
   ASSERT_EQ(0, FPDFPage_CountObjects(page));
 
-  const char kAllRemovedMD5[] = "eee4600ac08b458ac7ac2320e225674c";
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedChecksum);
   }
 
   // Save the file
@@ -1376,26 +1877,21 @@
   // Re-open the file and check the page object count is still 0.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
 
   EXPECT_EQ(0, FPDFPage_CountObjects(saved_page));
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedChecksum);
   }
 
   CloseSavedPage(saved_page);
   CloseSavedDocument();
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_RemoveFirstFromSingleStream DISABLED_RemoveFirstFromSingleStream
-#else
-#define MAYBE_RemoveFirstFromSingleStream RemoveFirstFromSingleStream
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveFirstFromSingleStream) {
+TEST_F(FPDFEditEmbedderTest, RemoveFirstFromSingleStream) {
   // Load document with a single stream.
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1427,16 +1923,9 @@
   cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
   ASSERT_EQ(0, cpdf_page_object->GetContentStream());
 
-#if defined(OS_MACOSX)
-  const char kFirstRemovedMD5[] = "af760c4702467cb1492a57fb8215efaa";
-#elif defined(OS_WIN)
-  const char kFirstRemovedMD5[] = "aae6c5334721f90ec30d3d59f4ef7deb";
-#else
-  const char kFirstRemovedMD5[] = "b76df015fe88009c3c342395df96abf1";
-#endif
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kFirstRemovedMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, FirstRemovedChecksum());
   }
 
   // Save the file
@@ -1446,6 +1935,7 @@
   // Re-open the file and check the page object count is still 0.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
 
   ASSERT_EQ(1, FPDFPage_CountObjects(saved_page));
   page_object = FPDFPage_GetObject(saved_page, 0);
@@ -1454,22 +1944,16 @@
   ASSERT_EQ(0, cpdf_page_object->GetContentStream());
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kFirstRemovedMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, FirstRemovedChecksum());
   }
 
   CloseSavedPage(saved_page);
   CloseSavedDocument();
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_RemoveLastFromSingleStream DISABLED_RemoveLastFromSingleStream
-#else
-#define MAYBE_RemoveLastFromSingleStream RemoveLastFromSingleStream
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveLastFromSingleStream) {
+TEST_F(FPDFEditEmbedderTest, RemoveLastFromSingleStream) {
   // Load document with a single stream.
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1501,16 +1985,10 @@
   cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
   ASSERT_EQ(0, cpdf_page_object->GetContentStream());
 
-#if defined(OS_MACOSX)
-  const char kLastRemovedMD5[] = "f8fbd14a048b9e2ea8e5f059f22a910e";
-#elif defined(OS_WIN)
-  const char kLastRemovedMD5[] = "93db13099042bafefb3c22a165bad684";
-#else
-  const char kLastRemovedMD5[] = "93dcc09055f87a2792c8e3065af99a1b";
-#endif
+  using pdfium::HelloWorldRemovedChecksum;
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kLastRemovedMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldRemovedChecksum());
   }
 
   // Save the file
@@ -1520,6 +1998,7 @@
   // Re-open the file and check the page object count is still 0.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
 
   ASSERT_EQ(1, FPDFPage_CountObjects(saved_page));
   page_object = FPDFPage_GetObject(saved_page, 0);
@@ -1528,7 +2007,7 @@
   ASSERT_EQ(0, cpdf_page_object->GetContentStream());
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kLastRemovedMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldRemovedChecksum());
   }
 
   CloseSavedPage(saved_page);
@@ -1537,7 +2016,7 @@
 
 TEST_F(FPDFEditEmbedderTest, RemoveAllFromMultipleStreams) {
   // Load document with some text.
-  EXPECT_TRUE(OpenDocument("hello_world_split_streams.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world_split_streams.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1563,10 +2042,9 @@
 
   ASSERT_EQ(0, FPDFPage_CountObjects(page));
 
-  const char kAllRemovedMD5[] = "eee4600ac08b458ac7ac2320e225674c";
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedChecksum);
   }
 
   // Save the file
@@ -1576,11 +2054,12 @@
   // Re-open the file and check the page object count is still 0.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
 
   EXPECT_EQ(0, FPDFPage_CountObjects(saved_page));
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedChecksum);
   }
 
   CloseSavedPage(saved_page);
@@ -1589,7 +2068,7 @@
 
 TEST_F(FPDFEditEmbedderTest, InsertPageObjectAndSave) {
   // Load document with some text.
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1611,6 +2090,7 @@
   // Re-open the file and check the page object count is still 3.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
   EXPECT_EQ(3, FPDFPage_CountObjects(saved_page));
   CloseSavedPage(saved_page);
   CloseSavedDocument();
@@ -1618,7 +2098,7 @@
 
 TEST_F(FPDFEditEmbedderTest, InsertPageObjectEditAndSave) {
   // Load document with some text.
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1644,28 +2124,24 @@
   // Re-open the file and check the page object count is still 3.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
   EXPECT_EQ(3, FPDFPage_CountObjects(saved_page));
   CloseSavedPage(saved_page);
   CloseSavedDocument();
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_InsertAndRemoveLargeFile DISABLED_InsertAndRemoveLargeFile
-#else
-#define MAYBE_InsertAndRemoveLargeFile InsertAndRemoveLargeFile
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_InsertAndRemoveLargeFile) {
+TEST_F(FPDFEditEmbedderTest, InsertAndRemoveLargeFile) {
   const int kOriginalObjectCount = 600;
 
   // Load document with many objects.
-  EXPECT_TRUE(OpenDocument("many_rectangles.pdf"));
+  ASSERT_TRUE(OpenDocument("many_rectangles.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
-  const char kOriginalMD5[] = "b0170c575b65ecb93ebafada0ff0f038";
+
+  using pdfium::ManyRectanglesChecksum;
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 300, kOriginalMD5);
+    CompareBitmap(page_bitmap.get(), 200, 300, ManyRectanglesChecksum());
   }
 
   // Add a black rectangle.
@@ -1677,10 +2153,14 @@
 
   // Verify the black rectangle was added.
   ASSERT_EQ(kOriginalObjectCount + 1, FPDFPage_CountObjects(page));
-  const char kPlusRectangleMD5[] = "6b9396ab570754b32b04ca629e902f77";
+  const char* plus_rectangle_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "0d3715fcfb9bd0dd25dcce60800bff47";
+    return "6b9396ab570754b32b04ca629e902f77";
+  }();
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 300, kPlusRectangleMD5);
+    CompareBitmap(page_bitmap.get(), 200, 300, plus_rectangle_checksum);
   }
 
   // Save the file.
@@ -1691,10 +2171,11 @@
   // Re-open the file and check the rectangle added is still there.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
   EXPECT_EQ(kOriginalObjectCount + 1, FPDFPage_CountObjects(saved_page));
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
-    CompareBitmap(page_bitmap.get(), 200, 300, kPlusRectangleMD5);
+    CompareBitmap(page_bitmap.get(), 200, 300, plus_rectangle_checksum);
   }
 
   // Remove the added rectangle.
@@ -1704,13 +2185,13 @@
   FPDFPageObj_Destroy(added_object);
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
-    CompareBitmap(page_bitmap.get(), 200, 300, kOriginalMD5);
+    CompareBitmap(page_bitmap.get(), 200, 300, ManyRectanglesChecksum());
   }
   EXPECT_EQ(kOriginalObjectCount, FPDFPage_CountObjects(saved_page));
 
   // Save the file again.
   EXPECT_TRUE(FPDFPage_GenerateContent(saved_page));
-  EXPECT_TRUE(FPDF_SaveAsCopy(saved_document_, this, 0));
+  EXPECT_TRUE(FPDF_SaveAsCopy(saved_document(), this, 0));
 
   CloseSavedPage(saved_page);
   CloseSavedDocument();
@@ -1719,10 +2200,11 @@
   // rest is intact.
   ASSERT_TRUE(OpenSavedDocument());
   saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
   EXPECT_EQ(kOriginalObjectCount, FPDFPage_CountObjects(saved_page));
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
-    CompareBitmap(page_bitmap.get(), 200, 300, kOriginalMD5);
+    CompareBitmap(page_bitmap.get(), 200, 300, ManyRectanglesChecksum());
   }
 
   CloseSavedPage(saved_page);
@@ -1735,10 +2217,10 @@
   ASSERT_TRUE(page);
 
   // Render the blank page and verify it's a blank bitmap.
-  const char kBlankMD5[] = "1940568c9ba33bac5d0b1ee9558c76b3";
+  using pdfium::kBlankPage612By792Checksum;
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 612, 792, kBlankMD5);
+    CompareBitmap(page_bitmap.get(), 612, 792, kBlankPage612By792Checksum);
   }
   ASSERT_EQ(0, FPDFPage_CountObjects(page));
 
@@ -1748,10 +2230,9 @@
   EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
   EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
   FPDFPage_InsertObject(page, red_rect);
-  const char kRedRectangleMD5[] = "66d02eaa6181e2c069ce2ea99beda497";
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 612, 792, kRedRectangleMD5);
+    CompareBitmap(page_bitmap.get(), 612, 792, kRedRectangleChecksum);
   }
   EXPECT_EQ(1, FPDFPage_CountObjects(page));
 
@@ -1760,7 +2241,7 @@
   EXPECT_TRUE(FPDFPage_RemoveObject(page, red_rect));
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 612, 792, kBlankMD5);
+    CompareBitmap(page_bitmap.get(), 612, 792, kBlankPage612By792Checksum);
   }
   EXPECT_EQ(0, FPDFPage_CountObjects(page));
 
@@ -1773,7 +2254,7 @@
 
 TEST_F(FPDFEditEmbedderTest, PathsPoints) {
   CreateNewDocument();
-  FPDF_PAGEOBJECT img = FPDFPageObj_NewImageObj(document_);
+  FPDF_PAGEOBJECT img = FPDFPageObj_NewImageObj(document());
   // This should fail gracefully, even if img is not a path.
   ASSERT_EQ(-1, FPDFPath_CountSegments(img));
 
@@ -1798,15 +2279,9 @@
   FPDFPageObj_Destroy(img);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_PathOnTopOfText DISABLED_PathOnTopOfText
-#else
-#define MAYBE_PathOnTopOfText PathOnTopOfText
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_PathOnTopOfText) {
+TEST_F(FPDFEditEmbedderTest, PathOnTopOfText) {
   // Load document with some text
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1825,28 +2300,24 @@
   EXPECT_TRUE(FPDFPath_Close(black_path));
   FPDFPage_InsertObject(page, black_path);
 
-  // Render and check the result. Text is slightly different on Mac.
+  // Render and check the result.
   ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-#if defined(OS_MACOSX)
-  const char md5[] = "f9e6fa74230f234286bfcada9f7606d8";
-#elif defined(OS_WIN)
-  const char md5[] = "74dd9c393b8b2578d2b7feb032b7daad";
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "72523cfac069f8a81057164682998961";
+#if BUILDFLAG(IS_APPLE)
+    return "279693baca9f48da2d75a8e289aed58e";
 #else
-  const char md5[] = "aa71b09b93b55f467f1290e5111babee";
+    return "fe415d47945c10b9cc8e9ca08887369e";
 #endif
-  CompareBitmap(bitmap.get(), 200, 200, md5);
+  }();
+  CompareBitmap(bitmap.get(), 200, 200, checksum);
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_EditOverExistingContent DISABLED_EditOverExistingContent
-#else
-#define MAYBE_EditOverExistingContent EditOverExistingContent
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_EditOverExistingContent) {
+TEST_F(FPDFEditEmbedderTest, EditOverExistingContent) {
   // Load document with existing content
-  EXPECT_TRUE(OpenDocument("bug_717.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_717.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1862,8 +2333,13 @@
   EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
   FPDFPage_InsertObject(page, red_rect);
 
+  const char* original_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "1e82fbdd21490cee9d3479fe6125af67";
+    return "ad04e5bd0f471a9a564fb034bd0fb073";
+  }();
   ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-  CompareBitmap(bitmap.get(), 612, 792, "ad04e5bd0f471a9a564fb034bd0fb073");
+  CompareBitmap(bitmap.get(), 612, 792, original_checksum);
   EXPECT_TRUE(FPDFPage_GenerateContent(page));
 
   // Now save the result, closing the page and document
@@ -1872,8 +2348,8 @@
 
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
-  VerifySavedRendering(saved_page, 612, 792,
-                       "ad04e5bd0f471a9a564fb034bd0fb073");
+  ASSERT_TRUE(saved_page);
+  VerifySavedRendering(saved_page, 612, 792, original_checksum);
 
   ClearString();
   // Add another opaque rectangle on top of the existing content
@@ -1887,30 +2363,28 @@
   EXPECT_TRUE(FPDFPageObj_SetFillColor(green_rect2, 0, 255, 0, 100));
   EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect2, FPDF_FILLMODE_ALTERNATE, 0));
   FPDFPage_InsertObject(saved_page, green_rect2);
-  const char kLastMD5[] = "4b5b00f824620f8c9b8801ebb98e1cdd";
+  const char* last_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "8705d023e5fec3499d1e30cf2bcc5dc1";
+    return "4b5b00f824620f8c9b8801ebb98e1cdd";
+  }();
   {
     ScopedFPDFBitmap new_bitmap = RenderSavedPage(saved_page);
-    CompareBitmap(new_bitmap.get(), 612, 792, kLastMD5);
+    CompareBitmap(new_bitmap.get(), 612, 792, last_checksum);
   }
   EXPECT_TRUE(FPDFPage_GenerateContent(saved_page));
 
   // Now save the result, closing the page and document
-  EXPECT_TRUE(FPDF_SaveAsCopy(saved_document_, this, 0));
+  EXPECT_TRUE(FPDF_SaveAsCopy(saved_document(), this, 0));
 
   CloseSavedPage(saved_page);
   CloseSavedDocument();
 
   // Render the saved result
-  VerifySavedDocument(612, 792, kLastMD5);
+  VerifySavedDocument(612, 792, last_checksum);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_AddStrokedPaths DISABLED_AddStrokedPaths
-#else
-#define MAYBE_AddStrokedPaths AddStrokedPaths
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_AddStrokedPaths) {
+TEST_F(FPDFEditEmbedderTest, AddStrokedPaths) {
   // Start with a blank page
   FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
 
@@ -1928,8 +2402,12 @@
   FPDFPage_InsertObject(page, rect);
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 612, 792,
-                  "64bd31f862a89e0a9e505a5af6efd506");
+    const char* checksum_1 = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+        return "1469acf60e7647ebeb8e1fb08c5d6c7a";
+      return "64bd31f862a89e0a9e505a5af6efd506";
+    }();
+    CompareBitmap(page_bitmap.get(), 612, 792, checksum_1);
   }
 
   // Add crossed-checkmark
@@ -1944,8 +2422,12 @@
   FPDFPage_InsertObject(page, check);
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 612, 792,
-                  "4b6f3b9d25c4e194821217d5016c3724");
+    const char* checksum_2 = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+        return "68b3194f74abd9d471695ce1415be43f";
+      return "4b6f3b9d25c4e194821217d5016c3724";
+    }();
+    CompareBitmap(page_bitmap.get(), 612, 792, checksum_2);
   }
 
   // Add stroked and filled oval-ish path.
@@ -1961,46 +2443,37 @@
   FPDFPage_InsertObject(page, path);
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 612, 792,
-                  "ff3e6a22326754944cc6e56609acd73b");
+    const char* checksum_3 = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+        return "ea784068651df2b9ba132ce9215e6780";
+      return "ff3e6a22326754944cc6e56609acd73b";
+    }();
+    CompareBitmap(page_bitmap.get(), 612, 792, checksum_3);
   }
   FPDF_ClosePage(page);
 }
 
 // Tests adding text from standard font using FPDFPageObj_NewTextObj.
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_AddStandardFontText DISABLED_AddStandardFontText
-#else
-#define MAYBE_AddStandardFontText AddStandardFontText
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_AddStandardFontText) {
+TEST_F(FPDFEditEmbedderTest, AddStandardFontText) {
   // Start with a blank page
-  FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
+  ScopedFPDFPage page(FPDFPage_New(CreateNewDocument(), 0, 612, 792));
 
   // Add some text to the page
   FPDF_PAGEOBJECT text_object1 =
       FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
   EXPECT_TRUE(text_object1);
-  ScopedFPDFWideString text1 =
-      GetFPDFWideString(L"I'm at the bottom of the page");
+  ScopedFPDFWideString text1 = GetFPDFWideString(kBottomText);
   EXPECT_TRUE(FPDFText_SetText(text_object1, text1.get()));
-  FPDFPageObj_Transform(text_object1, 1, 0, 0, 1, 20, 20);
-  FPDFPage_InsertObject(page, text_object1);
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  static constexpr FS_MATRIX kMatrix1{1, 0, 0, 1, 20, 20};
+  EXPECT_TRUE(FPDFPageObj_SetMatrix(text_object1, &kMatrix1));
+  FPDFPage_InsertObject(page.get(), text_object1);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
   {
-    ScopedFPDFBitmap page_bitmap = RenderPage(page);
-#if defined(OS_MACOSX)
-    const char md5[] = "a4dddc1a3930fa694bbff9789dab4161";
-#elif defined(OS_WIN)
-    const char md5[] = "08d1ff3e5a42801bee6077fd366bef00";
-#else
-    const char md5[] = "eacaa24573b8ce997b3882595f096f00";
-#endif
-    CompareBitmap(page_bitmap.get(), 612, 792, md5);
+    ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
+    CompareBitmap(page_bitmap.get(), 612, 792, BottomTextChecksum());
 
     EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-    VerifySavedDocument(612, 792, md5);
+    VerifySavedDocument(612, 792, BottomTextChecksum());
   }
 
   // Try another font
@@ -2011,21 +2484,24 @@
       GetFPDFWideString(L"Hi, I'm Bold. Times New Roman Bold.");
   EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
   FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 100, 600);
-  FPDFPage_InsertObject(page, text_object2);
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  FPDFPage_InsertObject(page.get(), text_object2);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
   {
-    ScopedFPDFBitmap page_bitmap = RenderPage(page);
-#if defined(OS_MACOSX)
-    const char md5[] = "a5c4ace4c6f27644094813fe1441a21c";
-#elif defined(OS_WIN)
-    const char md5[] = "3755dd35abd4c605755369401ee85b2d";
+    ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
+    const char* checksum = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+        return "3fa05f8935a43a38a8923e9d5fb94365";
+      }
+#if BUILDFLAG(IS_APPLE)
+      return "983baaa1f688eff7a14b1bf91c171a1a";
 #else
-    const char md5[] = "76fcc7d08aa15445efd2e2ceb7c6cc3b";
+      return "161523e196eb5341604cd73e12c97922";
 #endif
-    CompareBitmap(page_bitmap.get(), 612, 792, md5);
+    }();
+    CompareBitmap(page_bitmap.get(), 612, 792, checksum);
 
     EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-    VerifySavedDocument(612, 792, md5);
+    VerifySavedDocument(612, 792, checksum);
   }
 
   // And some randomly transformed text
@@ -2035,26 +2511,28 @@
   ScopedFPDFWideString text3 = GetFPDFWideString(L"Can you read me? <:)>");
   EXPECT_TRUE(FPDFText_SetText(text_object3, text3.get()));
   FPDFPageObj_Transform(text_object3, 1, 1.5, 2, 0.5, 200, 200);
-  FPDFPage_InsertObject(page, text_object3);
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  FPDFPage_InsertObject(page.get(), text_object3);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
   {
-    ScopedFPDFBitmap page_bitmap = RenderPage(page);
-#if defined(OS_MACOSX)
-    const char md5[] = "40b3ef04f915ff4c4208948001763544";
-#elif defined(OS_WIN)
-    const char md5[] = "5ded49fe157f89627903553771431e3d";
+    ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
+    const char* checksum = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+        return "63385a217934d9ee9e17ef4d7f7b2128";
+      }
+#if BUILDFLAG(IS_APPLE)
+      return "e0b3493c5c16e41d0d892ffb48e63fba";
 #else
-    const char md5[] = "344534539aa7c5cc78404cfff4bde7fb";
+      return "1fbf772dca8d82b960631e6683934964";
 #endif
-    CompareBitmap(page_bitmap.get(), 612, 792, md5);
+    }();
+    CompareBitmap(page_bitmap.get(), 612, 792, checksum);
 
     EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-    VerifySavedDocument(612, 792, md5);
+    VerifySavedDocument(612, 792, checksum);
   }
 
   FS_MATRIX matrix;
-  EXPECT_FALSE(FPDFTextObj_GetMatrix(nullptr, &matrix));
-  EXPECT_TRUE(FPDFTextObj_GetMatrix(text_object3, &matrix));
+  EXPECT_TRUE(FPDFPageObj_GetMatrix(text_object3, &matrix));
   EXPECT_FLOAT_EQ(1.0f, matrix.a);
   EXPECT_FLOAT_EQ(1.5f, matrix.b);
   EXPECT_FLOAT_EQ(2.0f, matrix.c);
@@ -2062,16 +2540,47 @@
   EXPECT_FLOAT_EQ(200.0f, matrix.e);
   EXPECT_FLOAT_EQ(200.0f, matrix.f);
 
-  EXPECT_EQ(0, FPDFTextObj_GetFontSize(nullptr));
-  EXPECT_EQ(20, FPDFTextObj_GetFontSize(text_object3));
+  EXPECT_FALSE(FPDFTextObj_GetFontSize(nullptr, nullptr));
+  float size = 55;
+  EXPECT_FALSE(FPDFTextObj_GetFontSize(nullptr, &size));
+  EXPECT_EQ(55, size);
+  EXPECT_TRUE(FPDFTextObj_GetFontSize(text_object3, &size));
+  EXPECT_EQ(20, size);
 
   // TODO(npm): Why are there issues with text rotated by 90 degrees?
   // TODO(npm): FPDF_SaveAsCopy not giving the desired result after this.
-  FPDF_ClosePage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, AddStandardFontTextOfSizeZero) {
+  // Start with a blank page
+  ScopedFPDFPage page(FPDFPage_New(CreateNewDocument(), 0, 612, 792));
+
+  // Add some text of size 0 to the page.
+  FPDF_PAGEOBJECT text_object =
+      FPDFPageObj_NewTextObj(document(), "Arial", 0.0f);
+  EXPECT_TRUE(text_object);
+  ScopedFPDFWideString text = GetFPDFWideString(kBottomText);
+  EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
+  FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 20, 20);
+
+  float size = -1;  // Make sure 'size' gets changed.
+  EXPECT_TRUE(FPDFTextObj_GetFontSize(text_object, &size));
+  EXPECT_EQ(0.0f, size);
+
+  FPDFPage_InsertObject(page.get(), text_object);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
+    CompareBitmap(page_bitmap.get(), 612, 792,
+                  pdfium::kBlankPage612By792Checksum);
+
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+    VerifySavedDocument(612, 792, pdfium::kBlankPage612By792Checksum);
+  }
 }
 
 TEST_F(FPDFEditEmbedderTest, GetTextRenderMode) {
-  EXPECT_TRUE(OpenDocument("text_render_mode.pdf"));
+  ASSERT_TRUE(OpenDocument("text_render_mode.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   ASSERT_EQ(2, FPDFPage_CountObjects(page));
@@ -2089,90 +2598,291 @@
 }
 
 TEST_F(FPDFEditEmbedderTest, SetTextRenderMode) {
-  EXPECT_TRUE(OpenDocument("text_render_mode.pdf"));
+  const char* original_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "48c7f21b2a1a1bbeab24cccccc131e47";
+#if BUILDFLAG(IS_APPLE)
+    return "c488514ce0fc949069ff560407edacd2";
+#else
+    return "97a4fcf3c9581e19917895631af31d41";
+#endif
+  }();
+  const char* stroke_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "d16eb1bb4748eeb5fb801594da70d519";
+    return "e06ee84aeebe926e8c980b7822027e8a";
+  }();
+
+  {
+    ASSERT_TRUE(OpenDocument("text_render_mode.pdf"));
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_TRUE(page);
+    ASSERT_EQ(2, FPDFPage_CountObjects(page));
+
+    // Check the bitmap
+    {
+      ScopedFPDFBitmap page_bitmap = RenderPage(page);
+      CompareBitmap(page_bitmap.get(), 612, 446, original_checksum);
+    }
+
+    // Cannot set on a null object.
+    EXPECT_FALSE(
+        FPDFTextObj_SetTextRenderMode(nullptr, FPDF_TEXTRENDERMODE_UNKNOWN));
+    EXPECT_FALSE(
+        FPDFTextObj_SetTextRenderMode(nullptr, FPDF_TEXTRENDERMODE_INVISIBLE));
+
+    FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
+    ASSERT_TRUE(page_object);
+    EXPECT_EQ(FPDF_TEXTRENDERMODE_FILL,
+              FPDFTextObj_GetTextRenderMode(page_object));
+
+    // Cannot set UNKNOWN as a render mode.
+    EXPECT_FALSE(FPDFTextObj_SetTextRenderMode(page_object,
+                                               FPDF_TEXTRENDERMODE_UNKNOWN));
+
+    EXPECT_TRUE(
+        FPDFTextObj_SetTextRenderMode(page_object, FPDF_TEXTRENDERMODE_STROKE));
+    EXPECT_EQ(FPDF_TEXTRENDERMODE_STROKE,
+              FPDFTextObj_GetTextRenderMode(page_object));
+
+    // Check that bitmap displays changed content
+    {
+      ScopedFPDFBitmap page_bitmap = RenderPage(page);
+      CompareBitmap(page_bitmap.get(), 612, 446, stroke_checksum);
+    }
+
+    // Save a copy.
+    EXPECT_TRUE(FPDFPage_GenerateContent(page));
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+    UnloadPage(page);
+  }
+
+  {
+    // Open the saved copy and render it. Check that the changed text render
+    // mode is kept in the saved copy.
+    ASSERT_TRUE(OpenSavedDocument());
+    FPDF_PAGE saved_page = LoadSavedPage(0);
+    ASSERT_TRUE(saved_page);
+
+    FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(saved_page, 0);
+    EXPECT_TRUE(page_object);
+    EXPECT_EQ(FPDF_TEXTRENDERMODE_STROKE,
+              FPDFTextObj_GetTextRenderMode(page_object));
+
+    ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
+    CompareBitmap(bitmap.get(), 612, 446, stroke_checksum);
+
+    CloseSavedPage(saved_page);
+    CloseSavedDocument();
+  }
+}
+
+TEST_F(FPDFEditEmbedderTest, TextFontProperties) {
+  // bad object tests
+  EXPECT_FALSE(FPDFTextObj_GetFont(nullptr));
+  EXPECT_EQ(0U, FPDFFont_GetFontName(nullptr, nullptr, 5));
+  EXPECT_EQ(-1, FPDFFont_GetFlags(nullptr));
+  EXPECT_EQ(-1, FPDFFont_GetWeight(nullptr));
+  EXPECT_FALSE(FPDFFont_GetItalicAngle(nullptr, nullptr));
+  EXPECT_FALSE(FPDFFont_GetAscent(nullptr, 12.f, nullptr));
+  EXPECT_FALSE(FPDFFont_GetDescent(nullptr, 12.f, nullptr));
+  EXPECT_FALSE(FPDFFont_GetGlyphWidth(nullptr, 's', 12.f, nullptr));
+  EXPECT_FALSE(FPDFFont_GetGlyphPath(nullptr, 's', 12.f));
+
+  // good object tests
+  ASSERT_TRUE(OpenDocument("text_font.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ASSERT_EQ(1, FPDFPage_CountObjects(page));
+  FPDF_PAGEOBJECT text = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(text);
+  float font_size;
+  ASSERT_TRUE(FPDFTextObj_GetFontSize(text, &font_size));
+  FPDF_FONT font = FPDFTextObj_GetFont(text);
+  ASSERT_TRUE(font);
+
+  // null return pointer tests
+  EXPECT_FALSE(FPDFFont_GetItalicAngle(font, nullptr));
+  EXPECT_FALSE(FPDFFont_GetAscent(font, font_size, nullptr));
+  EXPECT_FALSE(FPDFFont_GetDescent(font, font_size, nullptr));
+  EXPECT_FALSE(FPDFFont_GetGlyphWidth(font, 's', font_size, nullptr));
+
+  // correct property tests
+  {
+    EXPECT_EQ(4, FPDFFont_GetFlags(font));
+    EXPECT_EQ(400, FPDFFont_GetWeight(font));
+
+    int angle;
+    EXPECT_TRUE(FPDFFont_GetItalicAngle(font, &angle));
+    EXPECT_EQ(0, angle);
+
+    float ascent;
+    EXPECT_TRUE(FPDFFont_GetAscent(font, font_size, &ascent));
+    EXPECT_FLOAT_EQ(891 * font_size / 1000.0f, ascent);
+
+    float descent;
+    EXPECT_TRUE(FPDFFont_GetDescent(font, font_size, &descent));
+    EXPECT_FLOAT_EQ(-216 * font_size / 1000.0f, descent);
+
+    float a12;
+    float a24;
+    EXPECT_TRUE(FPDFFont_GetGlyphWidth(font, 'a', 12.0f, &a12));
+    EXPECT_FLOAT_EQ(a12, 5.316f);
+    EXPECT_TRUE(FPDFFont_GetGlyphWidth(font, 'a', 24.0f, &a24));
+    EXPECT_FLOAT_EQ(a24, 10.632f);
+  }
+
+  {
+    // FPDFFont_GetFontName() positive testing.
+    unsigned long size = FPDFFont_GetFontName(font, nullptr, 0);
+    const char kExpectedFontName[] = "Liberation Serif";
+    ASSERT_EQ(sizeof(kExpectedFontName), size);
+    std::vector<char> font_name(size);
+    ASSERT_EQ(size, FPDFFont_GetFontName(font, font_name.data(), size));
+    ASSERT_STREQ(kExpectedFontName, font_name.data());
+
+    // FPDFFont_GetFontName() negative testing.
+    ASSERT_EQ(0U, FPDFFont_GetFontName(nullptr, nullptr, 0));
+
+    font_name.resize(2);
+    font_name[0] = 'x';
+    font_name[1] = '\0';
+    size = FPDFFont_GetFontName(font, font_name.data(), font_name.size());
+    ASSERT_EQ(sizeof(kExpectedFontName), size);
+    ASSERT_STREQ("x", font_name.data());
+  }
+
+  {
+    // FPDFFont_GetFontData() positive testing.
+    constexpr size_t kExpectedSize = 8268;
+    std::vector<uint8_t> buf;
+    size_t buf_bytes_required = 123;
+    ASSERT_TRUE(FPDFFont_GetFontData(font, nullptr, 0, &buf_bytes_required));
+    ASSERT_EQ(kExpectedSize, buf_bytes_required);
+
+    buf.resize(kExpectedSize);
+    EXPECT_EQ("495800b8e56e2d37f3bc48a1b52db952", GenerateMD5Base16(buf));
+    buf_bytes_required = 234;
+    // Test with buffer that is too small. Make sure `buf` is unchanged.
+    EXPECT_TRUE(FPDFFont_GetFontData(font, buf.data(), buf.size() - 1,
+                                     &buf_bytes_required));
+    EXPECT_EQ("495800b8e56e2d37f3bc48a1b52db952", GenerateMD5Base16(buf));
+    EXPECT_EQ(kExpectedSize, buf_bytes_required);
+
+    // Test with buffer of the correct size.
+    buf_bytes_required = 234;
+    EXPECT_TRUE(FPDFFont_GetFontData(font, buf.data(), buf.size(),
+                                     &buf_bytes_required));
+    EXPECT_EQ("1a67be75f719b6c476804d85bb9e4844", GenerateMD5Base16(buf));
+    EXPECT_EQ(kExpectedSize, buf_bytes_required);
+
+    // FPDFFont_GetFontData() negative testing.
+    EXPECT_FALSE(FPDFFont_GetFontData(nullptr, nullptr, 0, nullptr));
+    EXPECT_FALSE(FPDFFont_GetFontData(font, nullptr, 0, nullptr));
+
+    buf_bytes_required = 345;
+    EXPECT_FALSE(
+        FPDFFont_GetFontData(nullptr, nullptr, 0, &buf_bytes_required));
+    EXPECT_EQ(345u, buf_bytes_required);
+
+    EXPECT_FALSE(
+        FPDFFont_GetFontData(nullptr, buf.data(), buf.size(), nullptr));
+    EXPECT_FALSE(FPDFFont_GetFontData(font, buf.data(), buf.size(), nullptr));
+
+    buf_bytes_required = 345;
+    EXPECT_FALSE(FPDFFont_GetFontData(nullptr, buf.data(), buf.size(),
+                                      &buf_bytes_required));
+    EXPECT_EQ(345u, buf_bytes_required);
+  }
+  {
+    ASSERT_EQ(1, FPDFFont_GetIsEmbedded(font));
+    ASSERT_EQ(-1, FPDFFont_GetIsEmbedded(nullptr));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, NoEmbeddedFontData) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   ASSERT_EQ(2, FPDFPage_CountObjects(page));
 
-  // Check the bitmap
-  {
-#if defined(OS_MACOSX)
-    const char md5[] = "139846b4ffbd34b1fd67e3b82cf33b7e";
-#elif defined(OS_WIN)
-    const char md5[] = "de6e86bad3e9fda753a8471a45cfbb58";
-#else
-    const char md5[] = "5a012d2920ac075c39ffa9437ea42faa";
-#endif
-    ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 612, 446, md5);
-  }
+  // Since hello_world.pdf does not embed any font data, FPDFFont_GetFontData()
+  // will return the substitution font data. Since pdfium_embeddertest is
+  // hermetic, this first object consistently maps to Tinos-Regular.ttf.
+  constexpr size_t kTinosRegularSize = 469968;
+  FPDF_PAGEOBJECT text = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(text);
+  FPDF_FONT font = FPDFTextObj_GetFont(text);
+  ASSERT_TRUE(font);
+  std::vector<uint8_t> buf;
+  buf.resize(kTinosRegularSize);
+  size_t buf_bytes_required;
+  ASSERT_TRUE(
+      FPDFFont_GetFontData(font, buf.data(), buf.size(), &buf_bytes_required));
+  EXPECT_EQ(kTinosRegularSize, buf_bytes_required);
+  EXPECT_EQ("2b019558f2c2de0b7cbc0a6e64b20599", GenerateMD5Base16(buf));
+  EXPECT_EQ(0, FPDFFont_GetIsEmbedded(font));
 
-  // Cannot set on a null object.
-  EXPECT_FALSE(
-      FPDFTextObj_SetTextRenderMode(nullptr, FPDF_TEXTRENDERMODE_UNKNOWN));
-  EXPECT_FALSE(
-      FPDFTextObj_SetTextRenderMode(nullptr, FPDF_TEXTRENDERMODE_INVISIBLE));
-
-  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
-  ASSERT_TRUE(page_object);
-  EXPECT_EQ(FPDF_TEXTRENDERMODE_FILL,
-            FPDFTextObj_GetTextRenderMode(page_object));
-
-  // Cannot set UNKNOWN as a render mode.
-  EXPECT_FALSE(
-      FPDFTextObj_SetTextRenderMode(page_object, FPDF_TEXTRENDERMODE_UNKNOWN));
-
-  EXPECT_TRUE(
-      FPDFTextObj_SetTextRenderMode(page_object, FPDF_TEXTRENDERMODE_STROKE));
-  EXPECT_EQ(FPDF_TEXTRENDERMODE_STROKE,
-            FPDFTextObj_GetTextRenderMode(page_object));
-
-  // Check that bitmap displays changed content
-  {
-    const char md5[] = "412e52e621b46bd77baf2162e1fb1a1d";
-    ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 612, 446, md5);
-  }
+  // Similarly, the second object consistently maps to Arimo-Regular.ttf.
+  constexpr size_t kArimoRegularSize = 436180;
+  text = FPDFPage_GetObject(page, 1);
+  ASSERT_TRUE(text);
+  font = FPDFTextObj_GetFont(text);
+  ASSERT_TRUE(font);
+  buf.resize(kArimoRegularSize);
+  ASSERT_TRUE(
+      FPDFFont_GetFontData(font, buf.data(), buf.size(), &buf_bytes_required));
+  EXPECT_EQ(kArimoRegularSize, buf_bytes_required);
+  EXPECT_EQ("7ac02a544211773d9636e056e9da6c35", GenerateMD5Base16(buf));
+  EXPECT_EQ(0, FPDFFont_GetIsEmbedded(font));
 
   UnloadPage(page);
 }
 
-TEST_F(FPDFEditEmbedderTest, TestGetTextFontName) {
-  EXPECT_TRUE(OpenDocument("text_font.pdf"));
+TEST_F(FPDFEditEmbedderTest, GlyphPaths) {
+  // bad glyphpath
+  EXPECT_EQ(-1, FPDFGlyphPath_CountGlyphSegments(nullptr));
+  EXPECT_FALSE(FPDFGlyphPath_GetGlyphPathSegment(nullptr, 1));
+
+  ASSERT_TRUE(OpenDocument("text_font.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   ASSERT_EQ(1, FPDFPage_CountObjects(page));
-
-  // FPDFTextObj_GetFontName() positive testing.
   FPDF_PAGEOBJECT text = FPDFPage_GetObject(page, 0);
-  unsigned long size = FPDFTextObj_GetFontName(text, nullptr, 0);
-  const char kExpectedFontName[] = "Liberation Serif";
-  ASSERT_EQ(sizeof(kExpectedFontName), size);
-  std::vector<char> font_name(size);
-  ASSERT_EQ(size, FPDFTextObj_GetFontName(text, font_name.data(), size));
-  ASSERT_STREQ(kExpectedFontName, font_name.data());
+  ASSERT_TRUE(text);
+  FPDF_FONT font = FPDFTextObj_GetFont(text);
+  ASSERT_TRUE(font);
 
-  // FPDFTextObj_GetFontName() negative testing.
-  ASSERT_EQ(0U, FPDFTextObj_GetFontName(nullptr, nullptr, 0));
+  // bad glyph argument.
+  ASSERT_FALSE(FPDFFont_GetGlyphPath(font, 1, 12.0f));
 
-  font_name.resize(2);
-  font_name[0] = 'x';
-  font_name[1] = '\0';
-  size = FPDFTextObj_GetFontName(text, font_name.data(), font_name.size());
-  ASSERT_EQ(sizeof(kExpectedFontName), size);
-  ASSERT_EQ(std::string("x"), std::string(font_name.data()));
+  // good glyphpath
+  FPDF_GLYPHPATH gpath = FPDFFont_GetGlyphPath(font, 's', 12.0f);
+  ASSERT_TRUE(gpath);
+
+  int count = FPDFGlyphPath_CountGlyphSegments(gpath);
+  ASSERT_GT(count, 0);
+  EXPECT_FALSE(FPDFGlyphPath_GetGlyphPathSegment(gpath, -1));
+  EXPECT_FALSE(FPDFGlyphPath_GetGlyphPathSegment(gpath, count));
+
+  FPDF_PATHSEGMENT segment = FPDFGlyphPath_GetGlyphPathSegment(gpath, 1);
+  ASSERT_TRUE(segment);
+  EXPECT_EQ(FPDF_SEGMENT_BEZIERTO, FPDFPathSegment_GetType(segment));
 
   UnloadPage(page);
 }
 
-TEST_F(FPDFEditEmbedderTest, TestFormGetObjects) {
-  EXPECT_TRUE(OpenDocument("form_object.pdf"));
+TEST_F(FPDFEditEmbedderTest, FormGetObjects) {
+  ASSERT_TRUE(OpenDocument("form_object.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   ASSERT_EQ(1, FPDFPage_CountObjects(page));
 
   FPDF_PAGEOBJECT form = FPDFPage_GetObject(page, 0);
-  EXPECT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(form));
+  ASSERT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(form));
   ASSERT_EQ(-1, FPDFFormObj_CountObjects(nullptr));
   ASSERT_EQ(2, FPDFFormObj_CountObjects(form));
 
@@ -2196,17 +2906,12 @@
   ASSERT_EQ(nullptr, FPDFFormObj_GetObject(form, -1));
   ASSERT_EQ(nullptr, FPDFFormObj_GetObject(form, 2));
 
-  // Reset the form object matrix to identity.
-  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(form);
-  CPDF_FormObject* pFormObj = pPageObj->AsForm();
-  pFormObj->Transform(pFormObj->form_matrix().GetInverse());
-
-  // FPDFFormObj_GetMatrix() positive testing.
+  // FPDFPageObj_GetMatrix() positive testing for forms.
   static constexpr FS_MATRIX kMatrix = {1.0f, 1.5f, 2.0f, 2.5f, 100.0f, 200.0f};
-  pFormObj->Transform(CFXMatrixFromFSMatrix(kMatrix));
+  EXPECT_TRUE(FPDFPageObj_SetMatrix(form, &kMatrix));
 
   FS_MATRIX matrix;
-  EXPECT_TRUE(FPDFFormObj_GetMatrix(form, &matrix));
+  EXPECT_TRUE(FPDFPageObj_GetMatrix(form, &matrix));
   EXPECT_FLOAT_EQ(kMatrix.a, matrix.a);
   EXPECT_FLOAT_EQ(kMatrix.b, matrix.b);
   EXPECT_FLOAT_EQ(kMatrix.c, matrix.c);
@@ -2214,22 +2919,63 @@
   EXPECT_FLOAT_EQ(kMatrix.e, matrix.e);
   EXPECT_FLOAT_EQ(kMatrix.f, matrix.f);
 
-  // FPDFFormObj_GetMatrix() negative testing.
-  EXPECT_FALSE(FPDFFormObj_GetMatrix(nullptr, &matrix));
-  EXPECT_FALSE(FPDFFormObj_GetMatrix(form, nullptr));
-  EXPECT_FALSE(FPDFFormObj_GetMatrix(nullptr, nullptr));
+  // FPDFPageObj_GetMatrix() negative testing for forms.
+  EXPECT_FALSE(FPDFPageObj_GetMatrix(form, nullptr));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, ModifyFormObject) {
+  const char* orig_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "1c6dae4b04fea7430a791135721eaba5";
+    }
+#if BUILDFLAG(IS_APPLE)
+    return "a637057185f50aac1aa5490f726aef95";
+#else
+    return "34a9ec0a9581a7970e073c0bcc4ca676";
+#endif
+  }();
+  const char* new_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "7282fe98693c0a7ad2c1b3f3f9563977";
+    }
+#if BUILDFLAG(IS_APPLE)
+    return "8ad9d79b02b609ff734e2a2195c96e2d";
+#else
+    return "609b5632a21c886fa93182dbc290bf7a";
+#endif
+  }();
+
+  ASSERT_TRUE(OpenDocument("form_object.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ASSERT_EQ(1, FPDFPage_CountObjects(page));
+
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    CompareBitmap(bitmap.get(), 62, 69, orig_checksum);
+  }
+
+  FPDF_PAGEOBJECT form = FPDFPage_GetObject(page, 0);
+  ASSERT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(form));
+
+  FPDFPageObj_Transform(form, 0.5, 0, 0, 0.5, 0, 0);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    CompareBitmap(bitmap.get(), 62, 69, new_checksum);
+  }
+
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedDocument(62, 69, new_checksum);
 
   UnloadPage(page);
 }
 
 // Tests adding text from standard font using FPDFText_LoadStandardFont.
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_AddStandardFontText2 DISABLED_AddStandardFontText2
-#else
-#define MAYBE_AddStandardFontText2 AddStandardFontText2
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_AddStandardFontText2) {
+TEST_F(FPDFEditEmbedderTest, AddStandardFontText2) {
   // Start with a blank page
   ScopedFPDFPage page(FPDFPage_New(CreateNewDocument(), 0, 612, 792));
 
@@ -2241,20 +2987,12 @@
   FPDF_PAGEOBJECT text_object =
       FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
   EXPECT_TRUE(text_object);
-  ScopedFPDFWideString text =
-      GetFPDFWideString(L"I'm at the bottom of the page");
+  ScopedFPDFWideString text = GetFPDFWideString(kBottomText);
   EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
   FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 20, 20);
   FPDFPage_InsertObject(page.get(), text_object);
   ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
-#if defined(OS_MACOSX)
-  const char md5[] = "a4dddc1a3930fa694bbff9789dab4161";
-#elif defined(OS_WIN)
-  const char md5[] = "08d1ff3e5a42801bee6077fd366bef00";
-#else
-  const char md5[] = "eacaa24573b8ce997b3882595f096f00";
-#endif
-  CompareBitmap(page_bitmap.get(), 612, 792, md5);
+  CompareBitmap(page_bitmap.get(), 612, 792, BottomTextChecksum());
 }
 
 TEST_F(FPDFEditEmbedderTest, LoadStandardFonts) {
@@ -2310,9 +3048,11 @@
 
   // Check that the ExtGState was created
   CPDF_Page* cpage = CPDFPageFromFPDFPage(page.get());
-  CPDF_Dictionary* graphics_dict = cpage->m_pResources->GetDictFor("ExtGState");
+  RetainPtr<const CPDF_Dictionary> graphics_dict =
+      cpage->GetResources()->GetDictFor("ExtGState");
   ASSERT_TRUE(graphics_dict);
-  EXPECT_EQ(2u, graphics_dict->size());
+  EXPECT_THAT(graphics_dict->GetKeys(),
+              UnorderedElementsAreArray({"FXE1", "FXE2"}));
 
   // Add a text object causing no change to the graphics dictionary
   FPDF_PAGEOBJECT text1 = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
@@ -2321,7 +3061,8 @@
   EXPECT_TRUE(FPDFPageObj_SetFillColor(text1, 100, 100, 100, 255));
   FPDFPage_InsertObject(page.get(), text1);
   EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
-  EXPECT_EQ(2u, graphics_dict->size());
+  EXPECT_THAT(graphics_dict->GetKeys(),
+              UnorderedElementsAreArray({"FXE1", "FXE2"}));
 
   // Add a text object increasing the size of the graphics dictionary
   FPDF_PAGEOBJECT text2 =
@@ -2330,7 +3071,8 @@
   FPDFPageObj_SetBlendMode(text2, "Darken");
   EXPECT_TRUE(FPDFPageObj_SetFillColor(text2, 0, 0, 255, 150));
   EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
-  EXPECT_EQ(3u, graphics_dict->size());
+  EXPECT_THAT(graphics_dict->GetKeys(),
+              UnorderedElementsAreArray({"FXE1", "FXE2", "FXE3"}));
 
   // Add a path that should reuse graphics
   FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(400, 100);
@@ -2338,7 +3080,8 @@
   EXPECT_TRUE(FPDFPageObj_SetFillColor(path, 200, 200, 100, 150));
   FPDFPage_InsertObject(page.get(), path);
   EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
-  EXPECT_EQ(3u, graphics_dict->size());
+  EXPECT_THAT(graphics_dict->GetKeys(),
+              UnorderedElementsAreArray({"FXE1", "FXE2", "FXE3"}));
 
   // Add a rect increasing the size of the graphics dictionary
   FPDF_PAGEOBJECT rect2 = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
@@ -2347,7 +3090,8 @@
   EXPECT_TRUE(FPDFPageObj_SetStrokeColor(rect2, 0, 0, 0, 200));
   FPDFPage_InsertObject(page.get(), rect2);
   EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
-  EXPECT_EQ(4u, graphics_dict->size());
+  EXPECT_THAT(graphics_dict->GetKeys(),
+              UnorderedElementsAreArray({"FXE1", "FXE2", "FXE3", "FXE4"}));
 }
 
 TEST_F(FPDFEditEmbedderTest, DoubleGenerating) {
@@ -2363,9 +3107,11 @@
 
   // Check the ExtGState
   CPDF_Page* cpage = CPDFPageFromFPDFPage(page);
-  CPDF_Dictionary* graphics_dict = cpage->m_pResources->GetDictFor("ExtGState");
+  RetainPtr<const CPDF_Dictionary> graphics_dict =
+      cpage->GetResources()->GetDictFor("ExtGState");
   ASSERT_TRUE(graphics_dict);
-  EXPECT_EQ(2u, graphics_dict->size());
+  EXPECT_THAT(graphics_dict->GetKeys(),
+              UnorderedElementsAreArray({"FXE1", "FXE2"}));
 
   // Check the bitmap
   {
@@ -2374,10 +3120,12 @@
                   "5384da3406d62360ffb5cac4476fff1c");
   }
 
-  // Never mind, my new favorite color is blue, increase alpha
+  // Never mind, my new favorite color is blue, increase alpha.
+  // The red graphics state goes away.
   EXPECT_TRUE(FPDFPageObj_SetFillColor(rect, 0, 0, 255, 180));
   EXPECT_TRUE(FPDFPage_GenerateContent(page));
-  EXPECT_EQ(3u, graphics_dict->size());
+  EXPECT_THAT(graphics_dict->GetKeys(),
+              UnorderedElementsAreArray({"FXE1", "FXE3"}));
 
   // Check that bitmap displays changed content
   {
@@ -2388,14 +3136,18 @@
 
   // And now generate, without changes
   EXPECT_TRUE(FPDFPage_GenerateContent(page));
-  EXPECT_EQ(3u, graphics_dict->size());
+  EXPECT_THAT(graphics_dict->GetKeys(),
+              UnorderedElementsAreArray({"FXE1", "FXE3"}));
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
     CompareBitmap(page_bitmap.get(), 612, 792,
                   "2e51656f5073b0bee611d9cd086aa09c");
   }
 
-  // Add some text to the page
+  // Add some text to the page, which starts out with no fonts.
+  RetainPtr<const CPDF_Dictionary> font_dict =
+      cpage->GetResources()->GetDictFor("Font");
+  EXPECT_FALSE(font_dict);
   FPDF_PAGEOBJECT text_object =
       FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
   ScopedFPDFWideString text =
@@ -2404,14 +3156,19 @@
   FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 300, 300);
   FPDFPage_InsertObject(page, text_object);
   EXPECT_TRUE(FPDFPage_GenerateContent(page));
-  CPDF_Dictionary* font_dict = cpage->m_pResources->GetDictFor("Font");
+
+  // After generating the content, there should now be a font resource.
+  font_dict = cpage->GetResources()->GetDictFor("Font");
   ASSERT_TRUE(font_dict);
-  EXPECT_EQ(1u, font_dict->size());
+  EXPECT_THAT(graphics_dict->GetKeys(),
+              UnorderedElementsAreArray({"FXE1", "FXE3"}));
+  EXPECT_THAT(font_dict->GetKeys(), UnorderedElementsAreArray({"FXF1"}));
 
   // Generate yet again, check dicts are reasonably sized
   EXPECT_TRUE(FPDFPage_GenerateContent(page));
-  EXPECT_EQ(3u, graphics_dict->size());
-  EXPECT_EQ(1u, font_dict->size());
+  EXPECT_THAT(graphics_dict->GetKeys(),
+              UnorderedElementsAreArray({"FXE1", "FXE3"}));
+  EXPECT_THAT(font_dict->GetKeys(), UnorderedElementsAreArray({"FXF1"}));
   FPDF_ClosePage(page);
 }
 
@@ -2427,21 +3184,21 @@
   CPDF_Font* typed_font = CPDFFontFromFPDFFont(font.get());
   EXPECT_TRUE(typed_font->IsType1Font());
 
-  const CPDF_Dictionary* font_dict = typed_font->GetFontDict();
-  EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
-  EXPECT_EQ("Type1", font_dict->GetStringFor("Subtype"));
-  EXPECT_EQ("TimesNewRomanPS-BoldMT", font_dict->GetStringFor("BaseFont"));
+  RetainPtr<const CPDF_Dictionary> font_dict = typed_font->GetFontDict();
+  EXPECT_EQ("Font", font_dict->GetNameFor("Type"));
+  EXPECT_EQ("Type1", font_dict->GetNameFor("Subtype"));
+  EXPECT_EQ("Tinos-Bold", font_dict->GetNameFor("BaseFont"));
   ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
   ASSERT_TRUE(font_dict->KeyExist("LastChar"));
   EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
   EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
 
-  const CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
+  RetainPtr<const CPDF_Array> widths_array = font_dict->GetArrayFor("Widths");
   ASSERT_TRUE(widths_array);
   ASSERT_EQ(224u, widths_array->size());
-  EXPECT_EQ(250, widths_array->GetNumberAt(0));
-  EXPECT_EQ(569, widths_array->GetNumberAt(11));
-  EXPECT_EQ(500, widths_array->GetNumberAt(223));
+  EXPECT_EQ(250, widths_array->GetFloatAt(0));
+  EXPECT_EQ(569, widths_array->GetFloatAt(11));
+  EXPECT_EQ(500, widths_array->GetFloatAt(223));
   CheckFontDescriptor(font_dict, FPDF_FONT_TYPE1, true, false, span);
 }
 
@@ -2456,21 +3213,21 @@
   CPDF_Font* typed_font = CPDFFontFromFPDFFont(font.get());
   EXPECT_TRUE(typed_font->IsTrueTypeFont());
 
-  const CPDF_Dictionary* font_dict = typed_font->GetFontDict();
-  EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
-  EXPECT_EQ("TrueType", font_dict->GetStringFor("Subtype"));
-  EXPECT_EQ("CourierNewPSMT", font_dict->GetStringFor("BaseFont"));
+  RetainPtr<const CPDF_Dictionary> font_dict = typed_font->GetFontDict();
+  EXPECT_EQ("Font", font_dict->GetNameFor("Type"));
+  EXPECT_EQ("TrueType", font_dict->GetNameFor("Subtype"));
+  EXPECT_EQ("Cousine-Regular", font_dict->GetNameFor("BaseFont"));
   ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
   ASSERT_TRUE(font_dict->KeyExist("LastChar"));
   EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
   EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
 
-  const CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
+  RetainPtr<const CPDF_Array> widths_array = font_dict->GetArrayFor("Widths");
   ASSERT_TRUE(widths_array);
   ASSERT_EQ(224u, widths_array->size());
-  EXPECT_EQ(600, widths_array->GetNumberAt(33));
-  EXPECT_EQ(600, widths_array->GetNumberAt(74));
-  EXPECT_EQ(600, widths_array->GetNumberAt(223));
+  EXPECT_EQ(600, widths_array->GetFloatAt(33));
+  EXPECT_EQ(600, widths_array->GetFloatAt(74));
+  EXPECT_EQ(600, widths_array->GetFloatAt(223));
   CheckFontDescriptor(font_dict, FPDF_FONT_TRUETYPE, false, false, span);
 }
 
@@ -2486,41 +3243,43 @@
   EXPECT_TRUE(typed_font->IsCIDFont());
 
   // Check font dictionary entries
-  const CPDF_Dictionary* font_dict = typed_font->GetFontDict();
-  EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
-  EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
-  EXPECT_EQ("TimesNewRomanPSMT-Identity-H",
-            font_dict->GetStringFor("BaseFont"));
-  EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
-  const CPDF_Array* descendant_array =
+  RetainPtr<const CPDF_Dictionary> font_dict = typed_font->GetFontDict();
+  EXPECT_EQ("Font", font_dict->GetNameFor("Type"));
+  EXPECT_EQ("Type0", font_dict->GetNameFor("Subtype"));
+  EXPECT_EQ("Tinos-Regular-Identity-H", font_dict->GetNameFor("BaseFont"));
+  EXPECT_EQ("Identity-H", font_dict->GetNameFor("Encoding"));
+  RetainPtr<const CPDF_Array> descendant_array =
       font_dict->GetArrayFor("DescendantFonts");
   ASSERT_TRUE(descendant_array);
   EXPECT_EQ(1u, descendant_array->size());
 
   // Check the CIDFontDict
-  const CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
-  EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
-  EXPECT_EQ("CIDFontType0", cidfont_dict->GetStringFor("Subtype"));
-  EXPECT_EQ("TimesNewRomanPSMT", cidfont_dict->GetStringFor("BaseFont"));
-  const CPDF_Dictionary* cidinfo_dict =
+  RetainPtr<const CPDF_Dictionary> cidfont_dict =
+      descendant_array->GetDictAt(0);
+  EXPECT_EQ("Font", cidfont_dict->GetNameFor("Type"));
+  EXPECT_EQ("CIDFontType0", cidfont_dict->GetNameFor("Subtype"));
+  EXPECT_EQ("Tinos-Regular", cidfont_dict->GetNameFor("BaseFont"));
+  RetainPtr<const CPDF_Dictionary> cidinfo_dict =
       cidfont_dict->GetDictFor("CIDSystemInfo");
   ASSERT_TRUE(cidinfo_dict);
-  const CPDF_Object* registry = cidinfo_dict->GetObjectFor("Registry");
+  RetainPtr<const CPDF_Object> registry =
+      cidinfo_dict->GetObjectFor("Registry");
   ASSERT_TRUE(registry);
   EXPECT_EQ(CPDF_Object::kString, registry->GetType());
   EXPECT_EQ("Adobe", registry->GetString());
-  const CPDF_Object* ordering = cidinfo_dict->GetObjectFor("Ordering");
+  RetainPtr<const CPDF_Object> ordering =
+      cidinfo_dict->GetObjectFor("Ordering");
   ASSERT_TRUE(ordering);
   EXPECT_EQ(CPDF_Object::kString, ordering->GetType());
   EXPECT_EQ("Identity", ordering->GetString());
-  EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
-  CheckFontDescriptor(cidfont_dict, FPDF_FONT_TYPE1, false, false, span);
+  EXPECT_EQ(0, cidinfo_dict->GetFloatFor("Supplement"));
+  CheckFontDescriptor(cidfont_dict.Get(), FPDF_FONT_TYPE1, false, false, span);
 
   // Check widths
-  const CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
+  RetainPtr<const CPDF_Array> widths_array = cidfont_dict->GetArrayFor("W");
   ASSERT_TRUE(widths_array);
   EXPECT_GT(widths_array->size(), 1u);
-  CheckCompositeFontWidths(widths_array, typed_font);
+  CheckCompositeFontWidths(widths_array.Get(), typed_font);
 }
 
 TEST_F(FPDFEditEmbedderTest, LoadCIDType2Font) {
@@ -2535,52 +3294,48 @@
   EXPECT_TRUE(typed_font->IsCIDFont());
 
   // Check font dictionary entries
-  const CPDF_Dictionary* font_dict = typed_font->GetFontDict();
-  EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
-  EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
-  EXPECT_EQ("Arial-ItalicMT", font_dict->GetStringFor("BaseFont"));
-  EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
-  const CPDF_Array* descendant_array =
+  RetainPtr<const CPDF_Dictionary> font_dict = typed_font->GetFontDict();
+  EXPECT_EQ("Font", font_dict->GetNameFor("Type"));
+  EXPECT_EQ("Type0", font_dict->GetNameFor("Subtype"));
+  EXPECT_EQ("Arimo-Italic", font_dict->GetNameFor("BaseFont"));
+  EXPECT_EQ("Identity-H", font_dict->GetNameFor("Encoding"));
+  RetainPtr<const CPDF_Array> descendant_array =
       font_dict->GetArrayFor("DescendantFonts");
   ASSERT_TRUE(descendant_array);
   EXPECT_EQ(1u, descendant_array->size());
 
   // Check the CIDFontDict
-  const CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
-  EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
-  EXPECT_EQ("CIDFontType2", cidfont_dict->GetStringFor("Subtype"));
-  EXPECT_EQ("Arial-ItalicMT", cidfont_dict->GetStringFor("BaseFont"));
-  const CPDF_Dictionary* cidinfo_dict =
+  RetainPtr<const CPDF_Dictionary> cidfont_dict =
+      descendant_array->GetDictAt(0);
+  EXPECT_EQ("Font", cidfont_dict->GetNameFor("Type"));
+  EXPECT_EQ("CIDFontType2", cidfont_dict->GetNameFor("Subtype"));
+  EXPECT_EQ("Arimo-Italic", cidfont_dict->GetNameFor("BaseFont"));
+  RetainPtr<const CPDF_Dictionary> cidinfo_dict =
       cidfont_dict->GetDictFor("CIDSystemInfo");
   ASSERT_TRUE(cidinfo_dict);
-  EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry"));
-  EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering"));
-  EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
-  CheckFontDescriptor(cidfont_dict, FPDF_FONT_TRUETYPE, false, true, span);
+  EXPECT_EQ("Adobe", cidinfo_dict->GetByteStringFor("Registry"));
+  EXPECT_EQ("Identity", cidinfo_dict->GetByteStringFor("Ordering"));
+  EXPECT_EQ(0, cidinfo_dict->GetFloatFor("Supplement"));
+  CheckFontDescriptor(cidfont_dict.Get(), FPDF_FONT_TRUETYPE, false, true,
+                      span);
 
   // Check widths
-  const CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
+  RetainPtr<const CPDF_Array> widths_array = cidfont_dict->GetArrayFor("W");
   ASSERT_TRUE(widths_array);
-  CheckCompositeFontWidths(widths_array, typed_font);
+  CheckCompositeFontWidths(widths_array.Get(), typed_font);
 }
 
 TEST_F(FPDFEditEmbedderTest, NormalizeNegativeRotation) {
   // Load document with a -90 degree rotation
-  EXPECT_TRUE(OpenDocument("bug_713197.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_713197.pdf"));
   FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
+  EXPECT_TRUE(page);
 
   EXPECT_EQ(3, FPDFPage_GetRotation(page));
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_AddTrueTypeFontText DISABLED_AddTrueTypeFontText
-#else
-#define MAYBE_AddTrueTypeFontText AddTrueTypeFontText
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_AddTrueTypeFontText) {
+TEST_F(FPDFEditEmbedderTest, AddTrueTypeFontText) {
   // Start with a blank page
   FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
   {
@@ -2595,20 +3350,12 @@
     FPDF_PAGEOBJECT text_object =
         FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
     EXPECT_TRUE(text_object);
-    ScopedFPDFWideString text =
-        GetFPDFWideString(L"I am testing my loaded font, WEE.");
+    ScopedFPDFWideString text = GetFPDFWideString(kLoadedFontText);
     EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
     FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 400, 400);
     FPDFPage_InsertObject(page, text_object);
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-#if defined(OS_MACOSX)
-    const char md5[] = "17d2b6cd574cf66170b09c8927529a94";
-#elif defined(OS_WIN)
-    const char md5[] = "d60ba39f9698e32360d99e727dd93165";
-#else
-    const char md5[] = "70592859010ffbf532a2237b8118bcc4";
-#endif
-    CompareBitmap(page_bitmap.get(), 612, 792, md5);
+    CompareBitmap(page_bitmap.get(), 612, 792, LoadedFontTextChecksum());
 
     // Add some more text, same font
     FPDF_PAGEOBJECT text_object2 =
@@ -2619,20 +3366,22 @@
     FPDFPage_InsertObject(page, text_object2);
   }
   ScopedFPDFBitmap page_bitmap2 = RenderPage(page);
-#if defined(OS_MACOSX)
-  const char md5_2[] = "8eded4193ff1f0f77b8b600a825e97ea";
-#elif defined(OS_WIN)
-  const char md5_2[] = "2199b579c49ab5f80c246a586a80ee90";
+  const char* insert_true_type_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "4f9a6c7752ac7d4e4c731260fdb5af15";
+#if BUILDFLAG(IS_APPLE)
+    return "c7e2271a7f30e5b919a13ead47cea105";
 #else
-  const char md5_2[] = "c1d10cce1761c4a998a16b2562030568";
+    return "683f4a385a891494100192cb338b11f0";
 #endif
-  CompareBitmap(page_bitmap2.get(), 612, 792, md5_2);
+  }();
+  CompareBitmap(page_bitmap2.get(), 612, 792, insert_true_type_checksum);
 
   EXPECT_TRUE(FPDFPage_GenerateContent(page));
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   FPDF_ClosePage(page);
 
-  VerifySavedDocument(612, 792, md5_2);
+  VerifySavedDocument(612, 792, insert_true_type_checksum);
 }
 
 TEST_F(FPDFEditEmbedderTest, TransformAnnot) {
@@ -2655,21 +3404,16 @@
 }
 
 // TODO(npm): Add tests using Japanese fonts in other OS.
-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_AddCIDFontText DISABLED_AddCIDFontText
-#else
-#define MAYBE_AddCIDFontText AddCIDFontText
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_AddCIDFontText) {
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+TEST_F(FPDFEditEmbedderTest, AddCIDFontText) {
   // Start with a blank page
   FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
   CFX_Font CIDfont;
   {
     // First, get the data from the font
-    CIDfont.LoadSubst("IPAGothic", 1, 0, 400, 0, 932, 0);
-    EXPECT_EQ("IPAGothic", CIDfont.GetFaceName());
+    CIDfont.LoadSubst("Noto Sans CJK JP", true, 0, 400, 0,
+                      FX_CodePage::kShiftJIS, false);
+    EXPECT_EQ("Noto Sans CJK JP", CIDfont.GetFaceName());
     pdfium::span<const uint8_t> span = CIDfont.GetFontSpan();
 
     // Load the data into a FPDF_Font.
@@ -2701,10 +3445,16 @@
   }
 
   // Check that the text renders properly.
-  const char md5[] = "5159a72903fe57bf0cf645c894de8a74";
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "2e174d17de96a760d42ca3a06acbf36a";
+    }
+    return "84d31d11b76845423a2cfc1879c0fbb9";
+  }();
+
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 612, 792, md5);
+    CompareBitmap(page_bitmap.get(), 612, 792, checksum);
   }
 
   // Save the document, close the page.
@@ -2712,27 +3462,25 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   FPDF_ClosePage(page);
 
-  VerifySavedDocument(612, 792, md5);
+  VerifySavedDocument(612, 792, checksum);
 }
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_SaveAndRender DISABLED_SaveAndRender
-#else
-#define MAYBE_SaveAndRender SaveAndRender
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_SaveAndRender) {
-  const char md5[] = "3c20472b0552c0c22b88ab1ed8c6202b";
+TEST_F(FPDFEditEmbedderTest, SaveAndRender) {
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "9a78649e85e69d220c22e0fc316da740";
+    return "3c20472b0552c0c22b88ab1ed8c6202b";
+  }();
   {
-    EXPECT_TRUE(OpenDocument("bug_779.pdf"));
+    ASSERT_TRUE(OpenDocument("bug_779.pdf"));
     FPDF_PAGE page = LoadPage(0);
     ASSERT_NE(nullptr, page);
 
-    // Now add a more complex blue path.
+    // Now add a more complex green path.
     FPDF_PAGEOBJECT green_path = FPDFPageObj_CreateNewPath(20, 20);
     EXPECT_TRUE(FPDFPageObj_SetFillColor(green_path, 0, 255, 0, 200));
-    // TODO(npm): stroking will cause the MD5s to differ.
+    // TODO(npm): stroking will cause the checksums to differ.
     EXPECT_TRUE(FPDFPath_SetDrawMode(green_path, FPDF_FILLMODE_WINDING, 0));
     EXPECT_TRUE(FPDFPath_LineTo(green_path, 20, 63));
     EXPECT_TRUE(FPDFPath_BezierTo(green_path, 55, 55, 78, 78, 90, 90));
@@ -2742,7 +3490,7 @@
     EXPECT_TRUE(FPDFPath_Close(green_path));
     FPDFPage_InsertObject(page, green_path);
     ScopedFPDFBitmap page_bitmap = RenderLoadedPage(page);
-    CompareBitmap(page_bitmap.get(), 612, 792, md5);
+    CompareBitmap(page_bitmap.get(), 612, 792, checksum);
 
     // Now save the result, closing the page and document
     EXPECT_TRUE(FPDFPage_GenerateContent(page));
@@ -2750,12 +3498,12 @@
     UnloadPage(page);
   }
 
-  VerifySavedDocument(612, 792, md5);
+  VerifySavedDocument(612, 792, checksum);
 }
 
 TEST_F(FPDFEditEmbedderTest, AddMark) {
   // Load document with some text.
-  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -2778,6 +3526,7 @@
   // Re-open the file and check the new mark is present.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
 
   CheckMarkCounts(saved_page, 1, 19, 8, 4, 9, 2);
 
@@ -2785,30 +3534,16 @@
   CloseSavedDocument();
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_AddMarkCompressedStream DISABLED_AddMarkCompressedStream
-#else
-#define MAYBE_AddMarkCompressedStream AddMarkCompressedStream
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_AddMarkCompressedStream) {
-#if defined(OS_MACOSX)
-  const char kOriginalMD5[] = "b90475ca64d1348c3bf5e2b77ad9187a";
-#elif defined(OS_WIN)
-  const char kOriginalMD5[] = "795b7ce1626931aa06af0fa23b7d80bb";
-#else
-  const char kOriginalMD5[] = "2baa4c0e1758deba1b9c908e1fbd04ed";
-#endif
-
+TEST_F(FPDFEditEmbedderTest, AddMarkCompressedStream) {
   // Load document with some text in a compressed stream.
-  EXPECT_TRUE(OpenDocument("hello_world_compressed_stream.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world_compressed_stream.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
   // Render and check there are no marks.
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum());
   }
   CheckMarkCounts(page, 0, 2, 0, 0, 0, 0);
 
@@ -2822,7 +3557,7 @@
   // Render and check there is 1 mark.
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum());
   }
   CheckMarkCounts(page, 0, 2, 0, 0, 0, 1);
 
@@ -2834,10 +3569,11 @@
   // Re-open the file and check the new mark is present.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
 
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
-    CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5);
+    CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum());
   }
   CheckMarkCounts(saved_page, 0, 2, 0, 0, 0, 1);
 
@@ -2847,7 +3583,7 @@
 
 TEST_F(FPDFEditEmbedderTest, SetMarkParam) {
   // Load document with some text.
-  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -2896,6 +3632,7 @@
   // Re-open the file and cerify "Position" still maps to "End".
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
 
   CheckMarkCounts(saved_page, 1, kExpectedObjectCount, 8, 4, 9, 1);
   page_object = FPDFPage_GetObject(saved_page, 18);
@@ -2909,13 +3646,7 @@
   CloseSavedDocument();
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_AddMarkedText DISABLED_AddMarkedText
-#else
-#define MAYBE_AddMarkedText AddMarkedText
-#endif
-TEST_F(FPDFEditEmbedderTest, MAYBE_AddMarkedText) {
+TEST_F(FPDFEditEmbedderTest, AddMarkedText) {
   // Start with a blank page.
   FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
 
@@ -2931,8 +3662,7 @@
       FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
 
   EXPECT_TRUE(text_object);
-  ScopedFPDFWideString text1 =
-      GetFPDFWideString(L"I am testing my loaded font, WEE.");
+  ScopedFPDFWideString text1 = GetFPDFWideString(kLoadedFontText);
   EXPECT_TRUE(FPDFText_SetText(text_object, text1.get()));
   FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 400, 400);
   FPDFPage_InsertObject(page, text_object);
@@ -2992,17 +3722,10 @@
   EXPECT_EQ(kBlobLen, out_buffer_len);
   EXPECT_EQ(0, memcmp(block_value, buffer, kBlobLen));
 
-// Render and check the bitmap is the expected one.
-#if defined(OS_MACOSX)
-  const char md5[] = "17d2b6cd574cf66170b09c8927529a94";
-#elif defined(OS_WIN)
-  const char md5[] = "d60ba39f9698e32360d99e727dd93165";
-#else
-  const char md5[] = "70592859010ffbf532a2237b8118bcc4";
-#endif
+  // Render and check the bitmap is the expected one.
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 612, 792, md5);
+    CompareBitmap(page_bitmap.get(), 612, 792, LoadedFontTextChecksum());
   }
 
   // Now save the result.
@@ -3015,6 +3738,7 @@
   // Re-open the file and check the changes were kept in the saved .pdf.
   ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
   EXPECT_EQ(1, FPDFPage_CountObjects(saved_page));
 
   text_object = FPDFPage_GetObject(saved_page, 0);
@@ -3034,7 +3758,7 @@
 }
 
 TEST_F(FPDFEditEmbedderTest, MarkGetName) {
-  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 18);
@@ -3067,7 +3791,7 @@
 }
 
 TEST_F(FPDFEditEmbedderTest, MarkGetParamKey) {
-  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 18);
@@ -3107,7 +3831,7 @@
 }
 
 TEST_F(FPDFEditEmbedderTest, MarkGetIntParam) {
-  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 8);
@@ -3143,7 +3867,7 @@
 }
 
 TEST_F(FPDFEditEmbedderTest, MarkGetStringParam) {
-  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 18);
@@ -3190,7 +3914,68 @@
   UnloadPage(page);
 }
 
-TEST_F(FPDFEditEmbedderTest, ExtractImageBitmap) {
+// See also FPDFStructTreeEmbedderTest.GetMarkedContentID, which traverses the
+// marked contents using FPDF_StructTree_GetForPage() and related API.
+TEST_F(FPDFEditEmbedderTest, TraverseMarkedContentID) {
+  ASSERT_TRUE(OpenDocument("marked_content_id.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ASSERT_EQ(2, FPDFPage_CountObjects(page));
+  FPDF_PAGEOBJECT object1 = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(object1);
+  ASSERT_EQ(1, FPDFPageObj_CountMarks(object1));
+
+  FPDF_PAGEOBJECTMARK mark11 = FPDFPageObj_GetMark(object1, 0);
+  ASSERT_TRUE(mark11);
+  unsigned long len = 0;
+  unsigned short buf[40];
+  ASSERT_TRUE(FPDFPageObjMark_GetName(mark11, buf, sizeof(buf), &len));
+  EXPECT_EQ(18u, len);
+  EXPECT_EQ(L"Artifact", GetPlatformWString(buf));
+  ASSERT_EQ(2, FPDFPageObjMark_CountParams(mark11));
+  ASSERT_TRUE(FPDFPageObjMark_GetParamKey(mark11, 0, buf, sizeof(buf), &len));
+  EXPECT_EQ(10u, len);
+  EXPECT_EQ(L"BBox", GetPlatformWString(buf));
+  EXPECT_EQ(FPDF_OBJECT_ARRAY,
+            FPDFPageObjMark_GetParamValueType(mark11, "BBox"));
+  ASSERT_TRUE(FPDFPageObjMark_GetParamKey(mark11, 1, buf, sizeof(buf), &len));
+  EXPECT_EQ(10u, len);
+  EXPECT_EQ(L"Type", GetPlatformWString(buf));
+  EXPECT_EQ(FPDF_OBJECT_NAME,
+            FPDFPageObjMark_GetParamValueType(mark11, "Type"));
+
+  FPDF_PAGEOBJECT object2 = FPDFPage_GetObject(page, 1);
+  ASSERT_TRUE(object2);
+  ASSERT_EQ(2, FPDFPageObj_CountMarks(object2));
+  EXPECT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(object2));
+
+  FPDF_PAGEOBJECTMARK mark21 = FPDFPageObj_GetMark(object2, 0);
+  ASSERT_TRUE(mark21);
+  ASSERT_TRUE(FPDFPageObjMark_GetName(mark21, buf, sizeof(buf), &len));
+  EXPECT_EQ(14u, len);
+  EXPECT_EQ(L"Figure", GetPlatformWString(buf));
+  ASSERT_EQ(1, FPDFPageObjMark_CountParams(mark21));
+  ASSERT_TRUE(FPDFPageObjMark_GetParamKey(mark21, 0, buf, sizeof(buf), &len));
+  EXPECT_EQ(10u, len);
+  EXPECT_EQ(L"MCID", GetPlatformWString(buf));
+  ASSERT_EQ(FPDF_OBJECT_NUMBER,
+            FPDFPageObjMark_GetParamValueType(mark21, "MCID"));
+  int mcid = -1;
+  ASSERT_TRUE(FPDFPageObjMark_GetParamIntValue(mark21, "MCID", &mcid));
+  EXPECT_EQ(0, mcid);
+
+  FPDF_PAGEOBJECTMARK mark22 = FPDFPageObj_GetMark(object2, 1);
+  ASSERT_TRUE(mark22);
+  ASSERT_TRUE(FPDFPageObjMark_GetName(mark22, buf, sizeof(buf), &len));
+  EXPECT_EQ(18u, len);
+  EXPECT_EQ(L"ClipSpan", GetPlatformWString(buf));
+  EXPECT_EQ(0, FPDFPageObjMark_CountParams(mark22));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, GetBitmap) {
   ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
@@ -3200,51 +3985,108 @@
   EXPECT_NE(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
   EXPECT_FALSE(FPDFImageObj_GetBitmap(obj));
 
-  obj = FPDFPage_GetObject(page, 33);
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  FPDF_BITMAP bitmap = FPDFImageObj_GetBitmap(obj);
-  EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
-  CompareBitmap(bitmap, 109, 88, "cb3637934bb3b95a6e4ae1ea9eb9e56e");
-  FPDFBitmap_Destroy(bitmap);
+  {
+    obj = FPDFPage_GetObject(page, 33);
+    ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+    ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
+    EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
+    CompareBitmap(bitmap.get(), 109, 88, kEmbeddedImage33Checksum);
+  }
 
-  obj = FPDFPage_GetObject(page, 34);
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  bitmap = FPDFImageObj_GetBitmap(obj);
-  EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
-  CompareBitmap(bitmap, 103, 75, "c8d51fa6821ceb2a67f08446ff236c40");
-  FPDFBitmap_Destroy(bitmap);
+  {
+    obj = FPDFPage_GetObject(page, 34);
+    ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+    ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
+    EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
+    CompareBitmap(bitmap.get(), 103, 75, "c8d51fa6821ceb2a67f08446ff236c40");
+  }
 
-  obj = FPDFPage_GetObject(page, 35);
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  bitmap = FPDFImageObj_GetBitmap(obj);
-  EXPECT_EQ(FPDFBitmap_Gray, FPDFBitmap_GetFormat(bitmap));
-  CompareBitmap(bitmap, 92, 68, "9c6d76cb1e37ef8514f9455d759391f3");
-  FPDFBitmap_Destroy(bitmap);
+  {
+    obj = FPDFPage_GetObject(page, 35);
+    ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+    ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
+    EXPECT_EQ(FPDFBitmap_Gray, FPDFBitmap_GetFormat(bitmap.get()));
+    CompareBitmap(bitmap.get(), 92, 68, "9c6d76cb1e37ef8514f9455d759391f3");
+  }
 
-  obj = FPDFPage_GetObject(page, 36);
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  bitmap = FPDFImageObj_GetBitmap(obj);
-  EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
-  CompareBitmap(bitmap, 79, 60, "f4e72fb783a01c7b4614cdc25eaa98ac");
-  FPDFBitmap_Destroy(bitmap);
+  {
+    obj = FPDFPage_GetObject(page, 36);
+    ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+    ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
+    EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
+    CompareBitmap(bitmap.get(), 79, 60, "f4e72fb783a01c7b4614cdc25eaa98ac");
+  }
 
-  obj = FPDFPage_GetObject(page, 37);
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  bitmap = FPDFImageObj_GetBitmap(obj);
-  EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
-  CompareBitmap(bitmap, 126, 106, "2cf9e66414c72461f4ccbf9cdebdfa1b");
-  FPDFBitmap_Destroy(bitmap);
+  {
+    obj = FPDFPage_GetObject(page, 37);
+    ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+    ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
+    EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
+    CompareBitmap(bitmap.get(), 126, 106, "2cf9e66414c72461f4ccbf9cdebdfa1b");
+  }
 
-  obj = FPDFPage_GetObject(page, 38);
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  bitmap = FPDFImageObj_GetBitmap(obj);
-  EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
-  CompareBitmap(bitmap, 194, 119, "a8f3a126cec274dab8242fd2ccdc1b8b");
-  FPDFBitmap_Destroy(bitmap);
+  {
+    obj = FPDFPage_GetObject(page, 38);
+    ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+    ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
+    EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
+    CompareBitmap(bitmap.get(), 194, 119, "a8f3a126cec274dab8242fd2ccdc1b8b");
+  }
+
   UnloadPage(page);
 }
 
-TEST_F(FPDFEditEmbedderTest, ExtractJBigImageBitmap) {
+TEST_F(FPDFEditEmbedderTest, GetBitmapIgnoresSetMatrix) {
+  ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ASSERT_EQ(39, FPDFPage_CountObjects(page));
+
+  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 33);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+
+  {
+    // Render |obj| as is.
+    ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
+    EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
+    CompareBitmap(bitmap.get(), 109, 88, kEmbeddedImage33Checksum);
+  }
+
+  // Check the matrix for |obj|.
+  FS_MATRIX matrix;
+  EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
+  EXPECT_FLOAT_EQ(53.0f, matrix.a);
+  EXPECT_FLOAT_EQ(0.0f, matrix.b);
+  EXPECT_FLOAT_EQ(0.0f, matrix.c);
+  EXPECT_FLOAT_EQ(43.0f, matrix.d);
+  EXPECT_FLOAT_EQ(72.0f, matrix.e);
+  EXPECT_FLOAT_EQ(646.510009765625f, matrix.f);
+
+  // Modify the matrix for |obj|.
+  matrix.a = 120.0;
+  EXPECT_TRUE(FPDFPageObj_SetMatrix(obj, &matrix));
+
+  // Make sure the matrix modification took place.
+  EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
+  EXPECT_FLOAT_EQ(120.0f, matrix.a);
+  EXPECT_FLOAT_EQ(0.0f, matrix.b);
+  EXPECT_FLOAT_EQ(0.0f, matrix.c);
+  EXPECT_FLOAT_EQ(43.0f, matrix.d);
+  EXPECT_FLOAT_EQ(72.0f, matrix.e);
+  EXPECT_FLOAT_EQ(646.510009765625f, matrix.f);
+
+  {
+    // Render |obj| again. Note that the FPDFPageObj_SetMatrix() call has no
+    // effect.
+    ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
+    EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
+    CompareBitmap(bitmap.get(), 109, 88, kEmbeddedImage33Checksum);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, GetBitmapForJBigImage) {
   ASSERT_TRUE(OpenDocument("bug_631912.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
@@ -3262,8 +4104,150 @@
   UnloadPage(page);
 }
 
+TEST_F(FPDFEditEmbedderTest, GetBitmapIgnoresSMask) {
+  ASSERT_TRUE(OpenDocument("matte.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  constexpr int kExpectedObjects = 4;
+  ASSERT_EQ(kExpectedObjects, FPDFPage_CountObjects(page));
+
+  for (int i = 0; i < kExpectedObjects; ++i) {
+    FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i);
+    ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+    ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
+    ASSERT_TRUE(bitmap);
+    EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
+    CompareBitmap(bitmap.get(), 50, 50, "46c9a1dbe0b44765ce46017ad629a2fe");
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapHandlesSetMatrix) {
+  ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ASSERT_EQ(39, FPDFPage_CountObjects(page));
+
+  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 33);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+
+  {
+    // Render `obj` as is.
+    ScopedFPDFBitmap bitmap(
+        FPDFImageObj_GetRenderedBitmap(document(), page, obj));
+    EXPECT_EQ(FPDFBitmap_BGRA, FPDFBitmap_GetFormat(bitmap.get()));
+    const char* checksum = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+        return "3b51fc066ee18efbf70bab0501763603";
+      return "582ca300e003f512d7b552c7b5b45d2e";
+    }();
+    CompareBitmap(bitmap.get(), 53, 43, checksum);
+  }
+
+  // Check the matrix for `obj`.
+  FS_MATRIX matrix;
+  EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
+  EXPECT_FLOAT_EQ(53.0f, matrix.a);
+  EXPECT_FLOAT_EQ(0.0f, matrix.b);
+  EXPECT_FLOAT_EQ(0.0f, matrix.c);
+  EXPECT_FLOAT_EQ(43.0f, matrix.d);
+  EXPECT_FLOAT_EQ(72.0f, matrix.e);
+  EXPECT_FLOAT_EQ(646.510009765625f, matrix.f);
+
+  // Modify the matrix for `obj`.
+  matrix.a = 120.0;
+  EXPECT_TRUE(FPDFPageObj_SetMatrix(obj, &matrix));
+
+  // Make sure the matrix modification took place.
+  EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
+  EXPECT_FLOAT_EQ(120.0f, matrix.a);
+  EXPECT_FLOAT_EQ(0.0f, matrix.b);
+  EXPECT_FLOAT_EQ(0.0f, matrix.c);
+  EXPECT_FLOAT_EQ(43.0f, matrix.d);
+  EXPECT_FLOAT_EQ(72.0f, matrix.e);
+  EXPECT_FLOAT_EQ(646.510009765625f, matrix.f);
+
+  {
+    // Render `obj` again. Note that the FPDFPageObj_SetMatrix() call has an
+    // effect.
+    ScopedFPDFBitmap bitmap(
+        FPDFImageObj_GetRenderedBitmap(document(), page, obj));
+    EXPECT_EQ(FPDFBitmap_BGRA, FPDFBitmap_GetFormat(bitmap.get()));
+    const char* checksum = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+        return "1003585870ad0fe37baf1c5bb3f5fd76";
+      return "0824c16dcf2dfcef44b45d88db1fddce";
+    }();
+    CompareBitmap(bitmap.get(), 120, 43, checksum);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapHandlesSMask) {
+  ASSERT_TRUE(OpenDocument("matte.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  constexpr int kExpectedObjects = 4;
+  ASSERT_EQ(kExpectedObjects, FPDFPage_CountObjects(page));
+
+  const char* smask_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "0653a18f3bf9b4d8413a2aa10bc11c38";
+    }
+    return "5a3ae4a660ce919e29c42ec2258142f1";
+  }();
+  const char* no_smask_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "0da49e63e7d6337aca78b19938e3bf65";
+    }
+    return "67504e83f5d78214ea00efc19082c5c1";
+  }();
+
+  for (int i = 0; i < kExpectedObjects; ++i) {
+    FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i);
+    ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+    ScopedFPDFBitmap bitmap(
+        FPDFImageObj_GetRenderedBitmap(document(), page, obj));
+    ASSERT_TRUE(bitmap);
+    EXPECT_EQ(FPDFBitmap_BGRA, FPDFBitmap_GetFormat(bitmap.get()));
+    if (i == 0)
+      CompareBitmap(bitmap.get(), 40, 60, smask_checksum);
+    else
+      CompareBitmap(bitmap.get(), 40, 60, no_smask_checksum);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapBadParams) {
+  ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 33);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+
+  // Test various null parameters.
+  EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(nullptr, nullptr, nullptr));
+  EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(document(), nullptr, nullptr));
+  EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(nullptr, page, nullptr));
+  EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(nullptr, nullptr, obj));
+  EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(document(), page, nullptr));
+  EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(nullptr, page, obj));
+
+  // Test mismatch between document and page parameters.
+  ScopedFPDFDocument new_document(FPDF_CreateNewDocument());
+  EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(new_document.get(), page, obj));
+
+  UnloadPage(page);
+}
+
 TEST_F(FPDFEditEmbedderTest, GetImageData) {
-  EXPECT_TRUE(OpenDocument("embedded_images.pdf"));
+  ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   ASSERT_EQ(39, FPDFPage_CountObjects(page));
@@ -3274,18 +4258,16 @@
 
   // Check that the raw image data has the correct length and hash value.
   unsigned long len = FPDFImageObj_GetImageDataRaw(obj, nullptr, 0);
-  std::vector<char> buf(len);
+  std::vector<uint8_t> buf(len);
   EXPECT_EQ(4091u, FPDFImageObj_GetImageDataRaw(obj, buf.data(), len));
-  EXPECT_EQ("f73802327d2e88e890f653961bcda81a",
-            GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
+  EXPECT_EQ("f73802327d2e88e890f653961bcda81a", GenerateMD5Base16(buf));
 
   // Check that the decoded image data has the correct length and hash value.
   len = FPDFImageObj_GetImageDataDecoded(obj, nullptr, 0);
   buf.clear();
   buf.resize(len);
   EXPECT_EQ(28776u, FPDFImageObj_GetImageDataDecoded(obj, buf.data(), len));
-  EXPECT_EQ("cb3637934bb3b95a6e4ae1ea9eb9e56e",
-            GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
+  EXPECT_EQ(kEmbeddedImage33Checksum, GenerateMD5Base16(buf));
 
   // Retrieve an image object with DCTDecode-encoded data stream.
   obj = FPDFPage_GetObject(page, 37);
@@ -3296,8 +4278,7 @@
   buf.clear();
   buf.resize(len);
   EXPECT_EQ(4370u, FPDFImageObj_GetImageDataRaw(obj, buf.data(), len));
-  EXPECT_EQ("6aae1f3710335023a9e12191be66b64b",
-            GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
+  EXPECT_EQ("6aae1f3710335023a9e12191be66b64b", GenerateMD5Base16(buf));
 
   // Check that the decoded image data has the correct length and hash value,
   // which should be the same as those of the raw data, since this image is
@@ -3306,8 +4287,7 @@
   buf.clear();
   buf.resize(len);
   EXPECT_EQ(4370u, FPDFImageObj_GetImageDataDecoded(obj, buf.data(), len));
-  EXPECT_EQ("6aae1f3710335023a9e12191be66b64b",
-            GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
+  EXPECT_EQ("6aae1f3710335023a9e12191be66b64b", GenerateMD5Base16(buf));
 
   UnloadPage(page);
 }
@@ -3319,72 +4299,67 @@
   ASSERT_EQ(39, FPDFPage_CountObjects(page));
 
   FPDF_PAGEOBJECT obj;
-  double a;
-  double b;
-  double c;
-  double d;
-  double e;
-  double f;
+  FS_MATRIX matrix;
 
   obj = FPDFPage_GetObject(page, 33);
   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
-  EXPECT_DOUBLE_EQ(53.0, a);
-  EXPECT_DOUBLE_EQ(0.0, b);
-  EXPECT_DOUBLE_EQ(0.0, c);
-  EXPECT_DOUBLE_EQ(43.0, d);
-  EXPECT_DOUBLE_EQ(72.0, e);
-  EXPECT_DOUBLE_EQ(646.510009765625, f);
+  EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
+  EXPECT_FLOAT_EQ(53.0f, matrix.a);
+  EXPECT_FLOAT_EQ(0.0f, matrix.b);
+  EXPECT_FLOAT_EQ(0.0f, matrix.c);
+  EXPECT_FLOAT_EQ(43.0f, matrix.d);
+  EXPECT_FLOAT_EQ(72.0f, matrix.e);
+  EXPECT_FLOAT_EQ(646.510009765625f, matrix.f);
 
   obj = FPDFPage_GetObject(page, 34);
   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
-  EXPECT_DOUBLE_EQ(70.0, a);
-  EXPECT_DOUBLE_EQ(0.0, b);
-  EXPECT_DOUBLE_EQ(0.0, c);
-  EXPECT_DOUBLE_EQ(51.0, d);
-  EXPECT_DOUBLE_EQ(216.0, e);
-  EXPECT_DOUBLE_EQ(646.510009765625, f);
+  EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
+  EXPECT_FLOAT_EQ(70.0f, matrix.a);
+  EXPECT_FLOAT_EQ(0.0f, matrix.b);
+  EXPECT_FLOAT_EQ(0.0f, matrix.c);
+  EXPECT_FLOAT_EQ(51.0f, matrix.d);
+  EXPECT_FLOAT_EQ(216.0f, matrix.e);
+  EXPECT_FLOAT_EQ(646.510009765625f, matrix.f);
 
   obj = FPDFPage_GetObject(page, 35);
   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
-  EXPECT_DOUBLE_EQ(69.0, a);
-  EXPECT_DOUBLE_EQ(0.0, b);
-  EXPECT_DOUBLE_EQ(0.0, c);
-  EXPECT_DOUBLE_EQ(51.0, d);
-  EXPECT_DOUBLE_EQ(360.0, e);
-  EXPECT_DOUBLE_EQ(646.510009765625, f);
+  EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
+  EXPECT_FLOAT_EQ(69.0f, matrix.a);
+  EXPECT_FLOAT_EQ(0.0f, matrix.b);
+  EXPECT_FLOAT_EQ(0.0f, matrix.c);
+  EXPECT_FLOAT_EQ(51.0f, matrix.d);
+  EXPECT_FLOAT_EQ(360.0f, matrix.e);
+  EXPECT_FLOAT_EQ(646.510009765625f, matrix.f);
 
   obj = FPDFPage_GetObject(page, 36);
   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
-  EXPECT_DOUBLE_EQ(59.0, a);
-  EXPECT_DOUBLE_EQ(0.0, b);
-  EXPECT_DOUBLE_EQ(0.0, c);
-  EXPECT_DOUBLE_EQ(45.0, d);
-  EXPECT_DOUBLE_EQ(72.0, e);
-  EXPECT_DOUBLE_EQ(553.510009765625, f);
+  EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
+  EXPECT_FLOAT_EQ(59.0f, matrix.a);
+  EXPECT_FLOAT_EQ(0.0f, matrix.b);
+  EXPECT_FLOAT_EQ(0.0f, matrix.c);
+  EXPECT_FLOAT_EQ(45.0f, matrix.d);
+  EXPECT_FLOAT_EQ(72.0f, matrix.e);
+  EXPECT_FLOAT_EQ(553.510009765625f, matrix.f);
 
   obj = FPDFPage_GetObject(page, 37);
   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
-  EXPECT_DOUBLE_EQ(55.94000244140625, a);
-  EXPECT_DOUBLE_EQ(0.0, b);
-  EXPECT_DOUBLE_EQ(0.0, c);
-  EXPECT_DOUBLE_EQ(46.950000762939453, d);
-  EXPECT_DOUBLE_EQ(216.0, e);
-  EXPECT_DOUBLE_EQ(552.510009765625, f);
+  EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
+  EXPECT_FLOAT_EQ(55.94000244140625f, matrix.a);
+  EXPECT_FLOAT_EQ(0.0f, matrix.b);
+  EXPECT_FLOAT_EQ(0.0f, matrix.c);
+  EXPECT_FLOAT_EQ(46.950000762939453f, matrix.d);
+  EXPECT_FLOAT_EQ(216.0f, matrix.e);
+  EXPECT_FLOAT_EQ(552.510009765625f, matrix.f);
 
   obj = FPDFPage_GetObject(page, 38);
   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
-  EXPECT_DOUBLE_EQ(70.528999328613281, a);
-  EXPECT_DOUBLE_EQ(0.0, b);
-  EXPECT_DOUBLE_EQ(0.0, c);
-  EXPECT_DOUBLE_EQ(43.149997711181641, d);
-  EXPECT_DOUBLE_EQ(360.0, e);
-  EXPECT_DOUBLE_EQ(553.3599853515625, f);
+  EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
+  EXPECT_FLOAT_EQ(70.528999328613281f, matrix.a);
+  EXPECT_FLOAT_EQ(0.0f, matrix.b);
+  EXPECT_FLOAT_EQ(0.0f, matrix.c);
+  EXPECT_FLOAT_EQ(43.149997711181641f, matrix.d);
+  EXPECT_FLOAT_EQ(360.0f, matrix.e);
+  EXPECT_FLOAT_EQ(553.3599853515625f, matrix.f);
 
   UnloadPage(page);
 }
@@ -3398,7 +4373,7 @@
 }
 
 TEST_F(FPDFEditEmbedderTest, GetImageFilters) {
-  EXPECT_TRUE(OpenDocument("embedded_images.pdf"));
+  ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -3492,3 +4467,304 @@
 
   UnloadPage(page);
 }
+
+TEST_F(FPDFEditEmbedderTest, GetImageMetadataJpxLzw) {
+  ASSERT_TRUE(OpenDocument("jpx_lzw.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 0);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+
+  FPDF_IMAGEOBJ_METADATA metadata;
+  ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, page, &metadata));
+  EXPECT_EQ(-1, metadata.marked_content_id);
+  EXPECT_EQ(612u, metadata.width);
+  EXPECT_EQ(792u, metadata.height);
+  EXPECT_FLOAT_EQ(72.0f, metadata.horizontal_dpi);
+  EXPECT_FLOAT_EQ(72.0f, metadata.vertical_dpi);
+  EXPECT_EQ(24u, metadata.bits_per_pixel);
+  EXPECT_EQ(FPDF_COLORSPACE_UNKNOWN, metadata.colorspace);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, GetImagePixelSize) {
+  ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Check that getting the size of a null object would fail.
+  unsigned int width = 0;
+  unsigned int height = 0;
+  EXPECT_FALSE(FPDFImageObj_GetImagePixelSize(nullptr, &width, &height));
+
+  // Check that receiving the size with a null width and height pointers would
+  // fail.
+  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 35);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  EXPECT_FALSE(FPDFImageObj_GetImagePixelSize(obj, nullptr, nullptr));
+  EXPECT_FALSE(FPDFImageObj_GetImagePixelSize(obj, nullptr, &height));
+  EXPECT_FALSE(FPDFImageObj_GetImagePixelSize(obj, &width, nullptr));
+
+  // Verify the pixel size of image.
+  ASSERT_TRUE(FPDFImageObj_GetImagePixelSize(obj, &width, &height));
+  EXPECT_EQ(92u, width);
+  EXPECT_EQ(68u, height);
+
+  obj = FPDFPage_GetObject(page, 37);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  ASSERT_TRUE(FPDFImageObj_GetImagePixelSize(obj, &width, &height));
+  EXPECT_EQ(126u, width);
+  EXPECT_EQ(106u, height);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForHelloWorldText) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    FPDF_PAGEOBJECT text_object = FPDFPage_GetObject(page, 0);
+    ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object));
+
+    ScopedFPDFBitmap bitmap(
+        FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 1));
+    ASSERT_TRUE(bitmap);
+    const char* 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* 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 "93dd7ad07bdaaba9ecd268350cb91596";
+      return "149f63de758ab01d3b75605cdfd4c176";
+    }();
+    CompareBitmap(x10_bitmap.get(), 631, 103, x10_checksum);
+  }
+
+  {
+    FPDF_PAGEOBJECT text_object = FPDFPage_GetObject(page, 1);
+    ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object));
+
+    ScopedFPDFBitmap bitmap(
+        FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 1));
+    ASSERT_TRUE(bitmap);
+    const char* 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* 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 "61476636eaa0da0b93d8b1937cf22b75";
+      return "f5f93bf64de579b59e775d7076ca0a5a";
+    }();
+    CompareBitmap(x10_bitmap.get(), 1143, 150, x10_checksum);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForRotatedText) {
+  ASSERT_TRUE(OpenDocument("rotated_text.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_PAGEOBJECT text_object = FPDFPage_GetObject(page, 0);
+  ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object));
+
+  ScopedFPDFBitmap bitmap(
+      FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 1));
+  ASSERT_TRUE(bitmap);
+  const char* 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* 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 "bb7c2ec575f27cf882dcd38f2563c00f";
+    return "bbd3842a4b50dbfcbce4eee2b067a297";
+  }();
+  CompareBitmap(x10_bitmap.get(), 275, 275, x10_checksum);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForColorText) {
+  ASSERT_TRUE(OpenDocument("text_color.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_PAGEOBJECT text_object = FPDFPage_GetObject(page, 0);
+  ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object));
+
+  ScopedFPDFBitmap bitmap(
+      FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 7.3f));
+  ASSERT_TRUE(bitmap);
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "1d74731d23a056c0e3fb88f2f85b2581";
+    return "e8154fa8ededf4d9b8b35b5260897b6c";
+  }();
+  CompareBitmap(bitmap.get(), 120, 186, checksum);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForNewlyCreatedText) {
+  // Start with a blank document.
+  ASSERT_TRUE(CreateNewDocument());
+
+  // Create a new text object.
+  ScopedFPDFPageObject text_object(
+      FPDFPageObj_NewTextObj(document(), "Arial", 12.0f));
+  ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object.get()));
+  ScopedFPDFWideString text = GetFPDFWideString(kBottomText);
+  EXPECT_TRUE(FPDFText_SetText(text_object.get(), text.get()));
+
+  ScopedFPDFBitmap bitmap(
+      FPDFTextObj_GetRenderedBitmap(document(), nullptr, text_object.get(), 1));
+  ASSERT_TRUE(bitmap);
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "574ae982d02e653ab6a8f23a6cdf4085";
+    }
+    return "fa947759dab76d68a07ccf6f97b2d9c2";
+  }();
+  CompareBitmap(bitmap.get(), 151, 12, checksum);
+}
+
+TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForTextWithBadParameters) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_PAGEOBJECT text_object = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(text_object);
+
+  // Simple bad parameters testing.
+  EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, nullptr, nullptr, 0));
+  EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(document(), nullptr, nullptr, 0));
+  EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, page, nullptr, 0));
+  EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, nullptr, text_object, 0));
+  EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, nullptr, nullptr, 1));
+  EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(document(), page, nullptr, 0));
+  EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(document(), nullptr, nullptr, 1));
+  EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, page, text_object, 0));
+  EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, page, nullptr, 1));
+  EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, nullptr, text_object, 1));
+  EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(document(), page, nullptr, 1));
+  EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, page, text_object, 1));
+
+  // Test bad scale values.
+  EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 0));
+  EXPECT_FALSE(
+      FPDFTextObj_GetRenderedBitmap(document(), page, text_object, -1));
+  EXPECT_FALSE(
+      FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 10000));
+  EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(
+      document(), page, text_object, std::numeric_limits<float>::max()));
+  EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(
+      document(), page, text_object, std::numeric_limits<float>::infinity()));
+
+  {
+    // `text_object` will render without `page`, but may not render correctly
+    // without the resources from `page`. Although it does in this simple case.
+    ScopedFPDFBitmap bitmap(
+        FPDFTextObj_GetRenderedBitmap(document(), nullptr, text_object, 1));
+    EXPECT_TRUE(bitmap);
+  }
+
+  // Mismatch between the document and the page fails too.
+  ScopedFPDFDocument empty_document(FPDF_CreateNewDocument());
+  EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(empty_document.get(), page,
+                                             text_object, 1));
+
+  UnloadPage(page);
+}
+
+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_edit_unittest.cpp b/fpdfsdk/fpdf_edit_unittest.cpp
index 6aa24f4..5f6fb0f 100644
--- a/fpdfsdk/fpdf_edit_unittest.cpp
+++ b/fpdfsdk/fpdf_edit_unittest.cpp
@@ -1,16 +1,13 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "public/fpdf_edit.h"
 
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fpdfapi/page/test_with_page_module.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-class PDFEditTest : public testing::Test {
-  void SetUp() override { CPDF_PageModule::Create(); }
-  void TearDown() override { CPDF_PageModule::Destroy(); }
-};
+using PDFEditTest = TestWithPageModule;
 
 TEST_F(PDFEditTest, LineJoin) {
   EXPECT_FALSE(FPDFPageObj_SetLineJoin(nullptr, -1));
diff --git a/fpdfsdk/fpdf_editimg.cpp b/fpdfsdk/fpdf_editimg.cpp
index ecd11d1..8e3402f 100644
--- a/fpdfsdk/fpdf_editimg.cpp
+++ b/fpdfsdk/fpdf_editimg.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,7 @@
 
 #include "public/fpdf_edit.h"
 
+#include <memory>
 #include <utility>
 
 #include "core/fpdfapi/page/cpdf_dib.h"
@@ -16,36 +17,54 @@
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "core/fpdfapi/render/cpdf_imagerenderer.h"
+#include "core/fpdfapi/render/cpdf_rendercontext.h"
+#include "core/fpdfapi/render/cpdf_renderstatus.h"
+#include "core/fxcrt/stl_util.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "fpdfsdk/cpdfsdk_customaccess.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
 // These checks ensure the consistency of colorspace values across core/ and
 // public/.
-static_assert(PDFCS_DEVICEGRAY == FPDF_COLORSPACE_DEVICEGRAY,
-              "PDFCS_DEVICEGRAY value mismatch");
-static_assert(PDFCS_DEVICERGB == FPDF_COLORSPACE_DEVICERGB,
-              "PDFCS_DEVICERGB value mismatch");
-static_assert(PDFCS_DEVICECMYK == FPDF_COLORSPACE_DEVICECMYK,
-              "PDFCS_DEVICECMYK value mismatch");
-static_assert(PDFCS_CALGRAY == FPDF_COLORSPACE_CALGRAY,
-              "PDFCS_CALGRAY value mismatch");
-static_assert(PDFCS_CALRGB == FPDF_COLORSPACE_CALRGB,
-              "PDFCS_CALRGB value mismatch");
-static_assert(PDFCS_LAB == FPDF_COLORSPACE_LAB, "PDFCS_LAB value mismatch");
-static_assert(PDFCS_ICCBASED == FPDF_COLORSPACE_ICCBASED,
-              "PDFCS_ICCBASED value mismatch");
-static_assert(PDFCS_SEPARATION == FPDF_COLORSPACE_SEPARATION,
-              "PDFCS_SEPARATION value mismatch");
-static_assert(PDFCS_DEVICEN == FPDF_COLORSPACE_DEVICEN,
-              "PDFCS_DEVICEN value mismatch");
-static_assert(PDFCS_INDEXED == FPDF_COLORSPACE_INDEXED,
-              "PDFCS_INDEXED value mismatch");
-static_assert(PDFCS_PATTERN == FPDF_COLORSPACE_PATTERN,
-              "PDFCS_PATTERN value mismatch");
+static_assert(static_cast<int>(CPDF_ColorSpace::Family::kDeviceGray) ==
+                  FPDF_COLORSPACE_DEVICEGRAY,
+              "kDeviceGray value mismatch");
+static_assert(static_cast<int>(CPDF_ColorSpace::Family::kDeviceRGB) ==
+                  FPDF_COLORSPACE_DEVICERGB,
+              "kDeviceRGB value mismatch");
+static_assert(static_cast<int>(CPDF_ColorSpace::Family::kDeviceCMYK) ==
+                  FPDF_COLORSPACE_DEVICECMYK,
+              "kDeviceCMYK value mismatch");
+static_assert(static_cast<int>(CPDF_ColorSpace::Family::kCalGray) ==
+                  FPDF_COLORSPACE_CALGRAY,
+              "kCalGray value mismatch");
+static_assert(static_cast<int>(CPDF_ColorSpace::Family::kCalRGB) ==
+                  FPDF_COLORSPACE_CALRGB,
+              "kCalRGB value mismatch");
+static_assert(static_cast<int>(CPDF_ColorSpace::Family::kLab) ==
+                  FPDF_COLORSPACE_LAB,
+              "kLab value mismatch");
+static_assert(static_cast<int>(CPDF_ColorSpace::Family::kICCBased) ==
+                  FPDF_COLORSPACE_ICCBASED,
+              "kICCBased value mismatch");
+static_assert(static_cast<int>(CPDF_ColorSpace::Family::kSeparation) ==
+                  FPDF_COLORSPACE_SEPARATION,
+              "kSeparation value mismatch");
+static_assert(static_cast<int>(CPDF_ColorSpace::Family::kDeviceN) ==
+                  FPDF_COLORSPACE_DEVICEN,
+              "kDeviceN value mismatch");
+static_assert(static_cast<int>(CPDF_ColorSpace::Family::kIndexed) ==
+                  FPDF_COLORSPACE_INDEXED,
+              "kIndexed value mismatch");
+static_assert(static_cast<int>(CPDF_ColorSpace::Family::kPattern) ==
+                  FPDF_COLORSPACE_PATTERN,
+              "kPattern value mismatch");
 
 RetainPtr<IFX_SeekableReadStream> MakeSeekableReadStream(
     FPDF_FILEACCESS* pFileAccess) {
@@ -80,9 +99,10 @@
 
   RetainPtr<IFX_SeekableReadStream> pFile = MakeSeekableReadStream(file_access);
   if (inline_jpeg)
-    pImgObj->GetImage()->SetJpegImageInline(pFile);
+    pImgObj->GetImage()->SetJpegImageInline(std::move(pFile));
   else
-    pImgObj->GetImage()->SetJpegImage(pFile);
+    pImgObj->GetImage()->SetJpegImage(std::move(pFile));
+
   pImgObj->SetDirty(true);
   return true;
 }
@@ -95,7 +115,7 @@
   if (!pDoc)
     return nullptr;
 
-  auto pImageObj = pdfium::MakeUnique<CPDF_ImageObject>();
+  auto pImageObj = std::make_unique<CPDF_ImageObject>();
   pImageObj->SetImage(pdfium::MakeRetain<CPDF_Image>(pDoc));
 
   // Caller takes ownership.
@@ -119,28 +139,6 @@
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFImageObj_GetMatrix(FPDF_PAGEOBJECT image_object,
-                       double* a,
-                       double* b,
-                       double* c,
-                       double* d,
-                       double* e,
-                       double* f) {
-  CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
-  if (!pImgObj || !a || !b || !c || !d || !e || !f)
-    return false;
-
-  const CFX_Matrix& matrix = pImgObj->matrix();
-  *a = matrix.a;
-  *b = matrix.b;
-  *c = matrix.c;
-  *d = matrix.d;
-  *e = matrix.e;
-  *f = matrix.f;
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFImageObj_SetMatrix(FPDF_PAGEOBJECT image_object,
                        double a,
                        double b,
@@ -152,10 +150,9 @@
   if (!pImgObj)
     return false;
 
-  pImgObj->set_matrix(CFX_Matrix(static_cast<float>(a), static_cast<float>(b),
-                                 static_cast<float>(c), static_cast<float>(d),
-                                 static_cast<float>(e), static_cast<float>(f)));
-  pImgObj->CalcBoundingBox();
+  pImgObj->SetImageMatrix(CFX_Matrix(
+      static_cast<float>(a), static_cast<float>(b), static_cast<float>(c),
+      static_cast<float>(d), static_cast<float>(e), static_cast<float>(f)));
   pImgObj->SetDirty(true);
   return true;
 }
@@ -201,19 +198,75 @@
   if (!pSource)
     return nullptr;
 
-  RetainPtr<CFX_DIBitmap> pBitmap;
   // If the source image has a representation of 1 bit per pixel, then convert
   // it to a grayscale bitmap having 1 byte per pixel, since bitmaps have no
   // concept of bits. Otherwise, convert the source image to a bitmap directly,
   // retaining its color representation.
-  if (pSource->GetBPP() == 1)
-    pBitmap = pSource->CloneConvert(FXDIB_8bppRgb);
-  else
-    pBitmap = pSource->Clone(nullptr);
+  RetainPtr<CFX_DIBitmap> pBitmap =
+      pSource->GetBPP() == 1 ? pSource->ConvertTo(FXDIB_Format::k8bppRgb)
+                             : pSource->Realize();
 
   return FPDFBitmapFromCFXDIBitmap(pBitmap.Leak());
 }
 
+FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
+FPDFImageObj_GetRenderedBitmap(FPDF_DOCUMENT document,
+                               FPDF_PAGE page,
+                               FPDF_PAGEOBJECT image_object) {
+  CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document);
+  if (!doc)
+    return nullptr;
+
+  CPDF_Page* optional_page = CPDFPageFromFPDFPage(page);
+  if (optional_page && optional_page->GetDocument() != doc)
+    return nullptr;
+
+  CPDF_ImageObject* image = CPDFImageObjectFromFPDFPageObject(image_object);
+  if (!image)
+    return nullptr;
+
+  // Create |result_bitmap|.
+  const CFX_Matrix& image_matrix = image->matrix();
+  int output_width = image_matrix.a;
+  int output_height = image_matrix.d;
+  auto result_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!result_bitmap->Create(output_width, output_height, FXDIB_Format::kArgb))
+    return nullptr;
+
+  // Set up all the rendering code.
+  RetainPtr<CPDF_Dictionary> page_resources =
+      optional_page ? optional_page->GetMutablePageResources() : nullptr;
+  CPDF_RenderContext context(doc, std::move(page_resources),
+                             /*pPageCache=*/nullptr);
+  CFX_DefaultRenderDevice device;
+  device.Attach(result_bitmap);
+  CPDF_RenderStatus status(&context, &device);
+  CPDF_ImageRenderer renderer(&status);
+
+  // Need to first flip the image, as expected by |renderer|.
+  CFX_Matrix render_matrix(1, 0, 0, -1, 0, output_height);
+
+  // Then take |image_matrix|'s offset into account.
+  render_matrix.Translate(-image_matrix.e, image_matrix.f);
+
+  // Do the actual rendering.
+  bool should_continue = renderer.Start(image, render_matrix,
+                                        /*bStdCS=*/false, BlendMode::kNormal);
+  while (should_continue)
+    should_continue = renderer.Continue(/*pPause=*/nullptr);
+
+  if (!renderer.GetResult())
+    return nullptr;
+
+#if defined(_SKIA_SUPPORT_)
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    result_bitmap->UnPreMultiply();
+#endif
+
+  // Caller takes ownership.
+  return FPDFBitmapFromCFXDIBitmap(result_bitmap.Leak());
+}
+
 FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDFImageObj_GetImageDataDecoded(FPDF_PAGEOBJECT image_object,
                                  void* buffer,
@@ -226,11 +279,13 @@
   if (!pImg)
     return 0;
 
-  CPDF_Stream* pImgStream = pImg->GetStream();
+  RetainPtr<const CPDF_Stream> pImgStream = pImg->GetStream();
   if (!pImgStream)
     return 0;
 
-  return DecodeStreamMaybeCopyAndReturnLength(pImgStream, buffer, buflen);
+  return DecodeStreamMaybeCopyAndReturnLength(
+      std::move(pImgStream),
+      {static_cast<uint8_t*>(buffer), static_cast<size_t>(buflen)});
 }
 
 FPDF_EXPORT unsigned long FPDF_CALLCONV
@@ -245,11 +300,13 @@
   if (!pImg)
     return 0;
 
-  CPDF_Stream* pImgStream = pImg->GetStream();
+  RetainPtr<const CPDF_Stream> pImgStream = pImg->GetStream();
   if (!pImgStream)
     return 0;
 
-  return GetRawStreamMaybeCopyAndReturnLength(pImgStream, buffer, buflen);
+  return GetRawStreamMaybeCopyAndReturnLength(
+      std::move(pImgStream),
+      {static_cast<uint8_t*>(buffer), static_cast<size_t>(buflen)});
 }
 
 FPDF_EXPORT int FPDF_CALLCONV
@@ -262,13 +319,17 @@
   if (!pImg)
     return 0;
 
-  CPDF_Dictionary* pDict = pImg->GetDict();
-  CPDF_Object* pFilter = pDict ? pDict->GetDirectObjectFor("Filter") : nullptr;
+  RetainPtr<const CPDF_Dictionary> pDict = pImg->GetDict();
+  if (!pDict)
+    return 0;
+
+  RetainPtr<const CPDF_Object> pFilter = pDict->GetDirectObjectFor("Filter");
   if (!pFilter)
     return 0;
 
   if (pFilter->IsArray())
-    return pFilter->AsArray()->size();
+    return fxcrt::CollectionSize<int>(*pFilter->AsArray());
+
   if (pFilter->IsName())
     return 1;
 
@@ -284,18 +345,14 @@
     return 0;
 
   CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(image_object);
-  CPDF_Object* pFilter =
-      pObj->AsImage()->GetImage()->GetDict()->GetDirectObjectFor("Filter");
-  ByteString bsFilter;
-  if (pFilter->IsName())
-    bsFilter = pFilter->AsName()->GetString();
-  else
-    bsFilter = pFilter->AsArray()->GetStringAt(index);
+  RetainPtr<const CPDF_Dictionary> pDict =
+      pObj->AsImage()->GetImage()->GetDict();
+  RetainPtr<const CPDF_Object> pFilter = pDict->GetDirectObjectFor("Filter");
+  ByteString bsFilter = pFilter->IsName()
+                            ? pFilter->AsName()->GetString()
+                            : pFilter->AsArray()->GetByteStringAt(index);
 
-  unsigned long len = bsFilter.GetLength() + 1;
-  if (buffer && len <= buflen)
-    memcpy(buffer, bsFilter.c_str(), len);
-  return len;
+  return NulTerminateMaybeCopyAndReturnLength(bsFilter, buffer, buflen);
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -310,7 +367,8 @@
   if (!pImg)
     return false;
 
-  metadata->marked_content_id = pImgObj->m_ContentMarks.GetMarkedContentID();
+  metadata->marked_content_id =
+      pImgObj->GetContentMarks()->GetMarkedContentID();
 
   const int nPixelWidth = pImg->GetPixelWidth();
   const int nPixelHeight = pImg->GetPixelHeight();
@@ -332,16 +390,40 @@
   if (!pPage || !pPage->GetDocument() || !pImg->GetStream())
     return true;
 
-  auto pSource = pdfium::MakeRetain<CPDF_DIB>();
+  // A cross-document image may have come from the embedder.
+  if (pPage->GetDocument() != pImg->GetDocument())
+    return false;
+
+  RetainPtr<CPDF_DIB> pSource = pImg->CreateNewDIB();
   CPDF_DIB::LoadState ret = pSource->StartLoadDIBBase(
-      pPage->GetDocument(), pImg->GetStream(), false, nullptr,
-      pPage->m_pPageResources.Get(), false, 0, false);
+      false, nullptr, pPage->GetPageResources().Get(), false,
+      CPDF_ColorSpace::Family::kUnknown, false, {0, 0});
   if (ret == CPDF_DIB::LoadState::kFail)
     return true;
 
   metadata->bits_per_pixel = pSource->GetBPP();
-  if (pSource->GetColorSpace())
-    metadata->colorspace = pSource->GetColorSpace()->GetFamily();
+  if (pSource->GetColorSpace()) {
+    metadata->colorspace =
+        static_cast<int>(pSource->GetColorSpace()->GetFamily());
+  }
+  return true;
+}
 
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFImageObj_GetImagePixelSize(FPDF_PAGEOBJECT image_object,
+                               unsigned int* width,
+                               unsigned int* height) {
+  CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
+  if (!pImgObj || !width || !height) {
+    return false;
+  }
+
+  RetainPtr<CPDF_Image> pImg = pImgObj->GetImage();
+  if (!pImg) {
+    return false;
+  }
+
+  *width = pImg->GetPixelWidth();
+  *height = pImg->GetPixelHeight();
   return true;
 }
diff --git a/fpdfsdk/fpdf_editimg_embeddertest.cpp b/fpdfsdk/fpdf_editimg_embeddertest.cpp
new file mode 100644
index 0000000..5038def
--- /dev/null
+++ b/fpdfsdk/fpdf_editimg_embeddertest.cpp
@@ -0,0 +1,152 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_edit.h"
+
+#include "public/cpp/fpdf_scopers.h"
+#include "testing/embedder_test.h"
+#include "testing/utils/file_util.h"
+
+class PDFEditImgTest : public EmbedderTest {};
+
+TEST_F(PDFEditImgTest, InsertObjectWithInvalidPage) {
+  FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
+  FPDF_PAGE page = FPDFPage_New(doc, 0, 100, 100);
+  EXPECT_EQ(0, FPDFPage_CountObjects(page));
+
+  FPDFPage_InsertObject(nullptr, nullptr);
+  EXPECT_EQ(0, FPDFPage_CountObjects(page));
+
+  FPDFPage_InsertObject(page, nullptr);
+  EXPECT_EQ(0, FPDFPage_CountObjects(page));
+
+  FPDF_PAGEOBJECT page_image = FPDFPageObj_NewImageObj(doc);
+  FPDFPage_InsertObject(nullptr, page_image);
+  EXPECT_EQ(0, FPDFPage_CountObjects(page));
+
+  FPDF_ClosePage(page);
+  FPDF_CloseDocument(doc);
+}
+
+TEST_F(PDFEditImgTest, NewImageObj) {
+  FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
+  FPDF_PAGE page = FPDFPage_New(doc, 0, 100, 100);
+  EXPECT_EQ(0, FPDFPage_CountObjects(page));
+
+  FPDF_PAGEOBJECT page_image = FPDFPageObj_NewImageObj(doc);
+  FPDFPage_InsertObject(page, page_image);
+  EXPECT_EQ(1, FPDFPage_CountObjects(page));
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+
+  FPDF_ClosePage(page);
+  FPDF_CloseDocument(doc);
+}
+
+TEST_F(PDFEditImgTest, NewImageObjGenerateContent) {
+  FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
+  FPDF_PAGE page = FPDFPage_New(doc, 0, 100, 100);
+  EXPECT_EQ(0, FPDFPage_CountObjects(page));
+
+  constexpr int kBitmapSize = 50;
+  FPDF_BITMAP bitmap = FPDFBitmap_Create(kBitmapSize, kBitmapSize, 0);
+  FPDFBitmap_FillRect(bitmap, 0, 0, kBitmapSize, kBitmapSize, 0x00000000);
+  EXPECT_EQ(kBitmapSize, FPDFBitmap_GetWidth(bitmap));
+  EXPECT_EQ(kBitmapSize, FPDFBitmap_GetHeight(bitmap));
+
+  FPDF_PAGEOBJECT page_image = FPDFPageObj_NewImageObj(doc);
+  ASSERT_TRUE(FPDFImageObj_SetBitmap(&page, 0, page_image, bitmap));
+  static constexpr FS_MATRIX kScaleBitmapMatrix{kBitmapSize, 0, 0,
+                                                kBitmapSize, 0, 0};
+  ASSERT_TRUE(FPDFPageObj_SetMatrix(page_image, &kScaleBitmapMatrix));
+  FPDFPage_InsertObject(page, page_image);
+  EXPECT_EQ(1, FPDFPage_CountObjects(page));
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+
+  FPDFBitmap_Destroy(bitmap);
+  FPDF_ClosePage(page);
+  FPDF_CloseDocument(doc);
+}
+
+TEST_F(PDFEditImgTest, NewImageObjLoadJpeg) {
+  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
+  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 200, 200));
+  ASSERT_TRUE(page);
+
+  ScopedFPDFPageObject image(FPDFPageObj_NewImageObj(doc.get()));
+  ASSERT_TRUE(image);
+
+  FileAccessForTesting file_access("mona_lisa.jpg");
+  FPDF_PAGE temp_page = page.get();
+  EXPECT_TRUE(
+      FPDFImageObj_LoadJpegFile(&temp_page, 1, image.get(), &file_access));
+
+  ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(image.get()));
+  EXPECT_TRUE(bitmap);
+  EXPECT_EQ(120, FPDFBitmap_GetWidth(bitmap.get()));
+  EXPECT_EQ(120, FPDFBitmap_GetHeight(bitmap.get()));
+}
+
+TEST_F(PDFEditImgTest, NewImageObjLoadJpegInline) {
+  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
+  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 200, 200));
+  ASSERT_TRUE(page);
+
+  ScopedFPDFPageObject image(FPDFPageObj_NewImageObj(doc.get()));
+  ASSERT_TRUE(image);
+
+  FileAccessForTesting file_access("mona_lisa.jpg");
+  FPDF_PAGE temp_page = page.get();
+  EXPECT_TRUE(FPDFImageObj_LoadJpegFileInline(&temp_page, 1, image.get(),
+                                              &file_access));
+
+  ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(image.get()));
+  EXPECT_TRUE(bitmap);
+  EXPECT_EQ(120, FPDFBitmap_GetWidth(bitmap.get()));
+  EXPECT_EQ(120, FPDFBitmap_GetHeight(bitmap.get()));
+}
+
+TEST_F(PDFEditImgTest, SetBitmap) {
+  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
+  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100));
+  ScopedFPDFPageObject image(FPDFPageObj_NewImageObj(doc.get()));
+  ScopedFPDFBitmap bitmap(FPDFBitmap_Create(100, 100, 0));
+
+  FPDF_PAGE page_ptr = page.get();
+  FPDF_PAGE* pages = &page_ptr;
+  EXPECT_TRUE(FPDFImageObj_SetBitmap(nullptr, 1, image.get(), bitmap.get()));
+  EXPECT_TRUE(FPDFImageObj_SetBitmap(pages, 0, image.get(), bitmap.get()));
+  EXPECT_FALSE(FPDFImageObj_SetBitmap(pages, 1, nullptr, bitmap.get()));
+  EXPECT_FALSE(FPDFImageObj_SetBitmap(pages, 1, image.get(), nullptr));
+}
+
+TEST_F(PDFEditImgTest, GetSetImageMatrix) {
+  FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
+  FPDF_PAGEOBJECT image = FPDFPageObj_NewImageObj(doc);
+
+  FS_MATRIX matrix;
+  EXPECT_FALSE(FPDFPageObj_GetMatrix(nullptr, nullptr));
+  EXPECT_FALSE(FPDFPageObj_GetMatrix(nullptr, &matrix));
+  EXPECT_FALSE(FPDFPageObj_GetMatrix(image, nullptr));
+
+  EXPECT_TRUE(FPDFPageObj_GetMatrix(image, &matrix));
+  EXPECT_FLOAT_EQ(1.0f, matrix.a);
+  EXPECT_FLOAT_EQ(0.0f, matrix.b);
+  EXPECT_FLOAT_EQ(0.0f, matrix.c);
+  EXPECT_FLOAT_EQ(1.0f, matrix.d);
+  EXPECT_FLOAT_EQ(0.0f, matrix.e);
+  EXPECT_FLOAT_EQ(0.0f, matrix.f);
+
+  static constexpr FS_MATRIX kMatrix{1, 2, 3, 4, 5, 6};
+  EXPECT_TRUE(FPDFPageObj_SetMatrix(image, &kMatrix));
+  EXPECT_TRUE(FPDFPageObj_GetMatrix(image, &matrix));
+  EXPECT_FLOAT_EQ(1.0f, matrix.a);
+  EXPECT_FLOAT_EQ(2.0f, matrix.b);
+  EXPECT_FLOAT_EQ(3.0f, matrix.c);
+  EXPECT_FLOAT_EQ(4.0f, matrix.d);
+  EXPECT_FLOAT_EQ(5.0f, matrix.e);
+  EXPECT_FLOAT_EQ(6.0f, matrix.f);
+
+  FPDFPageObj_Destroy(image);
+  FPDF_CloseDocument(doc);
+}
diff --git a/fpdfsdk/fpdf_editimg_unittest.cpp b/fpdfsdk/fpdf_editimg_unittest.cpp
deleted file mode 100644
index 09ec71e..0000000
--- a/fpdfsdk/fpdf_editimg_unittest.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "public/fpdf_edit.h"
-
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
-#include "public/cpp/fpdf_scopers.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/utils/file_util.h"
-
-class PDFEditImgTest : public testing::Test {
-  void SetUp() override { CPDF_PageModule::Create(); }
-  void TearDown() override { CPDF_PageModule::Destroy(); }
-};
-
-TEST_F(PDFEditImgTest, InsertObjectWithInvalidPage) {
-  FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
-  FPDF_PAGE page = FPDFPage_New(doc, 0, 100, 100);
-  EXPECT_EQ(0, FPDFPage_CountObjects(page));
-
-  FPDFPage_InsertObject(nullptr, nullptr);
-  EXPECT_EQ(0, FPDFPage_CountObjects(page));
-
-  FPDFPage_InsertObject(page, nullptr);
-  EXPECT_EQ(0, FPDFPage_CountObjects(page));
-
-  FPDF_PAGEOBJECT page_image = FPDFPageObj_NewImageObj(doc);
-  FPDFPage_InsertObject(nullptr, page_image);
-  EXPECT_EQ(0, FPDFPage_CountObjects(page));
-
-  FPDF_ClosePage(page);
-  FPDF_CloseDocument(doc);
-}
-
-TEST_F(PDFEditImgTest, NewImageObj) {
-  FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
-  FPDF_PAGE page = FPDFPage_New(doc, 0, 100, 100);
-  EXPECT_EQ(0, FPDFPage_CountObjects(page));
-
-  FPDF_PAGEOBJECT page_image = FPDFPageObj_NewImageObj(doc);
-  FPDFPage_InsertObject(page, page_image);
-  EXPECT_EQ(1, FPDFPage_CountObjects(page));
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
-
-  FPDF_ClosePage(page);
-  FPDF_CloseDocument(doc);
-}
-
-TEST_F(PDFEditImgTest, NewImageObjGenerateContent) {
-  FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
-  FPDF_PAGE page = FPDFPage_New(doc, 0, 100, 100);
-  EXPECT_EQ(0, FPDFPage_CountObjects(page));
-
-  constexpr int kBitmapSize = 50;
-  FPDF_BITMAP bitmap = FPDFBitmap_Create(kBitmapSize, kBitmapSize, 0);
-  FPDFBitmap_FillRect(bitmap, 0, 0, kBitmapSize, kBitmapSize, 0x00000000);
-  EXPECT_EQ(kBitmapSize, FPDFBitmap_GetWidth(bitmap));
-  EXPECT_EQ(kBitmapSize, FPDFBitmap_GetHeight(bitmap));
-
-  FPDF_PAGEOBJECT page_image = FPDFPageObj_NewImageObj(doc);
-  ASSERT_TRUE(FPDFImageObj_SetBitmap(&page, 0, page_image, bitmap));
-  ASSERT_TRUE(
-      FPDFImageObj_SetMatrix(page_image, kBitmapSize, 0, 0, kBitmapSize, 0, 0));
-  FPDFPage_InsertObject(page, page_image);
-  EXPECT_EQ(1, FPDFPage_CountObjects(page));
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
-
-  FPDFBitmap_Destroy(bitmap);
-  FPDF_ClosePage(page);
-  FPDF_CloseDocument(doc);
-}
-
-TEST_F(PDFEditImgTest, NewImageObjLoadJpeg) {
-  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
-  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 200, 200));
-  ASSERT_TRUE(page);
-
-  ScopedFPDFPageObject image(FPDFPageObj_NewImageObj(doc.get()));
-  ASSERT_TRUE(image);
-
-  FileAccessForTesting file_access("mona_lisa.jpg");
-  FPDF_PAGE temp_page = page.get();
-  EXPECT_TRUE(
-      FPDFImageObj_LoadJpegFile(&temp_page, 1, image.get(), &file_access));
-
-  ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(image.get()));
-  EXPECT_TRUE(bitmap);
-  EXPECT_EQ(120, FPDFBitmap_GetWidth(bitmap.get()));
-  EXPECT_EQ(120, FPDFBitmap_GetHeight(bitmap.get()));
-}
-
-TEST_F(PDFEditImgTest, NewImageObjLoadJpegInline) {
-  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
-  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 200, 200));
-  ASSERT_TRUE(page);
-
-  ScopedFPDFPageObject image(FPDFPageObj_NewImageObj(doc.get()));
-  ASSERT_TRUE(image);
-
-  FileAccessForTesting file_access("mona_lisa.jpg");
-  FPDF_PAGE temp_page = page.get();
-  EXPECT_TRUE(FPDFImageObj_LoadJpegFileInline(&temp_page, 1, image.get(),
-                                              &file_access));
-
-  ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(image.get()));
-  EXPECT_TRUE(bitmap);
-  EXPECT_EQ(120, FPDFBitmap_GetWidth(bitmap.get()));
-  EXPECT_EQ(120, FPDFBitmap_GetHeight(bitmap.get()));
-}
-
-TEST_F(PDFEditImgTest, SetBitmap) {
-  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
-  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100));
-  ScopedFPDFPageObject image(FPDFPageObj_NewImageObj(doc.get()));
-  ScopedFPDFBitmap bitmap(FPDFBitmap_Create(100, 100, 0));
-
-  FPDF_PAGE page_ptr = page.get();
-  FPDF_PAGE* pages = &page_ptr;
-  EXPECT_TRUE(FPDFImageObj_SetBitmap(nullptr, 1, image.get(), bitmap.get()));
-  EXPECT_TRUE(FPDFImageObj_SetBitmap(pages, 0, image.get(), bitmap.get()));
-  EXPECT_FALSE(FPDFImageObj_SetBitmap(pages, 1, nullptr, bitmap.get()));
-  EXPECT_FALSE(FPDFImageObj_SetBitmap(pages, 1, image.get(), nullptr));
-}
-
-TEST_F(PDFEditImgTest, GetSetImageMatrix) {
-  FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
-  FPDF_PAGEOBJECT image = FPDFPageObj_NewImageObj(doc);
-
-  double a;
-  double b;
-  double c;
-  double d;
-  double e;
-  double f;
-  EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, nullptr, nullptr, nullptr,
-                                      nullptr, nullptr, nullptr));
-  EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, &a, nullptr, nullptr, nullptr,
-                                      nullptr, nullptr));
-  EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, &a, &b, nullptr, nullptr,
-                                      nullptr, nullptr));
-  EXPECT_FALSE(
-      FPDFImageObj_GetMatrix(nullptr, &a, &b, &c, nullptr, nullptr, nullptr));
-  EXPECT_FALSE(
-      FPDFImageObj_GetMatrix(nullptr, &a, &b, &c, nullptr, nullptr, nullptr));
-  EXPECT_FALSE(
-      FPDFImageObj_GetMatrix(nullptr, &a, &b, &c, &d, nullptr, nullptr));
-  EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, &a, &b, &c, &d, &e, nullptr));
-  EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, &a, &b, &c, &d, &e, &f));
-  EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, &a, nullptr, &c, &d, &e, &f));
-
-  EXPECT_FALSE(FPDFImageObj_GetMatrix(image, nullptr, nullptr, nullptr, nullptr,
-                                      nullptr, nullptr));
-  EXPECT_FALSE(FPDFImageObj_GetMatrix(image, &a, nullptr, nullptr, nullptr,
-                                      nullptr, nullptr));
-  EXPECT_FALSE(FPDFImageObj_GetMatrix(image, &a, &b, nullptr, nullptr, nullptr,
-                                      nullptr));
-  EXPECT_FALSE(
-      FPDFImageObj_GetMatrix(image, &a, &b, &c, nullptr, nullptr, nullptr));
-  EXPECT_FALSE(
-      FPDFImageObj_GetMatrix(image, &a, &b, &c, nullptr, nullptr, nullptr));
-  EXPECT_FALSE(FPDFImageObj_GetMatrix(image, &a, &b, &c, &d, nullptr, nullptr));
-  EXPECT_FALSE(FPDFImageObj_GetMatrix(image, &a, &b, &c, &d, &e, nullptr));
-  EXPECT_FALSE(FPDFImageObj_GetMatrix(image, &a, nullptr, &c, &d, &e, &f));
-
-  EXPECT_TRUE(FPDFImageObj_GetMatrix(image, &a, &b, &c, &d, &e, &f));
-  EXPECT_DOUBLE_EQ(1.0, a);
-  EXPECT_DOUBLE_EQ(0.0, b);
-  EXPECT_DOUBLE_EQ(0.0, c);
-  EXPECT_DOUBLE_EQ(1.0, d);
-  EXPECT_DOUBLE_EQ(0.0, e);
-  EXPECT_DOUBLE_EQ(0.0, f);
-
-  EXPECT_TRUE(FPDFImageObj_SetMatrix(image, 1, 2, 3, 4, 5, 6));
-  EXPECT_TRUE(FPDFImageObj_GetMatrix(image, &a, &b, &c, &d, &e, &f));
-  EXPECT_DOUBLE_EQ(1.0, a);
-  EXPECT_DOUBLE_EQ(2.0, b);
-  EXPECT_DOUBLE_EQ(3.0, c);
-  EXPECT_DOUBLE_EQ(4.0, d);
-  EXPECT_DOUBLE_EQ(5.0, e);
-  EXPECT_DOUBLE_EQ(6.0, f);
-
-  FPDFPageObj_Destroy(image);
-  FPDF_CloseDocument(doc);
-}
diff --git a/fpdfsdk/fpdf_editpage.cpp b/fpdfsdk/fpdf_editpage.cpp
index 8e979e0..3e6d7fd 100644
--- a/fpdfsdk/fpdf_editpage.cpp
+++ b/fpdfsdk/fpdf_editpage.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -19,24 +19,26 @@
 #include "core/fpdfapi/page/cpdf_formobject.h"
 #include "core/fpdfapi/page/cpdf_imageobject.h"
 #include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/page/cpdf_pathobject.h"
 #include "core/fpdfapi/page/cpdf_shadingobject.h"
+#include "core/fpdfapi/page/cpdf_textobject.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/render/cpdf_docrenderdata.h"
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
 #include "core/fpdfdoc/cpdf_annot.h"
 #include "core/fpdfdoc/cpdf_annotlist.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_memcpy_wrappers.h"
+#include "core/fxcrt/stl_util.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "public/fpdf_formfill.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 #ifdef PDF_ENABLE_XFA
 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
@@ -45,69 +47,71 @@
 
 namespace {
 
-static_assert(FPDF_PAGEOBJ_TEXT == CPDF_PageObject::TEXT,
+static_assert(FPDF_PAGEOBJ_TEXT ==
+                  static_cast<int>(CPDF_PageObject::Type::kText),
               "FPDF_PAGEOBJ_TEXT/CPDF_PageObject::TEXT mismatch");
-static_assert(FPDF_PAGEOBJ_PATH == CPDF_PageObject::PATH,
+static_assert(FPDF_PAGEOBJ_PATH ==
+                  static_cast<int>(CPDF_PageObject::Type::kPath),
               "FPDF_PAGEOBJ_PATH/CPDF_PageObject::PATH mismatch");
-static_assert(FPDF_PAGEOBJ_IMAGE == CPDF_PageObject::IMAGE,
+static_assert(FPDF_PAGEOBJ_IMAGE ==
+                  static_cast<int>(CPDF_PageObject::Type::kImage),
               "FPDF_PAGEOBJ_IMAGE/CPDF_PageObject::IMAGE mismatch");
-static_assert(FPDF_PAGEOBJ_SHADING == CPDF_PageObject::SHADING,
+static_assert(FPDF_PAGEOBJ_SHADING ==
+                  static_cast<int>(CPDF_PageObject::Type::kShading),
               "FPDF_PAGEOBJ_SHADING/CPDF_PageObject::SHADING mismatch");
-static_assert(FPDF_PAGEOBJ_FORM == CPDF_PageObject::FORM,
+static_assert(FPDF_PAGEOBJ_FORM ==
+                  static_cast<int>(CPDF_PageObject::Type::kForm),
               "FPDF_PAGEOBJ_FORM/CPDF_PageObject::FORM mismatch");
 
 bool IsPageObject(CPDF_Page* pPage) {
   if (!pPage)
     return false;
 
-  const CPDF_Dictionary* pFormDict = pPage->GetDict();
-  if (!pFormDict->KeyExist("Type"))
+  RetainPtr<const CPDF_Dictionary> pFormDict = pPage->GetDict();
+  if (!pFormDict->KeyExist(pdfium::page_object::kType))
     return false;
 
-  const CPDF_Object* pObject = pFormDict->GetObjectFor("Type")->GetDirect();
-  return pObject && !pObject->GetString().Compare("Page");
+  RetainPtr<const CPDF_Name> pName =
+      ToName(pFormDict->GetObjectFor(pdfium::page_object::kType)->GetDirect());
+  return pName && pName->GetString() == "Page";
 }
 
 void CalcBoundingBox(CPDF_PageObject* pPageObj) {
   switch (pPageObj->GetType()) {
-    case CPDF_PageObject::TEXT: {
+    case CPDF_PageObject::Type::kText: {
       break;
     }
-    case CPDF_PageObject::PATH: {
+    case CPDF_PageObject::Type::kPath: {
       CPDF_PathObject* pPathObj = pPageObj->AsPath();
       pPathObj->CalcBoundingBox();
       break;
     }
-    case CPDF_PageObject::IMAGE: {
+    case CPDF_PageObject::Type::kImage: {
       CPDF_ImageObject* pImageObj = pPageObj->AsImage();
       pImageObj->CalcBoundingBox();
       break;
     }
-    case CPDF_PageObject::SHADING: {
+    case CPDF_PageObject::Type::kShading: {
       CPDF_ShadingObject* pShadingObj = pPageObj->AsShading();
       pShadingObj->CalcBoundingBox();
       break;
     }
-    case CPDF_PageObject::FORM: {
+    case CPDF_PageObject::Type::kForm: {
       CPDF_FormObject* pFormObj = pPageObj->AsForm();
       pFormObj->CalcBoundingBox();
       break;
     }
-    default: {
-      NOTREACHED();
-      break;
-    }
   }
 }
 
-CPDF_Dictionary* GetMarkParamDict(FPDF_PAGEOBJECTMARK mark) {
+RetainPtr<CPDF_Dictionary> GetMarkParamDict(FPDF_PAGEOBJECTMARK mark) {
   CPDF_ContentMarkItem* pMarkItem =
       CPDFContentMarkItemFromFPDFPageObjectMark(mark);
   return pMarkItem ? pMarkItem->GetParam() : nullptr;
 }
 
-CPDF_Dictionary* GetOrCreateMarkParamsDict(FPDF_DOCUMENT document,
-                                           FPDF_PAGEOBJECTMARK mark) {
+RetainPtr<CPDF_Dictionary> GetOrCreateMarkParamsDict(FPDF_DOCUMENT document,
+                                                     FPDF_PAGEOBJECTMARK mark) {
   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
   if (!pDoc)
     return nullptr;
@@ -117,15 +121,11 @@
   if (!pMarkItem)
     return nullptr;
 
-  CPDF_Dictionary* pParams = pMarkItem->GetParam();
-
-  // If the Params dict does not exist, create a new one.
+  RetainPtr<CPDF_Dictionary> pParams = pMarkItem->GetParam();
   if (!pParams) {
-    auto new_dict = pDoc->New<CPDF_Dictionary>();
-    pParams = new_dict.Get();
-    pMarkItem->SetDirectDict(std::move(new_dict));
+    pParams = pDoc->New<CPDF_Dictionary>();
+    pMarkItem->SetDirectDict(pParams);
   }
-
   return pParams;
 }
 
@@ -133,7 +133,7 @@
                             FPDF_PAGEOBJECTMARK mark) {
   const CPDF_ContentMarkItem* pMarkItem =
       CPDFContentMarkItemFromFPDFPageObjectMark(mark);
-  return pMarkItem && pPageObj->m_ContentMarks.ContainsItem(pMarkItem);
+  return pMarkItem && pPageObj->GetContentMarks()->ContainsItem(pMarkItem);
 }
 
 CPDF_FormObject* CPDFFormObjectFromFPDFPageObject(FPDF_PAGEOBJECT page_object) {
@@ -150,9 +150,9 @@
 }  // namespace
 
 FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV FPDF_CreateNewDocument() {
-  auto pDoc = pdfium::MakeUnique<CPDF_Document>(
-      pdfium::MakeUnique<CPDF_DocRenderData>(),
-      pdfium::MakeUnique<CPDF_DocPageData>());
+  auto pDoc =
+      std::make_unique<CPDF_Document>(std::make_unique<CPDF_DocRenderData>(),
+                                      std::make_unique<CPDF_DocPageData>());
   pDoc->CreateNewDoc();
 
   time_t currentTime;
@@ -168,17 +168,13 @@
     }
   }
 
-  CPDF_Dictionary* pInfoDict = pDoc->GetInfo();
+  RetainPtr<CPDF_Dictionary> pInfoDict = pDoc->GetInfo();
   if (pInfoDict) {
     if (IsPDFSandboxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
       pInfoDict->SetNewFor<CPDF_String>("CreationDate", DateStr, false);
     pInfoDict->SetNewFor<CPDF_String>("Creator", L"PDFium");
   }
 
-#ifdef PDF_ENABLE_XFA
-  pDoc->SetExtension(pdfium::MakeUnique<CPDFXFA_Context>(pDoc.get()));
-#endif  // PDF_ENABLE_XFA
-
   // Caller takes ownership of pDoc.
   return FPDFDocumentFromCPDFDocument(pDoc.release());
 }
@@ -206,8 +202,8 @@
   if (!pDoc)
     return nullptr;
 
-  page_index = pdfium::clamp(page_index, 0, pDoc->GetPageCount());
-  CPDF_Dictionary* pPageDict = pDoc->CreateNewPage(page_index);
+  page_index = std::clamp(page_index, 0, pDoc->GetPageCount());
+  RetainPtr<CPDF_Dictionary> pPageDict(pDoc->CreateNewPage(page_index));
   if (!pPageDict)
     return nullptr;
 
@@ -225,7 +221,7 @@
 #endif  // PDF_ENABLE_XFA
 
   auto pPage = pdfium::MakeRetain<CPDF_Page>(pDoc, pPageDict);
-  pPage->SetRenderCache(pdfium::MakeUnique<CPDF_PageRenderCache>(pPage.Get()));
+  pPage->AddPageImageCache();
   pPage->ParseContent();
 
   return FPDFPageFromIPDFPage(pPage.Leak());  // Caller takes ownership.
@@ -262,7 +258,8 @@
   if (!IsPageObject(pPage))
     return false;
 
-  return pPage->RemovePageObject(pPageObj);
+  // Caller takes ownership.
+  return !!pPage->RemovePageObject(pPageObj).release();
 }
 
 FPDF_EXPORT int FPDF_CALLCONV FPDFPage_CountObjects(FPDF_PAGE page) {
@@ -270,7 +267,7 @@
   if (!IsPageObject(pPage))
     return -1;
 
-  return pPage->GetPageObjectCount();
+  return pdfium::base::checked_cast<int>(pPage->GetPageObjectCount());
 }
 
 FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV FPDFPage_GetObject(FPDF_PAGE page,
@@ -297,7 +294,8 @@
   if (!pPageObj)
     return -1;
 
-  return pPageObj->m_ContentMarks.CountItems();
+  return pdfium::base::checked_cast<int>(
+      pPageObj->GetContentMarks()->CountItems());
 }
 
 FPDF_EXPORT FPDF_PAGEOBJECTMARK FPDF_CALLCONV
@@ -306,11 +304,11 @@
   if (!pPageObj)
     return nullptr;
 
-  auto& mark = pPageObj->m_ContentMarks;
-  if (index >= mark.CountItems())
+  CPDF_ContentMarks* pMarks = pPageObj->GetContentMarks();
+  if (index >= pMarks->CountItems())
     return nullptr;
 
-  return FPDFPageObjectMarkFromCPDFContentMarkItem(mark.GetItem(index));
+  return FPDFPageObjectMarkFromCPDFContentMarkItem(pMarks->GetItem(index));
 }
 
 FPDF_EXPORT FPDF_PAGEOBJECTMARK FPDF_CALLCONV
@@ -319,11 +317,12 @@
   if (!pPageObj)
     return nullptr;
 
-  auto& mark = pPageObj->m_ContentMarks;
-  mark.AddMark(name);
-  unsigned long index = mark.CountItems() - 1;
+  CPDF_ContentMarks* pMarks = pPageObj->GetContentMarks();
+  pMarks->AddMark(name);
   pPageObj->SetDirty(true);
-  return FPDFPageObjectMarkFromCPDFContentMarkItem(mark.GetItem(index));
+
+  const size_t index = pMarks->CountItems() - 1;
+  return FPDFPageObjectMarkFromCPDFContentMarkItem(pMarks->GetItem(index));
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -334,11 +333,11 @@
   if (!pPageObj || !pMarkItem)
     return false;
 
-  bool result = pPageObj->m_ContentMarks.RemoveMark(pMarkItem);
-  if (result)
-    pPageObj->SetDirty(true);
+  if (!pPageObj->GetContentMarks()->RemoveMark(pMarkItem))
+    return false;
 
-  return result;
+  pPageObj->SetDirty(true);
+  return true;
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -346,11 +345,10 @@
                         void* buffer,
                         unsigned long buflen,
                         unsigned long* out_buflen) {
-  if (!mark || !out_buflen)
-    return false;
-
   const CPDF_ContentMarkItem* pMarkItem =
       CPDFContentMarkItemFromFPDFPageObjectMark(mark);
+  if (!pMarkItem || !out_buflen)
+    return false;
 
   *out_buflen = Utf16EncodeMaybeCopyAndReturnLength(
       WideString::FromUTF8(pMarkItem->GetName().AsStringView()), buffer,
@@ -360,14 +358,13 @@
 
 FPDF_EXPORT int FPDF_CALLCONV
 FPDFPageObjMark_CountParams(FPDF_PAGEOBJECTMARK mark) {
-  if (!mark)
-    return -1;
-
   const CPDF_ContentMarkItem* pMarkItem =
       CPDFContentMarkItemFromFPDFPageObjectMark(mark);
+  if (!pMarkItem)
+    return -1;
 
-  const CPDF_Dictionary* pParams = pMarkItem->GetParam();
-  return pParams ? pParams->size() : 0;
+  RetainPtr<const CPDF_Dictionary> pParams = pMarkItem->GetParam();
+  return pParams ? fxcrt::CollectionSize<int>(*pParams) : 0;
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -379,7 +376,7 @@
   if (!out_buflen)
     return false;
 
-  const CPDF_Dictionary* pParams = GetMarkParamDict(mark);
+  RetainPtr<const CPDF_Dictionary> pParams = GetMarkParamDict(mark);
   if (!pParams)
     return false;
 
@@ -399,11 +396,11 @@
 FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
 FPDFPageObjMark_GetParamValueType(FPDF_PAGEOBJECTMARK mark,
                                   FPDF_BYTESTRING key) {
-  const CPDF_Dictionary* pParams = GetMarkParamDict(mark);
+  RetainPtr<const CPDF_Dictionary> pParams = GetMarkParamDict(mark);
   if (!pParams)
     return FPDF_OBJECT_UNKNOWN;
 
-  const CPDF_Object* pObject = pParams->GetObjectFor(key);
+  RetainPtr<const CPDF_Object> pObject = pParams->GetObjectFor(key);
   return pObject ? pObject->GetType() : FPDF_OBJECT_UNKNOWN;
 }
 
@@ -414,11 +411,11 @@
   if (!out_value)
     return false;
 
-  const CPDF_Dictionary* pParams = GetMarkParamDict(mark);
+  RetainPtr<const CPDF_Dictionary> pParams = GetMarkParamDict(mark);
   if (!pParams)
     return false;
 
-  const CPDF_Object* pObj = pParams->GetObjectFor(key);
+  RetainPtr<const CPDF_Object> pObj = pParams->GetObjectFor(key);
   if (!pObj || !pObj->IsNumber())
     return false;
 
@@ -435,11 +432,11 @@
   if (!out_buflen)
     return false;
 
-  const CPDF_Dictionary* pParams = GetMarkParamDict(mark);
+  RetainPtr<const CPDF_Dictionary> pParams = GetMarkParamDict(mark);
   if (!pParams)
     return false;
 
-  const CPDF_Object* pObj = pParams->GetObjectFor(key);
+  RetainPtr<const CPDF_Object> pObj = pParams->GetObjectFor(key);
   if (!pObj || !pObj->IsString())
     return false;
 
@@ -457,16 +454,17 @@
   if (!out_buflen)
     return false;
 
-  const CPDF_Dictionary* pParams = GetMarkParamDict(mark);
+  RetainPtr<const CPDF_Dictionary> pParams = GetMarkParamDict(mark);
   if (!pParams)
     return false;
 
-  const CPDF_Object* pObj = pParams->GetObjectFor(key);
+  RetainPtr<const CPDF_Object> pObj = pParams->GetObjectFor(key);
   if (!pObj || !pObj->IsString())
     return false;
 
   ByteString result = pObj->GetString();
-  unsigned long len = result.GetLength();
+  const unsigned long len =
+      pdfium::base::checked_cast<unsigned long>(result.GetLength());
 
   if (buffer && len <= buflen)
     memcpy(buffer, result.c_str(), len);
@@ -484,9 +482,7 @@
   if (pPageObj->m_GeneralState.GetBlendType() != BlendMode::kNormal)
     return true;
 
-  const CPDF_Dictionary* pSMaskDict =
-      ToDictionary(pPageObj->m_GeneralState.GetSoftMask());
-  if (pSMaskDict)
+  if (pPageObj->m_GeneralState.GetSoftMask())
     return true;
 
   if (pPageObj->m_GeneralState.GetFillAlpha() != 1.0f)
@@ -516,7 +512,8 @@
   if (!pPageObj || !PageObjectContainsMark(pPageObj, mark))
     return false;
 
-  CPDF_Dictionary* pParams = GetOrCreateMarkParamsDict(document, mark);
+  RetainPtr<CPDF_Dictionary> pParams =
+      GetOrCreateMarkParamsDict(document, mark);
   if (!pParams)
     return false;
 
@@ -535,7 +532,8 @@
   if (!pPageObj || !PageObjectContainsMark(pPageObj, mark))
     return false;
 
-  CPDF_Dictionary* pParams = GetOrCreateMarkParamsDict(document, mark);
+  RetainPtr<CPDF_Dictionary> pParams =
+      GetOrCreateMarkParamsDict(document, mark);
   if (!pParams)
     return false;
 
@@ -555,7 +553,8 @@
   if (!pPageObj || !PageObjectContainsMark(pPageObj, mark))
     return false;
 
-  CPDF_Dictionary* pParams = GetOrCreateMarkParamsDict(document, mark);
+  RetainPtr<CPDF_Dictionary> pParams =
+      GetOrCreateMarkParamsDict(document, mark);
   if (!pParams)
     return false;
 
@@ -576,7 +575,7 @@
   if (!pPageObj)
     return false;
 
-  CPDF_Dictionary* pParams = GetMarkParamDict(mark);
+  RetainPtr<CPDF_Dictionary> pParams = GetMarkParamDict(mark);
   if (!pParams)
     return false;
 
@@ -590,7 +589,8 @@
 
 FPDF_EXPORT int FPDF_CALLCONV FPDFPageObj_GetType(FPDF_PAGEOBJECT page_object) {
   CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
-  return pPageObj ? pPageObj->GetType() : FPDF_PAGEOBJ_UNKNOWN;
+  return pPageObj ? static_cast<int>(pPageObj->GetType())
+                  : FPDF_PAGEOBJ_UNKNOWN;
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GenerateContent(FPDF_PAGE page) {
@@ -619,6 +619,57 @@
   pPageObj->Transform(matrix);
 }
 
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_GetMatrix(FPDF_PAGEOBJECT page_object, FS_MATRIX* matrix) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj || !matrix)
+    return false;
+
+  switch (pPageObj->GetType()) {
+    case CPDF_PageObject::Type::kText:
+      *matrix = FSMatrixFromCFXMatrix(pPageObj->AsText()->GetTextMatrix());
+      return true;
+    case CPDF_PageObject::Type::kPath:
+      *matrix = FSMatrixFromCFXMatrix(pPageObj->AsPath()->matrix());
+      return true;
+    case CPDF_PageObject::Type::kImage:
+      *matrix = FSMatrixFromCFXMatrix(pPageObj->AsImage()->matrix());
+      return true;
+    case CPDF_PageObject::Type::kShading:
+      return false;
+    case CPDF_PageObject::Type::kForm:
+      *matrix = FSMatrixFromCFXMatrix(pPageObj->AsForm()->form_matrix());
+      return true;
+  }
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_SetMatrix(FPDF_PAGEOBJECT page_object, const FS_MATRIX* matrix) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj || !matrix)
+    return false;
+
+  CFX_Matrix cmatrix = CFXMatrixFromFSMatrix(*matrix);
+  switch (pPageObj->GetType()) {
+    case CPDF_PageObject::Type::kText:
+      pPageObj->AsText()->SetTextMatrix(cmatrix);
+      break;
+    case CPDF_PageObject::Type::kPath:
+      pPageObj->AsPath()->SetPathMatrix(cmatrix);
+      break;
+    case CPDF_PageObject::Type::kImage:
+      pPageObj->AsImage()->SetImageMatrix(cmatrix);
+      break;
+    case CPDF_PageObject::Type::kShading:
+      return false;
+    case CPDF_PageObject::Type::kForm:
+      pPageObj->AsForm()->SetFormMatrix(cmatrix);
+      break;
+  }
+  pPageObj->SetDirty(true);
+  return true;
+}
+
 FPDF_EXPORT void FPDF_CALLCONV
 FPDFPageObj_SetBlendMode(FPDF_PAGEOBJECT page_object,
                          FPDF_BYTESTRING blend_mode) {
@@ -648,17 +699,17 @@
                       (float)f);
     CFX_FloatRect rect = matrix.TransformRect(pAnnot->GetRect());
 
-    CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
-    CPDF_Array* pRectArray = pAnnotDict->GetArrayFor("Rect");
+    RetainPtr<CPDF_Dictionary> pAnnotDict = pAnnot->GetMutableAnnotDict();
+    RetainPtr<CPDF_Array> pRectArray = pAnnotDict->GetMutableArrayFor("Rect");
     if (pRectArray)
       pRectArray->Clear();
     else
       pRectArray = pAnnotDict->SetNewFor<CPDF_Array>("Rect");
 
-    pRectArray->AddNew<CPDF_Number>(rect.left);
-    pRectArray->AddNew<CPDF_Number>(rect.bottom);
-    pRectArray->AddNew<CPDF_Number>(rect.right);
-    pRectArray->AddNew<CPDF_Number>(rect.top);
+    pRectArray->AppendNew<CPDF_Number>(rect.left);
+    pRectArray->AppendNew<CPDF_Number>(rect.bottom);
+    pRectArray->AppendNew<CPDF_Number>(rect.right);
+    pRectArray->AppendNew<CPDF_Number>(rect.top);
 
     // TODO(unknown): Transform AP's rectangle
   }
@@ -671,8 +722,8 @@
     return;
 
   rotate %= 4;
-  pPage->GetDict()->SetNewFor<CPDF_Number>(pdfium::page_object::kRotate,
-                                           rotate * 90);
+  pPage->GetMutableDict()->SetNewFor<CPDF_Number>(pdfium::page_object::kRotate,
+                                                  rotate * 90);
   pPage->UpdateDimensions();
 }
 
@@ -688,7 +739,8 @@
   std::vector<float> rgb = {R / 255.f, G / 255.f, B / 255.f};
   pPageObj->m_GeneralState.SetFillAlpha(A / 255.f);
   pPageObj->m_ColorState.SetFillColor(
-      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB), rgb);
+      CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB),
+      std::move(rgb));
   pPageObj->SetDirty(true);
   return true;
 }
@@ -733,6 +785,44 @@
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_GetRotatedBounds(FPDF_PAGEOBJECT page_object,
+                             FS_QUADPOINTSF* quad_points) {
+  CPDF_PageObject* cpage_object = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!cpage_object || !quad_points)
+    return false;
+
+  CFX_Matrix matrix;
+  switch (cpage_object->GetType()) {
+    case CPDF_PageObject::Type::kText:
+      matrix = cpage_object->AsText()->GetTextMatrix();
+      break;
+    case CPDF_PageObject::Type::kImage:
+      matrix = cpage_object->AsImage()->matrix();
+      break;
+    default:
+      // TODO(crbug.com/pdfium/1840): Support more object types.
+      return false;
+  }
+
+  const CFX_FloatRect& bbox = cpage_object->GetOriginalRect();
+  const CFX_PointF bottom_left = matrix.Transform({bbox.left, bbox.bottom});
+  const CFX_PointF bottom_right = matrix.Transform({bbox.right, bbox.bottom});
+  const CFX_PointF top_right = matrix.Transform({bbox.right, bbox.top});
+  const CFX_PointF top_left = matrix.Transform({bbox.left, bbox.top});
+
+  // See PDF 32000-1:2008, figure 64 for the QuadPoints ordering.
+  quad_points->x1 = bottom_left.x;
+  quad_points->y1 = bottom_left.y;
+  quad_points->x2 = bottom_right.x;
+  quad_points->y2 = bottom_right.y;
+  quad_points->x3 = top_right.x;
+  quad_points->y3 = top_right.y;
+  quad_points->x4 = top_left.x;
+  quad_points->y4 = top_left.y;
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFPageObj_SetStrokeColor(FPDF_PAGEOBJECT page_object,
                            unsigned int R,
                            unsigned int G,
@@ -745,7 +835,8 @@
   std::vector<float> rgb = {R / 255.f, G / 255.f, B / 255.f};
   pPageObj->m_GeneralState.SetStrokeAlpha(A / 255.f);
   pPageObj->m_ColorState.SetStrokeColor(
-      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB), rgb);
+      CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB),
+      std::move(rgb));
   pPageObj->SetDirty(true);
   return true;
 }
@@ -795,7 +886,7 @@
 FPDF_EXPORT int FPDF_CALLCONV
 FPDFPageObj_GetLineJoin(FPDF_PAGEOBJECT page_object) {
   auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
-  return pPageObj ? pPageObj->m_GraphState.GetLineJoin() : -1;
+  return pPageObj ? static_cast<int>(pPageObj->m_GraphState.GetLineJoin()) : -1;
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -804,11 +895,7 @@
   if (!pPageObj)
     return false;
 
-  constexpr int kLineJoinMiter =
-      static_cast<int>(CFX_GraphStateData::LineJoin::LineJoinMiter);
-  constexpr int kLineJoinBevel =
-      static_cast<int>(CFX_GraphStateData::LineJoin::LineJoinBevel);
-  if (line_join < kLineJoinMiter || line_join > kLineJoinBevel)
+  if (line_join < FPDF_LINEJOIN_MITER || line_join > FPDF_LINEJOIN_BEVEL)
     return false;
 
   pPageObj->m_GraphState.SetLineJoin(
@@ -820,7 +907,7 @@
 FPDF_EXPORT int FPDF_CALLCONV
 FPDFPageObj_GetLineCap(FPDF_PAGEOBJECT page_object) {
   auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
-  return pPageObj ? pPageObj->m_GraphState.GetLineCap() : -1;
+  return pPageObj ? static_cast<int>(pPageObj->m_GraphState.GetLineCap()) : -1;
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -829,23 +916,92 @@
   if (!pPageObj)
     return false;
 
-  constexpr int kLineCapButt =
-      static_cast<int>(CFX_GraphStateData::LineCap::LineCapButt);
-  constexpr int kLineCapSquare =
-      static_cast<int>(CFX_GraphStateData::LineCap::LineCapSquare);
-  if (line_cap < kLineCapButt || line_cap > kLineCapSquare)
+  if (line_cap < FPDF_LINECAP_BUTT ||
+      line_cap > FPDF_LINECAP_PROJECTING_SQUARE) {
     return false;
-
+  }
   pPageObj->m_GraphState.SetLineCap(
       static_cast<CFX_GraphStateData::LineCap>(line_cap));
   pPageObj->SetDirty(true);
   return true;
 }
 
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_GetDashPhase(FPDF_PAGEOBJECT page_object, float* phase) {
+  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj || !phase)
+    return false;
+
+  *phase = pPageObj->m_GraphState.GetLineDashPhase();
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_SetDashPhase(FPDF_PAGEOBJECT page_object, float phase) {
+  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj)
+    return false;
+
+  pPageObj->m_GraphState.SetLineDashPhase(phase);
+  pPageObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFPageObj_GetDashCount(FPDF_PAGEOBJECT page_object) {
+  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  return pPageObj ? pdfium::base::checked_cast<int>(
+                        pPageObj->m_GraphState.GetLineDashSize())
+                  : -1;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_GetDashArray(FPDF_PAGEOBJECT page_object,
+                         float* dash_array,
+                         size_t dash_count) {
+  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj || !dash_array)
+    return false;
+
+  auto dash_vector = pPageObj->m_GraphState.GetLineDashArray();
+  if (dash_vector.size() > dash_count)
+    return false;
+
+  FXSYS_memcpy(dash_array, dash_vector.data(),
+               dash_vector.size() * sizeof(float));
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_SetDashArray(FPDF_PAGEOBJECT page_object,
+                         const float* dash_array,
+                         size_t dash_count,
+                         float phase) {
+  if (dash_count > 0 && !dash_array)
+    return false;
+
+  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj)
+    return false;
+
+  std::vector<float> dashes;
+  if (dash_count > 0) {
+    dashes.reserve(dash_count);
+    dashes.assign(dash_array, dash_array + dash_count);
+  }
+
+  pPageObj->m_GraphState.SetLineDash(dashes, phase, 1.0f);
+
+  pPageObj->SetDirty(true);
+  return true;
+}
+
 FPDF_EXPORT int FPDF_CALLCONV
 FPDFFormObj_CountObjects(FPDF_PAGEOBJECT form_object) {
   const auto* pObjectList = CPDFPageObjHolderFromFPDFFormObject(form_object);
-  return pObjectList ? pObjectList->GetPageObjectCount() : -1;
+  return pObjectList ? pdfium::base::checked_cast<int>(
+                           pObjectList->GetPageObjectCount())
+                     : -1;
 }
 
 FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
@@ -857,13 +1013,3 @@
   return FPDFPageObjectFromCPDFPageObject(
       pObjectList->GetPageObjectByIndex(index));
 }
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFFormObj_GetMatrix(FPDF_PAGEOBJECT form_object, FS_MATRIX* matrix) {
-  CPDF_FormObject* pFormObj = CPDFFormObjectFromFPDFPageObject(form_object);
-  if (!pFormObj || !matrix)
-    return false;
-
-  *matrix = FSMatrixFromCFXMatrix(pFormObj->form_matrix());
-  return true;
-}
diff --git a/fpdfsdk/fpdf_editpage_embeddertest.cpp b/fpdfsdk/fpdf_editpage_embeddertest.cpp
index 6650b40..e6eba89 100644
--- a/fpdfsdk/fpdf_editpage_embeddertest.cpp
+++ b/fpdfsdk/fpdf_editpage_embeddertest.cpp
@@ -1,22 +1,21 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fxcrt/fx_system.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
 #include "public/fpdf_edit.h"
 #include "testing/embedder_test.h"
+#include "testing/embedder_test_constants.h"
 
 class FPDFEditPageEmbedderTest : public EmbedderTest {};
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_Rotation DISABLED_Rotation
-#else
-#define MAYBE_Rotation Rotation
-#endif
-TEST_F(FPDFEditPageEmbedderTest, MAYBE_Rotation) {
-  const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe";
-  const char kRotatedMD5[] = "d599429574ff0dcad3bc898ea8b874ca";
+TEST_F(FPDFEditPageEmbedderTest, Rotation) {
+  const char* rotated_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "eded83f75f3d0332c584c416c571c0df";
+    return "d599429574ff0dcad3bc898ea8b874ca";
+  }();
 
   {
     ASSERT_TRUE(OpenDocument("rectangles.pdf"));
@@ -31,7 +30,8 @@
       EXPECT_EQ(200, page_width);
       EXPECT_EQ(300, page_height);
       ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-      CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+      CompareBitmap(bitmap.get(), page_width, page_height,
+                    pdfium::RectanglesChecksum());
     }
 
     FPDFPage_SetRotation(page, 1);
@@ -46,7 +46,7 @@
       EXPECT_EQ(300, page_width);
       EXPECT_EQ(200, page_height);
       ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-      CompareBitmap(bitmap.get(), page_width, page_height, kRotatedMD5);
+      CompareBitmap(bitmap.get(), page_width, page_height, rotated_checksum);
     }
 
     UnloadPage(page);
@@ -66,7 +66,7 @@
     EXPECT_EQ(300, page_width);
     EXPECT_EQ(200, page_height);
     ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
-    CompareBitmap(bitmap.get(), page_width, page_height, kRotatedMD5);
+    CompareBitmap(bitmap.get(), page_width, page_height, rotated_checksum);
 
     CloseSavedPage(saved_page);
     CloseSavedDocument();
@@ -97,7 +97,7 @@
 
 TEST_F(FPDFEditPageEmbedderTest, HasTransparencyPath) {
   constexpr int kExpectedObjectCount = 8;
-  EXPECT_TRUE(OpenDocument("rectangles.pdf"));
+  ASSERT_TRUE(OpenDocument("rectangles.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   ASSERT_EQ(kExpectedObjectCount, FPDFPage_CountObjects(page));
@@ -115,7 +115,7 @@
 
 TEST_F(FPDFEditPageEmbedderTest, HasTransparencyText) {
   constexpr int kExpectedObjectCount = 2;
-  EXPECT_TRUE(OpenDocument("text_render_mode.pdf"));
+  ASSERT_TRUE(OpenDocument("text_render_mode.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   ASSERT_EQ(kExpectedObjectCount, FPDFPage_CountObjects(page));
@@ -155,3 +155,352 @@
 
   UnloadPage(page);
 }
+
+TEST_F(FPDFEditPageEmbedderTest, DashingArrayAndPhase) {
+  {
+    EXPECT_FALSE(FPDFPageObj_GetDashPhase(nullptr, nullptr));
+
+    float phase = -1123.5f;
+    EXPECT_FALSE(FPDFPageObj_GetDashPhase(nullptr, &phase));
+    EXPECT_FLOAT_EQ(-1123.5f, phase);
+
+    EXPECT_EQ(-1, FPDFPageObj_GetDashCount(nullptr));
+
+    EXPECT_FALSE(FPDFPageObj_GetDashArray(nullptr, nullptr, 3));
+
+    float get_array[] = {-1.0f, -1.0f, -1.0f};
+    EXPECT_FALSE(FPDFPageObj_GetDashArray(nullptr, get_array, 3));
+    for (int i = 0; i < 3; i++)
+      EXPECT_FLOAT_EQ(-1.0f, get_array[i]);
+
+    EXPECT_FALSE(FPDFPageObj_SetDashPhase(nullptr, 5.0f));
+    EXPECT_FALSE(FPDFPageObj_SetDashArray(nullptr, nullptr, 3, 5.0f));
+
+    float set_array[] = {1.0f, 2.0f, 3.0f};
+    EXPECT_FALSE(FPDFPageObj_SetDashArray(nullptr, set_array, 3, 5.0f));
+  }
+
+  constexpr int kExpectedObjectCount = 3;
+  ASSERT_TRUE(OpenDocument("dashed_lines.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ASSERT_EQ(kExpectedObjectCount, FPDFPage_CountObjects(page));
+
+  {
+    FPDF_PAGEOBJECT path = FPDFPage_GetObject(page, 0);
+    ASSERT_TRUE(path);
+    EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(path));
+
+    EXPECT_FALSE(FPDFPageObj_GetDashPhase(path, nullptr));
+    EXPECT_FALSE(FPDFPageObj_GetDashArray(path, nullptr, 3));
+    EXPECT_FALSE(FPDFPageObj_SetDashArray(path, nullptr, 3, 5.0f));
+
+    float phase = -1123.5f;
+    EXPECT_TRUE(FPDFPageObj_GetDashPhase(path, &phase));
+    EXPECT_FLOAT_EQ(0.0f, phase);
+    EXPECT_EQ(0, FPDFPageObj_GetDashCount(path));
+
+    float get_array[] = {-1.0f, -1.0f, -1.0f};
+    EXPECT_TRUE(FPDFPageObj_GetDashArray(path, get_array, 3));
+    for (int i = 0; i < 3; i++)
+      EXPECT_FLOAT_EQ(-1.0f, get_array[i]);
+  }
+
+  {
+    FPDF_PAGEOBJECT path = FPDFPage_GetObject(page, 1);
+    ASSERT_TRUE(path);
+    EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(path));
+
+    float phase = -1123.5f;
+    EXPECT_TRUE(FPDFPageObj_GetDashPhase(path, &phase));
+    EXPECT_LT(0.0f, phase);
+    ASSERT_EQ(6, FPDFPageObj_GetDashCount(path));
+
+    float dash_array[] = {-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f};
+    ASSERT_TRUE(FPDFPageObj_GetDashArray(path, dash_array, 6));
+
+    for (int i = 0; i < 6; i++)
+      EXPECT_LT(0.0f, dash_array[i]);
+
+    // the array is decreasing in value.
+    for (int i = 0; i < 5; i++)
+      EXPECT_GT(dash_array[i], dash_array[i + 1]);
+
+    // modify phase
+    EXPECT_TRUE(FPDFPageObj_SetDashPhase(path, 1.0f));
+
+    phase = -1123.5f;
+    EXPECT_TRUE(FPDFPageObj_GetDashPhase(path, &phase));
+    EXPECT_FLOAT_EQ(1.0f, phase);
+
+    // clear array
+    EXPECT_TRUE(FPDFPageObj_SetDashArray(path, nullptr, 0, 0.0f));
+    EXPECT_EQ(0, FPDFPageObj_GetDashCount(path));
+
+    phase = -1123.5f;
+    EXPECT_TRUE(FPDFPageObj_GetDashPhase(path, &phase));
+    EXPECT_FLOAT_EQ(0.0f, phase);
+  }
+
+  {
+    FPDF_PAGEOBJECT path = FPDFPage_GetObject(page, 2);
+    ASSERT_TRUE(path);
+    EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(path));
+
+    float phase = -1123.5f;
+    EXPECT_TRUE(FPDFPageObj_GetDashPhase(path, &phase));
+    EXPECT_FLOAT_EQ(0.0f, phase);
+
+    EXPECT_EQ(0, FPDFPageObj_GetDashCount(path));
+
+    // `get_array` should be unmodified
+    float get_array[] = {-1.0f, -1.0f, -1.0f, -1.0f};
+    EXPECT_TRUE(FPDFPageObj_GetDashArray(path, get_array, 4));
+    for (int i = 0; i < 4; i++)
+      EXPECT_FLOAT_EQ(-1.0f, get_array[i]);
+
+    // modify dash_array and phase
+    const float set_array[] = {1.0f, 2.0f, 3.0f};
+    EXPECT_TRUE(FPDFPageObj_SetDashArray(path, set_array, 3, 5.0f));
+
+    phase = -1123.5f;
+    EXPECT_TRUE(FPDFPageObj_GetDashPhase(path, &phase));
+    EXPECT_FLOAT_EQ(5.0f, phase);
+    ASSERT_EQ(3, FPDFPageObj_GetDashCount(path));
+
+    // Pretend `get_array` has too few members.
+    EXPECT_FALSE(FPDFPageObj_GetDashArray(path, get_array, 2));
+    for (int i = 0; i < 4; i++)
+      EXPECT_FLOAT_EQ(-1.0f, get_array[i]);
+
+    ASSERT_TRUE(FPDFPageObj_GetDashArray(path, get_array, 4));
+
+    // `get_array` should be modified only up to dash_count
+    for (int i = 0; i < 3; i++)
+      EXPECT_FLOAT_EQ(static_cast<float>(i + 1), get_array[i]);
+
+    EXPECT_FLOAT_EQ(-1.0f, get_array[3]);
+
+    // clear array
+    EXPECT_TRUE(FPDFPageObj_SetDashArray(path, set_array, 0, 4.0f));
+    EXPECT_EQ(0, FPDFPageObj_GetDashCount(path));
+
+    phase = -1123.5f;
+    EXPECT_TRUE(FPDFPageObj_GetDashPhase(path, &phase));
+    EXPECT_FLOAT_EQ(4.0f, phase);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditPageEmbedderTest, GetRotatedBoundsBadParameters) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 0);
+  ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(obj));
+
+  FS_QUADPOINTSF quad;
+  ASSERT_FALSE(FPDFPageObj_GetRotatedBounds(nullptr, nullptr));
+  ASSERT_FALSE(FPDFPageObj_GetRotatedBounds(obj, nullptr));
+  ASSERT_FALSE(FPDFPageObj_GetRotatedBounds(nullptr, &quad));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditPageEmbedderTest, GetBoundsForNormalText) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 0);
+  ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(obj));
+
+  constexpr float kExpectedLeft = 20.348f;
+  constexpr float kExpectedBottom = 48.164f;
+  constexpr float kExpectedRight = 83.36f;
+  constexpr float kExpectedTop = 58.328f;
+
+  float left;
+  float bottom;
+  float right;
+  float top;
+  ASSERT_TRUE(FPDFPageObj_GetBounds(obj, &left, &bottom, &right, &top));
+  EXPECT_FLOAT_EQ(kExpectedLeft, left);
+  EXPECT_FLOAT_EQ(kExpectedBottom, bottom);
+  EXPECT_FLOAT_EQ(kExpectedRight, right);
+  EXPECT_FLOAT_EQ(kExpectedTop, top);
+
+  FS_QUADPOINTSF quad;
+  ASSERT_TRUE(FPDFPageObj_GetRotatedBounds(obj, &quad));
+  EXPECT_FLOAT_EQ(kExpectedLeft, quad.x1);
+  EXPECT_FLOAT_EQ(kExpectedBottom, quad.y1);
+  EXPECT_FLOAT_EQ(kExpectedRight, quad.x2);
+  EXPECT_FLOAT_EQ(kExpectedBottom, quad.y2);
+  EXPECT_FLOAT_EQ(kExpectedRight, quad.x3);
+  EXPECT_FLOAT_EQ(kExpectedTop, quad.y3);
+  EXPECT_FLOAT_EQ(kExpectedLeft, quad.x4);
+  EXPECT_FLOAT_EQ(kExpectedTop, quad.y4);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditPageEmbedderTest, GetBoundsForRotatedText) {
+  ASSERT_TRUE(OpenDocument("rotated_text.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 0);
+  ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(obj));
+
+  constexpr float kExpectedLeft = 98.9478f;
+  constexpr float kExpectedBottom = 78.2607f;
+  constexpr float kExpectedRight = 126.32983f;
+  constexpr float kExpectedTop = 105.64272f;
+
+  float left;
+  float bottom;
+  float right;
+  float top;
+  ASSERT_TRUE(FPDFPageObj_GetBounds(obj, &left, &bottom, &right, &top));
+  EXPECT_FLOAT_EQ(kExpectedLeft, left);
+  EXPECT_FLOAT_EQ(kExpectedBottom, bottom);
+  EXPECT_FLOAT_EQ(kExpectedRight, right);
+  EXPECT_FLOAT_EQ(kExpectedTop, top);
+
+  FS_QUADPOINTSF quad;
+  ASSERT_TRUE(FPDFPageObj_GetRotatedBounds(obj, &quad));
+  EXPECT_FLOAT_EQ(kExpectedLeft, quad.x1);
+  EXPECT_FLOAT_EQ(98.4557f, quad.y1);
+  EXPECT_FLOAT_EQ(119.14279f, quad.x2);
+  EXPECT_FLOAT_EQ(kExpectedBottom, quad.y2);
+  EXPECT_FLOAT_EQ(kExpectedRight, quad.x3);
+  EXPECT_FLOAT_EQ(85.447739f, quad.y3);
+  EXPECT_FLOAT_EQ(106.13486f, quad.x4);
+  EXPECT_FLOAT_EQ(kExpectedTop, quad.y4);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditPageEmbedderTest, GetBoundsForNormalImage) {
+  ASSERT_TRUE(OpenDocument("matte.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 2);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+
+  constexpr float kExpectedLeft = 0.0f;
+  constexpr float kExpectedBottom = 90.0f;
+  constexpr float kExpectedRight = 40.0f;
+  constexpr float kExpectedTop = 150.0f;
+
+  float left;
+  float bottom;
+  float right;
+  float top;
+  ASSERT_TRUE(FPDFPageObj_GetBounds(obj, &left, &bottom, &right, &top));
+  EXPECT_FLOAT_EQ(kExpectedLeft, left);
+  EXPECT_FLOAT_EQ(kExpectedBottom, bottom);
+  EXPECT_FLOAT_EQ(kExpectedRight, right);
+  EXPECT_FLOAT_EQ(kExpectedTop, top);
+
+  FS_QUADPOINTSF quad;
+  ASSERT_TRUE(FPDFPageObj_GetRotatedBounds(obj, &quad));
+  EXPECT_FLOAT_EQ(kExpectedLeft, quad.x1);
+  EXPECT_FLOAT_EQ(kExpectedBottom, quad.y1);
+  EXPECT_FLOAT_EQ(kExpectedRight, quad.x2);
+  EXPECT_FLOAT_EQ(kExpectedBottom, quad.y2);
+  EXPECT_FLOAT_EQ(kExpectedRight, quad.x3);
+  EXPECT_FLOAT_EQ(kExpectedTop, quad.y3);
+  EXPECT_FLOAT_EQ(kExpectedLeft, quad.x4);
+  EXPECT_FLOAT_EQ(kExpectedTop, quad.y4);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditPageEmbedderTest, GetBoundsForRotatedImage) {
+  ASSERT_TRUE(OpenDocument("rotated_image.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 0);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+
+  constexpr float kExpectedLeft = 100.0f;
+  constexpr float kExpectedBottom = 70.0f;
+  constexpr float kExpectedRight = 170.0f;
+  constexpr float kExpectedTop = 140.0f;
+
+  float left;
+  float bottom;
+  float right;
+  float top;
+  ASSERT_TRUE(FPDFPageObj_GetBounds(obj, &left, &bottom, &right, &top));
+  EXPECT_FLOAT_EQ(kExpectedLeft, left);
+  EXPECT_FLOAT_EQ(kExpectedBottom, bottom);
+  EXPECT_FLOAT_EQ(kExpectedRight, right);
+  EXPECT_FLOAT_EQ(kExpectedTop, top);
+
+  FS_QUADPOINTSF quad;
+  ASSERT_TRUE(FPDFPageObj_GetRotatedBounds(obj, &quad));
+  EXPECT_FLOAT_EQ(kExpectedLeft, quad.x1);
+  EXPECT_FLOAT_EQ(100.0f, quad.y1);
+  EXPECT_FLOAT_EQ(130.0f, quad.x2);
+  EXPECT_FLOAT_EQ(kExpectedBottom, quad.y2);
+  EXPECT_FLOAT_EQ(kExpectedRight, quad.x3);
+  EXPECT_FLOAT_EQ(110.0f, quad.y3);
+  EXPECT_FLOAT_EQ(140.0f, quad.x4);
+  EXPECT_FLOAT_EQ(kExpectedTop, quad.y4);
+
+  UnloadPage(page);
+}
+
+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 aaa4b72..c0b8db3 100644
--- a/fpdfsdk/fpdf_editpath.cpp
+++ b/fpdfsdk/fpdf_editpath.cpp
@@ -1,41 +1,49 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "public/fpdf_edit.h"
 
+#include <memory>
 #include <utility>
-#include <vector>
 
 #include "core/fpdfapi/page/cpdf_path.h"
 #include "core/fpdfapi/page/cpdf_pathobject.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/stl_util.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/containers/span.h"
 
 // These checks are here because core/ and public/ cannot depend on each other.
-static_assert(CFX_GraphStateData::LineCapButt == FPDF_LINECAP_BUTT,
-              "CFX_GraphStateData::LineCapButt value mismatch");
-static_assert(CFX_GraphStateData::LineCapRound == FPDF_LINECAP_ROUND,
-              "CFX_GraphStateData::LineCapRound value mismatch");
-static_assert(CFX_GraphStateData::LineCapSquare ==
+static_assert(static_cast<int>(CFX_GraphStateData::LineCap::kButt) ==
+                  FPDF_LINECAP_BUTT,
+              "CFX_GraphStateData::LineCap::kButt value mismatch");
+static_assert(static_cast<int>(CFX_GraphStateData::LineCap::kRound) ==
+                  FPDF_LINECAP_ROUND,
+              "CFX_GraphStateData::LineCap::kRound value mismatch");
+static_assert(static_cast<int>(CFX_GraphStateData::LineCap::kSquare) ==
                   FPDF_LINECAP_PROJECTING_SQUARE,
-              "CFX_GraphStateData::LineCapSquare value mismatch");
+              "CFX_GraphStateData::LineCap::kSquare value mismatch");
 
-static_assert(CFX_GraphStateData::LineJoinMiter == FPDF_LINEJOIN_MITER,
-              "CFX_GraphStateData::LineJoinMiter value mismatch");
-static_assert(CFX_GraphStateData::LineJoinRound == FPDF_LINEJOIN_ROUND,
-              "CFX_GraphStateData::LineJoinRound value mismatch");
-static_assert(CFX_GraphStateData::LineJoinBevel == FPDF_LINEJOIN_BEVEL,
-              "CFX_GraphStateData::LineJoinBevel value mismatch");
+static_assert(static_cast<int>(CFX_GraphStateData::LineJoin::kMiter) ==
+                  FPDF_LINEJOIN_MITER,
+              "CFX_GraphStateData::LineJoin::kMiter value mismatch");
+static_assert(static_cast<int>(CFX_GraphStateData::LineJoin::kRound) ==
+                  FPDF_LINEJOIN_ROUND,
+              "CFX_GraphStateData::LineJoin::kRound value mismatch");
+static_assert(static_cast<int>(CFX_GraphStateData::LineJoin::kBevel) ==
+                  FPDF_LINEJOIN_BEVEL,
+              "CFX_GraphStateData::LineJoin::kBevel value mismatch");
 
-static_assert(static_cast<int>(FXPT_TYPE::LineTo) == FPDF_SEGMENT_LINETO,
-              "FXPT_TYPE::LineTo value mismatch");
-static_assert(static_cast<int>(FXPT_TYPE::BezierTo) == FPDF_SEGMENT_BEZIERTO,
-              "FXPT_TYPE::BezierTo value mismatch");
-static_assert(static_cast<int>(FXPT_TYPE::MoveTo) == FPDF_SEGMENT_MOVETO,
-              "FXPT_TYPE::MoveTo value mismatch");
+static_assert(static_cast<int>(CFX_Path::Point::Type::kLine) ==
+                  FPDF_SEGMENT_LINETO,
+              "CFX_Path::Point::Type::kLine value mismatch");
+static_assert(static_cast<int>(CFX_Path::Point::Type::kBezier) ==
+                  FPDF_SEGMENT_BEZIERTO,
+              "CFX_Path::Point::Type::kBezier value mismatch");
+static_assert(static_cast<int>(CFX_Path::Point::Type::kMove) ==
+                  FPDF_SEGMENT_MOVETO,
+              "CFX_Path::Point::Type::kMove value mismatch");
 
 namespace {
 
@@ -48,8 +56,8 @@
 
 FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV FPDFPageObj_CreateNewPath(float x,
                                                                     float y) {
-  auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
-  pPathObj->path().AppendPoint(CFX_PointF(x, y), FXPT_TYPE::MoveTo, false);
+  auto pPathObj = std::make_unique<CPDF_PathObject>();
+  pPathObj->path().AppendPoint(CFX_PointF(x, y), CFX_Path::Point::Type::kMove);
   pPathObj->DefaultStates();
 
   // Caller takes ownership.
@@ -60,7 +68,7 @@
                                                                     float y,
                                                                     float w,
                                                                     float h) {
-  auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
+  auto pPathObj = std::make_unique<CPDF_PathObject>();
   pPathObj->path().AppendRect(x, y, x + w, y + h);
   pPathObj->DefaultStates();
 
@@ -72,7 +80,7 @@
   auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
   if (!pPathObj)
     return -1;
-  return pdfium::CollectionSize<int>(pPathObj->path().GetPoints());
+  return fxcrt::CollectionSize<int>(pPathObj->path().GetPoints());
 }
 
 FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
@@ -81,8 +89,8 @@
   if (!pPathObj)
     return nullptr;
 
-  const std::vector<FX_PATHPOINT>& points = pPathObj->path().GetPoints();
-  if (!pdfium::IndexInBounds(points, index))
+  pdfium::span<const CFX_Path::Point> points = pPathObj->path().GetPoints();
+  if (!fxcrt::IndexInBounds(points, index))
     return nullptr;
 
   return FPDFPathSegmentFromFXPathPoint(&points[index]);
@@ -95,7 +103,7 @@
   if (!pPathObj)
     return false;
 
-  pPathObj->path().AppendPoint(CFX_PointF(x, y), FXPT_TYPE::MoveTo, false);
+  pPathObj->path().AppendPoint(CFX_PointF(x, y), CFX_Path::Point::Type::kMove);
   pPathObj->SetDirty(true);
   return true;
 }
@@ -107,7 +115,7 @@
   if (!pPathObj)
     return false;
 
-  pPathObj->path().AppendPoint(CFX_PointF(x, y), FXPT_TYPE::LineTo, false);
+  pPathObj->path().AppendPoint(CFX_PointF(x, y), CFX_Path::Point::Type::kLine);
   pPathObj->SetDirty(true);
   return true;
 }
@@ -124,9 +132,9 @@
     return false;
 
   CPDF_Path& cpath = pPathObj->path();
-  cpath.AppendPoint(CFX_PointF(x1, y1), FXPT_TYPE::BezierTo, false);
-  cpath.AppendPoint(CFX_PointF(x2, y2), FXPT_TYPE::BezierTo, false);
-  cpath.AppendPoint(CFX_PointF(x3, y3), FXPT_TYPE::BezierTo, false);
+  cpath.AppendPoint(CFX_PointF(x1, y1), CFX_Path::Point::Type::kBezier);
+  cpath.AppendPoint(CFX_PointF(x2, y2), CFX_Path::Point::Type::kBezier);
+  cpath.AppendPoint(CFX_PointF(x3, y3), CFX_Path::Point::Type::kBezier);
   pPathObj->SetDirty(true);
   return true;
 }
@@ -181,33 +189,6 @@
   return true;
 }
 
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_GetMatrix(FPDF_PAGEOBJECT path,
-                                                       FS_MATRIX* matrix) {
-  if (!path || !matrix)
-    return false;
-
-  CPDF_PathObject* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
-  if (!pPathObj)
-    return false;
-
-  *matrix = FSMatrixFromCFXMatrix(pPathObj->matrix());
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFPath_SetMatrix(FPDF_PAGEOBJECT path, const FS_MATRIX* matrix) {
-  if (!matrix)
-    return false;
-
-  CPDF_PathObject* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
-  if (!pPathObj)
-    return false;
-
-  pPathObj->set_matrix(CFXMatrixFromFSMatrix(*matrix));
-  pPathObj->SetDirty(true);
-  return true;
-}
-
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFPathSegment_GetPoint(FPDF_PATHSEGMENT segment, float* x, float* y) {
   auto* pPathPoint = FXPathPointFromFPDFPathSegment(segment);
diff --git a/fpdfsdk/fpdf_editpath_embeddertest.cpp b/fpdfsdk/fpdf_editpath_embeddertest.cpp
index 2a8fb6a..4891004 100644
--- a/fpdfsdk/fpdf_editpath_embeddertest.cpp
+++ b/fpdfsdk/fpdf_editpath_embeddertest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,7 @@
 #include "public/fpdf_edit.h"
 #include "testing/embedder_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/check.h"
 
 class FPDFEditPathEmbedderTest : public EmbedderTest {};
 
@@ -33,7 +34,7 @@
 
   ASSERT_TRUE(OpenSavedDocument());
   page = LoadSavedPage(0);
-  ASSERT(page);
+  ASSERT_TRUE(page);
 
   for (size_t i = 0; i < kObjectCount; ++i) {
     FPDF_PAGEOBJECT path = FPDFPage_GetObject(page, i);
diff --git a/fpdfsdk/fpdf_edittext.cpp b/fpdfsdk/fpdf_edittext.cpp
index a3b4208..55ac4f7 100644
--- a/fpdfsdk/fpdf_edittext.cpp
+++ b/fpdfsdk/fpdf_edittext.cpp
@@ -1,16 +1,15 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <algorithm>
-#include <limits>
 #include <map>
 #include <memory>
+#include <sstream>
 #include <utility>
 #include <vector>
 
+#include "core/fpdfapi/font/cpdf_cidfont.h"
 #include "core/fpdfapi/font/cpdf_font.h"
-#include "core/fpdfapi/font/cpdf_type1font.h"
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
 #include "core/fpdfapi/page/cpdf_textobject.h"
 #include "core/fpdfapi/page/cpdf_textstate.h"
@@ -22,13 +21,28 @@
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fpdfapi/render/charposlist.h"
+#include "core/fpdfapi/render/cpdf_pagerendercontext.h"
+#include "core/fpdfapi/render/cpdf_rendercontext.h"
+#include "core/fpdfapi/render/cpdf_renderstatus.h"
+#include "core/fpdfapi/render/cpdf_textrenderer.h"
 #include "core/fpdftext/cpdf_textpage.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_string_wrappers.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxcrt/stl_util.h"
+#include "core/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"
 #include "public/fpdf_edit.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 // These checks are here because core/ and public/ cannot depend on each other.
 static_assert(static_cast<int>(TextRenderingMode::MODE_UNKNOWN) ==
@@ -64,12 +78,21 @@
 
 namespace {
 
-CPDF_Dictionary* LoadFontDesc(CPDF_Document* pDoc,
-                              const ByteString& font_name,
-                              CFX_Font* pFont,
-                              pdfium::span<const uint8_t> span,
-                              int font_type) {
-  CPDF_Dictionary* pFontDesc = pDoc->NewIndirect<CPDF_Dictionary>();
+ByteString BaseFontNameForType(CFX_Font* pFont, int font_type) {
+  ByteString name = font_type == FPDF_FONT_TYPE1 ? pFont->GetPsName()
+                                                 : pFont->GetBaseFontName();
+  if (!name.IsEmpty())
+    return name;
+
+  return CFX_Font::kUntitledFontName;
+}
+
+RetainPtr<CPDF_Dictionary> LoadFontDesc(CPDF_Document* pDoc,
+                                        const ByteString& font_name,
+                                        CFX_Font* pFont,
+                                        pdfium::span<const uint8_t> span,
+                                        int font_type) {
+  auto pFontDesc = pDoc->NewIndirect<CPDF_Dictionary>();
   pFontDesc->SetNewFor<CPDF_Name>("Type", "FontDescriptor");
   pFontDesc->SetNewFor<CPDF_Name>("FontName", font_name);
   int flags = 0;
@@ -86,8 +109,7 @@
   flags |= FXFONT_NONSYMBOLIC;
 
   pFontDesc->SetNewFor<CPDF_Number>("Flags", flags);
-  FX_RECT bbox;
-  pFont->GetBBox(&bbox);
+  FX_RECT bbox = pFont->GetBBox().value_or(FX_RECT());
   pFontDesc->SetRectFor("FontBBox", CFX_FloatRect(bbox));
 
   // TODO(npm): calculate italic angle correctly
@@ -100,12 +122,12 @@
   pFontDesc->SetNewFor<CPDF_Number>("CapHeight", pFont->GetAscent());
   pFontDesc->SetNewFor<CPDF_Number>("StemV", pFont->IsBold() ? 120 : 70);
 
-  CPDF_Stream* pStream = pDoc->NewIndirect<CPDF_Stream>();
+  auto pStream = pDoc->NewIndirect<CPDF_Stream>();
   pStream->SetData(span);
   // TODO(npm): Lengths for Type1 fonts.
   if (font_type == FPDF_FONT_TRUETYPE) {
-    pStream->GetDict()->SetNewFor<CPDF_Number>("Length1",
-                                               static_cast<int>(span.size()));
+    pStream->GetMutableDict()->SetNewFor<CPDF_Number>(
+        "Length1", static_cast<int>(span.size()));
   }
   ByteString fontFile = font_type == FPDF_FONT_TYPE1 ? "FontFile" : "FontFile2";
   pFontDesc->SetNewFor<CPDF_Reference>(fontFile, pDoc, pStream->GetObjNum());
@@ -133,8 +155,8 @@
     "end\n"
     "end\n";
 
-void AddCharcode(std::ostringstream* pBuffer, uint32_t number) {
-  ASSERT(number <= 0xFFFF);
+void AddCharcode(fxcrt::ostringstream* pBuffer, uint32_t number) {
+  DCHECK(number <= 0xFFFF);
   *pBuffer << "<";
   char ans[4];
   FXSYS_IntToFourHexChars(number, ans);
@@ -145,9 +167,10 @@
 
 // PDF spec 1.7 Section 5.9.2: "Unicode character sequences as expressed in
 // UTF-16BE encoding." See https://en.wikipedia.org/wiki/UTF-16#Description
-void AddUnicode(std::ostringstream* pBuffer, uint32_t unicode) {
-  if (unicode >= 0xD800 && unicode <= 0xDFFF)
+void AddUnicode(fxcrt::ostringstream* pBuffer, uint32_t unicode) {
+  if (pdfium::IsHighSurrogate(unicode) || pdfium::IsLowSurrogate(unicode)) {
     unicode = 0;
+  }
 
   char ans[8];
   *pBuffer << "<";
@@ -158,8 +181,9 @@
 }
 
 // Loads the charcode to unicode mapping into a stream
-CPDF_Stream* LoadUnicode(CPDF_Document* pDoc,
-                         const std::map<uint32_t, uint32_t>& to_unicode) {
+RetainPtr<CPDF_Stream> LoadUnicode(
+    CPDF_Document* pDoc,
+    const std::multimap<uint32_t, uint32_t>& to_unicode) {
   // A map charcode->unicode
   std::map<uint32_t, uint32_t> char_to_uni;
   // A map <char_start, char_end> to vector v of unicode characters of size (end
@@ -205,7 +229,7 @@
         unicodes.push_back(iter->second);
         next_it = std::next(iter);
       }
-      ASSERT(iter->first - firstCharcode + 1 == unicodes.size());
+      DCHECK_EQ(iter->first - firstCharcode + 1, unicodes.size());
       map_range_vector[std::make_pair(firstCharcode, iter->first)] = unicodes;
       continue;
     }
@@ -222,7 +246,7 @@
     }
     map_range[std::make_pair(firstCharcode, curCharcode)] = firstUnicode;
   }
-  std::ostringstream buffer;
+  fxcrt::ostringstream buffer;
   buffer << ToUnicodeStart;
   // Add maps to buffer
   buffer << static_cast<uint32_t>(char_to_uni.size()) << " beginbfchar\n";
@@ -262,7 +286,7 @@
   buffer << "endbfrange\n";
   buffer << ToUnicodeEnd;
   // TODO(npm): Encrypt / Compress?
-  CPDF_Stream* stream = pDoc->NewIndirect<CPDF_Stream>();
+  auto stream = pDoc->NewIndirect<CPDF_Stream>();
   stream->SetDataFromStringstream(&buffer);
   return stream;
 }
@@ -271,67 +295,60 @@
                                     std::unique_ptr<CFX_Font> pFont,
                                     pdfium::span<const uint8_t> span,
                                     int font_type) {
-  CPDF_Dictionary* pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
+  auto pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
   pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
   pFontDict->SetNewFor<CPDF_Name>(
       "Subtype", font_type == FPDF_FONT_TYPE1 ? "Type1" : "TrueType");
-  ByteString name = pFont->GetBaseFontName(font_type == FPDF_FONT_TYPE1);
-  if (name.IsEmpty())
-    name = CFX_Font::kUntitledFontName;
+  ByteString name = BaseFontNameForType(pFont.get(), font_type);
   pFontDict->SetNewFor<CPDF_Name>("BaseFont", name);
 
   uint32_t dwGlyphIndex;
-  uint32_t dwCurrentChar =
-      FT_Get_First_Char(pFont->GetFaceRec(), &dwGlyphIndex);
+  uint32_t dwCurrentChar = static_cast<uint32_t>(
+      FT_Get_First_Char(pFont->GetFaceRec(), &dwGlyphIndex));
   static constexpr uint32_t kMaxSimpleFontChar = 0xFF;
   if (dwCurrentChar > kMaxSimpleFontChar || dwGlyphIndex == 0)
     return nullptr;
   pFontDict->SetNewFor<CPDF_Number>("FirstChar",
                                     static_cast<int>(dwCurrentChar));
-  CPDF_Array* widthsArray = pDoc->NewIndirect<CPDF_Array>();
+  auto widthsArray = pDoc->NewIndirect<CPDF_Array>();
   while (true) {
-    uint32_t width =
-        std::min(pFont->GetGlyphWidth(dwGlyphIndex),
-                 static_cast<uint32_t>(std::numeric_limits<int>::max()));
-    widthsArray->AddNew<CPDF_Number>(static_cast<int>(width));
-    uint32_t nextChar =
-        FT_Get_Next_Char(pFont->GetFaceRec(), dwCurrentChar, &dwGlyphIndex);
+    widthsArray->AppendNew<CPDF_Number>(pFont->GetGlyphWidth(dwGlyphIndex));
+    uint32_t nextChar = static_cast<uint32_t>(
+        FT_Get_Next_Char(pFont->GetFaceRec(), dwCurrentChar, &dwGlyphIndex));
     // Simple fonts have 1-byte charcodes only.
     if (nextChar > kMaxSimpleFontChar || dwGlyphIndex == 0)
       break;
     for (uint32_t i = dwCurrentChar + 1; i < nextChar; i++)
-      widthsArray->AddNew<CPDF_Number>(0);
+      widthsArray->AppendNew<CPDF_Number>(0);
     dwCurrentChar = nextChar;
   }
   pFontDict->SetNewFor<CPDF_Number>("LastChar",
                                     static_cast<int>(dwCurrentChar));
   pFontDict->SetNewFor<CPDF_Reference>("Widths", pDoc,
                                        widthsArray->GetObjNum());
-  CPDF_Dictionary* pFontDesc =
+  RetainPtr<CPDF_Dictionary> pFontDesc =
       LoadFontDesc(pDoc, name, pFont.get(), span, font_type);
 
   pFontDict->SetNewFor<CPDF_Reference>("FontDescriptor", pDoc,
                                        pFontDesc->GetObjNum());
-  return CPDF_DocPageData::FromDocument(pDoc)->GetFont(pFontDict);
+  return CPDF_DocPageData::FromDocument(pDoc)->GetFont(std::move(pFontDict));
 }
 
 RetainPtr<CPDF_Font> LoadCompositeFont(CPDF_Document* pDoc,
                                        std::unique_ptr<CFX_Font> pFont,
                                        pdfium::span<const uint8_t> span,
                                        int font_type) {
-  CPDF_Dictionary* pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
+  auto pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
   pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
   pFontDict->SetNewFor<CPDF_Name>("Subtype", "Type0");
   // TODO(npm): Get the correct encoding, if it's not identity.
   ByteString encoding = "Identity-H";
   pFontDict->SetNewFor<CPDF_Name>("Encoding", encoding);
-  ByteString name = pFont->GetBaseFontName(font_type == FPDF_FONT_TYPE1);
-  if (name.IsEmpty())
-    name = CFX_Font::kUntitledFontName;
+  ByteString name = BaseFontNameForType(pFont.get(), font_type);
   pFontDict->SetNewFor<CPDF_Name>(
       "BaseFont", font_type == FPDF_FONT_TYPE1 ? name + "-" + encoding : name);
 
-  CPDF_Dictionary* pCIDFont = pDoc->NewIndirect<CPDF_Dictionary>();
+  auto pCIDFont = pDoc->NewIndirect<CPDF_Dictionary>();
   pCIDFont->SetNewFor<CPDF_Name>("Type", "Font");
   pCIDFont->SetNewFor<CPDF_Name>("Subtype", font_type == FPDF_FONT_TYPE1
                                                 ? "CIDFontType0"
@@ -340,50 +357,52 @@
 
   // TODO(npm): Maybe use FT_Get_CID_Registry_Ordering_Supplement to get the
   // CIDSystemInfo
-  CPDF_Dictionary* pCIDSystemInfo = pDoc->NewIndirect<CPDF_Dictionary>();
+  auto pCIDSystemInfo = pDoc->NewIndirect<CPDF_Dictionary>();
   pCIDSystemInfo->SetNewFor<CPDF_String>("Registry", "Adobe", false);
   pCIDSystemInfo->SetNewFor<CPDF_String>("Ordering", "Identity", false);
   pCIDSystemInfo->SetNewFor<CPDF_Number>("Supplement", 0);
   pCIDFont->SetNewFor<CPDF_Reference>("CIDSystemInfo", pDoc,
                                       pCIDSystemInfo->GetObjNum());
 
-  CPDF_Dictionary* pFontDesc =
+  RetainPtr<CPDF_Dictionary> pFontDesc =
       LoadFontDesc(pDoc, name, pFont.get(), span, font_type);
   pCIDFont->SetNewFor<CPDF_Reference>("FontDescriptor", pDoc,
                                       pFontDesc->GetObjNum());
 
   uint32_t dwGlyphIndex;
-  uint32_t dwCurrentChar =
-      FT_Get_First_Char(pFont->GetFaceRec(), &dwGlyphIndex);
-  static constexpr uint32_t kMaxUnicode = 0x10FFFF;
+  uint32_t dwCurrentChar = static_cast<uint32_t>(
+      FT_Get_First_Char(pFont->GetFaceRec(), &dwGlyphIndex));
   // If it doesn't have a single char, just fail
-  if (dwGlyphIndex == 0 || dwCurrentChar > kMaxUnicode)
+  if (dwGlyphIndex == 0 ||
+      dwCurrentChar > pdfium::kMaximumSupplementaryCodePoint) {
     return nullptr;
+  }
 
-  std::map<uint32_t, uint32_t> to_unicode;
+  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::ContainsKey(widths, dwGlyphIndex))
+    if (!pdfium::Contains(widths, dwGlyphIndex))
       widths[dwGlyphIndex] = pFont->GetGlyphWidth(dwGlyphIndex);
-    to_unicode[dwGlyphIndex] = dwCurrentChar;
-    dwCurrentChar =
-        FT_Get_Next_Char(pFont->GetFaceRec(), dwCurrentChar, &dwGlyphIndex);
+    to_unicode.emplace(dwGlyphIndex, dwCurrentChar);
+    dwCurrentChar = static_cast<uint32_t>(
+        FT_Get_Next_Char(pFont->GetFaceRec(), dwCurrentChar, &dwGlyphIndex));
     if (dwGlyphIndex == 0)
       break;
   }
-  CPDF_Array* widthsArray = pDoc->NewIndirect<CPDF_Array>();
+  auto widthsArray = pDoc->NewIndirect<CPDF_Array>();
   for (auto it = widths.begin(); it != widths.end(); ++it) {
     int ch = it->first;
     int w = it->second;
     if (std::next(it) == widths.end()) {
       // Only one char left, use format c [w]
       auto oneW = pdfium::MakeRetain<CPDF_Array>();
-      oneW->AddNew<CPDF_Number>(w);
-      widthsArray->AddNew<CPDF_Number>(ch);
-      widthsArray->Add(oneW);
+      oneW->AppendNew<CPDF_Number>(w);
+      widthsArray->AppendNew<CPDF_Number>(ch);
+      widthsArray->Append(oneW);
       break;
     }
     ++it;
@@ -392,7 +411,7 @@
     if (next_ch == ch + 1 && next_w == w) {
       // The array can have a group c_first c_last w: all CIDs in the range from
       // c_first to c_last will have width w
-      widthsArray->AddNew<CPDF_Number>(ch);
+      widthsArray->AppendNew<CPDF_Number>(ch);
       ch = next_ch;
       while (true) {
         auto next_it = std::next(it);
@@ -403,33 +422,33 @@
         ++it;
         ch = it->first;
       }
-      widthsArray->AddNew<CPDF_Number>(ch);
-      widthsArray->AddNew<CPDF_Number>(w);
+      widthsArray->AppendNew<CPDF_Number>(ch);
+      widthsArray->AppendNew<CPDF_Number>(w);
       continue;
     }
     // Otherwise we can have a group of the form c [w1 w2 ...]: c has width
     // w1, c+1 has width w2, etc.
-    widthsArray->AddNew<CPDF_Number>(ch);
+    widthsArray->AppendNew<CPDF_Number>(ch);
     auto curWidthArray = pdfium::MakeRetain<CPDF_Array>();
-    curWidthArray->AddNew<CPDF_Number>(w);
-    curWidthArray->AddNew<CPDF_Number>(next_w);
+    curWidthArray->AppendNew<CPDF_Number>(w);
+    curWidthArray->AppendNew<CPDF_Number>(next_w);
     while (true) {
       auto next_it = std::next(it);
       if (next_it == widths.end() || next_it->first != it->first + 1)
         break;
       ++it;
-      curWidthArray->AddNew<CPDF_Number>(static_cast<int>(it->second));
+      curWidthArray->AppendNew<CPDF_Number>(static_cast<int>(it->second));
     }
-    widthsArray->Add(curWidthArray);
+    widthsArray->Append(curWidthArray);
   }
   pCIDFont->SetNewFor<CPDF_Reference>("W", pDoc, widthsArray->GetObjNum());
 
   // TODO(npm): Support vertical writing
 
-  auto* pDescendant = pFontDict->SetNewFor<CPDF_Array>("DescendantFonts");
-  pDescendant->AddNew<CPDF_Reference>(pDoc, pCIDFont->GetObjNum());
+  auto pDescendant = pFontDict->SetNewFor<CPDF_Array>("DescendantFonts");
+  pDescendant->AppendNew<CPDF_Reference>(pDoc, pCIDFont->GetObjNum());
 
-  CPDF_Stream* toUnicodeStream = LoadUnicode(pDoc, to_unicode);
+  RetainPtr<CPDF_Stream> toUnicodeStream = LoadUnicode(pDoc, to_unicode);
   pFontDict->SetNewFor<CPDF_Reference>("ToUnicode", pDoc,
                                        toUnicodeStream->GetObjNum());
   return CPDF_DocPageData::FromDocument(pDoc)->GetFont(pFontDict);
@@ -440,6 +459,13 @@
   return obj ? obj->AsText() : nullptr;
 }
 
+FPDF_GLYPHPATH FPDFGlyphPathFromCFXPath(const CFX_Path* path) {
+  return reinterpret_cast<FPDF_GLYPHPATH>(path);
+}
+const CFX_Path* CFXPathFromFPDFGlyphPath(FPDF_GLYPHPATH path) {
+  return reinterpret_cast<const CFX_Path*>(path);
+}
+
 }  // namespace
 
 FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
@@ -455,8 +481,8 @@
   if (!pFont)
     return nullptr;
 
-  auto pTextObj = pdfium::MakeUnique<CPDF_TextObject>();
-  pTextObj->m_TextState.SetFont(pFont);
+  auto pTextObj = std::make_unique<CPDF_TextObject>();
+  pTextObj->m_TextState.SetFont(std::move(pFont));
   pTextObj->m_TextState.SetFontSize(font_size);
   pTextObj->DefaultStates();
 
@@ -480,6 +506,27 @@
   return true;
 }
 
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFText_SetCharcodes(FPDF_PAGEOBJECT text_object,
+                      const uint32_t* charcodes,
+                      size_t count) {
+  CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text_object);
+  if (!pTextObj)
+    return false;
+
+  if (!charcodes && count)
+    return false;
+
+  ByteString byte_text;
+  if (charcodes) {
+    for (size_t i = 0; i < count; ++i) {
+      pTextObj->GetFont()->AppendChar(&byte_text, charcodes[i]);
+    }
+  }
+  pTextObj->SetText(byte_text);
+  return true;
+}
+
 FPDF_EXPORT FPDF_FONT FPDF_CALLCONV FPDFText_LoadFont(FPDF_DOCUMENT document,
                                                       const uint8_t* data,
                                                       uint32_t size,
@@ -492,12 +539,12 @@
   }
 
   auto span = pdfium::make_span(data, size);
-  auto pFont = pdfium::MakeUnique<CFX_Font>();
+  auto pFont = std::make_unique<CFX_Font>();
 
   // TODO(npm): Maybe use FT_Get_X11_Font_Format to check format? Otherwise, we
   // are allowing giving any font that can be loaded on freetype and setting it
   // as any font type.
-  if (!pFont->LoadEmbedded(span, false))
+  if (!pFont->LoadEmbedded(span, /*force_vertical=*/false, /*object_tag=*/0))
     return nullptr;
 
   // Caller takes ownership.
@@ -517,46 +564,23 @@
       CPDF_Font::GetStockFont(pDoc, ByteStringView(font)).Leak());
 }
 
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFTextObj_GetMatrix(FPDF_PAGEOBJECT text,
-                                                          FS_MATRIX* matrix) {
-  if (!matrix)
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFTextObj_GetFontSize(FPDF_PAGEOBJECT text, float* size) {
+  if (!size)
     return false;
 
   CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text);
   if (!pTextObj)
     return false;
 
-  *matrix = FSMatrixFromCFXMatrix(pTextObj->GetTextMatrix());
+  *size = pTextObj->GetFontSize();
   return true;
 }
 
-FPDF_EXPORT float FPDF_CALLCONV FPDFTextObj_GetFontSize(FPDF_PAGEOBJECT text) {
-  CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text);
-  return pTextObj ? pTextObj->GetFontSize() : 0.0f;
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDFTextObj_GetFontName(FPDF_PAGEOBJECT text,
-                        void* buffer,
-                        unsigned long length) {
-  CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text);
-  if (!pTextObj)
-    return 0;
-
-  RetainPtr<CPDF_Font> pPdfFont = pTextObj->GetFont();
-  CFX_Font* pFont = pPdfFont->GetFont();
-  ByteString name = pFont->GetFamilyName();
-  unsigned long dwStringLen = name.GetLength() + 1;
-  if (buffer && length >= dwStringLen)
-    memcpy(buffer, name.c_str(), dwStringLen);
-
-  return dwStringLen;
-}
-
 FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDFTextObj_GetText(FPDF_PAGEOBJECT text_object,
                     FPDF_TEXTPAGE text_page,
-                    void* buffer,
+                    FPDF_WCHAR* buffer,
                     unsigned long length) {
   CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text_object);
   if (!pTextObj)
@@ -570,6 +594,70 @@
   return Utf16EncodeMaybeCopyAndReturnLength(text, buffer, length);
 }
 
+FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
+FPDFTextObj_GetRenderedBitmap(FPDF_DOCUMENT document,
+                              FPDF_PAGE page,
+                              FPDF_PAGEOBJECT text_object,
+                              float scale) {
+  CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document);
+  if (!doc)
+    return nullptr;
+
+  CPDF_Page* optional_page = CPDFPageFromFPDFPage(page);
+  if (optional_page && optional_page->GetDocument() != doc)
+    return nullptr;
+
+  CPDF_TextObject* text = CPDFTextObjectFromFPDFPageObject(text_object);
+  if (!text)
+    return nullptr;
+
+  if (scale <= 0)
+    return nullptr;
+
+  const CFX_Matrix scale_matrix(scale, 0, 0, scale, 0, 0);
+  const CFX_FloatRect& text_rect = text->GetRect();
+  const CFX_FloatRect scaled_text_rect = scale_matrix.TransformRect(text_rect);
+
+  // `rect` has to use integer values. Round up as needed.
+  const FX_RECT rect = scaled_text_rect.GetOuterRect();
+  if (rect.IsEmpty())
+    return nullptr;
+
+  auto result_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!result_bitmap->Create(rect.Width(), rect.Height(), FXDIB_Format::kArgb))
+    return nullptr;
+
+  auto render_context = std::make_unique<CPDF_PageRenderContext>();
+  CPDF_PageRenderContext* render_context_ptr = render_context.get();
+  CPDF_Page::RenderContextClearer clearer(optional_page);
+  if (optional_page)
+    optional_page->SetRenderContext(std::move(render_context));
+
+  RetainPtr<CPDF_Dictionary> page_resources =
+      optional_page ? optional_page->GetMutablePageResources() : nullptr;
+
+  auto device = std::make_unique<CFX_DefaultRenderDevice>();
+  CFX_DefaultRenderDevice* device_ptr = device.get();
+  render_context_ptr->m_pDevice = std::move(device);
+  render_context_ptr->m_pContext = std::make_unique<CPDF_RenderContext>(
+      doc, std::move(page_resources), /*pPageCache=*/nullptr);
+
+  device_ptr->Attach(result_bitmap);
+
+  CFX_Matrix device_matrix(rect.Width(), 0, 0, rect.Height(), 0, 0);
+  CPDF_RenderStatus status(render_context_ptr->m_pContext.get(), device_ptr);
+  status.SetDeviceMatrix(device_matrix);
+  status.Initialize(nullptr, nullptr);
+
+  // Need to flip the rendering and also move it to fit within `result_bitmap`.
+  CFX_Matrix render_matrix(1, 0, 0, -1, -text_rect.left, text_rect.top);
+  render_matrix *= scale_matrix;
+  status.RenderSingleObject(text, render_matrix);
+
+  // Caller takes ownership.
+  return FPDFBitmapFromCFXDIBitmap(result_bitmap.Leak());
+}
+
 FPDF_EXPORT void FPDF_CALLCONV FPDFFont_Close(FPDF_FONT font) {
   // Take back ownership from caller and release.
   RetainPtr<CPDF_Font>().Unleak(CPDFFontFromFPDFFont(font));
@@ -584,9 +672,9 @@
   if (!pDoc || !pFont)
     return nullptr;
 
-  auto pTextObj = pdfium::MakeUnique<CPDF_TextObject>();
-  pTextObj->m_TextState.SetFont(
-      CPDF_DocPageData::FromDocument(pDoc)->GetFont(pFont->GetFontDict()));
+  auto pTextObj = std::make_unique<CPDF_TextObject>();
+  pTextObj->m_TextState.SetFont(CPDF_DocPageData::FromDocument(pDoc)->GetFont(
+      pFont->GetMutableFontDict()));
   pTextObj->m_TextState.SetFontSize(font_size);
   pTextObj->DefaultStates();
   return FPDFPageObjectFromCPDFPageObject(pTextObj.release());
@@ -597,7 +685,7 @@
   CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text);
   if (!pTextObj)
     return FPDF_TEXTRENDERMODE_UNKNOWN;
-  return static_cast<FPDF_TEXT_RENDERMODE>(pTextObj->m_TextState.GetTextMode());
+  return static_cast<FPDF_TEXT_RENDERMODE>(pTextObj->GetTextRenderMode());
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -612,7 +700,174 @@
   if (!pTextObj)
     return false;
 
-  pTextObj->m_TextState.SetTextMode(
-      static_cast<TextRenderingMode>(render_mode));
+  pTextObj->SetTextRenderMode(static_cast<TextRenderingMode>(render_mode));
   return true;
 }
+
+FPDF_EXPORT FPDF_FONT FPDF_CALLCONV FPDFTextObj_GetFont(FPDF_PAGEOBJECT text) {
+  CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text);
+  if (!pTextObj)
+    return nullptr;
+
+  // Unretained reference in public API. NOLINTNEXTLINE
+  return FPDFFontFromCPDFFont(pTextObj->GetFont());
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFFont_GetFontName(FPDF_FONT font, char* buffer, unsigned long length) {
+  auto* pFont = CPDFFontFromFPDFFont(font);
+  if (!pFont)
+    return 0;
+
+  CFX_Font* pCfxFont = pFont->GetFont();
+  ByteString name = pCfxFont->GetFamilyName();
+  const unsigned long dwStringLen =
+      pdfium::base::checked_cast<unsigned long>(name.GetLength() + 1);
+  if (buffer && length >= dwStringLen)
+    memcpy(buffer, name.c_str(), dwStringLen);
+
+  return dwStringLen;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetFontData(FPDF_FONT font,
+                                                         uint8_t* buffer,
+                                                         size_t buflen,
+                                                         size_t* out_buflen) {
+  auto* cfont = CPDFFontFromFPDFFont(font);
+  if (!cfont || !out_buflen)
+    return false;
+
+  pdfium::span<uint8_t> data = cfont->GetFont()->GetFontSpan();
+  if (buffer && buflen >= data.size())
+    fxcrt::spancpy(pdfium::make_span(buffer, buflen), data);
+  *out_buflen = data.size();
+  return true;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFFont_GetIsEmbedded(FPDF_FONT font) {
+  auto* cfont = CPDFFontFromFPDFFont(font);
+  if (!cfont)
+    return -1;
+  return cfont->IsEmbedded() ? 1 : 0;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFFont_GetFlags(FPDF_FONT font) {
+  auto* pFont = CPDFFontFromFPDFFont(font);
+  if (!pFont)
+    return -1;
+
+  // Return only flags from ISO 32000-1:2008, table 123.
+  return pFont->GetFontFlags() & 0x7ffff;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetWeight(FPDF_FONT font) {
+  auto* pFont = CPDFFontFromFPDFFont(font);
+  return pFont ? pFont->GetFontWeight() : -1;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetItalicAngle(FPDF_FONT font,
+                                                            int* angle) {
+  auto* pFont = CPDFFontFromFPDFFont(font);
+  if (!pFont || !angle)
+    return false;
+
+  *angle = pFont->GetItalicAngle();
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetAscent(FPDF_FONT font,
+                                                       float font_size,
+                                                       float* ascent) {
+  auto* pFont = CPDFFontFromFPDFFont(font);
+  if (!pFont || !ascent)
+    return false;
+
+  *ascent = pFont->GetTypeAscent() * font_size / 1000.f;
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetDescent(FPDF_FONT font,
+                                                        float font_size,
+                                                        float* descent) {
+  auto* pFont = CPDFFontFromFPDFFont(font);
+  if (!pFont || !descent)
+    return false;
+
+  *descent = pFont->GetTypeDescent() * font_size / 1000.f;
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetGlyphWidth(FPDF_FONT font,
+                                                           uint32_t glyph,
+                                                           float font_size,
+                                                           float* width) {
+  auto* pFont = CPDFFontFromFPDFFont(font);
+  if (!pFont || !width)
+    return false;
+
+  uint32_t charcode = pFont->CharCodeFromUnicode(static_cast<wchar_t>(glyph));
+
+  CPDF_CIDFont* pCIDFont = pFont->AsCIDFont();
+  if (pCIDFont && pCIDFont->IsVertWriting()) {
+    uint16_t cid = pCIDFont->CIDFromCharCode(charcode);
+    *width = pCIDFont->GetVertWidth(cid) * font_size / 1000.f;
+  } else {
+    *width = pFont->GetCharWidthF(charcode) * font_size / 1000.f;
+  }
+
+  return true;
+}
+
+FPDF_EXPORT FPDF_GLYPHPATH FPDF_CALLCONV
+FPDFFont_GetGlyphPath(FPDF_FONT font, uint32_t glyph, float font_size) {
+  auto* pFont = CPDFFontFromFPDFFont(font);
+  if (!pFont)
+    return nullptr;
+
+  if (!pdfium::base::IsValueInRangeForNumericType<wchar_t>(glyph))
+    return nullptr;
+
+  uint32_t charcode = pFont->CharCodeFromUnicode(static_cast<wchar_t>(glyph));
+  std::vector<TextCharPos> pos =
+      GetCharPosList(pdfium::make_span(&charcode, 1),
+                     pdfium::span<const float>(), pFont, font_size);
+  if (pos.empty())
+    return nullptr;
+
+  CFX_Font* pCfxFont;
+  if (pos[0].m_FallbackFontPosition == -1) {
+    pCfxFont = pFont->GetFont();
+    DCHECK(pCfxFont);  // Never null.
+  } else {
+    pCfxFont = pFont->GetFontFallback(pos[0].m_FallbackFontPosition);
+    if (!pCfxFont)
+      return nullptr;
+  }
+
+  const CFX_Path* pPath =
+      pCfxFont->LoadGlyphPath(pos[0].m_GlyphIndex, pos[0].m_FontCharWidth);
+
+  return FPDFGlyphPathFromCFXPath(pPath);
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFGlyphPath_CountGlyphSegments(FPDF_GLYPHPATH glyphpath) {
+  auto* pPath = CFXPathFromFPDFGlyphPath(glyphpath);
+  if (!pPath)
+    return -1;
+
+  return fxcrt::CollectionSize<int>(pPath->GetPoints());
+}
+
+FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
+FPDFGlyphPath_GetGlyphPathSegment(FPDF_GLYPHPATH glyphpath, int index) {
+  auto* pPath = CFXPathFromFPDFGlyphPath(glyphpath);
+  if (!pPath)
+    return nullptr;
+
+  pdfium::span<const CFX_Path::Point> points = pPath->GetPoints();
+  if (!fxcrt::IndexInBounds(points, index))
+    return nullptr;
+
+  return FPDFPathSegmentFromFXPathPoint(&points[index]);
+}
diff --git a/fpdfsdk/fpdf_ext.cpp b/fpdfsdk/fpdf_ext.cpp
index dc928f6..44f329f 100644
--- a/fpdfsdk/fpdf_ext.cpp
+++ b/fpdfsdk/fpdf_ext.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,6 @@
 #include "core/fpdfdoc/cpdf_metadata.h"
 #include "core/fxcrt/fx_extension.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
-#include "third_party/base/ptr_util.h"
 
 static_assert(static_cast<int>(UnsupportedFeature::kDocumentXFAForm) ==
                   FPDF_UNSP_DOC_XFAFORM,
@@ -91,7 +90,7 @@
   if (!pRoot)
     return PAGEMODE_UNKNOWN;
 
-  const CPDF_Object* pName = pRoot->GetObjectFor("PageMode");
+  RetainPtr<const CPDF_Object> pName = pRoot->GetObjectFor("PageMode");
   if (!pName)
     return PAGEMODE_USENONE;
 
diff --git a/fpdfsdk/fpdf_ext_embeddertest.cpp b/fpdfsdk/fpdf_ext_embeddertest.cpp
index f3ae06e..e350675 100644
--- a/fpdfsdk/fpdf_ext_embeddertest.cpp
+++ b/fpdfsdk/fpdf_ext_embeddertest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,11 +14,11 @@
 }
 
 TEST_F(FPDFExtEmbedderTest, PageModeUseNone) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   EXPECT_EQ(PAGEMODE_USENONE, FPDFDoc_GetPageMode(document()));
 }
 
 TEST_F(FPDFExtEmbedderTest, PageModeUseOutlines) {
-  EXPECT_TRUE(OpenDocument("use_outlines.pdf"));
+  ASSERT_TRUE(OpenDocument("use_outlines.pdf"));
   EXPECT_EQ(PAGEMODE_USEOUTLINES, FPDFDoc_GetPageMode(document()));
 }
diff --git a/fpdfsdk/fpdf_flatten.cpp b/fpdfsdk/fpdf_flatten.cpp
index 424e1b4..c62a4ba 100644
--- a/fpdfsdk/fpdf_flatten.cpp
+++ b/fpdfsdk/fpdf_flatten.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,10 @@
 
 #include "public/fpdf_flatten.h"
 
+#include <limits.h>
+
 #include <algorithm>
-#include <memory>
+#include <sstream>
 #include <utility>
 #include <vector>
 
@@ -25,8 +27,11 @@
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fpdfdoc/cpdf_annot.h"
+#include "core/fxcrt/fx_string_wrappers.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
+#include "third_party/base/notreached.h"
 
 enum FPDF_TYPE { MAX, MIN };
 enum FPDF_VALUE { TOP, LEFT, RIGHT, BOTTOM };
@@ -49,7 +54,7 @@
 }
 
 void GetContentsRect(CPDF_Document* pDoc,
-                     CPDF_Dictionary* pDict,
+                     RetainPtr<CPDF_Dictionary> pDict,
                      std::vector<CFX_FloatRect>* pRectArray) {
   auto pPDFPage = pdfium::MakeRetain<CPDF_Page>(pDoc, pDict);
   pPDFPage->ParseContent();
@@ -61,7 +66,7 @@
   }
 }
 
-void ParserStream(CPDF_Dictionary* pPageDic,
+void ParserStream(const CPDF_Dictionary* pPageDic,
                   CPDF_Dictionary* pStream,
                   std::vector<CFX_FloatRect>* pRectArray,
                   std::vector<CPDF_Dictionary*>* pObjectArray) {
@@ -80,7 +85,7 @@
 }
 
 int ParserAnnots(CPDF_Document* pSourceDoc,
-                 CPDF_Dictionary* pPageDic,
+                 RetainPtr<CPDF_Dictionary> pPageDic,
                  std::vector<CFX_FloatRect>* pRectArray,
                  std::vector<CPDF_Dictionary*>* pObjectArray,
                  int nUsage) {
@@ -88,18 +93,19 @@
     return FLATTEN_FAIL;
 
   GetContentsRect(pSourceDoc, pPageDic, pRectArray);
-  CPDF_Array* pAnnots = pPageDic->GetArrayFor("Annots");
+  RetainPtr<const CPDF_Array> pAnnots = pPageDic->GetArrayFor("Annots");
   if (!pAnnots)
     return FLATTEN_NOTHINGTODO;
 
   CPDF_ArrayLocker locker(pAnnots);
   for (const auto& pAnnot : locker) {
-    CPDF_Dictionary* pAnnotDict = ToDictionary(pAnnot->GetDirect());
+    RetainPtr<CPDF_Dictionary> pAnnotDict =
+        ToDictionary(pAnnot->GetMutableDirect());
     if (!pAnnotDict)
       continue;
 
     ByteString sSubtype =
-        pAnnotDict->GetStringFor(pdfium::annotation::kSubtype);
+        pAnnotDict->GetByteStringFor(pdfium::annotation::kSubtype);
     if (sSubtype == "Popup")
       continue;
 
@@ -113,7 +119,7 @@
     else
       bParseStream = !!(nAnnotFlag & pdfium::annotation_flags::kPrint);
     if (bParseStream)
-      ParserStream(pPageDic, pAnnotDict, pRectArray, pObjectArray);
+      ParserStream(pPageDic.Get(), pAnnotDict.Get(), pRectArray, pObjectArray);
   }
   return FLATTEN_SUCCESS;
 }
@@ -143,9 +149,6 @@
       for (size_t i = 0; i < nRects; i++)
         pArray[i] = array[i].bottom;
       break;
-    default:
-      NOTREACHED();
-      return 0.0f;
   }
 
   float fRet = pArray[0];
@@ -174,27 +177,27 @@
   return "q 1 0 0 1 0 0 cm /" + key + " Do Q";
 }
 
-CPDF_Object* NewIndirectContentsStream(CPDF_Document* pDocument,
-                                       const ByteString& contents) {
-  CPDF_Stream* pNewContents = pDocument->NewIndirect<CPDF_Stream>(
-      nullptr, 0, pDocument->New<CPDF_Dictionary>());
+RetainPtr<CPDF_Reference> NewIndirectContentsStreamReference(
+    CPDF_Document* pDocument,
+    const ByteString& contents) {
+  auto pNewContents =
+      pDocument->NewIndirect<CPDF_Stream>(pDocument->New<CPDF_Dictionary>());
   pNewContents->SetData(contents.raw_span());
-  return pNewContents;
+  return pNewContents->MakeReference(pDocument);
 }
 
 void SetPageContents(const ByteString& key,
                      CPDF_Dictionary* pPage,
                      CPDF_Document* pDocument) {
-  CPDF_Array* pContentsArray =
-      pPage->GetArrayFor(pdfium::page_object::kContents);
-  CPDF_Stream* pContentsStream =
-      pPage->GetStreamFor(pdfium::page_object::kContents);
+  RetainPtr<CPDF_Array> pContentsArray =
+      pPage->GetMutableArrayFor(pdfium::page_object::kContents);
+  RetainPtr<CPDF_Stream> pContentsStream =
+      pPage->GetMutableStreamFor(pdfium::page_object::kContents);
   if (!pContentsStream && !pContentsArray) {
     if (!key.IsEmpty()) {
-      pPage->SetFor(
-          pdfium::page_object::kContents,
-          NewIndirectContentsStream(pDocument, GenerateFlattenedContent(key))
-              ->MakeReference(pDocument));
+      pPage->SetFor(pdfium::page_object::kContents,
+                    NewIndirectContentsStreamReference(
+                        pDocument, GenerateFlattenedContent(key)));
     }
     return;
   }
@@ -202,9 +205,8 @@
   pPage->ConvertToIndirectObjectFor(pdfium::page_object::kContents, pDocument);
   if (pContentsArray) {
     pContentsArray->InsertAt(
-        0, NewIndirectContentsStream(pDocument, "q")->MakeReference(pDocument));
-    pContentsArray->Add(
-        NewIndirectContentsStream(pDocument, "Q")->MakeReference(pDocument));
+        0, NewIndirectContentsStreamReference(pDocument, "q"));
+    pContentsArray->Append(NewIndirectContentsStreamReference(pDocument, "Q"));
   } else {
     ByteString sStream = "q\n";
     {
@@ -215,15 +217,14 @@
     }
     pContentsStream->SetDataAndRemoveFilter(sStream.raw_span());
     pContentsArray = pDocument->NewIndirect<CPDF_Array>();
-    pContentsArray->AddNew<CPDF_Reference>(pDocument,
-                                           pContentsStream->GetObjNum());
+    pContentsArray->AppendNew<CPDF_Reference>(pDocument,
+                                              pContentsStream->GetObjNum());
     pPage->SetNewFor<CPDF_Reference>(pdfium::page_object::kContents, pDocument,
                                      pContentsArray->GetObjNum());
   }
   if (!key.IsEmpty()) {
-    pContentsArray->Add(
-        NewIndirectContentsStream(pDocument, GenerateFlattenedContent(key))
-            ->MakeReference(pDocument));
+    pContentsArray->Append(NewIndirectContentsStreamReference(
+        pDocument, GenerateFlattenedContent(key)));
   }
 }
 
@@ -252,7 +253,7 @@
     return FLATTEN_FAIL;
 
   CPDF_Document* pDocument = pPage->GetDocument();
-  CPDF_Dictionary* pPageDict = pPage->GetDict();
+  RetainPtr<CPDF_Dictionary> pPageDict = pPage->GetMutableDict();
   if (!pDocument)
     return FLATTEN_FAIL;
 
@@ -269,12 +270,15 @@
   if (pPageDict->KeyExist(pdfium::page_object::kCropBox))
     rcOriginalMB = pPageDict->GetRectFor(pdfium::page_object::kCropBox);
 
+  rcOriginalMB.Normalize();
   if (rcOriginalMB.IsEmpty())
     rcOriginalMB = CFX_FloatRect(0.0f, 0.0f, 612.0f, 792.0f);
 
   CFX_FloatRect rcOriginalCB;
-  if (pPageDict->KeyExist(pdfium::page_object::kCropBox))
+  if (pPageDict->KeyExist(pdfium::page_object::kCropBox)) {
     rcOriginalCB = pPageDict->GetRectFor(pdfium::page_object::kCropBox);
+    rcOriginalCB.Normalize();
+  }
   if (rcOriginalCB.IsEmpty())
     rcOriginalCB = rcOriginalMB;
 
@@ -286,19 +290,11 @@
   pPageDict->SetRectFor(pdfium::page_object::kMediaBox, rcOriginalMB);
   pPageDict->SetRectFor(pdfium::page_object::kCropBox, rcOriginalCB);
 
-  CPDF_Dictionary* pRes =
-      pPageDict->GetDictFor(pdfium::page_object::kResources);
-  if (!pRes) {
-    pRes =
-        pPageDict->SetNewFor<CPDF_Dictionary>(pdfium::page_object::kResources);
-  }
-
-  CPDF_Stream* pNewXObject = pDocument->NewIndirect<CPDF_Stream>(
-      nullptr, 0, pDocument->New<CPDF_Dictionary>());
-
-  CPDF_Dictionary* pPageXObject = pRes->GetDictFor("XObject");
-  if (!pPageXObject)
-    pPageXObject = pRes->SetNewFor<CPDF_Dictionary>("XObject");
+  RetainPtr<CPDF_Dictionary> pRes =
+      pPageDict->GetOrCreateDictFor(pdfium::page_object::kResources);
+  auto pNewXObject =
+      pDocument->NewIndirect<CPDF_Stream>(pDocument->New<CPDF_Dictionary>());
+  RetainPtr<CPDF_Dictionary> pPageXObject = pRes->GetOrCreateDictFor("XObject");
 
   ByteString key;
   if (!ObjectArray.empty()) {
@@ -313,14 +309,14 @@
     }
   }
 
-  SetPageContents(key, pPageDict, pDocument);
+  SetPageContents(key, pPageDict.Get(), pDocument);
 
-  CPDF_Dictionary* pNewXORes = nullptr;
+  RetainPtr<CPDF_Dictionary> pNewXORes;
   if (!key.IsEmpty()) {
     pPageXObject->SetNewFor<CPDF_Reference>(key, pDocument,
                                             pNewXObject->GetObjNum());
 
-    CPDF_Dictionary* pNewOXbjectDic = pNewXObject->GetDict();
+    RetainPtr<CPDF_Dictionary> pNewOXbjectDic = pNewXObject->GetMutableDict();
     pNewXORes = pNewOXbjectDic->SetNewFor<CPDF_Dictionary>("Resources");
     pNewOXbjectDic->SetNewFor<CPDF_Name>("Type", "XObject");
     pNewOXbjectDic->SetNewFor<CPDF_Name>("Subtype", "Form");
@@ -336,29 +332,30 @@
     CFX_FloatRect rcAnnot = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
     rcAnnot.Normalize();
 
-    ByteString sAnnotState = pAnnotDict->GetStringFor("AS");
-    CPDF_Dictionary* pAnnotAP = pAnnotDict->GetDictFor(pdfium::annotation::kAP);
+    ByteString sAnnotState = pAnnotDict->GetByteStringFor("AS");
+    RetainPtr<CPDF_Dictionary> pAnnotAP =
+        pAnnotDict->GetMutableDictFor(pdfium::annotation::kAP);
     if (!pAnnotAP)
       continue;
 
-    CPDF_Stream* pAPStream = pAnnotAP->GetStreamFor("N");
+    RetainPtr<CPDF_Stream> pAPStream = pAnnotAP->GetMutableStreamFor("N");
     if (!pAPStream) {
-      CPDF_Dictionary* pAPDict = pAnnotAP->GetDictFor("N");
+      RetainPtr<CPDF_Dictionary> pAPDict = pAnnotAP->GetMutableDictFor("N");
       if (!pAPDict)
         continue;
 
       if (!sAnnotState.IsEmpty()) {
-        pAPStream = pAPDict->GetStreamFor(sAnnotState);
+        pAPStream = pAPDict->GetMutableStreamFor(sAnnotState);
       } else {
         if (pAPDict->size() > 0) {
           CPDF_DictionaryLocker locker(pAPDict);
-          CPDF_Object* pFirstObj = locker.begin()->second.Get();
+          RetainPtr<CPDF_Object> pFirstObj = locker.begin()->second;
           if (pFirstObj) {
             if (pFirstObj->IsReference())
-              pFirstObj = pFirstObj->GetDirect();
+              pFirstObj = pFirstObj->GetMutableDirect();
             if (!pFirstObj->IsStream())
               continue;
-            pAPStream = pFirstObj->AsStream();
+            pAPStream.Reset(pFirstObj->AsMutableStream());
           }
         }
       }
@@ -366,7 +363,7 @@
     if (!pAPStream)
       continue;
 
-    CPDF_Dictionary* pAPDict = pAPStream->GetDict();
+    RetainPtr<const CPDF_Dictionary> pAPDict = pAPStream->GetDict();
     CFX_FloatRect rcStream;
     if (pAPDict->KeyExist("Rect"))
       rcStream = pAPDict->GetRectFor("Rect");
@@ -377,23 +374,20 @@
     if (rcStream.IsEmpty())
       continue;
 
-    CPDF_Object* pObj = pAPStream;
+    RetainPtr<CPDF_Object> pObj = pAPStream;
     if (pObj->IsInline()) {
-      RetainPtr<CPDF_Object> pNew = pObj->Clone();
-      pObj = pNew.Get();
-      pDocument->AddIndirectObject(std::move(pNew));
+      pObj = pObj->Clone();
+      pDocument->AddIndirectObject(pObj);
     }
 
-    CPDF_Dictionary* pObjDict = pObj->GetDict();
+    RetainPtr<CPDF_Dictionary> pObjDict = pObj->GetMutableDict();
     if (pObjDict) {
       pObjDict->SetNewFor<CPDF_Name>("Type", "XObject");
       pObjDict->SetNewFor<CPDF_Name>("Subtype", "Form");
     }
 
-    CPDF_Dictionary* pXObject = pNewXORes->GetDictFor("XObject");
-    if (!pXObject)
-      pXObject = pNewXORes->SetNewFor<CPDF_Dictionary>("XObject");
-
+    RetainPtr<CPDF_Dictionary> pXObject =
+        pNewXORes->GetOrCreateDictFor("XObject");
     ByteString sFormName = ByteString::Format("F%d", i);
     pXObject->SetNewFor<CPDF_Reference>(sFormName, pDocument,
                                         pObj->GetObjNum());
@@ -408,8 +402,8 @@
     CFX_Matrix m = GetMatrix(rcAnnot, rcStream, matrix);
     m.b = 0;
     m.c = 0;
-    std::ostringstream buf;
-    buf << m;
+    fxcrt::ostringstream buf;
+    WriteMatrix(buf, m);
     ByteString str(buf);
     sStream += ByteString::Format("q %s cm /%s Do Q\n", str.c_str(),
                                   sFormName.c_str());
diff --git a/fpdfsdk/fpdf_flatten_embeddertest.cpp b/fpdfsdk/fpdf_flatten_embeddertest.cpp
index 2b244c4..f3305b5 100644
--- a/fpdfsdk/fpdf_flatten_embeddertest.cpp
+++ b/fpdfsdk/fpdf_flatten_embeddertest.cpp
@@ -1,8 +1,9 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "build/build_config.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
 #include "public/fpdf_flatten.h"
 #include "public/fpdfview.h"
 #include "testing/embedder_test.h"
@@ -15,7 +16,7 @@
 }  // namespace
 
 TEST_F(FPDFFlattenEmbedderTest, FlatNothing) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
   EXPECT_EQ(FLATTEN_NOTHINGTODO, FPDFPage_Flatten(page, FLAT_NORMALDISPLAY));
@@ -23,7 +24,7 @@
 }
 
 TEST_F(FPDFFlattenEmbedderTest, FlatNormal) {
-  EXPECT_TRUE(OpenDocument("annotiter.pdf"));
+  ASSERT_TRUE(OpenDocument("annotiter.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
   EXPECT_EQ(FLATTEN_SUCCESS, FPDFPage_Flatten(page, FLAT_NORMALDISPLAY));
@@ -31,34 +32,30 @@
 }
 
 TEST_F(FPDFFlattenEmbedderTest, FlatPrint) {
-  EXPECT_TRUE(OpenDocument("annotiter.pdf"));
+  ASSERT_TRUE(OpenDocument("annotiter.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
   EXPECT_EQ(FLATTEN_SUCCESS, FPDFPage_Flatten(page, FLAT_PRINT));
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_BUG_861842 DISABLED_BUG_861842
+TEST_F(FPDFFlattenEmbedderTest, BUG_861842) {
+  const char* checkbox_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "95fdaa000e81c80892b8d370f77be970";
+#if BUILDFLAG(IS_APPLE)
+    return "6aafcb2d98da222964bcdbf5aa1f4f1f";
 #else
-#define MAYBE_BUG_861842 BUG_861842
+    return "594265790b81df2d93120d33b72a6ada";
 #endif
-TEST_F(FPDFFlattenEmbedderTest, MAYBE_BUG_861842) {
-#if defined(OS_WIN)
-  static const char kCheckboxHash[] = "95fba3cb7bce7e0d3c94279f60984e17";
-#elif defined(OS_MACOSX)
-  static const char kCheckboxHash[] = "0a1d1d63d4452bc26a1c5c547d309655";
-#else
-  static const char kCheckboxHash[] = "594265790b81df2d93120d33b72a6ada";
-#endif
+  }();
 
-  EXPECT_TRUE(OpenDocument("bug_861842.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_861842.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
   ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-  CompareBitmap(bitmap.get(), 100, 120, kCheckboxHash);
+  CompareBitmap(bitmap.get(), 100, 120, checkbox_checksum);
 
   EXPECT_EQ(FLATTEN_SUCCESS, FPDFPage_Flatten(page, FLAT_PRINT));
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
@@ -66,52 +63,85 @@
   UnloadPage(page);
 
   // TODO(crbug.com/861842): This should not render blank.
-  static const char kBlankPageHash[] = "48400809c3862dae64b0cd00d51057a4";
+  static constexpr char kBlankPageHash[] = "48400809c3862dae64b0cd00d51057a4";
   VerifySavedDocument(100, 120, kBlankPageHash);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_BUG_890322 DISABLED_BUG_890322
+TEST_F(FPDFFlattenEmbedderTest, BUG_889099) {
+  const char* page_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "de7119d99f42deab2f4215017bdb16af";
+#if BUILDFLAG(IS_APPLE)
+    return "049ed3f1e21fc72f929af3410c64bc8f";
 #else
-#define MAYBE_BUG_890322 BUG_890322
+    return "3db87245e3f4e37f4cb18654bbe22d97";
 #endif
-TEST_F(FPDFFlattenEmbedderTest, MAYBE_BUG_890322) {
-  static const char md5_hash[] = "6c674642154408e877d88c6c082d67e9";
-  EXPECT_TRUE(OpenDocument("bug_890322.pdf"));
+  }();
+  const char* flattened_page_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "7978c7b3d643a5f0ac0f03ce759c55fe";
+    }
+#if BUILDFLAG(IS_APPLE)
+    return "41debc60cf2a8f74c710ec6082d77b18";
+#else
+    return "0832157462ea70fbbf053e14b1d6457f";
+#endif
+  }();
+
+  ASSERT_TRUE(OpenDocument("bug_889099.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
+  // The original document has a malformed media box; the height is -400.
   ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-  CompareBitmap(bitmap.get(), 200, 200, md5_hash);
+  CompareBitmap(bitmap.get(), 300, 400, page_checksum);
 
   EXPECT_EQ(FLATTEN_SUCCESS, FPDFPage_Flatten(page, FLAT_PRINT));
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
 
   UnloadPage(page);
 
-  VerifySavedDocument(200, 200, md5_hash);
+  VerifySavedDocument(300, 400, flattened_page_checksum);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_BUG_896366 DISABLED_BUG_896366
-#else
-#define MAYBE_BUG_896366 BUG_896366
-#endif
-TEST_F(FPDFFlattenEmbedderTest, MAYBE_BUG_896366) {
-  static const char md5_hash[] = "f71ab085c52c8445ae785eca3ec858b1";
-  EXPECT_TRUE(OpenDocument("bug_896366.pdf"));
+TEST_F(FPDFFlattenEmbedderTest, BUG_890322) {
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "793689536cf64fe792c2f241888c0cf3";
+    return "6c674642154408e877d88c6c082d67e9";
+  }();
+  ASSERT_TRUE(OpenDocument("bug_890322.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
   ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
-  CompareBitmap(bitmap.get(), 612, 792, md5_hash);
+  CompareBitmap(bitmap.get(), 200, 200, checksum);
 
   EXPECT_EQ(FLATTEN_SUCCESS, FPDFPage_Flatten(page, FLAT_PRINT));
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
 
   UnloadPage(page);
 
-  VerifySavedDocument(612, 792, md5_hash);
+  VerifySavedDocument(200, 200, checksum);
+}
+
+TEST_F(FPDFFlattenEmbedderTest, BUG_896366) {
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "c3cccfadc4c5249e6aa0675e511fa4c3";
+    return "f71ab085c52c8445ae785eca3ec858b1";
+  }();
+  ASSERT_TRUE(OpenDocument("bug_896366.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+  CompareBitmap(bitmap.get(), 612, 792, checksum);
+
+  EXPECT_EQ(FLATTEN_SUCCESS, FPDFPage_Flatten(page, FLAT_PRINT));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+  UnloadPage(page);
+
+  VerifySavedDocument(612, 792, checksum);
 }
diff --git a/fpdfsdk/fpdf_formfill.cpp b/fpdfsdk/fpdf_formfill.cpp
index a3f3022..ee72562 100644
--- a/fpdfsdk/fpdf_formfill.cpp
+++ b/fpdfsdk/fpdf_formfill.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,32 +8,37 @@
 
 #include <memory>
 #include <utility>
-#include <vector>
 
+#include "constants/form_fields.h"
+#include "core/fpdfapi/page/cpdf_annotcontext.h"
 #include "core/fpdfapi/page/cpdf_occontext.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fpdfdoc/cpdf_formcontrol.h"
 #include "core/fpdfdoc/cpdf_formfield.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
-#include "fpdfsdk/cpdfsdk_actionhandler.h"
-#include "fpdfsdk/cpdfsdk_baannothandler.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "fpdfsdk/cpdfsdk_annot.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
-#include "fpdfsdk/cpdfsdk_widgethandler.h"
 #include "public/fpdfview.h"
-#include "third_party/base/ptr_util.h"
 
 #ifdef PDF_ENABLE_XFA
 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
-#include "fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h"
+#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");
@@ -168,12 +173,12 @@
 
   CPDFSDK_FormFillEnvironment* pFormFillEnv =
       CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
-  return pFormFillEnv ? pFormFillEnv->GetPageView(pPage, true) : nullptr;
+  return pFormFillEnv ? pFormFillEnv->GetOrCreatePageView(pPage) : nullptr;
 }
 
 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,13 +199,15 @@
   const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y);
   CFX_Matrix matrix = pPage->GetDisplayMatrix(rect, rotate);
 
-  auto pDevice = pdfium::MakeUnique<CFX_DefaultRenderDevice>();
-#ifdef _SKIA_SUPPORT_
-  pDevice->AttachRecorder(static_cast<SkPictureRecorder*>(recorder));
+  auto pDevice = std::make_unique<CFX_DefaultRenderDevice>();
+#if defined(_SKIA_SUPPORT_)
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer() && canvas) {
+    pDevice->AttachCanvas(reinterpret_cast<SkCanvas*>(canvas));
+  }
 #endif
 
   RetainPtr<CFX_DIBitmap> holder(CFXDIBitmapFromFPDFBitmap(bitmap));
-  pDevice->Attach(holder, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false);
+  pDevice->AttachWithRgbByteOrder(holder, !!(flags & FPDF_REVERSE_BYTE_ORDER));
   {
     CFX_RenderDevice::StateRestorer restorer(pDevice.get());
     pDevice->SetClip_Rect(rect);
@@ -214,16 +221,25 @@
 
     options.SetDrawAnnots(flags & FPDF_ANNOT);
     options.SetOCContext(
-        pdfium::MakeRetain<CPDF_OCContext>(pPDFDoc, CPDF_OCContext::View));
+        pdfium::MakeRetain<CPDF_OCContext>(pPDFDoc, CPDF_OCContext::kView));
 
     if (pPageView)
       pPageView->PageView_OnDraw(pDevice.get(), matrix, &options, rect);
   }
+}
 
-#ifdef _SKIA_SUPPORT_PATHS_
-  pDevice->Flush(true);
-  holder->UnPreMultiply();
-#endif
+// Returns true if formfill version is correctly set. See |version| in
+// FPDF_FORMFILLINFO for details regarding correct version.
+bool CheckFormfillVersion(FPDF_FORMFILLINFO* formInfo) {
+  if (!formInfo || formInfo->version < 1 || formInfo->version > 2)
+    return false;
+
+#ifdef PDF_ENABLE_XFA
+  if (formInfo->version != 2)
+    return false;
+#endif  // PDF_ENABLE_XFA
+
+  return true;
 }
 
 }  // namespace
@@ -233,25 +249,25 @@
                              FPDF_PAGE page,
                              double page_x,
                              double page_y) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  const CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
   if (pPage) {
     CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
     if (!pForm)
       return -1;
 
-    CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
-    CPDF_FormControl* pFormCtrl = pPDFForm->GetControlAtPoint(
+    const CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
+    const CPDF_FormControl* pFormCtrl = pPDFForm->GetControlAtPoint(
         pPage,
         CFX_PointF(static_cast<float>(page_x), static_cast<float>(page_y)),
         nullptr);
     if (!pFormCtrl)
       return -1;
-    CPDF_FormField* pFormField = pFormCtrl->GetField();
+    const CPDF_FormField* pFormField = pFormCtrl->GetField();
     return pFormField ? static_cast<int>(pFormField->GetFieldType()) : -1;
   }
 
 #ifdef PDF_ENABLE_XFA
-  CPDFXFA_Page* pXFAPage = ToXFAPage(IPDFPageFromFPDFPage(page));
+  const CPDFXFA_Page* pXFAPage = ToXFAPage(IPDFPageFromFPDFPage(page));
   if (pXFAPage) {
     return pXFAPage->HasFormFieldAtPoint(
         CFX_PointF(static_cast<float>(page_x), static_cast<float>(page_y)));
@@ -285,24 +301,18 @@
 FPDF_EXPORT FPDF_FORMHANDLE FPDF_CALLCONV
 FPDFDOC_InitFormFillEnvironment(FPDF_DOCUMENT document,
                                 FPDF_FORMFILLINFO* formInfo) {
-#ifdef PDF_ENABLE_XFA
-  constexpr int kRequiredVersion = 2;
-#else   // PDF_ENABLE_XFA
-  constexpr int kRequiredVersion = 1;
-#endif  // PDF_ENABLE_XFA
-  if (!formInfo || formInfo->version != kRequiredVersion)
+  if (!CheckFormfillVersion(formInfo))
     return nullptr;
 
   auto* pDocument = CPDFDocumentFromFPDFDocument(document);
   if (!pDocument)
     return nullptr;
 
-  std::unique_ptr<IPDFSDK_AnnotHandler> pXFAHandler;
 #ifdef PDF_ENABLE_XFA
   CPDFXFA_Context* pContext = nullptr;
   if (!formInfo->xfa_disabled) {
     if (!pDocument->GetExtension()) {
-      pDocument->SetExtension(pdfium::MakeUnique<CPDFXFA_Context>(pDocument));
+      pDocument->SetExtension(std::make_unique<CPDFXFA_Context>(pDocument));
     }
 
     // If the CPDFXFA_Context has a FormFillEnvironment already then we've done
@@ -313,15 +323,11 @@
       return FPDFFormHandleFromCPDFSDKFormFillEnvironment(
           pContext->GetFormFillEnv());
     }
-    pXFAHandler = pdfium::MakeUnique<CPDFXFA_WidgetHandler>();
   }
 #endif  // PDF_ENABLE_XFA
 
-  auto pFormFillEnv = pdfium::MakeUnique<CPDFSDK_FormFillEnvironment>(
-      pDocument, formInfo,
-      pdfium::MakeUnique<CPDFSDK_AnnotHandlerMgr>(
-          pdfium::MakeUnique<CPDFSDK_BAAnnotHandler>(),
-          pdfium::MakeUnique<CPDFSDK_WidgetHandler>(), std::move(pXFAHandler)));
+  auto pFormFillEnv =
+      std::make_unique<CPDFSDK_FormFillEnvironment>(pDocument, formInfo);
 
 #ifdef PDF_ENABLE_XFA
   if (pContext)
@@ -363,9 +369,27 @@
                                                      double page_x,
                                                      double page_y) {
   CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
+  return pPageView &&
+         pPageView->OnMouseMove(
+             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier),
+             CFX_PointF(page_x, page_y));
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FORM_OnMouseWheel(FPDF_FORMHANDLE hHandle,
+                  FPDF_PAGE page,
+                  int modifier,
+                  const FS_POINTF* page_coord,
+                  int delta_x,
+                  int delta_y) {
+  if (!page_coord)
     return false;
-  return pPageView->OnMouseMove(CFX_PointF(page_x, page_y), modifier);
+
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  return pPageView &&
+         pPageView->OnMouseWheel(
+             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier),
+             CFXPointFFromFSPointF(*page_coord), CFX_Vector(delta_x, delta_y));
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnFocus(FPDF_FORMHANDLE hHandle,
@@ -374,9 +398,10 @@
                                                  double page_x,
                                                  double page_y) {
   CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
-  return pPageView->OnFocus(CFX_PointF(page_x, page_y), modifier);
+  return pPageView &&
+         pPageView->OnFocus(
+             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier),
+             CFX_PointF(page_x, page_y));
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnLButtonDown(FPDF_FORMHANDLE hHandle,
@@ -384,14 +409,15 @@
                                                        int modifier,
                                                        double page_x,
                                                        double page_y) {
-  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
 #ifdef PDF_ENABLE_CLICK_LOGGING
   fprintf(stderr, "mousedown,left,%d,%d\n", static_cast<int>(round(page_x)),
           static_cast<int>(round(page_y)));
 #endif  // PDF_ENABLE_CLICK_LOGGING
-  return pPageView->OnLButtonDown(CFX_PointF(page_x, page_y), modifier);
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  return pPageView &&
+         pPageView->OnLButtonDown(
+             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier),
+             CFX_PointF(page_x, page_y));
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnLButtonUp(FPDF_FORMHANDLE hHandle,
@@ -399,14 +425,15 @@
                                                      int modifier,
                                                      double page_x,
                                                      double page_y) {
-  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
 #ifdef PDF_ENABLE_CLICK_LOGGING
   fprintf(stderr, "mouseup,left,%d,%d\n", static_cast<int>(round(page_x)),
           static_cast<int>(round(page_y)));
 #endif  // PDF_ENABLE_CLICK_LOGGING
-  return pPageView->OnLButtonUp(CFX_PointF(page_x, page_y), modifier);
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  return pPageView &&
+         pPageView->OnLButtonUp(
+             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier),
+             CFX_PointF(page_x, page_y));
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -415,14 +442,15 @@
                           int modifier,
                           double page_x,
                           double page_y) {
-  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
 #ifdef PDF_ENABLE_CLICK_LOGGING
   fprintf(stderr, "mousedown,doubleleft,%d,%d\n",
           static_cast<int>(round(page_x)), static_cast<int>(round(page_y)));
 #endif  // PDF_ENABLE_CLICK_LOGGING
-  return pPageView->OnLButtonDblClk(CFX_PointF(page_x, page_y), modifier);
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  return pPageView &&
+         pPageView->OnLButtonDblClk(
+             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier),
+             CFX_PointF(page_x, page_y));
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonDown(FPDF_FORMHANDLE hHandle,
@@ -430,14 +458,15 @@
                                                        int modifier,
                                                        double page_x,
                                                        double page_y) {
-  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
 #ifdef PDF_ENABLE_CLICK_LOGGING
   fprintf(stderr, "mousedown,right,%d,%d\n", static_cast<int>(round(page_x)),
           static_cast<int>(round(page_y)));
 #endif  // PDF_ENABLE_CLICK_LOGGING
-  return pPageView->OnRButtonDown(CFX_PointF(page_x, page_y), modifier);
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  return pPageView &&
+         pPageView->OnRButtonDown(
+             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier),
+             CFX_PointF(page_x, page_y));
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonUp(FPDF_FORMHANDLE hHandle,
@@ -445,14 +474,15 @@
                                                      int modifier,
                                                      double page_x,
                                                      double page_y) {
-  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
 #ifdef PDF_ENABLE_CLICK_LOGGING
   fprintf(stderr, "mouseup,right,%d,%d\n", static_cast<int>(round(page_x)),
           static_cast<int>(round(page_y)));
 #endif  // PDF_ENABLE_CLICK_LOGGING
-  return pPageView->OnRButtonUp(CFX_PointF(page_x, page_y), modifier);
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  return pPageView &&
+         pPageView->OnRButtonUp(
+             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier),
+             CFX_PointF(page_x, page_y));
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyDown(FPDF_FORMHANDLE hHandle,
@@ -460,19 +490,17 @@
                                                    int nKeyCode,
                                                    int modifier) {
   CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
-  return pPageView->OnKeyDown(nKeyCode, modifier);
+  return pPageView &&
+         pPageView->OnKeyDown(
+             static_cast<FWL_VKEYCODE>(nKeyCode),
+             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier));
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyUp(FPDF_FORMHANDLE hHandle,
                                                  FPDF_PAGE page,
                                                  int nKeyCode,
                                                  int modifier) {
-  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
-  return pPageView->OnKeyUp(nKeyCode, modifier);
+  return false;
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnChar(FPDF_FORMHANDLE hHandle,
@@ -480,9 +508,9 @@
                                                 int nChar,
                                                 int modifier) {
   CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
-  return pPageView->OnChar(nChar, modifier);
+  return pPageView &&
+         pPageView->OnChar(
+             nChar, Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier));
 }
 
 FPDF_EXPORT unsigned long FPDF_CALLCONV
@@ -511,6 +539,17 @@
                                              buffer, buflen);
 }
 
+FPDF_EXPORT void FPDF_CALLCONV
+FORM_ReplaceAndKeepSelection(FPDF_FORMHANDLE hHandle,
+                             FPDF_PAGE page,
+                             FPDF_WIDESTRING wsText) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return;
+
+  pPageView->ReplaceAndKeepSelection(WideStringFromFPDFWideString(wsText));
+}
+
 FPDF_EXPORT void FPDF_CALLCONV FORM_ReplaceSelection(FPDF_FORMHANDLE hHandle,
                                                      FPDF_PAGE page,
                                                      FPDF_WIDESTRING wsText) {
@@ -521,6 +560,12 @@
   pPageView->ReplaceSelection(WideStringFromFPDFWideString(wsText));
 }
 
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_SelectAllText(FPDF_FORMHANDLE hHandle,
+                                                       FPDF_PAGE page) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  return pPageView && pPageView->SelectAllText();
+}
+
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_CanUndo(FPDF_FORMHANDLE hHandle,
                                                  FPDF_PAGE page) {
   CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
@@ -559,7 +604,76 @@
       CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
   if (!pFormFillEnv)
     return false;
-  return pFormFillEnv->KillFocusAnnot(0);
+  return pFormFillEnv->KillFocusAnnot({});
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FORM_GetFocusedAnnot(FPDF_FORMHANDLE handle,
+                     int* page_index,
+                     FPDF_ANNOTATION* annot) {
+  if (!page_index || !annot)
+    return false;
+
+  CPDFSDK_FormFillEnvironment* form_fill_env =
+      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(handle);
+  if (!form_fill_env)
+    return false;
+
+  // Set |page_index| and |annot| to default values. This is returned when there
+  // is no focused annotation.
+  *page_index = -1;
+  *annot = nullptr;
+
+  CPDFSDK_Annot* cpdfsdk_annot = form_fill_env->GetFocusAnnot();
+  if (!cpdfsdk_annot)
+    return true;
+
+  // TODO(crbug.com/pdfium/1482): Handle XFA case.
+  if (cpdfsdk_annot->AsXFAWidget())
+    return true;
+
+  CPDFSDK_PageView* page_view = cpdfsdk_annot->GetPageView();
+  if (!page_view->IsValid())
+    return true;
+
+  IPDF_Page* page = cpdfsdk_annot->GetPage();
+  if (!page)
+    return true;
+
+  RetainPtr<CPDF_Dictionary> annot_dict =
+      cpdfsdk_annot->GetPDFAnnot()->GetMutableAnnotDict();
+  auto annot_context =
+      std::make_unique<CPDF_AnnotContext>(std::move(annot_dict), page);
+
+  *page_index = page_view->GetPageIndex();
+  // Caller takes ownership.
+  *annot = FPDFAnnotationFromCPDFAnnotContext(annot_context.release());
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FORM_SetFocusedAnnot(FPDF_FORMHANDLE handle, FPDF_ANNOTATION annot) {
+  CPDFSDK_FormFillEnvironment* form_fill_env =
+      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(handle);
+  if (!form_fill_env)
+    return false;
+
+  CPDF_AnnotContext* annot_context = CPDFAnnotContextFromFPDFAnnotation(annot);
+  if (!annot_context)
+    return false;
+
+  CPDFSDK_PageView* page_view =
+      form_fill_env->GetOrCreatePageView(annot_context->GetPage());
+  if (!page_view->IsValid())
+    return false;
+
+  RetainPtr<CPDF_Dictionary> annot_dict = annot_context->GetMutableAnnotDict();
+  ObservedPtr<CPDFSDK_Annot> cpdfsdk_annot(
+      page_view->GetAnnotByDict(annot_dict.Get()));
+  if (!cpdfsdk_annot)
+    return false;
+
+  return form_fill_env->SetFocusAnnot(cpdfsdk_annot);
 }
 
 FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLDraw(FPDF_FORMHANDLE hHandle,
@@ -575,17 +689,17 @@
             rotate, flags);
 }
 
-#ifdef _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,
+#if defined(_SKIA_SUPPORT_)
+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
@@ -598,15 +712,17 @@
   if (!pForm)
     return;
 
-  Optional<FormFieldType> cast_input =
+  absl::optional<FormFieldType> cast_input =
       CPDF_FormField::IntToFormFieldType(fieldType);
-  if (!cast_input)
+  if (!cast_input.has_value())
     return;
 
-  if (cast_input.value() == FormFieldType::kUnknown)
-    pForm->SetAllHighlightColors(color);
-  else
-    pForm->SetHighlightColor(color, cast_input.value());
+  if (cast_input.value() == FormFieldType::kUnknown) {
+    pForm->SetAllHighlightColors(static_cast<FX_COLORREF>(color));
+  } else {
+    pForm->SetHighlightColor(static_cast<FX_COLORREF>(color),
+                             cast_input.value());
+  }
 }
 
 FPDF_EXPORT void FPDF_CALLCONV
@@ -638,7 +754,7 @@
   if (!pPage)
     return;
 
-  CPDFSDK_PageView* pPageView = pFormFillEnv->GetPageView(pPage, false);
+  CPDFSDK_PageView* pPageView = pFormFillEnv->GetPageView(pPage);
   if (pPageView) {
     pPageView->SetValid(false);
     // RemovePageView() takes care of the delete for us.
@@ -658,7 +774,7 @@
 FORM_DoDocumentOpenAction(FPDF_FORMHANDLE hHandle) {
   CPDFSDK_FormFillEnvironment* pFormFillEnv =
       CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
-  if (pFormFillEnv && pFormFillEnv->IsJSPlatformPresent())
+  if (pFormFillEnv)
     pFormFillEnv->ProcOpenAction();
 }
 
@@ -670,17 +786,14 @@
     return;
 
   CPDF_Document* pDoc = pFormFillEnv->GetPDFDocument();
-  CPDF_Dictionary* pDict = pDoc->GetRoot();
+  const CPDF_Dictionary* pDict = pDoc->GetRoot();
   if (!pDict)
     return;
 
-  CPDF_AAction aa(pDict->GetDictFor("AA"));
+  CPDF_AAction aa(pDict->GetDictFor(pdfium::form_fields::kAA));
   auto type = static_cast<CPDF_AAction::AActionType>(aaType);
-  if (aa.ActionExist(type)) {
-    CPDF_Action action = aa.GetAction(type);
-    pFormFillEnv->GetActionHandler()->DoAction_Document(action, type,
-                                                        pFormFillEnv);
-  }
+  if (aa.ActionExist(type))
+    pFormFillEnv->DoActionDocument(aa.GetAction(type), type);
 }
 
 FPDF_EXPORT void FPDF_CALLCONV FORM_DoPageAAction(FPDF_PAGE page,
@@ -696,19 +809,15 @@
   if (!pPDFPage)
     return;
 
-  if (!pFormFillEnv->GetPageView(pPage, false))
+  if (!pFormFillEnv->GetPageView(pPage))
     return;
 
-  CPDFSDK_ActionHandler* pActionHandler = pFormFillEnv->GetActionHandler();
-  CPDF_Dictionary* pPageDict = pPDFPage->GetDict();
-  CPDF_AAction aa(pPageDict->GetDictFor("AA"));
+  CPDF_AAction aa(pPDFPage->GetDict()->GetDictFor(pdfium::form_fields::kAA));
   CPDF_AAction::AActionType type = aaType == FPDFPAGE_AACTION_OPEN
                                        ? CPDF_AAction::kOpenPage
                                        : CPDF_AAction::kClosePage;
-  if (aa.ActionExist(type)) {
-    CPDF_Action action = aa.GetAction(type);
-    pActionHandler->DoAction_Page(action, type, pFormFillEnv);
-  }
+  if (aa.ActionExist(type))
+    pFormFillEnv->DoActionPage(aa.GetAction(type), type);
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -717,15 +826,11 @@
                       int index,
                       FPDF_BOOL selected) {
   CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
-  return pPageView->SetIndexSelected(index, selected);
+  return pPageView && pPageView->SetIndexSelected(index, selected);
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FORM_IsIndexSelected(FPDF_FORMHANDLE hHandle, FPDF_PAGE page, int index) {
   CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
-  return pPageView->IsIndexSelected(index);
+  return pPageView && pPageView->IsIndexSelected(index);
 }
diff --git a/fpdfsdk/fpdf_formfill_embeddertest.cpp b/fpdfsdk/fpdf_formfill_embeddertest.cpp
index 3ae272b..3f3f51e 100644
--- a/fpdfsdk/fpdf_formfill_embeddertest.cpp
+++ b/fpdfsdk/fpdf_formfill_embeddertest.cpp
@@ -1,26 +1,34 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
-#include <string>
 #include <vector>
 
 #include "build/build_config.h"
+#include "constants/ascii.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
 #include "public/cpp/fpdf_scopers.h"
 #include "public/fpdf_formfill.h"
 #include "public/fpdf_fwlevent.h"
 #include "public/fpdf_progressive.h"
 #include "testing/embedder_test.h"
+#include "testing/embedder_test_constants.h"
 #include "testing/embedder_test_mock_delegate.h"
 #include "testing/embedder_test_timer_handling_delegate.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+
+using pdfium::TextFormChecksum;
 
 using testing::_;
+using testing::InSequence;
+using testing::NiceMock;
+using testing::StrEq;
 
 using FPDFFormFillEmbedderTest = EmbedderTest;
 
@@ -103,7 +111,7 @@
 
   // Uses the mouse to navigate to text field and select text.
   void SelectTextWithMouse(const CFX_PointF& start, const CFX_PointF& end) {
-    ASSERT(start.y == end.y);
+    DCHECK_EQ(start.y, end.y);
 
     // Navigate to starting position and click mouse.
     FORM_OnMouseMove(form_handle(), page_, 0, start.x, start.y);
@@ -114,6 +122,11 @@
     FORM_OnLButtonUp(form_handle(), page_, 0, end.x, end.y);
   }
 
+  void SelectAllTextAtPoint(const CFX_PointF& point) {
+    FocusOnPoint(point);
+    EXPECT_TRUE(FORM_SelectAllText(form_handle(), page_));
+  }
+
   void CheckSelection(WideStringView expected_string) {
     unsigned long actual_len =
         FORM_GetSelectedText(form_handle(), page_, nullptr, 0);
@@ -128,6 +141,10 @@
     EXPECT_EQ(expected_string, WideString::FromUTF16LE(buf.data(), num_chars));
   }
 
+  void FocusOnPoint(const CFX_PointF& point) {
+    EXPECT_TRUE(FORM_OnFocus(form_handle(), page(), 0, point.x, point.y));
+  }
+
   void CheckFocusedFieldText(WideStringView expected_string) {
     unsigned long actual_len =
         FORM_GetFocusedText(form_handle(), page_, nullptr, 0);
@@ -194,11 +211,11 @@
   }
 
   void SelectAllCharLimitFormTextWithMouse() {
-    SelectTextWithMouse(CharLimitFormEnd(), CharLimitFormBegin());
+    SelectAllTextAtPoint(CharLimitFormBegin());
   }
 
   void SelectAllRegularFormTextWithMouse() {
-    SelectTextWithMouse(RegularFormEnd(), RegularFormBegin());
+    SelectAllTextAtPoint(RegularFormBegin());
   }
 
   const CFX_PointF& CharLimitFormBegin() const {
@@ -222,14 +239,14 @@
   }
 
   static CFX_PointF CharLimitFormAtX(float x) {
-    ASSERT(x >= kFormBeginX);
-    ASSERT(x <= kFormEndX);
+    DCHECK(x >= kFormBeginX);
+    DCHECK(x <= kFormEndX);
     return CFX_PointF(x, kCharLimitFormY);
   }
 
   static CFX_PointF RegularFormAtX(float x) {
-    ASSERT(x >= kFormBeginX);
-    ASSERT(x <= kFormEndX);
+    DCHECK(x >= kFormBeginX);
+    DCHECK(x <= kFormEndX);
     return CFX_PointF(x, kRegularFormY);
   }
 
@@ -266,29 +283,25 @@
   }
 
   void SelectEditableFormOption(int item_index) {
-    ASSERT(item_index >= 0);
-    ASSERT(item_index < 3);
+    DCHECK(item_index >= 0);
+    DCHECK(item_index < 3);
     SelectOption(item_index, EditableFormDropDown());
   }
 
   void SelectNonEditableFormOption(int item_index) {
-    ASSERT(item_index >= 0);
-    ASSERT(item_index < 26);
+    DCHECK(item_index >= 0);
+    DCHECK(item_index < 26);
     SelectOption(item_index, NonEditableFormDropDown());
   }
 
   void SelectAllEditableFormTextWithMouse() {
-    SelectTextWithMouse(EditableFormEnd(), EditableFormBegin());
+    SelectAllTextAtPoint(EditableFormBegin());
   }
 
   void FocusOnEditableForm() { FocusOnPoint(EditableFormDropDown()); }
 
   void FocusOnNonEditableForm() { FocusOnPoint(NonEditableFormDropDown()); }
 
-  void FocusOnPoint(const CFX_PointF& point) {
-    EXPECT_EQ(true, FORM_OnFocus(form_handle(), page(), 0, point.x, point.y));
-  }
-
   const CFX_PointF& EditableFormBegin() const {
     static const CFX_PointF point = EditableFormAtX(kFormBeginX);
     return point;
@@ -320,14 +333,14 @@
   }
 
   static CFX_PointF EditableFormAtX(float x) {
-    ASSERT(x >= kFormBeginX);
-    ASSERT(x <= kFormEndX);
+    DCHECK(x >= kFormBeginX);
+    DCHECK(x <= kFormEndX);
     return CFX_PointF(x, kEditableFormY);
   }
 
   static CFX_PointF NonEditableFormAtX(float x) {
-    ASSERT(x >= kFormBeginX);
-    ASSERT(x <= kFormEndX);
+    DCHECK(x >= kFormBeginX);
+    DCHECK(x <= kFormEndX);
     return CFX_PointF(x, kNonEditableFormY);
   }
 
@@ -368,10 +381,12 @@
     // - "Listbox_SingleSelect" - Ff: 0, 3 options with pair values.
     // - "Listbox_MultiSelect" - Ff: 2097152, 26 options with single values.
     // - "Listbox_ReadOnly" - Ff: 1, 3 options with single values.
-    // - "Listbox_MultiSelectMultipleSelected" - Ff: 2097152, 5 options with
-    // single values.
+    // - "Listbox_MultiSelectMultipleIndices" - Ff: 2097152, 5 options with
+    //    single values.
+    // - "Listbox_MultiSelectMultipleValues" - same configs as above.
+    // - "Listbox_MultiSelectMultipleMismatch" - same configs as above.
     // - "Listbox_SingleSelectLastSelected" - Ff: 0, 10 options with single
-    // values.
+    //    values.
     return "listbox_form.pdf";
   }
 
@@ -388,10 +403,22 @@
               GetFormTypeAtPoint(MultiSelectSecondVisibleOption()));
     EXPECT_EQ(
         GetFormType(),
-        GetFormTypeAtPoint(MultiSelectMultipleSelectedFirstVisibleOption()));
+        GetFormTypeAtPoint(MultiSelectMultipleIndicesFirstVisibleOption()));
     EXPECT_EQ(
         GetFormType(),
-        GetFormTypeAtPoint(MultiSelectMultipleSelectedSecondVisibleOption()));
+        GetFormTypeAtPoint(MultiSelectMultipleIndicesSecondVisibleOption()));
+    EXPECT_EQ(
+        GetFormType(),
+        GetFormTypeAtPoint(MultiSelectMultipleValuesFirstVisibleOption()));
+    EXPECT_EQ(
+        GetFormType(),
+        GetFormTypeAtPoint(MultiSelectMultipleValuesSecondVisibleOption()));
+    EXPECT_EQ(
+        GetFormType(),
+        GetFormTypeAtPoint(MultiSelectMultipleMismatchFirstVisibleOption()));
+    EXPECT_EQ(
+        GetFormType(),
+        GetFormTypeAtPoint(MultiSelectMultipleMismatchSecondVisibleOption()));
     EXPECT_EQ(GetFormType(),
               GetFormTypeAtPoint(SingleSelectLastSelectedFirstVisibleOption()));
     EXPECT_EQ(
@@ -402,8 +429,8 @@
   void ClickOnSingleSelectFormOption(int item_index) {
     // Only the first two indices are visible so can only click on those
     // without scrolling.
-    ASSERT(item_index >= 0);
-    ASSERT(item_index < 2);
+    DCHECK(item_index >= 0);
+    DCHECK(item_index < 2);
     if (item_index == 0) {
       ClickOnFormFieldAtPoint(SingleSelectFirstVisibleOption());
     } else {
@@ -414,8 +441,8 @@
   void ClickOnMultiSelectFormOption(int item_index) {
     // Only the first two indices are visible so can only click on those
     // without scrolling.
-    ASSERT(item_index >= 0);
-    ASSERT(item_index < 2);
+    DCHECK(item_index >= 0);
+    DCHECK(item_index < 2);
     if (item_index == 0) {
       ClickOnFormFieldAtPoint(MultiSelectFirstVisibleOption());
     } else {
@@ -423,23 +450,23 @@
     }
   }
 
-  void ClickOnMultiSelectMultipleSelectedFormOption(int item_index) {
+  void ClickOnMultiSelectMultipleValuesFormOption(int item_index) {
     // Only two indices are visible so can only click on those
     // without scrolling.
-    ASSERT(item_index >= 0);
-    ASSERT(item_index < 2);
+    DCHECK(item_index >= 0);
+    DCHECK(item_index < 2);
     if (item_index == 0) {
-      ClickOnFormFieldAtPoint(MultiSelectMultipleSelectedFirstVisibleOption());
+      ClickOnFormFieldAtPoint(MultiSelectMultipleValuesFirstVisibleOption());
     } else {
-      ClickOnFormFieldAtPoint(MultiSelectMultipleSelectedSecondVisibleOption());
+      ClickOnFormFieldAtPoint(MultiSelectMultipleValuesSecondVisibleOption());
     }
   }
 
   void ClickOnSingleSelectLastSelectedFormOption(int item_index) {
     // Only two indices are visible so can only click on those
     // without scrolling.
-    ASSERT(item_index >= 0);
-    ASSERT(item_index < 2);
+    DCHECK(item_index >= 0);
+    DCHECK(item_index < 2);
     if (item_index == 0) {
       ClickOnFormFieldAtPoint(SingleSelectLastSelectedFirstVisibleOption());
     } else {
@@ -455,8 +482,16 @@
     FocusOnPoint(MultiSelectFirstVisibleOption());
   }
 
-  void FocusOnMultiSelectMultipleSelectedForm() {
-    FocusOnPoint(MultiSelectMultipleSelectedFirstVisibleOption());
+  void FocusOnMultiSelectMultipleIndicesForm() {
+    FocusOnPoint(MultiSelectMultipleIndicesFirstVisibleOption());
+  }
+
+  void FocusOnMultiSelectMultipleValuesForm() {
+    FocusOnPoint(MultiSelectMultipleValuesFirstVisibleOption());
+  }
+
+  void FocusOnMultiSelectMultipleMismatchForm() {
+    FocusOnPoint(MultiSelectMultipleMismatchFirstVisibleOption());
   }
 
   void FocusOnSingleSelectLastSelectedForm() {
@@ -487,15 +522,39 @@
     return point;
   }
 
-  const CFX_PointF& MultiSelectMultipleSelectedFirstVisibleOption() const {
-    static const CFX_PointF point(
-        kFormBeginX, kMultiFormMultipleSelectedYFirstVisibleOption);
+  const CFX_PointF& MultiSelectMultipleIndicesFirstVisibleOption() const {
+    static const CFX_PointF point(kFormBeginX,
+                                  kMultiFormMultipleIndicesYFirstVisibleOption);
     return point;
   }
 
-  const CFX_PointF& MultiSelectMultipleSelectedSecondVisibleOption() const {
+  const CFX_PointF& MultiSelectMultipleIndicesSecondVisibleOption() const {
     static const CFX_PointF point(
-        kFormBeginX, kMultiFormMultipleSelectedYSecondVisibleOption);
+        kFormBeginX, kMultiFormMultipleIndicesYSecondVisibleOption);
+    return point;
+  }
+
+  const CFX_PointF& MultiSelectMultipleValuesFirstVisibleOption() const {
+    static const CFX_PointF point(kFormBeginX,
+                                  kMultiFormMultipleValuesYFirstVisibleOption);
+    return point;
+  }
+
+  const CFX_PointF& MultiSelectMultipleValuesSecondVisibleOption() const {
+    static const CFX_PointF point(kFormBeginX,
+                                  kMultiFormMultipleValuesYSecondVisibleOption);
+    return point;
+  }
+
+  const CFX_PointF& MultiSelectMultipleMismatchFirstVisibleOption() const {
+    static const CFX_PointF point(
+        kFormBeginX, kMultiFormMultipleMismatchYFirstVisibleOption);
+    return point;
+  }
+
+  const CFX_PointF& MultiSelectMultipleMismatchSecondVisibleOption() const {
+    static const CFX_PointF point(
+        kFormBeginX, kMultiFormMultipleMismatchYSecondVisibleOption);
     return point;
   }
 
@@ -517,21 +576,37 @@
   static constexpr float kSingleFormYSecondVisibleOption = 358.0;
   static constexpr float kMultiFormYFirstVisibleOption = 423.0;
   static constexpr float kMultiFormYSecondVisibleOption = 408.0;
-  static constexpr float kMultiFormMultipleSelectedYFirstVisibleOption = 223.0;
-  static constexpr float kMultiFormMultipleSelectedYSecondVisibleOption = 208.0;
+  static constexpr float kMultiFormMultipleIndicesYFirstVisibleOption = 273.0;
+  static constexpr float kMultiFormMultipleIndicesYSecondVisibleOption = 258.0;
+  static constexpr float kMultiFormMultipleValuesYFirstVisibleOption = 223.0;
+  static constexpr float kMultiFormMultipleValuesYSecondVisibleOption = 208.0;
+  static constexpr float kMultiFormMultipleMismatchYFirstVisibleOption = 173.0;
+  static constexpr float kMultiFormMultipleMismatchYSecondVisibleOption = 158.0;
   static constexpr float kSingleFormLastSelectedYFirstVisibleOption = 123.0;
   static constexpr float kSingleFormLastSelectedYSecondVisibleOption = 108.0;
 };
 
+class FPDFFormFillTextFormEmbedderTestVersion2
+    : public FPDFFormFillTextFormEmbedderTest {
+  void SetUp() override {
+    SetFormFillInfoVersion(2);
+    FPDFFormFillInteractiveEmbedderTest::SetUp();
+  }
+};
+
 TEST_F(FPDFFormFillEmbedderTest, FirstTest) {
   EmbedderTestMockDelegate mock;
   EXPECT_CALL(mock, Alert(_, _, _, _)).Times(0);
   EXPECT_CALL(mock, UnsupportedHandler(_)).Times(0);
   EXPECT_CALL(mock, SetTimer(_, _)).Times(0);
   EXPECT_CALL(mock, KillTimer(_)).Times(0);
+  EXPECT_CALL(mock, OnFocusChange(_, _, _)).Times(0);
+  EXPECT_CALL(mock, DoURIAction(_)).Times(0);
+  EXPECT_CALL(mock, DoURIActionWithKeyboardModifier(_, _, _)).Times(0);
+  EXPECT_CALL(mock, DoGoToAction(_, _, _, _, _)).Times(0);
   SetDelegate(&mock);
 
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
   UnloadPage(page);
@@ -541,7 +616,7 @@
   EmbedderTestTimerHandlingDelegate delegate;
   SetDelegate(&delegate);
 
-  EXPECT_TRUE(OpenDocument("bug_487928.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_487928.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
   DoOpenActions();
@@ -553,7 +628,7 @@
   EmbedderTestTimerHandlingDelegate delegate;
   SetDelegate(&delegate);
 
-  EXPECT_TRUE(OpenDocument("bug_507316.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_507316.pdf"));
   FPDF_PAGE page = LoadPage(2);
   EXPECT_TRUE(page);
   DoOpenActions();
@@ -562,7 +637,7 @@
 }
 
 TEST_F(FPDFFormFillEmbedderTest, BUG_514690) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
 
@@ -577,7 +652,7 @@
   EmbedderTestTimerHandlingDelegate delegate;
   SetDelegate(&delegate);
 
-  EXPECT_TRUE(OpenDocument("bug_900552.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_900552.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   DoOpenActions();
@@ -595,7 +670,7 @@
   EmbedderTestTimerHandlingDelegate delegate;
   SetDelegate(&delegate);
 
-  EXPECT_TRUE(OpenDocument("bug_901654.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_901654.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   DoOpenActions();
@@ -614,7 +689,7 @@
   EmbedderTestTimerHandlingDelegate delegate;
   SetDelegate(&delegate);
 
-  EXPECT_TRUE(OpenDocument("bug_901654_2.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_901654_2.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   DoOpenActions();
@@ -629,6 +704,296 @@
   UnloadPage(page);
 }
 
+TEST_F(FPDFFormFillEmbedderTest, GetFocusedAnnotation) {
+  ASSERT_TRUE(OpenDocument("annotiter.pdf"));
+  std::vector<FPDF_PAGE> pages;
+  for (size_t i = 0; i < 3; ++i) {
+    pages.push_back(LoadPage(i));
+    ASSERT_TRUE(pages.back());
+  }
+
+  // Ensure that there is no focused annotation.
+  FPDF_ANNOTATION annot = nullptr;
+  int page_index = -2;
+  ASSERT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot));
+  EXPECT_FALSE(annot);
+  EXPECT_EQ(-1, page_index);
+
+  // Validate that nullptr values are handled properly.
+  EXPECT_FALSE(FORM_GetFocusedAnnot(nullptr, &page_index, &annot));
+  EXPECT_FALSE(FORM_GetFocusedAnnot(form_handle(), &page_index, nullptr));
+  EXPECT_FALSE(FORM_GetFocusedAnnot(form_handle(), nullptr, &annot));
+
+  const CFX_PointF right_bottom_annot_point(410.0f, 210.0f);
+  constexpr int kExpectedAnnotIndex = 3;
+
+  for (size_t i = 0; i < pages.size(); ++i) {
+    // Invoke click on the form field to bring it to focus.
+    FORM_OnMouseMove(form_handle(), pages[i], 0, right_bottom_annot_point.x,
+                     right_bottom_annot_point.y);
+    FORM_OnLButtonDown(form_handle(), pages[i], 0, right_bottom_annot_point.x,
+                       right_bottom_annot_point.y);
+    FORM_OnLButtonUp(form_handle(), pages[i], 0, right_bottom_annot_point.x,
+                     right_bottom_annot_point.y);
+
+    ASSERT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot));
+    ASSERT_TRUE(annot);
+
+    EXPECT_EQ(kExpectedAnnotIndex, FPDFPage_GetAnnotIndex(pages[i], annot));
+    EXPECT_EQ(static_cast<int>(i), page_index);
+
+    FPDFPage_CloseAnnot(annot);
+  }
+
+  for (FPDF_PAGE page : pages)
+    UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, SetFocusedAnnotation) {
+  ASSERT_TRUE(OpenDocument("annotiter.pdf"));
+  std::vector<FPDF_PAGE> pages;
+  for (size_t i = 0; i < 3; ++i) {
+    pages.push_back(LoadPage(i));
+    ASSERT_TRUE(pages.back());
+  }
+
+  // Ensure that there is no focused annotation.
+  FPDF_ANNOTATION annot = nullptr;
+  int page_index = -2;
+  ASSERT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot));
+  EXPECT_FALSE(annot);
+  EXPECT_EQ(-1, page_index);
+
+  // Validate that nullptr values are handled properly.
+  EXPECT_FALSE(FORM_SetFocusedAnnot(nullptr, annot));
+  EXPECT_FALSE(FORM_SetFocusedAnnot(form_handle(), nullptr));
+
+  constexpr int kExpectedAnnotIndex = 2;
+
+  for (size_t i = 0; i < pages.size(); ++i) {
+    // Setting focus on an annotation on page i.
+    ScopedFPDFAnnotation focused_annot(
+        FPDFPage_GetAnnot(pages[i], kExpectedAnnotIndex));
+    ASSERT_TRUE(focused_annot);
+
+    ASSERT_TRUE(FORM_SetFocusedAnnot(form_handle(), focused_annot.get()));
+
+    ASSERT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot));
+    EXPECT_EQ(kExpectedAnnotIndex, FPDFPage_GetAnnotIndex(pages[i], annot));
+    EXPECT_EQ(static_cast<int>(i), page_index);
+
+    FPDFPage_CloseAnnot(annot);
+  }
+
+  for (FPDF_PAGE page : pages)
+    UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, FormFillFirstTab) {
+  ASSERT_TRUE(OpenDocument("annotiter.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Invoking first tab on the page.
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, 0));
+  int page_index = -2;
+  FPDF_ANNOTATION annot = nullptr;
+  EXPECT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot));
+  EXPECT_EQ(0, page_index);
+  ASSERT_TRUE(annot);
+  EXPECT_EQ(1, FPDFPage_GetAnnotIndex(page, annot));
+  FPDFPage_CloseAnnot(annot);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, FormFillFirstShiftTab) {
+  ASSERT_TRUE(OpenDocument("annotiter.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Invoking first shift-tab on the page.
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab,
+                             FWL_EVENTFLAG_ShiftKey));
+  int page_index = -2;
+  FPDF_ANNOTATION annot = nullptr;
+  EXPECT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot));
+  EXPECT_EQ(0, page_index);
+  ASSERT_TRUE(annot);
+  EXPECT_EQ(0, FPDFPage_GetAnnotIndex(page, annot));
+  FPDFPage_CloseAnnot(annot);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, FormFillContinuousTab) {
+  ASSERT_TRUE(OpenDocument("annotiter.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  static constexpr int kExpectedAnnotIndex[] = {1, 2, 3, 0};
+  // Tabs should iterate focus over annotations.
+  for (size_t i = 0; i < std::size(kExpectedAnnotIndex); ++i) {
+    ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, 0));
+    int page_index = -2;
+    FPDF_ANNOTATION annot = nullptr;
+    EXPECT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot));
+    EXPECT_EQ(0, page_index);
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(kExpectedAnnotIndex[i], FPDFPage_GetAnnotIndex(page, annot));
+    FPDFPage_CloseAnnot(annot);
+  }
+
+  // Tab should not be handled as the last annotation of the page is in focus.
+  ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, 0));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, FormFillContinuousShiftTab) {
+  ASSERT_TRUE(OpenDocument("annotiter.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  static constexpr int kExpectedAnnotIndex[] = {0, 3, 2, 1};
+  // Shift-tabs should iterate focus over annotations.
+  for (size_t i = 0; i < std::size(kExpectedAnnotIndex); ++i) {
+    ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab,
+                               FWL_EVENTFLAG_ShiftKey));
+    int page_index = -2;
+    FPDF_ANNOTATION annot = nullptr;
+    EXPECT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot));
+    EXPECT_EQ(0, page_index);
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(kExpectedAnnotIndex[i], FPDFPage_GetAnnotIndex(page, annot));
+    FPDFPage_CloseAnnot(annot);
+  }
+
+  // Shift-tab should not be handled as the first annotation of the page is in
+  // focus.
+  ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab,
+                              FWL_EVENTFLAG_ShiftKey));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, TabWithModifiers) {
+  ASSERT_TRUE(OpenDocument("annotiter.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab,
+                              FWL_EVENTFLAG_ControlKey));
+
+  ASSERT_FALSE(
+      FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, FWL_EVENTFLAG_AltKey));
+
+  ASSERT_FALSE(
+      FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab,
+                     (FWL_EVENTFLAG_ControlKey | FWL_EVENTFLAG_ShiftKey)));
+
+  ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab,
+                              (FWL_EVENTFLAG_AltKey | FWL_EVENTFLAG_ShiftKey)));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, KeyPressWithNoFocusedAnnot) {
+  ASSERT_TRUE(OpenDocument("annotiter.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // There should be no focused annotation to start with.
+  int page_index = -2;
+  FPDF_ANNOTATION annot = nullptr;
+  EXPECT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot));
+  EXPECT_EQ(-1, page_index);
+  EXPECT_FALSE(annot);
+
+  static constexpr int kKeysToPress[] = {
+      FWL_VKEY_NewLine, FWL_VKEY_Return, FWL_VKEY_Space,
+      FWL_VKEY_Delete,  FWL_VKEY_0,      FWL_VKEY_9,
+      FWL_VKEY_A,       FWL_VKEY_Z,      FWL_VKEY_F1,
+  };
+  for (int key : kKeysToPress) {
+    // Pressing random keys when there is no focus should not trigger focus.
+    EXPECT_FALSE(FORM_OnKeyDown(form_handle(), page, key, 0));
+    page_index = -2;
+    annot = nullptr;
+    EXPECT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot));
+    EXPECT_EQ(-1, page_index);
+    EXPECT_FALSE(annot);
+  }
+
+  UnloadPage(page);
+}
+
+#ifdef PDF_ENABLE_XFA
+TEST_F(FPDFFormFillEmbedderTest, XFAFormFillFirstTab) {
+  ASSERT_TRUE(OpenDocument("xfa/email_recommended.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Invoking first tab on the page.
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, 0));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, XFAFormFillFirstShiftTab) {
+  ASSERT_TRUE(OpenDocument("xfa/email_recommended.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Invoking first shift-tab on the page.
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab,
+                             FWL_EVENTFLAG_ShiftKey));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, XFAFormFillContinuousTab) {
+  ASSERT_TRUE(OpenDocument("xfa/email_recommended.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Invoking first tab on the page.
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, 0));
+
+  // Subsequent tabs should move focus over annotations.
+  for (size_t i = 0; i < 9; ++i)
+    ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, 0));
+
+  // Tab should not be handled as the last annotation of the page is in focus.
+  ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, 0));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, XFAFormFillContinuousShiftTab) {
+  ASSERT_TRUE(OpenDocument("xfa/email_recommended.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Invoking first shift-tab on the page.
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab,
+                             FWL_EVENTFLAG_ShiftKey));
+
+  // Subsequent shift-tabs should move focus over annotations.
+  for (size_t i = 0; i < 9; ++i) {
+    ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab,
+                               FWL_EVENTFLAG_ShiftKey));
+  }
+
+  // Shift-tab should not be handled as the first annotation of the page is in
+  // focus.
+  ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab,
+                              FWL_EVENTFLAG_ShiftKey));
+
+  UnloadPage(page);
+}
+#endif  // PDF_ENABLE_XFA
+
 class DoURIActionBlockedDelegate final : public EmbedderTest::Delegate {
  public:
   void DoURIAction(FPDF_BYTESTRING uri) override {
@@ -640,7 +1005,7 @@
   DoURIActionBlockedDelegate delegate;
   SetDelegate(&delegate);
 
-  EXPECT_TRUE(OpenDocument("redirect.pdf"));
+  ASSERT_TRUE(OpenDocument("redirect.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
   DoOpenActions();
@@ -648,13 +1013,82 @@
   UnloadPage(page);
 }
 
+TEST_F(FPDFFormFillEmbedderTest, CheckReadOnlyInCheckbox) {
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  ASSERT_TRUE(OpenDocument("click_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Check for read-only checkbox.
+    ScopedFPDFAnnotation focused_annot(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(FORM_SetFocusedAnnot(form_handle(), focused_annot.get()));
+
+    // Shift-tab to the previous control.
+    ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab,
+                               FWL_EVENTFLAG_ShiftKey));
+    FPDF_ANNOTATION annot = nullptr;
+    int page_index = -1;
+    ASSERT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot));
+    EXPECT_EQ(0, FPDFPage_GetAnnotIndex(page, annot));
+
+    // The read-only checkbox is initially in checked state.
+    EXPECT_TRUE(FPDFAnnot_IsChecked(form_handle(), annot));
+
+    EXPECT_TRUE(FORM_OnChar(form_handle(), page, pdfium::ascii::kReturn, 0));
+    EXPECT_TRUE(FPDFAnnot_IsChecked(form_handle(), annot));
+
+    EXPECT_TRUE(FORM_OnChar(form_handle(), page, pdfium::ascii::kSpace, 0));
+    EXPECT_TRUE(FPDFAnnot_IsChecked(form_handle(), annot));
+
+    FPDFPage_CloseAnnot(annot);
+  }
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, CheckReadOnlyInRadiobutton) {
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  ASSERT_TRUE(OpenDocument("click_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Check for read-only radio button.
+    ScopedFPDFAnnotation focused_annot(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(FORM_SetFocusedAnnot(form_handle(), focused_annot.get()));
+
+    // Tab to the next control.
+    ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, 0));
+
+    FPDF_ANNOTATION annot = nullptr;
+    int page_index = -1;
+    ASSERT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot));
+    EXPECT_EQ(2, FPDFPage_GetAnnotIndex(page, annot));
+    // The read-only radio button is initially in checked state.
+    EXPECT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot));
+
+    EXPECT_TRUE(FORM_OnChar(form_handle(), page, pdfium::ascii::kReturn, 0));
+    EXPECT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot));
+
+    EXPECT_TRUE(FORM_OnChar(form_handle(), page, pdfium::ascii::kSpace, 0));
+    EXPECT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot));
+
+    FPDFPage_CloseAnnot(annot);
+  }
+  UnloadPage(page);
+}
+
 #ifdef PDF_ENABLE_V8
 TEST_F(FPDFFormFillEmbedderTest, DisableJavaScript) {
   // Test that timers and intervals can't fire without JS.
   EmbedderTestTimerHandlingDelegate delegate;
   SetDelegate(&delegate);
 
-  EXPECT_TRUE(OpenDocumentWithoutJavaScript("bug_551248.pdf"));
+  ASSERT_TRUE(OpenDocumentWithoutJavaScript("bug_551248.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
   DoOpenActions();
@@ -683,7 +1117,7 @@
   EmbedderTestTimerHandlingDelegate delegate;
   SetDelegate(&delegate);
 
-  EXPECT_TRUE(OpenDocument("document_aactions.pdf"));
+  ASSERT_TRUE(OpenDocument("document_aactions.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
 
@@ -707,7 +1141,7 @@
   EmbedderTestTimerHandlingDelegate delegate;
   SetDelegate(&delegate);
 
-  EXPECT_TRUE(OpenDocumentWithoutJavaScript("document_aactions.pdf"));
+  ASSERT_TRUE(OpenDocumentWithoutJavaScript("document_aactions.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
 
@@ -728,7 +1162,7 @@
   EmbedderTestTimerHandlingDelegate delegate;
   SetDelegate(&delegate);
 
-  EXPECT_TRUE(OpenDocument("bug_551248.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_551248.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
   DoOpenActions();
@@ -780,7 +1214,7 @@
   EmbedderTestTimerHandlingDelegate delegate;
   SetDelegate(&delegate);
 
-  EXPECT_TRUE(OpenDocument("bug_620428.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_620428.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
   DoOpenActions();
@@ -797,7 +1231,7 @@
   EmbedderTestTimerHandlingDelegate delegate;
   SetDelegate(&delegate);
 
-  EXPECT_TRUE(OpenDocument("bug_634394.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_634394.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
   DoOpenActions();
@@ -819,7 +1253,7 @@
   EmbedderTestTimerHandlingDelegate delegate;
   SetDelegate(&delegate);
 
-  EXPECT_TRUE(OpenDocument("bug_634716.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_634716.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
   DoOpenActions();
@@ -841,7 +1275,7 @@
   EmbedderTestTimerHandlingDelegate delegate;
   SetDelegate(&delegate);
 
-  EXPECT_TRUE(OpenDocument("bug_679649.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_679649.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
 
@@ -858,7 +1292,7 @@
   EmbedderTestTimerHandlingDelegate delegate;
   SetDelegate(&delegate);
 
-  EXPECT_TRUE(OpenDocument("bug_707673.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_707673.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
 
@@ -873,7 +1307,7 @@
 }
 
 TEST_F(FPDFFormFillEmbedderTest, BUG_765384) {
-  EXPECT_TRUE(OpenDocument("bug_765384.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_765384.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
 
@@ -884,32 +1318,31 @@
 }
 #endif  // PDF_ENABLE_V8
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_FormText DISABLED_FormText
+TEST_F(FPDFFormFillEmbedderTest, FormText) {
+  const char* focused_text_form_with_abc_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "b9fb2245a98ac48146da84237a37f8cc";
+#if BUILDFLAG(IS_APPLE)
+    return "9fb14198d75ca0a107060c60ca21b0c7";
 #else
-#define MAYBE_FormText FormText
+    return "6e6f790bb14c4fc6107faf8c17d23dbd";
 #endif
-TEST_F(FPDFFormFillEmbedderTest, MAYBE_FormText) {
-#if defined(OS_MACOSX)
-  const char md5_1[] = "5f11dbe575fe197a37c3fb422559f8ff";
-  const char md5_2[] = "35b1a4b679eafc749a0b6fda750c0e8d";
-  const char md5_3[] = "65c64a7c355388f719a752aa1e23f6fe";
-#elif defined(OS_WIN)
-  const char md5_1[] = "d3204faa62b607f0bd3893c9c22cabcb";
-  const char md5_2[] = "29d1c3fd226ca6a69597f75937690320";
-  const char md5_3[] = "5e678a55912cb568fd677bf34abb8727";
+  }();
+  const char* unfocused_text_form_with_abc_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "5f3205f0189d9dde54665f970838f614";
+#if BUILDFLAG(IS_APPLE)
+    return "3c3209357e0c057a0620afa7d83eb784";
 #else
-  const char md5_1[] = "b890950d4b9bc163b1a96797f3004b53";
-  const char md5_2[] = "11487d5597599a26e8912b9c1d9422cb";
-  const char md5_3[] = "bffe0ecea9a533f217047ee41d6be466";
+    return "94b7e10ac8c662b73e33628ca2f5e63b";
 #endif
+  }();
   {
-    EXPECT_TRUE(OpenDocument("text_form.pdf"));
+    ASSERT_TRUE(OpenDocument("text_form.pdf"));
     FPDF_PAGE page = LoadPage(0);
     ASSERT_TRUE(page);
     ScopedFPDFBitmap bitmap1 = RenderLoadedPage(page);
-    CompareBitmap(bitmap1.get(), 300, 300, md5_1);
+    CompareBitmap(bitmap1.get(), 300, 300, TextFormChecksum());
 
     // Click on the textfield
     EXPECT_EQ(FPDF_FORMFIELD_TEXTFIELD,
@@ -921,25 +1354,26 @@
     FORM_OnLButtonUp(form_handle(), page, 0, 120.0, 120.0);
 
     // Write "ABC"
-    FORM_OnChar(form_handle(), page, 65, 0);
-    FORM_OnChar(form_handle(), page, 66, 0);
-    FORM_OnChar(form_handle(), page, 67, 0);
+    FORM_OnChar(form_handle(), page, 'A', 0);
+    FORM_OnChar(form_handle(), page, 'B', 0);
+    FORM_OnChar(form_handle(), page, 'C', 0);
     ScopedFPDFBitmap bitmap2 = RenderLoadedPage(page);
-    CompareBitmap(bitmap2.get(), 300, 300, md5_2);
+    CompareBitmap(bitmap2.get(), 300, 300, focused_text_form_with_abc_checksum);
 
     // Focus remains despite right clicking out of the textfield
     FORM_OnMouseMove(form_handle(), page, 0, 15.0, 15.0);
     FORM_OnRButtonDown(form_handle(), page, 0, 15.0, 15.0);
     FORM_OnRButtonUp(form_handle(), page, 0, 15.0, 15.0);
     ScopedFPDFBitmap bitmap3 = RenderLoadedPage(page);
-    CompareBitmap(bitmap3.get(), 300, 300, md5_2);
+    CompareBitmap(bitmap3.get(), 300, 300, focused_text_form_with_abc_checksum);
 
     // Take out focus by clicking out of the textfield
     FORM_OnMouseMove(form_handle(), page, 0, 15.0, 15.0);
     FORM_OnLButtonDown(form_handle(), page, 0, 15.0, 15.0);
     FORM_OnLButtonUp(form_handle(), page, 0, 15.0, 15.0);
     ScopedFPDFBitmap bitmap4 = RenderLoadedPage(page);
-    CompareBitmap(bitmap4.get(), 300, 300, md5_3);
+    CompareBitmap(bitmap4.get(), 300, 300,
+                  unfocused_text_form_with_abc_checksum);
 
     EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
 
@@ -947,90 +1381,209 @@
     UnloadPage(page);
   }
   // Check saved document
-  VerifySavedDocument(300, 300, md5_3);
+  VerifySavedDocument(300, 300, unfocused_text_form_with_abc_checksum);
 }
 
 // Tests using FPDF_REVERSE_BYTE_ORDER with FPDF_FFLDraw(). The two rendered
 // bitmaps should be different.
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_BUG_1281 DISABLED_BUG_1281
-#else
-#define MAYBE_BUG_1281 BUG_1281
-#endif
-TEST_F(FPDFFormFillEmbedderTest, MAYBE_BUG_1281) {
-  const char kMd5Normal[] = "6c674642154408e877d88c6c082d67e9";
-  const char kMd5ReverseByteOrder[] = "24fff03d1e663b7ece5f6e69ad837124";
+TEST_F(FPDFFormFillEmbedderTest, BUG_1281) {
+  const char* reverse_byte_order_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "8077970bbd10333f18186a9bb459bbe6";
+    return "24fff03d1e663b7ece5f6e69ad837124";
+  }();
 
   ASSERT_TRUE(OpenDocument("bug_890322.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
   ScopedFPDFBitmap bitmap_normal = RenderLoadedPage(page);
-  CompareBitmap(bitmap_normal.get(), 200, 200, kMd5Normal);
+  CompareBitmap(bitmap_normal.get(), 200, 200, pdfium::Bug890322Checksum());
 
   ScopedFPDFBitmap bitmap_reverse_byte_order =
       RenderLoadedPageWithFlags(page, FPDF_REVERSE_BYTE_ORDER);
   CompareBitmap(bitmap_reverse_byte_order.get(), 200, 200,
-                kMd5ReverseByteOrder);
+                reverse_byte_order_checksum);
 
   UnloadPage(page);
 }
 
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_RemoveFormFieldHighlight DISABLED_RemoveFormFieldHighlight
-#else
-#define MAYBE_RemoveFormFieldHighlight RemoveFormFieldHighlight
-#endif
-TEST_F(FPDFFormFillEmbedderTest, MAYBE_RemoveFormFieldHighlight) {
-#if defined(OS_MACOSX)
-  const char kMd5Normal[] = "5f11dbe575fe197a37c3fb422559f8ff";
-  const char kMd5NoHighlight[] = "575ec237c790950f40bfcaefb2e3923c";
-#elif defined(OS_WIN)
-  const char kMd5Normal[] = "d3204faa62b607f0bd3893c9c22cabcb";
-  const char kMd5NoHighlight[] = "3ec0938828e0a37ef23f687ee95a80e1";
-#else
-  const char kMd5Normal[] = "b890950d4b9bc163b1a96797f3004b53";
-  const char kMd5NoHighlight[] = "006010c318457810a518aa5e0b33c498";
-#endif
+TEST_F(FPDFFormFillEmbedderTest, Bug1302455RenderOnly) {
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "520c4415c9977f40d6b4af5a0a94d764";
+    return "bbee92af1daec2340c81f482878744d8";
+  }();
+  {
+    ASSERT_TRUE(OpenDocument("bug_1302455.pdf"));
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_TRUE(page);
 
-  EXPECT_TRUE(OpenDocument("text_form.pdf"));
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    CompareBitmap(bitmap.get(), 300, 300, checksum);
+
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+    UnloadPage(page);
+  }
+  VerifySavedDocument(300, 300, checksum);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, Bug1302455EditFirstForm) {
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "143c2bb79fcaecf24f5aa104dce27beb";
+#if BUILDFLAG(IS_APPLE)
+    return "bf5423874f188427d2500a2bc4abebbe";
+#else
+    return "6a4ac9a15d2c34589616c8f2b05fbedd";
+#endif
+  }();
+  {
+    ASSERT_TRUE(OpenDocument("bug_1302455.pdf"));
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_TRUE(page);
+
+    EXPECT_EQ(FPDF_FORMFIELD_TEXTFIELD,
+              FPDFPage_HasFormFieldAtPoint(form_handle(), page, 110, 110));
+    FORM_OnMouseMove(form_handle(), page, 0, 110, 110);
+    FORM_OnLButtonDown(form_handle(), page, 0, 110, 110);
+    FORM_OnLButtonUp(form_handle(), page, 0, 110, 110);
+    FORM_OnChar(form_handle(), page, 'A', 0);
+
+    FORM_ForceToKillFocus(form_handle());
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    CompareBitmap(bitmap.get(), 300, 300, checksum);
+
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+    UnloadPage(page);
+  }
+  VerifySavedDocument(300, 300, checksum);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, Bug1302455EditSecondForm) {
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "e36726414acb616dc203e8851b510e2c";
+#if BUILDFLAG(IS_APPLE)
+    return "8a0fd8772dba6e1e952e49d159cc64b5";
+#else
+    return "45a7694933c2ba3c5dc8f6cc18b79175";
+#endif
+  }();
+  {
+    ASSERT_TRUE(OpenDocument("bug_1302455.pdf"));
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_TRUE(page);
+
+    EXPECT_EQ(FPDF_FORMFIELD_TEXTFIELD,
+              FPDFPage_HasFormFieldAtPoint(form_handle(), page, 110, 170));
+    FORM_OnMouseMove(form_handle(), page, 0, 110, 170);
+    FORM_OnLButtonDown(form_handle(), page, 0, 110, 170);
+    FORM_OnLButtonUp(form_handle(), page, 0, 110, 170);
+    FORM_OnChar(form_handle(), page, 'B', 0);
+
+    FORM_ForceToKillFocus(form_handle());
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    CompareBitmap(bitmap.get(), 300, 300, checksum);
+
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+    UnloadPage(page);
+  }
+  VerifySavedDocument(300, 300, checksum);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, Bug1302455EditBothForms) {
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "f82a807c056e22aa55d3d7228eedfe6f";
+#if BUILDFLAG(IS_APPLE)
+    return "1f422ee1c520ad74b1a993b64bd4dc4a";
+#else
+    return "13984969b1e141079ab5f4aa80185463";
+#endif
+  }();
+  {
+    ASSERT_TRUE(OpenDocument("bug_1302455.pdf"));
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_TRUE(page);
+
+    EXPECT_EQ(FPDF_FORMFIELD_TEXTFIELD,
+              FPDFPage_HasFormFieldAtPoint(form_handle(), page, 110, 110));
+    FORM_OnMouseMove(form_handle(), page, 0, 110, 110);
+    FORM_OnLButtonDown(form_handle(), page, 0, 110, 110);
+    FORM_OnLButtonUp(form_handle(), page, 0, 110, 110);
+    FORM_OnChar(form_handle(), page, 'A', 0);
+
+    EXPECT_EQ(FPDF_FORMFIELD_TEXTFIELD,
+              FPDFPage_HasFormFieldAtPoint(form_handle(), page, 110, 170));
+    FORM_OnMouseMove(form_handle(), page, 0, 110, 170);
+    FORM_OnLButtonDown(form_handle(), page, 0, 110, 170);
+    FORM_OnLButtonUp(form_handle(), page, 0, 110, 170);
+    FORM_OnChar(form_handle(), page, 'B', 0);
+
+    FORM_ForceToKillFocus(form_handle());
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    CompareBitmap(bitmap.get(), 300, 300, checksum);
+
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+    UnloadPage(page);
+  }
+  VerifySavedDocument(300, 300, checksum);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, RemoveFormFieldHighlight) {
+  const char* no_highlight_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "3bfddb2529085021ad283b7e65f71525";
+    }
+#if BUILDFLAG(IS_APPLE)
+    return "5c82aa43e3b478aa1e4c94bb9ef1f11f";
+#else
+    return "a6268304f7eedfa9ee98fac3caaf2efb";
+#endif
+  }();
+
+  ASSERT_TRUE(OpenDocument("text_form.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
   ScopedFPDFBitmap bitmap1 = RenderLoadedPage(page);
-  CompareBitmap(bitmap1.get(), 300, 300, kMd5Normal);
+  CompareBitmap(bitmap1.get(), 300, 300, TextFormChecksum());
 
   // Removing the highlight changes the rendering.
   FPDF_RemoveFormFieldHighlight(form_handle());
   ScopedFPDFBitmap bitmap2 = RenderLoadedPage(page);
-  CompareBitmap(bitmap2.get(), 300, 300, kMd5NoHighlight);
+  CompareBitmap(bitmap2.get(), 300, 300, no_highlight_checksum);
 
   // Restoring it gives the original rendering.
   SetInitialFormFieldHighlight(form_handle());
   ScopedFPDFBitmap bitmap3 = RenderLoadedPage(page);
-  CompareBitmap(bitmap3.get(), 300, 300, kMd5Normal);
+  CompareBitmap(bitmap3.get(), 300, 300, TextFormChecksum());
 
   UnloadPage(page);
 }
 
 TEST_F(FPDFFormFillEmbedderTest, HasFormInfoNone) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
-  EXPECT_EQ(FORMTYPE_NONE, FPDF_GetFormType(document_));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_EQ(FORMTYPE_NONE, FPDF_GetFormType(document()));
 }
 
 TEST_F(FPDFFormFillEmbedderTest, HasFormInfoAcroForm) {
-  EXPECT_TRUE(OpenDocument("text_form.pdf"));
-  EXPECT_EQ(FORMTYPE_ACRO_FORM, FPDF_GetFormType(document_));
+  ASSERT_TRUE(OpenDocument("text_form.pdf"));
+  EXPECT_EQ(FORMTYPE_ACRO_FORM, FPDF_GetFormType(document()));
 }
 
 TEST_F(FPDFFormFillEmbedderTest, HasFormInfoXFAFull) {
-  EXPECT_TRUE(OpenDocument("simple_xfa.pdf"));
-  EXPECT_EQ(FORMTYPE_XFA_FULL, FPDF_GetFormType(document_));
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+  EXPECT_EQ(FORMTYPE_XFA_FULL, FPDF_GetFormType(document()));
 }
 
 TEST_F(FPDFFormFillEmbedderTest, HasFormInfoXFAForeground) {
-  EXPECT_TRUE(OpenDocument("bug_216.pdf"));
-  EXPECT_EQ(FORMTYPE_XFA_FOREGROUND, FPDF_GetFormType(document_));
+  ASSERT_TRUE(OpenDocument("bug_216.pdf"));
+  EXPECT_EQ(FORMTYPE_XFA_FOREGROUND, FPDF_GetFormType(document()));
 }
 
 TEST_F(FPDFFormFillEmbedderTest, BadApiInputsText) {
@@ -1076,6 +1629,62 @@
   UnloadPage(page);
 }
 
+TEST_F(FPDFFormFillEmbedderTest, HasFormFieldAtPointForXFADoc) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  EXPECT_EQ(-1, FPDFPage_HasFormFieldAtPoint(form_handle(), page, 612, 792));
+
+#ifdef PDF_ENABLE_XFA
+  constexpr int kExpectedFieldType = FPDF_FORMFIELD_XFA_TEXTFIELD;
+#else
+  constexpr int kExpectedFieldType = -1;
+#endif
+  EXPECT_EQ(kExpectedFieldType,
+            FPDFPage_HasFormFieldAtPoint(form_handle(), page, 50, 30));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, SelectAllText) {
+  ASSERT_TRUE(OpenDocument("text_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Test bad arguments.
+  EXPECT_FALSE(FORM_SelectAllText(nullptr, nullptr));
+  EXPECT_FALSE(FORM_SelectAllText(form_handle(), nullptr));
+  EXPECT_FALSE(FORM_SelectAllText(nullptr, page));
+
+  // Focus on the text field and add some text.
+  EXPECT_TRUE(FORM_OnFocus(form_handle(), page, 0, 115, 115));
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello");
+  FORM_ReplaceSelection(form_handle(), page, text_to_insert.get());
+
+  // Sanity check text field data.
+  uint16_t buffer[6];
+  ASSERT_EQ(12u, FORM_GetFocusedText(form_handle(), page, nullptr, 0));
+  ASSERT_EQ(12u,
+            FORM_GetFocusedText(form_handle(), page, buffer, sizeof(buffer)));
+  EXPECT_EQ("Hello", GetPlatformString(buffer));
+
+  // Check there is no selection.
+  ASSERT_EQ(2u, FORM_GetSelectedText(form_handle(), page, nullptr, 0));
+  ASSERT_EQ(2u,
+            FORM_GetSelectedText(form_handle(), page, buffer, sizeof(buffer)));
+  EXPECT_EQ("", GetPlatformString(buffer));
+
+  // Check FORM_SelectAllText() works.
+  EXPECT_TRUE(FORM_SelectAllText(form_handle(), page));
+  ASSERT_EQ(12u, FORM_GetSelectedText(form_handle(), page, nullptr, 0));
+  ASSERT_EQ(12u,
+            FORM_GetSelectedText(form_handle(), page, buffer, sizeof(buffer)));
+  EXPECT_EQ("Hello", GetPlatformString(buffer));
+
+  UnloadPage(page);
+}
+
 TEST_F(FPDFFormFillTextFormEmbedderTest, GetSelectedTextEmptyAndBasicKeyboard) {
   // Test empty selection.
   CheckFocusedFieldText(L"");
@@ -1852,6 +2461,81 @@
   CheckSelection(L"ABCDEHello");
 }
 
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       CheckIfEnterAndSpaceKeyAreHandled) {
+  // Non-editable field is set to 'Banana' (index 1) upon opening.
+  ClickOnFormFieldAtPoint(NonEditableFormBegin());
+  CheckIsIndexSelected(0, false);
+  CheckIsIndexSelected(1, true);
+
+  // Verify that the Return character is handled.
+  EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kReturn, 0));
+
+  // Change the selection in the combo-box using the arrow down key.
+  EXPECT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Down, 0));
+  CheckIsIndexSelected(1, false);
+  CheckIsIndexSelected(2, true);
+
+  // Tab to the next control.
+  EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kTab, 0));
+
+  // Shift-tab to the previous control.
+  EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kTab,
+                          FWL_EVENTFLAG_ShiftKey));
+
+  // Verify that the selection is unchanged.
+  CheckIsIndexSelected(2, true);
+
+  // Verify that the Space character is handled.
+  EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kSpace, 0));
+
+  // Change the selection in the combo-box using the arrow down key.
+  EXPECT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Down, 0));
+  CheckIsIndexSelected(3, true);
+
+  // Tab to the next control.
+  EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kTab, 0));
+
+  // Shift-tab to the previous control.
+  EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kTab,
+                          FWL_EVENTFLAG_ShiftKey));
+
+  // Verify that the selection is unchanged.
+  CheckIsIndexSelected(3, true);
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       CheckIfEnterAndSpaceKeyAreHandledOnEditableFormField) {
+  // Non-editable field is set to 'Banana' (index 1) upon opening.
+  ClickOnFormFieldAtPoint(EditableFormBegin());
+  CheckIsIndexSelected(0, false);
+  CheckIsIndexSelected(1, false);
+
+  // Verify that the Return character is handled.
+  EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kReturn, 0));
+
+  // Change the selection in the combo-box using the arrow down key.
+  EXPECT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Down, 0));
+  CheckIsIndexSelected(0, true);
+  CheckIsIndexSelected(1, false);
+
+  // Tab to the next control.
+  EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kTab, 0));
+
+  // Shift-tab to the previous control.
+  EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kTab,
+                          FWL_EVENTFLAG_ShiftKey));
+
+  // Verify that the selection is unchanged.
+  CheckIsIndexSelected(0, true);
+
+  // Verify that the Space character is handled.
+  EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kSpace, 0));
+
+  CheckFocusedFieldText(L" ");
+  CheckIsIndexSelected(0, false);
+}
+
 TEST_F(FPDFFormFillTextFormEmbedderTest,
        InsertTextInEmptyCharLimitTextFieldOverflow) {
   // Click on the textfield.
@@ -2056,6 +2740,30 @@
   CheckSelection(L"Hello World");
 }
 
+TEST_F(FPDFFormFillTextFormEmbedderTest, FocusAnnotationUpdateToEmbedder) {
+  testing::NiceMock<EmbedderTestMockDelegate> mock;
+  SetDelegate(&mock);
+  CheckFocusedFieldText(L"");
+
+#ifdef PDF_ENABLE_XFA
+  EXPECT_CALL(mock, OnFocusChange(_, _, 0)).Times(1);
+#else   // PDF_ENABLE_XFA
+  EXPECT_CALL(mock, OnFocusChange(_, _, 0)).Times(0);
+#endif  // PDF_ENABLE_XFA
+
+  ClickOnFormFieldAtPoint(RegularFormBegin());
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTestVersion2,
+       FocusAnnotationUpdateToEmbedder) {
+  testing::NiceMock<EmbedderTestMockDelegate> mock;
+  SetDelegate(&mock);
+  CheckFocusedFieldText(L"");
+
+  EXPECT_CALL(mock, OnFocusChange(_, _, 0)).Times(1);
+  ClickOnFormFieldAtPoint(RegularFormBegin());
+}
+
 TEST_F(FPDFFormFillTextFormEmbedderTest, FocusChanges) {
   static const CFX_PointF kNonFormPoint(1, 1);
   CheckFocusedFieldText(L"");
@@ -2428,13 +3136,32 @@
   CheckFocusedFieldText(L"Banana");
 }
 
-TEST_F(FPDFFormFillListBoxFormEmbedderTest, CheckIfMultipleSelected) {
+TEST_F(FPDFFormFillListBoxFormEmbedderTest, CheckIfMultipleSelectedIndices) {
+  // Multiselect field set to 'Belgium' (index 1) and 'Denmark' (index 3) upon
+  // opening.
+  FocusOnMultiSelectMultipleIndicesForm();
+  for (int i = 0; i < 5; i++) {
+    bool expected = (i == 1 || i == 3);
+    CheckIsIndexSelected(i, expected);
+  }
+}
+
+TEST_F(FPDFFormFillListBoxFormEmbedderTest, CheckIfMultipleSelectedValues) {
   // Multiselect field set to 'Gamma' (index 2) and 'Epsilon' (index 4) upon
   // opening.
-  FocusOnMultiSelectMultipleSelectedForm();
+  FocusOnMultiSelectMultipleValuesForm();
   for (int i = 0; i < 5; i++) {
-    // TODO(bug_1377): Should be selected at index 2 and index 4.
-    bool expected = false;
+    bool expected = (i == 2 || i == 4);
+    CheckIsIndexSelected(i, expected);
+  }
+}
+
+TEST_F(FPDFFormFillListBoxFormEmbedderTest, CheckIfMultipleSelectedMismatch) {
+  // Multiselect field set to 'Alligator' (index 0) and 'Cougar' (index 2) upon
+  // opening.
+  FocusOnMultiSelectMultipleMismatchForm();
+  for (int i = 0; i < 5; i++) {
+    bool expected = (i == 0 || i == 2);
     CheckIsIndexSelected(i, expected);
   }
 }
@@ -2447,7 +3174,7 @@
   // TODO(bug_1377): Behavior should be changed to the one described below.
   // The top visible option is 'Gamma' (index 2), so the first selection should
   // not change. The second selection, 'Epsilon,' should be deselected.
-  ClickOnMultiSelectMultipleSelectedFormOption(0);
+  ClickOnMultiSelectMultipleValuesFormOption(0);
   for (int i = 0; i < 5; i++) {
     bool expected = i == 0;
     CheckIsIndexSelected(i, expected);
@@ -2473,6 +3200,39 @@
   }
 }
 
+TEST_F(FPDFFormFillTextFormEmbedderTest, ReplaceAndKeepSelection) {
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"XYZ");
+  ClickOnFormFieldAtPoint(RegularFormBegin());
+  CheckCanUndo(false);
+  CheckCanRedo(false);
+
+  TypeTextIntoTextField(2, RegularFormBegin());
+  CheckFocusedFieldText(L"AB");
+  CheckSelection(L"");
+  SelectTextWithKeyboard(1, FWL_VKEY_Right, RegularFormBegin());
+  CheckSelection(L"A");
+
+  FORM_ReplaceAndKeepSelection(form_handle(), page(), text_to_insert.get());
+  CheckFocusedFieldText(L"XYZB");
+  CheckSelection(L"XYZ");
+  CheckCanUndo(true);
+  CheckCanRedo(false);
+
+  PerformUndo();
+  CheckFocusedFieldText(L"AB");
+  CheckCanUndo(true);
+  CheckCanRedo(true);
+
+  SelectTextWithKeyboard(1, FWL_VKEY_Left, RegularFormEnd());
+  CheckSelection(L"B");
+
+  FORM_ReplaceAndKeepSelection(form_handle(), page(), text_to_insert.get());
+  CheckFocusedFieldText(L"AXYZ");
+  CheckSelection(L"XYZ");
+  CheckCanUndo(true);
+  CheckCanRedo(false);
+}
+
 TEST_F(FPDFFormFillTextFormEmbedderTest, ReplaceSelection) {
   ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"XYZ");
   ClickOnFormFieldAtPoint(RegularFormBegin());
@@ -2520,3 +3280,247 @@
   CheckCanUndo(true);
   CheckCanRedo(false);
 }
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, SelectAllWithKeyboardShortcut) {
+  // Start with a couple of letters in the text form.
+  TypeTextIntoTextField(2, RegularFormBegin());
+  CheckFocusedFieldText(L"AB");
+  CheckSelection(L"");
+
+  // Select all with the keyboard shortcut.
+#if BUILDFLAG(IS_APPLE)
+  constexpr int kCorrectModifier = FWL_EVENTFLAG_MetaKey;
+#else
+  constexpr int kCorrectModifier = FWL_EVENTFLAG_ControlKey;
+#endif
+  FORM_OnChar(form_handle(), page(), pdfium::ascii::kControlA,
+              kCorrectModifier);
+  CheckSelection(L"AB");
+
+  // Reset the selection again.
+  ClickOnFormFieldAtPoint(RegularFormBegin());
+  CheckSelection(L"");
+
+  // Select all with the keyboard shortcut using the wrong modifier key.
+#if BUILDFLAG(IS_APPLE)
+  constexpr int kWrongModifier = FWL_EVENTFLAG_ControlKey;
+#else
+  constexpr int kWrongModifier = FWL_EVENTFLAG_MetaKey;
+#endif
+  FORM_OnChar(form_handle(), page(), pdfium::ascii::kControlA, kWrongModifier);
+  CheckSelection(L"");
+}
+
+class FPDFXFAFormBug1055869EmbedderTest
+    : public FPDFFormFillInteractiveEmbedderTest {
+ protected:
+  FPDFXFAFormBug1055869EmbedderTest() = default;
+  ~FPDFXFAFormBug1055869EmbedderTest() override = default;
+
+  const char* GetDocumentName() const override { return "bug_1055869.pdf"; }
+  int GetFormType() const override { return FORMTYPE_XFA_FULL; }
+};
+
+TEST_F(FPDFXFAFormBug1055869EmbedderTest, Paste) {
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"XYZ");
+  DoubleClickOnFormFieldAtPoint(CFX_PointF(100, 100));
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+}
+
+class FPDFXFAFormBug1058653EmbedderTest
+    : public FPDFFormFillInteractiveEmbedderTest {
+ protected:
+  FPDFXFAFormBug1058653EmbedderTest() = default;
+  ~FPDFXFAFormBug1058653EmbedderTest() override = default;
+
+  const char* GetDocumentName() const override { return "bug_1058653.pdf"; }
+  int GetFormType() const override { return FORMTYPE_XFA_FULL; }
+};
+
+TEST_F(FPDFXFAFormBug1058653EmbedderTest, Paste) {
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"");
+  DoubleClickOnFormFieldAtPoint(CFX_PointF(22, 22));
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+}
+
+class FPDFFormFillActionUriTest : public EmbedderTest {
+ protected:
+  FPDFFormFillActionUriTest() = default;
+  ~FPDFFormFillActionUriTest() override = default;
+
+  void SetUp() override {
+    EmbedderTest::SetUp();
+    ASSERT_TRUE(OpenDocument("annots_action_handling.pdf"));
+    page_ = LoadPage(0);
+    ASSERT_TRUE(page_);
+
+    // Set Widget and Link as supported tabbable annots.
+    constexpr FPDF_ANNOTATION_SUBTYPE kFocusableSubtypes[] = {FPDF_ANNOT_WIDGET,
+                                                              FPDF_ANNOT_LINK};
+    constexpr size_t kSubtypeCount = std::size(kFocusableSubtypes);
+    ASSERT_TRUE(FPDFAnnot_SetFocusableSubtypes(
+        form_handle(), kFocusableSubtypes, kSubtypeCount));
+  }
+
+  void TearDown() override {
+    UnloadPage(page_);
+    EmbedderTest::TearDown();
+  }
+
+  void SetFocusOnNthAnnot(size_t n) {
+    DCHECK_NE(n, 0u);
+    // Setting focus on first annot.
+    FORM_OnMouseMove(form_handle(), page(), /*modifier=*/0, 100, 680);
+    FORM_OnLButtonDown(form_handle(), page(), /*modifier=*/0, 100, 680);
+    FORM_OnLButtonUp(form_handle(), page(), /*modifier=*/0, 100, 680);
+    for (size_t i = 1; i < n; i++)
+      ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Tab, 0));
+  }
+
+  FPDF_PAGE page() { return page_; }
+
+ private:
+  FPDF_PAGE page_ = nullptr;
+};
+
+TEST_F(FPDFFormFillActionUriTest, ButtonActionInvokeTest) {
+  NiceMock<EmbedderTestMockDelegate> mock;
+  // TODO(crbug.com/1028991): DoURIAction expect call should be 1.
+  EXPECT_CALL(mock, DoURIAction(_)).Times(0);
+  SetDelegate(&mock);
+
+  SetFocusOnNthAnnot(1);
+
+  // Tab once from first form to go to button widget.
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Tab, 0));
+
+  // TODO(crbug.com/1028991): Following should be changed to ASSERT_TRUE after
+  // handling key press implementation on buttons.
+  ASSERT_FALSE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kReturn, 0));
+}
+
+TEST_F(FPDFFormFillActionUriTest, LinkActionInvokeTest) {
+  NiceMock<EmbedderTestMockDelegate> mock;
+  {
+    InSequence sequence;
+    const char kExpectedUri[] = "https://cs.chromium.org/";
+#ifdef PDF_ENABLE_XFA
+    EXPECT_CALL(mock,
+                DoURIActionWithKeyboardModifier(_, StrEq(kExpectedUri), _))
+        .Times(4);
+#else   // PDF_ENABLE_XFA
+    EXPECT_CALL(mock, DoURIAction(StrEq(kExpectedUri))).Times(4);
+    EXPECT_CALL(mock, DoURIActionWithKeyboardModifier(_, _, _)).Times(0);
+#endif  // PDF_ENABLE_XFA
+  }
+  SetDelegate(&mock);
+  SetFocusOnNthAnnot(3);
+  int modifier = 0;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier = FWL_EVENTFLAG_ControlKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier = FWL_EVENTFLAG_ShiftKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier |= FWL_EVENTFLAG_ControlKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+
+  ASSERT_FALSE(FORM_OnKeyDown(nullptr, page(), FWL_VKEY_Return, modifier));
+  ASSERT_FALSE(
+      FORM_OnKeyDown(form_handle(), nullptr, FWL_VKEY_Return, modifier));
+  // Following checks should be changed to ASSERT_TRUE if FORM_OnKeyDown starts
+  // handling for Shift/Space/Control.
+  ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Shift, modifier));
+  ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Space, modifier));
+  ASSERT_FALSE(
+      FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Control, modifier));
+}
+
+TEST_F(FPDFFormFillActionUriTest, InternalLinkActionInvokeTest) {
+  NiceMock<EmbedderTestMockDelegate> mock;
+  EXPECT_CALL(mock, DoGoToAction(_, _, 1, _, _)).Times(12);
+  SetDelegate(&mock);
+
+  SetFocusOnNthAnnot(4);
+  int modifier = 0;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier = FWL_EVENTFLAG_ControlKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier = FWL_EVENTFLAG_ShiftKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier |= FWL_EVENTFLAG_ControlKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+
+  SetFocusOnNthAnnot(5);
+  modifier = 0;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier = FWL_EVENTFLAG_ControlKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier = FWL_EVENTFLAG_ShiftKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier |= FWL_EVENTFLAG_ControlKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+
+  SetFocusOnNthAnnot(6);
+  modifier = 0;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier = FWL_EVENTFLAG_ControlKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier = FWL_EVENTFLAG_ShiftKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier |= FWL_EVENTFLAG_ControlKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+
+  ASSERT_FALSE(FORM_OnKeyDown(nullptr, page(), FWL_VKEY_Return, modifier));
+  ASSERT_FALSE(
+      FORM_OnKeyDown(form_handle(), nullptr, FWL_VKEY_Return, modifier));
+  // Following checks should be changed to ASSERT_TRUE if FORM_OnKeyDown starts
+  // handling for Shift/Space/Control.
+  ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Shift, modifier));
+  ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Space, modifier));
+  ASSERT_FALSE(
+      FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Control, modifier));
+}
+
+class FPDFFormFillActionUriTestVersion2 : public FPDFFormFillActionUriTest {
+  void SetUp() override {
+    SetFormFillInfoVersion(2);
+    FPDFFormFillActionUriTest::SetUp();
+  }
+};
+
+TEST_F(FPDFFormFillActionUriTestVersion2, LinkActionInvokeTest) {
+  NiceMock<EmbedderTestMockDelegate> mock;
+  {
+    InSequence sequence;
+    EXPECT_CALL(mock, DoURIAction(_)).Times(0);
+    const char kExpectedUri[] = "https://cs.chromium.org/";
+    EXPECT_CALL(mock,
+                DoURIActionWithKeyboardModifier(_, StrEq(kExpectedUri), 0));
+    EXPECT_CALL(mock, DoURIActionWithKeyboardModifier(
+                          _, StrEq(kExpectedUri), FWL_EVENTFLAG_ControlKey));
+    EXPECT_CALL(mock, DoURIActionWithKeyboardModifier(_, StrEq(kExpectedUri),
+                                                      FWL_EVENTFLAG_ShiftKey));
+    EXPECT_CALL(mock,
+                DoURIActionWithKeyboardModifier(_, StrEq(kExpectedUri), 3));
+  }
+  SetDelegate(&mock);
+  SetFocusOnNthAnnot(3);
+  int modifier = 0;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier = FWL_EVENTFLAG_ControlKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier = FWL_EVENTFLAG_ShiftKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier |= FWL_EVENTFLAG_ControlKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+
+  ASSERT_FALSE(FORM_OnKeyDown(nullptr, page(), FWL_VKEY_Return, modifier));
+  ASSERT_FALSE(
+      FORM_OnKeyDown(form_handle(), nullptr, FWL_VKEY_Return, modifier));
+  // Following checks should be changed to ASSERT_TRUE if FORM_OnKeyDown starts
+  // handling for Shift/Space/Control.
+  ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Shift, modifier));
+  ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Space, modifier));
+  ASSERT_FALSE(
+      FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Control, modifier));
+}
diff --git a/fpdfsdk/fpdf_javascript.cpp b/fpdfsdk/fpdf_javascript.cpp
index c2d119b..ed91ac6 100644
--- a/fpdfsdk/fpdf_javascript.cpp
+++ b/fpdfsdk/fpdf_javascript.cpp
@@ -1,17 +1,18 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "public/fpdf_javascript.h"
 
 #include <memory>
+#include <utility>
 
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfdoc/cpdf_action.h"
 #include "core/fpdfdoc/cpdf_nametree.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 struct CPDF_JavaScript {
   WideString name;
@@ -21,7 +22,11 @@
 FPDF_EXPORT int FPDF_CALLCONV
 FPDFDoc_GetJavaScriptActionCount(FPDF_DOCUMENT document) {
   CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document);
-  return doc ? CPDF_NameTree(doc, "JavaScript").GetCount() : -1;
+  if (!doc)
+    return -1;
+
+  auto name_tree = CPDF_NameTree::Create(doc, "JavaScript");
+  return name_tree ? pdfium::base::checked_cast<int>(name_tree->GetCount()) : 0;
 }
 
 FPDF_EXPORT FPDF_JAVASCRIPT_ACTION FPDF_CALLCONV
@@ -30,26 +35,26 @@
   if (!doc || index < 0)
     return nullptr;
 
-  CPDF_NameTree name_tree(doc, "JavaScript");
-  if (static_cast<size_t>(index) >= name_tree.GetCount())
+  auto name_tree = CPDF_NameTree::Create(doc, "JavaScript");
+  if (!name_tree || static_cast<size_t>(index) >= name_tree->GetCount())
     return nullptr;
 
   WideString name;
-  CPDF_Dictionary* obj =
-      ToDictionary(name_tree.LookupValueAndName(index, &name));
+  RetainPtr<CPDF_Dictionary> obj =
+      ToDictionary(name_tree->LookupValueAndName(index, &name));
   if (!obj)
     return nullptr;
 
   // Validate |obj|. Type is optional, but must be valid if present.
-  CPDF_Action action(obj);
-  if (action.GetType() != CPDF_Action::JavaScript)
+  CPDF_Action action(std::move(obj));
+  if (action.GetType() != CPDF_Action::Type::kJavaScript)
     return nullptr;
 
-  Optional<WideString> script = action.MaybeGetJavaScript();
+  absl::optional<WideString> script = action.MaybeGetJavaScript();
   if (!script.has_value())
     return nullptr;
 
-  auto js = pdfium::MakeUnique<CPDF_JavaScript>();
+  auto js = std::make_unique<CPDF_JavaScript>();
   js->name = name;
   js->script = script.value();
   return FPDFJavaScriptActionFromCPDFJavaScriptAction(js.release());
diff --git a/fpdfsdk/fpdf_javascript_embeddertest.cpp b/fpdfsdk/fpdf_javascript_embeddertest.cpp
index 35deb69..9a67f02 100644
--- a/fpdfsdk/fpdf_javascript_embeddertest.cpp
+++ b/fpdfsdk/fpdf_javascript_embeddertest.cpp
@@ -1,9 +1,7 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
-#include <string>
 #include <vector>
 
 #include "core/fxcrt/fx_memory.h"
@@ -32,12 +30,12 @@
 
 TEST_F(FPDFJavaScriptEmbedderTest, GetJS) {
   ASSERT_TRUE(OpenDocument("js.pdf"));
-  EXPECT_EQ(6, FPDFDoc_GetJavaScriptActionCount(document()));
+  EXPECT_EQ(5, FPDFDoc_GetJavaScriptActionCount(document()));
 
   ScopedFPDFJavaScriptAction js;
   js.reset(FPDFDoc_GetJavaScriptAction(document(), -1));
   EXPECT_FALSE(js);
-  js.reset(FPDFDoc_GetJavaScriptAction(document(), 6));
+  js.reset(FPDFDoc_GetJavaScriptAction(document(), 5));
   EXPECT_FALSE(js);
   js.reset(FPDFDoc_GetJavaScriptAction(nullptr, -1));
   EXPECT_FALSE(js);
@@ -49,21 +47,17 @@
   EXPECT_FALSE(js);
   js.reset(FPDFDoc_GetJavaScriptAction(nullptr, 5));
   EXPECT_FALSE(js);
-  js.reset(FPDFDoc_GetJavaScriptAction(nullptr, 6));
-  EXPECT_FALSE(js);
 
   js.reset(FPDFDoc_GetJavaScriptAction(document(), 0));
   EXPECT_TRUE(js);
   js.reset(FPDFDoc_GetJavaScriptAction(document(), 1));
   EXPECT_TRUE(js);
   js.reset(FPDFDoc_GetJavaScriptAction(document(), 2));
-  EXPECT_TRUE(js);
+  EXPECT_FALSE(js);
   js.reset(FPDFDoc_GetJavaScriptAction(document(), 3));
   EXPECT_FALSE(js);
   js.reset(FPDFDoc_GetJavaScriptAction(document(), 4));
   EXPECT_FALSE(js);
-  js.reset(FPDFDoc_GetJavaScriptAction(document(), 5));
-  EXPECT_FALSE(js);
 }
 
 TEST_F(FPDFJavaScriptEmbedderTest, GetJSName) {
diff --git a/fpdfsdk/fpdf_ppo.cpp b/fpdfsdk/fpdf_ppo.cpp
index 819ba05..9efb523 100644
--- a/fpdfsdk/fpdf_ppo.cpp
+++ b/fpdfsdk/fpdf_ppo.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,11 +9,16 @@
 #include <algorithm>
 #include <map>
 #include <memory>
+#include <numeric>
+#include <sstream>
 #include <utility>
 #include <vector>
 
 #include "constants/page_object.h"
+#include "core/fpdfapi/page/cpdf_form.h"
+#include "core/fpdfapi/page/cpdf_formobject.h"
 #include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
@@ -25,13 +30,20 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_string_wrappers.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "public/cpp/fpdf_scopers.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/span.h"
+
+struct XObjectContext {
+  UnownedPtr<CPDF_Document> dest_doc;
+  RetainPtr<CPDF_Stream> xobject;
+};
 
 namespace {
 
@@ -40,7 +52,7 @@
 // scaled down, and scale is in range of (0, 1) exclusive.
 struct NupPageSettings {
   CFX_PointF subPageStartPoint;
-  float scale;
+  float scale = 0.0f;
 };
 
 // Calculates the N-up parameters.  When importing multiple pages into one page.
@@ -87,10 +99,10 @@
       m_nPagesOnXAxis(nPagesOnXAxis),
       m_nPagesOnYAxis(nPagesOnYAxis),
       m_nPagesPerSheet(nPagesOnXAxis * nPagesOnYAxis) {
-  ASSERT(m_nPagesOnXAxis > 0);
-  ASSERT(m_nPagesOnYAxis > 0);
-  ASSERT(m_destPageSize.width > 0);
-  ASSERT(m_destPageSize.height > 0);
+  DCHECK(m_nPagesOnXAxis > 0);
+  DCHECK(m_nPagesOnYAxis > 0);
+  DCHECK(m_destPageSize.width > 0);
+  DCHECK(m_destPageSize.height > 0);
 
   m_subPageSize.width = m_destPageSize.width / m_nPagesOnXAxis;
   m_subPageSize.height = m_destPageSize.height / m_nPagesOnYAxis;
@@ -137,8 +149,9 @@
   return CalculatePageEdit(iSubX, iSubY, pagesize);
 }
 
-const CPDF_Object* PageDictGetInheritableTag(const CPDF_Dictionary* pDict,
-                                             const ByteString& bsSrcTag) {
+RetainPtr<const CPDF_Object> PageDictGetInheritableTag(
+    RetainPtr<const CPDF_Dictionary> pDict,
+    const ByteString& bsSrcTag) {
   if (!pDict || bsSrcTag.IsEmpty())
     return nullptr;
   if (!pDict->KeyExist(pdfium::page_object::kParent) ||
@@ -146,14 +159,12 @@
     return nullptr;
   }
 
-  const CPDF_Object* pType =
-      pDict->GetObjectFor(pdfium::page_object::kType)->GetDirect();
-  if (!ToName(pType))
-    return nullptr;
-  if (pType->GetString().Compare("Page"))
+  RetainPtr<const CPDF_Name> pName =
+      ToName(pDict->GetObjectFor(pdfium::page_object::kType)->GetDirect());
+  if (!pName || pName->GetString() != "Page")
     return nullptr;
 
-  const CPDF_Dictionary* pp = ToDictionary(
+  RetainPtr<const CPDF_Dictionary> pp = ToDictionary(
       pDict->GetObjectFor(pdfium::page_object::kParent)->GetDirect());
   if (!pp)
     return nullptr;
@@ -172,29 +183,14 @@
   return nullptr;
 }
 
-CFX_FloatRect GetMediaBox(const CPDF_Dictionary* pPageDict) {
-  const CPDF_Object* pMediaBox =
-      PageDictGetInheritableTag(pPageDict, pdfium::page_object::kMediaBox);
-  const CPDF_Array* pArray = ToArray(pMediaBox->GetDirect());
-  if (!pArray)
-    return CFX_FloatRect();
-  return pArray->GetRect();
-}
-
-CFX_FloatRect GetCropBox(const CPDF_Dictionary* pPageDict) {
-  if (pPageDict->KeyExist(pdfium::page_object::kCropBox))
-    return pPageDict->GetRectFor(pdfium::page_object::kCropBox);
-  return GetMediaBox(pPageDict);
-}
-
-bool CopyInheritable(CPDF_Dictionary* pDestPageDict,
-                     const CPDF_Dictionary* pSrcPageDict,
+bool CopyInheritable(RetainPtr<CPDF_Dictionary> pDestPageDict,
+                     RetainPtr<const CPDF_Dictionary> pSrcPageDict,
                      const ByteString& key) {
   if (pDestPageDict->KeyExist(key))
     return true;
 
-  const CPDF_Object* pInheritable =
-      PageDictGetInheritableTag(pSrcPageDict, key);
+  RetainPtr<const CPDF_Object> pInheritable =
+      PageDictGetInheritableTag(std::move(pSrcPageDict), key);
   if (!pInheritable)
     return false;
 
@@ -202,74 +198,15 @@
   return true;
 }
 
-bool ParsePageRangeString(const ByteString& bsPageRange,
-                          uint32_t nCount,
-                          std::vector<uint32_t>* pageArray) {
-  ByteString bsStrippedPageRange = bsPageRange;
-  bsStrippedPageRange.Remove(' ');
-  size_t nLength = bsStrippedPageRange.GetLength();
-  if (nLength == 0)
-    return true;
-
-  static const ByteString cbCompareString("0123456789-,");
-  for (size_t i = 0; i < nLength; ++i) {
-    if (!cbCompareString.Contains(bsStrippedPageRange[i]))
-      return false;
-  }
-
-  ByteString cbMidRange;
-  size_t nStringFrom = 0;
-  size_t nStringTo = 0;
-  while (nStringTo < nLength) {
-    nStringTo = bsStrippedPageRange.Find(',', nStringFrom).value_or(nLength);
-    cbMidRange =
-        bsStrippedPageRange.Substr(nStringFrom, nStringTo - nStringFrom);
-    Optional<size_t> nDashPosition = cbMidRange.Find('-');
-    if (nDashPosition) {
-      size_t nMid = nDashPosition.value();
-      uint32_t nStartPageNum = pdfium::base::checked_cast<uint32_t>(
-          atoi(cbMidRange.First(nMid).c_str()));
-      if (nStartPageNum == 0)
-        return false;
-
-      ++nMid;
-      size_t nEnd = cbMidRange.GetLength() - nMid;
-      if (nEnd == 0)
-        return false;
-
-      uint32_t nEndPageNum = pdfium::base::checked_cast<uint32_t>(
-          atoi(cbMidRange.Substr(nMid, nEnd).c_str()));
-      if (nStartPageNum < 0 || nStartPageNum > nEndPageNum ||
-          nEndPageNum > nCount) {
-        return false;
-      }
-      for (uint32_t i = nStartPageNum; i <= nEndPageNum; ++i) {
-        pageArray->push_back(i);
-      }
-    } else {
-      uint32_t nPageNum =
-          pdfium::base::checked_cast<uint32_t>(atoi(cbMidRange.c_str()));
-      if (nPageNum <= 0 || nPageNum > nCount)
-        return false;
-      pageArray->push_back(nPageNum);
-    }
-    nStringFrom = nStringTo + 1;
-  }
-  return true;
-}
-
-std::vector<uint32_t> GetPageNumbers(const CPDF_Document& doc,
+std::vector<uint32_t> GetPageIndices(const CPDF_Document& doc,
                                      const ByteString& bsPageRange) {
-  std::vector<uint32_t> page_numbers;
   uint32_t nCount = doc.GetPageCount();
-  if (bsPageRange.IsEmpty()) {
-    for (uint32_t i = 1; i <= nCount; ++i)
-      page_numbers.push_back(i);
-  } else {
-    if (!ParsePageRangeString(bsPageRange, nCount, &page_numbers))
-      page_numbers.clear();
-  }
-  return page_numbers;
+  if (!bsPageRange.IsEmpty())
+    return ParsePageRangeString(bsPageRange, nCount);
+
+  std::vector<uint32_t> page_indices(nCount);
+  std::iota(page_indices.begin(), page_indices.end(), 0);
+  return page_indices;
 }
 
 class CPDF_PageOrganizer {
@@ -280,13 +217,13 @@
   // Must be called after construction before doing anything else.
   bool Init();
 
-  bool UpdateReference(CPDF_Object* pObj);
+  bool UpdateReference(RetainPtr<CPDF_Object> pObj);
 
-  CPDF_Document* dest() { return m_pDestDoc.Get(); }
-  const CPDF_Document* dest() const { return m_pDestDoc.Get(); }
+  CPDF_Document* dest() { return m_pDestDoc; }
+  const CPDF_Document* dest() const { return m_pDestDoc; }
 
-  CPDF_Document* src() { return m_pSrcDoc.Get(); }
-  const CPDF_Document* src() const { return m_pSrcDoc.Get(); }
+  CPDF_Document* src() { return m_pSrcDoc; }
+  const CPDF_Document* src() const { return m_pSrcDoc; }
 
   void AddObjectMapping(uint32_t dwOldPageObj, uint32_t dwNewPageObj) {
     m_ObjectNumberMap[dwOldPageObj] = dwNewPageObj;
@@ -311,37 +248,37 @@
 CPDF_PageOrganizer::~CPDF_PageOrganizer() = default;
 
 bool CPDF_PageOrganizer::Init() {
-  ASSERT(m_pDestDoc);
-  ASSERT(m_pSrcDoc);
+  DCHECK(m_pDestDoc);
+  DCHECK(m_pSrcDoc);
 
-  CPDF_Dictionary* pNewRoot = dest()->GetRoot();
+  RetainPtr<CPDF_Dictionary> pNewRoot = dest()->GetMutableRoot();
   if (!pNewRoot)
     return false;
 
-  CPDF_Dictionary* pDocInfoDict = dest()->GetInfo();
+  RetainPtr<CPDF_Dictionary> pDocInfoDict = dest()->GetInfo();
   if (!pDocInfoDict)
     return false;
 
   pDocInfoDict->SetNewFor<CPDF_String>("Producer", "PDFium", false);
 
-  ByteString cbRootType = pNewRoot->GetStringFor("Type", ByteString());
+  ByteString cbRootType = pNewRoot->GetByteStringFor("Type", ByteString());
   if (cbRootType.IsEmpty())
     pNewRoot->SetNewFor<CPDF_Name>("Type", "Catalog");
 
-  CPDF_Object* pElement = pNewRoot->GetObjectFor("Pages");
-  CPDF_Dictionary* pNewPages =
-      pElement ? ToDictionary(pElement->GetDirect()) : nullptr;
+  RetainPtr<CPDF_Object> pElement = pNewRoot->GetMutableObjectFor("Pages");
+  RetainPtr<CPDF_Dictionary> pNewPages =
+      pElement ? ToDictionary(pElement->GetMutableDirect()) : nullptr;
   if (!pNewPages) {
     pNewPages = dest()->NewIndirect<CPDF_Dictionary>();
     pNewRoot->SetNewFor<CPDF_Reference>("Pages", dest(),
                                         pNewPages->GetObjNum());
   }
-  ByteString cbPageType = pNewPages->GetStringFor("Type", ByteString());
+  ByteString cbPageType = pNewPages->GetByteStringFor("Type", ByteString());
   if (cbPageType.IsEmpty())
     pNewPages->SetNewFor<CPDF_Name>("Type", "Pages");
 
   if (!pNewPages->GetArrayFor("Kids")) {
-    auto* pNewArray = dest()->NewIndirect<CPDF_Array>();
+    auto pNewArray = dest()->NewIndirect<CPDF_Array>();
     pNewPages->SetNewFor<CPDF_Number>("Count", 0);
     pNewPages->SetNewFor<CPDF_Reference>("Kids", dest(),
                                          pNewArray->GetObjNum());
@@ -349,10 +286,10 @@
   return true;
 }
 
-bool CPDF_PageOrganizer::UpdateReference(CPDF_Object* pObj) {
+bool CPDF_PageOrganizer::UpdateReference(RetainPtr<CPDF_Object> pObj) {
   switch (pObj->GetType()) {
     case CPDF_Object::kReference: {
-      CPDF_Reference* pReference = pObj->AsReference();
+      CPDF_Reference* pReference = pObj->AsMutableReference();
       uint32_t newobjnum = GetNewObjId(pReference);
       if (newobjnum == 0)
         return false;
@@ -360,7 +297,7 @@
       return true;
     }
     case CPDF_Object::kDictionary: {
-      CPDF_Dictionary* pDict = pObj->AsDictionary();
+      CPDF_Dictionary* pDict = pObj->AsMutableDictionary();
       std::vector<ByteString> bad_keys;
       {
         CPDF_DictionaryLocker locker(pDict);
@@ -368,30 +305,27 @@
           const ByteString& key = it.first;
           if (key == "Parent" || key == "Prev" || key == "First")
             continue;
-          CPDF_Object* pNextObj = it.second.Get();
-          if (!pNextObj)
-            return false;
+          RetainPtr<CPDF_Object> pNextObj = it.second;
           if (!UpdateReference(pNextObj))
             bad_keys.push_back(key);
         }
       }
       for (const auto& key : bad_keys)
-        pDict->RemoveFor(key);
+        pDict->RemoveFor(key.AsStringView());
       return true;
     }
     case CPDF_Object::kArray: {
-      CPDF_Array* pArray = pObj->AsArray();
+      CPDF_Array* pArray = pObj->AsMutableArray();
       for (size_t i = 0; i < pArray->size(); ++i) {
-        CPDF_Object* pNextObj = pArray->GetObjectAt(i);
-        if (!pNextObj || !UpdateReference(pNextObj))
+        if (!UpdateReference(pArray->GetMutableObjectAt(i)))
           return false;
       }
       return true;
     }
     case CPDF_Object::kStream: {
-      CPDF_Stream* pStream = pObj->AsStream();
-      CPDF_Dictionary* pDict = pStream->GetDict();
-      return pDict && UpdateReference(pDict);
+      CPDF_Stream* pStream = pObj->AsMutableStream();
+      RetainPtr<CPDF_Dictionary> pDict = pStream->GetMutableDict();
+      return pDict && UpdateReference(std::move(pDict));
     }
     default:
       return true;
@@ -410,24 +344,23 @@
   if (dwNewObjNum)
     return dwNewObjNum;
 
-  CPDF_Object* pDirect = pRef->GetDirect();
+  RetainPtr<const CPDF_Object> pDirect = pRef->GetDirect();
   if (!pDirect)
     return 0;
 
   RetainPtr<CPDF_Object> pClone = pDirect->Clone();
-  if (CPDF_Dictionary* pDictClone = pClone->AsDictionary()) {
-    if (pDictClone->KeyExist("Type")) {
-      ByteString strType = pDictClone->GetStringFor("Type");
-      if (!FXSYS_stricmp(strType.c_str(), "Pages"))
-        return 4;
-      if (!FXSYS_stricmp(strType.c_str(), "Page"))
-        return 0;
-    }
+  const CPDF_Dictionary* pDictClone = pClone->AsDictionary();
+  if (pDictClone && pDictClone->KeyExist("Type")) {
+    ByteString strType = pDictClone->GetByteStringFor("Type");
+    if (strType.EqualNoCase("Pages"))
+      return 4;
+    if (strType.EqualNoCase("Page"))
+      return 0;
   }
-  CPDF_Object* pUnownedClone = dest()->AddIndirectObject(std::move(pClone));
-  dwNewObjNum = pUnownedClone->GetObjNum();
+
+  dwNewObjNum = dest()->AddIndirectObject(pClone);
   AddObjectMapping(dwObjnum, dwNewObjNum);
-  if (!UpdateReference(pUnownedClone))
+  if (!UpdateReference(std::move(pClone)))
     return 0;
 
   return dwNewObjNum;
@@ -440,11 +373,10 @@
   CPDF_PageExporter(CPDF_Document* pDestDoc, CPDF_Document* pSrcDoc);
   ~CPDF_PageExporter();
 
-  // For the pages from the source document with |pageNums| as their page
-  // numbers, insert them into the destination document at page |nIndex|.
-  // |pageNums| is 1-based.
-  // |nIndex| is 0-based.
-  bool ExportPage(const std::vector<uint32_t>& pageNums, int nIndex);
+  // For the pages from the source document with |pageIndices| as their page
+  // indices, insert them into the destination document at page |nIndex|.
+  // |pageIndices| and |nIndex| are 0-based.
+  bool ExportPage(pdfium::span<const uint32_t> pageIndices, int nIndex);
 };
 
 CPDF_PageExporter::CPDF_PageExporter(CPDF_Document* pDestDoc,
@@ -453,15 +385,16 @@
 
 CPDF_PageExporter::~CPDF_PageExporter() = default;
 
-bool CPDF_PageExporter::ExportPage(const std::vector<uint32_t>& pageNums,
+bool CPDF_PageExporter::ExportPage(pdfium::span<const uint32_t> pageIndices,
                                    int nIndex) {
   if (!Init())
     return false;
 
   int curpage = nIndex;
-  for (size_t i = 0; i < pageNums.size(); ++i) {
-    CPDF_Dictionary* pDestPageDict = dest()->CreateNewPage(curpage);
-    auto* pSrcPageDict = src()->GetPageDictionary(pageNums[i] - 1);
+  for (uint32_t pageIndex : pageIndices) {
+    RetainPtr<CPDF_Dictionary> pDestPageDict = dest()->CreateNewPage(curpage);
+    RetainPtr<const CPDF_Dictionary> pSrcPageDict =
+        src()->GetPageDictionary(pageIndex);
     if (!pSrcPageDict || !pDestPageDict)
       return false;
 
@@ -469,12 +402,11 @@
     CPDF_DictionaryLocker locker(pSrcPageDict);
     for (const auto& it : locker) {
       const ByteString& cbSrcKeyStr = it.first;
+      const RetainPtr<CPDF_Object>& pObj = it.second;
       if (cbSrcKeyStr == pdfium::page_object::kType ||
           cbSrcKeyStr == pdfium::page_object::kParent) {
         continue;
       }
-
-      CPDF_Object* pObj = it.second.Get();
       pDestPageDict->SetFor(cbSrcKeyStr, pObj->Clone());
     }
 
@@ -486,7 +418,7 @@
                          pdfium::page_object::kMediaBox)) {
       // Search for "CropBox" in the source page dictionary.
       // If it does not exist, use the default letter size.
-      const CPDF_Object* pInheritable = PageDictGetInheritableTag(
+      RetainPtr<const CPDF_Object> pInheritable = PageDictGetInheritableTag(
           pSrcPageDict, pdfium::page_object::kCropBox);
       if (pInheritable) {
         pDestPageDict->SetFor(pdfium::page_object::kMediaBox,
@@ -531,37 +463,43 @@
   CPDF_NPageToOneExporter(CPDF_Document* pDestDoc, CPDF_Document* pSrcDoc);
   ~CPDF_NPageToOneExporter();
 
-  // For the pages from the source document with |pageNums| as their page
-  // numbers, insert them into the destination document, starting at page 0.
-  // |pageNums| is 1-based.
+  // For the pages from the source document with |pageIndices| as their page
+  // indices, insert them into the destination document, starting at page index
+  // 0.
+  // |pageIndices| is 0-based.
   // |destPageSize| is the destination document page dimensions, measured in
   // PDF "user space" units.
   // |nPagesOnXAxis| and |nPagesOnXAxis| together defines how many source
   // pages fit on one destination page.
-  bool ExportNPagesToOne(const std::vector<uint32_t>& pageNums,
+  bool ExportNPagesToOne(pdfium::span<const uint32_t> pageIndices,
                          const CFX_SizeF& destPageSize,
                          size_t nPagesOnXAxis,
                          size_t nPagesOnYAxis);
 
+  std::unique_ptr<XObjectContext> CreateXObjectContextFromPage(
+      int src_page_index);
+
  private:
   // Map page object number to XObject object name.
   using PageXObjectMap = std::map<uint32_t, ByteString>;
 
-  // Creates an XObject from |pSrcPageDict|, or find an existing XObject that
-  // represents |pSrcPageDict|. The transformation matrix is specified in
+  // Creates an XObject from |pSrcPage|, or find an existing XObject that
+  // represents |pSrcPage|. The transformation matrix is specified in
   // |settings|.
   // Returns the XObject reference surrounded by the transformation matrix.
-  ByteString AddSubPage(const CPDF_Dictionary* pSrcPageDict,
+  ByteString AddSubPage(const RetainPtr<CPDF_Page>& pSrcPage,
                         const NupPageSettings& settings);
 
-  // Creates an XObject from |pSrcPageDict|. Updates mapping as needed.
+  // Creates an XObject from |pSrcPage|. Updates mapping as needed.
   // Returns the name of the newly created XObject.
-  ByteString MakeXObjectFromPage(const CPDF_Dictionary* pSrcPageDict);
+  ByteString MakeXObjectFromPage(RetainPtr<CPDF_Page> pSrcPage);
+  RetainPtr<CPDF_Stream> MakeXObjectFromPageRaw(RetainPtr<CPDF_Page> pSrcPage);
 
   // Adds |bsContent| as the Contents key in |pDestPageDict|.
   // Adds the objects in |m_XObjectNameToNumberMap| to the XObject dictionary in
   // |pDestPageDict|'s Resources dictionary.
-  void FinishPage(CPDF_Dictionary* pDestPageDict, const ByteString& bsContent);
+  void FinishPage(RetainPtr<CPDF_Dictionary> pDestPageDict,
+                  const ByteString& bsContent);
 
   // Counter for giving new XObjects unique names.
   uint32_t m_nObjectNumber = 0;
@@ -583,7 +521,7 @@
 CPDF_NPageToOneExporter::~CPDF_NPageToOneExporter() = default;
 
 bool CPDF_NPageToOneExporter::ExportNPagesToOne(
-    const std::vector<uint32_t>& pageNums,
+    pdfium::span<const uint32_t> pageIndices,
     const CFX_SizeF& destPageSize,
     size_t nPagesOnXAxis,
     size_t nPagesOnYAxis) {
@@ -600,33 +538,33 @@
   size_t nPagesPerSheet = nSafePagesPerSheet.ValueOrDie();
   NupState nupState(destPageSize, nPagesOnXAxis, nPagesOnYAxis);
 
-  size_t curpage = 0;
+  FX_SAFE_INT32 curpage = 0;
   const CFX_FloatRect destPageRect(0, 0, destPageSize.width,
                                    destPageSize.height);
-  for (size_t iOuterPage = 0; iOuterPage < pageNums.size();
+  for (size_t iOuterPage = 0; iOuterPage < pageIndices.size();
        iOuterPage += nPagesPerSheet) {
     m_XObjectNameToNumberMap.clear();
 
-    // Create a new page
-    CPDF_Dictionary* pDestPageDict = dest()->CreateNewPage(curpage);
+    RetainPtr<CPDF_Dictionary> pDestPageDict =
+        dest()->CreateNewPage(curpage.ValueOrDie());
     if (!pDestPageDict)
       return false;
 
     pDestPageDict->SetRectFor(pdfium::page_object::kMediaBox, destPageRect);
     ByteString bsContent;
     size_t iInnerPageMax =
-        std::min(iOuterPage + nPagesPerSheet, pageNums.size());
+        std::min(iOuterPage + nPagesPerSheet, pageIndices.size());
     for (size_t i = iOuterPage; i < iInnerPageMax; ++i) {
-      auto* pSrcPageDict = src()->GetPageDictionary(pageNums[i] - 1);
+      RetainPtr<CPDF_Dictionary> pSrcPageDict =
+          src()->GetMutablePageDictionary(pageIndices[i]);
       if (!pSrcPageDict)
         return false;
 
       auto pSrcPage = pdfium::MakeRetain<CPDF_Page>(src(), pSrcPageDict);
-      pSrcPage->SetRenderCache(
-          pdfium::MakeUnique<CPDF_PageRenderCache>(pSrcPage.Get()));
+      pSrcPage->AddPageImageCache();
       NupPageSettings settings =
           nupState.CalculateNewPagePosition(pSrcPage->GetPageSize());
-      bsContent += AddSubPage(pSrcPageDict, settings);
+      bsContent += AddSubPage(pSrcPage, settings);
     }
 
     FinishPage(pDestPageDict, bsContent);
@@ -637,19 +575,19 @@
 }
 
 ByteString CPDF_NPageToOneExporter::AddSubPage(
-    const CPDF_Dictionary* pSrcPageDict,
+    const RetainPtr<CPDF_Page>& pSrcPage,
     const NupPageSettings& settings) {
-  uint32_t dwSrcPageObjnum = pSrcPageDict->GetObjNum();
+  uint32_t dwSrcPageObjnum = pSrcPage->GetDict()->GetObjNum();
   const auto it = m_SrcPageXObjectMap.find(dwSrcPageObjnum);
   ByteString bsXObjectName = it != m_SrcPageXObjectMap.end()
                                  ? it->second
-                                 : MakeXObjectFromPage(pSrcPageDict);
+                                 : MakeXObjectFromPage(pSrcPage);
 
   CFX_Matrix matrix;
   matrix.Scale(settings.scale, settings.scale);
   matrix.Translate(settings.subPageStartPoint.x, settings.subPageStartPoint.y);
 
-  std::ostringstream contentStream;
+  fxcrt::ostringstream contentStream;
   contentStream << "q\n"
                 << matrix.a << " " << matrix.b << " " << matrix.c << " "
                 << matrix.d << " " << matrix.e << " " << matrix.f << " cm\n"
@@ -657,16 +595,15 @@
   return ByteString(contentStream);
 }
 
-ByteString CPDF_NPageToOneExporter::MakeXObjectFromPage(
-    const CPDF_Dictionary* pSrcPageDict) {
-  ASSERT(pSrcPageDict);
-
-  const CPDF_Object* pSrcContentObj =
+RetainPtr<CPDF_Stream> CPDF_NPageToOneExporter::MakeXObjectFromPageRaw(
+    RetainPtr<CPDF_Page> pSrcPage) {
+  RetainPtr<const CPDF_Dictionary> pSrcPageDict = pSrcPage->GetDict();
+  RetainPtr<const CPDF_Object> pSrcContentObj =
       pSrcPageDict->GetDirectObjectFor(pdfium::page_object::kContents);
 
-  CPDF_Stream* pNewXObject = dest()->NewIndirect<CPDF_Stream>(
-      nullptr, 0, dest()->New<CPDF_Dictionary>());
-  CPDF_Dictionary* pNewXObjectDict = pNewXObject->GetDict();
+  auto pNewXObject =
+      dest()->NewIndirect<CPDF_Stream>(dest()->New<CPDF_Dictionary>());
+  RetainPtr<CPDF_Dictionary> pNewXObjectDict = pNewXObject->GetMutableDict();
   static const char kResourceString[] = "Resources";
   if (!CopyInheritable(pNewXObjectDict, pSrcPageDict, kResourceString)) {
     // Use a default empty resources if it does not exist.
@@ -676,68 +613,135 @@
   uint32_t dwNewXobjectObj = pNewXObjectDict->GetObjNum();
   AddObjectMapping(dwSrcPageObj, dwNewXobjectObj);
   UpdateReference(pNewXObjectDict);
-
   pNewXObjectDict->SetNewFor<CPDF_Name>("Type", "XObject");
   pNewXObjectDict->SetNewFor<CPDF_Name>("Subtype", "Form");
   pNewXObjectDict->SetNewFor<CPDF_Number>("FormType", 1);
-  pNewXObjectDict->SetRectFor("BBox", GetCropBox(pSrcPageDict));
-  // TODO(xlou): add matrix field to pNewXObjectDict.
+  pNewXObjectDict->SetRectFor("BBox", pSrcPage->GetBBox());
+  pNewXObjectDict->SetMatrixFor("Matrix", pSrcPage->GetPageMatrix());
 
   if (pSrcContentObj) {
     ByteString bsSrcContentStream;
-    const CPDF_Array* pSrcContentArray = ToArray(pSrcContentObj);
+    const CPDF_Array* pSrcContentArray = pSrcContentObj->AsArray();
     if (pSrcContentArray) {
       for (size_t i = 0; i < pSrcContentArray->size(); ++i) {
-        const CPDF_Stream* pStream = pSrcContentArray->GetStreamAt(i);
-        auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
+        RetainPtr<const CPDF_Stream> pStream = pSrcContentArray->GetStreamAt(i);
+        auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(pStream));
         pAcc->LoadAllDataFiltered();
         bsSrcContentStream += ByteString(pAcc->GetSpan());
         bsSrcContentStream += "\n";
       }
     } else {
-      const CPDF_Stream* pStream = pSrcContentObj->AsStream();
-      auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
+      RetainPtr<const CPDF_Stream> pStream(pSrcContentObj->AsStream());
+      auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(pStream));
       pAcc->LoadAllDataFiltered();
       bsSrcContentStream = ByteString(pAcc->GetSpan());
     }
     pNewXObject->SetDataAndRemoveFilter(bsSrcContentStream.raw_span());
   }
+  return pNewXObject;
+}
+
+ByteString CPDF_NPageToOneExporter::MakeXObjectFromPage(
+    RetainPtr<CPDF_Page> pSrcPage) {
+  RetainPtr<CPDF_Stream> pNewXObject = MakeXObjectFromPageRaw(pSrcPage);
 
   // TODO(xlou): A better name schema to avoid possible object name collision.
   ByteString bsXObjectName = ByteString::Format("X%d", ++m_nObjectNumber);
   m_XObjectNameToNumberMap[bsXObjectName] = pNewXObject->GetObjNum();
-  m_SrcPageXObjectMap[pSrcPageDict->GetObjNum()] = bsXObjectName;
+  m_SrcPageXObjectMap[pSrcPage->GetDict()->GetObjNum()] = bsXObjectName;
   return bsXObjectName;
 }
 
-void CPDF_NPageToOneExporter::FinishPage(CPDF_Dictionary* pDestPageDict,
-                                         const ByteString& bsContent) {
-  ASSERT(pDestPageDict);
+std::unique_ptr<XObjectContext>
+CPDF_NPageToOneExporter::CreateXObjectContextFromPage(int src_page_index) {
+  RetainPtr<CPDF_Dictionary> src_page_dict =
+      src()->GetMutablePageDictionary(src_page_index);
+  if (!src_page_dict)
+    return nullptr;
 
-  CPDF_Dictionary* pRes =
-      pDestPageDict->GetDictFor(pdfium::page_object::kResources);
-  if (!pRes) {
-    pRes = pDestPageDict->SetNewFor<CPDF_Dictionary>(
-        pdfium::page_object::kResources);
-  }
+  auto src_page = pdfium::MakeRetain<CPDF_Page>(src(), src_page_dict);
+  auto xobject = std::make_unique<XObjectContext>();
+  xobject->dest_doc = dest();
+  xobject->xobject.Reset(MakeXObjectFromPageRaw(src_page));
+  return xobject;
+}
 
-  CPDF_Dictionary* pPageXObject = pRes->GetDictFor("XObject");
-  if (!pPageXObject)
-    pPageXObject = pRes->SetNewFor<CPDF_Dictionary>("XObject");
-
+void CPDF_NPageToOneExporter::FinishPage(
+    RetainPtr<CPDF_Dictionary> pDestPageDict,
+    const ByteString& bsContent) {
+  RetainPtr<CPDF_Dictionary> pRes =
+      pDestPageDict->GetOrCreateDictFor(pdfium::page_object::kResources);
+  RetainPtr<CPDF_Dictionary> pPageXObject = pRes->GetOrCreateDictFor("XObject");
   for (auto& it : m_XObjectNameToNumberMap)
     pPageXObject->SetNewFor<CPDF_Reference>(it.first, dest(), it.second);
 
-  auto pDict = dest()->New<CPDF_Dictionary>();
-  CPDF_Stream* pStream =
-      dest()->NewIndirect<CPDF_Stream>(nullptr, 0, std::move(pDict));
+  auto pStream =
+      dest()->NewIndirect<CPDF_Stream>(dest()->New<CPDF_Dictionary>());
   pStream->SetData(bsContent.raw_span());
   pDestPageDict->SetNewFor<CPDF_Reference>(pdfium::page_object::kContents,
                                            dest(), pStream->GetObjNum());
 }
 
+// Make sure arrays only contain objects of basic types.
+bool IsValidViewerPreferencesArray(const CPDF_Array* array) {
+  CPDF_ArrayLocker locker(array);
+  for (const auto& obj : locker) {
+    if (obj->IsArray() || obj->IsDictionary() || obj->IsReference() ||
+        obj->IsStream()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool IsValidViewerPreferencesObject(const CPDF_Object* obj) {
+  // Per spec, there are no valid entries of these types.
+  if (obj->IsDictionary() || obj->IsNull() || obj->IsReference() ||
+      obj->IsStream()) {
+    return false;
+  }
+
+  const CPDF_Array* array = obj->AsArray();
+  if (!array) {
+    return true;
+  }
+
+  return IsValidViewerPreferencesArray(array);
+}
+
 }  // namespace
 
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_ImportPagesByIndex(FPDF_DOCUMENT dest_doc,
+                        FPDF_DOCUMENT src_doc,
+                        const int* page_indices,
+                        unsigned long length,
+                        int index) {
+  CPDF_Document* pDestDoc = CPDFDocumentFromFPDFDocument(dest_doc);
+  if (!dest_doc)
+    return false;
+
+  CPDF_Document* pSrcDoc = CPDFDocumentFromFPDFDocument(src_doc);
+  if (!pSrcDoc)
+    return false;
+
+  CPDF_PageExporter exporter(pDestDoc, pSrcDoc);
+
+  if (!page_indices) {
+    std::vector<uint32_t> page_indices_vec(pSrcDoc->GetPageCount());
+    std::iota(page_indices_vec.begin(), page_indices_vec.end(), 0);
+    return exporter.ExportPage(page_indices_vec, index);
+  }
+
+  if (length == 0)
+    return false;
+
+  return exporter.ExportPage(
+      pdfium::make_span(reinterpret_cast<const uint32_t*>(page_indices),
+                        length),
+      index);
+}
+
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_ImportPages(FPDF_DOCUMENT dest_doc,
                                                      FPDF_DOCUMENT src_doc,
                                                      FPDF_BYTESTRING pagerange,
@@ -750,12 +754,12 @@
   if (!pSrcDoc)
     return false;
 
-  std::vector<uint32_t> page_numbers = GetPageNumbers(*pSrcDoc, pagerange);
-  if (page_numbers.empty())
+  std::vector<uint32_t> page_indices = GetPageIndices(*pSrcDoc, pagerange);
+  if (page_indices.empty())
     return false;
 
   CPDF_PageExporter exporter(pDestDoc, pSrcDoc);
-  return exporter.ExportPage(page_numbers, index);
+  return exporter.ExportPage(page_indices, index);
 }
 
 FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
@@ -778,21 +782,21 @@
     return nullptr;
 
   CPDF_Document* pDestDoc = CPDFDocumentFromFPDFDocument(output_doc.get());
-  ASSERT(pDestDoc);
+  DCHECK(pDestDoc);
 
-  std::vector<uint32_t> page_numbers = GetPageNumbers(*pSrcDoc, ByteString());
-  if (page_numbers.empty())
+  std::vector<uint32_t> page_indices = GetPageIndices(*pSrcDoc, ByteString());
+  if (page_indices.empty())
     return nullptr;
 
   if (num_pages_on_x_axis == 1 && num_pages_on_y_axis == 1) {
     CPDF_PageExporter exporter(pDestDoc, pSrcDoc);
-    if (!exporter.ExportPage(page_numbers, 0))
+    if (!exporter.ExportPage(page_indices, 0))
       return nullptr;
     return output_doc.release();
   }
 
   CPDF_NPageToOneExporter exporter(pDestDoc, pSrcDoc);
-  if (!exporter.ExportNPagesToOne(page_numbers,
+  if (!exporter.ExportNPagesToOne(page_indices,
                                   CFX_SizeF(output_width, output_height),
                                   num_pages_on_x_axis, num_pages_on_y_axis)) {
     return nullptr;
@@ -800,6 +804,43 @@
   return output_doc.release();
 }
 
+FPDF_EXPORT FPDF_XOBJECT FPDF_CALLCONV
+FPDF_NewXObjectFromPage(FPDF_DOCUMENT dest_doc,
+                        FPDF_DOCUMENT src_doc,
+                        int src_page_index) {
+  CPDF_Document* dest = CPDFDocumentFromFPDFDocument(dest_doc);
+  if (!dest)
+    return nullptr;
+
+  CPDF_Document* src = CPDFDocumentFromFPDFDocument(src_doc);
+  if (!src)
+    return nullptr;
+
+  CPDF_NPageToOneExporter exporter(dest, src);
+  std::unique_ptr<XObjectContext> xobject =
+      exporter.CreateXObjectContextFromPage(src_page_index);
+  return FPDFXObjectFromXObjectContext(xobject.release());
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDF_CloseXObject(FPDF_XOBJECT xobject) {
+  std::unique_ptr<XObjectContext> xobject_deleter(
+      XObjectContextFromFPDFXObject(xobject));
+}
+
+FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
+FPDF_NewFormObjectFromXObject(FPDF_XOBJECT xobject) {
+  XObjectContext* xobj = XObjectContextFromFPDFXObject(xobject);
+  if (!xobj)
+    return nullptr;
+
+  auto form = std::make_unique<CPDF_Form>(xobj->dest_doc, nullptr,
+                                          xobj->xobject, nullptr);
+  form->ParseContent(nullptr, nullptr, nullptr);
+  auto form_object = std::make_unique<CPDF_FormObject>(
+      CPDF_PageObject::kNoContentStream, std::move(form), CFX_Matrix());
+  return FPDFPageObjectFromCPDFPageObject(form_object.release());
+}
+
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDF_CopyViewerPreferences(FPDF_DOCUMENT dest_doc, FPDF_DOCUMENT src_doc) {
   CPDF_Document* pDstDoc = CPDFDocumentFromFPDFDocument(dest_doc);
@@ -810,15 +851,23 @@
   if (!pSrcDoc)
     return false;
 
-  const CPDF_Dictionary* pSrcDict = pSrcDoc->GetRoot();
-  pSrcDict = pSrcDict->GetDictFor("ViewerPreferences");
-  if (!pSrcDict)
+  RetainPtr<const CPDF_Dictionary> pPrefDict =
+      pSrcDoc->GetRoot()->GetDictFor("ViewerPreferences");
+  if (!pPrefDict)
     return false;
 
-  CPDF_Dictionary* pDstDict = pDstDoc->GetRoot();
+  RetainPtr<CPDF_Dictionary> pDstDict = pDstDoc->GetMutableRoot();
   if (!pDstDict)
     return false;
 
-  pDstDict->SetFor("ViewerPreferences", pSrcDict->CloneDirectObject());
+  auto cloned_dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  CPDF_DictionaryLocker locker(pPrefDict);
+  for (const auto& it : locker) {
+    if (IsValidViewerPreferencesObject(it.second)) {
+      cloned_dict->SetFor(it.first, it.second->Clone());
+    }
+  }
+
+  pDstDict->SetFor("ViewerPreferences", std::move(cloned_dict));
   return true;
 }
diff --git a/fpdfsdk/fpdf_ppo_embeddertest.cpp b/fpdfsdk/fpdf_ppo_embeddertest.cpp
index 5684680..5750947 100644
--- a/fpdfsdk/fpdf_ppo_embeddertest.cpp
+++ b/fpdfsdk/fpdf_ppo_embeddertest.cpp
@@ -1,16 +1,27 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
-#include <string>
+#include <iterator>
 
+#include "core/fpdfapi/page/cpdf_form.h"
+#include "core/fpdfapi/page/cpdf_formobject.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
 #include "public/cpp/fpdf_scopers.h"
 #include "public/fpdf_edit.h"
 #include "public/fpdf_ppo.h"
 #include "public/fpdf_save.h"
 #include "public/fpdfview.h"
 #include "testing/embedder_test.h"
+#include "testing/embedder_test_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -20,13 +31,40 @@
 int FakeBlockWriter(FPDF_FILEWRITE* pThis,
                     const void* pData,
                     unsigned long size) {
-  return size;
+  return 1;  // Always succeeds.
+}
+
+constexpr int kRectanglesMultiPagesPageCount = 2;
+
+const char* RectanglesMultiPagesExpectedChecksum(int page_index) {
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    static constexpr const char* kChecksums[kRectanglesMultiPagesPageCount] = {
+        "07606a12487bd0c28a88f23fa00fc313", "94ea6e1eef220833a3ec14d6a1c612b0"};
+    return kChecksums[page_index];
+  }
+  static constexpr const char* kChecksums[kRectanglesMultiPagesPageCount] = {
+      "72d0d7a19a2f40e010ca6a1133b33e1e", "fb18142190d770cfbc329d2b071aee4d"};
+  return kChecksums[page_index];
+}
+
+const char* Bug750568PageHash(int page_index) {
+  constexpr int kBug750568PageCount = 4;
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    static constexpr const char* kChecksums[kBug750568PageCount] = {
+        "eaa139e944eafb43d31e8742a0e158de", "226485e9d4fa6a67dfe0a88723f12060",
+        "c5601a3492ae5dcc5dd25140fc463bfe", "1f60055b54de4fac8a59c65e90da156e"};
+    return kChecksums[page_index];
+  }
+  static constexpr const char* kChecksums[kBug750568PageCount] = {
+      "64ad08132a1c5a166768298c8a578f57", "83b83e2f6bc80707d0a917c7634140b9",
+      "913cd3723a451e4e46fbc2c05702d1ee", "81fb7cfd4860f855eb468f73dfeb6d60"};
+  return kChecksums[page_index];
 }
 
 }  // namespace
 
 TEST_F(FPDFPPOEmbedderTest, NoViewerPreferences) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
 
   FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
   EXPECT_TRUE(output_doc);
@@ -35,7 +73,7 @@
 }
 
 TEST_F(FPDFPPOEmbedderTest, ViewerPreferences) {
-  EXPECT_TRUE(OpenDocument("viewer_ref.pdf"));
+  ASSERT_TRUE(OpenDocument("viewer_ref.pdf"));
 
   FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
   EXPECT_TRUE(output_doc);
@@ -43,6 +81,24 @@
   FPDF_CloseDocument(output_doc);
 }
 
+TEST_F(FPDFPPOEmbedderTest, ImportPagesByIndex) {
+  ASSERT_TRUE(OpenDocument("viewer_ref.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+
+  ScopedFPDFDocument output_doc(FPDF_CreateNewDocument());
+  ASSERT_TRUE(output_doc);
+  EXPECT_TRUE(FPDF_CopyViewerPreferences(output_doc.get(), document()));
+
+  static constexpr int kPageIndices[] = {1};
+  EXPECT_TRUE(FPDF_ImportPagesByIndex(
+      output_doc.get(), document(), kPageIndices, std::size(kPageIndices), 0));
+  EXPECT_EQ(1, FPDF_GetPageCount(output_doc.get()));
+
+  UnloadPage(page);
+}
+
 TEST_F(FPDFPPOEmbedderTest, ImportPages) {
   ASSERT_TRUE(OpenDocument("viewer_ref.pdf"));
 
@@ -99,31 +155,194 @@
 
 // TODO(Xlou): Add more tests to check output doc content of
 // FPDF_ImportNPagesToOne()
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_NupRenderImage DISABLED_NupRenderImage
-#else
-#define MAYBE_NupRenderImage NupRenderImage
-#endif
-TEST_F(FPDFPPOEmbedderTest, MAYBE_NupRenderImage) {
+TEST_F(FPDFPPOEmbedderTest, NupRenderImage) {
   ASSERT_TRUE(OpenDocument("rectangles_multi_pages.pdf"));
-  const int kPageCount = 2;
-  static constexpr const char* kExpectedMD5s[kPageCount] = {
-      "4d225b961da0f1bced7c83273e64c9b6", "fb18142190d770cfbc329d2b071aee4d"};
   ScopedFPDFDocument output_doc_3up(
       FPDF_ImportNPagesToOne(document(), 792, 612, 3, 1));
   ASSERT_TRUE(output_doc_3up);
-  ASSERT_EQ(kPageCount, FPDF_GetPageCount(output_doc_3up.get()));
-  for (int i = 0; i < kPageCount; ++i) {
+  ASSERT_EQ(kRectanglesMultiPagesPageCount,
+            FPDF_GetPageCount(output_doc_3up.get()));
+  for (int i = 0; i < kRectanglesMultiPagesPageCount; ++i) {
     ScopedFPDFPage page(FPDF_LoadPage(output_doc_3up.get(), i));
     ASSERT_TRUE(page);
     ScopedFPDFBitmap bitmap = RenderPage(page.get());
     EXPECT_EQ(792, FPDFBitmap_GetWidth(bitmap.get()));
     EXPECT_EQ(612, FPDFBitmap_GetHeight(bitmap.get()));
-    EXPECT_EQ(kExpectedMD5s[i], HashBitmap(bitmap.get()));
+    EXPECT_EQ(RectanglesMultiPagesExpectedChecksum(i),
+              HashBitmap(bitmap.get()));
   }
 }
 
+TEST_F(FPDFPPOEmbedderTest, ImportPageToXObject) {
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "d6ebc0a8afc22fe0137f54ce54e1a19c";
+    return "2d88d180af7109eb346439f7c855bb29";
+  }();
+
+  ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+
+  {
+    ScopedFPDFDocument output_doc(FPDF_CreateNewDocument());
+    ASSERT_TRUE(output_doc);
+
+    FPDF_XOBJECT xobject =
+        FPDF_NewXObjectFromPage(output_doc.get(), document(), 0);
+    ASSERT_TRUE(xobject);
+
+    for (int i = 0; i < 2; ++i) {
+      ScopedFPDFPage page(FPDFPage_New(output_doc.get(), 0, 612, 792));
+      ASSERT_TRUE(page);
+
+      FPDF_PAGEOBJECT page_object = FPDF_NewFormObjectFromXObject(xobject);
+      ASSERT_TRUE(page_object);
+      EXPECT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(page_object));
+      FPDFPage_InsertObject(page.get(), page_object);
+      EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
+
+      ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
+      CompareBitmap(page_bitmap.get(), 612, 792, checksum);
+
+      float left;
+      float bottom;
+      float right;
+      float top;
+      ASSERT_TRUE(
+          FPDFPageObj_GetBounds(page_object, &left, &bottom, &right, &top));
+      EXPECT_FLOAT_EQ(-1.0f, left);
+      EXPECT_FLOAT_EQ(-1.0f, bottom);
+      EXPECT_FLOAT_EQ(201.0f, right);
+      EXPECT_FLOAT_EQ(301.0f, top);
+    }
+
+    EXPECT_TRUE(FPDF_SaveAsCopy(output_doc.get(), this, 0));
+
+    FPDF_CloseXObject(xobject);
+  }
+
+  constexpr int kExpectedPageCount = 2;
+  ASSERT_TRUE(OpenSavedDocument());
+
+  FPDF_PAGE saved_pages[kExpectedPageCount];
+  FPDF_PAGEOBJECT xobjects[kExpectedPageCount];
+  for (int i = 0; i < kExpectedPageCount; ++i) {
+    saved_pages[i] = LoadSavedPage(i);
+    ASSERT_TRUE(saved_pages[i]);
+
+    EXPECT_EQ(1, FPDFPage_CountObjects(saved_pages[i]));
+    xobjects[i] = FPDFPage_GetObject(saved_pages[i], 0);
+    ASSERT_TRUE(xobjects[i]);
+    ASSERT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(xobjects[i]));
+    EXPECT_EQ(8, FPDFFormObj_CountObjects(xobjects[i]));
+
+    {
+      ScopedFPDFBitmap page_bitmap = RenderPage(saved_pages[i]);
+      CompareBitmap(page_bitmap.get(), 612, 792, checksum);
+    }
+  }
+
+  for (int i = 0; i < kExpectedPageCount; ++i) {
+    float left;
+    float bottom;
+    float right;
+    float top;
+    ASSERT_TRUE(
+        FPDFPageObj_GetBounds(xobjects[i], &left, &bottom, &right, &top));
+    EXPECT_FLOAT_EQ(-1.0f, left);
+    EXPECT_FLOAT_EQ(-1.0f, bottom);
+    EXPECT_FLOAT_EQ(201.0f, right);
+    EXPECT_FLOAT_EQ(301.0f, top);
+  }
+
+  // Peek at object internals to make sure the two XObjects use the same stream.
+  EXPECT_NE(xobjects[0], xobjects[1]);
+  CPDF_PageObject* obj1 = CPDFPageObjectFromFPDFPageObject(xobjects[0]);
+  ASSERT_TRUE(obj1->AsForm());
+  ASSERT_TRUE(obj1->AsForm()->form());
+  ASSERT_TRUE(obj1->AsForm()->form()->GetStream());
+  CPDF_PageObject* obj2 = CPDFPageObjectFromFPDFPageObject(xobjects[1]);
+  ASSERT_TRUE(obj2->AsForm());
+  ASSERT_TRUE(obj2->AsForm()->form());
+  ASSERT_TRUE(obj2->AsForm()->form()->GetStream());
+  EXPECT_EQ(obj1->AsForm()->form()->GetStream(),
+            obj2->AsForm()->form()->GetStream());
+
+  for (FPDF_PAGE saved_page : saved_pages)
+    CloseSavedPage(saved_page);
+
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFPPOEmbedderTest, ImportPageToXObjectWithSameDoc) {
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "8e7d672f49f9ca98fb9157824cefc204";
+    return "4d5ca14827b7707f8283e639b33c121a";
+  }();
+
+  ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+
+  FPDF_XOBJECT xobject = FPDF_NewXObjectFromPage(document(), document(), 0);
+  ASSERT_TRUE(xobject);
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    CompareBitmap(bitmap.get(), 200, 300, pdfium::RectanglesChecksum());
+  }
+
+  FPDF_PAGEOBJECT page_object = FPDF_NewFormObjectFromXObject(xobject);
+  ASSERT_TRUE(page_object);
+  ASSERT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(page_object));
+
+  static constexpr FS_MATRIX kMatrix = {0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f};
+  EXPECT_TRUE(FPDFPageObj_SetMatrix(page_object, &kMatrix));
+
+  FPDFPage_InsertObject(page, page_object);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    CompareBitmap(bitmap.get(), 200, 300, checksum);
+  }
+
+  FPDF_CloseXObject(xobject);
+
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedDocument(200, 300, checksum);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFPPOEmbedderTest, XObjectNullParams) {
+  ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+  ASSERT_EQ(1, FPDF_GetPageCount(document()));
+
+  EXPECT_FALSE(FPDF_NewXObjectFromPage(nullptr, nullptr, -1));
+  EXPECT_FALSE(FPDF_NewXObjectFromPage(nullptr, nullptr, 0));
+  EXPECT_FALSE(FPDF_NewXObjectFromPage(nullptr, nullptr, 1));
+  EXPECT_FALSE(FPDF_NewXObjectFromPage(document(), nullptr, -1));
+  EXPECT_FALSE(FPDF_NewXObjectFromPage(document(), nullptr, 0));
+  EXPECT_FALSE(FPDF_NewXObjectFromPage(document(), nullptr, 1));
+  EXPECT_FALSE(FPDF_NewXObjectFromPage(nullptr, document(), -1));
+  EXPECT_FALSE(FPDF_NewXObjectFromPage(nullptr, document(), 0));
+  EXPECT_FALSE(FPDF_NewXObjectFromPage(nullptr, document(), 1));
+
+  {
+    ScopedFPDFDocument output_doc(FPDF_CreateNewDocument());
+    ASSERT_TRUE(output_doc);
+    EXPECT_FALSE(FPDF_NewXObjectFromPage(output_doc.get(), document(), -1));
+    EXPECT_FALSE(FPDF_NewXObjectFromPage(output_doc.get(), document(), 1));
+  }
+
+  // Should be a no-op.
+  FPDF_CloseXObject(nullptr);
+
+  EXPECT_FALSE(FPDF_NewFormObjectFromXObject(nullptr));
+}
+
 TEST_F(FPDFPPOEmbedderTest, BUG_925981) {
   ASSERT_TRUE(OpenDocument("bug_925981.pdf"));
   ScopedFPDFDocument output_doc_2up(
@@ -131,6 +350,34 @@
   EXPECT_EQ(1, FPDF_GetPageCount(output_doc_2up.get()));
 }
 
+TEST_F(FPDFPPOEmbedderTest, BUG_1229106) {
+  static constexpr int kPageCount = 4;
+  static constexpr int kTwoUpPageCount = 2;
+  static const char kRectsChecksum[] = "140d629b3c96a07ced2e3e408ea85a1d";
+  static const char kTwoUpChecksum[] = "fa4501562301b2e75da354bd067495ec";
+
+  ASSERT_TRUE(OpenDocument("bug_1229106.pdf"));
+
+  // Show all pages render the same.
+  ASSERT_EQ(kPageCount, FPDF_GetPageCount(document()));
+  for (int i = 0; i < kPageCount; ++i) {
+    FPDF_PAGE page = LoadPage(0);
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    CompareBitmap(bitmap.get(), 792, 612, kRectsChecksum);
+    UnloadPage(page);
+  }
+
+  // Create a 2-up PDF.
+  ScopedFPDFDocument output_doc_2up(
+      FPDF_ImportNPagesToOne(document(), 612, 792, 1, 2));
+  ASSERT_EQ(kTwoUpPageCount, FPDF_GetPageCount(output_doc_2up.get()));
+  for (int i = 0; i < kTwoUpPageCount; ++i) {
+    ScopedFPDFPage page(FPDF_LoadPage(output_doc_2up.get(), i));
+    ScopedFPDFBitmap bitmap = RenderPage(page.get());
+    CompareBitmap(bitmap.get(), 612, 792, kTwoUpChecksum);
+  }
+}
+
 TEST_F(FPDFPPOEmbedderTest, BadRepeatViewerPref) {
   ASSERT_TRUE(OpenDocument("repeat_viewer_ref.pdf"));
 
@@ -161,8 +408,117 @@
   FPDF_CloseDocument(output_doc);
 }
 
+TEST_F(FPDFPPOEmbedderTest, CopyViewerPrefTypes) {
+  ASSERT_TRUE(OpenDocument("viewer_pref_types.pdf"));
+
+  ScopedFPDFDocument output_doc(FPDF_CreateNewDocument());
+  ASSERT_TRUE(output_doc);
+  EXPECT_TRUE(FPDF_CopyViewerPreferences(output_doc.get(), document()));
+
+  // Peek under the hook to check the result.
+  const CPDF_Document* output_doc_impl =
+      CPDFDocumentFromFPDFDocument(output_doc.get());
+  RetainPtr<const CPDF_Dictionary> prefs =
+      output_doc_impl->GetRoot()->GetDictFor("ViewerPreferences");
+  ASSERT_TRUE(prefs);
+  EXPECT_EQ(6u, prefs->size());
+
+  RetainPtr<const CPDF_Object> bool_obj = prefs->GetObjectFor("Bool");
+  ASSERT_TRUE(bool_obj);
+  EXPECT_TRUE(bool_obj->IsBoolean());
+
+  RetainPtr<const CPDF_Number> num_obj = prefs->GetNumberFor("Num");
+  ASSERT_TRUE(num_obj);
+  EXPECT_TRUE(num_obj->IsInteger());
+  EXPECT_EQ(1, num_obj->GetInteger());
+
+  RetainPtr<const CPDF_String> str_obj = prefs->GetStringFor("Str");
+  ASSERT_TRUE(str_obj);
+  EXPECT_EQ("str", str_obj->GetString());
+
+  EXPECT_EQ("name", prefs->GetNameFor("Name"));
+
+  RetainPtr<const CPDF_Array> empty_array_obj =
+      prefs->GetArrayFor("EmptyArray");
+  ASSERT_TRUE(empty_array_obj);
+  EXPECT_TRUE(empty_array_obj->IsEmpty());
+
+  RetainPtr<const CPDF_Array> good_array_obj = prefs->GetArrayFor("GoodArray");
+  ASSERT_TRUE(good_array_obj);
+  EXPECT_EQ(4u, good_array_obj->size());
+}
+
+TEST_F(FPDFPPOEmbedderTest, BadIndices) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+
+  ScopedFPDFDocument output_doc(FPDF_CreateNewDocument());
+  EXPECT_TRUE(output_doc);
+
+  static constexpr int kBadIndices1[] = {-1};
+  EXPECT_FALSE(FPDF_ImportPagesByIndex(
+      output_doc.get(), document(), kBadIndices1, std::size(kBadIndices1), 0));
+
+  static constexpr int kBadIndices2[] = {1};
+  EXPECT_FALSE(FPDF_ImportPagesByIndex(
+      output_doc.get(), document(), kBadIndices2, std::size(kBadIndices2), 0));
+
+  static constexpr int kBadIndices3[] = {-1, 0, 1};
+  EXPECT_FALSE(FPDF_ImportPagesByIndex(
+      output_doc.get(), document(), kBadIndices3, std::size(kBadIndices3), 0));
+
+  static constexpr int kBadIndices4[] = {42};
+  EXPECT_FALSE(FPDF_ImportPagesByIndex(
+      output_doc.get(), document(), kBadIndices4, std::size(kBadIndices4), 0));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFPPOEmbedderTest, GoodIndices) {
+  ASSERT_TRUE(OpenDocument("viewer_ref.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+
+  ScopedFPDFDocument output_doc(FPDF_CreateNewDocument());
+  EXPECT_TRUE(output_doc);
+
+  static constexpr int kGoodIndices1[] = {0, 0, 0, 0};
+  EXPECT_TRUE(FPDF_ImportPagesByIndex(output_doc.get(), document(),
+                                      kGoodIndices1, std::size(kGoodIndices1),
+                                      0));
+  EXPECT_EQ(4, FPDF_GetPageCount(output_doc.get()));
+
+  static constexpr int kGoodIndices2[] = {0};
+  EXPECT_TRUE(FPDF_ImportPagesByIndex(output_doc.get(), document(),
+                                      kGoodIndices2, std::size(kGoodIndices2),
+                                      0));
+  EXPECT_EQ(5, FPDF_GetPageCount(output_doc.get()));
+
+  static constexpr int kGoodIndices3[] = {4};
+  EXPECT_TRUE(FPDF_ImportPagesByIndex(output_doc.get(), document(),
+                                      kGoodIndices3, std::size(kGoodIndices3),
+                                      0));
+  EXPECT_EQ(6, FPDF_GetPageCount(output_doc.get()));
+
+  static constexpr int kGoodIndices4[] = {1, 2, 3};
+  EXPECT_TRUE(FPDF_ImportPagesByIndex(output_doc.get(), document(),
+                                      kGoodIndices4, std::size(kGoodIndices4),
+                                      0));
+  EXPECT_EQ(9, FPDF_GetPageCount(output_doc.get()));
+
+  // Passing in a nullptr should import all the pages.
+  EXPECT_TRUE(
+      FPDF_ImportPagesByIndex(output_doc.get(), document(), nullptr, 0, 0));
+  EXPECT_EQ(14, FPDF_GetPageCount(output_doc.get()));
+
+  UnloadPage(page);
+}
+
 TEST_F(FPDFPPOEmbedderTest, BadRanges) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
 
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
@@ -185,7 +541,7 @@
 }
 
 TEST_F(FPDFPPOEmbedderTest, GoodRanges) {
-  EXPECT_TRUE(OpenDocument("viewer_ref.pdf"));
+  ASSERT_TRUE(OpenDocument("viewer_ref.pdf"));
 
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
@@ -207,30 +563,23 @@
 }
 
 TEST_F(FPDFPPOEmbedderTest, BUG_664284) {
-  EXPECT_TRUE(OpenDocument("bug_664284.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_664284.pdf"));
 
   FPDF_PAGE page = LoadPage(0);
   ASSERT_NE(nullptr, page);
 
   FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
   EXPECT_TRUE(output_doc);
-  EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "1", 0));
+
+  static constexpr int kIndices[] = {0};
+  EXPECT_TRUE(FPDF_ImportPagesByIndex(output_doc, document(), kIndices,
+                                      std::size(kIndices), 0));
   FPDF_CloseDocument(output_doc);
 
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_BUG_750568 DISABLED_BUG_750568
-#else
-#define MAYBE_BUG_750568 BUG_750568
-#endif
-TEST_F(FPDFPPOEmbedderTest, MAYBE_BUG_750568) {
-  const char* const kHashes[] = {
-      "64ad08132a1c5a166768298c8a578f57", "83b83e2f6bc80707d0a917c7634140b9",
-      "913cd3723a451e4e46fbc2c05702d1ee", "81fb7cfd4860f855eb468f73dfeb6d60"};
-
+TEST_F(FPDFPPOEmbedderTest, BUG_750568) {
   ASSERT_TRUE(OpenDocument("bug_750568.pdf"));
   ASSERT_EQ(4, FPDF_GetPageCount(document()));
 
@@ -239,59 +588,47 @@
     ASSERT_TRUE(page);
 
     ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-    ASSERT_EQ(200, FPDFBitmap_GetWidth(bitmap.get()));
-    ASSERT_EQ(200, FPDFBitmap_GetHeight(bitmap.get()));
-    ASSERT_EQ(800, FPDFBitmap_GetStride(bitmap.get()));
-
-    EXPECT_EQ(kHashes[i], HashBitmap(bitmap.get()));
+    CompareBitmap(bitmap.get(), 200, 200, Bug750568PageHash(i));
     UnloadPage(page);
   }
 
   FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
   ASSERT_TRUE(output_doc);
-  EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "1,2,3,4", 0));
+
+  static constexpr int kIndices[] = {0, 1, 2, 3};
+  EXPECT_TRUE(FPDF_ImportPagesByIndex(output_doc, document(), kIndices,
+                                      std::size(kIndices), 0));
   ASSERT_EQ(4, FPDF_GetPageCount(output_doc));
   for (size_t i = 0; i < 4; ++i) {
     FPDF_PAGE page = FPDF_LoadPage(output_doc, i);
     ASSERT_TRUE(page);
 
     ScopedFPDFBitmap bitmap = RenderPage(page);
-    ASSERT_EQ(200, FPDFBitmap_GetWidth(bitmap.get()));
-    ASSERT_EQ(200, FPDFBitmap_GetHeight(bitmap.get()));
-    ASSERT_EQ(800, FPDFBitmap_GetStride(bitmap.get()));
-
-    EXPECT_EQ(kHashes[i], HashBitmap(bitmap.get()));
+    CompareBitmap(bitmap.get(), 200, 200, Bug750568PageHash(i));
     FPDF_ClosePage(page);
   }
   FPDF_CloseDocument(output_doc);
 }
 
 TEST_F(FPDFPPOEmbedderTest, ImportWithZeroLengthStream) {
-  EXPECT_TRUE(OpenDocument("zero_length_stream.pdf"));
+  ASSERT_TRUE(OpenDocument("zero_length_stream.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
   ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-  ASSERT_EQ(200, FPDFBitmap_GetWidth(bitmap.get()));
-  ASSERT_EQ(200, FPDFBitmap_GetHeight(bitmap.get()));
-  ASSERT_EQ(800, FPDFBitmap_GetStride(bitmap.get()));
-
-  std::string digest = HashBitmap(bitmap.get());
+  CompareBitmap(bitmap.get(), 200, 200, pdfium::HelloWorldChecksum());
   UnloadPage(page);
 
-  FPDF_DOCUMENT new_doc = FPDF_CreateNewDocument();
-  EXPECT_TRUE(new_doc);
-  EXPECT_TRUE(FPDF_ImportPages(new_doc, document(), "1", 0));
+  ScopedFPDFDocument new_doc(FPDF_CreateNewDocument());
+  ASSERT_TRUE(new_doc);
 
-  EXPECT_EQ(1, FPDF_GetPageCount(new_doc));
-  FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
-  ASSERT_NE(nullptr, new_page);
-  ScopedFPDFBitmap new_bitmap = RenderPage(new_page);
-  ASSERT_EQ(200, FPDFBitmap_GetWidth(new_bitmap.get()));
-  ASSERT_EQ(200, FPDFBitmap_GetHeight(new_bitmap.get()));
-  ASSERT_EQ(800, FPDFBitmap_GetStride(new_bitmap.get()));
+  static constexpr int kIndices[] = {0};
+  EXPECT_TRUE(FPDF_ImportPagesByIndex(new_doc.get(), document(), kIndices,
+                                      std::size(kIndices), 0));
 
-  EXPECT_EQ(digest, HashBitmap(new_bitmap.get()));
-  FPDF_ClosePage(new_page);
-  FPDF_CloseDocument(new_doc);
+  EXPECT_EQ(1, FPDF_GetPageCount(new_doc.get()));
+  ScopedFPDFPage new_page(FPDF_LoadPage(new_doc.get(), 0));
+  ASSERT_TRUE(new_page);
+  ScopedFPDFBitmap new_bitmap = RenderPage(new_page.get());
+  CompareBitmap(new_bitmap.get(), 200, 200, pdfium::HelloWorldChecksum());
 }
diff --git a/fpdfsdk/fpdf_progressive.cpp b/fpdfsdk/fpdf_progressive.cpp
index 99a9230..83b5797 100644
--- a/fpdfsdk/fpdf_progressive.cpp
+++ b/fpdfsdk/fpdf_progressive.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,21 +6,18 @@
 
 #include "public/fpdf_progressive.h"
 
+#include <memory>
 #include <utility>
 
 #include "core/fpdfapi/page/cpdf_page.h"
 #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"
 #include "public/fpdfview.h"
-#include "third_party/base/ptr_util.h"
-
-#ifdef _SKIA_SUPPORT_PATHS_
-#include "core/fxge/cfx_renderdevice.h"
-#endif
 
 // These checks are here because core/ and public/ cannot depend on each other.
 static_assert(CPDF_ProgressiveRenderer::kReady == FPDF_RENDER_READY,
@@ -41,6 +38,51 @@
 
 }  // namespace
 
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_RenderPageBitmapWithColorScheme_Start(FPDF_BITMAP bitmap,
+                                           FPDF_PAGE page,
+                                           int start_x,
+                                           int start_y,
+                                           int size_x,
+                                           int size_y,
+                                           int rotate,
+                                           int flags,
+                                           const FPDF_COLORSCHEME* color_scheme,
+                                           IFSDK_PAUSE* pause) {
+  if (!bitmap || !pause || pause->version != 1)
+    return FPDF_RENDER_FAILED;
+
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return FPDF_RENDER_FAILED;
+
+  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 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(context, pPage, start_x, start_y, size_x,
+                                size_y, rotate, flags, color_scheme,
+                                /*need_to_restore=*/false, &pause_adapter);
+
+#if defined(_SKIA_SUPPORT_)
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    pBitmap->UnPreMultiply();
+  }
+#endif  // defined(_SKIA_SUPPORT_)
+
+  if (!context->m_pRenderer) {
+    return FPDF_RENDER_FAILED;
+  }
+
+  return ToFPDFStatus(context->m_pRenderer->GetStatus());
+}
+
 FPDF_EXPORT int FPDF_CALLCONV FPDF_RenderPageBitmap_Start(FPDF_BITMAP bitmap,
                                                           FPDF_PAGE page,
                                                           int start_x,
@@ -50,37 +92,9 @@
                                                           int rotate,
                                                           int flags,
                                                           IFSDK_PAUSE* pause) {
-  if (!bitmap || !pause || pause->version != 1)
-    return FPDF_RENDER_FAILED;
-
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage)
-    return FPDF_RENDER_FAILED;
-
-  auto pOwnedContext = pdfium::MakeUnique<CPDF_PageRenderContext>();
-  CPDF_PageRenderContext* pContext = pOwnedContext.get();
-  pPage->SetRenderContext(std::move(pOwnedContext));
-
-  RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
-  auto pOwnedDevice = pdfium::MakeUnique<CFX_DefaultRenderDevice>();
-  CFX_DefaultRenderDevice* pDevice = pOwnedDevice.get();
-  pContext->m_pDevice = std::move(pOwnedDevice);
-  pDevice->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false);
-
-  CPDFSDK_PauseAdapter pause_adapter(pause);
-  CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
-                                size_y, rotate, flags,
-                                /*need_to_restore=*/false, &pause_adapter);
-
-#ifdef _SKIA_SUPPORT_PATHS_
-  pDevice->Flush(false);
-  pBitmap->UnPreMultiply();
-#endif
-
-  if (!pContext->m_pRenderer)
-    return FPDF_RENDER_FAILED;
-
-  return ToFPDFStatus(pContext->m_pRenderer->GetStatus());
+  return FPDF_RenderPageBitmapWithColorScheme_Start(
+      bitmap, page, start_x, start_y, size_x, size_y, rotate, flags,
+      /*color_scheme=*/nullptr, pause);
 }
 
 FPDF_EXPORT int FPDF_CALLCONV FPDF_RenderPage_Continue(FPDF_PAGE page,
@@ -99,26 +113,17 @@
 
   CPDFSDK_PauseAdapter pause_adapter(pause);
   pContext->m_pRenderer->Continue(&pause_adapter);
-#ifdef _SKIA_SUPPORT_PATHS_
-  CFX_RenderDevice* pDevice = pContext->m_pDevice.get();
-  pDevice->Flush(false);
-  pDevice->GetBitmap()->UnPreMultiply();
-#endif
+
+#if defined(_SKIA_SUPPORT_)
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    pContext->m_pDevice->GetBitmap()->UnPreMultiply();
+  }
+#endif  // defined(_SKIA_SUPPORT_)
   return ToFPDFStatus(pContext->m_pRenderer->GetStatus());
 }
 
 FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPage_Close(FPDF_PAGE page) {
   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (pPage) {
-#ifdef _SKIA_SUPPORT_PATHS_
-    auto* pContext =
-        static_cast<CPDF_PageRenderContext*>(pPage->GetRenderContext());
-    if (pContext && pContext->m_pRenderer) {
-      CFX_RenderDevice* pDevice = pContext->m_pDevice.get();
-      pDevice->Flush(true);
-      pDevice->GetBitmap()->UnPreMultiply();
-    }
-#endif
-    pPage->SetRenderContext(nullptr);
-  }
+  if (pPage)
+    pPage->ClearRenderContext();
 }
diff --git a/fpdfsdk/fpdf_save.cpp b/fpdfsdk/fpdf_save.cpp
index 78e0e58..37a1905 100644
--- a/fpdfsdk/fpdf_save.cpp
+++ b/fpdfsdk/fpdf_save.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,6 @@
 
 #include "public/fpdf_save.h"
 
-#include <memory>
 #include <utility>
 #include <vector>
 
@@ -19,10 +18,11 @@
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/stl_util.h"
 #include "fpdfsdk/cpdfsdk_filewriteadapter.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "public/fpdf_edit.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #ifdef PDF_ENABLE_XFA
 #include "core/fpdfapi/parser/cpdf_stream.h"
@@ -31,12 +31,6 @@
 #include "public/fpdf_formfill.h"
 #endif
 
-#if defined(OS_ANDROID)
-#include <time.h>
-#else
-#include <ctime>
-#endif
-
 namespace {
 
 #ifdef PDF_ENABLE_XFA
@@ -52,27 +46,27 @@
   if (!pPDFDocument)
     return false;
 
-  CPDF_Dictionary* pRoot = pPDFDocument->GetRoot();
+  RetainPtr<CPDF_Dictionary> pRoot = pPDFDocument->GetMutableRoot();
   if (!pRoot)
     return false;
 
-  CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
+  RetainPtr<CPDF_Dictionary> pAcroForm = pRoot->GetMutableDictFor("AcroForm");
   if (!pAcroForm)
     return false;
 
-  CPDF_Object* pXFA = pAcroForm->GetObjectFor("XFA");
+  RetainPtr<CPDF_Object> pXFA = pAcroForm->GetMutableObjectFor("XFA");
   if (!pXFA)
     return true;
 
-  CPDF_Array* pArray = pXFA->AsArray();
+  CPDF_Array* pArray = pXFA->AsMutableArray();
   if (!pArray)
     return false;
 
-  int size = pArray->size();
+  int size = fxcrt::CollectionSize<int>(*pArray);
   int iFormIndex = -1;
   int iDataSetsIndex = -1;
   for (int i = 0; i < size - 1; i++) {
-    const CPDF_Object* pPDFObj = pArray->GetObjectAt(i);
+    RetainPtr<const CPDF_Object> pPDFObj = pArray->GetObjectAt(i);
     if (!pPDFObj->IsString())
       continue;
     if (pPDFObj->GetString() == "form")
@@ -81,32 +75,34 @@
       iDataSetsIndex = i + 1;
   }
 
-  CPDF_Stream* pFormStream = nullptr;
+  RetainPtr<CPDF_Stream> pFormStream;
   if (iFormIndex != -1) {
     // Get form CPDF_Stream
-    CPDF_Object* pFormPDFObj = pArray->GetObjectAt(iFormIndex);
+    RetainPtr<CPDF_Object> pFormPDFObj = pArray->GetMutableObjectAt(iFormIndex);
     if (pFormPDFObj->IsReference()) {
-      CPDF_Object* pFormDirectObj = pFormPDFObj->GetDirect();
+      RetainPtr<CPDF_Object> pFormDirectObj = pFormPDFObj->GetMutableDirect();
       if (pFormDirectObj && pFormDirectObj->IsStream()) {
-        pFormStream = pFormDirectObj->AsStream();
+        pFormStream.Reset(pFormDirectObj->AsMutableStream());
       }
     } else if (pFormPDFObj->IsStream()) {
-      pFormStream = pFormPDFObj->AsStream();
+      pFormStream.Reset(pFormPDFObj->AsMutableStream());
     }
   }
 
-  CPDF_Stream* pDataSetsStream = nullptr;
+  RetainPtr<CPDF_Stream> pDataSetsStream;
   if (iDataSetsIndex != -1) {
     // Get datasets CPDF_Stream
-    CPDF_Object* pDataSetsPDFObj = pArray->GetObjectAt(iDataSetsIndex);
+    RetainPtr<CPDF_Object> pDataSetsPDFObj =
+        pArray->GetMutableObjectAt(iDataSetsIndex);
     if (pDataSetsPDFObj->IsReference()) {
-      CPDF_Reference* pDataSetsRefObj = pDataSetsPDFObj->AsReference();
-      CPDF_Object* pDataSetsDirectObj = pDataSetsRefObj->GetDirect();
+      CPDF_Reference* pDataSetsRefObj = pDataSetsPDFObj->AsMutableReference();
+      RetainPtr<CPDF_Object> pDataSetsDirectObj =
+          pDataSetsRefObj->GetMutableDirect();
       if (pDataSetsDirectObj && pDataSetsDirectObj->IsStream()) {
-        pDataSetsStream = pDataSetsDirectObj->AsStream();
+        pDataSetsStream.Reset(pDataSetsDirectObj->AsMutableStream());
       }
     } else if (pDataSetsPDFObj->IsStream()) {
-      pDataSetsStream = pDataSetsPDFObj->AsStream();
+      pDataSetsStream.Reset(pDataSetsPDFObj->AsMutableStream());
     }
   }
   // L"datasets"
@@ -121,9 +117,9 @@
           pDataSetsStream->InitStreamFromFile(pFileWrite, std::move(pDataDict));
         }
       } else {
-        CPDF_Stream* pData = pPDFDocument->NewIndirect<CPDF_Stream>();
+        auto pData = pPDFDocument->NewIndirect<CPDF_Stream>();
         pData->InitStreamFromFile(pFileWrite, std::move(pDataDict));
-        int iLast = pArray->size() - 2;
+        int iLast = fxcrt::CollectionSize<int>(*pArray) - 2;
         pArray->InsertNewAt<CPDF_String>(iLast, "datasets", false);
         pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
                                             pData->GetObjNum());
@@ -141,9 +137,9 @@
         if (pFormStream)
           pFormStream->InitStreamFromFile(pFileWrite, std::move(pDataDict));
       } else {
-        CPDF_Stream* pData = pPDFDocument->NewIndirect<CPDF_Stream>();
+        auto pData = pPDFDocument->NewIndirect<CPDF_Stream>();
         pData->InitStreamFromFile(pFileWrite, std::move(pDataDict));
-        int iLast = pArray->size() - 2;
+        int iLast = fxcrt::CollectionSize<int>(*pArray) - 2;
         pArray->InsertNewAt<CPDF_String>(iLast, "form", false);
         pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
                                             pData->GetObjNum());
@@ -158,10 +154,10 @@
 bool DoDocSave(FPDF_DOCUMENT document,
                FPDF_FILEWRITE* pFileWrite,
                FPDF_DWORD flags,
-               Optional<int> version) {
+               absl::optional<int> version) {
   CPDF_Document* pPDFDoc = CPDFDocumentFromFPDFDocument(document);
   if (!pPDFDoc)
-    return 0;
+    return false;
 
 #ifdef PDF_ENABLE_XFA
   auto* pContext = static_cast<CPDFXFA_Context*>(pPDFDoc->GetExtension());
@@ -184,7 +180,7 @@
     fileMaker.RemoveSecurity();
   }
 
-  bool bRet = fileMaker.Create(flags);
+  bool bRet = fileMaker.Create(static_cast<uint32_t>(flags));
 
 #ifdef PDF_ENABLE_XFA
   if (pContext)
diff --git a/fpdfsdk/fpdf_save_embeddertest.cpp b/fpdfsdk/fpdf_save_embeddertest.cpp
index 614b4f4..3f5efd8 100644
--- a/fpdfsdk/fpdf_save_embeddertest.cpp
+++ b/fpdfsdk/fpdf_save_embeddertest.cpp
@@ -1,8 +1,7 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
 #include <string>
 
 #include "core/fxcrt/fx_string.h"
@@ -12,40 +11,76 @@
 #include "public/fpdf_save.h"
 #include "public/fpdfview.h"
 #include "testing/embedder_test.h"
+#include "testing/embedder_test_constants.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::HasSubstr;
+using testing::Not;
+using testing::StartsWith;
+
 class FPDFSaveEmbedderTest : public EmbedderTest {};
 
 TEST_F(FPDFSaveEmbedderTest, SaveSimpleDoc) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n"));
-  EXPECT_EQ(805u, GetString().length());
+  EXPECT_THAT(GetString(), StartsWith("%PDF-1.7\r\n"));
+  EXPECT_EQ(805u, GetString().size());
 }
 
 TEST_F(FPDFSaveEmbedderTest, SaveSimpleDocWithVersion) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 0, 14));
-  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.4\r\n"));
-  EXPECT_EQ(805u, GetString().length());
+  EXPECT_THAT(GetString(), StartsWith("%PDF-1.4\r\n"));
+  EXPECT_EQ(805u, GetString().size());
 }
+
 TEST_F(FPDFSaveEmbedderTest, SaveSimpleDocWithBadVersion) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 0, -1));
-  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n"));
+  EXPECT_THAT(GetString(), StartsWith("%PDF-1.7\r\n"));
 
   ClearString();
   EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 0, 0));
-  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n"));
+  EXPECT_THAT(GetString(), StartsWith("%PDF-1.7\r\n"));
 
   ClearString();
   EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 0, 18));
-  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n"));
+  EXPECT_THAT(GetString(), StartsWith("%PDF-1.7\r\n"));
+}
+
+TEST_F(FPDFSaveEmbedderTest, SaveSimpleDocIncremental) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, FPDF_INCREMENTAL, 14));
+  // Version gets taken as-is from input document.
+  EXPECT_THAT(GetString(), StartsWith("%PDF-1.7\n%\xa0\xf2\xa4\xf4"));
+  // Additional output produced vs. non incremental.
+  EXPECT_EQ(985u, GetString().size());
+}
+
+TEST_F(FPDFSaveEmbedderTest, SaveSimpleDocNoIncremental) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, FPDF_NO_INCREMENTAL, 14));
+  EXPECT_THAT(GetString(), StartsWith("%PDF-1.4\r\n"));
+  EXPECT_EQ(805u, GetString().size());
+}
+
+TEST_F(FPDFSaveEmbedderTest, SaveSimpleDocRemoveSecurity) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, FPDF_REMOVE_SECURITY, 14));
+  EXPECT_THAT(GetString(), StartsWith("%PDF-1.4\r\n"));
+  EXPECT_EQ(805u, GetString().size());
+}
+
+TEST_F(FPDFSaveEmbedderTest, SaveSimpleDocBadFlags) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 999999, 14));
+  EXPECT_THAT(GetString(), StartsWith("%PDF-1.4\r\n"));
+  EXPECT_EQ(805u, GetString().size());
 }
 
 TEST_F(FPDFSaveEmbedderTest, SaveCopiedDoc) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
 
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
@@ -63,7 +98,7 @@
   const int kPageCount = 3;
   std::string original_md5[kPageCount];
 
-  EXPECT_TRUE(OpenDocument("linearized.pdf"));
+  ASSERT_TRUE(OpenDocument("linearized.pdf"));
   for (int i = 0; i < kPageCount; ++i) {
     FPDF_PAGE page = LoadPage(i);
     ASSERT_TRUE(page);
@@ -75,10 +110,15 @@
   }
 
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.6\r\n"));
-  EXPECT_THAT(GetString(), testing::HasSubstr("/Root "));
-  EXPECT_THAT(GetString(), testing::HasSubstr("/Info "));
-  EXPECT_EQ(8219u, GetString().length());
+  EXPECT_THAT(GetString(), StartsWith("%PDF-1.6\r\n"));
+  EXPECT_THAT(GetString(), HasSubstr("/Root "));
+  EXPECT_THAT(GetString(), HasSubstr("/Info "));
+  EXPECT_THAT(GetString(), HasSubstr("/Size 37"));
+  EXPECT_THAT(GetString(), HasSubstr("35 0 obj"));
+  EXPECT_THAT(GetString(), HasSubstr("36 0 obj"));
+  EXPECT_THAT(GetString(), Not(HasSubstr("37 0 obj")));
+  EXPECT_THAT(GetString(), Not(HasSubstr("38 0 obj")));
+  EXPECT_EQ(7908u, GetString().size());
 
   // Make sure new document renders the same as the old one.
   ASSERT_TRUE(OpenSavedDocument());
@@ -92,11 +132,40 @@
   CloseSavedDocument();
 }
 
+TEST_F(FPDFSaveEmbedderTest, Bug1409) {
+  ASSERT_TRUE(OpenDocument("jpx_lzw.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  while (FPDFPage_CountObjects(page) > 0) {
+    ScopedFPDFPageObject object(FPDFPage_GetObject(page, 0));
+    ASSERT_TRUE(object);
+    ASSERT_TRUE(FPDFPage_RemoveObject(page, object.get()));
+  }
+  ASSERT_TRUE(FPDFPage_GenerateContent(page));
+  UnloadPage(page);
+
+  ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+  // The new document should render as empty.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+  ASSERT_TRUE(saved_page);
+  ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
+  EXPECT_EQ(pdfium::kBlankPage612By792Checksum, HashBitmap(bitmap.get()));
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+
+  EXPECT_THAT(GetString(), StartsWith("%PDF-1.7\r\n"));
+  EXPECT_THAT(GetString(), HasSubstr("/Root "));
+  EXPECT_THAT(GetString(), Not(HasSubstr("/Image")));
+  EXPECT_LT(GetString().size(), 600u);
+}
+
 #ifdef PDF_ENABLE_XFA
 TEST_F(FPDFSaveEmbedderTest, SaveXFADoc) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n"));
+  EXPECT_THAT(GetString(), StartsWith("%PDF-1.7\r\n"));
   ASSERT_TRUE(OpenSavedDocument());
   // TODO(tsepez): check for XFA forms in document
   CloseSavedDocument();
@@ -104,15 +173,22 @@
 #endif  // PDF_ENABLE_XFA
 
 TEST_F(FPDFSaveEmbedderTest, BUG_342) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  EXPECT_THAT(GetString(), testing::HasSubstr("0000000000 65535 f\r\n"));
-  EXPECT_THAT(GetString(),
-              testing::Not(testing::HasSubstr("0000000000 65536 f\r\n")));
+  EXPECT_THAT(GetString(), HasSubstr("0000000000 65535 f\r\n"));
+  EXPECT_THAT(GetString(), Not(HasSubstr("0000000000 65536 f\r\n")));
 }
 
 TEST_F(FPDFSaveEmbedderTest, BUG_905142) {
-  EXPECT_TRUE(OpenDocument("bug_905142.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_905142.pdf"));
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  EXPECT_THAT(GetString(), testing::HasSubstr("/Length 0"));
+  EXPECT_THAT(GetString(), HasSubstr("/Length 0"));
+}
+
+// Should not trigger a DCHECK() failure in CFX_FileBufferArchive.
+// Fails because the PDF is malformed.
+TEST_F(FPDFSaveEmbedderTest, Bug1328389) {
+  ASSERT_TRUE(OpenDocument("bug_1328389.pdf"));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  EXPECT_THAT(GetString(), HasSubstr("/Foo/"));
 }
diff --git a/fpdfsdk/fpdf_searchex.cpp b/fpdfsdk/fpdf_searchex.cpp
index dbc2d2a..74cdff1 100644
--- a/fpdfsdk/fpdf_searchex.cpp
+++ b/fpdfsdk/fpdf_searchex.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fpdfsdk/fpdf_searchex_embeddertest.cpp b/fpdfsdk/fpdf_searchex_embeddertest.cpp
index 54b4799..5740789 100644
--- a/fpdfsdk/fpdf_searchex_embeddertest.cpp
+++ b/fpdfsdk/fpdf_searchex_embeddertest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fpdfsdk/fpdf_signature.cpp b/fpdfsdk/fpdf_signature.cpp
new file mode 100644
index 0000000..2e7d267
--- /dev/null
+++ b/fpdfsdk/fpdf_signature.cpp
@@ -0,0 +1,222 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_signature.h"
+
+#include <utility>
+#include <vector>
+
+#include "constants/form_fields.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fxcrt/stl_util.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "third_party/base/numerics/safe_conversions.h"
+
+namespace {
+
+std::vector<RetainPtr<const CPDF_Dictionary>> CollectSignatures(
+    CPDF_Document* doc) {
+  std::vector<RetainPtr<const CPDF_Dictionary>> signatures;
+  const CPDF_Dictionary* root = doc->GetRoot();
+  if (!root)
+    return signatures;
+
+  RetainPtr<const CPDF_Dictionary> acro_form = root->GetDictFor("AcroForm");
+  if (!acro_form)
+    return signatures;
+
+  RetainPtr<const CPDF_Array> fields = acro_form->GetArrayFor("Fields");
+  if (!fields)
+    return signatures;
+
+  CPDF_ArrayLocker locker(std::move(fields));
+  for (auto& field : locker) {
+    RetainPtr<const CPDF_Dictionary> field_dict = field->GetDict();
+    if (field_dict && field_dict->GetNameFor(pdfium::form_fields::kFT) ==
+                          pdfium::form_fields::kSig) {
+      signatures.push_back(std::move(field_dict));
+    }
+  }
+  return signatures;
+}
+
+}  // namespace
+
+FPDF_EXPORT int FPDF_CALLCONV FPDF_GetSignatureCount(FPDF_DOCUMENT document) {
+  auto* doc = CPDFDocumentFromFPDFDocument(document);
+  if (!doc)
+    return -1;
+
+  return fxcrt::CollectionSize<int>(CollectSignatures(doc));
+}
+
+FPDF_EXPORT FPDF_SIGNATURE FPDF_CALLCONV
+FPDF_GetSignatureObject(FPDF_DOCUMENT document, int index) {
+  auto* doc = CPDFDocumentFromFPDFDocument(document);
+  if (!doc)
+    return nullptr;
+
+  std::vector<RetainPtr<const CPDF_Dictionary>> signatures =
+      CollectSignatures(doc);
+  if (!fxcrt::IndexInBounds(signatures, index))
+    return nullptr;
+
+  return FPDFSignatureFromCPDFDictionary(signatures[index].Get());
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFSignatureObj_GetContents(FPDF_SIGNATURE signature,
+                             void* buffer,
+                             unsigned long length) {
+  const CPDF_Dictionary* signature_dict =
+      CPDFDictionaryFromFPDFSignature(signature);
+  if (!signature_dict)
+    return 0;
+
+  RetainPtr<const CPDF_Dictionary> value_dict =
+      signature_dict->GetDictFor(pdfium::form_fields::kV);
+  if (!value_dict)
+    return 0;
+
+  ByteString contents = value_dict->GetByteStringFor("Contents");
+  const unsigned long contents_len =
+      pdfium::base::checked_cast<unsigned long>(contents.GetLength());
+  if (buffer && length >= contents_len)
+    memcpy(buffer, contents.c_str(), contents_len);
+
+  return contents_len;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFSignatureObj_GetByteRange(FPDF_SIGNATURE signature,
+                              int* buffer,
+                              unsigned long length) {
+  const CPDF_Dictionary* signature_dict =
+      CPDFDictionaryFromFPDFSignature(signature);
+  if (!signature_dict)
+    return 0;
+
+  RetainPtr<const CPDF_Dictionary> value_dict =
+      signature_dict->GetDictFor(pdfium::form_fields::kV);
+  if (!value_dict)
+    return 0;
+
+  RetainPtr<const CPDF_Array> byte_range = value_dict->GetArrayFor("ByteRange");
+  if (!byte_range)
+    return 0;
+
+  const unsigned long byte_range_len =
+      fxcrt::CollectionSize<unsigned long>(*byte_range);
+  if (buffer && length >= byte_range_len) {
+    for (size_t i = 0; i < byte_range_len; ++i)
+      buffer[i] = byte_range->GetIntegerAt(i);
+  }
+  return byte_range_len;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFSignatureObj_GetSubFilter(FPDF_SIGNATURE signature,
+                              char* buffer,
+                              unsigned long length) {
+  const CPDF_Dictionary* signature_dict =
+      CPDFDictionaryFromFPDFSignature(signature);
+  if (!signature_dict)
+    return 0;
+
+  RetainPtr<const CPDF_Dictionary> value_dict =
+      signature_dict->GetDictFor(pdfium::form_fields::kV);
+  if (!value_dict || !value_dict->KeyExist("SubFilter"))
+    return 0;
+
+  ByteString sub_filter = value_dict->GetNameFor("SubFilter");
+  return NulTerminateMaybeCopyAndReturnLength(sub_filter, buffer, length);
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFSignatureObj_GetReason(FPDF_SIGNATURE signature,
+                           void* buffer,
+                           unsigned long length) {
+  const CPDF_Dictionary* signature_dict =
+      CPDFDictionaryFromFPDFSignature(signature);
+  if (!signature_dict)
+    return 0;
+
+  RetainPtr<const CPDF_Dictionary> value_dict =
+      signature_dict->GetDictFor(pdfium::form_fields::kV);
+  if (!value_dict)
+    return 0;
+
+  RetainPtr<const CPDF_Object> obj = value_dict->GetObjectFor("Reason");
+  if (!obj || !obj->IsString())
+    return 0;
+
+  return Utf16EncodeMaybeCopyAndReturnLength(obj->GetUnicodeText(), buffer,
+                                             length);
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFSignatureObj_GetTime(FPDF_SIGNATURE signature,
+                         char* buffer,
+                         unsigned long length) {
+  const CPDF_Dictionary* signature_dict =
+      CPDFDictionaryFromFPDFSignature(signature);
+  if (!signature_dict)
+    return 0;
+
+  RetainPtr<const CPDF_Dictionary> value_dict =
+      signature_dict->GetDictFor(pdfium::form_fields::kV);
+  if (!value_dict)
+    return 0;
+
+  RetainPtr<const CPDF_Object> obj = value_dict->GetObjectFor("M");
+  if (!obj || !obj->IsString())
+    return 0;
+
+  return NulTerminateMaybeCopyAndReturnLength(obj->GetString(), buffer, length);
+}
+
+FPDF_EXPORT unsigned int FPDF_CALLCONV
+FPDFSignatureObj_GetDocMDPPermission(FPDF_SIGNATURE signature) {
+  int permission = 0;
+  const CPDF_Dictionary* signature_dict =
+      CPDFDictionaryFromFPDFSignature(signature);
+  if (!signature_dict)
+    return permission;
+
+  RetainPtr<const CPDF_Dictionary> value_dict =
+      signature_dict->GetDictFor(pdfium::form_fields::kV);
+  if (!value_dict)
+    return permission;
+
+  RetainPtr<const CPDF_Array> references = value_dict->GetArrayFor("Reference");
+  if (!references)
+    return permission;
+
+  CPDF_ArrayLocker locker(std::move(references));
+  for (auto& reference : locker) {
+    RetainPtr<const CPDF_Dictionary> reference_dict = reference->GetDict();
+    if (!reference_dict)
+      continue;
+
+    ByteString transform_method = reference_dict->GetNameFor("TransformMethod");
+    if (transform_method != "DocMDP")
+      continue;
+
+    RetainPtr<const CPDF_Dictionary> transform_params =
+        reference_dict->GetDictFor("TransformParams");
+    if (!transform_params)
+      continue;
+
+    // Valid values are 1, 2 and 3; 2 is the default.
+    permission = transform_params->GetIntegerFor("P", 2);
+    if (permission < 1 || permission > 3)
+      permission = 0;
+
+    return permission;
+  }
+
+  return permission;
+}
diff --git a/fpdfsdk/fpdf_signature_embeddertest.cpp b/fpdfsdk/fpdf_signature_embeddertest.cpp
new file mode 100644
index 0000000..02ae801
--- /dev/null
+++ b/fpdfsdk/fpdf_signature_embeddertest.cpp
@@ -0,0 +1,203 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "public/fpdf_signature.h"
+#include "testing/embedder_test.h"
+#include "testing/fx_string_testhelpers.h"
+
+class FPDFSignatureEmbedderTest : public EmbedderTest {};
+
+TEST_F(FPDFSignatureEmbedderTest, GetSignatureCount) {
+  ASSERT_TRUE(OpenDocument("two_signatures.pdf"));
+  EXPECT_EQ(2, FPDF_GetSignatureCount(document()));
+}
+
+TEST_F(FPDFSignatureEmbedderTest, GetSignatureCountZero) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_EQ(0, FPDF_GetSignatureCount(document()));
+
+  // Provide no document.
+  EXPECT_EQ(-1, FPDF_GetSignatureCount(nullptr));
+}
+
+TEST_F(FPDFSignatureEmbedderTest, GetSignatureObject) {
+  ASSERT_TRUE(OpenDocument("two_signatures.pdf"));
+  // Different, non-null signature objects are returned.
+  FPDF_SIGNATURE signature1 = FPDF_GetSignatureObject(document(), 0);
+  EXPECT_TRUE(signature1);
+  FPDF_SIGNATURE signature2 = FPDF_GetSignatureObject(document(), 1);
+  EXPECT_TRUE(signature2);
+  EXPECT_NE(signature1, signature2);
+
+  // Out of bounds.
+  EXPECT_FALSE(FPDF_GetSignatureObject(document(), -1));
+  EXPECT_FALSE(FPDF_GetSignatureObject(document(), 2));
+
+  // Provide no document.
+  EXPECT_FALSE(FPDF_GetSignatureObject(nullptr, 0));
+}
+
+TEST_F(FPDFSignatureEmbedderTest, GetContents) {
+  ASSERT_TRUE(OpenDocument("two_signatures.pdf"));
+  FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0);
+  EXPECT_TRUE(signature);
+
+  // FPDFSignatureObj_GetContents() positive testing.
+  unsigned long size = FPDFSignatureObj_GetContents(signature, nullptr, 0);
+  const uint8_t kExpectedContents[] = {0x30, 0x80, 0x06, 0x09, 0x2A, 0x86, 0x48,
+                                       0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02, 0xA0,
+                                       0x80, 0x30, 0x80, 0x02, 0x01, 0x01};
+  ASSERT_EQ(sizeof(kExpectedContents), size);
+  std::vector<char> contents(size);
+  ASSERT_EQ(size,
+            FPDFSignatureObj_GetContents(signature, contents.data(), size));
+  ASSERT_EQ(0, memcmp(kExpectedContents, contents.data(), size));
+
+  // FPDFSignatureObj_GetContents() negative testing.
+  ASSERT_EQ(0U, FPDFSignatureObj_GetContents(nullptr, nullptr, 0));
+
+  contents.resize(2);
+  contents[0] = 'x';
+  contents[1] = '\0';
+  size =
+      FPDFSignatureObj_GetContents(signature, contents.data(), contents.size());
+  ASSERT_EQ(sizeof(kExpectedContents), size);
+  EXPECT_EQ('x', contents[0]);
+  EXPECT_EQ('\0', contents[1]);
+}
+
+TEST_F(FPDFSignatureEmbedderTest, GetByteRange) {
+  ASSERT_TRUE(OpenDocument("two_signatures.pdf"));
+  FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0);
+  EXPECT_TRUE(signature);
+
+  // FPDFSignatureObj_GetByteRange() positive testing.
+  unsigned long size = FPDFSignatureObj_GetByteRange(signature, nullptr, 0);
+  const std::vector<int> kExpectedByteRange{0, 10, 30, 10};
+  ASSERT_EQ(kExpectedByteRange.size(), size);
+  std::vector<int> byte_range(size);
+  ASSERT_EQ(size,
+            FPDFSignatureObj_GetByteRange(signature, byte_range.data(), size));
+  ASSERT_EQ(kExpectedByteRange, byte_range);
+
+  // FPDFSignatureObj_GetByteRange() negative testing.
+  ASSERT_EQ(0U, FPDFSignatureObj_GetByteRange(nullptr, nullptr, 0));
+
+  byte_range.resize(2);
+  byte_range[0] = 0;
+  byte_range[1] = 1;
+  size = FPDFSignatureObj_GetByteRange(signature, byte_range.data(),
+                                       byte_range.size());
+  ASSERT_EQ(kExpectedByteRange.size(), size);
+  EXPECT_EQ(0, byte_range[0]);
+  EXPECT_EQ(1, byte_range[1]);
+}
+
+TEST_F(FPDFSignatureEmbedderTest, GetSubFilter) {
+  ASSERT_TRUE(OpenDocument("two_signatures.pdf"));
+  FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0);
+  EXPECT_TRUE(signature);
+
+  // FPDFSignatureObj_GetSubFilter() positive testing.
+  unsigned long size = FPDFSignatureObj_GetSubFilter(signature, nullptr, 0);
+  const char kExpectedSubFilter[] = "ETSI.CAdES.detached";
+  ASSERT_EQ(sizeof(kExpectedSubFilter), size);
+  std::vector<char> sub_filter(size);
+  ASSERT_EQ(size,
+            FPDFSignatureObj_GetSubFilter(signature, sub_filter.data(), size));
+  ASSERT_EQ(0, memcmp(kExpectedSubFilter, sub_filter.data(), size));
+
+  // FPDFSignatureObj_GetSubFilter() negative testing.
+  ASSERT_EQ(0U, FPDFSignatureObj_GetSubFilter(nullptr, nullptr, 0));
+
+  sub_filter.resize(2);
+  sub_filter[0] = 'x';
+  sub_filter[1] = '\0';
+  size = FPDFSignatureObj_GetSubFilter(signature, sub_filter.data(),
+                                       sub_filter.size());
+  ASSERT_EQ(sizeof(kExpectedSubFilter), size);
+  EXPECT_EQ('x', sub_filter[0]);
+  EXPECT_EQ('\0', sub_filter[1]);
+}
+
+TEST_F(FPDFSignatureEmbedderTest, GetSubFilterNoKeyExists) {
+  ASSERT_TRUE(OpenDocument("signature_no_sub_filter.pdf"));
+  FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0);
+  EXPECT_TRUE(signature);
+
+  // FPDFSignatureObj_GetSubFilter() negative testing: no SubFilter
+  ASSERT_EQ(0U, FPDFSignatureObj_GetSubFilter(signature, nullptr, 0));
+}
+
+TEST_F(FPDFSignatureEmbedderTest, GetReason) {
+  ASSERT_TRUE(OpenDocument("signature_reason.pdf"));
+  FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0);
+  EXPECT_TRUE(signature);
+
+  // FPDFSignatureObj_GetReason() positive testing.
+  constexpr char kReason[] = "test reason";
+  // Return value includes the terminating NUL that is provided.
+  constexpr unsigned long kReasonUTF16Size = std::size(kReason) * 2;
+  constexpr wchar_t kReasonWide[] = L"test reason";
+  unsigned long size = FPDFSignatureObj_GetReason(signature, nullptr, 0);
+  ASSERT_EQ(kReasonUTF16Size, size);
+
+  std::vector<unsigned short> buffer(size);
+  ASSERT_EQ(size, FPDFSignatureObj_GetReason(signature, buffer.data(), size));
+  ASSERT_EQ(kReasonWide, GetPlatformWString(buffer.data()));
+
+  // FPDFSignatureObj_GetReason() negative testing.
+  ASSERT_EQ(0U, FPDFSignatureObj_GetReason(nullptr, nullptr, 0));
+
+  // Buffer is too small, ensure it's not modified.
+  buffer.resize(2);
+  buffer[0] = 'x';
+  buffer[1] = '\0';
+  size = FPDFSignatureObj_GetReason(signature, buffer.data(), buffer.size());
+  ASSERT_EQ(kReasonUTF16Size, size);
+  EXPECT_EQ('x', buffer[0]);
+  EXPECT_EQ('\0', buffer[1]);
+}
+
+TEST_F(FPDFSignatureEmbedderTest, GetTime) {
+  ASSERT_TRUE(OpenDocument("two_signatures.pdf"));
+  FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0);
+  EXPECT_TRUE(signature);
+
+  // FPDFSignatureObj_GetTime() positive testing.
+  unsigned long size = FPDFSignatureObj_GetTime(signature, nullptr, 0);
+  const char kExpectedTime[] = "D:20200624093114+02'00'";
+  ASSERT_EQ(sizeof(kExpectedTime), size);
+  std::vector<char> time_buffer(size);
+  ASSERT_EQ(size,
+            FPDFSignatureObj_GetTime(signature, time_buffer.data(), size));
+  ASSERT_EQ(0, memcmp(kExpectedTime, time_buffer.data(), size));
+
+  // FPDFSignatureObj_GetTime() negative testing.
+  ASSERT_EQ(0U, FPDFSignatureObj_GetTime(nullptr, nullptr, 0));
+
+  time_buffer.resize(2);
+  time_buffer[0] = 'x';
+  time_buffer[1] = '\0';
+  size = FPDFSignatureObj_GetTime(signature, time_buffer.data(),
+                                  time_buffer.size());
+  ASSERT_EQ(sizeof(kExpectedTime), size);
+  EXPECT_EQ('x', time_buffer[0]);
+  EXPECT_EQ('\0', time_buffer[1]);
+}
+
+TEST_F(FPDFSignatureEmbedderTest, GetDocMDPPermission) {
+  ASSERT_TRUE(OpenDocument("docmdp.pdf"));
+  FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0);
+  ASSERT_NE(nullptr, signature);
+
+  // FPDFSignatureObj_GetDocMDPPermission() positive testing.
+  unsigned int permission = FPDFSignatureObj_GetDocMDPPermission(signature);
+  EXPECT_EQ(1U, permission);
+
+  // FPDFSignatureObj_GetDocMDPPermission() negative testing.
+  EXPECT_EQ(0U, FPDFSignatureObj_GetDocMDPPermission(nullptr));
+}
diff --git a/fpdfsdk/fpdf_structtree.cpp b/fpdfsdk/fpdf_structtree.cpp
index cfd57a8..4817f47 100644
--- a/fpdfsdk/fpdf_structtree.cpp
+++ b/fpdfsdk/fpdf_structtree.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,14 @@
 #include <memory>
 
 #include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfdoc/cpdf_structelement.h"
 #include "core/fpdfdoc/cpdf_structtree.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/stl_util.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
 
@@ -21,12 +25,22 @@
     return 0;
 
   ByteString encodedStr = str.ToUTF16LE();
-  const unsigned long len = encodedStr.GetLength();
+  const unsigned long len =
+      pdfium::base::checked_cast<unsigned long>(encodedStr.GetLength());
   if (buffer && len <= buflen)
     memcpy(buffer, encodedStr.c_str(), len);
   return len;
 }
 
+int GetMcidFromDict(const CPDF_Dictionary* dict) {
+  if (dict && dict->GetNameFor("Type") == "MCR") {
+    RetainPtr<const CPDF_Object> obj = dict->GetObjectFor("MCID");
+    if (obj && obj->IsNumber())
+      return obj->GetInteger();
+  }
+  return -1;
+}
+
 }  // namespace
 
 FPDF_EXPORT FPDF_STRUCTTREE FPDF_CALLCONV
@@ -53,7 +67,7 @@
   if (!tree)
     return -1;
 
-  pdfium::base::CheckedNumeric<int> tmp_size = tree->CountTopElements();
+  FX_SAFE_INT32 tmp_size = tree->CountTopElements();
   return tmp_size.ValueOrDefault(-1);
 }
 
@@ -77,11 +91,127 @@
   return elem ? WideStringToBuffer(elem->GetAltText(), buffer, buflen) : 0;
 }
 
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_StructElement_GetActualText(FPDF_STRUCTELEMENT struct_element,
+                                 void* buffer,
+                                 unsigned long buflen) {
+  CPDF_StructElement* elem =
+      CPDFStructElementFromFPDFStructElement(struct_element);
+  return elem ? WideStringToBuffer(elem->GetActualText(), buffer, buflen) : 0;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_StructElement_GetID(FPDF_STRUCTELEMENT struct_element,
+                         void* buffer,
+                         unsigned long buflen) {
+  CPDF_StructElement* elem =
+      CPDFStructElementFromFPDFStructElement(struct_element);
+  if (!elem)
+    return 0;
+  absl::optional<WideString> id = elem->GetID();
+  if (!id.has_value())
+    return 0;
+  return Utf16EncodeMaybeCopyAndReturnLength(id.value(), buffer, buflen);
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_StructElement_GetLang(FPDF_STRUCTELEMENT struct_element,
+                           void* buffer,
+                           unsigned long buflen) {
+  CPDF_StructElement* elem =
+      CPDFStructElementFromFPDFStructElement(struct_element);
+  if (!elem)
+    return 0;
+  absl::optional<WideString> lang = elem->GetLang();
+  if (!lang.has_value())
+    return 0;
+  return Utf16EncodeMaybeCopyAndReturnLength(lang.value(), buffer, buflen);
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_StructElement_GetAttributeCount(FPDF_STRUCTELEMENT struct_element) {
+  CPDF_StructElement* elem =
+      CPDFStructElementFromFPDFStructElement(struct_element);
+  if (!elem)
+    return -1;
+  RetainPtr<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())
+    return fxcrt::CollectionSize<int>(*attr_obj->AsArray());
+  return attr_obj->IsDictionary() ? 1 : -1;
+}
+
+FPDF_EXPORT FPDF_STRUCTELEMENT_ATTR FPDF_CALLCONV
+FPDF_StructElement_GetAttributeAtIndex(FPDF_STRUCTELEMENT struct_element,
+                                       int index) {
+  CPDF_StructElement* elem =
+      CPDFStructElementFromFPDFStructElement(struct_element);
+  if (!elem)
+    return nullptr;
+
+  RetainPtr<const CPDF_Object> attr_obj = elem->GetA();
+  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())
+                      : nullptr;
+  }
+  if (attr_obj->IsArray()) {
+    const CPDF_Array* array = attr_obj->AsArray();
+    if (index < 0 || static_cast<size_t>(index) >= array->size())
+      return nullptr;
+
+    // TODO(tsepez): should embedder take a reference here?
+    // Unretained reference in public API. NOLINTNEXTLINE
+    return FPDFStructElementAttrFromCPDFDictionary(array->GetDictAt(index));
+  }
+  return nullptr;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_StructElement_GetStringAttribute(FPDF_STRUCTELEMENT struct_element,
+                                      FPDF_BYTESTRING attr_name,
+                                      void* buffer,
+                                      unsigned long buflen) {
+  CPDF_StructElement* elem =
+      CPDFStructElementFromFPDFStructElement(struct_element);
+  if (!elem)
+    return 0;
+  RetainPtr<const CPDF_Array> array = ToArray(elem->GetA());
+  if (!array)
+    return 0;
+  CPDF_ArrayLocker locker(array);
+  for (const RetainPtr<CPDF_Object>& obj : locker) {
+    const CPDF_Dictionary* obj_dict = obj->AsDictionary();
+    if (!obj_dict)
+      continue;
+    RetainPtr<const CPDF_Object> attr = obj_dict->GetObjectFor(attr_name);
+    if (!attr || !(attr->IsString() || attr->IsName()))
+      continue;
+    return Utf16EncodeMaybeCopyAndReturnLength(attr->GetUnicodeText(), buffer,
+                                               buflen);
+  }
+  return 0;
+}
+
 FPDF_EXPORT int FPDF_CALLCONV
 FPDF_StructElement_GetMarkedContentID(FPDF_STRUCTELEMENT struct_element) {
   CPDF_StructElement* elem =
       CPDFStructElementFromFPDFStructElement(struct_element);
-  const CPDF_Object* p = elem ? elem->GetDict()->GetObjectFor("K") : nullptr;
+  if (!elem)
+    return -1;
+  RetainPtr<const CPDF_Object> p = elem->GetK();
   return p && p->IsNumber() ? p->GetInteger() : -1;
 }
 
@@ -98,6 +228,18 @@
 }
 
 FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_StructElement_GetObjType(FPDF_STRUCTELEMENT struct_element,
+                              void* buffer,
+                              unsigned long buflen) {
+  CPDF_StructElement* elem =
+      CPDFStructElementFromFPDFStructElement(struct_element);
+  return elem ? WideStringToBuffer(
+                    WideString::FromUTF8(elem->GetObjType().AsStringView()),
+                    buffer, buflen)
+              : 0;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDF_StructElement_GetTitle(FPDF_STRUCTELEMENT struct_element,
                             void* buffer,
                             unsigned long buflen) {
@@ -113,7 +255,7 @@
   if (!elem)
     return -1;
 
-  pdfium::base::CheckedNumeric<int> tmp_size = elem->CountKids();
+  FX_SAFE_INT32 tmp_size = elem->CountKids();
   return tmp_size.ValueOrDefault(-1);
 }
 
@@ -125,6 +267,202 @@
   if (!elem || index < 0 || static_cast<size_t>(index) >= elem->CountKids())
     return nullptr;
 
-  return FPDFStructElementFromCPDFStructElement(
-      elem->GetKidIfElement(static_cast<size_t>(index)));
+  return FPDFStructElementFromCPDFStructElement(elem->GetKidIfElement(index));
+}
+
+FPDF_EXPORT FPDF_STRUCTELEMENT FPDF_CALLCONV
+FPDF_StructElement_GetParent(FPDF_STRUCTELEMENT struct_element) {
+  CPDF_StructElement* elem =
+      CPDFStructElementFromFPDFStructElement(struct_element);
+  CPDF_StructElement* parent = elem ? elem->GetParent() : nullptr;
+  if (!parent) {
+    return nullptr;
+  }
+  return FPDFStructElementFromCPDFStructElement(parent);
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_StructElement_Attr_GetCount(FPDF_STRUCTELEMENT_ATTR struct_attribute) {
+  const CPDF_Dictionary* dict =
+      CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
+  if (!dict)
+    return -1;
+  return fxcrt::CollectionSize<int>(*dict);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetName(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+                                int index,
+                                void* buffer,
+                                unsigned long buflen,
+                                unsigned long* out_buflen) {
+  if (!out_buflen) {
+    return false;
+  }
+
+  const CPDF_Dictionary* dict =
+      CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
+  if (!dict)
+    return false;
+
+  CPDF_DictionaryLocker locker(dict);
+  for (auto& it : locker) {
+    if (index == 0) {
+      *out_buflen =
+          NulTerminateMaybeCopyAndReturnLength(it.first, buffer, buflen);
+      return true;
+    }
+    --index;
+  }
+  return false;
+}
+
+FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
+FPDF_StructElement_Attr_GetType(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+                                FPDF_BYTESTRING name) {
+  const CPDF_Dictionary* dict =
+      CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
+  if (!dict)
+    return FPDF_OBJECT_UNKNOWN;
+
+  RetainPtr<const CPDF_Object> obj = dict->GetObjectFor(name);
+  return obj ? obj->GetType() : FPDF_OBJECT_UNKNOWN;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_StructElement_Attr_GetBooleanValue(
+    FPDF_STRUCTELEMENT_ATTR struct_attribute,
+    FPDF_BYTESTRING name,
+    FPDF_BOOL* out_value) {
+  if (!out_value)
+    return false;
+
+  const CPDF_Dictionary* dict =
+      CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
+  if (!dict)
+    return false;
+
+  RetainPtr<const CPDF_Object> obj = dict->GetObjectFor(name);
+  if (!obj || !obj->IsBoolean())
+    return false;
+
+  *out_value = obj->GetInteger();
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetNumberValue(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+                                       FPDF_BYTESTRING name,
+                                       float* out_value) {
+  if (!out_value)
+    return false;
+
+  const CPDF_Dictionary* dict =
+      CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
+  if (!dict)
+    return false;
+
+  RetainPtr<const CPDF_Object> obj = dict->GetDirectObjectFor(name);
+  if (!obj || !obj->IsNumber())
+    return false;
+
+  *out_value = obj->GetNumber();
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetStringValue(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+                                       FPDF_BYTESTRING name,
+                                       void* buffer,
+                                       unsigned long buflen,
+                                       unsigned long* out_buflen) {
+  if (!out_buflen)
+    return false;
+
+  const CPDF_Dictionary* dict =
+      CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
+  if (!dict)
+    return false;
+
+  RetainPtr<const CPDF_Object> obj = dict->GetObjectFor(name);
+  if (!obj || !(obj->IsString() || obj->IsName()))
+    return false;
+
+  *out_buflen = Utf16EncodeMaybeCopyAndReturnLength(
+      WideString::FromUTF8(obj->GetString().AsStringView()), buffer, buflen);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetBlobValue(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+                                     FPDF_BYTESTRING name,
+                                     void* buffer,
+                                     unsigned long buflen,
+                                     unsigned long* out_buflen) {
+  if (!out_buflen)
+    return false;
+
+  const CPDF_Dictionary* dict =
+      CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
+  if (!dict)
+    return false;
+
+  RetainPtr<const CPDF_Object> obj = dict->GetObjectFor(name);
+  if (!obj || !obj->IsString())
+    return false;
+
+  ByteString result = obj->GetString();
+  const unsigned long len =
+      pdfium::base::checked_cast<unsigned long>(result.GetLength());
+  if (buffer && len <= buflen)
+    memcpy(buffer, result.c_str(), len);
+
+  *out_buflen = len;
+  return true;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_StructElement_GetMarkedContentIdCount(FPDF_STRUCTELEMENT struct_element) {
+  CPDF_StructElement* elem =
+      CPDFStructElementFromFPDFStructElement(struct_element);
+  if (!elem)
+    return -1;
+  RetainPtr<const CPDF_Object> p = elem->GetK();
+  if (!p)
+    return -1;
+
+  if (p->IsNumber() || p->IsDictionary())
+    return 1;
+
+  return p->IsArray() ? fxcrt::CollectionSize<int>(*p->AsArray()) : -1;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_StructElement_GetMarkedContentIdAtIndex(FPDF_STRUCTELEMENT struct_element,
+                                             int index) {
+  CPDF_StructElement* elem =
+      CPDFStructElementFromFPDFStructElement(struct_element);
+  if (!elem)
+    return -1;
+  RetainPtr<const CPDF_Object> p = elem->GetK();
+  if (!p)
+    return -1;
+
+  if (p->IsNumber())
+    return index == 0 ? p->GetInteger() : -1;
+
+  if (p->IsDictionary())
+    return GetMcidFromDict(p->GetDict().Get());
+
+  if (p->IsArray()) {
+    const CPDF_Array* array = p->AsArray();
+    if (index < 0 || static_cast<size_t>(index) >= array->size())
+      return -1;
+    RetainPtr<const CPDF_Object> array_elem = array->GetObjectAt(index);
+    if (array_elem->IsNumber())
+      return array_elem->GetInteger();
+    if (array_elem->IsDictionary()) {
+      return GetMcidFromDict(array_elem->GetDict().Get());
+    }
+  }
+  return -1;
 }
diff --git a/fpdfsdk/fpdf_structtree_embeddertest.cpp b/fpdfsdk/fpdf_structtree_embeddertest.cpp
index bbaa115..2704887 100644
--- a/fpdfsdk/fpdf_structtree_embeddertest.cpp
+++ b/fpdfsdk/fpdf_structtree_embeddertest.cpp
@@ -1,11 +1,13 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "core/fxcrt/fx_string.h"
+#include <iterator>
+
 #include "public/fpdf_structtree.h"
 #include "testing/embedder_test.h"
-#include "third_party/base/optional.h"
+#include "testing/fx_string_testhelpers.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class FPDFStructTreeEmbedderTest : public EmbedderTest {};
 
@@ -21,33 +23,33 @@
 
     FPDF_STRUCTELEMENT element =
         FPDF_StructTree_GetChildAtIndex(struct_tree.get(), -1);
-    EXPECT_EQ(nullptr, element);
+    EXPECT_FALSE(element);
     element = FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 1);
-    EXPECT_EQ(nullptr, element);
+    EXPECT_FALSE(element);
     element = FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
-    ASSERT_NE(nullptr, element);
+    ASSERT_TRUE(element);
     EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(element));
     EXPECT_EQ(0U, FPDF_StructElement_GetAltText(element, nullptr, 0));
 
     ASSERT_EQ(1, FPDF_StructElement_CountChildren(element));
     FPDF_STRUCTELEMENT child_element =
         FPDF_StructElement_GetChildAtIndex(element, -1);
-    EXPECT_EQ(nullptr, child_element);
+    EXPECT_FALSE(child_element);
     child_element = FPDF_StructElement_GetChildAtIndex(element, 1);
-    EXPECT_EQ(nullptr, child_element);
+    EXPECT_FALSE(child_element);
     child_element = FPDF_StructElement_GetChildAtIndex(element, 0);
-    ASSERT_NE(nullptr, child_element);
+    ASSERT_TRUE(child_element);
     EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(child_element));
     EXPECT_EQ(0U, FPDF_StructElement_GetAltText(child_element, nullptr, 0));
 
     ASSERT_EQ(1, FPDF_StructElement_CountChildren(child_element));
     FPDF_STRUCTELEMENT gchild_element =
         FPDF_StructElement_GetChildAtIndex(child_element, -1);
-    EXPECT_EQ(nullptr, gchild_element);
+    EXPECT_FALSE(gchild_element);
     gchild_element = FPDF_StructElement_GetChildAtIndex(child_element, 1);
-    EXPECT_EQ(nullptr, gchild_element);
+    EXPECT_FALSE(gchild_element);
     gchild_element = FPDF_StructElement_GetChildAtIndex(child_element, 0);
-    ASSERT_NE(nullptr, gchild_element);
+    ASSERT_TRUE(gchild_element);
     EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(gchild_element));
     ASSERT_EQ(24U, FPDF_StructElement_GetAltText(gchild_element, nullptr, 0));
 
@@ -56,25 +58,265 @@
     // Deliberately pass in a small buffer size to make sure |buffer| remains
     // untouched.
     ASSERT_EQ(24U, FPDF_StructElement_GetAltText(gchild_element, buffer, 1));
-    for (size_t i = 0; i < FX_ArraySize(buffer); ++i)
+    for (size_t i = 0; i < std::size(buffer); ++i)
       EXPECT_EQ(0U, buffer[i]);
 
     EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(gchild_element));
     ASSERT_EQ(24U, FPDF_StructElement_GetAltText(gchild_element, buffer,
                                                  sizeof(buffer)));
-    const wchar_t kExpected[] = L"Black Image";
-    EXPECT_EQ(WideString(kExpected),
-              WideString::FromUTF16LE(buffer, FXSYS_len(kExpected)));
+    EXPECT_EQ(L"Black Image", GetPlatformWString(buffer));
 
     ASSERT_EQ(1, FPDF_StructElement_CountChildren(gchild_element));
     FPDF_STRUCTELEMENT ggchild_element =
         FPDF_StructElement_GetChildAtIndex(gchild_element, 0);
-    EXPECT_EQ(nullptr, ggchild_element);
+    EXPECT_FALSE(ggchild_element);
   }
 
   UnloadPage(page);
 }
 
+TEST_F(FPDFStructTreeEmbedderTest, GetActualText) {
+  ASSERT_TRUE(OpenDocument("tagged_actual_text.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+    ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
+
+    EXPECT_EQ(0U, FPDF_StructElement_GetActualText(nullptr, nullptr, 0));
+
+    FPDF_STRUCTELEMENT element =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
+    ASSERT_TRUE(element);
+    EXPECT_EQ(0U, FPDF_StructElement_GetActualText(element, nullptr, 0));
+
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(element));
+    FPDF_STRUCTELEMENT child_element =
+        FPDF_StructElement_GetChildAtIndex(element, 0);
+    ASSERT_TRUE(child_element);
+    EXPECT_EQ(0U, FPDF_StructElement_GetActualText(child_element, nullptr, 0));
+
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(child_element));
+    FPDF_STRUCTELEMENT gchild_element =
+        FPDF_StructElement_GetChildAtIndex(child_element, 0);
+    ASSERT_TRUE(gchild_element);
+    ASSERT_EQ(24U,
+              FPDF_StructElement_GetActualText(gchild_element, nullptr, 0));
+
+    unsigned short buffer[12] = {};
+    // Deliberately pass in a small buffer size to make sure |buffer| remains
+    // untouched.
+    ASSERT_EQ(24U, FPDF_StructElement_GetActualText(gchild_element, buffer, 1));
+    for (size_t i = 0; i < std::size(buffer); ++i)
+      EXPECT_EQ(0U, buffer[i]);
+    ASSERT_EQ(24U, FPDF_StructElement_GetActualText(gchild_element, buffer,
+                                                    sizeof(buffer)));
+    EXPECT_EQ(L"Actual Text", GetPlatformWString(buffer));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFStructTreeEmbedderTest, GetStringAttribute) {
+  ASSERT_TRUE(OpenDocument("tagged_table.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+    ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
+
+    FPDF_STRUCTELEMENT document =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
+    ASSERT_TRUE(document);
+
+    constexpr int kBufLen = 100;
+    uint16_t buffer[kBufLen] = {0};
+    EXPECT_EQ(18U, FPDF_StructElement_GetType(document, buffer, kBufLen));
+    EXPECT_EQ("Document", GetPlatformString(buffer));
+
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(document));
+    FPDF_STRUCTELEMENT table = FPDF_StructElement_GetChildAtIndex(document, 0);
+    ASSERT_TRUE(table);
+
+    EXPECT_EQ(12U, FPDF_StructElement_GetType(table, buffer, kBufLen));
+    EXPECT_EQ("Table", GetPlatformString(buffer));
+
+    // The table should have an attribute "Summary" set to the empty string.
+    EXPECT_EQ(2U, FPDF_StructElement_GetStringAttribute(table, "Summary",
+                                                        buffer, kBufLen));
+
+    ASSERT_EQ(2, FPDF_StructElement_CountChildren(table));
+    FPDF_STRUCTELEMENT row = FPDF_StructElement_GetChildAtIndex(table, 0);
+    ASSERT_TRUE(row);
+
+    ASSERT_EQ(2, FPDF_StructElement_CountChildren(row));
+    FPDF_STRUCTELEMENT header_cell = FPDF_StructElement_GetChildAtIndex(row, 0);
+    ASSERT_TRUE(header_cell);
+
+    EXPECT_EQ(6U, FPDF_StructElement_GetType(header_cell, buffer, kBufLen));
+    EXPECT_EQ("TH", GetPlatformString(buffer));
+
+    // The header should have an attribute "Scope" with a scope of "Row".
+    EXPECT_EQ(8U, FPDF_StructElement_GetStringAttribute(header_cell, "Scope",
+                                                        buffer, kBufLen));
+    EXPECT_EQ("Row", GetPlatformString(buffer));
+
+    // The header has an attribute "ColSpan", but it's not a string so it
+    // returns null.
+    EXPECT_EQ(0U, FPDF_StructElement_GetStringAttribute(header_cell, "ColSpan",
+                                                        buffer, kBufLen));
+
+    // An unsupported attribute should return 0.
+    EXPECT_EQ(0U, FPDF_StructElement_GetStringAttribute(header_cell, "Other",
+                                                        buffer, kBufLen));
+
+    // A null struct element should not crash.
+    EXPECT_EQ(0U, FPDF_StructElement_GetStringAttribute(nullptr, "Other",
+                                                        buffer, kBufLen));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFStructTreeEmbedderTest, GetStringAttributeBadStructElement) {
+  ASSERT_TRUE(OpenDocument("tagged_table_bad_elem.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+    ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
+
+    FPDF_STRUCTELEMENT document =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
+    ASSERT_TRUE(document);
+
+    constexpr int kBufLen = 100;
+    uint16_t buffer[kBufLen] = {0};
+    EXPECT_EQ(18U, FPDF_StructElement_GetType(document, buffer, kBufLen));
+    EXPECT_EQ("Document", GetPlatformString(buffer));
+
+    // The table can be retrieved, even though it does not have /Type.
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(document));
+    FPDF_STRUCTELEMENT table = FPDF_StructElement_GetChildAtIndex(document, 0);
+    ASSERT_TRUE(table);
+
+    EXPECT_EQ(12U, FPDF_StructElement_GetType(table, buffer, kBufLen));
+    EXPECT_EQ("Table", GetPlatformString(buffer));
+
+    // The table entry cannot be retrieved, as the element is malformed.
+    EXPECT_EQ(0U, FPDF_StructElement_GetStringAttribute(table, "Summary",
+                                                        buffer, kBufLen));
+
+    // The row can be retrieved, even though it had an invalid /Type.
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(table));
+    FPDF_STRUCTELEMENT row = FPDF_StructElement_GetChildAtIndex(table, 0);
+    EXPECT_TRUE(row);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFStructTreeEmbedderTest, GetID) {
+  ASSERT_TRUE(OpenDocument("tagged_table.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+    ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
+
+    FPDF_STRUCTELEMENT document =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
+    ASSERT_TRUE(document);
+
+    constexpr int kBufLen = 100;
+    uint16_t buffer[kBufLen] = {0};
+    EXPECT_EQ(18U, FPDF_StructElement_GetType(document, buffer, kBufLen));
+    EXPECT_EQ("Document", GetPlatformString(buffer));
+
+    // The document has no ID.
+    EXPECT_EQ(0U, FPDF_StructElement_GetID(document, buffer, kBufLen));
+
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(document));
+    FPDF_STRUCTELEMENT table = FPDF_StructElement_GetChildAtIndex(document, 0);
+    ASSERT_TRUE(table);
+
+    EXPECT_EQ(12U, FPDF_StructElement_GetType(table, buffer, kBufLen));
+    EXPECT_EQ("Table", GetPlatformString(buffer));
+
+    // The table has an ID.
+    EXPECT_EQ(14U, FPDF_StructElement_GetID(table, buffer, kBufLen));
+    EXPECT_EQ("node12", GetPlatformString(buffer));
+
+    // The first child of the table is a row, which has an empty ID.
+    // It returns 2U, the length of an empty string, instead of 0U,
+    // representing null.
+    ASSERT_EQ(2, FPDF_StructElement_CountChildren(table));
+    FPDF_STRUCTELEMENT row = FPDF_StructElement_GetChildAtIndex(table, 0);
+    ASSERT_TRUE(row);
+    EXPECT_EQ(2U, FPDF_StructElement_GetID(row, buffer, kBufLen));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFStructTreeEmbedderTest, GetLang) {
+  ASSERT_TRUE(OpenDocument("tagged_table.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+    ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
+
+    FPDF_STRUCTELEMENT document =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
+    ASSERT_TRUE(document);
+
+    constexpr int kBufLen = 100;
+    uint16_t buffer[kBufLen] = {0};
+    EXPECT_EQ(18U, FPDF_StructElement_GetType(document, buffer, kBufLen));
+    EXPECT_EQ("Document", GetPlatformString(buffer));
+
+    // Nullptr test
+    EXPECT_EQ(0U, FPDF_StructElement_GetLang(nullptr, buffer, kBufLen));
+
+    // The document has a language.
+    EXPECT_EQ(12U, FPDF_StructElement_GetLang(document, buffer, kBufLen));
+    EXPECT_EQ("en-US", GetPlatformString(buffer));
+
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(document));
+    FPDF_STRUCTELEMENT table = FPDF_StructElement_GetChildAtIndex(document, 0);
+    ASSERT_TRUE(table);
+
+    // The first child is a table, with a language.
+    EXPECT_EQ(12U, FPDF_StructElement_GetType(table, buffer, kBufLen));
+    EXPECT_EQ("Table", GetPlatformString(buffer));
+
+    EXPECT_EQ(6U, FPDF_StructElement_GetLang(table, buffer, kBufLen));
+    EXPECT_EQ("hu", GetPlatformString(buffer));
+
+    // The first child of the table is a row, which doesn't have a
+    // language explicitly set on it.
+    ASSERT_EQ(2, FPDF_StructElement_CountChildren(table));
+    FPDF_STRUCTELEMENT row = FPDF_StructElement_GetChildAtIndex(table, 0);
+    ASSERT_TRUE(row);
+    EXPECT_EQ(0U, FPDF_StructElement_GetLang(row, buffer, kBufLen));
+  }
+
+  UnloadPage(page);
+}
+
+// See also FPDFEditEmbedderTest.TraverseMarkedContentID, which traverses the
+// marked contents using FPDFPageObj_GetMark() and related API.
 TEST_F(FPDFStructTreeEmbedderTest, GetMarkedContentID) {
   ASSERT_TRUE(OpenDocument("marked_content_id.pdf"));
   FPDF_PAGE page = LoadPage(0);
@@ -93,6 +335,60 @@
   UnloadPage(page);
 }
 
+TEST_F(FPDFStructTreeEmbedderTest, GetMarkedContentIdAtIndex) {
+  ASSERT_TRUE(OpenDocument("tagged_marked_content.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+    ASSERT_EQ(4, FPDF_StructTree_CountChildren(struct_tree.get()));
+
+    // K is an integer MCID
+    FPDF_STRUCTELEMENT child1 =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
+    ASSERT_TRUE(child1);
+    // Legacy API
+    EXPECT_EQ(0, FPDF_StructElement_GetMarkedContentID(child1));
+
+    // K is a dict containing MCR object reference
+    FPDF_STRUCTELEMENT child2 =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 1);
+    ASSERT_TRUE(child2);
+
+    // K is an array containing dict MCR object reference and integer MCID
+    FPDF_STRUCTELEMENT child3 =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 2);
+    ASSERT_TRUE(child3);
+
+    // K does not exist
+    FPDF_STRUCTELEMENT child4 =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 3);
+    ASSERT_TRUE(child4);
+
+    // New APIs
+    EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdCount(nullptr));
+    EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdAtIndex(nullptr, 0));
+    EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdAtIndex(child1, -1));
+    EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdAtIndex(child1, 1));
+    EXPECT_EQ(1, FPDF_StructElement_GetMarkedContentIdCount(child1));
+    EXPECT_EQ(0, FPDF_StructElement_GetMarkedContentIdAtIndex(child1, 0));
+
+    EXPECT_EQ(1, FPDF_StructElement_GetMarkedContentIdCount(child2));
+    EXPECT_EQ(1, FPDF_StructElement_GetMarkedContentIdAtIndex(child2, 0));
+
+    EXPECT_EQ(2, FPDF_StructElement_GetMarkedContentIdCount(child3));
+    EXPECT_EQ(2, FPDF_StructElement_GetMarkedContentIdAtIndex(child3, 0));
+    EXPECT_EQ(3, FPDF_StructElement_GetMarkedContentIdAtIndex(child3, 1));
+
+    EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdCount(child4));
+    EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdAtIndex(child4, 0));
+  }
+
+  UnloadPage(page);
+}
+
 TEST_F(FPDFStructTreeEmbedderTest, GetType) {
   ASSERT_TRUE(OpenDocument("tagged_alt_text.pdf"));
   FPDF_PAGE page = LoadPage(0);
@@ -105,7 +401,7 @@
 
     FPDF_STRUCTELEMENT element =
         FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
-    ASSERT_NE(nullptr, element);
+    ASSERT_TRUE(element);
 
     // test nullptr inputs
     unsigned short buffer[12];
@@ -117,13 +413,95 @@
     // Deliberately pass in a small buffer size to make sure |buffer| remains
     // untouched.
     ASSERT_EQ(18U, FPDF_StructElement_GetType(element, buffer, 1));
-    for (size_t i = 0; i < FX_ArraySize(buffer); ++i)
+    for (size_t i = 0; i < std::size(buffer); ++i)
       EXPECT_EQ(0U, buffer[i]);
 
     ASSERT_EQ(18U, FPDF_StructElement_GetType(element, buffer, sizeof(buffer)));
-    const wchar_t kExpected[] = L"Document";
-    EXPECT_EQ(WideString(kExpected),
-              WideString::FromUTF16LE(buffer, FXSYS_len(kExpected)));
+    EXPECT_EQ(L"Document", GetPlatformWString(buffer));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFStructTreeEmbedderTest, GetObjType) {
+  ASSERT_TRUE(OpenDocument("tagged_table_bad_elem.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+    ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
+
+    FPDF_STRUCTELEMENT child =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
+    ASSERT_TRUE(child);
+
+    // test nullptr inputs
+    unsigned short buffer[28] = {};
+    ASSERT_EQ(0U,
+              FPDF_StructElement_GetObjType(nullptr, buffer, sizeof(buffer)));
+    ASSERT_EQ(0U, FPDF_StructElement_GetObjType(nullptr, nullptr, 0));
+    ASSERT_EQ(22U, FPDF_StructElement_GetObjType(child, nullptr, 0));
+
+    // Deliberately pass in a small buffer size to make sure `buffer` remains
+    // untouched.
+    ASSERT_EQ(22U, FPDF_StructElement_GetObjType(child, buffer, 1));
+    for (size_t i = 0; i < std::size(buffer); ++i)
+      EXPECT_EQ(0U, buffer[i]);
+
+    ASSERT_EQ(22U,
+              FPDF_StructElement_GetObjType(child, buffer, sizeof(buffer)));
+    EXPECT_EQ(L"StructElem", GetPlatformWString(buffer));
+
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(child));
+    FPDF_STRUCTELEMENT gchild = FPDF_StructElement_GetChildAtIndex(child, 0);
+    memset(buffer, 0, sizeof(buffer));
+    // Missing /Type in `gchild`
+    ASSERT_EQ(0U,
+              FPDF_StructElement_GetObjType(gchild, buffer, sizeof(buffer)));
+    // Buffer is untouched.
+    for (size_t i = 0; i < std::size(buffer); ++i)
+      EXPECT_EQ(0U, buffer[i]);
+
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(gchild));
+    FPDF_STRUCTELEMENT ggchild = FPDF_StructElement_GetChildAtIndex(gchild, 0);
+    ASSERT_EQ(28U,
+              FPDF_StructElement_GetObjType(ggchild, buffer, sizeof(buffer)));
+    // Reading bad elem also works.
+    EXPECT_EQ(L"NotStructElem", GetPlatformWString(buffer));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFStructTreeEmbedderTest, GetParent) {
+  ASSERT_TRUE(OpenDocument("tagged_alt_text.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+    ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
+
+    FPDF_STRUCTELEMENT parent =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
+    ASSERT_TRUE(parent);
+
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(parent));
+
+    FPDF_STRUCTELEMENT child = FPDF_StructElement_GetChildAtIndex(parent, 0);
+    ASSERT_TRUE(child);
+
+    // test nullptr inputs
+    ASSERT_EQ(nullptr, FPDF_StructElement_GetParent(nullptr));
+
+    ASSERT_EQ(parent, FPDF_StructElement_GetParent(child));
+
+    // The parent of `parent` is StructTreeRoot and no longer a StructElement.
+    // We currently handle this case by returning a nullptr.
+    ASSERT_EQ(nullptr, FPDF_StructElement_GetParent(parent));
   }
 
   UnloadPage(page);
@@ -141,7 +519,7 @@
 
     FPDF_STRUCTELEMENT element =
         FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
-    ASSERT_NE(nullptr, element);
+    ASSERT_TRUE(element);
 
     // test nullptr inputs
     unsigned short buffer[13];
@@ -153,26 +531,340 @@
     // Deliberately pass in a small buffer size to make sure |buffer| remains
     // untouched.
     ASSERT_EQ(20U, FPDF_StructElement_GetTitle(element, buffer, 1));
-    for (size_t i = 0; i < FX_ArraySize(buffer); ++i)
+    for (size_t i = 0; i < std::size(buffer); ++i)
       EXPECT_EQ(0U, buffer[i]);
 
     ASSERT_EQ(20U,
               FPDF_StructElement_GetTitle(element, buffer, sizeof(buffer)));
 
-    const wchar_t kExpected[] = L"TitleText";
-    EXPECT_EQ(WideString(kExpected),
-              WideString::FromUTF16LE(buffer, FXSYS_len(kExpected)));
+    EXPECT_EQ(L"TitleText", GetPlatformWString(buffer));
 
     ASSERT_EQ(1, FPDF_StructElement_CountChildren(element));
     FPDF_STRUCTELEMENT child_element =
         FPDF_StructElement_GetChildAtIndex(element, 0);
-    ASSERT_NE(nullptr, element);
+    ASSERT_TRUE(element);
 
     ASSERT_EQ(26U, FPDF_StructElement_GetTitle(child_element, buffer,
                                                sizeof(buffer)));
-    const wchar_t kChildExpected[] = L"symbol: 100k";
-    EXPECT_EQ(WideString(kChildExpected),
-              WideString::FromUTF16LE(buffer, FXSYS_len(kChildExpected)));
+    EXPECT_EQ(L"symbol: 100k", GetPlatformWString(buffer));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFStructTreeEmbedderTest, GetAttributes) {
+  ASSERT_TRUE(OpenDocument("tagged_table.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+    ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
+
+    FPDF_STRUCTELEMENT document =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
+    ASSERT_TRUE(document);
+
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(document));
+    ASSERT_EQ(-1, FPDF_StructElement_GetAttributeCount(document));
+    FPDF_STRUCTELEMENT table = FPDF_StructElement_GetChildAtIndex(document, 0);
+    ASSERT_TRUE(table);
+
+    ASSERT_EQ(2, FPDF_StructElement_CountChildren(table));
+
+    {
+      FPDF_STRUCTELEMENT tr = FPDF_StructElement_GetChildAtIndex(table, 0);
+      ASSERT_TRUE(tr);
+
+      ASSERT_EQ(2, FPDF_StructElement_CountChildren(tr));
+      FPDF_STRUCTELEMENT th = FPDF_StructElement_GetChildAtIndex(tr, 0);
+      ASSERT_TRUE(th);
+
+      ASSERT_EQ(2, FPDF_StructElement_GetAttributeCount(th));
+
+      // nullptr test
+      ASSERT_EQ(nullptr, FPDF_StructElement_GetAttributeAtIndex(document, 0));
+      ASSERT_EQ(nullptr, FPDF_StructElement_GetAttributeAtIndex(document, -1));
+      ASSERT_EQ(nullptr, FPDF_StructElement_GetAttributeAtIndex(th, 2));
+
+      FPDF_STRUCTELEMENT_ATTR attr =
+          FPDF_StructElement_GetAttributeAtIndex(th, 1);
+      ASSERT_TRUE(attr);
+
+      ASSERT_EQ(2, FPDF_StructElement_Attr_GetCount(attr));
+      ASSERT_FALSE(
+          FPDF_StructElement_Attr_GetName(attr, 1, nullptr, 0U, nullptr));
+      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
+      // untouched.
+      ASSERT_TRUE(
+          FPDF_StructElement_Attr_GetName(attr, 1, buffer, 1, &out_len));
+      EXPECT_EQ(2U, out_len);
+      for (size_t i = 0; i < std::size(buffer); ++i)
+        EXPECT_EQ(0, buffer[i]);
+
+      ASSERT_TRUE(FPDF_StructElement_Attr_GetName(attr, 1, buffer,
+                                                  sizeof(buffer), &out_len));
+      EXPECT_EQ(2U, out_len);
+      EXPECT_STREQ("O", buffer);
+      EXPECT_EQ(FPDF_OBJECT_NAME,
+                FPDF_StructElement_Attr_GetType(attr, buffer));
+
+      unsigned short str_val[12] = {};
+      ASSERT_TRUE(FPDF_StructElement_Attr_GetStringValue(
+          attr, buffer, str_val, sizeof(str_val), &out_len));
+      EXPECT_EQ(12U, out_len);
+      EXPECT_EQ(L"Table", GetPlatformWString(str_val));
+
+      memset(buffer, 0, sizeof(buffer));
+      ASSERT_TRUE(FPDF_StructElement_Attr_GetName(attr, 0, buffer,
+                                                  sizeof(buffer), &out_len));
+      EXPECT_EQ(8U, out_len);
+      EXPECT_STREQ("ColSpan", buffer);
+      EXPECT_EQ(FPDF_OBJECT_NUMBER,
+                FPDF_StructElement_Attr_GetType(attr, buffer));
+      float num_val;
+      ASSERT_TRUE(
+          FPDF_StructElement_Attr_GetNumberValue(attr, buffer, &num_val));
+      EXPECT_FLOAT_EQ(2.0f, num_val);
+    }
+
+    {
+      FPDF_STRUCTELEMENT tr = FPDF_StructElement_GetChildAtIndex(table, 1);
+      ASSERT_TRUE(tr);
+
+      ASSERT_EQ(1, FPDF_StructElement_GetAttributeCount(tr));
+      // nullptr when index out of range
+      ASSERT_EQ(nullptr, FPDF_StructElement_GetAttributeAtIndex(tr, 1));
+
+      ASSERT_EQ(2, FPDF_StructElement_CountChildren(tr));
+      FPDF_STRUCTELEMENT td = FPDF_StructElement_GetChildAtIndex(tr, 1);
+      ASSERT_TRUE(td);
+      {
+        // 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(4, FPDF_StructElement_Attr_GetCount(attr));
+        // Test string and blob type
+        {
+          char buffer[16] = {};
+          unsigned long out_len = ULONG_MAX;
+          ASSERT_TRUE(FPDF_StructElement_Attr_GetName(
+              attr, 0, buffer, sizeof(buffer), &out_len));
+          EXPECT_EQ(8U, out_len);
+          EXPECT_STREQ("ColProp", buffer);
+
+          EXPECT_EQ(FPDF_OBJECT_STRING,
+                    FPDF_StructElement_Attr_GetType(attr, buffer));
+
+          unsigned short str_val[12] = {};
+          ASSERT_TRUE(FPDF_StructElement_Attr_GetStringValue(
+              attr, buffer, str_val, sizeof(str_val), &out_len));
+          EXPECT_EQ(8U, out_len);
+          EXPECT_EQ(L"Sum", GetPlatformWString(str_val));
+
+          char blob_val[3] = {};
+          ASSERT_TRUE(FPDF_StructElement_Attr_GetBlobValue(
+              attr, buffer, blob_val, sizeof(blob_val), &out_len));
+          EXPECT_EQ(3U, out_len);
+          EXPECT_EQ('S', blob_val[0]);
+          EXPECT_EQ('u', blob_val[1]);
+          EXPECT_EQ('m', blob_val[2]);
+        }
+
+        // Test boolean type
+        {
+          char buffer[16] = {};
+          unsigned long out_len = ULONG_MAX;
+          ASSERT_TRUE(FPDF_StructElement_Attr_GetName(
+              attr, 1, buffer, sizeof(buffer), &out_len));
+          EXPECT_EQ(7U, out_len);
+          EXPECT_STREQ("CurUSD", buffer);
+
+          EXPECT_EQ(FPDF_OBJECT_BOOLEAN,
+                    FPDF_StructElement_Attr_GetType(attr, buffer));
+          FPDF_BOOL val;
+          ASSERT_TRUE(
+              FPDF_StructElement_Attr_GetBooleanValue(attr, buffer, &val));
+          EXPECT_TRUE(val);
+        }
+
+        // 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);
+        }
+      }
+    }
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFStructTreeEmbedderTest, GetStructTreeForNestedTaggedPDF) {
+  ASSERT_TRUE(OpenDocument("tagged_nested.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // This call should not crash. https://crbug.com/pdfium/1480
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+  }
+  UnloadPage(page);
+}
+
+TEST_F(FPDFStructTreeEmbedderTest, MarkedContentReferenceAndObjectReference) {
+  ASSERT_TRUE(OpenDocument("tagged_mcr_objr.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+    ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
+
+    FPDF_STRUCTELEMENT object8 =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
+    ASSERT_TRUE(object8);
+    unsigned short buffer[12];
+    ASSERT_EQ(18U, FPDF_StructElement_GetType(object8, buffer, sizeof(buffer)));
+    EXPECT_EQ(L"Document", GetPlatformWString(buffer));
+    EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object8));
+    ASSERT_EQ(2, FPDF_StructElement_CountChildren(object8));
+
+    // First branch. 10 -> 12 -> 13 -> Inline dict.
+    FPDF_STRUCTELEMENT object10 =
+        FPDF_StructElement_GetChildAtIndex(object8, 0);
+    ASSERT_TRUE(object10);
+    ASSERT_EQ(20U,
+              FPDF_StructElement_GetType(object10, buffer, sizeof(buffer)));
+    EXPECT_EQ(L"NonStruct", GetPlatformWString(buffer));
+    EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object10));
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(object10));
+
+    FPDF_STRUCTELEMENT object12 =
+        FPDF_StructElement_GetChildAtIndex(object10, 0);
+    ASSERT_TRUE(object12);
+    ASSERT_EQ(4U, FPDF_StructElement_GetType(object12, buffer, sizeof(buffer)));
+    EXPECT_EQ(L"P", GetPlatformWString(buffer));
+    EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object12));
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(object12));
+
+    FPDF_STRUCTELEMENT object13 =
+        FPDF_StructElement_GetChildAtIndex(object12, 0);
+    ASSERT_TRUE(object13);
+    ASSERT_EQ(20U,
+              FPDF_StructElement_GetType(object13, buffer, sizeof(buffer)));
+    EXPECT_EQ(L"NonStruct", GetPlatformWString(buffer));
+    EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object13));
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(object13));
+
+    // TODO(crbug.com/pdfium/672): Fetch this child element.
+    EXPECT_FALSE(FPDF_StructElement_GetChildAtIndex(object13, 0));
+
+    // Second branch. 11 -> 14 -> Inline dict.
+    //                         -> 15 -> Inline dict.
+    FPDF_STRUCTELEMENT object11 =
+        FPDF_StructElement_GetChildAtIndex(object8, 1);
+    ASSERT_TRUE(object11);
+    ASSERT_EQ(4U, FPDF_StructElement_GetType(object11, buffer, sizeof(buffer)));
+    EXPECT_EQ(L"P", GetPlatformWString(buffer));
+    EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object11));
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(object11));
+
+    FPDF_STRUCTELEMENT object14 =
+        FPDF_StructElement_GetChildAtIndex(object11, 0);
+    ASSERT_TRUE(object14);
+    ASSERT_EQ(20U,
+              FPDF_StructElement_GetType(object14, buffer, sizeof(buffer)));
+    EXPECT_EQ(L"NonStruct", GetPlatformWString(buffer));
+    EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object14));
+    ASSERT_EQ(2, FPDF_StructElement_CountChildren(object14));
+
+    // TODO(crbug.com/pdfium/672): Object 15 should be at index 1.
+    EXPECT_FALSE(FPDF_StructElement_GetChildAtIndex(object14, 1));
+    FPDF_STRUCTELEMENT object15 =
+        FPDF_StructElement_GetChildAtIndex(object14, 0);
+    ASSERT_TRUE(object15);
+    ASSERT_EQ(20U,
+              FPDF_StructElement_GetType(object15, buffer, sizeof(buffer)));
+    EXPECT_EQ(L"NonStruct", GetPlatformWString(buffer));
+    EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object15));
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(object15));
+
+    // TODO(crbug.com/pdfium/672): Fetch this child element.
+    EXPECT_FALSE(FPDF_StructElement_GetChildAtIndex(object15, 0));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFStructTreeEmbedderTest, Bug1768) {
+  ASSERT_TRUE(OpenDocument("bug_1768.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+    ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
+
+    // TODO(crbug.com/pdfium/1768): Fetch this child element. Then consider
+    // writing more of the test to make sure other elements in the tree can be
+    // fetched correctly as well.
+    EXPECT_FALSE(FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFStructTreeEmbedderTest, Bug1296920) {
+  ASSERT_TRUE(OpenDocument("bug_1296920.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+    ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
+
+    // Destroying this tree should not crash.
+  }
+
+  UnloadPage(page);
+}
+
+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_sysfontinfo.cpp b/fpdfsdk/fpdf_sysfontinfo.cpp
index 96ceef2..9c38bfa 100644
--- a/fpdfsdk/fpdf_sysfontinfo.cpp
+++ b/fpdfsdk/fpdf_sysfontinfo.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,32 +11,57 @@
 #include <memory>
 
 #include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/stl_util.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_fontmapper.h"
 #include "core/fxge/cfx_fontmgr.h"
 #include "core/fxge/cfx_gemodule.h"
 #include "core/fxge/fx_font.h"
 #include "core/fxge/systemfontinfo_iface.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
-static_assert(FXFONT_ANSI_CHARSET == FX_CHARSET_ANSI, "Charset must match");
-static_assert(FXFONT_DEFAULT_CHARSET == FX_CHARSET_Default,
+#ifdef PDF_ENABLE_XFA
+#include "xfa/fgas/font/cfgas_fontmgr.h"
+#include "xfa/fgas/font/cfgas_gemodule.h"
+#endif
+
+static_assert(FXFONT_ANSI_CHARSET == static_cast<int>(FX_Charset::kANSI),
               "Charset must match");
-static_assert(FXFONT_SYMBOL_CHARSET == FX_CHARSET_Symbol, "Charset must match");
-static_assert(FXFONT_SHIFTJIS_CHARSET == FX_CHARSET_ShiftJIS,
+static_assert(FXFONT_DEFAULT_CHARSET == static_cast<int>(FX_Charset::kDefault),
               "Charset must match");
-static_assert(FXFONT_HANGEUL_CHARSET == FX_CHARSET_Hangul,
+static_assert(FXFONT_SYMBOL_CHARSET == static_cast<int>(FX_Charset::kSymbol),
               "Charset must match");
-static_assert(FXFONT_GB2312_CHARSET == FX_CHARSET_ChineseSimplified,
+static_assert(FXFONT_SHIFTJIS_CHARSET ==
+                  static_cast<int>(FX_Charset::kShiftJIS),
               "Charset must match");
-static_assert(FXFONT_CHINESEBIG5_CHARSET == FX_CHARSET_ChineseTraditional,
+static_assert(FXFONT_HANGEUL_CHARSET == static_cast<int>(FX_Charset::kHangul),
               "Charset must match");
-static_assert(FXFONT_ARABIC_CHARSET == FX_CHARSET_MSWin_Arabic,
+static_assert(FXFONT_GB2312_CHARSET ==
+                  static_cast<int>(FX_Charset::kChineseSimplified),
               "Charset must match");
-static_assert(FXFONT_CYRILLIC_CHARSET == FX_CHARSET_MSWin_Cyrillic,
+static_assert(FXFONT_CHINESEBIG5_CHARSET ==
+                  static_cast<int>(FX_Charset::kChineseTraditional),
+              "Charset must match");
+static_assert(FXFONT_GREEK_CHARSET ==
+                  static_cast<int>(FX_Charset::kMSWin_Greek),
+              "Charset must match");
+static_assert(FXFONT_VIETNAMESE_CHARSET ==
+                  static_cast<int>(FX_Charset::kMSWin_Vietnamese),
+              "Charset must match");
+static_assert(FXFONT_HEBREW_CHARSET ==
+                  static_cast<int>(FX_Charset::kMSWin_Hebrew),
+              "Charset must match");
+static_assert(FXFONT_ARABIC_CHARSET ==
+                  static_cast<int>(FX_Charset::kMSWin_Arabic),
+              "Charset must match");
+static_assert(FXFONT_CYRILLIC_CHARSET ==
+                  static_cast<int>(FX_Charset::kMSWin_Cyrillic),
+              "Charset must match");
+static_assert(FXFONT_THAI_CHARSET == static_cast<int>(FX_Charset::kThai),
               "Charset must match");
 static_assert(FXFONT_EASTERNEUROPEAN_CHARSET ==
-                  FX_CHARSET_MSWin_EasternEuropean,
+                  static_cast<int>(FX_Charset::kMSWin_EasternEuropean),
               "Charset must match");
 static_assert(offsetof(CFX_Font::CharsetFontMap, charset) ==
                   offsetof(FPDF_CharsetFontMap, charset),
@@ -65,36 +90,36 @@
 
   void* MapFont(int weight,
                 bool bItalic,
-                int charset,
+                FX_Charset charset,
                 int pitch_family,
-                const char* family) override {
+                const ByteString& face) override {
     if (!m_pInfo->MapFont)
       return nullptr;
 
     int iExact;
-    return m_pInfo->MapFont(m_pInfo, weight, bItalic, charset, pitch_family,
-                            family, &iExact);
+    return m_pInfo->MapFont(m_pInfo, weight, bItalic, static_cast<int>(charset),
+                            pitch_family, face.c_str(), &iExact);
   }
 
-  void* GetFont(const char* family) override {
+  void* GetFont(const ByteString& family) override {
     if (!m_pInfo->GetFont)
       return nullptr;
-    return m_pInfo->GetFont(m_pInfo, family);
+    return m_pInfo->GetFont(m_pInfo, family.c_str());
   }
 
-  uint32_t GetFontData(void* hFont,
-                       uint32_t table,
-                       pdfium::span<uint8_t> buffer) override {
+  size_t GetFontData(void* hFont,
+                     uint32_t table,
+                     pdfium::span<uint8_t> buffer) override {
     if (!m_pInfo->GetFontData)
       return 0;
     return m_pInfo->GetFontData(m_pInfo, hFont, table, buffer.data(),
-                                buffer.size());
+                                fxcrt::CollectionSize<unsigned long>(buffer));
   }
 
   bool GetFaceName(void* hFont, ByteString* name) override {
     if (!m_pInfo->GetFaceName)
       return false;
-    uint32_t size = m_pInfo->GetFaceName(m_pInfo, hFont, nullptr, 0);
+    unsigned long size = m_pInfo->GetFaceName(m_pInfo, hFont, nullptr, 0);
     if (size == 0)
       return false;
     char* buffer = FX_Alloc(char, size);
@@ -104,11 +129,11 @@
     return true;
   }
 
-  bool GetFontCharset(void* hFont, int* charset) override {
+  bool GetFontCharset(void* hFont, FX_Charset* charset) override {
     if (!m_pInfo->GetFontCharset)
       return false;
 
-    *charset = m_pInfo->GetFontCharset(m_pInfo, hFont);
+    *charset = FX_GetCharsetFromInt(m_pInfo->GetFontCharset(m_pInfo, hFont));
     return true;
   }
 
@@ -118,14 +143,14 @@
   }
 
  private:
-  FPDF_SYSFONTINFO* const m_pInfo;
+  UnownedPtr<FPDF_SYSFONTINFO> const m_pInfo;
 };
 
 FPDF_EXPORT void FPDF_CALLCONV FPDF_AddInstalledFont(void* mapper,
                                                      const char* face,
                                                      int charset) {
   CFX_FontMapper* pMapper = static_cast<CFX_FontMapper*>(mapper);
-  pMapper->AddInstalledFont(face, charset);
+  pMapper->AddInstalledFont(face, FX_GetCharsetFromInt(charset));
 }
 
 FPDF_EXPORT void FPDF_CALLCONV
@@ -133,12 +158,16 @@
   if (pFontInfoExt->version != 1)
     return;
 
-  CFX_GEModule::Get()->GetFontMgr()->SetSystemFontInfo(
-      pdfium::MakeUnique<CFX_ExternalFontInfo>(pFontInfoExt));
+  CFX_GEModule::Get()->GetFontMgr()->GetBuiltinMapper()->SetSystemFontInfo(
+      std::make_unique<CFX_ExternalFontInfo>(pFontInfoExt));
+
+#ifdef PDF_ENABLE_XFA
+  CFGAS_GEModule::Get()->GetFontMgr()->EnumFonts();
+#endif
 }
 
 FPDF_EXPORT const FPDF_CharsetFontMap* FPDF_CALLCONV FPDF_GetDefaultTTFMap() {
-  return reinterpret_cast<const FPDF_CharsetFontMap*>(CFX_Font::defaultTTFMap);
+  return reinterpret_cast<const FPDF_CharsetFontMap*>(CFX_Font::kDefaultTTFMap);
 }
 
 struct FPDF_SYSFONTINFO_DEFAULT final : public FPDF_SYSFONTINFO {
@@ -147,7 +176,7 @@
 
 static void DefaultRelease(struct _FPDF_SYSFONTINFO* pThis) {
   auto* pDefault = static_cast<FPDF_SYSFONTINFO_DEFAULT*>(pThis);
-  delete pDefault->m_pFontInfo.Release();
+  delete pDefault->m_pFontInfo.ExtractAsDangling();
 }
 
 static void DefaultEnumFonts(struct _FPDF_SYSFONTINFO* pThis, void* pMapper) {
@@ -163,8 +192,8 @@
                             const char* family,
                             int* bExact) {
   auto* pDefault = static_cast<FPDF_SYSFONTINFO_DEFAULT*>(pThis);
-  return pDefault->m_pFontInfo->MapFont(weight, !!bItalic, charset,
-                                        pitch_family, family);
+  return pDefault->m_pFontInfo->MapFont(
+      weight, !!bItalic, FX_GetCharsetFromInt(charset), pitch_family, family);
 }
 
 void* DefaultGetFont(struct _FPDF_SYSFONTINFO* pThis, const char* family) {
@@ -178,7 +207,8 @@
                                         unsigned char* buffer,
                                         unsigned long buf_size) {
   auto* pDefault = static_cast<FPDF_SYSFONTINFO_DEFAULT*>(pThis);
-  return pDefault->m_pFontInfo->GetFontData(hFont, table, {buffer, buf_size});
+  return pdfium::base::checked_cast<unsigned long>(
+      pDefault->m_pFontInfo->GetFontData(hFont, table, {buffer, buf_size}));
 }
 
 static unsigned long DefaultGetFaceName(struct _FPDF_SYSFONTINFO* pThis,
@@ -189,20 +219,21 @@
   auto* pDefault = static_cast<FPDF_SYSFONTINFO_DEFAULT*>(pThis);
   if (!pDefault->m_pFontInfo->GetFaceName(hFont, &name))
     return 0;
-  if (name.GetLength() >= static_cast<size_t>(buf_size))
-    return name.GetLength() + 1;
 
-  strncpy(buffer, name.c_str(),
-          (name.GetLength() + 1) * sizeof(ByteString::CharType));
-  return name.GetLength() + 1;
+  const unsigned long copy_length =
+      pdfium::base::checked_cast<unsigned long>(name.GetLength() + 1);
+  if (copy_length <= buf_size)
+    strncpy(buffer, name.c_str(), copy_length * sizeof(ByteString::CharType));
+
+  return copy_length;
 }
 
 static int DefaultGetFontCharset(struct _FPDF_SYSFONTINFO* pThis, void* hFont) {
-  int charset;
+  FX_Charset charset;
   auto* pDefault = static_cast<FPDF_SYSFONTINFO_DEFAULT*>(pThis);
   if (!pDefault->m_pFontInfo->GetFontCharset(hFont, &charset))
     return 0;
-  return charset;
+  return static_cast<int>(charset);
 }
 
 static void DefaultDeleteFont(struct _FPDF_SYSFONTINFO* pThis, void* hFont) {
@@ -212,7 +243,7 @@
 
 FPDF_EXPORT FPDF_SYSFONTINFO* FPDF_CALLCONV FPDF_GetDefaultSystemFontInfo() {
   std::unique_ptr<SystemFontInfoIface> pFontInfo =
-      SystemFontInfoIface::CreateDefault(nullptr);
+      CFX_GEModule::Get()->GetPlatform()->CreateDefaultSystemFontInfo();
   if (!pFontInfo)
     return nullptr;
 
diff --git a/fpdfsdk/fpdf_sysfontinfo_embeddertest.cpp b/fpdfsdk/fpdf_sysfontinfo_embeddertest.cpp
index 61cec04..24df525 100644
--- a/fpdfsdk/fpdf_sysfontinfo_embeddertest.cpp
+++ b/fpdfsdk/fpdf_sysfontinfo_embeddertest.cpp
@@ -1,12 +1,15 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "public/fpdf_sysfontinfo.h"
 
+#include <vector>
+
 #include "testing/embedder_test.h"
+#include "testing/embedder_test_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/stl_util.h"
 
 namespace {
 
@@ -73,6 +76,15 @@
     FPDF_SetSystemFontInfo(&font_info_);
   }
 
+  void TearDown() override {
+    EmbedderTest::TearDown();
+
+    // Bouncing the library is the only reliable way to undo the
+    // FPDF_SetSystemFontInfo() call at the moment.
+    EmbedderTestEnvironment::GetInstance()->TearDown();
+    EmbedderTestEnvironment::GetInstance()->SetUp();
+  }
+
   FPDF_SYSFONTINFO font_info_;
 };
 
@@ -90,7 +102,15 @@
 
   void TearDown() override {
     EmbedderTest::TearDown();
+
+    // Bouncing the library is the only reliable way to undo the
+    // FPDF_SetSystemFontInfo() call at the moment.
+    EmbedderTestEnvironment::GetInstance()->TearDown();
+
+    // After shutdown, it is safe to release the font info.
     FPDF_FreeDefaultSystemFontInfo(font_info_);
+
+    EmbedderTestEnvironment::GetInstance()->SetUp();
   }
 
   FPDF_SYSFONTINFO* font_info_;
@@ -125,31 +145,26 @@
 }
 
 TEST_F(FPDFSysFontInfoEmbedderTest, DefaultTTFMap) {
-  static const int kAllowedCharsets[] = {
-      FXFONT_ANSI_CHARSET,        FXFONT_DEFAULT_CHARSET,
-      FXFONT_SYMBOL_CHARSET,      FXFONT_SHIFTJIS_CHARSET,
+  static constexpr int kExpectedCharsets[] = {
+      FXFONT_ANSI_CHARSET,        FXFONT_SHIFTJIS_CHARSET,
       FXFONT_HANGEUL_CHARSET,     FXFONT_GB2312_CHARSET,
       FXFONT_CHINESEBIG5_CHARSET, FXFONT_ARABIC_CHARSET,
       FXFONT_CYRILLIC_CHARSET,    FXFONT_EASTERNEUROPEAN_CHARSET,
   };
-  std::set<int> seen_charsets;
+  std::vector<int> charsets;
 
   const FPDF_CharsetFontMap* cfmap = FPDF_GetDefaultTTFMap();
   ASSERT_TRUE(cfmap);
 
   // Stop at either end mark.
   while (cfmap->charset != -1 && cfmap->fontname) {
-    // Only returns values described as legitimate in public header.
-    EXPECT_TRUE(pdfium::ContainsValue(kAllowedCharsets, cfmap->charset))
-        << " for " << cfmap->charset;
-
-    // Duplicates are not allowed.
-    EXPECT_TRUE(seen_charsets.insert(cfmap->charset).second)
-        << " for " << cfmap->charset;
+    charsets.push_back(cfmap->charset);
     ++cfmap;
   }
 
   // Confirm end marks only occur as a pair.
   EXPECT_EQ(cfmap->charset, -1);
   EXPECT_EQ(cfmap->fontname, nullptr);
+
+  EXPECT_THAT(charsets, testing::UnorderedElementsAreArray(kExpectedCharsets));
 }
diff --git a/fpdfsdk/fpdf_text.cpp b/fpdfsdk/fpdf_text.cpp
index 5edbf27..d022ac3 100644
--- a/fpdfsdk/fpdf_text.cpp
+++ b/fpdfsdk/fpdf_text.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,6 @@
 #include <vector>
 
 #include "build/build_config.h"
-#include "core/fpdfapi/font/cpdf_cidfont.h"
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/page/cpdf_textobject.h"
@@ -19,14 +18,10 @@
 #include "core/fpdftext/cpdf_linkextract.h"
 #include "core/fpdftext/cpdf_textpage.h"
 #include "core/fpdftext/cpdf_textpagefind.h"
+#include "core/fxcrt/stl_util.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
+#include "third_party/base/check_op.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-#if defined(OS_WIN)
-#include <tchar.h>
-#endif
 
 namespace {
 
@@ -49,7 +44,7 @@
 
   CPDF_ViewerPreferences viewRef(pPDFPage->GetDocument());
   auto textpage =
-      pdfium::MakeUnique<CPDF_TextPage>(pPDFPage, viewRef.IsDirectionR2L());
+      std::make_unique<CPDF_TextPage>(pPDFPage, viewRef.IsDirectionR2L());
 
   // Caller takes ownership.
   return FPDFTextPageFromCPDFTextPage(textpage.release());
@@ -62,11 +57,8 @@
 }
 
 FPDF_EXPORT int FPDF_CALLCONV FPDFText_CountChars(FPDF_TEXTPAGE text_page) {
-  if (!text_page)
-    return -1;
-
   CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
-  return textpage->CountChars();
+  return textpage ? textpage->CountChars() : -1;
 }
 
 FPDF_EXPORT unsigned int FPDF_CALLCONV
@@ -79,6 +71,26 @@
   return charinfo.m_Unicode;
 }
 
+FPDF_EXPORT int FPDF_CALLCONV FPDFText_IsGenerated(FPDF_TEXTPAGE text_page,
+                                                   int index) {
+  CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
+  if (!textpage)
+    return -1;
+
+  const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
+  return charinfo.m_CharType == CPDF_TextPage::CharType::kGenerated ? 1 : 0;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFText_HasUnicodeMapError(FPDF_TEXTPAGE text_page, int index) {
+  CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
+  if (!textpage)
+    return -1;
+
+  const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
+  return charinfo.m_CharType == CPDF_TextPage::CharType::kNotUnicode;
+}
+
 FPDF_EXPORT double FPDF_CALLCONV FPDFText_GetFontSize(FPDF_TEXTPAGE text_page,
                                                       int index) {
   CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
@@ -107,7 +119,8 @@
     *flags = font->GetFontFlags();
 
   ByteString basefont = font->GetBaseFontName();
-  unsigned long length = basefont.GetLength() + 1;
+  const unsigned long length =
+      pdfium::base::checked_cast<unsigned long>(basefont.GetLength() + 1);
   if (buffer && buflen >= length)
     memcpy(buffer, basefont.c_str(), length);
 
@@ -205,7 +218,7 @@
   // Calculate the angle of the vector
   float angle = atan2f(charinfo.m_Matrix.c, charinfo.m_Matrix.a);
   if (angle < 0)
-    angle = 2 * FX_PI + angle;
+    angle = 2 * FXSYS_PI + angle;
 
   return angle;
 }
@@ -240,46 +253,7 @@
   if (!textpage)
     return false;
 
-  const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
-  float font_size = textpage->GetCharFontSize(index);
-
-  if (charinfo.m_pTextObj && !IsFloatZero(font_size)) {
-    bool is_vert_writing = charinfo.m_pTextObj->GetFont()->IsVertWriting();
-    if (is_vert_writing && charinfo.m_pTextObj->GetFont()->IsCIDFont()) {
-      CPDF_CIDFont* pCIDFont = charinfo.m_pTextObj->GetFont()->AsCIDFont();
-      uint16_t cid = pCIDFont->CIDFromCharCode(charinfo.m_CharCode);
-
-      short vx;
-      short vy;
-      pCIDFont->GetVertOrigin(cid, vx, vy);
-      double offsetx = (vx - 500) * font_size / 1000.0;
-      double offsety = vy * font_size / 1000.0;
-      short vert_width = pCIDFont->GetVertWidth(cid);
-      double height = vert_width * font_size / 1000.0;
-
-      rect->left = charinfo.m_Origin.x + offsetx;
-      rect->right = rect->left + font_size;
-      rect->bottom = charinfo.m_Origin.y + offsety;
-      rect->top = rect->bottom + height;
-      return true;
-    }
-
-    int ascent = charinfo.m_pTextObj->GetFont()->GetTypeAscent();
-    int descent = charinfo.m_pTextObj->GetFont()->GetTypeDescent();
-    if (ascent != descent) {
-      float width = charinfo.m_pTextObj->GetCharWidth(charinfo.m_CharCode);
-      float font_scale = font_size / (ascent - descent);
-
-      rect->left = charinfo.m_Origin.x;
-      rect->right = charinfo.m_Origin.x + (is_vert_writing ? -width : width);
-      rect->bottom = charinfo.m_Origin.y + descent * font_scale;
-      rect->top = charinfo.m_Origin.y + ascent * font_scale;
-      return true;
-    }
-  }
-
-  // Fallback to the tight bounds in empty text scenarios, or bad font metrics
-  *rect = FSRectFFromCFXFloatRect(charinfo.m_CharBox);
+  *rect = FSRectFFromCFXFloatRect(textpage->GetCharLooseBounds(index));
   return true;
 }
 
@@ -319,10 +293,10 @@
                            double y,
                            double xTolerance,
                            double yTolerance) {
-  if (!text_page)
+  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
+  if (!textpage)
     return -3;
 
-  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
   return textpage->GetIndexAtPos(
       CFX_PointF(static_cast<float>(x), static_cast<float>(y)),
       CFX_SizeF(static_cast<float>(xTolerance),
@@ -333,10 +307,10 @@
                                                int start_index,
                                                int char_count,
                                                unsigned short* result) {
-  if (!page || start_index < 0 || char_count < 0 || !result)
+  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(page);
+  if (!textpage || start_index < 0 || char_count < 0 || !result)
     return 0;
 
-  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(page);
   int char_available = textpage->CountChars() - start_index;
   if (char_available <= 0)
     return 0;
@@ -357,21 +331,20 @@
   // the number of items to stay the same.
   ByteString byte_str = str.ToUTF16LE();
   size_t byte_str_len = byte_str.GetLength();
-  int ret_count = byte_str_len / kBytesPerCharacter;
+  size_t ret_count = byte_str_len / kBytesPerCharacter;
 
-  ASSERT(ret_count <= char_count + 1);  // +1 to account for the NUL terminator.
+  // +1 to account for the NUL terminator.
+  DCHECK_LE(ret_count, static_cast<size_t>(char_count) + 1);
+
   memcpy(result, byte_str.c_str(), byte_str_len);
-  return ret_count;
+  return pdfium::base::checked_cast<int>(ret_count);
 }
 
 FPDF_EXPORT int FPDF_CALLCONV FPDFText_CountRects(FPDF_TEXTPAGE text_page,
                                                   int start,
                                                   int count) {
-  if (!text_page)
-    return 0;
-
   CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
-  return textpage->CountRects(start, count);
+  return textpage ? textpage->CountRects(start, count) : 0;
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_GetRect(FPDF_TEXTPAGE text_page,
@@ -380,10 +353,10 @@
                                                      double* top,
                                                      double* right,
                                                      double* bottom) {
-  if (!text_page)
+  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
+  if (!textpage)
     return false;
 
-  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
   CFX_FloatRect rect;
   bool result = textpage->GetRect(rect_index, &rect);
 
@@ -401,22 +374,22 @@
                                                       double bottom,
                                                       unsigned short* buffer,
                                                       int buflen) {
-  if (!text_page)
+  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
+  if (!textpage)
     return 0;
 
-  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
   CFX_FloatRect rect((float)left, (float)bottom, (float)right, (float)top);
   WideString str = textpage->GetTextByRect(rect);
 
   if (buflen <= 0 || !buffer)
-    return str.GetLength();
+    return pdfium::base::checked_cast<int>(str.GetLength());
 
   ByteString cbUTF16Str = str.ToUTF16LE();
-  int len = cbUTF16Str.GetLength() / sizeof(unsigned short);
+  int len = pdfium::base::checked_cast<int>(cbUTF16Str.GetLength()) /
+            sizeof(unsigned short);
   int size = buflen > len ? len : buflen;
   memcpy(buffer, cbUTF16Str.c_str(), size * sizeof(unsigned short));
   cbUTF16Str.ReleaseBuffer(size * sizeof(unsigned short));
-
   return size;
 }
 
@@ -425,7 +398,8 @@
                    FPDF_WIDESTRING findwhat,
                    unsigned long flags,
                    int start_index) {
-  if (!text_page)
+  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
+  if (!textpage)
     return nullptr;
 
   CPDF_TextPageFind::Options options;
@@ -433,9 +407,8 @@
   options.bMatchWholeWord = !!(flags & FPDF_MATCHWHOLEWORD);
   options.bConsecutive = !!(flags & FPDF_CONSECUTIVE);
   auto find = CPDF_TextPageFind::Create(
-      CPDFTextPageFromFPDFTextPage(text_page),
-      WideStringFromFPDFWideString(findwhat), options,
-      start_index >= 0 ? Optional<size_t>(start_index) : pdfium::nullopt);
+      textpage, WideStringFromFPDFWideString(findwhat), options,
+      start_index >= 0 ? absl::optional<size_t>(start_index) : absl::nullopt);
 
   // Caller takes ownership.
   return FPDFSchHandleFromCPDFTextPageFind(find.release());
@@ -486,15 +459,15 @@
 // web link
 FPDF_EXPORT FPDF_PAGELINK FPDF_CALLCONV
 FPDFLink_LoadWebLinks(FPDF_TEXTPAGE text_page) {
-  if (!text_page)
+  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
+  if (!textpage)
     return nullptr;
 
-  CPDF_TextPage* pPage = CPDFTextPageFromFPDFTextPage(text_page);
-  auto pageLink = pdfium::MakeUnique<CPDF_LinkExtract>(pPage);
-  pageLink->ExtractLinks();
+  auto pagelink = std::make_unique<CPDF_LinkExtract>(textpage);
+  pagelink->ExtractLinks();
 
   // Caller takes ownership.
-  return FPDFPageLinkFromCPDFLinkExtract(pageLink.release());
+  return FPDFPageLinkFromCPDFLinkExtract(pagelink.release());
 }
 
 FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountWebLinks(FPDF_PAGELINK link_page) {
@@ -515,7 +488,8 @@
     wsUrl = pageLink->GetURL(link_index);
   }
   ByteString cbUTF16URL = wsUrl.ToUTF16LE();
-  int required = cbUTF16URL.GetLength() / sizeof(unsigned short);
+  int required = pdfium::base::checked_cast<int>(cbUTF16URL.GetLength() /
+                                                 sizeof(unsigned short));
   if (!buffer || buflen <= 0)
     return required;
 
@@ -533,7 +507,7 @@
     return 0;
 
   CPDF_LinkExtract* pageLink = CPDFLinkExtractFromFPDFPageLink(link_page);
-  return pdfium::CollectionSize<int>(pageLink->GetRects(link_index));
+  return fxcrt::CollectionSize<int>(pageLink->GetRects(link_index));
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetRect(FPDF_PAGELINK link_page,
@@ -548,7 +522,7 @@
 
   CPDF_LinkExtract* pageLink = CPDFLinkExtractFromFPDFPageLink(link_page);
   std::vector<CFX_FloatRect> rectArray = pageLink->GetRects(link_index);
-  if (rect_index >= pdfium::CollectionSize<int>(rectArray))
+  if (rect_index >= fxcrt::CollectionSize<int>(rectArray))
     return false;
 
   *left = rectArray[rect_index].left;
@@ -567,7 +541,14 @@
     return false;
 
   CPDF_LinkExtract* page_link = CPDFLinkExtractFromFPDFPageLink(link_page);
-  return page_link->GetTextRange(link_index, start_char_index, char_count);
+  auto maybe_range = page_link->GetTextRange(link_index);
+  if (!maybe_range.has_value())
+    return false;
+
+  *start_char_index =
+      pdfium::base::checked_cast<int>(maybe_range.value().m_Start);
+  *char_count = pdfium::base::checked_cast<int>(maybe_range.value().m_Count);
+  return true;
 }
 
 FPDF_EXPORT void FPDF_CALLCONV FPDFLink_CloseWebLinks(FPDF_PAGELINK link_page) {
diff --git a/fpdfsdk/fpdf_text_embeddertest.cpp b/fpdfsdk/fpdf_text_embeddertest.cpp
index 06ff4cc..8d8b4ee 100644
--- a/fpdfsdk/fpdf_text_embeddertest.cpp
+++ b/fpdfsdk/fpdf_text_embeddertest.cpp
@@ -1,16 +1,15 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include <algorithm>
-#include <memory>
 #include <utility>
 #include <vector>
 
 #include "build/build_config.h"
-#include "core/fxcrt/fx_memory.h"
 #include "core/fxge/fx_font.h"
 #include "public/cpp/fpdf_scopers.h"
+#include "public/fpdf_doc.h"
 #include "public/fpdf_text.h"
 #include "public/fpdf_transformpage.h"
 #include "public/fpdfview.h"
@@ -21,7 +20,7 @@
 namespace {
 
 constexpr char kHelloGoodbyeText[] = "Hello, world!\r\nGoodbye, world!";
-constexpr int kHelloGoodbyeTextSize = FX_ArraySize(kHelloGoodbyeText);
+constexpr int kHelloGoodbyeTextSize = std::size(kHelloGoodbyeText);
 
 bool check_unsigned_shorts(const char* expected,
                            const unsigned short* actual,
@@ -120,10 +119,10 @@
       FPDFText_GetCharBox(textpage, 4, nullptr, nullptr, nullptr, nullptr));
 
   EXPECT_TRUE(FPDFText_GetCharBox(textpage, 4, &left, &right, &bottom, &top));
-  EXPECT_NEAR(41.071, left, 0.001);
-  EXPECT_NEAR(46.243, right, 0.001);
-  EXPECT_NEAR(49.844, bottom, 0.001);
-  EXPECT_NEAR(55.520, top, 0.001);
+  EXPECT_NEAR(41.120, left, 0.001);
+  EXPECT_NEAR(46.208, right, 0.001);
+  EXPECT_NEAR(49.892, bottom, 0.001);
+  EXPECT_NEAR(55.652, top, 0.001);
 
   FS_RECTF rect = {4.0f, 1.0f, 3.0f, 2.0f};
   EXPECT_FALSE(FPDFText_GetLooseCharBox(nullptr, 4, &rect));
@@ -172,10 +171,10 @@
   bottom = 0.0;
   top = 0.0;
   EXPECT_TRUE(FPDFText_GetRect(textpage, 1, &left, &top, &right, &bottom));
-  EXPECT_NEAR(20.847, left, 0.001);
-  EXPECT_NEAR(135.167, right, 0.001);
-  EXPECT_NEAR(96.655, bottom, 0.001);
-  EXPECT_NEAR(116.000, top, 0.001);
+  EXPECT_NEAR(20.800, left, 0.001);
+  EXPECT_NEAR(135.040, right, 0.001);
+  EXPECT_NEAR(96.688, bottom, 0.001);
+  EXPECT_NEAR(111.600, top, 0.001);
 
   // Test out of range indicies set outputs to (0.0, 0.0, 0.0, 0.0).
   left = -1.0;
@@ -211,7 +210,7 @@
   memset(buffer, 0xbd, sizeof(buffer));
   EXPECT_EQ(
       9, FPDFText_GetBoundedText(textpage, 41.0, 56.0, 82.0, 48.0, buffer, 9));
-  EXPECT_TRUE(check_unsigned_shorts(kHelloGoodbyeText + 4, buffer, 9));
+  EXPECT_TRUE(check_unsigned_shorts(kHelloGoodbyeText + 4, buffer, 8));
   EXPECT_EQ(0xbdbd, buffer[9]);
 
   memset(buffer, 0xbd, sizeof(buffer));
@@ -262,6 +261,37 @@
   UnloadPage(page);
 }
 
+TEST_F(FPDFTextEmbedderTest, TextHebrewMirrored) {
+  ASSERT_TRUE(OpenDocument("hebrew_mirrored.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFTextPage textpage(FPDFText_LoadPage(page));
+    ASSERT_TRUE(textpage);
+
+    constexpr int kCharCount = 10;
+    ASSERT_EQ(kCharCount, FPDFText_CountChars(textpage.get()));
+
+    unsigned short buffer[kCharCount + 1];
+    memset(buffer, 0x42, sizeof(buffer));
+    EXPECT_EQ(kCharCount + 1,
+              FPDFText_GetText(textpage.get(), 0, kCharCount, buffer));
+    EXPECT_EQ(0x05d1, buffer[0]);
+    EXPECT_EQ(0x05e0, buffer[1]);
+    EXPECT_EQ(0x05d9, buffer[2]);
+    EXPECT_EQ(0x05de, buffer[3]);
+    EXPECT_EQ(0x05d9, buffer[4]);
+    EXPECT_EQ(0x05df, buffer[5]);
+    EXPECT_EQ(0x000d, buffer[6]);
+    EXPECT_EQ(0x000a, buffer[7]);
+    EXPECT_EQ(0x05df, buffer[8]);
+    EXPECT_EQ(0x05d1, buffer[9]);
+  }
+
+  UnloadPage(page);
+}
+
 TEST_F(FPDFTextEmbedderTest, TextSearch) {
   ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   FPDF_PAGE page = LoadPage(0);
@@ -463,7 +493,7 @@
 }
 
 // Fails on Windows. https://crbug.com/pdfium/1370
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #define MAYBE_TextSearchLatinExtended DISABLED_TextSearchLatinExtended
 #else
 #define MAYBE_TextSearchLatinExtended TextSearchLatinExtended
@@ -593,10 +623,10 @@
   double top = 0.0;
   double bottom = 0.0;
   EXPECT_TRUE(FPDFLink_GetRect(pagelink, 0, 0, &left, &top, &right, &bottom));
-  EXPECT_NEAR(50.791, left, 0.001);
-  EXPECT_NEAR(187.963, right, 0.001);
-  EXPECT_NEAR(97.624, bottom, 0.001);
-  EXPECT_NEAR(108.736, top, 0.001);
+  EXPECT_NEAR(50.828, left, 0.001);
+  EXPECT_NEAR(187.904, right, 0.001);
+  EXPECT_NEAR(97.516, bottom, 0.001);
+  EXPECT_NEAR(108.700, top, 0.001);
 
   // Check that valid link with invalid rect index leaves parameters unchanged.
   left = -1.0;
@@ -645,7 +675,7 @@
       "http://example.com/",
       "http://www.abc.com",
   };
-  static const int kNumLinks = static_cast<int>(FX_ArraySize(kExpectedUrls));
+  static const int kNumLinks = static_cast<int>(std::size(kExpectedUrls));
 
   EXPECT_EQ(kNumLinks, FPDFLink_CountWebLinks(pagelink));
 
@@ -656,7 +686,7 @@
     EXPECT_EQ(static_cast<int>(expected_len),
               FPDFLink_GetURL(pagelink, i, nullptr, 0));
     EXPECT_EQ(static_cast<int>(expected_len),
-              FPDFLink_GetURL(pagelink, i, buffer, FX_ArraySize(buffer)));
+              FPDFLink_GetURL(pagelink, i, buffer, std::size(buffer)));
     EXPECT_TRUE(check_unsigned_shorts(kExpectedUrls[i], buffer, expected_len));
   }
 
@@ -683,8 +713,7 @@
   static const int kUrlSize = static_cast<int>(sizeof(kExpectedUrl));
 
   EXPECT_EQ(kUrlSize, FPDFLink_GetURL(pagelink, 1, nullptr, 0));
-  EXPECT_EQ(kUrlSize,
-            FPDFLink_GetURL(pagelink, 1, buffer, FX_ArraySize(buffer)));
+  EXPECT_EQ(kUrlSize, FPDFLink_GetURL(pagelink, 1, buffer, std::size(buffer)));
   EXPECT_TRUE(check_unsigned_shorts(kExpectedUrl, buffer, kUrlSize));
 
   FPDFLink_CloseWebLinks(pagelink);
@@ -741,13 +770,13 @@
 }
 
 TEST_F(FPDFTextEmbedderTest, AnnotLinks) {
-  ASSERT_TRUE(OpenDocument("link_annots.pdf"));
+  ASSERT_TRUE(OpenDocument("annots.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
   // Get link count via checking annotation subtype
   int annot_count = FPDFPage_GetAnnotCount(page);
-  ASSERT_EQ(8, annot_count);
+  ASSERT_EQ(9, annot_count);
   int annot_subtype_link_count = 0;
   for (int i = 0; i < annot_count; ++i) {
     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, i));
@@ -818,7 +847,7 @@
                                         16, 16, 16, 16, 16, 16, 16, 16, 16, 16};
 
   int count = FPDFText_CountChars(textpage);
-  ASSERT_EQ(FX_ArraySize(kExpectedFontsSizes), static_cast<size_t>(count));
+  ASSERT_EQ(std::size(kExpectedFontsSizes), static_cast<size_t>(count));
   for (int i = 0; i < count; ++i)
     EXPECT_EQ(kExpectedFontsSizes[i], FPDFText_GetFontSize(textpage, i)) << i;
 
@@ -923,6 +952,67 @@
   UnloadPage(page);
 }
 
+TEST_F(FPDFTextEmbedderTest, IsGenerated) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFTextPage textpage(FPDFText_LoadPage(page));
+    ASSERT_TRUE(textpage);
+
+    EXPECT_EQ(static_cast<unsigned int>('H'),
+              FPDFText_GetUnicode(textpage.get(), 0));
+    EXPECT_EQ(0, FPDFText_IsGenerated(textpage.get(), 0));
+    EXPECT_EQ(static_cast<unsigned int>(' '),
+              FPDFText_GetUnicode(textpage.get(), 6));
+    EXPECT_EQ(0, FPDFText_IsGenerated(textpage.get(), 6));
+
+    EXPECT_EQ(static_cast<unsigned int>('\r'),
+              FPDFText_GetUnicode(textpage.get(), 13));
+    EXPECT_EQ(1, FPDFText_IsGenerated(textpage.get(), 13));
+    EXPECT_EQ(static_cast<unsigned int>('\n'),
+              FPDFText_GetUnicode(textpage.get(), 14));
+    EXPECT_EQ(1, FPDFText_IsGenerated(textpage.get(), 14));
+
+    EXPECT_EQ(-1, FPDFText_IsGenerated(textpage.get(), -1));
+    EXPECT_EQ(-1, FPDFText_IsGenerated(textpage.get(), kHelloGoodbyeTextSize));
+    EXPECT_EQ(-1, FPDFText_IsGenerated(nullptr, 6));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, IsInvalidUnicode) {
+  ASSERT_TRUE(OpenDocument("bug_1388_2.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    constexpr int kExpectedCharCount = 5;
+    ScopedFPDFTextPage textpage(FPDFText_LoadPage(page));
+    ASSERT_TRUE(textpage);
+    EXPECT_EQ(kExpectedCharCount, FPDFText_CountChars(textpage.get()));
+
+    EXPECT_EQ(static_cast<unsigned int>('X'),
+              FPDFText_GetUnicode(textpage.get(), 0));
+    EXPECT_EQ(0, FPDFText_HasUnicodeMapError(textpage.get(), 0));
+    EXPECT_EQ(static_cast<unsigned int>(' '),
+              FPDFText_GetUnicode(textpage.get(), 1));
+    EXPECT_EQ(0, FPDFText_HasUnicodeMapError(textpage.get(), 1));
+
+    EXPECT_EQ(31u, FPDFText_GetUnicode(textpage.get(), 2));
+    EXPECT_EQ(1, FPDFText_HasUnicodeMapError(textpage.get(), 2));
+
+    EXPECT_EQ(-1, FPDFText_HasUnicodeMapError(textpage.get(), -1));
+    EXPECT_EQ(-1,
+              FPDFText_HasUnicodeMapError(textpage.get(), kExpectedCharCount));
+    EXPECT_EQ(-1, FPDFText_HasUnicodeMapError(nullptr, 0));
+  }
+
+  UnloadPage(page);
+}
+
 TEST_F(FPDFTextEmbedderTest, Bug_921) {
   ASSERT_TRUE(OpenDocument("bug_921.pdf"));
   FPDF_PAGE page = LoadPage(0);
@@ -937,18 +1027,17 @@
   static constexpr int kStartIndex = 238;
 
   ASSERT_EQ(268, FPDFText_CountChars(textpage));
-  for (size_t i = 0; i < FX_ArraySize(kData); ++i)
+  for (size_t i = 0; i < std::size(kData); ++i)
     EXPECT_EQ(kData[i], FPDFText_GetUnicode(textpage, kStartIndex + i));
 
-  unsigned short buffer[FX_ArraySize(kData) + 1];
+  unsigned short buffer[std::size(kData) + 1];
   memset(buffer, 0xbd, sizeof(buffer));
-  int count =
-      FPDFText_GetText(textpage, kStartIndex, FX_ArraySize(kData), buffer);
+  int count = FPDFText_GetText(textpage, kStartIndex, std::size(kData), buffer);
   ASSERT_GT(count, 0);
-  ASSERT_EQ(FX_ArraySize(kData) + 1, static_cast<size_t>(count));
-  for (size_t i = 0; i < FX_ArraySize(kData); ++i)
+  ASSERT_EQ(std::size(kData) + 1, static_cast<size_t>(count));
+  for (size_t i = 0; i < std::size(kData); ++i)
     EXPECT_EQ(kData[i], buffer[i]);
-  EXPECT_EQ(0, buffer[FX_ArraySize(kData)]);
+  EXPECT_EQ(0, buffer[std::size(kData)]);
 
   FPDFText_ClosePage(textpage);
   UnloadPage(page);
@@ -970,8 +1059,8 @@
       0x0056, 0x0065, 0x0072, 0x0069, 0x0074, 0x0061, 0xfffe,
       0x0073, 0x0065, 0x0072, 0x0075, 0x006D, 0x0000};
   {
-    constexpr int count = FX_ArraySize(soft_expected) - 1;
-    unsigned short buffer[FX_ArraySize(soft_expected)];
+    constexpr int count = std::size(soft_expected) - 1;
+    unsigned short buffer[std::size(soft_expected)];
     memset(buffer, 0, sizeof(buffer));
 
     EXPECT_EQ(count + 1, FPDFText_GetText(textpage, 0, count, buffer));
@@ -983,14 +1072,14 @@
   {
     // There isn't the \0 in the actual doc, but there is a \r\n, so need to
     // add 1 to get aligned.
-    constexpr size_t offset = FX_ArraySize(soft_expected) + 1;
-    // Expecting 'User-\r\ngenerated', the - is a unicode character, so cannnot
+    constexpr size_t offset = std::size(soft_expected) + 1;
+    // Expecting 'User-\r\ngenerated', the - is a unicode character, so cannot
     // store in a char[].
     constexpr unsigned short hard_expected[] = {
         0x0055, 0x0073, 0x0065, 0x0072, 0x2010, 0x000d, 0x000a, 0x0067, 0x0065,
         0x006e, 0x0065, 0x0072, 0x0061, 0x0074, 0x0065, 0x0064, 0x0000};
-    constexpr int count = FX_ArraySize(hard_expected) - 1;
-    unsigned short buffer[FX_ArraySize(hard_expected)];
+    constexpr int count = std::size(hard_expected) - 1;
+    unsigned short buffer[std::size(hard_expected)];
 
     EXPECT_EQ(count + 1, FPDFText_GetText(textpage, offset, count, buffer));
     for (int i = 0; i < count; i++)
@@ -1072,7 +1161,7 @@
       0x0061, 0x0073, 0x0020, 0x0063, 0x006f, 0x006d, 0x006d, 0x0069,
       0x0074, 0x0074, 0x0065, 0x0064, 0x002c, 0x0020, 0x0069, 0x0074,
       0x0020, 0x006e, 0x006f, 0x0074, 0x0069, 0x0002, 0x0066, 0x0069};
-  static_assert(page_range_length == FX_ArraySize(expected),
+  static_assert(page_range_length == std::size(expected),
                 "Expected should be the same size as the range being "
                 "extracted from page.");
   EXPECT_LT(page_range_offset + page_range_length,
@@ -1174,7 +1263,7 @@
   // Positive testing.
   constexpr char kHelloText[] = "Hello, world!";
   // Return value includes the terminating NUL that is provided.
-  constexpr unsigned long kHelloUTF16Size = FX_ArraySize(kHelloText) * 2;
+  constexpr unsigned long kHelloUTF16Size = std::size(kHelloText) * 2;
   constexpr wchar_t kHelloWideText[] = L"Hello, world!";
   unsigned long size = FPDFTextObj_GetText(text_object, text_page, nullptr, 0);
   ASSERT_EQ(kHelloUTF16Size, size);
@@ -1292,14 +1381,14 @@
     ASSERT_TRUE(text_page);
 
     constexpr char kText[] = "ABCD";
-    constexpr size_t kTextSize = FX_ArraySize(kText);
+    constexpr size_t kTextSize = std::size(kText);
     // -1 for CountChars not including the \0
     EXPECT_EQ(static_cast<int>(kTextSize) - 1,
               FPDFText_CountChars(text_page.get()));
 
     unsigned short buffer[kTextSize];
     int num_chars =
-        FPDFText_GetText(text_page.get(), 0, FX_ArraySize(buffer) - 1, buffer);
+        FPDFText_GetText(text_page.get(), 0, std::size(buffer) - 1, buffer);
     ASSERT_EQ(static_cast<int>(kTextSize), num_chars);
     EXPECT_TRUE(check_unsigned_shorts(kText, buffer, kTextSize));
   }
@@ -1315,9 +1404,8 @@
   FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
   ASSERT_TRUE(text_page);
 
-  static constexpr int kSubstringsSize[] = {FX_ArraySize("Hello,"),
-                                            FX_ArraySize(" world!\r\n"),
-                                            FX_ArraySize("Goodbye,")};
+  static constexpr int kSubstringsSize[] = {
+      std::size("Hello,"), std::size(" world!\r\n"), std::size("Goodbye,")};
 
   // -1 for CountChars not including the \0, but +1 for the extra control
   // character.
@@ -1329,15 +1417,15 @@
                   FPDFText_GetCharAngle(text_page, kHelloGoodbyeTextSize + 1));
 
   // Test GetCharAngle for every quadrant
-  EXPECT_NEAR(FX_PI / 4.0, FPDFText_GetCharAngle(text_page, 0), 0.001);
-  EXPECT_NEAR(3 * FX_PI / 4.0,
+  EXPECT_NEAR(FXSYS_PI / 4.0, FPDFText_GetCharAngle(text_page, 0), 0.001);
+  EXPECT_NEAR(3 * FXSYS_PI / 4.0,
               FPDFText_GetCharAngle(text_page, kSubstringsSize[0]), 0.001);
   EXPECT_NEAR(
-      5 * FX_PI / 4.0,
+      5 * FXSYS_PI / 4.0,
       FPDFText_GetCharAngle(text_page, kSubstringsSize[0] + kSubstringsSize[1]),
       0.001);
   EXPECT_NEAR(
-      7 * FX_PI / 4.0,
+      7 * FXSYS_PI / 4.0,
       FPDFText_GetCharAngle(text_page, kSubstringsSize[0] + kSubstringsSize[1] +
                                            kSubstringsSize[2]),
       0.001);
@@ -1374,7 +1462,7 @@
 }
 
 TEST_F(FPDFTextEmbedderTest, GetTextRenderMode) {
-  EXPECT_TRUE(OpenDocument("text_render_mode.pdf"));
+  ASSERT_TRUE(OpenDocument("text_render_mode.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -1467,7 +1555,7 @@
 
 TEST_F(FPDFTextEmbedderTest, GetMatrix) {
   constexpr char kExpectedText[] = "A1\r\nA2\r\nA3";
-  constexpr size_t kExpectedTextSize = FX_ArraySize(kExpectedText);
+  constexpr size_t kExpectedTextSize = std::size(kExpectedText);
   constexpr FS_MATRIX kExpectedMatrices[] = {
       {12.0f, 0.0f, 0.0f, 10.0f, 66.0f, 90.0f},
       {12.0f, 0.0f, 0.0f, 10.0f, 66.0f, 90.0f},
@@ -1480,14 +1568,10 @@
       {1.0f, 0.0f, 0.0f, 0.833333, 60.0f, 130.0f},
       {1.0f, 0.0f, 0.0f, 0.833333, 60.0f, 130.0f},
   };
-  constexpr size_t kExpectedCount = FX_ArraySize(kExpectedMatrices);
+  constexpr size_t kExpectedCount = std::size(kExpectedMatrices);
   static_assert(kExpectedCount + 1 == kExpectedTextSize,
                 "Bad expected matrix size");
 
-  // For a size 12 letter 'A'.
-  constexpr double kExpectedCharWidth = 8.436;
-  constexpr double kExpectedCharHeight = 6.77;
-
   ASSERT_TRUE(OpenDocument("font_matrix.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
@@ -1507,26 +1591,6 @@
           check_unsigned_shorts(kExpectedText, buffer, kExpectedTextSize));
     }
 
-    {
-      // Check the character box size.
-      double left;
-      double right;
-      double bottom;
-      double top;
-      ASSERT_TRUE(FPDFText_GetCharBox(text_page.get(), 0, &left, &right,
-                                      &bottom, &top));
-      EXPECT_NEAR(kExpectedCharWidth, right - left, 0.001);
-      EXPECT_NEAR(kExpectedCharHeight, top - bottom, 0.001);
-      ASSERT_TRUE(FPDFText_GetCharBox(text_page.get(), 4, &left, &right,
-                                      &bottom, &top));
-      EXPECT_NEAR(kExpectedCharWidth, right - left, 0.001);
-      EXPECT_NEAR(kExpectedCharHeight, top - bottom, 0.001);
-      ASSERT_TRUE(FPDFText_GetCharBox(text_page.get(), 8, &left, &right,
-                                      &bottom, &top));
-      EXPECT_NEAR(kExpectedCharWidth, right - left, 0.001);
-      EXPECT_NEAR(kExpectedCharHeight, top - bottom, 0.001);
-    }
-
     // Check the character matrix.
     FS_MATRIX matrix;
     for (size_t i = 0; i < kExpectedCount; ++i) {
@@ -1548,3 +1612,154 @@
 
   UnloadPage(page);
 }
+
+TEST_F(FPDFTextEmbedderTest, CharBox) {
+  // For a size 12 letter 'A'.
+  constexpr double kExpectedCharWidth = 8.460;
+  constexpr double kExpectedCharHeight = 6.600;
+  constexpr float kExpectedLooseCharWidth = 8.664f;
+  constexpr float kExpectedLooseCharHeight = 12.0f;
+
+  ASSERT_TRUE(OpenDocument("font_matrix.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFTextPage text_page(FPDFText_LoadPage(page));
+    ASSERT_TRUE(text_page);
+
+    // Check the character box size.
+    double left;
+    double right;
+    double bottom;
+    double top;
+    ASSERT_TRUE(
+        FPDFText_GetCharBox(text_page.get(), 0, &left, &right, &bottom, &top));
+    EXPECT_NEAR(kExpectedCharWidth, right - left, 0.001);
+    EXPECT_NEAR(kExpectedCharHeight, top - bottom, 0.001);
+    ASSERT_TRUE(
+        FPDFText_GetCharBox(text_page.get(), 4, &left, &right, &bottom, &top));
+    EXPECT_NEAR(kExpectedCharWidth, right - left, 0.001);
+    EXPECT_NEAR(kExpectedCharHeight, top - bottom, 0.001);
+    ASSERT_TRUE(
+        FPDFText_GetCharBox(text_page.get(), 8, &left, &right, &bottom, &top));
+    EXPECT_NEAR(kExpectedCharWidth, right - left, 0.001);
+    EXPECT_NEAR(kExpectedCharHeight, top - bottom, 0.001);
+
+    // Check the loose character box size.
+    FS_RECTF rect;
+    ASSERT_TRUE(FPDFText_GetLooseCharBox(text_page.get(), 0, &rect));
+    EXPECT_FLOAT_EQ(kExpectedLooseCharWidth, rect.right - rect.left);
+    EXPECT_FLOAT_EQ(kExpectedLooseCharHeight, rect.top - rect.bottom);
+    ASSERT_TRUE(FPDFText_GetLooseCharBox(text_page.get(), 4, &rect));
+    EXPECT_FLOAT_EQ(kExpectedLooseCharWidth, rect.right - rect.left);
+    EXPECT_FLOAT_EQ(kExpectedLooseCharHeight, rect.top - rect.bottom);
+    ASSERT_TRUE(FPDFText_GetLooseCharBox(text_page.get(), 8, &rect));
+    EXPECT_FLOAT_EQ(kExpectedLooseCharWidth, rect.right - rect.left);
+    EXPECT_NEAR(kExpectedLooseCharHeight, rect.top - rect.bottom, 0.00001);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, SmallType3Glyph) {
+  ASSERT_TRUE(OpenDocument("bug_1591.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFTextPage text_page(FPDFText_LoadPage(page));
+    ASSERT_TRUE(text_page);
+    ASSERT_EQ(5, FPDFText_CountChars(text_page.get()));
+
+    EXPECT_EQ(49u, FPDFText_GetUnicode(text_page.get(), 0));
+    EXPECT_EQ(32u, FPDFText_GetUnicode(text_page.get(), 1));
+    EXPECT_EQ(50u, FPDFText_GetUnicode(text_page.get(), 2));
+    EXPECT_EQ(32u, FPDFText_GetUnicode(text_page.get(), 3));
+    EXPECT_EQ(49u, FPDFText_GetUnicode(text_page.get(), 4));
+
+    // Check the character box size.
+    double left;
+    double right;
+    double bottom;
+    double top;
+    ASSERT_TRUE(
+        FPDFText_GetCharBox(text_page.get(), 0, &left, &right, &bottom, &top));
+    EXPECT_DOUBLE_EQ(63.439998626708984, left);
+    EXPECT_DOUBLE_EQ(65.360000610351562, right);
+    EXPECT_DOUBLE_EQ(50.0, bottom);
+    EXPECT_DOUBLE_EQ(61.520000457763672, top);
+    ASSERT_TRUE(
+        FPDFText_GetCharBox(text_page.get(), 1, &left, &right, &bottom, &top));
+    EXPECT_DOUBLE_EQ(62.007999420166016, left);
+    EXPECT_DOUBLE_EQ(62.007999420166016, right);
+    EXPECT_DOUBLE_EQ(50.0, bottom);
+    EXPECT_DOUBLE_EQ(50.0, top);
+    ASSERT_TRUE(
+        FPDFText_GetCharBox(text_page.get(), 2, &left, &right, &bottom, &top));
+    EXPECT_DOUBLE_EQ(86.0, left);
+    EXPECT_DOUBLE_EQ(88.400001525878906, right);
+    EXPECT_DOUBLE_EQ(50.0, bottom);
+    EXPECT_DOUBLE_EQ(50.240001678466797, top);
+    ASSERT_TRUE(
+        FPDFText_GetCharBox(text_page.get(), 3, &left, &right, &bottom, &top));
+    EXPECT_DOUBLE_EQ(86.010002136230469, left);
+    EXPECT_DOUBLE_EQ(86.010002136230469, right);
+    EXPECT_DOUBLE_EQ(50.0, bottom);
+    EXPECT_DOUBLE_EQ(50.0, top);
+    ASSERT_TRUE(
+        FPDFText_GetCharBox(text_page.get(), 4, &left, &right, &bottom, &top));
+    EXPECT_DOUBLE_EQ(99.44000244140625, left);
+    EXPECT_DOUBLE_EQ(101.36000061035156, right);
+    EXPECT_DOUBLE_EQ(50.0, bottom);
+    EXPECT_DOUBLE_EQ(61.520000457763672, top);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, BigtableTextExtraction) {
+  constexpr char kExpectedText[] =
+      "{fay,jeff,sanjay,wilsonh,kerr,m3b,tushar,\x02k es,gruber}@google.com";
+  constexpr int kExpectedTextCount = std::size(kExpectedText) - 1;
+
+  ASSERT_TRUE(OpenDocument("bigtable_mini.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFTextPage text_page(FPDFText_LoadPage(page));
+    ASSERT_TRUE(text_page);
+    int char_count = FPDFText_CountChars(text_page.get());
+    ASSERT_GE(char_count, 0);
+    ASSERT_EQ(kExpectedTextCount, char_count);
+
+    for (int i = 0; i < kExpectedTextCount; ++i) {
+      EXPECT_EQ(static_cast<uint32_t>(kExpectedText[i]),
+                FPDFText_GetUnicode(text_page.get(), i));
+    }
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, Bug1769) {
+  ASSERT_TRUE(OpenDocument("bug_1769.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFTextPage textpage(FPDFText_LoadPage(page));
+    ASSERT_TRUE(textpage);
+
+    unsigned short buffer[128] = {};
+    // TODO(crbug.com/pdfium/1769): Improve text extraction.
+    // The first instance of "world" is visible to the human eye and should be
+    // extracted as is. The second instance is not, so how it should be
+    // extracted is debatable.
+    ASSERT_EQ(10, FPDFText_GetText(textpage.get(), 0, 128, buffer));
+    EXPECT_TRUE(check_unsigned_shorts("wo d wo d", buffer, 10));
+  }
+
+  UnloadPage(page);
+}
diff --git a/fpdfsdk/fpdf_thumbnail.cpp b/fpdfsdk/fpdf_thumbnail.cpp
index 5c22c8c..0f1331d 100644
--- a/fpdfsdk/fpdf_thumbnail.cpp
+++ b/fpdfsdk/fpdf_thumbnail.cpp
@@ -1,10 +1,10 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "public/fpdf_thumbnail.h"
 
-#include <vector>
+#include <utility>
 
 #include "core/fpdfapi/page/cpdf_dib.h"
 #include "core/fpdfapi/page/cpdf_page.h"
@@ -17,12 +17,12 @@
 
 namespace {
 
-const CPDF_Stream* CPDFStreamForThumbnailFromPage(FPDF_PAGE page) {
-  const CPDF_Page* p_page = CPDFPageFromFPDFPage(page);
-  if (!p_page)
+RetainPtr<const CPDF_Stream> CPDFStreamForThumbnailFromPage(FPDF_PAGE page) {
+  const CPDF_Page* pdf_page = CPDFPageFromFPDFPage(page);
+  if (!pdf_page)
     return nullptr;
 
-  const CPDF_Dictionary* page_dict = p_page->GetDict();
+  RetainPtr<const CPDF_Dictionary> page_dict = pdf_page->GetDict();
   if (!page_dict->KeyExist("Type"))
     return nullptr;
 
@@ -35,41 +35,48 @@
 FPDFPage_GetDecodedThumbnailData(FPDF_PAGE page,
                                  void* buffer,
                                  unsigned long buflen) {
-  const CPDF_Stream* thumb_stream = CPDFStreamForThumbnailFromPage(page);
+  RetainPtr<const CPDF_Stream> thumb_stream =
+      CPDFStreamForThumbnailFromPage(page);
   if (!thumb_stream)
     return 0u;
 
-  return DecodeStreamMaybeCopyAndReturnLength(thumb_stream, buffer, buflen);
+  return DecodeStreamMaybeCopyAndReturnLength(
+      std::move(thumb_stream),
+      {static_cast<uint8_t*>(buffer), static_cast<size_t>(buflen)});
 }
 
 FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDFPage_GetRawThumbnailData(FPDF_PAGE page,
                              void* buffer,
                              unsigned long buflen) {
-  const CPDF_Stream* thumb_stream = CPDFStreamForThumbnailFromPage(page);
+  RetainPtr<const CPDF_Stream> thumb_stream =
+      CPDFStreamForThumbnailFromPage(page);
   if (!thumb_stream)
     return 0u;
 
-  return GetRawStreamMaybeCopyAndReturnLength(thumb_stream, buffer, buflen);
+  return GetRawStreamMaybeCopyAndReturnLength(
+      std::move(thumb_stream),
+      {static_cast<uint8_t*>(buffer), static_cast<size_t>(buflen)});
 }
 
 FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
 FPDFPage_GetThumbnailAsBitmap(FPDF_PAGE page) {
-  const CPDF_Stream* thumb_stream = CPDFStreamForThumbnailFromPage(page);
+  RetainPtr<const CPDF_Stream> thumb_stream =
+      CPDFStreamForThumbnailFromPage(page);
   if (!thumb_stream)
     return nullptr;
 
-  const CPDF_Page* p_page = CPDFPageFromFPDFPage(page);
-
-  auto p_source = pdfium::MakeRetain<CPDF_DIB>();
-  const CPDF_DIB::LoadState start_status = p_source->StartLoadDIBBase(
-      p_page->GetDocument(), thumb_stream, false, nullptr,
-      p_page->m_pPageResources.Get(), false, 0, false);
+  const CPDF_Page* pdf_page = CPDFPageFromFPDFPage(page);
+  auto dib_source = pdfium::MakeRetain<CPDF_DIB>(pdf_page->GetDocument(),
+                                                 std::move(thumb_stream));
+  const CPDF_DIB::LoadState start_status = dib_source->StartLoadDIBBase(
+      false, nullptr, pdf_page->GetPageResources().Get(), false,
+      CPDF_ColorSpace::Family::kUnknown, false, {0, 0});
   if (start_status == CPDF_DIB::LoadState::kFail)
     return nullptr;
 
   auto thumb_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!thumb_bitmap->Copy(p_source))
+  if (!thumb_bitmap->Copy(dib_source))
     return nullptr;
 
   return FPDFBitmapFromCFXDIBitmap(thumb_bitmap.Leak());
diff --git a/fpdfsdk/fpdf_thumbnail_embeddertest.cpp b/fpdfsdk/fpdf_thumbnail_embeddertest.cpp
index 101e1e4..023e8ba 100644
--- a/fpdfsdk/fpdf_thumbnail_embeddertest.cpp
+++ b/fpdfsdk/fpdf_thumbnail_embeddertest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,6 +9,14 @@
 #include "testing/embedder_test.h"
 #include "testing/utils/hash.h"
 
+namespace {
+
+const char kSimpleThumbnailChecksum[] = "f6a8e8db01cccd52abb91ea433a17373";
+const char kThumbnailWithNoFiltersChecksum[] =
+    "b5696e586382b3373741f8a1d651cab0";
+
+}  // namespace
+
 class FPDFThumbnailEmbedderTest : public EmbedderTest {};
 
 TEST_F(FPDFThumbnailEmbedderTest, GetDecodedThumbnailDataFromPageWithFilters) {
@@ -28,8 +36,7 @@
 
     EXPECT_EQ(kExpectedSize, FPDFPage_GetDecodedThumbnailData(
                                  page, thumb_buf.data(), length_bytes));
-    EXPECT_EQ(kHashedDecodedData,
-              GenerateMD5Base16(thumb_buf.data(), kExpectedSize));
+    EXPECT_EQ(kHashedDecodedData, GenerateMD5Base16(thumb_buf));
 
     UnloadPage(page);
   }
@@ -48,8 +55,7 @@
 
     EXPECT_EQ(kExpectedSize, FPDFPage_GetDecodedThumbnailData(
                                  page, thumb_buf.data(), length_bytes));
-    EXPECT_EQ(kHashedDecodedData,
-              GenerateMD5Base16(thumb_buf.data(), kExpectedSize));
+    EXPECT_EQ(kHashedDecodedData, GenerateMD5Base16(thumb_buf));
 
     UnloadPage(page);
   }
@@ -59,7 +65,6 @@
        GetDecodedThumbnailDataFromPageWithNoFilters) {
   ASSERT_TRUE(OpenDocument("thumbnail_with_no_filters.pdf"));
 
-  const char kHashedDecodedData[] = "b5696e586382b3373741f8a1d651cab0";
   const unsigned long kExpectedSize = 301u;
 
   FPDF_PAGE page = LoadPage(0);
@@ -72,8 +77,7 @@
 
   EXPECT_EQ(kExpectedSize, FPDFPage_GetDecodedThumbnailData(
                                page, thumb_buf.data(), length_bytes));
-  EXPECT_EQ(kHashedDecodedData,
-            GenerateMD5Base16(thumb_buf.data(), kExpectedSize));
+  EXPECT_EQ(kThumbnailWithNoFiltersChecksum, GenerateMD5Base16(thumb_buf));
 
   UnloadPage(page);
 }
@@ -98,7 +102,6 @@
   ASSERT_TRUE(OpenDocument("simple_thumbnail.pdf"));
 
   {
-    const char kHashedRawData[] = "f6a8e8db01cccd52abb91ea433a17373";
     const unsigned long kExpectedSize = 1851u;
 
     FPDF_PAGE page = LoadPage(0);
@@ -110,8 +113,7 @@
 
     EXPECT_EQ(kExpectedSize, FPDFPage_GetRawThumbnailData(
                                  page, thumb_buf.data(), length_bytes));
-    EXPECT_EQ(kHashedRawData,
-              GenerateMD5Base16(thumb_buf.data(), kExpectedSize));
+    EXPECT_EQ(kSimpleThumbnailChecksum, GenerateMD5Base16(thumb_buf));
 
     UnloadPage(page);
   }
@@ -129,8 +131,7 @@
 
     EXPECT_EQ(kExpectedSize, FPDFPage_GetRawThumbnailData(
                                  page, thumb_buf.data(), length_bytes));
-    EXPECT_EQ(kHashedRawData,
-              GenerateMD5Base16(thumb_buf.data(), kExpectedSize));
+    EXPECT_EQ(kHashedRawData, GenerateMD5Base16(thumb_buf));
 
     UnloadPage(page);
   }
@@ -139,7 +140,6 @@
 TEST_F(FPDFThumbnailEmbedderTest, GetRawThumbnailDataFromPageWithNoFilters) {
   ASSERT_TRUE(OpenDocument("thumbnail_with_no_filters.pdf"));
 
-  const char kHashedRawData[] = "b5696e586382b3373741f8a1d651cab0";
   const unsigned long kExpectedSize = 301u;
 
   FPDF_PAGE page = LoadPage(0);
@@ -151,7 +151,7 @@
 
   EXPECT_EQ(kExpectedSize,
             FPDFPage_GetRawThumbnailData(page, thumb_buf.data(), length_bytes));
-  EXPECT_EQ(kHashedRawData, GenerateMD5Base16(thumb_buf.data(), kExpectedSize));
+  EXPECT_EQ(kThumbnailWithNoFiltersChecksum, GenerateMD5Base16(thumb_buf));
 
   UnloadPage(page);
 }
@@ -251,7 +251,6 @@
 TEST_F(FPDFThumbnailEmbedderTest, GetThumbnailDoesNotAlterPage) {
   ASSERT_TRUE(OpenDocument("simple_thumbnail.pdf"));
 
-  const char kHashedRawData[] = "f6a8e8db01cccd52abb91ea433a17373";
   const unsigned long kExpectedRawSize = 1851u;
 
   FPDF_PAGE page = LoadPage(0);
@@ -264,8 +263,7 @@
 
   EXPECT_EQ(kExpectedRawSize,
             FPDFPage_GetRawThumbnailData(page, raw_thumb_buf.data(), raw_size));
-  EXPECT_EQ(kHashedRawData,
-            GenerateMD5Base16(raw_thumb_buf.data(), kExpectedRawSize));
+  EXPECT_EQ(kSimpleThumbnailChecksum, GenerateMD5Base16(raw_thumb_buf));
 
   // Get the thumbnail
   ScopedFPDFBitmap thumb_bitmap(FPDFPage_GetThumbnailAsBitmap(page));
@@ -283,12 +281,11 @@
   EXPECT_EQ(kExpectedRawSize,
             FPDFPage_GetRawThumbnailData(page, new_raw_thumb_buf.data(),
                                          new_raw_size));
-  EXPECT_EQ(kHashedRawData,
-            GenerateMD5Base16(new_raw_thumb_buf.data(), kExpectedRawSize));
+  EXPECT_EQ(kSimpleThumbnailChecksum, GenerateMD5Base16(new_raw_thumb_buf));
 
   UnloadPage(page);
 }
 
 TEST_F(FPDFThumbnailEmbedderTest, GetThumbnailAsBitmapFromPageNullPage) {
-  EXPECT_EQ(nullptr, FPDFPage_GetThumbnailAsBitmap(nullptr));
+  EXPECT_FALSE(FPDFPage_GetThumbnailAsBitmap(nullptr));
 }
diff --git a/fpdfsdk/fpdf_transformpage.cpp b/fpdfsdk/fpdf_transformpage.cpp
index cc26888..c9ea333 100644
--- a/fpdfsdk/fpdf_transformpage.cpp
+++ b/fpdfsdk/fpdf_transformpage.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,6 @@
 
 #include <memory>
 #include <sstream>
-#include <vector>
 
 #include "constants/page_object.h"
 #include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
@@ -22,10 +21,13 @@
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fxge/cfx_pathdata.h"
-#include "core/fxge/render_defines.h"
+#include "core/fxcrt/fx_string_wrappers.h"
+#include "core/fxcrt/stl_util.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "core/fxge/cfx_path.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/containers/span.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
 
@@ -35,11 +37,11 @@
   if (!page)
     return;
 
-  page->GetDict()->SetRectFor(key, rect);
+  page->GetMutableDict()->SetRectFor(key, rect);
   page->UpdateDimensions();
 }
 
-bool GetBoundingBox(CPDF_Page* page,
+bool GetBoundingBox(const CPDF_Page* page,
                     const ByteString& key,
                     float* left,
                     float* bottom,
@@ -48,52 +50,51 @@
   if (!page || !left || !bottom || !right || !top)
     return false;
 
-  CPDF_Array* pArray = page->GetDict()->GetArrayFor(key);
+  RetainPtr<const CPDF_Array> pArray = page->GetDict()->GetArrayFor(key);
   if (!pArray)
     return false;
 
-  *left = pArray->GetNumberAt(0);
-  *bottom = pArray->GetNumberAt(1);
-  *right = pArray->GetNumberAt(2);
-  *top = pArray->GetNumberAt(3);
+  *left = pArray->GetFloatAt(0);
+  *bottom = pArray->GetFloatAt(1);
+  *right = pArray->GetFloatAt(2);
+  *top = pArray->GetFloatAt(3);
   return true;
 }
 
-CPDF_Object* GetPageContent(CPDF_Dictionary* pPageDict) {
-  return pPageDict->GetDirectObjectFor(pdfium::page_object::kContents);
+RetainPtr<CPDF_Object> GetPageContent(CPDF_Dictionary* pPageDict) {
+  return pPageDict->GetMutableDirectObjectFor(pdfium::page_object::kContents);
 }
 
-void OutputPath(std::ostringstream& buf, CPDF_Path path) {
-  const CFX_PathData* pPathData = path.GetObject();
-  if (!pPathData)
+void OutputPath(fxcrt::ostringstream& buf, CPDF_Path path) {
+  const CFX_Path* pPath = path.GetObject();
+  if (!pPath)
     return;
 
-  const std::vector<FX_PATHPOINT>& pPoints = pPathData->GetPoints();
+  pdfium::span<const CFX_Path::Point> points = pPath->GetPoints();
   if (path.IsRect()) {
-    CFX_PointF diff = pPoints[2].m_Point - pPoints[0].m_Point;
-    buf << pPoints[0].m_Point.x << " " << pPoints[0].m_Point.y << " " << diff.x
+    CFX_PointF diff = points[2].m_Point - points[0].m_Point;
+    buf << points[0].m_Point.x << " " << points[0].m_Point.y << " " << diff.x
         << " " << diff.y << " re\n";
     return;
   }
 
-  ByteString temp;
-  for (size_t i = 0; i < pPoints.size(); i++) {
-    buf << pPoints[i].m_Point.x << " " << pPoints[i].m_Point.y;
-    FXPT_TYPE point_type = pPoints[i].m_Type;
-    if (point_type == FXPT_TYPE::MoveTo) {
+  for (size_t i = 0; i < points.size(); ++i) {
+    buf << points[i].m_Point.x << " " << points[i].m_Point.y;
+    CFX_Path::Point::Type point_type = points[i].m_Type;
+    if (point_type == CFX_Path::Point::Type::kMove) {
       buf << " m\n";
-    } else if (point_type == FXPT_TYPE::BezierTo) {
-      buf << " " << pPoints[i + 1].m_Point.x << " " << pPoints[i + 1].m_Point.y
-          << " " << pPoints[i + 2].m_Point.x << " " << pPoints[i + 2].m_Point.y;
+    } else if (point_type == CFX_Path::Point::Type::kBezier) {
+      buf << " " << points[i + 1].m_Point.x << " " << points[i + 1].m_Point.y
+          << " " << points[i + 2].m_Point.x << " " << points[i + 2].m_Point.y;
       buf << " c";
-      if (pPoints[i + 2].m_CloseFigure)
+      if (points[i + 2].m_CloseFigure)
         buf << " h";
       buf << "\n";
 
       i += 2;
-    } else if (point_type == FXPT_TYPE::LineTo) {
+    } else if (point_type == CFX_Path::Point::Type::kLine) {
       buf << " l";
-      if (pPoints[i].m_CloseFigure)
+      if (points[i].m_CloseFigure)
         buf << " h";
       buf << "\n";
     }
@@ -207,8 +208,8 @@
   if (!pPage)
     return false;
 
-  CPDF_Dictionary* pPageDict = pPage->GetDict();
-  CPDF_Object* pContentObj = GetPageContent(pPageDict);
+  RetainPtr<CPDF_Dictionary> pPageDict = pPage->GetMutableDict();
+  RetainPtr<CPDF_Object> pContentObj = GetPageContent(pPageDict.Get());
   if (!pContentObj)
     return false;
 
@@ -216,64 +217,58 @@
   if (!pDoc)
     return false;
 
-  std::ostringstream text_buf;
+  fxcrt::ostringstream text_buf;
   text_buf << "q ";
 
   if (clipRect) {
     CFX_FloatRect rect = CFXFloatRectFromFSRectF(*clipRect);
     rect.Normalize();
-
-    WriteFloat(text_buf, rect.left) << " ";
-    WriteFloat(text_buf, rect.bottom) << " ";
-    WriteFloat(text_buf, rect.Width()) << " ";
-    WriteFloat(text_buf, rect.Height()) << " re W* n ";
+    WriteRect(text_buf, rect) << " re W* n ";
   }
-  if (matrix) {
-    CFX_Matrix m = CFXMatrixFromFSMatrix(*matrix);
-    text_buf << m << " cm ";
-  }
+  if (matrix)
+    WriteMatrix(text_buf, CFXMatrixFromFSMatrix(*matrix)) << " cm ";
 
-  CPDF_Stream* pStream =
-      pDoc->NewIndirect<CPDF_Stream>(nullptr, 0, pDoc->New<CPDF_Dictionary>());
+  auto pStream = pDoc->NewIndirect<CPDF_Stream>(pDoc->New<CPDF_Dictionary>());
   pStream->SetDataFromStringstream(&text_buf);
 
-  CPDF_Stream* pEndStream =
-      pDoc->NewIndirect<CPDF_Stream>(nullptr, 0, pDoc->New<CPDF_Dictionary>());
+  auto pEndStream =
+      pDoc->NewIndirect<CPDF_Stream>(pDoc->New<CPDF_Dictionary>());
   pEndStream->SetData(ByteStringView(" Q").raw_span());
 
-  if (CPDF_Array* pContentArray = ToArray(pContentObj)) {
+  RetainPtr<CPDF_Array> pContentArray = ToArray(pContentObj);
+  if (pContentArray) {
     pContentArray->InsertNewAt<CPDF_Reference>(0, pDoc, pStream->GetObjNum());
-    pContentArray->AddNew<CPDF_Reference>(pDoc, pEndStream->GetObjNum());
+    pContentArray->AppendNew<CPDF_Reference>(pDoc, pEndStream->GetObjNum());
   } else if (pContentObj->IsStream() && !pContentObj->IsInline()) {
     pContentArray = pDoc->NewIndirect<CPDF_Array>();
-    pContentArray->AddNew<CPDF_Reference>(pDoc, pStream->GetObjNum());
-    pContentArray->AddNew<CPDF_Reference>(pDoc, pContentObj->GetObjNum());
-    pContentArray->AddNew<CPDF_Reference>(pDoc, pEndStream->GetObjNum());
+    pContentArray->AppendNew<CPDF_Reference>(pDoc, pStream->GetObjNum());
+    pContentArray->AppendNew<CPDF_Reference>(pDoc, pContentObj->GetObjNum());
+    pContentArray->AppendNew<CPDF_Reference>(pDoc, pEndStream->GetObjNum());
     pPageDict->SetNewFor<CPDF_Reference>(pdfium::page_object::kContents, pDoc,
                                          pContentArray->GetObjNum());
   }
 
   // Need to transform the patterns as well.
-  CPDF_Dictionary* pRes =
+  RetainPtr<const CPDF_Dictionary> pRes =
       pPageDict->GetDictFor(pdfium::page_object::kResources);
   if (!pRes)
     return true;
 
-  CPDF_Dictionary* pPatternDict = pRes->GetDictFor("Pattern");
+  RetainPtr<const CPDF_Dictionary> pPatternDict = pRes->GetDictFor("Pattern");
   if (!pPatternDict)
     return true;
 
   CPDF_DictionaryLocker locker(pPatternDict);
   for (const auto& it : locker) {
-    CPDF_Object* pObj = it.second.Get();
+    RetainPtr<CPDF_Object> pObj = it.second;
     if (pObj->IsReference())
-      pObj = pObj->GetDirect();
+      pObj = pObj->GetMutableDirect();
 
-    CPDF_Dictionary* pDict = nullptr;
+    RetainPtr<CPDF_Dictionary> pDict;
     if (pObj->IsDictionary())
-      pDict = pObj->AsDictionary();
-    else if (CPDF_Stream* pObjStream = pObj->AsStream())
-      pDict = pObjStream->GetDict();
+      pDict.Reset(pObj->AsMutableDictionary());
+    else if (CPDF_Stream* pObjStream = pObj->AsMutableStream())
+      pDict = pObjStream->GetMutableDict();
     else
       continue;
 
@@ -321,7 +316,7 @@
   if (!pClipPath || !pClipPath->HasRef())
     return -1;
 
-  return pClipPath->GetPathCount();
+  return pdfium::base::checked_cast<int>(pClipPath->GetPathCount());
 }
 
 FPDF_EXPORT int FPDF_CALLCONV
@@ -335,8 +330,7 @@
     return -1;
   }
 
-  return pdfium::CollectionSize<int>(
-      pClipPath->GetPath(path_index).GetPoints());
+  return fxcrt::CollectionSize<int>(pClipPath->GetPath(path_index).GetPoints());
 }
 
 FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
@@ -352,9 +346,9 @@
     return nullptr;
   }
 
-  const std::vector<FX_PATHPOINT>& points =
+  pdfium::span<const CFX_Path::Point> points =
       pClipPath->GetPath(path_index).GetPoints();
-  if (!pdfium::IndexInBounds(points, segment_index))
+  if (!fxcrt::IndexInBounds(points, segment_index))
     return nullptr;
 
   return FPDFPathSegmentFromFXPathPoint(&points[segment_index]);
@@ -367,8 +361,8 @@
   CPDF_Path Path;
   Path.AppendRect(left, bottom, right, top);
 
-  auto pNewClipPath = pdfium::MakeUnique<CPDF_ClipPath>();
-  pNewClipPath->AppendPath(Path, FXFILL_ALTERNATE, false);
+  auto pNewClipPath = std::make_unique<CPDF_ClipPath>();
+  pNewClipPath->AppendPath(Path, CFX_FillRenderOptions::FillType::kEvenOdd);
 
   // Caller takes ownership.
   return FPDFClipPathFromCPDFClipPath(pNewClipPath.release());
@@ -385,12 +379,12 @@
   if (!pPage)
     return;
 
-  CPDF_Dictionary* pPageDict = pPage->GetDict();
-  CPDF_Object* pContentObj = GetPageContent(pPageDict);
+  RetainPtr<CPDF_Dictionary> pPageDict = pPage->GetMutableDict();
+  RetainPtr<CPDF_Object> pContentObj = GetPageContent(pPageDict.Get());
   if (!pContentObj)
     return;
 
-  std::ostringstream strClip;
+  fxcrt::ostringstream strClip;
   CPDF_ClipPath* pClipPath = CPDFClipPathFromFPDFClipPath(clipPath);
   for (size_t i = 0; i < pClipPath->GetPathCount(); ++i) {
     CPDF_Path path = pClipPath->GetPath(i);
@@ -399,26 +393,28 @@
       strClip << "0 0 m W n ";
     } else {
       OutputPath(strClip, path);
-      if (pClipPath->GetClipType(i) == FXFILL_WINDING)
+      if (pClipPath->GetClipType(i) ==
+          CFX_FillRenderOptions::FillType::kWinding) {
         strClip << "W n\n";
-      else
+      } else {
         strClip << "W* n\n";
+      }
     }
   }
   CPDF_Document* pDoc = pPage->GetDocument();
   if (!pDoc)
     return;
 
-  CPDF_Stream* pStream =
-      pDoc->NewIndirect<CPDF_Stream>(nullptr, 0, pDoc->New<CPDF_Dictionary>());
+  auto pStream = pDoc->NewIndirect<CPDF_Stream>(pDoc->New<CPDF_Dictionary>());
   pStream->SetDataFromStringstream(&strClip);
 
-  if (CPDF_Array* pArray = ToArray(pContentObj)) {
+  RetainPtr<CPDF_Array> pArray = ToArray(pContentObj);
+  if (pArray) {
     pArray->InsertNewAt<CPDF_Reference>(0, pDoc, pStream->GetObjNum());
   } else if (pContentObj->IsStream() && !pContentObj->IsInline()) {
-    CPDF_Array* pContentArray = pDoc->NewIndirect<CPDF_Array>();
-    pContentArray->AddNew<CPDF_Reference>(pDoc, pStream->GetObjNum());
-    pContentArray->AddNew<CPDF_Reference>(pDoc, pContentObj->GetObjNum());
+    auto pContentArray = pDoc->NewIndirect<CPDF_Array>();
+    pContentArray->AppendNew<CPDF_Reference>(pDoc, pStream->GetObjNum());
+    pContentArray->AppendNew<CPDF_Reference>(pDoc, pContentObj->GetObjNum());
     pPageDict->SetNewFor<CPDF_Reference>(pdfium::page_object::kContents, pDoc,
                                          pContentArray->GetObjNum());
   }
diff --git a/fpdfsdk/fpdf_transformpage_embeddertest.cpp b/fpdfsdk/fpdf_transformpage_embeddertest.cpp
index 6ebb8d2..8072262 100644
--- a/fpdfsdk/fpdf_transformpage_embeddertest.cpp
+++ b/fpdfsdk/fpdf_transformpage_embeddertest.cpp
@@ -1,16 +1,30 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "public/fpdf_transformpage.h"
 
 #include "build/build_config.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
 #include "testing/embedder_test.h"
+#include "testing/embedder_test_constants.h"
 
-#if defined(OS_LINUX) || defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
 #include "third_party/base/test/scoped_locale.h"
 #endif
 
+using pdfium::RectanglesChecksum;
+
+namespace {
+
+const char* ShrunkChecksum() {
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    return "78c52d6029283090036e6db6683401e2";
+  return "f4136cc9209207ab60eb8381a3df2e69";
+}
+
+}  // namespace
+
 class FPDFTransformEmbedderTest : public EmbedderTest {};
 
 TEST_F(FPDFTransformEmbedderTest, GetBoundingBoxes) {
@@ -194,16 +208,12 @@
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_SetCropBox DISABLED_SetCropBox
-#else
-#define MAYBE_SetCropBox SetCropBox
-#endif
-TEST_F(FPDFTransformEmbedderTest, MAYBE_SetCropBox) {
-  const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe";
-  const char kCroppedMD5[] = "9937883715d5144c079fb8f7e3d4f395";
-
+TEST_F(FPDFTransformEmbedderTest, SetCropBox) {
+  const char* cropped_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "4b9d2d2246be61c583f454245fe3172f";
+    return "9937883715d5144c079fb8f7e3d4f395";
+  }();
   {
     ASSERT_TRUE(OpenDocument("rectangles.pdf"));
     FPDF_PAGE page = LoadPage(0);
@@ -219,7 +229,8 @@
       EXPECT_EQ(200, page_width);
       EXPECT_EQ(300, page_height);
       ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-      CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+      CompareBitmap(bitmap.get(), page_width, page_height,
+                    RectanglesChecksum());
     }
 
     FPDFPage_SetCropBox(page, 10, 20, 100, 150);
@@ -240,7 +251,7 @@
       EXPECT_EQ(90, page_width);
       EXPECT_EQ(130, page_height);
       ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-      CompareBitmap(bitmap.get(), page_width, page_height, kCroppedMD5);
+      CompareBitmap(bitmap.get(), page_width, page_height, cropped_checksum);
     }
 
     UnloadPage(page);
@@ -266,22 +277,19 @@
     EXPECT_EQ(90, page_width);
     EXPECT_EQ(130, page_height);
     ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
-    CompareBitmap(bitmap.get(), page_width, page_height, kCroppedMD5);
+    CompareBitmap(bitmap.get(), page_width, page_height, cropped_checksum);
 
     CloseSavedPage(saved_page);
     CloseSavedDocument();
   }
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_SetMediaBox DISABLED_SetMediaBox
-#else
-#define MAYBE_SetMediaBox SetMediaBox
-#endif
-TEST_F(FPDFTransformEmbedderTest, MAYBE_SetMediaBox) {
-  const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe";
-  const char kShrunkMD5[] = "eab5958f62f7ce65d7c32de98389fee1";
+TEST_F(FPDFTransformEmbedderTest, SetMediaBox) {
+  const char* shrunk_checksum_set_media_box = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "9f28f0610a7f789c24cfd5f9bd5dc3de";
+    return "eab5958f62f7ce65d7c32de98389fee1";
+  }();
 
   {
     ASSERT_TRUE(OpenDocument("rectangles.pdf"));
@@ -298,7 +306,8 @@
       EXPECT_EQ(200, page_width);
       EXPECT_EQ(300, page_height);
       ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-      CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+      CompareBitmap(bitmap.get(), page_width, page_height,
+                    RectanglesChecksum());
     }
 
     FPDFPage_SetMediaBox(page, 20, 30, 100, 150);
@@ -319,7 +328,8 @@
       EXPECT_EQ(80, page_width);
       EXPECT_EQ(120, page_height);
       ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-      CompareBitmap(bitmap.get(), page_width, page_height, kShrunkMD5);
+      CompareBitmap(bitmap.get(), page_width, page_height,
+                    shrunk_checksum_set_media_box);
     }
 
     UnloadPage(page);
@@ -346,7 +356,8 @@
     EXPECT_EQ(80, page_width);
     EXPECT_EQ(120, page_height);
     ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
-    CompareBitmap(bitmap.get(), page_width, page_height, kShrunkMD5);
+    CompareBitmap(bitmap.get(), page_width, page_height,
+                  shrunk_checksum_set_media_box);
 
     CloseSavedPage(saved_page);
     CloseSavedDocument();
@@ -412,16 +423,7 @@
   UnloadPage(page);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_TransFormWithClipAndSave DISABLED_TransFormWithClipAndSave
-#else
-#define MAYBE_TransFormWithClipAndSave TransFormWithClipAndSave
-#endif
-TEST_F(FPDFTransformEmbedderTest, MAYBE_TransFormWithClipAndSave) {
-  const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe";
-  const char kShrunkMD5[] = "f4136cc9209207ab60eb8381a3df2e69";
-
+TEST_F(FPDFTransformEmbedderTest, TransFormWithClipAndSave) {
   {
     ASSERT_TRUE(OpenDocument("rectangles.pdf"));
     FPDF_PAGE page = LoadPage(0);
@@ -434,14 +436,16 @@
       EXPECT_EQ(200, page_width);
       EXPECT_EQ(300, page_height);
       ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-      CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+      CompareBitmap(bitmap.get(), page_width, page_height,
+                    RectanglesChecksum());
     }
 
     {
       // Render the page after transforming.
       // Note that the change should affect the rendering, but does not.
       // It should behaves just like the case below, rather than the case above.
-      // TODO(bug_1328): The checksum below should be |kShrunkMD5|.
+      // TODO(crbug.com/pdfium/1328): The checksum after invoking
+      // `FPDFPage_TransFormWithClip()` below should match `ShrunkChecksum()`.
       const FS_MATRIX half_matrix{0.5, 0, 0, 0.5, 0, 0};
       EXPECT_TRUE(FPDFPage_TransFormWithClip(page, &half_matrix, nullptr));
       const int page_width = static_cast<int>(FPDF_GetPageWidth(page));
@@ -449,7 +453,8 @@
       EXPECT_EQ(200, page_width);
       EXPECT_EQ(300, page_height);
       ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-      CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+      CompareBitmap(bitmap.get(), page_width, page_height,
+                    RectanglesChecksum());
     }
 
     UnloadPage(page);
@@ -468,26 +473,15 @@
     EXPECT_EQ(200, page_width);
     EXPECT_EQ(300, page_height);
     ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
-    CompareBitmap(bitmap.get(), page_width, page_height, kShrunkMD5);
+    CompareBitmap(bitmap.get(), page_width, page_height, ShrunkChecksum());
 
     CloseSavedPage(saved_page);
     CloseSavedDocument();
   }
 }
 
-#if defined(OS_LINUX) || defined(OS_FUCHSIA)
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_TransFormWithClipAndSaveWithLocale \
-  DISABLED_TransFormWithClipAndSaveWithLocale
-#else
-#define MAYBE_TransFormWithClipAndSaveWithLocale \
-  TransFormWithClipAndSaveWithLocale
-#endif
-TEST_F(FPDFTransformEmbedderTest, MAYBE_TransFormWithClipAndSaveWithLocale) {
-  const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe";
-  const char kShrunkMD5[] = "f4136cc9209207ab60eb8381a3df2e69";
-
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
+TEST_F(FPDFTransformEmbedderTest, TransFormWithClipAndSaveWithLocale) {
   pdfium::base::ScopedLocale scoped_locale("da_DK.UTF-8");
 
   {
@@ -502,14 +496,16 @@
       EXPECT_EQ(200, page_width);
       EXPECT_EQ(300, page_height);
       ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-      CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+      CompareBitmap(bitmap.get(), page_width, page_height,
+                    RectanglesChecksum());
     }
 
     {
       // Render the page after transforming.
       // Note that the change should affect the rendering, but does not.
       // It should behaves just like the case below, rather than the case above.
-      // TODO(bug_1328): The checksum below should be |kShrunkMD5|.
+      // TODO(crbug.com/pdfium/1328): The checksum after invoking
+      // `FPDFPage_TransFormWithClip()` below should match `ShrunkChecksum()`.
       const FS_MATRIX half_matrix{0.5, 0, 0, 0.5, 0, 0};
       EXPECT_TRUE(FPDFPage_TransFormWithClip(page, &half_matrix, nullptr));
       const int page_width = static_cast<int>(FPDF_GetPageWidth(page));
@@ -517,7 +513,8 @@
       EXPECT_EQ(200, page_width);
       EXPECT_EQ(300, page_height);
       ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-      CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+      CompareBitmap(bitmap.get(), page_width, page_height,
+                    RectanglesChecksum());
     }
 
     UnloadPage(page);
@@ -536,10 +533,11 @@
     EXPECT_EQ(200, page_width);
     EXPECT_EQ(300, page_height);
     ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
-    CompareBitmap(bitmap.get(), page_width, page_height, kShrunkMD5);
+    CompareBitmap(bitmap.get(), page_width, page_height, ShrunkChecksum());
 
     CloseSavedPage(saved_page);
     CloseSavedDocument();
   }
 }
-#endif  // defined(OS_LINUX) || defined(OS_FUCHSIA)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
+        // BUILDFLAG(IS_FUCHSIA)
diff --git a/fpdfsdk/fpdf_view.cpp b/fpdfsdk/fpdf_view.cpp
index da25308..f93561c 100644
--- a/fpdfsdk/fpdf_view.cpp
+++ b/fpdfsdk/fpdf_view.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,27 +14,33 @@
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
 #include "core/fpdfapi/page/cpdf_occontext.h"
 #include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
 #include "core/fpdfapi/page/cpdf_pagemodule.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_parser.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fpdfapi/render/cpdf_docrenderdata.h"
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
 #include "core/fpdfapi/render/cpdf_pagerendercontext.h"
 #include "core/fpdfapi/render/cpdf_rendercontext.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fpdfdoc/cpdf_nametree.h"
 #include "core/fpdfdoc/cpdf_viewerpreferences.h"
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
+#include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxcrt/stl_util.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/cfx_gemodule.h"
 #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"
@@ -42,54 +48,159 @@
 #include "fpdfsdk/cpdfsdk_renderpage.h"
 #include "fxjs/ijs_runtime.h"
 #include "public/fpdf_formfill.h"
-#include "third_party/base/ptr_util.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/memory/ptr_util.h"
+#include "third_party/base/numerics/safe_conversions.h"
+
+#ifdef PDF_ENABLE_V8
+#include "fxjs/cfx_v8_array_buffer_allocator.h"
+#include "third_party/base/no_destructor.h"
+#endif
 
 #ifdef PDF_ENABLE_XFA
 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
-#include "fxbarcode/BC_Library.h"
 #endif  // PDF_ENABLE_XFA
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "core/fpdfapi/render/cpdf_progressiverenderer.h"
 #include "core/fpdfapi/render/cpdf_windowsrenderdevice.h"
 #include "public/fpdf_edit.h"
 
+#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(WindowsPrintMode::kModeEmf == FPDF_PRINTMODE_EMF,
-              "WindowsPrintMode::kModeEmf value mismatch");
-static_assert(WindowsPrintMode::kModeTextOnly == FPDF_PRINTMODE_TEXTONLY,
-              "WindowsPrintMode::kModeTextOnly value mismatch");
-static_assert(WindowsPrintMode::kModePostScript2 == FPDF_PRINTMODE_POSTSCRIPT2,
-              "WindowsPrintMode::kModePostScript2 value mismatch");
-static_assert(WindowsPrintMode::kModePostScript3 == FPDF_PRINTMODE_POSTSCRIPT3,
-              "WindowsPrintMode::kModePostScript3 value mismatch");
-static_assert(WindowsPrintMode::kModePostScript2PassThrough ==
+static_assert(static_cast<int>(WindowsPrintMode::kEmf) == FPDF_PRINTMODE_EMF,
+              "WindowsPrintMode::kEmf value mismatch");
+static_assert(static_cast<int>(WindowsPrintMode::kTextOnly) ==
+                  FPDF_PRINTMODE_TEXTONLY,
+              "WindowsPrintMode::kTextOnly value mismatch");
+static_assert(static_cast<int>(WindowsPrintMode::kPostScript2) ==
+                  FPDF_PRINTMODE_POSTSCRIPT2,
+              "WindowsPrintMode::kPostScript2 value mismatch");
+static_assert(static_cast<int>(WindowsPrintMode::kPostScript3) ==
+                  FPDF_PRINTMODE_POSTSCRIPT3,
+              "WindowsPrintMode::kPostScript3 value mismatch");
+static_assert(static_cast<int>(WindowsPrintMode::kPostScript2PassThrough) ==
                   FPDF_PRINTMODE_POSTSCRIPT2_PASSTHROUGH,
-              "WindowsPrintMode::kModePostScript2PassThrough value mismatch");
-static_assert(WindowsPrintMode::kModePostScript3PassThrough ==
+              "WindowsPrintMode::kPostScript2PassThrough value mismatch");
+static_assert(static_cast<int>(WindowsPrintMode::kPostScript3PassThrough) ==
                   FPDF_PRINTMODE_POSTSCRIPT3_PASSTHROUGH,
-              "WindowsPrintMode::kModePostScript3PassThrough value mismatch");
-#endif  // defined(OS_WIN)
+              "WindowsPrintMode::kPostScript3PassThrough value mismatch");
+static_assert(static_cast<int>(WindowsPrintMode::kEmfImageMasks) ==
+                  FPDF_PRINTMODE_EMF_IMAGE_MASKS,
+              "WindowsPrintMode::kEmfImageMasks value mismatch");
+static_assert(static_cast<int>(WindowsPrintMode::kPostScript3Type42) ==
+                  FPDF_PRINTMODE_POSTSCRIPT3_TYPE42,
+              "WindowsPrintMode::kPostScript3Type42 value mismatch");
+static_assert(
+    static_cast<int>(WindowsPrintMode::kPostScript3Type42PassThrough) ==
+        FPDF_PRINTMODE_POSTSCRIPT3_TYPE42_PASSTHROUGH,
+    "WindowsPrintMode::kPostScript3Type42PassThrough value mismatch");
+#endif  // BUILDFLAG(IS_WIN)
+
+#if defined(_SKIA_SUPPORT_)
+// These checks are here because core/ and public/ cannot depend on each other.
+static_assert(static_cast<int>(CFX_DefaultRenderDevice::RendererType::kAgg) ==
+                  FPDF_RENDERERTYPE_AGG,
+              "CFX_DefaultRenderDevice::RendererType::kAGG value mismatch");
+static_assert(static_cast<int>(CFX_DefaultRenderDevice::RendererType::kSkia) ==
+                  FPDF_RENDERERTYPE_SKIA,
+              "CFX_DefaultRenderDevice::RendererType::kSkia value mismatch");
+#endif  // defined(_SKIA_SUPPORT_)
 
 namespace {
 
 bool g_bLibraryInitialized = false;
 
-FPDF_DOCUMENT LoadDocumentImpl(
-    const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
-    FPDF_BYTESTRING password) {
+void UseRendererType(FPDF_RENDERER_TYPE public_type) {
+  // Internal definition of renderer types must stay updated with respect to
+  // the public definition, such that all public definitions can be mapped to
+  // an internal definition in `CFX_DefaultRenderDevice`. A public definition
+  // value might not be meaningful for a particular build configuration, which
+  // would mean use of that value is an error for that build.
+
+  // AGG is always present in a build. |FPDF_RENDERERTYPE_SKIA| is valid to use
+  // only if it is included in the build.
+#if defined(_SKIA_SUPPORT_)
+  // This build configuration has the option for runtime renderer selection.
+  if (public_type == FPDF_RENDERERTYPE_AGG ||
+      public_type == FPDF_RENDERERTYPE_SKIA) {
+    CFX_DefaultRenderDevice::SetDefaultRenderer(
+        static_cast<CFX_DefaultRenderDevice::RendererType>(public_type));
+    return;
+  }
+  CHECK(false);
+#else
+  // `FPDF_RENDERERTYPE_AGG` is used for fully AGG builds.
+  CHECK_EQ(public_type, FPDF_RENDERERTYPE_AGG);
+#endif
+}
+
+RetainPtr<const CPDF_Object> GetXFAEntryFromDocument(const CPDF_Document* doc) {
+  const CPDF_Dictionary* root = doc->GetRoot();
+  if (!root)
+    return nullptr;
+
+  RetainPtr<const CPDF_Dictionary> acro_form = root->GetDictFor("AcroForm");
+  return acro_form ? acro_form->GetObjectFor("XFA") : nullptr;
+}
+
+struct XFAPacket {
+  ByteString name;
+  RetainPtr<const CPDF_Stream> data;
+};
+
+std::vector<XFAPacket> GetXFAPackets(RetainPtr<const CPDF_Object> xfa_object) {
+  std::vector<XFAPacket> packets;
+
+  if (!xfa_object)
+    return packets;
+
+  RetainPtr<const CPDF_Stream> xfa_stream = ToStream(xfa_object->GetDirect());
+  if (xfa_stream) {
+    packets.push_back({"", std::move(xfa_stream)});
+    return packets;
+  }
+
+  RetainPtr<const CPDF_Array> xfa_array = ToArray(xfa_object->GetDirect());
+  if (!xfa_array)
+    return packets;
+
+  packets.reserve(1 + (xfa_array->size() / 2));
+  for (size_t i = 0; i < xfa_array->size(); i += 2) {
+    if (i + 1 == xfa_array->size())
+      break;
+
+    RetainPtr<const CPDF_String> name = xfa_array->GetStringAt(i);
+    if (!name)
+      continue;
+
+    RetainPtr<const CPDF_Stream> data = xfa_array->GetStreamAt(i + 1);
+    if (!data)
+      continue;
+
+    packets.push_back({name->GetString(), std::move(data)});
+  }
+  return packets;
+}
+
+FPDF_DOCUMENT LoadDocumentImpl(RetainPtr<IFX_SeekableReadStream> pFileAccess,
+                               FPDF_BYTESTRING password) {
   if (!pFileAccess) {
     ProcessParseError(CPDF_Parser::FILE_ERROR);
     return nullptr;
   }
 
-  auto pDocument = pdfium::MakeUnique<CPDF_Document>(
-      pdfium::MakeUnique<CPDF_DocRenderData>(),
-      pdfium::MakeUnique<CPDF_DocPageData>());
+  auto pDocument =
+      std::make_unique<CPDF_Document>(std::make_unique<CPDF_DocRenderData>(),
+                                      std::make_unique<CPDF_DocPageData>());
 
-  CPDF_Parser::Error error = pDocument->LoadDoc(pFileAccess, password);
+  CPDF_Parser::Error error =
+      pDocument->LoadDoc(std::move(pFileAccess), password);
   if (error != CPDF_Parser::SUCCESS) {
     ProcessParseError(error);
     return nullptr;
@@ -110,16 +221,22 @@
   if (g_bLibraryInitialized)
     return;
 
-  FXMEM_InitializePartitionAlloc();
+  FX_InitializeMemoryAllocators();
   CFX_GEModule::Create(config ? config->m_pUserFontPaths : nullptr);
   CPDF_PageModule::Create();
 
 #ifdef PDF_ENABLE_XFA
-  BC_Library_Init();
+  CPDFXFA_ModuleInit();
 #endif  // PDF_ENABLE_XFA
-  if (config && config->version >= 2)
-    IJS_Runtime::Initialize(config->m_v8EmbedderSlot, config->m_pIsolate);
 
+  if (config && config->version >= 2) {
+    void* platform = config->version >= 3 ? config->m_pPlatform : nullptr;
+    IJS_Runtime::Initialize(config->m_v8EmbedderSlot, config->m_pIsolate,
+                            platform);
+
+    if (config->version >= 4)
+      UseRendererType(config->m_RendererType);
+  }
   g_bLibraryInitialized = true;
 }
 
@@ -128,7 +245,7 @@
     return;
 
 #ifdef PDF_ENABLE_XFA
-  BC_Library_Destroy();
+  CPDFXFA_ModuleDestroy();
 #endif  // PDF_ENABLE_XFA
 
   CPDF_PageModule::Destroy();
@@ -143,27 +260,17 @@
   return SetPDFSandboxPolicy(policy, enable);
 }
 
-#if defined(OS_WIN)
-#if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
-FPDF_EXPORT void FPDF_CALLCONV
-FPDF_SetTypefaceAccessibleFunc(PDFiumEnsureTypefaceCharactersAccessible func) {
-  g_pdfium_typeface_accessible_func = func;
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_SetPrintTextWithGDI(FPDF_BOOL use_gdi) {
-  g_pdfium_print_text_with_gdi = !!use_gdi;
-}
-#endif  // PDFIUM_PRINT_TEXT_WITH_GDI
-
+#if BUILDFLAG(IS_WIN)
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SetPrintMode(int mode) {
   if (mode < FPDF_PRINTMODE_EMF ||
-      mode > FPDF_PRINTMODE_POSTSCRIPT3_PASSTHROUGH) {
+      mode > FPDF_PRINTMODE_POSTSCRIPT3_TYPE42_PASSTHROUGH) {
     return FALSE;
   }
+
   g_pdfium_print_mode = static_cast<WindowsPrintMode>(mode);
   return TRUE;
 }
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
 FPDF_LoadDocument(FPDF_STRING file_path, FPDF_BYTESTRING password) {
@@ -182,11 +289,11 @@
   if (!pRoot)
     return FORMTYPE_NONE;
 
-  const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
+  RetainPtr<const CPDF_Dictionary> pAcroForm = pRoot->GetDictFor("AcroForm");
   if (!pAcroForm)
     return FORMTYPE_NONE;
 
-  const CPDF_Object* pXFA = pAcroForm->GetObjectFor("XFA");
+  RetainPtr<const CPDF_Object> pXFA = pAcroForm->GetObjectFor("XFA");
   if (!pXFA)
     return FORMTYPE_ACRO_FORM;
 
@@ -210,7 +317,17 @@
 FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
 FPDF_LoadMemDocument(const void* data_buf, int size, FPDF_BYTESTRING password) {
   return LoadDocumentImpl(
-      pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+      pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
+          pdfium::make_span(static_cast<const uint8_t*>(data_buf), size)),
+      password);
+}
+
+FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
+FPDF_LoadMemDocument64(const void* data_buf,
+                       size_t size,
+                       FPDF_BYTESTRING password) {
+  return LoadDocumentImpl(
+      pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
           pdfium::make_span(static_cast<const uint8_t*>(data_buf), size)),
       password);
 }
@@ -260,7 +377,7 @@
   if (!pDoc || !pDoc->GetParser())
     return -1;
 
-  const CPDF_Dictionary* pDict = pDoc->GetParser()->GetEncryptDict();
+  RetainPtr<const CPDF_Dictionary> pDict = pDoc->GetParser()->GetEncryptDict();
   return pDict ? pDict->GetIntegerFor("R") : -1;
 }
 
@@ -284,17 +401,20 @@
 
 #ifdef PDF_ENABLE_XFA
   auto* pContext = static_cast<CPDFXFA_Context*>(pDoc->GetExtension());
-  if (pContext)
-    return FPDFPageFromIPDFPage(pContext->GetXFAPage(page_index).Leak());
+  if (pContext) {
+    return FPDFPageFromIPDFPage(
+        pContext->GetOrCreateXFAPage(page_index).Leak());
+  }
 #endif  // PDF_ENABLE_XFA
 
-  CPDF_Dictionary* pDict = pDoc->GetPageDictionary(page_index);
+  RetainPtr<CPDF_Dictionary> pDict = pDoc->GetMutablePageDictionary(page_index);
   if (!pDict)
     return nullptr;
 
-  auto pPage = pdfium::MakeRetain<CPDF_Page>(pDoc, pDict);
-  pPage->SetRenderCache(pdfium::MakeUnique<CPDF_PageRenderCache>(pPage.Get()));
+  auto pPage = pdfium::MakeRetain<CPDF_Page>(pDoc, std::move(pDict));
+  pPage->AddPageImageCache();
   pPage->ParseContent();
+
   return FPDFPageFromIPDFPage(pPage.Leak());
 }
 
@@ -329,84 +449,27 @@
   return true;
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 namespace {
 
-const double kEpsilonSize = 0.01f;
+constexpr float kEpsilonSize = 0.01f;
 
-void GetScaling(CPDF_Page* pPage,
-                int size_x,
-                int size_y,
-                int rotate,
-                double* scale_x,
-                double* scale_y) {
-  ASSERT(pPage);
-  ASSERT(scale_x);
-  ASSERT(scale_y);
-  double page_width = pPage->GetPageWidth();
-  double page_height = pPage->GetPageHeight();
-  if (page_width < kEpsilonSize || page_height < kEpsilonSize)
-    return;
-
-  if (rotate % 2 == 0) {
-    *scale_x = size_x / page_width;
-    *scale_y = size_y / page_height;
-  } else {
-    *scale_x = size_y / page_width;
-    *scale_y = size_x / page_height;
-  }
+bool IsPageTooSmall(const CPDF_Page* page) {
+  const CFX_SizeF& page_size = page->GetPageSize();
+  return page_size.width < kEpsilonSize || page_size.height < kEpsilonSize;
 }
 
-FX_RECT GetMaskDimensionsAndOffsets(CPDF_Page* pPage,
-                                    int start_x,
-                                    int start_y,
-                                    int size_x,
-                                    int size_y,
-                                    int rotate,
-                                    const CFX_FloatRect& mask_box) {
-  double scale_x = 0.0f;
-  double scale_y = 0.0f;
-  GetScaling(pPage, size_x, size_y, rotate, &scale_x, &scale_y);
-  if (scale_x < kEpsilonSize || scale_y < kEpsilonSize)
-    return FX_RECT();
-
-  // Compute sizes in page points. Round down to catch the entire bitmap.
-  int start_x_bm = static_cast<int>(mask_box.left * scale_x);
-  int start_y_bm = static_cast<int>(mask_box.bottom * scale_y);
-  int size_x_bm = static_cast<int>(mask_box.right * scale_x + 1.0f) -
-                  static_cast<int>(mask_box.left * scale_x);
-  int size_y_bm = static_cast<int>(mask_box.top * scale_y + 1.0f) -
-                  static_cast<int>(mask_box.bottom * scale_y);
-
-  // Get page rotation
-  int page_rotation = pPage->GetPageRotation();
-
-  // Compute offsets
-  int offset_x = 0;
-  int offset_y = 0;
-  if (size_x > size_y)
-    std::swap(size_x_bm, size_y_bm);
-
-  switch ((rotate + page_rotation) % 4) {
-    case 0:
-      offset_x = start_x_bm + start_x;
-      offset_y = start_y + size_y - size_y_bm - start_y_bm;
-      break;
-    case 1:
-      offset_x = start_y_bm + start_x;
-      offset_y = start_x_bm + start_y;
-      break;
-    case 2:
-      offset_x = start_x + size_x - size_x_bm - start_x_bm;
-      offset_y = start_y_bm + start_y;
-      break;
-    case 3:
-      offset_x = start_x + size_x - size_x_bm - start_y_bm;
-      offset_y = start_y + size_y - size_y_bm - start_x_bm;
-      break;
+bool IsScalingTooSmall(const CFX_Matrix& matrix) {
+  float horizontal;
+  float vertical;
+  if (matrix.a == 0.0f && matrix.d == 0.0f) {
+    horizontal = matrix.b;
+    vertical = matrix.c;
+  } else {
+    horizontal = matrix.a;
+    vertical = matrix.d;
   }
-  return FX_RECT(offset_x, offset_y, offset_x + size_x_bm,
-                 offset_y + size_y_bm);
+  return fabsf(horizontal) < kEpsilonSize || fabsf(vertical) < kEpsilonSize;
 }
 
 // Get a bitmap of just the mask section defined by |mask_box| from a full page
@@ -420,17 +483,24 @@
                                       const RetainPtr<CFX_DIBitmap>& pSrc,
                                       const CFX_FloatRect& mask_box,
                                       FX_RECT* bitmap_area) {
-  ASSERT(bitmap_area);
-  *bitmap_area = GetMaskDimensionsAndOffsets(pPage, start_x, start_y, size_x,
-                                             size_y, rotate, mask_box);
+  if (IsPageTooSmall(pPage))
+    return nullptr;
+
+  FX_RECT page_rect(start_x, start_y, start_x + size_x, start_y + size_y);
+  CFX_Matrix matrix = pPage->GetDisplayMatrix(page_rect, rotate);
+  if (IsScalingTooSmall(matrix))
+    return nullptr;
+
+  *bitmap_area = matrix.TransformRect(mask_box).GetOuterRect();
   if (bitmap_area->IsEmpty())
     return nullptr;
 
   // Create a new bitmap to transfer part of the page bitmap to.
   RetainPtr<CFX_DIBitmap> pDst = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pDst->Create(bitmap_area->Width(), bitmap_area->Height(), FXDIB_Argb))
+  if (!pDst->Create(bitmap_area->Width(), bitmap_area->Height(),
+                    FXDIB_Format::kArgb)) {
     return nullptr;
-
+  }
   pDst->Clear(0x00ffffff);
   pDst->TransferBitmap(0, 0, bitmap_area->Width(), bitmap_area->Height(), pSrc,
                        bitmap_area->left, bitmap_area->top);
@@ -447,7 +517,7 @@
 
   // Create a new bitmap from the old one
   RetainPtr<CFX_DIBitmap> pDst = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pDst->Create(size_x_bm, size_y_bm, FXDIB_Rgb32))
+  if (!pDst->Create(size_x_bm, size_y_bm, FXDIB_Format::kRgb32))
     return;
 
   pDst->Clear(0xffffffff);
@@ -476,62 +546,67 @@
   if (!pPage)
     return;
 
-  auto pOwnedContext = pdfium::MakeUnique<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
   // only be used when necessary. For large numbers of masks, rendering each
   // individually is inefficient and unlikely to significantly improve spool
-  // size. TODO(rbpotter): Find out why this still breaks printing for some
-  // PDFs (see crbug.com/777837).
-  const bool bEnableImageMasks = false;
+  // size.
+  const bool bEnableImageMasks =
+      g_pdfium_print_mode == WindowsPrintMode::kEmfImageMasks;
   const bool bNewBitmap = pPage->BackgroundAlphaNeeded() ||
                           (pPage->HasImageMask() && !bEnableImageMasks) ||
                           pPage->GetMaskBoundingBoxes().size() > 100;
   const bool bHasMask = pPage->HasImageMask() && !bNewBitmap;
+  auto* render_data = CPDF_DocRenderData::FromDocument(pPage->GetDocument());
   if (!bNewBitmap && !bHasMask) {
-    pContext->m_pDevice = pdfium::MakeUnique<CPDF_WindowsRenderDevice>(dc);
-    CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
+    context->m_pDevice = std::make_unique<CPDF_WindowsRenderDevice>(
+        dc, render_data->GetPSFontTracker());
+    CPDFSDK_RenderPageWithContext(context, pPage, start_x, start_y, size_x,
                                   size_y, rotate, flags,
+                                  /*color_scheme=*/nullptr,
                                   /*need_to_restore=*/true, /*pause=*/nullptr);
     return;
   }
 
   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|.
-  pBitmap->Create(size_x, size_y, FXDIB_Argb);
+  // 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, false, nullptr, false);
+  auto device = std::make_unique<CFX_DefaultRenderDevice>();
+  device->Attach(pBitmap);
+  context->m_pDevice = std::move(device);
   if (bHasMask) {
-    pContext->m_pOptions = pdfium::MakeUnique<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,
-                                size_y, rotate, flags, /*need_to_restore=*/true,
+  CPDFSDK_RenderPageWithContext(context, pPage, start_x, start_y, size_x,
+                                size_y, rotate, flags, /*color_scheme=*/nullptr,
+                                /*need_to_restore=*/true,
                                 /*pause=*/nullptr);
 
   if (!bHasMask) {
-    CPDF_WindowsRenderDevice WinDC(dc);
+    CPDF_WindowsRenderDevice win_dc(dc, render_data->GetPSFontTracker());
     bool bitsStretched = false;
-    if (WinDC.GetDeviceType() == DeviceType::kPrinter) {
+    if (win_dc.GetDeviceType() == DeviceType::kPrinter) {
       auto pDst = pdfium::MakeRetain<CFX_DIBitmap>();
-      if (pDst->Create(size_x, size_y, FXDIB_Rgb32)) {
-        memset(pDst->GetBuffer(), -1, pBitmap->GetPitch() * size_y);
+      if (pDst->Create(size_x, size_y, FXDIB_Format::kRgb32)) {
+        fxcrt::spanset(
+            pDst->GetWritableBuffer().first(pBitmap->GetPitch() * size_y), -1);
         pDst->CompositeBitmap(0, 0, size_x, size_y, pBitmap, 0, 0,
                               BlendMode::kNormal, nullptr, false);
-        WinDC.StretchDIBits(pDst, 0, 0, size_x, size_y);
+        win_dc.StretchDIBits(pDst, 0, 0, size_x, size_y);
         bitsStretched = true;
       }
     }
     if (!bitsStretched)
-      WinDC.SetDIBits(pBitmap, 0, 0);
+      win_dc.SetDIBits(pBitmap, 0, 0);
     return;
   }
 
@@ -543,33 +618,36 @@
   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.
-  pOwnedContext = pdfium::MakeUnique<CPDF_PageRenderContext>();
-  pContext = pOwnedContext.get();
-  pPage->SetRenderContext(std::move(pOwnedContext));
-  pContext->m_pDevice = pdfium::MakeUnique<CPDF_WindowsRenderDevice>(dc);
-  pContext->m_pOptions = pdfium::MakeUnique<CPDF_RenderOptions>();
-  pContext->m_pOptions->GetOptions().bBreakForMasks = true;
+  pPage->ClearRenderContext();
+  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());
+  context->m_pOptions = std::make_unique<CPDF_RenderOptions>();
+  context->m_pOptions->GetOptions().bBreakForMasks = true;
 
-  CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
-                                size_y, rotate, flags, /*need_to_restore=*/true,
+  CPDFSDK_RenderPageWithContext(context, pPage, start_x, start_y, size_x,
+                                size_y, rotate, flags, /*color_scheme=*/nullptr,
+                                /*need_to_restore=*/true,
                                 /*pause=*/nullptr);
 
   // Render masks
   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  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPageBitmap(FPDF_BITMAP bitmap,
                                                      FPDF_PAGE page,
@@ -586,24 +664,25 @@
   if (!pPage)
     return;
 
-  auto pOwnedContext = pdfium::MakeUnique<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 = pdfium::MakeUnique<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->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false);
-  CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
-                                size_y, rotate, flags, /*need_to_restore=*/true,
+  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);
 
-#ifdef _SKIA_SUPPORT_PATHS_
-  pDevice->Flush(true);
-  pBitmap->UnPreMultiply();
+#if defined(_SKIA_SUPPORT_)
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    pBitmap->UnPreMultiply();
+  }
 #endif
 }
 
@@ -620,17 +699,16 @@
   if (!pPage)
     return;
 
-  auto pOwnedContext = pdfium::MakeUnique<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 = pdfium::MakeUnique<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->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false);
+  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)
@@ -641,31 +719,38 @@
   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);
 }
 
-#ifdef _SKIA_SUPPORT_
-FPDF_EXPORT FPDF_RECORDER FPDF_CALLCONV FPDF_RenderPageSkp(FPDF_PAGE page,
-                                                           int size_x,
-                                                           int size_y) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage)
-    return nullptr;
+#if defined(_SKIA_SUPPORT_)
+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 = pdfium::MakeUnique<CPDF_PageRenderContext>();
-  CPDF_PageRenderContext* pContext = pOwnedContext.get();
-  CPDF_Page::RenderContextClearer clearer(pPage);
-  pPage->SetRenderContext(std::move(pOwnedContext));
+  CPDF_Page* cpdf_page = CPDFPageFromFPDFPage(page);
+  if (!cpdf_page) {
+    return;
+  }
 
-  auto skDevice = pdfium::MakeUnique<CFX_DefaultRenderDevice>();
-  FPDF_RECORDER recorder = skDevice->CreateRecorder(size_x, size_y);
-  pContext->m_pDevice = std::move(skDevice);
+  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;
 }
-#endif  // _SKIA_SUPPORT_
+#endif  // defined(_SKIA_SUPPORT_)
 
 FPDF_EXPORT void FPDF_CALLCONV FPDF_ClosePage(FPDF_PAGE page) {
   if (!page)
@@ -678,20 +763,10 @@
   if (pPage->AsXFAPage())
     return;
 
-  CPDFSDK_PageView* pPageView =
-      static_cast<CPDFSDK_PageView*>(pPage->AsPDFPage()->GetView());
-  if (!pPageView || pPageView->IsBeingDestroyed())
-    return;
-
-  if (pPageView->IsLocked()) {
-    pPageView->TakePageOwnership();
-    return;
-  }
-
-  // This will delete the |pPageView| object. We must cleanup the PageView
-  // first because it will attempt to reset the View on the |pPage| during
-  // destruction.
-  pPageView->GetFormFillEnv()->RemovePageView(pPage.Get());
+  // This will delete the PageView object corresponding to |pPage|. We must
+  // cleanup the PageView before releasing the reference on |pPage| as it will
+  // attempt to reset the PageView during destruction.
+  pPage->AsPDFPage()->ClearView();
 }
 
 FPDF_EXPORT void FPDF_CALLCONV FPDF_CloseDocument(FPDF_DOCUMENT document) {
@@ -718,9 +793,9 @@
 
   IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
   const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y);
-  Optional<CFX_PointF> pos =
+  absl::optional<CFX_PointF> pos =
       pPage->DeviceToPage(rect, rotate, CFX_PointF(device_x, device_y));
-  if (!pos)
+  if (!pos.has_value())
     return false;
 
   *page_x = pos->x;
@@ -744,8 +819,9 @@
   IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
   const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y);
   CFX_PointF page_point(static_cast<float>(page_x), static_cast<float>(page_y));
-  Optional<CFX_PointF> pos = pPage->PageToDevice(rect, rotate, page_point);
-  if (!pos)
+  absl::optional<CFX_PointF> pos =
+      pPage->PageToDevice(rect, rotate, page_point);
+  if (!pos.has_value())
     return false;
 
   *device_x = FXSYS_roundf(pos->x);
@@ -757,9 +833,10 @@
                                                         int height,
                                                         int alpha) {
   auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pBitmap->Create(width, height, alpha ? FXDIB_Argb : FXDIB_Rgb32))
+  if (!pBitmap->Create(width, height,
+                       alpha ? FXDIB_Format::kArgb : FXDIB_Format::kRgb32)) {
     return nullptr;
-
+  }
   return FPDFBitmapFromCFXDIBitmap(pBitmap.Leak());
 }
 
@@ -771,16 +848,16 @@
   FXDIB_Format fx_format;
   switch (format) {
     case FPDFBitmap_Gray:
-      fx_format = FXDIB_8bppRgb;
+      fx_format = FXDIB_Format::k8bppRgb;
       break;
     case FPDFBitmap_BGR:
-      fx_format = FXDIB_Rgb;
+      fx_format = FXDIB_Format::kRgb;
       break;
     case FPDFBitmap_BGRx:
-      fx_format = FXDIB_Rgb32;
+      fx_format = FXDIB_Format::kRgb32;
       break;
     case FPDFBitmap_BGRA:
-      fx_format = FXDIB_Argb;
+      fx_format = FXDIB_Format::kArgb;
       break;
     default:
       return nullptr;
@@ -789,7 +866,7 @@
   // Ensure external memory is good at least for the duration of this call.
   UnownedPtr<uint8_t> pChecker(static_cast<uint8_t*>(first_scan));
   auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pBitmap->Create(width, height, fx_format, pChecker.Get(), stride))
+  if (!pBitmap->Create(width, height, fx_format, pChecker, stride))
     return nullptr;
 
   return FPDFBitmapFromCFXDIBitmap(pBitmap.Leak());
@@ -801,14 +878,14 @@
 
   FXDIB_Format format = CFXDIBitmapFromFPDFBitmap(bitmap)->GetFormat();
   switch (format) {
-    case FXDIB_8bppRgb:
-    case FXDIB_8bppMask:
+    case FXDIB_Format::k8bppRgb:
+    case FXDIB_Format::k8bppMask:
       return FPDFBitmap_Gray;
-    case FXDIB_Rgb:
+    case FXDIB_Format::kRgb:
       return FPDFBitmap_BGR;
-    case FXDIB_Rgb32:
+    case FXDIB_Format::kRgb32:
       return FPDFBitmap_BGRx;
-    case FXDIB_Argb:
+    case FXDIB_Format::kArgb:
       return FPDFBitmap_BGRA;
     default:
       return FPDFBitmap_Unknown;
@@ -826,14 +903,16 @@
 
   CFX_DefaultRenderDevice device;
   RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
-  device.Attach(pBitmap, false, nullptr, false);
-  if (!pBitmap->HasAlpha())
+  device.Attach(pBitmap);
+  if (!pBitmap->IsAlphaFormat())
     color |= 0xFF000000;
-  device.FillRect(FX_RECT(left, top, left + width, top + height), color);
+  device.FillRect(FX_RECT(left, top, left + width, top + height),
+                  static_cast<uint32_t>(color));
 }
 
 FPDF_EXPORT void* FPDF_CALLCONV FPDFBitmap_GetBuffer(FPDF_BITMAP bitmap) {
-  return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetBuffer() : nullptr;
+  return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetWritableBuffer().data()
+                : nullptr;
 }
 
 FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetWidth(FPDF_BITMAP bitmap) {
@@ -870,7 +949,7 @@
 
   auto* pContext = static_cast<CPDFXFA_Context*>(pDoc->GetExtension());
   if (pContext) {
-    RetainPtr<CPDFXFA_Page> pPage = pContext->GetXFAPage(page_index);
+    RetainPtr<CPDFXFA_Page> pPage = pContext->GetOrCreateXFAPage(page_index);
     if (!pPage)
       return false;
 
@@ -880,12 +959,12 @@
   }
 #endif  // PDF_ENABLE_XFA
 
-  CPDF_Dictionary* pDict = pDoc->GetPageDictionary(page_index);
+  RetainPtr<CPDF_Dictionary> pDict = pDoc->GetMutablePageDictionary(page_index);
   if (!pDict)
     return false;
 
-  auto page = pdfium::MakeRetain<CPDF_Page>(pDoc, pDict);
-  page->SetRenderCache(pdfium::MakeUnique<CPDF_PageRenderCache>(page.Get()));
+  auto page = pdfium::MakeRetain<CPDF_Page>(pDoc, std::move(pDict));
+  page->AddPageImageCache();
   size->width = page->GetPageWidth();
   size->height = page->GetPageHeight();
   return true;
@@ -931,6 +1010,8 @@
   if (!pDoc)
     return nullptr;
   CPDF_ViewerPreferences viewRef(pDoc);
+
+  // Unretained reference in public API. NOLINTNEXTLINE
   return FPDFPageRangeFromCPDFArray(viewRef.PrintPageRange());
 }
 
@@ -975,14 +1056,11 @@
     return 0;
 
   CPDF_ViewerPreferences viewRef(pDoc);
-  Optional<ByteString> bsVal = viewRef.GenericName(key);
-  if (!bsVal)
+  absl::optional<ByteString> bsVal = viewRef.GenericName(key);
+  if (!bsVal.has_value())
     return 0;
 
-  unsigned long dwStringLen = bsVal->GetLength() + 1;
-  if (buffer && length >= dwStringLen)
-    memcpy(buffer, bsVal->c_str(), dwStringLen);
-  return dwStringLen;
+  return NulTerminateMaybeCopyAndReturnLength(bsVal.value(), buffer, length);
 }
 
 FPDF_EXPORT FPDF_DWORD FPDF_CALLCONV
@@ -995,16 +1073,12 @@
   if (!pRoot)
     return 0;
 
-  CPDF_NameTree nameTree(pDoc, "Dests");
-  pdfium::base::CheckedNumeric<FPDF_DWORD> count = nameTree.GetCount();
-  const CPDF_Dictionary* pDest = pRoot->GetDictFor("Dests");
-  if (pDest)
-    count += pDest->size();
-
-  if (!count.IsValid())
-    return 0;
-
-  return count.ValueOrDie();
+  auto name_tree = CPDF_NameTree::Create(pDoc, "Dests");
+  FX_SAFE_UINT32 count = name_tree ? name_tree->GetCount() : 0;
+  RetainPtr<const CPDF_Dictionary> pOldStyleDests = pRoot->GetDictFor("Dests");
+  if (pOldStyleDests)
+    count += pOldStyleDests->size();
+  return count.ValueOrDefault(0);
 }
 
 FPDF_EXPORT FPDF_DEST FPDF_CALLCONV
@@ -1016,17 +1090,24 @@
   if (!pDoc)
     return nullptr;
 
-  CPDF_NameTree name_tree(pDoc, "Dests");
-  ByteStringView name_view(name);
-  return FPDFDestFromCPDFArray(
-      name_tree.LookupNamedDest(pDoc, PDF_DecodeText(name_view.raw_span())));
+  ByteString dest_name(name);
+
+  // TODO(tsepez): murky ownership, should caller get a reference?
+  // Unretained reference in public API. NOLINTNEXTLINE
+  return FPDFDestFromCPDFArray(CPDF_NameTree::LookupNamedDest(pDoc, dest_name));
 }
 
 #ifdef PDF_ENABLE_V8
 FPDF_EXPORT const char* FPDF_CALLCONV FPDF_GetRecommendedV8Flags() {
-  // Reduce exposure since no PDF should contain web assembly.
-  // Use interpreted JS only to avoid RWX pages in our address space.
-  return "--no-expose-wasm --jitless";
+  // Use interpreted JS only to avoid RWX pages in our address space. Also,
+  // --jitless implies --no-expose-wasm, which reduce exposure since no PDF
+  // should contain web assembly.
+  return "--jitless";
+}
+
+FPDF_EXPORT void* FPDF_CALLCONV FPDF_GetArrayBufferAllocatorSharedInstance() {
+  static pdfium::base::NoDestructor<CFX_V8ArrayBufferAllocator> allocator;
+  return allocator.get();
 }
 #endif  // PDF_ENABLE_V8
 
@@ -1047,7 +1128,7 @@
     return -1;
 
   if (length == -1)
-    length = strlen(cstr);
+    length = pdfium::base::checked_cast<int>(strlen(cstr));
 
   if (length == 0) {
     FPDF_BStr_Clear(bstr);
@@ -1096,40 +1177,40 @@
   if (!pRoot)
     return nullptr;
 
-  CPDF_Object* pDestObj = nullptr;
+  auto name_tree = CPDF_NameTree::Create(pDoc, "Dests");
+  size_t name_tree_count = name_tree ? name_tree->GetCount() : 0;
+  RetainPtr<const CPDF_Object> pDestObj;
   WideString wsName;
-  CPDF_NameTree nameTree(pDoc, "Dests");
-  int count = nameTree.GetCount();
-  if (index >= count) {
-    const CPDF_Dictionary* pDest = pRoot->GetDictFor("Dests");
+  if (static_cast<size_t>(index) >= name_tree_count) {
+    // If |index| is out of bounds, then try to retrieve the Nth old style named
+    // destination. Where N is 0-indexed, with N = index - name_tree_count.
+    RetainPtr<const CPDF_Dictionary> pDest = pRoot->GetDictFor("Dests");
     if (!pDest)
       return nullptr;
 
-    pdfium::base::CheckedNumeric<int> checked_count = count;
+    FX_SAFE_INT32 checked_count = name_tree_count;
     checked_count += pDest->size();
     if (!checked_count.IsValid() || index >= checked_count.ValueOrDie())
       return nullptr;
 
-    index -= count;
+    index -= name_tree_count;
     int i = 0;
     ByteStringView bsName;
     CPDF_DictionaryLocker locker(pDest);
     for (const auto& it : locker) {
       bsName = it.first.AsStringView();
-      pDestObj = it.second.Get();
-      if (!pDestObj)
-        continue;
+      pDestObj = it.second;
       if (i == index)
         break;
       i++;
     }
     wsName = PDF_DecodeText(bsName.raw_span());
   } else {
-    pDestObj = nameTree.LookupValueAndName(index, &wsName);
+    pDestObj = name_tree->LookupValueAndName(index, &wsName);
   }
   if (!pDestObj)
     return nullptr;
-  if (CPDF_Dictionary* pDict = pDestObj->AsDictionary()) {
+  if (const CPDF_Dictionary* pDict = pDestObj->AsDictionary()) {
     pDestObj = pDict->GetArrayFor("D");
     if (!pDestObj)
       return nullptr;
@@ -1138,7 +1219,7 @@
     return nullptr;
 
   ByteString utf16Name = wsName.ToUTF16LE();
-  int len = utf16Name.GetLength();
+  int len = pdfium::base::checked_cast<int>(utf16Name.GetLength());
   if (!buffer) {
     *buflen = len;
   } else if (len <= *buflen) {
@@ -1149,3 +1230,72 @@
   }
   return FPDFDestFromCPDFArray(pDestObj->AsArray());
 }
+
+FPDF_EXPORT int FPDF_CALLCONV FPDF_GetXFAPacketCount(FPDF_DOCUMENT document) {
+  CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document);
+  if (!doc)
+    return -1;
+
+  return fxcrt::CollectionSize<int>(
+      GetXFAPackets(GetXFAEntryFromDocument(doc)));
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_GetXFAPacketName(FPDF_DOCUMENT document,
+                      int index,
+                      void* buffer,
+                      unsigned long buflen) {
+  CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document);
+  if (!doc || index < 0)
+    return 0;
+
+  std::vector<XFAPacket> xfa_packets =
+      GetXFAPackets(GetXFAEntryFromDocument(doc));
+  if (static_cast<size_t>(index) >= xfa_packets.size())
+    return 0;
+
+  return NulTerminateMaybeCopyAndReturnLength(xfa_packets[index].name, buffer,
+                                              buflen);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_GetXFAPacketContent(FPDF_DOCUMENT document,
+                         int index,
+                         void* buffer,
+                         unsigned long buflen,
+                         unsigned long* out_buflen) {
+  CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document);
+  if (!doc || index < 0 || !out_buflen)
+    return false;
+
+  std::vector<XFAPacket> xfa_packets =
+      GetXFAPackets(GetXFAEntryFromDocument(doc));
+  if (static_cast<size_t>(index) >= xfa_packets.size())
+    return false;
+
+  *out_buflen = DecodeStreamMaybeCopyAndReturnLength(
+      xfa_packets[index].data,
+      {static_cast<uint8_t*>(buffer), static_cast<size_t>(buflen)});
+  return true;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_GetTrailerEnds(FPDF_DOCUMENT document,
+                    unsigned int* buffer,
+                    unsigned long length) {
+  auto* doc = CPDFDocumentFromFPDFDocument(document);
+  if (!doc)
+    return 0;
+
+  // Start recording trailer ends.
+  auto* parser = doc->GetParser();
+  std::vector<unsigned int> trailer_ends = parser->GetTrailerEnds();
+  const unsigned long trailer_ends_len =
+      fxcrt::CollectionSize<unsigned long>(trailer_ends);
+  if (buffer && length >= trailer_ends_len) {
+    for (size_t i = 0; i < trailer_ends_len; ++i)
+      buffer[i] = trailer_ends[i];
+  }
+
+  return trailer_ends_len;
+}
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index 94cd3ce..5907a4e 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -24,6 +24,7 @@
 #include "public/fpdf_progressive.h"
 #include "public/fpdf_save.h"
 #include "public/fpdf_searchex.h"
+#include "public/fpdf_signature.h"
 #include "public/fpdf_structtree.h"
 #include "public/fpdf_sysfontinfo.h"
 #include "public/fpdf_text.h"
@@ -39,19 +40,32 @@
 // Function to call from gtest harness to ensure linker resolution.
 int CheckPDFiumCApi() {
     // fpdf_annot.h
+    CHK(FPDFAnnot_AddInkStroke);
     CHK(FPDFAnnot_AppendAttachmentPoints);
     CHK(FPDFAnnot_AppendObject);
     CHK(FPDFAnnot_CountAttachmentPoints);
     CHK(FPDFAnnot_GetAP);
     CHK(FPDFAnnot_GetAttachmentPoints);
+    CHK(FPDFAnnot_GetBorder);
     CHK(FPDFAnnot_GetColor);
     CHK(FPDFAnnot_GetFlags);
+    CHK(FPDFAnnot_GetFocusableSubtypes);
+    CHK(FPDFAnnot_GetFocusableSubtypesCount);
     CHK(FPDFAnnot_GetFontSize);
+    CHK(FPDFAnnot_GetFormAdditionalActionJavaScript);
+    CHK(FPDFAnnot_GetFormControlCount);
+    CHK(FPDFAnnot_GetFormControlIndex);
+    CHK(FPDFAnnot_GetFormFieldAlternateName);
     CHK(FPDFAnnot_GetFormFieldAtPoint);
+    CHK(FPDFAnnot_GetFormFieldExportValue);
     CHK(FPDFAnnot_GetFormFieldFlags);
     CHK(FPDFAnnot_GetFormFieldName);
     CHK(FPDFAnnot_GetFormFieldType);
     CHK(FPDFAnnot_GetFormFieldValue);
+    CHK(FPDFAnnot_GetInkListCount);
+    CHK(FPDFAnnot_GetInkListPath);
+    CHK(FPDFAnnot_GetLine);
+    CHK(FPDFAnnot_GetLink);
     CHK(FPDFAnnot_GetLinkedAnnot);
     CHK(FPDFAnnot_GetNumberValue);
     CHK(FPDFAnnot_GetObject);
@@ -62,18 +76,24 @@
     CHK(FPDFAnnot_GetStringValue);
     CHK(FPDFAnnot_GetSubtype);
     CHK(FPDFAnnot_GetValueType);
+    CHK(FPDFAnnot_GetVertices);
     CHK(FPDFAnnot_HasAttachmentPoints);
     CHK(FPDFAnnot_HasKey);
     CHK(FPDFAnnot_IsChecked);
     CHK(FPDFAnnot_IsObjectSupportedSubtype);
+    CHK(FPDFAnnot_IsOptionSelected);
     CHK(FPDFAnnot_IsSupportedSubtype);
+    CHK(FPDFAnnot_RemoveInkList);
     CHK(FPDFAnnot_RemoveObject);
     CHK(FPDFAnnot_SetAP);
     CHK(FPDFAnnot_SetAttachmentPoints);
+    CHK(FPDFAnnot_SetBorder);
     CHK(FPDFAnnot_SetColor);
     CHK(FPDFAnnot_SetFlags);
+    CHK(FPDFAnnot_SetFocusableSubtypes);
     CHK(FPDFAnnot_SetRect);
     CHK(FPDFAnnot_SetStringValue);
+    CHK(FPDFAnnot_SetURI);
     CHK(FPDFAnnot_UpdateObject);
     CHK(FPDFPage_CloseAnnot);
     CHK(FPDFPage_CreateAnnot);
@@ -115,6 +135,7 @@
     CHK(FPDFAction_GetURIPath);
     CHK(FPDFBookmark_Find);
     CHK(FPDFBookmark_GetAction);
+    CHK(FPDFBookmark_GetCount);
     CHK(FPDFBookmark_GetDest);
     CHK(FPDFBookmark_GetFirstChild);
     CHK(FPDFBookmark_GetNextSibling);
@@ -125,26 +146,41 @@
     CHK(FPDFLink_CountQuadPoints);
     CHK(FPDFLink_Enumerate);
     CHK(FPDFLink_GetAction);
+    CHK(FPDFLink_GetAnnot);
     CHK(FPDFLink_GetAnnotRect);
     CHK(FPDFLink_GetDest);
     CHK(FPDFLink_GetLinkAtPoint);
     CHK(FPDFLink_GetLinkZOrderAtPoint);
     CHK(FPDFLink_GetQuadPoints);
+    CHK(FPDF_GetFileIdentifier);
     CHK(FPDF_GetMetaText);
+    CHK(FPDF_GetPageAAction);
     CHK(FPDF_GetPageLabel);
 
     // fpdf_edit.h
     CHK(FPDFFont_Close);
+    CHK(FPDFFont_GetAscent);
+    CHK(FPDFFont_GetDescent);
+    CHK(FPDFFont_GetFlags);
+    CHK(FPDFFont_GetFontData);
+    CHK(FPDFFont_GetFontName);
+    CHK(FPDFFont_GetGlyphPath);
+    CHK(FPDFFont_GetGlyphWidth);
+    CHK(FPDFFont_GetIsEmbedded);
+    CHK(FPDFFont_GetItalicAngle);
+    CHK(FPDFFont_GetWeight);
     CHK(FPDFFormObj_CountObjects);
-    CHK(FPDFFormObj_GetMatrix);
     CHK(FPDFFormObj_GetObject);
+    CHK(FPDFGlyphPath_CountGlyphSegments);
+    CHK(FPDFGlyphPath_GetGlyphPathSegment);
     CHK(FPDFImageObj_GetBitmap);
     CHK(FPDFImageObj_GetImageDataDecoded);
     CHK(FPDFImageObj_GetImageDataRaw);
     CHK(FPDFImageObj_GetImageFilter);
     CHK(FPDFImageObj_GetImageFilterCount);
     CHK(FPDFImageObj_GetImageMetadata);
-    CHK(FPDFImageObj_GetMatrix);
+    CHK(FPDFImageObj_GetImagePixelSize);
+    CHK(FPDFImageObj_GetRenderedBitmap);
     CHK(FPDFImageObj_LoadJpegFile);
     CHK(FPDFImageObj_LoadJpegFileInline);
     CHK(FPDFImageObj_SetBitmap);
@@ -167,10 +203,15 @@
     CHK(FPDFPageObj_CreateTextObj);
     CHK(FPDFPageObj_Destroy);
     CHK(FPDFPageObj_GetBounds);
+    CHK(FPDFPageObj_GetDashArray);
+    CHK(FPDFPageObj_GetDashCount);
+    CHK(FPDFPageObj_GetDashPhase);
     CHK(FPDFPageObj_GetFillColor);
     CHK(FPDFPageObj_GetLineCap);
     CHK(FPDFPageObj_GetLineJoin);
     CHK(FPDFPageObj_GetMark);
+    CHK(FPDFPageObj_GetMatrix);
+    CHK(FPDFPageObj_GetRotatedBounds);
     CHK(FPDFPageObj_GetStrokeColor);
     CHK(FPDFPageObj_GetStrokeWidth);
     CHK(FPDFPageObj_GetType);
@@ -179,9 +220,12 @@
     CHK(FPDFPageObj_NewTextObj);
     CHK(FPDFPageObj_RemoveMark);
     CHK(FPDFPageObj_SetBlendMode);
+    CHK(FPDFPageObj_SetDashArray);
+    CHK(FPDFPageObj_SetDashPhase);
     CHK(FPDFPageObj_SetFillColor);
     CHK(FPDFPageObj_SetLineCap);
     CHK(FPDFPageObj_SetLineJoin);
+    CHK(FPDFPageObj_SetMatrix);
     CHK(FPDFPageObj_SetStrokeColor);
     CHK(FPDFPageObj_SetStrokeWidth);
     CHK(FPDFPageObj_Transform);
@@ -203,20 +247,19 @@
     CHK(FPDFPath_Close);
     CHK(FPDFPath_CountSegments);
     CHK(FPDFPath_GetDrawMode);
-    CHK(FPDFPath_GetMatrix);
     CHK(FPDFPath_GetPathSegment);
     CHK(FPDFPath_LineTo);
     CHK(FPDFPath_MoveTo);
     CHK(FPDFPath_SetDrawMode);
-    CHK(FPDFPath_SetMatrix);
-    CHK(FPDFTextObj_GetFontName);
+    CHK(FPDFTextObj_GetFont);
     CHK(FPDFTextObj_GetFontSize);
-    CHK(FPDFTextObj_GetMatrix);
+    CHK(FPDFTextObj_GetRenderedBitmap);
     CHK(FPDFTextObj_GetText);
     CHK(FPDFTextObj_GetTextRenderMode);
     CHK(FPDFTextObj_SetTextRenderMode);
     CHK(FPDFText_LoadFont);
     CHK(FPDFText_LoadStandardFont);
+    CHK(FPDFText_SetCharcodes);
     CHK(FPDFText_SetText);
     CHK(FPDF_CreateNewDocument);
 
@@ -239,6 +282,7 @@
     CHK(FORM_DoDocumentOpenAction);
     CHK(FORM_DoPageAAction);
     CHK(FORM_ForceToKillFocus);
+    CHK(FORM_GetFocusedAnnot);
     CHK(FORM_GetFocusedText);
     CHK(FORM_GetSelectedText);
     CHK(FORM_IsIndexSelected);
@@ -252,10 +296,14 @@
     CHK(FORM_OnLButtonDown);
     CHK(FORM_OnLButtonUp);
     CHK(FORM_OnMouseMove);
+    CHK(FORM_OnMouseWheel);
     CHK(FORM_OnRButtonDown);
     CHK(FORM_OnRButtonUp);
     CHK(FORM_Redo);
+    CHK(FORM_ReplaceAndKeepSelection);
     CHK(FORM_ReplaceSelection);
+    CHK(FORM_SelectAllText);
+    CHK(FORM_SetFocusedAnnot);
     CHK(FORM_SetIndexSelected);
     CHK(FORM_Undo);
     CHK(FPDFDOC_ExitFormFillEnvironment);
@@ -263,8 +311,8 @@
     CHK(FPDFPage_FormFieldZOrderAtPoint);
     CHK(FPDFPage_HasFormFieldAtPoint);
     CHK(FPDF_FFLDraw);
-#ifdef _SKIA_SUPPORT_
-    CHK(FPDF_FFLRecord);
+#if defined(_SKIA_SUPPORT_)
+    CHK(FPDF_FFLDrawSkia);
 #endif
     CHK(FPDF_GetFormType);
     CHK(FPDF_LoadXFA);
@@ -280,11 +328,16 @@
     CHK(FPDFJavaScriptAction_GetScript);
 
     // fpdf_ppo.h
+    CHK(FPDF_CloseXObject);
     CHK(FPDF_CopyViewerPreferences);
     CHK(FPDF_ImportNPagesToOne);
     CHK(FPDF_ImportPages);
+    CHK(FPDF_ImportPagesByIndex);
+    CHK(FPDF_NewFormObjectFromXObject);
+    CHK(FPDF_NewXObjectFromPage);
 
     // fpdf_progressive.h
+    CHK(FPDF_RenderPageBitmapWithColorScheme_Start);
     CHK(FPDF_RenderPageBitmap_Start);
     CHK(FPDF_RenderPage_Close);
     CHK(FPDF_RenderPage_Continue);
@@ -297,11 +350,38 @@
     CHK(FPDFText_GetCharIndexFromTextIndex);
     CHK(FPDFText_GetTextIndexFromCharIndex);
 
+    // fpdf_signature.h
+    CHK(FPDFSignatureObj_GetByteRange);
+    CHK(FPDFSignatureObj_GetContents);
+    CHK(FPDFSignatureObj_GetDocMDPPermission);
+    CHK(FPDFSignatureObj_GetReason);
+    CHK(FPDFSignatureObj_GetSubFilter);
+    CHK(FPDFSignatureObj_GetTime);
+    CHK(FPDF_GetSignatureCount);
+    CHK(FPDF_GetSignatureObject);
+
     // fpdf_structtree.h
+    CHK(FPDF_StructElement_Attr_GetBlobValue);
+    CHK(FPDF_StructElement_Attr_GetBooleanValue);
+    CHK(FPDF_StructElement_Attr_GetCount);
+    CHK(FPDF_StructElement_Attr_GetName);
+    CHK(FPDF_StructElement_Attr_GetNumberValue);
+    CHK(FPDF_StructElement_Attr_GetStringValue);
+    CHK(FPDF_StructElement_Attr_GetType);
     CHK(FPDF_StructElement_CountChildren);
+    CHK(FPDF_StructElement_GetActualText);
     CHK(FPDF_StructElement_GetAltText);
+    CHK(FPDF_StructElement_GetAttributeAtIndex);
+    CHK(FPDF_StructElement_GetAttributeCount);
     CHK(FPDF_StructElement_GetChildAtIndex);
+    CHK(FPDF_StructElement_GetID);
+    CHK(FPDF_StructElement_GetLang);
     CHK(FPDF_StructElement_GetMarkedContentID);
+    CHK(FPDF_StructElement_GetMarkedContentIdAtIndex);
+    CHK(FPDF_StructElement_GetMarkedContentIdCount);
+    CHK(FPDF_StructElement_GetObjType);
+    CHK(FPDF_StructElement_GetParent);
+    CHK(FPDF_StructElement_GetStringAttribute);
     CHK(FPDF_StructElement_GetTitle);
     CHK(FPDF_StructElement_GetType);
     CHK(FPDF_StructTree_Close);
@@ -349,6 +429,8 @@
     CHK(FPDFText_GetText);
     CHK(FPDFText_GetTextRenderMode);
     CHK(FPDFText_GetUnicode);
+    CHK(FPDFText_HasUnicodeMapError);
+    CHK(FPDFText_IsGenerated);
     CHK(FPDFText_LoadPage);
 
     // fpdf_thumbnail.h
@@ -398,6 +480,9 @@
     CHK(FPDF_DestroyLibrary);
     CHK(FPDF_DeviceToPage);
     CHK(FPDF_DocumentHasValidCrossReferenceTable);
+#ifdef PDF_ENABLE_V8
+    CHK(FPDF_GetArrayBufferAllocatorSharedInstance);
+#endif
     CHK(FPDF_GetDocPermissions);
     CHK(FPDF_GetFileVersion);
     CHK(FPDF_GetLastError);
@@ -415,11 +500,16 @@
     CHK(FPDF_GetRecommendedV8Flags);
 #endif
     CHK(FPDF_GetSecurityHandlerRevision);
+    CHK(FPDF_GetTrailerEnds);
+    CHK(FPDF_GetXFAPacketContent);
+    CHK(FPDF_GetXFAPacketCount);
+    CHK(FPDF_GetXFAPacketName);
     CHK(FPDF_InitLibrary);
     CHK(FPDF_InitLibraryWithConfig);
     CHK(FPDF_LoadCustomDocument);
     CHK(FPDF_LoadDocument);
     CHK(FPDF_LoadMemDocument);
+    CHK(FPDF_LoadMemDocument64);
     CHK(FPDF_LoadPage);
     CHK(FPDF_PageToDevice);
 #ifdef _WIN32
@@ -427,19 +517,13 @@
 #endif
     CHK(FPDF_RenderPageBitmap);
     CHK(FPDF_RenderPageBitmapWithMatrix);
-#ifdef _SKIA_SUPPORT_
-    CHK(FPDF_RenderPageSkp);
+#if defined(_SKIA_SUPPORT_)
+    CHK(FPDF_RenderPageSkia);
 #endif
 #if defined(_WIN32)
     CHK(FPDF_SetPrintMode);
-#if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
-    CHK(FPDF_SetPrintTextWithGDI);
-#endif
 #endif
     CHK(FPDF_SetSandBoxPolicy);
-#if defined(_WIN32) && defined(PDFIUM_PRINT_TEXT_WITH_GDI)
-    CHK(FPDF_SetTypefaceAccessibleFunc);
-#endif
     CHK(FPDF_VIEWERREF_GetDuplex);
     CHK(FPDF_VIEWERREF_GetName);
     CHK(FPDF_VIEWERREF_GetNumCopies);
diff --git a/fpdfsdk/fpdf_view_c_api_test.h b/fpdfsdk/fpdf_view_c_api_test.h
index c662f40..87603f0 100644
--- a/fpdfsdk/fpdf_view_c_api_test.h
+++ b/fpdfsdk/fpdf_view_c_api_test.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fpdfsdk/fpdf_view_embeddertest.cpp b/fpdfsdk/fpdf_view_embeddertest.cpp
index fe20970..06b337e 100644
--- a/fpdfsdk/fpdf_view_embeddertest.cpp
+++ b/fpdfsdk/fpdf_view_embeddertest.cpp
@@ -1,27 +1,53 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <cmath>
+#include <math.h>
+
 #include <limits>
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "build/build_config.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "fpdfsdk/fpdf_view_c_api_test.h"
 #include "public/cpp/fpdf_scopers.h"
 #include "public/fpdfview.h"
 #include "testing/embedder_test.h"
+#include "testing/embedder_test_constants.h"
+#include "testing/embedder_test_environment.h"
+#include "testing/fx_string_testhelpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/utils/file_util.h"
+#include "testing/utils/hash.h"
 #include "testing/utils/path_service.h"
+#include "third_party/base/check.h"
+
+#if defined(_SKIA_SUPPORT_)
+#include "third_party/skia/include/core/SkCanvas.h"           // nogncheck
+#include "third_party/skia/include/core/SkColor.h"            // nogncheck
+#include "third_party/skia/include/core/SkColorType.h"        // nogncheck
+#include "third_party/skia/include/core/SkImage.h"            // nogncheck
+#include "third_party/skia/include/core/SkImageInfo.h"        // nogncheck
+#include "third_party/skia/include/core/SkPicture.h"          // nogncheck
+#include "third_party/skia/include/core/SkPictureRecorder.h"  // nogncheck
+#include "third_party/skia/include/core/SkRefCnt.h"           // nogncheck
+#include "third_party/skia/include/core/SkSize.h"             // nogncheck
+#include "third_party/skia/include/core/SkSurface.h"          // nogncheck
+#endif  // defined(_SKIA_SUPPORT_)
+
+using pdfium::ManyRectanglesChecksum;
 
 namespace {
 
-#if defined(OS_WIN)
+constexpr char kFirstAlternate[] = "FirstAlternate";
+constexpr char kLastAlternate[] = "LastAlternate";
+
+#if BUILDFLAG(IS_WIN)
 const char kExpectedRectanglePostScript[] = R"(
 save
 /im/initmatrix load def
@@ -68,7 +94,7 @@
 
 restore
 )";
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 class MockDownloadHints final : public FX_DOWNLOADHINTS {
  public:
@@ -80,9 +106,51 @@
     FX_DOWNLOADHINTS::AddSegment = SAddSegment;
   }
 
-  ~MockDownloadHints() {}
+  ~MockDownloadHints() = default;
 };
 
+#if defined(_SKIA_SUPPORT_)
+ScopedFPDFBitmap SkImageToPdfiumBitmap(const SkImage& image) {
+  ScopedFPDFBitmap bitmap(
+      FPDFBitmap_Create(image.width(), image.height(), /*alpha=*/1));
+  if (!bitmap) {
+    ADD_FAILURE() << "Could not create FPDF_BITMAP";
+    return nullptr;
+  }
+
+  if (!image.readPixels(/*context=*/nullptr,
+                        image.imageInfo().makeColorType(kBGRA_8888_SkColorType),
+                        FPDFBitmap_GetBuffer(bitmap.get()),
+                        FPDFBitmap_GetStride(bitmap.get()),
+                        /*srcX=*/0, /*srcY=*/0)) {
+    ADD_FAILURE() << "Could not read pixels from SkImage";
+    return nullptr;
+  }
+
+  return bitmap;
+}
+
+ScopedFPDFBitmap SkPictureToPdfiumBitmap(sk_sp<SkPicture> picture,
+                                         const SkISize& size) {
+  sk_sp<SkSurface> surface =
+      SkSurfaces::Raster(SkImageInfo::MakeN32Premul(size));
+  if (!surface) {
+    ADD_FAILURE() << "Could not create SkSurface";
+    return nullptr;
+  }
+
+  surface->getCanvas()->clear(SK_ColorWHITE);
+  surface->getCanvas()->drawPicture(picture);
+  sk_sp<SkImage> image = surface->makeImageSnapshot();
+  if (!image) {
+    ADD_FAILURE() << "Could not snapshot SkSurface";
+    return nullptr;
+  }
+
+  return SkImageToPdfiumBitmap(*image);
+}
+#endif  // defined(_SKIA_SUPPORT_)
+
 }  // namespace
 
 TEST(fpdf, CApiTest) {
@@ -96,17 +164,17 @@
                                       int bitmap_height,
                                       const FS_MATRIX& matrix,
                                       const FS_RECTF& rect,
-                                      const char* expected_md5) {
+                                      const char* expected_checksum) {
     ScopedFPDFBitmap bitmap(FPDFBitmap_Create(bitmap_width, bitmap_height, 0));
     FPDFBitmap_FillRect(bitmap.get(), 0, 0, bitmap_width, bitmap_height,
                         0xFFFFFFFF);
     FPDF_RenderPageBitmapWithMatrix(bitmap.get(), page, &matrix, &rect, 0);
-    CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, expected_md5);
+    CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, expected_checksum);
   }
 
   void TestRenderPageBitmapWithFlags(FPDF_PAGE page,
                                      int flags,
-                                     const char* expected_md5) {
+                                     const char* expected_checksum) {
     int bitmap_width = static_cast<int>(FPDF_GetPageWidth(page));
     int bitmap_height = static_cast<int>(FPDF_GetPageHeight(page));
     ScopedFPDFBitmap bitmap(FPDFBitmap_Create(bitmap_width, bitmap_height, 0));
@@ -114,35 +182,109 @@
                         0xFFFFFFFF);
     FPDF_RenderPageBitmap(bitmap.get(), page, 0, 0, bitmap_width, bitmap_height,
                           0, flags);
-    CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, expected_md5);
+    CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, expected_checksum);
   }
 
-  void TestRenderPageBitmapWithExternalMemory(FPDF_PAGE page,
+  void TestRenderPageBitmapWithInternalMemory(FPDF_PAGE page,
                                               int format,
-                                              const char* expected_md5) {
+                                              const char* expected_checksum) {
+    TestRenderPageBitmapWithInternalMemoryAndStride(
+        page, format, /*bitmap_stride=*/0, expected_checksum);
+  }
+
+  void TestRenderPageBitmapWithInternalMemoryAndStride(
+      FPDF_PAGE page,
+      int format,
+      int bitmap_stride,
+      const char* expected_checksum) {
     int bitmap_width = static_cast<int>(FPDF_GetPageWidth(page));
     int bitmap_height = static_cast<int>(FPDF_GetPageHeight(page));
     int bytes_per_pixel = BytesPerPixelForFormat(format);
     ASSERT_NE(0, bytes_per_pixel);
 
+    ScopedFPDFBitmap bitmap(FPDFBitmap_CreateEx(
+        bitmap_width, bitmap_height, format, nullptr, bitmap_stride));
+    RenderPageToBitmapAndCheck(page, bitmap.get(), expected_checksum);
+  }
+
+  void TestRenderPageBitmapWithExternalMemory(FPDF_PAGE page,
+                                              int format,
+                                              const char* expected_checksum) {
+    int bitmap_width = static_cast<int>(FPDF_GetPageWidth(page));
+    int bytes_per_pixel = BytesPerPixelForFormat(format);
+    ASSERT_NE(0, bytes_per_pixel);
+
     int bitmap_stride = bytes_per_pixel * bitmap_width;
+    return TestRenderPageBitmapWithExternalMemoryImpl(
+        page, format, bitmap_stride, expected_checksum);
+  }
+
+  void TestRenderPageBitmapWithExternalMemoryAndNoStride(
+      FPDF_PAGE page,
+      int format,
+      const char* expected_checksum) {
+    return TestRenderPageBitmapWithExternalMemoryImpl(
+        page, format, /*bitmap_stride=*/0, expected_checksum);
+  }
+
+#if defined(_SKIA_SUPPORT_)
+  void TestRenderPageSkp(FPDF_PAGE page, const char* expected_checksum) {
+    int width = static_cast<int>(FPDF_GetPageWidth(page));
+    int height = static_cast<int>(FPDF_GetPageHeight(page));
+
+    sk_sp<SkPicture> picture;
+    {
+      auto recorder = std::make_unique<SkPictureRecorder>();
+      recorder->beginRecording(width, height);
+
+      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));
+    CompareBitmap(bitmap.get(), width, height, expected_checksum);
+  }
+#endif  // defined(_SKIA_SUPPORT_)
+
+ private:
+  void TestRenderPageBitmapWithExternalMemoryImpl(
+      FPDF_PAGE page,
+      int format,
+      int bitmap_stride,
+      const char* expected_checksum) {
+    int bitmap_width = static_cast<int>(FPDF_GetPageWidth(page));
+    int bitmap_height = static_cast<int>(FPDF_GetPageHeight(page));
+
     std::vector<uint8_t> external_memory(bitmap_stride * bitmap_height);
     ScopedFPDFBitmap bitmap(FPDFBitmap_CreateEx(bitmap_width, bitmap_height,
                                                 format, external_memory.data(),
                                                 bitmap_stride));
-    FPDFBitmap_FillRect(bitmap.get(), 0, 0, bitmap_width, bitmap_height,
-                        0xFFFFFFFF);
-    FPDF_RenderPageBitmap(bitmap.get(), page, 0, 0, bitmap_width, bitmap_height,
-                          0, 0);
-    CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, expected_md5);
+    RenderPageToBitmapAndCheck(page, bitmap.get(), expected_checksum);
+  }
+
+  void RenderPageToBitmapAndCheck(FPDF_PAGE page,
+                                  FPDF_BITMAP bitmap,
+                                  const char* expected_checksum) {
+    int bitmap_width = FPDFBitmap_GetWidth(bitmap);
+    int bitmap_height = FPDFBitmap_GetHeight(bitmap);
+    EXPECT_EQ(bitmap_width, static_cast<int>(FPDF_GetPageWidth(page)));
+    EXPECT_EQ(bitmap_height, static_cast<int>(FPDF_GetPageHeight(page)));
+    FPDFBitmap_FillRect(bitmap, 0, 0, bitmap_width, bitmap_height, 0xFFFFFFFF);
+    FPDF_RenderPageBitmap(bitmap, page, 0, 0, bitmap_width, bitmap_height, 0,
+                          0);
+    CompareBitmap(bitmap, bitmap_width, bitmap_height, expected_checksum);
   }
 };
 
 // Test for conversion of a point in device coordinates to page coordinates
 TEST_F(FPDFViewEmbedderTest, DeviceCoordinatesToPageCoordinates) {
-  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
+  ASSERT_TRUE(OpenDocument("about_blank.pdf"));
   FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
+  EXPECT_TRUE(page);
 
   // Error tolerance for floating point comparison
   const double kTolerance = 0.0001;
@@ -223,9 +365,9 @@
 
 // Test for conversion of a point in page coordinates to device coordinates.
 TEST_F(FPDFViewEmbedderTest, PageCoordinatesToDeviceCoordinates) {
-  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
+  ASSERT_TRUE(OpenDocument("about_blank.pdf"));
   FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
+  EXPECT_TRUE(page);
 
   // Display bounds in device coordinates
   int start_x = 0;
@@ -303,20 +445,38 @@
 }
 
 TEST_F(FPDFViewEmbedderTest, MultipleInitDestroy) {
-  FPDF_InitLibrary();  // Redundant given call in SetUp(), but safe.
+  FPDF_InitLibrary();  // Redundant given SetUp() in environment, but safe.
   FPDF_InitLibrary();  // Doubly-redundant even, but safe.
 
-  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
+  ASSERT_TRUE(OpenDocument("about_blank.pdf"));
   CloseDocument();
   CloseDocument();  // Redundant given above, but safe.
   CloseDocument();  // Doubly-redundant even, but safe.
 
-  FPDF_DestroyLibrary();  // Doubly-redundant even, but safe.
-  FPDF_DestroyLibrary();  // Redundant given call in TearDown(), but safe.
+  FPDF_DestroyLibrary();  // Doubly-Redundant even, but safe.
+  FPDF_DestroyLibrary();  // Redundant given call to TearDown(), but safe.
+
+  EmbedderTestEnvironment::GetInstance()->TearDown();
+  EmbedderTestEnvironment::GetInstance()->SetUp();
+}
+
+TEST_F(FPDFViewEmbedderTest, RepeatedInitDestroy) {
+  for (int i = 0; i < 3; ++i) {
+    if (!OpenDocument("about_blank.pdf"))
+      ADD_FAILURE();
+    CloseDocument();
+
+    FPDF_DestroyLibrary();
+    FPDF_InitLibrary();
+  }
+
+  // Puts the test environment back the way it was.
+  EmbedderTestEnvironment::GetInstance()->TearDown();
+  EmbedderTestEnvironment::GetInstance()->SetUp();
 }
 
 TEST_F(FPDFViewEmbedderTest, Document) {
-  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
+  ASSERT_TRUE(OpenDocument("about_blank.pdf"));
   EXPECT_EQ(1, GetPageCount());
   EXPECT_EQ(0, GetFirstPageNum());
 
@@ -326,6 +486,37 @@
 
   EXPECT_EQ(0xFFFFFFFF, FPDF_GetDocPermissions(document()));
   EXPECT_EQ(-1, FPDF_GetSecurityHandlerRevision(document()));
+  CloseDocument();
+
+  // Safe to open again and do the same things all over again.
+  ASSERT_TRUE(OpenDocument("about_blank.pdf"));
+  EXPECT_EQ(1, GetPageCount());
+  EXPECT_EQ(0, GetFirstPageNum());
+
+  version = 42;
+  EXPECT_TRUE(FPDF_GetFileVersion(document(), &version));
+  EXPECT_EQ(14, version);
+
+  EXPECT_EQ(0xFFFFFFFF, FPDF_GetDocPermissions(document()));
+  EXPECT_EQ(-1, FPDF_GetSecurityHandlerRevision(document()));
+  // CloseDocument() called by TearDown().
+}
+
+TEST_F(FPDFViewEmbedderTest, LoadDocument64) {
+  std::string file_path;
+  ASSERT_TRUE(PathService::GetTestFilePath("about_blank.pdf", &file_path));
+
+  size_t file_length = 0;
+  std::unique_ptr<char, pdfium::FreeDeleter> file_contents =
+      GetFileContents(file_path.c_str(), &file_length);
+  DCHECK(file_contents);
+  ScopedFPDFDocument doc(
+      FPDF_LoadMemDocument64(file_contents.get(), file_length, nullptr));
+  ASSERT_TRUE(doc);
+
+  int version;
+  EXPECT_TRUE(FPDF_GetFileVersion(doc.get(), &version));
+  EXPECT_EQ(14, version);
 }
 
 TEST_F(FPDFViewEmbedderTest, LoadNonexistentDocument) {
@@ -334,9 +525,19 @@
   EXPECT_EQ(static_cast<int>(FPDF_GetLastError()), FPDF_ERR_FILE);
 }
 
+TEST_F(FPDFViewEmbedderTest, DocumentWithNoPageCount) {
+  ASSERT_TRUE(OpenDocument("no_page_count.pdf"));
+  ASSERT_EQ(6, FPDF_GetPageCount(document()));
+}
+
+TEST_F(FPDFViewEmbedderTest, DocumentWithEmptyPageTreeNode) {
+  ASSERT_TRUE(OpenDocument("page_tree_empty_node.pdf"));
+  ASSERT_EQ(2, FPDF_GetPageCount(document()));
+}
+
 // See https://crbug.com/pdfium/465
 TEST_F(FPDFViewEmbedderTest, EmptyDocument) {
-  EXPECT_TRUE(CreateEmptyDocument());
+  CreateEmptyDocument();
   {
     int version = 42;
     EXPECT_FALSE(FPDF_GetFileVersion(document(), &version));
@@ -366,14 +567,14 @@
   uint16_t buf[200];
   unsigned long len;
 
-  ASSERT_TRUE(CreateEmptyDocument());
+  CreateEmptyDocument();
   len = FPDF_GetMetaText(document(), "CreationDate", buf, sizeof(buf));
   EXPECT_GT(len, 2u);  // Not just "double NUL" end-of-string indicator.
   EXPECT_NE(0u, buf[0]);
   CloseDocument();
 
   FPDF_SetSandBoxPolicy(FPDF_POLICY_MACHINETIME_ACCESS, false);
-  ASSERT_TRUE(CreateEmptyDocument());
+  CreateEmptyDocument();
   len = FPDF_GetMetaText(document(), "CreationDate", buf, sizeof(buf));
   EXPECT_EQ(2u, len);  // Only a "double NUL" end-of-string indicator.
   EXPECT_EQ(0u, buf[0]);
@@ -381,14 +582,14 @@
 
   constexpr unsigned long kNoSuchPolicy = 102;
   FPDF_SetSandBoxPolicy(kNoSuchPolicy, true);
-  ASSERT_TRUE(CreateEmptyDocument());
+  CreateEmptyDocument();
   len = FPDF_GetMetaText(document(), "CreationDate", buf, sizeof(buf));
   EXPECT_EQ(2u, len);  // Only a "double NUL" end-of-string indicator.
   EXPECT_EQ(0u, buf[0]);
   CloseDocument();
 
   FPDF_SetSandBoxPolicy(FPDF_POLICY_MACHINETIME_ACCESS, true);
-  ASSERT_TRUE(CreateEmptyDocument());
+  CreateEmptyDocument();
   len = FPDF_GetMetaText(document(), "CreationDate", buf, sizeof(buf));
   EXPECT_GT(len, 2u);  // Not just "double NUL" end-of-string indicator.
   EXPECT_NE(0u, buf[0]);
@@ -396,7 +597,7 @@
 }
 
 TEST_F(FPDFViewEmbedderTest, LinearizedDocument) {
-  EXPECT_TRUE(OpenDocumentLinearized("feature_linearized_loading.pdf"));
+  ASSERT_TRUE(OpenDocumentLinearized("feature_linearized_loading.pdf"));
   int version;
   EXPECT_TRUE(FPDF_GetFileVersion(document(), &version));
   EXPECT_EQ(16, version);
@@ -438,7 +639,7 @@
 }
 
 TEST_F(FPDFViewEmbedderTest, Page) {
-  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
+  ASSERT_TRUE(OpenDocument("about_blank.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
 
@@ -463,7 +664,7 @@
 }
 
 TEST_F(FPDFViewEmbedderTest, ViewerRefDummy) {
-  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
+  ASSERT_TRUE(OpenDocument("about_blank.pdf"));
   EXPECT_TRUE(FPDF_VIEWERREF_GetPrintScaling(document()));
   EXPECT_EQ(1, FPDF_VIEWERREF_GetNumCopies(document()));
   EXPECT_EQ(DuplexUndefined, FPDF_VIEWERREF_GetDuplex(document()));
@@ -480,7 +681,7 @@
 }
 
 TEST_F(FPDFViewEmbedderTest, ViewerRef) {
-  EXPECT_TRUE(OpenDocument("viewer_ref.pdf"));
+  ASSERT_TRUE(OpenDocument("viewer_ref.pdf"));
   EXPECT_TRUE(FPDF_VIEWERREF_GetPrintScaling(document()));
   EXPECT_EQ(5, FPDF_VIEWERREF_GetNumCopies(document()));
   EXPECT_EQ(DuplexUndefined, FPDF_VIEWERREF_GetDuplex(document()));
@@ -528,7 +729,9 @@
 }
 
 TEST_F(FPDFViewEmbedderTest, NamedDests) {
-  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
+  ASSERT_TRUE(OpenDocument("named_dests.pdf"));
+  EXPECT_EQ(6u, FPDF_CountNamedDests(document()));
+
   long buffer_size;
   char fixed_buffer[512];
   FPDF_DEST dest;
@@ -536,39 +739,39 @@
   // Query the size of the first item.
   buffer_size = 2000000;  // Absurdly large, check not used for this case.
   dest = FPDF_GetNamedDest(document(), 0, nullptr, &buffer_size);
-  EXPECT_NE(nullptr, dest);
+  EXPECT_TRUE(dest);
   EXPECT_EQ(12, buffer_size);
 
   // Try to retrieve the first item with too small a buffer.
   buffer_size = 10;
   dest = FPDF_GetNamedDest(document(), 0, fixed_buffer, &buffer_size);
-  EXPECT_NE(nullptr, dest);
+  EXPECT_TRUE(dest);
   EXPECT_EQ(-1, buffer_size);
 
   // Try to retrieve the first item with correctly sized buffer. Item is
   // taken from Dests NameTree in named_dests.pdf.
   buffer_size = 12;
   dest = FPDF_GetNamedDest(document(), 0, fixed_buffer, &buffer_size);
-  EXPECT_NE(nullptr, dest);
+  EXPECT_TRUE(dest);
   EXPECT_EQ(12, buffer_size);
-  EXPECT_EQ(std::string("F\0i\0r\0s\0t\0\0\0", 12),
-            std::string(fixed_buffer, buffer_size));
+  EXPECT_EQ("First",
+            GetPlatformString(reinterpret_cast<FPDF_WIDESTRING>(fixed_buffer)));
 
   // Try to retrieve the second item with ample buffer. Item is taken
   // from Dests NameTree but has a sub-dictionary in named_dests.pdf.
   buffer_size = sizeof(fixed_buffer);
   dest = FPDF_GetNamedDest(document(), 1, fixed_buffer, &buffer_size);
-  EXPECT_NE(nullptr, dest);
+  EXPECT_TRUE(dest);
   EXPECT_EQ(10, buffer_size);
-  EXPECT_EQ(std::string("N\0e\0x\0t\0\0\0", 10),
-            std::string(fixed_buffer, buffer_size));
+  EXPECT_EQ("Next",
+            GetPlatformString(reinterpret_cast<FPDF_WIDESTRING>(fixed_buffer)));
 
   // Try to retrieve third item with ample buffer. Item is taken
   // from Dests NameTree but has a bad sub-dictionary in named_dests.pdf.
   // in named_dests.pdf).
   buffer_size = sizeof(fixed_buffer);
   dest = FPDF_GetNamedDest(document(), 2, fixed_buffer, &buffer_size);
-  EXPECT_EQ(nullptr, dest);
+  EXPECT_FALSE(dest);
   EXPECT_EQ(sizeof(fixed_buffer),
             static_cast<size_t>(buffer_size));  // unmodified.
 
@@ -576,7 +779,7 @@
   // from Dests NameTree but has a vale of the wrong type in named_dests.pdf.
   buffer_size = sizeof(fixed_buffer);
   dest = FPDF_GetNamedDest(document(), 3, fixed_buffer, &buffer_size);
-  EXPECT_EQ(nullptr, dest);
+  EXPECT_FALSE(dest);
   EXPECT_EQ(sizeof(fixed_buffer),
             static_cast<size_t>(buffer_size));  // unmodified.
 
@@ -584,25 +787,25 @@
   // old-style Dests dictionary object in named_dests.pdf.
   buffer_size = sizeof(fixed_buffer);
   dest = FPDF_GetNamedDest(document(), 4, fixed_buffer, &buffer_size);
-  EXPECT_NE(nullptr, dest);
+  EXPECT_TRUE(dest);
   EXPECT_EQ(30, buffer_size);
-  EXPECT_EQ(std::string("F\0i\0r\0s\0t\0A\0l\0t\0e\0r\0n\0a\0t\0e\0\0\0", 30),
-            std::string(fixed_buffer, buffer_size));
+  EXPECT_EQ(kFirstAlternate,
+            GetPlatformString(reinterpret_cast<FPDF_WIDESTRING>(fixed_buffer)));
 
   // Try to retrieve sixth item with ample buffer. Item istaken from the
   // old-style Dests dictionary object but has a sub-dictionary in
   // named_dests.pdf.
   buffer_size = sizeof(fixed_buffer);
   dest = FPDF_GetNamedDest(document(), 5, fixed_buffer, &buffer_size);
-  EXPECT_NE(nullptr, dest);
+  EXPECT_TRUE(dest);
   EXPECT_EQ(28, buffer_size);
-  EXPECT_EQ(std::string("L\0a\0s\0t\0A\0l\0t\0e\0r\0n\0a\0t\0e\0\0\0", 28),
-            std::string(fixed_buffer, buffer_size));
+  EXPECT_EQ(kLastAlternate,
+            GetPlatformString(reinterpret_cast<FPDF_WIDESTRING>(fixed_buffer)));
 
   // Try to retrieve non-existent item with ample buffer.
   buffer_size = sizeof(fixed_buffer);
   dest = FPDF_GetNamedDest(document(), 6, fixed_buffer, &buffer_size);
-  EXPECT_EQ(nullptr, dest);
+  EXPECT_FALSE(dest);
   EXPECT_EQ(sizeof(fixed_buffer),
             static_cast<size_t>(buffer_size));  // unmodified.
 
@@ -610,38 +813,38 @@
   buffer_size = sizeof(fixed_buffer);
   dest = FPDF_GetNamedDest(document(), std::numeric_limits<int>::max(),
                            fixed_buffer, &buffer_size);
-  EXPECT_EQ(nullptr, dest);
+  EXPECT_FALSE(dest);
   EXPECT_EQ(sizeof(fixed_buffer),
             static_cast<size_t>(buffer_size));  // unmodified.
 
   buffer_size = sizeof(fixed_buffer);
   dest = FPDF_GetNamedDest(document(), std::numeric_limits<int>::min(),
                            fixed_buffer, &buffer_size);
-  EXPECT_EQ(nullptr, dest);
+  EXPECT_FALSE(dest);
   EXPECT_EQ(sizeof(fixed_buffer),
             static_cast<size_t>(buffer_size));  // unmodified.
 
   buffer_size = sizeof(fixed_buffer);
   dest = FPDF_GetNamedDest(document(), -1, fixed_buffer, &buffer_size);
-  EXPECT_EQ(nullptr, dest);
+  EXPECT_FALSE(dest);
   EXPECT_EQ(sizeof(fixed_buffer),
             static_cast<size_t>(buffer_size));  // unmodified.
 }
 
 TEST_F(FPDFViewEmbedderTest, NamedDestsByName) {
-  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
+  ASSERT_TRUE(OpenDocument("named_dests.pdf"));
 
   // Null pointer returns nullptr.
   FPDF_DEST dest = FPDF_GetNamedDestByName(document(), nullptr);
-  EXPECT_EQ(nullptr, dest);
+  EXPECT_FALSE(dest);
 
   // Empty string returns nullptr.
   dest = FPDF_GetNamedDestByName(document(), "");
-  EXPECT_EQ(nullptr, dest);
+  EXPECT_FALSE(dest);
 
   // Item from Dests NameTree.
   dest = FPDF_GetNamedDestByName(document(), "First");
-  EXPECT_NE(nullptr, dest);
+  EXPECT_TRUE(dest);
 
   long ignore_len = 0;
   FPDF_DEST dest_by_index =
@@ -649,8 +852,8 @@
   EXPECT_EQ(dest_by_index, dest);
 
   // Item from Dests dictionary.
-  dest = FPDF_GetNamedDestByName(document(), "FirstAlternate");
-  EXPECT_NE(nullptr, dest);
+  dest = FPDF_GetNamedDestByName(document(), kFirstAlternate);
+  EXPECT_TRUE(dest);
 
   ignore_len = 0;
   dest_by_index = FPDF_GetNamedDest(document(), 4, nullptr, &ignore_len);
@@ -658,16 +861,53 @@
 
   // Bad value type for item from Dests NameTree array.
   dest = FPDF_GetNamedDestByName(document(), "WrongType");
-  EXPECT_EQ(nullptr, dest);
+  EXPECT_FALSE(dest);
 
   // No such destination in either Dest NameTree or dictionary.
   dest = FPDF_GetNamedDestByName(document(), "Bogus");
-  EXPECT_EQ(nullptr, dest);
+  EXPECT_FALSE(dest);
+}
+
+TEST_F(FPDFViewEmbedderTest, NamedDestsOldStyle) {
+  ASSERT_TRUE(OpenDocument("named_dests_old_style.pdf"));
+  EXPECT_EQ(2u, FPDF_CountNamedDests(document()));
+
+  // Test bad parameters.
+  EXPECT_FALSE(FPDF_GetNamedDestByName(document(), nullptr));
+  EXPECT_FALSE(FPDF_GetNamedDestByName(document(), ""));
+  EXPECT_FALSE(FPDF_GetNamedDestByName(document(), "NoSuchName"));
+
+  // These should return a valid destination.
+  EXPECT_TRUE(FPDF_GetNamedDestByName(document(), kFirstAlternate));
+  EXPECT_TRUE(FPDF_GetNamedDestByName(document(), kLastAlternate));
+
+  char buffer[512];
+  constexpr long kBufferSize = sizeof(buffer);
+  long size = kBufferSize;
+
+  // Test bad indices.
+  EXPECT_FALSE(FPDF_GetNamedDest(document(), -1, buffer, &size));
+  EXPECT_EQ(kBufferSize, size);
+  size = kBufferSize;
+  EXPECT_FALSE(FPDF_GetNamedDest(document(), 2, buffer, &size));
+  EXPECT_EQ(kBufferSize, size);
+
+  // These should return a valid destination.
+  size = kBufferSize;
+  ASSERT_TRUE(FPDF_GetNamedDest(document(), 0, buffer, &size));
+  ASSERT_EQ(static_cast<int>(sizeof(kFirstAlternate) * 2), size);
+  EXPECT_EQ(kFirstAlternate,
+            GetPlatformString(reinterpret_cast<FPDF_WIDESTRING>(buffer)));
+  size = kBufferSize;
+  ASSERT_TRUE(FPDF_GetNamedDest(document(), 1, buffer, &size));
+  ASSERT_EQ(static_cast<int>(sizeof(kLastAlternate) * 2), size);
+  EXPECT_EQ(kLastAlternate,
+            GetPlatformString(reinterpret_cast<FPDF_WIDESTRING>(buffer)));
 }
 
 // The following tests pass if the document opens without crashing.
 TEST_F(FPDFViewEmbedderTest, Crasher_113) {
-  EXPECT_TRUE(OpenDocument("bug_113.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_113.pdf"));
 }
 
 TEST_F(FPDFViewEmbedderTest, Crasher_451830) {
@@ -676,9 +916,9 @@
 }
 
 TEST_F(FPDFViewEmbedderTest, Crasher_452455) {
-  EXPECT_TRUE(OpenDocument("bug_452455.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_452455.pdf"));
   FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
+  EXPECT_TRUE(page);
   UnloadPage(page);
 }
 
@@ -688,13 +928,13 @@
 }
 
 TEST_F(FPDFViewEmbedderTest, Crasher_572871) {
-  EXPECT_TRUE(OpenDocument("bug_572871.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_572871.pdf"));
 }
 
 // It tests that document can still be loaded even the trailer has no 'Size'
 // field if other information is right.
 TEST_F(FPDFViewEmbedderTest, Failed_213) {
-  EXPECT_TRUE(OpenDocument("bug_213.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_213.pdf"));
 }
 
 // The following tests pass if the document opens without infinite looping.
@@ -703,7 +943,7 @@
 }
 
 TEST_F(FPDFViewEmbedderTest, Crasher_773229) {
-  EXPECT_TRUE(OpenDocument("bug_773229.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_773229.pdf"));
 }
 
 // Test if the document opens without infinite looping.
@@ -711,14 +951,14 @@
 // the fix, LoadAllCrossRefV4 will return false after detecting a cross
 // reference loop. Cross references will be rebuilt successfully.
 TEST_F(FPDFViewEmbedderTest, CrossRefV4Loop) {
-  EXPECT_TRUE(OpenDocument("bug_xrefv4_loop.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_xrefv4_loop.pdf"));
   MockDownloadHints hints;
 
   // Make sure calling FPDFAvail_IsDocAvail() on this file does not infinite
   // loop either. See bug 875.
   int ret = PDF_DATA_NOTAVAIL;
   while (ret == PDF_DATA_NOTAVAIL)
-    ret = FPDFAvail_IsDocAvail(avail_, &hints);
+    ret = FPDFAvail_IsDocAvail(avail(), &hints);
   EXPECT_EQ(PDF_DATA_AVAIL, ret);
 }
 
@@ -747,47 +987,85 @@
 // Deliberately damaged version of linearized.pdf with bad data in the shared
 // object hint table.
 TEST_F(FPDFViewEmbedderTest, Hang_1055) {
-  EXPECT_TRUE(OpenDocumentLinearized("linearized_bug_1055.pdf"));
+  ASSERT_TRUE(OpenDocumentLinearized("linearized_bug_1055.pdf"));
   int version;
   EXPECT_TRUE(FPDF_GetFileVersion(document(), &version));
   EXPECT_EQ(16, version);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_FPDF_RenderPageBitmapWithMatrix \
-  DISABLED_FPDF_RenderPageBitmapWithMatrix
-#else
-#define MAYBE_FPDF_RenderPageBitmapWithMatrix FPDF_RenderPageBitmapWithMatrix
-#endif
-TEST_F(FPDFViewEmbedderTest, MAYBE_FPDF_RenderPageBitmapWithMatrix) {
-  const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe";
-  const char kClippedMD5[] = "a84cab93c102b9b9290fba3047ba702c";
-  const char kTopLeftQuarterMD5[] = "f11a11137c8834389e31cf555a4a6979";
-  const char kHoriStretchedMD5[] = "48ef9205941ed19691ccfa00d717187e";
-  const char kRotated90ClockwiseMD5[] = "d8da2c7bf77521550d0f2752b9cf3482";
-  const char kRotated180ClockwiseMD5[] = "0113386bb0bd45125bacc6dee78bfe78";
-  const char kRotated270ClockwiseMD5[] = "a287e0f74ce203699cda89f9cc97a240";
-  const char kMirrorHoriMD5[] = "6e8d7a6fde39d8e720fb9e620102918c";
-  const char kMirrorVertMD5[] = "8f3a555ef9c0d5031831ae3715273707";
-  const char kLargerTopLeftQuarterMD5[] = "172a2f4adafbadbe98017b1c025b9e27";
-  const char kLargerMD5[] = "c806145641c3e6fc4e022c7065343749";
-  const char kLargerClippedMD5[] = "091d3b1c7933c8f6945eb2cb41e588e9";
-  const char kLargerRotatedMD5[] = "115f13353ebfc82ddb392d1f0059eb12";
-  const char kLargerRotatedLandscapeMD5[] = "c901239d17d84ac84cb6f2124da71b0d";
-  const char kLargerRotatedDiagonalMD5[] = "3d62417468bdaff0eb14391a0c30a3b1";
-  const char kTileMD5[] = "0a190003c97220bf8877684c8d7e89cf";
+TEST_F(FPDFViewEmbedderTest, FPDF_RenderPageBitmapWithMatrix) {
+  const char* clipped_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "d2929fae285593cd1c1d446750d47d60";
+    return "a84cab93c102b9b9290fba3047ba702c";
+  }();
+  const char* top_left_quarter_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "31d24d8c6a2bac380b2f5c393e77ecc9";
+    return "f11a11137c8834389e31cf555a4a6979";
+  }();
+  const char* hori_stretched_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "6d3776d7bb21cbb7195126b8e95dfba2";
+    return "48ef9205941ed19691ccfa00d717187e";
+  }();
+  const char* rotated_90_clockwise_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "b4baa001d201baed576cd6d5d0d5a160";
+    return "d8da2c7bf77521550d0f2752b9cf3482";
+  }();
+  const char* rotated_180_clockwise_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "51819227d0863222aed366d5d7c5d9c8";
+    return "0113386bb0bd45125bacc6dee78bfe78";
+  }();
+  const char* rotated_270_clockwise_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "f2b046e46c2751cebc777a9725ae2f3e";
+    return "a287e0f74ce203699cda89f9cc97a240";
+  }();
+  const char* mirror_hori_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "c7fbec322b4fc6bcf46ec1eb89661c41";
+    return "6e8d7a6fde39d8e720fb9e620102918c";
+  }();
+  const char* mirror_vert_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "a8b00bc40677a73c15a08b9769d1b576";
+    return "8f3a555ef9c0d5031831ae3715273707";
+  }();
+  const char* larger_top_left_quarter_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "35deb5ed4b73675ce33f68328a33c687";
+    return "172a2f4adafbadbe98017b1c025b9e27";
+  }();
+  const char* larger_rotated_diagonal_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "85c41bb892c1a09882f432aa2f4a5ef6";
+    return "3d62417468bdaff0eb14391a0c30a3b1";
+  }();
+  const char* tile_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "387be3a84774f39aaa955314d2fe7106";
+    return "0a190003c97220bf8877684c8d7e89cf";
+  }();
+  const char kLargerChecksum[] = "c806145641c3e6fc4e022c7065343749";
+  const char kLargerClippedChecksum[] = "091d3b1c7933c8f6945eb2cb41e588e9";
+  const char kLargerRotatedChecksum[] = "115f13353ebfc82ddb392d1f0059eb12";
+  const char kLargerRotatedLandscapeChecksum[] =
+      "c901239d17d84ac84cb6f2124da71b0d";
 
-  EXPECT_TRUE(OpenDocument("rectangles.pdf"));
+  ASSERT_TRUE(OpenDocument("rectangles.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
-  const int page_width = static_cast<int>(FPDF_GetPageWidthF(page));
-  const int page_height = static_cast<int>(FPDF_GetPageHeightF(page));
-  EXPECT_EQ(200, page_width);
-  EXPECT_EQ(300, page_height);
+  const float page_width = FPDF_GetPageWidthF(page);
+  const float page_height = FPDF_GetPageHeightF(page);
+  EXPECT_FLOAT_EQ(200, page_width);
+  EXPECT_FLOAT_EQ(300, page_height);
 
+  using pdfium::RectanglesChecksum;
   ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-  CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+  CompareBitmap(bitmap.get(), page_width, page_height, RectanglesChecksum());
 
   FS_RECTF page_rect{0, 0, page_width, page_height};
 
@@ -795,89 +1073,92 @@
   // the RenderLoadedPage() output.
   FS_MATRIX identity_matrix{1, 0, 0, 1, 0, 0};
   TestRenderPageBitmapWithMatrix(page, page_width, page_height, identity_matrix,
-                                 page_rect, kOriginalMD5);
+                                 page_rect, RectanglesChecksum());
 
   // Again render with an identity matrix but with a smaller clipping rect.
   FS_RECTF middle_of_page_rect{page_width / 4, page_height / 4,
                                page_width * 3 / 4, page_height * 3 / 4};
   TestRenderPageBitmapWithMatrix(page, page_width, page_height, identity_matrix,
-                                 middle_of_page_rect, kClippedMD5);
+                                 middle_of_page_rect, clipped_checksum);
 
   // Now render again with the image scaled smaller.
   FS_MATRIX half_scale_matrix{0.5, 0, 0, 0.5, 0, 0};
   TestRenderPageBitmapWithMatrix(page, page_width, page_height,
                                  half_scale_matrix, page_rect,
-                                 kTopLeftQuarterMD5);
+                                 top_left_quarter_checksum);
 
   // Now render again with the image scaled larger horizontally (the right half
   // will be clipped).
   FS_MATRIX stretch_x_matrix{2, 0, 0, 1, 0, 0};
   TestRenderPageBitmapWithMatrix(page, page_width, page_height,
                                  stretch_x_matrix, page_rect,
-                                 kHoriStretchedMD5);
+                                 hori_stretched_checksum);
 
   // Try a 90 degree rotation clockwise but with the same bitmap size, so part
   // will be clipped.
   FS_MATRIX rotate_90_matrix{0, 1, -1, 0, page_width, 0};
   TestRenderPageBitmapWithMatrix(page, page_width, page_height,
                                  rotate_90_matrix, page_rect,
-                                 kRotated90ClockwiseMD5);
+                                 rotated_90_clockwise_checksum);
 
   // 180 degree rotation clockwise.
   FS_MATRIX rotate_180_matrix{-1, 0, 0, -1, page_width, page_height};
   TestRenderPageBitmapWithMatrix(page, page_width, page_height,
                                  rotate_180_matrix, page_rect,
-                                 kRotated180ClockwiseMD5);
+                                 rotated_180_clockwise_checksum);
 
   // 270 degree rotation clockwise.
   FS_MATRIX rotate_270_matrix{0, -1, 1, 0, 0, page_width};
   TestRenderPageBitmapWithMatrix(page, page_width, page_height,
                                  rotate_270_matrix, page_rect,
-                                 kRotated270ClockwiseMD5);
+                                 rotated_270_clockwise_checksum);
 
   // Mirror horizontally.
   FS_MATRIX mirror_hori_matrix{-1, 0, 0, 1, page_width, 0};
   TestRenderPageBitmapWithMatrix(page, page_width, page_height,
-                                 mirror_hori_matrix, page_rect, kMirrorHoriMD5);
+                                 mirror_hori_matrix, page_rect,
+                                 mirror_hori_checksum);
 
   // Mirror vertically.
   FS_MATRIX mirror_vert_matrix{1, 0, 0, -1, 0, page_height};
   TestRenderPageBitmapWithMatrix(page, page_width, page_height,
-                                 mirror_vert_matrix, page_rect, kMirrorVertMD5);
+                                 mirror_vert_matrix, page_rect,
+                                 mirror_vert_checksum);
 
   // Tests rendering to a larger bitmap
-  const int bitmap_width = page_width * 2;
-  const int bitmap_height = page_height * 2;
+  const float bitmap_width = page_width * 2;
+  const float bitmap_height = page_height * 2;
 
   // Render using an identity matrix and the whole bitmap area as clipping rect.
   FS_RECTF bitmap_rect{0, 0, bitmap_width, bitmap_height};
   TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height,
                                  identity_matrix, bitmap_rect,
-                                 kLargerTopLeftQuarterMD5);
+                                 larger_top_left_quarter_checksum);
 
   // Render using a scaling matrix to fill the larger bitmap.
   FS_MATRIX double_scale_matrix{2, 0, 0, 2, 0, 0};
   TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height,
-                                 double_scale_matrix, bitmap_rect, kLargerMD5);
+                                 double_scale_matrix, bitmap_rect,
+                                 kLargerChecksum);
 
   // Render the larger image again but with clipping.
   FS_RECTF middle_of_bitmap_rect{bitmap_width / 4, bitmap_height / 4,
                                  bitmap_width * 3 / 4, bitmap_height * 3 / 4};
   TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height,
                                  double_scale_matrix, middle_of_bitmap_rect,
-                                 kLargerClippedMD5);
+                                 kLargerClippedChecksum);
 
   // On the larger bitmap, try a 90 degree rotation but with the same bitmap
   // size, so part will be clipped.
   FS_MATRIX rotate_90_scale_2_matrix{0, 2, -2, 0, bitmap_width, 0};
   TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height,
                                  rotate_90_scale_2_matrix, bitmap_rect,
-                                 kLargerRotatedMD5);
+                                 kLargerRotatedChecksum);
 
   // On the larger bitmap, apply 90 degree rotation to a bitmap with the
   // appropriate dimensions.
-  const int landscape_bitmap_width = bitmap_height;
-  const int landscape_bitmap_height = bitmap_width;
+  const float landscape_bitmap_width = bitmap_height;
+  const float landscape_bitmap_height = bitmap_width;
   FS_RECTF landscape_bitmap_rect{0, 0, landscape_bitmap_width,
                                  landscape_bitmap_height};
   FS_MATRIX landscape_rotate_90_scale_2_matrix{
@@ -885,12 +1166,13 @@
   TestRenderPageBitmapWithMatrix(
       page, landscape_bitmap_width, landscape_bitmap_height,
       landscape_rotate_90_scale_2_matrix, landscape_bitmap_rect,
-      kLargerRotatedLandscapeMD5);
+      kLargerRotatedLandscapeChecksum);
 
   // On the larger bitmap, apply 45 degree rotation to a bitmap with the
   // appropriate dimensions.
   const float sqrt2 = 1.41421356f;
-  const int diagonal_bitmap_size = ceil((bitmap_width + bitmap_height) / sqrt2);
+  const float diagonal_bitmap_size =
+      ceil((bitmap_width + bitmap_height) / sqrt2);
   FS_RECTF diagonal_bitmap_rect{0, 0, diagonal_bitmap_size,
                                 diagonal_bitmap_size};
   FS_MATRIX rotate_45_scale_2_matrix{
@@ -898,7 +1180,7 @@
   TestRenderPageBitmapWithMatrix(page, diagonal_bitmap_size,
                                  diagonal_bitmap_size, rotate_45_scale_2_matrix,
                                  diagonal_bitmap_rect,
-                                 kLargerRotatedDiagonalMD5);
+                                 larger_rotated_diagonal_checksum);
 
   // Render the (2, 1) tile of the page (third column, second row) when the page
   // is divided in 50x50 pixel tiles. The tile is scaled by a factor of 7.
@@ -906,7 +1188,7 @@
   const int tile_size = 50;
   const int tile_x = 2;
   const int tile_y = 1;
-  int tile_bitmap_size = scale * tile_size;
+  float tile_bitmap_size = scale * tile_size;
   FS_RECTF tile_bitmap_rect{0, 0, tile_bitmap_size, tile_bitmap_size};
   FS_MATRIX tile_2_1_matrix{scale,
                             0,
@@ -915,13 +1197,14 @@
                             -tile_x * tile_bitmap_size,
                             -tile_y * tile_bitmap_size};
   TestRenderPageBitmapWithMatrix(page, tile_bitmap_size, tile_bitmap_size,
-                                 tile_2_1_matrix, tile_bitmap_rect, kTileMD5);
+                                 tile_2_1_matrix, tile_bitmap_rect,
+                                 tile_checksum);
 
   UnloadPage(page);
 }
 
 TEST_F(FPDFViewEmbedderTest, FPDF_GetPageSizeByIndexF) {
-  EXPECT_TRUE(OpenDocument("rectangles.pdf"));
+  ASSERT_TRUE(OpenDocument("rectangles.pdf"));
 
   FS_SIZEF size;
   EXPECT_FALSE(FPDF_GetPageSizeByIndexF(nullptr, 0, &size));
@@ -956,7 +1239,7 @@
 }
 
 TEST_F(FPDFViewEmbedderTest, FPDF_GetPageSizeByIndex) {
-  EXPECT_TRUE(OpenDocument("rectangles.pdf"));
+  ASSERT_TRUE(OpenDocument("rectangles.pdf"));
 
   double width = 0;
   double height = 0;
@@ -993,6 +1276,96 @@
   UnloadPage(page);
 }
 
+TEST_F(FPDFViewEmbedderTest, GetXFAArrayData) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  static constexpr struct {
+    const char* name;
+    size_t content_length;
+    const char* content_checksum;
+  } kExpectedResults[]{
+      {"preamble", 124u, "71be364e53292596412242bfcdb46eab"},
+      {"config", 642u, "bcd1ca1d420ee31a561273a54a06435f"},
+      {"template", 541u, "0f48cb2fa1bb9cbf9eee802d66e81bf4"},
+      {"localeSet", 3455u, "bb1f253d3e5c719ac0da87d055bc164e"},
+      {"postamble", 11u, "6b79e25da35d86634ea27c38f64cf243"},
+  };
+
+  ASSERT_EQ(static_cast<int>(std::size(kExpectedResults)),
+            FPDF_GetXFAPacketCount(document()));
+  for (size_t i = 0; i < std::size(kExpectedResults); ++i) {
+    char name_buffer[20] = {};
+    ASSERT_EQ(strlen(kExpectedResults[i].name) + 1,
+              FPDF_GetXFAPacketName(document(), i, nullptr, 0));
+    EXPECT_EQ(
+        strlen(kExpectedResults[i].name) + 1,
+        FPDF_GetXFAPacketName(document(), i, name_buffer, sizeof(name_buffer)));
+    EXPECT_STREQ(kExpectedResults[i].name, name_buffer);
+
+    unsigned long buflen;
+    ASSERT_TRUE(FPDF_GetXFAPacketContent(document(), i, nullptr, 0, &buflen));
+    ASSERT_EQ(kExpectedResults[i].content_length, buflen);
+    std::vector<uint8_t> data_buffer(buflen);
+    EXPECT_TRUE(FPDF_GetXFAPacketContent(document(), i, data_buffer.data(),
+                                         data_buffer.size(), &buflen));
+    EXPECT_EQ(kExpectedResults[i].content_length, buflen);
+    EXPECT_STREQ(kExpectedResults[i].content_checksum,
+                 GenerateMD5Base16(data_buffer).c_str());
+  }
+
+  // Test bad parameters.
+  EXPECT_EQ(-1, FPDF_GetXFAPacketCount(nullptr));
+
+  EXPECT_EQ(0u, FPDF_GetXFAPacketName(nullptr, 0, nullptr, 0));
+  EXPECT_EQ(0u, FPDF_GetXFAPacketName(document(), -1, nullptr, 0));
+  EXPECT_EQ(0u, FPDF_GetXFAPacketName(document(), std::size(kExpectedResults),
+                                      nullptr, 0));
+
+  unsigned long buflen = 123;
+  EXPECT_FALSE(FPDF_GetXFAPacketContent(nullptr, 0, nullptr, 0, &buflen));
+  EXPECT_EQ(123u, buflen);
+  EXPECT_FALSE(FPDF_GetXFAPacketContent(document(), -1, nullptr, 0, &buflen));
+  EXPECT_EQ(123u, buflen);
+  EXPECT_FALSE(FPDF_GetXFAPacketContent(document(), std::size(kExpectedResults),
+                                        nullptr, 0, &buflen));
+  EXPECT_EQ(123u, buflen);
+  EXPECT_FALSE(FPDF_GetXFAPacketContent(document(), 0, nullptr, 0, nullptr));
+}
+
+TEST_F(FPDFViewEmbedderTest, GetXFAStreamData) {
+  ASSERT_TRUE(OpenDocument("bug_1265.pdf"));
+
+  ASSERT_EQ(1, FPDF_GetXFAPacketCount(document()));
+
+  char name_buffer[20] = {};
+  ASSERT_EQ(1u, FPDF_GetXFAPacketName(document(), 0, nullptr, 0));
+  EXPECT_EQ(1u, FPDF_GetXFAPacketName(document(), 0, name_buffer,
+                                      sizeof(name_buffer)));
+  EXPECT_STREQ("", name_buffer);
+
+  unsigned long buflen;
+  ASSERT_TRUE(FPDF_GetXFAPacketContent(document(), 0, nullptr, 0, &buflen));
+  ASSERT_EQ(121u, buflen);
+  std::vector<uint8_t> data_buffer(buflen);
+  EXPECT_TRUE(FPDF_GetXFAPacketContent(document(), 0, data_buffer.data(),
+                                       data_buffer.size(), &buflen));
+  EXPECT_EQ(121u, buflen);
+  EXPECT_STREQ("8f912eaa1e66c9341cb3032ede71e147",
+               GenerateMD5Base16(data_buffer).c_str());
+}
+
+TEST_F(FPDFViewEmbedderTest, GetXFADataForNoForm) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+
+  EXPECT_EQ(0, FPDF_GetXFAPacketCount(document()));
+}
+
+TEST_F(FPDFViewEmbedderTest, GetXFADataForAcroForm) {
+  ASSERT_TRUE(OpenDocument("text_form.pdf"));
+
+  EXPECT_EQ(0, FPDF_GetXFAPacketCount(document()));
+}
+
 class RecordUnsupportedErrorDelegate final : public EmbedderTest::Delegate {
  public:
   RecordUnsupportedErrorDelegate() = default;
@@ -1061,7 +1434,7 @@
     size_t file_length = 0;
     std::unique_ptr<char, pdfium::FreeDeleter> file_contents =
         GetFileContents(file_path.c_str(), &file_length);
-    ASSERT(file_contents);
+    DCHECK(file_contents);
     ScopedFPDFDocument doc(
         FPDF_LoadMemDocument(file_contents.get(), file_length, ""));
     ASSERT_TRUE(doc);
@@ -1069,77 +1442,272 @@
   }
 }
 
-TEST_F(FPDFViewEmbedderTest, RenderManyRectanglesWithFlags) {
-  static const char kNormalMD5[] = "b0170c575b65ecb93ebafada0ff0f038";
-  static const char kGrayscaleMD5[] = "7b553f1052069a9c61237a05db0955d6";
-  static const char kNoSmoothpathMD5[] = "ff6e5c509d1f6984bcdfd18b26a4203a";
+TEST_F(FPDFViewEmbedderTest, RenderBug664284WithNoNativeText) {
+  // For Skia, since the font used in bug_664284.pdf is not a CID font,
+  // ShouldDrawDeviceText() will always return true. Therefore
+  // FPDF_NO_NATIVETEXT and the font widths defined in the PDF determines
+  // whether to go through the rendering path in
+  // CFX_SkiaDeviceDriver::DrawDeviceText(). In this case, it returns false and
+  // affects the rendering results across all platforms.
 
-  ASSERT_TRUE(OpenDocument("many_rectangles.pdf"));
+  // For AGG, since CFX_AggDeviceDriver::DrawDeviceText() always returns false,
+  // FPDF_NO_NATIVETEXT won't affect device-specific rendering path and it will
+  // only disable native text support on macOS. Therefore Windows and Linux
+  // rendering results remain the same as rendering with no flags, while the
+  // macOS rendering result doesn't.
+
+  const char* original_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "29cb8045c21cfa2c920fdf43de70efd8";
+    }
+#if BUILDFLAG(IS_APPLE)
+    return "0e339d606aafb63077f49e238dc27cb0";
+#else
+    return "288502887ffc63291f35a0573b944375";
+#endif
+  }();
+  static const char kNoNativeTextChecksum[] =
+      "288502887ffc63291f35a0573b944375";
+  ASSERT_TRUE(OpenDocument("bug_664284.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
-  TestRenderPageBitmapWithFlags(page, 0, kNormalMD5);
-  TestRenderPageBitmapWithFlags(page, FPDF_ANNOT, kNormalMD5);
-  TestRenderPageBitmapWithFlags(page, FPDF_LCD_TEXT, kNormalMD5);
-  TestRenderPageBitmapWithFlags(page, FPDF_NO_NATIVETEXT, kNormalMD5);
-  TestRenderPageBitmapWithFlags(page, FPDF_GRAYSCALE, kGrayscaleMD5);
+  TestRenderPageBitmapWithFlags(page, 0, original_checksum);
+  TestRenderPageBitmapWithFlags(page, FPDF_NO_NATIVETEXT,
+                                kNoNativeTextChecksum);
+
+  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) {
+  // When rendering with `FPDF_REVERSE_BYTE_ORDER` flag, the blue and red
+  // channels should be reversed.
+  ASSERT_TRUE(OpenDocument("bug_1396264.pdf"));
+  ScopedFPDFPage page(FPDF_LoadPage(document(), 0));
+  ASSERT_TRUE(page);
+
+  TestRenderPageBitmapWithFlags(page.get(), 0,
+                                "81e7f4498090977c848a21b5c6510d3a");
+  TestRenderPageBitmapWithFlags(page.get(), FPDF_REVERSE_BYTE_ORDER,
+                                "505ba6d1c7f4044c11c91873452a8bde");
+}
+
+TEST_F(FPDFViewEmbedderTest, RenderJpxLzwImageWithFlags) {
+  static const char kNormalChecksum[] = "4bcd56cae1ca2622403e8af07242e71a";
+  static const char kGrayscaleChecksum[] = "fe45ad56efe868ba82285fa5ffedc0cb";
+
+  ASSERT_TRUE(OpenDocument("jpx_lzw.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  TestRenderPageBitmapWithFlags(page, 0, kNormalChecksum);
+  TestRenderPageBitmapWithFlags(page, FPDF_ANNOT, kNormalChecksum);
+  TestRenderPageBitmapWithFlags(page, FPDF_LCD_TEXT, kNormalChecksum);
+  TestRenderPageBitmapWithFlags(page, FPDF_NO_NATIVETEXT, kNormalChecksum);
+  TestRenderPageBitmapWithFlags(page, FPDF_GRAYSCALE, kGrayscaleChecksum);
   TestRenderPageBitmapWithFlags(page, FPDF_RENDER_LIMITEDIMAGECACHE,
-                                kNormalMD5);
-  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_FORCEHALFTONE, kNormalMD5);
-  TestRenderPageBitmapWithFlags(page, FPDF_PRINTING, kNormalMD5);
-  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHTEXT, kNormalMD5);
-  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHIMAGE, kNormalMD5);
+                                kNormalChecksum);
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_FORCEHALFTONE,
+                                kNormalChecksum);
+  TestRenderPageBitmapWithFlags(page, FPDF_PRINTING, kNormalChecksum);
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHTEXT,
+                                kNormalChecksum);
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHIMAGE,
+                                kNormalChecksum);
   TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHPATH,
-                                kNoSmoothpathMD5);
+                                kNormalChecksum);
 
   UnloadPage(page);
 }
 
-TEST_F(FPDFViewEmbedderTest, RenderManyRectanglesWithExternalMemory) {
-  static const char kNormalMD5[] = "b0170c575b65ecb93ebafada0ff0f038";
-  static const char kGrayMD5[] = "b561c11edc44dc3972125a9b8744fa2f";
-  static const char kBgrMD5[] = "ab6312e04c0d3f4e46fb302a45173d05";
+TEST_F(FPDFViewEmbedderTest, RenderManyRectanglesWithFlags) {
+  const char* grayscale_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "b596ac8bbe64e7bff31888ab05e4dcf4";
+    return "7b553f1052069a9c61237a05db0955d6";
+  }();
+  const char* no_smoothpath_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "4d71ed53d9f6e6a761876ebb4ff23e19";
+    return "ff6e5c509d1f6984bcdfd18b26a4203a";
+  }();
 
   ASSERT_TRUE(OpenDocument("many_rectangles.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
-  TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_Gray, kGrayMD5);
-  TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGR, kBgrMD5);
-  TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGRx, kNormalMD5);
-  TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGRA, kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, 0, ManyRectanglesChecksum());
+  TestRenderPageBitmapWithFlags(page, FPDF_ANNOT, ManyRectanglesChecksum());
+  TestRenderPageBitmapWithFlags(page, FPDF_LCD_TEXT, ManyRectanglesChecksum());
+  TestRenderPageBitmapWithFlags(page, FPDF_NO_NATIVETEXT,
+                                ManyRectanglesChecksum());
+  TestRenderPageBitmapWithFlags(page, FPDF_GRAYSCALE, grayscale_checksum);
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_LIMITEDIMAGECACHE,
+                                ManyRectanglesChecksum());
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_FORCEHALFTONE,
+                                ManyRectanglesChecksum());
+  TestRenderPageBitmapWithFlags(page, FPDF_PRINTING, ManyRectanglesChecksum());
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHTEXT,
+                                ManyRectanglesChecksum());
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHIMAGE,
+                                ManyRectanglesChecksum());
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHPATH,
+                                no_smoothpath_checksum);
+
   UnloadPage(page);
 }
 
-#if defined(OS_LINUX)
-TEST_F(FPDFViewEmbedderTest, RenderHelloWorldWithFlags) {
-  static const char kNormalMD5[] = "2baa4c0e1758deba1b9c908e1fbd04ed";
-  static const char kLcdTextMD5[] = "825e881f39e48254e64e2808987a6b8c";
-  static const char kNoSmoothtextMD5[] = "3d01e234120b783a3fffb27273ea1ea8";
+TEST_F(FPDFViewEmbedderTest, RenderManyRectanglesWithAndWithoutExternalMemory) {
+  ASSERT_TRUE(OpenDocument("many_rectangles.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
 
+  const char* bgr_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "4d52e5cc1d4a8067bf918b85b232fff0";
+    return "ab6312e04c0d3f4e46fb302a45173d05";
+  }();
+  static constexpr int kBgrStride = 600;  // Width of 200 * 24 bits per pixel.
+  TestRenderPageBitmapWithInternalMemory(page, FPDFBitmap_BGR, bgr_checksum);
+  TestRenderPageBitmapWithInternalMemoryAndStride(page, FPDFBitmap_BGR,
+                                                  kBgrStride, bgr_checksum);
+  TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGR, bgr_checksum);
+  TestRenderPageBitmapWithExternalMemoryAndNoStride(page, FPDFBitmap_BGR,
+                                                    bgr_checksum);
+
+  const char* gray_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "3dfe1fc3889123d68e1748fefac65e72";
+    return "b561c11edc44dc3972125a9b8744fa2f";
+  }();
+
+  TestRenderPageBitmapWithInternalMemory(page, FPDFBitmap_Gray, gray_checksum);
+  static constexpr int kGrayStride = 200;  // Width of 200 * 8 bits per pixel.
+  TestRenderPageBitmapWithInternalMemoryAndStride(page, FPDFBitmap_Gray,
+                                                  kGrayStride, gray_checksum);
+  TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_Gray, gray_checksum);
+  TestRenderPageBitmapWithExternalMemoryAndNoStride(page, FPDFBitmap_Gray,
+                                                    gray_checksum);
+
+  static constexpr int kBgrxStride = 800;  // Width of 200 * 32 bits per pixel.
+  TestRenderPageBitmapWithInternalMemory(page, FPDFBitmap_BGRx,
+                                         ManyRectanglesChecksum());
+  TestRenderPageBitmapWithInternalMemoryAndStride(
+      page, FPDFBitmap_BGRx, kBgrxStride, ManyRectanglesChecksum());
+  TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGRx,
+                                         ManyRectanglesChecksum());
+  TestRenderPageBitmapWithExternalMemoryAndNoStride(page, FPDFBitmap_BGRx,
+                                                    ManyRectanglesChecksum());
+
+  TestRenderPageBitmapWithInternalMemory(page, FPDFBitmap_BGRA,
+                                         ManyRectanglesChecksum());
+  TestRenderPageBitmapWithInternalMemoryAndStride(
+      page, FPDFBitmap_BGRA, kBgrxStride, ManyRectanglesChecksum());
+  TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGRA,
+                                         ManyRectanglesChecksum());
+  TestRenderPageBitmapWithExternalMemoryAndNoStride(page, FPDFBitmap_BGRA,
+                                                    ManyRectanglesChecksum());
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFViewEmbedderTest, RenderHelloWorldWithFlags) {
   ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
-  TestRenderPageBitmapWithFlags(page, 0, kNormalMD5);
-  TestRenderPageBitmapWithFlags(page, FPDF_ANNOT, kNormalMD5);
-  TestRenderPageBitmapWithFlags(page, FPDF_LCD_TEXT, kLcdTextMD5);
-  TestRenderPageBitmapWithFlags(page, FPDF_NO_NATIVETEXT, kNormalMD5);
-  TestRenderPageBitmapWithFlags(page, FPDF_GRAYSCALE, kNormalMD5);
+  using pdfium::HelloWorldChecksum;
+  TestRenderPageBitmapWithFlags(page, 0, HelloWorldChecksum());
+  TestRenderPageBitmapWithFlags(page, FPDF_ANNOT, HelloWorldChecksum());
+  TestRenderPageBitmapWithFlags(page, FPDF_GRAYSCALE, HelloWorldChecksum());
   TestRenderPageBitmapWithFlags(page, FPDF_RENDER_LIMITEDIMAGECACHE,
-                                kNormalMD5);
-  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_FORCEHALFTONE, kNormalMD5);
-  TestRenderPageBitmapWithFlags(page, FPDF_PRINTING, kNormalMD5);
+                                HelloWorldChecksum());
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_FORCEHALFTONE,
+                                HelloWorldChecksum());
+  TestRenderPageBitmapWithFlags(page, FPDF_PRINTING, HelloWorldChecksum());
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHIMAGE,
+                                HelloWorldChecksum());
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHPATH,
+                                HelloWorldChecksum());
+
+  const char* lcd_text_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "d1decde2de1c07b5274cc8cb44f92427";
+#if BUILDFLAG(IS_APPLE)
+    return "6eef7237f7591f07616e238422086737";
+#else
+    return "09152e25e51fa8ca31fc28d0937bf477";
+#endif  // BUILDFLAG(IS_APPLE)
+  }();
+  const char* no_smoothtext_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "cd5bbe9407c3fcc85d365172a9a55abd";
+    }
+#if BUILDFLAG(IS_APPLE)
+    return "6eef7237f7591f07616e238422086737";
+#else
+    return "37d0b34e1762fdda4c05ce7ea357b828";
+#endif
+  }();
+
+  TestRenderPageBitmapWithFlags(page, FPDF_LCD_TEXT, lcd_text_checksum);
   TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHTEXT,
-                                kNoSmoothtextMD5);
-  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHIMAGE, kNormalMD5);
-  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHPATH, kNormalMD5);
+                                no_smoothtext_checksum);
+
+  // For text rendering, When anti-aliasing is disabled, LCD Optimization flag
+  // will be ignored.
+  TestRenderPageBitmapWithFlags(page, FPDF_LCD_TEXT | FPDF_RENDER_NO_SMOOTHTEXT,
+                                no_smoothtext_checksum);
 
   UnloadPage(page);
 }
-#endif  // defined(OS_LINUX)
 
-#if defined(OS_WIN)
+// Deliberately disabled because this test case renders a large bitmap, which is
+// very slow for debug builds.
+#if defined(NDEBUG)
+#define MAYBE_LargeImageDoesNotRenderBlank LargeImageDoesNotRenderBlank
+#else
+#define MAYBE_LargeImageDoesNotRenderBlank DISABLED_LargeImageDoesNotRenderBlank
+#endif
+TEST_F(FPDFViewEmbedderTest, MAYBE_LargeImageDoesNotRenderBlank) {
+  static const char kChecksum[] = "a6056db6961f4e65c42ab2e246171fe1";
+
+  ASSERT_TRUE(OpenDocument("bug_1646.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  constexpr int kWidth = 40000;
+  constexpr int kHeight = 100;
+  TestRenderPageBitmapWithMatrix(page, kWidth, kHeight, {1000, 0, 0, 1, 0, 0},
+                                 {0, 0, kWidth, kHeight}, kChecksum);
+
+  UnloadPage(page);
+}
+
+#if BUILDFLAG(IS_WIN)
 TEST_F(FPDFViewEmbedderTest, FPDFRenderPageEmf) {
   ASSERT_TRUE(OpenDocument("rectangles.pdf"));
   FPDF_PAGE page = LoadPage(0);
@@ -1343,4 +1911,172 @@
 
   UnloadPage(page);
 }
-#endif  // defined(OS_WIN)
+
+TEST_F(FPDFViewEmbedderTest, ImageMask) {
+  // TODO(crbug.com/pdfium/1500): Fix this test and enable.
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    return;
+
+  ASSERT_TRUE(OpenDocument("bug_674771.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Render the page with more efficient processing of image masks.
+  FPDF_SetPrintMode(FPDF_PRINTMODE_EMF_IMAGE_MASKS);
+  std::vector<uint8_t> emf_image_masks = RenderPageWithFlagsToEmf(page, 0);
+
+  // Render the page normally.
+  FPDF_SetPrintMode(FPDF_PRINTMODE_EMF);
+  std::vector<uint8_t> emf_normal = RenderPageWithFlagsToEmf(page, 0);
+
+  EXPECT_LT(emf_image_masks.size(), emf_normal.size());
+
+  UnloadPage(page);
+}
+#endif  // BUILDFLAG(IS_WIN)
+
+TEST_F(FPDFViewEmbedderTest, GetTrailerEnds) {
+  ASSERT_TRUE(OpenDocument("two_signatures.pdf"));
+
+  // FPDF_GetTrailerEnds() positive testing.
+  unsigned long size = FPDF_GetTrailerEnds(document(), nullptr, 0);
+  const std::vector<unsigned int> kExpectedEnds{633, 1703, 2781};
+  ASSERT_EQ(kExpectedEnds.size(), size);
+  std::vector<unsigned int> ends(size);
+  ASSERT_EQ(size, FPDF_GetTrailerEnds(document(), ends.data(), size));
+  ASSERT_EQ(kExpectedEnds, ends);
+
+  // FPDF_GetTrailerEnds() negative testing.
+  ASSERT_EQ(0U, FPDF_GetTrailerEnds(nullptr, nullptr, 0));
+
+  ends.resize(2);
+  ends[0] = 0;
+  ends[1] = 1;
+  size = FPDF_GetTrailerEnds(document(), ends.data(), ends.size());
+  ASSERT_EQ(kExpectedEnds.size(), size);
+  EXPECT_EQ(0U, ends[0]);
+  EXPECT_EQ(1U, ends[1]);
+}
+
+TEST_F(FPDFViewEmbedderTest, GetTrailerEndsHelloWorld) {
+  // Single trailer, \n line ending at the trailer end.
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+
+  // FPDF_GetTrailerEnds() positive testing.
+  unsigned long size = FPDF_GetTrailerEnds(document(), nullptr, 0);
+  const std::vector<unsigned int> kExpectedEnds{840};
+  ASSERT_EQ(kExpectedEnds.size(), size);
+  std::vector<unsigned int> ends(size);
+  ASSERT_EQ(size, FPDF_GetTrailerEnds(document(), ends.data(), size));
+  ASSERT_EQ(kExpectedEnds, ends);
+}
+
+TEST_F(FPDFViewEmbedderTest, GetTrailerEndsAnnotationStamp) {
+  // Multiple trailers, \r\n line ending at the trailer ends.
+  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+
+  // FPDF_GetTrailerEnds() positive testing.
+  unsigned long size = FPDF_GetTrailerEnds(document(), nullptr, 0);
+  const std::vector<unsigned int> kExpectedEnds{441, 7945, 101719};
+  ASSERT_EQ(kExpectedEnds.size(), size);
+  std::vector<unsigned int> ends(size);
+  ASSERT_EQ(size, FPDF_GetTrailerEnds(document(), ends.data(), size));
+  ASSERT_EQ(kExpectedEnds, ends);
+}
+
+TEST_F(FPDFViewEmbedderTest, GetTrailerEndsLinearized) {
+  // Set up linearized PDF.
+  FileAccessForTesting file_acc("linearized.pdf");
+  FakeFileAccess fake_acc(&file_acc);
+  CreateAvail(fake_acc.GetFileAvail(), fake_acc.GetFileAccess());
+  fake_acc.SetWholeFileAvailable();
+
+  // Multiple trailers, \r line ending at the trailer ends (no \n).
+  SetDocumentFromAvail();
+  ASSERT_TRUE(document());
+
+  // FPDF_GetTrailerEnds() positive testing.
+  unsigned long size = FPDF_GetTrailerEnds(document(), nullptr, 0);
+  const std::vector<unsigned int> kExpectedEnds{474, 11384};
+  ASSERT_EQ(kExpectedEnds.size(), size);
+  std::vector<unsigned int> ends(size);
+  ASSERT_EQ(size, FPDF_GetTrailerEnds(document(), ends.data(), size));
+  ASSERT_EQ(kExpectedEnds, ends);
+}
+
+TEST_F(FPDFViewEmbedderTest, GetTrailerEndsWhitespace) {
+  // Whitespace between 'endstream'/'endobj' and the newline.
+  ASSERT_TRUE(OpenDocument("trailer_end_trailing_space.pdf"));
+
+  unsigned long size = FPDF_GetTrailerEnds(document(), nullptr, 0);
+  const std::vector<unsigned int> kExpectedEnds{1193};
+  // Without the accompanying fix in place, this test would have failed, as the
+  // size was 0, not 1, i.e. no trailer ends were found.
+  ASSERT_EQ(kExpectedEnds.size(), size);
+  std::vector<unsigned int> ends(size);
+  ASSERT_EQ(size, FPDF_GetTrailerEnds(document(), ends.data(), size));
+  EXPECT_EQ(kExpectedEnds, ends);
+}
+
+TEST_F(FPDFViewEmbedderTest, RenderXfaPage) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Should always be blank, as we're not testing `FPDF_FFLDraw()` here.
+  TestRenderPageBitmapWithFlags(page, 0, pdfium::kBlankPage612By792Checksum);
+
+  UnloadPage(page);
+}
+
+#if defined(_SKIA_SUPPORT_)
+TEST_F(FPDFViewEmbedderTest, RenderPageToSkp) {
+  if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    GTEST_SKIP() << "FPDF_RenderPageSkp() only makes sense with Skia";
+  }
+
+  ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  TestRenderPageSkp(page, pdfium::RectanglesChecksum());
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFViewEmbedderTest, RenderXfaPageToSkp) {
+  if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    GTEST_SKIP() << "FPDF_RenderPageSkp() only makes sense with Skia";
+  }
+
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Should always be blank, as we're not testing `FPDF_FFLRecord()` here.
+  TestRenderPageSkp(page, pdfium::kBlankPage612By792Checksum);
+
+  UnloadPage(page);
+}
+#endif  // defined(_SKIA_SUPPORT_)
+
+TEST_F(FPDFViewEmbedderTest, NoSmoothTextItalicOverlappingGlyphs) {
+  ASSERT_TRUE(OpenDocument("bug_1919.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  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/fpdf_view_unittest.cpp b/fpdfsdk/fpdf_view_unittest.cpp
index c08c49b..26a6431 100644
--- a/fpdfsdk/fpdf_view_unittest.cpp
+++ b/fpdfsdk/fpdf_view_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fpdfsdk/fpdfxfa/BUILD.gn b/fpdfsdk/fpdfxfa/BUILD.gn
index 4e88c4c..b3d5fe0 100644
--- a/fpdfsdk/fpdfxfa/BUILD.gn
+++ b/fpdfsdk/fpdfxfa/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -17,8 +17,10 @@
     "cpdfxfa_page.h",
     "cpdfxfa_widget.cpp",
     "cpdfxfa_widget.h",
-    "cpdfxfa_widgethandler.cpp",
-    "cpdfxfa_widgethandler.h",
+  ]
+  configs += [
+    "../../:pdfium_strict_config",
+    "../../:pdfium_noshorten_config",
   ]
   deps = [
     "../../:pdfium_public_headers",
@@ -26,19 +28,28 @@
     "../../core/fpdfapi/parser",
     "../../core/fpdfapi/render",
     "../../core/fxcrt",
+    "../../fxbarcode",
     "../../fxjs",
+    "../../fxjs:gc",
+    "../../xfa/fgas/font",
+    "../../xfa/fgas/graphics",
     "../../xfa/fwl",
     "../../xfa/fxfa",
     "../../xfa/fxfa/parser",
-    "../../xfa/fxgraphics",
   ]
-  configs += [ "../../:pdfium_core_config" ]
   visibility = [ "../../*" ]
 }
 
 pdfium_embeddertest_source_set("embeddertests") {
-  sources = [ "cpdfxfa_docenvironment_embeddertest.cpp" ]
+  sources = [
+    "cpdfxfa_context_embeddertest.cpp",
+    "cpdfxfa_docenvironment_embeddertest.cpp",
+  ]
   configs = [ "//v8:external_startup_data" ]
-  deps = [ "../../fxjs" ]
+  deps = [
+    ":fpdfxfa",
+    "../:fpdfsdk",
+    "../../fxjs",
+  ]
   pdfium_root_dir = "../../"
 }
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp
index 7cb4a2c..7318cee 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
 
+#include <stdint.h>
+
 #include <algorithm>
 #include <utility>
 
@@ -13,14 +15,22 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_seekablemultistream.h"
+#include "core/fxcrt/autonuller.h"
+#include "core/fxcrt/fixed_zeroed_data_vector.h"
+#include "core/fxcrt/stl_util.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "core/fxcrt/xml/cfx_xmlparser.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
+#include "fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
+#include "fxbarcode/BC_Library.h"
 #include "fxjs/cjs_runtime.h"
 #include "fxjs/ijs_runtime.h"
 #include "public/fpdf_formfill.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "v8/include/cppgc/allocation.h"
+#include "xfa/fgas/font/cfgas_gemodule.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
@@ -53,62 +63,61 @@
   if (!pRoot)
     return nullptr;
 
-  const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
+  RetainPtr<const CPDF_Dictionary> pAcroForm = pRoot->GetDictFor("AcroForm");
   if (!pAcroForm)
     return nullptr;
 
-  const CPDF_Object* pElementXFA = pAcroForm->GetDirectObjectFor("XFA");
+  RetainPtr<const CPDF_Object> pElementXFA =
+      pAcroForm->GetDirectObjectFor("XFA");
   if (!pElementXFA)
     return nullptr;
 
-  std::vector<const CPDF_Stream*> xfaStreams;
+  std::vector<RetainPtr<const CPDF_Stream>> xfa_streams;
   if (pElementXFA->IsArray()) {
     const CPDF_Array* pXFAArray = pElementXFA->AsArray();
     for (size_t i = 0; i < pXFAArray->size() / 2; i++) {
-      if (const CPDF_Stream* pStream = pXFAArray->GetStreamAt(i * 2 + 1))
-        xfaStreams.push_back(pStream);
+      RetainPtr<const CPDF_Stream> pStream = pXFAArray->GetStreamAt(i * 2 + 1);
+      if (pStream)
+        xfa_streams.push_back(std::move(pStream));
     }
   } else if (pElementXFA->IsStream()) {
-    xfaStreams.push_back(pElementXFA->AsStream());
+    xfa_streams.push_back(ToStream(pElementXFA));
   }
-  if (xfaStreams.empty())
+  if (xfa_streams.empty())
     return nullptr;
 
-  return pdfium::MakeRetain<CPDF_SeekableMultiStream>(xfaStreams);
+  return pdfium::MakeRetain<CPDF_SeekableMultiStream>(std::move(xfa_streams));
 }
 
 }  // namespace
 
+void CPDFXFA_ModuleInit() {
+  CFGAS_GEModule::Create();
+  BC_Library_Init();
+}
+
+void CPDFXFA_ModuleDestroy() {
+  BC_Library_Destroy();
+  CFGAS_GEModule::Destroy();
+}
+
 CPDFXFA_Context::CPDFXFA_Context(CPDF_Document* pPDFDoc)
     : m_pPDFDoc(pPDFDoc),
-      m_pXFAApp(pdfium::MakeUnique<CXFA_FFApp>(this)),
-      m_DocEnv(this) {
-  ASSERT(m_pPDFDoc);
+      m_pDocEnv(std::make_unique<CPDFXFA_DocEnvironment>(this)),
+      m_pGCHeap(FXGC_CreateHeap()) {
+  DCHECK(m_pPDFDoc);
+
+  // There might not be a heap when JS not initialized.
+  if (m_pGCHeap) {
+    m_pXFAApp = cppgc::MakeGarbageCollected<CXFA_FFApp>(
+        m_pGCHeap->GetAllocationHandle(), this);
+  }
 }
 
 CPDFXFA_Context::~CPDFXFA_Context() {
-  m_nLoadStatus = FXFA_LOADSTATUS_CLOSING;
-
-  // Must happen before we remove the form fill environment.
-  CloseXFADoc();
-
-  if (m_pFormFillEnv) {
+  m_nLoadStatus = LoadStatus::kClosing;
+  if (m_pFormFillEnv)
     m_pFormFillEnv->ClearAllFocusedAnnots();
-    // Once we're deleted the FormFillEnvironment will point at a bad underlying
-    // doc so we need to reset it ...
-    m_pFormFillEnv->GetPDFDocument()->SetExtension(nullptr);
-    m_pFormFillEnv.Reset();
-  }
-
-  m_nLoadStatus = FXFA_LOADSTATUS_CLOSED;
-}
-
-void CPDFXFA_Context::CloseXFADoc() {
-  if (!m_pXFADoc)
-    return;
-
-  m_pXFADocView = nullptr;
-  m_pXFADoc.reset();
 }
 
 void CPDFXFA_Context::SetFormFillEnv(
@@ -117,58 +126,75 @@
   // context will be different if the form fill environment closes, so, force
   // the layout data to clear.
   if (m_pXFADoc && m_pXFADoc->GetXFADoc()) {
-    // The CPDF_XFADocView has a pointer to the CXFA_LayoutProcessor which is
-    // owned by the CXFA_Document. The Layout Processor will be freed with the
-    // ClearLayoutData() call. Make sure the doc view has already released the
-    // pointer.
-    if (m_pXFADocView)
-      m_pXFADocView->ResetLayoutProcessor();
-
     m_pXFADoc->GetXFADoc()->ClearLayoutData();
+    m_pXFADocView.Clear();
+    m_pXFADoc.Clear();
+    m_pXFAApp.Clear();
+    FXGC_ForceGarbageCollection(m_pGCHeap.get());
   }
-
   m_pFormFillEnv.Reset(pFormFillEnv);
 }
 
 bool CPDFXFA_Context::LoadXFADoc() {
-  m_nLoadStatus = FXFA_LOADSTATUS_LOADING;
+  m_nLoadStatus = LoadStatus::kLoading;
   m_XFAPageList.clear();
 
-  auto stream = CreateXFAMultiStream(m_pPDFDoc.Get());
+  CJS_Runtime* actual_runtime = GetCJSRuntime();  // Null if a stub.
+  if (!actual_runtime) {
+    FXSYS_SetLastError(FPDF_ERR_XFALOAD);
+    return false;
+  }
+
+  auto stream = CreateXFAMultiStream(m_pPDFDoc);
   if (!stream) {
     FXSYS_SetLastError(FPDF_ERR_XFALOAD);
     return false;
   }
 
-  m_pXFADoc = CXFA_FFDoc::CreateAndOpen(m_pXFAApp.get(), &m_DocEnv,
-                                        m_pPDFDoc.Get(), stream);
-  if (!m_pXFADoc) {
+  CFX_XMLParser parser(stream);
+  m_pXML = parser.Parse();
+  if (!m_pXML) {
     FXSYS_SetLastError(FPDF_ERR_XFALOAD);
     return false;
   }
 
-  CJS_Runtime* actual_runtime = GetCJSRuntime();  // Null if a stub.
-  if (!actual_runtime) {
+  AutoNuller<cppgc::Persistent<CXFA_FFDoc>> doc_nuller(&m_pXFADoc);
+  m_pXFADoc = cppgc::MakeGarbageCollected<CXFA_FFDoc>(
+      m_pGCHeap->GetAllocationHandle(), m_pXFAApp, m_pDocEnv.get(), m_pPDFDoc,
+      m_pGCHeap.get());
+
+  if (!m_pXFADoc->OpenDoc(m_pXML.get())) {
     FXSYS_SetLastError(FPDF_ERR_XFALOAD);
     return false;
   }
 
+  if (!m_pXFAApp->LoadFWLTheme(m_pXFADoc)) {
+    FXSYS_SetLastError(FPDF_ERR_XFALAYOUT);
+    return false;
+  }
+
   m_pXFADoc->GetXFADoc()->InitScriptContext(actual_runtime);
   if (m_pXFADoc->GetFormType() == FormType::kXFAFull)
     m_FormType = FormType::kXFAFull;
   else
     m_FormType = FormType::kXFAForeground;
 
+  AutoNuller<cppgc::Persistent<CXFA_FFDocView>> view_nuller(&m_pXFADocView);
   m_pXFADocView = m_pXFADoc->CreateDocView();
+
   if (m_pXFADocView->StartLayout() < 0) {
-    CloseXFADoc();
+    m_pXFADoc->GetXFADoc()->ClearLayoutData();
+    FXGC_ForceGarbageCollection(m_pGCHeap.get());
     FXSYS_SetLastError(FPDF_ERR_XFALAYOUT);
     return false;
   }
 
   m_pXFADocView->DoLayout();
   m_pXFADocView->StopLayout();
-  m_nLoadStatus = FXFA_LOADSTATUS_LOADED;
+
+  view_nuller.AbandonNullification();
+  doc_nuller.AbandonNullification();
+  m_nLoadStatus = LoadStatus::kLoaded;
   return true;
 }
 
@@ -181,15 +207,13 @@
     case FormType::kXFAFull:
       return m_pXFADoc ? m_pXFADocView->CountPageViews() : 0;
   }
-  NOTREACHED();
-  return 0;
 }
 
-RetainPtr<CPDFXFA_Page> CPDFXFA_Context::GetXFAPage(int page_index) {
+RetainPtr<CPDFXFA_Page> CPDFXFA_Context::GetOrCreateXFAPage(int page_index) {
   if (page_index < 0)
     return nullptr;
 
-  if (pdfium::IndexInBounds(m_XFAPageList, page_index)) {
+  if (fxcrt::IndexInBounds(m_XFAPageList, page_index)) {
     if (m_XFAPageList[page_index])
       return m_XFAPageList[page_index];
   } else {
@@ -201,12 +225,19 @@
   if (!pPage->LoadPage())
     return nullptr;
 
-  if (pdfium::IndexInBounds(m_XFAPageList, page_index))
+  if (fxcrt::IndexInBounds(m_XFAPageList, page_index))
     m_XFAPageList[page_index] = pPage;
 
   return pPage;
 }
 
+RetainPtr<CPDFXFA_Page> CPDFXFA_Context::GetXFAPage(int page_index) {
+  if (!fxcrt::IndexInBounds(m_XFAPageList, page_index))
+    return nullptr;
+
+  return m_XFAPageList[page_index];
+}
+
 RetainPtr<CPDFXFA_Page> CPDFXFA_Context::GetXFAPage(
     CXFA_FFPageView* pPage) const {
   if (!pPage)
@@ -225,17 +256,13 @@
   return nullptr;
 }
 
-CPDF_Document* CPDFXFA_Context::GetPDFDoc() const {
-  return m_pPDFDoc.Get();
-}
-
 void CPDFXFA_Context::DeletePage(int page_index) {
   // Delete from the document first because, if GetPage was never called for
   // this |page_index| then |m_XFAPageList| may have size < |page_index| even
   // if it's a valid page in the document.
   m_pPDFDoc->DeletePage(page_index);
 
-  if (pdfium::IndexInBounds(m_XFAPageList, page_index))
+  if (fxcrt::IndexInBounds(m_XFAPageList, page_index))
     m_XFAPageList[page_index].Reset();
 }
 
@@ -294,7 +321,7 @@
                                 const WideString& wsTitle,
                                 uint32_t dwIconType,
                                 uint32_t dwButtonType) {
-  if (!m_pFormFillEnv || m_nLoadStatus != FXFA_LOADSTATUS_LOADED)
+  if (!m_pFormFillEnv || m_nLoadStatus != LoadStatus::kLoaded)
     return -1;
 
   int iconType =
@@ -312,19 +339,18 @@
   if (!m_pFormFillEnv)
     return WideString();
 
-  int nLength = 2048;
-  std::vector<uint8_t> pBuff(nLength);
-  nLength = m_pFormFillEnv->JS_appResponse(wsQuestion, wsTitle, wsDefaultAnswer,
-                                           WideString(), bMark, pBuff.data(),
-                                           nLength);
-  if (nLength <= 0)
+  constexpr int kMaxWideChars = 1024;
+  FixedZeroedDataVector<uint16_t> buffer(kMaxWideChars);
+  pdfium::span<uint16_t> buffer_span = buffer.writable_span();
+  int byte_length = m_pFormFillEnv->JS_appResponse(
+      wsQuestion, wsTitle, wsDefaultAnswer, WideString(), bMark,
+      pdfium::as_writable_bytes(buffer_span));
+  if (byte_length <= 0)
     return WideString();
 
-  nLength = std::min(2046, nLength);
-  pBuff[nLength] = 0;
-  pBuff[nLength + 1] = 0;
-  return WideString::FromUTF16LE(reinterpret_cast<uint16_t*>(pBuff.data()),
-                                 nLength / sizeof(uint16_t));
+  buffer_span = buffer_span.first(
+      std::min<size_t>(kMaxWideChars, byte_length / sizeof(uint16_t)));
+  return WideString::FromUTF16LE(buffer_span.data(), buffer_span.size());
 }
 
 RetainPtr<IFX_SeekableReadStream> CPDFXFA_Context::DownloadURL(
@@ -353,10 +379,14 @@
          m_pFormFillEnv->PutRequestURL(wsURL, wsData, wsEncode);
 }
 
-TimerHandlerIface* CPDFXFA_Context::GetTimerHandler() const {
+CFX_Timer::HandlerIface* CPDFXFA_Context::GetTimerHandler() const {
   return m_pFormFillEnv ? m_pFormFillEnv->GetTimerHandler() : nullptr;
 }
 
+cppgc::Heap* CPDFXFA_Context::GetGCHeap() const {
+  return m_pGCHeap.get();
+}
+
 bool CPDFXFA_Context::SaveDatasetsPackage(
     const RetainPtr<IFX_SeekableStream>& pStream) {
   return SavePackage(pStream, XFA_HASHCODE_Datasets);
@@ -387,10 +417,11 @@
     return;
 
   CXFA_FFWidgetHandler* pWidgetHandler = pXFADocView->GetWidgetHandler();
-  auto it = pXFADocView->CreateReadyNodeIterator();
-  while (CXFA_Node* pNode = it->MoveToNext()) {
+  CXFA_ReadyNodeIterator it(pXFADocView->GetRootSubform());
+  while (CXFA_Node* pNode = it.MoveToNext()) {
     CXFA_EventParam preParam;
     preParam.m_eType = XFA_EVENT_PostSave;
+    preParam.m_bTargeted = false;
     pWidgetHandler->ProcessEvent(pNode, &preParam);
   }
   pXFADocView->UpdateDocView();
@@ -407,10 +438,11 @@
     return;
 
   CXFA_FFWidgetHandler* pWidgetHandler = pXFADocView->GetWidgetHandler();
-  auto it = pXFADocView->CreateReadyNodeIterator();
-  while (CXFA_Node* pNode = it->MoveToNext()) {
+  CXFA_ReadyNodeIterator it(pXFADocView->GetRootSubform());
+  while (CXFA_Node* pNode = it.MoveToNext()) {
     CXFA_EventParam preParam;
     preParam.m_eType = XFA_EVENT_PreSave;
+    preParam.m_bTargeted = false;
     pWidgetHandler->ProcessEvent(pNode, &preParam);
   }
   pXFADocView->UpdateDocView();
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_context.h b/fpdfsdk/fpdfxfa/cpdfxfa_context.h
index 0f6a14b..4188438 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_context.h
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_context.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,53 +7,67 @@
 #ifndef FPDFSDK_FPDFXFA_CPDFXFA_CONTEXT_H_
 #define FPDFSDK_FPDFXFA_CPDFXFA_CONTEXT_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
 #include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/cfx_timer.h"
 #include "core/fxcrt/observed_ptr.h"
-#include "core/fxcrt/timerhandler_iface.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/persistent.h"
+#include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 
+class CFX_XMLDocument;
 class CJS_Runtime;
-class CXFA_FFDocHandler;
-class IJS_EventContext;
-class IJS_Runtime;
+class CPDFXFA_DocEnvironment;
 
-enum LoadStatus {
-  FXFA_LOADSTATUS_PRELOAD = 0,
-  FXFA_LOADSTATUS_LOADING,
-  FXFA_LOADSTATUS_LOADED,
-  FXFA_LOADSTATUS_CLOSING,
-  FXFA_LOADSTATUS_CLOSED
-};
+// Per-process initializations.
+void CPDFXFA_ModuleInit();
+void CPDFXFA_ModuleDestroy();
 
 class CPDFXFA_Context final : public CPDF_Document::Extension,
-                              public IXFA_AppProvider {
+                              public CXFA_FFApp::CallbackIface {
  public:
+  enum class LoadStatus : uint8_t {
+    kPreload = 0,
+    kLoading,
+    kLoaded,
+    kClosing,
+  };
+
   explicit CPDFXFA_Context(CPDF_Document* pPDFDoc);
   ~CPDFXFA_Context() override;
 
   bool LoadXFADoc();
-  CXFA_FFDoc* GetXFADoc() { return m_pXFADoc.get(); }
-  CXFA_FFDocView* GetXFADocView() const { return m_pXFADocView.Get(); }
+  LoadStatus GetLoadStatus() const { return m_nLoadStatus; }
   FormType GetFormType() const { return m_FormType; }
+  int GetOriginalPageCount() const { return m_nPageCount; }
+  void SetOriginalPageCount(int count) {
+    m_nPageCount = count;
+    m_XFAPageList.resize(count);
+  }
+
+  CPDF_Document* GetPDFDoc() const { return m_pPDFDoc; }
+  CFX_XMLDocument* GetXMLDoc() { return m_pXML.get(); }
+  CXFA_FFDoc* GetXFADoc() { return m_pXFADoc; }
+  CXFA_FFDocView* GetXFADocView() const { return m_pXFADocView.Get(); }
   CPDFSDK_FormFillEnvironment* GetFormFillEnv() const {
     return m_pFormFillEnv.Get();
   }
   void SetFormFillEnv(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-
+  RetainPtr<CPDFXFA_Page> GetOrCreateXFAPage(int page_index);
   RetainPtr<CPDFXFA_Page> GetXFAPage(int page_index);
   RetainPtr<CPDFXFA_Page> GetXFAPage(CXFA_FFPageView* pPage) const;
   void ClearChangeMark();
 
   // CPDF_Document::Extension:
-  CPDF_Document* GetPDFDoc() const override;
   int GetPageCount() const override;
   void DeletePage(int page_index) override;
   uint32_t GetUserPermissions() const override;
@@ -61,7 +75,7 @@
   bool ContainsExtensionFullForm() const override;
   bool ContainsExtensionForegroundForm() const override;
 
-  // IFXA_AppProvider:
+  // CXFA_FFApp::CallbackIface:
   WideString GetLanguage() override;
   WideString GetPlatform() override;
   WideString GetAppName() override;
@@ -86,7 +100,8 @@
   bool PutRequestURL(const WideString& wsURL,
                      const WideString& wsData,
                      const WideString& wsEncode) override;
-  TimerHandlerIface* GetTimerHandler() const override;
+  CFX_Timer::HandlerIface* GetTimerHandler() const override;
+  cppgc::Heap* GetGCHeap() const override;
 
   bool SaveDatasetsPackage(const RetainPtr<IFX_SeekableStream>& pStream);
   bool SaveFormPackage(const RetainPtr<IFX_SeekableStream>& pStream);
@@ -95,37 +110,27 @@
       std::vector<RetainPtr<IFX_SeekableStream>>* fileList);
 
  private:
-  friend class CPDFXFA_DocEnvironment;
-
-  int GetOriginalPageCount() const { return m_nPageCount; }
-  void SetOriginalPageCount(int count) {
-    m_nPageCount = count;
-    m_XFAPageList.resize(count);
-  }
-
-  LoadStatus GetLoadStatus() const { return m_nLoadStatus; }
-  std::vector<RetainPtr<CPDFXFA_Page>>* GetXFAPageList() {
-    return &m_XFAPageList;
-  }
-
   CJS_Runtime* GetCJSRuntime() const;
   bool SavePackage(const RetainPtr<IFX_SeekableStream>& pStream,
                    XFA_HashCode code);
-  void CloseXFADoc();
 
   FormType m_FormType = FormType::kNone;
-  UnownedPtr<CPDF_Document> const m_pPDFDoc;
-  std::unique_ptr<CXFA_FFDoc> m_pXFADoc;
-  ObservedPtr<CPDFSDK_FormFillEnvironment> m_pFormFillEnv;
-  UnownedPtr<CXFA_FFDocView> m_pXFADocView;
-  std::unique_ptr<CXFA_FFApp> const m_pXFAApp;
-  std::unique_ptr<CJS_Runtime> m_pRuntime;
-  std::vector<RetainPtr<CPDFXFA_Page>> m_XFAPageList;
-  LoadStatus m_nLoadStatus = FXFA_LOADSTATUS_PRELOAD;
+  LoadStatus m_nLoadStatus = LoadStatus::kPreload;
   int m_nPageCount = 0;
 
-  // Must be destroyed before |m_pFormFillEnv|.
-  CPDFXFA_DocEnvironment m_DocEnv;
+  // The order in which the following members are destroyed is critical.
+  UnownedPtr<CPDF_Document> const m_pPDFDoc;
+  std::unique_ptr<CFX_XMLDocument> m_pXML;
+  ObservedPtr<CPDFSDK_FormFillEnvironment> m_pFormFillEnv;
+  std::vector<RetainPtr<CPDFXFA_Page>> m_XFAPageList;
+
+  // Can't outlive |m_pFormFillEnv|.
+  std::unique_ptr<CPDFXFA_DocEnvironment> m_pDocEnv;
+
+  FXGCScopedHeap m_pGCHeap;
+  cppgc::Persistent<CXFA_FFApp> m_pXFAApp;          // can't outlive |m_pGCHeap|
+  cppgc::Persistent<CXFA_FFDoc> m_pXFADoc;          // Can't outlive |m_pGCHeap|
+  cppgc::Persistent<CXFA_FFDocView> m_pXFADocView;  // Can't outlive |m_pGCHeap|
 };
 
 #endif  // FPDFSDK_FPDFXFA_CPDFXFA_CONTEXT_H_
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_context_embeddertest.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_context_embeddertest.cpp
new file mode 100644
index 0000000..36b1ad0
--- /dev/null
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_context_embeddertest.cpp
@@ -0,0 +1,20 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
+
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/xfa_js_embedder_test.h"
+
+class CPDFXFAContextEmbedderTest : public XFAJSEmbedderTest {};
+
+// Should not crash.
+TEST_F(CPDFXFAContextEmbedderTest, HasHeap) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  CPDF_Document* pDocument = CPDFDocumentFromFPDFDocument(document());
+  auto* pContext = static_cast<CPDFXFA_Context*>(pDocument->GetExtension());
+  EXPECT_TRUE(pContext->GetGCHeap());
+}
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp
index 11d1e49..ff93008 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,6 @@
 
 #include "fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h"
 
-#include <memory>
 #include <utility>
 
 #include "core/fpdfapi/parser/cpdf_array.h"
@@ -21,6 +20,7 @@
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
+#include "third_party/base/check.h"
 #include "xfa/fxfa/cxfa_ffdocview.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
 #include "xfa/fxfa/cxfa_ffwidgethandler.h"
@@ -43,12 +43,21 @@
 #define FXFA_PDF 0x10000000
 #define FXFA_XFA_ALL 0x01111111
 
+// Although there isn't direct casting between these types at present,
+// keep the internal and exernal types in sync.
+static_assert(FXFA_PAGEVIEWEVENT_POSTADDED ==
+                  static_cast<int>(CXFA_FFDoc::PageViewEvent::kPostAdded),
+              "kPostAdded mismatch");
+static_assert(FXFA_PAGEVIEWEVENT_POSTREMOVED ==
+                  static_cast<int>(CXFA_FFDoc::PageViewEvent::kPostRemoved),
+              "kPostRemoved mismatch");
+
 CPDFXFA_DocEnvironment::CPDFXFA_DocEnvironment(CPDFXFA_Context* pContext)
     : m_pContext(pContext) {
-  ASSERT(m_pContext);
+  DCHECK(m_pContext);
 }
 
-CPDFXFA_DocEnvironment::~CPDFXFA_DocEnvironment() {}
+CPDFXFA_DocEnvironment::~CPDFXFA_DocEnvironment() = default;
 
 void CPDFXFA_DocEnvironment::SetChangeMark(CXFA_FFDoc* hDoc) {
   if (hDoc == m_pContext->GetXFADoc() && m_pContext->GetFormFillEnv())
@@ -266,20 +275,20 @@
   if (hWidget->CanSelectAll())
     menuFlag |= FXFA_MENU_SELECTALL;
 
-  return pFormFillEnv->PopupMenu(pPage.Get(), nullptr, menuFlag, ptPopup);
+  return pFormFillEnv->PopupMenu(pPage.Get(), menuFlag, ptPopup);
 }
 
-void CPDFXFA_DocEnvironment::PageViewEvent(CXFA_FFPageView* pPageView,
-                                           uint32_t dwFlags) {
+void CPDFXFA_DocEnvironment::OnPageViewEvent(CXFA_FFPageView* pPageView,
+                                             CXFA_FFDoc::PageViewEvent eEvent) {
   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
   if (!pFormFillEnv)
     return;
 
-  if (m_pContext->GetLoadStatus() == FXFA_LOADSTATUS_LOADING ||
-      m_pContext->GetLoadStatus() == FXFA_LOADSTATUS_CLOSING ||
-      XFA_PAGEVIEWEVENT_StopLayout != dwFlags)
+  if (m_pContext->GetLoadStatus() == CPDFXFA_Context::LoadStatus::kLoading ||
+      m_pContext->GetLoadStatus() == CPDFXFA_Context::LoadStatus::kClosing ||
+      eEvent != CXFA_FFDoc::PageViewEvent::kStopLayout) {
     return;
-
+  }
   int nNewCount = m_pContext->GetPageCount();
   if (nNewCount == m_pContext->GetOriginalPageCount())
     return;
@@ -288,14 +297,13 @@
   if (!pXFADocView)
     return;
 
-  for (int iPageIter = 0; iPageIter < m_pContext->GetOriginalPageCount();
-       iPageIter++) {
-    RetainPtr<CPDFXFA_Page> pPage = (*m_pContext->GetXFAPageList())[iPageIter];
+  for (int i = 0; i < m_pContext->GetOriginalPageCount(); ++i) {
+    RetainPtr<CPDFXFA_Page> pPage = m_pContext->GetXFAPage(i);
     if (!pPage)
       continue;
 
     m_pContext->GetFormFillEnv()->RemovePageView(pPage.Get());
-    pPage->SetXFAPageViewIndex(iPageIter);
+    pPage->SetXFAPageViewIndex(i);
   }
 
   int flag = (nNewCount < m_pContext->GetOriginalPageCount())
@@ -307,7 +315,7 @@
 }
 
 void CPDFXFA_DocEnvironment::WidgetPostAdd(CXFA_FFWidget* hWidget) {
-  if (m_pContext->GetFormType() != FormType::kXFAFull || !hWidget)
+  if (m_pContext->GetFormType() != FormType::kXFAFull)
     return;
 
   CXFA_FFPageView* pPageView = hWidget->GetPageView();
@@ -318,13 +326,12 @@
   if (!pXFAPage)
     return;
 
-  m_pContext->GetFormFillEnv()
-      ->GetPageView(pXFAPage.Get(), true)
-      ->AddAnnot(hWidget);
+  auto* formfill = m_pContext->GetFormFillEnv();
+  formfill->GetOrCreatePageView(pXFAPage.Get())->AddAnnotForFFWidget(hWidget);
 }
 
 void CPDFXFA_DocEnvironment::WidgetPreRemove(CXFA_FFWidget* hWidget) {
-  if (m_pContext->GetFormType() != FormType::kXFAFull || !hWidget)
+  if (m_pContext->GetFormType() != FormType::kXFAFull)
     return;
 
   CXFA_FFPageView* pPageView = hWidget->GetPageView();
@@ -336,19 +343,17 @@
     return;
 
   CPDFSDK_PageView* pSdkPageView =
-      m_pContext->GetFormFillEnv()->GetPageView(pXFAPage.Get(), true);
-  CPDFSDK_Annot* pAnnot = pSdkPageView->GetAnnotByXFAWidget(hWidget);
-  if (pAnnot)
-    pSdkPageView->DeleteAnnot(pAnnot);
+      m_pContext->GetFormFillEnv()->GetOrCreatePageView(pXFAPage.Get());
+  pSdkPageView->DeleteAnnotForFFWidget(hWidget);
 }
 
-int32_t CPDFXFA_DocEnvironment::CountPages(CXFA_FFDoc* hDoc) {
+int32_t CPDFXFA_DocEnvironment::CountPages(const CXFA_FFDoc* hDoc) const {
   if (hDoc == m_pContext->GetXFADoc() && m_pContext->GetFormFillEnv())
     return m_pContext->GetPageCount();
   return 0;
 }
 
-int32_t CPDFXFA_DocEnvironment::GetCurrentPage(CXFA_FFDoc* hDoc) {
+int32_t CPDFXFA_DocEnvironment::GetCurrentPage(const CXFA_FFDoc* hDoc) const {
   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
     return -1;
 
@@ -374,7 +379,8 @@
   pFormFillEnv->SetCurrentPage(iCurPage);
 }
 
-bool CPDFXFA_DocEnvironment::IsCalculationsEnabled(CXFA_FFDoc* hDoc) {
+bool CPDFXFA_DocEnvironment::IsCalculationsEnabled(
+    const CXFA_FFDoc* hDoc) const {
   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
     return false;
   auto* pForm = m_pContext->GetFormFillEnv()->GetInteractiveForm();
@@ -389,26 +395,34 @@
       bEnabled);
 }
 
-void CPDFXFA_DocEnvironment::GetTitle(CXFA_FFDoc* hDoc, WideString& wsTitle) {
-  if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetPDFDoc())
-    return;
+WideString CPDFXFA_DocEnvironment::GetTitle(const CXFA_FFDoc* hDoc) const {
+  if (hDoc != m_pContext->GetXFADoc())
+    return WideString();
 
-  const CPDF_Dictionary* pInfoDict = m_pContext->GetPDFDoc()->GetInfo();
+  CPDF_Document* pPDFDoc = m_pContext->GetPDFDoc();
+  if (!pPDFDoc)
+    return WideString();
+
+  RetainPtr<const CPDF_Dictionary> pInfoDict = pPDFDoc->GetInfo();
   if (!pInfoDict)
-    return;
+    return WideString();
 
-  ByteString csTitle = pInfoDict->GetStringFor("Title");
-  wsTitle = WideString::FromDefANSI(csTitle.AsStringView());
+  ByteString csTitle = pInfoDict->GetByteStringFor("Title");
+  return WideString::FromDefANSI(csTitle.AsStringView());
 }
 
 void CPDFXFA_DocEnvironment::SetTitle(CXFA_FFDoc* hDoc,
                                       const WideString& wsTitle) {
-  if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetPDFDoc())
+  if (hDoc != m_pContext->GetXFADoc())
     return;
 
-  CPDF_Dictionary* pInfoDict = m_pContext->GetPDFDoc()->GetInfo();
+  CPDF_Document* pPDFDoc = m_pContext->GetPDFDoc();
+  if (!pPDFDoc)
+    return;
+
+  RetainPtr<CPDF_Dictionary> pInfoDict = pPDFDoc->GetInfo();
   if (pInfoDict)
-    pInfoDict->SetNewFor<CPDF_String>("Title", wsTitle);
+    pInfoDict->SetNewFor<CPDF_String>("Title", wsTitle.AsStringView());
 }
 
 void CPDFXFA_DocEnvironment::ExportData(CXFA_FFDoc* hDoc,
@@ -454,23 +468,24 @@
     if (!pRoot)
       return;
 
-    const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
+    RetainPtr<const CPDF_Dictionary> pAcroForm = pRoot->GetDictFor("AcroForm");
     if (!pAcroForm)
       return;
 
-    const CPDF_Array* pArray = ToArray(pAcroForm->GetObjectFor("XFA"));
+    RetainPtr<const CPDF_Array> pArray =
+        ToArray(pAcroForm->GetObjectFor("XFA"));
     if (!pArray)
       return;
 
     for (size_t i = 1; i < pArray->size(); i += 2) {
-      const CPDF_Object* pPDFObj = pArray->GetObjectAt(i);
-      const CPDF_Object* pPrePDFObj = pArray->GetObjectAt(i - 1);
+      RetainPtr<const CPDF_Object> pPDFObj = pArray->GetObjectAt(i);
+      RetainPtr<const CPDF_Object> pPrePDFObj = pArray->GetObjectAt(i - 1);
       if (!pPrePDFObj->IsString())
         continue;
       if (!pPDFObj->IsReference())
         continue;
 
-      const CPDF_Stream* pStream = ToStream(pPDFObj->GetDirect());
+      RetainPtr<const CPDF_Stream> pStream = ToStream(pPDFObj->GetDirect());
       if (!pStream)
         continue;
       if (pPrePDFObj->GetString() == "form") {
@@ -497,9 +512,9 @@
         ByteString content = ByteString::Format(kFormat, bPath.c_str());
         fileWrite->WriteString(content.AsStringView());
       }
-      auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
+      auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(pStream));
       pAcc->LoadAllDataFiltered();
-      fileWrite->WriteBlock(pAcc->GetData(), pAcc->GetSize());
+      fileWrite->WriteBlock(pAcc->GetSpan());
     }
   }
   fileWrite->Flush();
@@ -520,7 +535,8 @@
   pFormFillEnv->GotoURL(wsURL);
 }
 
-bool CPDFXFA_DocEnvironment::IsValidationsEnabled(CXFA_FFDoc* hDoc) {
+bool CPDFXFA_DocEnvironment::IsValidationsEnabled(
+    const CXFA_FFDoc* hDoc) const {
   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
     return false;
 
@@ -544,19 +560,20 @@
 
   if (!hWidget) {
     ObservedPtr<CPDFSDK_Annot> pNull;
-    m_pContext->GetFormFillEnv()->SetFocusAnnot(&pNull);
+    m_pContext->GetFormFillEnv()->SetFocusAnnot(pNull);
     return;
   }
 
   int pageViewCount = m_pContext->GetFormFillEnv()->GetPageViewCount();
   for (int i = 0; i < pageViewCount; i++) {
-    CPDFSDK_PageView* pPageView = m_pContext->GetFormFillEnv()->GetPageView(i);
+    CPDFSDK_PageView* pPageView =
+        m_pContext->GetFormFillEnv()->GetPageViewAtIndex(i);
     if (!pPageView)
       continue;
 
-    ObservedPtr<CPDFSDK_Annot> pAnnot(pPageView->GetAnnotByXFAWidget(hWidget));
+    ObservedPtr<CPDFSDK_Annot> pAnnot(pPageView->GetAnnotForFFWidget(hWidget));
     if (pAnnot) {
-      m_pContext->GetFormFillEnv()->SetFocusAnnot(&pAnnot);
+      m_pContext->GetFormFillEnv()->SetFocusAnnot(pAnnot);
       break;
     }
   }
@@ -565,7 +582,7 @@
 void CPDFXFA_DocEnvironment::Print(CXFA_FFDoc* hDoc,
                                    int32_t nStartPage,
                                    int32_t nEndPage,
-                                   uint32_t dwOptions) {
+                                   Mask<XFA_PrintOpt> dwOptions) {
   if (hDoc != m_pContext->GetXFADoc())
     return;
 
@@ -578,13 +595,16 @@
 
   pFormFillEnv->GetFormFillInfo()->m_pJsPlatform->Doc_print(
       pFormFillEnv->GetFormFillInfo()->m_pJsPlatform,
-      dwOptions & XFA_PRINTOPT_ShowDialog, nStartPage, nEndPage,
-      dwOptions & XFA_PRINTOPT_CanCancel, dwOptions & XFA_PRINTOPT_ShrinkPage,
-      dwOptions & XFA_PRINTOPT_AsImage, dwOptions & XFA_PRINTOPT_ReverseOrder,
-      dwOptions & XFA_PRINTOPT_PrintAnnot);
+      !!(dwOptions & XFA_PrintOpt::kShowDialog), nStartPage, nEndPage,
+      !!(dwOptions & XFA_PrintOpt::kCanCancel),
+      !!(dwOptions & XFA_PrintOpt::kShrinkPage),
+      !!(dwOptions & XFA_PrintOpt::kAsImage),
+      !!(dwOptions & XFA_PrintOpt::kReverseOrder),
+      !!(dwOptions & XFA_PrintOpt::kPrintAnnot));
 }
 
-FX_ARGB CPDFXFA_DocEnvironment::GetHighlightColor(CXFA_FFDoc* hDoc) {
+FX_ARGB CPDFXFA_DocEnvironment::GetHighlightColor(
+    const CXFA_FFDoc* hDoc) const {
   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
     return 0;
 
@@ -594,7 +614,8 @@
                                 pForm->GetHighlightColor(FormFieldType::kXFA));
 }
 
-IJS_Runtime* CPDFXFA_DocEnvironment::GetIJSRuntime(CXFA_FFDoc* hDoc) const {
+IJS_Runtime* CPDFXFA_DocEnvironment::GetIJSRuntime(
+    const CXFA_FFDoc* hDoc) const {
   if (hDoc != m_pContext->GetXFADoc())
     return nullptr;
 
@@ -602,6 +623,10 @@
   return pFormFillEnv ? pFormFillEnv->GetIJSRuntime() : nullptr;
 }
 
+CFX_XMLDocument* CPDFXFA_DocEnvironment::GetXMLDoc() const {
+  return m_pContext->GetXMLDoc();
+}
+
 RetainPtr<IFX_SeekableReadStream> CPDFXFA_DocEnvironment::OpenLinkedFile(
     CXFA_FFDoc* hDoc,
     const WideString& wsLink) {
@@ -620,12 +645,12 @@
 
 #ifdef PDF_XFA_ELEMENT_SUBMIT_ENABLED
 bool CPDFXFA_DocEnvironment::Submit(CXFA_FFDoc* hDoc, CXFA_Submit* submit) {
-  if (!NotifySubmit(true) || !m_pContext->GetXFADocView())
+  if (!OnBeforeNotifySubmit() || !m_pContext->GetXFADocView())
     return false;
 
   m_pContext->GetXFADocView()->UpdateDocView();
   bool ret = SubmitInternal(hDoc, submit);
-  NotifySubmit(false);
+  OnAfterNotifySubmit();
   return ret;
 }
 
@@ -733,27 +758,27 @@
     return false;
   }
 
-  const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
+  RetainPtr<const CPDF_Dictionary> pAcroForm = pRoot->GetDictFor("AcroForm");
   if (!pAcroForm) {
     fileStream->Flush();
     return false;
   }
 
-  const CPDF_Array* pArray = ToArray(pAcroForm->GetObjectFor("XFA"));
+  RetainPtr<const CPDF_Array> pArray = ToArray(pAcroForm->GetObjectFor("XFA"));
   if (!pArray) {
     fileStream->Flush();
     return false;
   }
 
   for (size_t i = 1; i < pArray->size(); i += 2) {
-    const CPDF_Object* pPDFObj = pArray->GetObjectAt(i);
-    const CPDF_Object* pPrePDFObj = pArray->GetObjectAt(i - 1);
+    RetainPtr<const CPDF_Object> pPDFObj = pArray->GetObjectAt(i);
+    RetainPtr<const CPDF_Object> pPrePDFObj = pArray->GetObjectAt(i - 1);
     if (!pPrePDFObj->IsString())
       continue;
     if (!pPDFObj->IsReference())
       continue;
 
-    const CPDF_Object* pDirectObj = pPDFObj->GetDirect();
+    RetainPtr<const CPDF_Object> pDirectObj = pPDFObj->GetDirect();
     if (!pDirectObj->IsStream())
       continue;
     ByteString bsType = pPrePDFObj->GetString();
@@ -878,14 +903,6 @@
   m_pContext->GetXFADocView()->UpdateDocView();
 }
 
-bool CPDFXFA_DocEnvironment::NotifySubmit(bool bPrevOrPost) {
-  if (bPrevOrPost)
-    return OnBeforeNotifySubmit();
-
-  OnAfterNotifySubmit();
-  return true;
-}
-
 bool CPDFXFA_DocEnvironment::SubmitInternal(CXFA_FFDoc* hDoc,
                                             CXFA_Submit* submit) {
   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h
index ebb86c4..6022434 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,17 +10,18 @@
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "public/fpdfview.h"
-#include "xfa/fxfa/fxfa.h"
+#include "xfa/fxfa/cxfa_ffdoc.h"
 
+class CFX_XMLDocument;
 class CPDFXFA_Context;
 class IJS_Runtime;
 
-class CPDFXFA_DocEnvironment final : public IXFA_DocEnvironment {
+class CPDFXFA_DocEnvironment final : public CXFA_FFDoc::CallbackIface {
  public:
   explicit CPDFXFA_DocEnvironment(CPDFXFA_Context*);
   ~CPDFXFA_DocEnvironment() override;
 
-  // IXFA_DocEnvironment:
+  // CFXA_FFDoc::CallbackIface:
   void SetChangeMark(CXFA_FFDoc* hDoc) override;
   void InvalidateRect(CXFA_FFPageView* pPageView, const CFX_RectF& rt) override;
   void DisplayCaret(CXFA_FFWidget* hWidget,
@@ -32,29 +33,31 @@
                    const CFX_RectF& rtAnchor,
                    CFX_RectF* pPopupRect) override;
   bool PopupMenu(CXFA_FFWidget* hWidget, const CFX_PointF& ptPopup) override;
-  void PageViewEvent(CXFA_FFPageView* pPageView, uint32_t dwFlags) override;
+  void OnPageViewEvent(CXFA_FFPageView* pPageView,
+                       CXFA_FFDoc::PageViewEvent eEvent) override;
   void WidgetPostAdd(CXFA_FFWidget* hWidget) override;
   void WidgetPreRemove(CXFA_FFWidget* hWidget) override;
-  int32_t CountPages(CXFA_FFDoc* hDoc) override;
-  int32_t GetCurrentPage(CXFA_FFDoc* hDoc) override;
+  int32_t CountPages(const CXFA_FFDoc* hDoc) const override;
+  int32_t GetCurrentPage(const CXFA_FFDoc* hDoc) const override;
   void SetCurrentPage(CXFA_FFDoc* hDoc, int32_t iCurPage) override;
-  bool IsCalculationsEnabled(CXFA_FFDoc* hDoc) override;
+  bool IsCalculationsEnabled(const CXFA_FFDoc* hDoc) const override;
   void SetCalculationsEnabled(CXFA_FFDoc* hDoc, bool bEnabled) override;
-  void GetTitle(CXFA_FFDoc* hDoc, WideString& wsTitle) override;
+  WideString GetTitle(const CXFA_FFDoc* hDoc) const override;
   void SetTitle(CXFA_FFDoc* hDoc, const WideString& wsTitle) override;
   void ExportData(CXFA_FFDoc* hDoc,
                   const WideString& wsFilePath,
                   bool bXDP) override;
   void GotoURL(CXFA_FFDoc* hDoc, const WideString& bsURL) override;
-  bool IsValidationsEnabled(CXFA_FFDoc* hDoc) override;
+  bool IsValidationsEnabled(const CXFA_FFDoc* hDoc) const override;
   void SetValidationsEnabled(CXFA_FFDoc* hDoc, bool bEnabled) override;
   void SetFocusWidget(CXFA_FFDoc* hDoc, CXFA_FFWidget* hWidget) override;
   void Print(CXFA_FFDoc* hDoc,
              int32_t nStartPage,
              int32_t nEndPage,
-             uint32_t dwOptions) override;
-  FX_ARGB GetHighlightColor(CXFA_FFDoc* hDoc) override;
-  IJS_Runtime* GetIJSRuntime(CXFA_FFDoc* hDoc) const override;
+             Mask<XFA_PrintOpt> dwOptions) override;
+  FX_ARGB GetHighlightColor(const CXFA_FFDoc* hDoc) const override;
+  IJS_Runtime* GetIJSRuntime(const CXFA_FFDoc* hDoc) const override;
+  CFX_XMLDocument* GetXMLDoc() const override;
   RetainPtr<IFX_SeekableReadStream> OpenLinkedFile(
       CXFA_FFDoc* hDoc,
       const WideString& wsLink) override;
@@ -78,7 +81,6 @@
   void ToXFAContentFlags(WideString csSrcContent, FPDF_DWORD& flag);
   bool OnBeforeNotifySubmit();
   void OnAfterNotifySubmit();
-  bool NotifySubmit(bool bPrevOrPost);
   bool SubmitInternal(CXFA_FFDoc* hDoc, CXFA_Submit* submit);
 #endif  // PDF_XFA_ELEMENT_SUBMIT_ENABLED
 
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment_embeddertest.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment_embeddertest.cpp
index 1dce59c..09c3d1e 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment_embeddertest.cpp
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment_embeddertest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_page.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_page.cpp
index 9797429..6f4f2b7 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_page.cpp
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_page.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,25 +7,73 @@
 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
 
 #include <memory>
+#include <utility>
 
 #include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
 #include "xfa/fxfa/cxfa_ffdocview.h"
 #include "xfa/fxfa/cxfa_ffpageview.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
 #include "xfa/fxfa/cxfa_ffwidgethandler.h"
-#include "xfa/fxfa/cxfa_rendercontext.h"
-#include "xfa/fxgraphics/cxfa_graphics.h"
+
+namespace {
+
+constexpr Mask<XFA_WidgetStatus> kIteratorFilter = {
+    XFA_WidgetStatus::kVisible,
+    XFA_WidgetStatus::kViewable,
+    XFA_WidgetStatus::kFocused,
+};
+
+CXFA_FFWidget::IteratorIface* GCedWidgetIteratorForPage(
+    CXFA_FFPageView* pFFPageView,
+    CPDFSDK_PageView* pPageView) {
+  if (!pFFPageView)
+    return nullptr;
+
+  ObservedPtr<CPDFSDK_PageView> pWatchedPageView(pPageView);
+  CXFA_FFWidget::IteratorIface* pIterator =
+      pFFPageView->CreateGCedTraverseWidgetIterator(kIteratorFilter);
+
+  // Check |pPageView| again because JS may have destroyed it.
+  return pWatchedPageView ? pIterator : nullptr;
+}
+
+CXFA_FFWidget::IteratorIface* GCedWidgetIteratorForAnnot(
+    CXFA_FFPageView* pFFPageView,
+    CPDFSDK_Annot* pSDKAnnot) {
+  if (!pFFPageView)
+    return nullptr;
+
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pSDKAnnot);
+  if (!pXFAWidget)
+    return nullptr;
+
+  ObservedPtr<CPDFSDK_Annot> pObservedAnnot(pSDKAnnot);
+  CXFA_FFWidget::IteratorIface* pWidgetIterator =
+      pFFPageView->CreateGCedTraverseWidgetIterator(kIteratorFilter);
+
+  // Check |pSDKAnnot| again because JS may have destroyed it.
+  if (!pObservedAnnot)
+    return nullptr;
+
+  if (pWidgetIterator->GetCurrentWidget() != pXFAWidget->GetXFAFFWidget())
+    pWidgetIterator->SetCurrentWidget(pXFAWidget->GetXFAFFWidget());
+
+  return pWidgetIterator;
+}
+
+}  // namespace
 
 CPDFXFA_Page::CPDFXFA_Page(CPDF_Document* pDocument, int page_index)
     : m_pDocument(pDocument), m_iPageIndex(page_index) {
-  ASSERT(m_pDocument->GetExtension());
-  ASSERT(m_iPageIndex >= 0);
+  DCHECK(m_pDocument->GetExtension());
+  DCHECK(m_iPageIndex >= 0);
 }
 
 CPDFXFA_Page::~CPDFXFA_Page() = default;
@@ -39,17 +87,17 @@
 }
 
 CPDF_Document* CPDFXFA_Page::GetDocument() const {
-  return m_pDocument.Get();
+  return m_pDocument;
 }
 
 bool CPDFXFA_Page::LoadPDFPage() {
-  CPDF_Document* pPDFDoc = GetDocument();
-  CPDF_Dictionary* pDict = pPDFDoc->GetPageDictionary(m_iPageIndex);
+  RetainPtr<CPDF_Dictionary> pDict =
+      GetDocument()->GetMutablePageDictionary(m_iPageIndex);
   if (!pDict)
     return false;
 
   if (!m_pPDFPage || m_pPDFPage->GetDict() != pDict)
-    LoadPDFPageFromDict(pDict);
+    LoadPDFPageFromDict(std::move(pDict));
 
   return true;
 }
@@ -70,15 +118,13 @@
     case FormType::kXFAFull:
       return !!GetXFAPageView();
   }
-  NOTREACHED();
-  return false;
 }
 
-void CPDFXFA_Page::LoadPDFPageFromDict(CPDF_Dictionary* pPageDict) {
-  ASSERT(pPageDict);
-  m_pPDFPage = pdfium::MakeRetain<CPDF_Page>(GetDocument(), pPageDict);
-  m_pPDFPage->SetRenderCache(
-      pdfium::MakeUnique<CPDF_PageRenderCache>(m_pPDFPage.Get()));
+void CPDFXFA_Page::LoadPDFPageFromDict(RetainPtr<CPDF_Dictionary> pPageDict) {
+  DCHECK(pPageDict);
+  m_pPDFPage =
+      pdfium::MakeRetain<CPDF_Page>(GetDocument(), std::move(pPageDict));
+  m_pPDFPage->AddPageImageCache();
   m_pPDFPage->ParseContent();
 }
 
@@ -94,7 +140,7 @@
     case FormType::kXFAForeground:
       if (m_pPDFPage)
         return m_pPDFPage->GetPageWidth();
-      FALLTHROUGH;
+      [[fallthrough]];
     case FormType::kXFAFull:
       if (pPageView)
         return pPageView->GetPageViewRect().width;
@@ -116,7 +162,7 @@
     case FormType::kXFAForeground:
       if (m_pPDFPage)
         return m_pPDFPage->GetPageHeight();
-      FALLTHROUGH;
+      [[fallthrough]];
     case FormType::kXFAFull:
       if (pPageView)
         return pPageView->GetPageViewRect().height;
@@ -126,26 +172,25 @@
   return 0.0f;
 }
 
-Optional<CFX_PointF> CPDFXFA_Page::DeviceToPage(
+absl::optional<CFX_PointF> CPDFXFA_Page::DeviceToPage(
     const FX_RECT& rect,
     int rotate,
     const CFX_PointF& device_point) const {
   CXFA_FFPageView* pPageView = GetXFAPageView();
   if (!m_pPDFPage && !pPageView)
-    return {};
+    return absl::nullopt;
 
-  CFX_PointF pos =
-      GetDisplayMatrix(rect, rotate).GetInverse().Transform(device_point);
-  return pos;
+  CFX_Matrix page2device = GetDisplayMatrix(rect, rotate);
+  return page2device.GetInverse().Transform(device_point);
 }
 
-Optional<CFX_PointF> CPDFXFA_Page::PageToDevice(
+absl::optional<CFX_PointF> CPDFXFA_Page::PageToDevice(
     const FX_RECT& rect,
     int rotate,
     const CFX_PointF& page_point) const {
   CXFA_FFPageView* pPageView = GetXFAPageView();
   if (!m_pPDFPage && !pPageView)
-    return {};
+    return absl::nullopt;
 
   CFX_Matrix page2device = GetDisplayMatrix(rect, rotate);
   return page2device.Transform(page_point);
@@ -164,7 +209,7 @@
     case FormType::kXFAForeground:
       if (m_pPDFPage)
         return m_pPDFPage->GetDisplayMatrix(rect, iRotate);
-      FALLTHROUGH;
+      [[fallthrough]];
     case FormType::kXFAFull:
       if (pPageView)
         return pPageView->GetDisplayMatrix(rect, iRotate);
@@ -174,32 +219,44 @@
   return CFX_Matrix();
 }
 
-CPDFSDK_Annot* CPDFXFA_Page::GetNextXFAAnnot(CPDFSDK_Annot* pSDKAnnot,
-                                             bool bNext) {
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pSDKAnnot);
-  if (!pXFAWidget)
+CPDFSDK_Annot* CPDFXFA_Page::GetNextXFAAnnot(CPDFSDK_Annot* pSDKAnnot) const {
+  CXFA_FFWidget::IteratorIface* pWidgetIterator =
+      GCedWidgetIteratorForAnnot(GetXFAPageView(), pSDKAnnot);
+  if (!pWidgetIterator)
     return nullptr;
 
-  ObservedPtr<CPDFSDK_Annot> pObservedAnnot(pSDKAnnot);
-  CPDFSDK_PageView* pPageView = pSDKAnnot->GetPageView();
-  std::unique_ptr<IXFA_WidgetIterator> pWidgetIterator =
-      GetXFAPageView()->CreateTraverseWidgetIterator(XFA_WidgetStatus_Visible |
-                                                     XFA_WidgetStatus_Viewable |
-                                                     XFA_WidgetStatus_Focused);
+  return pSDKAnnot->GetPageView()->GetAnnotForFFWidget(
+      pWidgetIterator->MoveToNext());
+}
 
-  // Check |pSDKAnnot| again because JS may have destroyed it
-  if (!pObservedAnnot)
+CPDFSDK_Annot* CPDFXFA_Page::GetPrevXFAAnnot(CPDFSDK_Annot* pSDKAnnot) const {
+  CXFA_FFWidget::IteratorIface* pWidgetIterator =
+      GCedWidgetIteratorForAnnot(GetXFAPageView(), pSDKAnnot);
+  if (!pWidgetIterator)
     return nullptr;
 
-  if (pWidgetIterator->GetCurrentWidget() != pXFAWidget->GetXFAFFWidget())
-    pWidgetIterator->SetCurrentWidget(pXFAWidget->GetXFAFFWidget());
+  return pSDKAnnot->GetPageView()->GetAnnotForFFWidget(
+      pWidgetIterator->MoveToPrevious());
+}
 
-  CXFA_FFWidget* hNextFocus =
-      bNext ? pWidgetIterator->MoveToNext() : pWidgetIterator->MoveToPrevious();
-  if (!hNextFocus && pSDKAnnot)
-    hNextFocus = pWidgetIterator->MoveToFirst();
+CPDFSDK_Annot* CPDFXFA_Page::GetFirstXFAAnnot(
+    CPDFSDK_PageView* page_view) const {
+  CXFA_FFWidget::IteratorIface* pWidgetIterator =
+      GCedWidgetIteratorForPage(GetXFAPageView(), page_view);
+  if (!pWidgetIterator)
+    return nullptr;
 
-  return pPageView->GetAnnotByXFAWidget(hNextFocus);
+  return page_view->GetAnnotForFFWidget(pWidgetIterator->MoveToFirst());
+}
+
+CPDFSDK_Annot* CPDFXFA_Page::GetLastXFAAnnot(
+    CPDFSDK_PageView* page_view) const {
+  CXFA_FFWidget::IteratorIface* pWidgetIterator =
+      GCedWidgetIteratorForPage(GetXFAPageView(), page_view);
+  if (!pWidgetIterator)
+    return nullptr;
+
+  return page_view->GetAnnotForFFWidget(pWidgetIterator->MoveToLast());
 }
 
 int CPDFXFA_Page::HasFormFieldAtPoint(const CFX_PointF& point) const {
@@ -214,12 +271,11 @@
   CXFA_FFWidgetHandler* pWidgetHandler = pDocView->GetWidgetHandler();
   if (!pWidgetHandler)
     return -1;
-
-  std::unique_ptr<IXFA_WidgetIterator> pWidgetIterator =
-      pPageView->CreateFormWidgetIterator(XFA_WidgetStatus_Viewable);
+  CXFA_FFPageWidgetIterator pWidgetIterator(pPageView,
+                                            XFA_WidgetStatus::kViewable);
 
   CXFA_FFWidget* pXFAAnnot;
-  while ((pXFAAnnot = pWidgetIterator->MoveToNext()) != nullptr) {
+  while ((pXFAAnnot = pWidgetIterator.MoveToNext()) != nullptr) {
     if (pXFAAnnot->GetFormFieldType() == FormFieldType::kXFA)
       continue;
 
@@ -237,12 +293,25 @@
                                   const CFX_Matrix& mtUser2Device,
                                   const FX_RECT& rtClip) {
   CFX_RectF rectClip(rtClip);
-  CXFA_Graphics gs(pDevice);
+  CFGAS_GEGraphics gs(pDevice);
   gs.SetClipRect(rectClip);
 
   CXFA_FFPageView* xfaView = GetXFAPageView();
-  CXFA_RenderContext renderContext(xfaView, rectClip, mtUser2Device);
-  renderContext.DoRender(&gs);
+  CXFA_FFPageWidgetIterator pWidgetIterator(
+      xfaView, Mask<XFA_WidgetStatus>{XFA_WidgetStatus::kVisible,
+                                      XFA_WidgetStatus::kViewable});
+
+  while (true) {
+    CXFA_FFWidget* pWidget = pWidgetIterator.MoveToNext();
+    if (!pWidget)
+      break;
+
+    CFX_RectF rtWidgetBox = pWidget->GetBBox(CXFA_FFWidget::kDoNotDrawFocus);
+    ++rtWidgetBox.width;
+    ++rtWidgetBox.height;
+    if (rtWidgetBox.IntersectWith(rectClip))
+      pWidget->RenderWidget(&gs, mtUser2Device, CXFA_FFWidget::kHighlight);
+  }
 
   CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
   if (!pXFAWidget)
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_page.h b/fpdfsdk/fpdfxfa/cpdfxfa_page.h
index 73d5421..1b05562 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_page.h
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_page.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,21 +10,20 @@
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/page/ipdf_page.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CFX_RenderDevice;
 class CPDF_Dictionary;
 class CPDF_Document;
 class CPDFSDK_Annot;
+class CPDFSDK_PageView;
 class CXFA_FFPageView;
 
 class CPDFXFA_Page final : public IPDF_Page {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // IPDF_Page:
   CPDF_Page* AsPDFPage() override;
@@ -33,21 +32,24 @@
   float GetPageWidth() const override;
   float GetPageHeight() const override;
   CFX_Matrix GetDisplayMatrix(const FX_RECT& rect, int iRotate) const override;
-  Optional<CFX_PointF> DeviceToPage(
+  absl::optional<CFX_PointF> DeviceToPage(
       const FX_RECT& rect,
       int rotate,
       const CFX_PointF& device_point) const override;
-  Optional<CFX_PointF> PageToDevice(
+  absl::optional<CFX_PointF> PageToDevice(
       const FX_RECT& rect,
       int rotate,
       const CFX_PointF& page_point) const override;
 
   bool LoadPage();
-  void LoadPDFPageFromDict(CPDF_Dictionary* pPageDict);
+  void LoadPDFPageFromDict(RetainPtr<CPDF_Dictionary> pPageDict);
   int GetPageIndex() const { return m_iPageIndex; }
   void SetXFAPageViewIndex(int index) { m_iPageIndex = index; }
   CXFA_FFPageView* GetXFAPageView() const;
-  CPDFSDK_Annot* GetNextXFAAnnot(CPDFSDK_Annot* pSDKAnnot, bool bNext);
+  CPDFSDK_Annot* GetNextXFAAnnot(CPDFSDK_Annot* pSDKAnnot) const;
+  CPDFSDK_Annot* GetPrevXFAAnnot(CPDFSDK_Annot* pSDKAnnot) const;
+  CPDFSDK_Annot* GetFirstXFAAnnot(CPDFSDK_PageView* page_view) const;
+  CPDFSDK_Annot* GetLastXFAAnnot(CPDFSDK_PageView* page_view) const;
   int HasFormFieldAtPoint(const CFX_PointF& point) const;
   void DrawFocusAnnot(CFX_RenderDevice* pDevice,
                       CPDFSDK_Annot* pAnnot,
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_widget.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_widget.cpp
index f4c9934..1367c1a 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_widget.cpp
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_widget.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,15 +6,218 @@
 
 #include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h"
 
-#include "fpdfsdk/ipdfsdk_annothandler.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "fpdfsdk/cpdfsdk_pageview.h"
+#include "third_party/base/check.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+#include "xfa/fxfa/cxfa_ffdocview.h"
+#include "xfa/fxfa/cxfa_ffpageview.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
+#include "xfa/fxfa/cxfa_ffwidgethandler.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+
+#define CHECK_FWL_VKEY_ENUM____(name)                                   \
+  static_assert(static_cast<int>(name) == static_cast<int>(XFA_##name), \
+                "FWL_VKEYCODE enum mismatch")
+
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Back);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Tab);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NewLine);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Clear);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Return);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Shift);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Control);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Menu);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Pause);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Capital);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Kana);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Hangul);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Junja);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Final);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Hanja);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Kanji);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Escape);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Convert);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NonConvert);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Accept);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_ModeChange);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Space);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Prior);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Next);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_End);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Home);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Left);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Up);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Right);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Down);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Select);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Print);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Execute);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Snapshot);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Insert);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Delete);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Help);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_0);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_1);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_2);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_3);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_4);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_5);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_6);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_7);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_8);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_9);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_A);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_B);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_C);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_D);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_E);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_G);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_H);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_I);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_J);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_K);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_L);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_M);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_N);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_O);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_P);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Q);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_R);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_S);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_T);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_U);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_V);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_W);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_X);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Y);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Z);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LWin);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Command);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RWin);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Apps);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Sleep);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad0);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad1);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad2);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad3);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad4);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad5);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad6);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad7);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad8);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad9);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Multiply);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Add);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Separator);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Subtract);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Decimal);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Divide);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F1);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F2);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F3);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F4);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F5);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F6);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F7);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F8);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F9);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F10);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F11);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F12);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F13);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F14);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F15);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F16);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F17);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F18);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F19);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F20);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F21);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F22);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F23);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F24);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NunLock);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Scroll);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LShift);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RShift);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LControl);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RControl);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LMenu);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RMenu);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Back);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Forward);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Refresh);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Stop);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Search);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Favorites);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Home);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_VOLUME_Mute);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_VOLUME_Down);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_VOLUME_Up);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_NEXT_Track);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_PREV_Track);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_Stop);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_PLAY_Pause);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_Mail);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_MEDIA_Select);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_APP1);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_APP2);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_1);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Plus);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Comma);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Minus);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Period);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_2);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_3);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_4);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_5);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_6);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_7);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_8);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_102);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_ProcessKey);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Packet);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Attn);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Crsel);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Exsel);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Ereof);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Play);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Zoom);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NoName);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_PA1);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Clear);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Unknown);
+
+#undef CHECK_FWL_VKEY_ENUM____
+
+namespace {
+
+Mask<XFA_FWL_KeyFlag> GetKeyFlags(Mask<FWL_EVENTFLAG> input) {
+  Mask<XFA_FWL_KeyFlag> results;
+
+  if (input & FWL_EVENTFLAG_ControlKey)
+    results |= XFA_FWL_KeyFlag::kCtrl;
+  if (input & FWL_EVENTFLAG_LeftButtonDown)
+    results |= XFA_FWL_KeyFlag::kLButton;
+  if (input & FWL_EVENTFLAG_MiddleButtonDown)
+    results |= XFA_FWL_KeyFlag::kMButton;
+  if (input & FWL_EVENTFLAG_RightButtonDown)
+    results |= XFA_FWL_KeyFlag::kRButton;
+  if (input & FWL_EVENTFLAG_ShiftKey)
+    results |= XFA_FWL_KeyFlag::kShift;
+  if (input & FWL_EVENTFLAG_AltKey)
+    results |= XFA_FWL_KeyFlag::kAlt;
+
+  return results;
+}
+
+}  // namespace
 
 CPDFXFA_Widget::CPDFXFA_Widget(CXFA_FFWidget* pXFAFFWidget,
-                               CPDFSDK_PageView* pPageView,
-                               CPDFSDK_InteractiveForm* pInteractiveForm)
-    : CPDFSDK_Annot(pPageView),
-      m_pInteractiveForm(pInteractiveForm),
-      m_pXFAFFWidget(pXFAFFWidget) {}
+                               CPDFSDK_PageView* pPageView)
+    : CPDFSDK_Annot(pPageView), m_pXFAFFWidget(pXFAFFWidget) {}
 
 CPDFXFA_Widget::~CPDFXFA_Widget() = default;
 
@@ -22,10 +225,223 @@
   return this;
 }
 
+CPDFSDK_Annot::UnsafeInputHandlers* CPDFXFA_Widget::GetUnsafeInputHandlers() {
+  return this;
+}
+
 CPDF_Annot::Subtype CPDFXFA_Widget::GetAnnotSubtype() const {
   return CPDF_Annot::Subtype::XFAWIDGET;
 }
 
 CFX_FloatRect CPDFXFA_Widget::GetRect() const {
-  return GetXFAFFWidget()->GetLayoutItem()->GetRect(false).ToFloatRect();
+  return GetXFAFFWidget()->GetLayoutItem()->GetAbsoluteRect().ToFloatRect();
+}
+
+void CPDFXFA_Widget::OnDraw(CFX_RenderDevice* pDevice,
+                            const CFX_Matrix& mtUser2Device,
+                            bool bDrawAnnots) {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  if (!widget_handler)
+    return;
+
+  CFGAS_GEGraphics gs(pDevice);
+  bool is_highlight = GetPageView()->GetFormFillEnv()->GetFocusAnnot() != this;
+  widget_handler->RenderWidget(GetXFAFFWidget(), &gs, mtUser2Device,
+                               is_highlight);
+
+  // to do highlight and shadow
+}
+
+bool CPDFXFA_Widget::DoHitTest(const CFX_PointF& point) {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  if (!widget_handler)
+    return false;
+
+  return widget_handler->HitTest(GetXFAFFWidget(), point) !=
+         FWL_WidgetHit::Unknown;
+}
+
+bool CPDFXFA_Widget::OnChangedFocus() {
+  CXFA_FFDocView* doc_view = GetDocView();
+  if (!doc_view)
+    return false;
+
+  CXFA_FFWidget* widget = GetXFAFFWidget();
+  if (doc_view->SetFocus(widget))
+    return false;
+
+  return doc_view->GetFocusWidget() != widget;
+}
+
+CFX_FloatRect CPDFXFA_Widget::GetViewBBox() {
+  CXFA_FFWidget* widget = GetXFAFFWidget();
+  CXFA_Node* node = widget->GetNode();
+  DCHECK(node->IsWidgetReady());
+
+  CFX_RectF bbox =
+      widget->GetBBox(node->GetFFWidgetType() == XFA_FFWidgetType::kSignature
+                          ? CXFA_FFWidget::kDrawFocus
+                          : CXFA_FFWidget::kDoNotDrawFocus);
+
+  CFX_FloatRect result = bbox.ToFloatRect();
+  result.Inflate(1.0f, 1.0f);
+  return result;
+}
+
+void CPDFXFA_Widget::OnMouseEnter(Mask<FWL_EVENTFLAG> nFlags) {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  if (widget_handler)
+    widget_handler->OnMouseEnter(GetXFAFFWidget());
+}
+
+void CPDFXFA_Widget::OnMouseExit(Mask<FWL_EVENTFLAG> nFlags) {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  if (widget_handler)
+    widget_handler->OnMouseExit(GetXFAFFWidget());
+}
+
+bool CPDFXFA_Widget::OnLButtonDown(Mask<FWL_EVENTFLAG> nFlags,
+                                   const CFX_PointF& point) {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  return widget_handler && widget_handler->OnLButtonDown(
+                               GetXFAFFWidget(), GetKeyFlags(nFlags), point);
+}
+
+bool CPDFXFA_Widget::OnLButtonUp(Mask<FWL_EVENTFLAG> nFlags,
+                                 const CFX_PointF& point) {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  return widget_handler && widget_handler->OnLButtonUp(
+                               GetXFAFFWidget(), GetKeyFlags(nFlags), point);
+}
+
+bool CPDFXFA_Widget::OnLButtonDblClk(Mask<FWL_EVENTFLAG> nFlags,
+                                     const CFX_PointF& point) {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  return widget_handler && widget_handler->OnLButtonDblClk(
+                               GetXFAFFWidget(), GetKeyFlags(nFlags), point);
+}
+
+bool CPDFXFA_Widget::OnMouseMove(Mask<FWL_EVENTFLAG> nFlags,
+                                 const CFX_PointF& point) {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  return widget_handler && widget_handler->OnMouseMove(
+                               GetXFAFFWidget(), GetKeyFlags(nFlags), point);
+}
+
+bool CPDFXFA_Widget::OnMouseWheel(Mask<FWL_EVENTFLAG> nFlags,
+                                  const CFX_PointF& point,
+                                  const CFX_Vector& delta) {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  return widget_handler &&
+         widget_handler->OnMouseWheel(GetXFAFFWidget(), GetKeyFlags(nFlags),
+                                      point, delta);
+}
+
+bool CPDFXFA_Widget::OnRButtonDown(Mask<FWL_EVENTFLAG> nFlags,
+                                   const CFX_PointF& point) {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  return widget_handler && widget_handler->OnRButtonDown(
+                               GetXFAFFWidget(), GetKeyFlags(nFlags), point);
+}
+
+bool CPDFXFA_Widget::OnRButtonUp(Mask<FWL_EVENTFLAG> nFlags,
+                                 const CFX_PointF& point) {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  return widget_handler && widget_handler->OnRButtonUp(
+                               GetXFAFFWidget(), GetKeyFlags(nFlags), point);
+}
+
+bool CPDFXFA_Widget::OnChar(uint32_t nChar, Mask<FWL_EVENTFLAG> nFlags) {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  return widget_handler &&
+         widget_handler->OnChar(GetXFAFFWidget(), nChar, GetKeyFlags(nFlags));
+}
+
+bool CPDFXFA_Widget::OnKeyDown(FWL_VKEYCODE nKeyCode,
+                               Mask<FWL_EVENTFLAG> nFlags) {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  return widget_handler &&
+         widget_handler->OnKeyDown(GetXFAFFWidget(),
+                                   static_cast<XFA_FWL_VKEYCODE>(nKeyCode),
+                                   GetKeyFlags(nFlags));
+}
+
+bool CPDFXFA_Widget::OnSetFocus(Mask<FWL_EVENTFLAG> nFlags) {
+  return true;
+}
+
+bool CPDFXFA_Widget::OnKillFocus(Mask<FWL_EVENTFLAG> nFlags) {
+  CXFA_FFDocView* doc_view = GetDocView();
+  if (doc_view)
+    doc_view->SetFocus(nullptr);
+  return true;
+}
+
+bool CPDFXFA_Widget::CanUndo() {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  return widget_handler && widget_handler->CanUndo(GetXFAFFWidget());
+}
+
+bool CPDFXFA_Widget::CanRedo() {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  return widget_handler && widget_handler->CanRedo(GetXFAFFWidget());
+}
+
+bool CPDFXFA_Widget::Undo() {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  return widget_handler && widget_handler->Undo(GetXFAFFWidget());
+}
+
+bool CPDFXFA_Widget::Redo() {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  return widget_handler && widget_handler->Redo(GetXFAFFWidget());
+}
+
+WideString CPDFXFA_Widget::GetText() {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  if (!widget_handler)
+    return WideString();
+  return widget_handler->GetText(GetXFAFFWidget());
+}
+
+WideString CPDFXFA_Widget::GetSelectedText() {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  if (!widget_handler)
+    return WideString();
+  return widget_handler->GetSelectedText(GetXFAFFWidget());
+}
+
+void CPDFXFA_Widget::ReplaceAndKeepSelection(const WideString& text) {
+  // XFA does not seem to support IME input at all. Therefore we don't bother
+  // to keep selection for IMEs.
+  ReplaceSelection(text);
+}
+
+void CPDFXFA_Widget::ReplaceSelection(const WideString& text) {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  if (widget_handler)
+    widget_handler->PasteText(GetXFAFFWidget(), text);
+}
+
+bool CPDFXFA_Widget::SelectAllText() {
+  CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
+  return widget_handler && widget_handler->SelectAllText(GetXFAFFWidget());
+}
+
+bool CPDFXFA_Widget::SetIndexSelected(int index, bool selected) {
+  return false;
+}
+
+bool CPDFXFA_Widget::IsIndexSelected(int index) {
+  return false;
+}
+
+CXFA_FFDocView* CPDFXFA_Widget::GetDocView() {
+  CXFA_FFPageView* page_view = GetXFAFFWidget()->GetPageView();
+  return page_view ? page_view->GetDocView() : nullptr;
+}
+
+CXFA_FFWidgetHandler* CPDFXFA_Widget::GetWidgetHandler() {
+  CXFA_FFDocView* doc_view = GetDocView();
+  return doc_view ? doc_view->GetWidgetHandler() : nullptr;
 }
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_widget.h b/fpdfsdk/fpdfxfa/cpdfxfa_widget.h
index 76d9878..1a74213 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_widget.h
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_widget.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,35 +8,74 @@
 #define FPDFSDK_FPDFXFA_CPDFXFA_WIDGET_H_
 
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/observed_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
+#include "v8/include/cppgc/persistent.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
 
-class CPDFSDK_InteractiveForm;
 class CPDFSDK_PageView;
+class CXFA_FFDocView;
+class CXFA_FFWidgetHandler;
 
-class CPDFXFA_Widget final : public CPDFSDK_Annot {
+class CPDFXFA_Widget final : public CPDFSDK_Annot,
+                             CPDFSDK_Annot::UnsafeInputHandlers {
  public:
-  CPDFXFA_Widget(CXFA_FFWidget* pXFAFFWidget,
-                 CPDFSDK_PageView* pPageView,
-                 CPDFSDK_InteractiveForm* pInteractiveForm);
+  CPDFXFA_Widget(CXFA_FFWidget* pXFAFFWidget, CPDFSDK_PageView* pPageView);
   ~CPDFXFA_Widget() override;
 
   // CPDFSDK_Annot:
   CPDFXFA_Widget* AsXFAWidget() override;
+  CPDFSDK_Annot::UnsafeInputHandlers* GetUnsafeInputHandlers() override;
   CPDF_Annot::Subtype GetAnnotSubtype() const override;
   CFX_FloatRect GetRect() const override;
+  void OnDraw(CFX_RenderDevice* pDevice,
+              const CFX_Matrix& mtUser2Device,
+              bool bDrawAnnots) override;
+  bool DoHitTest(const CFX_PointF& point) override;
+  CFX_FloatRect GetViewBBox() override;
+  bool CanUndo() override;
+  bool CanRedo() override;
+  bool Undo() override;
+  bool Redo() override;
+  WideString GetText() override;
+  WideString GetSelectedText() override;
+  void ReplaceAndKeepSelection(const WideString& text) override;
+  void ReplaceSelection(const WideString& text) override;
+  bool SelectAllText() override;
+  bool SetIndexSelected(int index, bool selected) override;
+  bool IsIndexSelected(int index) override;
 
   CXFA_FFWidget* GetXFAFFWidget() const { return m_pXFAFFWidget.Get(); }
-  CPDFSDK_InteractiveForm* GetInteractiveForm() const {
-    return m_pInteractiveForm.Get();
-  }
+
+  bool OnChangedFocus();
 
  private:
-  UnownedPtr<CPDFSDK_InteractiveForm> const m_pInteractiveForm;
-  ObservedPtr<CXFA_FFWidget> const m_pXFAFFWidget;
+  // CPDFSDK_Annot::UnsafeInputHandlers:
+  void OnMouseEnter(Mask<FWL_EVENTFLAG> nFlags) override;
+  void OnMouseExit(Mask<FWL_EVENTFLAG> nFlags) override;
+  bool OnLButtonDown(Mask<FWL_EVENTFLAG> nFlags,
+                     const CFX_PointF& point) override;
+  bool OnLButtonUp(Mask<FWL_EVENTFLAG> nFlags,
+                   const CFX_PointF& point) override;
+  bool OnLButtonDblClk(Mask<FWL_EVENTFLAG> nFlags,
+                       const CFX_PointF& point) override;
+  bool OnMouseMove(Mask<FWL_EVENTFLAG> nFlags,
+                   const CFX_PointF& point) override;
+  bool OnMouseWheel(Mask<FWL_EVENTFLAG> nFlags,
+                    const CFX_PointF& point,
+                    const CFX_Vector& delta) override;
+  bool OnRButtonDown(Mask<FWL_EVENTFLAG> nFlags,
+                     const CFX_PointF& point) override;
+  bool OnRButtonUp(Mask<FWL_EVENTFLAG> nFlags,
+                   const CFX_PointF& point) override;
+  bool OnChar(uint32_t nChar, Mask<FWL_EVENTFLAG> nFlags) override;
+  bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlags) override;
+  bool OnSetFocus(Mask<FWL_EVENTFLAG> nFlags) override;
+  bool OnKillFocus(Mask<FWL_EVENTFLAG> nFlags) override;
+
+  CXFA_FFDocView* GetDocView();
+  CXFA_FFWidgetHandler* GetWidgetHandler();
+
+  cppgc::Persistent<CXFA_FFWidget> const m_pXFAFFWidget;
 };
 
 #endif  // FPDFSDK_FPDFXFA_CPDFXFA_WIDGET_H_
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.cpp
deleted file mode 100644
index 3012b0d..0000000
--- a/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.cpp
+++ /dev/null
@@ -1,667 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h"
-
-#include "fpdfsdk/cpdfsdk_annot.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_interactiveform.h"
-#include "fpdfsdk/cpdfsdk_pageview.h"
-#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h"
-#include "public/fpdf_fwlevent.h"
-#include "xfa/fwl/cfwl_app.h"
-#include "xfa/fwl/fwl_widgetdef.h"
-#include "xfa/fwl/fwl_widgethit.h"
-#include "xfa/fxfa/cxfa_ffdocview.h"
-#include "xfa/fxfa/cxfa_ffpageview.h"
-#include "xfa/fxfa/cxfa_ffwidget.h"
-#include "xfa/fxfa/cxfa_ffwidgethandler.h"
-#include "xfa/fxfa/fxfa_basic.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxgraphics/cxfa_graphics.h"
-
-#define CHECK_FWL_VKEY_ENUM____(name)                                   \
-  static_assert(static_cast<int>(name) == static_cast<int>(XFA_##name), \
-                "FWL_VKEYCODE enum mismatch")
-
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Back);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Tab);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NewLine);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Clear);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Return);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Shift);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Control);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Menu);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Pause);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Capital);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Kana);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Hangul);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Junja);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Final);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Hanja);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Kanji);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Escape);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Convert);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NonConvert);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Accept);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_ModeChange);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Space);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Prior);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Next);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_End);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Home);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Left);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Up);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Right);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Down);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Select);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Print);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Execute);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Snapshot);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Insert);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Delete);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Help);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_0);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_1);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_2);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_3);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_4);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_5);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_6);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_7);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_8);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_9);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_A);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_B);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_C);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_D);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_E);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_G);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_H);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_I);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_J);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_K);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_L);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_M);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_N);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_O);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_P);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Q);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_R);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_S);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_T);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_U);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_V);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_W);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_X);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Y);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Z);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LWin);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Command);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RWin);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Apps);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Sleep);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad0);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad1);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad2);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad3);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad4);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad5);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad6);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad7);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad8);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad9);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Multiply);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Add);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Separator);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Subtract);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Decimal);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Divide);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F1);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F2);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F3);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F4);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F5);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F6);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F7);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F8);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F9);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F10);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F11);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F12);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F13);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F14);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F15);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F16);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F17);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F18);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F19);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F20);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F21);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F22);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F23);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F24);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NunLock);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Scroll);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LShift);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RShift);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LControl);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RControl);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LMenu);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RMenu);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Back);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Forward);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Refresh);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Stop);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Search);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Favorites);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Home);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_VOLUME_Mute);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_VOLUME_Down);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_VOLUME_Up);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_NEXT_Track);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_PREV_Track);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_Stop);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_PLAY_Pause);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_Mail);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_MEDIA_Select);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_APP1);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_APP2);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_1);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Plus);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Comma);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Minus);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Period);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_2);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_3);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_4);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_5);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_6);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_7);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_8);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_102);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_ProcessKey);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Packet);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Attn);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Crsel);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Exsel);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Ereof);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Play);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Zoom);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NoName);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_PA1);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Clear);
-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Unknown);
-
-#undef CHECK_FWL_VKEY_ENUM____
-
-CPDFXFA_WidgetHandler::CPDFXFA_WidgetHandler() = default;
-
-CPDFXFA_WidgetHandler::~CPDFXFA_WidgetHandler() = default;
-
-void CPDFXFA_WidgetHandler::SetFormFillEnvironment(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pFormFillEnv = pFormFillEnv;
-}
-
-bool CPDFXFA_WidgetHandler::CanAnswer(CPDFSDK_Annot* pAnnot) {
-  CPDFXFA_Widget* pWidget = ToXFAWidget(pAnnot);
-  return pWidget && pWidget->GetXFAFFWidget();
-}
-
-CPDFSDK_Annot* CPDFXFA_WidgetHandler::NewAnnot(CPDF_Annot* pAnnot,
-                                               CPDFSDK_PageView* pPage) {
-  return nullptr;
-}
-
-std::unique_ptr<CPDFSDK_Annot> CPDFXFA_WidgetHandler::NewAnnotForXFA(
-    CXFA_FFWidget* pAnnot,
-    CPDFSDK_PageView* pPage) {
-  CPDFSDK_InteractiveForm* pForm = m_pFormFillEnv->GetInteractiveForm();
-  return pdfium::MakeUnique<CPDFXFA_Widget>(pAnnot, pPage, pForm);
-}
-
-void CPDFXFA_WidgetHandler::OnDraw(CPDFSDK_PageView* pPageView,
-                                   CPDFSDK_Annot* pAnnot,
-                                   CFX_RenderDevice* pDevice,
-                                   const CFX_Matrix& mtUser2Device,
-                                   bool bDrawAnnots) {
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
-  ASSERT(pXFAWidget);
-
-  bool bIsHighlight = false;
-  if (pPageView->GetFormFillEnv()->GetFocusAnnot() != pAnnot)
-    bIsHighlight = true;
-
-  CXFA_Graphics gs(pDevice);
-  GetXFAFFWidgetHandler(pXFAWidget)
-      ->RenderWidget(pXFAWidget->GetXFAFFWidget(), &gs, mtUser2Device,
-                     bIsHighlight);
-
-  // to do highlight and shadow
-}
-
-void CPDFXFA_WidgetHandler::OnLoad(CPDFSDK_Annot* pAnnot) {}
-
-void CPDFXFA_WidgetHandler::ReleaseAnnot(
-    std::unique_ptr<CPDFSDK_Annot> pAnnot) {}
-
-CFX_FloatRect CPDFXFA_WidgetHandler::GetViewBBox(CPDFSDK_PageView* pPageView,
-                                                 CPDFSDK_Annot* pAnnot) {
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
-  CXFA_Node* node = pXFAWidget->GetXFAFFWidget()->GetNode();
-  ASSERT(node->IsWidgetReady());
-
-  CFX_RectF rcBBox = pXFAWidget->GetXFAFFWidget()->GetBBox(
-      node->GetFFWidgetType() == XFA_FFWidgetType::kSignature
-          ? CXFA_FFWidget::kDrawFocus
-          : CXFA_FFWidget::kDoNotDrawFocus);
-
-  CFX_FloatRect rcWidget = rcBBox.ToFloatRect();
-  rcWidget.Inflate(1.0f, 1.0f);
-  return rcWidget;
-}
-
-WideString CPDFXFA_WidgetHandler::GetText(CPDFSDK_Annot* pAnnot) {
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
-  if (!pXFAWidget)
-    return WideString();
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->GetText(pXFAWidget->GetXFAFFWidget());
-}
-
-WideString CPDFXFA_WidgetHandler::GetSelectedText(CPDFSDK_Annot* pAnnot) {
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
-  if (!pXFAWidget)
-    return WideString();
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->GetSelectedText(pXFAWidget->GetXFAFFWidget());
-}
-
-void CPDFXFA_WidgetHandler::ReplaceSelection(CPDFSDK_Annot* pAnnot,
-                                             const WideString& text) {
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
-  if (!pXFAWidget)
-    return;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->PasteText(pXFAWidget->GetXFAFFWidget(), text);
-}
-
-bool CPDFXFA_WidgetHandler::CanUndo(CPDFSDK_Annot* pAnnot) {
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
-  if (!pXFAWidget)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->CanUndo(pXFAWidget->GetXFAFFWidget());
-}
-
-bool CPDFXFA_WidgetHandler::CanRedo(CPDFSDK_Annot* pAnnot) {
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
-  if (!pXFAWidget)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->CanRedo(pXFAWidget->GetXFAFFWidget());
-}
-
-bool CPDFXFA_WidgetHandler::Undo(CPDFSDK_Annot* pAnnot) {
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
-  if (!pXFAWidget)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->Undo(pXFAWidget->GetXFAFFWidget());
-}
-
-bool CPDFXFA_WidgetHandler::Redo(CPDFSDK_Annot* pAnnot) {
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
-  if (!pXFAWidget)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->Redo(pXFAWidget->GetXFAFFWidget());
-}
-
-bool CPDFXFA_WidgetHandler::HitTest(CPDFSDK_PageView* pPageView,
-                                    CPDFSDK_Annot* pAnnot,
-                                    const CFX_PointF& point) {
-  if (!pPageView)
-    return false;
-
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
-  if (!pXFAWidget)
-    return false;
-
-  CPDFSDK_FormFillEnvironment* pFormFillEnv = pPageView->GetFormFillEnv();
-  if (!pFormFillEnv)
-    return false;
-
-  auto* pContext =
-      static_cast<CPDFXFA_Context*>(pFormFillEnv->GetDocExtension());
-  if (!pContext)
-    return false;
-
-  CXFA_FFDocView* pDocView = pContext->GetXFADocView();
-  if (!pDocView)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = pDocView->GetWidgetHandler();
-  return pWidgetHandler &&
-         pWidgetHandler->HitTest(pXFAWidget->GetXFAFFWidget(), point) !=
-             FWL_WidgetHit::Unknown;
-}
-
-void CPDFXFA_WidgetHandler::OnMouseEnter(CPDFSDK_PageView* pPageView,
-                                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                         uint32_t nFlag) {
-  if (!pPageView)
-    return;
-
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
-  if (!pXFAWidget)
-    return;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  pWidgetHandler->OnMouseEnter(pXFAWidget->GetXFAFFWidget());
-}
-
-void CPDFXFA_WidgetHandler::OnMouseExit(CPDFSDK_PageView* pPageView,
-                                        ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                        uint32_t nFlag) {
-  if (!pPageView)
-    return;
-
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
-  if (!pXFAWidget)
-    return;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  pWidgetHandler->OnMouseExit(pXFAWidget->GetXFAFFWidget());
-}
-
-bool CPDFXFA_WidgetHandler::OnLButtonDown(CPDFSDK_PageView* pPageView,
-                                          ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                          uint32_t nFlags,
-                                          const CFX_PointF& point) {
-  if (!pPageView)
-    return false;
-
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
-  if (!pXFAWidget)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->OnLButtonDown(pXFAWidget->GetXFAFFWidget(),
-                                       GetFWLFlags(nFlags), point);
-}
-
-bool CPDFXFA_WidgetHandler::OnLButtonUp(CPDFSDK_PageView* pPageView,
-                                        ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                        uint32_t nFlags,
-                                        const CFX_PointF& point) {
-  if (!pPageView)
-    return false;
-
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
-  if (!pXFAWidget)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->OnLButtonUp(pXFAWidget->GetXFAFFWidget(),
-                                     GetFWLFlags(nFlags), point);
-}
-
-bool CPDFXFA_WidgetHandler::OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                                            ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                            uint32_t nFlags,
-                                            const CFX_PointF& point) {
-  if (!pPageView)
-    return false;
-
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
-  if (!pXFAWidget)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->OnLButtonDblClk(pXFAWidget->GetXFAFFWidget(),
-                                         GetFWLFlags(nFlags), point);
-}
-
-bool CPDFXFA_WidgetHandler::OnMouseMove(CPDFSDK_PageView* pPageView,
-                                        ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                        uint32_t nFlags,
-                                        const CFX_PointF& point) {
-  if (!pPageView)
-    return false;
-
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
-  if (!pXFAWidget)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->OnMouseMove(pXFAWidget->GetXFAFFWidget(),
-                                     GetFWLFlags(nFlags), point);
-}
-
-bool CPDFXFA_WidgetHandler::OnMouseWheel(CPDFSDK_PageView* pPageView,
-                                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                         uint32_t nFlags,
-                                         short zDelta,
-                                         const CFX_PointF& point) {
-  if (!pPageView)
-    return false;
-
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
-  if (!pXFAWidget)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->OnMouseWheel(pXFAWidget->GetXFAFFWidget(),
-                                      GetFWLFlags(nFlags), zDelta, point);
-}
-
-bool CPDFXFA_WidgetHandler::OnRButtonDown(CPDFSDK_PageView* pPageView,
-                                          ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                          uint32_t nFlags,
-                                          const CFX_PointF& point) {
-  if (!pPageView)
-    return false;
-
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
-  if (!pXFAWidget)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->OnRButtonDown(pXFAWidget->GetXFAFFWidget(),
-                                       GetFWLFlags(nFlags), point);
-}
-
-bool CPDFXFA_WidgetHandler::OnRButtonUp(CPDFSDK_PageView* pPageView,
-                                        ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                        uint32_t nFlags,
-                                        const CFX_PointF& point) {
-  if (!pPageView)
-    return false;
-
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
-  if (!pXFAWidget)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->OnRButtonUp(pXFAWidget->GetXFAFFWidget(),
-                                     GetFWLFlags(nFlags), point);
-}
-
-bool CPDFXFA_WidgetHandler::OnRButtonDblClk(CPDFSDK_PageView* pPageView,
-                                            ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                            uint32_t nFlags,
-                                            const CFX_PointF& point) {
-  if (!pPageView)
-    return false;
-
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
-  if (!pXFAWidget)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->OnRButtonDblClk(pXFAWidget->GetXFAFFWidget(),
-                                         GetFWLFlags(nFlags), point);
-}
-
-bool CPDFXFA_WidgetHandler::OnChar(CPDFSDK_Annot* pAnnot,
-                                   uint32_t nChar,
-                                   uint32_t nFlags) {
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
-  if (!pXFAWidget)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->OnChar(pXFAWidget->GetXFAFFWidget(), nChar,
-                                GetFWLFlags(nFlags));
-}
-
-bool CPDFXFA_WidgetHandler::OnKeyDown(CPDFSDK_Annot* pAnnot,
-                                      int nKeyCode,
-                                      int nFlag) {
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
-  if (!pXFAWidget)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->OnKeyDown(pXFAWidget->GetXFAFFWidget(), nKeyCode,
-                                   GetFWLFlags(nFlag));
-}
-
-bool CPDFXFA_WidgetHandler::OnKeyUp(CPDFSDK_Annot* pAnnot,
-                                    int nKeyCode,
-                                    int nFlag) {
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
-  if (!pXFAWidget)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
-  return pWidgetHandler->OnKeyUp(pXFAWidget->GetXFAFFWidget(), nKeyCode,
-                                 GetFWLFlags(nFlag));
-}
-
-bool CPDFXFA_WidgetHandler::OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                       uint32_t nFlag) {
-  return true;
-}
-
-bool CPDFXFA_WidgetHandler::OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                        uint32_t nFlag) {
-  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
-  if (!pXFAWidget)
-    return true;
-
-  CXFA_FFWidget* hWidget = pXFAWidget->GetXFAFFWidget();
-  if (!hWidget)
-    return true;
-
-  CXFA_FFPageView* pXFAPageView = hWidget->GetPageView();
-  if (!pXFAPageView)
-    return true;
-
-  pXFAPageView->GetDocView()->SetFocus(nullptr);
-  return true;
-}
-
-bool CPDFXFA_WidgetHandler::OnXFAChangedFocus(
-    ObservedPtr<CPDFSDK_Annot>* pOldAnnot,
-    ObservedPtr<CPDFSDK_Annot>* pNewAnnot) {
-  CXFA_FFWidgetHandler* pWidgetHandler = nullptr;
-  if (pOldAnnot->HasObservable())
-    pWidgetHandler = GetXFAFFWidgetHandler(pOldAnnot->Get());
-  else if (pNewAnnot->HasObservable())
-    pWidgetHandler = GetXFAFFWidgetHandler(pNewAnnot->Get());
-
-  if (!pWidgetHandler)
-    return true;
-
-  CPDFXFA_Widget* pNewXFAWidget = ToXFAWidget(pNewAnnot->Get());
-  if (!pNewXFAWidget)
-    return true;
-
-  CXFA_FFWidget* hWidget = pNewXFAWidget->GetXFAFFWidget();
-  if (!hWidget)
-    return true;
-
-  CXFA_FFPageView* pXFAPageView = hWidget->GetPageView();
-  if (!pXFAPageView)
-    return true;
-
-  ObservedPtr<CXFA_FFPageView> pObservedXFAPageView(pXFAPageView);
-  bool bRet = pXFAPageView->GetDocView()->SetFocus(hWidget);
-
-  // Check |pXFAPageView| again because |SetFocus| can trigger JS to destroy it.
-  if (pObservedXFAPageView &&
-      pXFAPageView->GetDocView()->GetFocusWidget() == hWidget) {
-    bRet = true;
-  }
-
-  return bRet;
-}
-
-bool CPDFXFA_WidgetHandler::SetIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                             int index,
-                                             bool selected) {
-  return false;
-}
-
-bool CPDFXFA_WidgetHandler::IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                            int index) {
-  return false;
-}
-
-CXFA_FFWidgetHandler* CPDFXFA_WidgetHandler::GetXFAFFWidgetHandler(
-    CPDFSDK_Annot* pAnnot) {
-  if (!pAnnot)
-    return nullptr;
-
-  CPDFSDK_PageView* pPageView = pAnnot->GetPageView();
-  if (!pPageView)
-    return nullptr;
-
-  CPDFSDK_FormFillEnvironment* pFormFillEnv = pPageView->GetFormFillEnv();
-  if (!pFormFillEnv)
-    return nullptr;
-
-  auto* pDoc = static_cast<CPDFXFA_Context*>(pFormFillEnv->GetDocExtension());
-  if (!pDoc)
-    return nullptr;
-
-  CXFA_FFDocView* pDocView = pDoc->GetXFADocView();
-  if (!pDocView)
-    return nullptr;
-
-  return pDocView->GetWidgetHandler();
-}
-
-uint32_t CPDFXFA_WidgetHandler::GetFWLFlags(uint32_t dwFlag) {
-  uint32_t dwFWLFlag = 0;
-
-  if (dwFlag & FWL_EVENTFLAG_ControlKey)
-    dwFWLFlag |= FWL_KEYFLAG_Ctrl;
-  if (dwFlag & FWL_EVENTFLAG_LeftButtonDown)
-    dwFWLFlag |= FWL_KEYFLAG_LButton;
-  if (dwFlag & FWL_EVENTFLAG_MiddleButtonDown)
-    dwFWLFlag |= FWL_KEYFLAG_MButton;
-  if (dwFlag & FWL_EVENTFLAG_RightButtonDown)
-    dwFWLFlag |= FWL_KEYFLAG_RButton;
-  if (dwFlag & FWL_EVENTFLAG_ShiftKey)
-    dwFWLFlag |= FWL_KEYFLAG_Shift;
-  if (dwFlag & FWL_EVENTFLAG_AltKey)
-    dwFWLFlag |= FWL_KEYFLAG_Alt;
-
-  return dwFWLFlag;
-}
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h b/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h
deleted file mode 100644
index e18e0ca..0000000
--- a/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_FPDFXFA_CPDFXFA_WIDGETHANDLER_H_
-#define FPDFSDK_FPDFXFA_CPDFXFA_WIDGETHANDLER_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "fpdfsdk/ipdfsdk_annothandler.h"
-
-class CFX_Matrix;
-class CFX_RenderDevice;
-class CPDF_Annot;
-class CPDFSDK_FormFillEnvironment;
-class CPDFSDK_Annot;
-class CPDFSDK_PageView;
-class CXFA_FFWidget;
-class CXFA_FFWidgetHandler;
-
-class CPDFXFA_WidgetHandler final : public IPDFSDK_AnnotHandler {
- public:
-  CPDFXFA_WidgetHandler();
-  ~CPDFXFA_WidgetHandler() override;
-
-  // IPDFSDK_AnnotHandler:
-  void SetFormFillEnvironment(
-      CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  bool CanAnswer(CPDFSDK_Annot* pAnnot) override;
-  CPDFSDK_Annot* NewAnnot(CPDF_Annot* pAnnot, CPDFSDK_PageView* pPage) override;
-  void ReleaseAnnot(std::unique_ptr<CPDFSDK_Annot> pAnnot) override;
-  CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView,
-                            CPDFSDK_Annot* pAnnot) override;
-  WideString GetText(CPDFSDK_Annot* pAnnot) override;
-  WideString GetSelectedText(CPDFSDK_Annot* pAnnot) override;
-  void ReplaceSelection(CPDFSDK_Annot* pAnnot, const WideString& text) override;
-  bool CanUndo(CPDFSDK_Annot* pAnnot) override;
-  bool CanRedo(CPDFSDK_Annot* pAnnot) override;
-  bool Undo(CPDFSDK_Annot* pAnnot) override;
-  bool Redo(CPDFSDK_Annot* pAnnot) override;
-  bool HitTest(CPDFSDK_PageView* pPageView,
-               CPDFSDK_Annot* pAnnot,
-               const CFX_PointF& point) override;
-  void OnDraw(CPDFSDK_PageView* pPageView,
-              CPDFSDK_Annot* pAnnot,
-              CFX_RenderDevice* pDevice,
-              const CFX_Matrix& mtUser2Device,
-              bool bDrawAnnots) override;
-  void OnLoad(CPDFSDK_Annot* pAnnot) override;
-  void OnMouseEnter(CPDFSDK_PageView* pPageView,
-                    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                    uint32_t nFlag) override;
-  void OnMouseExit(CPDFSDK_PageView* pPageView,
-                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   uint32_t nFlag) override;
-  bool OnLButtonDown(CPDFSDK_PageView* pPageView,
-                     ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                     uint32_t nFlags,
-                     const CFX_PointF& point) override;
-  bool OnLButtonUp(CPDFSDK_PageView* pPageView,
-                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   uint32_t nFlags,
-                   const CFX_PointF& point) override;
-  bool OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                       ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                       uint32_t nFlags,
-                       const CFX_PointF& point) override;
-  bool OnMouseMove(CPDFSDK_PageView* pPageView,
-                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   uint32_t nFlags,
-                   const CFX_PointF& point) override;
-  bool OnMouseWheel(CPDFSDK_PageView* pPageView,
-                    ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                    uint32_t nFlags,
-                    short zDelta,
-                    const CFX_PointF& point) override;
-  bool OnRButtonDown(CPDFSDK_PageView* pPageView,
-                     ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                     uint32_t nFlags,
-                     const CFX_PointF& point) override;
-  bool OnRButtonUp(CPDFSDK_PageView* pPageView,
-                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                   uint32_t nFlags,
-                   const CFX_PointF& point) override;
-  bool OnRButtonDblClk(CPDFSDK_PageView* pPageView,
-                       ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                       uint32_t nFlags,
-                       const CFX_PointF& point) override;
-  bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override;
-  bool OnKeyDown(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override;
-  bool OnKeyUp(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override;
-  bool OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag) override;
-  bool OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag) override;
-  bool SetIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                        int index,
-                        bool selected) override;
-  bool IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot, int index) override;
-
-  std::unique_ptr<CPDFSDK_Annot> NewAnnotForXFA(CXFA_FFWidget* pAnnot,
-                                                CPDFSDK_PageView* pPage);
-  bool OnXFAChangedFocus(ObservedPtr<CPDFSDK_Annot>* pOldAnnot,
-                         ObservedPtr<CPDFSDK_Annot>* pNewAnnot);
-
- private:
-  CXFA_FFWidgetHandler* GetXFAFFWidgetHandler(CPDFSDK_Annot* pAnnot);
-  uint32_t GetFWLFlags(uint32_t dwFlag);
-
-  UnownedPtr<CPDFSDK_FormFillEnvironment> m_pFormFillEnv;
-};
-
-#endif  // FPDFSDK_FPDFXFA_CPDFXFA_WIDGETHANDLER_H_
diff --git a/fpdfsdk/ipdfsdk_annothandler.h b/fpdfsdk/ipdfsdk_annothandler.h
deleted file mode 100644
index 24b6ff0..0000000
--- a/fpdfsdk/ipdfsdk_annothandler.h
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_IPDFSDK_ANNOTHANDLER_H_
-#define FPDFSDK_IPDFSDK_ANNOTHANDLER_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "fpdfsdk/cpdfsdk_annot.h"
-
-class CFX_Matrix;
-class CFX_RenderDevice;
-class CPDF_Annot;
-class CPDFSDK_FormFillEnvironment;
-class CPDFSDK_PageView;
-
-class IPDFSDK_AnnotHandler {
- public:
-  virtual ~IPDFSDK_AnnotHandler() = default;
-
-  virtual void SetFormFillEnvironment(
-      CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-  virtual bool CanAnswer(CPDFSDK_Annot* pAnnot) = 0;
-  virtual CPDFSDK_Annot* NewAnnot(CPDF_Annot* pAnnot,
-                                  CPDFSDK_PageView* pPage) = 0;
-  virtual void ReleaseAnnot(std::unique_ptr<CPDFSDK_Annot> pAnnot) = 0;
-  virtual CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView,
-                                    CPDFSDK_Annot* pAnnot) = 0;
-  virtual WideString GetText(CPDFSDK_Annot* pAnnot) = 0;
-  virtual WideString GetSelectedText(CPDFSDK_Annot* pAnnot) = 0;
-  virtual void ReplaceSelection(CPDFSDK_Annot* pAnnot,
-                                const WideString& text) = 0;
-  virtual bool CanUndo(CPDFSDK_Annot* pAnnot) = 0;
-  virtual bool CanRedo(CPDFSDK_Annot* pAnnot) = 0;
-  virtual bool Undo(CPDFSDK_Annot* pAnnot) = 0;
-  virtual bool Redo(CPDFSDK_Annot* pAnnot) = 0;
-  virtual bool HitTest(CPDFSDK_PageView* pPageView,
-                       CPDFSDK_Annot* pAnnot,
-                       const CFX_PointF& point) = 0;
-  virtual void OnDraw(CPDFSDK_PageView* pPageView,
-                      CPDFSDK_Annot* pAnnot,
-                      CFX_RenderDevice* pDevice,
-                      const CFX_Matrix& mtUser2Device,
-                      bool bDrawAnnots) = 0;
-  virtual void OnLoad(CPDFSDK_Annot* pAnnot) = 0;
-  virtual void OnMouseEnter(CPDFSDK_PageView* pPageView,
-                            ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                            uint32_t nFlag) = 0;
-  virtual void OnMouseExit(CPDFSDK_PageView* pPageView,
-                           ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                           uint32_t nFlag) = 0;
-  virtual bool OnLButtonDown(CPDFSDK_PageView* pPageView,
-                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                             uint32_t nFlags,
-                             const CFX_PointF& point) = 0;
-  virtual bool OnLButtonUp(CPDFSDK_PageView* pPageView,
-                           ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                           uint32_t nFlags,
-                           const CFX_PointF& point) = 0;
-  virtual bool OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                               ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                               uint32_t nFlags,
-                               const CFX_PointF& point) = 0;
-  virtual bool OnMouseMove(CPDFSDK_PageView* pPageView,
-                           ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                           uint32_t nFlags,
-                           const CFX_PointF& point) = 0;
-  virtual bool OnMouseWheel(CPDFSDK_PageView* pPageView,
-                            ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                            uint32_t nFlags,
-                            short zDelta,
-                            const CFX_PointF& point) = 0;
-  virtual bool OnRButtonDown(CPDFSDK_PageView* pPageView,
-                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                             uint32_t nFlags,
-                             const CFX_PointF& point) = 0;
-  virtual bool OnRButtonUp(CPDFSDK_PageView* pPageView,
-                           ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                           uint32_t nFlags,
-                           const CFX_PointF& point) = 0;
-  virtual bool OnRButtonDblClk(CPDFSDK_PageView* pPageView,
-                               ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                               uint32_t nFlags,
-                               const CFX_PointF& point) = 0;
-  virtual bool OnChar(CPDFSDK_Annot* pAnnot,
-                      uint32_t nChar,
-                      uint32_t nFlags) = 0;
-  virtual bool OnKeyDown(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) = 0;
-  virtual bool OnKeyUp(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) = 0;
-  virtual bool OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                          uint32_t nFlag) = 0;
-  virtual bool OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                           uint32_t nFlag) = 0;
-  virtual bool SetIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                                int index,
-                                bool selected) = 0;
-  virtual bool IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
-                               int index) = 0;
-};
-
-#endif  // FPDFSDK_IPDFSDK_ANNOTHANDLER_H_
diff --git a/fpdfsdk/pwl/Android.bp b/fpdfsdk/pwl/Android.bp
index 74fc563..e02cc17 100644
--- a/fpdfsdk/pwl/Android.bp
+++ b/fpdfsdk/pwl/Android.bp
@@ -13,11 +13,8 @@
 
     visibility: ["//external/pdfium:__subpackages__"],
 
-    header_libs: [
-        "libpdfium-constants",
-    ],
-
     static_libs: [
+        "libpdfium-constants",
         "libpdfium-font",
         "libpdfium-render",
         "libpdfium-fpdfdoc",
diff --git a/fpdfsdk/pwl/BUILD.gn b/fpdfsdk/pwl/BUILD.gn
index ea26c13..e1722a8 100644
--- a/fpdfsdk/pwl/BUILD.gn
+++ b/fpdfsdk/pwl/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -11,29 +11,34 @@
     "cpwl_button.h",
     "cpwl_caret.cpp",
     "cpwl_caret.h",
+    "cpwl_cbbutton.cpp",
+    "cpwl_cbbutton.h",
+    "cpwl_cblistbox.cpp",
+    "cpwl_cblistbox.h",
     "cpwl_combo_box.cpp",
     "cpwl_combo_box.h",
     "cpwl_edit.cpp",
     "cpwl_edit.h",
-    "cpwl_edit_ctrl.cpp",
-    "cpwl_edit_ctrl.h",
     "cpwl_edit_impl.cpp",
     "cpwl_edit_impl.h",
-    "cpwl_icon.cpp",
-    "cpwl_icon.h",
     "cpwl_list_box.cpp",
     "cpwl_list_box.h",
-    "cpwl_list_impl.cpp",
-    "cpwl_list_impl.h",
+    "cpwl_list_ctrl.cpp",
+    "cpwl_list_ctrl.h",
+    "cpwl_sbbutton.cpp",
+    "cpwl_sbbutton.h",
     "cpwl_scroll_bar.cpp",
     "cpwl_scroll_bar.h",
     "cpwl_special_button.cpp",
     "cpwl_special_button.h",
     "cpwl_wnd.cpp",
     "cpwl_wnd.h",
-    "ipwl_systemhandler.h",
+    "ipwl_fillernotify.h",
   ]
-  configs += [ "../../:pdfium_core_config" ]
+  configs += [
+    "../../:pdfium_strict_config",
+    "../../:pdfium_noshorten_config",
+  ]
   deps = [
     "../../:pdfium_public_headers",
     "../../constants",
@@ -46,12 +51,31 @@
   visibility = [ "../../*" ]
 }
 
-pdfium_embeddertest_source_set("embeddertests") {
+source_set("embedder_test_support") {
+  testonly = true
   sources = [
     "cpwl_combo_box_embeddertest.cpp",
+    "cpwl_combo_box_embeddertest.h",
+  ]
+  configs += [ "../../:pdfium_strict_config" ]
+  deps = [
+    ":pwl",
+    "../:fpdfsdk",
+    "../../:pdfium_public_headers",
+    "../../testing:embedder_test_support",
+    "../formfiller",
+    "//testing/gtest",
+  ]
+}
+
+pdfium_embeddertest_source_set("embeddertests") {
+  sources = [
+    "cpwl_combo_box_edit_embeddertest.cpp",
     "cpwl_edit_embeddertest.cpp",
+    "cpwl_special_button_embeddertest.cpp",
   ]
   deps = [
+    ":embedder_test_support",
     ":pwl",
     "../:fpdfsdk",
     "../formfiller",
diff --git a/fpdfsdk/pwl/README.md b/fpdfsdk/pwl/README.md
index d9be3b1..9381b5f 100644
--- a/fpdfsdk/pwl/README.md
+++ b/fpdfsdk/pwl/README.md
@@ -11,7 +11,6 @@
     * CPWL_Caret
     * CPWL_EditCtrl
         * CPWL_Edit
-    * CPWL_Icon
     * CPWL_ListBox
         * CPWL_CBListBox (combo box)
     * CPWL_ScrollBar
diff --git a/fpdfsdk/pwl/cpwl_button.cpp b/fpdfsdk/pwl/cpwl_button.cpp
index b81afae..7b1cd93 100644
--- a/fpdfsdk/pwl/cpwl_button.cpp
+++ b/fpdfsdk/pwl/cpwl_button.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,22 +10,24 @@
 
 CPWL_Button::CPWL_Button(
     const CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData)
     : CPWL_Wnd(cp, std::move(pAttachedData)) {
-  GetCreationParams()->eCursorType = FXCT_HAND;
+  GetCreationParams()->eCursorType = IPWL_FillerNotify::CursorStyle::kHand;
 }
 
 CPWL_Button::~CPWL_Button() = default;
 
-bool CPWL_Button::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnLButtonDown(point, nFlag);
+bool CPWL_Button::OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,
+                                const CFX_PointF& point) {
+  CPWL_Wnd::OnLButtonDown(nFlag, point);
   m_bMouseDown = true;
   SetCapture();
   return true;
 }
 
-bool CPWL_Button::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnLButtonUp(point, nFlag);
+bool CPWL_Button::OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag,
+                              const CFX_PointF& point) {
+  CPWL_Wnd::OnLButtonUp(nFlag, point);
   ReleaseCapture();
   m_bMouseDown = false;
   return true;
diff --git a/fpdfsdk/pwl/cpwl_button.h b/fpdfsdk/pwl/cpwl_button.h
index e7760dd..56dbe6c 100644
--- a/fpdfsdk/pwl/cpwl_button.h
+++ b/fpdfsdk/pwl/cpwl_button.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,17 +10,18 @@
 #include <memory>
 
 #include "fpdfsdk/pwl/cpwl_wnd.h"
-#include "fpdfsdk/pwl/ipwl_systemhandler.h"
+#include "fpdfsdk/pwl/ipwl_fillernotify.h"
 
 class CPWL_Button : public CPWL_Wnd {
  public:
   CPWL_Button(const CreateParams& cp,
-              std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
+              std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData);
   ~CPWL_Button() override;
 
   // CPWL_Wnd
-  bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override;
-  bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override;
+  bool OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,
+                     const CFX_PointF& point) override;
+  bool OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag, const CFX_PointF& point) override;
 
  protected:
   bool m_bMouseDown = false;
diff --git a/fpdfsdk/pwl/cpwl_caret.cpp b/fpdfsdk/pwl/cpwl_caret.cpp
index 739fd37..47f7864 100644
--- a/fpdfsdk/pwl/cpwl_caret.cpp
+++ b/fpdfsdk/pwl/cpwl_caret.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,14 +9,14 @@
 #include <sstream>
 #include <utility>
 
+#include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
-#include "third_party/base/ptr_util.h"
 
 CPWL_Caret::CPWL_Caret(
     const CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData)
     : CPWL_Wnd(cp, std::move(pAttachedData)) {}
 
 CPWL_Caret::~CPWL_Caret() = default;
@@ -28,7 +28,6 @@
 
   CFX_FloatRect rcRect = GetCaretRect();
   CFX_FloatRect rcClip = GetClipRect();
-  CFX_PathData path;
 
   float fCaretX = rcRect.left + m_fWidth * 0.5f;
   float fCaretTop = rcRect.top;
@@ -42,13 +41,16 @@
     fCaretBottom = rcRect.bottom;
   }
 
-  path.AppendPoint(CFX_PointF(fCaretX, fCaretBottom), FXPT_TYPE::MoveTo, false);
-  path.AppendPoint(CFX_PointF(fCaretX, fCaretTop), FXPT_TYPE::LineTo, false);
+  CFX_Path path;
+  path.AppendPoint(CFX_PointF(fCaretX, fCaretBottom),
+                   CFX_Path::Point::Type::kMove);
+  path.AppendPoint(CFX_PointF(fCaretX, fCaretTop),
+                   CFX_Path::Point::Type::kLine);
 
   CFX_GraphStateData gsd;
   gsd.m_LineWidth = m_fWidth;
-  pDevice->DrawPath(&path, &mtUser2Device, &gsd, 0, ArgbEncode(255, 0, 0, 0),
-                    FXFILL_ALTERNATE);
+  pDevice->DrawPath(path, &mtUser2Device, &gsd, 0, ArgbEncode(255, 0, 0, 0),
+                    CFX_FillRenderOptions::EvenOddOptions());
 }
 
 void CPWL_Caret::OnTimerFired() {
@@ -74,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;
@@ -85,8 +87,8 @@
 
     m_ptHead = ptHead;
     m_ptFoot = ptFoot;
-    m_pTimer = pdfium::MakeUnique<CFX_Timer>(GetTimerHandler(), this,
-                                             kCaretFlashIntervalMs);
+    m_pTimer = std::make_unique<CFX_Timer>(GetTimerHandler(), this,
+                                           kCaretFlashIntervalMs);
 
     if (!CPWL_Wnd::SetVisible(true))
       return;
@@ -109,10 +111,9 @@
   // needs to be done, check the return value of Move().
 }
 
-bool CPWL_Caret::InvalidateRect(CFX_FloatRect* pRect) {
-  if (!pRect) {
+bool CPWL_Caret::InvalidateRect(const CFX_FloatRect* pRect) {
+  if (!pRect)
     return CPWL_Wnd::InvalidateRect(nullptr);
-  }
 
   CFX_FloatRect rcRefresh = *pRect;
   if (!rcRefresh.IsEmpty()) {
diff --git a/fpdfsdk/pwl/cpwl_caret.h b/fpdfsdk/pwl/cpwl_caret.h
index 71788ef..c3e5841 100644
--- a/fpdfsdk/pwl/cpwl_caret.h
+++ b/fpdfsdk/pwl/cpwl_caret.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,13 +15,13 @@
 class CPWL_Caret final : public CPWL_Wnd, public CFX_Timer::CallbackIface {
  public:
   CPWL_Caret(const CreateParams& cp,
-             std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
+             std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData);
   ~CPWL_Caret() override;
 
   // CPWL_Wnd:
   void DrawThisAppearance(CFX_RenderDevice* pDevice,
                           const CFX_Matrix& mtUser2Device) override;
-  bool InvalidateRect(CFX_FloatRect* pRect) override;
+  bool InvalidateRect(const CFX_FloatRect* pRect) override;
   bool SetVisible(bool bVisible) override;
 
   // CFX_Timer::CallbackIface:
diff --git a/fpdfsdk/pwl/cpwl_cbbutton.cpp b/fpdfsdk/pwl/cpwl_cbbutton.cpp
new file mode 100644
index 0000000..de248ce
--- /dev/null
+++ b/fpdfsdk/pwl/cpwl_cbbutton.cpp
@@ -0,0 +1,79 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fpdfsdk/pwl/cpwl_cbbutton.h"
+
+#include <utility>
+
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "core/fxge/cfx_path.h"
+#include "core/fxge/cfx_renderdevice.h"
+
+CPWL_CBButton::CPWL_CBButton(
+    const CreateParams& cp,
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData)
+    : CPWL_Wnd(cp, std::move(pAttachedData)) {}
+
+CPWL_CBButton::~CPWL_CBButton() = default;
+
+void CPWL_CBButton::DrawThisAppearance(CFX_RenderDevice* pDevice,
+                                       const CFX_Matrix& mtUser2Device) {
+  CPWL_Wnd::DrawThisAppearance(pDevice, mtUser2Device);
+
+  if (!IsVisible())
+    return;
+
+  CFX_FloatRect window = CPWL_Wnd::GetWindowRect();
+  if (window.IsEmpty())
+    return;
+
+  constexpr float kComboBoxTriangleLength = 6.0f;
+  constexpr float kComboBoxTriangleHalfLength = kComboBoxTriangleLength / 2;
+  constexpr float kComboBoxTriangleQuarterLength = kComboBoxTriangleLength / 4;
+  if (!FXSYS_IsFloatBigger(window.right - window.left,
+                           kComboBoxTriangleLength) ||
+      !FXSYS_IsFloatBigger(window.top - window.bottom,
+                           kComboBoxTriangleHalfLength)) {
+    return;
+  }
+
+  CFX_PointF ptCenter = GetCenterPoint();
+  CFX_PointF pt1(ptCenter.x - kComboBoxTriangleHalfLength,
+                 ptCenter.y + kComboBoxTriangleQuarterLength);
+  CFX_PointF pt2(ptCenter.x + kComboBoxTriangleHalfLength,
+                 ptCenter.y + kComboBoxTriangleQuarterLength);
+  CFX_PointF pt3(ptCenter.x, ptCenter.y - kComboBoxTriangleQuarterLength);
+
+  CFX_Path path;
+  path.AppendPoint(pt1, CFX_Path::Point::Type::kMove);
+  path.AppendPoint(pt2, CFX_Path::Point::Type::kLine);
+  path.AppendPoint(pt3, CFX_Path::Point::Type::kLine);
+  path.AppendPoint(pt1, CFX_Path::Point::Type::kLine);
+
+  pDevice->DrawPath(path, &mtUser2Device, nullptr,
+                    kDefaultBlackColor.ToFXColor(GetTransparency()), 0,
+                    CFX_FillRenderOptions::EvenOddOptions());
+}
+
+bool CPWL_CBButton::OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,
+                                  const CFX_PointF& point) {
+  CPWL_Wnd::OnLButtonDown(nFlag, point);
+
+  SetCapture();
+  CPWL_Wnd* pParent = GetParentWindow();
+  if (pParent)
+    pParent->NotifyLButtonDown(this, point);
+
+  return true;
+}
+
+bool CPWL_CBButton::OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag,
+                                const CFX_PointF& point) {
+  CPWL_Wnd::OnLButtonUp(nFlag, point);
+
+  ReleaseCapture();
+  return true;
+}
diff --git a/fpdfsdk/pwl/cpwl_cbbutton.h b/fpdfsdk/pwl/cpwl_cbbutton.h
new file mode 100644
index 0000000..a57942e
--- /dev/null
+++ b/fpdfsdk/pwl/cpwl_cbbutton.h
@@ -0,0 +1,30 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_PWL_CPWL_CBBUTTON_H_
+#define FPDFSDK_PWL_CPWL_CBBUTTON_H_
+
+#include <memory>
+
+#include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "fpdfsdk/pwl/ipwl_fillernotify.h"
+
+class CPWL_CBButton final : public CPWL_Wnd {
+ public:
+  CPWL_CBButton(
+      const CreateParams& cp,
+      std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData);
+  ~CPWL_CBButton() override;
+
+  // CPWL_Wnd:
+  void DrawThisAppearance(CFX_RenderDevice* pDevice,
+                          const CFX_Matrix& mtUser2Device) override;
+  bool OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,
+                     const CFX_PointF& point) override;
+  bool OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag, const CFX_PointF& point) override;
+};
+
+#endif  // FPDFSDK_PWL_CPWL_CBBUTTON_H_
diff --git a/fpdfsdk/pwl/cpwl_cblistbox.cpp b/fpdfsdk/pwl/cpwl_cblistbox.cpp
new file mode 100644
index 0000000..0771b26
--- /dev/null
+++ b/fpdfsdk/pwl/cpwl_cblistbox.cpp
@@ -0,0 +1,92 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fpdfsdk/pwl/cpwl_cblistbox.h"
+
+#include <utility>
+
+#include "fpdfsdk/pwl/cpwl_combo_box.h"
+#include "fpdfsdk/pwl/cpwl_list_ctrl.h"
+#include "public/fpdf_fwlevent.h"
+#include "third_party/base/notreached.h"
+
+CPWL_CBListBox::CPWL_CBListBox(
+    const CreateParams& cp,
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData)
+    : CPWL_ListBox(cp, std::move(pAttachedData)) {}
+
+CPWL_CBListBox::~CPWL_CBListBox() = default;
+
+bool CPWL_CBListBox::OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag,
+                                 const CFX_PointF& point) {
+  CPWL_Wnd::OnLButtonUp(nFlag, point);
+
+  if (!m_bMouseDown)
+    return true;
+
+  ReleaseCapture();
+  m_bMouseDown = false;
+
+  if (!ClientHitTest(point))
+    return true;
+  if (CPWL_Wnd* pParent = GetParentWindow())
+    pParent->NotifyLButtonUp(this, point);
+
+  return !OnNotifySelectionChanged(false, nFlag);
+}
+
+bool CPWL_CBListBox::IsMovementKey(FWL_VKEYCODE nKeyCode) const {
+  switch (nKeyCode) {
+    case FWL_VKEY_Up:
+    case FWL_VKEY_Down:
+    case FWL_VKEY_Home:
+    case FWL_VKEY_Left:
+    case FWL_VKEY_End:
+    case FWL_VKEY_Right:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool CPWL_CBListBox::OnMovementKeyDown(FWL_VKEYCODE nKeyCode,
+                                       Mask<FWL_EVENTFLAG> nFlag) {
+  switch (nKeyCode) {
+    case FWL_VKEY_Up:
+      m_pListCtrl->OnVK_UP(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
+      break;
+    case FWL_VKEY_Down:
+      m_pListCtrl->OnVK_DOWN(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
+      break;
+    case FWL_VKEY_Home:
+      m_pListCtrl->OnVK_HOME(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
+      break;
+    case FWL_VKEY_Left:
+      m_pListCtrl->OnVK_LEFT(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
+      break;
+    case FWL_VKEY_End:
+      m_pListCtrl->OnVK_END(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
+      break;
+    case FWL_VKEY_Right:
+      m_pListCtrl->OnVK_RIGHT(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
+      break;
+    default:
+      NOTREACHED_NORETURN();
+  }
+  return OnNotifySelectionChanged(true, nFlag);
+}
+
+bool CPWL_CBListBox::IsChar(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) const {
+  return m_pListCtrl->OnChar(nChar, IsSHIFTKeyDown(nFlag),
+                             IsCTRLKeyDown(nFlag));
+}
+
+bool CPWL_CBListBox::OnCharNotify(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) {
+  if (auto* pComboBox = static_cast<CPWL_ComboBox*>(GetParentWindow()))
+    pComboBox->SetSelectText();
+
+  return OnNotifySelectionChanged(true, nFlag);
+}
diff --git a/fpdfsdk/pwl/cpwl_cblistbox.h b/fpdfsdk/pwl/cpwl_cblistbox.h
new file mode 100644
index 0000000..0f97edd
--- /dev/null
+++ b/fpdfsdk/pwl/cpwl_cblistbox.h
@@ -0,0 +1,32 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_PWL_CPWL_CBLISTBOX_H_
+#define FPDFSDK_PWL_CPWL_CBLISTBOX_H_
+
+#include <memory>
+
+#include "fpdfsdk/pwl/cpwl_list_box.h"
+#include "fpdfsdk/pwl/ipwl_fillernotify.h"
+#include "public/fpdf_fwlevent.h"
+
+class CPWL_CBListBox final : public CPWL_ListBox {
+ public:
+  CPWL_CBListBox(
+      const CreateParams& cp,
+      std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData);
+  ~CPWL_CBListBox() override;
+
+  // CPWL_ListBox
+  bool OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag, const CFX_PointF& point) override;
+
+  bool IsMovementKey(FWL_VKEYCODE nKeyCode) const;
+  bool OnMovementKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlag);
+  bool IsChar(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) const;
+  bool OnCharNotify(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag);
+};
+
+#endif  // FPDFSDK_PWL_CPWL_CBLISTBOX_H_
diff --git a/fpdfsdk/pwl/cpwl_combo_box.cpp b/fpdfsdk/pwl/cpwl_combo_box.cpp
index 8192da2..7c122d7 100644
--- a/fpdfsdk/pwl/cpwl_combo_box.cpp
+++ b/fpdfsdk/pwl/cpwl_combo_box.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,165 +7,26 @@
 #include "fpdfsdk/pwl/cpwl_combo_box.h"
 
 #include <algorithm>
-#include <sstream>
 #include <utility>
 
-#include "core/fxge/cfx_pathdata.h"
-#include "core/fxge/cfx_renderdevice.h"
+#include "constants/ascii.h"
+#include "fpdfsdk/pwl/cpwl_cbbutton.h"
+#include "fpdfsdk/pwl/cpwl_cblistbox.h"
 #include "fpdfsdk/pwl/cpwl_edit.h"
-#include "fpdfsdk/pwl/cpwl_edit_ctrl.h"
-#include "fpdfsdk/pwl/cpwl_list_box.h"
-#include "fpdfsdk/pwl/cpwl_list_impl.h"
-#include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "fpdfsdk/pwl/ipwl_fillernotify.h"
 #include "public/fpdf_fwlevent.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
 constexpr float kComboBoxDefaultFontSize = 12.0f;
-constexpr float kComboBoxTriangleHalfLength = 3.0f;
 constexpr int kDefaultButtonWidth = 13;
 
 }  // namespace
 
-CPWL_CBListBox::CPWL_CBListBox(
-    const CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
-    : CPWL_ListBox(cp, std::move(pAttachedData)) {}
-
-CPWL_CBListBox::~CPWL_CBListBox() = default;
-
-bool CPWL_CBListBox::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnLButtonUp(point, nFlag);
-
-  if (!m_bMouseDown)
-    return true;
-
-  ReleaseCapture();
-  m_bMouseDown = false;
-
-  if (!ClientHitTest(point))
-    return true;
-  if (CPWL_Wnd* pParent = GetParentWindow())
-    pParent->NotifyLButtonUp(this, point);
-
-  return !OnNotifySelectionChanged(false, nFlag);
-}
-
-bool CPWL_CBListBox::IsMovementKey(uint16_t nChar) const {
-  switch (nChar) {
-    case FWL_VKEY_Up:
-    case FWL_VKEY_Down:
-    case FWL_VKEY_Home:
-    case FWL_VKEY_Left:
-    case FWL_VKEY_End:
-    case FWL_VKEY_Right:
-      return true;
-    default:
-      return false;
-  }
-}
-
-bool CPWL_CBListBox::OnMovementKeyDown(uint16_t nChar, uint32_t nFlag) {
-  ASSERT(IsMovementKey(nChar));
-
-  switch (nChar) {
-    case FWL_VKEY_Up:
-      m_pList->OnVK_UP(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
-      break;
-    case FWL_VKEY_Down:
-      m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
-      break;
-    case FWL_VKEY_Home:
-      m_pList->OnVK_HOME(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
-      break;
-    case FWL_VKEY_Left:
-      m_pList->OnVK_LEFT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
-      break;
-    case FWL_VKEY_End:
-      m_pList->OnVK_END(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
-      break;
-    case FWL_VKEY_Right:
-      m_pList->OnVK_RIGHT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
-      break;
-  }
-  return OnNotifySelectionChanged(true, nFlag);
-}
-
-bool CPWL_CBListBox::IsChar(uint16_t nChar, uint32_t nFlag) const {
-  return m_pList->OnChar(nChar, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
-}
-
-bool CPWL_CBListBox::OnCharNotify(uint16_t nChar, uint32_t nFlag) {
-  if (auto* pComboBox = static_cast<CPWL_ComboBox*>(GetParentWindow()))
-    pComboBox->SetSelectText();
-
-  return OnNotifySelectionChanged(true, nFlag);
-}
-
-CPWL_CBButton::CPWL_CBButton(
-    const CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
-    : CPWL_Wnd(cp, std::move(pAttachedData)) {}
-
-CPWL_CBButton::~CPWL_CBButton() = default;
-
-void CPWL_CBButton::DrawThisAppearance(CFX_RenderDevice* pDevice,
-                                       const CFX_Matrix& mtUser2Device) {
-  CPWL_Wnd::DrawThisAppearance(pDevice, mtUser2Device);
-
-  CFX_FloatRect rectWnd = CPWL_Wnd::GetWindowRect();
-  if (!IsVisible() || rectWnd.IsEmpty())
-    return;
-
-  CFX_PointF ptCenter = GetCenterPoint();
-
-  static constexpr float kComboBoxTriangleQuarterLength =
-      kComboBoxTriangleHalfLength * 0.5;
-  CFX_PointF pt1(ptCenter.x - kComboBoxTriangleHalfLength,
-                 ptCenter.y + kComboBoxTriangleQuarterLength);
-  CFX_PointF pt2(ptCenter.x + kComboBoxTriangleHalfLength,
-                 ptCenter.y + kComboBoxTriangleQuarterLength);
-  CFX_PointF pt3(ptCenter.x, ptCenter.y - kComboBoxTriangleQuarterLength);
-
-  if (IsFloatBigger(rectWnd.right - rectWnd.left,
-                    kComboBoxTriangleHalfLength * 2) &&
-      IsFloatBigger(rectWnd.top - rectWnd.bottom,
-                    kComboBoxTriangleHalfLength)) {
-    CFX_PathData path;
-    path.AppendPoint(pt1, FXPT_TYPE::MoveTo, false);
-    path.AppendPoint(pt2, FXPT_TYPE::LineTo, false);
-    path.AppendPoint(pt3, FXPT_TYPE::LineTo, false);
-    path.AppendPoint(pt1, FXPT_TYPE::LineTo, false);
-
-    pDevice->DrawPath(&path, &mtUser2Device, nullptr,
-                      PWL_DEFAULT_BLACKCOLOR.ToFXColor(GetTransparency()), 0,
-                      FXFILL_ALTERNATE);
-  }
-}
-
-bool CPWL_CBButton::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnLButtonDown(point, nFlag);
-
-  SetCapture();
-  if (CPWL_Wnd* pParent = GetParentWindow())
-    pParent->NotifyLButtonDown(this, point);
-
-  return true;
-}
-
-bool CPWL_CBButton::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnLButtonUp(point, nFlag);
-
-  ReleaseCapture();
-  return true;
-}
-
 CPWL_ComboBox::CPWL_ComboBox(
     const CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData)
     : CPWL_Wnd(cp, std::move(pAttachedData)) {
-  GetCreationParams()->dwFlags &= ~PWS_HSCROLL;
   GetCreationParams()->dwFlags &= ~PWS_VSCROLL;
 }
 
@@ -176,9 +37,9 @@
   // subclasses, implement the virtual OnDestroy method that does the
   // cleanup first, then invokes the superclass OnDestroy ... gee,
   // like a dtor would.
-  m_pList.Release();
-  m_pButton.Release();
-  m_pEdit.Release();
+  m_pList.ExtractAsDangling();
+  m_pButton.ExtractAsDangling();
+  m_pEdit.ExtractAsDangling();
   CPWL_Wnd::OnDestroy();
 }
 
@@ -201,11 +62,20 @@
   return WideString();
 }
 
+void CPWL_ComboBox::ReplaceAndKeepSelection(const WideString& text) {
+  if (m_pEdit)
+    m_pEdit->ReplaceAndKeepSelection(text);
+}
+
 void CPWL_ComboBox::ReplaceSelection(const WideString& text) {
   if (m_pEdit)
     m_pEdit->ReplaceSelection(text);
 }
 
+bool CPWL_ComboBox::SelectAllText() {
+  return m_pEdit && m_pEdit->SelectAllText();
+}
+
 bool CPWL_ComboBox::CanUndo() {
   return m_pEdit && m_pEdit->CanUndo();
 }
@@ -253,15 +123,6 @@
     m_pEdit->SetSelection(nStartChar, nEndChar);
 }
 
-void CPWL_ComboBox::GetEditSelection(int32_t& nStartChar,
-                                     int32_t& nEndChar) const {
-  nStartChar = -1;
-  nEndChar = -1;
-
-  if (m_pEdit)
-    m_pEdit->GetSelection(nStartChar, nEndChar);
-}
-
 void CPWL_ComboBox::ClearSelection() {
   if (m_pEdit)
     m_pEdit->ClearSelection();
@@ -278,8 +139,8 @@
     return;
 
   CreateParams ecp = cp;
-  ecp.dwFlags = PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PES_CENTER |
-                PES_AUTOSCROLL | PES_UNDO;
+  ecp.dwFlags =
+      PWS_VISIBLE | PWS_BORDER | PES_CENTER | PES_AUTOSCROLL | PES_UNDO;
 
   if (HasFlag(PWS_AUTOFONTSIZE))
     ecp.dwFlags |= PWS_AUTOFONTSIZE;
@@ -289,11 +150,10 @@
 
   ecp.rcRectWnd = CFX_FloatRect();
   ecp.dwBorderWidth = 0;
-  ecp.nBorderStyle = BorderStyle::SOLID;
+  ecp.nBorderStyle = BorderStyle::kSolid;
 
-  auto pEdit = pdfium::MakeUnique<CPWL_Edit>(ecp, CloneAttachedData());
+  auto pEdit = std::make_unique<CPWL_Edit>(ecp, CloneAttachedData());
   m_pEdit = pEdit.get();
-  m_pEdit->AttachFFLData(m_pFormFiller.Get());
   AddChild(std::move(pEdit));
   m_pEdit->Realize();
 }
@@ -303,15 +163,15 @@
     return;
 
   CreateParams bcp = cp;
-  bcp.dwFlags = PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PWS_BACKGROUND;
-  bcp.sBackgroundColor = CFX_Color(CFX_Color::kRGB, 220.0f / 255.0f,
+  bcp.dwFlags = PWS_VISIBLE | PWS_BORDER | PWS_BACKGROUND;
+  bcp.sBackgroundColor = CFX_Color(CFX_Color::Type::kRGB, 220.0f / 255.0f,
                                    220.0f / 255.0f, 220.0f / 255.0f);
-  bcp.sBorderColor = PWL_DEFAULT_BLACKCOLOR;
+  bcp.sBorderColor = kDefaultBlackColor;
   bcp.dwBorderWidth = 2;
-  bcp.nBorderStyle = BorderStyle::BEVELED;
-  bcp.eCursorType = FXCT_ARROW;
+  bcp.nBorderStyle = BorderStyle::kBeveled;
+  bcp.eCursorType = IPWL_FillerNotify::CursorStyle::kArrow;
 
-  auto pButton = pdfium::MakeUnique<CPWL_CBButton>(bcp, CloneAttachedData());
+  auto pButton = std::make_unique<CPWL_CBButton>(bcp, CloneAttachedData());
   m_pButton = pButton.get();
   AddChild(std::move(pButton));
   m_pButton->Realize();
@@ -322,31 +182,28 @@
     return;
 
   CreateParams lcp = cp;
-  lcp.dwFlags =
-      PWS_CHILD | PWS_BORDER | PWS_BACKGROUND | PLBS_HOVERSEL | PWS_VSCROLL;
-  lcp.nBorderStyle = BorderStyle::SOLID;
+  lcp.dwFlags = PWS_BORDER | PWS_BACKGROUND | PLBS_HOVERSEL | PWS_VSCROLL;
+  lcp.nBorderStyle = BorderStyle::kSolid;
   lcp.dwBorderWidth = 1;
-  lcp.eCursorType = FXCT_ARROW;
+  lcp.eCursorType = IPWL_FillerNotify::CursorStyle::kArrow;
   lcp.rcRectWnd = CFX_FloatRect();
-
   lcp.fFontSize =
       (cp.dwFlags & PWS_AUTOFONTSIZE) ? kComboBoxDefaultFontSize : cp.fFontSize;
 
-  if (cp.sBorderColor.nColorType == CFX_Color::kTransparent)
-    lcp.sBorderColor = PWL_DEFAULT_BLACKCOLOR;
+  if (cp.sBorderColor.nColorType == CFX_Color::Type::kTransparent)
+    lcp.sBorderColor = kDefaultBlackColor;
 
-  if (cp.sBackgroundColor.nColorType == CFX_Color::kTransparent)
-    lcp.sBackgroundColor = PWL_DEFAULT_WHITECOLOR;
+  if (cp.sBackgroundColor.nColorType == CFX_Color::Type::kTransparent)
+    lcp.sBackgroundColor = kDefaultWhiteColor;
 
-  auto pList = pdfium::MakeUnique<CPWL_CBListBox>(lcp, CloneAttachedData());
+  auto pList = std::make_unique<CPWL_CBListBox>(lcp, CloneAttachedData());
   m_pList = pList.get();
-  m_pList->AttachFFLData(m_pFormFiller.Get());
   AddChild(std::move(pList));
   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();
@@ -370,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;
   }
@@ -399,8 +261,9 @@
 
   if (m_pButton) {
     m_pButton->Move(rcButton, true, false);
-    if (!thisObserved)
+    if (!this_observed) {
       return false;
+    }
   }
 
   CFX_FloatRect rcEdit = rcClient;
@@ -408,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;
@@ -423,7 +291,7 @@
 
 void CPWL_ComboBox::SelectAll() {
   if (m_pEdit && HasFlag(PCBS_ALLOWCUSTOMTEXT))
-    m_pEdit->SelectAll();
+    m_pEdit->SelectAllText();
 }
 
 CFX_FloatRect CPWL_ComboBox::GetFocusRect() const {
@@ -436,7 +304,7 @@
   if (bPopup == m_bPopup)
     return true;
   float fListHeight = m_pList->GetContentRect().Height();
-  if (!IsFloatBigger(fListHeight, 0.0f))
+  if (!FXSYS_IsFloatBigger(fListHeight, 0.0f))
     return true;
 
   if (!bPopup) {
@@ -444,14 +312,12 @@
     return Move(m_rcOldWindow, true, true);
   }
 
-  if (!m_pFillerNotify)
-    return true;
-
-  ObservedPtr<CPWL_ComboBox> thisObserved(this);
-  if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), 0))
-    return !!thisObserved;
-  if (!thisObserved)
+  ObservedPtr<CPWL_ComboBox> this_observed(this);
+  if (GetFillerNotify()->OnPopupPreOpen(GetAttachedData(), {}))
+    return !!this_observed;
+  if (!this_observed) {
     return false;
+  }
 
   float fBorderWidth = m_pList->GetBorderWidth() * 2;
   float fPopupMin = 0.0f;
@@ -461,9 +327,9 @@
 
   bool bBottom;
   float fPopupRet;
-  m_pFillerNotify->QueryWherePopup(GetAttachedData(), fPopupMin, fPopupMax,
-                                   &bBottom, &fPopupRet);
-  if (!IsFloatBigger(fPopupRet, 0.0f))
+  GetFillerNotify()->QueryWherePopup(GetAttachedData(), fPopupMin, fPopupMax,
+                                     &bBottom, &fPopupRet);
+  if (!FXSYS_IsFloatBigger(fPopupRet, 0.0f))
     return true;
 
   m_rcOldWindow = CPWL_Wnd::GetWindowRect();
@@ -479,73 +345,112 @@
   if (!Move(rcWindow, true, true))
     return false;
 
-  m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), 0);
-  return !!thisObserved;
+  GetFillerNotify()->OnPopupPostOpen(GetAttachedData(), {});
+  return !!this_observed;
 }
 
-bool CPWL_ComboBox::OnKeyDown(uint16_t nChar, uint32_t nFlag) {
+bool CPWL_ComboBox::OnKeyDown(FWL_VKEYCODE nKeyCode,
+                              Mask<FWL_EVENTFLAG> nFlag) {
   if (!m_pList)
     return false;
   if (!m_pEdit)
     return false;
 
+  ObservedPtr<CPWL_Wnd> this_observed(this);
   m_nSelectItem = -1;
 
-  switch (nChar) {
+  switch (nKeyCode) {
     case FWL_VKEY_Up:
       if (m_pList->GetCurSel() > 0) {
-        if (m_pFillerNotify) {
-          if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), nFlag))
-            return false;
-          if (m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), nFlag))
-            return false;
+        if (GetFillerNotify()->OnPopupPreOpen(GetAttachedData(), nFlag) ||
+            !this_observed) {
+          return false;
         }
-        if (m_pList->IsMovementKey(nChar)) {
-          if (m_pList->OnMovementKeyDown(nChar, nFlag))
+        if (GetFillerNotify()->OnPopupPostOpen(GetAttachedData(), nFlag) ||
+            !this_observed) {
+          return false;
+        }
+        if (m_pList->IsMovementKey(nKeyCode)) {
+          if (m_pList->OnMovementKeyDown(nKeyCode, nFlag) || !this_observed) {
             return false;
+          }
           SetSelectText();
         }
       }
       return true;
     case FWL_VKEY_Down:
       if (m_pList->GetCurSel() < m_pList->GetCount() - 1) {
-        if (m_pFillerNotify) {
-          if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), nFlag))
-            return false;
-          if (m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), nFlag))
-            return false;
+        if (GetFillerNotify()->OnPopupPreOpen(GetAttachedData(), nFlag) ||
+            !this_observed) {
+          return false;
         }
-        if (m_pList->IsMovementKey(nChar)) {
-          if (m_pList->OnMovementKeyDown(nChar, nFlag))
+        if (GetFillerNotify()->OnPopupPostOpen(GetAttachedData(), nFlag) ||
+            !this_observed) {
+          return false;
+        }
+        if (m_pList->IsMovementKey(nKeyCode)) {
+          if (m_pList->OnMovementKeyDown(nKeyCode, nFlag) || !this_observed) {
             return false;
+          }
           SetSelectText();
         }
       }
       return true;
+    default:
+      break;
   }
 
   if (HasFlag(PCBS_ALLOWCUSTOMTEXT))
-    return m_pEdit->OnKeyDown(nChar, nFlag);
+    return m_pEdit->OnKeyDown(nKeyCode, nFlag);
 
   return false;
 }
 
-bool CPWL_ComboBox::OnChar(uint16_t nChar, uint32_t nFlag) {
+bool CPWL_ComboBox::OnChar(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) {
   if (!m_pList)
     return false;
 
   if (!m_pEdit)
     return false;
 
+  // In a combo box if the ENTER/SPACE key is pressed, show the combo box
+  // options.
+  switch (nChar) {
+    case pdfium::ascii::kReturn:
+      if (!SetPopup(!IsPopup())) {
+        return false;
+      }
+      SetSelectText();
+      return true;
+    case pdfium::ascii::kSpace:
+      // Show the combo box options with space only if the combo box is not
+      // editable
+      if (!HasFlag(PCBS_ALLOWCUSTOMTEXT)) {
+        if (!IsPopup()) {
+          if (!SetPopup(/*bPopUp=*/true)) {
+            return false;
+          }
+          SetSelectText();
+        }
+        return true;
+      }
+      break;
+    default:
+      break;
+  }
+
   m_nSelectItem = -1;
   if (HasFlag(PCBS_ALLOWCUSTOMTEXT))
     return m_pEdit->OnChar(nChar, nFlag);
 
-  if (m_pFillerNotify) {
-    if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), nFlag))
-      return false;
-    if (m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), nFlag))
-      return false;
+  ObservedPtr<CPWL_Wnd> this_observed(this);
+  if (GetFillerNotify()->OnPopupPreOpen(GetAttachedData(), nFlag) ||
+      !this_observed) {
+    return false;
+  }
+  if (GetFillerNotify()->OnPopupPostOpen(GetAttachedData(), nFlag) ||
+      !this_observed) {
+    return false;
   }
   if (!m_pList->IsChar(nChar, nFlag))
     return false;
@@ -554,7 +459,7 @@
 
 void CPWL_ComboBox::NotifyLButtonDown(CPWL_Wnd* child, const CFX_PointF& pos) {
   if (child == m_pButton) {
-    SetPopup(!m_bPopup);
+    (void)SetPopup(!m_bPopup);
     // Note, |this| may no longer be viable at this point. If more work needs to
     // be done, check the return value of SetPopup().
   }
@@ -565,9 +470,9 @@
     return;
 
   SetSelectText();
-  SelectAll();
+  SelectAllText();
   m_pEdit->SetFocus();
-  SetPopup(false);
+  (void)SetPopup(false);
   // Note, |this| may no longer be viable at this point. If more work needs to
   // be done, check the return value of SetPopup().
 }
@@ -577,18 +482,8 @@
 }
 
 void CPWL_ComboBox::SetSelectText() {
-  m_pEdit->SelectAll();
+  m_pEdit->SelectAllText();
   m_pEdit->ReplaceSelection(m_pList->GetText());
-  m_pEdit->SelectAll();
+  m_pEdit->SelectAllText();
   m_nSelectItem = m_pList->GetCurSel();
 }
-
-void CPWL_ComboBox::SetFillerNotify(IPWL_Filler_Notify* pNotify) {
-  m_pFillerNotify = pNotify;
-
-  if (m_pEdit)
-    m_pEdit->SetFillerNotify(pNotify);
-
-  if (m_pList)
-    m_pList->SetFillerNotify(pNotify);
-}
diff --git a/fpdfsdk/pwl/cpwl_combo_box.h b/fpdfsdk/pwl/cpwl_combo_box.h
index 8b9bf12..45729ae 100644
--- a/fpdfsdk/pwl/cpwl_combo_box.h
+++ b/fpdfsdk/pwl/cpwl_combo_box.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,82 +10,54 @@
 #include <memory>
 
 #include "core/fxcrt/unowned_ptr.h"
-#include "fpdfsdk/pwl/cpwl_edit.h"
-#include "fpdfsdk/pwl/cpwl_list_box.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "fpdfsdk/pwl/ipwl_fillernotify.h"
 
-class CPWL_CBListBox final : public CPWL_ListBox {
- public:
-  CPWL_CBListBox(
-      const CreateParams& cp,
-      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
-  ~CPWL_CBListBox() override;
-
-  // CPWL_ListBox
-  bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override;
-
-  bool IsMovementKey(uint16_t nChar) const;
-  bool OnMovementKeyDown(uint16_t nChar, uint32_t nFlag);
-  bool IsChar(uint16_t nChar, uint32_t nFlag) const;
-  bool OnCharNotify(uint16_t nChar, uint32_t nFlag);
-};
-
-class CPWL_CBButton final : public CPWL_Wnd {
- public:
-  CPWL_CBButton(
-      const CreateParams& cp,
-      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
-  ~CPWL_CBButton() override;
-
-  // CPWL_Wnd
-  void DrawThisAppearance(CFX_RenderDevice* pDevice,
-                          const CFX_Matrix& mtUser2Device) override;
-  bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override;
-  bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override;
-};
+class CPWL_Edit;
+class CPWL_CBButton;
+class CPWL_CBListBox;
+class IPWL_FillerNotify;
 
 class CPWL_ComboBox final : public CPWL_Wnd {
  public:
   CPWL_ComboBox(
       const CreateParams& cp,
-      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
+      std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData);
   ~CPWL_ComboBox() override;
 
-  CPWL_Edit* GetEdit() const { return m_pEdit.Get(); }
+  CPWL_Edit* GetEdit() const { return m_pEdit; }
 
   // CPWL_Wnd:
   void OnDestroy() override;
-  bool OnKeyDown(uint16_t nChar, uint32_t nFlag) override;
-  bool OnChar(uint16_t nChar, uint32_t nFlag) override;
+  bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlag) override;
+  bool OnChar(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) override;
   void NotifyLButtonDown(CPWL_Wnd* child, const CFX_PointF& pos) override;
   void NotifyLButtonUp(CPWL_Wnd* child, const CFX_PointF& pos) override;
   void CreateChildWnd(const CreateParams& cp) override;
-  bool RePosChildWnd() override;
+  bool RepositionChildWnd() override;
   CFX_FloatRect GetFocusRect() const override;
   void SetFocus() override;
   void KillFocus() override;
   WideString GetText() override;
   WideString GetSelectedText() override;
+  void ReplaceAndKeepSelection(const WideString& text) override;
   void ReplaceSelection(const WideString& text) override;
+  bool SelectAllText() override;
   bool CanUndo() override;
   bool CanRedo() override;
   bool Undo() override;
   bool Redo() override;
 
-  void SetFillerNotify(IPWL_Filler_Notify* pNotify);
-
   void SetText(const WideString& text);
   void AddString(const WideString& str);
   int32_t GetSelect() const;
   void SetSelect(int32_t nItemIndex);
 
   void SetEditSelection(int32_t nStartChar, int32_t nEndChar);
-  void GetEditSelection(int32_t& nStartChar, int32_t& nEndChar) const;
   void ClearSelection();
   void SelectAll();
   bool IsPopup() const;
   void SetSelectText();
-  void AttachFFLData(CFFL_FormFiller* pData) { m_pFormFiller = pData; }
 
  private:
   void CreateEdit(const CreateParams& cp);
@@ -93,7 +65,7 @@
   void CreateListBox(const CreateParams& cp);
 
   // Returns |true| iff this instance is still allocated.
-  bool SetPopup(bool bPopup);
+  [[nodiscard]] bool SetPopup(bool bPopup);
 
   UnownedPtr<CPWL_Edit> m_pEdit;
   UnownedPtr<CPWL_CBButton> m_pButton;
@@ -102,8 +74,6 @@
   bool m_bPopup = false;
   bool m_bBottom = true;
   int32_t m_nSelectItem = -1;
-  UnownedPtr<IPWL_Filler_Notify> m_pFillerNotify;
-  UnownedPtr<CFFL_FormFiller> m_pFormFiller;
 };
 
 #endif  // FPDFSDK_PWL_CPWL_COMBO_BOX_H_
diff --git a/fpdfsdk/pwl/cpwl_combo_box_edit_embeddertest.cpp b/fpdfsdk/pwl/cpwl_combo_box_edit_embeddertest.cpp
new file mode 100644
index 0000000..3a9d8ef
--- /dev/null
+++ b/fpdfsdk/pwl/cpwl_combo_box_edit_embeddertest.cpp
@@ -0,0 +1,283 @@
+// Copyright 2017 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fpdfsdk/pwl/cpwl_combo_box_embeddertest.h"
+
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/cpdfsdk_widget.h"
+#include "fpdfsdk/formfiller/cffl_formfield.h"
+#include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
+#include "fpdfsdk/pwl/cpwl_combo_box.h"
+#include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "public/fpdf_fwlevent.h"
+#include "testing/embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class CPWLComboBoxEditEmbedderTest : public CPWLComboBoxEmbedderTest {};
+
+TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextEmptyAndBasicNormal) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotNormal());
+
+  // Automatically pre-filled with "Banana".
+  EXPECT_FALSE(GetCPWLComboBox()->GetText().IsEmpty());
+  EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetText().c_str());
+
+  // Check that selection is initially empty, then select entire word.
+  EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty());
+  GetCPWLComboBox()->SetSelectText();
+  EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetSelectedText().c_str());
+
+  // Select other options.
+  GetCPWLComboBox()->SetSelect(0);
+  EXPECT_STREQ(L"Apple", GetCPWLComboBox()->GetSelectedText().c_str());
+  GetCPWLComboBox()->SetSelect(2);
+  EXPECT_STREQ(L"Cherry", GetCPWLComboBox()->GetSelectedText().c_str());
+
+  // Verify that combobox text cannot be edited.
+  EXPECT_FALSE(GetCFFLFormField()->OnChar(GetCPDFSDKAnnotNormal(), 'a', {}));
+}
+
+TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextFragmentsNormal) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotNormal());
+  EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetText().c_str());
+
+  GetCPWLComboBox()->SetEditSelection(0, 0);
+  EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty());
+
+  GetCPWLComboBox()->SetEditSelection(0, 1);
+  EXPECT_STREQ(L"B", GetCPWLComboBox()->GetSelectedText().c_str());
+
+  GetCPWLComboBox()->SetEditSelection(0, -1);
+  EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetSelectedText().c_str());
+
+  GetCPWLComboBox()->SetEditSelection(-8, -1);
+  EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty());
+
+  GetCPWLComboBox()->SetEditSelection(4, 1);
+  EXPECT_STREQ(L"ana", GetCPWLComboBox()->GetSelectedText().c_str());
+
+  GetCPWLComboBox()->SetEditSelection(1, 4);
+  EXPECT_STREQ(L"ana", GetCPWLComboBox()->GetSelectedText().c_str());
+
+  GetCPWLComboBox()->SetEditSelection(5, 6);
+  EXPECT_STREQ(L"a", GetCPWLComboBox()->GetSelectedText().c_str());
+}
+
+TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextEmptyAndBasicEditable) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
+  EXPECT_TRUE(GetCPWLComboBox()->GetText().IsEmpty());
+
+  // Check selection is initially empty, then select a provided option.
+  EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty());
+  GetCPWLComboBox()->SetSelect(0);
+  GetCPWLComboBox()->SetSelectText();
+  EXPECT_STREQ(L"Foo", GetCPWLComboBox()->GetSelectedText().c_str());
+
+  // Select another option and then select last char of that option.
+  GetCPWLComboBox()->SetSelect(1);
+  EXPECT_STREQ(L"Bar", GetCPWLComboBox()->GetSelectedText().c_str());
+  GetCPWLComboBox()->SetEditSelection(2, 3);
+  EXPECT_STREQ(L"r", GetCPWLComboBox()->GetSelectedText().c_str());
+
+  // Type into editable combobox text field and select new text.
+  EXPECT_TRUE(
+      GetCFFLFormField()->OnChar(GetCPDFSDKAnnotUserEditable(), 'a', {}));
+  EXPECT_TRUE(
+      GetCFFLFormField()->OnChar(GetCPDFSDKAnnotUserEditable(), 'b', {}));
+  EXPECT_TRUE(
+      GetCFFLFormField()->OnChar(GetCPDFSDKAnnotUserEditable(), 'c', {}));
+
+  EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty());
+  GetCPWLComboBox()->SetEditSelection(0, 5);
+  EXPECT_STREQ(L"Baabc", GetCPWLComboBox()->GetSelectedText().c_str());
+}
+
+TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextFragmentsEditable) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
+  TypeTextIntoTextField(50);
+
+  GetCPWLComboBox()->SetEditSelection(0, 0);
+  EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty());
+
+  GetCPWLComboBox()->SetEditSelection(0, 1);
+  EXPECT_STREQ(L"A", GetCPWLComboBox()->GetSelectedText().c_str());
+
+  GetCPWLComboBox()->SetEditSelection(0, -1);
+  EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr",
+               GetCPWLComboBox()->GetSelectedText().c_str());
+
+  GetCPWLComboBox()->SetEditSelection(-8, -1);
+  EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty());
+
+  GetCPWLComboBox()->SetEditSelection(23, 12);
+  EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLComboBox()->GetSelectedText().c_str());
+
+  GetCPWLComboBox()->SetEditSelection(12, 23);
+  EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLComboBox()->GetSelectedText().c_str());
+
+  GetCPWLComboBox()->SetEditSelection(49, 50);
+  EXPECT_STREQ(L"r", GetCPWLComboBox()->GetSelectedText().c_str());
+
+  GetCPWLComboBox()->SetEditSelection(49, 55);
+  EXPECT_STREQ(L"r", GetCPWLComboBox()->GetSelectedText().c_str());
+}
+
+TEST_F(CPWLComboBoxEditEmbedderTest, DeleteEntireTextSelection) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
+  TypeTextIntoTextField(50);
+
+  GetCPWLComboBox()->SetEditSelection(0, -1);
+  EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr",
+               GetCPWLComboBox()->GetSelectedText().c_str());
+
+  GetCPWLComboBox()->ReplaceSelection(L"");
+  EXPECT_TRUE(GetCPWLComboBox()->GetText().IsEmpty());
+}
+
+TEST_F(CPWLComboBoxEditEmbedderTest, DeleteTextSelectionMiddle) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
+  TypeTextIntoTextField(50);
+
+  GetCPWLComboBox()->SetEditSelection(12, 23);
+  EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLComboBox()->GetSelectedText().c_str());
+
+  GetCPWLComboBox()->ReplaceSelection(L"");
+  EXPECT_STREQ(L"ABCDEFGHIJKLXYZ[\\]^_`abcdefghijklmnopqr",
+               GetCPWLComboBox()->GetText().c_str());
+}
+
+TEST_F(CPWLComboBoxEditEmbedderTest, DeleteTextSelectionLeft) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
+  TypeTextIntoTextField(50);
+
+  GetCPWLComboBox()->SetEditSelection(0, 5);
+  EXPECT_STREQ(L"ABCDE", GetCPWLComboBox()->GetSelectedText().c_str());
+
+  GetCPWLComboBox()->ReplaceSelection(L"");
+  EXPECT_STREQ(L"FGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr",
+               GetCPWLComboBox()->GetText().c_str());
+}
+
+TEST_F(CPWLComboBoxEditEmbedderTest, DeleteTextSelectionRight) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
+  TypeTextIntoTextField(50);
+
+  GetCPWLComboBox()->SetEditSelection(45, 50);
+  EXPECT_STREQ(L"nopqr", GetCPWLComboBox()->GetSelectedText().c_str());
+
+  GetCPWLComboBox()->ReplaceSelection(L"");
+  EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklm",
+               GetCPWLComboBox()->GetText().c_str());
+}
+
+TEST_F(CPWLComboBoxEditEmbedderTest, DeleteEmptyTextSelection) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
+  TypeTextIntoTextField(50);
+
+  GetCPWLComboBox()->ReplaceSelection(L"");
+  EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr",
+               GetCPWLComboBox()->GetText().c_str());
+}
+
+TEST_F(CPWLComboBoxEditEmbedderTest, InsertTextInEmptyEditableComboBox) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
+  GetCPWLComboBox()->ReplaceSelection(L"Hello");
+  EXPECT_STREQ(L"Hello", GetCPWLComboBox()->GetText().c_str());
+}
+
+TEST_F(CPWLComboBoxEditEmbedderTest,
+       InsertTextInPopulatedEditableComboBoxLeft) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
+  TypeTextIntoTextField(10);
+
+  // Move cursor to beginning of user-editable combobox text field.
+  EXPECT_TRUE(GetCFFLFormField()->OnKeyDown(FWL_VKEY_Home, {}));
+
+  GetCPWLComboBox()->ReplaceSelection(L"Hello");
+  EXPECT_STREQ(L"HelloABCDEFGHIJ", GetCPWLComboBox()->GetText().c_str());
+}
+
+TEST_F(CPWLComboBoxEditEmbedderTest,
+       InsertTextInPopulatedEditableComboBoxMiddle) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
+  TypeTextIntoTextField(10);
+
+  // Move cursor to middle of user-editable combobox text field.
+  for (int i = 0; i < 5; ++i) {
+    EXPECT_TRUE(GetCFFLFormField()->OnKeyDown(FWL_VKEY_Left, {}));
+  }
+
+  GetCPWLComboBox()->ReplaceSelection(L"Hello");
+  EXPECT_STREQ(L"ABCDEHelloFGHIJ", GetCPWLComboBox()->GetText().c_str());
+}
+
+TEST_F(CPWLComboBoxEditEmbedderTest,
+       InsertTextInPopulatedEditableComboBoxRight) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
+  TypeTextIntoTextField(10);
+
+  GetCPWLComboBox()->ReplaceSelection(L"Hello");
+  EXPECT_STREQ(L"ABCDEFGHIJHello", GetCPWLComboBox()->GetText().c_str());
+}
+
+TEST_F(CPWLComboBoxEditEmbedderTest,
+       InsertTextAndReplaceSelectionInPopulatedEditableComboBoxWhole) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
+  TypeTextIntoTextField(10);
+
+  GetCPWLComboBox()->SetEditSelection(0, -1);
+  EXPECT_STREQ(L"ABCDEFGHIJ", GetCPWLComboBox()->GetSelectedText().c_str());
+  GetCPWLComboBox()->ReplaceSelection(L"Hello");
+  EXPECT_STREQ(L"Hello", GetCPWLComboBox()->GetText().c_str());
+}
+
+TEST_F(CPWLComboBoxEditEmbedderTest,
+       InsertTextAndReplaceSelectionInPopulatedEditableComboBoxLeft) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
+  TypeTextIntoTextField(10);
+
+  GetCPWLComboBox()->SetEditSelection(0, 5);
+  EXPECT_STREQ(L"ABCDE", GetCPWLComboBox()->GetSelectedText().c_str());
+  GetCPWLComboBox()->ReplaceSelection(L"Hello");
+  EXPECT_STREQ(L"HelloFGHIJ", GetCPWLComboBox()->GetText().c_str());
+}
+
+TEST_F(CPWLComboBoxEditEmbedderTest,
+       InsertTextAndReplaceSelectionInPopulatedEditableComboBoxMiddle) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
+  TypeTextIntoTextField(10);
+
+  GetCPWLComboBox()->SetEditSelection(2, 7);
+  EXPECT_STREQ(L"CDEFG", GetCPWLComboBox()->GetSelectedText().c_str());
+  GetCPWLComboBox()->ReplaceSelection(L"Hello");
+  EXPECT_STREQ(L"ABHelloHIJ", GetCPWLComboBox()->GetText().c_str());
+}
+
+TEST_F(CPWLComboBoxEditEmbedderTest,
+       InsertTextAndReplaceSelectionInPopulatedEditableComboBoxRight) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
+  TypeTextIntoTextField(10);
+
+  GetCPWLComboBox()->SetEditSelection(5, 10);
+  EXPECT_STREQ(L"FGHIJ", GetCPWLComboBox()->GetSelectedText().c_str());
+  GetCPWLComboBox()->ReplaceSelection(L"Hello");
+  EXPECT_STREQ(L"ABCDEHello", GetCPWLComboBox()->GetText().c_str());
+}
+
+TEST_F(CPWLComboBoxEditEmbedderTest, ReplaceAndKeepSelection) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
+  TypeTextIntoTextField(10);
+
+  GetCPWLComboBox()->SetEditSelection(1, 3);
+  EXPECT_STREQ(L"ABCDEFGHIJ", GetCPWLComboBox()->GetText().c_str());
+  GetCPWLComboBox()->ReplaceAndKeepSelection(L"xyz");
+  EXPECT_STREQ(L"AxyzDEFGHIJ", GetCPWLComboBox()->GetText().c_str());
+  EXPECT_STREQ(L"xyz", GetCPWLComboBox()->GetSelectedText().c_str());
+
+  GetCPWLComboBox()->SetEditSelection(4, 1);
+  GetCPWLComboBox()->ReplaceAndKeepSelection(L"12");
+  EXPECT_STREQ(L"A12DEFGHIJ", GetCPWLComboBox()->GetText().c_str());
+  EXPECT_STREQ(L"12", GetCPWLComboBox()->GetSelectedText().c_str());
+}
diff --git a/fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp b/fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp
index 7d3dd4d..299672f 100644
--- a/fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp
+++ b/fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp
@@ -1,348 +1,75 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "fpdfsdk/cpdfsdk_annot.h"
+#include "fpdfsdk/pwl/cpwl_combo_box_embeddertest.h"
+
 #include "fpdfsdk/cpdfsdk_annotiterator.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
-#include "fpdfsdk/formfiller/cffl_formfiller.h"
+#include "fpdfsdk/cpdfsdk_widget.h"
+#include "fpdfsdk/formfiller/cffl_formfield.h"
 #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
 #include "fpdfsdk/pwl/cpwl_combo_box.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 #include "public/fpdf_fwlevent.h"
-#include "testing/embedder_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-class CPWLComboBoxEditEmbedderTest : public EmbedderTest {
- protected:
-  void SetUp() override {
-    EmbedderTest::SetUp();
-    CreateAndInitializeFormComboboxPDF();
+void CPWLComboBoxEmbedderTest::SetUp() {
+  EmbedderTest::SetUp();
+  CreateAndInitializeFormComboboxPDF();
+}
+
+void CPWLComboBoxEmbedderTest::TearDown() {
+  UnloadPage(GetPage());
+  EmbedderTest::TearDown();
+}
+
+void CPWLComboBoxEmbedderTest::CreateAndInitializeFormComboboxPDF() {
+  ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
+  m_page = LoadPage(0);
+  ASSERT_TRUE(m_page);
+
+  m_pFormFillEnv = CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle());
+  m_pPageView = m_pFormFillEnv->GetPageViewAtIndex(0);
+  CPDFSDK_AnnotIterator iter(m_pPageView, {CPDF_Annot::Subtype::WIDGET});
+
+  // User editable combobox.
+  m_pAnnotEditable = ToCPDFSDKWidget(iter.GetFirstAnnot());
+  ASSERT_TRUE(m_pAnnotEditable);
+
+  // Normal combobox with pre-selected value.
+  m_pAnnotNormal = ToCPDFSDKWidget(iter.GetNextAnnot(m_pAnnotEditable));
+  ASSERT_TRUE(m_pAnnotNormal);
+
+  // Read-only combobox.
+  CPDFSDK_Annot* pAnnotReadOnly = iter.GetNextAnnot(m_pAnnotNormal);
+  CPDFSDK_Annot* pLastAnnot = iter.GetLastAnnot();
+  ASSERT_EQ(pAnnotReadOnly, pLastAnnot);
+}
+
+void CPWLComboBoxEmbedderTest::FormFillerAndWindowSetup(
+    CPDFSDK_Widget* pAnnotCombobox) {
+  CFFL_InteractiveFormFiller* pInteractiveFormFiller =
+      m_pFormFillEnv->GetInteractiveFormFiller();
+  {
+    ObservedPtr<CPDFSDK_Widget> pObserved(pAnnotCombobox);
+    EXPECT_TRUE(pInteractiveFormFiller->OnSetFocus(pObserved, {}));
   }
 
-  void TearDown() override {
-    UnloadPage(GetPage());
-    EmbedderTest::TearDown();
+  m_pFormField = pInteractiveFormFiller->GetFormFieldForTesting(pAnnotCombobox);
+  ASSERT_TRUE(m_pFormField);
+
+  CPWL_Wnd* pWindow =
+      m_pFormField->GetPWLWindow(m_pFormFillEnv->GetPageViewAtIndex(0));
+  ASSERT_TRUE(pWindow);
+  m_pComboBox = static_cast<CPWL_ComboBox*>(pWindow);
+}
+
+void CPWLComboBoxEmbedderTest::TypeTextIntoTextField(int num_chars) {
+  // Type text starting with 'A' to as many chars as specified by |num_chars|.
+  for (int i = 0; i < num_chars; ++i) {
+    EXPECT_TRUE(
+        GetCFFLFormField()->OnChar(GetCPDFSDKAnnotUserEditable(), i + 'A', {}));
   }
-
-  void CreateAndInitializeFormComboboxPDF() {
-    EXPECT_TRUE(OpenDocument("combobox_form.pdf"));
-    m_page = LoadPage(0);
-    ASSERT_TRUE(m_page);
-
-    m_pFormFillEnv =
-        CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle());
-    CPDFSDK_AnnotIterator iter(m_pFormFillEnv->GetPageView(0),
-                               CPDF_Annot::Subtype::WIDGET);
-
-    // User editable combobox.
-    m_pAnnotEditable = iter.GetFirstAnnot();
-    ASSERT_TRUE(m_pAnnotEditable);
-    ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, m_pAnnotEditable->GetAnnotSubtype());
-
-    // Normal combobox with pre-selected value.
-    m_pAnnotNormal = iter.GetNextAnnot(m_pAnnotEditable);
-    ASSERT_TRUE(m_pAnnotNormal);
-    ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, m_pAnnotNormal->GetAnnotSubtype());
-
-    // Read-only combobox.
-    CPDFSDK_Annot* pAnnotReadOnly = iter.GetNextAnnot(m_pAnnotNormal);
-    CPDFSDK_Annot* pLastAnnot = iter.GetLastAnnot();
-    ASSERT_EQ(pAnnotReadOnly, pLastAnnot);
-  }
-
-  void FormFillerAndWindowSetup(CPDFSDK_Annot* pAnnotCombobox) {
-    CFFL_InteractiveFormFiller* pInteractiveFormFiller =
-        m_pFormFillEnv->GetInteractiveFormFiller();
-    {
-      ObservedPtr<CPDFSDK_Annot> pObserved(pAnnotCombobox);
-      EXPECT_TRUE(pInteractiveFormFiller->OnSetFocus(&pObserved, 0));
-    }
-
-    m_pFormFiller =
-        pInteractiveFormFiller->GetFormFillerForTesting(pAnnotCombobox);
-    ASSERT_TRUE(m_pFormFiller);
-
-    CPWL_Wnd* pWindow =
-        m_pFormFiller->GetPWLWindow(m_pFormFillEnv->GetPageView(0), false);
-    ASSERT_TRUE(pWindow);
-    m_pComboBox = static_cast<CPWL_ComboBox*>(pWindow);
-  }
-
-  void TypeTextIntoTextField(int num_chars) {
-    // Type text starting with 'A' to as many chars as specified by |num_chars|.
-    for (int i = 0; i < num_chars; ++i) {
-      EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotUserEditable(),
-                                              i + 'A', 0));
-    }
-  }
-
-  FPDF_PAGE GetPage() const { return m_page; }
-  CPWL_ComboBox* GetCPWLComboBox() const { return m_pComboBox; }
-  CFFL_FormFiller* GetCFFLFormFiller() const { return m_pFormFiller; }
-  CPDFSDK_Annot* GetCPDFSDKAnnotNormal() const { return m_pAnnotNormal; }
-  CPDFSDK_Annot* GetCPDFSDKAnnotUserEditable() const {
-    return m_pAnnotEditable;
-  }
-  CPDFSDK_FormFillEnvironment* GetCPDFSDKFormFillEnv() const {
-    return m_pFormFillEnv;
-  }
-
- private:
-  FPDF_PAGE m_page;
-  CPWL_ComboBox* m_pComboBox;
-  CFFL_FormFiller* m_pFormFiller;
-  CPDFSDK_Annot* m_pAnnotNormal;
-  CPDFSDK_Annot* m_pAnnotEditable;
-  CPDFSDK_FormFillEnvironment* m_pFormFillEnv;
-};
-
-TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextEmptyAndBasicNormal) {
-  FormFillerAndWindowSetup(GetCPDFSDKAnnotNormal());
-
-  // Automatically pre-filled with "Banana".
-  EXPECT_FALSE(GetCPWLComboBox()->GetText().IsEmpty());
-  EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetText().c_str());
-
-  // Check that selection is intially empty, then select entire word.
-  EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty());
-  GetCPWLComboBox()->SetSelectText();
-  EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetSelectedText().c_str());
-
-  // Select other options.
-  GetCPWLComboBox()->SetSelect(0);
-  EXPECT_STREQ(L"Apple", GetCPWLComboBox()->GetSelectedText().c_str());
-  GetCPWLComboBox()->SetSelect(2);
-  EXPECT_STREQ(L"Cherry", GetCPWLComboBox()->GetSelectedText().c_str());
-
-  // Verify that combobox text cannot be edited.
-  EXPECT_FALSE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotNormal(), 'a', 0));
-}
-
-TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextFragmentsNormal) {
-  FormFillerAndWindowSetup(GetCPDFSDKAnnotNormal());
-  EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetText().c_str());
-
-  GetCPWLComboBox()->SetEditSelection(0, 0);
-  EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty());
-
-  GetCPWLComboBox()->SetEditSelection(0, 1);
-  EXPECT_STREQ(L"B", GetCPWLComboBox()->GetSelectedText().c_str());
-
-  GetCPWLComboBox()->SetEditSelection(0, -1);
-  EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetSelectedText().c_str());
-
-  GetCPWLComboBox()->SetEditSelection(-8, -1);
-  EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty());
-
-  GetCPWLComboBox()->SetEditSelection(4, 1);
-  EXPECT_STREQ(L"ana", GetCPWLComboBox()->GetSelectedText().c_str());
-
-  GetCPWLComboBox()->SetEditSelection(1, 4);
-  EXPECT_STREQ(L"ana", GetCPWLComboBox()->GetSelectedText().c_str());
-
-  GetCPWLComboBox()->SetEditSelection(5, 6);
-  EXPECT_STREQ(L"a", GetCPWLComboBox()->GetSelectedText().c_str());
-}
-
-TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextEmptyAndBasicEditable) {
-  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
-  EXPECT_TRUE(GetCPWLComboBox()->GetText().IsEmpty());
-
-  // Check selection is intially empty, then select a provided option.
-  EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty());
-  GetCPWLComboBox()->SetSelect(0);
-  GetCPWLComboBox()->SetSelectText();
-  EXPECT_STREQ(L"Foo", GetCPWLComboBox()->GetSelectedText().c_str());
-
-  // Select another option and then select last char of that option.
-  GetCPWLComboBox()->SetSelect(1);
-  EXPECT_STREQ(L"Bar", GetCPWLComboBox()->GetSelectedText().c_str());
-  GetCPWLComboBox()->SetEditSelection(2, 3);
-  EXPECT_STREQ(L"r", GetCPWLComboBox()->GetSelectedText().c_str());
-
-  // Type into editable combobox text field and select new text.
-  EXPECT_TRUE(
-      GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotUserEditable(), 'a', 0));
-  EXPECT_TRUE(
-      GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotUserEditable(), 'b', 0));
-  EXPECT_TRUE(
-      GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotUserEditable(), 'c', 0));
-
-  EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty());
-  GetCPWLComboBox()->SetEditSelection(0, 5);
-  EXPECT_STREQ(L"Baabc", GetCPWLComboBox()->GetSelectedText().c_str());
-}
-
-TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextFragmentsEditable) {
-  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
-  TypeTextIntoTextField(50);
-
-  GetCPWLComboBox()->SetEditSelection(0, 0);
-  EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty());
-
-  GetCPWLComboBox()->SetEditSelection(0, 1);
-  EXPECT_STREQ(L"A", GetCPWLComboBox()->GetSelectedText().c_str());
-
-  GetCPWLComboBox()->SetEditSelection(0, -1);
-  EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr",
-               GetCPWLComboBox()->GetSelectedText().c_str());
-
-  GetCPWLComboBox()->SetEditSelection(-8, -1);
-  EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty());
-
-  GetCPWLComboBox()->SetEditSelection(23, 12);
-  EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLComboBox()->GetSelectedText().c_str());
-
-  GetCPWLComboBox()->SetEditSelection(12, 23);
-  EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLComboBox()->GetSelectedText().c_str());
-
-  GetCPWLComboBox()->SetEditSelection(49, 50);
-  EXPECT_STREQ(L"r", GetCPWLComboBox()->GetSelectedText().c_str());
-
-  GetCPWLComboBox()->SetEditSelection(49, 55);
-  EXPECT_STREQ(L"r", GetCPWLComboBox()->GetSelectedText().c_str());
-}
-
-TEST_F(CPWLComboBoxEditEmbedderTest, DeleteEntireTextSelection) {
-  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
-  TypeTextIntoTextField(50);
-
-  GetCPWLComboBox()->SetEditSelection(0, -1);
-  EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr",
-               GetCPWLComboBox()->GetSelectedText().c_str());
-
-  GetCPWLComboBox()->ReplaceSelection(L"");
-  EXPECT_TRUE(GetCPWLComboBox()->GetText().IsEmpty());
-}
-
-TEST_F(CPWLComboBoxEditEmbedderTest, DeleteTextSelectionMiddle) {
-  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
-  TypeTextIntoTextField(50);
-
-  GetCPWLComboBox()->SetEditSelection(12, 23);
-  EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLComboBox()->GetSelectedText().c_str());
-
-  GetCPWLComboBox()->ReplaceSelection(L"");
-  EXPECT_STREQ(L"ABCDEFGHIJKLXYZ[\\]^_`abcdefghijklmnopqr",
-               GetCPWLComboBox()->GetText().c_str());
-}
-
-TEST_F(CPWLComboBoxEditEmbedderTest, DeleteTextSelectionLeft) {
-  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
-  TypeTextIntoTextField(50);
-
-  GetCPWLComboBox()->SetEditSelection(0, 5);
-  EXPECT_STREQ(L"ABCDE", GetCPWLComboBox()->GetSelectedText().c_str());
-
-  GetCPWLComboBox()->ReplaceSelection(L"");
-  EXPECT_STREQ(L"FGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr",
-               GetCPWLComboBox()->GetText().c_str());
-}
-
-TEST_F(CPWLComboBoxEditEmbedderTest, DeleteTextSelectionRight) {
-  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
-  TypeTextIntoTextField(50);
-
-  GetCPWLComboBox()->SetEditSelection(45, 50);
-  EXPECT_STREQ(L"nopqr", GetCPWLComboBox()->GetSelectedText().c_str());
-
-  GetCPWLComboBox()->ReplaceSelection(L"");
-  EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklm",
-               GetCPWLComboBox()->GetText().c_str());
-}
-
-TEST_F(CPWLComboBoxEditEmbedderTest, DeleteEmptyTextSelection) {
-  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
-  TypeTextIntoTextField(50);
-
-  GetCPWLComboBox()->ReplaceSelection(L"");
-  EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr",
-               GetCPWLComboBox()->GetText().c_str());
-}
-
-TEST_F(CPWLComboBoxEditEmbedderTest, InsertTextInEmptyEditableComboBox) {
-  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
-  GetCPWLComboBox()->ReplaceSelection(L"Hello");
-  EXPECT_STREQ(L"Hello", GetCPWLComboBox()->GetText().c_str());
-}
-
-TEST_F(CPWLComboBoxEditEmbedderTest,
-       InsertTextInPopulatedEditableComboBoxLeft) {
-  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
-  TypeTextIntoTextField(10);
-
-  // Move cursor to beginning of user-editable combobox text field.
-  EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Home, 0));
-
-  GetCPWLComboBox()->ReplaceSelection(L"Hello");
-  EXPECT_STREQ(L"HelloABCDEFGHIJ", GetCPWLComboBox()->GetText().c_str());
-}
-
-TEST_F(CPWLComboBoxEditEmbedderTest,
-       InsertTextInPopulatedEditableComboBoxMiddle) {
-  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
-  TypeTextIntoTextField(10);
-
-  // Move cursor to middle of user-editable combobox text field.
-  for (int i = 0; i < 5; ++i) {
-    EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Left, 0));
-  }
-
-  GetCPWLComboBox()->ReplaceSelection(L"Hello");
-  EXPECT_STREQ(L"ABCDEHelloFGHIJ", GetCPWLComboBox()->GetText().c_str());
-}
-
-TEST_F(CPWLComboBoxEditEmbedderTest,
-       InsertTextInPopulatedEditableComboBoxRight) {
-  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
-  TypeTextIntoTextField(10);
-
-  GetCPWLComboBox()->ReplaceSelection(L"Hello");
-  EXPECT_STREQ(L"ABCDEFGHIJHello", GetCPWLComboBox()->GetText().c_str());
-}
-
-TEST_F(CPWLComboBoxEditEmbedderTest,
-       InsertTextAndReplaceSelectionInPopulatedEditableComboBoxWhole) {
-  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
-  TypeTextIntoTextField(10);
-
-  GetCPWLComboBox()->SetEditSelection(0, -1);
-  EXPECT_STREQ(L"ABCDEFGHIJ", GetCPWLComboBox()->GetSelectedText().c_str());
-  GetCPWLComboBox()->ReplaceSelection(L"Hello");
-  EXPECT_STREQ(L"Hello", GetCPWLComboBox()->GetText().c_str());
-}
-
-TEST_F(CPWLComboBoxEditEmbedderTest,
-       InsertTextAndReplaceSelectionInPopulatedEditableComboBoxLeft) {
-  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
-  TypeTextIntoTextField(10);
-
-  GetCPWLComboBox()->SetEditSelection(0, 5);
-  EXPECT_STREQ(L"ABCDE", GetCPWLComboBox()->GetSelectedText().c_str());
-  GetCPWLComboBox()->ReplaceSelection(L"Hello");
-  EXPECT_STREQ(L"HelloFGHIJ", GetCPWLComboBox()->GetText().c_str());
-}
-
-TEST_F(CPWLComboBoxEditEmbedderTest,
-       InsertTextAndReplaceSelectionInPopulatedEditableComboBoxMiddle) {
-  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
-  TypeTextIntoTextField(10);
-
-  GetCPWLComboBox()->SetEditSelection(2, 7);
-  EXPECT_STREQ(L"CDEFG", GetCPWLComboBox()->GetSelectedText().c_str());
-  GetCPWLComboBox()->ReplaceSelection(L"Hello");
-  EXPECT_STREQ(L"ABHelloHIJ", GetCPWLComboBox()->GetText().c_str());
-}
-
-TEST_F(CPWLComboBoxEditEmbedderTest,
-       InsertTextAndReplaceSelectionInPopulatedEditableComboBoxRight) {
-  FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
-  TypeTextIntoTextField(10);
-
-  GetCPWLComboBox()->SetEditSelection(5, 10);
-  EXPECT_STREQ(L"FGHIJ", GetCPWLComboBox()->GetSelectedText().c_str());
-  GetCPWLComboBox()->ReplaceSelection(L"Hello");
-  EXPECT_STREQ(L"ABCDEHello", GetCPWLComboBox()->GetText().c_str());
 }
diff --git a/fpdfsdk/pwl/cpwl_combo_box_embeddertest.h b/fpdfsdk/pwl/cpwl_combo_box_embeddertest.h
new file mode 100644
index 0000000..eaf0678
--- /dev/null
+++ b/fpdfsdk/pwl/cpwl_combo_box_embeddertest.h
@@ -0,0 +1,48 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FPDFSDK_PWL_CPWL_COMBO_BOX_EMBEDDERTEST_H_
+#define FPDFSDK_PWL_CPWL_COMBO_BOX_EMBEDDERTEST_H_
+
+#include "public/fpdfview.h"
+#include "testing/embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class CFFL_FormField;
+class CPDFSDK_FormFillEnvironment;
+class CPDFSDK_PageView;
+class CPDFSDK_Widget;
+class CPWL_ComboBox;
+
+class CPWLComboBoxEmbedderTest : public EmbedderTest {
+ protected:
+  void SetUp() override;
+  void TearDown() override;
+
+  void CreateAndInitializeFormComboboxPDF();
+  void FormFillerAndWindowSetup(CPDFSDK_Widget* pAnnotCombobox);
+  void TypeTextIntoTextField(int num_chars);
+  FPDF_PAGE GetPage() const { return m_page; }
+  CPWL_ComboBox* GetCPWLComboBox() const { return m_pComboBox; }
+  CFFL_FormField* GetCFFLFormField() const { return m_pFormField; }
+  CPDFSDK_Widget* GetCPDFSDKAnnotNormal() const { return m_pAnnotNormal; }
+  CPDFSDK_Widget* GetCPDFSDKAnnotUserEditable() const {
+    return m_pAnnotEditable;
+  }
+  CPDFSDK_FormFillEnvironment* GetCPDFSDKFormFillEnv() const {
+    return m_pFormFillEnv;
+  }
+  CPDFSDK_PageView* GetPageView() const { return m_pPageView; }
+
+ private:
+  FPDF_PAGE m_page;
+  CPWL_ComboBox* m_pComboBox = nullptr;
+  CFFL_FormField* m_pFormField = nullptr;
+  CPDFSDK_Widget* m_pAnnotNormal = nullptr;
+  CPDFSDK_Widget* m_pAnnotEditable = nullptr;
+  CPDFSDK_FormFillEnvironment* m_pFormFillEnv = nullptr;
+  CPDFSDK_PageView* m_pPageView = nullptr;
+};
+
+#endif  // FPDFSDK_PWL_CPWL_COMBO_BOX_EMBEDDERTEST_H_
diff --git a/fpdfsdk/pwl/cpwl_edit.cpp b/fpdfsdk/pwl/cpwl_edit.cpp
index 77491da..3657b88 100644
--- a/fpdfsdk/pwl/cpwl_edit.cpp
+++ b/fpdfsdk/pwl/cpwl_edit.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,98 +10,108 @@
 #include <memory>
 #include <sstream>
 #include <utility>
-#include <vector>
 
+#include "constants/ascii.h"
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfdoc/cpvt_word.h"
 #include "core/fpdfdoc/ipvt_fontmap.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/fx_font.h"
 #include "fpdfsdk/pwl/cpwl_caret.h"
-#include "fpdfsdk/pwl/cpwl_edit_ctrl.h"
 #include "fpdfsdk/pwl/cpwl_edit_impl.h"
 #include "fpdfsdk/pwl/cpwl_scroll_bar.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "fpdfsdk/pwl/ipwl_fillernotify.h"
 #include "public/fpdf_fwlevent.h"
+#include "third_party/base/check.h"
 
 CPWL_Edit::CPWL_Edit(
     const CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
-    : CPWL_EditCtrl(cp, std::move(pAttachedData)) {}
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData)
+    : CPWL_Wnd(cp, std::move(pAttachedData)),
+      m_pEditImpl(std::make_unique<CPWL_EditImpl>()) {
+  GetCreationParams()->eCursorType = IPWL_FillerNotify::CursorStyle::kVBeam;
+}
 
 CPWL_Edit::~CPWL_Edit() {
-  ASSERT(!m_bFocus);
+  DCHECK(!m_bFocus);
 }
 
 void CPWL_Edit::SetText(const WideString& csText) {
-  m_pEdit->SetText(csText);
+  m_pEditImpl->SetText(csText);
+  m_pEditImpl->Paint();
 }
 
-bool CPWL_Edit::RePosChildWnd() {
+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 + PWL_SCROLLBAR_WIDTH, rcWindow.top);
+                      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_pEditCaret && !HasFlag(PES_TEXTOVERFLOW)) {
+  if (m_pCaret && !HasFlag(PES_TEXTOVERFLOW)) {
     CFX_FloatRect rect = GetClientRect();
     if (!rect.IsEmpty()) {
       // +1 for caret beside border
       rect.Inflate(1.0f, 1.0f);
       rect.Normalize();
     }
-    m_pEditCaret->SetClipRect(rect);
+    m_pCaret->SetClipRect(rect);
   }
 
-  return CPWL_EditCtrl::RePosChildWnd();
+  m_pEditImpl->SetPlateRect(GetClientRect());
+  m_pEditImpl->Paint();
+  return true;
 }
 
 CFX_FloatRect CPWL_Edit::GetClientRect() const {
   float width = static_cast<float>(GetBorderWidth() + GetInnerBorderWidth());
   CFX_FloatRect rcClient = GetWindowRect().GetDeflated(width, width);
-  if (CPWL_ScrollBar* pVSB = GetVScrollBar()) {
-    if (pVSB->IsVisible()) {
-      rcClient.right -= PWL_SCROLLBAR_WIDTH;
-    }
-  }
-
+  CPWL_ScrollBar* pVSB = GetVScrollBar();
+  if (pVSB && pVSB->IsVisible())
+    rcClient.right -= CPWL_ScrollBar::kWidth;
   return rcClient;
 }
 
 void CPWL_Edit::SetAlignFormatVerticalCenter() {
-  m_pEdit->SetAlignmentV(static_cast<int32_t>(PEAV_CENTER), true);
+  m_pEditImpl->SetAlignmentV(static_cast<int32_t>(PEAV_CENTER));
+  m_pEditImpl->Paint();
 }
 
 bool CPWL_Edit::CanSelectAll() const {
-  return GetSelectWordRange() != m_pEdit->GetWholeWordRange();
+  return GetSelectWordRange() != m_pEditImpl->GetWholeWordRange();
 }
 
 bool CPWL_Edit::CanCopy() const {
-  return !HasFlag(PES_PASSWORD) && !HasFlag(PES_NOREAD) &&
-         m_pEdit->IsSelected();
+  return !HasFlag(PES_PASSWORD) && m_pEditImpl->IsSelected();
 }
 
 bool CPWL_Edit::CanCut() const {
   return CanCopy() && !IsReadOnly();
 }
+
 void CPWL_Edit::CutText() {
   if (!CanCut())
     return;
-  m_pEdit->ClearSelection();
+  m_pEditImpl->ClearSelection();
 }
 
 void CPWL_Edit::OnCreated() {
-  CPWL_EditCtrl::OnCreated();
+  SetFontSize(GetCreationParams()->fFontSize);
+  m_pEditImpl->SetFontMap(GetFontMap());
+  m_pEditImpl->SetNotify(this);
+  m_pEditImpl->Initialize();
 
   if (CPWL_ScrollBar* pScroll = GetVScrollBar()) {
     pScroll->RemoveFlag(PWS_AUTOTRANSPARENT);
@@ -109,51 +119,46 @@
   }
 
   SetParamByFlag();
-
   m_rcOldWindow = GetWindowRect();
-
-  m_pEdit->SetOperationNotify(this);
 }
 
 void CPWL_Edit::SetParamByFlag() {
   if (HasFlag(PES_RIGHT)) {
-    m_pEdit->SetAlignmentH(2, false);
+    m_pEditImpl->SetAlignmentH(2);
   } else if (HasFlag(PES_MIDDLE)) {
-    m_pEdit->SetAlignmentH(1, false);
+    m_pEditImpl->SetAlignmentH(1);
   } else {
-    m_pEdit->SetAlignmentH(0, false);
+    m_pEditImpl->SetAlignmentH(0);
   }
 
-  if (HasFlag(PES_BOTTOM)) {
-    m_pEdit->SetAlignmentV(2, false);
-  } else if (HasFlag(PES_CENTER)) {
-    m_pEdit->SetAlignmentV(1, false);
+  if (HasFlag(PES_CENTER)) {
+    m_pEditImpl->SetAlignmentV(1);
   } else {
-    m_pEdit->SetAlignmentV(0, false);
+    m_pEditImpl->SetAlignmentV(0);
   }
 
   if (HasFlag(PES_PASSWORD)) {
-    m_pEdit->SetPasswordChar('*', false);
+    m_pEditImpl->SetPasswordChar('*');
   }
 
-  m_pEdit->SetMultiLine(HasFlag(PES_MULTILINE), false);
-  m_pEdit->SetAutoReturn(HasFlag(PES_AUTORETURN), false);
-  m_pEdit->SetAutoFontSize(HasFlag(PWS_AUTOFONTSIZE), false);
-  m_pEdit->SetAutoScroll(HasFlag(PES_AUTOSCROLL), false);
-  m_pEdit->EnableUndo(HasFlag(PES_UNDO));
+  m_pEditImpl->SetMultiLine(HasFlag(PES_MULTILINE));
+  m_pEditImpl->SetAutoReturn(HasFlag(PES_AUTORETURN));
+  m_pEditImpl->SetAutoFontSize(HasFlag(PWS_AUTOFONTSIZE));
+  m_pEditImpl->SetAutoScroll(HasFlag(PES_AUTOSCROLL));
+  m_pEditImpl->EnableUndo(HasFlag(PES_UNDO));
 
   if (HasFlag(PES_TEXTOVERFLOW)) {
     SetClipRect(CFX_FloatRect());
-    m_pEdit->SetTextOverflow(true, false);
+    m_pEditImpl->SetTextOverflow(true);
   } else {
-    if (m_pEditCaret) {
+    if (m_pCaret) {
       CFX_FloatRect rect = GetClientRect();
       if (!rect.IsEmpty()) {
         // +1 for caret beside border
         rect.Inflate(1.0f, 1.0f);
         rect.Normalize();
       }
-      m_pEditCaret->SetClipRect(rect);
+      m_pCaret->SetClipRect(rect);
     }
   }
 }
@@ -162,126 +167,54 @@
                                    const CFX_Matrix& mtUser2Device) {
   CPWL_Wnd::DrawThisAppearance(pDevice, mtUser2Device);
 
-  CFX_FloatRect rcClient = GetClientRect();
+  const CFX_FloatRect rcClient = GetClientRect();
+  const BorderStyle border_style = GetBorderStyle();
+  const int32_t nCharArray = m_pEditImpl->GetCharArray();
+  bool draw_border = nCharArray > 0 && (border_style == BorderStyle::kSolid ||
+                                        border_style == BorderStyle::kDash);
+  if (draw_border) {
+    FX_SAFE_INT32 nCharArraySafe = nCharArray;
+    nCharArraySafe -= 1;
+    nCharArraySafe *= 2;
+    draw_border = nCharArraySafe.IsValid();
+  }
 
-  int32_t nCharArray = m_pEdit->GetCharArray();
-  FX_SAFE_INT32 nCharArraySafe = nCharArray;
-  nCharArraySafe -= 1;
-  nCharArraySafe *= 2;
+  if (draw_border) {
+    CFX_GraphStateData gsd;
+    gsd.m_LineWidth = GetBorderWidth();
+    if (border_style == BorderStyle::kDash) {
+      gsd.m_DashArray = {static_cast<float>(GetBorderDash().nDash),
+                         static_cast<float>(GetBorderDash().nGap)};
+      gsd.m_DashPhase = GetBorderDash().nPhase;
+    }
 
-  if (nCharArray > 0 && nCharArraySafe.IsValid()) {
-    switch (GetBorderStyle()) {
-      case BorderStyle::SOLID: {
-        CFX_GraphStateData gsd;
-        gsd.m_LineWidth = GetBorderWidth();
-
-        CFX_PathData path;
-
-        for (int32_t i = 0; i < nCharArray - 1; i++) {
-          path.AppendPoint(
-              CFX_PointF(
-                  rcClient.left +
-                      ((rcClient.right - rcClient.left) / nCharArray) * (i + 1),
-                  rcClient.bottom),
-              FXPT_TYPE::MoveTo, false);
-          path.AppendPoint(
-              CFX_PointF(
-                  rcClient.left +
-                      ((rcClient.right - rcClient.left) / nCharArray) * (i + 1),
-                  rcClient.top),
-              FXPT_TYPE::LineTo, false);
-        }
-        if (!path.GetPoints().empty()) {
-          pDevice->DrawPath(&path, &mtUser2Device, &gsd, 0,
-                            GetBorderColor().ToFXColor(255), FXFILL_ALTERNATE);
-        }
-        break;
-      }
-      case BorderStyle::DASH: {
-        CFX_GraphStateData gsd;
-        gsd.m_LineWidth = static_cast<float>(GetBorderWidth());
-        gsd.m_DashArray = {static_cast<float>(GetBorderDash().nDash),
-                           static_cast<float>(GetBorderDash().nGap)};
-        gsd.m_DashPhase = static_cast<float>(GetBorderDash().nPhase);
-
-        CFX_PathData path;
-        for (int32_t i = 0; i < nCharArray - 1; i++) {
-          path.AppendPoint(
-              CFX_PointF(
-                  rcClient.left +
-                      ((rcClient.right - rcClient.left) / nCharArray) * (i + 1),
-                  rcClient.bottom),
-              FXPT_TYPE::MoveTo, false);
-          path.AppendPoint(
-              CFX_PointF(
-                  rcClient.left +
-                      ((rcClient.right - rcClient.left) / nCharArray) * (i + 1),
-                  rcClient.top),
-              FXPT_TYPE::LineTo, false);
-        }
-        if (!path.GetPoints().empty()) {
-          pDevice->DrawPath(&path, &mtUser2Device, &gsd, 0,
-                            GetBorderColor().ToFXColor(255), FXFILL_ALTERNATE);
-        }
-        break;
-      }
-      default:
-        break;
+    const float width = (rcClient.right - rcClient.left) / nCharArray;
+    CFX_Path path;
+    CFX_PointF bottom(0, rcClient.bottom);
+    CFX_PointF top(0, rcClient.top);
+    for (int32_t i = 0; i < nCharArray - 1; ++i) {
+      bottom.x = rcClient.left + width * (i + 1);
+      top.x = bottom.x;
+      path.AppendPoint(bottom, CFX_Path::Point::Type::kMove);
+      path.AppendPoint(top, CFX_Path::Point::Type::kLine);
+    }
+    if (!path.GetPoints().empty()) {
+      pDevice->DrawPath(path, &mtUser2Device, &gsd, 0,
+                        GetBorderColor().ToFXColor(255),
+                        CFX_FillRenderOptions::EvenOddOptions());
     }
   }
 
   CFX_FloatRect rcClip;
-  CPVT_WordRange wrRange = m_pEdit->GetVisibleWordRange();
+  CPVT_WordRange wrRange = m_pEditImpl->GetVisibleWordRange();
   CPVT_WordRange* pRange = nullptr;
   if (!HasFlag(PES_TEXTOVERFLOW)) {
     rcClip = GetClientRect();
     pRange = &wrRange;
   }
-
-  CPWL_EditImpl::DrawEdit(pDevice, mtUser2Device, m_pEdit.get(),
-                          GetTextColor().ToFXColor(GetTransparency()), rcClip,
-                          CFX_PointF(), pRange, GetSystemHandler(),
-                          m_pFormFiller.Get());
-}
-
-bool CPWL_Edit::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnLButtonDown(point, nFlag);
-
-  if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) {
-    if (m_bMouseDown && !InvalidateRect(nullptr))
-      return true;
-
-    m_bMouseDown = true;
-    SetCapture();
-
-    m_pEdit->OnMouseDown(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
-  }
-
-  return true;
-}
-
-bool CPWL_Edit::OnLButtonDblClk(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnLButtonDblClk(point, nFlag);
-
-  if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) {
-    m_pEdit->SelectAll();
-  }
-
-  return true;
-}
-
-bool CPWL_Edit::OnRButtonUp(const CFX_PointF& point, uint32_t nFlag) {
-  if (m_bMouseDown)
-    return false;
-
-  CPWL_Wnd::OnRButtonUp(point, nFlag);
-
-  if (!HasFlag(PES_TEXTOVERFLOW) && !ClientHitTest(point))
-    return true;
-
-  SetFocus();
-
-  return false;
+  m_pEditImpl->DrawEdit(
+      pDevice, mtUser2Device, GetTextColor().ToFXColor(GetTransparency()),
+      rcClip, CFX_PointF(), pRange, GetFillerNotify(), GetAttachedData());
 }
 
 void CPWL_Edit::OnSetFocus() {
@@ -291,8 +224,9 @@
     return;
 
   if (!IsReadOnly()) {
-    if (CPWL_Wnd::FocusHandlerIface* pFocusHandler = GetFocusHandler()) {
-      pFocusHandler->OnSetFocus(this);
+    CPWL_Wnd::ProviderIface* pProvider = GetProvider();
+    if (pProvider) {
+      pProvider->OnSetFocusForEdit(this);
       if (!observed_ptr)
         return;
     }
@@ -304,59 +238,43 @@
   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_pEdit->SelectNone();
+  m_pEditImpl->SelectNone();
   if (!observed_ptr)
     return;
 
   if (!SetCaret(false, CFX_PointF(), CFX_PointF()))
     return;
 
-  SetCharSet(FX_CHARSET_ANSI);
+  SetCharSet(FX_Charset::kANSI);
   m_bFocus = false;
 }
 
-void CPWL_Edit::SetCharSpace(float fCharSpace) {
-  m_pEdit->SetCharSpace(fCharSpace);
-}
-
 CPVT_WordRange CPWL_Edit::GetSelectWordRange() const {
-  if (!m_pEdit->IsSelected())
+  if (!m_pEditImpl->IsSelected())
     return CPVT_WordRange();
 
-  int32_t nStart = -1;
-  int32_t nEnd = -1;
+  int32_t nStart;
+  int32_t nEnd;
+  std::tie(nStart, nEnd) = m_pEditImpl->GetSelection();
 
-  m_pEdit->GetSelection(nStart, nEnd);
-
-  CPVT_WordPlace wpStart = m_pEdit->WordIndexToWordPlace(nStart);
-  CPVT_WordPlace wpEnd = m_pEdit->WordIndexToWordPlace(nEnd);
-
+  CPVT_WordPlace wpStart = m_pEditImpl->WordIndexToWordPlace(nStart);
+  CPVT_WordPlace wpEnd = m_pEditImpl->WordIndexToWordPlace(nEnd);
   return CPVT_WordRange(wpStart, wpEnd);
 }
 
-CFX_PointF CPWL_Edit::GetWordRightBottomPoint(const CPVT_WordPlace& wpWord) {
-  CPWL_EditImpl_Iterator* pIterator = m_pEdit->GetIterator();
-  CPVT_WordPlace wpOld = pIterator->GetAt();
-  pIterator->SetAt(wpWord);
-
-  CFX_PointF pt;
-  CPVT_Word word;
-  if (pIterator->GetWord(word))
-    pt = CFX_PointF(word.ptWord.x + word.fWidth, word.ptWord.y + word.fDescent);
-  pIterator->SetAt(wpOld);
-  return pt;
-}
-
 bool CPWL_Edit::IsTextFull() const {
-  return m_pEdit->IsTextFull();
+  return m_pEditImpl->IsTextFull();
 }
 
 float CPWL_Edit::GetCharArrayAutoFontSize(const CPDF_Font* pFont,
@@ -378,8 +296,9 @@
   if (!HasFlag(PES_CHARARRAY) || nCharArray <= 0)
     return;
 
-  m_pEdit->SetCharArray(nCharArray);
-  m_pEdit->SetTextOverflow(true, true);
+  m_pEditImpl->SetCharArray(nCharArray);
+  m_pEditImpl->SetTextOverflow(true);
+  m_pEditImpl->Paint();
 
   if (!HasFlag(PWS_AUTOFONTSIZE))
     return;
@@ -393,12 +312,14 @@
   if (fFontSize <= 0.0f)
     return;
 
-  m_pEdit->SetAutoFontSize(false, true);
-  m_pEdit->SetFontSize(fFontSize);
+  m_pEditImpl->SetAutoFontSize(false);
+  m_pEditImpl->SetFontSize(fFontSize);
+  m_pEditImpl->Paint();
 }
 
 void CPWL_Edit::SetLimitChar(int32_t nLimitChar) {
-  m_pEdit->SetLimitChar(nLimitChar);
+  m_pEditImpl->SetLimitChar(nLimitChar);
+  m_pEditImpl->Paint();
 }
 
 CFX_FloatRect CPWL_Edit::GetFocusRect() const {
@@ -410,61 +331,61 @@
   return pScroll && pScroll->IsVisible();
 }
 
-bool CPWL_Edit::OnKeyDown(uint16_t nChar, uint32_t nFlag) {
+bool CPWL_Edit::OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlag) {
   if (m_bMouseDown)
     return true;
 
-  if (nChar == FWL_VKEY_Delete) {
-    if (m_pFillerNotify) {
-      WideString strChange;
-      WideString strChangeEx;
+  if (nKeyCode == FWL_VKEY_Delete) {
+    WideString strChange;
+    WideString strChangeEx;
 
-      int nSelStart = 0;
-      int nSelEnd = 0;
-      GetSelection(nSelStart, nSelEnd);
+    int nSelStart;
+    int nSelEnd;
+    std::tie(nSelStart, nSelEnd) = GetSelection();
 
-      if (nSelStart == nSelEnd)
-        nSelEnd = nSelStart + 1;
+    if (nSelStart == nSelEnd)
+      nSelEnd = nSelStart + 1;
 
-      ObservedPtr<CPWL_Wnd> thisObserved(this);
+    ObservedPtr<CPWL_Wnd> this_observed(this);
 
-      bool bRC;
-      bool bExit;
-      std::tie(bRC, bExit) = m_pFillerNotify->OnBeforeKeyStroke(
-          GetAttachedData(), strChange, strChangeEx, nSelStart, nSelEnd, true,
-          nFlag);
+    bool bRC;
+    bool bExit;
+    std::tie(bRC, bExit) = GetFillerNotify()->OnBeforeKeyStroke(
+        GetAttachedData(), strChange, strChangeEx, nSelStart, nSelEnd, true,
+        nFlag);
 
-      if (!thisObserved)
-        return false;
-
-      if (!bRC)
-        return false;
-      if (bExit)
-        return false;
+    if (!this_observed) {
+      return false;
     }
+
+    if (!bRC)
+      return false;
+    if (bExit)
+      return false;
   }
 
-  bool bRet = CPWL_EditCtrl::OnKeyDown(nChar, nFlag);
+  bool bRet = OnKeyDownInternal(nKeyCode, nFlag);
 
   // In case of implementation swallow the OnKeyDown event.
-  if (IsProceedtoOnChar(nChar, nFlag))
+  if (IsProceedtoOnChar(nKeyCode, nFlag))
     return true;
 
   return bRet;
 }
 
 // static
-bool CPWL_Edit::IsProceedtoOnChar(uint16_t nKeyCode, uint32_t nFlag) {
-  bool bCtrl = IsCTRLpressed(nFlag);
-  bool bAlt = IsALTpressed(nFlag);
+bool CPWL_Edit::IsProceedtoOnChar(FWL_VKEYCODE nKeyCode,
+                                  Mask<FWL_EVENTFLAG> nFlag) {
+  bool bCtrl = IsPlatformShortcutKey(nFlag);
+  bool bAlt = IsALTKeyDown(nFlag);
   if (bCtrl && !bAlt) {
     // hot keys for edit control.
     switch (nKeyCode) {
-      case 'C':
-      case 'V':
-      case 'X':
-      case 'A':
-      case 'Z':
+      case FWL_VKEY_A:
+      case FWL_VKEY_C:
+      case FWL_VKEY_V:
+      case FWL_VKEY_X:
+      case FWL_VKEY_Z:
         return true;
       default:
         break;
@@ -482,42 +403,40 @@
   }
 }
 
-bool CPWL_Edit::OnChar(uint16_t nChar, uint32_t nFlag) {
+bool CPWL_Edit::OnChar(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) {
   if (m_bMouseDown)
     return true;
 
   bool bRC = true;
   bool bExit = false;
 
-  if (!IsCTRLpressed(nFlag)) {
-    if (m_pFillerNotify) {
-      WideString swChange;
+  if (!IsCTRLKeyDown(nFlag)) {
+    WideString swChange;
+    int nSelStart;
+    int nSelEnd;
+    std::tie(nSelStart, nSelEnd) = GetSelection();
 
-      int nSelStart = 0;
-      int nSelEnd = 0;
-      GetSelection(nSelStart, nSelEnd);
+    switch (nChar) {
+      case pdfium::ascii::kBackspace:
+        if (nSelStart == nSelEnd)
+          nSelStart = nSelEnd - 1;
+        break;
+      case pdfium::ascii::kReturn:
+        break;
+      default:
+        swChange += nChar;
+        break;
+    }
 
-      switch (nChar) {
-        case FWL_VKEY_Back:
-          if (nSelStart == nSelEnd)
-            nSelStart = nSelEnd - 1;
-          break;
-        case FWL_VKEY_Return:
-          break;
-        default:
-          swChange += nChar;
-          break;
-      }
+    ObservedPtr<CPWL_Wnd> this_observed(this);
 
-      ObservedPtr<CPWL_Wnd> thisObserved(this);
+    WideString strChangeEx;
+    std::tie(bRC, bExit) = GetFillerNotify()->OnBeforeKeyStroke(
+        GetAttachedData(), swChange, strChangeEx, nSelStart, nSelEnd, true,
+        nFlag);
 
-      WideString strChangeEx;
-      std::tie(bRC, bExit) = m_pFillerNotify->OnBeforeKeyStroke(
-          GetAttachedData(), swChange, strChangeEx, nSelStart, nSelEnd, true,
-          nFlag);
-
-      if (!thisObserved)
-        return false;
+    if (!this_observed) {
+      return false;
     }
   }
 
@@ -527,25 +446,25 @@
     return false;
 
   if (IPVT_FontMap* pFontMap = GetFontMap()) {
-    int32_t nOldCharSet = GetCharSet();
-    int32_t nNewCharSet =
-        pFontMap->CharSetFromUnicode(nChar, FX_CHARSET_Default);
+    FX_Charset nOldCharSet = GetCharSet();
+    FX_Charset nNewCharSet =
+        pFontMap->CharSetFromUnicode(nChar, FX_Charset::kDefault);
     if (nOldCharSet != nNewCharSet) {
       SetCharSet(nNewCharSet);
     }
   }
 
-  return CPWL_EditCtrl::OnChar(nChar, nFlag);
+  return OnCharInternal(nChar, nFlag);
 }
 
-bool CPWL_Edit::OnMouseWheel(short zDelta,
+bool CPWL_Edit::OnMouseWheel(Mask<FWL_EVENTFLAG> nFlag,
                              const CFX_PointF& point,
-                             uint32_t nFlag) {
+                             const CFX_Vector& delta) {
   if (!HasFlag(PES_MULTILINE))
     return false;
 
   CFX_PointF ptScroll = GetScrollPos();
-  if (zDelta > 0)
+  if (delta.y > 0)
     ptScroll.y += GetFontSize();
   else
     ptScroll.y -= GetFontSize();
@@ -553,117 +472,402 @@
   return true;
 }
 
-void CPWL_Edit::OnInsertReturn(const CPVT_WordPlace& place,
-                               const CPVT_WordPlace& oldplace) {
-  if (HasFlag(PES_SPELLCHECK)) {
-    m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
-                                               GetLatinWordsRange(place)));
+void CPWL_Edit::OnDestroy() {
+  m_pCaret.ExtractAsDangling();
+}
+
+bool CPWL_Edit::IsWndHorV() const {
+  CFX_Matrix mt = GetWindowMatrix();
+  return mt.Transform(CFX_PointF(1, 1)).y == mt.Transform(CFX_PointF(0, 1)).y;
+}
+
+void CPWL_Edit::SetCursor() {
+  if (IsValid()) {
+    GetFillerNotify()->SetCursor(IsWndHorV()
+                                     ? IPWL_FillerNotify::CursorStyle::kVBeam
+                                     : IPWL_FillerNotify::CursorStyle::kHBeam);
   }
 }
 
-void CPWL_Edit::OnBackSpace(const CPVT_WordPlace& place,
-                            const CPVT_WordPlace& oldplace) {
-  if (HasFlag(PES_SPELLCHECK)) {
-    m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
-                                               GetLatinWordsRange(place)));
+WideString CPWL_Edit::GetSelectedText() {
+  return m_pEditImpl->GetSelectedText();
+}
+
+void CPWL_Edit::ReplaceAndKeepSelection(const WideString& text) {
+  m_pEditImpl->ReplaceAndKeepSelection(text);
+}
+
+void CPWL_Edit::ReplaceSelection(const WideString& text) {
+  m_pEditImpl->ReplaceSelection(text);
+}
+
+bool CPWL_Edit::SelectAllText() {
+  m_pEditImpl->SelectAll();
+  return true;
+}
+
+void CPWL_Edit::SetScrollInfo(const PWL_SCROLL_INFO& info) {
+  if (CPWL_Wnd* pChild = GetVScrollBar())
+    pChild->SetScrollInfo(info);
+}
+
+void CPWL_Edit::SetScrollPosition(float pos) {
+  if (CPWL_Wnd* pChild = GetVScrollBar())
+    pChild->SetScrollPosition(pos);
+}
+
+void CPWL_Edit::ScrollWindowVertically(float pos) {
+  m_pEditImpl->SetScrollPos(CFX_PointF(m_pEditImpl->GetScrollPos().x, pos));
+}
+
+void CPWL_Edit::CreateChildWnd(const CreateParams& cp) {
+  if (!IsReadOnly())
+    CreateEditCaret(cp);
+}
+
+void CPWL_Edit::CreateEditCaret(const CreateParams& cp) {
+  if (m_pCaret)
+    return;
+
+  CreateParams ecp = cp;
+  ecp.dwFlags = PWS_NOREFRESHCLIP;
+  ecp.dwBorderWidth = 0;
+  ecp.nBorderStyle = BorderStyle::kSolid;
+  ecp.rcRectWnd = CFX_FloatRect();
+
+  auto pCaret = std::make_unique<CPWL_Caret>(ecp, CloneAttachedData());
+  m_pCaret = pCaret.get();
+  m_pCaret->SetInvalidRect(GetClientRect());
+  AddChild(std::move(pCaret));
+  m_pCaret->Realize();
+}
+
+void CPWL_Edit::SetFontSize(float fFontSize) {
+  m_pEditImpl->SetFontSize(fFontSize);
+  m_pEditImpl->Paint();
+}
+
+float CPWL_Edit::GetFontSize() const {
+  return m_pEditImpl->GetFontSize();
+}
+
+bool CPWL_Edit::OnKeyDownInternal(FWL_VKEYCODE nKeyCode,
+                                  Mask<FWL_EVENTFLAG> nFlag) {
+  if (m_bMouseDown)
+    return true;
+
+  bool bRet = CPWL_Wnd::OnKeyDown(nKeyCode, nFlag);
+
+  // FILTER
+  switch (nKeyCode) {
+    default:
+      return false;
+    case FWL_VKEY_Delete:
+    case FWL_VKEY_Up:
+    case FWL_VKEY_Down:
+    case FWL_VKEY_Left:
+    case FWL_VKEY_Right:
+    case FWL_VKEY_Home:
+    case FWL_VKEY_End:
+    case FWL_VKEY_Insert:
+    case FWL_VKEY_A:
+    case FWL_VKEY_C:
+    case FWL_VKEY_V:
+    case FWL_VKEY_X:
+    case FWL_VKEY_Z:
+      break;
   }
-}
 
-void CPWL_Edit::OnDelete(const CPVT_WordPlace& place,
-                         const CPVT_WordPlace& oldplace) {
-  if (HasFlag(PES_SPELLCHECK)) {
-    m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
-                                               GetLatinWordsRange(place)));
+  if (nKeyCode == FWL_VKEY_Delete && m_pEditImpl->IsSelected())
+    nKeyCode = FWL_VKEY_Unknown;
+
+  switch (nKeyCode) {
+    case FWL_VKEY_Delete:
+      Delete();
+      return true;
+    case FWL_VKEY_Insert:
+      if (IsSHIFTKeyDown(nFlag))
+        PasteText();
+      return true;
+    case FWL_VKEY_Up:
+      m_pEditImpl->OnVK_UP(IsSHIFTKeyDown(nFlag));
+      return true;
+    case FWL_VKEY_Down:
+      m_pEditImpl->OnVK_DOWN(IsSHIFTKeyDown(nFlag));
+      return true;
+    case FWL_VKEY_Left:
+      m_pEditImpl->OnVK_LEFT(IsSHIFTKeyDown(nFlag));
+      return true;
+    case FWL_VKEY_Right:
+      m_pEditImpl->OnVK_RIGHT(IsSHIFTKeyDown(nFlag));
+      return true;
+    case FWL_VKEY_Home:
+      m_pEditImpl->OnVK_HOME(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
+      return true;
+    case FWL_VKEY_End:
+      m_pEditImpl->OnVK_END(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
+      return true;
+    case FWL_VKEY_Unknown:
+      if (!IsSHIFTKeyDown(nFlag))
+        ClearSelection();
+      else
+        CutText();
+      return true;
+    default:
+      break;
   }
+
+  return bRet;
 }
 
-void CPWL_Edit::OnClear(const CPVT_WordPlace& place,
-                        const CPVT_WordPlace& oldplace) {
-  if (HasFlag(PES_SPELLCHECK)) {
-    m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
-                                               GetLatinWordsRange(place)));
+bool CPWL_Edit::OnCharInternal(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) {
+  if (m_bMouseDown)
+    return true;
+
+  CPWL_Wnd::OnChar(nChar, nFlag);
+
+  // FILTER
+  switch (nChar) {
+    case pdfium::ascii::kNewline:
+    case pdfium::ascii::kEscape:
+      return false;
+    default:
+      break;
   }
-}
 
-void CPWL_Edit::OnInsertWord(const CPVT_WordPlace& place,
-                             const CPVT_WordPlace& oldplace) {
-  if (HasFlag(PES_SPELLCHECK)) {
-    m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
-                                               GetLatinWordsRange(place)));
-  }
-}
+  bool bCtrl = IsPlatformShortcutKey(nFlag);
+  bool bAlt = IsALTKeyDown(nFlag);
+  bool bShift = IsSHIFTKeyDown(nFlag);
 
-void CPWL_Edit::OnInsertText(const CPVT_WordPlace& place,
-                             const CPVT_WordPlace& oldplace) {
-  if (HasFlag(PES_SPELLCHECK)) {
-    m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
-                                               GetLatinWordsRange(place)));
-  }
-}
+  uint16_t word = nChar;
 
-CPVT_WordRange CPWL_Edit::CombineWordRange(const CPVT_WordRange& wr1,
-                                           const CPVT_WordRange& wr2) {
-  return CPVT_WordRange(std::min(wr1.BeginPos, wr2.BeginPos),
-                        std::max(wr1.EndPos, wr2.EndPos));
-}
-
-CPVT_WordRange CPWL_Edit::GetLatinWordsRange(const CFX_PointF& point) const {
-  return GetSameWordsRange(m_pEdit->SearchWordPlace(point), true, false);
-}
-
-CPVT_WordRange CPWL_Edit::GetLatinWordsRange(
-    const CPVT_WordPlace& place) const {
-  return GetSameWordsRange(place, true, false);
-}
-
-#define PWL_ISARABICWORD(word) \
-  ((word >= 0x0600 && word <= 0x06FF) || (word >= 0xFB50 && word <= 0xFEFC))
-
-CPVT_WordRange CPWL_Edit::GetSameWordsRange(const CPVT_WordPlace& place,
-                                            bool bLatin,
-                                            bool bArabic) const {
-  CPWL_EditImpl_Iterator* pIterator = m_pEdit->GetIterator();
-  CPVT_Word wordinfo;
-  CPVT_WordPlace wpStart(place), wpEnd(place);
-  pIterator->SetAt(place);
-
-  if (bLatin) {
-    while (pIterator->NextWord()) {
-      if (!pIterator->GetWord(wordinfo) ||
-          !FX_EDIT_ISLATINWORD(wordinfo.Word)) {
-        break;
-      }
-
-      wpEnd = pIterator->GetAt();
-    }
-  } else if (bArabic) {
-    while (pIterator->NextWord()) {
-      if (!pIterator->GetWord(wordinfo) || !PWL_ISARABICWORD(wordinfo.Word))
-        break;
-
-      wpEnd = pIterator->GetAt();
+  if (bCtrl && !bAlt) {
+    switch (nChar) {
+      case pdfium::ascii::kControlC:
+        CopyText();
+        return true;
+      case pdfium::ascii::kControlV:
+        PasteText();
+        return true;
+      case pdfium::ascii::kControlX:
+        CutText();
+        return true;
+      case pdfium::ascii::kControlA:
+        SelectAllText();
+        return true;
+      case pdfium::ascii::kControlZ:
+        if (bShift)
+          Redo();
+        else
+          Undo();
+        return true;
+      default:
+        if (nChar < 32)
+          return false;
     }
   }
 
-  pIterator->SetAt(place);
+  if (IsReadOnly())
+    return true;
 
-  if (bLatin) {
-    do {
-      if (!pIterator->GetWord(wordinfo) ||
-          !FX_EDIT_ISLATINWORD(wordinfo.Word)) {
-        break;
-      }
+  if (m_pEditImpl->IsSelected() && word == pdfium::ascii::kBackspace)
+    word = pdfium::ascii::kNul;
 
-      wpStart = pIterator->GetAt();
-    } while (pIterator->PrevWord());
-  } else if (bArabic) {
-    do {
-      if (!pIterator->GetWord(wordinfo) || !PWL_ISARABICWORD(wordinfo.Word))
-        break;
+  ClearSelection();
 
-      wpStart = pIterator->GetAt();
-    } while (pIterator->PrevWord());
+  switch (word) {
+    case pdfium::ascii::kBackspace:
+      Backspace();
+      break;
+    case pdfium::ascii::kReturn:
+      InsertReturn();
+      break;
+    case pdfium::ascii::kNul:
+      break;
+    default:
+      InsertWord(word, GetCharSet());
+      break;
   }
 
-  return CPVT_WordRange(wpStart, wpEnd);
+  return true;
+}
+
+bool CPWL_Edit::OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,
+                              const CFX_PointF& point) {
+  CPWL_Wnd::OnLButtonDown(nFlag, point);
+  if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) {
+    if (m_bMouseDown && !InvalidateRect(nullptr))
+      return true;
+
+    m_bMouseDown = true;
+    SetCapture();
+    m_pEditImpl->OnMouseDown(point, IsSHIFTKeyDown(nFlag),
+                             IsCTRLKeyDown(nFlag));
+  }
+  return true;
+}
+
+bool CPWL_Edit::OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag,
+                            const CFX_PointF& point) {
+  CPWL_Wnd::OnLButtonUp(nFlag, point);
+  if (m_bMouseDown) {
+    // can receive keybord message
+    if (ClientHitTest(point) && !IsFocused())
+      SetFocus();
+
+    ReleaseCapture();
+    m_bMouseDown = false;
+  }
+  return true;
+}
+
+bool CPWL_Edit::OnLButtonDblClk(Mask<FWL_EVENTFLAG> nFlag,
+                                const CFX_PointF& point) {
+  CPWL_Wnd::OnLButtonDblClk(nFlag, point);
+  if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point))
+    m_pEditImpl->SelectAll();
+
+  return true;
+}
+
+bool CPWL_Edit::OnRButtonUp(Mask<FWL_EVENTFLAG> nFlag,
+                            const CFX_PointF& point) {
+  if (m_bMouseDown)
+    return false;
+
+  CPWL_Wnd::OnRButtonUp(nFlag, point);
+  if (!HasFlag(PES_TEXTOVERFLOW) && !ClientHitTest(point))
+    return true;
+
+  SetFocus();
+  return false;
+}
+
+bool CPWL_Edit::OnMouseMove(Mask<FWL_EVENTFLAG> nFlag,
+                            const CFX_PointF& point) {
+  CPWL_Wnd::OnMouseMove(nFlag, point);
+
+  if (m_bMouseDown)
+    m_pEditImpl->OnMouseMove(point, false, false);
+
+  return true;
+}
+
+void CPWL_Edit::SetEditCaret(bool bVisible) {
+  CFX_PointF ptHead;
+  CFX_PointF ptFoot;
+  if (bVisible)
+    GetCaretInfo(&ptHead, &ptFoot);
+
+  SetCaret(bVisible, ptHead, ptFoot);
+  // Note, |this| may no longer be viable at this point. If more work needs to
+  // be done, check the return value of SetCaret().
+}
+
+void CPWL_Edit::GetCaretInfo(CFX_PointF* ptHead, CFX_PointF* ptFoot) const {
+  CPWL_EditImpl::Iterator* pIterator = m_pEditImpl->GetIterator();
+  pIterator->SetAt(m_pEditImpl->GetCaret());
+  CPVT_Word word;
+  CPVT_Line line;
+  if (pIterator->GetWord(word)) {
+    ptHead->x = word.ptWord.x + word.fWidth;
+    ptHead->y = word.ptWord.y + word.fAscent;
+    ptFoot->x = word.ptWord.x + word.fWidth;
+    ptFoot->y = word.ptWord.y + word.fDescent;
+  } else if (pIterator->GetLine(line)) {
+    ptHead->x = line.ptLine.x;
+    ptHead->y = line.ptLine.y + line.fLineAscent;
+    ptFoot->x = line.ptLine.x;
+    ptFoot->y = line.ptLine.y + line.fLineDescent;
+  }
+}
+
+bool CPWL_Edit::SetCaret(bool bVisible,
+                         const CFX_PointF& ptHead,
+                         const CFX_PointF& ptFoot) {
+  if (!m_pCaret)
+    return true;
+
+  if (!IsFocused() || m_pEditImpl->IsSelected())
+    bVisible = false;
+
+  ObservedPtr<CPWL_Edit> this_observed(this);
+  m_pCaret->SetCaret(bVisible, ptHead, ptFoot);
+  if (!this_observed) {
+    return false;
+  }
+
+  return true;
+}
+
+WideString CPWL_Edit::GetText() {
+  return m_pEditImpl->GetText();
+}
+
+void CPWL_Edit::SetSelection(int32_t nStartChar, int32_t nEndChar) {
+  m_pEditImpl->SetSelection(nStartChar, nEndChar);
+}
+
+std::pair<int32_t, int32_t> CPWL_Edit::GetSelection() const {
+  return m_pEditImpl->GetSelection();
+}
+
+void CPWL_Edit::ClearSelection() {
+  if (!IsReadOnly())
+    m_pEditImpl->ClearSelection();
+}
+
+void CPWL_Edit::SetScrollPos(const CFX_PointF& point) {
+  m_pEditImpl->SetScrollPos(point);
+}
+
+CFX_PointF CPWL_Edit::GetScrollPos() const {
+  return m_pEditImpl->GetScrollPos();
+}
+
+void CPWL_Edit::CopyText() {}
+
+void CPWL_Edit::PasteText() {}
+
+void CPWL_Edit::InsertWord(uint16_t word, FX_Charset nCharset) {
+  if (!IsReadOnly())
+    m_pEditImpl->InsertWord(word, nCharset);
+}
+
+void CPWL_Edit::InsertReturn() {
+  if (!IsReadOnly())
+    m_pEditImpl->InsertReturn();
+}
+
+void CPWL_Edit::Delete() {
+  if (!IsReadOnly())
+    m_pEditImpl->Delete();
+}
+
+void CPWL_Edit::Backspace() {
+  if (!IsReadOnly())
+    m_pEditImpl->Backspace();
+}
+
+bool CPWL_Edit::CanUndo() {
+  return !IsReadOnly() && m_pEditImpl->CanUndo();
+}
+
+bool CPWL_Edit::CanRedo() {
+  return !IsReadOnly() && m_pEditImpl->CanRedo();
+}
+
+bool CPWL_Edit::Undo() {
+  return CanUndo() && m_pEditImpl->Undo();
+}
+
+bool CPWL_Edit::Redo() {
+  return CanRedo() && m_pEditImpl->Redo();
+}
+
+void CPWL_Edit::SetReadyToInput() {
+  if (m_bMouseDown) {
+    ReleaseCapture();
+    m_bMouseDown = false;
+  }
 }
diff --git a/fpdfsdk/pwl/cpwl_edit.h b/fpdfsdk/pwl/cpwl_edit.h
index 1de382e..5e2e5fc 100644
--- a/fpdfsdk/pwl/cpwl_edit.h
+++ b/fpdfsdk/pwl/cpwl_edit.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,125 +11,127 @@
 #include <utility>
 
 #include "core/fpdfdoc/cpvt_wordrange.h"
+#include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "fpdfsdk/pwl/cpwl_edit_ctrl.h"
-#include "fpdfsdk/pwl/ipwl_systemhandler.h"
+#include "core/fxcrt/widestring.h"
+#include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "fpdfsdk/pwl/ipwl_fillernotify.h"
 
 class CPDF_Font;
+class CPWL_Caret;
+class CPWL_EditImpl;
+class IPWL_FillerNotify;
 
-class IPWL_Filler_Notify {
- public:
-  virtual ~IPWL_Filler_Notify() = default;
+enum PWL_EDIT_ALIGNFORMAT_H { PEAH_LEFT = 0, PEAH_MIDDLE, PEAH_RIGHT };
 
-  // Must write to |bBottom| and |fPopupRet|.
-  virtual void QueryWherePopup(
-      const IPWL_SystemHandler::PerWindowData* pAttached,
-      float fPopupMin,
-      float fPopupMax,
-      bool* bBottom,
-      float* fPopupRet) = 0;
+enum PWL_EDIT_ALIGNFORMAT_V { PEAV_TOP = 0, PEAV_CENTER, PEAV_BOTTOM };
 
-  virtual std::pair<bool, bool> OnBeforeKeyStroke(
-      const IPWL_SystemHandler::PerWindowData* pAttached,
-      WideString& strChange,
-      const WideString& strChangeEx,
-      int nSelStart,
-      int nSelEnd,
-      bool bKeyDown,
-      uint32_t nFlag) = 0;
-
-  virtual bool OnPopupPreOpen(
-      const IPWL_SystemHandler::PerWindowData* pAttached,
-      uint32_t nFlag) = 0;
-
-  virtual bool OnPopupPostOpen(
-      const IPWL_SystemHandler::PerWindowData* pAttached,
-      uint32_t nFlag) = 0;
-};
-
-class CPWL_Edit final : public CPWL_EditCtrl {
+class CPWL_Edit final : public CPWL_Wnd {
  public:
   CPWL_Edit(const CreateParams& cp,
-            std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
+            std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData);
   ~CPWL_Edit() override;
 
-  // CPWL_EditCtrl
-  void OnCreated() override;
-  bool RePosChildWnd() override;
+  // CPWL_Wnd:
+  bool RepositionChildWnd() override;
   CFX_FloatRect GetClientRect() const override;
   void DrawThisAppearance(CFX_RenderDevice* pDevice,
                           const CFX_Matrix& mtUser2Device) override;
-  bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override;
-  bool OnLButtonDblClk(const CFX_PointF& point, uint32_t nFlag) override;
-  bool OnRButtonUp(const CFX_PointF& point, uint32_t nFlag) override;
-  bool OnMouseWheel(short zDelta,
+  bool OnMouseWheel(Mask<FWL_EVENTFLAG> nFlag,
                     const CFX_PointF& point,
-                    uint32_t nFlag) override;
-  bool OnKeyDown(uint16_t nChar, uint32_t nFlag) override;
-  bool OnChar(uint16_t nChar, uint32_t nFlag) override;
+                    const CFX_Vector& delta) override;
+  bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlag) override;
+  bool OnChar(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) override;
   CFX_FloatRect GetFocusRect() const override;
   void OnSetFocus() override;
   void OnKillFocus() override;
+  void OnCreated() override;
+  void OnDestroy() override;
+  bool OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,
+                     const CFX_PointF& point) override;
+  bool OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag, const CFX_PointF& point) override;
+  bool OnLButtonDblClk(Mask<FWL_EVENTFLAG> nFlag,
+                       const CFX_PointF& point) override;
+  bool OnRButtonUp(Mask<FWL_EVENTFLAG> nFlag, const CFX_PointF& point) override;
+  bool OnMouseMove(Mask<FWL_EVENTFLAG> nFlag, const CFX_PointF& point) override;
+  void SetScrollInfo(const PWL_SCROLL_INFO& info) override;
+  void SetScrollPosition(float pos) override;
+  void ScrollWindowVertically(float pos) override;
+  void CreateChildWnd(const CreateParams& cp) override;
+  void SetFontSize(float fFontSize) override;
+  float GetFontSize() const override;
+  void SetCursor() override;
+  WideString GetText() override;
+  WideString GetSelectedText() override;
+  void ReplaceAndKeepSelection(const WideString& text) override;
+  void ReplaceSelection(const WideString& text) override;
+  bool SelectAllText() override;
+  bool CanUndo() override;
+  bool CanRedo() override;
+  bool Undo() override;
+  bool Redo() override;
 
+  void SetSelection(int32_t nStartChar, int32_t nEndChar);
+  std::pair<int32_t, int32_t> GetSelection() const;
+  void ClearSelection();
+
+  CFX_PointF GetScrollPos() const;
+  void SetScrollPos(const CFX_PointF& point);
+
+  void SetCharSet(FX_Charset nCharSet) { m_nCharSet = nCharSet; }
+  FX_Charset GetCharSet() const { return m_nCharSet; }
+
+  void SetReadyToInput();
   void SetAlignFormatVerticalCenter();
   void SetCharArray(int32_t nCharArray);
   void SetLimitChar(int32_t nLimitChar);
-  void SetCharSpace(float fCharSpace);
-
   bool CanSelectAll() const;
   bool CanCopy() const;
   bool CanCut() const;
-
   void CutText();
-
   void SetText(const WideString& csText);
-
   bool IsTextFull() const;
 
   static float GetCharArrayAutoFontSize(const CPDF_Font* pFont,
                                         const CFX_FloatRect& rcPlate,
                                         int32_t nCharArray);
 
-  void SetFillerNotify(IPWL_Filler_Notify* pNotify) {
-    m_pFillerNotify = pNotify;
-  }
-
-  void AttachFFLData(CFFL_FormFiller* pData) { m_pFormFiller = pData; }
-
-  void OnInsertWord(const CPVT_WordPlace& place,
-                    const CPVT_WordPlace& oldplace);
-  void OnInsertReturn(const CPVT_WordPlace& place,
-                      const CPVT_WordPlace& oldplace);
-  void OnBackSpace(const CPVT_WordPlace& place, const CPVT_WordPlace& oldplace);
-  void OnDelete(const CPVT_WordPlace& place, const CPVT_WordPlace& oldplace);
-  void OnClear(const CPVT_WordPlace& place, const CPVT_WordPlace& oldplace);
-  void OnInsertText(const CPVT_WordPlace& place,
-                    const CPVT_WordPlace& oldplace);
+  bool SetCaret(bool bVisible,
+                const CFX_PointF& ptHead,
+                const CFX_PointF& ptFoot);
 
  private:
   // In case of implementation swallow the OnKeyDown event. If the event is
   // swallowed, implementation may do other unexpected things, which is not the
   // control means to do.
-  static bool IsProceedtoOnChar(uint16_t nKeyCode, uint32_t nFlag);
+  static bool IsProceedtoOnChar(FWL_VKEYCODE nKeyCode,
+                                Mask<FWL_EVENTFLAG> nFlag);
+
+  bool OnKeyDownInternal(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlag);
+  bool OnCharInternal(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag);
+
+  void CopyText();
+  void PasteText();
+  void InsertWord(uint16_t word, FX_Charset nCharset);
+  void InsertReturn();
+  bool IsWndHorV() const;
+  void Delete();
+  void Backspace();
+  void GetCaretInfo(CFX_PointF* ptHead, CFX_PointF* ptFoot) const;
+  void SetEditCaret(bool bVisible);
+
+  void CreateEditCaret(const CreateParams& cp);
 
   CPVT_WordRange GetSelectWordRange() const;
   bool IsVScrollBarVisible() const;
   void SetParamByFlag();
 
-  CFX_PointF GetWordRightBottomPoint(const CPVT_WordPlace& wpWord);
-
-  CPVT_WordRange CombineWordRange(const CPVT_WordRange& wr1,
-                                  const CPVT_WordRange& wr2);
-  CPVT_WordRange GetLatinWordsRange(const CFX_PointF& point) const;
-  CPVT_WordRange GetLatinWordsRange(const CPVT_WordPlace& place) const;
-  CPVT_WordRange GetSameWordsRange(const CPVT_WordPlace& place,
-                                   bool bLatin,
-                                   bool bArabic) const;
-
+  bool m_bMouseDown = false;
   bool m_bFocus = false;
+  FX_Charset m_nCharSet = FX_Charset::kDefault;
   CFX_FloatRect m_rcOldWindow;
-  UnownedPtr<IPWL_Filler_Notify> m_pFillerNotify;
-  UnownedPtr<CFFL_FormFiller> m_pFormFiller;
+  std::unique_ptr<CPWL_EditImpl> const m_pEditImpl;
+  UnownedPtr<CPWL_Caret> m_pCaret;
 };
 
 #endif  // FPDFSDK_PWL_CPWL_EDIT_H_
diff --git a/fpdfsdk/pwl/cpwl_edit_ctrl.cpp b/fpdfsdk/pwl/cpwl_edit_ctrl.cpp
deleted file mode 100644
index 54fe91a..0000000
--- a/fpdfsdk/pwl/cpwl_edit_ctrl.cpp
+++ /dev/null
@@ -1,415 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/pwl/cpwl_edit_ctrl.h"
-
-#include <utility>
-
-#include "core/fpdfdoc/cpvt_word.h"
-#include "core/fxge/fx_font.h"
-#include "fpdfsdk/pwl/cpwl_caret.h"
-#include "fpdfsdk/pwl/cpwl_edit_impl.h"
-#include "fpdfsdk/pwl/cpwl_scroll_bar.h"
-#include "fpdfsdk/pwl/cpwl_wnd.h"
-#include "public/fpdf_fwlevent.h"
-#include "third_party/base/ptr_util.h"
-
-CPWL_EditCtrl::CPWL_EditCtrl(
-    const CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
-    : CPWL_Wnd(cp, std::move(pAttachedData)),
-      m_pEdit(pdfium::MakeUnique<CPWL_EditImpl>()) {
-  GetCreationParams()->eCursorType = FXCT_VBEAM;
-}
-
-CPWL_EditCtrl::~CPWL_EditCtrl() = default;
-
-void CPWL_EditCtrl::OnCreated() {
-  SetFontSize(GetCreationParams()->fFontSize);
-  m_pEdit->SetFontMap(GetFontMap());
-  m_pEdit->SetNotify(this);
-  m_pEdit->Initialize();
-}
-
-bool CPWL_EditCtrl::IsWndHorV() const {
-  CFX_Matrix mt = GetWindowMatrix();
-  return mt.Transform(CFX_PointF(1, 1)).y == mt.Transform(CFX_PointF(0, 1)).y;
-}
-
-void CPWL_EditCtrl::SetCursor() {
-  if (IsValid())
-    GetSystemHandler()->SetCursor(IsWndHorV() ? FXCT_VBEAM : FXCT_HBEAM);
-}
-
-WideString CPWL_EditCtrl::GetSelectedText() {
-  return m_pEdit->GetSelectedText();
-}
-
-void CPWL_EditCtrl::ReplaceSelection(const WideString& text) {
-  m_pEdit->ReplaceSelection(text);
-}
-
-bool CPWL_EditCtrl::RePosChildWnd() {
-  m_pEdit->SetPlateRect(GetClientRect());
-  return true;
-}
-
-void CPWL_EditCtrl::SetScrollInfo(const PWL_SCROLL_INFO& info) {
-  if (CPWL_Wnd* pChild = GetVScrollBar())
-    pChild->SetScrollInfo(info);
-}
-
-void CPWL_EditCtrl::SetScrollPosition(float pos) {
-  if (CPWL_Wnd* pChild = GetVScrollBar())
-    pChild->SetScrollPosition(pos);
-}
-
-void CPWL_EditCtrl::ScrollWindowVertically(float pos) {
-  m_pEdit->SetScrollPos(CFX_PointF(m_pEdit->GetScrollPos().x, pos));
-}
-
-void CPWL_EditCtrl::CreateChildWnd(const CreateParams& cp) {
-  if (!IsReadOnly())
-    CreateEditCaret(cp);
-}
-
-void CPWL_EditCtrl::CreateEditCaret(const CreateParams& cp) {
-  if (m_pEditCaret)
-    return;
-
-  CreateParams ecp = cp;
-  ecp.dwFlags = PWS_CHILD | PWS_NOREFRESHCLIP;
-  ecp.dwBorderWidth = 0;
-  ecp.nBorderStyle = BorderStyle::SOLID;
-  ecp.rcRectWnd = CFX_FloatRect();
-
-  auto pCaret = pdfium::MakeUnique<CPWL_Caret>(ecp, CloneAttachedData());
-  m_pEditCaret = pCaret.get();
-  m_pEditCaret->SetInvalidRect(GetClientRect());
-  AddChild(std::move(pCaret));
-  m_pEditCaret->Realize();
-}
-
-void CPWL_EditCtrl::SetFontSize(float fFontSize) {
-  m_pEdit->SetFontSize(fFontSize);
-}
-
-float CPWL_EditCtrl::GetFontSize() const {
-  return m_pEdit->GetFontSize();
-}
-
-bool CPWL_EditCtrl::OnKeyDown(uint16_t nChar, uint32_t nFlag) {
-  if (m_bMouseDown)
-    return true;
-
-  bool bRet = CPWL_Wnd::OnKeyDown(nChar, nFlag);
-
-  // FILTER
-  switch (nChar) {
-    default:
-      return false;
-    case FWL_VKEY_Delete:
-    case FWL_VKEY_Up:
-    case FWL_VKEY_Down:
-    case FWL_VKEY_Left:
-    case FWL_VKEY_Right:
-    case FWL_VKEY_Home:
-    case FWL_VKEY_End:
-    case FWL_VKEY_Insert:
-    case 'C':
-    case 'V':
-    case 'X':
-    case 'A':
-    case 'Z':
-    case 'c':
-    case 'v':
-    case 'x':
-    case 'a':
-    case 'z':
-      break;
-  }
-
-  if (nChar == FWL_VKEY_Delete && m_pEdit->IsSelected())
-    nChar = FWL_VKEY_Unknown;
-
-  switch (nChar) {
-    case FWL_VKEY_Delete:
-      Delete();
-      return true;
-    case FWL_VKEY_Insert:
-      if (IsSHIFTpressed(nFlag))
-        PasteText();
-      return true;
-    case FWL_VKEY_Up:
-      m_pEdit->OnVK_UP(IsSHIFTpressed(nFlag), false);
-      return true;
-    case FWL_VKEY_Down:
-      m_pEdit->OnVK_DOWN(IsSHIFTpressed(nFlag), false);
-      return true;
-    case FWL_VKEY_Left:
-      m_pEdit->OnVK_LEFT(IsSHIFTpressed(nFlag), false);
-      return true;
-    case FWL_VKEY_Right:
-      m_pEdit->OnVK_RIGHT(IsSHIFTpressed(nFlag), false);
-      return true;
-    case FWL_VKEY_Home:
-      m_pEdit->OnVK_HOME(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
-      return true;
-    case FWL_VKEY_End:
-      m_pEdit->OnVK_END(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
-      return true;
-    case FWL_VKEY_Unknown:
-      if (!IsSHIFTpressed(nFlag))
-        ClearSelection();
-      else
-        CutText();
-      return true;
-    default:
-      break;
-  }
-
-  return bRet;
-}
-
-bool CPWL_EditCtrl::OnChar(uint16_t nChar, uint32_t nFlag) {
-  if (m_bMouseDown)
-    return true;
-
-  CPWL_Wnd::OnChar(nChar, nFlag);
-
-  // FILTER
-  switch (nChar) {
-    case 0x0A:
-    case 0x1B:
-      return false;
-    default:
-      break;
-  }
-
-  bool bCtrl = IsCTRLpressed(nFlag);
-  bool bAlt = IsALTpressed(nFlag);
-  bool bShift = IsSHIFTpressed(nFlag);
-
-  uint16_t word = nChar;
-
-  if (bCtrl && !bAlt) {
-    switch (nChar) {
-      case 'C' - 'A' + 1:
-        CopyText();
-        return true;
-      case 'V' - 'A' + 1:
-        PasteText();
-        return true;
-      case 'X' - 'A' + 1:
-        CutText();
-        return true;
-      case 'A' - 'A' + 1:
-        SelectAll();
-        return true;
-      case 'Z' - 'A' + 1:
-        if (bShift)
-          Redo();
-        else
-          Undo();
-        return true;
-      default:
-        if (nChar < 32)
-          return false;
-    }
-  }
-
-  if (IsReadOnly())
-    return true;
-
-  if (m_pEdit->IsSelected() && word == FWL_VKEY_Back)
-    word = FWL_VKEY_Unknown;
-
-  ClearSelection();
-
-  switch (word) {
-    case FWL_VKEY_Back:
-      Backspace();
-      break;
-    case FWL_VKEY_Return:
-      InsertReturn();
-      break;
-    case FWL_VKEY_Unknown:
-      break;
-    default:
-      InsertWord(word, GetCharSet());
-      break;
-  }
-
-  return true;
-}
-
-bool CPWL_EditCtrl::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnLButtonDown(point, nFlag);
-
-  if (ClientHitTest(point)) {
-    if (m_bMouseDown && !InvalidateRect(nullptr))
-      return true;
-
-    m_bMouseDown = true;
-    SetCapture();
-
-    m_pEdit->OnMouseDown(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
-  }
-
-  return true;
-}
-
-bool CPWL_EditCtrl::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnLButtonUp(point, nFlag);
-
-  if (m_bMouseDown) {
-    // can receive keybord message
-    if (ClientHitTest(point) && !IsFocused())
-      SetFocus();
-
-    ReleaseCapture();
-    m_bMouseDown = false;
-  }
-
-  return true;
-}
-
-bool CPWL_EditCtrl::OnMouseMove(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnMouseMove(point, nFlag);
-
-  if (m_bMouseDown)
-    m_pEdit->OnMouseMove(point, false, false);
-
-  return true;
-}
-
-void CPWL_EditCtrl::SetEditCaret(bool bVisible) {
-  CFX_PointF ptHead;
-  CFX_PointF ptFoot;
-  if (bVisible)
-    GetCaretInfo(&ptHead, &ptFoot);
-
-  SetCaret(bVisible, ptHead, ptFoot);
-  // Note, |this| may no longer be viable at this point. If more work needs to
-  // be done, check the return value of SetCaret().
-}
-
-void CPWL_EditCtrl::GetCaretInfo(CFX_PointF* ptHead, CFX_PointF* ptFoot) const {
-  CPWL_EditImpl_Iterator* pIterator = m_pEdit->GetIterator();
-  pIterator->SetAt(m_pEdit->GetCaret());
-  CPVT_Word word;
-  CPVT_Line line;
-  if (pIterator->GetWord(word)) {
-    ptHead->x = word.ptWord.x + word.fWidth;
-    ptHead->y = word.ptWord.y + word.fAscent;
-    ptFoot->x = word.ptWord.x + word.fWidth;
-    ptFoot->y = word.ptWord.y + word.fDescent;
-  } else if (pIterator->GetLine(line)) {
-    ptHead->x = line.ptLine.x;
-    ptHead->y = line.ptLine.y + line.fLineAscent;
-    ptFoot->x = line.ptLine.x;
-    ptFoot->y = line.ptLine.y + line.fLineDescent;
-  }
-}
-
-bool CPWL_EditCtrl::SetCaret(bool bVisible,
-                             const CFX_PointF& ptHead,
-                             const CFX_PointF& ptFoot) {
-  if (!m_pEditCaret)
-    return true;
-
-  if (!IsFocused() || m_pEdit->IsSelected())
-    bVisible = false;
-
-  ObservedPtr<CPWL_EditCtrl> thisObserved(this);
-  m_pEditCaret->SetCaret(bVisible, ptHead, ptFoot);
-  if (!thisObserved)
-    return false;
-
-  return true;
-}
-
-WideString CPWL_EditCtrl::GetText() {
-  return m_pEdit->GetText();
-}
-
-void CPWL_EditCtrl::SetSelection(int32_t nStartChar, int32_t nEndChar) {
-  m_pEdit->SetSelection(nStartChar, nEndChar);
-}
-
-void CPWL_EditCtrl::GetSelection(int32_t& nStartChar, int32_t& nEndChar) const {
-  m_pEdit->GetSelection(nStartChar, nEndChar);
-}
-
-void CPWL_EditCtrl::ClearSelection() {
-  if (!IsReadOnly())
-    m_pEdit->ClearSelection();
-}
-
-void CPWL_EditCtrl::SelectAll() {
-  m_pEdit->SelectAll();
-}
-
-void CPWL_EditCtrl::SetScrollPos(const CFX_PointF& point) {
-  m_pEdit->SetScrollPos(point);
-}
-
-CFX_PointF CPWL_EditCtrl::GetScrollPos() const {
-  return m_pEdit->GetScrollPos();
-}
-
-void CPWL_EditCtrl::CopyText() {}
-
-void CPWL_EditCtrl::PasteText() {}
-
-void CPWL_EditCtrl::CutText() {}
-
-void CPWL_EditCtrl::InsertWord(uint16_t word, int32_t nCharset) {
-  if (!IsReadOnly())
-    m_pEdit->InsertWord(word, nCharset);
-}
-
-void CPWL_EditCtrl::InsertReturn() {
-  if (!IsReadOnly())
-    m_pEdit->InsertReturn();
-}
-
-void CPWL_EditCtrl::Delete() {
-  if (!IsReadOnly())
-    m_pEdit->Delete();
-}
-
-void CPWL_EditCtrl::Backspace() {
-  if (!IsReadOnly())
-    m_pEdit->Backspace();
-}
-
-bool CPWL_EditCtrl::CanUndo() {
-  return !IsReadOnly() && m_pEdit->CanUndo();
-}
-
-bool CPWL_EditCtrl::CanRedo() {
-  return !IsReadOnly() && m_pEdit->CanRedo();
-}
-
-bool CPWL_EditCtrl::Undo() {
-  return CanUndo() && m_pEdit->Undo();
-}
-
-bool CPWL_EditCtrl::Redo() {
-  return CanRedo() && m_pEdit->Redo();
-}
-
-int32_t CPWL_EditCtrl::GetCharSet() const {
-  return m_nCharSet < 0 ? FX_CHARSET_Default : m_nCharSet;
-}
-
-void CPWL_EditCtrl::SetReadyToInput() {
-  if (m_bMouseDown) {
-    ReleaseCapture();
-    m_bMouseDown = false;
-  }
-}
diff --git a/fpdfsdk/pwl/cpwl_edit_ctrl.h b/fpdfsdk/pwl/cpwl_edit_ctrl.h
deleted file mode 100644
index 9cd92bf..0000000
--- a/fpdfsdk/pwl/cpwl_edit_ctrl.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_PWL_CPWL_EDIT_CTRL_H_
-#define FPDFSDK_PWL_CPWL_EDIT_CTRL_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_codepage.h"
-#include "core/fxcrt/fx_string.h"
-#include "fpdfsdk/pwl/cpwl_wnd.h"
-
-class CPWL_EditImpl;
-class CPWL_Caret;
-struct CPVT_WordPlace;
-
-enum PWL_EDIT_ALIGNFORMAT_H { PEAH_LEFT = 0, PEAH_MIDDLE, PEAH_RIGHT };
-
-enum PWL_EDIT_ALIGNFORMAT_V { PEAV_TOP = 0, PEAV_CENTER, PEAV_BOTTOM };
-
-class CPWL_EditCtrl : public CPWL_Wnd {
- public:
-  CPWL_EditCtrl(
-      const CreateParams& cp,
-      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
-  ~CPWL_EditCtrl() override;
-
-  void SetSelection(int32_t nStartChar, int32_t nEndChar);
-  void GetSelection(int32_t& nStartChar, int32_t& nEndChar) const;
-  void ClearSelection();
-  void SelectAll();
-
-  CFX_PointF GetScrollPos() const;
-  void SetScrollPos(const CFX_PointF& point);
-
-  void SetCharSet(uint8_t nCharSet) { m_nCharSet = nCharSet; }
-  int32_t GetCharSet() const;
-
-  bool CanUndo() override;
-  bool CanRedo() override;
-  bool Undo() override;
-  bool Redo() override;
-
-  void SetReadyToInput();
-
-  // CPWL_Wnd:
-  void OnCreated() override;
-  bool OnKeyDown(uint16_t nChar, uint32_t nFlag) override;
-  bool OnChar(uint16_t nChar, uint32_t nFlag) override;
-  bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override;
-  bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override;
-  bool OnMouseMove(const CFX_PointF& point, uint32_t nFlag) override;
-  void SetScrollInfo(const PWL_SCROLL_INFO& info) override;
-  void SetScrollPosition(float pos) override;
-  void ScrollWindowVertically(float pos) override;
-  void CreateChildWnd(const CreateParams& cp) override;
-  bool RePosChildWnd() override;
-  void SetFontSize(float fFontSize) override;
-  float GetFontSize() const override;
-  void SetCursor() override;
-  WideString GetText() override;
-  WideString GetSelectedText() override;
-  void ReplaceSelection(const WideString& text) override;
-
-  bool SetCaret(bool bVisible,
-                const CFX_PointF& ptHead,
-                const CFX_PointF& ptFoot);
-
- protected:
-  void CopyText();
-  void PasteText();
-  void CutText();
-  void InsertWord(uint16_t word, int32_t nCharset);
-  void InsertReturn();
-  bool IsWndHorV() const;
-  void Delete();
-  void Backspace();
-  void GetCaretInfo(CFX_PointF* ptHead, CFX_PointF* ptFoot) const;
-  void SetEditCaret(bool bVisible);
-
-  std::unique_ptr<CPWL_EditImpl> const m_pEdit;
-  CPWL_Caret* m_pEditCaret = nullptr;
-  bool m_bMouseDown = false;
-
- private:
-  void CreateEditCaret(const CreateParams& cp);
-
-  int32_t m_nCharSet = FX_CHARSET_Default;
-};
-
-#endif  // FPDFSDK_PWL_CPWL_EDIT_CTRL_H_
diff --git a/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp b/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp
index 8b68afa..f0e13d2 100644
--- a/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp
+++ b/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp
@@ -1,14 +1,16 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fpdfsdk/pwl/cpwl_edit.h"
 
-#include "fpdfsdk/cpdfsdk_annot.h"
+#include <utility>
+
 #include "fpdfsdk/cpdfsdk_annotiterator.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
-#include "fpdfsdk/formfiller/cffl_formfiller.h"
+#include "fpdfsdk/cpdfsdk_widget.h"
+#include "fpdfsdk/formfiller/cffl_formfield.h"
 #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
 #include "public/fpdf_fwlevent.h"
 #include "testing/embedder_test.h"
@@ -27,27 +29,24 @@
   }
 
   void CreateAndInitializeFormPDF() {
-    EXPECT_TRUE(OpenDocument("text_form_multiple.pdf"));
+    ASSERT_TRUE(OpenDocument("text_form_multiple.pdf"));
     m_page = LoadPage(0);
     ASSERT_TRUE(m_page);
 
     m_pFormFillEnv =
         CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle());
-    CPDFSDK_AnnotIterator iter(m_pFormFillEnv->GetPageView(0),
-                               CPDF_Annot::Subtype::WIDGET);
+    CPDFSDK_AnnotIterator iter(m_pFormFillEnv->GetPageViewAtIndex(0),
+                               {CPDF_Annot::Subtype::WIDGET});
     // Normal text field.
-    m_pAnnot = iter.GetFirstAnnot();
+    m_pAnnot = ToCPDFSDKWidget(iter.GetFirstAnnot());
     ASSERT_TRUE(m_pAnnot);
-    ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, m_pAnnot->GetAnnotSubtype());
 
     // Read-only text field.
     CPDFSDK_Annot* pAnnotReadOnly = iter.GetNextAnnot(m_pAnnot);
 
     // Pre-filled text field with char limit of 10.
-    m_pAnnotCharLimit = iter.GetNextAnnot(pAnnotReadOnly);
+    m_pAnnotCharLimit = ToCPDFSDKWidget(iter.GetNextAnnot(pAnnotReadOnly));
     ASSERT_TRUE(m_pAnnotCharLimit);
-    ASSERT_EQ(CPDF_Annot::Subtype::WIDGET,
-              m_pAnnotCharLimit->GetAnnotSubtype());
 
     // Password text field.
     CPDFSDK_Annot* password_annot = iter.GetNextAnnot(m_pAnnotCharLimit);
@@ -58,20 +57,20 @@
     ASSERT_EQ(password_annot, pLastAnnot);
   }
 
-  void FormFillerAndWindowSetup(CPDFSDK_Annot* pAnnotTextField) {
+  void FormFillerAndWindowSetup(CPDFSDK_Widget* pAnnotTextField) {
     CFFL_InteractiveFormFiller* pInteractiveFormFiller =
         m_pFormFillEnv->GetInteractiveFormFiller();
     {
-      ObservedPtr<CPDFSDK_Annot> pObserved(pAnnotTextField);
-      EXPECT_TRUE(pInteractiveFormFiller->OnSetFocus(&pObserved, 0));
+      ObservedPtr<CPDFSDK_Widget> pObserved(pAnnotTextField);
+      EXPECT_TRUE(pInteractiveFormFiller->OnSetFocus(pObserved, {}));
     }
 
     m_pFormFiller =
-        pInteractiveFormFiller->GetFormFillerForTesting(pAnnotTextField);
+        pInteractiveFormFiller->GetFormFieldForTesting(pAnnotTextField);
     ASSERT_TRUE(m_pFormFiller);
 
     CPWL_Wnd* pWindow =
-        m_pFormFiller->GetPWLWindow(m_pFormFillEnv->GetPageView(0), false);
+        m_pFormFiller->GetPWLWindow(m_pFormFillEnv->GetPageViewAtIndex(0));
     ASSERT_TRUE(pWindow);
     m_pEdit = static_cast<CPWL_Edit*>(pWindow);
   }
@@ -79,31 +78,31 @@
   void TypeTextIntoTextField(int num_chars) {
     // Type text starting with 'A' to as many chars as specified by |num_chars|.
     for (int i = 0; i < num_chars; ++i) {
-      EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), i + 'A', 0));
+      EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), i + 'A', {}));
     }
   }
 
   FPDF_PAGE GetPage() { return m_page; }
   CPWL_Edit* GetCPWLEdit() { return m_pEdit; }
-  CFFL_FormFiller* GetCFFLFormFiller() { return m_pFormFiller; }
-  CPDFSDK_Annot* GetCPDFSDKAnnot() { return m_pAnnot; }
-  CPDFSDK_Annot* GetCPDFSDKAnnotCharLimit() { return m_pAnnotCharLimit; }
+  CFFL_FormField* GetCFFLFormFiller() { return m_pFormFiller; }
+  CPDFSDK_Widget* GetCPDFSDKAnnot() { return m_pAnnot; }
+  CPDFSDK_Widget* GetCPDFSDKAnnotCharLimit() { return m_pAnnotCharLimit; }
 
  private:
   FPDF_PAGE m_page;
   CPWL_Edit* m_pEdit;
-  CFFL_FormFiller* m_pFormFiller;
-  CPDFSDK_Annot* m_pAnnot;
-  CPDFSDK_Annot* m_pAnnotCharLimit;
+  CFFL_FormField* m_pFormFiller;
+  CPDFSDK_Widget* m_pAnnot;
+  CPDFSDK_Widget* m_pAnnotCharLimit;
   CPDFSDK_FormFillEnvironment* m_pFormFillEnv;
 };
 
 TEST_F(CPWLEditEmbedderTest, TypeText) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   EXPECT_TRUE(GetCPWLEdit()->GetText().IsEmpty());
-  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'a', 0));
-  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'b', 0));
-  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'c', 0));
+  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'a', {}));
+  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'b', {}));
+  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'c', {}));
 
   EXPECT_STREQ(L"abc", GetCPWLEdit()->GetText().c_str());
 }
@@ -117,9 +116,9 @@
   GetCPWLEdit()->SetSelection(0, 3);
   EXPECT_TRUE(GetCPWLEdit()->GetSelectedText().IsEmpty());
 
-  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'a', 0));
-  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'b', 0));
-  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'c', 0));
+  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'a', {}));
+  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'b', {}));
+  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'c', {}));
   GetCPWLEdit()->SetSelection(0, 2);
 
   EXPECT_STREQ(L"ab", GetCPWLEdit()->GetSelectedText().c_str());
@@ -223,7 +222,7 @@
   TypeTextIntoTextField(10);
 
   // Move cursor to beginning of text field.
-  EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Home, 0));
+  EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Home, {}));
 
   GetCPWLEdit()->ReplaceSelection(L"Hello");
   EXPECT_STREQ(L"HelloABCDEFGHIJ", GetCPWLEdit()->GetText().c_str());
@@ -235,7 +234,7 @@
 
   // Move cursor to middle of text field.
   for (int i = 0; i < 5; ++i) {
-    EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Left, 0));
+    EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Left, {}));
   }
 
   GetCPWLEdit()->ReplaceSelection(L"Hello");
@@ -324,7 +323,7 @@
   FormFillerAndWindowSetup(GetCPDFSDKAnnotCharLimit());
   // Move cursor to middle of text field.
   for (int i = 0; i < 5; ++i) {
-    EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Right, 0));
+    EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Right, {}));
   }
 
   GetCPWLEdit()->ReplaceSelection(L"Hippopotamus");
@@ -334,7 +333,7 @@
 TEST_F(CPWLEditEmbedderTest, InsertTextInPopulatedCharLimitTextFieldRight) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotCharLimit());
   // Move cursor to end of text field.
-  EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_End, 0));
+  EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_End, {}));
 
   GetCPWLEdit()->ReplaceSelection(L"Hippopotamus");
   EXPECT_STREQ(L"ElephantHi", GetCPWLEdit()->GetText().c_str());
@@ -423,3 +422,21 @@
   GetCPWLEdit()->SetText(L"Foo\n\rBar");
   EXPECT_STREQ(L"FooBar", GetCPWLEdit()->GetText().c_str());
 }
+
+TEST_F(CPWLEditEmbedderTest, ReplaceAndKeepSelection) {
+  FormFillerAndWindowSetup(GetCPDFSDKAnnot());
+  TypeTextIntoTextField(10);
+
+  GetCPWLEdit()->SetSelection(1, 3);
+  EXPECT_STREQ(L"ABCDEFGHIJ", GetCPWLEdit()->GetText().c_str());
+  GetCPWLEdit()->ReplaceAndKeepSelection(L"xyz");
+  EXPECT_STREQ(L"AxyzDEFGHIJ", GetCPWLEdit()->GetText().c_str());
+  EXPECT_STREQ(L"xyz", GetCPWLEdit()->GetSelectedText().c_str());
+  EXPECT_EQ(GetCPWLEdit()->GetSelection(), std::make_pair(1, 4));
+
+  GetCPWLEdit()->SetSelection(4, 1);
+  GetCPWLEdit()->ReplaceAndKeepSelection(L"12");
+  EXPECT_STREQ(L"A12DEFGHIJ", GetCPWLEdit()->GetText().c_str());
+  EXPECT_STREQ(L"12", GetCPWLEdit()->GetSelectedText().c_str());
+  EXPECT_EQ(GetCPWLEdit()->GetSelection(), std::make_pair(1, 3));
+}
diff --git a/fpdfsdk/pwl/cpwl_edit_impl.cpp b/fpdfsdk/pwl/cpwl_edit_impl.cpp
index a9ee19e..37ec306 100644
--- a/fpdfsdk/pwl/cpwl_edit_impl.cpp
+++ b/fpdfsdk/pwl/cpwl_edit_impl.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,6 @@
 
 #include <algorithm>
 #include <memory>
-#include <sstream>
 #include <utility>
 
 #include "core/fpdfapi/font/cpdf_font.h"
@@ -18,15 +17,15 @@
 #include "core/fpdfdoc/ipvt_fontmap.h"
 #include "core/fxcrt/autorestorer.h"
 #include "core/fxcrt/fx_codepage.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "fpdfsdk/pwl/cpwl_edit.h"
-#include "fpdfsdk/pwl/cpwl_edit_ctrl.h"
 #include "fpdfsdk/pwl/cpwl_scroll_bar.h"
-#include "fpdfsdk/pwl/ipwl_systemhandler.h"
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/ptr_util.h"
+#include "fpdfsdk/pwl/ipwl_fillernotify.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 
 namespace {
 
@@ -44,7 +43,7 @@
 
   CFX_PointF pos = mtUser2Device.Transform(pt);
   CPDF_RenderOptions ro;
-  ASSERT(ro.GetOptions().bClearType);
+  DCHECK(ro.GetOptions().bClearType);
   ro.SetColorMode(CPDF_RenderOptions::kNormal);
   CPDF_TextRenderer::DrawTextString(pDevice, pos.x, pos.y, pFont, fFontSize,
                                     mtUser2Device, str, crTextFill, ro);
@@ -52,23 +51,18 @@
 
 }  // namespace
 
-CPWL_EditImpl_Iterator::CPWL_EditImpl_Iterator(
-    CPWL_EditImpl* pEdit,
-    CPDF_VariableText::Iterator* pVTIterator)
+CPWL_EditImpl::Iterator::Iterator(CPWL_EditImpl* pEdit,
+                                  CPVT_VariableText::Iterator* pVTIterator)
     : m_pEdit(pEdit), m_pVTIterator(pVTIterator) {}
 
-CPWL_EditImpl_Iterator::~CPWL_EditImpl_Iterator() {}
+CPWL_EditImpl::Iterator::~Iterator() = default;
 
-bool CPWL_EditImpl_Iterator::NextWord() {
+bool CPWL_EditImpl::Iterator::NextWord() {
   return m_pVTIterator->NextWord();
 }
 
-bool CPWL_EditImpl_Iterator::PrevWord() {
-  return m_pVTIterator->PrevWord();
-}
-
-bool CPWL_EditImpl_Iterator::GetWord(CPVT_Word& word) const {
-  ASSERT(m_pEdit);
+bool CPWL_EditImpl::Iterator::GetWord(CPVT_Word& word) const {
+  DCHECK(m_pEdit);
 
   if (m_pVTIterator->GetWord(word)) {
     word.ptWord = m_pEdit->VTToEdit(word.ptWord);
@@ -77,8 +71,8 @@
   return false;
 }
 
-bool CPWL_EditImpl_Iterator::GetLine(CPVT_Line& line) const {
-  ASSERT(m_pEdit);
+bool CPWL_EditImpl::Iterator::GetLine(CPVT_Line& line) const {
+  DCHECK(m_pEdit);
 
   if (m_pVTIterator->GetLine(line)) {
     line.ptLine = m_pEdit->VTToEdit(line.ptLine);
@@ -87,85 +81,71 @@
   return false;
 }
 
-void CPWL_EditImpl_Iterator::SetAt(int32_t nWordIndex) {
+void CPWL_EditImpl::Iterator::SetAt(int32_t nWordIndex) {
   m_pVTIterator->SetAt(nWordIndex);
 }
 
-void CPWL_EditImpl_Iterator::SetAt(const CPVT_WordPlace& place) {
+void CPWL_EditImpl::Iterator::SetAt(const CPVT_WordPlace& place) {
   m_pVTIterator->SetAt(place);
 }
 
-const CPVT_WordPlace& CPWL_EditImpl_Iterator::GetAt() const {
+const CPVT_WordPlace& CPWL_EditImpl::Iterator::GetAt() const {
   return m_pVTIterator->GetWordPlace();
 }
 
-CPWL_EditImpl_Provider::CPWL_EditImpl_Provider(IPVT_FontMap* pFontMap)
-    : CPDF_VariableText::Provider(pFontMap), m_pFontMap(pFontMap) {
-  ASSERT(m_pFontMap);
-}
+class CPWL_EditImpl::Provider final : public CPVT_VariableText::Provider {
+ public:
+  explicit Provider(IPVT_FontMap* pFontMap);
+  ~Provider() override;
 
-CPWL_EditImpl_Provider::~CPWL_EditImpl_Provider() {}
+  // CPVT_VariableText::Provider:
+  int GetCharWidth(int32_t nFontIndex, uint16_t word) override;
+  int32_t GetWordFontIndex(uint16_t word,
+                           FX_Charset charset,
+                           int32_t nFontIndex) override;
+};
 
-IPVT_FontMap* CPWL_EditImpl_Provider::GetFontMap() const {
-  return m_pFontMap;
-}
+CPWL_EditImpl::Provider::Provider(IPVT_FontMap* pFontMap)
+    : CPVT_VariableText::Provider(pFontMap) {}
 
-uint32_t CPWL_EditImpl_Provider::GetCharWidth(int32_t nFontIndex,
-                                              uint16_t word) {
-  RetainPtr<CPDF_Font> pPDFFont = m_pFontMap->GetPDFFont(nFontIndex);
+CPWL_EditImpl::Provider::~Provider() = default;
+
+int CPWL_EditImpl::Provider::GetCharWidth(int32_t nFontIndex, uint16_t word) {
+  RetainPtr<CPDF_Font> pPDFFont = GetFontMap()->GetPDFFont(nFontIndex);
   if (!pPDFFont)
     return 0;
 
   uint32_t charcode = pPDFFont->IsUnicodeCompatible()
                           ? pPDFFont->CharCodeFromUnicode(word)
-                          : m_pFontMap->CharCodeFromUnicode(nFontIndex, word);
-
+                          : GetFontMap()->CharCodeFromUnicode(nFontIndex, word);
   if (charcode == CPDF_Font::kInvalidCharCode)
     return 0;
 
   return pPDFFont->GetCharWidthF(charcode);
 }
 
-int32_t CPWL_EditImpl_Provider::GetTypeAscent(int32_t nFontIndex) {
-  RetainPtr<CPDF_Font> pPDFFont = m_pFontMap->GetPDFFont(nFontIndex);
-  return pPDFFont ? pPDFFont->GetTypeAscent() : 0;
+int32_t CPWL_EditImpl::Provider::GetWordFontIndex(uint16_t word,
+                                                  FX_Charset charset,
+                                                  int32_t nFontIndex) {
+  return GetFontMap()->GetWordFontIndex(word, charset, nFontIndex);
 }
 
-int32_t CPWL_EditImpl_Provider::GetTypeDescent(int32_t nFontIndex) {
-  RetainPtr<CPDF_Font> pPDFFont = m_pFontMap->GetPDFFont(nFontIndex);
-  return pPDFFont ? pPDFFont->GetTypeDescent() : 0;
-}
+CPWL_EditImpl::RefreshState::RefreshState() = default;
 
-int32_t CPWL_EditImpl_Provider::GetWordFontIndex(uint16_t word,
-                                                 int32_t charset,
-                                                 int32_t nFontIndex) {
-  return m_pFontMap->GetWordFontIndex(word, charset, nFontIndex);
-}
+CPWL_EditImpl::RefreshState::~RefreshState() = default;
 
-int32_t CPWL_EditImpl_Provider::GetDefaultFontIndex() {
-  return 0;
-}
-
-bool CPWL_EditImpl_Provider::IsLatinWord(uint16_t word) {
-  return FX_EDIT_ISLATINWORD(word);
-}
-
-CPWL_EditImpl_Refresh::CPWL_EditImpl_Refresh() {}
-
-CPWL_EditImpl_Refresh::~CPWL_EditImpl_Refresh() {}
-
-void CPWL_EditImpl_Refresh::BeginRefresh() {
+void CPWL_EditImpl::RefreshState::BeginRefresh() {
   m_OldLineRects = std::move(m_NewLineRects);
   m_NewLineRects.clear();
   m_RefreshRects.clear();
 }
 
-void CPWL_EditImpl_Refresh::Push(const CPVT_WordRange& linerange,
-                                 const CFX_FloatRect& rect) {
-  m_NewLineRects.emplace_back(CPWL_EditImpl_LineRect(linerange, rect));
+void CPWL_EditImpl::RefreshState::Push(const CPVT_WordRange& linerange,
+                                       const CFX_FloatRect& rect) {
+  m_NewLineRects.emplace_back(LineRect(linerange, rect));
 }
 
-void CPWL_EditImpl_Refresh::NoAnalyse() {
+void CPWL_EditImpl::RefreshState::NoAnalyse() {
   for (const auto& lineRect : m_OldLineRects)
     Add(lineRect.m_rcLine);
 
@@ -173,15 +153,15 @@
     Add(lineRect.m_rcLine);
 }
 
-std::vector<CFX_FloatRect>* CPWL_EditImpl_Refresh::GetRefreshRects() {
+std::vector<CFX_FloatRect>* CPWL_EditImpl::RefreshState::GetRefreshRects() {
   return &m_RefreshRects;
 }
 
-void CPWL_EditImpl_Refresh::EndRefresh() {
+void CPWL_EditImpl::RefreshState::EndRefresh() {
   m_RefreshRects.clear();
 }
 
-void CPWL_EditImpl_Refresh::Add(const CFX_FloatRect& new_rect) {
+void CPWL_EditImpl::RefreshState::Add(const CFX_FloatRect& new_rect) {
   // Check for overlapped area.
   for (const auto& rect : m_RefreshRects) {
     if (rect.Contains(new_rect))
@@ -190,17 +170,16 @@
   m_RefreshRects.emplace_back(CFX_FloatRect(new_rect));
 }
 
-CPWL_EditImpl_Undo::CPWL_EditImpl_Undo()
-    : m_nCurUndoPos(0), m_bWorking(false) {}
+CPWL_EditImpl::UndoStack::UndoStack() = default;
 
-CPWL_EditImpl_Undo::~CPWL_EditImpl_Undo() {}
+CPWL_EditImpl::UndoStack::~UndoStack() = default;
 
-bool CPWL_EditImpl_Undo::CanUndo() const {
+bool CPWL_EditImpl::UndoStack::CanUndo() const {
   return m_nCurUndoPos > 0;
 }
 
-void CPWL_EditImpl_Undo::Undo() {
-  ASSERT(!m_bWorking);
+void CPWL_EditImpl::UndoStack::Undo() {
+  DCHECK(!m_bWorking);
   m_bWorking = true;
   int nUndoRemain = 1;
   while (CanUndo() && nUndoRemain > 0) {
@@ -208,17 +187,17 @@
     m_nCurUndoPos--;
     nUndoRemain--;
   }
-  ASSERT(nUndoRemain == 0);
-  ASSERT(m_bWorking);
+  DCHECK_EQ(nUndoRemain, 0);
+  DCHECK(m_bWorking);
   m_bWorking = false;
 }
 
-bool CPWL_EditImpl_Undo::CanRedo() const {
+bool CPWL_EditImpl::UndoStack::CanRedo() const {
   return m_nCurUndoPos < m_UndoItemStack.size();
 }
 
-void CPWL_EditImpl_Undo::Redo() {
-  ASSERT(!m_bWorking);
+void CPWL_EditImpl::UndoStack::Redo() {
+  DCHECK(!m_bWorking);
   m_bWorking = true;
   int nRedoRemain = 1;
   while (CanRedo() && nRedoRemain > 0) {
@@ -226,14 +205,14 @@
     m_nCurUndoPos++;
     nRedoRemain--;
   }
-  ASSERT(nRedoRemain == 0);
-  ASSERT(m_bWorking);
+  DCHECK_EQ(nRedoRemain, 0);
+  DCHECK(m_bWorking);
   m_bWorking = false;
 }
 
-void CPWL_EditImpl_Undo::AddItem(std::unique_ptr<IFX_Edit_UndoItem> pItem) {
-  ASSERT(!m_bWorking);
-  ASSERT(pItem);
+void CPWL_EditImpl::UndoStack::AddItem(std::unique_ptr<UndoItemIface> pItem) {
+  DCHECK(!m_bWorking);
+  DCHECK(pItem);
   if (CanRedo())
     RemoveTails();
 
@@ -244,77 +223,137 @@
   m_nCurUndoPos = m_UndoItemStack.size();
 }
 
-void CPWL_EditImpl_Undo::RemoveHeads() {
-  ASSERT(m_UndoItemStack.size() > 1);
+void CPWL_EditImpl::UndoStack::RemoveHeads() {
+  DCHECK(m_UndoItemStack.size() > 1);
   m_UndoItemStack.pop_front();
 }
 
-void CPWL_EditImpl_Undo::RemoveTails() {
+void CPWL_EditImpl::UndoStack::RemoveTails() {
   while (CanRedo())
     m_UndoItemStack.pop_back();
 }
 
-CFXEU_InsertWord::CFXEU_InsertWord(CPWL_EditImpl* pEdit,
-                                   const CPVT_WordPlace& wpOldPlace,
-                                   const CPVT_WordPlace& wpNewPlace,
-                                   uint16_t word,
-                                   int32_t charset)
+class CPWL_EditImpl::UndoInsertWord final
+    : public CPWL_EditImpl::UndoItemIface {
+ public:
+  UndoInsertWord(CPWL_EditImpl* pEdit,
+                 const CPVT_WordPlace& wpOldPlace,
+                 const CPVT_WordPlace& wpNewPlace,
+                 uint16_t word,
+                 FX_Charset charset);
+  ~UndoInsertWord() override;
+
+  // UndoItemIface:
+  int Redo() override;
+  int Undo() override;
+
+ private:
+  UnownedPtr<CPWL_EditImpl> m_pEdit;
+
+  CPVT_WordPlace m_wpOld;
+  CPVT_WordPlace m_wpNew;
+  uint16_t m_Word;
+  FX_Charset m_nCharset;
+};
+
+CPWL_EditImpl::UndoInsertWord::UndoInsertWord(CPWL_EditImpl* pEdit,
+                                              const CPVT_WordPlace& wpOldPlace,
+                                              const CPVT_WordPlace& wpNewPlace,
+                                              uint16_t word,
+                                              FX_Charset charset)
     : m_pEdit(pEdit),
       m_wpOld(wpOldPlace),
       m_wpNew(wpNewPlace),
       m_Word(word),
       m_nCharset(charset) {
-  ASSERT(m_pEdit);
+  DCHECK(m_pEdit);
 }
 
-CFXEU_InsertWord::~CFXEU_InsertWord() {}
+CPWL_EditImpl::UndoInsertWord::~UndoInsertWord() = default;
 
-int CFXEU_InsertWord::Redo() {
+int CPWL_EditImpl::UndoInsertWord::Redo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpOld);
-  m_pEdit->InsertWord(m_Word, m_nCharset, false, true);
+  m_pEdit->InsertWord(m_Word, m_nCharset, false);
   return 0;
 }
 
-int CFXEU_InsertWord::Undo() {
+int CPWL_EditImpl::UndoInsertWord::Undo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpNew);
-  m_pEdit->Backspace(false, true);
+  m_pEdit->Backspace(false);
   return 0;
 }
 
-CFXEU_InsertReturn::CFXEU_InsertReturn(CPWL_EditImpl* pEdit,
-                                       const CPVT_WordPlace& wpOldPlace,
-                                       const CPVT_WordPlace& wpNewPlace)
+class CPWL_EditImpl::UndoInsertReturn final
+    : public CPWL_EditImpl::UndoItemIface {
+ public:
+  UndoInsertReturn(CPWL_EditImpl* pEdit,
+                   const CPVT_WordPlace& wpOldPlace,
+                   const CPVT_WordPlace& wpNewPlace);
+  ~UndoInsertReturn() override;
+
+  // UndoItemIface:
+  int Redo() override;
+  int Undo() override;
+
+ private:
+  UnownedPtr<CPWL_EditImpl> m_pEdit;
+
+  CPVT_WordPlace m_wpOld;
+  CPVT_WordPlace m_wpNew;
+};
+
+CPWL_EditImpl::UndoInsertReturn::UndoInsertReturn(
+    CPWL_EditImpl* pEdit,
+    const CPVT_WordPlace& wpOldPlace,
+    const CPVT_WordPlace& wpNewPlace)
     : m_pEdit(pEdit), m_wpOld(wpOldPlace), m_wpNew(wpNewPlace) {
-  ASSERT(m_pEdit);
+  DCHECK(m_pEdit);
 }
 
-CFXEU_InsertReturn::~CFXEU_InsertReturn() {}
+CPWL_EditImpl::UndoInsertReturn::~UndoInsertReturn() = default;
 
-int CFXEU_InsertReturn::Redo() {
+int CPWL_EditImpl::UndoInsertReturn::Redo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpOld);
-  m_pEdit->InsertReturn(false, true);
+  m_pEdit->InsertReturn(false);
   return 0;
 }
 
-int CFXEU_InsertReturn::Undo() {
+int CPWL_EditImpl::UndoInsertReturn::Undo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpNew);
-  m_pEdit->Backspace(false, true);
+  m_pEdit->Backspace(false);
   return 0;
 }
 
-CFXEU_ReplaceSelection::CFXEU_ReplaceSelection(CPWL_EditImpl* pEdit,
-                                               bool bIsEnd)
+class CPWL_EditImpl::UndoReplaceSelection final
+    : public CPWL_EditImpl::UndoItemIface {
+ public:
+  UndoReplaceSelection(CPWL_EditImpl* pEdit, bool bIsEnd);
+  ~UndoReplaceSelection() override;
+
+  // UndoItemIface:
+  int Redo() override;
+  int Undo() override;
+
+ private:
+  bool IsEnd() const { return m_bEnd; }
+
+  UnownedPtr<CPWL_EditImpl> m_pEdit;
+  const bool m_bEnd;  // indicate whether this is the end of replace action
+};
+
+CPWL_EditImpl::UndoReplaceSelection::UndoReplaceSelection(CPWL_EditImpl* pEdit,
+                                                          bool bIsEnd)
     : m_pEdit(pEdit), m_bEnd(bIsEnd) {
-  ASSERT(m_pEdit);
+  DCHECK(m_pEdit);
 }
 
-CFXEU_ReplaceSelection::~CFXEU_ReplaceSelection() {}
+CPWL_EditImpl::UndoReplaceSelection::~UndoReplaceSelection() = default;
 
-int CFXEU_ReplaceSelection::Redo() {
+int CPWL_EditImpl::UndoReplaceSelection::Redo() {
   m_pEdit->SelectNone();
   if (IsEnd())
     return 0;
@@ -322,7 +361,7 @@
   return 3;
 }
 
-int CFXEU_ReplaceSelection::Undo() {
+int CPWL_EditImpl::UndoReplaceSelection::Undo() {
   m_pEdit->SelectNone();
   if (!IsEnd())
     return 0;
@@ -331,155 +370,238 @@
   return 3;
 }
 
-CFXEU_Backspace::CFXEU_Backspace(CPWL_EditImpl* pEdit,
-                                 const CPVT_WordPlace& wpOldPlace,
-                                 const CPVT_WordPlace& wpNewPlace,
-                                 uint16_t word,
-                                 int32_t charset)
+class CPWL_EditImpl::UndoBackspace final : public CPWL_EditImpl::UndoItemIface {
+ public:
+  UndoBackspace(CPWL_EditImpl* pEdit,
+                const CPVT_WordPlace& wpOldPlace,
+                const CPVT_WordPlace& wpNewPlace,
+                uint16_t word,
+                FX_Charset charset);
+  ~UndoBackspace() override;
+
+  // UndoItemIface:
+  int Redo() override;
+  int Undo() override;
+
+ private:
+  UnownedPtr<CPWL_EditImpl> m_pEdit;
+
+  CPVT_WordPlace m_wpOld;
+  CPVT_WordPlace m_wpNew;
+  uint16_t m_Word;
+  FX_Charset m_nCharset;
+};
+
+CPWL_EditImpl::UndoBackspace::UndoBackspace(CPWL_EditImpl* pEdit,
+                                            const CPVT_WordPlace& wpOldPlace,
+                                            const CPVT_WordPlace& wpNewPlace,
+                                            uint16_t word,
+                                            FX_Charset charset)
     : m_pEdit(pEdit),
       m_wpOld(wpOldPlace),
       m_wpNew(wpNewPlace),
       m_Word(word),
       m_nCharset(charset) {
-  ASSERT(m_pEdit);
+  DCHECK(m_pEdit);
 }
 
-CFXEU_Backspace::~CFXEU_Backspace() {}
+CPWL_EditImpl::UndoBackspace::~UndoBackspace() = default;
 
-int CFXEU_Backspace::Redo() {
+int CPWL_EditImpl::UndoBackspace::Redo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpOld);
-  m_pEdit->Backspace(false, true);
+  m_pEdit->Backspace(false);
   return 0;
 }
 
-int CFXEU_Backspace::Undo() {
+int CPWL_EditImpl::UndoBackspace::Undo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpNew);
   if (m_wpNew.nSecIndex != m_wpOld.nSecIndex)
-    m_pEdit->InsertReturn(false, true);
+    m_pEdit->InsertReturn(false);
   else
-    m_pEdit->InsertWord(m_Word, m_nCharset, false, true);
+    m_pEdit->InsertWord(m_Word, m_nCharset, false);
   return 0;
 }
 
-CFXEU_Delete::CFXEU_Delete(CPWL_EditImpl* pEdit,
-                           const CPVT_WordPlace& wpOldPlace,
-                           const CPVT_WordPlace& wpNewPlace,
-                           uint16_t word,
-                           int32_t charset,
-                           bool bSecEnd)
+class CPWL_EditImpl::UndoDelete final : public CPWL_EditImpl::UndoItemIface {
+ public:
+  UndoDelete(CPWL_EditImpl* pEdit,
+             const CPVT_WordPlace& wpOldPlace,
+             const CPVT_WordPlace& wpNewPlace,
+             uint16_t word,
+             FX_Charset charset,
+             bool bSecEnd);
+  ~UndoDelete() override;
+
+  // UndoItemIface:
+  int Redo() override;
+  int Undo() override;
+
+ private:
+  UnownedPtr<CPWL_EditImpl> m_pEdit;
+
+  CPVT_WordPlace m_wpOld;
+  CPVT_WordPlace m_wpNew;
+  uint16_t m_Word;
+  FX_Charset m_nCharset;
+  bool m_bSecEnd;
+};
+
+CPWL_EditImpl::UndoDelete::UndoDelete(CPWL_EditImpl* pEdit,
+                                      const CPVT_WordPlace& wpOldPlace,
+                                      const CPVT_WordPlace& wpNewPlace,
+                                      uint16_t word,
+                                      FX_Charset charset,
+                                      bool bSecEnd)
     : m_pEdit(pEdit),
       m_wpOld(wpOldPlace),
       m_wpNew(wpNewPlace),
       m_Word(word),
       m_nCharset(charset),
       m_bSecEnd(bSecEnd) {
-  ASSERT(m_pEdit);
+  DCHECK(m_pEdit);
 }
 
-CFXEU_Delete::~CFXEU_Delete() {}
+CPWL_EditImpl::UndoDelete::~UndoDelete() = default;
 
-int CFXEU_Delete::Redo() {
+int CPWL_EditImpl::UndoDelete::Redo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpOld);
-  m_pEdit->Delete(false, true);
+  m_pEdit->Delete(false);
   return 0;
 }
 
-int CFXEU_Delete::Undo() {
+int CPWL_EditImpl::UndoDelete::Undo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpNew);
   if (m_bSecEnd)
-    m_pEdit->InsertReturn(false, true);
+    m_pEdit->InsertReturn(false);
   else
-    m_pEdit->InsertWord(m_Word, m_nCharset, false, true);
+    m_pEdit->InsertWord(m_Word, m_nCharset, false);
   return 0;
 }
 
-CFXEU_Clear::CFXEU_Clear(CPWL_EditImpl* pEdit,
-                         const CPVT_WordRange& wrSel,
-                         const WideString& swText)
+class CPWL_EditImpl::UndoClear final : public CPWL_EditImpl::UndoItemIface {
+ public:
+  UndoClear(CPWL_EditImpl* pEdit,
+            const CPVT_WordRange& wrSel,
+            const WideString& swText);
+  ~UndoClear() override;
+
+  // UndoItemIface:
+  int Redo() override;
+  int Undo() override;
+
+ private:
+  UnownedPtr<CPWL_EditImpl> m_pEdit;
+
+  CPVT_WordRange m_wrSel;
+  WideString m_swText;
+};
+
+CPWL_EditImpl::UndoClear::UndoClear(CPWL_EditImpl* pEdit,
+                                    const CPVT_WordRange& wrSel,
+                                    const WideString& swText)
     : m_pEdit(pEdit), m_wrSel(wrSel), m_swText(swText) {
-  ASSERT(m_pEdit);
+  DCHECK(m_pEdit);
 }
 
-CFXEU_Clear::~CFXEU_Clear() {}
+CPWL_EditImpl::UndoClear::~UndoClear() = default;
 
-int CFXEU_Clear::Redo() {
+int CPWL_EditImpl::UndoClear::Redo() {
   m_pEdit->SelectNone();
   m_pEdit->SetSelection(m_wrSel.BeginPos, m_wrSel.EndPos);
-  m_pEdit->Clear(false, true);
+  m_pEdit->Clear(false);
   return 0;
 }
 
-int CFXEU_Clear::Undo() {
+int CPWL_EditImpl::UndoClear::Undo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wrSel.BeginPos);
-  m_pEdit->InsertText(m_swText, FX_CHARSET_Default, false, true);
+  m_pEdit->InsertText(m_swText, FX_Charset::kDefault, false);
   m_pEdit->SetSelection(m_wrSel.BeginPos, m_wrSel.EndPos);
   return 0;
 }
 
-CFXEU_InsertText::CFXEU_InsertText(CPWL_EditImpl* pEdit,
-                                   const CPVT_WordPlace& wpOldPlace,
-                                   const CPVT_WordPlace& wpNewPlace,
-                                   const WideString& swText,
-                                   int32_t charset)
+class CPWL_EditImpl::UndoInsertText final
+    : public CPWL_EditImpl::UndoItemIface {
+ public:
+  UndoInsertText(CPWL_EditImpl* pEdit,
+                 const CPVT_WordPlace& wpOldPlace,
+                 const CPVT_WordPlace& wpNewPlace,
+                 const WideString& swText,
+                 FX_Charset charset);
+  ~UndoInsertText() override;
+
+  // UndoItemIface:
+  int Redo() override;
+  int Undo() override;
+
+ private:
+  UnownedPtr<CPWL_EditImpl> m_pEdit;
+
+  CPVT_WordPlace m_wpOld;
+  CPVT_WordPlace m_wpNew;
+  WideString m_swText;
+  FX_Charset m_nCharset;
+};
+
+CPWL_EditImpl::UndoInsertText::UndoInsertText(CPWL_EditImpl* pEdit,
+                                              const CPVT_WordPlace& wpOldPlace,
+                                              const CPVT_WordPlace& wpNewPlace,
+                                              const WideString& swText,
+                                              FX_Charset charset)
     : m_pEdit(pEdit),
       m_wpOld(wpOldPlace),
       m_wpNew(wpNewPlace),
       m_swText(swText),
       m_nCharset(charset) {
-  ASSERT(m_pEdit);
+  DCHECK(m_pEdit);
 }
 
-CFXEU_InsertText::~CFXEU_InsertText() {}
+CPWL_EditImpl::UndoInsertText::~UndoInsertText() = default;
 
-int CFXEU_InsertText::Redo() {
+int CPWL_EditImpl::UndoInsertText::Redo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpOld);
-  m_pEdit->InsertText(m_swText, m_nCharset, false, true);
+  m_pEdit->InsertText(m_swText, m_nCharset, false);
   return 0;
 }
 
-int CFXEU_InsertText::Undo() {
+int CPWL_EditImpl::UndoInsertText::Undo() {
   m_pEdit->SelectNone();
   m_pEdit->SetSelection(m_wpOld, m_wpNew);
-  m_pEdit->Clear(false, true);
+  m_pEdit->Clear(false);
   return 0;
 }
 
-// static
 void CPWL_EditImpl::DrawEdit(CFX_RenderDevice* pDevice,
                              const CFX_Matrix& mtUser2Device,
-                             CPWL_EditImpl* pEdit,
                              FX_COLORREF crTextFill,
                              const CFX_FloatRect& rcClip,
                              const CFX_PointF& ptOffset,
                              const CPVT_WordRange* pRange,
-                             IPWL_SystemHandler* pSystemHandler,
-                             CFFL_FormFiller* pFFLData) {
-  const bool bContinuous =
-      pEdit->GetCharArray() == 0 && pEdit->GetCharSpace() <= 0.0f;
-  uint16_t SubWord = pEdit->GetPasswordChar();
-  float fFontSize = pEdit->GetFontSize();
-  CPVT_WordRange wrSelect = pEdit->GetSelectWordRange();
+                             IPWL_FillerNotify* pFillerNotify,
+                             IPWL_FillerNotify::PerWindowData* pSystemData) {
+  const bool bContinuous = GetCharArray() == 0;
+  uint16_t SubWord = GetPasswordChar();
+  float fFontSize = GetFontSize();
+  CPVT_WordRange wrSelect = GetSelectWordRange();
   FX_COLORREF crCurFill = crTextFill;
   FX_COLORREF crOldFill = crCurFill;
   bool bSelect = false;
   const FX_COLORREF crWhite = ArgbEncode(255, 255, 255, 255);
   const FX_COLORREF crSelBK = ArgbEncode(255, 0, 51, 113);
 
-  std::ostringstream sTextBuf;
   int32_t nFontIndex = -1;
   CFX_PointF ptBT;
   CFX_RenderDevice::StateRestorer restorer(pDevice);
   if (!rcClip.IsEmpty())
     pDevice->SetClip_Rect(mtUser2Device.TransformRect(rcClip).ToFxRect());
 
-  CPWL_EditImpl_Iterator* pIterator = pEdit->GetIterator();
-  IPVT_FontMap* pFontMap = pEdit->GetFontMap();
+  Iterator* pIterator = GetIterator();
+  IPVT_FontMap* pFontMap = GetFontMap();
   if (!pFontMap)
     return;
 
@@ -488,6 +610,7 @@
   else
     pIterator->SetAt(0);
 
+  ByteString sTextBuf;
   CPVT_WordPlace oldplace;
   while (pIterator->NextWord()) {
     CPVT_WordPlace place = pIterator->GetAt();
@@ -498,7 +621,7 @@
       bSelect = place > wrSelect.BeginPos && place <= wrSelect.EndPos;
       crCurFill = bSelect ? crWhite : crTextFill;
     }
-    if (pSystemHandler->IsSelectionImplemented()) {
+    if (pFillerNotify->IsSelectionImplemented()) {
       crCurFill = crTextFill;
       crOldFill = crCurFill;
     }
@@ -507,73 +630,60 @@
       if (bSelect) {
         CPVT_Line line;
         pIterator->GetLine(line);
-
-        if (pSystemHandler->IsSelectionImplemented()) {
+        if (pFillerNotify->IsSelectionImplemented()) {
           CFX_FloatRect rc(word.ptWord.x, line.ptLine.y + line.fLineDescent,
                            word.ptWord.x + word.fWidth,
                            line.ptLine.y + line.fLineAscent);
           rc.Intersect(rcClip);
-          pSystemHandler->OutputSelectedRect(pFFLData, rc);
+          pFillerNotify->OutputSelectedRect(pSystemData, rc);
         } else {
-          CFX_PathData pathSelBK;
+          CFX_Path pathSelBK;
           pathSelBK.AppendRect(word.ptWord.x, line.ptLine.y + line.fLineDescent,
                                word.ptWord.x + word.fWidth,
                                line.ptLine.y + line.fLineAscent);
 
-          pDevice->DrawPath(&pathSelBK, &mtUser2Device, nullptr, crSelBK, 0,
-                            FXFILL_WINDING);
+          pDevice->DrawPath(pathSelBK, &mtUser2Device, nullptr, crSelBK, 0,
+                            CFX_FillRenderOptions::WindingOptions());
         }
       }
-
       if (bContinuous) {
         if (place.LineCmp(oldplace) != 0 || word.nFontIndex != nFontIndex ||
             crOldFill != crCurFill) {
-          if (sTextBuf.tellp() > 0) {
+          if (!sTextBuf.IsEmpty()) {
             DrawTextString(pDevice,
                            CFX_PointF(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y),
                            pFontMap->GetPDFFont(nFontIndex).Get(), fFontSize,
-                           mtUser2Device, ByteString(sTextBuf), crOldFill);
-
-            sTextBuf.str("");
+                           mtUser2Device, sTextBuf, crOldFill);
+            sTextBuf.clear();
           }
           nFontIndex = word.nFontIndex;
           ptBT = word.ptWord;
           crOldFill = crCurFill;
         }
-
-        sTextBuf << pEdit->GetPDFWordString(word.nFontIndex, word.Word,
-                                            SubWord);
+        sTextBuf += GetPDFWordString(word.nFontIndex, word.Word, SubWord);
       } else {
         DrawTextString(
             pDevice,
             CFX_PointF(word.ptWord.x + ptOffset.x, word.ptWord.y + ptOffset.y),
             pFontMap->GetPDFFont(word.nFontIndex).Get(), fFontSize,
             mtUser2Device,
-            pEdit->GetPDFWordString(word.nFontIndex, word.Word, SubWord),
-            crCurFill);
+            GetPDFWordString(word.nFontIndex, word.Word, SubWord), crCurFill);
       }
       oldplace = place;
     }
   }
-
-  if (sTextBuf.tellp() > 0) {
+  if (!sTextBuf.IsEmpty()) {
     DrawTextString(pDevice,
                    CFX_PointF(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y),
                    pFontMap->GetPDFFont(nFontIndex).Get(), fFontSize,
-                   mtUser2Device, ByteString(sTextBuf), crOldFill);
+                   mtUser2Device, sTextBuf, crOldFill);
   }
 }
 
 CPWL_EditImpl::CPWL_EditImpl()
-    : m_pVT(pdfium::MakeUnique<CPDF_VariableText>()),
-      m_bEnableScroll(false),
-      m_nAlignment(0),
-      m_bNotifyFlag(false),
-      m_bEnableOverflow(false),
-      m_bEnableRefresh(true),
-      m_bEnableUndo(true) {}
+    : m_pVT(std::make_unique<CPVT_VariableText>(nullptr)) {}
 
-CPWL_EditImpl::~CPWL_EditImpl() {}
+CPWL_EditImpl::~CPWL_EditImpl() = default;
 
 void CPWL_EditImpl::Initialize() {
   m_pVT->Initialize();
@@ -582,23 +692,17 @@
 }
 
 void CPWL_EditImpl::SetFontMap(IPVT_FontMap* pFontMap) {
-  m_pVTProvider = pdfium::MakeUnique<CPWL_EditImpl_Provider>(pFontMap);
+  m_pVTProvider = std::make_unique<Provider>(pFontMap);
   m_pVT->SetProvider(m_pVTProvider.get());
 }
 
-void CPWL_EditImpl::SetNotify(CPWL_EditCtrl* pNotify) {
+void CPWL_EditImpl::SetNotify(CPWL_Edit* pNotify) {
   m_pNotify = pNotify;
 }
 
-void CPWL_EditImpl::SetOperationNotify(CPWL_Edit* pOperationNotify) {
-  m_pOperationNotify = pOperationNotify;
-}
-
-CPWL_EditImpl_Iterator* CPWL_EditImpl::GetIterator() {
-  if (!m_pIterator) {
-    m_pIterator =
-        pdfium::MakeUnique<CPWL_EditImpl_Iterator>(this, m_pVT->GetIterator());
-  }
+CPWL_EditImpl::Iterator* CPWL_EditImpl::GetIterator() {
+  if (!m_pIterator)
+    m_pIterator = std::make_unique<Iterator>(this, m_pVT->GetIterator());
   return m_pIterator.get();
 }
 
@@ -609,75 +713,50 @@
 void CPWL_EditImpl::SetPlateRect(const CFX_FloatRect& rect) {
   m_pVT->SetPlateRect(rect);
   m_ptScrollPos = CFX_PointF(rect.left, rect.top);
-  Paint();
 }
 
-void CPWL_EditImpl::SetAlignmentH(int32_t nFormat, bool bPaint) {
+void CPWL_EditImpl::SetAlignmentH(int32_t nFormat) {
   m_pVT->SetAlignment(nFormat);
-  if (bPaint)
-    Paint();
 }
 
-void CPWL_EditImpl::SetAlignmentV(int32_t nFormat, bool bPaint) {
+void CPWL_EditImpl::SetAlignmentV(int32_t nFormat) {
   m_nAlignment = nFormat;
-  if (bPaint)
-    Paint();
 }
 
-void CPWL_EditImpl::SetPasswordChar(uint16_t wSubWord, bool bPaint) {
+void CPWL_EditImpl::SetPasswordChar(uint16_t wSubWord) {
   m_pVT->SetPasswordChar(wSubWord);
-  if (bPaint)
-    Paint();
 }
 
 void CPWL_EditImpl::SetLimitChar(int32_t nLimitChar) {
   m_pVT->SetLimitChar(nLimitChar);
-  Paint();
 }
 
 void CPWL_EditImpl::SetCharArray(int32_t nCharArray) {
   m_pVT->SetCharArray(nCharArray);
-  Paint();
 }
 
-void CPWL_EditImpl::SetCharSpace(float fCharSpace) {
-  m_pVT->SetCharSpace(fCharSpace);
-  Paint();
-}
-
-void CPWL_EditImpl::SetMultiLine(bool bMultiLine, bool bPaint) {
+void CPWL_EditImpl::SetMultiLine(bool bMultiLine) {
   m_pVT->SetMultiLine(bMultiLine);
-  if (bPaint)
-    Paint();
 }
 
-void CPWL_EditImpl::SetAutoReturn(bool bAuto, bool bPaint) {
+void CPWL_EditImpl::SetAutoReturn(bool bAuto) {
   m_pVT->SetAutoReturn(bAuto);
-  if (bPaint)
-    Paint();
 }
 
-void CPWL_EditImpl::SetAutoFontSize(bool bAuto, bool bPaint) {
+void CPWL_EditImpl::SetAutoFontSize(bool bAuto) {
   m_pVT->SetAutoFontSize(bAuto);
-  if (bPaint)
-    Paint();
 }
 
 void CPWL_EditImpl::SetFontSize(float fFontSize) {
   m_pVT->SetFontSize(fFontSize);
-  Paint();
 }
 
-void CPWL_EditImpl::SetAutoScroll(bool bAuto, bool bPaint) {
+void CPWL_EditImpl::SetAutoScroll(bool bAuto) {
   m_bEnableScroll = bAuto;
-  if (bPaint)
-    Paint();
 }
 
-void CPWL_EditImpl::SetTextOverflow(bool bAllowed, bool bPaint) {
+void CPWL_EditImpl::SetTextOverflow(bool bAllowed) {
   m_bEnableOverflow = bAllowed;
-  if (bPaint)
-    Paint();
 }
 
 void CPWL_EditImpl::SetSelection(int32_t nStartChar, int32_t nEndChar) {
@@ -712,24 +791,20 @@
   SetCaretInfo();
 }
 
-void CPWL_EditImpl::GetSelection(int32_t& nStartChar, int32_t& nEndChar) const {
-  nStartChar = -1;
-  nEndChar = -1;
+std::pair<int32_t, int32_t> CPWL_EditImpl::GetSelection() const {
   if (!m_pVT->IsValid())
-    return;
+    return std::make_pair(-1, -1);
 
   if (m_SelState.IsEmpty()) {
-    nStartChar = m_pVT->WordPlaceToWordIndex(m_wpCaret);
-    nEndChar = m_pVT->WordPlaceToWordIndex(m_wpCaret);
-    return;
+    return std::make_pair(m_pVT->WordPlaceToWordIndex(m_wpCaret),
+                          m_pVT->WordPlaceToWordIndex(m_wpCaret));
   }
   if (m_SelState.BeginPos < m_SelState.EndPos) {
-    nStartChar = m_pVT->WordPlaceToWordIndex(m_SelState.BeginPos);
-    nEndChar = m_pVT->WordPlaceToWordIndex(m_SelState.EndPos);
-    return;
+    return std::make_pair(m_pVT->WordPlaceToWordIndex(m_SelState.BeginPos),
+                          m_pVT->WordPlaceToWordIndex(m_SelState.EndPos));
   }
-  nStartChar = m_pVT->WordPlaceToWordIndex(m_SelState.EndPos);
-  nEndChar = m_pVT->WordPlaceToWordIndex(m_SelState.BeginPos);
+  return std::make_pair(m_pVT->WordPlaceToWordIndex(m_SelState.EndPos),
+                        m_pVT->WordPlaceToWordIndex(m_SelState.BeginPos));
 }
 
 int32_t CPWL_EditImpl::GetCaret() const {
@@ -748,7 +823,7 @@
   if (!m_pVT->IsValid())
     return swRet;
 
-  CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator();
+  CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator();
   pIterator->SetAt(0);
 
   CPVT_Word wordinfo;
@@ -769,7 +844,7 @@
   if (!m_pVT->IsValid())
     return swRet;
 
-  CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator();
+  CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator();
   CPVT_WordRange wrTemp = range;
   m_pVT->UpdateWordPlace(wrTemp.BeginPos);
   m_pVT->UpdateWordPlace(wrTemp.EndPos);
@@ -797,7 +872,7 @@
 int32_t CPWL_EditImpl::GetTotalLines() const {
   int32_t nLines = 1;
 
-  CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator();
+  CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator();
   pIterator->SetAt(0);
   while (pIterator->NextLine())
     ++nLines;
@@ -811,32 +886,31 @@
 
 void CPWL_EditImpl::SetText(const WideString& sText) {
   Clear();
-  DoInsertText(CPVT_WordPlace(0, 0, -1), sText, FX_CHARSET_Default);
-  Paint();
+  DoInsertText(CPVT_WordPlace(0, 0, -1), sText, FX_Charset::kDefault);
 }
 
-bool CPWL_EditImpl::InsertWord(uint16_t word, int32_t charset) {
-  return InsertWord(word, charset, true, true);
+bool CPWL_EditImpl::InsertWord(uint16_t word, FX_Charset charset) {
+  return InsertWord(word, charset, true);
 }
 
 bool CPWL_EditImpl::InsertReturn() {
-  return InsertReturn(true, true);
+  return InsertReturn(true);
 }
 
 bool CPWL_EditImpl::Backspace() {
-  return Backspace(true, true);
+  return Backspace(true);
 }
 
 bool CPWL_EditImpl::Delete() {
-  return Delete(true, true);
+  return Delete(true);
 }
 
 bool CPWL_EditImpl::ClearSelection() {
-  return Clear(true, true);
+  return Clear(true);
 }
 
-bool CPWL_EditImpl::InsertText(const WideString& sText, int32_t charset) {
-  return InsertText(sText, charset, true, true);
+bool CPWL_EditImpl::InsertText(const WideString& sText, FX_Charset charset) {
+  return InsertText(sText, charset, true);
 }
 
 float CPWL_EditImpl::GetFontSize() const {
@@ -855,10 +929,6 @@
   return VTToEdit(m_pVT->GetContentRect());
 }
 
-float CPWL_EditImpl::GetCharSpace() const {
-  return m_pVT->GetCharSpace();
-}
-
 CPVT_WordRange CPWL_EditImpl::GetWholeWordRange() const {
   if (m_pVT->IsValid())
     return CPVT_WordRange(m_pVT->GetBeginWordPlace(), m_pVT->GetEndWordPlace());
@@ -935,7 +1005,7 @@
 void CPWL_EditImpl::SelectAll() {
   if (!m_pVT->IsValid())
     return;
-  m_SelState = CPWL_EditImpl_Select(GetWholeWordRange());
+  m_SelState = SelectState(GetWholeWordRange());
   SetCaret(m_SelState.EndPos);
   ScrollToCaret();
   Refresh();
@@ -1032,7 +1102,7 @@
     return;
 
   if (m_pVT->IsValid()) {
-    if (!IsFloatEqual(m_ptScrollPos.x, fx)) {
+    if (!FXSYS_IsFloatEqual(m_ptScrollPos.x, fx)) {
       m_ptScrollPos.x = fx;
       Refresh();
     }
@@ -1044,7 +1114,7 @@
     return;
 
   if (m_pVT->IsValid()) {
-    if (!IsFloatEqual(m_ptScrollPos.y, fy)) {
+    if (!FXSYS_IsFloatEqual(m_ptScrollPos.y, fy)) {
       m_ptScrollPos.y = fy;
       Refresh();
 
@@ -1078,10 +1148,10 @@
     if (rcPlate.Width() > rcContent.Width()) {
       SetScrollPosX(rcPlate.left);
     } else {
-      if (IsFloatSmaller(m_ptScrollPos.x, rcContent.left)) {
+      if (FXSYS_IsFloatSmaller(m_ptScrollPos.x, rcContent.left)) {
         SetScrollPosX(rcContent.left);
-      } else if (IsFloatBigger(m_ptScrollPos.x,
-                               rcContent.right - rcPlate.Width())) {
+      } else if (FXSYS_IsFloatBigger(m_ptScrollPos.x,
+                                     rcContent.right - rcPlate.Width())) {
         SetScrollPosX(rcContent.right - rcPlate.Width());
       }
     }
@@ -1089,10 +1159,10 @@
     if (rcPlate.Height() > rcContent.Height()) {
       SetScrollPosY(rcPlate.top);
     } else {
-      if (IsFloatSmaller(m_ptScrollPos.y,
-                         rcContent.bottom + rcPlate.Height())) {
+      if (FXSYS_IsFloatSmaller(m_ptScrollPos.y,
+                               rcContent.bottom + rcPlate.Height())) {
         SetScrollPosY(rcContent.bottom + rcPlate.Height());
-      } else if (IsFloatBigger(m_ptScrollPos.y, rcContent.top)) {
+      } else if (FXSYS_IsFloatBigger(m_ptScrollPos.y, rcContent.top)) {
         SetScrollPosY(rcContent.top);
       }
     }
@@ -1105,7 +1175,7 @@
   if (!m_pVT->IsValid())
     return;
 
-  CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator();
+  CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator();
   pIterator->SetAt(m_wpCaret);
 
   CFX_PointF ptHead;
@@ -1127,23 +1197,23 @@
   CFX_PointF ptHeadEdit = VTToEdit(ptHead);
   CFX_PointF ptFootEdit = VTToEdit(ptFoot);
   CFX_FloatRect rcPlate = m_pVT->GetPlateRect();
-  if (!IsFloatEqual(rcPlate.left, rcPlate.right)) {
-    if (IsFloatSmaller(ptHeadEdit.x, rcPlate.left) ||
-        IsFloatEqual(ptHeadEdit.x, rcPlate.left)) {
+  if (!FXSYS_IsFloatEqual(rcPlate.left, rcPlate.right)) {
+    if (FXSYS_IsFloatSmaller(ptHeadEdit.x, rcPlate.left) ||
+        FXSYS_IsFloatEqual(ptHeadEdit.x, rcPlate.left)) {
       SetScrollPosX(ptHead.x);
-    } else if (IsFloatBigger(ptHeadEdit.x, rcPlate.right)) {
+    } else if (FXSYS_IsFloatBigger(ptHeadEdit.x, rcPlate.right)) {
       SetScrollPosX(ptHead.x - rcPlate.Width());
     }
   }
 
-  if (!IsFloatEqual(rcPlate.top, rcPlate.bottom)) {
-    if (IsFloatSmaller(ptFootEdit.y, rcPlate.bottom) ||
-        IsFloatEqual(ptFootEdit.y, rcPlate.bottom)) {
-      if (IsFloatSmaller(ptHeadEdit.y, rcPlate.top)) {
+  if (!FXSYS_IsFloatEqual(rcPlate.top, rcPlate.bottom)) {
+    if (FXSYS_IsFloatSmaller(ptFootEdit.y, rcPlate.bottom) ||
+        FXSYS_IsFloatEqual(ptFootEdit.y, rcPlate.bottom)) {
+      if (FXSYS_IsFloatSmaller(ptHeadEdit.y, rcPlate.top)) {
         SetScrollPosY(ptFoot.y + rcPlate.Height());
       }
-    } else if (IsFloatBigger(ptHeadEdit.y, rcPlate.top)) {
-      if (IsFloatBigger(ptFootEdit.y, rcPlate.bottom)) {
+    } else if (FXSYS_IsFloatBigger(ptHeadEdit.y, rcPlate.top)) {
+      if (FXSYS_IsFloatBigger(ptFootEdit.y, rcPlate.bottom)) {
         SetScrollPosY(ptHead.y);
       }
     }
@@ -1162,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;
+          }
         }
       }
     }
@@ -1177,7 +1250,7 @@
   if (!m_pVT->IsValid())
     return;
 
-  CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator();
+  CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator();
   CPVT_WordPlace wpBegin = wr.BeginPos;
   m_pVT->UpdateWordPlace(wpBegin);
   CPVT_WordPlace wpEnd = wr.EndPos;
@@ -1202,7 +1275,7 @@
 }
 
 void CPWL_EditImpl::RefreshWordRange(const CPVT_WordRange& wr) {
-  CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator();
+  CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator();
   CPVT_WordRange wrTemp = wr;
 
   m_pVT->UpdateWordPlace(wrTemp.BeginPos);
@@ -1232,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 {
@@ -1246,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.
+          }
         }
       }
 
@@ -1263,7 +1340,7 @@
 void CPWL_EditImpl::SetCaretInfo() {
   if (m_pNotify) {
     if (!m_bNotifyFlag) {
-      CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator();
+      CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator();
       pIterator->SetAt(m_wpCaret);
 
       CFX_PointF ptHead;
@@ -1321,7 +1398,7 @@
   SetCaretInfo();
 }
 
-void CPWL_EditImpl::OnVK_UP(bool bShift, bool bCtrl) {
+void CPWL_EditImpl::OnVK_UP(bool bShift) {
   if (!m_pVT->IsValid())
     return;
 
@@ -1344,7 +1421,7 @@
   }
 }
 
-void CPWL_EditImpl::OnVK_DOWN(bool bShift, bool bCtrl) {
+void CPWL_EditImpl::OnVK_DOWN(bool bShift) {
   if (!m_pVT->IsValid())
     return;
 
@@ -1367,7 +1444,7 @@
   }
 }
 
-void CPWL_EditImpl::OnVK_LEFT(bool bShift, bool bCtrl) {
+void CPWL_EditImpl::OnVK_LEFT(bool bShift) {
   if (!m_pVT->IsValid())
     return;
 
@@ -1410,7 +1487,7 @@
   }
 }
 
-void CPWL_EditImpl::OnVK_RIGHT(bool bShift, bool bCtrl) {
+void CPWL_EditImpl::OnVK_RIGHT(bool bShift) {
   if (!m_pVT->IsValid())
     return;
 
@@ -1528,9 +1605,8 @@
 }
 
 bool CPWL_EditImpl::InsertWord(uint16_t word,
-                               int32_t charset,
-                               bool bAddUndo,
-                               bool bPaint) {
+                               FX_Charset charset,
+                               bool bAddUndo) {
   if (IsTextOverflow() || !m_pVT->IsValid())
     return false;
 
@@ -1542,19 +1618,14 @@
     return false;
 
   if (bAddUndo && m_bEnableUndo) {
-    AddEditUndoItem(pdfium::MakeUnique<CFXEU_InsertWord>(
-        this, m_wpOldCaret, m_wpCaret, word, charset));
+    AddEditUndoItem(std::make_unique<UndoInsertWord>(this, m_wpOldCaret,
+                                                     m_wpCaret, word, charset));
   }
-  if (bPaint)
-    PaintInsertText(m_wpOldCaret, m_wpCaret);
-
-  if (m_pOperationNotify)
-    m_pOperationNotify->OnInsertWord(m_wpCaret, m_wpOldCaret);
-
+  PaintInsertText(m_wpOldCaret, m_wpCaret);
   return true;
 }
 
-bool CPWL_EditImpl::InsertReturn(bool bAddUndo, bool bPaint) {
+bool CPWL_EditImpl::InsertReturn(bool bAddUndo) {
   if (IsTextOverflow() || !m_pVT->IsValid())
     return false;
 
@@ -1566,28 +1637,23 @@
 
   if (bAddUndo && m_bEnableUndo) {
     AddEditUndoItem(
-        pdfium::MakeUnique<CFXEU_InsertReturn>(this, m_wpOldCaret, m_wpCaret));
+        std::make_unique<UndoInsertReturn>(this, m_wpOldCaret, m_wpCaret));
   }
-  if (bPaint) {
-    RearrangePart(CPVT_WordRange(m_wpOldCaret, m_wpCaret));
-    ScrollToCaret();
-    Refresh();
-    SetCaretOrigin();
-    SetCaretInfo();
-  }
-  if (m_pOperationNotify)
-    m_pOperationNotify->OnInsertReturn(m_wpCaret, m_wpOldCaret);
-
+  RearrangePart(CPVT_WordRange(m_wpOldCaret, m_wpCaret));
+  ScrollToCaret();
+  Refresh();
+  SetCaretOrigin();
+  SetCaretInfo();
   return true;
 }
 
-bool CPWL_EditImpl::Backspace(bool bAddUndo, bool bPaint) {
+bool CPWL_EditImpl::Backspace(bool bAddUndo) {
   if (!m_pVT->IsValid() || m_wpCaret == m_pVT->GetBeginWordPlace())
     return false;
 
   CPVT_Word word;
   if (bAddUndo) {
-    CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator();
+    CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator();
     pIterator->SetAt(m_wpCaret);
     pIterator->GetWord(word);
   }
@@ -1598,29 +1664,24 @@
     return false;
 
   if (bAddUndo && m_bEnableUndo) {
-    AddEditUndoItem(pdfium::MakeUnique<CFXEU_Backspace>(
+    AddEditUndoItem(std::make_unique<UndoBackspace>(
         this, m_wpOldCaret, m_wpCaret, word.Word, word.nCharset));
   }
-  if (bPaint) {
-    RearrangePart(CPVT_WordRange(m_wpCaret, m_wpOldCaret));
-    ScrollToCaret();
-    Refresh();
-    SetCaretOrigin();
-    SetCaretInfo();
-  }
-  if (m_pOperationNotify)
-    m_pOperationNotify->OnBackSpace(m_wpCaret, m_wpOldCaret);
-
+  RearrangePart(CPVT_WordRange(m_wpCaret, m_wpOldCaret));
+  ScrollToCaret();
+  Refresh();
+  SetCaretOrigin();
+  SetCaretInfo();
   return true;
 }
 
-bool CPWL_EditImpl::Delete(bool bAddUndo, bool bPaint) {
+bool CPWL_EditImpl::Delete(bool bAddUndo) {
   if (!m_pVT->IsValid() || m_wpCaret == m_pVT->GetEndWordPlace())
     return false;
 
   CPVT_Word word;
   if (bAddUndo) {
-    CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator();
+    CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator();
     pIterator->SetAt(m_pVT->GetNextWordPlace(m_wpCaret));
     pIterator->GetWord(word);
   }
@@ -1630,23 +1691,18 @@
   m_SelState.Set(m_wpCaret, m_wpCaret);
   if (bAddUndo && m_bEnableUndo) {
     if (bSecEnd) {
-      AddEditUndoItem(pdfium::MakeUnique<CFXEU_Delete>(
+      AddEditUndoItem(std::make_unique<UndoDelete>(
           this, m_wpOldCaret, m_wpCaret, word.Word, word.nCharset, bSecEnd));
     } else {
-      AddEditUndoItem(pdfium::MakeUnique<CFXEU_Delete>(
+      AddEditUndoItem(std::make_unique<UndoDelete>(
           this, m_wpOldCaret, m_wpCaret, word.Word, word.nCharset, bSecEnd));
     }
   }
-  if (bPaint) {
-    RearrangePart(CPVT_WordRange(m_wpOldCaret, m_wpCaret));
-    ScrollToCaret();
-    Refresh();
-    SetCaretOrigin();
-    SetCaretInfo();
-  }
-  if (m_pOperationNotify)
-    m_pOperationNotify->OnDelete(m_wpCaret, m_wpOldCaret);
-
+  RearrangePart(CPVT_WordRange(m_wpOldCaret, m_wpCaret));
+  ScrollToCaret();
+  Refresh();
+  SetCaretOrigin();
+  SetCaretInfo();
   return true;
 }
 
@@ -1661,36 +1717,29 @@
   return false;
 }
 
-bool CPWL_EditImpl::Clear(bool bAddUndo, bool bPaint) {
+bool CPWL_EditImpl::Clear(bool bAddUndo) {
   if (!m_pVT->IsValid() || m_SelState.IsEmpty())
     return false;
 
   CPVT_WordRange range = m_SelState.ConvertToWordRange();
   if (bAddUndo && m_bEnableUndo) {
     AddEditUndoItem(
-        pdfium::MakeUnique<CFXEU_Clear>(this, range, GetSelectedText()));
+        std::make_unique<UndoClear>(this, range, GetSelectedText()));
   }
-
   SelectNone();
   SetCaret(m_pVT->DeleteWords(range));
   m_SelState.Set(m_wpCaret, m_wpCaret);
-  if (bPaint) {
-    RearrangePart(range);
-    ScrollToCaret();
-    Refresh();
-    SetCaretOrigin();
-    SetCaretInfo();
-  }
-  if (m_pOperationNotify)
-    m_pOperationNotify->OnClear(m_wpCaret, m_wpOldCaret);
-
+  RearrangePart(range);
+  ScrollToCaret();
+  Refresh();
+  SetCaretOrigin();
+  SetCaretInfo();
   return true;
 }
 
 bool CPWL_EditImpl::InsertText(const WideString& sText,
-                               int32_t charset,
-                               bool bAddUndo,
-                               bool bPaint) {
+                               FX_Charset charset,
+                               bool bAddUndo) {
   if (IsTextOverflow())
     return false;
 
@@ -1701,15 +1750,10 @@
     return false;
 
   if (bAddUndo && m_bEnableUndo) {
-    AddEditUndoItem(pdfium::MakeUnique<CFXEU_InsertText>(
+    AddEditUndoItem(std::make_unique<UndoInsertText>(
         this, m_wpOldCaret, m_wpCaret, sText, charset));
   }
-  if (bPaint)
-    PaintInsertText(m_wpOldCaret, m_wpCaret);
-
-  if (m_pOperationNotify)
-    m_pOperationNotify->OnInsertText(m_wpCaret, m_wpOldCaret);
-
+  PaintInsertText(m_wpOldCaret, m_wpCaret);
   return true;
 }
 
@@ -1724,11 +1768,24 @@
   }
 }
 
-void CPWL_EditImpl::ReplaceSelection(const WideString& text) {
-  AddEditUndoItem(pdfium::MakeUnique<CFXEU_ReplaceSelection>(this, false));
+void CPWL_EditImpl::ReplaceAndKeepSelection(const WideString& text) {
+  AddEditUndoItem(std::make_unique<UndoReplaceSelection>(this, false));
   ClearSelection();
-  InsertText(text, FX_CHARSET_Default);
-  AddEditUndoItem(pdfium::MakeUnique<CFXEU_ReplaceSelection>(this, true));
+
+  // Select the inserted text.
+  CPVT_WordPlace caret_before_insert = m_wpCaret;
+  InsertText(text, FX_Charset::kDefault);
+  CPVT_WordPlace caret_after_insert = m_wpCaret;
+  m_SelState.Set(caret_before_insert, caret_after_insert);
+
+  AddEditUndoItem(std::make_unique<UndoReplaceSelection>(this, true));
+}
+
+void CPWL_EditImpl::ReplaceSelection(const WideString& text) {
+  AddEditUndoItem(std::make_unique<UndoReplaceSelection>(this, false));
+  ClearSelection();
+  InsertText(text, FX_Charset::kDefault);
+  AddEditUndoItem(std::make_unique<UndoReplaceSelection>(this, true));
 }
 
 bool CPWL_EditImpl::Redo() {
@@ -1757,7 +1814,7 @@
   if (!m_pVT->IsValid())
     return;
 
-  CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator();
+  CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator();
   pIterator->SetAt(m_wpCaret);
   CPVT_Word word;
   CPVT_Line line;
@@ -1792,11 +1849,11 @@
     CFX_FloatRect rcContent = m_pVT->GetContentRect();
 
     if (m_pVT->IsMultiLine() && GetTotalLines() > 1 &&
-        IsFloatBigger(rcContent.Height(), rcPlate.Height())) {
+        FXSYS_IsFloatBigger(rcContent.Height(), rcPlate.Height())) {
       return true;
     }
 
-    if (IsFloatBigger(rcContent.Width(), rcPlate.Width()))
+    if (FXSYS_IsFloatBigger(rcContent.Width(), rcPlate.Width()))
       return true;
   }
 
@@ -1829,44 +1886,42 @@
 
 CPVT_WordPlace CPWL_EditImpl::DoInsertText(const CPVT_WordPlace& place,
                                            const WideString& sText,
-                                           int32_t charset) {
-  CPVT_WordPlace wp = place;
+                                           FX_Charset charset) {
+  if (!m_pVT->IsValid())
+    return place;
 
-  if (m_pVT->IsValid()) {
-    for (int32_t i = 0, sz = sText.GetLength(); i < sz; i++) {
-      uint16_t word = sText[i];
-      switch (word) {
-        case '\r':
-          wp = m_pVT->InsertSection(wp);
-          if (i + 1 < sz && sText[i + 1] == '\n')
-            i++;
-          break;
-        case '\n':
-          wp = m_pVT->InsertSection(wp);
-          break;
-        case '\t':
-          word = ' ';
-          FALLTHROUGH;
-        default:
-          wp =
-              m_pVT->InsertWord(wp, word, GetCharSetFromUnicode(word, charset));
-          break;
-      }
+  CPVT_WordPlace wp = place;
+  for (size_t i = 0; i < sText.GetLength(); ++i) {
+    uint16_t word = sText[i];
+    switch (word) {
+      case '\r':
+        wp = m_pVT->InsertSection(wp);
+        if (i + 1 < sText.GetLength() && sText[i + 1] == '\n')
+          i++;
+        break;
+      case '\n':
+        wp = m_pVT->InsertSection(wp);
+        break;
+      case '\t':
+        word = ' ';
+        [[fallthrough]];
+      default:
+        wp = m_pVT->InsertWord(wp, word, GetCharSetFromUnicode(word, charset));
+        break;
     }
   }
-
   return wp;
 }
 
-int32_t CPWL_EditImpl::GetCharSetFromUnicode(uint16_t word,
-                                             int32_t nOldCharset) {
+FX_Charset CPWL_EditImpl::GetCharSetFromUnicode(uint16_t word,
+                                                FX_Charset nOldCharset) {
   if (IPVT_FontMap* pFontMap = GetFontMap())
     return pFontMap->CharSetFromUnicode(word, nOldCharset);
   return nOldCharset;
 }
 
 void CPWL_EditImpl::AddEditUndoItem(
-    std::unique_ptr<IFX_Edit_UndoItem> pEditUndoItem) {
+    std::unique_ptr<UndoItemIface> pEditUndoItem) {
   m_Undo.AddItem(std::move(pEditUndoItem));
 }
 
@@ -1894,31 +1949,31 @@
   return sWord;
 }
 
-CPWL_EditImpl_Select::CPWL_EditImpl_Select() {}
+CPWL_EditImpl::SelectState::SelectState() = default;
 
-CPWL_EditImpl_Select::CPWL_EditImpl_Select(const CPVT_WordRange& range) {
+CPWL_EditImpl::SelectState::SelectState(const CPVT_WordRange& range) {
   Set(range.BeginPos, range.EndPos);
 }
 
-CPVT_WordRange CPWL_EditImpl_Select::ConvertToWordRange() const {
+CPVT_WordRange CPWL_EditImpl::SelectState::ConvertToWordRange() const {
   return CPVT_WordRange(BeginPos, EndPos);
 }
 
-void CPWL_EditImpl_Select::Reset() {
+void CPWL_EditImpl::SelectState::Reset() {
   BeginPos.Reset();
   EndPos.Reset();
 }
 
-void CPWL_EditImpl_Select::Set(const CPVT_WordPlace& begin,
-                               const CPVT_WordPlace& end) {
+void CPWL_EditImpl::SelectState::Set(const CPVT_WordPlace& begin,
+                                     const CPVT_WordPlace& end) {
   BeginPos = begin;
   EndPos = end;
 }
 
-void CPWL_EditImpl_Select::SetEndPos(const CPVT_WordPlace& end) {
+void CPWL_EditImpl::SelectState::SetEndPos(const CPVT_WordPlace& end) {
   EndPos = end;
 }
 
-bool CPWL_EditImpl_Select::IsEmpty() const {
+bool CPWL_EditImpl::SelectState::IsEmpty() const {
   return BeginPos == EndPos;
 }
diff --git a/fpdfsdk/pwl/cpwl_edit_impl.h b/fpdfsdk/pwl/cpwl_edit_impl.h
index e9ac7de..0b3bc76 100644
--- a/fpdfsdk/pwl/cpwl_edit_impl.h
+++ b/fpdfsdk/pwl/cpwl_edit_impl.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,270 +9,56 @@
 
 #include <deque>
 #include <memory>
+#include <utility>
 #include <vector>
 
-#include "core/fpdfdoc/cpdf_variabletext.h"
+#include "core/fpdfdoc/cpvt_variabletext.h"
 #include "core/fpdfdoc/cpvt_wordrange.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/fx_codepage_forward.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "fpdfsdk/pwl/ipwl_fillernotify.h"
 
-#define FX_EDIT_ISLATINWORD(u)                  \
-  (u == 0x2D || (u <= 0x005A && u >= 0x0041) || \
-   (u <= 0x007A && u >= 0x0061) || (u <= 0x02AF && u >= 0x00C0))
-
-class CFFL_FormFiller;
-class CPWL_EditImpl;
-class CPWL_EditImpl_Iterator;
-class CPWL_EditImpl_Provider;
 class CFX_RenderDevice;
 class CPWL_Edit;
-class CPWL_EditCtrl;
-class IPWL_SystemHandler;
-class IFX_Edit_UndoItem;
-
-struct CPWL_EditImpl_LineRect {
-  CPWL_EditImpl_LineRect(const CPVT_WordRange& wrLine,
-                         const CFX_FloatRect& rcLine)
-      : m_wrLine(wrLine), m_rcLine(rcLine) {}
-
-  CPVT_WordRange m_wrLine;
-  CFX_FloatRect m_rcLine;
-};
-
-class CPWL_EditImpl_Refresh {
- public:
-  CPWL_EditImpl_Refresh();
-  ~CPWL_EditImpl_Refresh();
-
-  void BeginRefresh();
-  void Push(const CPVT_WordRange& linerange, const CFX_FloatRect& rect);
-  void NoAnalyse();
-  std::vector<CFX_FloatRect>* GetRefreshRects();
-  void EndRefresh();
-
- private:
-  void Add(const CFX_FloatRect& new_rect);
-
-  std::vector<CPWL_EditImpl_LineRect> m_NewLineRects;
-  std::vector<CPWL_EditImpl_LineRect> m_OldLineRects;
-  std::vector<CFX_FloatRect> m_RefreshRects;
-};
-
-class CPWL_EditImpl_Select {
- public:
-  CPWL_EditImpl_Select();
-  explicit CPWL_EditImpl_Select(const CPVT_WordRange& range);
-
-  void Reset();
-  void Set(const CPVT_WordPlace& begin, const CPVT_WordPlace& end);
-  void SetEndPos(const CPVT_WordPlace& end);
-
-  CPVT_WordRange ConvertToWordRange() const;
-  bool IsEmpty() const;
-
-  CPVT_WordPlace BeginPos;
-  CPVT_WordPlace EndPos;
-};
-
-class CPWL_EditImpl_Undo {
- public:
-  CPWL_EditImpl_Undo();
-  ~CPWL_EditImpl_Undo();
-
-  void AddItem(std::unique_ptr<IFX_Edit_UndoItem> pItem);
-  void Undo();
-  void Redo();
-  bool CanUndo() const;
-  bool CanRedo() const;
-
- private:
-  void RemoveHeads();
-  void RemoveTails();
-
-  std::deque<std::unique_ptr<IFX_Edit_UndoItem>> m_UndoItemStack;
-  size_t m_nCurUndoPos;
-  bool m_bWorking;
-};
-
-class IFX_Edit_UndoItem {
- public:
-  virtual ~IFX_Edit_UndoItem() = default;
-
-  // Undo/Redo the current undo item and returns the number of additional items
-  // to be processed in |m_UndoItemStack| to fully undo/redo the action. (An
-  // example is CFXEU_ReplaceSelection::Undo(), if CFXEU_ReplaceSelection marks
-  // the end of a replace action, CFXEU_ReplaceSelection::Undo() returns 3
-  // because 3 more undo items need to be processed to revert the replace
-  // action: insert text, clear selection and the CFXEU_ReplaceSelection which
-  // marks the beginning of replace action.) Implementations should return 0 by
-  // default.
-  virtual int Undo() = 0;
-  virtual int Redo() = 0;
-};
-
-class CFXEU_InsertWord final : public IFX_Edit_UndoItem {
- public:
-  CFXEU_InsertWord(CPWL_EditImpl* pEdit,
-                   const CPVT_WordPlace& wpOldPlace,
-                   const CPVT_WordPlace& wpNewPlace,
-                   uint16_t word,
-                   int32_t charset);
-  ~CFXEU_InsertWord() override;
-
-  // IFX_Edit_UndoItem:
-  int Redo() override;
-  int Undo() override;
-
- private:
-  UnownedPtr<CPWL_EditImpl> m_pEdit;
-
-  CPVT_WordPlace m_wpOld;
-  CPVT_WordPlace m_wpNew;
-  uint16_t m_Word;
-  int32_t m_nCharset;
-};
-
-class CFXEU_InsertReturn final : public IFX_Edit_UndoItem {
- public:
-  CFXEU_InsertReturn(CPWL_EditImpl* pEdit,
-                     const CPVT_WordPlace& wpOldPlace,
-                     const CPVT_WordPlace& wpNewPlace);
-  ~CFXEU_InsertReturn() override;
-
-  // IFX_Edit_UndoItem:
-  int Redo() override;
-  int Undo() override;
-
- private:
-  UnownedPtr<CPWL_EditImpl> m_pEdit;
-
-  CPVT_WordPlace m_wpOld;
-  CPVT_WordPlace m_wpNew;
-};
-
-class CFXEU_ReplaceSelection final : public IFX_Edit_UndoItem {
- public:
-  CFXEU_ReplaceSelection(CPWL_EditImpl* pEdit, bool bIsEnd);
-  ~CFXEU_ReplaceSelection() override;
-
-  // IFX_Edit_UndoItem:
-  int Redo() override;
-  int Undo() override;
-
- private:
-  bool IsEnd() const { return m_bEnd; }
-
-  UnownedPtr<CPWL_EditImpl> m_pEdit;
-  const bool m_bEnd;  // indicate whether this is the end of replace action
-};
-
-class CFXEU_Backspace final : public IFX_Edit_UndoItem {
- public:
-  CFXEU_Backspace(CPWL_EditImpl* pEdit,
-                  const CPVT_WordPlace& wpOldPlace,
-                  const CPVT_WordPlace& wpNewPlace,
-                  uint16_t word,
-                  int32_t charset);
-  ~CFXEU_Backspace() override;
-
-  // IFX_Edit_UndoItem:
-  int Redo() override;
-  int Undo() override;
-
- private:
-  UnownedPtr<CPWL_EditImpl> m_pEdit;
-
-  CPVT_WordPlace m_wpOld;
-  CPVT_WordPlace m_wpNew;
-  uint16_t m_Word;
-  int32_t m_nCharset;
-};
-
-class CFXEU_Delete final : public IFX_Edit_UndoItem {
- public:
-  CFXEU_Delete(CPWL_EditImpl* pEdit,
-               const CPVT_WordPlace& wpOldPlace,
-               const CPVT_WordPlace& wpNewPlace,
-               uint16_t word,
-               int32_t charset,
-               bool bSecEnd);
-  ~CFXEU_Delete() override;
-
-  // IFX_Edit_UndoItem:
-  int Redo() override;
-  int Undo() override;
-
- private:
-  UnownedPtr<CPWL_EditImpl> m_pEdit;
-
-  CPVT_WordPlace m_wpOld;
-  CPVT_WordPlace m_wpNew;
-  uint16_t m_Word;
-  int32_t m_nCharset;
-  bool m_bSecEnd;
-};
-
-class CFXEU_Clear final : public IFX_Edit_UndoItem {
- public:
-  CFXEU_Clear(CPWL_EditImpl* pEdit,
-              const CPVT_WordRange& wrSel,
-              const WideString& swText);
-  ~CFXEU_Clear() override;
-
-  // IFX_Edit_UndoItem:
-  int Redo() override;
-  int Undo() override;
-
- private:
-  UnownedPtr<CPWL_EditImpl> m_pEdit;
-
-  CPVT_WordRange m_wrSel;
-  WideString m_swText;
-};
-
-class CFXEU_InsertText final : public IFX_Edit_UndoItem {
- public:
-  CFXEU_InsertText(CPWL_EditImpl* pEdit,
-                   const CPVT_WordPlace& wpOldPlace,
-                   const CPVT_WordPlace& wpNewPlace,
-                   const WideString& swText,
-                   int32_t charset);
-  ~CFXEU_InsertText() override;
-
-  // IFX_Edit_UndoItem:
-  int Redo() override;
-  int Undo() override;
-
- private:
-  UnownedPtr<CPWL_EditImpl> m_pEdit;
-
-  CPVT_WordPlace m_wpOld;
-  CPVT_WordPlace m_wpNew;
-  WideString m_swText;
-  int32_t m_nCharset;
-};
 
 class CPWL_EditImpl {
  public:
-  static void DrawEdit(CFX_RenderDevice* pDevice,
-                       const CFX_Matrix& mtUser2Device,
-                       CPWL_EditImpl* pEdit,
-                       FX_COLORREF crTextFill,
-                       const CFX_FloatRect& rcClip,
-                       const CFX_PointF& ptOffset,
-                       const CPVT_WordRange* pRange,
-                       IPWL_SystemHandler* pSystemHandler,
-                       CFFL_FormFiller* pFFLData);
+  class Iterator {
+   public:
+    Iterator(CPWL_EditImpl* pEdit, CPVT_VariableText::Iterator* pVTIterator);
+    ~Iterator();
+
+    bool NextWord();
+    bool GetWord(CPVT_Word& word) const;
+    bool GetLine(CPVT_Line& line) const;
+    void SetAt(int32_t nWordIndex);
+    void SetAt(const CPVT_WordPlace& place);
+    const CPVT_WordPlace& GetAt() const;
+
+   private:
+    UnownedPtr<CPWL_EditImpl> m_pEdit;
+    UnownedPtr<CPVT_VariableText::Iterator> m_pVTIterator;
+  };
 
   CPWL_EditImpl();
   ~CPWL_EditImpl();
 
+  void DrawEdit(CFX_RenderDevice* pDevice,
+                const CFX_Matrix& mtUser2Device,
+                FX_COLORREF crTextFill,
+                const CFX_FloatRect& rcClip,
+                const CFX_PointF& ptOffset,
+                const CPVT_WordRange* pRange,
+                IPWL_FillerNotify* pFillerNotify,
+                IPWL_FillerNotify::PerWindowData* pSystemData);
+
   void SetFontMap(IPVT_FontMap* pFontMap);
-  void SetNotify(CPWL_EditCtrl* pNotify);
-  void SetOperationNotify(CPWL_Edit* pOperationNotify);
+  void SetNotify(CPWL_Edit* pNotify);
 
   // Returns an iterator for the contents. Should not be released.
-  CPWL_EditImpl_Iterator* GetIterator();
+  Iterator* GetIterator();
   IPVT_FontMap* GetFontMap();
   void Initialize();
 
@@ -281,38 +67,39 @@
   void SetScrollPos(const CFX_PointF& point);
 
   // Set the horizontal text alignment. (nFormat [0:left, 1:middle, 2:right])
-  void SetAlignmentH(int32_t nFormat, bool bPaint);
+  void SetAlignmentH(int32_t nFormat);
+
   // Set the vertical text alignment. (nFormat [0:left, 1:middle, 2:right])
-  void SetAlignmentV(int32_t nFormat, bool bPaint);
+  void SetAlignmentV(int32_t nFormat);
 
   // Set the substitution character for hidden text.
-  void SetPasswordChar(uint16_t wSubWord, bool bPaint);
+  void SetPasswordChar(uint16_t wSubWord);
 
   // Set the maximum number of words in the text.
   void SetLimitChar(int32_t nLimitChar);
   void SetCharArray(int32_t nCharArray);
-  void SetCharSpace(float fCharSpace);
-  void SetMultiLine(bool bMultiLine, bool bPaint);
-  void SetAutoReturn(bool bAuto, bool bPaint);
-  void SetAutoFontSize(bool bAuto, bool bPaint);
-  void SetAutoScroll(bool bAuto, bool bPaint);
+  void SetMultiLine(bool bMultiLine);
+  void SetAutoReturn(bool bAuto);
+  void SetAutoFontSize(bool bAuto);
+  void SetAutoScroll(bool bAuto);
   void SetFontSize(float fFontSize);
-  void SetTextOverflow(bool bAllowed, bool bPaint);
+  void SetTextOverflow(bool bAllowed);
   void OnMouseDown(const CFX_PointF& point, bool bShift, bool bCtrl);
   void OnMouseMove(const CFX_PointF& point, bool bShift, bool bCtrl);
-  void OnVK_UP(bool bShift, bool bCtrl);
-  void OnVK_DOWN(bool bShift, bool bCtrl);
-  void OnVK_LEFT(bool bShift, bool bCtrl);
-  void OnVK_RIGHT(bool bShift, bool bCtrl);
+  void OnVK_UP(bool bShift);
+  void OnVK_DOWN(bool bShift);
+  void OnVK_LEFT(bool bShift);
+  void OnVK_RIGHT(bool bShift);
   void OnVK_HOME(bool bShift, bool bCtrl);
   void OnVK_END(bool bShift, bool bCtrl);
   void SetText(const WideString& sText);
-  bool InsertWord(uint16_t word, int32_t charset);
+  bool InsertWord(uint16_t word, FX_Charset charset);
   bool InsertReturn();
   bool Backspace();
   bool Delete();
   bool ClearSelection();
-  bool InsertText(const WideString& sText, int32_t charset);
+  bool InsertText(const WideString& sText, FX_Charset charset);
+  void ReplaceAndKeepSelection(const WideString& text);
   void ReplaceSelection(const WideString& text);
   bool Redo();
   bool Undo();
@@ -328,9 +115,8 @@
   int32_t GetCharArray() const;
   CFX_FloatRect GetContentRect() const;
   WideString GetRangeText(const CPVT_WordRange& range) const;
-  float GetCharSpace() const;
   void SetSelection(int32_t nStartChar, int32_t nEndChar);
-  void GetSelection(int32_t& nStartChar, int32_t& nEndChar) const;
+  std::pair<int32_t, int32_t> GetSelection() const;
   void SelectAll();
   void SelectNone();
   bool IsSelected() const;
@@ -341,40 +127,121 @@
   CPVT_WordRange GetSelectWordRange() const;
   void EnableUndo(bool bUndo);
   bool IsTextFull() const;
-  bool IsTextOverflow() const;
   bool CanUndo() const;
   bool CanRedo() const;
   CPVT_WordRange GetVisibleWordRange() const;
 
-  bool Clear();
-
-  CPVT_WordPlace DoInsertText(const CPVT_WordPlace& place,
-                              const WideString& sText,
-                              int32_t charset);
-  int32_t GetCharSetFromUnicode(uint16_t word, int32_t nOldCharset);
-
-  int32_t GetTotalLines() const;
-
   ByteString GetPDFWordString(int32_t nFontIndex,
                               uint16_t Word,
                               uint16_t SubWord);
 
-  void SetSelection(const CPVT_WordPlace& begin, const CPVT_WordPlace& end);
+ private:
+  class RefreshState {
+   public:
+    RefreshState();
+    ~RefreshState();
 
-  bool Delete(bool bAddUndo, bool bPaint);
-  bool Clear(bool bAddUndo, bool bPaint);
-  bool InsertText(const WideString& sText,
-                  int32_t charset,
-                  bool bAddUndo,
-                  bool bPaint);
-  bool InsertWord(uint16_t word, int32_t charset, bool bAddUndo, bool bPaint);
-  bool InsertReturn(bool bAddUndo, bool bPaint);
-  bool Backspace(bool bAddUndo, bool bPaint);
+    void BeginRefresh();
+    void Push(const CPVT_WordRange& linerange, const CFX_FloatRect& rect);
+    void NoAnalyse();
+    std::vector<CFX_FloatRect>* GetRefreshRects();
+    void EndRefresh();
+
+   private:
+    struct LineRect {
+      LineRect(const CPVT_WordRange& wrLine, const CFX_FloatRect& rcLine)
+          : m_wrLine(wrLine), m_rcLine(rcLine) {}
+
+      CPVT_WordRange m_wrLine;
+      CFX_FloatRect m_rcLine;
+    };
+
+    void Add(const CFX_FloatRect& new_rect);
+
+    std::vector<LineRect> m_NewLineRects;
+    std::vector<LineRect> m_OldLineRects;
+    std::vector<CFX_FloatRect> m_RefreshRects;
+  };
+
+  class SelectState {
+   public:
+    SelectState();
+    explicit SelectState(const CPVT_WordRange& range);
+
+    void Reset();
+    void Set(const CPVT_WordPlace& begin, const CPVT_WordPlace& end);
+    void SetEndPos(const CPVT_WordPlace& end);
+
+    CPVT_WordRange ConvertToWordRange() const;
+    bool IsEmpty() const;
+
+    CPVT_WordPlace BeginPos;
+    CPVT_WordPlace EndPos;
+  };
+
+  class UndoItemIface {
+   public:
+    virtual ~UndoItemIface() = default;
+
+    // Undo/Redo the current undo item and returns the number of additional
+    // items to be processed in |m_UndoItemStack| to fully undo/redo the action.
+    // (An example is UndoReplaceSelection::Undo(), if UndoReplaceSelection
+    // marks the end of a replace action, UndoReplaceSelection::Undo() returns 3
+    // because 3 more undo items need to be processed to revert the replace
+    // action: insert text, clear selection and the UndoReplaceSelection which
+    // marks the beginning of replace action.) Implementations should return 0
+    // by default.
+    virtual int Undo() = 0;
+    virtual int Redo() = 0;
+  };
+
+  class UndoStack {
+   public:
+    UndoStack();
+    ~UndoStack();
+
+    void AddItem(std::unique_ptr<UndoItemIface> pItem);
+    void Undo();
+    void Redo();
+    bool CanUndo() const;
+    bool CanRedo() const;
+
+   private:
+    void RemoveHeads();
+    void RemoveTails();
+
+    std::deque<std::unique_ptr<UndoItemIface>> m_UndoItemStack;
+    size_t m_nCurUndoPos = 0;
+    bool m_bWorking = false;
+  };
+
+  class Provider;
+  class UndoBackspace;
+  class UndoClear;
+  class UndoDelete;
+  class UndoInsertReturn;
+  class UndoInsertText;
+  class UndoInsertWord;
+  class UndoReplaceSelection;
+
+  bool IsTextOverflow() const;
+  bool Clear();
+  CPVT_WordPlace DoInsertText(const CPVT_WordPlace& place,
+                              const WideString& sText,
+                              FX_Charset charset);
+  FX_Charset GetCharSetFromUnicode(uint16_t word, FX_Charset nOldCharset);
+  int32_t GetTotalLines() const;
+  void SetSelection(const CPVT_WordPlace& begin, const CPVT_WordPlace& end);
+  bool Delete(bool bAddUndo);
+  bool Clear(bool bAddUndo);
+  bool InsertText(const WideString& sText, FX_Charset charset, bool bAddUndo);
+  bool InsertWord(uint16_t word, FX_Charset charset, bool bAddUndo);
+  bool InsertReturn(bool bAddUndo);
+  bool Backspace(bool bAddUndo);
   void SetCaret(const CPVT_WordPlace& place);
 
   CFX_PointF VTToEdit(const CFX_PointF& point) const;
 
- private:
   void RearrangeAll();
   void RearrangePart(const CPVT_WordRange& range);
   void ScrollToCaret();
@@ -396,68 +263,27 @@
   void SetCaretInfo();
   void SetCaretOrigin();
 
-  void AddEditUndoItem(std::unique_ptr<IFX_Edit_UndoItem> pEditUndoItem);
+  void AddEditUndoItem(std::unique_ptr<UndoItemIface> pEditUndoItem);
 
-  std::unique_ptr<CPWL_EditImpl_Provider> m_pVTProvider;
-  std::unique_ptr<CPDF_VariableText> m_pVT;  // Must outlive |m_pVTProvider|.
-  UnownedPtr<CPWL_EditCtrl> m_pNotify;
-  UnownedPtr<CPWL_Edit> m_pOperationNotify;
+  bool m_bEnableScroll = false;
+  bool m_bNotifyFlag = false;
+  bool m_bEnableOverflow = false;
+  bool m_bEnableRefresh = true;
+  bool m_bEnableUndo = true;
+  int32_t m_nAlignment = 0;
+  std::unique_ptr<Provider> m_pVTProvider;
+  std::unique_ptr<CPVT_VariableText> m_pVT;  // Must outlive |m_pVTProvider|.
+  UnownedPtr<CPWL_Edit> m_pNotify;
   CPVT_WordPlace m_wpCaret;
   CPVT_WordPlace m_wpOldCaret;
-  CPWL_EditImpl_Select m_SelState;
+  SelectState m_SelState;
   CFX_PointF m_ptScrollPos;
   CFX_PointF m_ptRefreshScrollPos;
-  bool m_bEnableScroll;
-  std::unique_ptr<CPWL_EditImpl_Iterator> m_pIterator;
-  CPWL_EditImpl_Refresh m_Refresh;
+  std::unique_ptr<Iterator> m_pIterator;
+  RefreshState m_Refresh;
   CFX_PointF m_ptCaret;
-  CPWL_EditImpl_Undo m_Undo;
-  int32_t m_nAlignment;
-  bool m_bNotifyFlag;
-  bool m_bEnableOverflow;
-  bool m_bEnableRefresh;
+  UndoStack m_Undo;
   CFX_FloatRect m_rcOldContent;
-  bool m_bEnableUndo;
-};
-
-class CPWL_EditImpl_Iterator {
- public:
-  CPWL_EditImpl_Iterator(CPWL_EditImpl* pEdit,
-                         CPDF_VariableText::Iterator* pVTIterator);
-  ~CPWL_EditImpl_Iterator();
-
-  bool NextWord();
-  bool PrevWord();
-  bool GetWord(CPVT_Word& word) const;
-  bool GetLine(CPVT_Line& line) const;
-  void SetAt(int32_t nWordIndex);
-  void SetAt(const CPVT_WordPlace& place);
-  const CPVT_WordPlace& GetAt() const;
-
- private:
-  UnownedPtr<CPWL_EditImpl> m_pEdit;
-  CPDF_VariableText::Iterator* m_pVTIterator;
-};
-
-class CPWL_EditImpl_Provider final : public CPDF_VariableText::Provider {
- public:
-  explicit CPWL_EditImpl_Provider(IPVT_FontMap* pFontMap);
-  ~CPWL_EditImpl_Provider() override;
-
-  IPVT_FontMap* GetFontMap() const;
-
-  // CPDF_VariableText::Provider:
-  uint32_t GetCharWidth(int32_t nFontIndex, uint16_t word) override;
-  int32_t GetTypeAscent(int32_t nFontIndex) override;
-  int32_t GetTypeDescent(int32_t nFontIndex) override;
-  int32_t GetWordFontIndex(uint16_t word,
-                           int32_t charset,
-                           int32_t nFontIndex) override;
-  int32_t GetDefaultFontIndex() override;
-  bool IsLatinWord(uint16_t word) override;
-
- private:
-  IPVT_FontMap* m_pFontMap;
 };
 
 #endif  // FPDFSDK_PWL_CPWL_EDIT_IMPL_H_
diff --git a/fpdfsdk/pwl/cpwl_icon.cpp b/fpdfsdk/pwl/cpwl_icon.cpp
deleted file mode 100644
index 67df09e..0000000
--- a/fpdfsdk/pwl/cpwl_icon.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/pwl/cpwl_icon.h"
-
-#include <algorithm>
-#include <sstream>
-#include <utility>
-
-#include "core/fpdfdoc/cpdf_icon.h"
-#include "core/fpdfdoc/cpdf_iconfit.h"
-#include "fpdfsdk/pwl/cpwl_wnd.h"
-
-CPWL_Icon::CPWL_Icon(const CreateParams& cp,
-                     std::unique_ptr<CPDF_Icon> pIcon,
-                     CPDF_IconFit* pFit)
-    : CPWL_Wnd(cp, nullptr), m_pIcon(std::move(pIcon)), m_pIconFit(pFit) {
-  ASSERT(m_pIcon);
-}
-
-CPWL_Icon::~CPWL_Icon() = default;
-
-CFX_SizeF CPWL_Icon::GetImageSize() {
-  return m_pIcon->GetImageSize();
-}
-
-CFX_Matrix CPWL_Icon::GetImageMatrix() {
-  return m_pIcon->GetImageMatrix();
-}
-
-ByteString CPWL_Icon::GetImageAlias() {
-  return m_pIcon->GetImageAlias();
-}
-
-CFX_PointF CPWL_Icon::GetIconPosition() {
-  if (!m_pIconFit)
-    return CFX_PointF();
-
-  return m_pIconFit->GetIconPosition();
-}
-
-std::pair<float, float> CPWL_Icon::GetScale() {
-  float fHScale = 1.0f;
-  float fVScale = 1.0f;
-
-  CFX_FloatRect rcPlate = GetClientRect();
-  float fPlateWidth = rcPlate.Width();
-  float fPlateHeight = rcPlate.Height();
-
-  CFX_SizeF image_size = GetImageSize();
-  float fImageWidth = image_size.width;
-  float fImageHeight = image_size.height;
-  int32_t nScaleMethod = m_pIconFit ? m_pIconFit->GetScaleMethod() : 0;
-
-  switch (nScaleMethod) {
-    default:
-    case 0:
-      fHScale = fPlateWidth / std::max(fImageWidth, 1.0f);
-      fVScale = fPlateHeight / std::max(fImageHeight, 1.0f);
-      break;
-    case 1:
-      if (fPlateWidth < fImageWidth)
-        fHScale = fPlateWidth / std::max(fImageWidth, 1.0f);
-      if (fPlateHeight < fImageHeight)
-        fVScale = fPlateHeight / std::max(fImageHeight, 1.0f);
-      break;
-    case 2:
-      if (fPlateWidth > fImageWidth)
-        fHScale = fPlateWidth / std::max(fImageWidth, 1.0f);
-      if (fPlateHeight > fImageHeight)
-        fVScale = fPlateHeight / std::max(fImageHeight, 1.0f);
-      break;
-    case 3:
-      break;
-  }
-
-  float fMinScale;
-  if (m_pIconFit && m_pIconFit->IsProportionalScale()) {
-    fMinScale = std::min(fHScale, fVScale);
-    fHScale = fMinScale;
-    fVScale = fMinScale;
-  }
-  return {fHScale, fVScale};
-}
-
-std::pair<float, float> CPWL_Icon::GetImageOffset() {
-  CFX_PointF icon_position = GetIconPosition();
-  float fLeft = icon_position.x;
-  float fBottom = icon_position.y;
-
-  CFX_SizeF image_size = GetImageSize();
-  float fImageWidth = image_size.width;
-  float fImageHeight = image_size.height;
-
-  float fHScale, fVScale;
-  std::tie(fHScale, fVScale) = GetScale();
-
-  float fImageFactWidth = fImageWidth * fHScale;
-  float fImageFactHeight = fImageHeight * fVScale;
-
-  CFX_FloatRect rcPlate = GetClientRect();
-  float fPlateWidth = rcPlate.Width();
-  float fPlateHeight = rcPlate.Height();
-
-  return {(fPlateWidth - fImageFactWidth) * fLeft,
-          (fPlateHeight - fImageFactHeight) * fBottom};
-}
diff --git a/fpdfsdk/pwl/cpwl_icon.h b/fpdfsdk/pwl/cpwl_icon.h
deleted file mode 100644
index aa9b3d7..0000000
--- a/fpdfsdk/pwl/cpwl_icon.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_PWL_CPWL_ICON_H_
-#define FPDFSDK_PWL_CPWL_ICON_H_
-
-#include <memory>
-#include <utility>
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "fpdfsdk/pwl/cpwl_wnd.h"
-
-class CPDF_Icon;
-class CPDF_IconFit;
-
-class CPWL_Icon final : public CPWL_Wnd {
- public:
-  CPWL_Icon(const CreateParams& cp,
-            std::unique_ptr<CPDF_Icon> pIcon,
-            CPDF_IconFit* pFit);
-  ~CPWL_Icon() override;
-
-  // horizontal scale, vertical scale
-  std::pair<float, float> GetScale();
-
-  // x, y
-  std::pair<float, float> GetImageOffset();
-
-  CFX_Matrix GetImageMatrix();
-  ByteString GetImageAlias();
-
- private:
-  CFX_PointF GetIconPosition();  // left, bottom coordinates.
-  CFX_SizeF GetImageSize();
-
-  std::unique_ptr<CPDF_Icon> const m_pIcon;
-  UnownedPtr<CPDF_IconFit> const m_pIconFit;
-};
-
-#endif  // FPDFSDK_PWL_CPWL_ICON_H_
diff --git a/fpdfsdk/pwl/cpwl_list_box.cpp b/fpdfsdk/pwl/cpwl_list_box.cpp
index a0e2039..2203ca9 100644
--- a/fpdfsdk/pwl/cpwl_list_box.cpp
+++ b/fpdfsdk/pwl/cpwl_list_box.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,76 +11,27 @@
 
 #include "core/fxge/cfx_renderdevice.h"
 #include "fpdfsdk/pwl/cpwl_edit.h"
-#include "fpdfsdk/pwl/cpwl_edit_ctrl.h"
 #include "fpdfsdk/pwl/cpwl_edit_impl.h"
-#include "fpdfsdk/pwl/cpwl_list_impl.h"
 #include "fpdfsdk/pwl/cpwl_scroll_bar.h"
-#include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "fpdfsdk/pwl/ipwl_fillernotify.h"
 #include "public/fpdf_fwlevent.h"
-#include "third_party/base/ptr_util.h"
-
-CPWL_List_Notify::CPWL_List_Notify(CPWL_ListBox* pList) : m_pList(pList) {
-  ASSERT(m_pList);
-}
-
-CPWL_List_Notify::~CPWL_List_Notify() {}
-
-void CPWL_List_Notify::IOnSetScrollInfoY(float fPlateMin,
-                                         float fPlateMax,
-                                         float fContentMin,
-                                         float fContentMax,
-                                         float fSmallStep,
-                                         float fBigStep) {
-  PWL_SCROLL_INFO Info;
-  Info.fPlateWidth = fPlateMax - fPlateMin;
-  Info.fContentMin = fContentMin;
-  Info.fContentMax = fContentMax;
-  Info.fSmallStep = fSmallStep;
-  Info.fBigStep = fBigStep;
-  m_pList->SetScrollInfo(Info);
-
-  CPWL_ScrollBar* pScroll = m_pList->GetVScrollBar();
-  if (!pScroll)
-    return;
-
-  if (IsFloatBigger(Info.fPlateWidth, Info.fContentMax - Info.fContentMin) ||
-      IsFloatEqual(Info.fPlateWidth, Info.fContentMax - Info.fContentMin)) {
-    if (pScroll->IsVisible()) {
-      pScroll->SetVisible(false);
-      m_pList->RePosChildWnd();
-    }
-  } else {
-    if (!pScroll->IsVisible()) {
-      pScroll->SetVisible(true);
-      m_pList->RePosChildWnd();
-    }
-  }
-}
-
-void CPWL_List_Notify::IOnSetScrollPosY(float fy) {
-  m_pList->SetScrollPosition(fy);
-}
-
-void CPWL_List_Notify::IOnInvalidateRect(CFX_FloatRect* pRect) {
-  m_pList->InvalidateRect(pRect);
-}
+#include "third_party/base/numerics/safe_conversions.h"
 
 CPWL_ListBox::CPWL_ListBox(
     const CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData)
     : CPWL_Wnd(cp, std::move(pAttachedData)),
-      m_pList(pdfium::MakeUnique<CPWL_ListCtrl>()) {}
+      m_pListCtrl(std::make_unique<CPWL_ListCtrl>()) {}
 
 CPWL_ListBox::~CPWL_ListBox() = default;
 
 void CPWL_ListBox::OnCreated() {
-  m_pList->SetFontMap(GetFontMap());
-  m_pListNotify = pdfium::MakeUnique<CPWL_List_Notify>(this);
-  m_pList->SetNotify(m_pListNotify.get());
+  m_pListCtrl->SetFontMap(GetFontMap());
+  m_pListCtrl->SetNotify(this);
 
   SetHoverSel(HasFlag(PLBS_HOVERSEL));
-  m_pList->SetMultipleSel(HasFlag(PLBS_MULTIPLESEL));
-  m_pList->SetFontSize(GetCreationParams()->fFontSize);
+  m_pListCtrl->SetMultipleSel(HasFlag(PLBS_MULTIPLESEL));
+  m_pListCtrl->SetFontSize(GetCreationParams()->fFontSize);
 
   m_bHoverSel = HasFlag(PLBS_HOVERSEL);
 }
@@ -88,57 +39,55 @@
 void CPWL_ListBox::OnDestroy() {
   // Make sure the notifier is removed from the list as we are about to
   // destroy the notifier and don't want to leave a dangling pointer.
-  m_pList->SetNotify(nullptr);
-  m_pListNotify.reset();
+  m_pListCtrl->SetNotify(nullptr);
 }
 
 void CPWL_ListBox::DrawThisAppearance(CFX_RenderDevice* pDevice,
                                       const CFX_Matrix& mtUser2Device) {
   CPWL_Wnd::DrawThisAppearance(pDevice, mtUser2Device);
 
-  CFX_FloatRect rcPlate = m_pList->GetPlateRect();
+  CFX_FloatRect rcPlate = m_pListCtrl->GetPlateRect();
   CFX_FloatRect rcList = GetListRect();
   CFX_FloatRect rcClient = GetClientRect();
 
-  for (int32_t i = 0, sz = m_pList->GetCount(); i < sz; i++) {
-    CFX_FloatRect rcItem = m_pList->GetItemRect(i);
+  for (int32_t i = 0, sz = m_pListCtrl->GetCount(); i < sz; i++) {
+    CFX_FloatRect rcItem = m_pListCtrl->GetItemRect(i);
     if (rcItem.bottom > rcPlate.top || rcItem.top < rcPlate.bottom)
       continue;
 
     CFX_PointF ptOffset(rcItem.left, (rcItem.top + rcItem.bottom) * 0.5f);
-    if (CPWL_EditImpl* pEdit = m_pList->GetItemEdit(i)) {
+    if (CPWL_EditImpl* pEdit = m_pListCtrl->GetItemEdit(i)) {
       CFX_FloatRect rcContent = pEdit->GetContentRect();
       rcItem.Intersect(rcContent.Width() > rcClient.Width() ? rcList
                                                             : rcClient);
     }
 
-    IPWL_SystemHandler* pSysHandler = GetSystemHandler();
-    if (m_pList->IsItemSelected(i)) {
+    IPWL_FillerNotify* pSysHandler = GetFillerNotify();
+    if (m_pListCtrl->IsItemSelected(i)) {
       if (pSysHandler->IsSelectionImplemented()) {
-        CPWL_EditImpl::DrawEdit(pDevice, mtUser2Device, m_pList->GetItemEdit(i),
-                                GetTextColor().ToFXColor(255), rcList, ptOffset,
-                                nullptr, pSysHandler, m_pFormFiller.Get());
-        pSysHandler->OutputSelectedRect(m_pFormFiller.Get(), rcItem);
+        m_pListCtrl->GetItemEdit(i)->DrawEdit(
+            pDevice, mtUser2Device, GetTextColor().ToFXColor(255), rcList,
+            ptOffset, nullptr, pSysHandler, GetAttachedData());
+        pSysHandler->OutputSelectedRect(GetAttachedData(), rcItem);
       } else {
         pDevice->DrawFillRect(&mtUser2Device, rcItem,
                               ArgbEncode(255, 0, 51, 113));
-        CPWL_EditImpl::DrawEdit(pDevice, mtUser2Device, m_pList->GetItemEdit(i),
-                                ArgbEncode(255, 255, 255, 255), rcList,
-                                ptOffset, nullptr, pSysHandler,
-                                m_pFormFiller.Get());
+        m_pListCtrl->GetItemEdit(i)->DrawEdit(
+            pDevice, mtUser2Device, ArgbEncode(255, 255, 255, 255), rcList,
+            ptOffset, nullptr, pSysHandler, GetAttachedData());
       }
     } else {
-      CPWL_EditImpl::DrawEdit(pDevice, mtUser2Device, m_pList->GetItemEdit(i),
-                              GetTextColor().ToFXColor(255), rcList, ptOffset,
-                              nullptr, pSysHandler, nullptr);
+      m_pListCtrl->GetItemEdit(i)->DrawEdit(
+          pDevice, mtUser2Device, GetTextColor().ToFXColor(255), rcList,
+          ptOffset, nullptr, pSysHandler, nullptr);
     }
   }
 }
 
-bool CPWL_ListBox::OnKeyDown(uint16_t nChar, uint32_t nFlag) {
-  CPWL_Wnd::OnKeyDown(nChar, nFlag);
+bool CPWL_ListBox::OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlag) {
+  CPWL_Wnd::OnKeyDown(nKeyCode, nFlag);
 
-  switch (nChar) {
+  switch (nKeyCode) {
     default:
       return false;
     case FWL_VKEY_Up:
@@ -150,58 +99,61 @@
       break;
   }
 
-  switch (nChar) {
+  switch (nKeyCode) {
     case FWL_VKEY_Up:
-      m_pList->OnVK_UP(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
+      m_pListCtrl->OnVK_UP(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
       break;
     case FWL_VKEY_Down:
-      m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
+      m_pListCtrl->OnVK_DOWN(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
       break;
     case FWL_VKEY_Home:
-      m_pList->OnVK_HOME(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
+      m_pListCtrl->OnVK_HOME(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
       break;
     case FWL_VKEY_Left:
-      m_pList->OnVK_LEFT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
+      m_pListCtrl->OnVK_LEFT(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
       break;
     case FWL_VKEY_End:
-      m_pList->OnVK_END(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
+      m_pListCtrl->OnVK_END(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
       break;
     case FWL_VKEY_Right:
-      m_pList->OnVK_RIGHT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
+      m_pListCtrl->OnVK_RIGHT(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
       break;
-    case FWL_VKEY_Delete:
+    default:
       break;
   }
   OnNotifySelectionChanged(true, nFlag);
   return true;
 }
 
-bool CPWL_ListBox::OnChar(uint16_t nChar, uint32_t nFlag) {
+bool CPWL_ListBox::OnChar(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) {
   CPWL_Wnd::OnChar(nChar, nFlag);
 
-  if (!m_pList->OnChar(nChar, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)))
+  if (!m_pListCtrl->OnChar(nChar, IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)))
     return false;
 
   OnNotifySelectionChanged(true, nFlag);
   return true;
 }
 
-bool CPWL_ListBox::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnLButtonDown(point, nFlag);
+bool CPWL_ListBox::OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,
+                                 const CFX_PointF& point) {
+  CPWL_Wnd::OnLButtonDown(nFlag, point);
 
   if (ClientHitTest(point)) {
     m_bMouseDown = true;
     SetFocus();
     SetCapture();
 
-    m_pList->OnMouseDown(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
+    m_pListCtrl->OnMouseDown(point, IsSHIFTKeyDown(nFlag),
+                             IsCTRLKeyDown(nFlag));
   }
 
   return true;
 }
 
-bool CPWL_ListBox::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnLButtonUp(point, nFlag);
+bool CPWL_ListBox::OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag,
+                               const CFX_PointF& point) {
+  CPWL_Wnd::OnLButtonUp(nFlag, point);
 
   if (m_bMouseDown) {
     ReleaseCapture();
@@ -215,13 +167,15 @@
   m_bHoverSel = bHoverSel;
 }
 
-bool CPWL_ListBox::OnMouseMove(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnMouseMove(point, nFlag);
+bool CPWL_ListBox::OnMouseMove(Mask<FWL_EVENTFLAG> nFlag,
+                               const CFX_PointF& point) {
+  CPWL_Wnd::OnMouseMove(nFlag, point);
 
   if (m_bHoverSel && !IsCaptureMouse() && ClientHitTest(point))
-    m_pList->Select(m_pList->GetItemIndex(point));
+    m_pListCtrl->Select(m_pListCtrl->GetItemIndex(point));
   if (m_bMouseDown)
-    m_pList->OnMouseMove(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
+    m_pListCtrl->OnMouseMove(point, IsSHIFTKeyDown(nFlag),
+                             IsCTRLKeyDown(nFlag));
 
   return true;
 }
@@ -237,42 +191,42 @@
 }
 
 void CPWL_ListBox::ScrollWindowVertically(float pos) {
-  m_pList->SetScrollPos(CFX_PointF(0, pos));
+  m_pListCtrl->SetScrollPos(CFX_PointF(0, pos));
 }
 
-bool CPWL_ListBox::RePosChildWnd() {
-  if (!CPWL_Wnd::RePosChildWnd())
+bool CPWL_ListBox::RepositionChildWnd() {
+  if (!CPWL_Wnd::RepositionChildWnd()) {
     return false;
+  }
 
-  m_pList->SetPlateRect(GetListRect());
+  m_pListCtrl->SetPlateRect(GetListRect());
   return true;
 }
 
-bool CPWL_ListBox::OnNotifySelectionChanged(bool bKeyDown, uint32_t nFlag) {
-  if (!m_pFillerNotify)
-    return false;
-
-  ObservedPtr<CPWL_Wnd> thisObserved(this);
+bool CPWL_ListBox::OnNotifySelectionChanged(bool bKeyDown,
+                                            Mask<FWL_EVENTFLAG> nFlag) {
+  ObservedPtr<CPWL_Wnd> this_observed(this);
 
   WideString swChange = GetText();
   WideString strChangeEx;
   int nSelStart = 0;
-  int nSelEnd = swChange.GetLength();
+  int nSelEnd = pdfium::base::checked_cast<int>(swChange.GetLength());
   bool bRC;
   bool bExit;
-  std::tie(bRC, bExit) = m_pFillerNotify->OnBeforeKeyStroke(
+  std::tie(bRC, bExit) = GetFillerNotify()->OnBeforeKeyStroke(
       GetAttachedData(), swChange, strChangeEx, nSelStart, nSelEnd, bKeyDown,
       nFlag);
 
-  if (!thisObserved)
+  if (!this_observed) {
     return false;
+  }
 
   return bExit;
 }
 
 CFX_FloatRect CPWL_ListBox::GetFocusRect() const {
-  if (m_pList->IsMultipleSel()) {
-    CFX_FloatRect rcCaret = m_pList->GetItemRect(m_pList->GetCaret());
+  if (m_pListCtrl->IsMultipleSel()) {
+    CFX_FloatRect rcCaret = m_pListCtrl->GetItemRect(m_pListCtrl->GetCaret());
     rcCaret.Intersect(GetClientRect());
     return rcCaret;
   }
@@ -281,84 +235,112 @@
 }
 
 void CPWL_ListBox::AddString(const WideString& str) {
-  m_pList->AddString(str);
+  m_pListCtrl->AddString(str);
 }
 
 WideString CPWL_ListBox::GetText() {
-  return m_pList->GetText();
+  return m_pListCtrl->GetText();
 }
 
 void CPWL_ListBox::SetFontSize(float fFontSize) {
-  m_pList->SetFontSize(fFontSize);
+  m_pListCtrl->SetFontSize(fFontSize);
 }
 
 float CPWL_ListBox::GetFontSize() const {
-  return m_pList->GetFontSize();
+  return m_pListCtrl->GetFontSize();
+}
+
+void CPWL_ListBox::OnSetScrollInfoY(float fPlateMin,
+                                    float fPlateMax,
+                                    float fContentMin,
+                                    float fContentMax,
+                                    float fSmallStep,
+                                    float fBigStep) {
+  PWL_SCROLL_INFO Info;
+  Info.fPlateWidth = fPlateMax - fPlateMin;
+  Info.fContentMin = fContentMin;
+  Info.fContentMax = fContentMax;
+  Info.fSmallStep = fSmallStep;
+  Info.fBigStep = fBigStep;
+  SetScrollInfo(Info);
+
+  CPWL_ScrollBar* pScroll = GetVScrollBar();
+  if (!pScroll)
+    return;
+
+  if (FXSYS_IsFloatBigger(Info.fPlateWidth,
+                          Info.fContentMax - Info.fContentMin) ||
+      FXSYS_IsFloatEqual(Info.fPlateWidth,
+                         Info.fContentMax - Info.fContentMin)) {
+    if (pScroll->IsVisible() && pScroll->SetVisible(false)) {
+      RepositionChildWnd();
+    }
+    return;
+  }
+  if (!pScroll->IsVisible() && pScroll->SetVisible(true)) {
+    RepositionChildWnd();
+  }
+}
+
+void CPWL_ListBox::OnSetScrollPosY(float fy) {
+  SetScrollPosition(fy);
+}
+
+bool CPWL_ListBox::OnInvalidateRect(const CFX_FloatRect& rect) {
+  return InvalidateRect(&rect);
 }
 
 void CPWL_ListBox::Select(int32_t nItemIndex) {
-  m_pList->Select(nItemIndex);
+  m_pListCtrl->Select(nItemIndex);
 }
 
 void CPWL_ListBox::Deselect(int32_t nItemIndex) {
-  m_pList->Deselect(nItemIndex);
+  m_pListCtrl->Deselect(nItemIndex);
 }
 
 void CPWL_ListBox::SetCaret(int32_t nItemIndex) {
-  m_pList->SetCaret(nItemIndex);
+  m_pListCtrl->SetCaret(nItemIndex);
 }
 
 void CPWL_ListBox::SetTopVisibleIndex(int32_t nItemIndex) {
-  m_pList->SetTopItem(nItemIndex);
+  m_pListCtrl->SetTopItem(nItemIndex);
 }
 
 void CPWL_ListBox::ScrollToListItem(int32_t nItemIndex) {
-  m_pList->ScrollToListItem(nItemIndex);
-}
-
-void CPWL_ListBox::ResetContent() {
-  m_pList->Clear();
-}
-
-void CPWL_ListBox::Reset() {
-  m_pList->Cancel();
+  m_pListCtrl->ScrollToListItem(nItemIndex);
 }
 
 bool CPWL_ListBox::IsMultipleSel() const {
-  return m_pList->IsMultipleSel();
+  return m_pListCtrl->IsMultipleSel();
 }
 
 int32_t CPWL_ListBox::GetCaretIndex() const {
-  return m_pList->GetCaret();
+  return m_pListCtrl->GetCaret();
 }
 
 int32_t CPWL_ListBox::GetCurSel() const {
-  return m_pList->GetSelect();
+  return m_pListCtrl->GetSelect();
 }
 
 bool CPWL_ListBox::IsItemSelected(int32_t nItemIndex) const {
-  return m_pList->IsItemSelected(nItemIndex);
+  return m_pListCtrl->IsItemSelected(nItemIndex);
 }
 
 int32_t CPWL_ListBox::GetTopVisibleIndex() const {
-  m_pList->ScrollToListItem(m_pList->GetFirstSelected());
-  return m_pList->GetTopItem();
+  m_pListCtrl->ScrollToListItem(m_pListCtrl->GetFirstSelected());
+  return m_pListCtrl->GetTopItem();
 }
 
 int32_t CPWL_ListBox::GetCount() const {
-  return m_pList->GetCount();
-}
-
-int32_t CPWL_ListBox::FindNext(int32_t nIndex, wchar_t nChar) const {
-  return m_pList->FindNext(nIndex, nChar);
+  return m_pListCtrl->GetCount();
 }
 
 CFX_FloatRect CPWL_ListBox::GetContentRect() const {
-  return m_pList->GetContentRect();
+  return m_pListCtrl->GetContentRect();
 }
 
 float CPWL_ListBox::GetFirstHeight() const {
-  return m_pList->GetFirstHeight();
+  return m_pListCtrl->GetFirstHeight();
 }
 
 CFX_FloatRect CPWL_ListBox::GetListRect() const {
@@ -366,13 +348,13 @@
   return GetWindowRect().GetDeflated(width, width);
 }
 
-bool CPWL_ListBox::OnMouseWheel(short zDelta,
+bool CPWL_ListBox::OnMouseWheel(Mask<FWL_EVENTFLAG> nFlag,
                                 const CFX_PointF& point,
-                                uint32_t nFlag) {
-  if (zDelta < 0)
-    m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
+                                const CFX_Vector& delta) {
+  if (delta.y < 0)
+    m_pListCtrl->OnVK_DOWN(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
   else
-    m_pList->OnVK_UP(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
+    m_pListCtrl->OnVK_UP(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
 
   OnNotifySelectionChanged(false, nFlag);
   return true;
diff --git a/fpdfsdk/pwl/cpwl_list_box.h b/fpdfsdk/pwl/cpwl_list_box.h
index ba3a653..85b0077 100644
--- a/fpdfsdk/pwl/cpwl_list_box.h
+++ b/fpdfsdk/pwl/cpwl_list_box.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,68 +10,56 @@
 #include <memory>
 
 #include "core/fxcrt/unowned_ptr.h"
+#include "fpdfsdk/pwl/cpwl_list_ctrl.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 
-class CPWL_ListCtrl;
-class CPWL_List_Notify;
-class CPWL_ListBox;
-class IPWL_Filler_Notify;
-struct CPVT_WordPlace;
+class IPWL_FillerNotify;
 
-class CPWL_List_Notify {
+class CPWL_ListBox : public CPWL_Wnd, public CPWL_ListCtrl::NotifyIface {
  public:
-  explicit CPWL_List_Notify(CPWL_ListBox* pList);
-  ~CPWL_List_Notify();
-
-  void IOnSetScrollInfoY(float fPlateMin,
-                         float fPlateMax,
-                         float fContentMin,
-                         float fContentMax,
-                         float fSmallStep,
-                         float fBigStep);
-  void IOnSetScrollPosY(float fy);
-  void IOnInvalidateRect(CFX_FloatRect* pRect);
-
- private:
-  UnownedPtr<CPWL_ListBox> m_pList;
-};
-
-class CPWL_ListBox : public CPWL_Wnd {
- public:
-  CPWL_ListBox(
-      const CreateParams& cp,
-      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
+  CPWL_ListBox(const CreateParams& cp,
+               std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData);
   ~CPWL_ListBox() override;
 
-  // CPWL_Wnd
+  // CPWL_Wnd:
   void OnCreated() override;
   void OnDestroy() override;
   void DrawThisAppearance(CFX_RenderDevice* pDevice,
                           const CFX_Matrix& mtUser2Device) override;
-  bool OnKeyDown(uint16_t nChar, uint32_t nFlag) override;
-  bool OnChar(uint16_t nChar, uint32_t nFlag) override;
-  bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override;
-  bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override;
-  bool OnMouseMove(const CFX_PointF& point, uint32_t nFlag) override;
-  bool OnMouseWheel(short zDelta,
+  bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlag) override;
+  bool OnChar(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) override;
+  bool OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,
+                     const CFX_PointF& point) override;
+  bool OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag, const CFX_PointF& point) override;
+  bool OnMouseMove(Mask<FWL_EVENTFLAG> nFlag, const CFX_PointF& point) override;
+  bool OnMouseWheel(Mask<FWL_EVENTFLAG> nFlag,
                     const CFX_PointF& point,
-                    uint32_t nFlag) override;
+                    const CFX_Vector& delta) override;
   WideString GetText() override;
   void SetScrollInfo(const PWL_SCROLL_INFO& info) override;
   void SetScrollPosition(float pos) override;
   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;
 
-  bool OnNotifySelectionChanged(bool bKeyDown, uint32_t nFlag);
+  // CPWL_ListCtrl::NotifyIface:
+  void OnSetScrollInfoY(float fPlateMin,
+                        float fPlateMax,
+                        float fContentMin,
+                        float fContentMax,
+                        float fSmallStep,
+                        float fBigStep) override;
+  void OnSetScrollPosY(float fy) override;
+  [[nodiscard]] bool OnInvalidateRect(const CFX_FloatRect& pRect) override;
+
+  bool OnNotifySelectionChanged(bool bKeyDown, Mask<FWL_EVENTFLAG> nFlag);
 
   void AddString(const WideString& str);
   void SetTopVisibleIndex(int32_t nItemIndex);
   void ScrollToListItem(int32_t nItemIndex);
-  void ResetContent();
-  void Reset();
+
   void Select(int32_t nItemIndex);
   void Deselect(int32_t nItemIndex);
   void SetCaret(int32_t nItemIndex);
@@ -83,26 +71,14 @@
   int32_t GetCurSel() const;
   bool IsItemSelected(int32_t nItemIndex) const;
   int32_t GetTopVisibleIndex() const;
-  int32_t FindNext(int32_t nIndex, wchar_t nChar) const;
   CFX_FloatRect GetContentRect() const;
   float GetFirstHeight() const;
   CFX_FloatRect GetListRect() const;
 
-  void SetFillerNotify(IPWL_Filler_Notify* pNotify) {
-    m_pFillerNotify = pNotify;
-  }
-
-  void AttachFFLData(CFFL_FormFiller* pData) { m_pFormFiller = pData; }
-
  protected:
   bool m_bMouseDown = false;
   bool m_bHoverSel = false;
-  std::unique_ptr<CPWL_ListCtrl> m_pList;
-  std::unique_ptr<CPWL_List_Notify> m_pListNotify;
-  UnownedPtr<IPWL_Filler_Notify> m_pFillerNotify;
-
- private:
-  UnownedPtr<CFFL_FormFiller> m_pFormFiller;
+  std::unique_ptr<CPWL_ListCtrl> m_pListCtrl;
 };
 
 #endif  // FPDFSDK_PWL_CPWL_LIST_BOX_H_
diff --git a/fpdfsdk/pwl/cpwl_list_ctrl.cpp b/fpdfsdk/pwl/cpwl_list_ctrl.cpp
new file mode 100644
index 0000000..d7e3b15
--- /dev/null
+++ b/fpdfsdk/pwl/cpwl_list_ctrl.cpp
@@ -0,0 +1,632 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fpdfsdk/pwl/cpwl_list_ctrl.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "core/fpdfdoc/cpvt_word.h"
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/stl_util.h"
+#include "fpdfsdk/pwl/cpwl_edit_impl.h"
+#include "fpdfsdk/pwl/cpwl_list_box.h"
+#include "third_party/base/numerics/safe_conversions.h"
+
+CPWL_ListCtrl::NotifyIface::~NotifyIface() = default;
+
+CPWL_ListCtrl::Item::Item() : m_pEdit(std::make_unique<CPWL_EditImpl>()) {
+  m_pEdit->SetAlignmentV(1);
+  m_pEdit->Initialize();
+}
+
+CPWL_ListCtrl::Item::~Item() = default;
+
+void CPWL_ListCtrl::Item::SetFontMap(IPVT_FontMap* pFontMap) {
+  m_pEdit->SetFontMap(pFontMap);
+}
+
+void CPWL_ListCtrl::Item::SetText(const WideString& text) {
+  m_pEdit->SetText(text);
+  m_pEdit->Paint();
+}
+
+void CPWL_ListCtrl::Item::SetFontSize(float fFontSize) {
+  m_pEdit->SetFontSize(fFontSize);
+  m_pEdit->Paint();
+}
+
+float CPWL_ListCtrl::Item::GetItemHeight() const {
+  return m_pEdit->GetContentRect().Height();
+}
+
+uint16_t CPWL_ListCtrl::Item::GetFirstChar() const {
+  CPVT_Word word;
+  CPWL_EditImpl::Iterator* pIterator = m_pEdit->GetIterator();
+  pIterator->SetAt(1);
+  pIterator->GetWord(word);
+  return word.Word;
+}
+
+WideString CPWL_ListCtrl::Item::GetText() const {
+  return m_pEdit->GetText();
+}
+
+CPWL_ListCtrl::SelectState::SelectState() = default;
+
+CPWL_ListCtrl::SelectState::~SelectState() = default;
+
+void CPWL_ListCtrl::SelectState::Add(int32_t nItemIndex) {
+  m_Items[nItemIndex] = SELECTING;
+}
+
+void CPWL_ListCtrl::SelectState::Add(int32_t nBeginIndex, int32_t nEndIndex) {
+  if (nBeginIndex > nEndIndex)
+    std::swap(nBeginIndex, nEndIndex);
+
+  for (int32_t i = nBeginIndex; i <= nEndIndex; ++i)
+    Add(i);
+}
+
+void CPWL_ListCtrl::SelectState::Sub(int32_t nItemIndex) {
+  auto it = m_Items.find(nItemIndex);
+  if (it != m_Items.end())
+    it->second = DESELECTING;
+}
+
+void CPWL_ListCtrl::SelectState::Sub(int32_t nBeginIndex, int32_t nEndIndex) {
+  if (nBeginIndex > nEndIndex)
+    std::swap(nBeginIndex, nEndIndex);
+
+  for (int32_t i = nBeginIndex; i <= nEndIndex; ++i)
+    Sub(i);
+}
+
+void CPWL_ListCtrl::SelectState::DeselectAll() {
+  for (auto& item : m_Items)
+    item.second = DESELECTING;
+}
+
+void CPWL_ListCtrl::SelectState::Done() {
+  auto it = m_Items.begin();
+  while (it != m_Items.end()) {
+    if (it->second == DESELECTING)
+      it = m_Items.erase(it);
+    else
+      (it++)->second = NORMAL;
+  }
+}
+
+CPWL_ListCtrl::CPWL_ListCtrl() = default;
+
+CPWL_ListCtrl::~CPWL_ListCtrl() {
+  m_ListItems.clear();
+  InvalidateItem(-1);
+}
+
+CFX_PointF CPWL_ListCtrl::InToOut(const CFX_PointF& point) const {
+  CFX_FloatRect rcPlate = m_rcPlate;
+  return CFX_PointF(point.x - (m_ptScrollPos.x - rcPlate.left),
+                    point.y - (m_ptScrollPos.y - rcPlate.top));
+}
+
+CFX_PointF CPWL_ListCtrl::OutToIn(const CFX_PointF& point) const {
+  CFX_FloatRect rcPlate = m_rcPlate;
+  return CFX_PointF(point.x + (m_ptScrollPos.x - rcPlate.left),
+                    point.y + (m_ptScrollPos.y - rcPlate.top));
+}
+
+CFX_FloatRect CPWL_ListCtrl::InToOut(const CFX_FloatRect& rect) const {
+  CFX_PointF ptLeftBottom = InToOut(CFX_PointF(rect.left, rect.bottom));
+  CFX_PointF ptRightTop = InToOut(CFX_PointF(rect.right, rect.top));
+  return CFX_FloatRect(ptLeftBottom.x, ptLeftBottom.y, ptRightTop.x,
+                       ptRightTop.y);
+}
+
+CFX_FloatRect CPWL_ListCtrl::OutToIn(const CFX_FloatRect& rect) const {
+  CFX_PointF ptLeftBottom = OutToIn(CFX_PointF(rect.left, rect.bottom));
+  CFX_PointF ptRightTop = OutToIn(CFX_PointF(rect.right, rect.top));
+  return CFX_FloatRect(ptLeftBottom.x, ptLeftBottom.y, ptRightTop.x,
+                       ptRightTop.y);
+}
+
+CFX_PointF CPWL_ListCtrl::InnerToOuter(const CFX_PointF& point) const {
+  return CFX_PointF(point.x + GetBTPoint().x, GetBTPoint().y - point.y);
+}
+
+CFX_PointF CPWL_ListCtrl::OuterToInner(const CFX_PointF& point) const {
+  return CFX_PointF(point.x - GetBTPoint().x, GetBTPoint().y - point.y);
+}
+
+CFX_FloatRect CPWL_ListCtrl::InnerToOuter(const CFX_FloatRect& rect) const {
+  CFX_PointF ptLeftTop = InnerToOuter(CFX_PointF(rect.left, rect.top));
+  CFX_PointF ptRightBottom = InnerToOuter(CFX_PointF(rect.right, rect.bottom));
+  return CFX_FloatRect(ptLeftTop.x, ptRightBottom.y, ptRightBottom.x,
+                       ptLeftTop.y);
+}
+
+CFX_FloatRect CPWL_ListCtrl::OuterToInner(const CFX_FloatRect& rect) const {
+  CFX_PointF ptLeftTop = OuterToInner(CFX_PointF(rect.left, rect.top));
+  CFX_PointF ptRightBottom = OuterToInner(CFX_PointF(rect.right, rect.bottom));
+  return CFX_FloatRect(ptLeftTop.x, ptRightBottom.y, ptRightBottom.x,
+                       ptLeftTop.y);
+}
+
+void CPWL_ListCtrl::OnMouseDown(const CFX_PointF& point,
+                                bool bShift,
+                                bool bCtrl) {
+  int32_t nHitIndex = GetItemIndex(point);
+
+  if (IsMultipleSel()) {
+    if (bCtrl) {
+      if (IsItemSelected(nHitIndex)) {
+        m_SelectState.Sub(nHitIndex);
+        SelectItems();
+        m_bCtrlSel = false;
+      } else {
+        m_SelectState.Add(nHitIndex);
+        SelectItems();
+        m_bCtrlSel = true;
+      }
+
+      m_nFootIndex = nHitIndex;
+    } else if (bShift) {
+      m_SelectState.DeselectAll();
+      m_SelectState.Add(m_nFootIndex, nHitIndex);
+      SelectItems();
+    } else {
+      m_SelectState.DeselectAll();
+      m_SelectState.Add(nHitIndex);
+      SelectItems();
+
+      m_nFootIndex = nHitIndex;
+    }
+
+    SetCaret(nHitIndex);
+  } else {
+    SetSingleSelect(nHitIndex);
+  }
+
+  if (!IsItemVisible(nHitIndex))
+    ScrollToListItem(nHitIndex);
+}
+
+void CPWL_ListCtrl::OnMouseMove(const CFX_PointF& point,
+                                bool bShift,
+                                bool bCtrl) {
+  int32_t nHitIndex = GetItemIndex(point);
+
+  if (IsMultipleSel()) {
+    if (bCtrl) {
+      if (m_bCtrlSel)
+        m_SelectState.Add(m_nFootIndex, nHitIndex);
+      else
+        m_SelectState.Sub(m_nFootIndex, nHitIndex);
+
+      SelectItems();
+    } else {
+      m_SelectState.DeselectAll();
+      m_SelectState.Add(m_nFootIndex, nHitIndex);
+      SelectItems();
+    }
+
+    SetCaret(nHitIndex);
+  } else {
+    SetSingleSelect(nHitIndex);
+  }
+
+  if (!IsItemVisible(nHitIndex))
+    ScrollToListItem(nHitIndex);
+}
+
+void CPWL_ListCtrl::OnVK(int32_t nItemIndex, bool bShift, bool bCtrl) {
+  if (IsMultipleSel()) {
+    if (nItemIndex >= 0 && nItemIndex < GetCount()) {
+      if (bCtrl) {
+      } else if (bShift) {
+        m_SelectState.DeselectAll();
+        m_SelectState.Add(m_nFootIndex, nItemIndex);
+        SelectItems();
+      } else {
+        m_SelectState.DeselectAll();
+        m_SelectState.Add(nItemIndex);
+        SelectItems();
+        m_nFootIndex = nItemIndex;
+      }
+
+      SetCaret(nItemIndex);
+    }
+  } else {
+    SetSingleSelect(nItemIndex);
+  }
+
+  if (!IsItemVisible(nItemIndex))
+    ScrollToListItem(nItemIndex);
+}
+
+void CPWL_ListCtrl::OnVK_UP(bool bShift, bool bCtrl) {
+  OnVK(IsMultipleSel() ? GetCaret() - 1 : GetSelect() - 1, bShift, bCtrl);
+}
+
+void CPWL_ListCtrl::OnVK_DOWN(bool bShift, bool bCtrl) {
+  OnVK(IsMultipleSel() ? GetCaret() + 1 : GetSelect() + 1, bShift, bCtrl);
+}
+
+void CPWL_ListCtrl::OnVK_LEFT(bool bShift, bool bCtrl) {
+  OnVK(0, bShift, bCtrl);
+}
+
+void CPWL_ListCtrl::OnVK_RIGHT(bool bShift, bool bCtrl) {
+  OnVK(GetCount() - 1, bShift, bCtrl);
+}
+
+void CPWL_ListCtrl::OnVK_HOME(bool bShift, bool bCtrl) {
+  OnVK(0, bShift, bCtrl);
+}
+
+void CPWL_ListCtrl::OnVK_END(bool bShift, bool bCtrl) {
+  OnVK(GetCount() - 1, bShift, bCtrl);
+}
+
+bool CPWL_ListCtrl::OnChar(uint16_t nChar, bool bShift, bool bCtrl) {
+  int32_t nIndex = GetLastSelected();
+  int32_t nFindIndex = FindNext(nIndex, nChar);
+
+  if (nFindIndex != nIndex) {
+    OnVK(nFindIndex, bShift, bCtrl);
+    return true;
+  }
+  return false;
+}
+
+void CPWL_ListCtrl::SetPlateRect(const CFX_FloatRect& rect) {
+  m_rcPlate = rect;
+  m_ptScrollPos.x = rect.left;
+  SetScrollPos(CFX_PointF(rect.left, rect.top));
+  ReArrange(0);
+  InvalidateItem(-1);
+}
+
+CFX_FloatRect CPWL_ListCtrl::GetItemRect(int32_t nIndex) const {
+  return InToOut(GetItemRectInternal(nIndex));
+}
+
+CFX_FloatRect CPWL_ListCtrl::GetItemRectInternal(int32_t nIndex) const {
+  if (!IsValid(nIndex))
+    return CFX_FloatRect();
+
+  CFX_FloatRect rcItem = m_ListItems[nIndex]->GetRect();
+  rcItem.left = 0.0f;
+  rcItem.right = m_rcPlate.Width();
+  return InnerToOuter(rcItem);
+}
+
+void CPWL_ListCtrl::AddString(const WideString& str) {
+  AddItem(str);
+  ReArrange(GetCount() - 1);
+}
+
+void CPWL_ListCtrl::SetMultipleSelect(int32_t nItemIndex, bool bSelected) {
+  if (!IsValid(nItemIndex))
+    return;
+
+  if (bSelected != IsItemSelected(nItemIndex)) {
+    if (bSelected) {
+      SetItemSelect(nItemIndex, true);
+      InvalidateItem(nItemIndex);
+    } else {
+      SetItemSelect(nItemIndex, false);
+      InvalidateItem(nItemIndex);
+    }
+  }
+}
+
+void CPWL_ListCtrl::SetSingleSelect(int32_t nItemIndex) {
+  if (!IsValid(nItemIndex))
+    return;
+
+  if (m_nSelItem != nItemIndex) {
+    if (m_nSelItem >= 0) {
+      SetItemSelect(m_nSelItem, false);
+      InvalidateItem(m_nSelItem);
+    }
+
+    SetItemSelect(nItemIndex, true);
+    InvalidateItem(nItemIndex);
+    m_nSelItem = nItemIndex;
+  }
+}
+
+void CPWL_ListCtrl::SetCaret(int32_t nItemIndex) {
+  if (!IsValid(nItemIndex))
+    return;
+
+  if (IsMultipleSel()) {
+    int32_t nOldIndex = m_nCaretIndex;
+
+    if (nOldIndex != nItemIndex) {
+      m_nCaretIndex = nItemIndex;
+      InvalidateItem(nOldIndex);
+      InvalidateItem(nItemIndex);
+    }
+  }
+}
+
+void CPWL_ListCtrl::InvalidateItem(int32_t nItemIndex) {
+  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.
+      }
+      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;
+    }
+  }
+}
+
+void CPWL_ListCtrl::SelectItems() {
+  for (const auto& item : m_SelectState) {
+    if (item.second != SelectState::NORMAL)
+      SetMultipleSelect(item.first, item.second == SelectState::SELECTING);
+  }
+  m_SelectState.Done();
+}
+
+void CPWL_ListCtrl::Select(int32_t nItemIndex) {
+  if (!IsValid(nItemIndex))
+    return;
+
+  if (IsMultipleSel()) {
+    m_SelectState.Add(nItemIndex);
+    SelectItems();
+  } else {
+    SetSingleSelect(nItemIndex);
+  }
+}
+
+void CPWL_ListCtrl::Deselect(int32_t nItemIndex) {
+  if (!IsItemSelected(nItemIndex))
+    return;
+
+  SetMultipleSelect(nItemIndex, false);
+
+  if (!IsMultipleSel())
+    m_nSelItem = -1;
+}
+
+bool CPWL_ListCtrl::IsItemVisible(int32_t nItemIndex) const {
+  CFX_FloatRect rcPlate = m_rcPlate;
+  CFX_FloatRect rcItem = GetItemRect(nItemIndex);
+
+  return rcItem.bottom >= rcPlate.bottom && rcItem.top <= rcPlate.top;
+}
+
+void CPWL_ListCtrl::ScrollToListItem(int32_t nItemIndex) {
+  if (!IsValid(nItemIndex))
+    return;
+
+  CFX_FloatRect rcPlate = m_rcPlate;
+  CFX_FloatRect rcItem = GetItemRectInternal(nItemIndex);
+  CFX_FloatRect rcItemCtrl = GetItemRect(nItemIndex);
+
+  if (FXSYS_IsFloatSmaller(rcItemCtrl.bottom, rcPlate.bottom)) {
+    if (FXSYS_IsFloatSmaller(rcItemCtrl.top, rcPlate.top)) {
+      SetScrollPosY(rcItem.bottom + rcPlate.Height());
+    }
+  } else if (FXSYS_IsFloatBigger(rcItemCtrl.top, rcPlate.top)) {
+    if (FXSYS_IsFloatBigger(rcItemCtrl.bottom, rcPlate.bottom)) {
+      SetScrollPosY(rcItem.top);
+    }
+  }
+}
+
+void CPWL_ListCtrl::SetScrollInfo() {
+  if (m_pNotify) {
+    CFX_FloatRect rcPlate = m_rcPlate;
+    CFX_FloatRect rcContent = GetContentRectInternal();
+
+    if (!m_bNotifyFlag) {
+      m_bNotifyFlag = true;
+      m_pNotify->OnSetScrollInfoY(rcPlate.bottom, rcPlate.top, rcContent.bottom,
+                                  rcContent.top, GetFirstHeight(),
+                                  rcPlate.Height());
+      m_bNotifyFlag = false;
+    }
+  }
+}
+
+void CPWL_ListCtrl::SetScrollPos(const CFX_PointF& point) {
+  SetScrollPosY(point.y);
+}
+
+void CPWL_ListCtrl::SetScrollPosY(float fy) {
+  if (!FXSYS_IsFloatEqual(m_ptScrollPos.y, fy)) {
+    CFX_FloatRect rcPlate = m_rcPlate;
+    CFX_FloatRect rcContent = GetContentRectInternal();
+
+    if (rcPlate.Height() > rcContent.Height()) {
+      fy = rcPlate.top;
+    } else {
+      if (FXSYS_IsFloatSmaller(fy - rcPlate.Height(), rcContent.bottom)) {
+        fy = rcContent.bottom + rcPlate.Height();
+      } else if (FXSYS_IsFloatBigger(fy, rcContent.top)) {
+        fy = rcContent.top;
+      }
+    }
+
+    m_ptScrollPos.y = fy;
+    InvalidateItem(-1);
+
+    if (m_pNotify) {
+      if (!m_bNotifyFlag) {
+        m_bNotifyFlag = true;
+        m_pNotify->OnSetScrollPosY(fy);
+        m_bNotifyFlag = false;
+      }
+    }
+  }
+}
+
+CFX_FloatRect CPWL_ListCtrl::GetContentRectInternal() const {
+  return InnerToOuter(m_rcContent);
+}
+
+CFX_FloatRect CPWL_ListCtrl::GetContentRect() const {
+  return InToOut(GetContentRectInternal());
+}
+
+void CPWL_ListCtrl::ReArrange(int32_t nItemIndex) {
+  float fPosY = 0.0f;
+  if (IsValid(nItemIndex - 1))
+    fPosY = m_ListItems[nItemIndex - 1]->GetRect().bottom;
+
+  for (const auto& pListItem : m_ListItems) {
+    float fListItemHeight = pListItem->GetItemHeight();
+    pListItem->SetRect(
+        CFX_FloatRect(0.0f, fPosY + fListItemHeight, 0.0f, fPosY));
+    fPosY += fListItemHeight;
+  }
+  m_rcContent = CFX_FloatRect(0.0f, fPosY, 0.0f, 0.0f);
+  SetScrollInfo();
+}
+
+void CPWL_ListCtrl::SetTopItem(int32_t nIndex) {
+  if (IsValid(nIndex)) {
+    CFX_FloatRect rcItem = GetItemRectInternal(nIndex);
+    SetScrollPosY(rcItem.top);
+  }
+}
+
+int32_t CPWL_ListCtrl::GetTopItem() const {
+  int32_t nItemIndex = GetItemIndex(GetBTPoint());
+  if (!IsItemVisible(nItemIndex) && IsItemVisible(nItemIndex + 1))
+    nItemIndex += 1;
+
+  return nItemIndex;
+}
+
+int32_t CPWL_ListCtrl::GetItemIndex(const CFX_PointF& point) const {
+  CFX_PointF pt = OuterToInner(OutToIn(point));
+  bool bFirst = true;
+  bool bLast = true;
+  for (const auto& pListItem : m_ListItems) {
+    CFX_FloatRect rcListItem = pListItem->GetRect();
+    if (FXSYS_IsFloatBigger(pt.y, rcListItem.top))
+      bFirst = false;
+    if (FXSYS_IsFloatSmaller(pt.y, rcListItem.bottom))
+      bLast = false;
+    if (pt.y >= rcListItem.top && pt.y < rcListItem.bottom) {
+      return pdfium::base::checked_cast<int32_t>(&pListItem -
+                                                 &m_ListItems.front());
+    }
+  }
+  if (bFirst)
+    return 0;
+  if (bLast)
+    return GetCount() - 1;
+  return -1;
+}
+
+WideString CPWL_ListCtrl::GetText() const {
+  if (IsMultipleSel())
+    return GetItemText(m_nCaretIndex);
+  return GetItemText(m_nSelItem);
+}
+
+void CPWL_ListCtrl::AddItem(const WideString& str) {
+  auto pListItem = std::make_unique<Item>();
+  pListItem->SetFontMap(m_pFontMap);
+  pListItem->SetFontSize(m_fFontSize);
+  pListItem->SetText(str);
+  m_ListItems.push_back(std::move(pListItem));
+}
+
+CPWL_EditImpl* CPWL_ListCtrl::GetItemEdit(int32_t nIndex) const {
+  if (!IsValid(nIndex))
+    return nullptr;
+  return m_ListItems[nIndex]->GetEdit();
+}
+
+int32_t CPWL_ListCtrl::GetCount() const {
+  return fxcrt::CollectionSize<int32_t>(m_ListItems);
+}
+
+float CPWL_ListCtrl::GetFirstHeight() const {
+  if (m_ListItems.empty())
+    return 1.0f;
+  return m_ListItems.front()->GetItemHeight();
+}
+
+int32_t CPWL_ListCtrl::GetFirstSelected() const {
+  int32_t i = 0;
+  for (const auto& pListItem : m_ListItems) {
+    if (pListItem->IsSelected())
+      return i;
+    ++i;
+  }
+  return -1;
+}
+
+int32_t CPWL_ListCtrl::GetLastSelected() const {
+  for (auto iter = m_ListItems.rbegin(); iter != m_ListItems.rend(); ++iter) {
+    if ((*iter)->IsSelected())
+      return pdfium::base::checked_cast<int32_t>(&*iter - &m_ListItems.front());
+  }
+  return -1;
+}
+
+int32_t CPWL_ListCtrl::FindNext(int32_t nIndex, wchar_t nChar) const {
+  int32_t nCircleIndex = nIndex;
+  int32_t sz = GetCount();
+  for (int32_t i = 0; i < sz; i++) {
+    nCircleIndex++;
+    if (nCircleIndex >= sz)
+      nCircleIndex = 0;
+
+    if (Item* pListItem = m_ListItems[nCircleIndex].get()) {
+      if (FXSYS_towupper(pListItem->GetFirstChar()) == FXSYS_towupper(nChar))
+        return nCircleIndex;
+    }
+  }
+
+  return nCircleIndex;
+}
+
+bool CPWL_ListCtrl::IsItemSelected(int32_t nIndex) const {
+  return IsValid(nIndex) && m_ListItems[nIndex]->IsSelected();
+}
+
+void CPWL_ListCtrl::SetItemSelect(int32_t nIndex, bool bSelected) {
+  if (IsValid(nIndex))
+    m_ListItems[nIndex]->SetSelect(bSelected);
+}
+
+bool CPWL_ListCtrl::IsValid(int32_t nItemIndex) const {
+  return fxcrt::IndexInBounds(m_ListItems, nItemIndex);
+}
+
+WideString CPWL_ListCtrl::GetItemText(int32_t nIndex) const {
+  if (IsValid(nIndex))
+    return m_ListItems[nIndex]->GetText();
+  return WideString();
+}
diff --git a/fpdfsdk/pwl/cpwl_list_ctrl.h b/fpdfsdk/pwl/cpwl_list_ctrl.h
new file mode 100644
index 0000000..c278b81
--- /dev/null
+++ b/fpdfsdk/pwl/cpwl_list_ctrl.h
@@ -0,0 +1,186 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_PWL_CPWL_LIST_CTRL_H_
+#define FPDFSDK_PWL_CPWL_LIST_CTRL_H_
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "fpdfsdk/pwl/cpwl_edit_impl.h"
+
+class IPVT_FontMap;
+
+class CPWL_ListCtrl {
+ public:
+  class NotifyIface {
+   public:
+    virtual ~NotifyIface();
+
+    virtual void OnSetScrollInfoY(float fPlateMin,
+                                  float fPlateMax,
+                                  float fContentMin,
+                                  float fContentMax,
+                                  float fSmallStep,
+                                  float fBigStep) = 0;
+    virtual void OnSetScrollPosY(float fy) = 0;
+
+    // Returns true if `this` is still allocated.
+    [[nodiscard]] virtual bool OnInvalidateRect(const CFX_FloatRect& rect) = 0;
+  };
+
+  CPWL_ListCtrl();
+  ~CPWL_ListCtrl();
+
+  void SetNotify(NotifyIface* pNotify) { m_pNotify = pNotify; }
+  void OnMouseDown(const CFX_PointF& point, bool bShift, bool bCtrl);
+  void OnMouseMove(const CFX_PointF& point, bool bShift, bool bCtrl);
+  void OnVK_UP(bool bShift, bool bCtrl);
+  void OnVK_DOWN(bool bShift, bool bCtrl);
+  void OnVK_LEFT(bool bShift, bool bCtrl);
+  void OnVK_RIGHT(bool bShift, bool bCtrl);
+  void OnVK_HOME(bool bShift, bool bCtrl);
+  void OnVK_END(bool bShift, bool bCtrl);
+  bool OnChar(uint16_t nChar, bool bShift, bool bCtrl);
+
+  void SetScrollPos(const CFX_PointF& point);
+  void ScrollToListItem(int32_t nItemIndex);
+  CFX_FloatRect GetItemRect(int32_t nIndex) const;
+  int32_t GetCaret() const { return m_nCaretIndex; }
+  int32_t GetSelect() const { return m_nSelItem; }
+  int32_t GetTopItem() const;
+  CFX_FloatRect GetContentRect() const;
+
+  int32_t GetItemIndex(const CFX_PointF& point) const;
+  void AddString(const WideString& str);
+  void SetTopItem(int32_t nIndex);
+  void Select(int32_t nItemIndex);
+  void Deselect(int32_t nItemIndex);
+  void SetCaret(int32_t nItemIndex);
+  WideString GetText() const;
+
+  void SetFontMap(IPVT_FontMap* pFontMap) { m_pFontMap = pFontMap; }
+  void SetFontSize(float fFontSize) { m_fFontSize = fFontSize; }
+  CFX_FloatRect GetPlateRect() const { return m_rcPlate; }
+  void SetPlateRect(const CFX_FloatRect& rect);
+
+  float GetFontSize() const { return m_fFontSize; }
+  CPWL_EditImpl* GetItemEdit(int32_t nIndex) const;
+  int32_t GetCount() const;
+  bool IsItemSelected(int32_t nIndex) const;
+  float GetFirstHeight() const;
+  void SetMultipleSel(bool bMultiple) { m_bMultiple = bMultiple; }
+  bool IsMultipleSel() const { return m_bMultiple; }
+  int32_t FindNext(int32_t nIndex, wchar_t nChar) const;
+  int32_t GetFirstSelected() const;
+
+ private:
+  class Item {
+   public:
+    Item();
+    ~Item();
+
+    void SetFontMap(IPVT_FontMap* pFontMap);
+    CPWL_EditImpl* GetEdit() const { return m_pEdit.get(); }
+
+    void SetRect(const CFX_FloatRect& rect) { m_rcListItem = rect; }
+    void SetSelect(bool bSelected) { m_bSelected = bSelected; }
+    void SetText(const WideString& text);
+    void SetFontSize(float fFontSize);
+    WideString GetText() const;
+
+    CFX_FloatRect GetRect() const { return m_rcListItem; }
+    bool IsSelected() const { return m_bSelected; }
+    float GetItemHeight() const;
+    uint16_t GetFirstChar() const;
+
+   private:
+    CPWL_EditImpl::Iterator* GetIterator() const;
+
+    bool m_bSelected = false;
+    CFX_FloatRect m_rcListItem;
+    std::unique_ptr<CPWL_EditImpl> const m_pEdit;
+  };
+
+  class SelectState {
+   public:
+    enum State { DESELECTING = -1, NORMAL = 0, SELECTING = 1 };
+    using const_iterator = std::map<int32_t, State>::const_iterator;
+
+    SelectState();
+    ~SelectState();
+
+    void Add(int32_t nItemIndex);
+    void Add(int32_t nBeginIndex, int32_t nEndIndex);
+    void Sub(int32_t nItemIndex);
+    void Sub(int32_t nBeginIndex, int32_t nEndIndex);
+    void DeselectAll();
+    void Done();
+
+    const_iterator begin() const { return m_Items.begin(); }
+    const_iterator end() const { return m_Items.end(); }
+
+   private:
+    std::map<int32_t, State> m_Items;
+  };
+
+  CFX_PointF InToOut(const CFX_PointF& point) const;
+  CFX_PointF OutToIn(const CFX_PointF& point) const;
+  CFX_FloatRect InToOut(const CFX_FloatRect& rect) const;
+  CFX_FloatRect OutToIn(const CFX_FloatRect& rect) const;
+
+  CFX_PointF InnerToOuter(const CFX_PointF& point) const;
+  CFX_PointF OuterToInner(const CFX_PointF& point) const;
+  CFX_FloatRect InnerToOuter(const CFX_FloatRect& rect) const;
+  CFX_FloatRect OuterToInner(const CFX_FloatRect& rect) const;
+
+  void OnVK(int32_t nItemIndex, bool bShift, bool bCtrl);
+  bool IsValid(int32_t nItemIndex) const;
+
+  void ReArrange(int32_t nItemIndex);
+  CFX_FloatRect GetItemRectInternal(int32_t nIndex) const;
+  CFX_FloatRect GetContentRectInternal() const;
+  void SetMultipleSelect(int32_t nItemIndex, bool bSelected);
+  void SetSingleSelect(int32_t nItemIndex);
+  void InvalidateItem(int32_t nItemIndex);
+  void SelectItems();
+  bool IsItemVisible(int32_t nItemIndex) const;
+  void SetScrollInfo();
+  void SetScrollPosY(float fy);
+  void AddItem(const WideString& str);
+  WideString GetItemText(int32_t nIndex) const;
+  void SetItemSelect(int32_t nIndex, bool bSelected);
+  int32_t GetLastSelected() const;
+  CFX_PointF GetBTPoint() const {
+    return CFX_PointF(m_rcPlate.left, m_rcPlate.top);
+  }
+
+  CFX_FloatRect m_rcPlate;
+  CFX_FloatRect m_rcContent;
+  CFX_PointF m_ptScrollPos;
+  float m_fFontSize = 0.0f;
+
+  // For single:
+  int32_t m_nSelItem = -1;
+
+  // For multiple:
+  SelectState m_SelectState;
+  int32_t m_nFootIndex = -1;
+  int32_t m_nCaretIndex = -1;
+  bool m_bCtrlSel = false;
+
+  bool m_bMultiple = false;
+  bool m_bNotifyFlag = false;
+  UnownedPtr<NotifyIface> m_pNotify;
+  std::vector<std::unique_ptr<Item>> m_ListItems;
+  UnownedPtr<IPVT_FontMap> m_pFontMap;
+};
+
+#endif  // FPDFSDK_PWL_CPWL_LIST_CTRL_H_
diff --git a/fpdfsdk/pwl/cpwl_list_impl.cpp b/fpdfsdk/pwl/cpwl_list_impl.cpp
deleted file mode 100644
index a7ceeb9..0000000
--- a/fpdfsdk/pwl/cpwl_list_impl.cpp
+++ /dev/null
@@ -1,639 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/pwl/cpwl_list_impl.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "core/fpdfdoc/cpvt_word.h"
-#include "core/fxcrt/fx_extension.h"
-#include "fpdfsdk/pwl/cpwl_edit_impl.h"
-#include "fpdfsdk/pwl/cpwl_list_box.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-CPWL_ListCtrl::Item::Item() : m_pEdit(pdfium::MakeUnique<CPWL_EditImpl>()) {
-  m_pEdit->SetAlignmentV(1, true);
-  m_pEdit->Initialize();
-}
-
-CPWL_ListCtrl::Item::~Item() = default;
-
-void CPWL_ListCtrl::Item::SetFontMap(IPVT_FontMap* pFontMap) {
-  m_pEdit->SetFontMap(pFontMap);
-}
-
-void CPWL_ListCtrl::Item::SetText(const WideString& text) {
-  m_pEdit->SetText(text);
-}
-
-void CPWL_ListCtrl::Item::SetFontSize(float fFontSize) {
-  m_pEdit->SetFontSize(fFontSize);
-}
-
-float CPWL_ListCtrl::Item::GetItemHeight() const {
-  return m_pEdit->GetContentRect().Height();
-}
-
-uint16_t CPWL_ListCtrl::Item::GetFirstChar() const {
-  CPVT_Word word;
-  CPWL_EditImpl_Iterator* pIterator = m_pEdit->GetIterator();
-  pIterator->SetAt(1);
-  pIterator->GetWord(word);
-  return word.Word;
-}
-
-WideString CPWL_ListCtrl::Item::GetText() const {
-  return m_pEdit->GetText();
-}
-
-CPLST_Select::CPLST_Select() {}
-
-CPLST_Select::~CPLST_Select() {}
-
-void CPLST_Select::Add(int32_t nItemIndex) {
-  m_Items[nItemIndex] = SELECTING;
-}
-
-void CPLST_Select::Add(int32_t nBeginIndex, int32_t nEndIndex) {
-  if (nBeginIndex > nEndIndex)
-    std::swap(nBeginIndex, nEndIndex);
-
-  for (int32_t i = nBeginIndex; i <= nEndIndex; ++i)
-    Add(i);
-}
-
-void CPLST_Select::Sub(int32_t nItemIndex) {
-  auto it = m_Items.find(nItemIndex);
-  if (it != m_Items.end())
-    it->second = DESELECTING;
-}
-
-void CPLST_Select::Sub(int32_t nBeginIndex, int32_t nEndIndex) {
-  if (nBeginIndex > nEndIndex)
-    std::swap(nBeginIndex, nEndIndex);
-
-  for (int32_t i = nBeginIndex; i <= nEndIndex; ++i)
-    Sub(i);
-}
-
-void CPLST_Select::DeselectAll() {
-  for (auto& item : m_Items)
-    item.second = DESELECTING;
-}
-
-void CPLST_Select::Done() {
-  auto it = m_Items.begin();
-  while (it != m_Items.end()) {
-    if (it->second == DESELECTING)
-      it = m_Items.erase(it);
-    else
-      (it++)->second = NORMAL;
-  }
-}
-
-CPWL_ListCtrl::CPWL_ListCtrl()
-    : m_pNotify(nullptr),
-      m_bNotifyFlag(false),
-      m_nSelItem(-1),
-      m_nFootIndex(-1),
-      m_bCtrlSel(false),
-      m_nCaretIndex(-1),
-      m_fFontSize(0.0f),
-      m_pFontMap(nullptr),
-      m_bMultiple(false) {}
-
-CPWL_ListCtrl::~CPWL_ListCtrl() {
-  Clear();
-}
-
-CFX_PointF CPWL_ListCtrl::InToOut(const CFX_PointF& point) const {
-  CFX_FloatRect rcPlate = m_rcPlate;
-  return CFX_PointF(point.x - (m_ptScrollPos.x - rcPlate.left),
-                    point.y - (m_ptScrollPos.y - rcPlate.top));
-}
-
-CFX_PointF CPWL_ListCtrl::OutToIn(const CFX_PointF& point) const {
-  CFX_FloatRect rcPlate = m_rcPlate;
-  return CFX_PointF(point.x + (m_ptScrollPos.x - rcPlate.left),
-                    point.y + (m_ptScrollPos.y - rcPlate.top));
-}
-
-CFX_FloatRect CPWL_ListCtrl::InToOut(const CFX_FloatRect& rect) const {
-  CFX_PointF ptLeftBottom = InToOut(CFX_PointF(rect.left, rect.bottom));
-  CFX_PointF ptRightTop = InToOut(CFX_PointF(rect.right, rect.top));
-  return CFX_FloatRect(ptLeftBottom.x, ptLeftBottom.y, ptRightTop.x,
-                       ptRightTop.y);
-}
-
-CFX_FloatRect CPWL_ListCtrl::OutToIn(const CFX_FloatRect& rect) const {
-  CFX_PointF ptLeftBottom = OutToIn(CFX_PointF(rect.left, rect.bottom));
-  CFX_PointF ptRightTop = OutToIn(CFX_PointF(rect.right, rect.top));
-  return CFX_FloatRect(ptLeftBottom.x, ptLeftBottom.y, ptRightTop.x,
-                       ptRightTop.y);
-}
-
-CFX_PointF CPWL_ListCtrl::InnerToOuter(const CFX_PointF& point) const {
-  return CFX_PointF(point.x + GetBTPoint().x, GetBTPoint().y - point.y);
-}
-
-CFX_PointF CPWL_ListCtrl::OuterToInner(const CFX_PointF& point) const {
-  return CFX_PointF(point.x - GetBTPoint().x, GetBTPoint().y - point.y);
-}
-
-CFX_FloatRect CPWL_ListCtrl::InnerToOuter(const CFX_FloatRect& rect) const {
-  CFX_PointF ptLeftTop = InnerToOuter(CFX_PointF(rect.left, rect.top));
-  CFX_PointF ptRightBottom = InnerToOuter(CFX_PointF(rect.right, rect.bottom));
-  return CFX_FloatRect(ptLeftTop.x, ptRightBottom.y, ptRightBottom.x,
-                       ptLeftTop.y);
-}
-
-CFX_FloatRect CPWL_ListCtrl::OuterToInner(const CFX_FloatRect& rect) const {
-  CFX_PointF ptLeftTop = OuterToInner(CFX_PointF(rect.left, rect.top));
-  CFX_PointF ptRightBottom = OuterToInner(CFX_PointF(rect.right, rect.bottom));
-  return CFX_FloatRect(ptLeftTop.x, ptRightBottom.y, ptRightBottom.x,
-                       ptLeftTop.y);
-}
-
-void CPWL_ListCtrl::OnMouseDown(const CFX_PointF& point,
-                                bool bShift,
-                                bool bCtrl) {
-  int32_t nHitIndex = GetItemIndex(point);
-
-  if (IsMultipleSel()) {
-    if (bCtrl) {
-      if (IsItemSelected(nHitIndex)) {
-        m_aSelItems.Sub(nHitIndex);
-        SelectItems();
-        m_bCtrlSel = false;
-      } else {
-        m_aSelItems.Add(nHitIndex);
-        SelectItems();
-        m_bCtrlSel = true;
-      }
-
-      m_nFootIndex = nHitIndex;
-    } else if (bShift) {
-      m_aSelItems.DeselectAll();
-      m_aSelItems.Add(m_nFootIndex, nHitIndex);
-      SelectItems();
-    } else {
-      m_aSelItems.DeselectAll();
-      m_aSelItems.Add(nHitIndex);
-      SelectItems();
-
-      m_nFootIndex = nHitIndex;
-    }
-
-    SetCaret(nHitIndex);
-  } else {
-    SetSingleSelect(nHitIndex);
-  }
-
-  if (!IsItemVisible(nHitIndex))
-    ScrollToListItem(nHitIndex);
-}
-
-void CPWL_ListCtrl::OnMouseMove(const CFX_PointF& point,
-                                bool bShift,
-                                bool bCtrl) {
-  int32_t nHitIndex = GetItemIndex(point);
-
-  if (IsMultipleSel()) {
-    if (bCtrl) {
-      if (m_bCtrlSel)
-        m_aSelItems.Add(m_nFootIndex, nHitIndex);
-      else
-        m_aSelItems.Sub(m_nFootIndex, nHitIndex);
-
-      SelectItems();
-    } else {
-      m_aSelItems.DeselectAll();
-      m_aSelItems.Add(m_nFootIndex, nHitIndex);
-      SelectItems();
-    }
-
-    SetCaret(nHitIndex);
-  } else {
-    SetSingleSelect(nHitIndex);
-  }
-
-  if (!IsItemVisible(nHitIndex))
-    ScrollToListItem(nHitIndex);
-}
-
-void CPWL_ListCtrl::OnVK(int32_t nItemIndex, bool bShift, bool bCtrl) {
-  if (IsMultipleSel()) {
-    if (nItemIndex >= 0 && nItemIndex < GetCount()) {
-      if (bCtrl) {
-      } else if (bShift) {
-        m_aSelItems.DeselectAll();
-        m_aSelItems.Add(m_nFootIndex, nItemIndex);
-        SelectItems();
-      } else {
-        m_aSelItems.DeselectAll();
-        m_aSelItems.Add(nItemIndex);
-        SelectItems();
-        m_nFootIndex = nItemIndex;
-      }
-
-      SetCaret(nItemIndex);
-    }
-  } else {
-    SetSingleSelect(nItemIndex);
-  }
-
-  if (!IsItemVisible(nItemIndex))
-    ScrollToListItem(nItemIndex);
-}
-
-void CPWL_ListCtrl::OnVK_UP(bool bShift, bool bCtrl) {
-  OnVK(IsMultipleSel() ? GetCaret() - 1 : GetSelect() - 1, bShift, bCtrl);
-}
-
-void CPWL_ListCtrl::OnVK_DOWN(bool bShift, bool bCtrl) {
-  OnVK(IsMultipleSel() ? GetCaret() + 1 : GetSelect() + 1, bShift, bCtrl);
-}
-
-void CPWL_ListCtrl::OnVK_LEFT(bool bShift, bool bCtrl) {
-  OnVK(0, bShift, bCtrl);
-}
-
-void CPWL_ListCtrl::OnVK_RIGHT(bool bShift, bool bCtrl) {
-  OnVK(GetCount() - 1, bShift, bCtrl);
-}
-
-void CPWL_ListCtrl::OnVK_HOME(bool bShift, bool bCtrl) {
-  OnVK(0, bShift, bCtrl);
-}
-
-void CPWL_ListCtrl::OnVK_END(bool bShift, bool bCtrl) {
-  OnVK(GetCount() - 1, bShift, bCtrl);
-}
-
-bool CPWL_ListCtrl::OnChar(uint16_t nChar, bool bShift, bool bCtrl) {
-  int32_t nIndex = GetLastSelected();
-  int32_t nFindIndex = FindNext(nIndex, nChar);
-
-  if (nFindIndex != nIndex) {
-    OnVK(nFindIndex, bShift, bCtrl);
-    return true;
-  }
-  return false;
-}
-
-void CPWL_ListCtrl::SetPlateRect(const CFX_FloatRect& rect) {
-  m_rcPlate = rect;
-  m_ptScrollPos.x = rect.left;
-  SetScrollPos(CFX_PointF(rect.left, rect.top));
-  ReArrange(0);
-  InvalidateItem(-1);
-}
-
-CFX_FloatRect CPWL_ListCtrl::GetItemRect(int32_t nIndex) const {
-  return InToOut(GetItemRectInternal(nIndex));
-}
-
-CFX_FloatRect CPWL_ListCtrl::GetItemRectInternal(int32_t nIndex) const {
-  if (!IsValid(nIndex))
-    return CFX_FloatRect();
-
-  CFX_FloatRect rcItem = m_ListItems[nIndex]->GetRect();
-  rcItem.left = 0.0f;
-  rcItem.right = m_rcPlate.Width();
-  return InnerToOuter(rcItem);
-}
-
-void CPWL_ListCtrl::AddString(const WideString& str) {
-  AddItem(str);
-  ReArrange(GetCount() - 1);
-}
-
-void CPWL_ListCtrl::SetMultipleSelect(int32_t nItemIndex, bool bSelected) {
-  if (!IsValid(nItemIndex))
-    return;
-
-  if (bSelected != IsItemSelected(nItemIndex)) {
-    if (bSelected) {
-      SetItemSelect(nItemIndex, true);
-      InvalidateItem(nItemIndex);
-    } else {
-      SetItemSelect(nItemIndex, false);
-      InvalidateItem(nItemIndex);
-    }
-  }
-}
-
-void CPWL_ListCtrl::SetSingleSelect(int32_t nItemIndex) {
-  if (!IsValid(nItemIndex))
-    return;
-
-  if (m_nSelItem != nItemIndex) {
-    if (m_nSelItem >= 0) {
-      SetItemSelect(m_nSelItem, false);
-      InvalidateItem(m_nSelItem);
-    }
-
-    SetItemSelect(nItemIndex, true);
-    InvalidateItem(nItemIndex);
-    m_nSelItem = nItemIndex;
-  }
-}
-
-void CPWL_ListCtrl::SetCaret(int32_t nItemIndex) {
-  if (!IsValid(nItemIndex))
-    return;
-
-  if (IsMultipleSel()) {
-    int32_t nOldIndex = m_nCaretIndex;
-
-    if (nOldIndex != nItemIndex) {
-      m_nCaretIndex = nItemIndex;
-      InvalidateItem(nOldIndex);
-      InvalidateItem(nItemIndex);
-    }
-  }
-}
-
-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->IOnInvalidateRect(&rcRefresh);
-        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;
-
-        m_pNotify->IOnInvalidateRect(&rcRefresh);
-        m_bNotifyFlag = false;
-      }
-    }
-  }
-}
-
-void CPWL_ListCtrl::SelectItems() {
-  for (const auto& item : m_aSelItems) {
-    if (item.second != CPLST_Select::NORMAL)
-      SetMultipleSelect(item.first, item.second == CPLST_Select::SELECTING);
-  }
-  m_aSelItems.Done();
-}
-
-void CPWL_ListCtrl::Select(int32_t nItemIndex) {
-  if (!IsValid(nItemIndex))
-    return;
-
-  if (IsMultipleSel()) {
-    m_aSelItems.Add(nItemIndex);
-    SelectItems();
-  } else {
-    SetSingleSelect(nItemIndex);
-  }
-}
-
-void CPWL_ListCtrl::Deselect(int32_t nItemIndex) {
-  if (!IsItemSelected(nItemIndex))
-    return;
-
-  SetMultipleSelect(nItemIndex, false);
-
-  if (!IsMultipleSel())
-    m_nSelItem = -1;
-}
-
-bool CPWL_ListCtrl::IsItemVisible(int32_t nItemIndex) const {
-  CFX_FloatRect rcPlate = m_rcPlate;
-  CFX_FloatRect rcItem = GetItemRect(nItemIndex);
-
-  return rcItem.bottom >= rcPlate.bottom && rcItem.top <= rcPlate.top;
-}
-
-void CPWL_ListCtrl::ScrollToListItem(int32_t nItemIndex) {
-  if (!IsValid(nItemIndex))
-    return;
-
-  CFX_FloatRect rcPlate = m_rcPlate;
-  CFX_FloatRect rcItem = GetItemRectInternal(nItemIndex);
-  CFX_FloatRect rcItemCtrl = GetItemRect(nItemIndex);
-
-  if (IsFloatSmaller(rcItemCtrl.bottom, rcPlate.bottom)) {
-    if (IsFloatSmaller(rcItemCtrl.top, rcPlate.top)) {
-      SetScrollPosY(rcItem.bottom + rcPlate.Height());
-    }
-  } else if (IsFloatBigger(rcItemCtrl.top, rcPlate.top)) {
-    if (IsFloatBigger(rcItemCtrl.bottom, rcPlate.bottom)) {
-      SetScrollPosY(rcItem.top);
-    }
-  }
-}
-
-void CPWL_ListCtrl::SetScrollInfo() {
-  if (m_pNotify) {
-    CFX_FloatRect rcPlate = m_rcPlate;
-    CFX_FloatRect rcContent = GetContentRectInternal();
-
-    if (!m_bNotifyFlag) {
-      m_bNotifyFlag = true;
-      m_pNotify->IOnSetScrollInfoY(rcPlate.bottom, rcPlate.top,
-                                   rcContent.bottom, rcContent.top,
-                                   GetFirstHeight(), rcPlate.Height());
-      m_bNotifyFlag = false;
-    }
-  }
-}
-
-void CPWL_ListCtrl::SetScrollPos(const CFX_PointF& point) {
-  SetScrollPosY(point.y);
-}
-
-void CPWL_ListCtrl::SetScrollPosY(float fy) {
-  if (!IsFloatEqual(m_ptScrollPos.y, fy)) {
-    CFX_FloatRect rcPlate = m_rcPlate;
-    CFX_FloatRect rcContent = GetContentRectInternal();
-
-    if (rcPlate.Height() > rcContent.Height()) {
-      fy = rcPlate.top;
-    } else {
-      if (IsFloatSmaller(fy - rcPlate.Height(), rcContent.bottom)) {
-        fy = rcContent.bottom + rcPlate.Height();
-      } else if (IsFloatBigger(fy, rcContent.top)) {
-        fy = rcContent.top;
-      }
-    }
-
-    m_ptScrollPos.y = fy;
-    InvalidateItem(-1);
-
-    if (m_pNotify) {
-      if (!m_bNotifyFlag) {
-        m_bNotifyFlag = true;
-        m_pNotify->IOnSetScrollPosY(fy);
-        m_bNotifyFlag = false;
-      }
-    }
-  }
-}
-
-CFX_FloatRect CPWL_ListCtrl::GetContentRectInternal() const {
-  return InnerToOuter(m_rcContent);
-}
-
-CFX_FloatRect CPWL_ListCtrl::GetContentRect() const {
-  return InToOut(GetContentRectInternal());
-}
-
-void CPWL_ListCtrl::ReArrange(int32_t nItemIndex) {
-  float fPosY = 0.0f;
-  if (IsValid(nItemIndex - 1))
-    fPosY = m_ListItems[nItemIndex - 1]->GetRect().bottom;
-
-  for (const auto& pListItem : m_ListItems) {
-    float fListItemHeight = pListItem->GetItemHeight();
-    pListItem->SetRect(
-        CFX_FloatRect(0.0f, fPosY + fListItemHeight, 0.0f, fPosY));
-    fPosY += fListItemHeight;
-  }
-  SetContentRect(CFX_FloatRect(0.0f, fPosY, 0.0f, 0.0f));
-  SetScrollInfo();
-}
-
-void CPWL_ListCtrl::SetTopItem(int32_t nIndex) {
-  if (IsValid(nIndex)) {
-    CFX_FloatRect rcItem = GetItemRectInternal(nIndex);
-    SetScrollPosY(rcItem.top);
-  }
-}
-
-int32_t CPWL_ListCtrl::GetTopItem() const {
-  int32_t nItemIndex = GetItemIndex(GetBTPoint());
-  if (!IsItemVisible(nItemIndex) && IsItemVisible(nItemIndex + 1))
-    nItemIndex += 1;
-
-  return nItemIndex;
-}
-
-void CPWL_ListCtrl::Clear() {
-  m_ListItems.clear();
-  InvalidateItem(-1);
-}
-
-void CPWL_ListCtrl::Cancel() {
-  m_aSelItems.DeselectAll();
-}
-
-int32_t CPWL_ListCtrl::GetItemIndex(const CFX_PointF& point) const {
-  CFX_PointF pt = OuterToInner(OutToIn(point));
-  bool bFirst = true;
-  bool bLast = true;
-  for (const auto& pListItem : m_ListItems) {
-    CFX_FloatRect rcListItem = pListItem->GetRect();
-    if (IsFloatBigger(pt.y, rcListItem.top))
-      bFirst = false;
-    if (IsFloatSmaller(pt.y, rcListItem.bottom))
-      bLast = false;
-    if (pt.y >= rcListItem.top && pt.y < rcListItem.bottom)
-      return &pListItem - &m_ListItems.front();
-  }
-  if (bFirst)
-    return 0;
-  if (bLast)
-    return GetCount() - 1;
-  return -1;
-}
-
-WideString CPWL_ListCtrl::GetText() const {
-  if (IsMultipleSel())
-    return GetItemText(m_nCaretIndex);
-  return GetItemText(m_nSelItem);
-}
-
-void CPWL_ListCtrl::AddItem(const WideString& str) {
-  auto pListItem = pdfium::MakeUnique<Item>();
-  pListItem->SetFontMap(m_pFontMap.Get());
-  pListItem->SetFontSize(m_fFontSize);
-  pListItem->SetText(str);
-  m_ListItems.push_back(std::move(pListItem));
-}
-
-CPWL_EditImpl* CPWL_ListCtrl::GetItemEdit(int32_t nIndex) const {
-  if (!IsValid(nIndex))
-    return nullptr;
-  return m_ListItems[nIndex]->GetEdit();
-}
-
-int32_t CPWL_ListCtrl::GetCount() const {
-  return pdfium::CollectionSize<int32_t>(m_ListItems);
-}
-
-float CPWL_ListCtrl::GetFirstHeight() const {
-  if (m_ListItems.empty())
-    return 1.0f;
-  return m_ListItems.front()->GetItemHeight();
-}
-
-int32_t CPWL_ListCtrl::GetFirstSelected() const {
-  int32_t i = 0;
-  for (const auto& pListItem : m_ListItems) {
-    if (pListItem->IsSelected())
-      return i;
-    ++i;
-  }
-  return -1;
-}
-
-int32_t CPWL_ListCtrl::GetLastSelected() const {
-  for (auto iter = m_ListItems.rbegin(); iter != m_ListItems.rend(); ++iter) {
-    if ((*iter)->IsSelected())
-      return &*iter - &m_ListItems.front();
-  }
-  return -1;
-}
-
-int32_t CPWL_ListCtrl::FindNext(int32_t nIndex, wchar_t nChar) const {
-  int32_t nCircleIndex = nIndex;
-  int32_t sz = GetCount();
-  for (int32_t i = 0; i < sz; i++) {
-    nCircleIndex++;
-    if (nCircleIndex >= sz)
-      nCircleIndex = 0;
-
-    if (Item* pListItem = m_ListItems[nCircleIndex].get()) {
-      if (FXSYS_towupper(pListItem->GetFirstChar()) == FXSYS_towupper(nChar))
-        return nCircleIndex;
-    }
-  }
-
-  return nCircleIndex;
-}
-
-bool CPWL_ListCtrl::IsItemSelected(int32_t nIndex) const {
-  return IsValid(nIndex) && m_ListItems[nIndex]->IsSelected();
-}
-
-void CPWL_ListCtrl::SetItemSelect(int32_t nIndex, bool bSelected) {
-  if (IsValid(nIndex))
-    m_ListItems[nIndex]->SetSelect(bSelected);
-}
-
-bool CPWL_ListCtrl::IsValid(int32_t nItemIndex) const {
-  return pdfium::IndexInBounds(m_ListItems, nItemIndex);
-}
-
-WideString CPWL_ListCtrl::GetItemText(int32_t nIndex) const {
-  if (IsValid(nIndex))
-    return m_ListItems[nIndex]->GetText();
-  return WideString();
-}
diff --git a/fpdfsdk/pwl/cpwl_list_impl.h b/fpdfsdk/pwl/cpwl_list_impl.h
deleted file mode 100644
index c7a86ab..0000000
--- a/fpdfsdk/pwl/cpwl_list_impl.h
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_PWL_CPWL_LIST_IMPL_H_
-#define FPDFSDK_PWL_CPWL_LIST_IMPL_H_
-
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class CPWL_EditImpl;
-class CPWL_EditImpl_Iterator;
-class CPWL_List_Notify;
-class IPVT_FontMap;
-
-class CPLST_Select final {
- public:
-  enum State { DESELECTING = -1, NORMAL = 0, SELECTING = 1 };
-  using const_iterator = std::map<int32_t, State>::const_iterator;
-
-  CPLST_Select();
-  ~CPLST_Select();
-
-  void Add(int32_t nItemIndex);
-  void Add(int32_t nBeginIndex, int32_t nEndIndex);
-  void Sub(int32_t nItemIndex);
-  void Sub(int32_t nBeginIndex, int32_t nEndIndex);
-  void DeselectAll();
-  void Done();
-
-  const_iterator begin() const { return m_Items.begin(); }
-  const_iterator end() const { return m_Items.end(); }
-
- private:
-  std::map<int32_t, State> m_Items;
-};
-
-class CPWL_ListCtrl {
- public:
-  CPWL_ListCtrl();
-  ~CPWL_ListCtrl();
-
-  void SetNotify(CPWL_List_Notify* pNotify) { m_pNotify = pNotify; }
-  void OnMouseDown(const CFX_PointF& point, bool bShift, bool bCtrl);
-  void OnMouseMove(const CFX_PointF& point, bool bShift, bool bCtrl);
-  void OnVK_UP(bool bShift, bool bCtrl);
-  void OnVK_DOWN(bool bShift, bool bCtrl);
-  void OnVK_LEFT(bool bShift, bool bCtrl);
-  void OnVK_RIGHT(bool bShift, bool bCtrl);
-  void OnVK_HOME(bool bShift, bool bCtrl);
-  void OnVK_END(bool bShift, bool bCtrl);
-  bool OnChar(uint16_t nChar, bool bShift, bool bCtrl);
-
-  void SetScrollPos(const CFX_PointF& point);
-  void ScrollToListItem(int32_t nItemIndex);
-  CFX_FloatRect GetItemRect(int32_t nIndex) const;
-  int32_t GetCaret() const { return m_nCaretIndex; }
-  int32_t GetSelect() const { return m_nSelItem; }
-  int32_t GetTopItem() const;
-  void SetContentRect(const CFX_FloatRect& rect) { m_rcContent = rect; }
-  CFX_FloatRect GetContentRect() const;
-
-  int32_t GetItemIndex(const CFX_PointF& point) const;
-  void AddString(const WideString& str);
-  void SetTopItem(int32_t nIndex);
-  void Select(int32_t nItemIndex);
-  void Deselect(int32_t nItemIndex);
-  void SetCaret(int32_t nItemIndex);
-  void Clear();
-  void Cancel();
-  WideString GetText() const;
-
-  void SetFontMap(IPVT_FontMap* pFontMap) { m_pFontMap = pFontMap; }
-  void SetFontSize(float fFontSize) { m_fFontSize = fFontSize; }
-  CFX_FloatRect GetPlateRect() const { return m_rcPlate; }
-  void SetPlateRect(const CFX_FloatRect& rect);
-
-  float GetFontSize() const { return m_fFontSize; }
-  CPWL_EditImpl* GetItemEdit(int32_t nIndex) const;
-  int32_t GetCount() const;
-  bool IsItemSelected(int32_t nIndex) const;
-  float GetFirstHeight() const;
-  void SetMultipleSel(bool bMultiple) { m_bMultiple = bMultiple; }
-  bool IsMultipleSel() const { return m_bMultiple; }
-  int32_t FindNext(int32_t nIndex, wchar_t nChar) const;
-  int32_t GetFirstSelected() const;
-
- private:
-  class Item {
-   public:
-    Item();
-    ~Item();
-
-    void SetFontMap(IPVT_FontMap* pFontMap);
-    CPWL_EditImpl* GetEdit() const { return m_pEdit.get(); }
-
-    void SetRect(const CFX_FloatRect& rect) { m_rcListItem = rect; }
-    void SetSelect(bool bSelected) { m_bSelected = bSelected; }
-    void SetText(const WideString& text);
-    void SetFontSize(float fFontSize);
-    WideString GetText() const;
-
-    CFX_FloatRect GetRect() const { return m_rcListItem; }
-    bool IsSelected() const { return m_bSelected; }
-    float GetItemHeight() const;
-    uint16_t GetFirstChar() const;
-
-   private:
-    CPWL_EditImpl_Iterator* GetIterator() const;
-
-    bool m_bSelected = false;
-    CFX_FloatRect m_rcListItem;
-    std::unique_ptr<CPWL_EditImpl> const m_pEdit;
-  };
-
-  CFX_PointF InToOut(const CFX_PointF& point) const;
-  CFX_PointF OutToIn(const CFX_PointF& point) const;
-  CFX_FloatRect InToOut(const CFX_FloatRect& rect) const;
-  CFX_FloatRect OutToIn(const CFX_FloatRect& rect) const;
-
-  CFX_PointF InnerToOuter(const CFX_PointF& point) const;
-  CFX_PointF OuterToInner(const CFX_PointF& point) const;
-  CFX_FloatRect InnerToOuter(const CFX_FloatRect& rect) const;
-  CFX_FloatRect OuterToInner(const CFX_FloatRect& rect) const;
-
-  void OnVK(int32_t nItemIndex, bool bShift, bool bCtrl);
-  bool IsValid(int32_t nItemIndex) const;
-
-  void ReArrange(int32_t nItemIndex);
-  CFX_FloatRect GetItemRectInternal(int32_t nIndex) const;
-  CFX_FloatRect GetContentRectInternal() const;
-  void SetMultipleSelect(int32_t nItemIndex, bool bSelected);
-  void SetSingleSelect(int32_t nItemIndex);
-  void InvalidateItem(int32_t nItemIndex);
-  void SelectItems();
-  bool IsItemVisible(int32_t nItemIndex) const;
-  void SetScrollInfo();
-  void SetScrollPosY(float fy);
-  void AddItem(const WideString& str);
-  WideString GetItemText(int32_t nIndex) const;
-  void SetItemSelect(int32_t nIndex, bool bSelected);
-  int32_t GetLastSelected() const;
-  CFX_PointF GetBTPoint() const {
-    return CFX_PointF(m_rcPlate.left, m_rcPlate.top);
-  }
-
-  CFX_FloatRect m_rcPlate;
-  CFX_FloatRect m_rcContent;
-  UnownedPtr<CPWL_List_Notify> m_pNotify;
-  bool m_bNotifyFlag;
-  CFX_PointF m_ptScrollPos;
-  CPLST_Select m_aSelItems;  // for multiple
-  int32_t m_nSelItem;        // for single
-  int32_t m_nFootIndex;      // for multiple
-  bool m_bCtrlSel;           // for multiple
-  int32_t m_nCaretIndex;     // for multiple
-  std::vector<std::unique_ptr<Item>> m_ListItems;
-  float m_fFontSize;
-  UnownedPtr<IPVT_FontMap> m_pFontMap;
-  bool m_bMultiple;
-};
-
-#endif  // FPDFSDK_PWL_CPWL_LIST_IMPL_H_
diff --git a/fpdfsdk/pwl/cpwl_sbbutton.cpp b/fpdfsdk/pwl/cpwl_sbbutton.cpp
new file mode 100644
index 0000000..28f037a
--- /dev/null
+++ b/fpdfsdk/pwl/cpwl_sbbutton.cpp
@@ -0,0 +1,148 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fpdfsdk/pwl/cpwl_sbbutton.h"
+
+#include <utility>
+#include <vector>
+
+#include "core/fxge/cfx_renderdevice.h"
+
+CPWL_SBButton::CPWL_SBButton(
+    const CreateParams& cp,
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData,
+    Type eButtonType)
+    : CPWL_Wnd(cp, std::move(pAttachedData)), m_eSBButtonType(eButtonType) {
+  GetCreationParams()->eCursorType = IPWL_FillerNotify::CursorStyle::kArrow;
+}
+
+CPWL_SBButton::~CPWL_SBButton() = default;
+
+void CPWL_SBButton::DrawThisAppearance(CFX_RenderDevice* pDevice,
+                                       const CFX_Matrix& mtUser2Device) {
+  if (!IsVisible())
+    return;
+
+  CFX_FloatRect rectWnd = GetWindowRect();
+  if (rectWnd.IsEmpty())
+    return;
+
+  CFX_PointF ptCenter = GetCenterPoint();
+  int32_t nTransparency = GetTransparency();
+
+  // draw border
+  pDevice->DrawStrokeRect(mtUser2Device, rectWnd,
+                          ArgbEncode(nTransparency, 100, 100, 100), 0.0f);
+  pDevice->DrawStrokeRect(mtUser2Device, rectWnd.GetDeflated(0.5f, 0.5f),
+                          ArgbEncode(nTransparency, 255, 255, 255), 1.0f);
+
+  if (m_eSBButtonType != Type::kPosButton) {
+    // draw background
+    pDevice->DrawShadow(mtUser2Device, rectWnd.GetDeflated(1.0f, 1.0f),
+                        nTransparency, 80, 220);
+    // draw arrow
+    if (rectWnd.top - rectWnd.bottom > 6.0f) {
+      std::vector<CFX_PointF> pts;
+      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));
+    }
+    return;
+  }
+
+  // draw shadow effect
+  CFX_PointF ptTop = CFX_PointF(rectWnd.left, rectWnd.top - 1.0f);
+  CFX_PointF ptBottom = CFX_PointF(rectWnd.left, rectWnd.bottom + 1.0f);
+
+  ptTop.x += 1.5f;
+  ptBottom.x += 1.5f;
+
+  const FX_COLORREF refs[] = {ArgbEncode(nTransparency, 210, 210, 210),
+                              ArgbEncode(nTransparency, 220, 220, 220),
+                              ArgbEncode(nTransparency, 240, 240, 240),
+                              ArgbEncode(nTransparency, 240, 240, 240),
+                              ArgbEncode(nTransparency, 210, 210, 210),
+                              ArgbEncode(nTransparency, 180, 180, 180),
+                              ArgbEncode(nTransparency, 150, 150, 150),
+                              ArgbEncode(nTransparency, 150, 150, 150),
+                              ArgbEncode(nTransparency, 180, 180, 180),
+                              ArgbEncode(nTransparency, 210, 210, 210)};
+  for (FX_COLORREF ref : refs) {
+    pDevice->DrawStrokeLine(&mtUser2Device, ptTop, ptBottom, ref, 1.0f);
+
+    ptTop.x += 1.0f;
+    ptBottom.x += 1.0f;
+  }
+
+  // draw friction
+  if (rectWnd.Height() <= 8.0f)
+    return;
+
+  FX_COLORREF crStroke = ArgbEncode(nTransparency, 120, 120, 120);
+  float nFrictionWidth = 5.0f;
+  float nFrictionHeight = 5.5f;
+  CFX_PointF ptLeft = CFX_PointF(ptCenter.x - nFrictionWidth / 2.0f,
+                                 ptCenter.y - nFrictionHeight / 2.0f + 0.5f);
+  CFX_PointF ptRight = CFX_PointF(ptCenter.x + nFrictionWidth / 2.0f,
+                                  ptCenter.y - nFrictionHeight / 2.0f + 0.5f);
+
+  for (size_t i = 0; i < 3; ++i) {
+    pDevice->DrawStrokeLine(&mtUser2Device, ptLeft, ptRight, crStroke, 1.0f);
+    ptLeft.y += 2.0f;
+    ptRight.y += 2.0f;
+  }
+}
+
+bool CPWL_SBButton::OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,
+                                  const CFX_PointF& point) {
+  CPWL_Wnd::OnLButtonDown(nFlag, point);
+
+  if (CPWL_Wnd* pParent = GetParentWindow())
+    pParent->NotifyLButtonDown(this, point);
+
+  m_bMouseDown = true;
+  SetCapture();
+
+  return true;
+}
+
+bool CPWL_SBButton::OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag,
+                                const CFX_PointF& point) {
+  CPWL_Wnd::OnLButtonUp(nFlag, point);
+
+  if (CPWL_Wnd* pParent = GetParentWindow())
+    pParent->NotifyLButtonUp(this, point);
+
+  m_bMouseDown = false;
+  ReleaseCapture();
+
+  return true;
+}
+
+bool CPWL_SBButton::OnMouseMove(Mask<FWL_EVENTFLAG> nFlag,
+                                const CFX_PointF& point) {
+  CPWL_Wnd::OnMouseMove(nFlag, point);
+
+  if (CPWL_Wnd* pParent = GetParentWindow())
+    pParent->NotifyMouseMove(this, point);
+
+  return true;
+}
diff --git a/fpdfsdk/pwl/cpwl_sbbutton.h b/fpdfsdk/pwl/cpwl_sbbutton.h
new file mode 100644
index 0000000..9b4abc8
--- /dev/null
+++ b/fpdfsdk/pwl/cpwl_sbbutton.h
@@ -0,0 +1,37 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_PWL_CPWL_SBBUTTON_H_
+#define FPDFSDK_PWL_CPWL_SBBUTTON_H_
+
+#include <memory>
+
+#include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "fpdfsdk/pwl/ipwl_fillernotify.h"
+
+class CPWL_SBButton final : public CPWL_Wnd {
+ public:
+  enum class Type : uint8_t { kMinButton, kMaxButton, kPosButton };
+
+  CPWL_SBButton(const CreateParams& cp,
+                std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData,
+                Type eButtonType);
+  ~CPWL_SBButton() override;
+
+  // CPWL_Wnd
+  void DrawThisAppearance(CFX_RenderDevice* pDevice,
+                          const CFX_Matrix& mtUser2Device) override;
+  bool OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,
+                     const CFX_PointF& point) override;
+  bool OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag, const CFX_PointF& point) override;
+  bool OnMouseMove(Mask<FWL_EVENTFLAG> nFlag, const CFX_PointF& point) override;
+
+ private:
+  const Type m_eSBButtonType;
+  bool m_bMouseDown = false;
+};
+
+#endif  // FPDFSDK_PWL_CPWL_SBBUTTON_H_
diff --git a/fpdfsdk/pwl/cpwl_scroll_bar.cpp b/fpdfsdk/pwl/cpwl_scroll_bar.cpp
index bf4d1da..67c503c 100644
--- a/fpdfsdk/pwl/cpwl_scroll_bar.cpp
+++ b/fpdfsdk/pwl/cpwl_scroll_bar.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,26 +6,25 @@
 
 #include "fpdfsdk/pwl/cpwl_scroll_bar.h"
 
+#include <math.h>
+
 #include <algorithm>
 #include <sstream>
 #include <utility>
-#include <vector>
 
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 namespace {
 
 constexpr float kButtonWidth = 9.0f;
 constexpr float kPosButtonMinWidth = 2.0f;
-constexpr float kScrollBarTriangleHalfLength = 2.0f;
 
 }  // namespace
 
-#define PWL_DEFAULT_HEAVYGRAYCOLOR CFX_Color(CFX_Color::kGray, 0.50)
-
 void PWL_FLOATRANGE::Reset() {
   fMin = 0.0f;
   fMax = 0.0f;
@@ -37,8 +36,8 @@
 }
 
 bool PWL_FLOATRANGE::In(float x) const {
-  return (IsFloatBigger(x, fMin) || IsFloatEqual(x, fMin)) &&
-         (IsFloatSmaller(x, fMax) || IsFloatEqual(x, fMax));
+  return (FXSYS_IsFloatBigger(x, fMin) || FXSYS_IsFloatEqual(x, fMin)) &&
+         (FXSYS_IsFloatSmaller(x, fMax) || FXSYS_IsFloatEqual(x, fMax));
 }
 
 float PWL_FLOATRANGE::GetWidth() const {
@@ -60,9 +59,9 @@
 void PWL_SCROLL_PRIVATEDATA::SetScrollRange(float min, float max) {
   ScrollRange.Set(min, max);
 
-  if (IsFloatSmaller(fScrollPos, ScrollRange.fMin))
+  if (FXSYS_IsFloatSmaller(fScrollPos, ScrollRange.fMin))
     fScrollPos = ScrollRange.fMin;
-  if (IsFloatBigger(fScrollPos, ScrollRange.fMax))
+  if (FXSYS_IsFloatBigger(fScrollPos, ScrollRange.fMax))
     fScrollPos = ScrollRange.fMax;
 }
 
@@ -106,205 +105,11 @@
     SetPos(ScrollRange.fMin);
 }
 
-CPWL_SBButton::CPWL_SBButton(
-    const CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData,
-    PWL_SCROLLBAR_TYPE eScrollBarType,
-    PWL_SBBUTTON_TYPE eButtonType)
-    : CPWL_Wnd(cp, std::move(pAttachedData)),
-      m_eScrollBarType(eScrollBarType),
-      m_eSBButtonType(eButtonType) {
-  GetCreationParams()->eCursorType = FXCT_ARROW;
-}
-
-CPWL_SBButton::~CPWL_SBButton() = default;
-
-void CPWL_SBButton::DrawThisAppearance(CFX_RenderDevice* pDevice,
-                                       const CFX_Matrix& mtUser2Device) {
-  if (!IsVisible())
-    return;
-
-  CFX_FloatRect rectWnd = GetWindowRect();
-  if (rectWnd.IsEmpty())
-    return;
-
-  CFX_PointF ptCenter = GetCenterPoint();
-  int32_t nTransparency = GetTransparency();
-
-  if (m_eScrollBarType == SBT_HSCROLL) {
-    CPWL_Wnd::DrawThisAppearance(pDevice, mtUser2Device);
-
-    CFX_PointF pt1;
-    CFX_PointF pt2;
-    CFX_PointF pt3;
-    static constexpr float kScrollBarTriangleQuarterLength =
-        kScrollBarTriangleHalfLength * 0.5;
-    if (m_eSBButtonType == PSBT_MIN) {
-      pt1 =
-          CFX_PointF(ptCenter.x - kScrollBarTriangleQuarterLength, ptCenter.y);
-      pt2 = CFX_PointF(ptCenter.x + kScrollBarTriangleQuarterLength,
-                       ptCenter.y + kScrollBarTriangleHalfLength);
-      pt3 = CFX_PointF(ptCenter.x + kScrollBarTriangleQuarterLength,
-                       ptCenter.y - kScrollBarTriangleHalfLength);
-    } else if (m_eSBButtonType == PSBT_MAX) {
-      pt1 =
-          CFX_PointF(ptCenter.x + kScrollBarTriangleQuarterLength, ptCenter.y);
-      pt2 = CFX_PointF(ptCenter.x - kScrollBarTriangleQuarterLength,
-                       ptCenter.y + kScrollBarTriangleHalfLength);
-      pt3 = CFX_PointF(ptCenter.x - kScrollBarTriangleQuarterLength,
-                       ptCenter.y - kScrollBarTriangleHalfLength);
-    }
-
-    if (rectWnd.right - rectWnd.left > kScrollBarTriangleHalfLength * 2 &&
-        rectWnd.top - rectWnd.bottom > kScrollBarTriangleHalfLength) {
-      CFX_PathData path;
-      path.AppendPoint(pt1, FXPT_TYPE::MoveTo, false);
-      path.AppendPoint(pt2, FXPT_TYPE::LineTo, false);
-      path.AppendPoint(pt3, FXPT_TYPE::LineTo, false);
-      path.AppendPoint(pt1, FXPT_TYPE::LineTo, false);
-
-      pDevice->DrawPath(&path, &mtUser2Device, nullptr,
-                        PWL_DEFAULT_BLACKCOLOR.ToFXColor(nTransparency), 0,
-                        FXFILL_ALTERNATE);
-    }
-    return;
-  }
-
-  // draw border
-  pDevice->DrawStrokeRect(mtUser2Device, rectWnd,
-                          ArgbEncode(nTransparency, 100, 100, 100), 0.0f);
-  pDevice->DrawStrokeRect(mtUser2Device, rectWnd.GetDeflated(0.5f, 0.5f),
-                          ArgbEncode(nTransparency, 255, 255, 255), 1.0f);
-
-  if (m_eSBButtonType != PSBT_POS) {
-    // draw background
-    if (IsEnabled()) {
-      pDevice->DrawShadow(mtUser2Device, true, false,
-                          rectWnd.GetDeflated(1.0f, 1.0f), nTransparency, 80,
-                          220);
-    } else {
-      pDevice->DrawFillRect(&mtUser2Device, rectWnd.GetDeflated(1.0f, 1.0f),
-                            ArgbEncode(255, 255, 255, 255));
-    }
-
-    // draw arrow
-    if (rectWnd.top - rectWnd.bottom > 6.0f) {
-      float fX = rectWnd.left + 1.5f;
-      float fY = rectWnd.bottom;
-      std::vector<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(FX_ArraySize(kOffsetsX) == FX_ArraySize(kOffsetsY),
-                    "Wrong offset count");
-      static_assert(FX_ArraySize(kOffsetsX) == FX_ArraySize(kOffsetsMinY),
-                    "Wrong offset count");
-      const float* pOffsetsY =
-          m_eSBButtonType == PSBT_MIN ? kOffsetsMinY : kOffsetsY;
-      for (size_t i = 0; i < FX_ArraySize(kOffsetsX); ++i)
-        pts.push_back(CFX_PointF(fX + kOffsetsX[i], fY + pOffsetsY[i]));
-      pDevice->DrawFillArea(mtUser2Device, pts,
-                            IsEnabled()
-                                ? ArgbEncode(nTransparency, 255, 255, 255)
-                                : PWL_DEFAULT_HEAVYGRAYCOLOR.ToFXColor(255));
-    }
-    return;
-  }
-
-  if (IsEnabled()) {
-    // draw shadow effect
-    CFX_PointF ptTop = CFX_PointF(rectWnd.left, rectWnd.top - 1.0f);
-    CFX_PointF ptBottom = CFX_PointF(rectWnd.left, rectWnd.bottom + 1.0f);
-
-    ptTop.x += 1.5f;
-    ptBottom.x += 1.5f;
-
-    const FX_COLORREF refs[] = {ArgbEncode(nTransparency, 210, 210, 210),
-                                ArgbEncode(nTransparency, 220, 220, 220),
-                                ArgbEncode(nTransparency, 240, 240, 240),
-                                ArgbEncode(nTransparency, 240, 240, 240),
-                                ArgbEncode(nTransparency, 210, 210, 210),
-                                ArgbEncode(nTransparency, 180, 180, 180),
-                                ArgbEncode(nTransparency, 150, 150, 150),
-                                ArgbEncode(nTransparency, 150, 150, 150),
-                                ArgbEncode(nTransparency, 180, 180, 180),
-                                ArgbEncode(nTransparency, 210, 210, 210)};
-    for (FX_COLORREF ref : refs) {
-      pDevice->DrawStrokeLine(&mtUser2Device, ptTop, ptBottom, ref, 1.0f);
-
-      ptTop.x += 1.0f;
-      ptBottom.x += 1.0f;
-    }
-  } else {
-    pDevice->DrawFillRect(&mtUser2Device, rectWnd.GetDeflated(0.5f, 0.5f),
-                          ArgbEncode(255, 255, 255, 255));
-  }
-
-  // draw friction
-  if (rectWnd.Height() <= 8.0f)
-    return;
-
-  FX_COLORREF crStroke = ArgbEncode(nTransparency, 120, 120, 120);
-  if (!IsEnabled())
-    crStroke = PWL_DEFAULT_HEAVYGRAYCOLOR.ToFXColor(255);
-
-  float nFrictionWidth = 5.0f;
-  float nFrictionHeight = 5.5f;
-
-  CFX_PointF ptLeft = CFX_PointF(ptCenter.x - nFrictionWidth / 2.0f,
-                                 ptCenter.y - nFrictionHeight / 2.0f + 0.5f);
-  CFX_PointF ptRight = CFX_PointF(ptCenter.x + nFrictionWidth / 2.0f,
-                                  ptCenter.y - nFrictionHeight / 2.0f + 0.5f);
-
-  for (size_t i = 0; i < 3; ++i) {
-    pDevice->DrawStrokeLine(&mtUser2Device, ptLeft, ptRight, crStroke, 1.0f);
-    ptLeft.y += 2.0f;
-    ptRight.y += 2.0f;
-  }
-}
-
-bool CPWL_SBButton::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnLButtonDown(point, nFlag);
-
-  if (CPWL_Wnd* pParent = GetParentWindow())
-    pParent->NotifyLButtonDown(this, point);
-
-  m_bMouseDown = true;
-  SetCapture();
-
-  return true;
-}
-
-bool CPWL_SBButton::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnLButtonUp(point, nFlag);
-
-  if (CPWL_Wnd* pParent = GetParentWindow())
-    pParent->NotifyLButtonUp(this, point);
-
-  m_bMouseDown = false;
-  ReleaseCapture();
-
-  return true;
-}
-
-bool CPWL_SBButton::OnMouseMove(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnMouseMove(point, nFlag);
-
-  if (CPWL_Wnd* pParent = GetParentWindow())
-    pParent->NotifyMouseMove(this, point);
-
-  return true;
-}
-
 CPWL_ScrollBar::CPWL_ScrollBar(
     const CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData,
-    PWL_SCROLLBAR_TYPE sbType)
-    : CPWL_Wnd(cp, std::move(pAttachedData)), m_sbType(sbType) {
-  GetCreationParams()->eCursorType = FXCT_ARROW;
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData)
+    : CPWL_Wnd(cp, std::move(pAttachedData)) {
+  GetCreationParams()->eCursorType = IPWL_FillerNotify::CursorStyle::kArrow;
 }
 
 CPWL_ScrollBar::~CPWL_ScrollBar() = default;
@@ -314,80 +119,51 @@
   // subclasses, implement the virtual OnDestroy method that does the
   // cleanup first, then invokes the superclass OnDestroy ... gee,
   // like a dtor would.
-  m_pMinButton.Release();
-  m_pMaxButton.Release();
-  m_pPosButton.Release();
+  m_pMinButton.ExtractAsDangling();
+  m_pMaxButton.ExtractAsDangling();
+  m_pPosButton.ExtractAsDangling();
   CPWL_Wnd::OnDestroy();
 }
 
-bool CPWL_ScrollBar::RePosChildWnd() {
+bool CPWL_ScrollBar::RepositionChildWnd() {
   CFX_FloatRect rcClient = GetClientRect();
-  CFX_FloatRect rcMinButton, rcMaxButton;
-  float fBWidth = 0;
-
-  switch (m_sbType) {
-    case SBT_HSCROLL:
-      if (rcClient.right - rcClient.left >
-          kButtonWidth * 2 + kPosButtonMinWidth + 2) {
-        rcMinButton = CFX_FloatRect(rcClient.left, rcClient.bottom,
-                                    rcClient.left + kButtonWidth, rcClient.top);
-        rcMaxButton =
-            CFX_FloatRect(rcClient.right - kButtonWidth, rcClient.bottom,
-                          rcClient.right, rcClient.top);
-      } else {
-        fBWidth = (rcClient.right - rcClient.left - kPosButtonMinWidth - 2) / 2;
-
-        if (fBWidth > 0) {
-          rcMinButton = CFX_FloatRect(rcClient.left, rcClient.bottom,
-                                      rcClient.left + fBWidth, rcClient.top);
-          rcMaxButton = CFX_FloatRect(rcClient.right - fBWidth, rcClient.bottom,
-                                      rcClient.right, rcClient.top);
-        } else {
-          if (!SetVisible(false))
-            return false;
-        }
-      }
-      break;
-    case SBT_VSCROLL:
-      if (IsFloatBigger(rcClient.top - rcClient.bottom,
-                        kButtonWidth * 2 + kPosButtonMinWidth + 2)) {
-        rcMinButton = CFX_FloatRect(rcClient.left, rcClient.top - kButtonWidth,
-                                    rcClient.right, rcClient.top);
-        rcMaxButton =
-            CFX_FloatRect(rcClient.left, rcClient.bottom, rcClient.right,
-                          rcClient.bottom + kButtonWidth);
-      } else {
-        fBWidth = (rcClient.top - rcClient.bottom - kPosButtonMinWidth - 2) / 2;
-
-        if (IsFloatBigger(fBWidth, 0)) {
-          rcMinButton = CFX_FloatRect(rcClient.left, rcClient.top - fBWidth,
-                                      rcClient.right, rcClient.top);
-          rcMaxButton =
-              CFX_FloatRect(rcClient.left, rcClient.bottom, rcClient.right,
-                            rcClient.bottom + fBWidth);
-        } else {
-          if (!SetVisible(false))
-            return false;
-        }
-      }
-      break;
+  CFX_FloatRect rcMinButton;
+  CFX_FloatRect rcMaxButton;
+  if (FXSYS_IsFloatBigger(rcClient.top - rcClient.bottom,
+                          kButtonWidth * 2 + kPosButtonMinWidth + 2)) {
+    rcMinButton = CFX_FloatRect(rcClient.left, rcClient.top - kButtonWidth,
+                                rcClient.right, rcClient.top);
+    rcMaxButton = CFX_FloatRect(rcClient.left, rcClient.bottom, rcClient.right,
+                                rcClient.bottom + kButtonWidth);
+  } else {
+    float fBWidth =
+        (rcClient.top - rcClient.bottom - kPosButtonMinWidth - 2) / 2;
+    if (FXSYS_IsFloatBigger(fBWidth, 0)) {
+      rcMinButton = CFX_FloatRect(rcClient.left, rcClient.top - fBWidth,
+                                  rcClient.right, rcClient.top);
+      rcMaxButton = CFX_FloatRect(rcClient.left, rcClient.bottom,
+                                  rcClient.right, rcClient.bottom + fBWidth);
+    } else {
+      if (!SetVisible(false))
+        return false;
+    }
   }
 
-  ObservedPtr<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;
+    }
   }
-  if (!MovePosButton(false))
-    return false;
 
-  return true;
+  return MovePosButton(false);
 }
 
 void CPWL_ScrollBar::DrawThisAppearance(CFX_RenderDevice* pDevice,
@@ -410,8 +186,9 @@
   }
 }
 
-bool CPWL_ScrollBar::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnLButtonDown(point, nFlag);
+bool CPWL_ScrollBar::OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,
+                                   const CFX_PointF& point) {
+  CPWL_Wnd::OnLButtonDown(nFlag, point);
 
   if (HasFlag(PWS_AUTOTRANSPARENT)) {
     if (GetTransparency() != 255) {
@@ -421,27 +198,15 @@
     }
   }
 
-  CFX_FloatRect rcMinArea, rcMaxArea;
-
   if (m_pPosButton && m_pPosButton->IsVisible()) {
     CFX_FloatRect rcClient = GetClientRect();
     CFX_FloatRect rcPosButton = m_pPosButton->GetWindowRect();
-
-    switch (m_sbType) {
-      case SBT_HSCROLL:
-        rcMinArea = CFX_FloatRect(rcClient.left + kButtonWidth, rcClient.bottom,
-                                  rcPosButton.left, rcClient.top);
-        rcMaxArea = CFX_FloatRect(rcPosButton.right, rcClient.bottom,
-                                  rcClient.right - kButtonWidth, rcClient.top);
-
-        break;
-      case SBT_VSCROLL:
-        rcMinArea = CFX_FloatRect(rcClient.left, rcPosButton.top,
-                                  rcClient.right, rcClient.top - kButtonWidth);
-        rcMaxArea = CFX_FloatRect(rcClient.left, rcClient.bottom + kButtonWidth,
-                                  rcClient.right, rcPosButton.bottom);
-        break;
-    }
+    CFX_FloatRect rcMinArea =
+        CFX_FloatRect(rcClient.left, rcPosButton.top, rcClient.right,
+                      rcClient.top - kButtonWidth);
+    CFX_FloatRect rcMaxArea =
+        CFX_FloatRect(rcClient.left, rcClient.bottom + kButtonWidth,
+                      rcClient.right, rcPosButton.bottom);
 
     rcMinArea.Normalize();
     rcMaxArea.Normalize();
@@ -464,12 +229,13 @@
   return true;
 }
 
-bool CPWL_ScrollBar::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
-  CPWL_Wnd::OnLButtonUp(point, nFlag);
+bool CPWL_ScrollBar::OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag,
+                                 const CFX_PointF& point) {
+  CPWL_Wnd::OnLButtonUp(nFlag, point);
 
   if (HasFlag(PWS_AUTOTRANSPARENT)) {
-    if (GetTransparency() != PWL_SCROLLBAR_TRANSPARENCY) {
-      SetTransparency(PWL_SCROLLBAR_TRANSPARENCY);
+    if (GetTransparency() != kTransparency) {
+      SetTransparency(kTransparency);
       if (!InvalidateRect(nullptr))
         return true;
     }
@@ -492,14 +258,7 @@
 }
 
 void CPWL_ScrollBar::SetScrollPosition(float pos) {
-  switch (m_sbType) {
-    case SBT_HSCROLL:
-      pos = pos - m_OriginInfo.fContentMin;
-      break;
-    case SBT_VSCROLL:
-      pos = m_OriginInfo.fContentMax - pos;
-      break;
-  }
+  pos = m_OriginInfo.fContentMax - pos;
   SetScrollPos(pos);
 }
 
@@ -533,32 +292,31 @@
 void CPWL_ScrollBar::CreateButtons(const CreateParams& cp) {
   CreateParams scp = cp;
   scp.dwBorderWidth = 2;
-  scp.nBorderStyle = BorderStyle::BEVELED;
-  scp.dwFlags =
-      PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PWS_BACKGROUND | PWS_NOREFRESHCLIP;
+  scp.nBorderStyle = BorderStyle::kBeveled;
+  scp.dwFlags = PWS_VISIBLE | PWS_BORDER | PWS_BACKGROUND | PWS_NOREFRESHCLIP;
 
   if (!m_pMinButton) {
-    auto pButton = pdfium::MakeUnique<CPWL_SBButton>(scp, CloneAttachedData(),
-                                                     m_sbType, PSBT_MIN);
+    auto pButton = std::make_unique<CPWL_SBButton>(
+        scp, CloneAttachedData(), CPWL_SBButton::Type::kMinButton);
     m_pMinButton = pButton.get();
     AddChild(std::move(pButton));
     m_pMinButton->Realize();
   }
 
   if (!m_pMaxButton) {
-    auto pButton = pdfium::MakeUnique<CPWL_SBButton>(scp, CloneAttachedData(),
-                                                     m_sbType, PSBT_MAX);
+    auto pButton = std::make_unique<CPWL_SBButton>(
+        scp, CloneAttachedData(), CPWL_SBButton::Type::kMaxButton);
     m_pMaxButton = pButton.get();
     AddChild(std::move(pButton));
     m_pMaxButton->Realize();
   }
 
   if (!m_pPosButton) {
-    auto pButton = pdfium::MakeUnique<CPWL_SBButton>(scp, CloneAttachedData(),
-                                                     m_sbType, PSBT_POS);
+    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();
     }
@@ -566,10 +324,7 @@
 }
 
 float CPWL_ScrollBar::GetScrollBarWidth() const {
-  if (!IsVisible())
-    return 0;
-
-  return PWL_SCROLLBAR_WIDTH;
+  return IsVisible() ? kWidth : 0.0f;
 }
 
 void CPWL_ScrollBar::SetScrollRange(float fMin,
@@ -578,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 (IsFloatSmaller(m_sData.ScrollRange.GetWidth(), 0.0f)) {
-    m_pPosButton->SetVisible(false);
+  if (FXSYS_IsFloatSmaller(m_sData.ScrollRange.GetWidth(), 0.0f)) {
+    (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().
 }
@@ -600,8 +356,8 @@
 void CPWL_ScrollBar::SetScrollPos(float fPos) {
   float fOldPos = m_sData.fScrollPos;
   m_sData.SetPos(fPos);
-  if (!IsFloatEqual(m_sData.fScrollPos, fOldPos)) {
-    MovePosButton(true);
+  if (!FXSYS_IsFloatEqual(m_sData.fScrollPos, fOldPos)) {
+    (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().
   }
@@ -613,57 +369,30 @@
 }
 
 bool CPWL_ScrollBar::MovePosButton(bool bRefresh) {
-  ASSERT(m_pMinButton);
-  ASSERT(m_pMaxButton);
+  DCHECK(m_pMinButton);
+  DCHECK(m_pMaxButton);
 
   if (m_pPosButton->IsVisible()) {
-    CFX_FloatRect rcClient;
-    CFX_FloatRect rcPosArea, rcPosButton;
+    CFX_FloatRect rcPosArea = GetScrollArea();
+    float fBottom = TrueToFace(m_sData.fScrollPos + m_sData.fClientWidth);
+    float fTop = TrueToFace(m_sData.fScrollPos);
 
-    rcClient = GetClientRect();
-    rcPosArea = GetScrollArea();
+    if (FXSYS_IsFloatSmaller(fTop - fBottom, kPosButtonMinWidth))
+      fBottom = fTop - kPosButtonMinWidth;
 
-    float fLeft, fRight, fTop, fBottom;
-
-    switch (m_sbType) {
-      case SBT_HSCROLL:
-        fLeft = TrueToFace(m_sData.fScrollPos);
-        fRight = TrueToFace(m_sData.fScrollPos + m_sData.fClientWidth);
-
-        if (fRight - fLeft < kPosButtonMinWidth)
-          fRight = fLeft + kPosButtonMinWidth;
-
-        if (fRight > rcPosArea.right) {
-          fRight = rcPosArea.right;
-          fLeft = fRight - kPosButtonMinWidth;
-        }
-
-        rcPosButton =
-            CFX_FloatRect(fLeft, rcPosArea.bottom, fRight, rcPosArea.top);
-
-        break;
-      case SBT_VSCROLL:
-        fBottom = TrueToFace(m_sData.fScrollPos + m_sData.fClientWidth);
-        fTop = TrueToFace(m_sData.fScrollPos);
-
-        if (IsFloatSmaller(fTop - fBottom, kPosButtonMinWidth))
-          fBottom = fTop - kPosButtonMinWidth;
-
-        if (IsFloatSmaller(fBottom, rcPosArea.bottom)) {
-          fBottom = rcPosArea.bottom;
-          fTop = fBottom + kPosButtonMinWidth;
-        }
-
-        rcPosButton =
-            CFX_FloatRect(rcPosArea.left, fBottom, rcPosArea.right, fTop);
-
-        break;
+    if (FXSYS_IsFloatSmaller(fBottom, rcPosArea.bottom)) {
+      fBottom = rcPosArea.bottom;
+      fTop = fBottom + kPosButtonMinWidth;
     }
 
-    ObservedPtr<CPWL_ScrollBar> thisObserved(this);
+    CFX_FloatRect rcPosButton =
+        CFX_FloatRect(rcPosArea.left, fBottom, rcPosArea.right, fTop);
+
+    ObservedPtr<CPWL_ScrollBar> this_observed(this);
     m_pPosButton->Move(rcPosButton, true, bRefresh);
-    if (!thisObserved)
+    if (!this_observed) {
       return false;
+    }
   }
 
   return true;
@@ -676,7 +405,7 @@
 
   NotifyScrollWindow();
   m_bMinOrMax = true;
-  m_pTimer = pdfium::MakeUnique<CFX_Timer>(GetTimerHandler(), this, 100);
+  m_pTimer = std::make_unique<CFX_Timer>(GetTimerHandler(), this, 100);
 }
 
 void CPWL_ScrollBar::OnMinButtonLBUp(const CFX_PointF& point) {}
@@ -690,7 +419,7 @@
 
   NotifyScrollWindow();
   m_bMinOrMax = false;
-  m_pTimer = pdfium::MakeUnique<CFX_Timer>(GetTimerHandler(), this, 100);
+  m_pTimer = std::make_unique<CFX_Timer>(GetTimerHandler(), this, 100);
 }
 
 void CPWL_ScrollBar::OnMaxButtonLBUp(const CFX_PointF& point) {}
@@ -702,89 +431,44 @@
 
   if (m_pPosButton) {
     CFX_FloatRect rcPosButton = m_pPosButton->GetWindowRect();
-
-    switch (m_sbType) {
-      case SBT_HSCROLL:
-        m_nOldPos = point.x;
-        m_fOldPosButton = rcPosButton.left;
-        break;
-      case SBT_VSCROLL:
-        m_nOldPos = point.y;
-        m_fOldPosButton = rcPosButton.top;
-        break;
-    }
+    m_nOldPos = point.y;
+    m_fOldPosButton = rcPosButton.top;
   }
 }
 
 void CPWL_ScrollBar::OnPosButtonLBUp(const CFX_PointF& point) {
-  if (m_bMouseDown) {
-    if (!m_bNotifyForever)
-      NotifyScrollWindow();
-  }
   m_bMouseDown = false;
 }
 
 void CPWL_ScrollBar::OnPosButtonMouseMove(const CFX_PointF& point) {
+  if (fabs(point.y - m_nOldPos) < 1)
+    return;
+
   float fOldScrollPos = m_sData.fScrollPos;
-
-  float fNewPos = 0;
-
-  switch (m_sbType) {
-    case SBT_HSCROLL:
-      if (fabs(point.x - m_nOldPos) < 1)
-        return;
-      fNewPos = FaceToTrue(m_fOldPosButton + point.x - m_nOldPos);
-      break;
-    case SBT_VSCROLL:
-      if (fabs(point.y - m_nOldPos) < 1)
-        return;
-      fNewPos = FaceToTrue(m_fOldPosButton + point.y - m_nOldPos);
-      break;
-  }
-
+  float fNewPos = FaceToTrue(m_fOldPosButton + point.y - m_nOldPos);
   if (m_bMouseDown) {
-    switch (m_sbType) {
-      case SBT_HSCROLL:
-
-        if (IsFloatSmaller(fNewPos, m_sData.ScrollRange.fMin)) {
-          fNewPos = m_sData.ScrollRange.fMin;
-        }
-
-        if (IsFloatBigger(fNewPos, m_sData.ScrollRange.fMax)) {
-          fNewPos = m_sData.ScrollRange.fMax;
-        }
-
-        m_sData.SetPos(fNewPos);
-
-        break;
-      case SBT_VSCROLL:
-
-        if (IsFloatSmaller(fNewPos, m_sData.ScrollRange.fMin)) {
-          fNewPos = m_sData.ScrollRange.fMin;
-        }
-
-        if (IsFloatBigger(fNewPos, m_sData.ScrollRange.fMax)) {
-          fNewPos = m_sData.ScrollRange.fMax;
-        }
-
-        m_sData.SetPos(fNewPos);
-
-        break;
+    if (FXSYS_IsFloatSmaller(fNewPos, m_sData.ScrollRange.fMin)) {
+      fNewPos = m_sData.ScrollRange.fMin;
     }
 
-    if (!IsFloatEqual(fOldScrollPos, m_sData.fScrollPos)) {
+    if (FXSYS_IsFloatBigger(fNewPos, m_sData.ScrollRange.fMax)) {
+      fNewPos = m_sData.ScrollRange.fMax;
+    }
+
+    m_sData.SetPos(fNewPos);
+
+    if (!FXSYS_IsFloatEqual(fOldScrollPos, m_sData.fScrollPos)) {
       if (!MovePosButton(true))
         return;
 
-      if (m_bNotifyForever)
-        NotifyScrollWindow();
+      NotifyScrollWindow();
     }
   }
 }
 
 void CPWL_ScrollBar::NotifyScrollWindow() {
   CPWL_Wnd* pParent = GetParentWindow();
-  if (!pParent || m_sbType != SBT_VSCROLL)
+  if (!pParent)
     return;
 
   pParent->ScrollWindowVertically(m_OriginInfo.fContentMax -
@@ -793,90 +477,41 @@
 
 CFX_FloatRect CPWL_ScrollBar::GetScrollArea() const {
   CFX_FloatRect rcClient = GetClientRect();
-  CFX_FloatRect rcArea;
-
   if (!m_pMinButton || !m_pMaxButton)
     return rcClient;
 
   CFX_FloatRect rcMin = m_pMinButton->GetWindowRect();
   CFX_FloatRect rcMax = m_pMaxButton->GetWindowRect();
-
-  float fMinWidth = rcMin.Width();
   float fMinHeight = rcMin.Height();
-  float fMaxWidth = rcMax.Width();
   float fMaxHeight = rcMax.Height();
 
-  switch (m_sbType) {
-    case SBT_HSCROLL:
-      if (rcClient.right - rcClient.left > fMinWidth + fMaxWidth + 2) {
-        rcArea = CFX_FloatRect(rcClient.left + fMinWidth + 1, rcClient.bottom,
-                               rcClient.right - fMaxWidth - 1, rcClient.top);
-      } else {
-        rcArea = CFX_FloatRect(rcClient.left + fMinWidth + 1, rcClient.bottom,
-                               rcClient.left + fMinWidth + 1, rcClient.top);
-      }
-      break;
-    case SBT_VSCROLL:
-      if (rcClient.top - rcClient.bottom > fMinHeight + fMaxHeight + 2) {
-        rcArea = CFX_FloatRect(rcClient.left, rcClient.bottom + fMinHeight + 1,
-                               rcClient.right, rcClient.top - fMaxHeight - 1);
-      } else {
-        rcArea =
-            CFX_FloatRect(rcClient.left, rcClient.bottom + fMinHeight + 1,
-                          rcClient.right, rcClient.bottom + fMinHeight + 1);
-      }
-      break;
+  CFX_FloatRect rcArea;
+  if (rcClient.top - rcClient.bottom > fMinHeight + fMaxHeight + 2) {
+    rcArea = CFX_FloatRect(rcClient.left, rcClient.bottom + fMinHeight + 1,
+                           rcClient.right, rcClient.top - fMaxHeight - 1);
+  } else {
+    rcArea = CFX_FloatRect(rcClient.left, rcClient.bottom + fMinHeight + 1,
+                           rcClient.right, rcClient.bottom + fMinHeight + 1);
   }
 
   rcArea.Normalize();
-
   return rcArea;
 }
 
 float CPWL_ScrollBar::TrueToFace(float fTrue) {
-  CFX_FloatRect rcPosArea;
-  rcPosArea = GetScrollArea();
-
+  CFX_FloatRect rcPosArea = GetScrollArea();
   float fFactWidth = m_sData.ScrollRange.GetWidth() + m_sData.fClientWidth;
   fFactWidth = fFactWidth == 0 ? 1 : fFactWidth;
-
-  float fFace = 0;
-
-  switch (m_sbType) {
-    case SBT_HSCROLL:
-      fFace = rcPosArea.left +
-              fTrue * (rcPosArea.right - rcPosArea.left) / fFactWidth;
-      break;
-    case SBT_VSCROLL:
-      fFace = rcPosArea.top -
-              fTrue * (rcPosArea.top - rcPosArea.bottom) / fFactWidth;
-      break;
-  }
-
-  return fFace;
+  return rcPosArea.top -
+         fTrue * (rcPosArea.top - rcPosArea.bottom) / fFactWidth;
 }
 
 float CPWL_ScrollBar::FaceToTrue(float fFace) {
-  CFX_FloatRect rcPosArea;
-  rcPosArea = GetScrollArea();
-
+  CFX_FloatRect rcPosArea = GetScrollArea();
   float fFactWidth = m_sData.ScrollRange.GetWidth() + m_sData.fClientWidth;
   fFactWidth = fFactWidth == 0 ? 1 : fFactWidth;
-
-  float fTrue = 0;
-
-  switch (m_sbType) {
-    case SBT_HSCROLL:
-      fTrue = (fFace - rcPosArea.left) * fFactWidth /
-              (rcPosArea.right - rcPosArea.left);
-      break;
-    case SBT_VSCROLL:
-      fTrue = (rcPosArea.top - fFace) * fFactWidth /
-              (rcPosArea.top - rcPosArea.bottom);
-      break;
-  }
-
-  return fTrue;
+  return (rcPosArea.top - fFace) * fFactWidth /
+         (rcPosArea.top - rcPosArea.bottom);
 }
 
 void CPWL_ScrollBar::CreateChildWnd(const CreateParams& cp) {
diff --git a/fpdfsdk/pwl/cpwl_scroll_bar.h b/fpdfsdk/pwl/cpwl_scroll_bar.h
index f6bb2b9..91a7516 100644
--- a/fpdfsdk/pwl/cpwl_scroll_bar.h
+++ b/fpdfsdk/pwl/cpwl_scroll_bar.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 
 #include "core/fxcrt/cfx_timer.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "fpdfsdk/pwl/cpwl_sbbutton.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 
 struct PWL_SCROLL_INFO {
@@ -38,32 +39,6 @@
   float fSmallStep;
 };
 
-enum PWL_SCROLLBAR_TYPE { SBT_HSCROLL, SBT_VSCROLL };
-
-enum PWL_SBBUTTON_TYPE { PSBT_MIN, PSBT_MAX, PSBT_POS };
-
-class CPWL_SBButton final : public CPWL_Wnd {
- public:
-  CPWL_SBButton(
-      const CreateParams& cp,
-      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData,
-      PWL_SCROLLBAR_TYPE eScrollBarType,
-      PWL_SBBUTTON_TYPE eButtonType);
-  ~CPWL_SBButton() override;
-
-  // CPWL_Wnd
-  void DrawThisAppearance(CFX_RenderDevice* pDevice,
-                          const CFX_Matrix& mtUser2Device) override;
-  bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override;
-  bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override;
-  bool OnMouseMove(const CFX_PointF& point, uint32_t nFlag) override;
-
- private:
-  PWL_SCROLLBAR_TYPE m_eScrollBarType;
-  PWL_SBBUTTON_TYPE m_eSBButtonType;
-  bool m_bMouseDown = false;
-};
-
 struct PWL_FLOATRANGE {
  public:
   PWL_FLOATRANGE() = default;
@@ -116,19 +91,22 @@
 
 class CPWL_ScrollBar final : public CPWL_Wnd, public CFX_Timer::CallbackIface {
  public:
+  static constexpr float kWidth = 12.0f;
+  static constexpr uint8_t kTransparency = 150;
+
   CPWL_ScrollBar(
       const CreateParams& cp,
-      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData,
-      PWL_SCROLLBAR_TYPE sbType);
+      std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData);
   ~CPWL_ScrollBar() override;
 
   // CPWL_Wnd:
   void OnDestroy() override;
-  bool RePosChildWnd() override;
+  bool RepositionChildWnd() override;
   void DrawThisAppearance(CFX_RenderDevice* pDevice,
                           const CFX_Matrix& mtUser2Device) override;
-  bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override;
-  bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override;
+  bool OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,
+                     const CFX_PointF& point) override;
+  bool OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag, const CFX_PointF& point) override;
   void SetScrollInfo(const PWL_SCROLL_INFO& info) override;
   void SetScrollPosition(float pos) override;
   void NotifyLButtonDown(CPWL_Wnd* child, const CFX_PointF& pos) override;
@@ -140,16 +118,13 @@
   void OnTimerFired() override;
 
   float GetScrollBarWidth() const;
-  PWL_SCROLLBAR_TYPE GetScrollBarType() const { return m_sbType; }
-
-  void SetNotifyForever(bool bForever) { m_bNotifyForever = bForever; }
 
  private:
   void SetScrollRange(float fMin, float fMax, float fClientWidth);
   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;
@@ -171,7 +146,6 @@
   float TrueToFace(float);
   float FaceToTrue(float);
 
-  PWL_SCROLLBAR_TYPE m_sbType;
   PWL_SCROLL_INFO m_OriginInfo;
   UnownedPtr<CPWL_SBButton> m_pMinButton;
   UnownedPtr<CPWL_SBButton> m_pMaxButton;
@@ -180,7 +154,6 @@
   PWL_SCROLL_PRIVATEDATA m_sData;
   bool m_bMouseDown = false;
   bool m_bMinOrMax = false;
-  bool m_bNotifyForever = true;
   float m_nOldPos = 0.0f;
   float m_fOldPosButton = 0.0f;
 };
diff --git a/fpdfsdk/pwl/cpwl_special_button.cpp b/fpdfsdk/pwl/cpwl_special_button.cpp
index 3fbf919..17f209a 100644
--- a/fpdfsdk/pwl/cpwl_special_button.cpp
+++ b/fpdfsdk/pwl/cpwl_special_button.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,7 +13,7 @@
 
 CPWL_PushButton::CPWL_PushButton(
     const CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData)
     : CPWL_Button(cp, std::move(pAttachedData)) {}
 
 CPWL_PushButton::~CPWL_PushButton() = default;
@@ -25,12 +25,13 @@
 
 CPWL_CheckBox::CPWL_CheckBox(
     const CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData)
     : CPWL_Button(cp, std::move(pAttachedData)) {}
 
 CPWL_CheckBox::~CPWL_CheckBox() = default;
 
-bool CPWL_CheckBox::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
+bool CPWL_CheckBox::OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag,
+                                const CFX_PointF& point) {
   if (IsReadOnly())
     return false;
 
@@ -38,19 +39,23 @@
   return true;
 }
 
-bool CPWL_CheckBox::OnChar(uint16_t nChar, uint32_t nFlag) {
+bool CPWL_CheckBox::OnChar(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) {
+  if (IsReadOnly())
+    return false;
+
   SetCheck(!IsChecked());
   return true;
 }
 
 CPWL_RadioButton::CPWL_RadioButton(
     const CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData)
     : CPWL_Button(cp, std::move(pAttachedData)) {}
 
 CPWL_RadioButton::~CPWL_RadioButton() = default;
 
-bool CPWL_RadioButton::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
+bool CPWL_RadioButton::OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag,
+                                   const CFX_PointF& point) {
   if (IsReadOnly())
     return false;
 
@@ -58,7 +63,10 @@
   return true;
 }
 
-bool CPWL_RadioButton::OnChar(uint16_t nChar, uint32_t nFlag) {
+bool CPWL_RadioButton::OnChar(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) {
+  if (IsReadOnly())
+    return false;
+
   SetCheck(true);
   return true;
 }
diff --git a/fpdfsdk/pwl/cpwl_special_button.h b/fpdfsdk/pwl/cpwl_special_button.h
index 3d9fa25..8720ef1 100644
--- a/fpdfsdk/pwl/cpwl_special_button.h
+++ b/fpdfsdk/pwl/cpwl_special_button.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,7 +15,7 @@
  public:
   CPWL_PushButton(
       const CreateParams& cp,
-      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
+      std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData);
   ~CPWL_PushButton() override;
 
   // CPWL_Button:
@@ -26,12 +26,12 @@
  public:
   CPWL_CheckBox(
       const CreateParams& cp,
-      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
+      std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData);
   ~CPWL_CheckBox() override;
 
   // CPWL_Button:
-  bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override;
-  bool OnChar(uint16_t nChar, uint32_t nFlag) override;
+  bool OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag, const CFX_PointF& point) override;
+  bool OnChar(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) override;
 
   bool IsChecked() const { return m_bChecked; }
   void SetCheck(bool bCheck) { m_bChecked = bCheck; }
@@ -44,12 +44,12 @@
  public:
   CPWL_RadioButton(
       const CreateParams& cp,
-      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
+      std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData);
   ~CPWL_RadioButton() override;
 
   // CPWL_Button
-  bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override;
-  bool OnChar(uint16_t nChar, uint32_t nFlag) override;
+  bool OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag, const CFX_PointF& point) override;
+  bool OnChar(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) override;
 
   bool IsChecked() const { return m_bChecked; }
   void SetCheck(bool bCheck) { m_bChecked = bCheck; }
diff --git a/fpdfsdk/pwl/cpwl_special_button_embeddertest.cpp b/fpdfsdk/pwl/cpwl_special_button_embeddertest.cpp
new file mode 100644
index 0000000..c12f037
--- /dev/null
+++ b/fpdfsdk/pwl/cpwl_special_button_embeddertest.cpp
@@ -0,0 +1,147 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fpdfsdk/cpdfsdk_annotiterator.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/cpdfsdk_widget.h"
+#include "fpdfsdk/formfiller/cffl_formfield.h"
+#include "fpdfsdk/pwl/cpwl_special_button.h"
+#include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "testing/embedder_test.h"
+
+class CPWLSpecialButtonEmbedderTest : public EmbedderTest {
+ protected:
+  void SetUp() override {
+    EmbedderTest::SetUp();
+    CreateAndInitializeFormPDF();
+  }
+
+  void TearDown() override {
+    UnloadPage(page_);
+    EmbedderTest::TearDown();
+  }
+
+  void CreateAndInitializeFormPDF() {
+    ASSERT_TRUE(OpenDocument("click_form.pdf"));
+
+    page_ = LoadPage(0);
+    ASSERT_TRUE(page_);
+
+    formfill_env_ = CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle());
+    CPDFSDK_AnnotIterator it(formfill_env_->GetPageViewAtIndex(0),
+                             {CPDF_Annot::Subtype::WIDGET});
+
+    // Read only check box.
+    widget_readonly_checkbox_ = ToCPDFSDKWidget(it.GetFirstAnnot());
+    ASSERT_TRUE(widget_readonly_checkbox_);
+    ASSERT_EQ(CPDF_Annot::Subtype::WIDGET,
+              widget_readonly_checkbox_->GetAnnotSubtype());
+
+    // Check box.
+    widget_checkbox_ =
+        ToCPDFSDKWidget(it.GetNextAnnot(widget_readonly_checkbox_));
+    ASSERT_TRUE(widget_checkbox_);
+    ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, widget_checkbox_->GetAnnotSubtype());
+
+    // Read only radio button.
+    widget_readonly_radiobutton_ =
+        ToCPDFSDKWidget(it.GetNextAnnot(widget_checkbox_));
+    ASSERT_TRUE(widget_readonly_radiobutton_);
+    ASSERT_EQ(CPDF_Annot::Subtype::WIDGET,
+              widget_readonly_radiobutton_->GetAnnotSubtype());
+
+    // Tabbing three times from read only radio button to unselected normal
+    // radio button.
+    widget_radiobutton_ = widget_readonly_radiobutton_;
+    ASSERT_TRUE(widget_radiobutton_);
+    for (int i = 0; i < 3; i++) {
+      widget_radiobutton_ =
+          ToCPDFSDKWidget(it.GetNextAnnot(widget_radiobutton_));
+      ASSERT_TRUE(widget_radiobutton_);
+    }
+
+    ASSERT_EQ(CPDF_Annot::Subtype::WIDGET,
+              widget_radiobutton_->GetAnnotSubtype());
+  }
+
+  void FormFillerAndWindowSetup(CPDFSDK_Widget* widget) {
+    CFFL_InteractiveFormFiller* interactive_formfiller =
+        formfill_env_->GetInteractiveFormFiller();
+    {
+      ObservedPtr<CPDFSDK_Widget> observed(widget);
+      EXPECT_TRUE(interactive_formfiller->OnSetFocus(observed, {}));
+    }
+
+    form_filler_ = interactive_formfiller->GetFormFieldForTesting(widget);
+    ASSERT_TRUE(form_filler_);
+
+    window_ = form_filler_->CreateOrUpdatePWLWindow(
+        formfill_env_->GetPageViewAtIndex(0));
+    ASSERT_TRUE(window_);
+  }
+
+  CPDFSDK_Widget* GetCPDFSDKWidgetCheckBox() const { return widget_checkbox_; }
+  CPDFSDK_Widget* GetCPDFSDKWidgetReadOnlyCheckBox() const {
+    return widget_readonly_checkbox_;
+  }
+  CPDFSDK_Widget* GetCPDFSDKWidgetRadioButton() const {
+    return widget_radiobutton_;
+  }
+  CPDFSDK_Widget* GetCPDFSDKWidgetReadOnlyRadioButton() const {
+    return widget_readonly_radiobutton_;
+  }
+  CPDFSDK_FormFillEnvironment* GetCPDFSDKFormFillEnv() const {
+    return formfill_env_;
+  }
+  CPWL_Wnd* GetWindow() const { return window_; }
+
+ private:
+  FPDF_PAGE page_;
+  CFFL_FormField* form_filler_;
+  CPDFSDK_Widget* widget_checkbox_;
+  CPDFSDK_Widget* widget_readonly_checkbox_;
+  CPDFSDK_Widget* widget_radiobutton_;
+  CPDFSDK_Widget* widget_readonly_radiobutton_;
+  CPDFSDK_FormFillEnvironment* formfill_env_;
+  CPWL_Wnd* window_;
+};
+
+TEST_F(CPWLSpecialButtonEmbedderTest, EnterOnReadOnlyCheckBox) {
+  FormFillerAndWindowSetup(GetCPDFSDKWidgetReadOnlyCheckBox());
+  CPWL_CheckBox* check_box = static_cast<CPWL_CheckBox*>(GetWindow());
+  EXPECT_TRUE(check_box->IsChecked());
+  EXPECT_TRUE(GetCPDFSDKFormFillEnv()->GetInteractiveFormFiller()->OnChar(
+      GetCPDFSDKWidgetReadOnlyCheckBox(), '\r', {}));
+  EXPECT_TRUE(check_box->IsChecked());
+}
+
+TEST_F(CPWLSpecialButtonEmbedderTest, EnterOnCheckBox) {
+  FormFillerAndWindowSetup(GetCPDFSDKWidgetCheckBox());
+  CPWL_CheckBox* check_box = static_cast<CPWL_CheckBox*>(GetWindow());
+  EXPECT_TRUE(GetCPDFSDKFormFillEnv()->GetInteractiveFormFiller()->OnChar(
+      GetCPDFSDKWidgetCheckBox(), '\r', {}));
+  EXPECT_TRUE(check_box->IsChecked());
+
+  EXPECT_TRUE(GetCPDFSDKFormFillEnv()->GetInteractiveFormFiller()->OnChar(
+      GetCPDFSDKWidgetCheckBox(), '\r', {}));
+  EXPECT_FALSE(check_box->IsChecked());
+}
+
+TEST_F(CPWLSpecialButtonEmbedderTest, EnterOnReadOnlyRadioButton) {
+  FormFillerAndWindowSetup(GetCPDFSDKWidgetReadOnlyRadioButton());
+  CPWL_RadioButton* radio_button = static_cast<CPWL_RadioButton*>(GetWindow());
+  EXPECT_FALSE(radio_button->IsChecked());
+  EXPECT_TRUE(GetCPDFSDKFormFillEnv()->GetInteractiveFormFiller()->OnChar(
+      GetCPDFSDKWidgetReadOnlyRadioButton(), '\r', {}));
+  EXPECT_FALSE(radio_button->IsChecked());
+}
+
+TEST_F(CPWLSpecialButtonEmbedderTest, EnterOnRadioButton) {
+  FormFillerAndWindowSetup(GetCPDFSDKWidgetRadioButton());
+  CPWL_RadioButton* radio_button = static_cast<CPWL_RadioButton*>(GetWindow());
+  EXPECT_TRUE(GetCPDFSDKFormFillEnv()->GetInteractiveFormFiller()->OnChar(
+      GetCPDFSDKWidgetRadioButton(), '\r', {}));
+  EXPECT_TRUE(radio_button->IsChecked());
+}
diff --git a/fpdfsdk/pwl/cpwl_wnd.cpp b/fpdfsdk/pwl/cpwl_wnd.cpp
index 0ba0e56..a2dafdb 100644
--- a/fpdfsdk/pwl/cpwl_wnd.cpp
+++ b/fpdfsdk/pwl/cpwl_wnd.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,16 +6,18 @@
 
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 
-#include <map>
 #include <sstream>
 #include <utility>
 #include <vector>
 
+#include "build/build_config.h"
+#include "core/fxcrt/stl_util.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "fpdfsdk/pwl/cpwl_scroll_bar.h"
 #include "public/fpdf_fwlevent.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/contains.h"
 
 namespace {
 
@@ -23,24 +25,40 @@
 
 }  // namespace
 
-CPWL_Wnd::CreateParams::CreateParams()
-    : fFontSize(kDefaultFontSize), sDash(3, 0, 0) {}
+// static
+const CFX_Color CPWL_Wnd::kDefaultBlackColor =
+    CFX_Color(CFX_Color::Type::kGray, 0);
+
+// static
+const CFX_Color CPWL_Wnd::kDefaultWhiteColor =
+    CFX_Color(CFX_Color::Type::kGray, 1);
+
+CPWL_Wnd::CreateParams::CreateParams(CFX_Timer::HandlerIface* timer_handler,
+                                     IPWL_FillerNotify* filler_notify,
+                                     ProviderIface* provider)
+    : pTimerHandler(timer_handler),
+      pFillerNotify(filler_notify),
+      pProvider(provider),
+      fFontSize(kDefaultFontSize),
+      sDash(3, 0, 0) {}
 
 CPWL_Wnd::CreateParams::CreateParams(const CreateParams& other) = default;
 
 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(CPWL_Wnd* pWnd) : m_pCreatedWnd(pWnd) {}
-  ~CPWL_MsgControl() {}
+  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::ContainsValue(m_aMousePath, pWnd);
+    return pWnd && pdfium::Contains(m_MousePaths, pWnd);
   }
 
   bool IsMainCaptureKeyboard(const CPWL_Wnd* pWnd) const {
@@ -48,84 +66,99 @@
   }
 
   bool IsWndCaptureKeyboard(const CPWL_Wnd* pWnd) const {
-    return pWnd && pdfium::ContainsValue(m_aKeyboardPath, pWnd);
+    return pWnd && pdfium::Contains(m_KeyboardPaths, pWnd);
   }
 
-  void SetFocus(CPWL_Wnd* pWnd) {
-    m_aKeyboardPath.clear();
-    if (!pWnd)
-      return;
+  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;
-    CPWL_Wnd* pParent = pWnd;
-    while (pParent) {
-      m_aKeyboardPath.push_back(pParent);
-      pParent = pParent->GetParentWindow();
-    }
+
     // Note, pWnd may get destroyed in the OnSetFocus call.
     pWnd->OnSetFocus();
   }
 
-  void KillFocus() {
-    ObservedPtr<CPWL_MsgControl> observed_ptr(this);
-    if (!m_aKeyboardPath.empty())
-      if (CPWL_Wnd* pWnd = m_aKeyboardPath[0])
+  void ReleaseFocus() {
+    ObservedPtr<SharedCaptureFocusState> observed_ptr(this);
+    if (!m_KeyboardPaths.empty()) {
+      CPWL_Wnd* pWnd = m_KeyboardPaths.front();
+      if (pWnd)
         pWnd->OnKillFocus();
+    }
     if (!observed_ptr)
       return;
 
     m_pMainKeyboardWnd = nullptr;
-    m_aKeyboardPath.clear();
+    m_KeyboardPaths.clear();
   }
 
-  void SetCapture(CPWL_Wnd* pWnd) {
-    m_aMousePath.clear();
-    if (pWnd) {
-      CPWL_Wnd* pParent = pWnd;
-      while (pParent) {
-        m_aMousePath.push_back(pParent);
-        pParent = pParent->GetParentWindow();
-      }
+  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);
     }
   }
 
-  void ReleaseCapture() { m_aMousePath.clear(); }
-
-  CPWL_Wnd* GetFocusedWindow() const { return m_pMainKeyboardWnd.Get(); }
-
  private:
-  std::vector<CPWL_Wnd*> m_aMousePath;
-  std::vector<CPWL_Wnd*> m_aKeyboardPath;
-  UnownedPtr<CPWL_Wnd> m_pCreatedWnd;
-  UnownedPtr<CPWL_Wnd> m_pMainKeyboardWnd;
+  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;
 };
 
 // static
-bool CPWL_Wnd::IsSHIFTKeyDown(uint32_t nFlag) {
+bool CPWL_Wnd::IsSHIFTKeyDown(Mask<FWL_EVENTFLAG> nFlag) {
   return !!(nFlag & FWL_EVENTFLAG_ShiftKey);
 }
 
 // static
-bool CPWL_Wnd::IsCTRLKeyDown(uint32_t nFlag) {
+bool CPWL_Wnd::IsCTRLKeyDown(Mask<FWL_EVENTFLAG> nFlag) {
   return !!(nFlag & FWL_EVENTFLAG_ControlKey);
 }
 
 // static
-bool CPWL_Wnd::IsALTKeyDown(uint32_t nFlag) {
+bool CPWL_Wnd::IsALTKeyDown(Mask<FWL_EVENTFLAG> nFlag) {
   return !!(nFlag & FWL_EVENTFLAG_AltKey);
 }
 
+// static
+bool CPWL_Wnd::IsMETAKeyDown(Mask<FWL_EVENTFLAG> nFlag) {
+  return !!(nFlag & FWL_EVENTFLAG_MetaKey);
+}
+
+// static
+bool CPWL_Wnd::IsPlatformShortcutKey(Mask<FWL_EVENTFLAG> nFlag) {
+#if BUILDFLAG(IS_APPLE)
+  return IsMETAKeyDown(nFlag);
+#else
+  return IsCTRLKeyDown(nFlag);
+#endif
+}
+
 CPWL_Wnd::CPWL_Wnd(
     const CreateParams& cp,
-    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData)
     : m_CreationParams(cp), m_pAttachedData(std::move(pAttachedData)) {}
 
 CPWL_Wnd::~CPWL_Wnd() {
-  ASSERT(!m_bCreated);
+  DCHECK(!m_bCreated);
 }
 
 void CPWL_Wnd::Realize() {
-  ASSERT(!m_bCreated);
+  DCHECK(!m_bCreated);
 
   m_CreationParams.rcRectWnd.Normalize();
   m_rcWindow = m_CreationParams.rcRectWnd;
@@ -134,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;
 }
@@ -152,11 +186,6 @@
 
 void CPWL_Wnd::OnDestroy() {}
 
-void CPWL_Wnd::InvalidateFocusHandler(FocusHandlerIface* handler) {
-  if (m_CreationParams.pFocusHandler == handler)
-    m_CreationParams.pFocusHandler = nullptr;
-}
-
 void CPWL_Wnd::InvalidateProvider(ProviderIface* provider) {
   if (m_CreationParams.pProvider.Get() == provider)
     m_CreationParams.pProvider.Reset();
@@ -176,7 +205,7 @@
       m_pParent->RemoveChild(this);
     m_bCreated = false;
   }
-  DestroyMsgControl();
+  DestroySharedCaptureFocusState();
 }
 
 bool CPWL_Wnd::Move(const CFX_FloatRect& rcNew, bool bReset, bool bRefresh) {
@@ -190,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))
@@ -241,21 +271,15 @@
 void CPWL_Wnd::DrawChildAppearance(CFX_RenderDevice* pDevice,
                                    const CFX_Matrix& mtUser2Device) {
   for (const auto& pChild : m_Children) {
-    CFX_Matrix mt = pChild->GetChildMatrix();
-    if (mt.IsIdentity()) {
-      pChild->DrawAppearance(pDevice, mtUser2Device);
-      continue;
-    }
-    mt.Concat(mtUser2Device);
-    pChild->DrawAppearance(pDevice, mt);
+    pChild->DrawAppearance(pDevice, mtUser2Device);
   }
 }
 
-bool CPWL_Wnd::InvalidateRect(CFX_FloatRect* pRect) {
+bool CPWL_Wnd::InvalidateRect(const CFX_FloatRect* pRect) {
   if (!IsValid())
     return true;
 
-  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();
@@ -266,49 +290,56 @@
   CFX_FloatRect rcWin = PWLtoWnd(rcRefresh);
   rcWin.Inflate(1, 1);
   rcWin.Normalize();
-  GetSystemHandler()->InvalidateRect(m_pAttachedData.get(), rcWin);
-  return !!thisObserved;
+  GetFillerNotify()->InvalidateRect(m_pAttachedData.get(), rcWin);
+  return !!this_observed;
 }
 
-#define PWL_IMPLEMENT_KEY_METHOD(key_method_name)                  \
-  bool CPWL_Wnd::key_method_name(uint16_t nChar, uint32_t nFlag) { \
-    if (!IsValid() || !IsVisible() || !IsEnabled())                \
-      return false;                                                \
-    if (!IsWndCaptureKeyboard(this))                               \
-      return false;                                                \
-    for (const auto& pChild : m_Children) {                        \
-      if (IsWndCaptureKeyboard(pChild.get()))                      \
-        return pChild->key_method_name(nChar, nFlag);              \
-    }                                                              \
-    return false;                                                  \
+bool CPWL_Wnd::OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlag) {
+  if (!IsValid() || !IsVisible())
+    return false;
+  if (!IsWndCaptureKeyboard(this))
+    return false;
+  for (const auto& pChild : m_Children) {
+    if (IsWndCaptureKeyboard(pChild.get()))
+      return pChild->OnKeyDown(nKeyCode, nFlag);
   }
+  return false;
+}
 
-PWL_IMPLEMENT_KEY_METHOD(OnKeyDown)
-PWL_IMPLEMENT_KEY_METHOD(OnChar)
-#undef PWL_IMPLEMENT_KEY_METHOD
+bool CPWL_Wnd::OnChar(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) {
+  if (!IsValid() || !IsVisible())
+    return false;
+  if (!IsWndCaptureKeyboard(this))
+    return false;
+  for (const auto& pChild : m_Children) {
+    if (IsWndCaptureKeyboard(pChild.get()))
+      return pChild->OnChar(nChar, nFlag);
+  }
+  return false;
+}
 
-#define PWL_IMPLEMENT_MOUSE_METHOD(mouse_method_name)                          \
-  bool CPWL_Wnd::mouse_method_name(const CFX_PointF& point, uint32_t nFlag) {  \
-    if (!IsValid() || !IsVisible() || !IsEnabled())                            \
-      return false;                                                            \
-    if (IsWndCaptureMouse(this)) {                                             \
-      for (const auto& pChild : m_Children) {                                  \
-        if (IsWndCaptureMouse(pChild.get())) {                                 \
-          return pChild->mouse_method_name(pChild->ParentToChild(point),       \
-                                           nFlag);                             \
-        }                                                                      \
-      }                                                                        \
-      SetCursor();                                                             \
-      return false;                                                            \
-    }                                                                          \
-    for (const auto& pChild : m_Children) {                                    \
-      if (pChild->WndHitTest(pChild->ParentToChild(point))) {                  \
-        return pChild->mouse_method_name(pChild->ParentToChild(point), nFlag); \
-      }                                                                        \
-    }                                                                          \
-    if (WndHitTest(point))                                                     \
-      SetCursor();                                                             \
-    return false;                                                              \
+#define PWL_IMPLEMENT_MOUSE_METHOD(mouse_method_name)         \
+  bool CPWL_Wnd::mouse_method_name(Mask<FWL_EVENTFLAG> nFlag, \
+                                   const CFX_PointF& point) { \
+    if (!IsValid() || !IsVisible())                           \
+      return false;                                           \
+    if (IsWndCaptureMouse(this)) {                            \
+      for (const auto& pChild : m_Children) {                 \
+        if (IsWndCaptureMouse(pChild.get())) {                \
+          return pChild->mouse_method_name(nFlag, point);     \
+        }                                                     \
+      }                                                       \
+      SetCursor();                                            \
+      return false;                                           \
+    }                                                         \
+    for (const auto& pChild : m_Children) {                   \
+      if (pChild->WndHitTest(point)) {                        \
+        return pChild->mouse_method_name(nFlag, point);       \
+      }                                                       \
+    }                                                         \
+    if (WndHitTest(point))                                    \
+      SetCursor();                                            \
+    return false;                                             \
   }
 
 PWL_IMPLEMENT_MOUSE_METHOD(OnLButtonDblClk)
@@ -318,11 +349,12 @@
 #undef PWL_IMPLEMENT_MOUSE_METHOD
 
 // Unlike their FWL counterparts, PWL windows don't handle right clicks.
-bool CPWL_Wnd::OnRButtonDown(const CFX_PointF& point, uint32_t nFlag) {
+bool CPWL_Wnd::OnRButtonDown(Mask<FWL_EVENTFLAG> nFlag,
+                             const CFX_PointF& point) {
   return false;
 }
 
-bool CPWL_Wnd::OnRButtonUp(const CFX_PointF& point, uint32_t nFlag) {
+bool CPWL_Wnd::OnRButtonUp(Mask<FWL_EVENTFLAG> nFlag, const CFX_PointF& point) {
   return false;
 }
 
@@ -334,8 +366,14 @@
   return WideString();
 }
 
+void CPWL_Wnd::ReplaceAndKeepSelection(const WideString& text) {}
+
 void CPWL_Wnd::ReplaceSelection(const WideString& text) {}
 
+bool CPWL_Wnd::SelectAllText() {
+  return false;
+}
+
 bool CPWL_Wnd::CanUndo() {
   return false;
 }
@@ -352,10 +390,10 @@
   return false;
 }
 
-bool CPWL_Wnd::OnMouseWheel(short zDelta,
+bool CPWL_Wnd::OnMouseWheel(Mask<FWL_EVENTFLAG> nFlag,
                             const CFX_PointF& point,
-                            uint32_t nFlag) {
-  if (!IsValid() || !IsVisible() || !IsEnabled())
+                            const CFX_Vector& delta) {
+  if (!IsValid() || !IsVisible())
     return false;
 
   SetCursor();
@@ -364,21 +402,21 @@
 
   for (const auto& pChild : m_Children) {
     if (IsWndCaptureKeyboard(pChild.get()))
-      return pChild->OnMouseWheel(zDelta, pChild->ParentToChild(point), nFlag);
+      return pChild->OnMouseWheel(nFlag, point, delta);
   }
   return false;
 }
 
 void CPWL_Wnd::AddChild(std::unique_ptr<CPWL_Wnd> pWnd) {
-  ASSERT(!pWnd->m_pParent);
+  DCHECK(!pWnd->m_pParent);
   pWnd->m_pParent = this;
   m_Children.push_back(std::move(pWnd));
 }
 
 void CPWL_Wnd::RemoveChild(CPWL_Wnd* pWnd) {
-  ASSERT(pWnd->m_pParent == this);
-  auto it = std::find(m_Children.begin(), m_Children.end(),
-                      pdfium::FakeUniquePtr<CPWL_Wnd>(pWnd));
+  DCHECK_EQ(pWnd->m_pParent, this);
+  auto it =
+      std::find(m_Children.begin(), m_Children.end(), MakeFakeUniquePtr(pWnd));
   if (it == m_Children.end())
     return;
 
@@ -429,18 +467,10 @@
   m_CreationParams.dwFlags &= ~dwFlags;
 }
 
-void CPWL_Wnd::AddFlag(uint32_t dwFlags) {
-  m_CreationParams.dwFlags |= dwFlags;
-}
-
 CFX_Color CPWL_Wnd::GetBackgroundColor() const {
   return m_CreationParams.sBackgroundColor;
 }
 
-void CPWL_Wnd::SetBackgroundColor(const CFX_Color& color) {
-  m_CreationParams.sBackgroundColor = color;
-}
-
 CFX_Color CPWL_Wnd::GetTextColor() const {
   return m_CreationParams.sTextColor;
 }
@@ -449,11 +479,6 @@
   return m_CreationParams.nBorderStyle;
 }
 
-void CPWL_Wnd::SetBorderStyle(BorderStyle nBorderStyle) {
-  if (HasFlag(PWS_BORDER))
-    m_CreationParams.nBorderStyle = nBorderStyle;
-}
-
 int32_t CPWL_Wnd::GetBorderWidth() const {
   return HasFlag(PWS_BORDER) ? m_CreationParams.dwBorderWidth : 0;
 }
@@ -471,11 +496,7 @@
 }
 
 CPWL_ScrollBar* CPWL_Wnd::GetVScrollBar() const {
-  return HasFlag(PWS_VSCROLL) ? m_pVScrollBar.Get() : nullptr;
-}
-
-void CPWL_Wnd::CreateScrollBar(const CreateParams& cp) {
-  CreateVScrollBar(cp);
+  return HasFlag(PWS_VSCROLL) ? m_pVScrollBar : nullptr;
 }
 
 void CPWL_Wnd::CreateVScrollBar(const CreateParams& cp) {
@@ -483,44 +504,46 @@
     return;
 
   CreateParams scp = cp;
-  scp.dwFlags =
-      PWS_CHILD | PWS_BACKGROUND | PWS_AUTOTRANSPARENT | PWS_NOREFRESHCLIP;
-  scp.sBackgroundColor = PWL_DEFAULT_WHITECOLOR;
-  scp.eCursorType = FXCT_ARROW;
-  scp.nTransparency = PWL_SCROLLBAR_TRANSPARENCY;
+  scp.dwFlags = PWS_BACKGROUND | PWS_AUTOTRANSPARENT | PWS_NOREFRESHCLIP;
+  scp.sBackgroundColor = kDefaultWhiteColor;
+  scp.eCursorType = IPWL_FillerNotify::CursorStyle::kArrow;
+  scp.nTransparency = CPWL_ScrollBar::kTransparency;
 
-  auto pBar =
-      pdfium::MakeUnique<CPWL_ScrollBar>(scp, CloneAttachedData(), SBT_VSCROLL);
+  auto pBar = std::make_unique<CPWL_ScrollBar>(scp, CloneAttachedData());
   m_pVScrollBar = pBar.get();
   AddChild(std::move(pBar));
   m_pVScrollBar->Realize();
 }
 
 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();
+    }
   }
 }
 
@@ -528,11 +551,19 @@
 
 void CPWL_Wnd::OnKillFocus() {}
 
-std::unique_ptr<IPWL_SystemHandler::PerWindowData> CPWL_Wnd::CloneAttachedData()
+std::unique_ptr<IPWL_FillerNotify::PerWindowData> CPWL_Wnd::CloneAttachedData()
     const {
   return m_pAttachedData ? m_pAttachedData->Clone() : nullptr;
 }
 
+std::vector<UnownedPtr<CPWL_Wnd>> CPWL_Wnd::GetAncestors() {
+  std::vector<UnownedPtr<CPWL_Wnd>> results;
+  for (CPWL_Wnd* pWnd = this; pWnd; pWnd = pWnd->GetParentWindow()) {
+    results.emplace_back(pWnd);
+  }
+  return results;
+}
+
 bool CPWL_Wnd::WndHitTest(const CFX_PointF& point) const {
   return IsValid() && IsVisible() && GetWindowRect().Contains(point);
 }
@@ -545,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;
@@ -576,7 +611,7 @@
   return HasFlag(PWS_READONLY);
 }
 
-bool CPWL_Wnd::RePosChildWnd() {
+bool CPWL_Wnd::RepositionChildWnd() {
   CPWL_ScrollBar* pVSB = GetVScrollBar();
   if (!pVSB)
     return true;
@@ -588,13 +623,14 @@
     rcContent.Normalize();
   }
   CFX_FloatRect rcVScroll =
-      CFX_FloatRect(rcContent.right - PWL_SCROLLBAR_WIDTH, rcContent.bottom,
+      CFX_FloatRect(rcContent.right - CPWL_ScrollBar::kWidth, rcContent.bottom,
                     rcContent.right - 1.0f, rcContent.top);
 
-  ObservedPtr<CPWL_Wnd> thisObserved(this);
+  ObservedPtr<CPWL_Wnd> this_observed(this);
   pVSB->Move(rcVScroll, true, false);
-  if (!thisObserved)
+  if (!this_observed) {
     return false;
+  }
 
   return true;
 }
@@ -603,22 +639,32 @@
 
 void CPWL_Wnd::SetCursor() {
   if (IsValid())
-    GetSystemHandler()->SetCursor(GetCreationParams()->eCursorType);
+    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 {
@@ -626,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);
 }
 
@@ -659,10 +705,10 @@
 
 CFX_Color CPWL_Wnd::GetBorderLeftTopColor(BorderStyle nBorderStyle) const {
   switch (nBorderStyle) {
-    case BorderStyle::BEVELED:
-      return CFX_Color(CFX_Color::kGray, 1);
-    case BorderStyle::INSET:
-      return CFX_Color(CFX_Color::kGray, 0.5f);
+    case BorderStyle::kBeveled:
+      return CFX_Color(CFX_Color::Type::kGray, 1);
+    case BorderStyle::kInset:
+      return CFX_Color(CFX_Color::Type::kGray, 0.5f);
     default:
       return CFX_Color();
   }
@@ -670,10 +716,10 @@
 
 CFX_Color CPWL_Wnd::GetBorderRightBottomColor(BorderStyle nBorderStyle) const {
   switch (nBorderStyle) {
-    case BorderStyle::BEVELED:
+    case BorderStyle::kBeveled:
       return GetBackgroundColor() / 2.0f;
-    case BorderStyle::INSET:
-      return CFX_Color(CFX_Color::kGray, 0.75f);
+    case BorderStyle::kInset:
+      return CFX_Color(CFX_Color::Type::kGray, 0.75f);
     default:
       return CFX_Color();
   }
@@ -691,7 +737,7 @@
 }
 
 CFX_Matrix CPWL_Wnd::GetWindowMatrix() const {
-  CFX_Matrix mt = GetChildToRoot();
+  CFX_Matrix mt;
   if (ProviderIface* pProvider = GetProvider())
     mt.Concat(pProvider->GetWindowMatrix(GetAttachedData()));
   return mt;
@@ -701,61 +747,3 @@
   CFX_Matrix mt = GetWindowMatrix();
   return mt.TransformRect(rect);
 }
-
-CFX_PointF CPWL_Wnd::ParentToChild(const CFX_PointF& point) const {
-  CFX_Matrix mt = GetChildMatrix();
-  if (mt.IsIdentity())
-    return point;
-
-  CFX_Matrix inverse = mt.GetInverse();
-  if (!inverse.IsIdentity())
-    mt = inverse;
-  return mt.Transform(point);
-}
-
-CFX_FloatRect CPWL_Wnd::ParentToChild(const CFX_FloatRect& rect) const {
-  CFX_Matrix mt = GetChildMatrix();
-  if (mt.IsIdentity())
-    return rect;
-
-  CFX_Matrix inverse = mt.GetInverse();
-  if (!inverse.IsIdentity())
-    mt = inverse;
-
-  return mt.TransformRect(rect);
-}
-
-CFX_Matrix CPWL_Wnd::GetChildToRoot() const {
-  CFX_Matrix mt;
-  if (HasFlag(PWS_CHILD)) {
-    const CPWL_Wnd* pParent = this;
-    while (pParent) {
-      mt.Concat(pParent->GetChildMatrix());
-      pParent = pParent->GetParentWindow();
-    }
-  }
-  return mt;
-}
-
-CFX_Matrix CPWL_Wnd::GetChildMatrix() const {
-  return HasFlag(PWS_CHILD) ? m_CreationParams.mtChild : CFX_Matrix();
-}
-
-void CPWL_Wnd::SetChildMatrix(const CFX_Matrix& mt) {
-  m_CreationParams.mtChild = mt;
-}
-
-const CPWL_Wnd* CPWL_Wnd::GetFocused() const {
-  CPWL_MsgControl* pMsgCtrl = GetMsgControl();
-  return pMsgCtrl ? pMsgCtrl->GetFocusedWindow() : nullptr;
-}
-
-void CPWL_Wnd::EnableWindow(bool bEnable) {
-  if (m_bEnabled == bEnable)
-    return;
-
-  for (const auto& pChild : m_Children)
-    pChild->EnableWindow(bEnable);
-
-  m_bEnabled = bEnable;
-}
diff --git a/fpdfsdk/pwl/cpwl_wnd.h b/fpdfsdk/pwl/cpwl_wnd.h
index 6e8f073..f7aea8d 100644
--- a/fpdfsdk/pwl/cpwl_wnd.h
+++ b/fpdfsdk/pwl/cpwl_wnd.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,23 +11,24 @@
 #include <vector>
 
 #include "core/fxcrt/cfx_timer.h"
+#include "core/fxcrt/mask.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
+#include "core/fxcrt/widestring.h"
 #include "core/fxge/cfx_color.h"
 #include "core/fxge/cfx_renderdevice.h"
-#include "fpdfsdk/pwl/ipwl_systemhandler.h"
+#include "fpdfsdk/pwl/ipwl_fillernotify.h"
+#include "public/fpdf_fwlevent.h"
 
 class CPWL_Edit;
-class CPWL_MsgControl;
 class CPWL_ScrollBar;
 class IPVT_FontMap;
 struct PWL_SCROLL_INFO;
 
 // window styles
-#define PWS_CHILD 0x80000000L
 #define PWS_BORDER 0x40000000L
 #define PWS_BACKGROUND 0x20000000L
-#define PWS_HSCROLL 0x10000000L
 #define PWS_VSCROLL 0x08000000L
 #define PWS_VISIBLE 0x04000000L
 #define PWS_READONLY 0x01000000L
@@ -42,16 +43,13 @@
 #define PES_RIGHT 0x0008L
 #define PES_MIDDLE 0x0010L
 #define PES_TOP 0x0020L
-#define PES_BOTTOM 0x0040L
 #define PES_CENTER 0x0080L
 #define PES_CHARARRAY 0x0100L
 #define PES_AUTOSCROLL 0x0200L
 #define PES_AUTORETURN 0x0400L
 #define PES_UNDO 0x0800L
 #define PES_RICH 0x1000L
-#define PES_SPELLCHECK 0x2000L
 #define PES_TEXTOVERFLOW 0x4000L
-#define PES_NOREAD 0x8000L
 
 // listbox styles
 #define PLBS_MULTIPLESEL 0x0001L
@@ -60,14 +58,6 @@
 // combobox styles
 #define PCBS_ALLOWCUSTOMTEXT 0x0001L
 
-// Cursor style. These must match the values in public/fpdf_formfill.h
-#define FXCT_ARROW 0
-#define FXCT_NESW 1
-#define FXCT_NWSE 2
-#define FXCT_VBEAM 3
-#define FXCT_HBEAM 4
-#define FXCT_HAND 5
-
 struct CPWL_Dash {
   CPWL_Dash() : nDash(0), nGap(0), nPhase(0) {}
   CPWL_Dash(int32_t dash, int32_t gap, int32_t phase)
@@ -84,76 +74,88 @@
   int32_t nPhase;
 };
 
-#define PWL_SCROLLBAR_WIDTH 12.0f
-#define PWL_SCROLLBAR_TRANSPARENCY 150
-#define PWL_DEFAULT_BLACKCOLOR CFX_Color(CFX_Color::kGray, 0)
-#define PWL_DEFAULT_WHITECOLOR CFX_Color(CFX_Color::kGray, 1)
-
 class CPWL_Wnd : public Observable {
  public:
+  static const CFX_Color kDefaultBlackColor;
+  static const CFX_Color kDefaultWhiteColor;
+
+  class SharedCaptureFocusState;
+
   class ProviderIface : public Observable {
    public:
     virtual ~ProviderIface() = default;
 
     // get a matrix which map user space to CWnd client space
     virtual CFX_Matrix GetWindowMatrix(
-        const IPWL_SystemHandler::PerWindowData* pAttached) = 0;
+        const IPWL_FillerNotify::PerWindowData* pAttached) = 0;
+
+    virtual void OnSetFocusForEdit(CPWL_Edit* pEdit) = 0;
   };
 
-  class FocusHandlerIface {
-   public:
-    virtual ~FocusHandlerIface() = default;
-    virtual void OnSetFocus(CPWL_Edit* pEdit) = 0;
-  };
-
+  // Caller-provided options for window creation.
   class CreateParams {
    public:
-    CreateParams();
+    CreateParams(CFX_Timer::HandlerIface* timer_handler,
+                 IPWL_FillerNotify* filler_notify,
+                 ProviderIface* provider);
     CreateParams(const CreateParams& other);
     ~CreateParams();
 
-    CFX_FloatRect rcRectWnd;                        // required
-    UnownedPtr<TimerHandlerIface> pTimerHandler;    // required
-    UnownedPtr<IPWL_SystemHandler> pSystemHandler;  // required
-    UnownedPtr<IPVT_FontMap> pFontMap;              // required
-    ObservedPtr<ProviderIface> pProvider;           // required
-    UnownedPtr<FocusHandlerIface> pFocusHandler;    // optional
-    uint32_t dwFlags = 0;                           // optional
-    CFX_Color sBackgroundColor;                     // optional
-    BorderStyle nBorderStyle = BorderStyle::SOLID;  // optional
-    int32_t dwBorderWidth = 1;                      // optional
-    CFX_Color sBorderColor;                         // optional
-    CFX_Color sTextColor;                           // optional
-    int32_t nTransparency = 255;                    // optional
-    float fFontSize;                                // optional
-    CPWL_Dash sDash;                                // optional
-    CPWL_MsgControl* pMsgControl = nullptr;         // ignore
-    int32_t eCursorType = FXCT_ARROW;               // ignore
-    CFX_Matrix mtChild;                             // ignore
+    // Required:
+    CFX_FloatRect rcRectWnd;
+    ObservedPtr<CFX_Timer::HandlerIface> const pTimerHandler;
+    UnownedPtr<IPWL_FillerNotify> const pFillerNotify;
+    UnownedPtr<IPVT_FontMap> pFontMap;
+    ObservedPtr<ProviderIface> pProvider;
+
+    // Optional:
+    uint32_t dwFlags = 0;
+    CFX_Color sBackgroundColor;
+    BorderStyle nBorderStyle = BorderStyle::kSolid;
+    int32_t dwBorderWidth = 1;
+    CFX_Color sBorderColor;
+    CFX_Color sTextColor;
+    int32_t nTransparency = 255;
+    float fFontSize;
+    CPWL_Dash sDash;
+
+    // Ignore, used internally only:
+    // TODO(tsepez): fix murky ownership, bare delete.
+    UNOWNED_PTR_EXCLUSION SharedCaptureFocusState* pSharedCaptureFocusState =
+        nullptr;
+    IPWL_FillerNotify::CursorStyle eCursorType =
+        IPWL_FillerNotify::CursorStyle::kArrow;
   };
 
-  static bool IsSHIFTKeyDown(uint32_t nFlag);
-  static bool IsCTRLKeyDown(uint32_t nFlag);
-  static bool IsALTKeyDown(uint32_t nFlag);
+  static bool IsSHIFTKeyDown(Mask<FWL_EVENTFLAG> nFlag);
+  static bool IsCTRLKeyDown(Mask<FWL_EVENTFLAG> nFlag);
+  static bool IsALTKeyDown(Mask<FWL_EVENTFLAG> nFlag);
+  static bool IsMETAKeyDown(Mask<FWL_EVENTFLAG> nFlag);
+
+  // Selects between IsCTRLKeyDown() and IsMETAKeyDown() depending on platform.
+  static bool IsPlatformShortcutKey(Mask<FWL_EVENTFLAG> nFlag);
 
   CPWL_Wnd(const CreateParams& cp,
-           std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
+           std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData);
   virtual ~CPWL_Wnd();
 
   // Returns |true| iff this instance is still allocated.
-  virtual bool InvalidateRect(CFX_FloatRect* pRect);
+  [[nodiscard]] virtual bool InvalidateRect(const CFX_FloatRect* pRect);
 
-  virtual bool OnKeyDown(uint16_t nChar, uint32_t nFlag);
-  virtual bool OnChar(uint16_t nChar, uint32_t nFlag);
-  virtual bool OnLButtonDblClk(const CFX_PointF& point, uint32_t nFlag);
-  virtual bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag);
-  virtual bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag);
-  virtual bool OnRButtonDown(const CFX_PointF& point, uint32_t nFlag);
-  virtual bool OnRButtonUp(const CFX_PointF& point, uint32_t nFlag);
-  virtual bool OnMouseMove(const CFX_PointF& point, uint32_t nFlag);
-  virtual bool OnMouseWheel(short zDelta,
+  virtual bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlag);
+  virtual bool OnChar(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag);
+  virtual bool OnLButtonDblClk(Mask<FWL_EVENTFLAG> nFlag,
+                               const CFX_PointF& point);
+  virtual bool OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,
+                             const CFX_PointF& point);
+  virtual bool OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag, const CFX_PointF& point);
+  virtual bool OnRButtonDown(Mask<FWL_EVENTFLAG> nFlag,
+                             const CFX_PointF& point);
+  virtual bool OnRButtonUp(Mask<FWL_EVENTFLAG> nFlag, const CFX_PointF& point);
+  virtual bool OnMouseMove(Mask<FWL_EVENTFLAG> nFlag, const CFX_PointF& point);
+  virtual bool OnMouseWheel(Mask<FWL_EVENTFLAG> nFlag,
                             const CFX_PointF& point,
-                            uint32_t nFlag);
+                            const CFX_Vector& delta);
   virtual void SetScrollInfo(const PWL_SCROLL_INFO& info);
   virtual void SetScrollPosition(float pos);
   virtual void ScrollWindowVertically(float pos);
@@ -165,13 +167,15 @@
   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;
 
   virtual WideString GetText();
   virtual WideString GetSelectedText();
+  virtual void ReplaceAndKeepSelection(const WideString& text);
   virtual void ReplaceSelection(const WideString& text);
+  virtual bool SelectAllText();
 
   virtual bool CanUndo();
   virtual bool CanRedo();
@@ -181,85 +185,48 @@
   virtual CFX_FloatRect GetFocusRect() const;
   virtual CFX_FloatRect GetClientRect() const;
 
+  virtual void OnSetFocus();
+  virtual void OnKillFocus();
+
   void AddChild(std::unique_ptr<CPWL_Wnd> pWnd);
   void RemoveChild(CPWL_Wnd* pWnd);
   void Realize();
   void Destroy();
   bool Move(const CFX_FloatRect& rcNew, bool bReset, bool bRefresh);
 
-  void InvalidateFocusHandler(FocusHandlerIface* handler);
   void InvalidateProvider(ProviderIface* provider);
-  void SetCapture();
-  void ReleaseCapture();
   void DrawAppearance(CFX_RenderDevice* pDevice,
                       const CFX_Matrix& mtUser2Device);
 
-  CFX_Color GetBackgroundColor() const;
-  void SetBackgroundColor(const CFX_Color& color);
-  CFX_Color GetBorderColor() const;
-  CFX_Color GetTextColor() const;
-  void SetTextColor(const CFX_Color& color);
-  CFX_Color GetBorderLeftTopColor(BorderStyle nBorderStyle) const;
-  CFX_Color GetBorderRightBottomColor(BorderStyle nBorderStyle) const;
-
-  void SetBorderStyle(BorderStyle nBorderStyle);
-  BorderStyle GetBorderStyle() const;
-  const CPWL_Dash& GetBorderDash() const;
-
   int32_t GetBorderWidth() const;
-  int32_t GetInnerBorderWidth() const;
   CFX_FloatRect GetWindowRect() const;
-  CFX_PointF GetCenterPoint() const;
 
   bool IsVisible() const { return m_bVisible; }
   bool HasFlag(uint32_t dwFlags) const;
-  void AddFlag(uint32_t dwFlags);
   void RemoveFlag(uint32_t dwFlags);
-
   void SetClipRect(const CFX_FloatRect& rect);
-  const CFX_FloatRect& GetClipRect() const;
 
-  CPWL_Wnd* GetParentWindow() const { return m_pParent.Get(); }
-  const IPWL_SystemHandler::PerWindowData* GetAttachedData() const {
+  IPWL_FillerNotify::PerWindowData* GetAttachedData() const {
     return m_pAttachedData.get();
   }
-  std::unique_ptr<IPWL_SystemHandler::PerWindowData> CloneAttachedData() const;
+  std::unique_ptr<IPWL_FillerNotify::PerWindowData> CloneAttachedData() const;
+  std::vector<UnownedPtr<CPWL_Wnd>> GetAncestors();
 
   bool WndHitTest(const CFX_PointF& point) const;
   bool ClientHitTest(const CFX_PointF& point) const;
   bool IsCaptureMouse() const;
 
-  void EnableWindow(bool bEnable);
-  bool IsEnabled() const { return m_bEnabled; }
-  const CPWL_Wnd* GetFocused() const;
   bool IsFocused() const;
   bool IsReadOnly() const;
-  CPWL_ScrollBar* GetVScrollBar() const;
 
-  IPVT_FontMap* GetFontMap() const { return m_CreationParams.pFontMap.Get(); }
-  ProviderIface* GetProvider() const {
-    return m_CreationParams.pProvider.Get();
-  }
-  FocusHandlerIface* GetFocusHandler() const {
-    return m_CreationParams.pFocusHandler.Get();
-  }
-
-  int32_t GetTransparency();
   void SetTransparency(int32_t nTransparency);
-
-  CFX_Matrix GetChildToRoot() const;
-  CFX_Matrix GetChildMatrix() const;
-  void SetChildMatrix(const CFX_Matrix& mt);
   CFX_Matrix GetWindowMatrix() const;
 
-  virtual void OnSetFocus();
-  virtual void OnKillFocus();
-
  protected:
   virtual void CreateChildWnd(const CreateParams& cp);
 
   // 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);
@@ -267,53 +234,60 @@
   virtual void OnCreated();
   virtual void OnDestroy();
 
-  bool IsNotifying() const { return m_bNotifying; }
   bool IsValid() const { return m_bCreated; }
   CreateParams* GetCreationParams() { return &m_CreationParams; }
-  TimerHandlerIface* GetTimerHandler() const {
+  ProviderIface* GetProvider() const {
+    return m_CreationParams.pProvider.Get();
+  }
+  CFX_Timer::HandlerIface* GetTimerHandler() const {
     return m_CreationParams.pTimerHandler.Get();
   }
-  IPWL_SystemHandler* GetSystemHandler() const {
-    return m_CreationParams.pSystemHandler.Get();
+  IPWL_FillerNotify* GetFillerNotify() const {
+    return m_CreationParams.pFillerNotify;
   }
 
-  // Returns |true| iff this instance is still allocated.
-  bool InvalidateRectMove(const CFX_FloatRect& rcOld,
-                          const CFX_FloatRect& rcNew);
+  CPWL_Wnd* GetParentWindow() const { return m_pParent; }
+  CPWL_ScrollBar* GetVScrollBar() const;
 
+  // Returns |true| iff this instance is still allocated.
+  [[nodiscard]] bool InvalidateRectMove(const CFX_FloatRect& rcOld,
+                                        const CFX_FloatRect& rcNew);
+
+  void SetCapture();
+  void ReleaseCapture();
   bool IsWndCaptureMouse(const CPWL_Wnd* pWnd) const;
   bool IsWndCaptureKeyboard(const CPWL_Wnd* pWnd) const;
 
-  static bool IsCTRLpressed(uint32_t nFlag) {
-    return CPWL_Wnd::IsCTRLKeyDown(nFlag);
-  }
-  static bool IsSHIFTpressed(uint32_t nFlag) {
-    return CPWL_Wnd::IsSHIFTKeyDown(nFlag);
-  }
-  static bool IsALTpressed(uint32_t nFlag) {
-    return CPWL_Wnd::IsALTKeyDown(nFlag);
-  }
+  CFX_Color GetBackgroundColor() const;
+  CFX_Color GetBorderColor() const;
+  CFX_Color GetTextColor() const;
+  CFX_Color GetBorderLeftTopColor(BorderStyle nBorderStyle) const;
+  CFX_Color GetBorderRightBottomColor(BorderStyle nBorderStyle) const;
+  BorderStyle GetBorderStyle() const;
+  const CPWL_Dash& GetBorderDash() const;
+
+  int32_t GetTransparency();
+  int32_t GetInnerBorderWidth() const;
+  CFX_PointF GetCenterPoint() const;
+  const CFX_FloatRect& GetClipRect() const;
+
+  IPVT_FontMap* GetFontMap() const { return m_CreationParams.pFontMap; }
 
  private:
-  CFX_PointF ParentToChild(const CFX_PointF& point) const;
-  CFX_FloatRect ParentToChild(const CFX_FloatRect& rect) const;
-
   void DrawChildAppearance(CFX_RenderDevice* pDevice,
                            const CFX_Matrix& mtUser2Device);
 
   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_SystemHandler::PerWindowData> m_pAttachedData;
+  std::unique_ptr<IPWL_FillerNotify::PerWindowData> m_pAttachedData;
   UnownedPtr<CPWL_Wnd> m_pParent;
   std::vector<std::unique_ptr<CPWL_Wnd>> m_Children;
   UnownedPtr<CPWL_ScrollBar> m_pVScrollBar;
@@ -321,8 +295,6 @@
   CFX_FloatRect m_rcClip;
   bool m_bCreated = false;
   bool m_bVisible = false;
-  bool m_bNotifying = false;
-  bool m_bEnabled = true;
 };
 
 #endif  // FPDFSDK_PWL_CPWL_WND_H_
diff --git a/fpdfsdk/pwl/ipwl_fillernotify.h b/fpdfsdk/pwl/ipwl_fillernotify.h
new file mode 100644
index 0000000..8a56a10
--- /dev/null
+++ b/fpdfsdk/pwl/ipwl_fillernotify.h
@@ -0,0 +1,69 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_PWL_IPWL_FILLERNOTIFY_H_
+#define FPDFSDK_PWL_IPWL_FILLERNOTIFY_H_
+
+#include <memory>
+#include <utility>
+
+#include "core/fxcrt/mask.h"
+#include "core/fxcrt/widestring.h"
+#include "public/fpdf_fwlevent.h"
+
+class CFX_FloatRect;
+
+class IPWL_FillerNotify {
+ public:
+  // These must match the values in public/fpdf_formfill.h
+  enum CursorStyle {
+    kArrow = 0,
+    kNESW = 1,
+    kNWSE = 2,
+    kVBeam = 3,
+    kHBeam = 4,
+    kHand = 5,
+  };
+
+  class PerWindowData {
+   public:
+    virtual ~PerWindowData() = default;
+    virtual std::unique_ptr<PerWindowData> Clone() const = 0;
+  };
+
+  virtual ~IPWL_FillerNotify() = default;
+
+  virtual void InvalidateRect(PerWindowData* pWidgetData,
+                              const CFX_FloatRect& rect) = 0;
+  virtual void OutputSelectedRect(PerWindowData* pWidgetData,
+                                  const CFX_FloatRect& rect) = 0;
+  virtual bool IsSelectionImplemented() const = 0;
+  virtual void SetCursor(CursorStyle nCursorStyle) = 0;
+
+  // Must write to |bBottom| and |fPopupRet|.
+  virtual void QueryWherePopup(const PerWindowData* pAttached,
+                               float fPopupMin,
+                               float fPopupMax,
+                               bool* bBottom,
+                               float* fPopupRet) = 0;
+
+  virtual std::pair<bool, bool> OnBeforeKeyStroke(
+      const PerWindowData* pAttached,
+      WideString& strChange,
+      const WideString& strChangeEx,
+      int nSelStart,
+      int nSelEnd,
+      bool bKeyDown,
+      Mask<FWL_EVENTFLAG> nFlag) = 0;
+
+  virtual bool OnPopupPreOpen(const PerWindowData* pAttached,
+                              Mask<FWL_EVENTFLAG> nFlag) = 0;
+
+  virtual bool OnPopupPostOpen(const PerWindowData* pAttached,
+                               Mask<FWL_EVENTFLAG> nFlag) = 0;
+};
+
+#endif  // FPDFSDK_PWL_IPWL_FILLERNOTIFY_H_
diff --git a/fpdfsdk/pwl/ipwl_systemhandler.h b/fpdfsdk/pwl/ipwl_systemhandler.h
deleted file mode 100644
index 8a14d8e..0000000
--- a/fpdfsdk/pwl/ipwl_systemhandler.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_PWL_IPWL_SYSTEMHANDLER_H_
-#define FPDFSDK_PWL_IPWL_SYSTEMHANDLER_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
-
-class CFFL_FormFiller;
-
-class IPWL_SystemHandler {
- public:
-  class PerWindowData {
-   public:
-    virtual ~PerWindowData() = default;
-    virtual std::unique_ptr<PerWindowData> Clone() const = 0;
-  };
-
-  virtual ~IPWL_SystemHandler() = default;
-
-  virtual void InvalidateRect(PerWindowData* pWidgetData,
-                              const CFX_FloatRect& rect) = 0;
-  virtual void OutputSelectedRect(CFFL_FormFiller* pFormFiller,
-                                  const CFX_FloatRect& rect) = 0;
-  virtual bool IsSelectionImplemented() const = 0;
-  virtual void SetCursor(int32_t nCursorType) = 0;
-};
-
-#endif  // FPDFSDK_PWL_IPWL_SYSTEMHANDLER_H_
diff --git a/fxbarcode/BC_Library.cpp b/fxbarcode/BC_Library.cpp
index 393f329..aee3372 100644
--- a/fxbarcode/BC_Library.cpp
+++ b/fxbarcode/BC_Library.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/BC_Library.h b/fxbarcode/BC_Library.h
index 5f1af4d..81d5a88 100644
--- a/fxbarcode/BC_Library.h
+++ b/fxbarcode/BC_Library.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,33 +9,33 @@
 
 #include <stdint.h>
 
-enum BC_TEXT_LOC : uint8_t {
-  BC_TEXT_LOC_NONE = 0,
-  BC_TEXT_LOC_ABOVE,
-  BC_TEXT_LOC_BELOW,
-  BC_TEXT_LOC_ABOVEEMBED,
-  BC_TEXT_LOC_BELOWEMBED
+enum class BC_TEXT_LOC : uint8_t {
+  kNone = 0,
+  kAbove,
+  kBelow,
+  kAboveEmbed,
+  kBelowEmbed,
 };
 
-enum BC_CHAR_ENCODING : uint8_t {
-  CHAR_ENCODING_UTF8 = 0,
-  CHAR_ENCODING_UNICODE
+enum class BC_CHAR_ENCODING : uint8_t {
+  kUTF8 = 0,
+  kUnicode,
 };
 
-enum BC_TYPE : int8_t {
-  BC_UNKNOWN = -1,
-  BC_CODE39 = 0,
-  BC_CODABAR,
-  BC_CODE128,
-  BC_CODE128_B,
-  BC_CODE128_C,
-  BC_EAN8,
-  BC_UPCA,
-  BC_EAN13,
-  BC_QR_CODE,
-  BC_PDF417,
-  BC_DATAMATRIX,
-  BC_LAST = BC_DATAMATRIX
+enum class BC_TYPE : int8_t {
+  kUnknown = -1,
+  kCode39 = 0,
+  kCodabar,
+  kCode128,
+  kCode128B,
+  kCode128C,
+  kEAN8,
+  kUPCA,
+  kEAN13,
+  kQRCode,
+  kPDF417,
+  kDataMatrix,
+  kLast = kDataMatrix
 };
 
 void BC_Library_Init();
diff --git a/fxbarcode/BC_TwoDimWriter.cpp b/fxbarcode/BC_TwoDimWriter.cpp
index b88574c..73eb29e 100644
--- a/fxbarcode/BC_TwoDimWriter.cpp
+++ b/fxbarcode/BC_TwoDimWriter.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,13 +9,14 @@
 #include <algorithm>
 
 #include "core/fxcrt/fx_safe_types.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "fxbarcode/BC_Writer.h"
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
+#include "third_party/base/check.h"
 #include "third_party/base/numerics/safe_math.h"
-#include "third_party/base/ptr_util.h"
 
 CBC_TwoDimWriter::CBC_TwoDimWriter(bool bFixedSize)
     : m_bFixedSize(bFixedSize) {}
@@ -71,9 +72,7 @@
   m_leftPadding = std::max((m_Width - m_outputWidth) / 2, 0);
   m_topPadding = std::max((m_Height - m_outputHeight) / 2, 0);
 
-  m_output = pdfium::MakeUnique<CBC_CommonBitMatrix>();
-  m_output->Init(m_inputWidth, m_inputHeight);
-
+  m_output = std::make_unique<CBC_CommonBitMatrix>(m_inputWidth, m_inputHeight);
   for (int32_t y = 0; y < m_inputHeight; ++y) {
     for (int32_t x = 0; x < m_inputWidth; ++x) {
       if (code[x + y * m_inputWidth] == 1)
@@ -84,23 +83,23 @@
 }
 
 void CBC_TwoDimWriter::RenderDeviceResult(CFX_RenderDevice* device,
-                                          const CFX_Matrix* matrix) {
-  ASSERT(m_output);
+                                          const CFX_Matrix& matrix) {
+  DCHECK(m_output);
 
   CFX_GraphStateData stateData;
-  CFX_PathData path;
+  CFX_Path path;
   path.AppendRect(0, 0, m_Width, m_Height);
-  device->DrawPath(&path, matrix, &stateData, kBackgroundColor,
-                   kBackgroundColor, FXFILL_ALTERNATE);
+  device->DrawPath(path, &matrix, &stateData, kBackgroundColor,
+                   kBackgroundColor, CFX_FillRenderOptions::EvenOddOptions());
   int32_t leftPos = m_leftPadding;
   int32_t topPos = m_topPadding;
 
-  CFX_Matrix matri = *matrix;
+  CFX_Matrix matri = matrix;
   if (m_Width < m_outputWidth && m_Height < m_outputHeight) {
     CFX_Matrix matriScale(static_cast<float>(m_Width) / m_outputWidth, 0.0, 0.0,
                           static_cast<float>(m_Height) / m_outputHeight, 0.0,
                           0.0);
-    matriScale.Concat(*matrix);
+    matriScale.Concat(matrix);
     matri = matriScale;
   }
 
@@ -115,12 +114,13 @@
         int start_y_output = y + 1;
         int end_y_output = y + 2;
 
-        CFX_PathData rect;
+        CFX_Path rect;
         rect.AppendRect(leftPos + start_x_output * m_multiX,
                         topPos + start_y_output * m_multiY,
                         leftPos + end_x_output * m_multiX,
                         topPos + end_y_output * m_multiY);
-        device->DrawPath(&rect, &matri, &data, kBarColor, 0, FXFILL_WINDING);
+        device->DrawPath(rect, &matri, &data, kBarColor, 0,
+                         CFX_FillRenderOptions::WindingOptions());
       }
     }
   }
diff --git a/fxbarcode/BC_TwoDimWriter.h b/fxbarcode/BC_TwoDimWriter.h
index a887ded..cab1f45 100644
--- a/fxbarcode/BC_TwoDimWriter.h
+++ b/fxbarcode/BC_TwoDimWriter.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -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;
@@ -24,7 +24,7 @@
   bool RenderResult(pdfium::span<const uint8_t> code,
                     int32_t codeWidth,
                     int32_t codeHeight);
-  void RenderDeviceResult(CFX_RenderDevice* device, const CFX_Matrix* matrix);
+  void RenderDeviceResult(CFX_RenderDevice* device, const CFX_Matrix& matrix);
 
   int32_t error_correction_level() const { return m_iCorrectionLevel; }
 
diff --git a/fxbarcode/BC_Writer.cpp b/fxbarcode/BC_Writer.cpp
index 9f77ffb..48d02bb 100644
--- a/fxbarcode/BC_Writer.cpp
+++ b/fxbarcode/BC_Writer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,9 +10,8 @@
 
 CBC_Writer::~CBC_Writer() = default;
 
-bool CBC_Writer::SetCharEncoding(int32_t encoding) {
+void CBC_Writer::SetCharEncoding(BC_CHAR_ENCODING encoding) {
   m_CharEncoding = encoding;
-  return true;
 }
 
 bool CBC_Writer::SetModuleHeight(int32_t moduleHeight) {
@@ -31,19 +30,15 @@
   return true;
 }
 
-bool CBC_Writer::SetHeight(int32_t height) {
+void CBC_Writer::SetHeight(int32_t height) {
   m_Height = height;
-  return true;
 }
 
-bool CBC_Writer::SetWidth(int32_t width) {
+void CBC_Writer::SetWidth(int32_t width) {
   m_Width = width;
-  return true;
 }
 
-bool CBC_Writer::SetTextLocation(BC_TEXT_LOC location) {
-  return false;
-}
+void CBC_Writer::SetTextLocation(BC_TEXT_LOC location) {}
 
 bool CBC_Writer::SetWideNarrowRatio(int8_t ratio) {
   return false;
diff --git a/fxbarcode/BC_Writer.h b/fxbarcode/BC_Writer.h
index cf7f43b..be418aa 100644
--- a/fxbarcode/BC_Writer.h
+++ b/fxbarcode/BC_Writer.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,21 @@
 #ifndef FXBARCODE_BC_WRITER_H_
 #define FXBARCODE_BC_WRITER_H_
 
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
 #include "fxbarcode/BC_Library.h"
 
 class CBC_Writer {
  public:
   CBC_Writer();
   virtual ~CBC_Writer();
-  virtual bool SetCharEncoding(int32_t encoding);
-  virtual bool SetModuleHeight(int32_t moduleHeight);
-  virtual bool SetModuleWidth(int32_t moduleWidth);
-  virtual bool SetHeight(int32_t height);
-  virtual bool SetWidth(int32_t width);
-  virtual bool SetTextLocation(BC_TEXT_LOC location);
+
+  void SetCharEncoding(BC_CHAR_ENCODING encoding);
+  bool SetModuleHeight(int32_t moduleHeight);
+  bool SetModuleWidth(int32_t moduleWidth);
+  void SetHeight(int32_t height);
+  void SetWidth(int32_t width);
+
+  virtual void SetTextLocation(BC_TEXT_LOC location);
   virtual bool SetWideNarrowRatio(int8_t ratio);
   virtual bool SetStartChar(char start);
   virtual bool SetEndChar(char end);
@@ -29,12 +31,12 @@
   static const FX_ARGB kBarColor = 0xff000000;
   static const FX_ARGB kBackgroundColor = 0xffffffff;
 
-  int32_t m_CharEncoding = 0;
   int32_t m_ModuleHeight = 1;
   int32_t m_ModuleWidth = 1;
   int32_t m_Height = 320;
   int32_t m_Width = 640;
-  FXDIB_Format m_colorSpace = FXDIB_Argb;
+  FXDIB_Format m_colorSpace = FXDIB_Format::kArgb;
+  BC_CHAR_ENCODING m_CharEncoding = BC_CHAR_ENCODING::kUTF8;
 };
 
 #endif  // FXBARCODE_BC_WRITER_H_
diff --git a/fxbarcode/BUILD.gn b/fxbarcode/BUILD.gn
index 2b10e95..0ca11c9 100644
--- a/fxbarcode/BUILD.gn
+++ b/fxbarcode/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -131,14 +131,13 @@
     "qrcode/BC_QRCoderMode.h",
     "qrcode/BC_QRCoderVersion.cpp",
     "qrcode/BC_QRCoderVersion.h",
-    "utils.h",
   ]
   deps = [
     "../core/fxcrt",
     "../core/fxge",
     "../third_party:bigint",
   ]
-  configs += [ "../:pdfium_core_config" ]
+  configs += [ "../:pdfium_strict_config" ]
   visibility = [ "../*" ]
 }
 
diff --git a/fxbarcode/cbc_codabar.cpp b/fxbarcode/cbc_codabar.cpp
index ad4d21c..cb4b875 100644
--- a/fxbarcode/cbc_codabar.cpp
+++ b/fxbarcode/cbc_codabar.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,33 +23,27 @@
 
 #include <memory>
 
-#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_coordinates.h"
 #include "fxbarcode/oned/BC_OnedCodaBarWriter.h"
-#include "third_party/base/ptr_util.h"
 
 CBC_Codabar::CBC_Codabar()
-    : CBC_OneCode(pdfium::MakeUnique<CBC_OnedCodaBarWriter>()) {}
+    : CBC_OneCode(std::make_unique<CBC_OnedCodaBarWriter>()) {}
 
-CBC_Codabar::~CBC_Codabar() {}
+CBC_Codabar::~CBC_Codabar() = default;
 
 bool CBC_Codabar::Encode(WideStringView contents) {
-  if (contents.IsEmpty() || contents.GetLength() > kMaxInputLengthBytes)
+  auto* pWriter = GetOnedCodaBarWriter();
+  if (!pWriter->CheckContentValidity(contents))
     return false;
 
-  BCFORMAT format = BCFORMAT_CODABAR;
-  int32_t outWidth = 0;
-  int32_t outHeight = 0;
-  m_renderContents = GetOnedCodaBarWriter()->FilterContents(contents);
+  m_renderContents = pWriter->FilterContents(contents);
   ByteString byteString = m_renderContents.ToUTF8();
-  auto* pWriter = GetOnedCodaBarWriter();
-  std::unique_ptr<uint8_t, FxFreeDeleter> data(
-      pWriter->Encode(byteString, format, outWidth, outHeight));
-  return data && pWriter->RenderResult(m_renderContents.AsStringView(),
-                                       data.get(), outWidth);
+  return pWriter->RenderResult(m_renderContents.AsStringView(),
+                               pWriter->Encode(byteString));
 }
 
 bool CBC_Codabar::RenderDevice(CFX_RenderDevice* device,
-                               const CFX_Matrix* matrix) {
+                               const CFX_Matrix& matrix) {
   auto* pWriter = GetOnedCodaBarWriter();
   WideString renderCon =
       pWriter->encodedContents(m_renderContents.AsStringView());
@@ -57,7 +51,7 @@
 }
 
 BC_TYPE CBC_Codabar::GetType() {
-  return BC_CODABAR;
+  return BC_TYPE::kCodabar;
 }
 
 CBC_OnedCodaBarWriter* CBC_Codabar::GetOnedCodaBarWriter() {
diff --git a/fxbarcode/cbc_codabar.h b/fxbarcode/cbc_codabar.h
index a3e1df4..7b74486 100644
--- a/fxbarcode/cbc_codabar.h
+++ b/fxbarcode/cbc_codabar.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,12 @@
 #ifndef FXBARCODE_CBC_CODABAR_H_
 #define FXBARCODE_CBC_CODABAR_H_
 
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/widestring.h"
+#include "fxbarcode/BC_Library.h"
 #include "fxbarcode/cbc_onecode.h"
 
 class CBC_OnedCodaBarWriter;
+class CFX_Matrix;
 
 class CBC_Codabar final : public CBC_OneCode {
  public:
@@ -22,7 +23,7 @@
   BC_TYPE GetType() override;
   bool Encode(WideStringView contents) override;
   bool RenderDevice(CFX_RenderDevice* device,
-                    const CFX_Matrix* matrix) override;
+                    const CFX_Matrix& matrix) override;
 
  private:
   CBC_OnedCodaBarWriter* GetOnedCodaBarWriter();
diff --git a/fxbarcode/cbc_code128.cpp b/fxbarcode/cbc_code128.cpp
index c363c94..8b2b65d 100644
--- a/fxbarcode/cbc_code128.cpp
+++ b/fxbarcode/cbc_code128.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,43 +23,37 @@
 
 #include <memory>
 
-#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_coordinates.h"
 #include "fxbarcode/oned/BC_OnedCode128Writer.h"
-#include "third_party/base/ptr_util.h"
 
 CBC_Code128::CBC_Code128(BC_TYPE type)
-    : CBC_OneCode(pdfium::MakeUnique<CBC_OnedCode128Writer>(type)) {}
+    : CBC_OneCode(std::make_unique<CBC_OnedCode128Writer>(type)) {}
 
-CBC_Code128::~CBC_Code128() {}
+CBC_Code128::~CBC_Code128() = default;
 
 bool CBC_Code128::Encode(WideStringView contents) {
-  if (contents.IsEmpty() || contents.GetLength() > kMaxInputLengthBytes)
+  auto* pWriter = GetOnedCode128Writer();
+  if (!pWriter->CheckContentValidity(contents))
     return false;
 
-  BCFORMAT format = BCFORMAT_CODE_128;
-  int32_t outWidth = 0;
-  int32_t outHeight = 0;
-  auto* pWriter = GetOnedCode128Writer();
   WideString content(contents);
-  if (contents.GetLength() % 2 && pWriter->GetType() == BC_CODE128_C)
+  if (contents.GetLength() % 2 && pWriter->GetType() == BC_TYPE::kCode128C)
     content += '0';
 
   m_renderContents = pWriter->FilterContents(content.AsStringView());
   ByteString byteString = m_renderContents.ToUTF8();
-  std::unique_ptr<uint8_t, FxFreeDeleter> data(
-      pWriter->Encode(byteString, format, outWidth, outHeight));
-  return data && pWriter->RenderResult(m_renderContents.AsStringView(),
-                                       data.get(), outWidth);
+  return pWriter->RenderResult(m_renderContents.AsStringView(),
+                               pWriter->Encode(byteString));
 }
 
 bool CBC_Code128::RenderDevice(CFX_RenderDevice* device,
-                               const CFX_Matrix* matrix) {
+                               const CFX_Matrix& matrix) {
   return GetOnedCode128Writer()->RenderDeviceResult(
       device, matrix, m_renderContents.AsStringView());
 }
 
 BC_TYPE CBC_Code128::GetType() {
-  return BC_CODE128;
+  return BC_TYPE::kCode128;
 }
 
 CBC_OnedCode128Writer* CBC_Code128::GetOnedCode128Writer() {
diff --git a/fxbarcode/cbc_code128.h b/fxbarcode/cbc_code128.h
index 2ccf07d..81710c8 100644
--- a/fxbarcode/cbc_code128.h
+++ b/fxbarcode/cbc_code128.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,12 @@
 #ifndef FXBARCODE_CBC_CODE128_H_
 #define FXBARCODE_CBC_CODE128_H_
 
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/widestring.h"
+#include "fxbarcode/BC_Library.h"
 #include "fxbarcode/cbc_onecode.h"
 
 class CBC_OnedCode128Writer;
+class CFX_Matrix;
 
 class CBC_Code128 final : public CBC_OneCode {
  public:
@@ -22,7 +23,7 @@
   BC_TYPE GetType() override;
   bool Encode(WideStringView contents) override;
   bool RenderDevice(CFX_RenderDevice* device,
-                    const CFX_Matrix* matrix) override;
+                    const CFX_Matrix& matrix) override;
 
  private:
   CBC_OnedCode128Writer* GetOnedCode128Writer();
diff --git a/fxbarcode/cbc_code39.cpp b/fxbarcode/cbc_code39.cpp
index 2138d18..229048e 100644
--- a/fxbarcode/cbc_code39.cpp
+++ b/fxbarcode/cbc_code39.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,34 +23,28 @@
 
 #include <memory>
 
-#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_coordinates.h"
 #include "fxbarcode/oned/BC_OnedCode39Writer.h"
-#include "third_party/base/ptr_util.h"
 
 CBC_Code39::CBC_Code39()
-    : CBC_OneCode(pdfium::MakeUnique<CBC_OnedCode39Writer>()) {}
+    : CBC_OneCode(std::make_unique<CBC_OnedCode39Writer>()) {}
 
-CBC_Code39::~CBC_Code39() {}
+CBC_Code39::~CBC_Code39() = default;
 
 bool CBC_Code39::Encode(WideStringView contents) {
-  if (contents.IsEmpty() || contents.GetLength() > kMaxInputLengthBytes)
+  auto* pWriter = GetOnedCode39Writer();
+  if (!pWriter->CheckContentValidity(contents))
     return false;
 
-  BCFORMAT format = BCFORMAT_CODE_39;
-  int32_t outWidth = 0;
-  int32_t outHeight = 0;
-  auto* pWriter = GetOnedCode39Writer();
   WideString filtercontents = pWriter->FilterContents(contents);
   m_renderContents = pWriter->RenderTextContents(contents);
   ByteString byteString = filtercontents.ToUTF8();
-  std::unique_ptr<uint8_t, FxFreeDeleter> data(
-      pWriter->Encode(byteString, format, outWidth, outHeight));
-  return data && pWriter->RenderResult(m_renderContents.AsStringView(),
-                                       data.get(), outWidth);
+  return pWriter->RenderResult(m_renderContents.AsStringView(),
+                               pWriter->Encode(byteString));
 }
 
 bool CBC_Code39::RenderDevice(CFX_RenderDevice* device,
-                              const CFX_Matrix* matrix) {
+                              const CFX_Matrix& matrix) {
   auto* pWriter = GetOnedCode39Writer();
   WideString renderCon;
   if (!pWriter->encodedContents(m_renderContents.AsStringView(), &renderCon))
@@ -59,7 +53,7 @@
 }
 
 BC_TYPE CBC_Code39::GetType() {
-  return BC_CODE39;
+  return BC_TYPE::kCode39;
 }
 
 CBC_OnedCode39Writer* CBC_Code39::GetOnedCode39Writer() {
diff --git a/fxbarcode/cbc_code39.h b/fxbarcode/cbc_code39.h
index ab48292..0767bd7 100644
--- a/fxbarcode/cbc_code39.h
+++ b/fxbarcode/cbc_code39.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,13 @@
 #ifndef FXBARCODE_CBC_CODE39_H_
 #define FXBARCODE_CBC_CODE39_H_
 
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/widestring.h"
+#include "fxbarcode/BC_Library.h"
 #include "fxbarcode/cbc_onecode.h"
 
 class CBC_OnedCode39Writer;
+class CFX_Matrix;
+class CFX_RenderDevice;
 
 class CBC_Code39 final : public CBC_OneCode {
  public:
@@ -23,7 +24,7 @@
   BC_TYPE GetType() override;
   bool Encode(WideStringView contents) override;
   bool RenderDevice(CFX_RenderDevice* device,
-                    const CFX_Matrix* matrix) override;
+                    const CFX_Matrix& matrix) override;
 
  private:
   CBC_OnedCode39Writer* GetOnedCode39Writer();
diff --git a/fxbarcode/cbc_codebase.cpp b/fxbarcode/cbc_codebase.cpp
index 305d09b..78403c7 100644
--- a/fxbarcode/cbc_codebase.cpp
+++ b/fxbarcode/cbc_codebase.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -28,10 +28,10 @@
 CBC_CodeBase::CBC_CodeBase(std::unique_ptr<CBC_Writer> pWriter)
     : m_pBCWriter(std::move(pWriter)) {}
 
-CBC_CodeBase::~CBC_CodeBase() {}
+CBC_CodeBase::~CBC_CodeBase() = default;
 
-bool CBC_CodeBase::SetTextLocation(BC_TEXT_LOC location) {
-  return m_pBCWriter->SetTextLocation(location);
+void CBC_CodeBase::SetTextLocation(BC_TEXT_LOC location) {
+  m_pBCWriter->SetTextLocation(location);
 }
 
 bool CBC_CodeBase::SetWideNarrowRatio(int8_t ratio) {
@@ -50,8 +50,8 @@
   return m_pBCWriter->SetErrorCorrectionLevel(level);
 }
 
-bool CBC_CodeBase::SetCharEncoding(int32_t encoding) {
-  return m_pBCWriter->SetCharEncoding(encoding);
+void CBC_CodeBase::SetCharEncoding(BC_CHAR_ENCODING encoding) {
+  m_pBCWriter->SetCharEncoding(encoding);
 }
 
 bool CBC_CodeBase::SetModuleHeight(int32_t moduleHeight) {
@@ -62,10 +62,10 @@
   return m_pBCWriter->SetModuleWidth(moduleWidth);
 }
 
-bool CBC_CodeBase::SetHeight(int32_t height) {
+void CBC_CodeBase::SetHeight(int32_t height) {
   return m_pBCWriter->SetHeight(height);
 }
 
-bool CBC_CodeBase::SetWidth(int32_t width) {
+void CBC_CodeBase::SetWidth(int32_t width) {
   return m_pBCWriter->SetWidth(width);
 }
diff --git a/fxbarcode/cbc_codebase.h b/fxbarcode/cbc_codebase.h
index d3e21dc..250cbf3 100644
--- a/fxbarcode/cbc_codebase.h
+++ b/fxbarcode/cbc_codebase.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,15 @@
 #ifndef FXBARCODE_CBC_CODEBASE_H_
 #define FXBARCODE_CBC_CODEBASE_H_
 
+#include <stdint.h>
+
 #include <memory>
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxcrt/widestring.h"
+#include "core/fxge/dib/fx_dib.h"
 #include "fxbarcode/BC_Library.h"
 
-class CBC_Reader;
 class CBC_Writer;
-class CFX_DIBitmap;
 class CFX_Matrix;
 class CFX_RenderDevice;
 
@@ -28,18 +27,18 @@
   virtual BC_TYPE GetType() = 0;
   virtual bool Encode(WideStringView contents) = 0;
   virtual bool RenderDevice(CFX_RenderDevice* device,
-                            const CFX_Matrix* matrix) = 0;
+                            const CFX_Matrix& matrix) = 0;
 
-  bool SetTextLocation(BC_TEXT_LOC location);
+  void SetTextLocation(BC_TEXT_LOC location);
   bool SetWideNarrowRatio(int8_t ratio);
   bool SetStartChar(char start);
   bool SetEndChar(char end);
   bool SetErrorCorrectionLevel(int32_t level);
-  bool SetCharEncoding(int32_t encoding);
+  void SetCharEncoding(BC_CHAR_ENCODING encoding);
   bool SetModuleHeight(int32_t moduleHeight);
   bool SetModuleWidth(int32_t moduleWidth);
-  bool SetHeight(int32_t height);
-  bool SetWidth(int32_t width);
+  void SetHeight(int32_t height);
+  void SetWidth(int32_t width);
 
  protected:
   std::unique_ptr<CBC_Writer> m_pBCWriter;
diff --git a/fxbarcode/cbc_datamatrix.cpp b/fxbarcode/cbc_datamatrix.cpp
index e8695d2..e9a747b 100644
--- a/fxbarcode/cbc_datamatrix.cpp
+++ b/fxbarcode/cbc_datamatrix.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -21,13 +21,16 @@
 
 #include "fxbarcode/cbc_datamatrix.h"
 
-#include <vector>
+#include <stdint.h>
 
+#include <memory>
+
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_coordinates.h"
 #include "fxbarcode/datamatrix/BC_DataMatrixWriter.h"
-#include "third_party/base/ptr_util.h"
 
 CBC_DataMatrix::CBC_DataMatrix()
-    : CBC_CodeBase(pdfium::MakeUnique<CBC_DataMatrixWriter>()) {}
+    : CBC_CodeBase(std::make_unique<CBC_DataMatrixWriter>()) {}
 
 CBC_DataMatrix::~CBC_DataMatrix() = default;
 
@@ -35,19 +38,19 @@
   int32_t width;
   int32_t height;
   auto* pWriter = GetDataMatrixWriter();
-  std::vector<uint8_t> data =
+  DataVector<uint8_t> data =
       pWriter->Encode(WideString(contents), &width, &height);
   return pWriter->RenderResult(data, width, height);
 }
 
 bool CBC_DataMatrix::RenderDevice(CFX_RenderDevice* device,
-                                  const CFX_Matrix* matrix) {
+                                  const CFX_Matrix& matrix) {
   GetDataMatrixWriter()->RenderDeviceResult(device, matrix);
   return true;
 }
 
 BC_TYPE CBC_DataMatrix::GetType() {
-  return BC_DATAMATRIX;
+  return BC_TYPE::kDataMatrix;
 }
 
 CBC_DataMatrixWriter* CBC_DataMatrix::GetDataMatrixWriter() {
diff --git a/fxbarcode/cbc_datamatrix.h b/fxbarcode/cbc_datamatrix.h
index c504c95..940bad3 100644
--- a/fxbarcode/cbc_datamatrix.h
+++ b/fxbarcode/cbc_datamatrix.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,12 @@
 #ifndef FXBARCODE_CBC_DATAMATRIX_H_
 #define FXBARCODE_CBC_DATAMATRIX_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/widestring.h"
 #include "fxbarcode/cbc_codebase.h"
 
 class CBC_DataMatrixWriter;
+class CFX_Matrix;
+class CFX_RenderDevice;
 
 class CBC_DataMatrix final : public CBC_CodeBase {
  public:
@@ -21,7 +22,7 @@
   // CBC_OneCode:
   bool Encode(WideStringView contents) override;
   bool RenderDevice(CFX_RenderDevice* device,
-                    const CFX_Matrix* matrix) override;
+                    const CFX_Matrix& matrix) override;
   BC_TYPE GetType() override;
 
  private:
diff --git a/fxbarcode/cbc_ean13.cpp b/fxbarcode/cbc_ean13.cpp
index 934e61c..956d2df 100644
--- a/fxbarcode/cbc_ean13.cpp
+++ b/fxbarcode/cbc_ean13.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -24,19 +24,13 @@
 #include <memory>
 
 #include "fxbarcode/oned/BC_OnedEAN13Writer.h"
-#include "third_party/base/ptr_util.h"
 
-CBC_EAN13::CBC_EAN13()
-    : CBC_EANCode(pdfium::MakeUnique<CBC_OnedEAN13Writer>()) {}
+CBC_EAN13::CBC_EAN13() : CBC_EANCode(std::make_unique<CBC_OnedEAN13Writer>()) {}
 
 CBC_EAN13::~CBC_EAN13() = default;
 
 BC_TYPE CBC_EAN13::GetType() {
-  return BC_EAN13;
-}
-
-BCFORMAT CBC_EAN13::GetFormat() const {
-  return BCFORMAT_EAN_13;
+  return BC_TYPE::kEAN13;
 }
 
 size_t CBC_EAN13::GetMaxLength() const {
diff --git a/fxbarcode/cbc_ean13.h b/fxbarcode/cbc_ean13.h
index a82a03f..bba673e 100644
--- a/fxbarcode/cbc_ean13.h
+++ b/fxbarcode/cbc_ean13.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,10 @@
 #ifndef FXBARCODE_CBC_EAN13_H_
 #define FXBARCODE_CBC_EAN13_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "fxbarcode/cbc_eancode.h"
+#include <stddef.h>
 
-class CBC_OnedEAN13Writer;
+#include "fxbarcode/BC_Library.h"
+#include "fxbarcode/cbc_eancode.h"
 
 class CBC_EAN13 final : public CBC_EANCode {
  public:
@@ -20,7 +19,6 @@
 
   // CBC_EANCode:
   BC_TYPE GetType() override;
-  BCFORMAT GetFormat() const override;
   size_t GetMaxLength() const override;
 };
 
diff --git a/fxbarcode/cbc_ean8.cpp b/fxbarcode/cbc_ean8.cpp
index 84ed672..b3ee746 100644
--- a/fxbarcode/cbc_ean8.cpp
+++ b/fxbarcode/cbc_ean8.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -24,18 +24,13 @@
 #include <memory>
 
 #include "fxbarcode/oned/BC_OnedEAN8Writer.h"
-#include "third_party/base/ptr_util.h"
 
-CBC_EAN8::CBC_EAN8() : CBC_EANCode(pdfium::MakeUnique<CBC_OnedEAN8Writer>()) {}
+CBC_EAN8::CBC_EAN8() : CBC_EANCode(std::make_unique<CBC_OnedEAN8Writer>()) {}
 
 CBC_EAN8::~CBC_EAN8() = default;
 
 BC_TYPE CBC_EAN8::GetType() {
-  return BC_EAN8;
-}
-
-BCFORMAT CBC_EAN8::GetFormat() const {
-  return BCFORMAT_EAN_8;
+  return BC_TYPE::kEAN8;
 }
 
 size_t CBC_EAN8::GetMaxLength() const {
diff --git a/fxbarcode/cbc_ean8.h b/fxbarcode/cbc_ean8.h
index 57c06fa..d96282a 100644
--- a/fxbarcode/cbc_ean8.h
+++ b/fxbarcode/cbc_ean8.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,10 @@
 #ifndef FXBARCODE_CBC_EAN8_H_
 #define FXBARCODE_CBC_EAN8_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "fxbarcode/cbc_eancode.h"
+#include <stddef.h>
 
-class CBC_OnedEAN8Writer;
+#include "fxbarcode/BC_Library.h"
+#include "fxbarcode/cbc_eancode.h"
 
 class CBC_EAN8 final : public CBC_EANCode {
  public:
@@ -20,7 +19,6 @@
 
   // CBC_EANCode:
   BC_TYPE GetType() override;
-  BCFORMAT GetFormat() const override;
   size_t GetMaxLength() const override;
 };
 
diff --git a/fxbarcode/cbc_eancode.cpp b/fxbarcode/cbc_eancode.cpp
index 6e44f81..261b753 100644
--- a/fxbarcode/cbc_eancode.cpp
+++ b/fxbarcode/cbc_eancode.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,7 @@
 
 #include <utility>
 
-#include "core/fxcrt/fx_memory_wrappers.h"
+#include "fxbarcode/BC_Library.h"
 #include "fxbarcode/oned/BC_OnedEANWriter.h"
 
 CBC_EANCode::CBC_EANCode(std::unique_ptr<CBC_OneDimEANWriter> pWriter)
@@ -21,24 +21,19 @@
 }
 
 bool CBC_EANCode::Encode(WideStringView contents) {
-  if (contents.IsEmpty() || contents.GetLength() > kMaxInputLengthBytes)
+  auto* pWriter = GetOneDimEANWriter();
+  if (!pWriter->CheckContentValidity(contents))
     return false;
 
-  BCFORMAT format = GetFormat();
-  int32_t out_width = 0;
-  int32_t out_height = 0;
   m_renderContents = Preprocess(contents);
   ByteString str = m_renderContents.ToUTF8();
-  auto* pWriter = GetOneDimEANWriter();
   pWriter->InitEANWriter();
-  std::unique_ptr<uint8_t, FxFreeDeleter> data(
-      pWriter->Encode(str, format, out_width, out_height));
-  return data && pWriter->RenderResult(m_renderContents.AsStringView(),
-                                       data.get(), out_width);
+  return pWriter->RenderResult(m_renderContents.AsStringView(),
+                               pWriter->Encode(str));
 }
 
 bool CBC_EANCode::RenderDevice(CFX_RenderDevice* device,
-                               const CFX_Matrix* matrix) {
+                               const CFX_Matrix& matrix) {
   return GetOneDimEANWriter()->RenderDeviceResult(
       device, matrix, m_renderContents.AsStringView());
 }
diff --git a/fxbarcode/cbc_eancode.h b/fxbarcode/cbc_eancode.h
index 572bb42..1a29546 100644
--- a/fxbarcode/cbc_eancode.h
+++ b/fxbarcode/cbc_eancode.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,9 +9,8 @@
 
 #include <memory>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 #include "fxbarcode/cbc_onecode.h"
-#include "fxbarcode/utils.h"
 
 class CBC_OneDimEANWriter;
 
@@ -20,13 +19,12 @@
   explicit CBC_EANCode(std::unique_ptr<CBC_OneDimEANWriter> pWriter);
   ~CBC_EANCode() override;
 
-  virtual BCFORMAT GetFormat() const = 0;
   virtual size_t GetMaxLength() const = 0;
 
   // CBC_EANCode:
   bool Encode(WideStringView contents) override;
   bool RenderDevice(CFX_RenderDevice* device,
-                    const CFX_Matrix* matrix) override;
+                    const CFX_Matrix& matrix) override;
 
  protected:
   CBC_OneDimEANWriter* GetOneDimEANWriter();
diff --git a/fxbarcode/cbc_onecode.cpp b/fxbarcode/cbc_onecode.cpp
index 05501bd..220e36c 100644
--- a/fxbarcode/cbc_onecode.cpp
+++ b/fxbarcode/cbc_onecode.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -28,15 +28,7 @@
 CBC_OneCode::CBC_OneCode(std::unique_ptr<CBC_Writer> pWriter)
     : CBC_CodeBase(std::move(pWriter)) {}
 
-CBC_OneCode::~CBC_OneCode() {}
-
-bool CBC_OneCode::CheckContentValidity(WideStringView contents) {
-  return GetOneDimWriter()->CheckContentValidity(contents);
-}
-
-WideString CBC_OneCode::FilterContents(WideStringView contents) {
-  return GetOneDimWriter()->FilterContents(contents);
-}
+CBC_OneCode::~CBC_OneCode() = default;
 
 void CBC_OneCode::SetPrintChecksum(bool checksum) {
   GetOneDimWriter()->SetPrintChecksum(checksum);
diff --git a/fxbarcode/cbc_onecode.h b/fxbarcode/cbc_onecode.h
index 26aa943..1eb313f 100644
--- a/fxbarcode/cbc_onecode.h
+++ b/fxbarcode/cbc_onecode.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,11 @@
 #ifndef FXBARCODE_CBC_ONECODE_H_
 #define FXBARCODE_CBC_ONECODE_H_
 
+#include <stdint.h>
+
 #include <memory>
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxge/dib/fx_dib.h"
 #include "fxbarcode/cbc_codebase.h"
 
 class CBC_OneDimWriter;
@@ -18,16 +19,9 @@
 
 class CBC_OneCode : public CBC_CodeBase {
  public:
-  // Limit the size of 1D barcodes. Typical 1D barcodes are short so this should
-  // be sufficient for most use cases.
-  static constexpr size_t kMaxInputLengthBytes = 8192;
-
   explicit CBC_OneCode(std::unique_ptr<CBC_Writer> pWriter);
   ~CBC_OneCode() override;
 
-  virtual bool CheckContentValidity(WideStringView contents);
-  virtual WideString FilterContents(WideStringView contents);
-
   void SetPrintChecksum(bool checksum);
   void SetDataLength(int32_t length);
   void SetCalChecksum(bool calc);
diff --git a/fxbarcode/cbc_pdf417i.cpp b/fxbarcode/cbc_pdf417i.cpp
index f91f5c6..cb237a9 100644
--- a/fxbarcode/cbc_pdf417i.cpp
+++ b/fxbarcode/cbc_pdf417i.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -21,10 +21,12 @@
 
 #include "fxbarcode/cbc_pdf417i.h"
 
-#include <vector>
+#include <stdint.h>
 
+#include <memory>
+
+#include "core/fxcrt/data_vector.h"
 #include "fxbarcode/pdf417/BC_PDF417Writer.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -35,9 +37,9 @@
 }  // namespace
 
 CBC_PDF417I::CBC_PDF417I()
-    : CBC_CodeBase(pdfium::MakeUnique<CBC_PDF417Writer>()) {}
+    : CBC_CodeBase(std::make_unique<CBC_PDF417Writer>()) {}
 
-CBC_PDF417I::~CBC_PDF417I() {}
+CBC_PDF417I::~CBC_PDF417I() = default;
 
 bool CBC_PDF417I::Encode(WideStringView contents) {
   if (contents.GetLength() > kMaxPDF417InputLengthBytes)
@@ -46,18 +48,18 @@
   int32_t width;
   int32_t height;
   auto* pWriter = GetPDF417Writer();
-  std::vector<uint8_t> data = pWriter->Encode(contents, &width, &height);
+  DataVector<uint8_t> data = pWriter->Encode(contents, &width, &height);
   return pWriter->RenderResult(data, width, height);
 }
 
 bool CBC_PDF417I::RenderDevice(CFX_RenderDevice* device,
-                               const CFX_Matrix* matrix) {
+                               const CFX_Matrix& matrix) {
   GetPDF417Writer()->RenderDeviceResult(device, matrix);
   return true;
 }
 
 BC_TYPE CBC_PDF417I::GetType() {
-  return BC_PDF417;
+  return BC_TYPE::kPDF417;
 }
 
 CBC_PDF417Writer* CBC_PDF417I::GetPDF417Writer() {
diff --git a/fxbarcode/cbc_pdf417i.h b/fxbarcode/cbc_pdf417i.h
index ab773f9..d46b4ab 100644
--- a/fxbarcode/cbc_pdf417i.h
+++ b/fxbarcode/cbc_pdf417i.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,12 @@
 #ifndef FXBARCODE_CBC_PDF417I_H_
 #define FXBARCODE_CBC_PDF417I_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/widestring.h"
 #include "fxbarcode/cbc_codebase.h"
 
 class CBC_PDF417Writer;
+class CFX_Matrix;
+class CFX_RenderDevice;
 
 class CBC_PDF417I final : public CBC_CodeBase {
  public:
@@ -21,7 +22,7 @@
   // CBC_CodeBase:
   bool Encode(WideStringView contents) override;
   bool RenderDevice(CFX_RenderDevice* device,
-                    const CFX_Matrix* matrix) override;
+                    const CFX_Matrix& matrix) override;
   BC_TYPE GetType() override;
 
  private:
diff --git a/fxbarcode/cbc_pdf417i_unittest.cpp b/fxbarcode/cbc_pdf417i_unittest.cpp
index 94e69fb..dd71b6e 100644
--- a/fxbarcode/cbc_pdf417i_unittest.cpp
+++ b/fxbarcode/cbc_pdf417i_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/cbc_qrcode.cpp b/fxbarcode/cbc_qrcode.cpp
index 5e60fa3..841cf9e 100644
--- a/fxbarcode/cbc_qrcode.cpp
+++ b/fxbarcode/cbc_qrcode.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -21,13 +21,14 @@
 
 #include "fxbarcode/cbc_qrcode.h"
 
-#include <vector>
+#include <stdint.h>
 
+#include <memory>
+
+#include "core/fxcrt/data_vector.h"
 #include "fxbarcode/qrcode/BC_QRCodeWriter.h"
-#include "third_party/base/ptr_util.h"
 
-CBC_QRCode::CBC_QRCode()
-    : CBC_CodeBase(pdfium::MakeUnique<CBC_QRCodeWriter>()) {}
+CBC_QRCode::CBC_QRCode() : CBC_CodeBase(std::make_unique<CBC_QRCodeWriter>()) {}
 
 CBC_QRCode::~CBC_QRCode() = default;
 
@@ -35,19 +36,19 @@
   int32_t width;
   int32_t height;
   CBC_QRCodeWriter* pWriter = GetQRCodeWriter();
-  std::vector<uint8_t> data = pWriter->Encode(
+  DataVector<uint8_t> data = pWriter->Encode(
       contents, pWriter->error_correction_level(), &width, &height);
   return pWriter->RenderResult(data, width, height);
 }
 
 bool CBC_QRCode::RenderDevice(CFX_RenderDevice* device,
-                              const CFX_Matrix* matrix) {
+                              const CFX_Matrix& matrix) {
   GetQRCodeWriter()->RenderDeviceResult(device, matrix);
   return true;
 }
 
 BC_TYPE CBC_QRCode::GetType() {
-  return BC_QR_CODE;
+  return BC_TYPE::kQRCode;
 }
 
 CBC_QRCodeWriter* CBC_QRCode::GetQRCodeWriter() {
diff --git a/fxbarcode/cbc_qrcode.h b/fxbarcode/cbc_qrcode.h
index c541b7e..fcb4302 100644
--- a/fxbarcode/cbc_qrcode.h
+++ b/fxbarcode/cbc_qrcode.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,12 @@
 #ifndef FXBARCODE_CBC_QRCODE_H_
 #define FXBARCODE_CBC_QRCODE_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/widestring.h"
 #include "fxbarcode/cbc_codebase.h"
 
 class CBC_QRCodeWriter;
+class CFX_Matrix;
+class CFX_RenderDevice;
 
 class CBC_QRCode final : public CBC_CodeBase {
  public:
@@ -21,7 +22,7 @@
   // CBC_CodeBase:
   bool Encode(WideStringView contents) override;
   bool RenderDevice(CFX_RenderDevice* device,
-                    const CFX_Matrix* matrix) override;
+                    const CFX_Matrix& matrix) override;
   BC_TYPE GetType() override;
 
  private:
diff --git a/fxbarcode/cbc_upca.cpp b/fxbarcode/cbc_upca.cpp
index 3a3e87f..665c76c 100644
--- a/fxbarcode/cbc_upca.cpp
+++ b/fxbarcode/cbc_upca.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -24,18 +24,13 @@
 #include <memory>
 
 #include "fxbarcode/oned/BC_OnedUPCAWriter.h"
-#include "third_party/base/ptr_util.h"
 
-CBC_UPCA::CBC_UPCA() : CBC_EANCode(pdfium::MakeUnique<CBC_OnedUPCAWriter>()) {}
+CBC_UPCA::CBC_UPCA() : CBC_EANCode(std::make_unique<CBC_OnedUPCAWriter>()) {}
 
 CBC_UPCA::~CBC_UPCA() = default;
 
 BC_TYPE CBC_UPCA::GetType() {
-  return BC_UPCA;
-}
-
-BCFORMAT CBC_UPCA::GetFormat() const {
-  return BCFORMAT_UPC_A;
+  return BC_TYPE::kUPCA;
 }
 
 size_t CBC_UPCA::GetMaxLength() const {
diff --git a/fxbarcode/cbc_upca.h b/fxbarcode/cbc_upca.h
index bf71fc9..f919336 100644
--- a/fxbarcode/cbc_upca.h
+++ b/fxbarcode/cbc_upca.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,10 @@
 #ifndef FXBARCODE_CBC_UPCA_H_
 #define FXBARCODE_CBC_UPCA_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "fxbarcode/cbc_eancode.h"
+#include <stddef.h>
 
-class CBC_OnedUPCAWriter;
+#include "fxbarcode/BC_Library.h"
+#include "fxbarcode/cbc_eancode.h"
 
 class CBC_UPCA final : public CBC_EANCode {
  public:
@@ -20,7 +19,6 @@
 
   // CBC_EANCode:
   BC_TYPE GetType() override;
-  BCFORMAT GetFormat() const override;
   size_t GetMaxLength() const override;
 };
 
diff --git a/fxbarcode/cfx_barcode.cpp b/fxbarcode/cfx_barcode.cpp
index 54addad..5b2c134 100644
--- a/fxbarcode/cfx_barcode.cpp
+++ b/fxbarcode/cfx_barcode.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,46 +18,46 @@
 #include "fxbarcode/cbc_pdf417i.h"
 #include "fxbarcode/cbc_qrcode.h"
 #include "fxbarcode/cbc_upca.h"
-#include "fxbarcode/utils.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/memory/ptr_util.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
 std::unique_ptr<CBC_CodeBase> CreateBarCodeEngineObject(BC_TYPE type) {
   switch (type) {
-    case BC_CODE39:
-      return pdfium::MakeUnique<CBC_Code39>();
-    case BC_CODABAR:
-      return pdfium::MakeUnique<CBC_Codabar>();
-    case BC_CODE128:
-      return pdfium::MakeUnique<CBC_Code128>(BC_CODE128_B);
-    case BC_CODE128_B:
-      return pdfium::MakeUnique<CBC_Code128>(BC_CODE128_B);
-    case BC_CODE128_C:
-      return pdfium::MakeUnique<CBC_Code128>(BC_CODE128_C);
-    case BC_EAN8:
-      return pdfium::MakeUnique<CBC_EAN8>();
-    case BC_UPCA:
-      return pdfium::MakeUnique<CBC_UPCA>();
-    case BC_EAN13:
-      return pdfium::MakeUnique<CBC_EAN13>();
-    case BC_QR_CODE:
-      return pdfium::MakeUnique<CBC_QRCode>();
-    case BC_PDF417:
-      return pdfium::MakeUnique<CBC_PDF417I>();
-    case BC_DATAMATRIX:
-      return pdfium::MakeUnique<CBC_DataMatrix>();
-    case BC_UNKNOWN:
-    default:
+    case BC_TYPE::kCode39:
+      return std::make_unique<CBC_Code39>();
+    case BC_TYPE::kCodabar:
+      return std::make_unique<CBC_Codabar>();
+    case BC_TYPE::kCode128:
+      return std::make_unique<CBC_Code128>(BC_TYPE::kCode128B);
+    case BC_TYPE::kCode128B:
+      return std::make_unique<CBC_Code128>(BC_TYPE::kCode128B);
+    case BC_TYPE::kCode128C:
+      return std::make_unique<CBC_Code128>(BC_TYPE::kCode128C);
+    case BC_TYPE::kEAN8:
+      return std::make_unique<CBC_EAN8>();
+    case BC_TYPE::kUPCA:
+      return std::make_unique<CBC_UPCA>();
+    case BC_TYPE::kEAN13:
+      return std::make_unique<CBC_EAN13>();
+    case BC_TYPE::kQRCode:
+      return std::make_unique<CBC_QRCode>();
+    case BC_TYPE::kPDF417:
+      return std::make_unique<CBC_PDF417I>();
+    case BC_TYPE::kDataMatrix:
+      return std::make_unique<CBC_DataMatrix>();
+    case BC_TYPE::kUnknown:
       return nullptr;
   }
+  NOTREACHED_NORETURN();
 }
 
 }  // namespace
 
-CFX_Barcode::CFX_Barcode() {}
+CFX_Barcode::CFX_Barcode() = default;
 
-CFX_Barcode::~CFX_Barcode() {}
+CFX_Barcode::~CFX_Barcode() = default;
 
 std::unique_ptr<CFX_Barcode> CFX_Barcode::Create(BC_TYPE type) {
   auto barcode = pdfium::WrapUnique(new CFX_Barcode());  // Private ctor.
@@ -66,11 +66,12 @@
 }
 
 BC_TYPE CFX_Barcode::GetType() {
-  return m_pBCEngine ? m_pBCEngine->GetType() : BC_UNKNOWN;
+  return m_pBCEngine ? m_pBCEngine->GetType() : BC_TYPE::kUnknown;
 }
 
-bool CFX_Barcode::SetCharEncoding(BC_CHAR_ENCODING encoding) {
-  return m_pBCEngine && m_pBCEngine->SetCharEncoding(encoding);
+void CFX_Barcode::SetCharEncoding(BC_CHAR_ENCODING encoding) {
+  if (m_pBCEngine)
+    m_pBCEngine->SetCharEncoding(encoding);
 }
 
 bool CFX_Barcode::SetModuleHeight(int32_t moduleHeight) {
@@ -81,129 +82,138 @@
   return m_pBCEngine && m_pBCEngine->SetModuleWidth(moduleWidth);
 }
 
-bool CFX_Barcode::SetHeight(int32_t height) {
-  return m_pBCEngine && m_pBCEngine->SetHeight(height);
+void CFX_Barcode::SetHeight(int32_t height) {
+  if (m_pBCEngine)
+    m_pBCEngine->SetHeight(height);
 }
 
-bool CFX_Barcode::SetWidth(int32_t width) {
-  return m_pBCEngine && m_pBCEngine->SetWidth(width);
+void CFX_Barcode::SetWidth(int32_t width) {
+  if (m_pBCEngine)
+    m_pBCEngine->SetWidth(width);
 }
 
 bool CFX_Barcode::SetPrintChecksum(bool checksum) {
+  if (!m_pBCEngine)
+    return false;
+
   switch (GetType()) {
-    case BC_CODE39:
-    case BC_CODABAR:
-    case BC_CODE128:
-    case BC_CODE128_B:
-    case BC_CODE128_C:
-    case BC_EAN8:
-    case BC_EAN13:
-    case BC_UPCA:
-      return m_pBCEngine ? (static_cast<CBC_OneCode*>(m_pBCEngine.get())
-                                ->SetPrintChecksum(checksum),
-                            true)
-                         : false;
+    case BC_TYPE::kCode39:
+    case BC_TYPE::kCodabar:
+    case BC_TYPE::kCode128:
+    case BC_TYPE::kCode128B:
+    case BC_TYPE::kCode128C:
+    case BC_TYPE::kEAN8:
+    case BC_TYPE::kEAN13:
+    case BC_TYPE::kUPCA:
+      static_cast<CBC_OneCode*>(m_pBCEngine.get())->SetPrintChecksum(checksum);
+      return true;
     default:
       return false;
   }
 }
 
 bool CFX_Barcode::SetDataLength(int32_t length) {
+  if (!m_pBCEngine)
+    return false;
+
   switch (GetType()) {
-    case BC_CODE39:
-    case BC_CODABAR:
-    case BC_CODE128:
-    case BC_CODE128_B:
-    case BC_CODE128_C:
-    case BC_EAN8:
-    case BC_EAN13:
-    case BC_UPCA:
-      return m_pBCEngine ? (static_cast<CBC_OneCode*>(m_pBCEngine.get())
-                                ->SetDataLength(length),
-                            true)
-                         : false;
+    case BC_TYPE::kCode39:
+    case BC_TYPE::kCodabar:
+    case BC_TYPE::kCode128:
+    case BC_TYPE::kCode128B:
+    case BC_TYPE::kCode128C:
+    case BC_TYPE::kEAN8:
+    case BC_TYPE::kEAN13:
+    case BC_TYPE::kUPCA:
+      static_cast<CBC_OneCode*>(m_pBCEngine.get())->SetDataLength(length);
+      return true;
     default:
       return false;
   }
 }
 
 bool CFX_Barcode::SetCalChecksum(bool state) {
+  if (!m_pBCEngine)
+    return false;
+
   switch (GetType()) {
-    case BC_CODE39:
-    case BC_CODABAR:
-    case BC_CODE128:
-    case BC_CODE128_B:
-    case BC_CODE128_C:
-    case BC_EAN8:
-    case BC_EAN13:
-    case BC_UPCA:
-      return m_pBCEngine ? (static_cast<CBC_OneCode*>(m_pBCEngine.get())
-                                ->SetCalChecksum(state),
-                            true)
-                         : false;
+    case BC_TYPE::kCode39:
+    case BC_TYPE::kCodabar:
+    case BC_TYPE::kCode128:
+    case BC_TYPE::kCode128B:
+    case BC_TYPE::kCode128C:
+    case BC_TYPE::kEAN8:
+    case BC_TYPE::kEAN13:
+    case BC_TYPE::kUPCA:
+      static_cast<CBC_OneCode*>(m_pBCEngine.get())->SetCalChecksum(state);
+      return true;
     default:
       return false;
   }
 }
 
 bool CFX_Barcode::SetFont(CFX_Font* pFont) {
+  if (!m_pBCEngine)
+    return false;
+
   switch (GetType()) {
-    case BC_CODE39:
-    case BC_CODABAR:
-    case BC_CODE128:
-    case BC_CODE128_B:
-    case BC_CODE128_C:
-    case BC_EAN8:
-    case BC_EAN13:
-    case BC_UPCA:
-      return m_pBCEngine
-                 ? static_cast<CBC_OneCode*>(m_pBCEngine.get())->SetFont(pFont)
-                 : false;
+    case BC_TYPE::kCode39:
+    case BC_TYPE::kCodabar:
+    case BC_TYPE::kCode128:
+    case BC_TYPE::kCode128B:
+    case BC_TYPE::kCode128C:
+    case BC_TYPE::kEAN8:
+    case BC_TYPE::kEAN13:
+    case BC_TYPE::kUPCA:
+      return static_cast<CBC_OneCode*>(m_pBCEngine.get())->SetFont(pFont);
     default:
       return false;
   }
 }
 
 bool CFX_Barcode::SetFontSize(float size) {
+  if (!m_pBCEngine)
+    return false;
+
   switch (GetType()) {
-    case BC_CODE39:
-    case BC_CODABAR:
-    case BC_CODE128:
-    case BC_CODE128_B:
-    case BC_CODE128_C:
-    case BC_EAN8:
-    case BC_EAN13:
-    case BC_UPCA:
-      return m_pBCEngine ? (static_cast<CBC_OneCode*>(m_pBCEngine.get())
-                                ->SetFontSize(size),
-                            true)
-                         : false;
+    case BC_TYPE::kCode39:
+    case BC_TYPE::kCodabar:
+    case BC_TYPE::kCode128:
+    case BC_TYPE::kCode128B:
+    case BC_TYPE::kCode128C:
+    case BC_TYPE::kEAN8:
+    case BC_TYPE::kEAN13:
+    case BC_TYPE::kUPCA:
+      static_cast<CBC_OneCode*>(m_pBCEngine.get())->SetFontSize(size);
+      return true;
     default:
       return false;
   }
 }
 
 bool CFX_Barcode::SetFontColor(FX_ARGB color) {
+  if (!m_pBCEngine)
+    return false;
+
   switch (GetType()) {
-    case BC_CODE39:
-    case BC_CODABAR:
-    case BC_CODE128:
-    case BC_CODE128_B:
-    case BC_CODE128_C:
-    case BC_EAN8:
-    case BC_EAN13:
-    case BC_UPCA:
-      return m_pBCEngine ? (static_cast<CBC_OneCode*>(m_pBCEngine.get())
-                                ->SetFontColor(color),
-                            true)
-                         : false;
+    case BC_TYPE::kCode39:
+    case BC_TYPE::kCodabar:
+    case BC_TYPE::kCode128:
+    case BC_TYPE::kCode128B:
+    case BC_TYPE::kCode128C:
+    case BC_TYPE::kEAN8:
+    case BC_TYPE::kEAN13:
+    case BC_TYPE::kUPCA:
+      static_cast<CBC_OneCode*>(m_pBCEngine.get())->SetFontColor(color);
+      return true;
     default:
       return false;
   }
 }
 
-bool CFX_Barcode::SetTextLocation(BC_TEXT_LOC location) {
-  return m_pBCEngine && m_pBCEngine->SetTextLocation(location);
+void CFX_Barcode::SetTextLocation(BC_TEXT_LOC location) {
+  if (m_pBCEngine)
+    m_pBCEngine->SetTextLocation(location);
 }
 
 bool CFX_Barcode::SetWideNarrowRatio(int8_t ratio) {
@@ -227,6 +237,6 @@
 }
 
 bool CFX_Barcode::RenderDevice(CFX_RenderDevice* device,
-                               const CFX_Matrix* matrix) {
+                               const CFX_Matrix& matrix) {
   return m_pBCEngine && m_pBCEngine->RenderDevice(device, matrix);
 }
diff --git a/fxbarcode/cfx_barcode.h b/fxbarcode/cfx_barcode.h
index 14204ea..dda54e9 100644
--- a/fxbarcode/cfx_barcode.h
+++ b/fxbarcode/cfx_barcode.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #ifndef FXBARCODE_CFX_BARCODE_H_
 #define FXBARCODE_CFX_BARCODE_H_
 
+#include <stdint.h>
+
 #include <memory>
 
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxcrt/widestring.h"
+#include "core/fxge/dib/fx_dib.h"
 #include "fxbarcode/BC_Library.h"
 
 class CBC_CodeBase;
@@ -28,15 +28,13 @@
   BC_TYPE GetType();
   bool Encode(WideStringView contents);
 
-  bool RenderDevice(CFX_RenderDevice* device, const CFX_Matrix* matrix);
+  bool RenderDevice(CFX_RenderDevice* device, const CFX_Matrix& matrix);
 
-  bool SetCharEncoding(BC_CHAR_ENCODING encoding);
-
+  void SetCharEncoding(BC_CHAR_ENCODING encoding);
   bool SetModuleHeight(int32_t moduleHeight);
   bool SetModuleWidth(int32_t moduleWidth);
-
-  bool SetHeight(int32_t height);
-  bool SetWidth(int32_t width);
+  void SetHeight(int32_t height);
+  void SetWidth(int32_t width);
 
   bool SetPrintChecksum(bool checksum);
   bool SetDataLength(int32_t length);
@@ -46,8 +44,7 @@
   bool SetFontSize(float size);
   bool SetFontColor(FX_ARGB color);
 
-  bool SetTextLocation(BC_TEXT_LOC location);
-
+  void SetTextLocation(BC_TEXT_LOC location);
   bool SetWideNarrowRatio(int8_t ratio);
   bool SetStartChar(char start);
   bool SetEndChar(char end);
diff --git a/fxbarcode/cfx_barcode_unittest.cpp b/fxbarcode/cfx_barcode_unittest.cpp
index 7569bf2..d7a43c2 100644
--- a/fxbarcode/cfx_barcode_unittest.cpp
+++ b/fxbarcode/cfx_barcode_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,19 +16,18 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/utils/bitmap_saver.h"
 #include "testing/utils/hash.h"
-#include "third_party/base/ptr_util.h"
 
 class BarcodeTest : public testing::Test {
  public:
   void SetUp() override {
     BC_Library_Init();
 
-    auto device = pdfium::MakeUnique<CFX_DefaultRenderDevice>();
+    auto device = std::make_unique<CFX_DefaultRenderDevice>();
     auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-    if (bitmap->Create(640, 480, FXDIB_Rgb32))
+    if (bitmap->Create(640, 480, FXDIB_Format::kRgb32))
       bitmap_ = bitmap;
     ASSERT_TRUE(bitmap_);
-    ASSERT_TRUE(device->Attach(bitmap_, false, nullptr, false));
+    ASSERT_TRUE(device->Attach(bitmap_));
     device_ = std::move(device);
   }
 
@@ -47,13 +46,10 @@
     barcode_->SetWidth(418);
   }
 
-  bool RenderDevice() {
-    return barcode_->RenderDevice(device_.get(), &matrix_);
-  }
+  bool RenderDevice() { return barcode_->RenderDevice(device_.get(), matrix_); }
 
   std::string BitmapChecksum() {
-    return GenerateMD5Base16(bitmap_->GetBuffer(),
-                             bitmap_->GetPitch() * bitmap_->GetHeight());
+    return GenerateMD5Base16(bitmap_->GetBuffer());
   }
 
   // Manually insert calls to this as needed for debugging.
@@ -69,168 +65,176 @@
 };
 
 // https://crbug.com/pdfium/738
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#if defined(_SKIA_SUPPORT_)
 #define MAYBE_Code39 DISABLED_Code39
 #else
 #define MAYBE_Code39 Code39
 #endif
 TEST_F(BarcodeTest, MAYBE_Code39) {
-  Create(BC_CODE39);
+  Create(BC_TYPE::kCode39);
   EXPECT_TRUE(barcode()->Encode(L"CLAMS"));
   RenderDevice();
   EXPECT_EQ("cd4cd3f36da38ff58d9f621827018903", BitmapChecksum());
 }
 
 // https://crbug.com/pdfium/738
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#if defined(_SKIA_SUPPORT_)
 #define MAYBE_CodaBar DISABLED_CodaBar
 #else
 #define MAYBE_CodaBar CodaBar
 #endif
 TEST_F(BarcodeTest, MAYBE_CodaBar) {
-  Create(BC_CODABAR);
+  Create(BC_TYPE::kCodabar);
   EXPECT_TRUE(barcode()->Encode(L"$123-456"));
   RenderDevice();
   EXPECT_EQ("5fad4fc19f099001a0fe83c89430c977", BitmapChecksum());
 }
 
-TEST_F(BarcodeTest, DISABLED_CodaBarLetters) {
-  Create(BC_CODABAR);
+TEST_F(BarcodeTest, CodaBarLetters) {
+  Create(BC_TYPE::kCodabar);
   EXPECT_FALSE(barcode()->Encode(L"clams"));
 }
 
 // https://crbug.com/pdfium/738
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#if defined(_SKIA_SUPPORT_)
 #define MAYBE_Code128 DISABLED_Code128
 #else
 #define MAYBE_Code128 Code128
 #endif
 TEST_F(BarcodeTest, MAYBE_Code128) {
-  Create(BC_CODE128);
+  Create(BC_TYPE::kCode128);
   EXPECT_TRUE(barcode()->Encode(L"Clams"));
   RenderDevice();
   EXPECT_EQ("6351f0f6e997050e4658bbb4777aef74", BitmapChecksum());
 }
 
 // https://crbug.com/pdfium/738
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#if defined(_SKIA_SUPPORT_)
 #define MAYBE_Code128B DISABLED_Code128B
 #else
 #define MAYBE_Code128B Code128B
 #endif
 TEST_F(BarcodeTest, MAYBE_Code128B) {
-  Create(BC_CODE128_B);
+  Create(BC_TYPE::kCode128B);
   EXPECT_TRUE(barcode()->Encode(L"Clams"));
   RenderDevice();
   EXPECT_EQ("6351f0f6e997050e4658bbb4777aef74", BitmapChecksum());
 }
 
 // https://crbug.com/pdfium/738
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#if defined(_SKIA_SUPPORT_)
 #define MAYBE_Code128C DISABLED_Code128C
 #else
 #define MAYBE_Code128C Code128C
 #endif
 TEST_F(BarcodeTest, MAYBE_Code128C) {
-  Create(BC_CODE128_C);
+  Create(BC_TYPE::kCode128C);
   EXPECT_TRUE(barcode()->Encode(L"123456"));
   RenderDevice();
   EXPECT_EQ("fba730a807ba6363f9bd2bc7f8c56d1f", BitmapChecksum());
 }
 
-TEST_F(BarcodeTest, DISABLED_Code128CLetters) {
-  Create(BC_CODE128_C);
-  EXPECT_FALSE(barcode()->Encode(L"clams"));
+// https://crbug.com/pdfium/738
+#if defined(_SKIA_SUPPORT_)
+#define MAYBE_Code128CLetters DISABLED_Code128CLetters
+#else
+#define MAYBE_Code128CLetters Code128CLetters
+#endif
+TEST_F(BarcodeTest, MAYBE_Code128CLetters) {
+  Create(BC_TYPE::kCode128C);
+  EXPECT_TRUE(barcode()->Encode(L"clams"));
+  RenderDevice();
+  EXPECT_EQ("6284ec8503d5a948c9518108da33cdd3", BitmapChecksum());
 }
 
 // https://crbug.com/pdfium/738
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#if defined(_SKIA_SUPPORT_)
 #define MAYBE_Ean8 DISABLED_Ean8
 #else
 #define MAYBE_Ean8 Ean8
 #endif
 TEST_F(BarcodeTest, MAYBE_Ean8) {
-  Create(BC_EAN8);
+  Create(BC_TYPE::kEAN8);
   EXPECT_TRUE(barcode()->Encode(L"123456"));
   RenderDevice();
   EXPECT_EQ("aff88491ac46ca6217d780d185300cde", BitmapChecksum());
 }
 
-TEST_F(BarcodeTest, DISABLED_Ean8Letters) {
-  Create(BC_EAN8);
+TEST_F(BarcodeTest, Ean8Letters) {
+  Create(BC_TYPE::kEAN8);
   EXPECT_FALSE(barcode()->Encode(L"clams"));
 }
 
 // https://crbug.com/pdfium/738
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#if defined(_SKIA_SUPPORT_)
 #define MAYBE_UPCA DISABLED_UPCA
 #else
 #define MAYBE_UPCA UPCA
 #endif
 TEST_F(BarcodeTest, MAYBE_UPCA) {
-  Create(BC_UPCA);
+  Create(BC_TYPE::kUPCA);
   EXPECT_TRUE(barcode()->Encode(L"123456"));
   RenderDevice();
   EXPECT_EQ("fe26a5714cff7ffe3f9b02183efc435b", BitmapChecksum());
 }
 
-TEST_F(BarcodeTest, DISABLED_UPCALetters) {
-  Create(BC_UPCA);
+TEST_F(BarcodeTest, UPCALetters) {
+  Create(BC_TYPE::kUPCA);
   EXPECT_FALSE(barcode()->Encode(L"clams"));
 }
 
 // https://crbug.com/pdfium/738
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#if defined(_SKIA_SUPPORT_)
 #define MAYBE_Ean13 DISABLED_Ean13
 #else
 #define MAYBE_Ean13 Ean13
 #endif
 TEST_F(BarcodeTest, MAYBE_Ean13) {
-  Create(BC_EAN13);
+  Create(BC_TYPE::kEAN13);
   EXPECT_TRUE(barcode()->Encode(L"123456"));
   RenderDevice();
   EXPECT_EQ("72d2190b98d635c32834bf67552e561e", BitmapChecksum());
 }
 
-TEST_F(BarcodeTest, DISABLED_Ean13Letters) {
-  Create(BC_EAN13);
+TEST_F(BarcodeTest, Ean13Letters) {
+  Create(BC_TYPE::kEAN13);
   EXPECT_FALSE(barcode()->Encode(L"clams"));
 }
 
 // https://crbug.com/pdfium/738
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#if defined(_SKIA_SUPPORT_)
 #define MAYBE_Pdf417 DISABLED_Pdf417
 #else
 #define MAYBE_Pdf417 Pdf417
 #endif
 TEST_F(BarcodeTest, MAYBE_Pdf417) {
-  Create(BC_PDF417);
+  Create(BC_TYPE::kPDF417);
   EXPECT_TRUE(barcode()->Encode(L"clams"));
   RenderDevice();
   EXPECT_EQ("191e35d11613901b7d5d51033689aa89", BitmapChecksum());
 }
 
 // https://crbug.com/pdfium/738
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#if defined(_SKIA_SUPPORT_)
 #define MAYBE_DataMatrix DISABLED_DataMatrix
 #else
 #define MAYBE_DataMatrix DataMatrix
 #endif
 TEST_F(BarcodeTest, MAYBE_DataMatrix) {
-  Create(BC_DATAMATRIX);
+  Create(BC_TYPE::kDataMatrix);
   EXPECT_TRUE(barcode()->Encode(L"clams"));
   RenderDevice();
   EXPECT_EQ("5e5cd9a680b86fcd4ffd53ed36e3c980", BitmapChecksum());
 }
 
 // https://crbug.com/pdfium/738
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#if defined(_SKIA_SUPPORT_)
 #define MAYBE_QrCode DISABLED_QrCode
 #else
 #define MAYBE_QrCode QrCode
 #endif
 TEST_F(BarcodeTest, MAYBE_QrCode) {
-  Create(BC_QR_CODE);
+  Create(BC_TYPE::kQRCode);
   EXPECT_TRUE(barcode()->Encode(L"clams"));
   RenderDevice();
   EXPECT_EQ("4751c6e0f67749fabe24f787128decee", BitmapChecksum());
diff --git a/fxbarcode/common/BC_CommonBitMatrix.cpp b/fxbarcode/common/BC_CommonBitMatrix.cpp
index 4cf07ed..92dd917 100644
--- a/fxbarcode/common/BC_CommonBitMatrix.cpp
+++ b/fxbarcode/common/BC_CommonBitMatrix.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,32 +22,24 @@
 
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 
-#include <algorithm>
-#include <iterator>
+#include "core/fxcrt/fixed_zeroed_data_vector.h"
+#include "third_party/base/check_op.h"
 
-#include "third_party/base/stl_util.h"
-
-CBC_CommonBitMatrix::CBC_CommonBitMatrix() {}
-
-void CBC_CommonBitMatrix::Init(int32_t width, int32_t height) {
-  m_width = width;
-  m_height = height;
-  m_rowSize = (width + 31) >> 5;
-  m_bits = pdfium::Vector2D<int32_t>(m_rowSize, m_height);
+CBC_CommonBitMatrix::CBC_CommonBitMatrix(size_t width, size_t height)
+    : m_height(height), m_rowSize((width + 31) >> 5) {
+  static constexpr int32_t kMaxBits = 1024 * 1024 * 1024;  // 1 Gb.
+  CHECK_LT(m_rowSize, kMaxBits / m_height);
+  m_bits = FixedZeroedDataVector<uint32_t>(m_rowSize * m_height);
 }
 
 CBC_CommonBitMatrix::~CBC_CommonBitMatrix() = default;
 
-bool CBC_CommonBitMatrix::Get(int32_t x, int32_t y) const {
-  int32_t offset = y * m_rowSize + (x >> 5);
-  if (offset >= m_rowSize * m_height || offset < 0)
-    return false;
-  return ((((uint32_t)m_bits[offset]) >> (x & 0x1f)) & 1) != 0;
+bool CBC_CommonBitMatrix::Get(size_t x, size_t y) const {
+  size_t offset = y * m_rowSize + (x >> 5);
+  return ((m_bits.span()[offset] >> (x & 0x1f)) & 1) != 0;
 }
 
-void CBC_CommonBitMatrix::Set(int32_t x, int32_t y) {
-  int32_t offset = y * m_rowSize + (x >> 5);
-  ASSERT(offset >= 0);
-  ASSERT(offset < m_rowSize * m_height);
-  m_bits[offset] |= 1 << (x & 0x1f);
+void CBC_CommonBitMatrix::Set(size_t x, size_t y) {
+  size_t offset = y * m_rowSize + (x >> 5);
+  m_bits.writable_span()[offset] |= 1u << (x & 0x1f);
 }
diff --git a/fxbarcode/common/BC_CommonBitMatrix.h b/fxbarcode/common/BC_CommonBitMatrix.h
index ce40ade..3dabe8a 100644
--- a/fxbarcode/common/BC_CommonBitMatrix.h
+++ b/fxbarcode/common/BC_CommonBitMatrix.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,27 +7,23 @@
 #ifndef FXBARCODE_COMMON_BC_COMMONBITMATRIX_H_
 #define FXBARCODE_COMMON_BC_COMMONBITMATRIX_H_
 
-#include <vector>
+#include <stddef.h>
+#include <stdint.h>
 
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/fixed_zeroed_data_vector.h"
 
 class CBC_CommonBitMatrix {
  public:
-  CBC_CommonBitMatrix();
+  CBC_CommonBitMatrix(size_t width, size_t height);
   ~CBC_CommonBitMatrix();
 
-  void Init(int32_t width, int32_t height);
-
-  bool Get(int32_t x, int32_t y) const;
-  void Set(int32_t x, int32_t y);
-  int32_t GetWidth() const { return m_width; }
-  int32_t GetHeight() const { return m_height; }
+  bool Get(size_t x, size_t y) const;
+  void Set(size_t x, size_t y);
 
  private:
-  int32_t m_width = 0;
-  int32_t m_height = 0;
-  int32_t m_rowSize = 0;
-  std::vector<int32_t> m_bits;
+  const size_t m_height;
+  const size_t m_rowSize;
+  FixedZeroedDataVector<uint32_t> m_bits;
 };
 
 #endif  // FXBARCODE_COMMON_BC_COMMONBITMATRIX_H_
diff --git a/fxbarcode/common/BC_CommonByteMatrix.cpp b/fxbarcode/common/BC_CommonByteMatrix.cpp
index a05fde0..ee02b45 100644
--- a/fxbarcode/common/BC_CommonByteMatrix.cpp
+++ b/fxbarcode/common/BC_CommonByteMatrix.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,28 +23,37 @@
 
 #include <algorithm>
 #include <iterator>
-#include "third_party/base/stl_util.h"
+#include <utility>
 
-CBC_CommonByteMatrix::CBC_CommonByteMatrix(int32_t width, int32_t height)
+#include "core/fxcrt/data_vector.h"
+#include "third_party/base/check_op.h"
+
+CBC_CommonByteMatrix::CBC_CommonByteMatrix(size_t width, size_t height)
     : m_width(width), m_height(height) {
-  m_bytes = pdfium::Vector2D<uint8_t>(m_height, m_width);
-  clear(0xff);
+  static constexpr size_t kMaxBytes = 256 * 1024 * 1024;  // 256 MB.
+  static constexpr uint8_t kDefaultFill = 0xff;
+  CHECK_LT(m_width, kMaxBytes / m_height);
+  m_bytes.resize(m_width * m_height, kDefaultFill);
 }
 
 CBC_CommonByteMatrix::~CBC_CommonByteMatrix() = default;
 
-uint8_t CBC_CommonByteMatrix::Get(int32_t x, int32_t y) const {
-  return m_bytes[y * m_width + x];
+DataVector<uint8_t> CBC_CommonByteMatrix::TakeArray() {
+  return std::move(m_bytes);
 }
 
-void CBC_CommonByteMatrix::Set(int32_t x, int32_t y, int32_t value) {
-  m_bytes[y * m_width + x] = (uint8_t)value;
+uint8_t CBC_CommonByteMatrix::Get(size_t x, size_t y) const {
+  const size_t offset = y * m_width + x;
+  CHECK_LT(offset, m_bytes.size());
+  return m_bytes[offset];
 }
 
-void CBC_CommonByteMatrix::Set(int32_t x, int32_t y, uint8_t value) {
-  m_bytes[y * m_width + x] = value;
+void CBC_CommonByteMatrix::Set(size_t x, size_t y, uint8_t value) {
+  const size_t offset = y * m_width + x;
+  CHECK_LT(offset, m_bytes.size());
+  m_bytes[offset] = value;
 }
 
-void CBC_CommonByteMatrix::clear(uint8_t value) {
+void CBC_CommonByteMatrix::Fill(uint8_t value) {
   std::fill(std::begin(m_bytes), std::end(m_bytes), value);
 }
diff --git a/fxbarcode/common/BC_CommonByteMatrix.h b/fxbarcode/common/BC_CommonByteMatrix.h
index c5a66a3..fa97f69 100644
--- a/fxbarcode/common/BC_CommonByteMatrix.h
+++ b/fxbarcode/common/BC_CommonByteMatrix.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,29 +9,27 @@
 
 #include <stdint.h>
 
-#include <vector>
-
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/span.h"
+#include "core/fxcrt/data_vector.h"
+#include "third_party/base/containers/span.h"
 
 class CBC_CommonByteMatrix final {
  public:
-  CBC_CommonByteMatrix(int32_t width, int32_t height);
+  CBC_CommonByteMatrix(size_t width, size_t height);
   ~CBC_CommonByteMatrix();
 
-  int32_t GetWidth() const { return m_width; }
-  int32_t GetHeight() const { return m_height; }
+  size_t GetWidth() const { return m_width; }
+  size_t GetHeight() const { return m_height; }
   pdfium::span<const uint8_t> GetArray() const { return m_bytes; }
-  uint8_t Get(int32_t x, int32_t y) const;
+  DataVector<uint8_t> TakeArray();
 
-  void Set(int32_t x, int32_t y, int32_t value);
-  void Set(int32_t x, int32_t y, uint8_t value);
-  void clear(uint8_t value);
+  uint8_t Get(size_t x, size_t y) const;
+  void Set(size_t x, size_t y, uint8_t value);
+  void Fill(uint8_t value);
 
  private:
-  int32_t m_width;
-  int32_t m_height;
-  std::vector<uint8_t> m_bytes;
+  const size_t m_width;
+  const size_t m_height;
+  DataVector<uint8_t> m_bytes;
 };
 
 #endif  // FXBARCODE_COMMON_BC_COMMONBYTEMATRIX_H_
diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomon.cpp b/fxbarcode/common/reedsolomon/BC_ReedSolomon.cpp
index f19886e..c1785f2 100644
--- a/fxbarcode/common/reedsolomon/BC_ReedSolomon.cpp
+++ b/fxbarcode/common/reedsolomon/BC_ReedSolomon.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -27,23 +27,21 @@
 
 #include "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h"
 #include "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h"
-#include "third_party/base/ptr_util.h"
 
 CBC_ReedSolomonEncoder::CBC_ReedSolomonEncoder(CBC_ReedSolomonGF256* field)
     : m_field(field) {
-  m_cachedGenerators.push_back(pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(
-      m_field.Get(), std::vector<int32_t>{1}));
+  m_cachedGenerators.push_back(std::make_unique<CBC_ReedSolomonGF256Poly>(
+      m_field, std::vector<int32_t>{1}));
 }
 
-CBC_ReedSolomonEncoder::~CBC_ReedSolomonEncoder() {}
+CBC_ReedSolomonEncoder::~CBC_ReedSolomonEncoder() = default;
 
 CBC_ReedSolomonGF256Poly* CBC_ReedSolomonEncoder::BuildGenerator(
     size_t degree) {
   if (degree >= m_cachedGenerators.size()) {
     CBC_ReedSolomonGF256Poly* lastGenerator = m_cachedGenerators.back().get();
     for (size_t d = m_cachedGenerators.size(); d <= degree; ++d) {
-      CBC_ReedSolomonGF256Poly temp_poly(m_field.Get(),
-                                         {1, m_field->Exp(d - 1)});
+      CBC_ReedSolomonGF256Poly temp_poly(m_field, {1, m_field->Exp(d - 1)});
       auto nextGenerator = lastGenerator->Multiply(&temp_poly);
       if (!nextGenerator)
         return nullptr;
@@ -72,7 +70,7 @@
   for (size_t x = 0; x < dataBytes; x++)
     infoCoefficients[x] = (*toEncode)[x];
 
-  CBC_ReedSolomonGF256Poly info(m_field.Get(), infoCoefficients);
+  CBC_ReedSolomonGF256Poly info(m_field, infoCoefficients);
   auto infoTemp = info.MultiplyByMonomial(ecBytes, 1);
   if (!infoTemp)
     return false;
diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomon.h b/fxbarcode/common/reedsolomon/BC_ReedSolomon.h
index 772ca3c..fd8954a 100644
--- a/fxbarcode/common/reedsolomon/BC_ReedSolomon.h
+++ b/fxbarcode/common/reedsolomon/BC_ReedSolomon.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.cpp b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.cpp
index f2d86b3..31e6c89 100644
--- a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.cpp
+++ b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -25,7 +25,6 @@
 #include <vector>
 
 #include "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h"
-#include "third_party/base/ptr_util.h"
 
 CBC_ReedSolomonGF256::CBC_ReedSolomonGF256(int32_t primitive) {
   int32_t x = 1;
@@ -43,13 +42,13 @@
 }
 
 void CBC_ReedSolomonGF256::Init() {
-  m_zero = pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(
-      this, std::vector<int32_t>{0});
-  m_one = pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(this,
-                                                       std::vector<int32_t>{1});
+  m_zero =
+      std::make_unique<CBC_ReedSolomonGF256Poly>(this, std::vector<int32_t>{0});
+  m_one =
+      std::make_unique<CBC_ReedSolomonGF256Poly>(this, std::vector<int32_t>{1});
 }
 
-CBC_ReedSolomonGF256::~CBC_ReedSolomonGF256() {}
+CBC_ReedSolomonGF256::~CBC_ReedSolomonGF256() = default;
 
 std::unique_ptr<CBC_ReedSolomonGF256Poly> CBC_ReedSolomonGF256::BuildMonomial(
     int32_t degree,
@@ -62,7 +61,7 @@
 
   std::vector<int32_t> coefficients(degree + 1);
   coefficients[0] = coefficient;
-  return pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(this, coefficients);
+  return std::make_unique<CBC_ReedSolomonGF256Poly>(this, coefficients);
 }
 
 // static
@@ -74,9 +73,9 @@
   return m_expTable[a];
 }
 
-Optional<int32_t> CBC_ReedSolomonGF256::Inverse(int32_t a) {
+absl::optional<int32_t> CBC_ReedSolomonGF256::Inverse(int32_t a) {
   if (a == 0)
-    return {};
+    return absl::nullopt;
   return m_expTable[255 - m_logTable[a]];
 }
 
diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h
index 30f5524..b4b4da8 100644
--- a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h
+++ b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 
 #include <memory>
 
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CBC_ReedSolomonGF256Poly;
 
@@ -25,7 +25,7 @@
                                                           int32_t coefficient);
   static int32_t AddOrSubtract(int32_t a, int32_t b);
   int32_t Exp(int32_t a);
-  Optional<int32_t> Inverse(int32_t a);
+  absl::optional<int32_t> Inverse(int32_t a);
   int32_t Multiply(int32_t a, int32_t b);
   void Init();
 
diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.cpp b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.cpp
index 4339502..449d0a3 100644
--- a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.cpp
+++ b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -26,16 +26,16 @@
 #include <utility>
 
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/stl_util.h"
 #include "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
 
 CBC_ReedSolomonGF256Poly::CBC_ReedSolomonGF256Poly(
     CBC_ReedSolomonGF256* field,
     const std::vector<int32_t>& coefficients)
     : m_field(field) {
-  ASSERT(m_field);
-  ASSERT(!coefficients.empty());
+  DCHECK(m_field);
+  DCHECK(!coefficients.empty());
   if (coefficients.size() == 1 || coefficients.front() != 0) {
     m_coefficients = coefficients;
     return;
@@ -62,7 +62,7 @@
 }
 
 int32_t CBC_ReedSolomonGF256Poly::GetDegree() const {
-  return pdfium::CollectionSize<int32_t>(m_coefficients) - 1;
+  return fxcrt::CollectionSize<int32_t>(m_coefficients) - 1;
 }
 
 bool CBC_ReedSolomonGF256Poly::IsZero() const {
@@ -75,8 +75,7 @@
 
 std::unique_ptr<CBC_ReedSolomonGF256Poly> CBC_ReedSolomonGF256Poly::Clone()
     const {
-  return pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(m_field.Get(),
-                                                      m_coefficients);
+  return std::make_unique<CBC_ReedSolomonGF256Poly>(m_field, m_coefficients);
 }
 
 std::unique_ptr<CBC_ReedSolomonGF256Poly>
@@ -100,7 +99,7 @@
     sumDiff[i] = CBC_ReedSolomonGF256::AddOrSubtract(
         smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
   }
-  return pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(m_field.Get(), sumDiff);
+  return std::make_unique<CBC_ReedSolomonGF256Poly>(m_field, sumDiff);
 }
 
 std::unique_ptr<CBC_ReedSolomonGF256Poly> CBC_ReedSolomonGF256Poly::Multiply(
@@ -120,7 +119,7 @@
           product[i + j], m_field->Multiply(aCoeff, bCoefficients[j]));
     }
   }
-  return pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(m_field.Get(), product);
+  return std::make_unique<CBC_ReedSolomonGF256Poly>(m_field, product);
 }
 
 std::unique_ptr<CBC_ReedSolomonGF256Poly>
@@ -136,7 +135,7 @@
   for (size_t i = 0; i < size; i++)
     product[i] = m_field->Multiply(m_coefficients[i], coefficient);
 
-  return pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(m_field.Get(), product);
+  return std::make_unique<CBC_ReedSolomonGF256Poly>(m_field, product);
 }
 
 std::unique_ptr<CBC_ReedSolomonGF256Poly> CBC_ReedSolomonGF256Poly::Divide(
@@ -152,7 +151,7 @@
     return nullptr;
 
   int32_t denominatorLeadingTerm = other->GetCoefficients(other->GetDegree());
-  Optional<int32_t> inverseDenominatorLeadingTeam =
+  absl::optional<int32_t> inverseDenominatorLeadingTeam =
       m_field->Inverse(denominatorLeadingTerm);
   if (!inverseDenominatorLeadingTeam.has_value())
     return nullptr;
diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h
index 7c3c7fc..e274c76 100644
--- a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h
+++ b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/datamatrix/BC_ASCIIEncoder.cpp b/fxbarcode/datamatrix/BC_ASCIIEncoder.cpp
index ed51f81..333fffe 100644
--- a/fxbarcode/datamatrix/BC_ASCIIEncoder.cpp
+++ b/fxbarcode/datamatrix/BC_ASCIIEncoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -27,15 +27,15 @@
 #include "fxbarcode/datamatrix/BC_EncoderContext.h"
 #include "fxbarcode/datamatrix/BC_HighLevelEncoder.h"
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace {
 
-Optional<wchar_t> EncodeASCIIDigits(wchar_t digit1, wchar_t digit2) {
+absl::optional<wchar_t> EncodeASCIIDigits(wchar_t digit1, wchar_t digit2) {
   if (!FXSYS_IsDecimalDigit(digit1) || !FXSYS_IsDecimalDigit(digit2)) {
     // This could potentially return 0 as a sentinel value. Then this function
-    // can just return wchar_t instead of Optional<wchar_t>.
-    return {};
+    // can just return wchar_t instead of absl::optional<wchar_t>.
+    return absl::nullopt;
   }
   return static_cast<wchar_t>((digit1 - 48) * 10 + (digit2 - 48) + 130);
 }
@@ -67,12 +67,12 @@
 bool CBC_ASCIIEncoder::Encode(CBC_EncoderContext* context) {
   size_t n = DetermineConsecutiveDigitCount(context->m_msg, context->m_pos);
   if (n >= 2) {
-    Optional<wchar_t> code = EncodeASCIIDigits(
+    absl::optional<wchar_t> code = EncodeASCIIDigits(
         context->m_msg[context->m_pos], context->m_msg[context->m_pos + 1]);
-    if (!code)
+    if (!code.has_value())
       return false;
 
-    context->writeCodeword(*code);
+    context->writeCodeword(code.value());
     context->m_pos += 2;
     return true;
   }
diff --git a/fxbarcode/datamatrix/BC_ASCIIEncoder.h b/fxbarcode/datamatrix/BC_ASCIIEncoder.h
index 8b3c2de..59f44b9 100644
--- a/fxbarcode/datamatrix/BC_ASCIIEncoder.h
+++ b/fxbarcode/datamatrix/BC_ASCIIEncoder.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/datamatrix/BC_Base256Encoder.cpp b/fxbarcode/datamatrix/BC_Base256Encoder.cpp
index ea70f68..53eacf3 100644
--- a/fxbarcode/datamatrix/BC_Base256Encoder.cpp
+++ b/fxbarcode/datamatrix/BC_Base256Encoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -73,7 +73,7 @@
   if (!context->UpdateSymbolInfo(currentSize))
     return false;
 
-  bool mustPad = (context->m_symbolInfo->dataCapacity() - currentSize) > 0;
+  bool mustPad = (context->m_symbolInfo->data_capacity() - currentSize) > 0;
   if (context->hasMoreCharacters() || mustPad) {
     if (dataCount <= 249) {
       buffer.SetAt(0, static_cast<wchar_t>(dataCount));
diff --git a/fxbarcode/datamatrix/BC_Base256Encoder.h b/fxbarcode/datamatrix/BC_Base256Encoder.h
index 4897cc5..04929c8 100644
--- a/fxbarcode/datamatrix/BC_Base256Encoder.h
+++ b/fxbarcode/datamatrix/BC_Base256Encoder.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/datamatrix/BC_C40Encoder.cpp b/fxbarcode/datamatrix/BC_C40Encoder.cpp
index 9f20a35..c054cfc 100644
--- a/fxbarcode/datamatrix/BC_C40Encoder.cpp
+++ b/fxbarcode/datamatrix/BC_C40Encoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,12 +22,15 @@
 
 #include "fxbarcode/datamatrix/BC_C40Encoder.h"
 
+#include <iterator>
+
 #include "core/fxcrt/fx_extension.h"
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 #include "fxbarcode/datamatrix/BC_Encoder.h"
 #include "fxbarcode/datamatrix/BC_EncoderContext.h"
 #include "fxbarcode/datamatrix/BC_HighLevelEncoder.h"
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -39,7 +42,7 @@
   wchar_t cw[2];
   cw[0] = static_cast<wchar_t>(v / 256);
   cw[1] = static_cast<wchar_t>(v % 256);
-  return WideString(cw, FX_ArraySize(cw));
+  return WideString(cw, std::size(cw));
 }
 
 }  // namespace
@@ -67,7 +70,7 @@
       return false;
 
     int32_t available =
-        context->m_symbolInfo->dataCapacity() - curCodewordCount;
+        context->m_symbolInfo->data_capacity() - curCodewordCount;
     if (!context->hasMoreCharacters()) {
       if ((buffer.GetLength() % 3) == 2) {
         if (available < 2 || available > 2) {
@@ -112,7 +115,7 @@
   if (!context->UpdateSymbolInfo(curCodewordCount))
     return false;
 
-  int32_t available = context->m_symbolInfo->dataCapacity() - curCodewordCount;
+  int32_t available = context->m_symbolInfo->data_capacity() - curCodewordCount;
   if (rest == 2) {
     *buffer += (wchar_t)'\0';
     while (buffer->GetLength() >= 3)
@@ -149,7 +152,7 @@
     *sb += (wchar_t)(c - 48 + 4);
     return 1;
   }
-  if ((c >= 'A') && (c <= 'Z')) {
+  if (FXSYS_IsUpperASCII(c)) {
     *sb += (wchar_t)(c - 65 + 14);
     return 1;
   }
@@ -190,7 +193,7 @@
 int32_t CBC_C40Encoder::BacktrackOneCharacter(CBC_EncoderContext* context,
                                               WideString* buffer,
                                               int32_t lastCharSize) {
-  ASSERT(lastCharSize >= 0);
+  DCHECK(lastCharSize >= 0);
 
   if (context->m_pos < 1)
     return -1;
diff --git a/fxbarcode/datamatrix/BC_C40Encoder.h b/fxbarcode/datamatrix/BC_C40Encoder.h
index 8bea47b..a0ec9f4 100644
--- a/fxbarcode/datamatrix/BC_C40Encoder.h
+++ b/fxbarcode/datamatrix/BC_C40Encoder.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.cpp b/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.cpp
index 682d095..f22a154 100644
--- a/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.cpp
+++ b/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -21,14 +21,21 @@
  */
 
 #include "fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h"
+
 #include "fxbarcode/datamatrix/BC_Encoder.h"
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
 
+namespace {
+
+constexpr CBC_SymbolInfo::Data kSymbolDatum = {1558, 620, -1, 62, 22, 22, 36};
+
+}  // namespace
+
 CBC_DataMatrixSymbolInfo144::CBC_DataMatrixSymbolInfo144()
-    : CBC_SymbolInfo(1558, 620, 22, 22, 36, -1, 62) {}
+    : CBC_SymbolInfo(&kSymbolDatum) {}
 
-CBC_DataMatrixSymbolInfo144::~CBC_DataMatrixSymbolInfo144() {}
+CBC_DataMatrixSymbolInfo144::~CBC_DataMatrixSymbolInfo144() = default;
 
-size_t CBC_DataMatrixSymbolInfo144::getInterleavedBlockCount() const {
+size_t CBC_DataMatrixSymbolInfo144::GetInterleavedBlockCount() const {
   return 10;
 }
diff --git a/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h b/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h
index 2f44557..4ff532e 100644
--- a/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h
+++ b/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,7 +15,7 @@
   ~CBC_DataMatrixSymbolInfo144() override;
 
   // CBC_SymbolInfo:
-  size_t getInterleavedBlockCount() const override;
+  size_t GetInterleavedBlockCount() const override;
 };
 
 #endif  // FXBARCODE_DATAMATRIX_BC_DATAMATRIXSYMBOLINFO144_H_
diff --git a/fxbarcode/datamatrix/BC_DataMatrixWriter.cpp b/fxbarcode/datamatrix/BC_DataMatrixWriter.cpp
index 1d2b39e..afc15ea 100644
--- a/fxbarcode/datamatrix/BC_DataMatrixWriter.cpp
+++ b/fxbarcode/datamatrix/BC_DataMatrixWriter.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,8 +22,11 @@
 
 #include "fxbarcode/datamatrix/BC_DataMatrixWriter.h"
 
+#include <stdint.h>
+
 #include <memory>
 
+#include "core/fxcrt/data_vector.h"
 #include "fxbarcode/BC_TwoDimWriter.h"
 #include "fxbarcode/BC_Writer.h"
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
@@ -41,28 +44,27 @@
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
 #include "fxbarcode/datamatrix/BC_TextEncoder.h"
 #include "fxbarcode/datamatrix/BC_X12Encoder.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
 
 namespace {
 
-std::unique_ptr<CBC_CommonByteMatrix> encodeLowLevel(
+std::unique_ptr<CBC_CommonByteMatrix> EncodeLowLevel(
     CBC_DefaultPlacement* placement,
     const CBC_SymbolInfo* symbolInfo) {
-  int32_t symbolWidth = symbolInfo->getSymbolDataWidth();
-  ASSERT(symbolWidth);
-  int32_t symbolHeight = symbolInfo->getSymbolDataHeight();
-  ASSERT(symbolHeight);
-  int32_t width = symbolInfo->getSymbolWidth();
-  ASSERT(width);
-  int32_t height = symbolInfo->getSymbolHeight();
-  ASSERT(height);
+  int32_t symbolWidth = symbolInfo->GetSymbolDataWidth();
+  DCHECK(symbolWidth);
+  int32_t symbolHeight = symbolInfo->GetSymbolDataHeight();
+  DCHECK(symbolHeight);
+  int32_t width = symbolInfo->GetSymbolWidth();
+  DCHECK(width);
+  int32_t height = symbolInfo->GetSymbolHeight();
+  DCHECK(height);
 
-  auto matrix = pdfium::MakeUnique<CBC_CommonByteMatrix>(width, height);
+  auto matrix = std::make_unique<CBC_CommonByteMatrix>(width, height);
   int32_t matrixY = 0;
   for (int32_t y = 0; y < symbolHeight; y++) {
     int32_t matrixX;
-    if ((y % symbolInfo->matrixHeight()) == 0) {
+    if ((y % symbolInfo->matrix_height()) == 0) {
       matrixX = 0;
       for (int32_t x = 0; x < width; x++) {
         matrix->Set(matrixX, matrixY, x % 2 == 0);
@@ -72,19 +74,19 @@
     }
     matrixX = 0;
     for (int32_t x = 0; x < symbolWidth; x++) {
-      if (x % symbolInfo->matrixWidth() == 0) {
+      if (x % symbolInfo->matrix_width() == 0) {
         matrix->Set(matrixX, matrixY, true);
         matrixX++;
       }
-      matrix->Set(matrixX, matrixY, placement->getBit(x, y));
+      matrix->Set(matrixX, matrixY, placement->GetBit(x, y));
       matrixX++;
-      if (x % symbolInfo->matrixWidth() == symbolInfo->matrixWidth() - 1) {
+      if (x % symbolInfo->matrix_width() == symbolInfo->matrix_width() - 1) {
         matrix->Set(matrixX, matrixY, y % 2 == 0);
         matrixX++;
       }
     }
     matrixY++;
-    if (y % symbolInfo->matrixHeight() == symbolInfo->matrixHeight() - 1) {
+    if (y % symbolInfo->matrix_height() == symbolInfo->matrix_height() - 1) {
       matrixX = 0;
       for (int32_t x = 0; x < width; x++) {
         matrix->Set(matrixX, matrixY, true);
@@ -107,40 +109,34 @@
   return true;
 }
 
-std::vector<uint8_t> CBC_DataMatrixWriter::Encode(const WideString& contents,
-                                                  int32_t* pOutWidth,
-                                                  int32_t* pOutHeight) {
-  std::vector<uint8_t> results;
+DataVector<uint8_t> CBC_DataMatrixWriter::Encode(const WideString& contents,
+                                                 int32_t* pOutWidth,
+                                                 int32_t* pOutHeight) {
   WideString encoded = CBC_HighLevelEncoder::EncodeHighLevel(contents);
   if (encoded.IsEmpty())
-    return results;
+    return DataVector<uint8_t>();
 
   const CBC_SymbolInfo* pSymbolInfo =
       CBC_SymbolInfo::Lookup(encoded.GetLength(), false);
   if (!pSymbolInfo)
-    return results;
+    return DataVector<uint8_t>();
 
   WideString codewords =
       CBC_ErrorCorrection::EncodeECC200(encoded, pSymbolInfo);
   if (codewords.IsEmpty())
-    return results;
+    return DataVector<uint8_t>();
 
-  int32_t width = pSymbolInfo->getSymbolDataWidth();
-  ASSERT(width);
-  int32_t height = pSymbolInfo->getSymbolDataHeight();
-  ASSERT(height);
+  int32_t width = pSymbolInfo->GetSymbolDataWidth();
+  DCHECK(width);
+  int32_t height = pSymbolInfo->GetSymbolDataHeight();
+  DCHECK(height);
 
   auto placement =
-      pdfium::MakeUnique<CBC_DefaultPlacement>(codewords, width, height);
-  placement->place();
-  auto bytematrix = encodeLowLevel(placement.get(), pSymbolInfo);
-  if (!bytematrix)
-    return results;
+      std::make_unique<CBC_DefaultPlacement>(codewords, width, height);
+  auto bytematrix = EncodeLowLevel(placement.get(), pSymbolInfo);
+  DCHECK(bytematrix);
 
   *pOutWidth = bytematrix->GetWidth();
   *pOutHeight = bytematrix->GetHeight();
-  results = pdfium::Vector2D<uint8_t>(*pOutWidth, *pOutHeight);
-  memcpy(results.data(), bytematrix->GetArray().data(),
-         *pOutWidth * *pOutHeight);
-  return results;
+  return bytematrix->TakeArray();
 }
diff --git a/fxbarcode/datamatrix/BC_DataMatrixWriter.h b/fxbarcode/datamatrix/BC_DataMatrixWriter.h
index f95612e..ba6c59c 100644
--- a/fxbarcode/datamatrix/BC_DataMatrixWriter.h
+++ b/fxbarcode/datamatrix/BC_DataMatrixWriter.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,10 @@
 #ifndef FXBARCODE_DATAMATRIX_BC_DATAMATRIXWRITER_H_
 #define FXBARCODE_DATAMATRIX_BC_DATAMATRIXWRITER_H_
 
-#include <vector>
+#include <stdint.h>
 
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/widestring.h"
 #include "fxbarcode/BC_TwoDimWriter.h"
 
 class CBC_DataMatrixWriter final : public CBC_TwoDimWriter {
@@ -16,9 +18,9 @@
   CBC_DataMatrixWriter();
   ~CBC_DataMatrixWriter() override;
 
-  std::vector<uint8_t> Encode(const WideString& contents,
-                              int32_t* pOutWidth,
-                              int32_t* pOutHeight);
+  DataVector<uint8_t> Encode(const WideString& contents,
+                             int32_t* pOutWidth,
+                             int32_t* pOutHeight);
 
   // CBC_TwoDimWriter
   bool SetErrorCorrectionLevel(int32_t level) override;
diff --git a/fxbarcode/datamatrix/BC_DataMatrixWriter_unittest.cpp b/fxbarcode/datamatrix/BC_DataMatrixWriter_unittest.cpp
index fa5d26c..9613107 100644
--- a/fxbarcode/datamatrix/BC_DataMatrixWriter_unittest.cpp
+++ b/fxbarcode/datamatrix/BC_DataMatrixWriter_unittest.cpp
@@ -1,12 +1,12 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxbarcode/datamatrix/BC_DataMatrixWriter.h"
 
-#include <vector>
+#include <stdint.h>
 
-#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/data_vector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 class CBC_DataMatrixWriterTest : public testing::Test {
@@ -40,11 +40,11 @@
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1
     };
     // clang-format on
-    std::vector<uint8_t> data = writer.Encode(L"", &width, &height);
-    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    DataVector<uint8_t> data = writer.Encode(L"", &width, &height);
+    ASSERT_EQ(std::size(kExpectedData), data.size());
     ASSERT_EQ(kExpectedDimension, width);
     ASSERT_EQ(kExpectedDimension, height);
-    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+    for (size_t i = 0; i < std::size(kExpectedData); ++i)
       EXPECT_EQ(kExpectedData[i], data[i]) << i;
   }
   {
@@ -67,11 +67,11 @@
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
     };
     // clang-format on
-    std::vector<uint8_t> data = writer.Encode(L"helloworld", &width, &height);
-    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    DataVector<uint8_t> data = writer.Encode(L"helloworld", &width, &height);
+    ASSERT_EQ(std::size(kExpectedData), data.size());
     ASSERT_EQ(kExpectedDimension, width);
     ASSERT_EQ(kExpectedDimension, height);
-    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+    for (size_t i = 0; i < std::size(kExpectedData); ++i)
       EXPECT_EQ(kExpectedData[i], data[i]) << i;
   }
   {
@@ -90,11 +90,11 @@
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1
     };
     // clang-format on
-    std::vector<uint8_t> data = writer.Encode(L"12345", &width, &height);
-    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    DataVector<uint8_t> data = writer.Encode(L"12345", &width, &height);
+    ASSERT_EQ(std::size(kExpectedData), data.size());
     ASSERT_EQ(kExpectedDimension, width);
     ASSERT_EQ(kExpectedDimension, height);
-    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+    for (size_t i = 0; i < std::size(kExpectedData); ++i)
       EXPECT_EQ(kExpectedData[i], data[i]) << i;
   }
   {
@@ -121,16 +121,16 @@
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
     };
     // clang-format on
-    std::vector<uint8_t> data =
+    DataVector<uint8_t> data =
         writer.Encode(L"abcdefghijklmnopqrst", &width, &height);
-    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(std::size(kExpectedData), data.size());
     ASSERT_EQ(kExpectedDimension, width);
     ASSERT_EQ(kExpectedDimension, height);
-    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+    for (size_t i = 0; i < std::size(kExpectedData); ++i)
       EXPECT_EQ(kExpectedData[i], data[i]) << i;
   }
   {
-    std::vector<uint8_t> data = writer.Encode(L"hello world", &width, &height);
+    DataVector<uint8_t> data = writer.Encode(L"hello world", &width, &height);
     ASSERT_TRUE(data.empty());
   }
 }
@@ -147,7 +147,7 @@
 
   {
     static constexpr int kExpectedDimension = 144;
-    std::vector<uint8_t> data = writer.Encode(input.c_str(), &width, &height);
+    DataVector<uint8_t> data = writer.Encode(input.c_str(), &width, &height);
     EXPECT_EQ(20736u, data.size());
     EXPECT_EQ(kExpectedDimension, width);
     EXPECT_EQ(kExpectedDimension, height);
@@ -158,7 +158,7 @@
   {
     width = -1;
     height = -1;
-    std::vector<uint8_t> data = writer.Encode(input.c_str(), &width, &height);
+    DataVector<uint8_t> data = writer.Encode(input.c_str(), &width, &height);
     EXPECT_EQ(0u, data.size());
     EXPECT_EQ(-1, width);
     EXPECT_EQ(-1, height);
@@ -177,7 +177,7 @@
 
   {
     static constexpr int kExpectedDimension = 144;
-    std::vector<uint8_t> data = writer.Encode(input.c_str(), &width, &height);
+    DataVector<uint8_t> data = writer.Encode(input.c_str(), &width, &height);
     EXPECT_EQ(20736u, data.size());
     EXPECT_EQ(kExpectedDimension, width);
     EXPECT_EQ(kExpectedDimension, height);
@@ -188,7 +188,7 @@
   {
     width = -1;
     height = -1;
-    std::vector<uint8_t> data = writer.Encode(input.c_str(), &width, &height);
+    DataVector<uint8_t> data = writer.Encode(input.c_str(), &width, &height);
     EXPECT_EQ(0u, data.size());
     EXPECT_EQ(-1, width);
     EXPECT_EQ(-1, height);
diff --git a/fxbarcode/datamatrix/BC_DefaultPlacement.cpp b/fxbarcode/datamatrix/BC_DefaultPlacement.cpp
index 24d81f1..4e772dd 100644
--- a/fxbarcode/datamatrix/BC_DefaultPlacement.cpp
+++ b/fxbarcode/datamatrix/BC_DefaultPlacement.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,62 +22,81 @@
 
 #include "fxbarcode/datamatrix/BC_DefaultPlacement.h"
 
+#include <stdint.h>
+
 #include <utility>
 
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_2d_size.h"
 #include "fxbarcode/datamatrix/BC_Encoder.h"
+#include "third_party/base/check_op.h"
+
+namespace {
+
+size_t GetIndex(size_t col, size_t row, size_t num_cols) {
+  return row * num_cols + col;
+}
+
+}  // namespace
 
 CBC_DefaultPlacement::CBC_DefaultPlacement(WideString codewords,
                                            int32_t numcols,
                                            int32_t numrows)
     : m_codewords(std::move(codewords)),
       m_numrows(numrows),
-      m_numcols(numcols) {
-  m_bits.resize(numcols * numrows);
-  for (int32_t i = 0; i < numcols * numrows; i++) {
-    m_bits[i] = (uint8_t)2;
-  }
+      m_numcols(numcols),
+      m_bits(Fx2DSizeOrDie(numcols, numrows), 2) {
+  CHECK_GT(m_numrows, 0);
+  CHECK_GT(m_numcols, 0);
+  Init();
 }
 
-CBC_DefaultPlacement::~CBC_DefaultPlacement() {}
+CBC_DefaultPlacement::~CBC_DefaultPlacement() = default;
 
-int32_t CBC_DefaultPlacement::getNumrows() {
-  return m_numrows;
+bool CBC_DefaultPlacement::GetBit(int32_t col, int32_t row) const {
+  CHECK_GE(col, 0);
+  CHECK_GE(row, 0);
+  CHECK_LT(col, m_numcols);
+  CHECK_LT(row, m_numrows);
+  return m_bits[GetIndex(col, row, m_numcols)] == 1;
 }
-int32_t CBC_DefaultPlacement::getNumcols() {
-  return m_numcols;
+
+void CBC_DefaultPlacement::SetBit(int32_t col, int32_t row, bool bit) {
+  DCHECK_GE(col, 0);
+  DCHECK_GE(row, 0);
+  DCHECK_LT(col, m_numcols);
+  DCHECK_LT(row, m_numrows);
+  m_bits[GetIndex(col, row, m_numcols)] = bit ? 1 : 0;
 }
-std::vector<uint8_t>& CBC_DefaultPlacement::getBits() {
-  return m_bits;
+
+bool CBC_DefaultPlacement::HasBit(int32_t col, int32_t row) const {
+  DCHECK_GE(col, 0);
+  DCHECK_GE(row, 0);
+  DCHECK_LT(col, m_numcols);
+  DCHECK_LT(row, m_numrows);
+  return m_bits[GetIndex(col, row, m_numcols)] != 2;
 }
-bool CBC_DefaultPlacement::getBit(int32_t col, int32_t row) {
-  return m_bits[row * m_numcols + col] == 1;
-}
-void CBC_DefaultPlacement::setBit(int32_t col, int32_t row, bool bit) {
-  m_bits[row * m_numcols + col] = bit ? (uint8_t)1 : (uint8_t)0;
-}
-bool CBC_DefaultPlacement::hasBit(int32_t col, int32_t row) {
-  return m_bits[row * m_numcols + col] != 2;
-}
-void CBC_DefaultPlacement::place() {
+
+void CBC_DefaultPlacement::Init() {
   int32_t pos = 0;
   int32_t row = 4;
   int32_t col = 0;
   do {
     if ((row == m_numrows) && (col == 0)) {
-      corner1(pos++);
+      SetCorner1(pos++);
     }
     if ((row == m_numrows - 2) && (col == 0) && ((m_numcols % 4) != 0)) {
-      corner2(pos++);
+      SetCorner2(pos++);
     }
     if ((row == m_numrows - 2) && (col == 0) && (m_numcols % 8 == 4)) {
-      corner3(pos++);
+      SetCorner3(pos++);
     }
     if ((row == m_numrows + 4) && (col == 2) && ((m_numcols % 8) == 0)) {
-      corner4(pos++);
+      SetCorner4(pos++);
     }
     do {
-      if ((row < m_numrows) && (col >= 0) && !hasBit(col, row)) {
-        utah(row, col, pos++);
+      if ((row < m_numrows) && (col >= 0) && !HasBit(col, row)) {
+        SetUtah(row, col, pos++);
       }
       row -= 2;
       col += 2;
@@ -85,8 +104,8 @@
     row++;
     col += 3;
     do {
-      if ((row >= 0) && (col < m_numcols) && !hasBit(col, row)) {
-        utah(row, col, pos++);
+      if ((row >= 0) && (col < m_numcols) && !HasBit(col, row)) {
+        SetUtah(row, col, pos++);
       }
       row += 2;
       col -= 2;
@@ -94,15 +113,16 @@
     row += 3;
     col++;
   } while ((row < m_numrows) || (col < m_numcols));
-  if (!hasBit(m_numcols - 1, m_numrows - 1)) {
-    setBit(m_numcols - 1, m_numrows - 1, true);
-    setBit(m_numcols - 2, m_numrows - 2, true);
+  if (!HasBit(m_numcols - 1, m_numrows - 1)) {
+    SetBit(m_numcols - 1, m_numrows - 1, true);
+    SetBit(m_numcols - 2, m_numrows - 2, true);
   }
 }
-void CBC_DefaultPlacement::module(int32_t row,
-                                  int32_t col,
-                                  int32_t pos,
-                                  int32_t bit) {
+
+void CBC_DefaultPlacement::SetModule(int32_t row,
+                                     int32_t col,
+                                     int32_t pos,
+                                     int32_t bit) {
   if (row < 0) {
     row += m_numrows;
     col += 4 - ((m_numrows + 4) % 8);
@@ -113,55 +133,60 @@
   }
   int32_t v = m_codewords[pos];
   v &= 1 << (8 - bit);
-  setBit(col, row, v != 0);
+  SetBit(col, row, v != 0);
 }
-void CBC_DefaultPlacement::utah(int32_t row, int32_t col, int32_t pos) {
-  module(row - 2, col - 2, pos, 1);
-  module(row - 2, col - 1, pos, 2);
-  module(row - 1, col - 2, pos, 3);
-  module(row - 1, col - 1, pos, 4);
-  module(row - 1, col, pos, 5);
-  module(row, col - 2, pos, 6);
-  module(row, col - 1, pos, 7);
-  module(row, col, pos, 8);
+
+void CBC_DefaultPlacement::SetUtah(int32_t row, int32_t col, int32_t pos) {
+  SetModule(row - 2, col - 2, pos, 1);
+  SetModule(row - 2, col - 1, pos, 2);
+  SetModule(row - 1, col - 2, pos, 3);
+  SetModule(row - 1, col - 1, pos, 4);
+  SetModule(row - 1, col, pos, 5);
+  SetModule(row, col - 2, pos, 6);
+  SetModule(row, col - 1, pos, 7);
+  SetModule(row, col, pos, 8);
 }
-void CBC_DefaultPlacement::corner1(int32_t pos) {
-  module(m_numrows - 1, 0, pos, 1);
-  module(m_numrows - 1, 1, pos, 2);
-  module(m_numrows - 1, 2, pos, 3);
-  module(0, m_numcols - 2, pos, 4);
-  module(0, m_numcols - 1, pos, 5);
-  module(1, m_numcols - 1, pos, 6);
-  module(2, m_numcols - 1, pos, 7);
-  module(3, m_numcols - 1, pos, 8);
+
+void CBC_DefaultPlacement::SetCorner1(int32_t pos) {
+  SetModule(m_numrows - 1, 0, pos, 1);
+  SetModule(m_numrows - 1, 1, pos, 2);
+  SetModule(m_numrows - 1, 2, pos, 3);
+  SetModule(0, m_numcols - 2, pos, 4);
+  SetModule(0, m_numcols - 1, pos, 5);
+  SetModule(1, m_numcols - 1, pos, 6);
+  SetModule(2, m_numcols - 1, pos, 7);
+  SetModule(3, m_numcols - 1, pos, 8);
 }
-void CBC_DefaultPlacement::corner2(int32_t pos) {
-  module(m_numrows - 3, 0, pos, 1);
-  module(m_numrows - 2, 0, pos, 2);
-  module(m_numrows - 1, 0, pos, 3);
-  module(0, m_numcols - 4, pos, 4);
-  module(0, m_numcols - 3, pos, 5);
-  module(0, m_numcols - 2, pos, 6);
-  module(0, m_numcols - 1, pos, 7);
-  module(1, m_numcols - 1, pos, 8);
+
+void CBC_DefaultPlacement::SetCorner2(int32_t pos) {
+  SetModule(m_numrows - 3, 0, pos, 1);
+  SetModule(m_numrows - 2, 0, pos, 2);
+  SetModule(m_numrows - 1, 0, pos, 3);
+  SetModule(0, m_numcols - 4, pos, 4);
+  SetModule(0, m_numcols - 3, pos, 5);
+  SetModule(0, m_numcols - 2, pos, 6);
+  SetModule(0, m_numcols - 1, pos, 7);
+  SetModule(1, m_numcols - 1, pos, 8);
 }
-void CBC_DefaultPlacement::corner3(int32_t pos) {
-  module(m_numrows - 3, 0, pos, 1);
-  module(m_numrows - 2, 0, pos, 2);
-  module(m_numrows - 1, 0, pos, 3);
-  module(0, m_numcols - 2, pos, 4);
-  module(0, m_numcols - 1, pos, 5);
-  module(1, m_numcols - 1, pos, 6);
-  module(2, m_numcols - 1, pos, 7);
-  module(3, m_numcols - 1, pos, 8);
+
+void CBC_DefaultPlacement::SetCorner3(int32_t pos) {
+  SetModule(m_numrows - 3, 0, pos, 1);
+  SetModule(m_numrows - 2, 0, pos, 2);
+  SetModule(m_numrows - 1, 0, pos, 3);
+  SetModule(0, m_numcols - 2, pos, 4);
+  SetModule(0, m_numcols - 1, pos, 5);
+  SetModule(1, m_numcols - 1, pos, 6);
+  SetModule(2, m_numcols - 1, pos, 7);
+  SetModule(3, m_numcols - 1, pos, 8);
 }
-void CBC_DefaultPlacement::corner4(int32_t pos) {
-  module(m_numrows - 1, 0, pos, 1);
-  module(m_numrows - 1, m_numcols - 1, pos, 2);
-  module(0, m_numcols - 3, pos, 3);
-  module(0, m_numcols - 2, pos, 4);
-  module(0, m_numcols - 1, pos, 5);
-  module(1, m_numcols - 3, pos, 6);
-  module(1, m_numcols - 2, pos, 7);
-  module(1, m_numcols - 1, pos, 8);
+
+void CBC_DefaultPlacement::SetCorner4(int32_t pos) {
+  SetModule(m_numrows - 1, 0, pos, 1);
+  SetModule(m_numrows - 1, m_numcols - 1, pos, 2);
+  SetModule(0, m_numcols - 3, pos, 3);
+  SetModule(0, m_numcols - 2, pos, 4);
+  SetModule(0, m_numcols - 1, pos, 5);
+  SetModule(1, m_numcols - 3, pos, 6);
+  SetModule(1, m_numcols - 2, pos, 7);
+  SetModule(1, m_numcols - 1, pos, 8);
 }
diff --git a/fxbarcode/datamatrix/BC_DefaultPlacement.h b/fxbarcode/datamatrix/BC_DefaultPlacement.h
index 823d587..7a5b201 100644
--- a/fxbarcode/datamatrix/BC_DefaultPlacement.h
+++ b/fxbarcode/datamatrix/BC_DefaultPlacement.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,34 +7,34 @@
 #ifndef FXBARCODE_DATAMATRIX_BC_DEFAULTPLACEMENT_H_
 #define FXBARCODE_DATAMATRIX_BC_DEFAULTPLACEMENT_H_
 
-#include <vector>
+#include <stdint.h>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/widestring.h"
 
 class CBC_DefaultPlacement final {
  public:
   CBC_DefaultPlacement(WideString codewords, int32_t numcols, int32_t numrows);
   ~CBC_DefaultPlacement();
 
-  int32_t getNumrows();
-  int32_t getNumcols();
-  std::vector<uint8_t>& getBits();
-  bool getBit(int32_t col, int32_t row);
-  void setBit(int32_t col, int32_t row, bool bit);
-  bool hasBit(int32_t col, int32_t row);
-  void place();
+  bool GetBit(int32_t col, int32_t row) const;
 
  private:
-  WideString m_codewords;
-  int32_t m_numrows;
-  int32_t m_numcols;
-  std::vector<uint8_t> m_bits;
-  void module(int32_t row, int32_t col, int32_t pos, int32_t bit);
-  void utah(int32_t row, int32_t col, int32_t pos);
-  void corner1(int32_t pos);
-  void corner2(int32_t pos);
-  void corner3(int32_t pos);
-  void corner4(int32_t pos);
+  void Init();
+  void SetModule(int32_t row, int32_t col, int32_t pos, int32_t bit);
+  void SetUtah(int32_t row, int32_t col, int32_t pos);
+  void SetCorner1(int32_t pos);
+  void SetCorner2(int32_t pos);
+  void SetCorner3(int32_t pos);
+  void SetCorner4(int32_t pos);
+
+  void SetBit(int32_t col, int32_t row, bool bit);
+  bool HasBit(int32_t col, int32_t row) const;
+
+  const WideString m_codewords;
+  const int32_t m_numrows;
+  const int32_t m_numcols;
+  DataVector<uint8_t> m_bits;
 };
 
 #endif  // FXBARCODE_DATAMATRIX_BC_DEFAULTPLACEMENT_H_
diff --git a/fxbarcode/datamatrix/BC_EdifactEncoder.cpp b/fxbarcode/datamatrix/BC_EdifactEncoder.cpp
index bd86a95..1b98f67 100644
--- a/fxbarcode/datamatrix/BC_EdifactEncoder.cpp
+++ b/fxbarcode/datamatrix/BC_EdifactEncoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -32,17 +32,17 @@
 
 namespace {
 
-WideString EncodeToEdifactCodewords(const WideString& sb, int32_t startPos) {
-  int32_t len = sb.GetLength() - startPos;
+WideString EncodeToEdifactCodewords(const WideString& sb) {
+  size_t len = sb.GetLength();
   if (len == 0)
     return WideString();
 
-  wchar_t c1 = sb[startPos];
-  wchar_t c2 = len >= 2 ? sb[startPos + 1] : 0;
-  wchar_t c3 = len >= 3 ? sb[startPos + 2] : 0;
-  wchar_t c4 = len >= 4 ? sb[startPos + 3] : 0;
+  wchar_t c1 = sb[0];
+  wchar_t c2 = len >= 2 ? sb[1] : 0;
+  wchar_t c3 = len >= 3 ? sb[2] : 0;
+  wchar_t c4 = len >= 4 ? sb[3] : 0;
   int32_t v = (c1 << 18) + (c2 << 12) + (c3 << 6) + c4;
-  constexpr int32_t kBuflen = 3;
+  constexpr size_t kBuflen = 3;
   wchar_t cw[kBuflen];
   cw[0] = static_cast<wchar_t>((v >> 16) & 255);
   cw[1] = static_cast<wchar_t>((v >> 8) & 255);
@@ -62,14 +62,14 @@
       return false;
 
     int32_t available =
-        context->m_symbolInfo->dataCapacity() - context->getCodewordCount();
+        context->m_symbolInfo->data_capacity() - context->getCodewordCount();
     int32_t remaining = context->getRemainingCharacters();
     if (remaining == 0 && available <= 2)
       return true;
   }
 
   int32_t restChars = count - 1;
-  WideString encoded = EncodeToEdifactCodewords(buffer, 0);
+  WideString encoded = EncodeToEdifactCodewords(buffer);
   if (encoded.IsEmpty())
     return false;
 
@@ -80,7 +80,7 @@
       return false;
 
     int32_t available =
-        context->m_symbolInfo->dataCapacity() - context->getCodewordCount();
+        context->m_symbolInfo->data_capacity() - context->getCodewordCount();
     if (available >= 3) {
       restInAscii = false;
       if (!context->UpdateSymbolInfo(context->getCodewordCount() +
@@ -134,7 +134,7 @@
     context->m_pos++;
     size_t count = buffer.GetLength();
     if (count >= 4) {
-      WideString encoded = EncodeToEdifactCodewords(buffer, 0);
+      WideString encoded = EncodeToEdifactCodewords(buffer);
       if (encoded.IsEmpty())
         return false;
 
diff --git a/fxbarcode/datamatrix/BC_EdifactEncoder.h b/fxbarcode/datamatrix/BC_EdifactEncoder.h
index 0bd8e57..1fb88f0 100644
--- a/fxbarcode/datamatrix/BC_EdifactEncoder.h
+++ b/fxbarcode/datamatrix/BC_EdifactEncoder.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/datamatrix/BC_Encoder.cpp b/fxbarcode/datamatrix/BC_Encoder.cpp
index 9403904..c1e7097 100644
--- a/fxbarcode/datamatrix/BC_Encoder.cpp
+++ b/fxbarcode/datamatrix/BC_Encoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,5 +6,6 @@
 
 #include "fxbarcode/datamatrix/BC_Encoder.h"
 
-CBC_Encoder::CBC_Encoder() {}
-CBC_Encoder::~CBC_Encoder() {}
+CBC_Encoder::CBC_Encoder() = default;
+
+CBC_Encoder::~CBC_Encoder() = default;
diff --git a/fxbarcode/datamatrix/BC_Encoder.h b/fxbarcode/datamatrix/BC_Encoder.h
index 0ea22ab..ecba977 100644
--- a/fxbarcode/datamatrix/BC_Encoder.h
+++ b/fxbarcode/datamatrix/BC_Encoder.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/datamatrix/BC_EncoderContext.cpp b/fxbarcode/datamatrix/BC_EncoderContext.cpp
index 3fbfee8..d59d126 100644
--- a/fxbarcode/datamatrix/BC_EncoderContext.cpp
+++ b/fxbarcode/datamatrix/BC_EncoderContext.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -24,6 +24,7 @@
 
 #include <utility>
 
+#include "core/fxcrt/fx_string.h"
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 #include "fxbarcode/datamatrix/BC_Encoder.h"
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
@@ -90,7 +91,7 @@
 }
 
 bool CBC_EncoderContext::UpdateSymbolInfo(size_t len) {
-  if (!m_symbolInfo || len > m_symbolInfo->dataCapacity()) {
+  if (!m_symbolInfo || len > m_symbolInfo->data_capacity()) {
     m_symbolInfo = CBC_SymbolInfo::Lookup(len, m_bAllowRectangular);
     if (!m_symbolInfo)
       return false;
diff --git a/fxbarcode/datamatrix/BC_EncoderContext.h b/fxbarcode/datamatrix/BC_EncoderContext.h
index e09b63a..329ebb2 100644
--- a/fxbarcode/datamatrix/BC_EncoderContext.h
+++ b/fxbarcode/datamatrix/BC_EncoderContext.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/datamatrix/BC_ErrorCorrection.cpp b/fxbarcode/datamatrix/BC_ErrorCorrection.cpp
index 9c6ff7d..135c39e 100644
--- a/fxbarcode/datamatrix/BC_ErrorCorrection.cpp
+++ b/fxbarcode/datamatrix/BC_ErrorCorrection.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,11 +22,15 @@
 
 #include "fxbarcode/datamatrix/BC_ErrorCorrection.h"
 
+#include <stdint.h>
+
 #include <algorithm>
 #include <vector>
 
+#include "core/fxcrt/fixed_zeroed_data_vector.h"
 #include "fxbarcode/datamatrix/BC_Encoder.h"
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -141,10 +145,10 @@
     0};
 
 WideString CreateECCBlock(const WideString& codewords, size_t numECWords) {
-  ASSERT(numECWords > 0);
+  DCHECK(numECWords > 0);
 
   const size_t len = codewords.GetLength();
-  static constexpr size_t kFactorTableNum = FX_ArraySize(FACTOR_SETS);
+  static constexpr size_t kFactorTableNum = std::size(FACTOR_SETS);
   size_t table = 0;
   while (table < kFactorTableNum && FACTOR_SETS[table] != numECWords)
     ++table;
@@ -152,30 +156,31 @@
   if (table >= kFactorTableNum)
     return WideString();
 
-  std::vector<uint16_t> ecc(numECWords);
+  FixedZeroedDataVector<uint16_t> ecc(numECWords);
+  pdfium::span<uint16_t> ecc_span = ecc.writable_span();
   for (size_t i = 0; i < len; ++i) {
-    uint16_t m = ecc[numECWords - 1] ^ codewords[i];
+    uint16_t m = ecc_span[numECWords - 1] ^ codewords[i];
     for (int32_t j = numECWords - 1; j > 0; --j) {
       if (m != 0 && FACTORS[table][j] != 0) {
-        ecc[j] = static_cast<uint16_t>(
-            ecc[j - 1] ^ ALOG[(LOG[m] + LOG[FACTORS[table][j]]) % 255]);
+        ecc_span[j] = static_cast<uint16_t>(
+            ecc_span[j - 1] ^ ALOG[(LOG[m] + LOG[FACTORS[table][j]]) % 255]);
       } else {
-        ecc[j] = ecc[j - 1];
+        ecc_span[j] = ecc_span[j - 1];
       }
     }
     if (m != 0 && FACTORS[table][0] != 0) {
-      ecc[0] =
+      ecc_span[0] =
           static_cast<uint16_t>(ALOG[(LOG[m] + LOG[FACTORS[table][0]]) % 255]);
     } else {
-      ecc[0] = 0;
+      ecc_span[0] = 0;
     }
   }
   WideString strecc;
   strecc.Reserve(numECWords);
   for (size_t i = 0; i < numECWords; ++i)
-    strecc.InsertAtBack(static_cast<wchar_t>(ecc[numECWords - i - 1]));
+    strecc.InsertAtBack(static_cast<wchar_t>(ecc_span[numECWords - i - 1]));
 
-  ASSERT(!strecc.IsEmpty());
+  DCHECK(!strecc.IsEmpty());
   return strecc;
 }
 
@@ -183,13 +188,13 @@
 
 WideString CBC_ErrorCorrection::EncodeECC200(const WideString& codewords,
                                              const CBC_SymbolInfo* symbolInfo) {
-  if (codewords.GetLength() != symbolInfo->dataCapacity())
+  if (codewords.GetLength() != symbolInfo->data_capacity())
     return WideString();
 
   WideString sb = codewords;
-  size_t blockCount = symbolInfo->getInterleavedBlockCount();
+  size_t blockCount = symbolInfo->GetInterleavedBlockCount();
   if (blockCount == 1) {
-    WideString ecc = CreateECCBlock(codewords, symbolInfo->errorCodewords());
+    WideString ecc = CreateECCBlock(codewords, symbolInfo->error_codewords());
     if (ecc.IsEmpty())
       return WideString();
     sb += ecc;
@@ -198,8 +203,8 @@
     std::vector<size_t> errorSizes(blockCount);
     std::vector<size_t> startPos(blockCount);
     for (size_t i = 0; i < blockCount; ++i) {
-      dataSizes[i] = symbolInfo->getDataLengthForInterleavedBlock();
-      errorSizes[i] = symbolInfo->getErrorLengthForInterleavedBlock();
+      dataSizes[i] = symbolInfo->GetDataLengthForInterleavedBlock();
+      errorSizes[i] = symbolInfo->GetErrorLengthForInterleavedBlock();
       startPos[i] = i > 0 ? startPos[i - 1] + dataSizes[i] : 0;
     }
 
@@ -211,9 +216,9 @@
 
     for (size_t block = 0; block < blockCount; ++block) {
       WideString temp;
-      if (symbolInfo->dataCapacity() > block)
-        temp.Reserve((symbolInfo->dataCapacity() - block / blockCount) + 1);
-      for (size_t d = block; d < symbolInfo->dataCapacity(); d += blockCount)
+      if (symbolInfo->data_capacity() > block)
+        temp.Reserve((symbolInfo->data_capacity() - block / blockCount) + 1);
+      for (size_t d = block; d < symbolInfo->data_capacity(); d += blockCount)
         temp.InsertAtBack(static_cast<wchar_t>(codewords[d]));
 
       WideString ecc = CreateECCBlock(temp, errorSizes[block]);
@@ -222,10 +227,10 @@
 
       for (size_t pos = 0, i = block; i < errorSizes[block] * blockCount;
            ++pos, i += blockCount) {
-        sb.SetAt(symbolInfo->dataCapacity() + i, ecc[pos]);
+        sb.SetAt(symbolInfo->data_capacity() + i, ecc[pos]);
       }
     }
   }
-  ASSERT(!sb.IsEmpty());
+  DCHECK(!sb.IsEmpty());
   return sb;
 }
diff --git a/fxbarcode/datamatrix/BC_ErrorCorrection.h b/fxbarcode/datamatrix/BC_ErrorCorrection.h
index 676093f..53ffc68 100644
--- a/fxbarcode/datamatrix/BC_ErrorCorrection.h
+++ b/fxbarcode/datamatrix/BC_ErrorCorrection.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/datamatrix/BC_HighLevelEncoder.cpp b/fxbarcode/datamatrix/BC_HighLevelEncoder.cpp
index fbc4262..ee52de9 100644
--- a/fxbarcode/datamatrix/BC_HighLevelEncoder.cpp
+++ b/fxbarcode/datamatrix/BC_HighLevelEncoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -39,7 +39,7 @@
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
 #include "fxbarcode/datamatrix/BC_TextEncoder.h"
 #include "fxbarcode/datamatrix/BC_X12Encoder.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -91,11 +91,11 @@
 }
 
 bool IsNativeC40(wchar_t ch) {
-  return (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z');
+  return (ch == ' ') || (ch >= '0' && ch <= '9') || FXSYS_IsUpperASCII(ch);
 }
 
 bool IsNativeText(wchar_t ch) {
-  return (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z');
+  return (ch == ' ') || (ch >= '0' && ch <= '9') || FXSYS_IsLowerASCII(ch);
 }
 
 bool IsX12TermSep(wchar_t ch) {
@@ -104,7 +104,7 @@
 
 bool IsNativeX12(wchar_t ch) {
   return IsX12TermSep(ch) || (ch == ' ') || (ch >= '0' && ch <= '9') ||
-         (ch >= 'A' && ch <= 'Z');
+         FXSYS_IsUpperASCII(ch);
 }
 
 bool IsNativeEDIFACT(wchar_t ch) {
@@ -112,7 +112,7 @@
 }
 
 size_t EncoderIndex(CBC_HighLevelEncoder::Encoding encoding) {
-  ASSERT(encoding != CBC_HighLevelEncoder::Encoding::UNKNOWN);
+  DCHECK(encoding != CBC_HighLevelEncoder::Encoding::UNKNOWN);
   return static_cast<size_t>(encoding);
 }
 
@@ -145,12 +145,12 @@
   }
 
   std::vector<std::unique_ptr<CBC_Encoder>> encoders;
-  encoders.push_back(pdfium::MakeUnique<CBC_ASCIIEncoder>());
-  encoders.push_back(pdfium::MakeUnique<CBC_C40Encoder>());
-  encoders.push_back(pdfium::MakeUnique<CBC_TextEncoder>());
-  encoders.push_back(pdfium::MakeUnique<CBC_X12Encoder>());
-  encoders.push_back(pdfium::MakeUnique<CBC_EdifactEncoder>());
-  encoders.push_back(pdfium::MakeUnique<CBC_Base256Encoder>());
+  encoders.push_back(std::make_unique<CBC_ASCIIEncoder>());
+  encoders.push_back(std::make_unique<CBC_C40Encoder>());
+  encoders.push_back(std::make_unique<CBC_TextEncoder>());
+  encoders.push_back(std::make_unique<CBC_X12Encoder>());
+  encoders.push_back(std::make_unique<CBC_EdifactEncoder>());
+  encoders.push_back(std::make_unique<CBC_Base256Encoder>());
   Encoding encodingMode = Encoding::ASCII;
   while (context.hasMoreCharacters()) {
     if (!encoders[EncoderIndex(encodingMode)]->Encode(&context))
@@ -165,7 +165,7 @@
   if (!context.UpdateSymbolInfo())
     return WideString();
 
-  size_t capacity = context.m_symbolInfo->dataCapacity();
+  size_t capacity = context.m_symbolInfo->data_capacity();
   if (len < capacity) {
     if (encodingMode != Encoding::ASCII && encodingMode != Encoding::BASE256)
       context.writeCodeword(0x00fe);
@@ -177,7 +177,7 @@
   while (codewords.GetLength() < capacity)
     codewords += Randomize253State(kPad, codewords.GetLength() + 1);
 
-  ASSERT(!codewords.IsEmpty());
+  DCHECK(!codewords.IsEmpty());
   return codewords;
 }
 
diff --git a/fxbarcode/datamatrix/BC_HighLevelEncoder.h b/fxbarcode/datamatrix/BC_HighLevelEncoder.h
index 14f64e2..97f61cf 100644
--- a/fxbarcode/datamatrix/BC_HighLevelEncoder.h
+++ b/fxbarcode/datamatrix/BC_HighLevelEncoder.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,6 @@
 #ifndef FXBARCODE_DATAMATRIX_BC_HIGHLEVELENCODER_H_
 #define FXBARCODE_DATAMATRIX_BC_HIGHLEVELENCODER_H_
 
-#include <vector>
-
 #include "core/fxcrt/widestring.h"
 
 class CBC_HighLevelEncoder {
diff --git a/fxbarcode/datamatrix/BC_SymbolInfo.cpp b/fxbarcode/datamatrix/BC_SymbolInfo.cpp
index d80afc2..59d40d0 100644
--- a/fxbarcode/datamatrix/BC_SymbolInfo.cpp
+++ b/fxbarcode/datamatrix/BC_SymbolInfo.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,9 +22,12 @@
 
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
 
+#include <iterator>
+
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 #include "fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h"
 #include "fxbarcode/datamatrix/BC_Encoder.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
@@ -36,94 +39,62 @@
     nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
 
+constexpr CBC_SymbolInfo::Data kSymbolData[] = {
+    {3, 5, 3, 5, 8, 8, 1},           {5, 7, 5, 7, 10, 10, 1},
+    {5, 7, 5, 7, 16, 6, 1},          {8, 10, 8, 10, 12, 12, 1},
+    {10, 11, 10, 11, 14, 6, 2},      {12, 12, 12, 12, 14, 14, 1},
+    {16, 14, 16, 14, 24, 10, 1},     {18, 14, 18, 14, 16, 16, 1},
+    {22, 18, 22, 18, 18, 18, 1},     {22, 18, 22, 18, 16, 10, 2},
+    {30, 20, 30, 20, 20, 20, 1},     {32, 24, 32, 24, 16, 14, 2},
+    {36, 24, 36, 24, 22, 22, 1},     {44, 28, 44, 28, 24, 24, 1},
+    {49, 28, 49, 28, 22, 14, 2},     {62, 36, 62, 36, 14, 14, 4},
+    {86, 42, 86, 42, 16, 16, 4},     {114, 48, 114, 48, 18, 18, 4},
+    {144, 56, 144, 56, 20, 20, 4},   {174, 68, 174, 68, 22, 22, 4},
+    {204, 84, 102, 42, 24, 24, 4},   {280, 112, 140, 56, 14, 14, 16},
+    {368, 144, 92, 36, 16, 16, 16},  {456, 192, 114, 48, 18, 18, 16},
+    {576, 224, 144, 56, 20, 20, 16}, {696, 272, 174, 68, 22, 22, 16},
+    {816, 336, 136, 56, 24, 24, 16}, {1050, 408, 175, 68, 18, 18, 36},
+    {1304, 496, 163, 62, 20, 20, 36}};
+
+constexpr size_t kSymbolDataSize = std::size(kSymbolData);
+static_assert(kSymbolDataSize + 1 == kSymbolsCount, "Wrong kSymbolDataSize");
+
 }  // namespace
 
+// static
 void CBC_SymbolInfo::Initialize() {
-  g_symbols[0] = new CBC_SymbolInfo(3, 5, 8, 8, 1);
-  g_symbols[1] = new CBC_SymbolInfo(5, 7, 10, 10, 1);
-  g_symbols[2] = new CBC_SymbolInfo(5, 7, 16, 6, 1);
-  g_symbols[3] = new CBC_SymbolInfo(8, 10, 12, 12, 1);
-  g_symbols[4] = new CBC_SymbolInfo(10, 11, 14, 6, 2);
-  g_symbols[5] = new CBC_SymbolInfo(12, 12, 14, 14, 1);
-  g_symbols[6] = new CBC_SymbolInfo(16, 14, 24, 10, 1);
-  g_symbols[7] = new CBC_SymbolInfo(18, 14, 16, 16, 1);
-  g_symbols[8] = new CBC_SymbolInfo(22, 18, 18, 18, 1);
-  g_symbols[9] = new CBC_SymbolInfo(22, 18, 16, 10, 2);
-  g_symbols[10] = new CBC_SymbolInfo(30, 20, 20, 20, 1);
-  g_symbols[11] = new CBC_SymbolInfo(32, 24, 16, 14, 2);
-  g_symbols[12] = new CBC_SymbolInfo(36, 24, 22, 22, 1);
-  g_symbols[13] = new CBC_SymbolInfo(44, 28, 24, 24, 1);
-  g_symbols[14] = new CBC_SymbolInfo(49, 28, 22, 14, 2);
-  g_symbols[15] = new CBC_SymbolInfo(62, 36, 14, 14, 4);
-  g_symbols[16] = new CBC_SymbolInfo(86, 42, 16, 16, 4);
-  g_symbols[17] = new CBC_SymbolInfo(114, 48, 18, 18, 4);
-  g_symbols[18] = new CBC_SymbolInfo(144, 56, 20, 20, 4);
-  g_symbols[19] = new CBC_SymbolInfo(174, 68, 22, 22, 4);
-  g_symbols[20] = new CBC_SymbolInfo(204, 84, 24, 24, 4, 102, 42);
-  g_symbols[21] = new CBC_SymbolInfo(280, 112, 14, 14, 16, 140, 56);
-  g_symbols[22] = new CBC_SymbolInfo(368, 144, 16, 16, 16, 92, 36);
-  g_symbols[23] = new CBC_SymbolInfo(456, 192, 18, 18, 16, 114, 48);
-  g_symbols[24] = new CBC_SymbolInfo(576, 224, 20, 20, 16, 144, 56);
-  g_symbols[25] = new CBC_SymbolInfo(696, 272, 22, 22, 16, 174, 68);
-  g_symbols[26] = new CBC_SymbolInfo(816, 336, 24, 24, 16, 136, 56);
-  g_symbols[27] = new CBC_SymbolInfo(1050, 408, 18, 18, 36, 175, 68);
-  g_symbols[28] = new CBC_SymbolInfo(1304, 496, 20, 20, 36, 163, 62);
-  g_symbols[29] = new CBC_DataMatrixSymbolInfo144();
+  for (size_t i = 0; i < kSymbolDataSize; ++i)
+    g_symbols[i] = new CBC_SymbolInfo(&kSymbolData[i]);
+  g_symbols[kSymbolDataSize] = new CBC_DataMatrixSymbolInfo144();
 }
 
+// static
 void CBC_SymbolInfo::Finalize() {
-  for (size_t i = 0; i < kSymbolsCount; i++) {
+  for (size_t i = 0; i < kSymbolsCount; ++i) {
     delete g_symbols[i];
     g_symbols[i] = nullptr;
   }
 }
 
-CBC_SymbolInfo::CBC_SymbolInfo(size_t dataCapacity,
-                               size_t errorCodewords,
-                               int32_t matrixWidth,
-                               int32_t matrixHeight,
-                               int32_t dataRegions)
-    : CBC_SymbolInfo(dataCapacity,
-                     errorCodewords,
-                     matrixWidth,
-                     matrixHeight,
-                     dataRegions,
-                     dataCapacity,
-                     errorCodewords) {}
-
-CBC_SymbolInfo::CBC_SymbolInfo(size_t dataCapacity,
-                               size_t errorCodewords,
-                               int32_t matrixWidth,
-                               int32_t matrixHeight,
-                               int32_t dataRegions,
-                               size_t rsBlockData,
-                               size_t rsBlockError)
-    : m_rectangular(matrixWidth != matrixHeight),
-      m_dataCapacity(dataCapacity),
-      m_errorCodewords(errorCodewords),
-      m_matrixWidth(matrixWidth),
-      m_matrixHeight(matrixHeight),
-      m_dataRegions(dataRegions),
-      m_rsBlockData(rsBlockData),
-      m_rsBlockError(rsBlockError) {}
+CBC_SymbolInfo::CBC_SymbolInfo(const Data* data) : data_(data) {}
 
 CBC_SymbolInfo::~CBC_SymbolInfo() = default;
 
-const CBC_SymbolInfo* CBC_SymbolInfo::Lookup(size_t iDataCodewords,
-                                             bool bAllowRectangular) {
-  for (size_t i = 0; i < kSymbolsCount; i++) {
+const CBC_SymbolInfo* CBC_SymbolInfo::Lookup(size_t data_codewords,
+                                             bool allow_rectangular) {
+  for (size_t i = 0; i < kSymbolsCount; ++i) {
     CBC_SymbolInfo* symbol = g_symbols[i];
-    if (symbol->m_rectangular && !bAllowRectangular)
+    if (symbol->is_rectangular() && !allow_rectangular)
       continue;
 
-    if (iDataCodewords <= symbol->dataCapacity())
+    if (data_codewords <= symbol->data_capacity())
       return symbol;
   }
   return nullptr;
 }
 
-int32_t CBC_SymbolInfo::getHorizontalDataRegions() const {
-  switch (m_dataRegions) {
+int32_t CBC_SymbolInfo::GetHorizontalDataRegions() const {
+  switch (data_->data_regions) {
     case 1:
       return 1;
     case 2:
@@ -135,13 +106,12 @@
     case 36:
       return 6;
     default:
-      NOTREACHED();
-      return 0;
+      NOTREACHED_NORETURN();
   }
 }
 
-int32_t CBC_SymbolInfo::getVerticalDataRegions() const {
-  switch (m_dataRegions) {
+int32_t CBC_SymbolInfo::GetVerticalDataRegions() const {
+  switch (data_->data_regions) {
     case 1:
       return 1;
     case 2:
@@ -153,39 +123,34 @@
     case 36:
       return 6;
     default:
-      NOTREACHED();
-      return 0;
+      NOTREACHED_NORETURN();
   }
 }
 
-int32_t CBC_SymbolInfo::getSymbolDataWidth() const {
-  return getHorizontalDataRegions() * m_matrixWidth;
+int32_t CBC_SymbolInfo::GetSymbolDataWidth() const {
+  return GetHorizontalDataRegions() * data_->matrix_width;
 }
 
-int32_t CBC_SymbolInfo::getSymbolDataHeight() const {
-  return getVerticalDataRegions() * m_matrixHeight;
+int32_t CBC_SymbolInfo::GetSymbolDataHeight() const {
+  return GetVerticalDataRegions() * data_->matrix_height;
 }
 
-int32_t CBC_SymbolInfo::getSymbolWidth() const {
-  return getSymbolDataWidth() + (getHorizontalDataRegions() * 2);
+int32_t CBC_SymbolInfo::GetSymbolWidth() const {
+  return GetSymbolDataWidth() + (GetHorizontalDataRegions() * 2);
 }
 
-int32_t CBC_SymbolInfo::getSymbolHeight() const {
-  return getSymbolDataHeight() + (getVerticalDataRegions() * 2);
+int32_t CBC_SymbolInfo::GetSymbolHeight() const {
+  return GetSymbolDataHeight() + (GetVerticalDataRegions() * 2);
 }
 
-size_t CBC_SymbolInfo::getCodewordCount() const {
-  return m_dataCapacity + m_errorCodewords;
+size_t CBC_SymbolInfo::GetInterleavedBlockCount() const {
+  return data_->data_capacity / data_->rs_block_data;
 }
 
-size_t CBC_SymbolInfo::getInterleavedBlockCount() const {
-  return m_dataCapacity / m_rsBlockData;
+size_t CBC_SymbolInfo::GetDataLengthForInterleavedBlock() const {
+  return data_->rs_block_data;
 }
 
-size_t CBC_SymbolInfo::getDataLengthForInterleavedBlock() const {
-  return m_rsBlockData;
-}
-
-size_t CBC_SymbolInfo::getErrorLengthForInterleavedBlock() const {
-  return m_rsBlockError;
+size_t CBC_SymbolInfo::GetErrorLengthForInterleavedBlock() const {
+  return data_->rs_block_error;
 }
diff --git a/fxbarcode/datamatrix/BC_SymbolInfo.h b/fxbarcode/datamatrix/BC_SymbolInfo.h
index 07e563d..9aa9ed7 100644
--- a/fxbarcode/datamatrix/BC_SymbolInfo.h
+++ b/fxbarcode/datamatrix/BC_SymbolInfo.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,59 +7,54 @@
 #ifndef FXBARCODE_DATAMATRIX_BC_SYMBOLINFO_H_
 #define FXBARCODE_DATAMATRIX_BC_SYMBOLINFO_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include <stddef.h>
+#include <stdint.h>
+
+#include "core/fxcrt/unowned_ptr.h"
 
 class CBC_SymbolInfo {
  public:
-  CBC_SymbolInfo(size_t dataCapacity,
-                 size_t errorCodewords,
-                 int32_t matrixWidth,
-                 int32_t matrixHeight,
-                 int32_t dataRegions);
+  struct Data {
+    int16_t data_capacity;
+    int16_t error_codewords;
+    int16_t rs_block_data;
+    int8_t rs_block_error;
+    int8_t matrix_width;
+    int8_t matrix_height;
+    int8_t data_regions;
+  };
+
   virtual ~CBC_SymbolInfo();
 
   static void Initialize();
   static void Finalize();
-  static void overrideSymbolSet(CBC_SymbolInfo* override);
-  static const CBC_SymbolInfo* Lookup(size_t iDataCodewords,
-                                      bool bAllowRectangular);
+  static const CBC_SymbolInfo* Lookup(size_t data_codewords,
+                                      bool allow_rectangular);
 
-  int32_t getSymbolDataWidth() const;
-  int32_t getSymbolDataHeight() const;
-  int32_t getSymbolWidth() const;
-  int32_t getSymbolHeight() const;
-  size_t getCodewordCount() const;
-  virtual size_t getInterleavedBlockCount() const;
-  size_t getDataLengthForInterleavedBlock() const;
-  size_t getErrorLengthForInterleavedBlock() const;
+  int32_t GetSymbolDataWidth() const;
+  int32_t GetSymbolDataHeight() const;
+  int32_t GetSymbolWidth() const;
+  int32_t GetSymbolHeight() const;
+  virtual size_t GetInterleavedBlockCount() const;
+  size_t GetDataLengthForInterleavedBlock() const;
+  size_t GetErrorLengthForInterleavedBlock() const;
 
-  size_t dataCapacity() const { return m_dataCapacity; }
-  size_t errorCodewords() const { return m_errorCodewords; }
-  int32_t matrixWidth() const { return m_matrixWidth; }
-  int32_t matrixHeight() const { return m_matrixHeight; }
+  size_t data_capacity() const { return data_->data_capacity; }
+  size_t error_codewords() const { return data_->error_codewords; }
+  int32_t matrix_width() const { return data_->matrix_width; }
+  int32_t matrix_height() const { return data_->matrix_height; }
 
  protected:
-  CBC_SymbolInfo(size_t dataCapacity,
-                 size_t errorCodewords,
-                 int32_t matrixWidth,
-                 int32_t matrixHeight,
-                 int32_t dataRegions,
-                 size_t rsBlockData,
-                 size_t rsBlockError);
+  explicit CBC_SymbolInfo(const Data* data);
 
  private:
-  int32_t getHorizontalDataRegions() const;
-  int32_t getVerticalDataRegions() const;
+  int32_t GetHorizontalDataRegions() const;
+  int32_t GetVerticalDataRegions() const;
+  bool is_rectangular() const {
+    return data_->matrix_width != data_->matrix_height;
+  }
 
-  const bool m_rectangular;
-  const size_t m_dataCapacity;
-  const size_t m_errorCodewords;
-  const int32_t m_matrixWidth;
-  const int32_t m_matrixHeight;
-  const int32_t m_dataRegions;
-  const size_t m_rsBlockData;
-  const size_t m_rsBlockError;
+  UnownedPtr<const Data> const data_;
 };
 
 #endif  // FXBARCODE_DATAMATRIX_BC_SYMBOLINFO_H_
diff --git a/fxbarcode/datamatrix/BC_TextEncoder.cpp b/fxbarcode/datamatrix/BC_TextEncoder.cpp
index bf87afa..f5d2bd5 100644
--- a/fxbarcode/datamatrix/BC_TextEncoder.cpp
+++ b/fxbarcode/datamatrix/BC_TextEncoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -47,7 +47,7 @@
     *sb += (wchar_t)(c - 48 + 4);
     return 1;
   }
-  if (c >= 'a' && c <= 'z') {
+  if (FXSYS_IsLowerASCII(c)) {
     *sb += (wchar_t)(c - 97 + 14);
     return 1;
   }
@@ -76,7 +76,7 @@
     *sb += (wchar_t)(c - 96);
     return 2;
   }
-  if (c >= 'A' && c <= 'Z') {
+  if (FXSYS_IsUpperASCII(c)) {
     *sb += (wchar_t)'\2';
     *sb += (wchar_t)(c - 65 + 1);
     return 2;
diff --git a/fxbarcode/datamatrix/BC_TextEncoder.h b/fxbarcode/datamatrix/BC_TextEncoder.h
index 40914b3..00a4abf 100644
--- a/fxbarcode/datamatrix/BC_TextEncoder.h
+++ b/fxbarcode/datamatrix/BC_TextEncoder.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/datamatrix/BC_X12Encoder.cpp b/fxbarcode/datamatrix/BC_X12Encoder.cpp
index 37180ea..100bd90 100644
--- a/fxbarcode/datamatrix/BC_X12Encoder.cpp
+++ b/fxbarcode/datamatrix/BC_X12Encoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -67,7 +67,7 @@
     return false;
 
   int32_t available =
-      context->m_symbolInfo->dataCapacity() - context->getCodewordCount();
+      context->m_symbolInfo->data_capacity() - context->getCodewordCount();
   size_t count = buffer->GetLength();
   if (count == 2) {
     context->writeCodeword(CBC_HighLevelEncoder::X12_UNLATCH);
@@ -94,7 +94,7 @@
     *sb += (wchar_t)'\3';
   else if (FXSYS_IsDecimalDigit(c))
     *sb += (wchar_t)(c - 48 + 4);
-  else if (c >= 'A' && c <= 'Z')
+  else if (FXSYS_IsUpperASCII(c))
     *sb += (wchar_t)(c - 65 + 14);
   else
     return 0;
diff --git a/fxbarcode/datamatrix/BC_X12Encoder.h b/fxbarcode/datamatrix/BC_X12Encoder.h
index aa258a9..7f5e9b0 100644
--- a/fxbarcode/datamatrix/BC_X12Encoder.h
+++ b/fxbarcode/datamatrix/BC_X12Encoder.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/oned/BC_OneDimWriter.cpp b/fxbarcode/oned/BC_OneDimWriter.cpp
index 5a02cdf..3320c47 100644
--- a/fxbarcode/oned/BC_OneDimWriter.cpp
+++ b/fxbarcode/oned/BC_OneDimWriter.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,20 +22,33 @@
 
 #include "fxbarcode/oned/BC_OneDimWriter.h"
 
+#include <math.h>
+
 #include <algorithm>
 #include <memory>
 #include <vector>
 
 #include "build/build_config.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/cfx_unicodeencodingex.h"
 #include "core/fxge/text_char_pos.h"
 #include "fxbarcode/BC_Writer.h"
 
+// static
+bool CBC_OneDimWriter::HasValidContentSize(WideStringView contents) {
+  // Limit the size of 1D barcodes. Typical 1D barcodes are short so this should
+  // be sufficient for most use cases.
+  static constexpr size_t kMaxInputLengthBytes = 8192;
+
+  size_t size = contents.GetLength();
+  return size > 0 && size <= kMaxInputLengthBytes;
+}
+
 CBC_OneDimWriter::CBC_OneDimWriter() = default;
 
 CBC_OneDimWriter::~CBC_OneDimWriter() = default;
@@ -72,36 +85,20 @@
   m_fontColor = color;
 }
 
-uint8_t* CBC_OneDimWriter::EncodeWithHint(const ByteString& contents,
-                                          BCFORMAT format,
-                                          int32_t& outWidth,
-                                          int32_t& outHeight,
-                                          int32_t hints) {
-  outHeight = 1;
-  return EncodeImpl(contents, outWidth);
-}
-
-uint8_t* CBC_OneDimWriter::Encode(const ByteString& contents,
-                                  BCFORMAT format,
-                                  int32_t& outWidth,
-                                  int32_t& outHeight) {
-  return EncodeWithHint(contents, format, outWidth, outHeight, 0);
-}
-
-int32_t CBC_OneDimWriter::AppendPattern(uint8_t* target,
-                                        int32_t pos,
-                                        const int8_t* pattern,
-                                        int32_t patternLength,
-                                        bool startColor) {
+pdfium::span<uint8_t> CBC_OneDimWriter::AppendPattern(
+    pdfium::span<uint8_t> target,
+    pdfium::span<const uint8_t> pattern,
+    bool startColor) {
   bool color = startColor;
-  int32_t numAdded = 0;
-  for (int32_t i = 0; i < patternLength; i++) {
-    for (int32_t j = 0; j < pattern[i]; j++)
+  size_t added = 0;
+  size_t pos = 0;
+  for (const int8_t pattern_value : pattern) {
+    for (int32_t i = 0; i < pattern_value; ++i)
       target[pos++] = color ? 1 : 0;
-    numAdded += pattern[i];
+    added += pattern_value;
     color = !color;
   }
-  return numAdded;
+  return target.subspan(added);
 }
 
 void CBC_OneDimWriter::CalcTextInfo(const ByteString& text,
@@ -119,7 +116,7 @@
   for (size_t i = 0; i < length; ++i) {
     charcodes[i] = encoding->CharCodeFromUnicode(text[i]);
     int32_t glyph_code = encoding->GlyphFromCharCode(charcodes[i]);
-    uint32_t glyph_value = cFont->GetGlyphWidth(glyph_code);
+    int glyph_value = cFont->GetGlyphWidth(glyph_code);
     float temp = glyph_value * fontSize / 1000.0;
     charWidth += temp;
   }
@@ -135,7 +132,7 @@
   charPos[0].m_Origin = CFX_PointF(penX + left, penY + top);
   charPos[0].m_GlyphIndex = encoding->GlyphFromCharCode(charcodes[0]);
   charPos[0].m_FontCharWidth = cFont->GetGlyphWidth(charPos[0].m_GlyphIndex);
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
   charPos[0].m_ExtGID = charPos[0].m_GlyphIndex;
 #endif
   penX += (float)(charPos[0].m_FontCharWidth) * (float)fontSize / 1000.0f;
@@ -143,7 +140,7 @@
     charPos[i].m_Origin = CFX_PointF(penX + left, penY + top);
     charPos[i].m_GlyphIndex = encoding->GlyphFromCharCode(charcodes[i]);
     charPos[i].m_FontCharWidth = cFont->GetGlyphWidth(charPos[i].m_GlyphIndex);
-#if defined(OS_MACOSX)
+#if BUILDFLAG(IS_APPLE)
     charPos[i].m_ExtGID = charPos[i].m_GlyphIndex;
 #endif
     penX += (float)(charPos[i].m_FontCharWidth) * (float)fontSize / 1000.0f;
@@ -151,37 +148,34 @@
 }
 
 void CBC_OneDimWriter::ShowDeviceChars(CFX_RenderDevice* device,
-                                       const CFX_Matrix* matrix,
+                                       const CFX_Matrix& matrix,
                                        const ByteString str,
                                        float geWidth,
                                        TextCharPos* pCharPos,
                                        float locX,
                                        float locY,
                                        int32_t barWidth) {
-  int32_t iFontSize = (int32_t)fabs(m_fFontSize);
+  int32_t iFontSize = static_cast<int32_t>(fabs(m_fFontSize));
   int32_t iTextHeight = iFontSize + 1;
   CFX_FloatRect rect((float)locX, (float)locY, (float)(locX + geWidth),
                      (float)(locY + iTextHeight));
   if (geWidth != m_Width) {
     rect.right -= 1;
   }
-  FX_RECT re = matrix->TransformRect(rect).GetOuterRect();
+  FX_RECT re = matrix.TransformRect(rect).GetOuterRect();
   device->FillRect(re, kBackgroundColor);
   CFX_Matrix affine_matrix(1.0, 0.0, 0.0, -1.0, (float)locX,
                            (float)(locY + iFontSize));
-  if (matrix) {
-    affine_matrix.Concat(*matrix);
-  }
-  device->DrawNormalText(str.GetLength(), pCharPos, m_pFont.Get(),
+  affine_matrix.Concat(matrix);
+  device->DrawNormalText(pdfium::make_span(pCharPos, str.GetLength()), m_pFont,
                          static_cast<float>(iFontSize), affine_matrix,
-                         m_fontColor, FXTEXT_CLEARTYPE);
+                         m_fontColor, GetTextRenderOptions());
 }
 
 bool CBC_OneDimWriter::ShowChars(WideStringView contents,
                                  CFX_RenderDevice* device,
-                                 const CFX_Matrix* matrix,
-                                 int32_t barWidth,
-                                 int32_t multiple) {
+                                 const CFX_Matrix& matrix,
+                                 int32_t barWidth) {
   if (!device || !m_pFont)
     return false;
 
@@ -189,39 +183,38 @@
   std::vector<TextCharPos> charpos(str.GetLength());
   float charsLen = 0;
   float geWidth = 0;
-  if (m_locTextLoc == BC_TEXT_LOC_ABOVEEMBED ||
-      m_locTextLoc == BC_TEXT_LOC_BELOWEMBED) {
+  if (m_locTextLoc == BC_TEXT_LOC::kAboveEmbed ||
+      m_locTextLoc == BC_TEXT_LOC::kBelowEmbed) {
     geWidth = 0;
-  } else if (m_locTextLoc == BC_TEXT_LOC_ABOVE ||
-             m_locTextLoc == BC_TEXT_LOC_BELOW) {
+  } else if (m_locTextLoc == BC_TEXT_LOC::kAbove ||
+             m_locTextLoc == BC_TEXT_LOC::kBelow) {
     geWidth = (float)barWidth;
   }
-  int32_t iFontSize = (int32_t)fabs(m_fFontSize);
+  int32_t iFontSize = static_cast<int32_t>(fabs(m_fFontSize));
   int32_t iTextHeight = iFontSize + 1;
-  CalcTextInfo(str, charpos.data(), m_pFont.Get(), geWidth, iFontSize,
-               charsLen);
+  CalcTextInfo(str, charpos.data(), m_pFont, geWidth, iFontSize, charsLen);
   if (charsLen < 1)
     return true;
 
   int32_t locX = 0;
   int32_t locY = 0;
   switch (m_locTextLoc) {
-    case BC_TEXT_LOC_ABOVEEMBED:
-      locX = (int32_t)(barWidth - charsLen) / 2;
+    case BC_TEXT_LOC::kAboveEmbed:
+      locX = static_cast<int32_t>(barWidth - charsLen) / 2;
       locY = 0;
       geWidth = charsLen;
       break;
-    case BC_TEXT_LOC_ABOVE:
+    case BC_TEXT_LOC::kAbove:
       locX = 0;
       locY = 0;
       geWidth = (float)barWidth;
       break;
-    case BC_TEXT_LOC_BELOWEMBED:
-      locX = (int32_t)(barWidth - charsLen) / 2;
+    case BC_TEXT_LOC::kBelowEmbed:
+      locX = static_cast<int32_t>(barWidth - charsLen) / 2;
       locY = m_Height - iTextHeight;
       geWidth = charsLen;
       break;
-    case BC_TEXT_LOC_BELOW:
+    case BC_TEXT_LOC::kBelow:
     default:
       locX = 0;
       locY = m_Height - iTextHeight;
@@ -234,72 +227,57 @@
 }
 
 bool CBC_OneDimWriter::RenderDeviceResult(CFX_RenderDevice* device,
-                                          const CFX_Matrix* matrix,
+                                          const CFX_Matrix& matrix,
                                           WideStringView contents) {
   if (m_output.empty())
     return false;
 
   CFX_GraphStateData stateData;
-  CFX_PathData path;
+  CFX_Path path;
   path.AppendRect(0, 0, static_cast<float>(m_Width),
                   static_cast<float>(m_Height));
-  device->DrawPath(&path, matrix, &stateData, kBackgroundColor,
-                   kBackgroundColor, FXFILL_ALTERNATE);
+  device->DrawPath(path, &matrix, &stateData, kBackgroundColor,
+                   kBackgroundColor, CFX_FillRenderOptions::EvenOddOptions());
   CFX_Matrix scaledMatrix(m_outputHScale, 0.0, 0.0,
                           static_cast<float>(m_Height), 0.0, 0.0);
-  scaledMatrix.Concat(*matrix);
+  scaledMatrix.Concat(matrix);
   for (const auto& rect : m_output) {
     CFX_GraphStateData data;
-    device->DrawPath(&rect, &scaledMatrix, &data, kBarColor, 0, FXFILL_WINDING);
+    device->DrawPath(rect, &scaledMatrix, &data, kBarColor, 0,
+                     CFX_FillRenderOptions::WindingOptions());
   }
 
-  return m_locTextLoc == BC_TEXT_LOC_NONE || !contents.Contains(' ') ||
-         ShowChars(contents, device, matrix, m_barWidth, m_multiple);
+  return m_locTextLoc == BC_TEXT_LOC::kNone || !contents.Contains(' ') ||
+         ShowChars(contents, device, matrix, m_barWidth);
 }
 
 bool CBC_OneDimWriter::RenderResult(WideStringView contents,
-                                    uint8_t* code,
-                                    int32_t codeLength) {
-  if (codeLength < 1)
+                                    pdfium::span<const uint8_t> code) {
+  if (code.empty())
     return false;
 
   m_ModuleHeight = std::max(m_ModuleHeight, 20);
-  const int32_t codeOldLength = codeLength;
+  const size_t original_codelength = code.size();
   const int32_t leftPadding = m_bLeftPadding ? 7 : 0;
   const int32_t rightPadding = m_bRightPadding ? 7 : 0;
-  codeLength += leftPadding;
-  codeLength += rightPadding;
+  const size_t codelength = code.size() + leftPadding + rightPadding;
   m_outputHScale =
-      m_Width > 0 ? static_cast<float>(m_Width) / static_cast<float>(codeLength)
+      m_Width > 0 ? static_cast<float>(m_Width) / static_cast<float>(codelength)
                   : 1.0;
-  m_multiple = 1;
-  const int32_t outputWidth = codeLength;
   m_barWidth = m_Width;
 
   m_output.clear();
-  m_output.reserve(codeOldLength * m_multiple);
-  for (int32_t inputX = 0, outputX = leftPadding * m_multiple;
-       inputX < codeOldLength; ++inputX, outputX += m_multiple) {
-    if (code[inputX] != 1)
+  m_output.reserve(original_codelength);
+  for (size_t i = 0; i < original_codelength; ++i) {
+    if (code[i] != 1)
       continue;
 
-    if (outputX >= outputWidth)
+    size_t output_index = i + leftPadding;
+    if (output_index >= codelength)
       return true;
 
-    if (outputX + m_multiple > outputWidth && outputWidth - outputX > 0) {
-      RenderVerticalBars(outputX, outputWidth - outputX);
-      return true;
-    }
-
-    RenderVerticalBars(outputX, m_multiple);
+    m_output.emplace_back();
+    m_output.back().AppendRect(output_index, 0.0f, output_index + 1, 1.0f);
   }
   return true;
 }
-
-void CBC_OneDimWriter::RenderVerticalBars(int32_t outputX, int32_t width) {
-  for (int i = 0; i < width; ++i) {
-    float x = outputX + i;
-    m_output.emplace_back();
-    m_output.back().AppendRect(x, 0.0f, x + 1, 1.0f);
-  }
-}
diff --git a/fxbarcode/oned/BC_OneDimWriter.h b/fxbarcode/oned/BC_OneDimWriter.h
index 3e0ccc9..3abcce4 100644
--- a/fxbarcode/oned/BC_OneDimWriter.h
+++ b/fxbarcode/oned/BC_OneDimWriter.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,60 +7,60 @@
 #ifndef FXBARCODE_ONED_BC_ONEDIMWRITER_H_
 #define FXBARCODE_ONED_BC_ONEDIMWRITER_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <vector>
 
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxge/cfx_textrenderoptions.h"
 #include "fxbarcode/BC_Library.h"
 #include "fxbarcode/BC_Writer.h"
-#include "fxbarcode/utils.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_Font;
-class CFX_PathData;
+class CFX_Matrix;
+class CFX_Path;
 class CFX_RenderDevice;
 class TextCharPos;
 
 class CBC_OneDimWriter : public CBC_Writer {
  public:
+  static constexpr CFX_TextRenderOptions GetTextRenderOptions() {
+    return CFX_TextRenderOptions(CFX_TextRenderOptions::kLcd);
+  }
+  static bool HasValidContentSize(WideStringView contents);
+
   CBC_OneDimWriter();
   ~CBC_OneDimWriter() override;
 
   virtual bool RenderResult(WideStringView contents,
-                            uint8_t* code,
-                            int32_t codeLength);
+                            pdfium::span<const uint8_t> code);
   virtual bool CheckContentValidity(WideStringView contents) = 0;
   virtual WideString FilterContents(WideStringView contents) = 0;
-  virtual void SetPrintChecksum(bool checksum);
   virtual void SetDataLength(int32_t length);
-  virtual void SetCalcChecksum(bool state);
-  virtual void SetFontSize(float size);
-  virtual void SetFontStyle(int32_t style);
-  virtual void SetFontColor(FX_ARGB color);
 
-  uint8_t* Encode(const ByteString& contents,
-                  BCFORMAT format,
-                  int32_t& outWidth,
-                  int32_t& outHeight);
+  void SetPrintChecksum(bool checksum);
+  void SetCalcChecksum(bool state);
+  void SetFontSize(float size);
+  void SetFontStyle(int32_t style);
+  void SetFontColor(FX_ARGB color);
+
+  virtual DataVector<uint8_t> Encode(const ByteString& contents) = 0;
   bool RenderDeviceResult(CFX_RenderDevice* device,
-                          const CFX_Matrix* matrix,
+                          const CFX_Matrix& matrix,
                           WideStringView contents);
   bool SetFont(CFX_Font* cFont);
 
  protected:
-  virtual uint8_t* EncodeWithHint(const ByteString& contents,
-                                  BCFORMAT format,
-                                  int32_t& outWidth,
-                                  int32_t& outHeight,
-                                  int32_t hints);
-  virtual uint8_t* EncodeImpl(const ByteString& contents,
-                              int32_t& outLength) = 0;
   virtual bool ShowChars(WideStringView contents,
                          CFX_RenderDevice* device,
-                         const CFX_Matrix* matrix,
-                         int32_t barWidth,
-                         int32_t multiple);
+                         const CFX_Matrix& matrix,
+                         int32_t barWidth);
   void ShowDeviceChars(CFX_RenderDevice* device,
-                       const CFX_Matrix* matrix,
+                       const CFX_Matrix& matrix,
                        const ByteString str,
                        float geWidth,
                        TextCharPos* pCharPos,
@@ -73,13 +73,9 @@
                     float geWidth,
                     int32_t fontSize,
                     float& charsLen);
-  int32_t AppendPattern(uint8_t* target,
-                        int32_t pos,
-                        const int8_t* pattern,
-                        int32_t patternLength,
-                        bool startColor);
-
-  void RenderVerticalBars(int32_t outputX, int32_t width);
+  pdfium::span<uint8_t> AppendPattern(pdfium::span<uint8_t> target,
+                                      pdfium::span<const uint8_t> pattern,
+                                      bool startColor);
 
   bool m_bPrintChecksum = true;
   bool m_bCalcChecksum = false;
@@ -90,14 +86,13 @@
   float m_fFontSize = 10.0f;
   int32_t m_iFontStyle = 0;
   uint32_t m_fontColor = 0xff000000;
-  BC_TEXT_LOC m_locTextLoc = BC_TEXT_LOC_BELOWEMBED;
+  BC_TEXT_LOC m_locTextLoc = BC_TEXT_LOC::kBelowEmbed;
 
   int32_t m_iDataLenth = 0;
   size_t m_iContentLen = 0;
 
-  std::vector<CFX_PathData> m_output;
+  std::vector<CFX_Path> m_output;
   int32_t m_barWidth;
-  int32_t m_multiple;
   float m_outputHScale;
 };
 
diff --git a/fxbarcode/oned/BC_OnedCodaBarWriter.cpp b/fxbarcode/oned/BC_OnedCodaBarWriter.cpp
index c4334c6..6be75ad 100644
--- a/fxbarcode/oned/BC_OnedCodaBarWriter.cpp
+++ b/fxbarcode/oned/BC_OnedCodaBarWriter.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,10 +22,14 @@
 
 #include "fxbarcode/oned/BC_OnedCodaBarWriter.h"
 
+#include <iterator>
+
+#include "core/fxcrt/fx_2d_size.h"
+#include "core/fxcrt/fx_extension.h"
 #include "fxbarcode/BC_Writer.h"
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 #include "fxbarcode/oned/BC_OneDimWriter.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/containers/contains.h"
 
 namespace {
 
@@ -36,16 +40,25 @@
 const int8_t kOnedCodaCharacterEncoding[] = {
     0x03, 0x06, 0x09, 0x60, 0x12, 0x42, 0x21, 0x24, 0x30, 0x48, 0x0c,
     0x18, 0x45, 0x51, 0x54, 0x15, 0x1A, 0x29, 0x0B, 0x0E, 0x1A, 0x29};
-static_assert(FX_ArraySize(kOnedCodaCharacterEncoding) == 22, "Wrong size");
-static_assert(FX_ArraySize(kOnedCodaCharacterEncoding) ==
-                  FX_ArraySize(kOnedCodaAlphabet),
+static_assert(std::size(kOnedCodaCharacterEncoding) == 22, "Wrong size");
+static_assert(std::size(kOnedCodaCharacterEncoding) ==
+                  std::size(kOnedCodaAlphabet),
               "Wrong size");
 
 const char kStartEndChars[] = {'A', 'B', 'C', 'D', 'T', 'N', '*', 'E',
                                'a', 'b', 'c', 'd', 't', 'n', 'e'};
-const char kCOntentChars[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+const char kContentChars[] = {'0', '1', '2', '3', '4', '5', '6', '7',
                               '8', '9', '-', '$', '/', ':', '+', '.'};
 
+bool IsValidChar(wchar_t ch, bool isContent) {
+  if (ch > 0x7F)
+    return false;
+
+  char narrow_ch = static_cast<char>(ch);
+  return pdfium::Contains(kContentChars, narrow_ch) ||
+         (isContent && pdfium::Contains(kStartEndChars, narrow_ch));
+}
+
 }  // namespace
 
 CBC_OnedCodaBarWriter::CBC_OnedCodaBarWriter() = default;
@@ -53,7 +66,7 @@
 CBC_OnedCodaBarWriter::~CBC_OnedCodaBarWriter() = default;
 
 bool CBC_OnedCodaBarWriter::SetStartChar(char start) {
-  if (!pdfium::ContainsValue(kStartEndChars, start))
+  if (!pdfium::Contains(kStartEndChars, start))
     return false;
 
   m_chStart = start;
@@ -61,7 +74,7 @@
 }
 
 bool CBC_OnedCodaBarWriter::SetEndChar(char end) {
-  if (!pdfium::ContainsValue(kStartEndChars, end))
+  if (!pdfium::Contains(kStartEndChars, end))
     return false;
 
   m_chEnd = end;
@@ -72,12 +85,8 @@
   m_iDataLenth = length + 2;
 }
 
-bool CBC_OnedCodaBarWriter::SetTextLocation(BC_TEXT_LOC location) {
-  if (location < BC_TEXT_LOC_NONE || location > BC_TEXT_LOC_BELOWEMBED) {
-    return false;
-  }
+void CBC_OnedCodaBarWriter::SetTextLocation(BC_TEXT_LOC location) {
   m_locTextLoc = location;
-  return true;
 }
 
 bool CBC_OnedCodaBarWriter::SetWideNarrowRatio(int8_t ratio) {
@@ -88,19 +97,10 @@
   return true;
 }
 
-bool CBC_OnedCodaBarWriter::FindChar(wchar_t ch, bool isContent) {
-  if (ch > 0x7F)
-    return false;
-
-  char narrow_ch = static_cast<char>(ch);
-  return pdfium::ContainsValue(kCOntentChars, narrow_ch) ||
-         (isContent && pdfium::ContainsValue(kStartEndChars, narrow_ch));
-}
-
 bool CBC_OnedCodaBarWriter::CheckContentValidity(WideStringView contents) {
-  return std::all_of(
-      contents.begin(), contents.end(),
-      [this](const wchar_t& ch) { return this->FindChar(ch, false); });
+  return HasValidContentSize(contents) &&
+         std::all_of(contents.begin(), contents.end(),
+                     [](const wchar_t& ch) { return IsValidChar(ch, false); });
 }
 
 WideString CBC_OnedCodaBarWriter::FilterContents(WideStringView contents) {
@@ -113,36 +113,22 @@
       index++;
       continue;
     }
-    if (!FindChar(ch, true))
+    if (!IsValidChar(ch, true))
       continue;
     filtercontents += ch;
   }
   return filtercontents;
 }
 
-uint8_t* CBC_OnedCodaBarWriter::EncodeWithHint(const ByteString& contents,
-                                               BCFORMAT format,
-                                               int32_t& outWidth,
-                                               int32_t& outHeight,
-                                               int32_t hints) {
-  if (format != BCFORMAT_CODABAR)
-    return nullptr;
-  return CBC_OneDimWriter::EncodeWithHint(contents, format, outWidth, outHeight,
-                                          hints);
-}
-
-uint8_t* CBC_OnedCodaBarWriter::EncodeImpl(const ByteString& contents,
-                                           int32_t& outLength) {
+DataVector<uint8_t> CBC_OnedCodaBarWriter::Encode(const ByteString& contents) {
   ByteString data = m_chStart + contents + m_chEnd;
   m_iContentLen = data.GetLength();
-  uint8_t* result = FX_Alloc2D(uint8_t, m_iWideNarrRatio * 7, data.GetLength());
+  DataVector<uint8_t> result(
+      Fx2DSizeOrDie(m_iWideNarrRatio * 7, data.GetLength()));
   char ch;
   int32_t position = 0;
   for (size_t index = 0; index < data.GetLength(); index++) {
-    ch = data[index];
-    if (((ch >= 'a') && (ch <= 'z'))) {
-      ch = ch - 32;
-    }
+    ch = FXSYS_ToUpperASCII(data[index]);
     switch (ch) {
       case 'T':
         ch = 'A';
@@ -160,7 +146,7 @@
         break;
     }
     int8_t code = 0;
-    for (size_t i = 0; i < FX_ArraySize(kOnedCodaAlphabet); i++) {
+    for (size_t i = 0; i < std::size(kOnedCodaAlphabet); i++) {
       if (ch == kOnedCodaAlphabet[i]) {
         code = kOnedCodaCharacterEncoding[i];
         break;
@@ -185,7 +171,7 @@
       position++;
     }
   }
-  outLength = position;
+  result.resize(position);
   return result;
 }
 
@@ -196,8 +182,7 @@
 }
 
 bool CBC_OnedCodaBarWriter::RenderResult(WideStringView contents,
-                                         uint8_t* code,
-                                         int32_t codeLength) {
+                                         pdfium::span<const uint8_t> code) {
   return CBC_OneDimWriter::RenderResult(
-      encodedContents(contents).AsStringView(), code, codeLength);
+      encodedContents(contents).AsStringView(), code);
 }
diff --git a/fxbarcode/oned/BC_OnedCodaBarWriter.h b/fxbarcode/oned/BC_OnedCodaBarWriter.h
index 6feb380..32d2f59 100644
--- a/fxbarcode/oned/BC_OnedCodaBarWriter.h
+++ b/fxbarcode/oned/BC_OnedCodaBarWriter.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,9 @@
 #ifndef FXBARCODE_ONED_BC_ONEDCODABARWRITER_H_
 #define FXBARCODE_ONED_BC_ONEDCODABARWRITER_H_
 
+#include <stdint.h>
+
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
 #include "fxbarcode/BC_Library.h"
 #include "fxbarcode/oned/BC_OneDimWriter.h"
 
@@ -17,26 +18,18 @@
   CBC_OnedCodaBarWriter();
   ~CBC_OnedCodaBarWriter() override;
 
-  // CBC_OneDimWriter
-  uint8_t* EncodeImpl(const ByteString& contents, int32_t& outLength) override;
-  uint8_t* EncodeWithHint(const ByteString& contents,
-                          BCFORMAT format,
-                          int32_t& outWidth,
-                          int32_t& outHeight,
-                          int32_t hints) override;
+  // CBC_OneDimWriter:
+  DataVector<uint8_t> Encode(const ByteString& contents) override;
   bool RenderResult(WideStringView contents,
-                    uint8_t* code,
-                    int32_t codeLength) override;
+                    pdfium::span<const uint8_t> code) override;
   bool CheckContentValidity(WideStringView contents) override;
   WideString FilterContents(WideStringView contents) override;
   void SetDataLength(int32_t length) override;
-  bool SetTextLocation(BC_TEXT_LOC location) override;
+  void SetTextLocation(BC_TEXT_LOC location) override;
   bool SetWideNarrowRatio(int8_t ratio) override;
   bool SetStartChar(char start) override;
   bool SetEndChar(char end) override;
 
-  virtual bool FindChar(wchar_t ch, bool isContent);
-
   WideString encodedContents(WideStringView contents);
 
  private:
diff --git a/fxbarcode/oned/BC_OnedCodaBarWriter_unittest.cpp b/fxbarcode/oned/BC_OnedCodaBarWriter_unittest.cpp
index 89b0ac8..1f7744b 100644
--- a/fxbarcode/oned/BC_OnedCodaBarWriter_unittest.cpp
+++ b/fxbarcode/oned/BC_OnedCodaBarWriter_unittest.cpp
@@ -1,58 +1,39 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxbarcode/oned/BC_OnedCodaBarWriter.h"
 
+#include <string.h>
+
+#include "core/fxcrt/data_vector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
-// 3 wide and 4 narrow modules per delimiter. One space between them.
-constexpr int kModulesForDelimiters = (3 * 2 + 4) * 2 + 1;
-
-// 2 wide and 5 narrow modules per number, '_' or '$'. 1 space between chars.
-constexpr int kModulesPerNumber = 2 * 2 + 5 + 1;
-
-// 3 wide and 4 narrow modules per number, '_' or '$'. 1 space between chars.
-constexpr int kModulesPerPunctuation = 3 * 2 + 4 + 1;
-
 TEST(OnedCodaBarWriterTest, Encode) {
   CBC_OnedCodaBarWriter writer;
-  int32_t width;
-  int32_t height;
 
-  uint8_t* encoded = writer.Encode("", BCFORMAT_CODABAR, width, height);
-  EXPECT_EQ(1, height);
-  EXPECT_EQ(kModulesForDelimiters, width);
-  const char* expected =
+  static const char kExpected1[] =
       "# ##  #  # "  // A Start
       "#  #  # ##";  // B End
-  for (size_t i = 0; i < strlen(expected); i++) {
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  }
-  FX_Free(encoded);
+  DataVector<uint8_t> encoded = writer.Encode("");
+  ASSERT_EQ(strlen(kExpected1), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected1); i++)
+    EXPECT_EQ(kExpected1[i] != ' ', !!encoded[i]) << i;
 
-  encoded = writer.Encode("123", BCFORMAT_CODABAR, width, height);
-  EXPECT_EQ(1, height);
-  EXPECT_EQ(kModulesForDelimiters + 3 * kModulesPerNumber, width);
-  expected =
+  static const char kExpected2[] =
       "# ##  #  # "  // A Start
       "# # ##  # "   // 1
       "# #  # ## "   // 2
       "##  # # # "   // 3
       "#  #  # ##";  // B End
-  for (size_t i = 0; i < strlen(expected); i++) {
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  }
-  FX_Free(encoded);
+  encoded = writer.Encode("123");
+  ASSERT_EQ(strlen(kExpected2), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected2); i++)
+    EXPECT_EQ(kExpected2[i] != ' ', !!encoded[i]) << i;
 
-  encoded = writer.Encode("-$./:+", BCFORMAT_CODABAR, width, height);
-  EXPECT_EQ(1, height);
-  EXPECT_EQ(kModulesForDelimiters + 2 * kModulesPerNumber +
-                4 * kModulesPerPunctuation,
-            width);
-  expected =
+  static const char kExpected3[] =
       "# ##  #  # "  // A Start
       "# #  ## # "   // -
       "# ##  # # "   // $
@@ -61,17 +42,12 @@
       "## # ## ## "  // :
       "# ## ## ## "  // +
       "#  #  # ##";  // B End
-  for (size_t i = 0; i < strlen(expected); i++) {
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  }
-  FX_Free(encoded);
+  encoded = writer.Encode("-$./:+");
+  ASSERT_EQ(strlen(kExpected3), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected3); i++)
+    EXPECT_EQ(kExpected3[i] != ' ', !!encoded[i]) << i;
 
-  encoded = writer.Encode("456.987987987/001", BCFORMAT_CODABAR, width, height);
-  EXPECT_EQ(1, height);
-  EXPECT_EQ(kModulesForDelimiters + 15 * kModulesPerNumber +
-                2 * kModulesPerPunctuation,
-            width);
-  expected =
+  static const char kExpected4[] =
       "# ##  #  # "  // A Start
       "# ## #  # "   // 4
       "## # #  # "   // 5
@@ -91,16 +67,14 @@
       "# # #  ## "   // 0
       "# # ##  # "   // 1
       "#  #  # ##";  // B End
-  for (size_t i = 0; i < strlen(expected); i++) {
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  }
-  FX_Free(encoded);
+  encoded = writer.Encode("456.987987987/001");
+  ASSERT_EQ(strlen(kExpected4), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected4); i++)
+    EXPECT_EQ(kExpected4[i] != ' ', !!encoded[i]) << i;
 }
 
 TEST(OnedCodaBarWriterTest, SetDelimiters) {
   CBC_OnedCodaBarWriter writer;
-  int32_t width;
-  int32_t height;
 
   EXPECT_TRUE(writer.SetStartChar('A'));
   EXPECT_TRUE(writer.SetStartChar('B'));
@@ -131,19 +105,16 @@
   writer.SetStartChar('N');
   writer.SetEndChar('*');
 
-  uint8_t* encoded = writer.Encode("987", BCFORMAT_CODABAR, width, height);
-  EXPECT_EQ(1, height);
-  EXPECT_EQ(kModulesForDelimiters + 3 * kModulesPerNumber, width);
-  const char* expected =
+  static const char kExpected[] =
       "#  #  # ## "  // N (same as B) Start
       "## #  # # "   // 9
       "#  ## # # "   // 8
       "#  # ## # "   // 7
       "# #  #  ##";  // * (same as C) End
-  for (size_t i = 0; i < strlen(expected); i++) {
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  }
-  FX_Free(encoded);
+  DataVector<uint8_t> encoded = writer.Encode("987");
+  ASSERT_EQ(strlen(kExpected), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected); i++)
+    EXPECT_EQ(kExpected[i] != ' ', !!encoded[i]) << i;
 }
 
 }  // namespace
diff --git a/fxbarcode/oned/BC_OnedCode128Writer.cpp b/fxbarcode/oned/BC_OnedCode128Writer.cpp
index b01abb5..0dafaa0 100644
--- a/fxbarcode/oned/BC_OnedCode128Writer.cpp
+++ b/fxbarcode/oned/BC_OnedCode128Writer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,18 +22,20 @@
 
 #include "fxbarcode/oned/BC_OnedCode128Writer.h"
 
-#include <cctype>
+#include <ctype.h>
+
 #include <memory>
 
 #include "core/fxcrt/fx_memory_wrappers.h"
 #include "fxbarcode/BC_Writer.h"
 #include "fxbarcode/oned/BC_OneDimWriter.h"
+#include "third_party/base/check.h"
 
 namespace {
 
 constexpr size_t kPatternSize = 7;
 
-const int8_t CODE_PATTERNS[107][kPatternSize] = {
+const uint8_t kCodePatterns[107][kPatternSize] = {
     {2, 1, 2, 2, 2, 2, 0}, {2, 2, 2, 1, 2, 2, 0}, {2, 2, 2, 2, 2, 1, 0},
     {1, 2, 1, 2, 2, 3, 0}, {1, 2, 1, 3, 2, 2, 0}, {1, 3, 1, 2, 2, 2, 0},
     {1, 2, 2, 2, 1, 3, 0}, {1, 2, 2, 3, 1, 2, 0}, {1, 3, 2, 2, 1, 2, 0},
@@ -75,26 +77,28 @@
 const int32_t CODE_START_C = 105;
 const int32_t CODE_STOP = 106;
 
+bool IsInOnedCode128Alphabet(wchar_t ch) {
+  int32_t index = static_cast<int32_t>(ch);
+  return index >= 32 && index <= 126 && index != 34;
+}
+
 }  // namespace
 
 CBC_OnedCode128Writer::CBC_OnedCode128Writer(BC_TYPE type)
     : m_codeFormat(type) {
-  ASSERT(m_codeFormat == BC_CODE128_B || m_codeFormat == BC_CODE128_C);
+  DCHECK(m_codeFormat == BC_TYPE::kCode128B ||
+         m_codeFormat == BC_TYPE::kCode128C);
 }
 
 CBC_OnedCode128Writer::~CBC_OnedCode128Writer() = default;
 
 bool CBC_OnedCode128Writer::CheckContentValidity(WideStringView contents) {
-  for (const auto& ch : contents) {
-    int32_t patternIndex = static_cast<int32_t>(ch);
-    if (patternIndex < 32 || patternIndex > 126 || patternIndex == 34)
-      return false;
-  }
-  return true;
+  return HasValidContentSize(contents) &&
+         std::all_of(contents.begin(), contents.end(), IsInOnedCode128Alphabet);
 }
 
 WideString CBC_OnedCode128Writer::FilterContents(WideStringView contents) {
-  const wchar_t limit = m_codeFormat == BC_CODE128_B ? 126 : 106;
+  const wchar_t limit = m_codeFormat == BC_TYPE::kCode128B ? 126 : 106;
 
   WideString filtered;
   filtered.Reserve(contents.GetLength());
@@ -110,33 +114,17 @@
   return filtered;
 }
 
-bool CBC_OnedCode128Writer::SetTextLocation(BC_TEXT_LOC location) {
-  if (location < BC_TEXT_LOC_NONE || location > BC_TEXT_LOC_BELOWEMBED) {
-    return false;
-  }
+void CBC_OnedCode128Writer::SetTextLocation(BC_TEXT_LOC location) {
   m_locTextLoc = location;
-  return true;
 }
 
-uint8_t* CBC_OnedCode128Writer::EncodeWithHint(const ByteString& contents,
-                                               BCFORMAT format,
-                                               int32_t& outWidth,
-                                               int32_t& outHeight,
-                                               int32_t hints) {
-  if (format != BCFORMAT_CODE_128)
-    return nullptr;
-  return CBC_OneDimWriter::EncodeWithHint(contents, format, outWidth, outHeight,
-                                          hints);
-}
-
-uint8_t* CBC_OnedCode128Writer::EncodeImpl(const ByteString& contents,
-                                           int32_t& outLength) {
+DataVector<uint8_t> CBC_OnedCode128Writer::Encode(const ByteString& contents) {
   if (contents.GetLength() < 1 || contents.GetLength() > 80)
-    return nullptr;
+    return DataVector<uint8_t>();
 
   std::vector<int32_t> patterns;
   int32_t checkSum = 0;
-  if (m_codeFormat == BC_CODE128_B)
+  if (m_codeFormat == BC_TYPE::kCode128B)
     checkSum = Encode128B(contents, &patterns);
   else
     checkSum = Encode128C(contents, &patterns);
@@ -147,18 +135,17 @@
   m_iContentLen = contents.GetLength() + 3;
   int32_t codeWidth = 0;
   for (const auto& patternIndex : patterns) {
-    const int8_t* pattern = CODE_PATTERNS[patternIndex];
+    const uint8_t* pattern = kCodePatterns[patternIndex];
     for (size_t i = 0; i < kPatternSize; ++i)
       codeWidth += pattern[i];
   }
-  outLength = codeWidth;
-  std::unique_ptr<uint8_t, FxFreeDeleter> result(FX_Alloc(uint8_t, outLength));
-  int32_t pos = 0;
-  for (size_t i = 0; i < patterns.size(); ++i) {
-    const int8_t* pattern = CODE_PATTERNS[patterns[i]];
-    pos += AppendPattern(result.get(), pos, pattern, kPatternSize, true);
+  DataVector<uint8_t> result(codeWidth);
+  auto result_span = pdfium::make_span(result);
+  for (const int32_t pattern_index : patterns) {
+    const uint8_t* pattern = kCodePatterns[pattern_index];
+    result_span = AppendPattern(result_span, {pattern, kPatternSize}, true);
   }
-  return result.release();
+  return result;
 }
 
 // static
@@ -185,12 +172,12 @@
   while (position < contents.GetLength()) {
     int32_t patternIndex;
     char ch = contents[position];
-    if (std::isdigit(ch)) {
+    if (isdigit(ch)) {
       patternIndex = FXSYS_atoi(
           contents.Substr(position, contents.IsValidIndex(position + 1) ? 2 : 1)
               .c_str());
       ++position;
-      if (position < contents.GetLength() && std::isdigit(contents[position]))
+      if (position < contents.GetLength() && isdigit(contents[position]))
         ++position;
     } else {
       patternIndex = static_cast<int32_t>(ch);
diff --git a/fxbarcode/oned/BC_OnedCode128Writer.h b/fxbarcode/oned/BC_OnedCode128Writer.h
index 370c025..121c02b 100644
--- a/fxbarcode/oned/BC_OnedCode128Writer.h
+++ b/fxbarcode/oned/BC_OnedCode128Writer.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,12 @@
 #ifndef FXBARCODE_ONED_BC_ONEDCODE128WRITER_H_
 #define FXBARCODE_ONED_BC_ONEDCODE128WRITER_H_
 
+#include <stdint.h>
+
 #include <vector>
 
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "fxbarcode/BC_Library.h"
 #include "fxbarcode/oned/BC_OneDimWriter.h"
 
 class CBC_OnedCode128Writer final : public CBC_OneDimWriter {
@@ -25,15 +27,10 @@
                             std::vector<int32_t>* patterns);
 
   // CBC_OneDimWriter
-  uint8_t* EncodeWithHint(const ByteString& contents,
-                          BCFORMAT format,
-                          int32_t& outWidth,
-                          int32_t& outHeight,
-                          int32_t hints) override;
-  uint8_t* EncodeImpl(const ByteString& contents, int32_t& outLength) override;
+  DataVector<uint8_t> Encode(const ByteString& contents) override;
   bool CheckContentValidity(WideStringView contents) override;
   WideString FilterContents(WideStringView contents) override;
-  bool SetTextLocation(BC_TEXT_LOC location) override;
+  void SetTextLocation(BC_TEXT_LOC location) override;
 
   BC_TYPE GetType() const { return m_codeFormat; }
 
diff --git a/fxbarcode/oned/BC_OnedCode128Writer_unittest.cpp b/fxbarcode/oned/BC_OnedCode128Writer_unittest.cpp
index 19d91f9..ee1d9b1 100644
--- a/fxbarcode/oned/BC_OnedCode128Writer_unittest.cpp
+++ b/fxbarcode/oned/BC_OnedCode128Writer_unittest.cpp
@@ -1,9 +1,11 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxbarcode/oned/BC_OnedCode128Writer.h"
 
+#include <iterator>
+
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -31,7 +33,7 @@
       {"321ABC", 722, {104, 19, 18, 17, 33, 34, 35}, 7},
       {"XYZ", 448, {104, 56, 57, 58}, 4},
   };
-  for (size_t i = 0; i < FX_ArraySize(kTestCases); ++i) {
+  for (size_t i = 0; i < std::size(kTestCases); ++i) {
     FXSYS_snprintf(buf, sizeof(buf) - 1, "Test case %zu", i);
     SCOPED_TRACE(buf);
     const TestCase& test_case = kTestCases[i];
@@ -64,7 +66,7 @@
       {"321ABC", 933, {105, 32, 1, 65, 66, 67}, 6},
       {"XYZ", 641, {105, 88, 89, 90}, 4},
   };
-  for (size_t i = 0; i < FX_ArraySize(kTestCases); ++i) {
+  for (size_t i = 0; i < std::size(kTestCases); ++i) {
     FXSYS_snprintf(buf, sizeof(buf) - 1, "Test case %zu", i);
     SCOPED_TRACE(buf);
     const TestCase& test_case = kTestCases[i];
@@ -83,20 +85,20 @@
 
 TEST(OnedCode128WriterTest, CheckContentValidity) {
   {
-    CBC_OnedCode128Writer writer(BC_CODE128_B);
-    EXPECT_TRUE(writer.CheckContentValidity(L""));
+    CBC_OnedCode128Writer writer(BC_TYPE::kCode128B);
     EXPECT_TRUE(writer.CheckContentValidity(L"foo"));
     EXPECT_TRUE(writer.CheckContentValidity(L"xyz"));
+    EXPECT_FALSE(writer.CheckContentValidity(L""));
     EXPECT_FALSE(writer.CheckContentValidity(L"\""));
     EXPECT_FALSE(writer.CheckContentValidity(L"f\x10oo"));
     EXPECT_FALSE(writer.CheckContentValidity(L"bar\x7F"));
     EXPECT_FALSE(writer.CheckContentValidity(L"qux\x88"));
   }
   {
-    CBC_OnedCode128Writer writer(BC_CODE128_C);
-    EXPECT_TRUE(writer.CheckContentValidity(L""));
+    CBC_OnedCode128Writer writer(BC_TYPE::kCode128C);
     EXPECT_TRUE(writer.CheckContentValidity(L"foo"));
     EXPECT_TRUE(writer.CheckContentValidity(L"xyz"));
+    EXPECT_FALSE(writer.CheckContentValidity(L""));
     EXPECT_FALSE(writer.CheckContentValidity(L"\""));
     EXPECT_FALSE(writer.CheckContentValidity(L"f\x10oo"));
     EXPECT_FALSE(writer.CheckContentValidity(L"bar\x7F"));
@@ -106,7 +108,7 @@
 
 TEST(OnedCode128WriterTest, FilterContents) {
   {
-    CBC_OnedCode128Writer writer(BC_CODE128_B);
+    CBC_OnedCode128Writer writer(BC_TYPE::kCode128B);
     EXPECT_STREQ(L"", writer.FilterContents(L"").c_str());
     EXPECT_STREQ(L"foo", writer.FilterContents(L"foo\x10").c_str());
     EXPECT_STREQ(L"fool", writer.FilterContents(L"foo\x10l").c_str());
@@ -115,7 +117,7 @@
     EXPECT_STREQ(L"bar", writer.FilterContents(L"bar\x10\x7F\x88").c_str());
   }
   {
-    CBC_OnedCode128Writer writer(BC_CODE128_C);
+    CBC_OnedCode128Writer writer(BC_TYPE::kCode128C);
     EXPECT_STREQ(L"", writer.FilterContents(L"").c_str());
     EXPECT_STREQ(L"f", writer.FilterContents(L"foo\x10").c_str());
     EXPECT_STREQ(L"f", writer.FilterContents(L"foo\x10l").c_str());
diff --git a/fxbarcode/oned/BC_OnedCode39Writer.cpp b/fxbarcode/oned/BC_OnedCode39Writer.cpp
index 97ee881..2f781c2 100644
--- a/fxbarcode/oned/BC_OnedCode39Writer.cpp
+++ b/fxbarcode/oned/BC_OnedCode39Writer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,6 +23,7 @@
 #include "fxbarcode/oned/BC_OnedCode39Writer.h"
 
 #include <algorithm>
+#include <iterator>
 #include <memory>
 
 #include "core/fxcrt/fx_extension.h"
@@ -37,13 +38,13 @@
     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
     'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
     'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '*', '$', '/', '+', '%'};
-constexpr size_t kOnedCode39AlphabetLen = FX_ArraySize(kOnedCode39Alphabet);
+constexpr size_t kOnedCode39AlphabetLen = std::size(kOnedCode39Alphabet);
 
 const char kOnedCode39Checksum[] = {
     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
     'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
     'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%'};
-static_assert(FX_ArraySize(kOnedCode39Checksum) == 43, "Wrong size");
+static_assert(std::size(kOnedCode39Checksum) == 43, "Wrong size");
 
 const int16_t kOnedCode39CharacterEncoding[] = {
     0x0034, 0x0121, 0x0061, 0x0160, 0x0031, 0x0130, 0x0070, 0x0025, 0x0124,
@@ -51,7 +52,7 @@
     0x004C, 0x001C, 0x0103, 0x0043, 0x0142, 0x0013, 0x0112, 0x0052, 0x0007,
     0x0106, 0x0046, 0x0016, 0x0181, 0x00C1, 0x01C0, 0x0091, 0x0190, 0x00D0,
     0x0085, 0x0184, 0x00C4, 0x0094, 0x00A8, 0x00A2, 0x008A, 0x002A};
-static_assert(FX_ArraySize(kOnedCode39CharacterEncoding) == 44, "Wrong size");
+static_assert(std::size(kOnedCode39CharacterEncoding) == 44, "Wrong size");
 
 bool IsInOnedCode39Alphabet(wchar_t ch) {
   return FXSYS_IsDecimalDigit(ch) || (ch >= L'A' && ch <= L'Z') || ch == L'-' ||
@@ -59,6 +60,26 @@
          ch == L'+' || ch == L'%';
 }
 
+char CalcCheckSum(const ByteString& contents) {
+  if (contents.GetLength() > 80)
+    return '*';
+
+  int32_t checksum = 0;
+  for (const auto& c : contents) {
+    size_t j = 0;
+    for (; j < kOnedCode39AlphabetLen; j++) {
+      if (kOnedCode39Alphabet[j] == c) {
+        if (c != '*')
+          checksum += j;
+        break;
+      }
+    }
+    if (j >= kOnedCode39AlphabetLen)
+      return '*';
+  }
+  return kOnedCode39Checksum[checksum % std::size(kOnedCode39Checksum)];
+}
+
 }  // namespace
 
 CBC_OnedCode39Writer::CBC_OnedCode39Writer() = default;
@@ -66,7 +87,8 @@
 CBC_OnedCode39Writer::~CBC_OnedCode39Writer() = default;
 
 bool CBC_OnedCode39Writer::CheckContentValidity(WideStringView contents) {
-  return std::all_of(contents.begin(), contents.end(), IsInOnedCode39Alphabet);
+  return HasValidContentSize(contents) &&
+         std::all_of(contents.begin(), contents.end(), IsInOnedCode39Alphabet);
 }
 
 WideString CBC_OnedCode39Writer::FilterContents(WideStringView contents) {
@@ -105,13 +127,10 @@
   return renderContents;
 }
 
-bool CBC_OnedCode39Writer::SetTextLocation(BC_TEXT_LOC location) {
-  if (location < BC_TEXT_LOC_NONE || location > BC_TEXT_LOC_BELOWEMBED) {
-    return false;
-  }
+void CBC_OnedCode39Writer::SetTextLocation(BC_TEXT_LOC location) {
   m_locTextLoc = location;
-  return true;
 }
+
 bool CBC_OnedCode39Writer::SetWideNarrowRatio(int8_t ratio) {
   if (ratio < 2 || ratio > 3)
     return false;
@@ -120,75 +139,45 @@
   return true;
 }
 
-uint8_t* CBC_OnedCode39Writer::EncodeWithHint(const ByteString& contents,
-                                              BCFORMAT format,
-                                              int32_t& outWidth,
-                                              int32_t& outHeight,
-                                              int32_t hints) {
-  if (format != BCFORMAT_CODE_39)
-    return nullptr;
-  return CBC_OneDimWriter::EncodeWithHint(contents, format, outWidth, outHeight,
-                                          hints);
-}
-
-void CBC_OnedCode39Writer::ToIntArray(int16_t a, int8_t* toReturn) {
-  for (int32_t i = 0; i < 9; i++) {
-    toReturn[i] = (a & (1 << i)) == 0 ? 1 : m_iWideNarrRatio;
+void CBC_OnedCode39Writer::ToIntArray(int16_t value,
+                                      uint8_t array[kArraySize]) const {
+  for (size_t i = 0; i < kArraySize; i++) {
+    array[i] = (value & (1 << i)) == 0 ? 1 : m_iWideNarrRatio;
   }
 }
 
-char CBC_OnedCode39Writer::CalcCheckSum(const ByteString& contents) {
-  if (contents.GetLength() > 80)
-    return '*';
-
-  int32_t checksum = 0;
-  for (const auto& c : contents) {
-    size_t j = 0;
-    for (; j < kOnedCode39AlphabetLen; j++) {
-      if (kOnedCode39Alphabet[j] == c) {
-        if (c != '*')
-          checksum += j;
-        break;
-      }
-    }
-    if (j >= kOnedCode39AlphabetLen)
-      return '*';
-  }
-  return kOnedCode39Checksum[checksum % FX_ArraySize(kOnedCode39Checksum)];
-}
-
-uint8_t* CBC_OnedCode39Writer::EncodeImpl(const ByteString& contents,
-                                          int32_t& outlength) {
+DataVector<uint8_t> CBC_OnedCode39Writer::Encode(const ByteString& contents) {
   char checksum = CalcCheckSum(contents);
   if (checksum == '*')
-    return nullptr;
+    return DataVector<uint8_t>();
 
-  int8_t widths[9] = {0};
-  int32_t wideStrideNum = 3;
-  int32_t narrStrideNum = 9 - wideStrideNum;
+  uint8_t widths[kArraySize] = {0};
+  constexpr int32_t kWideStrideNum = 3;
+  constexpr int32_t kNarrowStrideNum = kArraySize - kWideStrideNum;
   ByteString encodedContents = contents;
   if (m_bCalcChecksum)
     encodedContents += checksum;
   m_iContentLen = encodedContents.GetLength();
-  int32_t codeWidth = (wideStrideNum * m_iWideNarrRatio + narrStrideNum) * 2 +
-                      1 + m_iContentLen;
+  size_t code_width =
+      (kWideStrideNum * m_iWideNarrRatio + kNarrowStrideNum) * 2 + 1 +
+      m_iContentLen;
   for (size_t j = 0; j < m_iContentLen; j++) {
     for (size_t i = 0; i < kOnedCode39AlphabetLen; i++) {
       if (kOnedCode39Alphabet[i] != encodedContents[j])
         continue;
 
       ToIntArray(kOnedCode39CharacterEncoding[i], widths);
-      for (size_t k = 0; k < 9; k++)
-        codeWidth += widths[k];
+      for (size_t k = 0; k < kArraySize; k++)
+        code_width += widths[k];
     }
   }
-  outlength = codeWidth;
-  std::unique_ptr<uint8_t, FxFreeDeleter> result(FX_Alloc(uint8_t, codeWidth));
+  DataVector<uint8_t> result(code_width);
+  auto result_span = pdfium::make_span(result);
   ToIntArray(kOnedCode39CharacterEncoding[39], widths);
-  int32_t pos = AppendPattern(result.get(), 0, widths, 9, true);
+  result_span = AppendPattern(result_span, widths, true);
 
-  int8_t narrowWhite[] = {1};
-  pos += AppendPattern(result.get(), pos, narrowWhite, 1, false);
+  static constexpr uint8_t kNarrowWhite[] = {1};
+  result_span = AppendPattern(result_span, kNarrowWhite, false);
 
   for (int32_t l = m_iContentLen - 1; l >= 0; l--) {
     for (size_t i = 0; i < kOnedCode39AlphabetLen; i++) {
@@ -196,20 +185,19 @@
         continue;
 
       ToIntArray(kOnedCode39CharacterEncoding[i], widths);
-      pos += AppendPattern(result.get(), pos, widths, 9, true);
+      result_span = AppendPattern(result_span, widths, true);
     }
-    pos += AppendPattern(result.get(), pos, narrowWhite, 1, false);
+    result_span = AppendPattern(result_span, kNarrowWhite, false);
   }
   ToIntArray(kOnedCode39CharacterEncoding[39], widths);
-  pos += AppendPattern(result.get(), pos, widths, 9, true);
+  AppendPattern(result_span, widths, true);
 
-  auto* result_ptr = result.get();
-  for (int32_t i = 0; i < codeWidth / 2; i++) {
-    result_ptr[i] ^= result_ptr[codeWidth - 1 - i];
-    result_ptr[codeWidth - 1 - i] ^= result_ptr[i];
-    result_ptr[i] ^= result_ptr[codeWidth - 1 - i];
+  for (size_t i = 0; i < code_width / 2; i++) {
+    result[i] ^= result[code_width - 1 - i];
+    result[code_width - 1 - i] ^= result[i];
+    result[i] ^= result[code_width - 1 - i];
   }
-  return result.release();
+  return result;
 }
 
 bool CBC_OnedCode39Writer::encodedContents(WideStringView contents,
@@ -229,11 +217,9 @@
 }
 
 bool CBC_OnedCode39Writer::RenderResult(WideStringView contents,
-                                        uint8_t* code,
-                                        int32_t codeLength) {
+                                        pdfium::span<const uint8_t> code) {
   WideString encodedCon;
   if (!encodedContents(contents, &encodedCon))
     return false;
-  return CBC_OneDimWriter::RenderResult(encodedCon.AsStringView(), code,
-                                        codeLength);
+  return CBC_OneDimWriter::RenderResult(encodedCon.AsStringView(), code);
 }
diff --git a/fxbarcode/oned/BC_OnedCode39Writer.h b/fxbarcode/oned/BC_OnedCode39Writer.h
index 525573a..932d703 100644
--- a/fxbarcode/oned/BC_OnedCode39Writer.h
+++ b/fxbarcode/oned/BC_OnedCode39Writer.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,8 @@
 #ifndef FXBARCODE_ONED_BC_ONEDCODE39WRITER_H_
 #define FXBARCODE_ONED_BC_ONEDCODE39WRITER_H_
 
+#include <stddef.h>
+
 #include "fxbarcode/BC_Library.h"
 #include "fxbarcode/oned/BC_OneDimWriter.h"
 
@@ -16,26 +18,21 @@
   ~CBC_OnedCode39Writer() override;
 
   // CBC_OneDimWriter
-  uint8_t* EncodeWithHint(const ByteString& contents,
-                          BCFORMAT format,
-                          int32_t& outWidth,
-                          int32_t& outHeight,
-                          int32_t hints) override;
-  uint8_t* EncodeImpl(const ByteString& contents, int32_t& outLength) override;
+  DataVector<uint8_t> Encode(const ByteString& contents) override;
   bool RenderResult(WideStringView contents,
-                    uint8_t* code,
-                    int32_t codeLength) override;
+                    pdfium::span<const uint8_t> code) override;
   bool CheckContentValidity(WideStringView contents) override;
   WideString FilterContents(WideStringView contents) override;
-  bool SetTextLocation(BC_TEXT_LOC location) override;
+  void SetTextLocation(BC_TEXT_LOC location) override;
   bool SetWideNarrowRatio(int8_t ratio) override;
 
   WideString RenderTextContents(WideStringView contents);
   bool encodedContents(WideStringView contents, WideString* result);
 
  private:
-  void ToIntArray(int16_t a, int8_t* toReturn);
-  char CalcCheckSum(const ByteString& contents);
+  static constexpr size_t kArraySize = 9;
+
+  void ToIntArray(int16_t value, uint8_t array[kArraySize]) const;
 
   int8_t m_iWideNarrRatio = 3;
 };
diff --git a/fxbarcode/oned/BC_OnedCode39Writer_unittest.cpp b/fxbarcode/oned/BC_OnedCode39Writer_unittest.cpp
index e263d59..b0645f4 100644
--- a/fxbarcode/oned/BC_OnedCode39Writer_unittest.cpp
+++ b/fxbarcode/oned/BC_OnedCode39Writer_unittest.cpp
@@ -1,24 +1,16 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxbarcode/oned/BC_OnedCode39Writer.h"
 
-#include <cstring>
+#include <string.h>
 
+#include "core/fxcrt/data_vector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
-// 3 wide and 6 narrow modules per char. 1 space between chars.
-constexpr int MODULES_PER_CHAR = 3 * 3 + 6 + 1;
-
-// '*' is added as the first and last char.
-const int DELIMITER_CHARS = 2;
-
-// Last char may serve as checksum.
-const int CHECKSUM_CHARS = 1;
-
 TEST(OnedCode39WriterTest, SetWideNarrowRatio) {
   // Code 39 barcodes encode strings of any size into modules in a
   // unidimensional disposition.
@@ -35,13 +27,7 @@
 
   writer.SetWideNarrowRatio(3);
 
-  int32_t width;
-  int32_t height;
-  uint8_t* encoded;
-  const char* expected;
-
-  encoded = writer.Encode("PDFIUM", BCFORMAT_CODE_39, width, height);
-  expected =
+  static const char kExpected1[] =
       "#   # ### ### # "  // * Start
       "# ### ### #   # "  // P
       "# # ###   # ### "  // D
@@ -50,14 +36,14 @@
       "###   # # # ### "  // U
       "### ### # #   # "  // M
       "#   # ### ### #";  // * End
-  for (size_t i = 0; i < strlen(expected); i++)
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  FX_Free(encoded);
+  DataVector<uint8_t> encoded = writer.Encode("PDFIUM");
+  ASSERT_EQ(strlen(kExpected1), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected1); i++)
+    EXPECT_EQ(kExpected1[i] != ' ', !!encoded[i]) << i;
 
   writer.SetWideNarrowRatio(2);
 
-  encoded = writer.Encode("PDFIUM", BCFORMAT_CODE_39, width, height);
-  expected =
+  static const char kExpected2[] =
       "#  # ## ## # "  // * Start
       "# ## ## #  # "  // P
       "# # ##  # ## "  // D
@@ -66,45 +52,35 @@
       "##  # # # ## "  // U
       "## ## # #  # "  // M
       "#  # ## ## #";  // * End
-  for (size_t i = 0; i < strlen(expected); i++)
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  FX_Free(encoded);
+  encoded = writer.Encode("PDFIUM");
+  ASSERT_EQ(strlen(kExpected2), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected2); i++)
+    EXPECT_EQ(kExpected2[i] != ' ', !!encoded[i]) << i;
 }
 
 TEST(OnedCode39WriterTest, Encode) {
   CBC_OnedCode39Writer writer;
-  int32_t width;
-  int32_t height;
-  uint8_t* encoded;
-  const char* expected;
 
-  encoded = writer.Encode("", BCFORMAT_CODE_39, width, height);
-  EXPECT_EQ(1, height);
-  EXPECT_EQ((0 + DELIMITER_CHARS) * MODULES_PER_CHAR - 1, width);
-  expected =
+  static const char kExpected1[] =
       "#   # ### ### # "  // * Start
       "#   # ### ### #";  // * End
-  for (size_t i = 0; i < strlen(expected); i++)
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  FX_Free(encoded);
+  DataVector<uint8_t> encoded = writer.Encode("");
+  ASSERT_EQ(strlen(kExpected1), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected1); i++)
+    EXPECT_EQ(kExpected1[i] != ' ', !!encoded[i]) << i;
 
-  encoded = writer.Encode("123", BCFORMAT_CODE_39, width, height);
-  EXPECT_EQ(1, height);
-  EXPECT_EQ((3 + DELIMITER_CHARS) * MODULES_PER_CHAR - 1, width);
-  expected =
+  static const char kExpected2[] =
       "#   # ### ### # "  // * Start
       "### #   # # ### "  // 1
       "# ###   # # ### "  // 2
       "### ###   # # # "  // 3
       "#   # ### ### #";  // * End
-  for (size_t i = 0; i < strlen(expected); i++)
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  FX_Free(encoded);
+  encoded = writer.Encode("123");
+  ASSERT_EQ(strlen(kExpected2), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected2); i++)
+    EXPECT_EQ(kExpected2[i] != ' ', !!encoded[i]) << i;
 
-  encoded = writer.Encode("PDFIUM", BCFORMAT_CODE_39, width, height);
-  EXPECT_EQ(1, height);
-  EXPECT_EQ((6 + DELIMITER_CHARS) * MODULES_PER_CHAR - 1, width);
-  expected =
+  static const char kExpected3[] =
       "#   # ### ### # "  // * Start
       "# ### ### #   # "  // P
       "# # ###   # ### "  // D
@@ -113,14 +89,12 @@
       "###   # # # ### "  // U
       "### ### # #   # "  // M
       "#   # ### ### #";  // * End
-  for (size_t i = 0; i < strlen(expected); i++)
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  FX_Free(encoded);
+  encoded = writer.Encode("PDFIUM");
+  ASSERT_EQ(strlen(kExpected3), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected3); i++)
+    EXPECT_EQ(kExpected3[i] != ' ', !!encoded[i]) << i;
 
-  encoded = writer.Encode("A -$%./+Z", BCFORMAT_CODE_39, width, height);
-  EXPECT_EQ(1, height);
-  EXPECT_EQ((9 + DELIMITER_CHARS) * MODULES_PER_CHAR - 1, width);
-  expected =
+  static const char kExpected4[] =
       "#   # ### ### # "  // * Start
       "### # #   # ### "  // A
       "#   ### # ### # "  // Space
@@ -132,40 +106,29 @@
       "#   # #   #   # "  // +
       "#   ### ### # # "  // Z
       "#   # ### ### #";  // * End
-  for (size_t i = 0; i < strlen(expected); i++)
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  FX_Free(encoded);
+  encoded = writer.Encode("A -$%./+Z");
+  ASSERT_EQ(strlen(kExpected4), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected4); i++)
+    EXPECT_EQ(kExpected4[i] != ' ', !!encoded[i]) << i;
 }
 
 TEST(OnedCode39WriterTest, Checksum) {
   CBC_OnedCode39Writer writer;
-  int32_t width;
-  int32_t height;
-  uint8_t* encoded;
-  const char* expected;
-
   writer.SetCalcChecksum(true);
 
-  encoded = writer.Encode("123", BCFORMAT_CODE_39, width, height);
-  EXPECT_EQ(1, height);
-  EXPECT_EQ((3 + CHECKSUM_CHARS + DELIMITER_CHARS) * MODULES_PER_CHAR - 1,
-            width);
-  expected =
+  static const char kExpected1[] =
       "#   # ### ### # "  // * Start
       "### #   # # ### "  // 1 (1)
       "# ###   # # ### "  // 2 (2)
       "### ###   # # # "  // 3 (3)
       "# ###   ### # # "  // 6 (6 = (1 + 2 + 3) % 43)
       "#   # ### ### #";  // * End
-  for (size_t i = 0; i < strlen(expected); i++)
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  FX_Free(encoded);
+  DataVector<uint8_t> encoded = writer.Encode("123");
+  ASSERT_EQ(strlen(kExpected1), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected1); i++)
+    EXPECT_EQ(kExpected1[i] != ' ', !!encoded[i]) << i;
 
-  encoded = writer.Encode("PDFIUM", BCFORMAT_CODE_39, width, height);
-  EXPECT_EQ(1, height);
-  EXPECT_EQ((6 + CHECKSUM_CHARS + DELIMITER_CHARS) * MODULES_PER_CHAR - 1,
-            width);
-  expected =
+  static const char kExpected2[] =
       "#   # ### ### # "  // * Start
       "# ### ### #   # "  // P (25)
       "# # ###   # ### "  // D (13)
@@ -175,9 +138,10 @@
       "### ### # #   # "  // M (22)
       "###   # # ### # "  // . (37 = (25 + 13 + 15 + 18 + 30 + 22) % 43)
       "#   # ### ### #";  // * End
-  for (size_t i = 0; i < strlen(expected); i++)
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  FX_Free(encoded);
+  encoded = writer.Encode("PDFIUM");
+  ASSERT_EQ(strlen(kExpected2), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected2); i++)
+    EXPECT_EQ(kExpected2[i] != ' ', !!encoded[i]) << i;
 }
 
 }  // namespace
diff --git a/fxbarcode/oned/BC_OnedEAN13Writer.cpp b/fxbarcode/oned/BC_OnedEAN13Writer.cpp
index f2e88dd..325d424 100644
--- a/fxbarcode/oned/BC_OnedEAN13Writer.cpp
+++ b/fxbarcode/oned/BC_OnedEAN13Writer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,8 +22,9 @@
 
 #include "fxbarcode/oned/BC_OnedEAN13Writer.h"
 
+#include <math.h>
+
 #include <algorithm>
-#include <cwctype>
 #include <memory>
 #include <vector>
 
@@ -39,12 +40,12 @@
 
 const int8_t kFirstDigitEncodings[10] = {0x00, 0x0B, 0x0D, 0xE,  0x13,
                                          0x19, 0x1C, 0x15, 0x16, 0x1A};
-const int8_t kOnedEAN13StartPattern[3] = {1, 1, 1};
-const int8_t kOnedEAN13MiddlePattern[5] = {1, 1, 1, 1, 1};
-const int8_t kOnedEAN13LPattern[10][4] = {
+const uint8_t kOnedEAN13StartPattern[3] = {1, 1, 1};
+const uint8_t kOnedEAN13MiddlePattern[5] = {1, 1, 1, 1, 1};
+const uint8_t kOnedEAN13LPattern[10][4] = {
     {3, 2, 1, 1}, {2, 2, 2, 1}, {2, 1, 2, 2}, {1, 4, 1, 1}, {1, 1, 3, 2},
     {1, 2, 3, 1}, {1, 1, 1, 4}, {1, 3, 1, 2}, {1, 2, 1, 3}, {3, 1, 1, 2}};
-const int8_t L_AND_G_PATTERNS[20][4] = {
+const uint8_t kOnedEAN13LGPattern[20][4] = {
     {3, 2, 1, 1}, {2, 2, 2, 1}, {2, 1, 2, 2}, {1, 4, 1, 1}, {1, 1, 3, 2},
     {1, 2, 3, 1}, {1, 1, 1, 4}, {1, 3, 1, 2}, {1, 2, 1, 3}, {3, 1, 1, 2},
     {1, 1, 2, 3}, {1, 2, 2, 2}, {2, 2, 1, 2}, {1, 1, 4, 1}, {2, 3, 1, 1},
@@ -56,10 +57,11 @@
   m_bLeftPadding = true;
   m_codeWidth = 3 + (7 * 6) + 5 + (7 * 6) + 3;
 }
-CBC_OnedEAN13Writer::~CBC_OnedEAN13Writer() {}
+CBC_OnedEAN13Writer::~CBC_OnedEAN13Writer() = default;
 
 bool CBC_OnedEAN13Writer::CheckContentValidity(WideStringView contents) {
-  return std::all_of(contents.begin(), contents.end(),
+  return HasValidContentSize(contents) &&
+         std::all_of(contents.begin(), contents.end(),
                      [](wchar_t c) { return FXSYS_IsDecimalDigit(c); });
 }
 
@@ -83,134 +85,110 @@
   return EANCalcChecksum(contents);
 }
 
-uint8_t* CBC_OnedEAN13Writer::EncodeWithHint(const ByteString& contents,
-                                             BCFORMAT format,
-                                             int32_t& outWidth,
-                                             int32_t& outHeight,
-                                             int32_t hints) {
-  if (format != BCFORMAT_EAN_13)
-    return nullptr;
-  return CBC_OneDimWriter::EncodeWithHint(contents, format, outWidth, outHeight,
-                                          hints);
-}
-
-uint8_t* CBC_OnedEAN13Writer::EncodeImpl(const ByteString& contents,
-                                         int32_t& outLength) {
+DataVector<uint8_t> CBC_OnedEAN13Writer::Encode(const ByteString& contents) {
   if (contents.GetLength() != 13)
-    return nullptr;
+    return DataVector<uint8_t>();
 
   m_iDataLenth = 13;
   int32_t firstDigit = FXSYS_DecimalCharToInt(contents.Front());
   int32_t parities = kFirstDigitEncodings[firstDigit];
-  outLength = m_codeWidth;
-  std::unique_ptr<uint8_t, FxFreeDeleter> result(
-      FX_Alloc(uint8_t, m_codeWidth));
-  int32_t pos = 0;
-  pos += AppendPattern(result.get(), pos, kOnedEAN13StartPattern, 3, true);
+  DataVector<uint8_t> result(m_codeWidth);
+  auto result_span = pdfium::make_span(result);
+  result_span = AppendPattern(result_span, kOnedEAN13StartPattern, true);
 
-  int32_t i = 0;
-  for (i = 1; i <= 6; i++) {
+  for (int i = 1; i <= 6; i++) {
     int32_t digit = FXSYS_DecimalCharToInt(contents[i]);
     if ((parities >> (6 - i) & 1) == 1) {
       digit += 10;
     }
-    pos += AppendPattern(result.get(), pos, L_AND_G_PATTERNS[digit], 4, false);
+    result_span = AppendPattern(result_span, kOnedEAN13LGPattern[digit], false);
   }
-  pos += AppendPattern(result.get(), pos, kOnedEAN13MiddlePattern, 5, false);
+  result_span = AppendPattern(result_span, kOnedEAN13MiddlePattern, false);
 
-  for (i = 7; i <= 12; i++) {
+  for (int i = 7; i <= 12; i++) {
     int32_t digit = FXSYS_DecimalCharToInt(contents[i]);
-    pos += AppendPattern(result.get(), pos, kOnedEAN13LPattern[digit], 4, true);
+    result_span = AppendPattern(result_span, kOnedEAN13LPattern[digit], true);
   }
-  pos += AppendPattern(result.get(), pos, kOnedEAN13StartPattern, 3, true);
-  return result.release();
+  AppendPattern(result_span, kOnedEAN13StartPattern, true);
+  return result;
 }
 
 bool CBC_OnedEAN13Writer::ShowChars(WideStringView contents,
                                     CFX_RenderDevice* device,
-                                    const CFX_Matrix* matrix,
-                                    int32_t barWidth,
-                                    int32_t multiple) {
+                                    const CFX_Matrix& matrix,
+                                    int32_t barWidth) {
   if (!device)
     return false;
 
-  int32_t leftPadding = 7 * multiple;
-  int32_t leftPosition = 3 * multiple + leftPadding;
+  constexpr float kLeftPosition = 10.0f;
   ByteString str = FX_UTF8Encode(contents);
   size_t length = str.GetLength();
   std::vector<TextCharPos> charpos(length);
-  int32_t iFontSize = (int32_t)fabs(m_fFontSize);
+  int32_t iFontSize = static_cast<int32_t>(fabs(m_fFontSize));
   int32_t iTextHeight = iFontSize + 1;
   ByteString tempStr = str.Substr(1, 6);
-  int32_t strWidth = multiple * 42;
+  constexpr int32_t kWidth = 42;
 
   CFX_Matrix matr(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0);
-  CFX_FloatRect rect((float)leftPosition, (float)(m_Height - iTextHeight),
-                     (float)(leftPosition + strWidth - 0.5), (float)m_Height);
-  matr.Concat(*matrix);
+  CFX_FloatRect rect(kLeftPosition, (float)(m_Height - iTextHeight),
+                     kLeftPosition + kWidth - 0.5, (float)m_Height);
+  matr.Concat(matrix);
   FX_RECT re = matr.TransformRect(rect).GetOuterRect();
   device->FillRect(re, kBackgroundColor);
-  CFX_FloatRect rect1(
-      (float)(leftPosition + 47 * multiple), (float)(m_Height - iTextHeight),
-      (float)(leftPosition + 47 * multiple + strWidth - 0.5), (float)m_Height);
+  CFX_FloatRect rect1(kLeftPosition + 47, (float)(m_Height - iTextHeight),
+                      kLeftPosition + 47 + kWidth - 0.5, (float)m_Height);
   CFX_Matrix matr1(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0);
-  matr1.Concat(*matrix);
+  matr1.Concat(matrix);
   re = matr1.TransformRect(rect1).GetOuterRect();
   device->FillRect(re, kBackgroundColor);
-  int32_t strWidth1 = multiple * 7;
   CFX_Matrix matr2(m_outputHScale, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
-  CFX_FloatRect rect2(0.0f, (float)(m_Height - iTextHeight),
-                      (float)strWidth1 - 0.5f, (float)m_Height);
-  matr2.Concat(*matrix);
+  CFX_FloatRect rect2(0.0f, (float)(m_Height - iTextHeight), 6.5f,
+                      (float)m_Height);
+  matr2.Concat(matrix);
   re = matr2.TransformRect(rect2).GetOuterRect();
   device->FillRect(re, kBackgroundColor);
 
-  float blank = 0.0;
+  float blank = 0.0f;
   length = tempStr.GetLength();
-  strWidth = (int32_t)(strWidth * m_outputHScale);
+  int32_t strWidth = static_cast<int32_t>(kWidth * m_outputHScale);
 
-  CalcTextInfo(tempStr, &charpos[1], m_pFont.Get(), (float)strWidth, iFontSize,
+  CalcTextInfo(tempStr, &charpos[1], m_pFont, (float)strWidth, iFontSize,
                blank);
   {
     CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0,
-                              (float)leftPosition * m_outputHScale,
+                              kLeftPosition * m_outputHScale,
                               (float)(m_Height - iTextHeight) + iFontSize);
-    if (matrix)
-      affine_matrix1.Concat(*matrix);
-    device->DrawNormalText(length, &charpos[1], m_pFont.Get(),
-                           static_cast<float>(iFontSize), affine_matrix1,
-                           m_fontColor, FXTEXT_CLEARTYPE);
+    affine_matrix1.Concat(matrix);
+    device->DrawNormalText(pdfium::make_span(charpos).subspan(1, length),
+                           m_pFont, static_cast<float>(iFontSize),
+                           affine_matrix1, m_fontColor, GetTextRenderOptions());
   }
   tempStr = str.Substr(7, 6);
   length = tempStr.GetLength();
-  CalcTextInfo(tempStr, &charpos[7], m_pFont.Get(), (float)strWidth, iFontSize,
+  CalcTextInfo(tempStr, &charpos[7], m_pFont, (float)strWidth, iFontSize,
                blank);
   {
-    CFX_Matrix affine_matrix1(
-        1.0, 0.0, 0.0, -1.0,
-        (float)(leftPosition + 47 * multiple) * m_outputHScale,
-        (float)(m_Height - iTextHeight + iFontSize));
-    if (matrix)
-      affine_matrix1.Concat(*matrix);
-    device->DrawNormalText(length, &charpos[7], m_pFont.Get(),
-                           static_cast<float>(iFontSize), affine_matrix1,
-                           m_fontColor, FXTEXT_CLEARTYPE);
+    CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0,
+                              (kLeftPosition + 47) * m_outputHScale,
+                              (float)(m_Height - iTextHeight + iFontSize));
+    affine_matrix1.Concat(matrix);
+    device->DrawNormalText(pdfium::make_span(charpos).subspan(7, length),
+                           m_pFont, static_cast<float>(iFontSize),
+                           affine_matrix1, m_fontColor, GetTextRenderOptions());
   }
   tempStr = str.First(1);
   length = tempStr.GetLength();
-  strWidth = multiple * 7;
-  strWidth = (int32_t)(strWidth * m_outputHScale);
+  strWidth = 7 * static_cast<int32_t>(strWidth * m_outputHScale);
 
-  CalcTextInfo(tempStr, charpos.data(), m_pFont.Get(), (float)strWidth,
-               iFontSize, blank);
+  CalcTextInfo(tempStr, charpos.data(), m_pFont, (float)strWidth, iFontSize,
+               blank);
   {
     CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0, 0.0,
                               (float)(m_Height - iTextHeight + iFontSize));
-    if (matrix)
-      affine_matrix1.Concat(*matrix);
-    device->DrawNormalText(length, charpos.data(), m_pFont.Get(),
+    affine_matrix1.Concat(matrix);
+    device->DrawNormalText(pdfium::make_span(charpos).first(length), m_pFont,
                            static_cast<float>(iFontSize), affine_matrix1,
-                           m_fontColor, FXTEXT_CLEARTYPE);
+                           m_fontColor, GetTextRenderOptions());
   }
   return true;
 }
diff --git a/fxbarcode/oned/BC_OnedEAN13Writer.h b/fxbarcode/oned/BC_OnedEAN13Writer.h
index 88fa9cb..8c7ff1c 100644
--- a/fxbarcode/oned/BC_OnedEAN13Writer.h
+++ b/fxbarcode/oned/BC_OnedEAN13Writer.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,12 @@
 #ifndef FXBARCODE_ONED_BC_ONEDEAN13WRITER_H_
 #define FXBARCODE_ONED_BC_ONEDEAN13WRITER_H_
 
+#include <stdint.h>
+
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "fxbarcode/BC_Library.h"
 #include "fxbarcode/oned/BC_OnedEANWriter.h"
 
-class CFX_DIBitmap;
 class CFX_RenderDevice;
 
 class CBC_OnedEAN13Writer final : public CBC_OneDimEANWriter {
@@ -20,12 +21,7 @@
   ~CBC_OnedEAN13Writer() override;
 
   // CBC_OneDimEANWriter:
-  uint8_t* EncodeWithHint(const ByteString& contents,
-                          BCFORMAT format,
-                          int32_t& outWidth,
-                          int32_t& outHeight,
-                          int32_t hints) override;
-  uint8_t* EncodeImpl(const ByteString& contents, int32_t& outLength) override;
+  DataVector<uint8_t> Encode(const ByteString& contents) override;
   bool CheckContentValidity(WideStringView contents) override;
   WideString FilterContents(WideStringView contents) override;
   int32_t CalcChecksum(const ByteString& contents) override;
@@ -33,9 +29,8 @@
  private:
   bool ShowChars(WideStringView contents,
                  CFX_RenderDevice* device,
-                 const CFX_Matrix* matrix,
-                 int32_t barWidth,
-                 int32_t multiple) override;
+                 const CFX_Matrix& matrix,
+                 int32_t barWidth) override;
 
   int32_t m_codeWidth;
 };
diff --git a/fxbarcode/oned/BC_OnedEAN13Writer_unittest.cpp b/fxbarcode/oned/BC_OnedEAN13Writer_unittest.cpp
index 55746ba..e4119e5 100644
--- a/fxbarcode/oned/BC_OnedEAN13Writer_unittest.cpp
+++ b/fxbarcode/oned/BC_OnedEAN13Writer_unittest.cpp
@@ -1,9 +1,12 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxbarcode/oned/BC_OnedEAN13Writer.h"
 
+#include <string.h>
+
+#include "core/fxcrt/data_vector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -11,35 +14,15 @@
 TEST(OnedEAN13WriterTest, Encode) {
   CBC_OnedEAN13Writer writer;
   writer.InitEANWriter();
-  int32_t width;
-  int32_t height;
-  uint8_t* encoded;
-  const char* expected;
 
   // EAN-13 barcodes encode 13-digit numbers into 95 modules in a unidimensional
   // disposition.
-  encoded = writer.Encode("", BCFORMAT_EAN_13, width, height);
-  EXPECT_EQ(nullptr, encoded);
-  FX_Free(encoded);
+  EXPECT_TRUE(writer.Encode("").empty());
+  EXPECT_TRUE(writer.Encode("123").empty());
+  EXPECT_TRUE(writer.Encode("123456789012").empty());
+  EXPECT_TRUE(writer.Encode("12345678901234").empty());
 
-  encoded = writer.Encode("123", BCFORMAT_EAN_13, width, height);
-  EXPECT_EQ(nullptr, encoded);
-  FX_Free(encoded);
-
-  encoded = writer.Encode("123456789012", BCFORMAT_EAN_13, width, height);
-  EXPECT_EQ(nullptr, encoded);
-  FX_Free(encoded);
-
-  encoded = writer.Encode("12345678901234", BCFORMAT_EAN_13, width, height);
-  EXPECT_EQ(nullptr, encoded);
-  FX_Free(encoded);
-
-  encoded = writer.Encode("1234567890128", BCFORMAT_EAN_13, width, height);
-  EXPECT_NE(nullptr, encoded);
-  EXPECT_EQ(1, height);
-  EXPECT_EQ(95, width);
-
-  expected =
+  static const char kExpected1[] =
       "# #"  // Start
       // 1 implicit by LLGLGG in next 6 digits
       "  #  ##"  // 2 L
@@ -56,17 +39,11 @@
       "## ##  "  // 2 R
       "#  #   "  // 8 R
       "# #";     // End
-  for (int i = 0; i < 95; i++) {
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  }
-  FX_Free(encoded);
+  DataVector<uint8_t> encoded = writer.Encode("1234567890128");
+  for (size_t i = 0; i < strlen(kExpected1); i++)
+    EXPECT_EQ(kExpected1[i] != ' ', !!encoded[i]) << i;
 
-  encoded = writer.Encode("7776665554440", BCFORMAT_EAN_13, width, height);
-  EXPECT_NE(nullptr, encoded);
-  EXPECT_EQ(1, height);
-  EXPECT_EQ(95, width);
-
-  expected =
+  static const char kExpected2[] =
       "# #"  // Start
       // 7 implicit by LGLGLG in next 6 digits
       " ### ##"  // 7 L
@@ -83,10 +60,10 @@
       "# ###  "  // 4 R
       "###  # "  // 0 R
       "# #";     // End
-  for (int i = 0; i < 95; i++) {
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  }
-  FX_Free(encoded);
+  encoded = writer.Encode("7776665554440");
+  ASSERT_EQ(strlen(kExpected2), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected2); i++)
+    EXPECT_EQ(kExpected2[i] != ' ', !!encoded[i]) << i;
 }
 
 TEST(OnedEAN13WriterTest, Checksum) {
diff --git a/fxbarcode/oned/BC_OnedEAN8Writer.cpp b/fxbarcode/oned/BC_OnedEAN8Writer.cpp
index 62c760c..6317c9b 100644
--- a/fxbarcode/oned/BC_OnedEAN8Writer.cpp
+++ b/fxbarcode/oned/BC_OnedEAN8Writer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,8 +22,9 @@
 
 #include "fxbarcode/oned/BC_OnedEAN8Writer.h"
 
+#include <math.h>
+
 #include <algorithm>
-#include <cwctype>
 #include <memory>
 #include <vector>
 
@@ -38,9 +39,9 @@
 
 namespace {
 
-const int8_t kOnedEAN8StartPattern[3] = {1, 1, 1};
-const int8_t kOnedEAN8MiddlePattern[5] = {1, 1, 1, 1, 1};
-const int8_t kOnedEAN8LPattern[10][4] = {
+const uint8_t kOnedEAN8StartPattern[3] = {1, 1, 1};
+const uint8_t kOnedEAN8MiddlePattern[5] = {1, 1, 1, 1, 1};
+const uint8_t kOnedEAN8LPattern[10][4] = {
     {3, 2, 1, 1}, {2, 2, 2, 1}, {2, 1, 2, 2}, {1, 4, 1, 1}, {1, 1, 3, 2},
     {1, 2, 3, 1}, {1, 1, 1, 4}, {1, 3, 1, 2}, {1, 2, 1, 3}, {3, 1, 1, 2}};
 
@@ -56,16 +57,14 @@
   m_iDataLenth = 8;
 }
 
-bool CBC_OnedEAN8Writer::SetTextLocation(BC_TEXT_LOC location) {
-  if (location == BC_TEXT_LOC_BELOWEMBED) {
+void CBC_OnedEAN8Writer::SetTextLocation(BC_TEXT_LOC location) {
+  if (location == BC_TEXT_LOC::kBelowEmbed)
     m_locTextLoc = location;
-    return true;
-  }
-  return false;
 }
 
 bool CBC_OnedEAN8Writer::CheckContentValidity(WideStringView contents) {
-  return std::all_of(contents.begin(), contents.end(),
+  return HasValidContentSize(contents) &&
+         std::all_of(contents.begin(), contents.end(),
                      [](wchar_t c) { return FXSYS_IsDecimalDigit(c); });
 }
 
@@ -89,103 +88,84 @@
   return EANCalcChecksum(contents);
 }
 
-uint8_t* CBC_OnedEAN8Writer::EncodeWithHint(const ByteString& contents,
-                                            BCFORMAT format,
-                                            int32_t& outWidth,
-                                            int32_t& outHeight,
-                                            int32_t hints) {
-  if (format != BCFORMAT_EAN_8)
-    return nullptr;
-  return CBC_OneDimWriter::EncodeWithHint(contents, format, outWidth, outHeight,
-                                          hints);
-}
-
-uint8_t* CBC_OnedEAN8Writer::EncodeImpl(const ByteString& contents,
-                                        int32_t& outLength) {
+DataVector<uint8_t> CBC_OnedEAN8Writer::Encode(const ByteString& contents) {
   if (contents.GetLength() != 8)
-    return nullptr;
+    return {};
 
-  outLength = m_codeWidth;
-  std::unique_ptr<uint8_t, FxFreeDeleter> result(
-      FX_Alloc(uint8_t, m_codeWidth));
-  int32_t pos = 0;
-  pos += AppendPattern(result.get(), pos, kOnedEAN8StartPattern, 3, true);
+  DataVector<uint8_t> result(m_codeWidth);
+  auto result_span = pdfium::make_span(result);
+  result_span = AppendPattern(result_span, kOnedEAN8StartPattern, true);
 
-  int32_t i = 0;
-  for (i = 0; i <= 3; i++) {
+  for (int i = 0; i <= 3; i++) {
     int32_t digit = FXSYS_DecimalCharToInt(contents[i]);
-    pos += AppendPattern(result.get(), pos, kOnedEAN8LPattern[digit], 4, false);
+    result_span = AppendPattern(result_span, kOnedEAN8LPattern[digit], false);
   }
-  pos += AppendPattern(result.get(), pos, kOnedEAN8MiddlePattern, 5, false);
+  result_span = AppendPattern(result_span, kOnedEAN8MiddlePattern, false);
 
-  for (i = 4; i <= 7; i++) {
+  for (int i = 4; i <= 7; i++) {
     int32_t digit = FXSYS_DecimalCharToInt(contents[i]);
-    pos += AppendPattern(result.get(), pos, kOnedEAN8LPattern[digit], 4, true);
+    result_span = AppendPattern(result_span, kOnedEAN8LPattern[digit], true);
   }
-  pos += AppendPattern(result.get(), pos, kOnedEAN8StartPattern, 3, true);
-  return result.release();
+  AppendPattern(result_span, kOnedEAN8StartPattern, true);
+  return result;
 }
 
 bool CBC_OnedEAN8Writer::ShowChars(WideStringView contents,
                                    CFX_RenderDevice* device,
-                                   const CFX_Matrix* matrix,
-                                   int32_t barWidth,
-                                   int32_t multiple) {
+                                   const CFX_Matrix& matrix,
+                                   int32_t barWidth) {
   if (!device)
     return false;
 
-  int32_t leftPosition = 3 * multiple;
+  constexpr float kLeftPosition = 3.0f;
   ByteString str = FX_UTF8Encode(contents);
   size_t iLength = str.GetLength();
   std::vector<TextCharPos> charpos(iLength);
   ByteString tempStr = str.First(4);
   size_t iLen = tempStr.GetLength();
-  int32_t strWidth = 7 * multiple * 4;
-  float blank = 0.0;
+  constexpr int32_t kWidth = 28;
+  float blank = 0.0f;
 
-  int32_t iFontSize = (int32_t)fabs(m_fFontSize);
+  int32_t iFontSize = static_cast<int32_t>(fabs(m_fFontSize));
   int32_t iTextHeight = iFontSize + 1;
 
   CFX_Matrix matr(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0);
-  CFX_FloatRect rect((float)leftPosition, (float)(m_Height - iTextHeight),
-                     (float)(leftPosition + strWidth - 0.5), (float)m_Height);
-  matr.Concat(*matrix);
+  CFX_FloatRect rect(kLeftPosition, (float)(m_Height - iTextHeight),
+                     kLeftPosition + kWidth - 0.5, (float)m_Height);
+  matr.Concat(matrix);
   FX_RECT re = matr.TransformRect(rect).GetOuterRect();
   device->FillRect(re, kBackgroundColor);
   CFX_Matrix matr1(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0);
-  CFX_FloatRect rect1(
-      (float)(leftPosition + 33 * multiple), (float)(m_Height - iTextHeight),
-      (float)(leftPosition + 33 * multiple + strWidth - 0.5), (float)m_Height);
-  matr1.Concat(*matrix);
+  CFX_FloatRect rect1(kLeftPosition + 33, (float)(m_Height - iTextHeight),
+                      kLeftPosition + 33 + kWidth - 0.5, (float)m_Height);
+  matr1.Concat(matrix);
   re = matr1.TransformRect(rect1).GetOuterRect();
   device->FillRect(re, kBackgroundColor);
-  strWidth = (int32_t)(strWidth * m_outputHScale);
+  int32_t strWidth = static_cast<int32_t>(kWidth * m_outputHScale);
 
-  CalcTextInfo(tempStr, charpos.data(), m_pFont.Get(), (float)strWidth,
-               iFontSize, blank);
+  CalcTextInfo(tempStr, charpos.data(), m_pFont, (float)strWidth, iFontSize,
+               blank);
   {
     CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0,
-                              (float)leftPosition * m_outputHScale,
+                              kLeftPosition * m_outputHScale,
                               (float)(m_Height - iTextHeight + iFontSize));
-    affine_matrix1.Concat(*matrix);
-    device->DrawNormalText(iLen, charpos.data(), m_pFont.Get(),
+    affine_matrix1.Concat(matrix);
+    device->DrawNormalText(pdfium::make_span(charpos).first(iLen), m_pFont,
                            static_cast<float>(iFontSize), affine_matrix1,
-                           m_fontColor, FXTEXT_CLEARTYPE);
+                           m_fontColor, GetTextRenderOptions());
   }
   tempStr = str.Substr(4, 4);
   iLen = tempStr.GetLength();
-  CalcTextInfo(tempStr, &charpos[4], m_pFont.Get(), (float)strWidth, iFontSize,
+  CalcTextInfo(tempStr, &charpos[4], m_pFont, (float)strWidth, iFontSize,
                blank);
   {
-    CFX_Matrix affine_matrix1(
-        1.0, 0.0, 0.0, -1.0,
-        (float)(leftPosition + 33 * multiple) * m_outputHScale,
-        (float)(m_Height - iTextHeight + iFontSize));
-    if (matrix)
-      affine_matrix1.Concat(*matrix);
-    device->DrawNormalText(iLen, &charpos[4], m_pFont.Get(),
+    CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0,
+                              (kLeftPosition + 33) * m_outputHScale,
+                              (float)(m_Height - iTextHeight + iFontSize));
+    affine_matrix1.Concat(matrix);
+    device->DrawNormalText(pdfium::make_span(charpos).subspan(4, iLen), m_pFont,
                            static_cast<float>(iFontSize), affine_matrix1,
-                           m_fontColor, FXTEXT_CLEARTYPE);
+                           m_fontColor, GetTextRenderOptions());
   }
   return true;
 }
diff --git a/fxbarcode/oned/BC_OnedEAN8Writer.h b/fxbarcode/oned/BC_OnedEAN8Writer.h
index b71dbb9..3f633e6 100644
--- a/fxbarcode/oned/BC_OnedEAN8Writer.h
+++ b/fxbarcode/oned/BC_OnedEAN8Writer.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,13 @@
 #ifndef FXBARCODE_ONED_BC_ONEDEAN8WRITER_H_
 #define FXBARCODE_ONED_BC_ONEDEAN8WRITER_H_
 
+#include <stdint.h>
+
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
 #include "fxbarcode/BC_Library.h"
 #include "fxbarcode/oned/BC_OnedEANWriter.h"
 
-class CFX_DIBitmap;
+class CFX_Matrix;
 class CFX_RenderDevice;
 
 class CBC_OnedEAN8Writer final : public CBC_OneDimEANWriter {
@@ -21,24 +22,18 @@
   ~CBC_OnedEAN8Writer() override;
 
   // CBC_OneDimEANWriter:
-  uint8_t* EncodeWithHint(const ByteString& contents,
-                          BCFORMAT format,
-                          int32_t& outWidth,
-                          int32_t& outHeight,
-                          int32_t hints) override;
-  uint8_t* EncodeImpl(const ByteString& contents, int32_t& outLength) override;
+  DataVector<uint8_t> Encode(const ByteString& contents) override;
   bool CheckContentValidity(WideStringView contents) override;
   WideString FilterContents(WideStringView contents) override;
   void SetDataLength(int32_t length) override;
-  bool SetTextLocation(BC_TEXT_LOC location) override;
+  void SetTextLocation(BC_TEXT_LOC location) override;
   int32_t CalcChecksum(const ByteString& contents) override;
 
  private:
   bool ShowChars(WideStringView contents,
                  CFX_RenderDevice* device,
-                 const CFX_Matrix* matrix,
-                 int32_t barWidth,
-                 int32_t multiple) override;
+                 const CFX_Matrix& matrix,
+                 int32_t barWidth) override;
 
   static constexpr int32_t kDefaultCodeWidth = 3 + (7 * 4) + 5 + (7 * 4) + 3;
   int32_t m_codeWidth = kDefaultCodeWidth;
diff --git a/fxbarcode/oned/BC_OnedEAN8Writer_unittest.cpp b/fxbarcode/oned/BC_OnedEAN8Writer_unittest.cpp
index ae1b823..36d961d 100644
--- a/fxbarcode/oned/BC_OnedEAN8Writer_unittest.cpp
+++ b/fxbarcode/oned/BC_OnedEAN8Writer_unittest.cpp
@@ -1,9 +1,12 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxbarcode/oned/BC_OnedEAN8Writer.h"
 
+#include <string.h>
+
+#include "core/fxcrt/data_vector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -11,35 +14,15 @@
 TEST(OnedEAN8WriterTest, Encode) {
   CBC_OnedEAN8Writer writer;
   writer.InitEANWriter();
-  int32_t width;
-  int32_t height;
-  uint8_t* encoded;
-  const char* expected;
 
   // EAN-8 barcodes encode 8-digit numbers into 67 modules in a unidimensional
   // disposition.
-  encoded = writer.Encode("", BCFORMAT_EAN_8, width, height);
-  EXPECT_EQ(nullptr, encoded);
-  FX_Free(encoded);
+  EXPECT_TRUE(writer.Encode("").empty());
+  EXPECT_TRUE(writer.Encode("123").empty());
+  EXPECT_TRUE(writer.Encode("1234567").empty());
+  EXPECT_TRUE(writer.Encode("123456789").empty());
 
-  encoded = writer.Encode("123", BCFORMAT_EAN_8, width, height);
-  EXPECT_EQ(nullptr, encoded);
-  FX_Free(encoded);
-
-  encoded = writer.Encode("1234567", BCFORMAT_EAN_8, width, height);
-  EXPECT_EQ(nullptr, encoded);
-  FX_Free(encoded);
-
-  encoded = writer.Encode("123456789", BCFORMAT_EAN_8, width, height);
-  EXPECT_EQ(nullptr, encoded);
-  FX_Free(encoded);
-
-  encoded = writer.Encode("12345670", BCFORMAT_EAN_8, width, height);
-  EXPECT_NE(nullptr, encoded);
-  EXPECT_EQ(1, height);
-  EXPECT_EQ(67, width);
-
-  expected =
+  static const char kExpected1[] =
       "# #"      // Start
       "  ##  #"  // 1 L
       "  #  ##"  // 2 L
@@ -51,17 +34,12 @@
       "#   #  "  // 7 R
       "###  # "  // 0 R
       "# #";     // End
-  for (int i = 0; i < 67; i++) {
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  }
-  FX_Free(encoded);
+  DataVector<uint8_t> encoded = writer.Encode("12345670");
+  ASSERT_EQ(strlen(kExpected1), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected1); i++)
+    EXPECT_EQ(kExpected1[i] != ' ', !!encoded[i]) << i;
 
-  encoded = writer.Encode("99441104", BCFORMAT_EAN_8, width, height);
-  EXPECT_NE(nullptr, encoded);
-  EXPECT_EQ(1, height);
-  EXPECT_EQ(67, width);
-
-  expected =
+  static const char kExpected2[] =
       "# #"      // Start
       "   # ##"  // 9 L
       "   # ##"  // 9 L
@@ -73,10 +51,10 @@
       "###  # "  // 0 R
       "# ###  "  // 4 R
       "# #";     // End
-  for (int i = 0; i < 67; i++) {
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  }
-  FX_Free(encoded);
+  encoded = writer.Encode("99441104");
+  ASSERT_EQ(strlen(kExpected2), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected2); i++)
+    EXPECT_EQ(kExpected2[i] != ' ', !!encoded[i]) << i;
 }
 
 TEST(OnedEAN8WriterTest, Checksum) {
diff --git a/fxbarcode/oned/BC_OnedEANChecksum.cpp b/fxbarcode/oned/BC_OnedEANChecksum.cpp
index b290c62..c1fbab4 100644
--- a/fxbarcode/oned/BC_OnedEANChecksum.cpp
+++ b/fxbarcode/oned/BC_OnedEANChecksum.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/oned/BC_OnedEANChecksum.h b/fxbarcode/oned/BC_OnedEANChecksum.h
index 5dd24bc..60b801c 100644
--- a/fxbarcode/oned/BC_OnedEANChecksum.h
+++ b/fxbarcode/oned/BC_OnedEANChecksum.h
@@ -1,11 +1,11 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef FXBARCODE_ONED_BC_ONEDEANCHECKSUM_H_
 #define FXBARCODE_ONED_BC_ONEDEANCHECKSUM_H_
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/bytestring.h"
 
 int32_t EANCalcChecksum(const ByteString& contents);
 
diff --git a/fxbarcode/oned/BC_OnedEANWriter.cpp b/fxbarcode/oned/BC_OnedEANWriter.cpp
index f43b9ad..34bcf9e 100644
--- a/fxbarcode/oned/BC_OnedEANWriter.cpp
+++ b/fxbarcode/oned/BC_OnedEANWriter.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/oned/BC_OnedEANWriter.h b/fxbarcode/oned/BC_OnedEANWriter.h
index 5d80167..a871edf 100644
--- a/fxbarcode/oned/BC_OnedEANWriter.h
+++ b/fxbarcode/oned/BC_OnedEANWriter.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/oned/BC_OnedUPCAWriter.cpp b/fxbarcode/oned/BC_OnedUPCAWriter.cpp
index 666b037..55a6a02 100644
--- a/fxbarcode/oned/BC_OnedUPCAWriter.cpp
+++ b/fxbarcode/oned/BC_OnedUPCAWriter.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,6 +22,9 @@
 
 #include "fxbarcode/oned/BC_OnedUPCAWriter.h"
 
+#include <math.h>
+
+#include <algorithm>
 #include <vector>
 
 #include "core/fxcrt/fx_extension.h"
@@ -30,21 +33,18 @@
 #include "fxbarcode/BC_Writer.h"
 #include "fxbarcode/oned/BC_OneDimWriter.h"
 #include "fxbarcode/oned/BC_OnedEAN13Writer.h"
-#include "third_party/base/ptr_util.h"
 
 CBC_OnedUPCAWriter::CBC_OnedUPCAWriter() {
   m_bLeftPadding = true;
   m_bRightPadding = true;
 }
 
-CBC_OnedUPCAWriter::~CBC_OnedUPCAWriter() {}
+CBC_OnedUPCAWriter::~CBC_OnedUPCAWriter() = default;
 
 bool CBC_OnedUPCAWriter::CheckContentValidity(WideStringView contents) {
-  for (size_t i = 0; i < contents.GetLength(); ++i) {
-    if (contents[i] < '0' || contents[i] > '9')
-      return false;
-  }
-  return true;
+  return HasValidContentSize(contents) &&
+         std::all_of(contents.begin(), contents.end(),
+                     [](wchar_t c) { return FXSYS_IsDecimalDigit(c); });
 }
 
 WideString CBC_OnedUPCAWriter::FilterContents(WideStringView contents) {
@@ -64,7 +64,7 @@
 }
 
 void CBC_OnedUPCAWriter::InitEANWriter() {
-  m_subWriter = pdfium::MakeUnique<CBC_OnedEAN13Writer>();
+  m_subWriter = std::make_unique<CBC_OnedEAN13Writer>();
 }
 
 int32_t CBC_OnedUPCAWriter::CalcChecksum(const ByteString& contents) {
@@ -84,132 +84,104 @@
   return checksum;
 }
 
-uint8_t* CBC_OnedUPCAWriter::EncodeWithHint(const ByteString& contents,
-                                            BCFORMAT format,
-                                            int32_t& outWidth,
-                                            int32_t& outHeight,
-                                            int32_t hints) {
-  if (format != BCFORMAT_UPC_A)
-    return nullptr;
-
+DataVector<uint8_t> CBC_OnedUPCAWriter::Encode(const ByteString& contents) {
   ByteString toEAN13String = '0' + contents;
   m_iDataLenth = 13;
-  return m_subWriter->EncodeWithHint(toEAN13String, BCFORMAT_EAN_13, outWidth,
-                                     outHeight, hints);
-}
-
-uint8_t* CBC_OnedUPCAWriter::EncodeImpl(const ByteString& contents,
-                                        int32_t& outLength) {
-  return nullptr;
+  return m_subWriter->Encode(toEAN13String);
 }
 
 bool CBC_OnedUPCAWriter::ShowChars(WideStringView contents,
                                    CFX_RenderDevice* device,
-                                   const CFX_Matrix* matrix,
-                                   int32_t barWidth,
-                                   int32_t multiple) {
+                                   const CFX_Matrix& matrix,
+                                   int32_t barWidth) {
   if (!device)
     return false;
 
-  int32_t leftPadding = 7 * multiple;
-  int32_t leftPosition = 10 * multiple + leftPadding;
+  constexpr float kLeftPosition = 17.0f;
   ByteString str = FX_UTF8Encode(contents);
   size_t length = str.GetLength();
   std::vector<TextCharPos> charpos(length);
   ByteString tempStr = str.Substr(1, 5);
-  float strWidth = (float)35 * multiple;
-  float blank = 0.0;
+  constexpr float kWidth = 35.0f;
+  float blank = 0.0f;
 
   length = tempStr.GetLength();
-  int32_t iFontSize = (int32_t)fabs(m_fFontSize);
+  int32_t iFontSize = static_cast<int32_t>(fabs(m_fFontSize));
   int32_t iTextHeight = iFontSize + 1;
 
   CFX_Matrix matr(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0);
-  CFX_FloatRect rect((float)leftPosition, (float)(m_Height - iTextHeight),
-                     (float)(leftPosition + strWidth - 0.5), (float)m_Height);
-  matr.Concat(*matrix);
+  CFX_FloatRect rect(kLeftPosition, (float)(m_Height - iTextHeight),
+                     kLeftPosition + kWidth - 0.5, (float)m_Height);
+  matr.Concat(matrix);
   FX_RECT re = matr.TransformRect(rect).GetOuterRect();
   device->FillRect(re, kBackgroundColor);
   CFX_Matrix matr1(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0);
-  CFX_FloatRect rect1((float)(leftPosition + 40 * multiple),
-                      (float)(m_Height - iTextHeight),
-                      (float)((leftPosition + 40 * multiple) + strWidth - 0.5),
-                      (float)m_Height);
-  matr1.Concat(*matrix);
+  CFX_FloatRect rect1(kLeftPosition + 40, (float)(m_Height - iTextHeight),
+                      kLeftPosition + 40 + kWidth - 0.5, (float)m_Height);
+  matr1.Concat(matrix);
   re = matr1.TransformRect(rect1).GetOuterRect();
   device->FillRect(re, kBackgroundColor);
-  float strWidth1 = (float)multiple * 7;
+  constexpr float kWidth1 = 7.0f;
   CFX_Matrix matr2(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0);
-  CFX_FloatRect rect2(0.0, (float)(m_Height - iTextHeight),
-                      (float)strWidth1 - 1, (float)m_Height);
-  matr2.Concat(*matrix);
+  CFX_FloatRect rect2(0.0, (float)(m_Height - iTextHeight), kWidth1 - 1,
+                      (float)m_Height);
+  matr2.Concat(matrix);
   re = matr2.TransformRect(rect2).GetOuterRect();
   device->FillRect(re, kBackgroundColor);
   CFX_Matrix matr3(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0);
-  CFX_FloatRect rect3((float)(leftPosition + 85 * multiple),
-                      (float)(m_Height - iTextHeight),
-                      (float)((leftPosition + 85 * multiple) + strWidth1 - 0.5),
-                      (float)m_Height);
-  matr3.Concat(*matrix);
+  CFX_FloatRect rect3(kLeftPosition + 85, (float)(m_Height - iTextHeight),
+                      kLeftPosition + 85 + kWidth1 - 0.5, (float)m_Height);
+  matr3.Concat(matrix);
   re = matr3.TransformRect(rect3).GetOuterRect();
   device->FillRect(re, kBackgroundColor);
-  strWidth = strWidth * m_outputHScale;
+  float strWidth = kWidth * m_outputHScale;
 
-  CalcTextInfo(tempStr, &charpos[1], m_pFont.Get(), strWidth, iFontSize, blank);
+  CalcTextInfo(tempStr, &charpos[1], m_pFont, strWidth, iFontSize, blank);
   {
     CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0,
-                              (float)leftPosition * m_outputHScale,
+                              kLeftPosition * m_outputHScale,
                               (float)(m_Height - iTextHeight + iFontSize));
-    if (matrix)
-      affine_matrix1.Concat(*matrix);
-    device->DrawNormalText(length, &charpos[1], m_pFont.Get(),
-                           static_cast<float>(iFontSize), affine_matrix1,
-                           m_fontColor, FXTEXT_CLEARTYPE);
+    affine_matrix1.Concat(matrix);
+    device->DrawNormalText(pdfium::make_span(charpos).subspan(1, length),
+                           m_pFont, static_cast<float>(iFontSize),
+                           affine_matrix1, m_fontColor, GetTextRenderOptions());
   }
   tempStr = str.Substr(6, 5);
   length = tempStr.GetLength();
-  CalcTextInfo(tempStr, &charpos[6], m_pFont.Get(), strWidth, iFontSize, blank);
+  CalcTextInfo(tempStr, &charpos[6], m_pFont, strWidth, iFontSize, blank);
   {
-    CFX_Matrix affine_matrix1(
-        1.0, 0.0, 0.0, -1.0,
-        (float)(leftPosition + 40 * multiple) * m_outputHScale,
-        (float)(m_Height - iTextHeight + iFontSize));
-    if (matrix)
-      affine_matrix1.Concat(*matrix);
-    device->DrawNormalText(length, &charpos[6], m_pFont.Get(),
-                           static_cast<float>(iFontSize), affine_matrix1,
-                           m_fontColor, FXTEXT_CLEARTYPE);
+    CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0,
+                              (kLeftPosition + 40) * m_outputHScale,
+                              (float)(m_Height - iTextHeight + iFontSize));
+    affine_matrix1.Concat(matrix);
+    device->DrawNormalText(pdfium::make_span(charpos).subspan(6, length),
+                           m_pFont, static_cast<float>(iFontSize),
+                           affine_matrix1, m_fontColor, GetTextRenderOptions());
   }
   tempStr = str.First(1);
   length = tempStr.GetLength();
-  strWidth = (float)multiple * 7;
-  strWidth = strWidth * m_outputHScale;
+  strWidth = 7 * m_outputHScale;
 
-  CalcTextInfo(tempStr, charpos.data(), m_pFont.Get(), strWidth, iFontSize,
-               blank);
+  CalcTextInfo(tempStr, charpos.data(), m_pFont, strWidth, iFontSize, blank);
   {
     CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0, 0,
                               (float)(m_Height - iTextHeight + iFontSize));
-    if (matrix)
-      affine_matrix1.Concat(*matrix);
-    device->DrawNormalText(length, charpos.data(), m_pFont.Get(),
+    affine_matrix1.Concat(matrix);
+    device->DrawNormalText(pdfium::make_span(charpos).first(length), m_pFont,
                            static_cast<float>(iFontSize), affine_matrix1,
-                           m_fontColor, FXTEXT_CLEARTYPE);
+                           m_fontColor, GetTextRenderOptions());
   }
   tempStr = str.Substr(11, 1);
   length = tempStr.GetLength();
-  CalcTextInfo(tempStr, &charpos[11], m_pFont.Get(), strWidth, iFontSize,
-               blank);
+  CalcTextInfo(tempStr, &charpos[11], m_pFont, strWidth, iFontSize, blank);
   {
-    CFX_Matrix affine_matrix1(
-        1.0, 0.0, 0.0, -1.0,
-        (float)(leftPosition + 85 * multiple) * m_outputHScale,
-        (float)(m_Height - iTextHeight + iFontSize));
-    if (matrix)
-      affine_matrix1.Concat(*matrix);
-    device->DrawNormalText(length, &charpos[11], m_pFont.Get(),
-                           static_cast<float>(iFontSize), affine_matrix1,
-                           m_fontColor, FXTEXT_CLEARTYPE);
+    CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0,
+                              (kLeftPosition + 85) * m_outputHScale,
+                              (float)(m_Height - iTextHeight + iFontSize));
+    affine_matrix1.Concat(matrix);
+    device->DrawNormalText(pdfium::make_span(charpos).subspan(11, length),
+                           m_pFont, static_cast<float>(iFontSize),
+                           affine_matrix1, m_fontColor, GetTextRenderOptions());
   }
   return true;
 }
diff --git a/fxbarcode/oned/BC_OnedUPCAWriter.h b/fxbarcode/oned/BC_OnedUPCAWriter.h
index 991dfe6..7d8beff 100644
--- a/fxbarcode/oned/BC_OnedUPCAWriter.h
+++ b/fxbarcode/oned/BC_OnedUPCAWriter.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,11 +10,10 @@
 #include <memory>
 
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "fxbarcode/BC_Library.h"
 #include "fxbarcode/oned/BC_OnedEANWriter.h"
 
 class CBC_OnedEAN13Writer;
-class CFX_DIBitmap;
 class CFX_Matrix;
 class CFX_RenderDevice;
 
@@ -24,12 +23,7 @@
   ~CBC_OnedUPCAWriter() override;
 
   // CBC_OneDimEANWriter:
-  uint8_t* EncodeWithHint(const ByteString& contents,
-                          BCFORMAT format,
-                          int32_t& outWidth,
-                          int32_t& outHeight,
-                          int32_t hints) override;
-  uint8_t* EncodeImpl(const ByteString& contents, int32_t& outLength) override;
+  DataVector<uint8_t> Encode(const ByteString& contents) override;
   bool CheckContentValidity(WideStringView contents) override;
   WideString FilterContents(WideStringView contents) override;
   void InitEANWriter() override;
@@ -38,9 +32,8 @@
  private:
   bool ShowChars(WideStringView contents,
                  CFX_RenderDevice* device,
-                 const CFX_Matrix* matrix,
-                 int32_t barWidth,
-                 int32_t multiple) override;
+                 const CFX_Matrix& matrix,
+                 int32_t barWidth) override;
 
   std::unique_ptr<CBC_OnedEAN13Writer> m_subWriter;
 };
diff --git a/fxbarcode/oned/BC_OnedUPCAWriter_unittest.cpp b/fxbarcode/oned/BC_OnedUPCAWriter_unittest.cpp
index c5acd23..71d10e1 100644
--- a/fxbarcode/oned/BC_OnedUPCAWriter_unittest.cpp
+++ b/fxbarcode/oned/BC_OnedUPCAWriter_unittest.cpp
@@ -1,9 +1,12 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxbarcode/oned/BC_OnedUPCAWriter.h"
 
+#include <string.h>
+
+#include "core/fxcrt/data_vector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -11,29 +14,15 @@
 TEST(OnedUPCAWriterTest, Encode) {
   CBC_OnedUPCAWriter writer;
   writer.InitEANWriter();
-  int32_t width;
-  int32_t height;
 
   // UPCA barcodes encode 12-digit numbers into 95 modules in a unidimensional
   // disposition.
-  uint8_t* encoded = writer.Encode("", BCFORMAT_UPC_A, width, height);
-  EXPECT_EQ(nullptr, encoded);
-  FX_Free(encoded);
+  EXPECT_TRUE(writer.Encode("").empty());
+  EXPECT_TRUE(writer.Encode("123").empty());
+  EXPECT_TRUE(writer.Encode("12345678901").empty());
+  EXPECT_TRUE(writer.Encode("1234567890123").empty());
 
-  encoded = writer.Encode("123", BCFORMAT_UPC_A, width, height);
-  EXPECT_EQ(nullptr, encoded);
-  FX_Free(encoded);
-
-  encoded = writer.Encode("12345678901", BCFORMAT_UPC_A, width, height);
-  EXPECT_EQ(nullptr, encoded);
-  FX_Free(encoded);
-
-  encoded = writer.Encode("1234567890123", BCFORMAT_UPC_A, width, height);
-  EXPECT_EQ(nullptr, encoded);
-  FX_Free(encoded);
-
-  encoded = writer.Encode("123456789012", BCFORMAT_UPC_A, width, height);
-  const char* expected =
+  static const char kExpected1[] =
       "# #"      // Start
       "  ##  #"  // 1 L
       "  #  ##"  // 2 L
@@ -49,16 +38,13 @@
       "##  ## "  // 1 R
       "## ##  "  // 2 R
       "# #";     // End
-  EXPECT_NE(nullptr, encoded);
-  EXPECT_EQ(1, height);
-  EXPECT_EQ(static_cast<int32_t>(strlen(expected)), width);
-  for (size_t i = 0; i < strlen(expected); i++) {
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  }
-  FX_Free(encoded);
+  DataVector<uint8_t> encoded = writer.Encode("123456789012");
+  ASSERT_EQ(strlen(kExpected1), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected1); i++)
+    EXPECT_EQ(kExpected1[i] != ' ', !!encoded[i]) << i;
 
-  encoded = writer.Encode("777666555440", BCFORMAT_UPC_A, width, height);
-  expected =
+  encoded = writer.Encode("777666555440");
+  static const char kExpected2[] =
       "# #"      // Start
       " ### ##"  // 7 L
       " ### ##"  // 7 L
@@ -74,13 +60,9 @@
       "# ###  "  // 4 R
       "###  # "  // 0 R
       "# #";     // End
-  EXPECT_NE(nullptr, encoded);
-  EXPECT_EQ(1, height);
-  EXPECT_EQ(static_cast<int32_t>(strlen(expected)), width);
-  for (size_t i = 0; i < strlen(expected); i++) {
-    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  }
-  FX_Free(encoded);
+  ASSERT_EQ(strlen(kExpected2), encoded.size());
+  for (size_t i = 0; i < strlen(kExpected2); i++)
+    EXPECT_EQ(kExpected2[i] != ' ', !!encoded[i]) << i;
 }
 
 TEST(OnedUPCAWriterTest, Checksum) {
diff --git a/fxbarcode/pdf417/BC_PDF417.cpp b/fxbarcode/pdf417/BC_PDF417.cpp
index b9ba390..d48b98a 100644
--- a/fxbarcode/pdf417/BC_PDF417.cpp
+++ b/fxbarcode/pdf417/BC_PDF417.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,15 +22,16 @@
 
 #include "fxbarcode/pdf417/BC_PDF417.h"
 
+#include <math.h>
+
 #include "fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h"
 #include "fxbarcode/pdf417/BC_PDF417BarcodeRow.h"
 #include "fxbarcode/pdf417/BC_PDF417ErrorCorrection.h"
 #include "fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
-const uint16_t g_CodewordTable[3][929] = {
+const uint16_t kCodewordTable[3][929] = {
     {0xd5c0, 0xeaf0, 0xf57c, 0xd4e0, 0xea78, 0xf53e, 0xa8c0, 0xd470, 0xa860,
      0x5040, 0xa830, 0x5020, 0xadc0, 0xd6f0, 0xeb7c, 0xace0, 0xd678, 0xeb3e,
      0x58c0, 0xac70, 0x5860, 0x5dc0, 0xaef0, 0xd77c, 0x5ce0, 0xae78, 0xd73e,
@@ -345,7 +346,7 @@
      0x0fb2, 0xc7ea}};
 
 int32_t Get17BitCodeword(int i, int j) {
-  return (0x10000 | g_CodewordTable[i][j]);
+  return (0x10000 | kCodewordTable[i][j]);
 }
 
 }  // namespace
@@ -365,7 +366,7 @@
   if (errorCorrectionCodeWords < 0)
     return false;
 
-  Optional<WideString> high_level =
+  absl::optional<WideString> high_level =
       CBC_PDF417HighLevelEncoder::EncodeHighLevel(msg);
   if (!high_level.has_value())
     return false;
@@ -390,13 +391,14 @@
     sb += (wchar_t)900;
 
   WideString dataCodewords(sb);
-  Optional<WideString> ec = CBC_PDF417ErrorCorrection::GenerateErrorCorrection(
-      dataCodewords, errorCorrectionLevel);
+  absl::optional<WideString> ec =
+      CBC_PDF417ErrorCorrection::GenerateErrorCorrection(dataCodewords,
+                                                         errorCorrectionLevel);
   if (!ec.has_value())
     return false;
 
   WideString fullCodewords = dataCodewords + ec.value();
-  m_barcodeMatrix = pdfium::MakeUnique<CBC_BarcodeMatrix>(cols, rows);
+  m_barcodeMatrix = std::make_unique<CBC_BarcodeMatrix>(cols, rows);
   encodeLowLevel(fullCodewords, cols, rows, errorCorrectionLevel,
                  m_barcodeMatrix.get());
   return true;
@@ -433,19 +435,19 @@
                             CBC_BarcodeRow* logic) {
   int32_t map = 1 << (len - 1);
   bool last = ((pattern & map) != 0);
-  int32_t width = 0;
+  size_t width = 0;
   for (int32_t i = 0; i < len; i++) {
     bool black = ((pattern & map) != 0);
     if (last == black) {
       width++;
     } else {
-      logic->addBar(last, width);
+      logic->AddBar(last, width);
       last = black;
       width = 1;
     }
     map >>= 1;
   }
-  logic->addBar(last, width);
+  logic->AddBar(last, width);
 }
 
 void CBC_PDF417::encodeLowLevel(WideString fullCodewords,
diff --git a/fxbarcode/pdf417/BC_PDF417.h b/fxbarcode/pdf417/BC_PDF417.h
index b445ede..0312deb 100644
--- a/fxbarcode/pdf417/BC_PDF417.h
+++ b/fxbarcode/pdf417/BC_PDF417.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,7 @@
 #include <memory>
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 
 class CBC_BarcodeRow;
 class CBC_BarcodeMatrix;
diff --git a/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.cpp b/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.cpp
index 70e7cd9..b6f407d 100644
--- a/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.cpp
+++ b/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -21,24 +21,26 @@
  */
 
 #include "fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h"
+
+#include <stdint.h>
+
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/span_util.h"
 #include "fxbarcode/pdf417/BC_PDF417BarcodeRow.h"
-#include "third_party/base/ptr_util.h"
 
 CBC_BarcodeMatrix::CBC_BarcodeMatrix(size_t width, size_t height)
     : m_width((width + 4) * 17 + 1), m_height(height) {
   m_matrix.resize(m_height);
   for (size_t i = 0; i < m_height; ++i)
-    m_matrix[i] = pdfium::MakeUnique<CBC_BarcodeRow>(m_width);
+    m_matrix[i] = std::make_unique<CBC_BarcodeRow>(m_width);
 }
 
-CBC_BarcodeMatrix::~CBC_BarcodeMatrix() {}
+CBC_BarcodeMatrix::~CBC_BarcodeMatrix() = default;
 
-std::vector<uint8_t> CBC_BarcodeMatrix::toBitArray() {
-  std::vector<uint8_t> bitArray(m_width * m_height);
-  for (size_t i = 0; i < m_height; ++i) {
-    std::vector<uint8_t>& bytearray = m_matrix[i]->getRow();
-    for (size_t j = 0; j < m_width; ++j)
-      bitArray[i * m_width + j] = bytearray[j];
-  }
-  return bitArray;
+DataVector<uint8_t> CBC_BarcodeMatrix::toBitArray() {
+  DataVector<uint8_t> bit_array(m_width * m_height);
+  pdfium::span<uint8_t> bit_array_span(bit_array);
+  for (size_t i = 0; i < m_height; ++i)
+    fxcrt::spancpy(bit_array_span.subspan(i * m_width), m_matrix[i]->GetRow());
+  return bit_array;
 }
diff --git a/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h b/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h
index d059cee..2ed8834 100644
--- a/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h
+++ b/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,9 +7,13 @@
 #ifndef FXBARCODE_PDF417_BC_PDF417BARCODEMATRIX_H_
 #define FXBARCODE_PDF417_BC_PDF417BARCODEMATRIX_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
+#include "core/fxcrt/data_vector.h"
+
 class CBC_BarcodeRow;
 
 class CBC_BarcodeMatrix final {
@@ -20,7 +24,7 @@
   CBC_BarcodeRow* getRow(size_t row) const { return m_matrix[row].get(); }
   size_t getWidth() const { return m_width; }
   size_t getHeight() const { return m_height; }
-  std::vector<uint8_t> toBitArray();
+  DataVector<uint8_t> toBitArray();
 
  private:
   std::vector<std::unique_ptr<CBC_BarcodeRow>> m_matrix;
diff --git a/fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp b/fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp
index 65c27de..48e4128 100644
--- a/fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp
+++ b/fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,18 +22,16 @@
 
 #include "fxbarcode/pdf417/BC_PDF417BarcodeRow.h"
 
-#include <algorithm>
+#include "core/fxcrt/span_util.h"
+#include "third_party/base/check_op.h"
 
-CBC_BarcodeRow::CBC_BarcodeRow(size_t width)
-    : m_row(width), m_currentLocation(0) {}
+CBC_BarcodeRow::CBC_BarcodeRow(size_t width) : row_(width) {}
 
-CBC_BarcodeRow::~CBC_BarcodeRow() {}
+CBC_BarcodeRow::~CBC_BarcodeRow() = default;
 
-void CBC_BarcodeRow::addBar(bool black, int32_t width) {
-  std::fill_n(m_row.begin() + m_currentLocation, width, black ? 1 : 0);
-  m_currentLocation += width;
-}
-
-std::vector<uint8_t>& CBC_BarcodeRow::getRow() {
-  return m_row;
+void CBC_BarcodeRow::AddBar(bool black, size_t width) {
+  pdfium::span<uint8_t> available = row_.writable_span().subspan(offset_);
+  CHECK_LE(width, available.size());
+  fxcrt::spanset(available.first(width), black ? 1 : 0);
+  offset_ += width;
 }
diff --git a/fxbarcode/pdf417/BC_PDF417BarcodeRow.h b/fxbarcode/pdf417/BC_PDF417BarcodeRow.h
index 07ebbd7..fc40701 100644
--- a/fxbarcode/pdf417/BC_PDF417BarcodeRow.h
+++ b/fxbarcode/pdf417/BC_PDF417BarcodeRow.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,19 +9,20 @@
 
 #include <stdint.h>
 
-#include <vector>
+#include "core/fxcrt/fixed_zeroed_data_vector.h"
+#include "third_party/base/containers/span.h"
 
 class CBC_BarcodeRow final {
  public:
   explicit CBC_BarcodeRow(size_t width);
   ~CBC_BarcodeRow();
 
-  void addBar(bool black, int32_t width);
-  std::vector<uint8_t>& getRow();
+  void AddBar(bool black, size_t width);
+  pdfium::span<const uint8_t> GetRow() const { return row_; }
 
  private:
-  std::vector<uint8_t> m_row;
-  int32_t m_currentLocation;
+  FixedZeroedDataVector<uint8_t> row_;
+  size_t offset_ = 0;
 };
 
 #endif  // FXBARCODE_PDF417_BC_PDF417BARCODEROW_H_
diff --git a/fxbarcode/pdf417/BC_PDF417ErrorCorrection.cpp b/fxbarcode/pdf417/BC_PDF417ErrorCorrection.cpp
index f910d4a..509f8aa 100644
--- a/fxbarcode/pdf417/BC_PDF417ErrorCorrection.cpp
+++ b/fxbarcode/pdf417/BC_PDF417ErrorCorrection.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,9 +22,9 @@
 
 #include "fxbarcode/pdf417/BC_PDF417ErrorCorrection.h"
 
-#include <vector>
+#include <stdint.h>
 
-#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/data_vector.h"
 
 namespace {
 
@@ -133,14 +133,14 @@
 }
 
 // static
-Optional<WideString> CBC_PDF417ErrorCorrection::GenerateErrorCorrection(
+absl::optional<WideString> CBC_PDF417ErrorCorrection::GenerateErrorCorrection(
     const WideString& dataCodewords,
     int32_t errorCorrectionLevel) {
   int32_t k = GetErrorCorrectionCodewordCount(errorCorrectionLevel);
   if (k < 0)
-    return {};
+    return absl::nullopt;
 
-  std::vector<wchar_t, FxAllocAllocator<wchar_t>> ech(k);
+  DataVector<wchar_t> ech(k);
   size_t sld = dataCodewords.GetLength();
   for (size_t i = 0; i < sld; i++) {
     int32_t t1 = (dataCodewords[i] + ech[k - 1]) % 929;
diff --git a/fxbarcode/pdf417/BC_PDF417ErrorCorrection.h b/fxbarcode/pdf417/BC_PDF417ErrorCorrection.h
index f5aa898..ccb2fa0 100644
--- a/fxbarcode/pdf417/BC_PDF417ErrorCorrection.h
+++ b/fxbarcode/pdf417/BC_PDF417ErrorCorrection.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,8 +9,8 @@
 
 #include <stdint.h>
 
-#include "core/fxcrt/fx_string.h"
-#include "third_party/base/optional.h"
+#include "core/fxcrt/widestring.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CBC_PDF417ErrorCorrection {
  public:
@@ -18,7 +18,7 @@
   ~CBC_PDF417ErrorCorrection() = delete;
 
   static int32_t GetErrorCorrectionCodewordCount(int32_t errorCorrectionLevel);
-  static Optional<WideString> GenerateErrorCorrection(
+  static absl::optional<WideString> GenerateErrorCorrection(
       const WideString& dataCodewords,
       int32_t errorCorrectionLevel);
 };
diff --git a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.cpp b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.cpp
index 09cac1b..7affbc8 100644
--- a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.cpp
+++ b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,6 +23,7 @@
 #include "fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h"
 
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_string.h"
 #include "third_party/bigint/BigIntegerLibrary.hh"
 
 namespace {
@@ -52,11 +53,11 @@
     -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 21, 27, 9,  -1};
 
 bool IsAlphaUpperOrSpace(wchar_t ch) {
-  return ch == ' ' || (ch >= 'A' && ch <= 'Z');
+  return ch == ' ' || FXSYS_IsUpperASCII(ch);
 }
 
 bool IsAlphaLowerOrSpace(wchar_t ch) {
-  return ch == ' ' || (ch >= 'a' && ch <= 'z');
+  return ch == ' ' || FXSYS_IsLowerASCII(ch);
 }
 
 bool IsMixed(wchar_t ch) {
@@ -76,20 +77,19 @@
 }  // namespace
 
 // static
-Optional<WideString> CBC_PDF417HighLevelEncoder::EncodeHighLevel(
+absl::optional<WideString> CBC_PDF417HighLevelEncoder::EncodeHighLevel(
     WideStringView msg) {
-  ByteString bytes = FX_UTF8Encode(msg);
+  const ByteString bytes = FX_UTF8Encode(msg);
   size_t len = bytes.GetLength();
   WideString result;
   result.Reserve(len);
   for (size_t i = 0; i < len; i++) {
     wchar_t ch = bytes[i] & 0xff;
     if (ch == '?' && bytes[i] != '?')
-      return {};
+      return absl::nullopt;
 
     result += ch;
   }
-  std::vector<uint8_t> byteArr(bytes.begin(), bytes.end());
   len = result.GetLength();
   WideString sb;
   sb.Reserve(len);
@@ -115,18 +115,18 @@
         textSubMode = EncodeText(result, p, t, textSubMode, &sb);
         p += t;
       } else {
-        Optional<size_t> b =
-            DetermineConsecutiveBinaryCount(result, &byteArr, p);
-        if (!b)
-          return {};
+        absl::optional<size_t> b =
+            DetermineConsecutiveBinaryCount(result, bytes.raw_span(), p);
+        if (!b.has_value())
+          return absl::nullopt;
 
         size_t b_value = b.value();
         if (b_value == 0)
           b_value = 1;
         if (b_value == 1 && encodingMode == EncodingMode::kText) {
-          EncodeBinary(byteArr, p, 1, EncodingMode::kText, &sb);
+          EncodeBinary(bytes.raw_span(), p, 1, EncodingMode::kText, &sb);
         } else {
-          EncodeBinary(byteArr, p, b_value, encodingMode, &sb);
+          EncodeBinary(bytes.raw_span(), p, b_value, encodingMode, &sb);
           encodingMode = EncodingMode::kByte;
           textSubMode = SubMode::kAlpha;
         }
@@ -352,9 +352,10 @@
   return idx - startpos;
 }
 
-Optional<size_t> CBC_PDF417HighLevelEncoder::DetermineConsecutiveBinaryCount(
+absl::optional<size_t>
+CBC_PDF417HighLevelEncoder::DetermineConsecutiveBinaryCount(
     WideString msg,
-    std::vector<uint8_t>* bytes,
+    pdfium::span<const uint8_t> bytes,
     size_t startpos) {
   size_t len = msg.GetLength();
   size_t idx = startpos;
@@ -382,8 +383,8 @@
     if (textCount >= 5)
       return idx - startpos;
     ch = msg[idx];
-    if ((*bytes)[idx] == 63 && ch != '?')
-      return {};
+    if (bytes[idx] == 63 && ch != '?')
+      return absl::nullopt;
     idx++;
   }
   return idx - startpos;
diff --git a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h
index df1cd54..6e8f034 100644
--- a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h
+++ b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,17 @@
 #ifndef FXBARCODE_PDF417_BC_PDF417HIGHLEVELENCODER_H_
 #define FXBARCODE_PDF417_BC_PDF417HIGHLEVELENCODER_H_
 
-#include <vector>
-
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 #include "fxbarcode/pdf417/BC_PDF417.h"
-#include "third_party/base/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/containers/span.h"
 
 class CBC_PDF417HighLevelEncoder {
  public:
   CBC_PDF417HighLevelEncoder() = delete;
   ~CBC_PDF417HighLevelEncoder() = delete;
 
-  static Optional<WideString> EncodeHighLevel(WideStringView msg);
+  static absl::optional<WideString> EncodeHighLevel(WideStringView msg);
 
  private:
   enum class EncodingMode { kUnknown = 0, kText, kByte, kNumeric };
@@ -42,9 +40,9 @@
                             WideString* sb);
   static size_t DetermineConsecutiveDigitCount(WideString msg, size_t startpos);
   static size_t DetermineConsecutiveTextCount(WideString msg, size_t startpos);
-  static Optional<size_t> DetermineConsecutiveBinaryCount(
+  static absl::optional<size_t> DetermineConsecutiveBinaryCount(
       WideString msg,
-      std::vector<uint8_t>* bytes,
+      pdfium::span<const uint8_t> bytes,
       size_t startpos);
 
   friend class PDF417HighLevelEncoderTest_ConsecutiveBinaryCount_Test;
diff --git a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder_unittest.cpp b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder_unittest.cpp
index 4ca5c8b..7f665ac 100644
--- a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder_unittest.cpp
+++ b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder_unittest.cpp
@@ -1,9 +1,11 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h"
 
+#include <vector>
+
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(PDF417HighLevelEncoderTest, EncodeHighLevel) {
@@ -37,11 +39,11 @@
       {L"0000000000000", L"\x0386\x000f\x00d9\x017b\x000b\x0064", 6},
   };
 
-  for (size_t i = 0; i < FX_ArraySize(kEncodeHighLevelCases); ++i) {
+  for (size_t i = 0; i < std::size(kEncodeHighLevelCases); ++i) {
     const EncodeHighLevelCase& testcase = kEncodeHighLevelCases[i];
     WideStringView input(testcase.input);
     WideString expected(testcase.expected, testcase.expected_length);
-    Optional<WideString> result =
+    absl::optional<WideString> result =
         CBC_PDF417HighLevelEncoder::EncodeHighLevel(input);
     ASSERT_TRUE(result.has_value());
     EXPECT_EQ(expected, result.value()) << " for case number " << i;
@@ -81,7 +83,7 @@
        L"\u039c\u00c9\u031f\u012a\u00d2\u02d0", 6},
   };
 
-  for (size_t i = 0; i < FX_ArraySize(kEncodeBinaryCases); ++i) {
+  for (size_t i = 0; i < std::size(kEncodeBinaryCases); ++i) {
     const EncodeBinaryCase& testcase = kEncodeBinaryCases[i];
     std::vector<uint8_t> input_array;
     size_t input_length = strlen(testcase.input);
@@ -148,7 +150,7 @@
        18},
   };
 
-  for (size_t i = 0; i < FX_ArraySize(kEncodeNumericCases); ++i) {
+  for (size_t i = 0; i < std::size(kEncodeNumericCases); ++i) {
     const EncodeNumericCase& testcase = kEncodeNumericCases[i];
     WideString input(testcase.input);
     WideString expected(testcase.expected, testcase.expected_length);
@@ -193,7 +195,7 @@
       {L"123FOO45678", 6, 5},
   };
 
-  for (size_t i = 0; i < FX_ArraySize(kConsecutiveDigitCases); ++i) {
+  for (size_t i = 0; i < std::size(kConsecutiveDigitCases); ++i) {
     const ConsecutiveDigitCase& testcase = kConsecutiveDigitCases[i];
     WideString input(testcase.input);
     int actual_count =
@@ -253,7 +255,7 @@
       {L"XXX121XXX12345678901234", 0, 9},
   };
 
-  for (size_t i = 0; i < FX_ArraySize(kConsecutiveTextCases); ++i) {
+  for (size_t i = 0; i < std::size(kConsecutiveTextCases); ++i) {
     const ConsecutiveTextCase& testcase = kConsecutiveTextCases[i];
     WideString input(testcase.input);
     int actual_count =
diff --git a/fxbarcode/pdf417/BC_PDF417Writer.cpp b/fxbarcode/pdf417/BC_PDF417Writer.cpp
index f9f3624..a7ed452 100644
--- a/fxbarcode/pdf417/BC_PDF417Writer.cpp
+++ b/fxbarcode/pdf417/BC_PDF417Writer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,14 +22,17 @@
 
 #include "fxbarcode/pdf417/BC_PDF417Writer.h"
 
+#include <stdint.h>
+
 #include <algorithm>
 #include <utility>
 
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/stl_util.h"
 #include "fxbarcode/BC_TwoDimWriter.h"
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 #include "fxbarcode/pdf417/BC_PDF417.h"
 #include "fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h"
-#include "third_party/base/stl_util.h"
 
 CBC_PDF417Writer::CBC_PDF417Writer() : CBC_TwoDimWriter(false) {}
 
@@ -43,10 +46,9 @@
   return true;
 }
 
-std::vector<uint8_t> CBC_PDF417Writer::Encode(WideStringView contents,
-                                              int32_t* pOutWidth,
-                                              int32_t* pOutHeight) {
-  std::vector<uint8_t> results;
+DataVector<uint8_t> CBC_PDF417Writer::Encode(WideStringView contents,
+                                             int32_t* pOutWidth,
+                                             int32_t* pOutHeight) {
   CBC_PDF417 encoder;
   int32_t col = (m_Width / m_ModuleWidth - 69) / 17;
   int32_t row = m_Height / (m_ModuleWidth * 20);
@@ -57,10 +59,10 @@
   else if (row >= 3 && row <= 90)
     encoder.setDimensions(30, 1, row, row);
   if (!encoder.GenerateBarcodeLogic(contents, error_correction_level()))
-    return results;
+    return DataVector<uint8_t>();
 
   CBC_BarcodeMatrix* barcodeMatrix = encoder.getBarcodeMatrix();
-  std::vector<uint8_t> matrixData = barcodeMatrix->toBitArray();
+  DataVector<uint8_t> matrixData = barcodeMatrix->toBitArray();
   int32_t matrixWidth = barcodeMatrix->getWidth();
   int32_t matrixHeight = barcodeMatrix->getHeight();
 
@@ -70,15 +72,13 @@
   }
   *pOutWidth = matrixWidth;
   *pOutHeight = matrixHeight;
-  results = pdfium::Vector2D<uint8_t>(*pOutWidth, *pOutHeight);
-  memcpy(results.data(), matrixData.data(), *pOutWidth * *pOutHeight);
-  return results;
+  return matrixData;
 }
 
-void CBC_PDF417Writer::RotateArray(std::vector<uint8_t>* bitarray,
+void CBC_PDF417Writer::RotateArray(DataVector<uint8_t>* bitarray,
                                    int32_t height,
                                    int32_t width) {
-  std::vector<uint8_t> temp = *bitarray;
+  DataVector<uint8_t> temp = *bitarray;
   for (int32_t ii = 0; ii < height; ii++) {
     int32_t inverseii = height - ii - 1;
     for (int32_t jj = 0; jj < width; jj++) {
diff --git a/fxbarcode/pdf417/BC_PDF417Writer.h b/fxbarcode/pdf417/BC_PDF417Writer.h
index 10f069a..dc1f1f4 100644
--- a/fxbarcode/pdf417/BC_PDF417Writer.h
+++ b/fxbarcode/pdf417/BC_PDF417Writer.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,11 @@
 #ifndef FXBARCODE_PDF417_BC_PDF417WRITER_H_
 #define FXBARCODE_PDF417_BC_PDF417WRITER_H_
 
-#include <vector>
+#include <stddef.h>
+#include <stdint.h>
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/widestring.h"
 #include "fxbarcode/BC_TwoDimWriter.h"
 
 class CBC_PDF417Writer final : public CBC_TwoDimWriter {
@@ -18,15 +19,15 @@
   CBC_PDF417Writer();
   ~CBC_PDF417Writer() override;
 
-  std::vector<uint8_t> Encode(WideStringView contents,
-                              int32_t* pOutWidth,
-                              int32_t* pOutHeight);
+  DataVector<uint8_t> Encode(WideStringView contents,
+                             int32_t* pOutWidth,
+                             int32_t* pOutHeight);
 
   // CBC_TwoDimWriter
   bool SetErrorCorrectionLevel(int32_t level) override;
 
  private:
-  void RotateArray(std::vector<uint8_t>* bitarray,
+  void RotateArray(DataVector<uint8_t>* bitarray,
                    int32_t width,
                    int32_t height);
 };
diff --git a/fxbarcode/pdf417/BC_PDF417Writer_unittest.cpp b/fxbarcode/pdf417/BC_PDF417Writer_unittest.cpp
index 1c09d78..d67d8b8 100644
--- a/fxbarcode/pdf417/BC_PDF417Writer_unittest.cpp
+++ b/fxbarcode/pdf417/BC_PDF417Writer_unittest.cpp
@@ -1,11 +1,12 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxbarcode/pdf417/BC_PDF417Writer.h"
 
-#include <vector>
+#include <stdint.h>
 
+#include "core/fxcrt/data_vector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 class CBC_PDF417WriterTest : public testing::Test {
@@ -413,11 +414,11 @@
         1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1,
         0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1,
         1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1};
-    std::vector<uint8_t> data = writer.Encode(L"", &width, &height);
-    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    DataVector<uint8_t> data = writer.Encode(L"", &width, &height);
+    ASSERT_EQ(std::size(kExpectedData), data.size());
     ASSERT_EQ(kExpectedWidth, width);
     ASSERT_EQ(kExpectedHeight, height);
-    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+    for (size_t i = 0; i < std::size(kExpectedData); ++i)
       EXPECT_EQ(kExpectedData[i], data[i]) << i;
   }
   {
@@ -810,11 +811,11 @@
         1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0,
         0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1,
         1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1};
-    std::vector<uint8_t> data = writer.Encode(L"hello world", &width, &height);
-    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    DataVector<uint8_t> data = writer.Encode(L"hello world", &width, &height);
+    ASSERT_EQ(std::size(kExpectedData), data.size());
     ASSERT_EQ(kExpectedWidth, width);
     ASSERT_EQ(kExpectedHeight, height);
-    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+    for (size_t i = 0; i < std::size(kExpectedData); ++i)
       EXPECT_EQ(kExpectedData[i], data[i]) << i;
   }
 }
diff --git a/fxbarcode/qrcode/BC_QRCodeWriter.cpp b/fxbarcode/qrcode/BC_QRCodeWriter.cpp
index 1eead62..8796ffb 100644
--- a/fxbarcode/qrcode/BC_QRCodeWriter.cpp
+++ b/fxbarcode/qrcode/BC_QRCodeWriter.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,6 +22,11 @@
 
 #include "fxbarcode/qrcode/BC_QRCodeWriter.h"
 
+#include <stdint.h>
+
+#include <memory>
+
+#include "core/fxcrt/data_vector.h"
 #include "fxbarcode/common/BC_CommonByteMatrix.h"
 #include "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h"
 #include "fxbarcode/qrcode/BC_QRCoder.h"
@@ -29,7 +34,6 @@
 #include "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h"
 #include "fxbarcode/qrcode/BC_QRCoderMode.h"
 #include "fxbarcode/qrcode/BC_QRCoderVersion.h"
-#include "third_party/base/stl_util.h"
 
 CBC_QRCodeWriter::CBC_QRCodeWriter() : CBC_TwoDimWriter(true) {}
 
@@ -43,11 +47,10 @@
   return true;
 }
 
-std::vector<uint8_t> CBC_QRCodeWriter::Encode(WideStringView contents,
-                                              int32_t ecLevel,
-                                              int32_t* pOutWidth,
-                                              int32_t* pOutHeight) {
-  std::vector<uint8_t> results;
+DataVector<uint8_t> CBC_QRCodeWriter::Encode(WideStringView contents,
+                                             int32_t ecLevel,
+                                             int32_t* pOutWidth,
+                                             int32_t* pOutHeight) {
   CBC_QRCoderErrorCorrectionLevel* ec = nullptr;
   switch (ecLevel) {
     case 0:
@@ -63,16 +66,14 @@
       ec = CBC_QRCoderErrorCorrectionLevel::H;
       break;
     default:
-      return results;
+      return DataVector<uint8_t>();
   }
   CBC_QRCoder qr;
   if (!CBC_QRCoderEncoder::Encode(contents, ec, &qr))
-    return results;
+    return DataVector<uint8_t>();
 
   *pOutWidth = qr.GetMatrixWidth();
   *pOutHeight = qr.GetMatrixWidth();
-  results = pdfium::Vector2D<uint8_t>(*pOutWidth, *pOutHeight);
-  memcpy(results.data(), qr.GetMatrix()->GetArray().data(),
-         *pOutWidth * *pOutHeight);
-  return results;
+  std::unique_ptr<CBC_CommonByteMatrix> matrix = qr.TakeMatrix();
+  return matrix->TakeArray();
 }
diff --git a/fxbarcode/qrcode/BC_QRCodeWriter.h b/fxbarcode/qrcode/BC_QRCodeWriter.h
index 3f2cca0..84a5525 100644
--- a/fxbarcode/qrcode/BC_QRCodeWriter.h
+++ b/fxbarcode/qrcode/BC_QRCodeWriter.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,10 @@
 #ifndef FXBARCODE_QRCODE_BC_QRCODEWRITER_H_
 #define FXBARCODE_QRCODE_BC_QRCODEWRITER_H_
 
-#include <vector>
+#include <stdint.h>
 
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/widestring.h"
 #include "fxbarcode/BC_TwoDimWriter.h"
 
 class CBC_QRCodeWriter final : public CBC_TwoDimWriter {
@@ -16,10 +18,10 @@
   CBC_QRCodeWriter();
   ~CBC_QRCodeWriter() override;
 
-  std::vector<uint8_t> Encode(WideStringView contents,
-                              int32_t ecLevel,
-                              int32_t* pOutWidth,
-                              int32_t* pOutHeight);
+  DataVector<uint8_t> Encode(WideStringView contents,
+                             int32_t ecLevel,
+                             int32_t* pOutWidth,
+                             int32_t* pOutHeight);
 
   // CBC_TwoDimWriter
   bool SetErrorCorrectionLevel(int32_t level) override;
diff --git a/fxbarcode/qrcode/BC_QRCodeWriter_unittest.cpp b/fxbarcode/qrcode/BC_QRCodeWriter_unittest.cpp
index 709d497..3136cb3 100644
--- a/fxbarcode/qrcode/BC_QRCodeWriter_unittest.cpp
+++ b/fxbarcode/qrcode/BC_QRCodeWriter_unittest.cpp
@@ -1,12 +1,12 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxbarcode/qrcode/BC_QRCodeWriter.h"
 
-#include <vector>
+#include <stdint.h>
 
-#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/data_vector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 class CBC_QRCodeWriterTest : public testing::Test {
@@ -51,11 +51,11 @@
         1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1
     };
     // clang-format on
-    std::vector<uint8_t> data = writer.Encode(L"", 0, &width, &height);
-    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    DataVector<uint8_t> data = writer.Encode(L"", 0, &width, &height);
+    ASSERT_EQ(std::size(kExpectedData), data.size());
     ASSERT_EQ(kExpectedDimension, width);
     ASSERT_EQ(kExpectedDimension, height);
-    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+    for (size_t i = 0; i < std::size(kExpectedData); ++i)
       EXPECT_EQ(kExpectedData[i], data[i]) << i;
   }
   {
@@ -85,11 +85,11 @@
         1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1
     };
     // clang-format on
-    std::vector<uint8_t> data = writer.Encode(L"", 1, &width, &height);
-    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    DataVector<uint8_t> data = writer.Encode(L"", 1, &width, &height);
+    ASSERT_EQ(std::size(kExpectedData), data.size());
     ASSERT_EQ(kExpectedDimension, width);
     ASSERT_EQ(kExpectedDimension, height);
-    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+    for (size_t i = 0; i < std::size(kExpectedData); ++i)
       EXPECT_EQ(kExpectedData[i], data[i]) << i;
   }
   {
@@ -119,11 +119,11 @@
         1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0
     };
     // clang-format on
-    std::vector<uint8_t> data = writer.Encode(L"", 2, &width, &height);
-    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    DataVector<uint8_t> data = writer.Encode(L"", 2, &width, &height);
+    ASSERT_EQ(std::size(kExpectedData), data.size());
     ASSERT_EQ(kExpectedDimension, width);
     ASSERT_EQ(kExpectedDimension, height);
-    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+    for (size_t i = 0; i < std::size(kExpectedData); ++i)
       EXPECT_EQ(kExpectedData[i], data[i]) << i;
   }
   {
@@ -153,11 +153,11 @@
         1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0
     };
     // clang-format on
-    std::vector<uint8_t> data = writer.Encode(L"", 3, &width, &height);
-    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    DataVector<uint8_t> data = writer.Encode(L"", 3, &width, &height);
+    ASSERT_EQ(std::size(kExpectedData), data.size());
     ASSERT_EQ(kExpectedDimension, width);
     ASSERT_EQ(kExpectedDimension, height);
-    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+    for (size_t i = 0; i < std::size(kExpectedData); ++i)
       EXPECT_EQ(kExpectedData[i], data[i]) << i;
   }
   {
@@ -187,12 +187,12 @@
         1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0
     };
     // clang-format on
-    std::vector<uint8_t> data =
+    DataVector<uint8_t> data =
         writer.Encode(L"hello world", 0, &width, &height);
-    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(std::size(kExpectedData), data.size());
     ASSERT_EQ(kExpectedDimension, width);
     ASSERT_EQ(kExpectedDimension, height);
-    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+    for (size_t i = 0; i < std::size(kExpectedData); ++i)
       EXPECT_EQ(kExpectedData[i], data[i]) << i;
   }
   {
@@ -222,12 +222,12 @@
         1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0
     };
     // clang-format on
-    std::vector<uint8_t> data =
+    DataVector<uint8_t> data =
         writer.Encode(L"hello world", 1, &width, &height);
-    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(std::size(kExpectedData), data.size());
     ASSERT_EQ(kExpectedDimension, width);
     ASSERT_EQ(kExpectedDimension, height);
-    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+    for (size_t i = 0; i < std::size(kExpectedData); ++i)
       EXPECT_EQ(kExpectedData[i], data[i]) << i;
   }
   {
@@ -260,12 +260,12 @@
         0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
         1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1,
         1};
-    std::vector<uint8_t> data =
+    DataVector<uint8_t> data =
         writer.Encode(L"hello world", 2, &width, &height);
-    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(std::size(kExpectedData), data.size());
     ASSERT_EQ(kExpectedDimension, width);
     ASSERT_EQ(kExpectedDimension, height);
-    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+    for (size_t i = 0; i < std::size(kExpectedData); ++i)
       EXPECT_EQ(kExpectedData[i], data[i]) << i;
   }
   {
@@ -298,12 +298,12 @@
         0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1,
         1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1,
         1};
-    std::vector<uint8_t> data =
+    DataVector<uint8_t> data =
         writer.Encode(L"hello world", 3, &width, &height);
-    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(std::size(kExpectedData), data.size());
     ASSERT_EQ(kExpectedDimension, width);
     ASSERT_EQ(kExpectedDimension, height);
-    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+    for (size_t i = 0; i < std::size(kExpectedData); ++i)
       EXPECT_EQ(kExpectedData[i], data[i]) << i;
   }
 }
diff --git a/fxbarcode/qrcode/BC_QRCoder.cpp b/fxbarcode/qrcode/BC_QRCoder.cpp
index 46f80fc..6525f1e 100644
--- a/fxbarcode/qrcode/BC_QRCoder.cpp
+++ b/fxbarcode/qrcode/BC_QRCoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -26,13 +26,14 @@
 #include "fxbarcode/qrcode/BC_QRCoder.h"
 #include "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h"
 #include "fxbarcode/qrcode/BC_QRCoderMode.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 CBC_QRCoder::CBC_QRCoder() = default;
 
 CBC_QRCoder::~CBC_QRCoder() = default;
 
 const CBC_QRCoderErrorCorrectionLevel* CBC_QRCoder::GetECLevel() const {
-  return m_ecLevel.Get();
+  return m_ecLevel;
 }
 
 int32_t CBC_QRCoder::GetVersion() const {
@@ -59,8 +60,8 @@
   return m_numRSBlocks;
 }
 
-const CBC_CommonByteMatrix* CBC_QRCoder::GetMatrix() const {
-  return m_matrix.get();
+std::unique_ptr<CBC_CommonByteMatrix> CBC_QRCoder::TakeMatrix() {
+  return std::move(m_matrix);
 }
 
 bool CBC_QRCoder::IsValid() const {
@@ -69,7 +70,8 @@
          m_numECBytes != -1 && m_numRSBlocks != -1 &&
          IsValidMaskPattern(m_maskPattern) &&
          m_numTotalBytes == m_numDataBytes + m_numECBytes && m_matrix &&
-         m_matrixWidth == m_matrix->GetWidth() &&
+         m_matrixWidth ==
+             pdfium::base::checked_cast<int32_t>(m_matrix->GetWidth()) &&
          m_matrix->GetWidth() == m_matrix->GetHeight();
 }
 
diff --git a/fxbarcode/qrcode/BC_QRCoder.h b/fxbarcode/qrcode/BC_QRCoder.h
index ec9bb09..e120b51 100644
--- a/fxbarcode/qrcode/BC_QRCoder.h
+++ b/fxbarcode/qrcode/BC_QRCoder.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,6 @@
 #include "core/fxcrt/unowned_ptr.h"
 
 class CBC_QRCoderErrorCorrectionLevel;
-class CBC_QRCoderMode;
 class CBC_CommonByteMatrix;
 
 class CBC_QRCoder final {
@@ -31,7 +30,7 @@
   int32_t GetNumTotalBytes() const;
   int32_t GetNumDataBytes() const;
   int32_t GetNumRSBlocks() const;
-  const CBC_CommonByteMatrix* GetMatrix() const;
+  std::unique_ptr<CBC_CommonByteMatrix> TakeMatrix();
 
   bool IsValid() const;
 
diff --git a/fxbarcode/qrcode/BC_QRCoderBitVector.cpp b/fxbarcode/qrcode/BC_QRCoderBitVector.cpp
index 121fa92..4561a15 100644
--- a/fxbarcode/qrcode/BC_QRCoderBitVector.cpp
+++ b/fxbarcode/qrcode/BC_QRCoderBitVector.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,7 +23,7 @@
 #include "fxbarcode/qrcode/BC_QRCoderBitVector.h"
 
 #include "core/fxcrt/fx_system.h"
-#include "third_party/base/logging.h"
+#include "third_party/base/check.h"
 
 CBC_QRCoderBitVector::CBC_QRCoderBitVector() = default;
 
@@ -44,7 +44,7 @@
 }
 
 void CBC_QRCoderBitVector::AppendBit(int32_t bit) {
-  ASSERT(bit == 0 || bit == 1);
+  DCHECK(bit == 0 || bit == 1);
   int32_t numBitsInLastByte = m_sizeInBits & 0x7;
   if (numBitsInLastByte == 0) {
     AppendByte(0);
@@ -55,8 +55,8 @@
 }
 
 void CBC_QRCoderBitVector::AppendBits(int32_t value, int32_t numBits) {
-  ASSERT(numBits > 0);
-  ASSERT(numBits <= 32);
+  DCHECK(numBits > 0);
+  DCHECK(numBits <= 32);
 
   int32_t numBitsLeft = numBits;
   while (numBitsLeft > 0) {
@@ -79,14 +79,14 @@
   if (m_sizeInBits != other->Size())
     return false;
 
-  const auto* pOther = other->GetArray();
+  pdfium::span<const uint8_t> other_span = other->GetArray();
   for (size_t i = 0; i < sizeInBytes(); ++i)
-    m_array[i] ^= pOther[i];
+    m_array[i] ^= other_span[i];
   return true;
 }
 
-const uint8_t* CBC_QRCoderBitVector::GetArray() const {
-  return m_array.data();
+pdfium::span<const uint8_t> CBC_QRCoderBitVector::GetArray() const {
+  return m_array;
 }
 
 void CBC_QRCoderBitVector::AppendByte(int8_t value) {
diff --git a/fxbarcode/qrcode/BC_QRCoderBitVector.h b/fxbarcode/qrcode/BC_QRCoderBitVector.h
index 9b8af81..51bbe7c 100644
--- a/fxbarcode/qrcode/BC_QRCoderBitVector.h
+++ b/fxbarcode/qrcode/BC_QRCoderBitVector.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,14 +10,15 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <vector>
+#include "core/fxcrt/data_vector.h"
+#include "third_party/base/containers/span.h"
 
 class CBC_QRCoderBitVector {
  public:
   CBC_QRCoderBitVector();
   ~CBC_QRCoderBitVector();
 
-  const uint8_t* GetArray() const;
+  pdfium::span<const uint8_t> GetArray() const;
   int32_t At(size_t index) const;
   size_t Size() const;
   size_t sizeInBytes() const;
@@ -31,7 +32,7 @@
   void AppendByte(int8_t value);
 
   size_t m_sizeInBits = 0;
-  std::vector<uint8_t> m_array;
+  DataVector<uint8_t> m_array;
 };
 
 #endif  // FXBARCODE_QRCODE_BC_QRCODERBITVECTOR_H_
diff --git a/fxbarcode/qrcode/BC_QRCoderECBlocks.cpp b/fxbarcode/qrcode/BC_QRCoderECBlocks.cpp
index 28f4688..a32c409 100644
--- a/fxbarcode/qrcode/BC_QRCoderECBlocks.cpp
+++ b/fxbarcode/qrcode/BC_QRCoderECBlocks.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -27,7 +27,7 @@
 CBC_QRCoderECBlocks::CBC_QRCoderECBlocks(const CBC_QRCoderECBlockData& data)
     : m_data(data) {}
 
-CBC_QRCoderECBlocks::~CBC_QRCoderECBlocks() {}
+CBC_QRCoderECBlocks::~CBC_QRCoderECBlocks() = default;
 
 int32_t CBC_QRCoderECBlocks::GetECCodeWordsPerBlock() const {
   return m_data.ecCodeWordsPerBlock;
diff --git a/fxbarcode/qrcode/BC_QRCoderECBlocks.h b/fxbarcode/qrcode/BC_QRCoderECBlocks.h
index 2583b12..2fd6a3f 100644
--- a/fxbarcode/qrcode/BC_QRCoderECBlocks.h
+++ b/fxbarcode/qrcode/BC_QRCoderECBlocks.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/qrcode/BC_QRCoderECBlocksData.cpp b/fxbarcode/qrcode/BC_QRCoderECBlocksData.cpp
index 26ff748..13b320e 100644
--- a/fxbarcode/qrcode/BC_QRCoderECBlocksData.cpp
+++ b/fxbarcode/qrcode/BC_QRCoderECBlocksData.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,7 +22,9 @@
 
 #include "fxbarcode/qrcode/BC_QRCoderECBlocksData.h"
 
-const CBC_QRCoderECBlockData g_ECBData[40][4] = {
+namespace fxbarcode {
+
+const CBC_QRCoderECBlockData kECBData[40][4] = {
     {{7, 1, 19, 0, 0}, {10, 1, 16, 0, 0}, {13, 1, 13, 0, 0}, {17, 1, 9, 0, 0}},
     {{10, 1, 34, 0, 0},
      {16, 1, 28, 0, 0},
@@ -178,3 +180,5 @@
      {30, 34, 24, 34, 25},
      {30, 20, 15, 61, 16}},
 };
+
+}  // namespace fxbarcode
diff --git a/fxbarcode/qrcode/BC_QRCoderECBlocksData.h b/fxbarcode/qrcode/BC_QRCoderECBlocksData.h
index 7ed4ca4..0597279 100644
--- a/fxbarcode/qrcode/BC_QRCoderECBlocksData.h
+++ b/fxbarcode/qrcode/BC_QRCoderECBlocksData.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,6 +17,10 @@
   uint8_t dataCodeWords2;
 };
 
-extern const CBC_QRCoderECBlockData g_ECBData[40][4];
+namespace fxbarcode {
+
+extern const CBC_QRCoderECBlockData kECBData[40][4];
+
+}  // namespace fxbarcode
 
 #endif  // FXBARCODE_QRCODE_BC_QRCODERECBLOCKSDATA_H_
diff --git a/fxbarcode/qrcode/BC_QRCoderEncoder.cpp b/fxbarcode/qrcode/BC_QRCoderEncoder.cpp
index be02f5b..b55255d 100644
--- a/fxbarcode/qrcode/BC_QRCoderEncoder.cpp
+++ b/fxbarcode/qrcode/BC_QRCoderEncoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,11 +22,16 @@
 
 #include "fxbarcode/qrcode/BC_QRCoderEncoder.h"
 
+#include <stdint.h>
+
 #include <algorithm>
 #include <memory>
 #include <utility>
 #include <vector>
 
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/span_util.h"
 #include "fxbarcode/common/BC_CommonByteMatrix.h"
 #include "fxbarcode/common/reedsolomon/BC_ReedSolomon.h"
 #include "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h"
@@ -37,8 +42,9 @@
 #include "fxbarcode/qrcode/BC_QRCoderMatrixUtil.h"
 #include "fxbarcode/qrcode/BC_QRCoderMode.h"
 #include "fxbarcode/qrcode/BC_QRCoderVersion.h"
-#include "third_party/base/optional.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 
 using ModeStringPair = std::pair<CBC_QRCoderMode*, ByteString>;
 
@@ -47,12 +53,12 @@
 CBC_ReedSolomonGF256* g_QRCodeField = nullptr;
 
 struct QRCoderBlockPair {
-  std::vector<uint8_t> data;
-  std::vector<uint8_t> ecc;
+  DataVector<uint8_t> data;
+  DataVector<uint8_t> ecc;
 };
 
 // This is a mapping for an ASCII table, starting at an index of 32.
-const int8_t g_alphaNumericTable[] = {
+const int8_t kAlphaNumericTable[] = {
     36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43,  // 32-47
     0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  44, -1, -1, -1, -1, -1,  // 48-63
     -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,  // 64-79
@@ -62,9 +68,9 @@
   if (code < 32)
     return -1;
   size_t code_index = static_cast<size_t>(code - 32);
-  if (code_index >= FX_ArraySize(g_alphaNumericTable))
+  if (code_index >= std::size(kAlphaNumericTable))
     return -1;
-  return g_alphaNumericTable[code_index];
+  return kAlphaNumericTable[code_index];
 }
 
 bool AppendNumericBytes(const ByteString& content, CBC_QRCoderBitVector* bits) {
@@ -113,55 +119,14 @@
   return true;
 }
 
-bool AppendGBKBytes(const ByteString& content, CBC_QRCoderBitVector* bits) {
-  size_t length = content.GetLength();
-  uint32_t value = 0;
-  for (size_t i = 0; i < length; i += 2) {
-    value = (uint32_t)(content[i] << 8 | content[i + 1]);
-    if (value <= 0xAAFE && value >= 0xA1A1)
-      value -= 0xA1A1;
-    else if (value <= 0xFAFE && value >= 0xB0A1)
-      value -= 0xA6A1;
-    else
-      return false;
-
-    value = (uint32_t)((value >> 8) * 0x60) + (uint32_t)(value & 0xff);
-    bits->AppendBits(value, 13);
-  }
-  return true;
-}
-
-bool Append8BitBytes(const ByteString& content,
-                     CBC_QRCoderBitVector* bits,
-                     ByteString encoding) {
-  for (size_t i = 0; i < content.GetLength(); i++)
-    bits->AppendBits(content[i], 8);
-  return true;
-}
-
-bool AppendKanjiBytes(const ByteString& content, CBC_QRCoderBitVector* bits) {
-  std::vector<uint8_t> bytes;
-  uint32_t value = 0;
-  // TODO(thestig): This is wrong, as |bytes| is empty.
-  for (size_t i = 0; i < bytes.size(); i += 2) {
-    value = (uint32_t)((content[i] << 8) | content[i + 1]);
-    if (value <= 0x9ffc && value >= 0x8140)
-      value -= 0x8140;
-    else if (value <= 0xebbf && value >= 0xe040)
-      value -= 0xc140;
-    else
-      return false;
-
-    value = (uint32_t)((value >> 8) * 0xc0) + (uint32_t)(value & 0xff);
-    bits->AppendBits(value, 13);
-  }
+bool Append8BitBytes(const ByteString& content, CBC_QRCoderBitVector* bits) {
+  for (char c : content)
+    bits->AppendBits(c, 8);
   return true;
 }
 
 void AppendModeInfo(CBC_QRCoderMode* mode, CBC_QRCoderBitVector* bits) {
   bits->AppendBits(mode->GetBits(), 4);
-  if (mode == CBC_QRCoderMode::sGBK)
-    bits->AppendBits(1, 4);
 }
 
 bool AppendLengthInfo(int32_t numLetters,
@@ -177,26 +142,19 @@
   if (numBits > ((1 << numBits) - 1))
     return true;
 
-  if (mode == CBC_QRCoderMode::sGBK)
-    bits->AppendBits(numLetters / 2, numBits);
   bits->AppendBits(numLetters, numBits);
   return true;
 }
 
 bool AppendBytes(const ByteString& content,
                  CBC_QRCoderMode* mode,
-                 CBC_QRCoderBitVector* bits,
-                 ByteString encoding) {
+                 CBC_QRCoderBitVector* bits) {
   if (mode == CBC_QRCoderMode::sNUMERIC)
     return AppendNumericBytes(content, bits);
   if (mode == CBC_QRCoderMode::sALPHANUMERIC)
     return AppendAlphaNumericBytes(content, bits);
   if (mode == CBC_QRCoderMode::sBYTE)
-    return Append8BitBytes(content, bits, encoding);
-  if (mode == CBC_QRCoderMode::sKANJI)
-    return AppendKanjiBytes(content, bits);
-  if (mode == CBC_QRCoderMode::sGBK)
-    return AppendGBKBytes(content, bits);
+    return Append8BitBytes(content, bits);
   return false;
 }
 
@@ -224,19 +182,19 @@
   return false;
 }
 
-std::vector<uint8_t> GenerateECBytes(pdfium::span<const uint8_t> dataBytes,
-                                     size_t numEcBytesInBlock) {
+DataVector<uint8_t> GenerateECBytes(pdfium::span<const uint8_t> dataBytes,
+                                    size_t numEcBytesInBlock) {
   // If |numEcBytesInBlock| is 0, the encoder will fail anyway.
-  ASSERT(numEcBytesInBlock > 0);
+  DCHECK(numEcBytesInBlock > 0);
   std::vector<int32_t> toEncode(dataBytes.size() + numEcBytesInBlock);
   std::copy(dataBytes.begin(), dataBytes.end(), toEncode.begin());
 
-  std::vector<uint8_t> ecBytes;
+  DataVector<uint8_t> ecBytes;
   CBC_ReedSolomonEncoder encoder(g_QRCodeField);
   if (encoder.Encode(&toEncode, numEcBytesInBlock)) {
-    ecBytes = std::vector<uint8_t>(toEncode.begin() + dataBytes.size(),
-                                   toEncode.end());
-    ASSERT(ecBytes.size() == static_cast<size_t>(numEcBytesInBlock));
+    ecBytes = DataVector<uint8_t>(toEncode.begin() + dataBytes.size(),
+                                  toEncode.end());
+    DCHECK_EQ(ecBytes.size(), static_cast<size_t>(numEcBytesInBlock));
   }
   return ecBytes;
 }
@@ -248,7 +206,7 @@
          CBC_QRCoderMaskUtil::ApplyMaskPenaltyRule4(matrix);
 }
 
-Optional<int32_t> ChooseMaskPattern(
+absl::optional<int32_t> ChooseMaskPattern(
     CBC_QRCoderBitVector* bits,
     const CBC_QRCoderErrorCorrectionLevel* ecLevel,
     int32_t version,
@@ -259,7 +217,7 @@
        maskPattern++) {
     if (!CBC_QRCoderMatrixUtil::BuildMatrix(bits, ecLevel, version, maskPattern,
                                             matrix)) {
-      return {};
+      return absl::nullopt;
     }
     int32_t penalty = CalculateMaskPenalty(matrix);
     if (penalty < minPenalty) {
@@ -320,10 +278,7 @@
   return bits->Size() == capacity;
 }
 
-CBC_QRCoderMode* ChooseMode(const ByteString& content, ByteString encoding) {
-  if (encoding.Compare("SHIFT_JIS") == 0)
-    return CBC_QRCoderMode::sKANJI;
-
+CBC_QRCoderMode* ChooseMode(const ByteString& content) {
   bool hasNumeric = false;
   bool hasAlphaNumeric = false;
   for (size_t i = 0; i < content.GetLength(); i++) {
@@ -347,8 +302,8 @@
                            int32_t numDataBytes,
                            int32_t numRSBlocks,
                            CBC_QRCoderBitVector* result) {
-  ASSERT(numTotalBytes >= 0);
-  ASSERT(numDataBytes >= 0);
+  DCHECK(numTotalBytes >= 0);
+  DCHECK(numDataBytes >= 0);
   if (bits->sizeInBytes() != static_cast<size_t>(numDataBytes))
     return false;
 
@@ -365,11 +320,11 @@
     if (numDataBytesInBlock < 0 || numEcBytesInBlock <= 0)
       return false;
 
-    std::vector<uint8_t> dataBytes(numDataBytesInBlock);
-    memcpy(dataBytes.data(), bits->GetArray() + dataBytesOffset,
-           numDataBytesInBlock);
-    std::vector<uint8_t> ecBytes =
-        GenerateECBytes(dataBytes, numEcBytesInBlock);
+    DataVector<uint8_t> dataBytes(numDataBytesInBlock);
+    fxcrt::spancpy(
+        pdfium::make_span(dataBytes),
+        bits->GetArray().subspan(dataBytesOffset, numDataBytesInBlock));
+    DataVector<uint8_t> ecBytes = GenerateECBytes(dataBytes, numEcBytesInBlock);
     if (ecBytes.empty())
       return false;
 
@@ -384,14 +339,14 @@
 
   for (size_t x = 0; x < maxNumDataBytes; x++) {
     for (size_t j = 0; j < blocks.size(); j++) {
-      const std::vector<uint8_t>& dataBytes = blocks[j].data;
+      const DataVector<uint8_t>& dataBytes = blocks[j].data;
       if (x < dataBytes.size())
         result->AppendBits(dataBytes[x], 8);
     }
   }
   for (size_t y = 0; y < maxNumEcBytes; y++) {
     for (size_t l = 0; l < blocks.size(); l++) {
-      const std::vector<uint8_t>& ecBytes = blocks[l].ecc;
+      const DataVector<uint8_t>& ecBytes = blocks[l].ecc;
       if (y < ecBytes.size())
         result->AppendBits(ecBytes[y], 8);
     }
@@ -417,11 +372,10 @@
 bool CBC_QRCoderEncoder::Encode(WideStringView content,
                                 const CBC_QRCoderErrorCorrectionLevel* ecLevel,
                                 CBC_QRCoder* qrCode) {
-  ByteString encoding = "utf8";
   ByteString utf8Data = FX_UTF8Encode(content);
-  CBC_QRCoderMode* mode = ChooseMode(utf8Data, encoding);
+  CBC_QRCoderMode* mode = ChooseMode(utf8Data);
   CBC_QRCoderBitVector dataBits;
-  if (!AppendBytes(utf8Data, mode, &dataBits, encoding))
+  if (!AppendBytes(utf8Data, mode, &dataBits))
     return false;
   int32_t numInputBytes = dataBits.sizeInBytes();
   if (!InitQRCode(numInputBytes, ecLevel, qrCode))
@@ -444,14 +398,14 @@
     return false;
   }
 
-  auto matrix = pdfium::MakeUnique<CBC_CommonByteMatrix>(
+  auto matrix = std::make_unique<CBC_CommonByteMatrix>(
       qrCode->GetMatrixWidth(), qrCode->GetMatrixWidth());
-  Optional<int32_t> maskPattern = ChooseMaskPattern(
+  absl::optional<int32_t> maskPattern = ChooseMaskPattern(
       &finalBits, qrCode->GetECLevel(), qrCode->GetVersion(), matrix.get());
-  if (!maskPattern)
+  if (!maskPattern.has_value())
     return false;
 
-  qrCode->SetMaskPattern(*maskPattern);
+  qrCode->SetMaskPattern(maskPattern.value());
   if (!CBC_QRCoderMatrixUtil::BuildMatrix(
           &finalBits, qrCode->GetECLevel(), qrCode->GetVersion(),
           qrCode->GetMaskPattern(), matrix.get())) {
diff --git a/fxbarcode/qrcode/BC_QRCoderEncoder.h b/fxbarcode/qrcode/BC_QRCoderEncoder.h
index a3af1fc..8677dd5 100644
--- a/fxbarcode/qrcode/BC_QRCoderEncoder.h
+++ b/fxbarcode/qrcode/BC_QRCoderEncoder.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #ifndef FXBARCODE_QRCODE_BC_QRCODERENCODER_H_
 #define FXBARCODE_QRCODE_BC_QRCODERENCODER_H_
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 
 class CBC_QRCoder;
 class CBC_QRCoderErrorCorrectionLevel;
diff --git a/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.cpp b/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.cpp
index a0e5c2b..4f89fba 100644
--- a/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.cpp
+++ b/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h b/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h
index 9a572a5..6cb8bb4 100644
--- a/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h
+++ b/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp b/fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp
index 9776ddc..10fac61 100644
--- a/fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp
+++ b/fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -25,6 +25,8 @@
 #include "fxbarcode/common/BC_CommonByteMatrix.h"
 #include "fxbarcode/qrcode/BC_QRCoder.h"
 #include "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h"
+#include "third_party/base/check.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
@@ -33,13 +35,13 @@
   int32_t penalty = 0;
   int32_t numSameBitCells = 0;
   int32_t prevBit = -1;
-  int32_t width = matrix->GetWidth();
-  int32_t height = matrix->GetHeight();
-  int32_t iLimit = isHorizontal ? height : width;
-  int32_t jLimit = isHorizontal ? width : height;
+  size_t width = matrix->GetWidth();
+  size_t height = matrix->GetHeight();
+  size_t iLimit = isHorizontal ? height : width;
+  size_t jLimit = isHorizontal ? width : height;
   pdfium::span<const uint8_t> array = matrix->GetArray();
-  for (int32_t i = 0; i < iLimit; ++i) {
-    for (int32_t j = 0; j < jLimit; ++j) {
+  for (size_t i = 0; i < iLimit; ++i) {
+    for (size_t j = 0; j < jLimit; ++j) {
       int32_t bit = isHorizontal ? array[i * width + j] : array[j * width + i];
       if (bit == prevBit) {
         numSameBitCells += 1;
@@ -72,10 +74,10 @@
     CBC_CommonByteMatrix* matrix) {
   int32_t penalty = 0;
   pdfium::span<const uint8_t> array = matrix->GetArray();
-  int32_t width = matrix->GetWidth();
-  int32_t height = matrix->GetHeight();
-  for (int32_t y = 0; y < height - 1; y++) {
-    for (int32_t x = 0; x < width - 1; x++) {
+  size_t width = matrix->GetWidth();
+  size_t height = matrix->GetHeight();
+  for (size_t y = 0; y + 1 < height; y++) {
+    for (size_t x = 0; x + 1 < width; x++) {
       int32_t value = array[y * width + x];
       if (value == array[y * width + x + 1] &&
           value == array[(y + 1) * width + x] &&
@@ -92,22 +94,20 @@
     CBC_CommonByteMatrix* matrix) {
   int32_t penalty = 0;
   pdfium::span<const uint8_t> array = matrix->GetArray();
-  int32_t width = matrix->GetWidth();
-  int32_t height = matrix->GetHeight();
-  for (int32_t y = 0; y < height; ++y) {
-    for (int32_t x = 0; x < width; ++x) {
-      if (x == 0 &&
-          ((y >= 0 && y <= 6) || (y >= height - 7 && y <= height - 1))) {
+  size_t width = matrix->GetWidth();
+  size_t height = matrix->GetHeight();
+  for (size_t y = 0; y < height; ++y) {
+    for (size_t x = 0; x < width; ++x) {
+      if (x == 0 && (y <= 6 || (y + 7 >= height && y + 1 <= height))) {
         continue;
       }
-      if (x == width - 7 && (y >= 0 && y <= 6)) {
+      if (x + 7 == width && y <= 6) {
         continue;
       }
-      if (y == 0 &&
-          ((x >= 0 && x <= 6) || (x >= width - 7 && x <= width - 1))) {
+      if (y == 0 && (x <= 6 || (x + 7 >= width && x + 1 <= width))) {
         continue;
       }
-      if (y == height - 7 && (x >= 0 && x <= 6)) {
+      if (y + 7 == height && x <= 6) {
         continue;
       }
       if (x + 6 < width && array[y * width + x] == 1 &&
@@ -117,7 +117,7 @@
           ((x + 10 < width && array[y * width + x + 7] == 0 &&
             array[y * width + x + 8] == 0 && array[y * width + x + 9] == 0 &&
             array[y * width + x + 10] == 0) ||
-           (x - 4 >= 0 && array[y * width + x - 1] == 0 &&
+           (x >= 4 && array[y * width + x - 1] == 0 &&
             array[y * width + x - 2] == 0 && array[y * width + x - 3] == 0 &&
             array[y * width + x - 4] == 0))) {
         penalty += 40;
@@ -130,7 +130,7 @@
             array[(y + 8) * width + x] == 0 &&
             array[(y + 9) * width + x] == 0 &&
             array[(y + 10) * width + x] == 0) ||
-           (y - 4 >= 0 && array[(y - 1) * width + x] == 0 &&
+           (y >= 4 && array[(y - 1) * width + x] == 0 &&
             array[(y - 2) * width + x] == 0 &&
             array[(y - 3) * width + x] == 0 &&
             array[(y - 4) * width + x] == 0))) {
@@ -146,15 +146,15 @@
     CBC_CommonByteMatrix* matrix) {
   int32_t numDarkCells = 0;
   pdfium::span<const uint8_t> array = matrix->GetArray();
-  int32_t width = matrix->GetWidth();
-  int32_t height = matrix->GetHeight();
-  for (int32_t y = 0; y < height; ++y) {
-    for (int32_t x = 0; x < width; ++x) {
+  size_t width = matrix->GetWidth();
+  size_t height = matrix->GetHeight();
+  for (size_t y = 0; y < height; ++y) {
+    for (size_t x = 0; x < width; ++x) {
       if (array[y * width + x] == 1)
         numDarkCells += 1;
     }
   }
-  int32_t numTotalCells = matrix->GetHeight() * matrix->GetWidth();
+  size_t numTotalCells = matrix->GetHeight() * matrix->GetWidth();
   double darkRatio = static_cast<double>(numDarkCells) / numTotalCells;
   return abs(static_cast<int32_t>(darkRatio * 100 - 50) / 5) * 5 * 10;
 }
@@ -163,9 +163,10 @@
 bool CBC_QRCoderMaskUtil::GetDataMaskBit(int32_t maskPattern,
                                          int32_t x,
                                          int32_t y) {
-  ASSERT(CBC_QRCoder::IsValidMaskPattern(maskPattern));
+  DCHECK(CBC_QRCoder::IsValidMaskPattern(maskPattern));
 
-  int32_t intermediate = 0, temp = 0;
+  int32_t intermediate = 0;
+  int32_t temp = 0;
   switch (maskPattern) {
     case 0:
       intermediate = (y + x) & 0x1;
@@ -195,8 +196,7 @@
       intermediate = (((temp % 3) + ((y + x) & 0x1)) & 0x1);
       break;
     default:
-      NOTREACHED();
-      return false;
+      NOTREACHED_NORETURN();
   }
   return intermediate == 0;
 }
diff --git a/fxbarcode/qrcode/BC_QRCoderMaskUtil.h b/fxbarcode/qrcode/BC_QRCoderMaskUtil.h
index b2da2a8..e741537 100644
--- a/fxbarcode/qrcode/BC_QRCoderMaskUtil.h
+++ b/fxbarcode/qrcode/BC_QRCoderMaskUtil.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/qrcode/BC_QRCoderMatrixUtil.cpp b/fxbarcode/qrcode/BC_QRCoderMatrixUtil.cpp
index 2c6b051..60fcd51 100644
--- a/fxbarcode/qrcode/BC_QRCoderMatrixUtil.cpp
+++ b/fxbarcode/qrcode/BC_QRCoderMatrixUtil.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,12 +22,15 @@
 
 #include "fxbarcode/qrcode/BC_QRCoderMatrixUtil.h"
 
-#include "core/fxcrt/fx_memory.h"
+#include <iterator>
+
 #include "fxbarcode/common/BC_CommonByteMatrix.h"
 #include "fxbarcode/qrcode/BC_QRCoder.h"
 #include "fxbarcode/qrcode/BC_QRCoderBitVector.h"
 #include "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h"
 #include "fxbarcode/qrcode/BC_QRCoderMaskUtil.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 
 namespace {
 
@@ -104,7 +107,7 @@
     if (x == 6)
       x -= 1;
 
-    while (y >= 0 && y < matrix->GetHeight()) {
+    while (y >= 0 && y < static_cast<int32_t>(matrix->GetHeight())) {
       if (y == 6) {
         y += direction;
         continue;
@@ -121,7 +124,7 @@
         } else {
           bit = 0;
         }
-        ASSERT(CBC_QRCoder::IsValidMaskPattern(maskPattern));
+        DCHECK(CBC_QRCoder::IsValidMaskPattern(maskPattern));
         if (CBC_QRCoderMaskUtil::GetDataMaskBit(maskPattern, xx, y))
           bit ^= 0x01;
         matrix->Set(xx, y, bit);
@@ -159,7 +162,7 @@
   if (!bits->XOR(&maskBits))
     return false;
 
-  ASSERT(bits->Size() == 15);
+  DCHECK_EQ(bits->Size(), 15);
   return true;
 }
 
@@ -167,7 +170,7 @@
   bits->AppendBits(version, 6);
   int32_t bchCode = CalculateBCHCode(version, VERSION_INFO_POLY);
   bits->AppendBits(bchCode, 12);
-  ASSERT(bits->Size() == 18);
+  DCHECK_EQ(bits->Size(), 18);
 }
 
 bool EmbedTypeInfo(const CBC_QRCoderErrorCorrectionLevel* ecLevel,
@@ -213,8 +216,8 @@
 }
 
 bool EmbedTimingPatterns(CBC_CommonByteMatrix* matrix) {
-  for (int32_t i = 8; i < matrix->GetWidth() - 8; i++) {
-    int32_t bit = (i + 1) % 2;
+  for (size_t i = 8; i + 8 < matrix->GetWidth(); i++) {
+    const uint8_t bit = static_cast<uint8_t>((i + 1) % 2);
     if (!IsValidValue(matrix->Get(i, 6)))
       return false;
 
@@ -331,7 +334,7 @@
     return true;
 
   const size_t index = version - 2;
-  if (index >= FX_ArraySize(kPositionAdjustmentPatternCoordinates))
+  if (index >= std::size(kPositionAdjustmentPatternCoordinates))
     return false;
 
   const auto* coordinates = &kPositionAdjustmentPatternCoordinates[index][0];
@@ -376,7 +379,7 @@
   if (!dataBits || !matrix)
     return false;
 
-  matrix->clear(0xff);
+  matrix->Fill(0xff);
 
   if (!EmbedBasicPatterns(version, matrix))
     return false;
diff --git a/fxbarcode/qrcode/BC_QRCoderMatrixUtil.h b/fxbarcode/qrcode/BC_QRCoderMatrixUtil.h
index 4bef5e1..bf498eb 100644
--- a/fxbarcode/qrcode/BC_QRCoderMatrixUtil.h
+++ b/fxbarcode/qrcode/BC_QRCoderMatrixUtil.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/qrcode/BC_QRCoderMode.cpp b/fxbarcode/qrcode/BC_QRCoderMode.cpp
index 8648f8c..2e1585d 100644
--- a/fxbarcode/qrcode/BC_QRCoderMode.cpp
+++ b/fxbarcode/qrcode/BC_QRCoderMode.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -25,17 +25,11 @@
 #include <utility>
 
 #include "core/fxcrt/fx_system.h"
+#include "third_party/base/check.h"
 
 CBC_QRCoderMode* CBC_QRCoderMode::sBYTE = nullptr;
 CBC_QRCoderMode* CBC_QRCoderMode::sNUMERIC = nullptr;
 CBC_QRCoderMode* CBC_QRCoderMode::sALPHANUMERIC = nullptr;
-CBC_QRCoderMode* CBC_QRCoderMode::sKANJI = nullptr;
-CBC_QRCoderMode* CBC_QRCoderMode::sECI = nullptr;
-CBC_QRCoderMode* CBC_QRCoderMode::sGBK = nullptr;
-CBC_QRCoderMode* CBC_QRCoderMode::sTERMINATOR = nullptr;
-CBC_QRCoderMode* CBC_QRCoderMode::sFNC1_FIRST_POSITION = nullptr;
-CBC_QRCoderMode* CBC_QRCoderMode::sFNC1_SECOND_POSITION = nullptr;
-CBC_QRCoderMode* CBC_QRCoderMode::sSTRUCTURED_APPEND = nullptr;
 
 CBC_QRCoderMode::CBC_QRCoderMode(std::vector<int32_t> charCountBits,
                                  int32_t bits)
@@ -46,14 +40,7 @@
 void CBC_QRCoderMode::Initialize() {
   sBYTE = new CBC_QRCoderMode({8, 16, 16}, 0x4);
   sALPHANUMERIC = new CBC_QRCoderMode({9, 11, 13}, 0x2);
-  sECI = new CBC_QRCoderMode(std::vector<int32_t>(), 0x7);
-  sKANJI = new CBC_QRCoderMode({8, 10, 12}, 0x8);
   sNUMERIC = new CBC_QRCoderMode({10, 12, 14}, 0x1);
-  sGBK = new CBC_QRCoderMode({8, 10, 12}, 0x0D);
-  sTERMINATOR = new CBC_QRCoderMode(std::vector<int32_t>(), 0x00);
-  sFNC1_FIRST_POSITION = new CBC_QRCoderMode(std::vector<int32_t>(), 0x05);
-  sFNC1_SECOND_POSITION = new CBC_QRCoderMode(std::vector<int32_t>(), 0x09);
-  sSTRUCTURED_APPEND = new CBC_QRCoderMode(std::vector<int32_t>(), 0x03);
 }
 
 void CBC_QRCoderMode::Finalize() {
@@ -61,22 +48,8 @@
   sBYTE = nullptr;
   delete sALPHANUMERIC;
   sALPHANUMERIC = nullptr;
-  delete sECI;
-  sECI = nullptr;
-  delete sKANJI;
-  sKANJI = nullptr;
   delete sNUMERIC;
   sNUMERIC = nullptr;
-  delete sGBK;
-  sGBK = nullptr;
-  delete sTERMINATOR;
-  sTERMINATOR = nullptr;
-  delete sFNC1_FIRST_POSITION;
-  sFNC1_FIRST_POSITION = nullptr;
-  delete sFNC1_SECOND_POSITION;
-  sFNC1_SECOND_POSITION = nullptr;
-  delete sSTRUCTURED_APPEND;
-  sSTRUCTURED_APPEND = nullptr;
 }
 
 int32_t CBC_QRCoderMode::GetBits() const {
@@ -96,6 +69,6 @@
     offset = 2;
 
   int32_t result = m_characterCountBitsForVersions[offset];
-  ASSERT(result != 0);
+  DCHECK(result != 0);
   return result;
 }
diff --git a/fxbarcode/qrcode/BC_QRCoderMode.h b/fxbarcode/qrcode/BC_QRCoderMode.h
index b61d751..0e12d7d 100644
--- a/fxbarcode/qrcode/BC_QRCoderMode.h
+++ b/fxbarcode/qrcode/BC_QRCoderMode.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -24,13 +24,6 @@
   static CBC_QRCoderMode* sBYTE;
   static CBC_QRCoderMode* sNUMERIC;
   static CBC_QRCoderMode* sALPHANUMERIC;
-  static CBC_QRCoderMode* sKANJI;
-  static CBC_QRCoderMode* sECI;
-  static CBC_QRCoderMode* sGBK;
-  static CBC_QRCoderMode* sTERMINATOR;
-  static CBC_QRCoderMode* sFNC1_FIRST_POSITION;
-  static CBC_QRCoderMode* sFNC1_SECOND_POSITION;
-  static CBC_QRCoderMode* sSTRUCTURED_APPEND;
 
  private:
   CBC_QRCoderMode(std::vector<int32_t> charCountBits, int32_t bits);
diff --git a/fxbarcode/qrcode/BC_QRCoderVersion.cpp b/fxbarcode/qrcode/BC_QRCoderVersion.cpp
index 9263ee7..aa7c2c8 100644
--- a/fxbarcode/qrcode/BC_QRCoderVersion.cpp
+++ b/fxbarcode/qrcode/BC_QRCoderVersion.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -29,7 +29,6 @@
 #include "fxbarcode/qrcode/BC_QRCoderBitVector.h"
 #include "fxbarcode/qrcode/BC_QRCoderECBlocksData.h"
 #include "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -40,10 +39,10 @@
 CBC_QRCoderVersion::CBC_QRCoderVersion(int32_t versionNumber,
                                        const CBC_QRCoderECBlockData data[4])
     : m_versionNumber(versionNumber) {
-  m_ecBlocksArray[0] = pdfium::MakeUnique<CBC_QRCoderECBlocks>(data[0]);
-  m_ecBlocksArray[1] = pdfium::MakeUnique<CBC_QRCoderECBlocks>(data[1]);
-  m_ecBlocksArray[2] = pdfium::MakeUnique<CBC_QRCoderECBlocks>(data[2]);
-  m_ecBlocksArray[3] = pdfium::MakeUnique<CBC_QRCoderECBlocks>(data[3]);
+  m_ecBlocksArray[0] = std::make_unique<CBC_QRCoderECBlocks>(data[0]);
+  m_ecBlocksArray[1] = std::make_unique<CBC_QRCoderECBlocks>(data[1]);
+  m_ecBlocksArray[2] = std::make_unique<CBC_QRCoderECBlocks>(data[2]);
+  m_ecBlocksArray[3] = std::make_unique<CBC_QRCoderECBlocks>(data[3]);
   m_totalCodeWords = m_ecBlocksArray[0]->GetTotalDataCodeWords();
 }
 
@@ -66,7 +65,7 @@
   if (g_VERSION->empty()) {
     for (int i = 0; i < kMaxVersion; ++i) {
       g_VERSION->push_back(
-          pdfium::MakeUnique<CBC_QRCoderVersion>(i + 1, g_ECBData[i]));
+          std::make_unique<CBC_QRCoderVersion>(i + 1, fxbarcode::kECBData[i]));
     }
   }
   if (versionNumber < 1 || versionNumber > kMaxVersion)
diff --git a/fxbarcode/qrcode/BC_QRCoderVersion.h b/fxbarcode/qrcode/BC_QRCoderVersion.h
index b94a9e7..76ad09a 100644
--- a/fxbarcode/qrcode/BC_QRCoderVersion.h
+++ b/fxbarcode/qrcode/BC_QRCoderVersion.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxbarcode/utils.h b/fxbarcode/utils.h
deleted file mode 100644
index fe3caa6..0000000
--- a/fxbarcode/utils.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXBARCODE_UTILS_H_
-#define FXBARCODE_UTILS_H_
-
-enum BCFORMAT {
-  BCFORMAT_UNSPECIFY = -1,
-  BCFORMAT_CODABAR,
-  BCFORMAT_CODE_39,
-  BCFORMAT_CODE_128,
-  BCFORMAT_CODE_128B,
-  BCFORMAT_CODE_128C,
-  BCFORMAT_EAN_8,
-  BCFORMAT_UPC_A,
-  BCFORMAT_EAN_13,
-  BCFORMAT_PDF_417,
-  BCFORMAT_DATAMATRIX,
-  BCFORMAT_QR_CODE
-};
-
-#endif  // FXBARCODE_UTILS_H_
diff --git a/fxjs/Android.bp b/fxjs/Android.bp
index 2dc3861..a3a03d9 100644
--- a/fxjs/Android.bp
+++ b/fxjs/Android.bp
@@ -13,11 +13,8 @@
 
     visibility: ["//external/pdfium:__subpackages__"],
 
-    header_libs: [
-        "libpdfium-constants",
-    ],
-
     static_libs: [
+        "libpdfium-constants",
         "libpdfium-fdrm",
         "libpdfium-page",
         "libpdfium-parser",
diff --git a/fxjs/BUILD.gn b/fxjs/BUILD.gn
index 2fb819c..e3f62ff 100644
--- a/fxjs/BUILD.gn
+++ b/fxjs/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -15,8 +15,9 @@
     "ijs_runtime.cpp",
     "ijs_runtime.h",
   ]
-  configs += [ "../:pdfium_core_config" ]
+  configs += [ "../:pdfium_strict_config" ]
   deps = [ "../core/fxcrt" ]
+  public_deps = []
   visibility = [ "../*" ]
 
   if (pdf_enable_v8) {
@@ -27,6 +28,8 @@
       "cfx_keyvalue.h",
       "cfx_v8.cpp",
       "cfx_v8.h",
+      "cfx_v8_array_buffer_allocator.cpp",
+      "cfx_v8_array_buffer_allocator.h",
       "cfxjs_engine.cpp",
       "cfxjs_engine.h",
       "cjs_annot.cpp",
@@ -49,8 +52,6 @@
       "cjs_event.h",
       "cjs_event_context.cpp",
       "cjs_event_context.h",
-      "cjs_eventrecorder.cpp",
-      "cjs_eventrecorder.h",
       "cjs_field.cpp",
       "cjs_field.h",
       "cjs_font.cpp",
@@ -89,6 +90,8 @@
       "cjs_zoomtype.h",
       "fx_date_helpers.cpp",
       "fx_date_helpers.h",
+      "fxv8.cpp",
+      "fxv8.h",
       "global_timer.cpp",
       "global_timer.h",
       "js_define.cpp",
@@ -108,12 +111,13 @@
       "//v8:v8_libplatform",
     ]
     configs += [ "//v8:external_startup_data" ]
-    public_deps = [ "//v8" ]
+    public_deps += [
+      "../core/fxcrt",
+      "//v8",
+    ]
 
     if (pdf_enable_xfa) {
       sources += [
-        "xfa/cfxjse_arguments.cpp",
-        "xfa/cfxjse_arguments.h",
         "xfa/cfxjse_class.cpp",
         "xfa/cfxjse_class.h",
         "xfa/cfxjse_context.cpp",
@@ -124,6 +128,10 @@
         "xfa/cfxjse_formcalc_context.h",
         "xfa/cfxjse_isolatetracker.cpp",
         "xfa/cfxjse_isolatetracker.h",
+        "xfa/cfxjse_mapmodule.cpp",
+        "xfa/cfxjse_mapmodule.h",
+        "xfa/cfxjse_nodehelper.cpp",
+        "xfa/cfxjse_nodehelper.h",
         "xfa/cfxjse_resolveprocessor.cpp",
         "xfa/cfxjse_resolveprocessor.h",
         "xfa/cfxjse_runtimedata.cpp",
@@ -203,27 +211,65 @@
         "xfa/jse_define.h",
       ]
       deps += [
-        "../xfa/fgas",
-        "../xfa/fxfa/fm2js",
+        ":gc",
+        "../xfa/fgas/crt",
+        "../xfa/fxfa/formcalc",
       ]
     }
   }
 }
 
 if (pdf_enable_v8) {
+  if (pdf_enable_xfa) {
+    source_set("gc") {
+      sources = [
+        "gc/container_trace.h",
+        "gc/gced_tree_node.h",
+        "gc/gced_tree_node_mixin.h",
+        "gc/heap.cpp",
+        "gc/heap.h",
+      ]
+      configs += [ "../:pdfium_strict_config" ]
+      deps = [
+        "../core/fxcrt",
+        "//v8:v8_libplatform",
+      ]
+      public_deps = [ "//v8:cppgc" ]
+    }
+  }
+}
+
+if (pdf_enable_v8) {
   pdfium_unittest_source_set("unittests") {
     sources = [
       "cfx_globaldata_unittest.cpp",
       "cfx_v8_unittest.cpp",
-      "cfx_v8_unittest.h",
       "cfxjs_engine_unittest.cpp",
       "cjs_publicmethods_unittest.cpp",
       "cjs_util_unittest.cpp",
       "fx_date_helpers_unittest.cpp",
     ]
     configs = [ "//v8:external_startup_data" ]
-    deps = [ ":fxjs" ]
+    deps = [
+      ":fxjs",
+      "../core/fxcrt:unit_test_support",
+    ]
     pdfium_root_dir = "../"
+    if (pdf_enable_xfa) {
+      sources += [
+        "gc/container_trace_unittest.cpp",
+        "gc/gced_tree_node_mixin_unittest.cpp",
+        "gc/gced_tree_node_unittest.cpp",
+        "gc/heap_unittest.cpp",
+        "gc/move_unittest.cpp",
+        "xfa/cfxjse_formcalc_context_unittest.cpp",
+        "xfa/cfxjse_mapmodule_unittest.cpp",
+      ]
+      deps += [
+        ":gc",
+        "../xfa/fxfa/parser",
+      ]
+    }
   }
 
   pdfium_embeddertest_source_set("embeddertests") {
@@ -237,7 +283,6 @@
       "../fpdfsdk",
     ]
     pdfium_root_dir = "../"
-
     if (pdf_enable_xfa) {
       sources += [
         "xfa/cfxjse_app_embeddertest.cpp",
@@ -245,8 +290,12 @@
         "xfa/cfxjse_value_embeddertest.cpp",
         "xfa/cjx_hostpseudomodel_embeddertest.cpp",
         "xfa/cjx_list_embeddertest.cpp",
+        "xfa/cjx_object_embeddertest.cpp",
       ]
-      deps += [ "../xfa/fxfa" ]
+      deps += [
+        ":gc",
+        "../xfa/fxfa",
+      ]
     }
   }
 }
diff --git a/fxjs/README b/fxjs/README
index a1cfe32..59b55a2 100644
--- a/fxjs/README
+++ b/fxjs/README
@@ -20,20 +20,20 @@
 objects, regardless of the FXJS/FXJSE distinction.  Slot 0 is the
 tag and contains either:
   kPerObjectDataTag for FXJS objects, or
-  g_FXJSEHostObjectTag for FXJSE Host objects, or
-  g_FXJSEProxyObjectTag for a global proxy object under FXJSE, or
+  kFXJSEHostObjectTag for FXJSE Host objects, or
+  kFXJSEProxyObjectTag for a global proxy object under FXJSE, or
   One of 4 specific FXJSE_CLASS_DESCRIPTOR globals for FXJSE classes:
-    GlobalClassDescriptor
-    NormalClassDescriptor
-    VariablesClassDescriptor
-    formcalc_fm2js_descriptor
+    kGlobalClassDescriptor
+    kNormalClassDescriptor
+    kVariablesClassDescriptor
+    kFormCalcDescriptor
 
 Slot 1's contents are determined by these tags:
   kPerObjectDataTag means an aligned pointer to CFXJS_PerObjectData.
-  g_FXJSEHostObjectTag means an aligned pointer to CFXJSE_HostObject.
-  g_FXJSEProxyObjectTag means nullptr, and to check the prototype instead.
+  kFXJSEHostObjectTag means an aligned pointer to CFXJSE_HostObject.
+  kFXJSEProxyObjectTag means nullptr, and to check the prototype instead.
   A FXJSE_CLASS_DESCRIPTOR pointer means to expect an actual v8 function
-  object (or a string naming that function),  and not an aligned pointer.
+  object (or a string naming that function), and not an aligned pointer.
 
 Because PDFium uses V8 for various unrelated purposes, there may be up to
 four v8::Contexts (JS Global Objects) associated with each document. One is
diff --git a/fxjs/cfx_globaldata.cpp b/fxjs/cfx_globaldata.cpp
index 9131f02..6d6efd5 100644
--- a/fxjs/cfx_globaldata.cpp
+++ b/fxjs/cfx_globaldata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,8 +9,8 @@
 #include <utility>
 
 #include "core/fdrm/fx_crypt.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
 
@@ -40,45 +40,41 @@
 
 void MakeNameTypeString(const ByteString& name,
                         CFX_Value::DataType eType,
-                        CFX_BinaryBuf* result) {
-  uint32_t dwNameLen = (uint32_t)name.GetLength();
-  result->AppendBlock(&dwNameLen, sizeof(uint32_t));
+                        BinaryBuffer* result) {
+  uint32_t dwNameLen = pdfium::base::checked_cast<uint32_t>(name.GetLength());
+  result->AppendUint32(dwNameLen);
   result->AppendString(name);
-
-  uint16_t wType = static_cast<uint16_t>(eType);
-  result->AppendBlock(&wType, sizeof(uint16_t));
+  result->AppendUint16(static_cast<uint16_t>(eType));
 }
 
 bool MakeByteString(const ByteString& name,
                     const CFX_KeyValue& pData,
-                    CFX_BinaryBuf* result) {
+                    BinaryBuffer* result) {
   switch (pData.nType) {
-    case CFX_Value::DataType::NUMBER: {
+    case CFX_Value::DataType::kNumber: {
       MakeNameTypeString(name, pData.nType, result);
-      double dData = pData.dData;
-      result->AppendBlock(&dData, sizeof(double));
+      result->AppendDouble(pData.dData);
       return true;
     }
-    case CFX_Value::DataType::BOOLEAN: {
+    case CFX_Value::DataType::kBoolean: {
       MakeNameTypeString(name, pData.nType, result);
-      uint16_t wData = static_cast<uint16_t>(pData.bData);
-      result->AppendBlock(&wData, sizeof(uint16_t));
+      result->AppendUint16(static_cast<uint16_t>(pData.bData));
       return true;
     }
-    case CFX_Value::DataType::STRING: {
+    case CFX_Value::DataType::kString: {
       MakeNameTypeString(name, pData.nType, result);
-      uint32_t dwDataLen = (uint32_t)pData.sData.GetLength();
-      result->AppendBlock(&dwDataLen, sizeof(uint32_t));
+      uint32_t dwDataLen =
+          pdfium::base::checked_cast<uint32_t>(pData.sData.GetLength());
+      result->AppendUint32(dwDataLen);
       result->AppendString(pData.sData);
       return true;
     }
-    case CFX_Value::DataType::NULLOBJ: {
+    case CFX_Value::DataType::kNull: {
       MakeNameTypeString(name, pData.nType, result);
       return true;
     }
     // Arrays don't get persisted per JS spec page 484.
-    case CFX_Value::DataType::OBJECT:
-    default:
+    case CFX_Value::DataType::kObject:
       break;
   }
   return false;
@@ -135,13 +131,13 @@
 
   CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
   if (pData) {
-    pData->data.nType = CFX_Value::DataType::NUMBER;
+    pData->data.nType = CFX_Value::DataType::kNumber;
     pData->data.dData = dData;
     return;
   }
-  auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
+  auto pNewData = std::make_unique<CFX_GlobalData::Element>();
   pNewData->data.sKey = std::move(sPropName);
-  pNewData->data.nType = CFX_Value::DataType::NUMBER;
+  pNewData->data.nType = CFX_Value::DataType::kNumber;
   pNewData->data.dData = dData;
   m_arrayGlobalData.push_back(std::move(pNewData));
 }
@@ -153,13 +149,13 @@
 
   CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
   if (pData) {
-    pData->data.nType = CFX_Value::DataType::BOOLEAN;
+    pData->data.nType = CFX_Value::DataType::kBoolean;
     pData->data.bData = bData;
     return;
   }
-  auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
+  auto pNewData = std::make_unique<CFX_GlobalData::Element>();
   pNewData->data.sKey = std::move(sPropName);
-  pNewData->data.nType = CFX_Value::DataType::BOOLEAN;
+  pNewData->data.nType = CFX_Value::DataType::kBoolean;
   pNewData->data.bData = bData;
   m_arrayGlobalData.push_back(std::move(pNewData));
 }
@@ -171,13 +167,13 @@
 
   CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
   if (pData) {
-    pData->data.nType = CFX_Value::DataType::STRING;
+    pData->data.nType = CFX_Value::DataType::kString;
     pData->data.sData = sData;
     return;
   }
-  auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
+  auto pNewData = std::make_unique<CFX_GlobalData::Element>();
   pNewData->data.sKey = std::move(sPropName);
-  pNewData->data.nType = CFX_Value::DataType::STRING;
+  pNewData->data.nType = CFX_Value::DataType::kString;
   pNewData->data.sData = sData;
   m_arrayGlobalData.push_back(std::move(pNewData));
 }
@@ -190,13 +186,13 @@
 
   CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
   if (pData) {
-    pData->data.nType = CFX_Value::DataType::OBJECT;
+    pData->data.nType = CFX_Value::DataType::kObject;
     pData->data.objData = std::move(array);
     return;
   }
-  auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
+  auto pNewData = std::make_unique<CFX_GlobalData::Element>();
   pNewData->data.sKey = std::move(sPropName);
-  pNewData->data.nType = CFX_Value::DataType::OBJECT;
+  pNewData->data.nType = CFX_Value::DataType::kObject;
   pNewData->data.objData = std::move(array);
   m_arrayGlobalData.push_back(std::move(pNewData));
 }
@@ -207,12 +203,12 @@
 
   CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
   if (pData) {
-    pData->data.nType = CFX_Value::DataType::NULLOBJ;
+    pData->data.nType = CFX_Value::DataType::kNull;
     return;
   }
-  auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
+  auto pNewData = std::make_unique<CFX_GlobalData::Element>();
   pNewData->data.sKey = std::move(sPropName);
-  pNewData->data.nType = CFX_Value::DataType::NULLOBJ;
+  pNewData->data.nType = CFX_Value::DataType::kNull;
   m_arrayGlobalData.push_back(std::move(pNewData));
 }
 
@@ -242,7 +238,7 @@
 }
 
 int32_t CFX_GlobalData::GetSize() const {
-  return pdfium::CollectionSize<int32_t>(m_arrayGlobalData);
+  return fxcrt::CollectionSize<int32_t>(m_arrayGlobalData);
 }
 
 CFX_GlobalData::Element* CFX_GlobalData::GetAt(int index) {
@@ -258,7 +254,7 @@
   bool ret;
   {
     // Span can't outlive call to BufferDone().
-    Optional<pdfium::span<uint8_t>> buffer = m_pDelegate->LoadBuffer();
+    absl::optional<pdfium::span<uint8_t>> buffer = m_pDelegate->LoadBuffer();
     if (!buffer.has_value() || buffer.value().empty())
       return false;
 
@@ -296,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;
@@ -307,35 +305,41 @@
     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) {
-      case CFX_Value::DataType::NUMBER: {
+    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;
         }
         SetGlobalVariableNumber(sEntry, dData);
         SetGlobalVariablePersistent(sEntry, true);
       } break;
-      case CFX_Value::DataType::BOOLEAN: {
-        uint16_t wData = *((uint16_t*)p);
+      case CFX_Value::DataType::kBoolean: {
+        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::STRING: {
-        uint32_t dwLength = *((uint32_t*)p);
+      case CFX_Value::DataType::kString: {
+        uint32_t dwLength = 0;
+        memcpy(&dwLength, p, sizeof(uint32_t));
         p += sizeof(uint32_t);
         if (p + dwLength > buffer.end())
           break;
@@ -344,13 +348,12 @@
         SetGlobalVariablePersistent(sEntry, true);
         p += sizeof(char) * dwLength;
       } break;
-      case CFX_Value::DataType::NULLOBJ: {
+      case CFX_Value::DataType::kNull: {
         SetGlobalVariableNull(sEntry);
         SetGlobalVariablePersistent(sEntry, true);
       } break;
-      case CFX_Value::DataType::OBJECT:
-      default:
-        // Arrays aren't allowed in these buffers, nor are unrecoginzed tags.
+      case CFX_Value::DataType::kObject:
+        // Arrays aren't allowed in these buffers, nor are unrecognized tags.
         return false;
     }
   }
@@ -362,12 +365,12 @@
     return false;
 
   uint32_t nCount = 0;
-  CFX_BinaryBuf sData;
+  BinaryBuffer sData;
   for (const auto& pElement : m_arrayGlobalData) {
     if (!pElement->bPersistent)
       continue;
 
-    CFX_BinaryBuf sElement;
+    BinaryBuffer sElement;
     if (!MakeByteString(pElement->data.sKey, pElement->data, &sElement))
       continue;
 
@@ -378,20 +381,17 @@
     nCount++;
   }
 
-  CFX_BinaryBuf sFile;
-  uint16_t wType = kMagic;
-  uint16_t wVersion = 2;
-  sFile.AppendBlock(&wType, sizeof(uint16_t));
-  sFile.AppendBlock(&wVersion, sizeof(uint16_t));
-  sFile.AppendBlock(&nCount, sizeof(uint32_t));
+  BinaryBuffer sFile;
+  sFile.AppendUint16(kMagic);
+  sFile.AppendUint16(kMaxVersion);
+  sFile.AppendUint32(nCount);
 
-  uint32_t dwSize = sData.GetSize();
-  sFile.AppendBlock(&dwSize, sizeof(uint32_t));
+  uint32_t dwSize = pdfium::base::checked_cast<uint32_t>(sData.GetSize());
+  sFile.AppendUint32(dwSize);
   sFile.AppendSpan(sData.GetSpan());
 
-  CRYPT_ArcFourCryptBlock(sFile.GetSpan(), kRC4KEY);
-
-  return m_pDelegate->StoreBuffer({sFile.GetBuffer(), sFile.GetSize()});
+  CRYPT_ArcFourCryptBlock(sFile.GetMutableSpan(), kRC4KEY);
+  return m_pDelegate->StoreBuffer(sFile.GetSpan());
 }
 
 CFX_GlobalData::Element::Element() = default;
diff --git a/fxjs/cfx_globaldata.h b/fxjs/cfx_globaldata.h
index 421a3b4..ac94058 100644
--- a/fxjs/cfx_globaldata.h
+++ b/fxjs/cfx_globaldata.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,22 +10,20 @@
 #include <memory>
 #include <vector>
 
-#include "core/fxcrt/cfx_binarybuf.h"
+#include "core/fxcrt/binary_buffer.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "fxjs/cfx_keyvalue.h"
-#include "third_party/base/optional.h"
-#include "third_party/base/span.h"
-
-class CPDFSDK_FormFillEnvironment;
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_GlobalData {
  public:
   class Delegate {
    public:
-    virtual ~Delegate() {}
+    virtual ~Delegate() = default;
 
     virtual bool StoreBuffer(pdfium::span<const uint8_t> pBuffer) = 0;
-    virtual Optional<pdfium::span<uint8_t>> LoadBuffer() = 0;
+    virtual absl::optional<pdfium::span<uint8_t>> LoadBuffer() = 0;
     virtual void BufferDone() = 0;
   };
 
@@ -35,7 +33,7 @@
     ~Element();
 
     CFX_KeyValue data;
-    bool bPersistent;
+    bool bPersistent = false;
   };
 
   static CFX_GlobalData* GetRetainedInstance(Delegate* pDelegate);
@@ -66,16 +64,8 @@
   bool LoadGlobalPersistentVariables();
   bool LoadGlobalPersistentVariablesFromBuffer(pdfium::span<uint8_t> buffer);
   bool SaveGlobalPersisitentVariables();
-
   iterator FindGlobalVariable(const ByteString& sPropname);
 
-  void LoadFileBuffer(const wchar_t* sFilePath,
-                      uint8_t*& pBuffer,
-                      int32_t& nLength);
-  void WriteFileBuffer(const wchar_t* sFilePath,
-                       const char* pBuffer,
-                       int32_t nLength);
-
   size_t m_RefCount = 0;
   UnownedPtr<Delegate> const m_pDelegate;
   std::vector<std::unique_ptr<Element>> m_arrayGlobalData;
diff --git a/fxjs/cfx_globaldata_unittest.cpp b/fxjs/cfx_globaldata_unittest.cpp
index 3eb0ac4..8121701 100644
--- a/fxjs/cfx_globaldata_unittest.cpp
+++ b/fxjs/cfx_globaldata_unittest.cpp
@@ -1,12 +1,15 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxjs/cfx_globaldata.h"
 
+#include <stdint.h>
+
 #include <utility>
 #include <vector>
 
+#include "core/fxcrt/data_vector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -14,39 +17,40 @@
 class TestDelegate : public CFX_GlobalData::Delegate {
  public:
   TestDelegate() = default;
-  ~TestDelegate() override {}
+  ~TestDelegate() override = default;
 
   bool StoreBuffer(pdfium::span<const uint8_t> buffer) override {
-    last_buffer_ = std::vector<uint8_t>(buffer.begin(), buffer.end());
+    last_buffer_ = DataVector<uint8_t>(buffer.begin(), buffer.end());
     return true;
   }
-  Optional<pdfium::span<uint8_t>> LoadBuffer() override {
+  absl::optional<pdfium::span<uint8_t>> LoadBuffer() override {
     return pdfium::span<uint8_t>(last_buffer_);
   }
   void BufferDone() override {
-    last_buffer_ = std::vector<uint8_t>();  // Catch misuse after done.
+    // Catch misuse after done.
+    last_buffer_ = DataVector<uint8_t>();
   }
 
-  std::vector<uint8_t> last_buffer_;
+  DataVector<uint8_t> last_buffer_;
 };
 
 }  // namespace
 
 TEST(CFXGlobalData, GetSafety) {
   CFX_GlobalData* pInstance = CFX_GlobalData::GetRetainedInstance(nullptr);
-  EXPECT_EQ(nullptr, pInstance->GetGlobalVariable("nonesuch"));
-  EXPECT_EQ(nullptr, pInstance->GetAt(-1));
-  EXPECT_EQ(nullptr, pInstance->GetAt(0));
-  EXPECT_EQ(nullptr, pInstance->GetAt(1));
+  EXPECT_FALSE(pInstance->GetGlobalVariable("nonesuch"));
+  EXPECT_FALSE(pInstance->GetAt(-1));
+  EXPECT_FALSE(pInstance->GetAt(0));
+  EXPECT_FALSE(pInstance->GetAt(1));
 
   pInstance->SetGlobalVariableNumber("double", 2.0);
   pInstance->SetGlobalVariableString("string", "clams");
 
-  EXPECT_EQ(nullptr, pInstance->GetGlobalVariable("nonesuch"));
-  EXPECT_EQ(nullptr, pInstance->GetAt(-1));
+  EXPECT_FALSE(pInstance->GetGlobalVariable("nonesuch"));
+  EXPECT_FALSE(pInstance->GetAt(-1));
   EXPECT_EQ(pInstance->GetGlobalVariable("double"), pInstance->GetAt(0));
   EXPECT_EQ(pInstance->GetGlobalVariable("string"), pInstance->GetAt(1));
-  EXPECT_EQ(nullptr, pInstance->GetAt(2));
+  EXPECT_FALSE(pInstance->GetAt(2));
 
   ASSERT_TRUE(pInstance->Release());
 }
@@ -71,25 +75,25 @@
   auto* element = pInstance->GetAt(0);
   ASSERT_TRUE(element);
   EXPECT_EQ("double", element->data.sKey);
-  EXPECT_EQ(CFX_Value::DataType::NUMBER, element->data.nType);
+  EXPECT_EQ(CFX_Value::DataType::kNumber, element->data.nType);
   EXPECT_EQ(2.0, element->data.dData);
 
   element = pInstance->GetAt(1);
   ASSERT_TRUE(element);
   EXPECT_EQ("string", element->data.sKey);
-  EXPECT_EQ(CFX_Value::DataType::STRING, element->data.nType);
+  EXPECT_EQ(CFX_Value::DataType::kString, element->data.nType);
   EXPECT_EQ("clams", element->data.sData);
 
   element = pInstance->GetAt(2);
   ASSERT_TRUE(element);
   EXPECT_EQ("boolean", element->data.sKey);
-  EXPECT_EQ(CFX_Value::DataType::BOOLEAN, element->data.nType);
+  EXPECT_EQ(CFX_Value::DataType::kBoolean, element->data.nType);
   EXPECT_EQ(true, element->data.bData);
 
   element = pInstance->GetAt(3);
   ASSERT_TRUE(element);
   EXPECT_EQ("null", element->data.sKey);
-  EXPECT_EQ(CFX_Value::DataType::NULLOBJ, element->data.nType);
+  EXPECT_EQ(CFX_Value::DataType::kNull, element->data.nType);
 
   // Arrays don't get persisted.
   element = pInstance->GetAt(4);
@@ -113,25 +117,25 @@
   auto* element = pInstance->GetAt(0);
   ASSERT_TRUE(element);
   EXPECT_EQ("double", element->data.sKey);
-  EXPECT_EQ(CFX_Value::DataType::NUMBER, element->data.nType);
+  EXPECT_EQ(CFX_Value::DataType::kNumber, element->data.nType);
   EXPECT_EQ(2.0, element->data.dData);
 
   element = pInstance->GetAt(1);
   ASSERT_TRUE(element);
   EXPECT_EQ("string", element->data.sKey);
-  EXPECT_EQ(CFX_Value::DataType::STRING, element->data.nType);
+  EXPECT_EQ(CFX_Value::DataType::kString, element->data.nType);
   EXPECT_EQ("clams", element->data.sData);
 
   element = pInstance->GetAt(2);
   ASSERT_TRUE(element);
   EXPECT_EQ("boolean", element->data.sKey);
-  EXPECT_EQ(CFX_Value::DataType::BOOLEAN, element->data.nType);
+  EXPECT_EQ(CFX_Value::DataType::kBoolean, element->data.nType);
   EXPECT_EQ(true, element->data.bData);
 
   element = pInstance->GetAt(3);
   ASSERT_TRUE(element);
   EXPECT_EQ("null", element->data.sKey);
-  EXPECT_EQ(CFX_Value::DataType::NULLOBJ, element->data.nType);
+  EXPECT_EQ(CFX_Value::DataType::kNull, element->data.nType);
 
   ASSERT_TRUE(pInstance->Release());
 }
diff --git a/fxjs/cfx_keyvalue.cpp b/fxjs/cfx_keyvalue.cpp
index d1c7fae..a0ee7c2 100644
--- a/fxjs/cfx_keyvalue.cpp
+++ b/fxjs/cfx_keyvalue.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxjs/cfx_keyvalue.h b/fxjs/cfx_keyvalue.h
index b66dc12..a6ff52a 100644
--- a/fxjs/cfx_keyvalue.h
+++ b/fxjs/cfx_keyvalue.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,26 +10,26 @@
 #include <memory>
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/bytestring.h"
 
 class CFX_KeyValue;
 
 class CFX_Value {
  public:
   enum class DataType : uint8_t {
-    NUMBER = 0,
-    BOOLEAN,
-    STRING,
-    OBJECT,
-    NULLOBJ
+    kNumber = 0,
+    kBoolean,
+    kString,
+    kObject,
+    kNull
   };
 
   CFX_Value();
   ~CFX_Value();
 
-  DataType nType = DataType::NULLOBJ;
-  bool bData;
-  double dData;
+  DataType nType = DataType::kNull;
+  bool bData = false;
+  double dData = 0.0;
   ByteString sData;
   std::vector<std::unique_ptr<CFX_KeyValue>> objData;
 };
diff --git a/fxjs/cfx_v8.cpp b/fxjs/cfx_v8.cpp
index 72bf361..422e28a 100644
--- a/fxjs/cfx_v8.cpp
+++ b/fxjs/cfx_v8.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,8 @@
 
 #include "fxjs/cfx_v8.h"
 
-#include "core/fxcrt/fx_memory.h"
-#include "third_party/base/allocator/partition_allocator/partition_alloc.h"
+#include "fxjs/fxv8.h"
+#include "v8/include/v8-isolate.h"
 
 CFX_V8::CFX_V8(v8::Isolate* isolate) : m_pIsolate(isolate) {}
 
@@ -16,103 +16,68 @@
 v8::Local<v8::Value> CFX_V8::GetObjectProperty(
     v8::Local<v8::Object> pObj,
     ByteStringView bsUTF8PropertyName) {
-  if (pObj.IsEmpty())
-    return v8::Local<v8::Value>();
-  v8::Local<v8::Value> val;
-  if (!pObj->Get(m_pIsolate->GetCurrentContext(), NewString(bsUTF8PropertyName))
-           .ToLocal(&val))
-    return v8::Local<v8::Value>();
-  return val;
+  return fxv8::ReentrantGetObjectPropertyHelper(GetIsolate(), pObj,
+                                                bsUTF8PropertyName);
 }
 
 std::vector<WideString> CFX_V8::GetObjectPropertyNames(
     v8::Local<v8::Object> pObj) {
-  if (pObj.IsEmpty())
-    return std::vector<WideString>();
-
-  v8::Local<v8::Array> val;
-  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
-  if (!pObj->GetPropertyNames(context).ToLocal(&val))
-    return std::vector<WideString>();
-
-  std::vector<WideString> result;
-  for (uint32_t i = 0; i < val->Length(); ++i) {
-    result.push_back(ToWideString(val->Get(context, i).ToLocalChecked()));
-  }
-
-  return result;
+  return fxv8::ReentrantGetObjectPropertyNamesHelper(GetIsolate(), pObj);
 }
 
-bool CFX_V8::PutObjectProperty(v8::Local<v8::Object> pObj,
+void CFX_V8::PutObjectProperty(v8::Local<v8::Object> pObj,
                                ByteStringView bsUTF8PropertyName,
                                v8::Local<v8::Value> pPut) {
-  ASSERT(!pPut.IsEmpty());
-  if (pObj.IsEmpty())
-    return false;
-
-  v8::Local<v8::String> name = NewString(bsUTF8PropertyName);
-  return pObj->Set(m_pIsolate->GetCurrentContext(), name, pPut).IsJust();
+  fxv8::ReentrantPutObjectPropertyHelper(GetIsolate(), pObj, bsUTF8PropertyName,
+                                         pPut);
 }
 
 void CFX_V8::DisposeIsolate() {
   if (m_pIsolate)
-    m_pIsolate.Release()->Dispose();
+    m_pIsolate.ExtractAsDangling()->Dispose();
 }
 
 v8::Local<v8::Array> CFX_V8::NewArray() {
-  return v8::Array::New(GetIsolate());
+  return fxv8::NewArrayHelper(GetIsolate());
 }
 
 v8::Local<v8::Object> CFX_V8::NewObject() {
-  return v8::Object::New(GetIsolate());
+  return fxv8::NewObjectHelper(GetIsolate());
 }
 
-bool CFX_V8::PutArrayElement(v8::Local<v8::Array> pArray,
-                             unsigned index,
+void CFX_V8::PutArrayElement(v8::Local<v8::Array> pArray,
+                             size_t index,
                              v8::Local<v8::Value> pValue) {
-  ASSERT(!pValue.IsEmpty());
-  if (pArray.IsEmpty())
-    return false;
-  return pArray->Set(m_pIsolate->GetCurrentContext(), index, pValue).IsJust();
+  fxv8::ReentrantPutArrayElementHelper(GetIsolate(), pArray, index, pValue);
 }
 
 v8::Local<v8::Value> CFX_V8::GetArrayElement(v8::Local<v8::Array> pArray,
-                                             unsigned index) {
-  if (pArray.IsEmpty())
-    return v8::Local<v8::Value>();
-  v8::Local<v8::Value> val;
-  if (!pArray->Get(m_pIsolate->GetCurrentContext(), index).ToLocal(&val))
-    return v8::Local<v8::Value>();
-  return val;
+                                             size_t index) {
+  return fxv8::ReentrantGetArrayElementHelper(GetIsolate(), pArray, index);
 }
 
-unsigned CFX_V8::GetArrayLength(v8::Local<v8::Array> pArray) {
-  if (pArray.IsEmpty())
-    return 0;
-  return pArray->Length();
+size_t CFX_V8::GetArrayLength(v8::Local<v8::Array> pArray) {
+  return fxv8::GetArrayLengthHelper(pArray);
 }
 
 v8::Local<v8::Number> CFX_V8::NewNumber(int number) {
-  return v8::Int32::New(GetIsolate(), number);
+  return fxv8::NewNumberHelper(GetIsolate(), number);
 }
 
 v8::Local<v8::Number> CFX_V8::NewNumber(double number) {
-  return v8::Number::New(GetIsolate(), number);
+  return fxv8::NewNumberHelper(GetIsolate(), number);
 }
 
 v8::Local<v8::Number> CFX_V8::NewNumber(float number) {
-  return v8::Number::New(GetIsolate(), number);
+  return fxv8::NewNumberHelper(GetIsolate(), number);
 }
 
 v8::Local<v8::Boolean> CFX_V8::NewBoolean(bool b) {
-  return v8::Boolean::New(GetIsolate(), b);
+  return fxv8::NewBooleanHelper(GetIsolate(), b);
 }
 
 v8::Local<v8::String> CFX_V8::NewString(ByteStringView str) {
-  v8::Isolate* pIsolate = m_pIsolate ? GetIsolate() : v8::Isolate::GetCurrent();
-  return v8::String::NewFromUtf8(pIsolate, str.unterminated_c_str(),
-                                 v8::NewStringType::kNormal, str.GetLength())
-      .ToLocalChecked();
+  return fxv8::NewStringHelper(GetIsolate(), str);
 }
 
 v8::Local<v8::String> CFX_V8::NewString(WideStringView str) {
@@ -123,95 +88,45 @@
 }
 
 v8::Local<v8::Value> CFX_V8::NewNull() {
-  return v8::Null(GetIsolate());
+  return fxv8::NewNullHelper(GetIsolate());
 }
 
 v8::Local<v8::Value> CFX_V8::NewUndefined() {
-  return v8::Undefined(GetIsolate());
+  return fxv8::NewUndefinedHelper(GetIsolate());
 }
 
 v8::Local<v8::Date> CFX_V8::NewDate(double d) {
-  return v8::Date::New(m_pIsolate->GetCurrentContext(), d)
-      .ToLocalChecked()
-      .As<v8::Date>();
+  return fxv8::NewDateHelper(GetIsolate(), d);
 }
 
 int CFX_V8::ToInt32(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty())
-    return 0;
-  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
-  v8::MaybeLocal<v8::Int32> maybe_int32 = pValue->ToInt32(context);
-  if (maybe_int32.IsEmpty())
-    return 0;
-  return maybe_int32.ToLocalChecked()->Value();
+  return fxv8::ReentrantToInt32Helper(GetIsolate(), pValue);
 }
 
 bool CFX_V8::ToBoolean(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty())
-    return false;
-  return pValue->BooleanValue(m_pIsolate.Get());
+  return fxv8::ReentrantToBooleanHelper(GetIsolate(), pValue);
 }
 
 double CFX_V8::ToDouble(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty())
-    return 0.0;
-  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
-  v8::MaybeLocal<v8::Number> maybe_number = pValue->ToNumber(context);
-  if (maybe_number.IsEmpty())
-    return 0.0;
-  return maybe_number.ToLocalChecked()->Value();
+  return fxv8::ReentrantToDoubleHelper(GetIsolate(), pValue);
 }
 
 WideString CFX_V8::ToWideString(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty())
-    return WideString();
-  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
-  v8::MaybeLocal<v8::String> maybe_string = pValue->ToString(context);
-  if (maybe_string.IsEmpty())
-    return WideString();
-  v8::String::Utf8Value s(GetIsolate(), maybe_string.ToLocalChecked());
-  return WideString::FromUTF8(ByteStringView(*s, s.length()));
+  return fxv8::ReentrantToWideStringHelper(GetIsolate(), pValue);
 }
 
 ByteString CFX_V8::ToByteString(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty())
-    return ByteString();
-  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
-  v8::MaybeLocal<v8::String> maybe_string = pValue->ToString(context);
-  if (maybe_string.IsEmpty())
-    return ByteString();
-  v8::String::Utf8Value s(GetIsolate(), maybe_string.ToLocalChecked());
-  return ByteString(*s);
+  return fxv8::ReentrantToByteStringHelper(GetIsolate(), pValue);
 }
 
 v8::Local<v8::Object> CFX_V8::ToObject(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty() || !pValue->IsObject())
-    return v8::Local<v8::Object>();
-  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
-  return pValue->ToObject(context).ToLocalChecked();
+  return fxv8::ReentrantToObjectHelper(GetIsolate(), pValue);
 }
 
 v8::Local<v8::Array> CFX_V8::ToArray(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty() || !pValue->IsArray())
-    return v8::Local<v8::Array>();
-  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
-  return v8::Local<v8::Array>::Cast(pValue->ToObject(context).ToLocalChecked());
+  return fxv8::ReentrantToArrayHelper(GetIsolate(), pValue);
 }
 
-void* CFX_V8ArrayBufferAllocator::Allocate(size_t length) {
-  if (length > kMaxAllowedBytes)
-    return nullptr;
-  return GetArrayBufferPartitionAllocator().root()->AllocFlags(
-      pdfium::base::PartitionAllocZeroFill, length, "CFX_V8ArrayBuffer");
-}
-
-void* CFX_V8ArrayBufferAllocator::AllocateUninitialized(size_t length) {
-  if (length > kMaxAllowedBytes)
-    return nullptr;
-  return GetArrayBufferPartitionAllocator().root()->Alloc(length,
-                                                          "CFX_V8ArrayBuffer");
-}
-
-void CFX_V8ArrayBufferAllocator::Free(void* data, size_t length) {
-  GetArrayBufferPartitionAllocator().root()->Free(data);
+void CFX_V8IsolateDeleter::operator()(v8::Isolate* ptr) {
+  ptr->Dispose();
 }
diff --git a/fxjs/cfx_v8.h b/fxjs/cfx_v8.h
index cb152ac..7b2ddc9 100644
--- a/fxjs/cfx_v8.h
+++ b/fxjs/cfx_v8.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,18 +7,20 @@
 #ifndef FXJS_CFX_V8_H_
 #define FXJS_CFX_V8_H_
 
+#include <stddef.h>
+
 #include <vector>
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "v8/include/v8.h"
+#include "v8/include/v8-forward.h"
 
 class CFX_V8 {
  public:
   explicit CFX_V8(v8::Isolate* pIsolate);
   virtual ~CFX_V8();
 
-  v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); }
+  v8::Isolate* GetIsolate() const { return m_pIsolate; }
 
   v8::Local<v8::Value> NewNull();
   v8::Local<v8::Value> NewUndefined();
@@ -41,18 +43,18 @@
   v8::Local<v8::Array> ToArray(v8::Local<v8::Value> pValue);
 
   // Arrays.
-  unsigned GetArrayLength(v8::Local<v8::Array> pArray);
+  size_t GetArrayLength(v8::Local<v8::Array> pArray);
   v8::Local<v8::Value> GetArrayElement(v8::Local<v8::Array> pArray,
-                                       unsigned index);
-  bool PutArrayElement(v8::Local<v8::Array> pArray,
-                       unsigned index,
+                                       size_t index);
+  void PutArrayElement(v8::Local<v8::Array> pArray,
+                       size_t index,
                        v8::Local<v8::Value> pValue);
 
   // Objects.
   std::vector<WideString> GetObjectPropertyNames(v8::Local<v8::Object> pObj);
   v8::Local<v8::Value> GetObjectProperty(v8::Local<v8::Object> pObj,
                                          ByteStringView bsUTF8PropertyName);
-  bool PutObjectProperty(v8::Local<v8::Object> pObj,
+  void PutObjectProperty(v8::Local<v8::Object> pObj,
                          ByteStringView bsUTF8PropertyName,
                          v8::Local<v8::Value> pValue);
 
@@ -64,16 +66,9 @@
   UnownedPtr<v8::Isolate> m_pIsolate;
 };
 
-class CFX_V8ArrayBufferAllocator final : public v8::ArrayBuffer::Allocator {
-  static const size_t kMaxAllowedBytes = 0x10000000;
-  void* Allocate(size_t length) override;
-  void* AllocateUninitialized(size_t length) override;
-  void Free(void* data, size_t length) override;
-};
-
 // Use with std::unique_ptr<v8::Isolate> to dispose of isolates correctly.
 struct CFX_V8IsolateDeleter {
-  inline void operator()(v8::Isolate* ptr) { ptr->Dispose(); }
+  void operator()(v8::Isolate* ptr);
 };
 
 #endif  // FXJS_CFX_V8_H_
diff --git a/fxjs/cfx_v8_array_buffer_allocator.cpp b/fxjs/cfx_v8_array_buffer_allocator.cpp
new file mode 100644
index 0000000..c0ddad0
--- /dev/null
+++ b/fxjs/cfx_v8_array_buffer_allocator.cpp
@@ -0,0 +1,53 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/cfx_v8_array_buffer_allocator.h"
+
+#include "core/fxcrt/fx_memory.h"
+
+CFX_V8ArrayBufferAllocator::CFX_V8ArrayBufferAllocator() = default;
+
+CFX_V8ArrayBufferAllocator::~CFX_V8ArrayBufferAllocator() = default;
+
+// NOTE: Under V8 sandbox mode, defer NewDefaultAllocator() call until
+// first use, since V8 must be initialized first for it to succeed, but
+// we need the allocator in order to initialize V8.
+
+void* CFX_V8ArrayBufferAllocator::Allocate(size_t length) {
+  if (length > kMaxAllowedBytes)
+    return nullptr;
+#ifdef V8_ENABLE_SANDBOX
+  if (!wrapped_) {
+    wrapped_.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator());
+  }
+  return wrapped_->Allocate(length);
+#else   // V8_ENABLE_SANDBOX
+  return FX_ArrayBufferAllocate(length);
+#endif  // V8_ENABLE_SANDBOX
+}
+
+void* CFX_V8ArrayBufferAllocator::AllocateUninitialized(size_t length) {
+  if (length > kMaxAllowedBytes)
+    return nullptr;
+#ifdef V8_ENABLE_SANDBOX
+  if (!wrapped_) {
+    wrapped_.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator());
+  }
+  return wrapped_->AllocateUninitialized(length);
+#else  // V8_ENABLE_SANDBOX
+  return FX_ArrayBufferAllocateUninitialized(length);
+#endif
+}
+
+void CFX_V8ArrayBufferAllocator::Free(void* data, size_t length) {
+#ifdef V8_ENABLE_SANDBOX
+  if (wrapped_) {
+    wrapped_->Free(data, length);
+  }
+#else  // V8_ENABLE_SANDBOX
+  FX_ArrayBufferFree(data);
+#endif
+}
diff --git a/fxjs/cfx_v8_array_buffer_allocator.h b/fxjs/cfx_v8_array_buffer_allocator.h
new file mode 100644
index 0000000..a6daef0
--- /dev/null
+++ b/fxjs/cfx_v8_array_buffer_allocator.h
@@ -0,0 +1,35 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_CFX_V8_ARRAY_BUFFER_ALLOCATOR_H_
+#define FXJS_CFX_V8_ARRAY_BUFFER_ALLOCATOR_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "v8/include/v8-array-buffer.h"
+
+class CFX_V8ArrayBufferAllocator final : public v8::ArrayBuffer::Allocator {
+ public:
+  static const size_t kMaxAllowedBytes = 0x10000000;
+
+  CFX_V8ArrayBufferAllocator();
+  CFX_V8ArrayBufferAllocator(const CFX_V8ArrayBufferAllocator&) = delete;
+  CFX_V8ArrayBufferAllocator(CFX_V8ArrayBufferAllocator&&) = delete;
+  ~CFX_V8ArrayBufferAllocator() override;
+
+  void* Allocate(size_t length) override;
+  void* AllocateUninitialized(size_t length) override;
+  void Free(void* data, size_t length) override;
+
+#ifdef V8_ENABLE_SANDBOX
+ private:
+  std::unique_ptr<v8::ArrayBuffer::Allocator> wrapped_;
+#endif  // V8_ENABLE_SANDBOX
+};
+
+#endif  // FXJS_CFX_V8_ARRAY_BUFFER_ALLOCATOR_H_
diff --git a/fxjs/cfx_v8_unittest.cpp b/fxjs/cfx_v8_unittest.cpp
index 7b9ecb5..0a2dca8 100644
--- a/fxjs/cfx_v8_unittest.cpp
+++ b/fxjs/cfx_v8_unittest.cpp
@@ -1,39 +1,43 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "fxjs/cfx_v8_unittest.h"
+#include "fxjs/cfx_v8.h"
+
+#include <math.h>
 
 #include <memory>
 
-#include "fxjs/cfx_v8.h"
+#include "testing/fxv8_unittest.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
+#include "v8/include/v8-container.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-date.h"
+#include "v8/include/v8-isolate.h"
 
 namespace {
 bool getter_sentinel = false;
 bool setter_sentinel = false;
 }  // namespace
 
-void FXV8UnitTest::V8IsolateDeleter::operator()(v8::Isolate* ptr) const {
-  ptr->Dispose();
-}
+class CFXV8UnitTest : public FXV8UnitTest {
+ public:
+  CFXV8UnitTest() = default;
+  ~CFXV8UnitTest() override = default;
 
-FXV8UnitTest::FXV8UnitTest() = default;
+  // FXV8UnitTest:
+  void SetUp() override {
+    FXV8UnitTest::SetUp();
+    cfx_v8_ = std::make_unique<CFX_V8>(isolate());
+  }
 
-FXV8UnitTest::~FXV8UnitTest() = default;
+  CFX_V8* cfx_v8() const { return cfx_v8_.get(); }
 
-void FXV8UnitTest::SetUp() {
-  array_buffer_allocator_ = pdfium::MakeUnique<CFX_V8ArrayBufferAllocator>();
+ protected:
+  std::unique_ptr<CFX_V8> cfx_v8_;
+};
 
-  v8::Isolate::CreateParams params;
-  params.array_buffer_allocator = array_buffer_allocator_.get();
-  isolate_.reset(v8::Isolate::New(params));
-
-  cfx_v8_ = pdfium::MakeUnique<CFX_V8>(isolate_.get());
-}
-
-TEST_F(FXV8UnitTest, EmptyLocal) {
+TEST_F(CFXV8UnitTest, EmptyLocal) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -50,18 +54,18 @@
   // Can't set properties on empty objects, but does not fault.
   v8::Local<v8::Value> marker = cfx_v8()->NewNumber(2);
   v8::Local<v8::Object> empty_object;
-  EXPECT_FALSE(cfx_v8()->PutObjectProperty(empty_object, "clams", marker));
+  cfx_v8()->PutObjectProperty(empty_object, "clams", marker);
   EXPECT_TRUE(cfx_v8()->GetObjectProperty(empty_object, "clams").IsEmpty());
   EXPECT_EQ(0u, cfx_v8()->GetObjectPropertyNames(empty_object).size());
 
   // Can't set elements in empty arrays, but does not fault.
   v8::Local<v8::Array> empty_array;
-  EXPECT_FALSE(cfx_v8()->PutArrayElement(empty_array, 0, marker));
+  cfx_v8()->PutArrayElement(empty_array, 0, marker);
   EXPECT_TRUE(cfx_v8()->GetArrayElement(empty_array, 0).IsEmpty());
   EXPECT_EQ(0u, cfx_v8()->GetArrayLength(empty_array));
 }
 
-TEST_F(FXV8UnitTest, NewNull) {
+TEST_F(CFXV8UnitTest, NewNull) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -76,7 +80,7 @@
   EXPECT_TRUE(cfx_v8()->ToArray(nullz).IsEmpty());
 }
 
-TEST_F(FXV8UnitTest, NewUndefined) {
+TEST_F(CFXV8UnitTest, NewUndefined) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -84,14 +88,14 @@
   auto undef = cfx_v8()->NewUndefined();
   EXPECT_FALSE(cfx_v8()->ToBoolean(undef));
   EXPECT_EQ(0, cfx_v8()->ToInt32(undef));
-  EXPECT_TRUE(std::isnan(cfx_v8()->ToDouble(undef)));
+  EXPECT_TRUE(isnan(cfx_v8()->ToDouble(undef)));
   EXPECT_EQ("undefined", cfx_v8()->ToByteString(undef));
   EXPECT_EQ(L"undefined", cfx_v8()->ToWideString(undef));
   EXPECT_TRUE(cfx_v8()->ToObject(undef).IsEmpty());
   EXPECT_TRUE(cfx_v8()->ToArray(undef).IsEmpty());
 }
 
-TEST_F(FXV8UnitTest, NewBoolean) {
+TEST_F(CFXV8UnitTest, NewBoolean) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -115,7 +119,7 @@
   EXPECT_TRUE(cfx_v8()->ToArray(boolz).IsEmpty());
 }
 
-TEST_F(FXV8UnitTest, NewNumber) {
+TEST_F(CFXV8UnitTest, NewNumber) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -130,7 +134,7 @@
   EXPECT_TRUE(cfx_v8()->ToArray(num).IsEmpty());
 }
 
-TEST_F(FXV8UnitTest, NewString) {
+TEST_F(CFXV8UnitTest, NewString) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -154,7 +158,7 @@
   EXPECT_TRUE(cfx_v8()->ToArray(str2).IsEmpty());
 }
 
-TEST_F(FXV8UnitTest, NewDate) {
+TEST_F(CFXV8UnitTest, NewDate) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -169,7 +173,7 @@
   EXPECT_TRUE(cfx_v8()->ToArray(date).IsEmpty());
 }
 
-TEST_F(FXV8UnitTest, NewArray) {
+TEST_F(CFXV8UnitTest, NewArray) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -180,7 +184,7 @@
   EXPECT_TRUE(cfx_v8()->GetArrayElement(array, 2)->IsUndefined());
   EXPECT_EQ(0u, cfx_v8()->GetArrayLength(array));
 
-  EXPECT_TRUE(cfx_v8()->PutArrayElement(array, 3, cfx_v8()->NewNumber(12)));
+  cfx_v8()->PutArrayElement(array, 3, cfx_v8()->NewNumber(12));
   EXPECT_FALSE(cfx_v8()->GetArrayElement(array, 2).IsEmpty());
   EXPECT_TRUE(cfx_v8()->GetArrayElement(array, 2)->IsUndefined());
   EXPECT_FALSE(cfx_v8()->GetArrayElement(array, 3).IsEmpty());
@@ -196,7 +200,7 @@
   EXPECT_TRUE(cfx_v8()->ToArray(array)->IsArray());
 }
 
-TEST_F(FXV8UnitTest, NewObject) {
+TEST_F(CFXV8UnitTest, NewObject) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -208,8 +212,7 @@
   EXPECT_TRUE(cfx_v8()->GetObjectProperty(object, "clams")->IsUndefined());
   EXPECT_EQ(0u, cfx_v8()->GetObjectPropertyNames(object).size());
 
-  EXPECT_TRUE(
-      cfx_v8()->PutObjectProperty(object, "clams", cfx_v8()->NewNumber(12)));
+  cfx_v8()->PutObjectProperty(object, "clams", cfx_v8()->NewNumber(12));
   EXPECT_FALSE(cfx_v8()->GetObjectProperty(object, "clams").IsEmpty());
   EXPECT_TRUE(cfx_v8()->GetObjectProperty(object, "clams")->IsNumber());
   EXPECT_EQ(1u, cfx_v8()->GetObjectPropertyNames(object).size());
@@ -224,7 +227,7 @@
   EXPECT_TRUE(cfx_v8()->ToArray(object).IsEmpty());
 }
 
-TEST_F(FXV8UnitTest, ThrowFromGetter) {
+TEST_F(CFXV8UnitTest, ThrowFromGetter) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Local<v8::Context> context = v8::Context::New(isolate());
@@ -246,7 +249,7 @@
   EXPECT_TRUE(getter_sentinel);
 }
 
-TEST_F(FXV8UnitTest, ThrowFromSetter) {
+TEST_F(CFXV8UnitTest, ThrowFromSetter) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Local<v8::Context> context = v8::Context::New(isolate());
@@ -264,6 +267,6 @@
                                 })
                   .FromJust());
   setter_sentinel = false;
-  EXPECT_FALSE(cfx_v8()->PutObjectProperty(object, "clams", name));
+  cfx_v8()->PutObjectProperty(object, "clams", name);
   EXPECT_TRUE(setter_sentinel);
 }
diff --git a/fxjs/cfx_v8_unittest.h b/fxjs/cfx_v8_unittest.h
deleted file mode 100644
index e5d4e3f..0000000
--- a/fxjs/cfx_v8_unittest.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef FXJS_CFX_V8_UNITTEST_H_
-#define FXJS_CFX_V8_UNITTEST_H_
-
-#include <memory>
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-class CFX_V8;
-class CFX_V8ArrayBufferAllocator;
-
-namespace v8 {
-class Isolate;
-}  // namespace v8
-
-class FXV8UnitTest : public ::testing::Test {
- public:
-  struct V8IsolateDeleter {
-    void operator()(v8::Isolate* ptr) const;
-  };
-
-  FXV8UnitTest();
-  ~FXV8UnitTest() override;
-
-  void SetUp() override;
-
-  v8::Isolate* isolate() const { return isolate_.get(); }
-  CFX_V8* cfx_v8() const { return cfx_v8_.get(); }
-
- protected:
-  std::unique_ptr<CFX_V8ArrayBufferAllocator> array_buffer_allocator_;
-  std::unique_ptr<v8::Isolate, V8IsolateDeleter> isolate_;
-  std::unique_ptr<CFX_V8> cfx_v8_;
-};
-
-#endif  // FXJS_CFX_V8_UNITTEST_H_
diff --git a/fxjs/cfxjs_engine.cpp b/fxjs/cfxjs_engine.cpp
index c4d46a2..35653c9 100644
--- a/fxjs/cfxjs_engine.cpp
+++ b/fxjs/cfxjs_engine.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,17 +8,23 @@
 
 #include <memory>
 #include <utility>
-#include <vector>
 
+#include "core/fxcrt/stl_util.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "fxjs/cfx_v8_array_buffer_allocator.h"
 #include "fxjs/cjs_object.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_runtimedata.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-exception.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-message.h"
+#include "v8/include/v8-primitive.h"
+#include "v8/include/v8-script.h"
 #include "v8/include/v8-util.h"
 
-class CFXJS_PerObjectData;
-
 namespace {
 
 unsigned int g_embedderDataSlot = 1u;
@@ -26,7 +32,14 @@
 size_t g_isolate_ref_count = 0;
 CFX_V8ArrayBufferAllocator* g_arrayBufferAllocator = nullptr;
 v8::Global<v8::ObjectTemplate>* g_DefaultGlobalObjectTemplate = nullptr;
+
+// Only the address matters, values are for humans debugging. ASLR should
+// ensure that these values are unlikely to arise otherwise. Keep these
+// wchar_t to prevent the compiler from doing something clever, like
+// aligning them on a byte boundary to save space, which would make them
+// incompatible for use as V8 aligned pointers.
 const wchar_t kPerObjectDataTag[] = L"CFXJS_PerObjectData";
+const wchar_t kPerIsolateDataTag[] = L"FXJS_PerIsolateData";
 
 void* GetAlignedPointerForPerObjectDataTag() {
   return const_cast<void*>(static_cast<const void*>(kPerObjectDataTag));
@@ -42,6 +55,41 @@
 
 }  // namespace
 
+class CFXJS_PerObjectData {
+ public:
+  ~CFXJS_PerObjectData() = default;
+
+  static void SetNewDataInObject(uint32_t nObjDefnID,
+                                 v8::Local<v8::Object> pObj) {
+    if (pObj->InternalFieldCount() == 2) {
+      pObj->SetAlignedPointerInInternalField(
+          0, GetAlignedPointerForPerObjectDataTag());
+      pObj->SetAlignedPointerInInternalField(
+          1, new CFXJS_PerObjectData(nObjDefnID));
+    }
+  }
+
+  static CFXJS_PerObjectData* GetFromObject(v8::Local<v8::Object> pObj) {
+    if (pObj.IsEmpty() || pObj->InternalFieldCount() != 2 ||
+        pObj->GetAlignedPointerFromInternalField(0) !=
+            GetAlignedPointerForPerObjectDataTag()) {
+      return nullptr;
+    }
+    return static_cast<CFXJS_PerObjectData*>(
+        pObj->GetAlignedPointerFromInternalField(1));
+  }
+
+  uint32_t GetObjDefnID() const { return m_ObjDefnID; }
+  CJS_Object* GetPrivate() { return m_pPrivate.get(); }
+  void SetPrivate(std::unique_ptr<CJS_Object> p) { m_pPrivate = std::move(p); }
+
+ private:
+  explicit CFXJS_PerObjectData(uint32_t nObjDefnID) : m_ObjDefnID(nObjDefnID) {}
+
+  const uint32_t m_ObjDefnID;
+  std::unique_ptr<CJS_Object> m_pPrivate;
+};
+
 // Global weak map to save dynamic objects.
 class V8TemplateMapTraits final
     : public v8::StdMapTraits<CFXJS_PerObjectData*, v8::Object> {
@@ -84,8 +132,9 @@
   explicit V8TemplateMap(v8::Isolate* isolate) : m_map(isolate) {}
   ~V8TemplateMap() = default;
 
-  void SetAndMakeWeak(WeakCallbackDataType* key, v8::Local<v8::Object> handle) {
-    ASSERT(!m_map.Contains(key));
+  void SetAndMakeWeak(v8::Local<v8::Object> handle) {
+    WeakCallbackDataType* key = CFXJS_PerObjectData::GetFromObject(handle);
+    DCHECK(!m_map.Contains(key));
 
     // Inserting an object into a GlobalValueMap with the appropriate traits
     // has the side-effect of making the object weak deep in the guts of V8,
@@ -93,41 +142,12 @@
     m_map.Set(key, handle);
   }
 
-  friend class V8TemplateMapTraits;
+  MapType* GetMap() { return &m_map; }
 
  private:
   MapType m_map;
 };
 
-class CFXJS_PerObjectData {
- public:
-  explicit CFXJS_PerObjectData(int nObjDefID) : m_ObjDefID(nObjDefID) {}
-
-  ~CFXJS_PerObjectData() = default;
-
-  static void SetInObject(CFXJS_PerObjectData* pData,
-                          v8::Local<v8::Object> pObj) {
-    if (pObj->InternalFieldCount() == 2) {
-      pObj->SetAlignedPointerInInternalField(
-          0, GetAlignedPointerForPerObjectDataTag());
-      pObj->SetAlignedPointerInInternalField(1, pData);
-    }
-  }
-
-  static CFXJS_PerObjectData* GetFromObject(v8::Local<v8::Object> pObj) {
-    if (pObj.IsEmpty() || pObj->InternalFieldCount() != 2 ||
-        pObj->GetAlignedPointerFromInternalField(0) !=
-            GetAlignedPointerForPerObjectDataTag()) {
-      return nullptr;
-    }
-    return static_cast<CFXJS_PerObjectData*>(
-        pObj->GetAlignedPointerFromInternalField(1));
-  }
-
-  const int m_ObjDefID;
-  std::unique_ptr<CJS_Object> m_pPrivate;
-};
-
 class CFXJS_ObjDefinition {
  public:
   CFXJS_ObjDefinition(v8::Isolate* isolate,
@@ -142,42 +162,37 @@
         m_pIsolate(isolate) {
     v8::Isolate::Scope isolate_scope(isolate);
     v8::HandleScope handle_scope(isolate);
-    v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate);
-    fun->InstanceTemplate()->SetInternalFieldCount(2);
-    fun->SetCallHandler(CallHandler, v8::Number::New(isolate, eObjType));
+    v8::Local<v8::FunctionTemplate> fn = v8::FunctionTemplate::New(isolate);
+    fn->InstanceTemplate()->SetInternalFieldCount(2);
+    fn->InstanceTemplate()->SetImmutableProto();
+    fn->SetCallHandler(CallHandler, v8::Number::New(isolate, eObjType));
     if (eObjType == FXJSOBJTYPE_GLOBAL) {
-      fun->InstanceTemplate()->Set(
-          v8::Symbol::GetToStringTag(isolate),
-          v8::String::NewFromUtf8(isolate, "global", v8::NewStringType::kNormal)
-              .ToLocalChecked());
+      fn->InstanceTemplate()->Set(v8::Symbol::GetToStringTag(isolate),
+                                  fxv8::NewStringHelper(isolate, "global"));
     }
-    m_FunctionTemplate.Reset(isolate, fun);
-    m_Signature.Reset(isolate, v8::Signature::New(isolate, fun));
+    m_FunctionTemplate.Reset(isolate, fn);
+    m_Signature.Reset(isolate, v8::Signature::New(isolate, fn));
   }
 
   static void CallHandler(const v8::FunctionCallbackInfo<v8::Value>& info) {
     v8::Isolate* isolate = info.GetIsolate();
     if (!info.IsConstructCall()) {
-      isolate->ThrowException(
-          v8::String::NewFromUtf8(isolate, "illegal constructor",
-                                  v8::NewStringType::kNormal)
-              .ToLocalChecked());
+      fxv8::ThrowExceptionHelper(isolate, "illegal constructor");
       return;
     }
     if (info.Data().As<v8::Int32>()->Value() != FXJSOBJTYPE_DYNAMIC) {
-      isolate->ThrowException(
-          v8::String::NewFromUtf8(isolate, "not a dynamic object",
-                                  v8::NewStringType::kNormal)
-              .ToLocalChecked());
+      fxv8::ThrowExceptionHelper(isolate, "not a dynamic object");
       return;
     }
     v8::Local<v8::Object> holder = info.Holder();
-    ASSERT(holder->InternalFieldCount() == 2);
+    DCHECK_EQ(holder->InternalFieldCount(), 2);
     holder->SetAlignedPointerInInternalField(0, nullptr);
     holder->SetAlignedPointerInInternalField(1, nullptr);
   }
 
-  v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); }
+  FXJSOBJTYPE GetObjType() const { return m_ObjType; }
+  const char* GetObjName() const { return m_ObjName; }
+  v8::Isolate* GetIsolate() const { return m_pIsolate; }
 
   void DefineConst(const char* sConstName, v8::Local<v8::Value> pDefault) {
     GetInstanceTemplate()->Set(GetIsolate(), sConstName, pDefault);
@@ -197,12 +212,14 @@
     GetInstanceTemplate()->Set(sMethodName, fun, v8::ReadOnly);
   }
 
-  void DefineAllProperties(v8::GenericNamedPropertyQueryCallback pPropQurey,
-                           v8::GenericNamedPropertyGetterCallback pPropGet,
-                           v8::GenericNamedPropertySetterCallback pPropPut,
-                           v8::GenericNamedPropertyDeleterCallback pPropDel) {
+  void DefineAllProperties(
+      v8::GenericNamedPropertyQueryCallback pPropQurey,
+      v8::GenericNamedPropertyGetterCallback pPropGet,
+      v8::GenericNamedPropertySetterCallback pPropPut,
+      v8::GenericNamedPropertyDeleterCallback pPropDel,
+      v8::GenericNamedPropertyEnumeratorCallback pPropEnum) {
     GetInstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
-        pPropGet, pPropPut, pPropQurey, pPropDel, nullptr,
+        pPropGet, pPropPut, pPropQurey, pPropDel, pPropEnum,
         v8::Local<v8::Value>(),
         v8::PropertyHandlerFlags::kOnlyInterceptStrings));
   }
@@ -219,7 +236,20 @@
     return scope.Escape(m_Signature.Get(GetIsolate()));
   }
 
-  const char* const m_ObjName;
+  void RunConstructor(CFXJS_Engine* pEngine,
+                      v8::Local<v8::Object> obj,
+                      v8::Local<v8::Object> proxy) {
+    if (m_pConstructor)
+      m_pConstructor(pEngine, obj, proxy);
+  }
+
+  void RunDestructor(v8::Local<v8::Object> obj) {
+    if (m_pDestructor)
+      m_pDestructor(obj);
+  }
+
+ private:
+  UnownedPtr<const char> const m_ObjName;
   const FXJSOBJTYPE m_ObjType;
   const CFXJS_Engine::Constructor m_pConstructor;
   const CFXJS_Engine::Destructor m_pDestructor;
@@ -231,18 +261,16 @@
 static v8::Local<v8::ObjectTemplate> GetGlobalObjectTemplate(
     v8::Isolate* pIsolate) {
   FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(pIsolate);
-  for (int i = 0; i < pIsolateData->MaxObjDefinitionID(); ++i) {
+  for (uint32_t i = 1; i <= pIsolateData->CurrentMaxObjDefinitionID(); ++i) {
     CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(i);
-    if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL)
+    if (pObjDef->GetObjType() == FXJSOBJTYPE_GLOBAL)
       return pObjDef->GetInstanceTemplate();
   }
   if (!g_DefaultGlobalObjectTemplate) {
     v8::Local<v8::ObjectTemplate> hGlobalTemplate =
         v8::ObjectTemplate::New(pIsolate);
-    hGlobalTemplate->Set(
-        v8::Symbol::GetToStringTag(pIsolate),
-        v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal)
-            .ToLocalChecked());
+    hGlobalTemplate->Set(v8::Symbol::GetToStringTag(pIsolate),
+                         fxv8::NewStringHelper(pIsolate, "global"));
     g_DefaultGlobalObjectTemplate =
         new v8::Global<v8::ObjectTemplate>(pIsolate, hGlobalTemplate);
   }
@@ -255,15 +283,14 @@
   v8::Local<v8::Object> obj = value.Get(isolate);
   if (obj.IsEmpty())
     return;
-  int id = CFXJS_Engine::GetObjDefnID(obj);
-  if (id == -1)
+  uint32_t id = CFXJS_Engine::GetObjDefnID(obj);
+  if (id == 0)
     return;
   FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(isolate);
   CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(id);
   if (!pObjDef)
     return;
-  if (pObjDef->m_pDestructor)
-    pObjDef->m_pDestructor(obj);
+  pObjDef->RunDestructor(obj);
   CFXJS_Engine::FreeObjectPrivate(obj);
 }
 
@@ -273,16 +300,16 @@
 }
 
 V8TemplateMapTraits::MapType* V8TemplateMapTraits::MapFromWeakCallbackInfo(
-    const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
-  V8TemplateMap* pMap =
-      FXJS_PerIsolateData::Get(data.GetIsolate())->m_pDynamicObjsMap.get();
-  return pMap ? &pMap->m_map : nullptr;
+    const v8::WeakCallbackInfo<WeakCallbackDataType>& info) {
+  auto* pIsolateData = FXJS_PerIsolateData::Get(info.GetIsolate());
+  V8TemplateMap* pObjsMap = pIsolateData->GetDynamicObjsMap();
+  return pObjsMap ? pObjsMap->GetMap() : nullptr;
 }
 
 void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate) {
   if (g_isolate) {
-    ASSERT(g_embedderDataSlot == embedderDataSlot);
-    ASSERT(g_isolate == pIsolate);
+    DCHECK_EQ(g_embedderDataSlot, embedderDataSlot);
+    DCHECK_EQ(g_isolate, pIsolate);
     return;
   }
   g_embedderDataSlot = embedderDataSlot;
@@ -290,7 +317,7 @@
 }
 
 void FXJS_Release() {
-  ASSERT(!g_isolate || g_isolate_ref_count == 0);
+  DCHECK(!g_isolate || g_isolate_ref_count == 0);
   delete g_DefaultGlobalObjectTemplate;
   g_DefaultGlobalObjectTemplate = nullptr;
   g_isolate = nullptr;
@@ -317,8 +344,6 @@
   return g_isolate_ref_count;
 }
 
-FXJS_PerIsolateData::~FXJS_PerIsolateData() {}
-
 // static
 void FXJS_PerIsolateData::SetUp(v8::Isolate* pIsolate) {
   if (!pIsolate->GetData(g_embedderDataSlot))
@@ -327,26 +352,33 @@
 
 // static
 FXJS_PerIsolateData* FXJS_PerIsolateData::Get(v8::Isolate* pIsolate) {
-  return static_cast<FXJS_PerIsolateData*>(
-      pIsolate->GetData(g_embedderDataSlot));
-}
-
-int FXJS_PerIsolateData::MaxObjDefinitionID() const {
-  return pdfium::CollectionSize<int>(m_ObjectDefnArray);
+  auto* result =
+      static_cast<FXJS_PerIsolateData*>(pIsolate->GetData(g_embedderDataSlot));
+  CHECK(result->m_Tag == kPerIsolateDataTag);
+  return result;
 }
 
 FXJS_PerIsolateData::FXJS_PerIsolateData(v8::Isolate* pIsolate)
-    : m_pDynamicObjsMap(new V8TemplateMap(pIsolate)) {}
+    : m_Tag(kPerIsolateDataTag),
+      m_pDynamicObjsMap(std::make_unique<V8TemplateMap>(pIsolate)) {}
 
-CFXJS_ObjDefinition* FXJS_PerIsolateData::ObjDefinitionForID(int id) const {
-  return (id >= 0 && id < MaxObjDefinitionID()) ? m_ObjectDefnArray[id].get()
-                                                : nullptr;
+FXJS_PerIsolateData::~FXJS_PerIsolateData() = default;
+
+uint32_t FXJS_PerIsolateData::CurrentMaxObjDefinitionID() const {
+  return fxcrt::CollectionSize<uint32_t>(m_ObjectDefnArray);
 }
 
-int FXJS_PerIsolateData::AssignIDForObjDefinition(
+CFXJS_ObjDefinition* FXJS_PerIsolateData::ObjDefinitionForID(
+    uint32_t id) const {
+  return id > 0 && id <= CurrentMaxObjDefinitionID()
+             ? m_ObjectDefnArray[id - 1].get()
+             : nullptr;
+}
+
+uint32_t FXJS_PerIsolateData::AssignIDForObjDefinition(
     std::unique_ptr<CFXJS_ObjDefinition> pDefn) {
   m_ObjectDefnArray.push_back(std::move(pDefn));
-  return m_ObjectDefnArray.size() - 1;
+  return CurrentMaxObjDefinitionID();
 }
 
 CFXJS_Engine::CFXJS_Engine() : CFX_V8(nullptr) {}
@@ -356,9 +388,9 @@
 CFXJS_Engine::~CFXJS_Engine() = default;
 
 // static
-int CFXJS_Engine::GetObjDefnID(v8::Local<v8::Object> pObj) {
+uint32_t CFXJS_Engine::GetObjDefnID(v8::Local<v8::Object> pObj) {
   CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj);
-  return pData ? pData->m_ObjDefID : -1;
+  return pData ? pData->GetObjDefnID() : 0;
 }
 
 // static
@@ -366,9 +398,8 @@
                                     std::unique_ptr<CJS_Object> p) {
   CFXJS_PerObjectData* pPerObjectData =
       CFXJS_PerObjectData::GetFromObject(pObj);
-  if (!pPerObjectData)
-    return;
-  pPerObjectData->m_pPrivate = std::move(p);
+  if (pPerObjectData)
+    pPerObjectData->SetPrivate(std::move(p));
 }
 
 // static
@@ -379,20 +410,20 @@
   delete pData;
 }
 
-int CFXJS_Engine::DefineObj(const char* sObjName,
-                            FXJSOBJTYPE eObjType,
-                            CFXJS_Engine::Constructor pConstructor,
-                            CFXJS_Engine::Destructor pDestructor) {
+uint32_t CFXJS_Engine::DefineObj(const char* sObjName,
+                                 FXJSOBJTYPE eObjType,
+                                 CFXJS_Engine::Constructor pConstructor,
+                                 CFXJS_Engine::Destructor pDestructor) {
   v8::Isolate::Scope isolate_scope(GetIsolate());
   v8::HandleScope handle_scope(GetIsolate());
   FXJS_PerIsolateData::SetUp(GetIsolate());
   FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate());
   return pIsolateData->AssignIDForObjDefinition(
-      pdfium::MakeUnique<CFXJS_ObjDefinition>(GetIsolate(), sObjName, eObjType,
-                                              pConstructor, pDestructor));
+      std::make_unique<CFXJS_ObjDefinition>(GetIsolate(), sObjName, eObjType,
+                                            pConstructor, pDestructor));
 }
 
-void CFXJS_Engine::DefineObjMethod(int nObjDefnID,
+void CFXJS_Engine::DefineObjMethod(uint32_t nObjDefnID,
                                    const char* sMethodName,
                                    v8::FunctionCallback pMethodCall) {
   v8::Isolate::Scope isolate_scope(GetIsolate());
@@ -402,7 +433,7 @@
   pObjDef->DefineMethod(NewString(sMethodName), pMethodCall);
 }
 
-void CFXJS_Engine::DefineObjProperty(int nObjDefnID,
+void CFXJS_Engine::DefineObjProperty(uint32_t nObjDefnID,
                                      const char* sPropName,
                                      v8::AccessorGetterCallback pPropGet,
                                      v8::AccessorSetterCallback pPropPut) {
@@ -414,19 +445,21 @@
 }
 
 void CFXJS_Engine::DefineObjAllProperties(
-    int nObjDefnID,
+    uint32_t nObjDefnID,
     v8::GenericNamedPropertyQueryCallback pPropQurey,
     v8::GenericNamedPropertyGetterCallback pPropGet,
     v8::GenericNamedPropertySetterCallback pPropPut,
-    v8::GenericNamedPropertyDeleterCallback pPropDel) {
+    v8::GenericNamedPropertyDeleterCallback pPropDel,
+    v8::GenericNamedPropertyEnumeratorCallback pPropEnum) {
   v8::Isolate::Scope isolate_scope(GetIsolate());
   v8::HandleScope handle_scope(GetIsolate());
   FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate());
   CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(nObjDefnID);
-  pObjDef->DefineAllProperties(pPropQurey, pPropGet, pPropPut, pPropDel);
+  pObjDef->DefineAllProperties(pPropQurey, pPropGet, pPropPut, pPropDel,
+                               pPropEnum);
 }
 
-void CFXJS_Engine::DefineObjConst(int nObjDefnID,
+void CFXJS_Engine::DefineObjConst(uint32_t nObjDefnID,
                                   const char* sConstName,
                                   v8::Local<v8::Value> pDefault) {
   v8::Isolate::Scope isolate_scope(GetIsolate());
@@ -486,24 +519,15 @@
 
   v8::Context::Scope context_scope(v8Context);
   FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate());
-  int maxID = pIsolateData->MaxObjDefinitionID();
+  uint32_t maxID = pIsolateData->CurrentMaxObjDefinitionID();
   m_StaticObjects.resize(maxID + 1);
-  for (int i = 0; i < maxID; ++i) {
+  for (uint32_t i = 1; i <= maxID; ++i) {
     CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(i);
-    if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) {
-      CFXJS_PerObjectData::SetInObject(new CFXJS_PerObjectData(i),
-                                       v8Context->Global()
-                                           ->GetPrototype()
-                                           ->ToObject(v8Context)
-                                           .ToLocalChecked());
-      if (pObjDef->m_pConstructor) {
-        pObjDef->m_pConstructor(this, v8Context->Global()
-                                          ->GetPrototype()
-                                          ->ToObject(v8Context)
-                                          .ToLocalChecked());
-      }
-    } else if (pObjDef->m_ObjType == FXJSOBJTYPE_STATIC) {
-      v8::Local<v8::String> pObjName = NewString(pObjDef->m_ObjName);
+    if (pObjDef->GetObjType() == FXJSOBJTYPE_GLOBAL) {
+      CFXJS_PerObjectData::SetNewDataInObject(i, pThis);
+      pObjDef->RunConstructor(this, pThis, pThisProxy);
+    } else if (pObjDef->GetObjType() == FXJSOBJTYPE_STATIC) {
+      v8::Local<v8::String> pObjName = NewString(pObjDef->GetObjName());
       v8::Local<v8::Object> obj = NewFXJSBoundObject(i, FXJSOBJTYPE_STATIC);
       if (!obj.IsEmpty()) {
         v8Context->Global()->Set(v8Context, pObjName, obj).FromJust();
@@ -525,10 +549,10 @@
 
   m_ConstArrays.clear();
 
-  for (int i = 0; i < pIsolateData->MaxObjDefinitionID(); ++i) {
+  for (uint32_t i = 1; i <= pIsolateData->CurrentMaxObjDefinitionID(); ++i) {
     CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(i);
     v8::Local<v8::Object> pObj;
-    if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) {
+    if (pObjDef->GetObjType() == FXJSOBJTYPE_GLOBAL) {
       pObj =
           context->Global()->GetPrototype()->ToObject(context).ToLocalChecked();
     } else if (!m_StaticObjects[i].IsEmpty()) {
@@ -536,8 +560,7 @@
       m_StaticObjects[i].Reset();
     }
     if (!pObj.IsEmpty()) {
-      if (pObjDef->m_pDestructor)
-        pObjDef->m_pDestructor(pObj);
+      pObjDef->RunDestructor(pObj);
       FreeObjectPrivate(pObj);
     }
   }
@@ -551,7 +574,7 @@
   GetIsolate()->SetData(g_embedderDataSlot, nullptr);
 }
 
-Optional<IJS_Runtime::JS_Error> CFXJS_Engine::Execute(
+absl::optional<IJS_Runtime::JS_Error> CFXJS_Engine::Execute(
     const WideString& script) {
   v8::Isolate::Scope isolate_scope(GetIsolate());
   v8::TryCatch try_catch(GetIsolate());
@@ -576,10 +599,10 @@
     std::tie(line, column) = GetLineAndColumnFromError(msg, context);
     return IJS_Runtime::JS_Error(line, column, WideString::FromUTF8(*error));
   }
-  return pdfium::nullopt;
+  return absl::nullopt;
 }
 
-v8::Local<v8::Object> CFXJS_Engine::NewFXJSBoundObject(int nObjDefnID,
+v8::Local<v8::Object> CFXJS_Engine::NewFXJSBoundObject(uint32_t nObjDefnID,
                                                        FXJSOBJTYPE type) {
   v8::Isolate::Scope isolate_scope(GetIsolate());
   v8::Local<v8::Context> context = GetIsolate()->GetCurrentContext();
@@ -595,15 +618,13 @@
   if (!pObjDef->GetInstanceTemplate()->NewInstance(context).ToLocal(&obj))
     return v8::Local<v8::Object>();
 
-  CFXJS_PerObjectData* pObjData = new CFXJS_PerObjectData(nObjDefnID);
-  CFXJS_PerObjectData::SetInObject(pObjData, obj);
-  if (pObjDef->m_pConstructor)
-    pObjDef->m_pConstructor(this, obj);
-
+  CFXJS_PerObjectData::SetNewDataInObject(nObjDefnID, obj);
+  pObjDef->RunConstructor(this, obj, obj);
   if (type == FXJSOBJTYPE_DYNAMIC) {
     auto* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate());
-    if (pIsolateData->m_pDynamicObjsMap)
-      pIsolateData->m_pDynamicObjsMap->SetAndMakeWeak(pObjData, obj);
+    V8TemplateMap* pObjsMap = pIsolateData->GetDynamicObjsMap();
+    if (pObjsMap)
+      pObjsMap->SetAndMakeWeak(obj);
   }
   return obj;
 }
@@ -619,7 +640,7 @@
 }
 
 void CFXJS_Engine::Error(const WideString& message) {
-  GetIsolate()->ThrowException(NewString(message.AsStringView()));
+  fxv8::ThrowExceptionHelper(GetIsolate(), message.AsStringView());
 }
 
 v8::Local<v8::Context> CFXJS_Engine::GetV8Context() {
@@ -627,10 +648,11 @@
 }
 
 // static
-CJS_Object* CFXJS_Engine::GetObjectPrivate(v8::Local<v8::Object> pObj) {
+CJS_Object* CFXJS_Engine::GetObjectPrivate(v8::Isolate* pIsolate,
+                                           v8::Local<v8::Object> pObj) {
   auto* pData = CFXJS_PerObjectData::GetFromObject(pObj);
   if (pData)
-    return pData->m_pPrivate.get();
+    return pData->GetPrivate();
 
   if (pObj.IsEmpty())
     return nullptr;
@@ -645,16 +667,16 @@
   if (!pProtoData)
     return nullptr;
 
-  auto* pIsolateData = FXJS_PerIsolateData::Get(v8::Isolate::GetCurrent());
+  auto* pIsolateData = FXJS_PerIsolateData::Get(pIsolate);
   if (!pIsolateData)
     return nullptr;
 
   CFXJS_ObjDefinition* pObjDef =
-      pIsolateData->ObjDefinitionForID(pProtoData->m_ObjDefID);
-  if (!pObjDef || pObjDef->m_ObjType != FXJSOBJTYPE_GLOBAL)
+      pIsolateData->ObjDefinitionForID(pProtoData->GetObjDefnID());
+  if (!pObjDef || pObjDef->GetObjType() != FXJSOBJTYPE_GLOBAL)
     return nullptr;
 
-  return pProtoData->m_pPrivate.get();
+  return pProtoData->GetPrivate();
 }
 
 v8::Local<v8::Array> CFXJS_Engine::GetConstArray(const WideString& name) {
diff --git a/fxjs/cfxjs_engine.h b/fxjs/cfxjs_engine.h
index 933c250..b0011fc 100644
--- a/fxjs/cfxjs_engine.h
+++ b/fxjs/cfxjs_engine.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,21 +17,21 @@
 #include <functional>
 #include <map>
 #include <memory>
+#include <utility>
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 #include "fxjs/cfx_v8.h"
 #include "fxjs/ijs_runtime.h"
-#include "v8/include/v8.h"
+#include "v8/include/v8-forward.h"
+#include "v8/include/v8-function-callback.h"
+#include "v8/include/v8-persistent-handle.h"
+#include "v8/include/v8-template.h"
 
 class CFXJS_ObjDefinition;
 class CJS_Object;
 class V8TemplateMap;
 
-// CFXJS_ENGINE places no restrictions on this class; it merely passes it
-// on to caller-provided methods.
-class IJS_EventContext;  // A description of the event that caused JS execution.
-
 enum FXJSOBJTYPE {
   FXJSOBJTYPE_DYNAMIC = 0,  // Created by native method and returned to JS.
   FXJSOBJTYPE_STATIC,       // Created by init and hung off of global object.
@@ -51,16 +51,22 @@
   static void SetUp(v8::Isolate* pIsolate);
   static FXJS_PerIsolateData* Get(v8::Isolate* pIsolate);
 
-  int MaxObjDefinitionID() const;
-  CFXJS_ObjDefinition* ObjDefinitionForID(int id) const;
-  int AssignIDForObjDefinition(std::unique_ptr<CFXJS_ObjDefinition> pDefn);
+  uint32_t CurrentMaxObjDefinitionID() const;
+  CFXJS_ObjDefinition* ObjDefinitionForID(uint32_t id) const;
+  uint32_t AssignIDForObjDefinition(std::unique_ptr<CFXJS_ObjDefinition> pDefn);
+  V8TemplateMap* GetDynamicObjsMap() { return m_pDynamicObjsMap.get(); }
+  ExtensionIface* GetExtension() { return m_pExtension.get(); }
+  void SetExtension(std::unique_ptr<ExtensionIface> extension) {
+    m_pExtension = std::move(extension);
+  }
 
+ private:
+  explicit FXJS_PerIsolateData(v8::Isolate* pIsolate);
+
+  const wchar_t* const m_Tag;  // Raw, always a literal.
   std::vector<std::unique_ptr<CFXJS_ObjDefinition>> m_ObjectDefnArray;
   std::unique_ptr<V8TemplateMap> m_pDynamicObjsMap;
-  std::unique_ptr<ExtensionIface> m_pFXJSERuntimeData;
-
- protected:
-  explicit FXJS_PerIsolateData(v8::Isolate* pIsolate);
+  std::unique_ptr<ExtensionIface> m_pExtension;
 };
 
 void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate);
@@ -79,35 +85,39 @@
   explicit CFXJS_Engine(v8::Isolate* pIsolate);
   ~CFXJS_Engine() override;
 
-  using Constructor =
-      std::function<void(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj)>;
+  using Constructor = std::function<void(CFXJS_Engine* pEngine,
+                                         v8::Local<v8::Object> obj,
+                                         v8::Local<v8::Object> proxy)>;
   using Destructor = std::function<void(v8::Local<v8::Object> obj)>;
 
-  static int GetObjDefnID(v8::Local<v8::Object> pObj);
-  static CJS_Object* GetObjectPrivate(v8::Local<v8::Object> pObj);
+  static uint32_t GetObjDefnID(v8::Local<v8::Object> pObj);
+  static CJS_Object* GetObjectPrivate(v8::Isolate* pIsolate,
+                                      v8::Local<v8::Object> pObj);
   static void SetObjectPrivate(v8::Local<v8::Object> pObj,
                                std::unique_ptr<CJS_Object> p);
   static void FreeObjectPrivate(v8::Local<v8::Object> pObj);
 
-  // Always returns a valid, newly-created objDefnID.
-  int DefineObj(const char* sObjName,
-                FXJSOBJTYPE eObjType,
-                Constructor pConstructor,
-                Destructor pDestructor);
+  // Always returns a valid (i.e. non-zero), newly-created objDefnID.
+  uint32_t DefineObj(const char* sObjName,
+                     FXJSOBJTYPE eObjType,
+                     Constructor pConstructor,
+                     Destructor pDestructor);
 
-  void DefineObjMethod(int nObjDefnID,
+  void DefineObjMethod(uint32_t nObjDefnID,
                        const char* sMethodName,
                        v8::FunctionCallback pMethodCall);
-  void DefineObjProperty(int nObjDefnID,
+  void DefineObjProperty(uint32_t nObjDefnID,
                          const char* sPropName,
                          v8::AccessorGetterCallback pPropGet,
                          v8::AccessorSetterCallback pPropPut);
-  void DefineObjAllProperties(int nObjDefnID,
-                              v8::GenericNamedPropertyQueryCallback pPropQurey,
-                              v8::GenericNamedPropertyGetterCallback pPropGet,
-                              v8::GenericNamedPropertySetterCallback pPropPut,
-                              v8::GenericNamedPropertyDeleterCallback pPropDel);
-  void DefineObjConst(int nObjDefnID,
+  void DefineObjAllProperties(
+      uint32_t nObjDefnID,
+      v8::GenericNamedPropertyQueryCallback pPropQurey,
+      v8::GenericNamedPropertyGetterCallback pPropGet,
+      v8::GenericNamedPropertySetterCallback pPropPut,
+      v8::GenericNamedPropertyDeleterCallback pPropDel,
+      v8::GenericNamedPropertyEnumeratorCallback pPropEnum);
+  void DefineObjConst(uint32_t nObjDefnID,
                       const char* sConstName,
                       v8::Local<v8::Value> pDefault);
   void DefineGlobalMethod(const char* sMethodName,
@@ -120,10 +130,11 @@
   void ReleaseEngine();
 
   // Called after FXJS_InitializeEngine call made.
-  Optional<IJS_Runtime::JS_Error> Execute(const WideString& script);
+  absl::optional<IJS_Runtime::JS_Error> Execute(const WideString& script);
 
   v8::Local<v8::Object> GetThisObj();
-  v8::Local<v8::Object> NewFXJSBoundObject(int nObjDefnID, FXJSOBJTYPE type);
+  v8::Local<v8::Object> NewFXJSBoundObject(uint32_t nObjDefnID,
+                                           FXJSOBJTYPE type);
   void Error(const WideString& message);
 
   v8::Local<v8::Context> GetV8Context();
diff --git a/fxjs/cfxjs_engine_embeddertest.cpp b/fxjs/cfxjs_engine_embeddertest.cpp
index c18171e..70e84ab 100644
--- a/fxjs/cfxjs_engine_embeddertest.cpp
+++ b/fxjs/cfxjs_engine_embeddertest.cpp
@@ -1,11 +1,16 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxjs/cfxjs_engine.h"
 
+#include "testing/external_engine_embedder_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/js_embedder_test.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-local-handle.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-value.h"
 
 namespace {
 
@@ -19,7 +24,7 @@
 
 }  // namespace
 
-using CFXJSEngineEmbedderTest = JSEmbedderTest;
+class CFXJSEngineEmbedderTest : public ExternalEngineEmbedderTest {};
 
 void CheckAssignmentInEngineContext(CFXJS_Engine* current_engine,
                                     double expected) {
@@ -35,7 +40,8 @@
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(GetV8Context());
 
-  Optional<IJS_Runtime::JS_Error> err = engine()->Execute(WideString(kScript1));
+  absl::optional<IJS_Runtime::JS_Error> err =
+      engine()->Execute(WideString(kScript1));
   EXPECT_FALSE(err);
   CheckAssignmentInEngineContext(engine(), kExpected1);
 }
@@ -52,7 +58,7 @@
 
   v8::Context::Scope context_scope(GetV8Context());
   {
-    Optional<IJS_Runtime::JS_Error> err =
+    absl::optional<IJS_Runtime::JS_Error> err =
         engine()->Execute(WideString(kScript0));
     EXPECT_FALSE(err);
     CheckAssignmentInEngineContext(engine(), kExpected0);
@@ -60,7 +66,8 @@
   {
     // engine1 executing in engine1's context doesn't affect main.
     v8::Context::Scope context_scope1(engine1.GetV8Context());
-    Optional<IJS_Runtime::JS_Error> err = engine1.Execute(WideString(kScript1));
+    absl::optional<IJS_Runtime::JS_Error> err =
+        engine1.Execute(WideString(kScript1));
     EXPECT_FALSE(err);
     CheckAssignmentInEngineContext(engine(), kExpected0);
     CheckAssignmentInEngineContext(&engine1, kExpected1);
@@ -68,7 +75,8 @@
   {
     // engine1 executing in engine2's context doesn't affect engine1.
     v8::Context::Scope context_scope2(engine2.GetV8Context());
-    Optional<IJS_Runtime::JS_Error> err = engine1.Execute(WideString(kScript2));
+    absl::optional<IJS_Runtime::JS_Error> err =
+        engine1.Execute(WideString(kScript2));
     EXPECT_FALSE(err);
     CheckAssignmentInEngineContext(engine(), kExpected0);
     CheckAssignmentInEngineContext(&engine1, kExpected1);
@@ -83,7 +91,7 @@
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(GetV8Context());
 
-  Optional<IJS_Runtime::JS_Error> err =
+  absl::optional<IJS_Runtime::JS_Error> err =
       engine()->Execute(L"functoon(x) { return x+1; }");
   EXPECT_TRUE(err);
   EXPECT_STREQ(L"SyntaxError: Unexpected token '{'", err->exception.c_str());
@@ -96,11 +104,12 @@
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(GetV8Context());
 
-  Optional<IJS_Runtime::JS_Error> err =
+  absl::optional<IJS_Runtime::JS_Error> err =
       engine()->Execute(L"let a = 3;\nundefined.colour");
   EXPECT_TRUE(err);
-  EXPECT_EQ(L"TypeError: Cannot read property 'colour' of undefined",
-            err->exception);
+  EXPECT_EQ(
+      L"TypeError: Cannot read properties of undefined (reading 'colour')",
+      err->exception);
   EXPECT_EQ(2, err->line);
   EXPECT_EQ(10, err->column);
 }
diff --git a/fxjs/cfxjs_engine_unittest.cpp b/fxjs/cfxjs_engine_unittest.cpp
index e0f7301..8d6c36b 100644
--- a/fxjs/cfxjs_engine_unittest.cpp
+++ b/fxjs/cfxjs_engine_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,22 +6,23 @@
 
 #include <memory>
 
-#include "fxjs/cfx_v8_unittest.h"
 #include "fxjs/cjs_object.h"
+#include "testing/fxv8_unittest.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-isolate.h"
 
 class FXJSEngineUnitTest : public FXV8UnitTest {
  public:
   FXJSEngineUnitTest() = default;
   ~FXJSEngineUnitTest() override = default;
 
+  // FXV8UnitTest:
   void SetUp() override {
     FXV8UnitTest::SetUp();
     FXJS_Initialize(1, isolate());
-    engine_ = pdfium::MakeUnique<CFXJS_Engine>(isolate());
+    engine_ = std::make_unique<CFXJS_Engine>(isolate());
   }
-
   void TearDown() override { FXJS_Release(); }
 
   CFXJS_Engine* engine() const { return engine_.get(); }
@@ -36,15 +37,22 @@
 static bool temp_destroyed = false;
 
 TEST_F(FXJSEngineUnitTest, GC) {
+  // Reset variables since there might be multiple iterations.
+  perm_created = false;
+  perm_destroyed = false;
+  temp_created = false;
+  temp_destroyed = false;
+
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
 
-  // Object: 0
+  // Object: 1
   engine()->DefineObj(
       "perm", FXJSOBJTYPE_DYNAMIC,
-      [](CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
+      [](CFXJS_Engine* pEngine, v8::Local<v8::Object> obj,
+         v8::Local<v8::Object> proxy) {
         pEngine->SetObjectPrivate(obj,
-                                  pdfium::MakeUnique<CJS_Object>(obj, nullptr));
+                                  std::make_unique<CJS_Object>(proxy, nullptr));
         perm_created = true;
       },
       [](v8::Local<v8::Object> obj) {
@@ -52,12 +60,13 @@
         CFXJS_Engine::SetObjectPrivate(obj, nullptr);
       });
 
-  // Object: 1
+  // Object: 2
   engine()->DefineObj(
       "temp", FXJSOBJTYPE_DYNAMIC,
-      [](CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
+      [](CFXJS_Engine* pEngine, v8::Local<v8::Object> obj,
+         v8::Local<v8::Object> proxy) {
         pEngine->SetObjectPrivate(obj,
-                                  pdfium::MakeUnique<CJS_Object>(obj, nullptr));
+                                  std::make_unique<CJS_Object>(proxy, nullptr));
         temp_created = true;
       },
       [](v8::Local<v8::Object> obj) {
@@ -69,7 +78,7 @@
 
   v8::Context::Scope context_scope(engine()->GetV8Context());
   v8::Local<v8::Object> perm =
-      engine()->NewFXJSBoundObject(0, FXJSOBJTYPE_DYNAMIC);
+      engine()->NewFXJSBoundObject(1, FXJSOBJTYPE_DYNAMIC);
   EXPECT_FALSE(perm.IsEmpty());
   EXPECT_TRUE(perm_created);
   EXPECT_FALSE(perm_destroyed);
@@ -77,13 +86,13 @@
   {
     v8::HandleScope inner_handle_scope(isolate());
     v8::Local<v8::Object> temp =
-        engine()->NewFXJSBoundObject(1, FXJSOBJTYPE_DYNAMIC);
+        engine()->NewFXJSBoundObject(2, FXJSOBJTYPE_DYNAMIC);
     EXPECT_FALSE(temp.IsEmpty());
     EXPECT_TRUE(temp_created);
     EXPECT_FALSE(temp_destroyed);
   }
 
-  Optional<IJS_Runtime::JS_Error> err = engine()->Execute(L"gc();");
+  absl::optional<IJS_Runtime::JS_Error> err = engine()->Execute(L"gc();");
   EXPECT_FALSE(err);
 
   EXPECT_TRUE(perm_created);
diff --git a/fxjs/cjs_annot.cpp b/fxjs/cjs_annot.cpp
index 292e586..cee6111 100644
--- a/fxjs/cjs_annot.cpp
+++ b/fxjs/cjs_annot.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,6 @@
 #include "fxjs/cjs_annot.h"
 
 #include "constants/annotation_flags.h"
-#include "fpdfsdk/cpdfsdk_baannot.h"
 #include "fxjs/cjs_event_context.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/js_define.h"
@@ -18,12 +17,12 @@
     {"name", get_name_static, set_name_static},
     {"type", get_type_static, set_type_static}};
 
-int CJS_Annot::ObjDefnID = -1;
+uint32_t CJS_Annot::ObjDefnID = 0;
 
 const char CJS_Annot::kName[] = "Annot";
 
 // static
-int CJS_Annot::GetObjDefnID() {
+uint32_t CJS_Annot::GetObjDefnID() {
   return ObjDefnID;
 }
 
@@ -47,7 +46,7 @@
   if (!m_pAnnot)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_Annot* pPDFAnnot = m_pAnnot->AsBAAnnot()->GetPDFAnnot();
+  CPDF_Annot* pPDFAnnot = m_pAnnot->GetPDFAnnot();
   return CJS_Result::Success(pRuntime->NewBoolean(pPDFAnnot->IsHidden()));
 }
 
@@ -56,7 +55,7 @@
   // May invalidate m_pAnnot.
   bool bHidden = pRuntime->ToBoolean(vp);
 
-  CPDFSDK_BAAnnot* pBAAnnot = ToBAAnnot(m_pAnnot.Get());
+  CPDFSDK_BAAnnot* pBAAnnot = m_pAnnot.Get();
   if (!pBAAnnot)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -77,7 +76,7 @@
 }
 
 CJS_Result CJS_Annot::get_name(CJS_Runtime* pRuntime) {
-  CPDFSDK_BAAnnot* pBAAnnot = ToBAAnnot(m_pAnnot.Get());
+  CPDFSDK_BAAnnot* pBAAnnot = m_pAnnot.Get();
   if (!pBAAnnot)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -89,7 +88,7 @@
   // May invalidate m_pAnnot.
   WideString annotName = pRuntime->ToWideString(vp);
 
-  CPDFSDK_BAAnnot* pBAAnnot = ToBAAnnot(m_pAnnot.Get());
+  CPDFSDK_BAAnnot* pBAAnnot = m_pAnnot.Get();
   if (!pBAAnnot)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -98,14 +97,12 @@
 }
 
 CJS_Result CJS_Annot::get_type(CJS_Runtime* pRuntime) {
-  CPDFSDK_BAAnnot* pBAAnnot = ToBAAnnot(m_pAnnot.Get());
+  CPDFSDK_BAAnnot* pBAAnnot = m_pAnnot.Get();
   if (!pBAAnnot)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   return CJS_Result::Success(pRuntime->NewString(
-      WideString::FromDefANSI(
-          CPDF_Annot::AnnotSubtypeToString(pBAAnnot->GetAnnotSubtype())
-              .AsStringView())
+      CPDF_Annot::AnnotSubtypeToString(pBAAnnot->GetAnnotSubtype())
           .AsStringView()));
 }
 
diff --git a/fxjs/cjs_annot.h b/fxjs/cjs_annot.h
index ceb2615..0e53407 100644
--- a/fxjs/cjs_annot.h
+++ b/fxjs/cjs_annot.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,13 @@
 #ifndef FXJS_CJS_ANNOT_H_
 #define FXJS_CJS_ANNOT_H_
 
+#include "fpdfsdk/cpdfsdk_baannot.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/js_define.h"
 
-class CPDFSDK_BAAnnot;
-
 class CJS_Annot final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
   CJS_Annot(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
@@ -27,7 +26,7 @@
   JS_STATIC_PROP(type, type, CJS_Annot)
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSPropertySpec PropertySpecs[];
 
@@ -40,7 +39,7 @@
   CJS_Result get_type(CJS_Runtime* pRuntime);
   CJS_Result set_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
 
-  ObservedPtr<CPDFSDK_Annot> m_pAnnot;
+  ObservedPtr<CPDFSDK_BAAnnot> m_pAnnot;
 };
 
 #endif  // FXJS_CJS_ANNOT_H_
diff --git a/fxjs/cjs_app.cpp b/fxjs/cjs_app.cpp
index 992fd98..0784e7e 100644
--- a/fxjs/cjs_app.cpp
+++ b/fxjs/cjs_app.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,24 +6,33 @@
 
 #include "fxjs/cjs_app.h"
 
+#include <stdint.h>
+
+#include <algorithm>
 #include <utility>
 
+#include "core/fxcrt/fixed_zeroed_data_vector.h"
+#include "core/fxcrt/stl_util.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fxjs/cjs_document.h"
 #include "fxjs/cjs_timerobj.h"
 #include "fxjs/global_timer.h"
 #include "fxjs/ijs_event_context.h"
 #include "fxjs/js_resources.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "v8/include/v8-container.h"
 
-#define JS_STR_VIEWERTYPE L"pdfium"
-#define JS_STR_VIEWERVARIATION L"Full"
-#define JS_STR_PLATFORM L"WIN"
-#define JS_STR_LANGUAGE L"ENU"
-#define JS_NUM_VIEWERVERSION 8
-#define JS_NUM_VIEWERVERSION_XFA 11
-#define JS_NUM_FORMSVERSION 7
+namespace {
+
+constexpr wchar_t kStrViewerType[] = L"pdfium";
+constexpr wchar_t kStrViewerVariation[] = L"Full";
+constexpr wchar_t kStrPlatform[] = L"WIN";
+constexpr wchar_t kStrLanguage[] = L"ENU";
+constexpr int kNumViewerVersion = 8;
+constexpr int kNumViewerVersionXfa = 11;
+constexpr int kNumFormsVersion = 7;
+
+}  // namespace
 
 const JSPropertySpec CJS_App::PropertySpecs[] = {
     {"activeDocs", get_active_docs_static, set_active_docs_static},
@@ -64,12 +73,12 @@
     {"setInterval", setInterval_static},
     {"setTimeOut", setTimeOut_static}};
 
-int CJS_App::ObjDefnID = -1;
+uint32_t CJS_App::ObjDefnID = 0;
 
 const char CJS_App::kName[] = "app";
 
 // static
-int CJS_App::GetObjDefnID() {
+uint32_t CJS_App::GetObjDefnID() {
   return ObjDefnID;
 }
 
@@ -88,7 +97,7 @@
 
 CJS_Result CJS_App::get_active_docs(CJS_Runtime* pRuntime) {
   v8::Local<v8::Object> pObj = pRuntime->GetThisObj();
-  auto pJSDocument = JSGetObject<CJS_Document>(pObj);
+  auto pJSDocument = JSGetObject<CJS_Document>(pRuntime->GetIsolate(), pObj);
   if (!pJSDocument)
     return CJS_Result::Failure(JSMessage::kObjectTypeError);
   v8::Local<v8::Array> aDocs = pRuntime->NewArray();
@@ -117,7 +126,7 @@
 }
 
 CJS_Result CJS_App::get_forms_version(CJS_Runtime* pRuntime) {
-  return CJS_Result::Success(pRuntime->NewNumber(JS_NUM_FORMSVERSION));
+  return CJS_Result::Success(pRuntime->NewNumber(kNumFormsVersion));
 }
 
 CJS_Result CJS_App::set_forms_version(CJS_Runtime* pRuntime,
@@ -126,7 +135,7 @@
 }
 
 CJS_Result CJS_App::get_viewer_type(CJS_Runtime* pRuntime) {
-  return CJS_Result::Success(pRuntime->NewString(JS_STR_VIEWERTYPE));
+  return CJS_Result::Success(pRuntime->NewString(kStrViewerType));
 }
 
 CJS_Result CJS_App::set_viewer_type(CJS_Runtime* pRuntime,
@@ -135,7 +144,7 @@
 }
 
 CJS_Result CJS_App::get_viewer_variation(CJS_Runtime* pRuntime) {
-  return CJS_Result::Success(pRuntime->NewString(JS_STR_VIEWERVARIATION));
+  return CJS_Result::Success(pRuntime->NewString(kStrViewerVariation));
 }
 
 CJS_Result CJS_App::set_viewer_variation(CJS_Runtime* pRuntime,
@@ -147,8 +156,8 @@
   CPDF_Document::Extension* pContext =
       pRuntime->GetFormFillEnv()->GetDocExtension();
   int version = pContext && pContext->ContainsExtensionForm()
-                    ? JS_NUM_VIEWERVERSION_XFA
-                    : JS_NUM_VIEWERVERSION;
+                    ? kNumViewerVersionXfa
+                    : kNumViewerVersion;
   return CJS_Result::Success(pRuntime->NewNumber(version));
 }
 
@@ -164,7 +173,7 @@
     if (!platform.IsEmpty())
       return CJS_Result::Success(pRuntime->NewString(platform.AsStringView()));
   }
-  return CJS_Result::Success(pRuntime->NewString(JS_STR_PLATFORM));
+  return CJS_Result::Success(pRuntime->NewString(kStrPlatform));
 }
 
 CJS_Result CJS_App::set_platform(CJS_Runtime* pRuntime,
@@ -179,7 +188,7 @@
     if (!language.IsEmpty())
       return CJS_Result::Success(pRuntime->NewString(language.AsStringView()));
   }
-  return CJS_Result::Success(pRuntime->NewString(JS_STR_LANGUAGE));
+  return CJS_Result::Success(pRuntime->NewString(kStrLanguage));
 }
 
 CJS_Result CJS_App::set_language(CJS_Runtime* pRuntime,
@@ -250,7 +259,7 @@
     swTitle = JSGetStringFromID(JSMessage::kAlert);
 
   pRuntime->BeginBlock();
-  pFormFillEnv->KillFocusAnnot(0);
+  pFormFillEnv->KillFocusAnnot({});
   v8::Local<v8::Value> ret = pRuntime->NewNumber(
       pFormFillEnv->JS_appAlert(swMsg, swTitle, iType, iIcon));
   pRuntime->EndBlock();
@@ -302,7 +311,7 @@
     return CJS_Result::Failure(JSMessage::kInvalidInputError);
 
   uint32_t dwInterval = params.size() > 1 ? pRuntime->ToInt32(params[1]) : 1000;
-  auto timerRef = pdfium::MakeUnique<GlobalTimer>(
+  auto timerRef = std::make_unique<GlobalTimer>(
       this, pRuntime, GlobalTimer::Type::kRepeating, script, dwInterval, 0);
   GlobalTimer* pTimerRef = timerRef.get();
   m_Timers.insert(std::move(timerRef));
@@ -312,8 +321,8 @@
   if (pRetObj.IsEmpty())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  auto* pJS_TimerObj =
-      static_cast<CJS_TimerObj*>(CFXJS_Engine::GetObjectPrivate(pRetObj));
+  auto* pJS_TimerObj = static_cast<CJS_TimerObj*>(
+      CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pRetObj));
 
   pJS_TimerObj->SetTimer(pTimerRef);
   return CJS_Result::Success(pRetObj);
@@ -330,9 +339,9 @@
     return CJS_Result::Failure(JSMessage::kInvalidInputError);
 
   uint32_t dwTimeOut = params.size() > 1 ? pRuntime->ToInt32(params[1]) : 1000;
-  auto timerRef = pdfium::MakeUnique<GlobalTimer>(this, pRuntime,
-                                                  GlobalTimer::Type::kOneShot,
-                                                  script, dwTimeOut, dwTimeOut);
+  auto timerRef =
+      std::make_unique<GlobalTimer>(this, pRuntime, GlobalTimer::Type::kOneShot,
+                                    script, dwTimeOut, dwTimeOut);
   GlobalTimer* pTimerRef = timerRef.get();
   m_Timers.insert(std::move(timerRef));
 
@@ -341,8 +350,8 @@
   if (pRetObj.IsEmpty())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  auto* pJS_TimerObj =
-      static_cast<CJS_TimerObj*>(CFXJS_Engine::GetObjectPrivate(pRetObj));
+  auto* pJS_TimerObj = static_cast<CJS_TimerObj*>(
+      CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pRetObj));
 
   pJS_TimerObj->SetTimer(pTimerRef);
   return CJS_Result::Success(pRetObj);
@@ -374,7 +383,7 @@
     return;
 
   v8::Local<v8::Object> pObj = pRuntime->ToObject(param);
-  auto pTimer = JSGetObject<CJS_TimerObj>(pObj);
+  auto pTimer = JSGetObject<CJS_TimerObj>(pRuntime->GetIsolate(), pObj);
   if (!pTimer)
     return;
 
@@ -394,7 +403,7 @@
 }
 
 void CJS_App::CancelProc(GlobalTimer* pTimer) {
-  m_Timers.erase(pdfium::FakeUniquePtr<GlobalTimer>(pTimer));
+  m_Timers.erase(fxcrt::MakeFakeUniquePtr(pTimer));
 }
 
 void CJS_App::RunJsScript(CJS_Runtime* pRuntime, const WideString& wsScript) {
@@ -453,8 +462,8 @@
     cMsg = pRuntime->ToWideString(newParams[5]);
 
   pRuntime->BeginBlock();
-  pRuntime->GetFormFillEnv()->JS_docmailForm(nullptr, 0, bUI, cTo, cSubject,
-                                             cCc, cBcc, cMsg);
+  pRuntime->GetFormFillEnv()->JS_docmailForm(pdfium::span<const uint8_t>(), bUI,
+                                             cTo, cSubject, cCc, cBcc, cMsg);
   pRuntime->EndBlock();
   return CJS_Result::Success();
 }
@@ -541,18 +550,20 @@
   if (IsExpandedParamKnown(newParams[4]))
     swLabel = pRuntime->ToWideString(newParams[4]);
 
-  const int MAX_INPUT_BYTES = 2048;
-  std::vector<uint8_t> pBuff(MAX_INPUT_BYTES + 2);
-  int nLengthBytes = pRuntime->GetFormFillEnv()->JS_appResponse(
-      swQuestion, swTitle, swDefault, swLabel, bPassword, pBuff.data(),
-      MAX_INPUT_BYTES);
-
-  if (nLengthBytes < 0 || nLengthBytes > MAX_INPUT_BYTES)
+  constexpr int kMaxWideChars = 1024;
+  constexpr int kMaxBytes = kMaxWideChars * sizeof(uint16_t);
+  FixedZeroedDataVector<uint16_t> buffer(kMaxWideChars);
+  pdfium::span<uint16_t> buffer_span = buffer.writable_span();
+  int byte_length = pRuntime->GetFormFillEnv()->JS_appResponse(
+      swQuestion, swTitle, swDefault, swLabel, bPassword,
+      pdfium::as_writable_bytes(buffer_span));
+  if (byte_length < 0 || byte_length > kMaxBytes)
     return CJS_Result::Failure(JSMessage::kParamTooLongError);
 
+  buffer_span = buffer_span.first(
+      std::min<size_t>(kMaxWideChars, byte_length / sizeof(uint16_t)));
   return CJS_Result::Success(pRuntime->NewString(
-      WideString::FromUTF16LE(reinterpret_cast<uint16_t*>(pBuff.data()),
-                              nLengthBytes / sizeof(uint16_t))
+      WideString::FromUTF16LE(buffer_span.data(), buffer_span.size())
           .AsStringView()));
 }
 
diff --git a/fxjs/cjs_app.h b/fxjs/cjs_app.h
index 524a7f0..23b5c1f 100644
--- a/fxjs/cjs_app.h
+++ b/fxjs/cjs_app.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -19,7 +19,7 @@
 
 class CJS_App final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
   CJS_App(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
@@ -66,7 +66,7 @@
   JS_STATIC_METHOD(setTimeOut, CJS_App)
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSPropertySpec PropertySpecs[];
   static const JSMethodSpec MethodSpecs[];
diff --git a/fxjs/cjs_border.cpp b/fxjs/cjs_border.cpp
index 35204d7..b8edd09 100644
--- a/fxjs/cjs_border.cpp
+++ b/fxjs/cjs_border.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,7 +13,7 @@
     {"i", JSConstSpec::String, 0, "inset"},
     {"u", JSConstSpec::String, 0, "underline"}};
 
-int CJS_Border::ObjDefnID = -1;
+uint32_t CJS_Border::ObjDefnID = 0;
 
 // static
 void CJS_Border::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_border.h b/fxjs/cjs_border.h
index 0a306f7..29a9251 100644
--- a/fxjs/cjs_border.h
+++ b/fxjs/cjs_border.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_Border() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/cjs_color.cpp b/fxjs/cjs_color.cpp
index 2b75e75..22b31fc 100644
--- a/fxjs/cjs_color.cpp
+++ b/fxjs/cjs_color.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,10 +11,11 @@
 
 #include "core/fxge/cfx_color.h"
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/cjs_runtime.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_define.h"
+#include "v8/include/v8-container.h"
 
 const JSPropertySpec CJS_Color::PropertySpecs[] = {
     {"black", get_black_static, set_black_static},
@@ -33,11 +34,11 @@
 const JSMethodSpec CJS_Color::MethodSpecs[] = {{"convert", convert_static},
                                                {"equal", equal_static}};
 
-int CJS_Color::ObjDefnID = -1;
+uint32_t CJS_Color::ObjDefnID = 0;
 const char CJS_Color::kName[] = "color";
 
 // static
-int CJS_Color::GetObjDefnID() {
+uint32_t CJS_Color::GetObjDefnID() {
   return ObjDefnID;
 }
 
@@ -54,23 +55,23 @@
                                                        const CFX_Color& color) {
   v8::Local<v8::Array> array;
   switch (color.nColorType) {
-    case CFX_Color::kTransparent:
+    case CFX_Color::Type::kTransparent:
       array = pRuntime->NewArray();
       pRuntime->PutArrayElement(array, 0, pRuntime->NewString("T"));
       break;
-    case CFX_Color::kGray:
+    case CFX_Color::Type::kGray:
       array = pRuntime->NewArray();
       pRuntime->PutArrayElement(array, 0, pRuntime->NewString("G"));
       pRuntime->PutArrayElement(array, 1, pRuntime->NewNumber(color.fColor1));
       break;
-    case CFX_Color::kRGB:
+    case CFX_Color::Type::kRGB:
       array = pRuntime->NewArray();
       pRuntime->PutArrayElement(array, 0, pRuntime->NewString("RGB"));
       pRuntime->PutArrayElement(array, 1, pRuntime->NewNumber(color.fColor1));
       pRuntime->PutArrayElement(array, 2, pRuntime->NewNumber(color.fColor2));
       pRuntime->PutArrayElement(array, 3, pRuntime->NewNumber(color.fColor3));
       break;
-    case CFX_Color::kCMYK:
+    case CFX_Color::Type::kCMYK:
       array = pRuntime->NewArray();
       pRuntime->PutArrayElement(array, 0, pRuntime->NewString("CMYK"));
       pRuntime->PutArrayElement(array, 1, pRuntime->NewNumber(color.fColor1));
@@ -85,14 +86,14 @@
 // static
 CFX_Color CJS_Color::ConvertArrayToPWLColor(CJS_Runtime* pRuntime,
                                             v8::Local<v8::Array> array) {
-  int nArrayLen = pRuntime->GetArrayLength(array);
-  if (nArrayLen < 1)
+  size_t nArrayLen = pRuntime->GetArrayLength(array);
+  if (nArrayLen == 0)
     return CFX_Color();
 
   WideString sSpace =
       pRuntime->ToWideString(pRuntime->GetArrayElement(array, 0));
   if (sSpace.EqualsASCII("T"))
-    return CFX_Color(CFX_Color::kTransparent);
+    return CFX_Color(CFX_Color::Type::kTransparent);
 
   float d1 = 0;
   if (nArrayLen > 1) {
@@ -100,7 +101,7 @@
         pRuntime->ToDouble(pRuntime->GetArrayElement(array, 1)));
   }
   if (sSpace.EqualsASCII("G"))
-    return CFX_Color(CFX_Color::kGray, d1);
+    return CFX_Color(CFX_Color::Type::kGray, d1);
 
   float d2 = 0;
   float d3 = 0;
@@ -113,7 +114,7 @@
         pRuntime->ToDouble(pRuntime->GetArrayElement(array, 3)));
   }
   if (sSpace.EqualsASCII("RGB"))
-    return CFX_Color(CFX_Color::kRGB, d1, d2, d3);
+    return CFX_Color(CFX_Color::Type::kRGB, d1, d2, d3);
 
   float d4 = 0;
   if (nArrayLen > 4) {
@@ -121,25 +122,25 @@
         pRuntime->ToDouble(pRuntime->GetArrayElement(array, 4)));
   }
   if (sSpace.EqualsASCII("CMYK"))
-    return CFX_Color(CFX_Color::kCMYK, d1, d2, d3, d4);
+    return CFX_Color(CFX_Color::Type::kCMYK, d1, d2, d3, d4);
 
   return CFX_Color();
 }
 
 CJS_Color::CJS_Color(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
     : CJS_Object(pObject, pRuntime),
-      m_crTransparent(CFX_Color::kTransparent),
-      m_crBlack(CFX_Color::kGray, 0),
-      m_crWhite(CFX_Color::kGray, 1),
-      m_crRed(CFX_Color::kRGB, 1, 0, 0),
-      m_crGreen(CFX_Color::kRGB, 0, 1, 0),
-      m_crBlue(CFX_Color::kRGB, 0, 0, 1),
-      m_crCyan(CFX_Color::kCMYK, 1, 0, 0, 0),
-      m_crMagenta(CFX_Color::kCMYK, 0, 1, 0, 0),
-      m_crYellow(CFX_Color::kCMYK, 0, 0, 1, 0),
-      m_crDKGray(CFX_Color::kGray, 0.25),
-      m_crGray(CFX_Color::kGray, 0.5),
-      m_crLTGray(CFX_Color::kGray, 0.75) {}
+      m_crTransparent(CFX_Color::Type::kTransparent),
+      m_crBlack(CFX_Color::Type::kGray, 0),
+      m_crWhite(CFX_Color::Type::kGray, 1),
+      m_crRed(CFX_Color::Type::kRGB, 1, 0, 0),
+      m_crGreen(CFX_Color::Type::kRGB, 0, 1, 0),
+      m_crBlue(CFX_Color::Type::kRGB, 0, 0, 1),
+      m_crCyan(CFX_Color::Type::kCMYK, 1, 0, 0, 0),
+      m_crMagenta(CFX_Color::Type::kCMYK, 0, 1, 0, 0),
+      m_crYellow(CFX_Color::Type::kCMYK, 0, 0, 1, 0),
+      m_crDKGray(CFX_Color::Type::kGray, 0.25),
+      m_crGray(CFX_Color::Type::kGray, 0.5),
+      m_crLTGray(CFX_Color::Type::kGray, 0.75) {}
 
 CJS_Color::~CJS_Color() = default;
 
@@ -273,19 +274,19 @@
   if (params.size() < 2)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  if (params[0].IsEmpty() || !params[0]->IsArray())
+  if (!fxv8::IsArray(params[0]))
     return CJS_Result::Failure(JSMessage::kTypeError);
 
   WideString sDestSpace = pRuntime->ToWideString(params[1]);
-  int nColorType = CFX_Color::kTransparent;
+  CFX_Color::Type nColorType = CFX_Color::Type::kTransparent;
   if (sDestSpace.EqualsASCII("T"))
-    nColorType = CFX_Color::kTransparent;
+    nColorType = CFX_Color::Type::kTransparent;
   else if (sDestSpace.EqualsASCII("G"))
-    nColorType = CFX_Color::kGray;
+    nColorType = CFX_Color::Type::kGray;
   else if (sDestSpace.EqualsASCII("RGB"))
-    nColorType = CFX_Color::kRGB;
+    nColorType = CFX_Color::Type::kRGB;
   else if (sDestSpace.EqualsASCII("CMYK"))
-    nColorType = CFX_Color::kCMYK;
+    nColorType = CFX_Color::Type::kCMYK;
 
   CFX_Color color =
       ConvertArrayToPWLColor(pRuntime, pRuntime->ToArray(params[0]));
@@ -302,10 +303,8 @@
   if (params.size() < 2)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  if (params[0].IsEmpty() || !params[0]->IsArray() || params[1].IsEmpty() ||
-      !params[1]->IsArray()) {
+  if (!fxv8::IsArray(params[0]) || !fxv8::IsArray(params[1]))
     return CJS_Result::Failure(JSMessage::kTypeError);
-  }
 
   CFX_Color color1 =
       ConvertArrayToPWLColor(pRuntime, pRuntime->ToArray(params[0]));
@@ -313,7 +312,7 @@
       ConvertArrayToPWLColor(pRuntime, pRuntime->ToArray(params[1]));
 
   // Relies on higher values having more components.
-  int32_t best = std::max(color1.nColorType, color2.nColorType);
+  CFX_Color::Type best = std::max(color1.nColorType, color2.nColorType);
   return CJS_Result::Success(pRuntime->NewBoolean(
       color1.ConvertColorType(best) == color2.ConvertColorType(best)));
 }
diff --git a/fxjs/cjs_color.h b/fxjs/cjs_color.h
index 03aa735..b768709 100644
--- a/fxjs/cjs_color.h
+++ b/fxjs/cjs_color.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,12 +9,13 @@
 
 #include <vector>
 
+#include "core/fxge/cfx_color.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/js_define.h"
 
 class CJS_Color final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
   static v8::Local<v8::Array> ConvertPWLColorToArray(CJS_Runtime* pRuntime,
                                                      const CFX_Color& color);
@@ -41,7 +42,7 @@
   JS_STATIC_METHOD(equal, CJS_Color)
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSPropertySpec PropertySpecs[];
   static const JSMethodSpec MethodSpecs[];
diff --git a/fxjs/cjs_console.cpp b/fxjs/cjs_console.cpp
index c696bbd..91c2ca0 100644
--- a/fxjs/cjs_console.cpp
+++ b/fxjs/cjs_console.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/js_define.h"
 
@@ -18,11 +17,11 @@
                                                  {"println", println_static},
                                                  {"show", show_static}};
 
-int CJS_Console::ObjDefnID = -1;
+uint32_t CJS_Console::ObjDefnID = 0;
 const char CJS_Console::kName[] = "console";
 
 // static
-int CJS_Console::GetObjDefnID() {
+uint32_t CJS_Console::GetObjDefnID() {
   return ObjDefnID;
 }
 
diff --git a/fxjs/cjs_console.h b/fxjs/cjs_console.h
index 2bc118a..5608d55 100644
--- a/fxjs/cjs_console.h
+++ b/fxjs/cjs_console.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJS_Console final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
   CJS_Console(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
@@ -26,7 +26,7 @@
   JS_STATIC_METHOD(show, CJS_Console)
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSMethodSpec MethodSpecs[];
 
diff --git a/fxjs/cjs_delaydata.cpp b/fxjs/cjs_delaydata.cpp
index d7e1f78..12b34fa 100644
--- a/fxjs/cjs_delaydata.cpp
+++ b/fxjs/cjs_delaydata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,4 +9,4 @@
 CJS_DelayData::CJS_DelayData(FIELD_PROP prop, int idx, const WideString& name)
     : eProp(prop), nControlIndex(idx), sFieldName(name) {}
 
-CJS_DelayData::~CJS_DelayData() {}
+CJS_DelayData::~CJS_DelayData() = default;
diff --git a/fxjs/cjs_delaydata.h b/fxjs/cjs_delaydata.h
index f4c7820..861dbab 100644
--- a/fxjs/cjs_delaydata.h
+++ b/fxjs/cjs_delaydata.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -20,9 +20,9 @@
 
   FIELD_PROP eProp;
   int nControlIndex;
+  int32_t num = 0;
+  bool b = false;
   WideString sFieldName;
-  int32_t num;
-  bool b;
   ByteString bytestring;
   WideString widestring;
   CFX_FloatRect rect;
diff --git a/fxjs/cjs_display.cpp b/fxjs/cjs_display.cpp
index 71d6c02..0e0ee26 100644
--- a/fxjs/cjs_display.cpp
+++ b/fxjs/cjs_display.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,7 @@
     {"noPrint", JSConstSpec::Number, 2, 0},
     {"noView", JSConstSpec::Number, 3, 0}};
 
-int CJS_Display::ObjDefnID = -1;
+uint32_t CJS_Display::ObjDefnID = 0;
 
 // static
 void CJS_Display::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_display.h b/fxjs/cjs_display.h
index 59b6e9c..7de5368 100644
--- a/fxjs/cjs_display.h
+++ b/fxjs/cjs_display.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_Display() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/cjs_document.cpp b/fxjs/cjs_document.cpp
index 9294f17..5a842ce 100644
--- a/fxjs/cjs_document.cpp
+++ b/fxjs/cjs_document.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,18 +6,22 @@
 
 #include "fxjs/cjs_document.h"
 
+#include <stdint.h>
+
 #include <utility>
 
+#include "constants/access_permissions.h"
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/page/cpdf_textobject.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "core/fpdfdoc/cpdf_nametree.h"
 #include "fpdfsdk/cpdfsdk_annotiteration.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fxjs/cjs_annot.h"
@@ -27,6 +31,8 @@
 #include "fxjs/cjs_field.h"
 #include "fxjs/cjs_icon.h"
 #include "fxjs/js_resources.h"
+#include "third_party/base/check.h"
+#include "v8/include/v8-container.h"
 
 const JSPropertySpec CJS_Document::PropertySpecs[] = {
     {"ADBE", get_ADBE_static, set_ADBE_static},
@@ -108,11 +114,11 @@
     {"submitForm", submitForm_static},
     {"syncAnnotScan", syncAnnotScan_static}};
 
-int CJS_Document::ObjDefnID = -1;
+uint32_t CJS_Document::ObjDefnID = 0;
 const char CJS_Document::kName[] = "Document";
 
 // static
-int CJS_Document::GetObjDefnID() {
+uint32_t CJS_Document::GetObjDefnID() {
   return ObjDefnID;
 }
 
@@ -255,8 +261,8 @@
   if (pFieldObj.IsEmpty())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  auto* pJSField =
-      static_cast<CJS_Field*>(CFXJS_Engine::GetObjectPrivate(pFieldObj));
+  auto* pJSField = static_cast<CJS_Field*>(
+      CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pFieldObj));
   if (!pJSField)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -340,8 +346,8 @@
     cMsg = pRuntime->ToWideString(newParams[5]);
 
   pRuntime->BeginBlock();
-  m_pFormFillEnv->JS_docmailForm(nullptr, 0, bUI, cTo, cSubject, cCc, cBcc,
-                                 cMsg);
+  m_pFormFillEnv->JS_docmailForm(pdfium::span<const uint8_t>(), bUI, cTo,
+                                 cSubject, cCc, cBcc, cMsg);
   pRuntime->EndBlock();
   return CJS_Result::Success();
 }
@@ -354,7 +360,9 @@
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
-  if (!m_pFormFillEnv->GetPermissions(FPDFPERM_EXTRACT_ACCESS))
+
+  using pdfium::access_permissions::kExtractForAccessibility;
+  if (!m_pFormFillEnv->HasPermissions(kExtractForAccessibility))
     return CJS_Result::Failure(JSMessage::kPermissionError);
 
   CPDFSDK_InteractiveForm* pInteractiveForm = GetSDKInteractiveForm();
@@ -389,10 +397,9 @@
   if (IsExpandedParamKnown(newParams[5]))
     cMsg = pRuntime->ToWideString(newParams[5]);
 
-  std::vector<char> mutable_buf(sTextBuf.begin(), sTextBuf.end());
   pRuntime->BeginBlock();
-  m_pFormFillEnv->JS_docmailForm(mutable_buf.data(), mutable_buf.size(), bUI,
-                                 cTo, cSubject, cCc, cBcc, cMsg);
+  m_pFormFillEnv->JS_docmailForm(sTextBuf.raw_span(), bUI, cTo, cSubject, cCc,
+                                 cBcc, cMsg);
   pRuntime->EndBlock();
   return CJS_Result::Success();
 }
@@ -439,8 +446,7 @@
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CJS_EventRecorder* pHandler =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pHandler = pRuntime->GetCurrentEventContext();
   if (!pHandler->IsUserGesture())
     return CJS_Result::Failure(JSMessage::kUserGestureRequiredError);
 
@@ -460,30 +466,30 @@
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  if (!(m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY) ||
-        m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM))) {
+  if (!m_pFormFillEnv->HasPermissions(
+          pdfium::access_permissions::kModifyContent |
+          pdfium::access_permissions::kModifyAnnotation)) {
     return CJS_Result::Failure(JSMessage::kPermissionError);
   }
 
   WideString sFieldName = pRuntime->ToWideString(params[0]);
   CPDFSDK_InteractiveForm* pInteractiveForm = GetSDKInteractiveForm();
-  std::vector<ObservedPtr<CPDFSDK_Annot>> widgets;
+  std::vector<ObservedPtr<CPDFSDK_Widget>> widgets;
   pInteractiveForm->GetWidgets(sFieldName, &widgets);
   if (widgets.empty())
     return CJS_Result::Success();
 
-  for (const auto& pAnnot : widgets) {
-    CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot.Get());
+  for (const auto& pWidget : widgets) {
     if (!pWidget)
       continue;
 
     IPDF_Page* pPage = pWidget->GetPage();
-    ASSERT(pPage);
+    DCHECK(pPage);
 
     // If there is currently no pageview associated with the page being used
     // do not create one. We may be in the process of tearing down the document
     // and creating a new pageview at this point will cause bad things.
-    CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(pPage, false);
+    CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(pPage);
     if (!pPageView)
       continue;
 
@@ -506,15 +512,17 @@
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
-  if (!(m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY) ||
-        m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM) ||
-        m_pFormFillEnv->GetPermissions(FPDFPERM_FILL_FORM))) {
+
+  if (!m_pFormFillEnv->HasPermissions(
+          pdfium::access_permissions::kModifyContent |
+          pdfium::access_permissions::kModifyAnnotation |
+          pdfium::access_permissions::kFillForm)) {
     return CJS_Result::Failure(JSMessage::kPermissionError);
   }
 
   CPDF_InteractiveForm* pPDFForm = GetCoreInteractiveForm();
   if (params.empty()) {
-    pPDFForm->ResetForm(NotificationOption::kNotify);
+    pPDFForm->ResetForm();
     m_pFormFillEnv->SetChangeMark();
     return CJS_Result::Success();
   }
@@ -531,12 +539,13 @@
   for (size_t i = 0; i < pRuntime->GetArrayLength(array); ++i) {
     WideString swVal =
         pRuntime->ToWideString(pRuntime->GetArrayElement(array, i));
-    for (int j = 0, jsz = pPDFForm->CountFields(swVal); j < jsz; ++j)
+    const size_t jsz = pPDFForm->CountFields(swVal);
+    for (size_t j = 0; j < jsz; ++j)
       aFields.push_back(pPDFForm->GetField(j, swVal));
   }
 
   if (!aFields.empty()) {
-    pPDFForm->ResetForm(aFields, true, NotificationOption::kNotify);
+    pPDFForm->ResetForm(aFields, true);
     m_pFormFillEnv->SetChangeMark();
   }
 
@@ -565,8 +574,7 @@
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CJS_EventRecorder* pHandler =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pHandler = pRuntime->GetCurrentEventContext();
   if (!pHandler->IsUserGesture())
     return CJS_Result::Failure(JSMessage::kUserGestureRequiredError);
 
@@ -597,7 +605,7 @@
   if (pRuntime->GetArrayLength(aFields) == 0 && bEmpty) {
     if (pPDFForm->CheckRequiredFields(nullptr, true)) {
       pRuntime->BeginBlock();
-      GetSDKInteractiveForm()->SubmitForm(strURL, false);
+      GetSDKInteractiveForm()->SubmitForm(strURL);
       pRuntime->EndBlock();
     }
     return CJS_Result::Success();
@@ -607,7 +615,8 @@
   for (size_t i = 0; i < pRuntime->GetArrayLength(aFields); ++i) {
     WideString sName =
         pRuntime->ToWideString(pRuntime->GetArrayElement(aFields, i));
-    for (int j = 0, jsz = pPDFForm->CountFields(sName); j < jsz; ++j) {
+    const size_t jsz = pPDFForm->CountFields(sName);
+    for (size_t j = 0; j < jsz; ++j) {
       CPDF_FormField* pField = pPDFForm->GetField(j, sName);
       if (!bEmpty && pField->GetValue().IsEmpty())
         continue;
@@ -643,14 +652,16 @@
 
 CJS_Result CJS_Document::set_author(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  return setPropertyInternal(pRuntime, vp, "Author");
+  // Read-only.
+  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Document::get_info(CJS_Runtime* pRuntime) {
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  const auto* pDictionary = m_pFormFillEnv->GetPDFDocument()->GetInfo();
+  RetainPtr<const CPDF_Dictionary> pDictionary =
+      m_pFormFillEnv->GetPDFDocument()->GetInfo();
   if (!pDictionary)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -685,11 +696,10 @@
                               pRuntime->NewString(cwTrapped.AsStringView()));
 
   // PutObjectProperty() calls below may re-enter JS and change info dict.
-  auto pCopy = pDictionary->Clone();
-  CPDF_DictionaryLocker locker(ToDictionary(pCopy.Get()));
+  CPDF_DictionaryLocker locker(ToDictionary(pDictionary->Clone()));
   for (const auto& it : locker) {
     const ByteString& bsKey = it.first;
-    CPDF_Object* pValueObj = it.second.Get();
+    const RetainPtr<CPDF_Object>& pValueObj = it.second;
     if (pValueObj->IsString() || pValueObj->IsName()) {
       pRuntime->PutObjectProperty(
           pObj, bsKey.AsStringView(),
@@ -716,38 +726,23 @@
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_Dictionary* pDictionary = m_pFormFillEnv->GetPDFDocument()->GetInfo();
+  RetainPtr<CPDF_Dictionary> pDictionary =
+      m_pFormFillEnv->GetPDFDocument()->GetInfo();
   if (!pDictionary)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
+
   return CJS_Result::Success(pRuntime->NewString(
       pDictionary->GetUnicodeTextFor(propName).AsStringView()));
 }
 
-CJS_Result CJS_Document::setPropertyInternal(CJS_Runtime* pRuntime,
-                                             v8::Local<v8::Value> vp,
-                                             const ByteString& propName) {
-  if (!m_pFormFillEnv)
-    return CJS_Result::Failure(JSMessage::kBadObjectError);
-
-  CPDF_Dictionary* pDictionary = m_pFormFillEnv->GetPDFDocument()->GetInfo();
-  if (!pDictionary)
-    return CJS_Result::Failure(JSMessage::kBadObjectError);
-
-  if (!m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY))
-    return CJS_Result::Failure(JSMessage::kPermissionError);
-
-  pDictionary->SetNewFor<CPDF_String>(propName, pRuntime->ToWideString(vp));
-  m_pFormFillEnv->SetChangeMark();
-  return CJS_Result::Success();
-}
-
 CJS_Result CJS_Document::get_creation_date(CJS_Runtime* pRuntime) {
   return getPropertyInternal(pRuntime, "CreationDate");
 }
 
 CJS_Result CJS_Document::set_creation_date(CJS_Runtime* pRuntime,
                                            v8::Local<v8::Value> vp) {
-  return setPropertyInternal(pRuntime, vp, "CreationDate");
+  // Read-only.
+  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Document::get_creator(CJS_Runtime* pRuntime) {
@@ -756,7 +751,8 @@
 
 CJS_Result CJS_Document::set_creator(CJS_Runtime* pRuntime,
                                      v8::Local<v8::Value> vp) {
-  return setPropertyInternal(pRuntime, vp, "Creator");
+  // Read-only.
+  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Document::get_delay(CJS_Runtime* pRuntime) {
@@ -769,7 +765,9 @@
                                    v8::Local<v8::Value> vp) {
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
-  if (!m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY))
+
+  using pdfium::access_permissions::kModifyContent;
+  if (!m_pFormFillEnv->HasPermissions(kModifyContent))
     return CJS_Result::Failure(JSMessage::kPermissionError);
 
   m_bDelay = pRuntime->ToBoolean(vp);
@@ -792,7 +790,8 @@
 
 CJS_Result CJS_Document::set_keywords(CJS_Runtime* pRuntime,
                                       v8::Local<v8::Value> vp) {
-  return setPropertyInternal(pRuntime, vp, "Keywords");
+  // Read-only.
+  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Document::get_mod_date(CJS_Runtime* pRuntime) {
@@ -801,7 +800,8 @@
 
 CJS_Result CJS_Document::set_mod_date(CJS_Runtime* pRuntime,
                                       v8::Local<v8::Value> vp) {
-  return setPropertyInternal(pRuntime, vp, "ModDate");
+  // Read-only.
+  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Document::get_producer(CJS_Runtime* pRuntime) {
@@ -810,7 +810,8 @@
 
 CJS_Result CJS_Document::set_producer(CJS_Runtime* pRuntime,
                                       v8::Local<v8::Value> vp) {
-  return setPropertyInternal(pRuntime, vp, "Producer");
+  // Read-only.
+  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Document::get_subject(CJS_Runtime* pRuntime) {
@@ -819,7 +820,8 @@
 
 CJS_Result CJS_Document::set_subject(CJS_Runtime* pRuntime,
                                      v8::Local<v8::Value> vp) {
-  return setPropertyInternal(pRuntime, vp, "Subject");
+  // Read-only.
+  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Document::get_title(CJS_Runtime* pRuntime) {
@@ -830,9 +832,8 @@
 
 CJS_Result CJS_Document::set_title(CJS_Runtime* pRuntime,
                                    v8::Local<v8::Value> vp) {
-  if (!m_pFormFillEnv)
-    return CJS_Result::Failure(JSMessage::kBadObjectError);
-  return setPropertyInternal(pRuntime, vp, "Title");
+  // Read-only.
+  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Document::get_num_pages(CJS_Runtime* pRuntime) {
@@ -935,9 +936,10 @@
     if (wsFilePath[i - 1] == L'\\' || wsFilePath[i - 1] == L'/')
       break;
   }
-  if (i > 0 && i < wsFilePath.GetLength())
-    return CJS_Result::Success(pRuntime->NewString(wsFilePath.c_str() + i));
-
+  if (i > 0 && i < wsFilePath.GetLength()) {
+    return CJS_Result::Success(
+        pRuntime->NewString(wsFilePath.AsStringView().Substr(i)));
+  }
   return CJS_Result::Success(pRuntime->NewString(""));
 }
 
@@ -1005,13 +1007,13 @@
 
   int nPageNo = pRuntime->ToInt32(params[0]);
   WideString swAnnotName = pRuntime->ToWideString(params[1]);
-  CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(nPageNo);
+  CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageViewAtIndex(nPageNo);
   if (!pPageView)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDFSDK_AnnotIteration annotIteration(pPageView, false);
+  CPDFSDK_AnnotIteration annot_iteration(pPageView);
   CPDFSDK_BAAnnot* pSDKBAAnnot = nullptr;
-  for (const auto& pSDKAnnotCur : annotIteration) {
+  for (const auto& pSDKAnnotCur : annot_iteration) {
     auto* pBAAnnot = pSDKAnnotCur->AsBAAnnot();
     if (pBAAnnot && pBAAnnot->GetAnnotName() == swAnnotName) {
       pSDKBAAnnot = pBAAnnot;
@@ -1026,8 +1028,8 @@
   if (pObj.IsEmpty())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  auto* pJS_Annot =
-      static_cast<CJS_Annot*>(CFXJS_Engine::GetObjectPrivate(pObj));
+  auto* pJS_Annot = static_cast<CJS_Annot*>(
+      CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj));
   if (!pJS_Annot)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -1047,12 +1049,12 @@
   int nPageNo = m_pFormFillEnv->GetPageCount();
   v8::Local<v8::Array> annots = pRuntime->NewArray();
   for (int i = 0; i < nPageNo; ++i) {
-    CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(i);
+    CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageViewAtIndex(i);
     if (!pPageView)
       return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-    CPDFSDK_AnnotIteration annotIteration(pPageView, false);
-    for (const auto& pSDKAnnotCur : annotIteration) {
+    CPDFSDK_AnnotIteration annot_iteration(pPageView);
+    for (const auto& pSDKAnnotCur : annot_iteration) {
       if (!pSDKAnnotCur)
         return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -1061,8 +1063,8 @@
       if (pObj.IsEmpty())
         return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-      auto* pJS_Annot =
-          static_cast<CJS_Annot*>(CFXJS_Engine::GetObjectPrivate(pObj));
+      auto* pJS_Annot = static_cast<CJS_Annot*>(
+          CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj));
       pJS_Annot->SetSDKAnnot(pSDKAnnotCur->AsBAAnnot());
       pRuntime->PutArrayElement(
           annots, i,
@@ -1107,7 +1109,7 @@
     return CJS_Result::Failure(JSMessage::kTypeError);
 
   v8::Local<v8::Object> pObj = pRuntime->ToObject(params[1]);
-  if (!JSGetObject<CJS_Icon>(pObj))
+  if (!JSGetObject<CJS_Icon>(pRuntime->GetIsolate(), pObj))
     return CJS_Result::Failure(JSMessage::kTypeError);
 
   WideString swIconName = pRuntime->ToWideString(params[0]);
@@ -1129,8 +1131,8 @@
     if (pObj.IsEmpty())
       return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-    auto* pJS_Icon =
-        static_cast<CJS_Icon*>(CFXJS_Engine::GetObjectPrivate(pObj));
+    auto* pJS_Icon = static_cast<CJS_Icon*>(
+        CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj));
     pJS_Icon->SetIconName(name);
     pRuntime->PutArrayElement(Icons, i++,
                               pJS_Icon
@@ -1161,7 +1163,8 @@
   if (pObj.IsEmpty())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  auto* pJSIcon = static_cast<CJS_Icon*>(CFXJS_Engine::GetObjectPrivate(pObj));
+  auto* pJSIcon = static_cast<CJS_Icon*>(
+      CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj));
   if (!pJSIcon)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -1198,9 +1201,10 @@
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  if (!(m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY) ||
-        m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM) ||
-        m_pFormFillEnv->GetPermissions(FPDFPERM_FILL_FORM))) {
+  if (!m_pFormFillEnv->HasPermissions(
+          pdfium::access_permissions::kModifyContent |
+          pdfium::access_permissions::kModifyAnnotation |
+          pdfium::access_permissions::kFillForm)) {
     return CJS_Result::Failure(JSMessage::kPermissionError);
   }
 
@@ -1222,7 +1226,9 @@
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
-  if (!m_pFormFillEnv->GetPermissions(FPDFPERM_EXTRACT_ACCESS))
+
+  using pdfium::access_permissions::kExtractForAccessibility;
+  if (!m_pFormFillEnv->HasPermissions(kExtractForAccessibility))
     return CJS_Result::Failure(JSMessage::kPermissionError);
 
   // TODO(tsepez): check maximum allowable params.
@@ -1235,12 +1241,13 @@
   if (nPageNo < 0 || nPageNo >= pDocument->GetPageCount())
     return CJS_Result::Failure(JSMessage::kValueError);
 
-  CPDF_Dictionary* pPageDict = pDocument->GetPageDictionary(nPageNo);
+  RetainPtr<CPDF_Dictionary> pPageDict =
+      pDocument->GetMutablePageDictionary(nPageNo);
   if (!pPageDict)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  auto page = pdfium::MakeRetain<CPDF_Page>(pDocument, pPageDict);
-  page->SetRenderCache(pdfium::MakeUnique<CPDF_PageRenderCache>(page.Get()));
+  auto page = pdfium::MakeRetain<CPDF_Page>(pDocument, std::move(pPageDict));
+  page->AddPageImageCache();
   page->ParseContent();
 
   int nWords = 0;
@@ -1267,8 +1274,11 @@
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
-  if (!m_pFormFillEnv->GetPermissions(FPDFPERM_EXTRACT_ACCESS))
+
+  using pdfium::access_permissions::kExtractForAccessibility;
+  if (!m_pFormFillEnv->HasPermissions(kExtractForAccessibility))
     return CJS_Result::Failure(JSMessage::kBadObjectError);
+
   return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
@@ -1277,7 +1287,9 @@
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
-  if (!m_pFormFillEnv->GetPermissions(FPDFPERM_EXTRACT_ACCESS))
+
+  using pdfium::access_permissions::kExtractForAccessibility;
+  if (!m_pFormFillEnv->HasPermissions(kExtractForAccessibility))
     return CJS_Result::Failure(JSMessage::kPermissionError);
 
   int nPageNo = params.size() > 0 ? pRuntime->ToInt32(params[0]) : 0;
@@ -1285,12 +1297,13 @@
   if (nPageNo < 0 || nPageNo >= pDocument->GetPageCount())
     return CJS_Result::Failure(JSMessage::kValueError);
 
-  CPDF_Dictionary* pPageDict = pDocument->GetPageDictionary(nPageNo);
+  RetainPtr<CPDF_Dictionary> pPageDict =
+      pDocument->GetMutablePageDictionary(nPageNo);
   if (!pPageDict)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  auto page = pdfium::MakeRetain<CPDF_Page>(pDocument, pPageDict);
-  page->SetRenderCache(pdfium::MakeUnique<CPDF_PageRenderCache>(page.Get()));
+  auto page = pdfium::MakeRetain<CPDF_Page>(pDocument, std::move(pPageDict));
+  page->AddPageImageCache();
   page->ParseContent();
 
   int nWords = 0;
@@ -1370,23 +1383,16 @@
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   CPDF_Document* pDocument = m_pFormFillEnv->GetPDFDocument();
-  CPDF_NameTree nameTree(pDocument, "Dests");
-  CPDF_Array* destArray =
-      nameTree.LookupNamedDest(pDocument, pRuntime->ToWideString(params[0]));
-  if (!destArray)
+  RetainPtr<const CPDF_Array> dest_array = CPDF_NameTree::LookupNamedDest(
+      pDocument, pRuntime->ToByteString(params[0]));
+  if (!dest_array)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_Dest dest(destArray);
-  const CPDF_Array* arrayObject = dest.GetArray();
-  std::vector<float> scrollPositionArray;
-  if (arrayObject) {
-    for (size_t i = 2; i < arrayObject->size(); i++)
-      scrollPositionArray.push_back(arrayObject->GetNumberAt(i));
-  }
+  CPDF_Dest dest(std::move(dest_array));
+  std::vector<float> positions = dest.GetScrollPositionArray();
   pRuntime->BeginBlock();
   m_pFormFillEnv->DoGoToAction(dest.GetDestPageIndex(pDocument),
-                               dest.GetZoomMode(), scrollPositionArray.data(),
-                               scrollPositionArray.size());
+                               dest.GetZoomMode(), positions);
   pRuntime->EndBlock();
   return CJS_Result::Success();
 }
diff --git a/fxjs/cjs_document.h b/fxjs/cjs_document.h
index 49448fc..d410a5e 100644
--- a/fxjs/cjs_document.h
+++ b/fxjs/cjs_document.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,12 +17,11 @@
 
 class CPDFSDK_InteractiveForm;
 class CPDF_InteractiveForm;
-class CPDF_TextObject;
 struct CJS_DelayData;
 
 class CJS_Document final : public CJS_Object, public Observable {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
   CJS_Document(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
@@ -112,7 +111,7 @@
   JS_STATIC_METHOD(syncAnnotScan, CJS_Document)
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSPropertySpec PropertySpecs[];
   static const JSMethodSpec MethodSpecs[];
@@ -303,9 +302,6 @@
 
   CJS_Result getPropertyInternal(CJS_Runtime* pRuntime,
                                  const ByteString& propName);
-  CJS_Result setPropertyInternal(CJS_Runtime* pRuntime,
-                                 v8::Local<v8::Value> vp,
-                                 const ByteString& propName);
 
   CPDF_InteractiveForm* GetCoreInteractiveForm();
   CPDFSDK_InteractiveForm* GetSDKInteractiveForm();
diff --git a/fxjs/cjs_event.cpp b/fxjs/cjs_event.cpp
index a16ee6c..6b89038 100644
--- a/fxjs/cjs_event.cpp
+++ b/fxjs/cjs_event.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,6 @@
 #include "fxjs/cjs_event.h"
 
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_field.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/js_define.h"
@@ -34,11 +33,11 @@
     {"value", get_value_static, set_value_static},
     {"willCommit", get_will_commit_static, set_will_commit_static}};
 
-int CJS_Event::ObjDefnID = -1;
+uint32_t CJS_Event::ObjDefnID = 0;
 const char CJS_Event::kName[] = "event";
 
 // static
-int CJS_Event::GetObjDefnID() {
+uint32_t CJS_Event::GetObjDefnID() {
   return ObjDefnID;
 }
 
@@ -55,27 +54,22 @@
 CJS_Event::~CJS_Event() = default;
 
 CJS_Result CJS_Event::get_change(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(
       pRuntime->NewString(pEvent->Change().AsStringView()));
 }
 
 CJS_Result CJS_Event::set_change(CJS_Runtime* pRuntime,
                                  v8::Local<v8::Value> vp) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
-
   if (vp->IsString()) {
-    WideString& wChange = pEvent->Change();
-    wChange = pRuntime->ToWideString(vp);
+    CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
+    pEvent->Change() = pRuntime->ToWideString(vp);
   }
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Event::get_change_ex(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(
       pRuntime->NewString(pEvent->ChangeEx().AsStringView()));
 }
@@ -86,8 +80,7 @@
 }
 
 CJS_Result CJS_Event::get_commit_key(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(pRuntime->NewNumber(pEvent->CommitKey()));
 }
 
@@ -97,8 +90,7 @@
 }
 
 CJS_Result CJS_Event::get_field_full(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (pEvent->Name() != "Keystroke")
     return CJS_Result::Failure(L"unrecognized event");
 
@@ -111,8 +103,7 @@
 }
 
 CJS_Result CJS_Event::get_key_down(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(pRuntime->NewBoolean(pEvent->KeyDown()));
 }
 
@@ -122,8 +113,7 @@
 }
 
 CJS_Result CJS_Event::get_modifier(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(pRuntime->NewBoolean(pEvent->Modifier()));
 }
 
@@ -133,8 +123,7 @@
 }
 
 CJS_Result CJS_Event::get_name(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(pRuntime->NewString(pEvent->Name()));
 }
 
@@ -143,14 +132,12 @@
 }
 
 CJS_Result CJS_Event::get_rc(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(pRuntime->NewBoolean(pEvent->Rc()));
 }
 
 CJS_Result CJS_Event::set_rc(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   pEvent->Rc() = pRuntime->ToBoolean(vp);
   return CJS_Result::Success();
 }
@@ -183,8 +170,7 @@
 }
 
 CJS_Result CJS_Event::get_sel_end(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (pEvent->Name() != "Keystroke")
     return CJS_Result::Success();
 
@@ -193,8 +179,7 @@
 
 CJS_Result CJS_Event::set_sel_end(CJS_Runtime* pRuntime,
                                   v8::Local<v8::Value> vp) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (pEvent->Name() == "Keystroke")
     pEvent->SetSelEnd(pRuntime->ToInt32(vp));
 
@@ -202,8 +187,7 @@
 }
 
 CJS_Result CJS_Event::get_sel_start(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (pEvent->Name() != "Keystroke")
     return CJS_Result::Success();
 
@@ -212,8 +196,7 @@
 
 CJS_Result CJS_Event::set_sel_start(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (pEvent->Name() == "Keystroke")
     pEvent->SetSelStart(pRuntime->ToInt32(vp));
 
@@ -221,8 +204,7 @@
 }
 
 CJS_Result CJS_Event::get_shift(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(pRuntime->NewBoolean(pEvent->Shift()));
 }
 
@@ -256,8 +238,7 @@
 }
 
 CJS_Result CJS_Event::get_target_name(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(
       pRuntime->NewString(pEvent->TargetName().AsStringView()));
 }
@@ -268,8 +249,7 @@
 }
 
 CJS_Result CJS_Event::get_type(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(pRuntime->NewString(pEvent->Type()));
 }
 
@@ -278,8 +258,7 @@
 }
 
 CJS_Result CJS_Event::get_value(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (pEvent->Type() != "Field")
     return CJS_Result::Failure(L"Bad event type.");
 
@@ -292,8 +271,7 @@
 
 CJS_Result CJS_Event::set_value(CJS_Runtime* pRuntime,
                                 v8::Local<v8::Value> vp) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (pEvent->Type() != "Field")
     return CJS_Result::Failure(L"Bad event type.");
 
@@ -311,8 +289,7 @@
 }
 
 CJS_Result CJS_Event::get_will_commit(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
 
   return CJS_Result::Success(pRuntime->NewBoolean(pEvent->WillCommit()));
 }
diff --git a/fxjs/cjs_event.h b/fxjs/cjs_event.h
index b2fa4dc..fc9bec2 100644
--- a/fxjs/cjs_event.h
+++ b/fxjs/cjs_event.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,7 @@
 
 class CJS_Event final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
   CJS_Event(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
@@ -40,7 +40,7 @@
   JS_STATIC_PROP(willCommit, will_commit, CJS_Event)
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSPropertySpec PropertySpecs[];
 
diff --git a/fxjs/cjs_event_context.cpp b/fxjs/cjs_event_context.cpp
index 27e8120..65eee9d 100644
--- a/fxjs/cjs_event_context.cpp
+++ b/fxjs/cjs_event_context.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,27 +6,23 @@
 
 #include "fxjs/cjs_event_context.h"
 
+#include "core/fpdfdoc/cpdf_formfield.h"
 #include "core/fxcrt/autorestorer.h"
-#include "fxjs/cjs_eventrecorder.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fxjs/cjs_field.h"
 #include "fxjs/cjs_runtime.h"
 #include "fxjs/js_define.h"
 #include "fxjs/js_resources.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-isolate.h"
 
 CJS_EventContext::CJS_EventContext(CJS_Runtime* pRuntime)
-    : m_pRuntime(pRuntime),
-      m_pEventRecorder(pdfium::MakeUnique<CJS_EventRecorder>()) {
-  ASSERT(pRuntime);
-}
+    : m_pRuntime(pRuntime), m_pFormFillEnv(pRuntime->GetFormFillEnv()) {}
 
 CJS_EventContext::~CJS_EventContext() = default;
 
-CPDFSDK_FormFillEnvironment* CJS_EventContext::GetFormFillEnv() {
-  return m_pRuntime->GetFormFillEnv();
-}
-
-Optional<IJS_Runtime::JS_Error> CJS_EventContext::RunScript(
+absl::optional<IJS_Runtime::JS_Error> CJS_EventContext::RunScript(
     const WideString& script) {
   v8::Isolate::Scope isolate_scope(m_pRuntime->GetIsolate());
   v8::HandleScope handle_scope(m_pRuntime->GetIsolate());
@@ -41,20 +37,19 @@
   AutoRestorer<bool> restorer(&m_bBusy);
   m_bBusy = true;
 
-  ASSERT(m_pEventRecorder->IsValid());
-  CJS_Runtime::FieldEvent event(m_pEventRecorder->TargetName(),
-                                m_pEventRecorder->EventType());
+  DCHECK(IsValid());
+  CJS_Runtime::FieldEvent event(TargetName(), EventKind());
   if (!m_pRuntime->AddEventToSet(event)) {
     return IJS_Runtime::JS_Error(
         1, 1, JSGetStringFromID(JSMessage::kDuplicateEventError));
   }
 
-  Optional<IJS_Runtime::JS_Error> err;
+  absl::optional<IJS_Runtime::JS_Error> err;
   if (script.GetLength() > 0)
     err = m_pRuntime->ExecuteScript(script);
 
   m_pRuntime->RemoveEventFromSet(event);
-  m_pEventRecorder->Destroy();
+  Destroy();
   return err;
 }
 
@@ -69,17 +64,14 @@
   if (pFieldObj.IsEmpty())
     return nullptr;
 
-  auto* pFormFillEnv = m_pEventRecorder->GetFormFillEnvironment();
-  if (!pFormFillEnv)
-    pFormFillEnv = GetFormFillEnv();
-
-  auto* pJSDocument =
-      static_cast<CJS_Document*>(CFXJS_Engine::GetObjectPrivate(pDocObj));
+  auto* pFormFillEnv = GetFormFillEnv();
+  auto* pJSDocument = static_cast<CJS_Document*>(
+      CFXJS_Engine::GetObjectPrivate(m_pRuntime->GetIsolate(), pDocObj));
   pJSDocument->SetFormFillEnv(pFormFillEnv);
 
-  auto* pJSField =
-      static_cast<CJS_Field*>(CFXJS_Engine::GetObjectPrivate(pFieldObj));
-  pJSField->AttachField(pJSDocument, m_pEventRecorder->SourceName());
+  auto* pJSField = static_cast<CJS_Field*>(
+      CFXJS_Engine::GetObjectPrivate(m_pRuntime->GetIsolate(), pFieldObj));
+  pJSField->AttachField(pJSDocument, SourceName());
   return pJSField;
 }
 
@@ -94,137 +86,149 @@
   if (pFieldObj.IsEmpty())
     return nullptr;
 
-  auto* pFormFillEnv = m_pEventRecorder->GetFormFillEnvironment();
-  if (!pFormFillEnv)
-    pFormFillEnv = GetFormFillEnv();
-
-  auto* pJSDocument =
-      static_cast<CJS_Document*>(CFXJS_Engine::GetObjectPrivate(pDocObj));
+  auto* pFormFillEnv = GetFormFillEnv();
+  auto* pJSDocument = static_cast<CJS_Document*>(
+      CFXJS_Engine::GetObjectPrivate(m_pRuntime->GetIsolate(), pDocObj));
   pJSDocument->SetFormFillEnv(pFormFillEnv);
 
-  auto* pJSField =
-      static_cast<CJS_Field*>(CFXJS_Engine::GetObjectPrivate(pFieldObj));
-  pJSField->AttachField(pJSDocument, m_pEventRecorder->TargetName());
+  auto* pJSField = static_cast<CJS_Field*>(
+      CFXJS_Engine::GetObjectPrivate(m_pRuntime->GetIsolate(), pFieldObj));
+  pJSField->AttachField(pJSDocument, TargetName());
   return pJSField;
 }
 
-void CJS_EventContext::OnApp_Init() {
-  m_pEventRecorder->OnApp_Init();
+void CJS_EventContext::OnDoc_Open(const WideString& strTargetName) {
+  Initialize(Kind::kDocOpen);
+  m_strTargetName = strTargetName;
 }
 
-void CJS_EventContext::OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                  const WideString& strTargetName) {
-  m_pEventRecorder->OnDoc_Open(pFormFillEnv, strTargetName);
+void CJS_EventContext::OnDoc_WillPrint() {
+  Initialize(Kind::kDocWillPrint);
 }
 
-void CJS_EventContext::OnDoc_WillPrint(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnDoc_WillPrint(pFormFillEnv);
+void CJS_EventContext::OnDoc_DidPrint() {
+  Initialize(Kind::kDocDidPrint);
 }
 
-void CJS_EventContext::OnDoc_DidPrint(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnDoc_DidPrint(pFormFillEnv);
+void CJS_EventContext::OnDoc_WillSave() {
+  Initialize(Kind::kDocWillSave);
 }
 
-void CJS_EventContext::OnDoc_WillSave(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnDoc_WillSave(pFormFillEnv);
+void CJS_EventContext::OnDoc_DidSave() {
+  Initialize(Kind::kDocDidSave);
 }
 
-void CJS_EventContext::OnDoc_DidSave(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnDoc_DidSave(pFormFillEnv);
+void CJS_EventContext::OnDoc_WillClose() {
+  Initialize(Kind::kDocWillClose);
 }
 
-void CJS_EventContext::OnDoc_WillClose(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnDoc_WillClose(pFormFillEnv);
+void CJS_EventContext::OnPage_Open() {
+  Initialize(Kind::kPageOpen);
 }
 
-void CJS_EventContext::OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnPage_Open(pFormFillEnv);
+void CJS_EventContext::OnPage_Close() {
+  Initialize(Kind::kPageClose);
 }
 
-void CJS_EventContext::OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnPage_Close(pFormFillEnv);
+void CJS_EventContext::OnPage_InView() {
+  Initialize(Kind::kPageInView);
 }
 
-void CJS_EventContext::OnPage_InView(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnPage_InView(pFormFillEnv);
-}
-
-void CJS_EventContext::OnPage_OutView(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnPage_OutView(pFormFillEnv);
-}
-
-void CJS_EventContext::OnField_MouseDown(bool bModifier,
-                                         bool bShift,
-                                         CPDF_FormField* pTarget) {
-  m_pEventRecorder->OnField_MouseDown(bModifier, bShift, pTarget);
+void CJS_EventContext::OnPage_OutView() {
+  Initialize(Kind::kPageOutView);
 }
 
 void CJS_EventContext::OnField_MouseEnter(bool bModifier,
                                           bool bShift,
                                           CPDF_FormField* pTarget) {
-  m_pEventRecorder->OnField_MouseEnter(bModifier, bShift, pTarget);
+  Initialize(Kind::kFieldMouseEnter);
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
 }
 
 void CJS_EventContext::OnField_MouseExit(bool bModifier,
                                          bool bShift,
                                          CPDF_FormField* pTarget) {
-  m_pEventRecorder->OnField_MouseExit(bModifier, bShift, pTarget);
+  Initialize(Kind::kFieldMouseExit);
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
+}
+
+void CJS_EventContext::OnField_MouseDown(bool bModifier,
+                                         bool bShift,
+                                         CPDF_FormField* pTarget) {
+  Initialize(Kind::kFieldMouseDown);
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
 }
 
 void CJS_EventContext::OnField_MouseUp(bool bModifier,
                                        bool bShift,
                                        CPDF_FormField* pTarget) {
-  m_pEventRecorder->OnField_MouseUp(bModifier, bShift, pTarget);
+  Initialize(Kind::kFieldMouseUp);
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
 }
 
 void CJS_EventContext::OnField_Focus(bool bModifier,
                                      bool bShift,
                                      CPDF_FormField* pTarget,
-                                     WideString* Value) {
-  m_pEventRecorder->OnField_Focus(bModifier, bShift, pTarget, Value);
+                                     WideString* pValue) {
+  DCHECK(pValue);
+  Initialize(Kind::kFieldFocus);
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
 }
 
 void CJS_EventContext::OnField_Blur(bool bModifier,
                                     bool bShift,
                                     CPDF_FormField* pTarget,
-                                    WideString* Value) {
-  m_pEventRecorder->OnField_Blur(bModifier, bShift, pTarget, Value);
-}
-
-void CJS_EventContext::OnField_Calculate(CPDF_FormField* pSource,
-                                         CPDF_FormField* pTarget,
-                                         WideString* pValue,
-                                         bool* pRc) {
-  m_pEventRecorder->OnField_Calculate(pSource, pTarget, pValue, pRc);
-}
-
-void CJS_EventContext::OnField_Format(CPDF_FormField* pTarget,
-                                      WideString* Value) {
-  m_pEventRecorder->OnField_Format(pTarget, Value);
+                                    WideString* pValue) {
+  DCHECK(pValue);
+  Initialize(Kind::kFieldBlur);
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
 }
 
 void CJS_EventContext::OnField_Keystroke(WideString* strChange,
                                          const WideString& strChangeEx,
-                                         bool bKeyDown,
+                                         bool KeyDown,
                                          bool bModifier,
-                                         int* nSelEnd,
-                                         int* nSelStart,
+                                         int* pSelEnd,
+                                         int* pSelStart,
                                          bool bShift,
                                          CPDF_FormField* pTarget,
-                                         WideString* Value,
+                                         WideString* pValue,
                                          bool bWillCommit,
                                          bool bFieldFull,
-                                         bool* bRc) {
-  m_pEventRecorder->OnField_Keystroke(
-      strChange, strChangeEx, bKeyDown, bModifier, nSelEnd, nSelStart, bShift,
-      pTarget, Value, bWillCommit, bFieldFull, bRc);
+                                         bool* pbRc) {
+  DCHECK(pValue);
+  DCHECK(pbRc);
+  DCHECK(pSelStart);
+  DCHECK(pSelEnd);
+
+  Initialize(Kind::kFieldKeystroke);
+  m_nCommitKey = 0;
+  m_pWideStrChange = strChange;
+  m_WideStrChangeEx = strChangeEx;
+  m_bKeyDown = KeyDown;
+  m_bModifier = bModifier;
+  m_pISelEnd = pSelEnd;
+  m_pISelStart = pSelStart;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
+  m_bWillCommit = bWillCommit;
+  m_pbRc = pbRc;
+  m_bFieldFull = bFieldFull;
 }
 
 void CJS_EventContext::OnField_Validate(WideString* strChange,
@@ -233,94 +237,190 @@
                                         bool bModifier,
                                         bool bShift,
                                         CPDF_FormField* pTarget,
-                                        WideString* Value,
-                                        bool* bRc) {
-  m_pEventRecorder->OnField_Validate(strChange, strChangeEx, bKeyDown,
-                                     bModifier, bShift, pTarget, Value, bRc);
+                                        WideString* pValue,
+                                        bool* pbRc) {
+  DCHECK(pValue);
+  DCHECK(pbRc);
+  Initialize(Kind::kFieldValidate);
+  m_pWideStrChange = strChange;
+  m_WideStrChangeEx = strChangeEx;
+  m_bKeyDown = bKeyDown;
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
+  m_pbRc = pbRc;
 }
 
-void CJS_EventContext::OnScreen_Focus(bool bModifier,
-                                      bool bShift,
-                                      CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_Focus(bModifier, bShift, pScreen);
+void CJS_EventContext::OnField_Calculate(CPDF_FormField* pSource,
+                                         CPDF_FormField* pTarget,
+                                         WideString* pValue,
+                                         bool* pRc) {
+  DCHECK(pValue);
+  DCHECK(pRc);
+  Initialize(Kind::kFieldCalculate);
+  if (pSource)
+    m_strSourceName = pSource->GetFullName();
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
+  m_pbRc = pRc;
 }
 
-void CJS_EventContext::OnScreen_Blur(bool bModifier,
-                                     bool bShift,
-                                     CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_Blur(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnScreen_Open(bool bModifier,
-                                     bool bShift,
-                                     CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_Open(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnScreen_Close(bool bModifier,
-                                      bool bShift,
-                                      CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_Close(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnScreen_MouseDown(bool bModifier,
-                                          bool bShift,
-                                          CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_MouseDown(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnScreen_MouseUp(bool bModifier,
-                                        bool bShift,
-                                        CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_MouseUp(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnScreen_MouseEnter(bool bModifier,
-                                           bool bShift,
-                                           CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_MouseEnter(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnScreen_MouseExit(bool bModifier,
-                                          bool bShift,
-                                          CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_MouseExit(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnScreen_InView(bool bModifier,
-                                       bool bShift,
-                                       CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_InView(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnScreen_OutView(bool bModifier,
-                                        bool bShift,
-                                        CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_OutView(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) {
-  m_pEventRecorder->OnBookmark_MouseUp(pBookMark);
-}
-
-void CJS_EventContext::OnLink_MouseUp(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnLink_MouseUp(pFormFillEnv);
-}
-
-void CJS_EventContext::OnConsole_Exec() {
-  m_pEventRecorder->OnConsole_Exec();
+void CJS_EventContext::OnField_Format(CPDF_FormField* pTarget,
+                                      WideString* pValue) {
+  DCHECK(pValue);
+  Initialize(Kind::kFieldFormat);
+  m_nCommitKey = 0;
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
+  m_bWillCommit = true;
 }
 
 void CJS_EventContext::OnExternal_Exec() {
-  m_pEventRecorder->OnExternal_Exec();
+  Initialize(Kind::kExternalExec);
 }
 
-void CJS_EventContext::OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnBatchExec(pFormFillEnv);
+void CJS_EventContext::Initialize(Kind kind) {
+  m_eKind = kind;
+  m_strTargetName.clear();
+  m_strSourceName.clear();
+  m_pWideStrChange = nullptr;
+  m_WideStrChangeDu.clear();
+  m_WideStrChangeEx.clear();
+  m_nCommitKey = -1;
+  m_bKeyDown = false;
+  m_bModifier = false;
+  m_bShift = false;
+  m_pISelEnd = nullptr;
+  m_nSelEndDu = 0;
+  m_pISelStart = nullptr;
+  m_nSelStartDu = 0;
+  m_bWillCommit = false;
+  m_pValue = nullptr;
+  m_bFieldFull = false;
+  m_pbRc = nullptr;
+  m_bRcDu = false;
+  m_bValid = true;
 }
 
-void CJS_EventContext::OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                   const WideString& strTargetName) {
-  m_pEventRecorder->OnMenu_Exec(pFormFillEnv, strTargetName);
+void CJS_EventContext::Destroy() {
+  m_bValid = false;
+}
+
+bool CJS_EventContext::IsUserGesture() const {
+  switch (m_eKind) {
+    case Kind::kFieldMouseDown:
+    case Kind::kFieldMouseUp:
+    case Kind::kFieldKeystroke:
+      return true;
+    default:
+      return false;
+  }
+}
+
+WideString& CJS_EventContext::Change() {
+  return m_pWideStrChange ? *m_pWideStrChange : m_WideStrChangeDu;
+}
+
+ByteStringView CJS_EventContext::Name() const {
+  switch (m_eKind) {
+    case Kind::kDocDidPrint:
+      return "DidPrint";
+    case Kind::kDocDidSave:
+      return "DidSave";
+    case Kind::kDocOpen:
+      return "Open";
+    case Kind::kDocWillClose:
+      return "WillClose";
+    case Kind::kDocWillPrint:
+      return "WillPrint";
+    case Kind::kDocWillSave:
+      return "WillSave";
+    case Kind::kExternalExec:
+      return "Exec";
+    case Kind::kFieldFocus:
+      return "Focus";
+    case Kind::kFieldBlur:
+      return "Blur";
+    case Kind::kFieldMouseDown:
+      return "Mouse Down";
+    case Kind::kFieldMouseUp:
+      return "Mouse Up";
+    case Kind::kFieldMouseEnter:
+      return "Mouse Enter";
+    case Kind::kFieldMouseExit:
+      return "Mouse Exit";
+    case Kind::kFieldCalculate:
+      return "Calculate";
+    case Kind::kFieldFormat:
+      return "Format";
+    case Kind::kFieldKeystroke:
+      return "Keystroke";
+    case Kind::kFieldValidate:
+      return "Validate";
+    case Kind::kPageOpen:
+      return "Open";
+    case Kind::kPageClose:
+      return "Close";
+    case Kind::kPageInView:
+      return "InView";
+    case Kind::kPageOutView:
+      return "OutView";
+    default:
+      return "";
+  }
+}
+
+ByteStringView CJS_EventContext::Type() const {
+  switch (m_eKind) {
+    case Kind::kDocDidPrint:
+    case Kind::kDocDidSave:
+    case Kind::kDocOpen:
+    case Kind::kDocWillClose:
+    case Kind::kDocWillPrint:
+    case Kind::kDocWillSave:
+      return "Doc";
+    case Kind::kExternalExec:
+      return "External";
+    case Kind::kFieldBlur:
+    case Kind::kFieldFocus:
+    case Kind::kFieldMouseDown:
+    case Kind::kFieldMouseUp:
+    case Kind::kFieldMouseEnter:
+    case Kind::kFieldMouseExit:
+    case Kind::kFieldCalculate:
+    case Kind::kFieldFormat:
+    case Kind::kFieldKeystroke:
+    case Kind::kFieldValidate:
+      return "Field";
+    case Kind::kPageOpen:
+    case Kind::kPageClose:
+    case Kind::kPageInView:
+    case Kind::kPageOutView:
+      return "Page";
+    default:
+      return "";
+  }
+}
+
+bool& CJS_EventContext::Rc() {
+  return m_pbRc ? *m_pbRc : m_bRcDu;
+}
+
+int CJS_EventContext::SelEnd() const {
+  return m_pISelEnd ? *m_pISelEnd : m_nSelEndDu;
+}
+
+int CJS_EventContext::SelStart() const {
+  return m_pISelStart ? *m_pISelStart : m_nSelStartDu;
+}
+
+void CJS_EventContext::SetSelEnd(int value) {
+  int& target = m_pISelEnd ? *m_pISelEnd : m_nSelEndDu;
+  target = value;
+}
+
+void CJS_EventContext::SetSelStart(int value) {
+  int& target = m_pISelStart ? *m_pISelStart : m_nSelStartDu;
+  target = value;
 }
diff --git a/fxjs/cjs_event_context.h b/fxjs/cjs_event_context.h
index 6a953e6..a0b266b 100644
--- a/fxjs/cjs_event_context.h
+++ b/fxjs/cjs_event_context.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,37 +7,58 @@
 #ifndef FXJS_CJS_EVENT_CONTEXT_H_
 #define FXJS_CJS_EVENT_CONTEXT_H_
 
-#include <memory>
-
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fxjs/ijs_event_context.h"
 
-class CJS_EventRecorder;
 class CJS_Field;
 class CJS_Runtime;
-class CPDFSDK_FormFillEnvironment;
 
 class CJS_EventContext final : public IJS_EventContext {
  public:
+  enum class Kind : uint8_t {
+    kUnknown,
+    kDocOpen,
+    kDocWillPrint,
+    kDocDidPrint,
+    kDocWillSave,
+    kDocDidSave,
+    kDocWillClose,
+    kPageOpen,
+    kPageClose,
+    kPageInView,
+    kPageOutView,
+    kFieldMouseDown,
+    kFieldMouseUp,
+    kFieldMouseEnter,
+    kFieldMouseExit,
+    kFieldFocus,
+    kFieldBlur,
+    kFieldKeystroke,
+    kFieldValidate,
+    kFieldCalculate,
+    kFieldFormat,
+    kExternalExec,
+  };
+
   explicit CJS_EventContext(CJS_Runtime* pRuntime);
   ~CJS_EventContext() override;
 
   // IJS_EventContext
-  Optional<IJS_Runtime::JS_Error> RunScript(const WideString& script) override;
-  void OnApp_Init() override;
-  void OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                  const WideString& strTargetName) override;
-  void OnDoc_WillPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnDoc_DidPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnDoc_WillSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnDoc_DidSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnDoc_WillClose(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnPage_InView(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnPage_OutView(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
+  absl::optional<IJS_Runtime::JS_Error> RunScript(
+      const WideString& script) override;
+  void OnDoc_Open(const WideString& strTargetName) override;
+  void OnDoc_WillPrint() override;
+  void OnDoc_DidPrint() override;
+  void OnDoc_WillSave() override;
+  void OnDoc_DidSave() override;
+  void OnDoc_WillClose() override;
+  void OnPage_Open() override;
+  void OnPage_Close() override;
+  void OnPage_InView() override;
+  void OnPage_OutView() override;
   void OnField_MouseDown(bool bModifier,
                          bool bShift,
                          CPDF_FormField* pTarget) override;
@@ -83,54 +104,72 @@
                         CPDF_FormField* pTarget,
                         WideString* Value,
                         bool* bRc) override;
-  void OnScreen_Focus(bool bModifier,
-                      bool bShift,
-                      CPDFSDK_Annot* pScreen) override;
-  void OnScreen_Blur(bool bModifier,
-                     bool bShift,
-                     CPDFSDK_Annot* pScreen) override;
-  void OnScreen_Open(bool bModifier,
-                     bool bShift,
-                     CPDFSDK_Annot* pScreen) override;
-  void OnScreen_Close(bool bModifier,
-                      bool bShift,
-                      CPDFSDK_Annot* pScreen) override;
-  void OnScreen_MouseDown(bool bModifier,
-                          bool bShift,
-                          CPDFSDK_Annot* pScreen) override;
-  void OnScreen_MouseUp(bool bModifier,
-                        bool bShift,
-                        CPDFSDK_Annot* pScreen) override;
-  void OnScreen_MouseEnter(bool bModifier,
-                           bool bShift,
-                           CPDFSDK_Annot* pScreen) override;
-  void OnScreen_MouseExit(bool bModifier,
-                          bool bShift,
-                          CPDFSDK_Annot* pScreen) override;
-  void OnScreen_InView(bool bModifier,
-                       bool bShift,
-                       CPDFSDK_Annot* pScreen) override;
-  void OnScreen_OutView(bool bModifier,
-                        bool bShift,
-                        CPDFSDK_Annot* pScreen) override;
-  void OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) override;
-  void OnLink_MouseUp(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                   const WideString& strTargetName) override;
-  void OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnConsole_Exec() override;
   void OnExternal_Exec() override;
 
-  CJS_Runtime* GetJSRuntime() const { return m_pRuntime.Get(); }
-  CJS_EventRecorder* GetEventRecorder() const { return m_pEventRecorder.get(); }
-  CPDFSDK_FormFillEnvironment* GetFormFillEnv();
+  CJS_Runtime* GetJSRuntime() const { return m_pRuntime; }
+  CPDFSDK_FormFillEnvironment* GetFormFillEnv() const {
+    return m_pFormFillEnv.Get();
+  }
   CJS_Field* SourceField();
   CJS_Field* TargetField();
 
+  Kind EventKind() const { return m_eKind; }
+  bool IsValid() const { return m_bValid; }
+  bool IsUserGesture() const;
+  WideString& Change();
+  WideString ChangeEx() const { return m_WideStrChangeEx; }
+  WideString SourceName() const { return m_strSourceName; }
+  WideString TargetName() const { return m_strTargetName; }
+  int CommitKey() const { return m_nCommitKey; }
+  bool FieldFull() const { return m_bFieldFull; }
+  bool KeyDown() const { return m_bKeyDown; }
+  bool Modifier() const { return m_bModifier; }
+  ByteStringView Name() const;
+  ByteStringView Type() const;
+  bool& Rc();
+  int SelEnd() const;
+  int SelStart() const;
+  void SetSelEnd(int value);
+  void SetSelStart(int value);
+  bool Shift() const { return m_bShift; }
+  bool HasValue() const { return !!m_pValue; }
+  WideString& Value() { return *m_pValue; }
+  bool WillCommit() const { return m_bWillCommit; }
+
+  void SetValueForTest(WideString* pStr) { m_pValue = pStr; }
+  void SetRCForTest(bool* pRC) { m_pbRc = pRC; }
+  void SetStrChangeForTest(WideString* pStrChange) {
+    m_pWideStrChange = pStrChange;
+  }
+  void ResetWillCommitForTest() { m_bWillCommit = false; }
+
  private:
+  void Initialize(Kind kind);
+  void Destroy();
+
   UnownedPtr<CJS_Runtime> const m_pRuntime;
-  std::unique_ptr<CJS_EventRecorder> m_pEventRecorder;
+  ObservedPtr<CPDFSDK_FormFillEnvironment> m_pFormFillEnv;
+  Kind m_eKind = Kind::kUnknown;
   bool m_bBusy = false;
+  bool m_bValid = false;
+  UnownedPtr<WideString> m_pValue;
+  WideString m_strSourceName;
+  WideString m_strTargetName;
+  WideString m_WideStrChangeDu;
+  WideString m_WideStrChangeEx;
+  UnownedPtr<WideString> m_pWideStrChange;
+  int m_nCommitKey = -1;
+  bool m_bKeyDown = false;
+  bool m_bModifier = false;
+  bool m_bShift = false;
+  int m_nSelEndDu = 0;
+  int m_nSelStartDu = 0;
+  UnownedPtr<int> m_pISelEnd;
+  UnownedPtr<int> m_pISelStart;
+  bool m_bWillCommit = false;
+  bool m_bFieldFull = false;
+  bool m_bRcDu = false;
+  UnownedPtr<bool> m_pbRc;
 };
 
 #endif  // FXJS_CJS_EVENT_CONTEXT_H_
diff --git a/fxjs/cjs_event_context_stub.cpp b/fxjs/cjs_event_context_stub.cpp
index 82530e4..b8d0518 100644
--- a/fxjs/cjs_event_context_stub.cpp
+++ b/fxjs/cjs_event_context_stub.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,11 @@
 
 #include "fxjs/cjs_event_context_stub.h"
 
-Optional<IJS_Runtime::JS_Error> CJS_EventContextStub::RunScript(
+CJS_EventContextStub::CJS_EventContextStub() = default;
+
+CJS_EventContextStub::~CJS_EventContextStub() = default;
+
+absl::optional<IJS_Runtime::JS_Error> CJS_EventContextStub::RunScript(
     const WideString& script) {
   return IJS_Runtime::JS_Error(1, 1, L"JavaScript support not present");
 }
diff --git a/fxjs/cjs_event_context_stub.h b/fxjs/cjs_event_context_stub.h
index 06d0184..5c9b50a 100644
--- a/fxjs/cjs_event_context_stub.h
+++ b/fxjs/cjs_event_context_stub.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,24 +11,23 @@
 
 class CJS_EventContextStub final : public IJS_EventContext {
  public:
-  CJS_EventContextStub() {}
-  ~CJS_EventContextStub() override {}
+  CJS_EventContextStub();
+  ~CJS_EventContextStub() override;
 
   // IJS_EventContext:
-  Optional<IJS_Runtime::JS_Error> RunScript(const WideString& script) override;
+  absl::optional<IJS_Runtime::JS_Error> RunScript(
+      const WideString& script) override;
 
-  void OnApp_Init() override {}
-  void OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                  const WideString& strTargetName) override {}
-  void OnDoc_WillPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnDoc_DidPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnDoc_WillSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnDoc_DidSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnDoc_WillClose(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnPage_InView(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnPage_OutView(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
+  void OnDoc_Open(const WideString& strTargetName) override {}
+  void OnDoc_WillPrint() override {}
+  void OnDoc_DidPrint() override {}
+  void OnDoc_WillSave() override {}
+  void OnDoc_DidSave() override {}
+  void OnDoc_WillClose() override {}
+  void OnPage_Open() override {}
+  void OnPage_Close() override {}
+  void OnPage_InView() override {}
+  void OnPage_OutView() override {}
   void OnField_MouseDown(bool bModifier,
                          bool bShift,
                          CPDF_FormField* pTarget) override {}
@@ -74,42 +73,6 @@
                         CPDF_FormField* pTarget,
                         WideString* Value,
                         bool* bRc) override {}
-  void OnScreen_Focus(bool bModifier,
-                      bool bShift,
-                      CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_Blur(bool bModifier,
-                     bool bShift,
-                     CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_Open(bool bModifier,
-                     bool bShift,
-                     CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_Close(bool bModifier,
-                      bool bShift,
-                      CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_MouseDown(bool bModifier,
-                          bool bShift,
-                          CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_MouseUp(bool bModifier,
-                        bool bShift,
-                        CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_MouseEnter(bool bModifier,
-                           bool bShift,
-                           CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_MouseExit(bool bModifier,
-                          bool bShift,
-                          CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_InView(bool bModifier,
-                       bool bShift,
-                       CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_OutView(bool bModifier,
-                        bool bShift,
-                        CPDFSDK_Annot* pScreen) override {}
-  void OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) override {}
-  void OnLink_MouseUp(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                   const WideString&) override {}
-  void OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnConsole_Exec() override {}
   void OnExternal_Exec() override {}
 };
 
diff --git a/fxjs/cjs_eventrecorder.cpp b/fxjs/cjs_eventrecorder.cpp
deleted file mode 100644
index 59febf0..0000000
--- a/fxjs/cjs_eventrecorder.cpp
+++ /dev/null
@@ -1,558 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/cjs_eventrecorder.h"
-
-#include "core/fpdfdoc/cpdf_bookmark.h"
-#include "core/fpdfdoc/cpdf_formfield.h"
-
-CJS_EventRecorder::CJS_EventRecorder() = default;
-
-CJS_EventRecorder::~CJS_EventRecorder() = default;
-
-void CJS_EventRecorder::OnApp_Init() {
-  Initialize(JET_APP_INIT);
-}
-
-void CJS_EventRecorder::OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                   const WideString& strTargetName) {
-  Initialize(JET_DOC_OPEN);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-  m_strTargetName = strTargetName;
-}
-
-void CJS_EventRecorder::OnDoc_WillPrint(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_DOC_WILLPRINT);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnDoc_DidPrint(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_DOC_DIDPRINT);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnDoc_WillSave(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_DOC_WILLSAVE);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnDoc_DidSave(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_DOC_DIDSAVE);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnDoc_WillClose(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_DOC_WILLCLOSE);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_PAGE_OPEN);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnPage_Close(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_PAGE_CLOSE);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnPage_InView(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_PAGE_INVIEW);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnPage_OutView(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_PAGE_OUTVIEW);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnField_MouseEnter(bool bModifier,
-                                           bool bShift,
-                                           CPDF_FormField* pTarget) {
-  Initialize(JET_FIELD_MOUSEENTER);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-
-  m_strTargetName = pTarget->GetFullName();
-}
-
-void CJS_EventRecorder::OnField_MouseExit(bool bModifier,
-                                          bool bShift,
-                                          CPDF_FormField* pTarget) {
-  Initialize(JET_FIELD_MOUSEEXIT);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-}
-
-void CJS_EventRecorder::OnField_MouseDown(bool bModifier,
-                                          bool bShift,
-                                          CPDF_FormField* pTarget) {
-  Initialize(JET_FIELD_MOUSEDOWN);
-  m_eEventType = JET_FIELD_MOUSEDOWN;
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-}
-
-void CJS_EventRecorder::OnField_MouseUp(bool bModifier,
-                                        bool bShift,
-                                        CPDF_FormField* pTarget) {
-  Initialize(JET_FIELD_MOUSEUP);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-}
-
-void CJS_EventRecorder::OnField_Focus(bool bModifier,
-                                      bool bShift,
-                                      CPDF_FormField* pTarget,
-                                      WideString* pValue) {
-  ASSERT(pValue);
-  Initialize(JET_FIELD_FOCUS);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = pValue;
-}
-
-void CJS_EventRecorder::OnField_Blur(bool bModifier,
-                                     bool bShift,
-                                     CPDF_FormField* pTarget,
-                                     WideString* pValue) {
-  ASSERT(pValue);
-  Initialize(JET_FIELD_BLUR);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = pValue;
-}
-
-void CJS_EventRecorder::OnField_Keystroke(WideString* strChange,
-                                          const WideString& strChangeEx,
-                                          bool KeyDown,
-                                          bool bModifier,
-                                          int* pSelEnd,
-                                          int* pSelStart,
-                                          bool bShift,
-                                          CPDF_FormField* pTarget,
-                                          WideString* pValue,
-                                          bool bWillCommit,
-                                          bool bFieldFull,
-                                          bool* pbRc) {
-  ASSERT(pValue);
-  ASSERT(pbRc);
-  ASSERT(pSelStart);
-  ASSERT(pSelEnd);
-
-  Initialize(JET_FIELD_KEYSTROKE);
-
-  m_nCommitKey = 0;
-  m_pWideStrChange = strChange;
-  m_WideStrChangeEx = strChangeEx;
-  m_bKeyDown = KeyDown;
-  m_bModifier = bModifier;
-  m_pISelEnd = pSelEnd;
-  m_pISelStart = pSelStart;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = pValue;
-  m_bWillCommit = bWillCommit;
-  m_pbRc = pbRc;
-  m_bFieldFull = bFieldFull;
-}
-
-void CJS_EventRecorder::OnField_Validate(WideString* strChange,
-                                         const WideString& strChangeEx,
-                                         bool bKeyDown,
-                                         bool bModifier,
-                                         bool bShift,
-                                         CPDF_FormField* pTarget,
-                                         WideString* pValue,
-                                         bool* pbRc) {
-  ASSERT(pValue);
-  ASSERT(pbRc);
-
-  Initialize(JET_FIELD_VALIDATE);
-
-  m_pWideStrChange = strChange;
-  m_WideStrChangeEx = strChangeEx;
-  m_bKeyDown = bKeyDown;
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = pValue;
-  m_pbRc = pbRc;
-}
-
-void CJS_EventRecorder::OnField_Calculate(CPDF_FormField* pSource,
-                                          CPDF_FormField* pTarget,
-                                          WideString* pValue,
-                                          bool* pRc) {
-  ASSERT(pValue);
-  ASSERT(pRc);
-
-  Initialize(JET_FIELD_CALCULATE);
-
-  if (pSource)
-    m_strSourceName = pSource->GetFullName();
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = pValue;
-  m_pbRc = pRc;
-}
-
-void CJS_EventRecorder::OnField_Format(CPDF_FormField* pTarget,
-                                       WideString* pValue) {
-  ASSERT(pValue);
-  Initialize(JET_FIELD_FORMAT);
-
-  m_nCommitKey = 0;
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = pValue;
-  m_bWillCommit = true;
-}
-
-void CJS_EventRecorder::OnScreen_Focus(bool bModifier,
-                                       bool bShift,
-                                       CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_FOCUS);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_Blur(bool bModifier,
-                                      bool bShift,
-                                      CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_BLUR);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_Open(bool bModifier,
-                                      bool bShift,
-                                      CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_OPEN);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_Close(bool bModifier,
-                                       bool bShift,
-                                       CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_CLOSE);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_MouseDown(bool bModifier,
-                                           bool bShift,
-                                           CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_MOUSEDOWN);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_MouseUp(bool bModifier,
-                                         bool bShift,
-                                         CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_MOUSEUP);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_MouseEnter(bool bModifier,
-                                            bool bShift,
-                                            CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_MOUSEENTER);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_MouseExit(bool bModifier,
-                                           bool bShift,
-                                           CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_MOUSEEXIT);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_InView(bool bModifier,
-                                        bool bShift,
-                                        CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_INVIEW);
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_OutView(bool bModifier,
-                                         bool bShift,
-                                         CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_OUTVIEW);
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnLink_MouseUp(
-    CPDFSDK_FormFillEnvironment* pTargetFormFillEnv) {
-  Initialize(JET_LINK_MOUSEUP);
-  m_pTargetFormFillEnv.Reset(pTargetFormFillEnv);
-}
-
-void CJS_EventRecorder::OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) {
-  Initialize(JET_BOOKMARK_MOUSEUP);
-  m_pTargetBookMark = pBookMark;
-}
-
-void CJS_EventRecorder::OnMenu_Exec(
-    CPDFSDK_FormFillEnvironment* pTargetFormFillEnv,
-    const WideString& strTargetName) {
-  Initialize(JET_MENU_EXEC);
-  m_pTargetFormFillEnv.Reset(pTargetFormFillEnv);
-  m_strTargetName = strTargetName;
-}
-
-void CJS_EventRecorder::OnExternal_Exec() {
-  Initialize(JET_EXTERNAL_EXEC);
-}
-
-void CJS_EventRecorder::OnBatchExec(
-    CPDFSDK_FormFillEnvironment* pTargetFormFillEnv) {
-  Initialize(JET_BATCH_EXEC);
-  m_pTargetFormFillEnv.Reset(pTargetFormFillEnv);
-}
-
-void CJS_EventRecorder::OnConsole_Exec() {
-  Initialize(JET_CONSOLE_EXEC);
-}
-
-void CJS_EventRecorder::Initialize(JS_EVENT_T type) {
-  m_eEventType = type;
-  m_strTargetName.clear();
-  m_strSourceName.clear();
-  m_pWideStrChange = nullptr;
-  m_WideStrChangeDu.clear();
-  m_WideStrChangeEx.clear();
-  m_nCommitKey = -1;
-  m_bKeyDown = false;
-  m_bModifier = false;
-  m_bShift = false;
-  m_pISelEnd = nullptr;
-  m_nSelEndDu = 0;
-  m_pISelStart = nullptr;
-  m_nSelStartDu = 0;
-  m_bWillCommit = false;
-  m_pValue = nullptr;
-  m_bFieldFull = false;
-  m_pbRc = nullptr;
-  m_bRcDu = false;
-  m_pTargetBookMark = nullptr;
-  m_pTargetFormFillEnv.Reset();
-  m_pTargetAnnot.Reset();
-  m_bValid = true;
-}
-
-void CJS_EventRecorder::Destroy() {
-  m_bValid = false;
-}
-
-bool CJS_EventRecorder::IsUserGesture() const {
-  switch (m_eEventType) {
-    case JET_FIELD_MOUSEDOWN:
-    case JET_FIELD_MOUSEUP:
-    case JET_SCREEN_MOUSEDOWN:
-    case JET_SCREEN_MOUSEUP:
-    case JET_BOOKMARK_MOUSEUP:
-    case JET_LINK_MOUSEUP:
-    case JET_FIELD_KEYSTROKE:
-      return true;
-    default:
-      return false;
-  }
-}
-
-WideString& CJS_EventRecorder::Change() {
-  return m_pWideStrChange ? *m_pWideStrChange : m_WideStrChangeDu;
-}
-
-ByteStringView CJS_EventRecorder::Name() const {
-  switch (m_eEventType) {
-    case JET_APP_INIT:
-      return "Init";
-    case JET_BATCH_EXEC:
-      return "Exec";
-    case JET_BOOKMARK_MOUSEUP:
-      return "Mouse Up";
-    case JET_CONSOLE_EXEC:
-      return "Exec";
-    case JET_DOC_DIDPRINT:
-      return "DidPrint";
-    case JET_DOC_DIDSAVE:
-      return "DidSave";
-    case JET_DOC_OPEN:
-      return "Open";
-    case JET_DOC_WILLCLOSE:
-      return "WillClose";
-    case JET_DOC_WILLPRINT:
-      return "WillPrint";
-    case JET_DOC_WILLSAVE:
-      return "WillSave";
-    case JET_EXTERNAL_EXEC:
-      return "Exec";
-    case JET_FIELD_FOCUS:
-    case JET_SCREEN_FOCUS:
-      return "Focus";
-    case JET_FIELD_BLUR:
-    case JET_SCREEN_BLUR:
-      return "Blur";
-    case JET_FIELD_MOUSEDOWN:
-    case JET_SCREEN_MOUSEDOWN:
-      return "Mouse Down";
-    case JET_FIELD_MOUSEUP:
-    case JET_SCREEN_MOUSEUP:
-      return "Mouse Up";
-    case JET_FIELD_MOUSEENTER:
-    case JET_SCREEN_MOUSEENTER:
-      return "Mouse Enter";
-    case JET_FIELD_MOUSEEXIT:
-    case JET_SCREEN_MOUSEEXIT:
-      return "Mouse Exit";
-    case JET_FIELD_CALCULATE:
-      return "Calculate";
-    case JET_FIELD_FORMAT:
-      return "Format";
-    case JET_FIELD_KEYSTROKE:
-      return "Keystroke";
-    case JET_FIELD_VALIDATE:
-      return "Validate";
-    case JET_LINK_MOUSEUP:
-      return "Mouse Up";
-    case JET_MENU_EXEC:
-      return "Exec";
-    case JET_PAGE_OPEN:
-    case JET_SCREEN_OPEN:
-      return "Open";
-    case JET_PAGE_CLOSE:
-    case JET_SCREEN_CLOSE:
-      return "Close";
-    case JET_SCREEN_INVIEW:
-    case JET_PAGE_INVIEW:
-      return "InView";
-    case JET_PAGE_OUTVIEW:
-    case JET_SCREEN_OUTVIEW:
-      return "OutView";
-    default:
-      return "";
-  }
-}
-
-ByteStringView CJS_EventRecorder::Type() const {
-  switch (m_eEventType) {
-    case JET_APP_INIT:
-      return "App";
-    case JET_BATCH_EXEC:
-      return "Batch";
-    case JET_BOOKMARK_MOUSEUP:
-      return "BookMark";
-    case JET_CONSOLE_EXEC:
-      return "Console";
-    case JET_DOC_DIDPRINT:
-    case JET_DOC_DIDSAVE:
-    case JET_DOC_OPEN:
-    case JET_DOC_WILLCLOSE:
-    case JET_DOC_WILLPRINT:
-    case JET_DOC_WILLSAVE:
-      return "Doc";
-    case JET_EXTERNAL_EXEC:
-      return "External";
-    case JET_FIELD_BLUR:
-    case JET_FIELD_FOCUS:
-    case JET_FIELD_MOUSEDOWN:
-    case JET_FIELD_MOUSEENTER:
-    case JET_FIELD_MOUSEEXIT:
-    case JET_FIELD_MOUSEUP:
-    case JET_FIELD_CALCULATE:
-    case JET_FIELD_FORMAT:
-    case JET_FIELD_KEYSTROKE:
-    case JET_FIELD_VALIDATE:
-      return "Field";
-    case JET_SCREEN_FOCUS:
-    case JET_SCREEN_BLUR:
-    case JET_SCREEN_OPEN:
-    case JET_SCREEN_CLOSE:
-    case JET_SCREEN_MOUSEDOWN:
-    case JET_SCREEN_MOUSEUP:
-    case JET_SCREEN_MOUSEENTER:
-    case JET_SCREEN_MOUSEEXIT:
-    case JET_SCREEN_INVIEW:
-    case JET_SCREEN_OUTVIEW:
-      return "Screen";
-    case JET_LINK_MOUSEUP:
-      return "Link";
-    case JET_MENU_EXEC:
-      return "Menu";
-    case JET_PAGE_OPEN:
-    case JET_PAGE_CLOSE:
-    case JET_PAGE_INVIEW:
-    case JET_PAGE_OUTVIEW:
-      return "Page";
-    default:
-      return "";
-  }
-}
-
-bool& CJS_EventRecorder::Rc() {
-  return m_pbRc ? *m_pbRc : m_bRcDu;
-}
-
-int CJS_EventRecorder::SelEnd() const {
-  return m_pISelEnd ? *m_pISelEnd : m_nSelEndDu;
-}
-
-int CJS_EventRecorder::SelStart() const {
-  return m_pISelStart ? *m_pISelStart : m_nSelStartDu;
-}
-
-void CJS_EventRecorder::SetSelEnd(int value) {
-  int& target = m_pISelEnd ? *m_pISelEnd : m_nSelEndDu;
-  target = value;
-}
-
-void CJS_EventRecorder::SetSelStart(int value) {
-  int& target = m_pISelStart ? *m_pISelStart : m_nSelStartDu;
-  target = value;
-}
diff --git a/fxjs/cjs_eventrecorder.h b/fxjs/cjs_eventrecorder.h
deleted file mode 100644
index acf9856..0000000
--- a/fxjs/cjs_eventrecorder.h
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_CJS_EVENTRECORDER_H_
-#define FXJS_CJS_EVENTRECORDER_H_
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "fpdfsdk/cpdfsdk_annot.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-
-class CPDF_Bookmark;
-class CPDF_FormField;
-
-enum JS_EVENT_T {
-  JET_UNKNOWN,
-  JET_APP_INIT,
-  JET_DOC_OPEN,
-  JET_DOC_WILLPRINT,
-  JET_DOC_DIDPRINT,
-  JET_DOC_WILLSAVE,
-  JET_DOC_DIDSAVE,
-  JET_DOC_WILLCLOSE,
-  JET_PAGE_OPEN,
-  JET_PAGE_CLOSE,
-  JET_PAGE_INVIEW,
-  JET_PAGE_OUTVIEW,
-  JET_FIELD_MOUSEDOWN,
-  JET_FIELD_MOUSEUP,
-  JET_FIELD_MOUSEENTER,
-  JET_FIELD_MOUSEEXIT,
-  JET_FIELD_FOCUS,
-  JET_FIELD_BLUR,
-  JET_FIELD_KEYSTROKE,
-  JET_FIELD_VALIDATE,
-  JET_FIELD_CALCULATE,
-  JET_FIELD_FORMAT,
-  JET_SCREEN_FOCUS,
-  JET_SCREEN_BLUR,
-  JET_SCREEN_OPEN,
-  JET_SCREEN_CLOSE,
-  JET_SCREEN_MOUSEDOWN,
-  JET_SCREEN_MOUSEUP,
-  JET_SCREEN_MOUSEENTER,
-  JET_SCREEN_MOUSEEXIT,
-  JET_SCREEN_INVIEW,
-  JET_SCREEN_OUTVIEW,
-  JET_BATCH_EXEC,
-  JET_MENU_EXEC,
-  JET_CONSOLE_EXEC,
-  JET_EXTERNAL_EXEC,
-  JET_BOOKMARK_MOUSEUP,
-  JET_LINK_MOUSEUP
-};
-
-class CJS_EventRecorder {
- public:
-  CJS_EventRecorder();
-  ~CJS_EventRecorder();
-
-  void OnApp_Init();
-
-  void OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                  const WideString& strTargetName);
-  void OnDoc_WillPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnDoc_DidPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnDoc_WillSave(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnDoc_DidSave(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnDoc_WillClose(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-
-  void OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnPage_InView(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnPage_OutView(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-
-  void OnField_Calculate(CPDF_FormField* pSource,
-                         CPDF_FormField* pTarget,
-                         WideString* Value,
-                         bool* pbRc);
-  void OnField_Format(CPDF_FormField* pTarget, WideString* Value);
-  void OnField_Keystroke(WideString* strChange,
-                         const WideString& strChangeEx,
-                         bool KeyDown,
-                         bool bModifier,
-                         int* nSelEnd,
-                         int* nSelStart,
-                         bool bShift,
-                         CPDF_FormField* pTarget,
-                         WideString* Value,
-                         bool bWillCommit,
-                         bool bFieldFull,
-                         bool* bRc);
-  void OnField_Validate(WideString* strChange,
-                        const WideString& strChangeEx,
-                        bool bKeyDown,
-                        bool bModifier,
-                        bool bShift,
-                        CPDF_FormField* pTarget,
-                        WideString* Value,
-                        bool* bRc);
-  void OnField_MouseDown(bool bModifier, bool bShift, CPDF_FormField* pTarget);
-  void OnField_MouseEnter(bool bModifier, bool bShift, CPDF_FormField* pTarget);
-  void OnField_MouseExit(bool bModifier, bool bShift, CPDF_FormField* pTarget);
-  void OnField_MouseUp(bool bModifier, bool bShift, CPDF_FormField* pTarget);
-  void OnField_Blur(bool bModifier,
-                    bool bShift,
-                    CPDF_FormField* pTarget,
-                    WideString* Value);
-  void OnField_Focus(bool bModifier,
-                     bool bShift,
-                     CPDF_FormField* pTarget,
-                     WideString* Value);
-
-  void OnScreen_Focus(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_Blur(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_Open(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_Close(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_MouseDown(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_MouseUp(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_MouseEnter(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_MouseExit(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_InView(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_OutView(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-
-  void OnBookmark_MouseUp(CPDF_Bookmark* pBookMark);
-  void OnLink_MouseUp(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-
-  void OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                   const WideString& strTargetName);
-  void OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnConsole_Exec();
-  void OnExternal_Exec();
-
-  void Destroy();
-
-  JS_EVENT_T EventType() const { return m_eEventType; }
-  bool IsValid() const { return m_bValid; }
-  bool IsUserGesture() const;
-  WideString& Change();
-  WideString ChangeEx() const { return m_WideStrChangeEx; }
-  WideString SourceName() const { return m_strSourceName; }
-  WideString TargetName() const { return m_strTargetName; }
-  int CommitKey() const { return m_nCommitKey; }
-  bool FieldFull() const { return m_bFieldFull; }
-  bool KeyDown() const { return m_bKeyDown; }
-  bool Modifier() const { return m_bModifier; }
-  ByteStringView Name() const;
-  ByteStringView Type() const;
-  bool& Rc();
-  int SelEnd() const;
-  int SelStart() const;
-  void SetSelEnd(int value);
-  void SetSelStart(int value);
-  bool Shift() const { return m_bShift; }
-  bool HasValue() const { return !!m_pValue; }
-  WideString& Value() { return *m_pValue; }
-  bool WillCommit() const { return m_bWillCommit; }
-  CPDFSDK_FormFillEnvironment* GetFormFillEnvironment() const {
-    return m_pTargetFormFillEnv.Get();
-  }
-
-  void SetValueForTest(WideString* pStr) { m_pValue = pStr; }
-  void SetRCForTest(bool* pRC) { m_pbRc = pRC; }
-  void SetStrChangeForTest(WideString* pStrChange) {
-    m_pWideStrChange = pStrChange;
-  }
-  void ResetWillCommitForTest() { m_bWillCommit = false; }
-
- private:
-  void Initialize(JS_EVENT_T type);
-
-  JS_EVENT_T m_eEventType = JET_UNKNOWN;
-  bool m_bValid = false;
-  UnownedPtr<WideString> m_pValue;
-  WideString m_strSourceName;
-  WideString m_strTargetName;
-  WideString m_WideStrChangeDu;
-  WideString m_WideStrChangeEx;
-  UnownedPtr<WideString> m_pWideStrChange;
-  int m_nCommitKey = -1;
-  bool m_bKeyDown = false;
-  bool m_bModifier = false;
-  bool m_bShift = false;
-  int m_nSelEndDu = 0;
-  int m_nSelStartDu = 0;
-  UnownedPtr<int> m_pISelEnd;
-  UnownedPtr<int> m_pISelStart;
-  bool m_bWillCommit = false;
-  bool m_bFieldFull = false;
-  bool m_bRcDu = false;
-  UnownedPtr<bool> m_pbRc;
-  UnownedPtr<CPDF_Bookmark> m_pTargetBookMark;
-  ObservedPtr<CPDFSDK_FormFillEnvironment> m_pTargetFormFillEnv;
-  ObservedPtr<CPDFSDK_Annot> m_pTargetAnnot;
-};
-
-#endif  // FXJS_CJS_EVENTRECORDER_H_
diff --git a/fxjs/cjs_field.cpp b/fxjs/cjs_field.cpp
index b6aa9bb..c9127dd 100644
--- a/fxjs/cjs_field.cpp
+++ b/fxjs/cjs_field.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,12 +10,14 @@
 #include <memory>
 #include <utility>
 
+#include "constants/access_permissions.h"
 #include "constants/annotation_flags.h"
 #include "constants/form_flags.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfdoc/cpdf_formcontrol.h"
 #include "core/fpdfdoc/cpdf_formfield.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
-#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
@@ -23,11 +25,22 @@
 #include "fxjs/cjs_delaydata.h"
 #include "fxjs/cjs_document.h"
 #include "fxjs/cjs_icon.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/check.h"
+#include "third_party/base/notreached.h"
+#include "v8/include/v8-container.h"
 
 namespace {
 
+constexpr wchar_t kCheckSelector = L'4';
+constexpr wchar_t kCircleSelector = L'l';
+constexpr wchar_t kCrossSelector = L'8';
+constexpr wchar_t kDiamondSelector = L'u';
+constexpr wchar_t kSquareSelector = L'n';
+constexpr wchar_t kStarSelector = L'H';
+
 bool IsCheckBoxOrRadioButton(const CPDF_FormField* pFormField) {
   return pFormField->GetFieldType() == FormFieldType::kCheckBox ||
          pFormField->GetFieldType() == FormFieldType::kRadioButton;
@@ -45,121 +58,99 @@
 
 void UpdateFormField(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                      CPDF_FormField* pFormField,
-                     bool bChangeMark,
-                     bool bResetAP,
-                     bool bRefresh) {
+                     bool bResetAP) {
   CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
-
   if (bResetAP) {
-    std::vector<ObservedPtr<CPDFSDK_Annot>> widgets;
+    std::vector<ObservedPtr<CPDFSDK_Widget>> widgets;
     pForm->GetWidgets(pFormField, &widgets);
 
     if (IsComboBoxOrTextField(pFormField)) {
-      for (auto& pObserved : widgets) {
-        if (pObserved) {
-          Optional<WideString> sValue =
-              ToCPDFSDKWidget(pObserved.Get())->OnFormat();
-          if (pObserved) {  // Not redundant, may be clobbered by OnFormat.
-            ToCPDFSDKWidget(pObserved.Get())->ResetAppearance(sValue, false);
+      for (auto& pWidget : widgets) {
+        if (pWidget) {
+          absl::optional<WideString> sValue = pWidget->OnFormat();
+          if (pWidget) {  // Not redundant, may be clobbered by OnFormat.
+            pWidget->ResetAppearance(sValue, CPDFSDK_Widget::kValueUnchanged);
           }
         }
       }
     } else {
-      for (auto& pObserved : widgets) {
-        if (pObserved)
-          ToCPDFSDKWidget(pObserved.Get())->ResetAppearance({}, false);
+      for (auto& pWidget : widgets) {
+        if (pWidget) {
+          pWidget->ResetAppearance(absl::nullopt,
+                                   CPDFSDK_Widget::kValueUnchanged);
+        }
       }
     }
   }
 
-  if (bRefresh) {
-    // Refresh the widget list. The calls in |bResetAP| may have caused widgets
-    // to be removed from the list. We need to call |GetWidgets| again to be
-    // sure none of the widgets have been deleted.
-    std::vector<ObservedPtr<CPDFSDK_Annot>> widgets;
-    pForm->GetWidgets(pFormField, &widgets);
-
-    // TODO(dsinclair): Determine if all widgets share the same
-    // CPDFSDK_InteractiveForm. If that's the case, we can move the code to
-    // |GetFormFillEnv| out of the loop.
-    for (auto& pObserved : widgets) {
-      if (pObserved) {
-        CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pObserved.Get());
-        pWidget->GetInteractiveForm()->GetFormFillEnv()->UpdateAllViews(
-            nullptr, pWidget);
-      }
-    }
+  // Refresh the widget list. The calls in |bResetAP| may have caused widgets
+  // to be removed from the list. We need to call |GetWidgets| again to be
+  // sure none of the widgets have been deleted.
+  std::vector<ObservedPtr<CPDFSDK_Widget>> widgets;
+  pForm->GetWidgets(pFormField, &widgets);
+  for (auto& pWidget : widgets) {
+    if (pWidget)
+      pFormFillEnv->UpdateAllViews(pWidget.Get());
   }
-
-  if (bChangeMark)
-    pFormFillEnv->SetChangeMark();
+  pFormFillEnv->SetChangeMark();
 }
 
 void UpdateFormControl(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                        CPDF_FormControl* pFormControl,
-                       bool bChangeMark,
-                       bool bResetAP,
-                       bool bRefresh) {
-  ASSERT(pFormControl);
-
+                       bool bResetAP) {
+  DCHECK(pFormControl);
   CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
   CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl);
-
   if (pWidget) {
     ObservedPtr<CPDFSDK_Widget> observed_widget(pWidget);
     if (bResetAP) {
       FormFieldType fieldType = pWidget->GetFieldType();
       if (fieldType == FormFieldType::kComboBox ||
           fieldType == FormFieldType::kTextField) {
-        Optional<WideString> sValue = pWidget->OnFormat();
+        absl::optional<WideString> sValue = pWidget->OnFormat();
         if (!observed_widget)
           return;
-        pWidget->ResetAppearance(sValue, false);
+        pWidget->ResetAppearance(sValue, CPDFSDK_Widget::kValueUnchanged);
       } else {
-        pWidget->ResetAppearance({}, false);
+        pWidget->ResetAppearance(absl::nullopt,
+                                 CPDFSDK_Widget::kValueUnchanged);
       }
       if (!observed_widget)
         return;
     }
-
-    if (bRefresh) {
-      CPDFSDK_InteractiveForm* pWidgetForm = pWidget->GetInteractiveForm();
-      pWidgetForm->GetFormFillEnv()->UpdateAllViews(nullptr, pWidget);
-    }
+    pFormFillEnv->UpdateAllViews(pWidget);
   }
-
-  if (bChangeMark)
-    pFormFillEnv->SetChangeMark();
+  pFormFillEnv->SetChangeMark();
 }
 
-// note: iControlNo = -1, means not a widget.
-void ParseFieldName(const WideString& strFieldNameParsed,
-                    WideString& strFieldName,
-                    int& iControlNo) {
-  auto reverse_it = strFieldNameParsed.rbegin();
-  while (reverse_it != strFieldNameParsed.rend()) {
+struct FieldNameData {
+  FieldNameData(WideString field_name_in, int control_index_in)
+      : field_name(field_name_in), control_index(control_index_in) {}
+
+  WideString field_name;
+  int control_index;
+};
+
+absl::optional<FieldNameData> ParseFieldName(const WideString& field_name) {
+  auto reverse_it = field_name.rbegin();
+  while (reverse_it != field_name.rend()) {
     if (*reverse_it == L'.')
       break;
     ++reverse_it;
   }
-  if (reverse_it == strFieldNameParsed.rend()) {
-    strFieldName = strFieldNameParsed;
-    iControlNo = -1;
-    return;
+  if (reverse_it == field_name.rend()) {
+    return absl::nullopt;
   }
-  WideString suffixal =
-      strFieldNameParsed.Last(reverse_it - strFieldNameParsed.rbegin());
-  iControlNo = FXSYS_wtoi(suffixal.c_str());
-  if (iControlNo == 0) {
+  WideString suffixal = field_name.Last(reverse_it - field_name.rbegin());
+  int control_index = FXSYS_wtoi(suffixal.c_str());
+  if (control_index == 0) {
     suffixal.TrimRight(L' ');
     if (suffixal != L"0") {
-      strFieldName = strFieldNameParsed;
-      iControlNo = -1;
-      return;
+      return absl::nullopt;
     }
   }
-  strFieldName =
-      strFieldNameParsed.First(strFieldNameParsed.rend() - reverse_it - 1);
+  return FieldNameData(field_name.First(field_name.rend() - reverse_it - 1),
+                       control_index);
 }
 
 std::vector<CPDF_FormField*> GetFormFieldsForName(
@@ -168,13 +159,37 @@
   std::vector<CPDF_FormField*> fields;
   CPDFSDK_InteractiveForm* pReaderForm = pFormFillEnv->GetInteractiveForm();
   CPDF_InteractiveForm* pForm = pReaderForm->GetInteractiveForm();
-  for (int i = 0, sz = pForm->CountFields(csFieldName); i < sz; ++i) {
-    if (CPDF_FormField* pFormField = pForm->GetField(i, csFieldName))
+  const size_t sz = pForm->CountFields(csFieldName);
+  for (size_t i = 0; i < sz; ++i) {
+    CPDF_FormField* pFormField = pForm->GetField(i, csFieldName);
+    if (pFormField)
       fields.push_back(pFormField);
   }
   return fields;
 }
 
+CFX_Color GetFormControlColor(CPDF_FormControl* pFormControl,
+                              const ByteString& entry) {
+  switch (pFormControl->GetColorARGB(entry).color_type) {
+    case CFX_Color::Type::kTransparent:
+      return CFX_Color(CFX_Color::Type::kTransparent);
+    case CFX_Color::Type::kGray:
+      return CFX_Color(CFX_Color::Type::kGray,
+                       pFormControl->GetOriginalColorComponent(0, entry));
+    case CFX_Color::Type::kRGB:
+      return CFX_Color(CFX_Color::Type::kRGB,
+                       pFormControl->GetOriginalColorComponent(0, entry),
+                       pFormControl->GetOriginalColorComponent(1, entry),
+                       pFormControl->GetOriginalColorComponent(2, entry));
+    case CFX_Color::Type::kCMYK:
+      return CFX_Color(CFX_Color::Type::kCMYK,
+                       pFormControl->GetOriginalColorComponent(0, entry),
+                       pFormControl->GetOriginalColorComponent(1, entry),
+                       pFormControl->GetOriginalColorComponent(2, entry),
+                       pFormControl->GetOriginalColorComponent(3, entry));
+  }
+}
+
 bool SetWidgetDisplayStatus(CPDFSDK_Widget* pWidget, int value) {
   if (!pWidget)
     return false;
@@ -217,20 +232,20 @@
 void SetBorderStyle(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                     const WideString& swFieldName,
                     int nControlIndex,
-                    const ByteString& string) {
-  ASSERT(pFormFillEnv);
+                    const ByteString& bsString) {
+  DCHECK(pFormFillEnv);
 
-  BorderStyle nBorderStyle = BorderStyle::SOLID;
-  if (string == "solid")
-    nBorderStyle = BorderStyle::SOLID;
-  else if (string == "beveled")
-    nBorderStyle = BorderStyle::BEVELED;
-  else if (string == "dashed")
-    nBorderStyle = BorderStyle::DASH;
-  else if (string == "inset")
-    nBorderStyle = BorderStyle::INSET;
-  else if (string == "underline")
-    nBorderStyle = BorderStyle::UNDERLINE;
+  BorderStyle nBorderStyle;
+  if (bsString == "solid")
+    nBorderStyle = BorderStyle::kSolid;
+  else if (bsString == "beveled")
+    nBorderStyle = BorderStyle::kBeveled;
+  else if (bsString == "dashed")
+    nBorderStyle = BorderStyle::kDash;
+  else if (bsString == "inset")
+    nBorderStyle = BorderStyle::kInset;
+  else if (bsString == "underline")
+    nBorderStyle = BorderStyle::kUnderline;
   else
     return;
 
@@ -250,7 +265,7 @@
         }
       }
       if (bSet)
-        UpdateFormField(pFormFillEnv, pFormField, true, true, true);
+        UpdateFormField(pFormFillEnv, pFormField, true);
     } else {
       if (nControlIndex >= pFormField->CountControls())
         return;
@@ -260,7 +275,7 @@
         if (pWidget) {
           if (pWidget->GetBorderStyle() != nBorderStyle) {
             pWidget->SetBorderStyle(nBorderStyle);
-            UpdateFormControl(pFormFillEnv, pFormControl, true, true, true);
+            UpdateFormControl(pFormFillEnv, pFormControl, true);
           }
         }
       }
@@ -272,7 +287,7 @@
                             const WideString& swFieldName,
                             int nControlIndex,
                             const std::vector<uint32_t>& array) {
-  ASSERT(pFormFillEnv);
+  DCHECK(pFormFillEnv);
   std::vector<CPDF_FormField*> FieldArray =
       GetFormFieldsForName(pFormFillEnv, swFieldName);
 
@@ -287,11 +302,11 @@
         break;
       if (array[i] < static_cast<uint32_t>(pFormField->CountOptions()) &&
           !pFormField->IsItemSelected(array[i])) {
-        pFormField->SetItemSelection(array[i], true,
+        pFormField->SetItemSelection(array[i],
                                      NotificationOption::kDoNotNotify);
       }
     }
-    UpdateFormField(pFormFillEnv, pFormField, true, true, true);
+    UpdateFormField(pFormFillEnv, pFormField, true);
   }
 }
 
@@ -307,7 +322,7 @@
       bool bAnySet = false;
       for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) {
         CPDF_FormControl* pFormControl = pFormField->GetControl(i);
-        ASSERT(pFormControl);
+        DCHECK(pFormControl);
 
         CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl);
         if (SetWidgetDisplayStatus(pWidget, number))
@@ -315,7 +330,7 @@
       }
 
       if (bAnySet)
-        UpdateFormField(pFormFillEnv, pFormField, true, false, true);
+        UpdateFormField(pFormFillEnv, pFormField, false);
     } else {
       if (nControlIndex >= pFormField->CountControls())
         return;
@@ -326,7 +341,7 @@
 
       CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl);
       if (SetWidgetDisplayStatus(pWidget, number))
-        UpdateFormControl(pFormFillEnv, pFormControl, true, false, true);
+        UpdateFormControl(pFormFillEnv, pFormControl, false);
     }
   }
 }
@@ -351,7 +366,7 @@
       bool bSet = false;
       for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) {
         CPDF_FormControl* pFormControl = pFormField->GetControl(i);
-        ASSERT(pFormControl);
+        DCHECK(pFormControl);
 
         if (CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl)) {
           if (number != pWidget->GetBorderWidth()) {
@@ -361,7 +376,7 @@
         }
       }
       if (bSet)
-        UpdateFormField(pFormFillEnv, pFormField, true, true, true);
+        UpdateFormField(pFormFillEnv, pFormField, true);
     } else {
       if (nControlIndex >= pFormField->CountControls())
         return;
@@ -370,7 +385,7 @@
         if (CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl)) {
           if (number != pWidget->GetBorderWidth()) {
             pWidget->SetBorderWidth(number);
-            UpdateFormControl(pFormFillEnv, pFormControl, true, true, true);
+            UpdateFormControl(pFormFillEnv, pFormControl, true);
           }
         }
       }
@@ -390,7 +405,7 @@
       bool bSet = false;
       for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) {
         CPDF_FormControl* pFormControl = pFormField->GetControl(i);
-        ASSERT(pFormControl);
+        DCHECK(pFormControl);
 
         if (CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl)) {
           CFX_FloatRect crRect = rect;
@@ -410,7 +425,7 @@
       }
 
       if (bSet)
-        UpdateFormField(pFormFillEnv, pFormField, true, true, true);
+        UpdateFormField(pFormFillEnv, pFormField, true);
 
       continue;
     }
@@ -428,7 +443,7 @@
           if (crRect.left != rcOld.left || crRect.right != rcOld.right ||
               crRect.top != rcOld.top || crRect.bottom != rcOld.bottom) {
             pWidget->SetRect(crRect);
-            UpdateFormControl(pFormFillEnv, pFormControl, true, true, true);
+            UpdateFormControl(pFormFillEnv, pFormControl, true);
           }
         }
       }
@@ -440,7 +455,7 @@
                    const WideString& swFieldName,
                    int nControlIndex,
                    const std::vector<WideString>& strArray) {
-  ASSERT(pFormFillEnv);
+  DCHECK(pFormFillEnv);
   if (strArray.empty())
     return;
 
@@ -448,7 +463,7 @@
       GetFormFieldsForName(pFormFillEnv, swFieldName);
 
   for (CPDF_FormField* pFormField : FieldArray) {
-    if (pFormField->GetFullName().Compare(swFieldName) != 0)
+    if (pFormField->GetFullName() != swFieldName)
       continue;
 
     switch (pFormField->GetFieldType()) {
@@ -456,14 +471,14 @@
       case FormFieldType::kComboBox:
         if (pFormField->GetValue() != strArray[0]) {
           pFormField->SetValue(strArray[0], NotificationOption::kNotify);
-          UpdateFormField(pFormFillEnv, pFormField, true, false, true);
+          UpdateFormField(pFormFillEnv, pFormField, false);
         }
         break;
       case FormFieldType::kCheckBox:
       case FormFieldType::kRadioButton:
         if (pFormField->GetValue() != strArray[0]) {
           pFormField->SetValue(strArray[0], NotificationOption::kNotify);
-          UpdateFormField(pFormFillEnv, pFormField, true, false, true);
+          UpdateFormField(pFormFillEnv, pFormField, false);
         }
         break;
       case FormFieldType::kListBox: {
@@ -479,10 +494,9 @@
           for (const auto& str : strArray) {
             int index = pFormField->FindOption(str);
             if (!pFormField->IsItemSelected(index))
-              pFormField->SetItemSelection(index, true,
-                                           NotificationOption::kNotify);
+              pFormField->SetItemSelection(index, NotificationOption::kNotify);
           }
-          UpdateFormField(pFormFillEnv, pFormField, true, false, true);
+          UpdateFormField(pFormFillEnv, pFormField, false);
         }
         break;
       }
@@ -492,6 +506,19 @@
   }
 }
 
+wchar_t GetSelectorFromCaptionForFieldType(const WideString& caption,
+                                           CPDF_FormField::Type type) {
+  if (!caption.IsEmpty())
+    return caption[0];
+
+  switch (type) {
+    case CPDF_FormField::kRadioButton:
+      return kCircleSelector;
+    default:
+      return kCheckSelector;
+  }
+}
+
 }  // namespace
 
 const JSPropertySpec CJS_Field::PropertySpecs[] = {
@@ -585,11 +612,11 @@
     {"signatureSign", signatureSign_static},
     {"signatureValidate", signatureValidate_static}};
 
-int CJS_Field::ObjDefnID = -1;
+uint32_t CJS_Field::ObjDefnID = 0;
 const char CJS_Field::kName[] = "Field";
 
 // static
-int CJS_Field::GetObjDefnID() {
+uint32_t CJS_Field::GetObjDefnID() {
   return ObjDefnID;
 }
 
@@ -610,9 +637,10 @@
                             const WideString& csFieldName) {
   m_pJSDoc.Reset(pDocument);
   m_pFormFillEnv.Reset(pDocument->GetFormFillEnv());
-  m_bCanSet = m_pFormFillEnv->GetPermissions(FPDFPERM_FILL_FORM) ||
-              m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM) ||
-              m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY);
+  m_bCanSet = m_pFormFillEnv->HasPermissions(
+      pdfium::access_permissions::kFillForm |
+      pdfium::access_permissions::kModifyAnnotation |
+      pdfium::access_permissions::kModifyContent);
 
   CPDFSDK_InteractiveForm* pRDForm = m_pFormFillEnv->GetInteractiveForm();
   CPDF_InteractiveForm* pForm = pRDForm->GetInteractiveForm();
@@ -620,14 +648,12 @@
   swFieldNameTemp.Replace(L"..", L".");
 
   if (pForm->CountFields(swFieldNameTemp) <= 0) {
-    WideString strFieldName;
-    int iControlNo = -1;
-    ParseFieldName(swFieldNameTemp, strFieldName, iControlNo);
-    if (iControlNo == -1)
+    absl::optional<FieldNameData> parsed_data = ParseFieldName(swFieldNameTemp);
+    if (!parsed_data.has_value())
       return false;
 
-    m_FieldName = strFieldName;
-    m_nFormControlIndex = iControlNo;
+    m_FieldName = parsed_data.value().field_name;
+    m_nFormControlIndex = parsed_data.value().control_index;
     return true;
   }
 
@@ -656,7 +682,7 @@
 }
 
 CJS_Result CJS_Field::get_alignment(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -682,7 +708,7 @@
 
 CJS_Result CJS_Field::set_alignment(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
@@ -690,7 +716,7 @@
 }
 
 CJS_Result CJS_Field::get_border_style(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -702,15 +728,15 @@
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   switch (pWidget->GetBorderStyle()) {
-    case BorderStyle::SOLID:
+    case BorderStyle::kSolid:
       return CJS_Result::Success(pRuntime->NewString("solid"));
-    case BorderStyle::DASH:
+    case BorderStyle::kDash:
       return CJS_Result::Success(pRuntime->NewString("dashed"));
-    case BorderStyle::BEVELED:
+    case BorderStyle::kBeveled:
       return CJS_Result::Success(pRuntime->NewString("beveled"));
-    case BorderStyle::INSET:
+    case BorderStyle::kInset:
       return CJS_Result::Success(pRuntime->NewString("inset"));
-    case BorderStyle::UNDERLINE:
+    case BorderStyle::kUnderline:
       return CJS_Result::Success(pRuntime->NewString("underline"));
   }
   return CJS_Result::Success(pRuntime->NewString(""));
@@ -718,11 +744,11 @@
 
 CJS_Result CJS_Field::set_border_style(CJS_Runtime* pRuntime,
                                        v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
-  ByteString byte_str = pRuntime->ToWideString(vp).ToDefANSI();
+  ByteString byte_str = pRuntime->ToByteString(vp);
   if (m_bDelay) {
     AddDelay_String(FP_BORDERSTYLE, byte_str);
   } else {
@@ -733,7 +759,7 @@
 }
 
 CJS_Result CJS_Field::get_button_align_x(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -753,14 +779,14 @@
 
 CJS_Result CJS_Field::set_button_align_x(CJS_Runtime* pRuntime,
                                          v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_button_align_y(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -780,14 +806,14 @@
 
 CJS_Result CJS_Field::set_button_align_y(CJS_Runtime* pRuntime,
                                          v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_button_fit_bounds(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -806,14 +832,14 @@
 
 CJS_Result CJS_Field::set_button_fit_bounds(CJS_Runtime* pRuntime,
                                             v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_button_position(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -832,14 +858,14 @@
 
 CJS_Result CJS_Field::set_button_position(CJS_Runtime* pRuntime,
                                           v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_button_scale_how(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -852,20 +878,20 @@
   if (!pFormControl)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  return CJS_Result::Success(pRuntime->NewBoolean(
-      pFormControl->GetIconFit().IsProportionalScale() ? 0 : 1));
+  return CJS_Result::Success(
+      pRuntime->NewBoolean(!pFormControl->GetIconFit().IsProportionalScale()));
 }
 
 CJS_Result CJS_Field::set_button_scale_how(CJS_Runtime* pRuntime,
                                            v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_button_scale_when(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -879,34 +905,27 @@
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   CPDF_IconFit IconFit = pFormControl->GetIconFit();
-  int ScaleM = IconFit.GetScaleMethod();
-  switch (ScaleM) {
-    case CPDF_IconFit::Always:
+  CPDF_IconFit::ScaleMethod scale_method = IconFit.GetScaleMethod();
+  switch (scale_method) {
+    case CPDF_IconFit::ScaleMethod::kAlways:
+    case CPDF_IconFit::ScaleMethod::kBigger:
+    case CPDF_IconFit::ScaleMethod::kNever:
+    case CPDF_IconFit::ScaleMethod::kSmaller:
       return CJS_Result::Success(
-          pRuntime->NewNumber(static_cast<int32_t>(CPDF_IconFit::Always)));
-    case CPDF_IconFit::Bigger:
-      return CJS_Result::Success(
-          pRuntime->NewNumber(static_cast<int32_t>(CPDF_IconFit::Bigger)));
-    case CPDF_IconFit::Never:
-      return CJS_Result::Success(
-          pRuntime->NewNumber(static_cast<int32_t>(CPDF_IconFit::Never)));
-    case CPDF_IconFit::Smaller:
-      return CJS_Result::Success(
-          pRuntime->NewNumber(static_cast<int32_t>(CPDF_IconFit::Smaller)));
+          pRuntime->NewNumber(static_cast<int>(scale_method)));
   }
-  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::set_button_scale_when(CJS_Runtime* pRuntime,
                                             v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_calc_order_index(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -917,20 +936,20 @@
 
   CPDFSDK_InteractiveForm* pRDForm = m_pFormFillEnv->GetInteractiveForm();
   CPDF_InteractiveForm* pForm = pRDForm->GetInteractiveForm();
-  return CJS_Result::Success(pRuntime->NewNumber(
-      static_cast<int32_t>(pForm->FindFieldInCalculationOrder(pFormField))));
+  return CJS_Result::Success(
+      pRuntime->NewNumber(pForm->FindFieldInCalculationOrder(pFormField)));
 }
 
 CJS_Result CJS_Field::set_calc_order_index(CJS_Runtime* pRuntime,
                                            v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_char_limit(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -944,14 +963,14 @@
 
 CJS_Result CJS_Field::set_char_limit(CJS_Runtime* pRuntime,
                                      v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_comb(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -965,14 +984,14 @@
 }
 
 CJS_Result CJS_Field::set_comb(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_commit_on_sel_change(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -988,7 +1007,7 @@
 
 CJS_Result CJS_Field::set_commit_on_sel_change(CJS_Runtime* pRuntime,
                                                v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -1027,7 +1046,7 @@
   std::vector<uint32_t> array;
   if (vp->IsNumber()) {
     array.push_back(pRuntime->ToInt32(vp));
-  } else if (!vp.IsEmpty() && vp->IsArray()) {
+  } else if (fxv8::IsArray(vp)) {
     v8::Local<v8::Array> SelArray = pRuntime->ToArray(vp);
     for (size_t i = 0; i < pRuntime->GetArrayLength(SelArray); i++) {
       array.push_back(
@@ -1054,7 +1073,7 @@
 }
 
 CJS_Result CJS_Field::get_default_value(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1071,14 +1090,14 @@
 
 CJS_Result CJS_Field::set_default_value(CJS_Runtime* pRuntime,
                                         v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_do_not_scroll(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1093,14 +1112,14 @@
 
 CJS_Result CJS_Field::set_do_not_scroll(CJS_Runtime* pRuntime,
                                         v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_do_not_spell_check(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1116,21 +1135,12 @@
 
 CJS_Result CJS_Field::set_do_not_spell_check(CJS_Runtime* pRuntime,
                                              v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
-void CJS_Field::SetDelay(bool bDelay) {
-  m_bDelay = bDelay;
-
-  if (m_bDelay)
-    return;
-  if (m_pJSDoc)
-    m_pJSDoc->DoFieldDelay(m_FieldName, m_nFormControlIndex);
-}
-
 CJS_Result CJS_Field::get_delay(CJS_Runtime* pRuntime) {
   return CJS_Result::Success(pRuntime->NewBoolean(m_bDelay));
 }
@@ -1253,7 +1263,7 @@
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
-  if (vp.IsEmpty() || !vp->IsArray())
+  if (!fxv8::IsArray(vp))
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   return CJS_Result::Success();
@@ -1295,30 +1305,7 @@
   if (!pFormControl)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  int iColorType;
-  pFormControl->GetBackgroundColor(iColorType);
-
-  CFX_Color color;
-  if (iColorType == CFX_Color::kTransparent) {
-    color = CFX_Color(CFX_Color::kTransparent);
-  } else if (iColorType == CFX_Color::kGray) {
-    color = CFX_Color(CFX_Color::kGray,
-                      pFormControl->GetOriginalBackgroundColor(0));
-  } else if (iColorType == CFX_Color::kRGB) {
-    color =
-        CFX_Color(CFX_Color::kRGB, pFormControl->GetOriginalBackgroundColor(0),
-                  pFormControl->GetOriginalBackgroundColor(1),
-                  pFormControl->GetOriginalBackgroundColor(2));
-  } else if (iColorType == CFX_Color::kCMYK) {
-    color =
-        CFX_Color(CFX_Color::kCMYK, pFormControl->GetOriginalBackgroundColor(0),
-                  pFormControl->GetOriginalBackgroundColor(1),
-                  pFormControl->GetOriginalBackgroundColor(2),
-                  pFormControl->GetOriginalBackgroundColor(3));
-  } else {
-    return CJS_Result::Failure(JSMessage::kValueError);
-  }
-
+  CFX_Color color = GetFormControlColor(pFormControl, pdfium::appearance::kBG);
   v8::Local<v8::Value> array =
       CJS_Color::ConvertPWLColorToArray(pRuntime, color);
   if (array.IsEmpty())
@@ -1333,7 +1320,7 @@
     return CJS_Result::Failure(JSMessage::kBadObjectError);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
-  if (vp.IsEmpty() || !vp->IsArray())
+  if (!fxv8::IsArray(vp))
     return CJS_Result::Failure(JSMessage::kBadObjectError);
   return CJS_Result::Success();
 }
@@ -1369,7 +1356,7 @@
 }
 
 CJS_Result CJS_Field::get_highlight(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1384,15 +1371,15 @@
 
   int eHM = pFormControl->GetHighlightingMode();
   switch (eHM) {
-    case CPDF_FormControl::None:
+    case CPDF_FormControl::kNone:
       return CJS_Result::Success(pRuntime->NewString("none"));
-    case CPDF_FormControl::Push:
+    case CPDF_FormControl::kPush:
       return CJS_Result::Success(pRuntime->NewString("push"));
-    case CPDF_FormControl::Invert:
+    case CPDF_FormControl::kInvert:
       return CJS_Result::Success(pRuntime->NewString("invert"));
-    case CPDF_FormControl::Outline:
+    case CPDF_FormControl::kOutline:
       return CJS_Result::Success(pRuntime->NewString("outline"));
-    case CPDF_FormControl::Toggle:
+    case CPDF_FormControl::kToggle:
       return CJS_Result::Success(pRuntime->NewString("toggle"));
   }
   return CJS_Result::Success();
@@ -1400,7 +1387,7 @@
 
 CJS_Result CJS_Field::set_highlight(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -1441,7 +1428,7 @@
 }
 
 CJS_Result CJS_Field::get_multiline(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1456,14 +1443,14 @@
 
 CJS_Result CJS_Field::set_multiline(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_multiple_selection(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
@@ -1478,7 +1465,7 @@
 
 CJS_Result CJS_Field::set_multiple_selection(CJS_Runtime* pRuntime,
                                              v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -1517,25 +1504,20 @@
   if (!pFormField)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  std::vector<ObservedPtr<CPDFSDK_Annot>> widgets;
+  std::vector<ObservedPtr<CPDFSDK_Widget>> widgets;
   m_pFormFillEnv->GetInteractiveForm()->GetWidgets(pFormField, &widgets);
   if (widgets.empty())
     return CJS_Result::Success(pRuntime->NewNumber(-1));
 
   v8::Local<v8::Array> PageArray = pRuntime->NewArray();
   int i = 0;
-  for (const auto& pObserved : widgets) {
-    if (!pObserved)
-      return CJS_Result::Failure(JSMessage::kBadObjectError);
-
-    auto* pWidget = ToCPDFSDKWidget(pObserved.Get());
-    CPDFSDK_PageView* pPageView = pWidget->GetPageView();
-    if (!pPageView)
+  for (const auto& pWidget : widgets) {
+    if (!pWidget)
       return CJS_Result::Failure(JSMessage::kBadObjectError);
 
     pRuntime->PutArrayElement(
         PageArray, i,
-        pRuntime->NewNumber(static_cast<int32_t>(pPageView->GetPageIndex())));
+        pRuntime->NewNumber(pWidget->GetPageView()->GetPageIndex()));
     ++i;
   }
   return CJS_Result::Success(PageArray);
@@ -1546,7 +1528,7 @@
 }
 
 CJS_Result CJS_Field::get_password(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1561,7 +1543,7 @@
 
 CJS_Result CJS_Field::set_password(CJS_Runtime* pRuntime,
                                    v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -1611,7 +1593,7 @@
       }
 
       if (bSet)
-        UpdateFormField(m_pFormFillEnv.Get(), pFormField, true, false, true);
+        UpdateFormField(m_pFormFillEnv.Get(), pFormField, false);
 
       continue;
     }
@@ -1631,8 +1613,7 @@
         if (dwFlags != pWidget->GetFlags()) {
           pWidget->SetFlags(dwFlags);
           UpdateFormControl(m_pFormFillEnv.Get(),
-                            pFormField->GetControl(m_nFormControlIndex), true,
-                            false, true);
+                            pFormField->GetControl(m_nFormControlIndex), false);
         }
       }
     }
@@ -1674,11 +1655,21 @@
 
 CJS_Result CJS_Field::set_readonly(CJS_Runtime* pRuntime,
                                    v8::Local<v8::Value> vp) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields();
-  if (FieldArray.empty())
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
+
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
+
+  const bool bReadOnly = pRuntime->ToBoolean(vp);
+  const uint32_t dwFlags = pFormField->GetFieldFlags();
+  const uint32_t dwNewFlags = bReadOnly
+                                  ? (dwFlags | pdfium::form_flags::kReadOnly)
+                                  : (dwFlags & ~pdfium::form_flags::kReadOnly);
+  if (dwNewFlags != dwFlags)
+    pFormField->SetFieldFlags(dwNewFlags);
+
   return CJS_Result::Success();
 }
 
@@ -1709,24 +1700,23 @@
 CJS_Result CJS_Field::set_rect(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
-  if (vp.IsEmpty() || !vp->IsArray())
+  if (!fxv8::IsArray(vp))
     return CJS_Result::Failure(JSMessage::kValueError);
 
   v8::Local<v8::Array> rcArray = pRuntime->ToArray(vp);
   if (pRuntime->GetArrayLength(rcArray) < 4)
     return CJS_Result::Failure(JSMessage::kValueError);
 
-  float pArray[4];
-  pArray[0] = static_cast<float>(
+  float f0 = static_cast<float>(
       pRuntime->ToInt32(pRuntime->GetArrayElement(rcArray, 0)));
-  pArray[1] = static_cast<float>(
+  float f1 = static_cast<float>(
       pRuntime->ToInt32(pRuntime->GetArrayElement(rcArray, 1)));
-  pArray[2] = static_cast<float>(
+  float f2 = static_cast<float>(
       pRuntime->ToInt32(pRuntime->GetArrayElement(rcArray, 2)));
-  pArray[3] = static_cast<float>(
+  float f3 = static_cast<float>(
       pRuntime->ToInt32(pRuntime->GetArrayElement(rcArray, 3)));
 
-  CFX_FloatRect crRect(pArray);
+  CFX_FloatRect crRect(f0, f1, f2, f3);
   if (m_bDelay) {
     AddDelay_Rect(FP_RECT, crRect);
   } else {
@@ -1758,7 +1748,7 @@
 }
 
 CJS_Result CJS_Field::get_rich_text(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1773,7 +1763,7 @@
 
 CJS_Result CJS_Field::set_rich_text(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -1789,7 +1779,7 @@
 }
 
 CJS_Result CJS_Field::get_rotation(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1804,7 +1794,7 @@
 
 CJS_Result CJS_Field::set_rotation(CJS_Runtime* pRuntime,
                                    v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -1828,28 +1818,7 @@
   if (!pFormControl)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  int iColorType;
-  pFormControl->GetBorderColor(iColorType);
-
-  CFX_Color color;
-  if (iColorType == CFX_Color::kTransparent) {
-    color = CFX_Color(CFX_Color::kTransparent);
-  } else if (iColorType == CFX_Color::kGray) {
-    color =
-        CFX_Color(CFX_Color::kGray, pFormControl->GetOriginalBorderColor(0));
-  } else if (iColorType == CFX_Color::kRGB) {
-    color = CFX_Color(CFX_Color::kRGB, pFormControl->GetOriginalBorderColor(0),
-                      pFormControl->GetOriginalBorderColor(1),
-                      pFormControl->GetOriginalBorderColor(2));
-  } else if (iColorType == CFX_Color::kCMYK) {
-    color = CFX_Color(CFX_Color::kCMYK, pFormControl->GetOriginalBorderColor(0),
-                      pFormControl->GetOriginalBorderColor(1),
-                      pFormControl->GetOriginalBorderColor(2),
-                      pFormControl->GetOriginalBorderColor(3));
-  } else {
-    return CJS_Result::Failure(JSMessage::kObjectTypeError);
-  }
-
+  CFX_Color color = GetFormControlColor(pFormControl, pdfium::appearance::kBC);
   v8::Local<v8::Value> array =
       CJS_Color::ConvertPWLColorToArray(pRuntime, color);
   if (array.IsEmpty())
@@ -1861,13 +1830,13 @@
                                        v8::Local<v8::Value> vp) {
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
-  if (vp.IsEmpty() || !vp->IsArray())
+  if (!fxv8::IsArray(vp))
     return CJS_Result::Failure(JSMessage::kBadObjectError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_style(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1880,37 +1849,37 @@
   if (!pFormControl)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  WideString csWCaption = pFormControl->GetNormalCaption();
-  wchar_t selector = !csWCaption.IsEmpty() ? csWCaption[0] : L'4';
+  wchar_t selector = GetSelectorFromCaptionForFieldType(
+      pFormControl->GetNormalCaption(), pFormControl->GetType());
 
   ByteString csBCaption;
   switch (selector) {
-    case L'l':
+    case kCircleSelector:
       csBCaption = "circle";
       break;
-    case L'8':
+    case kCrossSelector:
       csBCaption = "cross";
       break;
-    case L'u':
+    case kDiamondSelector:
       csBCaption = "diamond";
       break;
-    case L'n':
+    case kSquareSelector:
       csBCaption = "square";
       break;
-    case L'H':
+    case kStarSelector:
       csBCaption = "star";
       break;
-    default:  // L'4'
+    case kCheckSelector:
+    default:
       csBCaption = "check";
       break;
   }
-  return CJS_Result::Success(pRuntime->NewString(
-      WideString::FromDefANSI(csBCaption.AsStringView()).AsStringView()));
+  return CJS_Result::Success(pRuntime->NewString(csBCaption.AsStringView()));
 }
 
 CJS_Result CJS_Field::set_style(CJS_Runtime* pRuntime,
                                 v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -1934,21 +1903,21 @@
   if (!pFormControl)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  Optional<CFX_Color::Type> iColorType;
-  FX_ARGB color;
   CPDF_DefaultAppearance FieldAppearance = pFormControl->GetDefaultAppearance();
-  std::tie(iColorType, color) = FieldAppearance.GetColor();
+  absl::optional<CFX_Color::TypeAndARGB> maybe_type_argb_pair =
+      FieldAppearance.GetColorARGB();
 
   CFX_Color crRet;
-  if (!iColorType || *iColorType == CFX_Color::kTransparent) {
-    crRet = CFX_Color(CFX_Color::kTransparent);
-  } else {
+  if (maybe_type_argb_pair.has_value() &&
+      maybe_type_argb_pair.value().color_type !=
+          CFX_Color::Type::kTransparent) {
     int32_t a;
     int32_t r;
     int32_t g;
     int32_t b;
-    std::tie(a, r, g, b) = ArgbDecode(color);
-    crRet = CFX_Color(CFX_Color::kRGB, r / 255.0f, g / 255.0f, b / 255.0f);
+    std::tie(a, r, g, b) = ArgbDecode(maybe_type_argb_pair.value().argb);
+    crRet =
+        CFX_Color(CFX_Color::Type::kRGB, r / 255.0f, g / 255.0f, b / 255.0f);
   }
 
   v8::Local<v8::Value> array =
@@ -1962,13 +1931,13 @@
                                      v8::Local<v8::Value> vp) {
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
-  if (vp.IsEmpty() || !vp->IsArray())
+  if (!fxv8::IsArray(vp))
     return CJS_Result::Failure(JSMessage::kBadObjectError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_text_font(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1986,7 +1955,8 @@
     return CJS_Result::Failure(JSMessage::kObjectTypeError);
   }
 
-  Optional<WideString> wsFontName = pFormControl->GetDefaultControlFontName();
+  absl::optional<WideString> wsFontName =
+      pFormControl->GetDefaultControlFontName();
   if (!wsFontName.has_value())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -1996,17 +1966,17 @@
 
 CJS_Result CJS_Field::set_text_font(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
-  if (pRuntime->ToWideString(vp).ToDefANSI().IsEmpty())
+  if (pRuntime->ToByteString(vp).IsEmpty())
     return CJS_Result::Failure(JSMessage::kValueError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_text_size(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -2024,7 +1994,7 @@
 
 CJS_Result CJS_Field::set_text_size(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -2052,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
   }
 }
 
@@ -2062,7 +2034,7 @@
 }
 
 CJS_Result CJS_Field::get_user_name(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -2074,7 +2046,7 @@
 
 CJS_Result CJS_Field::set_user_name(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -2102,7 +2074,7 @@
           iIndex = pFormField->GetSelectedIndex(i);
           ElementValue = pRuntime->NewString(
               pFormField->GetOptionValue(iIndex).AsStringView());
-          if (wcslen(pRuntime->ToWideString(ElementValue).c_str()) == 0) {
+          if (pRuntime->ToWideString(ElementValue).IsEmpty()) {
             ElementValue = pRuntime->NewString(
                 pFormField->GetOptionLabel(iIndex).AsStringView());
           }
@@ -2143,7 +2115,7 @@
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
   std::vector<WideString> strArray;
-  if (!vp.IsEmpty() && vp->IsArray()) {
+  if (fxv8::IsArray(vp)) {
     v8::Local<v8::Array> ValueArray = pRuntime->ToArray(vp);
     for (size_t i = 0; i < pRuntime->GetArrayLength(ValueArray); i++) {
       strArray.push_back(
@@ -2214,7 +2186,7 @@
     WideString wsFileName = m_pFormFillEnv->JS_fieldBrowse();
     if (!wsFileName.IsEmpty()) {
       pFormField->SetValue(wsFileName, NotificationOption::kDoNotNotify);
-      UpdateFormField(m_pFormFillEnv.Get(), pFormField, true, true, true);
+      UpdateFormField(m_pFormFillEnv.Get(), pFormField, true);
     }
     return CJS_Result::Success();
   }
@@ -2225,8 +2197,7 @@
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   int nface = 0;
-  int iSize = params.size();
-  if (iSize >= 1)
+  if (params.size() >= 1)
     nface = pRuntime->ToInt32(params[0]);
 
   CPDF_FormField* pFormField = GetFirstFormField();
@@ -2280,7 +2251,8 @@
   if (pObj.IsEmpty())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  auto* pJS_Icon = static_cast<CJS_Icon*>(CFXJS_Engine::GetObjectPrivate(pObj));
+  auto* pJS_Icon = static_cast<CJS_Icon*>(
+      CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj));
   return pJS_Icon ? CJS_Result::Success(pJS_Icon->ToV8Object())
                   : CJS_Result::Failure(JSMessage::kBadObjectError);
 }
@@ -2306,8 +2278,8 @@
 CJS_Result CJS_Field::checkThisBox(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  int iSize = params.size();
-  if (iSize < 1)
+  const size_t nSize = params.size();
+  if (nSize == 0)
     return CJS_Result::Failure(JSMessage::kParamError);
 
   if (!m_bCanSet)
@@ -2315,7 +2287,7 @@
 
   int nWidget = pRuntime->ToInt32(params[0]);
   bool bCheckit = true;
-  if (iSize >= 2)
+  if (nSize >= 2)
     bCheckit = pRuntime->ToBoolean(params[1]);
 
   CPDF_FormField* pFormField = GetFirstFormField();
@@ -2331,7 +2303,7 @@
   // TODO(weili): Check whether anything special needed for radio button.
   // (When pFormField->GetFieldType() == FormFieldType::kRadioButton.)
   pFormField->CheckControl(nWidget, bCheckit, NotificationOption::kNotify);
-  UpdateFormField(m_pFormFillEnv.Get(), pFormField, true, true, true);
+  UpdateFormField(m_pFormFillEnv.Get(), pFormField, true);
   return CJS_Result::Success();
 }
 
@@ -2347,8 +2319,7 @@
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
-  int iSize = params.size();
-  if (iSize < 1)
+  if (params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
 
   CPDF_FormField* pFormField = GetFirstFormField();
@@ -2378,7 +2349,7 @@
 
   std::vector<std::unique_ptr<WideString>> swSort;
   for (CPDF_FormField* pFormField : FieldArray) {
-    swSort.push_back(pdfium::MakeUnique<WideString>(pFormField->GetFullName()));
+    swSort.push_back(std::make_unique<WideString>(pFormField->GetFullName()));
   }
 
   std::sort(swSort.begin(), swSort.end(),
@@ -2393,8 +2364,8 @@
     if (pObj.IsEmpty())
       return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-    auto* pJSField =
-        static_cast<CJS_Field*>(CFXJS_Engine::GetObjectPrivate(pObj));
+    auto* pJSField = static_cast<CJS_Field*>(
+        CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj));
     pJSField->AttachField(m_pJSDoc.Get(), *pStr);
     pRuntime->PutArrayElement(FormFieldArray, j++,
                               pJSField
@@ -2407,13 +2378,13 @@
 CJS_Result CJS_Field::getItemAt(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  int iSize = params.size();
+  const size_t nSize = params.size();
   int nIdx = -1;
-  if (iSize >= 1)
+  if (nSize >= 1)
     nIdx = pRuntime->ToInt32(params[0]);
 
   bool bExport = true;
-  if (iSize >= 2)
+  if (nSize >= 2)
     bExport = pRuntime->ToBoolean(params[1]);
 
   CPDF_FormField* pFormField = GetFirstFormField();
@@ -2509,18 +2480,16 @@
   if (nCount == 1) {
     pWidget = pForm->GetWidget(pFormField->GetControl(0));
   } else {
-    IPDF_Page* pPage = IPDFPageFromFPDFPage(m_pFormFillEnv->GetCurrentPage());
+    IPDF_Page* pPage = m_pFormFillEnv->GetCurrentPage();
     if (!pPage)
       return CJS_Result::Failure(JSMessage::kBadObjectError);
-    if (CPDFSDK_PageView* pCurPageView =
-            m_pFormFillEnv->GetPageView(pPage, true)) {
-      for (int32_t i = 0; i < nCount; i++) {
-        if (CPDFSDK_Widget* pTempWidget =
-                pForm->GetWidget(pFormField->GetControl(i))) {
-          if (pTempWidget->GetPDFPage() == pCurPageView->GetPDFPage()) {
-            pWidget = pTempWidget;
-            break;
-          }
+    CPDFSDK_PageView* pCurPageView = m_pFormFillEnv->GetOrCreatePageView(pPage);
+    for (int32_t i = 0; i < nCount; i++) {
+      if (CPDFSDK_Widget* pTempWidget =
+              pForm->GetWidget(pFormField->GetControl(i))) {
+        if (pTempWidget->GetPDFPage() == pCurPageView->GetPDFPage()) {
+          pWidget = pTempWidget;
+          break;
         }
       }
     }
@@ -2528,7 +2497,7 @@
 
   if (pWidget) {
     ObservedPtr<CPDFSDK_Annot> pObserved(pWidget);
-    m_pFormFillEnv->SetFocusAnnot(&pObserved);
+    m_pFormFillEnv->SetFocusAnnot(pObserved);
   }
 
   return CJS_Result::Success();
@@ -2581,30 +2550,39 @@
   return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
+void CJS_Field::SetDelay(bool bDelay) {
+  m_bDelay = bDelay;
+  if (m_bDelay)
+    return;
+
+  if (m_pJSDoc)
+    m_pJSDoc->DoFieldDelay(m_FieldName, m_nFormControlIndex);
+}
+
 void CJS_Field::AddDelay_Int(FIELD_PROP prop, int32_t n) {
   auto pNewData =
-      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
+      std::make_unique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
   pNewData->num = n;
   m_pJSDoc->AddDelayData(std::move(pNewData));
 }
 
 void CJS_Field::AddDelay_Bool(FIELD_PROP prop, bool b) {
   auto pNewData =
-      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
+      std::make_unique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
   pNewData->b = b;
   m_pJSDoc->AddDelayData(std::move(pNewData));
 }
 
 void CJS_Field::AddDelay_String(FIELD_PROP prop, const ByteString& str) {
   auto pNewData =
-      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
+      std::make_unique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
   pNewData->bytestring = str;
   m_pJSDoc->AddDelayData(std::move(pNewData));
 }
 
 void CJS_Field::AddDelay_Rect(FIELD_PROP prop, const CFX_FloatRect& rect) {
   auto pNewData =
-      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
+      std::make_unique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
   pNewData->rect = rect;
   m_pJSDoc->AddDelayData(std::move(pNewData));
 }
@@ -2612,7 +2590,7 @@
 void CJS_Field::AddDelay_WordArray(FIELD_PROP prop,
                                    const std::vector<uint32_t>& array) {
   auto pNewData =
-      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
+      std::make_unique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
   pNewData->wordarray = array;
   m_pJSDoc->AddDelayData(std::move(pNewData));
 }
@@ -2620,14 +2598,14 @@
 void CJS_Field::AddDelay_WideStringArray(FIELD_PROP prop,
                                          const std::vector<WideString>& array) {
   auto pNewData =
-      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
+      std::make_unique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
   pNewData->widestringarray = array;
   m_pJSDoc->AddDelayData(std::move(pNewData));
 }
 
 void CJS_Field::DoDelay(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                         CJS_DelayData* pData) {
-  ASSERT(pFormFillEnv);
+  DCHECK(pFormFillEnv);
   switch (pData->eProp) {
     case FP_BORDERSTYLE:
       SetBorderStyle(pFormFillEnv, pData->sFieldName, pData->nControlIndex,
@@ -2657,7 +2635,5 @@
       SetFieldValue(pFormFillEnv, pData->sFieldName, pData->nControlIndex,
                     pData->widestringarray);
       break;
-    default:
-      NOTREACHED();
   }
 }
diff --git a/fxjs/cjs_field.h b/fxjs/cjs_field.h
index d772e66..7a66277 100644
--- a/fxjs/cjs_field.h
+++ b/fxjs/cjs_field.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,8 +13,8 @@
 #include "fxjs/cjs_object.h"
 #include "fxjs/js_define.h"
 
+class CFX_FloatRect;
 class CPDF_FormControl;
-class CPDFSDK_Widget;
 struct CJS_DelayData;
 
 enum FIELD_PROP {
@@ -29,7 +29,7 @@
 
 class CJS_Field final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
   static void DoDelay(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                       CJS_DelayData* pData);
@@ -123,7 +123,7 @@
   CJS_Result set_text_color(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSPropertySpec PropertySpecs[];
   static const JSMethodSpec MethodSpecs[];
@@ -349,11 +349,11 @@
   CJS_Result signatureValidate(CJS_Runtime* pRuntime,
                                const std::vector<v8::Local<v8::Value>>& params);
 
-  void SetDelay(bool bDelay);
   std::vector<CPDF_FormField*> GetFormFields() const;
   CPDF_FormField* GetFirstFormField() const;
   CPDF_FormControl* GetSmartFieldControl(CPDF_FormField* pFormField);
 
+  void SetDelay(bool bDelay);
   void AddDelay_Int(FIELD_PROP prop, int32_t n);
   void AddDelay_Bool(FIELD_PROP prop, bool b);
   void AddDelay_String(FIELD_PROP prop, const ByteString& str);
@@ -361,7 +361,6 @@
   void AddDelay_WordArray(FIELD_PROP prop, const std::vector<uint32_t>& array);
   void AddDelay_WideStringArray(FIELD_PROP prop,
                                 const std::vector<WideString>& array);
-
   void DoDelay();
 
   ObservedPtr<CJS_Document> m_pJSDoc;
diff --git a/fxjs/cjs_font.cpp b/fxjs/cjs_font.cpp
index 0e6e94b..fcf69ac 100644
--- a/fxjs/cjs_font.cpp
+++ b/fxjs/cjs_font.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,7 +22,7 @@
     {"Symbol", JSConstSpec::String, 0, "Symbol"},
     {"ZapfD", JSConstSpec::String, 0, "ZapfDingbats"}};
 
-int CJS_Font::ObjDefnID = -1;
+uint32_t CJS_Font::ObjDefnID = 0;
 
 // static
 void CJS_Font::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_font.h b/fxjs/cjs_font.h
index d3cf159..a4ecca1 100644
--- a/fxjs/cjs_font.h
+++ b/fxjs/cjs_font.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_Font() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/cjs_global.cpp b/fxjs/cjs_global.cpp
index 8ac3181..5582682 100644
--- a/fxjs/cjs_global.cpp
+++ b/fxjs/cjs_global.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,6 @@
 
 #include "fxjs/cjs_global.h"
 
-#include <map>
 #include <memory>
 #include <utility>
 #include <vector>
@@ -15,110 +14,20 @@
 #include "fxjs/cfx_globaldata.h"
 #include "fxjs/cfx_keyvalue.h"
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_object.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_define.h"
 #include "fxjs/js_resources.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+#include "v8/include/v8-isolate.h"
 
 namespace {
 
-WideString PropFromV8Prop(v8::Isolate* pIsolate,
-                          v8::Local<v8::String> property) {
-  v8::String::Utf8Value utf8_value(pIsolate, property);
-  return WideString::FromUTF8(ByteStringView(*utf8_value, utf8_value.length()));
-}
-
-template <class Alt>
-void JSSpecialPropQuery(const char*,
-                        v8::Local<v8::String> property,
-                        const v8::PropertyCallbackInfo<v8::Integer>& info) {
-  auto pObj = JSGetObject<Alt>(info.Holder());
-  if (!pObj)
-    return;
-
-  CJS_Runtime* pRuntime = pObj->GetRuntime();
-  if (!pRuntime)
-    return;
-
-  CJS_Result result =
-      pObj->QueryProperty(PropFromV8Prop(info.GetIsolate(), property).c_str());
-
-  info.GetReturnValue().Set(!result.HasError() ? 4 : 0);
-}
-
-template <class Alt>
-void JSSpecialPropGet(const char* class_name,
-                      v8::Local<v8::String> property,
-                      const v8::PropertyCallbackInfo<v8::Value>& info) {
-  auto pObj = JSGetObject<Alt>(info.Holder());
-  if (!pObj)
-    return;
-
-  CJS_Runtime* pRuntime = pObj->GetRuntime();
-  if (!pRuntime)
-    return;
-
-  CJS_Result result = pObj->GetProperty(
-      pRuntime, PropFromV8Prop(info.GetIsolate(), property).c_str());
-
-  if (result.HasError()) {
-    pRuntime->Error(
-        JSFormatErrorString(class_name, "GetProperty", result.Error()));
-    return;
-  }
-  if (result.HasReturn())
-    info.GetReturnValue().Set(result.Return());
-}
-
-template <class Alt>
-void JSSpecialPropPut(const char* class_name,
-                      v8::Local<v8::String> property,
-                      v8::Local<v8::Value> value,
-                      const v8::PropertyCallbackInfo<v8::Value>& info) {
-  auto pObj = JSGetObject<Alt>(info.Holder());
-  if (!pObj)
-    return;
-
-  CJS_Runtime* pRuntime = pObj->GetRuntime();
-  if (!pRuntime)
-    return;
-
-  CJS_Result result = pObj->SetProperty(
-      pRuntime, PropFromV8Prop(info.GetIsolate(), property).c_str(), value);
-
-  if (result.HasError()) {
-    pRuntime->Error(
-        JSFormatErrorString(class_name, "PutProperty", result.Error()));
-  }
-}
-
-template <class Alt>
-void JSSpecialPropDel(const char* class_name,
-                      v8::Local<v8::String> property,
-                      const v8::PropertyCallbackInfo<v8::Boolean>& info) {
-  auto pObj = JSGetObject<Alt>(info.Holder());
-  if (!pObj)
-    return;
-
-  CJS_Runtime* pRuntime = pObj->GetRuntime();
-  if (!pRuntime)
-    return;
-
-  CJS_Result result = pObj->DelProperty(
-      pRuntime, PropFromV8Prop(info.GetIsolate(), property).c_str());
-  if (result.HasError()) {
-    // TODO(dsinclair): Should this set the pRuntime->Error result?
-    // ByteString cbName =
-    //     ByteString::Format("%s.%s", class_name, "DelProperty");
-  }
-}
-
-template <class T>
-v8::Local<v8::String> GetV8StringFromProperty(v8::Local<v8::Name> property,
-                                              const T& info) {
-  return property->ToString(info.GetIsolate()->GetCurrentContext())
-      .ToLocalChecked();
+ByteString ByteStringFromV8Name(v8::Isolate* pIsolate,
+                                v8::Local<v8::Name> name) {
+  CHECK(name->IsString());
+  return fxv8::ToByteString(pIsolate, name.As<v8::String>());
 }
 
 }  // namespace
@@ -130,7 +39,7 @@
 const JSMethodSpec CJS_Global::MethodSpecs[] = {
     {"setPersistent", setPersistent_static}};
 
-int CJS_Global::ObjDefnID = -1;
+uint32_t CJS_Global::ObjDefnID = 0;
 
 // static
 void CJS_Global::setPersistent_static(
@@ -143,24 +52,36 @@
 void CJS_Global::queryprop_static(
     v8::Local<v8::Name> property,
     const v8::PropertyCallbackInfo<v8::Integer>& info) {
-  ASSERT(property->IsString());
-  JSSpecialPropQuery<CJS_Global>(
-      "global",
-      v8::Local<v8::String>::New(info.GetIsolate(),
-                                 GetV8StringFromProperty(property, info)),
-      info);
+  auto pObj = JSGetObject<CJS_Global>(info.GetIsolate(), info.Holder());
+  if (!pObj)
+    return;
+
+  ByteString bsProp = ByteStringFromV8Name(info.GetIsolate(), property);
+  if (pObj->HasProperty(bsProp))
+    info.GetReturnValue().Set(static_cast<int>(v8::PropertyAttribute::None));
 }
 
 // static
 void CJS_Global::getprop_static(
     v8::Local<v8::Name> property,
     const v8::PropertyCallbackInfo<v8::Value>& info) {
-  ASSERT(property->IsString());
-  JSSpecialPropGet<CJS_Global>(
-      "global",
-      v8::Local<v8::String>::New(info.GetIsolate(),
-                                 GetV8StringFromProperty(property, info)),
-      info);
+  auto pObj = JSGetObject<CJS_Global>(info.GetIsolate(), info.Holder());
+  if (!pObj)
+    return;
+
+  CJS_Runtime* pRuntime = pObj->GetRuntime();
+  if (!pRuntime)
+    return;
+
+  ByteString bsProp = ByteStringFromV8Name(info.GetIsolate(), property);
+  CJS_Result result = pObj->GetProperty(pRuntime, bsProp);
+  if (result.HasError()) {
+    pRuntime->Error(
+        JSFormatErrorString("global", "GetProperty", result.Error()));
+    return;
+  }
+  if (result.HasReturn())
+    info.GetReturnValue().Set(result.Return());
 }
 
 // static
@@ -168,35 +89,60 @@
     v8::Local<v8::Name> property,
     v8::Local<v8::Value> value,
     const v8::PropertyCallbackInfo<v8::Value>& info) {
-  ASSERT(property->IsString());
-  JSSpecialPropPut<CJS_Global>(
-      "global",
-      v8::Local<v8::String>::New(info.GetIsolate(),
-                                 GetV8StringFromProperty(property, info)),
-      value, info);
+  auto pObj = JSGetObject<CJS_Global>(info.GetIsolate(), info.Holder());
+  if (!pObj)
+    return;
+
+  CJS_Runtime* pRuntime = pObj->GetRuntime();
+  if (!pRuntime)
+    return;
+
+  ByteString bsProp = ByteStringFromV8Name(info.GetIsolate(), property);
+  CJS_Result result = pObj->SetProperty(pRuntime, bsProp, value);
+  if (result.HasError()) {
+    pRuntime->Error(
+        JSFormatErrorString("global", "PutProperty", result.Error()));
+    return;
+  }
+  info.GetReturnValue().Set(value);
 }
 
 // static
 void CJS_Global::delprop_static(
     v8::Local<v8::Name> property,
     const v8::PropertyCallbackInfo<v8::Boolean>& info) {
-  ASSERT(property->IsString());
-  JSSpecialPropDel<CJS_Global>(
-      "global",
-      v8::Local<v8::String>::New(info.GetIsolate(),
-                                 GetV8StringFromProperty(property, info)),
-      info);
+  auto pObj = JSGetObject<CJS_Global>(info.GetIsolate(), info.Holder());
+  if (!pObj)
+    return;
+
+  ByteString bsProp = ByteStringFromV8Name(info.GetIsolate(), property);
+  if (pObj->DelProperty(bsProp))
+    info.GetReturnValue().Set(true);
+}
+
+void CJS_Global::enumprop_static(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  auto pObj = JSGetObject<CJS_Global>(info.GetIsolate(), info.Holder());
+  if (!pObj)
+    return;
+
+  CJS_Runtime* pRuntime = pObj->GetRuntime();
+  if (!pRuntime)
+    return;
+
+  pObj->EnumProperties(pRuntime, info);
 }
 
 // static
 void CJS_Global::DefineAllProperties(CFXJS_Engine* pEngine) {
   pEngine->DefineObjAllProperties(
       ObjDefnID, CJS_Global::queryprop_static, CJS_Global::getprop_static,
-      CJS_Global::putprop_static, CJS_Global::delprop_static);
+      CJS_Global::putprop_static, CJS_Global::delprop_static,
+      CJS_Global::enumprop_static);
 }
 
 // static
-int CJS_Global::GetObjDefnID() {
+uint32_t CJS_Global::GetObjDefnID() {
   return ObjDefnID;
 }
 
@@ -209,38 +155,32 @@
 }
 
 CJS_Global::CJS_Global(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
-    : CJS_Object(pObject, pRuntime) {
-  CPDFSDK_FormFillEnvironment* pFormFillEnv = GetRuntime()->GetFormFillEnv();
-  m_pFormFillEnv.Reset(pFormFillEnv);
-  m_pGlobalData = CFX_GlobalData::GetRetainedInstance(nullptr);
+    : CJS_Object(pObject, pRuntime),
+      m_pGlobalData(CFX_GlobalData::GetRetainedInstance(nullptr)) {
   UpdateGlobalPersistentVariables();
 }
 
 CJS_Global::~CJS_Global() {
   DestroyGlobalPersisitentVariables();
-  m_pGlobalData->Release();
+  m_pGlobalData.ExtractAsDangling()->Release();
 }
 
-CJS_Result CJS_Global::QueryProperty(const wchar_t* propname) {
-  if (WideString(propname).EqualsASCII("setPersistent"))
-    return CJS_Result::Success();
-
-  return CJS_Result::Failure(JSMessage::kUnknownProperty);
+bool CJS_Global::HasProperty(const ByteString& propname) {
+  return pdfium::Contains(m_MapGlobal, propname);
 }
 
-CJS_Result CJS_Global::DelProperty(CJS_Runtime* pRuntime,
-                                   const wchar_t* propname) {
-  auto it = m_MapGlobal.find(WideString(propname).ToDefANSI());
+bool CJS_Global::DelProperty(const ByteString& propname) {
+  auto it = m_MapGlobal.find(propname);
   if (it == m_MapGlobal.end())
-    return CJS_Result::Failure(JSMessage::kUnknownProperty);
+    return false;
 
   it->second->bDeleted = true;
-  return CJS_Result::Success();
+  return true;
 }
 
 CJS_Result CJS_Global::GetProperty(CJS_Runtime* pRuntime,
-                                   const wchar_t* propname) {
-  auto it = m_MapGlobal.find(WideString(propname).ToDefANSI());
+                                   const ByteString& propname) {
+  auto it = m_MapGlobal.find(propname);
   if (it == m_MapGlobal.end())
     return CJS_Result::Success();
 
@@ -249,65 +189,76 @@
     return CJS_Result::Success();
 
   switch (pData->nType) {
-    case CFX_Value::DataType::NUMBER:
+    case CFX_Value::DataType::kNumber:
       return CJS_Result::Success(pRuntime->NewNumber(pData->dData));
-    case CFX_Value::DataType::BOOLEAN:
+    case CFX_Value::DataType::kBoolean:
       return CJS_Result::Success(pRuntime->NewBoolean(pData->bData));
-    case CFX_Value::DataType::STRING:
-      return CJS_Result::Success(pRuntime->NewString(
-          WideString::FromDefANSI(pData->sData.AsStringView()).AsStringView()));
-    case CFX_Value::DataType::OBJECT:
+    case CFX_Value::DataType::kString:
+      return CJS_Result::Success(
+          pRuntime->NewString(pData->sData.AsStringView()));
+    case CFX_Value::DataType::kObject:
       return CJS_Result::Success(
           v8::Local<v8::Object>::New(pRuntime->GetIsolate(), pData->pData));
-    case CFX_Value::DataType::NULLOBJ:
+    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,
-                                   const wchar_t* propname,
+                                   const ByteString& propname,
                                    v8::Local<v8::Value> vp) {
-  ByteString sPropName = WideString(propname).ToDefANSI();
   if (vp->IsNumber()) {
-    return SetGlobalVariables(sPropName, CFX_Value::DataType::NUMBER,
+    return SetGlobalVariables(propname, CFX_Value::DataType::kNumber,
                               pRuntime->ToDouble(vp), false, ByteString(),
                               v8::Local<v8::Object>(), false);
   }
   if (vp->IsBoolean()) {
-    return SetGlobalVariables(sPropName, CFX_Value::DataType::BOOLEAN, 0,
+    return SetGlobalVariables(propname, CFX_Value::DataType::kBoolean, 0,
                               pRuntime->ToBoolean(vp), ByteString(),
                               v8::Local<v8::Object>(), false);
   }
   if (vp->IsString()) {
-    return SetGlobalVariables(sPropName, CFX_Value::DataType::STRING, 0, false,
-                              pRuntime->ToWideString(vp).ToDefANSI(),
+    return SetGlobalVariables(propname, CFX_Value::DataType::kString, 0, false,
+                              pRuntime->ToByteString(vp),
                               v8::Local<v8::Object>(), false);
   }
   if (vp->IsObject()) {
-    return SetGlobalVariables(sPropName, CFX_Value::DataType::OBJECT, 0, false,
+    return SetGlobalVariables(propname, CFX_Value::DataType::kObject, 0, false,
                               ByteString(), pRuntime->ToObject(vp), false);
   }
   if (vp->IsNull()) {
-    return SetGlobalVariables(sPropName, CFX_Value::DataType::NULLOBJ, 0, false,
+    return SetGlobalVariables(propname, CFX_Value::DataType::kNull, 0, false,
                               ByteString(), v8::Local<v8::Object>(), false);
   }
   if (vp->IsUndefined()) {
-    DelProperty(pRuntime, propname);
+    DelProperty(propname);
     return CJS_Result::Success();
   }
   return CJS_Result::Failure(JSMessage::kObjectTypeError);
 }
 
+void CJS_Global::EnumProperties(
+    CJS_Runtime* pRuntime,
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Local<v8::Array> result = pRuntime->NewArray();
+  int idx = 0;
+  for (const auto& it : m_MapGlobal) {
+    if (it.second->bDeleted)
+      continue;
+    v8::Local<v8::Name> name = pRuntime->NewString(it.first.AsStringView());
+    pRuntime->PutArrayElement(result, idx, name);
+    ++idx;
+  }
+  info.GetReturnValue().Set(result);
+}
+
 CJS_Result CJS_Global::setPersistent(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  auto it = m_MapGlobal.find(pRuntime->ToWideString(params[0]).ToDefANSI());
+  auto it = m_MapGlobal.find(pRuntime->ToByteString(params[0]));
   if (it == m_MapGlobal.end() || it->second->bDeleted)
     return CJS_Result::Failure(JSMessage::kGlobalNotFoundError);
 
@@ -323,47 +274,44 @@
   for (int i = 0, sz = m_pGlobalData->GetSize(); i < sz; i++) {
     CFX_GlobalData::Element* pData = m_pGlobalData->GetAt(i);
     switch (pData->data.nType) {
-      case CFX_Value::DataType::NUMBER:
-        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::NUMBER,
+      case CFX_Value::DataType::kNumber:
+        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kNumber,
                            pData->data.dData, false, ByteString(),
-                           v8::Local<v8::Object>(), pData->bPersistent == 1);
+                           v8::Local<v8::Object>(), pData->bPersistent);
         pRuntime->PutObjectProperty(ToV8Object(),
                                     pData->data.sKey.AsStringView(),
                                     pRuntime->NewNumber(pData->data.dData));
         break;
-      case CFX_Value::DataType::BOOLEAN:
-        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::BOOLEAN, 0,
+      case CFX_Value::DataType::kBoolean:
+        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kBoolean, 0,
                            pData->data.bData == 1, ByteString(),
-                           v8::Local<v8::Object>(), pData->bPersistent == 1);
+                           v8::Local<v8::Object>(), pData->bPersistent);
         pRuntime->PutObjectProperty(
             ToV8Object(), pData->data.sKey.AsStringView(),
             pRuntime->NewBoolean(pData->data.bData == 1));
         break;
-      case CFX_Value::DataType::STRING:
-        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::STRING, 0,
+      case CFX_Value::DataType::kString:
+        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kString, 0,
                            false, pData->data.sData, v8::Local<v8::Object>(),
-                           pData->bPersistent == 1);
+                           pData->bPersistent);
         pRuntime->PutObjectProperty(
             ToV8Object(), pData->data.sKey.AsStringView(),
-            pRuntime->NewString(
-                WideString::FromUTF8(pData->data.sData.AsStringView())
-                    .AsStringView()));
+            pRuntime->NewString(pData->data.sData.AsStringView()));
         break;
-      case CFX_Value::DataType::OBJECT: {
+      case CFX_Value::DataType::kObject: {
         v8::Local<v8::Object> pObj = pRuntime->NewObject();
         if (!pObj.IsEmpty()) {
           PutObjectProperty(pObj, &pData->data);
-          SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::OBJECT, 0,
-                             false, ByteString(), pObj,
-                             pData->bPersistent == 1);
+          SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kObject, 0,
+                             false, ByteString(), pObj, pData->bPersistent);
           pRuntime->PutObjectProperty(ToV8Object(),
                                       pData->data.sKey.AsStringView(), pObj);
         }
       } break;
-      case CFX_Value::DataType::NULLOBJ:
-        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::NULLOBJ, 0,
+      case CFX_Value::DataType::kNull:
+        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kNull, 0,
                            false, ByteString(), v8::Local<v8::Object>(),
-                           pData->bPersistent == 1);
+                           pData->bPersistent);
         pRuntime->PutObjectProperty(
             ToV8Object(), pData->data.sKey.AsStringView(), pRuntime->NewNull());
         break;
@@ -371,7 +319,11 @@
   }
 }
 
-void CJS_Global::CommitGlobalPersisitentVariables(CJS_Runtime* pRuntime) {
+void CJS_Global::CommitGlobalPersisitentVariables() {
+  CJS_Runtime* pRuntime = GetRuntime();
+  if (!pRuntime)
+    return;
+
   for (const auto& iter : m_MapGlobal) {
     ByteString name = iter.first;
     JSGlobalData* pData = iter.second.get();
@@ -380,27 +332,26 @@
       continue;
     }
     switch (pData->nType) {
-      case CFX_Value::DataType::NUMBER:
+      case CFX_Value::DataType::kNumber:
         m_pGlobalData->SetGlobalVariableNumber(name, pData->dData);
         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
         break;
-      case CFX_Value::DataType::BOOLEAN:
+      case CFX_Value::DataType::kBoolean:
         m_pGlobalData->SetGlobalVariableBoolean(name, pData->bData);
         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
         break;
-      case CFX_Value::DataType::STRING:
+      case CFX_Value::DataType::kString:
         m_pGlobalData->SetGlobalVariableString(name, pData->sData);
         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
         break;
-      case CFX_Value::DataType::OBJECT: {
-        std::vector<std::unique_ptr<CFX_KeyValue>> array;
+      case CFX_Value::DataType::kObject: {
         v8::Local<v8::Object> obj =
-            v8::Local<v8::Object>::New(GetIsolate(), pData->pData);
-        ObjectToArray(pRuntime, obj, &array);
-        m_pGlobalData->SetGlobalVariableObject(name, std::move(array));
+            v8::Local<v8::Object>::New(pRuntime->GetIsolate(), pData->pData);
+        m_pGlobalData->SetGlobalVariableObject(name,
+                                               ObjectToArray(pRuntime, obj));
         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
       } break;
-      case CFX_Value::DataType::NULLOBJ:
+      case CFX_Value::DataType::kNull:
         m_pGlobalData->SetGlobalVariableNull(name);
         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
         break;
@@ -408,55 +359,56 @@
   }
 }
 
-void CJS_Global::ObjectToArray(
+std::vector<std::unique_ptr<CFX_KeyValue>> CJS_Global::ObjectToArray(
     CJS_Runtime* pRuntime,
-    v8::Local<v8::Object> pObj,
-    std::vector<std::unique_ptr<CFX_KeyValue>>* pArray) {
+    v8::Local<v8::Object> pObj) {
+  std::vector<std::unique_ptr<CFX_KeyValue>> array;
   std::vector<WideString> pKeyList = pRuntime->GetObjectPropertyNames(pObj);
   for (const auto& ws : pKeyList) {
     ByteString sKey = ws.ToUTF8();
     v8::Local<v8::Value> v =
         pRuntime->GetObjectProperty(pObj, sKey.AsStringView());
     if (v->IsNumber()) {
-      auto pObjElement = pdfium::MakeUnique<CFX_KeyValue>();
-      pObjElement->nType = CFX_Value::DataType::NUMBER;
+      auto pObjElement = std::make_unique<CFX_KeyValue>();
+      pObjElement->nType = CFX_Value::DataType::kNumber;
       pObjElement->sKey = sKey;
       pObjElement->dData = pRuntime->ToDouble(v);
-      pArray->push_back(std::move(pObjElement));
+      array.push_back(std::move(pObjElement));
       continue;
     }
     if (v->IsBoolean()) {
-      auto pObjElement = pdfium::MakeUnique<CFX_KeyValue>();
-      pObjElement->nType = CFX_Value::DataType::BOOLEAN;
+      auto pObjElement = std::make_unique<CFX_KeyValue>();
+      pObjElement->nType = CFX_Value::DataType::kBoolean;
       pObjElement->sKey = sKey;
       pObjElement->dData = pRuntime->ToBoolean(v);
-      pArray->push_back(std::move(pObjElement));
+      array.push_back(std::move(pObjElement));
       continue;
     }
     if (v->IsString()) {
-      ByteString sValue = pRuntime->ToWideString(v).ToDefANSI();
-      auto pObjElement = pdfium::MakeUnique<CFX_KeyValue>();
-      pObjElement->nType = CFX_Value::DataType::STRING;
+      ByteString sValue = pRuntime->ToByteString(v);
+      auto pObjElement = std::make_unique<CFX_KeyValue>();
+      pObjElement->nType = CFX_Value::DataType::kString;
       pObjElement->sKey = sKey;
       pObjElement->sData = sValue;
-      pArray->push_back(std::move(pObjElement));
+      array.push_back(std::move(pObjElement));
       continue;
     }
     if (v->IsObject()) {
-      auto pObjElement = pdfium::MakeUnique<CFX_KeyValue>();
-      pObjElement->nType = CFX_Value::DataType::OBJECT;
+      auto pObjElement = std::make_unique<CFX_KeyValue>();
+      pObjElement->nType = CFX_Value::DataType::kObject;
       pObjElement->sKey = sKey;
-      ObjectToArray(pRuntime, pRuntime->ToObject(v), &pObjElement->objData);
-      pArray->push_back(std::move(pObjElement));
+      pObjElement->objData = ObjectToArray(pRuntime, pRuntime->ToObject(v));
+      array.push_back(std::move(pObjElement));
       continue;
     }
     if (v->IsNull()) {
-      auto pObjElement = pdfium::MakeUnique<CFX_KeyValue>();
-      pObjElement->nType = CFX_Value::DataType::NULLOBJ;
+      auto pObjElement = std::make_unique<CFX_KeyValue>();
+      pObjElement->nType = CFX_Value::DataType::kNull;
       pObjElement->sKey = sKey;
-      pArray->push_back(std::move(pObjElement));
+      array.push_back(std::move(pObjElement));
     }
   }
+  return array;
 }
 
 void CJS_Global::PutObjectProperty(v8::Local<v8::Object> pObj,
@@ -468,22 +420,20 @@
   for (size_t i = 0; i < pData->objData.size(); ++i) {
     CFX_KeyValue* pObjData = pData->objData.at(i).get();
     switch (pObjData->nType) {
-      case CFX_Value::DataType::NUMBER:
+      case CFX_Value::DataType::kNumber:
         pRuntime->PutObjectProperty(pObj, pObjData->sKey.AsStringView(),
                                     pRuntime->NewNumber(pObjData->dData));
         break;
-      case CFX_Value::DataType::BOOLEAN:
+      case CFX_Value::DataType::kBoolean:
         pRuntime->PutObjectProperty(pObj, pObjData->sKey.AsStringView(),
                                     pRuntime->NewBoolean(pObjData->bData == 1));
         break;
-      case CFX_Value::DataType::STRING:
+      case CFX_Value::DataType::kString:
         pRuntime->PutObjectProperty(
             pObj, pObjData->sKey.AsStringView(),
-            pRuntime->NewString(
-                WideString::FromUTF8(pObjData->sData.AsStringView())
-                    .AsStringView()));
+            pRuntime->NewString(pObjData->sData.AsStringView()));
         break;
-      case CFX_Value::DataType::OBJECT: {
+      case CFX_Value::DataType::kObject: {
         v8::Local<v8::Object> pNewObj = pRuntime->NewObject();
         if (!pNewObj.IsEmpty()) {
           PutObjectProperty(pNewObj, pObjData);
@@ -491,7 +441,7 @@
                                       pNewObj);
         }
       } break;
-      case CFX_Value::DataType::NULLOBJ:
+      case CFX_Value::DataType::kNull:
         pRuntime->PutObjectProperty(pObj, pObjData->sKey.AsStringView(),
                                     pRuntime->NewNull());
         break;
@@ -518,60 +468,56 @@
     JSGlobalData* pTemp = it->second.get();
     if (pTemp->bDeleted || pTemp->nType != nType) {
       pTemp->dData = 0;
-      pTemp->bData = 0;
+      pTemp->bData = false;
       pTemp->sData.clear();
       pTemp->nType = nType;
     }
     pTemp->bDeleted = false;
     switch (nType) {
-      case CFX_Value::DataType::NUMBER:
+      case CFX_Value::DataType::kNumber:
         pTemp->dData = dData;
         break;
-      case CFX_Value::DataType::BOOLEAN:
+      case CFX_Value::DataType::kBoolean:
         pTemp->bData = bData;
         break;
-      case CFX_Value::DataType::STRING:
+      case CFX_Value::DataType::kString:
         pTemp->sData = sData;
         break;
-      case CFX_Value::DataType::OBJECT:
+      case CFX_Value::DataType::kObject:
         pTemp->pData.Reset(pData->GetIsolate(), pData);
         break;
-      case CFX_Value::DataType::NULLOBJ:
+      case CFX_Value::DataType::kNull:
         break;
-      default:
-        return CJS_Result::Failure(JSMessage::kObjectTypeError);
     }
     return CJS_Result::Success();
   }
 
-  auto pNewData = pdfium::MakeUnique<JSGlobalData>();
+  auto pNewData = std::make_unique<JSGlobalData>();
   switch (nType) {
-    case CFX_Value::DataType::NUMBER:
-      pNewData->nType = CFX_Value::DataType::NUMBER;
+    case CFX_Value::DataType::kNumber:
+      pNewData->nType = CFX_Value::DataType::kNumber;
       pNewData->dData = dData;
       pNewData->bPersistent = bDefaultPersistent;
       break;
-    case CFX_Value::DataType::BOOLEAN:
-      pNewData->nType = CFX_Value::DataType::BOOLEAN;
+    case CFX_Value::DataType::kBoolean:
+      pNewData->nType = CFX_Value::DataType::kBoolean;
       pNewData->bData = bData;
       pNewData->bPersistent = bDefaultPersistent;
       break;
-    case CFX_Value::DataType::STRING:
-      pNewData->nType = CFX_Value::DataType::STRING;
+    case CFX_Value::DataType::kString:
+      pNewData->nType = CFX_Value::DataType::kString;
       pNewData->sData = sData;
       pNewData->bPersistent = bDefaultPersistent;
       break;
-    case CFX_Value::DataType::OBJECT:
-      pNewData->nType = CFX_Value::DataType::OBJECT;
+    case CFX_Value::DataType::kObject:
+      pNewData->nType = CFX_Value::DataType::kObject;
       pNewData->pData.Reset(pData->GetIsolate(), pData);
       pNewData->bPersistent = bDefaultPersistent;
       break;
-    case CFX_Value::DataType::NULLOBJ:
-      pNewData->nType = CFX_Value::DataType::NULLOBJ;
+    case CFX_Value::DataType::kNull:
+      pNewData->nType = CFX_Value::DataType::kNull;
       pNewData->bPersistent = bDefaultPersistent;
       break;
-    default:
-      return CJS_Result::Failure(JSMessage::kObjectTypeError);
   }
   m_MapGlobal[propname] = std::move(pNewData);
   return CJS_Result::Success();
diff --git a/fxjs/cjs_global.h b/fxjs/cjs_global.h
index 2c89188..944c030 100644
--- a/fxjs/cjs_global.h
+++ b/fxjs/cjs_global.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,15 +11,26 @@
 #include <memory>
 #include <vector>
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "fxjs/cfx_keyvalue.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/cjs_result.h"
 
 class CFX_GlobalData;
 
+// The CJS_Global object is not the V8 global object (i.e. it is not |this|
+// in JavaScript outside of a bound function call). It is a facility for
+// sharing data amongst documents and persisting data within a document
+// between sessions. It is only partially implemented due to security and
+// privacy concerns. It provides access via properties in the usual manner,
+// execpt that these are stored on the C++ side rather than in V8 itself.
+// It is a static object that is available as "global" property of the V8
+// global object and can be manipulated from JavaScript as |global['foo']|
+// for example.
+
 class CJS_Global final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
   static void DefineAllProperties(CFXJS_Engine* pEngine);
 
@@ -33,6 +44,7 @@
                              const v8::PropertyCallbackInfo<v8::Value>& info);
   static void delprop_static(v8::Local<v8::Name> property,
                              const v8::PropertyCallbackInfo<v8::Boolean>& info);
+  static void enumprop_static(const v8::PropertyCallbackInfo<v8::Array>& info);
 
   static void setPersistent_static(
       const v8::FunctionCallbackInfo<v8::Value>& info);
@@ -40,16 +52,6 @@
   CJS_Global(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
   ~CJS_Global() override;
 
-  CJS_Result DelProperty(CJS_Runtime* pRuntime, const wchar_t* propname);
-
-  CJS_Result setPersistent(CJS_Runtime* pRuntime,
-                           const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Result QueryProperty(const wchar_t* propname);
-  CJS_Result GetProperty(CJS_Runtime* pRuntime, const wchar_t* propname);
-  CJS_Result SetProperty(CJS_Runtime* pRuntime,
-                         const wchar_t* propname,
-                         v8::Local<v8::Value> vp);
-
  private:
   struct JSGlobalData : public CFX_Value {
    public:
@@ -61,11 +63,12 @@
     bool bDeleted = false;
   };
 
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSMethodSpec MethodSpecs[];
 
   void UpdateGlobalPersistentVariables();
-  void CommitGlobalPersisitentVariables(CJS_Runtime* pRuntime);
+  // TODO(crbug.com/pdfium/926): This method is never called.
+  void CommitGlobalPersisitentVariables();
   void DestroyGlobalPersisitentVariables();
   CJS_Result SetGlobalVariables(const ByteString& propname,
                                 CFX_Value::DataType nType,
@@ -74,15 +77,23 @@
                                 const ByteString& sData,
                                 v8::Local<v8::Object> pData,
                                 bool bDefaultPersistent);
-  void ObjectToArray(CJS_Runtime* pRuntime,
-                     v8::Local<v8::Object> pObj,
-                     std::vector<std::unique_ptr<CFX_KeyValue>>* pArray);
+  std::vector<std::unique_ptr<CFX_KeyValue>> ObjectToArray(
+      CJS_Runtime* pRuntime,
+      v8::Local<v8::Object> pObj);
   void PutObjectProperty(v8::Local<v8::Object> obj, CFX_KeyValue* pData);
+  CJS_Result setPersistent(CJS_Runtime* pRuntime,
+                           const std::vector<v8::Local<v8::Value>>& params);
+  bool HasProperty(const ByteString& propname);
+  bool DelProperty(const ByteString& propname);
+  CJS_Result GetProperty(CJS_Runtime* pRuntime, const ByteString& propname);
+  CJS_Result SetProperty(CJS_Runtime* pRuntime,
+                         const ByteString& propname,
+                         v8::Local<v8::Value> vp);
+  void EnumProperties(CJS_Runtime* pRuntime,
+                      const v8::PropertyCallbackInfo<v8::Array>& info);
 
   std::map<ByteString, std::unique_ptr<JSGlobalData>> m_MapGlobal;
-  WideString m_sFilePath;
-  CFX_GlobalData* m_pGlobalData;
-  ObservedPtr<CPDFSDK_FormFillEnvironment> m_pFormFillEnv;
+  UnownedPtr<CFX_GlobalData> m_pGlobalData;
 };
 
 #endif  // FXJS_CJS_GLOBAL_H_
diff --git a/fxjs/cjs_globalarrays.cpp b/fxjs/cjs_globalarrays.cpp
index 9cf540e..3dbf62c 100644
--- a/fxjs/cjs_globalarrays.cpp
+++ b/fxjs/cjs_globalarrays.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,21 +6,32 @@
 
 #include "fxjs/cjs_globalarrays.h"
 
-#define GLOBAL_ARRAY(rt, name, ...)                                          \
-  {                                                                          \
-    static const wchar_t* const values[] = {__VA_ARGS__};                    \
-    v8::Local<v8::Array> array = (rt)->NewArray();                           \
-    v8::Local<v8::Context> ctx = (rt)->GetIsolate()->GetCurrentContext();    \
-    for (size_t i = 0; i < FX_ArraySize(values); ++i)                        \
-      array->Set(ctx, i, (rt)->NewString(values[i])).FromJust();             \
-    (rt)->SetConstArray((name), array);                                      \
-    (rt)->DefineGlobalConst(                                                 \
-        (name), [](const v8::FunctionCallbackInfo<v8::Value>& info) {        \
-          CJS_Object* pObj = CFXJS_Engine::GetObjectPrivate(info.Holder());  \
-          CJS_Runtime* pCurrentRuntime = pObj->GetRuntime();                 \
-          if (pCurrentRuntime)                                               \
-            info.GetReturnValue().Set(pCurrentRuntime->GetConstArray(name)); \
-        });                                                                  \
+#include <iterator>
+
+#include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/v8-container.h"
+#include "v8/include/v8-isolate.h"
+
+#define GLOBAL_ARRAY(rt, name, ...)                                            \
+  {                                                                            \
+    static const wchar_t* const values[] = {__VA_ARGS__};                      \
+    v8::Local<v8::Array> array = (rt)->NewArray();                             \
+    v8::Local<v8::Context> ctx = (rt)->GetIsolate()->GetCurrentContext();      \
+    for (size_t i = 0; i < std::size(values); ++i) {                           \
+      array                                                                    \
+          ->Set(ctx, pdfium::base::checked_cast<uint32_t>(i),                  \
+                (rt)->NewString(values[i]))                                    \
+          .FromJust();                                                         \
+    }                                                                          \
+    (rt)->SetConstArray((name), array);                                        \
+    (rt)->DefineGlobalConst(                                                   \
+        (name), [](const v8::FunctionCallbackInfo<v8::Value>& info) {          \
+          CJS_Object* pObj = CFXJS_Engine::GetObjectPrivate(info.GetIsolate(), \
+                                                            info.Holder());    \
+          CJS_Runtime* pCurrentRuntime = pObj->GetRuntime();                   \
+          if (pCurrentRuntime)                                                 \
+            info.GetReturnValue().Set(pCurrentRuntime->GetConstArray(name));   \
+        });                                                                    \
   }
 
 // static
diff --git a/fxjs/cjs_globalarrays.h b/fxjs/cjs_globalarrays.h
index 244db2e..726e3a7 100644
--- a/fxjs/cjs_globalarrays.h
+++ b/fxjs/cjs_globalarrays.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxjs/cjs_globalconsts.cpp b/fxjs/cjs_globalconsts.cpp
index cb6bd33..854ded8 100644
--- a/fxjs/cjs_globalconsts.cpp
+++ b/fxjs/cjs_globalconsts.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,14 +6,13 @@
 
 #include "fxjs/cjs_globalconsts.h"
 
-#define GLOBAL_STRING(rt, name, value)                                        \
-  (rt)->DefineGlobalConst(                                                    \
-      (name), [](const v8::FunctionCallbackInfo<v8::Value>& info) {           \
-        const char* pStr = (value);                                           \
-        info.GetReturnValue().Set(                                            \
-            v8::String::NewFromUtf8(info.GetIsolate(), pStr,                  \
-                                    v8::NewStringType::kNormal, strlen(pStr)) \
-                .ToLocalChecked());                                           \
+#include "fxjs/fxv8.h"
+
+#define GLOBAL_STRING(rt, name, value)                              \
+  (rt)->DefineGlobalConst(                                          \
+      (name), [](const v8::FunctionCallbackInfo<v8::Value>& info) { \
+        info.GetReturnValue().Set(                                  \
+            fxv8::NewStringHelper(info.GetIsolate(), (value)));     \
       })
 
 // static
diff --git a/fxjs/cjs_globalconsts.h b/fxjs/cjs_globalconsts.h
index 71c689b..f553f3e 100644
--- a/fxjs/cjs_globalconsts.h
+++ b/fxjs/cjs_globalconsts.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxjs/cjs_highlight.cpp b/fxjs/cjs_highlight.cpp
index 4ada397..174685d 100644
--- a/fxjs/cjs_highlight.cpp
+++ b/fxjs/cjs_highlight.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,7 @@
     {"p", JSConstSpec::String, 0, "push"},
     {"o", JSConstSpec::String, 0, "outline"}};
 
-int CJS_Highlight::ObjDefnID = -1;
+uint32_t CJS_Highlight::ObjDefnID = 0;
 
 // static
 void CJS_Highlight::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_highlight.h b/fxjs/cjs_highlight.h
index efec127..ad68811 100644
--- a/fxjs/cjs_highlight.h
+++ b/fxjs/cjs_highlight.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_Highlight() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/cjs_icon.cpp b/fxjs/cjs_icon.cpp
index 80be5c8..d3b9da4 100644
--- a/fxjs/cjs_icon.cpp
+++ b/fxjs/cjs_icon.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,11 +9,11 @@
 const JSPropertySpec CJS_Icon::PropertySpecs[] = {
     {"name", get_name_static, set_name_static}};
 
-int CJS_Icon::ObjDefnID = -1;
+uint32_t CJS_Icon::ObjDefnID = 0;
 const char CJS_Icon::kName[] = "Icon";
 
 // static
-int CJS_Icon::GetObjDefnID() {
+uint32_t CJS_Icon::GetObjDefnID() {
   return ObjDefnID;
 }
 
diff --git a/fxjs/cjs_icon.h b/fxjs/cjs_icon.h
index 321f508..54dc1cb 100644
--- a/fxjs/cjs_icon.h
+++ b/fxjs/cjs_icon.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,7 @@
 
 class CJS_Icon final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
   CJS_Icon(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
@@ -24,7 +24,7 @@
   JS_STATIC_PROP(name, name, CJS_Icon)
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSPropertySpec PropertySpecs[];
 
diff --git a/fxjs/cjs_object.cpp b/fxjs/cjs_object.cpp
index e13a509..f27d37d 100644
--- a/fxjs/cjs_object.cpp
+++ b/fxjs/cjs_object.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,11 +10,11 @@
 
 // static
 void CJS_Object::DefineConsts(CFXJS_Engine* pEngine,
-                              int objId,
+                              uint32_t nObjDefnID,
                               pdfium::span<const JSConstSpec> consts) {
   for (const auto& item : consts) {
     pEngine->DefineObjConst(
-        objId, item.pName,
+        nObjDefnID, item.pName,
         item.eType == JSConstSpec::Number
             ? pEngine->NewNumber(item.number).As<v8::Value>()
             : pEngine->NewString(item.pStr).As<v8::Value>());
@@ -23,23 +23,22 @@
 
 // static
 void CJS_Object::DefineProps(CFXJS_Engine* pEngine,
-                             int objId,
+                             uint32_t nObjDefnID,
                              pdfium::span<const JSPropertySpec> props) {
   for (const auto& item : props)
-    pEngine->DefineObjProperty(objId, item.pName, item.pPropGet, item.pPropPut);
+    pEngine->DefineObjProperty(nObjDefnID, item.pName, item.pPropGet,
+                               item.pPropPut);
 }
 
 // static
 void CJS_Object::DefineMethods(CFXJS_Engine* pEngine,
-                               int objId,
+                               uint32_t nObjDefnID,
                                pdfium::span<const JSMethodSpec> methods) {
   for (const auto& item : methods)
-    pEngine->DefineObjMethod(objId, item.pName, item.pMethodCall);
+    pEngine->DefineObjMethod(nObjDefnID, item.pName, item.pMethodCall);
 }
 
 CJS_Object::CJS_Object(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
-    : m_pIsolate(pObject->GetIsolate()),
-      m_pV8Object(GetIsolate(), pObject),
-      m_pRuntime(pRuntime) {}
+    : m_pV8Object(pObject->GetIsolate(), pObject), m_pRuntime(pRuntime) {}
 
-CJS_Object::~CJS_Object() {}
+CJS_Object::~CJS_Object() = default;
diff --git a/fxjs/cjs_object.h b/fxjs/cjs_object.h
index 5da72ff..4c567f2 100644
--- a/fxjs/cjs_object.h
+++ b/fxjs/cjs_object.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -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;
 
@@ -36,24 +36,24 @@
 class CJS_Object {
  public:
   static void DefineConsts(CFXJS_Engine* pEngine,
-                           int objId,
+                           uint32_t nObjDefnID,
                            pdfium::span<const JSConstSpec> consts);
   static void DefineProps(CFXJS_Engine* pEngine,
-                          int objId,
+                          uint32_t nObjDefnID,
                           pdfium::span<const JSPropertySpec> consts);
   static void DefineMethods(CFXJS_Engine* pEngine,
-                            int objId,
+                            uint32_t nObjDefnID,
                             pdfium::span<const JSMethodSpec> consts);
 
   CJS_Object(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
   virtual ~CJS_Object();
 
-  v8::Local<v8::Object> ToV8Object() { return m_pV8Object.Get(GetIsolate()); }
-  v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); }
+  v8::Local<v8::Object> ToV8Object() {
+    return m_pV8Object.Get(GetRuntime()->GetIsolate());
+  }
   CJS_Runtime* GetRuntime() const { return m_pRuntime.Get(); }
 
  private:
-  UnownedPtr<v8::Isolate> m_pIsolate;
   v8::Global<v8::Object> m_pV8Object;
   ObservedPtr<CJS_Runtime> m_pRuntime;
 };
diff --git a/fxjs/cjs_position.cpp b/fxjs/cjs_position.cpp
index dc9594e..ccf4fe6 100644
--- a/fxjs/cjs_position.cpp
+++ b/fxjs/cjs_position.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,7 +15,7 @@
     {"textIconH", JSConstSpec::Number, 5, 0},
     {"overlay", JSConstSpec::Number, 6, 0}};
 
-int CJS_Position::ObjDefnID = -1;
+uint32_t CJS_Position::ObjDefnID = 0;
 
 // static
 void CJS_Position::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_position.h b/fxjs/cjs_position.h
index ae951bf..14097a9 100644
--- a/fxjs/cjs_position.h
+++ b/fxjs/cjs_position.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_Position() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/cjs_publicmethods.cpp b/fxjs/cjs_publicmethods.cpp
index 8eed97d..07fe8c5 100644
--- a/fxjs/cjs_publicmethods.cpp
+++ b/fxjs/cjs_publicmethods.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,9 @@
 
 #include "fxjs/cjs_publicmethods.h"
 
+#include <math.h>
+
 #include <algorithm>
-#include <cmath>
-#include <cwctype>
 #include <iomanip>
 #include <iterator>
 #include <limits>
@@ -18,15 +18,16 @@
 #include <vector>
 
 #include "build/build_config.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfdoc/cpdf_formcontrol.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_string_wrappers.h"
 #include "core/fxge/cfx_color.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fxjs/cjs_color.h"
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_field.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/cjs_runtime.h"
@@ -34,7 +35,10 @@
 #include "fxjs/fx_date_helpers.h"
 #include "fxjs/js_define.h"
 #include "fxjs/js_resources.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/check.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/v8-container.h"
 
 // static
 const JSMethodSpec CJS_PublicMethods::GlobalFunctionSpecs[] = {
@@ -64,7 +68,7 @@
 
 namespace {
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 constexpr double kDoubleCorrect = 0.000000000000001;
 #endif
 
@@ -93,14 +97,17 @@
   return result;
 }
 
-void AlertIfPossible(CJS_EventContext* pContext, const WideString& swMsg) {
+void AlertIfPossible(CJS_EventContext* pContext,
+                     const WideString& wsCaller,
+                     const WideString& wsMsg) {
   CPDFSDK_FormFillEnvironment* pFormFillEnv = pContext->GetFormFillEnv();
-  if (pFormFillEnv)
-    pFormFillEnv->JS_appAlert(swMsg, WideString(), JSPLATFORM_ALERT_BUTTON_OK,
+  if (pFormFillEnv) {
+    pFormFillEnv->JS_appAlert(wsMsg, wsCaller, JSPLATFORM_ALERT_BUTTON_OK,
                               JSPLATFORM_ALERT_ICON_STATUS);
+  }
 }
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 ByteString CalculateString(double dValue,
                            int iDec,
                            int* iDec2,
@@ -112,16 +119,17 @@
   // Make sure the number of precision characters will fit.
   iDec = std::min(iDec, std::numeric_limits<double>::digits10);
 
-  std::stringstream ss;
+  fxcrt::ostringstream ss;
   ss << std::fixed << std::setprecision(iDec) << dValue;
-  std::string value = ss.str();
+  fxcrt::string value = ss.str();
   size_t pos = value.find('.');
-  *iDec2 = pos == std::string::npos ? value.size() : static_cast<int>(pos);
+  *iDec2 = pdfium::base::checked_cast<int>(
+      pos == fxcrt::string::npos ? value.size() : pos);
   return ByteString(value.c_str());
 }
 #endif
 
-WideString CalcMergedString(const CJS_EventRecorder* event,
+WideString CalcMergedString(const CJS_EventContext* event,
                             const WideString& value,
                             const WideString& change) {
   WideString prefix = value.First(event->SelStart());
@@ -136,7 +144,8 @@
                           const std::vector<v8::Local<v8::Value>>&)>
 void JSGlobalFunc(const char* func_name_string,
                   const v8::FunctionCallbackInfo<v8::Value>& info) {
-  CJS_Object* pObj = CFXJS_Engine::GetObjectPrivate(info.Holder());
+  CJS_Object* pObj =
+      CFXJS_Engine::GetObjectPrivate(info.GetIsolate(), info.Holder());
   if (!pObj)
     return;
 
@@ -171,13 +180,13 @@
   return c == '.' || c == ',';
 }
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 bool IsStyleWithDigitSeparator(int style) {
   return style == 0 || style == 2;
 }
 
 char DigitSeparatorForStyle(int style) {
-  ASSERT(IsStyleWithDigitSeparator(style));
+  DCHECK(IsStyleWithDigitSeparator(style));
   return style == 0 ? ',' : '.';
 }
 
@@ -194,7 +203,7 @@
   return IsStyleWithCommaDecimalMark(style) ? ',' : '.';
 }
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 void NormalizeDecimalMark(ByteString* str) {
   str->Replace(",", ".");
 }
@@ -204,20 +213,20 @@
   str->Replace(L",", L".");
 }
 
-Optional<double> ApplyNamedOperation(const wchar_t* sFunction,
-                                     double dValue1,
-                                     double dValue2) {
-  if (FXSYS_wcsicmp(sFunction, L"AVG") == 0 ||
-      FXSYS_wcsicmp(sFunction, L"SUM") == 0) {
+absl::optional<double> ApplyNamedOperation(const WideString& wsFunction,
+                                           double dValue1,
+                                           double dValue2) {
+  if (wsFunction.EqualsASCIINoCase("AVG") ||
+      wsFunction.EqualsASCIINoCase("SUM")) {
     return dValue1 + dValue2;
   }
-  if (FXSYS_wcsicmp(sFunction, L"PRD") == 0)
+  if (wsFunction.EqualsASCIINoCase("PRD"))
     return dValue1 * dValue2;
-  if (FXSYS_wcsicmp(sFunction, L"MIN") == 0)
+  if (wsFunction.EqualsASCIINoCase("MIN"))
     return std::min(dValue1, dValue2);
-  if (FXSYS_wcsicmp(sFunction, L"MAX") == 0)
+  if (wsFunction.EqualsASCIINoCase("MAX"))
     return std::max(dValue1, dValue2);
-  return {};
+  return absl::nullopt;
 }
 
 }  // namespace
@@ -313,14 +322,13 @@
 v8::Local<v8::Array> CJS_PublicMethods::AF_MakeArrayFromList(
     CJS_Runtime* pRuntime,
     v8::Local<v8::Value> val) {
-  ASSERT(!val.IsEmpty());
+  DCHECK(!val.IsEmpty());
   if (val->IsArray())
     return pRuntime->ToArray(val);
 
-  ASSERT(val->IsString());
-  WideString wsStr = pRuntime->ToWideString(val);
-  ByteString t = wsStr.ToDefANSI();
-  const char* p = t.c_str();
+  DCHECK(val->IsString());
+  ByteString bsVal = pRuntime->ToByteString(val);
+  const char* p = bsVal.c_str();
 
   int nIndex = 0;
   v8::Local<v8::Array> StrArray = pRuntime->NewArray();
@@ -343,7 +351,8 @@
   return StrArray;
 }
 
-double CJS_PublicMethods::ParseDate(const WideString& value,
+double CJS_PublicMethods::ParseDate(v8::Isolate* isolate,
+                                    const WideString& value,
                                     bool* bWrongFormat) {
   double dt = FX_GetDateTime();
   int nYear = FX_GetYearFromTime(dt);
@@ -417,26 +426,28 @@
   }
 
   // TODO(thestig): Should we set |bWrongFormat| to false here too?
-  return JS_DateParse(WideString::Format(L"%d/%d/%d %d:%d:%d", nMonth, nDay,
-                                         nYear, nHour, nMin, nSec));
+  return JS_DateParse(
+      isolate, WideString::Format(L"%d/%d/%d %d:%d:%d", nMonth, nDay, nYear,
+                                  nHour, nMin, nSec));
 }
 
-double CJS_PublicMethods::ParseDateUsingFormat(const WideString& value,
+double CJS_PublicMethods::ParseDateUsingFormat(v8::Isolate* isolate,
+                                               const WideString& value,
                                                const WideString& format,
                                                bool* bWrongFormat) {
-  double dRet = std::nan("");
+  double dRet = nan("");
   fxjs::ConversionStatus status = FX_ParseDateUsingFormat(value, format, &dRet);
   if (status == fxjs::ConversionStatus::kSuccess)
     return dRet;
 
   if (status == fxjs::ConversionStatus::kBadDate) {
-    dRet = JS_DateParse(value);
-    if (!std::isnan(dRet))
+    dRet = JS_DateParse(isolate, value);
+    if (!isnan(dRet))
       return dRet;
   }
 
   bool bBadFormat = false;
-  dRet = ParseDate(value, &bBadFormat);
+  dRet = ParseDate(isolate, value, &bBadFormat);
   if (bWrongFormat)
     *bWrongFormat = bBadFormat;
 
@@ -475,23 +486,23 @@
               sPart += c;
               break;
             case 'm':
-              sPart = WideString::Format(L"%d", nMonth);
+              sPart = WideString::FormatInteger(nMonth);
               break;
             case 'd':
-              sPart = WideString::Format(L"%d", nDay);
+              sPart = WideString::FormatInteger(nDay);
               break;
             case 'H':
-              sPart = WideString::Format(L"%d", nHour);
+              sPart = WideString::FormatInteger(nHour);
               break;
             case 'h':
               sPart =
-                  WideString::Format(L"%d", nHour > 12 ? nHour - 12 : nHour);
+                  WideString::FormatInteger(nHour > 12 ? nHour - 12 : nHour);
               break;
             case 'M':
-              sPart = WideString::Format(L"%d", nMin);
+              sPart = WideString::FormatInteger(nMin);
               break;
             case 's':
-              sPart = WideString::Format(L"%d", nSec);
+              sPart = WideString::FormatInteger(nSec);
               break;
             case 't':
               sPart += nHour > 12 ? 'p' : 'a';
@@ -582,17 +593,16 @@
 CJS_Result CJS_PublicMethods::AFNumber_Format(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
   if (params.size() != 6)
     return CJS_Result::Failure(JSMessage::kParamError);
 
   CJS_EventContext* pEventContext = pRuntime->GetCurrentEventContext();
-  CJS_EventRecorder* pEvent = pEventContext->GetEventRecorder();
-  if (!pEvent->HasValue())
+  if (!pEventContext->HasValue())
     return CJS_Result::Failure(WideString::FromASCII("No event handler"));
 
-  WideString& Value = pEvent->Value();
-  ByteString strValue = StrTrim(Value.ToDefANSI());
+  WideString& Value = pEventContext->Value();
+  ByteString strValue = StrTrim(Value.ToUTF8());
   if (strValue.IsEmpty())
     return CJS_Result::Success();
 
@@ -621,7 +631,7 @@
       iDec2 = 1;
     }
   }
-  ASSERT(iDec2 >= 0);
+  DCHECK(iDec2 >= 0);
 
   // Processing separator style
   if (static_cast<size_t>(iDec2) < strValue.GetLength()) {
@@ -638,7 +648,7 @@
   }
 
   // Processing currency string
-  Value = WideString::FromDefANSI(strValue.AsStringView());
+  Value = WideString::FromUTF8(strValue.AsStringView());
   if (bCurrencyPrepend)
     Value = wstrCurrency + Value;
   else
@@ -694,24 +704,23 @@
     return CJS_Result::Failure(JSMessage::kParamError);
 
   CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
-  if (!pEvent->HasValue())
+  if (!pContext->HasValue())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  WideString& val = pEvent->Value();
-  WideString& wstrChange = pEvent->Change();
+  WideString& val = pContext->Value();
+  WideString& wstrChange = pContext->Change();
   WideString wstrValue = val;
 
-  if (pEvent->WillCommit()) {
+  if (pContext->WillCommit()) {
     WideString swTemp = StrTrim(wstrValue);
     if (swTemp.IsEmpty())
       return CJS_Result::Success();
 
     NormalizeDecimalMarkW(&swTemp);
     if (!IsNumber(swTemp)) {
-      pEvent->Rc() = false;
+      pContext->Rc() = false;
       WideString sError = JSGetStringFromID(JSMessage::kInvalidInputError);
-      AlertIfPossible(pContext, sError);
+      AlertIfPossible(pContext, L"AFNumber_Keystroke", sError);
       return CJS_Result::Failure(sError);
     }
     // It happens after the last keystroke and before validating,
@@ -719,16 +728,16 @@
   }
 
   WideString wstrSelected;
-  if (pEvent->SelStart() != -1) {
-    wstrSelected = wstrValue.Substr(pEvent->SelStart(),
-                                    pEvent->SelEnd() - pEvent->SelStart());
+  if (pContext->SelStart() != -1) {
+    wstrSelected = wstrValue.Substr(pContext->SelStart(),
+                                    pContext->SelEnd() - pContext->SelStart());
   }
 
   bool bHasSign = wstrValue.Contains(L'-') && !wstrSelected.Contains(L'-');
   if (bHasSign) {
     // can't insert "change" in front of sign position.
-    if (!wstrSelected.IsEmpty() && pEvent->SelStart() == 0) {
-      pEvent->Rc() = false;
+    if (!wstrSelected.IsEmpty() && pContext->SelStart() == 0) {
+      pContext->Rc() = false;
       return CJS_Result::Success();
     }
   }
@@ -740,7 +749,7 @@
   for (size_t i = 0; i < wstrChange.GetLength(); ++i) {
     if (wstrChange[i] == cSep) {
       if (bHasSep) {
-        pEvent->Rc() = false;
+        pContext->Rc() = false;
         return CJS_Result::Success();
       }
       bHasSep = true;
@@ -748,16 +757,16 @@
     }
     if (wstrChange[i] == L'-') {
       if (bHasSign) {
-        pEvent->Rc() = false;
+        pContext->Rc() = false;
         return CJS_Result::Success();
       }
       // sign's position is not correct
       if (i != 0) {
-        pEvent->Rc() = false;
+        pContext->Rc() = false;
         return CJS_Result::Success();
       }
-      if (pEvent->SelStart() != 0) {
-        pEvent->Rc() = false;
+      if (pContext->SelStart() != 0) {
+        pContext->Rc() = false;
         return CJS_Result::Success();
       }
       bHasSign = true;
@@ -765,12 +774,12 @@
     }
 
     if (!FXSYS_IsDecimalDigit(wstrChange[i])) {
-      pEvent->Rc() = false;
+      pContext->Rc() = false;
       return CJS_Result::Success();
     }
   }
 
-  val = CalcMergedString(pEvent, wstrValue, wstrChange);
+  val = CalcMergedString(pContext, wstrValue, wstrChange);
   return CJS_Result::Success();
 }
 
@@ -778,12 +787,11 @@
 CJS_Result CJS_PublicMethods::AFPercent_Format(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
   if (params.size() < 2)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (!pEvent->HasValue())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -799,8 +807,7 @@
 
   // When the |iDec| value is too big, Acrobat will just return "%".
   static constexpr int kDecLimit = 512;
-  // TODO(thestig): Calculate this once C++14 can be used to declare variables
-  // in constexpr functions.
+  // This count must be in sync with |kDecLimit|.
   static constexpr size_t kDigitsInDecLimit = 3;
   WideString& Value = pEvent->Value();
   if (iDec > kDecLimit) {
@@ -808,7 +815,7 @@
     return CJS_Result::Success();
   }
 
-  ByteString strValue = StrTrim(Value.ToDefANSI());
+  ByteString strValue = StrTrim(Value.ToUTF8());
   if (strValue.IsEmpty())
     strValue = "0";
 
@@ -839,7 +846,7 @@
   strValue.ReleaseBuffer(szNewSize);
 
   // for processing separator style
-  Optional<size_t> mark_pos = strValue.Find('.');
+  absl::optional<size_t> mark_pos = strValue.Find('.');
   if (mark_pos.has_value()) {
     char mark = DecimalMarkForStyle(iSepStyle);
     if (mark != '.')
@@ -859,7 +866,7 @@
     strValue.InsertAtFront('%');
   else
     strValue.InsertAtBack('%');
-  Value = WideString::FromDefANSI(strValue.AsStringView());
+  Value = WideString::FromUTF8(strValue.AsStringView());
 #endif
   return CJS_Result::Success();
 }
@@ -878,8 +885,7 @@
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (!pEvent->HasValue())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -892,15 +898,16 @@
   double dDate;
   if (strValue.Contains(L"GMT")) {
     // e.g. "Tue Aug 11 14:24:16 GMT+08002009"
-    dDate = ParseDateAsGMT(strValue);
+    dDate = ParseDateAsGMT(pRuntime->GetIsolate(), strValue);
   } else {
-    dDate = ParseDateUsingFormat(strValue, sFormat, nullptr);
+    dDate = ParseDateUsingFormat(pRuntime->GetIsolate(), strValue, sFormat,
+                                 nullptr);
   }
 
-  if (std::isnan(dDate)) {
+  if (isnan(dDate)) {
     WideString swMsg = WideString::Format(
         JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str());
-    AlertIfPossible(pContext, swMsg);
+    AlertIfPossible(pEvent, L"AFDate_FormatEx", swMsg);
     return CJS_Result::Failure(JSMessage::kParseDateError);
   }
 
@@ -908,7 +915,8 @@
   return CJS_Result::Success();
 }
 
-double CJS_PublicMethods::ParseDateAsGMT(const WideString& strValue) {
+double CJS_PublicMethods::ParseDateAsGMT(v8::Isolate* isolate,
+                                         const WideString& strValue) {
   std::vector<WideString> wsArray;
   WideString sTemp;
   for (const auto& c : strValue) {
@@ -924,9 +932,9 @@
 
   int nMonth = 1;
   sTemp = wsArray[1];
-  for (size_t i = 0; i < FX_ArraySize(fxjs::kMonths); ++i) {
-    if (sTemp.Compare(fxjs::kMonths[i]) == 0) {
-      nMonth = i + 1;
+  for (size_t i = 0; i < std::size(fxjs::kMonths); ++i) {
+    if (sTemp == fxjs::kMonths[i]) {
+      nMonth = static_cast<int>(i) + 1;
       break;
     }
   }
@@ -938,8 +946,8 @@
   int nYear = StringToFloat(wsArray[7].AsStringView());
   double dRet = FX_MakeDate(FX_MakeDay(nYear, nMonth - 1, nDay),
                             FX_MakeTime(nHour, nMin, nSec, 0));
-  if (std::isnan(dRet))
-    dRet = JS_DateParse(strValue);
+  if (isnan(dRet))
+    dRet = JS_DateParse(isolate, strValue);
 
   return dRet;
 }
@@ -953,8 +961,7 @@
         "AFDate_KeystrokeEx's parameter size not correct"));
   }
 
-  CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (!pEvent->WillCommit())
     return CJS_Result::Success();
 
@@ -967,11 +974,12 @@
 
   bool bWrongFormat = false;
   WideString sFormat = pRuntime->ToWideString(params[0]);
-  double dRet = ParseDateUsingFormat(strValue, sFormat, &bWrongFormat);
-  if (bWrongFormat || std::isnan(dRet)) {
+  double dRet = ParseDateUsingFormat(pRuntime->GetIsolate(), strValue, sFormat,
+                                     &bWrongFormat);
+  if (bWrongFormat || isnan(dRet)) {
     WideString swMsg = WideString::Format(
         JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str());
-    AlertIfPossible(pContext, swMsg);
+    AlertIfPossible(pEvent, L"AFDate_KeystrokeEx", swMsg);
     pEvent->Rc() = false;
   }
   return CJS_Result::Success();
@@ -983,8 +991,8 @@
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]),
-                                  FX_ArraySize(kDateFormats));
+  int iIndex =
+      WithinBoundsOrZero(pRuntime->ToInt32(params[0]), std::size(kDateFormats));
   std::vector<v8::Local<v8::Value>> newParams;
   newParams.push_back(pRuntime->NewString(kDateFormats[iIndex]));
   return AFDate_FormatEx(pRuntime, newParams);
@@ -997,8 +1005,8 @@
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]),
-                                  FX_ArraySize(kDateFormats));
+  int iIndex =
+      WithinBoundsOrZero(pRuntime->ToInt32(params[0]), std::size(kDateFormats));
   std::vector<v8::Local<v8::Value>> newParams;
   newParams.push_back(pRuntime->NewString(kDateFormats[iIndex]));
   return AFDate_KeystrokeEx(pRuntime, newParams);
@@ -1011,8 +1019,8 @@
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]),
-                                  FX_ArraySize(kTimeFormats));
+  int iIndex =
+      WithinBoundsOrZero(pRuntime->ToInt32(params[0]), std::size(kTimeFormats));
   std::vector<v8::Local<v8::Value>> newParams;
   newParams.push_back(pRuntime->NewString(kTimeFormats[iIndex]));
   return AFDate_FormatEx(pRuntime, newParams);
@@ -1024,8 +1032,8 @@
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]),
-                                  FX_ArraySize(kTimeFormats));
+  int iIndex =
+      WithinBoundsOrZero(pRuntime->ToInt32(params[0]), std::size(kTimeFormats));
   std::vector<v8::Local<v8::Value>> newParams;
   newParams.push_back(pRuntime->NewString(kTimeFormats[iIndex]));
   return AFDate_KeystrokeEx(pRuntime, newParams);
@@ -1050,8 +1058,7 @@
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (!pEvent->HasValue())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -1086,8 +1093,7 @@
   if (params.size() < 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (!pEvent->HasValue())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -1101,7 +1107,7 @@
       return CJS_Result::Success();
 
     if (valEvent.GetLength() > wstrMask.GetLength()) {
-      AlertIfPossible(pContext,
+      AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx",
                       JSGetStringFromID(JSMessage::kParamTooLongError));
       pEvent->Rc() = false;
       return CJS_Result::Success();
@@ -1113,7 +1119,7 @@
         break;
     }
     if (iIndex != wstrMask.GetLength()) {
-      AlertIfPossible(pContext,
+      AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx",
                       JSGetStringFromID(JSMessage::kInvalidInputError));
       pEvent->Rc() = false;
     }
@@ -1129,20 +1135,22 @@
   size_t combined_len = valEvent.GetLength() + wChange.GetLength() +
                         pEvent->SelStart() - pEvent->SelEnd();
   if (combined_len > wstrMask.GetLength()) {
-    AlertIfPossible(pContext, JSGetStringFromID(JSMessage::kParamTooLongError));
+    AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx",
+                    JSGetStringFromID(JSMessage::kParamTooLongError));
     pEvent->Rc() = false;
     return CJS_Result::Success();
   }
 
   if (iIndexMask >= wstrMask.GetLength() && !wChange.IsEmpty()) {
-    AlertIfPossible(pContext, JSGetStringFromID(JSMessage::kParamTooLongError));
+    AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx",
+                    JSGetStringFromID(JSMessage::kParamTooLongError));
     pEvent->Rc() = false;
     return CJS_Result::Success();
   }
 
   for (size_t i = 0; i < wChange.GetLength(); ++i) {
     if (iIndexMask >= wstrMask.GetLength()) {
-      AlertIfPossible(pContext,
+      AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx",
                       JSGetStringFromID(JSMessage::kParamTooLongError));
       pEvent->Rc() = false;
       return CJS_Result::Success();
@@ -1168,8 +1176,7 @@
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (!pEvent->HasValue())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -1203,19 +1210,17 @@
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventRecorder* pEventRecorder =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
 
   WideString swValue;
-  if (pEventRecorder->HasValue())
-    swValue = pEventRecorder->Value();
+  if (pEvent->HasValue())
+    swValue = pEvent->Value();
 
-  if (pEventRecorder->WillCommit())
+  if (pEvent->WillCommit())
     return CJS_Result::Success(pRuntime->NewString(swValue.AsStringView()));
 
   return CJS_Result::Success(pRuntime->NewString(
-      CalcMergedString(pEventRecorder, swValue, pEventRecorder->Change())
-          .AsStringView()));
+      CalcMergedString(pEvent, swValue, pEvent->Change()).AsStringView()));
 }
 
 CJS_Result CJS_PublicMethods::AFParseDateEx(
@@ -1226,11 +1231,13 @@
 
   WideString sValue = pRuntime->ToWideString(params[0]);
   WideString sFormat = pRuntime->ToWideString(params[1]);
-  double dDate = ParseDateUsingFormat(sValue, sFormat, nullptr);
-  if (std::isnan(dDate)) {
+  double dDate =
+      ParseDateUsingFormat(pRuntime->GetIsolate(), sValue, sFormat, nullptr);
+  if (isnan(dDate)) {
     WideString swMsg = WideString::Format(
         JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str());
-    AlertIfPossible(pRuntime->GetCurrentEventContext(), swMsg);
+    AlertIfPossible(pRuntime->GetCurrentEventContext(), L"AFParseDateEx",
+                    swMsg);
     return CJS_Result::Failure(JSMessage::kParseDateError);
   }
   return CJS_Result::Success(pRuntime->NewNumber(dDate));
@@ -1245,10 +1252,10 @@
   WideString sFunction = pRuntime->ToWideString(params[0]);
   double arg1 = pRuntime->ToDouble(params[1]);
   double arg2 = pRuntime->ToDouble(params[2]);
-  if (std::isnan(arg1) || std::isnan(arg2))
+  if (isnan(arg1) || isnan(arg2))
     return CJS_Result::Failure(JSMessage::kValueError);
 
-  Optional<double> result = ApplyNamedOperation(sFunction.c_str(), arg1, arg2);
+  absl::optional<double> result = ApplyNamedOperation(sFunction, arg1, arg2);
   if (!result.has_value())
     return CJS_Result::Failure(JSMessage::kValueError);
 
@@ -1347,8 +1354,8 @@
            wcscmp(sFunction.c_str(), L"MAX") == 0)) {
         dValue = dTemp;
       }
-      Optional<double> dResult =
-          ApplyNamedOperation(sFunction.c_str(), dValue, dTemp);
+      absl::optional<double> dResult =
+          ApplyNamedOperation(sFunction, dValue, dTemp);
       if (!dResult.has_value())
         return CJS_Result::Failure(JSMessage::kValueError);
 
@@ -1360,13 +1367,11 @@
   if (wcscmp(sFunction.c_str(), L"AVG") == 0 && nFieldsCount > 0)
     dValue /= nFieldsCount;
 
-  dValue = floor(dValue * FXSYS_pow(10, 6) + 0.49) / FXSYS_pow(10, 6);
+  dValue = floor(dValue * powf(10, 6) + 0.49) / powf(10, 6);
 
   CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  if (pContext->GetEventRecorder()->HasValue()) {
-    pContext->GetEventRecorder()->Value() =
-        pRuntime->ToWideString(pRuntime->NewNumber(dValue));
-  }
+  if (pContext->HasValue())
+    pContext->Value() = pRuntime->ToWideString(pRuntime->NewNumber(dValue));
 
   return CJS_Result::Success();
 }
@@ -1379,15 +1384,14 @@
   if (params.size() != 4)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (!pEvent->HasValue())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   if (pEvent->Value().IsEmpty())
     return CJS_Result::Success();
 
-  double dEentValue = atof(pEvent->Value().ToDefANSI().c_str());
+  double dEventValue = atof(pEvent->Value().ToUTF8().c_str());
   bool bGreaterThan = pRuntime->ToBoolean(params[0]);
   double dGreaterThan = pRuntime->ToDouble(params[1]);
   bool bLessThan = pRuntime->ToBoolean(params[2]);
@@ -1395,25 +1399,25 @@
   WideString swMsg;
 
   if (bGreaterThan && bLessThan) {
-    if (dEentValue < dGreaterThan || dEentValue > dLessThan)
+    if (dEventValue < dGreaterThan || dEventValue > dLessThan)
       swMsg = WideString::Format(
           JSGetStringFromID(JSMessage::kRangeBetweenError).c_str(),
           pRuntime->ToWideString(params[1]).c_str(),
           pRuntime->ToWideString(params[3]).c_str());
   } else if (bGreaterThan) {
-    if (dEentValue < dGreaterThan)
+    if (dEventValue < dGreaterThan)
       swMsg = WideString::Format(
           JSGetStringFromID(JSMessage::kRangeGreaterError).c_str(),
           pRuntime->ToWideString(params[1]).c_str());
   } else if (bLessThan) {
-    if (dEentValue > dLessThan)
+    if (dEventValue > dLessThan)
       swMsg = WideString::Format(
           JSGetStringFromID(JSMessage::kRangeLessError).c_str(),
           pRuntime->ToWideString(params[3]).c_str());
   }
 
   if (!swMsg.IsEmpty()) {
-    AlertIfPossible(pContext, swMsg);
+    AlertIfPossible(pEvent, L"AFRange_Validate", swMsg);
     pEvent->Rc() = false;
   }
   return CJS_Result::Success();
diff --git a/fxjs/cjs_publicmethods.h b/fxjs/cjs_publicmethods.h
index 53a4dcc..ddf13cd 100644
--- a/fxjs/cjs_publicmethods.h
+++ b/fxjs/cjs_publicmethods.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,9 +18,12 @@
 
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  static double ParseDate(const WideString& value, bool* bWrongFormat);
-  static double ParseDateAsGMT(const WideString& value);
-  static double ParseDateUsingFormat(const WideString& value,
+  static double ParseDate(v8::Isolate* isolate,
+                          const WideString& value,
+                          bool* bWrongFormat);
+  static double ParseDateAsGMT(v8::Isolate* isolate, const WideString& value);
+  static double ParseDateUsingFormat(v8::Isolate* isolate,
+                                     const WideString& value,
                                      const WideString& format,
                                      bool* bWrongFormat);
 
diff --git a/fxjs/cjs_publicmethods_embeddertest.cpp b/fxjs/cjs_publicmethods_embeddertest.cpp
index d35f0cc..5a1a6e7 100644
--- a/fxjs/cjs_publicmethods_embeddertest.cpp
+++ b/fxjs/cjs_publicmethods_embeddertest.cpp
@@ -1,16 +1,22 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <cmath>
+#include <math.h>
+
 #include <vector>
 
 #include "core/fxcrt/fx_string.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "fxjs/cjs_event_context.h"
 #include "fxjs/cjs_publicmethods.h"
+#include "testing/external_engine_embedder_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/js_embedder_test.h"
+#include "v8/include/v8-container.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-local-handle.h"
+#include "v8/include/v8-value.h"
 
 namespace {
 
@@ -20,7 +26,7 @@
 
 }  // namespace
 
-class CJS_PublicMethodsEmbedderTest : public JSEmbedderTest {};
+class CJS_PublicMethodsEmbedderTest : public ExternalEngineEmbedderTest {};
 
 TEST_F(CJS_PublicMethodsEmbedderTest, ParseDateUsingFormat) {
   v8::Isolate::Scope isolate_scope(isolate());
@@ -31,80 +37,80 @@
 
   // 1968
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"06/25/1968", L"mm/dd/yyyy",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"06/25/1968",
+                                                 L"mm/dd/yyyy", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(-47865600000, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 1968
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"25061968", L"ddmmyyyy",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"25061968",
+                                                 L"ddmmyyyy", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(-47865600000, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 1968
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"19680625", L"yyyymmdd",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"19680625",
+                                                 L"yyyymmdd", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(-47865600000, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 1985
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"31121985", L"ddmmyyyy",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"31121985",
+                                                 L"ddmmyyyy", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(504835200000.0, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 2085, the other '85.
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"311285", L"ddmmyy",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"311285",
+                                                 L"ddmmyy", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(3660595200000.0, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 1995
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"01021995", L"ddmmyyyy",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"01021995",
+                                                 L"ddmmyyyy", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(791596800000.0, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 2095, the other '95.
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"010295", L"ddmmyy",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"010295",
+                                                 L"ddmmyy", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(3947356800000.0, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 2005
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"01022005", L"ddmmyyyy",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"01022005",
+                                                 L"ddmmyyyy", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(1107216000000.0, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 2005
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"010205", L"ddmmyy",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"010205",
+                                                 L"ddmmyy", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(1107216000000.0, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 2005 in a different format. https://crbug.com/436572
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"050201", L"yymmdd",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"050201",
+                                                 L"yymmdd", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(1107216000000.0, date);
   EXPECT_FALSE(bWrongFormat);
@@ -182,7 +188,7 @@
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(GetV8Context());
 
-  EXPECT_TRUE(OpenDocument("calculate.pdf"));
+  ASSERT_TRUE(OpenDocument("calculate.pdf"));
   auto* page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -191,8 +197,7 @@
   runtime.NewEventContext();
 
   WideString result;
-  runtime.GetCurrentEventContext()->GetEventRecorder()->SetValueForTest(
-      &result);
+  runtime.GetCurrentEventContext()->SetValueForTest(&result);
 
   auto ary = runtime.NewArray();
   runtime.PutArrayElement(ary, 0, runtime.NewString("Calc1_A"));
@@ -205,8 +210,7 @@
   CJS_Result ret = CJS_PublicMethods::AFSimple_Calculate(&runtime, params);
   UnloadPage(page);
 
-  runtime.GetCurrentEventContext()->GetEventRecorder()->SetValueForTest(
-      nullptr);
+  runtime.GetCurrentEventContext()->SetValueForTest(nullptr);
 
   ASSERT_TRUE(!ret.HasError());
   ASSERT_TRUE(!ret.HasReturn());
@@ -218,7 +222,7 @@
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(GetV8Context());
 
-  EXPECT_TRUE(OpenDocument("calculate.pdf"));
+  ASSERT_TRUE(OpenDocument("calculate.pdf"));
   auto* page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -226,7 +230,7 @@
       CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle()));
   runtime.NewEventContext();
 
-  auto* handler = runtime.GetCurrentEventContext()->GetEventRecorder();
+  auto* handler = runtime.GetCurrentEventContext();
 
   bool valid = true;
   WideString result = L"-10";
diff --git a/fxjs/cjs_publicmethods_unittest.cpp b/fxjs/cjs_publicmethods_unittest.cpp
index 1d72418..a5b9314 100644
--- a/fxjs/cjs_publicmethods_unittest.cpp
+++ b/fxjs/cjs_publicmethods_unittest.cpp
@@ -1,9 +1,11 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxjs/cjs_publicmethods.h"
 
+#include <iterator>
+
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(CJS_PublicMethods, IsNumber) {
@@ -42,7 +44,7 @@
       {L"0123", true},
       {L"9876123", true},
   };
-  for (size_t i = 0; i < FX_ArraySize(test_data); ++i) {
+  for (size_t i = 0; i < std::size(test_data); ++i) {
     EXPECT_EQ(test_data[i].expected,
               CJS_PublicMethods::IsNumber(test_data[i].input))
         << "for case " << i;
diff --git a/fxjs/cjs_result.cpp b/fxjs/cjs_result.cpp
index 92f2b0b..8bed633 100644
--- a/fxjs/cjs_result.cpp
+++ b/fxjs/cjs_result.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "fxjs/cjs_result.h"
 
-CJS_Result::CJS_Result() {}
+CJS_Result::CJS_Result() = default;
 
 CJS_Result::CJS_Result(v8::Local<v8::Value> ret) : return_(ret) {}
 
diff --git a/fxjs/cjs_result.h b/fxjs/cjs_result.h
index 5e1ed7c..993a4dc 100644
--- a/fxjs/cjs_result.h
+++ b/fxjs/cjs_result.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,12 +8,12 @@
 #define FXJS_CJS_RESULT_H_
 
 #include "fxjs/js_resources.h"
-#include "third_party/base/optional.h"
-#include "v8/include/v8.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "v8/include/v8-forward.h"
 
 class CJS_Result {
  public:
-  // Wrap constructors with static methods so we can apply WARN_UNUSED_RESULT,
+  // Wrap constructors with static methods so we can apply [[nodiscard]],
   // otherwise we can't catch places where someone mistakenly writes:
   //
   //     if (error)
@@ -24,14 +24,14 @@
   //     if (error)
   //       return CJS_Result(JS_ERROR_CODE);
   //
-  static CJS_Result Success() WARN_UNUSED_RESULT { return CJS_Result(); }
-  static CJS_Result Success(v8::Local<v8::Value> value) WARN_UNUSED_RESULT {
+  [[nodiscard]] static CJS_Result Success() { return CJS_Result(); }
+  [[nodiscard]] static CJS_Result Success(v8::Local<v8::Value> value) {
     return CJS_Result(value);
   }
-  static CJS_Result Failure(const WideString& str) WARN_UNUSED_RESULT {
+  [[nodiscard]] static CJS_Result Failure(const WideString& str) {
     return CJS_Result(str);
   }
-  static CJS_Result Failure(JSMessage id) WARN_UNUSED_RESULT {
+  [[nodiscard]] static CJS_Result Failure(JSMessage id) {
     return CJS_Result(id);
   }
 
@@ -39,7 +39,7 @@
   ~CJS_Result();
 
   bool HasError() const { return error_.has_value(); }
-  WideString Error() const { return error_.value(); }
+  const WideString& Error() const { return error_.value(); }
 
   bool HasReturn() const { return !return_.IsEmpty(); }
   v8::Local<v8::Value> Return() const { return return_; }
@@ -50,7 +50,7 @@
   explicit CJS_Result(const WideString&);     // Error with custom message.
   explicit CJS_Result(JSMessage id);          // Error with stock message.
 
-  Optional<WideString> error_;
+  absl::optional<WideString> error_;
   v8::Local<v8::Value> return_;
 };
 
diff --git a/fxjs/cjs_runtime.cpp b/fxjs/cjs_runtime.cpp
index 919d47a..f7d76da 100644
--- a/fxjs/cjs_runtime.cpp
+++ b/fxjs/cjs_runtime.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include "fxjs/cjs_runtime.h"
 
+#include <math.h>
+
 #include <algorithm>
 
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
@@ -19,7 +21,6 @@
 #include "fxjs/cjs_document.h"
 #include "fxjs/cjs_event.h"
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_field.h"
 #include "fxjs/cjs_font.h"
 #include "fxjs/cjs_global.h"
@@ -36,14 +37,20 @@
 #include "fxjs/cjs_timerobj.h"
 #include "fxjs/cjs_util.h"
 #include "fxjs/cjs_zoomtype.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_define.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check_op.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-exception.h"
+#include "v8/include/v8-isolate.h"
 
 CJS_Runtime::CJS_Runtime(CPDFSDK_FormFillEnvironment* pFormFillEnv)
     : m_pFormFillEnv(pFormFillEnv) {
   v8::Isolate* pIsolate = nullptr;
   IPDF_JSPLATFORM* pPlatform = m_pFormFillEnv->GetFormFillInfo()->m_pJsPlatform;
   if (pPlatform->version <= 2) {
+    // Backwards compatibility - JS now initialized earlier in more modern
+    // JSPLATFORM versions.
     unsigned int embedderDataSlot = 0;
     v8::Isolate* pExternalIsolate = nullptr;
     if (pPlatform->version == 2) {
@@ -120,12 +127,12 @@
 }
 
 IJS_EventContext* CJS_Runtime::NewEventContext() {
-  m_EventContextArray.push_back(pdfium::MakeUnique<CJS_EventContext>(this));
+  m_EventContextArray.push_back(std::make_unique<CJS_EventContext>(this));
   return m_EventContextArray.back().get();
 }
 
 void CJS_Runtime::ReleaseEventContext(IJS_EventContext* pContext) {
-  ASSERT(pContext == m_EventContextArray.back().get());
+  DCHECK_EQ(pContext, m_EventContextArray.back().get());
   m_EventContextArray.pop_back();
 }
 
@@ -134,7 +141,7 @@
                                      : m_EventContextArray.back().get();
 }
 
-TimerHandlerIface* CJS_Runtime::GetTimerHandler() const {
+CFX_Timer::HandlerIface* CJS_Runtime::GetTimerHandler() const {
   return m_pFormFillEnv ? m_pFormFillEnv->GetTimerHandler() : nullptr;
 }
 
@@ -148,7 +155,7 @@
   if (pThis.IsEmpty())
     return;
 
-  auto pJSDocument = JSGetObject<CJS_Document>(pThis);
+  auto pJSDocument = JSGetObject<CJS_Document>(GetIsolate(), pThis);
   if (!pJSDocument)
     return;
 
@@ -159,7 +166,7 @@
   return m_pFormFillEnv.Get();
 }
 
-Optional<IJS_Runtime::JS_Error> CJS_Runtime::ExecuteScript(
+absl::optional<IJS_Runtime::JS_Error> CJS_Runtime::ExecuteScript(
     const WideString& script) {
   return Execute(script);
 }
@@ -176,22 +183,16 @@
   return this;
 }
 
-bool CJS_Runtime::GetValueByNameFromGlobalObject(ByteStringView utf8Name,
-                                                 v8::Local<v8::Value>* pValue) {
+v8::Local<v8::Value> CJS_Runtime::GetValueByNameFromGlobalObject(
+    ByteStringView utf8Name) {
   v8::Isolate::Scope isolate_scope(GetIsolate());
   v8::Local<v8::Context> context = GetV8Context();
   v8::Context::Scope context_scope(context);
-  v8::Local<v8::String> str =
-      v8::String::NewFromUtf8(GetIsolate(), utf8Name.unterminated_c_str(),
-                              v8::NewStringType::kNormal, utf8Name.GetLength())
-          .ToLocalChecked();
-  v8::MaybeLocal<v8::Value> maybe_propvalue =
-      context->Global()->Get(context, str);
-  if (maybe_propvalue.IsEmpty())
-    return false;
-
-  *pValue = maybe_propvalue.ToLocalChecked();
-  return true;
+  v8::Local<v8::String> str = fxv8::NewStringHelper(GetIsolate(), utf8Name);
+  v8::MaybeLocal<v8::Value> maybe_value = context->Global()->Get(context, str);
+  if (maybe_value.IsEmpty())
+    return v8::Local<v8::Value>();
+  return maybe_value.ToLocalChecked();
 }
 
 bool CJS_Runtime::SetValueByNameInGlobalObject(ByteStringView utf8Name,
@@ -203,10 +204,7 @@
   v8::Isolate::Scope isolate_scope(pIsolate);
   v8::Local<v8::Context> context = GetV8Context();
   v8::Context::Scope context_scope(context);
-  v8::Local<v8::String> str =
-      v8::String::NewFromUtf8(pIsolate, utf8Name.unterminated_c_str(),
-                              v8::NewStringType::kNormal, utf8Name.GetLength())
-          .ToLocalChecked();
+  v8::Local<v8::String> str = fxv8::NewStringHelper(pIsolate, utf8Name);
   v8::Maybe<bool> result = context->Global()->Set(context, str, pValue);
   return result.IsJust() && result.FromJust();
 }
@@ -215,22 +213,21 @@
     v8::Local<v8::Value> value) {
   bool bAllowNaN = false;
   if (value->IsString()) {
-    ByteString bstr = ToWideString(value).ToDefANSI();
+    ByteString bstr = fxv8::ToByteString(GetIsolate(), value.As<v8::String>());
     if (bstr.IsEmpty())
       return value;
     if (bstr == "NaN")
       bAllowNaN = true;
   }
 
-  v8::Isolate* pIsolate = GetIsolate();
-  v8::TryCatch try_catch(pIsolate);
+  v8::TryCatch try_catch(GetIsolate());
   v8::MaybeLocal<v8::Number> maybeNum =
-      value->ToNumber(pIsolate->GetCurrentContext());
+      value->ToNumber(GetIsolate()->GetCurrentContext());
   if (maybeNum.IsEmpty())
     return value;
 
   v8::Local<v8::Number> num = maybeNum.ToLocalChecked();
-  if (std::isnan(num->Value()) && !bAllowNaN)
+  if (isnan(num->Value()) && !bAllowNaN)
     return value;
 
   return num;
diff --git a/fxjs/cjs_runtime.h b/fxjs/cjs_runtime.h
index b3f8ef6..a3569f6 100644
--- a/fxjs/cjs_runtime.h
+++ b/fxjs/cjs_runtime.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,20 +12,19 @@
 #include <utility>
 #include <vector>
 
+#include "core/fxcrt/cfx_timer.h"
 #include "core/fxcrt/observed_ptr.h"
-#include "core/fxcrt/timerhandler_iface.h"
 #include "fxjs/cfxjs_engine.h"
-#include "fxjs/cjs_eventrecorder.h"
+#include "fxjs/cjs_event_context.h"
 #include "fxjs/ijs_runtime.h"
 
-class CJS_EventContext;
 class CPDFSDK_FormFillEnvironment;
 
 class CJS_Runtime final : public IJS_Runtime,
                           public CFXJS_Engine,
                           public Observable {
  public:
-  using FieldEvent = std::pair<WideString, JS_EVENT_T>;
+  using FieldEvent = std::pair<WideString, CJS_EventContext::Kind>;
 
   explicit CJS_Runtime(CPDFSDK_FormFillEnvironment* pFormFillEnv);
   ~CJS_Runtime() override;
@@ -35,11 +34,11 @@
   IJS_EventContext* NewEventContext() override;
   void ReleaseEventContext(IJS_EventContext* pContext) override;
   CPDFSDK_FormFillEnvironment* GetFormFillEnv() const override;
-  Optional<IJS_Runtime::JS_Error> ExecuteScript(
+  absl::optional<IJS_Runtime::JS_Error> ExecuteScript(
       const WideString& script) override;
 
   CJS_EventContext* GetCurrentEventContext() const;
-  TimerHandlerIface* GetTimerHandler() const;
+  CFX_Timer::HandlerIface* GetTimerHandler() const;
 
   // Returns true if the event isn't already found in the set.
   bool AddEventToSet(const FieldEvent& event);
@@ -53,8 +52,7 @@
   // value will be returned, otherwise |value| is returned.
   v8::Local<v8::Value> MaybeCoerceToNumber(v8::Local<v8::Value> value);
 
-  bool GetValueByNameFromGlobalObject(ByteStringView utf8Name,
-                                      v8::Local<v8::Value>* pValue);
+  v8::Local<v8::Value> GetValueByNameFromGlobalObject(ByteStringView utf8Name);
   bool SetValueByNameInGlobalObject(ByteStringView utf8Name,
                                     v8::Local<v8::Value> pValue);
 
diff --git a/fxjs/cjs_runtimestub.cpp b/fxjs/cjs_runtimestub.cpp
index 6e313de..0a68a17 100644
--- a/fxjs/cjs_runtimestub.cpp
+++ b/fxjs/cjs_runtimestub.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,6 @@
 #include "fxjs/cjs_runtimestub.h"
 
 #include "fxjs/cjs_event_context_stub.h"
-#include "third_party/base/ptr_util.h"
 
 CJS_RuntimeStub::CJS_RuntimeStub(CPDFSDK_FormFillEnvironment* pFormFillEnv)
     : m_pFormFillEnv(pFormFillEnv) {}
@@ -16,21 +15,21 @@
 
 IJS_EventContext* CJS_RuntimeStub::NewEventContext() {
   if (!m_pContext)
-    m_pContext = pdfium::MakeUnique<CJS_EventContextStub>();
+    m_pContext = std::make_unique<CJS_EventContextStub>();
   return m_pContext.get();
 }
 
 void CJS_RuntimeStub::ReleaseEventContext(IJS_EventContext* pContext) {}
 
 CPDFSDK_FormFillEnvironment* CJS_RuntimeStub::GetFormFillEnv() const {
-  return m_pFormFillEnv.Get();
+  return m_pFormFillEnv;
 }
 
 CJS_Runtime* CJS_RuntimeStub::AsCJSRuntime() {
   return nullptr;
 }
 
-Optional<IJS_Runtime::JS_Error> CJS_RuntimeStub::ExecuteScript(
+absl::optional<IJS_Runtime::JS_Error> CJS_RuntimeStub::ExecuteScript(
     const WideString& script) {
-  return pdfium::nullopt;
+  return absl::nullopt;
 }
diff --git a/fxjs/cjs_runtimestub.h b/fxjs/cjs_runtimestub.h
index 2b5e713..c58e2c4 100644
--- a/fxjs/cjs_runtimestub.h
+++ b/fxjs/cjs_runtimestub.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,8 +9,8 @@
 
 #include <memory>
 
-#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
 #include "fxjs/ijs_runtime.h"
 
 class CPDFSDK_FormFillEnvironment;
@@ -27,7 +27,7 @@
   void ReleaseEventContext(IJS_EventContext* pContext) override;
   CPDFSDK_FormFillEnvironment* GetFormFillEnv() const override;
 
-  Optional<IJS_Runtime::JS_Error> ExecuteScript(
+  absl::optional<IJS_Runtime::JS_Error> ExecuteScript(
       const WideString& script) override;
 
  private:
diff --git a/fxjs/cjs_scalehow.cpp b/fxjs/cjs_scalehow.cpp
index 999949c..b64da9f 100644
--- a/fxjs/cjs_scalehow.cpp
+++ b/fxjs/cjs_scalehow.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,7 @@
     {"proportional", JSConstSpec::Number, 0, 0},
     {"anamorphic", JSConstSpec::Number, 1, 0}};
 
-int CJS_ScaleHow::ObjDefnID = -1;
+uint32_t CJS_ScaleHow::ObjDefnID = 0;
 
 // static
 void CJS_ScaleHow::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_scalehow.h b/fxjs/cjs_scalehow.h
index f8c7eb5..b5ab3e5 100644
--- a/fxjs/cjs_scalehow.h
+++ b/fxjs/cjs_scalehow.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_ScaleHow() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/cjs_scalewhen.cpp b/fxjs/cjs_scalewhen.cpp
index 5f39153..e8a3dd4 100644
--- a/fxjs/cjs_scalewhen.cpp
+++ b/fxjs/cjs_scalewhen.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,7 @@
     {"tooBig", JSConstSpec::Number, 2, 0},
     {"tooSmall", JSConstSpec::Number, 3, 0}};
 
-int CJS_ScaleWhen::ObjDefnID = -1;
+uint32_t CJS_ScaleWhen::ObjDefnID = 0;
 
 // static
 void CJS_ScaleWhen::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_scalewhen.h b/fxjs/cjs_scalewhen.h
index ef046f9..43ab541 100644
--- a/fxjs/cjs_scalewhen.h
+++ b/fxjs/cjs_scalewhen.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_ScaleWhen() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/cjs_style.cpp b/fxjs/cjs_style.cpp
index c068702..a43fa7d 100644
--- a/fxjs/cjs_style.cpp
+++ b/fxjs/cjs_style.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
     {"st", JSConstSpec::String, 0, "star"},
     {"sq", JSConstSpec::String, 0, "square"}};
 
-int CJS_Style::ObjDefnID = -1;
+uint32_t CJS_Style::ObjDefnID = 0;
 
 // static
 void CJS_Style::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_style.h b/fxjs/cjs_style.h
index 6e3ee2f..02449a6 100644
--- a/fxjs/cjs_style.h
+++ b/fxjs/cjs_style.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_Style() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/cjs_timerobj.cpp b/fxjs/cjs_timerobj.cpp
index 5fa1ac3..9dc8cfa 100644
--- a/fxjs/cjs_timerobj.cpp
+++ b/fxjs/cjs_timerobj.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,10 +9,10 @@
 #include "fxjs/global_timer.h"
 #include "fxjs/js_define.h"
 
-int CJS_TimerObj::ObjDefnID = -1;
+uint32_t CJS_TimerObj::ObjDefnID = 0;
 
 // static
-int CJS_TimerObj::GetObjDefnID() {
+uint32_t CJS_TimerObj::GetObjDefnID() {
   return ObjDefnID;
 }
 
diff --git a/fxjs/cjs_timerobj.h b/fxjs/cjs_timerobj.h
index 69effa6..a82ecd1 100644
--- a/fxjs/cjs_timerobj.h
+++ b/fxjs/cjs_timerobj.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,7 +13,7 @@
 
 class CJS_TimerObj final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
   CJS_TimerObj(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
@@ -23,7 +23,7 @@
   int GetTimerID() const { return m_nTimerID; }
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
 
   int m_nTimerID = 0;  // Weak reference to GlobalTimer through global map.
 };
diff --git a/fxjs/cjs_util.cpp b/fxjs/cjs_util.cpp
index 1f95c31..812e12b 100644
--- a/fxjs/cjs_util.cpp
+++ b/fxjs/cjs_util.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,25 +6,27 @@
 
 #include "fxjs/cjs_util.h"
 
+#include <math.h>
 #include <time.h>
 
 #include <algorithm>
-#include <cmath>
-#include <cwctype>
+#include <string>
 #include <vector>
 
 #include "build/build_config.h"
 #include "core/fxcrt/fx_extension.h"
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/cjs_publicmethods.h"
 #include "fxjs/cjs_runtime.h"
 #include "fxjs/fx_date_helpers.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_define.h"
 #include "fxjs/js_resources.h"
+#include "third_party/base/check_op.h"
+#include "v8/include/v8-date.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #include <ctype.h>
 #endif
 
@@ -40,8 +42,8 @@
 // Map PDF-style directives lacking direct wcsftime directives to
 // the value with which they will be replaced.
 struct TbConvertAdditional {
-  const wchar_t* lpszJSMark;
-  int iValue;
+  wchar_t js_mark;
+  int value;
 };
 
 const TbConvert TbConvertTable[] = {
@@ -49,7 +51,7 @@
     {L"ddd", L"%a"},  {L"dd", L"%d"},  {L"yyyy", L"%Y"}, {L"yy", L"%y"},
     {L"HH", L"%H"},   {L"hh", L"%I"},  {L"MM", L"%M"},   {L"ss", L"%S"},
     {L"TT", L"%p"},
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     {L"tt", L"%p"},   {L"h", L"%#I"},
 #else
     {L"tt", L"%P"},   {L"h", L"%l"},
@@ -75,11 +77,11 @@
     {"scand", scand_static},
     {"byteToChar", byteToChar_static}};
 
-int CJS_Util::ObjDefnID = -1;
+uint32_t CJS_Util::ObjDefnID = 0;
 const char CJS_Util::kName[] = "util";
 
 // static
-int CJS_Util::GetObjDefnID() {
+uint32_t CJS_Util::GetObjDefnID() {
   return ObjDefnID;
 }
 
@@ -109,7 +111,8 @@
   {
     size_t offset = 0;
     while (true) {
-      Optional<size_t> offset_end = unsafe_fmt_string.Find(L"%", offset + 1);
+      absl::optional<size_t> offset_end =
+          unsafe_fmt_string.Find(L"%", offset + 1);
       if (!offset_end.has_value()) {
         unsafe_conversion_specifiers.push_back(
             unsafe_fmt_string.Last(unsafe_fmt_string.GetLength() - offset));
@@ -132,14 +135,14 @@
 
     WideString segment;
     switch (ParseDataType(&fmt)) {
-      case UTIL_INT:
+      case DataType::kInt:
         segment = WideString::Format(fmt.c_str(), pRuntime->ToInt32(params[i]));
         break;
-      case UTIL_DOUBLE:
+      case DataType::kDouble:
         segment =
             WideString::Format(fmt.c_str(), pRuntime->ToDouble(params[i]));
         break;
-      case UTIL_STRING:
+      case DataType::kString:
         segment = WideString::Format(fmt.c_str(),
                                      pRuntime->ToWideString(params[i]).c_str());
         break;
@@ -163,11 +166,11 @@
   if (iSize < 2)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  if (params[1].IsEmpty() || !params[1]->IsDate())
+  if (!fxv8::IsDate(params[1]))
     return CJS_Result::Failure(JSMessage::kSecondParamNotDateError);
 
   v8::Local<v8::Date> v8_date = params[1].As<v8::Date>();
-  if (v8_date.IsEmpty() || std::isnan(pRuntime->ToDouble(v8_date)))
+  if (v8_date.IsEmpty() || isnan(pRuntime->ToDouble(v8_date)))
     return CJS_Result::Failure(JSMessage::kSecondParamInvalidDateError);
 
   double date = FX_LocalTime(pRuntime->ToDouble(v8_date));
@@ -209,18 +212,19 @@
 
   // Convert PDF-style format specifiers to wcsftime specifiers. Remove any
   // pre-existing %-directives before inserting our own.
-  std::basic_string<wchar_t> cFormat =
-      pRuntime->ToWideString(params[0]).c_str();
+  std::wstring cFormat = pRuntime->ToWideString(params[0]).c_str();
   cFormat.erase(std::remove(cFormat.begin(), cFormat.end(), '%'),
                 cFormat.end());
 
-  for (size_t i = 0; i < FX_ArraySize(TbConvertTable); ++i) {
-    int iStart = 0;
-    int iEnd;
-    while ((iEnd = cFormat.find(TbConvertTable[i].lpszJSMark, iStart)) != -1) {
-      cFormat.replace(iEnd, wcslen(TbConvertTable[i].lpszJSMark),
+  for (size_t i = 0; i < std::size(TbConvertTable); ++i) {
+    size_t nFound = 0;
+    while (true) {
+      nFound = cFormat.find(TbConvertTable[i].lpszJSMark, nFound);
+      if (nFound == std::wstring::npos)
+        break;
+
+      cFormat.replace(nFound, wcslen(TbConvertTable[i].lpszJSMark),
                       TbConvertTable[i].lpszCppMark);
-      iStart = iEnd;
     }
   }
 
@@ -228,24 +232,24 @@
     return CJS_Result::Failure(JSMessage::kValueError);
 
   const TbConvertAdditional cTableAd[] = {
-      {L"m", month}, {L"d", day},
-      {L"H", hour},  {L"h", hour > 12 ? hour - 12 : hour},
-      {L"M", min},   {L"s", sec},
+      {L'm', month}, {L'd', day},
+      {L'H', hour},  {L'h', hour > 12 ? hour - 12 : hour},
+      {L'M', min},   {L's', sec},
   };
 
-  for (size_t i = 0; i < FX_ArraySize(cTableAd); ++i) {
-    int iStart = 0;
-    int iEnd;
-    while ((iEnd = cFormat.find(cTableAd[i].lpszJSMark, iStart)) != -1) {
-      if (iEnd > 0) {
-        if (cFormat[iEnd - 1] == L'%') {
-          iStart = iEnd + 1;
-          continue;
-        }
+  for (size_t i = 0; i < std::size(cTableAd); ++i) {
+    size_t nFound = 0;
+    while (true) {
+      nFound = cFormat.find(cTableAd[i].js_mark, nFound);
+      if (nFound == std::wstring::npos)
+        break;
+
+      if (nFound != 0 && cFormat[nFound - 1] == L'%') {
+        ++nFound;
+        continue;
       }
-      cFormat.replace(iEnd, wcslen(cTableAd[i].lpszJSMark),
-                      WideString::Format(L"%d", cTableAd[i].iValue).c_str());
-      iStart = iEnd;
+      cFormat.replace(nFound, 1,
+                      WideString::FormatInteger(cTableAd[i].value).c_str());
     }
   }
 
@@ -373,8 +377,9 @@
   WideString sDate = pRuntime->ToWideString(params[1]);
   double dDate = FX_GetDateTime();
   if (sDate.GetLength() > 0)
-    dDate = CJS_PublicMethods::ParseDateUsingFormat(sDate, sFormat, nullptr);
-  if (std::isnan(dDate))
+    dDate = CJS_PublicMethods::ParseDateUsingFormat(pRuntime->GetIsolate(),
+                                                    sDate, sFormat, nullptr);
+  if (isnan(dDate))
     return CJS_Result::Success(pRuntime->NewUndefined());
 
   return CJS_Result::Success(pRuntime->NewDate(dDate));
@@ -395,80 +400,80 @@
 }
 
 // static
-int CJS_Util::ParseDataType(WideString* sFormat) {
-  enum State { BEFORE, FLAGS, WIDTH, PRECISION, SPECIFIER, AFTER };
+CJS_Util::DataType CJS_Util::ParseDataType(WideString* sFormat) {
+  enum State { kBefore, kFlags, kWidth, kPrecision, kSpecifier, kAfter };
 
-  int result = -1;
-  State state = BEFORE;
+  DataType result = DataType::kInvalid;
+  State state = kBefore;
   size_t precision_digits = 0;
   size_t i = 0;
   while (i < sFormat->GetLength()) {
     wchar_t c = (*sFormat)[i];
     switch (state) {
-      case BEFORE:
+      case kBefore:
         if (c == L'%')
-          state = FLAGS;
+          state = kFlags;
         break;
-      case FLAGS:
+      case kFlags:
         if (c == L'+' || c == L'-' || c == L'#' || c == L' ') {
           // Stay in same state.
         } else {
-          state = WIDTH;
+          state = kWidth;
           continue;  // Re-process same character.
         }
         break;
-      case WIDTH:
+      case kWidth:
         if (c == L'*')
-          return -1;
+          return DataType::kInvalid;
         if (FXSYS_IsDecimalDigit(c)) {
           // Stay in same state.
         } else if (c == L'.') {
-          state = PRECISION;
+          state = kPrecision;
         } else {
-          state = SPECIFIER;
+          state = kSpecifier;
           continue;  // Re-process same character.
         }
         break;
-      case PRECISION:
+      case kPrecision:
         if (c == L'*')
-          return -1;
+          return DataType::kInvalid;
         if (FXSYS_IsDecimalDigit(c)) {
           // Stay in same state.
           ++precision_digits;
         } else {
-          state = SPECIFIER;
+          state = kSpecifier;
           continue;  // Re-process same character.
         }
         break;
-      case SPECIFIER:
+      case kSpecifier:
         if (c == L'c' || c == L'C' || c == L'd' || c == L'i' || c == L'o' ||
             c == L'u' || c == L'x' || c == L'X') {
-          result = UTIL_INT;
+          result = DataType::kInt;
         } else if (c == L'e' || c == L'E' || c == L'f' || c == L'g' ||
                    c == L'G') {
-          result = UTIL_DOUBLE;
+          result = DataType::kDouble;
         } else if (c == L's' || c == L'S') {
           // Map s to S since we always deal internally with wchar_t strings.
           // TODO(tsepez): Probably 100% borked. %S is not a standard
           // conversion.
           sFormat->SetAt(i, L'S');
-          result = UTIL_STRING;
+          result = DataType::kString;
         } else {
-          return -1;
+          return DataType::kInvalid;
         }
-        state = AFTER;
+        state = kAfter;
         break;
-      case AFTER:
+      case kAfter:
         if (c == L'%')
-          return -1;
+          return DataType::kInvalid;
         // Stay in same state until string exhausted.
         break;
     }
     ++i;
   }
   // See https://crbug.com/740166
-  if (result == UTIL_INT && precision_digits > 2)
-    return -1;
+  if (result == DataType::kInt && precision_digits > 2)
+    return DataType::kInvalid;
 
   return result;
 }
diff --git a/fxjs/cjs_util.h b/fxjs/cjs_util.h
index 6f55b3a..45e58e0 100644
--- a/fxjs/cjs_util.h
+++ b/fxjs/cjs_util.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,15 +12,18 @@
 #include "core/fxcrt/widestring.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/js_define.h"
-
-// Return values for ParseDataType() below.
-#define UTIL_INT 0
-#define UTIL_DOUBLE 1
-#define UTIL_STRING 2
+#include "v8/include/v8-forward.h"
 
 class CJS_Util final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  enum class DataType {
+    kInvalid = -1,
+    kInt = 0,
+    kDouble = 1,
+    kString = 2,
+  };
+
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
   CJS_Util(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
@@ -33,7 +36,7 @@
   // byte-by-byte.
   //
   // Exposed for testing.
-  static int ParseDataType(WideString* sFormat);
+  static DataType ParseDataType(WideString* sFormat);
 
   // Exposed for testing.
   static WideString StringPrintx(const WideString& cFormat,
@@ -46,7 +49,7 @@
   JS_STATIC_METHOD(byteToChar, CJS_Util)
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSMethodSpec MethodSpecs[];
 
diff --git a/fxjs/cjs_util_unittest.cpp b/fxjs/cjs_util_unittest.cpp
index d405746..b925268 100644
--- a/fxjs/cjs_util_unittest.cpp
+++ b/fxjs/cjs_util_unittest.cpp
@@ -1,110 +1,112 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxjs/cjs_util.h"
 
+#include <iterator>
+
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(CJS_Util, ParseDataType) {
   struct ParseDataTypeCase {
     const wchar_t* const input_string;
-    const int expected;
+    const CJS_Util::DataType expected;
   };
 
   // Commented out tests follow the spec but are not passing.
   const ParseDataTypeCase cases[] = {
       // Not conversions
-      {L"", -1},
-      {L"d", -1},
+      {L"", CJS_Util::DataType::kInvalid},
+      {L"d", CJS_Util::DataType::kInvalid},
 
       // Simple cases
-      {L"%d", UTIL_INT},
-      {L"%x", UTIL_INT},
-      {L"%f", UTIL_DOUBLE},
-      {L"%s", UTIL_STRING},
+      {L"%d", CJS_Util::DataType::kInt},
+      {L"%x", CJS_Util::DataType::kInt},
+      {L"%f", CJS_Util::DataType::kDouble},
+      {L"%s", CJS_Util::DataType::kString},
 
       // nDecSep Not implemented
-      // {L"%,0d", UTIL_INT},
-      // {L"%,1d", UTIL_INT},
-      // {L"%,2d", UTIL_INT},
-      // {L"%,3d", UTIL_INT},
+      // {L"%,0d", CJS_Util::DataType::kInt},
+      // {L"%,1d", CJS_Util::DataType::kInt},
+      // {L"%,2d", CJS_Util::DataType::kInt},
+      // {L"%,3d", CJS_Util::DataType::kInt},
       // {L"%,4d", -1},
       // {L"%,d", -1},
 
       // cFlags("+ 0#"") are only valid for numeric conversions.
-      {L"%+d", UTIL_INT},
-      {L"%+x", UTIL_INT},
-      {L"%+f", UTIL_DOUBLE},
+      {L"%+d", CJS_Util::DataType::kInt},
+      {L"%+x", CJS_Util::DataType::kInt},
+      {L"%+f", CJS_Util::DataType::kDouble},
       // {L"%+s", -1},
-      {L"% d", UTIL_INT},
-      {L"% x", UTIL_INT},
-      {L"% f", UTIL_DOUBLE},
+      {L"% d", CJS_Util::DataType::kInt},
+      {L"% x", CJS_Util::DataType::kInt},
+      {L"% f", CJS_Util::DataType::kDouble},
       // {L"% s", -1},
-      {L"%0d", UTIL_INT},
-      {L"%0x", UTIL_INT},
-      {L"%0f", UTIL_DOUBLE},
+      {L"%0d", CJS_Util::DataType::kInt},
+      {L"%0x", CJS_Util::DataType::kInt},
+      {L"%0f", CJS_Util::DataType::kDouble},
       // {L"%0s", -1},
-      {L"%#d", UTIL_INT},
-      {L"%#x", UTIL_INT},
-      {L"%#f", UTIL_DOUBLE},
+      {L"%#d", CJS_Util::DataType::kInt},
+      {L"%#x", CJS_Util::DataType::kInt},
+      {L"%#f", CJS_Util::DataType::kDouble},
       // {L"%#s", -1},
 
       // nWidth should work. for all conversions, can be combined with cFlags=0
       // for numbers.
-      {L"%5d", UTIL_INT},
-      {L"%05d", UTIL_INT},
-      {L"%5x", UTIL_INT},
-      {L"%05x", UTIL_INT},
-      {L"%5f", UTIL_DOUBLE},
-      {L"%05f", UTIL_DOUBLE},
-      {L"%5s", UTIL_STRING},
+      {L"%5d", CJS_Util::DataType::kInt},
+      {L"%05d", CJS_Util::DataType::kInt},
+      {L"%5x", CJS_Util::DataType::kInt},
+      {L"%05x", CJS_Util::DataType::kInt},
+      {L"%5f", CJS_Util::DataType::kDouble},
+      {L"%05f", CJS_Util::DataType::kDouble},
+      {L"%5s", CJS_Util::DataType::kString},
       // {L"%05s", -1},
 
       // nPrecision should only work for float
       // {L"%.5d", -1},
       // {L"%.5x", -1},
-      {L"%.5f", UTIL_DOUBLE},
+      {L"%.5f", CJS_Util::DataType::kDouble},
       // {L"%.5s", -1},
       // {L"%.14d", -1},
       // {L"%.14x", -1},
-      {L"%.14f", UTIL_DOUBLE},
+      {L"%.14f", CJS_Util::DataType::kDouble},
       // {L"%.14s", -1},
       // {L"%.f", -1},
 
       // See https://crbug.com/740166
       // nPrecision too large (> 260) causes crashes in Windows.
       // Avoid this by limiting to two digits
-      {L"%.1d", UTIL_INT},
-      {L"%.10d", UTIL_INT},
-      {L"%.100d", -1},
+      {L"%.1d", CJS_Util::DataType::kInt},
+      {L"%.10d", CJS_Util::DataType::kInt},
+      {L"%.100d", CJS_Util::DataType::kInvalid},
 
       // Unexpected characters
-      {L"%ad", -1},
-      {L"%bx", -1},
-      // {L"%cf", -1},
-      // {L"%es", -1},
-      // {L"%gd", -1},
-      {L"%hx", -1},
-      // {L"%if", -1},
-      {L"%js", -1},
-      {L"%@d", -1},
-      {L"%~x", -1},
-      {L"%[f", -1},
-      {L"%\0s", -1},
-      {L"%\nd", -1},
-      {L"%\rx", -1},
-      // {L"%%f", -1},
-      // {L"%  s", -1},
+      {L"%ad", CJS_Util::DataType::kInvalid},
+      {L"%bx", CJS_Util::DataType::kInvalid},
+      // {L"%cf", CJS_Util::DataType::kInvalid},
+      // {L"%es", CJS_Util::DataType::kInvalid},
+      // {L"%gd", CJS_Util::DataType::kInvalid},
+      {L"%hx", CJS_Util::DataType::kInvalid},
+      // {L"%if", CJS_Util::DataType::kInvalid},
+      {L"%js", CJS_Util::DataType::kInvalid},
+      {L"%@d", CJS_Util::DataType::kInvalid},
+      {L"%~x", CJS_Util::DataType::kInvalid},
+      {L"%[f", CJS_Util::DataType::kInvalid},
+      {L"%\0s", CJS_Util::DataType::kInvalid},
+      {L"%\nd", CJS_Util::DataType::kInvalid},
+      {L"%\rx", CJS_Util::DataType::kInvalid},
+      // {L"%%f", CJS_Util::DataType::kInvalid},
+      // {L"%  s", CJS_Util::DataType::kInvalid},
 
       // Combine multiple valid components
-      {L"%+6d", UTIL_INT},
-      {L"% 7x", UTIL_INT},
-      {L"%#9.3f", UTIL_DOUBLE},
-      {L"%10s", UTIL_STRING},
+      {L"%+6d", CJS_Util::DataType::kInt},
+      {L"% 7x", CJS_Util::DataType::kInt},
+      {L"%#9.3f", CJS_Util::DataType::kDouble},
+      {L"%10s", CJS_Util::DataType::kString},
   };
 
-  for (size_t i = 0; i < FX_ArraySize(cases); i++) {
+  for (size_t i = 0; i < std::size(cases); i++) {
     WideString input(cases[i].input_string);
     EXPECT_EQ(cases[i].expected, CJS_Util::ParseDataType(&input))
         << cases[i].input_string;
diff --git a/fxjs/cjs_zoomtype.cpp b/fxjs/cjs_zoomtype.cpp
index cdaa2d5..b978468 100644
--- a/fxjs/cjs_zoomtype.cpp
+++ b/fxjs/cjs_zoomtype.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,7 +15,7 @@
     {"pref", JSConstSpec::String, 0, "Preferred"},
     {"refW", JSConstSpec::String, 0, "ReflowWidth"}};
 
-int CJS_Zoomtype::ObjDefnID = -1;
+uint32_t CJS_Zoomtype::ObjDefnID = 0;
 
 // static
 void CJS_Zoomtype::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_zoomtype.h b/fxjs/cjs_zoomtype.h
index de268cd..2efd49a 100644
--- a/fxjs/cjs_zoomtype.h
+++ b/fxjs/cjs_zoomtype.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_Zoomtype() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/fx_date_helpers.cpp b/fxjs/fx_date_helpers.cpp
index 5256fb1..3d7d1f3 100644
--- a/fxjs/fx_date_helpers.cpp
+++ b/fxjs/fx_date_helpers.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,11 @@
 
 #include "fxjs/fx_date_helpers.h"
 
+#include <math.h>
 #include <time.h>
+#include <wctype.h>
 
-#include <cmath>
+#include <iterator>
 
 #include "build/build_config.h"
 #include "core/fxcrt/fx_extension.h"
@@ -36,7 +38,7 @@
   time_t t = 0;
   FXSYS_time(&t);
   FXSYS_localtime(&t);
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // In gcc 'timezone' is a global variable declared in time.h. In VC++, that
   // variable was removed in VC++ 2015, with _get_timezone replacing it.
   long timezone = 0;
@@ -113,9 +115,9 @@
   // Check for February onwards.
   static constexpr int kCumulativeDaysInMonths[] = {
       59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
-  for (size_t i = 0; i < FX_ArraySize(kCumulativeDaysInMonths); ++i) {
+  for (size_t i = 0; i < std::size(kCumulativeDaysInMonths); ++i) {
     if (day < kCumulativeDaysInMonths[i])
-      return i + 1;
+      return static_cast<int>(i) + 1;
   }
 
   return -1;
@@ -159,7 +161,7 @@
 size_t FindSubWordLength(const WideString& str, size_t nStart) {
   pdfium::span<const wchar_t> data = str.span();
   size_t i = nStart;
-  while (i < data.size() && std::iswalnum(data[i]))
+  while (i < data.size() && iswalnum(data[i]))
     ++i;
   return i - nStart;
 }
@@ -247,7 +249,7 @@
   double mn = Mod(m, 12);
   double t = TimeFromYearMonth(static_cast<int>(ym), static_cast<int>(mn));
   if (YearFromTime(t) != ym || MonthFromTime(t) != mn || DateFromTime(t) != 1)
-    return std::nan("");
+    return nan("");
 
   return Day(t) + dt - 1;
 }
@@ -261,8 +263,8 @@
 }
 
 double FX_MakeDate(double day, double time) {
-  if (!std::isfinite(day) || !std::isfinite(time))
-    return std::nan("");
+  if (!isfinite(day) || !isfinite(time))
+    return nan("");
 
   return day * 86400000 + time;
 }
@@ -433,9 +435,9 @@
               nSkip = FindSubWordLength(value, j);
               if (nSkip == KMonthAbbreviationLength) {
                 WideString sMonth = value.Substr(j, KMonthAbbreviationLength);
-                for (size_t m = 0; m < FX_ArraySize(kMonths); ++m) {
+                for (size_t m = 0; m < std::size(kMonths); ++m) {
                   if (sMonth.CompareNoCase(kMonths[m]) == 0) {
-                    nMonth = m + 1;
+                    nMonth = static_cast<int>(m) + 1;
                     i += 3;
                     j += nSkip;
                     bFind = true;
@@ -470,11 +472,11 @@
               if (nSkip <= kLongestFullMonthLength) {
                 WideString sMonth = value.Substr(j, nSkip);
                 sMonth.MakeLower();
-                for (size_t m = 0; m < FX_ArraySize(kFullMonths); ++m) {
+                for (size_t m = 0; m < std::size(kFullMonths); ++m) {
                   WideString sFullMonths = WideString(kFullMonths[m]);
                   sFullMonths.MakeLower();
-                  if (sFullMonths.Contains(sMonth.c_str())) {
-                    nMonth = m + 1;
+                  if (sFullMonths.Contains(sMonth.AsStringView())) {
+                    nMonth = static_cast<int>(m) + 1;
                     i += 4;
                     j += nSkip;
                     bFind = true;
@@ -482,7 +484,6 @@
                   }
                 }
               }
-
               if (!bFind) {
                 nMonth = FX_ParseStringInteger(value, j, &nSkip, 4);
                 i += 4;
@@ -541,7 +542,7 @@
 
   dt = FX_MakeDate(FX_MakeDay(nYear, nMonth - 1, nDay),
                    FX_MakeTime(nHour, nMin, nSec, 0));
-  if (std::isnan(dt))
+  if (isnan(dt))
     return ConversionStatus::kBadDate;
 
   *result = dt;
diff --git a/fxjs/fx_date_helpers.h b/fxjs/fx_date_helpers.h
index 3657694..6f6f403 100644
--- a/fxjs/fx_date_helpers.h
+++ b/fxjs/fx_date_helpers.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 
 #include <stddef.h>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 
 namespace fxjs {
 
diff --git a/fxjs/fx_date_helpers_unittest.cpp b/fxjs/fx_date_helpers_unittest.cpp
index fb756b9..cc95719 100644
--- a/fxjs/fx_date_helpers_unittest.cpp
+++ b/fxjs/fx_date_helpers_unittest.cpp
@@ -1,9 +1,10 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxjs/fx_date_helpers.h"
 
+#include "core/fxcrt/fake_time_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -12,6 +13,8 @@
 
 }  // namespace
 
+using fxjs::ConversionStatus;
+
 TEST(FX_DateHelper, GetYearFromTime) {
   static constexpr struct {
     double time_ms;
@@ -106,3 +109,47 @@
         << test.time_ms;
   }
 }
+
+using FXDateHelperFakeTimeTest = FakeTimeTest;
+
+TEST_F(FXDateHelperFakeTimeTest, ParseDateUsingFormatWithEmptyParams) {
+  double result = 0.0;
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"", L"", &result));
+  EXPECT_DOUBLE_EQ(1'587'654'321'000, result);
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"value", L"", &result));
+  EXPECT_DOUBLE_EQ(1'587'654'321'000, result);
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"", L"format", &result));
+  EXPECT_DOUBLE_EQ(1'587'654'321'000, result);
+}
+
+TEST_F(FXDateHelperFakeTimeTest, ParseDateUsingFormatForValidMonthDay) {
+  double result = 0.0;
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"01/02/2000", L"mm/dd/yyyy", &result));
+  EXPECT_DOUBLE_EQ(946'825'521'000, result);
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"1/2/2000", L"m/d/yyyy", &result));
+  EXPECT_DOUBLE_EQ(946'825'521'000, result);
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"1-2-2000", L"m-d-yyyy", &result));
+  EXPECT_DOUBLE_EQ(946'825'521'000, result);
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"2-1-2000", L"d-m-yyyy", &result));
+  EXPECT_DOUBLE_EQ(946'825'521'000, result);
+
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"11/12/2000", L"mm/dd/yyyy", &result));
+  EXPECT_DOUBLE_EQ(973'955'121'000, result);
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"11/12/2000", L"m/d/yyyy", &result));
+  EXPECT_DOUBLE_EQ(973'955'121'000, result);
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"11-12-2000", L"m-d-yyyy", &result));
+  EXPECT_DOUBLE_EQ(973'955'121'000, result);
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"12-11-2000", L"d-m-yyyy", &result));
+  EXPECT_DOUBLE_EQ(973'955'121'000, result);
+}
diff --git a/fxjs/fxv8.cpp b/fxjs/fxv8.cpp
new file mode 100644
index 0000000..bbf1ec6
--- /dev/null
+++ b/fxjs/fxv8.cpp
@@ -0,0 +1,337 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/fxv8.h"
+
+#include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/v8-container.h"
+#include "v8/include/v8-date.h"
+#include "v8/include/v8-exception.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-primitive.h"
+#include "v8/include/v8-value.h"
+
+namespace fxv8 {
+
+bool IsUndefined(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsUndefined();
+}
+
+bool IsNull(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsNull();
+}
+
+bool IsBoolean(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsBoolean();
+}
+
+bool IsString(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsString();
+}
+
+bool IsNumber(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsNumber();
+}
+
+bool IsInteger(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsInt32();
+}
+
+bool IsObject(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsObject();
+}
+
+bool IsArray(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsArray();
+}
+
+bool IsDate(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsDate();
+}
+
+bool IsFunction(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsFunction();
+}
+
+v8::Local<v8::Value> NewNullHelper(v8::Isolate* pIsolate) {
+  return v8::Null(pIsolate);
+}
+
+v8::Local<v8::Value> NewUndefinedHelper(v8::Isolate* pIsolate) {
+  return v8::Undefined(pIsolate);
+}
+
+v8::Local<v8::Number> NewNumberHelper(v8::Isolate* pIsolate, int number) {
+  return v8::Int32::New(pIsolate, number);
+}
+
+v8::Local<v8::Number> NewNumberHelper(v8::Isolate* pIsolate, double number) {
+  return v8::Number::New(pIsolate, number);
+}
+
+v8::Local<v8::Number> NewNumberHelper(v8::Isolate* pIsolate, float number) {
+  return v8::Number::New(pIsolate, number);
+}
+
+v8::Local<v8::Boolean> NewBooleanHelper(v8::Isolate* pIsolate, bool b) {
+  return v8::Boolean::New(pIsolate, b);
+}
+
+v8::Local<v8::String> NewStringHelper(v8::Isolate* pIsolate,
+                                      ByteStringView str) {
+  return v8::String::NewFromUtf8(
+             pIsolate, str.unterminated_c_str(), v8::NewStringType::kNormal,
+             pdfium::base::checked_cast<int>(str.GetLength()))
+      .ToLocalChecked();
+}
+
+v8::Local<v8::String> NewStringHelper(v8::Isolate* pIsolate,
+                                      WideStringView str) {
+  return NewStringHelper(pIsolate, FX_UTF8Encode(str).AsStringView());
+}
+
+v8::Local<v8::Array> NewArrayHelper(v8::Isolate* pIsolate) {
+  return v8::Array::New(pIsolate);
+}
+
+v8::Local<v8::Array> NewArrayHelper(v8::Isolate* pIsolate,
+                                    pdfium::span<v8::Local<v8::Value>> values) {
+  v8::Local<v8::Array> result = NewArrayHelper(pIsolate);
+  for (size_t i = 0; i < values.size(); ++i) {
+    fxv8::ReentrantPutArrayElementHelper(
+        pIsolate, result, i,
+        values[i].IsEmpty() ? fxv8::NewUndefinedHelper(pIsolate) : values[i]);
+  }
+  return result;
+}
+
+v8::Local<v8::Object> NewObjectHelper(v8::Isolate* pIsolate) {
+  return v8::Object::New(pIsolate);
+}
+
+v8::Local<v8::Date> NewDateHelper(v8::Isolate* pIsolate, double d) {
+  return v8::Date::New(pIsolate->GetCurrentContext(), d)
+      .ToLocalChecked()
+      .As<v8::Date>();
+}
+
+WideString ToWideString(v8::Isolate* pIsolate, v8::Local<v8::String> pValue) {
+  v8::String::Utf8Value s(pIsolate, pValue);
+  return WideString::FromUTF8(ByteStringView(*s, s.length()));
+}
+
+ByteString ToByteString(v8::Isolate* pIsolate, v8::Local<v8::String> pValue) {
+  v8::String::Utf8Value s(pIsolate, pValue);
+  return ByteString(*s, s.length());
+}
+
+int ReentrantToInt32Helper(v8::Isolate* pIsolate, v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty())
+    return 0;
+  v8::TryCatch squash_exceptions(pIsolate);
+  return pValue->Int32Value(pIsolate->GetCurrentContext()).FromMaybe(0);
+}
+
+bool ReentrantToBooleanHelper(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty())
+    return false;
+  v8::TryCatch squash_exceptions(pIsolate);
+  return pValue->BooleanValue(pIsolate);
+}
+
+float ReentrantToFloatHelper(v8::Isolate* pIsolate,
+                             v8::Local<v8::Value> pValue) {
+  return static_cast<float>(ReentrantToDoubleHelper(pIsolate, pValue));
+}
+
+double ReentrantToDoubleHelper(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty())
+    return 0.0;
+  v8::TryCatch squash_exceptions(pIsolate);
+  return pValue->NumberValue(pIsolate->GetCurrentContext()).FromMaybe(0.0);
+}
+
+WideString ReentrantToWideStringHelper(v8::Isolate* pIsolate,
+                                       v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty())
+    return WideString();
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::MaybeLocal<v8::String> maybe_string =
+      pValue->ToString(pIsolate->GetCurrentContext());
+  if (maybe_string.IsEmpty())
+    return WideString();
+
+  return ToWideString(pIsolate, maybe_string.ToLocalChecked());
+}
+
+ByteString ReentrantToByteStringHelper(v8::Isolate* pIsolate,
+                                       v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty())
+    return ByteString();
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::MaybeLocal<v8::String> maybe_string =
+      pValue->ToString(pIsolate->GetCurrentContext());
+  if (maybe_string.IsEmpty())
+    return ByteString();
+
+  return ToByteString(pIsolate, maybe_string.ToLocalChecked());
+}
+
+v8::Local<v8::Object> ReentrantToObjectHelper(v8::Isolate* pIsolate,
+                                              v8::Local<v8::Value> pValue) {
+  if (!fxv8::IsObject(pValue))
+    return v8::Local<v8::Object>();
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
+  return pValue->ToObject(context).ToLocalChecked();
+}
+
+v8::Local<v8::Array> ReentrantToArrayHelper(v8::Isolate* pIsolate,
+                                            v8::Local<v8::Value> pValue) {
+  if (!fxv8::IsArray(pValue))
+    return v8::Local<v8::Array>();
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
+  return v8::Local<v8::Array>::Cast(pValue->ToObject(context).ToLocalChecked());
+}
+
+v8::Local<v8::Value> ReentrantGetObjectPropertyHelper(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Object> pObj,
+    ByteStringView bsUTF8PropertyName) {
+  if (pObj.IsEmpty())
+    return v8::Local<v8::Value>();
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Local<v8::Value> val;
+  if (!pObj->Get(pIsolate->GetCurrentContext(),
+                 NewStringHelper(pIsolate, bsUTF8PropertyName))
+           .ToLocal(&val)) {
+    return v8::Local<v8::Value>();
+  }
+  return val;
+}
+
+std::vector<WideString> ReentrantGetObjectPropertyNamesHelper(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Object> pObj) {
+  if (pObj.IsEmpty())
+    return std::vector<WideString>();
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Local<v8::Array> val;
+  v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
+  if (!pObj->GetPropertyNames(context).ToLocal(&val))
+    return std::vector<WideString>();
+
+  std::vector<WideString> result;
+  for (uint32_t i = 0; i < val->Length(); ++i) {
+    result.push_back(ReentrantToWideStringHelper(
+        pIsolate, val->Get(context, i).ToLocalChecked()));
+  }
+  return result;
+}
+
+bool ReentrantHasObjectOwnPropertyHelper(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Object> pObj,
+                                         ByteStringView bsUTF8PropertyName) {
+  if (pObj.IsEmpty())
+    return false;
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Local<v8::Context> pContext = pIsolate->GetCurrentContext();
+  v8::Local<v8::String> hKey =
+      fxv8::NewStringHelper(pIsolate, bsUTF8PropertyName);
+  return pObj->HasRealNamedProperty(pContext, hKey).FromJust();
+}
+
+bool ReentrantSetObjectOwnPropertyHelper(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Object> pObj,
+                                         ByteStringView bsUTF8PropertyName,
+                                         v8::Local<v8::Value> pValue) {
+  if (pObj.IsEmpty() || pValue.IsEmpty())
+    return false;
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Local<v8::String> name = NewStringHelper(pIsolate, bsUTF8PropertyName);
+  return pObj->DefineOwnProperty(pIsolate->GetCurrentContext(), name, pValue)
+      .FromMaybe(false);
+}
+
+bool ReentrantPutObjectPropertyHelper(v8::Isolate* pIsolate,
+                                      v8::Local<v8::Object> pObj,
+                                      ByteStringView bsUTF8PropertyName,
+                                      v8::Local<v8::Value> pPut) {
+  if (pObj.IsEmpty() || pPut.IsEmpty())
+    return false;
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Local<v8::String> name = NewStringHelper(pIsolate, bsUTF8PropertyName);
+  v8::Maybe<bool> result = pObj->Set(pIsolate->GetCurrentContext(), name, pPut);
+  return result.IsJust() && result.FromJust();
+}
+
+void ReentrantDeleteObjectPropertyHelper(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Object> pObj,
+                                         ByteStringView bsUTF8PropertyName) {
+  v8::TryCatch squash_exceptions(pIsolate);
+  pObj->Delete(pIsolate->GetCurrentContext(),
+               fxv8::NewStringHelper(pIsolate, bsUTF8PropertyName))
+      .FromJust();
+}
+
+bool ReentrantPutArrayElementHelper(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Array> pArray,
+                                    size_t index,
+                                    v8::Local<v8::Value> pValue) {
+  if (pArray.IsEmpty())
+    return false;
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Maybe<bool> result =
+      pArray->Set(pIsolate->GetCurrentContext(),
+                  pdfium::base::checked_cast<uint32_t>(index), pValue);
+  return result.IsJust() && result.FromJust();
+}
+
+v8::Local<v8::Value> ReentrantGetArrayElementHelper(v8::Isolate* pIsolate,
+                                                    v8::Local<v8::Array> pArray,
+                                                    size_t index) {
+  if (pArray.IsEmpty())
+    return v8::Local<v8::Value>();
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Local<v8::Value> val;
+  if (!pArray
+           ->Get(pIsolate->GetCurrentContext(),
+                 pdfium::base::checked_cast<uint32_t>(index))
+           .ToLocal(&val)) {
+    return v8::Local<v8::Value>();
+  }
+  return val;
+}
+
+size_t GetArrayLengthHelper(v8::Local<v8::Array> pArray) {
+  if (pArray.IsEmpty())
+    return 0;
+  return pArray->Length();
+}
+
+void ThrowExceptionHelper(v8::Isolate* pIsolate, ByteStringView str) {
+  pIsolate->ThrowException(NewStringHelper(pIsolate, str));
+}
+
+void ThrowExceptionHelper(v8::Isolate* pIsolate, WideStringView str) {
+  pIsolate->ThrowException(NewStringHelper(pIsolate, str));
+}
+
+}  // namespace fxv8
diff --git a/fxjs/fxv8.h b/fxjs/fxv8.h
new file mode 100644
index 0000000..62c9bb3
--- /dev/null
+++ b/fxjs/fxv8.h
@@ -0,0 +1,110 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_FXV8_H_
+#define FXJS_FXV8_H_
+
+#include <stddef.h>
+
+#include <vector>
+
+#include "core/fxcrt/fx_string.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,
+// PDFium uses size_t for sizes and indices, but V8 mostly uses ints, so
+// these routines perform checked conversions.
+
+namespace fxv8 {
+
+// These first check for empty locals.
+bool IsUndefined(v8::Local<v8::Value> value);
+bool IsNull(v8::Local<v8::Value> value);
+bool IsBoolean(v8::Local<v8::Value> value);
+bool IsString(v8::Local<v8::Value> value);
+bool IsNumber(v8::Local<v8::Value> value);
+bool IsInteger(v8::Local<v8::Value> value);
+bool IsObject(v8::Local<v8::Value> value);
+bool IsArray(v8::Local<v8::Value> value);
+bool IsDate(v8::Local<v8::Value> value);
+bool IsFunction(v8::Local<v8::Value> value);
+
+v8::Local<v8::Value> NewNullHelper(v8::Isolate* pIsolate);
+v8::Local<v8::Value> NewUndefinedHelper(v8::Isolate* pIsolate);
+v8::Local<v8::Number> NewNumberHelper(v8::Isolate* pIsolate, int number);
+v8::Local<v8::Number> NewNumberHelper(v8::Isolate* pIsolate, double number);
+v8::Local<v8::Number> NewNumberHelper(v8::Isolate* pIsolate, float number);
+v8::Local<v8::Boolean> NewBooleanHelper(v8::Isolate* pIsolate, bool b);
+v8::Local<v8::String> NewStringHelper(v8::Isolate* pIsolate,
+                                      ByteStringView str);
+v8::Local<v8::String> NewStringHelper(v8::Isolate* pIsolate,
+                                      WideStringView str);
+v8::Local<v8::Array> NewArrayHelper(v8::Isolate* pIsolate);
+v8::Local<v8::Array> NewArrayHelper(v8::Isolate* pIsolate,
+                                    pdfium::span<v8::Local<v8::Value>> values);
+v8::Local<v8::Object> NewObjectHelper(v8::Isolate* pIsolate);
+v8::Local<v8::Date> NewDateHelper(v8::Isolate* pIsolate, double d);
+
+// Conversion to PDFium type without re-entry from known v8 type.
+WideString ToWideString(v8::Isolate* pIsolate, v8::Local<v8::String> pValue);
+ByteString ToByteString(v8::Isolate* pIsolate, v8::Local<v8::String> pValue);
+
+// Conversion to PDFium type with possible re-entry for coercion.
+int32_t ReentrantToInt32Helper(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value> pValue);
+bool ReentrantToBooleanHelper(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value> pValue);
+float ReentrantToFloatHelper(v8::Isolate* pIsolate,
+                             v8::Local<v8::Value> pValue);
+double ReentrantToDoubleHelper(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value> pValue);
+WideString ReentrantToWideStringHelper(v8::Isolate* pIsolate,
+                                       v8::Local<v8::Value> pValue);
+ByteString ReentrantToByteStringHelper(v8::Isolate* pIsolate,
+                                       v8::Local<v8::Value> pValue);
+v8::Local<v8::Object> ReentrantToObjectHelper(v8::Isolate* pIsolate,
+                                              v8::Local<v8::Value> pValue);
+v8::Local<v8::Array> ReentrantToArrayHelper(v8::Isolate* pIsolate,
+                                            v8::Local<v8::Value> pValue);
+
+v8::Local<v8::Value> ReentrantGetObjectPropertyHelper(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Object> pObj,
+    ByteStringView bsUTF8PropertyName);
+std::vector<WideString> ReentrantGetObjectPropertyNamesHelper(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Object> pObj);
+bool ReentrantHasObjectOwnPropertyHelper(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Object> pObj,
+                                         ByteStringView bsUTF8PropertyName);
+bool ReentrantSetObjectOwnPropertyHelper(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Object> pObj,
+                                         ByteStringView bsUTF8PropertyName,
+                                         v8::Local<v8::Value> pValue);
+bool ReentrantPutObjectPropertyHelper(v8::Isolate* pIsolate,
+                                      v8::Local<v8::Object> pObj,
+                                      ByteStringView bsUTF8PropertyName,
+                                      v8::Local<v8::Value> pPut);
+void ReentrantDeleteObjectPropertyHelper(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Object> pObj,
+                                         ByteStringView bsUTF8PropertyName);
+
+bool ReentrantPutArrayElementHelper(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Array> pArray,
+                                    size_t index,
+                                    v8::Local<v8::Value> pValue);
+v8::Local<v8::Value> ReentrantGetArrayElementHelper(v8::Isolate* pIsolate,
+                                                    v8::Local<v8::Array> pArray,
+                                                    size_t index);
+size_t GetArrayLengthHelper(v8::Local<v8::Array> pArray);
+
+void ThrowExceptionHelper(v8::Isolate* pIsolate, ByteStringView str);
+void ThrowExceptionHelper(v8::Isolate* pIsolate, WideStringView str);
+
+}  // namespace fxv8
+
+#endif  // FXJS_FXV8_H_
diff --git a/fxjs/gc/container_trace.h b/fxjs/gc/container_trace.h
new file mode 100644
index 0000000..dfe4a74
--- /dev/null
+++ b/fxjs/gc/container_trace.h
@@ -0,0 +1,66 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FXJS_GC_CONTAINER_TRACE_H_
+#define FXJS_GC_CONTAINER_TRACE_H_
+
+#include <list>
+#include <map>
+#include <set>
+#include <vector>
+
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
+
+namespace fxgc {
+
+template <typename T, typename V = cppgc::Visitor>
+void ContainerTrace(V* visitor, const std::list<cppgc::Member<T>>& container) {
+  for (const auto& item : container)
+    visitor->Trace(item);
+}
+
+template <typename T, typename U, typename V = cppgc::Visitor>
+void ContainerTrace(V* visitor,
+                    const std::map<cppgc::Member<T>, U>& container) {
+  for (const auto& item : container) {
+    visitor->Trace(item.first);
+  }
+}
+
+template <typename T, typename U, typename V = cppgc::Visitor>
+void ContainerTrace(V* visitor,
+                    const std::map<U, cppgc::Member<T>>& container) {
+  for (const auto& item : container)
+    visitor->Trace(item.second);
+}
+
+template <typename T, typename U, typename V = cppgc::Visitor>
+void ContainerTrace(
+    V* visitor,
+    const std::map<cppgc::Member<U>, cppgc::Member<T>>& container) {
+  for (const auto& item : container) {
+    visitor->Trace(item.first);
+    visitor->Trace(item.second);
+  }
+}
+
+template <typename T, typename V = cppgc::Visitor>
+void ContainerTrace(V* visitor, const std::set<cppgc::Member<T>>& container) {
+  for (const auto& item : container)
+    visitor->Trace(item);
+}
+
+template <typename T, typename V = cppgc::Visitor>
+void ContainerTrace(V* visitor,
+                    const std::vector<cppgc::Member<T>>& container) {
+  for (const auto& item : container)
+    visitor->Trace(item);
+}
+
+}  // namespace fxgc
+
+using fxgc::ContainerTrace;
+
+#endif  // FXJS_GC_CONTAINER_TRACE_H_
diff --git a/fxjs/gc/container_trace_unittest.cpp b/fxjs/gc/container_trace_unittest.cpp
new file mode 100644
index 0000000..9273afe
--- /dev/null
+++ b/fxjs/gc/container_trace_unittest.cpp
@@ -0,0 +1,89 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/gc/container_trace.h"
+
+#include <stdint.h>
+
+#include <list>
+#include <map>
+#include <set>
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "v8/include/cppgc/member.h"
+
+namespace {
+
+class Thing : public cppgc::GarbageCollected<Thing> {
+ public:
+  void Trace(cppgc::Visitor* visitor) const {}
+};
+
+class CountingVisitor {
+ public:
+  CountingVisitor() = default;
+
+  void Trace(const void* that) { ++call_count_; }
+  int call_count() const { return call_count_; }
+
+ private:
+  int call_count_ = 0;
+};
+
+}  // namespace
+
+TEST(ContainerTrace, ActualListTrace) {
+  std::list<cppgc::Member<Thing>> thing;
+  thing.emplace_back(nullptr);
+
+  CountingVisitor cv;
+  ContainerTrace(&cv, thing);
+  EXPECT_EQ(1, cv.call_count());
+}
+
+TEST(ContainerTrace, ActualMapTraceFirst) {
+  std::map<cppgc::Member<Thing>, int> thing;
+  thing[nullptr] = 42;
+
+  CountingVisitor cv;
+  ContainerTrace(&cv, thing);
+  EXPECT_EQ(1, cv.call_count());
+}
+
+TEST(ContainerTrace, ActualMapTraceSecond) {
+  std::map<int, cppgc::Member<Thing>> thing;
+  thing[42] = nullptr;
+
+  CountingVisitor cv;
+  ContainerTrace(&cv, thing);
+  EXPECT_EQ(1, cv.call_count());
+}
+
+TEST(ContainerTrace, ActualMapTraceBoth) {
+  std::map<cppgc::Member<Thing>, cppgc::Member<Thing>> thing;
+  thing[nullptr] = nullptr;
+
+  CountingVisitor cv;
+  ContainerTrace(&cv, thing);
+  EXPECT_EQ(2, cv.call_count());
+}
+
+TEST(ContainerTrace, ActualSetTrace) {
+  std::set<cppgc::Member<Thing>> thing;
+  thing.insert(nullptr);
+
+  CountingVisitor cv;
+  ContainerTrace(&cv, thing);
+  EXPECT_EQ(1, cv.call_count());
+}
+
+TEST(ContainerTrace, ActualVectorTrace) {
+  std::vector<cppgc::Member<Thing>> thing;
+  thing.emplace_back(nullptr);
+
+  CountingVisitor cv;
+  ContainerTrace(&cv, thing);
+  EXPECT_EQ(1, cv.call_count());
+}
diff --git a/fxjs/gc/gced_tree_node.h b/fxjs/gc/gced_tree_node.h
new file mode 100644
index 0000000..fed5e73
--- /dev/null
+++ b/fxjs/gc/gced_tree_node.h
@@ -0,0 +1,47 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FXJS_GC_GCED_TREE_NODE_H_
+#define FXJS_GC_GCED_TREE_NODE_H_
+
+#include "core/fxcrt/tree_node.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
+
+namespace fxjs {
+
+// For DOM/XML-ish trees, where references outside the tree are Persistent<>.
+template <typename T>
+class GCedTreeNode : public cppgc::GarbageCollected<GCedTreeNode<T>>,
+                     public fxcrt::TreeNodeBase<T> {
+ public:
+  virtual void Trace(cppgc::Visitor* visitor) const {
+    visitor->Trace(m_pParent);
+    visitor->Trace(m_pFirstChild);
+    visitor->Trace(m_pLastChild);
+    visitor->Trace(m_pNextSibling);
+    visitor->Trace(m_pPrevSibling);
+  }
+
+ protected:
+  GCedTreeNode() = default;
+  GCedTreeNode(const GCedTreeNode& that) = delete;
+  GCedTreeNode& operator=(const GCedTreeNode& that) = delete;
+
+ private:
+  friend class fxcrt::TreeNodeBase<T>;
+
+  cppgc::Member<T> m_pParent;
+  cppgc::Member<T> m_pFirstChild;
+  cppgc::Member<T> m_pLastChild;
+  cppgc::Member<T> m_pNextSibling;
+  cppgc::Member<T> m_pPrevSibling;
+};
+
+}  // namespace fxjs
+
+using fxjs::GCedTreeNode;
+
+#endif  // FXJS_GC_GCED_TREE_NODE_H_
diff --git a/fxjs/gc/gced_tree_node_mixin.h b/fxjs/gc/gced_tree_node_mixin.h
new file mode 100644
index 0000000..2f160e0
--- /dev/null
+++ b/fxjs/gc/gced_tree_node_mixin.h
@@ -0,0 +1,48 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FXJS_GC_GCED_TREE_NODE_MIXIN_H_
+#define FXJS_GC_GCED_TREE_NODE_MIXIN_H_
+
+#include "core/fxcrt/tree_node.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
+
+namespace fxjs {
+
+// For DOM/XML-ish trees, where references outside the tree are Persistent<>,
+// usable by classes that are already garbage collected themselves.
+template <typename T>
+class GCedTreeNodeMixin : public cppgc::GarbageCollectedMixin,
+                          public fxcrt::TreeNodeBase<T> {
+ public:
+  virtual void Trace(cppgc::Visitor* visitor) const {
+    visitor->Trace(m_pParent);
+    visitor->Trace(m_pFirstChild);
+    visitor->Trace(m_pLastChild);
+    visitor->Trace(m_pNextSibling);
+    visitor->Trace(m_pPrevSibling);
+  }
+
+ protected:
+  GCedTreeNodeMixin() = default;
+  GCedTreeNodeMixin(const GCedTreeNodeMixin& that) = delete;
+  GCedTreeNodeMixin& operator=(const GCedTreeNodeMixin& that) = delete;
+
+ private:
+  friend class fxcrt::TreeNodeBase<T>;
+
+  cppgc::Member<T> m_pParent;
+  cppgc::Member<T> m_pFirstChild;
+  cppgc::Member<T> m_pLastChild;
+  cppgc::Member<T> m_pNextSibling;
+  cppgc::Member<T> m_pPrevSibling;
+};
+
+}  // namespace fxjs
+
+using fxjs::GCedTreeNodeMixin;
+
+#endif  // FXJS_GC_GCED_TREE_NODE_MIXIN_H_
diff --git a/fxjs/gc/gced_tree_node_mixin_unittest.cpp b/fxjs/gc/gced_tree_node_mixin_unittest.cpp
new file mode 100644
index 0000000..2cd098e
--- /dev/null
+++ b/fxjs/gc/gced_tree_node_mixin_unittest.cpp
@@ -0,0 +1,149 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/gc/gced_tree_node_mixin.h"
+
+#include <map>
+
+#include "core/fxcrt/observed_ptr.h"
+#include "fxjs/gc/heap.h"
+#include "testing/fxgc_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/v8_test_environment.h"
+#include "v8/include/cppgc/allocation.h"
+#include "v8/include/cppgc/persistent.h"
+
+namespace {
+
+class ObservableGCedTreeNodeMixinForTest
+    : public cppgc::GarbageCollected<ObservableGCedTreeNodeMixinForTest>,
+      public GCedTreeNodeMixin<ObservableGCedTreeNodeMixinForTest>,
+      public Observable {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+
+  // GCedTreeNodeMixin:
+  void Trace(cppgc::Visitor* visitor) const override {
+    GCedTreeNodeMixin<ObservableGCedTreeNodeMixinForTest>::Trace(visitor);
+  }
+
+ private:
+  ObservableGCedTreeNodeMixinForTest() = default;
+};
+
+}  // namespace
+
+class GCedTreeNodeMixinUnitTest : public FXGCUnitTest {
+ public:
+  GCedTreeNodeMixinUnitTest() = default;
+  ~GCedTreeNodeMixinUnitTest() override = default;
+
+  // FXGCUnitTest:
+  void TearDown() override {
+    root_ = nullptr;  // Can't (yet) outlive |FXGCUnitTest::heap_|.
+    FXGCUnitTest::TearDown();
+  }
+
+  ObservableGCedTreeNodeMixinForTest* root() const { return root_; }
+  void CreateRoot() { root_ = CreateNode(); }
+
+  ObservableGCedTreeNodeMixinForTest* CreateNode() {
+    return cppgc::MakeGarbageCollected<ObservableGCedTreeNodeMixinForTest>(
+        heap()->GetAllocationHandle());
+  }
+
+  void AddClutterToFront(ObservableGCedTreeNodeMixinForTest* parent) {
+    for (int i = 0; i < 4; ++i) {
+      parent->AppendFirstChild(
+          cppgc::MakeGarbageCollected<ObservableGCedTreeNodeMixinForTest>(
+              heap()->GetAllocationHandle()));
+    }
+  }
+
+  void AddClutterToBack(ObservableGCedTreeNodeMixinForTest* parent) {
+    for (int i = 0; i < 4; ++i) {
+      parent->AppendLastChild(
+          cppgc::MakeGarbageCollected<ObservableGCedTreeNodeMixinForTest>(
+              heap()->GetAllocationHandle()));
+    }
+  }
+
+ private:
+  cppgc::Persistent<ObservableGCedTreeNodeMixinForTest> root_;
+};
+
+TEST_F(GCedTreeNodeMixinUnitTest, OneRefence) {
+  CreateRoot();
+  ObservedPtr<ObservableGCedTreeNodeMixinForTest> watcher(root());
+  ForceGCAndPump();
+  EXPECT_TRUE(watcher);
+}
+
+TEST_F(GCedTreeNodeMixinUnitTest, NoReferences) {
+  ObservedPtr<ObservableGCedTreeNodeMixinForTest> watcher(CreateNode());
+  ForceGCAndPump();
+  EXPECT_FALSE(watcher);
+}
+
+TEST_F(GCedTreeNodeMixinUnitTest, FirstHasParent) {
+  CreateRoot();
+  ObservedPtr<ObservableGCedTreeNodeMixinForTest> watcher(CreateNode());
+  root()->AppendFirstChild(watcher.Get());
+  ForceGCAndPump();
+  ASSERT_TRUE(root());
+  EXPECT_TRUE(watcher);
+  root()->RemoveChild(watcher.Get());
+  ForceGCAndPump();
+  ASSERT_TRUE(root());
+  EXPECT_FALSE(watcher);
+
+  // Now add some clutter.
+  watcher.Reset(CreateNode());
+  root()->AppendFirstChild(watcher.Get());
+  AddClutterToFront(root());
+  AddClutterToBack(root());
+  ForceGCAndPump();
+  ASSERT_TRUE(root());
+  EXPECT_TRUE(watcher);
+  root()->RemoveChild(watcher.Get());
+  ForceGCAndPump();
+  EXPECT_TRUE(root());
+  EXPECT_FALSE(watcher);
+}
+
+TEST_F(GCedTreeNodeMixinUnitTest, RemoveSelf) {
+  CreateRoot();
+  ObservedPtr<ObservableGCedTreeNodeMixinForTest> watcher(CreateNode());
+  root()->AppendFirstChild(watcher.Get());
+  ForceGCAndPump();
+  EXPECT_TRUE(root());
+  ASSERT_TRUE(watcher);
+  watcher->RemoveSelfIfParented();
+  ForceGCAndPump();
+  EXPECT_TRUE(root());
+  EXPECT_FALSE(watcher);
+}
+
+TEST_F(GCedTreeNodeMixinUnitTest, InsertBeforeAfter) {
+  CreateRoot();
+  AddClutterToFront(root());
+  ObservedPtr<ObservableGCedTreeNodeMixinForTest> watcher(CreateNode());
+  root()->AppendFirstChild(watcher.Get());
+  root()->InsertBefore(root()->GetFirstChild(), root()->GetLastChild());
+  root()->InsertAfter(root()->GetLastChild(), root()->GetFirstChild());
+  ForceGCAndPump();
+  ASSERT_TRUE(root());
+  EXPECT_TRUE(watcher);
+  root()->RemoveChild(watcher.Get());
+  ForceGCAndPump();
+  EXPECT_TRUE(root());
+  EXPECT_FALSE(watcher);
+}
+
+TEST_F(GCedTreeNodeMixinUnitTest, AsMapKey) {
+  std::map<cppgc::Persistent<ObservableGCedTreeNodeMixinForTest>, int> score;
+  ObservableGCedTreeNodeMixinForTest* node = CreateNode();
+  score[node] = 100;
+  EXPECT_EQ(100, score[node]);
+}
diff --git a/fxjs/gc/gced_tree_node_unittest.cpp b/fxjs/gc/gced_tree_node_unittest.cpp
new file mode 100644
index 0000000..a456a31
--- /dev/null
+++ b/fxjs/gc/gced_tree_node_unittest.cpp
@@ -0,0 +1,143 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/gc/gced_tree_node.h"
+
+#include <map>
+
+#include "core/fxcrt/observed_ptr.h"
+#include "fxjs/gc/heap.h"
+#include "testing/fxgc_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/v8_test_environment.h"
+#include "v8/include/cppgc/allocation.h"
+#include "v8/include/cppgc/persistent.h"
+
+namespace {
+
+class ObservableGCedTreeNodeForTest
+    : public GCedTreeNode<ObservableGCedTreeNodeForTest>,
+      public Observable {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+
+ private:
+  ObservableGCedTreeNodeForTest() = default;
+};
+
+}  // namespace
+
+class GCedTreeNodeUnitTest : public FXGCUnitTest {
+ public:
+  GCedTreeNodeUnitTest() = default;
+  ~GCedTreeNodeUnitTest() override = default;
+
+  // FXGCUnitTest:
+  void TearDown() override {
+    root_ = nullptr;  // Can't (yet) outlive |FXGCUnitTest::heap_|.
+    FXGCUnitTest::TearDown();
+  }
+
+  ObservableGCedTreeNodeForTest* root() const { return root_; }
+  void CreateRoot() { root_ = CreateNode(); }
+
+  ObservableGCedTreeNodeForTest* CreateNode() {
+    return cppgc::MakeGarbageCollected<ObservableGCedTreeNodeForTest>(
+        heap()->GetAllocationHandle());
+  }
+
+  void AddClutterToFront(ObservableGCedTreeNodeForTest* parent) {
+    for (int i = 0; i < 4; ++i) {
+      parent->AppendFirstChild(
+          cppgc::MakeGarbageCollected<ObservableGCedTreeNodeForTest>(
+              heap()->GetAllocationHandle()));
+    }
+  }
+
+  void AddClutterToBack(ObservableGCedTreeNodeForTest* parent) {
+    for (int i = 0; i < 4; ++i) {
+      parent->AppendLastChild(
+          cppgc::MakeGarbageCollected<ObservableGCedTreeNodeForTest>(
+              heap()->GetAllocationHandle()));
+    }
+  }
+
+ private:
+  cppgc::Persistent<ObservableGCedTreeNodeForTest> root_;
+};
+
+TEST_F(GCedTreeNodeUnitTest, OneRefence) {
+  CreateRoot();
+  ObservedPtr<ObservableGCedTreeNodeForTest> watcher(root());
+  ForceGCAndPump();
+  EXPECT_TRUE(watcher);
+}
+
+TEST_F(GCedTreeNodeUnitTest, NoReferences) {
+  ObservedPtr<ObservableGCedTreeNodeForTest> watcher(CreateNode());
+  ForceGCAndPump();
+  EXPECT_FALSE(watcher);
+}
+
+TEST_F(GCedTreeNodeUnitTest, FirstHasParent) {
+  CreateRoot();
+  ObservedPtr<ObservableGCedTreeNodeForTest> watcher(CreateNode());
+  root()->AppendFirstChild(watcher.Get());
+  ForceGCAndPump();
+  ASSERT_TRUE(root());
+  EXPECT_TRUE(watcher);
+  root()->RemoveChild(watcher.Get());
+  ForceGCAndPump();
+  ASSERT_TRUE(root());
+  EXPECT_FALSE(watcher);
+
+  // Now add some clutter.
+  watcher.Reset(CreateNode());
+  root()->AppendFirstChild(watcher.Get());
+  AddClutterToFront(root());
+  AddClutterToBack(root());
+  ForceGCAndPump();
+  ASSERT_TRUE(root());
+  EXPECT_TRUE(watcher);
+  root()->RemoveChild(watcher.Get());
+  ForceGCAndPump();
+  EXPECT_TRUE(root());
+  EXPECT_FALSE(watcher);
+}
+
+TEST_F(GCedTreeNodeUnitTest, RemoveSelf) {
+  CreateRoot();
+  ObservedPtr<ObservableGCedTreeNodeForTest> watcher(CreateNode());
+  root()->AppendFirstChild(watcher.Get());
+  ForceGCAndPump();
+  EXPECT_TRUE(root());
+  ASSERT_TRUE(watcher);
+  watcher->RemoveSelfIfParented();
+  ForceGCAndPump();
+  EXPECT_TRUE(root());
+  EXPECT_FALSE(watcher);
+}
+
+TEST_F(GCedTreeNodeUnitTest, InsertBeforeAfter) {
+  CreateRoot();
+  AddClutterToFront(root());
+  ObservedPtr<ObservableGCedTreeNodeForTest> watcher(CreateNode());
+  root()->AppendFirstChild(watcher.Get());
+  root()->InsertBefore(root()->GetFirstChild(), root()->GetLastChild());
+  root()->InsertAfter(root()->GetLastChild(), root()->GetFirstChild());
+  ForceGCAndPump();
+  ASSERT_TRUE(root());
+  EXPECT_TRUE(watcher);
+  root()->RemoveChild(watcher.Get());
+  ForceGCAndPump();
+  EXPECT_TRUE(root());
+  EXPECT_FALSE(watcher);
+}
+
+TEST_F(GCedTreeNodeUnitTest, AsMapKey) {
+  std::map<cppgc::Persistent<ObservableGCedTreeNodeForTest>, int> score;
+  ObservableGCedTreeNodeForTest* node = CreateNode();
+  score[node] = 100;
+  EXPECT_EQ(100, score[node]);
+}
diff --git a/fxjs/gc/heap.cpp b/fxjs/gc/heap.cpp
new file mode 100644
index 0000000..301018f
--- /dev/null
+++ b/fxjs/gc/heap.cpp
@@ -0,0 +1,98 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/gc/heap.h"
+
+#include <utility>
+
+#include "core/fxcrt/fx_system.h"
+#include "third_party/base/check.h"
+#include "v8/include/cppgc/heap.h"
+
+namespace {
+
+size_t g_platform_ref_count = 0;
+v8::Platform* g_platform = nullptr;
+v8::Isolate* g_isolate = nullptr;
+
+}  // namespace
+
+// Taken from v8/samples/cppgc/cppgc-for-v8-embedders.cc.
+// Adaptper that makes the global v8::Platform compatible with a
+// cppgc::Platform.
+class CFXGC_Platform final : public cppgc::Platform {
+ public:
+  CFXGC_Platform() = default;
+  ~CFXGC_Platform() override = default;
+
+  cppgc::PageAllocator* GetPageAllocator() override {
+    return g_platform->GetPageAllocator();
+  }
+
+  double MonotonicallyIncreasingTime() override {
+    return g_platform->MonotonicallyIncreasingTime();
+  }
+
+  std::shared_ptr<cppgc::TaskRunner> GetForegroundTaskRunner() override {
+    // V8's default platform creates a new task runner when passed the
+    // v8::Isolate pointer the first time. For non-default platforms this will
+    // require getting the appropriate task runner.
+    return g_platform->GetForegroundTaskRunner(g_isolate);
+  }
+
+  std::unique_ptr<cppgc::JobHandle> PostJob(
+      cppgc::TaskPriority priority,
+      std::unique_ptr<cppgc::JobTask> job_task) override {
+    return g_platform->PostJob(priority, std::move(job_task));
+  }
+};
+
+void FXGC_Initialize(v8::Platform* platform, v8::Isolate* isolate) {
+  if (platform) {
+    DCHECK(!g_platform);
+    g_platform = platform;
+    g_isolate = isolate;
+  }
+}
+
+void FXGC_Release() {
+  if (g_platform && g_platform_ref_count == 0) {
+    g_platform = nullptr;
+    g_isolate = nullptr;
+  }
+}
+
+FXGCScopedHeap FXGC_CreateHeap() {
+  // If XFA is included at compile-time, but JS is disabled at run-time,
+  // we may still attempt to build a CPDFXFA_Context which will want a
+  // heap. But we can't make one because JS is disabled.
+  // TODO(tsepez): Stop the context from even being created.
+  if (!g_platform)
+    return nullptr;
+
+  ++g_platform_ref_count;
+  auto heap = cppgc::Heap::Create(
+      std::make_shared<CFXGC_Platform>(),
+      cppgc::Heap::HeapOptions{
+          {},
+          cppgc::Heap::StackSupport::kNoConservativeStackScan,
+          cppgc::Heap::MarkingType::kAtomic,
+          cppgc::Heap::SweepingType::kIncrementalAndConcurrent,
+          {}});
+  return FXGCScopedHeap(heap.release());
+}
+
+void FXGC_ForceGarbageCollection(cppgc::Heap* heap) {
+  heap->ForceGarbageCollectionSlow("FXGC", "ForceGarbageCollection",
+                                   cppgc::Heap::StackState::kNoHeapPointers);
+}
+
+void FXGCHeapDeleter::operator()(cppgc::Heap* heap) {
+  DCHECK(heap);
+  DCHECK(g_platform_ref_count > 0);
+  --g_platform_ref_count;
+
+  FXGC_ForceGarbageCollection(heap);
+  delete heap;
+}
diff --git a/fxjs/gc/heap.h b/fxjs/gc/heap.h
new file mode 100644
index 0000000..1e2bf61
--- /dev/null
+++ b/fxjs/gc/heap.h
@@ -0,0 +1,36 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FXJS_GC_HEAP_H_
+#define FXJS_GC_HEAP_H_
+
+#include <memory>
+
+#include "v8/include/cppgc/allocation.h"
+
+namespace cppgc {
+class Heap;
+}  // namespace cppgc
+
+namespace v8 {
+class Isolate;
+class Platform;
+}  // namespace v8
+
+struct FXGCHeapDeleter {
+  void operator()(cppgc::Heap* heap);
+};
+
+using FXGCScopedHeap = std::unique_ptr<cppgc::Heap, FXGCHeapDeleter>;
+
+void FXGC_Initialize(v8::Platform* platform, v8::Isolate* isolate);
+void FXGC_Release();
+FXGCScopedHeap FXGC_CreateHeap();
+void FXGC_ForceGarbageCollection(cppgc::Heap* heap);
+
+#define CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED \
+  template <typename T>                      \
+  friend class cppgc::MakeGarbageCollectedTrait
+
+#endif  // FXJS_GC_HEAP_H_
diff --git a/fxjs/gc/heap_unittest.cpp b/fxjs/gc/heap_unittest.cpp
new file mode 100644
index 0000000..aa90d00
--- /dev/null
+++ b/fxjs/gc/heap_unittest.cpp
@@ -0,0 +1,175 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/gc/heap.h"
+
+#include <memory>
+#include <set>
+
+#include "testing/fxgc_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/v8_test_environment.h"
+#include "third_party/base/containers/contains.h"
+#include "v8/include/cppgc/allocation.h"
+#include "v8/include/cppgc/persistent.h"
+
+namespace {
+
+class PseudoCollectible : public cppgc::GarbageCollected<PseudoCollectible> {
+ public:
+  static void ClearCounts() {
+    s_live_.clear();
+    s_dead_.clear();
+  }
+  static size_t LiveCount() { return s_live_.size(); }
+  static size_t DeadCount() { return s_dead_.size(); }
+
+  PseudoCollectible() { s_live_.insert(this); }
+  virtual ~PseudoCollectible() {
+    s_live_.erase(this);
+    s_dead_.insert(this);
+  }
+
+  bool IsLive() const { return pdfium::Contains(s_live_, this); }
+
+  virtual void Trace(cppgc::Visitor* visitor) const {}
+
+ private:
+  static std::set<const PseudoCollectible*> s_live_;
+  static std::set<const PseudoCollectible*> s_dead_;
+};
+
+std::set<const PseudoCollectible*> PseudoCollectible::s_live_;
+std::set<const PseudoCollectible*> PseudoCollectible::s_dead_;
+
+class CollectibleHolder {
+ public:
+  explicit CollectibleHolder(PseudoCollectible* holdee) : holdee_(holdee) {}
+  ~CollectibleHolder() = default;
+
+  PseudoCollectible* holdee() const { return holdee_; }
+
+ private:
+  cppgc::Persistent<PseudoCollectible> holdee_;
+};
+
+class Bloater : public cppgc::GarbageCollected<Bloater> {
+ public:
+  void Trace(cppgc::Visitor* visitor) const {}
+  uint8_t bloat_[65536];
+};
+
+}  // namespace
+
+class HeapUnitTest : public FXGCUnitTest {
+ public:
+  HeapUnitTest() = default;
+  ~HeapUnitTest() override = default;
+
+  // FXGCUnitTest:
+  void TearDown() override {
+    PseudoCollectible::ClearCounts();
+    FXGCUnitTest::TearDown();
+  }
+};
+
+TEST_F(HeapUnitTest, SeveralHeaps) {
+  FXGCScopedHeap heap1 = FXGC_CreateHeap();
+  EXPECT_TRUE(heap1);
+
+  FXGCScopedHeap heap2 = FXGC_CreateHeap();
+  EXPECT_TRUE(heap2);
+
+  FXGCScopedHeap heap3 = FXGC_CreateHeap();
+  EXPECT_TRUE(heap3);
+
+  // Test manually destroying the heap.
+  heap3.reset();
+  EXPECT_FALSE(heap3);
+  heap3.reset();
+  EXPECT_FALSE(heap3);
+}
+
+TEST_F(HeapUnitTest, NoReferences) {
+  FXGCScopedHeap heap1 = FXGC_CreateHeap();
+  ASSERT_TRUE(heap1);
+  {
+    auto holder = std::make_unique<CollectibleHolder>(
+        cppgc::MakeGarbageCollected<PseudoCollectible>(
+            heap1->GetAllocationHandle()));
+
+    EXPECT_TRUE(holder->holdee()->IsLive());
+    EXPECT_EQ(1u, PseudoCollectible::LiveCount());
+    EXPECT_EQ(0u, PseudoCollectible::DeadCount());
+  }
+  FXGC_ForceGarbageCollection(heap1.get());
+  EXPECT_EQ(0u, PseudoCollectible::LiveCount());
+  EXPECT_EQ(1u, PseudoCollectible::DeadCount());
+}
+
+TEST_F(HeapUnitTest, HasReferences) {
+  FXGCScopedHeap heap1 = FXGC_CreateHeap();
+  ASSERT_TRUE(heap1);
+  {
+    auto holder = std::make_unique<CollectibleHolder>(
+        cppgc::MakeGarbageCollected<PseudoCollectible>(
+            heap1->GetAllocationHandle()));
+
+    EXPECT_TRUE(holder->holdee()->IsLive());
+    EXPECT_EQ(1u, PseudoCollectible::LiveCount());
+    EXPECT_EQ(0u, PseudoCollectible::DeadCount());
+
+    FXGC_ForceGarbageCollection(heap1.get());
+    EXPECT_TRUE(holder->holdee()->IsLive());
+    EXPECT_EQ(1u, PseudoCollectible::LiveCount());
+    EXPECT_EQ(0u, PseudoCollectible::DeadCount());
+  }
+}
+
+// TODO(tsepez): enable when CPPGC fixes this segv.
+TEST_F(HeapUnitTest, DISABLED_DeleteHeapHasReferences) {
+  FXGCScopedHeap heap1 = FXGC_CreateHeap();
+  ASSERT_TRUE(heap1);
+  {
+    auto holder = std::make_unique<CollectibleHolder>(
+        cppgc::MakeGarbageCollected<PseudoCollectible>(
+            heap1->GetAllocationHandle()));
+
+    EXPECT_TRUE(holder->holdee()->IsLive());
+    EXPECT_EQ(1u, PseudoCollectible::LiveCount());
+    EXPECT_EQ(0u, PseudoCollectible::DeadCount());
+
+    heap1.reset();
+
+    // Maybe someday magically nulled by heap destruction.
+    EXPECT_FALSE(holder->holdee());
+    EXPECT_EQ(1u, PseudoCollectible::LiveCount());
+    EXPECT_EQ(0u, PseudoCollectible::DeadCount());
+  }
+}
+
+TEST_F(HeapUnitTest, DeleteHeapNoReferences) {
+  FXGCScopedHeap heap1 = FXGC_CreateHeap();
+  ASSERT_TRUE(heap1);
+  {
+    auto holder = std::make_unique<CollectibleHolder>(
+        cppgc::MakeGarbageCollected<PseudoCollectible>(
+            heap1->GetAllocationHandle()));
+
+    EXPECT_TRUE(holder->holdee()->IsLive());
+    EXPECT_EQ(1u, PseudoCollectible::LiveCount());
+    EXPECT_EQ(0u, PseudoCollectible::DeadCount());
+  }
+  heap1.reset();
+  EXPECT_EQ(0u, PseudoCollectible::LiveCount());
+  EXPECT_EQ(1u, PseudoCollectible::DeadCount());
+}
+
+TEST_F(HeapUnitTest, Bloat) {
+  ASSERT_TRUE(heap());
+  for (int i = 0; i < 100000; ++i) {
+    cppgc::MakeGarbageCollected<Bloater>(heap()->GetAllocationHandle());
+    Pump();  // Do not force GC, must happen implicitly when space required.
+  }
+}
diff --git a/fxjs/gc/move_unittest.cpp b/fxjs/gc/move_unittest.cpp
new file mode 100644
index 0000000..3d5a809
--- /dev/null
+++ b/fxjs/gc/move_unittest.cpp
@@ -0,0 +1,62 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <utility>
+
+#include "fxjs/gc/heap.h"
+#include "testing/fxgc_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/persistent.h"
+
+namespace {
+
+class HeapObject : public cppgc::GarbageCollected<HeapObject> {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+
+  void Trace(cppgc::Visitor* visitor) const {
+    visitor->Trace(frick_);
+    visitor->Trace(frack_);
+  }
+
+  cppgc::Member<HeapObject> frick_;
+  cppgc::Member<HeapObject> frack_;
+
+ private:
+  HeapObject() = default;
+};
+
+class CppObject {
+ public:
+  CppObject() = default;
+
+  cppgc::Persistent<HeapObject> click_;
+  cppgc::Persistent<HeapObject> clack_;
+};
+
+}  // namespace
+
+class MoveUnitTest : public FXGCUnitTest {};
+
+TEST_F(MoveUnitTest, Member) {
+  // Moving a Member<> leaves the moved-from object as null.
+  auto* obj =
+      cppgc::MakeGarbageCollected<HeapObject>(heap()->GetAllocationHandle());
+  obj->frick_ = obj;
+  obj->frack_ = std::move(obj->frick_);
+  EXPECT_FALSE(obj->frick_);
+  EXPECT_EQ(obj, obj->frack_);
+}
+
+TEST_F(MoveUnitTest, Persistent) {
+  // Moving a Persistent<> leaves the moved-from object as null.
+  auto* obj =
+      cppgc::MakeGarbageCollected<HeapObject>(heap()->GetAllocationHandle());
+  CppObject outsider;
+  outsider.click_ = obj;
+  outsider.clack_ = std::move(outsider.click_);
+  EXPECT_FALSE(outsider.click_);
+  EXPECT_EQ(obj, outsider.clack_);
+}
diff --git a/fxjs/global_timer.cpp b/fxjs/global_timer.cpp
index b837508..035aff8 100644
--- a/fxjs/global_timer.cpp
+++ b/fxjs/global_timer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,8 +8,10 @@
 
 #include <map>
 
-#include "core/fxcrt/timerhandler_iface.h"
+#include "core/fxcrt/cfx_timer.h"
 #include "fxjs/cjs_app.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
 #include "third_party/base/no_destructor.h"
 
 namespace {
@@ -34,8 +36,10 @@
       m_swJScript(script),
       m_pRuntime(pRuntime),
       m_pEmbedApp(pObj) {
-  if (HasValidID())
+  if (HasValidID()) {
+    DCHECK(!pdfium::Contains(GetGlobalTimerMap(), m_nTimerID));
     GetGlobalTimerMap()[m_nTimerID] = this;
+  }
 }
 
 GlobalTimer::~GlobalTimer() {
@@ -45,6 +49,7 @@
   if (m_pRuntime && m_pRuntime->GetTimerHandler())
     m_pRuntime->GetTimerHandler()->KillTimer(m_nTimerID);
 
+  DCHECK(pdfium::Contains(GetGlobalTimerMap(), m_nTimerID));
   GetGlobalTimerMap().erase(m_nTimerID);
 }
 
@@ -84,5 +89,5 @@
 }
 
 bool GlobalTimer::HasValidID() const {
-  return m_nTimerID != TimerHandlerIface::kInvalidTimerID;
+  return m_nTimerID != CFX_Timer::HandlerIface::kInvalidTimerID;
 }
diff --git a/fxjs/global_timer.h b/fxjs/global_timer.h
index ef6bbb6..106c9e6 100644
--- a/fxjs/global_timer.h
+++ b/fxjs/global_timer.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #ifndef FXJS_GLOBAL_TIMER_H_
 #define FXJS_GLOBAL_TIMER_H_
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "fxjs/cjs_runtime.h"
 
 class CJS_App;
@@ -44,7 +45,7 @@
   const uint32_t m_dwTimeOut;
   const WideString m_swJScript;
   ObservedPtr<CJS_Runtime> m_pRuntime;
-  CJS_App* const m_pEmbedApp;
+  UnownedPtr<CJS_App> const m_pEmbedApp;
 };
 
 #endif  // FXJS_GLOBAL_TIMER_H_
diff --git a/fxjs/ijs_event_context.h b/fxjs/ijs_event_context.h
index e2c6407..8e9d23c 100644
--- a/fxjs/ijs_event_context.h
+++ b/fxjs/ijs_event_context.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,11 @@
 #ifndef FXJS_IJS_EVENT_CONTEXT_H_
 #define FXJS_IJS_EVENT_CONTEXT_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/widestring.h"
 #include "fxjs/ijs_runtime.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
-class CPDF_Bookmark;
 class CPDF_FormField;
-class CPDFSDK_Annot;
-class CPDFSDK_FormFillEnvironment;
 
 // Records the details of an event and triggers JS execution for it. There
 // can be more than one of these at any given time, as JS callbacks to C++
@@ -24,23 +20,20 @@
  public:
   virtual ~IJS_EventContext() = default;
 
-  virtual Optional<IJS_Runtime::JS_Error> RunScript(
+  virtual absl::optional<IJS_Runtime::JS_Error> RunScript(
       const WideString& script) = 0;
 
-  virtual void OnApp_Init() = 0;
+  virtual void OnDoc_Open(const WideString& strTargetName) = 0;
+  virtual void OnDoc_WillPrint() = 0;
+  virtual void OnDoc_DidPrint() = 0;
+  virtual void OnDoc_WillSave() = 0;
+  virtual void OnDoc_DidSave() = 0;
+  virtual void OnDoc_WillClose() = 0;
 
-  virtual void OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                          const WideString& strTargetName) = 0;
-  virtual void OnDoc_WillPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-  virtual void OnDoc_DidPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-  virtual void OnDoc_WillSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-  virtual void OnDoc_DidSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-  virtual void OnDoc_WillClose(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-
-  virtual void OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-  virtual void OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-  virtual void OnPage_InView(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-  virtual void OnPage_OutView(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
+  virtual void OnPage_Open() = 0;
+  virtual void OnPage_Close() = 0;
+  virtual void OnPage_InView() = 0;
+  virtual void OnPage_OutView() = 0;
 
   virtual void OnField_MouseDown(bool bModifier,
                                  bool bShift,
@@ -88,44 +81,6 @@
                                 WideString* Value,
                                 bool* bRc) = 0;
 
-  virtual void OnScreen_Focus(bool bModifier,
-                              bool bShift,
-                              CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_Blur(bool bModifier,
-                             bool bShift,
-                             CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_Open(bool bModifier,
-                             bool bShift,
-                             CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_Close(bool bModifier,
-                              bool bShift,
-                              CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_MouseDown(bool bModifier,
-                                  bool bShift,
-                                  CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_MouseUp(bool bModifier,
-                                bool bShift,
-                                CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_MouseEnter(bool bModifier,
-                                   bool bShift,
-                                   CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_MouseExit(bool bModifier,
-                                  bool bShift,
-                                  CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_InView(bool bModifier,
-                               bool bShift,
-                               CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_OutView(bool bModifier,
-                                bool bShift,
-                                CPDFSDK_Annot* pScreen) = 0;
-
-  virtual void OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) = 0;
-  virtual void OnLink_MouseUp(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-
-  virtual void OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                           const WideString&) = 0;
-  virtual void OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-  virtual void OnConsole_Exec() = 0;
   virtual void OnExternal_Exec() = 0;
 };
 
diff --git a/fxjs/ijs_runtime.cpp b/fxjs/ijs_runtime.cpp
index 34a846e..0308524 100644
--- a/fxjs/ijs_runtime.cpp
+++ b/fxjs/ijs_runtime.cpp
@@ -1,36 +1,46 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxjs/ijs_runtime.h"
 
 #include "fxjs/cjs_runtimestub.h"
-#include "third_party/base/ptr_util.h"
 
 #ifdef PDF_ENABLE_V8
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fxjs/cfxjs_engine.h"
 #include "fxjs/cjs_runtime.h"
-#endif
+#ifdef PDF_ENABLE_XFA
+#include "fxjs/gc/heap.h"
+#endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
 
 IJS_Runtime::ScopedEventContext::ScopedEventContext(IJS_Runtime* pRuntime)
     : m_pRuntime(pRuntime), m_pContext(pRuntime->NewEventContext()) {}
 
 IJS_Runtime::ScopedEventContext::~ScopedEventContext() {
-  m_pRuntime->ReleaseEventContext(m_pContext.Release());
+  m_pRuntime->ReleaseEventContext(m_pContext.ExtractAsDangling());
 }
 
 // static
-void IJS_Runtime::Initialize(unsigned int slot, void* isolate) {
+void IJS_Runtime::Initialize(unsigned int slot, void* isolate, void* platform) {
 #ifdef PDF_ENABLE_V8
   FXJS_Initialize(slot, static_cast<v8::Isolate*>(isolate));
-#endif
+#ifdef PDF_ENABLE_XFA
+  FXGC_Initialize(static_cast<v8::Platform*>(platform),
+                  static_cast<v8::Isolate*>(isolate));
+#endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
 }
 
 // static
 void IJS_Runtime::Destroy() {
 #ifdef PDF_ENABLE_V8
+#ifdef PDF_ENABLE_XFA
+  FXGC_Release();
+#endif  // PDF_ENABLE_XFA
   FXJS_Release();
-#endif
+#endif  // PDF_ENABLE_V8
 }
 
 // static
@@ -38,9 +48,9 @@
     CPDFSDK_FormFillEnvironment* pFormFillEnv) {
 #ifdef PDF_ENABLE_V8
   if (pFormFillEnv->IsJSPlatformPresent())
-    return pdfium::MakeUnique<CJS_Runtime>(pFormFillEnv);
+    return std::make_unique<CJS_Runtime>(pFormFillEnv);
 #endif
-  return pdfium::MakeUnique<CJS_RuntimeStub>(pFormFillEnv);
+  return std::make_unique<CJS_RuntimeStub>(pFormFillEnv);
 }
 
 IJS_Runtime::~IJS_Runtime() = default;
diff --git a/fxjs/ijs_runtime.h b/fxjs/ijs_runtime.h
index ca103aa..d2ce19f 100644
--- a/fxjs/ijs_runtime.h
+++ b/fxjs/ijs_runtime.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,12 +9,11 @@
 
 #include <memory>
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/optional.h"
+#include "core/fxcrt/widestring.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
-class CFXJSE_Value;
 class CJS_Runtime;
 class CPDFSDK_FormFillEnvironment;
 class IJS_EventContext;
@@ -34,18 +33,20 @@
 
   class ScopedEventContext {
    public:
+    FX_STACK_ALLOCATED();
+
     explicit ScopedEventContext(IJS_Runtime* pRuntime);
     ~ScopedEventContext();
 
-    IJS_EventContext* Get() const { return m_pContext.Get(); }
-    IJS_EventContext* operator->() const { return m_pContext.Get(); }
+    IJS_EventContext* Get() const { return m_pContext; }
+    IJS_EventContext* operator->() const { return m_pContext; }
 
    private:
     UnownedPtr<IJS_Runtime> const m_pRuntime;
     UnownedPtr<IJS_EventContext> m_pContext;
   };
 
-  static void Initialize(unsigned int slot, void* isolate);
+  static void Initialize(unsigned int slot, void* isolate, void* platform);
   static void Destroy();
   static std::unique_ptr<IJS_Runtime> Create(
       CPDFSDK_FormFillEnvironment* pFormFillEnv);
@@ -56,7 +57,7 @@
   virtual IJS_EventContext* NewEventContext() = 0;
   virtual void ReleaseEventContext(IJS_EventContext* pContext) = 0;
   virtual CPDFSDK_FormFillEnvironment* GetFormFillEnv() const = 0;
-  virtual Optional<JS_Error> ExecuteScript(const WideString& script) = 0;
+  virtual absl::optional<JS_Error> ExecuteScript(const WideString& script) = 0;
 
  protected:
   IJS_Runtime() = default;
diff --git a/fxjs/js_define.cpp b/fxjs/js_define.cpp
index 0c0c02c..b19c2e2 100644
--- a/fxjs/js_define.cpp
+++ b/fxjs/js_define.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,10 @@
 
 #include "fxjs/js_define.h"
 
+#include <math.h>
+#include <stdarg.h>
+
 #include <algorithm>
-#include <cmath>
 #include <limits>
 #include <vector>
 
@@ -15,50 +17,46 @@
 #include "fxjs/cjs_document.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/fx_date_helpers.h"
+#include "fxjs/fxv8.h"
+#include "third_party/base/check.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-function.h"
+#include "v8/include/v8-isolate.h"
 
 void JSDestructor(v8::Local<v8::Object> obj) {
   CFXJS_Engine::SetObjectPrivate(obj, nullptr);
 }
 
-double JS_DateParse(const WideString& str) {
-  v8::Isolate* pIsolate = v8::Isolate::GetCurrent();
+double JS_DateParse(v8::Isolate* pIsolate, const WideString& str) {
   v8::Isolate::Scope isolate_scope(pIsolate);
   v8::HandleScope scope(pIsolate);
 
   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
 
   // Use the built-in object method.
-  v8::Local<v8::Value> v =
-      context->Global()
-          ->Get(context, v8::String::NewFromUtf8(pIsolate, "Date",
-                                                 v8::NewStringType::kNormal)
-                             .ToLocalChecked())
-          .ToLocalChecked();
-  if (v->IsObject()) {
-    v8::Local<v8::Object> o = v->ToObject(context).ToLocalChecked();
-    v = o->Get(context, v8::String::NewFromUtf8(pIsolate, "parse",
-                                                v8::NewStringType::kNormal)
-                            .ToLocalChecked())
-            .ToLocalChecked();
-    if (v->IsFunction()) {
-      v8::Local<v8::Function> funC = v8::Local<v8::Function>::Cast(v);
-      const int argc = 1;
-      v8::Local<v8::Value> timeStr =
-          v8::String::NewFromUtf8(pIsolate,
-                                  FX_UTF8Encode(str.AsStringView()).c_str(),
-                                  v8::NewStringType::kNormal)
-              .ToLocalChecked();
-      v8::Local<v8::Value> argv[argc] = {timeStr};
-      v = funC->Call(context, context->Global(), argc, argv).ToLocalChecked();
-      if (v->IsNumber()) {
-        double date = v->ToNumber(context).ToLocalChecked()->Value();
-        if (!std::isfinite(date))
-          return date;
-        return FX_LocalTime(date);
-      }
-    }
-  }
-  return 0;
+  v8::MaybeLocal<v8::Value> maybe_value =
+      context->Global()->Get(context, fxv8::NewStringHelper(pIsolate, "Date"));
+
+  v8::Local<v8::Value> value;
+  if (!maybe_value.ToLocal(&value) || !value->IsObject())
+    return 0;
+
+  v8::Local<v8::Object> obj = value.As<v8::Object>();
+  maybe_value = obj->Get(context, fxv8::NewStringHelper(pIsolate, "parse"));
+  if (!maybe_value.ToLocal(&value) || !value->IsFunction())
+    return 0;
+
+  v8::Local<v8::Function> func = value.As<v8::Function>();
+  static constexpr int argc = 1;
+  v8::Local<v8::Value> argv[argc] = {
+      fxv8::NewStringHelper(pIsolate, str.AsStringView()),
+  };
+  maybe_value = func->Call(context, context->Global(), argc, argv);
+  if (!maybe_value.ToLocal(&value) || !value->IsNumber())
+    return 0;
+
+  double date = value.As<v8::Number>()->Value();
+  return isfinite(date) ? FX_LocalTime(date) : date;
 }
 
 std::vector<v8::Local<v8::Value>> ExpandKeywordParams(
@@ -66,7 +64,7 @@
     const std::vector<v8::Local<v8::Value>>& originals,
     size_t nKeywords,
     ...) {
-  ASSERT(nKeywords);
+  DCHECK(nKeywords);
 
   std::vector<v8::Local<v8::Value>> result(nKeywords, v8::Local<v8::Value>());
   size_t size = std::min(originals.size(), nKeywords);
diff --git a/fxjs/js_define.h b/fxjs/js_define.h
index 2c15c9f..5b6573a 100644
--- a/fxjs/js_define.h
+++ b/fxjs/js_define.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #ifndef FXJS_JS_DEFINE_H_
 #define FXJS_JS_DEFINE_H_
 
+#include <memory>
 #include <vector>
 
 #include "core/fxcrt/unowned_ptr.h"
@@ -14,11 +15,10 @@
 #include "fxjs/cjs_result.h"
 #include "fxjs/cjs_runtime.h"
 #include "fxjs/js_resources.h"
-#include "third_party/base/ptr_util.h"
 
 class CJS_Object;
 
-double JS_DateParse(const WideString& str);
+double JS_DateParse(v8::Isolate* pIsolate, const WideString& str);
 
 // Some JS methods have the bizarre convention that they may also be called
 // with a single argument which is an object containing the actual arguments
@@ -42,20 +42,22 @@
 // to construct native object state.
 
 template <class T>
-static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
+static void JSConstructor(CFXJS_Engine* pEngine,
+                          v8::Local<v8::Object> obj,
+                          v8::Local<v8::Object> proxy) {
   pEngine->SetObjectPrivate(
-      obj, pdfium::MakeUnique<T>(obj, static_cast<CJS_Runtime*>(pEngine)));
+      obj, std::make_unique<T>(proxy, static_cast<CJS_Runtime*>(pEngine)));
 }
 
 // CJS_Object has virtual dtor, template not required.
 void JSDestructor(v8::Local<v8::Object> obj);
 
 template <class C>
-UnownedPtr<C> JSGetObject(v8::Local<v8::Object> obj) {
+UnownedPtr<C> JSGetObject(v8::Isolate* isolate, v8::Local<v8::Object> obj) {
   if (CFXJS_Engine::GetObjDefnID(obj) != C::GetObjDefnID())
     return nullptr;
 
-  CJS_Object* pJSObj = CFXJS_Engine::GetObjectPrivate(obj);
+  CJS_Object* pJSObj = CFXJS_Engine::GetObjectPrivate(isolate, obj);
   if (!pJSObj)
     return nullptr;
 
@@ -67,7 +69,7 @@
                   const char* class_name_string,
                   v8::Local<v8::String> property,
                   const v8::PropertyCallbackInfo<v8::Value>& info) {
-  auto pObj = JSGetObject<C>(info.Holder());
+  auto pObj = JSGetObject<C>(info.GetIsolate(), info.Holder());
   if (!pObj)
     return;
 
@@ -75,7 +77,7 @@
   if (!pRuntime)
     return;
 
-  CJS_Result result = (pObj.Get()->*M)(pRuntime);
+  CJS_Result result = (pObj.get()->*M)(pRuntime);
   if (result.HasError()) {
     pRuntime->Error(JSFormatErrorString(class_name_string, prop_name_string,
                                         result.Error()));
@@ -92,7 +94,7 @@
                   v8::Local<v8::String> property,
                   v8::Local<v8::Value> value,
                   const v8::PropertyCallbackInfo<void>& info) {
-  auto pObj = JSGetObject<C>(info.Holder());
+  auto pObj = JSGetObject<C>(info.GetIsolate(), info.Holder());
   if (!pObj)
     return;
 
@@ -100,7 +102,7 @@
   if (!pRuntime)
     return;
 
-  CJS_Result result = (pObj.Get()->*M)(pRuntime, value);
+  CJS_Result result = (pObj.get()->*M)(pRuntime, value);
   if (result.HasError()) {
     pRuntime->Error(JSFormatErrorString(class_name_string, prop_name_string,
                                         result.Error()));
@@ -113,7 +115,7 @@
 void JSMethod(const char* method_name_string,
               const char* class_name_string,
               const v8::FunctionCallbackInfo<v8::Value>& info) {
-  auto pObj = JSGetObject<C>(info.Holder());
+  auto pObj = JSGetObject<C>(info.GetIsolate(), info.Holder());
   if (!pObj)
     return;
 
@@ -125,7 +127,7 @@
   for (unsigned int i = 0; i < (unsigned int)info.Length(); i++)
     parameters.push_back(info[i]);
 
-  CJS_Result result = (pObj.Get()->*M)(pRuntime, parameters);
+  CJS_Result result = (pObj.get()->*M)(pRuntime, parameters);
   if (result.HasError()) {
     pRuntime->Error(JSFormatErrorString(class_name_string, method_name_string,
                                         result.Error()));
diff --git a/fxjs/js_resources.cpp b/fxjs/js_resources.cpp
index 3bc8df3..5c054fd 100644
--- a/fxjs/js_resources.cpp
+++ b/fxjs/js_resources.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -81,14 +81,14 @@
     case JSMessage::kUserGestureRequiredError:
       msg_string = "User gesture required.";
       break;
-    case JSMessage::kTooManyOccurances:
-      msg_string = "Too many occurances.";
+    case JSMessage::kTooManyOccurrences:
+      msg_string = "Too many occurrences.";
       break;
     case JSMessage::kUnknownMethod:
       msg_string = "Unknown method.";
       break;
-    default:
-      NOTREACHED();
+    case JSMessage::kWouldBeCyclic:
+      msg_string = "Operation would create a cycle.";
       break;
   }
   return WideString::FromASCII(msg_string);
@@ -97,10 +97,10 @@
 WideString JSFormatErrorString(const char* class_name,
                                const char* property_name,
                                const WideString& details) {
-  WideString result = WideString::FromDefANSI(class_name);
+  WideString result = WideString::FromUTF8(class_name);
   if (property_name) {
     result += L".";
-    result += WideString::FromDefANSI(property_name);
+    result += WideString::FromUTF8(property_name);
   }
   result += L": ";
   result += details;
diff --git a/fxjs/js_resources.h b/fxjs/js_resources.h
index 8a30862..0bc6be0 100644
--- a/fxjs/js_resources.h
+++ b/fxjs/js_resources.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -33,8 +33,9 @@
   kUnknownProperty,
   kInvalidSetError,
   kUserGestureRequiredError,
-  kTooManyOccurances,
+  kTooManyOccurrences,
   kUnknownMethod,
+  kWouldBeCyclic,
 };
 
 WideString JSGetStringFromID(JSMessage msg);
diff --git a/fxjs/xfa/cfxjse_app_embeddertest.cpp b/fxjs/xfa/cfxjse_app_embeddertest.cpp
index 628f56e..0e6fa45 100644
--- a/fxjs/xfa/cfxjse_app_embeddertest.cpp
+++ b/fxjs/xfa/cfxjse_app_embeddertest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxjs/xfa/cfxjse_arguments.cpp b/fxjs/xfa/cfxjse_arguments.cpp
deleted file mode 100644
index 8bfc8e3..0000000
--- a/fxjs/xfa/cfxjse_arguments.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cfxjse_arguments.h"
-
-#include "fxjs/xfa/cfxjse_context.h"
-#include "fxjs/xfa/cfxjse_value.h"
-#include "third_party/base/ptr_util.h"
-
-CFXJSE_Arguments::CFXJSE_Arguments(
-    const v8::FunctionCallbackInfo<v8::Value>* pInfo,
-    CFXJSE_Value* pRetValue)
-    : m_pInfo(pInfo), m_pRetValue(pRetValue) {}
-
-CFXJSE_Arguments::~CFXJSE_Arguments() {}
-
-int32_t CFXJSE_Arguments::GetLength() const {
-  return m_pInfo->Length();
-}
-
-std::unique_ptr<CFXJSE_Value> CFXJSE_Arguments::GetValue(int32_t index) const {
-  auto pArgValue = pdfium::MakeUnique<CFXJSE_Value>(v8::Isolate::GetCurrent());
-  pArgValue->ForceSetValue((*m_pInfo)[index]);
-  return pArgValue;
-}
-
-bool CFXJSE_Arguments::GetBoolean(int32_t index) const {
-  return (*m_pInfo)[index]->BooleanValue(m_pInfo->GetIsolate());
-}
-
-int32_t CFXJSE_Arguments::GetInt32(int32_t index) const {
-  return static_cast<int32_t>(
-      (*m_pInfo)[index]
-          ->NumberValue(m_pInfo->GetIsolate()->GetCurrentContext())
-          .FromMaybe(0.0));
-}
-
-float CFXJSE_Arguments::GetFloat(int32_t index) const {
-  return static_cast<float>(
-      (*m_pInfo)[index]
-          ->NumberValue(m_pInfo->GetIsolate()->GetCurrentContext())
-          .FromMaybe(0.0));
-}
-
-ByteString CFXJSE_Arguments::GetUTF8String(int32_t index) const {
-  v8::Isolate* isolate = m_pInfo->GetIsolate();
-  v8::Local<v8::Value> info = (*m_pInfo)[index];
-  v8::Local<v8::String> hString =
-      info->ToString(isolate->GetCurrentContext()).ToLocalChecked();
-  v8::String::Utf8Value szStringVal(isolate, hString);
-  return ByteString(*szStringVal);
-}
-
-CFXJSE_Value* CFXJSE_Arguments::GetReturnValue() const {
-  return m_pRetValue.Get();
-}
diff --git a/fxjs/xfa/cfxjse_arguments.h b/fxjs/xfa/cfxjse_arguments.h
deleted file mode 100644
index e048bc2..0000000
--- a/fxjs/xfa/cfxjse_arguments.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CFXJSE_ARGUMENTS_H_
-#define FXJS_XFA_CFXJSE_ARGUMENTS_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "v8/include/v8.h"
-
-class CFXJSE_Value;
-
-class CFXJSE_Arguments {
- public:
-  CFXJSE_Arguments(const v8::FunctionCallbackInfo<v8::Value>* pInfo,
-                   CFXJSE_Value* pRetValue);
-  ~CFXJSE_Arguments();
-
-  int32_t GetLength() const;
-  std::unique_ptr<CFXJSE_Value> GetValue(int32_t index) const;
-  bool GetBoolean(int32_t index) const;
-  int32_t GetInt32(int32_t index) const;
-  float GetFloat(int32_t index) const;
-  ByteString GetUTF8String(int32_t index) const;
-  CFXJSE_Value* GetReturnValue() const;
-
- private:
-  UnownedPtr<const v8::FunctionCallbackInfo<v8::Value>> const m_pInfo;
-  UnownedPtr<CFXJSE_Value> const m_pRetValue;
-};
-
-#endif  // FXJS_XFA_CFXJSE_ARGUMENTS_H_
diff --git a/fxjs/xfa/cfxjse_class.cpp b/fxjs/xfa/cfxjse_class.cpp
index f6caf07..c600bd7 100644
--- a/fxjs/xfa/cfxjse_class.cpp
+++ b/fxjs/xfa/cfxjse_class.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,12 +10,21 @@
 #include <utility>
 
 #include "fxjs/cjs_result.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
-#include "fxjs/xfa/cfxjse_arguments.h"
 #include "fxjs/xfa/cfxjse_context.h"
 #include "fxjs/xfa/cfxjse_isolatetracker.h"
 #include "fxjs/xfa/cfxjse_value.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "v8/include/v8-container.h"
+#include "v8/include/v8-external.h"
+#include "v8/include/v8-function-callback.h"
+#include "v8/include/v8-function.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
+#include "v8/include/v8-template.h"
 
 using pdfium::fxjse::kClassTag;
 using pdfium::fxjse::kFuncTag;
@@ -34,19 +43,12 @@
 
 void V8FunctionCallback_Wrapper(
     const v8::FunctionCallbackInfo<v8::Value>& info) {
-  const FXJSE_FUNCTION_DESCRIPTOR* lpFunctionInfo =
+  const FXJSE_FUNCTION_DESCRIPTOR* pFunctionInfo =
       AsFunctionDescriptor(info.Data().As<v8::External>()->Value());
-  if (!lpFunctionInfo)
+  if (!pFunctionInfo)
     return;
 
-  ByteStringView szFunctionName(lpFunctionInfo->name);
-  auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  lpThisValue->ForceSetValue(info.Holder());
-  auto lpRetValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  CFXJSE_Arguments impl(&info, lpRetValue.get());
-  lpFunctionInfo->callbackProc(lpThisValue.get(), szFunctionName, impl);
-  if (!lpRetValue->DirectGetValue().IsEmpty())
-    info.GetReturnValue().Set(lpRetValue->DirectGetValue());
+  pFunctionInfo->callbackProc(CFXJSE_HostObject::FromV8(info.Holder()), info);
 }
 
 void V8ConstructorCallback_Wrapper(
@@ -54,30 +56,28 @@
   if (!info.IsConstructCall())
     return;
 
-  const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition =
+  const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor =
       AsClassDescriptor(info.Data().As<v8::External>()->Value());
-  if (!lpClassDefinition)
+  if (!pClassDescriptor)
     return;
 
-  ASSERT(info.Holder()->InternalFieldCount() == 2);
+  DCHECK_EQ(info.Holder()->InternalFieldCount(), 2);
   info.Holder()->SetAlignedPointerInInternalField(0, nullptr);
   info.Holder()->SetAlignedPointerInInternalField(1, nullptr);
 }
 
 void Context_GlobalObjToString(
     const v8::FunctionCallbackInfo<v8::Value>& info) {
-  const FXJSE_CLASS_DESCRIPTOR* lpClass =
+  const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor =
       AsClassDescriptor(info.Data().As<v8::External>()->Value());
-  if (!lpClass)
+  if (!pClassDescriptor)
     return;
 
-  if (info.This() == info.Holder() && lpClass->name) {
-    ByteString szStringVal = ByteString::Format("[object %s]", lpClass->name);
+  if (info.This() == info.Holder() && pClassDescriptor->name) {
+    ByteString szStringVal =
+        ByteString::Format("[object %s]", pClassDescriptor->name);
     info.GetReturnValue().Set(
-        v8::String::NewFromUtf8(info.GetIsolate(), szStringVal.c_str(),
-                                v8::NewStringType::kNormal,
-                                szStringVal.GetLength())
-            .ToLocalChecked());
+        fxv8::NewStringHelper(info.GetIsolate(), szStringVal.AsStringView()));
     return;
   }
   v8::Local<v8::String> local_str =
@@ -95,15 +95,15 @@
 
   auto* pClassDescriptor = static_cast<const FXJSE_CLASS_DESCRIPTOR*>(
       hCallBackInfo->GetAlignedPointerFromInternalField(0));
-  if (pClassDescriptor != &GlobalClassDescriptor &&
-      pClassDescriptor != &NormalClassDescriptor &&
-      pClassDescriptor != &VariablesClassDescriptor &&
-      pClassDescriptor != &kFormCalcFM2JSDescriptor) {
+  if (pClassDescriptor != &kGlobalClassDescriptor &&
+      pClassDescriptor != &kNormalClassDescriptor &&
+      pClassDescriptor != &kVariablesClassDescriptor &&
+      pClassDescriptor != &kFormCalcDescriptor) {
     return;
   }
 
   v8::Local<v8::String> hPropName =
-      hCallBackInfo->GetInternalField(1).As<v8::String>();
+      hCallBackInfo->GetInternalField(1).As<v8::Value>().As<v8::String>();
   if (hPropName.IsEmpty())
     return;
 
@@ -114,9 +114,7 @@
   if (result.HasError()) {
     WideString err = JSFormatErrorString(pClassDescriptor->name, *szPropName,
                                          result.Error());
-    v8::MaybeLocal<v8::String> str = v8::String::NewFromUtf8(
-        info.GetIsolate(), err.ToDefANSI().c_str(), v8::NewStringType::kNormal);
-    info.GetIsolate()->ThrowException(str.ToLocalChecked());
+    fxv8::ThrowExceptionHelper(info.GetIsolate(), err.AsStringView());
     return;
   }
 
@@ -124,39 +122,39 @@
     info.GetReturnValue().Set(result.Return());
 }
 
-void DynPropGetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
-                          CFXJSE_Value* pObject,
+void DynPropGetterAdapter(v8::Isolate* pIsolate,
+                          const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor,
+                          v8::Local<v8::Object> pObject,
                           ByteStringView szPropName,
                           CFXJSE_Value* pValue) {
-  ASSERT(lpClass);
+  DCHECK(pClassDescriptor);
 
-  int32_t nPropType =
-      lpClass->dynPropTypeGetter == nullptr
-          ? FXJSE_ClassPropType_Property
-          : lpClass->dynPropTypeGetter(pObject, szPropName, false);
-  if (nPropType == FXJSE_ClassPropType_Property) {
-    if (lpClass->dynPropGetter)
-      lpClass->dynPropGetter(pObject, szPropName, pValue);
-  } else if (nPropType == FXJSE_ClassPropType_Method) {
-    if (lpClass->dynMethodCall && pValue) {
-      v8::Isolate* pIsolate = pValue->GetIsolate();
+  FXJSE_ClassPropType nPropType =
+      pClassDescriptor->dynPropTypeGetter
+          ? pClassDescriptor->dynPropTypeGetter(pIsolate, pObject, szPropName,
+                                                false)
+          : FXJSE_ClassPropType::kProperty;
+  if (nPropType == FXJSE_ClassPropType::kProperty) {
+    if (pClassDescriptor->dynPropGetter) {
+      pValue->ForceSetValue(pIsolate, pClassDescriptor->dynPropGetter(
+                                          pIsolate, pObject, szPropName));
+    }
+  } else if (nPropType == FXJSE_ClassPropType::kMethod) {
+    if (pClassDescriptor->dynMethodCall && pValue) {
       v8::HandleScope hscope(pIsolate);
       v8::Local<v8::ObjectTemplate> hCallBackInfoTemplate =
           v8::ObjectTemplate::New(pIsolate);
       hCallBackInfoTemplate->SetInternalFieldCount(2);
       v8::Local<v8::Object> hCallBackInfo =
-          hCallBackInfoTemplate
-              ->NewInstance(pValue->GetIsolate()->GetCurrentContext())
+          hCallBackInfoTemplate->NewInstance(pIsolate->GetCurrentContext())
               .ToLocalChecked();
       hCallBackInfo->SetAlignedPointerInInternalField(
-          0, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClass));
+          0, const_cast<FXJSE_CLASS_DESCRIPTOR*>(pClassDescriptor));
       hCallBackInfo->SetInternalField(
-          1, v8::String::NewFromUtf8(
-                 pIsolate, reinterpret_cast<const char*>(szPropName.raw_str()),
-                 v8::NewStringType::kNormal, szPropName.GetLength())
-                 .ToLocalChecked());
+          1, fxv8::NewStringHelper(pIsolate, szPropName));
       pValue->ForceSetValue(
-          v8::Function::New(pValue->GetIsolate()->GetCurrentContext(),
+          pIsolate,
+          v8::Function::New(pIsolate->GetCurrentContext(),
                             DynPropGetterAdapter_MethodCallback, hCallBackInfo,
                             0, v8::ConstructorBehavior::kThrow)
               .ToLocalChecked());
@@ -164,47 +162,49 @@
   }
 }
 
-void DynPropSetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
-                          CFXJSE_Value* pObject,
+void DynPropSetterAdapter(v8::Isolate* pIsolate,
+                          const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor,
+                          v8::Local<v8::Object> pObject,
                           ByteStringView szPropName,
                           CFXJSE_Value* pValue) {
-  ASSERT(lpClass);
-  int32_t nPropType =
-      lpClass->dynPropTypeGetter == nullptr
-          ? FXJSE_ClassPropType_Property
-          : lpClass->dynPropTypeGetter(pObject, szPropName, false);
-  if (nPropType != FXJSE_ClassPropType_Method) {
-    if (lpClass->dynPropSetter)
-      lpClass->dynPropSetter(pObject, szPropName, pValue);
+  DCHECK(pClassDescriptor);
+  FXJSE_ClassPropType nPropType =
+      pClassDescriptor->dynPropTypeGetter
+          ? pClassDescriptor->dynPropTypeGetter(pIsolate, pObject, szPropName,
+                                                false)
+          : FXJSE_ClassPropType::kProperty;
+  if (nPropType != FXJSE_ClassPropType::kMethod) {
+    if (pClassDescriptor->dynPropSetter) {
+      pClassDescriptor->dynPropSetter(pIsolate, pObject, szPropName,
+                                      pValue->GetValue(pIsolate));
+    }
   }
 }
 
-bool DynPropQueryAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
-                         CFXJSE_Value* pObject,
+bool DynPropQueryAdapter(v8::Isolate* pIsolate,
+                         const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor,
+                         v8::Local<v8::Object> pObject,
                          ByteStringView szPropName) {
-  ASSERT(lpClass);
-  int32_t nPropType =
-      lpClass->dynPropTypeGetter == nullptr
-          ? FXJSE_ClassPropType_Property
-          : lpClass->dynPropTypeGetter(pObject, szPropName, true);
-  return nPropType != FXJSE_ClassPropType_None;
+  FXJSE_ClassPropType nPropType = pClassDescriptor->dynPropTypeGetter
+                                      ? pClassDescriptor->dynPropTypeGetter(
+                                            pIsolate, pObject, szPropName, true)
+                                      : FXJSE_ClassPropType::kProperty;
+  return nPropType != FXJSE_ClassPropType::kNone;
 }
 
 void NamedPropertyQueryCallback(
     v8::Local<v8::Name> property,
     const v8::PropertyCallbackInfo<v8::Integer>& info) {
-  v8::Local<v8::Object> thisObject = info.Holder();
-  const FXJSE_CLASS_DESCRIPTOR* lpClass =
+  const FXJSE_CLASS_DESCRIPTOR* pClass =
       AsClassDescriptor(info.Data().As<v8::External>()->Value());
-  if (!lpClass)
+  if (!pClass)
     return;
 
   v8::HandleScope scope(info.GetIsolate());
   v8::String::Utf8Value szPropName(info.GetIsolate(), property);
   ByteStringView szFxPropName(*szPropName, szPropName.length());
-  auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  lpThisValue->ForceSetValue(thisObject);
-  if (DynPropQueryAdapter(lpClass, lpThisValue.get(), szFxPropName)) {
+  if (DynPropQueryAdapter(info.GetIsolate(), pClass, info.Holder(),
+                          szFxPropName)) {
     info.GetReturnValue().Set(v8::DontDelete);
     return;
   }
@@ -215,40 +215,33 @@
 void NamedPropertyGetterCallback(
     v8::Local<v8::Name> property,
     const v8::PropertyCallbackInfo<v8::Value>& info) {
-  v8::Local<v8::Object> thisObject = info.Holder();
-  const FXJSE_CLASS_DESCRIPTOR* lpClass =
+  const FXJSE_CLASS_DESCRIPTOR* pClass =
       AsClassDescriptor(info.Data().As<v8::External>()->Value());
-  if (!lpClass)
+  if (!pClass)
     return;
 
   v8::String::Utf8Value szPropName(info.GetIsolate(), property);
   ByteStringView szFxPropName(*szPropName, szPropName.length());
-  auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  lpThisValue->ForceSetValue(thisObject);
-  auto lpNewValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  DynPropGetterAdapter(lpClass, lpThisValue.get(), szFxPropName,
-                       lpNewValue.get());
-  info.GetReturnValue().Set(lpNewValue->DirectGetValue());
+  auto pNewValue = std::make_unique<CFXJSE_Value>();
+  DynPropGetterAdapter(info.GetIsolate(), pClass, info.Holder(), szFxPropName,
+                       pNewValue.get());
+  info.GetReturnValue().Set(pNewValue->DirectGetValue());
 }
 
 void NamedPropertySetterCallback(
     v8::Local<v8::Name> property,
     v8::Local<v8::Value> value,
     const v8::PropertyCallbackInfo<v8::Value>& info) {
-  v8::Local<v8::Object> thisObject = info.Holder();
-  const FXJSE_CLASS_DESCRIPTOR* lpClass =
+  const FXJSE_CLASS_DESCRIPTOR* pClass =
       AsClassDescriptor(info.Data().As<v8::External>()->Value());
-  if (!lpClass)
+  if (!pClass)
     return;
 
   v8::String::Utf8Value szPropName(info.GetIsolate(), property);
   ByteStringView szFxPropName(*szPropName, szPropName.length());
-  auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  lpThisValue->ForceSetValue(thisObject);
-  auto lpNewValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  lpNewValue->ForceSetValue(value);
-  DynPropSetterAdapter(lpClass, lpThisValue.get(), szFxPropName,
-                       lpNewValue.get());
+  auto pNewValue = std::make_unique<CFXJSE_Value>(info.GetIsolate(), value);
+  DynPropSetterAdapter(info.GetIsolate(), pClass, info.Holder(), szFxPropName,
+                       pNewValue.get());
   info.GetReturnValue().Set(value);
 }
 
@@ -258,86 +251,89 @@
 }
 
 void SetUpNamedPropHandler(v8::Isolate* pIsolate,
-                           v8::Local<v8::ObjectTemplate>* pObjectTemplate,
-                           const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition) {
+                           v8::Local<v8::ObjectTemplate> pObjectTemplate,
+                           const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor) {
   v8::NamedPropertyHandlerConfiguration configuration(
-      lpClassDefinition->dynPropGetter ? NamedPropertyGetterCallback : nullptr,
-      lpClassDefinition->dynPropSetter ? NamedPropertySetterCallback : nullptr,
-      lpClassDefinition->dynPropTypeGetter ? NamedPropertyQueryCallback
-                                           : nullptr,
+      pClassDescriptor->dynPropGetter ? NamedPropertyGetterCallback : nullptr,
+      pClassDescriptor->dynPropSetter ? NamedPropertySetterCallback : nullptr,
+      pClassDescriptor->dynPropTypeGetter ? NamedPropertyQueryCallback
+                                          : nullptr,
       nullptr, NamedPropertyEnumeratorCallback,
       v8::External::New(pIsolate,
-                        const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)),
+                        const_cast<FXJSE_CLASS_DESCRIPTOR*>(pClassDescriptor)),
       v8::PropertyHandlerFlags::kNonMasking);
-  (*pObjectTemplate)->SetHandler(configuration);
+  pObjectTemplate->SetHandler(configuration);
 }
 
 }  // namespace
 
 // static
 CFXJSE_Class* CFXJSE_Class::Create(
-    CFXJSE_Context* lpContext,
-    const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition,
+    CFXJSE_Context* pContext,
+    const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor,
     bool bIsJSGlobal) {
-  if (!lpContext || !lpClassDefinition)
+  if (!pContext || !pClassDescriptor)
     return nullptr;
 
   CFXJSE_Class* pExistingClass =
-      lpContext->GetClassByName(lpClassDefinition->name);
+      pContext->GetClassByName(pClassDescriptor->name);
   if (pExistingClass)
     return pExistingClass;
 
-  v8::Isolate* pIsolate = lpContext->GetIsolate();
-  auto pClass = pdfium::MakeUnique<CFXJSE_Class>(lpContext);
-  pClass->m_szClassName = lpClassDefinition->name;
-  pClass->m_lpClassDefinition = lpClassDefinition;
+  v8::Isolate* pIsolate = pContext->GetIsolate();
+  auto pClass = std::make_unique<CFXJSE_Class>(pContext);
+  pClass->m_szClassName = pClassDescriptor->name;
+  pClass->m_pClassDescriptor = pClassDescriptor;
   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
   v8::Local<v8::FunctionTemplate> hFunctionTemplate = v8::FunctionTemplate::New(
       pIsolate, bIsJSGlobal ? 0 : V8ConstructorCallback_Wrapper,
-      v8::External::New(
-          pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)));
-  hFunctionTemplate->SetClassName(
-      v8::String::NewFromUtf8(pIsolate, lpClassDefinition->name,
-                              v8::NewStringType::kNormal)
-          .ToLocalChecked());
+      v8::External::New(pIsolate,
+                        const_cast<FXJSE_CLASS_DESCRIPTOR*>(pClassDescriptor)));
+  v8::Local<v8::String> classname =
+      fxv8::NewStringHelper(pIsolate, pClassDescriptor->name);
+  hFunctionTemplate->SetClassName(classname);
+  hFunctionTemplate->PrototypeTemplate()->Set(
+      v8::Symbol::GetToStringTag(pIsolate), classname,
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
   hFunctionTemplate->InstanceTemplate()->SetInternalFieldCount(2);
   v8::Local<v8::ObjectTemplate> hObjectTemplate =
       hFunctionTemplate->InstanceTemplate();
-  SetUpNamedPropHandler(pIsolate, &hObjectTemplate, lpClassDefinition);
+  SetUpNamedPropHandler(pIsolate, hObjectTemplate, pClassDescriptor);
 
-  if (lpClassDefinition->methNum) {
-    for (int32_t i = 0; i < lpClassDefinition->methNum; i++) {
+  if (pClassDescriptor->methNum) {
+    for (int32_t i = 0; i < pClassDescriptor->methNum; i++) {
       v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
           pIsolate, V8FunctionCallback_Wrapper,
           v8::External::New(pIsolate, const_cast<FXJSE_FUNCTION_DESCRIPTOR*>(
-                                          lpClassDefinition->methods + i)));
+                                          pClassDescriptor->methods + i)));
       fun->RemovePrototype();
       hObjectTemplate->Set(
-          v8::String::NewFromUtf8(pIsolate, lpClassDefinition->methods[i].name,
-                                  v8::NewStringType::kNormal)
-              .ToLocalChecked(),
+          fxv8::NewStringHelper(pIsolate, pClassDescriptor->methods[i].name),
           fun,
           static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
     }
   }
 
   if (bIsJSGlobal) {
-    v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
+    v8::Local<v8::FunctionTemplate> fn = v8::FunctionTemplate::New(
         pIsolate, Context_GlobalObjToString,
         v8::External::New(
-            pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)));
-    fun->RemovePrototype();
-    hObjectTemplate->Set(v8::String::NewFromUtf8(pIsolate, "toString",
-                                                 v8::NewStringType::kNormal)
-                             .ToLocalChecked(),
-                         fun);
+            pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(pClassDescriptor)));
+    fn->RemovePrototype();
+    hObjectTemplate->Set(fxv8::NewStringHelper(pIsolate, "toString"), fn);
   }
-  pClass->m_hTemplate.Reset(lpContext->GetIsolate(), hFunctionTemplate);
+  pClass->m_hTemplate.Reset(pContext->GetIsolate(), hFunctionTemplate);
   CFXJSE_Class* pResult = pClass.get();
-  lpContext->AddClass(std::move(pClass));
+  pContext->AddClass(std::move(pClass));
   return pResult;
 }
 
-CFXJSE_Class::CFXJSE_Class(CFXJSE_Context* lpContext) : m_pContext(lpContext) {}
+CFXJSE_Class::CFXJSE_Class(const CFXJSE_Context* pContext)
+    : m_pContext(pContext) {}
 
-CFXJSE_Class::~CFXJSE_Class() {}
+CFXJSE_Class::~CFXJSE_Class() = default;
+
+v8::Local<v8::FunctionTemplate> CFXJSE_Class::GetTemplate(
+    v8::Isolate* pIsolate) {
+  return v8::Local<v8::FunctionTemplate>::New(pIsolate, m_hTemplate);
+}
diff --git a/fxjs/xfa/cfxjse_class.h b/fxjs/xfa/cfxjse_class.h
index f5bc232..5f4d752 100644
--- a/fxjs/xfa/cfxjse_class.h
+++ b/fxjs/xfa/cfxjse_class.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,31 +9,29 @@
 
 #include "core/fxcrt/unowned_ptr.h"
 #include "fxjs/xfa/fxjse.h"
-#include "v8/include/v8.h"
+#include "v8/include/v8-forward.h"
+#include "v8/include/v8-persistent-handle.h"
 
 class CFXJSE_Context;
-class CFXJSE_Value;
 struct FXJSE_CLASS_DESCRIPTOR;
 
 class CFXJSE_Class {
  public:
   static CFXJSE_Class* Create(CFXJSE_Context* pContext,
-                              const FXJSE_CLASS_DESCRIPTOR* lpClassDefintion,
+                              const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor,
                               bool bIsJSGlobal);
 
-  explicit CFXJSE_Class(CFXJSE_Context* lpContext);
+  explicit CFXJSE_Class(const CFXJSE_Context* pContext);
   ~CFXJSE_Class();
 
-  CFXJSE_Context* GetContext() const { return m_pContext.Get(); }
-  v8::Global<v8::FunctionTemplate>& GetTemplate() { return m_hTemplate; }
+  bool IsName(ByteStringView name) const { return name == m_szClassName; }
+  const CFXJSE_Context* GetContext() const { return m_pContext; }
+  v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* pIsolate);
 
  protected:
-  friend class CFXJSE_Context;
-  friend class CFXJSE_Value;
-
   ByteString m_szClassName;
-  UnownedPtr<const FXJSE_CLASS_DESCRIPTOR> m_lpClassDefinition;
-  UnownedPtr<CFXJSE_Context> const m_pContext;
+  UnownedPtr<const FXJSE_CLASS_DESCRIPTOR> m_pClassDescriptor;
+  UnownedPtr<const CFXJSE_Context> const m_pContext;
   v8::Global<v8::FunctionTemplate> m_hTemplate;
 };
 
diff --git a/fxjs/xfa/cfxjse_context.cpp b/fxjs/xfa/cfxjse_context.cpp
index 1ff1417..337b443 100644
--- a/fxjs/xfa/cfxjse_context.cpp
+++ b/fxjs/xfa/cfxjse_context.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,11 +9,20 @@
 #include <utility>
 
 #include "fxjs/cfxjs_engine.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_class.h"
 #include "fxjs/xfa/cfxjse_isolatetracker.h"
 #include "fxjs/xfa/cfxjse_runtimedata.h"
 #include "fxjs/xfa/cfxjse_value.h"
-#include "third_party/base/ptr_util.h"
+#include "fxjs/xfa/cjx_object.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.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"
+#include "v8/include/v8-script.h"
+#include "xfa/fxfa/parser/cxfa_thisproxy.h"
 
 namespace {
 
@@ -51,9 +60,12 @@
     "  this.log(...args);\n"
     "};";
 
-// Only address matters, values are for humans debuging here.
-char g_FXJSEHostObjectTag[] = "FXJSE Host Object";
-char g_FXJSEProxyObjectTag[] = "FXJSE Proxy Object";
+// Only address matters, values are for humans debuging here.  Keep these
+// wchar_t to prevent the compiler from doing something clever, like
+// aligning them on a byte boundary to save space, which would make them
+// incompatible for use as V8 aligned pointers.
+const wchar_t kFXJSEHostObjectTag[] = L"FXJSE Host Object";
+const wchar_t kFXJSEProxyObjectTag[] = L"FXJSE Proxy Object";
 
 v8::Local<v8::Object> CreateReturnValue(v8::Isolate* pIsolate,
                                         v8::TryCatch* trycatch) {
@@ -68,23 +80,18 @@
   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
   v8::Local<v8::Value> hException = trycatch->Exception();
   if (hException->IsObject()) {
-    v8::Local<v8::String> hNameStr =
-        v8::String::NewFromUtf8(pIsolate, "name", v8::NewStringType::kNormal)
-            .ToLocalChecked();
+    v8::Local<v8::String> hNameStr = fxv8::NewStringHelper(pIsolate, "name");
     v8::Local<v8::Value> hValue =
         hException.As<v8::Object>()->Get(context, hNameStr).ToLocalChecked();
     if (hValue->IsString() || hValue->IsStringObject()) {
       hReturnValue->Set(context, 0, hValue).FromJust();
     } else {
       v8::Local<v8::String> hErrorStr =
-          v8::String::NewFromUtf8(pIsolate, "Error", v8::NewStringType::kNormal)
-              .ToLocalChecked();
+          fxv8::NewStringHelper(pIsolate, "Error");
       hReturnValue->Set(context, 0, hErrorStr).FromJust();
     }
-
     v8::Local<v8::String> hMessageStr =
-        v8::String::NewFromUtf8(pIsolate, "message", v8::NewStringType::kNormal)
-            .ToLocalChecked();
+        fxv8::NewStringHelper(pIsolate, "message");
     hValue =
         hException.As<v8::Object>()->Get(context, hMessageStr).ToLocalChecked();
     if (hValue->IsString() || hValue->IsStringObject())
@@ -92,9 +99,7 @@
     else
       hReturnValue->Set(context, 1, hMessage->Get()).FromJust();
   } else {
-    v8::Local<v8::String> hErrorStr =
-        v8::String::NewFromUtf8(pIsolate, "Error", v8::NewStringType::kNormal)
-            .ToLocalChecked();
+    v8::Local<v8::String> hErrorStr = fxv8::NewStringHelper(pIsolate, "Error");
     hReturnValue->Set(context, 0, hErrorStr).FromJust();
     hReturnValue->Set(context, 1, hMessage->Get()).FromJust();
   }
@@ -111,65 +116,48 @@
   return hReturnValue;
 }
 
-class CFXJSE_ScopeUtil_IsolateHandleContext {
- public:
-  explicit CFXJSE_ScopeUtil_IsolateHandleContext(CFXJSE_Context* pContext)
-      : m_parent(pContext->GetIsolate()), m_cscope(pContext->GetContext()) {}
-  CFXJSE_ScopeUtil_IsolateHandleContext(
-      const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete;
-  CFXJSE_ScopeUtil_IsolateHandleContext& operator=(
-      const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete;
-
- private:
-  void* operator new(size_t size) = delete;
-  void operator delete(void*, size_t) = delete;
-
-  CFXJSE_ScopeUtil_IsolateHandle m_parent;
-  v8::Context::Scope m_cscope;
-};
-
 void FXJSE_UpdateProxyBinding(v8::Local<v8::Object> hObject) {
-  ASSERT(!hObject.IsEmpty());
-  ASSERT(hObject->InternalFieldCount() == 2);
-  hObject->SetAlignedPointerInInternalField(0, g_FXJSEProxyObjectTag);
+  DCHECK(!hObject.IsEmpty());
+  DCHECK_EQ(hObject->InternalFieldCount(), 2);
+  hObject->SetAlignedPointerInInternalField(
+      0, const_cast<wchar_t*>(kFXJSEProxyObjectTag));
   hObject->SetAlignedPointerInInternalField(1, nullptr);
 }
 
 }  // namespace
 
 void FXJSE_UpdateObjectBinding(v8::Local<v8::Object> hObject,
-                               CFXJSE_HostObject* lpNewBinding) {
-  ASSERT(!hObject.IsEmpty());
-  ASSERT(hObject->InternalFieldCount() == 2);
-  hObject->SetAlignedPointerInInternalField(0, g_FXJSEHostObjectTag);
-  hObject->SetAlignedPointerInInternalField(1, lpNewBinding);
+                               CFXJSE_HostObject* pNewBinding) {
+  DCHECK(!hObject.IsEmpty());
+  DCHECK_EQ(hObject->InternalFieldCount(), 2);
+  hObject->SetAlignedPointerInInternalField(
+      0, const_cast<wchar_t*>(kFXJSEHostObjectTag));
+  hObject->SetAlignedPointerInInternalField(1, pNewBinding);
 }
 
 void FXJSE_ClearObjectBinding(v8::Local<v8::Object> hObject) {
-  ASSERT(!hObject.IsEmpty());
-  ASSERT(hObject->InternalFieldCount() == 2);
+  DCHECK(!hObject.IsEmpty());
+  DCHECK_EQ(hObject->InternalFieldCount(), 2);
   hObject->SetAlignedPointerInInternalField(0, nullptr);
   hObject->SetAlignedPointerInInternalField(1, nullptr);
 }
 
-CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(
-    v8::Local<v8::Object> hJSObject) {
-  ASSERT(!hJSObject.IsEmpty());
-  if (!hJSObject->IsObject())
+CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(v8::Local<v8::Value> hValue) {
+  if (!fxv8::IsObject(hValue))
     return nullptr;
 
-  v8::Local<v8::Object> hObject = hJSObject;
+  v8::Local<v8::Object> hObject = hValue.As<v8::Object>();
   if (hObject->InternalFieldCount() != 2 ||
-      hObject->GetAlignedPointerFromInternalField(0) == g_FXJSEProxyObjectTag) {
+      hObject->GetAlignedPointerFromInternalField(0) == kFXJSEProxyObjectTag) {
     v8::Local<v8::Value> hProtoObject = hObject->GetPrototype();
-    if (hProtoObject.IsEmpty() || !hProtoObject->IsObject())
+    if (!fxv8::IsObject(hProtoObject))
       return nullptr;
 
     hObject = hProtoObject.As<v8::Object>();
     if (hObject->InternalFieldCount() != 2)
       return nullptr;
   }
-  if (hObject->GetAlignedPointerFromInternalField(0) != g_FXJSEHostObjectTag)
+  if (hObject->GetAlignedPointerFromInternalField(0) != kFXJSEHostObjectTag)
     return nullptr;
 
   return static_cast<CFXJSE_HostObject*>(
@@ -180,31 +168,27 @@
 std::unique_ptr<CFXJSE_Context> CFXJSE_Context::Create(
     v8::Isolate* pIsolate,
     const FXJSE_CLASS_DESCRIPTOR* pGlobalClass,
-    CFXJSE_HostObject* pGlobalObject) {
+    CFXJSE_HostObject* pGlobalObject,
+    CXFA_ThisProxy* pProxy) {
   CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
-  auto pContext = pdfium::MakeUnique<CFXJSE_Context>(pIsolate);
 
+  // Private constructor.
+  auto pContext = pdfium::WrapUnique(new CFXJSE_Context(pIsolate, pProxy));
   v8::Local<v8::ObjectTemplate> hObjectTemplate;
   if (pGlobalClass) {
     CFXJSE_Class* pGlobalClassObj =
         CFXJSE_Class::Create(pContext.get(), pGlobalClass, true);
-    ASSERT(pGlobalClassObj);
-    v8::Local<v8::FunctionTemplate> hFunctionTemplate =
-        v8::Local<v8::FunctionTemplate>::New(pIsolate,
-                                             pGlobalClassObj->m_hTemplate);
-    hObjectTemplate = hFunctionTemplate->InstanceTemplate();
+    hObjectTemplate =
+        pGlobalClassObj->GetTemplate(pIsolate)->InstanceTemplate();
   } else {
     hObjectTemplate = v8::ObjectTemplate::New(pIsolate);
     hObjectTemplate->SetInternalFieldCount(2);
   }
-  hObjectTemplate->Set(
-      v8::Symbol::GetToStringTag(pIsolate),
-      v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal)
-          .ToLocalChecked());
+  hObjectTemplate->Set(v8::Symbol::GetToStringTag(pIsolate),
+                       fxv8::NewStringHelper(pIsolate, "global"));
 
   v8::Local<v8::Context> hNewContext =
       v8::Context::New(pIsolate, nullptr, hObjectTemplate);
-
   v8::Local<v8::Object> pThisProxy = hNewContext->Global();
   FXJSE_UpdateProxyBinding(pThisProxy);
 
@@ -212,25 +196,25 @@
   FXJSE_UpdateObjectBinding(pThis, pGlobalObject);
 
   v8::Local<v8::Context> hRootContext = v8::Local<v8::Context>::New(
-      pIsolate, CFXJSE_RuntimeData::Get(pIsolate)->m_hRootContext);
+      pIsolate, CFXJSE_RuntimeData::Get(pIsolate)->GetRootContext());
   hNewContext->SetSecurityToken(hRootContext->GetSecurityToken());
   pContext->m_hContext.Reset(pIsolate, hNewContext);
   return pContext;
 }
 
-CFXJSE_Context::CFXJSE_Context(v8::Isolate* pIsolate) : m_pIsolate(pIsolate) {}
+CFXJSE_Context::CFXJSE_Context(v8::Isolate* pIsolate, CXFA_ThisProxy* pProxy)
+    : m_pIsolate(pIsolate), m_pProxy(pProxy) {}
 
-CFXJSE_Context::~CFXJSE_Context() {}
+CFXJSE_Context::~CFXJSE_Context() = default;
 
-std::unique_ptr<CFXJSE_Value> CFXJSE_Context::GetGlobalObject() {
-  auto pValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
-  CFXJSE_ScopeUtil_IsolateHandleContext scope(this);
+v8::Local<v8::Object> CFXJSE_Context::GetGlobalObject() {
+  v8::Isolate::Scope isolate_scope(GetIsolate());
+  v8::EscapableHandleScope handle_scope(GetIsolate());
   v8::Local<v8::Context> hContext =
       v8::Local<v8::Context>::New(GetIsolate(), m_hContext);
-  v8::Local<v8::Object> hGlobalObject =
+  v8::Local<v8::Object> result =
       hContext->Global()->GetPrototype().As<v8::Object>();
-  pValue->ForceSetValue(hGlobalObject);
-  return pValue;
+  return handle_scope.Escape(result);
 }
 
 v8::Local<v8::Context> CFXJSE_Context::GetContext() {
@@ -245,64 +229,58 @@
   auto pClass =
       std::find_if(m_rgClasses.begin(), m_rgClasses.end(),
                    [szName](const std::unique_ptr<CFXJSE_Class>& item) {
-                     return szName == item->m_szClassName;
+                     return item->IsName(szName);
                    });
   return pClass != m_rgClasses.end() ? pClass->get() : nullptr;
 }
 
 void CFXJSE_Context::EnableCompatibleMode() {
-  ExecuteScript(szCompatibleModeScript, nullptr, nullptr);
-  ExecuteScript(szConsoleScript, nullptr, nullptr);
+  ExecuteScript(szCompatibleModeScript, nullptr, v8::Local<v8::Object>());
+  ExecuteScript(szConsoleScript, nullptr, v8::Local<v8::Object>());
 }
 
-bool CFXJSE_Context::ExecuteScript(const char* szScript,
-                                   CFXJSE_Value* lpRetValue,
-                                   CFXJSE_Value* lpNewThisObject) {
+bool CFXJSE_Context::ExecuteScript(ByteStringView bsScript,
+                                   CFXJSE_Value* pRetValue,
+                                   v8::Local<v8::Object> hNewThis) {
   CFXJSE_ScopeUtil_IsolateHandleContext scope(this);
   v8::Local<v8::Context> hContext = GetIsolate()->GetCurrentContext();
   v8::TryCatch trycatch(GetIsolate());
   v8::Local<v8::String> hScriptString =
-      v8::String::NewFromUtf8(GetIsolate(), szScript,
-                              v8::NewStringType::kNormal)
-          .ToLocalChecked();
-  if (!lpNewThisObject) {
+      fxv8::NewStringHelper(GetIsolate(), bsScript);
+  if (hNewThis.IsEmpty()) {
     v8::Local<v8::Script> hScript;
     if (v8::Script::Compile(hContext, hScriptString).ToLocal(&hScript)) {
-      ASSERT(!trycatch.HasCaught());
+      CHECK(!trycatch.HasCaught());
       v8::Local<v8::Value> hValue;
       if (hScript->Run(hContext).ToLocal(&hValue)) {
-        ASSERT(!trycatch.HasCaught());
-        if (lpRetValue)
-          lpRetValue->ForceSetValue(hValue);
+        CHECK(!trycatch.HasCaught());
+        if (pRetValue)
+          pRetValue->ForceSetValue(GetIsolate(), hValue);
         return true;
       }
     }
-    if (lpRetValue)
-      lpRetValue->ForceSetValue(CreateReturnValue(GetIsolate(), &trycatch));
+    if (pRetValue) {
+      pRetValue->ForceSetValue(GetIsolate(),
+                               CreateReturnValue(GetIsolate(), &trycatch));
+    }
     return false;
   }
 
-  v8::Local<v8::Value> hNewThis = v8::Local<v8::Value>::New(
-      GetIsolate(), lpNewThisObject->DirectGetValue());
-  ASSERT(!hNewThis.IsEmpty());
-  v8::Local<v8::String> hEval =
-      v8::String::NewFromUtf8(GetIsolate(),
-                              "(function () { return eval(arguments[0]); })",
-                              v8::NewStringType::kNormal)
-          .ToLocalChecked();
+  v8::Local<v8::String> hEval = fxv8::NewStringHelper(
+      GetIsolate(), "(function () { return eval(arguments[0]); })");
   v8::Local<v8::Script> hWrapper =
       v8::Script::Compile(hContext, hEval).ToLocalChecked();
   v8::Local<v8::Value> hWrapperValue;
   if (hWrapper->Run(hContext).ToLocal(&hWrapperValue)) {
-    ASSERT(!trycatch.HasCaught());
+    CHECK(!trycatch.HasCaught());
+    CHECK(hWrapperValue->IsFunction());
     v8::Local<v8::Function> hWrapperFn = hWrapperValue.As<v8::Function>();
     v8::Local<v8::Value> rgArgs[] = {hScriptString};
     v8::Local<v8::Value> hValue;
-    if (hWrapperFn->Call(hContext, hNewThis.As<v8::Object>(), 1, rgArgs)
-            .ToLocal(&hValue)) {
-      ASSERT(!trycatch.HasCaught());
-      if (lpRetValue)
-        lpRetValue->ForceSetValue(hValue);
+    if (hWrapperFn->Call(hContext, hNewThis, 1, rgArgs).ToLocal(&hValue)) {
+      DCHECK(!trycatch.HasCaught());
+      if (pRetValue)
+        pRetValue->ForceSetValue(GetIsolate(), hValue);
       return true;
     }
   }
@@ -321,7 +299,9 @@
   }
 #endif  // NDEBUG
 
-  if (lpRetValue)
-    lpRetValue->ForceSetValue(CreateReturnValue(GetIsolate(), &trycatch));
+  if (pRetValue) {
+    pRetValue->ForceSetValue(GetIsolate(),
+                             CreateReturnValue(GetIsolate(), &trycatch));
+  }
   return false;
 }
diff --git a/fxjs/xfa/cfxjse_context.h b/fxjs/xfa/cfxjse_context.h
index b519e77..4217b2b 100644
--- a/fxjs/xfa/cfxjse_context.h
+++ b/fxjs/xfa/cfxjse_context.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,14 +10,16 @@
 #include <memory>
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "v8/include/v8.h"
+#include "v8/include/cppgc/persistent.h"
+#include "v8/include/v8-forward.h"
+#include "v8/include/v8-persistent-handle.h"
 
-class CFXJS_Engine;
 class CFXJSE_Class;
 class CFXJSE_HostObject;
 class CFXJSE_Value;
+class CXFA_ThisProxy;
 struct FXJSE_CLASS_DESCRIPTOR;
 
 class CFXJSE_Context {
@@ -25,34 +27,39 @@
   static std::unique_ptr<CFXJSE_Context> Create(
       v8::Isolate* pIsolate,
       const FXJSE_CLASS_DESCRIPTOR* pGlobalClass,
-      CFXJSE_HostObject* pGlobalObject);
+      CFXJSE_HostObject* pGlobalObject,
+      CXFA_ThisProxy* pProxy);
 
-  explicit CFXJSE_Context(v8::Isolate* pIsolate);
   ~CFXJSE_Context();
 
-  v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); }
+  v8::Isolate* GetIsolate() const { return m_pIsolate; }
   v8::Local<v8::Context> GetContext();
-  std::unique_ptr<CFXJSE_Value> GetGlobalObject();
+  v8::Local<v8::Object> GetGlobalObject();
+
   void AddClass(std::unique_ptr<CFXJSE_Class> pClass);
   CFXJSE_Class* GetClassByName(ByteStringView szName) const;
   void EnableCompatibleMode();
-  bool ExecuteScript(const char* szScript,
-                     CFXJSE_Value* lpRetValue,
-                     CFXJSE_Value* lpNewThisObject);
+
+  // Note: `pNewThisObject` may be empty.
+  bool ExecuteScript(ByteStringView bsScript,
+                     CFXJSE_Value* pRetValue,
+                     v8::Local<v8::Object> pNewThisObject);
 
  private:
+  CFXJSE_Context(v8::Isolate* pIsolate, CXFA_ThisProxy* pProxy);
   CFXJSE_Context(const CFXJSE_Context&) = delete;
   CFXJSE_Context& operator=(const CFXJSE_Context&) = delete;
 
   v8::Global<v8::Context> m_hContext;
   UnownedPtr<v8::Isolate> m_pIsolate;
   std::vector<std::unique_ptr<CFXJSE_Class>> m_rgClasses;
+  cppgc::Persistent<CXFA_ThisProxy> m_pProxy;
 };
 
 void FXJSE_UpdateObjectBinding(v8::Local<v8::Object> hObject,
-                               CFXJSE_HostObject* lpNewBinding);
+                               CFXJSE_HostObject* pNewBinding);
 
 void FXJSE_ClearObjectBinding(v8::Local<v8::Object> hJSObject);
-CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(v8::Local<v8::Object> hJSObject);
+CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(v8::Local<v8::Value> hValue);
 
 #endif  // FXJS_XFA_CFXJSE_CONTEXT_H_
diff --git a/fxjs/xfa/cfxjse_engine.cpp b/fxjs/xfa/cfxjse_engine.cpp
index 9be2498..ecb8c96 100644
--- a/fxjs/xfa/cfxjse_engine.cpp
+++ b/fxjs/xfa/cfxjse_engine.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,34 +9,38 @@
 #include <utility>
 
 #include "core/fxcrt/autorestorer.h"
-#include "core/fxcrt/cfx_widetextbuf.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/stl_util.h"
+#include "core/fxcrt/widetext_buffer.h"
 #include "fxjs/cjs_runtime.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_class.h"
 #include "fxjs/xfa/cfxjse_context.h"
 #include "fxjs/xfa/cfxjse_formcalc_context.h"
+#include "fxjs/xfa/cfxjse_isolatetracker.h"
+#include "fxjs/xfa/cfxjse_nodehelper.h"
 #include "fxjs/xfa/cfxjse_resolveprocessor.h"
 #include "fxjs/xfa/cfxjse_value.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/containers/contains.h"
+#include "v8/include/v8-function-callback.h"
+#include "v8/include/v8-function.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_localemgr.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_nodehelper.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
 #include "xfa/fxfa/parser/cxfa_thisproxy.h"
-#include "xfa/fxfa/parser/cxfa_treelist.h"
+#include "xfa/fxfa/parser/cxfa_variables.h"
 #include "xfa/fxfa/parser/xfa_basic_data.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
 using pdfium::fxjse::kClassTag;
 
-const FXJSE_CLASS_DESCRIPTOR GlobalClassDescriptor = {
+const FXJSE_CLASS_DESCRIPTOR kGlobalClassDescriptor = {
     kClassTag,  // tag
     "Root",     // name
     nullptr,    // methods
@@ -47,7 +51,7 @@
     CFXJSE_Engine::NormalMethodCall,
 };
 
-const FXJSE_CLASS_DESCRIPTOR NormalClassDescriptor = {
+const FXJSE_CLASS_DESCRIPTOR kNormalClassDescriptor = {
     kClassTag,    // tag
     "XFAObject",  // name
     nullptr,      // methods
@@ -58,7 +62,7 @@
     CFXJSE_Engine::NormalMethodCall,
 };
 
-const FXJSE_CLASS_DESCRIPTOR VariablesClassDescriptor = {
+const FXJSE_CLASS_DESCRIPTOR kVariablesClassDescriptor = {
     kClassTag,          // tag
     "XFAScriptObject",  // name
     nullptr,            // methods
@@ -73,28 +77,46 @@
 
 const char kFormCalcRuntime[] = "pfm_rt";
 
-CXFA_ThisProxy* ToThisProxy(CFXJSE_Value* pValue) {
-  CFXJSE_HostObject* pHostObject = pValue->ToHostObject();
-  return pHostObject ? ToThisProxy(pHostObject->AsCXFAObject()) : nullptr;
-}
-
 }  // namespace
 
+CFXJSE_Engine::ResolveResult::ResolveResult() = default;
+
+CFXJSE_Engine::ResolveResult::ResolveResult(const ResolveResult& that) =
+    default;
+
+CFXJSE_Engine::ResolveResult& CFXJSE_Engine::ResolveResult::operator=(
+    const ResolveResult& that) = default;
+
+CFXJSE_Engine::ResolveResult::~ResolveResult() = default;
+
 // static
 CXFA_Object* CFXJSE_Engine::ToObject(
     const v8::FunctionCallbackInfo<v8::Value>& info) {
-  if (!info.Holder()->IsObject())
+  return ToObject(info.GetIsolate(), info.Holder());
+}
+
+// static
+CXFA_Object* CFXJSE_Engine::ToObject(v8::Isolate* pIsolate,
+                                     v8::Local<v8::Value> value) {
+  if (!value->IsObject())
     return nullptr;
 
-  CFXJSE_HostObject* pHostObj =
-      FXJSE_RetrieveObjectBinding(info.Holder().As<v8::Object>());
-  return pHostObj ? pHostObj->AsCXFAObject() : nullptr;
+  return ToObject(FXJSE_RetrieveObjectBinding(value.As<v8::Object>()));
 }
 
 // static.
-CXFA_Object* CFXJSE_Engine::ToObject(CFXJSE_Value* pValue) {
-  CFXJSE_HostObject* pHostObj = pValue->ToHostObject();
-  return pHostObj ? pHostObj->AsCXFAObject() : nullptr;
+CXFA_Object* CFXJSE_Engine::ToObject(v8::Isolate* pIsolate,
+                                     CFXJSE_Value* pValue) {
+  return ToObject(pValue->ToHostObject(pIsolate));
+}
+
+// static
+CXFA_Object* CFXJSE_Engine::ToObject(CFXJSE_HostObject* pHostObj) {
+  if (!pHostObj)
+    return nullptr;
+
+  CJX_Object* pJSObject = pHostObj->AsCJXObject();
+  return pJSObject ? pJSObject->GetXFAObject() : nullptr;
 }
 
 CFXJSE_Engine::CFXJSE_Engine(CXFA_Document* pDocument,
@@ -103,311 +125,352 @@
       m_pSubordinateRuntime(fxjs_runtime),
       m_pDocument(pDocument),
       m_JsContext(CFXJSE_Context::Create(fxjs_runtime->GetIsolate(),
-                                         &GlobalClassDescriptor,
-                                         pDocument->GetRoot())),
-      m_ResolveProcessor(pdfium::MakeUnique<CFXJSE_ResolveProcessor>()) {
+                                         &kGlobalClassDescriptor,
+                                         pDocument->GetRoot()->JSObject(),
+                                         nullptr)),
+      m_NodeHelper(std::make_unique<CFXJSE_NodeHelper>()),
+      m_ResolveProcessor(
+          std::make_unique<CFXJSE_ResolveProcessor>(this, m_NodeHelper.get())) {
   RemoveBuiltInObjs(m_JsContext.get());
   m_JsContext->EnableCompatibleMode();
 
   // Don't know if this can happen before we remove the builtin objs and set
   // compatibility mode.
   m_pJsClass =
-      CFXJSE_Class::Create(m_JsContext.get(), &NormalClassDescriptor, false);
+      CFXJSE_Class::Create(m_JsContext.get(), &kNormalClassDescriptor, false);
 }
 
 CFXJSE_Engine::~CFXJSE_Engine() {
-  for (const auto& pair : m_mapVariableToContext)
-    delete ToThisProxy(pair.second->GetGlobalObject().get());
+  // This is what ensures that the v8 object bound to a CJX_Object
+  // no longer retains that binding since it will outlive that object.
+  CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+  for (const auto& pair : m_mapObjectToObject) {
+    const v8::Global<v8::Object>& binding = pair.second;
+    FXJSE_ClearObjectBinding(v8::Local<v8::Object>::New(GetIsolate(), binding));
+  }
+}
 
-  for (const auto& pair : m_mapObjectToValue)
-    pair.second->ClearHostObject();
+CFXJSE_Engine::EventParamScope::EventParamScope(CFXJSE_Engine* pEngine,
+                                                CXFA_Node* pTarget,
+                                                CXFA_EventParam* pEventParam)
+    : m_pEngine(pEngine),
+      m_pPrevTarget(pEngine->GetEventTarget()),
+      m_pPrevEventParam(pEngine->GetEventParam()) {
+  m_pEngine->m_pTarget = pTarget;
+  m_pEngine->m_eventParam = pEventParam;
+}
+
+CFXJSE_Engine::EventParamScope::~EventParamScope() {
+  m_pEngine->m_pTarget = m_pPrevTarget;
+  m_pEngine->m_eventParam = m_pPrevEventParam;
 }
 
 bool CFXJSE_Engine::RunScript(CXFA_Script::Type eScriptType,
                               WideStringView wsScript,
                               CFXJSE_Value* hRetValue,
                               CXFA_Object* pThisObject) {
-  ByteString btScript;
+  CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
   AutoRestorer<CXFA_Script::Type> typeRestorer(&m_eScriptType);
   m_eScriptType = eScriptType;
+
+  ByteString btScript;
   if (eScriptType == CXFA_Script::Type::Formcalc) {
-    if (!m_FM2JSContext) {
-      m_FM2JSContext = pdfium::MakeUnique<CFXJSE_FormCalcContext>(
+    if (!m_FormCalcContext) {
+      m_FormCalcContext = std::make_unique<CFXJSE_FormCalcContext>(
           GetIsolate(), m_JsContext.get(), m_pDocument.Get());
     }
-    CFX_WideTextBuf wsJavaScript;
-    if (!CFXJSE_FormCalcContext::Translate(wsScript, &wsJavaScript)) {
-      hRetValue->SetUndefined();
+    absl::optional<WideTextBuffer> wsJavaScript =
+        CFXJSE_FormCalcContext::Translate(m_pDocument->GetHeap(), wsScript);
+    if (!wsJavaScript.has_value()) {
+      hRetValue->SetUndefined(GetIsolate());
       return false;
     }
-    btScript = FX_UTF8Encode(wsJavaScript.AsStringView());
+    btScript = FX_UTF8Encode(wsJavaScript.value().AsStringView());
   } else {
     btScript = FX_UTF8Encode(wsScript);
   }
-  AutoRestorer<UnownedPtr<CXFA_Object>> nodeRestorer(&m_pThisObject);
+  AutoRestorer<cppgc::Persistent<CXFA_Object>> nodeRestorer(&m_pThisObject);
   m_pThisObject = pThisObject;
 
-  CFXJSE_Value* pValue =
-      pThisObject ? GetOrCreateJSBindingFromMap(pThisObject) : nullptr;
-  IJS_Runtime::ScopedEventContext ctx(m_pSubordinateRuntime.Get());
-  return m_JsContext->ExecuteScript(btScript.c_str(), hRetValue, pValue);
+  v8::Local<v8::Object> pThisBinding;
+  if (pThisObject)
+    pThisBinding = GetOrCreateJSBindingFromMap(pThisObject);
+
+  IJS_Runtime::ScopedEventContext ctx(m_pSubordinateRuntime);
+  return m_JsContext->ExecuteScript(btScript.AsStringView(), hRetValue,
+                                    pThisBinding);
 }
 
 bool CFXJSE_Engine::QueryNodeByFlag(CXFA_Node* refNode,
                                     WideStringView propname,
-                                    CFXJSE_Value* pValue,
-                                    uint32_t dwFlag,
-                                    bool bSetting) {
+                                    v8::Local<v8::Value>* pValue,
+                                    Mask<XFA_ResolveFlag> dwFlag) {
   if (!refNode)
     return false;
 
-  XFA_RESOLVENODE_RS resolveRs;
-  if (!ResolveObjects(refNode, propname, &resolveRs, dwFlag, nullptr))
+  absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+      ResolveObjects(refNode, propname, dwFlag);
+  if (!maybeResult.has_value())
     return false;
-  if (resolveRs.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    pValue->Assign(
-        GetOrCreateJSBindingFromMap(resolveRs.objects.front().Get()));
+
+  if (maybeResult.value().type == ResolveResult::Type::kNodes) {
+    *pValue =
+        GetOrCreateJSBindingFromMap(maybeResult.value().objects.front().Get());
     return true;
   }
-  if (resolveRs.dwFlags == XFA_ResolveNode_RSType_Attribute &&
-      resolveRs.script_attribute.callback) {
-    CJX_Object* jsObject = resolveRs.objects.front()->JSObject();
-    (*resolveRs.script_attribute.callback)(
-        jsObject, pValue, bSetting, resolveRs.script_attribute.attribute);
+  if (maybeResult.value().type == ResolveResult::Type::kAttribute &&
+      maybeResult.value().script_attribute.callback) {
+    CJX_Object* jsObject = maybeResult.value().objects.front()->JSObject();
+    (*maybeResult.value().script_attribute.callback)(
+        GetIsolate(), jsObject, pValue, false,
+        maybeResult.value().script_attribute.attribute);
+  }
+  return true;
+}
+
+bool CFXJSE_Engine::UpdateNodeByFlag(CXFA_Node* refNode,
+                                     WideStringView propname,
+                                     v8::Local<v8::Value> pValue,
+                                     Mask<XFA_ResolveFlag> dwFlag) {
+  if (!refNode)
+    return false;
+
+  absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+      ResolveObjects(refNode, propname, dwFlag);
+  if (!maybeResult.has_value())
+    return false;
+
+  if (maybeResult.value().type == ResolveResult::Type::kAttribute &&
+      maybeResult.value().script_attribute.callback) {
+    CJX_Object* jsObject = maybeResult.value().objects.front()->JSObject();
+    (*maybeResult.value().script_attribute.callback)(
+        GetIsolate(), jsObject, &pValue, true,
+        maybeResult.value().script_attribute.attribute);
   }
   return true;
 }
 
 // static
-void CFXJSE_Engine::GlobalPropertySetter(CFXJSE_Value* pObject,
+void CFXJSE_Engine::GlobalPropertySetter(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Object> pObject,
                                          ByteStringView szPropName,
-                                         CFXJSE_Value* pValue) {
-  CXFA_Object* lpOrginalNode = ToObject(pObject);
-  CXFA_Document* pDoc = lpOrginalNode->GetDocument();
-  CFXJSE_Engine* lpScriptContext = pDoc->GetScriptContext();
-  CXFA_Node* pRefNode = ToNode(lpScriptContext->GetThisObject());
-  if (lpOrginalNode->IsThisProxy())
-    pRefNode = ToNode(lpScriptContext->GetVariablesThis(lpOrginalNode, false));
+                                         v8::Local<v8::Value> pValue) {
+  CXFA_Object* pOriginalNode = ToObject(pIsolate, pObject);
+  CXFA_Document* pDoc = pOriginalNode->GetDocument();
+  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
+  CXFA_Node* pRefNode = ToNode(pScriptContext->GetThisObject());
+  if (pOriginalNode->IsThisProxy())
+    pRefNode = ToNode(pScriptContext->GetVariablesThis(pOriginalNode));
 
   WideString wsPropName = WideString::FromUTF8(szPropName);
-  if (lpScriptContext->QueryNodeByFlag(
+  if (pScriptContext->UpdateNodeByFlag(
           pRefNode, wsPropName.AsStringView(), pValue,
-          XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings |
-              XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
-              XFA_RESOLVENODE_Attributes,
-          true)) {
+          Mask<XFA_ResolveFlag>{
+              XFA_ResolveFlag::kParent, XFA_ResolveFlag::kSiblings,
+              XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kProperties,
+              XFA_ResolveFlag::kAttributes})) {
     return;
   }
-  if (lpOrginalNode->IsThisProxy() && pValue && pValue->IsUndefined()) {
-    pObject->DeleteObjectProperty(szPropName);
+  if (pOriginalNode->IsThisProxy() && fxv8::IsUndefined(pValue)) {
+    fxv8::ReentrantDeleteObjectPropertyHelper(pScriptContext->GetIsolate(),
+                                              pObject, szPropName);
     return;
   }
   CXFA_FFNotify* pNotify = pDoc->GetNotify();
   if (!pNotify)
     return;
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  auto* pCJSRuntime =
-      static_cast<CJS_Runtime*>(hDoc->GetDocEnvironment()->GetIJSRuntime(hDoc));
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
+  auto* pCJSRuntime = static_cast<CJS_Runtime*>(hDoc->GetIJSRuntime());
   if (!pCJSRuntime)
     return;
 
-  v8::HandleScope handle_scope(lpScriptContext->GetIsolate());
   IJS_Runtime::ScopedEventContext pContext(pCJSRuntime);
-  pCJSRuntime->SetValueByNameInGlobalObject(
-      szPropName, v8::Local<v8::Value>::New(lpScriptContext->GetIsolate(),
-                                            pValue->DirectGetValue()));
+  pCJSRuntime->SetValueByNameInGlobalObject(szPropName, pValue);
 }
 
 // static
-void CFXJSE_Engine::GlobalPropertyGetter(CFXJSE_Value* pObject,
-                                         ByteStringView szPropName,
-                                         CFXJSE_Value* pValue) {
-  CXFA_Object* pOriginalObject = ToObject(pObject);
+v8::Local<v8::Value> CFXJSE_Engine::GlobalPropertyGetter(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Object> pObject,
+    ByteStringView szPropName) {
+  CXFA_Object* pOriginalObject = ToObject(pIsolate, pObject);
   CXFA_Document* pDoc = pOriginalObject->GetDocument();
-  CFXJSE_Engine* lpScriptContext = pDoc->GetScriptContext();
+  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
   WideString wsPropName = WideString::FromUTF8(szPropName);
 
-  pValue->SetUndefined();  // Assume failure.
-  if (lpScriptContext->GetType() == CXFA_Script::Type::Formcalc) {
-    if (szPropName == kFormCalcRuntime) {
-      lpScriptContext->m_FM2JSContext->GlobalPropertyGetter(pValue);
-      return;
-    }
-    XFA_HashCode uHashCode = static_cast<XFA_HashCode>(
-        FX_HashCode_GetW(wsPropName.AsStringView(), false));
+  // Assume failure.
+  v8::Local<v8::Value> pValue = fxv8::NewUndefinedHelper(pIsolate);
+
+  if (pScriptContext->GetType() == CXFA_Script::Type::Formcalc) {
+    if (szPropName == kFormCalcRuntime)
+      return pScriptContext->m_FormCalcContext->GlobalPropertyGetter();
+
+    XFA_HashCode uHashCode =
+        static_cast<XFA_HashCode>(FX_HashCode_GetW(wsPropName.AsStringView()));
     if (uHashCode != XFA_HASHCODE_Layout) {
       CXFA_Object* pObj =
-          lpScriptContext->GetDocument()->GetXFAObject(uHashCode);
-      if (pObj) {
-        pValue->Assign(lpScriptContext->GetOrCreateJSBindingFromMap(pObj));
-        return;
-      }
+          pScriptContext->GetDocument()->GetXFAObject(uHashCode);
+      if (pObj)
+        return pScriptContext->GetOrCreateJSBindingFromMap(pObj);
     }
   }
 
-  CXFA_Node* pRefNode = ToNode(lpScriptContext->GetThisObject());
-  if (pOriginalObject->IsThisProxy()) {
-    pRefNode =
-        ToNode(lpScriptContext->GetVariablesThis(pOriginalObject, false));
+  CXFA_Node* pRefNode = ToNode(pScriptContext->GetThisObject());
+  if (pOriginalObject->IsThisProxy())
+    pRefNode = ToNode(pScriptContext->GetVariablesThis(pOriginalObject));
+
+  if (pScriptContext->QueryNodeByFlag(
+          pRefNode, wsPropName.AsStringView(), &pValue,
+          Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kChildren,
+                                XFA_ResolveFlag::kProperties,
+                                XFA_ResolveFlag::kAttributes})) {
+    return pValue;
   }
-  if (lpScriptContext->QueryNodeByFlag(
-          pRefNode, wsPropName.AsStringView(), pValue,
-          XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
-              XFA_RESOLVENODE_Attributes,
-          false)) {
-    return;
-  }
-  if (lpScriptContext->QueryNodeByFlag(
-          pRefNode, wsPropName.AsStringView(), pValue,
-          XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings, false)) {
-    return;
+  if (pScriptContext->QueryNodeByFlag(
+          pRefNode, wsPropName.AsStringView(), &pValue,
+          Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kParent,
+                                XFA_ResolveFlag::kSiblings})) {
+    return pValue;
   }
 
   CXFA_Object* pScriptObject =
-      lpScriptContext->GetVariablesThis(pOriginalObject, true);
-  if (pScriptObject && lpScriptContext->QueryVariableValue(
-                           pScriptObject->AsNode(), szPropName, pValue, true)) {
-    return;
+      pScriptContext->GetVariablesScript(pOriginalObject);
+  if (pScriptObject && pScriptContext->QueryVariableValue(
+                           CXFA_Script::FromNode(pScriptObject->AsNode()),
+                           szPropName, &pValue)) {
+    return pValue;
   }
 
   CXFA_FFNotify* pNotify = pDoc->GetNotify();
   if (!pNotify)
-    return;
+    return pValue;
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  auto* pCJSRuntime =
-      static_cast<CJS_Runtime*>(hDoc->GetDocEnvironment()->GetIJSRuntime(hDoc));
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
+  auto* pCJSRuntime = static_cast<CJS_Runtime*>(hDoc->GetIJSRuntime());
   if (!pCJSRuntime)
-    return;
+    return pValue;
 
-  v8::HandleScope handle_scope(lpScriptContext->GetIsolate());
   IJS_Runtime::ScopedEventContext pContext(pCJSRuntime);
-  v8::Local<v8::Value> temp_value;
-  if (!pCJSRuntime->GetValueByNameFromGlobalObject(szPropName, &temp_value))
-    return;
+  v8::Local<v8::Value> temp_value =
+      pCJSRuntime->GetValueByNameFromGlobalObject(szPropName);
 
-  if (temp_value.IsEmpty())
-    return;
-
-  pValue->ForceSetValue(temp_value);
-}
-
-int32_t CFXJSE_Engine::GlobalPropTypeGetter(CFXJSE_Value* pOriginalValue,
-                                            ByteStringView szPropName,
-                                            bool bQueryIn) {
-  CXFA_Object* pObject = ToObject(pOriginalValue);
-  if (!pObject)
-    return FXJSE_ClassPropType_None;
-
-  CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext();
-  pObject = lpScriptContext->GetVariablesThis(pObject, false);
-  WideString wsPropName = WideString::FromUTF8(szPropName);
-  if (pObject->JSObject()->HasMethod(wsPropName))
-    return FXJSE_ClassPropType_Method;
-
-  return FXJSE_ClassPropType_Property;
+  return !temp_value.IsEmpty() ? temp_value : pValue;
 }
 
 // static
-void CFXJSE_Engine::NormalPropertyGetter(CFXJSE_Value* pOriginalValue,
-                                         ByteStringView szPropName,
-                                         CFXJSE_Value* pReturnValue) {
-  pReturnValue->SetUndefined();  // Assume failure.
-  CXFA_Object* pOriginalObject = ToObject(pOriginalValue);
+FXJSE_ClassPropType CFXJSE_Engine::GlobalPropTypeGetter(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Object> pHolder,
+    ByteStringView szPropName,
+    bool bQueryIn) {
+  CXFA_Object* pObject = ToObject(pIsolate, pHolder);
+  if (!pObject)
+    return FXJSE_ClassPropType::kNone;
+
+  CFXJSE_Engine* pScriptContext = pObject->GetDocument()->GetScriptContext();
+  pObject = pScriptContext->GetVariablesThis(pObject);
+  WideString wsPropName = WideString::FromUTF8(szPropName);
+  if (pObject->JSObject()->HasMethod(wsPropName))
+    return FXJSE_ClassPropType::kMethod;
+
+  return FXJSE_ClassPropType::kProperty;
+}
+
+// static
+v8::Local<v8::Value> CFXJSE_Engine::NormalPropertyGetter(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Object> pHolder,
+    ByteStringView szPropName) {
+  CXFA_Object* pOriginalObject = ToObject(pIsolate, pHolder);
   if (!pOriginalObject)
-    return;
+    return fxv8::NewUndefinedHelper(pIsolate);
+
+  CFXJSE_Engine* pScriptContext =
+      pOriginalObject->GetDocument()->GetScriptContext();
 
   WideString wsPropName = WideString::FromUTF8(szPropName);
-  CFXJSE_Engine* lpScriptContext =
-      pOriginalObject->GetDocument()->GetScriptContext();
-  CXFA_Object* pObject =
-      lpScriptContext->GetVariablesThis(pOriginalObject, false);
   if (wsPropName.EqualsASCII("xfa")) {
-    CFXJSE_Value* pValue = lpScriptContext->GetOrCreateJSBindingFromMap(
-        lpScriptContext->GetDocument()->GetRoot());
-    pReturnValue->Assign(pValue);
-    return;
+    return pScriptContext->GetOrCreateJSBindingFromMap(
+        pScriptContext->GetDocument()->GetRoot());
   }
 
-  bool bRet = lpScriptContext->QueryNodeByFlag(
-      ToNode(pObject), wsPropName.AsStringView(), pReturnValue,
-      XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
-          XFA_RESOLVENODE_Attributes,
-      false);
-  if (bRet)
-    return;
-
-  if (pObject == lpScriptContext->GetThisObject() ||
-      (lpScriptContext->GetType() == CXFA_Script::Type::Javascript &&
-       !lpScriptContext->IsStrictScopeInJavaScript())) {
-    bRet = lpScriptContext->QueryNodeByFlag(
-        ToNode(pObject), wsPropName.AsStringView(), pReturnValue,
-        XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings, false);
+  v8::Local<v8::Value> pReturnValue = fxv8::NewUndefinedHelper(pIsolate);
+  CXFA_Object* pObject = pScriptContext->GetVariablesThis(pOriginalObject);
+  CXFA_Node* pRefNode = ToNode(pObject);
+  if (pScriptContext->QueryNodeByFlag(
+          pRefNode, wsPropName.AsStringView(), &pReturnValue,
+          Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kChildren,
+                                XFA_ResolveFlag::kProperties,
+                                XFA_ResolveFlag::kAttributes})) {
+    return pReturnValue;
   }
-  if (bRet)
-    return;
-
+  if (pObject == pScriptContext->GetThisObject() ||
+      (pScriptContext->GetType() == CXFA_Script::Type::Javascript &&
+       !pScriptContext->IsStrictScopeInJavaScript())) {
+    if (pScriptContext->QueryNodeByFlag(
+            pRefNode, wsPropName.AsStringView(), &pReturnValue,
+            Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kParent,
+                                  XFA_ResolveFlag::kSiblings})) {
+      return pReturnValue;
+    }
+  }
   CXFA_Object* pScriptObject =
-      lpScriptContext->GetVariablesThis(pOriginalObject, true);
+      pScriptContext->GetVariablesScript(pOriginalObject);
   if (!pScriptObject)
-    return;
+    return pReturnValue;
 
-  bRet = lpScriptContext->QueryVariableValue(ToNode(pScriptObject), szPropName,
-                                             pReturnValue, true);
-  if (bRet)
-    return;
-
-  Optional<XFA_SCRIPTATTRIBUTEINFO> info = XFA_GetScriptAttributeByName(
+  if (pScriptContext->QueryVariableValue(
+          CXFA_Script::FromNode(pScriptObject->AsNode()), szPropName,
+          &pReturnValue)) {
+    return pReturnValue;
+  }
+  absl::optional<XFA_SCRIPTATTRIBUTEINFO> info = XFA_GetScriptAttributeByName(
       pObject->GetElementType(), wsPropName.AsStringView());
   if (info.has_value()) {
-    CJX_Object* jsObject = pObject->JSObject();
-    (*info.value().callback)(jsObject, pReturnValue, false,
-                             info.value().attribute);
-    return;
+    (*info.value().callback)(pIsolate, pObject->JSObject(), &pReturnValue,
+                             false, info.value().attribute);
+    return pReturnValue;
   }
 
   CXFA_FFNotify* pNotify = pObject->GetDocument()->GetNotify();
   if (!pNotify)
-    return;
+    return pReturnValue;
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  auto* pCJSRuntime =
-      static_cast<CJS_Runtime*>(hDoc->GetDocEnvironment()->GetIJSRuntime(hDoc));
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
+  auto* pCJSRuntime = static_cast<CJS_Runtime*>(hDoc->GetIJSRuntime());
   if (!pCJSRuntime)
-    return;
+    return pReturnValue;
 
-  v8::HandleScope handle_scope(lpScriptContext->GetIsolate());
   IJS_Runtime::ScopedEventContext pContext(pCJSRuntime);
-  v8::Local<v8::Value> temp_local;
-  if (!pCJSRuntime->GetValueByNameFromGlobalObject(szPropName, &temp_local))
-    return;
+  v8::Local<v8::Value> temp_local =
+      pCJSRuntime->GetValueByNameFromGlobalObject(szPropName);
 
-  if (temp_local.IsEmpty())
-    return;
-
-  pReturnValue->ForceSetValue(temp_local);
+  return !temp_local.IsEmpty() ? temp_local : pReturnValue;
 }
 
 // static
-void CFXJSE_Engine::NormalPropertySetter(CFXJSE_Value* pOriginalValue,
+void CFXJSE_Engine::NormalPropertySetter(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Object> pHolder,
                                          ByteStringView szPropName,
-                                         CFXJSE_Value* pReturnValue) {
-  CXFA_Object* pOriginalObject = ToObject(pOriginalValue);
+                                         v8::Local<v8::Value> pValue) {
+  CXFA_Object* pOriginalObject = ToObject(pIsolate, pHolder);
   if (!pOriginalObject)
     return;
 
-  CFXJSE_Engine* lpScriptContext =
+  CFXJSE_Engine* pScriptContext =
       pOriginalObject->GetDocument()->GetScriptContext();
-  CXFA_Object* pObject =
-      lpScriptContext->GetVariablesThis(pOriginalObject, false);
+  if (pScriptContext->IsResolvingNodes())
+    return;
+
+  CXFA_Object* pObject = pScriptContext->GetVariablesThis(pOriginalObject);
   WideString wsPropName = WideString::FromUTF8(szPropName);
   WideStringView wsPropNameView = wsPropName.AsStringView();
-  Optional<XFA_SCRIPTATTRIBUTEINFO> info =
+  absl::optional<XFA_SCRIPTATTRIBUTEINFO> info =
       XFA_GetScriptAttributeByName(pObject->GetElementType(), wsPropNameView);
   if (info.has_value()) {
     CJX_Object* jsObject = pObject->JSObject();
-    (*info.value().callback)(jsObject, pReturnValue, true,
+    (*info.value().callback)(pIsolate, jsObject, &pValue, true,
                              info.value().attribute);
     return;
   }
@@ -430,7 +493,7 @@
       info = XFA_GetScriptAttributeByName(pPropOrChild->GetElementType(),
                                           L"{default}");
       if (info.has_value()) {
-        pPropOrChild->JSObject()->ScriptSomDefaultValue(pReturnValue, true,
+        pPropOrChild->JSObject()->ScriptSomDefaultValue(pIsolate, &pValue, true,
                                                         XFA_Attribute::Unknown);
         return;
       }
@@ -438,32 +501,36 @@
   }
 
   CXFA_Object* pScriptObject =
-      lpScriptContext->GetVariablesThis(pOriginalObject, true);
+      pScriptContext->GetVariablesScript(pOriginalObject);
   if (pScriptObject) {
-    lpScriptContext->QueryVariableValue(ToNode(pScriptObject), szPropName,
-                                        pReturnValue, false);
+    pScriptContext->UpdateVariableValue(
+        CXFA_Script::FromNode(pScriptObject->AsNode()), szPropName, pValue);
   }
 }
 
-int32_t CFXJSE_Engine::NormalPropTypeGetter(CFXJSE_Value* pOriginalValue,
-                                            ByteStringView szPropName,
-                                            bool bQueryIn) {
-  CXFA_Object* pObject = ToObject(pOriginalValue);
+FXJSE_ClassPropType CFXJSE_Engine::NormalPropTypeGetter(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Object> pHolder,
+    ByteStringView szPropName,
+    bool bQueryIn) {
+  CXFA_Object* pObject = ToObject(pIsolate, pHolder);
   if (!pObject)
-    return FXJSE_ClassPropType_None;
+    return FXJSE_ClassPropType::kNone;
 
-  CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext();
-  pObject = lpScriptContext->GetVariablesThis(pObject, false);
+  CFXJSE_Engine* pScriptContext = pObject->GetDocument()->GetScriptContext();
+  pObject = pScriptContext->GetVariablesThis(pObject);
   XFA_Element eType = pObject->GetElementType();
   WideString wsPropName = WideString::FromUTF8(szPropName);
   if (pObject->JSObject()->HasMethod(wsPropName))
-    return FXJSE_ClassPropType_Method;
+    return FXJSE_ClassPropType::kMethod;
 
-  if (bQueryIn &&
-      !XFA_GetScriptAttributeByName(eType, wsPropName.AsStringView())) {
-    return FXJSE_ClassPropType_None;
+  if (bQueryIn) {
+    absl::optional<XFA_SCRIPTATTRIBUTEINFO> maybe_info =
+        XFA_GetScriptAttributeByName(eType, wsPropName.AsStringView());
+    if (!maybe_info.has_value())
+      return FXJSE_ClassPropType::kNone;
   }
-  return FXJSE_ClassPropType_Property;
+  return FXJSE_ClassPropType::kProperty;
 }
 
 CJS_Result CFXJSE_Engine::NormalMethodCall(
@@ -473,14 +540,15 @@
   if (!pObject)
     return CJS_Result::Failure(L"no Holder() present.");
 
-  CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext();
-  pObject = lpScriptContext->GetVariablesThis(pObject, false);
+  CFXJSE_Engine* pScriptContext = pObject->GetDocument()->GetScriptContext();
+  pObject = pScriptContext->GetVariablesThis(pObject);
 
   std::vector<v8::Local<v8::Value>> parameters;
   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() {
@@ -491,154 +559,184 @@
   return m_eScriptType;
 }
 
-CFXJSE_Context* CFXJSE_Engine::CreateVariablesContext(CXFA_Node* pScriptNode,
+void CFXJSE_Engine::AddObjectToUpArray(CXFA_Node* pNode) {
+  m_upObjectArray.push_back(pNode);
+}
+
+CXFA_Node* CFXJSE_Engine::LastObjectFromUpArray() {
+  return !m_upObjectArray.empty() ? m_upObjectArray.back() : nullptr;
+}
+
+CFXJSE_Context* CFXJSE_Engine::CreateVariablesContext(CXFA_Script* pScriptNode,
                                                       CXFA_Node* pSubform) {
   if (!pScriptNode || !pSubform)
     return nullptr;
 
-  auto pNewContext =
-      CFXJSE_Context::Create(GetIsolate(), &VariablesClassDescriptor,
-                             new CXFA_ThisProxy(pSubform, pScriptNode));
+  auto* proxy = cppgc::MakeGarbageCollected<CXFA_ThisProxy>(
+      pScriptNode->GetDocument()->GetHeap()->GetAllocationHandle(), pSubform,
+      pScriptNode);
+  auto pNewContext = CFXJSE_Context::Create(
+      GetIsolate(), &kVariablesClassDescriptor, proxy->JSObject(), proxy);
   RemoveBuiltInObjs(pNewContext.get());
   pNewContext->EnableCompatibleMode();
   CFXJSE_Context* pResult = pNewContext.get();
-  m_mapVariableToContext[pScriptNode] = std::move(pNewContext);
+  m_mapVariableToContext[pScriptNode->JSObject()] = std::move(pNewContext);
   return pResult;
 }
 
-CXFA_Object* CFXJSE_Engine::GetVariablesThis(CXFA_Object* pObject,
-                                             bool bScriptNode) {
+CXFA_Object* CFXJSE_Engine::GetVariablesThis(CXFA_Object* pObject) {
   CXFA_ThisProxy* pProxy = ToThisProxy(pObject);
-  if (!pProxy)
-    return pObject;
-
-  return bScriptNode ? pProxy->GetScriptNode() : pProxy->GetThisNode();
+  return pProxy ? pProxy->GetThisNode() : pObject;
 }
 
-bool CFXJSE_Engine::RunVariablesScript(CXFA_Node* pScriptNode) {
+CXFA_Object* CFXJSE_Engine::GetVariablesScript(CXFA_Object* pObject) {
+  CXFA_ThisProxy* pProxy = ToThisProxy(pObject);
+  return pProxy ? pProxy->GetScriptNode() : pObject;
+}
+
+void CFXJSE_Engine::RunVariablesScript(CXFA_Script* pScriptNode) {
   if (!pScriptNode)
-    return false;
+    return;
 
-  if (pScriptNode->GetElementType() != XFA_Element::Script)
-    return true;
+  auto* pParent = CXFA_Variables::FromNode(pScriptNode->GetParent());
+  if (!pParent)
+    return;
 
-  CXFA_Node* pParent = pScriptNode->GetParent();
-  if (!pParent || pParent->GetElementType() != XFA_Element::Variables)
-    return false;
-
-  auto it = m_mapVariableToContext.find(pScriptNode);
+  auto it = m_mapVariableToContext.find(pScriptNode->JSObject());
   if (it != m_mapVariableToContext.end() && it->second)
-    return true;
+    return;
 
   CXFA_Node* pTextNode = pScriptNode->GetFirstChild();
   if (!pTextNode)
-    return false;
+    return;
 
-  Optional<WideString> wsScript =
+  absl::optional<WideString> wsScript =
       pTextNode->JSObject()->TryCData(XFA_Attribute::Value, true);
-  if (!wsScript)
-    return false;
+  if (!wsScript.has_value())
+    return;
 
   ByteString btScript = wsScript->ToUTF8();
-  auto hRetValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
+  auto hRetValue = std::make_unique<CFXJSE_Value>();
   CXFA_Node* pThisObject = pParent->GetParent();
   CFXJSE_Context* pVariablesContext =
       CreateVariablesContext(pScriptNode, pThisObject);
-  AutoRestorer<UnownedPtr<CXFA_Object>> nodeRestorer(&m_pThisObject);
+  AutoRestorer<cppgc::Persistent<CXFA_Object>> nodeRestorer(&m_pThisObject);
   m_pThisObject = pThisObject;
-  return pVariablesContext->ExecuteScript(btScript.c_str(), hRetValue.get(),
-                                          nullptr);
+  pVariablesContext->ExecuteScript(btScript.AsStringView(), hRetValue.get(),
+                                   v8::Local<v8::Object>());
 }
 
-bool CFXJSE_Engine::QueryVariableValue(CXFA_Node* pScriptNode,
+CFXJSE_Context* CFXJSE_Engine::VariablesContextForScriptNode(
+    CXFA_Script* pScriptNode) {
+  if (!pScriptNode)
+    return nullptr;
+
+  auto* variablesNode = CXFA_Variables::FromNode(pScriptNode->GetParent());
+  if (!variablesNode)
+    return nullptr;
+
+  auto it = m_mapVariableToContext.find(pScriptNode->JSObject());
+  return it != m_mapVariableToContext.end() ? it->second.get() : nullptr;
+}
+
+bool CFXJSE_Engine::QueryVariableValue(CXFA_Script* pScriptNode,
                                        ByteStringView szPropName,
-                                       CFXJSE_Value* pValue,
-                                       bool bGetter) {
-  if (!pScriptNode || pScriptNode->GetElementType() != XFA_Element::Script)
+                                       v8::Local<v8::Value>* pValue) {
+  CFXJSE_Context* pVariableContext = VariablesContextForScriptNode(pScriptNode);
+  if (!pVariableContext)
     return false;
 
-  CXFA_Node* variablesNode = pScriptNode->GetParent();
-  if (!variablesNode ||
-      variablesNode->GetElementType() != XFA_Element::Variables)
+  v8::Local<v8::Object> pObject = pVariableContext->GetGlobalObject();
+  if (!fxv8::ReentrantHasObjectOwnPropertyHelper(GetIsolate(), pObject,
+                                                 szPropName)) {
     return false;
-
-  auto it = m_mapVariableToContext.find(pScriptNode);
-  if (it == m_mapVariableToContext.end() || !it->second)
-    return false;
-
-  CFXJSE_Context* pVariableContext = it->second.get();
-  std::unique_ptr<CFXJSE_Value> pObject = pVariableContext->GetGlobalObject();
-  auto hVariableValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
-  if (!bGetter) {
-    pObject->SetObjectOwnProperty(szPropName, pValue);
-    return true;
   }
 
-  if (!pObject->HasObjectOwnProperty(szPropName, false))
-    return false;
-
-  pObject->GetObjectProperty(szPropName, hVariableValue.get());
-  if (hVariableValue->IsFunction())
-    pValue->SetFunctionBind(hVariableValue.get(), pObject.get());
-  else if (bGetter)
-    pValue->Assign(hVariableValue.get());
-  else
-    hVariableValue.get()->Assign(pValue);
+  v8::Local<v8::Value> hVariableValue =
+      fxv8::ReentrantGetObjectPropertyHelper(GetIsolate(), pObject, szPropName);
+  if (fxv8::IsFunction(hVariableValue)) {
+    v8::Local<v8::Function> maybeFunc = CFXJSE_Value::NewBoundFunction(
+        GetIsolate(), hVariableValue.As<v8::Function>(), pObject);
+    if (!maybeFunc.IsEmpty())
+      *pValue = maybeFunc;
+  } else {
+    *pValue = hVariableValue;
+  }
   return true;
 }
 
-void CFXJSE_Engine::RemoveBuiltInObjs(CFXJSE_Context* pContext) const {
-  const ByteStringView kObjNames[2] = {"Number", "Date"};
-  std::unique_ptr<CFXJSE_Value> pObject = pContext->GetGlobalObject();
-  auto hProp = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
-  for (const auto& obj : kObjNames) {
-    if (pObject->GetObjectProperty(obj, hProp.get()))
-      pObject->DeleteObjectProperty(obj);
-  }
-}
-
-bool CFXJSE_Engine::ResolveObjects(CXFA_Object* refObject,
-                                   WideStringView wsExpression,
-                                   XFA_RESOLVENODE_RS* resolveNodeRS,
-                                   uint32_t dwStyles,
-                                   CXFA_Node* bindNode) {
-  if (wsExpression.IsEmpty())
+bool CFXJSE_Engine::UpdateVariableValue(CXFA_Script* pScriptNode,
+                                        ByteStringView szPropName,
+                                        v8::Local<v8::Value> pValue) {
+  CFXJSE_Context* pVariableContext = VariablesContextForScriptNode(pScriptNode);
+  if (!pVariableContext)
     return false;
 
-  if (m_eScriptType != CXFA_Script::Type::Formcalc ||
-      (dwStyles & (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings))) {
+  v8::Local<v8::Object> pObject = pVariableContext->GetGlobalObject();
+  fxv8::ReentrantSetObjectOwnPropertyHelper(GetIsolate(), pObject, szPropName,
+                                            pValue);
+  return true;
+}
+
+void CFXJSE_Engine::RemoveBuiltInObjs(CFXJSE_Context* pContext) {
+  CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+  v8::Local<v8::Object> pObject = pContext->GetGlobalObject();
+  fxv8::ReentrantDeleteObjectPropertyHelper(GetIsolate(), pObject, "Number");
+  fxv8::ReentrantDeleteObjectPropertyHelper(GetIsolate(), pObject, "Date");
+}
+
+absl::optional<CFXJSE_Engine::ResolveResult> CFXJSE_Engine::ResolveObjects(
+    CXFA_Object* refObject,
+    WideStringView wsExpression,
+    Mask<XFA_ResolveFlag> dwStyles) {
+  return ResolveObjectsWithBindNode(refObject, wsExpression, dwStyles, nullptr);
+}
+
+absl::optional<CFXJSE_Engine::ResolveResult>
+CFXJSE_Engine::ResolveObjectsWithBindNode(CXFA_Object* refObject,
+                                          WideStringView wsExpression,
+                                          Mask<XFA_ResolveFlag> dwStyles,
+                                          CXFA_Node* bindNode) {
+  if (wsExpression.IsEmpty())
+    return absl::nullopt;
+
+  AutoRestorer<bool> resolving_restorer(&m_bResolvingNodes);
+  m_bResolvingNodes = true;
+
+  const bool bParentOrSiblings =
+      !!(dwStyles & Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kParent,
+                                          XFA_ResolveFlag::kSiblings});
+  if (m_eScriptType != CXFA_Script::Type::Formcalc || bParentOrSiblings)
     m_upObjectArray.clear();
-  }
-  if (refObject && refObject->IsNode() &&
-      (dwStyles & (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings))) {
+  if (refObject && refObject->IsNode() && bParentOrSiblings)
     m_upObjectArray.push_back(refObject->AsNode());
-  }
 
+  ResolveResult result;
   bool bNextCreate = false;
-  CXFA_NodeHelper* pNodeHelper = m_ResolveProcessor->GetNodeHelper();
-  if (dwStyles & XFA_RESOLVENODE_CreateNode)
-    pNodeHelper->SetCreateNodeType(bindNode);
+  if (dwStyles & XFA_ResolveFlag::kCreateNode)
+    m_NodeHelper->SetCreateNodeType(bindNode);
 
-  pNodeHelper->m_pCreateParent = nullptr;
-  pNodeHelper->m_iCurAllStart = -1;
+  m_NodeHelper->m_pCreateParent = nullptr;
+  m_NodeHelper->m_iCurAllStart = -1;
 
-  CFXJSE_ResolveNodeData rndFind(this);
+  CFXJSE_ResolveProcessor::NodeData rndFind;
   int32_t nStart = 0;
   int32_t nLevel = 0;
 
-  std::vector<UnownedPtr<CXFA_Object>> findObjects;
+  std::vector<cppgc::Member<CXFA_Object>> findObjects;
   findObjects.emplace_back(refObject ? refObject : m_pDocument->GetRoot());
   int32_t nNodes = 0;
+  CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
   while (true) {
-    nNodes = pdfium::CollectionSize<int32_t>(findObjects);
+    nNodes = fxcrt::CollectionSize<int32_t>(findObjects);
     int32_t i = 0;
     rndFind.m_dwStyles = dwStyles;
     m_ResolveProcessor->SetCurStart(nStart);
     nStart = m_ResolveProcessor->GetFilter(wsExpression, nStart, rndFind);
     if (nStart < 1) {
-      if ((dwStyles & XFA_RESOLVENODE_CreateNode) && !bNextCreate) {
+      if ((dwStyles & XFA_ResolveFlag::kCreateNode) && !bNextCreate) {
         CXFA_Node* pDataNode = nullptr;
-        nStart = pNodeHelper->m_iCurAllStart;
+        nStart = m_NodeHelper->m_iCurAllStart;
         if (nStart != -1) {
           pDataNode = m_pDocument->GetNotBindNode(findObjects);
           if (pDataNode) {
@@ -652,9 +750,9 @@
           findObjects.emplace_back(pDataNode);
           break;
         }
-        dwStyles |= XFA_RESOLVENODE_Bind;
+        dwStyles |= XFA_ResolveFlag::kBind;
         findObjects.clear();
-        findObjects.emplace_back(pNodeHelper->m_pAllStartParent.Get());
+        findObjects.emplace_back(m_NodeHelper->m_pAllStartParent.Get());
         continue;
       }
       break;
@@ -662,62 +760,64 @@
     if (bNextCreate) {
       int32_t checked_length =
           pdfium::base::checked_cast<int32_t>(wsExpression.GetLength());
-      if (pNodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition,
-                                  nStart == checked_length, this)) {
+      if (m_NodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition,
+                                   nStart == checked_length, this)) {
         continue;
       }
       break;
     }
-    std::vector<UnownedPtr<CXFA_Object>> retObjects;
+    std::vector<cppgc::Member<CXFA_Object>> retObjects;
     while (i < nNodes) {
       bool bDataBind = false;
-      if (((dwStyles & XFA_RESOLVENODE_Bind) ||
-           (dwStyles & XFA_RESOLVENODE_CreateNode)) &&
+      if (((dwStyles & XFA_ResolveFlag::kBind) ||
+           (dwStyles & XFA_ResolveFlag::kCreateNode)) &&
           nNodes > 1) {
-        CFXJSE_ResolveNodeData rndBind(nullptr);
+        CFXJSE_ResolveProcessor::NodeData rndBind;
         m_ResolveProcessor->GetFilter(wsExpression, nStart, rndBind);
-        m_ResolveProcessor->SetIndexDataBind(rndBind.m_wsCondition, i, nNodes);
+        i = m_ResolveProcessor->IndexForDataBind(rndBind.m_wsCondition, nNodes);
         bDataBind = true;
       }
       rndFind.m_CurObject = findObjects[i++].Get();
       rndFind.m_nLevel = nLevel;
-      rndFind.m_dwFlag = XFA_ResolveNode_RSType_Nodes;
-      if (!m_ResolveProcessor->Resolve(rndFind))
+      rndFind.m_Result.type = ResolveResult::Type::kNodes;
+      if (!m_ResolveProcessor->Resolve(GetIsolate(), rndFind))
         continue;
 
-      if (rndFind.m_dwFlag == XFA_ResolveNode_RSType_Attribute &&
-          rndFind.m_ScriptAttribute.callback &&
+      if (rndFind.m_Result.type == ResolveResult::Type::kAttribute &&
+          rndFind.m_Result.script_attribute.callback &&
           nStart <
               pdfium::base::checked_cast<int32_t>(wsExpression.GetLength())) {
-        auto pValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
-        CJX_Object* jsObject = rndFind.m_Objects.front()->JSObject();
-        (*rndFind.m_ScriptAttribute.callback)(
-            jsObject, pValue.get(), false, rndFind.m_ScriptAttribute.attribute);
-        if (!pValue->IsEmpty())
-          rndFind.m_Objects.front() = ToObject(pValue.get());
+        v8::Local<v8::Value> pValue;
+        CJX_Object* jsObject = rndFind.m_Result.objects.front()->JSObject();
+        (*rndFind.m_Result.script_attribute.callback)(
+            GetIsolate(), jsObject, &pValue, false,
+            rndFind.m_Result.script_attribute.attribute);
+        if (!pValue.IsEmpty()) {
+          rndFind.m_Result.objects.front() = ToObject(GetIsolate(), pValue);
+        }
       }
       if (!m_upObjectArray.empty())
         m_upObjectArray.pop_back();
-      retObjects.insert(retObjects.end(), rndFind.m_Objects.begin(),
-                        rndFind.m_Objects.end());
-      rndFind.m_Objects.clear();
+      retObjects.insert(retObjects.end(), rndFind.m_Result.objects.begin(),
+                        rndFind.m_Result.objects.end());
+      rndFind.m_Result.objects.clear();
       if (bDataBind)
         break;
     }
     findObjects.clear();
 
-    nNodes = pdfium::CollectionSize<int32_t>(retObjects);
+    nNodes = fxcrt::CollectionSize<int32_t>(retObjects);
     if (nNodes < 1) {
-      if (dwStyles & XFA_RESOLVENODE_CreateNode) {
+      if (dwStyles & XFA_ResolveFlag::kCreateNode) {
         bNextCreate = true;
-        if (!pNodeHelper->m_pCreateParent) {
-          pNodeHelper->m_pCreateParent = ToNode(rndFind.m_CurObject.Get());
-          pNodeHelper->m_iCreateCount = 1;
+        if (!m_NodeHelper->m_pCreateParent) {
+          m_NodeHelper->m_pCreateParent = ToNode(rndFind.m_CurObject);
+          m_NodeHelper->m_iCreateCount = 1;
         }
         int32_t checked_length =
             pdfium::base::checked_cast<int32_t>(wsExpression.GetLength());
-        if (pNodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition,
-                                    nStart == checked_length, this)) {
+        if (m_NodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition,
+                                     nStart == checked_length, this)) {
           continue;
         }
       }
@@ -725,101 +825,95 @@
     }
 
     findObjects = std::move(retObjects);
-    rndFind.m_Objects.clear();
-    if (nLevel == 0)
-      dwStyles &= ~(XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings);
-
+    rndFind.m_Result.objects.clear();
+    if (nLevel == 0) {
+      dwStyles.Clear(XFA_ResolveFlag::kParent);
+      dwStyles.Clear(XFA_ResolveFlag::kSiblings);
+    }
     nLevel++;
   }
 
   if (!bNextCreate) {
-    resolveNodeRS->dwFlags = rndFind.m_dwFlag;
+    result.type = rndFind.m_Result.type;
     if (nNodes > 0) {
-      resolveNodeRS->objects.insert(resolveNodeRS->objects.end(),
-                                    findObjects.begin(), findObjects.end());
+      result.objects.insert(result.objects.end(), findObjects.begin(),
+                            findObjects.end());
     }
-    if (rndFind.m_dwFlag == XFA_ResolveNode_RSType_Attribute) {
-      resolveNodeRS->script_attribute = rndFind.m_ScriptAttribute;
-      return true;
+    if (rndFind.m_Result.type == ResolveResult::Type::kAttribute) {
+      result.script_attribute = rndFind.m_Result.script_attribute;
+      return result;
     }
   }
-  if (dwStyles & (XFA_RESOLVENODE_CreateNode | XFA_RESOLVENODE_Bind |
-                  XFA_RESOLVENODE_BindNew)) {
-    if (pNodeHelper->m_pCreateParent)
-      resolveNodeRS->objects.emplace_back(pNodeHelper->m_pCreateParent.Get());
+  if ((dwStyles & XFA_ResolveFlag::kCreateNode) ||
+      (dwStyles & XFA_ResolveFlag::kBind) ||
+      (dwStyles & XFA_ResolveFlag::kBindNew)) {
+    if (m_NodeHelper->m_pCreateParent)
+      result.objects.emplace_back(m_NodeHelper->m_pCreateParent.Get());
     else
-      pNodeHelper->CreateNodeForCondition(rndFind.m_wsCondition);
+      m_NodeHelper->CreateNodeForCondition(rndFind.m_wsCondition);
 
-    resolveNodeRS->dwFlags = pNodeHelper->m_iCreateFlag;
-    if (resolveNodeRS->dwFlags == XFA_ResolveNode_RSType_CreateNodeOne) {
-      if (pNodeHelper->m_iCurAllStart != -1)
-        resolveNodeRS->dwFlags = XFA_ResolveNode_RSType_CreateNodeMidAll;
+    result.type = m_NodeHelper->m_iCreateFlag;
+    if (result.type == ResolveResult::Type::kCreateNodeOne) {
+      if (m_NodeHelper->m_iCurAllStart != -1)
+        result.type = ResolveResult::Type::kCreateNodeMidAll;
     }
 
-    if (!bNextCreate && (dwStyles & XFA_RESOLVENODE_CreateNode))
-      resolveNodeRS->dwFlags = XFA_ResolveNode_RSType_ExistNodes;
+    if (!bNextCreate && (dwStyles & XFA_ResolveFlag::kCreateNode))
+      result.type = ResolveResult::Type::kExistNodes;
 
-    return !resolveNodeRS->objects.empty();
+    if (result.objects.empty())
+      return absl::nullopt;
+
+    return result;
   }
-  return nNodes > 0;
+  if (nNodes == 0)
+    return absl::nullopt;
+
+  return result;
 }
 
-void CFXJSE_Engine::AddToCacheList(std::unique_ptr<CXFA_List> pList) {
-  m_CacheList.push_back(std::move(pList));
+v8::Local<v8::Object> CFXJSE_Engine::GetOrCreateJSBindingFromMap(
+    CXFA_Object* pObject) {
+  RunVariablesScript(CXFA_Script::FromNode(pObject->AsNode()));
+
+  CJX_Object* pCJXObject = pObject->JSObject();
+  auto iter = m_mapObjectToObject.find(pCJXObject);
+  if (iter != m_mapObjectToObject.end())
+    return v8::Local<v8::Object>::New(GetIsolate(), iter->second);
+
+  v8::Local<v8::Object> binding = pCJXObject->NewBoundV8Object(
+      GetIsolate(), m_pJsClass->GetTemplate(GetIsolate()));
+
+  m_mapObjectToObject[pCJXObject].Reset(GetIsolate(), binding);
+  return binding;
 }
 
-CFXJSE_Value* CFXJSE_Engine::GetOrCreateJSBindingFromMap(CXFA_Object* pObject) {
-  if (pObject->IsNode())
-    RunVariablesScript(pObject->AsNode());
-
-  auto iter = m_mapObjectToValue.find(pObject);
-  if (iter != m_mapObjectToValue.end())
-    return iter->second.get();
-
-  auto jsValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
-  jsValue->SetHostObject(pObject, m_pJsClass.Get());
-
-  CFXJSE_Value* pValue = jsValue.get();
-  m_mapObjectToValue.insert(std::make_pair(pObject, std::move(jsValue)));
-  return pValue;
-}
-
-void CFXJSE_Engine::RemoveJSBindingFromMap(CXFA_Object* pObject) {
-  auto iter = m_mapObjectToValue.find(pObject);
-  if (iter == m_mapObjectToValue.end())
-    return;
-
-  iter->second->ClearHostObject();
-  m_mapObjectToValue.erase(iter);
-}
-
-void CFXJSE_Engine::SetNodesOfRunScript(std::vector<CXFA_Node*>* pArray) {
+void CFXJSE_Engine::SetNodesOfRunScript(
+    std::vector<cppgc::Persistent<CXFA_Node>>* pArray) {
   m_pScriptNodeArray = pArray;
 }
 
 void CFXJSE_Engine::AddNodesOfRunScript(CXFA_Node* pNode) {
-  if (m_pScriptNodeArray && !pdfium::ContainsValue(*m_pScriptNodeArray, pNode))
-    m_pScriptNodeArray->push_back(pNode);
+  if (m_pScriptNodeArray && !pdfium::Contains(*m_pScriptNodeArray, pNode))
+    m_pScriptNodeArray->emplace_back(pNode);
 }
 
 CXFA_Object* CFXJSE_Engine::ToXFAObject(v8::Local<v8::Value> obj) {
-  if (obj.IsEmpty() || !obj->IsObject())
+  if (!fxv8::IsObject(obj))
     return nullptr;
 
   CFXJSE_HostObject* pHostObj =
       FXJSE_RetrieveObjectBinding(obj.As<v8::Object>());
-  return pHostObj ? pHostObj->AsCXFAObject() : nullptr;
+  if (!pHostObj)
+    return nullptr;
+
+  CJX_Object* pJSObject = pHostObj->AsCJXObject();
+  return pJSObject ? pJSObject->GetXFAObject() : nullptr;
 }
 
-v8::Local<v8::Value> CFXJSE_Engine::NewXFAObject(
-    CXFA_Object* obj,
-    v8::Global<v8::FunctionTemplate>& tmpl) {
+v8::Local<v8::Object> CFXJSE_Engine::NewNormalXFAObject(CXFA_Object* obj) {
   v8::EscapableHandleScope scope(GetIsolate());
-  v8::Local<v8::FunctionTemplate> klass =
-      v8::Local<v8::FunctionTemplate>::New(GetIsolate(), tmpl);
-  v8::Local<v8::Object> object = klass->InstanceTemplate()
-                                     ->NewInstance(m_JsContext->GetContext())
-                                     .ToLocalChecked();
-  FXJSE_UpdateObjectBinding(object, obj);
+  v8::Local<v8::Object> object = obj->JSObject()->NewBoundV8Object(
+      GetIsolate(), GetJseNormalClass()->GetTemplate(GetIsolate()));
   return scope.Escape(object);
 }
diff --git a/fxjs/xfa/cfxjse_engine.h b/fxjs/xfa/cfxjse_engine.h
index addf0cc..c6a95c3 100644
--- a/fxjs/xfa/cfxjse_engine.h
+++ b/fxjs/xfa/cfxjse_engine.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,133 +9,210 @@
 
 #include <map>
 #include <memory>
+#include <type_traits>
 #include <vector>
 
+#include "core/fxcrt/mask.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "fxjs/cfx_v8.h"
-#include "v8/include/v8.h"
+#include "v8/include/cppgc/persistent.h"
+#include "v8/include/v8-forward.h"
+#include "v8/include/v8-persistent-handle.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_script.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
+#include "xfa/fxfa/parser/xfa_basic_data.h"
 
 class CFXJSE_Class;
 class CFXJSE_Context;
 class CFXJSE_FormCalcContext;
+class CFXJSE_HostObject;
+class CFXJSE_NodeHelper;
 class CFXJSE_ResolveProcessor;
+class CFXJSE_Value;
 class CJS_Runtime;
-class CXFA_List;
 
-// Flags for |dwStyles| argument to CFXJSE_Engine::ResolveObjects().
-#define XFA_RESOLVENODE_Children 0x0001
-#define XFA_RESOLVENODE_TagName 0x0002
-#define XFA_RESOLVENODE_Attributes 0x0004
-#define XFA_RESOLVENODE_Properties 0x0008
-#define XFA_RESOLVENODE_Siblings 0x0020
-#define XFA_RESOLVENODE_Parent 0x0040
-#define XFA_RESOLVENODE_AnyChild 0x0080
-#define XFA_RESOLVENODE_ALL 0x0100
-#define XFA_RESOLVENODE_CreateNode 0x0400
-#define XFA_RESOLVENODE_Bind 0x0800
-#define XFA_RESOLVENODE_BindNew 0x1000
+enum class XFA_ResolveFlag : uint16_t {
+  kChildren = 1 << 0,
+  kTagName = 1 << 1,
+  kAttributes = 1 << 2,
+  kProperties = 1 << 3,
+  kSiblings = 1 << 5,
+  kParent = 1 << 6,
+  kAnyChild = 1 << 7,
+  kALL = 1 << 8,
+  kCreateNode = 1 << 10,
+  kBind = 1 << 11,
+  kBindNew = 1 << 12,
+};
 
 class CFXJSE_Engine final : public CFX_V8 {
  public:
+  class ResolveResult {
+    CPPGC_STACK_ALLOCATED();  // Allow raw/unowned pointers.
+
+   public:
+    enum class Type {
+      kNodes = 0,
+      kAttribute,
+      kCreateNodeOne,
+      kCreateNodeAll,
+      kCreateNodeMidAll,
+      kExistNodes,
+    };
+
+    ResolveResult();
+    ResolveResult(const ResolveResult& that);
+    ResolveResult& operator=(const ResolveResult& that);
+    ~ResolveResult();
+
+    Type type = Type::kNodes;
+    XFA_SCRIPTATTRIBUTEINFO script_attribute = {};
+
+    // Vector of Member would be correct for stack-based vectors, if
+    // STL worked with cppgc.
+    std::vector<cppgc::Member<CXFA_Object>> objects;
+  };
+
   static CXFA_Object* ToObject(const v8::FunctionCallbackInfo<v8::Value>& info);
-  static CXFA_Object* ToObject(CFXJSE_Value* pValue);
-  static void GlobalPropertyGetter(CFXJSE_Value* pObject,
+  static CXFA_Object* ToObject(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value> value);
+  static CXFA_Object* ToObject(v8::Isolate* pIsolate, CFXJSE_Value* pValue);
+  static CXFA_Object* ToObject(CFXJSE_HostObject* pHostObj);
+  static v8::Local<v8::Value> GlobalPropertyGetter(
+      v8::Isolate* pIsolate,
+      v8::Local<v8::Object> pObject,
+      ByteStringView szPropName);
+  static void GlobalPropertySetter(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Object> pObject,
                                    ByteStringView szPropName,
-                                   CFXJSE_Value* pValue);
-  static void GlobalPropertySetter(CFXJSE_Value* pObject,
+                                   v8::Local<v8::Value> pValue);
+  static v8::Local<v8::Value> NormalPropertyGetter(
+      v8::Isolate* pIsolate,
+      v8::Local<v8::Object> pObject,
+      ByteStringView szPropName);
+  static void NormalPropertySetter(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Object> pObject,
                                    ByteStringView szPropName,
-                                   CFXJSE_Value* pValue);
-  static void NormalPropertyGetter(CFXJSE_Value* pObject,
-                                   ByteStringView szPropName,
-                                   CFXJSE_Value* pValue);
-  static void NormalPropertySetter(CFXJSE_Value* pObject,
-                                   ByteStringView szPropName,
-                                   CFXJSE_Value* pValue);
+                                   v8::Local<v8::Value> pValue);
   static CJS_Result NormalMethodCall(
       const v8::FunctionCallbackInfo<v8::Value>& info,
       const WideString& functionName);
-  static int32_t NormalPropTypeGetter(CFXJSE_Value* pObject,
-                                      ByteStringView szPropName,
-                                      bool bQueryIn);
-  static int32_t GlobalPropTypeGetter(CFXJSE_Value* pObject,
-                                      ByteStringView szPropName,
-                                      bool bQueryIn);
+  static FXJSE_ClassPropType NormalPropTypeGetter(v8::Isolate* pIsolate,
+                                                  v8::Local<v8::Object> pObject,
+                                                  ByteStringView szPropName,
+                                                  bool bQueryIn);
+  static FXJSE_ClassPropType GlobalPropTypeGetter(v8::Isolate* pIsolate,
+                                                  v8::Local<v8::Object> pObject,
+                                                  ByteStringView szPropName,
+                                                  bool bQueryIn);
 
   CFXJSE_Engine(CXFA_Document* pDocument, CJS_Runtime* fxjs_runtime);
   ~CFXJSE_Engine() override;
 
-  void SetEventParam(CXFA_EventParam* param) { m_eventParam = param; }
-  CXFA_EventParam* GetEventParam() const { return m_eventParam.Get(); }
+  class EventParamScope {
+    CPPGC_STACK_ALLOCATED();
+
+   public:
+    EventParamScope(CFXJSE_Engine* pEngine,
+                    CXFA_Node* pTarget,
+                    CXFA_EventParam* pEventParam);
+    ~EventParamScope();
+
+   private:
+    UnownedPtr<CFXJSE_Engine> m_pEngine;
+    UnownedPtr<CXFA_Node> m_pPrevTarget;
+    UnownedPtr<CXFA_EventParam> m_pPrevEventParam;
+  };
+  friend class EventParamScope;
+
+  CXFA_Node* GetEventTarget() const { return m_pTarget; }
+  CXFA_EventParam* GetEventParam() const { return m_eventParam; }
   bool RunScript(CXFA_Script::Type eScriptType,
                  WideStringView wsScript,
                  CFXJSE_Value* pRetValue,
                  CXFA_Object* pThisObject);
 
-  bool ResolveObjects(CXFA_Object* refObject,
-                      WideStringView wsExpression,
-                      XFA_RESOLVENODE_RS* resolveNodeRS,
-                      uint32_t dwStyles,
-                      CXFA_Node* bindNode);
+  absl::optional<ResolveResult> ResolveObjects(CXFA_Object* refObject,
+                                               WideStringView wsExpression,
+                                               Mask<XFA_ResolveFlag> dwStyles);
 
-  CFXJSE_Value* GetOrCreateJSBindingFromMap(CXFA_Object* pObject);
-  void RemoveJSBindingFromMap(CXFA_Object* pObject);
+  absl::optional<ResolveResult> ResolveObjectsWithBindNode(
+      CXFA_Object* refObject,
+      WideStringView wsExpression,
+      Mask<XFA_ResolveFlag> dwStyles,
+      CXFA_Node* bindNode);
 
-  void AddToCacheList(std::unique_ptr<CXFA_List> pList);
-  CXFA_Object* GetThisObject() const { return m_pThisObject.Get(); }
+  v8::Local<v8::Object> GetOrCreateJSBindingFromMap(CXFA_Object* pObject);
 
-  void SetNodesOfRunScript(std::vector<CXFA_Node*>* pArray);
+  CXFA_Object* GetThisObject() const { return m_pThisObject; }
+  CFXJSE_Class* GetJseNormalClass() const { return m_pJsClass; }
+  CXFA_Document* GetDocument() const { return m_pDocument.Get(); }
+
+  void SetNodesOfRunScript(std::vector<cppgc::Persistent<CXFA_Node>>* pArray);
   void AddNodesOfRunScript(CXFA_Node* pNode);
-  CFXJSE_Class* GetJseNormalClass() const { return m_pJsClass.Get(); }
 
   void SetRunAtType(XFA_AttributeValue eRunAt) { m_eRunAtType = eRunAt; }
   bool IsRunAtClient() { return m_eRunAtType != XFA_AttributeValue::Server; }
 
   CXFA_Script::Type GetType();
-  std::vector<CXFA_Node*>* GetUpObjectArray() { return &m_upObjectArray; }
-  CXFA_Document* GetDocument() const { return m_pDocument.Get(); }
+
+  void AddObjectToUpArray(CXFA_Node* pNode);
+  CXFA_Node* LastObjectFromUpArray();
 
   CXFA_Object* ToXFAObject(v8::Local<v8::Value> obj);
-  v8::Local<v8::Value> NewXFAObject(CXFA_Object* obj,
-                                    v8::Global<v8::FunctionTemplate>& tmpl);
+  v8::Local<v8::Object> NewNormalXFAObject(CXFA_Object* obj);
+
+  bool IsResolvingNodes() const { return m_bResolvingNodes; }
+
+  CFXJSE_Context* GetJseContextForTest() const { return GetJseContext(); }
 
  private:
-  CFXJSE_Context* CreateVariablesContext(CXFA_Node* pScriptNode,
+  CFXJSE_Context* GetJseContext() const { return m_JsContext.get(); }
+  CFXJSE_Context* CreateVariablesContext(CXFA_Script* pScriptNode,
                                          CXFA_Node* pSubform);
-  void RemoveBuiltInObjs(CFXJSE_Context* pContext) const;
+  void RemoveBuiltInObjs(CFXJSE_Context* pContext);
   bool QueryNodeByFlag(CXFA_Node* refNode,
                        WideStringView propname,
-                       CFXJSE_Value* pValue,
-                       uint32_t dwFlag,
-                       bool bSetting);
+                       v8::Local<v8::Value>* pValue,
+                       Mask<XFA_ResolveFlag> dwFlag);
+  bool UpdateNodeByFlag(CXFA_Node* refNode,
+                        WideStringView propname,
+                        v8::Local<v8::Value> pValue,
+                        Mask<XFA_ResolveFlag> dwFlag);
   bool IsStrictScopeInJavaScript();
-  CXFA_Object* GetVariablesThis(CXFA_Object* pObject, bool bScriptNode);
-  bool QueryVariableValue(CXFA_Node* pScriptNode,
+  CXFA_Object* GetVariablesThis(CXFA_Object* pObject);
+  CXFA_Object* GetVariablesScript(CXFA_Object* pObject);
+  CFXJSE_Context* VariablesContextForScriptNode(CXFA_Script* pScriptNode);
+  bool QueryVariableValue(CXFA_Script* pScriptNode,
                           ByteStringView szPropName,
-                          CFXJSE_Value* pValue,
-                          bool bGetter);
-  bool RunVariablesScript(CXFA_Node* pScriptNode);
+                          v8::Local<v8::Value>* pValue);
+  bool UpdateVariableValue(CXFA_Script* pScriptNode,
+                           ByteStringView szPropName,
+                           v8::Local<v8::Value> pValue);
+  void RunVariablesScript(CXFA_Script* pScriptNode);
 
   UnownedPtr<CJS_Runtime> const m_pSubordinateRuntime;
-  UnownedPtr<CXFA_Document> const m_pDocument;
+  cppgc::WeakPersistent<CXFA_Document> const m_pDocument;
   std::unique_ptr<CFXJSE_Context> m_JsContext;
   UnownedPtr<CFXJSE_Class> m_pJsClass;
   CXFA_Script::Type m_eScriptType = CXFA_Script::Type::Unknown;
-  std::map<CXFA_Object*, std::unique_ptr<CFXJSE_Value>> m_mapObjectToValue;
-  std::map<CXFA_Object*, std::unique_ptr<CFXJSE_Context>>
+  // |m_mapObjectToValue| is what ensures the v8 object bound to a
+  // CJX_Object remains valid for the lifetime of the engine.
+  std::map<cppgc::Persistent<CJX_Object>, v8::Global<v8::Object>>
+      m_mapObjectToObject;
+  std::map<cppgc::Persistent<CJX_Object>, std::unique_ptr<CFXJSE_Context>>
       m_mapVariableToContext;
+  cppgc::Persistent<CXFA_Node> m_pTarget;
   UnownedPtr<CXFA_EventParam> m_eventParam;
-  std::vector<CXFA_Node*> m_upObjectArray;
-  // CacheList holds the List items so we can clean them up when we're done.
-  std::vector<std::unique_ptr<CXFA_List>> m_CacheList;
-  UnownedPtr<std::vector<CXFA_Node*>> m_pScriptNodeArray;
+  std::vector<cppgc::Persistent<CXFA_Node>> m_upObjectArray;
+  UnownedPtr<std::vector<cppgc::Persistent<CXFA_Node>>> m_pScriptNodeArray;
+  std::unique_ptr<CFXJSE_NodeHelper> const m_NodeHelper;
   std::unique_ptr<CFXJSE_ResolveProcessor> const m_ResolveProcessor;
-  std::unique_ptr<CFXJSE_FormCalcContext> m_FM2JSContext;
-  UnownedPtr<CXFA_Object> m_pThisObject;
+  std::unique_ptr<CFXJSE_FormCalcContext> m_FormCalcContext;
+  cppgc::Persistent<CXFA_Object> m_pThisObject;
   XFA_AttributeValue m_eRunAtType = XFA_AttributeValue::Client;
+  bool m_bResolvingNodes = false;
 };
 
 #endif  //  FXJS_XFA_CFXJSE_ENGINE_H_
diff --git a/fxjs/xfa/cfxjse_formcalc_context.cpp b/fxjs/xfa/cfxjse_formcalc_context.cpp
index 7a6796c..a99c135 100644
--- a/fxjs/xfa/cfxjse_formcalc_context.cpp
+++ b/fxjs/xfa/cfxjse_formcalc_context.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,31 +6,47 @@
 
 #include "fxjs/xfa/cfxjse_formcalc_context.h"
 
-#include <algorithm>
-#include <cstdlib>
-#include <string>
-#include <utility>
+#include <ctype.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
 
-#include "core/fxcrt/cfx_widetextbuf.h"
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <utility>
+#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"
-#include "fxjs/xfa/cfxjse_arguments.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/widetext_buffer.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_class.h"
 #include "fxjs/xfa/cfxjse_context.h"
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cfxjse_value.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/v8-container.h"
+#include "v8/include/v8-function-callback.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fgas/crt/cfgas_decimal.h"
-#include "xfa/fgas/crt/locale_iface.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
-#include "xfa/fxfa/fm2js/cxfa_fmparser.h"
-#include "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h"
+#include "xfa/fxfa/formcalc/cxfa_fmparser.h"
+#include "xfa/fxfa/formcalc/cxfa_fmtojavascriptdepth.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_localevalue.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/cxfa_thisproxy.h"
 #include "xfa/fxfa/parser/cxfa_timezoneprovider.h"
+#include "xfa/fxfa/parser/gced_locale_iface.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
 using pdfium::fxjse::kClassTag;
@@ -38,13 +54,17 @@
 
 namespace {
 
+// Maximum number of characters Acrobat can fit in a text box.
+constexpr int kMaxCharCount = 15654908;
+
 const double kFinancialPrecision = 0.00000001;
 
 const wchar_t kStrCode[] = L"0123456789abcdef";
 
 struct XFA_FMHtmlReserveCode {
-  uint32_t m_uCode;
-  const char* m_htmlReserve;
+  uint16_t m_uCode;
+  // Inline string data reduces size for small strings.
+  const char m_htmlReserve[10];
 };
 
 // Sorted by |m_htmlReserve|.
@@ -181,7 +201,7 @@
     {9824, "spades"}, {9827, "clubs"},  {9829, "hearts"},  {9830, "diams"},
 };
 
-const FXJSE_FUNCTION_DESCRIPTOR kFormCalcFM2JSFunctions[] = {
+const FXJSE_FUNCTION_DESCRIPTOR kFormCalcFunctions[] = {
     {kFuncTag, "Abs", CFXJSE_FormCalcContext::Abs},
     {kFuncTag, "Avg", CFXJSE_FormCalcContext::Avg},
     {kFuncTag, "Ceil", CFXJSE_FormCalcContext::Ceil},
@@ -280,7 +300,7 @@
     255, 2,   255, 255, 255, 255, 255, 255, 255, 255, 255,
     255, 255, 1,   255, 255, 255, 255, 255, 255, 255, 255,
 };
-static_assert(FX_ArraySize(kAltTableDate) == L'a' - L'A' + 1,
+static_assert(std::size(kAltTableDate) == L'a' - L'A' + 1,
               "Invalid kAltTableDate size.");
 
 const uint8_t kAltTableTime[] = {
@@ -288,7 +308,7 @@
     255, 6,   255, 255, 255, 255, 255, 7,   255, 255, 255,
     255, 255, 1,   17,  255, 255, 255, 255, 255, 255, 255,
 };
-static_assert(FX_ArraySize(kAltTableTime) == L'a' - L'A' + 1,
+static_assert(std::size(kAltTableTime) == L'a' - L'A' + 1,
               "Invalid kAltTableTime size.");
 
 void AlternateDateTimeSymbols(WideString* pPattern,
@@ -322,37 +342,34 @@
   }
 }
 
-std::pair<bool, uint32_t> PatternStringType(ByteStringView bsPattern) {
+std::pair<bool, CXFA_LocaleValue::ValueType> PatternStringType(
+    ByteStringView bsPattern) {
   WideString wsPattern = WideString::FromUTF8(bsPattern);
   if (L"datetime" == wsPattern.First(8))
-    return {true, XFA_VT_DATETIME};
+    return {true, CXFA_LocaleValue::ValueType::kDateTime};
   if (L"date" == wsPattern.First(4)) {
     auto pos = wsPattern.Find(L"time");
-    uint32_t type =
-        pos.has_value() && pos.value() != 0 ? XFA_VT_DATETIME : XFA_VT_DATE;
-    return {true, type};
+    if (pos.has_value() && pos.value() != 0)
+      return {true, CXFA_LocaleValue::ValueType::kDateTime};
+    return {true, CXFA_LocaleValue::ValueType::kDate};
   }
   if (L"time" == wsPattern.First(4))
-    return {true, XFA_VT_TIME};
+    return {true, CXFA_LocaleValue::ValueType::kTime};
   if (L"text" == wsPattern.First(4))
-    return {true, XFA_VT_TEXT};
+    return {true, CXFA_LocaleValue::ValueType::kText};
   if (L"num" == wsPattern.First(3)) {
-    uint32_t type;
-    if (L"integer" == wsPattern.Substr(4, 7)) {
-      type = XFA_VT_INTEGER;
-    } else if (L"decimal" == wsPattern.Substr(4, 7)) {
-      type = XFA_VT_DECIMAL;
-    } else if (L"currency" == wsPattern.Substr(4, 8)) {
-      type = XFA_VT_FLOAT;
-    } else if (L"percent" == wsPattern.Substr(4, 7)) {
-      type = XFA_VT_FLOAT;
-    } else {
-      type = XFA_VT_FLOAT;
-    }
-    return {true, type};
+    if (L"integer" == wsPattern.Substr(4, 7))
+      return {true, CXFA_LocaleValue::ValueType::kInteger};
+    if (L"decimal" == wsPattern.Substr(4, 7))
+      return {true, CXFA_LocaleValue::ValueType::kDecimal};
+    if (L"currency" == wsPattern.Substr(4, 8))
+      return {true, CXFA_LocaleValue::ValueType::kFloat};
+    if (L"percent" == wsPattern.Substr(4, 7))
+      return {true, CXFA_LocaleValue::ValueType::kFloat};
+    return {true, CXFA_LocaleValue::ValueType::kFloat};
   }
 
-  uint32_t type = XFA_VT_NULL;
+  CXFA_LocaleValue::ValueType type = CXFA_LocaleValue::ValueType::kNull;
   wsPattern.MakeLower();
   const wchar_t* pData = wsPattern.c_str();
   int32_t iLength = wsPattern.GetLength();
@@ -371,11 +388,11 @@
     }
 
     if (wsPatternChar == 'h' || wsPatternChar == 'k')
-      return {false, XFA_VT_TIME};
+      return {false, CXFA_LocaleValue::ValueType::kTime};
     if (wsPatternChar == 'x' || wsPatternChar == 'o' || wsPatternChar == '0')
-      return {false, XFA_VT_TEXT};
+      return {false, CXFA_LocaleValue::ValueType::kText};
     if (wsPatternChar == 'v' || wsPatternChar == '8' || wsPatternChar == '$')
-      return {false, XFA_VT_FLOAT};
+      return {false, CXFA_LocaleValue::ValueType::kFloat};
     if (wsPatternChar == 'y' || wsPatternChar == 'j') {
       iIndex++;
       wchar_t timePatternChar;
@@ -387,35 +404,31 @@
           continue;
         }
         if (!bSingleQuotation && timePatternChar == 't')
-          return {false, XFA_VT_DATETIME};
+          return {false, CXFA_LocaleValue::ValueType::kDateTime};
         iIndex++;
       }
-      return {false, XFA_VT_DATE};
+      return {false, CXFA_LocaleValue::ValueType::kDate};
     }
 
     if (wsPatternChar == 'a') {
-      type = XFA_VT_TEXT;
+      type = CXFA_LocaleValue::ValueType::kText;
     } else if (wsPatternChar == 'z' || wsPatternChar == 's' ||
                wsPatternChar == 'e' || wsPatternChar == ',' ||
                wsPatternChar == '.') {
-      type = XFA_VT_FLOAT;
+      type = CXFA_LocaleValue::ValueType::kFloat;
     }
     iIndex++;
   }
-
-  if (type == XFA_VT_NULL)
-    type = XFA_VT_TEXT | XFA_VT_FLOAT;
   return {false, type};
 }
 
-CFXJSE_FormCalcContext* ToFormCalcContext(CFXJSE_Value* pValue) {
-  CFXJSE_HostObject* pHostObj = pValue->ToHostObject();
+CFXJSE_FormCalcContext* ToFormCalcContext(CFXJSE_HostObject* pHostObj) {
   return pHostObj ? pHostObj->AsFormCalcContext() : nullptr;
 }
 
-LocaleIface* LocaleFromString(CXFA_Document* pDoc,
-                              CXFA_LocaleMgr* pMgr,
-                              ByteStringView bsLocale) {
+GCedLocaleIface* LocaleFromString(CXFA_Document* pDoc,
+                                  CXFA_LocaleMgr* pMgr,
+                                  ByteStringView bsLocale) {
   if (!bsLocale.IsEmpty())
     return pMgr->GetLocaleByName(WideString::FromUTF8(bsLocale));
 
@@ -427,21 +440,21 @@
   if (!bsFormat.IsEmpty())
     return WideString::FromUTF8(bsFormat);
 
-  return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Default);
+  return pLocale->GetDatePattern(LocaleIface::DateTimeSubcategory::kDefault);
 }
 
-FX_LOCALEDATETIMESUBCATEGORY SubCategoryFromInt(int32_t iStyle) {
+LocaleIface::DateTimeSubcategory SubCategoryFromInt(int32_t iStyle) {
   switch (iStyle) {
     case 1:
-      return FX_LOCALEDATETIMESUBCATEGORY_Short;
+      return LocaleIface::DateTimeSubcategory::kShort;
     case 3:
-      return FX_LOCALEDATETIMESUBCATEGORY_Long;
+      return LocaleIface::DateTimeSubcategory::kLong;
     case 4:
-      return FX_LOCALEDATETIMESUBCATEGORY_Full;
+      return LocaleIface::DateTimeSubcategory::kFull;
     case 0:
     case 2:
     default:
-      return FX_LOCALEDATETIMESUBCATEGORY_Medium;
+      return LocaleIface::DateTimeSubcategory::kMedium;
   }
 }
 
@@ -455,7 +468,7 @@
   if (!pLocale)
     return ByteString();
 
-  FX_LOCALEDATETIMESUBCATEGORY category = SubCategoryFromInt(iStyle);
+  LocaleIface::DateTimeSubcategory category = SubCategoryFromInt(iStyle);
   WideString wsLocal = bIsDate ? pLocale->GetDatePattern(category)
                                : pLocale->GetTimePattern(category);
   if (!bStandard)
@@ -469,7 +482,7 @@
 }
 
 bool IsPartOfNumber(char ch) {
-  return std::isdigit(ch) || ch == '-' || ch == '.';
+  return isdigit(ch) || ch == '-' || ch == '.';
 }
 
 bool IsPartOfNumberW(wchar_t ch) {
@@ -497,332 +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 (!std::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 (!std::isdigit(pData[iPosOff]) || !std::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 (!std::isdigit(pData[iPosOff]) || !std::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 (!std::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 (!std::isdigit(pData[iIndex]))
-      return false;
-
-    szBuffer[0] = pData[iIndex];
-    if (!std::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 (!std::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 (!std::isdigit(pData[iIndex]))
-      return false;
-
-    szBuffer[0] = pData[iIndex];
-    if (!std::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) {
-  int32_t& iYear = *pYear;
-  int32_t& iMonth = *pMonth;
-  int32_t& iDay = *pDay;
-  int32_t& iHour = *pHour;
-  int32_t& iMinute = *pMinute;
-  int32_t& iSecond = *pSecond;
-  int32_t& iMilliSecond = *pMilliSecond;
-  int32_t& iZoneHour = *pZoneHour;
-  int32_t& iZoneMinute = *pZoneMinute;
-
-  iYear = 0;
-  iMonth = 0;
-  iDay = 0;
-  iHour = 0;
-  iMinute = 0;
-  iSecond = 0;
-
-  if (pData.empty())
-    return false;
-
-  size_t iIndex = 0;
-  while (pData[iIndex] != 'T' && pData[iIndex] != 't') {
-    if (iIndex >= pData.size())
-      return false;
-    ++iIndex;
-  }
-  if (iIndex != 8 && iIndex != 10)
-    return false;
-
-  int32_t iStyle = -1;
-  if (!IsIsoDateFormat(pData.subspan(0, iIndex), &iStyle, &iYear, &iMonth,
-                       &iDay)) {
-    return false;
-  }
-  if (pData[iIndex] != 'T' && pData[iIndex] != 't')
-    return true;
-
-  return IsIsoTimeFormat(pData.subspan(iIndex + 1), &iHour, &iMinute, &iSecond,
-                         &iMilliSecond, &iZoneHour, &iZoneMinute);
-}
-
-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 (int32_t)dDays;
-}
-
 void GetLocalTimeZone(int32_t* pHour, int32_t* pMin, int32_t* pSec) {
   time_t now;
   FXSYS_time(&now);
@@ -869,7 +556,7 @@
 WideString DecodeURL(const WideString& wsURL) {
   const wchar_t* pData = wsURL.c_str();
   size_t iLen = wsURL.GetLength();
-  CFX_WideTextBuf wsResultBuf;
+  WideTextBuffer wsResultBuf;
   for (size_t i = 0; i < iLen; ++i) {
     wchar_t ch = pData[i];
     if ('%' != ch) {
@@ -891,14 +578,13 @@
     }
     wsResultBuf.AppendChar(chTemp);
   }
-  wsResultBuf.AppendChar(0);
   return wsResultBuf.MakeString();
 }
 
 WideString DecodeMLInternal(const WideString& wsHTML, bool bIsHTML) {
   const wchar_t* pData = wsHTML.c_str();
   size_t iLen = wsHTML.GetLength();
-  CFX_WideTextBuf wsResultBuf;
+  WideTextBuffer wsResultBuf;
   for (size_t i = 0; i < iLen; ++i) {
     wchar_t ch = pData[i];
     if (ch != '&') {
@@ -959,8 +645,6 @@
         wsResultBuf.AppendChar('>');
     }
   }
-
-  wsResultBuf.AppendChar(0);
   return wsResultBuf.MakeString();
 }
 
@@ -973,20 +657,22 @@
 }
 
 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());
-  CFX_WideTextBuf wsResultBuf;
+  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 = FX_ArraySize(kStrUnsafe);
+    size_t iCount = std::size(kStrUnsafe);
     while (i < iCount) {
       if (ch == kStrUnsafe[i]) {
         int32_t iIndex = ch / 16;
@@ -1001,7 +687,7 @@
       continue;
 
     i = 0;
-    iCount = FX_ArraySize(kStrReserved);
+    iCount = std::size(kStrReserved);
     while (i < iCount) {
       if (ch == kStrReserved[i]) {
         int32_t iIndex = ch / 16;
@@ -1016,7 +702,7 @@
       continue;
 
     i = 0;
-    iCount = FX_ArraySize(kStrSpecial);
+    iCount = std::size(kStrSpecial);
     while (i < iCount) {
       if (ch == kStrSpecial[i]) {
         wsResultBuf.AppendChar(ch);
@@ -1066,7 +752,6 @@
       }
     }
   }
-  wsResultBuf.AppendChar(0);
   return wsResultBuf.MakeString();
 }
 
@@ -1076,8 +761,8 @@
   szEncode[0] = '&';
   szEncode[1] = '#';
   szEncode[2] = 'x';
-  CFX_WideTextBuf wsResultBuf;
-  for (uint32_t ch : wsHTML) {
+  WideTextBuffer wsResultBuf;
+  for (char32_t ch : pdfium::CodePointView(wsHTML.AsStringView())) {
     WideString htmlReserve;
     if (HTMLCode2STR(ch, &htmlReserve)) {
       wsResultBuf.AppendChar(L'&');
@@ -1106,18 +791,17 @@
       // TODO(tsepez): Handle codepoint not in BMP.
     }
   }
-  wsResultBuf.AppendChar(0);
   return wsResultBuf.MakeString();
 }
 
 WideString EncodeXML(const ByteString& bsXML) {
   WideString wsXML = WideString::FromUTF8(bsXML.AsStringView());
-  CFX_WideTextBuf wsResultBuf;
+  WideTextBuffer wsResultBuf;
   wchar_t szEncode[9];
   szEncode[0] = '&';
   szEncode[1] = '#';
   szEncode[2] = 'x';
-  for (uint32_t ch : wsXML) {
+  for (char32_t ch : pdfium::CodePointView(wsXML.AsStringView())) {
     switch (ch) {
       case '"':
         wsResultBuf.AppendChar('&');
@@ -1171,25 +855,21 @@
       }
     }
   }
-  wsResultBuf.AppendChar(0);
   return wsResultBuf.MakeString();
 }
 
 ByteString TrillionUS(ByteStringView bsData) {
-  static const ByteStringView pUnits[] = {"zero",  "one",  "two", "three",
-                                          "four",  "five", "six", "seven",
-                                          "eight", "nine"};
-  static const ByteStringView pCapUnits[] = {"Zero",  "One",  "Two", "Three",
-                                             "Four",  "Five", "Six", "Seven",
-                                             "Eight", "Nine"};
-  static const ByteStringView pTens[] = {
+  static const char kUnits[][6] = {"zero", "one", "two",   "three", "four",
+                                   "five", "six", "seven", "eight", "nine"};
+  static const char kCapUnits[][6] = {"Zero", "One", "Two",   "Three", "Four",
+                                      "Five", "Six", "Seven", "Eight", "Nine"};
+  static const char kTens[][10] = {
       "Ten",     "Eleven",  "Twelve",    "Thirteen", "Fourteen",
       "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"};
-  static const ByteStringView pLastTens[] = {"Twenty", "Thirty", "Forty",
-                                             "Fifty",  "Sixty",  "Seventy",
-                                             "Eighty", "Ninety"};
-  static const ByteStringView pComm[] = {" Hundred ", " Thousand ", " Million ",
-                                         " Billion ", "Trillion"};
+  static const char kLastTens[][8] = {"Twenty", "Thirty",  "Forty",  "Fifty",
+                                      "Sixty",  "Seventy", "Eighty", "Ninety"};
+  static const char kComm[][11] = {" Hundred ", " Thousand ", " Million ",
+                                   " Billion ", "Trillion"};
   const char* pData = bsData.unterminated_c_str();
   int32_t iLength = bsData.GetLength();
   int32_t iComm = 0;
@@ -1206,89 +886,86 @@
   if (iFirstCount == 0)
     iFirstCount = 3;
 
-  std::ostringstream strBuf;
+  ByteString strBuf;
   int32_t iIndex = 0;
   if (iFirstCount == 3) {
     if (pData[iIndex] != '0') {
-      strBuf << pCapUnits[pData[iIndex] - '0'];
-      strBuf << pComm[0];
+      strBuf += kCapUnits[pData[iIndex] - '0'];
+      strBuf += kComm[0];
     }
     if (pData[iIndex + 1] == '0') {
-      strBuf << pCapUnits[pData[iIndex + 2] - '0'];
+      strBuf += kCapUnits[pData[iIndex + 2] - '0'];
     } else {
       if (pData[iIndex + 1] > '1') {
-        strBuf << pLastTens[pData[iIndex + 1] - '2'];
-        strBuf << "-";
-        strBuf << pUnits[pData[iIndex + 2] - '0'];
+        strBuf += kLastTens[pData[iIndex + 1] - '2'];
+        strBuf += "-";
+        strBuf += kUnits[pData[iIndex + 2] - '0'];
       } else if (pData[iIndex + 1] == '1') {
-        strBuf << pTens[pData[iIndex + 2] - '0'];
+        strBuf += kTens[pData[iIndex + 2] - '0'];
       } else if (pData[iIndex + 1] == '0') {
-        strBuf << pCapUnits[pData[iIndex + 2] - '0'];
+        strBuf += kCapUnits[pData[iIndex + 2] - '0'];
       }
     }
     iIndex += 3;
   } else if (iFirstCount == 2) {
     if (pData[iIndex] == '0') {
-      strBuf << pCapUnits[pData[iIndex + 1] - '0'];
+      strBuf += kCapUnits[pData[iIndex + 1] - '0'];
     } else {
       if (pData[iIndex] > '1') {
-        strBuf << pLastTens[pData[iIndex] - '2'];
-        strBuf << "-";
-        strBuf << pUnits[pData[iIndex + 1] - '0'];
+        strBuf += kLastTens[pData[iIndex] - '2'];
+        strBuf += "-";
+        strBuf += kUnits[pData[iIndex + 1] - '0'];
       } else if (pData[iIndex] == '1') {
-        strBuf << pTens[pData[iIndex + 1] - '0'];
+        strBuf += kTens[pData[iIndex + 1] - '0'];
       } else if (pData[iIndex] == '0') {
-        strBuf << pCapUnits[pData[iIndex + 1] - '0'];
+        strBuf += kCapUnits[pData[iIndex + 1] - '0'];
       }
     }
     iIndex += 2;
   } else if (iFirstCount == 1) {
-    strBuf << pCapUnits[pData[iIndex] - '0'];
+    strBuf += kCapUnits[pData[iIndex] - '0'];
     ++iIndex;
   }
   if (iLength > 3 && iFirstCount > 0) {
-    strBuf << pComm[iComm];
+    strBuf += kComm[iComm];
     --iComm;
   }
   while (iIndex < iLength) {
     if (pData[iIndex] != '0') {
-      strBuf << pCapUnits[pData[iIndex] - '0'];
-      strBuf << pComm[0];
+      strBuf += kCapUnits[pData[iIndex] - '0'];
+      strBuf += kComm[0];
     }
     if (pData[iIndex + 1] == '0') {
-      strBuf << pCapUnits[pData[iIndex + 2] - '0'];
+      strBuf += kCapUnits[pData[iIndex + 2] - '0'];
     } else {
       if (pData[iIndex + 1] > '1') {
-        strBuf << pLastTens[pData[iIndex + 1] - '2'];
-        strBuf << "-";
-        strBuf << pUnits[pData[iIndex + 2] - '0'];
+        strBuf += kLastTens[pData[iIndex + 1] - '2'];
+        strBuf += "-";
+        strBuf += kUnits[pData[iIndex + 2] - '0'];
       } else if (pData[iIndex + 1] == '1') {
-        strBuf << pTens[pData[iIndex + 2] - '0'];
+        strBuf += kTens[pData[iIndex + 2] - '0'];
       } else if (pData[iIndex + 1] == '0') {
-        strBuf << pCapUnits[pData[iIndex + 2] - '0'];
+        strBuf += kCapUnits[pData[iIndex + 2] - '0'];
       }
     }
     if (iIndex < iLength - 3) {
-      strBuf << pComm[iComm];
+      strBuf += kComm[iComm];
       --iComm;
     }
     iIndex += 3;
   }
-  return ByteString(strBuf);
+  return strBuf;
 }
 
-ByteString WordUS(const ByteString& bsData, int32_t iStyle) {
-  const char* pData = bsData.c_str();
-  int32_t iLength = bsData.GetLength();
-  if (iStyle < 0 || iStyle > 2) {
+ByteString WordUS(ByteStringView bsData, int32_t iStyle) {
+  if (iStyle < 0 || iStyle > 2)
     return ByteString();
-  }
 
-  std::ostringstream strBuf;
-
+  int32_t iLength = bsData.GetLength();
+  ByteString strBuf;
   int32_t iIndex = 0;
   while (iIndex < iLength) {
-    if (pData[iIndex] == '.')
+    if (bsData[iIndex] == '.')
       break;
     ++iIndex;
   }
@@ -1299,535 +976,641 @@
     if (!iCount && iInteger - iIndex > 0)
       iCount = 12;
 
-    strBuf << TrillionUS(ByteStringView(pData + iIndex, iCount));
+    strBuf += TrillionUS(bsData.Substr(iIndex, iCount));
     iIndex += iCount;
     if (iIndex < iInteger)
-      strBuf << " Trillion ";
+      strBuf += " Trillion ";
   }
 
   if (iStyle > 0)
-    strBuf << " Dollars";
+    strBuf += " Dollars";
 
   if (iStyle > 1 && iInteger < iLength) {
-    strBuf << " And ";
+    strBuf += " And ";
     iIndex = iInteger + 1;
     while (iIndex < iLength) {
       int32_t iCount = (iLength - iIndex) % 12;
       if (!iCount && iLength - iIndex > 0)
         iCount = 12;
 
-      strBuf << TrillionUS(ByteStringView(pData + iIndex, iCount));
+      strBuf += TrillionUS(bsData.Substr(iIndex, iCount));
       iIndex += iCount;
       if (iIndex < iLength)
-        strBuf << " Trillion ";
+        strBuf += " Trillion ";
     }
-    strBuf << " Cents";
+    strBuf += " Cents";
   }
-  return ByteString(strBuf);
+  return strBuf;
+}
+
+v8::Local<v8::Value> GetObjectDefaultValue(v8::Isolate* pIsolate,
+                                           v8::Local<v8::Object> pObject) {
+  CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pIsolate, pObject));
+  if (!pNode)
+    return fxv8::NewNullHelper(pIsolate);
+
+  v8::Local<v8::Value> value;
+  pNode->JSObject()->ScriptSomDefaultValue(pIsolate, &value, false,
+                                           XFA_Attribute::Unknown);
+  return value;
+}
+
+bool SetObjectDefaultValue(v8::Isolate* pIsolate,
+                           v8::Local<v8::Object> pObject,
+                           v8::Local<v8::Value> hNewValue) {
+  CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pIsolate, pObject));
+  if (!pNode)
+    return false;
+
+  pNode->JSObject()->ScriptSomDefaultValue(pIsolate, &hNewValue, true,
+                                           XFA_Attribute::Unknown);
+  return true;
+}
+
+v8::Local<v8::Value> GetExtractedValue(v8::Isolate* pIsolate,
+                                       v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty())
+    return v8::Local<v8::Value>();
+
+  if (fxv8::IsArray(pValue)) {
+    v8::Local<v8::Array> arr = pValue.As<v8::Array>();
+    uint32_t iLength = fxv8::GetArrayLengthHelper(arr);
+    if (iLength < 3)
+      return fxv8::NewUndefinedHelper(pIsolate);
+
+    v8::Local<v8::Value> propertyValue =
+        fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1);
+    v8::Local<v8::Value> jsValue =
+        fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 2);
+    if (!fxv8::IsObject(jsValue))
+      return fxv8::NewUndefinedHelper(pIsolate);
+
+    v8::Local<v8::Object> jsObjectValue = jsValue.As<v8::Object>();
+    if (fxv8::IsNull(propertyValue))
+      return GetObjectDefaultValue(pIsolate, jsObjectValue);
+
+    ByteString bsName =
+        fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue);
+    return fxv8::ReentrantGetObjectPropertyHelper(pIsolate, jsObjectValue,
+                                                  bsName.AsStringView());
+  }
+
+  if (fxv8::IsObject(pValue))
+    return GetObjectDefaultValue(pIsolate, pValue.As<v8::Object>());
+
+  return pValue;
+}
+
+v8::Local<v8::Value> GetSimpleValue(
+    const v8::FunctionCallbackInfo<v8::Value>& info,
+    uint32_t index) {
+  DCHECK(index < static_cast<uint32_t>(info.Length()));
+  return GetExtractedValue(info.GetIsolate(), info[index]);
+}
+
+bool ValueIsNull(v8::Isolate* pIsolate, v8::Local<v8::Value> arg) {
+  v8::Local<v8::Value> extracted = GetExtractedValue(pIsolate, arg);
+  return extracted.IsEmpty() || fxv8::IsNull(extracted);
+}
+
+int32_t ValueToInteger(v8::Isolate* pIsolate, v8::Local<v8::Value> arg) {
+  v8::Local<v8::Value> extracted = GetExtractedValue(pIsolate, arg);
+  if (extracted.IsEmpty())
+    return 0;
+
+  if (fxv8::IsObject(extracted) || fxv8::IsArray(extracted))
+    return ValueToInteger(pIsolate, extracted);
+
+  if (fxv8::IsString(extracted)) {
+    ByteString bsValue = fxv8::ReentrantToByteStringHelper(pIsolate, extracted);
+    return FXSYS_atoi(bsValue.c_str());
+  }
+
+  return fxv8::ReentrantToInt32Helper(pIsolate, extracted);
+}
+
+float ValueToFloat(v8::Isolate* pIsolate, v8::Local<v8::Value> arg) {
+  v8::Local<v8::Value> extracted = GetExtractedValue(pIsolate, arg);
+  if (extracted.IsEmpty())
+    return 0.0f;
+
+  if (fxv8::IsUndefined(extracted))
+    return 0.0f;
+
+  if (fxv8::IsObject(extracted) || fxv8::IsArray(extracted))
+    return ValueToFloat(pIsolate, extracted);
+
+  if (fxv8::IsString(extracted)) {
+    ByteString bsValue = fxv8::ReentrantToByteStringHelper(pIsolate, extracted);
+    return strtof(bsValue.c_str(), nullptr);
+  }
+
+  return fxv8::ReentrantToFloatHelper(pIsolate, extracted);
+}
+
+double ValueToDouble(v8::Isolate* pIsolate, v8::Local<v8::Value> arg) {
+  v8::Local<v8::Value> extracted = GetExtractedValue(pIsolate, arg);
+  if (extracted.IsEmpty())
+    return 0.0;
+
+  if (fxv8::IsUndefined(extracted))
+    return 0.0;
+
+  if (fxv8::IsObject(extracted) || fxv8::IsArray(extracted))
+    return ValueToDouble(pIsolate, extracted);
+
+  if (fxv8::IsString(extracted)) {
+    ByteString bsValue = fxv8::ReentrantToByteStringHelper(pIsolate, extracted);
+    return strtod(bsValue.c_str(), nullptr);
+  }
+
+  return fxv8::ReentrantToDoubleHelper(pIsolate, extracted);
+}
+
+absl::optional<double> ExtractDouble(v8::Isolate* pIsolate,
+                                     v8::Local<v8::Value> src) {
+  if (src.IsEmpty())
+    return 0.0;
+
+  if (!fxv8::IsArray(src))
+    return ValueToDouble(pIsolate, src);
+
+  v8::Local<v8::Array> arr = src.As<v8::Array>();
+  uint32_t iLength = fxv8::GetArrayLengthHelper(arr);
+  if (iLength < 3)
+    return absl::nullopt;
+
+  v8::Local<v8::Value> propertyValue =
+      fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1);
+  v8::Local<v8::Value> jsValue =
+      fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 2);
+  if (fxv8::IsNull(propertyValue) || !fxv8::IsObject(jsValue))
+    return ValueToDouble(pIsolate, jsValue);
+
+  ByteString bsName =
+      fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue);
+  return ValueToDouble(
+      pIsolate, fxv8::ReentrantGetObjectPropertyHelper(
+                    pIsolate, jsValue.As<v8::Object>(), bsName.AsStringView()));
+}
+
+ByteString ValueToUTF8String(v8::Isolate* pIsolate, v8::Local<v8::Value> arg) {
+  if (arg.IsEmpty())
+    return ByteString();
+
+  if (fxv8::IsNull(arg) || fxv8::IsUndefined(arg))
+    return ByteString();
+
+  if (fxv8::IsBoolean(arg))
+    return fxv8::ReentrantToBooleanHelper(pIsolate, arg) ? "1" : "0";
+
+  return fxv8::ReentrantToByteStringHelper(pIsolate, arg);
+}
+
+bool SimpleValueCompare(v8::Isolate* pIsolate,
+                        v8::Local<v8::Value> firstValue,
+                        v8::Local<v8::Value> secondValue) {
+  if (firstValue.IsEmpty())
+    return false;
+
+  if (fxv8::IsString(firstValue)) {
+    const ByteString first = ValueToUTF8String(pIsolate, firstValue);
+    const ByteString second = ValueToUTF8String(pIsolate, secondValue);
+    return first == second;
+  }
+  if (fxv8::IsNumber(firstValue)) {
+    const float first = ValueToFloat(pIsolate, firstValue);
+    const float second = ValueToFloat(pIsolate, secondValue);
+    return first == second;
+  }
+  if (fxv8::IsBoolean(firstValue)) {
+    const bool first = fxv8::ReentrantToBooleanHelper(pIsolate, firstValue);
+    const bool second = fxv8::ReentrantToBooleanHelper(pIsolate, secondValue);
+    return first == second;
+  }
+  return fxv8::IsNull(firstValue) && fxv8::IsNull(secondValue);
+}
+
+std::vector<v8::Local<v8::Value>> UnfoldArgs(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  std::vector<v8::Local<v8::Value>> results;
+  v8::Isolate* pIsolate = info.GetIsolate();
+  for (int i = 1; i < info.Length(); ++i) {
+    v8::Local<v8::Value> arg = info[i];
+    if (fxv8::IsArray(arg)) {
+      v8::Local<v8::Array> arr = arg.As<v8::Array>();
+      uint32_t iLength = fxv8::GetArrayLengthHelper(arr);
+      if (iLength < 3)
+        continue;
+
+      v8::Local<v8::Value> propertyValue =
+          fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1);
+
+      for (uint32_t j = 2; j < iLength; j++) {
+        v8::Local<v8::Value> jsValue =
+            fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, j);
+
+        if (!fxv8::IsObject(jsValue)) {
+          results.push_back(fxv8::NewUndefinedHelper(pIsolate));
+        } else if (fxv8::IsNull(propertyValue)) {
+          results.push_back(
+              GetObjectDefaultValue(pIsolate, jsValue.As<v8::Object>()));
+        } else {
+          ByteString bsName =
+              fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue);
+          results.push_back(fxv8::ReentrantGetObjectPropertyHelper(
+              pIsolate, jsValue.As<v8::Object>(), bsName.AsStringView()));
+        }
+      }
+    } else if (fxv8::IsObject(arg)) {
+      results.push_back(GetObjectDefaultValue(pIsolate, arg.As<v8::Object>()));
+    } else {
+      results.push_back(arg);
+    }
+  }
+  return results;
+}
+
+// Returns empty value on failure.
+v8::Local<v8::Value> GetObjectForName(CFXJSE_HostObject* pHostObject,
+                                      ByteStringView bsAccessorName) {
+  CXFA_Document* pDoc = ToFormCalcContext(pHostObject)->GetDocument();
+  if (!pDoc)
+    return v8::Local<v8::Value>();
+
+  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
+  absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+      pScriptContext->ResolveObjects(
+          pScriptContext->GetThisObject(),
+          WideString::FromUTF8(bsAccessorName).AsStringView(),
+          Mask<XFA_ResolveFlag>{
+              XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kProperties,
+              XFA_ResolveFlag::kSiblings, XFA_ResolveFlag::kParent});
+  if (!maybeResult.has_value() ||
+      maybeResult.value().type != CFXJSE_Engine::ResolveResult::Type::kNodes ||
+      maybeResult.value().objects.empty()) {
+    return v8::Local<v8::Value>();
+  }
+  return pScriptContext->GetOrCreateJSBindingFromMap(
+      maybeResult.value().objects.front().Get());
+}
+
+absl::optional<CFXJSE_Engine::ResolveResult> ResolveObjects(
+    CFXJSE_HostObject* pHostObject,
+    v8::Local<v8::Value> pRefValue,
+    ByteStringView bsSomExp,
+    bool bDotAccessor,
+    bool bHasNoResolveName) {
+  CXFA_Document* pDoc = ToFormCalcContext(pHostObject)->GetDocument();
+  if (!pDoc)
+    return absl::nullopt;
+
+  v8::Isolate* pIsolate = ToFormCalcContext(pHostObject)->GetIsolate();
+  WideString wsSomExpression = WideString::FromUTF8(bsSomExp);
+  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
+  CXFA_Object* pNode = nullptr;
+  Mask<XFA_ResolveFlag> dwFlags;
+  if (bDotAccessor) {
+    if (fxv8::IsNull(pRefValue)) {
+      pNode = pScriptContext->GetThisObject();
+      dwFlags = {XFA_ResolveFlag::kSiblings, XFA_ResolveFlag::kParent};
+    } else {
+      pNode = CFXJSE_Engine::ToObject(pIsolate, pRefValue);
+      if (!pNode)
+        return absl::nullopt;
+
+      if (bHasNoResolveName) {
+        WideString wsName;
+        if (CXFA_Node* pXFANode = pNode->AsNode()) {
+          absl::optional<WideString> ret =
+              pXFANode->JSObject()->TryAttribute(XFA_Attribute::Name, false);
+          if (ret.has_value())
+            wsName = ret.value();
+        }
+        if (wsName.IsEmpty())
+          wsName = L"#" + WideString::FromASCII(pNode->GetClassName());
+
+        wsSomExpression = wsName + wsSomExpression;
+        dwFlags = XFA_ResolveFlag::kSiblings;
+      } else {
+        dwFlags = (bsSomExp == "*")
+                      ? Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kChildren}
+                      : Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kChildren,
+                                              XFA_ResolveFlag::kAttributes,
+                                              XFA_ResolveFlag::kProperties};
+      }
+    }
+  } else {
+    pNode = CFXJSE_Engine::ToObject(pIsolate, pRefValue);
+    dwFlags = XFA_ResolveFlag::kAnyChild;
+  }
+  return pScriptContext->ResolveObjects(pNode, wsSomExpression.AsStringView(),
+                                        dwFlags);
+}
+
+std::vector<v8::Local<v8::Value>> ParseResolveResult(
+    CFXJSE_HostObject* pHostObject,
+    const CFXJSE_Engine::ResolveResult& resolveNodeRS,
+    v8::Local<v8::Value> pParentValue,
+    bool* bAttribute) {
+  std::vector<v8::Local<v8::Value>> resultValues;
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pHostObject);
+  v8::Isolate* pIsolate = pContext->GetIsolate();
+
+  if (resolveNodeRS.type == CFXJSE_Engine::ResolveResult::Type::kNodes) {
+    *bAttribute = false;
+    CFXJSE_Engine* pScriptContext = pContext->GetDocument()->GetScriptContext();
+    for (auto& pObject : resolveNodeRS.objects) {
+      resultValues.push_back(
+          pScriptContext->GetOrCreateJSBindingFromMap(pObject.Get()));
+    }
+    return resultValues;
+  }
+
+  *bAttribute = true;
+  if (resolveNodeRS.script_attribute.callback &&
+      resolveNodeRS.script_attribute.eValueType == XFA_ScriptType::Object) {
+    for (auto& pObject : resolveNodeRS.objects) {
+      v8::Local<v8::Value> pValue;
+      CJX_Object* jsObject = pObject->JSObject();
+      (*resolveNodeRS.script_attribute.callback)(
+          pIsolate, jsObject, &pValue, false,
+          resolveNodeRS.script_attribute.attribute);
+      resultValues.push_back(pValue);
+      *bAttribute = false;
+    }
+  }
+  if (*bAttribute && fxv8::IsObject(pParentValue))
+    resultValues.push_back(pParentValue);
+
+  return resultValues;
+}
+
+// Returns 0 if the provided `arg` is an invalid payment period count.
+int GetValidatedPaymentPeriods(v8::Isolate* isolate, v8::Local<v8::Value> arg) {
+  double periods = ValueToDouble(isolate, arg);
+  if (periods < 1 ||
+      periods > static_cast<double>(std::numeric_limits<int32_t>::max())) {
+    return 0;
+  }
+
+  return static_cast<int>(periods);
 }
 
 }  // namespace
 
-const FXJSE_CLASS_DESCRIPTOR kFormCalcFM2JSDescriptor = {
-    kClassTag,                              // tag
-    "XFA_FM2JS_FormCalcClass",              // name
-    kFormCalcFM2JSFunctions,                // methods
-    FX_ArraySize(kFormCalcFM2JSFunctions),  // number of methods
-    nullptr,                                // dynamic prop type
-    nullptr,                                // dynamic prop getter
-    nullptr,                                // dynamic prop setter
-    nullptr,                                // dynamic prop method call
+const FXJSE_CLASS_DESCRIPTOR kFormCalcDescriptor = {
+    kClassTag,                      // tag
+    "XFA_FormCalcClass",            // name
+    kFormCalcFunctions,             // methods
+    std::size(kFormCalcFunctions),  // number of methods
+    nullptr,                        // dynamic prop type
+    nullptr,                        // dynamic prop getter
+    nullptr,                        // dynamic prop setter
+    nullptr,                        // dynamic prop method call
 };
 
 // static
-void CFXJSE_FormCalcContext::Abs(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Abs");
+void CFXJSE_FormCalcContext::Abs(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Abs");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
+  if (ValueIsNull(info.GetIsolate(), info[0])) {
+    info.GetReturnValue().SetNull();
     return;
   }
-
-  double dValue = ValueToDouble(pThis, argOne.get());
+  double dValue = ValueToDouble(info.GetIsolate(), info[0]);
   if (dValue < 0)
     dValue = -dValue;
 
-  args.GetReturnValue()->SetDouble(dValue);
+  info.GetReturnValue().Set(dValue);
 }
 
 // static
-void CFXJSE_FormCalcContext::Avg(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 1) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
+void CFXJSE_FormCalcContext::Avg(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   uint32_t uCount = 0;
   double dSum = 0.0;
-  for (int32_t i = 0; i < argc; i++) {
-    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
-    if (argValue->IsNull())
-      continue;
+  auto fn = [&uCount, &dSum](v8::Isolate* pIsolate,
+                             v8::Local<v8::Value> pValue) {
+    dSum += ValueToDouble(pIsolate, pValue);
+    uCount++;
+  };
+  if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/false))
+    return;
 
-    if (!argValue->IsArray()) {
-      dSum += ValueToDouble(pThis, argValue.get());
-      uCount++;
-      continue;
-    }
-
-    auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argValue->GetObjectProperty("length", lengthValue.get());
-    int32_t iLength = lengthValue->ToInteger();
-
-    if (iLength > 2) {
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
-
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          auto defaultPropValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-          GetObjectDefaultValue(jsObjectValue.get(), defaultPropValue.get());
-          if (defaultPropValue->IsNull())
-            continue;
-
-          dSum += ValueToDouble(pThis, defaultPropValue.get());
-          uCount++;
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          dSum += ValueToDouble(pThis, newPropertyValue.get());
-          uCount++;
-        }
-      }
-    }
-  }
   if (uCount == 0) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
-
-  args.GetReturnValue()->SetDouble(dSum / uCount);
+  info.GetReturnValue().Set(dSum / uCount);
 }
 
 // static
-void CFXJSE_FormCalcContext::Ceil(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Ceil");
+void CFXJSE_FormCalcContext::Ceil(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Ceil");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argValue = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argValue.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argValue = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  args.GetReturnValue()->SetFloat(ceil(ValueToFloat(pThis, argValue.get())));
+  info.GetReturnValue().Set(ceil(ValueToFloat(info.GetIsolate(), argValue)));
 }
 
 // static
-void CFXJSE_FormCalcContext::Count(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  int32_t iCount = 0;
-  for (int32_t i = 0; i < args.GetLength(); i++) {
-    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
-    if (argValue->IsNull())
-      continue;
+void CFXJSE_FormCalcContext::Count(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  uint32_t iCount = 0;
+  auto fn = [&iCount](v8::Isolate* pIsolate, v8::Local<v8::Value> pvalue) {
+    ++iCount;
+  };
+  if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true))
+    return;
 
-    if (argValue->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectProperty("length", lengthValue.get());
-
-      int32_t iLength = lengthValue->ToInteger();
-      if (iLength <= 2) {
-        pContext->ThrowArgumentMismatchException();
-        return;
-      }
-
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
-      argValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-          if (!newPropertyValue->IsNull())
-            iCount++;
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
-          iCount += newPropertyValue->IsNull() ? 0 : 1;
-        }
-      }
-    } else if (argValue->IsObject()) {
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
-      if (!newPropertyValue->IsNull())
-        iCount++;
-    } else {
-      iCount++;
-    }
-  }
-  args.GetReturnValue()->SetInteger(iCount);
+  info.GetReturnValue().Set(iCount);
 }
 
 // static
-void CFXJSE_FormCalcContext::Floor(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Floor");
+void CFXJSE_FormCalcContext::Floor(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Floor");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argValue = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argValue.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argValue = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  args.GetReturnValue()->SetFloat(floor(ValueToFloat(pThis, argValue.get())));
+  info.GetReturnValue().Set(floor(ValueToFloat(info.GetIsolate(), argValue)));
 }
 
 // static
-void CFXJSE_FormCalcContext::Max(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+void CFXJSE_FormCalcContext::Max(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   uint32_t uCount = 0;
   double dMaxValue = 0.0;
-  for (int32_t i = 0; i < args.GetLength(); i++) {
-    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
-    if (argValue->IsNull())
-      continue;
+  auto fn = [&uCount, &dMaxValue](v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value> pValue) {
+    ++uCount;
+    double dValue = ValueToDouble(pIsolate, pValue);
+    dMaxValue = uCount == 1 ? dValue : std::max(dMaxValue, dValue);
+  };
+  if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true))
+    return;
 
-    if (argValue->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
-      if (iLength <= 2) {
-        pContext->ThrowArgumentMismatchException();
-        return;
-      }
-
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
-      argValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          uCount++;
-          double dValue = ValueToDouble(pThis, newPropertyValue.get());
-          dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          uCount++;
-          double dValue = ValueToDouble(pThis, newPropertyValue.get());
-          dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
-        }
-      }
-    } else if (argValue->IsObject()) {
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
-      if (newPropertyValue->IsNull())
-        continue;
-
-      uCount++;
-      double dValue = ValueToDouble(pThis, newPropertyValue.get());
-      dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
-    } else {
-      uCount++;
-      double dValue = ValueToDouble(pThis, argValue.get());
-      dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
-    }
-  }
   if (uCount == 0) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
-
-  args.GetReturnValue()->SetDouble(dMaxValue);
+  info.GetReturnValue().Set(dMaxValue);
 }
 
 // static
-void CFXJSE_FormCalcContext::Min(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+void CFXJSE_FormCalcContext::Min(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   uint32_t uCount = 0;
   double dMinValue = 0.0;
-  for (int32_t i = 0; i < args.GetLength(); i++) {
-    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
-    if (argValue->IsNull())
-      continue;
+  auto fn = [&uCount, &dMinValue](v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value> pValue) {
+    ++uCount;
+    double dValue = ValueToDouble(pIsolate, pValue);
+    dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
+  };
+  if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true))
+    return;
 
-    if (argValue->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
-      if (iLength <= 2) {
-        pContext->ThrowArgumentMismatchException();
-        return;
-      }
-
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
-      argValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          uCount++;
-          double dValue = ValueToDouble(pThis, newPropertyValue.get());
-          dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          uCount++;
-          double dValue = ValueToDouble(pThis, newPropertyValue.get());
-          dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
-        }
-      }
-    } else if (argValue->IsObject()) {
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
-      if (newPropertyValue->IsNull())
-        continue;
-
-      uCount++;
-      double dValue = ValueToDouble(pThis, newPropertyValue.get());
-      dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
-    } else {
-      uCount++;
-      double dValue = ValueToDouble(pThis, argValue.get());
-      dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
-    }
-  }
   if (uCount == 0) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
-
-  args.GetReturnValue()->SetDouble(dMinValue);
+  info.GetReturnValue().Set(dMinValue);
 }
 
 // static
-void CFXJSE_FormCalcContext::Mod(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Mod(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 2) {
-    pContext->ThrowParamCountMismatchException(L"Mod");
+  if (info.Length() != 2) {
+    pContext->ThrowParamCountMismatchException("Mod");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  std::unique_ptr<CFXJSE_Value> argTwo = args.GetValue(1);
-  if (argOne->IsNull() || argTwo->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  if (fxv8::IsNull(info[0]) || fxv8::IsNull(info[1])) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  bool argOneResult;
-  double dDividend = ExtractDouble(pThis, argOne.get(), &argOneResult);
-  bool argTwoResult;
-  double dDivisor = ExtractDouble(pThis, argTwo.get(), &argTwoResult);
-  if (!argOneResult || !argTwoResult) {
+  absl::optional<double> maybe_dividend =
+      ExtractDouble(info.GetIsolate(), info[0]);
+  absl::optional<double> maybe_divisor =
+      ExtractDouble(info.GetIsolate(), info[1]);
+  if (!maybe_dividend.has_value() || !maybe_divisor.has_value()) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  if (dDivisor == 0.0) {
+  double dividend = maybe_dividend.value();
+  double divisor = maybe_divisor.value();
+  if (divisor == 0.0) {
     pContext->ThrowDivideByZeroException();
     return;
   }
 
-  args.GetReturnValue()->SetDouble(dDividend -
-                                   dDivisor * (int32_t)(dDividend / dDivisor));
+  info.GetReturnValue().Set(dividend -
+                            divisor * static_cast<int32_t>(dividend / divisor));
 }
 
 // static
-void CFXJSE_FormCalcContext::Round(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Round(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  int32_t argc = args.GetLength();
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 2) {
-    pContext->ThrowParamCountMismatchException(L"Round");
+    pContext->ThrowParamCountMismatchException("Round");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  if (fxv8::IsNull(info[0])) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  bool dValueRet;
-  double dValue = ExtractDouble(pThis, argOne.get(), &dValueRet);
-  if (!dValueRet) {
+  absl::optional<double> maybe_value =
+      ExtractDouble(info.GetIsolate(), info[0]);
+  if (!maybe_value.has_value()) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
+  double dValue = maybe_value.value();
   uint8_t uPrecision = 0;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> argTwo = args.GetValue(1);
-    if (argTwo->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    if (fxv8::IsNull(info[1])) {
+      info.GetReturnValue().SetNull();
       return;
     }
-
-    bool dPrecisionRet;
-    double dPrecision = ExtractDouble(pThis, argTwo.get(), &dPrecisionRet);
-    if (!dPrecisionRet) {
+    absl::optional<double> maybe_precision =
+        ExtractDouble(info.GetIsolate(), info[1]);
+    if (!maybe_precision.has_value()) {
       pContext->ThrowArgumentMismatchException();
       return;
     }
-
-    uPrecision = static_cast<uint8_t>(pdfium::clamp(dPrecision, 0.0, 12.0));
+    double dPrecision = maybe_precision.value();
+    uPrecision = static_cast<uint8_t>(std::clamp(dPrecision, 0.0, 12.0));
   }
 
   CFGAS_Decimal decimalValue(static_cast<float>(dValue), uPrecision);
-  args.GetReturnValue()->SetDouble(decimalValue.ToDouble());
+  info.GetReturnValue().Set(decimalValue.ToDouble());
 }
 
 // static
-void CFXJSE_FormCalcContext::Sum(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc == 0) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+void CFXJSE_FormCalcContext::Sum(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   uint32_t uCount = 0;
   double dSum = 0.0;
-  for (int32_t i = 0; i < argc; i++) {
-    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
-    if (argValue->IsNull())
-      continue;
+  auto fn = [&uCount, &dSum](v8::Isolate* pIsolate,
+                             v8::Local<v8::Value> pValue) {
+    ++uCount;
+    dSum += ValueToDouble(pIsolate, pValue);
+  };
+  if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true))
+    return;
 
-    if (argValue->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
-      if (iLength <= 2) {
-        pContext->ThrowArgumentMismatchException();
-        return;
-      }
-
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          dSum += ValueToDouble(pThis, jsObjectValue.get());
-          uCount++;
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          dSum += ValueToDouble(pThis, newPropertyValue.get());
-          uCount++;
-        }
-      }
-    } else if (argValue->IsObject()) {
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
-      if (newPropertyValue->IsNull())
-        continue;
-
-      dSum += ValueToDouble(pThis, argValue.get());
-      uCount++;
-    } else {
-      dSum += ValueToDouble(pThis, argValue.get());
-      uCount++;
-    }
-  }
   if (uCount == 0) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
-
-  args.GetReturnValue()->SetDouble(dSum);
+  info.GetReturnValue().Set(dSum);
 }
 
 // static
-void CFXJSE_FormCalcContext::Date(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 0) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Date");
+void CFXJSE_FormCalcContext::Date(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 0) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Date");
     return;
   }
 
@@ -1835,140 +1618,141 @@
   FXSYS_time(&currentTime);
   struct tm* pTmStruct = gmtime(&currentTime);
 
-  args.GetReturnValue()->SetInteger(DateString2Num(
+  info.GetReturnValue().Set(DateString2Num(
       ByteString::Format("%d%02d%02d", pTmStruct->tm_year + 1900,
                          pTmStruct->tm_mon + 1, pTmStruct->tm_mday)
           .AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Date2Num(CFXJSE_Value* pThis,
-                                      ByteStringView bsFuncName,
-                                      CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Date2Num(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Date2Num");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Date2Num");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> dateValue = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, dateValue.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> dateValue = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), dateValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsDate = ValueToUTF8String(dateValue.get());
+  ByteString bsDate = ValueToUTF8String(info.GetIsolate(), dateValue);
   ByteString bsFormat;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
-    if (ValueIsNull(pThis, formatValue.get())) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> formatValue = GetSimpleValue(info, 1);
+    if (ValueIsNull(info.GetIsolate(), formatValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsFormat = ValueToUTF8String(formatValue.get());
+    bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue);
   }
 
   ByteString bsLocale;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
-    if (ValueIsNull(pThis, localeValue.get())) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
+    if (ValueIsNull(info.GetIsolate(), localeValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(localeValue.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
   }
 
   ByteString bsIsoDate =
       Local2IsoDate(pThis, bsDate.AsStringView(), bsFormat.AsStringView(),
                     bsLocale.AsStringView());
-  args.GetReturnValue()->SetInteger(DateString2Num(bsIsoDate.AsStringView()));
+  info.GetReturnValue().Set(DateString2Num(bsIsoDate.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::DateFmt(CFXJSE_Value* pThis,
-                                     ByteStringView bsFuncName,
-                                     CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::DateFmt(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Date2Num");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Date2Num");
     return;
   }
 
   int32_t iStyle = 0;
   if (argc > 0) {
-    std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
-    if (argStyle->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> infotyle = GetSimpleValue(info, 0);
+    if (fxv8::IsNull(infotyle)) {
+      info.GetReturnValue().SetNull();
       return;
     }
 
-    iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
+    iStyle = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), infotyle));
     if (iStyle < 0 || iStyle > 4)
       iStyle = 0;
   }
 
   ByteString bsLocale;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> argLocale = GetSimpleValue(pThis, args, 1);
-    if (argLocale->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> argLocale = GetSimpleValue(info, 1);
+    if (fxv8::IsNull(argLocale)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(argLocale.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale);
   }
 
   ByteString bsFormat =
       GetStandardDateFormat(pThis, iStyle, bsLocale.AsStringView());
-  args.GetReturnValue()->SetString(bsFormat.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::IsoDate2Num(CFXJSE_Value* pThis,
-                                         ByteStringView bsFuncName,
-                                         CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"IsoDate2Num");
+void CFXJSE_FormCalcContext::IsoDate2Num(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("IsoDate2Num");
     return;
   }
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  ByteString bsArg = ValueToUTF8String(argOne.get());
-  args.GetReturnValue()->SetInteger(DateString2Num(bsArg.AsStringView()));
+  ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne);
+  info.GetReturnValue().Set(DateString2Num(bsArg.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::IsoTime2Num(CFXJSE_Value* pThis,
-                                         ByteStringView bsFuncName,
-                                         CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::IsoTime2Num(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 1) {
-    pContext->ThrowParamCountMismatchException(L"IsoTime2Num");
+  if (info.Length() != 1) {
+    pContext->ThrowParamCountMismatchException("IsoTime2Num");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
   CXFA_Document* pDoc = pContext->GetDocument();
   CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
-  ByteString bsArg = ValueToUTF8String(argOne.get());
+  ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne);
   auto pos = bsArg.Find('T', 0);
   if (!pos.has_value() || pos.value() == bsArg.GetLength() - 1) {
-    args.GetReturnValue()->SetInteger(0);
+    info.GetReturnValue().Set(0);
     return;
   }
   bsArg = bsArg.Last(bsArg.GetLength() - (pos.value() + 1));
 
-  CXFA_LocaleValue timeValue(XFA_VT_TIME,
+  CXFA_LocaleValue timeValue(CXFA_LocaleValue::ValueType::kTime,
                              WideString::FromUTF8(bsArg.AsStringView()), pMgr);
   if (!timeValue.IsValid()) {
-    args.GetReturnValue()->SetInteger(0);
+    info.GetReturnValue().Set(0);
     return;
   }
 
@@ -1981,7 +1765,7 @@
   // TODO(dsinclair): See if there is other time conversion code in pdfium and
   //   consolidate.
   int32_t mins = hour * 60 + min;
-  mins -= (pMgr->GetDefLocale()->GetTimeZone().tzHour * 60);
+  mins -= pMgr->GetDefLocale()->GetTimeZoneInMinutes();
   while (mins > 1440)
     mins -= 1440;
   while (mins < 0)
@@ -1989,123 +1773,126 @@
   hour = mins / 60;
   min = mins % 60;
 
-  args.GetReturnValue()->SetInteger(hour * 3600000 + min * 60000 +
-                                    second * 1000 + milSecond + 1);
+  info.GetReturnValue().Set(hour * 3600000 + min * 60000 + second * 1000 +
+                            milSecond + 1);
 }
 
 // static
-void CFXJSE_FormCalcContext::LocalDateFmt(CFXJSE_Value* pThis,
-                                          ByteStringView bsFuncName,
-                                          CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::LocalDateFmt(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"LocalDateFmt");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("LocalDateFmt");
     return;
   }
 
   int32_t iStyle = 0;
   if (argc > 0) {
-    std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
-    if (argStyle->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> infotyle = GetSimpleValue(info, 0);
+    if (fxv8::IsNull(infotyle)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
+    iStyle = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), infotyle));
     if (iStyle > 4 || iStyle < 0)
       iStyle = 0;
   }
 
   ByteString bsLocale;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> argLocale = GetSimpleValue(pThis, args, 1);
-    if (argLocale->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> argLocale = GetSimpleValue(info, 1);
+    if (fxv8::IsNull(argLocale)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(argLocale.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale);
   }
 
   ByteString bsFormat =
       GetLocalDateFormat(pThis, iStyle, bsLocale.AsStringView(), false);
-  args.GetReturnValue()->SetString(bsFormat.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::LocalTimeFmt(CFXJSE_Value* pThis,
-                                          ByteStringView bsFuncName,
-                                          CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::LocalTimeFmt(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"LocalTimeFmt");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("LocalTimeFmt");
     return;
   }
 
   int32_t iStyle = 0;
   if (argc > 0) {
-    std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
-    if (argStyle->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> infotyle = GetSimpleValue(info, 0);
+    if (fxv8::IsNull(infotyle)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
+    iStyle = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), infotyle));
     if (iStyle > 4 || iStyle < 0)
       iStyle = 0;
   }
 
   ByteString bsLocale;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> argLocale = GetSimpleValue(pThis, args, 1);
-    if (argLocale->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> argLocale = GetSimpleValue(info, 1);
+    if (fxv8::IsNull(argLocale)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(argLocale.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale);
   }
 
   ByteString bsFormat =
       GetLocalTimeFormat(pThis, iStyle, bsLocale.AsStringView(), false);
-  args.GetReturnValue()->SetString(bsFormat.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Num2Date(CFXJSE_Value* pThis,
-                                      ByteStringView bsFuncName,
-                                      CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Num2Date(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Num2Date");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Num2Date");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> dateValue = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, dateValue.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> dateValue = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), dateValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  int32_t dDate = (int32_t)ValueToFloat(pThis, dateValue.get());
+  int32_t dDate =
+      static_cast<int32_t>(ValueToFloat(info.GetIsolate(), dateValue));
   if (dDate < 1) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
 
   ByteString bsFormat;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
-    if (ValueIsNull(pThis, formatValue.get())) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> formatValue = GetSimpleValue(info, 1);
+    if (ValueIsNull(info.GetIsolate(), formatValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsFormat = ValueToUTF8String(formatValue.get());
+    bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue);
   }
 
   ByteString bsLocale;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
-    if (ValueIsNull(pThis, localeValue.get())) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
+    if (ValueIsNull(info.GetIsolate(), localeValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(localeValue.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
   }
 
   int32_t iYear = 1900;
@@ -2204,242 +1991,247 @@
       pThis,
       ByteString::Format("%d%02d%02d", iYear + i, iMonth, iDay).AsStringView(),
       bsFormat.AsStringView(), bsLocale.AsStringView());
-  args.GetReturnValue()->SetString(bsLocalDate.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsLocalDate.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Num2GMTime(CFXJSE_Value* pThis,
-                                        ByteStringView bsFuncName,
-                                        CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Num2GMTime(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Num2GMTime");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Num2GMTime");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> timeValue = GetSimpleValue(pThis, args, 0);
-  if (timeValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> timeValue = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(timeValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  int32_t iTime = (int32_t)ValueToFloat(pThis, timeValue.get());
+  int32_t iTime =
+      static_cast<int32_t>(ValueToFloat(info.GetIsolate(), timeValue));
   if (abs(iTime) < 1.0) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
 
   ByteString bsFormat;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
-    if (formatValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> formatValue = GetSimpleValue(info, 1);
+    if (fxv8::IsNull(formatValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsFormat = ValueToUTF8String(formatValue.get());
+    bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue);
   }
 
   ByteString bsLocale;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
-    if (localeValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
+    if (fxv8::IsNull(localeValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(localeValue.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
   }
 
   ByteString bsGMTTime = Num2AllTime(pThis, iTime, bsFormat.AsStringView(),
                                      bsLocale.AsStringView(), true);
-  args.GetReturnValue()->SetString(bsGMTTime.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsGMTTime.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Num2Time(CFXJSE_Value* pThis,
-                                      ByteStringView bsFuncName,
-                                      CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Num2Time(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Num2Time");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Num2Time");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> timeValue = GetSimpleValue(pThis, args, 0);
-  if (timeValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> timeValue = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(timeValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  float fTime = ValueToFloat(pThis, timeValue.get());
+  float fTime = ValueToFloat(info.GetIsolate(), timeValue);
   if (fabs(fTime) < 1.0) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
 
   ByteString bsFormat;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
-    if (formatValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> formatValue = GetSimpleValue(info, 1);
+    if (fxv8::IsNull(formatValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsFormat = ValueToUTF8String(formatValue.get());
+    bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue);
   }
 
   ByteString bsLocale;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
-    if (localeValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
+    if (fxv8::IsNull(localeValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(localeValue.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
   }
 
   ByteString bsLocalTime =
       Num2AllTime(pThis, static_cast<int32_t>(fTime), bsFormat.AsStringView(),
                   bsLocale.AsStringView(), false);
-  args.GetReturnValue()->SetString(bsLocalTime.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsLocalTime.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Time(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 0) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Time");
+void CFXJSE_FormCalcContext::Time(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 0) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Time");
     return;
   }
 
   time_t now;
   FXSYS_time(&now);
-
   struct tm* pGmt = gmtime(&now);
-  args.GetReturnValue()->SetInteger(
+  info.GetReturnValue().Set(
       (pGmt->tm_hour * 3600 + pGmt->tm_min * 60 + pGmt->tm_sec) * 1000);
 }
 
 // static
-void CFXJSE_FormCalcContext::Time2Num(CFXJSE_Value* pThis,
-                                      ByteStringView bsFuncName,
-                                      CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Time2Num(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Time2Num");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Time2Num");
     return;
   }
 
   ByteString bsTime;
-  std::unique_ptr<CFXJSE_Value> timeValue = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, timeValue.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> timeValue = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), timeValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  bsTime = ValueToUTF8String(timeValue.get());
+  bsTime = ValueToUTF8String(info.GetIsolate(), timeValue);
 
   ByteString bsFormat;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
-    if (ValueIsNull(pThis, formatValue.get())) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> formatValue = GetSimpleValue(info, 1);
+    if (ValueIsNull(info.GetIsolate(), formatValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsFormat = ValueToUTF8String(formatValue.get());
+    bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue);
   }
 
   ByteString bsLocale;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
-    if (ValueIsNull(pThis, localeValue.get())) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
+    if (ValueIsNull(info.GetIsolate(), localeValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(localeValue.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
   }
 
   CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
   CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
-  LocaleIface* pLocale = nullptr;
-  if (bsLocale.IsEmpty()) {
-    CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
-    pLocale = pThisNode->GetLocale();
-  } else {
+  GCedLocaleIface* pLocale = nullptr;
+  if (!bsLocale.IsEmpty()) {
     pLocale =
         pMgr->GetLocaleByName(WideString::FromUTF8(bsLocale.AsStringView()));
   }
+  if (!pLocale) {
+    CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
+    pLocale = pThisNode->GetLocale();
+  }
 
   WideString wsFormat;
-  if (bsFormat.IsEmpty())
-    wsFormat = pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Default);
-  else
+  if (bsFormat.IsEmpty()) {
+    wsFormat =
+        pLocale->GetTimePattern(LocaleIface::DateTimeSubcategory::kDefault);
+  } else {
     wsFormat = WideString::FromUTF8(bsFormat.AsStringView());
-
+  }
   wsFormat = L"time{" + wsFormat + L"}";
-  CXFA_LocaleValue localeValue(XFA_VT_TIME,
+  CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kTime,
                                WideString::FromUTF8(bsTime.AsStringView()),
                                wsFormat, pLocale, pMgr);
   if (!localeValue.IsValid()) {
-    args.GetReturnValue()->SetInteger(0);
+    info.GetReturnValue().Set(0);
     return;
   }
 
   CFX_DateTime uniTime = localeValue.GetTime();
   int32_t hour = uniTime.GetHour();
-  int32_t min = uniTime.GetMinute();
-  int32_t second = uniTime.GetSecond();
-  int32_t milSecond = uniTime.GetMillisecond();
-  int32_t mins = hour * 60 + min;
+  int32_t minute = uniTime.GetMinute();
+  const int32_t second = uniTime.GetSecond();
+  const int32_t millisecond = uniTime.GetMillisecond();
 
-  mins -= (CXFA_TimeZoneProvider().GetTimeZone().tzHour * 60);
-  while (mins > 1440)
-    mins -= 1440;
+  constexpr int kMinutesInDay = 24 * 60;
+  int32_t minutes_with_tz =
+      hour * 60 + minute - CXFA_TimeZoneProvider().GetTimeZoneInMinutes();
+  minutes_with_tz %= kMinutesInDay;
+  if (minutes_with_tz < 0)
+    minutes_with_tz += kMinutesInDay;
 
-  while (mins < 0)
-    mins += 1440;
-
-  hour = mins / 60;
-  min = mins % 60;
-  args.GetReturnValue()->SetInteger(hour * 3600000 + min * 60000 +
-                                    second * 1000 + milSecond + 1);
+  hour = minutes_with_tz / 60;
+  minute = minutes_with_tz % 60;
+  info.GetReturnValue().Set(hour * 3600000 + minute * 60000 + second * 1000 +
+                            millisecond + 1);
 }
 
 // static
-void CFXJSE_FormCalcContext::TimeFmt(CFXJSE_Value* pThis,
-                                     ByteStringView bsFuncName,
-                                     CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::TimeFmt(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"TimeFmt");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("TimeFmt");
     return;
   }
 
   int32_t iStyle = 0;
   if (argc > 0) {
-    std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
-    if (argStyle->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> infotyle = GetSimpleValue(info, 0);
+    if (fxv8::IsNull(infotyle)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
+    iStyle = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), infotyle));
     if (iStyle > 4 || iStyle < 0)
       iStyle = 0;
   }
 
   ByteString bsLocale;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> argLocale = GetSimpleValue(pThis, args, 1);
-    if (argLocale->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> argLocale = GetSimpleValue(info, 1);
+    if (fxv8::IsNull(argLocale)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(argLocale.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale);
   }
 
   ByteString bsFormat =
       GetStandardTimeFormat(pThis, iStyle, bsLocale.AsStringView());
-  args.GetReturnValue()->SetString(bsFormat.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView()));
 }
 
 // static
-ByteString CFXJSE_FormCalcContext::Local2IsoDate(CFXJSE_Value* pThis,
+ByteString CFXJSE_FormCalcContext::Local2IsoDate(CFXJSE_HostObject* pThis,
                                                  ByteStringView bsDate,
                                                  ByteStringView bsFormat,
                                                  ByteStringView bsLocale) {
@@ -2448,21 +2240,22 @@
     return ByteString();
 
   CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
-  LocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
+  GCedLocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
   if (!pLocale)
     return ByteString();
 
   WideString wsFormat = FormatFromString(pLocale, bsFormat);
-  CFX_DateTime dt = CXFA_LocaleValue(XFA_VT_DATE, WideString::FromUTF8(bsDate),
-                                     wsFormat, pLocale, pMgr)
-                        .GetDate();
+  CFX_DateTime dt =
+      CXFA_LocaleValue(CXFA_LocaleValue::ValueType::kDate,
+                       WideString::FromUTF8(bsDate), wsFormat, pLocale, pMgr)
+          .GetDate();
 
   return ByteString::Format("%4d-%02d-%02d", dt.GetYear(), dt.GetMonth(),
                             dt.GetDay());
 }
 
 // static
-ByteString CFXJSE_FormCalcContext::IsoDate2Local(CFXJSE_Value* pThis,
+ByteString CFXJSE_FormCalcContext::IsoDate2Local(CFXJSE_HostObject* pThis,
                                                  ByteStringView bsDate,
                                                  ByteStringView bsFormat,
                                                  ByteStringView bsLocale) {
@@ -2471,19 +2264,20 @@
     return ByteString();
 
   CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
-  LocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
+  GCedLocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
   if (!pLocale)
     return ByteString();
 
   WideString wsFormat = FormatFromString(pLocale, bsFormat);
   WideString wsRet;
-  CXFA_LocaleValue(XFA_VT_DATE, WideString::FromUTF8(bsDate), pMgr)
-      .FormatPatterns(wsRet, wsFormat, pLocale, XFA_VALUEPICTURE_Display);
+  CXFA_LocaleValue(CXFA_LocaleValue::ValueType::kDate,
+                   WideString::FromUTF8(bsDate), pMgr)
+      .FormatPatterns(wsRet, wsFormat, pLocale, XFA_ValuePicture::kDisplay);
   return wsRet.ToUTF8();
 }
 
 // static
-ByteString CFXJSE_FormCalcContext::IsoTime2Local(CFXJSE_Value* pThis,
+ByteString CFXJSE_FormCalcContext::IsoTime2Local(CFXJSE_HostObject* pThis,
                                                  ByteStringView bsTime,
                                                  ByteStringView bsFormat,
                                                  ByteStringView bsLocale) {
@@ -2492,21 +2286,22 @@
     return ByteString();
 
   CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
-  LocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
+  GCedLocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
   if (!pLocale)
     return ByteString();
 
   WideString wsFormat = {
       L"time{", FormatFromString(pLocale, bsFormat).AsStringView(), L"}"};
-  CXFA_LocaleValue widgetValue(XFA_VT_TIME, WideString::FromUTF8(bsTime), pMgr);
+  CXFA_LocaleValue widgetValue(CXFA_LocaleValue::ValueType::kTime,
+                               WideString::FromUTF8(bsTime), pMgr);
   WideString wsRet;
   widgetValue.FormatPatterns(wsRet, wsFormat, pLocale,
-                             XFA_VALUEPICTURE_Display);
+                             XFA_ValuePicture::kDisplay);
   return wsRet.ToUTF8();
 }
 
 // static
-ByteString CFXJSE_FormCalcContext::GetLocalDateFormat(CFXJSE_Value* pThis,
+ByteString CFXJSE_FormCalcContext::GetLocalDateFormat(CFXJSE_HostObject* pThis,
                                                       int32_t iStyle,
                                                       ByteStringView bsLocale,
                                                       bool bStandard) {
@@ -2519,7 +2314,7 @@
 }
 
 // static
-ByteString CFXJSE_FormCalcContext::GetLocalTimeFormat(CFXJSE_Value* pThis,
+ByteString CFXJSE_FormCalcContext::GetLocalTimeFormat(CFXJSE_HostObject* pThis,
                                                       int32_t iStyle,
                                                       ByteStringView bsLocale,
                                                       bool bStandard) {
@@ -2533,7 +2328,7 @@
 
 // static
 ByteString CFXJSE_FormCalcContext::GetStandardDateFormat(
-    CFXJSE_Value* pThis,
+    CFXJSE_HostObject* pThis,
     int32_t iStyle,
     ByteStringView bsLocale) {
   return GetLocalDateFormat(pThis, iStyle, bsLocale, true);
@@ -2541,14 +2336,14 @@
 
 // static
 ByteString CFXJSE_FormCalcContext::GetStandardTimeFormat(
-    CFXJSE_Value* pThis,
+    CFXJSE_HostObject* pThis,
     int32_t iStyle,
     ByteStringView bsLocale) {
   return GetLocalTimeFormat(pThis, iStyle, bsLocale, true);
 }
 
 // static
-ByteString CFXJSE_FormCalcContext::Num2AllTime(CFXJSE_Value* pThis,
+ByteString CFXJSE_FormCalcContext::Num2AllTime(CFXJSE_HostObject* pThis,
                                                int32_t iTime,
                                                ByteStringView bsFormat,
                                                ByteStringView bsLocale,
@@ -2577,28 +2372,29 @@
 }
 
 // static
-void CFXJSE_FormCalcContext::Apr(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Apr(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"Apr");
+  if (info.Length() != 3) {
+    pContext->ThrowParamCountMismatchException("Apr");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  double nPrincipal = ValueToDouble(pThis, argOne.get());
-  double nPayment = ValueToDouble(pThis, argTwo.get());
-  double nPeriods = ValueToDouble(pThis, argThree.get());
-  if (nPrincipal <= 0 || nPayment <= 0 || nPeriods <= 0) {
+  double nPrincipal = ValueToDouble(info.GetIsolate(), argOne);
+  double nPayment = ValueToDouble(info.GetIsolate(), argTwo);
+  int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
+  if (nPrincipal <= 0 || nPayment <= 0 || nPeriods == 0) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
@@ -2615,7 +2411,7 @@
          (r * nTemp * nPeriods * (nTemp / (1 + r)))) /
         ((nTemp - 1) * (nTemp - 1));
     if (nDerivative == 0) {
-      args.GetReturnValue()->SetNull();
+      info.GetReturnValue().SetNull();
       return;
     }
 
@@ -2626,63 +2422,65 @@
     }
     nRet = r * nTemp / (nTemp - 1) - nPayment / nPrincipal;
   }
-  args.GetReturnValue()->SetDouble(r * 12);
+  info.GetReturnValue().Set(r * 12);
 }
 
 // static
-void CFXJSE_FormCalcContext::CTerm(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::CTerm(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"CTerm");
+  if (info.Length() != 3) {
+    pContext->ThrowParamCountMismatchException("CTerm");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  float nRate = ValueToFloat(pThis, argOne.get());
-  float nFutureValue = ValueToFloat(pThis, argTwo.get());
-  float nInitAmount = ValueToFloat(pThis, argThree.get());
+  float nRate = ValueToFloat(info.GetIsolate(), argOne);
+  float nFutureValue = ValueToFloat(info.GetIsolate(), argTwo);
+  float nInitAmount = ValueToFloat(info.GetIsolate(), argThree);
   if ((nRate <= 0) || (nFutureValue <= 0) || (nInitAmount <= 0)) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  args.GetReturnValue()->SetFloat(log((float)(nFutureValue / nInitAmount)) /
-                                  log((float)(1 + nRate)));
+  info.GetReturnValue().Set(log((float)(nFutureValue / nInitAmount)) /
+                            log((float)(1 + nRate)));
 }
 
 // static
-void CFXJSE_FormCalcContext::FV(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::FV(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"FV");
+  if (info.Length() != 3) {
+    pContext->ThrowParamCountMismatchException("FV");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  double nAmount = ValueToDouble(pThis, argOne.get());
-  double nRate = ValueToDouble(pThis, argTwo.get());
-  double nPeriod = ValueToDouble(pThis, argThree.get());
-  if ((nRate < 0) || (nPeriod <= 0) || (nAmount <= 0)) {
+  double nAmount = ValueToDouble(info.GetIsolate(), argOne);
+  double nRate = ValueToDouble(info.GetIsolate(), argTwo);
+  int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
+  if (nAmount <= 0 || nRate < 0 || nPeriods == 0) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
@@ -2690,44 +2488,46 @@
   double dResult = 0;
   if (nRate) {
     double nTemp = 1;
-    for (int i = 0; i < nPeriod; ++i) {
+    for (int i = 0; i < nPeriods; ++i) {
       nTemp *= 1 + nRate;
     }
     dResult = nAmount * (nTemp - 1) / nRate;
   } else {
-    dResult = nAmount * nPeriod;
+    dResult = nAmount * nPeriods;
   }
 
-  args.GetReturnValue()->SetDouble(dResult);
+  info.GetReturnValue().Set(dResult);
 }
 
 // static
-void CFXJSE_FormCalcContext::IPmt(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::IPmt(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 5) {
-    pContext->ThrowParamCountMismatchException(L"IPmt");
+  if (info.Length() != 5) {
+    pContext->ThrowParamCountMismatchException("IPmt");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  std::unique_ptr<CFXJSE_Value> argFour = GetSimpleValue(pThis, args, 3);
-  std::unique_ptr<CFXJSE_Value> argFive = GetSimpleValue(pThis, args, 4);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get()) || ValueIsNull(pThis, argFour.get()) ||
-      ValueIsNull(pThis, argFive.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  v8::Local<v8::Value> argFour = GetSimpleValue(info, 3);
+  v8::Local<v8::Value> argFive = GetSimpleValue(info, 4);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree) ||
+      ValueIsNull(info.GetIsolate(), argFour) ||
+      ValueIsNull(info.GetIsolate(), argFive)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  float nPrincipalAmount = ValueToFloat(pThis, argOne.get());
-  float nRate = ValueToFloat(pThis, argTwo.get());
-  float nPayment = ValueToFloat(pThis, argThree.get());
-  float nFirstMonth = ValueToFloat(pThis, argFour.get());
-  float nNumberOfMonths = ValueToFloat(pThis, argFive.get());
+  float nPrincipalAmount = ValueToFloat(info.GetIsolate(), argOne);
+  float nRate = ValueToFloat(info.GetIsolate(), argTwo);
+  float nPayment = ValueToFloat(info.GetIsolate(), argThree);
+  float nFirstMonth = ValueToFloat(info.GetIsolate(), argFour);
+  float nNumberOfMonths = ValueToFloat(info.GetIsolate(), argFive);
   if ((nPrincipalAmount <= 0) || (nRate <= 0) || (nPayment <= 0) ||
       (nFirstMonth < 0) || (nNumberOfMonths < 0)) {
     pContext->ThrowArgumentMismatchException();
@@ -2735,14 +2535,15 @@
   }
 
   float nRateOfMonth = nRate / 12;
-  int32_t iNums =
-      (int32_t)((log10((float)(nPayment / nPrincipalAmount)) -
-                 log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) /
-                log10((float)(1 + nRateOfMonth)));
-  int32_t iEnd = std::min((int32_t)(nFirstMonth + nNumberOfMonths - 1), iNums);
+  int32_t iNums = static_cast<int32_t>(
+      (log10((float)(nPayment / nPrincipalAmount)) -
+       log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) /
+      log10((float)(1 + nRateOfMonth)));
+  int32_t iEnd =
+      std::min(static_cast<int32_t>(nFirstMonth + nNumberOfMonths - 1), iNums);
 
   if (nPayment < nPrincipalAmount * nRateOfMonth) {
-    args.GetReturnValue()->SetFloat(0);
+    info.GetReturnValue().Set(0);
     return;
   }
 
@@ -2755,114 +2556,113 @@
     nSum += nPrincipalAmount * nRateOfMonth;
     nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth;
   }
-  args.GetReturnValue()->SetFloat(nSum);
+  info.GetReturnValue().Set(nSum);
 }
 
 // static
-void CFXJSE_FormCalcContext::NPV(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::NPV(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  int32_t argc = args.GetLength();
-  if (argc < 3) {
-    pContext->ThrowParamCountMismatchException(L"NPV");
+  int32_t argc = info.Length();
+  if (argc < 2) {
+    pContext->ThrowParamCountMismatchException("NPV");
     return;
   }
 
-  std::vector<std::unique_ptr<CFXJSE_Value>> argValues;
-  for (int32_t i = 0; i < argc; i++) {
-    argValues.push_back(GetSimpleValue(pThis, args, i));
-    if (ValueIsNull(pThis, argValues[i].get())) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
+  v8::Local<v8::Value> argValue = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argValue)) {
+    info.GetReturnValue().SetNull();
+    return;
   }
 
-  double nRate = ValueToDouble(pThis, argValues[0].get());
+  double nRate = ValueToDouble(info.GetIsolate(), argValue);
   if (nRate <= 0) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  std::vector<double> data(argc - 1);
-  for (int32_t i = 1; i < argc; i++)
-    data.push_back(ValueToDouble(pThis, argValues[i].get()));
+  std::vector<double> data;
+  for (int32_t i = 1; i < argc; i++) {
+    argValue = GetSimpleValue(info, i);
+    if (ValueIsNull(info.GetIsolate(), argValue)) {
+      info.GetReturnValue().SetNull();
+      return;
+    }
+    data.push_back(ValueToDouble(info.GetIsolate(), argValue));
+  }
 
   double nSum = 0;
-  int32_t iIndex = 0;
-  for (int32_t i = 0; i < argc - 1; i++) {
-    double nTemp = 1;
-    for (int32_t j = 0; j <= i; j++)
-      nTemp *= 1 + nRate;
-
-    double nNum = data[iIndex++];
-    nSum += nNum / nTemp;
+  double nDivisor = 1.0 + nRate;
+  while (!data.empty()) {
+    nSum += data.back();
+    nSum /= nDivisor;
+    data.pop_back();
   }
-  args.GetReturnValue()->SetDouble(nSum);
+  info.GetReturnValue().Set(nSum);
 }
 
 // static
-void CFXJSE_FormCalcContext::Pmt(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Pmt(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"Pmt");
+  if (info.Length() != 3) {
+    pContext->ThrowParamCountMismatchException("Pmt");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  float nPrincipal = ValueToFloat(pThis, argOne.get());
-  float nRate = ValueToFloat(pThis, argTwo.get());
-  float nPeriods = ValueToFloat(pThis, argThree.get());
-  if ((nPrincipal <= 0) || (nRate <= 0) || (nPeriods <= 0)) {
+  double nPrincipal = ValueToDouble(info.GetIsolate(), argOne);
+  double nRate = ValueToDouble(info.GetIsolate(), argTwo);
+  int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
+  if (nPrincipal <= 0 || nRate <= 0 || nPeriods == 0) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  float nTmp = 1 + nRate;
-  float nSum = nTmp;
-  for (int32_t i = 0; i < nPeriods - 1; ++i)
-    nSum *= nTmp;
-
-  args.GetReturnValue()->SetFloat((nPrincipal * nRate * nSum) / (nSum - 1));
+  double nSum = pow(1.0 + nRate, nPeriods);
+  info.GetReturnValue().Set((nPrincipal * nRate * nSum) / (nSum - 1));
 }
 
 // static
-void CFXJSE_FormCalcContext::PPmt(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::PPmt(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 5) {
-    pContext->ThrowParamCountMismatchException(L"PPmt");
+  if (info.Length() != 5) {
+    pContext->ThrowParamCountMismatchException("PPmt");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  std::unique_ptr<CFXJSE_Value> argFour = GetSimpleValue(pThis, args, 3);
-  std::unique_ptr<CFXJSE_Value> argFive = GetSimpleValue(pThis, args, 4);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get()) || ValueIsNull(pThis, argFour.get()) ||
-      ValueIsNull(pThis, argFive.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  v8::Local<v8::Value> argFour = GetSimpleValue(info, 3);
+  v8::Local<v8::Value> argFive = GetSimpleValue(info, 4);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree) ||
+      ValueIsNull(info.GetIsolate(), argFour) ||
+      ValueIsNull(info.GetIsolate(), argFive)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  float nPrincipalAmount = ValueToFloat(pThis, argOne.get());
-  float nRate = ValueToFloat(pThis, argTwo.get());
-  float nPayment = ValueToFloat(pThis, argThree.get());
-  float nFirstMonth = ValueToFloat(pThis, argFour.get());
-  float nNumberOfMonths = ValueToFloat(pThis, argFive.get());
+  float nPrincipalAmount = ValueToFloat(info.GetIsolate(), argOne);
+  float nRate = ValueToFloat(info.GetIsolate(), argTwo);
+  float nPayment = ValueToFloat(info.GetIsolate(), argThree);
+  float nFirstMonth = ValueToFloat(info.GetIsolate(), argFour);
+  float nNumberOfMonths = ValueToFloat(info.GetIsolate(), argFive);
   if ((nPrincipalAmount <= 0) || (nRate <= 0) || (nPayment <= 0) ||
       (nFirstMonth < 0) || (nNumberOfMonths < 0)) {
     pContext->ThrowArgumentMismatchException();
@@ -2870,11 +2670,12 @@
   }
 
   float nRateOfMonth = nRate / 12;
-  int32_t iNums =
-      (int32_t)((log10((float)(nPayment / nPrincipalAmount)) -
-                 log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) /
-                log10((float)(1 + nRateOfMonth)));
-  int32_t iEnd = std::min((int32_t)(nFirstMonth + nNumberOfMonths - 1), iNums);
+  int32_t iNums = static_cast<int32_t>(
+      (log10((float)(nPayment / nPrincipalAmount)) -
+       log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) /
+      log10((float)(1 + nRateOfMonth)));
+  int32_t iEnd =
+      std::min(static_cast<int32_t>(nFirstMonth + nNumberOfMonths - 1), iNums);
   if (nPayment < nPrincipalAmount * nRateOfMonth) {
     pContext->ThrowArgumentMismatchException();
     return;
@@ -2891,126 +2692,124 @@
     nSum += nTemp;
     nPrincipalAmount -= nTemp;
   }
-  args.GetReturnValue()->SetFloat(nSum);
+  info.GetReturnValue().Set(nSum);
 }
 
 // static
-void CFXJSE_FormCalcContext::PV(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::PV(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"PV");
+  if (info.Length() != 3) {
+    pContext->ThrowParamCountMismatchException("PV");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  double nAmount = ValueToDouble(pThis, argOne.get());
-  double nRate = ValueToDouble(pThis, argTwo.get());
-  double nPeriod = ValueToDouble(pThis, argThree.get());
-  if ((nAmount <= 0) || (nRate < 0) || (nPeriod <= 0)) {
+  double nAmount = ValueToDouble(info.GetIsolate(), argOne);
+  double nRate = ValueToDouble(info.GetIsolate(), argTwo);
+  int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
+  if (nAmount <= 0 || nRate < 0 || nPeriods == 0) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  double nTemp = 1;
-  for (int32_t i = 0; i < nPeriod; ++i)
-    nTemp *= 1 + nRate;
-
-  nTemp = 1 / nTemp;
-  args.GetReturnValue()->SetDouble(nAmount * ((1 - nTemp) / nRate));
+  double nTemp = 1 / pow(1.0 + nRate, nPeriods);
+  info.GetReturnValue().Set(nAmount * ((1.0 - nTemp) / nRate));
 }
 
 // static
-void CFXJSE_FormCalcContext::Rate(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Rate(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"Rate");
+  if (info.Length() != 3) {
+    pContext->ThrowParamCountMismatchException("Rate");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  float nFuture = ValueToFloat(pThis, argOne.get());
-  float nPresent = ValueToFloat(pThis, argTwo.get());
-  float nTotalNumber = ValueToFloat(pThis, argThree.get());
-  if ((nFuture <= 0) || (nPresent < 0) || (nTotalNumber <= 0)) {
+  float nFuture = ValueToFloat(info.GetIsolate(), argOne);
+  float nPresent = ValueToFloat(info.GetIsolate(), argTwo);
+  int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
+  if (nFuture <= 0 || nPresent < 0 || nPeriods == 0) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  args.GetReturnValue()->SetFloat(
-      FXSYS_pow((float)(nFuture / nPresent), (float)(1 / nTotalNumber)) - 1);
+  info.GetReturnValue().Set(powf(nFuture / nPresent, 1.0f / nPeriods) - 1.0f);
 }
 
 // static
-void CFXJSE_FormCalcContext::Term(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Term(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"Term");
+  if (info.Length() != 3) {
+    pContext->ThrowParamCountMismatchException("Term");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  float nMount = ValueToFloat(pThis, argOne.get());
-  float nRate = ValueToFloat(pThis, argTwo.get());
-  float nFuture = ValueToFloat(pThis, argThree.get());
+  float nMount = ValueToFloat(info.GetIsolate(), argOne);
+  float nRate = ValueToFloat(info.GetIsolate(), argTwo);
+  float nFuture = ValueToFloat(info.GetIsolate(), argThree);
   if ((nMount <= 0) || (nRate <= 0) || (nFuture <= 0)) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  args.GetReturnValue()->SetFloat(log((float)(nFuture / nMount * nRate) + 1) /
-                                  log((float)(1 + nRate)));
+  info.GetReturnValue().Set(log((float)(nFuture / nMount * nRate) + 1) /
+                            log((float)(1 + nRate)));
 }
 
 // static
-void CFXJSE_FormCalcContext::Choose(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Choose(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  int32_t argc = args.GetLength();
+  int32_t argc = info.Length();
   if (argc < 2) {
-    pContext->ThrowParamCountMismatchException(L"Choose");
+    pContext->ThrowParamCountMismatchException("Choose");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
+  if (ValueIsNull(info.GetIsolate(), info[0])) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  int32_t iIndex = (int32_t)ValueToFloat(pThis, argOne.get());
+  int32_t iIndex =
+      static_cast<int32_t>(ValueToFloat(info.GetIsolate(), info[0]));
   if (iIndex < 1) {
-    args.GetReturnValue()->SetString("");
+    info.GetReturnValue().SetEmptyString();
     return;
   }
 
@@ -3018,268 +2817,261 @@
   bool bStopCounterFlags = false;
   int32_t iArgIndex = 1;
   int32_t iValueIndex = 0;
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
   while (!bFound && !bStopCounterFlags && (iArgIndex < argc)) {
-    std::unique_ptr<CFXJSE_Value> argIndexValue = args.GetValue(iArgIndex);
-    if (argIndexValue->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argIndexValue->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
+    v8::Local<v8::Value> argIndexValue = info[iArgIndex];
+    if (fxv8::IsArray(argIndexValue)) {
+      v8::Local<v8::Array> arr = argIndexValue.As<v8::Array>();
+      uint32_t iLength = fxv8::GetArrayLengthHelper(arr);
       if (iLength > 3)
         bStopCounterFlags = true;
 
       iValueIndex += (iLength - 2);
       if (iValueIndex >= iIndex) {
-        auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-        auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-        auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-        argIndexValue->GetObjectPropertyByIdx(1, propertyValue.get());
-        argIndexValue->GetObjectPropertyByIdx(
-            (iLength - 1) - (iValueIndex - iIndex), jsObjectValue.get());
-        if (propertyValue->IsNull()) {
-          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-        } else {
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
+        v8::Local<v8::Value> propertyValue =
+            fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 1);
+        v8::Local<v8::Value> jsValue = fxv8::ReentrantGetArrayElementHelper(
+            info.GetIsolate(), arr, (iLength - 1) - (iValueIndex - iIndex));
+        v8::Local<v8::Value> newPropertyValue;
+        if (fxv8::IsObject(jsValue)) {
+          v8::Local<v8::Object> jsObjectValue = jsValue.As<v8::Object>();
+          if (fxv8::IsNull(propertyValue)) {
+            newPropertyValue =
+                GetObjectDefaultValue(info.GetIsolate(), jsObjectValue);
+          } else {
+            ByteString bsName = fxv8::ReentrantToByteStringHelper(
+                info.GetIsolate(), propertyValue);
+            newPropertyValue = fxv8::ReentrantGetObjectPropertyHelper(
+                info.GetIsolate(), jsObjectValue, bsName.AsStringView());
+          }
         }
-        ByteString bsChosen = ValueToUTF8String(newPropertyValue.get());
-        args.GetReturnValue()->SetString(bsChosen.AsStringView());
+        ByteString bsChosen =
+            ValueToUTF8String(info.GetIsolate(), newPropertyValue);
+        info.GetReturnValue().Set(
+            fxv8::NewStringHelper(info.GetIsolate(), bsChosen.AsStringView()));
         bFound = true;
       }
     } else {
       iValueIndex++;
       if (iValueIndex == iIndex) {
-        ByteString bsChosen = ValueToUTF8String(argIndexValue.get());
-        args.GetReturnValue()->SetString(bsChosen.AsStringView());
+        ByteString bsChosen =
+            ValueToUTF8String(info.GetIsolate(), argIndexValue);
+        info.GetReturnValue().Set(
+            fxv8::NewStringHelper(info.GetIsolate(), bsChosen.AsStringView()));
         bFound = true;
       }
     }
     iArgIndex++;
   }
   if (!bFound)
-    args.GetReturnValue()->SetString("");
+    info.GetReturnValue().SetEmptyString();
 }
 
 // static
-void CFXJSE_FormCalcContext::Exists(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Exists");
+void CFXJSE_FormCalcContext::Exists(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Exists");
     return;
   }
-  args.GetReturnValue()->SetInteger(args.GetValue(0)->IsObject());
+  info.GetReturnValue().Set(fxv8::IsObject(info[0]));
 }
 
 // static
-void CFXJSE_FormCalcContext::HasValue(CFXJSE_Value* pThis,
-                                      ByteStringView bsFuncName,
-                                      CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"HasValue");
+void CFXJSE_FormCalcContext::HasValue(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("HasValue");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (!argOne->IsString()) {
-    args.GetReturnValue()->SetInteger(argOne->IsNumber() ||
-                                      argOne->IsBoolean());
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (!fxv8::IsString(argOne)) {
+    info.GetReturnValue().Set(
+        static_cast<int>(fxv8::IsNumber(argOne) || fxv8::IsBoolean(argOne)));
     return;
   }
 
-  ByteString bsValue = argOne->ToString();
+  ByteString bsValue =
+      fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argOne);
   bsValue.TrimLeft();
-  args.GetReturnValue()->SetInteger(!bsValue.IsEmpty());
+  info.GetReturnValue().Set(static_cast<int>(!bsValue.IsEmpty()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Oneof(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() < 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Oneof");
+void CFXJSE_FormCalcContext::Oneof(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() < 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Oneof");
     return;
   }
 
-  bool bFlags = false;
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::vector<std::unique_ptr<CFXJSE_Value>> parameterValues =
-      unfoldArgs(pThis, args);
-  for (const auto& value : parameterValues) {
-    if (simpleValueCompare(pThis, argOne.get(), value.get())) {
-      bFlags = true;
-      break;
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  for (const auto& value : UnfoldArgs(info)) {
+    if (SimpleValueCompare(info.GetIsolate(), argOne, value)) {
+      info.GetReturnValue().Set(1);
+      return;
     }
   }
-
-  args.GetReturnValue()->SetInteger(bFlags);
+  info.GetReturnValue().Set(0);
 }
 
 // static
-void CFXJSE_FormCalcContext::Within(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args) {
-  if (args.GetLength() != 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Within");
+void CFXJSE_FormCalcContext::Within(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 3) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Within");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetUndefined();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(argOne)) {
+    info.GetReturnValue().SetUndefined();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argLow = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argHigh = GetSimpleValue(pThis, args, 2);
-  if (argOne->IsNumber()) {
-    float oneNumber = ValueToFloat(pThis, argOne.get());
-    float lowNumber = ValueToFloat(pThis, argLow.get());
-    float heightNumber = ValueToFloat(pThis, argHigh.get());
-    args.GetReturnValue()->SetInteger((oneNumber >= lowNumber) &&
-                                      (oneNumber <= heightNumber));
+  v8::Local<v8::Value> argLow = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argHigh = GetSimpleValue(info, 2);
+  if (fxv8::IsNumber(argOne)) {
+    float oneNumber = ValueToFloat(info.GetIsolate(), argOne);
+    float lowNumber = ValueToFloat(info.GetIsolate(), argLow);
+    float heightNumber = ValueToFloat(info.GetIsolate(), argHigh);
+    info.GetReturnValue().Set(static_cast<int>((oneNumber >= lowNumber) &&
+                                               (oneNumber <= heightNumber)));
     return;
   }
 
-  ByteString bsOne = ValueToUTF8String(argOne.get());
-  ByteString bsLow = ValueToUTF8String(argLow.get());
-  ByteString bsHeight = ValueToUTF8String(argHigh.get());
-  args.GetReturnValue()->SetInteger(
-      (bsOne.Compare(bsLow.AsStringView()) >= 0) &&
-      (bsOne.Compare(bsHeight.AsStringView()) <= 0));
+  ByteString bsOne = ValueToUTF8String(info.GetIsolate(), argOne);
+  ByteString bsLow = ValueToUTF8String(info.GetIsolate(), argLow);
+  ByteString bsHeight = ValueToUTF8String(info.GetIsolate(), argHigh);
+  info.GetReturnValue().Set(
+      static_cast<int>((bsOne.Compare(bsLow.AsStringView()) >= 0) &&
+                       (bsOne.Compare(bsHeight.AsStringView()) <= 0)));
 }
 
 // static
-void CFXJSE_FormCalcContext::If(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args) {
-  if (args.GetLength() != 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"If");
+void CFXJSE_FormCalcContext::If(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 3) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("If");
     return;
   }
 
-  args.GetReturnValue()->Assign(GetSimpleValue(pThis, args, 0)->ToBoolean()
-                                    ? GetSimpleValue(pThis, args, 1).get()
-                                    : GetSimpleValue(pThis, args, 2).get());
+  const bool condition = fxv8::ReentrantToBooleanHelper(
+      info.GetIsolate(), GetSimpleValue(info, 0));
+
+  info.GetReturnValue().Set(GetSimpleValue(info, condition ? 1 : 2));
 }
 
 // static
-void CFXJSE_FormCalcContext::Eval(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Eval(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 1) {
-    pContext->ThrowParamCountMismatchException(L"Eval");
+  if (info.Length() != 1) {
+    pContext->ThrowParamCountMismatchException("Eval");
     return;
   }
 
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  std::unique_ptr<CFXJSE_Value> scriptValue = GetSimpleValue(pThis, args, 0);
-  ByteString bsUtf8Script = ValueToUTF8String(scriptValue.get());
+  v8::Isolate* pIsolate = pContext->GetIsolate();
+  v8::Local<v8::Value> scriptValue = GetSimpleValue(info, 0);
+  ByteString bsUtf8Script = ValueToUTF8String(info.GetIsolate(), scriptValue);
   if (bsUtf8Script.IsEmpty()) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  CFX_WideTextBuf wsJavaScriptBuf;
-  if (!CFXJSE_FormCalcContext::Translate(
-          WideString::FromUTF8(bsUtf8Script.AsStringView()).AsStringView(),
-          &wsJavaScriptBuf)) {
+  WideString wsCalcScript = WideString::FromUTF8(bsUtf8Script.AsStringView());
+  absl::optional<WideTextBuffer> wsJavaScriptBuf =
+      CFXJSE_FormCalcContext::Translate(pContext->GetDocument()->GetHeap(),
+                                        wsCalcScript.AsStringView());
+  if (!wsJavaScriptBuf.has_value()) {
     pContext->ThrowCompilerErrorException();
     return;
   }
+  std::unique_ptr<CFXJSE_Context> pNewContext =
+      CFXJSE_Context::Create(pIsolate, nullptr, nullptr, nullptr);
 
-  std::unique_ptr<CFXJSE_Context> pNewContext(
-      CFXJSE_Context::Create(pIsolate, nullptr, nullptr));
+  auto returnValue = std::make_unique<CFXJSE_Value>();
+  ByteString bsScript = FX_UTF8Encode(wsJavaScriptBuf.value().AsStringView());
+  pNewContext->ExecuteScript(bsScript.AsStringView(), returnValue.get(),
+                             v8::Local<v8::Object>());
 
-  auto returnValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  pNewContext->ExecuteScript(
-      FX_UTF8Encode(wsJavaScriptBuf.AsStringView()).c_str(), returnValue.get(),
-      nullptr);
-
-  args.GetReturnValue()->Assign(returnValue.get());
+  info.GetReturnValue().Set(returnValue->DirectGetValue());
 }
 
 // static
-void CFXJSE_FormCalcContext::Ref(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Ref(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  if (args.GetLength() != 1) {
-    pContext->ThrowParamCountMismatchException(L"Ref");
+  if (info.Length() != 1) {
+    pContext->ThrowParamCountMismatchException("Ref");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (!argOne->IsArray() && !argOne->IsObject() && !argOne->IsBoolean() &&
-      !argOne->IsString() && !argOne->IsNull() && !argOne->IsNumber()) {
+  v8::Local<v8::Value> argOne = info[0];
+  if (fxv8::IsBoolean(argOne) || fxv8::IsString(argOne) ||
+      fxv8::IsNumber(argOne)) {
+    info.GetReturnValue().Set(argOne);
+    return;
+  }
+
+  std::vector<v8::Local<v8::Value>> values(3);
+  int intVal = 3;
+  if (fxv8::IsNull(argOne)) {
+    // TODO(dsinclair): Why is this 4 when the others are all 3?
+    intVal = 4;
+    values[2] = fxv8::NewNullHelper(info.GetIsolate());
+  } else if (fxv8::IsArray(argOne)) {
+    v8::Local<v8::Array> arr = argOne.As<v8::Array>();
+    v8::Local<v8::Value> propertyValue =
+        fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 1);
+    v8::Local<v8::Value> jsObjectValue =
+        fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2);
+    if (!fxv8::IsNull(propertyValue) || fxv8::IsNull(jsObjectValue)) {
+      pContext->ThrowArgumentMismatchException();
+      return;
+    }
+    values[2] = jsObjectValue;
+  } else if (fxv8::IsObject(argOne)) {
+    values[2] = argOne;
+  } else {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  if (argOne->IsBoolean() || argOne->IsString() || argOne->IsNumber()) {
-    args.GetReturnValue()->Assign(argOne.get());
-    return;
-  }
-
-  std::vector<std::unique_ptr<CFXJSE_Value>> values;
-  for (int32_t i = 0; i < 3; i++)
-    values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-  int intVal = 3;
-  if (argOne->IsNull()) {
-    // TODO(dsinclair): Why is this 4 when the others are all 3?
-    intVal = 4;
-    values[2]->SetNull();
-  } else if (argOne->IsArray()) {
-#ifndef NDEBUG
-    auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argOne->GetObjectProperty("length", lengthValue.get());
-    ASSERT(lengthValue->ToInteger() >= 3);
-#endif
-
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argOne->GetObjectPropertyByIdx(1, propertyValue.get());
-    argOne->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (!propertyValue->IsNull() || jsObjectValue->IsNull()) {
-      pContext->ThrowArgumentMismatchException();
-      return;
-    }
-
-    values[2]->Assign(jsObjectValue.get());
-  } else if (argOne->IsObject()) {
-    values[2]->Assign(argOne.get());
-  }
-
-  values[0]->SetInteger(intVal);
-  values[1]->SetNull();
-  args.GetReturnValue()->SetArray(values);
+  values[0] = fxv8::NewNumberHelper(info.GetIsolate(), intVal);
+  values[1] = fxv8::NewNullHelper(info.GetIsolate());
+  info.GetReturnValue().Set(fxv8::NewArrayHelper(info.GetIsolate(), values));
 }
 
 // static
-void CFXJSE_FormCalcContext::UnitType(CFXJSE_Value* pThis,
-                                      ByteStringView bsFuncName,
-                                      CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"UnitType");
+void CFXJSE_FormCalcContext::UnitType(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("UnitType");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> unitspanValue = GetSimpleValue(pThis, args, 0);
-  if (unitspanValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> unitspanValue = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(unitspanValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsUnitspan = ValueToUTF8String(unitspanValue.get());
+  ByteString bsUnitspan = ValueToUTF8String(info.GetIsolate(), unitspanValue);
   if (bsUnitspan.IsEmpty()) {
-    args.GetReturnValue()->SetString("in");
+    info.GetReturnValue().SetEmptyString();
     return;
   }
 
-  enum XFA_FM2JS_VALUETYPE_ParserStatus {
+  enum XFA_FormCalc_VALUETYPE_ParserStatus {
     VALUETYPE_START,
     VALUETYPE_HAVEINVALIDCHAR,
     VALUETYPE_HAVEDIGIT,
@@ -3298,7 +3090,7 @@
   while (IsWhitespace(pData[u]))
     u++;
 
-  XFA_FM2JS_VALUETYPE_ParserStatus eParserStatus = VALUETYPE_START;
+  XFA_FormCalc_VALUETYPE_ParserStatus eParserStatus = VALUETYPE_START;
   wchar_t typeChar;
   // TODO(dsinclair): Cleanup this parser, figure out what the various checks
   //    are for.
@@ -3348,43 +3140,43 @@
   }
   switch (eParserStatus) {
     case VALUETYPE_ISCM:
-      args.GetReturnValue()->SetString("cm");
+      info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "cm"));
       break;
     case VALUETYPE_ISMM:
-      args.GetReturnValue()->SetString("mm");
+      info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "mm"));
       break;
     case VALUETYPE_ISPT:
-      args.GetReturnValue()->SetString("pt");
+      info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "pt"));
       break;
     case VALUETYPE_ISMP:
-      args.GetReturnValue()->SetString("mp");
+      info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "mp"));
       break;
     default:
-      args.GetReturnValue()->SetString("in");
+      info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "in"));
       break;
   }
 }
 
 // static
-void CFXJSE_FormCalcContext::UnitValue(CFXJSE_Value* pThis,
-                                       ByteStringView bsFuncName,
-                                       CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::UnitValue(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"UnitValue");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("UnitValue");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> unitspanValue = GetSimpleValue(pThis, args, 0);
-  if (unitspanValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> unitspanValue = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(unitspanValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsUnitspan = ValueToUTF8String(unitspanValue.get());
+  ByteString bsUnitspan = ValueToUTF8String(info.GetIsolate(), unitspanValue);
   const char* pData = bsUnitspan.c_str();
   if (!pData) {
-    args.GetReturnValue()->SetInteger(0);
+    info.GetReturnValue().Set(0);
     return;
   }
 
@@ -3416,15 +3208,15 @@
 
   ByteString bsUnit;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> unitValue = GetSimpleValue(pThis, args, 1);
-    ByteString bsUnitTemp = ValueToUTF8String(unitValue.get());
+    v8::Local<v8::Value> unitValue = GetSimpleValue(info, 1);
+    ByteString bsUnitTemp = ValueToUTF8String(info.GetIsolate(), unitValue);
     const char* pChar = bsUnitTemp.c_str();
     size_t uVal = 0;
     while (IsWhitespace(pChar[uVal]))
       ++uVal;
 
     while (uVal < bsUnitTemp.GetLength()) {
-      if (!std::isdigit(pChar[uVal]) && pChar[uVal] != '.')
+      if (!isdigit(pChar[uVal]) && pChar[uVal] != '.')
         break;
       ++uVal;
     }
@@ -3501,99 +3293,102 @@
     else
       dResult = dFirstNumber / 72000;
   }
-  args.GetReturnValue()->SetDouble(dResult);
+  info.GetReturnValue().Set(dResult);
 }
 
 // static
-void CFXJSE_FormCalcContext::At(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"At");
+void CFXJSE_FormCalcContext::At(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("At");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString stringTwo = ValueToUTF8String(argTwo.get());
+  ByteString stringTwo = ValueToUTF8String(info.GetIsolate(), argTwo);
   if (stringTwo.IsEmpty()) {
-    args.GetReturnValue()->SetInteger(1);
+    info.GetReturnValue().Set(1);
     return;
   }
 
-  ByteString stringOne = ValueToUTF8String(argOne.get());
+  ByteString stringOne = ValueToUTF8String(info.GetIsolate(), argOne);
   auto pos = stringOne.Find(stringTwo.AsStringView());
-  args.GetReturnValue()->SetInteger(pos.has_value() ? pos.value() + 1 : 0);
+  info.GetReturnValue().Set(
+      static_cast<int>(pos.has_value() ? pos.value() + 1 : 0));
 }
 
 // static
-void CFXJSE_FormCalcContext::Concat(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Concat(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Concat");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Concat");
     return;
   }
 
   ByteString bsResult;
   bool bAllNull = true;
   for (int32_t i = 0; i < argc; i++) {
-    std::unique_ptr<CFXJSE_Value> value = GetSimpleValue(pThis, args, i);
-    if (ValueIsNull(pThis, value.get()))
+    v8::Local<v8::Value> value = GetSimpleValue(info, i);
+    if (ValueIsNull(info.GetIsolate(), value))
       continue;
 
     bAllNull = false;
-    bsResult += ValueToUTF8String(value.get());
+    bsResult += ValueToUTF8String(info.GetIsolate(), value);
   }
 
   if (bAllNull) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
-
-  args.GetReturnValue()->SetString(bsResult.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsResult.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Decode(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Decode(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Decode");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Decode");
     return;
   }
 
   if (argc == 1) {
-    std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-    if (ValueIsNull(pThis, argOne.get())) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+    if (ValueIsNull(info.GetIsolate(), argOne)) {
+      info.GetReturnValue().SetNull();
       return;
     }
 
-    WideString decoded = DecodeURL(
-        WideString::FromUTF8(ValueToUTF8String(argOne.get()).AsStringView()));
-
-    args.GetReturnValue()->SetString(
-        FX_UTF8Encode(decoded.AsStringView()).AsStringView());
+    WideString decoded = DecodeURL(WideString::FromUTF8(
+        ValueToUTF8String(info.GetIsolate(), argOne).AsStringView()));
+    auto result = FX_UTF8Encode(decoded.AsStringView());
+    info.GetReturnValue().Set(
+        fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsToDecode = ValueToUTF8String(argOne.get());
-  ByteString bsIdentify = ValueToUTF8String(argTwo.get());
+  ByteString bsToDecode = ValueToUTF8String(info.GetIsolate(), argOne);
+  ByteString bsIdentify = ValueToUTF8String(info.GetIsolate(), argTwo);
   WideString decoded;
 
   WideString wsToDecode = WideString::FromUTF8(bsToDecode.AsStringView());
@@ -3605,42 +3400,45 @@
   else
     decoded = DecodeURL(wsToDecode);
 
-  args.GetReturnValue()->SetString(
-      FX_UTF8Encode(decoded.AsStringView()).AsStringView());
+  auto result = FX_UTF8Encode(decoded.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Encode(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Encode(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Encode");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Encode");
     return;
   }
 
   if (argc == 1) {
-    std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-    if (ValueIsNull(pThis, argOne.get())) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+    if (ValueIsNull(info.GetIsolate(), argOne)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-
-    WideString encoded = EncodeURL(ValueToUTF8String(argOne.get()));
-    args.GetReturnValue()->SetString(
-        FX_UTF8Encode(encoded.AsStringView()).AsStringView());
+    WideString encoded =
+        EncodeURL(ValueToUTF8String(info.GetIsolate(), argOne));
+    auto result = FX_UTF8Encode(encoded.AsStringView());
+    info.GetReturnValue().Set(
+        fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsToEncode = ValueToUTF8String(argOne.get());
-  ByteString bsIdentify = ValueToUTF8String(argTwo.get());
+  ByteString bsToEncode = ValueToUTF8String(info.GetIsolate(), argOne);
+  ByteString bsIdentify = ValueToUTF8String(info.GetIsolate(), argTwo);
   WideString encoded;
   if (bsIdentify.EqualNoCase("html"))
     encoded = EncodeHTML(bsToEncode);
@@ -3649,42 +3447,43 @@
   else
     encoded = EncodeURL(bsToEncode);
 
-  args.GetReturnValue()->SetString(
-      FX_UTF8Encode(encoded.AsStringView()).AsStringView());
+  auto result = FX_UTF8Encode(encoded.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Format(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Format(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() < 2) {
-    pContext->ThrowParamCountMismatchException(L"Format");
+  if (info.Length() < 2) {
+    pContext->ThrowParamCountMismatchException("Format");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  ByteString bsPattern = ValueToUTF8String(argOne.get());
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  ByteString bsPattern = ValueToUTF8String(info.GetIsolate(), argOne);
 
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  ByteString bsValue = ValueToUTF8String(argTwo.get());
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  ByteString bsValue = ValueToUTF8String(info.GetIsolate(), argTwo);
 
   CXFA_Document* pDoc = pContext->GetDocument();
   CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
   CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
-  LocaleIface* pLocale = pThisNode->GetLocale();
+  GCedLocaleIface* pLocale = pThisNode->GetLocale();
   WideString wsPattern = WideString::FromUTF8(bsPattern.AsStringView());
   WideString wsValue = WideString::FromUTF8(bsValue.AsStringView());
   bool bPatternIsString;
-  uint32_t dwPatternType;
+  CXFA_LocaleValue::ValueType dwPatternType;
   std::tie(bPatternIsString, dwPatternType) =
       PatternStringType(bsPattern.AsStringView());
   if (!bPatternIsString) {
     switch (dwPatternType) {
-      case XFA_VT_DATETIME: {
+      case CXFA_LocaleValue::ValueType::kDateTime: {
         auto iTChar = wsPattern.Find(L'T');
         if (!iTChar.has_value()) {
-          args.GetReturnValue()->SetString("");
+          info.GetReturnValue().SetEmptyString();
           return;
         }
         WideString wsDatePattern(L"date{");
@@ -3695,28 +3494,28 @@
             wsPattern.Last(wsPattern.GetLength() - (iTChar.value() + 1)) + L"}";
         wsPattern = wsDatePattern + wsTimePattern;
       } break;
-      case XFA_VT_DATE: {
+      case CXFA_LocaleValue::ValueType::kDate: {
         wsPattern = L"date{" + wsPattern + L"}";
       } break;
-      case XFA_VT_TIME: {
+      case CXFA_LocaleValue::ValueType::kTime: {
         wsPattern = L"time{" + wsPattern + L"}";
       } break;
-      case XFA_VT_TEXT: {
+      case CXFA_LocaleValue::ValueType::kText: {
         wsPattern = L"text{" + wsPattern + L"}";
       } break;
-      case XFA_VT_FLOAT: {
+      case CXFA_LocaleValue::ValueType::kFloat: {
         wsPattern = L"num{" + wsPattern + L"}";
       } break;
       default: {
         WideString wsTestPattern = L"num{" + wsPattern + L"}";
-        CXFA_LocaleValue tempLocaleValue(XFA_VT_FLOAT, wsValue, wsTestPattern,
-                                         pLocale, pMgr);
+        CXFA_LocaleValue tempLocaleValue(CXFA_LocaleValue::ValueType::kFloat,
+                                         wsValue, wsTestPattern, pLocale, pMgr);
         if (tempLocaleValue.IsValid()) {
           wsPattern = std::move(wsTestPattern);
-          dwPatternType = XFA_VT_FLOAT;
+          dwPatternType = CXFA_LocaleValue::ValueType::kFloat;
         } else {
           wsPattern = L"text{" + wsPattern + L"}";
-          dwPatternType = XFA_VT_TEXT;
+          dwPatternType = CXFA_LocaleValue::ValueType::kText;
         }
       } break;
     }
@@ -3725,73 +3524,74 @@
                                pMgr);
   WideString wsRet;
   if (!localeValue.FormatPatterns(wsRet, wsPattern, pLocale,
-                                  XFA_VALUEPICTURE_Display)) {
-    args.GetReturnValue()->SetString("");
+                                  XFA_ValuePicture::kDisplay)) {
+    info.GetReturnValue().SetEmptyString();
     return;
   }
-
-  args.GetReturnValue()->SetString(wsRet.ToUTF8().AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), wsRet.ToUTF8().AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Left(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Left");
+void CFXJSE_FormCalcContext::Left(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Left");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if ((ValueIsNull(pThis, argOne.get())) ||
-      (ValueIsNull(pThis, argTwo.get()))) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  if ((ValueIsNull(info.GetIsolate(), argOne)) ||
+      (ValueIsNull(info.GetIsolate(), argTwo))) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsSource = ValueToUTF8String(argOne.get());
-  int32_t count = std::max(0, ValueToInteger(pThis, argTwo.get()));
-  args.GetReturnValue()->SetString(bsSource.First(count).AsStringView());
+  ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne);
+  int32_t count = std::max(0, ValueToInteger(info.GetIsolate(), argTwo));
+  info.GetReturnValue().Set(fxv8::NewStringHelper(
+      info.GetIsolate(), bsSource.First(count).AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Len(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Len");
+void CFXJSE_FormCalcContext::Len(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Len");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsSource = ValueToUTF8String(argOne.get());
-  args.GetReturnValue()->SetInteger(bsSource.GetLength());
+  ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne);
+  info.GetReturnValue().Set(static_cast<int>(bsSource.GetLength()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Lower(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Lower(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Lower");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Lower");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  CFX_WideTextBuf szLowBuf;
-  ByteString bsArg = ValueToUTF8String(argOne.get());
+  WideTextBuffer szLowBuf;
+  ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne);
   WideString wsArg = WideString::FromUTF8(bsArg.AsStringView());
   for (wchar_t ch : wsArg) {
     if ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0xC0 && ch <= 0xDE))
@@ -3800,78 +3600,79 @@
       ch += 1;
     szLowBuf.AppendChar(ch);
   }
-  szLowBuf.AppendChar(0);
-
-  args.GetReturnValue()->SetString(
-      FX_UTF8Encode(szLowBuf.AsStringView()).AsStringView());
+  auto result = FX_UTF8Encode(szLowBuf.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Ltrim(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Ltrim");
+void CFXJSE_FormCalcContext::Ltrim(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Ltrim");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsSource = ValueToUTF8String(argOne.get());
+  ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne);
   bsSource.TrimLeft();
-  args.GetReturnValue()->SetString(bsSource.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsSource.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Parse(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Parse(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 2) {
-    pContext->ThrowParamCountMismatchException(L"Parse");
+  if (info.Length() != 2) {
+    pContext->ThrowParamCountMismatchException("Parse");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if (ValueIsNull(pThis, argTwo.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  if (ValueIsNull(info.GetIsolate(), argTwo)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsPattern = ValueToUTF8String(argOne.get());
-  ByteString bsValue = ValueToUTF8String(argTwo.get());
+  ByteString bsPattern = ValueToUTF8String(info.GetIsolate(), argOne);
+  ByteString bsValue = ValueToUTF8String(info.GetIsolate(), argTwo);
   CXFA_Document* pDoc = pContext->GetDocument();
   CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
   CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
-  LocaleIface* pLocale = pThisNode->GetLocale();
+  GCedLocaleIface* pLocale = pThisNode->GetLocale();
   WideString wsPattern = WideString::FromUTF8(bsPattern.AsStringView());
   WideString wsValue = WideString::FromUTF8(bsValue.AsStringView());
   bool bPatternIsString;
-  uint32_t dwPatternType;
+  CXFA_LocaleValue::ValueType dwPatternType;
   std::tie(bPatternIsString, dwPatternType) =
       PatternStringType(bsPattern.AsStringView());
   if (bPatternIsString) {
     CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
                                  pMgr);
     if (!localeValue.IsValid()) {
-      args.GetReturnValue()->SetString("");
+      info.GetReturnValue().SetEmptyString();
       return;
     }
-    args.GetReturnValue()->SetString(
-        localeValue.GetValue().ToUTF8().AsStringView());
+    auto result = localeValue.GetValue().ToUTF8();
+    info.GetReturnValue().Set(
+        fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
     return;
   }
 
   switch (dwPatternType) {
-    case XFA_VT_DATETIME: {
+    case CXFA_LocaleValue::ValueType::kDateTime: {
       auto iTChar = wsPattern.Find(L'T');
       if (!iTChar.has_value()) {
-        args.GetReturnValue()->SetString("");
+        info.GetReturnValue().SetEmptyString();
         return;
       }
       WideString wsDatePattern(L"date{" + wsPattern.First(iTChar.value()) +
@@ -3883,243 +3684,228 @@
       CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
                                    pMgr);
       if (!localeValue.IsValid()) {
-        args.GetReturnValue()->SetString("");
+        info.GetReturnValue().SetEmptyString();
         return;
       }
-      args.GetReturnValue()->SetString(
-          localeValue.GetValue().ToUTF8().AsStringView());
+      auto result = localeValue.GetValue().ToUTF8();
+      info.GetReturnValue().Set(
+          fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
       return;
     }
-    case XFA_VT_DATE: {
+    case CXFA_LocaleValue::ValueType::kDate: {
       wsPattern = L"date{" + wsPattern + L"}";
       CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
                                    pMgr);
       if (!localeValue.IsValid()) {
-        args.GetReturnValue()->SetString("");
+        info.GetReturnValue().SetEmptyString();
         return;
       }
-      args.GetReturnValue()->SetString(
-          localeValue.GetValue().ToUTF8().AsStringView());
+      auto result = localeValue.GetValue().ToUTF8();
+      info.GetReturnValue().Set(
+          fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
       return;
     }
-    case XFA_VT_TIME: {
+    case CXFA_LocaleValue::ValueType::kTime: {
       wsPattern = L"time{" + wsPattern + L"}";
       CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
                                    pMgr);
       if (!localeValue.IsValid()) {
-        args.GetReturnValue()->SetString("");
+        info.GetReturnValue().SetEmptyString();
         return;
       }
-      args.GetReturnValue()->SetString(
-          localeValue.GetValue().ToUTF8().AsStringView());
+      auto result = localeValue.GetValue().ToUTF8();
+      info.GetReturnValue().Set(
+          fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
       return;
     }
-    case XFA_VT_TEXT: {
+    case CXFA_LocaleValue::ValueType::kText: {
       wsPattern = L"text{" + wsPattern + L"}";
-      CXFA_LocaleValue localeValue(XFA_VT_TEXT, wsValue, wsPattern, pLocale,
-                                   pMgr);
+      CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kText, wsValue,
+                                   wsPattern, pLocale, pMgr);
       if (!localeValue.IsValid()) {
-        args.GetReturnValue()->SetString("");
+        info.GetReturnValue().SetEmptyString();
         return;
       }
-      args.GetReturnValue()->SetString(
-          localeValue.GetValue().ToUTF8().AsStringView());
+      auto result = localeValue.GetValue().ToUTF8();
+      info.GetReturnValue().Set(
+          fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
       return;
     }
-    case XFA_VT_FLOAT: {
+    case CXFA_LocaleValue::ValueType::kFloat: {
       wsPattern = L"num{" + wsPattern + L"}";
-      CXFA_LocaleValue localeValue(XFA_VT_FLOAT, wsValue, wsPattern, pLocale,
-                                   pMgr);
+      CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kFloat, wsValue,
+                                   wsPattern, pLocale, pMgr);
       if (!localeValue.IsValid()) {
-        args.GetReturnValue()->SetString("");
+        info.GetReturnValue().SetEmptyString();
         return;
       }
-      args.GetReturnValue()->SetDouble(localeValue.GetDoubleNum());
+      info.GetReturnValue().Set(localeValue.GetDoubleNum());
       return;
     }
     default: {
       {
         WideString wsTestPattern = L"num{" + wsPattern + L"}";
-        CXFA_LocaleValue localeValue(XFA_VT_FLOAT, wsValue, wsTestPattern,
-                                     pLocale, pMgr);
+        CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kFloat,
+                                     wsValue, wsTestPattern, pLocale, pMgr);
         if (localeValue.IsValid()) {
-          args.GetReturnValue()->SetDouble(localeValue.GetDoubleNum());
+          info.GetReturnValue().Set(localeValue.GetDoubleNum());
           return;
         }
       }
 
       {
         WideString wsTestPattern = L"text{" + wsPattern + L"}";
-        CXFA_LocaleValue localeValue(XFA_VT_TEXT, wsValue, wsTestPattern,
-                                     pLocale, pMgr);
+        CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kText,
+                                     wsValue, wsTestPattern, pLocale, pMgr);
         if (localeValue.IsValid()) {
-          args.GetReturnValue()->SetString(
-              localeValue.GetValue().ToUTF8().AsStringView());
+          auto result = localeValue.GetValue().ToUTF8();
+          info.GetReturnValue().Set(
+              fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
           return;
         }
       }
-      args.GetReturnValue()->SetString("");
+      info.GetReturnValue().SetEmptyString();
       return;
     }
   }
 }
 
 // static
-void CFXJSE_FormCalcContext::Replace(CFXJSE_Value* pThis,
-                                     ByteStringView bsFuncName,
-                                     CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Replace(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 2 || argc > 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Replace");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Replace");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
   ByteString bsOne;
   ByteString bsTwo;
-  if (!ValueIsNull(pThis, argOne.get()) && !ValueIsNull(pThis, argTwo.get())) {
-    bsOne = ValueToUTF8String(argOne.get());
-    bsTwo = ValueToUTF8String(argTwo.get());
+  if (!ValueIsNull(info.GetIsolate(), argOne) &&
+      !ValueIsNull(info.GetIsolate(), argTwo)) {
+    bsOne = ValueToUTF8String(info.GetIsolate(), argOne);
+    bsTwo = ValueToUTF8String(info.GetIsolate(), argTwo);
   }
 
   ByteString bsThree;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-    bsThree = ValueToUTF8String(argThree.get());
+    v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+    bsThree = ValueToUTF8String(info.GetIsolate(), argThree);
   }
 
-  size_t iFindLen = bsTwo.GetLength();
-  std::ostringstream szResult;
-  size_t iFindIndex = 0;
-  for (size_t u = 0; u < bsOne.GetLength(); ++u) {
-    char ch = static_cast<char>(bsOne[u]);
-    if (ch != static_cast<char>(bsTwo[iFindIndex])) {
-      szResult << ch;
-      continue;
-    }
-
-    size_t iTemp = u + 1;
-    ++iFindIndex;
-    while (iFindIndex < iFindLen) {
-      uint8_t chTemp = bsOne[iTemp];
-      if (chTemp != bsTwo[iFindIndex]) {
-        iFindIndex = 0;
-        break;
-      }
-
-      ++iTemp;
-      ++iFindIndex;
-    }
-    if (iFindIndex == iFindLen) {
-      szResult << bsThree;
-      u += iFindLen - 1;
-      iFindIndex = 0;
-    } else {
-      szResult << ch;
-    }
-  }
-  szResult << '\0';
-  args.GetReturnValue()->SetString(ByteStringView(szResult.str().c_str()));
+  bsOne.Replace(bsTwo.AsStringView(), bsThree.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsOne.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Right(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Right");
+void CFXJSE_FormCalcContext::Right(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Right");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if ((ValueIsNull(pThis, argOne.get())) ||
-      (ValueIsNull(pThis, argTwo.get()))) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  if ((ValueIsNull(info.GetIsolate(), argOne)) ||
+      (ValueIsNull(info.GetIsolate(), argTwo))) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsSource = ValueToUTF8String(argOne.get());
-  int32_t count = std::max(0, ValueToInteger(pThis, argTwo.get()));
-  args.GetReturnValue()->SetString(bsSource.Last(count).AsStringView());
+  ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne);
+  int32_t count = std::max(0, ValueToInteger(info.GetIsolate(), argTwo));
+  info.GetReturnValue().Set(fxv8::NewStringHelper(
+      info.GetIsolate(), bsSource.Last(count).AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Rtrim(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Rtrim");
+void CFXJSE_FormCalcContext::Rtrim(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Rtrim");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsSource = ValueToUTF8String(argOne.get());
+  ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne);
   bsSource.TrimRight();
-  args.GetReturnValue()->SetString(bsSource.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsSource.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Space(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Space");
+void CFXJSE_FormCalcContext::Space(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Space");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  int32_t count = std::max(0, ValueToInteger(pThis, argOne.get()));
-  std::ostringstream spaceString;
-  int32_t index = 0;
-  while (index < count) {
-    spaceString << ' ';
-    index++;
+  int count = std::max(0, ValueToInteger(info.GetIsolate(), argOne));
+  if (count > kMaxCharCount) {
+    ToFormCalcContext(pThis)->ThrowException("String too long.");
+    return;
   }
-  spaceString << '\0';
-  args.GetReturnValue()->SetString(ByteStringView(spaceString.str().c_str()));
+  DataVector<char> space_string(count, ' ');
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), ByteStringView(space_string)));
 }
 
 // static
-void CFXJSE_FormCalcContext::Str(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Str(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Str");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Str");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> numberValue = GetSimpleValue(pThis, args, 0);
-  if (numberValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> numberValue = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(numberValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  float fNumber = ValueToFloat(pThis, numberValue.get());
+  float fNumber = ValueToFloat(info.GetIsolate(), numberValue);
 
-  int32_t iWidth = 10;
+  constexpr int32_t kDefaultWidth = 10;
+  int32_t iWidth = kDefaultWidth;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> widthValue = GetSimpleValue(pThis, args, 1);
-    iWidth = static_cast<int32_t>(ValueToFloat(pThis, widthValue.get()));
+    v8::Local<v8::Value> widthValue = GetSimpleValue(info, 1);
+    iWidth = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), widthValue));
+    if (iWidth > kMaxCharCount) {
+      ToFormCalcContext(pThis)->ThrowException("String too long.");
+      return;
+    }
   }
 
-  int32_t iPrecision = 0;
+  constexpr int32_t kDefaultPrecision = 0;
+  int32_t iPrecision = kDefaultPrecision;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> precisionValue =
-        GetSimpleValue(pThis, args, 2);
-    iPrecision = std::max(
-        0, static_cast<int32_t>(ValueToFloat(pThis, precisionValue.get())));
+    constexpr int32_t kMaxPrecision = 15;
+    v8::Local<v8::Value> precision_value = GetSimpleValue(info, 2);
+    iPrecision = std::max(0, static_cast<int32_t>(ValueToFloat(
+                                 info.GetIsolate(), precision_value)));
+    iPrecision = std::min(iPrecision, kMaxPrecision);
   }
 
   ByteString bsFormat = "%";
@@ -4140,34 +3926,31 @@
     ++u;
   }
 
-  std::ostringstream resultBuf;
   if (u > iWidth || (iPrecision + u) >= iWidth) {
-    int32_t i = 0;
-    while (i < iWidth) {
-      resultBuf << '*';
-      ++i;
-    }
-    resultBuf << '\0';
-    args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str()));
+    DataVector<char> stars(std::max(iWidth, 0), '*');
+    info.GetReturnValue().Set(
+        fxv8::NewStringHelper(info.GetIsolate(), ByteStringView(stars)));
     return;
   }
 
+  ByteString resultBuf;
   if (u == iLength) {
     if (iLength > iWidth) {
       int32_t i = 0;
       while (i < iWidth) {
-        resultBuf << '*';
+        resultBuf += '*';
         ++i;
       }
     } else {
       int32_t i = 0;
       while (i < iWidth - iLength) {
-        resultBuf << ' ';
+        resultBuf += ' ';
         ++i;
       }
-      resultBuf << pData;
+      resultBuf += pData;
     }
-    args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str()));
+    info.GetReturnValue().Set(
+        fxv8::NewStringHelper(info.GetIsolate(), resultBuf.AsStringView()));
     return;
   }
 
@@ -4177,16 +3960,16 @@
 
   int32_t i = 0;
   while (i < iLeavingSpace) {
-    resultBuf << ' ';
+    resultBuf += ' ';
     ++i;
   }
   i = 0;
   while (i < u) {
-    resultBuf << pData[i];
+    resultBuf += pData[i];
     ++i;
   }
   if (iPrecision != 0)
-    resultBuf << '.';
+    resultBuf += '.';
 
   u++;
   i = 0;
@@ -4194,221 +3977,213 @@
     if (i >= iPrecision)
       break;
 
-    resultBuf << pData[u];
+    resultBuf += pData[u];
     ++i;
     ++u;
   }
   while (i < iPrecision) {
-    resultBuf << '0';
+    resultBuf += '0';
     ++i;
   }
-  resultBuf << '\0';
-  args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str()));
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), resultBuf.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Stuff(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Stuff(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 3 || argc > 4) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Stuff");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Stuff");
     return;
   }
 
-  ByteString bsSource;
-  ByteString bsInsert;
-  int32_t iLength = 0;
-  int32_t iStart = 0;
+  v8::Local<v8::Value> sourceValue = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> startValue = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> deleteValue = GetSimpleValue(info, 2);
+  if (fxv8::IsNull(sourceValue) || fxv8::IsNull(startValue) ||
+      fxv8::IsNull(deleteValue)) {
+    info.GetReturnValue().SetNull();
+    return;
+  }
+
+  int32_t iStart = 1;  // one-based character indexing.
   int32_t iDelete = 0;
-  std::unique_ptr<CFXJSE_Value> sourceValue = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> startValue = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> deleteValue = GetSimpleValue(pThis, args, 2);
-  if (!sourceValue->IsNull() && !startValue->IsNull() &&
-      !deleteValue->IsNull()) {
-    bsSource = ValueToUTF8String(sourceValue.get());
-    iLength = bsSource.GetLength();
-    iStart = pdfium::clamp(
-        static_cast<int32_t>(ValueToFloat(pThis, startValue.get())), 1,
+  ByteString bsSource = ValueToUTF8String(info.GetIsolate(), sourceValue);
+  int32_t iLength = pdfium::base::checked_cast<int32_t>(bsSource.GetLength());
+  if (iLength) {
+    iStart = std::clamp(
+        static_cast<int32_t>(ValueToFloat(info.GetIsolate(), startValue)), 1,
         iLength);
-    iDelete = std::max(
-        0, static_cast<int32_t>(ValueToFloat(pThis, deleteValue.get())));
+    iDelete = std::clamp(
+        static_cast<int32_t>(ValueToFloat(info.GetIsolate(), deleteValue)), 0,
+        iLength - iStart + 1);
   }
 
+  ByteString bsInsert;
   if (argc > 3) {
-    std::unique_ptr<CFXJSE_Value> insertValue = GetSimpleValue(pThis, args, 3);
-    bsInsert = ValueToUTF8String(insertValue.get());
+    v8::Local<v8::Value> insertValue = GetSimpleValue(info, 3);
+    bsInsert = ValueToUTF8String(info.GetIsolate(), insertValue);
   }
 
-  --iStart;
-  std::ostringstream szResult;
-  int32_t i = 0;
-  while (i < iStart) {
-    szResult << static_cast<char>(bsSource[i]);
-    ++i;
-  }
-  szResult << bsInsert.AsStringView();
-  i = iStart + iDelete;
-  while (i < iLength) {
-    szResult << static_cast<char>(bsSource[i]);
-    ++i;
-  }
-  szResult << '\0';
-  args.GetReturnValue()->SetString(ByteStringView(szResult.str().c_str()));
+  --iStart;  // now zero-based.
+  ByteString bsResult = {bsSource.AsStringView().First(iStart),
+                         bsInsert.AsStringView(),
+                         bsSource.AsStringView().Substr(iStart + iDelete)};
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsResult.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Substr(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args) {
-  if (args.GetLength() != 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Substr");
+void CFXJSE_FormCalcContext::Substr(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 3) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Substr");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> string_value = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> start_value = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> end_value = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, string_value.get()) ||
-      ValueIsNull(pThis, start_value.get()) ||
-      ValueIsNull(pThis, end_value.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> string_value = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> start_value = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> end_value = GetSimpleValue(info, 2);
+  if (ValueIsNull(info.GetIsolate(), string_value) ||
+      ValueIsNull(info.GetIsolate(), start_value) ||
+      ValueIsNull(info.GetIsolate(), end_value)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsSource = ValueToUTF8String(string_value.get());
+  ByteString bsSource = ValueToUTF8String(info.GetIsolate(), string_value);
   size_t iLength = bsSource.GetLength();
   if (iLength == 0) {
-    args.GetReturnValue()->SetString("");
+    info.GetReturnValue().SetEmptyString();
     return;
   }
 
   // |start_value| is 1-based. Assume first character if |start_value| is less
   // than 1, per spec. Subtract 1 since |iStart| is 0-based.
-  size_t iStart = std::max(ValueToInteger(pThis, start_value.get()), 1) - 1;
+  size_t iStart =
+      std::max(ValueToInteger(info.GetIsolate(), start_value), 1) - 1;
   if (iStart >= iLength) {
-    args.GetReturnValue()->SetString("");
+    info.GetReturnValue().SetEmptyString();
     return;
   }
 
   // Negative values are treated as 0. Can't clamp() due to sign mismatches.
-  size_t iCount = std::max(ValueToInteger(pThis, end_value.get()), 0);
+  size_t iCount = std::max(ValueToInteger(info.GetIsolate(), end_value), 0);
   iCount = std::min(iCount, iLength - iStart);
-  args.GetReturnValue()->SetString(
-      bsSource.Substr(iStart, iCount).AsStringView());
+  info.GetReturnValue().Set(fxv8::NewStringHelper(
+      info.GetIsolate(), bsSource.Substr(iStart, iCount).AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Uuid(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Uuid(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 0 || argc > 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Uuid");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Uuid");
     return;
   }
 
   int32_t iNum = 0;
   if (argc > 0) {
-    std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-    iNum = static_cast<int32_t>(ValueToFloat(pThis, argOne.get()));
+    v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+    iNum = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), argOne));
   }
-  args.GetReturnValue()->SetString(GUIDString(!!iNum).AsStringView());
+  info.GetReturnValue().Set(fxv8::NewStringHelper(
+      info.GetIsolate(), GUIDString(!!iNum).AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Upper(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Upper(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Upper");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Upper");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  CFX_WideTextBuf upperStringBuf;
-  ByteString bsArg = ValueToUTF8String(argOne.get());
+  ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne);
   WideString wsArg = WideString::FromUTF8(bsArg.AsStringView());
-  const wchar_t* pData = wsArg.c_str();
-  size_t i = 0;
-  while (i < wsArg.GetLength()) {
-    int32_t ch = pData[i];
+  WideString upperStringBuf;
+  upperStringBuf.Reserve(wsArg.GetLength());
+  for (wchar_t ch : wsArg) {
     if ((ch >= 0x61 && ch <= 0x7A) || (ch >= 0xE0 && ch <= 0xFE))
       ch -= 32;
     else if (ch == 0x101 || ch == 0x103 || ch == 0x105)
       ch -= 1;
 
-    upperStringBuf.AppendChar(ch);
-    ++i;
+    upperStringBuf += ch;
   }
-  upperStringBuf.AppendChar(0);
-
-  args.GetReturnValue()->SetString(
-      FX_UTF8Encode(upperStringBuf.AsStringView()).AsStringView());
+  info.GetReturnValue().Set(fxv8::NewStringHelper(
+      info.GetIsolate(),
+      FX_UTF8Encode(upperStringBuf.AsStringView()).AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::WordNum(CFXJSE_Value* pThis,
-                                     ByteStringView bsFuncName,
-                                     CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::WordNum(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"WordNum");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("WordNum");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> numberValue = GetSimpleValue(pThis, args, 0);
-  if (numberValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> numberValue = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(numberValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  float fNumber = ValueToFloat(pThis, numberValue.get());
+  float fNumber = ValueToFloat(info.GetIsolate(), numberValue);
 
   int32_t iIdentifier = 0;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> identifierValue =
-        GetSimpleValue(pThis, args, 1);
-    if (identifierValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> identifierValue = GetSimpleValue(info, 1);
+    if (fxv8::IsNull(identifierValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
     iIdentifier =
-        static_cast<int32_t>(ValueToFloat(pThis, identifierValue.get()));
+        static_cast<int32_t>(ValueToFloat(info.GetIsolate(), identifierValue));
   }
 
   ByteString bsLocale;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
-    if (localeValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
+    if (fxv8::IsNull(localeValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(localeValue.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
   }
 
-  if (std::isnan(fNumber) || fNumber < 0.0f ||
-      fNumber > 922337203685477550.0f) {
-    args.GetReturnValue()->SetString("*");
+  if (isnan(fNumber) || fNumber < 0.0f || fNumber > 922337203685477550.0f) {
+    info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "*"));
     return;
   }
-
-  args.GetReturnValue()->SetString(
-      WordUS(ByteString::Format("%.2f", fNumber), iIdentifier).AsStringView());
+  ByteString bsFormatted = ByteString::Format("%.2f", fNumber);
+  ByteString bsWorded = WordUS(bsFormatted.AsStringView(), iIdentifier);
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsWorded.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Get(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Get(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 1) {
-    pContext->ThrowParamCountMismatchException(L"Get");
+  if (info.Length() != 1) {
+    pContext->ThrowParamCountMismatchException("Get");
     return;
   }
 
@@ -4416,31 +4191,34 @@
   if (!pDoc)
     return;
 
-  IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider();
+  CXFA_FFApp::CallbackIface* pAppProvider = pDoc->GetNotify()->GetAppProvider();
   if (!pAppProvider)
     return;
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  ByteString bsUrl = ValueToUTF8String(argOne.get());
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  ByteString bsUrl = ValueToUTF8String(info.GetIsolate(), argOne);
   RetainPtr<IFX_SeekableReadStream> pFile =
       pAppProvider->DownloadURL(WideString::FromUTF8(bsUrl.AsStringView()));
   if (!pFile)
     return;
 
-  int32_t size = pFile->GetSize();
-  std::vector<uint8_t> dataBuf(size);
-  pFile->ReadBlock(dataBuf.data(), size);
-  args.GetReturnValue()->SetString(ByteStringView(dataBuf));
+  FX_FILESIZE size = pFile->GetSize();
+  DataVector<uint8_t> dataBuf(size);
+
+  // TODO(tsepez): check return value?
+  (void)pFile->ReadBlock(dataBuf);
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), ByteStringView(dataBuf)));
 }
 
 // static
-void CFXJSE_FormCalcContext::Post(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Post(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  int32_t argc = args.GetLength();
+  int32_t argc = info.Length();
   if (argc < 2 || argc > 5) {
-    pContext->ThrowParamCountMismatchException(L"Post");
+    pContext->ThrowParamCountMismatchException("Post");
     return;
   }
 
@@ -4448,32 +4226,32 @@
   if (!pDoc)
     return;
 
-  IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider();
+  CXFA_FFApp::CallbackIface* pAppProvider = pDoc->GetNotify()->GetAppProvider();
   if (!pAppProvider)
     return;
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  ByteString bsURL = ValueToUTF8String(argOne.get());
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  ByteString bsURL = ValueToUTF8String(info.GetIsolate(), argOne);
 
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  ByteString bsData = ValueToUTF8String(argTwo.get());
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  ByteString bsData = ValueToUTF8String(info.GetIsolate(), argTwo);
 
   ByteString bsContentType;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-    bsContentType = ValueToUTF8String(argThree.get());
+    v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+    bsContentType = ValueToUTF8String(info.GetIsolate(), argThree);
   }
 
   ByteString bsEncode;
   if (argc > 3) {
-    std::unique_ptr<CFXJSE_Value> argFour = GetSimpleValue(pThis, args, 3);
-    bsEncode = ValueToUTF8String(argFour.get());
+    v8::Local<v8::Value> argFour = GetSimpleValue(info, 3);
+    bsEncode = ValueToUTF8String(info.GetIsolate(), argFour);
   }
 
   ByteString bsHeader;
   if (argc > 4) {
-    std::unique_ptr<CFXJSE_Value> argFive = GetSimpleValue(pThis, args, 4);
-    bsHeader = ValueToUTF8String(argFive.get());
+    v8::Local<v8::Value> argFive = GetSimpleValue(info, 4);
+    bsHeader = ValueToUTF8String(info.GetIsolate(), argFive);
   }
 
   WideString decodedResponse;
@@ -4486,17 +4264,18 @@
     pContext->ThrowServerDeniedException();
     return;
   }
-  args.GetReturnValue()->SetString(decodedResponse.ToUTF8().AsStringView());
+  info.GetReturnValue().Set(fxv8::NewStringHelper(
+      info.GetIsolate(), decodedResponse.ToUTF8().AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Put(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Put(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  int32_t argc = args.GetLength();
+  int32_t argc = info.Length();
   if (argc < 2 || argc > 3) {
-    pContext->ThrowParamCountMismatchException(L"Put");
+    pContext->ThrowParamCountMismatchException("Put");
     return;
   }
 
@@ -4504,22 +4283,21 @@
   if (!pDoc)
     return;
 
-  IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider();
+  CXFA_FFApp::CallbackIface* pAppProvider = pDoc->GetNotify()->GetAppProvider();
   if (!pAppProvider)
     return;
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  ByteString bsURL = ValueToUTF8String(argOne.get());
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  ByteString bsURL = ValueToUTF8String(info.GetIsolate(), argOne);
 
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  ByteString bsData = ValueToUTF8String(argTwo.get());
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  ByteString bsData = ValueToUTF8String(info.GetIsolate(), argTwo);
 
   ByteString bsEncode;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-    bsEncode = ValueToUTF8String(argThree.get());
+    v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+    bsEncode = ValueToUTF8String(info.GetIsolate(), argThree);
   }
-
   if (!pAppProvider->PutRequestURL(
           WideString::FromUTF8(bsURL.AsStringView()),
           WideString::FromUTF8(bsData.AsStringView()),
@@ -4527,869 +4305,641 @@
     pContext->ThrowServerDeniedException();
     return;
   }
-
-  args.GetReturnValue()->SetString("");
+  info.GetReturnValue().SetEmptyString();
 }
 
 // static
-void CFXJSE_FormCalcContext::assign_value_operator(CFXJSE_Value* pThis,
-                                                   ByteStringView bsFuncName,
-                                                   CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::assign_value_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Isolate* pIsolate = info.GetIsolate();
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 2) {
+  if (info.Length() != 2) {
     pContext->ThrowCompilerErrorException();
     return;
   }
-
-  std::unique_ptr<CFXJSE_Value> lValue = args.GetValue(0);
-  std::unique_ptr<CFXJSE_Value> rValue = GetSimpleValue(pThis, args, 1);
-  if (lValue->IsArray()) {
-    v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-    auto leftLengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    lValue->GetObjectProperty("length", leftLengthValue.get());
-    int32_t iLeftLength = leftLengthValue->ToInteger();
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    lValue->GetObjectPropertyByIdx(1, propertyValue.get());
-    if (propertyValue->IsNull()) {
-      for (int32_t i = 2; i < iLeftLength; i++) {
-        lValue->GetObjectPropertyByIdx(i, jsObjectValue.get());
-        if (!SetObjectDefaultValue(jsObjectValue.get(), rValue.get())) {
+  ByteStringView bsFuncName("asgn_val_op");
+  v8::Local<v8::Value> lValue = info[0];
+  v8::Local<v8::Value> rValue = GetSimpleValue(info, 1);
+  if (fxv8::IsArray(lValue)) {
+    v8::Local<v8::Array> arr = lValue.As<v8::Array>();
+    uint32_t iLeftLength = fxv8::GetArrayLengthHelper(arr);
+    v8::Local<v8::Value> propertyValue =
+        fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1);
+    for (uint32_t i = 2; i < iLeftLength; i++) {
+      v8::Local<v8::Value> jsValue =
+          fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, i);
+      if (!fxv8::IsObject(jsValue)) {
+        pContext->ThrowNoDefaultPropertyException(bsFuncName);
+        return;
+      }
+      v8::Local<v8::Object> jsObjectValue = jsValue.As<v8::Object>();
+      if (fxv8::IsNull(propertyValue)) {
+        if (!SetObjectDefaultValue(pIsolate, jsObjectValue, rValue)) {
           pContext->ThrowNoDefaultPropertyException(bsFuncName);
           return;
         }
-      }
-    } else {
-      for (int32_t i = 2; i < iLeftLength; i++) {
-        lValue->GetObjectPropertyByIdx(i, jsObjectValue.get());
-        jsObjectValue->SetObjectProperty(
-            propertyValue->ToString().AsStringView(), rValue.get());
+      } else {
+        fxv8::ReentrantPutObjectPropertyHelper(
+            pIsolate, jsObjectValue,
+            fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue)
+                .AsStringView(),
+            rValue);
       }
     }
-  } else if (lValue->IsObject()) {
-    if (!SetObjectDefaultValue(lValue.get(), rValue.get())) {
+  } else if (fxv8::IsObject(lValue)) {
+    if (!SetObjectDefaultValue(pIsolate, lValue.As<v8::Object>(), rValue)) {
       pContext->ThrowNoDefaultPropertyException(bsFuncName);
       return;
     }
   }
-  args.GetReturnValue()->Assign(rValue.get());
+  info.GetReturnValue().Set(rValue);
 }
 
 // static
-void CFXJSE_FormCalcContext::logical_or_operator(CFXJSE_Value* pThis,
-                                                 ByteStringView bsFuncName,
-                                                 CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::logical_or_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() && argSecond->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  float first = ValueToFloat(pThis, argFirst.get());
-  float second = ValueToFloat(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first || second) ? 1 : 0);
+  float first = ValueToFloat(info.GetIsolate(), argFirst);
+  float second = ValueToFloat(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(static_cast<int>(first || second));
 }
 
 // static
-void CFXJSE_FormCalcContext::logical_and_operator(CFXJSE_Value* pThis,
-                                                  ByteStringView bsFuncName,
-                                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::logical_and_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() && argSecond->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  float first = ValueToFloat(pThis, argFirst.get());
-  float second = ValueToFloat(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first && second) ? 1 : 0);
+  float first = ValueToFloat(info.GetIsolate(), argFirst);
+  float second = ValueToFloat(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(static_cast<int>(first && second));
 }
 
 // static
-void CFXJSE_FormCalcContext::equality_operator(CFXJSE_Value* pThis,
-                                               ByteStringView bsFuncName,
-                                               CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::equality_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  if (fm_ref_equal(pThis, args)) {
-    args.GetReturnValue()->SetInteger(1);
+  if (fm_ref_equal(pThis, info)) {
+    info.GetReturnValue().Set(1);
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(
-        (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0);
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().Set(
+        static_cast<int>(fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)));
     return;
   }
 
-  if (argFirst->IsString() && argSecond->IsString()) {
-    args.GetReturnValue()->SetInteger(argFirst->ToString() ==
-                                      argSecond->ToString());
+  if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
+    info.GetReturnValue().Set(static_cast<int>(
+        fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst) ==
+        fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond)));
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first == second) ? 1 : 0);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(static_cast<int>(first == second));
 }
 
 // static
-void CFXJSE_FormCalcContext::notequality_operator(CFXJSE_Value* pThis,
-                                                  ByteStringView bsFuncName,
-                                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::notequality_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  if (fm_ref_equal(pThis, args)) {
-    args.GetReturnValue()->SetInteger(0);
+  if (fm_ref_equal(pThis, info)) {
+    info.GetReturnValue().Set(0);
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(
-        (argFirst->IsNull() && argSecond->IsNull()) ? 0 : 1);
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().Set(
+        static_cast<int>(!fxv8::IsNull(argFirst) || !fxv8::IsNull(argSecond)));
     return;
   }
 
-  if (argFirst->IsString() && argSecond->IsString()) {
-    args.GetReturnValue()->SetInteger(argFirst->ToString() !=
-                                      argSecond->ToString());
+  if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
+    info.GetReturnValue().Set(static_cast<int>(
+        fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst) !=
+        fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond)));
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger(first != second);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(static_cast<int>(first != second));
 }
 
 // static
-bool CFXJSE_FormCalcContext::fm_ref_equal(CFXJSE_Value* pThis,
-                                          CFXJSE_Arguments& args) {
-  std::unique_ptr<CFXJSE_Value> argFirst = args.GetValue(0);
-  std::unique_ptr<CFXJSE_Value> argSecond = args.GetValue(1);
-  if (!argFirst->IsArray() || !argSecond->IsArray())
+bool CFXJSE_FormCalcContext::fm_ref_equal(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Local<v8::Value> argFirst = info[0];
+  v8::Local<v8::Value> argSecond = info[1];
+  if (!fxv8::IsArray(argFirst) || !fxv8::IsArray(argSecond))
     return false;
 
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  auto firstFlagValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  auto secondFlagValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argFirst->GetObjectPropertyByIdx(0, firstFlagValue.get());
-  argSecond->GetObjectPropertyByIdx(0, secondFlagValue.get());
-  if (firstFlagValue->ToInteger() != 3 || secondFlagValue->ToInteger() != 3)
+  v8::Local<v8::Array> firstArr = argFirst.As<v8::Array>();
+  v8::Local<v8::Array> secondArr = argSecond.As<v8::Array>();
+  v8::Local<v8::Value> firstFlag =
+      fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), firstArr, 0);
+  v8::Local<v8::Value> secondFlag =
+      fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), secondArr, 0);
+  if (fxv8::ReentrantToInt32Helper(info.GetIsolate(), firstFlag) != 3 ||
+      fxv8::ReentrantToInt32Helper(info.GetIsolate(), secondFlag) != 3) {
+    return false;
+  }
+
+  v8::Local<v8::Value> firstValue =
+      fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), firstArr, 2);
+  v8::Local<v8::Value> secondValue =
+      fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), secondArr, 2);
+
+  if (fxv8::IsNull(firstValue) || fxv8::IsNull(secondValue))
     return false;
 
-  auto firstJSObject = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  auto secondJSObject = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argFirst->GetObjectPropertyByIdx(2, firstJSObject.get());
-  argSecond->GetObjectPropertyByIdx(2, secondJSObject.get());
-  if (firstJSObject->IsNull() || secondJSObject->IsNull())
-    return false;
-
-  return firstJSObject->ToHostObject() == secondJSObject->ToHostObject();
+  return FXJSE_RetrieveObjectBinding(firstValue) ==
+         FXJSE_RetrieveObjectBinding(secondValue);
 }
 
 // static
-void CFXJSE_FormCalcContext::less_operator(CFXJSE_Value* pThis,
-                                           ByteStringView bsFuncName,
-                                           CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::less_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(0);
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().Set(0);
     return;
   }
 
-  if (argFirst->IsString() && argSecond->IsString()) {
-    int result =
-        argFirst->ToString().Compare(argSecond->ToString().AsStringView()) < 0;
-    args.GetReturnValue()->SetInteger(result);
+  if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
+    ByteString bs1 =
+        fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst);
+    ByteString bs2 =
+        fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond);
+    info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) < 0);
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first < second) ? 1 : 0);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(static_cast<int>(first < second));
 }
 
 // static
-void CFXJSE_FormCalcContext::lessequal_operator(CFXJSE_Value* pThis,
-                                                ByteStringView bsFuncName,
-                                                CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::lessequal_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(
-        (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0);
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().Set(
+        static_cast<int>(fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)));
     return;
   }
 
-  if (argFirst->IsString() && argSecond->IsString()) {
-    int result =
-        argFirst->ToString().Compare(argSecond->ToString().AsStringView()) <= 0;
-    args.GetReturnValue()->SetInteger(result);
+  if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
+    auto bs1 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst);
+    auto bs2 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond);
+    info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) <= 0);
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first <= second) ? 1 : 0);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(static_cast<int>(first <= second));
 }
 
 // static
-void CFXJSE_FormCalcContext::greater_operator(CFXJSE_Value* pThis,
-                                              ByteStringView bsFuncName,
-                                              CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::greater_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(0);
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().Set(0);
     return;
   }
 
-  if (argFirst->IsString() && argSecond->IsString()) {
-    int result =
-        argFirst->ToString().Compare(argSecond->ToString().AsStringView()) > 0;
-    args.GetReturnValue()->SetInteger(result);
+  if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
+    auto bs1 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst);
+    auto bs2 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond);
+    info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) > 0);
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first > second) ? 1 : 0);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(static_cast<int>(first > second));
 }
 
 // static
-void CFXJSE_FormCalcContext::greaterequal_operator(CFXJSE_Value* pThis,
-                                                   ByteStringView bsFuncName,
-                                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::greaterequal_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(
-        (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0);
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().Set(
+        static_cast<int>(fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)));
     return;
   }
 
-  if (argFirst->IsString() && argSecond->IsString()) {
-    int result =
-        argFirst->ToString().Compare(argSecond->ToString().AsStringView()) >= 0;
-    args.GetReturnValue()->SetInteger(result);
+  if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
+    auto bs1 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst);
+    auto bs2 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond);
+    info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) >= 0);
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first >= second) ? 1 : 0);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(static_cast<int>(first >= second));
 }
 
 // static
-void CFXJSE_FormCalcContext::plus_operator(CFXJSE_Value* pThis,
-                                           ByteStringView bsFuncName,
-                                           CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::plus_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = args.GetValue(0);
-  std::unique_ptr<CFXJSE_Value> argSecond = args.GetValue(1);
-  if (ValueIsNull(pThis, argFirst.get()) &&
-      ValueIsNull(pThis, argSecond.get())) {
-    args.GetReturnValue()->SetNull();
+  if (ValueIsNull(info.GetIsolate(), info[0]) &&
+      ValueIsNull(info.GetIsolate(), info[1])) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetDouble(first + second);
+  const double first = ValueToDouble(info.GetIsolate(), info[0]);
+  const double second = ValueToDouble(info.GetIsolate(), info[1]);
+  info.GetReturnValue().Set(first + second);
 }
 
 // static
-void CFXJSE_FormCalcContext::minus_operator(CFXJSE_Value* pThis,
-                                            ByteStringView bsFuncName,
-                                            CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::minus_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() && argSecond->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetDouble(first - second);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(first - second);
 }
 
 // static
-void CFXJSE_FormCalcContext::multiple_operator(CFXJSE_Value* pThis,
-                                               ByteStringView bsFuncName,
-                                               CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::multiple_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() && argSecond->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetDouble(first * second);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(first * second);
 }
 
 // static
-void CFXJSE_FormCalcContext::divide_operator(CFXJSE_Value* pThis,
-                                             ByteStringView bsFuncName,
-                                             CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::divide_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 2) {
+  if (info.Length() != 2) {
     pContext->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() && argSecond->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  double second = ValueToDouble(pThis, argSecond.get());
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
   if (second == 0.0) {
     pContext->ThrowDivideByZeroException();
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  args.GetReturnValue()->SetDouble(first / second);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  info.GetReturnValue().Set(first / second);
 }
 
 // static
-void CFXJSE_FormCalcContext::positive_operator(CFXJSE_Value* pThis,
-                                               ByteStringView bsFuncName,
-                                               CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
+void CFXJSE_FormCalcContext::positive_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  args.GetReturnValue()->SetDouble(0.0 + ValueToDouble(pThis, argOne.get()));
+  info.GetReturnValue().Set(0.0 + ValueToDouble(info.GetIsolate(), argOne));
 }
 
 // static
-void CFXJSE_FormCalcContext::negative_operator(CFXJSE_Value* pThis,
-                                               ByteStringView bsFuncName,
-                                               CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
+void CFXJSE_FormCalcContext::negative_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  args.GetReturnValue()->SetDouble(0.0 - ValueToDouble(pThis, argOne.get()));
+  info.GetReturnValue().Set(0.0 - ValueToDouble(info.GetIsolate(), argOne));
 }
 
 // static
-void CFXJSE_FormCalcContext::logical_not_operator(CFXJSE_Value* pThis,
-                                                  ByteStringView bsFuncName,
-                                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
+void CFXJSE_FormCalcContext::logical_not_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  double first = ValueToDouble(pThis, argOne.get());
-  args.GetReturnValue()->SetInteger((first == 0.0) ? 1 : 0);
+  double first = ValueToDouble(info.GetIsolate(), argOne);
+  info.GetReturnValue().Set((first == 0.0) ? 1 : 0);
 }
 
 // static
-void CFXJSE_FormCalcContext::dot_accessor(CFXJSE_Value* pThis,
-                                          ByteStringView bsFuncName,
-                                          CFXJSE_Arguments& args) {
-  DotAccessorCommon(pThis, bsFuncName, args, /*bDotAccessor=*/true);
+void CFXJSE_FormCalcContext::dot_accessor(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  DotAccessorCommon(pThis, info, /*bDotAccessor=*/true);
 }
 
 // static
-void CFXJSE_FormCalcContext::dotdot_accessor(CFXJSE_Value* pThis,
-                                             ByteStringView bsFuncName,
-                                             CFXJSE_Arguments& args) {
-  DotAccessorCommon(pThis, bsFuncName, args, /*bDotAccessor=*/false);
+void CFXJSE_FormCalcContext::dotdot_accessor(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  DotAccessorCommon(pThis, info, /*bDotAccessor=*/false);
 }
 
 // static
-void CFXJSE_FormCalcContext::eval_translation(CFXJSE_Value* pThis,
-                                              ByteStringView bsFuncName,
-                                              CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::eval_translation(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 1) {
-    pContext->ThrowParamCountMismatchException(L"Eval");
+  if (info.Length() != 1) {
+    pContext->ThrowParamCountMismatchException("Eval");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  ByteString bsArg = ValueToUTF8String(argOne.get());
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne);
   if (bsArg.IsEmpty()) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  WideString wsScript = WideString::FromUTF8(bsArg.AsStringView());
-  CFX_WideTextBuf wsJavaScriptBuf;
-  if (!CFXJSE_FormCalcContext::Translate(wsScript.AsStringView(),
-                                         &wsJavaScriptBuf)) {
+  WideString wsCalcScript = WideString::FromUTF8(bsArg.AsStringView());
+  absl::optional<WideTextBuffer> wsJavaScriptBuf =
+      CFXJSE_FormCalcContext::Translate(pContext->GetDocument()->GetHeap(),
+                                        wsCalcScript.AsStringView());
+  if (!wsJavaScriptBuf.has_value()) {
     pContext->ThrowCompilerErrorException();
     return;
   }
-
-  args.GetReturnValue()->SetString(
-      FX_UTF8Encode(wsJavaScriptBuf.AsStringView()).AsStringView());
+  info.GetReturnValue().Set(fxv8::NewStringHelper(
+      info.GetIsolate(),
+      FX_UTF8Encode(wsJavaScriptBuf.value().AsStringView()).AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::is_fm_object(CFXJSE_Value* pThis,
-                                          ByteStringView bsFuncName,
-                                          CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    args.GetReturnValue()->SetBoolean(false);
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  args.GetReturnValue()->SetBoolean(argOne->IsObject());
+void CFXJSE_FormCalcContext::is_fm_object(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  const bool result = info.Length() == 1 && fxv8::IsObject(info[0]);
+  info.GetReturnValue().Set(result);
 }
 
 // static
-void CFXJSE_FormCalcContext::is_fm_array(CFXJSE_Value* pThis,
-                                         ByteStringView bsFuncName,
-                                         CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    args.GetReturnValue()->SetBoolean(false);
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  args.GetReturnValue()->SetBoolean(argOne->IsArray());
+void CFXJSE_FormCalcContext::is_fm_array(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  const bool result = info.Length() == 1 && fxv8::IsArray(info[0]);
+  info.GetReturnValue().Set(result);
 }
 
 // static
-void CFXJSE_FormCalcContext::get_fm_value(CFXJSE_Value* pThis,
-                                          ByteStringView bsFuncName,
-                                          CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::get_fm_value(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 1) {
+  if (info.Length() != 1) {
     pContext->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (argOne->IsArray()) {
-    v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argOne->GetObjectPropertyByIdx(1, propertyValue.get());
-    argOne->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      GetObjectDefaultValue(jsObjectValue.get(), args.GetReturnValue());
+  v8::Local<v8::Value> argOne = info[0];
+  if (fxv8::IsArray(argOne)) {
+    v8::Local<v8::Array> arr = argOne.As<v8::Array>();
+    v8::Local<v8::Value> propertyValue =
+        fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 1);
+    v8::Local<v8::Value> jsValue =
+        fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2);
+    if (!fxv8::IsObject(jsValue)) {
+      info.GetReturnValue().Set(fxv8::NewUndefinedHelper(info.GetIsolate()));
       return;
     }
-
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     args.GetReturnValue());
+    v8::Local<v8::Object> jsObjectValue = jsValue.As<v8::Object>();
+    if (fxv8::IsNull(propertyValue)) {
+      info.GetReturnValue().Set(
+          GetObjectDefaultValue(info.GetIsolate(), jsObjectValue));
+      return;
+    }
+    ByteString bsName =
+        fxv8::ReentrantToByteStringHelper(info.GetIsolate(), propertyValue);
+    info.GetReturnValue().Set(fxv8::ReentrantGetObjectPropertyHelper(
+        info.GetIsolate(), jsObjectValue, bsName.AsStringView()));
     return;
   }
 
-  if (argOne->IsObject()) {
-    GetObjectDefaultValue(argOne.get(), args.GetReturnValue());
+  if (fxv8::IsObject(argOne)) {
+    v8::Local<v8::Object> obj = argOne.As<v8::Object>();
+    info.GetReturnValue().Set(GetObjectDefaultValue(info.GetIsolate(), obj));
     return;
   }
 
-  args.GetReturnValue()->Assign(argOne.get());
+  info.GetReturnValue().Set(argOne);
 }
 
 // static
-void CFXJSE_FormCalcContext::get_fm_jsobj(CFXJSE_Value* pThis,
-                                          ByteStringView bsFuncName,
-                                          CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
+void CFXJSE_FormCalcContext::get_fm_jsobj(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (!argOne->IsArray()) {
-    args.GetReturnValue()->Assign(argOne.get());
+  v8::Local<v8::Value> argOne = info[0];
+  if (!fxv8::IsArray(argOne)) {
+    info.GetReturnValue().Set(argOne);
     return;
   }
 
-#ifndef NDEBUG
-  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argOne->GetObjectProperty("length", lengthValue.get());
-  ASSERT(lengthValue->ToInteger() >= 3);
-#endif
-
-  argOne->GetObjectPropertyByIdx(2, args.GetReturnValue());
+  v8::Local<v8::Array> arr = argOne.As<v8::Array>();
+  info.GetReturnValue().Set(
+      fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2));
 }
 
 // static
-void CFXJSE_FormCalcContext::fm_var_filter(CFXJSE_Value* pThis,
-                                           ByteStringView bsFuncName,
-                                           CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::fm_var_filter(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 1) {
+  if (info.Length() != 1) {
     pContext->ThrowCompilerErrorException();
     return;
   }
 
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (!argOne->IsArray()) {
-    std::unique_ptr<CFXJSE_Value> simpleValue = GetSimpleValue(pThis, args, 0);
-    args.GetReturnValue()->Assign(simpleValue.get());
+  v8::Local<v8::Value> argOne = info[0];
+  if (!fxv8::IsArray(argOne)) {
+    info.GetReturnValue().Set(GetSimpleValue(info, 0));
     return;
   }
 
-#ifndef NDEBUG
-  auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argOne->GetObjectProperty("length", lengthValue.get());
-  ASSERT(lengthValue->ToInteger() >= 3);
-#endif
-
-  auto flagsValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argOne->GetObjectPropertyByIdx(0, flagsValue.get());
-  int32_t iFlags = flagsValue->ToInteger();
+  v8::Local<v8::Array> arr = argOne.As<v8::Array>();
+  v8::Local<v8::Value> flagsValue =
+      fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 0);
+  int32_t iFlags = fxv8::ReentrantToInt32Helper(info.GetIsolate(), flagsValue);
   if (iFlags != 3 && iFlags != 4) {
-    std::unique_ptr<CFXJSE_Value> simpleValue = GetSimpleValue(pThis, args, 0);
-    args.GetReturnValue()->Assign(simpleValue.get());
+    info.GetReturnValue().Set(GetSimpleValue(info, 0));
     return;
   }
 
   if (iFlags == 4) {
-    std::vector<std::unique_ptr<CFXJSE_Value>> values;
-    for (int32_t i = 0; i < 3; i++)
-      values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-    values[0]->SetInteger(3);
-    values[1]->SetNull();
-    values[2]->SetNull();
-    args.GetReturnValue()->SetArray(values);
+    std::vector<v8::Local<v8::Value>> values(3);
+    values[0] = fxv8::NewNumberHelper(info.GetIsolate(), 3);
+    values[1] = fxv8::NewNullHelper(info.GetIsolate());
+    values[2] = fxv8::NewNullHelper(info.GetIsolate());
+    info.GetReturnValue().Set(fxv8::NewArrayHelper(info.GetIsolate(), values));
     return;
   }
 
-  auto objectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argOne->GetObjectPropertyByIdx(2, objectValue.get());
-  if (objectValue->IsNull()) {
+  v8::Local<v8::Value> objectValue =
+      fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2);
+  if (fxv8::IsNull(objectValue)) {
     pContext->ThrowCompilerErrorException();
     return;
   }
-  args.GetReturnValue()->Assign(argOne.get());
+  info.GetReturnValue().Set(argOne);
 }
 
 // static
-void CFXJSE_FormCalcContext::concat_fm_object(CFXJSE_Value* pThis,
-                                              ByteStringView bsFuncName,
-                                              CFXJSE_Arguments& args) {
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  uint32_t iLength = 0;
-  int32_t argc = args.GetLength();
-  std::vector<std::unique_ptr<CFXJSE_Value>> argValues;
-  for (int32_t i = 0; i < argc; i++) {
-    argValues.push_back(args.GetValue(i));
-    if (argValues[i]->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValues[i]->GetObjectProperty("length", lengthValue.get());
-      int32_t length = lengthValue->ToInteger();
-      iLength = iLength + ((length > 2) ? (length - 2) : 0);
-    }
-    ++iLength;
-  }
-
-  std::vector<std::unique_ptr<CFXJSE_Value>> returnValues;
-  for (int32_t i = 0; i < (int32_t)iLength; i++)
-    returnValues.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-  int32_t index = 0;
-  for (int32_t i = 0; i < argc; i++) {
-    if (argValues[i]->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValues[i]->GetObjectProperty("length", lengthValue.get());
-
-      int32_t length = lengthValue->ToInteger();
-      for (int32_t j = 2; j < length; j++) {
-        argValues[i]->GetObjectPropertyByIdx(j, returnValues[index].get());
-        index++;
+void CFXJSE_FormCalcContext::concat_fm_object(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetIsolate();
+  std::vector<v8::Local<v8::Value>> returnValues;
+  for (int i = 0; i < info.Length(); ++i) {
+    if (fxv8::IsArray(info[i])) {
+      v8::Local<v8::Array> arr = info[i].As<v8::Array>();
+      uint32_t length = fxv8::GetArrayLengthHelper(arr);
+      for (uint32_t j = 2; j < length; j++) {
+        returnValues.push_back(
+            fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, j));
       }
     }
-    returnValues[index]->Assign(argValues[i].get());
-    index++;
+    returnValues.push_back(info[i]);
   }
-  args.GetReturnValue()->SetArray(returnValues);
-}
-
-// static
-std::unique_ptr<CFXJSE_Value> CFXJSE_FormCalcContext::GetSimpleValue(
-    CFXJSE_Value* pThis,
-    CFXJSE_Arguments& args,
-    uint32_t index) {
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  ASSERT(index < (uint32_t)args.GetLength());
-
-  std::unique_ptr<CFXJSE_Value> argIndex = args.GetValue(index);
-  if (!argIndex->IsArray() && !argIndex->IsObject())
-    return argIndex;
-
-  if (argIndex->IsArray()) {
-    auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argIndex->GetObjectProperty("length", lengthValue.get());
-    int32_t iLength = lengthValue->ToInteger();
-    auto simpleValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    if (iLength < 3) {
-      simpleValue.get()->SetUndefined();
-      return simpleValue;
-    }
-
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argIndex->GetObjectPropertyByIdx(1, propertyValue.get());
-    argIndex->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      GetObjectDefaultValue(jsObjectValue.get(), simpleValue.get());
-      return simpleValue;
-    }
-
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     simpleValue.get());
-    return simpleValue;
-  }
-
-  auto defaultValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  GetObjectDefaultValue(argIndex.get(), defaultValue.get());
-  return defaultValue;
-}
-
-// static
-bool CFXJSE_FormCalcContext::ValueIsNull(CFXJSE_Value* pThis,
-                                         CFXJSE_Value* arg) {
-  if (!arg || arg->IsNull())
-    return true;
-
-  if (!arg->IsArray() && !arg->IsObject())
-    return false;
-
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  if (arg->IsArray()) {
-    int32_t iLength = hvalue_get_array_length(pThis, arg);
-    if (iLength < 3)
-      return true;
-
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    arg->GetObjectPropertyByIdx(1, propertyValue.get());
-    arg->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      auto defaultValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      GetObjectDefaultValue(jsObjectValue.get(), defaultValue.get());
-      return defaultValue->IsNull();
-    }
-
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     newPropertyValue.get());
-    return newPropertyValue->IsNull();
-  }
-
-  auto defaultValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  GetObjectDefaultValue(arg, defaultValue.get());
-  return defaultValue->IsNull();
-}
-
-// static
-int32_t CFXJSE_FormCalcContext::hvalue_get_array_length(CFXJSE_Value* pThis,
-                                                        CFXJSE_Value* arg) {
-  if (!arg || !arg->IsArray())
-    return 0;
-
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  arg->GetObjectProperty("length", lengthValue.get());
-  return lengthValue->ToInteger();
-}
-
-// static
-bool CFXJSE_FormCalcContext::simpleValueCompare(CFXJSE_Value* pThis,
-                                                CFXJSE_Value* firstValue,
-                                                CFXJSE_Value* secondValue) {
-  if (!firstValue)
-    return false;
-
-  if (firstValue->IsString()) {
-    ByteString bsFirst = ValueToUTF8String(firstValue);
-    ByteString bsSecond = ValueToUTF8String(secondValue);
-    return bsFirst == bsSecond;
-  }
-  if (firstValue->IsNumber()) {
-    float first = ValueToFloat(pThis, firstValue);
-    float second = ValueToFloat(pThis, secondValue);
-    return first == second;
-  }
-  if (firstValue->IsBoolean())
-    return firstValue->ToBoolean() == secondValue->ToBoolean();
-
-  return firstValue->IsNull() && secondValue && secondValue->IsNull();
-}
-
-// static
-std::vector<std::unique_ptr<CFXJSE_Value>> CFXJSE_FormCalcContext::unfoldArgs(
-    CFXJSE_Value* pThis,
-    CFXJSE_Arguments& args) {
-  std::vector<std::unique_ptr<CFXJSE_Value>> results;
-
-  int32_t iCount = 0;
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  int32_t argc = args.GetLength();
-  std::vector<std::unique_ptr<CFXJSE_Value>> argsValue;
-  static constexpr int kStart = 1;
-  for (int32_t i = 0; i < argc - kStart; i++) {
-    argsValue.push_back(args.GetValue(i + kStart));
-    if (argsValue[i]->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argsValue[i]->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
-      iCount += ((iLength > 2) ? (iLength - 2) : 0);
-    } else {
-      ++iCount;
-    }
-  }
-
-  for (int32_t i = 0; i < iCount; i++)
-    results.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-  int32_t index = 0;
-  for (int32_t i = 0; i < argc - kStart; i++) {
-    if (argsValue[i]->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argsValue[i]->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
-      if (iLength < 3)
-        continue;
-
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argsValue[i]->GetObjectPropertyByIdx(1, propertyValue.get());
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argsValue[i]->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          GetObjectDefaultValue(jsObjectValue.get(), results[index].get());
-          index++;
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argsValue[i]->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), results[index].get());
-          index++;
-        }
-      }
-    } else if (argsValue[i]->IsObject()) {
-      GetObjectDefaultValue(argsValue[i].get(), results[index].get());
-      index++;
-    } else {
-      results[index]->Assign(argsValue[i].get());
-      index++;
-    }
-  }
-  return results;
-}
-
-// static
-void CFXJSE_FormCalcContext::GetObjectDefaultValue(
-    CFXJSE_Value* pValue,
-    CFXJSE_Value* pDefaultValue) {
-  CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pValue));
-  if (!pNode) {
-    pDefaultValue->SetNull();
-    return;
-  }
-  pNode->JSObject()->ScriptSomDefaultValue(pDefaultValue, false,
-                                           XFA_Attribute::Unknown);
-}
-
-// static
-bool CFXJSE_FormCalcContext::SetObjectDefaultValue(CFXJSE_Value* pValue,
-                                                   CFXJSE_Value* hNewValue) {
-  CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pValue));
-  if (!pNode)
-    return false;
-
-  pNode->JSObject()->ScriptSomDefaultValue(hNewValue, true,
-                                           XFA_Attribute::Unknown);
-  return true;
+  info.GetReturnValue().Set(fxv8::NewArrayHelper(pIsolate, returnValues));
 }
 
 // static
@@ -5400,6 +4950,8 @@
   if (bIsStar)
     return ByteString(bsName, "[*]");
 
+  // `iIndexFlags` values are the same as enum class
+  // `CXFA_FMIndexExpression::AccessorIndex` values.
   if (iIndexFlags == 0)
     return ByteString(bsName);
 
@@ -5410,311 +4962,53 @@
 
   const bool bNegative = iIndexValue < 0;
   ByteString bsSomExp(bsName);
-  if (iIndexFlags == 2)
+  if (iIndexFlags == 2) {
     bsSomExp += bNegative ? "[-" : "[+";
-  else
+  } else {
+    DCHECK_EQ(iIndexFlags, 3);
     bsSomExp += bNegative ? "[" : "[-";
-  iIndexValue = bNegative ? 0 - iIndexValue : iIndexValue;
-  bsSomExp += ByteString::FormatInteger(iIndexValue);
+  }
+
+  FX_SAFE_INT32 safe_index = iIndexValue;
+  if (bNegative)
+    safe_index = -safe_index;
+  bsSomExp += ByteString::FormatInteger(safe_index.ValueOrDefault(0));
   bsSomExp += "]";
   return bsSomExp;
 }
 
-// static
-bool CFXJSE_FormCalcContext::GetObjectForName(CFXJSE_Value* pThis,
-                                              CFXJSE_Value* accessorValue,
-                                              ByteStringView bsAccessorName) {
-  CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
-  if (!pDoc)
-    return false;
+absl::optional<WideTextBuffer> CFXJSE_FormCalcContext::Translate(
+    cppgc::Heap* pHeap,
+    WideStringView wsFormcalc) {
+  if (wsFormcalc.IsEmpty())
+    return WideTextBuffer();
 
-  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
-  XFA_RESOLVENODE_RS resolveNodeRS;
-  uint32_t dwFlags = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
-                     XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent;
-  bool bRet = pScriptContext->ResolveObjects(
-      pScriptContext->GetThisObject(),
-      WideString::FromUTF8(bsAccessorName).AsStringView(), &resolveNodeRS,
-      dwFlags, nullptr);
-  if (bRet && resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    accessorValue->Assign(pScriptContext->GetOrCreateJSBindingFromMap(
-        resolveNodeRS.objects.front().Get()));
-    return true;
-  }
-  return false;
-}
-
-// static
-bool CFXJSE_FormCalcContext::ResolveObjects(CFXJSE_Value* pThis,
-                                            CFXJSE_Value* pRefValue,
-                                            ByteStringView bsSomExp,
-                                            XFA_RESOLVENODE_RS* resolveNodeRS,
-                                            bool bDotAccessor,
-                                            bool bHasNoResolveName) {
-  CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
-  if (!pDoc)
-    return false;
-
-  WideString wsSomExpression = WideString::FromUTF8(bsSomExp);
-  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
-  CXFA_Object* pNode = nullptr;
-  uint32_t dFlags = 0UL;
-  if (bDotAccessor) {
-    if (pRefValue && pRefValue->IsNull()) {
-      pNode = pScriptContext->GetThisObject();
-      dFlags = XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent;
-    } else {
-      pNode = CFXJSE_Engine::ToObject(pRefValue);
-      if (!pNode)
-        return false;
-
-      if (bHasNoResolveName) {
-        WideString wsName;
-        if (CXFA_Node* pXFANode = pNode->AsNode()) {
-          Optional<WideString> ret =
-              pXFANode->JSObject()->TryAttribute(XFA_Attribute::Name, false);
-          if (ret)
-            wsName = *ret;
-        }
-        if (wsName.IsEmpty())
-          wsName = L"#" + WideString::FromASCII(pNode->GetClassName());
-
-        wsSomExpression = wsName + wsSomExpression;
-        dFlags = XFA_RESOLVENODE_Siblings;
-      } else {
-        dFlags = (bsSomExp == "*")
-                     ? (XFA_RESOLVENODE_Children)
-                     : (XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
-                        XFA_RESOLVENODE_Properties);
-      }
-    }
-  } else {
-    pNode = CFXJSE_Engine::ToObject(pRefValue);
-    dFlags = XFA_RESOLVENODE_AnyChild;
-  }
-  return pScriptContext->ResolveObjects(pNode, wsSomExpression.AsStringView(),
-                                        resolveNodeRS, dFlags, nullptr);
-}
-
-// static
-void CFXJSE_FormCalcContext::ParseResolveResult(
-    CFXJSE_Value* pThis,
-    const XFA_RESOLVENODE_RS& resolveNodeRS,
-    CFXJSE_Value* pParentValue,
-    std::vector<std::unique_ptr<CFXJSE_Value>>* resultValues,
-    bool* bAttribute) {
-  ASSERT(bAttribute);
-
-  resultValues->clear();
-
-  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-
-  if (resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    *bAttribute = false;
-    CFXJSE_Engine* pScriptContext = pContext->GetDocument()->GetScriptContext();
-    for (auto& pObject : resolveNodeRS.objects) {
-      resultValues->push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-      resultValues->back()->Assign(
-          pScriptContext->GetOrCreateJSBindingFromMap(pObject.Get()));
-    }
-    return;
-  }
-
-  *bAttribute = true;
-  if (resolveNodeRS.script_attribute.callback &&
-      resolveNodeRS.script_attribute.eValueType == XFA_ScriptType::Object) {
-    for (auto& pObject : resolveNodeRS.objects) {
-      auto pValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      CJX_Object* jsObject = pObject->JSObject();
-      (*resolveNodeRS.script_attribute.callback)(
-          jsObject, pValue.get(), false,
-          resolveNodeRS.script_attribute.attribute);
-      resultValues->push_back(std::move(pValue));
-      *bAttribute = false;
-    }
-  }
-  if (!*bAttribute)
-    return;
-  if (!pParentValue || !pParentValue->IsObject())
-    return;
-
-  resultValues->push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-  resultValues->back()->Assign(pParentValue);
-}
-
-// static
-int32_t CFXJSE_FormCalcContext::ValueToInteger(CFXJSE_Value* pThis,
-                                               CFXJSE_Value* pValue) {
-  if (!pValue || pValue->IsEmpty())
-    return 0;
-
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  if (pValue->IsArray()) {
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    pValue->GetObjectPropertyByIdx(1, propertyValue.get());
-    pValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-      return ValueToInteger(pThis, newPropertyValue.get());
-    }
-
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     newPropertyValue.get());
-    return ValueToInteger(pThis, newPropertyValue.get());
-  }
-  if (pValue->IsObject()) {
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    GetObjectDefaultValue(pValue, newPropertyValue.get());
-    return ValueToInteger(pThis, newPropertyValue.get());
-  }
-  if (pValue->IsString())
-    return FXSYS_atoi(pValue->ToString().c_str());
-  return pValue->ToInteger();
-}
-
-// static
-float CFXJSE_FormCalcContext::ValueToFloat(CFXJSE_Value* pThis,
-                                           CFXJSE_Value* arg) {
-  if (!arg)
-    return 0.0f;
-
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  if (arg->IsArray()) {
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    arg->GetObjectPropertyByIdx(1, propertyValue.get());
-    arg->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-      return ValueToFloat(pThis, newPropertyValue.get());
-    }
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     newPropertyValue.get());
-    return ValueToFloat(pThis, newPropertyValue.get());
-  }
-  if (arg->IsObject()) {
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    GetObjectDefaultValue(arg, newPropertyValue.get());
-    return ValueToFloat(pThis, newPropertyValue.get());
-  }
-  if (arg->IsString())
-    return strtof(arg->ToString().c_str(), nullptr);
-  if (arg->IsUndefined() || arg->IsEmpty())
-    return 0.0f;
-  return arg->ToFloat();
-}
-
-// static
-double CFXJSE_FormCalcContext::ValueToDouble(CFXJSE_Value* pThis,
-                                             CFXJSE_Value* arg) {
-  if (!arg)
-    return 0;
-
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  if (arg->IsArray()) {
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    arg->GetObjectPropertyByIdx(1, propertyValue.get());
-    arg->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-      return ValueToDouble(pThis, newPropertyValue.get());
-    }
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     newPropertyValue.get());
-    return ValueToDouble(pThis, newPropertyValue.get());
-  }
-  if (arg->IsObject()) {
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    GetObjectDefaultValue(arg, newPropertyValue.get());
-    return ValueToDouble(pThis, newPropertyValue.get());
-  }
-  if (arg->IsString())
-    return strtod(arg->ToString().c_str(), nullptr);
-  if (arg->IsUndefined() || arg->IsEmpty())
-    return 0;
-  return arg->ToDouble();
-}
-
-// static.
-double CFXJSE_FormCalcContext::ExtractDouble(CFXJSE_Value* pThis,
-                                             CFXJSE_Value* src,
-                                             bool* ret) {
-  ASSERT(ret);
-  *ret = true;
-
-  if (!src)
-    return 0;
-
-  if (!src->IsArray())
-    return ValueToDouble(pThis, src);
-
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  src->GetObjectProperty("length", lengthValue.get());
-  int32_t iLength = lengthValue->ToInteger();
-  if (iLength <= 2) {
-    *ret = false;
-    return 0.0;
-  }
-
-  auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  src->GetObjectPropertyByIdx(1, propertyValue.get());
-  src->GetObjectPropertyByIdx(2, jsObjectValue.get());
-  if (propertyValue->IsNull())
-    return ValueToDouble(pThis, jsObjectValue.get());
-
-  auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                   newPropertyValue.get());
-  return ValueToDouble(pThis, newPropertyValue.get());
-}
-
-// static
-ByteString CFXJSE_FormCalcContext::ValueToUTF8String(CFXJSE_Value* arg) {
-  if (!arg || arg->IsNull() || arg->IsUndefined() || arg->IsEmpty())
-    return ByteString();
-  if (arg->IsBoolean())
-    return arg->ToBoolean() ? "1" : "0";
-  return arg->ToString();
-}
-
-// static.
-bool CFXJSE_FormCalcContext::Translate(WideStringView wsFormcalc,
-                                       CFX_WideTextBuf* wsJavascript) {
-  if (wsFormcalc.IsEmpty()) {
-    wsJavascript->Clear();
-    return true;
-  }
-
-  CXFA_FMParser parser(wsFormcalc);
-  std::unique_ptr<CXFA_FMAST> ast = parser.Parse();
+  CXFA_FMLexer lexer(wsFormcalc);
+  CXFA_FMParser parser(pHeap, &lexer);
+  CXFA_FMAST* ast = parser.Parse();
   if (!ast || parser.HasError())
-    return false;
+    return absl::nullopt;
 
   CXFA_FMToJavaScriptDepth::Reset();
-  if (!ast->ToJavaScript(wsJavascript))
-    return false;
+  absl::optional<WideTextBuffer> wsJavaScript = ast->ToJavaScript();
+  if (!wsJavaScript.has_value())
+    return absl::nullopt;
 
-  wsJavascript->AppendChar(0);
-  return !CXFA_IsTooBig(wsJavascript);
+  if (CXFA_IsTooBig(wsJavaScript.value()))
+    return absl::nullopt;
+
+  return wsJavaScript;
 }
 
-CFXJSE_FormCalcContext::CFXJSE_FormCalcContext(v8::Isolate* pScriptIsolate,
+CFXJSE_FormCalcContext::CFXJSE_FormCalcContext(v8::Isolate* pIsolate,
                                                CFXJSE_Context* pScriptContext,
                                                CXFA_Document* pDoc)
-    : m_pIsolate(pScriptIsolate),
-      m_pValue(pdfium::MakeUnique<CFXJSE_Value>(pScriptIsolate)),
-      m_pDocument(pDoc) {
-  m_pValue->SetHostObject(
-      this,
-      CFXJSE_Class::Create(pScriptContext, &kFormCalcFM2JSDescriptor, false));
+    : m_pIsolate(pIsolate), m_pDocument(pDoc) {
+  m_Value.Reset(m_pIsolate,
+                NewBoundV8Object(
+                    m_pIsolate, CFXJSE_Class::Create(
+                                    pScriptContext, &kFormCalcDescriptor, false)
+                                    ->GetTemplate(m_pIsolate)));
 }
 
 CFXJSE_FormCalcContext::~CFXJSE_FormCalcContext() = default;
@@ -5723,18 +5017,18 @@
   return this;
 }
 
-void CFXJSE_FormCalcContext::GlobalPropertyGetter(CFXJSE_Value* pValue) {
-  pValue->Assign(m_pValue.get());
+v8::Local<v8::Value> CFXJSE_FormCalcContext::GlobalPropertyGetter() {
+  return v8::Local<v8::Value>::New(m_pIsolate, m_Value);
 }
 
 // static
-void CFXJSE_FormCalcContext::DotAccessorCommon(CFXJSE_Value* pThis,
-                                               ByteStringView bsFuncName,
-                                               CFXJSE_Arguments& args,
-                                               bool bDotAccessor) {
+void CFXJSE_FormCalcContext::DotAccessorCommon(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info,
+    bool bDotAccessor) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  int32_t argc = args.GetLength();
+  v8::Isolate* pIsolate = pContext->GetIsolate();
+  int32_t argc = info.Length();
   if (argc < 4 || argc > 5) {
     pContext->ThrowCompilerErrorException();
     return;
@@ -5744,148 +5038,480 @@
   int32_t iIndexValue = 0;
   if (argc > 4) {
     bIsStar = false;
-    iIndexValue = ValueToInteger(pThis, args.GetValue(4).get());
+    iIndexValue = ValueToInteger(info.GetIsolate(), info[4]);
   }
 
-  const ByteString bsName = args.GetUTF8String(2);
+  const ByteString bsName =
+      fxv8::ReentrantToByteStringHelper(info.GetIsolate(), info[2]);
   const bool bHasNoResolveName = bDotAccessor && bsName.IsEmpty();
   ByteString bsSomExp = GenerateSomExpression(
-      bsName.AsStringView(), args.GetInt32(3), iIndexValue, bIsStar);
+      bsName.AsStringView(),
+      fxv8::ReentrantToInt32Helper(info.GetIsolate(), info[3]), iIndexValue,
+      bIsStar);
 
-  std::unique_ptr<CFXJSE_Value> argAccessor = args.GetValue(0);
-  if (argAccessor->IsArray()) {
-    auto pLengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argAccessor->GetObjectProperty("length", pLengthValue.get());
-    int32_t iLength = pLengthValue->ToInteger();
+  v8::Local<v8::Value> argAccessor = info[0];
+  if (fxv8::IsArray(argAccessor)) {
+    v8::Local<v8::Array> arr = argAccessor.As<v8::Array>();
+    uint32_t iLength = fxv8::GetArrayLengthHelper(arr);
     if (iLength < 3) {
       pContext->ThrowArgumentMismatchException();
       return;
     }
 
-    int32_t iCounter = 0;
-    auto hJSObjValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    std::vector<std::vector<std::unique_ptr<CFXJSE_Value>>> resolveValues(
-        iLength - 2);
+    std::vector<std::vector<v8::Local<v8::Value>>> resolveValues(iLength - 2);
     bool bAttribute = false;
-    for (int32_t i = 2; i < iLength; i++) {
-      argAccessor->GetObjectPropertyByIdx(i, hJSObjValue.get());
-      XFA_RESOLVENODE_RS resolveNodeRS;
-      if (ResolveObjects(pThis, hJSObjValue.get(), bsSomExp.AsStringView(),
-                         &resolveNodeRS, bDotAccessor, bHasNoResolveName)) {
-        ParseResolveResult(pThis, resolveNodeRS, hJSObjValue.get(),
-                           &resolveValues[i - 2], &bAttribute);
-        iCounter += resolveValues[i - 2].size();
+    bool bAllEmpty = true;
+    for (uint32_t i = 2; i < iLength; i++) {
+      v8::Local<v8::Value> hJSObjValue =
+          fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, i);
+      absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+          ResolveObjects(pThis, hJSObjValue, bsSomExp.AsStringView(),
+                         bDotAccessor, bHasNoResolveName);
+      if (maybeResult.has_value()) {
+        resolveValues[i - 2] = ParseResolveResult(pThis, maybeResult.value(),
+                                                  hJSObjValue, &bAttribute);
+        bAllEmpty = bAllEmpty && resolveValues[i - 2].empty();
       }
     }
-    if (iCounter < 1) {
-      pContext->ThrowPropertyNotInObjectException(
-          WideString::FromUTF8(bsName.AsStringView()),
-          WideString::FromUTF8(bsSomExp.AsStringView()));
+    if (bAllEmpty) {
+      pContext->ThrowPropertyNotInObjectException(bsName.AsStringView(),
+                                                  bsSomExp.AsStringView());
       return;
     }
 
-    std::vector<std::unique_ptr<CFXJSE_Value>> values;
-    for (int32_t i = 0; i < iCounter + 2; i++)
-      values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-    values[0]->SetInteger(1);
-    if (bAttribute)
-      values[1]->SetString(bsName.AsStringView());
-    else
-      values[1]->SetNull();
-
-    int32_t iIndex = 2;
-    for (int32_t i = 0; i < iLength - 2; i++) {
-      for (size_t j = 0; j < resolveValues[i].size(); j++) {
-        values[iIndex]->Assign(resolveValues[i][j].get());
-        iIndex++;
-      }
+    std::vector<v8::Local<v8::Value>> values;
+    values.push_back(fxv8::NewNumberHelper(pIsolate, 1));
+    values.push_back(
+        bAttribute ? fxv8::NewStringHelper(pIsolate, bsName.AsStringView())
+                         .As<v8::Value>()
+                   : fxv8::NewNullHelper(pIsolate).As<v8::Value>());
+    for (uint32_t i = 0; i < iLength - 2; i++) {
+      for (size_t j = 0; j < resolveValues[i].size(); j++)
+        values.push_back(resolveValues[i][j]);
     }
-    args.GetReturnValue()->SetArray(values);
+    info.GetReturnValue().Set(fxv8::NewArrayHelper(pIsolate, values));
     return;
   }
 
-  XFA_RESOLVENODE_RS resolveNodeRS;
-  bool bRet = false;
-  ByteString bsAccessorName = args.GetUTF8String(1);
-  if (argAccessor->IsObject() ||
-      (argAccessor->IsNull() && bsAccessorName.IsEmpty())) {
-    bRet = ResolveObjects(pThis, argAccessor.get(), bsSomExp.AsStringView(),
-                          &resolveNodeRS, bDotAccessor, bHasNoResolveName);
-  } else if (!argAccessor->IsObject() && !bsAccessorName.IsEmpty() &&
-             GetObjectForName(pThis, argAccessor.get(),
-                              bsAccessorName.AsStringView())) {
-    bRet = ResolveObjects(pThis, argAccessor.get(), bsSomExp.AsStringView(),
-                          &resolveNodeRS, bDotAccessor, bHasNoResolveName);
+  absl::optional<CFXJSE_Engine::ResolveResult> maybeResult;
+  ByteString bsAccessorName =
+      fxv8::ReentrantToByteStringHelper(info.GetIsolate(), info[1]);
+  if (fxv8::IsObject(argAccessor) ||
+      (fxv8::IsNull(argAccessor) && bsAccessorName.IsEmpty())) {
+    maybeResult = ResolveObjects(pThis, argAccessor, bsSomExp.AsStringView(),
+                                 bDotAccessor, bHasNoResolveName);
+  } else if (!fxv8::IsObject(argAccessor) && !bsAccessorName.IsEmpty()) {
+    v8::Local<v8::Value> obj =
+        GetObjectForName(pThis, bsAccessorName.AsStringView());
+    if (!obj.IsEmpty()) {
+      argAccessor = obj;
+      maybeResult = ResolveObjects(pThis, argAccessor, bsSomExp.AsStringView(),
+                                   bDotAccessor, bHasNoResolveName);
+    }
   }
-  if (!bRet) {
-    pContext->ThrowPropertyNotInObjectException(
-        WideString::FromUTF8(bsName.AsStringView()),
-        WideString::FromUTF8(bsSomExp.AsStringView()));
+  if (!maybeResult.has_value()) {
+    pContext->ThrowPropertyNotInObjectException(bsName.AsStringView(),
+                                                bsSomExp.AsStringView());
     return;
   }
 
-  std::vector<std::unique_ptr<CFXJSE_Value>> resolveValues;
   bool bAttribute = false;
-  ParseResolveResult(pThis, resolveNodeRS, argAccessor.get(), &resolveValues,
-                     &bAttribute);
+  std::vector<v8::Local<v8::Value>> resolveValues =
+      ParseResolveResult(pThis, maybeResult.value(), argAccessor, &bAttribute);
 
-  std::vector<std::unique_ptr<CFXJSE_Value>> values;
-  for (size_t i = 0; i < resolveValues.size() + 2; i++)
-    values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-  values[0]->SetInteger(1);
-  if (bAttribute)
-    values[1]->SetString(bsName.AsStringView());
-  else
-    values[1]->SetNull();
+  std::vector<v8::Local<v8::Value>> values(resolveValues.size() + 2);
+  values[0] = fxv8::NewNumberHelper(pIsolate, 1);
+  values[1] = bAttribute
+                  ? fxv8::NewStringHelper(pIsolate, bsName.AsStringView())
+                        .As<v8::Value>()
+                  : fxv8::NewNullHelper(pIsolate).As<v8::Value>();
 
   for (size_t i = 0; i < resolveValues.size(); i++)
-    values[i + 2]->Assign(resolveValues[i].get());
+    values[i + 2] = resolveValues[i];
 
-  args.GetReturnValue()->SetArray(values);
+  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,
+    bool bStrict) {
+  v8::Isolate* pIsolate = info.GetIsolate();
+  for (int32_t i = 0; i < info.Length(); i++) {
+    v8::Local<v8::Value> argValue = info[i];
+    if (fxv8::IsArray(argValue)) {
+      if (!ApplyToArray(pIsolate, fn, argValue.As<v8::Array>()) && bStrict) {
+        ThrowArgumentMismatchException();
+        return false;
+      }
+    } else if (fxv8::IsObject(argValue)) {
+      ApplyToObject(pIsolate, fn, argValue.As<v8::Object>());
+    } else if (!fxv8::IsNull(argValue)) {
+      fn(pIsolate, argValue);
+    }
+  }
+  return true;
+}
+
+bool CFXJSE_FormCalcContext::ApplyToArray(
+    v8::Isolate* pIsolate,
+    std::function<void(v8::Isolate*, v8::Local<v8::Value>)> fn,
+    v8::Local<v8::Array> pArray) {
+  uint32_t iLength = fxv8::GetArrayLengthHelper(pArray);
+  if (iLength < 3)
+    return false;
+
+  v8::Local<v8::Value> propertyValue =
+      fxv8::ReentrantGetArrayElementHelper(pIsolate, pArray, 1);
+
+  ByteString bsName;
+  const bool nullprop = fxv8::IsNull(propertyValue);
+  if (!nullprop)
+    bsName = fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue);
+
+  for (uint32_t j = 2; j < iLength; j++) {
+    v8::Local<v8::Value> jsValue =
+        fxv8::ReentrantGetArrayElementHelper(pIsolate, pArray, j);
+    if (!fxv8::IsObject(jsValue))
+      continue;
+
+    v8::Local<v8::Object> jsObjectValue = jsValue.As<v8::Object>();
+    v8::Local<v8::Value> newPropertyValue =
+        nullprop ? GetObjectDefaultValue(pIsolate, jsObjectValue)
+                 : fxv8::ReentrantGetObjectPropertyHelper(
+                       pIsolate, jsObjectValue, bsName.AsStringView());
+    if (!fxv8::IsNull(newPropertyValue))
+      fn(pIsolate, newPropertyValue);
+  }
+  return true;
+}
+
+void CFXJSE_FormCalcContext::ApplyToObject(
+    v8::Isolate* pIsolate,
+    std::function<void(v8::Isolate*, v8::Local<v8::Value>)> fn,
+    v8::Local<v8::Object> pObject) {
+  v8::Local<v8::Value> newPropertyValue =
+      GetObjectDefaultValue(pIsolate, pObject);
+  if (!fxv8::IsNull(newPropertyValue))
+    fn(pIsolate, newPropertyValue);
 }
 
 void CFXJSE_FormCalcContext::ThrowNoDefaultPropertyException(
     ByteStringView name) const {
-  ThrowException(WideString::FromUTF8(name) +
-                 WideString::FromASCII(" doesn't have a default property."));
+  ByteString msg(name);
+  msg += " doesn't have a default property.";
+  ThrowException(msg.AsStringView());
 }
 
 void CFXJSE_FormCalcContext::ThrowCompilerErrorException() const {
-  ThrowException(WideString::FromASCII("Compiler error."));
+  ThrowException("Compiler error.");
 }
 
 void CFXJSE_FormCalcContext::ThrowDivideByZeroException() const {
-  ThrowException(WideString::FromASCII("Divide by zero."));
+  ThrowException("Divide by zero.");
 }
 
 void CFXJSE_FormCalcContext::ThrowServerDeniedException() const {
-  ThrowException(WideString::FromASCII("Server does not permit operation."));
+  ThrowException("Server does not permit operation.");
 }
 
 void CFXJSE_FormCalcContext::ThrowPropertyNotInObjectException(
-    const WideString& name,
-    const WideString& exp) const {
-  ThrowException(
-      WideString::FromASCII("An attempt was made to reference property '") +
-      name + WideString::FromASCII("' of a non-object in SOM expression ") +
-      exp + L".");
+    ByteStringView name,
+    ByteStringView exp) const {
+  ByteString msg("An attempt was made to reference property '");
+  msg += name;
+  msg += "' of a non-object in SOM expression ";
+  msg += exp;
+  msg += ".";
+  ThrowException(msg.AsStringView());
 }
 
 void CFXJSE_FormCalcContext::ThrowParamCountMismatchException(
-    const WideString& method) const {
-  ThrowException(
-      WideString::FromASCII("Incorrect number of parameters calling method '") +
-      method + L"'.");
+    ByteStringView method) const {
+  ByteString msg("Incorrect number of parameters calling method '");
+  msg += method;
+  msg += "'.";
+  ThrowException(msg.AsStringView());
 }
 
 void CFXJSE_FormCalcContext::ThrowArgumentMismatchException() const {
-  ThrowException(WideString::FromASCII(
-      "Argument mismatch in property or function argument."));
+  ThrowException("Argument mismatch in property or function argument.");
 }
 
-void CFXJSE_FormCalcContext::ThrowException(const WideString& str) const {
-  ASSERT(!str.IsEmpty());
-  FXJSE_ThrowMessage(str.ToUTF8().AsStringView());
+void CFXJSE_FormCalcContext::ThrowException(ByteStringView str) const {
+  DCHECK(!str.IsEmpty());
+  FXJSE_ThrowMessage(GetIsolate(), str);
 }
diff --git a/fxjs/xfa/cfxjse_formcalc_context.h b/fxjs/xfa/cfxjse_formcalc_context.h
index cf1bc95..a0bf84f 100644
--- a/fxjs/xfa/cfxjse_formcalc_context.h
+++ b/fxjs/xfa/cfxjse_formcalc_context.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,21 +7,28 @@
 #ifndef FXJS_XFA_CFXJSE_FORMCALC_CONTEXT_H_
 #define FXJS_XFA_CFXJSE_FORMCALC_CONTEXT_H_
 
-#include <memory>
-#include <vector>
+#include <stdint.h>
 
-#include "core/fxcrt/unowned_ptr.h"
+#include <functional>
+
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/widetext_buffer.h"
 #include "fxjs/xfa/fxjse.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "v8/include/cppgc/persistent.h"
+#include "v8/include/v8-forward.h"
+#include "v8/include/v8-persistent-handle.h"
 
-class CFXJSE_Arguments;
 class CFXJSE_Context;
-class CFX_WideTextBuf;
 class CXFA_Document;
 
+namespace cppgc {
+class Heap;
+}  // namespace cppgc
+
 class CFXJSE_FormCalcContext final : public CFXJSE_HostObject {
  public:
-  CFXJSE_FormCalcContext(v8::Isolate* pScriptIsolate,
+  CFXJSE_FormCalcContext(v8::Isolate* pIsolate,
                          CFXJSE_Context* pScriptContext,
                          CXFA_Document* pDoc);
   ~CFXJSE_FormCalcContext() override;
@@ -29,387 +36,298 @@
   // CFXJSE_HostObject:
   CFXJSE_FormCalcContext* AsFormCalcContext() override;
 
-  static void Abs(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Avg(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Ceil(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Count(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Floor(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Max(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Min(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Mod(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Round(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Sum(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Date(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Date2Num(CFXJSE_Value* pThis,
-                       ByteStringView bsFuncName,
-                       CFXJSE_Arguments& args);
-  static void DateFmt(CFXJSE_Value* pThis,
-                      ByteStringView bsFuncName,
-                      CFXJSE_Arguments& args);
-  static void IsoDate2Num(CFXJSE_Value* pThis,
-                          ByteStringView bsFuncName,
-                          CFXJSE_Arguments& args);
-  static void IsoTime2Num(CFXJSE_Value* pThis,
-                          ByteStringView bsFuncName,
-                          CFXJSE_Arguments& args);
-  static void LocalDateFmt(CFXJSE_Value* pThis,
-                           ByteStringView bsFuncName,
-                           CFXJSE_Arguments& args);
-  static void LocalTimeFmt(CFXJSE_Value* pThis,
-                           ByteStringView bsFuncName,
-                           CFXJSE_Arguments& args);
-  static void Num2Date(CFXJSE_Value* pThis,
-                       ByteStringView bsFuncName,
-                       CFXJSE_Arguments& args);
-  static void Num2GMTime(CFXJSE_Value* pThis,
-                         ByteStringView bsFuncName,
-                         CFXJSE_Arguments& args);
-  static void Num2Time(CFXJSE_Value* pThis,
-                       ByteStringView bsFuncName,
-                       CFXJSE_Arguments& args);
-  static void Time(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Time2Num(CFXJSE_Value* pThis,
-                       ByteStringView bsFuncName,
-                       CFXJSE_Arguments& args);
-  static void TimeFmt(CFXJSE_Value* pThis,
-                      ByteStringView bsFuncName,
-                      CFXJSE_Arguments& args);
+  static void Abs(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Avg(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Ceil(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Count(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Floor(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Max(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Min(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Mod(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Round(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Sum(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Date(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Date2Num(CFXJSE_HostObject* pThis,
+                       const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void DateFmt(CFXJSE_HostObject* pThis,
+                      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void IsoDate2Num(CFXJSE_HostObject* pThis,
+                          const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void IsoTime2Num(CFXJSE_HostObject* pThis,
+                          const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void LocalDateFmt(CFXJSE_HostObject* pThis,
+                           const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void LocalTimeFmt(CFXJSE_HostObject* pThis,
+                           const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Num2Date(CFXJSE_HostObject* pThis,
+                       const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Num2GMTime(CFXJSE_HostObject* pThis,
+                         const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Num2Time(CFXJSE_HostObject* pThis,
+                       const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Time(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Time2Num(CFXJSE_HostObject* pThis,
+                       const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void TimeFmt(CFXJSE_HostObject* pThis,
+                      const v8::FunctionCallbackInfo<v8::Value>& info);
 
-  static ByteString Local2IsoDate(CFXJSE_Value* pThis,
+  static ByteString Local2IsoDate(CFXJSE_HostObject* pThis,
                                   ByteStringView bsDate,
                                   ByteStringView bsFormat,
                                   ByteStringView bsLocale);
-  static ByteString IsoDate2Local(CFXJSE_Value* pThis,
+  static ByteString IsoDate2Local(CFXJSE_HostObject* pThis,
                                   ByteStringView bsDate,
                                   ByteStringView bsFormat,
                                   ByteStringView bsLocale);
-  static ByteString IsoTime2Local(CFXJSE_Value* pThis,
+  static ByteString IsoTime2Local(CFXJSE_HostObject* pThis,
                                   ByteStringView bsTime,
                                   ByteStringView bsFormat,
                                   ByteStringView bsLocale);
-  static ByteString GetLocalDateFormat(CFXJSE_Value* pThis,
+  static ByteString GetLocalDateFormat(CFXJSE_HostObject* pThis,
                                        int32_t iStyle,
                                        ByteStringView bsLocale,
                                        bool bStandard);
-  static ByteString GetLocalTimeFormat(CFXJSE_Value* pThis,
+  static ByteString GetLocalTimeFormat(CFXJSE_HostObject* pThis,
                                        int32_t iStyle,
                                        ByteStringView bsLocale,
                                        bool bStandard);
-  static ByteString GetStandardDateFormat(CFXJSE_Value* pThis,
+  static ByteString GetStandardDateFormat(CFXJSE_HostObject* pThis,
                                           int32_t iStyle,
                                           ByteStringView bsLocale);
-  static ByteString GetStandardTimeFormat(CFXJSE_Value* pThis,
+  static ByteString GetStandardTimeFormat(CFXJSE_HostObject* pThis,
                                           int32_t iStyle,
                                           ByteStringView bsLocale);
-  static ByteString Num2AllTime(CFXJSE_Value* pThis,
+  static ByteString Num2AllTime(CFXJSE_HostObject* pThis,
                                 int32_t iTime,
                                 ByteStringView bsFormat,
                                 ByteStringView bsLocale,
                                 bool bGM);
 
-  static void Apr(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void CTerm(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void FV(CFXJSE_Value* pThis,
-                 ByteStringView bsFuncName,
-                 CFXJSE_Arguments& args);
-  static void IPmt(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void NPV(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Pmt(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void PPmt(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void PV(CFXJSE_Value* pThis,
-                 ByteStringView bsFuncName,
-                 CFXJSE_Arguments& args);
-  static void Rate(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Term(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Choose(CFXJSE_Value* pThis,
-                     ByteStringView bsFuncName,
-                     CFXJSE_Arguments& args);
-  static void Exists(CFXJSE_Value* pThis,
-                     ByteStringView bsFuncName,
-                     CFXJSE_Arguments& args);
-  static void HasValue(CFXJSE_Value* pThis,
-                       ByteStringView bsFuncName,
-                       CFXJSE_Arguments& args);
-  static void Oneof(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Within(CFXJSE_Value* pThis,
-                     ByteStringView bsFuncName,
-                     CFXJSE_Arguments& args);
-  static void If(CFXJSE_Value* pThis,
-                 ByteStringView bsFuncName,
-                 CFXJSE_Arguments& args);
-  static void Eval(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Ref(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void UnitType(CFXJSE_Value* pThis,
-                       ByteStringView bsFuncName,
-                       CFXJSE_Arguments& args);
-  static void UnitValue(CFXJSE_Value* pThis,
-                        ByteStringView bsFuncName,
-                        CFXJSE_Arguments& args);
+  static void Apr(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void CTerm(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void FV(CFXJSE_HostObject* pThis,
+                 const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void IPmt(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void NPV(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Pmt(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void PPmt(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void PV(CFXJSE_HostObject* pThis,
+                 const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Rate(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Term(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Choose(CFXJSE_HostObject* pThis,
+                     const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Exists(CFXJSE_HostObject* pThis,
+                     const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void HasValue(CFXJSE_HostObject* pThis,
+                       const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Oneof(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Within(CFXJSE_HostObject* pThis,
+                     const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void If(CFXJSE_HostObject* pThis,
+                 const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Eval(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Ref(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void UnitType(CFXJSE_HostObject* pThis,
+                       const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void UnitValue(CFXJSE_HostObject* pThis,
+                        const v8::FunctionCallbackInfo<v8::Value>& info);
 
-  static void At(CFXJSE_Value* pThis,
-                 ByteStringView bsFuncName,
-                 CFXJSE_Arguments& args);
-  static void Concat(CFXJSE_Value* pThis,
-                     ByteStringView bsFuncName,
-                     CFXJSE_Arguments& args);
-  static void Decode(CFXJSE_Value* pThis,
-                     ByteStringView bsFuncName,
-                     CFXJSE_Arguments& args);
-  static void Encode(CFXJSE_Value* pThis,
-                     ByteStringView bsFuncName,
-                     CFXJSE_Arguments& args);
-  static void Format(CFXJSE_Value* pThis,
-                     ByteStringView bsFuncName,
-                     CFXJSE_Arguments& args);
-  static void Left(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Len(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Lower(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Ltrim(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Parse(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Replace(CFXJSE_Value* pThis,
-                      ByteStringView bsFuncName,
-                      CFXJSE_Arguments& args);
-  static void Right(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Rtrim(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Space(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Str(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Stuff(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Substr(CFXJSE_Value* pThis,
-                     ByteStringView bsFuncName,
-                     CFXJSE_Arguments& args);
-  static void Uuid(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Upper(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void WordNum(CFXJSE_Value* pThis,
-                      ByteStringView bsFuncName,
-                      CFXJSE_Arguments& args);
+  static void At(CFXJSE_HostObject* pThis,
+                 const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Concat(CFXJSE_HostObject* pThis,
+                     const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Decode(CFXJSE_HostObject* pThis,
+                     const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Encode(CFXJSE_HostObject* pThis,
+                     const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Format(CFXJSE_HostObject* pThis,
+                     const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Left(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Len(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Lower(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Ltrim(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Parse(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Replace(CFXJSE_HostObject* pThis,
+                      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Right(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Rtrim(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Space(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Str(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Stuff(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Substr(CFXJSE_HostObject* pThis,
+                     const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Uuid(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Upper(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void WordNum(CFXJSE_HostObject* pThis,
+                      const v8::FunctionCallbackInfo<v8::Value>& info);
 
-  static void Get(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Post(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Put(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void assign_value_operator(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args);
-  static void logical_or_operator(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args);
-  static void logical_and_operator(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args);
-  static void equality_operator(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args);
-  static void notequality_operator(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args);
-  static bool fm_ref_equal(CFXJSE_Value* pThis, CFXJSE_Arguments& args);
-  static void less_operator(CFXJSE_Value* pThis,
-                            ByteStringView bsFuncName,
-                            CFXJSE_Arguments& args);
-  static void lessequal_operator(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args);
-  static void greater_operator(CFXJSE_Value* pThis,
-                               ByteStringView bsFuncName,
-                               CFXJSE_Arguments& args);
-  static void greaterequal_operator(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args);
-  static void plus_operator(CFXJSE_Value* pThis,
-                            ByteStringView bsFuncName,
-                            CFXJSE_Arguments& args);
-  static void minus_operator(CFXJSE_Value* pThis,
-                             ByteStringView bsFuncName,
-                             CFXJSE_Arguments& args);
-  static void multiple_operator(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args);
-  static void divide_operator(CFXJSE_Value* pThis,
-                              ByteStringView bsFuncName,
-                              CFXJSE_Arguments& args);
-  static void positive_operator(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args);
-  static void negative_operator(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args);
-  static void logical_not_operator(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args);
-  static void dot_accessor(CFXJSE_Value* pThis,
-                           ByteStringView bsFuncName,
-                           CFXJSE_Arguments& args);
-  static void dotdot_accessor(CFXJSE_Value* pThis,
-                              ByteStringView bsFuncName,
-                              CFXJSE_Arguments& args);
-  static void eval_translation(CFXJSE_Value* pThis,
-                               ByteStringView bsFuncName,
-                               CFXJSE_Arguments& args);
-  static void is_fm_object(CFXJSE_Value* pThis,
-                           ByteStringView bsFuncName,
-                           CFXJSE_Arguments& args);
-  static void is_fm_array(CFXJSE_Value* pThis,
-                          ByteStringView bsFuncName,
-                          CFXJSE_Arguments& args);
-  static void get_fm_value(CFXJSE_Value* pThis,
-                           ByteStringView bsFuncName,
-                           CFXJSE_Arguments& args);
-  static void get_fm_jsobj(CFXJSE_Value* pThis,
-                           ByteStringView bsFuncName,
-                           CFXJSE_Arguments& args);
-  static void fm_var_filter(CFXJSE_Value* pThis,
-                            ByteStringView bsFuncName,
-                            CFXJSE_Arguments& args);
-  static void concat_fm_object(CFXJSE_Value* pThis,
-                               ByteStringView bsFuncName,
-                               CFXJSE_Arguments& args);
+  static void Get(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Post(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Put(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void assign_value_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void logical_or_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void logical_and_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void equality_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void notequality_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static bool fm_ref_equal(CFXJSE_HostObject* pThis,
+                           const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void less_operator(CFXJSE_HostObject* pThis,
+                            const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void lessequal_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void greater_operator(CFXJSE_HostObject* pThis,
+                               const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void greaterequal_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void plus_operator(CFXJSE_HostObject* pThis,
+                            const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void minus_operator(CFXJSE_HostObject* pThis,
+                             const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void multiple_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void divide_operator(CFXJSE_HostObject* pThis,
+                              const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void positive_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void negative_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void logical_not_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void dot_accessor(CFXJSE_HostObject* pThis,
+                           const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void dotdot_accessor(CFXJSE_HostObject* pThis,
+                              const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void eval_translation(CFXJSE_HostObject* pThis,
+                               const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void is_fm_object(CFXJSE_HostObject* pThis,
+                           const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void is_fm_array(CFXJSE_HostObject* pThis,
+                          const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void get_fm_value(CFXJSE_HostObject* pThis,
+                           const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void get_fm_jsobj(CFXJSE_HostObject* pThis,
+                           const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void fm_var_filter(CFXJSE_HostObject* pThis,
+                            const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void concat_fm_object(CFXJSE_HostObject* pThis,
+                               const v8::FunctionCallbackInfo<v8::Value>& info);
 
-  static int32_t hvalue_get_array_length(CFXJSE_Value* pThis,
-                                         CFXJSE_Value* arg);
-  static bool simpleValueCompare(CFXJSE_Value* pThis,
-                                 CFXJSE_Value* firstValue,
-                                 CFXJSE_Value* secondValue);
-  static std::vector<std::unique_ptr<CFXJSE_Value>> unfoldArgs(
-      CFXJSE_Value* pThis,
-      CFXJSE_Arguments& args);
-  static void GetObjectDefaultValue(CFXJSE_Value* pObjectValue,
-                                    CFXJSE_Value* pDefaultValue);
-  static bool SetObjectDefaultValue(CFXJSE_Value* pObjectValue,
-                                    CFXJSE_Value* pNewValue);
+  static absl::optional<WideTextBuffer> Translate(cppgc::Heap* pHeap,
+                                                  WideStringView wsFormcalc);
+
+  v8::Local<v8::Value> GlobalPropertyGetter();
+  v8::Isolate* GetIsolate() const { return m_pIsolate; }
+  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 bool GetObjectForName(CFXJSE_Value* pThis,
-                               CFXJSE_Value* accessorValue,
-                               ByteStringView bsAccessorName);
-  static bool ResolveObjects(CFXJSE_Value* pThis,
-                             CFXJSE_Value* pParentValue,
-                             ByteStringView bsSomExp,
-                             XFA_RESOLVENODE_RS* resolveNodeRS,
-                             bool bdotAccessor,
-                             bool bHasNoResolveName);
-  static void ParseResolveResult(
-      CFXJSE_Value* pThis,
-      const XFA_RESOLVENODE_RS& resolveNodeRS,
-      CFXJSE_Value* pParentValue,
-      std::vector<std::unique_ptr<CFXJSE_Value>>* resultValues,
-      bool* bAttribute);
 
-  static std::unique_ptr<CFXJSE_Value> GetSimpleValue(CFXJSE_Value* pThis,
-                                                      CFXJSE_Arguments& args,
-                                                      uint32_t index);
-  static bool ValueIsNull(CFXJSE_Value* pThis, CFXJSE_Value* pValue);
-  static int32_t ValueToInteger(CFXJSE_Value* pThis, CFXJSE_Value* pValue);
-  static float ValueToFloat(CFXJSE_Value* pThis, CFXJSE_Value* pValue);
-  static double ValueToDouble(CFXJSE_Value* pThis, CFXJSE_Value* pValue);
-  static ByteString ValueToUTF8String(CFXJSE_Value* pValue);
-  static double ExtractDouble(CFXJSE_Value* pThis,
-                              CFXJSE_Value* src,
-                              bool* ret);
-
-  static bool Translate(WideStringView wsFormcalc,
-                        CFX_WideTextBuf* wsJavascript);
-
-  void GlobalPropertyGetter(CFXJSE_Value* pValue);
-
- private:
-  static void DotAccessorCommon(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args,
+  static void DotAccessorCommon(CFXJSE_HostObject* pThis,
+                                const v8::FunctionCallbackInfo<v8::Value>& info,
                                 bool bDotAccessor);
 
-  v8::Isolate* GetScriptRuntime() const { return m_pIsolate.Get(); }
-  CXFA_Document* GetDocument() const { return m_pDocument.Get(); }
+  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,
+      bool bStrict);
+
+  bool ApplyToArray(v8::Isolate* pIsolate,
+                    std::function<void(v8::Isolate*, v8::Local<v8::Value>)> fn,
+                    v8::Local<v8::Array> pArray);
+
+  void ApplyToObject(v8::Isolate* pIsolate,
+                     std::function<void(v8::Isolate*, v8::Local<v8::Value>)> fn,
+                     v8::Local<v8::Object> pObject);
+
+  void ThrowArgumentMismatchException() const;
   void ThrowNoDefaultPropertyException(ByteStringView name) const;
   void ThrowCompilerErrorException() const;
   void ThrowDivideByZeroException() const;
   void ThrowServerDeniedException() const;
-  void ThrowPropertyNotInObjectException(const WideString& name,
-                                         const WideString& exp) const;
-  void ThrowArgumentMismatchException() const;
-  void ThrowParamCountMismatchException(const WideString& method) const;
-  void ThrowException(const WideString& str) const;
+  void ThrowPropertyNotInObjectException(ByteStringView name,
+                                         ByteStringView exp) const;
+  void ThrowParamCountMismatchException(ByteStringView method) const;
+  void ThrowException(ByteStringView str) const;
 
-  UnownedPtr<v8::Isolate> m_pIsolate;
-  std::unique_ptr<CFXJSE_Value> m_pValue;
-  UnownedPtr<CXFA_Document> const m_pDocument;
+  UnownedPtr<v8::Isolate> const m_pIsolate;
+  v8::Global<v8::Value> m_Value;
+  cppgc::WeakPersistent<CXFA_Document> const m_pDocument;
 };
 
 #endif  // FXJS_XFA_CFXJSE_FORMCALC_CONTEXT_H_
diff --git a/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
index 19fc730..db03f03 100644
--- a/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
+++ b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
@@ -1,10 +1,15 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <math.h>
+
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_isolatetracker.h"
 #include "fxjs/xfa/cfxjse_value.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/scoped_set_tz.h"
 #include "testing/xfa_js_embedder_test.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 
@@ -14,8 +19,85 @@
   ~CFXJSE_FormCalcContextEmbedderTest() override = default;
 
  protected:
-  bool ExecuteExpectNull(ByteStringView input) {
-    return Execute(input) && GetValue()->IsNull();
+  CFXJSE_Context* GetJseContext() {
+    return GetScriptContext()->GetJseContextForTest();
+  }
+
+  void ExecuteExpectError(ByteStringView input) {
+    EXPECT_FALSE(Execute(input)) << "Program: " << input;
+  }
+
+  void ExecuteExpectNull(ByteStringView input) {
+    EXPECT_TRUE(Execute(input)) << "Program: " << input;
+
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+    EXPECT_TRUE(fxv8::IsNull(GetValue())) << "Program: " << input;
+  }
+
+  void ExecuteExpectBool(ByteStringView input, bool expected) {
+    EXPECT_TRUE(Execute(input)) << "Program: " << input;
+
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+    v8::Local<v8::Value> value = GetValue();
+
+    // Yes, bools might be integers, somehow.
+    EXPECT_TRUE(fxv8::IsBoolean(value) || fxv8::IsInteger(value))
+        << "Program: " << input;
+    EXPECT_EQ(expected, fxv8::ReentrantToBooleanHelper(isolate(), value))
+        << "Program: " << input;
+  }
+
+  void ExecuteExpectInt32(ByteStringView input, int32_t expected) {
+    EXPECT_TRUE(Execute(input)) << "Program: " << input;
+
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+    v8::Local<v8::Value> value = GetValue();
+    EXPECT_TRUE(fxv8::IsInteger(value)) << "Program: " << input;
+    EXPECT_EQ(expected, fxv8::ReentrantToInt32Helper(isolate(), value))
+        << "Program: " << input;
+  }
+
+  void ExecuteExpectFloat(ByteStringView input, float expected) {
+    EXPECT_TRUE(Execute(input)) << "Program: " << input;
+
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+    v8::Local<v8::Value> value = GetValue();
+    EXPECT_TRUE(fxv8::IsNumber(value));
+    EXPECT_FLOAT_EQ(expected, fxv8::ReentrantToFloatHelper(isolate(), value))
+        << "Program: " << input;
+  }
+
+  void ExecuteExpectFloatNear(ByteStringView input, float expected) {
+    constexpr float kPrecision = 0.000001f;
+
+    EXPECT_TRUE(Execute(input)) << "Program: " << input;
+
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+    v8::Local<v8::Value> value = GetValue();
+    EXPECT_TRUE(fxv8::IsNumber(value));
+    EXPECT_NEAR(expected, fxv8::ReentrantToFloatHelper(isolate(), value),
+                kPrecision)
+        << "Program: " << input;
+  }
+
+  void ExecuteExpectNaN(ByteStringView input) {
+    EXPECT_TRUE(Execute(input)) << "Program: " << input;
+
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+    v8::Local<v8::Value> value = GetValue();
+    EXPECT_TRUE(fxv8::IsNumber(value));
+    EXPECT_TRUE(isnan(fxv8::ReentrantToDoubleHelper(isolate(), value)));
+  }
+
+  void ExecuteExpectString(ByteStringView input, const char* expected) {
+    EXPECT_TRUE(Execute(input)) << "Program: " << input;
+
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+    v8::Local<v8::Value> value = GetValue();
+    EXPECT_TRUE(fxv8::IsString(value));
+    EXPECT_STREQ(expected,
+                 fxv8::ReentrantToByteStringHelper(isolate(), value).c_str())
+        << "Program: " << input;
   }
 };
 
@@ -32,309 +114,167 @@
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, TranslateNumber) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  const char input[] = "123";
-  EXPECT_TRUE(Execute(input));
-
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsInteger());
-  EXPECT_EQ(123, value->ToInteger()) << "Program: " << input;
+  ExecuteExpectInt32("123", 123);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Numeric) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"123 + 456", 579},
-               {"2 - 3 * 10 / 2 + 7", -6},
-               {"10 * 3 + 5 * 4", 50},
-               {"(5 - \"abc\") * 3", 15},
-               {"\"100\" / 10e1", 1},
-               {"5 + null + 3", 8},
-               // {"if (\"abc\") then\n"
-               //  "  10\n"
-               //  "else\n"
-               //  "  20\n"
-               //  "endif",
-               //  20},
-               // {"3 / 0 + 1", 0},
-               {"-(17)", -17},
-               {"-(-17)", 17},
-               {"+(17)", 17},
-               {"+(-17)", -17},
-               {"if (1 < 2) then\n1\nendif", 1},
-               {"if (\"abc\" > \"def\") then\n"
-                "  1 and 0\n"
-                "else\n"
-                "  0\n"
-                "endif",
-                0}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("123 + 456", 579);
+  ExecuteExpectInt32("2 - 3 * 10 / 2 + 7", -6);
+  ExecuteExpectInt32("10 * 3 + 5 * 4", 50);
+  ExecuteExpectInt32("(5 - \"abc\") * 3", 15);
+  ExecuteExpectInt32("\"100\" / 10e1", 1);
+  ExecuteExpectInt32("5 + null + 3", 8);
+#if 0
+  // TODO(thestig): Investigate these cases.
+  ExecuteExpectInt32(
+      "if (\"abc\") then\n"
+      "  10\n"
+      "else\n"
+      "  20\n"
+      "endif",
+      20);
+  ExecuteExpectInt32("3 / 0 + 1", 0);
+#endif
+  ExecuteExpectInt32("-(17)", -17);
+  ExecuteExpectInt32("-(-17)", 17);
+  ExecuteExpectInt32("+(17)", 17);
+  ExecuteExpectInt32("+(-17)", -17);
+  ExecuteExpectInt32("if (1 < 2) then\n1\nendif", 1);
+  ExecuteExpectInt32(
+      "if (\"abc\" > \"def\") then\n"
+      "  1 and 0\n"
+      "else\n"
+      "  0\n"
+      "endif",
+      0);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Strings) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      {"\"abc\"", "abc"},
-      {"concat(\"The total is \", 2, \" dollars and \", 57, \" cents.\")",
-       "The total is 2 dollars and 57 cents."}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("\"abc\"", "abc");
+  ExecuteExpectString(
+      "concat(\"The total is \", 2, \" dollars and \", 57, \" cents.\")",
+      "The total is 2 dollars and 57 cents.");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Booleans) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    bool result;
-  } tests[] = {{"0 and 1 or 2 > 1", true},
-               {"2 < 3 not 1 == 1", false},
-               {"\"abc\" | 2", true},
-               {"1 or 0", true},
-               {"0 | 0", false},
-               {"0 or 1 | 0 or 0", true},
-               {"1 and 0", false},
-               // {"0 & 0", true},  // TODO(dsinclair) Confirm with Reader.
-               {"0 and 1 & 0 and 0", false},
-               {"not(\"true\")", true},
-               {"not(1)", false},
-               {"3 == 3", true},
-               {"3 <> 4", true},
-               {"\"abc\" eq \"def\"", false},
-               {"\"def\" ne \"abc\"", true},
-               {"5 + 5 == 10", true},
-               {"5 + 5 <> \"10\"", false},
-               {"3 < 3", false},
-               {"3 > 4", false},
-               {"\"abc\" <= \"def\"", true},
-               {"\"def\" > \"abc\"", true},
-               {"12 >= 12", true},
-               {"\"true\" < \"false\"", false}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program;
-    EXPECT_EQ(tests[i].result, value->ToBoolean())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectBool("0 and 1 or 2 > 1", true);
+  ExecuteExpectBool("2 < 3 not 1 == 1", false);
+  ExecuteExpectBool("\"abc\" | 2", true);
+  ExecuteExpectBool("1 or 0", true);
+  ExecuteExpectBool("0 | 0", false);
+  ExecuteExpectBool("0 or 1 | 0 or 0", true);
+  ExecuteExpectBool("1 and 0", false);
+  ExecuteExpectBool("0 and 1 & 0 and 0", false);
+  ExecuteExpectBool("not(\"true\")", true);
+  ExecuteExpectBool("not(1)", false);
+  ExecuteExpectBool("3 == 3", true);
+  ExecuteExpectBool("3 <> 4", true);
+  ExecuteExpectBool("\"abc\" eq \"def\"", false);
+  ExecuteExpectBool("\"def\" ne \"abc\"", true);
+  ExecuteExpectBool("5 + 5 == 10", true);
+  ExecuteExpectBool("5 + 5 <> \"10\"", false);
+  ExecuteExpectBool("3 < 3", false);
+  ExecuteExpectBool("3 > 4", false);
+  ExecuteExpectBool("\"abc\" <= \"def\"", true);
+  ExecuteExpectBool("\"def\" > \"abc\"", true);
+  ExecuteExpectBool("12 >= 12", true);
+  ExecuteExpectBool("\"true\" < \"false\"", false);
+#if 0
+  // TODO(thestig): Investigate this case.
+  // Confirm with Reader.
+  ExecuteExpectBool("0 & 0", true);
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Abs) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"Abs(1.03)", 1.03f}, {"Abs(-1.03)", 1.03f}, {"Abs(0)", 0.0f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("Abs(1.03)", 1.03f);
+  ExecuteExpectFloat("Abs(-1.03)", 1.03f);
+  ExecuteExpectFloat("Abs(0)", 0.0f);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Avg) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"Avg(0, 32, 16)", 16.0f}, {"Avg(2.5, 17, null)", 9.75f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("Avg(0, 32, 16)", 16.0f);
+  ExecuteExpectFloat("Avg(2.5, 17, null)", 9.75f);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Ceil) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Ceil(2.5875)", 3}, {"Ceil(-5.9)", -5}, {"Ceil(\"abc\")", 0}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("Ceil(2.5875)", 3);
+  ExecuteExpectInt32("Ceil(-5.9)", -5);
+  ExecuteExpectInt32("Ceil(\"abc\")", 0);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Count) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Count(\"Tony\", \"Blue\", 41)", 3}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("Count(\"Tony\", \"Blue\", 41)", 3);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Floor) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Floor(21.3409873)", 21},
-               {"Floor(5.999965342)", 5},
-               {"Floor(3.2 * 15)", 48}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("Floor(21.3409873)", 21);
+  ExecuteExpectInt32("Floor(5.999965342)", 5);
+  ExecuteExpectInt32("Floor(3.2 * 15)", 48);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Max) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Max(234, 15, 107)", 234},
-               {"Max(\"abc\", 15, \"Tony Blue\")", 15},
-               {"Max(\"abc\")", 0}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("Max(234, 15, 107)", 234);
+  ExecuteExpectInt32("Max(\"abc\", 15, \"Tony Blue\")", 15);
+  ExecuteExpectInt32("Max(\"abc\")", 0);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Min) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Min(234, 15, 107)", 15},
-               // TODO(dsinclair): Verify with Reader; I believe this should
-               // have a return of 0.
-               // {"Min(\"abc\", 15, \"Tony Blue\")", 15},
-               {"Min(\"abc\")", 0}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("Min(234, 15, 107)", 15);
+#if 0
+  // TODO(thestig): Investigate these cases.
+  // Verify with Reader; This should have a return value of 0.
+  ExecuteExpectInt32("Min(\"abc\", 15, \"Tony Blue\")", 15);
+#endif
+  ExecuteExpectInt32("Min(\"abc\")", 0);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Mod) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Mod(64, -3)", 1}, {"Mod(-13, 3)", -1}, {"Mod(\"abc\", 2)", 0}};
+  ExecuteExpectInt32("Mod(64, -3)", 1);
+  ExecuteExpectInt32("Mod(-13, 3)", -1);
+  ExecuteExpectInt32("Mod(\"abc\", 2)", 0);
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectNaN("Mod(10, NaN)");
+  ExecuteExpectNaN("Mod(10, Infinity)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Round) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"Round(12.389764537, 4)", 12.3898f},
-               {"Round(20/3, 2)", 6.67f},
-               {"Round(8.9897, \"abc\")", 9.0f},
-               {"Round(FV(400, 0.10/12, 30*12), 2)", 904195.17f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber()) << "Program: " << tests[i].program;
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("Round(12.389764537, 4)", 12.3898f);
+  ExecuteExpectFloat("Round(20/3, 2)", 6.67f);
+  ExecuteExpectFloat("Round(8.9897, \"abc\")", 9.0f);
+  ExecuteExpectFloat("Round(FV(400, 0.10/12, 30*12), 2)", 904195.17f);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Sum) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Sum(2, 4, 6, 8)", 20},
-               {"Sum(-2, 4, -6, 8)", 4},
-               {"Sum(4, 16, \"abc\", 19)", 39}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("Sum(2, 4, 6, 8)", 20);
+  ExecuteExpectInt32("Sum(-2, 4, -6, 8)", 4);
+  ExecuteExpectInt32("Sum(4, 16, \"abc\", 19)", 39);
 }
 
 // TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Date) {
@@ -346,7 +286,7 @@
 
 //   EXPECT_TRUE(Execute("Date()"));
 
-//   CFXJSE_Value* value = GetValue();
+//   v8::Local<v8::Value> value = GetValue();
 //   EXPECT_TRUE(value->IsNumber());
 //   EXPECT_EQ(days, value->ToInteger());
 // }
@@ -354,203 +294,100 @@
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Date2Num) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {
-      // {"Date2Num(\"Mar 15, 1996\")", 35138},
-      {"Date2Num(\"1/1/1900\", \"D/M/YYYY\")", 1},
-      {"Date2Num(\"03/15/96\", \"MM/DD/YY\")", 35138},
-      // {"Date2Num(\"Aug 1, 1996\", \"MMM D, YYYY\")", 35277},
-      {"Date2Num(\"96-08-20\", \"YY-MM-DD\", \"fr_FR\")", 35296},
-      {"Date2Num(\"1/3/00\", \"D/M/YY\") - Date2Num(\"1/2/00\", \"D/M/YY\")",
-       29}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("Date2Num(\"1/1/1900\", \"D/M/YYYY\")", 1);
+  ExecuteExpectInt32("Date2Num(\"03/15/96\", \"MM/DD/YY\")", 35138);
+  ExecuteExpectInt32("Date2Num(\"96-08-20\", \"YY-MM-DD\", \"fr_FR\")", 35296);
+  ExecuteExpectInt32(
+      "Date2Num(\"1/3/00\", \"D/M/YY\") - Date2Num(\"1/2/00\", \"D/M/YY\")",
+      29);
+#if 0
+  // TODO(thestig): Investigate these cases.
+  ExecuteExpectInt32("Date2Num(\"Mar 15, 1996\")", 35138);
+  ExecuteExpectInt32("Date2Num(\"Aug 1, 1996\", \"MMM D, YYYY\")", 35277);
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, DateFmt) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      // {"DateFmt(1)", "M/D/YY"},
-      // {"DateFmt(2, \"fr_CA\")", "YY-MM-DD"},
-      {"DateFmt(3, \"de_DE\")", "D. MMMM YYYY"},
-      // {"DateFmt(4, \"fr_FR\")", "EEE D' MMMM YYYY"}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("DateFmt(3, \"de_DE\")", "D. MMMM YYYY");
+#if 0
+  // TODO(thestig): Investigate these cases.
+  ExecuteExpectString("DateFmt(1)", "M/D/YY");
+  ExecuteExpectString("DateFmt(2, \"fr_CA\")", "YY-MM-DD");
+  ExecuteExpectString("DateFmt(4, \"fr_FR\")", "EEE D' MMMM YYYY");
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, IsoDate2Num) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"IsoDate2Num(\"1900\")", 1},
-               {"IsoDate2Num(\"1900-01\")", 1},
-               {"IsoDate2Num(\"1900-01-01\")", 1},
-               {"IsoDate2Num(\"19960315T20:20:20\")", 35138},
-               {"IsoDate2Num(\"2000-03-01\") - IsoDate2Num(\"20000201\")", 29}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("IsoDate2Num(\"1900\")", 1);
+  ExecuteExpectInt32("IsoDate2Num(\"1900-01\")", 1);
+  ExecuteExpectInt32("IsoDate2Num(\"1900-01-01\")", 1);
+  ExecuteExpectInt32("IsoDate2Num(\"19960315T20:20:20\")", 35138);
+  ExecuteExpectInt32("IsoDate2Num(\"2000-03-01\") - IsoDate2Num(\"20000201\")",
+                     29);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_IsoTime2Num) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"IsoTime2Num(\"00:00:00Z\")", 1}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("IsoTime2Num(\"00:00:00Z\")", 1);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, LocalDateFmt) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {// {"LocalDateFmt(1, \"de_DE\")", "tt.MM.uu"},
-               // {"LocalDateFmt(2, \"fr_CA\")", "aa-MM-jj"},
-               {"LocalDateFmt(3, \"de_CH\")", "t. MMMM jjjj"},
-               {"LocalDateFmt(4, \"fr_FR\")", "EEEE j MMMM aaaa"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("LocalDateFmt(3, \"de_CH\")", "t. MMMM jjjj");
+  ExecuteExpectString("LocalDateFmt(4, \"fr_FR\")", "EEEE j MMMM aaaa");
+#if 0
+  // TODO(thestig): Investigate these cases.
+  ExecuteExpectString("LocalDateFmt(1, \"de_DE\")", "tt.MM.uu");
+  ExecuteExpectString("LocalDateFmt(2, \"fr_CA\")", "aa-MM-jj");
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_LocalTimeFmt) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"LocalTimeFmt(1, \"de_DE\")", "HH:mm"},
-               {"LocalTimeFmt(2, \"fr_CA\")", "HH:mm::ss"},
-               {"LocalTimeFmt(3, \"de_CH\")", "HH:mm:ss z"},
-               {"LocalTimeFmt(4, \"fr_FR\")", "HH' h 'mm z"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("LocalTimeFmt(1, \"de_DE\")", "HH:mm");
+  ExecuteExpectString("LocalTimeFmt(2, \"fr_CA\")", "HH:mm::ss");
+  ExecuteExpectString("LocalTimeFmt(3, \"de_CH\")", "HH:mm:ss z");
+  ExecuteExpectString("LocalTimeFmt(4, \"fr_FR\")", "HH' h 'mm z");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Num2Date) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      {"Num2Date(1, \"DD/MM/YYYY\")", "01/01/1900"},
-      {"Num2Date(35139, \"DD-MMM-YYYY\", \"de_DE\")", "16-Mrz-1996"},
-      // {"Num2Date(Date2Num(\"Mar 15, 2000\") - Date2Num(\"98-03-15\", "
-      //  "\"YY-MM-DD\", \"fr_CA\"))",
-      //  "Jan 1, 1902"}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString()) << "Program: " << tests[i].program;
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Num2Date(1, \"DD/MM/YYYY\")", "01/01/1900");
+  ExecuteExpectString("Num2Date(35139, \"DD-MMM-YYYY\", \"de_DE\")",
+                      "16-Mrz-1996");
+#if 0
+  // TODO(thestig): Investigate this case.
+  ExecuteExpectString(
+      "Num2Date(Date2Num(\"Mar 15, 2000\") - Date2Num(\"98-03-15\", "
+      "\"YY-MM-DD\", \"fr_CA\"))",
+      "Jan 1, 1902");
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Num2GMTime) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {// Broken on Windows only.
-               {"Num2GMTime(1, \"HH:MM:SS\")", "00:00:00"},
-               // Below broken on other platforms.
-               {"Num2GMTime(65593001, \"HH:MM:SS Z\")", "18:13:13 GMT"},
-               {"Num2GMTime(43993001, TimeFmt(4, \"de_DE\"), \"de_DE\")",
-                "12.13 Uhr GMT"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  // Broken on Windows only.
+  ExecuteExpectString("Num2GMTime(1, \"HH:MM:SS\")", "00:00:00");
+  // Below broken on other platforms.
+  ExecuteExpectString("Num2GMTime(65593001, \"HH:MM:SS Z\")", "18:13:13 GMT");
+  ExecuteExpectString("Num2GMTime(43993001, TimeFmt(4, \"de_DE\"), \"de_DE\")",
+                      "12.13 Uhr GMT");
 }
 
 // TODO(dsinclair): Broken on Mac ...
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Num2Time) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Num2Time(1, \"HH:MM:SS\")", "00:00:00"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Num2Time(1, \"HH:MM:SS\")", "00:00:00");
 }
 
 // TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Time) {
@@ -561,7 +398,7 @@
 
 //   EXPECT_TRUE(Execute("Time()"));
 
-//   CFXJSE_Value* value = GetValue();
+//   v8::Local<v8::Value> value = GetValue();
 //   EXPECT_TRUE(value->IsInteger());
 //   EXPECT_EQ(tp.tv_sec * 1000L + tp.tv_usec / 1000, value->ToInteger())
 //       << "Program: Time()";
@@ -570,653 +407,348 @@
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Time2Num) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {
-      // {"Time2Num(\"00:00:00 GMT\", \"HH:MM:SS Z\")", 1},
-      {"Time2Num(\"13:13:13 GMT\", \"HH:MM:SS Z\", \"fr_FR\")", 47593001}};
+  ExecuteExpectInt32("Time2Num(\"00:00:00 GMT\", \"HH:MM:SS Z\")", 1);
+  ExecuteExpectInt32("Time2Num(\"00:00:01 GMT\", \"HH:MM:SS Z\")", 1001);
+  ExecuteExpectInt32("Time2Num(\"00:01:00 GMT\", \"HH:MM:SS Z\")", 60001);
+  ExecuteExpectInt32("Time2Num(\"01:00:00 GMT\", \"HH:MM:SS Z\")", 3600001);
+  ExecuteExpectInt32("Time2Num(\"23:59:59 GMT\", \"HH:MM:SS Z\")", 86399001);
+  // https://crbug.com/pdfium/1257
+  ExecuteExpectInt32("Time2Num(\"\", \"\", 1)", 0);
+  ExecuteExpectInt32("Time2Num(\"13:13:13 GMT\", \"HH:MM:SS Z\", \"fr_FR\")",
+                     47593001);
+}
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Time2NumWithTZ) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
+  static constexpr const char* kTimeZones[] = {
+      "UTC+14",   "UTC-14",   "UTC+9:30", "UTC-0:30",
+      "UTC+0:30", "UTC-0:01", "UTC+0:01"};
+  for (const char* tz : kTimeZones) {
+    ScopedSetTZ scoped_set_tz(tz);
+    ExecuteExpectInt32("Time2Num(\"00:00:00 GMT\", \"HH:MM:SS Z\")", 1);
+    ExecuteExpectInt32("Time2Num(\"11:59:59 GMT\", \"HH:MM:SS Z\")", 43199001);
+    ExecuteExpectInt32("Time2Num(\"12:00:00 GMT\", \"HH:MM:SS Z\")", 43200001);
+    ExecuteExpectInt32("Time2Num(\"23:59:59 GMT\", \"HH:MM:SS Z\")", 86399001);
+  }
+  {
+    ScopedSetTZ scoped_set_tz("UTC-3:00");
+    ExecuteExpectInt32("Time2Num(\"1:13:13 PM\")", 36793001);
+    ExecuteExpectInt32(
+        "Time2Num(\"13:13:13 GMT\", \"HH:MM:SS Z\") - "
+        "Time2Num(\"13:13:13\", \"HH:MM:SS\")",
+        10800000);
   }
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, TimeFmt) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      // {"TimeFmt(1)", "h::MM A"},
-      {"TimeFmt(2, \"fr_CA\")", "HH:MM:SS"},
-      {"TimeFmt(3, \"fr_FR\")", "HH:MM:SS Z"},
-      // {"TimeFmt(4, \"de_DE\")", "H.MM' Uhr 'Z"}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("TimeFmt(2, \"fr_CA\")", "HH:MM:SS");
+  ExecuteExpectString("TimeFmt(3, \"fr_FR\")", "HH:MM:SS Z");
+#if 0
+  // TODO(thestig): Investigate these cases.
+  ExecuteExpectString("TimeFmt(1)", "h::MM A");
+  ExecuteExpectString("TimeFmt(4, \"de_DE\")", "H.MM' Uhr 'Z");
+#endif
 }
 
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Apr) {
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Apr) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"Apr(35000, 269.50, 360)", 0.08515404566f},
-               {"Apr(210000 * 0.75, 850 + 110, 25 * 26)", 0.07161332404f}};
+  ExecuteExpectFloatNear("Apr(35000, 269.50, 360)", 0.08515404566f);
+  ExecuteExpectFloatNear("Apr(210000 * 0.75, 850 + 110, 25 * 26)",
+                         0.07161332404f);
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectError("Apr(2, 2, 2147483648)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, CTerm) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {
-      // {"CTerm(0.02, 1000, 100)", 116.2767474515f},
-      {"CTerm(0.10, 500000, 12000)", 39.13224648502f},
-      // {"CTerm(0.0275 + 0.0025, 1000000, 55000 * 0.10)", 176.02226044975f}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("CTerm(0.10, 500000, 12000)", 39.13224648502f);
+#if 0
+  // TODO(thestig): Investigate these cases.
+  ExecuteExpectFloat("CTerm(0.02, 1000, 100)", 116.2767474515f);
+  ExecuteExpectFloat("CTerm(0.0275 + 0.0025, 1000000, 55000 * 0.10)",
+                     176.02226044975f);
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, FV) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"FV(400, 0.10 / 12, 30 * 12)", 904195.16991842445f},
-               {"FV(1000, 0.075 / 4, 10 * 4)", 58791.96145535981f}};
+  ExecuteExpectFloat("FV(400, 0.10 / 12, 30 * 12)", 904195.16991842445f);
+  ExecuteExpectFloat("FV(1000, 0.075 / 4, 10 * 4)", 58791.96145535981f);
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectError("FV(2, 2, 2147483648)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, IPmt) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"IPmt(30000, 0.085, 295.50, 7, 3)", 624.8839283142f},
-               {"IPmt(160000, 0.0475, 980, 24, 12)", 7103.80833569485f},
-               {"IPmt(15000, 0.065, 65.50, 15, 1)", 0.0f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("IPmt(30000, 0.085, 295.50, 7, 3)", 624.8839283142f);
+  ExecuteExpectFloat("IPmt(160000, 0.0475, 980, 24, 12)", 7103.80833569485f);
+  ExecuteExpectFloat("IPmt(15000, 0.065, 65.50, 15, 1)", 0.0f);
 }
 
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_NPV) {
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, NPV) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"NPV(0.065, 5000)", 4694.83568075117f},
-               {"NPV(0.10, 500, 1500, 4000, 10000)", 11529.60863329007f},
-               {"NPV(0.0275 / 12, 50, 60, 40, 100, 25)", 273.14193838457f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber()) << "Program: " << tests[i].program;
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("NPV(0.065, 5000)", 4694.83568075117f);
+  ExecuteExpectFloat("NPV(0.10, 500, 1500, 4000, 10000)", 11529.60863329007f);
+  ExecuteExpectFloat("NPV(0.0275 / 12, 50, 60, 40, 100, 25)", 273.14193838457f);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Pmt) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {// {"Pmt(150000, 0.0475 / 12, 25 * 12)", 855.17604207164f},
-               {"Pmt(25000, 0.085, 12)", 3403.82145169876f}};
+  ExecuteExpectFloat("Pmt(25000, 0.085, 12)", 3403.82145169876f);
+  ExecuteExpectFloat("Pmt(5000, 0.01, 1)", 5050);
+  ExecuteExpectFloat("Pmt(5000, 0.01, 1.5)", 5050);
+  ExecuteExpectFloat("Pmt(30000.00, .085 / 12, 12 * 12)", 333.01666929435f);
+  ExecuteExpectFloat("Pmt(10000, .08 / 12, 10)", 1037.03208935916f);
+  ExecuteExpectFloat("Pmt(150000, 0.0475 / 12, 25 * 12)", 855.17604207164f);
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  // https://crbug.com/1293179
+  ExecuteExpectError("Pmt(2, 2, 99999997952)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, PPmt) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {
-      {"PPmt(30000, 0.085, 295.50, 7, 3)", 261.6160716858f},
-      {"PPmt(160000, 0.0475, 980, 24, 12)", 4656.19166430515f},
-      // {"PPmt(15000, 0.065, 65.50, 15, 1)", 0.0f}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("PPmt(30000, 0.085, 295.50, 7, 3)", 261.6160716858f);
+  ExecuteExpectFloat("PPmt(160000, 0.0475, 980, 24, 12)", 4656.19166430515f);
+#if 0
+  // TODO(thestig): Investigate this case.
+  ExecuteExpectFloat("PPmt(15000, 0.065, 65.50, 15, 1)", 0.0f);
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, PV) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {
-      {"PV(400, 0.10 / 12, 30 * 12)", 45580.32799074439f},
-      // {"PV(1000, 0.075 / 4, 10 * 4)", 58791.96145535981f}
-  };
+  ExecuteExpectFloat("PV(400, 0.10 / 12, 30 * 12)", 45580.32799074439f);
+  ExecuteExpectFloat("PV(1000, 0.075 / 4, 10 * 4)", 27964.88770467326f);
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  // https://crbug.com/1296840
+  ExecuteExpectError("PV(2, 2, 2147483648)");
 }
 
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Rate) {
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Rate) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"Rate(12000, 8000, 5)", 0.0844717712f},
-               {"Rate(10000, 0.25 * 5000, 4 * 12)", 0.04427378243f}};
+  ExecuteExpectFloatNear("Rate(12000, 8000, 5)", 0.0844717712f);
+  ExecuteExpectFloatNear("Rate(10000, 0.25 * 5000, 4 * 12)", 0.04427378243f);
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectError("Rate(2, 2, 2147483648)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Term) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {// {"Term(475, .05, 1500)", 3.00477517728f},
-               {"Term(2500, 0.0275 + 0.0025, 5000)", 1.97128786369f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("Term(2500, 0.0275 + 0.0025, 5000)", 1.97128786369f);
+#if 0
+  // TODO(thestig): Investigate this case.
+  ExecuteExpectFloat("Term(475, .05, 1500)", 3.00477517728f);
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Choose) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      {"Choose(3, \"Taxes\", \"Price\", \"Person\", \"Teller\")", "Person"},
-      {"Choose(2, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)", "9"},
-      {"Choose(20/3, \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\")",
-       "F"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Choose(3, \"Taxes\", \"Price\", \"Person\", \"Teller\")",
+                      "Person");
+  ExecuteExpectString("Choose(2, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)", "9");
+  ExecuteExpectString(
+      "Choose(20/3, \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\")",
+      "F");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Exists) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  EXPECT_TRUE(Execute("Exists(\"hello world\")"));
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsInteger());
-  EXPECT_FALSE(value->ToBoolean());
+  ExecuteExpectBool("Exists(\"hello world\")", false);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, HasValue) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    bool result;
-  } tests[] = {{"HasValue(2)", true}, {"HasValue(\" \")", false}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program;
-    EXPECT_EQ(tests[i].result, value->ToBoolean())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectBool("HasValue(2)", true);
+  ExecuteExpectBool("HasValue(\" \")", false);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Oneof) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    bool result;
-  } tests[] = {
-      {"Oneof(3, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)", true},
-      {"Oneof(\"John\", \"Bill\", \"Gary\", \"Joan\", \"John\", \"Lisa\")",
-       true},
-      {"Oneof(3, 1, 25)", false},
-      {"Oneof(3, 3, null)", true},
-      {"Oneof(3, null, null)", false},
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program;
-    EXPECT_EQ(tests[i].result, value->ToBoolean())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectBool("Oneof(3, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)", true);
+  ExecuteExpectBool(
+      "Oneof(\"John\", \"Bill\", \"Gary\", \"Joan\", \"John\", \"Lisa\")",
+      true);
+  ExecuteExpectBool("Oneof(3, 1, 25)", false);
+  ExecuteExpectBool("Oneof(3, 3, null)", true);
+  ExecuteExpectBool("Oneof(3, null, null)", false);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Within) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    bool result;
-  } tests[] = {{"Within(\"C\", \"A\", \"D\")", true},
-               {"Within(1.5, 0, 2)", true},
-               {"Within(-1, 0, 2)", false}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program;
-    EXPECT_EQ(tests[i].result, value->ToBoolean())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectBool("Within(\"C\", \"A\", \"D\")", true);
+  ExecuteExpectBool("Within(1.5, 0, 2)", true);
+  ExecuteExpectBool("Within(-1, 0, 2)", false);
 }
 
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Eval) {
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Eval) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"eval(\"10*3+5*4\")", 50}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("eval(\"10*3+5*4\")", 50);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Null) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Null()", "null"},
-               {"Concat(\"ABC\", Null(), \"DEF\")", "ABCDEF"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
-
-  EXPECT_TRUE(Execute("Null() + 5"));
-
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsInteger());
-  EXPECT_EQ(5, value->ToInteger());
+  ExecuteExpectString("Null()", "null");
+  ExecuteExpectString("Concat(\"ABC\", Null(), \"DEF\")", "ABCDEF");
+  ExecuteExpectInt32("Null() + 5", 5);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Ref) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Ref(\"10*3+5*4\")", "10*3+5*4"}, {"Ref(\"hello\")", "hello"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Ref(\"10*3+5*4\")", "10*3+5*4");
+  ExecuteExpectString("Ref(\"hello\")", "hello");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, UnitType) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"UnitType(\"36 in\")", "in"},
-               {"UnitType(\"2.54centimeters\")", "cm"},
-               {"UnitType(\"picas\")", "pt"},
-               {"UnitType(\"2.cm\")", "cm"},
-               {"UnitType(\"2.zero cm\")", "in"},
-               {"UnitType(\"kilometers\")", "in"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("UnitType(\"36 in\")", "in");
+  ExecuteExpectString("UnitType(\"2.54centimeters\")", "cm");
+  ExecuteExpectString("UnitType(\"picas\")", "pt");
+  ExecuteExpectString("UnitType(\"2.cm\")", "cm");
+  ExecuteExpectString("UnitType(\"2.zero cm\")", "in");
+  ExecuteExpectString("UnitType(\"kilometers\")", "in");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, UnitValue) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {
-      {"UnitValue(\"2in\")", 2.0f}, {"UnitValue(\"2in\", \"cm\")", 5.08f},
-      // {"UnitValue(\"6\", \"pt\")", 432f},
-      // {"UnitType(\"A\", \"cm\")", 0.0f},
-      // {"UnitType(\"5.08cm\", \"kilograms\")", 2.0f}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("UnitValue(\"2in\")", 2.0f);
+  ExecuteExpectFloat("UnitValue(\"2in\", \"cm\")", 5.08f);
+#if 0
+  // TODO(thestig): Investigate these cases.
+  // Should the UnitType cases move into the UnitType test case?
+  ExecuteExpectFloat("UnitValue(\"6\", \"pt\")", 432f);
+  ExecuteExpectFloat("UnitType(\"A\", \"cm\")", 0.0f);
+  ExecuteExpectFloat("UnitType(\"5.08cm\", \"kilograms\")", 2.0f);
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, At) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"At(\"ABCDEFGH\", \"AB\")", 1},
-               {"At(\"ABCDEFGH\", \"F\")", 6},
-               {"At(23412931298471, 29)", 5}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("At(\"ABCDEFGH\", \"AB\")", 1);
+  ExecuteExpectInt32("At(\"ABCDEFGH\", \"F\")", 6);
+  ExecuteExpectInt32("At(23412931298471, 29)", 5);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Concat) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Concat(\"ABC\", \"DEF\")", "ABCDEF"},
-               {"Concat(\"Tony\", Space(1), \"Blue\")", "Tony Blue"},
-               {"Concat(\"You owe \", WordNum(1154.67, 2), \".\")",
-                "You owe One Thousand One Hundred Fifty-four Dollars And "
-                "Sixty-seven Cents."}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Concat(\"ABC\", \"DEF\")", "ABCDEF");
+  ExecuteExpectString("Concat(\"Tony\", Space(1), \"Blue\")", "Tony Blue");
+  ExecuteExpectString("Concat(\"You owe \", WordNum(1154.67, 2), \".\")",
+                      "You owe One Thousand One Hundred Fifty-four Dollars And "
+                      "Sixty-seven Cents.");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Decode) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      // HTML
-      {R"(Decode("", "html"))", ""},
-      {R"(Decode("abc&Acirc;xyz", "html"))", "abc\xC3\x82xyz"},
-      {R"(Decode("abc&NoneSuchButVeryLongIndeed;", "html"))", "abc"},
-      {R"(Decode("&#x0041;&AElig;&Aacute;", "html"))", "A\xC3\x86\xC3\x81"},
-      {R"(Decode("xyz&#", "html"))", "xyz"},
-      {R"(Decode("|&zzzzzz;|", "html"))", "||"},
+  // HTML
+  ExecuteExpectString(R"(Decode("", "html"))", "");
+  ExecuteExpectString(R"(Decode("abc&Acirc;xyz", "html"))", "abc\xC3\x82xyz");
+  ExecuteExpectString(R"(Decode("abc&NoneSuchButVeryLongIndeed;", "html"))",
+                      "abc");
+  ExecuteExpectString(R"(Decode("&#x0041;&AElig;&Aacute;", "html"))",
+                      "A\xC3\x86\xC3\x81");
+  ExecuteExpectString(R"(Decode("xyz&#", "html"))", "xyz");
+  ExecuteExpectString(R"(Decode("|&zzzzzz;|", "html"))", "||");
 
-      // XML
-      {R"(Decode("", "xml"))", ""},
-      {R"(Decode("~!@#$%%^&amp;*()_+|`", "xml"))", "~!@#$%%^&*()_+|`"},
-      {R"(Decode("abc&nonesuchbutverylongindeed;", "xml"))", "abc"},
-      {R"(Decode("&quot;&#x45;&lt;&gt;[].&apos;", "xml"))", "\"E<>[].'"},
-      {R"(Decode("xyz&#", "xml"))", "xyz"},
-      {R"(Decode("|&zzzzzz;|", "xml"))", "||"},
+  // XML
+  ExecuteExpectString(R"(Decode("", "xml"))", "");
+  ExecuteExpectString(R"(Decode("~!@#$%%^&amp;*()_+|`", "xml"))",
+                      "~!@#$%%^&*()_+|`");
+  ExecuteExpectString(R"(Decode("abc&nonesuchbutverylongindeed;", "xml"))",
+                      "abc");
+  ExecuteExpectString(R"(Decode("&quot;&#x45;&lt;&gt;[].&apos;", "xml"))",
+                      "\"E<>[].'");
+  ExecuteExpectString(R"(Decode("xyz&#", "xml"))", "xyz");
+  ExecuteExpectString(R"(Decode("|&zzzzzz;|", "xml"))", "||");
 
-      // URL
-      {R"(Decode("", "url"))", ""},
-      {R"(Decode("~%26^&*()_+|`{", "url"))", "~&^&*()_+|`{"},
-      {R"(Decode("~%26^&*()_+|`{", "mbogo"))", "~&^&*()_+|`{"},
-      {R"(Decode("~%26^&*()_+|`{"))", "~&^&*()_+|`{"},
-      {R"(Decode("~%~~"))", ""},
-      {R"(Decode("?%~"))", ""},
-      {R"(Decode("?%"))", "?"},
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  // URL
+  ExecuteExpectString(R"(Decode("", "url"))", "");
+  ExecuteExpectString(R"(Decode("~%26^&*()_+|`{", "url"))", "~&^&*()_+|`{");
+  ExecuteExpectString(R"(Decode("~%26^&*()_+|`{", "mbogo"))", "~&^&*()_+|`{");
+  ExecuteExpectString(R"(Decode("~%26^&*()_+|`{"))", "~&^&*()_+|`{");
+  ExecuteExpectString(R"(Decode("~%~~"))", "");
+  ExecuteExpectString(R"(Decode("?%~"))", "");
+  ExecuteExpectString(R"(Decode("?%"))", "?");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Encode) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-    {"Encode(\"X/~&^*<=>?|\")", "X%2f%7e%26%5e*%3c%3d%3e%3f%7c"},
-    {"Encode(\"X/~&^*<=>?|\", \"mbogo\")", "X%2f%7e%26%5e*%3c%3d%3e%3f%7c"},
-    {"Encode(\"X/~&^*<=>?|\", \"url\")", "X%2f%7e%26%5e*%3c%3d%3e%3f%7c"},
-    {"Encode(\"X/~&^*<=>?|\", \"xml\")", "X/~&amp;^*&lt;=&gt;?|"},
-    {"Encode(\"X/~&^*<=>?|\", \"html\")", "X/~&amp;^*&lt;=&gt;?|"},
+  ExecuteExpectString("Encode(\"X/~&^*<=>?|\")",
+                      "X%2f%7e%26%5e*%3c%3d%3e%3f%7c");
+  ExecuteExpectString("Encode(\"X/~&^*<=>?|\", \"mbogo\")",
+                      "X%2f%7e%26%5e*%3c%3d%3e%3f%7c");
+  ExecuteExpectString("Encode(\"X/~&^*<=>?|\", \"url\")",
+                      "X%2f%7e%26%5e*%3c%3d%3e%3f%7c");
+  ExecuteExpectString("Encode(\"X/~&^*<=>?|\", \"xml\")",
+                      "X/~&amp;^*&lt;=&gt;?|");
+  ExecuteExpectString("Encode(\"X/~&^*<=>?|\", \"html\")",
+                      "X/~&amp;^*&lt;=&gt;?|");
 
-    {"Encode(\"\\u0022\\u00f5\\ufed0\", \"url\")", "%22%f5%fe%d0"},
-    {"Encode(\"\\u0022\\u00f4\\ufed0\", \"xml\")", "&quot;&#xf4;&#xfed0;"},
-    {"Encode(\"\\u0022\\u00f5\\ufed0\", \"html\")", "&quot;&otilde;&#xfed0;"},
+  ExecuteExpectString("Encode(\"\\u0022\\u00f5\\ufed0\", \"url\")",
+                      "%22%f5%fe%d0");
+  ExecuteExpectString("Encode(\"\\u0022\\u00f4\\ufed0\", \"xml\")",
+                      "&quot;&#xf4;&#xfed0;");
+  ExecuteExpectString("Encode(\"\\u0022\\u00f5\\ufed0\", \"html\")",
+                      "&quot;&otilde;&#xfed0;");
 
-#if !defined(OS_WIN)
-    // Windows wchar_t isn't wide enough to handle these anyways.
-    // TODO(tsepez): fix surrogate encodings.
-    {"Encode(\"\\uD83D\\uDCA9\", \"url\")", "%01%f4%a9"},
-    {"Encode(\"\\uD83D\\uDCA9\", \"xml\")", ""},
-    {"Encode(\"\\uD83D\\uDCA9\", \"html\")", ""},
-#endif  // !defined(OS_WIN)
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Encode(\"\\uD83D\\uDCA9\", \"url\")", "%01%f4%a9");
+  ExecuteExpectString("Encode(\"\\uD83D\\uDCA9\", \"xml\")", "");
+  ExecuteExpectString("Encode(\"\\uD83D\\uDCA9\", \"html\")", "");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Format) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Format(\"MMM D, YYYY\", \"20020901\")", "Sep 1, 2002"},
-               {"Format(\"$9,999,999.99\", 1234567.89)", "$1,234,567.89"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Format(\"MMM D, YYYY\", \"20020901\")", "Sep 1, 2002");
+  ExecuteExpectString("Format(\"$9,999,999.99\", 1234567.89)", "$1,234,567.89");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Left) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Left(\"ABCDEFGH\", 3)", "ABC"},
-               {"Left(\"Tony Blue\", 5)", "Tony "}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Left(\"ABCDEFGH\", 3)", "ABC");
+  ExecuteExpectString("Left(\"Tony Blue\", 5)", "Tony ");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Len) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {
-      {"Len(\"ABCDEFGH\")", 8}, {"Len(4)", 1}, {"Len(Str(4.532, 6, 4))", 6}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("Len(\"ABCDEFGH\")", 8);
+  ExecuteExpectInt32("Len(4)", 1);
+  ExecuteExpectInt32("Len(Str(4.532, 6, 4))", 6);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Lower) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Lower(\"ABC\")", "abc"},
-               {"Lower(\"21 Main St.\")", "21 main st."},
-               {"Lower(15)", "15"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Lower(\"ABC\")", "abc");
+  ExecuteExpectString("Lower(\"21 Main St.\")", "21 main st.");
+  ExecuteExpectString("Lower(15)", "15");
 }
 
 // This is testing for an OOB read, so will likely only fail under ASAN.
@@ -1232,311 +764,262 @@
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Ltrim) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Ltrim(\"   ABCD\")", "ABCD"},
-               {"Ltrim(Rtrim(\"    Tony Blue    \"))", "Tony Blue"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Ltrim(\"   ABCD\")", "ABCD");
+  ExecuteExpectString("Ltrim(Rtrim(\"    Tony Blue    \"))", "Tony Blue");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Parse) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Parse(\"MMM D, YYYY\", \"Sep 1, 2002\")", "2002-09-01"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
-
-  EXPECT_TRUE(Execute("Parse(\"$9,999,999.99\", \"$1,234,567.89\")"));
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsNumber());
-  EXPECT_FLOAT_EQ(1234567.89f, value->ToFloat());
+  ExecuteExpectString("Parse(\"MMM D, YYYY\", \"Sep 1, 2002\")", "2002-09-01");
+  ExecuteExpectFloat("Parse(\"$9,999,999.99\", \"$1,234,567.89\")",
+                     1234567.89f);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Replace) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Replace(\"Tony Blue\", \"Tony\", \"Chris\")", "Chris Blue"},
-               {"Replace(\"ABCDEFGH\", \"D\")", "ABCEFGH"},
-               {"Replace(\"ABCDEFGH\", \"d\")", "ABCDEFGH"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Replace(\"Tony Blue\", \"Tony\", \"Chris\")",
+                      "Chris Blue");
+  ExecuteExpectString("Replace(\"ABCDEFGH\", \"D\")", "ABCEFGH");
+  ExecuteExpectString("Replace(\"ABCDEFGH\", \"d\")", "ABCDEFGH");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Right) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Right(\"ABCDEFGH\", 3)", "FGH"},
-               {"Right(\"Tony Blue\", 5)", " Blue"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Right(\"ABCDEFGH\", 3)", "FGH");
+  ExecuteExpectString("Right(\"Tony Blue\", 5)", " Blue");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Rtrim) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Rtrim(\"ABCD   \")", "ABCD"},
-               {"Rtrim(\"Tony Blue      \t\")", "Tony Blue"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Rtrim(\"ABCD   \")", "ABCD");
+  ExecuteExpectString("Rtrim(\"Tony Blue      \t\")", "Tony Blue");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Space) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Space(5)", "     "},
-               {"Concat(\"Tony\", Space(1), \"Blue\")", "Tony Blue"}};
+  ExecuteExpectString("Space(5)", "     ");
+  ExecuteExpectString("Concat(\"Tony\", Space(1), \"Blue\")", "Tony Blue");
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  // Error cases.
+  ExecuteExpectError("Space(15654909)");
+  ExecuteExpectError("Space(99999999)");
+  ExecuteExpectError("Space()");
+  ExecuteExpectError("Space(1, 2)");
+  ExecuteExpectNull("Space( $)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Str) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Str(2.456)", "         2"},
-               {"Str(4.532, 6, 4)", "4.5320"},
-               {"Str(234.458, 4)", " 234"},
-               {"Str(31.2345, 4, 2)", "****"}};
+  ExecuteExpectString("Str(2.456)", "         2");
+  ExecuteExpectString("Str(4.532, 6, 4)", "4.5320");
+  ExecuteExpectString("Str(234.458, 4)", " 234");
+  ExecuteExpectString("Str(31.2345, 4, 2)", "****");
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
+  // Test maximum "n3" precision value.
+  ExecuteExpectString("Str(-765, 19, 14)", "-765.00000000000000");
+  ExecuteExpectString("Str(-765, 20, 15)", "-765.000000000000000");
+  ExecuteExpectString("Str(-765, 21, 16)", " -765.000000000000000");
 
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  // Error cases.
+  ExecuteExpectError("Str()");
+  ExecuteExpectError("Str(1, 2, 3, 4)");
+  ExecuteExpectError("Str(42, 15654909)");
+  ExecuteExpectNull("Str( $)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Stuff) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Stuff(\"TonyBlue\", 5, 0, \" \")", "Tony Blue"},
-               {"Stuff(\"ABCDEFGH\", 4, 2)", "ABCFGH"},
-               {"Stuff(\"members-list@myweb.com\", 0, 0, \"cc:\")",
-                "cc:members-list@myweb.com"}};
+  // Test wrong number of parameters.
+  ExecuteExpectError("Stuff(1, 2)");
+  ExecuteExpectError("Stuff(1, 2, 3, 4, 5)");
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
+  // Test null arguments.
+  ExecuteExpectNull("Stuff(null, 0, 4)");
+  ExecuteExpectNull("Stuff(\"ABCDEFG\", null, 4)");
+  ExecuteExpectNull("Stuff(\"ABCDEFG\", 0, null)");
 
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  // Insertions.
+  ExecuteExpectString("Stuff(\"\", 0, 0, \"clams\")", "clams");
+  ExecuteExpectString("Stuff(\"TonyBlue\", 5, 0, \" \")", "Tony Blue");
+
+  // Deletions.
+  ExecuteExpectString("Stuff(\"A\", 1, 0)", "A");
+  ExecuteExpectString("Stuff(\"A\", 1, 1)", "");
+  ExecuteExpectString("Stuff(\"ABCDEFGH\", 4, 2)", "ABCFGH");
+  ExecuteExpectString("Stuff(\"ABCDEFGH\", 7, 2)", "ABCDEF");
+
+  // Test index clamping.
+  ExecuteExpectString("Stuff(\"ABCDEFGH\", -400, 400)", "");
+
+  // Need significant amount of text to test start + count overflow due to
+  // intermediate float representation of count not being able to hold
+  // INT_MAX.
+  ExecuteExpectString(
+      "Stuff(\""
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900"
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900"
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900"
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900"
+      "\", 133, 2147483520)",
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900"
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900"
+      "abcd");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Substr) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
   // Test wrong number of parameters.
-  EXPECT_FALSE(Execute("Substr()"));
-  EXPECT_FALSE(Execute("Substr(1)"));
-  EXPECT_FALSE(Execute("Substr(1, 2)"));
-  EXPECT_FALSE(Execute("Substr(1, 2, 3, 4)"));
+  ExecuteExpectError("Substr()");
+  ExecuteExpectError("Substr(1)");
+  ExecuteExpectError("Substr(1, 2)");
+  ExecuteExpectError("Substr(1, 2, 3, 4)");
 
   // Test null input.
-  EXPECT_TRUE(ExecuteExpectNull("Substr(null, 0, 4)"));
-  EXPECT_TRUE(ExecuteExpectNull("Substr(\"ABCDEFG\", null, 4)"));
-  EXPECT_TRUE(ExecuteExpectNull("Substr(\"ABCDEFG\", 0, null)"));
-  EXPECT_TRUE(ExecuteExpectNull("Substr(null, null, 4)"));
-  EXPECT_TRUE(ExecuteExpectNull("Substr(null, 0, null)"));
-  EXPECT_TRUE(ExecuteExpectNull("Substr(\"ABCDEFG\", null, null)"));
-  EXPECT_TRUE(ExecuteExpectNull("Substr(null, null, null)"));
+  ExecuteExpectNull("Substr(null, 0, 4)");
+  ExecuteExpectNull("Substr(\"ABCDEFG\", null, 4)");
+  ExecuteExpectNull("Substr(\"ABCDEFG\", 0, null)");
+  ExecuteExpectNull("Substr(null, null, 4)");
+  ExecuteExpectNull("Substr(null, 0, null)");
+  ExecuteExpectNull("Substr(\"ABCDEFG\", null, null)");
+  ExecuteExpectNull("Substr(null, null, null)");
 
-  struct {
-    const char* program;
-    const char* result;
-  } static const kTests[] = {{"Substr(\"ABCDEFG\", -1, 4)", "ABCD"},
-                             {"Substr(\"ABCDEFG\", 0, 4)", "ABCD"},
-                             {"Substr(\"ABCDEFG\", 3, 4)", "CDEF"},
-                             {"Substr(\"ABCDEFG\", 4, 4)", "DEFG"},
-                             {"Substr(\"ABCDEFG\", 5, 4)", "EFG"},
-                             {"Substr(\"ABCDEFG\", 6, 4)", "FG"},
-                             {"Substr(\"ABCDEFG\", 7, 4)", "G"},
-                             {"Substr(\"ABCDEFG\", 8, 4)", ""},
-                             {"Substr(\"ABCDEFG\", 5, -1)", ""},
-                             {"Substr(\"ABCDEFG\", 5, 0)", ""},
-                             {"Substr(\"ABCDEFG\", 5, 1)", "E"},
-                             {"Substr(\"abcdefghi\", 5, 3)", "efg"},
-                             {"Substr(3214, 2, 1)", "2"},
-                             {"Substr(\"21 Waterloo St.\", 4, 5)", "Water"}};
-
-  for (const auto& test : kTests) {
-    EXPECT_TRUE(Execute(test.program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(test.result, value->ToString().c_str())
-        << "Program: " << test.program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Substr(\"ABCDEFG\", -1, 4)", "ABCD");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 0, 4)", "ABCD");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 3, 4)", "CDEF");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 4, 4)", "DEFG");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 5, 4)", "EFG");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 6, 4)", "FG");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 7, 4)", "G");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 8, 4)", "");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 5, -1)", "");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 5, 0)", "");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 5, 1)", "E");
+  ExecuteExpectString("Substr(\"abcdefghi\", 5, 3)", "efg");
+  ExecuteExpectString("Substr(3214, 2, 1)", "2");
+  ExecuteExpectString("Substr(\"21 Waterloo St.\", 4, 5)", "Water");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Uuid) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
   EXPECT_TRUE(Execute("Uuid()"));
 
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsString());
+  CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+  v8::Local<v8::Value> value = GetValue();
+  EXPECT_TRUE(fxv8::IsString(value));
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Upper) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Upper(\"abc\")", "ABC"},
-               {"Upper(\"21 Main St.\")", "21 MAIN ST."},
-               {"Upper(15)", "15"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Upper(\"abc\")", "ABC");
+  ExecuteExpectString("Upper(\"21 Main St.\")", "21 MAIN ST.");
+  ExecuteExpectString("Upper(15)", "15");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, WordNum) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      // {"WordNum(123.45)",
-      //  "One Hundred and Twenty-three"},  // This looks like it's wrong in the
-      //                                    // Formcalc document.
-      // {"WordNum(123.45, 1)", "One Hundred and Twenty-three Dollars"},
-      {"WordNum(1154.67, 2)",
-       "One Thousand One Hundred Fifty-four Dollars And Sixty-seven Cents"},
-      {"WordNum(43, 2)", "Forty-three Dollars And Zero Cents"}};
+  // Wrong number of parameters.
+  ExecuteExpectError("WordNum()");
+  ExecuteExpectError("WordNum(1, 2, 3, 4)");
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
+  // Normal format codes.
+  ExecuteExpectString("WordNum(123.45)", "One Hundred Twenty-three");
+  ExecuteExpectString("WordNum(123.45, 0)", "One Hundred Twenty-three");
+  ExecuteExpectString("WordNum(123.45, 1)", "One Hundred Twenty-three Dollars");
+  ExecuteExpectString("WordNum(123.45, 2)",
+                      "One Hundred Twenty-three Dollars And Forty-five Cents");
 
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  // Invalid format code.
+  ExecuteExpectString("WordNum(123.45, -1)", "");
+  ExecuteExpectString("WordNum(123.45, 3)", "");
+
+  // Locale string is ignored.
+  ExecuteExpectString("WordNum(123.45, 0, \"zh_CN\")",
+                      "One Hundred Twenty-three");
+
+  // Zero (and near zero) values.
+  ExecuteExpectString("WordNum(0, 0)", "Zero");
+  ExecuteExpectString("WordNum(0, 1)", "Zero Dollars");
+  ExecuteExpectString("WordNum(0, 2)", "Zero Dollars And Zero Cents");
+  ExecuteExpectString("WordNum(0.12, 0)", "Zero");
+  ExecuteExpectString("WordNum(0.12, 1)", "Zero Dollars");
+  ExecuteExpectString("WordNum(0.12, 2)", "Zero Dollars And Twelve Cents");
+
+  // Negative values.
+  ExecuteExpectString("WordNum(-1, 0)", "*");
+  ExecuteExpectString("WordNum(-1, 1)", "*");
+  ExecuteExpectString("WordNum(-1, 2)", "*");
+
+  // Test larger values
+  // TODO(tsepez): check on "Thousand Zero"
+  ExecuteExpectString("WordNum(1.234e+6)",
+                      "One Million Two Hundred Thirty-four Thousand Zero");
+
+  // TODO(tsepez): check on "Zero Thousand Zero"
+  ExecuteExpectString(
+      "WordNum(1.234e+9)",
+      "One Billion Two Hundred Thirty-four Million Zero Thousand Zero");
+
+  // TODO(tsepez): check on "Zero Million"
+  ExecuteExpectString(
+      "WordNum(1.234e+12)",
+      "One Trillion Two Hundred Thirty-four Billion Zero Million Nineteen "
+      "Thousand Four Hundred Fifty-six");
+
+  ExecuteExpectString(
+      "WordNum(1.234e+15)",
+      "One Thousand Two Hundred Thirty-three Trillion Nine Hundred Ninety-nine "
+      "Billion Nine Hundred Thirty-eight Million Seven Hundred Fifteen "
+      "Thousand "
+      "Six Hundred Forty-eight");
+
+  // Out-of-range.
+  ExecuteExpectString("WordNum(1.234e+18)", "*");
+  ExecuteExpectString("WordNum(1.234e+21)", "*");
+  ExecuteExpectString("WordNum(1.234e+24)", "*");
+  ExecuteExpectString("WordNum(1.234e+30)", "*");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Get) {
-  // TODO(dsinclair): Is this supported?
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+  ExecuteExpectString("Get(\"https://example.com\")", "<body>secrets</body>");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Post) {
-  // TODO(dsinclair): Is this supported?
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+  ExecuteExpectString(
+      "Post(\"http://example.com\", \"secret stuff\", \"text/plain\")",
+      "posted");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Put) {
-  // TODO(dsinclair): Is this supported?
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+  ExecuteExpectString("Put(\"http://example.com\", \"secret stuff\")", "");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, InvalidFunctions) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  const char* const tests[] = {
-      "F()",
-      "()",
-      "()()()",
-      "Round(2.0)()",
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_FALSE(ExecuteSilenceFailure(tests[i]));
-  }
+  EXPECT_FALSE(ExecuteSilenceFailure("F()"));
+  EXPECT_FALSE(ExecuteSilenceFailure("()"));
+  EXPECT_FALSE(ExecuteSilenceFailure("()()()"));
+  EXPECT_FALSE(ExecuteSilenceFailure("Round(2.0)()"));
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, MethodCall) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
   const char test[] = {"$form.form1.TextField11.getAttribute(\"h\")"};
-  EXPECT_TRUE(Execute(test));
-
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsString());
-  EXPECT_STREQ("12.7mm", value->ToString().c_str());
+  ExecuteExpectString(test, "12.7mm");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, GetXFAEventChange) {
@@ -1546,15 +1029,10 @@
   params.m_wsChange = L"changed";
 
   CFXJSE_Engine* context = GetScriptContext();
-  context->SetEventParam(&params);
+  CFXJSE_Engine::EventParamScope event_scope(context, nullptr, &params);
 
   const char test[] = {"xfa.event.change"};
-  EXPECT_TRUE(Execute(test));
-
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsString());
-  EXPECT_STREQ("changed", value->ToString().c_str());
-  context->SetEventParam(nullptr);
+  ExecuteExpectString(test, "changed");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, SetXFAEventChange) {
@@ -1562,12 +1040,11 @@
 
   CXFA_EventParam params;
   CFXJSE_Engine* context = GetScriptContext();
-  context->SetEventParam(&params);
+  CFXJSE_Engine::EventParamScope event_scope(context, nullptr, &params);
 
   const char test[] = {"xfa.event.change = \"changed\""};
   EXPECT_TRUE(Execute(test));
   EXPECT_EQ(L"changed", params.m_wsChange);
-  context->SetEventParam(nullptr);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, SetXFAEventFullTextFails) {
@@ -1577,12 +1054,11 @@
   params.m_wsFullText = L"Original Full Text";
 
   CFXJSE_Engine* context = GetScriptContext();
-  context->SetEventParam(&params);
+  CFXJSE_Engine::EventParamScope event_scope(context, nullptr, &params);
 
   const char test[] = {"xfa.event.fullText = \"Changed Full Text\""};
   EXPECT_TRUE(Execute(test));
   EXPECT_EQ(L"Original Full Text", params.m_wsFullText);
-  context->SetEventParam(nullptr);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, EventChangeSelection) {
@@ -1594,7 +1070,7 @@
   params.m_iSelEnd = 3;
 
   CFXJSE_Engine* context = GetScriptContext();
-  context->SetEventParam(&params);
+  CFXJSE_Engine::EventParamScope event_scope(context, nullptr, &params);
 
   // Moving end to start works fine.
   EXPECT_TRUE(Execute("xfa.event.selEnd = \"1\""));
@@ -1637,8 +1113,6 @@
   EXPECT_TRUE(Execute("xfa.event.selStart = \"20\""));
   EXPECT_EQ(4, params.m_iSelStart);
   EXPECT_EQ(4, params.m_iSelEnd);
-
-  context->SetEventParam(nullptr);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, XFAEventCancelAction) {
@@ -1648,18 +1122,10 @@
   params.m_bCancelAction = false;
 
   CFXJSE_Engine* context = GetScriptContext();
-  context->SetEventParam(&params);
-
-  EXPECT_TRUE(Execute("xfa.event.cancelAction"));
-
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsBoolean());
-  EXPECT_FALSE(value->ToBoolean());
-
+  CFXJSE_Engine::EventParamScope event_scope(context, nullptr, &params);
+  ExecuteExpectBool("xfa.event.cancelAction", false);
   EXPECT_TRUE(Execute("xfa.event.cancelAction = \"true\""));
   EXPECT_TRUE(params.m_bCancelAction);
-
-  context->SetEventParam(nullptr);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, ComplexTextChangeEvent) {
@@ -1672,7 +1138,7 @@
   params.m_iSelEnd = 3;
 
   CFXJSE_Engine* context = GetScriptContext();
-  context->SetEventParam(&params);
+  CFXJSE_Engine::EventParamScope event_scope(context, nullptr, &params);
 
   EXPECT_EQ(L"abcd", params.m_wsPrevText);
   EXPECT_EQ(L"agd", params.GetNewText());
@@ -1697,13 +1163,10 @@
   EXPECT_EQ(L"axyzbcd", params.GetNewText());
   EXPECT_EQ(1, params.m_iSelStart);
   EXPECT_EQ(1, params.m_iSelEnd);
-
-  context->SetEventParam(nullptr);
 }
 
 // Should not crash.
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, BUG_1223) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  EXPECT_FALSE(Execute("!.somExpression=0"));
+  EXPECT_TRUE(Execute("!.somExpression=0"));
 }
diff --git a/fxjs/xfa/cfxjse_formcalc_context_unittest.cpp b/fxjs/xfa/cfxjse_formcalc_context_unittest.cpp
new file mode 100644
index 0000000..ee9437e
--- /dev/null
+++ b/fxjs/xfa/cfxjse_formcalc_context_unittest.cpp
@@ -0,0 +1,143 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/xfa/cfxjse_formcalc_context.h"
+
+#include "core/fxcrt/bytestring.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(FormCalcContextTest, GenerateSomExpression) {
+  ByteString result =
+      CFXJSE_FormCalcContext::GenerateSomExpression("", 0, 0, /*bIsStar=*/true);
+  EXPECT_EQ(result, "[*]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("foo", 0, 0,
+                                                         /*bIsStar=*/true);
+  EXPECT_EQ(result, "foo[*]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("foo", 0, 0,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "foo");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("fu", 1, 0,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "fu[0]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("food", 1, 99,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "food[99]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("foot", 1, -65,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "foot[-65]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("football", 2, 0,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "football[0]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("foosball", 2, 123,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "foosball[+123]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("bar", 2, -654,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "bar[-654]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("barb", 2, 2147483647,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "barb[+2147483647]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression(
+      "bart", 2, -2147483648, /*bIsStar=*/false);
+  EXPECT_EQ(result, "bart[-0]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("bark", 3, 0,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "bark[0]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("bard", 3, 357,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "bard[-357]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("bars", 3, -9876,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "bars[9876]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("cars", 3, 2147483647,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "cars[-2147483647]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression(
+      "mars", 3, -2147483648, /*bIsStar=*/false);
+  EXPECT_EQ(result, "mars[0]");
+}
+
+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/cfxjse_isolatetracker.cpp b/fxjs/xfa/cfxjse_isolatetracker.cpp
index 6a29e44..939553d 100644
--- a/fxjs/xfa/cfxjse_isolatetracker.cpp
+++ b/fxjs/xfa/cfxjse_isolatetracker.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,20 +6,38 @@
 
 #include "fxjs/xfa/cfxjse_isolatetracker.h"
 
+#include "fxjs/xfa/cfxjse_context.h"
 #include "fxjs/xfa/cfxjse_runtimedata.h"
 
 CFXJSE_ScopeUtil_IsolateHandle::CFXJSE_ScopeUtil_IsolateHandle(
     v8::Isolate* pIsolate)
-    : m_iscope(pIsolate), m_hscope(pIsolate) {}
+    : isolate_scope_(pIsolate), handle_scope_(pIsolate) {}
 
 CFXJSE_ScopeUtil_IsolateHandle::~CFXJSE_ScopeUtil_IsolateHandle() = default;
 
+CFXJSE_ScopeUtil_Context::CFXJSE_ScopeUtil_Context(CFXJSE_Context* pContext)
+    : context_scope_(pContext->GetContext()) {}
+
+CFXJSE_ScopeUtil_Context::~CFXJSE_ScopeUtil_Context() = default;
+
+CFXJSE_ScopeUtil_IsolateHandleContext::CFXJSE_ScopeUtil_IsolateHandleContext(
+    CFXJSE_Context* pContext)
+    : isolate_handle_(pContext->GetIsolate()), context_(pContext) {}
+
+CFXJSE_ScopeUtil_IsolateHandleContext::
+    ~CFXJSE_ScopeUtil_IsolateHandleContext() = default;
+
+CFXJSE_ScopeUtil_RootContext::CFXJSE_ScopeUtil_RootContext(
+    v8::Isolate* pIsolate)
+    : context_scope_(v8::Local<v8::Context>::New(
+          pIsolate,
+          CFXJSE_RuntimeData::Get(pIsolate)->GetRootContext())) {}
+
+CFXJSE_ScopeUtil_RootContext::~CFXJSE_ScopeUtil_RootContext() = default;
+
 CFXJSE_ScopeUtil_IsolateHandleRootContext::
     CFXJSE_ScopeUtil_IsolateHandleRootContext(v8::Isolate* pIsolate)
-    : CFXJSE_ScopeUtil_IsolateHandle(pIsolate),
-      m_cscope(v8::Local<v8::Context>::New(
-          pIsolate,
-          CFXJSE_RuntimeData::Get(pIsolate)->m_hRootContext)) {}
+    : isolate_handle_(pIsolate), root_context_(pIsolate) {}
 
 CFXJSE_ScopeUtil_IsolateHandleRootContext::
     ~CFXJSE_ScopeUtil_IsolateHandleRootContext() = default;
diff --git a/fxjs/xfa/cfxjse_isolatetracker.h b/fxjs/xfa/cfxjse_isolatetracker.h
index 020142d..7353ac0 100644
--- a/fxjs/xfa/cfxjse_isolatetracker.h
+++ b/fxjs/xfa/cfxjse_isolatetracker.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,16 @@
 #ifndef FXJS_XFA_CFXJSE_ISOLATETRACKER_H_
 #define FXJS_XFA_CFXJSE_ISOLATETRACKER_H_
 
-#include "v8/include/v8.h"
+#include "core/fxcrt/fx_memory.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-isolate.h"
+
+class CFXJSE_Context;
 
 class CFXJSE_ScopeUtil_IsolateHandle {
  public:
+  FX_STACK_ALLOCATED();
+
   explicit CFXJSE_ScopeUtil_IsolateHandle(v8::Isolate* pIsolate);
   CFXJSE_ScopeUtil_IsolateHandle(const CFXJSE_ScopeUtil_IsolateHandle&) =
       delete;
@@ -19,16 +25,57 @@
   ~CFXJSE_ScopeUtil_IsolateHandle();
 
  private:
-  void* operator new(size_t size) = delete;
-  void operator delete(void*, size_t) = delete;
-
-  v8::Isolate::Scope m_iscope;
-  v8::HandleScope m_hscope;
+  v8::Isolate::Scope isolate_scope_;
+  v8::HandleScope handle_scope_;
 };
 
-class CFXJSE_ScopeUtil_IsolateHandleRootContext final
-    : public CFXJSE_ScopeUtil_IsolateHandle {
+class CFXJSE_ScopeUtil_Context {
  public:
+  FX_STACK_ALLOCATED();
+
+  explicit CFXJSE_ScopeUtil_Context(CFXJSE_Context* pContext);
+  CFXJSE_ScopeUtil_Context(const CFXJSE_ScopeUtil_Context&) = delete;
+  CFXJSE_ScopeUtil_Context& operator=(const CFXJSE_ScopeUtil_Context&) = delete;
+  ~CFXJSE_ScopeUtil_Context();
+
+ private:
+  v8::Context::Scope context_scope_;
+};
+
+class CFXJSE_ScopeUtil_IsolateHandleContext {
+ public:
+  FX_STACK_ALLOCATED();
+
+  explicit CFXJSE_ScopeUtil_IsolateHandleContext(CFXJSE_Context* pContext);
+  CFXJSE_ScopeUtil_IsolateHandleContext(
+      const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete;
+  CFXJSE_ScopeUtil_IsolateHandleContext& operator=(
+      const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete;
+  ~CFXJSE_ScopeUtil_IsolateHandleContext();
+
+ private:
+  CFXJSE_ScopeUtil_IsolateHandle isolate_handle_;
+  CFXJSE_ScopeUtil_Context context_;
+};
+
+class CFXJSE_ScopeUtil_RootContext {
+ public:
+  FX_STACK_ALLOCATED();
+
+  explicit CFXJSE_ScopeUtil_RootContext(v8::Isolate* pIsolate);
+  CFXJSE_ScopeUtil_RootContext(const CFXJSE_ScopeUtil_RootContext&) = delete;
+  CFXJSE_ScopeUtil_RootContext& operator=(const CFXJSE_ScopeUtil_RootContext&) =
+      delete;
+  ~CFXJSE_ScopeUtil_RootContext();
+
+ private:
+  v8::Context::Scope context_scope_;
+};
+
+class CFXJSE_ScopeUtil_IsolateHandleRootContext {
+ public:
+  FX_STACK_ALLOCATED();
+
   explicit CFXJSE_ScopeUtil_IsolateHandleRootContext(v8::Isolate* pIsolate);
   CFXJSE_ScopeUtil_IsolateHandleRootContext(
       const CFXJSE_ScopeUtil_IsolateHandleRootContext&) = delete;
@@ -37,10 +84,8 @@
   ~CFXJSE_ScopeUtil_IsolateHandleRootContext();
 
  private:
-  void* operator new(size_t size) = delete;
-  void operator delete(void*, size_t) = delete;
-
-  v8::Context::Scope m_cscope;
+  CFXJSE_ScopeUtil_IsolateHandle isolate_handle_;
+  CFXJSE_ScopeUtil_RootContext root_context_;
 };
 
 #endif  // FXJS_XFA_CFXJSE_ISOLATETRACKER_H_
diff --git a/fxjs/xfa/cfxjse_mapmodule.cpp b/fxjs/xfa/cfxjse_mapmodule.cpp
new file mode 100644
index 0000000..7e656b8
--- /dev/null
+++ b/fxjs/xfa/cfxjse_mapmodule.cpp
@@ -0,0 +1,78 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/xfa/cfxjse_mapmodule.h"
+
+#include "third_party/base/containers/contains.h"
+#include "xfa/fxfa/parser/cxfa_measurement.h"
+
+CFXJSE_MapModule::CFXJSE_MapModule() = default;
+
+CFXJSE_MapModule::~CFXJSE_MapModule() = default;
+
+void CFXJSE_MapModule::SetValue(uint32_t key, int32_t value) {
+  m_StringMap.erase(key);
+  m_MeasurementMap.erase(key);
+  m_ValueMap[key] = value;
+}
+
+void CFXJSE_MapModule::SetString(uint32_t key, const WideString& wsString) {
+  m_ValueMap.erase(key);
+  m_MeasurementMap.erase(key);
+  m_StringMap[key] = wsString;
+}
+
+void CFXJSE_MapModule::SetMeasurement(uint32_t key,
+                                      const CXFA_Measurement& measurement) {
+  m_ValueMap.erase(key);
+  m_StringMap.erase(key);
+  m_MeasurementMap[key] = measurement;
+}
+
+absl::optional<int32_t> CFXJSE_MapModule::GetValue(uint32_t key) const {
+  auto it = m_ValueMap.find(key);
+  if (it == m_ValueMap.end())
+    return absl::nullopt;
+  return it->second;
+}
+
+absl::optional<WideString> CFXJSE_MapModule::GetString(uint32_t key) const {
+  auto it = m_StringMap.find(key);
+  if (it == m_StringMap.end())
+    return absl::nullopt;
+  return it->second;
+}
+
+absl::optional<CXFA_Measurement> CFXJSE_MapModule::GetMeasurement(
+    uint32_t key) const {
+  auto it = m_MeasurementMap.find(key);
+  if (it == m_MeasurementMap.end())
+    return absl::nullopt;
+  return it->second;
+}
+
+bool CFXJSE_MapModule::HasKey(uint32_t key) const {
+  return pdfium::Contains(m_ValueMap, key) ||
+         pdfium::Contains(m_StringMap, key) ||
+         pdfium::Contains(m_MeasurementMap, key);
+}
+
+void CFXJSE_MapModule::RemoveKey(uint32_t key) {
+  m_ValueMap.erase(key);
+  m_StringMap.erase(key);
+  m_MeasurementMap.erase(key);
+}
+
+void CFXJSE_MapModule::MergeDataFrom(const CFXJSE_MapModule* pSrc) {
+  for (const auto& pair : pSrc->m_ValueMap)
+    SetValue(pair.first, pair.second);
+
+  for (const auto& pair : pSrc->m_StringMap)
+    SetString(pair.first, pair.second);
+
+  for (const auto& pair : pSrc->m_MeasurementMap)
+    SetMeasurement(pair.first, pair.second);
+}
diff --git a/fxjs/xfa/cfxjse_mapmodule.h b/fxjs/xfa/cfxjse_mapmodule.h
new file mode 100644
index 0000000..9c75750
--- /dev/null
+++ b/fxjs/xfa/cfxjse_mapmodule.h
@@ -0,0 +1,44 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_XFA_CFXJSE_MAPMODULE_H_
+#define FXJS_XFA_CFXJSE_MAPMODULE_H_
+
+#include <stdint.h>
+
+#include <map>
+
+#include "core/fxcrt/widestring.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+class CXFA_Measurement;
+
+class CFXJSE_MapModule {
+ public:
+  CFXJSE_MapModule();
+  ~CFXJSE_MapModule();
+
+  CFXJSE_MapModule(const CFXJSE_MapModule& that) = delete;
+  CFXJSE_MapModule& operator=(const CFXJSE_MapModule& that) = delete;
+
+  void SetValue(uint32_t key, int32_t value);
+  void SetString(uint32_t key, const WideString& wsString);
+  void SetMeasurement(uint32_t key, const CXFA_Measurement& measurement);
+  absl::optional<int32_t> GetValue(uint32_t key) const;
+  absl::optional<WideString> GetString(uint32_t key) const;
+  absl::optional<CXFA_Measurement> GetMeasurement(uint32_t key) const;
+  bool HasKey(uint32_t key) const;
+  void RemoveKey(uint32_t key);
+  void MergeDataFrom(const CFXJSE_MapModule* pSrc);
+
+ private:
+  // keyed by result of GetMapKey_*().
+  std::map<uint32_t, int32_t> m_ValueMap;
+  std::map<uint32_t, WideString> m_StringMap;
+  std::map<uint32_t, CXFA_Measurement> m_MeasurementMap;
+};
+
+#endif  // FXJS_XFA_CFXJSE_MAPMODULE_H_
diff --git a/fxjs/xfa/cfxjse_mapmodule_unittest.cpp b/fxjs/xfa/cfxjse_mapmodule_unittest.cpp
new file mode 100644
index 0000000..3119b58
--- /dev/null
+++ b/fxjs/xfa/cfxjse_mapmodule_unittest.cpp
@@ -0,0 +1,124 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/xfa/cfxjse_mapmodule.h"
+
+#include "core/fxcrt/fx_string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "xfa/fxfa/parser/cxfa_measurement.h"
+
+TEST(CFXJSEMapModule, EmptyModule) {
+  CFXJSE_MapModule module;
+  EXPECT_FALSE(module.HasKey(1));
+  EXPECT_FALSE(module.HasKey(2));
+  EXPECT_FALSE(module.HasKey(3));
+  EXPECT_FALSE(module.GetValue(1).has_value());
+  EXPECT_FALSE(module.GetString(2).has_value());
+  EXPECT_FALSE(module.GetMeasurement(3).has_value());
+}
+
+TEST(CFXJSEMapModule, InsertDelete) {
+  const int value = 101;
+  WideString str(L"foo");
+  CXFA_Measurement measure(L"1 pt");
+  CFXJSE_MapModule module;
+
+  module.SetValue(100, value);
+  module.SetString(200, str);
+  module.SetMeasurement(300, measure);
+  EXPECT_TRUE(module.HasKey(100));
+  EXPECT_TRUE(module.HasKey(200));
+  EXPECT_TRUE(module.HasKey(300));
+
+  EXPECT_EQ(module.GetValue(100).value(), value);
+  EXPECT_FALSE(module.GetString(100).has_value());
+  EXPECT_FALSE(module.GetMeasurement(100).has_value());
+
+  EXPECT_FALSE(module.GetValue(200).has_value());
+  EXPECT_EQ(module.GetString(200).value(), str);
+  EXPECT_FALSE(module.GetMeasurement(200).has_value());
+
+  EXPECT_FALSE(module.GetValue(300).has_value());
+  EXPECT_FALSE(module.GetString(300).has_value());
+  EXPECT_EQ(module.GetMeasurement(300).value().GetUnit(), measure.GetUnit());
+  EXPECT_EQ(module.GetMeasurement(300).value().GetValue(), measure.GetValue());
+
+  module.RemoveKey(100);
+  module.RemoveKey(200);
+  module.RemoveKey(300);
+  EXPECT_FALSE(module.HasKey(100));
+  EXPECT_FALSE(module.HasKey(200));
+  EXPECT_FALSE(module.HasKey(300));
+  EXPECT_FALSE(module.GetValue(100).has_value());
+  EXPECT_FALSE(module.GetString(200).has_value());
+  EXPECT_FALSE(module.GetMeasurement(200).has_value());
+}
+
+TEST(CFXJSEMapModule, KeyCollision) {
+  const int value = 37;
+  WideString str(L"foo");
+  CXFA_Measurement measure(L"1 pt");
+  CFXJSE_MapModule module;
+
+  module.SetValue(100, value);
+  EXPECT_TRUE(module.HasKey(100));
+  EXPECT_EQ(module.GetValue(100).value(), value);
+  EXPECT_FALSE(module.GetString(100).has_value());
+  EXPECT_FALSE(module.GetMeasurement(100).has_value());
+
+  module.SetString(100, str);
+  EXPECT_TRUE(module.HasKey(100));
+  EXPECT_FALSE(module.GetValue(100).has_value());
+  EXPECT_EQ(module.GetString(100).value(), str);
+  EXPECT_FALSE(module.GetMeasurement(100).has_value());
+
+  module.SetMeasurement(100, measure);
+  EXPECT_FALSE(module.GetValue(100).has_value());
+  EXPECT_FALSE(module.GetString(100).has_value());
+  EXPECT_EQ(module.GetMeasurement(100).value().GetUnit(), measure.GetUnit());
+
+  module.SetValue(100, value);
+  EXPECT_TRUE(module.HasKey(100));
+  EXPECT_EQ(module.GetValue(100).value(), value);
+  EXPECT_FALSE(module.GetString(100).has_value());
+  EXPECT_FALSE(module.GetMeasurement(100).has_value());
+}
+
+TEST(CFXJSEMapModule, MergeData) {
+  const int value1 = 42;
+  const int value2 = -1999;
+  WideString string1(L"foo");
+  WideString string2(L"foo");
+  CXFA_Measurement measure1(L"1 pt");
+  CXFA_Measurement measure2(L"2 mm");
+  CFXJSE_MapModule module1;
+  CFXJSE_MapModule module2;
+
+  module1.SetValue(100, value1);
+  module1.SetValue(101, value1);
+  module1.SetString(200, string1);
+  module1.SetString(201, string1);
+  module1.SetMeasurement(300, measure1);
+  module1.SetMeasurement(301, measure1);
+
+  module2.SetString(100, string2);
+  module2.SetMeasurement(200, measure2);
+  module2.SetValue(300, value2);
+
+  module1.MergeDataFrom(&module2);
+  EXPECT_EQ(module1.GetString(100).value(), string2);
+  EXPECT_EQ(module1.GetValue(101).value(), value1);
+  EXPECT_EQ(module1.GetMeasurement(200).value().GetUnit(), measure2.GetUnit());
+  EXPECT_EQ(module1.GetString(201).value(), string1);
+  EXPECT_EQ(module1.GetValue(300).value(), value2);
+  EXPECT_EQ(module1.GetMeasurement(301).value().GetUnit(), measure1.GetUnit());
+
+  // module2 is undisturbed.
+  EXPECT_EQ(module2.GetString(100).value(), string2);
+  EXPECT_EQ(module2.GetMeasurement(200).value().GetUnit(), measure2.GetUnit());
+  EXPECT_EQ(module2.GetValue(300).value(), value2);
+}
diff --git a/fxjs/xfa/cfxjse_nodehelper.cpp b/fxjs/xfa/cfxjse_nodehelper.cpp
new file mode 100644
index 0000000..de24aa4
--- /dev/null
+++ b/fxjs/xfa/cfxjse_nodehelper.cpp
@@ -0,0 +1,136 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/xfa/cfxjse_nodehelper.h"
+
+#include "core/fxcrt/fx_extension.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cjx_object.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
+#include "xfa/fxfa/parser/cxfa_localemgr.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/xfa_basic_data.h"
+#include "xfa/fxfa/parser/xfa_utils.h"
+
+CFXJSE_NodeHelper::CFXJSE_NodeHelper() = default;
+
+CFXJSE_NodeHelper::~CFXJSE_NodeHelper() = default;
+
+bool CFXJSE_NodeHelper::CreateNodeForCondition(const WideString& wsCondition) {
+  size_t szLen = wsCondition.GetLength();
+  WideString wsIndex(L"0");
+  bool bAll = false;
+  if (szLen == 0) {
+    m_iCreateFlag = CFXJSE_Engine::ResolveResult::Type::kCreateNodeOne;
+    return false;
+  }
+  if (wsCondition[0] != '[')
+    return false;
+
+  size_t i = 1;
+  for (; i < szLen; ++i) {
+    wchar_t ch = wsCondition[i];
+    if (ch == ' ')
+      continue;
+
+    if (ch == '*')
+      bAll = true;
+    break;
+  }
+  if (bAll) {
+    wsIndex = L"1";
+    m_iCreateFlag = CFXJSE_Engine::ResolveResult::Type::kCreateNodeAll;
+  } else {
+    m_iCreateFlag = CFXJSE_Engine::ResolveResult::Type::kCreateNodeOne;
+    wsIndex = wsCondition.Substr(i, szLen - 1 - i);
+  }
+  int32_t iCount = wsIndex.GetInteger();
+  if (iCount < 0)
+    return false;
+
+  m_iCreateCount = iCount;
+  return true;
+}
+
+bool CFXJSE_NodeHelper::CreateNode(const WideString& wsName,
+                                   const WideString& wsCondition,
+                                   bool bLastNode,
+                                   CFXJSE_Engine* pScriptContext) {
+  if (!m_pCreateParent)
+    return false;
+
+  WideStringView wsNameView = wsName.AsStringView();
+  bool bIsClassName = false;
+  bool bResult = false;
+  if (!wsNameView.IsEmpty() && wsNameView[0] == '!') {
+    wsNameView = wsNameView.Last(wsNameView.GetLength() - 1);
+    m_pCreateParent = ToNode(
+        pScriptContext->GetDocument()->GetXFAObject(XFA_HASHCODE_Datasets));
+  }
+  if (!wsNameView.IsEmpty() && wsNameView[0] == '#') {
+    bIsClassName = true;
+    wsNameView = wsNameView.Last(wsNameView.GetLength() - 1);
+  }
+  if (wsNameView.IsEmpty())
+    return false;
+
+  if (m_iCreateCount == 0)
+    CreateNodeForCondition(wsCondition);
+
+  if (bIsClassName) {
+    XFA_Element eType = XFA_GetElementByName(wsNameView);
+    if (eType == XFA_Element::Unknown)
+      return false;
+
+    for (size_t i = 0; i < m_iCreateCount; ++i) {
+      CXFA_Node* pNewNode = m_pCreateParent->CreateSamePacketNode(eType);
+      if (pNewNode) {
+        m_pCreateParent->InsertChildAndNotify(pNewNode, nullptr);
+        if (i == m_iCreateCount - 1) {
+          m_pCreateParent = pNewNode;
+        }
+        bResult = true;
+      }
+    }
+  } else {
+    XFA_Element eClassType = XFA_Element::DataGroup;
+    if (bLastNode) {
+      eClassType = m_eLastCreateType;
+    }
+    for (size_t i = 0; i < m_iCreateCount; ++i) {
+      CXFA_Node* pNewNode = m_pCreateParent->CreateSamePacketNode(eClassType);
+      if (pNewNode) {
+        pNewNode->JSObject()->SetAttributeByEnum(XFA_Attribute::Name,
+                                                 WideString(wsNameView), false);
+        pNewNode->CreateXMLMappingNode();
+        m_pCreateParent->InsertChildAndNotify(pNewNode, nullptr);
+        if (i == m_iCreateCount - 1) {
+          m_pCreateParent = pNewNode;
+        }
+        bResult = true;
+      }
+    }
+  }
+  if (!bResult)
+    m_pCreateParent = nullptr;
+
+  return bResult;
+}
+
+void CFXJSE_NodeHelper::SetCreateNodeType(CXFA_Node* refNode) {
+  if (!refNode)
+    return;
+
+  if (refNode->GetElementType() == XFA_Element::Subform) {
+    m_eLastCreateType = XFA_Element::DataGroup;
+  } else if (refNode->GetElementType() == XFA_Element::Field) {
+    m_eLastCreateType = XFA_FieldIsMultiListBox(refNode)
+                            ? XFA_Element::DataGroup
+                            : XFA_Element::DataValue;
+  } else if (refNode->GetElementType() == XFA_Element::ExclGroup) {
+    m_eLastCreateType = XFA_Element::DataValue;
+  }
+}
diff --git a/fxjs/xfa/cfxjse_nodehelper.h b/fxjs/xfa/cfxjse_nodehelper.h
new file mode 100644
index 0000000..7afc10c
--- /dev/null
+++ b/fxjs/xfa/cfxjse_nodehelper.h
@@ -0,0 +1,38 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_XFA_CFXJSE_NODEHELPER_H_
+#define FXJS_XFA_CFXJSE_NODEHELPER_H_
+
+#include "core/fxcrt/widestring.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "v8/include/cppgc/persistent.h"
+#include "xfa/fxfa/fxfa_basic.h"
+
+class CXFA_Node;
+
+class CFXJSE_NodeHelper {
+ public:
+  CFXJSE_NodeHelper();
+  ~CFXJSE_NodeHelper();
+
+  bool CreateNode(const WideString& wsName,
+                  const WideString& wsCondition,
+                  bool bLastNode,
+                  CFXJSE_Engine* pScriptContext);
+  bool CreateNodeForCondition(const WideString& wsCondition);
+  void SetCreateNodeType(CXFA_Node* refNode);
+
+  XFA_Element m_eLastCreateType = XFA_Element::DataValue;
+  CFXJSE_Engine::ResolveResult::Type m_iCreateFlag =
+      CFXJSE_Engine::ResolveResult::Type::kCreateNodeOne;
+  size_t m_iCreateCount = 0;
+  int32_t m_iCurAllStart = -1;
+  cppgc::Persistent<CXFA_Node> m_pCreateParent;
+  cppgc::Persistent<CXFA_Node> m_pAllStartParent;
+};
+
+#endif  // FXJS_XFA_CFXJSE_NODEHELPER_H_
diff --git a/fxjs/xfa/cfxjse_resolveprocessor.cpp b/fxjs/xfa/cfxjse_resolveprocessor.cpp
index 89883d6..bfe571c 100644
--- a/fxjs/xfa/cfxjse_resolveprocessor.cpp
+++ b/fxjs/xfa/cfxjse_resolveprocessor.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,114 +12,87 @@
 
 #include "core/fxcrt/fx_extension.h"
 #include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_nodehelper.h"
 #include "fxjs/xfa/cfxjse_value.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/contains.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_localemgr.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_nodehelper.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
 #include "xfa/fxfa/parser/cxfa_occur.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
-namespace {
-
-void DoPredicateFilter(WideString wsCondition,
-                       size_t iFoundCount,
-                       CFXJSE_ResolveNodeData* pRnd) {
-  ASSERT(iFoundCount == pRnd->m_Objects.size());
-  WideString wsExpression;
-  CXFA_Script::Type eLangType = CXFA_Script::Type::Unknown;
-  if (wsCondition.First(2).EqualsASCII(".[") && wsCondition.Back() == L']')
-    eLangType = CXFA_Script::Type::Formcalc;
-  else if (wsCondition.First(2).EqualsASCII(".(") && wsCondition.Back() == L')')
-    eLangType = CXFA_Script::Type::Javascript;
-  else
-    return;
-
-  wsExpression = wsCondition.Substr(2, wsCondition.GetLength() - 3);
-  for (size_t i = iFoundCount; i > 0; --i) {
-    auto pRetValue =
-        pdfium::MakeUnique<CFXJSE_Value>(pRnd->m_pSC->GetIsolate());
-    bool bRet =
-        pRnd->m_pSC->RunScript(eLangType, wsExpression.AsStringView(),
-                               pRetValue.get(), pRnd->m_Objects[i - 1].Get());
-    if (!bRet || !pRetValue->ToBoolean())
-      pRnd->m_Objects.erase(pRnd->m_Objects.begin() + i - 1);
-  }
-}
-
-}  // namespace
-
-CFXJSE_ResolveProcessor::CFXJSE_ResolveProcessor()
-    : m_pNodeHelper(pdfium::MakeUnique<CXFA_NodeHelper>()) {}
+CFXJSE_ResolveProcessor::CFXJSE_ResolveProcessor(CFXJSE_Engine* pEngine,
+                                                 CFXJSE_NodeHelper* pHelper)
+    : m_pEngine(pEngine), m_pNodeHelper(pHelper) {}
 
 CFXJSE_ResolveProcessor::~CFXJSE_ResolveProcessor() = default;
 
-bool CFXJSE_ResolveProcessor::Resolve(CFXJSE_ResolveNodeData& rnd) {
+bool CFXJSE_ResolveProcessor::Resolve(v8::Isolate* pIsolate, NodeData& rnd) {
   if (!rnd.m_CurObject)
     return false;
 
   if (!rnd.m_CurObject->IsNode()) {
-    if (rnd.m_dwStyles & XFA_RESOLVENODE_Attributes) {
-      return ResolveForAttributeRs(rnd.m_CurObject.Get(), rnd,
+    if (rnd.m_dwStyles & XFA_ResolveFlag::kAttributes) {
+      return ResolveForAttributeRs(rnd.m_CurObject, &rnd.m_Result,
                                    rnd.m_wsName.AsStringView());
     }
     return false;
   }
-  if (rnd.m_dwStyles & XFA_RESOLVENODE_AnyChild)
-    return ResolveAnyChild(rnd);
+  if (rnd.m_dwStyles & XFA_ResolveFlag::kAnyChild)
+    return ResolveAnyChild(pIsolate, rnd);
 
   if (rnd.m_wsName.GetLength()) {
     wchar_t wch = rnd.m_wsName[0];
     switch (wch) {
       case '$':
-        return ResolveDollar(rnd);
+        return ResolveDollar(pIsolate, rnd);
       case '!':
-        return ResolveExcalmatory(rnd);
+        return ResolveExcalmatory(pIsolate, rnd);
       case '#':
-        return ResolveNumberSign(rnd);
+        return ResolveNumberSign(pIsolate, rnd);
       case '*':
         return ResolveAsterisk(rnd);
       // TODO(dsinclair): We could probably remove this.
       case '.':
-        return ResolveAnyChild(rnd);
+        return ResolveAnyChild(pIsolate, rnd);
       default:
         break;
     }
   }
   if (rnd.m_uHashName == XFA_HASHCODE_This && rnd.m_nLevel == 0) {
-    rnd.m_Objects.emplace_back(rnd.m_pSC->GetThisObject());
+    rnd.m_Result.objects.emplace_back(m_pEngine->GetThisObject());
     return true;
   }
   if (rnd.m_CurObject->GetElementType() == XFA_Element::Xfa) {
     CXFA_Object* pObjNode =
-        rnd.m_pSC->GetDocument()->GetXFAObject(rnd.m_uHashName);
+        m_pEngine->GetDocument()->GetXFAObject(rnd.m_uHashName);
     if (pObjNode) {
-      rnd.m_Objects.emplace_back(pObjNode);
+      rnd.m_Result.objects.emplace_back(pObjNode);
     } else if (rnd.m_uHashName == XFA_HASHCODE_Xfa) {
-      rnd.m_Objects.push_back(rnd.m_CurObject);
-    } else if ((rnd.m_dwStyles & XFA_RESOLVENODE_Attributes) &&
-               ResolveForAttributeRs(rnd.m_CurObject.Get(), rnd,
+      rnd.m_Result.objects.emplace_back(rnd.m_CurObject);
+    } else if ((rnd.m_dwStyles & XFA_ResolveFlag::kAttributes) &&
+               ResolveForAttributeRs(rnd.m_CurObject, &rnd.m_Result,
                                      rnd.m_wsName.AsStringView())) {
       return true;
     }
-    if (!rnd.m_Objects.empty())
-      FilterCondition(rnd.m_wsCondition, &rnd);
+    if (!rnd.m_Result.objects.empty())
+      FilterCondition(pIsolate, rnd.m_wsCondition, &rnd);
 
-    return !rnd.m_Objects.empty();
+    return !rnd.m_Result.objects.empty();
   }
-  if (!ResolveNormal(rnd) && rnd.m_uHashName == XFA_HASHCODE_Xfa)
-    rnd.m_Objects.emplace_back(rnd.m_pSC->GetDocument()->GetRoot());
+  if (!ResolveNormal(pIsolate, rnd) && rnd.m_uHashName == XFA_HASHCODE_Xfa)
+    rnd.m_Result.objects.emplace_back(m_pEngine->GetDocument()->GetRoot());
 
-  return !rnd.m_Objects.empty();
+  return !rnd.m_Result.objects.empty();
 }
 
-bool CFXJSE_ResolveProcessor::ResolveAnyChild(CFXJSE_ResolveNodeData& rnd) {
-  CXFA_Node* pParent = ToNode(rnd.m_CurObject.Get());
+bool CFXJSE_ResolveProcessor::ResolveAnyChild(v8::Isolate* pIsolate,
+                                              NodeData& rnd) {
+  CXFA_Node* pParent = ToNode(rnd.m_CurObject);
   if (!pParent)
     return false;
 
@@ -134,128 +107,136 @@
     return false;
 
   if (wsCondition.IsEmpty()) {
-    rnd.m_Objects.emplace_back(pChild);
+    rnd.m_Result.objects.emplace_back(pChild);
     return true;
   }
 
   std::vector<CXFA_Node*> nodes;
-  for (const auto& pObject : rnd.m_Objects)
+  for (const auto& pObject : rnd.m_Result.objects)
     nodes.push_back(pObject->AsNode());
 
   std::vector<CXFA_Node*> siblings = pChild->GetSiblings(bClassName);
   nodes.insert(nodes.end(), siblings.begin(), siblings.end());
-  rnd.m_Objects =
-      std::vector<UnownedPtr<CXFA_Object>>(nodes.begin(), nodes.end());
-  FilterCondition(wsCondition, &rnd);
-  return !rnd.m_Objects.empty();
+  rnd.m_Result.objects =
+      std::vector<cppgc::Member<CXFA_Object>>(nodes.begin(), nodes.end());
+  FilterCondition(pIsolate, wsCondition, &rnd);
+  return !rnd.m_Result.objects.empty();
 }
 
-bool CFXJSE_ResolveProcessor::ResolveDollar(CFXJSE_ResolveNodeData& rnd) {
+bool CFXJSE_ResolveProcessor::ResolveDollar(v8::Isolate* pIsolate,
+                                            NodeData& rnd) {
   WideString wsName = rnd.m_wsName;
   WideString wsCondition = rnd.m_wsCondition;
-  int32_t iNameLen = wsName.GetLength();
-  if (iNameLen == 1) {
-    rnd.m_Objects.push_back(rnd.m_CurObject);
+  size_t nNameLen = wsName.GetLength();
+  if (nNameLen == 1) {
+    rnd.m_Result.objects.emplace_back(rnd.m_CurObject);
     return true;
   }
   if (rnd.m_nLevel > 0)
     return false;
 
+  CXFA_Document* pDocument = m_pEngine->GetDocument();
   XFA_HashCode dwNameHash = static_cast<XFA_HashCode>(
-      FX_HashCode_GetW(wsName.AsStringView().Last(iNameLen - 1), false));
+      FX_HashCode_GetW(wsName.AsStringView().Last(nNameLen - 1)));
   if (dwNameHash == XFA_HASHCODE_Xfa) {
-    rnd.m_Objects.emplace_back(rnd.m_pSC->GetDocument()->GetRoot());
+    rnd.m_Result.objects.emplace_back(pDocument->GetRoot());
   } else {
-    CXFA_Object* pObjNode = rnd.m_pSC->GetDocument()->GetXFAObject(dwNameHash);
+    CXFA_Object* pObjNode = pDocument->GetXFAObject(dwNameHash);
     if (pObjNode)
-      rnd.m_Objects.emplace_back(pObjNode);
+      rnd.m_Result.objects.emplace_back(pObjNode);
   }
-  if (!rnd.m_Objects.empty())
-    FilterCondition(wsCondition, &rnd);
-  return !rnd.m_Objects.empty();
+  if (!rnd.m_Result.objects.empty())
+    FilterCondition(pIsolate, wsCondition, &rnd);
+  return !rnd.m_Result.objects.empty();
 }
 
-bool CFXJSE_ResolveProcessor::ResolveExcalmatory(CFXJSE_ResolveNodeData& rnd) {
+bool CFXJSE_ResolveProcessor::ResolveExcalmatory(v8::Isolate* pIsolate,
+                                                 NodeData& rnd) {
   if (rnd.m_nLevel > 0)
     return false;
 
   CXFA_Node* datasets =
-      ToNode(rnd.m_pSC->GetDocument()->GetXFAObject(XFA_HASHCODE_Datasets));
+      ToNode(m_pEngine->GetDocument()->GetXFAObject(XFA_HASHCODE_Datasets));
   if (!datasets)
     return false;
 
-  CFXJSE_ResolveNodeData rndFind(rnd.m_pSC.Get());
+  NodeData rndFind;
   rndFind.m_CurObject = datasets;
   rndFind.m_wsName = rnd.m_wsName.Last(rnd.m_wsName.GetLength() - 1);
   rndFind.m_uHashName = static_cast<XFA_HashCode>(
-      FX_HashCode_GetW(rndFind.m_wsName.AsStringView(), false));
+      FX_HashCode_GetW(rndFind.m_wsName.AsStringView()));
   rndFind.m_nLevel = rnd.m_nLevel + 1;
-  rndFind.m_dwStyles = XFA_RESOLVENODE_Children;
+  rndFind.m_dwStyles = XFA_ResolveFlag::kChildren;
   rndFind.m_wsCondition = rnd.m_wsCondition;
-  Resolve(rndFind);
+  Resolve(pIsolate, rndFind);
 
-  rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                       rndFind.m_Objects.end());
-  return !rnd.m_Objects.empty();
+  rnd.m_Result.objects.insert(rnd.m_Result.objects.end(),
+                              rndFind.m_Result.objects.begin(),
+                              rndFind.m_Result.objects.end());
+  return !rnd.m_Result.objects.empty();
 }
 
-bool CFXJSE_ResolveProcessor::ResolveNumberSign(CFXJSE_ResolveNodeData& rnd) {
+bool CFXJSE_ResolveProcessor::ResolveNumberSign(v8::Isolate* pIsolate,
+                                                NodeData& rnd) {
   WideString wsName = rnd.m_wsName.Last(rnd.m_wsName.GetLength() - 1);
   WideString wsCondition = rnd.m_wsCondition;
-  CXFA_Node* curNode = ToNode(rnd.m_CurObject.Get());
-  if (ResolveForAttributeRs(curNode, rnd, wsName.AsStringView()))
+  CXFA_Node* curNode = ToNode(rnd.m_CurObject);
+  if (ResolveForAttributeRs(curNode, &rnd.m_Result, wsName.AsStringView()))
     return true;
 
-  CFXJSE_ResolveNodeData rndFind(rnd.m_pSC.Get());
+  NodeData rndFind;
   rndFind.m_nLevel = rnd.m_nLevel + 1;
   rndFind.m_dwStyles = rnd.m_dwStyles;
-  rndFind.m_dwStyles |= XFA_RESOLVENODE_TagName;
-  rndFind.m_dwStyles &= ~XFA_RESOLVENODE_Attributes;
+  rndFind.m_dwStyles |= XFA_ResolveFlag::kTagName;
+  rndFind.m_dwStyles.Clear(XFA_ResolveFlag::kAttributes);
   rndFind.m_wsName = std::move(wsName);
   rndFind.m_uHashName = static_cast<XFA_HashCode>(
-      FX_HashCode_GetW(rndFind.m_wsName.AsStringView(), false));
+      FX_HashCode_GetW(rndFind.m_wsName.AsStringView()));
   rndFind.m_wsCondition = wsCondition;
   rndFind.m_CurObject = curNode;
-  ResolveNormal(rndFind);
-  if (rndFind.m_Objects.empty())
+  ResolveNormal(pIsolate, rndFind);
+  if (rndFind.m_Result.objects.empty())
     return false;
 
   if (wsCondition.IsEmpty() &&
-      pdfium::ContainsValue(rndFind.m_Objects, curNode)) {
-    rnd.m_Objects.emplace_back(curNode);
+      pdfium::Contains(rndFind.m_Result.objects, curNode)) {
+    rnd.m_Result.objects.emplace_back(curNode);
   } else {
-    rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                         rndFind.m_Objects.end());
+    rnd.m_Result.objects.insert(rnd.m_Result.objects.end(),
+                                rndFind.m_Result.objects.begin(),
+                                rndFind.m_Result.objects.end());
   }
-  return !rnd.m_Objects.empty();
+  return !rnd.m_Result.objects.empty();
 }
 
-bool CFXJSE_ResolveProcessor::ResolveForAttributeRs(CXFA_Object* curNode,
-                                                    CFXJSE_ResolveNodeData& rnd,
-                                                    WideStringView strAttr) {
-  Optional<XFA_SCRIPTATTRIBUTEINFO> info =
+bool CFXJSE_ResolveProcessor::ResolveForAttributeRs(
+    CXFA_Object* curNode,
+    CFXJSE_Engine::ResolveResult* rnd,
+    WideStringView strAttr) {
+  absl::optional<XFA_SCRIPTATTRIBUTEINFO> info =
       XFA_GetScriptAttributeByName(curNode->GetElementType(), strAttr);
   if (!info.has_value())
     return false;
 
-  rnd.m_ScriptAttribute = info.value();
-  rnd.m_Objects.emplace_back(curNode);
-  rnd.m_dwFlag = XFA_ResolveNode_RSType_Attribute;
+  rnd->type = CFXJSE_Engine::ResolveResult::Type::kAttribute;
+  rnd->script_attribute = info.value();
+  rnd->objects.emplace_back(curNode);
   return true;
 }
 
-bool CFXJSE_ResolveProcessor::ResolveNormal(CFXJSE_ResolveNodeData& rnd) {
+bool CFXJSE_ResolveProcessor::ResolveNormal(v8::Isolate* pIsolate,
+                                            NodeData& rnd) {
   if (rnd.m_nLevel > 32 || !rnd.m_CurObject->IsNode())
     return false;
 
   CXFA_Node* curNode = rnd.m_CurObject->AsNode();
-  size_t nNum = rnd.m_Objects.size();
-  uint32_t dwStyles = rnd.m_dwStyles;
+  size_t nNum = rnd.m_Result.objects.size();
+  Mask<XFA_ResolveFlag> dwStyles = rnd.m_dwStyles;
   WideString& wsName = rnd.m_wsName;
   XFA_HashCode uNameHash = rnd.m_uHashName;
   WideString& wsCondition = rnd.m_wsCondition;
 
-  CFXJSE_ResolveNodeData rndFind(rnd.m_pSC.Get());
+  NodeData rndFind;
   rndFind.m_wsName = rnd.m_wsName;
   rndFind.m_wsCondition = rnd.m_wsCondition;
   rndFind.m_nLevel = rnd.m_nLevel + 1;
@@ -280,36 +261,37 @@
     else
       children.push_back(pChild);
   }
-  if ((dwStyles & XFA_RESOLVENODE_Properties) && pVariablesNode) {
+  if ((dwStyles & XFA_ResolveFlag::kProperties) && pVariablesNode) {
     if (pVariablesNode->GetClassHashCode() == uNameHash) {
-      rnd.m_Objects.emplace_back(pVariablesNode);
+      rnd.m_Result.objects.emplace_back(pVariablesNode);
     } else {
       rndFind.m_CurObject = pVariablesNode;
       SetStylesForChild(dwStyles, rndFind);
       WideString wsSaveCondition = std::move(rndFind.m_wsCondition);
-      ResolveNormal(rndFind);
+      ResolveNormal(pIsolate, rndFind);
       rndFind.m_wsCondition = std::move(wsSaveCondition);
-      rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                           rndFind.m_Objects.end());
-      rndFind.m_Objects.clear();
+      rnd.m_Result.objects.insert(rnd.m_Result.objects.end(),
+                                  rndFind.m_Result.objects.begin(),
+                                  rndFind.m_Result.objects.end());
+      rndFind.m_Result.objects.clear();
     }
-    if (rnd.m_Objects.size() > nNum) {
-      FilterCondition(wsCondition, &rnd);
-      return !rnd.m_Objects.empty();
+    if (rnd.m_Result.objects.size() > nNum) {
+      FilterCondition(pIsolate, wsCondition, &rnd);
+      return !rnd.m_Result.objects.empty();
     }
   }
 
-  if (dwStyles & XFA_RESOLVENODE_Children) {
+  if (dwStyles & XFA_ResolveFlag::kChildren) {
     bool bSetFlag = false;
-    if (pPageSetNode && (dwStyles & XFA_RESOLVENODE_Properties))
+    if (pPageSetNode && (dwStyles & XFA_ResolveFlag::kProperties))
       children.push_back(pPageSetNode);
 
     for (CXFA_Node* child : children) {
-      if (dwStyles & XFA_RESOLVENODE_TagName) {
+      if (dwStyles & XFA_ResolveFlag::kTagName) {
         if (child->GetClassHashCode() == uNameHash)
-          rnd.m_Objects.emplace_back(child);
+          rnd.m_Result.objects.emplace_back(child);
       } else if (child->GetNameHash() == uNameHash) {
-        rnd.m_Objects.emplace_back(child);
+        rnd.m_Result.objects.emplace_back(child);
       }
 
       if (child->GetElementType() != XFA_Element::PageSet &&
@@ -321,54 +303,55 @@
         rndFind.m_CurObject = child;
 
         WideString wsSaveCondition = std::move(rndFind.m_wsCondition);
-        ResolveNormal(rndFind);
+        ResolveNormal(pIsolate, rndFind);
         rndFind.m_wsCondition = std::move(wsSaveCondition);
-        rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                             rndFind.m_Objects.end());
-        rndFind.m_Objects.clear();
+        rnd.m_Result.objects.insert(rnd.m_Result.objects.end(),
+                                    rndFind.m_Result.objects.begin(),
+                                    rndFind.m_Result.objects.end());
+        rndFind.m_Result.objects.clear();
       }
     }
-    if (rnd.m_Objects.size() > nNum) {
-      if (!(dwStyles & XFA_RESOLVENODE_ALL)) {
+    if (rnd.m_Result.objects.size() > nNum) {
+      if (!(dwStyles & XFA_ResolveFlag::kALL)) {
         std::vector<CXFA_Node*> upArrayNodes;
         if (curNode->IsTransparent()) {
-          CXFA_Node* pCurrent = ToNode(rnd.m_Objects.front().Get());
+          CXFA_Node* pCurrent = ToNode(rnd.m_Result.objects.front().Get());
           if (pCurrent) {
             upArrayNodes =
-                pCurrent->GetSiblings(!!(dwStyles & XFA_RESOLVENODE_TagName));
+                pCurrent->GetSiblings(!!(dwStyles & XFA_ResolveFlag::kTagName));
           }
         }
-        if (upArrayNodes.size() > rnd.m_Objects.size()) {
-          CXFA_Object* pSaveObject = rnd.m_Objects.front().Get();
-          rnd.m_Objects = std::vector<UnownedPtr<CXFA_Object>>(
+        if (upArrayNodes.size() > rnd.m_Result.objects.size()) {
+          CXFA_Object* pSaveObject = rnd.m_Result.objects.front().Get();
+          rnd.m_Result.objects = std::vector<cppgc::Member<CXFA_Object>>(
               upArrayNodes.begin(), upArrayNodes.end());
-          rnd.m_Objects.front() = pSaveObject;
+          rnd.m_Result.objects.front() = pSaveObject;
         }
       }
-      FilterCondition(wsCondition, &rnd);
-      return !rnd.m_Objects.empty();
+      FilterCondition(pIsolate, wsCondition, &rnd);
+      return !rnd.m_Result.objects.empty();
     }
   }
-  if (dwStyles & XFA_RESOLVENODE_Attributes) {
-    if (ResolveForAttributeRs(curNode, rnd, wsName.AsStringView()))
-      return 1;
+  if (dwStyles & XFA_ResolveFlag::kAttributes) {
+    if (ResolveForAttributeRs(curNode, &rnd.m_Result, wsName.AsStringView()))
+      return true;
   }
-  if (dwStyles & XFA_RESOLVENODE_Properties) {
+  if (dwStyles & XFA_ResolveFlag::kProperties) {
     for (CXFA_Node* pChildProperty : properties) {
       if (pChildProperty->IsUnnamed()) {
         if (pChildProperty->GetClassHashCode() == uNameHash)
-          rnd.m_Objects.emplace_back(pChildProperty);
+          rnd.m_Result.objects.emplace_back(pChildProperty);
         continue;
       }
       if (pChildProperty->GetNameHash() == uNameHash &&
           pChildProperty->GetElementType() != XFA_Element::Extras &&
           pChildProperty->GetElementType() != XFA_Element::Items) {
-        rnd.m_Objects.emplace_back(pChildProperty);
+        rnd.m_Result.objects.emplace_back(pChildProperty);
       }
     }
-    if (rnd.m_Objects.size() > nNum) {
-      FilterCondition(wsCondition, &rnd);
-      return !rnd.m_Objects.empty();
+    if (rnd.m_Result.objects.size() > nNum) {
+      FilterCondition(pIsolate, wsCondition, &rnd);
+      return !rnd.m_Result.objects.empty();
     }
 
     CXFA_Node* pProp = nullptr;
@@ -388,8 +371,8 @@
       }
     }
     if (pProp) {
-      rnd.m_Objects.emplace_back(pProp);
-      return !rnd.m_Objects.empty();
+      rnd.m_Result.objects.emplace_back(pProp);
+      return !rnd.m_Result.objects.empty();
     }
   }
 
@@ -397,35 +380,35 @@
   uint32_t uCurClassHash = curNode->GetClassHashCode();
   if (!parentNode) {
     if (uCurClassHash == uNameHash) {
-      rnd.m_Objects.emplace_back(curNode);
-      FilterCondition(wsCondition, &rnd);
-      if (!rnd.m_Objects.empty())
+      rnd.m_Result.objects.emplace_back(curNode);
+      FilterCondition(pIsolate, wsCondition, &rnd);
+      if (!rnd.m_Result.objects.empty())
         return true;
     }
     return false;
   }
 
-  if (dwStyles & XFA_RESOLVENODE_Siblings) {
+  if (dwStyles & XFA_ResolveFlag::kSiblings) {
     CXFA_Node* child = parentNode->GetFirstChild();
-    uint32_t dwSubStyles =
-        XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties;
-    if (dwStyles & XFA_RESOLVENODE_TagName)
-      dwSubStyles |= XFA_RESOLVENODE_TagName;
-    if (dwStyles & XFA_RESOLVENODE_ALL)
-      dwSubStyles |= XFA_RESOLVENODE_ALL;
+    Mask<XFA_ResolveFlag> dwSubStyles = {XFA_ResolveFlag::kChildren,
+                                         XFA_ResolveFlag::kProperties};
+    if (dwStyles & XFA_ResolveFlag::kTagName)
+      dwSubStyles |= XFA_ResolveFlag::kTagName;
+    if (dwStyles & XFA_ResolveFlag::kALL)
+      dwSubStyles |= XFA_ResolveFlag::kALL;
 
     rndFind.m_dwStyles = dwSubStyles;
     while (child) {
       if (child == curNode) {
-        if (dwStyles & XFA_RESOLVENODE_TagName) {
+        if (dwStyles & XFA_ResolveFlag::kTagName) {
           if (uCurClassHash == uNameHash)
-            rnd.m_Objects.emplace_back(curNode);
+            rnd.m_Result.objects.emplace_back(curNode);
         } else {
           if (child->GetNameHash() == uNameHash) {
-            rnd.m_Objects.emplace_back(curNode);
+            rnd.m_Result.objects.emplace_back(curNode);
             if (rnd.m_nLevel == 0 && wsCondition.IsEmpty()) {
-              rnd.m_Objects.clear();
-              rnd.m_Objects.emplace_back(curNode);
+              rnd.m_Result.objects.clear();
+              rnd.m_Result.objects.emplace_back(curNode);
               return true;
             }
           }
@@ -434,11 +417,11 @@
         continue;
       }
 
-      if (dwStyles & XFA_RESOLVENODE_TagName) {
+      if (dwStyles & XFA_ResolveFlag::kTagName) {
         if (child->GetClassHashCode() == uNameHash)
-          rnd.m_Objects.emplace_back(child);
+          rnd.m_Result.objects.emplace_back(child);
       } else if (child->GetNameHash() == uNameHash) {
-        rnd.m_Objects.emplace_back(child);
+        rnd.m_Result.objects.emplace_back(child);
       }
 
       bool bInnerSearch = false;
@@ -453,70 +436,74 @@
       if (bInnerSearch) {
         rndFind.m_CurObject = child;
         WideString wsOriginCondition = std::move(rndFind.m_wsCondition);
-        uint32_t dwOriginStyle = rndFind.m_dwStyles;
-        rndFind.m_dwStyles = dwOriginStyle | XFA_RESOLVENODE_ALL;
-        ResolveNormal(rndFind);
+        Mask<XFA_ResolveFlag> dwOriginStyle = rndFind.m_dwStyles;
+        rndFind.m_dwStyles = dwOriginStyle | XFA_ResolveFlag::kALL;
+        ResolveNormal(pIsolate, rndFind);
         rndFind.m_dwStyles = dwOriginStyle;
         rndFind.m_wsCondition = std::move(wsOriginCondition);
-        rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                             rndFind.m_Objects.end());
-        rndFind.m_Objects.clear();
+        rnd.m_Result.objects.insert(rnd.m_Result.objects.end(),
+                                    rndFind.m_Result.objects.begin(),
+                                    rndFind.m_Result.objects.end());
+        rndFind.m_Result.objects.clear();
       }
       child = child->GetNextSibling();
     }
-    if (rnd.m_Objects.size() > nNum) {
+    if (rnd.m_Result.objects.size() > nNum) {
       if (parentNode->IsTransparent()) {
         std::vector<CXFA_Node*> upArrayNodes;
-        CXFA_Node* pCurrent = ToNode(rnd.m_Objects.front().Get());
+        CXFA_Node* pCurrent = ToNode(rnd.m_Result.objects.front().Get());
         if (pCurrent) {
           upArrayNodes =
-              pCurrent->GetSiblings(!!(dwStyles & XFA_RESOLVENODE_TagName));
+              pCurrent->GetSiblings(!!(dwStyles & XFA_ResolveFlag::kTagName));
         }
-        if (upArrayNodes.size() > rnd.m_Objects.size()) {
-          CXFA_Object* pSaveObject = rnd.m_Objects.front().Get();
-          rnd.m_Objects = std::vector<UnownedPtr<CXFA_Object>>(
+        if (upArrayNodes.size() > rnd.m_Result.objects.size()) {
+          CXFA_Object* pSaveObject = rnd.m_Result.objects.front().Get();
+          rnd.m_Result.objects = std::vector<cppgc::Member<CXFA_Object>>(
               upArrayNodes.begin(), upArrayNodes.end());
-          rnd.m_Objects.front() = pSaveObject;
+          rnd.m_Result.objects.front() = pSaveObject;
         }
       }
-      FilterCondition(wsCondition, &rnd);
-      return !rnd.m_Objects.empty();
+      FilterCondition(pIsolate, wsCondition, &rnd);
+      return !rnd.m_Result.objects.empty();
     }
   }
 
-  if (dwStyles & XFA_RESOLVENODE_Parent) {
-    uint32_t dwSubStyles = XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent |
-                           XFA_RESOLVENODE_Properties;
-    if (dwStyles & XFA_RESOLVENODE_TagName)
-      dwSubStyles |= XFA_RESOLVENODE_TagName;
-    if (dwStyles & XFA_RESOLVENODE_ALL)
-      dwSubStyles |= XFA_RESOLVENODE_ALL;
+  if (dwStyles & XFA_ResolveFlag::kParent) {
+    Mask<XFA_ResolveFlag> dwSubStyles = {XFA_ResolveFlag::kSiblings,
+                                         XFA_ResolveFlag::kParent,
+                                         XFA_ResolveFlag::kProperties};
+    if (dwStyles & XFA_ResolveFlag::kTagName)
+      dwSubStyles |= XFA_ResolveFlag::kTagName;
+    if (dwStyles & XFA_ResolveFlag::kALL)
+      dwSubStyles |= XFA_ResolveFlag::kALL;
 
+    m_pEngine->AddObjectToUpArray(parentNode);
     rndFind.m_dwStyles = dwSubStyles;
     rndFind.m_CurObject = parentNode;
-    rnd.m_pSC->GetUpObjectArray()->push_back(parentNode);
-    ResolveNormal(rndFind);
-    rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                         rndFind.m_Objects.end());
-    rndFind.m_Objects.clear();
-    if (rnd.m_Objects.size() > nNum)
+    ResolveNormal(pIsolate, rndFind);
+    rnd.m_Result.objects.insert(rnd.m_Result.objects.end(),
+                                rndFind.m_Result.objects.begin(),
+                                rndFind.m_Result.objects.end());
+    rndFind.m_Result.objects.clear();
+    if (rnd.m_Result.objects.size() > nNum)
       return true;
   }
   return false;
 }
 
-bool CFXJSE_ResolveProcessor::ResolveAsterisk(CFXJSE_ResolveNodeData& rnd) {
-  CXFA_Node* curNode = ToNode(rnd.m_CurObject.Get());
+bool CFXJSE_ResolveProcessor::ResolveAsterisk(NodeData& rnd) {
+  CXFA_Node* curNode = ToNode(rnd.m_CurObject);
   std::vector<CXFA_Node*> array = curNode->GetNodeListWithFilter(
-      XFA_NODEFILTER_Children | XFA_NODEFILTER_Properties);
-  rnd.m_Objects.insert(rnd.m_Objects.end(), array.begin(), array.end());
-  return !rnd.m_Objects.empty();
+      {XFA_NodeFilter::kChildren, XFA_NodeFilter::kProperties});
+  rnd.m_Result.objects.insert(rnd.m_Result.objects.end(), array.begin(),
+                              array.end());
+  return !rnd.m_Result.objects.empty();
 }
 
 int32_t CFXJSE_ResolveProcessor::GetFilter(WideStringView wsExpression,
                                            int32_t nStart,
-                                           CFXJSE_ResolveNodeData& rnd) {
-  ASSERT(nStart > -1);
+                                           NodeData& rnd) {
+  DCHECK(nStart > -1);
 
   int32_t iLength = wsExpression.GetLength();
   if (nStart >= iLength)
@@ -541,7 +528,7 @@
       wCur = pSrc[nStart++];
       if (wCur == '.') {
         if (nNameCount == 0) {
-          rnd.m_dwStyles |= XFA_RESOLVENODE_AnyChild;
+          rnd.m_dwStyles |= XFA_ResolveFlag::kAnyChild;
           continue;
         }
         if (wPrev == '\\') {
@@ -589,14 +576,14 @@
   wsName.Trim();
   wsCondition.Trim();
   rnd.m_uHashName =
-      static_cast<XFA_HashCode>(FX_HashCode_GetW(wsName.AsStringView(), false));
+      static_cast<XFA_HashCode>(FX_HashCode_GetW(wsName.AsStringView()));
   return nStart;
 }
 
 void CFXJSE_ResolveProcessor::ConditionArray(size_t iCurIndex,
                                              WideString wsCondition,
                                              size_t iFoundCount,
-                                             CFXJSE_ResolveNodeData* pRnd) {
+                                             NodeData* pRnd) {
   size_t iLen = wsCondition.GetLength();
   bool bRelative = false;
   bool bAll = false;
@@ -613,18 +600,18 @@
     break;
   }
   if (bAll) {
-    if (pRnd->m_dwStyles & XFA_RESOLVENODE_CreateNode) {
-      if (pRnd->m_dwStyles & XFA_RESOLVENODE_Bind) {
-        m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject.Get());
+    if (pRnd->m_dwStyles & XFA_ResolveFlag::kCreateNode) {
+      if (pRnd->m_dwStyles & XFA_ResolveFlag::kBind) {
+        m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject);
         m_pNodeHelper->m_iCreateCount = 1;
-        pRnd->m_Objects.clear();
+        pRnd->m_Result.objects.clear();
         m_pNodeHelper->m_iCurAllStart = -1;
         m_pNodeHelper->m_pAllStartParent = nullptr;
       } else if (m_pNodeHelper->m_iCurAllStart == -1) {
         m_pNodeHelper->m_iCurAllStart = m_iCurStart;
-        m_pNodeHelper->m_pAllStartParent = ToNode(pRnd->m_CurObject.Get());
+        m_pNodeHelper->m_pAllStartParent = ToNode(pRnd->m_CurObject);
       }
-    } else if (pRnd->m_dwStyles & XFA_RESOLVENODE_BindNew) {
+    } else if (pRnd->m_dwStyles & XFA_ResolveFlag::kBindNew) {
       if (m_pNodeHelper->m_iCurAllStart == -1)
         m_pNodeHelper->m_iCurAllStart = m_iCurStart;
     }
@@ -638,51 +625,51 @@
     iIndex += iCurIndex;
 
   if (iIndex < 0 || static_cast<size_t>(iIndex) >= iFoundCount) {
-    if (pRnd->m_dwStyles & XFA_RESOLVENODE_CreateNode) {
-      m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject.Get());
+    if (pRnd->m_dwStyles & XFA_ResolveFlag::kCreateNode) {
+      m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject);
       m_pNodeHelper->m_iCreateCount = iIndex - iFoundCount + 1;
     }
-    pRnd->m_Objects.clear();
+    pRnd->m_Result.objects.clear();
   } else {
-    pRnd->m_Objects =
-        std::vector<UnownedPtr<CXFA_Object>>(1, pRnd->m_Objects[iIndex]);
+    pRnd->m_Result.objects = std::vector<cppgc::Member<CXFA_Object>>(
+        1, pRnd->m_Result.objects[iIndex]);
   }
 }
 
-void CFXJSE_ResolveProcessor::FilterCondition(WideString wsCondition,
-                                              CFXJSE_ResolveNodeData* pRnd) {
+void CFXJSE_ResolveProcessor::FilterCondition(v8::Isolate* pIsolate,
+                                              WideString wsCondition,
+                                              NodeData* pRnd) {
   size_t iCurIndex = 0;
-  const std::vector<CXFA_Node*>* pArray = pRnd->m_pSC->GetUpObjectArray();
-  if (!pArray->empty()) {
-    CXFA_Node* pNode = pArray->back();
-    bool bIsProperty = pNode->IsProperty();
-    bool bIsClassIndex =
+  CXFA_Node* pNode = m_pEngine->LastObjectFromUpArray();
+  if (pNode) {
+    const bool bIsProperty = pNode->IsProperty();
+    const bool bIsClassIndex =
         pNode->IsUnnamed() ||
         (bIsProperty && pNode->GetElementType() != XFA_Element::PageSet);
     iCurIndex = pNode->GetIndex(bIsProperty, bIsClassIndex);
   }
 
-  size_t iFoundCount = pRnd->m_Objects.size();
+  size_t iFoundCount = pRnd->m_Result.objects.size();
   wsCondition.Trim();
 
-  int32_t iLen = wsCondition.GetLength();
-  if (!iLen) {
-    if (pRnd->m_dwStyles & XFA_RESOLVENODE_ALL)
+  const size_t nLen = wsCondition.GetLength();
+  if (nLen == 0) {
+    if (pRnd->m_dwStyles & XFA_ResolveFlag::kALL)
       return;
     if (iFoundCount == 1)
       return;
 
     if (iFoundCount <= iCurIndex) {
-      if (pRnd->m_dwStyles & XFA_RESOLVENODE_CreateNode) {
-        m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject.Get());
+      if (pRnd->m_dwStyles & XFA_ResolveFlag::kCreateNode) {
+        m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject);
         m_pNodeHelper->m_iCreateCount = iCurIndex - iFoundCount + 1;
       }
-      pRnd->m_Objects.clear();
+      pRnd->m_Result.objects.clear();
       return;
     }
 
-    pRnd->m_Objects =
-        std::vector<UnownedPtr<CXFA_Object>>(1, pRnd->m_Objects[iCurIndex]);
+    pRnd->m_Result.objects = std::vector<cppgc::Member<CXFA_Object>>(
+        1, pRnd->m_Result.objects[iCurIndex]);
     return;
   }
 
@@ -692,8 +679,8 @@
       ConditionArray(iCurIndex, wsCondition, iFoundCount, pRnd);
       return;
     case '.':
-      if (iLen > 1 && (wsCondition[1] == '[' || wsCondition[1] == '('))
-        DoPredicateFilter(wsCondition, iFoundCount, pRnd);
+      if (nLen > 1 && (wsCondition[1] == '[' || wsCondition[1] == '('))
+        DoPredicateFilter(pIsolate, wsCondition, iFoundCount, pRnd);
       return;
     case '(':
     case '"':
@@ -702,34 +689,50 @@
   }
 }
 
-void CFXJSE_ResolveProcessor::SetStylesForChild(uint32_t dwParentStyles,
-                                                CFXJSE_ResolveNodeData& rnd) {
-  uint32_t dwSubStyles = XFA_RESOLVENODE_Children;
-  if (dwParentStyles & XFA_RESOLVENODE_TagName)
-    dwSubStyles |= XFA_RESOLVENODE_TagName;
-
-  dwSubStyles &= ~XFA_RESOLVENODE_Parent;
-  dwSubStyles &= ~XFA_RESOLVENODE_Siblings;
-  dwSubStyles &= ~XFA_RESOLVENODE_Properties;
-  dwSubStyles |= XFA_RESOLVENODE_ALL;
+void CFXJSE_ResolveProcessor::SetStylesForChild(
+    Mask<XFA_ResolveFlag> dwParentStyles,
+    NodeData& rnd) {
+  Mask<XFA_ResolveFlag> dwSubStyles = {XFA_ResolveFlag::kChildren,
+                                       XFA_ResolveFlag::kALL};
+  if (dwParentStyles & XFA_ResolveFlag::kTagName)
+    dwSubStyles |= XFA_ResolveFlag::kTagName;
   rnd.m_dwStyles = dwSubStyles;
 }
 
-void CFXJSE_ResolveProcessor::SetIndexDataBind(WideString& wsNextCondition,
-                                               int32_t& iIndex,
-                                               int32_t iCount) {
-  if (m_pNodeHelper->CreateNodeForCondition(wsNextCondition)) {
-    if (m_pNodeHelper->m_eLastCreateType == XFA_Element::DataGroup) {
-      iIndex = 0;
-    } else {
-      iIndex = iCount - 1;
-    }
-  } else {
-    iIndex = iCount - 1;
+int32_t CFXJSE_ResolveProcessor::IndexForDataBind(
+    const WideString& wsNextCondition,
+    int32_t iCount) {
+  if (m_pNodeHelper->CreateNodeForCondition(wsNextCondition) &&
+      m_pNodeHelper->m_eLastCreateType == XFA_Element::DataGroup) {
+    return 0;
+  }
+  return iCount - 1;
+}
+
+void CFXJSE_ResolveProcessor::DoPredicateFilter(v8::Isolate* pIsolate,
+                                                WideString wsCondition,
+                                                size_t iFoundCount,
+                                                NodeData* pRnd) {
+  DCHECK_EQ(iFoundCount, pRnd->m_Result.objects.size());
+  CXFA_Script::Type eLangType = CXFA_Script::Type::Unknown;
+  if (wsCondition.First(2).EqualsASCII(".[") && wsCondition.Back() == L']')
+    eLangType = CXFA_Script::Type::Formcalc;
+  else if (wsCondition.First(2).EqualsASCII(".(") && wsCondition.Back() == L')')
+    eLangType = CXFA_Script::Type::Javascript;
+  else
+    return;
+
+  WideString wsExpression = wsCondition.Substr(2, wsCondition.GetLength() - 3);
+  for (size_t i = iFoundCount; i > 0; --i) {
+    auto pRetValue = std::make_unique<CFXJSE_Value>();
+    bool bRet = m_pEngine->RunScript(eLangType, wsExpression.AsStringView(),
+                                     pRetValue.get(),
+                                     pRnd->m_Result.objects[i - 1].Get());
+    if (!bRet || !pRetValue->ToBoolean(pIsolate))
+      pRnd->m_Result.objects.erase(pRnd->m_Result.objects.begin() + i - 1);
   }
 }
 
-CFXJSE_ResolveNodeData::CFXJSE_ResolveNodeData(CFXJSE_Engine* pSC)
-    : m_pSC(pSC) {}
+CFXJSE_ResolveProcessor::NodeData::NodeData() = default;
 
-CFXJSE_ResolveNodeData::~CFXJSE_ResolveNodeData() = default;
+CFXJSE_ResolveProcessor::NodeData::~NodeData() = default;
diff --git a/fxjs/xfa/cfxjse_resolveprocessor.h b/fxjs/xfa/cfxjse_resolveprocessor.h
index 6e4fcba..f7125c9 100644
--- a/fxjs/xfa/cfxjse_resolveprocessor.h
+++ b/fxjs/xfa/cfxjse_resolveprocessor.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,69 +8,69 @@
 #define FXJS_XFA_CFXJSE_RESOLVEPROCESSOR_H_
 
 #include <memory>
-#include <vector>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
 #include "fxjs/xfa/cfxjse_engine.h"
+#include "v8/include/cppgc/macros.h"
 #include "xfa/fxfa/fxfa_basic.h"
 #include "xfa/fxfa/parser/xfa_basic_data.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 
-class CXFA_NodeHelper;
-
-class CFXJSE_ResolveNodeData {
- public:
-  explicit CFXJSE_ResolveNodeData(CFXJSE_Engine* pSC);
-  ~CFXJSE_ResolveNodeData();
-
-  UnownedPtr<CFXJSE_Engine> const m_pSC;
-  UnownedPtr<CXFA_Object> m_CurObject;
-  WideString m_wsName;
-  WideString m_wsCondition;
-  XFA_HashCode m_uHashName = XFA_HASHCODE_None;
-  int32_t m_nLevel = 0;
-  uint32_t m_dwStyles = XFA_RESOLVENODE_Children;
-  XFA_ResolveNode_RSType m_dwFlag = XFA_ResolveNode_RSType_Nodes;
-  std::vector<UnownedPtr<CXFA_Object>> m_Objects;
-  XFA_SCRIPTATTRIBUTEINFO m_ScriptAttribute;
-};
+class CFXJSE_NodeHelper;
 
 class CFXJSE_ResolveProcessor {
  public:
-  CFXJSE_ResolveProcessor();
+  class NodeData {
+    CPPGC_STACK_ALLOCATED();  // Allows Raw/Unowned pointers.
+
+   public:
+    NodeData();
+    ~NodeData();
+
+    UnownedPtr<CXFA_Object> m_CurObject;  // Ok, stack-only.
+    WideString m_wsName;
+    WideString m_wsCondition;
+    XFA_HashCode m_uHashName = XFA_HASHCODE_None;
+    int32_t m_nLevel = 0;
+    Mask<XFA_ResolveFlag> m_dwStyles = XFA_ResolveFlag::kChildren;
+    CFXJSE_Engine::ResolveResult m_Result;
+  };
+
+  CFXJSE_ResolveProcessor(CFXJSE_Engine* pEngine, CFXJSE_NodeHelper* pHelper);
   ~CFXJSE_ResolveProcessor();
 
-  bool Resolve(CFXJSE_ResolveNodeData& rnd);
-  int32_t GetFilter(WideStringView wsExpression,
-                    int32_t nStart,
-                    CFXJSE_ResolveNodeData& rnd);
-  void SetIndexDataBind(WideString& wsNextCondition,
-                        int32_t& iIndex,
-                        int32_t iCount);
+  bool Resolve(v8::Isolate* pIsolate, NodeData& rnd);
+  int32_t GetFilter(WideStringView wsExpression, int32_t nStart, NodeData& rnd);
+  int32_t IndexForDataBind(const WideString& wsNextCondition, int32_t iCount);
   void SetCurStart(int32_t start) { m_iCurStart = start; }
 
-  CXFA_NodeHelper* GetNodeHelper() { return m_pNodeHelper.get(); }
-
  private:
   bool ResolveForAttributeRs(CXFA_Object* curNode,
-                             CFXJSE_ResolveNodeData& rnd,
+                             CFXJSE_Engine::ResolveResult* rnd,
                              WideStringView strAttr);
-  bool ResolveAnyChild(CFXJSE_ResolveNodeData& rnd);
-  bool ResolveDollar(CFXJSE_ResolveNodeData& rnd);
-  bool ResolveExcalmatory(CFXJSE_ResolveNodeData& rnd);
-  bool ResolveNumberSign(CFXJSE_ResolveNodeData& rnd);
-  bool ResolveAsterisk(CFXJSE_ResolveNodeData& rnd);
-  bool ResolveNormal(CFXJSE_ResolveNodeData& rnd);
-  void SetStylesForChild(uint32_t dwParentStyles, CFXJSE_ResolveNodeData& rnd);
+  bool ResolveAnyChild(v8::Isolate* pIsolate, NodeData& rnd);
+  bool ResolveDollar(v8::Isolate* pIsolate, NodeData& rnd);
+  bool ResolveExcalmatory(v8::Isolate* pIsolate, NodeData& rnd);
+  bool ResolveNumberSign(v8::Isolate* pIsolate, NodeData& rnd);
+  bool ResolveAsterisk(NodeData& rnd);
+  bool ResolveNormal(v8::Isolate* pIsolate, NodeData& rnd);
+  void SetStylesForChild(Mask<XFA_ResolveFlag> dwParentStyles, NodeData& rnd);
 
   void ConditionArray(size_t iCurIndex,
                       WideString wsCondition,
                       size_t iFoundCount,
-                      CFXJSE_ResolveNodeData* pRnd);
-  void FilterCondition(WideString wsCondition, CFXJSE_ResolveNodeData* pRnd);
+                      NodeData* pRnd);
+  void FilterCondition(v8::Isolate* pIsolate,
+                       WideString wsCondition,
+                       NodeData* pRnd);
+  void DoPredicateFilter(v8::Isolate* pIsolate,
+                         WideString wsCondition,
+                         size_t iFoundCount,
+                         NodeData* pRnd);
 
   int32_t m_iCurStart = 0;
-  std::unique_ptr<CXFA_NodeHelper> const m_pNodeHelper;
+  UnownedPtr<CFXJSE_Engine> const m_pEngine;
+  UnownedPtr<CFXJSE_NodeHelper> const m_pNodeHelper;
 };
 
 #endif  // FXJS_XFA_CFXJSE_RESOLVEPROCESSOR_H_
diff --git a/fxjs/xfa/cfxjse_runtimedata.cpp b/fxjs/xfa/cfxjse_runtimedata.cpp
index 0478e3e..b6a24c4 100644
--- a/fxjs/xfa/cfxjse_runtimedata.cpp
+++ b/fxjs/xfa/cfxjse_runtimedata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,15 @@
 #include <utility>
 
 #include "fxjs/cfxjs_engine.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_isolatetracker.h"
+#include "third_party/base/check_op.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-external.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
+#include "v8/include/v8-template.h"
 
 CFXJSE_RuntimeData::CFXJSE_RuntimeData() = default;
 
@@ -19,25 +27,21 @@
     v8::Isolate* pIsolate) {
   std::unique_ptr<CFXJSE_RuntimeData> pRuntimeData(new CFXJSE_RuntimeData());
   CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
-
   v8::Local<v8::FunctionTemplate> hFuncTemplate =
       v8::FunctionTemplate::New(pIsolate);
 
   v8::Local<v8::ObjectTemplate> hGlobalTemplate =
       hFuncTemplate->InstanceTemplate();
-  hGlobalTemplate->Set(
-      v8::Symbol::GetToStringTag(pIsolate),
-      v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal)
-          .ToLocalChecked());
+  hGlobalTemplate->Set(v8::Symbol::GetToStringTag(pIsolate),
+                       fxv8::NewStringHelper(pIsolate, "global"));
 
   v8::Local<v8::Context> hContext =
       v8::Context::New(pIsolate, 0, hGlobalTemplate);
 
-  ASSERT(hContext->Global()->InternalFieldCount() == 0);
-  ASSERT(hContext->Global()
-             ->GetPrototype()
-             .As<v8::Object>()
-             ->InternalFieldCount() == 0);
+  DCHECK_EQ(hContext->Global()->InternalFieldCount(), 0);
+  DCHECK_EQ(
+      hContext->Global()->GetPrototype().As<v8::Object>()->InternalFieldCount(),
+      0);
 
   hContext->SetSecurityToken(v8::External::New(pIsolate, pIsolate));
   pRuntimeData->m_hRootContextGlobalTemplate.Reset(pIsolate, hFuncTemplate);
@@ -47,9 +51,8 @@
 
 CFXJSE_RuntimeData* CFXJSE_RuntimeData::Get(v8::Isolate* pIsolate) {
   FXJS_PerIsolateData::SetUp(pIsolate);
-
   FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(pIsolate);
-  if (!pData->m_pFXJSERuntimeData)
-    pData->m_pFXJSERuntimeData = CFXJSE_RuntimeData::Create(pIsolate);
-  return static_cast<CFXJSE_RuntimeData*>(pData->m_pFXJSERuntimeData.get());
+  if (!pData->GetExtension())
+    pData->SetExtension(CFXJSE_RuntimeData::Create(pIsolate));
+  return static_cast<CFXJSE_RuntimeData*>(pData->GetExtension());
 }
diff --git a/fxjs/xfa/cfxjse_runtimedata.h b/fxjs/xfa/cfxjse_runtimedata.h
index 148b01e..67925a0 100644
--- a/fxjs/xfa/cfxjse_runtimedata.h
+++ b/fxjs/xfa/cfxjse_runtimedata.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,25 +10,26 @@
 #include <memory>
 
 #include "fxjs/cfxjs_engine.h"
-#include "v8/include/v8.h"
+#include "v8/include/v8-forward.h"
+#include "v8/include/v8-persistent-handle.h"
 
-class CFXJSE_RuntimeData : public FXJS_PerIsolateData::ExtensionIface {
+class CFXJSE_RuntimeData final : public FXJS_PerIsolateData::ExtensionIface {
  public:
+  CFXJSE_RuntimeData(const CFXJSE_RuntimeData&) = delete;
+  CFXJSE_RuntimeData& operator=(const CFXJSE_RuntimeData&) = delete;
   ~CFXJSE_RuntimeData() override;
 
   static CFXJSE_RuntimeData* Get(v8::Isolate* pIsolate);
 
-  v8::Global<v8::FunctionTemplate> m_hRootContextGlobalTemplate;
-  v8::Global<v8::Context> m_hRootContext;
-
- protected:
-  CFXJSE_RuntimeData();
-
-  static std::unique_ptr<CFXJSE_RuntimeData> Create(v8::Isolate* pIsolate);
+  const v8::Global<v8::Context>& GetRootContext() { return m_hRootContext; }
 
  private:
-  CFXJSE_RuntimeData(const CFXJSE_RuntimeData&) = delete;
-  CFXJSE_RuntimeData& operator=(const CFXJSE_RuntimeData&) = delete;
+  static std::unique_ptr<CFXJSE_RuntimeData> Create(v8::Isolate* pIsolate);
+
+  CFXJSE_RuntimeData();
+
+  v8::Global<v8::FunctionTemplate> m_hRootContextGlobalTemplate;
+  v8::Global<v8::Context> m_hRootContext;
 };
 
 #endif  // FXJS_XFA_CFXJSE_RUNTIMEDATA_H_
diff --git a/fxjs/xfa/cfxjse_value.cpp b/fxjs/xfa/cfxjse_value.cpp
index 1d4cec4..112a648 100644
--- a/fxjs/xfa/cfxjse_value.cpp
+++ b/fxjs/xfa/cfxjse_value.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,9 +8,16 @@
 
 #include <math.h>
 
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_class.h"
 #include "fxjs/xfa/cfxjse_context.h"
 #include "fxjs/xfa/cfxjse_isolatetracker.h"
+#include "third_party/base/check.h"
+#include "v8/include/v8-container.h"
+#include "v8/include/v8-exception.h"
+#include "v8/include/v8-function.h"
+#include "v8/include/v8-primitive.h"
+#include "v8/include/v8-script.h"
 
 namespace {
 
@@ -26,15 +33,18 @@
   if (nErrExp >= 0)
     return fNumber;
 
-  double dwError = pow(2.0, nErrExp), dwErrorHalf = dwError / 2;
-  double dNumber = fNumber, dNumberAbs = fabs(fNumber);
-  double dNumberAbsMin = dNumberAbs - dwErrorHalf,
-         dNumberAbsMax = dNumberAbs + dwErrorHalf;
+  double dwError = pow(2.0, nErrExp);
+  double dwErrorHalf = dwError / 2;
+  double dNumber = fNumber;
+  double dNumberAbs = fabs(fNumber);
+  double dNumberAbsMin = dNumberAbs - dwErrorHalf;
+  double dNumberAbsMax = dNumberAbs + dwErrorHalf;
   int32_t iErrPos = 0;
   if (floor(dNumberAbsMin) == floor(dNumberAbsMax)) {
     dNumberAbsMin = fmod(dNumberAbsMin, 1.0);
     dNumberAbsMax = fmod(dNumberAbsMax, 1.0);
-    int32_t iErrPosMin = 1, iErrPosMax = 38;
+    int32_t iErrPosMin = 1;
+    int32_t iErrPosMax = 38;
     do {
       int32_t iMid = (iErrPosMin + iErrPosMax) / 2;
       double dPow = pow(10.0, iMid);
@@ -53,228 +63,140 @@
 
 }  // namespace
 
-void FXJSE_ThrowMessage(ByteStringView utf8Message) {
-  v8::Isolate* pIsolate = v8::Isolate::GetCurrent();
-  ASSERT(pIsolate);
-
+void FXJSE_ThrowMessage(v8::Isolate* pIsolate, ByteStringView utf8Message) {
+  DCHECK(pIsolate);
   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
-  v8::Local<v8::String> hMessage =
-      v8::String::NewFromUtf8(pIsolate, utf8Message.unterminated_c_str(),
-                              v8::NewStringType::kNormal,
-                              utf8Message.GetLength())
-          .ToLocalChecked();
+  v8::Local<v8::String> hMessage = fxv8::NewStringHelper(pIsolate, utf8Message);
   v8::Local<v8::Value> hError = v8::Exception::Error(hMessage);
   pIsolate->ThrowException(hError);
 }
 
-CFXJSE_Value::CFXJSE_Value(v8::Isolate* pIsolate) : m_pIsolate(pIsolate) {}
+CFXJSE_Value::CFXJSE_Value() = default;
 
-CFXJSE_Value::~CFXJSE_Value() {}
-
-CFXJSE_HostObject* CFXJSE_Value::ToHostObject() const {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> pValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  ASSERT(!pValue.IsEmpty());
-  if (!pValue->IsObject())
-    return nullptr;
-
-  return FXJSE_RetrieveObjectBinding(pValue.As<v8::Object>());
+CFXJSE_Value::CFXJSE_Value(v8::Isolate* pIsolate, v8::Local<v8::Value> value) {
+  ForceSetValue(pIsolate, value);
 }
 
-void CFXJSE_Value::SetHostObject(CFXJSE_HostObject* lpObject,
+CFXJSE_Value::~CFXJSE_Value() = default;
+
+CFXJSE_HostObject* CFXJSE_Value::ToHostObject(v8::Isolate* pIsolate) const {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  return CFXJSE_HostObject::FromV8(
+      v8::Local<v8::Value>::New(pIsolate, m_hValue));
+}
+
+void CFXJSE_Value::SetHostObject(v8::Isolate* pIsolate,
+                                 CFXJSE_HostObject* pObject,
                                  CFXJSE_Class* pClass) {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::FunctionTemplate> hClass =
-      v8::Local<v8::FunctionTemplate>::New(GetIsolate(), pClass->m_hTemplate);
-  v8::Local<v8::Object> hObject =
-      hClass->InstanceTemplate()
-          ->NewInstance(GetIsolate()->GetCurrentContext())
-          .ToLocalChecked();
-  FXJSE_UpdateObjectBinding(hObject, lpObject);
-  m_hValue.Reset(GetIsolate(), hObject);
-}
-
-void CFXJSE_Value::ClearHostObject() {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  FXJSE_ClearObjectBinding(m_hValue.Get(GetIsolate()).As<v8::Object>());
-  v8::Local<v8::Value> hValue = v8::Null(GetIsolate());
-  m_hValue.Reset(GetIsolate(), hValue);
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  m_hValue.Reset(pIsolate, pObject->NewBoundV8Object(
+                               pIsolate, pClass->GetTemplate(pIsolate)));
 }
 
 void CFXJSE_Value::SetArray(
+    v8::Isolate* pIsolate,
     const std::vector<std::unique_ptr<CFXJSE_Value>>& values) {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Array> hArrayObject =
-      v8::Array::New(GetIsolate(), values.size());
-  v8::Local<v8::Context> context = GetIsolate()->GetCurrentContext();
-  uint32_t count = 0;
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  std::vector<v8::Local<v8::Value>> local_values;
+  local_values.reserve(values.size());
   for (auto& v : values) {
     if (v->IsEmpty())
-      v->SetUndefined();
-    hArrayObject
-        ->Set(
-            context, count++,
-            v8::Local<v8::Value>::New(GetIsolate(), v.get()->DirectGetValue()))
-        .FromJust();
+      local_values.push_back(fxv8::NewUndefinedHelper(pIsolate));
+    else
+      local_values.push_back(v->GetValue(pIsolate));
   }
-  m_hValue.Reset(GetIsolate(), hArrayObject);
+  v8::Local<v8::Array> hArrayObject =
+      v8::Array::New(pIsolate, local_values.data(), local_values.size());
+  m_hValue.Reset(pIsolate, hArrayObject);
 }
 
-void CFXJSE_Value::SetFloat(float fFloat) {
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> pValue = v8::Number::New(GetIsolate(), ftod(fFloat));
-  m_hValue.Reset(GetIsolate(), pValue);
+void CFXJSE_Value::SetFloat(v8::Isolate* pIsolate, float fFloat) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  m_hValue.Reset(pIsolate, fxv8::NewNumberHelper(pIsolate, ftod(fFloat)));
 }
 
-bool CFXJSE_Value::SetObjectProperty(ByteStringView szPropName,
-                                     CFXJSE_Value* lpPropValue) {
-  ASSERT(lpPropValue);
-  if (lpPropValue->IsEmpty())
+bool CFXJSE_Value::SetObjectProperty(v8::Isolate* pIsolate,
+                                     ByteStringView szPropName,
+                                     CFXJSE_Value* pPropValue) {
+  if (pPropValue->IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  v8::Local<v8::Value> hObject = GetValue(pIsolate);
   if (!hObject->IsObject())
     return false;
 
-  v8::Local<v8::String> hPropName =
-      v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
-                              v8::NewStringType::kNormal,
-                              szPropName.GetLength())
-          .ToLocalChecked();
-  v8::Local<v8::Value> hPropValue =
-      v8::Local<v8::Value>::New(GetIsolate(), lpPropValue->DirectGetValue());
-  v8::Maybe<bool> result = hObject.As<v8::Object>()->Set(
-      GetIsolate()->GetCurrentContext(), hPropName, hPropValue);
-  return result.IsJust() && result.FromJust();
+  return fxv8::ReentrantPutObjectPropertyHelper(
+      pIsolate, hObject.As<v8::Object>(), szPropName,
+      pPropValue->GetValue(pIsolate));
 }
 
-bool CFXJSE_Value::GetObjectProperty(ByteStringView szPropName,
-                                     CFXJSE_Value* lpPropValue) {
-  ASSERT(lpPropValue);
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+bool CFXJSE_Value::GetObjectProperty(v8::Isolate* pIsolate,
+                                     ByteStringView szPropName,
+                                     CFXJSE_Value* pPropValue) {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  v8::Local<v8::Value> hObject = GetValue(pIsolate);
   if (!hObject->IsObject())
     return false;
 
-  v8::Local<v8::String> hPropName =
-      v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
-                              v8::NewStringType::kNormal,
-                              szPropName.GetLength())
-          .ToLocalChecked();
-  v8::Local<v8::Value> hPropValue =
-      hObject.As<v8::Object>()
-          ->Get(GetIsolate()->GetCurrentContext(), hPropName)
-          .ToLocalChecked();
-  lpPropValue->ForceSetValue(hPropValue);
+  pPropValue->ForceSetValue(
+      pIsolate, fxv8::ReentrantGetObjectPropertyHelper(
+                    pIsolate, hObject.As<v8::Object>(), szPropName));
   return true;
 }
 
-bool CFXJSE_Value::GetObjectPropertyByIdx(uint32_t uPropIdx,
-                                          CFXJSE_Value* lpPropValue) {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  if (!hObject->IsObject())
+bool CFXJSE_Value::GetObjectPropertyByIdx(v8::Isolate* pIsolate,
+                                          uint32_t uPropIdx,
+                                          CFXJSE_Value* pPropValue) {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  v8::Local<v8::Value> hObject = GetValue(pIsolate);
+  if (!hObject->IsArray())
     return false;
 
-  v8::Local<v8::Value> hPropValue =
-      hObject.As<v8::Object>()
-          ->Get(GetIsolate()->GetCurrentContext(), uPropIdx)
-          .ToLocalChecked();
-  lpPropValue->ForceSetValue(hPropValue);
+  pPropValue->ForceSetValue(pIsolate,
+                            fxv8::ReentrantGetArrayElementHelper(
+                                pIsolate, hObject.As<v8::Array>(), uPropIdx));
   return true;
 }
 
-bool CFXJSE_Value::DeleteObjectProperty(ByteStringView szPropName) {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  if (!hObject->IsObject())
-    return false;
-
-  v8::Local<v8::String> hPropName =
-      v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
-                              v8::NewStringType::kNormal,
-                              szPropName.GetLength())
-          .ToLocalChecked();
-  return hObject.As<v8::Object>()
-      ->Delete(GetIsolate()->GetCurrentContext(), hPropName)
-      .FromJust();
+void CFXJSE_Value::DeleteObjectProperty(v8::Isolate* pIsolate,
+                                        ByteStringView szPropName) {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  v8::Local<v8::Value> hObject = v8::Local<v8::Value>::New(pIsolate, m_hValue);
+  if (hObject->IsObject()) {
+    fxv8::ReentrantDeleteObjectPropertyHelper(
+        pIsolate, hObject.As<v8::Object>(), szPropName);
+  }
 }
 
-bool CFXJSE_Value::HasObjectOwnProperty(ByteStringView szPropName,
-                                        bool bUseTypeGetter) {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+bool CFXJSE_Value::SetObjectOwnProperty(v8::Isolate* pIsolate,
+                                        ByteStringView szPropName,
+                                        CFXJSE_Value* pPropValue) {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  v8::Local<v8::Value> hObject = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   if (!hObject->IsObject())
     return false;
 
-  v8::Local<v8::String> hKey =
-      v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
-                              v8::NewStringType::kNormal,
-                              szPropName.GetLength())
-          .ToLocalChecked();
-  return hObject.As<v8::Object>()
-             ->HasRealNamedProperty(GetIsolate()->GetCurrentContext(), hKey)
-             .FromJust() ||
-         (bUseTypeGetter &&
-          hObject.As<v8::Object>()
-              ->HasOwnProperty(GetIsolate()->GetCurrentContext(), hKey)
-              .FromMaybe(false));
-}
-
-bool CFXJSE_Value::SetObjectOwnProperty(ByteStringView szPropName,
-                                        CFXJSE_Value* lpPropValue) {
-  ASSERT(lpPropValue);
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  if (!hObject->IsObject())
-    return false;
-
-  v8::Local<v8::String> hPropName =
-      v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
-                              v8::NewStringType::kNormal,
-                              szPropName.GetLength())
-          .ToLocalChecked();
   v8::Local<v8::Value> pValue =
-      v8::Local<v8::Value>::New(GetIsolate(), lpPropValue->m_hValue);
-  return hObject.As<v8::Object>()
-      ->DefineOwnProperty(GetIsolate()->GetCurrentContext(), hPropName, pValue)
-      .FromMaybe(false);
+      v8::Local<v8::Value>::New(pIsolate, pPropValue->m_hValue);
+  return fxv8::ReentrantSetObjectOwnPropertyHelper(
+      pIsolate, hObject.As<v8::Object>(), szPropName, pValue);
 }
 
-bool CFXJSE_Value::SetFunctionBind(CFXJSE_Value* lpOldFunction,
-                                   CFXJSE_Value* lpNewThis) {
-  ASSERT(lpOldFunction);
-  ASSERT(lpNewThis);
+v8::Local<v8::Function> CFXJSE_Value::NewBoundFunction(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Function> hOldFunction,
+    v8::Local<v8::Object> hNewThis) {
+  DCHECK(!hOldFunction.IsEmpty());
+  DCHECK(!hNewThis.IsEmpty());
 
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  CFXJSE_ScopeUtil_RootContext scope(pIsolate);
   v8::Local<v8::Value> rgArgs[2];
-  v8::Local<v8::Value> hOldFunction =
-      v8::Local<v8::Value>::New(GetIsolate(), lpOldFunction->DirectGetValue());
-  if (hOldFunction.IsEmpty() || !hOldFunction->IsFunction())
-    return false;
-
   rgArgs[0] = hOldFunction;
-  v8::Local<v8::Value> hNewThis =
-      v8::Local<v8::Value>::New(GetIsolate(), lpNewThis->DirectGetValue());
-  if (hNewThis.IsEmpty())
-    return false;
-
   rgArgs[1] = hNewThis;
-  v8::Local<v8::String> hBinderFuncSource =
-      v8::String::NewFromUtf8(GetIsolate(),
-                              "(function (oldfunction, newthis) { return "
-                              "oldfunction.bind(newthis); })",
-                              v8::NewStringType::kNormal)
-          .ToLocalChecked();
-  v8::Local<v8::Context> hContext = GetIsolate()->GetCurrentContext();
+  v8::Local<v8::String> hBinderFuncSource = fxv8::NewStringHelper(
+      pIsolate, "(function (fn, obj) { return fn.bind(obj); })");
+  v8::Local<v8::Context> hContext = pIsolate->GetCurrentContext();
   v8::Local<v8::Function> hBinderFunc =
       v8::Script::Compile(hContext, hBinderFuncSource)
           .ToLocalChecked()
@@ -284,187 +206,159 @@
   v8::Local<v8::Value> hBoundFunction =
       hBinderFunc->Call(hContext, hContext->Global(), 2, rgArgs)
           .ToLocalChecked();
-  if (hBoundFunction.IsEmpty() || !hBoundFunction->IsFunction())
-    return false;
+  if (!fxv8::IsFunction(hBoundFunction))
+    return v8::Local<v8::Function>();
 
-  m_hValue.Reset(GetIsolate(), hBoundFunction);
-  return true;
+  return hBoundFunction.As<v8::Function>();
+}
+
+v8::Local<v8::Value> CFXJSE_Value::GetValue(v8::Isolate* pIsolate) const {
+  return v8::Local<v8::Value>::New(pIsolate, m_hValue);
 }
 
 bool CFXJSE_Value::IsEmpty() const {
   return m_hValue.IsEmpty();
 }
 
-bool CFXJSE_Value::IsUndefined() const {
+bool CFXJSE_Value::IsUndefined(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsUndefined();
 }
 
-bool CFXJSE_Value::IsNull() const {
+bool CFXJSE_Value::IsNull(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsNull();
 }
 
-bool CFXJSE_Value::IsBoolean() const {
+bool CFXJSE_Value::IsBoolean(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsBoolean();
 }
 
-bool CFXJSE_Value::IsString() const {
+bool CFXJSE_Value::IsString(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsString();
 }
 
-bool CFXJSE_Value::IsNumber() const {
+bool CFXJSE_Value::IsNumber(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsNumber();
 }
 
-bool CFXJSE_Value::IsInteger() const {
+bool CFXJSE_Value::IsInteger(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsInt32();
 }
 
-bool CFXJSE_Value::IsObject() const {
+bool CFXJSE_Value::IsObject(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsObject();
 }
 
-bool CFXJSE_Value::IsArray() const {
+bool CFXJSE_Value::IsArray(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsArray();
 }
 
-bool CFXJSE_Value::IsFunction() const {
+bool CFXJSE_Value::IsFunction(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsFunction();
 }
 
-bool CFXJSE_Value::ToBoolean() const {
-  ASSERT(!IsEmpty());
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  return hValue->BooleanValue(GetIsolate());
+bool CFXJSE_Value::ToBoolean(v8::Isolate* pIsolate) const {
+  DCHECK(!IsEmpty());
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  return fxv8::ReentrantToBooleanHelper(
+      pIsolate, v8::Local<v8::Value>::New(pIsolate, m_hValue));
 }
 
-float CFXJSE_Value::ToFloat() const {
-  ASSERT(!IsEmpty());
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  return static_cast<float>(
-      hValue->NumberValue(GetIsolate()->GetCurrentContext()).FromMaybe(0.0));
+float CFXJSE_Value::ToFloat(v8::Isolate* pIsolate) const {
+  return static_cast<float>(ToDouble(pIsolate));
 }
 
-double CFXJSE_Value::ToDouble() const {
-  ASSERT(!IsEmpty());
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  return hValue->NumberValue(GetIsolate()->GetCurrentContext()).FromMaybe(0.0);
+double CFXJSE_Value::ToDouble(v8::Isolate* pIsolate) const {
+  DCHECK(!IsEmpty());
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  return fxv8::ReentrantToDoubleHelper(
+      pIsolate, v8::Local<v8::Value>::New(pIsolate, m_hValue));
 }
 
-int32_t CFXJSE_Value::ToInteger() const {
-  ASSERT(!IsEmpty());
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  return static_cast<int32_t>(
-      hValue->NumberValue(GetIsolate()->GetCurrentContext()).FromMaybe(0.0));
+int32_t CFXJSE_Value::ToInteger(v8::Isolate* pIsolate) const {
+  DCHECK(!IsEmpty());
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  return fxv8::ReentrantToInt32Helper(
+      pIsolate, v8::Local<v8::Value>::New(pIsolate, m_hValue));
 }
 
-ByteString CFXJSE_Value::ToString() const {
-  ASSERT(!IsEmpty());
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  v8::Local<v8::String> hString =
-      hValue->ToString(GetIsolate()->GetCurrentContext()).ToLocalChecked();
-  v8::String::Utf8Value hStringVal(GetIsolate(), hString);
-  return ByteString(*hStringVal);
+ByteString CFXJSE_Value::ToString(v8::Isolate* pIsolate) const {
+  DCHECK(!IsEmpty());
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  return fxv8::ReentrantToByteStringHelper(
+      pIsolate, v8::Local<v8::Value>::New(pIsolate, m_hValue));
 }
 
-void CFXJSE_Value::SetUndefined() {
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue = v8::Undefined(GetIsolate());
-  m_hValue.Reset(GetIsolate(), hValue);
+void CFXJSE_Value::SetUndefined(v8::Isolate* pIsolate) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  m_hValue.Reset(pIsolate, fxv8::NewUndefinedHelper(pIsolate));
 }
 
-void CFXJSE_Value::SetNull() {
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue = v8::Null(GetIsolate());
-  m_hValue.Reset(GetIsolate(), hValue);
+void CFXJSE_Value::SetNull(v8::Isolate* pIsolate) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  m_hValue.Reset(pIsolate, fxv8::NewNullHelper(pIsolate));
 }
 
-void CFXJSE_Value::SetBoolean(bool bBoolean) {
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue = v8::Boolean::New(GetIsolate(), !!bBoolean);
-  m_hValue.Reset(GetIsolate(), hValue);
+void CFXJSE_Value::SetBoolean(v8::Isolate* pIsolate, bool bBoolean) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  m_hValue.Reset(pIsolate, fxv8::NewBooleanHelper(pIsolate, bBoolean));
 }
 
-void CFXJSE_Value::SetInteger(int32_t nInteger) {
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue = v8::Integer::New(GetIsolate(), nInteger);
-  m_hValue.Reset(GetIsolate(), hValue);
+void CFXJSE_Value::SetInteger(v8::Isolate* pIsolate, int32_t nInteger) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  m_hValue.Reset(pIsolate, fxv8::NewNumberHelper(pIsolate, nInteger));
 }
 
-void CFXJSE_Value::SetDouble(double dDouble) {
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue = v8::Number::New(GetIsolate(), dDouble);
-  m_hValue.Reset(GetIsolate(), hValue);
+void CFXJSE_Value::SetDouble(v8::Isolate* pIsolate, double dDouble) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  m_hValue.Reset(pIsolate, fxv8::NewNumberHelper(pIsolate, dDouble));
 }
 
-void CFXJSE_Value::SetString(ByteStringView szString) {
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::String::NewFromUtf8(GetIsolate(), szString.unterminated_c_str(),
-                              v8::NewStringType::kNormal, szString.GetLength())
-          .ToLocalChecked();
-  m_hValue.Reset(GetIsolate(), hValue);
+void CFXJSE_Value::SetString(v8::Isolate* pIsolate, ByteStringView szString) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  m_hValue.Reset(pIsolate, fxv8::NewStringHelper(pIsolate, szString));
 }
diff --git a/fxjs/xfa/cfxjse_value.h b/fxjs/xfa/cfxjse_value.h
index 44cc58c..b8b6687 100644
--- a/fxjs/xfa/cfxjse_value.h
+++ b/fxjs/xfa/cfxjse_value.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,84 +7,91 @@
 #ifndef FXJS_XFA_CFXJSE_VALUE_H_
 #define FXJS_XFA_CFXJSE_VALUE_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "v8/include/v8.h"
+#include "third_party/base/check.h"
+#include "v8/include/v8-forward.h"
+#include "v8/include/v8-persistent-handle.h"
 
 class CFXJSE_Class;
 class CFXJSE_HostObject;
 
 class CFXJSE_Value {
  public:
-  explicit CFXJSE_Value(v8::Isolate* pIsolate);
+  CFXJSE_Value();
+  CFXJSE_Value(v8::Isolate* pIsolate, v8::Local<v8::Value> value);
   ~CFXJSE_Value();
 
   bool IsEmpty() const;
-  bool IsUndefined() const;
-  bool IsNull() const;
-  bool IsBoolean() const;
-  bool IsString() const;
-  bool IsNumber() const;
-  bool IsInteger() const;
-  bool IsObject() const;
-  bool IsArray() const;
-  bool IsFunction() const;
-  bool ToBoolean() const;
-  float ToFloat() const;
-  double ToDouble() const;
-  int32_t ToInteger() const;
-  ByteString ToString() const;
-  WideString ToWideString() const {
-    return WideString::FromUTF8(ToString().AsStringView());
+  bool IsUndefined(v8::Isolate* pIsolate) const;
+  bool IsNull(v8::Isolate* pIsolate) const;
+  bool IsBoolean(v8::Isolate* pIsolate) const;
+  bool IsString(v8::Isolate* pIsolate) const;
+  bool IsNumber(v8::Isolate* pIsolate) const;
+  bool IsInteger(v8::Isolate* pIsolate) const;
+  bool IsObject(v8::Isolate* pIsolate) const;
+  bool IsArray(v8::Isolate* pIsolate) const;
+  bool IsFunction(v8::Isolate* pIsolate) const;
+  bool ToBoolean(v8::Isolate* pIsolate) const;
+  float ToFloat(v8::Isolate* pIsolate) const;
+  double ToDouble(v8::Isolate* pIsolate) const;
+  int32_t ToInteger(v8::Isolate* pIsolate) const;
+  ByteString ToString(v8::Isolate* pIsolate) const;
+  WideString ToWideString(v8::Isolate* pIsolate) const {
+    return WideString::FromUTF8(ToString(pIsolate).AsStringView());
   }
-  CFXJSE_HostObject* ToHostObject() const;
+  CFXJSE_HostObject* ToHostObject(v8::Isolate* pIsolate) const;
 
-  void SetUndefined();
-  void SetNull();
-  void SetBoolean(bool bBoolean);
-  void SetInteger(int32_t nInteger);
-  void SetDouble(double dDouble);
-  void SetString(ByteStringView szString);
-  void SetFloat(float fFloat);
+  void SetUndefined(v8::Isolate* pIsolate);
+  void SetNull(v8::Isolate* pIsolate);
+  void SetBoolean(v8::Isolate* pIsolate, bool bBoolean);
+  void SetInteger(v8::Isolate* pIsolate, int32_t nInteger);
+  void SetDouble(v8::Isolate* pIsolate, double dDouble);
+  void SetString(v8::Isolate* pIsolate, ByteStringView szString);
+  void SetFloat(v8::Isolate* pIsolate, float fFloat);
 
-  void SetHostObject(CFXJSE_HostObject* lpObject, CFXJSE_Class* pClass);
-  void ClearHostObject();
+  void SetHostObject(v8::Isolate* pIsolate,
+                     CFXJSE_HostObject* pObject,
+                     CFXJSE_Class* pClass);
 
-  void SetArray(const std::vector<std::unique_ptr<CFXJSE_Value>>& values);
+  void SetArray(v8::Isolate* pIsolate,
+                const std::vector<std::unique_ptr<CFXJSE_Value>>& values);
 
-  bool GetObjectProperty(ByteStringView szPropName, CFXJSE_Value* lpPropValue);
-  bool SetObjectProperty(ByteStringView szPropName, CFXJSE_Value* lpPropValue);
-  bool GetObjectPropertyByIdx(uint32_t uPropIdx, CFXJSE_Value* lpPropValue);
-  bool DeleteObjectProperty(ByteStringView szPropName);
-  bool HasObjectOwnProperty(ByteStringView szPropName, bool bUseTypeGetter);
-  bool SetObjectOwnProperty(ByteStringView szPropName,
-                            CFXJSE_Value* lpPropValue);
-  bool SetFunctionBind(CFXJSE_Value* lpOldFunction, CFXJSE_Value* lpNewThis);
+  bool GetObjectProperty(v8::Isolate* pIsolate,
+                         ByteStringView szPropName,
+                         CFXJSE_Value* pPropValue);
+  bool SetObjectProperty(v8::Isolate* pIsolate,
+                         ByteStringView szPropName,
+                         CFXJSE_Value* pPropValue);
+  bool GetObjectPropertyByIdx(v8::Isolate* pIsolate,
+                              uint32_t uPropIdx,
+                              CFXJSE_Value* pPropValue);
+  void DeleteObjectProperty(v8::Isolate* pIsolate, ByteStringView szPropName);
+  bool SetObjectOwnProperty(v8::Isolate* pIsolate,
+                            ByteStringView szPropName,
+                            CFXJSE_Value* pPropValue);
 
-  v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); }
+  // Return empty local on error.
+  static v8::Local<v8::Function> NewBoundFunction(
+      v8::Isolate* pIsolate,
+      v8::Local<v8::Function> hOldFunction,
+      v8::Local<v8::Object> lpNewThis);
+
+  v8::Local<v8::Value> GetValue(v8::Isolate* pIsolate) const;
   const v8::Global<v8::Value>& DirectGetValue() const { return m_hValue; }
-  void ForceSetValue(v8::Local<v8::Value> hValue) {
-    m_hValue.Reset(GetIsolate(), hValue);
-  }
-  void Assign(const CFXJSE_Value* lpValue) {
-    ASSERT(lpValue);
-    if (lpValue) {
-      m_hValue.Reset(GetIsolate(), lpValue->m_hValue);
-    } else {
-      m_hValue.Reset();
-    }
+  void ForceSetValue(v8::Isolate* pIsolate, v8::Local<v8::Value> hValue) {
+    m_hValue.Reset(pIsolate, hValue);
   }
 
  private:
-  CFXJSE_Value() = delete;
   CFXJSE_Value(const CFXJSE_Value&) = delete;
   CFXJSE_Value& operator=(const CFXJSE_Value&) = delete;
 
-  UnownedPtr<v8::Isolate> const m_pIsolate;
   v8::Global<v8::Value> m_hValue;
 };
 
diff --git a/fxjs/xfa/cfxjse_value_embeddertest.cpp b/fxjs/xfa/cfxjse_value_embeddertest.cpp
index e9c39c1..c1e810b 100644
--- a/fxjs/xfa/cfxjse_value_embeddertest.cpp
+++ b/fxjs/xfa/cfxjse_value_embeddertest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,34 +11,33 @@
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/xfa_js_embedder_test.h"
-#include "third_party/base/ptr_util.h"
 
 class CFXJSE_ValueEmbedderTest : public XFAJSEmbedderTest {};
 
 TEST_F(CFXJSE_ValueEmbedderTest, Empty) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  auto pValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
+  auto pValue = std::make_unique<CFXJSE_Value>();
   EXPECT_TRUE(pValue->IsEmpty());
-  EXPECT_FALSE(pValue->IsUndefined());
-  EXPECT_FALSE(pValue->IsNull());
-  EXPECT_FALSE(pValue->IsBoolean());
-  EXPECT_FALSE(pValue->IsString());
-  EXPECT_FALSE(pValue->IsNumber());
-  EXPECT_FALSE(pValue->IsObject());
-  EXPECT_FALSE(pValue->IsArray());
-  EXPECT_FALSE(pValue->IsFunction());
+  EXPECT_FALSE(pValue->IsUndefined(isolate()));
+  EXPECT_FALSE(pValue->IsNull(isolate()));
+  EXPECT_FALSE(pValue->IsBoolean(isolate()));
+  EXPECT_FALSE(pValue->IsString(isolate()));
+  EXPECT_FALSE(pValue->IsNumber(isolate()));
+  EXPECT_FALSE(pValue->IsObject(isolate()));
+  EXPECT_FALSE(pValue->IsArray(isolate()));
+  EXPECT_FALSE(pValue->IsFunction(isolate()));
 }
 
 TEST_F(CFXJSE_ValueEmbedderTest, EmptyArrayInsert) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
   // Test inserting empty values into arrays.
-  auto pValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
+  auto pValue = std::make_unique<CFXJSE_Value>();
   std::vector<std::unique_ptr<CFXJSE_Value>> vec;
   vec.push_back(std::move(pValue));
 
-  CFXJSE_Value array(GetIsolate());
-  array.SetArray(vec);
-  EXPECT_TRUE(array.IsArray());
+  CFXJSE_Value array;
+  array.SetArray(isolate(), vec);
+  EXPECT_TRUE(array.IsArray(isolate()));
 }
diff --git a/fxjs/xfa/cjx_boolean.cpp b/fxjs/xfa/cjx_boolean.cpp
index d44e039..a7e6f8b 100644
--- a/fxjs/xfa/cjx_boolean.cpp
+++ b/fxjs/xfa/cjx_boolean.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "fxjs/xfa/cjx_boolean.h"
 
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_boolean.h"
 
 CJX_Boolean::CJX_Boolean(CXFA_Boolean* node) : CJX_Object(node) {}
@@ -17,30 +19,33 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_Boolean::defaultValue(CFXJSE_Value* pValue,
+void CJX_Boolean::defaultValue(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value>* pValue,
                                bool bSetting,
                                XFA_Attribute eAttribute) {
   if (!bSetting) {
-    pValue->SetBoolean(GetContent(true).EqualsASCII("1"));
+    *pValue =
+        fxv8::NewBooleanHelper(pIsolate, GetContent(true).EqualsASCII("1"));
     return;
   }
 
   ByteString newValue;
-  if (pValue && !(pValue->IsNull() || pValue->IsUndefined()))
-    newValue = pValue->ToString();
+  if (pValue && !(fxv8::IsNull(*pValue) || fxv8::IsUndefined(*pValue)))
+    newValue = fxv8::ReentrantToByteStringHelper(pIsolate, *pValue);
 
   int32_t iValue = FXSYS_atoi(newValue.c_str());
   WideString wsNewValue(iValue == 0 ? L"0" : L"1");
   WideString wsFormatValue(wsNewValue);
-  CXFA_Node* pContainerNode = ToNode(GetXFAObject())->GetContainerNode();
+  CXFA_Node* pContainerNode = GetXFANode()->GetContainerNode();
   if (pContainerNode)
     wsFormatValue = pContainerNode->GetFormatDataValue(wsNewValue);
 
   SetContent(wsNewValue, wsFormatValue, true, true, true);
 }
 
-void CJX_Boolean::value(CFXJSE_Value* pValue,
+void CJX_Boolean::value(v8::Isolate* pIsolate,
+                        v8::Local<v8::Value>* pValue,
                         bool bSetting,
                         XFA_Attribute eAttribute) {
-  defaultValue(pValue, bSetting, eAttribute);
+  defaultValue(pIsolate, pValue, bSetting, eAttribute);
 }
diff --git a/fxjs/xfa/cjx_boolean.h b/fxjs/xfa/cjx_boolean.h
index d2a85ba..7fc2111 100644
--- a/fxjs/xfa/cjx_boolean.h
+++ b/fxjs/xfa/cjx_boolean.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Boolean final : public CJX_Object {
  public:
-  explicit CJX_Boolean(CXFA_Boolean* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Boolean() override;
 
   // CJX_Object:
@@ -24,6 +24,8 @@
   JSE_PROP(value);
 
  private:
+  explicit CJX_Boolean(CXFA_Boolean* node);
+
   using Type__ = CJX_Boolean;
   using ParentType__ = CJX_Object;
 
diff --git a/fxjs/xfa/cjx_container.cpp b/fxjs/xfa/cjx_container.cpp
index 44b1ff8..d6cabac 100644
--- a/fxjs/xfa/cjx_container.cpp
+++ b/fxjs/xfa/cjx_container.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,8 @@
 #include "fxjs/xfa/cfxjse_class.h"
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/cppgc/allocation.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/parser/cxfa_arraynodelist.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_field.h"
@@ -23,23 +25,24 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Container::~CJX_Container() {}
+CJX_Container::~CJX_Container() = default;
 
 bool CJX_Container::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Container::getDelta(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_Container::getDeltas(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  auto* pEngine = static_cast<CFXJSE_Engine*>(runtime);
-  return CJS_Result::Success(pEngine->NewXFAObject(
-      new CXFA_ArrayNodeList(GetDocument()),
-      GetDocument()->GetScriptContext()->GetJseNormalClass()->GetTemplate()));
+  CXFA_Document* pDoc = GetDocument();
+  auto* pList = cppgc::MakeGarbageCollected<CXFA_ArrayNodeList>(
+      pDoc->GetHeap()->GetAllocationHandle(), pDoc);
+  pDoc->GetNodeOwner()->PersistList(pList);
+  return CJS_Result::Success(runtime->NewNormalXFAObject(pList));
 }
diff --git a/fxjs/xfa/cjx_container.h b/fxjs/xfa/cjx_container.h
index 51675e5..b44dd72 100644
--- a/fxjs/xfa/cjx_container.h
+++ b/fxjs/xfa/cjx_container.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Container : public CJX_Node {
  public:
-  explicit CJX_Container(CXFA_Node* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Container() override;
 
   // CJX_Object:
@@ -23,6 +23,9 @@
   JSE_METHOD(getDelta);
   JSE_METHOD(getDeltas);
 
+ protected:
+  explicit CJX_Container(CXFA_Node* node);
+
  private:
   using Type__ = CJX_Container;
   using ParentType__ = CJX_Node;
diff --git a/fxjs/xfa/cjx_datawindow.cpp b/fxjs/xfa/cjx_datawindow.cpp
index d165140..10f8365 100644
--- a/fxjs/xfa/cjx_datawindow.cpp
+++ b/fxjs/xfa/cjx_datawindow.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,48 +22,52 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_DataWindow::~CJX_DataWindow() {}
+CJX_DataWindow::~CJX_DataWindow() = default;
 
 bool CJX_DataWindow::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_DataWindow::moveCurrentRecord(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_DataWindow::record(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_DataWindow::gotoRecord(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_DataWindow::isRecordGroup(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
-void CJX_DataWindow::recordsBefore(CFXJSE_Value* pValue,
+void CJX_DataWindow::recordsBefore(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Value>* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {}
 
-void CJX_DataWindow::currentRecordNumber(CFXJSE_Value* pValue,
+void CJX_DataWindow::currentRecordNumber(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Value>* pValue,
                                          bool bSetting,
                                          XFA_Attribute eAttribute) {}
 
-void CJX_DataWindow::recordsAfter(CFXJSE_Value* pValue,
+void CJX_DataWindow::recordsAfter(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {}
 
-void CJX_DataWindow::isDefined(CFXJSE_Value* pValue,
+void CJX_DataWindow::isDefined(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value>* pValue,
                                bool bSetting,
                                XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_datawindow.h b/fxjs/xfa/cjx_datawindow.h
index 6f3ad54..df5092d 100644
--- a/fxjs/xfa/cjx_datawindow.h
+++ b/fxjs/xfa/cjx_datawindow.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,12 +11,11 @@
 #include "fxjs/xfa/jse_define.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
-class CFXJSE_Value;
 class CScript_DataWindow;
 
 class CJX_DataWindow final : public CJX_Object {
  public:
-  explicit CJX_DataWindow(CScript_DataWindow* window);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_DataWindow() override;
 
   // CJX_Object:
@@ -33,6 +32,8 @@
   JSE_PROP(recordsBefore);
 
  private:
+  explicit CJX_DataWindow(CScript_DataWindow* window);
+
   using Type__ = CJX_DataWindow;
   using ParentType__ = CJX_Object;
 
diff --git a/fxjs/xfa/cjx_delta.cpp b/fxjs/xfa/cjx_delta.cpp
index 3ad20a6..266ddef 100644
--- a/fxjs/xfa/cjx_delta.cpp
+++ b/fxjs/xfa/cjx_delta.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,13 +18,13 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Delta::~CJX_Delta() {}
+CJX_Delta::~CJX_Delta() = default;
 
 bool CJX_Delta::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-CJS_Result CJX_Delta::restore(CFX_V8* runtime,
+CJS_Result CJX_Delta::restore(CFXJSE_Engine* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -32,14 +32,17 @@
   return CJS_Result::Success();
 }
 
-void CJX_Delta::currentValue(CFXJSE_Value* pValue,
+void CJX_Delta::currentValue(v8::Isolate* pIsolate,
+                             v8::Local<v8::Value>* pValue,
                              bool bSetting,
                              XFA_Attribute eAttribute) {}
 
-void CJX_Delta::savedValue(CFXJSE_Value* pValue,
+void CJX_Delta::savedValue(v8::Isolate* pIsolate,
+                           v8::Local<v8::Value>* pValue,
                            bool bSetting,
                            XFA_Attribute eAttribute) {}
 
-void CJX_Delta::target(CFXJSE_Value* pValue,
+void CJX_Delta::target(v8::Isolate* pIsolate,
+                       v8::Local<v8::Value>* pValue,
                        bool bSetting,
                        XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_delta.h b/fxjs/xfa/cjx_delta.h
index 716dd1e..1862558 100644
--- a/fxjs/xfa/cjx_delta.h
+++ b/fxjs/xfa/cjx_delta.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Delta final : public CJX_Object {
  public:
-  explicit CJX_Delta(CXFA_Delta* delta);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Delta() override;
 
   // CJX_Object:
@@ -27,6 +27,8 @@
   JSE_PROP(target);
 
  private:
+  explicit CJX_Delta(CXFA_Delta* delta);
+
   using Type__ = CJX_Delta;
   using ParentType__ = CJX_Object;
 
diff --git a/fxjs/xfa/cjx_desc.cpp b/fxjs/xfa/cjx_desc.cpp
index 2bbff2b..792f076 100644
--- a/fxjs/xfa/cjx_desc.cpp
+++ b/fxjs/xfa/cjx_desc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 #include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_desc.h"
 
 const CJX_MethodSpec CJX_Desc::MethodSpecs[] = {{"metadata", metadata_static}};
@@ -19,13 +20,13 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Desc::~CJX_Desc() {}
+CJX_Desc::~CJX_Desc() = default;
 
 bool CJX_Desc::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-CJS_Result CJX_Desc::metadata(CFX_V8* runtime,
+CJS_Result CJX_Desc::metadata(CFXJSE_Engine* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 0 && params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
diff --git a/fxjs/xfa/cjx_desc.h b/fxjs/xfa/cjx_desc.h
index 62cdec8..5474d56 100644
--- a/fxjs/xfa/cjx_desc.h
+++ b/fxjs/xfa/cjx_desc.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Desc final : public CJX_Node {
  public:
-  explicit CJX_Desc(CXFA_Desc* desc);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Desc() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_METHOD(metadata);
 
  private:
+  explicit CJX_Desc(CXFA_Desc* desc);
+
   using Type__ = CJX_Desc;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_draw.cpp b/fxjs/xfa/cjx_draw.cpp
index 4e8ddc1..82307b0 100644
--- a/fxjs/xfa/cjx_draw.cpp
+++ b/fxjs/xfa/cjx_draw.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,11 @@
 
 #include "fxjs/xfa/cjx_draw.h"
 
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "third_party/base/check.h"
+#include "v8/include/v8-primitive.h"
+#include "v8/include/v8-value.h"
 #include "xfa/fxfa/parser/cxfa_draw.h"
 
 CJX_Draw::CJX_Draw(CXFA_Draw* node) : CJX_Container(node) {}
@@ -17,32 +21,33 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_Draw::rawValue(CFXJSE_Value* pValue,
+void CJX_Draw::rawValue(v8::Isolate* pIsolate,
+                        v8::Local<v8::Value>* pValue,
                         bool bSetting,
                         XFA_Attribute eAttribute) {
-  defaultValue(pValue, bSetting, eAttribute);
+  defaultValue(pIsolate, pValue, bSetting, eAttribute);
 }
 
-void CJX_Draw::defaultValue(CFXJSE_Value* pValue,
+void CJX_Draw::defaultValue(v8::Isolate* pIsolate,
+                            v8::Local<v8::Value>* pValue,
                             bool bSetting,
                             XFA_Attribute eAttribute) {
   if (!bSetting) {
-    WideString content = GetContent(true);
-    if (content.IsEmpty())
-      pValue->SetNull();
-    else
-      pValue->SetString(content.ToUTF8().AsStringView());
-
+    ByteString content = GetContent(true).ToUTF8();
+    *pValue = content.IsEmpty()
+                  ? fxv8::NewNullHelper(pIsolate).As<v8::Value>()
+                  : fxv8::NewStringHelper(pIsolate, content.AsStringView())
+                        .As<v8::Value>();
     return;
   }
 
-  if (!pValue || !pValue->IsString())
+  if (!pValue || !fxv8::IsString(*pValue))
     return;
 
-  ASSERT(GetXFANode()->IsWidgetReady());
+  DCHECK(GetXFANode()->IsWidgetReady());
   if (GetXFANode()->GetFFWidgetType() != XFA_FFWidgetType::kText)
     return;
 
-  WideString wsNewValue = pValue->ToWideString();
+  WideString wsNewValue = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue);
   SetContent(wsNewValue, wsNewValue, true, true, true);
 }
diff --git a/fxjs/xfa/cjx_draw.h b/fxjs/xfa/cjx_draw.h
index bf3c3a7..a813f15 100644
--- a/fxjs/xfa/cjx_draw.h
+++ b/fxjs/xfa/cjx_draw.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Draw final : public CJX_Container {
  public:
-  explicit CJX_Draw(CXFA_Draw* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Draw() override;
 
   // CJX_Object:
@@ -24,6 +24,8 @@
   JSE_PROP(rawValue);
 
  private:
+  explicit CJX_Draw(CXFA_Draw* node);
+
   using Type__ = CJX_Draw;
   using ParentType__ = CJX_Container;
 
diff --git a/fxjs/xfa/cjx_encrypt.cpp b/fxjs/xfa/cjx_encrypt.cpp
index f50133d..51e02d6 100644
--- a/fxjs/xfa/cjx_encrypt.cpp
+++ b/fxjs/xfa/cjx_encrypt.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,6 +16,7 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_Encrypt::format(CFXJSE_Value* pValue,
+void CJX_Encrypt::format(v8::Isolate* pIsolate,
+                         v8::Local<v8::Value>* pValue,
                          bool bSetting,
                          XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_encrypt.h b/fxjs/xfa/cjx_encrypt.h
index 826bc1f..c7d4a46 100644
--- a/fxjs/xfa/cjx_encrypt.h
+++ b/fxjs/xfa/cjx_encrypt.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Encrypt final : public CJX_Node {
  public:
-  explicit CJX_Encrypt(CXFA_Encrypt* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Encrypt() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_PROP(format);
 
  private:
+  explicit CJX_Encrypt(CXFA_Encrypt* node);
+
   using Type__ = CJX_Encrypt;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_eventpseudomodel.cpp b/fxjs/xfa/cjx_eventpseudomodel.cpp
index d037eb7..66dbb07 100644
--- a/fxjs/xfa/cjx_eventpseudomodel.cpp
+++ b/fxjs/xfa/cjx_eventpseudomodel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,37 +9,48 @@
 #include <algorithm>
 #include <vector>
 
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "third_party/base/notreached.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
-#include "xfa/fxfa/cxfa_ffwidgethandler.h"
 #include "xfa/fxfa/parser/cscript_eventpseudomodel.h"
 
 namespace {
 
-void StringProperty(CFXJSE_Value* pReturn, WideString* wsValue, bool bSetting) {
+void StringProperty(v8::Isolate* pIsolate,
+                    v8::Local<v8::Value>* pReturn,
+                    WideString* wsValue,
+                    bool bSetting) {
   if (bSetting) {
-    *wsValue = pReturn->ToWideString();
+    *wsValue = fxv8::ReentrantToWideStringHelper(pIsolate, *pReturn);
     return;
   }
-  pReturn->SetString(wsValue->ToUTF8().AsStringView());
+  *pReturn = fxv8::NewStringHelper(pIsolate, wsValue->ToUTF8().AsStringView());
 }
 
-void IntegerProperty(CFXJSE_Value* pReturn, int32_t* iValue, bool bSetting) {
+void IntegerProperty(v8::Isolate* pIsolate,
+                     v8::Local<v8::Value>* pReturn,
+                     int32_t* iValue,
+                     bool bSetting) {
   if (bSetting) {
-    *iValue = pReturn->ToInteger();
+    *iValue = fxv8::ReentrantToInt32Helper(pIsolate, *pReturn);
     return;
   }
-  pReturn->SetInteger(*iValue);
+  *pReturn = fxv8::NewNumberHelper(pIsolate, *iValue);
 }
 
-void BooleanProperty(CFXJSE_Value* pReturn, bool* bValue, bool bSetting) {
+void BooleanProperty(v8::Isolate* pIsolate,
+                     v8::Local<v8::Value>* pReturn,
+                     bool* bValue,
+                     bool bSetting) {
   if (bSetting) {
-    *bValue = pReturn->ToBoolean();
+    *bValue = fxv8::ReentrantToBooleanHelper(pIsolate, *pReturn);
     return;
   }
-  pReturn->SetBoolean(*bValue);
+  *pReturn = fxv8::NewBooleanHelper(pIsolate, *bValue);
 }
 
 }  // namespace
@@ -53,127 +64,144 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_EventPseudoModel::~CJX_EventPseudoModel() {}
+CJX_EventPseudoModel::~CJX_EventPseudoModel() = default;
 
 bool CJX_EventPseudoModel::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_EventPseudoModel::cancelAction(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::cancelAction(v8::Isolate* pIsolate,
+                                        v8::Local<v8::Value>* pValue,
                                         bool bSetting,
                                         XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::CancelAction, bSetting);
+  Property(pIsolate, pValue, XFA_Event::CancelAction, bSetting);
 }
 
-void CJX_EventPseudoModel::change(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::change(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::Change, bSetting);
+  Property(pIsolate, pValue, XFA_Event::Change, bSetting);
 }
 
-void CJX_EventPseudoModel::commitKey(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::commitKey(v8::Isolate* pIsolate,
+                                     v8::Local<v8::Value>* pValue,
                                      bool bSetting,
                                      XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::CommitKey, bSetting);
+  Property(pIsolate, pValue, XFA_Event::CommitKey, bSetting);
 }
 
-void CJX_EventPseudoModel::fullText(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::fullText(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     bool bSetting,
                                     XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::FullText, bSetting);
+  Property(pIsolate, pValue, XFA_Event::FullText, bSetting);
 }
 
-void CJX_EventPseudoModel::keyDown(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::keyDown(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Value>* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::Keydown, bSetting);
+  Property(pIsolate, pValue, XFA_Event::Keydown, bSetting);
 }
 
-void CJX_EventPseudoModel::modifier(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::modifier(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     bool bSetting,
                                     XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::Modifier, bSetting);
+  Property(pIsolate, pValue, XFA_Event::Modifier, bSetting);
 }
 
-void CJX_EventPseudoModel::newContentType(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::newContentType(v8::Isolate* pIsolate,
+                                          v8::Local<v8::Value>* pValue,
                                           bool bSetting,
                                           XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::NewContentType, bSetting);
+  Property(pIsolate, pValue, XFA_Event::NewContentType, bSetting);
 }
 
-void CJX_EventPseudoModel::newText(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::newText(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Value>* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {
   if (bSetting)
     return;
 
-  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  CXFA_EventParam* pEventParam = pScriptContext->GetEventParam();
+  CXFA_EventParam* pEventParam =
+      GetDocument()->GetScriptContext()->GetEventParam();
   if (!pEventParam)
     return;
 
-  pValue->SetString(pEventParam->GetNewText().ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(
+      pIsolate, pEventParam->GetNewText().ToUTF8().AsStringView());
 }
 
-void CJX_EventPseudoModel::prevContentType(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::prevContentType(v8::Isolate* pIsolate,
+                                           v8::Local<v8::Value>* pValue,
                                            bool bSetting,
                                            XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::PreviousContentType, bSetting);
+  Property(pIsolate, pValue, XFA_Event::PreviousContentType, bSetting);
 }
 
-void CJX_EventPseudoModel::prevText(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::prevText(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     bool bSetting,
                                     XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::PreviousText, bSetting);
+  Property(pIsolate, pValue, XFA_Event::PreviousText, bSetting);
 }
 
-void CJX_EventPseudoModel::reenter(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::reenter(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Value>* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::Reenter, bSetting);
+  Property(pIsolate, pValue, XFA_Event::Reenter, bSetting);
 }
 
-void CJX_EventPseudoModel::selEnd(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::selEnd(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::SelectionEnd, bSetting);
+  Property(pIsolate, pValue, XFA_Event::SelectionEnd, bSetting);
 }
 
-void CJX_EventPseudoModel::selStart(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::selStart(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     bool bSetting,
                                     XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::SelectionStart, bSetting);
+  Property(pIsolate, pValue, XFA_Event::SelectionStart, bSetting);
 }
 
-void CJX_EventPseudoModel::shift(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::shift(v8::Isolate* pIsolate,
+                                 v8::Local<v8::Value>* pValue,
                                  bool bSetting,
                                  XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::Shift, bSetting);
+  Property(pIsolate, pValue, XFA_Event::Shift, bSetting);
 }
 
-void CJX_EventPseudoModel::soapFaultCode(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::soapFaultCode(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Value>* pValue,
                                          bool bSetting,
                                          XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::SoapFaultCode, bSetting);
+  Property(pIsolate, pValue, XFA_Event::SoapFaultCode, bSetting);
 }
 
-void CJX_EventPseudoModel::soapFaultString(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::soapFaultString(v8::Isolate* pIsolate,
+                                           v8::Local<v8::Value>* pValue,
                                            bool bSetting,
                                            XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::SoapFaultString, bSetting);
+  Property(pIsolate, pValue, XFA_Event::SoapFaultString, bSetting);
 }
 
-void CJX_EventPseudoModel::target(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::target(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::Target, bSetting);
+  Property(pIsolate, pValue, XFA_Event::Target, bSetting);
 }
 
 CJS_Result CJX_EventPseudoModel::emit(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<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();
 
@@ -181,26 +209,22 @@
   if (!pNotify)
     return CJS_Result::Success();
 
-  CXFA_FFWidgetHandler* pWidgetHandler = pNotify->GetWidgetHandler();
-  if (!pWidgetHandler)
-    return CJS_Result::Success();
-
-  pWidgetHandler->ProcessEvent(pEventParam->m_pTarget.Get(), pEventParam);
+  pNotify->HandleWidgetEvent(runtime->GetEventTarget(), pEventParam);
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_EventPseudoModel::reset(
-    CFX_V8* runtime,
+    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();
 
   return CJS_Result::Success();
 }
 
-void CJX_EventPseudoModel::Property(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::Property(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     XFA_Event dwFlag,
                                     bool bSetting) {
   // Only the cancelAction, selStart, selEnd and change properties are writable.
@@ -217,68 +241,72 @@
 
   switch (dwFlag) {
     case XFA_Event::CancelAction:
-      BooleanProperty(pValue, &pEventParam->m_bCancelAction, bSetting);
+      BooleanProperty(pIsolate, pValue, &pEventParam->m_bCancelAction,
+                      bSetting);
       break;
     case XFA_Event::Change:
-      StringProperty(pValue, &pEventParam->m_wsChange, bSetting);
+      StringProperty(pIsolate, pValue, &pEventParam->m_wsChange, bSetting);
       break;
     case XFA_Event::CommitKey:
-      IntegerProperty(pValue, &pEventParam->m_iCommitKey, bSetting);
+      IntegerProperty(pIsolate, pValue, &pEventParam->m_iCommitKey, bSetting);
       break;
     case XFA_Event::FullText:
-      StringProperty(pValue, &pEventParam->m_wsFullText, bSetting);
+      StringProperty(pIsolate, pValue, &pEventParam->m_wsFullText, bSetting);
       break;
     case XFA_Event::Keydown:
-      BooleanProperty(pValue, &pEventParam->m_bKeyDown, bSetting);
+      BooleanProperty(pIsolate, pValue, &pEventParam->m_bKeyDown, bSetting);
       break;
     case XFA_Event::Modifier:
-      BooleanProperty(pValue, &pEventParam->m_bModifier, bSetting);
+      BooleanProperty(pIsolate, pValue, &pEventParam->m_bModifier, bSetting);
       break;
     case XFA_Event::NewContentType:
-      StringProperty(pValue, &pEventParam->m_wsNewContentType, bSetting);
+      StringProperty(pIsolate, pValue, &pEventParam->m_wsNewContentType,
+                     bSetting);
       break;
     case XFA_Event::NewText:
       NOTREACHED();
       break;
     case XFA_Event::PreviousContentType:
-      StringProperty(pValue, &pEventParam->m_wsPrevContentType, bSetting);
+      StringProperty(pIsolate, pValue, &pEventParam->m_wsPrevContentType,
+                     bSetting);
       break;
     case XFA_Event::PreviousText:
-      StringProperty(pValue, &pEventParam->m_wsPrevText, bSetting);
+      StringProperty(pIsolate, pValue, &pEventParam->m_wsPrevText, bSetting);
       break;
     case XFA_Event::Reenter:
-      BooleanProperty(pValue, &pEventParam->m_bReenter, bSetting);
+      BooleanProperty(pIsolate, pValue, &pEventParam->m_bReenter, bSetting);
       break;
     case XFA_Event::SelectionEnd:
-      IntegerProperty(pValue, &pEventParam->m_iSelEnd, bSetting);
+      IntegerProperty(pIsolate, pValue, &pEventParam->m_iSelEnd, bSetting);
 
       pEventParam->m_iSelEnd = std::max(0, pEventParam->m_iSelEnd);
-      pEventParam->m_iSelEnd =
-          std::min(static_cast<size_t>(pEventParam->m_iSelEnd),
-                   pEventParam->m_wsPrevText.GetLength());
+      pEventParam->m_iSelEnd = std::min(
+          pEventParam->m_iSelEnd, pdfium::base::checked_cast<int32_t>(
+                                      pEventParam->m_wsPrevText.GetLength()));
       pEventParam->m_iSelStart =
           std::min(pEventParam->m_iSelStart, pEventParam->m_iSelEnd);
       break;
     case XFA_Event::SelectionStart:
-      IntegerProperty(pValue, &pEventParam->m_iSelStart, bSetting);
+      IntegerProperty(pIsolate, pValue, &pEventParam->m_iSelStart, bSetting);
       pEventParam->m_iSelStart = std::max(0, pEventParam->m_iSelStart);
-      pEventParam->m_iSelStart =
-          std::min(static_cast<size_t>(pEventParam->m_iSelStart),
-                   pEventParam->m_wsPrevText.GetLength());
+      pEventParam->m_iSelStart = std::min(
+          pEventParam->m_iSelStart, pdfium::base::checked_cast<int32_t>(
+                                        pEventParam->m_wsPrevText.GetLength()));
       pEventParam->m_iSelEnd =
           std::max(pEventParam->m_iSelStart, pEventParam->m_iSelEnd);
       break;
     case XFA_Event::Shift:
-      BooleanProperty(pValue, &pEventParam->m_bShift, bSetting);
+      BooleanProperty(pIsolate, pValue, &pEventParam->m_bShift, bSetting);
       break;
     case XFA_Event::SoapFaultCode:
-      StringProperty(pValue, &pEventParam->m_wsSoapFaultCode, bSetting);
+      StringProperty(pIsolate, pValue, &pEventParam->m_wsSoapFaultCode,
+                     bSetting);
       break;
     case XFA_Event::SoapFaultString:
-      StringProperty(pValue, &pEventParam->m_wsSoapFaultString, bSetting);
+      StringProperty(pIsolate, pValue, &pEventParam->m_wsSoapFaultString,
+                     bSetting);
       break;
     case XFA_Event::Target:
-    default:
       break;
   }
 }
diff --git a/fxjs/xfa/cjx_eventpseudomodel.h b/fxjs/xfa/cjx_eventpseudomodel.h
index 6f3cc84..3be881a 100644
--- a/fxjs/xfa/cjx_eventpseudomodel.h
+++ b/fxjs/xfa/cjx_eventpseudomodel.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,6 @@
 #include "fxjs/xfa/cjx_object.h"
 #include "fxjs/xfa/jse_define.h"
 
-class CFXJSE_Value;
 class CScript_EventPseudoModel;
 
 enum class XFA_Event {
@@ -35,7 +34,7 @@
 
 class CJX_EventPseudoModel final : public CJX_Object {
  public:
-  explicit CJX_EventPseudoModel(CScript_EventPseudoModel* model);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_EventPseudoModel() override;
 
   // CJX_Object:
@@ -63,13 +62,18 @@
   JSE_PROP(target);
 
  private:
+  explicit CJX_EventPseudoModel(CScript_EventPseudoModel* model);
+
   using Type__ = CJX_EventPseudoModel;
   using ParentType__ = CJX_Object;
 
   static const TypeTag static_type__ = TypeTag::EventPseudoModel;
   static const CJX_MethodSpec MethodSpecs[];
 
-  void Property(CFXJSE_Value* pValue, XFA_Event dwFlag, bool bSetting);
+  void Property(v8::Isolate* pIsolate,
+                v8::Local<v8::Value>* pValue,
+                XFA_Event dwFlag,
+                bool bSetting);
 };
 
 #endif  // FXJS_XFA_CJX_EVENTPSEUDOMODEL_H_
diff --git a/fxjs/xfa/cjx_exclgroup.cpp b/fxjs/xfa/cjx_exclgroup.cpp
index 00bf539..641f0f2 100644
--- a/fxjs/xfa/cjx_exclgroup.cpp
+++ b/fxjs/xfa/cjx_exclgroup.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,9 +8,11 @@
 
 #include <vector>
 
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/fxfa.h"
@@ -28,14 +30,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_ExclGroup::~CJX_ExclGroup() {}
+CJX_ExclGroup::~CJX_ExclGroup() = default;
 
 bool CJX_ExclGroup::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_ExclGroup::execEvent(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -46,7 +48,7 @@
 }
 
 CJS_Result CJX_ExclGroup::execInitialize(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -59,7 +61,7 @@
 }
 
 CJS_Result CJX_ExclGroup::execCalculate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -72,7 +74,7 @@
 }
 
 CJS_Result CJX_ExclGroup::execValidate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -88,7 +90,7 @@
 }
 
 CJS_Result CJX_ExclGroup::selectedMember(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -102,20 +104,16 @@
     pReturnNode = node->GetSelectedMember();
   } else {
     pReturnNode = node->SetSelectedMember(
-        runtime->ToWideString(params[0]).AsStringView(), true);
+        runtime->ToWideString(params[0]).AsStringView());
   }
   if (!pReturnNode)
     return CJS_Result::Success(runtime->NewNull());
 
-  CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-          pReturnNode);
-
-  return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+  return CJS_Result::Success(runtime->GetOrCreateJSBindingFromMap(pReturnNode));
 }
 
-void CJX_ExclGroup::defaultValue(CFXJSE_Value* pValue,
+void CJX_ExclGroup::defaultValue(v8::Isolate* pIsolate,
+                                 v8::Local<v8::Value>* pValue,
                                  bool bSetting,
                                  XFA_Attribute eAttribute) {
   CXFA_Node* node = GetXFANode();
@@ -123,33 +121,37 @@
     return;
 
   if (bSetting) {
-    node->SetSelectedMemberByValue(pValue->ToWideString().AsStringView(), true,
-                                   true, true);
+    node->SetSelectedMemberByValue(
+        fxv8::ReentrantToWideStringHelper(pIsolate, *pValue).AsStringView(),
+        true, true, true);
     return;
   }
 
   WideString wsValue = GetContent(true);
   XFA_VERSION curVersion = GetDocument()->GetCurVersionMode();
   if (wsValue.IsEmpty() && curVersion >= XFA_VERSION_300) {
-    pValue->SetNull();
+    *pValue = fxv8::NewNullHelper(pIsolate);
     return;
   }
-  pValue->SetString(wsValue.ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(pIsolate, wsValue.ToUTF8().AsStringView());
 }
 
-void CJX_ExclGroup::rawValue(CFXJSE_Value* pValue,
+void CJX_ExclGroup::rawValue(v8::Isolate* pIsolate,
+                             v8::Local<v8::Value>* pValue,
                              bool bSetting,
                              XFA_Attribute eAttribute) {
-  defaultValue(pValue, bSetting, eAttribute);
+  defaultValue(pIsolate, pValue, bSetting, eAttribute);
 }
 
-void CJX_ExclGroup::transient(CFXJSE_Value* pValue,
+void CJX_ExclGroup::transient(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value>* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {}
 
-void CJX_ExclGroup::errorText(CFXJSE_Value* pValue,
+void CJX_ExclGroup::errorText(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value>* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {
   if (bSetting)
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
 }
diff --git a/fxjs/xfa/cjx_exclgroup.h b/fxjs/xfa/cjx_exclgroup.h
index b459808..f6a983c 100644
--- a/fxjs/xfa/cjx_exclgroup.h
+++ b/fxjs/xfa/cjx_exclgroup.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_ExclGroup final : public CJX_Node {
  public:
-  explicit CJX_ExclGroup(CXFA_ExclGroup* group);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_ExclGroup() override;
 
   // CJX_Object:
@@ -32,6 +32,8 @@
   JSE_PROP(transient);
 
  private:
+  explicit CJX_ExclGroup(CXFA_ExclGroup* group);
+
   using Type__ = CJX_ExclGroup;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_extras.cpp b/fxjs/xfa/cjx_extras.cpp
index a2e2eea..74cdbba 100644
--- a/fxjs/xfa/cjx_extras.cpp
+++ b/fxjs/xfa/cjx_extras.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,6 +16,7 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_Extras::type(CFXJSE_Value* pValue,
+void CJX_Extras::type(v8::Isolate* pIsolate,
+                      v8::Local<v8::Value>* pValue,
                       bool bSetting,
                       XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_extras.h b/fxjs/xfa/cjx_extras.h
index 0723575..6a1b212 100644
--- a/fxjs/xfa/cjx_extras.h
+++ b/fxjs/xfa/cjx_extras.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Extras final : public CJX_Node {
  public:
-  explicit CJX_Extras(CXFA_Extras* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Extras() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_PROP(type);
 
  private:
+  explicit CJX_Extras(CXFA_Extras* node);
+
   using Type__ = CJX_Extras;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_field.cpp b/fxjs/xfa/cjx_field.cpp
index ef334a3..8d158dc 100644
--- a/fxjs/xfa/cjx_field.cpp
+++ b/fxjs/xfa/cjx_field.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,8 +9,10 @@
 #include <vector>
 
 #include "fxjs/cfx_v8.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fgas/crt/cfgas_decimal.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
@@ -37,14 +39,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Field::~CJX_Field() {}
+CJX_Field::~CJX_Field() = default;
 
 bool CJX_Field::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Field::clearItems(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CXFA_Node* node = GetXFANode();
   if (node->IsWidgetReady())
@@ -53,7 +55,7 @@
 }
 
 CJS_Result CJX_Field::execEvent(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -69,7 +71,7 @@
 }
 
 CJS_Result CJX_Field::execInitialize(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -83,7 +85,7 @@
 }
 
 CJS_Result CJX_Field::deleteItem(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -97,7 +99,7 @@
 }
 
 CJS_Result CJX_Field::getSaveItem(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -110,8 +112,8 @@
   if (!node->IsWidgetReady())
     return CJS_Result::Success(runtime->NewNull());
 
-  Optional<WideString> value = node->GetChoiceListItem(iIndex, true);
-  if (!value)
+  absl::optional<WideString> value = node->GetChoiceListItem(iIndex, true);
+  if (!value.has_value())
     return CJS_Result::Success(runtime->NewNull());
 
   return CJS_Result::Success(
@@ -119,7 +121,7 @@
 }
 
 CJS_Result CJX_Field::boundItem(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -135,7 +137,7 @@
 }
 
 CJS_Result CJX_Field::getItemState(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -149,7 +151,7 @@
 }
 
 CJS_Result CJX_Field::execCalculate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -163,7 +165,7 @@
 }
 
 CJS_Result CJX_Field::getDisplayItem(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -176,8 +178,8 @@
   if (!node->IsWidgetReady())
     return CJS_Result::Success(runtime->NewNull());
 
-  Optional<WideString> value = node->GetChoiceListItem(iIndex, false);
-  if (!value)
+  absl::optional<WideString> value = node->GetChoiceListItem(iIndex, false);
+  if (!value.has_value())
     return CJS_Result::Success(runtime->NewNull());
 
   return CJS_Result::Success(
@@ -185,7 +187,7 @@
 }
 
 CJS_Result CJX_Field::setItemState(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -196,16 +198,16 @@
 
   int32_t iIndex = runtime->ToInt32(params[0]);
   if (runtime->ToInt32(params[1]) != 0) {
-    node->SetItemState(iIndex, true, true, true, true);
+    node->SetItemState(iIndex, true, true, true);
     return CJS_Result::Success();
   }
   if (node->GetItemState(iIndex))
-    node->SetItemState(iIndex, false, true, true, true);
+    node->SetItemState(iIndex, false, true, true);
 
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Field::addItem(CFX_V8* runtime,
+CJS_Result CJX_Field::addItem(CFXJSE_Engine* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1 && params.size() != 2)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -227,7 +229,7 @@
 }
 
 CJS_Result CJX_Field::execValidate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -242,7 +244,8 @@
       runtime->NewBoolean(iRet != XFA_EventError::kError));
 }
 
-void CJX_Field::defaultValue(CFXJSE_Value* pValue,
+void CJX_Field::defaultValue(v8::Isolate* pIsolate,
+                             v8::Local<v8::Value>* pValue,
                              bool bSetting,
                              XFA_Attribute eAttribute) {
   CXFA_Node* xfaNode = GetXFANode();
@@ -252,12 +255,12 @@
   if (bSetting) {
     if (pValue) {
       xfaNode->SetPreNull(xfaNode->IsNull());
-      xfaNode->SetIsNull(pValue->IsNull());
+      xfaNode->SetIsNull(fxv8::IsNull(*pValue));
     }
 
     WideString wsNewText;
-    if (pValue && !(pValue->IsNull() || pValue->IsUndefined()))
-      wsNewText = pValue->ToWideString();
+    if (pValue && !(fxv8::IsNull(*pValue) || fxv8::IsUndefined(*pValue)))
+      wsNewText = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue);
     if (xfaNode->GetUIChildNode()->GetElementType() == XFA_Element::NumericEdit)
       wsNewText = xfaNode->NumericLimit(wsNewText);
 
@@ -272,7 +275,7 @@
 
   WideString content = GetContent(true);
   if (content.IsEmpty()) {
-    pValue->SetNull();
+    *pValue = fxv8::NewNullHelper(pIsolate);
     return;
   }
 
@@ -282,24 +285,27 @@
     if (xfaNode->GetUIChildNode()->GetElementType() ==
             XFA_Element::NumericEdit &&
         (pNode->JSObject()->GetInteger(XFA_Attribute::FracDigits) == -1)) {
-      pValue->SetString(content.ToUTF8().AsStringView());
+      *pValue =
+          fxv8::NewStringHelper(pIsolate, content.ToUTF8().AsStringView());
     } else {
       CFGAS_Decimal decimal(content.AsStringView());
-      pValue->SetFloat(decimal.ToFloat());
+      *pValue = fxv8::NewNumberHelper(pIsolate, decimal.ToFloat());
     }
   } else if (pNode && pNode->GetElementType() == XFA_Element::Integer) {
-    pValue->SetInteger(FXSYS_wtoi(content.c_str()));
+    *pValue = fxv8::NewNumberHelper(pIsolate, FXSYS_wtoi(content.c_str()));
   } else if (pNode && pNode->GetElementType() == XFA_Element::Boolean) {
-    pValue->SetBoolean(FXSYS_wtoi(content.c_str()) != 0);
+    *pValue =
+        fxv8::NewBooleanHelper(pIsolate, FXSYS_wtoi(content.c_str()) != 0);
   } else if (pNode && pNode->GetElementType() == XFA_Element::Float) {
     CFGAS_Decimal decimal(content.AsStringView());
-    pValue->SetFloat(decimal.ToFloat());
+    *pValue = fxv8::NewNumberHelper(pIsolate, decimal.ToFloat());
   } else {
-    pValue->SetString(content.ToUTF8().AsStringView());
+    *pValue = fxv8::NewStringHelper(pIsolate, content.ToUTF8().AsStringView());
   }
 }
 
-void CJX_Field::editValue(CFXJSE_Value* pValue,
+void CJX_Field::editValue(v8::Isolate* pIsolate,
+                          v8::Local<v8::Value>* pValue,
                           bool bSetting,
                           XFA_Attribute eAttribute) {
   CXFA_Node* node = GetXFANode();
@@ -307,20 +313,24 @@
     return;
 
   if (bSetting) {
-    node->SetValue(XFA_VALUEPICTURE_Edit, pValue->ToWideString());
+    node->SetValue(XFA_ValuePicture::kEdit,
+                   fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
     return;
   }
-  pValue->SetString(
-      node->GetValue(XFA_VALUEPICTURE_Edit).ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(
+      pIsolate,
+      node->GetValue(XFA_ValuePicture::kEdit).ToUTF8().AsStringView());
 }
 
-void CJX_Field::formatMessage(CFXJSE_Value* pValue,
+void CJX_Field::formatMessage(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value>* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {
-  ScriptSomMessage(pValue, bSetting, XFA_SOM_FormatMessage);
+  ScriptSomMessage(pIsolate, pValue, bSetting, SOMMessageType::kFormatMessage);
 }
 
-void CJX_Field::formattedValue(CFXJSE_Value* pValue,
+void CJX_Field::formattedValue(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value>* pValue,
                                bool bSetting,
                                XFA_Attribute eAttribute) {
   CXFA_Node* node = GetXFANode();
@@ -328,40 +338,44 @@
     return;
 
   if (bSetting) {
-    node->SetValue(XFA_VALUEPICTURE_Display, pValue->ToWideString());
+    node->SetValue(XFA_ValuePicture::kDisplay,
+                   fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
     return;
   }
-  pValue->SetString(
-      node->GetValue(XFA_VALUEPICTURE_Display).ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(
+      pIsolate,
+      node->GetValue(XFA_ValuePicture::kDisplay).ToUTF8().AsStringView());
 }
 
-void CJX_Field::length(CFXJSE_Value* pValue,
+void CJX_Field::length(v8::Isolate* pIsolate,
+                       v8::Local<v8::Value>* pValue,
                        bool bSetting,
                        XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
   CXFA_Node* node = GetXFANode();
-  if (!node->IsWidgetReady()) {
-    pValue->SetInteger(0);
-    return;
-  }
-  pValue->SetInteger(node->CountChoiceListItems(true));
+  *pValue = fxv8::NewNumberHelper(
+      pIsolate, node->IsWidgetReady() ? pdfium::base::checked_cast<int>(
+                                            node->CountChoiceListItems(true))
+                                      : 0);
 }
 
-void CJX_Field::parentSubform(CFXJSE_Value* pValue,
+void CJX_Field::parentSubform(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value>* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->SetNull();
+  *pValue = fxv8::NewNullHelper(pIsolate);
 }
 
-void CJX_Field::selectedIndex(CFXJSE_Value* pValue,
+void CJX_Field::selectedIndex(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value>* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {
   CXFA_Node* node = GetXFANode();
@@ -369,21 +383,22 @@
     return;
 
   if (!bSetting) {
-    pValue->SetInteger(node->GetSelectedItem(0));
+    *pValue = fxv8::NewNumberHelper(pIsolate, node->GetSelectedItem(0));
     return;
   }
 
-  int32_t iIndex = pValue->ToInteger();
+  int32_t iIndex = fxv8::ReentrantToInt32Helper(pIsolate, *pValue);
   if (iIndex == -1) {
     node->ClearAllSelections();
     return;
   }
 
-  node->SetItemState(iIndex, true, true, true, true);
+  node->SetItemState(iIndex, true, true, true);
 }
 
-void CJX_Field::rawValue(CFXJSE_Value* pValue,
+void CJX_Field::rawValue(v8::Isolate* pIsolate,
+                         v8::Local<v8::Value>* pValue,
                          bool bSetting,
                          XFA_Attribute eAttribute) {
-  defaultValue(pValue, bSetting, eAttribute);
+  defaultValue(pIsolate, pValue, bSetting, eAttribute);
 }
diff --git a/fxjs/xfa/cjx_field.h b/fxjs/xfa/cjx_field.h
index 6e16e7a..5628fc4 100644
--- a/fxjs/xfa/cjx_field.h
+++ b/fxjs/xfa/cjx_field.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Field final : public CJX_Container {
  public:
-  explicit CJX_Field(CXFA_Field* field);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Field() override;
 
   // CJX_Object:
@@ -43,6 +43,8 @@
   JSE_PROP(selectedIndex);
 
  private:
+  explicit CJX_Field(CXFA_Field* field);
+
   using Type__ = CJX_Field;
   using ParentType__ = CJX_Container;
 
diff --git a/fxjs/xfa/cjx_form.cpp b/fxjs/xfa/cjx_form.cpp
index 07e8602..ec68b70 100644
--- a/fxjs/xfa/cjx_form.cpp
+++ b/fxjs/xfa/cjx_form.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,9 +8,12 @@
 
 #include <vector>
 
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/cppgc/allocation.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cxfa_arraynodelist.h"
@@ -29,43 +32,42 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Form::~CJX_Form() {}
+CJX_Form::~CJX_Form() = default;
 
 bool CJX_Form::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Form::formNodes(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_Node* pDataNode =
-      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+  CXFA_Node* pDataNode = ToNode(runtime->ToXFAObject(params[0]));
   if (!pDataNode)
     return CJS_Result::Failure(JSMessage::kValueError);
 
-  CXFA_ArrayNodeList* pFormNodes = new CXFA_ArrayNodeList(GetDocument());
-  CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-          pFormNodes);
+  CXFA_Document* pDoc = GetDocument();
+  auto* pFormNodes = cppgc::MakeGarbageCollected<CXFA_ArrayNodeList>(
+      pDoc->GetHeap()->GetAllocationHandle(), pDoc);
+  pDoc->GetNodeOwner()->PersistList(pFormNodes);
 
-  return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+  v8::Local<v8::Value> value = runtime->GetOrCreateJSBindingFromMap(pFormNodes);
+  return CJS_Result::Success(value);
 }
 
-CJS_Result CJX_Form::remerge(CFX_V8* runtime,
+CJS_Result CJX_Form::remerge(CFXJSE_Engine* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  GetDocument()->DoDataRemerge(true);
+  GetDocument()->DoDataRemerge();
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_Form::execInitialize(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -78,10 +80,9 @@
 }
 
 CJS_Result CJX_Form::recalculate(
-    CFX_V8* runtime,
+    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();
@@ -100,7 +101,7 @@
 }
 
 CJS_Result CJX_Form::execCalculate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -113,7 +114,7 @@
 }
 
 CJS_Result CJX_Form::execValidate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 0)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -128,15 +129,20 @@
       runtime->NewBoolean(iRet != XFA_EventError::kError));
 }
 
-void CJX_Form::checksumS(CFXJSE_Value* pValue,
+void CJX_Form::checksumS(v8::Isolate* pIsolate,
+                         v8::Local<v8::Value>* pValue,
                          bool bSetting,
                          XFA_Attribute eAttribute) {
   if (bSetting) {
-    SetAttribute(XFA_Attribute::Checksum, pValue->ToWideString().AsStringView(),
-                 false);
+    SetAttributeByEnum(XFA_Attribute::Checksum,
+                       fxv8::ReentrantToWideStringHelper(pIsolate, *pValue),
+                       false);
     return;
   }
 
-  Optional<WideString> checksum = TryAttribute(XFA_Attribute::Checksum, false);
-  pValue->SetString(checksum ? checksum->ToUTF8().AsStringView() : "");
+  absl::optional<WideString> checksum =
+      TryAttribute(XFA_Attribute::Checksum, false);
+  *pValue = fxv8::NewStringHelper(
+      pIsolate,
+      checksum.has_value() ? checksum.value().ToUTF8().AsStringView() : "");
 }
diff --git a/fxjs/xfa/cjx_form.h b/fxjs/xfa/cjx_form.h
index c7b1ce3..05ea04c 100644
--- a/fxjs/xfa/cjx_form.h
+++ b/fxjs/xfa/cjx_form.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Form final : public CJX_Model {
  public:
-  explicit CJX_Form(CXFA_Form* form);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Form() override;
 
   // CJX_Object:
@@ -30,6 +30,8 @@
   JSE_PROP(checksumS);
 
  private:
+  explicit CJX_Form(CXFA_Form* form);
+
   using Type__ = CJX_Form;
   using ParentType__ = CJX_Model;
 
diff --git a/fxjs/xfa/cjx_handler.cpp b/fxjs/xfa/cjx_handler.cpp
index 21be88c..d8caeca 100644
--- a/fxjs/xfa/cjx_handler.cpp
+++ b/fxjs/xfa/cjx_handler.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,6 +16,7 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_Handler::version(CFXJSE_Value* pValue,
+void CJX_Handler::version(v8::Isolate* pIsolate,
+                          v8::Local<v8::Value>* pValue,
                           bool bSetting,
                           XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_handler.h b/fxjs/xfa/cjx_handler.h
index 348eaf1..e5cd2cb 100644
--- a/fxjs/xfa/cjx_handler.h
+++ b/fxjs/xfa/cjx_handler.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Handler final : public CJX_TextNode {
  public:
-  explicit CJX_Handler(CXFA_Handler* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Handler() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_PROP(version);
 
  private:
+  explicit CJX_Handler(CXFA_Handler* node);
+
   using Type__ = CJX_Handler;
   using ParentType__ = CJX_TextNode;
 
diff --git a/fxjs/xfa/cjx_hostpseudomodel.cpp b/fxjs/xfa/cjx_hostpseudomodel.cpp
index c45495c..f79a0cd 100644
--- a/fxjs/xfa/cjx_hostpseudomodel.cpp
+++ b/fxjs/xfa/cjx_hostpseudomodel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,34 +6,33 @@
 
 #include "fxjs/xfa/cjx_hostpseudomodel.h"
 
-#include <memory>
 #include <vector>
 
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "third_party/base/check.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cscript_hostpseudomodel.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 
 namespace {
 
-int32_t FilterName(WideStringView wsExpression,
-                   int32_t nStart,
-                   WideString& wsFilter) {
-  ASSERT(nStart > -1);
-  int32_t iLength = wsExpression.GetLength();
-  if (nStart >= iLength)
-    return iLength;
+size_t FilterName(WideStringView wsExpression,
+                  size_t nStart,
+                  WideString& wsFilter) {
+  const size_t nLength = wsExpression.GetLength();
+  if (nStart >= nLength)
+    return nLength;
 
-  int32_t nCount = 0;
+  size_t nCount = 0;
   {
     // Span's lifetime must end before ReleaseBuffer() below.
-    pdfium::span<wchar_t> pBuf = wsFilter.GetBuffer(iLength - nStart);
+    pdfium::span<wchar_t> pBuf = wsFilter.GetBuffer(nLength - nStart);
     const wchar_t* pSrc = wsExpression.unterminated_c_str();
-    while (nStart < iLength) {
+    while (nStart < nLength) {
       wchar_t wCur = pSrc[nStart++];
       if (wCur == ',')
         break;
@@ -70,13 +69,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_HostPseudoModel::~CJX_HostPseudoModel() {}
+CJX_HostPseudoModel::~CJX_HostPseudoModel() = default;
 
 bool CJX_HostPseudoModel::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_HostPseudoModel::appType(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::appType(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
@@ -84,44 +84,47 @@
     return;
 
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->SetString("Exchange");
+  *pValue = fxv8::NewStringHelper(pIsolate, "Exchange");
 }
 
-void CJX_HostPseudoModel::calculationsEnabled(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::calculationsEnabled(v8::Isolate* pIsolate,
+                                              v8::Local<v8::Value>* pValue,
                                               bool bSetting,
                                               XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return;
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
   if (bSetting) {
-    hDoc->GetDocEnvironment()->SetCalculationsEnabled(hDoc,
-                                                      pValue->ToBoolean());
+    hDoc->SetCalculationsEnabled(
+        fxv8::ReentrantToBooleanHelper(pIsolate, *pValue));
     return;
   }
-  pValue->SetBoolean(hDoc->GetDocEnvironment()->IsCalculationsEnabled(hDoc));
+  *pValue = fxv8::NewBooleanHelper(pIsolate, hDoc->IsCalculationsEnabled());
 }
 
-void CJX_HostPseudoModel::currentPage(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::currentPage(v8::Isolate* pIsolate,
+                                      v8::Local<v8::Value>* pValue,
                                       bool bSetting,
                                       XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return;
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
   if (bSetting) {
-    hDoc->GetDocEnvironment()->SetCurrentPage(hDoc, pValue->ToInteger());
+    hDoc->SetCurrentPage(fxv8::ReentrantToInt32Helper(pIsolate, *pValue));
     return;
   }
-  pValue->SetInteger(hDoc->GetDocEnvironment()->GetCurrentPage(hDoc));
+  *pValue = fxv8::NewNumberHelper(pIsolate, hDoc->GetCurrentPage());
 }
 
-void CJX_HostPseudoModel::language(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::language(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Value>* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
@@ -129,29 +132,33 @@
     return;
 
   if (bSetting) {
-    ThrowException(WideString::FromASCII("Unable to set language value."));
+    ThrowException(pIsolate,
+                   WideString::FromASCII("Unable to set language value."));
     return;
   }
-  pValue->SetString(
-      pNotify->GetAppProvider()->GetLanguage().ToUTF8().AsStringView());
+  ByteString lang = pNotify->GetAppProvider()->GetLanguage().ToUTF8();
+  *pValue = fxv8::NewStringHelper(pIsolate, lang.AsStringView());
 }
 
-void CJX_HostPseudoModel::numPages(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::numPages(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Value>* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return;
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
   if (bSetting) {
-    ThrowException(WideString::FromASCII("Unable to set numPages value."));
+    ThrowException(pIsolate,
+                   WideString::FromASCII("Unable to set numPages value."));
     return;
   }
-  pValue->SetInteger(hDoc->GetDocEnvironment()->CountPages(hDoc));
+  *pValue = fxv8::NewNumberHelper(pIsolate, hDoc->CountPages());
 }
 
-void CJX_HostPseudoModel::platform(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::platform(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Value>* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
@@ -159,14 +166,16 @@
     return;
 
   if (bSetting) {
-    ThrowException(WideString::FromASCII("Unable to set platform value."));
+    ThrowException(pIsolate,
+                   WideString::FromASCII("Unable to set platform value."));
     return;
   }
-  pValue->SetString(
-      pNotify->GetAppProvider()->GetPlatform().ToUTF8().AsStringView());
+  ByteString plat = pNotify->GetAppProvider()->GetPlatform().ToUTF8();
+  *pValue = fxv8::NewStringHelper(pIsolate, plat.AsStringView());
 }
 
-void CJX_HostPseudoModel::title(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::title(v8::Isolate* pIsolate,
+                                v8::Local<v8::Value>* pValue,
                                 bool bSetting,
                                 XFA_Attribute eAttribute) {
   if (!GetDocument()->GetScriptContext()->IsRunAtClient())
@@ -176,58 +185,63 @@
   if (!pNotify)
     return;
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
   if (bSetting) {
-    hDoc->GetDocEnvironment()->SetTitle(hDoc, pValue->ToWideString());
+    hDoc->SetTitle(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
     return;
   }
 
-  WideString wsTitle;
-  hDoc->GetDocEnvironment()->GetTitle(hDoc, wsTitle);
-  pValue->SetString(wsTitle.ToUTF8().AsStringView());
+  ByteString bsTitle = hDoc->GetTitle().ToUTF8();
+  *pValue = fxv8::NewStringHelper(pIsolate, bsTitle.AsStringView());
 }
 
-void CJX_HostPseudoModel::validationsEnabled(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::validationsEnabled(v8::Isolate* pIsolate,
+                                             v8::Local<v8::Value>* pValue,
                                              bool bSetting,
                                              XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return;
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
   if (bSetting) {
-    hDoc->GetDocEnvironment()->SetValidationsEnabled(hDoc, pValue->ToBoolean());
+    hDoc->SetValidationsEnabled(
+        fxv8::ReentrantToBooleanHelper(pIsolate, *pValue));
     return;
   }
 
-  bool bEnabled = hDoc->GetDocEnvironment()->IsValidationsEnabled(hDoc);
-  pValue->SetBoolean(bEnabled);
+  *pValue = fxv8::NewBooleanHelper(pIsolate, hDoc->IsValidationsEnabled());
 }
 
-void CJX_HostPseudoModel::variation(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::variation(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     bool bSetting,
                                     XFA_Attribute eAttribute) {
   if (!GetDocument()->GetScriptContext()->IsRunAtClient())
     return;
 
   if (bSetting) {
-    ThrowException(WideString::FromASCII("Unable to set variation value."));
+    ThrowException(pIsolate,
+                   WideString::FromASCII("Unable to set variation value."));
     return;
   }
-  pValue->SetString("Full");
+  *pValue = fxv8::NewStringHelper(pIsolate, "Full");
 }
 
-void CJX_HostPseudoModel::version(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::version(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowException(WideString::FromASCII("Unable to set version value."));
+    ThrowException(pIsolate,
+                   WideString::FromASCII("Unable to set version value."));
     return;
   }
-  pValue->SetString("11");
+  *pValue = fxv8::NewStringHelper(pIsolate, "11");
 }
 
-void CJX_HostPseudoModel::name(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::name(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value>* pValue,
                                bool bSetting,
                                XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
@@ -235,18 +249,19 @@
     return;
 
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->SetString(
-      pNotify->GetAppProvider()->GetAppName().ToUTF8().AsStringView());
+  ByteString bsName = pNotify->GetAppProvider()->GetAppName().ToUTF8();
+  *pValue = fxv8::NewStringHelper(pIsolate, bsName.AsStringView());
 }
 
 CJS_Result CJX_HostPseudoModel::gotoURL(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<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);
@@ -255,17 +270,16 @@
   if (!pNotify)
     return CJS_Result::Success();
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  WideString URL = runtime->ToWideString(params[0]);
-  hDoc->GetDocEnvironment()->GotoURL(hDoc, URL);
+  pNotify->GetFFDoc()->GotoURL(runtime->ToWideString(params[0]));
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_HostPseudoModel::openList(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<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,26 +290,24 @@
 
   CXFA_Node* pNode = nullptr;
   if (params[0]->IsObject()) {
-    pNode =
-        ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+    pNode = ToNode(runtime->ToXFAObject(params[0]));
   } else if (params[0]->IsString()) {
-    CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-    CXFA_Object* pObject = pScriptContext->GetThisObject();
+    CXFA_Object* pObject = runtime->GetThisObject();
     if (!pObject)
       return CJS_Result::Success();
 
-    uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Parent |
-                      XFA_RESOLVENODE_Siblings;
-    XFA_RESOLVENODE_RS resolveNodeRS;
-    bool bRet = pScriptContext->ResolveObjects(
-        pObject, runtime->ToWideString(params[0]).AsStringView(),
-        &resolveNodeRS, dwFlag, nullptr);
-    if (!bRet || !resolveNodeRS.objects.front()->IsNode())
+    constexpr Mask<XFA_ResolveFlag> kFlags = {XFA_ResolveFlag::kChildren,
+                                              XFA_ResolveFlag::kParent,
+                                              XFA_ResolveFlag::kSiblings};
+    absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+        runtime->ResolveObjects(
+            pObject, runtime->ToWideString(params[0]).AsStringView(), kFlags);
+    if (!maybeResult.has_value() ||
+        !maybeResult.value().objects.front()->IsNode()) {
       return CJS_Result::Success();
-
-    pNode = resolveNodeRS.objects.front()->AsNode();
+    }
+    pNode = maybeResult.value().objects.front()->AsNode();
   }
-
   if (pNode)
     pNotify->OpenDropDownList(pNode);
 
@@ -303,7 +315,7 @@
 }
 
 CJS_Result CJX_HostPseudoModel::response(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 4)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -335,13 +347,13 @@
 }
 
 CJS_Result CJX_HostPseudoModel::documentInBatch(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success(runtime->NewNumber(0));
 }
 
 CJS_Result CJX_HostPseudoModel::resetData(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() > 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -359,26 +371,26 @@
     return CJS_Result::Success();
   }
 
-  int32_t iStart = 0;
   WideString wsName;
   CXFA_Node* pNode = nullptr;
-  int32_t iExpLength = expression.GetLength();
-  while (iStart < iExpLength) {
-    iStart = FilterName(expression.AsStringView(), iStart, wsName);
-    CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-    CXFA_Object* pObject = pScriptContext->GetThisObject();
+  size_t nStart = 0;
+  const size_t nExpLength = expression.GetLength();
+  while (nStart < nExpLength) {
+    nStart = FilterName(expression.AsStringView(), nStart, wsName);
+    CXFA_Object* pObject = runtime->GetThisObject();
     if (!pObject)
       return CJS_Result::Success();
 
-    uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Parent |
-                      XFA_RESOLVENODE_Siblings;
-    XFA_RESOLVENODE_RS resolveNodeRS;
-    bool bRet = pScriptContext->ResolveObjects(pObject, wsName.AsStringView(),
-                                               &resolveNodeRS, dwFlag, nullptr);
-    if (!bRet || !resolveNodeRS.objects.front()->IsNode())
+    constexpr Mask<XFA_ResolveFlag> kFlags = {XFA_ResolveFlag::kChildren,
+                                              XFA_ResolveFlag::kParent,
+                                              XFA_ResolveFlag::kSiblings};
+    absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+        runtime->ResolveObjects(pObject, wsName.AsStringView(), kFlags);
+    if (!maybeResult.has_value() ||
+        !maybeResult.value().objects.front()->IsNode())
       continue;
 
-    pNode = resolveNodeRS.objects.front()->AsNode();
+    pNode = maybeResult.value().objects.front()->AsNode();
     pNotify->ResetData(pNode->IsWidgetReady() ? pNode : nullptr);
   }
   if (!pNode)
@@ -388,10 +400,11 @@
 }
 
 CJS_Result CJX_HostPseudoModel::beep(
-    CFX_V8* runtime,
+    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);
@@ -409,10 +422,11 @@
 }
 
 CJS_Result CJX_HostPseudoModel::setFocus(
-    CFX_V8* runtime,
+    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);
@@ -424,24 +438,23 @@
   CXFA_Node* pNode = nullptr;
   if (params.size() >= 1) {
     if (params[0]->IsObject()) {
-      pNode =
-          ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+      pNode = ToNode(runtime->ToXFAObject(params[0]));
     } else if (params[0]->IsString()) {
-      CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-      CXFA_Object* pObject = pScriptContext->GetThisObject();
+      CXFA_Object* pObject = runtime->GetThisObject();
       if (!pObject)
         return CJS_Result::Success();
 
-      uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Parent |
-                        XFA_RESOLVENODE_Siblings;
-      XFA_RESOLVENODE_RS resolveNodeRS;
-      bool bRet = pScriptContext->ResolveObjects(
-          pObject, runtime->ToWideString(params[0]).AsStringView(),
-          &resolveNodeRS, dwFlag, nullptr);
-      if (!bRet || !resolveNodeRS.objects.front()->IsNode())
+      constexpr Mask<XFA_ResolveFlag> kFlags = {XFA_ResolveFlag::kChildren,
+                                                XFA_ResolveFlag::kParent,
+                                                XFA_ResolveFlag::kSiblings};
+      absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+          runtime->ResolveObjects(
+              pObject, runtime->ToWideString(params[0]).AsStringView(), kFlags);
+      if (!maybeResult.has_value() ||
+          !maybeResult.value().objects.front()->IsNode()) {
         return CJS_Result::Success();
-
-      pNode = resolveNodeRS.objects.front()->AsNode();
+      }
+      pNode = maybeResult.value().objects.front()->AsNode();
     }
   }
   pNotify->SetFocusWidgetNode(pNode);
@@ -449,7 +462,7 @@
 }
 
 CJS_Result CJX_HostPseudoModel::getFocus(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
@@ -459,18 +472,15 @@
   if (!pNode)
     return CJS_Result::Success();
 
-  CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode);
-
-  return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+  return CJS_Result::Success(runtime->GetOrCreateJSBindingFromMap(pNode));
 }
 
 CJS_Result CJX_HostPseudoModel::messageBox(
-    CFX_V8* runtime,
+    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);
@@ -507,16 +517,17 @@
 }
 
 CJS_Result CJX_HostPseudoModel::documentCountInBatch(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success(runtime->NewNumber(0));
 }
 
 CJS_Result CJX_HostPseudoModel::print(
-    CFX_V8* runtime,
+    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);
@@ -525,30 +536,28 @@
   if (!pNotify)
     return CJS_Result::Success();
 
-  uint32_t dwOptions = 0;
+  Mask<XFA_PrintOpt> dwOptions;
   if (runtime->ToBoolean(params[0]))
-    dwOptions |= XFA_PRINTOPT_ShowDialog;
+    dwOptions |= XFA_PrintOpt::kShowDialog;
   if (runtime->ToBoolean(params[3]))
-    dwOptions |= XFA_PRINTOPT_CanCancel;
+    dwOptions |= XFA_PrintOpt::kCanCancel;
   if (runtime->ToBoolean(params[4]))
-    dwOptions |= XFA_PRINTOPT_ShrinkPage;
+    dwOptions |= XFA_PrintOpt::kShrinkPage;
   if (runtime->ToBoolean(params[5]))
-    dwOptions |= XFA_PRINTOPT_AsImage;
+    dwOptions |= XFA_PrintOpt::kAsImage;
   if (runtime->ToBoolean(params[6]))
-    dwOptions |= XFA_PRINTOPT_ReverseOrder;
+    dwOptions |= XFA_PrintOpt::kReverseOrder;
   if (runtime->ToBoolean(params[7]))
-    dwOptions |= XFA_PRINTOPT_PrintAnnot;
+    dwOptions |= XFA_PrintOpt::kPrintAnnot;
 
   int32_t nStartPage = runtime->ToInt32(params[1]);
   int32_t nEndPage = runtime->ToInt32(params[2]);
-
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  hDoc->GetDocEnvironment()->Print(hDoc, nStartPage, nEndPage, dwOptions);
+  pNotify->GetFFDoc()->Print(nStartPage, nEndPage, dwOptions);
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_HostPseudoModel::importData(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -557,7 +566,7 @@
 }
 
 CJS_Result CJX_HostPseudoModel::exportData(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 2)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -574,39 +583,36 @@
   if (params.size() >= 2)
     XDP = runtime->ToBoolean(params[1]);
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  hDoc->GetDocEnvironment()->ExportData(hDoc, filePath, XDP);
+  pNotify->GetFFDoc()->ExportData(filePath, XDP);
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_HostPseudoModel::pageUp(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return CJS_Result::Success();
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  int32_t nCurPage = hDoc->GetDocEnvironment()->GetCurrentPage(hDoc);
-  int32_t nNewPage = 0;
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
+  int32_t nCurPage = hDoc->GetCurrentPage();
   if (nCurPage <= 1)
     return CJS_Result::Success();
 
-  nNewPage = nCurPage - 1;
-  hDoc->GetDocEnvironment()->SetCurrentPage(hDoc, nNewPage);
+  hDoc->SetCurrentPage(nCurPage - 1);
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_HostPseudoModel::pageDown(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return CJS_Result::Success();
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  int32_t nCurPage = hDoc->GetDocEnvironment()->GetCurrentPage(hDoc);
-  int32_t nPageCount = hDoc->GetDocEnvironment()->CountPages(hDoc);
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
+  int32_t nCurPage = hDoc->GetCurrentPage();
+  int32_t nPageCount = hDoc->CountPages();
   if (!nPageCount || nCurPage == nPageCount)
     return CJS_Result::Success();
 
@@ -616,6 +622,6 @@
   else
     nNewPage = nCurPage + 1;
 
-  hDoc->GetDocEnvironment()->SetCurrentPage(hDoc, nNewPage);
+  hDoc->SetCurrentPage(nNewPage);
   return CJS_Result::Success();
 }
diff --git a/fxjs/xfa/cjx_hostpseudomodel.h b/fxjs/xfa/cjx_hostpseudomodel.h
index cdc293d..7c1b3b5 100644
--- a/fxjs/xfa/cjx_hostpseudomodel.h
+++ b/fxjs/xfa/cjx_hostpseudomodel.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,12 +11,11 @@
 #include "fxjs/xfa/jse_define.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
-class CFXJSE_Value;
 class CScript_HostPseudoModel;
 
 class CJX_HostPseudoModel final : public CJX_Object {
  public:
-  explicit CJX_HostPseudoModel(CScript_HostPseudoModel* model);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_HostPseudoModel() override;
 
   // CJX_Object:
@@ -54,6 +53,8 @@
   JSE_PROP(name);
 
  private:
+  explicit CJX_HostPseudoModel(CScript_HostPseudoModel* model);
+
   using Type__ = CJX_HostPseudoModel;
   using ParentType__ = CJX_Object;
 
diff --git a/fxjs/xfa/cjx_hostpseudomodel_embeddertest.cpp b/fxjs/xfa/cjx_hostpseudomodel_embeddertest.cpp
index 1d42898..86e0902 100644
--- a/fxjs/xfa/cjx_hostpseudomodel_embeddertest.cpp
+++ b/fxjs/xfa/cjx_hostpseudomodel_embeddertest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxjs/xfa/cjx_instancemanager.cpp b/fxjs/xfa/cjx_instancemanager.cpp
index 160c4a5..f5b4d61 100644
--- a/fxjs/xfa/cjx_instancemanager.cpp
+++ b/fxjs/xfa/cjx_instancemanager.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,14 +9,18 @@
 #include <algorithm>
 #include <vector>
 
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "third_party/base/notreached.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_instancemanager.h"
 #include "xfa/fxfa/parser/cxfa_occur.h"
+#include "xfa/fxfa/parser/cxfa_subform.h"
 
 const CJX_MethodSpec CJX_InstanceManager::MethodSpecs[] = {
     {"addInstance", addInstance_static},
@@ -30,23 +34,24 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_InstanceManager::~CJX_InstanceManager() {}
+CJX_InstanceManager::~CJX_InstanceManager() = default;
 
 bool CJX_InstanceManager::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-int32_t CJX_InstanceManager::SetInstances(int32_t iDesired) {
+int32_t CJX_InstanceManager::SetInstances(v8::Isolate* pIsolate,
+                                          int32_t iDesired) {
   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
   int32_t iMin = occur ? occur->GetMin() : CXFA_Occur::kDefaultMin;
   if (iDesired < iMin) {
-    ThrowTooManyOccurancesException(L"min");
+    ThrowTooManyOccurrencesException(pIsolate, L"min");
     return 1;
   }
 
   int32_t iMax = occur ? occur->GetMax() : CXFA_Occur::kDefaultMax;
   if (iMax >= 0 && iDesired > iMax) {
-    ThrowTooManyOccurancesException(L"max");
+    ThrowTooManyOccurrencesException(pIsolate, L"max");
     return 2;
   }
 
@@ -61,13 +66,13 @@
             ? wsInstManagerName
             : wsInstManagerName.Last(wsInstManagerName.GetLength() - 1));
     uint32_t dInstanceNameHash =
-        FX_HashCode_GetW(wsInstanceName.AsStringView(), false);
+        FX_HashCode_GetW(wsInstanceName.AsStringView());
     CXFA_Node* pPrevSibling = iDesired == 0
                                   ? GetXFANode()
                                   : GetXFANode()->GetItemIfExists(iDesired - 1);
     if (!pPrevSibling) {
       // TODO(dsinclair): Better error?
-      ThrowIndexOutOfBoundsException();
+      ThrowIndexOutOfBoundsException(pIsolate);
       return 0;
     }
 
@@ -102,15 +107,16 @@
       pNotify->RunNodeInitialize(pNewInstance);
     }
   }
-  GetDocument()->GetLayoutProcessor()->AddChangedContainer(
-      ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
+  GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
   return 0;
 }
 
-int32_t CJX_InstanceManager::MoveInstance(int32_t iTo, int32_t iFrom) {
+int32_t CJX_InstanceManager::MoveInstance(v8::Isolate* pIsolate,
+                                          int32_t iTo,
+                                          int32_t iFrom) {
   int32_t iCount = GetXFANode()->GetCount();
   if (iFrom > iCount || iTo > iCount - 1) {
-    ThrowIndexOutOfBoundsException();
+    ThrowIndexOutOfBoundsException(pIsolate);
     return 1;
   }
   if (iFrom < 0 || iTo < 0 || iFrom == iTo)
@@ -118,21 +124,20 @@
 
   CXFA_Node* pMoveInstance = GetXFANode()->GetItemIfExists(iFrom);
   if (!pMoveInstance) {
-    ThrowIndexOutOfBoundsException();
+    ThrowIndexOutOfBoundsException(pIsolate);
     return 1;
   }
 
   GetXFANode()->RemoveItem(pMoveInstance, false);
   GetXFANode()->InsertItem(pMoveInstance, iTo, iCount - 1, true);
-  GetDocument()->GetLayoutProcessor()->AddChangedContainer(
-      ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
+  GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
   return 0;
 }
 
 CJS_Result CJX_InstanceManager::moveInstance(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  CXFA_Document* doc = static_cast<CFXJSE_Engine*>(runtime)->GetDocument();
+  CXFA_Document* doc = runtime->GetDocument();
   if (doc->GetFormType() != FormType::kXFAFull)
     return CJS_Result::Failure(JSMessage::kNotSupportedError);
 
@@ -141,29 +146,28 @@
 
   int32_t iFrom = runtime->ToInt32(params[0]);
   int32_t iTo = runtime->ToInt32(params[1]);
-  MoveInstance(iTo, iFrom);
+  MoveInstance(runtime->GetIsolate(), iTo, iFrom);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return CJS_Result::Success();
 
-  CXFA_Node* pToInstance = GetXFANode()->GetItemIfExists(iTo);
-  if (pToInstance && pToInstance->GetElementType() == XFA_Element::Subform)
+  CXFA_Node* pXFA = GetXFANode();
+  auto* pToInstance = CXFA_Subform::FromNode(pXFA->GetItemIfExists(iTo));
+  if (pToInstance)
     pNotify->RunSubformIndexChange(pToInstance);
 
-  CXFA_Node* pFromInstance = GetXFANode()->GetItemIfExists(iFrom);
-  if (pFromInstance &&
-      pFromInstance->GetElementType() == XFA_Element::Subform) {
+  auto* pFromInstance = CXFA_Subform::FromNode(pXFA->GetItemIfExists(iFrom));
+  if (pFromInstance)
     pNotify->RunSubformIndexChange(pFromInstance);
-  }
 
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_InstanceManager::removeInstance(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  CXFA_Document* doc = static_cast<CFXJSE_Engine*>(runtime)->GetDocument();
+  CXFA_Document* doc = runtime->GetDocument();
   if (doc->GetFormType() != FormType::kXFAFull)
     return CJS_Result::Failure(JSMessage::kNotSupportedError);
 
@@ -178,7 +182,7 @@
   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
   int32_t iMin = occur ? occur->GetMin() : CXFA_Occur::kDefaultMin;
   if (iCount - 1 < iMin)
-    return CJS_Result::Failure(JSMessage::kTooManyOccurances);
+    return CJS_Result::Failure(JSMessage::kTooManyOccurrences);
 
   CXFA_Node* pRemoveInstance = GetXFANode()->GetItemIfExists(iIndex);
   if (!pRemoveInstance)
@@ -188,37 +192,35 @@
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (pNotify) {
+    CXFA_Node* pXFA = GetXFANode();
     for (int32_t i = iIndex; i < iCount - 1; i++) {
-      CXFA_Node* pSubformInstance = GetXFANode()->GetItemIfExists(i);
-      if (pSubformInstance &&
-          pSubformInstance->GetElementType() == XFA_Element::Subform) {
+      auto* pSubformInstance = CXFA_Subform::FromNode(pXFA->GetItemIfExists(i));
+      if (pSubformInstance)
         pNotify->RunSubformIndexChange(pSubformInstance);
-      }
     }
   }
-  GetDocument()->GetLayoutProcessor()->AddChangedContainer(
-      ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
+  GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_InstanceManager::setInstances(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  CXFA_Document* doc = static_cast<CFXJSE_Engine*>(runtime)->GetDocument();
+  CXFA_Document* doc = runtime->GetDocument();
   if (doc->GetFormType() != FormType::kXFAFull)
     return CJS_Result::Failure(JSMessage::kNotSupportedError);
 
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  SetInstances(runtime->ToInt32(params[0]));
+  SetInstances(runtime->GetIsolate(), runtime->ToInt32(params[0]));
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_InstanceManager::addInstance(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  CXFA_Document* doc = static_cast<CFXJSE_Engine*>(runtime)->GetDocument();
+  CXFA_Document* doc = runtime->GetDocument();
   if (doc->GetFormType() != FormType::kXFAFull)
     return CJS_Result::Failure(JSMessage::kNotSupportedError);
 
@@ -233,7 +235,7 @@
   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
   int32_t iMax = occur ? occur->GetMax() : CXFA_Occur::kDefaultMax;
   if (iMax >= 0 && iCount >= iMax)
-    return CJS_Result::Failure(JSMessage::kTooManyOccurances);
+    return CJS_Result::Failure(JSMessage::kTooManyOccurrences);
 
   CXFA_Node* pNewInstance = GetXFANode()->CreateInstanceIfPossible(fFlags);
   if (!pNewInstance)
@@ -244,22 +246,17 @@
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (pNotify) {
     pNotify->RunNodeInitialize(pNewInstance);
-    GetDocument()->GetLayoutProcessor()->AddChangedContainer(
-        ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
+    GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
   }
 
-  CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-          pNewInstance);
-
   return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+      runtime->GetOrCreateJSBindingFromMap(pNewInstance));
 }
 
 CJS_Result CJX_InstanceManager::insertInstance(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  CXFA_Document* doc = static_cast<CFXJSE_Engine*>(runtime)->GetDocument();
+  CXFA_Document* doc = runtime->GetDocument();
   if (doc->GetFormType() != FormType::kXFAFull)
     return CJS_Result::Failure(JSMessage::kNotSupportedError);
 
@@ -289,46 +286,46 @@
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (pNotify) {
     pNotify->RunNodeInitialize(pNewInstance);
-    GetDocument()->GetLayoutProcessor()->AddChangedContainer(
-        ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
+    GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
   }
 
-  CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-          pNewInstance);
-
   return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+      runtime->GetOrCreateJSBindingFromMap(pNewInstance));
 }
 
-void CJX_InstanceManager::max(CFXJSE_Value* pValue,
+void CJX_InstanceManager::max(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value>* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
-  pValue->SetInteger(occur ? occur->GetMax() : CXFA_Occur::kDefaultMax);
+  *pValue = fxv8::NewNumberHelper(
+      pIsolate, occur ? occur->GetMax() : CXFA_Occur::kDefaultMax);
 }
 
-void CJX_InstanceManager::min(CFXJSE_Value* pValue,
+void CJX_InstanceManager::min(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value>* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
-  pValue->SetInteger(occur ? occur->GetMin() : CXFA_Occur::kDefaultMin);
+  *pValue = fxv8::NewNumberHelper(
+      pIsolate, occur ? occur->GetMin() : CXFA_Occur::kDefaultMin);
 }
 
-void CJX_InstanceManager::count(CFXJSE_Value* pValue,
+void CJX_InstanceManager::count(v8::Isolate* pIsolate,
+                                v8::Local<v8::Value>* pValue,
                                 bool bSetting,
                                 XFA_Attribute eAttribute) {
   if (bSetting) {
-    SetInstances(pValue->ToInteger());
+    SetInstances(pIsolate, fxv8::ReentrantToInt32Helper(pIsolate, *pValue));
     return;
   }
-  pValue->SetInteger(GetXFANode()->GetCount());
+  *pValue = fxv8::NewNumberHelper(pIsolate, GetXFANode()->GetCount());
 }
diff --git a/fxjs/xfa/cjx_instancemanager.h b/fxjs/xfa/cjx_instancemanager.h
index 3fae9ab..96210e8 100644
--- a/fxjs/xfa/cjx_instancemanager.h
+++ b/fxjs/xfa/cjx_instancemanager.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,12 +9,13 @@
 
 #include "fxjs/xfa/cjx_node.h"
 #include "fxjs/xfa/jse_define.h"
+#include "v8/include/v8-forward.h"
 
 class CXFA_InstanceManager;
 
 class CJX_InstanceManager final : public CJX_Node {
  public:
-  explicit CJX_InstanceManager(CXFA_InstanceManager* mgr);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_InstanceManager() override;
 
   // CJX_Object:
@@ -30,16 +31,18 @@
   JSE_PROP(max);
   JSE_PROP(min);
 
-  int32_t MoveInstance(int32_t iTo, int32_t iFrom);
+  int32_t MoveInstance(v8::Isolate* pIsolate, int32_t iTo, int32_t iFrom);
 
  private:
+  explicit CJX_InstanceManager(CXFA_InstanceManager* mgr);
+
   using Type__ = CJX_InstanceManager;
   using ParentType__ = CJX_Node;
 
   static const TypeTag static_type__ = TypeTag::InstanceManager;
   static const CJX_MethodSpec MethodSpecs[];
 
-  int32_t SetInstances(int32_t iDesired);
+  int32_t SetInstances(v8::Isolate* pIsolate, int32_t iDesired);
 };
 
 #endif  // FXJS_XFA_CJX_INSTANCEMANAGER_H_
diff --git a/fxjs/xfa/cjx_layoutpseudomodel.cpp b/fxjs/xfa/cjx_layoutpseudomodel.cpp
index 35852a8..484d883 100644
--- a/fxjs/xfa/cjx_layoutpseudomodel.cpp
+++ b/fxjs/xfa/cjx_layoutpseudomodel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,12 +10,13 @@
 #include <utility>
 
 #include "core/fxcrt/fx_coordinates.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_class.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/containers/contains.h"
+#include "v8/include/cppgc/allocation.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
 #include "xfa/fxfa/layout/cxfa_layoutitem.h"
@@ -56,36 +57,39 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_LayoutPseudoModel::~CJX_LayoutPseudoModel() {}
+CJX_LayoutPseudoModel::~CJX_LayoutPseudoModel() = default;
 
 bool CJX_LayoutPseudoModel::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_LayoutPseudoModel::ready(CFXJSE_Value* pValue,
+void CJX_LayoutPseudoModel::ready(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return;
   if (bSetting) {
-    ThrowException(WideString::FromASCII("Unable to set ready value."));
+    ThrowException(pIsolate,
+                   WideString::FromASCII("Unable to set ready value."));
     return;
   }
 
-  int32_t iStatus = pNotify->GetLayoutStatus();
-  pValue->SetBoolean(iStatus >= 2);
+  CXFA_FFDocView::LayoutStatus iStatus = pNotify->GetLayoutStatus();
+  const bool bReady = iStatus != CXFA_FFDocView::LayoutStatus::kNone &&
+                      iStatus != CXFA_FFDocView::LayoutStatus::kStart;
+  *pValue = fxv8::NewBooleanHelper(pIsolate, bReady);
 }
 
-CJS_Result CJX_LayoutPseudoModel::HWXY(
-    CFX_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::DoHWXYInternal(
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params,
-    XFA_LAYOUTMODEL_HWXY layoutModel) {
+    HWXY layoutModel) {
   if (params.empty() || params.size() > 3)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_Node* pNode =
-      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+  CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
   if (!pNode)
     return CJS_Result::Success();
 
@@ -111,18 +115,18 @@
     return CJS_Result::Success(runtime->NewNumber(0.0));
 
   CXFA_Measurement measure;
-  CFX_RectF rtRect = pLayoutItem->GetRect(true);
+  CFX_RectF rtRect = pLayoutItem->GetRelativeRect();
   switch (layoutModel) {
-    case XFA_LAYOUTMODEL_H:
+    case HWXY::kH:
       measure.Set(rtRect.height, XFA_Unit::Pt);
       break;
-    case XFA_LAYOUTMODEL_W:
+    case HWXY::kW:
       measure.Set(rtRect.width, XFA_Unit::Pt);
       break;
-    case XFA_LAYOUTMODEL_X:
+    case HWXY::kX:
       measure.Set(rtRect.left, XFA_Unit::Pt);
       break;
-    case XFA_LAYOUTMODEL_Y:
+    case HWXY::kY:
       measure.Set(rtRect.top, XFA_Unit::Pt);
       break;
   }
@@ -137,64 +141,63 @@
 }
 
 CJS_Result CJX_LayoutPseudoModel::h(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return HWXY(runtime, params, XFA_LAYOUTMODEL_H);
+  return DoHWXYInternal(runtime, params, HWXY::kH);
 }
 
 CJS_Result CJX_LayoutPseudoModel::w(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return HWXY(runtime, params, XFA_LAYOUTMODEL_W);
+  return DoHWXYInternal(runtime, params, HWXY::kW);
 }
 
 CJS_Result CJX_LayoutPseudoModel::x(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return HWXY(runtime, params, XFA_LAYOUTMODEL_X);
+  return DoHWXYInternal(runtime, params, HWXY::kX);
 }
 
 CJS_Result CJX_LayoutPseudoModel::y(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return HWXY(runtime, params, XFA_LAYOUTMODEL_Y);
+  return DoHWXYInternal(runtime, params, HWXY::kY);
 }
 
-CJS_Result CJX_LayoutPseudoModel::NumberedPageCount(CFX_V8* runtime,
-                                                    bool bNumbered) {
+CJS_Result CJX_LayoutPseudoModel::AllPageCount(CFXJSE_Engine* runtime) {
+  auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
+  return CJS_Result::Success(runtime->NewNumber(pDocLayout->CountPages()));
+}
+
+CJS_Result CJX_LayoutPseudoModel::NumberedPageCount(CFXJSE_Engine* runtime) {
   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
   int32_t iPageCount = 0;
   int32_t iPageNum = pDocLayout->CountPages();
-  if (bNumbered) {
-    for (int32_t i = 0; i < iPageNum; i++) {
-      CXFA_ViewLayoutItem* pLayoutPage = pDocLayout->GetPage(i);
-      if (!pLayoutPage)
-        continue;
+  for (int32_t i = 0; i < iPageNum; i++) {
+    CXFA_ViewLayoutItem* pLayoutPage = pDocLayout->GetPage(i);
+    if (!pLayoutPage)
+      continue;
 
-      CXFA_Node* pMasterPage = pLayoutPage->GetMasterPage();
-      if (pMasterPage->JSObject()->GetInteger(XFA_Attribute::Numbered))
-        iPageCount++;
-    }
-  } else {
-    iPageCount = iPageNum;
+    CXFA_Node* pMasterPage = pLayoutPage->GetMasterPage();
+    if (pMasterPage->JSObject()->GetInteger(XFA_Attribute::Numbered))
+      iPageCount++;
   }
   return CJS_Result::Success(runtime->NewNumber(iPageCount));
 }
 
 CJS_Result CJX_LayoutPseudoModel::pageCount(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return NumberedPageCount(runtime, true);
+  return NumberedPageCount(runtime);
 }
 
 CJS_Result CJX_LayoutPseudoModel::pageSpan(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_Node* pNode =
-      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+  CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
   if (!pNode)
     return CJS_Result::Success();
 
@@ -211,7 +214,7 @@
 }
 
 CJS_Result CJX_LayoutPseudoModel::page(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return PageInternals(runtime, params, false);
 }
@@ -261,7 +264,7 @@
                 eType != XFA_Element::Subform && eType != XFA_Element::Area) {
               continue;
             }
-            if (pdfium::ContainsValue(formItems, pItemChild->GetFormNode()))
+            if (pdfium::Contains(formItems, pItemChild->GetFormNode()))
               continue;
 
             formItems.insert(pItemChild->GetFormNode());
@@ -282,7 +285,7 @@
                 eType != XFA_Element::Subform && eType != XFA_Element::Area) {
               continue;
             }
-            if (pdfium::ContainsValue(formItems, pItemChild->GetFormNode()))
+            if (pdfium::Contains(formItems, pItemChild->GetFormNode()))
               continue;
 
             formItems.insert(pItemChild->GetFormNode());
@@ -317,7 +320,7 @@
               continue;
             if (pItemChild->GetFormNode()->GetElementType() != eType)
               continue;
-            if (pdfium::ContainsValue(formItems, pItemChild->GetFormNode()))
+            if (pdfium::Contains(formItems, pItemChild->GetFormNode()))
               continue;
 
             formItems.insert(pItemChild->GetFormNode());
@@ -334,7 +337,7 @@
               continue;
             if (pItemChild->GetFormNode()->GetElementType() != eType)
               continue;
-            if (pdfium::ContainsValue(formItems, pItemChild->GetFormNode()))
+            if (pdfium::Contains(formItems, pItemChild->GetFormNode()))
               continue;
 
             formItems.insert(pItemChild->GetFormNode());
@@ -348,7 +351,7 @@
 }
 
 CJS_Result CJX_LayoutPseudoModel::pageContent(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 3)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -369,59 +372,57 @@
   if (!pNotify)
     return CJS_Result::Success();
 
-  auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
-  auto pArrayNodeList = pdfium::MakeUnique<CXFA_ArrayNodeList>(GetDocument());
+  CXFA_Document* pDoc = GetDocument();
+  auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(pDoc);
+  auto* pArrayNodeList = cppgc::MakeGarbageCollected<CXFA_ArrayNodeList>(
+      pDoc->GetHeap()->GetAllocationHandle(), pDoc);
+  pDoc->GetNodeOwner()->PersistList(pArrayNodeList);
   pArrayNodeList->SetArrayNodeList(
       GetObjArray(pDocLayout, iIndex, wsType, bOnPageArea));
-
-  // TODO(dsinclair): Who owns the array once we release it? Won't this leak?
-  return CJS_Result::Success(static_cast<CFXJSE_Engine*>(runtime)->NewXFAObject(
-      pArrayNodeList.release(),
-      GetDocument()->GetScriptContext()->GetJseNormalClass()->GetTemplate()));
+  return CJS_Result::Success(runtime->NewNormalXFAObject(pArrayNodeList));
 }
 
 CJS_Result CJX_LayoutPseudoModel::absPageCount(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return NumberedPageCount(runtime, false);
+  return AllPageCount(runtime);
 }
 
 CJS_Result CJX_LayoutPseudoModel::absPageCountInBatch(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success(runtime->NewNumber(0));
 }
 
 CJS_Result CJX_LayoutPseudoModel::sheetCountInBatch(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success(runtime->NewNumber(0));
 }
 
 CJS_Result CJX_LayoutPseudoModel::relayout(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CXFA_Node* pRootNode = GetDocument()->GetRoot();
   auto* pLayoutProcessor = GetDocument()->GetLayoutProcessor();
   CXFA_Form* pFormRoot =
       pRootNode->GetFirstChildByClass<CXFA_Form>(XFA_Element::Form);
   if (pFormRoot) {
-    CXFA_Node* pContentRootNode = pFormRoot->GetFirstChild();
-    if (pContentRootNode)
-      pLayoutProcessor->AddChangedContainer(pContentRootNode);
+    if (pFormRoot->GetFirstChild())
+      pLayoutProcessor->SetHasChangedContainer();
   }
-  pLayoutProcessor->SetForceRelayout(true);
+  pLayoutProcessor->SetForceRelayout();
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_LayoutPseudoModel::absPageSpan(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return pageSpan(runtime, params);
 }
 
 CJS_Result CJX_LayoutPseudoModel::absPageInBatch(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -430,7 +431,7 @@
 }
 
 CJS_Result CJX_LayoutPseudoModel::sheetInBatch(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -439,38 +440,37 @@
 }
 
 CJS_Result CJX_LayoutPseudoModel::sheet(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return PageInternals(runtime, params, true);
 }
 
 CJS_Result CJX_LayoutPseudoModel::relayoutPageArea(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_LayoutPseudoModel::sheetCount(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return NumberedPageCount(runtime, false);
+  return AllPageCount(runtime);
 }
 
 CJS_Result CJX_LayoutPseudoModel::absPage(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return PageInternals(runtime, params, true);
 }
 
 CJS_Result CJX_LayoutPseudoModel::PageInternals(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params,
     bool bAbsPage) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_Node* pNode =
-      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+  CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
   if (!pNode)
     return CJS_Result::Success(runtime->NewNumber(0));
 
diff --git a/fxjs/xfa/cjx_layoutpseudomodel.h b/fxjs/xfa/cjx_layoutpseudomodel.h
index d5f0cba..80cf310 100644
--- a/fxjs/xfa/cjx_layoutpseudomodel.h
+++ b/fxjs/xfa/cjx_layoutpseudomodel.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,21 +12,13 @@
 #include "fxjs/xfa/cjx_object.h"
 #include "fxjs/xfa/jse_define.h"
 
-enum XFA_LAYOUTMODEL_HWXY {
-  XFA_LAYOUTMODEL_H,
-  XFA_LAYOUTMODEL_W,
-  XFA_LAYOUTMODEL_X,
-  XFA_LAYOUTMODEL_Y
-};
-
-class CFXJSE_Value;
 class CScript_LayoutPseudoModel;
 class CXFA_LayoutProcessor;
 class CXFA_Node;
 
 class CJX_LayoutPseudoModel final : public CJX_Object {
  public:
-  explicit CJX_LayoutPseudoModel(CScript_LayoutPseudoModel* model);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_LayoutPseudoModel() override;
 
   // CJX_Object:
@@ -55,21 +47,26 @@
   JSE_PROP(ready);
 
  private:
+  enum class HWXY { kH, kW, kX, kY };
+
+  explicit CJX_LayoutPseudoModel(CScript_LayoutPseudoModel* model);
+
   using Type__ = CJX_LayoutPseudoModel;
   using ParentType__ = CJX_Object;
 
   static const TypeTag static_type__ = TypeTag::LayoutPseudoModel;
   static const CJX_MethodSpec MethodSpecs[];
 
-  CJS_Result NumberedPageCount(CFX_V8* runtime, bool bNumbered);
-  CJS_Result HWXY(CFX_V8* runtime,
-                  const std::vector<v8::Local<v8::Value>>& params,
-                  XFA_LAYOUTMODEL_HWXY layoutModel);
+  CJS_Result AllPageCount(CFXJSE_Engine* runtime);
+  CJS_Result NumberedPageCount(CFXJSE_Engine* runtime);
+  CJS_Result DoHWXYInternal(CFXJSE_Engine* runtime,
+                            const std::vector<v8::Local<v8::Value>>& params,
+                            HWXY layoutModel);
   std::vector<CXFA_Node*> GetObjArray(CXFA_LayoutProcessor* pDocLayout,
                                       int32_t iPageNo,
                                       const WideString& wsType,
                                       bool bOnPageArea);
-  CJS_Result PageInternals(CFX_V8* runtime,
+  CJS_Result PageInternals(CFXJSE_Engine* runtime,
                            const std::vector<v8::Local<v8::Value>>& params,
                            bool bAbsPage);
 };
diff --git a/fxjs/xfa/cjx_list.cpp b/fxjs/xfa/cjx_list.cpp
index 7c0cf23..8c4735d 100644
--- a/fxjs/xfa/cjx_list.cpp
+++ b/fxjs/xfa/cjx_list.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,11 +8,13 @@
 
 #include <vector>
 
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_class.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
 #include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_list.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
@@ -26,7 +28,7 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_List::~CJX_List() {}
+CJX_List::~CJX_List() = default;
 
 bool CJX_List::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
@@ -36,45 +38,43 @@
   return ToList(GetXFAObject());
 }
 
-CJS_Result CJX_List::append(CFX_V8* runtime,
+CJS_Result CJX_List::append(CFXJSE_Engine* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  auto* pNode =
-      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+  auto* pNode = ToNode(runtime->ToXFAObject(params[0]));
   if (!pNode)
     return CJS_Result::Failure(JSMessage::kValueError);
 
-  GetXFAList()->Append(pNode);
+  if (!GetXFAList()->Append(pNode))
+    return CJS_Result::Failure(JSMessage::kWouldBeCyclic);
+
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_List::insert(CFX_V8* runtime,
+CJS_Result CJX_List::insert(CFXJSE_Engine* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  auto* pNewNode =
-      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+  auto* pNewNode = ToNode(runtime->ToXFAObject(params[0]));
   if (!pNewNode)
     return CJS_Result::Failure(JSMessage::kValueError);
 
-  auto* pBeforeNode =
-      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[1]));
+  auto* pBeforeNode = ToNode(runtime->ToXFAObject(params[1]));
   if (!GetXFAList()->Insert(pNewNode, pBeforeNode))
     return CJS_Result::Failure(JSMessage::kValueError);
 
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_List::remove(CFX_V8* runtime,
+CJS_Result CJX_List::remove(CFXJSE_Engine* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  auto* pNode =
-      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+  auto* pNode = ToNode(runtime->ToXFAObject(params[0]));
   if (!pNode)
     return CJS_Result::Failure(JSMessage::kValueError);
 
@@ -82,7 +82,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_List::item(CFX_V8* runtime,
+CJS_Result CJX_List::item(CFXJSE_Engine* runtime,
                           const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -92,18 +92,18 @@
   if (index < 0 || cast_index >= GetXFAList()->GetLength())
     return CJS_Result::Failure(JSMessage::kInvalidInputError);
 
-  return CJS_Result::Success(static_cast<CFXJSE_Engine*>(runtime)->NewXFAObject(
-      GetXFAList()->Item(cast_index),
-      GetDocument()->GetScriptContext()->GetJseNormalClass()->GetTemplate()));
+  return CJS_Result::Success(
+      runtime->NewNormalXFAObject(GetXFAList()->Item(cast_index)));
 }
 
-void CJX_List::length(CFXJSE_Value* pValue,
+void CJX_List::length(v8::Isolate* pIsolate,
+                      v8::Local<v8::Value>* pValue,
                       bool bSetting,
                       XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->SetInteger(
-      pdfium::base::checked_cast<int32_t>(GetXFAList()->GetLength()));
+  *pValue = fxv8::NewNumberHelper(
+      pIsolate, pdfium::base::checked_cast<int32_t>(GetXFAList()->GetLength()));
 }
diff --git a/fxjs/xfa/cjx_list.h b/fxjs/xfa/cjx_list.h
index 842b859..4471867 100644
--- a/fxjs/xfa/cjx_list.h
+++ b/fxjs/xfa/cjx_list.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_List : public CJX_Object {
  public:
-  explicit CJX_List(CXFA_List* list);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_List() override;
 
   // CJX_Object:
@@ -27,6 +27,9 @@
 
   JSE_PROP(length);
 
+ protected:
+  explicit CJX_List(CXFA_List* list);
+
  private:
   using Type__ = CJX_List;
   using ParentType__ = CJX_Object;
diff --git a/fxjs/xfa/cjx_list_embeddertest.cpp b/fxjs/xfa/cjx_list_embeddertest.cpp
index 02450dc..17d2de4 100644
--- a/fxjs/xfa/cjx_list_embeddertest.cpp
+++ b/fxjs/xfa/cjx_list_embeddertest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxjs/xfa/cjx_logpseudomodel.cpp b/fxjs/xfa/cjx_logpseudomodel.cpp
index 8cb9ee7..a100697 100644
--- a/fxjs/xfa/cjx_logpseudomodel.cpp
+++ b/fxjs/xfa/cjx_logpseudomodel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,14 +23,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_LogPseudoModel::~CJX_LogPseudoModel() {}
+CJX_LogPseudoModel::~CJX_LogPseudoModel() = default;
 
 bool CJX_LogPseudoModel::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_LogPseudoModel::message(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   // Uncomment to allow using xfa.log.message(""); from JS.
   // fprintf(stderr, "LOG\n");
@@ -43,25 +43,25 @@
 }
 
 CJS_Result CJX_LogPseudoModel::traceEnabled(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_LogPseudoModel::traceActivate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_LogPseudoModel::traceDeactivate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_LogPseudoModel::trace(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
diff --git a/fxjs/xfa/cjx_logpseudomodel.h b/fxjs/xfa/cjx_logpseudomodel.h
index fda3bb9..030d1df 100644
--- a/fxjs/xfa/cjx_logpseudomodel.h
+++ b/fxjs/xfa/cjx_logpseudomodel.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
 // xfa_basic_data_element_script is removed.
 class CJX_LogPseudoModel final : public CJX_Object {
  public:
-  explicit CJX_LogPseudoModel(CScript_LogPseudoModel* model);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_LogPseudoModel() override;
 
   // CJX_Object:
@@ -29,6 +29,8 @@
   JSE_METHOD(trace);
 
  private:
+  explicit CJX_LogPseudoModel(CScript_LogPseudoModel* model);
+
   using Type__ = CJX_LogPseudoModel;
   using ParentType__ = CJX_Object;
 
diff --git a/fxjs/xfa/cjx_manifest.cpp b/fxjs/xfa/cjx_manifest.cpp
index f94232e..7d4e6e6 100644
--- a/fxjs/xfa/cjx_manifest.cpp
+++ b/fxjs/xfa/cjx_manifest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 #include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_manifest.h"
 
 const CJX_MethodSpec CJX_Manifest::MethodSpecs[] = {
@@ -20,14 +21,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Manifest::~CJX_Manifest() {}
+CJX_Manifest::~CJX_Manifest() = default;
 
 bool CJX_Manifest::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Manifest::evaluate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
diff --git a/fxjs/xfa/cjx_manifest.h b/fxjs/xfa/cjx_manifest.h
index 8380ac6..0f95648 100644
--- a/fxjs/xfa/cjx_manifest.h
+++ b/fxjs/xfa/cjx_manifest.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Manifest final : public CJX_Node {
  public:
-  explicit CJX_Manifest(CXFA_Manifest* manifest);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Manifest() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_METHOD(evaluate);
 
  private:
+  explicit CJX_Manifest(CXFA_Manifest* manifest);
+
   using Type__ = CJX_Manifest;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_model.cpp b/fxjs/xfa/cjx_model.cpp
index 4fa0a2e..e94954e 100644
--- a/fxjs/xfa/cjx_model.cpp
+++ b/fxjs/xfa/cjx_model.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/parser/cxfa_delta.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/xfa_basic_data.h"
@@ -24,20 +25,20 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Model::~CJX_Model() {}
+CJX_Model::~CJX_Model() = default;
 
 bool CJX_Model::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Model::clearErrorList(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_Model::createNode(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 3)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -60,21 +61,15 @@
     if (!pNewNode->HasAttribute(XFA_Attribute::Name))
       return CJS_Result::Failure(JSMessage::kParamError);
 
-    pNewNode->JSObject()->SetAttribute(XFA_Attribute::Name, name.AsStringView(),
-                                       true);
+    pNewNode->JSObject()->SetAttributeByEnum(XFA_Attribute::Name, name, true);
     if (pNewNode->GetPacketType() == XFA_PacketType::Datasets)
       pNewNode->CreateXMLMappingNode();
   }
-
-  CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNewNode);
-
-  return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+  return CJS_Result::Success(runtime->GetOrCreateJSBindingFromMap(pNewNode));
 }
 
 CJS_Result CJX_Model::isCompatibleNS(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -87,10 +82,12 @@
       runtime->NewBoolean(TryNamespace().value_or(WideString()) == nameSpace));
 }
 
-void CJX_Model::context(CFXJSE_Value* pValue,
+void CJX_Model::context(v8::Isolate* pIsolate,
+                        v8::Local<v8::Value>* pValue,
                         bool bSetting,
                         XFA_Attribute eAttribute) {}
 
-void CJX_Model::aliasNode(CFXJSE_Value* pValue,
+void CJX_Model::aliasNode(v8::Isolate* pIsolate,
+                          v8::Local<v8::Value>* pValue,
                           bool bSetting,
                           XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_model.h b/fxjs/xfa/cjx_model.h
index 9fd54f5..7b0e17e 100644
--- a/fxjs/xfa/cjx_model.h
+++ b/fxjs/xfa/cjx_model.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Model : public CJX_Node {
  public:
-  explicit CJX_Model(CXFA_Node* obj);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Model() override;
 
   // CJX_Object:
@@ -27,6 +27,9 @@
   JSE_PROP(aliasNode);
   JSE_PROP(context);
 
+ protected:
+  explicit CJX_Model(CXFA_Node* obj);
+
  private:
   using Type__ = CJX_Model;
   using ParentType__ = CJX_Node;
diff --git a/fxjs/xfa/cjx_node.cpp b/fxjs/xfa/cjx_node.cpp
index 3877f72..9db88ba 100644
--- a/fxjs/xfa/cjx_node.cpp
+++ b/fxjs/xfa/cjx_node.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,25 +11,27 @@
 #include <vector>
 
 #include "core/fxcrt/cfx_memorystream.h"
+#include "core/fxcrt/cfx_read_only_string_stream.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/xml/cfx_xmldocument.h"
-#include "core/fxcrt/xml/cfx_xmlnode.h"
+#include "core/fxcrt/xml/cfx_xmlelement.h"
+#include "core/fxcrt/xml/cfx_xmlparser.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
-#include "third_party/base/ptr_util.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_document_parser.h"
+#include "xfa/fxfa/parser/cxfa_document_builder.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/xfa_basic_data.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
 namespace {
 
-enum class EventAppliesToo : uint8_t {
+enum class EventAppliesTo : uint8_t {
   kNone = 0,
   kAll = 1,
   kAllNonRecursive = 2,
@@ -40,90 +42,47 @@
   kChoiceList = 7
 };
 
-struct XFA_ExecEventParaInfo {
- public:
+struct ExecEventParaInfo {
   uint32_t m_uHash;  // hashed as wide string.
   XFA_EVENTTYPE m_eventType;
-  EventAppliesToo m_validFlags;
+  EventAppliesTo m_validFlags;
 };
 
 #undef PARA
-#define PARA(a, b, c, d) a, c, d
-const XFA_ExecEventParaInfo gs_eventParaInfos[] = {
-    {PARA(0x109d7ce7,
-          "mouseEnter",
-          XFA_EVENT_MouseEnter,
-          EventAppliesToo::kField)},
-    {PARA(0x1bfc72d9,
-          "preOpen",
-          XFA_EVENT_PreOpen,
-          EventAppliesToo::kChoiceList)},
-    {PARA(0x2196a452,
-          "initialize",
-          XFA_EVENT_Initialize,
-          EventAppliesToo::kAll)},
-    {PARA(0x27410f03,
-          "mouseExit",
-          XFA_EVENT_MouseExit,
-          EventAppliesToo::kField)},
-    {PARA(0x36f1c6d8,
-          "preSign",
-          XFA_EVENT_PreSign,
-          EventAppliesToo::kSignature)},
-    {PARA(0x4731d6ba,
-          "exit",
-          XFA_EVENT_Exit,
-          EventAppliesToo::kAllNonRecursive)},
-    {PARA(0x7233018a, "validate", XFA_EVENT_Validate, EventAppliesToo::kAll)},
-    {PARA(0x8808385e,
-          "indexChange",
-          XFA_EVENT_IndexChange,
-          EventAppliesToo::kSubform)},
-    {PARA(0x891f4606,
-          "change",
-          XFA_EVENT_Change,
-          EventAppliesToo::kFieldOrExclusion)},
-    {PARA(0x9f693b21,
-          "mouseDown",
-          XFA_EVENT_MouseDown,
-          EventAppliesToo::kField)},
-    {PARA(0xcdce56b3,
-          "full",
-          XFA_EVENT_Full,
-          EventAppliesToo::kFieldOrExclusion)},
-    {PARA(0xd576d08e, "mouseUp", XFA_EVENT_MouseUp, EventAppliesToo::kField)},
-    {PARA(0xd95657a6,
-          "click",
-          XFA_EVENT_Click,
-          EventAppliesToo::kFieldOrExclusion)},
-    {PARA(0xdbfbe02e, "calculate", XFA_EVENT_Calculate, EventAppliesToo::kAll)},
-    {PARA(0xe25fa7b8,
-          "postOpen",
-          XFA_EVENT_PostOpen,
-          EventAppliesToo::kChoiceList)},
-    {PARA(0xe28dce7e,
-          "enter",
-          XFA_EVENT_Enter,
-          EventAppliesToo::kAllNonRecursive)},
-    {PARA(0xfd54fbb7,
-          "postSign",
-          XFA_EVENT_PostSign,
-          EventAppliesToo::kSignature)},
+#define PARA(a, b, c, d) a, c, EventAppliesTo::d
+const ExecEventParaInfo kExecEventParaInfoTable[] = {
+    {PARA(0x109d7ce7, "mouseEnter", XFA_EVENT_MouseEnter, kField)},
+    {PARA(0x1bfc72d9, "preOpen", XFA_EVENT_PreOpen, kChoiceList)},
+    {PARA(0x2196a452, "initialize", XFA_EVENT_Initialize, kAll)},
+    {PARA(0x27410f03, "mouseExit", XFA_EVENT_MouseExit, kField)},
+    {PARA(0x36f1c6d8, "preSign", XFA_EVENT_PreSign, kSignature)},
+    {PARA(0x4731d6ba, "exit", XFA_EVENT_Exit, kAllNonRecursive)},
+    {PARA(0x7233018a, "validate", XFA_EVENT_Validate, kAll)},
+    {PARA(0x8808385e, "indexChange", XFA_EVENT_IndexChange, kSubform)},
+    {PARA(0x891f4606, "change", XFA_EVENT_Change, kFieldOrExclusion)},
+    {PARA(0x9f693b21, "mouseDown", XFA_EVENT_MouseDown, kField)},
+    {PARA(0xcdce56b3, "full", XFA_EVENT_Full, kFieldOrExclusion)},
+    {PARA(0xd576d08e, "mouseUp", XFA_EVENT_MouseUp, kField)},
+    {PARA(0xd95657a6, "click", XFA_EVENT_Click, kFieldOrExclusion)},
+    {PARA(0xdbfbe02e, "calculate", XFA_EVENT_Calculate, kAll)},
+    {PARA(0xe25fa7b8, "postOpen", XFA_EVENT_PostOpen, kChoiceList)},
+    {PARA(0xe28dce7e, "enter", XFA_EVENT_Enter, kAllNonRecursive)},
+    {PARA(0xfd54fbb7, "postSign", XFA_EVENT_PostSign, kSignature)},
 };
 #undef PARA
 
-const XFA_ExecEventParaInfo* GetEventParaInfoByName(
+const ExecEventParaInfo* GetExecEventParaInfoByName(
     WideStringView wsEventName) {
   if (wsEventName.IsEmpty())
     return nullptr;
 
-  uint32_t uHash = FX_HashCode_GetW(wsEventName, false);
+  uint32_t uHash = FX_HashCode_GetW(wsEventName);
   auto* result = std::lower_bound(
-      std::begin(gs_eventParaInfos), std::end(gs_eventParaInfos), uHash,
-      [](const XFA_ExecEventParaInfo& iter, const uint16_t& hash) {
+      std::begin(kExecEventParaInfoTable), std::end(kExecEventParaInfoTable),
+      uHash, [](const ExecEventParaInfo& iter, const uint16_t& hash) {
         return iter.m_uHash < hash;
       });
-  if (result != std::end(gs_eventParaInfos) && result->m_uHash == uHash)
+  if (result != std::end(kExecEventParaInfoTable) && result->m_uHash == uHash)
     return result;
   return nullptr;
 }
@@ -153,11 +112,7 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-CXFA_Node* CJX_Node::GetXFANode() const {
-  return ToNode(GetXFAObject());
-}
-
-CJS_Result CJX_Node::applyXSL(CFX_V8* runtime,
+CJS_Result CJX_Node::applyXSL(CFXJSE_Engine* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -167,7 +122,7 @@
 }
 
 CJS_Result CJX_Node::assignNode(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 3)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -176,33 +131,28 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Node::clone(CFX_V8* runtime,
+CJS_Result CJX_Node::clone(CFXJSE_Engine* runtime,
                            const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_Node* pCloneNode = GetXFANode()->Clone(runtime->ToBoolean(params[0]));
-  CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-          pCloneNode);
-
-  return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+  return CJS_Result::Success(runtime->GetOrCreateJSBindingFromMap(pCloneNode));
 }
 
 CJS_Result CJX_Node::getAttribute(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString expression = runtime->ToWideString(params[0]);
   return CJS_Result::Success(runtime->NewString(
-      GetAttribute(expression.AsStringView()).ToUTF8().AsStringView()));
+      GetAttributeByString(expression.AsStringView()).ToUTF8().AsStringView()));
 }
 
 CJS_Result CJX_Node::getElement(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 2)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -217,21 +167,17 @@
   if (!pNode)
     return CJS_Result::Success(runtime->NewNull());
 
-  CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode);
-
-  return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+  return CJS_Result::Success(runtime->GetOrCreateJSBindingFromMap(pNode));
 }
 
 CJS_Result CJX_Node::isPropertySpecified(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 3)
     return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString expression = runtime->ToWideString(params[0]);
-  Optional<XFA_ATTRIBUTEINFO> attr =
+  absl::optional<XFA_ATTRIBUTEINFO> attr =
       XFA_GetAttributeByName(expression.AsStringView());
   if (attr.has_value() && HasAttribute(attr.value().attribute))
     return CJS_Result::Success(runtime->NewBoolean(true));
@@ -252,7 +198,7 @@
   return CJS_Result::Success(runtime->NewBoolean(bHas));
 }
 
-CJS_Result CJX_Node::loadXML(CFX_V8* runtime,
+CJS_Result CJX_Node::loadXML(CFXJSE_Engine* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 3)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -265,18 +211,23 @@
   if (params.size() >= 2)
     bIgnoreRoot = runtime->ToBoolean(params[1]);
 
-  bool bOverwrite = 0;
+  bool bOverwrite = false;
   if (params.size() >= 3)
     bOverwrite = runtime->ToBoolean(params[2]);
 
-  auto pParser = pdfium::MakeUnique<CXFA_DocumentParser>(GetDocument());
-  CFX_XMLNode* pXMLNode = pParser->ParseXMLData(expression);
+  auto stream =
+      pdfium::MakeRetain<CFX_ReadOnlyStringStream>(std::move(expression));
+
+  CFX_XMLParser parser(stream);
+  std::unique_ptr<CFX_XMLDocument> xml_doc = parser.Parse();
+  CXFA_DocumentBuilder builder(GetDocument());
+  CFX_XMLNode* pXMLNode = builder.Build(xml_doc.get());
   if (!pXMLNode)
     return CJS_Result::Success();
 
   CFX_XMLDocument* top_xml_doc =
-      GetXFANode()->GetDocument()->GetNotify()->GetHDOC()->GetXMLDocument();
-  top_xml_doc->AppendNodesFrom(pParser->GetXMLDoc().get());
+      GetXFANode()->GetDocument()->GetNotify()->GetFFDoc()->GetXMLDocument();
+  top_xml_doc->AppendNodesFrom(xml_doc.get());
 
   if (bIgnoreRoot &&
       (pXMLNode->GetType() != CFX_XMLNode::Type::kElement ||
@@ -288,7 +239,7 @@
   WideString wsContentType = GetCData(XFA_Attribute::ContentType);
   if (!wsContentType.IsEmpty()) {
     pFakeRoot->JSObject()->SetCData(XFA_Attribute::ContentType,
-                                    WideString(wsContentType), false, false);
+                                    WideString(wsContentType));
   }
 
   CFX_XMLNode* pFakeXMLRoot = pFakeRoot->GetXMLMappingNode();
@@ -317,8 +268,8 @@
     pFakeXMLRoot->AppendLastChild(pXMLNode);
   }
 
-  pParser->ConstructXFANode(pFakeRoot, pFakeXMLRoot);
-  pFakeRoot = pParser->GetRootNode();
+  builder.ConstructXFANode(pFakeRoot, pFakeXMLRoot);
+  pFakeRoot = builder.GetRootNode();
   if (!pFakeRoot)
     return CJS_Result::Success();
 
@@ -330,7 +281,7 @@
       CXFA_Node* pItem = pNewChild->GetNextSibling();
       pFakeRoot->RemoveChildAndNotify(pNewChild, true);
       GetXFANode()->InsertChildAndNotify(index++, pNewChild);
-      pNewChild->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+      pNewChild->SetInitializedFlagAndNotify();
       pNewChild = pItem;
     }
 
@@ -358,7 +309,7 @@
       CXFA_Node* pItem = pChild->GetNextSibling();
       pFakeRoot->RemoveChildAndNotify(pChild, true);
       GetXFANode()->InsertChildAndNotify(pChild, nullptr);
-      pChild->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+      pChild->SetInitializedFlagAndNotify();
       pChild = pItem;
     }
   }
@@ -366,19 +317,19 @@
   if (pFakeXMLRoot) {
     pFakeRoot->SetXMLMappingNode(std::move(pFakeXMLRoot));
   }
-  pFakeRoot->SetFlag(XFA_NodeFlag_HasRemovedChildren);
+  pFakeRoot->SetFlag(XFA_NodeFlag::kHasRemovedChildren);
 
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_Node::saveFilteredXML(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   // TODO(weili): Check whether we need to implement this, pdfium:501.
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Node::saveXML(CFX_V8* runtime,
+CJS_Result CJX_Node::saveXML(CFXJSE_Engine* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() > 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -416,12 +367,12 @@
     pElement->Save(pMemoryStream);
   }
 
-  return CJS_Result::Success(runtime->NewString(
-      ByteStringView(pMemoryStream->GetBuffer(), pMemoryStream->GetSize())));
+  return CJS_Result::Success(
+      runtime->NewString(ByteStringView(pMemoryStream->GetSpan())));
 }
 
 CJS_Result CJX_Node::setAttribute(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -432,12 +383,12 @@
   WideString attribute = runtime->ToWideString(params[1]);
 
   // Pass them to our method, however, in the more usual manner.
-  SetAttribute(attribute.AsStringView(), attributeValue.AsStringView(), true);
+  SetAttributeByString(attribute.AsStringView(), attributeValue);
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_Node::setElement(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1 && params.size() != 2)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -446,66 +397,75 @@
   return CJS_Result::Success();
 }
 
-void CJX_Node::ns(CFXJSE_Value* pValue,
+void CJX_Node::ns(v8::Isolate* pIsolate,
+                  v8::Local<v8::Value>* pValue,
                   bool bSetting,
                   XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->SetString(
-      TryNamespace().value_or(WideString()).ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(
+      pIsolate, TryNamespace().value_or(WideString()).ToUTF8().AsStringView());
 }
 
-void CJX_Node::model(CFXJSE_Value* pValue,
+void CJX_Node::model(v8::Isolate* pIsolate,
+                     v8::Local<v8::Value>* pValue,
                      bool bSetting,
                      XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->Assign(GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-      GetXFANode()->GetModelNode()));
+  CXFA_Node* pModel = GetXFANode()->GetModelNode();
+  if (!pModel) {
+    *pValue = fxv8::NewNullHelper(pIsolate);
+    return;
+  }
+  *pValue =
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pModel);
 }
 
-void CJX_Node::isContainer(CFXJSE_Value* pValue,
+void CJX_Node::isContainer(v8::Isolate* pIsolate,
+                           v8::Local<v8::Value>* pValue,
                            bool bSetting,
                            XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->SetBoolean(GetXFANode()->IsContainerNode());
+  *pValue = fxv8::NewBooleanHelper(pIsolate, GetXFANode()->IsContainerNode());
 }
 
-void CJX_Node::isNull(CFXJSE_Value* pValue,
+void CJX_Node::isNull(v8::Isolate* pIsolate,
+                      v8::Local<v8::Value>* pValue,
                       bool bSetting,
                       XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
   if (GetXFANode()->GetElementType() == XFA_Element::Subform) {
-    pValue->SetBoolean(false);
+    *pValue = fxv8::NewBooleanHelper(pIsolate, false);
     return;
   }
-  pValue->SetBoolean(GetContent(false).IsEmpty());
+  *pValue = fxv8::NewBooleanHelper(pIsolate, GetContent(false).IsEmpty());
 }
 
-void CJX_Node::oneOfChild(CFXJSE_Value* pValue,
+void CJX_Node::oneOfChild(v8::Isolate* pIsolate,
+                          v8::Local<v8::Value>* pValue,
                           bool bSetting,
                           XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
   std::vector<CXFA_Node*> properties =
-      GetXFANode()->GetNodeListWithFilter(XFA_NODEFILTER_OneOfProperty);
+      GetXFANode()->GetNodeListWithFilter(XFA_NodeFilter::kOneOfProperty);
   if (!properties.empty()) {
-    pValue->Assign(
-        GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-            properties.front()));
+    *pValue = GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+        properties.front());
   }
 }
 
@@ -515,26 +475,26 @@
   if (!pNotify)
     return XFA_EventError::kNotExist;
 
-  const XFA_ExecEventParaInfo* eventParaInfo =
-      GetEventParaInfoByName(wsEventName);
+  const ExecEventParaInfo* eventParaInfo =
+      GetExecEventParaInfoByName(wsEventName);
   if (!eventParaInfo)
     return XFA_EventError::kNotExist;
 
   switch (eventParaInfo->m_validFlags) {
-    case EventAppliesToo::kNone:
+    case EventAppliesTo::kNone:
       return XFA_EventError::kNotExist;
-    case EventAppliesToo::kAll:
-    case EventAppliesToo::kAllNonRecursive:
+    case EventAppliesTo::kAll:
+    case EventAppliesTo::kAllNonRecursive:
       return pNotify->ExecEventByDeepFirst(
           GetXFANode(), eventParaInfo->m_eventType, false,
-          eventParaInfo->m_validFlags == EventAppliesToo::kAll);
-    case EventAppliesToo::kSubform:
+          eventParaInfo->m_validFlags == EventAppliesTo::kAll);
+    case EventAppliesTo::kSubform:
       if (eType != XFA_Element::Subform)
         return XFA_EventError::kNotExist;
 
       return pNotify->ExecEventByDeepFirst(
           GetXFANode(), eventParaInfo->m_eventType, false, false);
-    case EventAppliesToo::kFieldOrExclusion: {
+    case EventAppliesTo::kFieldOrExclusion: {
       if (eType != XFA_Element::ExclGroup && eType != XFA_Element::Field)
         return XFA_EventError::kNotExist;
 
@@ -548,13 +508,13 @@
       return pNotify->ExecEventByDeepFirst(
           GetXFANode(), eventParaInfo->m_eventType, false, false);
     }
-    case EventAppliesToo::kField:
+    case EventAppliesTo::kField:
       if (eType != XFA_Element::Field)
         return XFA_EventError::kNotExist;
 
       return pNotify->ExecEventByDeepFirst(
           GetXFANode(), eventParaInfo->m_eventType, false, false);
-    case EventAppliesToo::kSignature: {
+    case EventAppliesTo::kSignature: {
       if (!GetXFANode()->IsWidgetReady())
         return XFA_EventError::kNotExist;
       if (GetXFANode()->GetUIChildNode()->GetElementType() !=
@@ -564,7 +524,7 @@
       return pNotify->ExecEventByDeepFirst(
           GetXFANode(), eventParaInfo->m_eventType, false, false);
     }
-    case EventAppliesToo::kChoiceList: {
+    case EventAppliesTo::kChoiceList: {
       if (!GetXFANode()->IsWidgetReady())
         return XFA_EventError::kNotExist;
       if (GetXFANode()->GetUIChildNode()->GetElementType() !=
diff --git a/fxjs/xfa/cjx_node.h b/fxjs/xfa/cjx_node.h
index 1cbceb6..bc8eb3f 100644
--- a/fxjs/xfa/cjx_node.h
+++ b/fxjs/xfa/cjx_node.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
 
 class CJX_Node : public CJX_Tree {
  public:
-  explicit CJX_Node(CXFA_Node* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Node() override;
 
   // CJX_Object:
@@ -40,9 +40,9 @@
   JSE_PROP(ns);
   JSE_PROP(oneOfChild);
 
-  CXFA_Node* GetXFANode() const;
-
  protected:
+  explicit CJX_Node(CXFA_Node* node);
+
   XFA_EventError execSingleEventByName(WideStringView wsEventName,
                                        XFA_Element eType);
 
diff --git a/fxjs/xfa/cjx_object.cpp b/fxjs/xfa/cjx_object.cpp
index 64006b3..7542e8a 100644
--- a/fxjs/xfa/cjx_object.cpp
+++ b/fxjs/xfa/cjx_object.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,20 +8,27 @@
 
 #include <set>
 #include <tuple>
+#include <utility>
 
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmltext.h"
 #include "fxjs/cjs_result.h"
+#include "fxjs/fxv8.h"
+#include "fxjs/gc/container_trace.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "fxjs/xfa/cfxjse_mapmodule.h"
 #include "fxjs/xfa/cjx_boolean.h"
 #include "fxjs/xfa/cjx_draw.h"
 #include "fxjs/xfa/cjx_field.h"
 #include "fxjs/xfa/cjx_instancemanager.h"
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/contains.h"
+#include "v8/include/v8-forward.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fgas/crt/cfgas_decimal.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
@@ -44,33 +51,19 @@
 
 namespace {
 
-void XFA_DeleteWideString(void* pData) {
-  delete static_cast<WideString*>(pData);
-}
-
-void XFA_CopyWideString(void*& pData) {
-  if (!pData)
-    return;
-  pData = new WideString(*reinterpret_cast<WideString*>(pData));
-}
-
-const XFA_MAPDATABLOCKCALLBACKINFO deleteWideStringCallBack = {
-    XFA_DeleteWideString, XFA_CopyWideString};
-
 enum XFA_KEYTYPE {
   XFA_KEYTYPE_Custom,
   XFA_KEYTYPE_Element,
 };
 
-void* GetMapKey_Custom(WideStringView wsKey) {
-  uint32_t dwKey = FX_HashCode_GetW(wsKey, false);
-  return (void*)(uintptr_t)((dwKey << 1) | XFA_KEYTYPE_Custom);
+uint32_t GetMapKey_Custom(WideStringView wsKey) {
+  uint32_t dwKey = FX_HashCode_GetW(wsKey);
+  return ((dwKey << 1) | XFA_KEYTYPE_Custom);
 }
 
-void* GetMapKey_Element(XFA_Element eType, XFA_Attribute eAttribute) {
-  return (void*)(uintptr_t)((static_cast<uint32_t>(eType) << 16) |
-                            (static_cast<uint32_t>(eAttribute) << 8) |
-                            XFA_KEYTYPE_Element);
+uint32_t GetMapKey_Element(XFA_Element eType, XFA_Attribute eAttribute) {
+  return ((static_cast<uint32_t>(eType) << 16) |
+          (static_cast<uint32_t>(eAttribute) << 8) | XFA_KEYTYPE_Element);
 }
 
 std::tuple<int32_t, int32_t, int32_t> StrToRGB(const WideString& strRGB) {
@@ -106,25 +99,18 @@
 
 }  // namespace
 
-struct XFA_MAPDATABLOCK {
-  uint8_t* GetData() const { return (uint8_t*)this + sizeof(XFA_MAPDATABLOCK); }
-
-  const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo;
-  int32_t iBytes;
-};
-
-struct XFA_MAPMODULEDATA {
-  XFA_MAPMODULEDATA() {}
-  ~XFA_MAPMODULEDATA() {}
-
-  std::map<void*, void*> m_ValueMap;
-  std::map<void*, XFA_MAPDATABLOCK*> m_BufferMap;
-};
-
 CJX_Object::CJX_Object(CXFA_Object* obj) : object_(obj) {}
 
-CJX_Object::~CJX_Object() {
-  ClearMapModuleBuffer();
+CJX_Object::~CJX_Object() = default;
+
+CJX_Object* CJX_Object::AsCJXObject() {
+  return this;
+}
+
+void CJX_Object::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(object_);
+  visitor->Trace(layout_item_);
+  visitor->Trace(calc_data_);
 }
 
 bool CJX_Object::DynamicTypeIs(TypeTag eType) const {
@@ -140,19 +126,24 @@
   return object_->GetDocument();
 }
 
-void CJX_Object::className(CFXJSE_Value* pValue,
+CXFA_Node* CJX_Object::GetXFANode() const {
+  return ToNode(GetXFAObject());
+}
+
+void CJX_Object::className(v8::Isolate* pIsolate,
+                           v8::Local<v8::Value>* pValue,
                            bool bSetting,
                            XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->SetString(GetXFAObject()->GetClassName());
+  *pValue = fxv8::NewStringHelper(pIsolate, GetXFAObject()->GetClassName());
 }
 
 int32_t CJX_Object::Subform_and_SubformSet_InstanceIndex() {
   int32_t index = 0;
-  for (CXFA_Node* pNode = ToNode(GetXFAObject())->GetPrevSibling(); pNode;
+  for (CXFA_Node* pNode = GetXFANode()->GetPrevSibling(); pNode;
        pNode = pNode->GetPrevSibling()) {
     if ((pNode->GetElementType() != XFA_Element::Subform) &&
         (pNode->GetElementType() != XFA_Element::SubformSet)) {
@@ -164,190 +155,186 @@
 }
 
 bool CJX_Object::HasMethod(const WideString& func) const {
-  return pdfium::ContainsKey(method_specs_, func.ToUTF8());
+  return pdfium::Contains(method_specs_, func.ToUTF8());
 }
 
 CJS_Result CJX_Object::RunMethod(
+    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::ThrowTooManyOccurancesException(const WideString& obj) const {
-  ThrowException(WideString::FromASCII("The element [") + obj +
-                 WideString::FromASCII(
-                     "] has violated its allowable number of occurrences."));
+void CJX_Object::ThrowTooManyOccurrencesException(v8::Isolate* pIsolate,
+                                                  const WideString& obj) const {
+  ThrowException(
+      pIsolate, WideString::FromASCII("The element [") + obj +
+                    WideString::FromASCII(
+                        "] has violated its allowable number of occurrences."));
 }
 
-void CJX_Object::ThrowInvalidPropertyException() const {
-  ThrowException(WideString::FromASCII("Invalid property set operation."));
+void CJX_Object::ThrowInvalidPropertyException(v8::Isolate* pIsolate) const {
+  ThrowException(pIsolate,
+                 WideString::FromASCII("Invalid property set operation."));
 }
 
-void CJX_Object::ThrowIndexOutOfBoundsException() const {
-  ThrowException(WideString::FromASCII("Index value is out of bounds."));
+void CJX_Object::ThrowIndexOutOfBoundsException(v8::Isolate* pIsolate) const {
+  ThrowException(pIsolate,
+                 WideString::FromASCII("Index value is out of bounds."));
 }
 
 void CJX_Object::ThrowParamCountMismatchException(
+    v8::Isolate* pIsolate,
     const WideString& method) const {
   ThrowException(
+      pIsolate,
       WideString::FromASCII("Incorrect number of parameters calling method '") +
-      method + WideString::FromASCII("'."));
+          method + WideString::FromASCII("'."));
 }
 
-void CJX_Object::ThrowArgumentMismatchException() const {
-  ThrowException(WideString::FromASCII(
-      "Argument mismatch in property or function argument."));
+void CJX_Object::ThrowArgumentMismatchException(v8::Isolate* pIsolate) const {
+  ThrowException(pIsolate,
+                 WideString::FromASCII(
+                     "Argument mismatch in property or function argument."));
 }
 
-void CJX_Object::ThrowException(const WideString& str) const {
-  ASSERT(!str.IsEmpty());
-  FXJSE_ThrowMessage(str.ToUTF8().AsStringView());
+void CJX_Object::ThrowException(v8::Isolate* pIsolate,
+                                const WideString& str) const {
+  DCHECK(!str.IsEmpty());
+  FXJSE_ThrowMessage(pIsolate, str.ToUTF8().AsStringView());
 }
 
-bool CJX_Object::HasAttribute(XFA_Attribute eAttr) {
-  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  return HasMapModuleKey(pKey);
+bool CJX_Object::HasAttribute(XFA_Attribute eAttr) const {
+  uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  return HasMapModuleKey(key);
 }
 
-void CJX_Object::SetAttribute(XFA_Attribute eAttr,
-                              WideStringView wsValue,
-                              bool bNotify) {
-  switch (ToNode(GetXFAObject())->GetAttributeType(eAttr)) {
+void CJX_Object::SetAttributeByEnum(XFA_Attribute eAttr,
+                                    const WideString& wsValue,
+                                    bool bNotify) {
+  switch (GetXFANode()->GetAttributeType(eAttr)) {
     case XFA_AttributeType::Enum: {
-      Optional<XFA_AttributeValue> item = XFA_GetAttributeValueByName(wsValue);
+      absl::optional<XFA_AttributeValue> item =
+          XFA_GetAttributeValueByName(wsValue.AsStringView());
       SetEnum(eAttr,
-              item ? *item : *(ToNode(GetXFAObject())->GetDefaultEnum(eAttr)),
+              item.has_value() ? item.value()
+                               : GetXFANode()->GetDefaultEnum(eAttr).value(),
               bNotify);
       break;
     }
     case XFA_AttributeType::CData:
-      SetCData(eAttr, WideString(wsValue), bNotify, false);
+      SetCDataImpl(eAttr, WideString(wsValue), bNotify, false);
       break;
     case XFA_AttributeType::Boolean:
       SetBoolean(eAttr, !wsValue.EqualsASCII("0"), bNotify);
       break;
     case XFA_AttributeType::Integer:
       SetInteger(eAttr,
-                 FXSYS_roundf(FXSYS_wcstof(wsValue.unterminated_c_str(),
-                                           wsValue.GetLength(), nullptr)),
+                 FXSYS_roundf(FXSYS_wcstof(wsValue.c_str(), wsValue.GetLength(),
+                                           nullptr)),
                  bNotify);
       break;
     case XFA_AttributeType::Measure:
-      SetMeasure(eAttr, CXFA_Measurement(wsValue), bNotify);
-      break;
-    default:
+      SetMeasure(eAttr, CXFA_Measurement(wsValue.AsStringView()), bNotify);
       break;
   }
 }
 
-void CJX_Object::SetMapModuleString(void* pKey, WideStringView wsValue) {
-  SetMapModuleBuffer(pKey, const_cast<wchar_t*>(wsValue.unterminated_c_str()),
-                     wsValue.GetLength() * sizeof(wchar_t), nullptr);
-}
-
-void CJX_Object::SetAttribute(WideStringView wsAttr,
-                              WideStringView wsValue,
-                              bool bNotify) {
-  Optional<XFA_ATTRIBUTEINFO> attr = XFA_GetAttributeByName(wsAttr);
+void CJX_Object::SetAttributeByString(WideStringView wsAttr,
+                                      const WideString& wsValue) {
+  absl::optional<XFA_ATTRIBUTEINFO> attr = XFA_GetAttributeByName(wsAttr);
   if (attr.has_value()) {
-    SetAttribute(attr.value().attribute, wsValue, bNotify);
+    SetAttributeByEnum(attr.value().attribute, wsValue, true);
     return;
   }
-  void* pKey = GetMapKey_Custom(wsAttr);
-  SetMapModuleString(pKey, wsValue);
+  uint32_t key = GetMapKey_Custom(wsAttr);
+  SetMapModuleString(key, wsValue);
 }
 
-WideString CJX_Object::GetAttribute(WideStringView attr) {
+WideString CJX_Object::GetAttributeByString(WideStringView attr) const {
+  absl::optional<WideString> result;
+  absl::optional<XFA_ATTRIBUTEINFO> enum_attr = XFA_GetAttributeByName(attr);
+  if (enum_attr.has_value())
+    result = TryAttribute(enum_attr.value().attribute, true);
+  else
+    result = GetMapModuleStringFollowingChain(GetMapKey_Custom(attr));
+  return result.value_or(WideString());
+}
+
+WideString CJX_Object::GetAttributeByEnum(XFA_Attribute attr) const {
   return TryAttribute(attr, true).value_or(WideString());
 }
 
-WideString CJX_Object::GetAttribute(XFA_Attribute attr) {
-  return TryAttribute(attr, true).value_or(WideString());
-}
-
-Optional<WideString> CJX_Object::TryAttribute(XFA_Attribute eAttr,
-                                              bool bUseDefault) {
-  switch (ToNode(GetXFAObject())->GetAttributeType(eAttr)) {
+absl::optional<WideString> CJX_Object::TryAttribute(XFA_Attribute eAttr,
+                                                    bool bUseDefault) const {
+  switch (GetXFANode()->GetAttributeType(eAttr)) {
     case XFA_AttributeType::Enum: {
-      Optional<XFA_AttributeValue> value = TryEnum(eAttr, bUseDefault);
-      if (!value)
-        return {};
-      return WideString::FromASCII(XFA_AttributeValueToName(*value));
+      absl::optional<XFA_AttributeValue> value = TryEnum(eAttr, bUseDefault);
+      if (!value.has_value())
+        return absl::nullopt;
+      return WideString::FromASCII(XFA_AttributeValueToName(value.value()));
     }
     case XFA_AttributeType::CData:
       return TryCData(eAttr, bUseDefault);
 
     case XFA_AttributeType::Boolean: {
-      Optional<bool> value = TryBoolean(eAttr, bUseDefault);
-      if (!value)
-        return {};
-      return WideString(*value ? L"1" : L"0");
+      absl::optional<bool> value = TryBoolean(eAttr, bUseDefault);
+      if (!value.has_value())
+        return absl::nullopt;
+      return WideString(value.value() ? L"1" : L"0");
     }
     case XFA_AttributeType::Integer: {
-      Optional<int32_t> iValue = TryInteger(eAttr, bUseDefault);
-      if (!iValue)
-        return {};
-      return WideString::Format(L"%d", *iValue);
+      absl::optional<int32_t> iValue = TryInteger(eAttr, bUseDefault);
+      if (!iValue.has_value())
+        return absl::nullopt;
+      return WideString::FormatInteger(iValue.value());
     }
     case XFA_AttributeType::Measure: {
-      Optional<CXFA_Measurement> value = TryMeasure(eAttr, bUseDefault);
-      if (!value)
-        return {};
-
+      absl::optional<CXFA_Measurement> value = TryMeasure(eAttr, bUseDefault);
+      if (!value.has_value())
+        return absl::nullopt;
       return value->ToString();
     }
-    default:
-      break;
   }
-  return {};
-}
-
-Optional<WideString> CJX_Object::TryAttribute(WideStringView wsAttr,
-                                              bool bUseDefault) {
-  Optional<XFA_ATTRIBUTEINFO> attr = XFA_GetAttributeByName(wsAttr);
-  if (attr.has_value())
-    return TryAttribute(attr.value().attribute, bUseDefault);
-  return GetMapModuleString(GetMapKey_Custom(wsAttr));
+  return absl::nullopt;
 }
 
 void CJX_Object::RemoveAttribute(WideStringView wsAttr) {
-  void* pKey = GetMapKey_Custom(wsAttr);
-  if (pKey)
-    RemoveMapModuleKey(pKey);
+  RemoveMapModuleKey(GetMapKey_Custom(wsAttr));
 }
 
-Optional<bool> CJX_Object::TryBoolean(XFA_Attribute eAttr, bool bUseDefault) {
-  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  Optional<void*> value = GetMapModuleValue(pKey);
+absl::optional<bool> CJX_Object::TryBoolean(XFA_Attribute eAttr,
+                                            bool bUseDefault) const {
+  uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  absl::optional<int32_t> value = GetMapModuleValueFollowingChain(key);
   if (value.has_value())
     return !!value.value();
   if (!bUseDefault)
-    return {};
-  return ToNode(GetXFAObject())->GetDefaultBoolean(eAttr);
+    return absl::nullopt;
+  return GetXFANode()->GetDefaultBoolean(eAttr);
 }
 
 void CJX_Object::SetBoolean(XFA_Attribute eAttr, bool bValue, bool bNotify) {
-  CFX_XMLElement* elem = SetValue(eAttr, (void*)(uintptr_t)bValue, bNotify);
+  CFX_XMLElement* elem = SetValue(eAttr, static_cast<int32_t>(bValue), bNotify);
   if (elem) {
     elem->SetAttribute(WideString::FromASCII(XFA_AttributeToName(eAttr)),
                        bValue ? L"1" : L"0");
   }
 }
 
-bool CJX_Object::GetBoolean(XFA_Attribute eAttr) {
+bool CJX_Object::GetBoolean(XFA_Attribute eAttr) const {
   return TryBoolean(eAttr, true).value_or(false);
 }
 
 void CJX_Object::SetInteger(XFA_Attribute eAttr, int32_t iValue, bool bNotify) {
-  CFX_XMLElement* elem = SetValue(eAttr, (void*)(uintptr_t)iValue, bNotify);
+  CFX_XMLElement* elem = SetValue(eAttr, iValue, bNotify);
   if (elem) {
     elem->SetAttribute(WideString::FromASCII(XFA_AttributeToName(eAttr)),
-                       WideString::Format(L"%d", iValue));
+                       WideString::FormatInteger(iValue));
   }
 }
 
@@ -355,34 +342,32 @@
   return TryInteger(eAttr, true).value_or(0);
 }
 
-Optional<int32_t> CJX_Object::TryInteger(XFA_Attribute eAttr,
-                                         bool bUseDefault) const {
-  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  Optional<void*> value = GetMapModuleValue(pKey);
+absl::optional<int32_t> CJX_Object::TryInteger(XFA_Attribute eAttr,
+                                               bool bUseDefault) const {
+  uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  absl::optional<int32_t> value = GetMapModuleValueFollowingChain(key);
   if (value.has_value())
-    return static_cast<int32_t>(reinterpret_cast<uintptr_t>(value.value()));
+    return value.value();
   if (!bUseDefault)
-    return {};
-  return ToNode(GetXFAObject())->GetDefaultInteger(eAttr);
+    return absl::nullopt;
+  return GetXFANode()->GetDefaultInteger(eAttr);
 }
 
-Optional<XFA_AttributeValue> CJX_Object::TryEnum(XFA_Attribute eAttr,
-                                                 bool bUseDefault) const {
-  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  Optional<void*> value = GetMapModuleValue(pKey);
-  if (value.has_value()) {
-    return static_cast<XFA_AttributeValue>(
-        reinterpret_cast<uintptr_t>(value.value()));
-  }
+absl::optional<XFA_AttributeValue> CJX_Object::TryEnum(XFA_Attribute eAttr,
+                                                       bool bUseDefault) const {
+  uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  absl::optional<int32_t> value = GetMapModuleValueFollowingChain(key);
+  if (value.has_value())
+    return static_cast<XFA_AttributeValue>(value.value());
   if (!bUseDefault)
-    return {};
-  return ToNode(GetXFAObject())->GetDefaultEnum(eAttr);
+    return absl::nullopt;
+  return GetXFANode()->GetDefaultEnum(eAttr);
 }
 
 void CJX_Object::SetEnum(XFA_Attribute eAttr,
                          XFA_AttributeValue eValue,
                          bool bNotify) {
-  CFX_XMLElement* elem = SetValue(eAttr, (void*)(uintptr_t)eValue, bNotify);
+  CFX_XMLElement* elem = SetValue(eAttr, static_cast<int32_t>(eValue), bNotify);
   if (elem) {
     elem->SetAttribute(WideString::FromASCII(XFA_AttributeToName(eAttr)),
                        WideString::FromASCII(XFA_AttributeValueToName(eValue)));
@@ -394,33 +379,36 @@
 }
 
 void CJX_Object::SetMeasure(XFA_Attribute eAttr,
-                            CXFA_Measurement mValue,
+                            const CXFA_Measurement& mValue,
                             bool bNotify) {
-  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  OnChanging(eAttr, bNotify);
-  SetMapModuleBuffer(pKey, &mValue, sizeof(CXFA_Measurement), nullptr);
-  OnChanged(eAttr, bNotify, false);
+  // Can't short-circuit update here when the value is the same since it
+  // might have come from further up the chain from where we are setting it.
+  uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  if (bNotify)
+    OnChanging(eAttr);
+  SetMapModuleMeasurement(key, mValue);
+  if (bNotify)
+    OnChanged(eAttr, false);
 }
 
-Optional<CXFA_Measurement> CJX_Object::TryMeasure(XFA_Attribute eAttr,
-                                                  bool bUseDefault) const {
-  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  void* pValue;
-  int32_t iBytes;
-  if (GetMapModuleBuffer(pKey, &pValue, &iBytes) &&
-      iBytes == sizeof(CXFA_Measurement)) {
-    return *static_cast<CXFA_Measurement*>(pValue);
-  }
+absl::optional<CXFA_Measurement> CJX_Object::TryMeasure(
+    XFA_Attribute eAttr,
+    bool bUseDefault) const {
+  uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  absl::optional<CXFA_Measurement> result =
+      GetMapModuleMeasurementFollowingChain(key);
+  if (result.has_value())
+    return result.value();
   if (!bUseDefault)
-    return {};
-  return ToNode(GetXFAObject())->GetDefaultMeasurement(eAttr);
+    return absl::nullopt;
+  return GetXFANode()->GetDefaultMeasurement(eAttr);
 }
 
-Optional<float> CJX_Object::TryMeasureAsFloat(XFA_Attribute attr) const {
-  Optional<CXFA_Measurement> measure = TryMeasure(attr, false);
-  if (measure)
-    return measure->ToUnit(XFA_Unit::Pt);
-  return {};
+absl::optional<float> CJX_Object::TryMeasureAsFloat(XFA_Attribute attr) const {
+  absl::optional<CXFA_Measurement> measure = TryMeasure(attr, false);
+  if (!measure.has_value())
+    return absl::nullopt;
+  return measure->ToUnit(XFA_Unit::Pt);
 }
 
 CXFA_Measurement CJX_Object::GetMeasure(XFA_Attribute eAttr) const {
@@ -428,30 +416,33 @@
 }
 
 float CJX_Object::GetMeasureInUnit(XFA_Attribute eAttr, XFA_Unit unit) const {
-  auto measure = TryMeasure(eAttr, true).value_or(CXFA_Measurement());
-  return measure.ToUnit(unit);
+  return GetMeasure(eAttr).ToUnit(unit);
 }
 
 WideString CJX_Object::GetCData(XFA_Attribute eAttr) const {
   return TryCData(eAttr, true).value_or(WideString());
 }
 
-void CJX_Object::SetCData(XFA_Attribute eAttr,
-                          const WideString& wsValue,
-                          bool bNotify,
-                          bool bScriptModify) {
-  CXFA_Node* xfaObj = ToNode(GetXFAObject());
-  void* pKey = GetMapKey_Element(xfaObj->GetElementType(), eAttr);
-  OnChanging(eAttr, bNotify);
-  if (eAttr == XFA_Attribute::Value) {
-    WideString* pClone = new WideString(wsValue);
-    SetUserData(pKey, pClone, &deleteWideStringCallBack);
-  } else {
-    SetMapModuleString(pKey, wsValue.AsStringView());
+void CJX_Object::SetCData(XFA_Attribute eAttr, const WideString& wsValue) {
+  return SetCDataImpl(eAttr, wsValue, false, false);
+}
+
+void CJX_Object::SetCDataImpl(XFA_Attribute eAttr,
+                              const WideString& wsValue,
+                              bool bNotify,
+                              bool bScriptModify) {
+  CXFA_Node* xfaObj = GetXFANode();
+  uint32_t key = GetMapKey_Element(xfaObj->GetElementType(), eAttr);
+  absl::optional<WideString> old_value = GetMapModuleString(key);
+  if (!old_value.has_value() || old_value.value() != wsValue) {
+    if (bNotify)
+      OnChanging(eAttr);
+    SetMapModuleString(key, wsValue);
     if (eAttr == XFA_Attribute::Name)
       xfaObj->UpdateNameHash();
+    if (bNotify)
+      OnChanged(eAttr, bScriptModify);
   }
-  OnChanged(eAttr, bNotify, bScriptModify);
 
   if (!xfaObj->IsNeedSavingXMLNode() || eAttr == XFA_Attribute::QualifiedName ||
       eAttr == XFA_Attribute::BindingNode) {
@@ -469,67 +460,67 @@
     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,
-                                   const WideString& wsXMLValue,
-                                   bool bNotify,
-                                   bool bScriptModify) {
-  auto* xfaObj = ToNode(GetXFAObject());
-  void* pKey =
-      GetMapKey_Element(xfaObj->GetElementType(), XFA_Attribute::Value);
-
-  OnChanging(XFA_Attribute::Value, bNotify);
-  WideString* pClone = new WideString(wsValue);
-
-  SetUserData(pKey, pClone, &deleteWideStringCallBack);
-  OnChanged(XFA_Attribute::Value, bNotify, bScriptModify);
-
-  if (!xfaObj->IsNeedSavingXMLNode())
-    return;
-
-  xfaObj->SetToXML(wsXMLValue);
+                                   const WideString& wsXMLValue) {
+  SetAttributeValueImpl(wsValue, wsXMLValue, false, false);
 }
 
-Optional<WideString> CJX_Object::TryCData(XFA_Attribute eAttr,
-                                          bool bUseDefault) const {
-  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  if (eAttr == XFA_Attribute::Value) {
-    void* pData;
-    int32_t iBytes = 0;
-    WideString* pStr = nullptr;
-    if (GetMapModuleBuffer(pKey, &pData, &iBytes) && iBytes == sizeof(void*)) {
-      memcpy(&pData, pData, iBytes);
-      pStr = reinterpret_cast<WideString*>(pData);
-    }
-    if (pStr)
-      return *pStr;
-  } else {
-    Optional<WideString> value = GetMapModuleString(pKey);
-    if (value.has_value())
-      return value;
+void CJX_Object::SetAttributeValueImpl(const WideString& wsValue,
+                                       const WideString& wsXMLValue,
+                                       bool bNotify,
+                                       bool bScriptModify) {
+  auto* xfaObj = GetXFANode();
+  uint32_t key =
+      GetMapKey_Element(xfaObj->GetElementType(), XFA_Attribute::Value);
+  absl::optional<WideString> old_value = GetMapModuleString(key);
+  if (!old_value.has_value() || old_value.value() != wsValue) {
+    if (bNotify)
+      OnChanging(XFA_Attribute::Value);
+    SetMapModuleString(key, wsValue);
+    if (bNotify)
+      OnChanged(XFA_Attribute::Value, bScriptModify);
+    if (xfaObj->IsNeedSavingXMLNode())
+      xfaObj->SetToXML(wsXMLValue);
   }
+}
+
+absl::optional<WideString> CJX_Object::TryCData(XFA_Attribute eAttr,
+                                                bool bUseDefault) const {
+  uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  absl::optional<WideString> value = GetMapModuleStringFollowingChain(key);
+  if (value.has_value())
+    return value;
+
   if (!bUseDefault)
-    return {};
-  return ToNode(GetXFAObject())->GetDefaultCData(eAttr);
+    return absl::nullopt;
+
+  return GetXFANode()->GetDefaultCData(eAttr);
 }
 
 CFX_XMLElement* CJX_Object::SetValue(XFA_Attribute eAttr,
-                                     void* pValue,
+                                     int32_t value,
                                      bool bNotify) {
-  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  OnChanging(eAttr, bNotify);
-  SetMapModuleValue(pKey, pValue);
-  OnChanged(eAttr, bNotify, false);
-
-  CXFA_Node* pNode = ToNode(GetXFAObject());
+  uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  absl::optional<int32_t> old_value = GetMapModuleValue(key);
+  if (!old_value.has_value() || old_value.value() != value) {
+    if (bNotify)
+      OnChanging(eAttr);
+    SetMapModuleValue(key, value);
+    if (bNotify)
+      OnChanged(eAttr, false);
+  }
+  CXFA_Node* pNode = GetXFANode();
   return pNode->IsNeedSavingXMLNode() ? ToXMLElement(pNode->GetXMLMappingNode())
                                       : nullptr;
 }
@@ -541,9 +532,9 @@
                             bool bSyncData) {
   CXFA_Node* pNode = nullptr;
   CXFA_Node* pBindNode = nullptr;
-  switch (ToNode(GetXFAObject())->GetObjectType()) {
+  switch (GetXFANode()->GetObjectType()) {
     case XFA_ObjectType::ContainerNode: {
-      if (XFA_FieldIsMultiListBox(ToNode(GetXFAObject()))) {
+      if (XFA_FieldIsMultiListBox(GetXFANode())) {
         CXFA_Value* pValue =
             GetOrCreateProperty<CXFA_Value>(0, XFA_Element::Value);
         if (!pValue)
@@ -551,11 +542,11 @@
 
         CXFA_Node* pChildValue = pValue->GetFirstChild();
         pChildValue->JSObject()->SetCData(XFA_Attribute::ContentType,
-                                          L"text/xml", false, false);
+                                          L"text/xml");
         pChildValue->JSObject()->SetContent(wsContent, wsContent, bNotify,
                                             bScriptModify, false);
 
-        CXFA_Node* pBind = ToNode(GetXFAObject())->GetBindData();
+        CXFA_Node* pBind = GetXFANode()->GetBindData();
         if (bSyncData && pBind) {
           std::vector<WideString> wsSaveTextArray =
               fxcrt::Split(wsContent, L'\n');
@@ -573,8 +564,8 @@
               while (iAddNodes-- > 0) {
                 CXFA_Node* pValueNodes =
                     pBind->CreateSamePacketNode(XFA_Element::DataValue);
-                pValueNodes->JSObject()->SetCData(XFA_Attribute::Name, L"value",
-                                                  false, false);
+                pValueNodes->JSObject()->SetCData(XFA_Attribute::Name,
+                                                  L"value");
                 pValueNodes->CreateXMLMappingNode();
                 pBind->InsertChildAndNotify(pValueNodes, nullptr);
               }
@@ -585,15 +576,15 @@
             }
             valueNodes = pBind->GetNodeListForType(XFA_Element::DataValue);
           }
-          ASSERT(valueNodes.size() == wsSaveTextArray.size());
+          DCHECK_EQ(valueNodes.size(), wsSaveTextArray.size());
           size_t i = 0;
           for (CXFA_Node* pValueNode : valueNodes) {
-            pValueNode->JSObject()->SetAttributeValue(
-                wsSaveTextArray[i], wsSaveTextArray[i], false, false);
+            pValueNode->JSObject()->SetAttributeValue(wsSaveTextArray[i],
+                                                      wsSaveTextArray[i]);
             i++;
           }
           for (auto* pArrayNode : pBind->GetBindItemsCopy()) {
-            if (pArrayNode != ToNode(GetXFAObject())) {
+            if (pArrayNode != GetXFANode()) {
               pArrayNode->JSObject()->SetContent(wsContent, wsContent, bNotify,
                                                  bScriptModify, false);
             }
@@ -601,8 +592,8 @@
         }
         break;
       }
-      if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::ExclGroup) {
-        pNode = ToNode(GetXFAObject());
+      if (GetXFANode()->GetElementType() == XFA_Element::ExclGroup) {
+        pNode = GetXFANode();
       } else {
         CXFA_Value* pValue =
             GetOrCreateProperty<CXFA_Value>(0, XFA_Element::Value);
@@ -610,16 +601,17 @@
           break;
 
         CXFA_Node* pChildValue = pValue->GetFirstChild();
-        ASSERT(pChildValue);
-        pChildValue->JSObject()->SetContent(wsContent, wsContent, bNotify,
-                                            bScriptModify, false);
+        if (pChildValue) {
+          pChildValue->JSObject()->SetContent(wsContent, wsContent, bNotify,
+                                              bScriptModify, false);
+        }
       }
-      pBindNode = ToNode(GetXFAObject())->GetBindData();
+      pBindNode = GetXFANode()->GetBindData();
       if (pBindNode && bSyncData) {
         pBindNode->JSObject()->SetContent(wsContent, wsXMLValue, bNotify,
                                           bScriptModify, false);
         for (auto* pArrayNode : pBindNode->GetBindItemsCopy()) {
-          if (pArrayNode != ToNode(GetXFAObject())) {
+          if (pArrayNode != GetXFANode()) {
             pArrayNode->JSObject()->SetContent(wsContent, wsContent, bNotify,
                                                true, false);
           }
@@ -630,27 +622,23 @@
     }
     case XFA_ObjectType::ContentNode: {
       WideString wsContentType;
-      if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::ExData) {
-        Optional<WideString> ret =
+      if (GetXFANode()->GetElementType() == XFA_Element::ExData) {
+        absl::optional<WideString> ret =
             TryAttribute(XFA_Attribute::ContentType, false);
-        if (ret)
-          wsContentType = *ret;
+        if (ret.has_value())
+          wsContentType = ret.value();
         if (wsContentType.EqualsASCII("text/html")) {
           wsContentType.clear();
-          SetAttribute(XFA_Attribute::ContentType, wsContentType.AsStringView(),
-                       false);
+          SetAttributeByEnum(XFA_Attribute::ContentType, wsContentType, false);
         }
       }
 
-      CXFA_Node* pContentRawDataNode = ToNode(GetXFAObject())->GetFirstChild();
+      CXFA_Node* pContentRawDataNode = GetXFANode()->GetFirstChild();
       if (!pContentRawDataNode) {
-        pContentRawDataNode =
-            ToNode(GetXFAObject())
-                ->CreateSamePacketNode(wsContentType.EqualsASCII("text/xml")
-                                           ? XFA_Element::Sharpxml
-                                           : XFA_Element::Sharptext);
-        ToNode(GetXFAObject())
-            ->InsertChildAndNotify(pContentRawDataNode, nullptr);
+        pContentRawDataNode = GetXFANode()->CreateSamePacketNode(
+            wsContentType.EqualsASCII("text/xml") ? XFA_Element::Sharpxml
+                                                  : XFA_Element::Sharptext);
+        GetXFANode()->InsertChildAndNotify(pContentRawDataNode, nullptr);
       }
       pContentRawDataNode->JSObject()->SetContent(
           wsContent, wsXMLValue, bNotify, bScriptModify, bSyncData);
@@ -658,13 +646,12 @@
     }
     case XFA_ObjectType::NodeC:
     case XFA_ObjectType::TextNode:
-      pNode = ToNode(GetXFAObject());
+      pNode = GetXFANode();
       break;
     case XFA_ObjectType::NodeV:
-      pNode = ToNode(GetXFAObject());
-      if (bSyncData &&
-          ToNode(GetXFAObject())->GetPacketType() == XFA_PacketType::Form) {
-        CXFA_Node* pParent = ToNode(GetXFAObject())->GetParent();
+      pNode = GetXFANode();
+      if (bSyncData && GetXFANode()->GetPacketType() == XFA_PacketType::Form) {
+        CXFA_Node* pParent = GetXFANode()->GetParent();
         if (pParent) {
           pParent = pParent->GetParent();
         }
@@ -681,16 +668,16 @@
       }
       break;
     default:
-      if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::DataValue) {
-        pNode = ToNode(GetXFAObject());
-        pBindNode = ToNode(GetXFAObject());
+      if (GetXFANode()->GetElementType() == XFA_Element::DataValue) {
+        pNode = GetXFANode();
+        pBindNode = GetXFANode();
       }
       break;
   }
   if (!pNode)
     return;
 
-  SetAttributeValue(wsContent, wsXMLValue, bNotify, bScriptModify);
+  SetAttributeValueImpl(wsContent, wsXMLValue, bNotify, bScriptModify);
   if (pBindNode && bSyncData) {
     for (auto* pArrayNode : pBindNode->GetBindItemsCopy()) {
       pArrayNode->JSObject()->SetContent(wsContent, wsContent, bNotify,
@@ -699,39 +686,39 @@
   }
 }
 
-WideString CJX_Object::GetContent(bool bScriptModify) {
+WideString CJX_Object::GetContent(bool bScriptModify) const {
   return TryContent(bScriptModify, true).value_or(WideString());
 }
 
-Optional<WideString> CJX_Object::TryContent(bool bScriptModify, bool bProto) {
+absl::optional<WideString> CJX_Object::TryContent(bool bScriptModify,
+                                                  bool bProto) const {
   CXFA_Node* pNode = nullptr;
-  switch (ToNode(GetXFAObject())->GetObjectType()) {
+  switch (GetXFANode()->GetObjectType()) {
     case XFA_ObjectType::ContainerNode:
-      if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::ExclGroup) {
-        pNode = ToNode(GetXFAObject());
+      if (GetXFANode()->GetElementType() == XFA_Element::ExclGroup) {
+        pNode = GetXFANode();
       } else {
         CXFA_Value* pValue =
-            ToNode(GetXFAObject())
-                ->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
+            GetXFANode()->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
         if (!pValue)
-          return {};
+          return absl::nullopt;
 
         CXFA_Node* pChildValue = pValue->GetFirstChild();
-        if (pChildValue && XFA_FieldIsMultiListBox(ToNode(GetXFAObject()))) {
-          pChildValue->JSObject()->SetAttribute(XFA_Attribute::ContentType,
-                                                L"text/xml", false);
+        if (pChildValue && XFA_FieldIsMultiListBox(GetXFANode())) {
+          pChildValue->JSObject()->SetAttributeByEnum(
+              XFA_Attribute::ContentType, L"text/xml", false);
         }
-        if (pChildValue)
-          return pChildValue->JSObject()->TryContent(bScriptModify, bProto);
-        return {};
+        if (!pChildValue)
+          return absl::nullopt;
+        return pChildValue->JSObject()->TryContent(bScriptModify, bProto);
       }
       break;
     case XFA_ObjectType::ContentNode: {
-      CXFA_Node* pContentRawDataNode = ToNode(GetXFAObject())->GetFirstChild();
+      CXFA_Node* pContentRawDataNode = GetXFANode()->GetFirstChild();
       if (!pContentRawDataNode) {
         XFA_Element element = XFA_Element::Sharptext;
-        if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::ExData) {
-          Optional<WideString> contentType =
+        if (GetXFANode()->GetElementType() == XFA_Element::ExData) {
+          absl::optional<WideString> contentType =
               TryAttribute(XFA_Attribute::ContentType, false);
           if (contentType.has_value()) {
             if (contentType.value().EqualsASCII("text/html"))
@@ -740,349 +727,270 @@
               element = XFA_Element::Sharpxml;
           }
         }
-        pContentRawDataNode =
-            ToNode(GetXFAObject())->CreateSamePacketNode(element);
-        ToNode(GetXFAObject())
-            ->InsertChildAndNotify(pContentRawDataNode, nullptr);
+        pContentRawDataNode = GetXFANode()->CreateSamePacketNode(element);
+        GetXFANode()->InsertChildAndNotify(pContentRawDataNode, nullptr);
       }
       return pContentRawDataNode->JSObject()->TryContent(bScriptModify, true);
     }
     case XFA_ObjectType::NodeC:
     case XFA_ObjectType::NodeV:
     case XFA_ObjectType::TextNode:
-      pNode = ToNode(GetXFAObject());
-      FALLTHROUGH;
+      pNode = GetXFANode();
+      [[fallthrough]];
     default:
-      if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::DataValue)
-        pNode = ToNode(GetXFAObject());
+      if (GetXFANode()->GetElementType() == XFA_Element::DataValue)
+        pNode = GetXFANode();
       break;
   }
   if (pNode) {
     if (bScriptModify) {
       CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-      pScriptContext->AddNodesOfRunScript(ToNode(GetXFAObject()));
+      pScriptContext->AddNodesOfRunScript(GetXFANode());
     }
     return TryCData(XFA_Attribute::Value, false);
   }
-  return {};
+  return absl::nullopt;
 }
 
-Optional<WideString> CJX_Object::TryNamespace() {
-  if (ToNode(GetXFAObject())->IsModelNode() ||
-      ToNode(GetXFAObject())->GetElementType() == XFA_Element::Packet) {
-    CFX_XMLNode* pXMLNode = ToNode(GetXFAObject())->GetXMLMappingNode();
+absl::optional<WideString> CJX_Object::TryNamespace() const {
+  if (GetXFANode()->IsModelNode() ||
+      GetXFANode()->GetElementType() == XFA_Element::Packet) {
+    CFX_XMLNode* pXMLNode = GetXFANode()->GetXMLMappingNode();
     CFX_XMLElement* element = ToXMLElement(pXMLNode);
     if (!element)
-      return {};
+      return absl::nullopt;
 
     return element->GetNamespaceURI();
   }
 
-  if (ToNode(GetXFAObject())->GetPacketType() != XFA_PacketType::Datasets)
-    return ToNode(GetXFAObject())->GetModelNode()->JSObject()->TryNamespace();
+  if (GetXFANode()->GetPacketType() != XFA_PacketType::Datasets)
+    return GetXFANode()->GetModelNode()->JSObject()->TryNamespace();
 
-  CFX_XMLNode* pXMLNode = ToNode(GetXFAObject())->GetXMLMappingNode();
+  CFX_XMLNode* pXMLNode = GetXFANode()->GetXMLMappingNode();
   CFX_XMLElement* element = ToXMLElement(pXMLNode);
   if (!element)
-    return {};
+    return absl::nullopt;
 
-  if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::DataValue &&
+  if (GetXFANode()->GetElementType() == XFA_Element::DataValue &&
       GetEnum(XFA_Attribute::Contains) == XFA_AttributeValue::MetaData) {
     WideString wsNamespace;
     if (!XFA_FDEExtension_ResolveNamespaceQualifier(
             element, GetCData(XFA_Attribute::QualifiedName), &wsNamespace)) {
-      return {};
+      return absl::nullopt;
     }
     return wsNamespace;
   }
   return element->GetNamespaceURI();
 }
 
-std::pair<CXFA_Node*, int32_t> CJX_Object::GetPropertyInternal(
-    int32_t index,
-    XFA_Element eProperty) const {
-  return ToNode(GetXFAObject())->GetProperty(index, eProperty);
+CXFA_Node* CJX_Object::GetPropertyInternal(int32_t index,
+                                           XFA_Element eProperty) const {
+  return GetXFANode()->GetProperty(index, eProperty).first;
 }
 
 CXFA_Node* CJX_Object::GetOrCreatePropertyInternal(int32_t index,
                                                    XFA_Element eProperty) {
-  return ToNode(GetXFAObject())->GetOrCreateProperty(index, eProperty);
+  return GetXFANode()->GetOrCreateProperty(index, eProperty);
 }
 
-void CJX_Object::SetUserData(
-    void* pKey,
-    void* pData,
-    const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo) {
-  ASSERT(pCallbackInfo);
-  SetMapModuleBuffer(pKey, &pData, sizeof(void*), pCallbackInfo);
+CFXJSE_MapModule* CJX_Object::CreateMapModule() {
+  if (!map_module_)
+    map_module_ = std::make_unique<CFXJSE_MapModule>();
+  return map_module_.get();
 }
 
-XFA_MAPMODULEDATA* CJX_Object::CreateMapModuleData() {
-  if (!map_module_data_)
-    map_module_data_ = pdfium::MakeUnique<XFA_MAPMODULEDATA>();
-  return map_module_data_.get();
+CFXJSE_MapModule* CJX_Object::GetMapModule() const {
+  return map_module_.get();
 }
 
-XFA_MAPMODULEDATA* CJX_Object::GetMapModuleData() const {
-  return map_module_data_.get();
+void CJX_Object::SetMapModuleValue(uint32_t key, int32_t value) {
+  CreateMapModule()->SetValue(key, value);
 }
 
-void CJX_Object::SetMapModuleValue(void* pKey, void* pValue) {
-  CreateMapModuleData()->m_ValueMap[pKey] = pValue;
+void CJX_Object::SetMapModuleString(uint32_t key, const WideString& wsValue) {
+  CreateMapModule()->SetString(key, wsValue);
 }
 
-Optional<void*> CJX_Object::GetMapModuleValue(void* pKey) const {
+void CJX_Object::SetMapModuleMeasurement(uint32_t key,
+                                         const CXFA_Measurement& value) {
+  CreateMapModule()->SetMeasurement(key, value);
+}
+
+absl::optional<int32_t> CJX_Object::GetMapModuleValue(uint32_t key) const {
+  CFXJSE_MapModule* pModule = GetMapModule();
+  if (!pModule)
+    return absl::nullopt;
+  return pModule->GetValue(key);
+}
+
+absl::optional<WideString> CJX_Object::GetMapModuleString(uint32_t key) const {
+  CFXJSE_MapModule* pModule = GetMapModule();
+  if (!pModule)
+    return absl::nullopt;
+  return pModule->GetString(key);
+}
+
+absl::optional<CXFA_Measurement> CJX_Object::GetMapModuleMeasurement(
+    uint32_t key) const {
+  CFXJSE_MapModule* pModule = GetMapModule();
+  if (!pModule)
+    return absl::nullopt;
+  return pModule->GetMeasurement(key);
+}
+
+absl::optional<int32_t> CJX_Object::GetMapModuleValueFollowingChain(
+    uint32_t key) const {
   std::set<const CXFA_Node*> visited;
-  for (const CXFA_Node* pNode = ToNode(GetXFAObject()); pNode;
+  for (const CXFA_Node* pNode = GetXFANode(); pNode;
        pNode = pNode->GetTemplateNodeIfExists()) {
     if (!visited.insert(pNode).second)
       break;
 
-    XFA_MAPMODULEDATA* pModule = pNode->JSObject()->GetMapModuleData();
-    if (pModule) {
-      auto it = pModule->m_ValueMap.find(pKey);
-      if (it != pModule->m_ValueMap.end())
-        return it->second;
-    }
+    absl::optional<int32_t> result = pNode->JSObject()->GetMapModuleValue(key);
+    if (result.has_value())
+      return result;
+
     if (pNode->GetPacketType() == XFA_PacketType::Datasets)
       break;
   }
-  return {};
+  return absl::nullopt;
 }
 
-Optional<WideString> CJX_Object::GetMapModuleString(void* pKey) const {
-  void* pRawValue;
-  int32_t iBytes;
-  if (!GetMapModuleBuffer(pKey, &pRawValue, &iBytes))
-    return {};
-
-  // Defensive measure: no out-of-bounds pointers even if zero length.
-  int32_t iChars = iBytes / sizeof(wchar_t);
-  return WideString(iChars ? static_cast<const wchar_t*>(pRawValue) : nullptr,
-                    iChars);
-}
-
-void CJX_Object::SetMapModuleBuffer(
-    void* pKey,
-    void* pValue,
-    int32_t iBytes,
-    const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo) {
-  XFA_MAPDATABLOCK*& pBuffer = CreateMapModuleData()->m_BufferMap[pKey];
-  if (!pBuffer) {
-    pBuffer = reinterpret_cast<XFA_MAPDATABLOCK*>(
-        FX_Alloc(uint8_t, sizeof(XFA_MAPDATABLOCK) + iBytes));
-  } else if (pBuffer->iBytes != iBytes) {
-    if (pBuffer->pCallbackInfo && pBuffer->pCallbackInfo->pFree)
-      pBuffer->pCallbackInfo->pFree(*(void**)pBuffer->GetData());
-
-    pBuffer = reinterpret_cast<XFA_MAPDATABLOCK*>(
-        FX_Realloc(uint8_t, pBuffer, sizeof(XFA_MAPDATABLOCK) + iBytes));
-  } else if (pBuffer->pCallbackInfo && pBuffer->pCallbackInfo->pFree) {
-    pBuffer->pCallbackInfo->pFree(
-        *reinterpret_cast<void**>(pBuffer->GetData()));
-  }
-  if (!pBuffer)
-    return;
-
-  pBuffer->pCallbackInfo = pCallbackInfo;
-  pBuffer->iBytes = iBytes;
-  memcpy(pBuffer->GetData(), pValue, iBytes);
-}
-
-bool CJX_Object::GetMapModuleBuffer(void* pKey,
-                                    void** pValue,
-                                    int32_t* pBytes) const {
+absl::optional<WideString> CJX_Object::GetMapModuleStringFollowingChain(
+    uint32_t key) const {
   std::set<const CXFA_Node*> visited;
-  XFA_MAPDATABLOCK* pBuffer = nullptr;
-  for (const CXFA_Node* pNode = ToNode(GetXFAObject()); pNode;
+  for (const CXFA_Node* pNode = GetXFANode(); pNode;
        pNode = pNode->GetTemplateNodeIfExists()) {
     if (!visited.insert(pNode).second)
       break;
 
-    XFA_MAPMODULEDATA* pModule = pNode->JSObject()->GetMapModuleData();
-    if (pModule) {
-      auto it = pModule->m_BufferMap.find(pKey);
-      if (it != pModule->m_BufferMap.end()) {
-        pBuffer = it->second;
-        break;
-      }
-    }
+    absl::optional<WideString> result =
+        pNode->JSObject()->GetMapModuleString(key);
+    if (result.has_value())
+      return result;
+
     if (pNode->GetPacketType() == XFA_PacketType::Datasets)
       break;
   }
-  if (!pBuffer)
-    return false;
-
-  *pValue = pBuffer->GetData();
-  *pBytes = pBuffer->iBytes;
-  return true;
+  return absl::nullopt;
 }
 
-bool CJX_Object::HasMapModuleKey(void* pKey) {
-  XFA_MAPMODULEDATA* pModule = GetMapModuleData();
-  return pModule && (pdfium::ContainsKey(pModule->m_ValueMap, pKey) ||
-                     pdfium::ContainsKey(pModule->m_BufferMap, pKey));
-}
+absl::optional<CXFA_Measurement>
+CJX_Object::GetMapModuleMeasurementFollowingChain(uint32_t key) const {
+  std::set<const CXFA_Node*> visited;
+  for (const CXFA_Node* pNode = GetXFANode(); pNode;
+       pNode = pNode->GetTemplateNodeIfExists()) {
+    if (!visited.insert(pNode).second)
+      break;
 
-void CJX_Object::ClearMapModuleBuffer() {
-  XFA_MAPMODULEDATA* pModule = GetMapModuleData();
-  if (!pModule)
-    return;
+    absl::optional<CXFA_Measurement> result =
+        pNode->JSObject()->GetMapModuleMeasurement(key);
+    if (result.has_value())
+      return result;
 
-  for (auto& pair : pModule->m_BufferMap) {
-    XFA_MAPDATABLOCK* pBuffer = pair.second;
-    if (pBuffer) {
-      if (pBuffer->pCallbackInfo && pBuffer->pCallbackInfo->pFree)
-        pBuffer->pCallbackInfo->pFree(*(void**)pBuffer->GetData());
-
-      FX_Free(pBuffer);
-    }
+    if (pNode->GetPacketType() == XFA_PacketType::Datasets)
+      break;
   }
-  pModule->m_BufferMap.clear();
-  pModule->m_ValueMap.clear();
+  return absl::nullopt;
 }
 
-void CJX_Object::RemoveMapModuleKey(void* pKey) {
-  ASSERT(pKey);
-
-  XFA_MAPMODULEDATA* pModule = GetMapModuleData();
-  if (!pModule)
-    return;
-
-  auto it = pModule->m_BufferMap.find(pKey);
-  if (it != pModule->m_BufferMap.end()) {
-    XFA_MAPDATABLOCK* pBuffer = it->second;
-    if (pBuffer) {
-      if (pBuffer->pCallbackInfo && pBuffer->pCallbackInfo->pFree)
-        pBuffer->pCallbackInfo->pFree(*(void**)pBuffer->GetData());
-
-      FX_Free(pBuffer);
-    }
-    pModule->m_BufferMap.erase(it);
-  }
-  pModule->m_ValueMap.erase(pKey);
-  return;
+bool CJX_Object::HasMapModuleKey(uint32_t key) const {
+  CFXJSE_MapModule* pModule = GetMapModule();
+  return pModule && pModule->HasKey(key);
 }
 
-void CJX_Object::MergeAllData(CXFA_Object* pDstModule) {
-  XFA_MAPMODULEDATA* pDstModuleData =
-      ToNode(pDstModule)->JSObject()->CreateMapModuleData();
-  XFA_MAPMODULEDATA* pSrcModuleData = GetMapModuleData();
-  if (!pSrcModuleData)
-    return;
-
-  for (const auto& pair : pSrcModuleData->m_ValueMap)
-    pDstModuleData->m_ValueMap[pair.first] = pair.second;
-
-  for (const auto& pair : pSrcModuleData->m_BufferMap) {
-    XFA_MAPDATABLOCK* pSrcBuffer = pair.second;
-    XFA_MAPDATABLOCK*& pDstBuffer = pDstModuleData->m_BufferMap[pair.first];
-    if (pSrcBuffer->pCallbackInfo && pSrcBuffer->pCallbackInfo->pFree &&
-        !pSrcBuffer->pCallbackInfo->pCopy) {
-      if (pDstBuffer) {
-        pDstBuffer->pCallbackInfo->pFree(*(void**)pDstBuffer->GetData());
-        pDstModuleData->m_BufferMap.erase(pair.first);
-      }
-      continue;
-    }
-    if (!pDstBuffer) {
-      pDstBuffer = (XFA_MAPDATABLOCK*)FX_Alloc(
-          uint8_t, sizeof(XFA_MAPDATABLOCK) + pSrcBuffer->iBytes);
-    } else if (pDstBuffer->iBytes != pSrcBuffer->iBytes) {
-      if (pDstBuffer->pCallbackInfo && pDstBuffer->pCallbackInfo->pFree) {
-        pDstBuffer->pCallbackInfo->pFree(*(void**)pDstBuffer->GetData());
-      }
-      pDstBuffer = (XFA_MAPDATABLOCK*)FX_Realloc(
-          uint8_t, pDstBuffer, sizeof(XFA_MAPDATABLOCK) + pSrcBuffer->iBytes);
-    } else if (pDstBuffer->pCallbackInfo && pDstBuffer->pCallbackInfo->pFree) {
-      pDstBuffer->pCallbackInfo->pFree(*(void**)pDstBuffer->GetData());
-    }
-    if (!pDstBuffer)
-      continue;
-
-    pDstBuffer->pCallbackInfo = pSrcBuffer->pCallbackInfo;
-    pDstBuffer->iBytes = pSrcBuffer->iBytes;
-    memcpy(pDstBuffer->GetData(), pSrcBuffer->GetData(), pSrcBuffer->iBytes);
-    if (pDstBuffer->pCallbackInfo && pDstBuffer->pCallbackInfo->pCopy) {
-      pDstBuffer->pCallbackInfo->pCopy(*(void**)pDstBuffer->GetData());
-    }
-  }
+void CJX_Object::RemoveMapModuleKey(uint32_t key) {
+  CFXJSE_MapModule* pModule = GetMapModule();
+  if (pModule)
+    pModule->RemoveKey(key);
 }
 
-void CJX_Object::MoveBufferMapData(CXFA_Object* pDstModule) {
-  if (!pDstModule)
+void CJX_Object::MergeAllData(CXFA_Object* pDstObj) {
+  CFXJSE_MapModule* pDstModule = ToNode(pDstObj)->JSObject()->CreateMapModule();
+  CFXJSE_MapModule* pSrcModule = GetMapModule();
+  if (!pSrcModule)
     return;
 
-  bool bNeedMove = true;
-  if (pDstModule->GetElementType() != GetXFAObject()->GetElementType())
-    bNeedMove = false;
+  pDstModule->MergeDataFrom(pSrcModule);
+}
 
-  if (bNeedMove)
-    ToNode(pDstModule)->JSObject()->SetCalcData(ReleaseCalcData());
-  if (!pDstModule->IsNodeV())
+void CJX_Object::MoveBufferMapData(CXFA_Object* pDstObj) {
+  if (!pDstObj)
     return;
 
-  WideString wsValue = ToNode(pDstModule)->JSObject()->GetContent(false);
+  if (pDstObj->GetElementType() == GetXFAObject()->GetElementType())
+    ToNode(pDstObj)->JSObject()->TakeCalcDataFrom(this);
+
+  if (!pDstObj->IsNodeV())
+    return;
+
+  WideString wsValue = ToNode(pDstObj)->JSObject()->GetContent(false);
   WideString wsFormatValue(wsValue);
-  CXFA_Node* pNode = ToNode(pDstModule)->GetContainerNode();
+  CXFA_Node* pNode = ToNode(pDstObj)->GetContainerNode();
   if (pNode)
     wsFormatValue = pNode->GetFormatDataValue(wsValue);
 
-  ToNode(pDstModule)
-      ->JSObject()
-      ->SetContent(wsValue, wsFormatValue, true, true, true);
+  ToNode(pDstObj)->JSObject()->SetContent(wsValue, wsFormatValue, true, true,
+                                          true);
 }
 
-void CJX_Object::MoveBufferMapData(CXFA_Object* pSrcModule,
-                                   CXFA_Object* pDstModule) {
-  if (!pSrcModule || !pDstModule)
+void CJX_Object::MoveBufferMapData(CXFA_Object* pSrcObj, CXFA_Object* pDstObj) {
+  if (!pSrcObj || !pDstObj)
     return;
 
-  CXFA_Node* pSrcChild = ToNode(pSrcModule)->GetFirstChild();
-  CXFA_Node* pDstChild = ToNode(pDstModule)->GetFirstChild();
+  CXFA_Node* pSrcChild = ToNode(pSrcObj)->GetFirstChild();
+  CXFA_Node* pDstChild = ToNode(pDstObj)->GetFirstChild();
   while (pSrcChild && pDstChild) {
     MoveBufferMapData(pSrcChild, pDstChild);
-
     pSrcChild = pSrcChild->GetNextSibling();
     pDstChild = pDstChild->GetNextSibling();
   }
-  ToNode(pSrcModule)->JSObject()->MoveBufferMapData(pDstModule);
+  ToNode(pSrcObj)->JSObject()->MoveBufferMapData(pDstObj);
 }
 
-void CJX_Object::OnChanging(XFA_Attribute eAttr, bool bNotify) {
-  if (!bNotify || !ToNode(GetXFAObject())->IsInitialized())
+void CJX_Object::OnChanging(XFA_Attribute eAttr) {
+  if (!GetXFANode()->IsInitialized())
     return;
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
-  if (pNotify)
-    pNotify->OnValueChanging(ToNode(GetXFAObject()), eAttr);
+  if (!pNotify)
+    return;
+
+  pNotify->OnValueChanging(GetXFANode(), eAttr);
 }
 
-void CJX_Object::OnChanged(XFA_Attribute eAttr,
-                           bool bNotify,
-                           bool bScriptModify) {
-  if (bNotify && ToNode(GetXFAObject())->IsInitialized())
-    ToNode(GetXFAObject())->SendAttributeChangeMessage(eAttr, bScriptModify);
+void CJX_Object::OnChanged(XFA_Attribute eAttr, bool bScriptModify) {
+  if (!GetXFANode()->IsInitialized())
+    return;
+
+  GetXFANode()->SendAttributeChangeMessage(eAttr, bScriptModify);
 }
 
-void CJX_Object::SetCalcData(std::unique_ptr<CXFA_CalcData> data) {
-  calc_data_ = std::move(data);
+CJX_Object::CalcData* CJX_Object::GetOrCreateCalcData(cppgc::Heap* heap) {
+  if (!calc_data_) {
+    calc_data_ =
+        cppgc::MakeGarbageCollected<CalcData>(heap->GetAllocationHandle());
+  }
+  return calc_data_;
 }
 
-std::unique_ptr<CXFA_CalcData> CJX_Object::ReleaseCalcData() {
-  return std::move(calc_data_);
+void CJX_Object::TakeCalcDataFrom(CJX_Object* that) {
+  calc_data_ = that->calc_data_;
+  that->calc_data_ = nullptr;
 }
 
-void CJX_Object::ScriptAttributeString(CFXJSE_Value* pValue,
+void CJX_Object::ScriptAttributeString(v8::Isolate* pIsolate,
+                                       v8::Local<v8::Value>* pValue,
                                        bool bSetting,
                                        XFA_Attribute eAttribute) {
   if (!bSetting) {
-    pValue->SetString(GetAttribute(eAttribute).ToUTF8().AsStringView());
+    *pValue = fxv8::NewStringHelper(
+        pIsolate, GetAttributeByEnum(eAttribute).ToUTF8().AsStringView());
     return;
   }
 
-  WideString wsValue = pValue->ToWideString();
-  SetAttribute(eAttribute, wsValue.AsStringView(), true);
+  WideString wsValue = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue);
+  SetAttributeByEnum(eAttribute, wsValue, true);
   if (eAttribute != XFA_Attribute::Use ||
       GetXFAObject()->GetElementType() != XFA_Element::Desc) {
     return;
@@ -1100,33 +1008,34 @@
   WideString wsSOM;
   if (!wsValue.IsEmpty()) {
     if (wsValue[0] == '#')
-      wsID = wsValue.Substr(1, wsValue.GetLength() - 1);
+      wsID = wsValue.Substr(1);
     else
       wsSOM = std::move(wsValue);
   }
 
   CXFA_Node* pProtoNode = nullptr;
   if (!wsSOM.IsEmpty()) {
-    XFA_RESOLVENODE_RS resolveNodeRS;
-    bool bRet = GetDocument()->GetScriptContext()->ResolveObjects(
-        pProtoRoot, wsSOM.AsStringView(), &resolveNodeRS,
-        XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
-            XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent |
-            XFA_RESOLVENODE_Siblings,
-        nullptr);
-    if (bRet && resolveNodeRS.objects.front()->IsNode())
-      pProtoNode = resolveNodeRS.objects.front()->AsNode();
-
+    absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+        GetDocument()->GetScriptContext()->ResolveObjects(
+            pProtoRoot, wsSOM.AsStringView(),
+            Mask<XFA_ResolveFlag>{
+                XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes,
+                XFA_ResolveFlag::kProperties, XFA_ResolveFlag::kParent,
+                XFA_ResolveFlag::kSiblings});
+    if (maybeResult.has_value() &&
+        maybeResult.value().objects.front()->IsNode()) {
+      pProtoNode = maybeResult.value().objects.front()->AsNode();
+    }
   } else if (!wsID.IsEmpty()) {
     pProtoNode = GetDocument()->GetNodeByID(pProtoRoot, wsID.AsStringView());
   }
-  if (!pProtoNode)
+  if (!pProtoNode || pProtoNode->GetPacketType() != XFA_PacketType::Template)
     return;
 
-  CXFA_Node* pHeadChild = ToNode(GetXFAObject())->GetFirstChild();
+  CXFA_Node* pHeadChild = GetXFANode()->GetFirstChild();
   while (pHeadChild) {
     CXFA_Node* pSibling = pHeadChild->GetNextSibling();
-    ToNode(GetXFAObject())->RemoveChildAndNotify(pHeadChild, true);
+    GetXFANode()->RemoveChildAndNotify(pHeadChild, true);
     pHeadChild = pSibling;
   }
 
@@ -1135,32 +1044,37 @@
   while (pHeadChild) {
     CXFA_Node* pSibling = pHeadChild->GetNextSibling();
     pProtoForm->RemoveChildAndNotify(pHeadChild, true);
-    ToNode(GetXFAObject())->InsertChildAndNotify(pHeadChild, nullptr);
+    GetXFANode()->InsertChildAndNotify(pHeadChild, nullptr);
     pHeadChild = pSibling;
   }
 }
 
-void CJX_Object::ScriptAttributeBool(CFXJSE_Value* pValue,
+void CJX_Object::ScriptAttributeBool(v8::Isolate* pIsolate,
+                                     v8::Local<v8::Value>* pValue,
                                      bool bSetting,
                                      XFA_Attribute eAttribute) {
   if (bSetting) {
-    SetBoolean(eAttribute, pValue->ToBoolean(), true);
+    SetBoolean(eAttribute, fxv8::ReentrantToBooleanHelper(pIsolate, *pValue),
+               true);
     return;
   }
-  pValue->SetString(GetBoolean(eAttribute) ? "1" : "0");
+  *pValue = fxv8::NewStringHelper(pIsolate, GetBoolean(eAttribute) ? "1" : "0");
 }
 
-void CJX_Object::ScriptAttributeInteger(CFXJSE_Value* pValue,
+void CJX_Object::ScriptAttributeInteger(v8::Isolate* pIsolate,
+                                        v8::Local<v8::Value>* pValue,
                                         bool bSetting,
                                         XFA_Attribute eAttribute) {
   if (bSetting) {
-    SetInteger(eAttribute, pValue->ToInteger(), true);
+    SetInteger(eAttribute, fxv8::ReentrantToInt32Helper(pIsolate, *pValue),
+               true);
     return;
   }
-  pValue->SetInteger(GetInteger(eAttribute));
+  *pValue = fxv8::NewNumberHelper(pIsolate, GetInteger(eAttribute));
 }
 
-void CJX_Object::ScriptSomFontColor(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomFontColor(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     bool bSetting,
                                     XFA_Attribute eAttribute) {
   CXFA_Font* font = ToNode(object_.Get())->GetOrCreateFontIfPossible();
@@ -1171,7 +1085,8 @@
     int32_t r;
     int32_t g;
     int32_t b;
-    std::tie(r, g, b) = StrToRGB(pValue->ToWideString());
+    std::tie(r, g, b) =
+        StrToRGB(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
     FX_ARGB color = ArgbEncode(0xff, r, g, b);
     font->SetColor(color);
     return;
@@ -1182,10 +1097,12 @@
   int32_t g;
   int32_t b;
   std::tie(a, r, g, b) = ArgbDecode(font->GetColor());
-  pValue->SetString(ByteString::Format("%d,%d,%d", r, g, b).AsStringView());
+  *pValue = fxv8::NewStringHelper(
+      pIsolate, ByteString::Format("%d,%d,%d", r, g, b).AsStringView());
 }
 
-void CJX_Object::ScriptSomFillColor(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomFillColor(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     bool bSetting,
                                     XFA_Attribute eAttribute) {
   CXFA_Border* border = ToNode(object_.Get())->GetOrCreateBorderIfPossible();
@@ -1197,23 +1114,25 @@
     int32_t r;
     int32_t g;
     int32_t b;
-    std::tie(r, g, b) = StrToRGB(pValue->ToWideString());
+    std::tie(r, g, b) =
+        StrToRGB(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
     FX_ARGB color = ArgbEncode(0xff, r, g, b);
     borderfill->SetColor(color);
     return;
   }
 
-  FX_ARGB color = borderfill->GetColor(false);
+  FX_ARGB color = borderfill->GetFillColor();
   int32_t a;
   int32_t r;
   int32_t g;
   int32_t b;
   std::tie(a, r, g, b) = ArgbDecode(color);
-  pValue->SetString(
-      WideString::Format(L"%d,%d,%d", r, g, b).ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(
+      pIsolate, ByteString::Format("%d,%d,%d", r, g, b).AsStringView());
 }
 
-void CJX_Object::ScriptSomBorderColor(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomBorderColor(v8::Isolate* pIsolate,
+                                      v8::Local<v8::Value>* pValue,
                                       bool bSetting,
                                       XFA_Attribute eAttribute) {
   CXFA_Border* border = ToNode(object_.Get())->GetOrCreateBorderIfPossible();
@@ -1222,7 +1141,8 @@
     int32_t r = 0;
     int32_t g = 0;
     int32_t b = 0;
-    std::tie(r, g, b) = StrToRGB(pValue->ToWideString());
+    std::tie(r, g, b) =
+        StrToRGB(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
     FX_ARGB rgb = ArgbEncode(100, r, g, b);
     for (int32_t i = 0; i < iSize; ++i) {
       CXFA_Edge* edge = border->GetEdgeIfExists(i);
@@ -1240,11 +1160,12 @@
   int32_t g;
   int32_t b;
   std::tie(a, r, g, b) = ArgbDecode(color);
-  pValue->SetString(
-      WideString::Format(L"%d,%d,%d", r, g, b).ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(
+      pIsolate, ByteString::Format("%d,%d,%d", r, g, b).AsStringView());
 }
 
-void CJX_Object::ScriptSomBorderWidth(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomBorderWidth(v8::Isolate* pIsolate,
+                                      v8::Local<v8::Value>* pValue,
                                       bool bSetting,
                                       XFA_Attribute eAttribute) {
   CXFA_Border* border = ToNode(object_.Get())->GetOrCreateBorderIfPossible();
@@ -1252,24 +1173,26 @@
     CXFA_Edge* edge = border->GetEdgeIfExists(0);
     CXFA_Measurement thickness =
         edge ? edge->GetMSThickness() : CXFA_Measurement(0.5, XFA_Unit::Pt);
-    pValue->SetString(thickness.ToString().ToUTF8().AsStringView());
+    *pValue = fxv8::NewStringHelper(
+        pIsolate, thickness.ToString().ToUTF8().AsStringView());
     return;
   }
 
   if (pValue->IsEmpty())
     return;
 
-  WideString wsThickness = pValue->ToWideString();
-  for (int32_t i = 0; i < border->CountEdges(); ++i) {
+  WideString wsThickness = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue);
+  for (size_t i = 0; i < border->CountEdges(); ++i) {
     CXFA_Edge* edge = border->GetEdgeIfExists(i);
     if (edge)
       edge->SetMSThickness(CXFA_Measurement(wsThickness.AsStringView()));
   }
 }
 
-void CJX_Object::ScriptSomMessage(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomMessage(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
-                                  XFA_SOM_MESSAGETYPE iMessageType) {
+                                  SOMMessageType iMessageType) {
   bool bNew = false;
   CXFA_Validate* validate = ToNode(object_.Get())->GetValidateIfExists();
   if (!validate) {
@@ -1280,16 +1203,17 @@
   if (bSetting) {
     if (validate) {
       switch (iMessageType) {
-        case XFA_SOM_ValidationMessage:
-          validate->SetScriptMessageText(pValue->ToWideString());
+        case SOMMessageType::kValidationMessage:
+          validate->SetScriptMessageText(
+              fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
           break;
-        case XFA_SOM_FormatMessage:
-          validate->SetFormatMessageText(pValue->ToWideString());
+        case SOMMessageType::kFormatMessage:
+          validate->SetFormatMessageText(
+              fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
           break;
-        case XFA_SOM_MandatoryMessage:
-          validate->SetNullMessageText(pValue->ToWideString());
-          break;
-        default:
+        case SOMMessageType::kMandatoryMessage:
+          validate->SetNullMessageText(
+              fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
           break;
       }
     }
@@ -1299,98 +1223,100 @@
       if (!pNotify)
         return;
 
-      pNotify->AddCalcValidate(ToNode(GetXFAObject()));
+      pNotify->AddCalcValidate(GetXFANode());
     }
     return;
   }
 
   if (!validate) {
     // TODO(dsinclair): Better error message?
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
   WideString wsMessage;
   switch (iMessageType) {
-    case XFA_SOM_ValidationMessage:
+    case SOMMessageType::kValidationMessage:
       wsMessage = validate->GetScriptMessageText();
       break;
-    case XFA_SOM_FormatMessage:
+    case SOMMessageType::kFormatMessage:
       wsMessage = validate->GetFormatMessageText();
       break;
-    case XFA_SOM_MandatoryMessage:
+    case SOMMessageType::kMandatoryMessage:
       wsMessage = validate->GetNullMessageText();
       break;
-    default:
-      break;
   }
-  pValue->SetString(wsMessage.ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(pIsolate, wsMessage.ToUTF8().AsStringView());
 }
 
-void CJX_Object::ScriptSomValidationMessage(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomValidationMessage(v8::Isolate* pIsolate,
+                                            v8::Local<v8::Value>* pValue,
                                             bool bSetting,
                                             XFA_Attribute eAttribute) {
-  ScriptSomMessage(pValue, bSetting, XFA_SOM_ValidationMessage);
+  ScriptSomMessage(pIsolate, pValue, bSetting,
+                   SOMMessageType::kValidationMessage);
 }
 
-void CJX_Object::ScriptSomMandatoryMessage(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomMandatoryMessage(v8::Isolate* pIsolate,
+                                           v8::Local<v8::Value>* pValue,
                                            bool bSetting,
                                            XFA_Attribute eAttribute) {
-  ScriptSomMessage(pValue, bSetting, XFA_SOM_MandatoryMessage);
+  ScriptSomMessage(pIsolate, pValue, bSetting,
+                   SOMMessageType::kMandatoryMessage);
 }
 
-void CJX_Object::ScriptSomDefaultValue(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomDefaultValue(v8::Isolate* pIsolate,
+                                       v8::Local<v8::Value>* pValue,
                                        bool bSetting,
                                        XFA_Attribute /* unused */) {
-  XFA_Element eType = ToNode(GetXFAObject())->GetElementType();
+  XFA_Element eType = GetXFANode()->GetElementType();
 
   // TODO(dsinclair): This should look through the properties on the node to see
   // if defaultValue is defined and, if so, call that one. Just have to make
   // sure that those defaultValue calls don't call back to this one ....
   if (eType == XFA_Element::Field) {
-    static_cast<CJX_Field*>(this)->defaultValue(pValue, bSetting,
+    static_cast<CJX_Field*>(this)->defaultValue(pIsolate, pValue, bSetting,
                                                 XFA_Attribute::Unknown);
     return;
   }
   if (eType == XFA_Element::Draw) {
-    static_cast<CJX_Draw*>(this)->defaultValue(pValue, bSetting,
+    static_cast<CJX_Draw*>(this)->defaultValue(pIsolate, pValue, bSetting,
                                                XFA_Attribute::Unknown);
     return;
   }
   if (eType == XFA_Element::Boolean) {
-    static_cast<CJX_Boolean*>(this)->defaultValue(pValue, bSetting,
+    static_cast<CJX_Boolean*>(this)->defaultValue(pIsolate, pValue, bSetting,
                                                   XFA_Attribute::Unknown);
     return;
   }
 
   if (bSetting) {
     WideString wsNewValue;
-    if (pValue &&
-        !(pValue->IsEmpty() || pValue->IsNull() || pValue->IsUndefined())) {
-      wsNewValue = pValue->ToWideString();
+    if (pValue && !(pValue->IsEmpty() || fxv8::IsNull(*pValue) ||
+                    fxv8::IsUndefined(*pValue))) {
+      wsNewValue = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue);
     }
 
-    WideString wsFormatValue(wsNewValue);
+    WideString wsFormatValue = wsNewValue;
     CXFA_Node* pContainerNode = nullptr;
-    if (ToNode(GetXFAObject())->GetPacketType() == XFA_PacketType::Datasets) {
+    if (GetXFANode()->GetPacketType() == XFA_PacketType::Datasets) {
       WideString wsPicture;
-      for (auto* pFormNode : ToNode(GetXFAObject())->GetBindItemsCopy()) {
+      for (auto* pFormNode : GetXFANode()->GetBindItemsCopy()) {
         if (!pFormNode || pFormNode->HasRemovedChildren())
           continue;
 
         pContainerNode = pFormNode->GetContainerNode();
         if (pContainerNode) {
           wsPicture =
-              pContainerNode->GetPictureContent(XFA_VALUEPICTURE_DataBind);
+              pContainerNode->GetPictureContent(XFA_ValuePicture::kDataBind);
         }
         if (!wsPicture.IsEmpty())
           break;
 
         pContainerNode = nullptr;
       }
-    } else if (ToNode(GetXFAObject())->GetPacketType() ==
-               XFA_PacketType::Form) {
-      pContainerNode = ToNode(GetXFAObject())->GetContainerNode();
+    } else if (GetXFANode()->GetPacketType() == XFA_PacketType::Form) {
+      pContainerNode = GetXFANode()->GetContainerNode();
     }
 
     if (pContainerNode)
@@ -1403,52 +1329,55 @@
   WideString content = GetContent(true);
   if (content.IsEmpty() && eType != XFA_Element::Text &&
       eType != XFA_Element::SubmitUrl) {
-    pValue->SetNull();
+    *pValue = fxv8::NewNullHelper(pIsolate);
   } else if (eType == XFA_Element::Integer) {
-    pValue->SetInteger(FXSYS_wtoi(content.c_str()));
+    *pValue = fxv8::NewNumberHelper(pIsolate, FXSYS_wtoi(content.c_str()));
   } else if (eType == XFA_Element::Float || eType == XFA_Element::Decimal) {
     CFGAS_Decimal decimal(content.AsStringView());
-    pValue->SetFloat(decimal.ToFloat());
+    *pValue = fxv8::NewNumberHelper(pIsolate, decimal.ToFloat());
   } else {
-    pValue->SetString(content.ToUTF8().AsStringView());
+    *pValue = fxv8::NewStringHelper(pIsolate, content.ToUTF8().AsStringView());
   }
 }
 
-void CJX_Object::ScriptSomDefaultValue_Read(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomDefaultValue_Read(v8::Isolate* pIsolate,
+                                            v8::Local<v8::Value>* pValue,
                                             bool bSetting,
                                             XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
   WideString content = GetContent(true);
   if (content.IsEmpty()) {
-    pValue->SetNull();
+    *pValue = fxv8::NewNullHelper(pIsolate);
     return;
   }
-  pValue->SetString(content.ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(pIsolate, content.ToUTF8().AsStringView());
 }
 
-void CJX_Object::ScriptSomDataNode(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomDataNode(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Value>* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
-  CXFA_Node* pDataNode = ToNode(GetXFAObject())->GetBindData();
+  CXFA_Node* pDataNode = GetXFANode()->GetBindData();
   if (!pDataNode) {
-    pValue->SetNull();
+    *pValue = fxv8::NewNullHelper(pIsolate);
     return;
   }
 
-  pValue->Assign(GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-      pDataNode));
+  *pValue =
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pDataNode);
 }
 
-void CJX_Object::ScriptSomMandatory(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomMandatory(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     bool bSetting,
                                     XFA_Attribute eAttribute) {
   CXFA_Validate* validate =
@@ -1457,25 +1386,28 @@
     return;
 
   if (bSetting) {
-    validate->SetNullTest(pValue->ToWideString());
+    validate->SetNullTest(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
     return;
   }
 
-  pValue->SetString(XFA_AttributeValueToName(validate->GetNullTest()));
+  *pValue = fxv8::NewStringHelper(
+      pIsolate, XFA_AttributeValueToName(validate->GetNullTest()));
 }
 
-void CJX_Object::ScriptSomInstanceIndex(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomInstanceIndex(v8::Isolate* pIsolate,
+                                        v8::Local<v8::Value>* pValue,
                                         bool bSetting,
                                         XFA_Attribute eAttribute) {
   if (!bSetting) {
-    pValue->SetInteger(Subform_and_SubformSet_InstanceIndex());
+    *pValue =
+        fxv8::NewNumberHelper(pIsolate, Subform_and_SubformSet_InstanceIndex());
     return;
   }
 
-  int32_t iTo = pValue->ToInteger();
+  int32_t iTo = fxv8::ReentrantToInt32Helper(pIsolate, *pValue);
   int32_t iFrom = Subform_and_SubformSet_InstanceIndex();
   CXFA_Node* pManagerNode = nullptr;
-  for (CXFA_Node* pNode = ToNode(GetXFAObject())->GetPrevSibling(); pNode;
+  for (CXFA_Node* pNode = GetXFANode()->GetPrevSibling(); pNode;
        pNode = pNode->GetPrevSibling()) {
     if (pNode->GetElementType() == XFA_Element::InstanceManager) {
       pManagerNode = pNode;
@@ -1486,23 +1418,31 @@
     return;
 
   auto* mgr = static_cast<CJX_InstanceManager*>(pManagerNode->JSObject());
-  mgr->MoveInstance(iTo, iFrom);
+  mgr->MoveInstance(pIsolate, iTo, iFrom);
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return;
 
-  CXFA_Node* pToInstance = pManagerNode->GetItemIfExists(iTo);
-  if (pToInstance && pToInstance->GetElementType() == XFA_Element::Subform) {
+  auto* pToInstance =
+      CXFA_Subform::FromNode(pManagerNode->GetItemIfExists(iTo));
+  if (pToInstance)
     pNotify->RunSubformIndexChange(pToInstance);
-  }
 
-  CXFA_Node* pFromInstance = pManagerNode->GetItemIfExists(iFrom);
-  if (pFromInstance &&
-      pFromInstance->GetElementType() == XFA_Element::Subform) {
+  auto* pFromInstance =
+      CXFA_Subform::FromNode(pManagerNode->GetItemIfExists(iFrom));
+  if (pFromInstance)
     pNotify->RunSubformIndexChange(pFromInstance);
-  }
 }
 
-void CJX_Object::ScriptSubmitFormatMode(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSubmitFormatMode(v8::Isolate* pIsolate,
+                                        v8::Local<v8::Value>* pValue,
                                         bool bSetting,
                                         XFA_Attribute eAttribute) {}
+
+CJX_Object::CalcData::CalcData() = default;
+
+CJX_Object::CalcData::~CalcData() = default;
+
+void CJX_Object::CalcData::Trace(cppgc::Visitor* visitor) const {
+  ContainerTrace(visitor, m_Globals);
+}
diff --git a/fxjs/xfa/cjx_object.h b/fxjs/xfa/cjx_object.h
index 0633b5f..51a2a13 100644
--- a/fxjs/xfa/cjx_object.h
+++ b/fxjs/xfa/cjx_object.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,53 +9,41 @@
 
 #include <map>
 #include <memory>
-#include <utility>
 #include <vector>
 
-#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxcrt/widestring.h"
+#include "fxjs/gc/heap.h"
+#include "fxjs/xfa/fxjse.h"
 #include "fxjs/xfa/jse_define.h"
-#include "third_party/base/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/abseil-cpp/absl/types/optional.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"
 #include "xfa/fxfa/fxfa_basic.h"
-#include "xfa/fxfa/parser/cxfa_measurement.h"
 
+class CFXJSE_Engine;
+class CFXJSE_MapModule;
 class CFX_XMLElement;
-class CFXJSE_Value;
-class CFX_V8;
 class CJX_Object;
-class CXFA_CalcData;
 class CXFA_Document;
 class CXFA_LayoutItem;
+class CXFA_Measurement;
 class CXFA_Node;
 class CXFA_Object;
-struct XFA_MAPMODULEDATA;
 
-typedef CJS_Result (*CJX_MethodCall)(
-    CJX_Object* obj,
-    CFX_V8* runtime,
-    const std::vector<v8::Local<v8::Value>>& params);
+using CJX_MethodCall =
+    CJS_Result (*)(CJX_Object* obj,
+                   CFXJSE_Engine* runtime,
+                   const std::vector<v8::Local<v8::Value>>& params);
 
 struct CJX_MethodSpec {
   const char* pName;
   CJX_MethodCall pMethodCall;
 };
 
-typedef void (*PD_CALLBACK_FREEDATA)(void* pData);
-typedef void (*PD_CALLBACK_DUPLICATEDATA)(void*& pData);
-
-struct XFA_MAPDATABLOCKCALLBACKINFO {
-  PD_CALLBACK_FREEDATA pFree;
-  PD_CALLBACK_DUPLICATEDATA pCopy;
-};
-
-enum XFA_SOM_MESSAGETYPE {
-  XFA_SOM_ValidationMessage,
-  XFA_SOM_FormatMessage,
-  XFA_SOM_MandatoryMessage
-};
-
-class CJX_Object {
+class CJX_Object : public cppgc::GarbageCollected<CJX_Object>,
+                   public CFXJSE_HostObject {
  public:
   // Corresponds 1:1 with CJX_ subclasses.
   enum class TypeTag {
@@ -96,51 +84,67 @@
     Xfa,
   };
 
-  explicit CJX_Object(CXFA_Object* obj);
-  virtual ~CJX_Object();
+  class CalcData : public cppgc::GarbageCollected<CalcData> {
+   public:
+    CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+    ~CalcData();
 
+    void Trace(cppgc::Visitor* visitor) const;
+
+    std::vector<cppgc::Member<CXFA_Node>> m_Globals;
+
+   private:
+    CalcData();
+  };
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CJX_Object() override;
+
+  // CFXJSE_HostObject:
+  CJX_Object* AsCJXObject() override;
+
+  virtual void Trace(cppgc::Visitor* visitor) const;
   virtual bool DynamicTypeIs(TypeTag eType) const;
 
   JSE_PROP(className);
 
   CXFA_Document* GetDocument() const;
-  CXFA_Object* GetXFAObject() const { return object_.Get(); }
+  CXFA_Node* GetXFANode() const;
+  CXFA_Object* GetXFAObject() const { return object_; }
 
   void SetCalcRecursionCount(size_t count) { calc_recursion_count_ = count; }
   size_t GetCalcRecursionCount() const { return calc_recursion_count_; }
 
-  void SetLayoutItem(CXFA_LayoutItem* item) { layout_item_.Reset(item); }
-  CXFA_LayoutItem* GetLayoutItem() const { return layout_item_.Get(); }
+  void SetLayoutItem(CXFA_LayoutItem* item) { layout_item_ = item; }
+  CXFA_LayoutItem* GetLayoutItem() const { return layout_item_; }
 
   bool HasMethod(const WideString& func) const;
-  CJS_Result RunMethod(const WideString& func,
+  CJS_Result RunMethod(CFXJSE_Engine* pScriptContext,
+                       const WideString& func,
                        const std::vector<v8::Local<v8::Value>>& params);
 
-  bool HasAttribute(XFA_Attribute eAttr);
-  void SetAttribute(XFA_Attribute eAttr, WideStringView wsValue, bool bNotify);
-  void SetAttribute(WideStringView wsAttr,
-                    WideStringView wsValue,
-                    bool bNotify);
+  bool HasAttribute(XFA_Attribute eAttr) const;
+  WideString GetAttributeByString(WideStringView attr) const;
+  WideString GetAttributeByEnum(XFA_Attribute attr) const;
+  absl::optional<WideString> TryAttribute(XFA_Attribute eAttr,
+                                          bool bUseDefault) const;
+  void SetAttributeByEnum(XFA_Attribute eAttr,
+                          const WideString& wsValue,
+                          bool bNotify);
+  void SetAttributeByString(WideStringView wsAttr, const WideString& wsValue);
   void RemoveAttribute(WideStringView wsAttr);
-  WideString GetAttribute(WideStringView attr);
-  WideString GetAttribute(XFA_Attribute attr);
-  Optional<WideString> TryAttribute(WideStringView wsAttr, bool bUseDefault);
-  Optional<WideString> TryAttribute(XFA_Attribute eAttr, bool bUseDefault);
 
-  Optional<WideString> TryContent(bool bScriptModify, bool bProto);
+  WideString GetContent(bool bScriptModify) const;
+  absl::optional<WideString> TryContent(bool bScriptModify, bool bProto) const;
   void SetContent(const WideString& wsContent,
                   const WideString& wsXMLValue,
                   bool bNotify,
                   bool bScriptModify,
                   bool bSyncData);
-  WideString GetContent(bool bScriptModify);
 
   template <typename T>
   T* GetProperty(int32_t index, XFA_Element eType) const {
-    CXFA_Node* node;
-    int32_t count;
-    std::tie(node, count) = GetPropertyInternal(index, eType);
-    return static_cast<T*>(node);
+    return static_cast<T*>(GetPropertyInternal(index, eType));
   }
   template <typename T>
   T* GetOrCreateProperty(int32_t index, XFA_Element eType) {
@@ -148,9 +152,7 @@
   }
 
   void SetAttributeValue(const WideString& wsValue,
-                         const WideString& wsXMLValue,
-                         bool bNotify,
-                         bool bScriptModify);
+                         const WideString& wsXMLValue);
 
   // Not actual properties, but invoked as property handlers to cover
   // a broad range of underlying properties.
@@ -170,105 +172,113 @@
   JSE_PROP(ScriptSomInstanceIndex);
   JSE_PROP(ScriptSubmitFormatMode);
 
-  void ScriptSomMessage(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_SOM_MESSAGETYPE iMessageType);
+  absl::optional<WideString> TryNamespace() const;
 
-  Optional<WideString> TryNamespace();
-
-  Optional<int32_t> TryInteger(XFA_Attribute eAttr, bool bUseDefault) const;
-  void SetInteger(XFA_Attribute eAttr, int32_t iValue, bool bNotify);
   int32_t GetInteger(XFA_Attribute eAttr) const;
+  absl::optional<int32_t> TryInteger(XFA_Attribute eAttr,
+                                     bool bUseDefault) const;
+  void SetInteger(XFA_Attribute eAttr, int32_t iValue, bool bNotify);
 
-  Optional<WideString> TryCData(XFA_Attribute eAttr, bool bUseDefault) const;
-  void SetCData(XFA_Attribute eAttr,
-                const WideString& wsValue,
-                bool bNotify,
-                bool bScriptModify);
   WideString GetCData(XFA_Attribute eAttr) const;
+  absl::optional<WideString> TryCData(XFA_Attribute eAttr,
+                                      bool bUseDefault) const;
+  void SetCData(XFA_Attribute eAttr, const WideString& wsValue);
 
-  Optional<XFA_AttributeValue> TryEnum(XFA_Attribute eAttr,
-                                       bool bUseDefault) const;
-  void SetEnum(XFA_Attribute eAttr, XFA_AttributeValue eValue, bool bNotify);
   XFA_AttributeValue GetEnum(XFA_Attribute eAttr) const;
+  absl::optional<XFA_AttributeValue> TryEnum(XFA_Attribute eAttr,
+                                             bool bUseDefault) const;
+  void SetEnum(XFA_Attribute eAttr, XFA_AttributeValue eValue, bool bNotify);
 
-  Optional<bool> TryBoolean(XFA_Attribute eAttr, bool bUseDefault);
+  bool GetBoolean(XFA_Attribute eAttr) const;
+  absl::optional<bool> TryBoolean(XFA_Attribute eAttr, bool bUseDefault) const;
   void SetBoolean(XFA_Attribute eAttr, bool bValue, bool bNotify);
-  bool GetBoolean(XFA_Attribute eAttr);
 
-  Optional<CXFA_Measurement> TryMeasure(XFA_Attribute eAttr,
-                                        bool bUseDefault) const;
-  Optional<float> TryMeasureAsFloat(XFA_Attribute attr) const;
-  void SetMeasure(XFA_Attribute eAttr, CXFA_Measurement mValue, bool bNotify);
   CXFA_Measurement GetMeasure(XFA_Attribute eAttr) const;
   float GetMeasureInUnit(XFA_Attribute eAttr, XFA_Unit unit) const;
+  absl::optional<CXFA_Measurement> TryMeasure(XFA_Attribute eAttr,
+                                              bool bUseDefault) const;
+  absl::optional<float> TryMeasureAsFloat(XFA_Attribute attr) const;
+  void SetMeasure(XFA_Attribute eAttr,
+                  const CXFA_Measurement& mValue,
+                  bool bNotify);
 
-  void MergeAllData(CXFA_Object* pDstModule);
+  void MergeAllData(CXFA_Object* pDstObj);
 
-  void SetCalcData(std::unique_ptr<CXFA_CalcData> data);
-  CXFA_CalcData* GetCalcData() const { return calc_data_.get(); }
-  std::unique_ptr<CXFA_CalcData> ReleaseCalcData();
+  CalcData* GetCalcData() const { return calc_data_; }
+  CalcData* GetOrCreateCalcData(cppgc::Heap* heap);
+  void TakeCalcDataFrom(CJX_Object* that);
 
-  int32_t InstanceManager_SetInstances(int32_t iDesired);
-  int32_t InstanceManager_MoveInstance(int32_t iTo, int32_t iFrom);
-
-  void ThrowInvalidPropertyException() const;
-  void ThrowArgumentMismatchException() const;
-  void ThrowIndexOutOfBoundsException() const;
-  void ThrowParamCountMismatchException(const WideString& method) const;
-  void ThrowTooManyOccurancesException(const WideString& obj) const;
+  void ThrowInvalidPropertyException(v8::Isolate* pIsolate) const;
+  void ThrowArgumentMismatchException(v8::Isolate* pIsolate) const;
+  void ThrowIndexOutOfBoundsException(v8::Isolate* pIsolate) const;
+  void ThrowParamCountMismatchException(v8::Isolate* pIsolate,
+                                        const WideString& method) const;
+  void ThrowTooManyOccurrencesException(v8::Isolate* pIsolate,
+                                        const WideString& obj) const;
 
  protected:
+  enum class SOMMessageType {
+    kValidationMessage,
+    kFormatMessage,
+    kMandatoryMessage
+  };
+
+  explicit CJX_Object(CXFA_Object* obj);
+
+  void ScriptSomMessage(v8::Isolate* pIsolate,
+                        v8::Local<v8::Value>* pValue,
+                        bool bSetting,
+                        SOMMessageType iMessageType);
+  void SetAttributeValueImpl(const WideString& wsValue,
+                             const WideString& wsXMLValue,
+                             bool bNotify,
+                             bool bScriptModify);
+  void SetCDataImpl(XFA_Attribute eAttr,
+                    const WideString& wsValue,
+                    bool bNotify,
+                    bool bScriptModify);
   void DefineMethods(pdfium::span<const CJX_MethodSpec> methods);
-  void MoveBufferMapData(CXFA_Object* pSrcModule, CXFA_Object* pDstModule);
-  void SetMapModuleString(void* pKey, WideStringView wsValue);
-  void ThrowException(const WideString& str) const;
+  void MoveBufferMapData(CXFA_Object* pSrcObj, CXFA_Object* pDstObj);
+  void ThrowException(v8::Isolate* pIsolate, const WideString& str) const;
 
  private:
   using Type__ = CJX_Object;
   static const TypeTag static_type__ = TypeTag::Object;
 
-  std::pair<CXFA_Node*, int32_t> GetPropertyInternal(int32_t index,
-                                                     XFA_Element eType) const;
+  CXFA_Node* GetPropertyInternal(int32_t index, XFA_Element eType) const;
   CXFA_Node* GetOrCreatePropertyInternal(int32_t index, XFA_Element eType);
 
-  void OnChanged(XFA_Attribute eAttr, bool bNotify, bool bScriptModify);
-  void OnChanging(XFA_Attribute eAttr, bool bNotify);
-  void SetUserData(void* pKey,
-                   void* pData,
-                   const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo);
+  void OnChanging(XFA_Attribute eAttr);
+  void OnChanged(XFA_Attribute eAttr, bool bScriptModify);
 
   // Returns a pointer to the XML node that needs to be updated with the new
   // attribute value. |nullptr| if no update is needed.
-  CFX_XMLElement* SetValue(XFA_Attribute eAttr, void* pValue, bool bNotify);
+  CFX_XMLElement* SetValue(XFA_Attribute eAttr, int32_t value, bool bNotify);
   int32_t Subform_and_SubformSet_InstanceIndex();
 
-  XFA_MAPMODULEDATA* CreateMapModuleData();
-  XFA_MAPMODULEDATA* GetMapModuleData() const;
-  void SetMapModuleValue(void* pKey, void* pValue);
-  Optional<void*> GetMapModuleValue(void* pKey) const;
-  Optional<WideString> GetMapModuleString(void* pKey) const;
-  void SetMapModuleBuffer(void* pKey,
-                          void* pValue,
-                          int32_t iBytes,
-                          const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo);
-  bool GetMapModuleBuffer(void* pKey, void** pValue, int32_t* pBytes) const;
-  bool HasMapModuleKey(void* pKey);
-  void ClearMapModuleBuffer();
-  void RemoveMapModuleKey(void* pKey);
-  void MoveBufferMapData(CXFA_Object* pDstModule);
+  CFXJSE_MapModule* CreateMapModule();
+  CFXJSE_MapModule* GetMapModule() const;
+  void SetMapModuleValue(uint32_t key, int32_t value);
+  void SetMapModuleString(uint32_t key, const WideString& wsValue);
+  void SetMapModuleMeasurement(uint32_t key, const CXFA_Measurement& value);
+  absl::optional<int32_t> GetMapModuleValue(uint32_t key) const;
+  absl::optional<WideString> GetMapModuleString(uint32_t key) const;
+  absl::optional<CXFA_Measurement> GetMapModuleMeasurement(uint32_t key) const;
+  absl::optional<int32_t> GetMapModuleValueFollowingChain(uint32_t key) const;
+  absl::optional<WideString> GetMapModuleStringFollowingChain(
+      uint32_t key) const;
+  absl::optional<CXFA_Measurement> GetMapModuleMeasurementFollowingChain(
+      uint32_t key) const;
+  bool HasMapModuleKey(uint32_t key) const;
+  void RemoveMapModuleKey(uint32_t key);
+  void MoveBufferMapData(CXFA_Object* pDstObj);
 
-  UnownedPtr<CXFA_Object> object_;
-  UnownedPtr<CXFA_LayoutItem> layout_item_;
-  std::unique_ptr<XFA_MAPMODULEDATA> map_module_data_;
-  std::unique_ptr<CXFA_CalcData> calc_data_;
+  cppgc::Member<CXFA_Object> object_;
+  cppgc::Member<CXFA_LayoutItem> layout_item_;
+  cppgc::Member<CalcData> calc_data_;
+  std::unique_ptr<CFXJSE_MapModule> map_module_;
   std::map<ByteString, CJX_MethodCall> method_specs_;
   size_t calc_recursion_count_ = 0;
 };
 
-typedef void (*XFA_ATTRIBUTE_CALLBACK)(CJX_Object* pNode,
-                                       CFXJSE_Value* pValue,
-                                       bool bSetting,
-                                       XFA_Attribute eAttribute);
-
 #endif  // FXJS_XFA_CJX_OBJECT_H_
diff --git a/fxjs/xfa/cjx_object_embeddertest.cpp b/fxjs/xfa/cjx_object_embeddertest.cpp
new file mode 100644
index 0000000..b433665
--- /dev/null
+++ b/fxjs/xfa/cjx_object_embeddertest.cpp
@@ -0,0 +1,20 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/xfa/cjx_object.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/xfa_js_embedder_test.h"
+
+class CJX_ObjectEmbedderTest : public XFAJSEmbedderTest {};
+
+// Should not crash, but document is not valid.
+TEST_F(CJX_ObjectEmbedderTest, Bug1327884) {
+  ASSERT_FALSE(OpenDocument("bug_1327884.pdf"));
+}
+
+// Should not CHECK(), but document is uninteresting.
+TEST_F(CJX_ObjectEmbedderTest, Bug1333298) {
+  ASSERT_TRUE(OpenDocument("bug_1333298.pdf"));
+}
diff --git a/fxjs/xfa/cjx_occur.cpp b/fxjs/xfa/cjx_occur.cpp
index de8942d..657b230 100644
--- a/fxjs/xfa/cjx_occur.cpp
+++ b/fxjs/xfa/cjx_occur.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,9 @@
 
 #include "fxjs/xfa/cjx_occur.h"
 
-#include <algorithm>
-
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_occur.h"
 
 CJX_Occur::CJX_Occur(CXFA_Occur* node) : CJX_Node(node) {}
@@ -19,24 +19,27 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_Occur::max(CFXJSE_Value* pValue,
+void CJX_Occur::max(v8::Isolate* pIsolate,
+                    v8::Local<v8::Value>* pValue,
                     bool bSetting,
                     XFA_Attribute eAttribute) {
   CXFA_Occur* occur = static_cast<CXFA_Occur*>(GetXFANode());
   if (!bSetting) {
-    pValue->SetInteger(occur->GetMax());
+    *pValue = fxv8::NewNumberHelper(pIsolate, occur->GetMax());
     return;
   }
-  occur->SetMax(pValue->ToInteger());
+  occur->SetMax(fxv8::ReentrantToInt32Helper(pIsolate, *pValue));
 }
 
-void CJX_Occur::min(CFXJSE_Value* pValue,
+// NOLINTNEXTLINE(build/include_what_you_use)
+void CJX_Occur::min(v8::Isolate* pIsolate,
+                    v8::Local<v8::Value>* pValue,
                     bool bSetting,
                     XFA_Attribute eAttribute) {
   CXFA_Occur* occur = static_cast<CXFA_Occur*>(GetXFANode());
   if (!bSetting) {
-    pValue->SetInteger(occur->GetMin());
+    *pValue = fxv8::NewNumberHelper(pIsolate, occur->GetMin());
     return;
   }
-  occur->SetMin(pValue->ToInteger());
+  occur->SetMin(fxv8::ReentrantToInt32Helper(pIsolate, *pValue));
 }
diff --git a/fxjs/xfa/cjx_occur.h b/fxjs/xfa/cjx_occur.h
index 8b912b6..06887dd 100644
--- a/fxjs/xfa/cjx_occur.h
+++ b/fxjs/xfa/cjx_occur.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Occur final : public CJX_Node {
  public:
-  explicit CJX_Occur(CXFA_Occur* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Occur() override;
 
   // CJX_Object:
@@ -24,6 +24,8 @@
   JSE_PROP(min);
 
  private:
+  explicit CJX_Occur(CXFA_Occur* node);
+
   using Type__ = CJX_Occur;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_packet.cpp b/fxjs/xfa/cjx_packet.cpp
index 2c8b67d..e77f165 100644
--- a/fxjs/xfa/cjx_packet.cpp
+++ b/fxjs/xfa/cjx_packet.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,10 +10,12 @@
 #include <vector>
 
 #include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmltext.h"
 #include "fxjs/cfx_v8.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cxfa_packet.h"
@@ -27,14 +29,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Packet::~CJX_Packet() {}
+CJX_Packet::~CJX_Packet() = default;
 
 bool CJX_Packet::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Packet::getAttribute(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -49,7 +51,7 @@
 }
 
 CJS_Result CJX_Packet::setAttribute(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -63,21 +65,20 @@
 }
 
 CJS_Result CJX_Packet::removeAttribute(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
   CFX_XMLElement* pElement = ToXMLElement(GetXFANode()->GetXMLMappingNode());
-  if (pElement) {
-    WideString name = runtime->ToWideString(params[0]);
-    if (pElement->HasAttribute(name))
-      pElement->RemoveAttribute(name);
-  }
+  if (pElement)
+    pElement->RemoveAttribute(runtime->ToWideString(params[0]));
+
   return CJS_Result::Success(runtime->NewNull());
 }
 
-void CJX_Packet::content(CFXJSE_Value* pValue,
+void CJX_Packet::content(v8::Isolate* pIsolate,
+                         v8::Local<v8::Value>* pValue,
                          bool bSetting,
                          XFA_Attribute eAttribute) {
   CFX_XMLElement* element = ToXMLElement(GetXFANode()->GetXMLMappingNode());
@@ -87,9 +88,10 @@
           GetXFANode()
               ->GetDocument()
               ->GetNotify()
-              ->GetHDOC()
+              ->GetFFDoc()
               ->GetXMLDocument()
-              ->CreateNode<CFX_XMLText>(pValue->ToWideString()));
+              ->CreateNode<CFX_XMLText>(
+                  fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)));
     }
     return;
   }
@@ -98,5 +100,5 @@
   if (element)
     wsTextData = element->GetTextData();
 
-  pValue->SetString(wsTextData.ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(pIsolate, wsTextData.ToUTF8().AsStringView());
 }
diff --git a/fxjs/xfa/cjx_packet.h b/fxjs/xfa/cjx_packet.h
index df4a987..4b4687c 100644
--- a/fxjs/xfa/cjx_packet.h
+++ b/fxjs/xfa/cjx_packet.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Packet final : public CJX_Node {
  public:
-  explicit CJX_Packet(CXFA_Packet* packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Packet() override;
 
   // CJX_Object:
@@ -27,6 +27,8 @@
   JSE_PROP(content);
 
  private:
+  explicit CJX_Packet(CXFA_Packet* packet);
+
   using Type__ = CJX_Packet;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_script.cpp b/fxjs/xfa/cjx_script.cpp
index 482a57e..2c02b2b 100644
--- a/fxjs/xfa/cjx_script.cpp
+++ b/fxjs/xfa/cjx_script.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,8 @@
 
 #include "fxjs/xfa/cjx_script.h"
 
-#include "fxjs/xfa/cfxjse_value.h"
+#include "fxjs/fxv8.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_script.h"
 
 CJX_Script::CJX_Script(CXFA_Script* node) : CJX_Node(node) {}
@@ -17,12 +18,13 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_Script::stateless(CFXJSE_Value* pValue,
+void CJX_Script::stateless(v8::Isolate* pIsolate,
+                           v8::Local<v8::Value>* pValue,
                            bool bSetting,
                            XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->SetString(FX_UTF8Encode(WideStringView(L"0", 1)).AsStringView());
+  *pValue = fxv8::NewStringHelper(pIsolate, "0");
 }
diff --git a/fxjs/xfa/cjx_script.h b/fxjs/xfa/cjx_script.h
index af7c985..272ec2a 100644
--- a/fxjs/xfa/cjx_script.h
+++ b/fxjs/xfa/cjx_script.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Script final : public CJX_Node {
  public:
-  explicit CJX_Script(CXFA_Script* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Script() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_PROP(stateless);
 
  private:
+  explicit CJX_Script(CXFA_Script* node);
+
   using Type__ = CJX_Script;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_signaturepseudomodel.cpp b/fxjs/xfa/cjx_signaturepseudomodel.cpp
index 97ecd6e..a740cb8 100644
--- a/fxjs/xfa/cjx_signaturepseudomodel.cpp
+++ b/fxjs/xfa/cjx_signaturepseudomodel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,9 +8,10 @@
 
 #include <vector>
 
-#include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cscript_signaturepseudomodel.h"
 
 const CJX_MethodSpec CJX_SignaturePseudoModel::MethodSpecs[] = {
@@ -25,14 +26,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_SignaturePseudoModel::~CJX_SignaturePseudoModel() {}
+CJX_SignaturePseudoModel::~CJX_SignaturePseudoModel() = default;
 
 bool CJX_SignaturePseudoModel::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_SignaturePseudoModel::verifySignature(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 4)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -41,7 +42,7 @@
 }
 
 CJS_Result CJX_SignaturePseudoModel::sign(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() < 3 || params.size() > 7)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -50,7 +51,7 @@
 }
 
 CJS_Result CJX_SignaturePseudoModel::enumerate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -59,7 +60,7 @@
 }
 
 CJS_Result CJX_SignaturePseudoModel::clear(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 2)
     return CJS_Result::Failure(JSMessage::kParamError);
diff --git a/fxjs/xfa/cjx_signaturepseudomodel.h b/fxjs/xfa/cjx_signaturepseudomodel.h
index ed77238..6cf3460 100644
--- a/fxjs/xfa/cjx_signaturepseudomodel.h
+++ b/fxjs/xfa/cjx_signaturepseudomodel.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_SignaturePseudoModel final : public CJX_Object {
  public:
-  explicit CJX_SignaturePseudoModel(CScript_SignaturePseudoModel* model);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_SignaturePseudoModel() override;
 
   // CJX_Object:
@@ -26,6 +26,8 @@
   JSE_METHOD(clear);
 
  private:
+  explicit CJX_SignaturePseudoModel(CScript_SignaturePseudoModel* model);
+
   using Type__ = CJX_SignaturePseudoModel;
   using ParentType__ = CJX_Object;
 
diff --git a/fxjs/xfa/cjx_source.cpp b/fxjs/xfa/cjx_source.cpp
index f6d3187..33a11ff 100644
--- a/fxjs/xfa/cjx_source.cpp
+++ b/fxjs/xfa/cjx_source.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -35,13 +35,13 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Source::~CJX_Source() {}
+CJX_Source::~CJX_Source() = default;
 
 bool CJX_Source::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-CJS_Result CJX_Source::next(CFX_V8* runtime,
+CJS_Result CJX_Source::next(CFXJSE_Engine* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -50,7 +50,7 @@
 }
 
 CJS_Result CJX_Source::cancelBatch(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -58,7 +58,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::first(CFX_V8* runtime,
+CJS_Result CJX_Source::first(CFXJSE_Engine* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -67,7 +67,7 @@
 }
 
 CJS_Result CJX_Source::updateBatch(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -76,7 +76,7 @@
 }
 
 CJS_Result CJX_Source::previous(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -84,7 +84,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::isBOF(CFX_V8* runtime,
+CJS_Result CJX_Source::isBOF(CFXJSE_Engine* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -92,7 +92,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::isEOF(CFX_V8* runtime,
+CJS_Result CJX_Source::isEOF(CFXJSE_Engine* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -100,7 +100,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::cancel(CFX_V8* runtime,
+CJS_Result CJX_Source::cancel(CFXJSE_Engine* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -108,7 +108,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::update(CFX_V8* runtime,
+CJS_Result CJX_Source::update(CFXJSE_Engine* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -116,7 +116,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::open(CFX_V8* runtime,
+CJS_Result CJX_Source::open(CFXJSE_Engine* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -125,7 +125,7 @@
 }
 
 CJS_Result CJX_Source::deleteItem(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -133,7 +133,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::addNew(CFX_V8* runtime,
+CJS_Result CJX_Source::addNew(CFXJSE_Engine* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -142,7 +142,7 @@
 }
 
 CJS_Result CJX_Source::requery(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -150,7 +150,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::resync(CFX_V8* runtime,
+CJS_Result CJX_Source::resync(CFXJSE_Engine* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -158,7 +158,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::close(CFX_V8* runtime,
+CJS_Result CJX_Source::close(CFXJSE_Engine* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -166,7 +166,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::last(CFX_V8* runtime,
+CJS_Result CJX_Source::last(CFXJSE_Engine* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -175,7 +175,7 @@
 }
 
 CJS_Result CJX_Source::hasDataChanged(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -183,6 +183,7 @@
   return CJS_Result::Success();
 }
 
-void CJX_Source::db(CFXJSE_Value* pValue,
+void CJX_Source::db(v8::Isolate* pIsolate,
+                    v8::Local<v8::Value>* pValue,
                     bool bSetting,
                     XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_source.h b/fxjs/xfa/cjx_source.h
index cd70601..a3e7fda 100644
--- a/fxjs/xfa/cjx_source.h
+++ b/fxjs/xfa/cjx_source.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Source final : public CJX_Node {
  public:
-  explicit CJX_Source(CXFA_Source* src);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Source() override;
 
   // CJX_Object:
@@ -41,6 +41,8 @@
   JSE_PROP(db);
 
  private:
+  explicit CJX_Source(CXFA_Source* src);
+
   using Type__ = CJX_Source;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_subform.cpp b/fxjs/xfa/cjx_subform.cpp
index 8a72b5b..5a4efc0 100644
--- a/fxjs/xfa/cjx_subform.cpp
+++ b/fxjs/xfa/cjx_subform.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,9 +9,10 @@
 #include <vector>
 
 #include "fxjs/cfx_v8.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/fxfa.h"
@@ -28,14 +29,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Subform::~CJX_Subform() {}
+CJX_Subform::~CJX_Subform() = default;
 
 bool CJX_Subform::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Subform::execEvent(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -46,7 +47,7 @@
 }
 
 CJS_Result CJX_Subform::execInitialize(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -59,7 +60,7 @@
 }
 
 CJS_Result CJX_Subform::execCalculate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -72,7 +73,7 @@
 }
 
 CJS_Result CJX_Subform::execValidate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -87,29 +88,34 @@
       runtime->NewBoolean(iRet != XFA_EventError::kError));
 }
 
-void CJX_Subform::locale(CFXJSE_Value* pValue,
+void CJX_Subform::locale(v8::Isolate* pIsolate,
+                         v8::Local<v8::Value>* pValue,
                          bool bSetting,
                          XFA_Attribute eAttribute) {
   if (bSetting) {
-    SetCData(XFA_Attribute::Locale, pValue->ToWideString(), true, true);
+    SetCDataImpl(XFA_Attribute::Locale,
+                 fxv8::ReentrantToWideStringHelper(pIsolate, *pValue), true,
+                 true);
     return;
   }
 
   WideString wsLocaleName = GetXFANode()->GetLocaleName().value_or(L"");
-  pValue->SetString(wsLocaleName.ToUTF8().AsStringView());
+  *pValue =
+      fxv8::NewStringHelper(pIsolate, wsLocaleName.ToUTF8().AsStringView());
 }
 
-void CJX_Subform::instanceManager(CFXJSE_Value* pValue,
+void CJX_Subform::instanceManager(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
   WideString wsName = GetCData(XFA_Attribute::Name);
   CXFA_Node* pInstanceMgr = nullptr;
-  for (CXFA_Node* pNode = ToNode(GetXFAObject())->GetPrevSibling(); pNode;
+  for (CXFA_Node* pNode = GetXFANode()->GetPrevSibling(); pNode;
        pNode = pNode->GetPrevSibling()) {
     if (pNode->GetElementType() == XFA_Element::InstanceManager) {
       WideString wsInstMgrName =
@@ -121,11 +127,9 @@
       break;
     }
   }
-  if (!pInstanceMgr) {
-    pValue->SetNull();
-    return;
-  }
-
-  pValue->Assign(GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-      pInstanceMgr));
+  *pValue = pInstanceMgr ? GetDocument()
+                               ->GetScriptContext()
+                               ->GetOrCreateJSBindingFromMap(pInstanceMgr)
+                               .As<v8::Value>()
+                         : fxv8::NewNullHelper(pIsolate).As<v8::Value>();
 }
diff --git a/fxjs/xfa/cjx_subform.h b/fxjs/xfa/cjx_subform.h
index 83ac66e..c33712f 100644
--- a/fxjs/xfa/cjx_subform.h
+++ b/fxjs/xfa/cjx_subform.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,11 +10,11 @@
 #include "fxjs/xfa/cjx_container.h"
 #include "fxjs/xfa/jse_define.h"
 
-class CXFA_Delta;
+class CXFA_Node;
 
 class CJX_Subform final : public CJX_Container {
  public:
-  explicit CJX_Subform(CXFA_Node* container);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Subform() override;
 
   // CJX_Object:
@@ -29,6 +29,8 @@
   JSE_PROP(locale);
 
  private:
+  explicit CJX_Subform(CXFA_Node* container);
+
   using Type__ = CJX_Subform;
   using ParentType__ = CJX_Container;
 
diff --git a/fxjs/xfa/cjx_template.cpp b/fxjs/xfa/cjx_template.cpp
index b3d9d90..e3779e3 100644
--- a/fxjs/xfa/cjx_template.cpp
+++ b/fxjs/xfa/cjx_template.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 #include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_template.h"
 
@@ -26,14 +27,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Template::~CJX_Template() {}
+CJX_Template::~CJX_Template() = default;
 
 bool CJX_Template::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Template::formNodes(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -42,17 +43,17 @@
 }
 
 CJS_Result CJX_Template::remerge(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  GetDocument()->DoDataRemerge(true);
+  GetDocument()->DoDataRemerge();
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_Template::execInitialize(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -62,7 +63,7 @@
 }
 
 CJS_Result CJX_Template::recalculate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -71,7 +72,7 @@
 }
 
 CJS_Result CJX_Template::execCalculate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -81,7 +82,7 @@
 }
 
 CJS_Result CJX_Template::execValidate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
diff --git a/fxjs/xfa/cjx_template.h b/fxjs/xfa/cjx_template.h
index d396459..caf1b74 100644
--- a/fxjs/xfa/cjx_template.h
+++ b/fxjs/xfa/cjx_template.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Template final : public CJX_Model {
  public:
-  explicit CJX_Template(CXFA_Template* tmpl);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Template() override;
 
   // CJX_Object:
@@ -31,6 +31,8 @@
   JSE_METHOD(remerge);
 
  private:
+  explicit CJX_Template(CXFA_Template* tmpl);
+
   using Type__ = CJX_Template;
   using ParentType__ = CJX_Model;
 
diff --git a/fxjs/xfa/cjx_textnode.cpp b/fxjs/xfa/cjx_textnode.cpp
index 756a71d..2bdb414 100644
--- a/fxjs/xfa/cjx_textnode.cpp
+++ b/fxjs/xfa/cjx_textnode.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,20 +11,22 @@
 
 CJX_TextNode::CJX_TextNode(CXFA_Node* node) : CJX_Node(node) {}
 
-CJX_TextNode::~CJX_TextNode() {}
+CJX_TextNode::~CJX_TextNode() = default;
 
 bool CJX_TextNode::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_TextNode::defaultValue(CFXJSE_Value* pValue,
+void CJX_TextNode::defaultValue(v8::Isolate* pIsolate,
+                                v8::Local<v8::Value>* pValue,
                                 bool bSetting,
                                 XFA_Attribute attr) {
-  ScriptSomDefaultValue(pValue, bSetting, attr);
+  ScriptSomDefaultValue(pIsolate, pValue, bSetting, attr);
 }
 
-void CJX_TextNode::value(CFXJSE_Value* pValue,
+void CJX_TextNode::value(v8::Isolate* pIsolate,
+                         v8::Local<v8::Value>* pValue,
                          bool bSetting,
                          XFA_Attribute attr) {
-  ScriptSomDefaultValue(pValue, bSetting, attr);
+  ScriptSomDefaultValue(pIsolate, pValue, bSetting, attr);
 }
diff --git a/fxjs/xfa/cjx_textnode.h b/fxjs/xfa/cjx_textnode.h
index 6d74658..dbef713 100644
--- a/fxjs/xfa/cjx_textnode.h
+++ b/fxjs/xfa/cjx_textnode.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_TextNode : public CJX_Node {
  public:
-  explicit CJX_TextNode(CXFA_Node* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_TextNode() override;
 
   // CJX_Object:
@@ -23,6 +23,9 @@
   JSE_PROP(defaultValue); /* {default} */
   JSE_PROP(value);
 
+ protected:
+  explicit CJX_TextNode(CXFA_Node* node);
+
  private:
   using Type__ = CJX_TextNode;
   using ParentType__ = CJX_Node;
diff --git a/fxjs/xfa/cjx_tree.cpp b/fxjs/xfa/cjx_tree.cpp
index cc13b7a..daabd09 100644
--- a/fxjs/xfa/cjx_tree.cpp
+++ b/fxjs/xfa/cjx_tree.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,16 +8,19 @@
 
 #include <vector>
 
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_class.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/cppgc/allocation.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_arraynodelist.h"
 #include "xfa/fxfa/parser/cxfa_attachnodelist.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 
 const CJX_MethodSpec CJX_Tree::MethodSpecs[] = {
     {"resolveNode", resolveNode_static},
@@ -27,207 +30,222 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Tree::~CJX_Tree() {}
+CJX_Tree::~CJX_Tree() = default;
 
 bool CJX_Tree::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Tree::resolveNode(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  WideString expression = runtime->ToWideString(params[0]);
-  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  CXFA_Object* refNode = GetXFAObject();
-  if (refNode->GetElementType() == XFA_Element::Xfa)
-    refNode = pScriptContext->GetThisObject();
+  WideString wsExpression = runtime->ToWideString(params[0]);
+  CXFA_Object* pRefNode = GetXFAObject();
+  if (pRefNode->GetElementType() == XFA_Element::Xfa)
+    pRefNode = runtime->GetThisObject();
 
-  uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
-                    XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent |
-                    XFA_RESOLVENODE_Siblings;
-  XFA_RESOLVENODE_RS resolveNodeRS;
-  if (!pScriptContext->ResolveObjects(ToNode(refNode),
-                                      expression.AsStringView(), &resolveNodeRS,
-                                      dwFlag, nullptr)) {
+  absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+      runtime->ResolveObjects(
+          ToNode(pRefNode), wsExpression.AsStringView(),
+          Mask<XFA_ResolveFlag>{
+              XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes,
+              XFA_ResolveFlag::kProperties, XFA_ResolveFlag::kParent,
+              XFA_ResolveFlag::kSiblings});
+  if (!maybeResult.has_value())
+    return CJS_Result::Success(runtime->NewNull());
+
+  if (maybeResult.value().type == CFXJSE_Engine::ResolveResult::Type::kNodes) {
+    return CJS_Result::Success(runtime->GetOrCreateJSBindingFromMap(
+        maybeResult.value().objects.front().Get()));
+  }
+
+  if (!maybeResult.value().script_attribute.callback ||
+      maybeResult.value().script_attribute.eValueType !=
+          XFA_ScriptType::Object) {
     return CJS_Result::Success(runtime->NewNull());
   }
 
-  if (resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    CXFA_Object* pObject = resolveNodeRS.objects.front().Get();
-    CFXJSE_Value* value =
-        GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pObject);
-
-    return CJS_Result::Success(
-        value->DirectGetValue().Get(runtime->GetIsolate()));
-  }
-
-  if (!resolveNodeRS.script_attribute.callback ||
-      resolveNodeRS.script_attribute.eValueType != XFA_ScriptType::Object) {
-    return CJS_Result::Success(runtime->NewNull());
-  }
-
-  auto pValue = pdfium::MakeUnique<CFXJSE_Value>(pScriptContext->GetIsolate());
-  CJX_Object* jsObject = resolveNodeRS.objects.front()->JSObject();
-  (*resolveNodeRS.script_attribute.callback)(
-      jsObject, pValue.get(), false, resolveNodeRS.script_attribute.attribute);
-  return CJS_Result::Success(
-      pValue->DirectGetValue().Get(runtime->GetIsolate()));
+  v8::Local<v8::Value> pValue;
+  CJX_Object* jsObject = maybeResult.value().objects.front()->JSObject();
+  (*maybeResult.value().script_attribute.callback)(
+      runtime->GetIsolate(), jsObject, &pValue, false,
+      maybeResult.value().script_attribute.attribute);
+  return CJS_Result::Success(pValue);
 }
 
 CJS_Result CJX_Tree::resolveNodes(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_Object* refNode = GetXFAObject();
   if (refNode->GetElementType() == XFA_Element::Xfa)
-    refNode = GetDocument()->GetScriptContext()->GetThisObject();
+    refNode = runtime->GetThisObject();
 
-  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  auto pValue = pdfium::MakeUnique<CFXJSE_Value>(pScriptContext->GetIsolate());
-  ResolveNodeList(pValue.get(), runtime->ToWideString(params[0]),
-                  XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
-                      XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent |
-                      XFA_RESOLVENODE_Siblings,
-                  ToNode(refNode));
-  return CJS_Result::Success(
-      pValue->DirectGetValue().Get(runtime->GetIsolate()));
+  const Mask<XFA_ResolveFlag> kFlags = {
+      XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes,
+      XFA_ResolveFlag::kProperties, XFA_ResolveFlag::kParent,
+      XFA_ResolveFlag::kSiblings};
+  return CJS_Result::Success(ResolveNodeList(runtime->GetIsolate(),
+                                             runtime->ToWideString(params[0]),
+                                             kFlags, ToNode(refNode)));
 }
 
-void CJX_Tree::all(CFXJSE_Value* pValue,
+void CJX_Tree::all(v8::Isolate* pIsolate,
+                   v8::Local<v8::Value>* pValue,
                    bool bSetting,
                    XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-
-  uint32_t dwFlag = XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_ALL;
-  WideString wsExpression = GetAttribute(XFA_Attribute::Name) + L"[*]";
-  ResolveNodeList(pValue, wsExpression, dwFlag, nullptr);
+  const Mask<XFA_ResolveFlag> kFlags = {XFA_ResolveFlag::kSiblings,
+                                        XFA_ResolveFlag::kALL};
+  WideString wsExpression = GetAttributeByEnum(XFA_Attribute::Name) + L"[*]";
+  *pValue = ResolveNodeList(pIsolate, wsExpression, kFlags, nullptr);
 }
 
-void CJX_Tree::classAll(CFXJSE_Value* pValue,
+void CJX_Tree::classAll(v8::Isolate* pIsolate,
+                        v8::Local<v8::Value>* pValue,
                         bool bSetting,
                         XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-
+  const Mask<XFA_ResolveFlag> kFlags = {XFA_ResolveFlag::kSiblings,
+                                        XFA_ResolveFlag::kALL};
   WideString wsExpression =
       L"#" + WideString::FromASCII(GetXFAObject()->GetClassName()) + L"[*]";
-  ResolveNodeList(pValue, wsExpression,
-                  XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_ALL, nullptr);
+  *pValue = ResolveNodeList(pIsolate, wsExpression, kFlags, nullptr);
 }
 
-void CJX_Tree::nodes(CFXJSE_Value* pValue,
+void CJX_Tree::nodes(v8::Isolate* pIsolate,
+                     v8::Local<v8::Value>* pValue,
                      bool bSetting,
                      XFA_Attribute eAttribute) {
   if (bSetting) {
     WideString wsMessage = L"Unable to set ";
-    FXJSE_ThrowMessage(wsMessage.ToUTF8().AsStringView());
+    FXJSE_ThrowMessage(pIsolate, wsMessage.ToUTF8().AsStringView());
     return;
   }
 
-  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  CXFA_AttachNodeList* pNodeList =
-      new CXFA_AttachNodeList(GetDocument(), ToNode(GetXFAObject()));
-  pValue->SetHostObject(pNodeList, pScriptContext->GetJseNormalClass());
+  CXFA_Document* pDoc = GetDocument();
+  auto* pNodeList = cppgc::MakeGarbageCollected<CXFA_AttachNodeList>(
+      pDoc->GetHeap()->GetAllocationHandle(), pDoc, GetXFANode());
+  pDoc->GetNodeOwner()->PersistList(pNodeList);
+
+  CFXJSE_Engine* pEngine = pDoc->GetScriptContext();
+  *pValue = pNodeList->JSObject()->NewBoundV8Object(
+      pIsolate, pEngine->GetJseNormalClass()->GetTemplate(pIsolate));
 }
 
-void CJX_Tree::parent(CFXJSE_Value* pValue,
+void CJX_Tree::parent(v8::Isolate* pIsolate,
+                      v8::Local<v8::Value>* pValue,
                       bool bSetting,
                       XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
-  CXFA_Node* pParent = ToNode(GetXFAObject())->GetParent();
-  if (!pParent) {
-    pValue->SetNull();
-    return;
-  }
-
-  pValue->Assign(
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pParent));
+  CXFA_Node* pParent = GetXFANode()->GetParent();
+  *pValue = pParent ? GetDocument()
+                          ->GetScriptContext()
+                          ->GetOrCreateJSBindingFromMap(pParent)
+                          .As<v8::Value>()
+                    : fxv8::NewNullHelper(pIsolate).As<v8::Value>();
 }
 
-void CJX_Tree::index(CFXJSE_Value* pValue,
+void CJX_Tree::index(v8::Isolate* pIsolate,
+                     v8::Local<v8::Value>* pValue,
                      bool bSetting,
                      XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
-  CXFA_Node* pNode = ToNode(GetXFAObject());
+  CXFA_Node* pNode = GetXFANode();
   size_t iIndex = pNode ? pNode->GetIndexByName() : 0;
-  pValue->SetInteger(pdfium::base::checked_cast<int32_t>(iIndex));
+  *pValue = fxv8::NewNumberHelper(pIsolate,
+                                  pdfium::base::checked_cast<int32_t>(iIndex));
 }
 
-void CJX_Tree::classIndex(CFXJSE_Value* pValue,
+void CJX_Tree::classIndex(v8::Isolate* pIsolate,
+                          v8::Local<v8::Value>* pValue,
                           bool bSetting,
                           XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
-  CXFA_Node* pNode = ToNode(GetXFAObject());
+  CXFA_Node* pNode = GetXFANode();
   size_t iIndex = pNode ? pNode->GetIndexByClassName() : 0;
-  pValue->SetInteger(pdfium::base::checked_cast<int32_t>(iIndex));
+  *pValue = fxv8::NewNumberHelper(pIsolate,
+                                  pdfium::base::checked_cast<int32_t>(iIndex));
 }
 
-void CJX_Tree::somExpression(CFXJSE_Value* pValue,
+void CJX_Tree::somExpression(v8::Isolate* pIsolate,
+                             v8::Local<v8::Value>* pValue,
                              bool bSetting,
                              XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
-  WideString wsSOMExpression = GetXFAObject()->GetSOMExpression();
-  pValue->SetString(wsSOMExpression.ToUTF8().AsStringView());
+  ByteString bsSOMExpression = GetXFAObject()->GetSOMExpression().ToUTF8();
+  *pValue = fxv8::NewStringHelper(pIsolate, bsSOMExpression.AsStringView());
 }
 
-void CJX_Tree::ResolveNodeList(CFXJSE_Value* pValue,
-                               WideString wsExpression,
-                               uint32_t dwFlag,
-                               CXFA_Node* refNode) {
+v8::Local<v8::Value> CJX_Tree::ResolveNodeList(v8::Isolate* pIsolate,
+                                               WideString wsExpression,
+                                               Mask<XFA_ResolveFlag> dwFlag,
+                                               CXFA_Node* refNode) {
   if (!refNode)
-    refNode = ToNode(GetXFAObject());
+    refNode = GetXFANode();
 
-  XFA_RESOLVENODE_RS resolveNodeRS;
-  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  pScriptContext->ResolveObjects(refNode, wsExpression.AsStringView(),
-                                 &resolveNodeRS, dwFlag, nullptr);
-  CXFA_ArrayNodeList* pNodeList = new CXFA_ArrayNodeList(GetDocument());
-  if (resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    for (auto& pObject : resolveNodeRS.objects) {
-      if (pObject->IsNode())
-        pNodeList->Append(pObject->AsNode());
-    }
-  } else {
-    if (resolveNodeRS.script_attribute.callback &&
-        resolveNodeRS.script_attribute.eValueType == XFA_ScriptType::Object) {
-      for (auto& pObject : resolveNodeRS.objects) {
-        auto innerValue =
-            pdfium::MakeUnique<CFXJSE_Value>(pScriptContext->GetIsolate());
-        CJX_Object* jsObject = pObject->JSObject();
-        (*resolveNodeRS.script_attribute.callback)(
-            jsObject, innerValue.get(), false,
-            resolveNodeRS.script_attribute.attribute);
-        CXFA_Object* obj = CFXJSE_Engine::ToObject(innerValue.get());
-        if (obj->IsNode())
-          pNodeList->Append(obj->AsNode());
+  CXFA_Document* pDoc = GetDocument();
+  auto* pNodeList = cppgc::MakeGarbageCollected<CXFA_ArrayNodeList>(
+      pDoc->GetHeap()->GetAllocationHandle(), pDoc);
+  pDoc->GetNodeOwner()->PersistList(pNodeList);
+
+  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
+  absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+      pScriptContext->ResolveObjects(refNode, wsExpression.AsStringView(),
+                                     dwFlag);
+
+  if (maybeResult.has_value()) {
+    if (maybeResult.value().type ==
+        CFXJSE_Engine::ResolveResult::Type::kNodes) {
+      for (auto& pObject : maybeResult.value().objects) {
+        if (pObject->IsNode())
+          pNodeList->Append(pObject->AsNode());
+      }
+    } else {
+      if (maybeResult.value().script_attribute.callback &&
+          maybeResult.value().script_attribute.eValueType ==
+              XFA_ScriptType::Object) {
+        for (auto& pObject : maybeResult.value().objects) {
+          v8::Local<v8::Value> innerValue;
+          CJX_Object* jsObject = pObject->JSObject();
+          (*maybeResult.value().script_attribute.callback)(
+              pIsolate, jsObject, &innerValue, false,
+              maybeResult.value().script_attribute.attribute);
+          CXFA_Object* obj =
+              CFXJSE_Engine::ToObject(pScriptContext->GetIsolate(), innerValue);
+          if (obj->IsNode())
+            pNodeList->Append(obj->AsNode());
+        }
       }
     }
   }
-  pValue->SetHostObject(pNodeList, pScriptContext->GetJseNormalClass());
+  return pNodeList->JSObject()->NewBoundV8Object(
+      pIsolate, pScriptContext->GetJseNormalClass()->GetTemplate(pIsolate));
 }
diff --git a/fxjs/xfa/cjx_tree.h b/fxjs/xfa/cjx_tree.h
index cbef888..42c522e 100644
--- a/fxjs/xfa/cjx_tree.h
+++ b/fxjs/xfa/cjx_tree.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #ifndef FXJS_XFA_CJX_TREE_H_
 #define FXJS_XFA_CJX_TREE_H_
 
+#include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cjx_object.h"
 #include "fxjs/xfa/jse_define.h"
 
@@ -15,7 +16,7 @@
 
 class CJX_Tree : public CJX_Object {
  public:
-  explicit CJX_Tree(CXFA_Object* obj);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Tree() override;
 
   // CJX_Object:
@@ -32,6 +33,9 @@
   JSE_PROP(parent);
   JSE_PROP(somExpression);
 
+ protected:
+  explicit CJX_Tree(CXFA_Object* obj);
+
  private:
   using Type__ = CJX_Tree;
   using ParentType__ = CJX_Object;
@@ -39,10 +43,10 @@
   static const TypeTag static_type__ = TypeTag::Tree;
   static const CJX_MethodSpec MethodSpecs[];
 
-  void ResolveNodeList(CFXJSE_Value* pValue,
-                       WideString wsExpression,
-                       uint32_t dwFlag,
-                       CXFA_Node* refNode);
+  v8::Local<v8::Value> ResolveNodeList(v8::Isolate* pIsolate,
+                                       WideString wsExpression,
+                                       Mask<XFA_ResolveFlag> dwFlag,
+                                       CXFA_Node* refNode);
 };
 
 #endif  // FXJS_XFA_CJX_TREE_H_
diff --git a/fxjs/xfa/cjx_treelist.cpp b/fxjs/xfa/cjx_treelist.cpp
index 51bbb0a..a721349 100644
--- a/fxjs/xfa/cjx_treelist.cpp
+++ b/fxjs/xfa/cjx_treelist.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_treelist.h"
@@ -22,7 +23,7 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_TreeList::~CJX_TreeList() {}
+CJX_TreeList::~CJX_TreeList() = default;
 
 bool CJX_TreeList::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
@@ -33,7 +34,7 @@
 }
 
 CJS_Result CJX_TreeList::namedItem(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -43,9 +44,5 @@
   if (!pNode)
     return CJS_Result::Success();
 
-  CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode);
-
-  return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+  return CJS_Result::Success(runtime->GetOrCreateJSBindingFromMap(pNode));
 }
diff --git a/fxjs/xfa/cjx_treelist.h b/fxjs/xfa/cjx_treelist.h
index 294ab26..a5dbaaa 100644
--- a/fxjs/xfa/cjx_treelist.h
+++ b/fxjs/xfa/cjx_treelist.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_TreeList final : public CJX_List {
  public:
-  explicit CJX_TreeList(CXFA_TreeList* list);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_TreeList() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_METHOD(namedItem);
 
  private:
+  explicit CJX_TreeList(CXFA_TreeList* list);
+
   using Type__ = CJX_TreeList;
   using ParentType__ = CJX_List;
 
diff --git a/fxjs/xfa/cjx_wsdlconnection.cpp b/fxjs/xfa/cjx_wsdlconnection.cpp
index 7821232..1f7f45e 100644
--- a/fxjs/xfa/cjx_wsdlconnection.cpp
+++ b/fxjs/xfa/cjx_wsdlconnection.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 #include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_wsdlconnection.h"
 
 const CJX_MethodSpec CJX_WsdlConnection::MethodSpecs[] = {
@@ -21,14 +22,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_WsdlConnection::~CJX_WsdlConnection() {}
+CJX_WsdlConnection::~CJX_WsdlConnection() = default;
 
 bool CJX_WsdlConnection::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_WsdlConnection::execute(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty() && params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
diff --git a/fxjs/xfa/cjx_wsdlconnection.h b/fxjs/xfa/cjx_wsdlconnection.h
index bd5db40..8706695 100644
--- a/fxjs/xfa/cjx_wsdlconnection.h
+++ b/fxjs/xfa/cjx_wsdlconnection.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_WsdlConnection final : public CJX_Node {
  public:
-  explicit CJX_WsdlConnection(CXFA_WsdlConnection* connection);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_WsdlConnection() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_METHOD(execute);
 
  private:
+  explicit CJX_WsdlConnection(CXFA_WsdlConnection* connection);
+
   using Type__ = CJX_WsdlConnection;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_xfa.cpp b/fxjs/xfa/cjx_xfa.cpp
index 08c6a80..5248693 100644
--- a/fxjs/xfa/cjx_xfa.cpp
+++ b/fxjs/xfa/cjx_xfa.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,9 @@
 
 #include "fxjs/xfa/cjx_xfa.h"
 
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_xfa.h"
 
@@ -19,7 +20,8 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_Xfa::thisValue(CFXJSE_Value* pValue,
+void CJX_Xfa::thisValue(v8::Isolate* pIsolate,
+                        v8::Local<v8::Value>* pValue,
                         bool bSetting,
                         XFA_Attribute eAttribute) {
   if (bSetting)
@@ -27,9 +29,7 @@
 
   auto* pScriptContext = GetDocument()->GetScriptContext();
   CXFA_Object* pThis = pScriptContext->GetThisObject();
-  if (!pThis) {
-    pValue->SetNull();
-    return;
-  }
-  pValue->Assign(pScriptContext->GetOrCreateJSBindingFromMap(pThis));
+  *pValue =
+      pThis ? pScriptContext->GetOrCreateJSBindingFromMap(pThis).As<v8::Value>()
+            : fxv8::NewNullHelper(pIsolate);
 }
diff --git a/fxjs/xfa/cjx_xfa.h b/fxjs/xfa/cjx_xfa.h
index 63b0f5f..031425d 100644
--- a/fxjs/xfa/cjx_xfa.h
+++ b/fxjs/xfa/cjx_xfa.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Xfa final : public CJX_Model {
  public:
-  explicit CJX_Xfa(CXFA_Xfa* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Xfa() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_PROP(thisValue); /* this */
 
  private:
+  explicit CJX_Xfa(CXFA_Xfa* node);
+
   using Type__ = CJX_Xfa;
   using ParentType__ = CJX_Model;
 
diff --git a/fxjs/xfa/fxjse.cpp b/fxjs/xfa/fxjse.cpp
index ff6176b..d03ca7e 100644
--- a/fxjs/xfa/fxjse.cpp
+++ b/fxjs/xfa/fxjse.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,12 @@
 
 #include "fxjs/xfa/fxjse.h"
 
+#include "fxjs/fxv8.h"
+#include "fxjs/xfa/cfxjse_context.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-template.h"
+
 namespace pdfium {
 namespace fxjse {
 
@@ -15,6 +21,14 @@
 }  // namespace fxjse
 }  // namespace pdfium
 
+// static
+CFXJSE_HostObject* CFXJSE_HostObject::FromV8(v8::Local<v8::Value> arg) {
+  if (!fxv8::IsObject(arg))
+    return nullptr;
+
+  return FXJSE_RetrieveObjectBinding(arg.As<v8::Object>());
+}
+
 CFXJSE_HostObject::CFXJSE_HostObject() = default;
 
 CFXJSE_HostObject::~CFXJSE_HostObject() = default;
@@ -23,6 +37,17 @@
   return nullptr;
 }
 
-CXFA_Object* CFXJSE_HostObject::AsCXFAObject() {
+CJX_Object* CFXJSE_HostObject::AsCJXObject() {
   return nullptr;
 }
+
+v8::Local<v8::Object> CFXJSE_HostObject::NewBoundV8Object(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::FunctionTemplate> tmpl) {
+  v8::Local<v8::Object> hObject =
+      tmpl->InstanceTemplate()
+          ->NewInstance(pIsolate->GetCurrentContext())
+          .ToLocalChecked();
+  FXJSE_UpdateObjectBinding(hObject, this);
+  return hObject;
+}
diff --git a/fxjs/xfa/fxjse.h b/fxjs/xfa/fxjse.h
index 5aa6b39..04d746b 100644
--- a/fxjs/xfa/fxjse.h
+++ b/fxjs/xfa/fxjse.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,79 +7,91 @@
 #ifndef FXJS_XFA_FXJSE_H_
 #define FXJS_XFA_FXJSE_H_
 
+#include <stdint.h>
+
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "v8/include/v8.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
+#include "v8/include/v8-forward.h"
 
 namespace pdfium {
 namespace fxjse {
 
+// These are strings by design. With ASLR, their addresses should be random, so
+// it should be very unlikely for an object to accidentally have the same tag.
 extern const char kFuncTag[];
 extern const char kClassTag[];
 
 }  // namespace fxjse
 }  // namespace pdfium
 
-class CFXJSE_Arguments;
 class CFXJSE_FormCalcContext;
-class CFXJSE_Value;
 class CJS_Result;
-class CXFA_Object;
+class CJX_Object;
+
+enum class FXJSE_ClassPropType {
+  kNone,
+  kProperty,
+  kMethod,
+};
 
 // C++ object which is retrieved from v8 object's slot.
 class CFXJSE_HostObject {
  public:
+  static CFXJSE_HostObject* FromV8(v8::Local<v8::Value> arg);
   virtual ~CFXJSE_HostObject();
 
   // Two subclasses.
   virtual CFXJSE_FormCalcContext* AsFormCalcContext();
-  virtual CXFA_Object* AsCXFAObject();
+  virtual CJX_Object* AsCJXObject();
+
+  v8::Local<v8::Object> NewBoundV8Object(v8::Isolate* pIsolate,
+                                         v8::Local<v8::FunctionTemplate> tmpl);
 
  protected:
   CFXJSE_HostObject();
 };
 
-typedef CJS_Result (*FXJSE_MethodCallback)(
-    const v8::FunctionCallbackInfo<v8::Value>& info,
-    const WideString& functionName);
-typedef void (*FXJSE_FuncCallback)(CFXJSE_Value* pThis,
-                                   ByteStringView szFuncName,
-                                   CFXJSE_Arguments& args);
-typedef void (*FXJSE_PropAccessor)(CFXJSE_Value* pObject,
-                                   ByteStringView szPropName,
-                                   CFXJSE_Value* pValue);
-typedef int32_t (*FXJSE_PropTypeGetter)(CFXJSE_Value* pObject,
-                                        ByteStringView szPropName,
-                                        bool bQueryIn);
-
-enum FXJSE_ClassPropTypes {
-  FXJSE_ClassPropType_None,
-  FXJSE_ClassPropType_Property,
-  FXJSE_ClassPropType_Method
-};
+using FXJSE_MethodCallback =
+    CJS_Result (*)(const v8::FunctionCallbackInfo<v8::Value>& info,
+                   const WideString& functionName);
+using FXJSE_FuncCallback =
+    void (*)(CFXJSE_HostObject* pThis,
+             const v8::FunctionCallbackInfo<v8::Value>& info);
+using FXJSE_PropGetter = v8::Local<v8::Value> (*)(v8::Isolate* pIsolate,
+                                                  v8::Local<v8::Object> pObject,
+                                                  ByteStringView szPropName);
+using FXJSE_PropSetter = void (*)(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Object> pObject,
+                                  ByteStringView szPropName,
+                                  v8::Local<v8::Value> pValue);
+using FXJSE_PropTypeGetter =
+    FXJSE_ClassPropType (*)(v8::Isolate* pIsolate,
+                            v8::Local<v8::Object> pObject,
+                            ByteStringView szPropName,
+                            bool bQueryIn);
 
 struct FXJSE_FUNCTION_DESCRIPTOR {
-  const char* tag;  // pdfium::kFuncTag always.
+  const char* tag;  // `pdfium::fxjse::kFuncTag` always.
   const char* name;
   FXJSE_FuncCallback callbackProc;
 };
 
 struct FXJSE_CLASS_DESCRIPTOR {
-  const char* tag;  // pdfium::kClassTag always.
+  const char* tag;  // `pdfium::fxjse::kClassTag` always.
   const char* name;
-  const FXJSE_FUNCTION_DESCRIPTOR* methods;
+  UNOWNED_PTR_EXCLUSION const FXJSE_FUNCTION_DESCRIPTOR* methods;
   int32_t methNum;
   FXJSE_PropTypeGetter dynPropTypeGetter;
-  FXJSE_PropAccessor dynPropGetter;
-  FXJSE_PropAccessor dynPropSetter;
+  FXJSE_PropGetter dynPropGetter;
+  FXJSE_PropSetter dynPropSetter;
   FXJSE_MethodCallback dynMethodCall;
 };
 
-extern const FXJSE_CLASS_DESCRIPTOR GlobalClassDescriptor;
-extern const FXJSE_CLASS_DESCRIPTOR NormalClassDescriptor;
-extern const FXJSE_CLASS_DESCRIPTOR VariablesClassDescriptor;
-extern const FXJSE_CLASS_DESCRIPTOR kFormCalcFM2JSDescriptor;
+extern const FXJSE_CLASS_DESCRIPTOR kGlobalClassDescriptor;
+extern const FXJSE_CLASS_DESCRIPTOR kNormalClassDescriptor;
+extern const FXJSE_CLASS_DESCRIPTOR kVariablesClassDescriptor;
+extern const FXJSE_CLASS_DESCRIPTOR kFormCalcDescriptor;
 
-void FXJSE_ThrowMessage(ByteStringView utf8Message);
+void FXJSE_ThrowMessage(v8::Isolate* pIsolate, ByteStringView utf8Message);
 
 #endif  // FXJS_XFA_FXJSE_H_
diff --git a/fxjs/xfa/jse_define.h b/fxjs/xfa/jse_define.h
index 26405c9..0800601 100644
--- a/fxjs/xfa/jse_define.h
+++ b/fxjs/xfa/jse_define.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,25 +11,28 @@
 
 #include "fxjs/cjs_result.h"
 
-class CFX_V8;
+class CFXJSE_Engine;
 
 #define JSE_METHOD(method_name)                                      \
   static CJS_Result method_name##_static(                            \
-      CJX_Object* node, CFX_V8* runtime,                             \
+      CJX_Object* node, CFXJSE_Engine* runtime,                      \
       const std::vector<v8::Local<v8::Value>>& params) {             \
     if (!node->DynamicTypeIs(static_type__))                         \
       return CJS_Result::Failure(JSMessage::kBadObjectError);        \
     return static_cast<Type__*>(node)->method_name(runtime, params); \
   }                                                                  \
-  CJS_Result method_name(CFX_V8* runtime,                            \
+  CJS_Result method_name(CFXJSE_Engine* runtime,                     \
                          const std::vector<v8::Local<v8::Value>>& params)
 
-#define JSE_PROP(prop_name)                                               \
-  static void prop_name##_static(CJX_Object* node, CFXJSE_Value* value,   \
-                                 bool setting, XFA_Attribute attribute) { \
-    if (node->DynamicTypeIs(static_type__))                               \
-      static_cast<Type__*>(node)->prop_name(value, setting, attribute);   \
-  }                                                                       \
-  void prop_name(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute)
+#define JSE_PROP(prop_name)                                                 \
+  static void prop_name##_static(v8::Isolate* pIsolate, CJX_Object* node,   \
+                                 v8::Local<v8::Value>* value, bool setting, \
+                                 XFA_Attribute attribute) {                 \
+    if (node->DynamicTypeIs(static_type__))                                 \
+      static_cast<Type__*>(node)->prop_name(pIsolate, value, setting,       \
+                                            attribute);                     \
+  }                                                                         \
+  void prop_name(v8::Isolate* pIsolate, v8::Local<v8::Value>* pValue,       \
+                 bool bSetting, XFA_Attribute eAttribute)
 
 #endif  // FXJS_XFA_JSE_DEFINE_H_
diff --git a/navbar.md b/navbar.md
index c12cf11..386ef7f 100644
--- a/navbar.md
+++ b/navbar.md
@@ -1,5 +1,5 @@
 <!--
-Copyright 2016 PDFium Authors. All rights reserved.
+Copyright 2016 The PDFium Authors
 Use of this source code is governed by a BSD-style license that can be
 found in the LICENSE file.
 -->
diff --git a/pdfium.gni b/pdfium.gni
index 00a3886..ffa740a 100644
--- a/pdfium.gni
+++ b/pdfium.gni
@@ -1,4 +1,4 @@
-# Copyright 2015 PDFium Authors. All rights reserved.
+# Copyright 2015 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -33,21 +33,28 @@
   # If XFA, also support png codec. Ignored if not XFA.
   pdf_enable_xfa_png = true
 
-  # If XFA, also support png codec. Ignored if not XFA.
+  # If XFA, also support tiff codec. Ignored if not XFA.
   pdf_enable_xfa_tiff = true
 
-  # Build PDFium against Skia (experimental) rather than AGG. Use Skia to draw
-  # everything.
+  # Build PDFium using C++20 if set to true. Otherwise builds with C++17.
+  # There is no "pdf_use_cxx20_override" variable because this is only used in
+  # standalone PDFium, and not when PDFium is embedded in our projects.
+  pdf_use_cxx20 = (is_win && is_component_build) || is_fuchsia
+
+  # Build PDFium against 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.
+  # If enabled, coexists in build with AGG graphics and the default
+  # renderer is selectable at runtime.
   pdf_use_skia = pdf_use_skia_override
 
-  # Build PDFium against Skia (experimental) rather than AGG. Use Skia to draw
-  # paths.
-  pdf_use_skia_paths = pdf_use_skia_paths_override
-
-  # Build PDFium with or without experimental win32 GDI APIs.
-  pdf_use_win32_gdi = pdf_use_win32_gdi_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
@@ -56,9 +63,6 @@
   # Enable callgrind for performance profiling
   enable_callgrind = false
 
-  # Don't build against bundled zlib.
-  use_system_zlib = false
-
   # Don't build against bundled lcms2.
   use_system_lcms2 = false
 
@@ -67,10 +71,15 @@
 
   # Don't build against bundled libpng.
   use_system_libpng = false
-}
 
-if (pdf_use_skia && pdf_use_skia_paths) {
-  assert(false, "Enable at most ONE of pdf_use_skia and pdf_use_skia_paths")
+  # Don't build against bundled libtiff.
+  use_system_libtiff = false
+
+  # Don't build against bundled zlib.
+  use_system_zlib = false
+
+  # Enable SSE2 for MSVC builds. Ignored if it's not a MSVC build.
+  msvc_use_sse2 = true
 }
 
 assert(!pdf_is_complete_lib || !is_component_build,
diff --git a/public/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/PRESUBMIT.py b/public/PRESUBMIT.py
index 5081823..be01858 100644
--- a/public/PRESUBMIT.py
+++ b/public/PRESUBMIT.py
@@ -1,4 +1,4 @@
-# Copyright 2017 The PDFium Authors. All rights reserved.
+# Copyright 2017 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -8,12 +8,15 @@
 for more details about the presubmit API built into depot_tools.
 """
 
+USE_PYTHON3 = True
+
+
 def _CheckPublicHeaders(input_api, output_api):
   """Checks that the public headers match the API tests."""
   src_path = input_api.os_path.dirname(input_api.PresubmitLocalPath())
   check_script = input_api.os_path.join(
       src_path, 'testing' , 'tools' , 'api_check.py')
-  cmd = [input_api.python_executable, check_script]
+  cmd = [input_api.python3_executable, check_script]
   try:
     input_api.subprocess.check_output(cmd)
     return []
@@ -24,11 +27,11 @@
 
 def CheckChangeOnUpload(input_api, output_api):
   results = []
-  results += _CheckPublicHeaders(input_api, output_api)
+  results.extend(_CheckPublicHeaders(input_api, output_api))
   return results
 
 
 def CheckChangeOnCommit(input_api, output_api):
   results = []
-  results += _CheckPublicHeaders(input_api, output_api)
+  results.extend(_CheckPublicHeaders(input_api, output_api))
   return results
diff --git a/public/README b/public/README
index b07d0f3..5a8fd49 100644
--- a/public/README
+++ b/public/README
@@ -4,6 +4,8 @@
 included by an embedder of PDFium.  If there arises a need for functionality
 beyond what is present here, then a new API must be added here to provide it.
 
+Documentation for the API is within the headers. Start by reading fpdfview.h.
+
 These header files must be entirely contained in this directory; they must
 never include other header files from outside of it.
 
diff --git a/public/cpp/fpdf_deleters.h b/public/cpp/fpdf_deleters.h
index 633ddf5..d58f8d5 100644
--- a/public/cpp/fpdf_deleters.h
+++ b/public/cpp/fpdf_deleters.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/public/cpp/fpdf_scopers.h b/public/cpp/fpdf_scopers.h
index ff57c1b..fd9ce69 100644
--- a/public/cpp/fpdf_scopers.h
+++ b/public/cpp/fpdf_scopers.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/public/fpdf_annot.h b/public/fpdf_annot.h
index 58da809..abcef6b 100644
--- a/public/fpdf_annot.h
+++ b/public/fpdf_annot.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,6 @@
 #include "fpdfview.h"
 
 // NOLINTNEXTLINE(build/include)
-#include "fpdf_doc.h"
-// NOLINTNEXTLINE(build/include)
 #include "fpdf_formfill.h"
 
 #ifdef __cplusplus
@@ -47,6 +45,7 @@
 #define FPDF_ANNOT_THREED 25
 #define FPDF_ANNOT_RICHMEDIA 26
 #define FPDF_ANNOT_XFAWIDGET 27
+#define FPDF_ANNOT_REDACT 28
 
 // Refer to PDF Reference (6th edition) table 8.16 for all annotation flags.
 #define FPDF_ANNOT_FLAG_NONE 0
@@ -81,6 +80,17 @@
 // interactive form choice fields.
 #define FPDF_FORMFLAG_CHOICE_COMBO (1 << 17)
 #define FPDF_FORMFLAG_CHOICE_EDIT (1 << 18)
+#define FPDF_FORMFLAG_CHOICE_MULTI_SELECT (1 << 21)
+
+// Additional actions type of form field:
+//   K, on key stroke, JavaScript action.
+//   F, on format, JavaScript action.
+//   V, on validate, JavaScript action.
+//   C, on calculate, JavaScript action.
+#define FPDF_ANNOT_AACTION_KEY_STROKE 12
+#define FPDF_ANNOT_AACTION_FORMAT 13
+#define FPDF_ANNOT_AACTION_VALIDATE 14
+#define FPDF_ANNOT_AACTION_CALCULATE 15
 
 typedef enum FPDFANNOT_COLORTYPE {
   FPDFANNOT_COLORTYPE_Color = 0,
@@ -89,8 +99,19 @@
 
 // Experimental API.
 // Check if an annotation subtype is currently supported for creation.
-// Currently supported subtypes: circle, highlight, ink, popup, square,
-// squiggly, stamp, strikeout, text, and underline.
+// Currently supported subtypes:
+//    - circle
+//    - freetext
+//    - highlight
+//    - ink
+//    - link
+//    - popup
+//    - square,
+//    - squiggly
+//    - stamp
+//    - strikeout
+//    - text
+//    - underline
 //
 //   subtype   - the subtype to be checked.
 //
@@ -194,6 +215,34 @@
 FPDFAnnot_UpdateObject(FPDF_ANNOTATION annot, FPDF_PAGEOBJECT obj);
 
 // Experimental API.
+// Add a new InkStroke, represented by an array of points, to the InkList of
+// |annot|. The API creates an InkList if one doesn't already exist in |annot|.
+// This API works only for ink annotations. Please refer to ISO 32000-1:2008
+// spec, section 12.5.6.13.
+//
+//   annot       - handle to an annotation.
+//   points      - pointer to a FS_POINTF array representing input points.
+//   point_count - number of elements in |points| array. This should not exceed
+//                 the maximum value that can be represented by an int32_t).
+//
+// Returns the 0-based index at which the new InkStroke is added in the InkList
+// of the |annot|. Returns -1 on failure.
+FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_AddInkStroke(FPDF_ANNOTATION annot,
+                                                     const FS_POINTF* points,
+                                                     size_t point_count);
+
+// Experimental API.
+// Removes an InkList in |annot|.
+// This API works only for ink annotations.
+//
+//   annot  - handle to an annotation.
+//
+// Return true on successful removal of /InkList entry from context of the
+// non-null ink |annot|. Returns false on failure.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_RemoveInkList(FPDF_ANNOTATION annot);
+
+// Experimental API.
 // Add |obj| to |annot|. |obj| must have been created by
 // FPDFPageObj_CreateNew{Path|Rect}() or FPDFPageObj_New{Text|Image}Obj(), and
 // will be owned by |annot|. Note that an |obj| cannot belong to more than one
@@ -366,6 +415,121 @@
                                                       FS_RECTF* rect);
 
 // Experimental API.
+// Get the vertices of a polygon or polyline annotation. |buffer| is an array of
+// points of the annotation. If |length| is less than the returned length, or
+// |annot| or |buffer| is NULL, |buffer| will not be modified.
+//
+//   annot  - handle to an annotation, as returned by e.g. FPDFPage_GetAnnot()
+//   buffer - buffer for holding the points.
+//   length - length of the buffer in points.
+//
+// Returns the number of points if the annotation is of type polygon or
+// polyline, 0 otherwise.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetVertices(FPDF_ANNOTATION annot,
+                      FS_POINTF* buffer,
+                      unsigned long length);
+
+// Experimental API.
+// Get the number of paths in the ink list of an ink annotation.
+//
+//   annot  - handle to an annotation, as returned by e.g. FPDFPage_GetAnnot()
+//
+// Returns the number of paths in the ink list if the annotation is of type ink,
+// 0 otherwise.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetInkListCount(FPDF_ANNOTATION annot);
+
+// Experimental API.
+// Get a path in the ink list of an ink annotation. |buffer| is an array of
+// points of the path. If |length| is less than the returned length, or |annot|
+// or |buffer| is NULL, |buffer| will not be modified.
+//
+//   annot  - handle to an annotation, as returned by e.g. FPDFPage_GetAnnot()
+//   path_index - index of the path
+//   buffer - buffer for holding the points.
+//   length - length of the buffer in points.
+//
+// Returns the number of points of the path if the annotation is of type ink, 0
+// otherwise.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetInkListPath(FPDF_ANNOTATION annot,
+                         unsigned long path_index,
+                         FS_POINTF* buffer,
+                         unsigned long length);
+
+// Experimental API.
+// Get the starting and ending coordinates of a line annotation.
+//
+//   annot  - handle to an annotation, as returned by e.g. FPDFPage_GetAnnot()
+//   start - starting point
+//   end - ending point
+//
+// Returns true if the annotation is of type line, |start| and |end| are not
+// NULL, false otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetLine(FPDF_ANNOTATION annot,
+                                                      FS_POINTF* start,
+                                                      FS_POINTF* end);
+
+// Experimental API.
+// Set the characteristics of the annotation's border (rounded rectangle).
+//
+//   annot              - handle to an annotation
+//   horizontal_radius  - horizontal corner radius, in default user space units
+//   vertical_radius    - vertical corner radius, in default user space units
+//   border_width       - border width, in default user space units
+//
+// Returns true if setting the border for |annot| succeeds, false otherwise.
+//
+// If |annot| contains an appearance stream that overrides the border values,
+// then the appearance stream will be removed on success.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetBorder(FPDF_ANNOTATION annot,
+                                                        float horizontal_radius,
+                                                        float vertical_radius,
+                                                        float border_width);
+
+// Experimental API.
+// Get the characteristics of the annotation's border (rounded rectangle).
+//
+//   annot              - handle to an annotation
+//   horizontal_radius  - horizontal corner radius, in default user space units
+//   vertical_radius    - vertical corner radius, in default user space units
+//   border_width       - border width, in default user space units
+//
+// Returns true if |horizontal_radius|, |vertical_radius| and |border_width| are
+// not NULL, false otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_GetBorder(FPDF_ANNOTATION annot,
+                    float* horizontal_radius,
+                    float* vertical_radius,
+                    float* border_width);
+
+// Experimental API.
+// Get the JavaScript of an event of the annotation's additional actions.
+// |buffer| is only modified if |buflen| is large enough to hold the whole
+// JavaScript string. If |buflen| is smaller, the total size of the JavaScript
+// is still returned, but nothing is copied.  If there is no JavaScript for
+// |event| in |annot|, an empty string is written to |buf| and 2 is returned,
+// denoting the size of the null terminator in the buffer.  On other errors,
+// nothing is written to |buffer| and 0 is returned.
+//
+//    hHandle     -   handle to the form fill module, returned by
+//                    FPDFDOC_InitFormFillEnvironment().
+//    annot       -   handle to an interactive form annotation.
+//    event       -   event type, one of the FPDF_ANNOT_AACTION_* values.
+//    buffer      -   buffer for holding the value string, encoded in UTF-16LE.
+//    buflen      -   length of the buffer in bytes.
+//
+// Returns the length of the string value in bytes, including the 2-byte
+// null terminator.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetFormAdditionalActionJavaScript(FPDF_FORMHANDLE hHandle,
+                                            FPDF_ANNOTATION annot,
+                                            int event,
+                                            FPDF_WCHAR* buffer,
+                                            unsigned long buflen);
+
+// Experimental API.
 // Check if |annot|'s dictionary has |key| as a key.
 //
 //   annot  - handle to an annotation.
@@ -558,6 +722,26 @@
                            unsigned long buflen);
 
 // Experimental API.
+// Gets the alternate name of |annot|, which is an interactive form annotation.
+// |buffer| is only modified if |buflen| is longer than the length of contents.
+// In case of error, nothing will be added to |buffer| and the return value will
+// be 0. Note that return value of empty string is 2 for "\0\0".
+//
+//    hHandle     -   handle to the form fill module, returned by
+//                    FPDFDOC_InitFormFillEnvironment().
+//    annot       -   handle to an interactive form annotation.
+//    buffer      -   buffer for holding the alternate name string, encoded in
+//                    UTF-16LE.
+//    buflen      -   length of the buffer in bytes.
+//
+// Returns the length of the string value in bytes.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetFormFieldAlternateName(FPDF_FORMHANDLE hHandle,
+                                    FPDF_ANNOTATION annot,
+                                    FPDF_WCHAR* buffer,
+                                    unsigned long buflen);
+
+// Experimental API.
 // Gets the form field type of |annot|, which is an interactive form annotation.
 //
 //    hHandle     -   handle to the form fill module, returned by
@@ -628,6 +812,22 @@
                          unsigned long buflen);
 
 // Experimental API.
+// Determine whether or not the option at |index| in |annot|'s "Opt" dictionary
+// is selected. Intended for use with listbox and combobox widget annotations.
+//
+//   handle  - handle to the form fill module, returned by
+//             FPDFDOC_InitFormFillEnvironment.
+//   annot   - handle to an annotation.
+//   index   - numeric index of the option in the "Opt" array.
+//
+// Returns true if the option at |index| in |annot|'s "Opt" dictionary is
+// selected, false otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_IsOptionSelected(FPDF_FORMHANDLE handle,
+                           FPDF_ANNOTATION annot,
+                           int index);
+
+// Experimental API.
 // Get the float value of the font size for an |annot| with variable text.
 // If 0, the font is to be auto-sized: its size is computed as a function of
 // the height of the annotation rectangle.
@@ -656,6 +856,118 @@
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_IsChecked(FPDF_FORMHANDLE hHandle,
                                                         FPDF_ANNOTATION annot);
 
+// Experimental API.
+// Set the list of focusable annotation subtypes. Annotations of subtype
+// FPDF_ANNOT_WIDGET are by default focusable. New subtypes set using this API
+// will override the existing subtypes.
+//
+//   hHandle  - handle to the form fill module, returned by
+//              FPDFDOC_InitFormFillEnvironment.
+//   subtypes - list of annotation subtype which can be tabbed over.
+//   count    - total number of annotation subtype in list.
+// Returns true if list of annotation subtype is set successfully, false
+// otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_SetFocusableSubtypes(FPDF_FORMHANDLE hHandle,
+                               const FPDF_ANNOTATION_SUBTYPE* subtypes,
+                               size_t count);
+
+// Experimental API.
+// Get the count of focusable annotation subtypes as set by host
+// for a |hHandle|.
+//
+//   hHandle  - handle to the form fill module, returned by
+//              FPDFDOC_InitFormFillEnvironment.
+// Returns the count of focusable annotation subtypes or -1 on error.
+// Note : Annotations of type FPDF_ANNOT_WIDGET are by default focusable.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFAnnot_GetFocusableSubtypesCount(FPDF_FORMHANDLE hHandle);
+
+// Experimental API.
+// Get the list of focusable annotation subtype as set by host.
+//
+//   hHandle  - handle to the form fill module, returned by
+//              FPDFDOC_InitFormFillEnvironment.
+//   subtypes - receives the list of annotation subtype which can be tabbed
+//              over. Caller must have allocated |subtypes| more than or
+//              equal to the count obtained from
+//              FPDFAnnot_GetFocusableSubtypesCount() API.
+//   count    - size of |subtypes|.
+// Returns true on success and set list of annotation subtype to |subtypes|,
+// false otherwise.
+// Note : Annotations of type FPDF_ANNOT_WIDGET are by default focusable.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_GetFocusableSubtypes(FPDF_FORMHANDLE hHandle,
+                               FPDF_ANNOTATION_SUBTYPE* subtypes,
+                               size_t count);
+
+// Experimental API.
+// Gets FPDF_LINK object for |annot|. Intended to use for link annotations.
+//
+//   annot   - handle to an annotation.
+//
+// Returns FPDF_LINK from the FPDF_ANNOTATION and NULL on failure,
+// if the input annot is NULL or input annot's subtype is not link.
+FPDF_EXPORT FPDF_LINK FPDF_CALLCONV FPDFAnnot_GetLink(FPDF_ANNOTATION annot);
+
+// Experimental API.
+// Gets the count of annotations in the |annot|'s control group.
+// A group of interactive form annotations is collectively called a form
+// control group. Here, |annot|, an interactive form annotation, should be
+// either a radio button or a checkbox.
+//
+//   hHandle - handle to the form fill module, returned by
+//             FPDFDOC_InitFormFillEnvironment.
+//   annot   - handle to an annotation.
+//
+// Returns number of controls in its control group or -1 on error.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFAnnot_GetFormControlCount(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot);
+
+// Experimental API.
+// Gets the index of |annot| in |annot|'s control group.
+// A group of interactive form annotations is collectively called a form
+// control group. Here, |annot|, an interactive form annotation, should be
+// either a radio button or a checkbox.
+//
+//   hHandle - handle to the form fill module, returned by
+//             FPDFDOC_InitFormFillEnvironment.
+//   annot   - handle to an annotation.
+//
+// Returns index of a given |annot| in its control group or -1 on error.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFAnnot_GetFormControlIndex(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot);
+
+// Experimental API.
+// Gets the export value of |annot| which is an interactive form annotation.
+// Intended for use with radio button and checkbox widget annotations.
+// |buffer| is only modified if |buflen| is longer than the length of contents.
+// In case of error, nothing will be added to |buffer| and the return value
+// will be 0. Note that return value of empty string is 2 for "\0\0".
+//
+//    hHandle     -   handle to the form fill module, returned by
+//                    FPDFDOC_InitFormFillEnvironment().
+//    annot       -   handle to an interactive form annotation.
+//    buffer      -   buffer for holding the value string, encoded in UTF-16LE.
+//    buflen      -   length of the buffer in bytes.
+//
+// Returns the length of the string value in bytes.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetFormFieldExportValue(FPDF_FORMHANDLE hHandle,
+                                  FPDF_ANNOTATION annot,
+                                  FPDF_WCHAR* buffer,
+                                  unsigned long buflen);
+
+// Experimental API.
+// Add a URI action to |annot|, overwriting the existing action, if any.
+//
+//   annot  - handle to a link annotation.
+//   uri    - the URI to be set, encoded in 7-bit ASCII.
+//
+// Returns true if successful.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetURI(FPDF_ANNOTATION annot,
+                                                     const char* uri);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif  // __cplusplus
diff --git a/public/fpdf_attachment.h b/public/fpdf_attachment.h
index 791a90b..d25bdda 100644
--- a/public/fpdf_attachment.h
+++ b/public/fpdf_attachment.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -146,22 +146,31 @@
 FPDFAttachment_SetFile(FPDF_ATTACHMENT attachment,
                        FPDF_DOCUMENT document,
                        const void* contents,
-                       const unsigned long len);
+                       unsigned long len);
 
 // Experimental API.
-// Get the file data of |attachment|. |buffer| is only modified if |buflen| is
-// longer than the length of the file. On errors, |buffer| is unmodified and the
-// returned length is 0.
+// Get the file data of |attachment|.
+// When the attachment file data is readable, true is returned, and |out_buflen|
+// is updated to indicate the file data size. |buffer| is only modified if
+// |buflen| is non-null and long enough to contain the entire file data. Callers
+// must check both the return value and the input |buflen| is no less than the
+// returned |out_buflen| before using the data.
+//
+// Otherwise, when the attachment file data is unreadable or when |out_buflen|
+// is null, false is returned and |buffer| and |out_buflen| remain unmodified.
 //
 //   attachment - handle to an attachment.
 //   buffer     - buffer for holding the file data from |attachment|.
 //   buflen     - length of the buffer in bytes.
+//   out_buflen - pointer to the variable that will receive the minimum buffer
+//                size to contain the file data of |attachment|.
 //
-// Returns the length of the file.
-FPDF_EXPORT unsigned long FPDF_CALLCONV
+// Returns true on success, false otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFAttachment_GetFile(FPDF_ATTACHMENT attachment,
                        void* buffer,
-                       unsigned long buflen);
+                       unsigned long buflen,
+                       unsigned long* out_buflen);
 
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/public/fpdf_catalog.h b/public/fpdf_catalog.h
index 48d2775..1a48411 100644
--- a/public/fpdf_catalog.h
+++ b/public/fpdf_catalog.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/public/fpdf_dataavail.h b/public/fpdf_dataavail.h
index ad70b7f..004d9be 100644
--- a/public/fpdf_dataavail.h
+++ b/public/fpdf_dataavail.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -50,7 +50,6 @@
                            size_t offset,
                            size_t size);
 } FX_FILEAVAIL;
-typedef void* FPDF_AVAIL;
 
 // Create a document availability provider.
 //
diff --git a/public/fpdf_doc.h b/public/fpdf_doc.h
index 565314e..b073f4d 100644
--- a/public/fpdf_doc.h
+++ b/public/fpdf_doc.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -24,6 +24,8 @@
 #define PDFACTION_URI 3
 // Launch an application or open a file.
 #define PDFACTION_LAUNCH 4
+// Go to a destination in an embedded file.
+#define PDFACTION_EMBEDDEDGOTO 5
 
 // View destination fit types. See pdfmark reference v9, page 48.
 #define PDFDEST_VIEW_UNKNOWN_MODE 0
@@ -36,16 +38,12 @@
 #define PDFDEST_VIEW_FITBH 7
 #define PDFDEST_VIEW_FITBV 8
 
-typedef struct _FS_QUADPOINTSF {
-  FS_FLOAT x1;
-  FS_FLOAT y1;
-  FS_FLOAT x2;
-  FS_FLOAT y2;
-  FS_FLOAT x3;
-  FS_FLOAT y3;
-  FS_FLOAT x4;
-  FS_FLOAT y4;
-} FS_QUADPOINTSF;
+// The file identifier entry type. See section 14.4 "File Identifiers" of the
+// ISO 32000-1:2008 spec.
+typedef enum {
+  FILEIDTYPE_PERMANENT = 0,
+  FILEIDTYPE_CHANGING = 1
+} FPDF_FILEIDTYPE;
 
 // Get the first child of |bookmark|, or the first top-level bookmark item.
 //
@@ -65,6 +63,9 @@
 //
 // Returns a handle to the next sibling of |bookmark|, or NULL if this is the
 // last bookmark at this level.
+//
+// Note that the caller is responsible for handling circular bookmark
+// references, as may arise from malformed documents.
 FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
 FPDFBookmark_GetNextSibling(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark);
 
@@ -86,6 +87,18 @@
                       void* buffer,
                       unsigned long buflen);
 
+// Experimental API.
+// Get the number of chlidren of |bookmark|.
+//
+//   bookmark - handle to the bookmark.
+//
+// Returns a signed integer that represents the number of sub-items the given
+// bookmark has. If the value is positive, child items shall be shown by default
+// (open state). If the value is negative, child items shall be hidden by
+// default (closed state). Please refer to PDF 32000-1:2008, Table 153.
+// Returns 0 if the bookmark has no children or is invalid.
+FPDF_EXPORT int FPDF_CALLCONV FPDFBookmark_GetCount(FPDF_BOOKMARK bookmark);
+
 // Find the bookmark with |title| in |document|.
 //
 //   document - handle to the document.
@@ -103,7 +116,7 @@
 //   document - handle to the document.
 //   bookmark - handle to the bookmark.
 //
-// Returns the handle to the destination data,  NULL if no destination is
+// Returns the handle to the destination data, or NULL if no destination is
 // associated with |bookmark|.
 FPDF_EXPORT FPDF_DEST FPDF_CALLCONV
 FPDFBookmark_GetDest(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark);
@@ -113,8 +126,11 @@
 //   bookmark - handle to the bookmark.
 //
 // Returns the handle to the action data, or NULL if no action is associated
-// with |bookmark|. When NULL is returned, FPDFBookmark_GetDest() should be
-// called to get the |bookmark| destination data.
+// with |bookmark|.
+// If this function returns a valid handle, it is valid as long as |bookmark| is
+// valid.
+// If this function returns NULL, FPDFBookmark_GetDest() should be called to get
+// the |bookmark| destination data.
 FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV
 FPDFBookmark_GetAction(FPDF_BOOKMARK bookmark);
 
@@ -173,8 +189,18 @@
 // character, or 0 on error, typically because the arguments were bad or the
 // action was of the wrong type.
 //
-// The |buffer| is always encoded in 7-bit ASCII. If |buflen| is less than the
-// returned length, or |buffer| is NULL, |buffer| will not be modified.
+// The |buffer| may contain badly encoded data. The caller should validate the
+// output. e.g. Check to see if it is UTF-8.
+//
+// If |buflen| is less than the returned length, or |buffer| is NULL, |buffer|
+// will not be modified.
+//
+// Historically, the documentation for this API claimed |buffer| is always
+// encoded in 7-bit ASCII, but did not actually enforce it.
+// https://pdfium.googlesource.com/pdfium.git/+/d609e84cee2e14a18333247485af91df48a40592
+// added that enforcement, but that did not work well for real world PDFs that
+// used UTF-8. As of this writing, this API reverted back to its original
+// behavior prior to commit d609e84cee.
 FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDFAction_GetURIPath(FPDF_DOCUMENT document,
                       FPDF_ACTION action,
@@ -190,8 +216,8 @@
 FPDF_EXPORT int FPDF_CALLCONV FPDFDest_GetDestPageIndex(FPDF_DOCUMENT document,
                                                         FPDF_DEST dest);
 
+// Experimental API.
 // Get the view (fit type) specified by |dest|.
-// Experimental API. Subject to change.
 //
 //   dest         - handle to the destination.
 //   pNumParams   - receives the number of view parameters, which is at most 4.
@@ -270,6 +296,8 @@
 //   link - handle to the link.
 //
 // Returns a handle to the action associated to |link|, or NULL if no action.
+// If this function returns a valid handle, it is valid as long as |link| is
+// valid.
 FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV FPDFLink_GetAction(FPDF_LINK link);
 
 // Enumerates all the link annotations in |page|.
@@ -284,6 +312,17 @@
                                                        int* start_pos,
                                                        FPDF_LINK* link_annot);
 
+// Experimental API.
+// Gets FPDF_ANNOTATION object for |link_annot|.
+//
+//   page       - handle to the page in which FPDF_LINK object is present.
+//   link_annot - handle to link annotation.
+//
+// Returns FPDF_ANNOTATION from the FPDF_LINK and NULL on failure,
+// if the input link annot or page is NULL.
+FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
+FPDFLink_GetAnnot(FPDF_PAGE page, FPDF_LINK link_annot);
+
 // Get the rectangle for |link_annot|.
 //
 //   link_annot - handle to the link annotation.
@@ -312,6 +351,40 @@
                        int quad_index,
                        FS_QUADPOINTSF* quad_points);
 
+// Experimental API
+// Gets an additional-action from |page|.
+//
+//   page      - handle to the page, as returned by FPDF_LoadPage().
+//   aa_type   - the type of the page object's addtional-action, defined
+//               in public/fpdf_formfill.h
+//
+//   Returns the handle to the action data, or NULL if there is no
+//   additional-action of type |aa_type|.
+//   If this function returns a valid handle, it is valid as long as |page| is
+//   valid.
+FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV FPDF_GetPageAAction(FPDF_PAGE page,
+                                                          int aa_type);
+
+// Experimental API.
+// Get the file identifer defined in the trailer of |document|.
+//
+//   document - handle to the document.
+//   id_type  - the file identifier type to retrieve.
+//   buffer   - a buffer for the file identifier. May be NULL.
+//   buflen   - the length of the buffer, in bytes. May be 0.
+//
+// Returns the number of bytes in the file identifier, including the NUL
+// terminator.
+//
+// The |buffer| is always a byte string. The |buffer| is followed by a NUL
+// terminator.  If |buflen| is less than the returned length, or |buffer| is
+// NULL, |buffer| will not be modified.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_GetFileIdentifier(FPDF_DOCUMENT document,
+                       FPDF_FILEIDTYPE id_type,
+                       void* buffer,
+                       unsigned long buflen);
+
 // Get meta-data |tag| content from |document|.
 //
 //   document - handle to the document.
diff --git a/public/fpdf_edit.h b/public/fpdf_edit.h
index 8e0d234..8df2e3b 100644
--- a/public/fpdf_edit.h
+++ b/public/fpdf_edit.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -63,12 +63,16 @@
 #define FPDF_LINEJOIN_ROUND 1
 #define FPDF_LINEJOIN_BEVEL 2
 
+// See FPDF_SetPrintMode() for descriptions.
 #define FPDF_PRINTMODE_EMF 0
 #define FPDF_PRINTMODE_TEXTONLY 1
 #define FPDF_PRINTMODE_POSTSCRIPT2 2
 #define FPDF_PRINTMODE_POSTSCRIPT3 3
 #define FPDF_PRINTMODE_POSTSCRIPT2_PASSTHROUGH 4
 #define FPDF_PRINTMODE_POSTSCRIPT3_PASSTHROUGH 5
+#define FPDF_PRINTMODE_EMF_IMAGE_MASKS 6
+#define FPDF_PRINTMODE_POSTSCRIPT3_TYPE42 7
+#define FPDF_PRINTMODE_POSTSCRIPT3_TYPE42_PASSTHROUGH 8
 
 typedef struct FPDF_IMAGEOBJ_METADATA {
   // The image width in pixels.
@@ -245,6 +249,36 @@
                       double e,
                       double f);
 
+// Experimental API.
+// Get the transform matrix of a page object.
+//
+//   page_object - handle to a page object.
+//   matrix      - pointer to struct to receive the matrix value.
+//
+// The matrix is composed as:
+//   |a c e|
+//   |b d f|
+// and used to scale, rotate, shear and translate the page object.
+//
+// Returns TRUE on success.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_GetMatrix(FPDF_PAGEOBJECT page_object, FS_MATRIX* matrix);
+
+// Experimental API.
+// Set the transform matrix of a page object.
+//
+//   page_object - handle to a page object.
+//   matrix      - pointer to struct with the matrix value.
+//
+// The matrix is composed as:
+//   |a c e|
+//   |b d f|
+// and can be used to scale, rotate, shear and translate the page object.
+//
+// Returns TRUE on success.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_SetMatrix(FPDF_PAGEOBJECT path, const FS_MATRIX* matrix);
+
 // Transform all annotations in |page|.
 //
 //   page - handle to a page.
@@ -556,32 +590,8 @@
                                 FPDF_PAGEOBJECT image_object,
                                 FPDF_FILEACCESS* file_access);
 
-// Experimental API.
-// Get the transform matrix of an image object.
+// TODO(thestig): Start deprecating this once FPDFPageObj_SetMatrix() is stable.
 //
-//   image_object - handle to an image object.
-//   a            - matrix value.
-//   b            - matrix value.
-//   c            - matrix value.
-//   d            - matrix value.
-//   e            - matrix value.
-//   f            - matrix value.
-//
-// The matrix is composed as:
-//   |a c e|
-//   |b d f|
-// and used to scale, rotate, shear and translate the image.
-//
-// Returns TRUE on success.
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFImageObj_GetMatrix(FPDF_PAGEOBJECT image_object,
-                       double* a,
-                       double* b,
-                       double* c,
-                       double* d,
-                       double* e,
-                       double* f);
-
 // Set the transform matrix of |image_object|.
 //
 //   image_object - handle to an image object.
@@ -621,9 +631,11 @@
                        FPDF_PAGEOBJECT image_object,
                        FPDF_BITMAP bitmap);
 
-// Get a bitmap rasterisation of |image_object|. The returned bitmap will be
-// owned by the caller, and FPDFBitmap_Destroy() must be called on the returned
-// bitmap when it is no longer needed.
+// Get a bitmap rasterization of |image_object|. FPDFImageObj_GetBitmap() only
+// operates on |image_object| and does not take the associated image mask into
+// account. It also ignores the matrix for |image_object|.
+// The returned bitmap will be owned by the caller, and FPDFBitmap_Destroy()
+// must be called on the returned bitmap when it is no longer needed.
 //
 //   image_object - handle to an image object.
 //
@@ -631,6 +643,24 @@
 FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
 FPDFImageObj_GetBitmap(FPDF_PAGEOBJECT image_object);
 
+// Experimental API.
+// Get a bitmap rasterization of |image_object| that takes the image mask and
+// image matrix into account. To render correctly, the caller must provide the
+// |document| associated with |image_object|. If there is a |page| associated
+// with |image_object|, the caller should provide that as well.
+// The returned bitmap will be owned by the caller, and FPDFBitmap_Destroy()
+// must be called on the returned bitmap when it is no longer needed.
+//
+//   document     - handle to a document associated with |image_object|.
+//   page         - handle to an optional page associated with |image_object|.
+//   image_object - handle to an image object.
+//
+// Returns the bitmap or NULL on failure.
+FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
+FPDFImageObj_GetRenderedBitmap(FPDF_DOCUMENT document,
+                               FPDF_PAGE page,
+                               FPDF_PAGEOBJECT image_object);
+
 // Get the decoded image data of |image_object|. The decoded data is the
 // uncompressed image data, i.e. the raw image data after having all filters
 // applied. |buffer| is only modified if |buflen| is longer than the length of
@@ -701,6 +731,19 @@
                               FPDF_PAGE page,
                               FPDF_IMAGEOBJ_METADATA* metadata);
 
+// Experimental API.
+// Get the image size in pixels. Faster method to get only image size.
+//
+//   image_object - handle to an image object.
+//   width        - receives the image width in pixels; must not be NULL.
+//   height       - receives the image height in pixels; must not be NULL.
+//
+// Returns true if successful.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFImageObj_GetImagePixelSize(FPDF_PAGEOBJECT image_object,
+                               unsigned int* width,
+                               unsigned int* height);
+
 // Create a new path object at an initial position.
 //
 //   x - initial horizontal position.
@@ -731,7 +774,7 @@
 // right        - pointer where the right coordinate will be stored
 // top          - pointer where the top coordinate will be stored
 //
-// Returns TRUE on success.
+// On success, returns TRUE and fills in the 4 coordinates.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFPageObj_GetBounds(FPDF_PAGEOBJECT page_object,
                       float* left,
@@ -739,6 +782,25 @@
                       float* right,
                       float* top);
 
+// Experimental API.
+// Get the quad points that bounds |page_object|.
+//
+// page_object  - handle to a page object.
+// quad_points  - pointer where the quadrilateral points will be stored.
+//
+// On success, returns TRUE and fills in |quad_points|.
+//
+// Similar to FPDFPageObj_GetBounds(), this returns the bounds of a page
+// object. When the object is rotated by a non-multiple of 90 degrees, this API
+// returns a tighter bound that cannot be represented with just the 4 sides of
+// a rectangle.
+//
+// Currently only works the following |page_object| types: FPDF_PAGEOBJ_TEXT and
+// FPDF_PAGEOBJ_IMAGE.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_GetRotatedBounds(FPDF_PAGEOBJECT page_object,
+                             FS_QUADPOINTSF* quad_points);
+
 // Set the blend mode of |page_object|.
 //
 // page_object  - handle to a page object.
@@ -792,7 +854,6 @@
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFPageObj_SetStrokeWidth(FPDF_PAGEOBJECT page_object, float width);
 
-// Experimental API.
 // Get the stroke width of a page object.
 //
 // path   - the handle to the page object.
@@ -875,6 +936,62 @@
                          unsigned int* A);
 
 // Experimental API.
+// Get the line dash |phase| of |page_object|.
+//
+// page_object - handle to a page object.
+// phase - pointer where the dashing phase will be stored.
+//
+// Returns TRUE on success.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_GetDashPhase(FPDF_PAGEOBJECT page_object, float* phase);
+
+// Experimental API.
+// Set the line dash phase of |page_object|.
+//
+// page_object - handle to a page object.
+// phase - line dash phase.
+//
+// Returns TRUE on success.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_SetDashPhase(FPDF_PAGEOBJECT page_object, float phase);
+
+// Experimental API.
+// Get the line dash array of |page_object|.
+//
+// page_object - handle to a page object.
+//
+// Returns the line dash array size or -1 on failure.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFPageObj_GetDashCount(FPDF_PAGEOBJECT page_object);
+
+// Experimental API.
+// Get the line dash array of |page_object|.
+//
+// page_object - handle to a page object.
+// dash_array - pointer where the dashing array will be stored.
+// dash_count - number of elements in |dash_array|.
+//
+// Returns TRUE on success.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_GetDashArray(FPDF_PAGEOBJECT page_object,
+                         float* dash_array,
+                         size_t dash_count);
+
+// Experimental API.
+// Set the line dash array of |page_object|.
+//
+// page_object - handle to a page object.
+// dash_array - the dash array.
+// dash_count - number of elements in |dash_array|.
+// phase - the line dash phase.
+//
+// Returns TRUE on success.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_SetDashArray(FPDF_PAGEOBJECT page_object,
+                         const float* dash_array,
+                         size_t dash_count,
+                         float phase);
+
 // Get number of segments inside |path|.
 //
 //   path - handle to a path.
@@ -885,7 +1002,6 @@
 // Returns the number of objects in |path| or -1 on failure.
 FPDF_EXPORT int FPDF_CALLCONV FPDFPath_CountSegments(FPDF_PAGEOBJECT path);
 
-// Experimental API.
 // Get segment in |path| at |index|.
 //
 //   path  - handle to a path.
@@ -895,7 +1011,6 @@
 FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
 FPDFPath_GetPathSegment(FPDF_PAGEOBJECT path, int index);
 
-// Experimental API.
 // Get coordinates of |segment|.
 //
 //   segment  - handle to a segment.
@@ -906,7 +1021,6 @@
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFPathSegment_GetPoint(FPDF_PATHSEGMENT segment, float* x, float* y);
 
-// Experimental API.
 // Get type of |segment|.
 //
 //   segment - handle to a segment.
@@ -915,7 +1029,6 @@
 // FPDF_SEGMENT_UNKNOWN on error.
 FPDF_EXPORT int FPDF_CALLCONV FPDFPathSegment_GetType(FPDF_PATHSEGMENT segment);
 
-// Experimental API.
 // Gets if the |segment| closes the current subpath of a given path.
 //
 //   segment - handle to a segment.
@@ -991,7 +1104,6 @@
                                                          int fillmode,
                                                          FPDF_BOOL stroke);
 
-// Experimental API.
 // Get the drawing mode of a path.
 //
 // path     - the handle to the path object.
@@ -1003,36 +1115,6 @@
                                                          int* fillmode,
                                                          FPDF_BOOL* stroke);
 
-// Experimental API.
-// Get the transform matrix of a path.
-//
-//   path   - handle to a path.
-//   matrix - pointer to struct to receive the matrix value.
-//
-// The matrix is composed as:
-//   |a c e|
-//   |b d f|
-// and used to scale, rotate, shear and translate the path.
-//
-// Returns TRUE on success.
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_GetMatrix(FPDF_PAGEOBJECT path,
-                                                       FS_MATRIX* matrix);
-
-// Experimental API.
-// Set the transform matrix of a path.
-//
-//   path   - handle to a path.
-//   matrix - pointer to struct with the matrix value.
-//
-// The matrix is composed as:
-//   |a c e|
-//   |b d f|
-// and can be used to scale, rotate, shear and translate the path.
-//
-// Returns TRUE on success.
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_SetMatrix(FPDF_PAGEOBJECT path,
-                                                       const FS_MATRIX* matrix);
-
 // Create a new text object using one of the standard PDF fonts.
 //
 // document   - handle to the document.
@@ -1045,7 +1127,7 @@
                        FPDF_BYTESTRING font,
                        float font_size);
 
-// Set the text for a textobject. If it had text, it will be replaced.
+// Set the text for a text object. If it had text, it will be replaced.
 //
 // text_object  - handle to the text object.
 // text         - the UTF-16LE encoded string containing the text to be added.
@@ -1054,6 +1136,20 @@
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFText_SetText(FPDF_PAGEOBJECT text_object, FPDF_WIDESTRING text);
 
+// Experimental API.
+// Set the text using charcodes for a text object. If it had text, it will be
+// replaced.
+//
+// text_object  - handle to the text object.
+// charcodes    - pointer to an array of charcodes to be added.
+// count        - number of elements in |charcodes|.
+//
+// Returns TRUE on success
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFText_SetCharcodes(FPDF_PAGEOBJECT text_object,
+                      const uint32_t* charcodes,
+                      size_t count);
+
 // Returns a font object loaded from a stream of data. The font is loaded
 // into the document.
 //
@@ -1087,29 +1183,15 @@
 FPDF_EXPORT FPDF_FONT FPDF_CALLCONV
 FPDFText_LoadStandardFont(FPDF_DOCUMENT document, FPDF_BYTESTRING font);
 
-// Experimental API.
-// Get the transform matrix of a text object.
-//
-//   text   - handle to a text.
-//   matrix - pointer to struct with the matrix value.
-//
-// The matrix is composed as:
-//   |a c e|
-//   |b d f|
-// and used to scale, rotate, shear and translate the text.
-//
-// Returns TRUE on success.
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFTextObj_GetMatrix(FPDF_PAGEOBJECT text,
-                                                          FS_MATRIX* matrix);
-
-// Experimental API.
 // Get the font size of a text object.
 //
 //   text - handle to a text.
+//   size - pointer to the font size of the text object, measured in points
+//   (about 1/72 inch)
 //
-// Returns the font size of the text object, measured in points (about 1/72
-// inch) on success; 0 on failure.
-FPDF_EXPORT float FPDF_CALLCONV FPDFTextObj_GetFontSize(FPDF_PAGEOBJECT text);
+// Returns TRUE on success.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFTextObj_GetFontSize(FPDF_PAGEOBJECT text, float* size);
 
 // Close a loaded PDF font.
 //
@@ -1128,7 +1210,6 @@
                           FPDF_FONT font,
                           float font_size);
 
-// Experimental API.
 // Get the text rendering mode of a text object.
 //
 // text     - the handle to the text object.
@@ -1150,25 +1231,6 @@
 FPDFTextObj_SetTextRenderMode(FPDF_PAGEOBJECT text,
                               FPDF_TEXT_RENDERMODE render_mode);
 
-// Experimental API.
-// Get the font name of a text object.
-//
-// text             - the handle to the text object.
-// buffer           - the address of a buffer that receives the font name.
-// length           - the size, in bytes, of |buffer|.
-//
-// Returns the number of bytes in the font name (including the trailing NUL
-// character) on success, 0 on error.
-//
-// Regardless of the platform, the |buffer| is always in UTF-8 encoding.
-// If |length| is less than the returned length, or |buffer| is NULL, |buffer|
-// will not be modified.
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDFTextObj_GetFontName(FPDF_PAGEOBJECT text,
-                        void* buffer,
-                        unsigned long length);
-
-// Experimental API.
 // Get the text of a text object.
 //
 // text_object      - the handle to the text object.
@@ -1185,10 +1247,193 @@
 FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDFTextObj_GetText(FPDF_PAGEOBJECT text_object,
                     FPDF_TEXTPAGE text_page,
-                    void* buffer,
+                    FPDF_WCHAR* buffer,
                     unsigned long length);
 
 // Experimental API.
+// Get a bitmap rasterization of |text_object|. To render correctly, the caller
+// must provide the |document| associated with |text_object|. If there is a
+// |page| associated with |text_object|, the caller should provide that as well.
+// The returned bitmap will be owned by the caller, and FPDFBitmap_Destroy()
+// must be called on the returned bitmap when it is no longer needed.
+//
+//   document    - handle to a document associated with |text_object|.
+//   page        - handle to an optional page associated with |text_object|.
+//   text_object - handle to a text object.
+//   scale       - the scaling factor, which must be greater than 0.
+//
+// Returns the bitmap or NULL on failure.
+FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
+FPDFTextObj_GetRenderedBitmap(FPDF_DOCUMENT document,
+                              FPDF_PAGE page,
+                              FPDF_PAGEOBJECT text_object,
+                              float scale);
+
+// Experimental API.
+// Get the font of a text object.
+//
+// text - the handle to the text object.
+//
+// Returns a handle to the font object held by |text| which retains ownership.
+FPDF_EXPORT FPDF_FONT FPDF_CALLCONV FPDFTextObj_GetFont(FPDF_PAGEOBJECT text);
+
+// Experimental API.
+// Get the font name of a font.
+//
+// font   - the handle to the font object.
+// buffer - the address of a buffer that receives the font name.
+// length - the size, in bytes, of |buffer|.
+//
+// Returns the number of bytes in the font name (including the trailing NUL
+// character) on success, 0 on error.
+//
+// Regardless of the platform, the |buffer| is always in UTF-8 encoding.
+// If |length| is less than the returned length, or |buffer| is NULL, |buffer|
+// will not be modified.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFFont_GetFontName(FPDF_FONT font, char* buffer, unsigned long length);
+
+// Experimental API.
+// Get the decoded data from the |font| object.
+//
+// font       - The handle to the font object. (Required)
+// buffer     - The address of a buffer that receives the font data.
+// buflen     - Length of the buffer.
+// out_buflen - Pointer to variable that will receive the minimum buffer size
+//              to contain the font data. Not filled if the return value is
+//              FALSE. (Required)
+//
+// Returns TRUE on success. In which case, |out_buflen| will be filled, and
+// |buffer| will be filled if it is large enough. Returns FALSE if any of the
+// required parameters are null.
+//
+// The decoded data is the uncompressed font data. i.e. the raw font data after
+// having all stream filters applied, when the data is embedded.
+//
+// If the font is not embedded, then this API will instead return the data for
+// the substitution font it is using.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetFontData(FPDF_FONT font,
+                                                         uint8_t* buffer,
+                                                         size_t buflen,
+                                                         size_t* out_buflen);
+
+// Experimental API.
+// Get whether |font| is embedded or not.
+//
+// font - the handle to the font object.
+//
+// Returns 1 if the font is embedded, 0 if it not, and -1 on failure.
+FPDF_EXPORT int FPDF_CALLCONV FPDFFont_GetIsEmbedded(FPDF_FONT font);
+
+// Experimental API.
+// Get the descriptor flags of a font.
+//
+// font - the handle to the font object.
+//
+// Returns the bit flags specifying various characteristics of the font as
+// defined in ISO 32000-1:2008, table 123, -1 on failure.
+FPDF_EXPORT int FPDF_CALLCONV FPDFFont_GetFlags(FPDF_FONT font);
+
+// Experimental API.
+// Get the font weight of a font.
+//
+// font - the handle to the font object.
+//
+// Returns the font weight, -1 on failure.
+// Typical values are 400 (normal) and 700 (bold).
+FPDF_EXPORT int FPDF_CALLCONV FPDFFont_GetWeight(FPDF_FONT font);
+
+// Experimental API.
+// Get the italic angle of a font.
+//
+// font  - the handle to the font object.
+// angle - pointer where the italic angle will be stored
+//
+// The italic angle of a |font| is defined as degrees counterclockwise
+// from vertical. For a font that slopes to the right, this will be negative.
+//
+// Returns TRUE on success; |angle| unmodified on failure.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetItalicAngle(FPDF_FONT font,
+                                                            int* angle);
+
+// Experimental API.
+// Get ascent distance of a font.
+//
+// font       - the handle to the font object.
+// font_size  - the size of the |font|.
+// ascent     - pointer where the font ascent will be stored
+//
+// Ascent is the maximum distance in points above the baseline reached by the
+// glyphs of the |font|. One point is 1/72 inch (around 0.3528 mm).
+//
+// Returns TRUE on success; |ascent| unmodified on failure.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetAscent(FPDF_FONT font,
+                                                       float font_size,
+                                                       float* ascent);
+
+// Experimental API.
+// Get descent distance of a font.
+//
+// font       - the handle to the font object.
+// font_size  - the size of the |font|.
+// descent    - pointer where the font descent will be stored
+//
+// Descent is the maximum distance in points below the baseline reached by the
+// glyphs of the |font|. One point is 1/72 inch (around 0.3528 mm).
+//
+// Returns TRUE on success; |descent| unmodified on failure.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetDescent(FPDF_FONT font,
+                                                        float font_size,
+                                                        float* descent);
+
+// Experimental API.
+// Get the width of a glyph in a font.
+//
+// font       - the handle to the font object.
+// glyph      - the glyph.
+// font_size  - the size of the font.
+// width      - pointer where the glyph width will be stored
+//
+// Glyph width is the distance from the end of the prior glyph to the next
+// glyph. This will be the vertical distance for vertical writing.
+//
+// Returns TRUE on success; |width| unmodified on failure.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetGlyphWidth(FPDF_FONT font,
+                                                           uint32_t glyph,
+                                                           float font_size,
+                                                           float* width);
+
+// Experimental API.
+// Get the glyphpath describing how to draw a font glyph.
+//
+// font       - the handle to the font object.
+// glyph      - the glyph being drawn.
+// font_size  - the size of the font.
+//
+// Returns the handle to the segment, or NULL on faiure.
+FPDF_EXPORT FPDF_GLYPHPATH FPDF_CALLCONV FPDFFont_GetGlyphPath(FPDF_FONT font,
+                                                               uint32_t glyph,
+                                                               float font_size);
+
+// Experimental API.
+// Get number of segments inside glyphpath.
+//
+// glyphpath - handle to a glyph path.
+//
+// Returns the number of objects in |glyphpath| or -1 on failure.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFGlyphPath_CountGlyphSegments(FPDF_GLYPHPATH glyphpath);
+
+// Experimental API.
+// Get segment in glyphpath at index.
+//
+// glyphpath  - handle to a glyph path.
+// index      - the index of a segment.
+//
+// Returns the handle to the segment, or NULL on faiure.
+FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
+FPDFGlyphPath_GetGlyphPathSegment(FPDF_GLYPHPATH glyphpath, int index);
+
 // Get number of page objects inside |form_object|.
 //
 //   form_object - handle to a form object.
@@ -1197,7 +1442,6 @@
 FPDF_EXPORT int FPDF_CALLCONV
 FPDFFormObj_CountObjects(FPDF_PAGEOBJECT form_object);
 
-// Experimental API.
 // Get page object in |form_object| at |index|.
 //
 //   form_object - handle to a form object.
@@ -1207,21 +1451,6 @@
 FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
 FPDFFormObj_GetObject(FPDF_PAGEOBJECT form_object, unsigned long index);
 
-// Experimental API.
-// Get the transform matrix of a form object.
-//
-//   form_object - handle to a form.
-//   matrix      - pointer to struct to receive the matrix value.
-//
-// The matrix is composed as:
-//   |a c e|
-//   |b d f|
-// and used to scale, rotate, shear and translate the form object.
-//
-// Returns TRUE on success.
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFFormObj_GetMatrix(FPDF_PAGEOBJECT form_object, FS_MATRIX* matrix);
-
 #ifdef __cplusplus
 }  // extern "C"
 #endif  // __cplusplus
diff --git a/public/fpdf_ext.h b/public/fpdf_ext.h
index b1784dd..068a977 100644
--- a/public/fpdf_ext.h
+++ b/public/fpdf_ext.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/public/fpdf_flatten.h b/public/fpdf_flatten.h
index 614540e..aba5186 100644
--- a/public/fpdf_flatten.h
+++ b/public/fpdf_flatten.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/public/fpdf_formfill.h b/public/fpdf_formfill.h
index 95c24b4..a0769b7 100644
--- a/public/fpdf_formfill.h
+++ b/public/fpdf_formfill.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -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
@@ -307,7 +307,9 @@
                       int length);
 
   /*
-   * Pointer to FPDF_FORMFILLINFO interface.
+   * Pointer for embedder-specific data. Unused by PDFium, and despite
+   * its name, can be any data the embedder desires, though traditionally
+   * a FPDF_FORMFILLINFO interface.
    */
   void* m_pFormfillinfo;
 
@@ -374,8 +376,17 @@
 
 typedef struct _FPDF_FORMFILLINFO {
   /*
-   * Version number of the interface. Currently must be 1 (when PDFium is built
-   *  without the XFA module) or must be 2 (when built with the XFA module).
+   * Version number of the interface.
+   * Version 1 contains stable interfaces. Version 2 has additional
+   * experimental interfaces.
+   * When PDFium is built without the XFA module, version can be 1 or 2.
+   * With version 1, only stable interfaces are called. With version 2,
+   * additional experimental interfaces are also called.
+   * When PDFium is built with the XFA module, version must be 2.
+   * All the XFA related interfaces are experimental. If PDFium is built with
+   * the XFA module and version 1 then none of the XFA related interfaces
+   * would be called. When PDFium is built with XFA module then the version
+   * must be 2.
    */
   int version;
 
@@ -570,8 +581,8 @@
    *       action, the implementation needs to load the page.
    */
   FPDF_PAGE (*FFI_GetPage)(struct _FPDF_FORMFILLINFO* pThis,
-                             FPDF_DOCUMENT document,
-                             int nPageIndex);
+                           FPDF_DOCUMENT document,
+                           int nPageIndex);
 
   /*
    * Method: FFI_GetCurrentPage
@@ -579,18 +590,19 @@
    * Interface Version:
    *       1
    * Implementation Required:
-   *       yes
+   *       Yes when V8 support is present, otherwise unused.
    * Parameters:
    *       pThis       -   Pointer to the interface structure itself.
    *       document    -   Handle to document. Returned by FPDF_LoadDocument().
    * Return value:
    *       Handle to the page. Returned by FPDF_LoadPage().
    * Comments:
-   *       The implementation is expected to keep track of the current page,
-   *       e.g. the current page can be the one that is most visible on screen.
+   *       PDFium doesn't keep keep track of the "current page" (e.g. the one
+   *       that is most visible on screen), so it must ask the embedder for
+   *       this information.
    */
   FPDF_PAGE (*FFI_GetCurrentPage)(struct _FPDF_FORMFILLINFO* pThis,
-                                    FPDF_DOCUMENT document);
+                                  FPDF_DOCUMENT document);
 
   /*
    * Method: FFI_GetRotation
@@ -627,8 +639,9 @@
    * Return value:
    *       None.
    * Comments:
-   *       See the named actions description of <<PDF Reference, version 1.7>>
-   *       for more details.
+   *       See ISO 32000-1:2008, section 12.6.4.11 for descriptions of the
+   *       standard named actions, but note that a document may supply any
+   *       name of its choosing.
    */
   void (*FFI_ExecuteNamedAction)(struct _FPDF_FORMFILLINFO* pThis,
                                  FPDF_BYTESTRING namedAction);
@@ -671,6 +684,10 @@
    * Return value:
    *       None.
    * Comments:
+   *       If the embedder is version 2 or higher and have implementation for
+   *       FFI_DoURIActionWithKeyboardModifier, then
+   *       FFI_DoURIActionWithKeyboardModifier takes precedence over
+   *       FFI_DoURIAction.
    *       See the URI actions description of <<PDF Reference, version 1.7>>
    *       for more details.
    */
@@ -1066,9 +1083,59 @@
    *       TRUE indicates success, otherwise FALSE.
    */
   FPDF_BOOL (*FFI_PutRequestURL)(struct _FPDF_FORMFILLINFO* pThis,
-                                   FPDF_WIDESTRING wsURL,
-                                   FPDF_WIDESTRING wsData,
-                                   FPDF_WIDESTRING wsEncode);
+                                 FPDF_WIDESTRING wsURL,
+                                 FPDF_WIDESTRING wsData,
+                                 FPDF_WIDESTRING wsEncode);
+
+  /*
+   * Method: FFI_OnFocusChange
+   *     Called when the focused annotation is updated.
+   * Interface Version:
+   *     Ignored if |version| < 2.
+   * Implementation Required:
+   *     No
+   * Parameters:
+   *     param           -   Pointer to the interface structure itself.
+   *     annot           -   The focused annotation.
+   *     page_index      -   Index number of the page which contains the
+   *                         focused annotation. 0 for the first page.
+   * Return value:
+   *     None.
+   * Comments:
+   *     This callback function is useful for implementing any view based
+   *     action such as scrolling the annotation rect into view. The
+   *     embedder should not copy and store the annot as its scope is
+   *     limited to this call only.
+   */
+  void (*FFI_OnFocusChange)(struct _FPDF_FORMFILLINFO* param,
+                            FPDF_ANNOTATION annot,
+                            int page_index);
+
+  /**
+   * Method: FFI_DoURIActionWithKeyboardModifier
+   *       Ask the implementation to navigate to a uniform resource identifier
+   *       with the specified modifiers.
+   * Interface Version:
+   *       Ignored if |version| < 2.
+   * Implementation Required:
+   *       No
+   * Parameters:
+   *       param           -   Pointer to the interface structure itself.
+   *       uri             -   A byte string which indicates the uniform
+   *                           resource identifier, terminated by 0.
+   *       modifiers       -   Keyboard modifier that indicates which of
+   *                           the virtual keys are down, if any.
+   * Return value:
+   *       None.
+   * Comments:
+   *       If the embedder who is version 2 and does not implement this API,
+   *       then a call will be redirected to FFI_DoURIAction.
+   *       See the URI actions description of <<PDF Reference, version 1.7>>
+   *       for more details.
+   */
+  void(*FFI_DoURIActionWithKeyboardModifier)(struct _FPDF_FORMFILLINFO* param,
+      FPDF_BYTESTRING uri,
+      int modifiers);
 } FPDF_FORMFILLINFO;
 
 /*
@@ -1076,11 +1143,13 @@
  *       Initialize form fill environment.
  * Parameters:
  *       document        -   Handle to document from FPDF_LoadDocument().
- *       pFormFillInfo   -   Pointer to a FPDF_FORMFILLINFO structure.
+ *       formInfo        -   Pointer to a FPDF_FORMFILLINFO structure.
  * Return Value:
  *       Handle to the form fill module, or NULL on failure.
  * Comments:
  *       This function should be called before any form fill operation.
+ *       The FPDF_FORMFILLINFO passed in via |formInfo| must remain valid until
+ *       the returned FPDF_FORMHANDLE is closed.
  */
 FPDF_EXPORT FPDF_FORMHANDLE FPDF_CALLCONV
 FPDFDOC_InitFormFillEnvironment(FPDF_DOCUMENT document,
@@ -1106,7 +1175,7 @@
  *       functions. Should be invoked after user successfully loaded a
  *       PDF page, and FPDFDOC_InitFormFillEnvironment() has been invoked.
  * Parameters:
- *       hHandle     -   Handle to the form fill module, as eturned by
+ *       hHandle     -   Handle to the form fill module, as returned by
  *                       FPDFDOC_InitFormFillEnvironment().
  * Return Value:
  *       None.
@@ -1239,6 +1308,39 @@
                                                      double page_y);
 
 /*
+ * Experimental API
+ * Function: FORM_OnMouseWheel
+ *       Call this member function when the user scrolls the mouse wheel.
+ * Parameters:
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page        -   Handle to the page, as returned by FPDF_LoadPage().
+ *       modifier    -   Indicates whether various virtual keys are down.
+ *       page_coord  -   Specifies the coordinates of the cursor in PDF user
+ *                       space.
+ *       delta_x     -   Specifies the amount of wheel movement on the x-axis,
+ *                       in units of platform-agnostic wheel deltas. Negative
+ *                       values mean left.
+ *       delta_y     -   Specifies the amount of wheel movement on the y-axis,
+ *                       in units of platform-agnostic wheel deltas. Negative
+ *                       values mean down.
+ * Return Value:
+ *       True indicates success; otherwise false.
+ * Comments:
+ *       For |delta_x| and |delta_y|, the caller must normalize
+ *       platform-specific wheel deltas. e.g. On Windows, a delta value of 240
+ *       for a WM_MOUSEWHEEL event normalizes to 2, since Windows defines
+ *       WHEEL_DELTA as 120.
+ */
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnMouseWheel(
+    FPDF_FORMHANDLE hHandle,
+    FPDF_PAGE page,
+    int modifier,
+    const FS_POINTF* page_coord,
+    int delta_x,
+    int delta_y);
+
+/*
  * Function: FORM_OnFocus
  *       This function focuses the form annotation at a given point. If the
  *       annotation at the point already has focus, nothing happens. If there
@@ -1266,7 +1368,7 @@
  *       Call this member function when the user presses the left
  *       mouse button.
  * Parameters:
- *       hHandle     -   Handle to the form fill module. as returned by
+ *       hHandle     -   Handle to the form fill module, as returned by
  *                       FPDFDOC_InitFormFillEnvironment().
  *       page        -   Handle to the page, as returned by FPDF_LoadPage().
  *       modifier    -   Indicates whether various virtual keys are down.
@@ -1302,7 +1404,7 @@
  * Parameters:
  *       hHandle     -   Handle to the form fill module, as returned by
  *                       FPDFDOC_InitFormFillEnvironment().
- *       page        -   Handle to the page. as returned by FPDF_LoadPage().
+ *       page        -   Handle to the page, as returned by FPDF_LoadPage().
  *       modifier    -   Indicates whether various virtual keys are down.
  *       page_x      -   Specifies the x-coordinate of the cursor in device.
  *       page_y      -   Specifies the y-coordinate of the cursor in device.
@@ -1358,9 +1460,10 @@
  *       hHandle     -   Handle to the form fill module, aseturned by
  *                       FPDFDOC_InitFormFillEnvironment().
  *       page        -   Handle to the page, as returned by FPDF_LoadPage().
- *       nKeyCode    -   Indicates whether various virtual keys are down.
- *       modifier    -   Contains the scan code, key-transition code,
- *                       previous key state, and context code.
+ *       nKeyCode    -   The virtual-key code of the given key (see
+ *                       fpdf_fwlevent.h for virtual key codes).
+ *       modifier    -   Mask of key flags (see fpdf_fwlevent.h for key
+ *                       flag values).
  * Return Value:
  *       True indicates success; otherwise false.
  */
@@ -1376,11 +1479,15 @@
  *       hHandle     -   Handle to the form fill module, as returned by
  *                       FPDFDOC_InitFormFillEnvironment().
  *       page        -   Handle to the page, as returned by FPDF_LoadPage().
- *       nKeyCode    -   The virtual-key code of the given key.
- *       modifier    -   Contains the scan code, key-transition code,
- *                       previous key state, and context code.
+ *       nKeyCode    -   The virtual-key code of the given key (see
+ *                       fpdf_fwlevent.h for virtual key codes).
+ *       modifier    -   Mask of key flags (see fpdf_fwlevent.h for key
+ *                       flag values).
  * Return Value:
  *       True indicates success; otherwise false.
+ * Comments:
+ *       Currently unimplemented and always returns false. PDFium reserves this
+ *       API and may implement it in the future on an as-needed basis.
  */
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyUp(FPDF_FORMHANDLE hHandle,
                                                  FPDF_PAGE page,
@@ -1392,12 +1499,12 @@
  *       Call this member function when a keystroke translates to a
  *       nonsystem character.
  * Parameters:
- *        hHandle    -   Handle to the form fill module, as returned by
+ *       hHandle     -   Handle to the form fill module, as returned by
  *                       FPDFDOC_InitFormFillEnvironment().
- *        page       -   Handle to the page, as returned by FPDF_LoadPage().
- *        nChar      -   The character code value of the key.
- *        modifier   -   Contains the scan code, key-transition code,
- *                       previous key state, and context code.
+ *       page        -   Handle to the page, as returned by FPDF_LoadPage().
+ *       nChar       -   The character code value itself.
+ *       modifier    -   Mask of key flags (see fpdf_fwlevent.h for key
+ *                       flag values).
  * Return Value:
  *       True indicates success; otherwise false.
  */
@@ -1434,7 +1541,7 @@
  *       Call this function to obtain selected text within a form text
  *       field or form combobox text field.
  * Parameters:
- *       hHandle     -   Handle to the form fill module, asr eturned by
+ *       hHandle     -   Handle to the form fill module, as returned by
  *                       FPDFDOC_InitFormFillEnvironment().
  *       page        -   Handle to the page, as returned by FPDF_LoadPage().
  *       buffer      -   Buffer for holding the selected text, encoded in
@@ -1453,12 +1560,35 @@
                      unsigned long buflen);
 
 /*
+ * Experimental API
+ * Function: FORM_ReplaceAndKeepSelection
+ *       Call this function to replace the selected text in a form
+ *       text field or user-editable form combobox text field with another
+ *       text string (which can be empty or non-empty). If there is no
+ *       selected text, this function will append the replacement text after
+ *       the current caret position. After the insertion, the inserted text
+ *       will be selected.
+ * Parameters:
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page        -   Handle to the page, as Returned by FPDF_LoadPage().
+ *       wsText      -   The text to be inserted, in UTF-16LE format.
+ * Return Value:
+ *       None.
+ */
+FPDF_EXPORT void FPDF_CALLCONV
+FORM_ReplaceAndKeepSelection(FPDF_FORMHANDLE hHandle,
+                             FPDF_PAGE page,
+                             FPDF_WIDESTRING wsText);
+
+/*
  * Function: FORM_ReplaceSelection
  *       Call this function to replace the selected text in a form
  *       text field or user-editable form combobox text field with another
  *       text string (which can be empty or non-empty). If there is no
  *       selected text, this function will append the replacement text after
- *       the current caret position.
+ *       the current caret position. After the insertion, the selection range
+ *       will be set to empty.
  * Parameters:
  *       hHandle     -   Handle to the form fill module, as returned by
  *                       FPDFDOC_InitFormFillEnvironment().
@@ -1472,6 +1602,21 @@
                                                      FPDF_WIDESTRING wsText);
 
 /*
+ * Experimental API
+ * Function: FORM_SelectAllText
+ *       Call this function to select all the text within the currently focused
+ *       form text field or form combobox text field.
+ * Parameters:
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page        -   Handle to the page, as returned by FPDF_LoadPage().
+ * Return Value:
+ *       Whether the operation succeeded or not.
+ */
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FORM_SelectAllText(FPDF_FORMHANDLE hHandle, FPDF_PAGE page);
+
+/*
  * Function: FORM_CanUndo
  *       Find out if it is possible for the current focused widget in a given
  *       form to perform an undo operation.
@@ -1503,7 +1648,7 @@
  * Function: FORM_Undo
  *       Make the current focussed widget perform an undo operation.
  * Parameters:
- *       hHandle     -   Handle to the form fill module. as returned by
+ *       hHandle     -   Handle to the form fill module, as returned by
  *                       FPDFDOC_InitFormFillEnvironment().
  *       page        -   Handle to the page, as returned by FPDF_LoadPage().
  * Return Value:
@@ -1518,7 +1663,7 @@
  * Parameters:
  *       hHandle     -   Handle to the form fill module, as returned by
  *                       FPDFDOC_InitFormFillEnvironment().
- *       page        -   Handle to the page, as eturned by FPDF_LoadPage().
+ *       page        -   Handle to the page, as returned by FPDF_LoadPage().
  * Return Value:
  *       True if the redo operation succeeded.
  */
@@ -1539,6 +1684,50 @@
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FORM_ForceToKillFocus(FPDF_FORMHANDLE hHandle);
 
+/*
+ * Experimental API.
+ * Function: FORM_GetFocusedAnnot.
+ *       Call this member function to get the currently focused annotation.
+ * Parameters:
+ *       handle      -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page_index  -   Buffer to hold the index number of the page which
+ *                       contains the focused annotation. 0 for the first page.
+ *                       Can't be NULL.
+ *       annot       -   Buffer to hold the focused annotation. Can't be NULL.
+ * Return Value:
+ *       On success, return true and write to the out parameters. Otherwise return
+ *       false and leave the out parameters unmodified.
+ * Comments:
+ *       Not currently supported for XFA forms - will report no focused
+ *       annotation.
+ *       Must call FPDFPage_CloseAnnot() when the annotation returned in |annot|
+ *       by this function is no longer needed.
+ *       This will return true and set |page_index| to -1 and |annot| to NULL, if
+ *       there is no focused annotation.
+ */
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FORM_GetFocusedAnnot(FPDF_FORMHANDLE handle,
+                     int* page_index,
+                     FPDF_ANNOTATION* annot);
+
+/*
+ * Experimental API.
+ * Function: FORM_SetFocusedAnnot.
+ *       Call this member function to set the currently focused annotation.
+ * Parameters:
+ *       handle      -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       annot       -   Handle to an annotation.
+ * Return Value:
+ *       True indicates success; otherwise false.
+ * Comments:
+ *       |annot| can't be NULL. To kill focus, use FORM_ForceToKillFocus()
+ *       instead.
+ */
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FORM_SetFocusedAnnot(FPDF_FORMHANDLE handle, FPDF_ANNOTATION annot);
+
 // Form Field Types
 // The names of the defines are stable, but the specific values associated with
 // them are not, so do not hardcode their values.
@@ -1723,16 +1912,16 @@
                                             int rotate,
                                             int flags);
 
-#ifdef _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);
+#if defined(_SKIA_SUPPORT_)
+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/fpdf_fwlevent.h b/public/fpdf_fwlevent.h
index c8593e9..e61606d 100644
--- a/public/fpdf_fwlevent.h
+++ b/public/fpdf_fwlevent.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/public/fpdf_javascript.h b/public/fpdf_javascript.h
index 19f3810..2b02405 100644
--- a/public/fpdf_javascript.h
+++ b/public/fpdf_javascript.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/public/fpdf_ppo.h b/public/fpdf_ppo.h
index e9f3f66..1734bc6 100644
--- a/public/fpdf_ppo.h
+++ b/public/fpdf_ppo.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,15 +14,38 @@
 extern "C" {
 #endif
 
+// Experimental API.
+// Import pages to a FPDF_DOCUMENT.
+//
+//   dest_doc     - The destination document for the pages.
+//   src_doc      - The document to be imported.
+//   page_indices - An array of page indices to be imported. The first page is
+//                  zero. If |page_indices| is NULL, all pages from |src_doc|
+//                  are imported.
+//   length       - The length of the |page_indices| array.
+//   index        - The page index at which to insert the first imported page
+//                  into |dest_doc|. The first page is zero.
+//
+// Returns TRUE on success. Returns FALSE if any pages in |page_indices| is
+// invalid.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_ImportPagesByIndex(FPDF_DOCUMENT dest_doc,
+                        FPDF_DOCUMENT src_doc,
+                        const int* page_indices,
+                        unsigned long length,
+                        int index);
+
 // Import pages to a FPDF_DOCUMENT.
 //
 //   dest_doc  - The destination document for the pages.
 //   src_doc   - The document to be imported.
-//   pagerange - A page range string, Such as "1,3,5-7". If |pagerange| is NULL,
-//               all pages from |src_doc| are imported.
-//   index     - The page index to insert at.
+//   pagerange - A page range string, Such as "1,3,5-7". The first page is one.
+//               If |pagerange| is NULL, all pages from |src_doc| are imported.
+//   index     - The page index at which to insert the first imported page into
+//               |dest_doc|. The first page is zero.
 //
-// Returns TRUE on success.
+// Returns TRUE on success. Returns FALSE if any pages in |pagerange| is
+// invalid or if |pagerange| cannot be read.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_ImportPages(FPDF_DOCUMENT dest_doc,
                                                      FPDF_DOCUMENT src_doc,
                                                      FPDF_BYTESTRING pagerange,
@@ -52,6 +75,30 @@
                        size_t num_pages_on_x_axis,
                        size_t num_pages_on_y_axis);
 
+// Experimental API.
+// Create a template to generate form xobjects from |src_doc|'s page at
+// |src_page_index|, for use in |dest_doc|.
+//
+// Returns a handle on success, or NULL on failure. Caller owns the newly
+// created object.
+FPDF_EXPORT FPDF_XOBJECT FPDF_CALLCONV
+FPDF_NewXObjectFromPage(FPDF_DOCUMENT dest_doc,
+                        FPDF_DOCUMENT src_doc,
+                        int src_page_index);
+
+// Experimental API.
+// Close an FPDF_XOBJECT handle created by FPDF_NewXObjectFromPage().
+// FPDF_PAGEOBJECTs created from the FPDF_XOBJECT handle are not affected.
+FPDF_EXPORT void FPDF_CALLCONV FPDF_CloseXObject(FPDF_XOBJECT xobject);
+
+// Experimental API.
+// Create a new form object from an FPDF_XOBJECT object.
+//
+// Returns a new form object on success, or NULL on failure. Caller owns the
+// newly created object.
+FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
+FPDF_NewFormObjectFromXObject(FPDF_XOBJECT xobject);
+
 // Copy the viewer preferences from |src_doc| into |dest_doc|.
 //
 //   dest_doc - Document to write the viewer preferences into.
diff --git a/public/fpdf_progressive.h b/public/fpdf_progressive.h
index 99803b8..b146d48 100644
--- a/public/fpdf_progressive.h
+++ b/public/fpdf_progressive.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -46,6 +46,51 @@
   void* user;
 } IFSDK_PAUSE;
 
+// Experimental API.
+// Function: FPDF_RenderPageBitmapWithColorScheme_Start
+//          Start to render page contents to a device independent bitmap
+//          progressively with a specified color scheme for the content.
+// Parameters:
+//          bitmap       -   Handle to the device independent bitmap (as the
+//                           output buffer). Bitmap handle can be created by
+//                           FPDFBitmap_Create function.
+//          page         -   Handle to the page as returned by FPDF_LoadPage
+//                           function.
+//          start_x      -   Left pixel position of the display area in the
+//                           bitmap coordinate.
+//          start_y      -   Top pixel position of the display area in the
+//                           bitmap coordinate.
+//          size_x       -   Horizontal size (in pixels) for displaying the
+//                           page.
+//          size_y       -   Vertical size (in pixels) for displaying the page.
+//          rotate       -   Page orientation: 0 (normal), 1 (rotated 90
+//                           degrees clockwise), 2 (rotated 180 degrees),
+//                           3 (rotated 90 degrees counter-clockwise).
+//          flags        -   0 for normal display, or combination of flags
+//                           defined in fpdfview.h. With FPDF_ANNOT flag, it
+//                           renders all annotations that does not require
+//                           user-interaction, which are all annotations except
+//                           widget and popup annotations.
+//          color_scheme -   Color scheme to be used in rendering the |page|.
+//                           If null, this function will work similar to
+//                           FPDF_RenderPageBitmap_Start().
+//          pause        -   The IFSDK_PAUSE interface. A callback mechanism
+//                           allowing the page rendering process.
+// Return value:
+//          Rendering Status. See flags for progressive process status for the
+//          details.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_RenderPageBitmapWithColorScheme_Start(FPDF_BITMAP bitmap,
+                                           FPDF_PAGE page,
+                                           int start_x,
+                                           int start_y,
+                                           int size_x,
+                                           int size_y,
+                                           int rotate,
+                                           int flags,
+                                           const FPDF_COLORSCHEME* color_scheme,
+                                           IFSDK_PAUSE* pause);
+
 // Function: FPDF_RenderPageBitmap_Start
 //          Start to render page contents to a device independent bitmap
 //          progressively.
@@ -73,7 +118,6 @@
 // Return value:
 //          Rendering Status. See flags for progressive process status for the
 //          details.
-//
 FPDF_EXPORT int FPDF_CALLCONV FPDF_RenderPageBitmap_Start(FPDF_BITMAP bitmap,
                                                           FPDF_PAGE page,
                                                           int start_x,
diff --git a/public/fpdf_save.h b/public/fpdf_save.h
index 527d942..800d4e7 100644
--- a/public/fpdf_save.h
+++ b/public/fpdf_save.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/public/fpdf_searchex.h b/public/fpdf_searchex.h
index 4b67786..9c980db 100644
--- a/public/fpdf_searchex.h
+++ b/public/fpdf_searchex.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/public/fpdf_signature.h b/public/fpdf_signature.h
new file mode 100644
index 0000000..9a075e5
--- /dev/null
+++ b/public/fpdf_signature.h
@@ -0,0 +1,155 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PUBLIC_FPDF_SIGNATURE_H_
+#define PUBLIC_FPDF_SIGNATURE_H_
+
+// NOLINTNEXTLINE(build/include)
+#include "fpdfview.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+// Experimental API.
+// Function: FPDF_GetSignatureCount
+//          Get total number of signatures in the document.
+// Parameters:
+//          document    -   Handle to document. Returned by FPDF_LoadDocument().
+// Return value:
+//          Total number of signatures in the document on success, -1 on error.
+FPDF_EXPORT int FPDF_CALLCONV FPDF_GetSignatureCount(FPDF_DOCUMENT document);
+
+// Experimental API.
+// Function: FPDF_GetSignatureObject
+//          Get the Nth signature of the document.
+// Parameters:
+//          document    -   Handle to document. Returned by FPDF_LoadDocument().
+//          index       -   Index into the array of signatures of the document.
+// Return value:
+//          Returns the handle to the signature, or NULL on failure. The caller
+//          does not take ownership of the returned FPDF_SIGNATURE. Instead, it
+//          remains valid until FPDF_CloseDocument() is called for the document.
+FPDF_EXPORT FPDF_SIGNATURE FPDF_CALLCONV
+FPDF_GetSignatureObject(FPDF_DOCUMENT document, int index);
+
+// Experimental API.
+// Function: FPDFSignatureObj_GetContents
+//          Get the contents of a signature object.
+// Parameters:
+//          signature   -   Handle to the signature object. Returned by
+//                          FPDF_GetSignatureObject().
+//          buffer      -   The address of a buffer that receives the contents.
+//          length      -   The size, in bytes, of |buffer|.
+// Return value:
+//          Returns the number of bytes in the contents on success, 0 on error.
+//
+// For public-key signatures, |buffer| is either a DER-encoded PKCS#1 binary or
+// a DER-encoded PKCS#7 binary. If |length| is less than the returned length, or
+// |buffer| is NULL, |buffer| will not be modified.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFSignatureObj_GetContents(FPDF_SIGNATURE signature,
+                             void* buffer,
+                             unsigned long length);
+
+// Experimental API.
+// Function: FPDFSignatureObj_GetByteRange
+//          Get the byte range of a signature object.
+// Parameters:
+//          signature   -   Handle to the signature object. Returned by
+//                          FPDF_GetSignatureObject().
+//          buffer      -   The address of a buffer that receives the
+//                          byte range.
+//          length      -   The size, in ints, of |buffer|.
+// Return value:
+//          Returns the number of ints in the byte range on
+//          success, 0 on error.
+//
+// |buffer| is an array of pairs of integers (starting byte offset,
+// length in bytes) that describes the exact byte range for the digest
+// calculation. If |length| is less than the returned length, or
+// |buffer| is NULL, |buffer| will not be modified.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFSignatureObj_GetByteRange(FPDF_SIGNATURE signature,
+                              int* buffer,
+                              unsigned long length);
+
+// Experimental API.
+// Function: FPDFSignatureObj_GetSubFilter
+//          Get the encoding of the value of a signature object.
+// Parameters:
+//          signature   -   Handle to the signature object. Returned by
+//                          FPDF_GetSignatureObject().
+//          buffer      -   The address of a buffer that receives the encoding.
+//          length      -   The size, in bytes, of |buffer|.
+// Return value:
+//          Returns the number of bytes in the encoding name (including the
+//          trailing NUL character) on success, 0 on error.
+//
+// The |buffer| is always encoded in 7-bit ASCII. If |length| is less than the
+// returned length, or |buffer| is NULL, |buffer| will not be modified.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFSignatureObj_GetSubFilter(FPDF_SIGNATURE signature,
+                              char* buffer,
+                              unsigned long length);
+
+// Experimental API.
+// Function: FPDFSignatureObj_GetReason
+//          Get the reason (comment) of the signature object.
+// Parameters:
+//          signature   -   Handle to the signature object. Returned by
+//                          FPDF_GetSignatureObject().
+//          buffer      -   The address of a buffer that receives the reason.
+//          length      -   The size, in bytes, of |buffer|.
+// Return value:
+//          Returns the number of bytes in the reason on success, 0 on error.
+//
+// Regardless of the platform, the |buffer| is always in UTF-16LE encoding. The
+// string is terminated by a UTF16 NUL character. If |length| is less than the
+// returned length, or |buffer| is NULL, |buffer| will not be modified.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFSignatureObj_GetReason(FPDF_SIGNATURE signature,
+                           void* buffer,
+                           unsigned long length);
+
+// Experimental API.
+// Function: FPDFSignatureObj_GetTime
+//          Get the time of signing of a signature object.
+// Parameters:
+//          signature   -   Handle to the signature object. Returned by
+//                          FPDF_GetSignatureObject().
+//          buffer      -   The address of a buffer that receives the time.
+//          length      -   The size, in bytes, of |buffer|.
+// Return value:
+//          Returns the number of bytes in the encoding name (including the
+//          trailing NUL character) on success, 0 on error.
+//
+// The |buffer| is always encoded in 7-bit ASCII. If |length| is less than the
+// returned length, or |buffer| is NULL, |buffer| will not be modified.
+//
+// The format of time is expected to be D:YYYYMMDDHHMMSS+XX'YY', i.e. it's
+// percision is seconds, with timezone information. This value should be used
+// only when the time of signing is not available in the (PKCS#7 binary)
+// signature.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFSignatureObj_GetTime(FPDF_SIGNATURE signature,
+                         char* buffer,
+                         unsigned long length);
+
+// Experimental API.
+// Function: FPDFSignatureObj_GetDocMDPPermission
+//          Get the DocMDP permission of a signature object.
+// Parameters:
+//          signature   -   Handle to the signature object. Returned by
+//                          FPDF_GetSignatureObject().
+// Return value:
+//          Returns the permission (1, 2 or 3) on success, 0 on error.
+FPDF_EXPORT unsigned int FPDF_CALLCONV
+FPDFSignatureObj_GetDocMDPPermission(FPDF_SIGNATURE signature);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif  // __cplusplus
+
+#endif  // PUBLIC_FPDF_SIGNATURE_H_
diff --git a/public/fpdf_structtree.h b/public/fpdf_structtree.h
index a8083d5..a6c158a 100644
--- a/public/fpdf_structtree.h
+++ b/public/fpdf_structtree.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -62,7 +62,7 @@
 //          buffer         -   A buffer for output the alt text. May be NULL.
 //          buflen         -   The length of the buffer, in bytes. May be 0.
 // Return value:
-//          The number of bytes in the title, including the terminating NUL
+//          The number of bytes in the alt text, including the terminating NUL
 //          character. The number of bytes is returned regardless of the
 //          |buffer| and |buflen| parameters.
 // Comments:
@@ -75,6 +75,91 @@
                               void* buffer,
                               unsigned long buflen);
 
+// Experimental API.
+// Function: FPDF_StructElement_GetActualText
+//          Get the actual text for a given element.
+// Parameters:
+//          struct_element -   Handle to the struct element.
+//          buffer         -   A buffer for output the actual text. May be NULL.
+//          buflen         -   The length of the buffer, in bytes. May be 0.
+// Return value:
+//          The number of bytes in the actual text, including the terminating
+//          NUL character. The number of bytes is returned regardless of the
+//          |buffer| and |buflen| parameters.
+// Comments:
+//          Regardless of the platform, the |buffer| is always in UTF-16LE
+//          encoding. The string is terminated by a UTF16 NUL character. If
+//          |buflen| is less than the required length, or |buffer| is NULL,
+//          |buffer| will not be modified.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_StructElement_GetActualText(FPDF_STRUCTELEMENT struct_element,
+                                 void* buffer,
+                                 unsigned long buflen);
+
+// Function: FPDF_StructElement_GetID
+//          Get the ID for a given element.
+// Parameters:
+//          struct_element -   Handle to the struct element.
+//          buffer         -   A buffer for output the ID string. May be NULL.
+//          buflen         -   The length of the buffer, in bytes. May be 0.
+// Return value:
+//          The number of bytes in the ID string, including the terminating NUL
+//          character. The number of bytes is returned regardless of the
+//          |buffer| and |buflen| parameters.
+// Comments:
+//          Regardless of the platform, the |buffer| is always in UTF-16LE
+//          encoding. The string is terminated by a UTF16 NUL character. If
+//          |buflen| is less than the required length, or |buffer| is NULL,
+//          |buffer| will not be modified.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_StructElement_GetID(FPDF_STRUCTELEMENT struct_element,
+                         void* buffer,
+                         unsigned long buflen);
+
+// Experimental API.
+// Function: FPDF_StructElement_GetLang
+//          Get the case-insensitive IETF BCP 47 language code for an element.
+// Parameters:
+//          struct_element -   Handle to the struct element.
+//          buffer         -   A buffer for output the lang string. May be NULL.
+//          buflen         -   The length of the buffer, in bytes. May be 0.
+// Return value:
+//          The number of bytes in the ID string, including the terminating NUL
+//          character. The number of bytes is returned regardless of the
+//          |buffer| and |buflen| parameters.
+// Comments:
+//          Regardless of the platform, the |buffer| is always in UTF-16LE
+//          encoding. The string is terminated by a UTF16 NUL character. If
+//          |buflen| is less than the required length, or |buffer| is NULL,
+//          |buffer| will not be modified.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_StructElement_GetLang(FPDF_STRUCTELEMENT struct_element,
+                           void* buffer,
+                           unsigned long buflen);
+
+// Experimental API.
+// Function: FPDF_StructElement_GetStringAttribute
+//          Get a struct element attribute of type "name" or "string".
+// Parameters:
+//          struct_element -   Handle to the struct element.
+//          attr_name      -   The name of the attribute to retrieve.
+//          buffer         -   A buffer for output. May be NULL.
+//          buflen         -   The length of the buffer, in bytes. May be 0.
+// Return value:
+//          The number of bytes in the attribute value, including the
+//          terminating NUL character. The number of bytes is returned
+//          regardless of the |buffer| and |buflen| parameters.
+// Comments:
+//          Regardless of the platform, the |buffer| is always in UTF-16LE
+//          encoding. The string is terminated by a UTF16 NUL character. If
+//          |buflen| is less than the required length, or |buffer| is NULL,
+//          |buffer| will not be modified.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_StructElement_GetStringAttribute(FPDF_STRUCTELEMENT struct_element,
+                                      FPDF_BYTESTRING attr_name,
+                                      void* buffer,
+                                      unsigned long buflen);
+
 // Function: FPDF_StructElement_GetMarkedContentID
 //          Get the marked content ID for a given element.
 // Parameters:
@@ -89,8 +174,8 @@
 //           Get the type (/S) for a given element.
 // Parameters:
 //           struct_element - Handle to the struct element.
-//           buffer        - A buffer for output. May be NULL.
-//           buflen        - The length of the buffer, in bytes. May be 0.
+//           buffer         - A buffer for output. May be NULL.
+//           buflen         - The length of the buffer, in bytes. May be 0.
 // Return value:
 //           The number of bytes in the type, including the terminating NUL
 //           character. The number of bytes is returned regardless of the
@@ -105,6 +190,27 @@
                            void* buffer,
                            unsigned long buflen);
 
+// Experimental API.
+// Function: FPDF_StructElement_GetObjType
+//           Get the object type (/Type) for a given element.
+// Parameters:
+//           struct_element - Handle to the struct element.
+//           buffer         - A buffer for output. May be NULL.
+//           buflen         - The length of the buffer, in bytes. May be 0.
+// Return value:
+//           The number of bytes in the object type, including the terminating
+//           NUL character. The number of bytes is returned regardless of the
+//           |buffer| and |buflen| parameters.
+// Comments:
+//           Regardless of the platform, the |buffer| is always in UTF-16LE
+//           encoding. The string is terminated by a UTF16 NUL character. If
+//           |buflen| is less than the required length, or |buffer| is NULL,
+//           |buffer| will not be modified.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_StructElement_GetObjType(FPDF_STRUCTELEMENT struct_element,
+                              void* buffer,
+                              unsigned long buflen);
+
 // Function: FPDF_StructElement_GetTitle
 //           Get the title (/T) for a given element.
 // Parameters:
@@ -137,8 +243,8 @@
 // Function: FPDF_StructElement_GetChildAtIndex
 //          Get a child in the structure element.
 // Parameters:
-//          struct_tree -   Handle to the struct element.
-//          index       -   The index for the child, 0-based.
+//          struct_element -   Handle to the struct element.
+//          index          -   The index for the child, 0-based.
 // Return value:
 //          The child at the n-th index or NULL on error.
 // Comments:
@@ -148,6 +254,199 @@
 FPDF_StructElement_GetChildAtIndex(FPDF_STRUCTELEMENT struct_element,
                                    int index);
 
+// Experimental API.
+// Function: FPDF_StructElement_GetParent
+//          Get the parent of the structure element.
+// Parameters:
+//          struct_element -   Handle to the struct element.
+// Return value:
+//          The parent structure element or NULL on error.
+// Comments:
+//          If structure element is StructTreeRoot, then this function will
+//          return NULL.
+FPDF_EXPORT FPDF_STRUCTELEMENT FPDF_CALLCONV
+FPDF_StructElement_GetParent(FPDF_STRUCTELEMENT struct_element);
+
+// Function: FPDF_StructElement_GetAttributeCount
+//          Count the number of attributes for the structure element.
+// Parameters:
+//          struct_element -   Handle to the struct element.
+// Return value:
+//          The number of attributes, or -1 on error.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_StructElement_GetAttributeCount(FPDF_STRUCTELEMENT struct_element);
+
+// Experimental API.
+// Function: FPDF_StructElement_GetAttributeAtIndex
+//          Get an attribute object in the structure element.
+// Parameters:
+//          struct_element -   Handle to the struct element.
+//          index          -   The index for the attribute object, 0-based.
+// Return value:
+//          The attribute object at the n-th index or NULL on error.
+// Comments:
+//          If the attribute object exists but is not a dict, then this
+//          function will return NULL. This will also return NULL for out of
+//          bounds indices.
+FPDF_EXPORT FPDF_STRUCTELEMENT_ATTR FPDF_CALLCONV
+FPDF_StructElement_GetAttributeAtIndex(FPDF_STRUCTELEMENT struct_element, int index);
+
+// Experimental API.
+// Function: FPDF_StructElement_Attr_GetCount
+//          Count the number of attributes in a structure element attribute map.
+// Parameters:
+//          struct_attribute - Handle to the struct element attribute.
+// Return value:
+//          The number of attributes, or -1 on error.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_StructElement_Attr_GetCount(FPDF_STRUCTELEMENT_ATTR struct_attribute);
+
+
+// Experimental API.
+// Function: FPDF_StructElement_Attr_GetName
+//          Get the name of an attribute in a structure element attribute map.
+// Parameters:
+//          struct_attribute   - Handle to the struct element attribute.
+//          index              - The index of attribute in the map.
+//          buffer             - A buffer for output. May be NULL. This is only
+//                               modified if |buflen| is longer than the length
+//                               of the key. Optional, pass null to just
+//                               retrieve the size of the buffer needed.
+//          buflen             - The length of the buffer.
+//          out_buflen         - A pointer to variable that will receive the
+//                               minimum buffer size to contain the key. Not
+//                               filled if FALSE is returned.
+// Return value:
+//          TRUE if the operation was successful, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetName(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+                                int index,
+                                void* buffer,
+                                unsigned long buflen,
+                                unsigned long* out_buflen);
+
+// Experimental API.
+// Function: FPDF_StructElement_Attr_GetType
+//          Get the type of an attribute in a structure element attribute map.
+// Parameters:
+//          struct_attribute   - Handle to the struct element attribute.
+//          name               - The attribute name.
+// Return value:
+//          Returns the type of the value, or FPDF_OBJECT_UNKNOWN in case of
+//          failure.
+FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
+FPDF_StructElement_Attr_GetType(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+                                FPDF_BYTESTRING name);
+
+// Experimental API.
+// Function: FPDF_StructElement_Attr_GetBooleanValue
+//          Get the value of a boolean attribute in an attribute map by name as
+//          FPDF_BOOL. FPDF_StructElement_Attr_GetType() should have returned
+//          FPDF_OBJECT_BOOLEAN for this property.
+// Parameters:
+//          struct_attribute   - Handle to the struct element attribute.
+//          name               - The attribute name.
+//          out_value          - A pointer to variable that will receive the
+//                               value. Not filled if false is returned.
+// Return value:
+//          Returns TRUE if the name maps to a boolean value, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetBooleanValue(
+    FPDF_STRUCTELEMENT_ATTR struct_attribute,
+    FPDF_BYTESTRING name,
+    FPDF_BOOL* out_value);
+
+// Experimental API.
+// Function: FPDF_StructElement_Attr_GetNumberValue
+//          Get the value of a number attribute in an attribute map by name as
+//          float. FPDF_StructElement_Attr_GetType() should have returned
+//          FPDF_OBJECT_NUMBER for this property.
+// Parameters:
+//          struct_attribute   - Handle to the struct element attribute.
+//          name               - The attribute name.
+//          out_value          - A pointer to variable that will receive the
+//                               value. Not filled if false is returned.
+// Return value:
+//          Returns TRUE if the name maps to a number value, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetNumberValue(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+                                       FPDF_BYTESTRING name,
+                                       float* out_value);
+
+// Experimental API.
+// Function: FPDF_StructElement_Attr_GetStringValue
+//          Get the value of a string attribute in an attribute map by name as
+//          string. FPDF_StructElement_Attr_GetType() should have returned
+//          FPDF_OBJECT_STRING or FPDF_OBJECT_NAME for this property.
+// Parameters:
+//          struct_attribute   - Handle to the struct element attribute.
+//          name               - The attribute name.
+//          buffer             - A buffer for holding the returned key in
+//                               UTF-16LE. This is only modified if |buflen| is
+//                               longer than the length of the key. Optional,
+//                               pass null to just retrieve the size of the
+//                               buffer needed.
+//          buflen             - The length of the buffer.
+//          out_buflen         - A pointer to variable that will receive the
+//                               minimum buffer size to contain the key. Not
+//                               filled if FALSE is returned.
+// Return value:
+//          Returns TRUE if the name maps to a string value, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetStringValue(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+                                       FPDF_BYTESTRING name,
+                                       void* buffer,
+                                       unsigned long buflen,
+                                       unsigned long* out_buflen);
+
+// Experimental API.
+// Function: FPDF_StructElement_Attr_GetBlobValue
+//          Get the value of a blob attribute in an attribute map by name as
+//          string.
+// Parameters:
+//          struct_attribute   - Handle to the struct element attribute.
+//          name               - The attribute name.
+//          buffer             - A buffer for holding the returned value. This
+//                               is only modified if |buflen| is at least as
+//                               long as the length of the value. Optional, pass
+//                               null to just retrieve the size of the buffer
+//                               needed.
+//          buflen             - The length of the buffer.
+//          out_buflen         - A pointer to variable that will receive the
+//                               minimum buffer size to contain the key. Not
+//                               filled if FALSE is returned.
+// Return value:
+//          Returns TRUE if the name maps to a string value, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetBlobValue(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+                                     FPDF_BYTESTRING name,
+                                     void* buffer,
+                                     unsigned long buflen,
+                                     unsigned long* out_buflen);
+
+// Experimental API.
+// Function: FPDF_StructElement_GetMarkedContentIdCount
+//          Get the count of marked content ids for a given element.
+// Parameters:
+//          struct_element -   Handle to the struct element.
+// Return value:
+//          The count of marked content ids or -1 if none exists.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_StructElement_GetMarkedContentIdCount(FPDF_STRUCTELEMENT struct_element);
+
+// Experimental API.
+// Function: FPDF_StructElement_GetMarkedContentIdAtIndex
+//          Get the marked content id at a given index for a given element.
+// Parameters:
+//          struct_element -   Handle to the struct element.
+//          index          -   The index of the marked content id, 0-based.
+// Return value:
+//          The marked content ID of the element. If no ID exists, returns
+//          -1.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_StructElement_GetMarkedContentIdAtIndex(FPDF_STRUCTELEMENT struct_element,
+                                             int index);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/public/fpdf_sysfontinfo.h b/public/fpdf_sysfontinfo.h
index 936de96..7dcf25d 100644
--- a/public/fpdf_sysfontinfo.h
+++ b/public/fpdf_sysfontinfo.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -19,8 +19,12 @@
 #define FXFONT_HANGEUL_CHARSET 129
 #define FXFONT_GB2312_CHARSET 134
 #define FXFONT_CHINESEBIG5_CHARSET 136
+#define FXFONT_GREEK_CHARSET 161
+#define FXFONT_VIETNAMESE_CHARSET 163
+#define FXFONT_HEBREW_CHARSET 177
 #define FXFONT_ARABIC_CHARSET 178
 #define FXFONT_CYRILLIC_CHARSET 204
+#define FXFONT_THAI_CHARSET 222
 #define FXFONT_EASTERNEUROPEAN_CHARSET 238
 
 /* Font pitch and family flags */
diff --git a/public/fpdf_text.h b/public/fpdf_text.h
index 65554e4..a5566a0 100644
--- a/public/fpdf_text.h
+++ b/public/fpdf_text.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -74,6 +74,36 @@
 FPDF_EXPORT unsigned int FPDF_CALLCONV
 FPDFText_GetUnicode(FPDF_TEXTPAGE text_page, int index);
 
+// Experimental API.
+// Function: FPDFText_IsGenerated
+//          Get if a character in a page is generated by PDFium.
+// Parameters:
+//          text_page   -   Handle to a text page information structure.
+//                          Returned by FPDFText_LoadPage function.
+//          index       -   Zero-based index of the character.
+// Return value:
+//          1 if the character is generated by PDFium.
+//          0 if the character is not generated by PDFium.
+//          -1 if there was an error.
+//
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFText_IsGenerated(FPDF_TEXTPAGE text_page, int index);
+
+// Experimental API.
+// Function: FPDFText_HasUnicodeMapError
+//          Get if a character in a page has an invalid unicode mapping.
+// Parameters:
+//          text_page   -   Handle to a text page information structure.
+//                          Returned by FPDFText_LoadPage function.
+//          index       -   Zero-based index of the character.
+// Return value:
+//          1 if the character has an invalid unicode mapping.
+//          0 if the character has no known unicode mapping issues.
+//          -1 if there was an error.
+//
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFText_HasUnicodeMapError(FPDF_TEXTPAGE text_page, int index);
+
 // Function: FPDFText_GetFontSize
 //          Get the font size of a particular character.
 // Parameters:
@@ -341,6 +371,10 @@
 //          trailing terminator.
 // Comments:
 //          This function ignores characters without unicode information.
+//          It returns all characters on the page, even those that are not
+//          visible when the page has a cropbox. To filter out the characters
+//          outside of the cropbox, use FPDF_GetPageBoundingBox() and
+//          FPDFText_GetCharBox().
 //
 FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetText(FPDF_TEXTPAGE text_page,
                                                int start_index,
@@ -348,20 +382,22 @@
                                                unsigned short* result);
 
 // Function: FPDFText_CountRects
-//          Count number of rectangular areas occupied by a segment of texts.
+//          Counts number of rectangular areas occupied by a segment of text,
+//          and caches the result for subsequent FPDFText_GetRect() calls.
 // Parameters:
 //          text_page   -   Handle to a text page information structure.
 //                          Returned by FPDFText_LoadPage function.
-//          start_index -   Index for the start characters.
-//          count       -   Number of characters.
+//          start_index -   Index for the start character.
+//          count       -   Number of characters, or -1 for all remaining.
 // Return value:
-//          Number of rectangles. Zero for error.
+//          Number of rectangles, 0 if text_page is null, or -1 on bad
+//          start_index.
 // Comments:
 //          This function, along with FPDFText_GetRect can be used by
 //          applications to detect the position on the page for a text segment,
-//          so proper areas can be highlighted. FPDFTEXT will automatically
-//          merge small character boxes into bigger one if those characters
-//          are on the same line and use same font settings.
+//          so proper areas can be highlighted. The FPDFText_* functions will
+//          automatically merge small character boxes into bigger one if those
+//          characters are on the same line and use same font settings.
 //
 FPDF_EXPORT int FPDF_CALLCONV FPDFText_CountRects(FPDF_TEXTPAGE text_page,
                                                   int start_index,
diff --git a/public/fpdf_thumbnail.h b/public/fpdf_thumbnail.h
index 8ee8580..27b6d49 100644
--- a/public/fpdf_thumbnail.h
+++ b/public/fpdf_thumbnail.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/public/fpdf_transformpage.h b/public/fpdf_transformpage.h
index 38ef113..d5c5daa 100644
--- a/public/fpdf_transformpage.h
+++ b/public/fpdf_transformpage.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -226,9 +226,9 @@
 //   page object - Handle to a page object. Returned by e.g.
 //                 FPDFPage_GetObject().
 //
-// Caller does not take ownership of the returned FPDF_CLIPPATH. Instead, it
-// remains valid until FPDF_ClosePage() is called for the page containing
-// page_object.
+// Returns the handle to the clip path, or NULL on failure. The caller does not
+// take ownership of the returned FPDF_CLIPPATH. Instead, it remains valid until
+// FPDF_ClosePage() is called for the page containing |page_object|.
 FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV
 FPDFPageObj_GetClipPath(FPDF_PAGEOBJECT page_object);
 
@@ -257,9 +257,9 @@
 //   path_index    - the index of a path.
 //   segment_index - the index of a segment.
 //
-// Returns the handle to the segment, or NULL on failure.  The caller does not
-// take ownership of the returned FPDF_CLIPPATH. Instead, it remains valid until
-// FPDF_ClosePage() is called for the page containing page_object.
+// Returns the handle to the segment, or NULL on failure. The caller does not
+// take ownership of the returned FPDF_PATHSEGMENT. Instead, it remains valid
+// until FPDF_ClosePage() is called for the page containing |clip_path|.
 FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
 FPDFClipPath_GetPathSegment(FPDF_CLIPPATH clip_path,
                             int path_index,
diff --git a/public/fpdfview.h b/public/fpdfview.h
index 0542b05..0c901a7 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -1,9 +1,17 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
+// 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.
 
@@ -50,10 +58,11 @@
   FPDF_TEXTRENDERMODE_LAST = FPDF_TEXTRENDERMODE_CLIP,
 } FPDF_TEXT_RENDERMODE;
 
-// PDF types - use incomplete types (never completed) just for API type safety.
+// PDF types - use incomplete types (never completed) to force API type safety.
 typedef struct fpdf_action_t__* FPDF_ACTION;
 typedef struct fpdf_annotation_t__* FPDF_ANNOTATION;
 typedef struct fpdf_attachment_t__* FPDF_ATTACHMENT;
+typedef struct fpdf_avail_t__* FPDF_AVAIL;
 typedef struct fpdf_bitmap_t__* FPDF_BITMAP;
 typedef struct fpdf_bookmark_t__* FPDF_BOOKMARK;
 typedef struct fpdf_clippath_t__* FPDF_CLIPPATH;
@@ -61,20 +70,24 @@
 typedef struct fpdf_document_t__* FPDF_DOCUMENT;
 typedef struct fpdf_font_t__* FPDF_FONT;
 typedef struct fpdf_form_handle_t__* FPDF_FORMHANDLE;
+typedef const struct fpdf_glyphpath_t__* FPDF_GLYPHPATH;
 typedef struct fpdf_javascript_action_t* FPDF_JAVASCRIPT_ACTION;
 typedef struct fpdf_link_t__* FPDF_LINK;
 typedef struct fpdf_page_t__* FPDF_PAGE;
 typedef struct fpdf_pagelink_t__* FPDF_PAGELINK;
 typedef struct fpdf_pageobject_t__* FPDF_PAGEOBJECT;  // (text, path, etc.)
 typedef struct fpdf_pageobjectmark_t__* FPDF_PAGEOBJECTMARK;
-typedef struct fpdf_pagerange_t__* FPDF_PAGERANGE;
+typedef const struct fpdf_pagerange_t__* FPDF_PAGERANGE;
 typedef const struct fpdf_pathsegment_t* FPDF_PATHSEGMENT;
-typedef void* FPDF_RECORDER;  // Passed into skia.
 typedef 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;
 typedef struct fpdf_textpage_t__* FPDF_TEXTPAGE;
 typedef struct fpdf_widget_t__* FPDF_WIDGET;
+typedef struct fpdf_xobject_t__* FPDF_XOBJECT;
 
 // Basic data types
 typedef int FPDF_BOOL;
@@ -93,13 +106,15 @@
 // 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.
-typedef const unsigned short* FPDF_WIDESTRING;
+// 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.
 // Note: although represented as a char*, string may be interpreted as
@@ -168,6 +183,17 @@
 // Const Pointer to FS_POINTF structure.
 typedef const FS_POINTF* FS_LPCPOINTF;
 
+typedef struct _FS_QUADPOINTSF {
+  FS_FLOAT x1;
+  FS_FLOAT y1;
+  FS_FLOAT x2;
+  FS_FLOAT y2;
+  FS_FLOAT x3;
+  FS_FLOAT y3;
+  FS_FLOAT x4;
+  FS_FLOAT y4;
+} FS_QUADPOINTSF;
+
 // Annotation enums.
 typedef int FPDF_ANNOTATION_SUBTYPE;
 typedef int FPDF_ANNOT_APPEARANCEMODE;
@@ -206,17 +232,14 @@
 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 {
+  // Anti-Grain Geometry - https://sourceforge.net/projects/agg/
+  FPDF_RENDERERTYPE_AGG = 0,
+  // Skia - https://skia.org/
+  FPDF_RENDERERTYPE_SKIA = 1,
+} FPDF_RENDERER_TYPE;
 
 // Process-wide options for initializing the library.
 typedef struct FPDF_LIBRARY_CONFIG_ {
@@ -232,7 +255,7 @@
 
   // Version 2.
 
-  // pointer to the v8::Isolate to use, or NULL to force PDFium to create one.
+  // Pointer to the v8::Isolate to use, or NULL to force PDFium to create one.
   void* m_pIsolate;
 
   // The embedder data slot to use in the v8::Isolate to store PDFium's
@@ -240,10 +263,25 @@
   // [0, |v8::Internals::kNumIsolateDataLots|). Note that 0 is fine for most
   // embedders.
   unsigned int m_v8EmbedderSlot;
+
+  // Version 3 - Experimental.
+
+  // Pointer to the V8::Platform to use.
+  void* m_pPlatform;
+
+  // Version 4 - Experimental.
+
+  // Explicit specification of core renderer to use. |m_RendererType| must be
+  // a valid value for |FPDF_LIBRARY_CONFIG| versions of this level or higher,
+  // or else the initialization will fail with an immediate crash.
+  // Note that use of a specified |FPDF_RENDERER_TYPE| value for which the
+  // corresponding render library is not included in the build will similarly
+  // fail with an immediate crash.
+  FPDF_RENDERER_TYPE m_RendererType;
 } FPDF_LIBRARY_CONFIG;
 
 // Function: FPDF_InitLibraryWithConfig
-//          Initialize the FPDFSDK library
+//          Initialize the PDFium library and allocate global resources for it.
 // Parameters:
 //          config - configuration information as above.
 // Return value:
@@ -254,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.
@@ -282,37 +336,9 @@
                                                      FPDF_BOOL enable);
 
 #if defined(_WIN32)
-#if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
-// Pointer to a helper function to make |font| with |text| of |text_length|
-// accessible when printing text with GDI. This is useful in sandboxed
-// environments where PDFium's access to GDI may be restricted.
-typedef void (*PDFiumEnsureTypefaceCharactersAccessible)(const LOGFONT* font,
-                                                         const wchar_t* text,
-                                                         size_t text_length);
-
-// Function: FPDF_SetTypefaceAccessibleFunc
-//          Set the function pointer that makes GDI fonts available in sandboxed
-//          environments. Experimental API.
-// Parameters:
-//          func -   A function pointer. See description above.
-// Return value:
-//          None.
-FPDF_EXPORT void FPDF_CALLCONV
-FPDF_SetTypefaceAccessibleFunc(PDFiumEnsureTypefaceCharactersAccessible func);
-
-// Function: FPDF_SetPrintTextWithGDI
-//          Set whether to use GDI to draw fonts when printing on Windows.
-//          Experimental API.
-// Parameters:
-//          use_gdi -   Set to true to enable printing text with GDI.
-// Return value:
-//          None.
-FPDF_EXPORT void FPDF_CALLCONV FPDF_SetPrintTextWithGDI(FPDF_BOOL use_gdi);
-#endif  // PDFIUM_PRINT_TEXT_WITH_GDI
-
+// Experimental API.
 // Function: FPDF_SetPrintMode
 //          Set printing mode when printing on Windows.
-//          Experimental API.
 // Parameters:
 //          mode - FPDF_PRINTMODE_EMF to output EMF (default)
 //                 FPDF_PRINTMODE_TEXTONLY to output text only (for charstream
@@ -325,6 +351,14 @@
 //                 PostScript via ExtEscape() in PASSTHROUGH mode.
 //                 FPDF_PRINTMODE_POSTSCRIPT3_PASSTHROUGH to output level 3
 //                 PostScript via ExtEscape() in PASSTHROUGH mode.
+//                 FPDF_PRINTMODE_EMF_IMAGE_MASKS to output EMF, with more
+//                 efficient processing of documents containing image masks.
+//                 FPDF_PRINTMODE_POSTSCRIPT3_TYPE42 to output level 3
+//                 PostScript with embedded Type 42 fonts, when applicable, into
+//                 EMF as a series of GDI comments.
+//                 FPDF_PRINTMODE_POSTSCRIPT3_TYPE42_PASSTHROUGH to output level
+//                 3 PostScript with embedded Type 42 fonts, when applicable,
+//                 via ExtEscape() in PASSTHROUGH mode.
 // Return value:
 //          True if successful, false if unsuccessful (typically invalid input).
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SetPrintMode(int mode);
@@ -344,6 +378,8 @@
 //          If this function fails, you can use FPDF_GetLastError() to retrieve
 //          the reason why it failed.
 //
+//          The encoding for |file_path| is UTF-8.
+//
 //          The encoding for |password| can be either UTF-8 or Latin-1. PDFs,
 //          depending on the security handler revision, will only accept one or
 //          the other encoding. If |password|'s encoding and the PDF's expected
@@ -376,6 +412,33 @@
 FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
 FPDF_LoadMemDocument(const void* data_buf, int size, FPDF_BYTESTRING password);
 
+// Experimental API.
+// Function: FPDF_LoadMemDocument64
+//          Open and load a PDF document from memory.
+// Parameters:
+//          data_buf    -   Pointer to a buffer containing the PDF document.
+//          size        -   Number of bytes in the PDF document.
+//          password    -   A string used as the password for the PDF file.
+//                          If no password is needed, empty or NULL can be used.
+// Return value:
+//          A handle to the loaded document, or NULL on failure.
+// Comments:
+//          The memory buffer must remain valid when the document is open.
+//          The loaded document can be closed by FPDF_CloseDocument.
+//          If this function fails, you can use FPDF_GetLastError() to retrieve
+//          the reason why it failed.
+//
+//          See the comments for FPDF_LoadDocument() regarding the encoding for
+//          |password|.
+// Notes:
+//          If PDFium is built with the XFA module, the application should call
+//          FPDF_LoadXFA() function after the PDF document loaded to support XFA
+//          fields defined in the fpdfformfill.h file.
+FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
+FPDF_LoadMemDocument64(const void* data_buf,
+                       size_t size,
+                       FPDF_BYTESTRING password);
+
 // Structure for custom file access.
 typedef struct {
   // File length, in bytes.
@@ -385,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,
@@ -551,12 +614,13 @@
 //          A 32-bit integer indicating error code as defined above.
 // Comments:
 //          If the previous SDK call succeeded, the return value of this
-//          function is not defined.
+//          function is not defined. This function only works in conjunction
+//          with APIs that mention FPDF_GetLastError() in their documentation.
 FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetLastError();
 
+// Experimental API.
 // Function: FPDF_DocumentHasValidCrossReferenceTable
 //          Whether the document's cross reference table is valid or not.
-//          Experimental API.
 // Parameters:
 //          document    -   Handle to a document. Returned by FPDF_LoadDocument.
 // Return value:
@@ -569,6 +633,25 @@
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDF_DocumentHasValidCrossReferenceTable(FPDF_DOCUMENT document);
 
+// Experimental API.
+// Function: FPDF_GetTrailerEnds
+//          Get the byte offsets of trailer ends.
+// Parameters:
+//          document    -   Handle to document. Returned by FPDF_LoadDocument().
+//          buffer      -   The address of a buffer that receives the
+//                          byte offsets.
+//          length      -   The size, in ints, of |buffer|.
+// Return value:
+//          Returns the number of ints in the buffer on success, 0 on error.
+//
+// |buffer| is an array of integers that describes the exact byte offsets of the
+// trailer ends in the document. If |length| is less than the returned length,
+// or |document| or |buffer| is NULL, |buffer| will not be modified.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_GetTrailerEnds(FPDF_DOCUMENT document,
+                    unsigned int* buffer,
+                    unsigned long length);
+
 // Function: FPDF_GetDocPermission
 //          Get file permission flags of the document.
 // Parameters:
@@ -707,7 +790,8 @@
 //
 // Set if annotations are to be rendered.
 #define FPDF_ANNOT 0x01
-// Set if using text rendering optimized for LCD display.
+// Set if using text rendering optimized for LCD display. This flag will only
+// take effect if anti-aliasing is enabled for text.
 #define FPDF_LCD_TEXT 0x02
 // Don't use the native text output available on some platforms
 #define FPDF_NO_NATIVETEXT 0x04
@@ -723,7 +807,8 @@
 #define FPDF_RENDER_FORCEHALFTONE 0x400
 // Render for printing.
 #define FPDF_PRINTING 0x800
-// Set to disable anti-aliasing on text.
+// Set to disable anti-aliasing on text. This flag will also disable LCD
+// optimization for text rendering.
 #define FPDF_RENDER_NO_SMOOTHTEXT 0x1000
 // Set to disable anti-aliasing on images.
 #define FPDF_RENDER_NO_SMOOTHIMAGE 0x2000
@@ -732,6 +817,19 @@
 // Set whether to render in a reverse Byte order, this flag is only used when
 // rendering to a bitmap.
 #define FPDF_REVERSE_BYTE_ORDER 0x10
+// Set whether fill paths need to be stroked. This flag is only used when
+// FPDF_COLORSCHEME is passed in, since with a single fill color for paths the
+// boundaries of adjacent fill paths are less visible.
+#define FPDF_CONVERT_FILL_TO_STROKE 0x20
+
+// Struct for color scheme.
+// Each should be a 32-bit value specifying the color, in 8888 ARGB format.
+typedef struct FPDF_COLORSCHEME_ {
+  FPDF_DWORD path_fill_color;
+  FPDF_DWORD path_stroke_color;
+  FPDF_DWORD text_fill_color;
+  FPDF_DWORD text_stroke_color;
+} FPDF_COLORSCHEME;
 
 #ifdef _WIN32
 // Function: FPDF_RenderPage
@@ -825,10 +923,21 @@
                                 const FS_RECTF* clipping,
                                 int flags);
 
-#ifdef _SKIA_SUPPORT_
-FPDF_EXPORT FPDF_RECORDER FPDF_CALLCONV FPDF_RenderPageSkp(FPDF_PAGE page,
-                                                           int size_x,
-                                                           int size_y);
+#if defined(_SKIA_SUPPORT_)
+// Experimental API.
+// 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:
+//          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
@@ -993,9 +1102,15 @@
 //                          above.
 //          first_scan  -   A pointer to the first byte of the first line if
 //                          using an external buffer. If this parameter is NULL,
-//                          then the a new buffer will be created.
-//          stride      -   Number of bytes for each scan line, for external
-//                          buffer only.
+//                          then a new buffer will be created.
+//          stride      -   Number of bytes for each scan line. The value must
+//                          be 0 or greater. When the value is 0,
+//                          FPDFBitmap_CreateEx() will automatically calculate
+//                          the appropriate value using |width| and |format|.
+//                          When using an external buffer, it is recommended for
+//                          the caller to pass in the value.
+//                          When not using an external buffer, it is recommended
+//                          for the caller to pass in 0.
 // Return value:
 //          The bitmap handle, or NULL if parameter error or out of memory.
 // Comments:
@@ -1004,9 +1119,11 @@
 //          function can be used in any place that a FPDF_BITMAP handle is
 //          required.
 //
-//          If an external buffer is used, then the application should destroy
-//          the buffer by itself. FPDFBitmap_Destroy function will not destroy
-//          the buffer.
+//          If an external buffer is used, then the caller should destroy the
+//          buffer. FPDFBitmap_Destroy() will not destroy the buffer.
+//
+//          It is recommended to use FPDFBitmap_GetStride() to get the stride
+//          value.
 FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_CreateEx(int width,
                                                           int height,
                                                           int format,
@@ -1070,8 +1187,7 @@
 //          then manipulate any color and/or alpha values for any pixels in the
 //          bitmap.
 //
-//          The data is in BGRA format. Where the A maybe unused if alpha was
-//          not specified.
+//          Use FPDFBitmap_GetFormat() to find out the format of the data.
 FPDF_EXPORT void* FPDF_CALLCONV FPDFBitmap_GetBuffer(FPDF_BITMAP bitmap);
 
 // Function: FPDFBitmap_GetWidth
@@ -1142,9 +1258,9 @@
 FPDF_EXPORT FPDF_PAGERANGE FPDF_CALLCONV
 FPDF_VIEWERREF_GetPrintPageRange(FPDF_DOCUMENT document);
 
+// Experimental API.
 // Function: FPDF_VIEWERREF_GetPrintPageRangeCount
 //          Returns the number of elements in a FPDF_PAGERANGE.
-//          Experimental API.
 // Parameters:
 //          pagerange   -   Handle to the page range.
 // Return value:
@@ -1152,9 +1268,9 @@
 FPDF_EXPORT size_t FPDF_CALLCONV
 FPDF_VIEWERREF_GetPrintPageRangeCount(FPDF_PAGERANGE pagerange);
 
+// Experimental API.
 // Function: FPDF_VIEWERREF_GetPrintPageRangeElement
 //          Returns an element from a FPDF_PAGERANGE.
-//          Experimental API.
 // Parameters:
 //          pagerange   -   Handle to the page range.
 //          index       -   Index of the element.
@@ -1240,6 +1356,65 @@
                                                       void* buffer,
                                                       long* buflen);
 
+// Experimental API.
+// Function: FPDF_GetXFAPacketCount
+//          Get the number of valid packets in the XFA entry.
+// Parameters:
+//          document - Handle to the document.
+// Return value:
+//          The number of valid packets, or -1 on error.
+FPDF_EXPORT int FPDF_CALLCONV FPDF_GetXFAPacketCount(FPDF_DOCUMENT document);
+
+// Experimental API.
+// Function: FPDF_GetXFAPacketName
+//          Get the name of a packet in the XFA array.
+// Parameters:
+//          document - Handle to the document.
+//          index    - Index number of the packet. 0 for the first packet.
+//          buffer   - Buffer for holding the name of the XFA packet.
+//          buflen   - Length of |buffer| in bytes.
+// Return value:
+//          The length of the packet name in bytes, or 0 on error.
+//
+// |document| must be valid and |index| must be in the range [0, N), where N is
+// the value returned by FPDF_GetXFAPacketCount().
+// |buffer| is only modified if it is non-NULL and |buflen| is greater than or
+// equal to the length of the packet name. The packet name includes a
+// terminating NUL character. |buffer| is unmodified on error.
+FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetXFAPacketName(
+    FPDF_DOCUMENT document,
+    int index,
+    void* buffer,
+    unsigned long buflen);
+
+// Experimental API.
+// Function: FPDF_GetXFAPacketContent
+//          Get the content of a packet in the XFA array.
+// Parameters:
+//          document   - Handle to the document.
+//          index      - Index number of the packet. 0 for the first packet.
+//          buffer     - Buffer for holding the content of the XFA packet.
+//          buflen     - Length of |buffer| in bytes.
+//          out_buflen - Pointer to the variable that will receive the minimum
+//                       buffer size needed to contain the content of the XFA
+//                       packet.
+// Return value:
+//          Whether the operation succeeded or not.
+//
+// |document| must be valid and |index| must be in the range [0, N), where N is
+// the value returned by FPDF_GetXFAPacketCount(). |out_buflen| must not be
+// NULL. When the aforementioned arguments are valid, the operation succeeds,
+// and |out_buflen| receives the content size. |buffer| is only modified if
+// |buffer| is non-null and long enough to contain the content. Callers must
+// check both the return value and the input |buflen| is no less than the
+// returned |out_buflen| before using the data in |buffer|.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_GetXFAPacketContent(
+    FPDF_DOCUMENT document,
+    int index,
+    void* buffer,
+    unsigned long buflen,
+    unsigned long* out_buflen);
+
 #ifdef PDF_ENABLE_V8
 // Function: FPDF_GetRecommendedV8Flags
 //          Returns a space-separated string of command line flags that are
@@ -1251,6 +1426,21 @@
 //          NUL-terminated string of the form "--flag1 --flag2".
 //          The caller must not attempt to modify or free the result.
 FPDF_EXPORT const char* FPDF_CALLCONV FPDF_GetRecommendedV8Flags();
+
+// Experimental API.
+// Function: FPDF_GetArrayBufferAllocatorSharedInstance()
+//          Helper function for initializing V8 isolates that will
+//          use PDFium's internal memory management.
+// Parameters:
+//          None.
+// Return Value:
+//          Pointer to a suitable v8::ArrayBuffer::Allocator, returned
+//          as void for C compatibility.
+// Notes:
+//          Use is optional, but allows external creation of isolates
+//          matching the ones PDFium will make when none is provided
+//          via |FPDF_LIBRARY_CONFIG::m_pIsolate|.
+FPDF_EXPORT void* FPDF_CALLCONV FPDF_GetArrayBufferAllocatorSharedInstance();
 #endif  // PDF_ENABLE_V8
 
 #ifdef PDF_ENABLE_XFA
diff --git a/samples/BUILD.gn b/samples/BUILD.gn
index 581d05a..981750b 100644
--- a/samples/BUILD.gn
+++ b/samples/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2014 PDFium Authors. All rights reserved.
+# Copyright 2014 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -13,35 +13,37 @@
 config("pdfium_samples_config") {
   cflags = []
   ldflags = []
-  defines = [
-    "PNG_PREFIX",
-    "PNG_USE_READ_MACROS",
-  ]
+  defines = []
   include_dirs = [ ".." ]
   if (pdf_use_skia) {
     defines += [ "PDF_ENABLE_SKIA" ]
-  } else if (pdf_use_skia_paths) {
-    defines += [ "PDF_ENABLE_SKIA_PATHS" ]
   }
   if (is_asan) {
     defines += [ "PDF_ENABLE_ASAN" ]
   }
-
+  if (pdf_use_partition_alloc) {
+    defines += [ "PDF_USE_PARTITION_ALLOC" ]
+  }
   if (enable_callgrind) {
     defines += [ "ENABLE_CALLGRIND" ]
   }
+  if (build_with_chromium) {
+    defines += [ "BUILD_WITH_CHROMIUM" ]
+  }
 }
 
 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
@@ -50,13 +52,20 @@
   deps = [
     "../:pdfium_public_headers",
     "../fpdfsdk",
-    "../testing/:test_support",
+    "../testing:test_support",
     "../testing/image_diff",
     "../third_party:pdfium_base",
     "//build/win:default_exe_manifest",
   ]
   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",
@@ -65,7 +74,14 @@
     include_dirs = [ "//v8" ]
     configs += [ "//v8:external_startup_data" ]
   }
-  if (pdf_use_skia || pdf_use_skia_paths) {
+  if (pdf_use_skia) {
     deps += [ "//skia" ]
+    if (build_with_chromium) {
+      sources += [
+        "chromium_support/discardable_memory_allocator.cc",
+        "chromium_support/discardable_memory_allocator.h",
+      ]
+      deps += [ "//base/test:test_support" ]
+    }
   }
 }
diff --git a/samples/chromium_support/DEPS b/samples/chromium_support/DEPS
new file mode 100644
index 0000000..75f0926
--- /dev/null
+++ b/samples/chromium_support/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  '+base/test/test_discardable_memory_allocator.h',
+]
diff --git a/samples/chromium_support/discardable_memory_allocator.cc b/samples/chromium_support/discardable_memory_allocator.cc
new file mode 100644
index 0000000..f89bb1c
--- /dev/null
+++ b/samples/chromium_support/discardable_memory_allocator.cc
@@ -0,0 +1,16 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "samples/chromium_support/discardable_memory_allocator.h"
+
+#include "base/test/test_discardable_memory_allocator.h"
+
+namespace chromium_support {
+
+void InitializeDiscardableMemoryAllocator() {
+  static base::TestDiscardableMemoryAllocator test_memory_allocator;
+  base::DiscardableMemoryAllocator::SetInstance(&test_memory_allocator);
+}
+
+}  // namespace chromium_support
diff --git a/samples/chromium_support/discardable_memory_allocator.h b/samples/chromium_support/discardable_memory_allocator.h
new file mode 100644
index 0000000..1f91f70
--- /dev/null
+++ b/samples/chromium_support/discardable_memory_allocator.h
@@ -0,0 +1,14 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SAMPLES_CHROMIUM_SUPPORT_DISCARDABLE_MEMORY_ALLOCATOR_H_
+#define SAMPLES_CHROMIUM_SUPPORT_DISCARDABLE_MEMORY_ALLOCATOR_H_
+
+namespace chromium_support {
+
+void InitializeDiscardableMemoryAllocator();
+
+}  // namespace chromium_support
+
+#endif  // SAMPLES_CHROMIUM_SUPPORT_DISCARDABLE_MEMORY_ALLOCATOR_H_
diff --git a/samples/helpers/dump.cc b/samples/helpers/dump.cc
new file mode 100644
index 0000000..e2a8469
--- /dev/null
+++ b/samples/helpers/dump.cc
@@ -0,0 +1,227 @@
+// Copyright 2018 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "samples/helpers/dump.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <string>
+#include <utility>
+
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdf_doc.h"
+#include "public/fpdf_transformpage.h"
+#include "testing/fx_string_testhelpers.h"
+
+using GetBoxInfoFunc =
+    std::function<bool(FPDF_PAGE, float*, float*, float*, float*)>;
+
+namespace {
+
+std::wstring ConvertToWString(const unsigned short* buf,
+                              unsigned long buf_size) {
+  std::wstring result;
+  result.reserve(buf_size);
+  std::copy(buf, buf + buf_size, std::back_inserter(result));
+  return result;
+}
+
+void DumpBoxInfo(GetBoxInfoFunc func,
+                 const char* box_type,
+                 FPDF_PAGE page,
+                 int page_idx) {
+  FS_RECTF rect;
+  bool ret = func(page, &rect.left, &rect.bottom, &rect.right, &rect.top);
+  if (!ret) {
+    printf("Page %d: No %s.\n", page_idx, box_type);
+    return;
+  }
+  printf("Page %d: %s: %0.2f %0.2f %0.2f %0.2f\n", page_idx, box_type,
+         rect.left, rect.bottom, rect.right, rect.top);
+}
+
+void DumpStructureElementAttributes(FPDF_STRUCTELEMENT_ATTR attr, int indent) {
+  static const size_t kBufSize = 1024;
+  int count = FPDF_StructElement_Attr_GetCount(attr);
+  for (int i = 0; i < count; i++) {
+    char name[kBufSize] = {};
+    unsigned long len = ULONG_MAX;
+    if (!FPDF_StructElement_Attr_GetName(attr, i, name, sizeof(name), &len)) {
+      printf("%*s FPDF_StructElement_Attr_GetName failed for %d\n", indent, "",
+             i);
+      continue;
+    }
+
+    FPDF_OBJECT_TYPE type = FPDF_StructElement_Attr_GetType(attr, name);
+    if (type == FPDF_OBJECT_BOOLEAN) {
+      int value;
+      if (!FPDF_StructElement_Attr_GetBooleanValue(attr, name, &value)) {
+        printf("%*s %s: Failed FPDF_StructElement_Attr_GetBooleanValue\n",
+               indent, "", name);
+        continue;
+      }
+      printf("%*s %s: %d\n", indent, "", name, value);
+    } else if (type == FPDF_OBJECT_NUMBER) {
+      float value;
+      if (!FPDF_StructElement_Attr_GetNumberValue(attr, name, &value)) {
+        printf("%*s %s: Failed FPDF_StructElement_Attr_GetNumberValue\n",
+               indent, "", name);
+        continue;
+      }
+      printf("%*s %s: %f\n", indent, "", name, value);
+    } else if (type == FPDF_OBJECT_STRING || type == FPDF_OBJECT_NAME) {
+      unsigned short buffer[kBufSize] = {};
+      if (!FPDF_StructElement_Attr_GetStringValue(attr, name, buffer,
+                                                  sizeof(buffer), &len)) {
+        printf("%*s %s: Failed FPDF_StructElement_Attr_GetStringValue\n",
+               indent, "", name);
+        continue;
+      }
+      printf("%*s %s: %ls\n", indent, "", name,
+             ConvertToWString(buffer, len).c_str());
+    } else if (type == FPDF_OBJECT_UNKNOWN) {
+      printf("%*s %s: FPDF_OBJECT_UNKNOWN\n", indent, "", name);
+    } else {
+      printf("%*s %s: NOT_YET_IMPLEMENTED: %d\n", indent, "", name, type);
+    }
+  }
+}
+
+}  // namespace
+
+void DumpChildStructure(FPDF_STRUCTELEMENT child, int indent) {
+  static const size_t kBufSize = 1024;
+  unsigned short buf[kBufSize];
+  unsigned long len = FPDF_StructElement_GetType(child, buf, kBufSize);
+  if (len > 0) {
+    printf("%*s S: %ls\n", indent * 2, "", ConvertToWString(buf, len).c_str());
+  }
+
+  int attr_count = FPDF_StructElement_GetAttributeCount(child);
+  for (int i = 0; i < attr_count; i++) {
+    FPDF_STRUCTELEMENT_ATTR child_attr =
+        FPDF_StructElement_GetAttributeAtIndex(child, i);
+    if (!child_attr) {
+      continue;
+    }
+    printf("%*s A[%d]:\n", indent * 2, "", i);
+    DumpStructureElementAttributes(child_attr, indent * 2 + 2);
+  }
+
+  memset(buf, 0, sizeof(buf));
+  len = FPDF_StructElement_GetActualText(child, buf, kBufSize);
+  if (len > 0) {
+    printf("%*s ActualText: %ls\n", indent * 2, "",
+           ConvertToWString(buf, len).c_str());
+  }
+
+  memset(buf, 0, sizeof(buf));
+  len = FPDF_StructElement_GetAltText(child, buf, kBufSize);
+  if (len > 0) {
+    printf("%*s AltText: %ls\n", indent * 2, "",
+           ConvertToWString(buf, len).c_str());
+  }
+
+  memset(buf, 0, sizeof(buf));
+  len = FPDF_StructElement_GetID(child, buf, kBufSize);
+  if (len > 0) {
+    printf("%*s ID: %ls\n", indent * 2, "", ConvertToWString(buf, len).c_str());
+  }
+
+  memset(buf, 0, sizeof(buf));
+  len = FPDF_StructElement_GetLang(child, buf, kBufSize);
+  if (len > 0) {
+    printf("%*s Lang: %ls\n", indent * 2, "",
+           ConvertToWString(buf, len).c_str());
+  }
+
+  int mcid = FPDF_StructElement_GetMarkedContentID(child);
+  if (mcid != -1) {
+    printf("%*s MCID: %d\n", indent * 2, "", mcid);
+  }
+
+  FPDF_STRUCTELEMENT parent = FPDF_StructElement_GetParent(child);
+  if (parent) {
+    memset(buf, 0, sizeof(buf));
+    len = FPDF_StructElement_GetID(parent, buf, kBufSize);
+    if (len > 0) {
+      printf("%*s Parent ID: %ls\n", indent * 2, "",
+             ConvertToWString(buf, len).c_str());
+    }
+  }
+
+  memset(buf, 0, sizeof(buf));
+  len = FPDF_StructElement_GetTitle(child, buf, kBufSize);
+  if (len > 0) {
+    printf("%*s Title: %ls\n", indent * 2, "",
+           ConvertToWString(buf, len).c_str());
+  }
+
+  memset(buf, 0, sizeof(buf));
+  len = FPDF_StructElement_GetObjType(child, buf, kBufSize);
+  if (len > 0) {
+    printf("%*s Type: %ls\n", indent * 2, "",
+           ConvertToWString(buf, len).c_str());
+  }
+
+  for (int i = 0; i < FPDF_StructElement_CountChildren(child); ++i) {
+    FPDF_STRUCTELEMENT sub_child = FPDF_StructElement_GetChildAtIndex(child, i);
+    // 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) {
+      continue;
+    }
+
+    DumpChildStructure(sub_child, indent + 1);
+  }
+}
+
+void DumpPageInfo(FPDF_PAGE page, int page_idx) {
+  DumpBoxInfo(&FPDFPage_GetMediaBox, "MediaBox", page, page_idx);
+  DumpBoxInfo(&FPDFPage_GetCropBox, "CropBox", page, page_idx);
+  DumpBoxInfo(&FPDFPage_GetBleedBox, "BleedBox", page, page_idx);
+  DumpBoxInfo(&FPDFPage_GetTrimBox, "TrimBox", page, page_idx);
+  DumpBoxInfo(&FPDFPage_GetArtBox, "ArtBox", page, page_idx);
+}
+
+void DumpPageStructure(FPDF_PAGE page, int page_idx) {
+  ScopedFPDFStructTree tree(FPDF_StructTree_GetForPage(page));
+  if (!tree) {
+    fprintf(stderr, "Failed to load struct tree for page %d\n", page_idx);
+    return;
+  }
+
+  printf("Structure Tree for Page %d\n", page_idx);
+  for (int i = 0; i < FPDF_StructTree_CountChildren(tree.get()); ++i) {
+    FPDF_STRUCTELEMENT child = FPDF_StructTree_GetChildAtIndex(tree.get(), i);
+    if (!child) {
+      fprintf(stderr, "Failed to load child %d for page %d\n", i, page_idx);
+      continue;
+    }
+    DumpChildStructure(child, 0);
+  }
+  printf("\n\n");
+}
+
+void DumpMetaData(FPDF_DOCUMENT doc) {
+  static constexpr const char* kMetaTags[] = {
+      "Title",   "Author",   "Subject",      "Keywords",
+      "Creator", "Producer", "CreationDate", "ModDate"};
+  for (const char* meta_tag : kMetaTags) {
+    char meta_buffer[4096];
+    unsigned long len =
+        FPDF_GetMetaText(doc, meta_tag, meta_buffer, sizeof(meta_buffer));
+    if (!len) {
+      continue;
+    }
+
+    auto* meta_string = reinterpret_cast<unsigned short*>(meta_buffer);
+    printf("%-12s = %ls (%lu bytes)\n", meta_tag,
+           GetPlatformWString(meta_string).c_str(), len);
+  }
+}
diff --git a/samples/helpers/dump.h b/samples/helpers/dump.h
new file mode 100644
index 0000000..6682b29
--- /dev/null
+++ b/samples/helpers/dump.h
@@ -0,0 +1,15 @@
+// Copyright 2018 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SAMPLES_HELPERS_DUMP_H_
+#define SAMPLES_HELPERS_DUMP_H_
+
+#include "public/fpdfview.h"
+
+void DumpChildStructure(FPDF_STRUCTELEMENT child, int indent);
+void DumpPageInfo(FPDF_PAGE page, int page_idx);
+void DumpPageStructure(FPDF_PAGE page, int page_idx);
+void DumpMetaData(FPDF_DOCUMENT doc);
+
+#endif  // SAMPLES_HELPERS_DUMP_H_
diff --git a/samples/helpers/event.cc b/samples/helpers/event.cc
new file mode 100644
index 0000000..48491e8
--- /dev/null
+++ b/samples/helpers/event.cc
@@ -0,0 +1,194 @@
+// Copyright 2018 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "samples/helpers/event.h"
+
+#include <stdio.h>
+
+#include <string>
+#include <vector>
+
+#include "public/fpdf_fwlevent.h"
+#include "public/fpdfview.h"
+#include "testing/fx_string_testhelpers.h"
+
+namespace {
+
+uint32_t GetModifiers(std::string modifiers_string) {
+  uint32_t modifiers = 0;
+  if (modifiers_string.find("shift") != std::string::npos) {
+    modifiers |= FWL_EVENTFLAG_ShiftKey;
+  }
+  if (modifiers_string.find("control") != std::string::npos) {
+    modifiers |= FWL_EVENTFLAG_ControlKey;
+  }
+  if (modifiers_string.find("alt") != std::string::npos) {
+    modifiers |= FWL_EVENTFLAG_AltKey;
+  }
+
+  return modifiers;
+}
+
+void SendCharCodeEvent(FPDF_FORMHANDLE form,
+                       FPDF_PAGE page,
+                       const std::vector<std::string>& tokens) {
+  if (tokens.size() != 2) {
+    fprintf(stderr, "charcode: bad args\n");
+    return;
+  }
+
+  int charcode = atoi(tokens[1].c_str());
+  FORM_OnChar(form, page, charcode, 0);
+}
+
+void SendKeyCodeEvent(FPDF_FORMHANDLE form,
+                      FPDF_PAGE page,
+                      const std::vector<std::string>& tokens) {
+  if (tokens.size() < 2 || tokens.size() > 3) {
+    fprintf(stderr, "keycode: bad args\n");
+    return;
+  }
+
+  int keycode = atoi(tokens[1].c_str());
+  uint32_t modifiers = tokens.size() >= 3 ? GetModifiers(tokens[2]) : 0;
+  FORM_OnKeyDown(form, page, keycode, modifiers);
+  FORM_OnKeyUp(form, page, keycode, modifiers);
+}
+
+void SendMouseDownEvent(FPDF_FORMHANDLE form,
+                        FPDF_PAGE page,
+                        const std::vector<std::string>& tokens) {
+  if (tokens.size() < 4 && tokens.size() > 5) {
+    fprintf(stderr, "mousedown: bad args\n");
+    return;
+  }
+
+  int x = atoi(tokens[2].c_str());
+  int y = atoi(tokens[3].c_str());
+  uint32_t modifiers = tokens.size() >= 5 ? GetModifiers(tokens[4]) : 0;
+
+  if (tokens[1] == "left") {
+    FORM_OnLButtonDown(form, page, modifiers, x, y);
+  } else if (tokens[1] == "right") {
+    FORM_OnRButtonDown(form, page, modifiers, x, y);
+  } else {
+    fprintf(stderr, "mousedown: bad button name\n");
+  }
+}
+
+void SendMouseUpEvent(FPDF_FORMHANDLE form,
+                      FPDF_PAGE page,
+                      const std::vector<std::string>& tokens) {
+  if (tokens.size() < 4 && tokens.size() > 5) {
+    fprintf(stderr, "mouseup: bad args\n");
+    return;
+  }
+
+  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") {
+    FORM_OnLButtonUp(form, page, modifiers, x, y);
+  } else if (tokens[1] == "right") {
+    FORM_OnRButtonUp(form, page, modifiers, x, y);
+  } else {
+    fprintf(stderr, "mouseup: bad button name\n");
+  }
+}
+
+void SendMouseDoubleClickEvent(FPDF_FORMHANDLE form,
+                               FPDF_PAGE page,
+                               const std::vector<std::string>& tokens) {
+  if (tokens.size() < 4 && tokens.size() > 5) {
+    fprintf(stderr, "mousedoubleclick: bad args\n");
+    return;
+  }
+
+  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") {
+    fprintf(stderr, "mousedoubleclick: bad button name\n");
+    return;
+  }
+  FORM_OnLButtonDoubleClick(form, page, modifiers, x, y);
+}
+
+void SendMouseMoveEvent(FPDF_FORMHANDLE form,
+                        FPDF_PAGE page,
+                        const std::vector<std::string>& tokens) {
+  if (tokens.size() != 3) {
+    fprintf(stderr, "mousemove: bad args\n");
+    return;
+  }
+
+  int x = atoi(tokens[1].c_str());
+  int y = atoi(tokens[2].c_str());
+  FORM_OnMouseMove(form, page, 0, x, y);
+}
+
+void SendMouseWheelEvent(FPDF_FORMHANDLE form,
+                         FPDF_PAGE page,
+                         const std::vector<std::string>& tokens) {
+  if (tokens.size() < 5 && tokens.size() > 6) {
+    fprintf(stderr, "mousewheel: bad args\n");
+    return;
+  }
+
+  const FS_POINTF point = {static_cast<float>(atoi(tokens[1].c_str())),
+                           static_cast<float>(atoi(tokens[2].c_str()))};
+  int delta_x = atoi(tokens[3].c_str());
+  int delta_y = atoi(tokens[4].c_str());
+  int modifiers = tokens.size() >= 6 ? GetModifiers(tokens[5]) : 0;
+  FORM_OnMouseWheel(form, page, modifiers, &point, delta_x, delta_y);
+}
+
+void SendFocusEvent(FPDF_FORMHANDLE form,
+                    FPDF_PAGE page,
+                    const std::vector<std::string>& tokens) {
+  if (tokens.size() != 3) {
+    fprintf(stderr, "focus: bad args\n");
+    return;
+  }
+
+  int x = atoi(tokens[1].c_str());
+  int y = atoi(tokens[2].c_str());
+  FORM_OnFocus(form, page, 0, x, y);
+}
+
+}  // namespace
+
+void SendPageEvents(FPDF_FORMHANDLE form,
+                    FPDF_PAGE page,
+                    const std::string& events,
+                    const std::function<void()>& idler) {
+  auto lines = StringSplit(events, '\n');
+  for (const auto& line : lines) {
+    auto command = StringSplit(line, '#');
+    if (command[0].empty()) {
+      continue;
+    }
+    auto tokens = StringSplit(command[0], ',');
+    if (tokens[0] == "charcode") {
+      SendCharCodeEvent(form, page, tokens);
+    } else if (tokens[0] == "keycode") {
+      SendKeyCodeEvent(form, page, tokens);
+    } else if (tokens[0] == "mousedown") {
+      SendMouseDownEvent(form, page, tokens);
+    } else if (tokens[0] == "mouseup") {
+      SendMouseUpEvent(form, page, tokens);
+    } else if (tokens[0] == "mousedoubleclick") {
+      SendMouseDoubleClickEvent(form, page, tokens);
+    } else if (tokens[0] == "mousemove") {
+      SendMouseMoveEvent(form, page, tokens);
+    } else if (tokens[0] == "mousewheel") {
+      SendMouseWheelEvent(form, page, tokens);
+    } else if (tokens[0] == "focus") {
+      SendFocusEvent(form, page, tokens);
+    } else {
+      fprintf(stderr, "Unrecognized event: %s\n", tokens[0].c_str());
+    }
+    idler();
+  }
+}
diff --git a/samples/helpers/event.h b/samples/helpers/event.h
new file mode 100644
index 0000000..021c075
--- /dev/null
+++ b/samples/helpers/event.h
@@ -0,0 +1,19 @@
+// Copyright 2018 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SAMPLES_HELPERS_EVENT_H_
+#define SAMPLES_HELPERS_EVENT_H_
+
+#include <functional>
+#include <string>
+
+#include "public/fpdf_formfill.h"
+#include "public/fpdfview.h"
+
+void SendPageEvents(FPDF_FORMHANDLE form,
+                    FPDF_PAGE page,
+                    const std::string& events,
+                    const std::function<void()>& idler);
+
+#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/helpers/write.cc b/samples/helpers/write.cc
new file mode 100644
index 0000000..13223d2
--- /dev/null
+++ b/samples/helpers/write.cc
@@ -0,0 +1,914 @@
+// Copyright 2018 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "samples/helpers/write.h"
+
+#include <limits.h>
+
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdf_annot.h"
+#include "public/fpdf_attachment.h"
+#include "public/fpdf_edit.h"
+#include "public/fpdf_thumbnail.h"
+#include "testing/fx_string_testhelpers.h"
+#include "testing/image_diff/image_diff_png.h"
+#include "third_party/base/notreached.h"
+
+#ifdef PDF_ENABLE_SKIA
+#include "third_party/skia/include/core/SkPicture.h"  // nogncheck
+#include "third_party/skia/include/core/SkStream.h"   // nogncheck
+#endif
+
+namespace {
+
+bool CheckDimensions(int stride, int width, int height) {
+  if (stride < 0 || width < 0 || height < 0) {
+    return false;
+  }
+  if (height > 0 && stride > INT_MAX / height) {
+    return false;
+  }
+  return true;
+}
+
+const char* AnnotSubtypeToCString(FPDF_ANNOTATION_SUBTYPE subtype) {
+  if (subtype == FPDF_ANNOT_TEXT) {
+    return "Text";
+  }
+  if (subtype == FPDF_ANNOT_LINK) {
+    return "Link";
+  }
+  if (subtype == FPDF_ANNOT_FREETEXT) {
+    return "FreeText";
+  }
+  if (subtype == FPDF_ANNOT_LINE) {
+    return "Line";
+  }
+  if (subtype == FPDF_ANNOT_SQUARE) {
+    return "Square";
+  }
+  if (subtype == FPDF_ANNOT_CIRCLE) {
+    return "Circle";
+  }
+  if (subtype == FPDF_ANNOT_POLYGON) {
+    return "Polygon";
+  }
+  if (subtype == FPDF_ANNOT_POLYLINE) {
+    return "PolyLine";
+  }
+  if (subtype == FPDF_ANNOT_HIGHLIGHT) {
+    return "Highlight";
+  }
+  if (subtype == FPDF_ANNOT_UNDERLINE) {
+    return "Underline";
+  }
+  if (subtype == FPDF_ANNOT_SQUIGGLY) {
+    return "Squiggly";
+  }
+  if (subtype == FPDF_ANNOT_STRIKEOUT) {
+    return "StrikeOut";
+  }
+  if (subtype == FPDF_ANNOT_STAMP) {
+    return "Stamp";
+  }
+  if (subtype == FPDF_ANNOT_CARET) {
+    return "Caret";
+  }
+  if (subtype == FPDF_ANNOT_INK) {
+    return "Ink";
+  }
+  if (subtype == FPDF_ANNOT_POPUP) {
+    return "Popup";
+  }
+  if (subtype == FPDF_ANNOT_FILEATTACHMENT) {
+    return "FileAttachment";
+  }
+  if (subtype == FPDF_ANNOT_SOUND) {
+    return "Sound";
+  }
+  if (subtype == FPDF_ANNOT_MOVIE) {
+    return "Movie";
+  }
+  if (subtype == FPDF_ANNOT_WIDGET) {
+    return "Widget";
+  }
+  if (subtype == FPDF_ANNOT_SCREEN) {
+    return "Screen";
+  }
+  if (subtype == FPDF_ANNOT_PRINTERMARK) {
+    return "PrinterMark";
+  }
+  if (subtype == FPDF_ANNOT_TRAPNET) {
+    return "TrapNet";
+  }
+  if (subtype == FPDF_ANNOT_WATERMARK) {
+    return "Watermark";
+  }
+  if (subtype == FPDF_ANNOT_THREED) {
+    return "3D";
+  }
+  if (subtype == FPDF_ANNOT_RICHMEDIA) {
+    return "RichMedia";
+  }
+  if (subtype == FPDF_ANNOT_XFAWIDGET) {
+    return "XFAWidget";
+  }
+  NOTREACHED_NORETURN();
+}
+
+void AppendFlagString(const char* flag, std::string* output) {
+  if (!output->empty()) {
+    *output += ", ";
+  }
+  *output += flag;
+}
+
+std::string AnnotFlagsToString(int flags) {
+  std::string str;
+  if (flags & FPDF_ANNOT_FLAG_INVISIBLE) {
+    AppendFlagString("Invisible", &str);
+  }
+  if (flags & FPDF_ANNOT_FLAG_HIDDEN) {
+    AppendFlagString("Hidden", &str);
+  }
+  if (flags & FPDF_ANNOT_FLAG_PRINT) {
+    AppendFlagString("Print", &str);
+  }
+  if (flags & FPDF_ANNOT_FLAG_NOZOOM) {
+    AppendFlagString("NoZoom", &str);
+  }
+  if (flags & FPDF_ANNOT_FLAG_NOROTATE) {
+    AppendFlagString("NoRotate", &str);
+  }
+  if (flags & FPDF_ANNOT_FLAG_NOVIEW) {
+    AppendFlagString("NoView", &str);
+  }
+  if (flags & FPDF_ANNOT_FLAG_READONLY) {
+    AppendFlagString("ReadOnly", &str);
+  }
+  if (flags & FPDF_ANNOT_FLAG_LOCKED) {
+    AppendFlagString("Locked", &str);
+  }
+  if (flags & FPDF_ANNOT_FLAG_TOGGLENOVIEW) {
+    AppendFlagString("ToggleNoView", &str);
+  }
+  return str;
+}
+
+const char* PageObjectTypeToCString(int type) {
+  if (type == FPDF_PAGEOBJ_TEXT) {
+    return "Text";
+  }
+  if (type == FPDF_PAGEOBJ_PATH) {
+    return "Path";
+  }
+  if (type == FPDF_PAGEOBJ_IMAGE) {
+    return "Image";
+  }
+  if (type == FPDF_PAGEOBJ_SHADING) {
+    return "Shading";
+  }
+  if (type == FPDF_PAGEOBJ_FORM) {
+    return "Form";
+  }
+  NOTREACHED_NORETURN();
+}
+
+std::vector<uint8_t> EncodePng(pdfium::span<const uint8_t> input,
+                               int width,
+                               int height,
+                               int stride,
+                               int format) {
+  std::vector<uint8_t> png;
+  switch (format) {
+    case FPDFBitmap_Unknown:
+      break;
+    case FPDFBitmap_Gray:
+      png = image_diff_png::EncodeGrayPNG(input, width, height, stride);
+      break;
+    case FPDFBitmap_BGR:
+      png = image_diff_png::EncodeBGRPNG(input, width, height, stride);
+      break;
+    case FPDFBitmap_BGRx:
+      png = image_diff_png::EncodeBGRAPNG(input, width, height, stride,
+                                          /*discard_transparency=*/true);
+      break;
+    case FPDFBitmap_BGRA:
+      png = image_diff_png::EncodeBGRAPNG(input, width, height, stride,
+                                          /*discard_transparency=*/false);
+      break;
+    default:
+      NOTREACHED_NORETURN();
+  }
+  return png;
+}
+
+#ifdef _WIN32
+int CALLBACK EnhMetaFileProc(HDC hdc,
+                             HANDLETABLE* handle_table,
+                             const ENHMETARECORD* record,
+                             int objects_count,
+                             LPARAM param) {
+  std::vector<const ENHMETARECORD*>& items =
+      *reinterpret_cast<std::vector<const ENHMETARECORD*>*>(param);
+  items.push_back(record);
+  return 1;
+}
+#endif  // _WIN32
+
+std::string GeneratePageOutputFilename(const char* pdf_name,
+                                       int page_num,
+                                       const char* extension) {
+  std::ostringstream stream;
+  stream << pdf_name << "." << page_num << "." << extension;
+  std::string filename = stream.str();
+  if (filename.size() >= 256) {
+    fprintf(stderr, "Filename %s is too long\n", filename.c_str());
+    return std::string();
+  }
+
+  return filename;
+}
+
+std::string GenerateImageOutputFilename(const char* pdf_name,
+                                        int page_num,
+                                        int image_num,
+                                        const char* extension) {
+  std::ostringstream stream;
+  stream << pdf_name << "." << page_num << "." << image_num << "." << extension;
+  std::string filename = stream.str();
+  if (filename.size() >= 256) {
+    fprintf(stderr, "Filename %s for saving image is too long.\n",
+            filename.c_str());
+    return std::string();
+  }
+
+  return filename;
+}
+
+}  // namespace
+
+std::string WritePpm(const char* pdf_name,
+                     int num,
+                     void* buffer_void,
+                     int stride,
+                     int width,
+                     int height) {
+  if (!CheckDimensions(stride, width, height)) {
+    return "";
+  }
+
+  int out_len = width * height;
+  if (out_len > INT_MAX / 3) {
+    return "";
+  }
+
+  out_len *= 3;
+
+  std::string filename = GeneratePageOutputFilename(pdf_name, num, "ppm");
+  if (filename.empty()) {
+    return std::string();
+  }
+  FILE* fp = fopen(filename.c_str(), "wb");
+  if (!fp) {
+    return std::string();
+  }
+
+  fprintf(fp, "P6\n# PDF test render\n%d %d\n255\n", width, height);
+  // Source data is B, G, R, unused.
+  // Dest data is R, G, B.
+  const uint8_t* buffer = reinterpret_cast<const uint8_t*>(buffer_void);
+  std::vector<uint8_t> result(out_len);
+  for (int h = 0; h < height; ++h) {
+    const uint8_t* src_line = buffer + (stride * h);
+    uint8_t* dest_line = result.data() + (width * h * 3);
+    for (int w = 0; w < width; ++w) {
+      // R
+      dest_line[w * 3] = src_line[(w * 4) + 2];
+      // G
+      dest_line[(w * 3) + 1] = src_line[(w * 4) + 1];
+      // B
+      dest_line[(w * 3) + 2] = src_line[w * 4];
+    }
+  }
+  if (fwrite(result.data(), out_len, 1, fp) != 1) {
+    fprintf(stderr, "Failed to write to %s\n", filename.c_str());
+  }
+
+  fclose(fp);
+  return filename;
+}
+
+void WriteText(FPDF_TEXTPAGE textpage, const char* pdf_name, int num) {
+  std::string filename = GeneratePageOutputFilename(pdf_name, num, "txt");
+  if (filename.empty()) {
+    return;
+  }
+  FILE* fp = fopen(filename.c_str(), "w");
+  if (!fp) {
+    fprintf(stderr, "Failed to open %s for output\n", filename.c_str());
+    return;
+  }
+
+  // Output in UTF32-LE.
+  uint32_t bom = 0x0000FEFF;
+  if (fwrite(&bom, sizeof(bom), 1, fp) != 1) {
+    fprintf(stderr, "Failed to write to %s\n", filename.c_str());
+    (void)fclose(fp);
+    return;
+  }
+
+  for (int i = 0; i < FPDFText_CountChars(textpage); i++) {
+    uint32_t c = FPDFText_GetUnicode(textpage, i);
+    if (fwrite(&c, sizeof(c), 1, fp) != 1) {
+      fprintf(stderr, "Failed to write to %s\n", filename.c_str());
+      break;
+    }
+  }
+  (void)fclose(fp);
+}
+
+void WriteAnnot(FPDF_PAGE page, const char* pdf_name, int num) {
+  // Open the output text file.
+  std::string filename = GeneratePageOutputFilename(pdf_name, num, "annot.txt");
+  if (filename.empty()) {
+    return;
+  }
+  FILE* fp = fopen(filename.c_str(), "w");
+  if (!fp) {
+    fprintf(stderr, "Failed to open %s for output\n", filename.c_str());
+    return;
+  }
+
+  int annot_count = FPDFPage_GetAnnotCount(page);
+  fprintf(fp, "Number of annotations: %d\n\n", annot_count);
+
+  // Iterate through all annotations on this page.
+  for (int i = 0; i < annot_count; ++i) {
+    // Retrieve the annotation object and its subtype.
+    fprintf(fp, "Annotation #%d:\n", i + 1);
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, i));
+    if (!annot) {
+      fprintf(fp, "Failed to retrieve annotation!\n\n");
+      continue;
+    }
+
+    FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot.get());
+    fprintf(fp, "Subtype: %s\n", AnnotSubtypeToCString(subtype));
+
+    // Retrieve the annotation flags.
+    fprintf(fp, "Flags set: %s\n",
+            AnnotFlagsToString(FPDFAnnot_GetFlags(annot.get())).c_str());
+
+    // Retrieve the annotation's object count and object types.
+    const int obj_count = FPDFAnnot_GetObjectCount(annot.get());
+    fprintf(fp, "Number of objects: %d\n", obj_count);
+    if (obj_count > 0) {
+      fprintf(fp, "Object types: ");
+      for (int j = 0; j < obj_count; ++j) {
+        const char* type = PageObjectTypeToCString(
+            FPDFPageObj_GetType(FPDFAnnot_GetObject(annot.get(), j)));
+        fprintf(fp, "%s  ", type);
+      }
+      fprintf(fp, "\n");
+    }
+
+    // Retrieve the annotation's color and interior color.
+    unsigned int R;
+    unsigned int G;
+    unsigned int B;
+    unsigned int A;
+    if (FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, &R, &G, &B,
+                           &A)) {
+      fprintf(fp, "Color in RGBA: %d %d %d %d\n", R, G, B, A);
+    } else {
+      fprintf(fp, "Failed to retrieve color.\n");
+    }
+    if (FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_InteriorColor, &R,
+                           &G, &B, &A)) {
+      fprintf(fp, "Interior color in RGBA: %d %d %d %d\n", R, G, B, A);
+    } else {
+      fprintf(fp, "Failed to retrieve interior color.\n");
+    }
+
+    // Retrieve the annotation's contents and author.
+    static constexpr char kContentsKey[] = "Contents";
+    static constexpr char kAuthorKey[] = "T";
+    unsigned long length_bytes =
+        FPDFAnnot_GetStringValue(annot.get(), kContentsKey, nullptr, 0);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    FPDFAnnot_GetStringValue(annot.get(), kContentsKey, buf.data(),
+                             length_bytes);
+    fprintf(fp, "Content: %ls\n", GetPlatformWString(buf.data()).c_str());
+    length_bytes =
+        FPDFAnnot_GetStringValue(annot.get(), kAuthorKey, nullptr, 0);
+    buf = GetFPDFWideStringBuffer(length_bytes);
+    FPDFAnnot_GetStringValue(annot.get(), kAuthorKey, buf.data(), length_bytes);
+    fprintf(fp, "Author: %ls\n", GetPlatformWString(buf.data()).c_str());
+
+    // Retrieve the annotation's quadpoints if it is a markup annotation.
+    if (FPDFAnnot_HasAttachmentPoints(annot.get())) {
+      size_t qp_count = FPDFAnnot_CountAttachmentPoints(annot.get());
+      fprintf(fp, "Number of quadpoints sets: %zu\n", qp_count);
+
+      // Iterate through all quadpoints of the current annotation
+      for (size_t j = 0; j < qp_count; ++j) {
+        FS_QUADPOINTSF quadpoints;
+        if (FPDFAnnot_GetAttachmentPoints(annot.get(), j, &quadpoints)) {
+          fprintf(fp,
+                  "Quadpoints set #%zu: (%.3f, %.3f), (%.3f, %.3f), "
+                  "(%.3f, %.3f), (%.3f, %.3f)\n",
+                  j + 1, quadpoints.x1, quadpoints.y1, quadpoints.x2,
+                  quadpoints.y2, quadpoints.x3, quadpoints.y3, quadpoints.x4,
+                  quadpoints.y4);
+        } else {
+          fprintf(fp, "Failed to retrieve quadpoints set #%zu.\n", j + 1);
+        }
+      }
+    }
+
+    // Retrieve the annotation's rectangle coordinates.
+    FS_RECTF rect;
+    if (FPDFAnnot_GetRect(annot.get(), &rect)) {
+      fprintf(fp, "Rectangle: l - %.3f, b - %.3f, r - %.3f, t - %.3f\n\n",
+              rect.left, rect.bottom, rect.right, rect.top);
+    } else {
+      fprintf(fp, "Failed to retrieve annotation rectangle.\n");
+    }
+  }
+
+  (void)fclose(fp);
+}
+
+std::string WritePng(const char* pdf_name,
+                     int num,
+                     void* buffer,
+                     int stride,
+                     int width,
+                     int height) {
+  if (!CheckDimensions(stride, width, height)) {
+    return "";
+  }
+
+  auto input =
+      pdfium::make_span(static_cast<uint8_t*>(buffer), stride * height);
+  std::vector<uint8_t> png_encoding =
+      EncodePng(input, width, height, stride, FPDFBitmap_BGRA);
+  if (png_encoding.empty()) {
+    fprintf(stderr, "Failed to convert bitmap to PNG\n");
+    return "";
+  }
+
+  std::string filename = GeneratePageOutputFilename(pdf_name, num, "png");
+  if (filename.empty()) {
+    return std::string();
+  }
+  FILE* fp = fopen(filename.c_str(), "wb");
+  if (!fp) {
+    fprintf(stderr, "Failed to open %s for output\n", filename.c_str());
+    return std::string();
+  }
+
+  size_t bytes_written =
+      fwrite(&png_encoding.front(), 1, png_encoding.size(), fp);
+  if (bytes_written != png_encoding.size()) {
+    fprintf(stderr, "Failed to write to %s\n", filename.c_str());
+  }
+
+  (void)fclose(fp);
+  return filename;
+}
+
+#ifdef _WIN32
+std::string WriteBmp(const char* pdf_name,
+                     int num,
+                     void* buffer,
+                     int stride,
+                     int width,
+                     int height) {
+  if (!CheckDimensions(stride, width, height)) {
+    return std::string();
+  }
+
+  int out_len = stride * height;
+  if (out_len > INT_MAX / 3) {
+    return std::string();
+  }
+
+  std::string filename = GeneratePageOutputFilename(pdf_name, num, "bmp");
+  if (filename.empty()) {
+    return std::string();
+  }
+  FILE* fp = fopen(filename.c_str(), "wb");
+  if (!fp) {
+    return std::string();
+  }
+
+  BITMAPINFO bmi = {};
+  bmi.bmiHeader.biSize = sizeof(bmi) - sizeof(RGBQUAD);
+  bmi.bmiHeader.biWidth = width;
+  bmi.bmiHeader.biHeight = -height;  // top-down image
+  bmi.bmiHeader.biPlanes = 1;
+  bmi.bmiHeader.biBitCount = 32;
+  bmi.bmiHeader.biCompression = BI_RGB;
+  bmi.bmiHeader.biSizeImage = 0;
+
+  BITMAPFILEHEADER file_header = {};
+  file_header.bfType = 0x4d42;
+  file_header.bfSize = sizeof(file_header) + bmi.bmiHeader.biSize + out_len;
+  file_header.bfOffBits = file_header.bfSize - out_len;
+
+  if (fwrite(&file_header, sizeof(file_header), 1, fp) != 1 ||
+      fwrite(&bmi, bmi.bmiHeader.biSize, 1, fp) != 1 ||
+      fwrite(buffer, out_len, 1, fp) != 1) {
+    fprintf(stderr, "Failed to write to %s\n", filename.c_str());
+  }
+  fclose(fp);
+  return filename;
+}
+
+void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) {
+  std::string filename = GeneratePageOutputFilename(pdf_name, num, "emf");
+  if (filename.empty()) {
+    return;
+  }
+
+  HDC dc = CreateEnhMetaFileA(nullptr, filename.c_str(), nullptr, nullptr);
+
+  int width = static_cast<int>(FPDF_GetPageWidthF(page));
+  int height = static_cast<int>(FPDF_GetPageHeightF(page));
+  HRGN rgn = CreateRectRgn(0, 0, width, height);
+  SelectClipRgn(dc, rgn);
+  DeleteObject(rgn);
+
+  SelectObject(dc, GetStockObject(NULL_PEN));
+  SelectObject(dc, GetStockObject(WHITE_BRUSH));
+  // If a PS_NULL pen is used, the dimensions of the rectangle are 1 pixel less.
+  Rectangle(dc, 0, 0, width + 1, height + 1);
+
+  FPDF_RenderPage(dc, page, 0, 0, width, height, 0, FPDF_ANNOT | FPDF_PRINTING);
+
+  DeleteEnhMetaFile(CloseEnhMetaFile(dc));
+}
+
+void WritePS(FPDF_PAGE page, const char* pdf_name, int num) {
+  std::string filename = GeneratePageOutputFilename(pdf_name, num, "ps");
+  if (filename.empty()) {
+    return;
+  }
+  FILE* fp = fopen(filename.c_str(), "wb");
+  if (!fp) {
+    return;
+  }
+
+  HDC dc = CreateEnhMetaFileA(nullptr, nullptr, nullptr, nullptr);
+
+  int width = static_cast<int>(FPDF_GetPageWidthF(page));
+  int height = static_cast<int>(FPDF_GetPageHeightF(page));
+  FPDF_RenderPage(dc, page, 0, 0, width, height, 0, FPDF_ANNOT | FPDF_PRINTING);
+
+  HENHMETAFILE emf = CloseEnhMetaFile(dc);
+  std::vector<const ENHMETARECORD*> items;
+  EnumEnhMetaFile(nullptr, emf, &EnhMetaFileProc, &items, nullptr);
+  for (const ENHMETARECORD* record : items) {
+    if (record->iType != EMR_GDICOMMENT) {
+      continue;
+    }
+
+    const auto* comment = reinterpret_cast<const EMRGDICOMMENT*>(record);
+    const char* data = reinterpret_cast<const char*>(comment->Data);
+    uint16_t size = *reinterpret_cast<const uint16_t*>(data);
+    if (fwrite(data + sizeof(uint16_t), size, 1, fp) != 1) {
+      fprintf(stderr, "Failed to write to %s\n", filename.c_str());
+      break;
+    }
+  }
+  fclose(fp);
+  DeleteEnhMetaFile(emf);
+}
+#endif  // _WIN32
+
+#ifdef PDF_ENABLE_SKIA
+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 nullptr;
+  }
+
+  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  // PDF_ENABLE_SKIA
+
+enum class ThumbnailDecodeType { kBitmap, kRawStream, kDecodedStream };
+
+bool GetThumbnailFilename(char* name_buf,
+                          size_t name_buf_size,
+                          const char* pdf_name,
+                          int page_num,
+                          ThumbnailDecodeType decode_type) {
+  const char* format;
+  switch (decode_type) {
+    case ThumbnailDecodeType::kBitmap:
+      format = "%s.thumbnail.%d.png";
+      break;
+    case ThumbnailDecodeType::kDecodedStream:
+      format = "%s.thumbnail.decoded.%d.bin";
+      break;
+    case ThumbnailDecodeType::kRawStream:
+      format = "%s.thumbnail.raw.%d.bin";
+      break;
+  }
+
+  int chars_formatted =
+      snprintf(name_buf, name_buf_size, format, pdf_name, page_num);
+  if (chars_formatted < 0 ||
+      static_cast<size_t>(chars_formatted) >= name_buf_size) {
+    fprintf(stderr, "Filename %s for saving is too long.\n", name_buf);
+    return false;
+  }
+
+  return true;
+}
+
+void WriteBufferToFile(const void* buf,
+                       size_t buflen,
+                       const char* filename,
+                       const char* filetype) {
+  FILE* fp = fopen(filename, "wb");
+  if (!fp) {
+    fprintf(stderr, "Failed to open %s for saving %s.", filename, filetype);
+    return;
+  }
+
+  size_t bytes_written = fwrite(buf, 1, buflen, fp);
+  if (bytes_written == buflen) {
+    fprintf(stderr, "Successfully wrote %s %s.\n", filetype, filename);
+  } 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) {
+    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)) {
+    return png_encoding;
+  }
+
+  auto input = pdfium::make_span(
+      static_cast<const uint8_t*>(FPDFBitmap_GetBuffer(bitmap.get())),
+      stride * height);
+
+  png_encoding = EncodePng(input, width, height, stride, format);
+  return png_encoding;
+}
+
+void WriteAttachments(FPDF_DOCUMENT doc, const std::string& name) {
+  for (int i = 0; i < FPDFDoc_GetAttachmentCount(doc); ++i) {
+    FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(doc, i);
+
+    // Retrieve the attachment file name.
+    std::string attachment_name;
+    unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
+    if (length_bytes) {
+      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) {
+        attachment_name = GetPlatformString(buf.data());
+      }
+    }
+    if (attachment_name.empty()) {
+      fprintf(stderr, "Attachment #%d has an empty file name.\n", i + 1);
+      continue;
+    }
+
+    // Calculate the full attachment file name.
+    char save_name[256];
+    int chars_formatted =
+        snprintf(save_name, sizeof(save_name), "%s.attachment.%s", name.c_str(),
+                 attachment_name.c_str());
+    if (chars_formatted < 0 ||
+        static_cast<size_t>(chars_formatted) >= sizeof(save_name)) {
+      fprintf(stderr, "Filename %s is too long.\n", save_name);
+      continue;
+    }
+
+    // Retrieve the attachment.
+    if (!FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes)) {
+      fprintf(stderr, "Failed to retrieve attachment \"%s\".\n",
+              attachment_name.c_str());
+      continue;
+    }
+
+    std::vector<char> data_buf(length_bytes);
+    if (length_bytes) {
+      unsigned long actual_length_bytes;
+      if (!FPDFAttachment_GetFile(attachment, data_buf.data(), length_bytes,
+                                  &actual_length_bytes)) {
+        fprintf(stderr, "Failed to retrieve attachment \"%s\".\n",
+                attachment_name.c_str());
+        continue;
+      }
+    }
+
+    // Write the attachment file. Since a PDF document could have 0-byte files
+    // as attachments, we should allow saving the 0-byte attachments to files.
+    WriteBufferToFile(data_buf.data(), length_bytes, save_name, "attachment");
+  }
+}
+
+void WriteImages(FPDF_PAGE page, const char* pdf_name, int page_num) {
+  for (int i = 0; i < FPDFPage_CountObjects(page); ++i) {
+    FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i);
+    if (FPDFPageObj_GetType(obj) != FPDF_PAGEOBJ_IMAGE) {
+      continue;
+    }
+
+    ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
+    if (!bitmap) {
+      fprintf(stderr, "Image object #%d on page #%d has an empty bitmap.\n",
+              i + 1, page_num + 1);
+      continue;
+    }
+
+    std::string filename =
+        GenerateImageOutputFilename(pdf_name, page_num, i, "png");
+    if (filename.empty()) {
+      continue;
+    }
+
+    std::vector<uint8_t> png_encoding = EncodeBitmapToPng(std::move(bitmap));
+    if (png_encoding.empty()) {
+      fprintf(stderr,
+              "Failed to convert image object #%d, on page #%d to png.\n",
+              i + 1, page_num + 1);
+      continue;
+    }
+
+    WriteBufferToFile(&png_encoding.front(), png_encoding.size(),
+                      filename.c_str(), "image");
+  }
+}
+
+void WriteRenderedImages(FPDF_DOCUMENT doc,
+                         FPDF_PAGE page,
+                         const char* pdf_name,
+                         int page_num) {
+  for (int i = 0; i < FPDFPage_CountObjects(page); ++i) {
+    FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i);
+    if (FPDFPageObj_GetType(obj) != FPDF_PAGEOBJ_IMAGE) {
+      continue;
+    }
+
+    ScopedFPDFBitmap bitmap(FPDFImageObj_GetRenderedBitmap(doc, page, obj));
+    if (!bitmap) {
+      fprintf(stderr, "Image object #%d on page #%d has an empty bitmap.\n",
+              i + 1, page_num + 1);
+      continue;
+    }
+
+    std::string filename =
+        GenerateImageOutputFilename(pdf_name, page_num, i, "png");
+    if (filename.empty()) {
+      continue;
+    }
+
+    std::vector<uint8_t> png_encoding = EncodeBitmapToPng(std::move(bitmap));
+    if (png_encoding.empty()) {
+      fprintf(stderr,
+              "Failed to convert image object #%d, on page #%d to png.\n",
+              i + 1, page_num + 1);
+      continue;
+    }
+
+    WriteBufferToFile(&png_encoding.front(), png_encoding.size(),
+                      filename.c_str(), "image");
+  }
+}
+
+void WriteDecodedThumbnailStream(FPDF_PAGE page,
+                                 const char* pdf_name,
+                                 int page_num) {
+  char filename[256];
+  if (!GetThumbnailFilename(filename, sizeof(filename), pdf_name, page_num,
+                            ThumbnailDecodeType::kDecodedStream)) {
+    return;
+  }
+
+  unsigned long decoded_data_size =
+      FPDFPage_GetDecodedThumbnailData(page, nullptr, 0u);
+
+  // Only continue if there actually is a thumbnail for this page
+  if (decoded_data_size == 0) {
+    fprintf(stderr, "Failed to get decoded thumbnail for page #%d.\n",
+            page_num + 1);
+    return;
+  }
+
+  std::vector<uint8_t> thumb_buf(decoded_data_size);
+  if (FPDFPage_GetDecodedThumbnailData(
+          page, thumb_buf.data(), decoded_data_size) != decoded_data_size) {
+    fprintf(stderr, "Failed to get decoded thumbnail data for %s.\n", filename);
+    return;
+  }
+
+  WriteBufferToFile(thumb_buf.data(), decoded_data_size, filename,
+                    "decoded thumbnail");
+}
+
+void WriteRawThumbnailStream(FPDF_PAGE page,
+                             const char* pdf_name,
+                             int page_num) {
+  char filename[256];
+  if (!GetThumbnailFilename(filename, sizeof(filename), pdf_name, page_num,
+                            ThumbnailDecodeType::kRawStream)) {
+    return;
+  }
+
+  unsigned long raw_data_size = FPDFPage_GetRawThumbnailData(page, nullptr, 0u);
+
+  // Only continue if there actually is a thumbnail for this page
+  if (raw_data_size == 0) {
+    fprintf(stderr, "Failed to get raw thumbnail data for page #%d.\n",
+            page_num + 1);
+    return;
+  }
+
+  std::vector<uint8_t> thumb_buf(raw_data_size);
+  if (FPDFPage_GetRawThumbnailData(page, thumb_buf.data(), raw_data_size) !=
+      raw_data_size) {
+    fprintf(stderr, "Failed to get raw thumbnail data for %s.\n", filename);
+    return;
+  }
+
+  WriteBufferToFile(thumb_buf.data(), raw_data_size, filename, "raw thumbnail");
+}
+
+void WriteThumbnail(FPDF_PAGE page, const char* pdf_name, int page_num) {
+  char filename[256];
+  if (!GetThumbnailFilename(filename, sizeof(filename), pdf_name, page_num,
+                            ThumbnailDecodeType::kBitmap)) {
+    return;
+  }
+
+  ScopedFPDFBitmap thumb_bitmap(FPDFPage_GetThumbnailAsBitmap(page));
+  if (!thumb_bitmap) {
+    fprintf(stderr, "Thumbnail of page #%d has an empty bitmap.\n",
+            page_num + 1);
+    return;
+  }
+
+  std::vector<uint8_t> png_encoding =
+      EncodeBitmapToPng(std::move(thumb_bitmap));
+  if (png_encoding.empty()) {
+    fprintf(stderr, "Failed to convert thumbnail of page #%d to png.\n",
+            page_num + 1);
+    return;
+  }
+
+  WriteBufferToFile(&png_encoding.front(), png_encoding.size(), filename,
+                    "thumbnail");
+}
diff --git a/samples/helpers/write.h b/samples/helpers/write.h
new file mode 100644
index 0000000..b23760e
--- /dev/null
+++ b/samples/helpers/write.h
@@ -0,0 +1,69 @@
+// Copyright 2018 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SAMPLES_HELPERS_WRITE_H_
+#define SAMPLES_HELPERS_WRITE_H_
+
+#include <memory>
+#include <string>
+
+#include "public/fpdfview.h"
+
+#ifdef PDF_ENABLE_SKIA
+class SkPicture;
+class SkWStream;
+#endif  // PDF_ENABLE_SKIA
+
+std::string WritePpm(const char* pdf_name,
+                     int num,
+                     void* buffer_void,
+                     int stride,
+                     int width,
+                     int height);
+void WriteText(FPDF_TEXTPAGE textpage, const char* pdf_name, int num);
+void WriteAnnot(FPDF_PAGE page, const char* pdf_name, int num);
+std::string WritePng(const char* pdf_name,
+                     int num,
+                     void* buffer,
+                     int stride,
+                     int width,
+                     int height);
+
+#ifdef _WIN32
+std::string WriteBmp(const char* pdf_name,
+                     int num,
+                     void* buffer,
+                     int stride,
+                     int width,
+                     int height);
+void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num);
+void WritePS(FPDF_PAGE page, const char* pdf_name, int num);
+#endif  // _WIN32
+
+#ifdef PDF_ENABLE_SKIA
+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
+
+void WriteAttachments(FPDF_DOCUMENT doc, const std::string& name);
+void WriteImages(FPDF_PAGE page, const char* pdf_name, int page_num);
+void WriteRenderedImages(FPDF_DOCUMENT doc,
+                         FPDF_PAGE page,
+                         const char* pdf_name,
+                         int page_num);
+void WriteDecodedThumbnailStream(FPDF_PAGE page,
+                                 const char* pdf_name,
+                                 int page_num);
+void WriteRawThumbnailStream(FPDF_PAGE page,
+                             const char* pdf_name,
+                             int page_num);
+void WriteThumbnail(FPDF_PAGE page, const char* pdf_name, int page_num);
+
+#endif  // SAMPLES_HELPERS_WRITE_H_
diff --git a/samples/pdfium_test.cc b/samples/pdfium_test.cc
index dc539dd..282c539 100644
--- a/samples/pdfium_test.cc
+++ b/samples/pdfium_test.cc
@@ -1,12 +1,15 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright 2010 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <limits.h>
+#include <locale.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include <functional>
 #include <iterator>
 #include <map>
 #include <memory>
@@ -14,14 +17,10 @@
 #include <string>
 #include <vector>
 
-#if defined PDF_ENABLE_SKIA && !defined _SKIA_SUPPORT_
+#if defined(PDF_ENABLE_SKIA) && !defined(_SKIA_SUPPORT_)
 #define _SKIA_SUPPORT_
 #endif
 
-#if defined PDF_ENABLE_SKIA_PATHS && !defined _SKIA_SUPPORT_PATHS_
-#define _SKIA_SUPPORT_PATHS_
-#endif
-
 #include "public/cpp/fpdf_scopers.h"
 #include "public/fpdf_annot.h"
 #include "public/fpdf_attachment.h"
@@ -33,31 +32,67 @@
 #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"
 #include "testing/test_loader.h"
 #include "testing/utils/file_util.h"
 #include "testing/utils/hash.h"
 #include "testing/utils/path_service.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#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
+#endif  // _WIN32
 
 #ifdef ENABLE_CALLGRIND
 #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
+#endif  // PDF_ENABLE_SKIA
+
 #ifdef PDF_ENABLE_V8
 #include "testing/v8_initializer.h"
 #include "v8/include/libplatform/libplatform.h"
-#include "v8/include/v8.h"
+#include "v8/include/v8-array-buffer.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-platform.h"
+#include "v8/include/v8-snapshot.h"
 #endif  // PDF_ENABLE_V8
 
 #ifdef _WIN32
@@ -66,7 +101,7 @@
 #define R_OK 4
 #endif
 
-// wordexp is a POSIX function that is only available on OSX and non-Android
+// wordexp is a POSIX function that is only available on macOS and non-Android
 // Linux platforms.
 #if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__))
 #define WORDEXP_AVAILABLE
@@ -76,26 +111,41 @@
 #include <wordexp.h>
 #endif  // WORDEXP_AVAILABLE
 
-enum OutputFormat {
-  OUTPUT_NONE,
-  OUTPUT_PAGEINFO,
-  OUTPUT_STRUCTURE,
-  OUTPUT_TEXT,
-  OUTPUT_PPM,
-  OUTPUT_PNG,
-  OUTPUT_ANNOT,
+namespace {
+
+enum class RendererType {
+  kDefault,
+  kAgg,
 #ifdef _WIN32
-  OUTPUT_BMP,
-  OUTPUT_EMF,
-  OUTPUT_PS2,
-  OUTPUT_PS3,
-#endif
-#ifdef PDF_ENABLE_SKIA
-  OUTPUT_SKP,
-#endif
+  kGdi,
+#endif  // _WIN32
+#if defined(PDF_ENABLE_SKIA)
+  kSkia,
+#endif  // defined(PDF_ENABLE_SKIA)
 };
 
-namespace {
+enum class OutputFormat {
+  kNone,
+  kPageInfo,
+  kStructure,
+  kText,
+  kPpm,
+  kPng,
+  kAnnot,
+#ifdef _WIN32
+  kBmp,
+  kEmf,
+  kPs2,
+  kPs3,
+  kPs3Type42,
+#endif
+#ifdef PDF_ENABLE_SKIA
+  kSkp,
+#ifdef _WIN32
+  kXps,
+#endif  // _WIN32
+#endif  // PDF_ENABLE_SKIA
+};
 
 struct Options {
   Options() = default;
@@ -108,6 +158,8 @@
   bool lcd_text = false;
   bool no_nativetext = false;
   bool grayscale = false;
+  bool forced_color = false;
+  bool fill_to_stroke = false;
   bool limit_cache = false;
   bool force_halftone = false;
   bool printing = false;
@@ -117,11 +169,14 @@
   bool reverse_byte_order = false;
   bool save_attachments = false;
   bool save_images = false;
+  bool save_rendered_images = false;
   bool save_thumbnails = false;
   bool save_thumbnails_decoded = false;
   bool save_thumbnails_raw = false;
+  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.
 #ifdef PDF_ENABLE_XFA
   bool disable_xfa = false;
 #endif  // PDF_ENABLE_XFA
@@ -134,7 +189,8 @@
 #if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__))
   bool linux_no_system_fonts = false;
 #endif
-  OutputFormat output_format = OUTPUT_NONE;
+  bool croscore_font_names = false;
+  OutputFormat output_format = OutputFormat::kNone;
   std::string password;
   std::string scale_factor_as_string;
   std::string exe_path;
@@ -153,6 +209,8 @@
     flags |= FPDF_NO_NATIVETEXT;
   if (options.grayscale)
     flags |= FPDF_GRAYSCALE;
+  if (options.fill_to_stroke)
+    flags |= FPDF_CONVERT_FILL_TO_STROKE;
   if (options.limit_cache)
     flags |= FPDF_RENDER_LIMITEDIMAGECACHE;
   if (options.force_halftone)
@@ -170,7 +228,7 @@
   return flags;
 }
 
-Optional<std::string> ExpandDirectoryPath(const std::string& path) {
+absl::optional<std::string> ExpandDirectoryPath(const std::string& path) {
 #if defined(WORDEXP_AVAILABLE)
   wordexp_t expansion;
   if (wordexp(path.c_str(), &expansion, 0) != 0 || expansion.we_wordc < 1) {
@@ -179,7 +237,7 @@
   }
   // Need to contruct the return value before hand, since wordfree will
   // deallocate |expansion|.
-  Optional<std::string> ret_val = {expansion.we_wordv[0]};
+  absl::optional<std::string> ret_val = {expansion.we_wordv[0]};
   wordfree(&expansion);
   return ret_val;
 #else
@@ -187,7 +245,7 @@
 #endif  // WORDEXP_AVAILABLE
 }
 
-Optional<const char*> GetCustomFontPath(const Options& options) {
+absl::optional<const char*> GetCustomFontPath(const Options& options) {
 #if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__))
   // Set custom font path to an empty path. This avoids the fallback to default
   // font paths.
@@ -197,7 +255,7 @@
 
   // No custom font path. Use default.
   if (options.font_directory.empty())
-    return pdfium::nullopt;
+    return absl::nullopt;
 
   // Set custom font path to |options.font_directory|.
   return options.font_directory.c_str();
@@ -218,13 +276,18 @@
   return static_cast<FPDF_FORMFILLINFO_PDFiumTest*>(form_fill_info);
 }
 
-void OutputMD5Hash(const char* file_name, const uint8_t* buffer, int len) {
+void OutputMD5Hash(const char* file_name, pdfium::span<const uint8_t> output) {
   // Get the MD5 hash and write it to stdout.
-  std::string hash = GenerateMD5Base16(buffer, len);
+  std::string hash = GenerateMD5Base16(output);
   printf("MD5:%s:%s\n", file_name, hash.c_str());
 }
 
 #ifdef PDF_ENABLE_V8
+
+struct V8IsolateDeleter {
+  inline void operator()(v8::Isolate* ptr) { ptr->Dispose(); }
+};
+
 // These example JS platform callback handlers are entirely optional,
 // and exist here to show the flow of information from a document back
 // to the embedder.
@@ -328,6 +391,22 @@
 }
 #endif  // PDF_ENABLE_V8
 
+#ifdef PDF_ENABLE_XFA
+FPDF_BOOL ExamplePopupMenu(FPDF_FORMFILLINFO* pInfo,
+                           FPDF_PAGE page,
+                           FPDF_WIDGET always_null,
+                           int flags,
+                           float x,
+                           float y) {
+  printf("Popup: x=%2.1f, y=%2.1f, flags=0x%x\n", x, y, flags);
+  return true;
+}
+#endif  // PDF_ENABLE_XFA
+
+void ExampleNamedAction(FPDF_FORMFILLINFO* pInfo, FPDF_BYTESTRING name) {
+  printf("Execute named action: %s\n", name);
+}
+
 void ExampleUnsupportedHandler(UNSUPPORT_INFO*, int type) {
   std::string feature = "Unknown";
   switch (type) {
@@ -372,17 +451,6 @@
   printf("Unsupported feature: %s.\n", feature.c_str());
 }
 
-// |arg| is expected to be "--key=value", and |key| is "--key=".
-bool ParseSwitchKeyValue(const std::string& arg,
-                         const std::string& key,
-                         std::string* value) {
-  if (arg.size() <= key.size() || arg.compare(0, key.size(), key) != 0)
-    return false;
-
-  *value = arg.substr(key.size());
-  return true;
-}
-
 bool ParseCommandLine(const std::vector<std::string>& args,
                       Options* options,
                       std::vector<std::string>* files) {
@@ -410,6 +478,10 @@
       options->no_nativetext = true;
     } else if (cur_arg == "--grayscale") {
       options->grayscale = true;
+    } else if (cur_arg == "--forced-color") {
+      options->forced_color = true;
+    } else if (cur_arg == "--fill-to-stroke") {
+      options->fill_to_stroke = true;
     } else if (cur_arg == "--limit-cache") {
       options->limit_cache = true;
     } else if (cur_arg == "--force-halftone") {
@@ -427,16 +499,53 @@
     } else if (cur_arg == "--save-attachments") {
       options->save_attachments = true;
     } else if (cur_arg == "--save-images") {
+      if (options->save_rendered_images) {
+        fprintf(stderr,
+                "--save-rendered-images conflicts with --save-images\n");
+        return false;
+      }
       options->save_images = true;
+    } else if (cur_arg == "--save-rendered-images") {
+      if (options->save_images) {
+        fprintf(stderr,
+                "--save-images conflicts with --save-rendered-images\n");
+        return false;
+      }
+      options->save_rendered_images = true;
     } else if (cur_arg == "--save-thumbs") {
       options->save_thumbnails = true;
     } else if (cur_arg == "--save-thumbs-dec") {
       options->save_thumbnails_decoded = true;
     } else if (cur_arg == "--save-thumbs-raw") {
       options->save_thumbnails_raw = true;
+    } else if (ParseSwitchKeyValue(cur_arg, "--use-renderer=", &value)) {
+      if (options->use_renderer_type != RendererType::kDefault) {
+        fprintf(stderr, "Duplicate --use-renderer argument\n");
+        return false;
+      }
+      if (value == "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 = RendererType::kSkia;
+#endif  // defined(PDF_ENABLE_SKIA)
+      } else {
+        fprintf(stderr, "Invalid --use-renderer argument\n");
+        return false;
+      }
 #ifdef PDF_ENABLE_V8
     } else if (cur_arg == "--disable-javascript") {
       options->disable_javascript = true;
+    } else if (ParseSwitchKeyValue(cur_arg, "--js-flags=", &value)) {
+      if (!options->js_flags.empty()) {
+        fprintf(stderr, "Duplicate --js-flags argument\n");
+        return false;
+      }
+      options->js_flags = value;
 #ifdef PDF_ENABLE_XFA
     } else if (cur_arg == "--disable-xfa") {
       options->disable_xfa = true;
@@ -450,37 +559,47 @@
     } else if (cur_arg == "--no-system-fonts") {
       options->linux_no_system_fonts = true;
 #endif
+    } else if (cur_arg == "--croscore-font-names") {
+      options->croscore_font_names = true;
     } else if (cur_arg == "--ppm") {
-      if (options->output_format != OUTPUT_NONE) {
+      if (options->output_format != OutputFormat::kNone) {
         fprintf(stderr, "Duplicate or conflicting --ppm argument\n");
         return false;
       }
-      options->output_format = OUTPUT_PPM;
+      options->output_format = OutputFormat::kPpm;
     } else if (cur_arg == "--png") {
-      if (options->output_format != OUTPUT_NONE) {
+      if (options->output_format != OutputFormat::kNone) {
         fprintf(stderr, "Duplicate or conflicting --png argument\n");
         return false;
       }
-      options->output_format = OUTPUT_PNG;
+      options->output_format = OutputFormat::kPng;
     } else if (cur_arg == "--txt") {
-      if (options->output_format != OUTPUT_NONE) {
+      if (options->output_format != OutputFormat::kNone) {
         fprintf(stderr, "Duplicate or conflicting --txt argument\n");
         return false;
       }
-      options->output_format = OUTPUT_TEXT;
+      options->output_format = OutputFormat::kText;
     } else if (cur_arg == "--annot") {
-      if (options->output_format != OUTPUT_NONE) {
+      if (options->output_format != OutputFormat::kNone) {
         fprintf(stderr, "Duplicate or conflicting --annot argument\n");
         return false;
       }
-      options->output_format = OUTPUT_ANNOT;
+      options->output_format = OutputFormat::kAnnot;
 #ifdef PDF_ENABLE_SKIA
     } else if (cur_arg == "--skp") {
-      if (options->output_format != OUTPUT_NONE) {
+      if (options->output_format != OutputFormat::kNone) {
         fprintf(stderr, "Duplicate or conflicting --skp argument\n");
         return false;
       }
-      options->output_format = OUTPUT_SKP;
+      options->output_format = OutputFormat::kSkp;
+#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()) {
@@ -488,8 +607,8 @@
         return false;
       }
       std::string path = value;
-      auto expanded_path = ExpandDirectoryPath(path);
-      if (!expanded_path) {
+      absl::optional<std::string> expanded_path = ExpandDirectoryPath(path);
+      if (!expanded_path.has_value()) {
         fprintf(stderr, "Failed to expand --font-dir, %s\n", path.c_str());
         return false;
       }
@@ -504,29 +623,35 @@
 
 #ifdef _WIN32
     } else if (cur_arg == "--emf") {
-      if (options->output_format != OUTPUT_NONE) {
+      if (options->output_format != OutputFormat::kNone) {
         fprintf(stderr, "Duplicate or conflicting --emf argument\n");
         return false;
       }
-      options->output_format = OUTPUT_EMF;
+      options->output_format = OutputFormat::kEmf;
     } else if (cur_arg == "--ps2") {
-      if (options->output_format != OUTPUT_NONE) {
+      if (options->output_format != OutputFormat::kNone) {
         fprintf(stderr, "Duplicate or conflicting --ps2 argument\n");
         return false;
       }
-      options->output_format = OUTPUT_PS2;
+      options->output_format = OutputFormat::kPs2;
     } else if (cur_arg == "--ps3") {
-      if (options->output_format != OUTPUT_NONE) {
+      if (options->output_format != OutputFormat::kNone) {
         fprintf(stderr, "Duplicate or conflicting --ps3 argument\n");
         return false;
       }
-      options->output_format = OUTPUT_PS3;
+      options->output_format = OutputFormat::kPs3;
+    } else if (cur_arg == "--ps3-type42") {
+      if (options->output_format != OutputFormat::kNone) {
+        fprintf(stderr, "Duplicate or conflicting --ps3-type42 argument\n");
+        return false;
+      }
+      options->output_format = OutputFormat::kPs3Type42;
     } else if (cur_arg == "--bmp") {
-      if (options->output_format != OUTPUT_NONE) {
+      if (options->output_format != OutputFormat::kNone) {
         fprintf(stderr, "Duplicate or conflicting --bmp argument\n");
         return false;
       }
-      options->output_format = OUTPUT_BMP;
+      options->output_format = OutputFormat::kBmp;
 #endif  // _WIN32
 
 #ifdef PDF_ENABLE_V8
@@ -537,8 +662,8 @@
         return false;
       }
       std::string path = value;
-      auto expanded_path = ExpandDirectoryPath(path);
-      if (!expanded_path) {
+      absl::optional<std::string> expanded_path = ExpandDirectoryPath(path);
+      if (!expanded_path.has_value()) {
         fprintf(stderr, "Failed to expand --bin-dir, %s\n", path.c_str());
         return false;
       }
@@ -559,17 +684,17 @@
       }
       options->scale_factor_as_string = value;
     } else if (cur_arg == "--show-pageinfo") {
-      if (options->output_format != OUTPUT_NONE) {
+      if (options->output_format != OutputFormat::kNone) {
         fprintf(stderr, "Duplicate or conflicting --show-pageinfo argument\n");
         return false;
       }
-      options->output_format = OUTPUT_PAGEINFO;
+      options->output_format = OutputFormat::kPageInfo;
     } else if (cur_arg == "--show-structure") {
-      if (options->output_format != OUTPUT_NONE) {
+      if (options->output_format != OutputFormat::kNone) {
         fprintf(stderr, "Duplicate or conflicting --show-structure argument\n");
         return false;
       }
-      options->output_format = OUTPUT_STRUCTURE;
+      options->output_format = OutputFormat::kStructure;
     } else if (ParseSwitchKeyValue(cur_arg, "--pages=", &value)) {
       if (options->pages) {
         fprintf(stderr, "Duplicate --pages argument\n");
@@ -577,7 +702,7 @@
       }
       options->pages = true;
       const std::string pages_string = value;
-      size_t first_dash = pages_string.find("-");
+      size_t first_dash = pages_string.find('-');
       if (first_dash == std::string::npos) {
         std::stringstream(pages_string) >> options->first_page;
         options->last_page = options->first_page;
@@ -681,142 +806,729 @@
   return true;
 }
 
-bool RenderPage(const std::string& name,
-                FPDF_DOCUMENT doc,
-                FPDF_FORMHANDLE form,
-                FPDF_FORMFILLINFO_PDFiumTest* form_fill_info,
-                const int page_index,
-                const Options& options,
-                const std::string& events) {
-  FPDF_PAGE page = GetPageForIndex(form_fill_info, doc, page_index);
-  if (!page)
+class Processor final {
+ public:
+  Processor(const Options* options, const std::function<void()>* idler)
+      : options_(options), idler_(idler) {
+    DCHECK(options_);
+    DCHECK(idler_);
+  }
+
+  const Options& options() const { return *options_; }
+  const std::function<void()>& idler() const { return *idler_; }
+
+#ifdef _WIN32
+  ComFactory& com_factory() { return com_factory_; }
+#endif  // _WIN32
+
+  // Invokes `idler()`.
+  void Idle() const { idler()(); }
+
+  void ProcessPdf(const std::string& name,
+                  const char* buf,
+                  size_t len,
+                  const std::string& events);
+
+ private:
+  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 rendered output to a file, returning `false` on
+  // failure.
+  //
+  // 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_; }
+
+  void Finish(FPDF_FORMHANDLE form) override {
+    FPDF_FFLDraw(form, bitmap(), page(), /*start_x=*/0, /*start_y=*/0,
+                 /*size_x=*/width(), /*size_y=*/height(), /*rotate=*/0,
+                 /*flags=*/flags());
+    Idle();
+  }
+
+  bool Write(const std::string& name, int page_index, bool md5) override {
+    return writer_ && writer_(*this, name, page_index, md5);
+  }
+
+ protected:
+  BitmapPageRenderer(FPDF_PAGE page,
+                     int width,
+                     int height,
+                     int flags,
+                     const std::function<void()>& idler,
+                     PageWriter writer)
+      : PageRenderer(page, /*width=*/width, /*height=*/height, /*flags=*/flags),
+        idler_(idler),
+        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_;
+  PageWriter writer_;
+  ScopedFPDFBitmap bitmap_;
+};
+
+// Bitmap page renderer completing in a single operation.
+class OneShotBitmapPageRenderer : public BitmapPageRenderer {
+ public:
+  OneShotBitmapPageRenderer(FPDF_PAGE page,
+                            int width,
+                            int height,
+                            int flags,
+                            const std::function<void()>& idler,
+                            PageWriter writer)
+      : BitmapPageRenderer(page,
+                           /*width=*/width,
+                           /*height=*/height,
+                           /*flags=*/flags,
+                           idler,
+                           std::move(writer)) {}
+
+  bool Start() override {
+    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
+    // rendering to update the UI, the PDF renderer will break when possible.
+    FPDF_RenderPageBitmap(bitmap(), page(), /*start_x=*/0, /*start_y=*/0,
+                          /*size_x=*/width(), /*size_y=*/height(), /*rotate=*/0,
+                          /*flags=*/flags());
+    return true;
+  }
+};
+
+// Bitmap page renderer completing over multiple operations.
+class ProgressiveBitmapPageRenderer : public BitmapPageRenderer {
+ public:
+  ProgressiveBitmapPageRenderer(FPDF_PAGE page,
+                                int width,
+                                int height,
+                                int flags,
+                                const std::function<void()>& idler,
+                                PageWriter writer,
+                                const FPDF_COLORSCHEME* color_scheme)
+      : BitmapPageRenderer(page,
+                           /*width=*/width,
+                           /*height=*/height,
+                           /*flags=*/flags,
+                           idler,
+                           std::move(writer)),
+        color_scheme_(color_scheme) {
+    pause_.version = 1;
+    pause_.NeedToPauseNow = &NeedToPauseNow;
+  }
+
+  bool Start() override {
+    if (!InitializeBitmap(/*first_scan=*/nullptr)) {
+      return false;
+    }
+
+    if (FPDF_RenderPageBitmapWithColorScheme_Start(
+            bitmap(), page(), /*start_x=*/0, /*start_y=*/0, /*size_x=*/width(),
+            /*size_y=*/height(), /*rotate=*/0, /*flags=*/flags(), color_scheme_,
+            &pause_) == FPDF_RENDER_TOBECONTINUED) {
+      to_be_continued_ = true;
+    }
+    return true;
+  }
+
+  bool Continue() override {
+    if (to_be_continued_) {
+      to_be_continued_ = (FPDF_RenderPage_Continue(page(), &pause_) ==
+                          FPDF_RENDER_TOBECONTINUED);
+    }
+    return to_be_continued_;
+  }
+
+  void Finish(FPDF_FORMHANDLE form) override {
+    BitmapPageRenderer::Finish(form);
+    FPDF_RenderPage_Close(page());
+    Idle();
+  }
+
+ private:
+  const FPDF_COLORSCHEME* color_scheme_;
+  IFSDK_PAUSE pause_;
+  bool to_be_continued_ = false;
+};
+
+#ifdef _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 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)
+      : SkCanvasPageRenderer(page, width, height, flags) {}
+
+  bool HasOutput() const override { return !!picture_; }
+
+  bool Start() override {
+    recorder_ = std::make_unique<SkPictureRecorder>();
+    recorder_->beginRecording(width(), height());
+    return SkCanvasPageRenderer::Start();
+  }
+
+  void Finish(FPDF_FORMHANDLE form) override {
+    SkCanvasPageRenderer::Finish(form);
+    picture_ = recorder_->finishRecordingAsPicture();
+    recorder_.reset();
+  }
+
+  bool Write(const std::string& name, int page_index, bool md5) override {
+    std::string image_file_name = WriteSkp(name.c_str(), page_index, *picture_);
+    if (image_file_name.empty())
+      return false;
+
+    if (md5) {
+      // Play back the `SkPicture` so we can take a hash of the result.
+      sk_sp<SkSurface> surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(
+          /*width=*/width(), /*height=*/height()));
+      if (!surface)
+        return false;
+
+      // Must clear to white before replay to match initial `CFX_DIBitmap`.
+      surface->getCanvas()->clear(SK_ColorWHITE);
+      surface->getCanvas()->drawPicture(picture_);
+
+      // Write the filename and the MD5 of the buffer to stdout.
+      SkPixmap pixmap;
+      if (!surface->peekPixels(&pixmap))
+        return false;
+
+      OutputMD5Hash(image_file_name.c_str(),
+                    {static_cast<const uint8_t*>(pixmap.addr()),
+                     pixmap.computeByteSize()});
+    }
+    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 PdfProcessor::ProcessPage(const int page_index) {
+  FPDF_PAGE page = GetPage(page_index);
+  if (!page) {
     return false;
-  if (options.send_events)
-    SendPageEvents(form, page, events);
-  if (options.save_images)
-    WriteImages(page, name.c_str(), page_index);
-  if (options.save_thumbnails)
-    WriteThumbnail(page, name.c_str(), page_index);
-  if (options.save_thumbnails_decoded)
-    WriteDecodedThumbnailStream(page, name.c_str(), page_index);
-  if (options.save_thumbnails_raw)
-    WriteRawThumbnailStream(page, name.c_str(), page_index);
-  if (options.output_format == OUTPUT_PAGEINFO) {
+  }
+
+  if (options().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 == OUTPUT_STRUCTURE) {
+  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;
+  }
 
-  auto width = static_cast<int>(FPDF_GetPageWidthF(page) * scale);
-  auto height = static_cast<int>(FPDF_GetPageHeightF(page) * scale);
-  int alpha = FPDFPage_HasTransparency(page) ? 1 : 0;
-  ScopedFPDFBitmap bitmap(FPDFBitmap_Create(width, height, alpha));
+  int width = static_cast<int>(FPDF_GetPageWidthF(page) * scale);
+  int height = static_cast<int>(FPDF_GetPageHeightF(page) * scale);
+  int flags = PageRenderFlagsFromOptions(options());
 
-  if (bitmap) {
-    FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
-    FPDFBitmap_FillRect(bitmap.get(), 0, 0, width, height, fill_color);
+  std::unique_ptr<PageRenderer> renderer;
+  BitmapPageRenderer::PageWriter writer;
+  switch (options().output_format) {
+    case OutputFormat::kText:
+      writer = BitmapPageRenderer::WrapPageWriter(WriteText);
+      break;
 
-    int flags = PageRenderFlagsFromOptions(options);
-    if (options.render_oneshot) {
-      // Note, client programs probably want to use this method instead of the
-      // progressive calls. The progressive calls are if you need to pause the
-      // rendering to update the UI, the PDF renderer will break when possible.
-      FPDF_RenderPageBitmap(bitmap.get(), page, 0, 0, width, height, 0, flags);
-    } else {
-      IFSDK_PAUSE pause;
-      pause.version = 1;
-      pause.NeedToPauseNow = &NeedToPauseNow;
+    case OutputFormat::kAnnot:
+      writer = BitmapPageRenderer::WrapPageWriter(WriteAnnot);
+      break;
 
-      int rv = FPDF_RenderPageBitmap_Start(bitmap.get(), page, 0, 0, width,
-                                           height, 0, flags, &pause);
-      while (rv == FPDF_RENDER_TOBECONTINUED)
-        rv = FPDF_RenderPage_Continue(page, &pause);
-    }
+    case OutputFormat::kPpm:
+      writer = BitmapPageRenderer::WrapPageWriter(WritePpm);
+      break;
 
-    FPDF_FFLDraw(form, bitmap.get(), page, 0, 0, width, height, 0, flags);
+    case OutputFormat::kPng:
+      writer = BitmapPageRenderer::WrapPageWriter(WritePng);
+      break;
 
-    if (!options.render_oneshot)
-      FPDF_RenderPage_Close(page);
-
-    int stride = FPDFBitmap_GetStride(bitmap.get());
-    void* buffer = FPDFBitmap_GetBuffer(bitmap.get());
-
-    std::string image_file_name;
-    switch (options.output_format) {
 #ifdef _WIN32
-      case OUTPUT_BMP:
-        image_file_name =
-            WriteBmp(name.c_str(), page_index, buffer, stride, width, height);
-        break;
+    case OutputFormat::kBmp:
+      writer = BitmapPageRenderer::WrapPageWriter(WriteBmp);
+      break;
 
-      case OUTPUT_EMF:
-        WriteEmf(page, name.c_str(), page_index);
-        break;
+    case OutputFormat::kEmf:
+      // TODO(crbug.com/pdfium/2054): Render directly to DC.
+      writer = BitmapPageRenderer::WrapPageWriter(WriteEmf);
+      break;
 
-      case OUTPUT_PS2:
-      case OUTPUT_PS3:
-        WritePS(page, name.c_str(), page_index);
-        break;
-#endif
-      case OUTPUT_TEXT:
-        WriteText(page, name.c_str(), page_index);
-        break;
-
-      case OUTPUT_ANNOT:
-        WriteAnnot(page, name.c_str(), page_index);
-        break;
-
-      case OUTPUT_PNG:
-        image_file_name =
-            WritePng(name.c_str(), page_index, buffer, stride, width, height);
-        break;
-
-      case OUTPUT_PPM:
-        image_file_name =
-            WritePpm(name.c_str(), page_index, buffer, stride, width, height);
-        break;
+    case OutputFormat::kPs2:
+    case OutputFormat::kPs3:
+      // TODO(crbug.com/pdfium/2054): Render directly to DC.
+      writer = BitmapPageRenderer::WrapPageWriter(WritePS);
+      break;
+#endif  // _WIN32
 
 #ifdef PDF_ENABLE_SKIA
-      case OUTPUT_SKP: {
-        std::unique_ptr<SkPictureRecorder> recorder(
-            reinterpret_cast<SkPictureRecorder*>(
-                FPDF_RenderPageSkp(page, width, height)));
-        FPDF_FFLRecord(form, recorder.get(), page, 0, 0, width, height, 0, 0);
-        image_file_name = WriteSkp(name.c_str(), page_index, recorder.get());
-      } break;
-#endif
-      default:
-        break;
-    }
+    case OutputFormat::kSkp:
+      renderer = std::make_unique<SkPicturePageRenderer>(
+          page, /*width=*/width, /*height=*/height, /*flags=*/flags);
+      break;
 
-    // Write the filename and the MD5 of the buffer to stdout if we wrote a
-    // file.
-    if (options.md5 && !image_file_name.empty()) {
-      OutputMD5Hash(image_file_name.c_str(),
-                    static_cast<const uint8_t*>(buffer), stride * height);
+#ifdef _WIN32
+    case OutputFormat::kXps: {
+      IXpsOMObjectFactory* xps_factory = com_factory().GetXpsOMObjectFactory();
+      if (!xps_factory) {
+        break;
+      }
+
+      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
+
+    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(),
+          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.
+      FPDF_COLORSCHEME color_scheme;
+      color_scheme.path_fill_color = 0xFFFF0000;
+      color_scheme.path_stroke_color = 0xFF00FF00;
+      color_scheme.text_fill_color = 0xFF0000FF;
+      color_scheme.text_stroke_color = 0xFF00FFFF;
+
+      renderer = std::make_unique<ProgressiveBitmapPageRenderer>(
+          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());
+    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);
-  FORM_OnBeforeClosePage(page, form);
-  return !!bitmap;
+  FORM_DoPageAAction(page, form(), FPDFPAGE_AACTION_CLOSE);
+  Idle();
+
+  FORM_OnBeforeClosePage(page, form());
+  Idle();
+
+  return renderer->HasOutput();
 }
 
-void RenderPdf(const std::string& name,
-               const char* buf,
-               size_t len,
-               const Options& options,
-               const std::string& events) {
+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 = {};
@@ -839,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) {
@@ -880,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 = {};
@@ -904,15 +1618,18 @@
 #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;
 #endif  // PDF_ENABLE_XFA
+  form_callbacks.FFI_ExecuteNamedAction = ExampleNamedAction;
   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(
@@ -920,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()))
@@ -935,17 +1652,22 @@
   FORM_DoDocumentOpenAction(form.get());
 
 #if _WIN32
-  if (options.output_format == OUTPUT_PS2)
+  if (options().output_format == OutputFormat::kPs2) {
     FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT2);
-  else if (options.output_format == OUTPUT_PS3)
+  } else if (options().output_format == OutputFormat::kPs3) {
     FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT3);
+  } else if (options().output_format == OutputFormat::kPs3Type42) {
+    FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT3_TYPE42);
+  }
 #endif
 
   int page_count = FPDF_GetPageCount(doc.get());
-  int rendered_pages = 0;
+  int processed_pages = 0;
   int bad_pages = 0;
-  int first_page = options.pages ? options.first_page : 0;
-  int last_page = options.pages ? options.last_page + 1 : page_count;
+  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;
@@ -958,101 +1680,121 @@
         return;
       }
     }
-    if (RenderPage(name, doc.get(), form.get(), &form_callbacks, i, options,
-                   events)) {
-      ++rendered_pages;
+    if (pdf_processor.ProcessPage(i)) {
+      ++processed_pages;
     } else {
       ++bad_pages;
     }
+    Idle();
   }
 
   FORM_DoDocumentAAction(form.get(), FPDFDOC_AACTION_WC);
-  fprintf(stderr, "Rendered %d pages.\n", rendered_pages);
+  Idle();
+
+  fprintf(stderr, "Processed %d pages.\n", processed_pages);
   if (bad_pages)
     fprintf(stderr, "Skipped %d bad pages.\n", bad_pages);
 }
 
 void ShowConfig() {
   std::string config;
-  std::string maybe_comma;
+  [[maybe_unused]] auto append_config = [&config](const char* name) {
+    if (!config.empty())
+      config += ',';
+    config += name;
+  };
+
 #ifdef PDF_ENABLE_V8
-  config.append(maybe_comma);
-  config.append("V8");
-  maybe_comma = ",";
-#endif  // PDF_ENABLE_V8
+  append_config("V8");
+#endif
 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
-  config.append(maybe_comma);
-  config.append("V8_EXTERNAL");
-  maybe_comma = ",";
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+  append_config("V8_EXTERNAL");
+#endif
 #ifdef PDF_ENABLE_XFA
-  config.append(maybe_comma);
-  config.append("XFA");
-  maybe_comma = ",";
-#endif  // PDF_ENABLE_XFA
+  append_config("XFA");
+#endif
 #ifdef PDF_ENABLE_ASAN
-  config.append(maybe_comma);
-  config.append("ASAN");
-  maybe_comma = ",";
-#endif  // PDF_ENABLE_ASAN
-#if defined(PDF_ENABLE_SKIA)
-  config.append(maybe_comma);
-  config.append("SKIA");
-  maybe_comma = ",";
-#elif defined(PDF_ENABLE_SKIA_PATHS)
-  config.append(maybe_comma);
-  config.append("SKIAPATHS");
-  maybe_comma = ",";
+  append_config("ASAN");
+#endif
+#ifdef PDF_ENABLE_SKIA
+  append_config("SKIA");
+#endif
+#ifdef _WIN32
+  append_config("GDI");
 #endif
   printf("%s\n", config.c_str());
 }
 
 constexpr char kUsageString[] =
     "Usage: pdfium_test [OPTION] [FILE]...\n"
-    "  --show-config        - print build options and exit\n"
-    "  --show-metadata      - print the file metadata\n"
-    "  --show-pageinfo      - print information about pages\n"
-    "  --show-structure     - print the structure elements from the document\n"
-    "  --send-events        - send input described by .evt file\n"
-    "  --mem-document       - load document with FPDF_LoadMemDocument()\n"
-    "  --render-oneshot     - render image without using progressive renderer\n"
-    "  --lcd-text           - render text optimized for LCD displays\n"
-    "  --no-nativetext      - render without using the native text output\n"
-    "  --grayscale          - render grayscale output\n"
-    "  --limit-cache        - render limiting image cache size\n"
-    "  --force-halftone     - render forcing halftone\n"
-    "  --printing           - render as if for printing\n"
-    "  --no-smoothtext      - render disabling text anti-aliasing\n"
-    "  --no-smoothimage     - render disabling image anti-alisasing\n"
-    "  --no-smoothpath      - render disabling path anti-aliasing\n"
-    "  --reverse-byte-order - render to BGRA, if supported by the output "
+    "  --show-config          - print build options and exit\n"
+    "  --show-metadata        - print the file metadata\n"
+    "  --show-pageinfo        - print information about pages\n"
+    "  --show-structure       - print the structure elements from the "
+    "document\n"
+    "  --send-events          - send input described by .evt file\n"
+    "  --mem-document         - load document with FPDF_LoadMemDocument()\n"
+    "  --render-oneshot       - render image without using progressive "
+    "renderer\n"
+    "  --lcd-text             - render text optimized for LCD displays\n"
+    "  --no-nativetext        - render without using the native text output\n"
+    "  --grayscale            - render grayscale output\n"
+    "  --forced-color         - render in forced color mode\n"
+    "  --fill-to-stroke       - render fill as stroke in forced color mode\n"
+    "  --limit-cache          - render limiting image cache size\n"
+    "  --force-halftone       - render forcing halftone\n"
+    "  --printing             - render as if for printing\n"
+    "  --no-smoothtext        - render disabling text anti-aliasing\n"
+    "  --no-smoothimage       - render disabling image anti-alisasing\n"
+    "  --no-smoothpath        - render disabling path anti-aliasing\n"
+    "  --reverse-byte-order   - render to BGRA, if supported by the output "
     "format\n"
-    "  --save-attachments   - write embedded attachments "
+    "  --save-attachments     - write embedded attachments "
     "<pdf-name>.attachment.<attachment-name>\n"
-    "  --save-images        - write embedded images "
+    "  --save-images          - write raw embedded images "
     "<pdf-name>.<page-number>.<object-number>.png\n"
-    "  --save-thumbs        - write page thumbnails "
+    "  --save-rendered-images - write embedded images as rendered on the page "
+    "<pdf-name>.<page-number>.<object-number>.png\n"
+    "  --save-thumbs          - write page thumbnails "
     "<pdf-name>.thumbnail.<page-number>.png\n"
-    "  --save-thumbs-dec    - write page thumbnails' decoded stream data"
+    "  --save-thumbs-dec      - write page thumbnails' decoded stream data"
     "<pdf-name>.thumbnail.decoded.<page-number>.png\n"
-    "  --save-thumbs-raw    - write page thumbnails' raw stream data"
+    "  --save-thumbs-raw      - write page thumbnails' raw stream data"
     "<pdf-name>.thumbnail.raw.<page-number>.png\n"
+
+#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  // _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"
+    "  --disable-javascript   - do not execute JS in PDF files\n"
+    "  --js-flags=<flags>     - additional flags to pass to V8\n"
 #ifdef PDF_ENABLE_XFA
-    "  --disable-xfa        - do not process XFA forms\n"
+    "  --disable-xfa          - do not process XFA forms\n"
 #endif  // PDF_ENABLE_XFA
 #endif  // PDF_ENABLE_V8
 #ifdef ENABLE_CALLGRIND
-    "  --callgrind-delim    - delimit interesting section when using "
+    "  --callgrind-delim      - delimit interesting section when using "
     "callgrind\n"
 #endif
 #if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__))
-    "  --no-system-fonts    - do not use system fonts, overrides --font-dir\n"
+    "  --no-system-fonts      - do not use system fonts, overrides --font-dir\n"
 #endif
-    "  --bin-dir=<path>     - override path to v8 external data\n"
-    "  --font-dir=<path>    - override path to external fonts\n"
-    "  --scale=<number>     - scale output size by number (e.g. 0.5)\n"
+    "  --croscore-font-names  - use Croscore font names\n"
+    "  --bin-dir=<path>       - override path to v8 external data\n"
+    "  --font-dir=<path>      - override path to external fonts\n"
+    "  --scale=<number>       - scale output size by number (e.g. 0.5)\n"
+    "  --password=<secret>    - password to decrypt the PDF with\n"
     "  --pages=<number>(-<number>) - only render the given 0-based page(s)\n"
 #ifdef _WIN32
     "  --bmp   - write page images <pdf-name>.<page-number>.bmp\n"
@@ -1061,6 +1803,8 @@
     "<pdf-name>.<page-number>.ps\n"
     "  --ps3   - write page raw PostScript (Lvl 3) "
     "<pdf-name>.<page-number>.ps\n"
+    "  --ps3-type42 - write page raw PostScript (Lvl 3 with Type 42 fonts) "
+    "<pdf-name>.<page-number>.ps\n"
 #endif
     "  --txt   - write page text in UTF32-LE <pdf-name>.<page-number>.txt\n"
     "  --png   - write page images <pdf-name>.<page-number>.png\n"
@@ -1068,14 +1812,37 @@
     "  --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"
     "";
 
+void SetUpErrorHandling() {
+#ifdef _WIN32
+  // Suppress various Windows error reporting mechanisms that can pop up dialog
+  // boxes and cause the program to hang.
+  SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT |
+               SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
+  _set_error_mode(_OUT_TO_STDERR);
+  _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
+  _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+  _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+#endif  // _WIN32
+}
+
 }  // namespace
 
 int main(int argc, const char* argv[]) {
+#if defined(PDF_USE_PARTITION_ALLOC)
+  pdfium::ConfigurePartitionAllocShimPartitionForTest();
+#endif
+
+  SetUpErrorHandling();
+  setlocale(LC_CTYPE, "en_US.UTF-8");  // For printf() of high-characters.
+
   std::vector<std::string> args(argv, argv + argc);
   Options options;
   std::vector<std::string> files;
@@ -1094,28 +1861,79 @@
     return 1;
   }
 
-#ifdef PDF_ENABLE_V8
-  std::unique_ptr<v8::Platform> platform;
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
-  v8::StartupData snapshot;
-  if (!options.disable_javascript) {
-    platform = InitializeV8ForPDFiumWithStartupData(
-        options.exe_path, options.bin_directory, &snapshot);
-  }
-#else   // V8_USE_EXTERNAL_STARTUP_DATA
-  if (!options.disable_javascript)
-    platform = InitializeV8ForPDFium(options.exe_path);
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-#endif  // PDF_ENABLE_V8
-
   FPDF_LIBRARY_CONFIG config;
-  config.version = 2;
+  config.version = 4;
   config.m_pUserFontPaths = nullptr;
   config.m_pIsolate = nullptr;
   config.m_v8EmbedderSlot = 0;
+  config.m_pPlatform = nullptr;
+
+  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
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+  v8::StartupData snapshot;
+#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+  std::unique_ptr<v8::Platform> platform;
+  std::unique_ptr<v8::Isolate, V8IsolateDeleter> isolate;
+  if (!options.disable_javascript) {
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+    platform = InitializeV8ForPDFiumWithStartupData(
+        options.exe_path, options.js_flags, options.bin_directory, &snapshot);
+#else   // V8_USE_EXTERNAL_STARTUP_DATA
+    platform = InitializeV8ForPDFium(options.exe_path, options.js_flags);
+#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+    if (!platform) {
+      fprintf(stderr, "V8 initialization failed.\n");
+      return 1;
+    }
+    config.m_pPlatform = platform.get();
+
+    v8::Isolate::CreateParams params;
+    params.array_buffer_allocator = static_cast<v8::ArrayBuffer::Allocator*>(
+        FPDF_GetArrayBufferAllocatorSharedInstance());
+    isolate.reset(v8::Isolate::New(params));
+    config.m_pIsolate = isolate.get();
+
+    idler = [&platform, &isolate]() {
+      int task_count = 0;
+      while (v8::platform::PumpMessageLoop(platform.get(), isolate.get()))
+        ++task_count;
+      if (task_count)
+        fprintf(stderr, "Pumped %d tasks\n", task_count);
+    };
+  }
+#endif  // PDF_ENABLE_V8
 
   const char* path_array[2] = {nullptr, nullptr};
-  Optional<const char*> custom_font_path = GetCustomFontPath(options);
+  absl::optional<const char*> custom_font_path = GetCustomFontPath(options);
   if (custom_font_path.has_value()) {
     path_array[0] = custom_font_path.value();
     config.m_pUserFontPaths = path_array;
@@ -1123,6 +1941,10 @@
 
   FPDF_InitLibraryWithConfig(&config);
 
+  std::unique_ptr<FontRenamer> font_renamer;
+  if (options.croscore_font_names)
+    font_renamer = std::make_unique<FontRenamer>();
+
   UNSUPPORT_INFO unsupported_info = {};
   unsupported_info.version = 1;
   unsupported_info.FSDK_UnSupport_Handler = ExampleUnsupportedHandler;
@@ -1137,13 +1959,14 @@
     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 =
         GetFileContents(filename.c_str(), &file_length);
     if (!file_contents)
       continue;
-    fprintf(stderr, "Rendering PDF file %s.\n", filename.c_str());
+    fprintf(stderr, "Processing PDF file %s.\n", filename.c_str());
 
 #ifdef ENABLE_CALLGRIND
     if (options.callgrind_delimiters)
@@ -1169,7 +1992,8 @@
         }
       }
     }
-    RenderPdf(filename, file_contents.get(), file_length, options, events);
+
+    processor.ProcessPdf(filename, file_contents.get(), file_length, events);
 
 #ifdef ENABLE_CALLGRIND
     if (options.callgrind_delimiters)
@@ -1181,7 +2005,8 @@
 
 #ifdef PDF_ENABLE_V8
   if (!options.disable_javascript) {
-    v8::V8::ShutdownPlatform();
+    isolate.reset();
+    ShutdownV8ForPDFium();
 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
     free(const_cast<char*>(snapshot.data));
 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
diff --git a/samples/pdfium_test_dump_helper.cc b/samples/pdfium_test_dump_helper.cc
deleted file mode 100644
index 047a670..0000000
--- a/samples/pdfium_test_dump_helper.cc
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "samples/pdfium_test_dump_helper.h"
-
-#include <string.h>
-
-#include <algorithm>
-#include <functional>
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "public/cpp/fpdf_scopers.h"
-#include "public/fpdf_transformpage.h"
-#include "testing/fx_string_testhelpers.h"
-
-using GetBoxInfoFunc =
-    std::function<bool(FPDF_PAGE, float*, float*, float*, float*)>;
-
-namespace {
-
-std::wstring ConvertToWString(const unsigned short* buf,
-                              unsigned long buf_size) {
-  std::wstring result;
-  result.reserve(buf_size);
-  std::copy(buf, buf + buf_size, std::back_inserter(result));
-  return result;
-}
-
-void DumpBoxInfo(GetBoxInfoFunc func,
-                 const char* box_type,
-                 FPDF_PAGE page,
-                 int page_idx) {
-  FS_RECTF rect;
-  bool ret = func(page, &rect.left, &rect.bottom, &rect.right, &rect.top);
-  if (!ret) {
-    printf("Page %d: No %s.\n", page_idx, box_type);
-    return;
-  }
-  printf("Page %d: %s: %0.2f %0.2f %0.2f %0.2f\n", page_idx, box_type,
-         rect.left, rect.bottom, rect.right, rect.top);
-}
-
-}  // namespace
-
-void DumpChildStructure(FPDF_STRUCTELEMENT child, int indent) {
-  static const size_t kBufSize = 1024;
-  unsigned short buf[kBufSize];
-  unsigned long len = FPDF_StructElement_GetType(child, buf, kBufSize);
-  printf("%*s%ls", indent * 2, "", ConvertToWString(buf, len).c_str());
-
-  memset(buf, 0, sizeof(buf));
-  len = FPDF_StructElement_GetTitle(child, buf, kBufSize);
-  if (len > 0)
-    printf(": '%ls'", ConvertToWString(buf, len).c_str());
-
-  memset(buf, 0, sizeof(buf));
-  len = FPDF_StructElement_GetAltText(child, buf, kBufSize);
-  if (len > 0)
-    printf(" (%ls)", ConvertToWString(buf, len).c_str());
-  printf("\n");
-
-  for (int i = 0; i < FPDF_StructElement_CountChildren(child); ++i) {
-    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)
-      continue;
-
-    DumpChildStructure(sub_child, indent + 1);
-  }
-}
-
-void DumpPageInfo(FPDF_PAGE page, int page_idx) {
-  DumpBoxInfo(&FPDFPage_GetMediaBox, "MediaBox", page, page_idx);
-  DumpBoxInfo(&FPDFPage_GetCropBox, "CropBox", page, page_idx);
-  DumpBoxInfo(&FPDFPage_GetBleedBox, "BleedBox", page, page_idx);
-  DumpBoxInfo(&FPDFPage_GetTrimBox, "TrimBox", page, page_idx);
-  DumpBoxInfo(&FPDFPage_GetArtBox, "ArtBox", page, page_idx);
-}
-
-void DumpPageStructure(FPDF_PAGE page, int page_idx) {
-  ScopedFPDFStructTree tree(FPDF_StructTree_GetForPage(page));
-  if (!tree) {
-    fprintf(stderr, "Failed to load struct tree for page %d\n", page_idx);
-    return;
-  }
-
-  printf("Structure Tree for Page %d\n", page_idx);
-  for (int i = 0; i < FPDF_StructTree_CountChildren(tree.get()); ++i) {
-    FPDF_STRUCTELEMENT child = FPDF_StructTree_GetChildAtIndex(tree.get(), i);
-    if (!child) {
-      fprintf(stderr, "Failed to load child %d for page %d\n", i, page_idx);
-      continue;
-    }
-    DumpChildStructure(child, 0);
-  }
-  printf("\n\n");
-}
-
-void DumpMetaData(FPDF_DOCUMENT doc) {
-  static constexpr const char* kMetaTags[] = {
-      "Title",   "Author",   "Subject",      "Keywords",
-      "Creator", "Producer", "CreationDate", "ModDate"};
-  for (const char* meta_tag : kMetaTags) {
-    char meta_buffer[4096];
-    unsigned long len =
-        FPDF_GetMetaText(doc, meta_tag, meta_buffer, sizeof(meta_buffer));
-    if (!len)
-      continue;
-
-    auto* meta_string = reinterpret_cast<unsigned short*>(meta_buffer);
-    printf("%-12s = %ls (%lu bytes)\n", meta_tag,
-           GetPlatformWString(meta_string).c_str(), len);
-  }
-}
diff --git a/samples/pdfium_test_dump_helper.h b/samples/pdfium_test_dump_helper.h
deleted file mode 100644
index 17cbd8f..0000000
--- a/samples/pdfium_test_dump_helper.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SAMPLES_PDFIUM_TEST_DUMP_HELPER_H_
-#define SAMPLES_PDFIUM_TEST_DUMP_HELPER_H_
-
-#include "public/fpdfview.h"
-
-void DumpChildStructure(FPDF_STRUCTELEMENT child, int indent);
-void DumpPageInfo(FPDF_PAGE page, int page_idx);
-void DumpPageStructure(FPDF_PAGE page, int page_idx);
-void DumpMetaData(FPDF_DOCUMENT doc);
-
-#endif  // SAMPLES_PDFIUM_TEST_DUMP_HELPER_H_
diff --git a/samples/pdfium_test_event_helper.cc b/samples/pdfium_test_event_helper.cc
deleted file mode 100644
index a3ddbef..0000000
--- a/samples/pdfium_test_event_helper.cc
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "samples/pdfium_test_event_helper.h"
-
-#include <stdio.h>
-
-#include <string>
-#include <vector>
-
-#include "public/fpdf_fwlevent.h"
-#include "public/fpdfview.h"
-#include "testing/fx_string_testhelpers.h"
-
-namespace {
-void SendCharCodeEvent(FPDF_FORMHANDLE form,
-                       FPDF_PAGE page,
-                       const std::vector<std::string>& tokens) {
-  if (tokens.size() != 2) {
-    fprintf(stderr, "charcode: bad args\n");
-    return;
-  }
-
-  int keycode = atoi(tokens[1].c_str());
-  FORM_OnChar(form, page, keycode, 0);
-}
-
-void SendKeyCodeEvent(FPDF_FORMHANDLE form,
-                      FPDF_PAGE page,
-                      const std::vector<std::string>& tokens) {
-  if (tokens.size() != 2) {
-    fprintf(stderr, "keycode: bad args\n");
-    return;
-  }
-
-  int keycode = atoi(tokens[1].c_str());
-  FORM_OnKeyDown(form, page, keycode, 0);
-  FORM_OnKeyUp(form, page, keycode, 0);
-}
-
-uint32_t GetModifiers(std::string modifiers_string) {
-  int modifiers = 0;
-  if (modifiers_string.find("shift") != std::string::npos)
-    modifiers |= FWL_EVENTFLAG_ShiftKey;
-  if (modifiers_string.find("control") != std::string::npos)
-    modifiers |= FWL_EVENTFLAG_ControlKey;
-  if (modifiers_string.find("alt") != std::string::npos)
-    modifiers |= FWL_EVENTFLAG_AltKey;
-
-  return modifiers;
-}
-
-void SendMouseDownEvent(FPDF_FORMHANDLE form,
-                        FPDF_PAGE page,
-                        const std::vector<std::string>& tokens) {
-  if (tokens.size() != 4 && tokens.size() != 5) {
-    fprintf(stderr, "mousedown: bad args\n");
-    return;
-  }
-
-  int x = atoi(tokens[2].c_str());
-  int y = atoi(tokens[3].c_str());
-  uint32_t modifiers = tokens.size() >= 5 ? GetModifiers(tokens[4]) : 0;
-
-  if (tokens[1] == "left")
-    FORM_OnLButtonDown(form, page, modifiers, x, y);
-  else if (tokens[1] == "right")
-    FORM_OnRButtonDown(form, page, modifiers, x, y);
-  else
-    fprintf(stderr, "mousedown: bad button name\n");
-}
-
-void SendMouseUpEvent(FPDF_FORMHANDLE form,
-                      FPDF_PAGE page,
-                      const std::vector<std::string>& tokens) {
-  if (tokens.size() != 4 && tokens.size() != 5) {
-    fprintf(stderr, "mouseup: bad args\n");
-    return;
-  }
-
-  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")
-    FORM_OnLButtonUp(form, page, modifiers, x, y);
-  else if (tokens[1] == "right")
-    FORM_OnRButtonUp(form, page, modifiers, x, y);
-  else
-    fprintf(stderr, "mouseup: bad button name\n");
-}
-
-void SendMouseDoubleClickEvent(FPDF_FORMHANDLE form,
-                               FPDF_PAGE page,
-                               const std::vector<std::string>& tokens) {
-  if (tokens.size() != 4 && tokens.size() != 5) {
-    fprintf(stderr, "mousedoubleclick: bad args\n");
-    return;
-  }
-
-  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") {
-    fprintf(stderr, "mousedoubleclick: bad button name\n");
-    return;
-  }
-  FORM_OnLButtonDoubleClick(form, page, modifiers, x, y);
-}
-
-void SendMouseMoveEvent(FPDF_FORMHANDLE form,
-                        FPDF_PAGE page,
-                        const std::vector<std::string>& tokens) {
-  if (tokens.size() != 3) {
-    fprintf(stderr, "mousemove: bad args\n");
-    return;
-  }
-
-  int x = atoi(tokens[1].c_str());
-  int y = atoi(tokens[2].c_str());
-  FORM_OnMouseMove(form, page, 0, x, y);
-}
-
-void SendFocusEvent(FPDF_FORMHANDLE form,
-                    FPDF_PAGE page,
-                    const std::vector<std::string>& tokens) {
-  if (tokens.size() != 3) {
-    fprintf(stderr, "focus: bad args\n");
-    return;
-  }
-
-  int x = atoi(tokens[1].c_str());
-  int y = atoi(tokens[2].c_str());
-  FORM_OnFocus(form, page, 0, x, y);
-}
-}  // namespace
-
-void SendPageEvents(FPDF_FORMHANDLE form,
-                    FPDF_PAGE page,
-                    const std::string& events) {
-  auto lines = StringSplit(events, '\n');
-  for (auto line : lines) {
-    auto command = StringSplit(line, '#');
-    if (command[0].empty())
-      continue;
-    auto tokens = StringSplit(command[0], ',');
-    if (tokens[0] == "charcode") {
-      SendCharCodeEvent(form, page, tokens);
-    } else if (tokens[0] == "keycode") {
-      SendKeyCodeEvent(form, page, tokens);
-    } else if (tokens[0] == "mousedown") {
-      SendMouseDownEvent(form, page, tokens);
-    } else if (tokens[0] == "mouseup") {
-      SendMouseUpEvent(form, page, tokens);
-    } else if (tokens[0] == "mousedoubleclick") {
-      SendMouseDoubleClickEvent(form, page, tokens);
-    } else if (tokens[0] == "mousemove") {
-      SendMouseMoveEvent(form, page, tokens);
-    } else if (tokens[0] == "focus") {
-      SendFocusEvent(form, page, tokens);
-    } else {
-      fprintf(stderr, "Unrecognized event: %s\n", tokens[0].c_str());
-    }
-  }
-}
diff --git a/samples/pdfium_test_event_helper.h b/samples/pdfium_test_event_helper.h
deleted file mode 100644
index e823369..0000000
--- a/samples/pdfium_test_event_helper.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SAMPLES_PDFIUM_TEST_EVENT_HELPER_H_
-#define SAMPLES_PDFIUM_TEST_EVENT_HELPER_H_
-
-#include <string>
-
-#include "public/fpdf_formfill.h"
-#include "public/fpdfview.h"
-
-void SendPageEvents(FPDF_FORMHANDLE form,
-                    FPDF_PAGE page,
-                    const std::string& events);
-
-#endif  // SAMPLES_PDFIUM_TEST_EVENT_HELPER_H_
diff --git a/samples/pdfium_test_write_helper.cc b/samples/pdfium_test_write_helper.cc
deleted file mode 100644
index 122d783..0000000
--- a/samples/pdfium_test_write_helper.cc
+++ /dev/null
@@ -1,773 +0,0 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "samples/pdfium_test_write_helper.h"
-
-#include <limits.h>
-
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "public/cpp/fpdf_scopers.h"
-#include "public/fpdf_annot.h"
-#include "public/fpdf_attachment.h"
-#include "public/fpdf_edit.h"
-#include "public/fpdf_thumbnail.h"
-#include "testing/fx_string_testhelpers.h"
-#include "testing/image_diff/image_diff_png.h"
-#include "third_party/base/logging.h"
-
-namespace {
-
-bool CheckDimensions(int stride, int width, int height) {
-  if (stride < 0 || width < 0 || height < 0)
-    return false;
-  if (height > 0 && stride > INT_MAX / height)
-    return false;
-  return true;
-}
-
-const char* AnnotSubtypeToCString(FPDF_ANNOTATION_SUBTYPE subtype) {
-  if (subtype == FPDF_ANNOT_TEXT)
-    return "Text";
-  if (subtype == FPDF_ANNOT_LINK)
-    return "Link";
-  if (subtype == FPDF_ANNOT_FREETEXT)
-    return "FreeText";
-  if (subtype == FPDF_ANNOT_LINE)
-    return "Line";
-  if (subtype == FPDF_ANNOT_SQUARE)
-    return "Square";
-  if (subtype == FPDF_ANNOT_CIRCLE)
-    return "Circle";
-  if (subtype == FPDF_ANNOT_POLYGON)
-    return "Polygon";
-  if (subtype == FPDF_ANNOT_POLYLINE)
-    return "PolyLine";
-  if (subtype == FPDF_ANNOT_HIGHLIGHT)
-    return "Highlight";
-  if (subtype == FPDF_ANNOT_UNDERLINE)
-    return "Underline";
-  if (subtype == FPDF_ANNOT_SQUIGGLY)
-    return "Squiggly";
-  if (subtype == FPDF_ANNOT_STRIKEOUT)
-    return "StrikeOut";
-  if (subtype == FPDF_ANNOT_STAMP)
-    return "Stamp";
-  if (subtype == FPDF_ANNOT_CARET)
-    return "Caret";
-  if (subtype == FPDF_ANNOT_INK)
-    return "Ink";
-  if (subtype == FPDF_ANNOT_POPUP)
-    return "Popup";
-  if (subtype == FPDF_ANNOT_FILEATTACHMENT)
-    return "FileAttachment";
-  if (subtype == FPDF_ANNOT_SOUND)
-    return "Sound";
-  if (subtype == FPDF_ANNOT_MOVIE)
-    return "Movie";
-  if (subtype == FPDF_ANNOT_WIDGET)
-    return "Widget";
-  if (subtype == FPDF_ANNOT_SCREEN)
-    return "Screen";
-  if (subtype == FPDF_ANNOT_PRINTERMARK)
-    return "PrinterMark";
-  if (subtype == FPDF_ANNOT_TRAPNET)
-    return "TrapNet";
-  if (subtype == FPDF_ANNOT_WATERMARK)
-    return "Watermark";
-  if (subtype == FPDF_ANNOT_THREED)
-    return "3D";
-  if (subtype == FPDF_ANNOT_RICHMEDIA)
-    return "RichMedia";
-  if (subtype == FPDF_ANNOT_XFAWIDGET)
-    return "XFAWidget";
-  NOTREACHED();
-  return "";
-}
-
-void AppendFlagString(const char* flag, std::string* output) {
-  if (!output->empty())
-    *output += ", ";
-  *output += flag;
-}
-
-std::string AnnotFlagsToString(int flags) {
-  std::string str;
-  if (flags & FPDF_ANNOT_FLAG_INVISIBLE)
-    AppendFlagString("Invisible", &str);
-  if (flags & FPDF_ANNOT_FLAG_HIDDEN)
-    AppendFlagString("Hidden", &str);
-  if (flags & FPDF_ANNOT_FLAG_PRINT)
-    AppendFlagString("Print", &str);
-  if (flags & FPDF_ANNOT_FLAG_NOZOOM)
-    AppendFlagString("NoZoom", &str);
-  if (flags & FPDF_ANNOT_FLAG_NOROTATE)
-    AppendFlagString("NoRotate", &str);
-  if (flags & FPDF_ANNOT_FLAG_NOVIEW)
-    AppendFlagString("NoView", &str);
-  if (flags & FPDF_ANNOT_FLAG_READONLY)
-    AppendFlagString("ReadOnly", &str);
-  if (flags & FPDF_ANNOT_FLAG_LOCKED)
-    AppendFlagString("Locked", &str);
-  if (flags & FPDF_ANNOT_FLAG_TOGGLENOVIEW)
-    AppendFlagString("ToggleNoView", &str);
-  return str;
-}
-
-const char* PageObjectTypeToCString(int type) {
-  if (type == FPDF_PAGEOBJ_TEXT)
-    return "Text";
-  if (type == FPDF_PAGEOBJ_PATH)
-    return "Path";
-  if (type == FPDF_PAGEOBJ_IMAGE)
-    return "Image";
-  if (type == FPDF_PAGEOBJ_SHADING)
-    return "Shading";
-  if (type == FPDF_PAGEOBJ_FORM)
-    return "Form";
-  NOTREACHED();
-  return "";
-}
-
-std::vector<uint8_t> EncodePng(pdfium::span<const uint8_t> input,
-                               int width,
-                               int height,
-                               int stride,
-                               int format) {
-  std::vector<uint8_t> png;
-  switch (format) {
-    case FPDFBitmap_Unknown:
-      break;
-    case FPDFBitmap_Gray:
-      png = image_diff_png::EncodeGrayPNG(input, width, height, stride);
-      break;
-    case FPDFBitmap_BGR:
-      png = image_diff_png::EncodeBGRPNG(input, width, height, stride);
-      break;
-    case FPDFBitmap_BGRx:
-      png = image_diff_png::EncodeBGRAPNG(input, width, height, stride,
-                                          /*discard_transparency=*/true);
-      break;
-    case FPDFBitmap_BGRA:
-      png = image_diff_png::EncodeBGRAPNG(input, width, height, stride,
-                                          /*discard_transparency=*/false);
-      break;
-    default:
-      NOTREACHED();
-  }
-  return png;
-}
-
-#ifdef _WIN32
-int CALLBACK EnhMetaFileProc(HDC hdc,
-                             HANDLETABLE* handle_table,
-                             const ENHMETARECORD* record,
-                             int objects_count,
-                             LPARAM param) {
-  std::vector<const ENHMETARECORD*>& items =
-      *reinterpret_cast<std::vector<const ENHMETARECORD*>*>(param);
-  items.push_back(record);
-  return 1;
-}
-#endif  // _WIN32
-
-}  // namespace
-
-std::string WritePpm(const char* pdf_name,
-                     int num,
-                     void* buffer_void,
-                     int stride,
-                     int width,
-                     int height) {
-  if (!CheckDimensions(stride, width, height))
-    return "";
-
-  int out_len = width * height;
-  if (out_len > INT_MAX / 3)
-    return "";
-
-  out_len *= 3;
-
-  char filename[256];
-  snprintf(filename, sizeof(filename), "%s.%d.ppm", pdf_name, num);
-  FILE* fp = fopen(filename, "wb");
-  if (!fp)
-    return "";
-
-  fprintf(fp, "P6\n# PDF test render\n%d %d\n255\n", width, height);
-  // Source data is B, G, R, unused.
-  // Dest data is R, G, B.
-  const uint8_t* buffer = reinterpret_cast<const uint8_t*>(buffer_void);
-  std::vector<uint8_t> result(out_len);
-  for (int h = 0; h < height; ++h) {
-    const uint8_t* src_line = buffer + (stride * h);
-    uint8_t* dest_line = result.data() + (width * h * 3);
-    for (int w = 0; w < width; ++w) {
-      // R
-      dest_line[w * 3] = src_line[(w * 4) + 2];
-      // G
-      dest_line[(w * 3) + 1] = src_line[(w * 4) + 1];
-      // B
-      dest_line[(w * 3) + 2] = src_line[w * 4];
-    }
-  }
-  if (fwrite(result.data(), out_len, 1, fp) != 1)
-    fprintf(stderr, "Failed to write to %s\n", filename);
-
-  fclose(fp);
-  return std::string(filename);
-}
-
-void WriteText(FPDF_PAGE page, const char* pdf_name, int num) {
-  char filename[256];
-  int chars_formatted =
-      snprintf(filename, sizeof(filename), "%s.%d.txt", pdf_name, num);
-  if (chars_formatted < 0 ||
-      static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
-    fprintf(stderr, "Filename %s is too long\n", filename);
-    return;
-  }
-
-  FILE* fp = fopen(filename, "w");
-  if (!fp) {
-    fprintf(stderr, "Failed to open %s for output\n", filename);
-    return;
-  }
-
-  // Output in UTF32-LE.
-  uint32_t bom = 0x0000FEFF;
-  if (fwrite(&bom, sizeof(bom), 1, fp) != 1) {
-    fprintf(stderr, "Failed to write to %s\n", filename);
-    (void)fclose(fp);
-    return;
-  }
-
-  ScopedFPDFTextPage textpage(FPDFText_LoadPage(page));
-  for (int i = 0; i < FPDFText_CountChars(textpage.get()); i++) {
-    uint32_t c = FPDFText_GetUnicode(textpage.get(), i);
-    if (fwrite(&c, sizeof(c), 1, fp) != 1) {
-      fprintf(stderr, "Failed to write to %s\n", filename);
-      break;
-    }
-  }
-  (void)fclose(fp);
-}
-
-void WriteAnnot(FPDF_PAGE page, const char* pdf_name, int num) {
-  // Open the output text file.
-  char filename[256];
-  int chars_formatted =
-      snprintf(filename, sizeof(filename), "%s.%d.annot.txt", pdf_name, num);
-  if (chars_formatted < 0 ||
-      static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
-    fprintf(stderr, "Filename %s is too long\n", filename);
-    return;
-  }
-
-  FILE* fp = fopen(filename, "w");
-  if (!fp) {
-    fprintf(stderr, "Failed to open %s for output\n", filename);
-    return;
-  }
-
-  int annot_count = FPDFPage_GetAnnotCount(page);
-  fprintf(fp, "Number of annotations: %d\n\n", annot_count);
-
-  // Iterate through all annotations on this page.
-  for (int i = 0; i < annot_count; ++i) {
-    // Retrieve the annotation object and its subtype.
-    fprintf(fp, "Annotation #%d:\n", i + 1);
-    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, i));
-    if (!annot) {
-      fprintf(fp, "Failed to retrieve annotation!\n\n");
-      continue;
-    }
-
-    FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot.get());
-    fprintf(fp, "Subtype: %s\n", AnnotSubtypeToCString(subtype));
-
-    // Retrieve the annotation flags.
-    fprintf(fp, "Flags set: %s\n",
-            AnnotFlagsToString(FPDFAnnot_GetFlags(annot.get())).c_str());
-
-    // Retrieve the annotation's object count and object types.
-    const int obj_count = FPDFAnnot_GetObjectCount(annot.get());
-    fprintf(fp, "Number of objects: %d\n", obj_count);
-    if (obj_count > 0) {
-      fprintf(fp, "Object types: ");
-      for (int j = 0; j < obj_count; ++j) {
-        const char* type = PageObjectTypeToCString(
-            FPDFPageObj_GetType(FPDFAnnot_GetObject(annot.get(), j)));
-        fprintf(fp, "%s  ", type);
-      }
-      fprintf(fp, "\n");
-    }
-
-    // Retrieve the annotation's color and interior color.
-    unsigned int R;
-    unsigned int G;
-    unsigned int B;
-    unsigned int A;
-    if (FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, &R, &G, &B,
-                           &A)) {
-      fprintf(fp, "Color in RGBA: %d %d %d %d\n", R, G, B, A);
-    } else {
-      fprintf(fp, "Failed to retrieve color.\n");
-    }
-    if (FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_InteriorColor, &R,
-                           &G, &B, &A)) {
-      fprintf(fp, "Interior color in RGBA: %d %d %d %d\n", R, G, B, A);
-    } else {
-      fprintf(fp, "Failed to retrieve interior color.\n");
-    }
-
-    // Retrieve the annotation's contents and author.
-    static constexpr char kContentsKey[] = "Contents";
-    static constexpr char kAuthorKey[] = "T";
-    unsigned long length_bytes =
-        FPDFAnnot_GetStringValue(annot.get(), kContentsKey, nullptr, 0);
-    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
-    FPDFAnnot_GetStringValue(annot.get(), kContentsKey, buf.data(),
-                             length_bytes);
-    fprintf(fp, "Content: %ls\n", GetPlatformWString(buf.data()).c_str());
-    length_bytes =
-        FPDFAnnot_GetStringValue(annot.get(), kAuthorKey, nullptr, 0);
-    buf = GetFPDFWideStringBuffer(length_bytes);
-    FPDFAnnot_GetStringValue(annot.get(), kAuthorKey, buf.data(), length_bytes);
-    fprintf(fp, "Author: %ls\n", GetPlatformWString(buf.data()).c_str());
-
-    // Retrieve the annotation's quadpoints if it is a markup annotation.
-    if (FPDFAnnot_HasAttachmentPoints(annot.get())) {
-      size_t qp_count = FPDFAnnot_CountAttachmentPoints(annot.get());
-      fprintf(fp, "Number of quadpoints sets: %zu\n", qp_count);
-
-      // Iterate through all quadpoints of the current annotation
-      for (size_t j = 0; j < qp_count; ++j) {
-        FS_QUADPOINTSF quadpoints;
-        if (FPDFAnnot_GetAttachmentPoints(annot.get(), j, &quadpoints)) {
-          fprintf(fp,
-                  "Quadpoints set #%zu: (%.3f, %.3f), (%.3f, %.3f), "
-                  "(%.3f, %.3f), (%.3f, %.3f)\n",
-                  j + 1, quadpoints.x1, quadpoints.y1, quadpoints.x2,
-                  quadpoints.y2, quadpoints.x3, quadpoints.y3, quadpoints.x4,
-                  quadpoints.y4);
-        } else {
-          fprintf(fp, "Failed to retrieve quadpoints set #%zu.\n", j + 1);
-        }
-      }
-    }
-
-    // Retrieve the annotation's rectangle coordinates.
-    FS_RECTF rect;
-    if (FPDFAnnot_GetRect(annot.get(), &rect)) {
-      fprintf(fp, "Rectangle: l - %.3f, b - %.3f, r - %.3f, t - %.3f\n\n",
-              rect.left, rect.bottom, rect.right, rect.top);
-    } else {
-      fprintf(fp, "Failed to retrieve annotation rectangle.\n");
-    }
-  }
-
-  (void)fclose(fp);
-}
-
-std::string WritePng(const char* pdf_name,
-                     int num,
-                     void* buffer,
-                     int stride,
-                     int width,
-                     int height) {
-  if (!CheckDimensions(stride, width, height))
-    return "";
-
-  auto input =
-      pdfium::make_span(static_cast<uint8_t*>(buffer), stride * height);
-  std::vector<uint8_t> png_encoding =
-      EncodePng(input, width, height, stride, FPDFBitmap_BGRA);
-  if (png_encoding.empty()) {
-    fprintf(stderr, "Failed to convert bitmap to PNG\n");
-    return "";
-  }
-
-  char filename[256];
-  int chars_formatted =
-      snprintf(filename, sizeof(filename), "%s.%d.png", pdf_name, num);
-  if (chars_formatted < 0 ||
-      static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
-    fprintf(stderr, "Filename %s is too long\n", filename);
-    return "";
-  }
-
-  FILE* fp = fopen(filename, "wb");
-  if (!fp) {
-    fprintf(stderr, "Failed to open %s for output\n", filename);
-    return "";
-  }
-
-  size_t bytes_written =
-      fwrite(&png_encoding.front(), 1, png_encoding.size(), fp);
-  if (bytes_written != png_encoding.size())
-    fprintf(stderr, "Failed to write to %s\n", filename);
-
-  (void)fclose(fp);
-  return std::string(filename);
-}
-
-#ifdef _WIN32
-std::string WriteBmp(const char* pdf_name,
-                     int num,
-                     void* buffer,
-                     int stride,
-                     int width,
-                     int height) {
-  if (!CheckDimensions(stride, width, height))
-    return "";
-
-  int out_len = stride * height;
-  if (out_len > INT_MAX / 3)
-    return "";
-
-  char filename[256];
-  snprintf(filename, sizeof(filename), "%s.%d.bmp", pdf_name, num);
-  FILE* fp = fopen(filename, "wb");
-  if (!fp)
-    return "";
-
-  BITMAPINFO bmi = {};
-  bmi.bmiHeader.biSize = sizeof(bmi) - sizeof(RGBQUAD);
-  bmi.bmiHeader.biWidth = width;
-  bmi.bmiHeader.biHeight = -height;  // top-down image
-  bmi.bmiHeader.biPlanes = 1;
-  bmi.bmiHeader.biBitCount = 32;
-  bmi.bmiHeader.biCompression = BI_RGB;
-  bmi.bmiHeader.biSizeImage = 0;
-
-  BITMAPFILEHEADER file_header = {};
-  file_header.bfType = 0x4d42;
-  file_header.bfSize = sizeof(file_header) + bmi.bmiHeader.biSize + out_len;
-  file_header.bfOffBits = file_header.bfSize - out_len;
-
-  if (fwrite(&file_header, sizeof(file_header), 1, fp) != 1 ||
-      fwrite(&bmi, bmi.bmiHeader.biSize, 1, fp) != 1 ||
-      fwrite(buffer, out_len, 1, fp) != 1) {
-    fprintf(stderr, "Failed to write to %s\n", filename);
-  }
-  fclose(fp);
-  return std::string(filename);
-}
-
-void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) {
-  char filename[256];
-  snprintf(filename, sizeof(filename), "%s.%d.emf", pdf_name, num);
-
-  HDC dc = CreateEnhMetaFileA(nullptr, filename, nullptr, nullptr);
-
-  int width = static_cast<int>(FPDF_GetPageWidthF(page));
-  int height = static_cast<int>(FPDF_GetPageHeightF(page));
-  HRGN rgn = CreateRectRgn(0, 0, width, height);
-  SelectClipRgn(dc, rgn);
-  DeleteObject(rgn);
-
-  SelectObject(dc, GetStockObject(NULL_PEN));
-  SelectObject(dc, GetStockObject(WHITE_BRUSH));
-  // If a PS_NULL pen is used, the dimensions of the rectangle are 1 pixel less.
-  Rectangle(dc, 0, 0, width + 1, height + 1);
-
-  FPDF_RenderPage(dc, page, 0, 0, width, height, 0, FPDF_ANNOT | FPDF_PRINTING);
-
-  DeleteEnhMetaFile(CloseEnhMetaFile(dc));
-}
-
-void WritePS(FPDF_PAGE page, const char* pdf_name, int num) {
-  char filename[256];
-  snprintf(filename, sizeof(filename), "%s.%d.ps", pdf_name, num);
-  FILE* fp = fopen(filename, "wb");
-  if (!fp)
-    return;
-
-  HDC dc = CreateEnhMetaFileA(nullptr, nullptr, nullptr, nullptr);
-
-  int width = static_cast<int>(FPDF_GetPageWidthF(page));
-  int height = static_cast<int>(FPDF_GetPageHeightF(page));
-  FPDF_RenderPage(dc, page, 0, 0, width, height, 0, FPDF_ANNOT | FPDF_PRINTING);
-
-  HENHMETAFILE emf = CloseEnhMetaFile(dc);
-  std::vector<const ENHMETARECORD*> items;
-  EnumEnhMetaFile(nullptr, emf, &EnhMetaFileProc, &items, nullptr);
-  for (const ENHMETARECORD* record : items) {
-    if (record->iType != EMR_GDICOMMENT)
-      continue;
-
-    const auto* comment = reinterpret_cast<const EMRGDICOMMENT*>(record);
-    const char* data = reinterpret_cast<const char*>(comment->Data);
-    uint16_t size = *reinterpret_cast<const uint16_t*>(data);
-    if (fwrite(data + sizeof(uint16_t), size, 1, fp) != 1) {
-      fprintf(stderr, "Failed to write to %s\n", filename);
-      break;
-    }
-  }
-  fclose(fp);
-  DeleteEnhMetaFile(emf);
-}
-#endif  // _WIN32
-
-#ifdef PDF_ENABLE_SKIA
-std::string WriteSkp(const char* pdf_name,
-                     int num,
-                     SkPictureRecorder* recorder) {
-  char filename[256];
-  int chars_formatted =
-      snprintf(filename, sizeof(filename), "%s.%d.skp", pdf_name, num);
-
-  if (chars_formatted < 0 ||
-      static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
-    fprintf(stderr, "Filename %s is too long\n", filename);
-    return "";
-  }
-
-  sk_sp<SkPicture> picture(recorder->finishRecordingAsPicture());
-  SkFILEWStream wStream(filename);
-  picture->serialize(&wStream);
-  return std::string(filename);
-}
-#endif
-
-enum class ThumbnailDecodeType { kBitmap, kRawStream, kDecodedStream };
-
-bool GetThumbnailFilename(char* name_buf,
-                          size_t name_buf_size,
-                          const char* pdf_name,
-                          int page_num,
-                          ThumbnailDecodeType decode_type) {
-  const char* format;
-  switch (decode_type) {
-    case ThumbnailDecodeType::kBitmap:
-      format = "%s.thumbnail.%d.png";
-      break;
-    case ThumbnailDecodeType::kDecodedStream:
-      format = "%s.thumbnail.decoded.%d.bin";
-      break;
-    case ThumbnailDecodeType::kRawStream:
-      format = "%s.thumbnail.raw.%d.bin";
-      break;
-  }
-
-  int chars_formatted =
-      snprintf(name_buf, name_buf_size, format, pdf_name, page_num);
-  if (chars_formatted < 0 ||
-      static_cast<size_t>(chars_formatted) >= name_buf_size) {
-    fprintf(stderr, "Filename %s for saving is too long.\n", name_buf);
-    return false;
-  }
-
-  return true;
-}
-
-void WriteBufferToFile(const void* buf,
-                       size_t buflen,
-                       const char* filename,
-                       const char* filetype) {
-  FILE* fp = fopen(filename, "wb");
-  if (!fp) {
-    fprintf(stderr, "Failed to open %s for saving %s.", filename, filetype);
-    return;
-  }
-
-  size_t bytes_written = fwrite(buf, 1, buflen, fp);
-  if (bytes_written == buflen)
-    fprintf(stderr, "Successfully wrote %s %s.\n", filetype, filename);
-  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)
-    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))
-    return png_encoding;
-
-  auto input = pdfium::make_span(
-      static_cast<const uint8_t*>(FPDFBitmap_GetBuffer(bitmap.get())),
-      stride * height);
-
-  png_encoding = EncodePng(input, width, height, stride, format);
-  return png_encoding;
-}
-
-void WriteAttachments(FPDF_DOCUMENT doc, const std::string& name) {
-  for (int i = 0; i < FPDFDoc_GetAttachmentCount(doc); ++i) {
-    FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(doc, i);
-
-    // Retrieve the attachment file name.
-    std::string attachment_name;
-    unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
-    if (length_bytes) {
-      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)
-        attachment_name = GetPlatformString(buf.data());
-    }
-    if (attachment_name.empty()) {
-      fprintf(stderr, "Attachment #%d has an empty file name.\n", i + 1);
-      continue;
-    }
-
-    // Calculate the full attachment file name.
-    char save_name[256];
-    int chars_formatted =
-        snprintf(save_name, sizeof(save_name), "%s.attachment.%s", name.c_str(),
-                 attachment_name.c_str());
-    if (chars_formatted < 0 ||
-        static_cast<size_t>(chars_formatted) >= sizeof(save_name)) {
-      fprintf(stderr, "Filename %s is too long.\n", save_name);
-      continue;
-    }
-
-    // Retrieve the attachment.
-    length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0);
-    std::vector<char> data_buf(length_bytes);
-    if (length_bytes) {
-      unsigned long actual_length_bytes =
-          FPDFAttachment_GetFile(attachment, data_buf.data(), length_bytes);
-      if (actual_length_bytes != length_bytes)
-        data_buf.clear();
-    }
-    if (data_buf.empty()) {
-      fprintf(stderr, "Attachment \"%s\" is empty.\n", attachment_name.c_str());
-      continue;
-    }
-
-    // Write the attachment file.
-    WriteBufferToFile(data_buf.data(), length_bytes, save_name, "attachment");
-  }
-}
-
-void WriteImages(FPDF_PAGE page, const char* pdf_name, int page_num) {
-  for (int i = 0; i < FPDFPage_CountObjects(page); ++i) {
-    FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i);
-    if (FPDFPageObj_GetType(obj) != FPDF_PAGEOBJ_IMAGE)
-      continue;
-
-    ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
-    if (!bitmap) {
-      fprintf(stderr, "Image object #%d on page #%d has an empty bitmap.\n",
-              i + 1, page_num + 1);
-      continue;
-    }
-
-    char filename[256];
-    int chars_formatted = snprintf(filename, sizeof(filename), "%s.%d.%d.png",
-                                   pdf_name, page_num, i);
-    if (chars_formatted < 0 ||
-        static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
-      fprintf(stderr, "Filename %s for saving image is too long.\n", filename);
-      continue;
-    }
-
-    std::vector<uint8_t> png_encoding = EncodeBitmapToPng(std::move(bitmap));
-    if (png_encoding.empty()) {
-      fprintf(stderr,
-              "Failed to convert image object #%d, on page #%d to png.\n",
-              i + 1, page_num + 1);
-      continue;
-    }
-
-    WriteBufferToFile(&png_encoding.front(), png_encoding.size(), filename,
-                      "image");
-  }
-}
-
-void WriteDecodedThumbnailStream(FPDF_PAGE page,
-                                 const char* pdf_name,
-                                 int page_num) {
-  char filename[256];
-  if (!GetThumbnailFilename(filename, sizeof(filename), pdf_name, page_num,
-                            ThumbnailDecodeType::kDecodedStream)) {
-    return;
-  }
-
-  unsigned long decoded_data_size =
-      FPDFPage_GetDecodedThumbnailData(page, nullptr, 0u);
-
-  // Only continue if there actually is a thumbnail for this page
-  if (decoded_data_size == 0) {
-    fprintf(stderr, "Failed to get decoded thumbnail for page #%d.\n",
-            page_num + 1);
-    return;
-  }
-
-  std::vector<uint8_t> thumb_buf(decoded_data_size);
-  if (FPDFPage_GetDecodedThumbnailData(
-          page, thumb_buf.data(), decoded_data_size) != decoded_data_size) {
-    fprintf(stderr, "Failed to get decoded thumbnail data for %s.\n", filename);
-    return;
-  }
-
-  WriteBufferToFile(thumb_buf.data(), decoded_data_size, filename,
-                    "decoded thumbnail");
-}
-
-void WriteRawThumbnailStream(FPDF_PAGE page,
-                             const char* pdf_name,
-                             int page_num) {
-  char filename[256];
-  if (!GetThumbnailFilename(filename, sizeof(filename), pdf_name, page_num,
-                            ThumbnailDecodeType::kRawStream)) {
-    return;
-  }
-
-  unsigned long raw_data_size = FPDFPage_GetRawThumbnailData(page, nullptr, 0u);
-
-  // Only continue if there actually is a thumbnail for this page
-  if (raw_data_size == 0) {
-    fprintf(stderr, "Failed to get raw thumbnail data for page #%d.\n",
-            page_num + 1);
-    return;
-  }
-
-  std::vector<uint8_t> thumb_buf(raw_data_size);
-  if (FPDFPage_GetRawThumbnailData(page, thumb_buf.data(), raw_data_size) !=
-      raw_data_size) {
-    fprintf(stderr, "Failed to get raw thumbnail data for %s.\n", filename);
-    return;
-  }
-
-  WriteBufferToFile(thumb_buf.data(), raw_data_size, filename, "raw thumbnail");
-}
-
-void WriteThumbnail(FPDF_PAGE page, const char* pdf_name, int page_num) {
-  char filename[256];
-  if (!GetThumbnailFilename(filename, sizeof(filename), pdf_name, page_num,
-                            ThumbnailDecodeType::kBitmap)) {
-    return;
-  }
-
-  ScopedFPDFBitmap thumb_bitmap(FPDFPage_GetThumbnailAsBitmap(page));
-  if (!thumb_bitmap) {
-    fprintf(stderr, "Thumbnail of page #%d has an empty bitmap.\n",
-            page_num + 1);
-    return;
-  }
-
-  std::vector<uint8_t> png_encoding =
-      EncodeBitmapToPng(std::move(thumb_bitmap));
-  if (png_encoding.empty()) {
-    fprintf(stderr, "Failed to convert thumbnail of page #%d to png.\n",
-            page_num + 1);
-    return;
-  }
-
-  WriteBufferToFile(&png_encoding.front(), png_encoding.size(), filename,
-                    "thumbnail");
-}
diff --git a/samples/pdfium_test_write_helper.h b/samples/pdfium_test_write_helper.h
deleted file mode 100644
index b53d555..0000000
--- a/samples/pdfium_test_write_helper.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SAMPLES_PDFIUM_TEST_WRITE_HELPER_H_
-#define SAMPLES_PDFIUM_TEST_WRITE_HELPER_H_
-
-#include <string>
-
-#include "public/fpdfview.h"
-
-#ifdef PDF_ENABLE_SKIA
-#include "third_party/skia/include/core/SkPictureRecorder.h"
-#include "third_party/skia/include/core/SkStream.h"
-#endif
-
-std::string WritePpm(const char* pdf_name,
-                     int num,
-                     void* buffer_void,
-                     int stride,
-                     int width,
-                     int height);
-void WriteText(FPDF_PAGE page, const char* pdf_name, int num);
-void WriteAnnot(FPDF_PAGE page, const char* pdf_name, int num);
-std::string WritePng(const char* pdf_name,
-                     int num,
-                     void* buffer,
-                     int stride,
-                     int width,
-                     int height);
-
-#ifdef _WIN32
-std::string WriteBmp(const char* pdf_name,
-                     int num,
-                     void* buffer,
-                     int stride,
-                     int width,
-                     int height);
-void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num);
-void WritePS(FPDF_PAGE page, const char* pdf_name, int num);
-#endif  // _WIN32
-
-#ifdef PDF_ENABLE_SKIA
-std::string WriteSkp(const char* pdf_name,
-                     int num,
-                     SkPictureRecorder* recorder);
-#endif  // PDF_ENABLE_SKIA
-
-void WriteAttachments(FPDF_DOCUMENT doc, const std::string& name);
-void WriteImages(FPDF_PAGE page, const char* pdf_name, int page_num);
-void WriteDecodedThumbnailStream(FPDF_PAGE page,
-                                 const char* pdf_name,
-                                 int page_num);
-void WriteRawThumbnailStream(FPDF_PAGE page,
-                             const char* pdf_name,
-                             int page_num);
-void WriteThumbnail(FPDF_PAGE page, const char* pdf_name, int page_num);
-
-#endif  // SAMPLES_PDFIUM_TEST_WRITE_HELPER_H_
diff --git a/samples/simple_no_v8.c b/samples/simple_no_v8.c
new file mode 100644
index 0000000..7e9d7b9
--- /dev/null
+++ b/samples/simple_no_v8.c
@@ -0,0 +1,52 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// No-frills example of how to initialize and call into a PDFium environment,
+// from C. The PDFium API is compatible with C (the C++ internals are hidden
+// beneath it).
+
+#include <string.h>
+
+#include "public/fpdf_edit.h"
+#include "public/fpdf_formfill.h"
+#include "public/fpdfview.h"
+
+int main(int argc, const char* argv[]) {
+  // The PDF library must be initialized before creating a document.
+  FPDF_LIBRARY_CONFIG config;
+  memset(&config, 0, sizeof(config));
+  config.version = 3;
+  FPDF_InitLibraryWithConfig(&config);
+
+  // The document must be created before creating a form-fill environment.
+  // Typically use FPDF_LoadDocument() for pre-existing documents. Here, we
+  // create a new blank document for simplicity.
+  FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
+
+  FPDF_FORMFILLINFO formfillinfo;
+  memset(&formfillinfo, 0, sizeof(formfillinfo));
+  formfillinfo.version = 1;
+
+  FPDF_FORMHANDLE form_handle =
+      FPDFDOC_InitFormFillEnvironment(doc, &formfillinfo);
+
+  // Typically use FPDF_LoadPage() for pre-existing pages. Here, we
+  // create a new blank page for simplicity.
+  FPDF_PAGE page = FPDFPage_New(doc, 0, 640.0, 480.0);
+  FORM_OnAfterLoadPage(page, form_handle);
+  FORM_DoPageAAction(page, form_handle, FPDFPAGE_AACTION_OPEN);
+
+  // Do actual work with the page here.
+
+  FORM_DoPageAAction(page, form_handle, FPDFPAGE_AACTION_CLOSE);
+  FORM_OnBeforeClosePage(page, form_handle);
+  FPDF_ClosePage(page);
+
+  FORM_DoDocumentAAction(form_handle, FPDFDOC_AACTION_WC);
+  FPDFDOC_ExitFormFillEnvironment(form_handle);
+  FPDF_CloseDocument(doc);
+  FPDF_DestroyLibrary();
+
+  return 0;
+}
diff --git a/samples/simple_with_v8.cc b/samples/simple_with_v8.cc
new file mode 100644
index 0000000..028669f
--- /dev/null
+++ b/samples/simple_with_v8.cc
@@ -0,0 +1,78 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// No-frills example of how to initialize and call into a PDFium environment
+// including V8 support (but not XFA) from C++. Since the V8 API is in C++,
+// C++ is required in this case (not just C).
+
+#include <string.h>
+
+#include "public/fpdf_edit.h"
+#include "public/fpdf_formfill.h"
+#include "public/fpdfview.h"
+#include "v8/include/libplatform/libplatform.h"
+#include "v8/include/v8-array-buffer.h"
+#include "v8/include/v8-initialization.h"
+#include "v8/include/v8-isolate.h"
+
+int main(int argc, const char* argv[]) {
+  // V8 must be initialized before the PDFium library if using V8.
+  v8::V8::InitializeICUDefaultLocation(argv[0]);
+  v8::V8::InitializeExternalStartupData(argv[0]);
+  v8::Platform* platform = v8::platform::NewDefaultPlatform().release();
+  v8::V8::InitializePlatform(platform);
+  v8::V8::Initialize();
+
+  v8::Isolate::CreateParams params;
+  params.array_buffer_allocator = static_cast<v8::ArrayBuffer::Allocator*>(
+      FPDF_GetArrayBufferAllocatorSharedInstance());
+  v8::Isolate* isolate = v8::Isolate::New(params);
+
+  // The PDF library must be initialized before creating a document.
+  FPDF_LIBRARY_CONFIG config;
+  memset(&config, 0, sizeof(config));
+  config.version = 3;
+  config.m_pIsolate = isolate;
+  config.m_pPlatform = platform;
+  FPDF_InitLibraryWithConfig(&config);
+
+  // The document must be created before creating a form-fill environment.
+  // Typically use FPDF_LoadDocument() for pre-existing documents. Here, we
+  // create a new blank document for simplicity.
+  FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
+
+  IPDF_JSPLATFORM jsplatform;
+  memset(&jsplatform, 0, sizeof(jsplatform));
+
+  FPDF_FORMFILLINFO formfillinfo;
+  memset(&formfillinfo, 0, sizeof(formfillinfo));
+  formfillinfo.version = 1;
+  formfillinfo.m_pJsPlatform = &jsplatform;
+
+  FPDF_FORMHANDLE form_handle =
+      FPDFDOC_InitFormFillEnvironment(doc, &formfillinfo);
+
+  // Typically use FPDF_LoadPage() for pre-existing pages. Here, we
+  // create a new blank page for simplicity.
+  FPDF_PAGE page = FPDFPage_New(doc, 0, 640.0, 480.0);
+  FORM_OnAfterLoadPage(page, form_handle);
+  FORM_DoPageAAction(page, form_handle, FPDFPAGE_AACTION_OPEN);
+
+  // Do actual work with the page here.
+
+  FORM_DoPageAAction(page, form_handle, FPDFPAGE_AACTION_CLOSE);
+  FORM_OnBeforeClosePage(page, form_handle);
+  FPDF_ClosePage(page);
+
+  FORM_DoDocumentAAction(form_handle, FPDFDOC_AACTION_WC);
+  FPDFDOC_ExitFormFillEnvironment(form_handle);
+  FPDF_CloseDocument(doc);
+  FPDF_DestroyLibrary();
+
+  isolate->Dispose();
+  v8::V8::ShutdownPlatform();
+  delete platform;
+
+  return 0;
+}
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index d49587c..f9d1bf8 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -1,12 +1,13 @@
-# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Copyright 2013 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
 import("//build/config/features.gni")
 import("//build/config/ui.gni")
 import("//testing/test.gni")
+import("//third_party/skia/gn/codec.gni")
 import("//third_party/skia/gn/shared_sources.gni")
-import("//third_party/skia/third_party/skcms/skcms.gni")
+import("//third_party/skia/modules/skcms/skcms.gni")
 import("features.gni")
 
 if (current_cpu == "arm") {
@@ -16,14 +17,17 @@
   import("//build/config/mips.gni")
 }
 
-skia_support_gpu = !is_ios
+skia_use_ganesh_backend = !is_ios
 skia_support_pdf = false
 
 # External-facing config for dependent code.
 config("skia_config") {
   include_dirs = [ "//third_party/skia" ]
 
-  defines = [ "SK_USER_CONFIG_HEADER=\"../../skia/config/SkUserConfig.h\"" ]
+  defines = [
+    "SK_ENCODE_PNG",
+    "SK_USER_CONFIG_HEADER=\"../../skia/config/SkPdfiumUserConfig.h\"",
+  ]
 
   if (is_win) {
     defines += [ "SK_FREETYPE_MINIMUM_RUNTIME_VERSION=(((FREETYPE_MAJOR) * 0x01000000) | ((FREETYPE_MINOR) * 0x00010000) | ((FREETYPE_PATCH) * 0x00000100))" ]
@@ -36,10 +40,8 @@
     ]
   }
 
-  if (skia_support_gpu) {
-    defines += [ "SK_SUPPORT_GPU=1" ]
-  } else {
-    defines += [ "SK_SUPPORT_GPU=0" ]
+  if (skia_use_ganesh_backend) {
+    defines += [ "SK_GANESH" ]
   }
 
   if (skia_use_gl) {
@@ -64,11 +66,11 @@
 
 # Internal-facing config for Skia library code.
 config("skia_library_config") {
-  defines = []
-
-  if (is_component_build) {
-    defines += [ "SKIA_IMPLEMENTATION=1" ]
-  }
+  # Turn on SK_API to export Skia's public API
+  defines = [
+    "IS_SKIA_IMPL=1",
+    "SKIA_IMPLEMENTATION=1",
+  ]
 
   if (current_cpu == "arm") {
     if (arm_use_neon) {
@@ -79,7 +81,7 @@
   }
 
   # Settings for text blitting, chosen to approximate the system browser.
-  if (is_linux) {
+  if (is_linux || is_chromeos) {
     defines += [
       "SK_GAMMA_EXPONENT=1.2",
       "SK_GAMMA_CONTRAST=0.2",
@@ -131,12 +133,9 @@
                   # assembly code contains flow control(jmp or jcc) statements.
 
       "/wd4800",  # forcing value to bool 'true/false'(assigning int to bool).
-      "/wd5041",  # out-of-line definition for constexpr static data member is not needed and is deprecated in C++17
+      "/wd5041",  # out-of-line definition for constexpr static data member is
+                  # not needed and is deprecated in C++17
     ]
-    cflags_cc = [ "/std:c++17" ]
-  } else {
-    cflags_cc = [ "-std=c++17" ]
-    cflags_objcc = [ "-std=c++17" ]
   }
 }
 
@@ -152,96 +151,72 @@
   # LLVM automatically sets the equivalent of GCC's -mfp16-format=ieee on ARM
   # builds by default, while GCC itself does not. We need it to enable support
   # for half-precision floating point data types used by SKCMS on ARM.
-  if (is_linux && !is_clang && current_cpu == "arm") {
+  if ((is_linux || is_chromeos) && !is_clang && current_cpu == "arm") {
     cflags += [ "-mfp16-format=ieee" ]
   }
 
-  public = [ "//third_party/skia/include/third_party/skcms/skcms.h" ]
-  include_dirs = [ "//third_party/skia/include/third_party/skcms" ]
-  sources =
-      rebase_path(skcms_sources, ".", "//third_party/skia/third_party/skcms")
+  public =
+      rebase_path(skcms_public_headers, ".", "//third_party/skia/modules/skcms")
+  sources = rebase_path(skcms_sources, ".", "//third_party/skia/modules/skcms")
 }
 
 component("skia") {
+  deps = []
+
   sources = [
     # PDFium sources.
-    "config/SkUserConfig.h",
+    "config/SkPdfiumUserConfig.h",
     "ext/google_logging.cc",
   ]
 
   # The skia sources values are relative to the skia_dir, so we need to rebase.
+  sources += skia_core_public
   sources += skia_sksl_gpu_sources
   sources += skia_sksl_sources
-  sources += skia_utils_sources
+  sources += skia_utils_private
+  sources += skia_xps_sources
+  sources += skia_needs_sksl_sources
+  sources += skia_codec_core
+  sources += skia_codec_decode_bmp
+  sources += skia_encode_srcs
+  sources += skia_encode_png_srcs
   sources += [
-    "//third_party/skia/src/codec/SkMasks.cpp",
     "//third_party/skia/src/fonts/SkFontMgr_indirect.cpp",
     "//third_party/skia/src/fonts/SkRemotableFontMgr.cpp",
-    "//third_party/skia/src/images/SkImageEncoder.cpp",
-    "//third_party/skia/src/ports/SkFontHost_FreeType.cpp",
-    "//third_party/skia/src/ports/SkFontHost_FreeType_common.cpp",
-    "//third_party/skia/src/ports/SkFontHost_win.cpp",
-    "//third_party/skia/src/ports/SkFontMgr_android.cpp",
-    "//third_party/skia/src/ports/SkFontMgr_android_factory.cpp",
-    "//third_party/skia/src/ports/SkFontMgr_android_parser.cpp",
     "//third_party/skia/src/ports/SkGlobalInitialization_default.cpp",
     "//third_party/skia/src/ports/SkImageGenerator_none.cpp",
-    "//third_party/skia/src/ports/SkOSFile_posix.cpp",
     "//third_party/skia/src/ports/SkOSFile_stdio.cpp",
-    "//third_party/skia/src/ports/SkOSFile_win.cpp",
-    "//third_party/skia/src/ports/SkRemotableFontMgr_win_dw.cpp",
-    "//third_party/skia/src/ports/SkScalerContext_win_dw.cpp",
-    "//third_party/skia/src/ports/SkTLS_pthread.cpp",
-    "//third_party/skia/src/ports/SkTLS_win.cpp",
-    "//third_party/skia/src/ports/SkTypeface_win_dw.cpp",
     "//third_party/skia/src/sfnt/SkOTTable_name.cpp",
     "//third_party/skia/src/sfnt/SkOTUtils.cpp",
 
-    #mac
-    "//third_party/skia/src/utils/mac/SkStream_mac.cpp",
-
     #pdfium
     "//third_party/skia/src/ports/SkDiscardableMemory_none.cpp",
-    "//third_party/skia/src/ports/SkFontMgr_custom.cpp",
-    "//third_party/skia/src/ports/SkFontMgr_custom_empty.cpp",
-    "//third_party/skia/src/ports/SkFontMgr_custom_empty_factory.cpp",
     "//third_party/skia/src/ports/SkMemory_malloc.cpp",
   ]
 
   # This and skia_opts are really the same conceptual target so share headers.
   allow_circular_includes_from = [ ":skia_opts" ]
 
-  if (current_cpu == "arm") {
-    sources += [ "//third_party/skia/src/core/SkUtilsArm.cpp" ]
-  }
-
   # GPU
-  if (skia_support_gpu) {
-    sources += skia_gpu_sources
+  if (skia_use_ganesh_backend) {
+    sources += skia_gpu_public
+    sources += skia_ganesh_private
     sources += skia_null_gpu_sources
+    sources += skia_shared_gpu_sources
     if (skia_use_gl) {
-      sources += skia_gl_gpu_sources
+      sources += skia_gpu_gl_public
+      sources += skia_gpu_gl_private
     }
   }
 
   # Remove unused util files include in utils.gni
   sources -= [
     "//third_party/skia/src/utils/SkCamera.cpp",
-    "//third_party/skia/src/utils/SkFrontBufferedStream.cpp",
-    "//third_party/skia/src/utils/SkInterpolator.cpp",
     "//third_party/skia/src/utils/SkParsePath.cpp",
   ]
 
   if (is_win) {
-    sources -= [
-      # Keeping _win.cpp
-      "//third_party/skia/src/utils/SkThreadUtils_pthread.cpp",
-    ]
-  } else {
-    sources -= [
-      # Keeping _pthread.cpp
-      "//third_party/skia/src/utils/SkThreadUtils_win.cpp",
-    ]
+    libs = [ "fontsub.lib" ]
   }
 
   # need separate win section to handle chromes auto gn filter
@@ -249,42 +224,101 @@
   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",
     ]
   }
 
-  # Fixup skia library sources.
-  if (is_win) {
-    sources -= [
-      "//third_party/skia/src/ports/SkOSFile_posix.cpp",
-      "//third_party/skia/src/ports/SkTLS_pthread.cpp",
+  # Select Skia ports.
+
+  # FreeType is needed everywhere (except on iOS), on Linux and Android as main
+  # font backend, on Windows and Mac as fallback backend for Variations.
+  if (!is_ios) {
+    sources += [
+      "//third_party/skia/src/ports/SkFontHost_FreeType.cpp",
+      "//third_party/skia/src/ports/SkFontHost_FreeType_common.cpp",
     ]
-  } else {
-    sources -= [
+  }
+
+  if (is_win) {
+    sources += [
       "//third_party/skia/src/ports/SkFontHost_win.cpp",
+
+      # TODO(crbug.com/pdfium/1830)  Consider using SkFontMgr_win_dw_factory
+      # instead of SkFontMgr_custom_empty (which is what the embedder test
+      # hashes are based upon).
+      "//third_party/skia/src/ports/SkFontMgr_custom.cpp",
+      "//third_party/skia/src/ports/SkFontMgr_custom_empty.cpp",
+      "//third_party/skia/src/ports/SkFontMgr_custom_empty_factory.cpp",
+      "//third_party/skia/src/ports/SkFontMgr_win_dw.cpp",
       "//third_party/skia/src/ports/SkOSFile_win.cpp",
       "//third_party/skia/src/ports/SkRemotableFontMgr_win_dw.cpp",
       "//third_party/skia/src/ports/SkScalerContext_win_dw.cpp",
-      "//third_party/skia/src/ports/SkTLS_win.cpp",
       "//third_party/skia/src/ports/SkTypeface_win_dw.cpp",
     ]
+  } else {
+    sources += [ "//third_party/skia/src/ports/SkOSFile_posix.cpp" ]
   }
-  if (!is_android) {
-    sources -= [
+
+  frameworks = []
+  if (is_apple) {
+    sources += [
+      # TODO(crbug.com/pdfium/1830)  Consider using SkFontMgr_mac_ct instead of
+      # SkFontMgr_custom_empty (which is what the embedder test hashes are
+      # based upon).
+      "//third_party/skia/src/ports/SkFontMgr_custom.cpp",
+      "//third_party/skia/src/ports/SkFontMgr_custom_empty.cpp",
+      "//third_party/skia/src/ports/SkFontMgr_custom_empty_factory.cpp",
+      "//third_party/skia/src/ports/SkScalerContext_mac_ct.cpp",
+      "//third_party/skia/src/ports/SkScalerContext_mac_ct.h",
+      "//third_party/skia/src/ports/SkTypeface_mac_ct.cpp",
+      "//third_party/skia/src/ports/SkTypeface_mac_ct.h",
+    ]
+    frameworks += [
+      "CoreFoundation.framework",
+      "CoreGraphics.framework",
+      "CoreText.framework",
+    ]
+  }
+
+  if (is_linux || is_chromeos) {
+    sources += [
+      "//third_party/skia/src/ports/SkFontConfigInterface.cpp",
+      "//third_party/skia/src/ports/SkFontConfigInterface_direct.cpp",
+      "//third_party/skia/src/ports/SkFontConfigInterface_direct_factory.cpp",
+      "//third_party/skia/src/ports/SkFontMgr_FontConfigInterface.cpp",
+      "//third_party/skia/src/ports/SkFontMgr_custom.cpp",
+      "//third_party/skia/src/ports/SkFontMgr_custom_empty.cpp",
+      "//third_party/skia/src/ports/SkFontMgr_custom_empty_factory.cpp",
+    ]
+  }
+
+  if (is_android) {
+    sources += [
+      # Unlike Chromium, standalone PDFium on Linux and Chrome OS does not
+      # require these files, since PDFium does not perform Android emulation.
+      # Note that this requires expat.
       "//third_party/skia/src/ports/SkFontMgr_android.cpp",
       "//third_party/skia/src/ports/SkFontMgr_android_factory.cpp",
       "//third_party/skia/src/ports/SkFontMgr_android_parser.cpp",
     ]
   }
-  if (!is_linux && !is_android && !is_win && !is_mac) {
-    sources -= [
-      "//third_party/skia/src/ports/SkFontHost_FreeType.cpp",
-      "//third_party/skia/src/ports/SkFontHost_FreeType_common.cpp",
+
+  if (is_fuchsia) {
+    sources += [
+      # TODO(crbug.com/pdfium/2019): Consider using SkFontMgr_fuchsia.cpp
+      # instead of SkFontMgr_custom_empty.cpp.
       "//third_party/skia/src/ports/SkFontMgr_custom.cpp",
+      "//third_party/skia/src/ports/SkFontMgr_custom_empty.cpp",
       "//third_party/skia/src/ports/SkFontMgr_custom_empty_factory.cpp",
     ]
+    deps += [
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.fonts:fuchsia.fonts_hlcpp",
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.io:fuchsia.io_hlcpp",
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.sys:fuchsia.sys_hlcpp",
+      "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
+      "//third_party/fuchsia-sdk/sdk/pkg/zx",
+      "//third_party/icu:icuuc",
+    ]
   }
 
   if (is_clang && !is_nacl) {
@@ -302,25 +336,26 @@
   ]
   public_configs = [ ":skia_config" ]
 
-  deps = [
+  deps += [
     ":skcms",
     ":skia_opts",
+    "../third_party:png",
     "../third_party:zlib",
     "//:freetype_common",
   ]
   public_deps = [ ":skia_core_and_effects" ]
 
-  if (is_linux) {
-    deps += [ "//third_party/icu:icuuc" ]
+  if (is_linux || is_chromeos) {
+    deps += [
+      "//third_party:fontconfig",
+      "//third_party/icu:icuuc",
+    ]
   }
 
   if (is_android) {
-    set_sources_assignment_filter([])
-    set_sources_assignment_filter(sources_assignment_filter)
     deps += [
-      "//third_party/android_sdk:cpu_features",
+      "//third_party/cpu_features:ndk_compat",
       "//third_party/expat",
-      "//third_party/freetype-android:freetype",
     ]
   }
 
@@ -330,14 +365,7 @@
   }
 
   if (is_ios) {
-    libs = [ "ImageIO.framework" ]
-    set_sources_assignment_filter([])
-    sources += [
-      "//third_party/skia/src/ports/SkFontHost_mac.cpp",
-      "//third_party/skia/src/utils/mac/SkCreateCGImageRef.cpp",
-      "//third_party/skia/src/utils/mac/SkStream_mac.cpp",
-    ]
-    set_sources_assignment_filter(sources_assignment_filter)
+    frameworks += [ "ImageIO.framework" ]
   }
 
   if (skia_support_pdf) {
@@ -374,25 +402,25 @@
 
     if (is_win) {
       cflags_cc = [
-        "/std:c++17",
-        "/wd5041",  # out-of-line definition for constexpr static data member is not needed and is deprecated in C++17
+        "/wd5041",  # out-of-line definition for constexpr static data member is
+                    # not needed and is deprecated in C++17
       ]
-    } else {
-      cflags_cc = [ "-std=c++17" ]
     }
   }
 }
 
 skia_source_set("skia_core_and_effects") {
-  defines = skia_core_defines
+  defines = []
   sources = skia_core_sources
   sources += skia_effects_sources
+  sources += skia_colorfilters_sources
+  sources += skia_colorfilters_sksl_sources
   sources += skia_effects_imagefilter_sources
 
   visibility = [ ":skia" ]
 }
 
-# Separated out so it can be compiled with different flags for SSE.
+# Bits that involve special vector-y hardware.
 if (current_cpu == "x86" || current_cpu == "x64") {
   skia_source_set("skia_opts_sse3") {
     sources = skia_opts.ssse3_sources
@@ -404,26 +432,6 @@
     }
     visibility = [ ":skia_opts" ]
   }
-  skia_source_set("skia_opts_sse41") {
-    sources = skia_opts.sse41_sources
-    if (!is_win || is_clang) {
-      cflags = [ "-msse4.1" ]
-    }
-    if (is_win) {
-      defines = [ "SK_CPU_SSE_LEVEL=41" ]
-    }
-    visibility = [ ":skia_opts" ]
-  }
-  skia_source_set("skia_opts_sse42") {
-    sources = skia_opts.sse42_sources
-    if (!is_win || is_clang) {
-      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) {
@@ -457,13 +465,10 @@
   defines = []
 
   if (current_cpu == "x86" || current_cpu == "x64") {
-    sources = skia_opts.sse2_sources
     deps = [
       ":skia_opts_avx",
       ":skia_opts_hsw",
       ":skia_opts_sse3",
-      ":skia_opts_sse41",
-      ":skia_opts_sse42",
     ]
   } else if (current_cpu == "arm") {
     # The assembly uses the frame pointer register (r7 in Thumb/r11 in
@@ -471,10 +476,7 @@
     cflags += [ "-fomit-frame-pointer" ]
 
     if (arm_version >= 7) {
-      sources = skia_opts.armv7_sources
       if (arm_use_neon || arm_optionally_use_neon) {
-        sources += skia_opts.neon_sources
-
         # Root build config sets -mfpu=$arm_fpu, which we expect to be neon
         # when running this.
         if (!arm_use_neon) {
@@ -482,24 +484,13 @@
           cflags += [ "-mfpu=neon" ]
         }
       }
-    } else {
-      sources = skia_opts.none_sources
     }
   } else if (current_cpu == "arm64") {
-    sources = skia_opts.arm64_sources
-  } else if (current_cpu == "mipsel") {
+    # Conditional and empty body needed to avoid assert below
+  } else if (current_cpu == "mipsel" || current_cpu == "mips64el") {
     cflags += [ "-fomit-frame-pointer" ]
-
-    if (mips_dsp_rev >= 1) {
-      sources = skia_opts.mips_dsp_sources
-    } else {
-      sources = skia_opts.none_sources
-    }
-  } else if (current_cpu == "mips64el") {
-    cflags += [ "-fomit-frame-pointer" ]
-    sources = skia_opts.none_sources
   } else {
-    assert(false, "Need to port cpu specific stuff from gn/BUILDCONFIG.gn")
+    assert(false, "Unsupported target CPU " + current_cpu)
   }
 
   if (is_android && !is_debug) {
diff --git a/skia/OWNERS b/skia/OWNERS
new file mode 100644
index 0000000..e1922a5
--- /dev/null
+++ b/skia/OWNERS
@@ -0,0 +1,2 @@
+kjlubick@google.com
+bungeman@google.com
\ No newline at end of file
diff --git a/skia/config/SkPdfiumUserConfig.h b/skia/config/SkPdfiumUserConfig.h
new file mode 100644
index 0000000..e035d09
--- /dev/null
+++ b/skia/config/SkPdfiumUserConfig.h
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2006 The PDFium Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKIA_CONFIG_SKUSERCONFIG_H_
+#define SKIA_CONFIG_SKUSERCONFIG_H_
+
+/*  SkTypes.h, the root of the public header files, does the following trick:
+
+    #include "SkPreConfig.h"
+    #include "SkUserConfig.h"
+    #include "SkPostConfig.h"
+
+    SkPreConfig.h runs first, and it is responsible for initializing certain
+    skia defines.
+
+    SkPostConfig.h runs last, and its job is to just check that the final
+    defines are consistent (i.e. that we don't have mutually conflicting
+    defines).
+
+    SkUserConfig.h (this file) runs in the middle. It gets to change or augment
+    the list of flags initially set in preconfig, and then postconfig checks
+    that everything still makes sense.
+
+    Below are optional defines that add, subtract, or change default behavior
+    in Skia. Your port can locally edit this file to enable/disable flags as
+    you choose, or these can be delared on your command line (i.e. -Dfoo).
+
+    By default, this include file will always default to having all of the flags
+    commented out, so including it will have no effect.
+*/
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  Skia has lots of debug-only code. Often this is just null checks or other
+    parameter checking, but sometimes it can be quite intrusive (e.g. check that
+    each 32bit pixel is in premultiplied form). This code can be very useful
+    during development, but will slow things down in a shipping product.
+
+    By default, these mutually exclusive flags are defined in SkPreConfig.h,
+    based on the presence or absence of NDEBUG, but that decision can be changed
+    here.
+ */
+// #define SK_DEBUG
+// #define SK_RELEASE
+
+/*  Skia has certain debug-only code that is extremely intensive even for debug
+    builds.  This code is useful for diagnosing specific issues, but is not
+    generally applicable, therefore it must be explicitly enabled to avoid
+    the performance impact. By default these flags are undefined, but can be
+    enabled by uncommenting them below.
+ */
+// #define SK_DEBUG_GLYPH_CACHE
+// #define SK_DEBUG_PATH
+
+/*  preconfig will have attempted to determine the endianness of the system,
+    but you can change these mutually exclusive flags here.
+ */
+// #define SK_CPU_BENDIAN
+// #define SK_CPU_LENDIAN
+
+/*  Most compilers use the same bit endianness for bit flags in a byte as the
+    system byte endianness, and this is the default. If for some reason this
+    needs to be overridden, specify which of the mutually exclusive flags to
+    use. For example, some atom processors in certain configurations have big
+    endian byte order but little endian bit orders.
+*/
+// #define SK_UINT8_BITFIELD_BENDIAN
+// #define SK_UINT8_BITFIELD_LENDIAN
+
+
+/*  To write debug messages to a console, skia will call SkDebugf(...) following
+    printf conventions (e.g. const char* format, ...). If you want to redirect
+    this to something other than printf, define yours here
+ */
+// #define SkDebugf(...)  MyFunction(__VA_ARGS__)
+
+/*
+ *  To specify a different default font cache limit, define this. If this is
+ *  undefined, skia will use a built-in value.
+ */
+// #define SK_DEFAULT_FONT_CACHE_LIMIT   (1024 * 1024)
+
+/*
+ *  To specify the default size of the image cache, undefine this and set it to
+ *  the desired value (in bytes). SkGraphics.h as a runtime API to set this
+ *  value as well. If this is undefined, a built-in value will be used.
+ */
+// #define SK_DEFAULT_IMAGE_CACHE_LIMIT (1024 * 1024)
+
+/*  Define this to set the upper limit for text to support LCD. Values that
+    are very large increase the cost in the font cache and draw slower, without
+    improving readability. If this is undefined, Skia will use its default
+    value (e.g. 48)
+ */
+// #define SK_MAX_SIZE_FOR_LCDTEXT     48
+
+/*  Change the ordering to work in X windows.
+ */
+// #ifdef SK_SAMPLES_FOR_X
+//         #define SK_R32_SHIFT    16
+//         #define SK_G32_SHIFT    8
+//         #define SK_B32_SHIFT    0
+//         #define SK_A32_SHIFT    24
+// #endif
+
+
+/* Determines whether to build code that supports the Ganesh GPU backend. Some classes
+   that are not GPU-specific, such as SkShader subclasses, have optional code
+   that is used to interact with this GPU backend. If you'd like to
+   include this code, include -DSK_GANESH in your cflags or uncomment below.
+   Defaults to not set (No Ganesh GPU backend).
+   This define affects the ABI of Skia, so make sure it matches the client which uses
+   the compiled version of Skia.
+*/
+// #define SK_GANESH
+
+/* Skia makes use of histogram logging macros to trace the frequency of
+ * events. By default, Skia provides no-op versions of these macros.
+ * Skia consumers can provide their own definitions of these macros to
+ * integrate with their histogram collection backend.
+ */
+// #define SK_HISTOGRAM_BOOLEAN(name, value)
+// #define SK_HISTOGRAM_ENUMERATION(name, value, boundary_value)
+#include "third_party/base/component_export.h"
+
+// ===== Begin Chrome-specific definitions =====
+
+#define SK_MSCALAR_IS_FLOAT
+#undef SK_MSCALAR_IS_DOUBLE
+
+#define GR_MAX_OFFSCREEN_AA_DIM 512
+
+// Handle exporting using base/component_export.h
+#define SK_API COMPONENT_EXPORT(SKIA)
+
+// Log the file and line number for assertions.
+#if defined(SK_BUILD_FOR_WIN) && !defined(__clang__)
+// String formatting with this toolchain not supported.
+#define SkDebugf(...) SkDebugf_FileLineOnly(__FILE__, __LINE__)
+SK_API void SkDebugf_FileLineOnly(const char* file, int line);
+#else
+#define SkDebugf(...) SkDebugf_FileLine(__FILE__, __LINE__, __VA_ARGS__)
+SK_API void SkDebugf_FileLine(const char* file,
+                              int line,
+                              const char* format,
+                              ...);
+#endif
+
+#if !defined(ANDROID)  // On Android, we use the skia default settings.
+#define SK_A32_SHIFT 24
+#define SK_R32_SHIFT 16
+#define SK_G32_SHIFT 8
+#define SK_B32_SHIFT 0
+#endif
+
+#if defined(SK_BUILD_FOR_WIN32)
+
+#define SK_BUILD_FOR_WIN
+
+// Skia uses this deprecated bzero function to fill zeros into a string.
+#define bzero(str, len) memset(str, 0, len)
+
+#elif defined(SK_BUILD_FOR_MAC)
+
+#define SK_CPU_LENDIAN
+#undef SK_CPU_BENDIAN
+
+#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID)
+
+// Prefer FreeType's emboldening algorithm to Skia's
+// TODO: skia used to just use hairline, but has improved since then, so
+// we should revisit this choice...
+#define SK_USE_FREETYPE_EMBOLDEN
+
+#if defined(SK_BUILD_FOR_UNIX) && defined(SK_CPU_BENDIAN)
+// Above we set the order for ARGB channels in registers. I suspect that, on
+// big endian machines, you can keep this the same and everything will work.
+// The in-memory order will be different, of course, but as long as everything
+// is reading memory as words rather than bytes, it will all work. However, if
+// you find that colours are messed up I thought that I would leave a helpful
+// locator for you. Also see the comments in
+// base/gfx/bitmap_platform_device_linux.h
+#error Read the comment at this location
+#endif
+
+#endif
+
+// These flags are no longer defined in Skia, but we have them (temporarily)
+// until we update our call-sites (typically these are for API changes).
+//
+// Remove these as we update our sites.
+//
+#ifndef SK_SUPPORT_LEGACY_GETTOPDEVICE
+#define SK_SUPPORT_LEGACY_GETTOPDEVICE
+#endif
+
+#ifndef SK_SUPPORT_EXOTIC_CLIPOPS
+#define SK_SUPPORT_EXOTIC_CLIPOPS
+#endif
+
+#ifndef SK_SUPPORT_LEGACY_GETDEVICE
+#define SK_SUPPORT_LEGACY_GETDEVICE
+#endif
+
+// Workaround for poor anisotropic mipmap quality,
+// pending Skia ripmap support.
+// (https://bugs.chromium.org/p/skia/issues/detail?id=4863)
+#ifndef SK_SUPPORT_LEGACY_ANISOTROPIC_MIPMAP_SCALE
+#define SK_SUPPORT_LEGACY_ANISOTROPIC_MIPMAP_SCALE
+#endif
+
+#ifndef SK_SUPPORT_LEGACY_REFENCODEDDATA_NOCTX
+#define SK_SUPPORT_LEGACY_REFENCODEDDATA_NOCTX
+#endif
+
+#ifndef SK_IGNORE_ETC1_SUPPORT
+#define SK_IGNORE_ETC1_SUPPORT
+#endif
+
+#ifndef SK_IGNORE_GPU_DITHER
+#define SK_IGNORE_GPU_DITHER
+#endif
+
+#ifndef SK_SUPPORT_LEGACY_EVAL_CUBIC
+#define SK_SUPPORT_LEGACY_EVAL_CUBIC
+#endif
+
+///////////////////////// Imported from BUILD.gn
+
+/* In some places Skia can use static initializers for global initialization,
+ *  or fall back to lazy runtime initialization. Chrome always wants the latter.
+ */
+#define SK_ALLOW_STATIC_GLOBAL_INITIALIZERS 0
+
+/* This flag forces Skia not to use typographic metrics with GDI.
+ */
+#define SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
+
+#define SK_IGNORE_BLURRED_RRECT_OPT
+#define SK_USE_DISCARDABLE_SCALEDIMAGECACHE
+#define SK_WILL_NEVER_DRAW_PERSPECTIVE_TEXT
+
+#define SK_ATTR_DEPRECATED SK_NOTHING_ARG1
+#define SK_ENABLE_INST_COUNT 0
+#define GR_GL_CUSTOM_SETUP_HEADER "GrGLConfig_chrome.h"
+
+// Blink layout tests are baselined to Clang optimizing through the UB in
+// SkDivBits.
+#define SK_SUPPORT_LEGACY_DIVBITS_UB
+
+// mtklein's fiddling with Src / SrcOver.  Will rebaseline these only once when
+// done.
+#define SK_SUPPORT_LEGACY_X86_BLITS
+
+#define SK_DISABLE_TILE_IMAGE_FILTER_OPTIMIZATION
+
+#if defined(SK_BUILD_FOR_WIN) && !defined(__clang__)
+#define SK_ABORT(format, ...) \
+  SkAbort_FileLine(__FILE__, __LINE__, format, ##__VA_ARGS__)
+[[noreturn]] SK_API void SkAbort_FileLine(const char* file,
+                                          int line,
+                                          const char* format,
+                                          ...);
+#endif
+
+// ===== End Chrome-specific definitions =====
+
+#endif  // SKIA_CONFIG_SKUSERCONFIG_H_
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
deleted file mode 100644
index 679a19c..0000000
--- a/skia/config/SkUserConfig.h
+++ /dev/null
@@ -1,254 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SKIA_CONFIG_SKUSERCONFIG_H_
-#define SKIA_CONFIG_SKUSERCONFIG_H_
-
-/*  SkTypes.h, the root of the public header files, does the following trick:
-
-    #include "SkPreConfig.h"
-    #include "SkUserConfig.h"
-    #include "SkPostConfig.h"
-
-    SkPreConfig.h runs first, and it is responsible for initializing certain
-    skia defines.
-
-    SkPostConfig.h runs last, and its job is to just check that the final
-    defines are consistent (i.e. that we don't have mutually conflicting
-    defines).
-
-    SkUserConfig.h (this file) runs in the middle. It gets to change or augment
-    the list of flags initially set in preconfig, and then postconfig checks
-    that everything still makes sense.
-
-    Below are optional defines that add, subtract, or change default behavior
-    in Skia. Your port can locally edit this file to enable/disable flags as
-    you choose, or these can be delared on your command line (i.e. -Dfoo).
-
-    By default, this include file will always default to having all of the flags
-    commented out, so including it will have no effect.
-*/
-
-///////////////////////////////////////////////////////////////////////////////
-
-/*  Skia has lots of debug-only code. Often this is just null checks or other
-    parameter checking, but sometimes it can be quite intrusive (e.g. check that
-    each 32bit pixel is in premultiplied form). This code can be very useful
-    during development, but will slow things down in a shipping product.
-
-    By default, these mutually exclusive flags are defined in SkPreConfig.h,
-    based on the presence or absence of NDEBUG, but that decision can be changed
-    here.
- */
-// #define SK_DEBUG
-// #define SK_RELEASE
-
-/*  Skia has certain debug-only code that is extremely intensive even for debug
-    builds.  This code is useful for diagnosing specific issues, but is not
-    generally applicable, therefore it must be explicitly enabled to avoid
-    the performance impact. By default these flags are undefined, but can be
-    enabled by uncommenting them below.
- */
-// #define SK_DEBUG_GLYPH_CACHE
-// #define SK_DEBUG_PATH
-
-/*  preconfig will have attempted to determine the endianness of the system,
-    but you can change these mutually exclusive flags here.
- */
-// #define SK_CPU_BENDIAN
-// #define SK_CPU_LENDIAN
-
-/*  Most compilers use the same bit endianness for bit flags in a byte as the
-    system byte endianness, and this is the default. If for some reason this
-    needs to be overridden, specify which of the mutually exclusive flags to
-    use. For example, some atom processors in certain configurations have big
-    endian byte order but little endian bit orders.
-*/
-// #define SK_UINT8_BITFIELD_BENDIAN
-// #define SK_UINT8_BITFIELD_LENDIAN
-
-
-/*  To write debug messages to a console, skia will call SkDebugf(...) following
-    printf conventions (e.g. const char* format, ...). If you want to redirect
-    this to something other than printf, define yours here
- */
-// #define SkDebugf(...)  MyFunction(__VA_ARGS__)
-
-/*
- *  To specify a different default font cache limit, define this. If this is
- *  undefined, skia will use a built-in value.
- */
-// #define SK_DEFAULT_FONT_CACHE_LIMIT   (1024 * 1024)
-
-/*
- *  To specify the default size of the image cache, undefine this and set it to
- *  the desired value (in bytes). SkGraphics.h as a runtime API to set this
- *  value as well. If this is undefined, a built-in value will be used.
- */
-// #define SK_DEFAULT_IMAGE_CACHE_LIMIT (1024 * 1024)
-
-/*  Define this to set the upper limit for text to support LCD. Values that
-    are very large increase the cost in the font cache and draw slower, without
-    improving readability. If this is undefined, Skia will use its default
-    value (e.g. 48)
- */
-// #define SK_MAX_SIZE_FOR_LCDTEXT     48
-
-/*  Change the ordering to work in X windows.
- */
-// #ifdef SK_SAMPLES_FOR_X
-//         #define SK_R32_SHIFT    16
-//         #define SK_G32_SHIFT    8
-//         #define SK_B32_SHIFT    0
-//         #define SK_A32_SHIFT    24
-// #endif
-
-
-/* Determines whether to build code that supports the GPU backend. Some classes
-   that are not GPU-specific, such as SkShader subclasses, have optional code
-   that is used allows them to interact with the GPU backend. If you'd like to
-   omit this code set SK_SUPPORT_GPU to 0. This also allows you to omit the gpu
-   directories from your include search path when you're not building the GPU
-   backend. Defaults to 1 (build the GPU code).
- */
-// #define SK_SUPPORT_GPU 1
-
-/* Skia makes use of histogram logging macros to trace the frequency of
- * events. By default, Skia provides no-op versions of these macros.
- * Skia consumers can provide their own definitions of these macros to
- * integrate with their histogram collection backend.
- */
-// #define SK_HISTOGRAM_BOOLEAN(name, value)
-// #define SK_HISTOGRAM_ENUMERATION(name, value, boundary_value)
-
-// ===== Begin Chrome-specific definitions =====
-
-#define SK_MSCALAR_IS_FLOAT
-#undef SK_MSCALAR_IS_DOUBLE
-
-#define GR_MAX_OFFSCREEN_AA_DIM 512
-
-// Log the file and line number for assertions.
-#define SkDebugf(...) SkDebugf_FileLine(__FILE__, __LINE__, __VA_ARGS__)
-SK_API void SkDebugf_FileLine(const char* file,
-                              int line,
-                              const char* format,
-                              ...);
-
-#if !defined(ANDROID)  // On Android, we use the skia default settings.
-#define SK_A32_SHIFT 24
-#define SK_R32_SHIFT 16
-#define SK_G32_SHIFT 8
-#define SK_B32_SHIFT 0
-#endif
-
-#if defined(SK_BUILD_FOR_WIN32)
-
-#define SK_BUILD_FOR_WIN
-
-// Skia uses this deprecated bzero function to fill zeros into a string.
-#define bzero(str, len) memset(str, 0, len)
-
-#elif defined(SK_BUILD_FOR_MAC)
-
-#define SK_CPU_LENDIAN
-#undef SK_CPU_BENDIAN
-
-#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID)
-
-// Prefer FreeType's emboldening algorithm to Skia's
-// TODO: skia used to just use hairline, but has improved since then, so
-// we should revisit this choice...
-#define SK_USE_FREETYPE_EMBOLDEN
-
-#if defined(SK_BUILD_FOR_UNIX) && defined(SK_CPU_BENDIAN)
-// Above we set the order for ARGB channels in registers. I suspect that, on
-// big endian machines, you can keep this the same and everything will work.
-// The in-memory order will be different, of course, but as long as everything
-// is reading memory as words rather than bytes, it will all work. However, if
-// you find that colours are messed up I thought that I would leave a helpful
-// locator for you. Also see the comments in
-// base/gfx/bitmap_platform_device_linux.h
-#error Read the comment at this location
-#endif
-
-#endif
-
-// These flags are no longer defined in Skia, but we have them (temporarily)
-// until we update our call-sites (typically these are for API changes).
-//
-// Remove these as we update our sites.
-//
-#ifndef SK_SUPPORT_LEGACY_GETTOPDEVICE
-#define SK_SUPPORT_LEGACY_GETTOPDEVICE
-#endif
-
-#ifndef SK_SUPPORT_EXOTIC_CLIPOPS
-#define SK_SUPPORT_EXOTIC_CLIPOPS
-#endif
-
-#ifndef SK_SUPPORT_LEGACY_GETDEVICE
-#define SK_SUPPORT_LEGACY_GETDEVICE
-#endif
-
-// Workaround for poor anisotropic mipmap quality,
-// pending Skia ripmap support.
-// (https://bugs.chromium.org/p/skia/issues/detail?id=4863)
-#ifndef SK_SUPPORT_LEGACY_ANISOTROPIC_MIPMAP_SCALE
-#define SK_SUPPORT_LEGACY_ANISOTROPIC_MIPMAP_SCALE
-#endif
-
-#ifndef SK_SUPPORT_LEGACY_REFENCODEDDATA_NOCTX
-#define SK_SUPPORT_LEGACY_REFENCODEDDATA_NOCTX
-#endif
-
-#ifndef SK_IGNORE_ETC1_SUPPORT
-#define SK_IGNORE_ETC1_SUPPORT
-#endif
-
-#ifndef SK_IGNORE_GPU_DITHER
-#define SK_IGNORE_GPU_DITHER
-#endif
-
-#ifndef SK_SUPPORT_LEGACY_EVAL_CUBIC
-#define SK_SUPPORT_LEGACY_EVAL_CUBIC
-#endif
-
-///////////////////////// Imported from BUILD.gn
-
-/* In some places Skia can use static initializers for global initialization,
- *  or fall back to lazy runtime initialization. Chrome always wants the latter.
- */
-#define SK_ALLOW_STATIC_GLOBAL_INITIALIZERS 0
-
-/* This flag forces Skia not to use typographic metrics with GDI.
- */
-#define SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
-
-#define SK_IGNORE_BLURRED_RRECT_OPT
-#define SK_USE_DISCARDABLE_SCALEDIMAGECACHE
-#define SK_WILL_NEVER_DRAW_PERSPECTIVE_TEXT
-
-#define SK_ATTR_DEPRECATED SK_NOTHING_ARG1
-#define SK_ENABLE_INST_COUNT 0
-#define GR_GL_CUSTOM_SETUP_HEADER "GrGLConfig_chrome.h"
-
-// Blink layout tests are baselined to Clang optimizing through the UB in
-// SkDivBits.
-#define SK_SUPPORT_LEGACY_DIVBITS_UB
-
-// mtklein's fiddling with Src / SrcOver.  Will rebaseline these only once when
-// done.
-#define SK_SUPPORT_LEGACY_X86_BLITS
-
-#define SK_DISABLE_TILE_IMAGE_FILTER_OPTIMIZATION
-
-// ===== End Chrome-specific definitions =====
-
-#endif  // SKIA_CONFIG_SKUSERCONFIG_H_
diff --git a/skia/ext/google_logging.cc b/skia/ext/google_logging.cc
index 10d7674..b5003ad 100644
--- a/skia/ext/google_logging.cc
+++ b/skia/ext/google_logging.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,10 @@
 
 #include "third_party/skia/include/core/SkTypes.h"
 
+#if defined(SK_BUILD_FOR_WIN) && !defined(__clang__)
+#include <stdlib.h>
+#endif
+
 void SkDebugf_FileLine(const char* file, int line, const char* format, ...) {
   va_list ap;
   va_start(ap, format);
@@ -19,3 +23,24 @@
   vfprintf(stderr, format, ap);
   va_end(ap);
 }
+
+#if defined(SK_BUILD_FOR_WIN) && !defined(__clang__)
+
+void SkDebugf_FileLineOnly(const char* file, int line) {
+  fprintf(stderr, "%s:%d\n", file, line);
+}
+
+void SkAbort_FileLine(const char* file, int line, const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+
+  fprintf(stderr, "%s:%d ", file, line);
+  vfprintf(stderr, format, ap);
+  va_end(ap);
+
+  sk_abort_no_print();
+  // Extra safety abort().
+  abort();
+}
+
+#endif  // defined(SK_BUILD_FOR_WIN) && !defined(__clang__)
diff --git a/skia/features.gni b/skia/features.gni
index 47771d1..7306075 100644
--- a/skia/features.gni
+++ b/skia/features.gni
@@ -1,4 +1,4 @@
-# Copyright 2019 The PDFium Authors. All rights reserved.
+# Copyright 2019 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/testing/BUILD.gn b/testing/BUILD.gn
index 787aa6d..331c2af 100644
--- a/testing/BUILD.gn
+++ b/testing/BUILD.gn
@@ -1,22 +1,31 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/sanitizers/sanitizers.gni")
+import("//build_overrides/build.gni")
 import("../pdfium.gni")
 
 source_set("test_support") {
   testonly = true
   sources = [
+    "command_line_helpers.cpp",
+    "command_line_helpers.h",
+    "font_renamer.cpp",
+    "font_renamer.h",
     "fx_string_testhelpers.cpp",
     "fx_string_testhelpers.h",
     "invalid_seekable_read_stream.cpp",
     "invalid_seekable_read_stream.h",
     "pseudo_retainable.h",
+    "scoped_set_tz.cpp",
+    "scoped_set_tz.h",
     "string_write_stream.cpp",
     "string_write_stream.h",
+    "test_fonts.cpp",
+    "test_fonts.h",
     "test_loader.cpp",
     "test_loader.h",
-    "test_support.cpp",
     "test_support.h",
     "utils/bitmap_saver.cpp",
     "utils/bitmap_saver.h",
@@ -24,10 +33,12 @@
     "utils/file_util.h",
     "utils/hash.cpp",
     "utils/hash.h",
-    "utils/path_service.cpp",
-    "utils/path_service.h",
   ]
   data = [ "resources/" ]
+  public_deps = [
+    ":path_service",
+    "//third_party/test_fonts",
+  ]
   deps = [
     "../:pdfium_public_headers",
     "../core/fdrm",
@@ -35,9 +46,18 @@
     "../core/fxge",
     "image_diff",
   ]
-  configs += [ "../:pdfium_core_config" ]
+  configs += [
+    "../:pdfium_strict_config",
+    "../: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",
@@ -51,25 +71,92 @@
   }
 }
 
+source_set("path_service") {
+  testonly = true
+  sources = [
+    "utils/path_service.cpp",
+    "utils/path_service.h",
+  ]
+  deps = [ "../core/fxcrt" ]
+  configs += [
+    "../:pdfium_strict_config",
+    "../:pdfium_noshorten_config",
+  ]
+  visibility = [ "../*" ]
+}
+
+source_set("test_environments") {
+  testonly = true
+  sources = [
+    "pdf_test_environment.cpp",
+    "pdf_test_environment.h",
+  ]
+  deps = [
+    ":test_support",
+    "../core/fxcrt",
+    "../core/fxge",
+    "//testing/gtest",
+  ]
+  configs += [
+    "../:pdfium_strict_config",
+    "../:pdfium_noshorten_config",
+  ]
+  if (pdf_enable_v8) {
+    sources += [
+      "v8_test_environment.cpp",
+      "v8_test_environment.h",
+    ]
+    deps += [
+      "../fxjs",
+      "//v8",
+      "//v8:v8_libplatform",
+    ]
+    configs += [ "//v8:external_startup_data" ]
+  }
+  if (pdf_enable_xfa) {
+    sources += [
+      "xfa_test_environment.cpp",
+      "xfa_test_environment.h",
+    ]
+    deps += [
+      "../fxjs:gc",
+      "../xfa/fgas/font",
+    ]
+  }
+}
+
 source_set("unit_test_support") {
   testonly = true
   sources = []
   deps = []
-
-  configs += [ "../:pdfium_core_config" ]
-  visibility = [ "../*" ]
-
-  if (pdf_enable_xfa) {
+  configs += [
+    "../:pdfium_strict_config",
+    "../:pdfium_noshorten_config",
+  ]
+  public_deps = [
+    ":test_environments",
+    ":test_support",
+  ]
+  if (pdf_enable_v8) {
     sources += [
-      "xfa_unit_test_support.cpp",
-      "xfa_unit_test_support.h",
+      "fxv8_unittest.cpp",
+      "fxv8_unittest.h",
     ]
     deps += [
-      "../:pdfium",
-      "../core/fxge",
-      "../xfa/fgas",
+      "../fxjs",
       "//testing/gtest",
     ]
+    configs += [ "//v8:external_startup_data" ]
+    if (pdf_enable_xfa) {
+      sources += [
+        "fxgc_unittest.cpp",
+        "fxgc_unittest.h",
+      ]
+      deps += [
+        "../fxjs:gc",
+        "//testing/gtest",
+      ]
+    }
   }
 }
 
@@ -78,6 +165,10 @@
   sources = [
     "embedder_test.cpp",
     "embedder_test.h",
+    "embedder_test_constants.cpp",
+    "embedder_test_constants.h",
+    "embedder_test_environment.cpp",
+    "embedder_test_environment.h",
     "embedder_test_mock_delegate.h",
     "embedder_test_timer_handling_delegate.h",
     "fake_file_access.cpp",
@@ -85,26 +176,37 @@
     "range_set.cpp",
     "range_set.h",
   ]
-
   deps = [
-    ":test_support",
     "../:pdfium_public_headers",
     "../core/fdrm",
-    "../core/fxcrt",
+    "../core/fxge",
     "../third_party:pdfium_base",
     "//testing/gmock",
     "//testing/gtest",
   ]
-  configs += [ "../:pdfium_core_config" ]
+  public_deps = [
+    ":test_environments",
+    ":test_support",
+    "../core/fxcrt",
+  ]
+  configs += [
+    "../:pdfium_strict_config",
+    "../:pdfium_noshorten_config",
+  ]
   visibility = [ "../*" ]
-
   if (pdf_enable_v8) {
     sources += [
+      "external_engine_embedder_test.cpp",
+      "external_engine_embedder_test.h",
       "js_embedder_test.cpp",
       "js_embedder_test.h",
     ]
-    deps += [ "../fxjs" ]
-
+    deps += [
+      "../fxjs",
+      "//v8",
+      "//v8:v8_libplatform",
+    ]
+    configs += [ "//v8:external_startup_data" ]
     if (pdf_enable_xfa) {
       sources += [
         "xfa_js_embedder_test.cpp",
@@ -119,3 +221,7 @@
     }
   }
 }
+
+# Dummy group to keep satisfy references from //build.
+group("test_scripts_shared") {
+}
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 b3059e9..47b807c 100644
--- a/testing/SUPPRESSIONS
+++ b/testing/SUPPRESSIONS
@@ -1,4 +1,4 @@
-# Copyright 2016 The PDFium Authors. All rights reserved.
+# Copyright 2016 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 #
@@ -10,6 +10,7 @@
 # Column 1: platform: *, win, mac, linux
 # Column 2: v8 support: *, nov8, v8
 # Column 3: xfa support: *, noxfa, xfa
+# Column 4: rendering support: *, agg, 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
@@ -21,337 +22,768 @@
 #
 # Corpus tests
 #
-050_extra_m.pdf mac,win * *
-12.pdf mac * *
-1_10_watermark.pdf * * *
-1_1_textbox.pdf * * *
-1_2_typewriter.pdf * * *
-1_3_callout.pdf * * *
-1_matrix.pdf mac * *
-1m_diff_lsjdf.pdf mac * *
-1m_same_xxxx.pdf mac * *
-2_11_stamp3.pdf mac * *
-2_6_textbox.pdf * * *
-2_color_calrgb.pdf mac * *
-2_color_indexed.pdf mac * *
-3_4_textbox.pdf * * *
-3_interpolate_image.pdf mac * *
-3bigpreview.pdf mac,win * *
-4_35.pdf mac * *
-4_39.pdf mac * *
-5.1.pdf mac * *
-5.2.pdf * * *
-5.5_simple_font.pdf mac,win * *
-8.2_name_dest_f_dest.pdf mac * *
-8.2_outline.pdf mac * *
-8.3_presentation.pdf mac * *
-FRC_10_8.2.2__T8.3_original_file.pdf * * *
-FRC_11_8.2.2__T8.3_first_last_exchange.pdf * * *
-FRC_12_8.2.2__T8.3_first_outline_obj_ID.pdf * * *
-FRC_13_8.2.2__T8.3_Count_edit300.pdf * * *
-FRC_14_8.2.2__T8.3_Count_edit0.pdf * * *
-FRC_15_8.2.2__T8.3_Count_edit1.pdf * * *
-FRC_16_8.2.2__T8.3_Count_edit_1.pdf * * *
-FRC_1_8.2.2__T8.3_First_empty.pdf * * *
-FRC_2_8.2.2__T8.3_Last_empty.pdf * * *
-FRC_3.5_P__3616_Password_1.pdf * * *
-FRC_3_8.2.2_Type_empty.pdf * * *
-FRC_4_8.2.2__T8.3_Count_empty.pdf * * *
-FRC_5_8.2.2__T8.3_First_remove.pdf * * *
-FRC_6_8.2.2__T8.3_Last_remove.pdf * * *
-FRC_7_8.2.2_Type_remove.pdf * * *
-FRC_8.4.1_Annotations_M.pdf * * *
-FRC_8.4.1_Annotations_NM.pdf * * *
-FRC_8.5_Page_C_SubmitForm.pdf * * *
-FRC_8.5_Page_PI_ResetForm_Phantom.pdf * * *
-FRC_8.5_Widget_F.pdf * nov8 *
-FRC_8_8.2.2__T8.3_Count_remove.pdf * * *
-FRC_9_8.2.2__T8.3_remove_first_item.pdf * * *
-action.pdf * * *
-action_execute_a_menu_item.pdf mac * *
-action_hide_show_form.pdf mac * *
-action_on_focus.pdf mac * *
-action_open_a_file.pdf mac * *
-action_pdf_save_close.pdf mac * *
-action_reset.pdf mac * *
-action_run_javascript.pdf mac * *
-action_submit_a_form.pdf mac * *
-all_trigger_alert.pdf * * *
-all_trigger_browsefordoc.pdf * * *
-all_trigger_mailmsg.pdf * * *
-all_trigger_newdoc.pdf * * *
-all_trigger_print.pdf * * *
-all_trigger_run_js_lunchurl.pdf mac * *
-all_trigger_run_js_maildoc.pdf mac * *
-annotation_highlight_author_content.pdf mac * *
-annotation_highlight_long_content.pdf mac * *
-annotation_highlight_no_author.pdf mac * *
-app_launchurl.pdf mac * *
-appstoredescription3.1_en_updated.pdf mac * *
-bookmark.pdf * * *
-bookmarkgetcolor.pdf mac * *
-bug_0_length_line.pdf mac * *
-bug_0_width_line.pdf mac * *
-bug_440132.pdf mac * *
-bug_white_space.pdf mac * *
-calcorderindex_test.pdf * * *
-calculate_average.pdf mac * *
-calculate_order.pdf * * *
-calculate_sum_a_b_c.pdf mac * *
-calculate_validate.pdf mac * *
-calculate_validate.pdf * nov8 *
-ch_1.pdf * * *
-check_box.pdf * * *
-color.pdf mac * *
-colorspace.pdf mac * *
-colorspace_test1.pdf mac * *
-combo_box.pdf * * *
-combo_box_format.pdf mac * *
-date.pdf mac * *
-edit_transform.pdf mac * *
-en_contact.pdf mac * *
-en_diy.pdf mac * *
-en_foxit.pdf mac * *
-en_fqa2.pdf mac * *
-en_introduce.pdf mac * *
-en_tem.pdf mac * *
-en_uicase_.pdf mac * *
-event.change.pdf mac * *
-event.changeex.pdf mac * *
-event.keydown.pdf mac * *
-event.keydown_1_.pdf mac * *
-event.type_name.pdf mac * *
-event.value.pdf mac * *
-event_change.pdf mac * *
-example_001.pdf mac * *
-example_002.pdf mac,win * *
-example_003.pdf mac,win * *
-example_004.pdf mac * *
-example_005.pdf mac * *
-example_006.pdf mac,win * *
-example_007.pdf mac * *
-example_008.pdf mac * *
-example_009.pdf mac * *
-example_010.pdf mac * *
-example_011.pdf mac * *
-example_012.pdf mac * *
-example_013.pdf mac * *
-example_014.pdf mac * *
-example_015.pdf mac * *
-example_016.pdf mac * *
-example_017.pdf mac * *
-example_018.pdf mac * *
-example_019.pdf mac * *
-example_020.pdf mac * *
-example_021.pdf mac * *
-example_022.pdf mac * *
-example_023.pdf mac * *
-example_024.pdf mac * *
-example_025.pdf mac * *
-example_026.pdf mac * *
-example_027.pdf mac * *
-example_028.pdf mac * *
-example_029.pdf mac * *
-example_030.pdf mac * *
-example_031.pdf mac * *
-example_032.pdf mac * *
-example_033.pdf mac,win * *
-example_034.pdf mac * *
-example_035.pdf mac,win * *
-example_036.pdf mac,win * *
-example_037.pdf mac * *
-example_038.pdf mac,win * *
-example_039.pdf mac * *
-example_040.pdf mac * *
-example_041.pdf mac,win * *
-example_042.pdf mac * *
-example_043.pdf mac * *
-example_044.pdf mac * *
-example_045.pdf mac,win * *
-example_046.pdf mac,win * *
-example_047.pdf mac * *
-example_048.pdf mac * *
-example_049.pdf mac * *
-example_050.pdf mac * *
-example_051.pdf mac * *
-example_052.pdf mac * *
-example_053.pdf mac * *
-example_054.pdf mac * *
-example_055.pdf mac,win * *
-example_056.pdf mac * *
-example_057.pdf mac * *
-example_058.pdf mac * *
-example_059.pdf mac,win * *
-example_060.pdf mac * *
-example_061.pdf mac,win * *
-example_062.pdf mac * *
-example_063.pdf mac * *
-example_064.pdf mac * *
-example_065.pdf mac * *
-fillform.pdf * * *
-form_action_trigger.pdf * * *
-form_button_sign_url.pdf * * *
-form_combo_sign_url.pdf * * *
-form_combobox0.pdf * * *
-form_combobox_actioin_goto.pdf * * *
-form_combobox_date.pdf mac * *
-form_combobox_date.pdf * nov8 *
-form_combobox_date1.pdf * * *
-form_combobox_date2.pdf mac * *
-form_combobox_date2.pdf * nov8 *
-form_combobox_importform.pdf * * *
-form_combobox_num.pdf mac * *
-form_combobox_num.pdf * nov8 *
-form_combobox_per.pdf mac * *
-form_combobox_per.pdf * nov8 *
-form_combobox_plus.pdf mac * *
-form_combobox_plus.pdf * nov8 *
-form_combobox_product.pdf mac * *
-form_combobox_product.pdf * nov8 *
-form_combobox_resetform.pdf * * *
-form_combobox_time.pdf mac * *
-form_combobox_time.pdf * nov8 *
-form_list.pdf * * *
-form_list1.pdf * * *
-form_same.pdf mac * *
-form_text_sign_url.pdf * * *
-format_combo_box.pdf mac * *
-format_combo_box.pdf * nov8 *
-format_custom_format.pdf linux,mac * *
-format_custom_format.pdf * nov8 *
-format_custom_keystroke.pdf * * *
-format_date.pdf * nov8 *
-format_number.pdf mac * *
-format_percentage.pdf mac * *
-format_special.pdf * nov8 *
-format_text_color.pdf mac * *
-formfeild.pdf * * *
-getarray.pdf mac * *
-javascriptaction.pdf * * *
-jetman_std.pdf mac * *
-jetman_std_fixed.pdf mac * *
-js_calculate.pdf * * *
-list_box.pdf * * *
-negative.pdf mac * *
-new_certify1.pdf mac * *
-new_signature1.pdf mac * *
-new_signature2.pdf mac * *
-new_stamp4.pdf mac * *
-new_stamp5.pdf mac * *
-new_textmarkup1.pdf mac * *
-new_textmarkup1_hidden.pdf mac * *
-new_textmarkup2.pdf mac * *
-new_textmarkup2_hidden.pdf mac * *
-new_textmarkup4.pdf mac * *
-new_textmarkup4_hidden.pdf mac * *
-new_textmarkup5.pdf mac * *
-new_textmarkup5_hidden.pdf mac * *
-new_textmarkup6.pdf mac * *
-new_textmarkup7.pdf mac * *
-new_textmarkup7_hidden.pdf mac * *
-new_textmarkup8.pdf mac * *
-new_textmarkup8_hidden.pdf mac * *
-number.pdf * * *
-octest.pdf mac * *
-open_a_weblink.pdf mac * *
-path_10_jd.pdf mac * *
-path_5_pattern.pdf mac * *
-path_6_graphics4.5.5.pdf mac * *
-path_7.pdf mac * *
-path_9.pdf mac * *
-percentage.pdf mac * *
-push_button.pdf * * *
-quick_start_guide.pdf mac * *
-radio_button.pdf * * *
-run_custom_validate_script.pdf * * *
-show_1.pdf mac * *
-signature.pdf * * *
-signature_4.pdf * * *
-simplified_field_notation.pdf mac * *
-special.pdf mac * *
-submit_form.pdf mac * *
-test_app_beep.pdf * * *
-test_control.pdf * * *
-test_m.pdf mac,win * *
-text_field.pdf * * *
-text_field_font_input_decimal_point.pdf mac * *
-text_field_multiline_line_spacing.pdf mac * *
-thread_action.pdf mac * *
-time.pdf mac * *
-transformation.pdf mac * *
-transparent.pdf mac * *
-whats_new_in_v3.0.pdf mac * *
-widget_javascript.pdf mac * *
-zh_file1.pdf mac * *
-zh_function_list.pdf mac * *
-zh_shared_document.pdf mac * *
+12.pdf mac * * agg
+1_1_textbox.pdf * * * *
+1_2_typewriter.pdf * * * *
+1_3_callout.pdf * * * *
+1_matrix.pdf mac * * agg
+1m_diff_lsjdf.pdf mac * * agg
+1m_same_xxxx.pdf mac * * agg
+2_11_stamp3.pdf mac * * agg
+2_6_textbox.pdf * * * *
+2_color_calrgb.pdf mac * * agg
+2_color_indexed.pdf mac * * agg
+3_4_textbox.pdf * * * *
+3_interpolate_image.pdf mac * * agg
+3bigpreview.pdf mac * * agg
+4_35.pdf mac * * agg
+4_39.pdf mac * * agg
+5.1.pdf mac * * agg
+5.2.pdf * * * *
+5.5_simple_font.pdf mac * * agg
+8.2_name_dest_f_dest.pdf mac * * agg
+8.2_outline.pdf mac * * agg
+8.3_presentation.pdf mac * * agg
+FRC_10_8.2.2__T8.3_original_file.pdf * * * *
+FRC_11_8.2.2__T8.3_first_last_exchange.pdf * * * *
+FRC_12_8.2.2__T8.3_first_outline_obj_ID.pdf * * * *
+FRC_13_8.2.2__T8.3_Count_edit300.pdf * * * *
+FRC_14_8.2.2__T8.3_Count_edit0.pdf * * * *
+FRC_15_8.2.2__T8.3_Count_edit1.pdf * * * *
+FRC_16_8.2.2__T8.3_Count_edit_1.pdf * * * *
+FRC_1_8.2.2__T8.3_First_empty.pdf * * * *
+FRC_2_8.2.2__T8.3_Last_empty.pdf * * * *
+FRC_3.5_P__3616_Password_1.pdf * * * *
+FRC_3_8.2.2_Type_empty.pdf * * * *
+FRC_4_8.2.2__T8.3_Count_empty.pdf * * * *
+FRC_5_8.2.2__T8.3_First_remove.pdf * * * *
+FRC_6_8.2.2__T8.3_Last_remove.pdf * * * *
+FRC_7_8.2.2_Type_remove.pdf * * * *
+FRC_8.4.1_Annotations_M.pdf * * * *
+FRC_8.4.1_Annotations_NM.pdf * * * *
+FRC_8.5_Page_C_SubmitForm.pdf * * * *
+FRC_8.5_Page_PI_ResetForm_Phantom.pdf * * * *
+FRC_8.5_Widget_F.pdf * nov8 * *
+FRC_8_8.2.2__T8.3_Count_remove.pdf * * * *
+FRC_9_8.2.2__T8.3_remove_first_item.pdf * * * *
+action.pdf * * * *
+action_execute_a_menu_item.pdf mac * * agg
+action_hide_show_form.pdf mac * * agg
+action_on_focus.pdf mac * * agg
+action_open_a_file.pdf mac * * agg
+action_pdf_save_close.pdf mac * * agg
+action_reset.pdf mac * * agg
+action_run_javascript.pdf mac * * agg
+action_submit_a_form.pdf mac * * agg
+all_trigger_alert.pdf * * * *
+all_trigger_mailmsg.pdf * * * *
+all_trigger_print.pdf * * * *
+all_trigger_run_js_lunchurl.pdf mac * * agg
+all_trigger_run_js_maildoc.pdf mac * * agg
+annotation_highlight_author_content.pdf mac * * agg
+annotation_highlight_long_content.pdf mac * * agg
+annotation_highlight_no_author.pdf mac * * agg
+app_launchurl.pdf mac * * agg
+appstoredescription3.1_en_updated.pdf mac * * agg
+bookmark.pdf * * * *
+bookmarkgetcolor.pdf mac * * agg
+bug_0_length_line.pdf mac * * agg
+
+# TODO(pdfium:1812): Remove after associated bug is fixed
+bug_0_length_line.pdf * * * skia
+
+bug_0_width_line.pdf mac * * agg
+bug_440132.pdf mac * * agg
+bug_white_space.pdf mac * * agg
+calcorderindex_test.pdf * * * *
+calculate_average.pdf mac * * agg
+calculate_order.pdf * * * *
+calculate_sum_a_b_c.pdf mac * * agg
+calculate_validate.pdf mac * * agg
+calculate_validate.pdf * nov8 * *
+ch_1.pdf * * * *
+check_box.pdf * * * *
+color.pdf mac * * agg
+colorspace.pdf mac * * agg
+colorspace_test1.pdf mac * * agg
+combo_box.pdf * * * *
+combo_box_format.pdf mac * * agg
+date.pdf mac * * agg
+edit_transform.pdf mac * * agg
+en_contact.pdf mac * * agg
+en_diy.pdf mac * * agg
+en_foxit.pdf mac * * agg
+en_fqa2.pdf mac * * agg
+en_introduce.pdf mac * * agg
+en_tem.pdf mac * * agg
+en_uicase_.pdf mac * * agg
+event.change.pdf mac * * agg
+event.changeex.pdf mac * * agg
+event.keydown.pdf mac * * agg
+event.keydown_1_.pdf mac * * agg
+event.type_name.pdf mac * * agg
+event.value.pdf mac * * agg
+event_change.pdf mac * * agg
+example_001.pdf mac * * agg
+example_002.pdf mac * * agg
+example_003.pdf mac * * agg
+example_004.pdf mac * * agg
+example_005.pdf mac * * agg
+example_006.pdf mac * * agg
+example_007.pdf mac * * agg
+example_008.pdf mac * * agg
+example_009.pdf mac * * agg
+example_010.pdf mac * * agg
+example_011.pdf mac * * agg
+example_012.pdf mac * * agg
+example_013.pdf mac * * agg
+example_014.pdf mac * * agg
+example_015.pdf mac * * agg
+example_016.pdf mac * * agg
+example_017.pdf mac * * agg
+example_018.pdf mac * * agg
+example_019.pdf mac * * agg
+example_020.pdf mac * * agg
+example_021.pdf mac * * agg
+example_022.pdf mac * * agg
+example_023.pdf mac * * agg
+example_024.pdf mac * * agg
+example_025.pdf mac * * agg
+example_026.pdf mac * * agg
+example_027.pdf mac * * agg
+example_028.pdf mac * * agg
+example_029.pdf mac * * agg
+example_030.pdf mac * * agg
+example_031.pdf mac * * agg
+example_032.pdf mac * * agg
+example_033.pdf mac * * agg
+example_034.pdf mac * * agg
+example_035.pdf mac * * agg
+example_036.pdf mac * * agg
+example_037.pdf mac * * agg
+example_038.pdf mac * * agg
+example_039.pdf mac * * agg
+example_040.pdf mac * * agg
+example_041.pdf mac * * agg
+example_042.pdf mac * * agg
+example_043.pdf mac * * agg
+example_044.pdf mac * * agg
+example_045.pdf mac * * agg
+example_046.pdf mac * * agg
+example_047.pdf mac * * agg
+example_048.pdf mac * * agg
+example_049.pdf mac * * agg
+example_050.pdf mac * * agg
+example_051.pdf mac * * agg
+example_052.pdf mac * * agg
+example_053.pdf mac * * agg
+example_054.pdf mac * * agg
+example_055.pdf mac * * agg
+example_056.pdf mac * * agg
+example_057.pdf mac * * agg
+example_058.pdf mac * * agg
+example_059.pdf mac * * agg
+example_060.pdf mac * * agg
+example_061.pdf mac * * agg
+example_062.pdf mac * * agg
+example_063.pdf mac * * agg
+example_064.pdf mac * * agg
+example_065.pdf mac * * agg
+fillform.pdf * * * *
+form_action_trigger.pdf * * * *
+form_button_sign_url.pdf * * * *
+form_combo_sign_url.pdf * * * *
+form_combobox0.pdf * * * *
+form_combobox_actioin_goto.pdf * * * *
+form_combobox_date.pdf mac * * agg
+form_combobox_date.pdf * nov8 * *
+form_combobox_date1.pdf * * * *
+form_combobox_date2.pdf mac * * agg
+form_combobox_date2.pdf * nov8 * *
+form_combobox_importform.pdf * * * *
+form_combobox_num.pdf mac * * agg
+form_combobox_num.pdf * nov8 * *
+form_combobox_per.pdf mac * * agg
+form_combobox_per.pdf * nov8 * *
+form_combobox_plus.pdf mac * * agg
+form_combobox_plus.pdf * nov8 * *
+form_combobox_product.pdf mac * * agg
+form_combobox_product.pdf * nov8 * *
+form_combobox_resetform.pdf * * * *
+form_combobox_time.pdf mac * * agg
+form_combobox_time.pdf * nov8 * *
+form_list.pdf * * * *
+form_list1.pdf * * * *
+form_same.pdf mac * * agg
+form_text_sign_url.pdf * * * *
+format_combo_box.pdf mac * * agg
+format_combo_box.pdf * nov8 * *
+format_custom_format.pdf * nov8 * *
+format_custom_keystroke.pdf * * * *
+format_date.pdf * nov8 * *
+format_number.pdf mac * * agg
+format_percentage.pdf mac * * agg
+format_special.pdf * nov8 * *
+format_text_color.pdf mac * * agg
+formfield.pdf * * * *
+getarray.pdf mac * * agg
+
+# TODO(pdfium:489): Remove after associated bug is fixed.
+gradient_many_stops.pdf * * * agg
+
+javascriptaction.pdf * * * *
+jetman_std.pdf mac * * agg
+jetman_std_fixed.pdf mac * * agg
+js_calculate.pdf * * * *
+list_box.pdf * * * *
+negative.pdf mac * * agg
+new_certify1.pdf mac * * agg
+new_signature1.pdf mac * * agg
+new_signature2.pdf mac * * agg
+new_stamp4.pdf mac * * agg
+new_stamp5.pdf mac * * agg
+new_textmarkup1.pdf mac * * agg
+new_textmarkup1_hidden.pdf mac * * agg
+new_textmarkup2.pdf mac * * agg
+new_textmarkup2_hidden.pdf mac * * agg
+new_textmarkup4.pdf mac * * agg
+new_textmarkup4_hidden.pdf mac * * agg
+new_textmarkup5.pdf mac * * agg
+new_textmarkup5_hidden.pdf mac * * agg
+new_textmarkup6.pdf mac * * agg
+new_textmarkup7.pdf mac * * agg
+new_textmarkup7_hidden.pdf mac * * agg
+new_textmarkup8.pdf mac * * agg
+new_textmarkup8_hidden.pdf mac * * agg
+number.pdf * * * *
+octest.pdf mac * * agg
+open_a_weblink.pdf mac * * agg
+path_10_jd.pdf mac * * agg
+path_5_pattern.pdf mac * * agg
+path_6_graphics4.5.5.pdf mac * * agg
+path_7.pdf mac * * agg
+path_9.pdf mac * * agg
+percentage.pdf mac * * agg
+push_button.pdf * * * *
+quick_start_guide.pdf mac * * agg
+radio_button.pdf * * * *
+run_custom_validate_script.pdf * * * *
+show_1.pdf mac * * agg
+signature.pdf * * * *
+signature_4.pdf * * * *
+simplified_field_notation.pdf mac * * agg
+special.pdf mac * * agg
+submit_form.pdf mac * * agg
+test_app_beep.pdf * * * *
+test_control.pdf * * * *
+text_field.pdf * * * *
+text_field_font_input_decimal_point.pdf mac * * agg
+text_field_multiline_line_spacing.pdf mac * * agg
+thread_action.pdf mac * * agg
+time.pdf mac * * agg
+transformation.pdf mac * * agg
+transparent.pdf mac * * agg
+whats_new_in_v3.0.pdf mac * * agg
+widget_javascript.pdf mac * * agg
+
+# TODO(pdfium:1990): Remove after associated bug is fixed
+xfermodes2.pdf * * * agg
+
+zh_file1.pdf mac * * agg
+zh_function_list.pdf mac * * agg
+zh_shared_document.pdf mac * * agg
 
 # TODO(hnakashima): These might never have been run. Go over them and fix.
 
-FRC_8.5_E&X.pdf * * *
-FRC_8.5_O&PO_GoToE.pdf * * *
-FRC_8.5_OpenAction&O_URI.pdf * * *
-FRC_8.5_PC&C_GoToE_T_T.pdf * * *
-FRC_8.5_PC_GoToE_T_R&P&A.pdf * * *
-FRC_8.5_PO_GoToE_T_R&N.pdf * * *
+FRC_8.5_E&X.pdf * * * *
+FRC_8.5_O&PO_GoToE.pdf * * * *
+FRC_8.5_OpenAction&O_URI.pdf * * * *
+FRC_8.5_PC&C_GoToE_T_T.pdf * * * *
+FRC_8.5_PC_GoToE_T_R&P&A.pdf * * * *
+FRC_8.5_PO_GoToE_T_R&N.pdf * * * *
 
 # xfa_specific
 
-Choose.pdf * * *
-data_binding.pdf * * *
+Choose.pdf * * * *
+data_binding.pdf * * * *
 # TODO(npm): Add proper evt for MouseEvents.
-MouseEvents_enter.pdf * * *
-MouseEvents_exit.pdf * * *
-Oneof3.pdf * * *
-Sum.pdf * * *
-TimeField.pdf win,linux * *
-Test_CheckBox.pdf * * *
-Test_DateField_locale_zh_HK.pdf mac,win * *
+MouseEvents_enter.pdf * * * *
+MouseEvents_exit.pdf * * * *
+Oneof3.pdf * * * *
+Sum.pdf * * * *
+Test_CheckBox.pdf * * * *
+
+# 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
 #
-bug_679642.in * * noxfa
-bug_679643.in * * noxfa
-bug_735912.in * * noxfa
+bug_679642.in * * noxfa *
+bug_679643.in * * noxfa *
+bug_735912.in * * noxfa *
+
+# JS tests in nov8 mode expect empty results. This one will
+# not be empty as the callback is not js-based.
+named_action.in * nov8 * *
 
 # xfa_specific
 
 # TODO(pdfium:1106): Remove after associated bug is fixed
-resolve_nodes_1.pdf * * *
+resolve_nodes_1.pdf * * * *
 
 #
 # Pixel tests
 #
-bug_492.in * nov8 *
 
-# TODO(pdfium:304): Remove after associated bug is fixed
-bug_304.pdf * * *
+# TODO(pdfium: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 * * * *
 
 # TODO(pdfium:1331): Remove after associated bug is fixed
-bug_1331.in * * *
+bug_1331.in * * * *
 
-# TODO(pdfium:1461): Remove after associated bug is fixed
-bug_1402.in win * *
+# TODO(chromium:1356149): Remove after associated bug is fixed
+bug_1356149.in mac * * agg
+
+# 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 * * *
+bug_1457.in * * * *
+
+# TODO(pdfium:1519): Remove after associated bug is fixed
+bug_1519.in * * * *
+
+# TODO(pdfium:1571): Remove after associated bug is fixed
+bug_1571.in * * * *
+
+# TODO(pdfium:1723): Remove after associated bug is fixed
+bug_1723.in * * * *
+
+# TODO(pdfium:1972): Remove after associated bug is fixed
+bug_1972_1.in * * * agg
+bug_1972_2.in * * * agg
+bug_1972_3.in * * * agg
+
+# TODO(pdfium:1973): Remove after associated bug is fixed
+bug_1973.in * * * *
+
+# TODO(pdfium:2001): Remove after associated bug is fixed
+bug_2001.pdf * * * *
+
+# TODO(chromium:237527): Remove after associated bug is fixed
+bug_237527_1.in * * * *
 
 # TODO(chromium:451366): Remove after associated bug is fixed
-bug_451366.in * * *
+bug_451366.in * * * agg
 
-# TODO(chromium:1012369): Remove after associated bug is fixed
-bug_1012369.in * * *
+# TODO(pdfium:492): Remove after associated bug is fixed
+bug_492.in * nov8 * *
+
+# TODO(chromium:725555, skia:9265): Remove after associated bug is fixed
+bug_725555.in * * * skia
+
+# TODO(chromium:983289): Remove after associated bug is fixed
+bug_983289.in * * * agg
+
+# TODO(pdfium:1747): Remove after associated bug is fixed
+jpxdecode.in * * * *
+jpxdecode_without_bitspercomponent.in * * * *
+jpxdecode_without_colorspace.in * * * *
+
+# TODO(chromium:1028991): Remove after associated bug is fixed
+reset_button.in * * * *
 
 # xfa_specific
 
+# TODO(pdfium:1095): Remove after associated bug is fixed
+bug_997412.in win * * *
 # TODO(pdfium:1107): Remove after associated bug is fixed
-standard_symbols.pdf * * *
-# TODO(pdfium:1168): Remove after associated bug is fixed
-xfa_bmp_image.in * * *
+standard_symbols.pdf * * * *
 # TODO(pdfium:1095): Remove after associated bug is fixed
-xfa_example.in win * *
-# TODO(pdfium:1167): Remove after associated bug is fixed
-xfa_gif_image.in * * *
+xfa_example.in win * * *
 # TODO(pdfium:1095): Remove after associated bug is fixed
-xfa_textfield.in win * *
+xfa_textfield.in win * * *
+
+# 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
new file mode 100644
index 0000000..b9734d9
--- /dev/null
+++ b/testing/SUPPRESSIONS_EXACT_MATCHING
@@ -0,0 +1,28 @@
+# Copyright 2023 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# List of tests to use fuzzy instead of exact matching, one per line.
+# There are four space-separated columns per line
+# Each column (except column 0) can contain a comma-separated list of values.
+#
+# Column 0: test file name
+# Column 1: platform: *, win, mac, linux
+# Column 2: v8 support: *, nov8, v8
+# Column 3: xfa support: *, noxfa, xfa
+# Column 4: rendering support: *, agg, 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
+# the comma-separated values must match in order for the colum to "match".
+# The filenames and keywords are case-sensitive.
+#
+# Try to keep the file alphabetized within each category of test.
+
+#
+# Corpus tests
+#
+
+# Device-specific ColorBurn differences (see pdfium:1959).
+xfermodes2.pdf * * * skia
+xfermodes3.pdf * * * skia
diff --git a/testing/SUPPRESSIONS_IMAGE_DIFF b/testing/SUPPRESSIONS_IMAGE_DIFF
index cdb2d66..e7ecd21 100644
--- a/testing/SUPPRESSIONS_IMAGE_DIFF
+++ b/testing/SUPPRESSIONS_IMAGE_DIFF
@@ -1,4 +1,4 @@
-# Copyright 2017 The PDFium Authors. All rights reserved.
+# Copyright 2017 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 #
@@ -10,6 +10,7 @@
 # Column 1: platform: *, win, mac, linux
 # Column 2: v8 support: *, nov8, v8
 # Column 3: xfa support: *, noxfa, xfa
+# Column 4: rendering support: *, agg, 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
@@ -21,12 +22,12 @@
 #
 # Corpus tests
 #
-FRC_3.5_CF_Strf_stmf_DefaultCryptFilter.pdf * * *
-FRC_3.5_EncryptMetadata_T.pdf * * *
-FRC_3.5_Encrypt_is_damage.pdf * * *
-FRC_3.5_Filter_PubSec_SubFilter_s5.pdf * * *
-FRC_3.5_Filter_PubSec_Sub_SubFilter_s4.pdf * * *
-MouseEvents.pdf * * *
-Oneof.pdf * * *
-bug_651304.pdf * * *
-outline.pdf * * *
+FRC_3.5_CF_Strf_stmf_DefaultCryptFilter.pdf * * * *
+FRC_3.5_EncryptMetadata_T.pdf * * * *
+FRC_3.5_Encrypt_is_damage.pdf * * * *
+FRC_3.5_Filter_PubSec_SubFilter_s5.pdf * * * *
+FRC_3.5_Filter_PubSec_Sub_SubFilter_s4.pdf * * * *
+MouseEvents.pdf * * * *
+Oneof.pdf * * * *
+bug_651304.pdf * * * *
+outline.pdf * * * *
diff --git a/testing/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/command_line_helpers.cpp b/testing/command_line_helpers.cpp
new file mode 100644
index 0000000..758f278
--- /dev/null
+++ b/testing/command_line_helpers.cpp
@@ -0,0 +1,23 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/command_line_helpers.h"
+
+bool ParseSwitchKeyValue(const std::string& arg,
+                         const std::string& key,
+                         std::string* value) {
+  if (arg.size() <= key.size() || arg.compare(0, key.size(), key) != 0)
+    return false;
+
+  *value = arg.substr(key.size());
+  return true;
+}
+
+FPDF_RENDERER_TYPE GetDefaultRendererType() {
+#if defined(_SKIA_SUPPORT_)
+  return FPDF_RENDERERTYPE_SKIA;
+#else
+  return FPDF_RENDERERTYPE_AGG;
+#endif
+}
diff --git a/testing/command_line_helpers.h b/testing/command_line_helpers.h
new file mode 100644
index 0000000..cc134cb
--- /dev/null
+++ b/testing/command_line_helpers.h
@@ -0,0 +1,23 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_COMMAND_LINE_HELPERS_H_
+#define TESTING_COMMAND_LINE_HELPERS_H_
+
+#include <string>
+
+#include "public/fpdfview.h"
+
+// Extract the value from a keyed command line argument.
+// `arg` is expected to be "--key=value", and `key` is "--key=".
+bool ParseSwitchKeyValue(const std::string& arg,
+                         const std::string& key,
+                         std::string* value);
+
+// Identifies the compile-time default 2D graphics library to use for rendering
+// to FPDF_BITMAPs. Used as part of support to override the renderer at runtime
+// based upon command line options.
+FPDF_RENDERER_TYPE GetDefaultRendererType();
+
+#endif  // TESTING_COMMAND_LINE_HELPERS_H_
diff --git a/testing/embedder_test.cpp b/testing/embedder_test.cpp
index 9bf4a18..4d31c91 100644
--- a/testing/embedder_test.cpp
+++ b/testing/embedder_test.cpp
@@ -1,13 +1,10 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "testing/embedder_test.h"
 
-#include <limits.h>
-
-#include <list>
-#include <map>
+#include <algorithm>
 #include <memory>
 #include <string>
 #include <utility>
@@ -19,20 +16,19 @@
 #include "public/fpdf_edit.h"
 #include "public/fpdf_text.h"
 #include "public/fpdfview.h"
+#include "testing/embedder_test_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/test_loader.h"
 #include "testing/utils/bitmap_saver.h"
 #include "testing/utils/file_util.h"
 #include "testing/utils/hash.h"
 #include "testing/utils/path_service.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-#ifdef PDF_ENABLE_V8
-#include "v8/include/v8-platform.h"
-#include "v8/include/v8.h"
-#endif  // PDF_ENABLE_V8
+#include "third_party/base/check.h"
+#include "third_party/base/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 {
 
@@ -40,7 +36,7 @@
   return EmbedderTest::BytesPerPixelForFormat(FPDFBitmap_GetFormat(bitmap));
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 int CALLBACK GetRecordProc(HDC hdc,
                            HANDLETABLE* handle_table,
                            const ENHMETARECORD* record,
@@ -50,12 +46,224 @@
   records.push_back(record);
   return 1;
 }
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
+
+// These "jump" into the delegate to do actual testing.
+void UnsupportedHandlerTrampoline(UNSUPPORT_INFO* info, int type) {
+  auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
+  delegate->UnsupportedHandler(type);
+}
+
+int AlertTrampoline(IPDF_JSPLATFORM* platform,
+                    FPDF_WIDESTRING message,
+                    FPDF_WIDESTRING title,
+                    int type,
+                    int icon) {
+  auto* delegate = static_cast<EmbedderTest*>(platform)->GetDelegate();
+  return delegate->Alert(message, title, type, icon);
+}
+
+int SetTimerTrampoline(FPDF_FORMFILLINFO* info, int msecs, TimerCallback fn) {
+  auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
+  return delegate->SetTimer(msecs, fn);
+}
+
+void KillTimerTrampoline(FPDF_FORMFILLINFO* info, int id) {
+  auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
+  return delegate->KillTimer(id);
+}
+
+FPDF_PAGE GetPageTrampoline(FPDF_FORMFILLINFO* info,
+                            FPDF_DOCUMENT document,
+                            int page_index) {
+  auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
+  return delegate->GetPage(info, document, page_index);
+}
+
+void DoURIActionTrampoline(FPDF_FORMFILLINFO* info, FPDF_BYTESTRING uri) {
+  auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
+  return delegate->DoURIAction(uri);
+}
+
+void DoGoToActionTrampoline(FPDF_FORMFILLINFO* info,
+                            int page_index,
+                            int zoom_mode,
+                            float* pos_array,
+                            int array_size) {
+  auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
+  return delegate->DoGoToAction(info, page_index, zoom_mode, pos_array,
+                                array_size);
+}
+
+void OnFocusChangeTrampoline(FPDF_FORMFILLINFO* info,
+                             FPDF_ANNOTATION annot,
+                             int page_index) {
+  auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
+  return delegate->OnFocusChange(info, annot, page_index);
+}
+
+void DoURIActionWithKeyboardModifierTrampoline(FPDF_FORMFILLINFO* info,
+                                               FPDF_BYTESTRING uri,
+                                               int modifiers) {
+  auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
+  return delegate->DoURIActionWithKeyboardModifier(info, uri, modifiers);
+}
+
+// These do nothing (but must return a reasonable default value).
+void InvalidateStub(FPDF_FORMFILLINFO* pThis,
+                    FPDF_PAGE page,
+                    double left,
+                    double top,
+                    double right,
+                    double bottom) {}
+
+void OutputSelectedRectStub(FPDF_FORMFILLINFO* pThis,
+                            FPDF_PAGE page,
+                            double left,
+                            double top,
+                            double right,
+                            double bottom) {}
+
+void SetCursorStub(FPDF_FORMFILLINFO* pThis, int nCursorType) {}
+
+FPDF_SYSTEMTIME GetLocalTimeStub(FPDF_FORMFILLINFO* pThis) {
+  return {122, 11, 6, 28, 12, 59, 59, 500};
+}
+
+void OnChangeStub(FPDF_FORMFILLINFO* pThis) {}
+
+FPDF_PAGE GetCurrentPageStub(FPDF_FORMFILLINFO* pThis, FPDF_DOCUMENT document) {
+  return GetPageTrampoline(pThis, document, 0);
+}
+
+int GetRotationStub(FPDF_FORMFILLINFO* pThis, FPDF_PAGE page) {
+  return 0;
+}
+
+void ExecuteNamedActionStub(FPDF_FORMFILLINFO* pThis, FPDF_BYTESTRING name) {}
+
+void SetTextFieldFocusStub(FPDF_FORMFILLINFO* pThis,
+                           FPDF_WIDESTRING value,
+                           FPDF_DWORD valueLen,
+                           FPDF_BOOL is_focus) {}
+
+#ifdef PDF_ENABLE_XFA
+void DisplayCaretStub(FPDF_FORMFILLINFO* pThis,
+                      FPDF_PAGE page,
+                      FPDF_BOOL bVisible,
+                      double left,
+                      double top,
+                      double right,
+                      double bottom) {}
+
+int GetCurrentPageIndexStub(FPDF_FORMFILLINFO* pThis, FPDF_DOCUMENT document) {
+  return 0;
+}
+
+void SetCurrentPageStub(FPDF_FORMFILLINFO* pThis,
+                        FPDF_DOCUMENT document,
+                        int iCurPage) {}
+
+void GotoURLStub(FPDF_FORMFILLINFO* pThis,
+                 FPDF_DOCUMENT document,
+                 FPDF_WIDESTRING wsURL) {}
+
+void GetPageViewRectStub(FPDF_FORMFILLINFO* pThis,
+                         FPDF_PAGE page,
+                         double* left,
+                         double* top,
+                         double* right,
+                         double* bottom) {
+  *left = 0.0;
+  *top = 0.0;
+  *right = 512.0;
+  *bottom = 512.0;
+}
+
+void PageEventStub(FPDF_FORMFILLINFO* pThis,
+                   int page_count,
+                   FPDF_DWORD event_type) {}
+
+FPDF_BOOL PopupMenuStub(FPDF_FORMFILLINFO* pThis,
+                        FPDF_PAGE page,
+                        FPDF_WIDGET hWidget,
+                        int menuFlag,
+                        float x,
+                        float y) {
+  return true;
+}
+
+FPDF_FILEHANDLER* OpenFileStub(FPDF_FORMFILLINFO* pThis,
+                               int fileFlag,
+                               FPDF_WIDESTRING wsURL,
+                               const char* mode) {
+  return nullptr;
+}
+
+void EmailToStub(FPDF_FORMFILLINFO* pThis,
+                 FPDF_FILEHANDLER* fileHandler,
+                 FPDF_WIDESTRING pTo,
+                 FPDF_WIDESTRING pSubject,
+                 FPDF_WIDESTRING pCC,
+                 FPDF_WIDESTRING pBcc,
+                 FPDF_WIDESTRING pMsg) {}
+
+void UploadToStub(FPDF_FORMFILLINFO* pThis,
+                  FPDF_FILEHANDLER* fileHandler,
+                  int fileFlag,
+                  FPDF_WIDESTRING uploadTo) {}
+
+int GetPlatformStub(FPDF_FORMFILLINFO* pThis, void* platform, int length) {
+  return 0;
+}
+
+int GetLanguageStub(FPDF_FORMFILLINFO* pThis, void* language, int length) {
+  return 0;
+}
+
+FPDF_FILEHANDLER* DownloadFromURLStub(FPDF_FORMFILLINFO* pThis,
+                                      FPDF_WIDESTRING URL) {
+  static const char kString[] = "<body>secrets</body>";
+  static FPDF_FILEHANDLER kFakeFileHandler = {
+      nullptr,
+      [](void*) -> void {},
+      [](void*) -> FPDF_DWORD { return sizeof(kString); },
+      [](void*, FPDF_DWORD off, void* buffer, FPDF_DWORD size) -> FPDF_RESULT {
+        memcpy(buffer, kString, std::min<size_t>(size, sizeof(kString)));
+        return 0;
+      },
+      [](void*, FPDF_DWORD, const void*, FPDF_DWORD) -> FPDF_RESULT {
+        return -1;
+      },
+      [](void*) -> FPDF_RESULT { return 0; },
+      [](void*, FPDF_DWORD) -> FPDF_RESULT { return 0; }};
+  return &kFakeFileHandler;
+}
+
+FPDF_BOOL PostRequestURLStub(FPDF_FORMFILLINFO* pThis,
+                             FPDF_WIDESTRING wsURL,
+                             FPDF_WIDESTRING wsData,
+                             FPDF_WIDESTRING wsContentType,
+                             FPDF_WIDESTRING wsEncode,
+                             FPDF_WIDESTRING wsHeader,
+                             FPDF_BSTR* response) {
+  const char kString[] = "p\0o\0s\0t\0e\0d\0";
+  FPDF_BStr_Set(response, kString, sizeof(kString) - 1);
+  return true;
+}
+
+FPDF_BOOL PutRequestURLStub(FPDF_FORMFILLINFO* pThis,
+                            FPDF_WIDESTRING wsURL,
+                            FPDF_WIDESTRING wsData,
+                            FPDF_WIDESTRING wsEncode) {
+  return true;
+}
+#endif  // PDF_ENABLE_XFA
 
 }  // namespace
 
 EmbedderTest::EmbedderTest()
-    : default_delegate_(pdfium::MakeUnique<EmbedderTest::Delegate>()),
+    : default_delegate_(std::make_unique<EmbedderTest::Delegate>()),
       delegate_(default_delegate_.get()) {
   FPDF_FILEWRITE::version = 1;
   FPDF_FILEWRITE::WriteBlock = WriteBlockCallback;
@@ -64,52 +272,31 @@
 EmbedderTest::~EmbedderTest() = default;
 
 void EmbedderTest::SetUp() {
-  FPDF_LIBRARY_CONFIG config;
-  config.version = 2;
-  config.m_pUserFontPaths = nullptr;
-  config.m_v8EmbedderSlot = 0;
-  config.m_pIsolate = external_isolate_;
-  FPDF_InitLibraryWithConfig(&config);
-
   UNSUPPORT_INFO* info = static_cast<UNSUPPORT_INFO*>(this);
   memset(info, 0, sizeof(UNSUPPORT_INFO));
   info->version = 1;
   info->FSDK_UnSupport_Handler = UnsupportedHandlerTrampoline;
   FSDK_SetUnSpObjProcessHandler(info);
-
-  saved_document_ = nullptr;
 }
 
 void EmbedderTest::TearDown() {
   // Use an EXPECT_EQ() here and continue to let TearDown() finish as cleanly as
-  // possible. This can fail when an ASSERT test fails in a test case.
+  // possible. This can fail when an DCHECK test fails in a test case.
   EXPECT_EQ(0U, page_map_.size());
   EXPECT_EQ(0U, saved_page_map_.size());
-
-  if (document_) {
-    FORM_DoDocumentAAction(form_handle_, FPDFDOC_AACTION_WC);
+  if (document())
     CloseDocument();
-  }
-
-  FPDFAvail_Destroy(avail_);
-  FPDF_DestroyLibrary();
-  loader_.reset();
 }
 
-#ifdef PDF_ENABLE_V8
-void EmbedderTest::SetExternalIsolate(void* isolate) {
-  external_isolate_ = static_cast<v8::Isolate*>(isolate);
+void EmbedderTest::CreateEmptyDocument() {
+  CreateEmptyDocumentWithoutFormFillEnvironment();
+  form_handle_.reset(SetupFormFillEnvironment(
+      document(), JavaScriptOption::kEnableJavaScript));
 }
-#endif  // PDF_ENABLE_V8
 
-bool EmbedderTest::CreateEmptyDocument() {
-  document_ = FPDF_CreateNewDocument();
-  if (!document_)
-    return false;
-
-  form_handle_ =
-      SetupFormFillEnvironment(document_, JavaScriptOption::kEnableJavaScript);
-  return true;
+void EmbedderTest::CreateEmptyDocumentWithoutFormFillEnvironment() {
+  document_.reset(FPDF_CreateNewDocument());
+  DCHECK(document_);
 }
 
 bool EmbedderTest::OpenDocument(const std::string& filename) {
@@ -150,7 +337,7 @@
     return false;
 
   EXPECT_TRUE(!loader_);
-  loader_ = pdfium::MakeUnique<TestLoader>(
+  loader_ = std::make_unique<TestLoader>(
       pdfium::make_span(file_contents_.get(), file_length_));
 
   memset(&file_access_, 0, sizeof(file_access_));
@@ -158,7 +345,7 @@
   file_access_.m_GetBlock = TestLoader::GetBlock;
   file_access_.m_Param = loader_.get();
 
-  fake_file_access_ = pdfium::MakeUnique<FakeFileAccess>(&file_access_);
+  fake_file_access_ = std::make_unique<FakeFileAccess>(&file_access_);
   return OpenDocumentHelper(password, linearize_option, javascript_option,
                             fake_file_access_.get(), &document_, &avail_,
                             &form_handle_);
@@ -168,42 +355,45 @@
                                       LinearizeOption linearize_option,
                                       JavaScriptOption javascript_option,
                                       FakeFileAccess* network_simulator,
-                                      FPDF_DOCUMENT* document,
-                                      FPDF_AVAIL* avail,
-                                      FPDF_FORMHANDLE* form_handle) {
+                                      ScopedFPDFDocument* document,
+                                      ScopedFPDFAvail* avail,
+                                      ScopedFPDFFormHandle* form_handle) {
   network_simulator->AddSegment(0, 1024);
   network_simulator->SetRequestedDataAvailable();
-  *avail = FPDFAvail_Create(network_simulator->GetFileAvail(),
-                            network_simulator->GetFileAccess());
-  if (FPDFAvail_IsLinearized(*avail) == PDF_LINEARIZED) {
+  avail->reset(FPDFAvail_Create(network_simulator->GetFileAvail(),
+                                network_simulator->GetFileAccess()));
+  FPDF_AVAIL avail_ptr = avail->get();
+  FPDF_DOCUMENT document_ptr = nullptr;
+  if (FPDFAvail_IsLinearized(avail_ptr) == PDF_LINEARIZED) {
     int32_t nRet = PDF_DATA_NOTAVAIL;
     while (nRet == PDF_DATA_NOTAVAIL) {
       network_simulator->SetRequestedDataAvailable();
-      nRet =
-          FPDFAvail_IsDocAvail(*avail, network_simulator->GetDownloadHints());
+      nRet = FPDFAvail_IsDocAvail(avail_ptr,
+                                  network_simulator->GetDownloadHints());
     }
     if (nRet == PDF_DATA_ERROR)
       return false;
 
-    *document = FPDFAvail_GetDocument(*avail, password);
-    if (!*document)
+    document->reset(FPDFAvail_GetDocument(avail_ptr, password));
+    document_ptr = document->get();
+    if (!document_ptr)
       return false;
 
     nRet = PDF_DATA_NOTAVAIL;
     while (nRet == PDF_DATA_NOTAVAIL) {
       network_simulator->SetRequestedDataAvailable();
-      nRet =
-          FPDFAvail_IsFormAvail(*avail, network_simulator->GetDownloadHints());
+      nRet = FPDFAvail_IsFormAvail(avail_ptr,
+                                   network_simulator->GetDownloadHints());
     }
     if (nRet == PDF_FORM_ERROR)
       return false;
 
-    int page_count = FPDF_GetPageCount(*document);
+    int page_count = FPDF_GetPageCount(document_ptr);
     for (int i = 0; i < page_count; ++i) {
       nRet = PDF_DATA_NOTAVAIL;
       while (nRet == PDF_DATA_NOTAVAIL) {
         network_simulator->SetRequestedDataAvailable();
-        nRet = FPDFAvail_IsPageAvail(*avail, i,
+        nRet = FPDFAvail_IsPageAvail(avail_ptr, i,
                                      network_simulator->GetDownloadHints());
       }
       if (nRet == PDF_DATA_ERROR)
@@ -213,27 +403,31 @@
     if (linearize_option == LinearizeOption::kMustLinearize)
       return false;
     network_simulator->SetWholeFileAvailable();
-    *document =
-        FPDF_LoadCustomDocument(network_simulator->GetFileAccess(), password);
-    if (!*document)
+    document->reset(
+        FPDF_LoadCustomDocument(network_simulator->GetFileAccess(), password));
+    document_ptr = document->get();
+    if (!document_ptr)
       return false;
   }
-  *form_handle = SetupFormFillEnvironment(*document, javascript_option);
+  form_handle->reset(SetupFormFillEnvironment(document_ptr, javascript_option));
 
-  int doc_type = FPDF_GetFormType(*document);
+  int doc_type = FPDF_GetFormType(document_ptr);
   if (doc_type == FORMTYPE_XFA_FULL || doc_type == FORMTYPE_XFA_FOREGROUND)
-    FPDF_LoadXFA(*document);
+    FPDF_LoadXFA(document_ptr);
 
-  (void)FPDF_GetDocPermissions(*document);
+  (void)FPDF_GetDocPermissions(document_ptr);
   return true;
 }
 
 void EmbedderTest::CloseDocument() {
-  FPDFDOC_ExitFormFillEnvironment(form_handle_);
-  form_handle_ = nullptr;
-
-  FPDF_CloseDocument(document_);
-  document_ = nullptr;
+  FORM_DoDocumentAAction(form_handle(), FPDFDOC_AACTION_WC);
+  form_handle_.reset();
+  document_.reset();
+  avail_.reset();
+  fake_file_access_.reset();
+  memset(&file_access_, 0, sizeof(file_access_));
+  loader_.reset();
+  file_contents_.reset();
 }
 
 FPDF_FORMHANDLE EmbedderTest::SetupFormFillEnvironment(
@@ -241,21 +435,46 @@
     JavaScriptOption javascript_option) {
   IPDF_JSPLATFORM* platform = static_cast<IPDF_JSPLATFORM*>(this);
   memset(platform, '\0', sizeof(IPDF_JSPLATFORM));
-  platform->version = 2;
+  platform->version = 3;
   platform->app_alert = AlertTrampoline;
-  platform->m_isolate = external_isolate_;
 
   FPDF_FORMFILLINFO* formfillinfo = static_cast<FPDF_FORMFILLINFO*>(this);
   memset(formfillinfo, 0, sizeof(FPDF_FORMFILLINFO));
-#ifdef PDF_ENABLE_XFA
-  formfillinfo->version = 2;
-#else   // PDF_ENABLE_XFA
-  formfillinfo->version = 1;
-#endif  // PDF_ENABLE_XFA
+  formfillinfo->version = form_fill_info_version_;
+  formfillinfo->FFI_Invalidate = InvalidateStub;
+  formfillinfo->FFI_OutputSelectedRect = OutputSelectedRectStub;
+  formfillinfo->FFI_SetCursor = SetCursorStub;
   formfillinfo->FFI_SetTimer = SetTimerTrampoline;
   formfillinfo->FFI_KillTimer = KillTimerTrampoline;
+  formfillinfo->FFI_GetLocalTime = GetLocalTimeStub;
+  formfillinfo->FFI_OnChange = OnChangeStub;
   formfillinfo->FFI_GetPage = GetPageTrampoline;
+  formfillinfo->FFI_GetCurrentPage = GetCurrentPageStub;
+  formfillinfo->FFI_GetRotation = GetRotationStub;
+  formfillinfo->FFI_ExecuteNamedAction = ExecuteNamedActionStub;
+  formfillinfo->FFI_SetTextFieldFocus = SetTextFieldFocusStub;
   formfillinfo->FFI_DoURIAction = DoURIActionTrampoline;
+  formfillinfo->FFI_DoGoToAction = DoGoToActionTrampoline;
+#ifdef PDF_ENABLE_XFA
+  formfillinfo->FFI_DisplayCaret = DisplayCaretStub;
+  formfillinfo->FFI_GetCurrentPageIndex = GetCurrentPageIndexStub;
+  formfillinfo->FFI_SetCurrentPage = SetCurrentPageStub;
+  formfillinfo->FFI_GotoURL = GotoURLStub;
+  formfillinfo->FFI_GetPageViewRect = GetPageViewRectStub;
+  formfillinfo->FFI_PageEvent = PageEventStub;
+  formfillinfo->FFI_PopupMenu = PopupMenuStub;
+  formfillinfo->FFI_OpenFile = OpenFileStub;
+  formfillinfo->FFI_EmailTo = EmailToStub;
+  formfillinfo->FFI_UploadTo = UploadToStub;
+  formfillinfo->FFI_GetPlatform = GetPlatformStub;
+  formfillinfo->FFI_GetLanguage = GetLanguageStub;
+  formfillinfo->FFI_DownloadFromURL = DownloadFromURLStub;
+  formfillinfo->FFI_PostRequestURL = PostRequestURLStub;
+  formfillinfo->FFI_PutRequestURL = PutRequestURLStub;
+#endif  // PDF_ENABLE_XFA
+  formfillinfo->FFI_OnFocusChange = OnFocusChangeTrampoline;
+  formfillinfo->FFI_DoURIActionWithKeyboardModifier =
+      DoURIActionWithKeyboardModifierTrampoline;
 
   if (javascript_option == JavaScriptOption::kEnableJavaScript)
     formfillinfo->m_pJsPlatform = platform;
@@ -267,22 +486,22 @@
 }
 
 void EmbedderTest::DoOpenActions() {
-  ASSERT(form_handle_);
-  FORM_DoDocumentJSAction(form_handle_);
-  FORM_DoDocumentOpenAction(form_handle_);
+  DCHECK(form_handle());
+  FORM_DoDocumentJSAction(form_handle());
+  FORM_DoDocumentOpenAction(form_handle());
 }
 
 int EmbedderTest::GetFirstPageNum() {
-  int first_page = FPDFAvail_GetFirstPageNum(document_);
-  (void)FPDFAvail_IsPageAvail(avail_, first_page,
+  int first_page = FPDFAvail_GetFirstPageNum(document());
+  (void)FPDFAvail_IsPageAvail(avail(), first_page,
                               fake_file_access_->GetDownloadHints());
   return first_page;
 }
 
 int EmbedderTest::GetPageCount() {
-  int page_count = FPDF_GetPageCount(document_);
+  int page_count = FPDF_GetPageCount(document());
   for (int i = 0; i < page_count; ++i)
-    (void)FPDFAvail_IsPageAvail(avail_, i,
+    (void)FPDFAvail_IsPageAvail(avail(), i,
                                 fake_file_access_->GetDownloadHints());
   return page_count;
 }
@@ -296,17 +515,17 @@
 }
 
 FPDF_PAGE EmbedderTest::LoadPageCommon(int page_number, bool do_events) {
-  ASSERT(form_handle_);
-  ASSERT(page_number >= 0);
-  ASSERT(!pdfium::ContainsKey(page_map_, page_number));
+  DCHECK(form_handle());
+  DCHECK(page_number >= 0);
+  DCHECK(!pdfium::Contains(page_map_, page_number));
 
-  FPDF_PAGE page = FPDF_LoadPage(document_, page_number);
+  FPDF_PAGE page = FPDF_LoadPage(document(), page_number);
   if (!page)
     return nullptr;
 
   if (do_events) {
-    FORM_OnAfterLoadPage(page, form_handle_);
-    FORM_DoPageAAction(page, form_handle_, FPDFPAGE_AACTION_OPEN);
+    FORM_OnAfterLoadPage(page, form_handle());
+    FORM_DoPageAAction(page, form_handle(), FPDFPAGE_AACTION_OPEN);
   }
   page_map_[page_number] = page;
   return page;
@@ -321,15 +540,13 @@
 }
 
 void EmbedderTest::UnloadPageCommon(FPDF_PAGE page, bool do_events) {
-  ASSERT(form_handle_);
+  DCHECK(form_handle());
   int page_number = GetPageNumberForLoadedPage(page);
-  if (page_number < 0) {
-    NOTREACHED();
-    return;
-  }
+  CHECK_GE(page_number, 0);
+
   if (do_events) {
-    FORM_DoPageAAction(page, form_handle_, FPDFPAGE_AACTION_CLOSE);
-    FORM_OnBeforeClosePage(page, form_handle_);
+    FORM_DoPageAAction(page, form_handle(), FPDFPAGE_AACTION_CLOSE);
+    FORM_OnBeforeClosePage(page, form_handle());
   }
   FPDF_ClosePage(page);
   page_map_.erase(page_number);
@@ -346,11 +563,9 @@
 
 ScopedFPDFBitmap EmbedderTest::RenderLoadedPageWithFlags(FPDF_PAGE page,
                                                          int flags) {
-  if (GetPageNumberForLoadedPage(page) < 0) {
-    NOTREACHED();
-    return nullptr;
-  }
-  return RenderPageWithFlags(page, form_handle_, flags);
+  int page_number = GetPageNumberForLoadedPage(page);
+  CHECK_GE(page_number, 0);
+  return RenderPageWithFlags(page, form_handle(), flags);
 }
 
 ScopedFPDFBitmap EmbedderTest::RenderSavedPage(FPDF_PAGE page) {
@@ -359,11 +574,9 @@
 
 ScopedFPDFBitmap EmbedderTest::RenderSavedPageWithFlags(FPDF_PAGE page,
                                                         int flags) {
-  if (GetPageNumberForSavedPage(page) < 0) {
-    NOTREACHED();
-    return nullptr;
-  }
-  return RenderPageWithFlags(page, saved_form_handle_, flags);
+  int page_number = GetPageNumberForSavedPage(page);
+  CHECK_GE(page_number, 0);
+  return RenderPageWithFlags(page, saved_form_handle(), flags);
 }
 
 // static
@@ -386,7 +599,7 @@
   return RenderPageWithFlags(page, nullptr, 0);
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 // static
 std::vector<uint8_t> EmbedderTest::RenderPageWithFlagsToEmf(FPDF_PAGE page,
                                                             int flags) {
@@ -406,7 +619,7 @@
   FPDF_RenderPage(dc, page, 0, 0, width, height, 0, flags);
 
   HENHMETAFILE emf = CloseEnhMetaFile(dc);
-  size_t size_in_bytes = GetEnhMetaFileBits(emf, 0, nullptr);
+  UINT size_in_bytes = GetEnhMetaFileBits(emf, 0, nullptr);
   std::vector<uint8_t> buffer(size_in_bytes);
   GetEnhMetaFileBits(emf, size_in_bytes, buffer.data());
   DeleteEnhMetaFile(emf);
@@ -417,7 +630,8 @@
 std::string EmbedderTest::GetPostScriptFromEmf(
     pdfium::span<const uint8_t> emf_data) {
   // This comes from Emf::InitFromData() in Chromium.
-  HENHMETAFILE emf = SetEnhMetaFileBits(emf_data.size(), emf_data.data());
+  HENHMETAFILE emf = SetEnhMetaFileBits(
+      pdfium::base::checked_cast<UINT>(emf_data.size()), emf_data.data());
   if (!emf)
     return std::string();
 
@@ -446,7 +660,7 @@
   DeleteEnhMetaFile(emf);
   return ps_data;
 }
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 FPDF_DOCUMENT EmbedderTest::OpenSavedDocument() {
   return OpenSavedDocumentWithPassword(nullptr);
@@ -463,68 +677,61 @@
     case FPDFBitmap_BGRA:
       return 4;
     default:
-      NOTREACHED();
-      return 0;
+      NOTREACHED_NORETURN();
   }
 }
 
 FPDF_DOCUMENT EmbedderTest::OpenSavedDocumentWithPassword(
     const char* password) {
   memset(&saved_file_access_, 0, sizeof(saved_file_access_));
-  saved_file_access_.m_FileLen = data_string_.size();
+  saved_file_access_.m_FileLen =
+      pdfium::base::checked_cast<unsigned long>(data_string_.size());
   saved_file_access_.m_GetBlock = GetBlockFromString;
   // Copy data to prevent clearing it before saved document close.
   saved_document_file_data_ = data_string_;
   saved_file_access_.m_Param = &saved_document_file_data_;
 
   saved_fake_file_access_ =
-      pdfium::MakeUnique<FakeFileAccess>(&saved_file_access_);
+      std::make_unique<FakeFileAccess>(&saved_file_access_);
 
   EXPECT_TRUE(OpenDocumentHelper(
       password, LinearizeOption::kDefaultLinearize,
       JavaScriptOption::kEnableJavaScript, saved_fake_file_access_.get(),
       &saved_document_, &saved_avail_, &saved_form_handle_));
-  return saved_document_;
+  return saved_document();
 }
 
 void EmbedderTest::CloseSavedDocument() {
-  ASSERT(saved_document_);
+  DCHECK(saved_document());
 
-  FPDFDOC_ExitFormFillEnvironment(saved_form_handle_);
-  FPDF_CloseDocument(saved_document_);
-  FPDFAvail_Destroy(saved_avail_);
-
-  saved_form_handle_ = nullptr;
-  saved_document_ = nullptr;
-  saved_avail_ = nullptr;
+  saved_form_handle_.reset();
+  saved_document_.reset();
+  saved_avail_.reset();
 }
 
 FPDF_PAGE EmbedderTest::LoadSavedPage(int page_number) {
-  ASSERT(saved_form_handle_);
-  ASSERT(page_number >= 0);
-  ASSERT(!pdfium::ContainsKey(saved_page_map_, page_number));
+  DCHECK(saved_form_handle());
+  DCHECK(page_number >= 0);
+  DCHECK(!pdfium::Contains(saved_page_map_, page_number));
 
-  FPDF_PAGE page = FPDF_LoadPage(saved_document_, page_number);
+  FPDF_PAGE page = FPDF_LoadPage(saved_document(), page_number);
   if (!page)
     return nullptr;
 
-  FORM_OnAfterLoadPage(page, saved_form_handle_);
-  FORM_DoPageAAction(page, saved_form_handle_, FPDFPAGE_AACTION_OPEN);
+  FORM_OnAfterLoadPage(page, saved_form_handle());
+  FORM_DoPageAAction(page, saved_form_handle(), FPDFPAGE_AACTION_OPEN);
   saved_page_map_[page_number] = page;
   return page;
 }
 
 void EmbedderTest::CloseSavedPage(FPDF_PAGE page) {
-  ASSERT(saved_form_handle_);
+  DCHECK(saved_form_handle());
 
   int page_number = GetPageNumberForSavedPage(page);
-  if (page_number < 0) {
-    NOTREACHED();
-    return;
-  }
+  CHECK_GE(page_number, 0);
 
-  FORM_DoPageAAction(page, saved_form_handle_, FPDFPAGE_AACTION_CLOSE);
-  FORM_OnBeforeClosePage(page, saved_form_handle_);
+  FORM_DoPageAAction(page, saved_form_handle(), FPDFPAGE_AACTION_CLOSE);
+  FORM_OnBeforeClosePage(page, saved_form_handle());
   FPDF_ClosePage(page);
 
   saved_page_map_.erase(page_number);
@@ -534,8 +741,8 @@
                                         int width,
                                         int height,
                                         const char* md5) {
-  ASSERT(saved_document_);
-  ASSERT(page);
+  DCHECK(saved_document());
+  DCHECK(page);
 
   ScopedFPDFBitmap bitmap = RenderSavedPageWithFlags(page, FPDF_ANNOT);
   CompareBitmap(bitmap.get(), width, height, md5);
@@ -550,10 +757,19 @@
 }
 
 void EmbedderTest::SetWholeFileAvailable() {
-  ASSERT(fake_file_access_);
+  DCHECK(fake_file_access_);
   fake_file_access_->SetWholeFileAvailable();
 }
 
+void EmbedderTest::SetDocumentFromAvail() {
+  document_.reset(FPDFAvail_GetDocument(avail(), nullptr));
+}
+
+void EmbedderTest::CreateAvail(FX_FILEAVAIL* file_avail,
+                               FPDF_FILEACCESS* file) {
+  avail_.reset(FPDFAvail_Create(file_avail, file));
+}
+
 FPDF_PAGE EmbedderTest::Delegate::GetPage(FPDF_FORMFILLINFO* info,
                                           FPDF_DOCUMENT document,
                                           int page_index) {
@@ -563,52 +779,6 @@
 }
 
 // static
-void EmbedderTest::UnsupportedHandlerTrampoline(UNSUPPORT_INFO* info,
-                                                int type) {
-  EmbedderTest* test = static_cast<EmbedderTest*>(info);
-  test->delegate_->UnsupportedHandler(type);
-}
-
-// static
-int EmbedderTest::AlertTrampoline(IPDF_JSPLATFORM* platform,
-                                  FPDF_WIDESTRING message,
-                                  FPDF_WIDESTRING title,
-                                  int type,
-                                  int icon) {
-  EmbedderTest* test = static_cast<EmbedderTest*>(platform);
-  return test->delegate_->Alert(message, title, type, icon);
-}
-
-// static
-int EmbedderTest::SetTimerTrampoline(FPDF_FORMFILLINFO* info,
-                                     int msecs,
-                                     TimerCallback fn) {
-  EmbedderTest* test = static_cast<EmbedderTest*>(info);
-  return test->delegate_->SetTimer(msecs, fn);
-}
-
-// static
-void EmbedderTest::KillTimerTrampoline(FPDF_FORMFILLINFO* info, int id) {
-  EmbedderTest* test = static_cast<EmbedderTest*>(info);
-  return test->delegate_->KillTimer(id);
-}
-
-// static
-FPDF_PAGE EmbedderTest::GetPageTrampoline(FPDF_FORMFILLINFO* info,
-                                          FPDF_DOCUMENT document,
-                                          int page_index) {
-  return static_cast<EmbedderTest*>(info)->delegate_->GetPage(info, document,
-                                                              page_index);
-}
-
-// static
-void EmbedderTest::DoURIActionTrampoline(FPDF_FORMFILLINFO* info,
-                                         FPDF_BYTESTRING uri) {
-  EmbedderTest* test = static_cast<EmbedderTest*>(info);
-  return test->delegate_->DoURIAction(uri);
-}
-
-// static
 std::string EmbedderTest::HashBitmap(FPDF_BITMAP bitmap) {
   int stride = FPDFBitmap_GetStride(bitmap);
   int usable_bytes_per_row =
@@ -625,13 +795,11 @@
   return CryptToBase16(digest);
 }
 
-#ifndef NDEBUG
 // static
 void EmbedderTest::WriteBitmapToPng(FPDF_BITMAP bitmap,
                                     const std::string& filename) {
   BitmapSaver::WriteBitmapToPng(bitmap, filename);
 }
-#endif
 
 // static
 void EmbedderTest::CompareBitmap(FPDF_BITMAP bitmap,
@@ -650,7 +818,11 @@
   if (!expected_md5sum)
     return;
 
-  EXPECT_EQ(expected_md5sum, HashBitmap(bitmap));
+  std::string actual_md5sum = HashBitmap(bitmap);
+  EXPECT_EQ(expected_md5sum, actual_md5sum);
+  if (EmbedderTestEnvironment::GetInstance()->write_pngs()) {
+    WriteBitmapToPng(bitmap, actual_md5sum + ".png");
+  }
 }
 
 // static
@@ -673,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;
@@ -693,7 +861,7 @@
   for (const auto& it : page_map) {
     if (it.second == page) {
       int page_number = it.first;
-      ASSERT(page_number >= 0);
+      DCHECK(page_number >= 0);
       return page_number;
     }
   }
diff --git a/testing/embedder_test.h b/testing/embedder_test.h
index 662333c..829b1aa 100644
--- a/testing/embedder_test.h
+++ b/testing/embedder_test.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -21,10 +21,14 @@
 #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;
 
+// The loading time of the CFGAS_FontMgr is linear in the number of times it is
+// loaded. So, if a test suite has a lot of tests that need a font manager they
+// can end up executing very, very slowly.
+
 // This class is used to load a PDF document, and then run programatic
 // API tests against it.
 class EmbedderTest : public ::testing::Test,
@@ -64,6 +68,23 @@
 
     // Equivalent to FPDF_FORMFILLINFO::FFI_DoURIAction().
     virtual void DoURIAction(FPDF_BYTESTRING uri) {}
+
+    // Equivalent to FPDF_FORMFILLINFO::FFI_DoGoToAction().
+    virtual void DoGoToAction(FPDF_FORMFILLINFO* info,
+                              int page_index,
+                              int zoom_mode,
+                              float* pos_arry,
+                              int array_size) {}
+
+    // Equivalent to FPDF_FORMFILLINFO::FFI_OnFocusChange().
+    virtual void OnFocusChange(FPDF_FORMFILLINFO* info,
+                               FPDF_ANNOTATION annot,
+                               int page_index) {}
+
+    // Equivalent to FPDF_FORMFILLINFO::FFI_DoURIActionWithKeyboardModifier().
+    virtual void DoURIActionWithKeyboardModifier(FPDF_FORMFILLINFO* info,
+                                                 FPDF_BYTESTRING uri,
+                                                 int modifiers) {}
   };
 
   EmbedderTest();
@@ -72,21 +93,30 @@
   void SetUp() override;
   void TearDown() override;
 
-#ifdef PDF_ENABLE_V8
-  // Call before SetUp to pass shared isolate, otherwise PDFium creates one.
-  void SetExternalIsolate(void* isolate);
-#endif  // PDF_ENABLE_V8
-
+  Delegate* GetDelegate() { return delegate_; }
   void SetDelegate(Delegate* delegate) {
     delegate_ = delegate ? delegate : default_delegate_.get();
   }
 
-  FPDF_DOCUMENT document() const { return document_; }
-  FPDF_FORMHANDLE form_handle() const { return form_handle_; }
+  void SetFormFillInfoVersion(int form_fill_info_version) {
+    form_fill_info_version_ = form_fill_info_version;
+  }
 
-  // Create an empty document, and its form fill environment. Returns true
-  // on success or false on failure.
-  bool CreateEmptyDocument();
+  void SetDocumentFromAvail();
+  FPDF_DOCUMENT document() const { return document_.get(); }
+  FPDF_DOCUMENT saved_document() const { return saved_document_.get(); }
+  FPDF_FORMHANDLE form_handle() const { return form_handle_.get(); }
+  FPDF_FORMHANDLE saved_form_handle() const { return saved_form_handle_.get(); }
+
+  // Wrapper for FPDFAvail_Create() to set `avail_`.
+  void CreateAvail(FX_FILEAVAIL* file_avail, FPDF_FILEACCESS* file);
+  FPDF_AVAIL avail() { return avail_.get(); }
+
+  // Create an empty document, and its form fill environment.
+  void CreateEmptyDocument();
+
+  // Create an empty document without a form fill environment.
+  void CreateEmptyDocumentWithoutFormFillEnvironment();
 
   // Open the document specified by |filename|, and create its form fill
   // environment, or return false on failure. The |filename| is relative to
@@ -171,14 +201,14 @@
   // Simplified form of RenderPageWithFlags() with no handle and no flags.
   static ScopedFPDFBitmap RenderPage(FPDF_PAGE page);
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // Convert |page| into EMF with the specified page rendering |flags|.
   static std::vector<uint8_t> RenderPageWithFlagsToEmf(FPDF_PAGE page,
                                                        int flags);
 
   // Get the PostScript data from |emf_data|.
   static std::string GetPostScriptFromEmf(pdfium::span<const uint8_t> emf_data);
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
   // Return bytes for each of the FPDFBitmap_* format types.
   static int BytesPerPixelForFormat(int format);
@@ -190,9 +220,9 @@
                           LinearizeOption linearize_option,
                           JavaScriptOption javascript_option,
                           FakeFileAccess* network_simulator,
-                          FPDF_DOCUMENT* document,
-                          FPDF_AVAIL* avail,
-                          FPDF_FORMHANDLE* form_handle);
+                          ScopedFPDFDocument* document,
+                          ScopedFPDFAvail* avail,
+                          ScopedFPDFFormHandle* form_handle);
 
   FPDF_FORMHANDLE SetupFormFillEnvironment(FPDF_DOCUMENT doc,
                                            JavaScriptOption javascript_option);
@@ -201,11 +231,9 @@
   // any, at the end of a row where the stride is larger than width * bpp.
   static std::string HashBitmap(FPDF_BITMAP bitmap);
 
-#ifndef NDEBUG
   // For debugging purposes.
   // Write |bitmap| as a PNG to |filename|.
   static void WriteBitmapToPng(FPDF_BITMAP bitmap, const std::string& filename);
-#endif
 
   // Check |bitmap| to make sure it has the right dimensions and content.
   static void CompareBitmap(FPDF_BITMAP bitmap,
@@ -245,45 +273,7 @@
   void ClosePDFFileForWrite();
 #endif
 
-  std::unique_ptr<Delegate> default_delegate_;
-  Delegate* delegate_;
-
-  FPDF_DOCUMENT document_ = nullptr;
-  FPDF_FORMHANDLE form_handle_ = nullptr;
-  FPDF_AVAIL avail_ = nullptr;
-  FPDF_FILEACCESS file_access_;                       // must outlive |avail_|.
-  std::unique_ptr<FakeFileAccess> fake_file_access_;  // must outlive |avail_|.
-
-  void* external_isolate_ = nullptr;
-  std::unique_ptr<TestLoader> loader_;
-  size_t file_length_ = 0;
-  std::unique_ptr<char, pdfium::FreeDeleter> file_contents_;
-  PageNumberToHandleMap page_map_;
-
-  FPDF_DOCUMENT saved_document_ = nullptr;
-  FPDF_FORMHANDLE saved_form_handle_ = nullptr;
-  FPDF_AVAIL saved_avail_ = nullptr;
-  FPDF_FILEACCESS saved_file_access_;  // must outlive |saved_avail_|.
-  // must outlive |saved_avail_|.
-  std::unique_ptr<FakeFileAccess> saved_fake_file_access_;
-  PageNumberToHandleMap saved_page_map_;
-
  private:
-  static void UnsupportedHandlerTrampoline(UNSUPPORT_INFO*, int type);
-  static int AlertTrampoline(IPDF_JSPLATFORM* plaform,
-                             FPDF_WIDESTRING message,
-                             FPDF_WIDESTRING title,
-                             int type,
-                             int icon);
-  static int SetTimerTrampoline(FPDF_FORMFILLINFO* info,
-                                int msecs,
-                                TimerCallback fn);
-  static void KillTimerTrampoline(FPDF_FORMFILLINFO* info, int id);
-  static FPDF_PAGE GetPageTrampoline(FPDF_FORMFILLINFO* info,
-                                     FPDF_DOCUMENT document,
-                                     int page_index);
-  static void DoURIActionTrampoline(FPDF_FORMFILLINFO* info,
-                                    FPDF_BYTESTRING uri);
   static int WriteBlockCallback(FPDF_FILEWRITE* pFileWrite,
                                 const void* data,
                                 unsigned long size);
@@ -301,6 +291,34 @@
   void UnloadPageCommon(FPDF_PAGE page, bool do_events);
   FPDF_PAGE LoadPageCommon(int page_number, bool do_events);
 
+  std::unique_ptr<Delegate> default_delegate_;
+  Delegate* delegate_;
+
+#ifdef PDF_ENABLE_XFA
+  int form_fill_info_version_ = 2;
+#else   // PDF_ENABLE_XFA
+  int form_fill_info_version_ = 1;
+#endif  // PDF_ENABLE_XFA
+
+  size_t file_length_ = 0;
+  // must outlive `loader_`.
+  std::unique_ptr<char, pdfium::FreeDeleter> file_contents_;
+  std::unique_ptr<TestLoader> loader_;
+  FPDF_FILEACCESS file_access_;                       // must outlive `avail_`.
+  std::unique_ptr<FakeFileAccess> fake_file_access_;  // must outlive `avail_`.
+  ScopedFPDFAvail avail_;
+  ScopedFPDFDocument document_;
+  ScopedFPDFFormHandle form_handle_;
+  PageNumberToHandleMap page_map_;
+
+  FPDF_FILEACCESS saved_file_access_;  // must outlive `saved_avail_`.
+  // must outlive `saved_avail_`.
+  std::unique_ptr<FakeFileAccess> saved_fake_file_access_;
+  ScopedFPDFAvail saved_avail_;
+  ScopedFPDFDocument saved_document_;
+  ScopedFPDFFormHandle saved_form_handle_;
+  PageNumberToHandleMap saved_page_map_;
+
   std::string data_string_;
   std::string saved_document_file_data_;
   std::ofstream filestream_;
diff --git a/testing/embedder_test_constants.cpp b/testing/embedder_test_constants.cpp
new file mode 100644
index 0000000..1364de1
--- /dev/null
+++ b/testing/embedder_test_constants.cpp
@@ -0,0 +1,74 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/embedder_test_constants.h"
+
+#include "build/build_config.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+
+namespace pdfium {
+
+const char* AnnotationStampWithApChecksum() {
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    return "c7ff65a3ad1b01c3a0e94d635f10670e";
+#if BUILDFLAG(IS_APPLE)
+  return "0521eaa52fe2aa43aafd3e4495f63f0b";
+#else
+  return "5f19ddad9d48f5b7b87ee7d92f577db6";
+#endif
+}
+
+const char kBlankPage612By792Checksum[] = "1940568c9ba33bac5d0b1ee9558c76b3";
+
+const char* Bug890322Checksum() {
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    return "793689536cf64fe792c2f241888c0cf3";
+  return "6c674642154408e877d88c6c082d67e9";
+}
+
+const char* HelloWorldChecksum() {
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    return "d1decde2de1c07b5274cc8cb44f92427";
+  }
+#if BUILDFLAG(IS_APPLE)
+  return "6eef7237f7591f07616e238422086737";
+#else
+  return "c1c548442e0e0f949c5550d89bf8ae3b";
+#endif
+}
+
+const char* HelloWorldRemovedChecksum() {
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    return "6e0307348e7c1b92f2f061f92f62fd45";
+  }
+#if BUILDFLAG(IS_APPLE)
+  return "6e1cae48a2e35c521dee4ca502f48af6";
+#else
+  return "4a9b80f675f7f3bf2da1b02f12449e4b";
+#endif
+}
+
+const char* ManyRectanglesChecksum() {
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    return "4e7e280c1597222afcb0ee3bb90ec119";
+  return "b0170c575b65ecb93ebafada0ff0f038";
+}
+
+const char* RectanglesChecksum() {
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    return "b4e411a6b5ffa59a50efede2efece597";
+  return "0a90de37f52127619c3dfb642b5fa2fe";
+}
+
+const char* TextFormChecksum() {
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    return "b259776fd156003e2a594d1c7ce2d8d7";
+#if BUILDFLAG(IS_APPLE)
+  return "fa2bf756942a950101fc147fc4ef3f82";
+#else
+  return "6f86fe1dbed5965d91aec6e0b829e29f";
+#endif
+}
+
+}  // namespace pdfium
diff --git a/testing/embedder_test_constants.h b/testing/embedder_test_constants.h
new file mode 100644
index 0000000..306880b
--- /dev/null
+++ b/testing/embedder_test_constants.h
@@ -0,0 +1,36 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_EMBEDDER_TEST_CONSTANTS_H_
+#define TESTING_EMBEDDER_TEST_CONSTANTS_H_
+
+namespace pdfium {
+
+// MD5 hash for rendering annotation_stamp_with_ap.pdf with annotations.
+const char* AnnotationStampWithApChecksum();
+
+// MD5 hash for rendering a 612x792 blank page.
+extern const char kBlankPage612By792Checksum[];
+
+// MD5 hash for rendering bug_890322.pdf.
+const char* Bug890322Checksum();
+
+// MD5 hash for rendering hello_world.pdf or bug_455199.pdf.
+const char* HelloWorldChecksum();
+
+// MD5 hash for rendering hello_world.pdf after removing "Goodbye, world!".
+const char* HelloWorldRemovedChecksum();
+
+// MD5 hash for rendering many_rectangles.pdf.
+const char* ManyRectanglesChecksum();
+
+// MD5 hash for rendering rectangles.pdf.
+const char* RectanglesChecksum();
+
+// MD5 hash for rendering text_form.pdf.
+const char* TextFormChecksum();
+
+}  // namespace pdfium
+
+#endif  // TESTING_EMBEDDER_TEST_CONSTANTS_H_
diff --git a/testing/embedder_test_environment.cpp b/testing/embedder_test_environment.cpp
new file mode 100644
index 0000000..07e459a
--- /dev/null
+++ b/testing/embedder_test_environment.cpp
@@ -0,0 +1,93 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/embedder_test_environment.h"
+
+#include <ostream>
+
+#include "core/fxcrt/fx_system.h"
+#include "public/fpdfview.h"
+#include "testing/command_line_helpers.h"
+#include "third_party/base/check.h"
+
+#ifdef PDF_ENABLE_V8
+#include "testing/v8_test_environment.h"
+#endif  // PDF_ENABLE_V8
+
+namespace {
+
+EmbedderTestEnvironment* g_environment = nullptr;
+
+}  // namespace
+
+EmbedderTestEnvironment::EmbedderTestEnvironment()
+    : renderer_type_(GetDefaultRendererType()) {
+  DCHECK(!g_environment);
+  g_environment = this;
+}
+
+EmbedderTestEnvironment::~EmbedderTestEnvironment() {
+  DCHECK(g_environment);
+  g_environment = nullptr;
+}
+
+// static
+EmbedderTestEnvironment* EmbedderTestEnvironment::GetInstance() {
+  return g_environment;
+}
+
+void EmbedderTestEnvironment::SetUp() {
+  FPDF_LIBRARY_CONFIG config;
+  config.version = 4;
+  config.m_pUserFontPaths = nullptr;
+  config.m_v8EmbedderSlot = 0;
+  config.m_pPlatform = nullptr;
+
+  config.m_pUserFontPaths = test_fonts_.font_paths();
+
+#ifdef PDF_ENABLE_V8
+  config.m_pIsolate = V8TestEnvironment::GetInstance()->isolate();
+  config.m_pPlatform = V8TestEnvironment::GetInstance()->platform();
+#else   // PDF_ENABLE_V8
+  config.m_pIsolate = nullptr;
+  config.m_pPlatform = nullptr;
+#endif  // PDF_ENABLE_V8
+  config.m_RendererType = renderer_type_;
+
+  FPDF_InitLibraryWithConfig(&config);
+
+  test_fonts_.InstallFontMapper();
+}
+
+void EmbedderTestEnvironment::TearDown() {
+  FPDF_DestroyLibrary();
+}
+
+void EmbedderTestEnvironment::AddFlags(int argc, char** argv) {
+  for (int i = 1; i < argc; ++i)
+    AddFlag(argv[i]);
+}
+
+void EmbedderTestEnvironment::AddFlag(const std::string& flag) {
+  if (flag == "--write-pngs") {
+    write_pngs_ = true;
+    return;
+  }
+#if defined(_SKIA_SUPPORT_)
+  std::string value;
+  if (ParseSwitchKeyValue(flag, "--use-renderer=", &value)) {
+    if (value == "agg") {
+      renderer_type_ = FPDF_RENDERERTYPE_AGG;
+    } else if (value == "skia") {
+      renderer_type_ = FPDF_RENDERERTYPE_SKIA;
+    } else {
+      std::cerr << "Invalid --use-renderer argument, value must be one of agg "
+                   "or skia\n";
+    }
+    return;
+  }
+#endif  // defined(_SKIA_SUPPORT_)
+
+  std::cerr << "Unknown flag: " << flag << "\n";
+}
diff --git a/testing/embedder_test_environment.h b/testing/embedder_test_environment.h
new file mode 100644
index 0000000..e04c5ae
--- /dev/null
+++ b/testing/embedder_test_environment.h
@@ -0,0 +1,39 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_EMBEDDER_TEST_ENVIRONMENT_H_
+#define TESTING_EMBEDDER_TEST_ENVIRONMENT_H_
+
+#include <string>
+
+#include "public/fpdfview.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/test_fonts.h"
+
+class EmbedderTestEnvironment : public testing::Environment {
+ public:
+  EmbedderTestEnvironment();
+  ~EmbedderTestEnvironment() override;
+
+  // Note: GetInstance() does not create one if it does not exist,
+  // so the main program must explicitly add this enviroment.
+  static EmbedderTestEnvironment* GetInstance();
+
+  // testing::Environment:
+  void SetUp() override;
+  void TearDown() override;
+
+  void AddFlags(int argc, char** argv);
+
+  bool write_pngs() const { return write_pngs_; }
+
+ private:
+  void AddFlag(const std::string& flag);
+
+  FPDF_RENDERER_TYPE renderer_type_;
+  bool write_pngs_ = false;
+  TestFonts test_fonts_;
+};
+
+#endif  // TESTING_EMBEDDER_TEST_ENVIRONMENT_H_
diff --git a/testing/embedder_test_main.cpp b/testing/embedder_test_main.cpp
index 34dfb06..e089b1a 100644
--- a/testing/embedder_test_main.cpp
+++ b/testing/embedder_test_main.cpp
@@ -1,90 +1,44 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
-#include <string>
-
+#include "build/build_config.h"
 #include "core/fxcrt/fx_memory.h"
+#include "testing/embedder_test_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #ifdef PDF_ENABLE_V8
-#include "testing/v8_initializer.h"
-#include "v8/include/v8-platform.h"
-#include "v8/include/v8.h"
-#endif  // PDF_ENABLE_v8
-
-namespace {
-
-const char* g_exe_path = nullptr;
-
-#ifdef PDF_ENABLE_V8
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
-v8::StartupData* g_v8_snapshot = nullptr;
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+#include "testing/v8_test_environment.h"
 #endif  // PDF_ENABLE_V8
 
-// The loading time of the CFGAS_FontMgr is linear in the number of times it is
-// loaded. So, if a test suite has a lot of tests that need a font manager they
-// can end up executing very, very slowly.
-class Environment final : public testing::Environment {
- public:
-  void SetUp() override {
-#ifdef PDF_ENABLE_V8
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
-    if (g_v8_snapshot) {
-      platform_ = InitializeV8ForPDFiumWithStartupData(g_exe_path,
-                                                       std::string(), nullptr);
-    } else {
-      g_v8_snapshot = new v8::StartupData;
-      platform_ = InitializeV8ForPDFiumWithStartupData(
-          g_exe_path, std::string(), g_v8_snapshot);
-    }
-#else
-    platform_ = InitializeV8ForPDFium(g_exe_path);
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-#endif  // FPDF_ENABLE_V8
-  }
+#if defined(PDF_USE_PARTITION_ALLOC)
+#include "testing/allocator_shim_config.h"
+#endif
 
-  void TearDown() override {
-#ifdef PDF_ENABLE_V8
-    v8::V8::ShutdownPlatform();
-#endif  // PDF_ENABLE_V8
-  }
-
- private:
-#ifdef PDF_ENABLE_V8
-  std::unique_ptr<v8::Platform> platform_;
-#endif  // PDF_ENABLE_V8
-};
-
-Environment* env_ = nullptr;
-
-}  // namespace
-
-// Can't use gtest-provided main since we need to stash the path to the
-// executable in order to find the external V8 binary data files.
+// 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) {
-  g_exe_path = argv[0];
+#if defined(PDF_USE_PARTITION_ALLOC)
+  pdfium::ConfigurePartitionAllocShimPartitionForTest();
+#endif
 
-  FXMEM_InitializePartitionAlloc();
+  FX_InitializeMemoryAllocators();
 
-  env_ = new Environment();
+#ifdef PDF_ENABLE_V8
   // The env will be deleted by gtest.
-  AddGlobalTestEnvironment(env_);
+  AddGlobalTestEnvironment(new V8TestEnvironment(argv[0]));
+#endif  // PDF_ENABLE_V8
+
+  // The env will be deleted by gtest.
+  AddGlobalTestEnvironment(new EmbedderTestEnvironment);
 
   testing::InitGoogleTest(&argc, argv);
   testing::InitGoogleMock(&argc, argv);
 
-  int ret_val = RUN_ALL_TESTS();
+  // Anything remaining in argc/argv is an embedder_tests flag.
+  EmbedderTestEnvironment::GetInstance()->AddFlags(argc, argv);
 
-#ifdef PDF_ENABLE_V8
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
-  if (g_v8_snapshot)
-    free(const_cast<char*>(g_v8_snapshot->data));
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-#endif  // PDF_ENABLE_V8
-
-  return ret_val;
+  return RUN_ALL_TESTS();
 }
diff --git a/testing/embedder_test_mock_delegate.h b/testing/embedder_test_mock_delegate.h
index c3f2820..7309541 100644
--- a/testing/embedder_test_mock_delegate.h
+++ b/testing/embedder_test_mock_delegate.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,7 @@
 #include "testing/embedder_test.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
-class EmbedderTestMockDelegate final : public EmbedderTest::Delegate {
+class EmbedderTestMockDelegate : public EmbedderTest::Delegate {
  public:
   MOCK_METHOD1(UnsupportedHandler, void(int type));
   MOCK_METHOD4(
@@ -16,6 +16,21 @@
       int(FPDF_WIDESTRING message, FPDF_WIDESTRING title, int type, int icon));
   MOCK_METHOD2(SetTimer, int(int msecs, TimerCallback fn));
   MOCK_METHOD1(KillTimer, void(int msecs));
+  MOCK_METHOD1(DoURIAction, void(FPDF_BYTESTRING uri));
+  MOCK_METHOD5(DoGoToAction,
+               void(FPDF_FORMFILLINFO* info,
+                    int page_index,
+                    int zoom_mode,
+                    float* pos_array,
+                    int array_size));
+  MOCK_METHOD3(OnFocusChange,
+               void(FPDF_FORMFILLINFO* info,
+                    FPDF_ANNOTATION annot,
+                    int page_index));
+  MOCK_METHOD3(DoURIActionWithKeyboardModifier,
+               void(FPDF_FORMFILLINFO* info,
+                    FPDF_BYTESTRING uri,
+                    int modifiers));
 };
 
 #endif  // TESTING_EMBEDDER_TEST_MOCK_DELEGATE_H_
diff --git a/testing/embedder_test_timer_handling_delegate.h b/testing/embedder_test_timer_handling_delegate.h
index a32ad20..e511254 100644
--- a/testing/embedder_test_timer_handling_delegate.h
+++ b/testing/embedder_test_timer_handling_delegate.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -57,7 +57,7 @@
 
   void AdvanceTime(int increment_msecs) {
     fake_elapsed_msecs_ += increment_msecs;
-    while (1) {
+    while (true) {
       auto iter = expiry_to_timer_map_.begin();
       if (iter == expiry_to_timer_map_.end()) {
         break;
diff --git a/testing/external_engine_embedder_test.cpp b/testing/external_engine_embedder_test.cpp
new file mode 100644
index 0000000..031981d
--- /dev/null
+++ b/testing/external_engine_embedder_test.cpp
@@ -0,0 +1,37 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/external_engine_embedder_test.h"
+
+#include <memory>
+
+#include "fxjs/cfxjs_engine.h"
+#include "testing/v8_test_environment.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-local-handle.h"
+
+ExternalEngineEmbedderTest::ExternalEngineEmbedderTest() = default;
+
+ExternalEngineEmbedderTest::~ExternalEngineEmbedderTest() = default;
+
+void ExternalEngineEmbedderTest::SetUp() {
+  EmbedderTest::SetUp();
+
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  FXJS_PerIsolateData::SetUp(isolate());
+  m_Engine = std::make_unique<CFXJS_Engine>(isolate());
+  m_Engine->InitializeEngine();
+}
+
+void ExternalEngineEmbedderTest::TearDown() {
+  m_Engine->ReleaseEngine();
+  m_Engine.reset();
+  JSEmbedderTest::TearDown();
+}
+
+v8::Local<v8::Context> ExternalEngineEmbedderTest::GetV8Context() {
+  return m_Engine->GetV8Context();
+}
diff --git a/testing/external_engine_embedder_test.h b/testing/external_engine_embedder_test.h
new file mode 100644
index 0000000..258e124
--- /dev/null
+++ b/testing/external_engine_embedder_test.h
@@ -0,0 +1,35 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_EXTERNAL_ENGINE_EMBEDDER_TEST_H_
+#define TESTING_EXTERNAL_ENGINE_EMBEDDER_TEST_H_
+
+#include <memory>
+
+#include "testing/js_embedder_test.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-local-handle.h"
+
+class CFXJS_Engine;
+
+// Test class that allows creating a FXJS javascript engine without
+// first having to load a document and instantiate a form filler
+// against it. Generally, most tests will want to do the latter.
+class ExternalEngineEmbedderTest : public JSEmbedderTest {
+ public:
+  ExternalEngineEmbedderTest();
+  ~ExternalEngineEmbedderTest() override;
+
+  // EmbedderTest:
+  void SetUp() override;
+  void TearDown() override;
+
+  CFXJS_Engine* engine() const { return m_Engine.get(); }
+  v8::Local<v8::Context> GetV8Context();
+
+ private:
+  std::unique_ptr<CFXJS_Engine> m_Engine;
+};
+
+#endif  // TESTING_EXTERNAL_ENGINE_EMBEDDER_TEST_H_
diff --git a/testing/fake_file_access.cpp b/testing/fake_file_access.cpp
index 723f772..4bd8fa1 100644
--- a/testing/fake_file_access.cpp
+++ b/testing/fake_file_access.cpp
@@ -1,16 +1,13 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "testing/fake_file_access.h"
 
-#include <algorithm>
-#include <set>
 #include <utility>
-#include <vector>
 
 #include "core/fxcrt/fx_system.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -76,13 +73,13 @@
 
 FakeFileAccess::FakeFileAccess(FPDF_FILEACCESS* file_access)
     : file_access_(file_access),
-      file_access_wrapper_(pdfium::MakeUnique<FileAccessWrapper>(this)),
-      file_avail_(pdfium::MakeUnique<FileAvailImpl>(this)),
-      download_hints_(pdfium::MakeUnique<DownloadHintsImpl>(this)) {
-  ASSERT(file_access_);
+      file_access_wrapper_(std::make_unique<FileAccessWrapper>(this)),
+      file_avail_(std::make_unique<FileAvailImpl>(this)),
+      download_hints_(std::make_unique<DownloadHintsImpl>(this)) {
+  DCHECK(file_access_);
 }
 
-FakeFileAccess::~FakeFileAccess() {}
+FakeFileAccess::~FakeFileAccess() = default;
 
 FPDF_BOOL FakeFileAccess::IsDataAvail(size_t offset, size_t size) const {
   return available_data_.Contains(RangeSet::Range(offset, offset + size));
diff --git a/testing/fake_file_access.h b/testing/fake_file_access.h
index c8c08e3..6a0f3cf 100644
--- a/testing/fake_file_access.h
+++ b/testing/fake_file_access.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/font_renamer.cpp b/testing/font_renamer.cpp
new file mode 100644
index 0000000..6a3a81c
--- /dev/null
+++ b/testing/font_renamer.cpp
@@ -0,0 +1,91 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/font_renamer.h"
+
+#include <string>
+
+#include "testing/test_fonts.h"
+
+namespace {
+
+FPDF_SYSFONTINFO* GetImpl(FPDF_SYSFONTINFO* info) {
+  return static_cast<FontRenamer*>(info)->impl();
+}
+
+void ReleaseImpl(FPDF_SYSFONTINFO* info) {
+  FPDF_SYSFONTINFO* impl = GetImpl(info);
+  impl->Release(impl);
+}
+
+void EnumFontsImpl(FPDF_SYSFONTINFO* info, void* mapper) {
+  FPDF_SYSFONTINFO* impl = GetImpl(info);
+  impl->EnumFonts(impl, mapper);
+}
+
+void* MapFontImpl(FPDF_SYSFONTINFO* info,
+                  int weight,
+                  FPDF_BOOL italic,
+                  int charset,
+                  int pitch_family,
+                  const char* face,
+                  FPDF_BOOL* exact) {
+  std::string renamed_face = TestFonts::RenameFont(face);
+  FPDF_SYSFONTINFO* impl = GetImpl(info);
+  return impl->MapFont(impl, weight, italic, charset, pitch_family,
+                       renamed_face.c_str(), exact);
+}
+
+void* GetFontImpl(FPDF_SYSFONTINFO* info, const char* face) {
+  // Any non-null return will do.
+  FPDF_SYSFONTINFO* impl = GetImpl(info);
+  std::string renamed_face = TestFonts::RenameFont(face);
+  return impl->GetFont(impl, renamed_face.c_str());
+}
+
+unsigned long GetFontDataImpl(FPDF_SYSFONTINFO* info,
+                              void* font,
+                              unsigned int table,
+                              unsigned char* buffer,
+                              unsigned long buf_size) {
+  FPDF_SYSFONTINFO* impl = GetImpl(info);
+  return impl->GetFontData(impl, font, table, buffer, buf_size);
+}
+
+unsigned long GetFaceNameImpl(FPDF_SYSFONTINFO* info,
+                              void* font,
+                              char* buffer,
+                              unsigned long buf_size) {
+  FPDF_SYSFONTINFO* impl = GetImpl(info);
+  return impl->GetFaceName(impl, font, buffer, buf_size);
+}
+
+int GetFontCharsetImpl(FPDF_SYSFONTINFO* info, void* font) {
+  FPDF_SYSFONTINFO* impl = GetImpl(info);
+  return impl->GetFontCharset(impl, font);
+}
+
+void DeleteFontImpl(FPDF_SYSFONTINFO* info, void* font) {
+  FPDF_SYSFONTINFO* impl = GetImpl(info);
+  impl->DeleteFont(impl, font);
+}
+
+}  // namespace
+
+FontRenamer::FontRenamer() : impl_(FPDF_GetDefaultSystemFontInfo()) {
+  version = 1;
+  Release = ReleaseImpl;
+  EnumFonts = EnumFontsImpl;
+  MapFont = MapFontImpl;
+  GetFont = GetFontImpl;
+  GetFontData = GetFontDataImpl;
+  GetFaceName = GetFaceNameImpl;
+  GetFontCharset = GetFontCharsetImpl;
+  DeleteFont = DeleteFontImpl;
+  FPDF_SetSystemFontInfo(this);
+}
+
+FontRenamer::~FontRenamer() {
+  FPDF_FreeDefaultSystemFontInfo(impl_.ExtractAsDangling());
+}
diff --git a/testing/font_renamer.h b/testing/font_renamer.h
new file mode 100644
index 0000000..2c32c35
--- /dev/null
+++ b/testing/font_renamer.h
@@ -0,0 +1,22 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_FONT_RENAMER_H_
+#define TESTING_FONT_RENAMER_H_
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "public/fpdf_sysfontinfo.h"
+
+class FontRenamer final : public FPDF_SYSFONTINFO {
+ public:
+  FontRenamer();
+  ~FontRenamer();
+
+  FPDF_SYSFONTINFO* impl() { return impl_; }
+
+ private:
+  UnownedPtr<FPDF_SYSFONTINFO> impl_;
+};
+
+#endif  // TESTING_FONT_RENAMER_H_
diff --git a/testing/free_deleter.h b/testing/free_deleter.h
index 58f40da..d183050 100644
--- a/testing/free_deleter.h
+++ b/testing/free_deleter.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/fuzzers/BUILD.gn b/testing/fuzzers/BUILD.gn
index 03e0610..698987f 100644
--- a/testing/fuzzers/BUILD.gn
+++ b/testing/fuzzers/BUILD.gn
@@ -1,16 +1,15 @@
-# Copyright 2016 The PDFium Authors. All rights reserved.
+# Copyright 2016 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
 import("../../pdfium.gni")
 
 config("fuzzer_config") {
-  configs = [ "../..:pdfium_core_config" ]
-
-  defines = [
-    "PNG_PREFIX",
-    "PNG_USE_READ_MACROS",
+  configs = [
+    "../..:pdfium_strict_config",
+    "../..:pdfium_noshorten_config",
   ]
+  defines = []
   include_dirs = [ "../.." ]
 }
 
@@ -43,9 +42,9 @@
       "pdf_cfx_barcode_fuzzer",
       "pdf_codec_jpeg_fuzzer",
       "pdf_css_fuzzer",
-      "pdf_fm2js_fuzzer",
       "pdf_formcalc_context_fuzzer",
       "pdf_formcalc_fuzzer",
+      "pdf_formcalc_translate_fuzzer",
       "pdfium_xfa_fuzzer",
       "pdfium_xfa_lpm_fuzzer",
     ]
@@ -68,45 +67,96 @@
 }
 if (is_clang) {
   # Fuzzers that use FuzzedDataProvider can only be built with Clang.
-  fuzzer_list += [ "pdf_nametree_fuzzer" ]
+  fuzzer_list += [
+    "pdf_cpdf_tounicodemap_fuzzer",
+    "pdf_nametree_fuzzer",
+  ]
+  if (pdf_enable_xfa) {
+    fuzzer_list += [
+      "pdf_xfa_fdp_fuzzer",
+      "pdf_xfa_raw_fuzzer",
+      "pdf_xfa_xdp_fdp_fuzzer",
+    ]
+  }
 }
 
+# Note that this only compiles all the fuzzers, to prevent compile breakages.
+# It does not link and create fuzzer executables. That is done in Chromium.
 group("fuzzers") {
   testonly = true
   deps = []
   foreach(fuzzer, fuzzer_list) {
     deps += [ ":${fuzzer}_src" ]
   }
+
+  if (is_component_build) {
+    deps += [ ":fuzzer_impls" ]
+  }
+}
+
+source_set("fuzzer_pdf_templates") {
+  sources = [ "pdf_fuzzer_templates.h" ]
 }
 
 source_set("fuzzer_init") {
   testonly = true
   sources = [ "pdf_fuzzer_init.cc" ]
   include_dirs = [ "../.." ]
-  deps = [ "../../:pdfium_public_headers" ]
+  deps = [
+    "../../:pdfium_public_headers",
+    "../../fpdfsdk",
+  ]
+}
+
+if (pdf_enable_xfa) {
+  assert(pdf_enable_v8)
+  source_set("fuzzer_xfa_process_state") {
+    testonly = !is_component_build
+    sources = [
+      "xfa_process_state.cc",
+      "xfa_process_state.h",
+    ]
+    configs += [ ":fuzzer_config" ]
+    deps = [
+      "../../fxjs:gc",
+      "//v8",
+    ]
+  }
 }
 
 source_set("fuzzer_init_public") {
   testonly = true
   sources = [ "pdf_fuzzer_init_public.cc" ]
   include_dirs = [ "../.." ]
-  deps = [ "../../:pdfium_public_headers" ]
+  deps = [
+    ":fuzzer_utils",
+    "../../:pdfium_public_headers",
+    "../../fpdfsdk",
+  ]
   if (pdf_enable_v8) {
     configs += [ "//v8:external_startup_data" ]
     deps += [
       "../:test_support",
+      "../../fxjs",
       "//v8",
       "//v8:v8_libplatform",
     ]
+    if (pdf_enable_xfa) {
+      deps += [ ":fuzzer_xfa_process_state" ]
+    }
   }
 }
 
 if (is_component_build) {
   group("fuzzer_impls") {
+    testonly = true
     deps = []
     foreach(fuzzer, fuzzer_list) {
       deps += [ ":${fuzzer}_impl" ]
     }
+    if (pdf_enable_xfa) {
+      deps += [ ":fuzzer_xfa_process_state" ]
+    }
   }
 }
 
@@ -119,6 +169,7 @@
   configs += [ ":fuzzer_config" ]
   deps = [
     "../../:pdfium_public_headers",
+    "../../fpdfsdk",
     "../../third_party:pdfium_base",
   ]
 }
@@ -137,18 +188,20 @@
 }
 
 template("pdfium_fuzzer") {
-  if (defined(invoker.public_fuzzer) && invoker.public_fuzzer) {
+  is_public = defined(invoker.public_fuzzer) && invoker.public_fuzzer
+  if (is_public) {
     init_dep = ":fuzzer_init_public"
   } else {
     init_dep = ":fuzzer_init"
   }
   if (is_component_build) {
     # In component builds, fuzzers are split into "_impl" and "_src" targets.
-    # The "_impl" target exports the fuzzer implementation and gets statically
-    # linked into the PDFium shared library.  The "_src" target is a thin
-    # wrapper that imports the fuzzer from PDFium; this gets linked into the
-    # real fuzzer executable.  In static builds, there's only a single "_src"
-    # target that contains the implementation and statically links in PDFium.
+    # The "_impl" target exports the fuzzer implementation. The "_src" target
+    # is a thin wrapper that imports the fuzzer from PDFium; this gets linked
+    # into the real fuzzer executable. The real fuzzer target has to depend on
+    # both the "_impl" and "_src" targets.
+    # In static builds, there's only a single "_src" target that contains the
+    # implementation and statically links in PDFium.
 
     impl_name = target_name + "_impl"
     template_target_name = target_name
@@ -156,7 +209,7 @@
       testonly = true
       sources = [ "component_fuzzer_template.cc" ]
       deps = [
-        "../../:pdfium",
+        "../../:pdfium_public_headers",
         init_dep,
       ]
       configs += [ ":fuzzer_config" ]
@@ -166,16 +219,14 @@
     impl_name = target_name + "_src"
   }
   source_set(impl_name) {
+    testonly = true
     sources = invoker.sources
+    defines = []
     deps = []
     if (defined(invoker.deps)) {
       deps += invoker.deps
     }
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      "//build/config/compiler:no_chromium_code",
-      ":fuzzer_config",
-    ]
+    configs += [ ":fuzzer_config" ]
     if (is_component_build) {
       # |export| should be consistent with FPDF_EXPORT In public/fpdfview.h.
       if (is_win) {
@@ -183,7 +234,7 @@
       } else {
         export = "__attribute__((visibility(\"default\")))"
       }
-      defines = [ "LLVMFuzzerTestOneInput=${export} ${template_target_name}" ]
+      defines += [ "LLVMFuzzerTestOneInput=${export} ${template_target_name}" ]
       deps += [ "../../:pdfium_public_headers" ]
     } else {
       testonly = true
@@ -192,6 +243,9 @@
         init_dep,
       ]
     }
+    if (is_public && pdf_enable_xfa) {
+      deps += [ ":fuzzer_xfa_process_state" ]
+    }
   }
 }
 
@@ -200,6 +254,7 @@
     sources = [ "pdf_cjs_util_fuzzer.cc" ]
     deps = [
       "../../core/fxcrt",
+      "../../fpdfsdk",
       "../../fxjs",
     ]
   }
@@ -207,6 +262,7 @@
     sources = [ "pdf_fx_date_helpers_fuzzer.cc" ]
     deps = [
       "../../core/fxcrt",
+      "../../fpdfsdk",
       "../../fxjs",
     ]
   }
@@ -218,7 +274,7 @@
         "../../:freetype_common",
         "../../core/fxcrt",
         "../../core/fxge",
-        "../../xfa/fgas",
+        "../../xfa/fgas/font",
         "../../xfa/fgas/layout",
         "//third_party/icu:icuuc",
       ]
@@ -227,10 +283,15 @@
     pdfium_fuzzer("pdf_cfgas_stringformatter_fuzzer") {
       sources = [ "pdf_cfgas_stringformatter_fuzzer.cc" ]
       deps = [
+        ":fuzzer_utils",
         "../../core/fxcrt",
-        "../../xfa/fgas",
+        "../../fpdfsdk",
+        "../../fxjs:gc",
+        "../../xfa/fgas/crt",
+        "../../xfa/fxfa",
         "../../xfa/fxfa/parser",
       ]
+      public_fuzzer = true
     }
 
     pdfium_fuzzer("pdf_cfx_barcode_fuzzer") {
@@ -331,12 +392,15 @@
       ]
     }
 
-    pdfium_fuzzer("pdf_fm2js_fuzzer") {
-      sources = [ "pdf_fm2js_fuzzer.cc" ]
+    pdfium_fuzzer("pdf_formcalc_translate_fuzzer") {
+      sources = [ "pdf_formcalc_translate_fuzzer.cc" ]
       deps = [
+        ":fuzzer_utils",
         "../../core/fxcrt",
+        "../../fpdfsdk",
         "../../fxjs",
       ]
+      public_fuzzer = true
     }
 
     pdfium_fuzzer("pdf_formcalc_context_fuzzer") {
@@ -356,9 +420,12 @@
     pdfium_fuzzer("pdf_formcalc_fuzzer") {
       sources = [ "pdf_formcalc_fuzzer.cc" ]
       deps = [
+        ":fuzzer_utils",
         "../../core/fxcrt",
-        "../../xfa/fxfa/fm2js",
+        "../../fxjs:gc",
+        "../../xfa/fxfa/formcalc",
       ]
+      public_fuzzer = true
     }
 
     pdfium_fuzzer("pdfium_xfa_fuzzer") {
@@ -385,6 +452,15 @@
 }
 
 if (is_clang) {
+  pdfium_fuzzer("pdf_cpdf_tounicodemap_fuzzer") {
+    sources = [ "pdf_cpdf_tounicodemap_fuzzer.cc" ]
+    deps = [
+      "../../core/fpdfapi/font",
+      "../../core/fpdfapi/parser",
+      "../../core/fxcrt",
+    ]
+  }
+
   pdfium_fuzzer("pdf_nametree_fuzzer") {
     sources = [ "pdf_nametree_fuzzer.cc" ]
     deps = [
@@ -394,6 +470,34 @@
       "../../third_party:pdfium_base",
     ]
   }
+  if (pdf_enable_xfa) {
+    pdfium_fuzzer("pdf_xfa_fdp_fuzzer") {
+      sources = [ "pdf_xfa_fdp_fuzzer.cc" ]
+      deps = [
+        ":fuzzer_helper",
+        ":fuzzer_pdf_templates",
+        "../../third_party:pdfium_base",
+      ]
+      public_fuzzer = true
+    }
+    pdfium_fuzzer("pdf_xfa_raw_fuzzer") {
+      sources = [ "pdf_xfa_raw_fuzzer.cc" ]
+      deps = [
+        ":fuzzer_helper",
+        ":fuzzer_pdf_templates",
+        "../../third_party:pdfium_base",
+      ]
+      public_fuzzer = true
+    }
+    pdfium_fuzzer("pdf_xfa_xdp_fdp_fuzzer") {
+      sources = [ "pdf_xfa_xdp_fdp_fuzzer.cc" ]
+      deps = [
+        ":fuzzer_helper",
+        ":fuzzer_pdf_templates",
+      ]
+      public_fuzzer = true
+    }
+  }
 }
 
 pdfium_fuzzer("pdf_cmap_fuzzer") {
@@ -401,6 +505,7 @@
   deps = [
     "../../:freetype_common",
     "../../core/fpdfapi/font",
+    "../../core/fxcrt",
     "../../third_party:pdfium_base",
   ]
 }
@@ -485,6 +590,7 @@
   sources = [ "pdf_scanlinecompositor_fuzzer.cc" ]
   deps = [
     ":fuzzer_utils",
+    "../../core/fxcrt",
     "../../core/fxge",
     "../../third_party:pdfium_base",
   ]
diff --git a/testing/fuzzers/component_fuzzer_template.cc b/testing/fuzzers/component_fuzzer_template.cc
index 89883f5..6f65bc1 100644
--- a/testing/fuzzers/component_fuzzer_template.cc
+++ b/testing/fuzzers/component_fuzzer_template.cc
@@ -1,9 +1,9 @@
-// Copyright 2019 The PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <cstddef>
-#include <cstdint>
+#include <stddef.h>
+#include <stdint.h>
 
 #include "public/fpdfview.h"
 
diff --git a/testing/fuzzers/pdf_bidi_fuzzer.cc b/testing/fuzzers/pdf_bidi_fuzzer.cc
index 614df52..c0ca776 100644
--- a/testing/fuzzers/pdf_bidi_fuzzer.cc
+++ b/testing/fuzzers/pdf_bidi_fuzzer.cc
@@ -1,28 +1,30 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include <cstdint>
+#include <memory>
 
+#include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/widestring.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/fx_font.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fgas/font/cfgas_fontmgr.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
-#include "xfa/fgas/layout/cfx_char.h"
-#include "xfa/fgas/layout/cfx_rtfbreak.h"
+#include "xfa/fgas/layout/cfgas_char.h"
+#include "xfa/fgas/layout/cfgas_rtfbreak.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  auto fontmgr = pdfium::MakeUnique<CFGAS_FontMgr>();
+  if (size > 8192)
+    return 0;
 
-  auto font = pdfium::MakeUnique<CFX_Font>();
-  font->LoadSubst("Arial", true, 0, FXFONT_FW_NORMAL, 0, 0, 0);
+  auto font = std::make_unique<CFX_Font>();
+  font->LoadSubst("Arial", true, 0, FXFONT_FW_NORMAL, 0, FX_CodePage::kDefANSI,
+                  0);
   assert(font);
 
-  CFX_RTFBreak rtf_break(FX_LAYOUTSTYLE_ExpandTab);
+  CFGAS_RTFBreak rtf_break(CFGAS_Break::LayoutStyle::kExpandTab);
   rtf_break.SetLineBreakTolerance(1);
-  rtf_break.SetFont(CFGAS_GEFont::LoadFont(std::move(font), fontmgr.get()));
+  rtf_break.SetFont(CFGAS_GEFont::LoadFont(std::move(font)));
   rtf_break.SetFontSize(12);
 
   WideString input =
@@ -31,8 +33,8 @@
   for (wchar_t ch : input)
     rtf_break.AppendChar(ch);
 
-  std::vector<CFX_Char> chars =
+  std::vector<CFGAS_Char> chars =
       rtf_break.GetCurrentLineForTesting()->m_LineChars;
-  CFX_Char::BidiLine(&chars, chars.size());
+  CFGAS_Char::BidiLine(&chars, chars.size());
   return 0;
 }
diff --git a/testing/fuzzers/pdf_cfgas_stringformatter_fuzzer.cc b/testing/fuzzers/pdf_cfgas_stringformatter_fuzzer.cc
index b6a6663..12f6206 100644
--- a/testing/fuzzers/pdf_cfgas_stringformatter_fuzzer.cc
+++ b/testing/fuzzers/pdf_cfgas_stringformatter_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,17 +6,25 @@
 
 #include <stdint.h>
 
-#include "core/fxcrt/fx_memory.h"
+#include <iterator>
+
+#include "core/fxcrt/cfx_datetime.h"
 #include "core/fxcrt/fx_string.h"
-#include "third_party/base/ptr_util.h"
+#include "public/fpdfview.h"
+#include "testing/fuzzers/pdfium_fuzzer_util.h"
+#include "testing/fuzzers/xfa_process_state.h"
+#include "v8/include/cppgc/heap.h"
+#include "v8/include/cppgc/persistent.h"
 #include "xfa/fxfa/parser/cxfa_localemgr.h"
 
 namespace {
 
 const wchar_t* const kLocales[] = {L"en", L"fr", L"jp", L"zh"};
-const FX_DATETIMETYPE kTypes[] = {FX_DATETIMETYPE_Date, FX_DATETIMETYPE_Time,
-                                  FX_DATETIMETYPE_DateTime,
-                                  FX_DATETIMETYPE_TimeDate};
+const CFGAS_StringFormatter::DateTimeType kTypes[] = {
+    CFGAS_StringFormatter::DateTimeType::kDate,
+    CFGAS_StringFormatter::DateTimeType::kTime,
+    CFGAS_StringFormatter::DateTimeType::kDateTime,
+    CFGAS_StringFormatter::DateTimeType::kTimeDate};
 
 }  // namespace
 
@@ -24,16 +32,12 @@
   if (size < 5 || size > 128)  // Big strings are unlikely to help.
     return 0;
 
-  // Static for speed.
-  static std::vector<std::unique_ptr<CXFA_LocaleMgr>> mgrs;
-  if (mgrs.empty()) {
-    for (const auto* locale : kLocales)
-      mgrs.push_back(pdfium::MakeUnique<CXFA_LocaleMgr>(nullptr, locale));
-  }
+  auto* state = static_cast<XFAProcessState*>(FPDF_GetFuzzerPerProcessState());
+  cppgc::Heap* heap = state->GetHeap();
 
   uint8_t test_selector = data[0] % 10;
-  uint8_t locale_selector = data[1] % FX_ArraySize(kLocales);
-  uint8_t type_selector = data[2] % FX_ArraySize(kTypes);
+  uint8_t locale_selector = data[1] % std::size(kLocales);
+  uint8_t type_selector = data[2] % std::size(kTypes);
   data += 3;
   size -= 3;
 
@@ -44,8 +48,7 @@
   WideString value =
       WideString::FromLatin1(ByteStringView(data + pattern_len, value_len));
 
-  auto fmt = pdfium::MakeUnique<CFGAS_StringFormatter>(
-      mgrs[locale_selector].get(), pattern);
+  auto fmt = std::make_unique<CFGAS_StringFormatter>(pattern);
 
   WideString result;
   CFX_DateTime dt;
@@ -53,33 +56,55 @@
     case 0:
       fmt->FormatText(value, &result);
       break;
-    case 1:
-      fmt->FormatNum(value, &result);
+    case 1: {
+      auto* mgr = cppgc::MakeGarbageCollected<CXFA_LocaleMgr>(
+          heap->GetAllocationHandle(), heap, nullptr,
+          kLocales[locale_selector]);
+      fmt->FormatNum(mgr, value, &result);
       break;
-    case 2:
-      fmt->FormatDateTime(value, kTypes[type_selector], &result);
+    }
+    case 2: {
+      auto* mgr = cppgc::MakeGarbageCollected<CXFA_LocaleMgr>(
+          heap->GetAllocationHandle(), heap, nullptr,
+          kLocales[locale_selector]);
+      fmt->FormatDateTime(mgr, value, kTypes[type_selector], &result);
       break;
-    case 3:
+    }
+    case 3: {
       fmt->FormatNull(&result);
       break;
-    case 4:
+    }
+    case 4: {
       fmt->FormatZero(&result);
       break;
-    case 5:
+    }
+    case 5: {
       fmt->ParseText(value, &result);
       break;
-    case 6:
-      fmt->ParseNum(value, &result);
+    }
+    case 6: {
+      auto* mgr = cppgc::MakeGarbageCollected<CXFA_LocaleMgr>(
+          heap->GetAllocationHandle(), heap, nullptr,
+          kLocales[locale_selector]);
+      fmt->ParseNum(mgr, value, &result);
       break;
-    case 7:
-      fmt->ParseDateTime(value, kTypes[type_selector], &dt);
+    }
+    case 7: {
+      auto* mgr = cppgc::MakeGarbageCollected<CXFA_LocaleMgr>(
+          heap->GetAllocationHandle(), heap, nullptr,
+          kLocales[locale_selector]);
+      fmt->ParseDateTime(mgr, value, kTypes[type_selector], &dt);
       break;
-    case 8:
+    }
+    case 8: {
       fmt->ParseNull(value);
       break;
-    case 9:
+    }
+    case 9: {
       fmt->ParseZero(value);
       break;
+    }
   }
+  state->ForceGCAndPump();
   return 0;
 }
diff --git a/testing/fuzzers/pdf_cfx_barcode_fuzzer.cc b/testing/fuzzers/pdf_cfx_barcode_fuzzer.cc
index 7b31b2b..35afec9 100644
--- a/testing/fuzzers/pdf_cfx_barcode_fuzzer.cc
+++ b/testing/fuzzers/pdf_cfx_barcode_fuzzer.cc
@@ -1,9 +1,7 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
-
 #include "core/fxcrt/fx_string.h"
 #include "fxbarcode/cfx_barcode.h"
 
@@ -11,7 +9,8 @@
   if (size < 2 * sizeof(uint16_t))
     return 0;
 
-  BC_TYPE type = static_cast<BC_TYPE>(data[0] % (BC_LAST + 1));
+  BC_TYPE type =
+      static_cast<BC_TYPE>(data[0] % (static_cast<int>(BC_TYPE::kLast) + 1));
 
   // Only used one byte, but align with uint16_t for string below.
   data += sizeof(uint16_t);
diff --git a/testing/fuzzers/pdf_cjs_util_fuzzer.cc b/testing/fuzzers/pdf_cjs_util_fuzzer.cc
index 5ccb65b..3885c7b 100644
--- a/testing/fuzzers/pdf_cjs_util_fuzzer.cc
+++ b/testing/fuzzers/pdf_cjs_util_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/fuzzers/pdf_cmap_fuzzer.cc b/testing/fuzzers/pdf_cmap_fuzzer.cc
index 180a6a7..58dfbe6 100644
--- a/testing/fuzzers/pdf_cmap_fuzzer.cc
+++ b/testing/fuzzers/pdf_cmap_fuzzer.cc
@@ -1,11 +1,12 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <cstdint>
+#include <stdint.h>
 
 #include "core/fpdfapi/font/cpdf_cmap.h"
-#include "third_party/base/span.h"
+#include "core/fxcrt/retain_ptr.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_a85_fuzzer.cc b/testing/fuzzers/pdf_codec_a85_fuzzer.cc
index f9ae1fe..2b1614d 100644
--- a/testing/fuzzers/pdf_codec_a85_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_a85_fuzzer.cc
@@ -1,16 +1,12 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include <cstdint>
-#include <memory>
 
 #include "core/fxcodec/basic/basicmodule.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
-  uint32_t dest_size = 0;
-  BasicModule::A85Encode({data, size}, &dest_buf, &dest_size);
+  BasicModule::A85Encode({data, size});
   return 0;
 }
diff --git a/testing/fuzzers/pdf_codec_bmp_fuzzer.cc b/testing/fuzzers/pdf_codec_bmp_fuzzer.cc
index 71f9150..1bc023a 100644
--- a/testing/fuzzers/pdf_codec_bmp_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_bmp_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/fuzzers/pdf_codec_fax_fuzzer.cc b/testing/fuzzers/pdf_codec_fax_fuzzer.cc
index d0c2984..793d18e 100644
--- a/testing/fuzzers/pdf_codec_fax_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_fax_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -38,7 +38,7 @@
 
   if (decoder) {
     int line = 0;
-    while (decoder->GetScanline(line))
+    while (!decoder->GetScanline(line).empty())
       line++;
   }
 
diff --git a/testing/fuzzers/pdf_codec_gif_fuzzer.cc b/testing/fuzzers/pdf_codec_gif_fuzzer.cc
index 69129e7..4adbad7 100644
--- a/testing/fuzzers/pdf_codec_gif_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_gif_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/fuzzers/pdf_codec_icc_fuzzer.cc b/testing/fuzzers/pdf_codec_icc_fuzzer.cc
index ca027331..ada4bc7 100644
--- a/testing/fuzzers/pdf_codec_icc_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_icc_fuzzer.cc
@@ -1,23 +1,21 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include <cstdint>
 
-#include "core/fxcodec/icc/iccmodule.h"
-#include "third_party/base/span.h"
+#include "core/fxcodec/icc/icc_transform.h"
+#include "third_party/base/containers/span.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  std::unique_ptr<CLcmsCmm> transform =
-      IccModule::CreateTransformSRGB(pdfium::make_span(data, size));
+  std::unique_ptr<fxcodec::IccTransform> transform =
+      fxcodec::IccTransform::CreateTransformSRGB(pdfium::make_span(data, size));
+  if (!transform)
+    return 0;
 
-  if (transform) {
-    float src[4];
-    float dst[4];
-    for (int i = 0; i < 4; i++)
-      src[i] = 0.5f;
-    IccModule::Translate(transform.get(), transform->components(), src, dst);
-  }
-
+  const float src[4] = {0.5f, 0.5f, 0.5f, 0.5f};
+  float dst[4];
+  transform->Translate(pdfium::make_span(src).first(transform->components()),
+                       pdfium::make_span(dst));
   return 0;
 }
diff --git a/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc b/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc
index 2878d7c..4c48b6f 100644
--- a/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc
@@ -1,17 +1,16 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <cstdint>
+#include <stdint.h>
 
 #include "core/fxcodec/jbig2/JBig2_Context.h"
 #include "core/fxcodec/jbig2/JBig2_DocumentContext.h"
-#include "core/fxcodec/jbig2/jbig2module.h"
+#include "core/fxcodec/jbig2/jbig2_decoder.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
 #include "testing/fuzzers/pdfium_fuzzer_util.h"
-#include "third_party/base/ptr_util.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   const size_t kParameterSize = 8;
@@ -32,17 +31,16 @@
     return 0;
 
   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!bitmap->Create(width, height, FXDIB_1bppRgb))
+  if (!bitmap->Create(width, height, FXDIB_Format::k1bppRgb))
     return 0;
 
-  Jbig2Module module;
+  JBig2_DocumentContext document_context;
   Jbig2Context jbig2_context;
-  std::unique_ptr<JBig2_DocumentContext> document_context;
-  FXCODEC_STATUS status = module.StartDecode(
+  FXCODEC_STATUS status = Jbig2Decoder::StartDecode(
       &jbig2_context, &document_context, width, height, {data, size}, 1, {}, 0,
-      bitmap->GetBuffer(), bitmap->GetPitch(), nullptr);
+      bitmap->GetWritableBuffer(), bitmap->GetPitch(), nullptr);
 
-  while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE)
-    status = module.ContinueDecode(&jbig2_context, nullptr);
+  while (status == FXCODEC_STATUS::kDecodeToBeContinued)
+    status = Jbig2Decoder::ContinueDecode(&jbig2_context, nullptr);
   return 0;
 }
diff --git a/testing/fuzzers/pdf_codec_jpeg_fuzzer.cc b/testing/fuzzers/pdf_codec_jpeg_fuzzer.cc
index eaa0889..a9f7216 100644
--- a/testing/fuzzers/pdf_codec_jpeg_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_jpeg_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/fuzzers/pdf_codec_png_fuzzer.cc b/testing/fuzzers/pdf_codec_png_fuzzer.cc
index 61a6574..14d9bd3 100644
--- a/testing/fuzzers/pdf_codec_png_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_png_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/fuzzers/pdf_codec_rle_fuzzer.cc b/testing/fuzzers/pdf_codec_rle_fuzzer.cc
index 7b40b01..70ce5d4 100644
--- a/testing/fuzzers/pdf_codec_rle_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_rle_fuzzer.cc
@@ -1,16 +1,12 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include <cstdint>
-#include <memory>
 
 #include "core/fxcodec/basic/basicmodule.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
-  uint32_t dest_size = 0;
-  BasicModule::RunLengthEncode({data, size}, &dest_buf, &dest_size);
+  BasicModule::RunLengthEncode({data, size});
   return 0;
 }
diff --git a/testing/fuzzers/pdf_codec_tiff_fuzzer.cc b/testing/fuzzers/pdf_codec_tiff_fuzzer.cc
index 187c311..6566b6b 100644
--- a/testing/fuzzers/pdf_codec_tiff_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_tiff_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/fuzzers/pdf_cpdf_tounicodemap_fuzzer.cc b/testing/fuzzers/pdf_cpdf_tounicodemap_fuzzer.cc
new file mode 100644
index 0000000..5f18564
--- /dev/null
+++ b/testing/fuzzers/pdf_cpdf_tounicodemap_fuzzer.cc
@@ -0,0 +1,38 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "core/fpdfapi/font/cpdf_tounicodemap.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/retain_ptr.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  static constexpr size_t kParameterSize = sizeof(uint32_t) + sizeof(wchar_t);
+  if (size <= kParameterSize)
+    return 0;
+
+  // Limit data size to prevent fuzzer timeout.
+  static constexpr size_t kMaxDataSize = 256 * 1024;
+  if (size > kParameterSize + kMaxDataSize)
+    return 0;
+
+  FuzzedDataProvider data_provider(data, size);
+  uint32_t charcode_to_lookup = data_provider.ConsumeIntegral<uint32_t>();
+  wchar_t char_for_reverse_lookup = data_provider.ConsumeIntegral<wchar_t>();
+
+  std::vector<uint8_t> remaining =
+      data_provider.ConsumeRemainingBytes<uint8_t>();
+  auto stream = pdfium::MakeRetain<CPDF_Stream>();
+  stream->SetData(remaining);
+
+  auto to_unicode_map = std::make_unique<CPDF_ToUnicodeMap>(std::move(stream));
+  to_unicode_map->Lookup(charcode_to_lookup);
+  to_unicode_map->ReverseLookup(char_for_reverse_lookup);
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_css_fuzzer.cc b/testing/fuzzers/pdf_css_fuzzer.cc
index 5f1471d..f11705c 100644
--- a/testing/fuzzers/pdf_css_fuzzer.cc
+++ b/testing/fuzzers/pdf_css_fuzzer.cc
@@ -1,9 +1,7 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
-
 #include "core/fxcrt/css/cfx_css.h"
 #include "core/fxcrt/css/cfx_csssyntaxparser.h"
 #include "core/fxcrt/fx_string.h"
@@ -16,11 +14,11 @@
   if (input.IsEmpty())
     return 0;
 
-  CFX_CSSSyntaxParser parser(input.c_str(), input.GetLength());
-  CFX_CSSSyntaxStatus status;
+  CFX_CSSSyntaxParser parser(input.AsStringView());
+  CFX_CSSSyntaxParser::Status status;
   do {
     status = parser.DoSyntaxParse();
-  } while (status != CFX_CSSSyntaxStatus::Error &&
-           status != CFX_CSSSyntaxStatus::EOS);
+  } while (status != CFX_CSSSyntaxParser::Status::kError &&
+           status != CFX_CSSSyntaxParser::Status::kEOS);
   return 0;
 }
diff --git a/testing/fuzzers/pdf_fm2js_fuzzer.cc b/testing/fuzzers/pdf_fm2js_fuzzer.cc
deleted file mode 100644
index 67daa46..0000000
--- a/testing/fuzzers/pdf_fm2js_fuzzer.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cstddef>
-#include <cstdint>
-
-#include "core/fxcrt/cfx_widetextbuf.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxcrt/fx_string.h"
-#include "fxjs/xfa/cfxjse_formcalc_context.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  FX_SAFE_SIZE_T safe_size = size;
-  if (!safe_size.IsValid())
-    return 0;
-
-  CFX_WideTextBuf js;
-  WideString input =
-      WideString::FromUTF8(ByteStringView(data, safe_size.ValueOrDie()));
-  CFXJSE_FormCalcContext::Translate(input.AsStringView(), &js);
-  return 0;
-}
diff --git a/testing/fuzzers/pdf_font_fuzzer.cc b/testing/fuzzers/pdf_font_fuzzer.cc
index 7c59630..a02052a 100644
--- a/testing/fuzzers/pdf_font_fuzzer.cc
+++ b/testing/fuzzers/pdf_font_fuzzer.cc
@@ -1,16 +1,15 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <cstring>
-#include <memory>
-
 #include "public/cpp/fpdf_scopers.h"
 #include "public/fpdf_edit.h"
 #include "public/fpdfview.h"
 
+static constexpr size_t kMaxFuzzBytes = 1024 * 1024 * 1024;  // 1 GB.
+
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  if (size < 2)
+  if (size < 2 || size > kMaxFuzzBytes)
     return 0;
 
   ScopedFPDFDocument doc(FPDF_CreateNewDocument());
@@ -19,7 +18,8 @@
   FPDF_BOOL cid = data[1];
   data += 2;
   size -= 2;
-  ScopedFPDFFont font(FPDFText_LoadFont(doc.get(), data, size, font_type, cid));
+  ScopedFPDFFont font(FPDFText_LoadFont(
+      doc.get(), data, static_cast<uint32_t>(size), font_type, cid));
   if (!font)
     return 0;
 
diff --git a/testing/fuzzers/pdf_formcalc_context_fuzzer.cc b/testing/fuzzers/pdf_formcalc_context_fuzzer.cc
index e2d73a8..638d086 100644
--- a/testing/fuzzers/pdf_formcalc_context_fuzzer.cc
+++ b/testing/fuzzers/pdf_formcalc_context_fuzzer.cc
@@ -1,9 +1,11 @@
-// Copyright 2019 The PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include <stdint.h>
 
+#include <memory>
+
 #include "core/fxcrt/fx_string.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
@@ -11,7 +13,6 @@
 #include "fxjs/xfa/cfxjse_value.h"
 #include "public/fpdf_formfill.h"
 #include "testing/fuzzers/pdfium_fuzzer_helper.h"
-#include "v8/include/v8.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 
 namespace {
@@ -522,15 +523,14 @@
 
     CXFA_EventParam params;
     params.m_bCancelAction = false;
-    script_context->SetEventParam(&params);
+    CFXJSE_Engine::EventParamScope param_scope(script_context, nullptr,
+                                               &params);
     ByteStringView data_view(data_, size_);
 
-    auto value = pdfium::MakeUnique<CFXJSE_Value>(script_context->GetIsolate());
+    auto value = std::make_unique<CFXJSE_Value>();
     script_context->RunScript(CXFA_Script::Type::Formcalc,
                               WideString::FromUTF8(data_view).AsStringView(),
                               value.get(), xfa_document->GetRoot());
-
-    script_context->SetEventParam(nullptr);
   }
 
  private:
diff --git a/testing/fuzzers/pdf_formcalc_fuzzer.cc b/testing/fuzzers/pdf_formcalc_fuzzer.cc
index 08e22bb..d8a2a89 100644
--- a/testing/fuzzers/pdf_formcalc_fuzzer.cc
+++ b/testing/fuzzers/pdf_formcalc_fuzzer.cc
@@ -1,16 +1,18 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "core/fxcrt/cfx_widetextbuf.h"
 #include "core/fxcrt/fx_string.h"
-#include "xfa/fxfa/fm2js/cxfa_fmparser.h"
+#include "testing/fuzzers/pdfium_fuzzer_util.h"
+#include "testing/fuzzers/xfa_process_state.h"
+#include "xfa/fxfa/formcalc/cxfa_fmparser.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  auto* state = static_cast<XFAProcessState*>(FPDF_GetFuzzerPerProcessState());
   WideString input = WideString::FromUTF8(ByteStringView(data, size));
-
-  CXFA_FMParser parser(input.AsStringView());
+  CXFA_FMLexer lexer(input.AsStringView());
+  CXFA_FMParser parser(state->GetHeap(), &lexer);
   parser.Parse();
-
+  state->ForceGCAndPump();
   return 0;
 }
diff --git a/testing/fuzzers/pdf_formcalc_translate_fuzzer.cc b/testing/fuzzers/pdf_formcalc_translate_fuzzer.cc
new file mode 100644
index 0000000..82b8188
--- /dev/null
+++ b/testing/fuzzers/pdf_formcalc_translate_fuzzer.cc
@@ -0,0 +1,20 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_string.h"
+#include "fxjs/xfa/cfxjse_formcalc_context.h"
+#include "testing/fuzzers/pdfium_fuzzer_util.h"
+#include "testing/fuzzers/xfa_process_state.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  auto* state = static_cast<XFAProcessState*>(FPDF_GetFuzzerPerProcessState());
+  WideString input = WideString::FromUTF8(ByteStringView(data, size));
+  CFXJSE_FormCalcContext::Translate(state->GetHeap(), input.AsStringView());
+  state->ForceGCAndPump();
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_fuzzer_init.cc b/testing/fuzzers/pdf_fuzzer_init.cc
index 954eed0..9a75dd2 100644
--- a/testing/fuzzers/pdf_fuzzer_init.cc
+++ b/testing/fuzzers/pdf_fuzzer_init.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/fuzzers/pdf_fuzzer_init_public.cc b/testing/fuzzers/pdf_fuzzer_init_public.cc
index 5ece0bc..993c8a7 100644
--- a/testing/fuzzers/pdf_fuzzer_init_public.cc
+++ b/testing/fuzzers/pdf_fuzzer_init_public.cc
@@ -1,24 +1,33 @@
-// Copyright 2019 The PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <string.h>
+#include "testing/fuzzers/pdf_fuzzer_init_public.h"
 
-#include <memory>
+#include <string.h>  // For memset()
 
-#include "public/fpdf_ext.h"
+#include <string>
+
+#include "testing/fuzzers/pdfium_fuzzer_util.h"
 
 #ifdef PDF_ENABLE_V8
 #include "testing/free_deleter.h"
 #include "testing/v8_initializer.h"
 #include "v8/include/v8-platform.h"
-#include "v8/include/v8.h"
-#endif
+#ifdef PDF_ENABLE_XFA
+#include "testing/fuzzers/xfa_process_state.h"
+#include "v8/include/v8-array-buffer.h"
+#include "v8/include/v8-isolate.h"
+#endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
 
 #ifdef _WIN32
 #include <windows.h>
 #elif defined(__APPLE__)
 #include <mach-o/dyld.h>
+#elif defined(__Fuchsia__)
+#include <limits.h>
+#include <unistd.h>
 #else  // Linux
 #include <linux/limits.h>
 #include <unistd.h>
@@ -26,10 +35,14 @@
 
 namespace {
 
+// pdf_fuzzer_init.cc and pdf_fuzzer_init_public.cc are mutually exclusive
+// 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() {
   std::string result;
-
 #ifdef _WIN32
   char path[MAX_PATH];
   DWORD len = GetModuleFileNameA(nullptr, path, MAX_PATH);
@@ -56,41 +69,45 @@
 
 }  // namespace
 
-// Initialize the library once for all runs of the fuzzer.
-struct TestCase {
-  TestCase() {
+PDFFuzzerInitPublic::PDFFuzzerInitPublic() {
 #ifdef PDF_ENABLE_V8
 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
-    platform = InitializeV8ForPDFiumWithStartupData(
-        ProgramPath(), std::string(), &snapshot_blob);
-#else
-    platform = InitializeV8ForPDFium(ProgramPath());
+  platform_ = InitializeV8ForPDFiumWithStartupData(
+      ProgramPath(), std::string(), std::string(), &snapshot_blob_);
+#else   // V8_USE_EXTERNAL_STARTUP_DATA
+  platform_ = InitializeV8ForPDFium(ProgramPath(), std::string());
 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
+#ifdef PDF_ENABLE_XFA
+  allocator_.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator());
+  v8::Isolate::CreateParams create_params;
+  create_params.array_buffer_allocator = allocator_.get();
+  isolate_.reset(v8::Isolate::New(create_params));
+#endif  // PDF_ENABLE_XFA
 #endif  // PDF_ENABLE_V8
-
-    memset(&config, '\0', sizeof(config));
-    config.version = 2;
-    config.m_pUserFontPaths = nullptr;
-    config.m_pIsolate = nullptr;
-    config.m_v8EmbedderSlot = 0;
-    FPDF_InitLibraryWithConfig(&config);
-
-    memset(&unsupport_info, '\0', sizeof(unsupport_info));
-    unsupport_info.version = 1;
-    unsupport_info.FSDK_UnSupport_Handler = [](UNSUPPORT_INFO*, int) {};
-    FSDK_SetUnSpObjProcessHandler(&unsupport_info);
-  }
-
+  memset(&config_, '\0', sizeof(config_));
+  config_.version = 3;
+  config_.m_pUserFontPaths = nullptr;
+  config_.m_pPlatform = nullptr;
+  config_.m_pIsolate = nullptr;
+  config_.m_v8EmbedderSlot = 0;
 #ifdef PDF_ENABLE_V8
-  std::unique_ptr<v8::Platform> platform;
-  v8::StartupData snapshot_blob;
+  config_.m_pPlatform = platform_.get();
+  config_.m_pIsolate = isolate_.get();
+#endif  // PDF_ENABLE_V8
+  FPDF_InitLibraryWithConfig(&config_);
+
+  memset(&unsupport_info_, '\0', sizeof(unsupport_info_));
+  unsupport_info_.version = 1;
+  unsupport_info_.FSDK_UnSupport_Handler = [](UNSUPPORT_INFO*, int) {};
+  FSDK_SetUnSpObjProcessHandler(&unsupport_info_);
+
+#ifdef PDF_ENABLE_XFA
+  xfa_process_state_ =
+      std::make_unique<XFAProcessState>(platform_.get(), isolate_.get());
+  FPDF_SetFuzzerPerProcessState(xfa_process_state_.get());
 #endif
+}
 
-  FPDF_LIBRARY_CONFIG config;
-  UNSUPPORT_INFO unsupport_info;
-};
-
-// pdf_fuzzer_init.cc and pdfium_fuzzer_init_public.cc are mutually exclusive
-// and should not be built together. They deliberately have the same global
-// variable.
-static TestCase* g_test_case = new TestCase();
+PDFFuzzerInitPublic::~PDFFuzzerInitPublic() {
+  FPDF_SetFuzzerPerProcessState(nullptr);
+}
diff --git a/testing/fuzzers/pdf_fuzzer_init_public.h b/testing/fuzzers/pdf_fuzzer_init_public.h
new file mode 100644
index 0000000..4b7f46f
--- /dev/null
+++ b/testing/fuzzers/pdf_fuzzer_init_public.h
@@ -0,0 +1,50 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_FUZZERS_PDF_FUZZER_INIT_PUBLIC_H_
+#define TESTING_FUZZERS_PDF_FUZZER_INIT_PUBLIC_H_
+
+#include <memory>
+
+#include "public/fpdf_ext.h"
+#include "public/fpdfview.h"
+
+#ifdef PDF_ENABLE_V8
+#include "fxjs/cfx_v8.h"
+#include "v8/include/v8-array-buffer.h"
+#include "v8/include/v8-snapshot.h"
+#endif  // PDF_ENABLE_V8
+
+class XFAProcessState;
+
+#ifdef PDF_ENABLE_V8
+namespace v8 {
+class Isolate;
+class Platform;
+}  // namespace v8
+#endif  // PDF_ENABLE_V8
+
+// Initializes the library once for all runs of the fuzzer.
+class PDFFuzzerInitPublic {
+ public:
+  PDFFuzzerInitPublic();
+  ~PDFFuzzerInitPublic();
+
+ private:
+  FPDF_LIBRARY_CONFIG config_;
+  UNSUPPORT_INFO unsupport_info_;
+#ifdef PDF_ENABLE_V8
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+  v8::StartupData snapshot_blob_;
+#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+  std::unique_ptr<v8::Platform> platform_;
+  std::unique_ptr<v8::ArrayBuffer::Allocator> allocator_;
+  std::unique_ptr<v8::Isolate, CFX_V8IsolateDeleter> isolate_;
+#ifdef PDF_ENABLE_XFA
+  std::unique_ptr<XFAProcessState> xfa_process_state_;
+#endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
+};
+
+#endif  // TESTING_FUZZERS_PDF_FUZZER_INIT_PUBLIC_H_
diff --git a/testing/fuzzers/pdf_fuzzer_templates.h b/testing/fuzzers/pdf_fuzzer_templates.h
new file mode 100644
index 0000000..3fe4ea8
--- /dev/null
+++ b/testing/fuzzers/pdf_fuzzer_templates.h
@@ -0,0 +1,52 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// File for holding strings representing PDF templates that are used by fuzzers.
+
+#ifndef TESTING_FUZZERS_PDF_FUZZER_TEMPLATES_H_
+#define TESTING_FUZZERS_PDF_FUZZER_TEMPLATES_H_
+
+constexpr char kSimplePdfTemplate[] = R"(%PDF-1.7
+1 0 obj
+<</Type /Catalog /Pages 2 0 R /AcroForm <</XFA 30 0 R>> /NeedsRendering true>>
+endobj
+2 0 obj
+<</Type /Pages /Kids [3 0 R] /Count 1>>
+endobj
+3 0 obj
+<</Type /Page /Parent 2 0 R /MediaBox [0 0 3 3]>>
+endobj
+30 0 obj
+<</Length $1>>
+stream
+$2
+endstream
+endobj
+trailer
+<</Root 1 0 R /Size 31>>
+%%EOF)";
+
+// We define the bytes of the header explicitly to make the values more readable
+constexpr uint8_t kSimplePdfHeader[] = {0x25, 0x50, 0x44, 0x46, 0x2d,
+                                        0x31, 0x2e, 0x37, 0x0a, 0x25,
+                                        0xa0, 0xf2, 0xa4, 0xf4, 0x0a};
+
+constexpr char kCatalog[] = R""(<</AcroForm 2 0 R /Extensions
+  <</ADBE <</BaseVersion /1.7 /ExtensionLevel 8>>>> /NeedsRendering true
+  /Pages 3 0 R /Type /Catalog>>)"";
+
+constexpr char kSimpleXfaObjWrapper[] = R""(<</XFA
+  [(preamble) 5 0 R ($1) 6 0 R ($2) 7 0 R ($3) 8 0 R
+  (postamble) 9 0 R]>>)"";
+
+constexpr char kSimplePagesObj[] = "<</Count 1 /Kids [4 0 R] /Type /Pages>>";
+constexpr char kSimplePageObj[] =
+    "<</MediaBox [0 0 612 792] /Parent 3 0 R /Type /Page>>";
+constexpr char kSimplePreamble[] =
+    R""(<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" 
+timeStamp="2021-12-14T14:14:14Z" 
+uuid="11111111-1ab1-11b1-aa1a-1aaaaaaa11a1">)"";
+constexpr char kSimplePostamble[] = "</xdp:xdp>";
+
+#endif  // TESTING_FUZZERS_PDF_FUZZER_TEMPLATES_H_
diff --git a/testing/fuzzers/pdf_fx_date_helpers_fuzzer.cc b/testing/fuzzers/pdf_fx_date_helpers_fuzzer.cc
index e31decc..d98fffd 100644
--- a/testing/fuzzers/pdf_fx_date_helpers_fuzzer.cc
+++ b/testing/fuzzers/pdf_fx_date_helpers_fuzzer.cc
@@ -1,9 +1,7 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <string>
-
 #include "core/fxcrt/widestring.h"
 #include "fxjs/fx_date_helpers.h"
 
diff --git a/testing/fuzzers/pdf_hint_table_fuzzer.cc b/testing/fuzzers/pdf_hint_table_fuzzer.cc
index 1540074..1351a04 100644
--- a/testing/fuzzers/pdf_hint_table_fuzzer.cc
+++ b/testing/fuzzers/pdf_hint_table_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +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/ptr_util.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;
@@ -28,7 +27,7 @@
                       int shared_hint_table_offset)
       : CPDF_HintTables(nullptr, pLinearized),
         shared_hint_table_offset_(shared_hint_table_offset) {}
-  ~HintTableForFuzzing() {}
+  ~HintTableForFuzzing() = default;
 
   void Fuzz(const uint8_t* data, size_t size) {
     if (shared_hint_table_offset_ <= 0)
@@ -76,9 +75,9 @@
 
   auto hint_info = pdfium::MakeRetain<CPDF_Array>();
   // Add primary hint stream offset
-  hint_info->AddNew<CPDF_Number>(GetData(&data32, &data, &size));
+  hint_info->AppendNew<CPDF_Number>(GetData(&data32, &data, &size));
   // Add primary hint stream size
-  hint_info->AddNew<CPDF_Number>(GetData(&data32, &data, &size));
+  hint_info->AppendNew<CPDF_Number>(GetData(&data32, &data, &size));
   // Set hint stream info.
   linearized_dict->SetFor("H", std::move(hint_info));
 
diff --git a/testing/fuzzers/pdf_jpx_fuzzer.cc b/testing/fuzzers/pdf_jpx_fuzzer.cc
index 3986ae2..c7e7e2f 100644
--- a/testing/fuzzers/pdf_jpx_fuzzer.cc
+++ b/testing/fuzzers/pdf_jpx_fuzzer.cc
@@ -1,17 +1,15 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include <cstdint>
 #include <memory>
-#include <vector>
 
 #include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fxcodec/jpx/cjpx_decoder.h"
-#include "core/fxcodec/jpx/jpxmodule.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
 
 namespace {
 
@@ -21,19 +19,19 @@
   static constexpr uint32_t kMemLimitBytes = 1024 * 1024 * 1024;  // 1 GB.
   FX_SAFE_UINT32 mem = image_info.width;
   mem *= image_info.height;
-  mem *= image_info.components;
+  mem *= image_info.channels;
   return mem.IsValid() && mem.ValueOrDie() <= kMemLimitBytes;
 }
 
 }  // namespace
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  if (size < 1)
+  if (size < 2)
     return 0;
 
-  std::unique_ptr<CJPX_Decoder> decoder = JpxModule::CreateDecoder(
-      {data + 1, size - 1},
-      static_cast<CJPX_Decoder::ColorSpaceOption>(data[0] % 3));
+  std::unique_ptr<CJPX_Decoder> decoder = CJPX_Decoder::Create(
+      {data + 2, size - 2},
+      static_cast<CJPX_Decoder::ColorSpaceOption>(data[0] % 3), data[1]);
   if (!decoder)
     return 0;
 
@@ -52,15 +50,15 @@
     return 0;
 
   FXDIB_Format format;
-  if (image_info.components == 1) {
-    format = FXDIB_8bppRgb;
-  } else if (image_info.components <= 3) {
-    format = FXDIB_Rgb;
-  } else if (image_info.components == 4) {
-    format = FXDIB_Rgb32;
+  if (image_info.channels == 1) {
+    format = FXDIB_Format::k8bppRgb;
+  } else if (image_info.channels <= 3) {
+    format = FXDIB_Format::kRgb;
+  } else if (image_info.channels == 4) {
+    format = FXDIB_Format::kRgb32;
   } else {
-    image_info.width = (image_info.width * image_info.components + 2) / 3;
-    format = FXDIB_Rgb;
+    image_info.width = (image_info.width * image_info.channels + 2) / 3;
+    format = FXDIB_Format::kRgb;
   }
   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
   if (!bitmap->Create(image_info.width, image_info.height, format))
@@ -71,8 +69,8 @@
           static_cast<uint32_t>(bitmap->GetHeight()))
     return 0;
 
-  decoder->Decode(bitmap->GetBuffer(), bitmap->GetPitch(),
-                  /*swap_rgb=*/false);
+  decoder->Decode(bitmap->GetWritableBuffer(), bitmap->GetPitch(),
+                  /*swap_rgb=*/false, GetCompsFromFormat(format));
 
   return 0;
 }
diff --git a/testing/fuzzers/pdf_lzw_fuzzer.cc b/testing/fuzzers/pdf_lzw_fuzzer.cc
index e4d993e..38d7929 100644
--- a/testing/fuzzers/pdf_lzw_fuzzer.cc
+++ b/testing/fuzzers/pdf_lzw_fuzzer.cc
@@ -1,10 +1,13 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <vector>
 
-#include "core/fxcodec/gif/cfx_lzwdecompressor.h"
+#include "core/fxcodec/gif/lzw_decompressor.h"
 #include "third_party/base/numerics/safe_conversions.h"
 
 // Between 2x and 5x is a standard range for LZW according to a quick
@@ -12,12 +15,14 @@
 constexpr uint32_t kMinCompressionRatio = 2;
 constexpr uint32_t kMaxCompressionRatio = 10;
 
+static constexpr size_t kMaxFuzzBytes = 1024 * 1024 * 1024;  // 1 GB.
+
 void LZWFuzz(const uint8_t* src_buf,
-             size_t src_size,
+             uint32_t src_size,
              uint8_t color_exp,
              uint8_t code_exp) {
-  std::unique_ptr<CFX_LZWDecompressor> decompressor =
-      CFX_LZWDecompressor::Create(color_exp, code_exp);
+  std::unique_ptr<LZWDecompressor> decompressor =
+      LZWDecompressor::Create(color_exp, code_exp);
   if (!decompressor)
     return;
 
@@ -27,8 +32,9 @@
     // This cast should be safe since the caller is checking for overflow on
     // the initial data.
     uint32_t dest_size = static_cast<uint32_t>(dest_buf.size());
-    if (CFX_GifDecodeStatus::InsufficientDestSize !=
-        decompressor->Decode(src_buf, src_size, dest_buf.data(), &dest_size)) {
+    decompressor->SetSource(src_buf, src_size);
+    if (LZWDecompressor::Status::kInsufficientDestSize !=
+        decompressor->Decode(dest_buf.data(), &dest_size)) {
       return;
     }
   }
@@ -36,7 +42,7 @@
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   // Need at least 3 bytes to do anything.
-  if (size < 3)
+  if (size < 3 || size > kMaxFuzzBytes)
     return 0;
 
   // Normally the GIF would provide the code and color sizes, instead, going
diff --git a/testing/fuzzers/pdf_nametree_fuzzer.cc b/testing/fuzzers/pdf_nametree_fuzzer.cc
index 9a37024..43c10c7 100644
--- a/testing/fuzzers/pdf_nametree_fuzzer.cc
+++ b/testing/fuzzers/pdf_nametree_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -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;
@@ -54,22 +54,23 @@
 
   CPDF_StreamParser parser(remaining);
   auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
-  CPDF_NameTree name_tree(dict.Get());
+  std::unique_ptr<CPDF_NameTree> name_tree =
+      CPDF_NameTree::CreateForTesting(dict.Get());
   for (const auto& name : params.names) {
     RetainPtr<CPDF_Object> obj = parser.ReadNextObject(
         /*bAllowNestedArray*/ true, /*bInArray=*/false, /*dwRecursionLevel=*/0);
     if (!obj)
       break;
 
-    name_tree.AddValueAndName(std::move(obj), name);
+    name_tree->AddValueAndName(std::move(obj), name);
   }
 
   if (params.delete_backwards) {
     for (size_t i = params.count; i > 0; --i)
-      name_tree.DeleteValueAndName(i);
+      name_tree->DeleteValueAndName(i);
   } else {
     for (size_t i = 0; i < params.count; ++i)
-      name_tree.DeleteValueAndName(0);
+      name_tree->DeleteValueAndName(0);
   }
   return 0;
 }
diff --git a/testing/fuzzers/pdf_psengine_fuzzer.cc b/testing/fuzzers/pdf_psengine_fuzzer.cc
index d72088d..f24bdce 100644
--- a/testing/fuzzers/pdf_psengine_fuzzer.cc
+++ b/testing/fuzzers/pdf_psengine_fuzzer.cc
@@ -1,11 +1,11 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <cstdint>
+#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_scanlinecompositor_fuzzer.cc b/testing/fuzzers/pdf_scanlinecompositor_fuzzer.cc
index 4b20068..9c6e434 100644
--- a/testing/fuzzers/pdf_scanlinecompositor_fuzzer.cc
+++ b/testing/fuzzers/pdf_scanlinecompositor_fuzzer.cc
@@ -1,20 +1,36 @@
-// Copyright 2019 The PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <iterator>
+#include <memory>
+
+#include "core/fxcrt/fx_safe_types.h"
 #include "core/fxge/cfx_cliprgn.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
 #include "testing/fuzzers/pdfium_fuzzer_util.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
+// Some unused formats were removed, and their slots have been filled in
+// `FXDIB_Format::kInvalid` to keep the fuzzer input stable.
 constexpr FXDIB_Format kFormat[] = {
-    FXDIB_Invalid, FXDIB_1bppRgb,   FXDIB_8bppRgb,  FXDIB_Rgb,
-    FXDIB_Rgb32,   FXDIB_1bppMask,  FXDIB_8bppMask, FXDIB_8bppRgba,
-    FXDIB_Rgba,    FXDIB_Argb,      FXDIB_1bppCmyk, FXDIB_8bppCmyk,
-    FXDIB_Cmyk,    FXDIB_8bppCmyka, FXDIB_Cmyka};
+    FXDIB_Format::kInvalid,
+    FXDIB_Format::k1bppRgb,
+    FXDIB_Format::k8bppRgb,
+    FXDIB_Format::kRgb,
+    FXDIB_Format::kRgb32,
+    FXDIB_Format::k1bppMask,
+    FXDIB_Format::k8bppMask,
+    FXDIB_Format::kInvalid /* Was FXDIB_Format::k8bppRgba */,
+    FXDIB_Format::kInvalid /* Was FXDIB_Format::kRgba */,
+    FXDIB_Format::kArgb,
+    FXDIB_Format::kInvalid /* Was FXDIB_Format::k1bppCmyk */,
+    FXDIB_Format::kInvalid /* Was FXDIB_Format::k8bppCmyk */,
+    FXDIB_Format::kInvalid /* Was FXDIB_Format::kCmyk */,
+    FXDIB_Format::kInvalid /* Was FXDIB_Format::k8bppCmyka */,
+    FXDIB_Format::kInvalid /* Was FXDIB_Format::kCmyka */};
 
 }  // namespace
 
@@ -33,27 +49,35 @@
 
   BlendMode blend_mode = static_cast<BlendMode>(
       data[28] % (static_cast<int>(BlendMode::kLast) + 1));
-  FXDIB_Format dest_format = kFormat[data[29] % FX_ArraySize(kFormat)];
-  FXDIB_Format src_format = kFormat[data[30] % FX_ArraySize(kFormat)];
+  FXDIB_Format dest_format = kFormat[data[29] % std::size(kFormat)];
+  FXDIB_Format src_format = kFormat[data[30] % std::size(kFormat)];
   bool is_clip = !(data[31] % 2);
   bool is_rgb_byte_order = !(data[32] % 2);
   size -= kParameterSize;
   data += kParameterSize;
 
+  static constexpr uint32_t kMemLimit = 512000000;  // 512 MB
+  static constexpr uint32_t kComponents = 4;
+  FX_SAFE_UINT32 mem = width;
+  mem *= height;
+  mem *= kComponents;
+  if (!mem.IsValid() || mem.ValueOrDie() > kMemLimit)
+    return 0;
+
   auto src_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
   auto dest_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
   if (!src_bitmap->Create(width, height, src_format) ||
       !dest_bitmap->Create(width, height, dest_format)) {
     return 0;
   }
-  if (!src_bitmap->GetBuffer() || !dest_bitmap->GetBuffer()) {
+  if (src_bitmap->GetBuffer().empty() || dest_bitmap->GetBuffer().empty()) {
     return 0;
   }
 
   std::unique_ptr<CFX_ClipRgn> clip_rgn;
   if (is_clip)
-    clip_rgn = pdfium::MakeUnique<CFX_ClipRgn>(width, height);
-  if (src_bitmap->IsAlphaMask()) {
+    clip_rgn = std::make_unique<CFX_ClipRgn>(width, height);
+  if (src_bitmap->IsMaskFormat()) {
     dest_bitmap->CompositeMask(dest_left, dest_top, width, height, src_bitmap,
                                argb, src_left, src_top, blend_mode,
                                clip_rgn.get(), is_rgb_byte_order);
diff --git a/testing/fuzzers/pdf_streamparser_fuzzer.cc b/testing/fuzzers/pdf_streamparser_fuzzer.cc
index 2bbda5e..6921cc9 100644
--- a/testing/fuzzers/pdf_streamparser_fuzzer.cc
+++ b/testing/fuzzers/pdf_streamparser_fuzzer.cc
@@ -1,13 +1,12 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <cstdint>
-#include <memory>
+#include <stdint.h>
 
 #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
new file mode 100644
index 0000000..cefd09d
--- /dev/null
+++ b/testing/fuzzers/pdf_xfa_fdp_fuzzer.cc
@@ -0,0 +1,911 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <string>
+#include <vector>
+
+#include "public/fpdf_formfill.h"
+#include "testing/fuzzers/pdf_fuzzer_templates.h"
+#include "testing/fuzzers/pdfium_fuzzer_helper.h"
+#include "third_party/base/containers/adapters.h"
+
+class PDFiumXFAFuzzer : public PDFiumFuzzerHelper {
+ public:
+  PDFiumXFAFuzzer() = default;
+  ~PDFiumXFAFuzzer() override = default;
+
+  int GetFormCallbackVersion() const override { return 2; }
+
+  void SetFdp(FuzzedDataProvider* fdp) { fdp_ = fdp; }
+
+  // Return false if XFA doesn't load as otherwise we're duplicating the work
+  // done by the non-xfa fuzzer.
+  bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc) override {
+    int form_type = FPDF_GetFormType(doc);
+    if (form_type != FORMTYPE_XFA_FULL && form_type != FORMTYPE_XFA_FOREGROUND)
+      return false;
+    return FPDF_LoadXFA(doc);
+  }
+
+  void FormActionHandler(FPDF_FORMHANDLE form,
+                         FPDF_DOCUMENT doc,
+                         FPDF_PAGE page) override {
+    if (!fdp_) {
+      return;
+    }
+    char local_buf[50];
+    int number_of_calls = fdp_->ConsumeIntegralInRange<int>(0, 250);
+    for (int i = 0; i < number_of_calls; i++) {
+      UserInteraction selector = fdp_->ConsumeEnum<UserInteraction>();
+      switch (selector) {
+        case kOnLButtonUp: {
+          FORM_OnLButtonUp(form, page, fdp_->ConsumeIntegral<int>(),
+                           fdp_->ConsumeIntegralInRange<int>(-100, 1000),
+                           fdp_->ConsumeIntegralInRange<int>(-100, 1000));
+          break;
+        }
+        case kOnRButtonUp: {
+          FORM_OnRButtonUp(form, page, fdp_->ConsumeIntegral<int>(),
+                           fdp_->ConsumeIntegralInRange<int>(-100, 1000),
+                           fdp_->ConsumeIntegralInRange<int>(-100, 1000));
+          break;
+        }
+        case kOnLButtonDown: {
+          FORM_OnLButtonDown(form, page, fdp_->ConsumeIntegral<int>(),
+                             fdp_->ConsumeIntegralInRange<int>(-100, 1000),
+                             fdp_->ConsumeIntegralInRange<int>(-100, 1000));
+          break;
+        }
+        case kOnRButtonDown: {
+          FORM_OnRButtonDown(form, page, fdp_->ConsumeIntegral<int>(),
+                             fdp_->ConsumeIntegralInRange<int>(-100, 1000),
+                             fdp_->ConsumeIntegralInRange<int>(-100, 1000));
+          break;
+        }
+        case kOnChar: {
+          FORM_OnChar(form, page, fdp_->ConsumeIntegral<int>(),
+                      fdp_->ConsumeIntegral<int>());
+          break;
+        }
+        case kOnKeyDown: {
+          FORM_OnKeyDown(form, page, fdp_->ConsumeIntegral<int>(),
+                         fdp_->ConsumeIntegral<int>());
+          break;
+        }
+        case kOnKeyUp: {
+          FORM_OnKeyUp(form, page, fdp_->ConsumeIntegral<int>(),
+                       fdp_->ConsumeIntegral<int>());
+          break;
+        }
+        case kOnLButtonDoubleClick: {
+          FORM_OnLButtonDoubleClick(form, page, fdp_->ConsumeIntegral<int>(),
+                                    fdp_->ConsumeIntegral<int>(),
+                                    fdp_->ConsumeIntegral<int>());
+          break;
+        }
+        case kOnMouseMove: {
+          FORM_OnMouseMove(form, page, fdp_->ConsumeIntegral<int>(),
+                           fdp_->ConsumeIntegral<int>(),
+                           fdp_->ConsumeIntegral<int>());
+          break;
+        }
+        case kOnMouseWheel: {
+          const FS_POINTF point = {fdp_->ConsumeFloatingPoint<float>(),
+                                   fdp_->ConsumeFloatingPoint<float>()};
+          FORM_OnMouseWheel(form, page, fdp_->ConsumeIntegral<int>(), &point,
+                            fdp_->ConsumeIntegral<int>(),
+                            fdp_->ConsumeIntegral<int>());
+          break;
+        }
+        case kOnFocus: {
+          FORM_OnFocus(form, page, fdp_->ConsumeIntegral<int>(),
+                       fdp_->ConsumeIntegral<int>(),
+                       fdp_->ConsumeIntegral<int>());
+          break;
+        }
+        case kUndo: {
+          if (FORM_CanUndo(form, page)) {
+            FORM_Undo(form, page);
+          }
+          break;
+        }
+        case kSelectAllText: {
+          FORM_SelectAllText(form, page);
+          break;
+        }
+        case kRedo: {
+          if (FORM_CanRedo(form, page)) {
+            FORM_Redo(form, page);
+          }
+          break;
+        }
+        case kAnnot: {
+          FPDF_ANNOTATION annot = nullptr;
+          int page_index = -2;
+          FORM_GetFocusedAnnot(form, &page_index, &annot);
+          if (annot) {
+            FORM_SetFocusedAnnot(form, annot);
+          }
+          break;
+        }
+        case kSetIndexSelected: {
+          FORM_SetIndexSelected(form, page, fdp_->ConsumeIntegral<int>(),
+                                fdp_->ConsumeBool());
+          break;
+        }
+        case kIsIndexSelected: {
+          FORM_IsIndexSelected(form, page, fdp_->ConsumeIntegral<int>());
+          break;
+        }
+        case kHasFormFieldAtPoint: {
+          FPDFPage_HasFormFieldAtPoint(form, page, fdp_->ConsumeIntegral<int>(),
+                                       fdp_->ConsumeIntegral<int>());
+          break;
+        }
+        case kFormFieldZOrderAtPoint: {
+          FPDFPage_FormFieldZOrderAtPoint(form, page,
+                                          fdp_->ConsumeIntegral<int>(),
+                                          fdp_->ConsumeIntegral<int>());
+          break;
+        }
+        case kGetSelectedText: {
+          FORM_GetSelectedText(form, page, local_buf, sizeof(local_buf));
+          break;
+        }
+        case kGetFocusedText: {
+          FORM_GetFocusedText(form, page, local_buf, sizeof(local_buf));
+          break;
+        }
+      }
+    }
+  }
+
+ private:
+  enum UserInteraction {
+    kOnLButtonUp = 0,
+    kOnRButtonUp,
+    kOnLButtonDown,
+    kOnRButtonDown,
+    kOnChar,
+    kOnKeyDown,
+    kOnKeyUp,
+    kOnLButtonDoubleClick,
+    kOnMouseMove,
+    kOnMouseWheel,
+    kOnFocus,
+    kUndo,
+    kSelectAllText,
+    kRedo,
+    kAnnot,
+    kSetIndexSelected,
+    kIsIndexSelected,
+    kHasFormFieldAtPoint,
+    kFormFieldZOrderAtPoint,
+    kGetSelectedText,
+    kGetFocusedText,
+    kMaxValue = kGetFocusedText
+  };
+  FuzzedDataProvider* fdp_ = nullptr;
+};
+
+// Possible names of an XFA FormCalc script function
+std::string GenXfaFormCalcScriptFuncName(FuzzedDataProvider* data_provider) {
+  static const char* const kXfaScriptFuncs[] = {
+      "Abs",       "Apr",        "At",           "Avg",          "Ceil",
+      "Choose",    "Concat",     "Count",        "Cterm",        "Date",
+      "Date2Num",  "DateFmt",    "Decode",       "Encode",       "Eval",
+      "Exists",    "Floor",      "Format",       "FV",           "Get",
+      "HasValue",  "If",         "Ipmt",         "IsoDate2Num",  "IsoTime2Num",
+      "Left",      "Len",        "LocalDateFmt", "LocalTimeFmt", "Lower",
+      "Ltrim",     "Max",        "Min",          "Mod",          "NPV",
+      "Num2Date",  "Num2GMTime", "Num2Time",     "Oneof",        "Parse",
+      "Pmt",       "Post",       "PPmt",         "Put",          "PV",
+      "Rate",      "Ref",        "Replace",      "Right",        "Round",
+      "Rtrim",     "Space",      "Str",          "Stuff",        "Substr",
+      "Sum",       "Term",       "Time",         "Time2Num",     "TimeFmt",
+      "Translate", "UnitType",   "UnitValue",    "Upper",        "Uuid",
+      "Within",    "WordNum",
+  };
+
+  size_t elem_selector = data_provider->ConsumeIntegralInRange<size_t>(
+      0, std::size(kXfaScriptFuncs) - 1);
+  return kXfaScriptFuncs[elem_selector];
+}
+
+std::string MaybeQuote(FuzzedDataProvider* data_provider, std::string body) {
+  if (data_provider->ConsumeIntegralInRange<uint32_t>(0, 100) < 20) {
+    return "\"" + body + "\"";
+  }
+  return body;
+}
+
+// Possible arguments to a XFA script function
+std::string GenXfaScriptParam(FuzzedDataProvider* data_provider) {
+  static const char* const kXfaFuncParams[] = {
+      "$",
+      "-0",
+      "04/13/2019",
+      ".05",
+      "-1",
+      "1",
+      " 1 | 0",
+      "10 * 10 * 10 * 9 * 123",
+      "1024",
+      "10 * a + 9",
+      "1.2131",
+      "[1,2,3]",
+      "%123",
+      "[1,2,3][0]",
+      "123124",
+      "123342123",
+      "13:13:13",
+      "13:13:13 GMT",
+      "19960315T20:20:20",
+      "1 and 1",
+      "1 and 2",
+      "2",
+      "20000201",
+      "2009-06-01T13:45:30",
+      "2009-06-15T01:45:30",
+      "2009-06-15T13:45:30-07:00",
+      "2009-06-15T13:45:30.5275000",
+      " 2 < 3 + 1",
+      "2 + 3 + 9",
+      "3",
+      "3 * 1",
+      "3 -9",
+      "5 < 5",
+      "-99",
+      "99",
+      "9999999",
+      "99999999999",
+      "A",
+      "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+      "\xc3\x81\xc3\x82\xc3\x83\xc3\x84\xc3\x85\xc3\x86",
+      "<a><b></b></a>",
+      "&Acirc;",
+      "&AElig;&Aacute;&Acirc;&Aacute;",
+      "Amount[*]",
+      "~!@#$%^&amp;*()_+",
+      "&amp;|",
+      "&apos",
+      "apr",
+      "april",
+      "B",
+      "<br>",
+      "C",
+      "de_DE",
+      "es_ES",
+      "feb",
+      "febuary",
+      "HH:MM:SS",
+      "<html>",
+      "html",
+      "HTML",
+      "jan",
+      "january",
+      "json",
+      "lkdjfglsdkfgj",
+      "mar",
+      "march",
+      "name[0]",
+      "name1",
+      "name2",
+      "name3",
+      "name4",
+      "name[*].numAmount",
+      "&quot;",
+      "Space",
+      "Str",
+      "url",
+      "xhtml",
+      "xml",
+      "XML&quot;",
+  };
+
+  size_t elem_selector = data_provider->ConsumeIntegralInRange<size_t>(
+      0, std::size(kXfaFuncParams) - 1);
+  return MaybeQuote(data_provider, kXfaFuncParams[elem_selector]);
+}
+
+// Possible XFA tags
+std::string GenXfaTag(FuzzedDataProvider* data_provider) {
+  static const char* const kXfaElemTags[] = {
+      "accessibleContent",
+      "acrobat",
+      "acrobat",
+      "acrobat7",
+      "ADBE_JSConsole",
+      "ADBE_JSDebugger",
+      "addSilentPrint",
+      "addViewerPreferences",
+      "adjustData",
+      "adobeExtensionLevel",
+      "agent",
+      "alwaysEmbed",
+      "amd",
+      "appearanceFilter",
+      "arc",
+      "area",
+      "assist",
+      "attributes",
+      "autoSave",
+      "barcode",
+      "base",
+      "batchOutput",
+      "behaviorOverride",
+      "bind",
+      "bindItems",
+      "bookend",
+      "boolean",
+      "border",
+      "break",
+      "breakAfter",
+      "breakBefore",
+      "button",
+      "cache",
+      "calculate",
+      "calendarSymbols",
+      "caption",
+      "certificate",
+      "certificates",
+      "change",
+      "checkButton",
+      "choiceList",
+      "color",
+      "comb",
+      "command",
+      "common",
+      "compress",
+      "compression",
+      "compressLogicalStructure",
+      "compressObjectStream",
+      "config",
+      "config",
+      "conformance",
+      "connect",
+      "connectionSet",
+      "connectString",
+      "contentArea",
+      "contentCopy",
+      "copies",
+      "corner",
+      "creator",
+      "currencySymbol",
+      "currencySymbols",
+      "currentPage",
+      "data",
+      "dataGroup",
+      "dataModel",
+      "dataValue",
+      "dataWindow",
+      "date",
+      "datePattern",
+      "datePatterns",
+      "dateTime",
+      "dateTimeEdit",
+      "dateTimeSymbols",
+      "day",
+      "dayNames",
+      "debug",
+      "decimal",
+      "defaultTypeface",
+      "defaultUi",
+      "delete",
+      "delta",
+      "deltas",
+      "desc",
+      "destination",
+      "digestMethod",
+      "digestMethods",
+      "documentAssembly",
+      "draw",
+      "driver",
+      "dSigData",
+      "duplexOption",
+      "dynamicRender",
+      "edge",
+      "effectiveInputPolicy",
+      "effectiveOutputPolicy",
+      "embed",
+      "encoding",
+      "encodings",
+      "encrypt",
+      "encryption",
+      "encryptionLevel",
+      "encryptionMethod",
+      "encryptionMethods",
+      "enforce",
+      "equate",
+      "equateRange",
+      "era",
+      "eraNames",
+      "event",
+      "eventPseudoModel",
+      "exclGroup",
+      "exclude",
+      "excludeNS",
+      "exData",
+      "execute",
+      "exObject",
+      "extras",
+      "field",
+      "fill",
+      "filter",
+      "flipLabel",
+      "float",
+      "font",
+      "fontInfo",
+      "form",
+      "format",
+      "formFieldFilling",
+      "groupParent",
+      "handler",
+      "hostPseudoModel",
+      "hyphenation",
+      "ifEmpty",
+      "image",
+      "imageEdit",
+      "includeXDPContent",
+      "incrementalLoad",
+      "incrementalMerge",
+      "insert",
+      "instanceManager",
+      "integer",
+      "interactive",
+      "issuers",
+      "items",
+      "jog",
+      "keep",
+      "keyUsage",
+      "labelPrinter",
+      "layout",
+      "layoutPseudoModel",
+      "level",
+      "line",
+      "linear",
+      "linearized",
+      "list",
+      "locale",
+      "localeSet",
+      "lockDocument",
+      "log",
+      "logPseudoModel",
+      "manifest",
+      "map",
+      "margin",
+      "mdp",
+      "medium",
+      "mediumInfo",
+      "meridiem",
+      "meridiemNames",
+      "message",
+      "messaging",
+      "mode",
+      "modifyAnnots",
+      "month",
+      "monthNames",
+      "msgId",
+      "nameAttr",
+      "neverEmbed",
+      "numberOfCopies",
+      "numberPattern",
+      "numberPatterns",
+      "numberSymbol",
+      "numberSymbols",
+      "numericEdit",
+      "object",
+      "occur",
+      "oid",
+      "oids",
+      "openAction",
+      "operation",
+      "output",
+      "outputBin",
+      "outputXSL",
+      "overflow",
+      "overprint",
+      "packet",
+      "packets",
+      "pageArea",
+      "pageOffset",
+      "pageRange",
+      "pageSet",
+      "pagination",
+      "paginationOverride",
+      "para",
+      "part",
+      "password",
+      "passwordEdit",
+      "pattern",
+      "pcl",
+      "pdf",
+      "pdfa",
+      "permissions",
+      "pickTrayByPDFSize",
+      "picture",
+      "plaintextMetadata",
+      "presence",
+      "present",
+      "present",
+      "print",
+      "printerName",
+      "printHighQuality",
+      "printScaling",
+      "producer",
+      "proto",
+      "ps",
+      "psMap",
+      "query",
+      "radial",
+      "range",
+      "reason",
+      "reasons",
+      "record",
+      "recordSet",
+      "rectangle",
+      "ref",
+      "relevant",
+      "rename",
+      "renderPolicy",
+      "rootElement",
+      "runScripts",
+      "script",
+      "scriptModel",
+      "select",
+      "setProperty",
+      "severity",
+      "signature",
+      "signatureProperties",
+      "signaturePseudoModel",
+      "signData",
+      "signing",
+      "silentPrint",
+      "soapAction",
+      "soapAddress",
+      "solid",
+      "source",
+      "sourceSet",
+      "speak",
+      "staple",
+      "startNode",
+      "startPage",
+      "stipple",
+      "subform",
+      "subform",
+      "subformSet",
+      "subjectDN",
+      "subjectDNs",
+      "submit",
+      "submitFormat",
+      "submitUrl",
+      "subsetBelow",
+      "suppressBanner",
+      "tagged",
+      "template",
+      "template",
+      "templateCache",
+      "#text",
+      "text",
+      "textedit",
+      "textEdit",
+      "threshold",
+      "time",
+      "timePattern",
+      "timePatterns",
+      "timeStamp",
+      "to",
+      "toolTip",
+      "trace",
+      "transform",
+      "traversal",
+      "traverse",
+      "treeList",
+      "type",
+      "typeface",
+      "typefaces",
+      "ui",
+      "update",
+      "uri",
+      "user",
+      "validate",
+      "validate",
+      "validateApprovalSignatures",
+      "validationMessaging",
+      "value",
+      "variables",
+      "version",
+      "versionControl",
+      "viewerPreferences",
+      "webClient",
+      "whitespace",
+      "window",
+      "wsdlAddress",
+      "wsdlConnection",
+      "xdc",
+      "xdp",
+      "xfa",
+      "#xHTML",
+      "#xml",
+      "xmlConnection",
+      "xsdConnection",
+      "xsl",
+      "zpl",
+  };
+
+  size_t elem_selector = data_provider->ConsumeIntegralInRange<size_t>(
+      0, std::size(kXfaElemTags) - 1);
+  return kXfaElemTags[elem_selector];
+}
+
+// Possible XFA attributes values
+std::string GenXfaTagValue(FuzzedDataProvider* data_provider) {
+  static const char* const kXfaTagVals[] = {
+      "0",         "0pt",         "-1",
+      "123",       "1pt",         "203.2mm",
+      "22.1404mm", "255",         "256",
+      "321",       "5431.21mm",   "6.35mm",
+      "8in",       "8pt",         "application/x-javascript",
+      "bold",      "bold",        "change",
+      "click",     "consumeData", "docReady",
+      "en_US",     "form1",       "initialize",
+      "italic",    "middle",      "name2",
+      "name3",     "name4",       "name5",
+      "onEnter",   "Page1",       "RadioList[0]",
+      "subform_1", "tb",          "Verdana",
+  };
+
+  size_t elem_selector = data_provider->ConsumeIntegralInRange<size_t>(
+      0, std::size(kXfaTagVals) - 1);
+  return MaybeQuote(data_provider, kXfaTagVals[elem_selector]);
+}
+
+// possible XFA attributes
+std::string GenXfaTagName(FuzzedDataProvider* data_provider) {
+  static const char* const kXfaTagNames[] = {
+      "activity",    "baselineShift",
+      "contentType", "h",
+      "id",          "layout",
+      "layout",      "leftInset",
+      "locale",      "long",
+      "marginLeft",  "marginRight",
+      "marginRight", "mergeMode",
+      "name",        "ref",
+      "scriptTest",  "short",
+      "size",        "spaceAbove",
+      "spaceBelow",  "startNew",
+      "stock",       "textIndent",
+      "timeStamp",   "typeface",
+      "uuid",        "vAlign",
+      "value",       "w",
+      "weight",      "x",
+      "y",
+  };
+  size_t elem_selector = data_provider->ConsumeIntegralInRange<size_t>(
+      0, std::size(kXfaTagNames) - 1);
+  return kXfaTagNames[elem_selector];
+}
+
+// Will create a simple XFA FormCalc script that calls a single function.
+std::string GenXfaFormCalcScript(FuzzedDataProvider* data_provider) {
+  std::string xfa_string = GenXfaFormCalcScriptFuncName(data_provider);
+  xfa_string += "(";
+
+  // Generate parameters
+  size_t num_params = data_provider->ConsumeIntegralInRange<size_t>(0, 3);
+  for (size_t i = 0; i < num_params; i++) {
+    if (i != 0) {
+      xfa_string += ",";
+    }
+    xfa_string += GenXfaScriptParam(data_provider);
+  }
+  xfa_string += ")";
+  return xfa_string;
+}
+
+// XFA Javascript logic
+std::string GenXfaName(FuzzedDataProvider* data_provider) {
+  return "name" + std::to_string(data_provider->ConsumeIntegralInRange(0, 25));
+}
+
+std::string GetXfaJSPrimitiveType(FuzzedDataProvider* data_provider) {
+  return GenXfaScriptParam(data_provider);
+}
+
+std::string GenXfaJSRValue(FuzzedDataProvider* data_provider) {
+  if (data_provider->ConsumeBool()) {
+    return GenXfaScriptParam(data_provider);
+  }
+
+  std::string xfa_string;
+  if (data_provider->ConsumeBool()) {
+    xfa_string += "xfa.form.";
+  }
+
+  // Handle the possibility of nested names
+  size_t num_nests = data_provider->ConsumeIntegralInRange<size_t>(1, 3);
+  for (size_t i = 0; i < num_nests; i++) {
+    if (i != 0) {
+      xfa_string += ".";
+    }
+    xfa_string += GenXfaName(data_provider);
+  }
+  return MaybeQuote(data_provider, xfa_string);
+}
+
+std::string GenXfaJSAssignment(FuzzedDataProvider* data_provider) {
+  return GenXfaName(data_provider) + " = " + GenXfaJSRValue(data_provider);
+}
+
+std::string GenXfaJSMethodCall(FuzzedDataProvider* data_provider) {
+  static const char* const kXfaJSFuncs[] = {
+      "addItem",
+      "boundItem",
+      "clearItems",
+      "deleteItem",
+      "execCalculate",
+      "execEvent",
+      "execInitialize",
+      "execValidate",
+      "getDisplayItem",
+      "getItemState",
+      "getSaveItem",
+      "exec.form.formNodes",
+      "exec.form.recalculate",
+      "setItemState",
+      "xfa.container.getDelta",
+      "xfa.container.getDeltas",
+      "xfa.event.emit",
+      "xfa.event.reset",
+      "xfa.form.execCalculat",
+      "xfa.form.execInitialize",
+      "xfa.form.execValidate",
+      "xfa.form.remerge",
+      "xfa.host.beep",
+      "xfa.host.documentCountInBatch",
+      "xfa.host.documentInBatch",
+      "xfa.host.exportData",
+      "xfa.host.getFocus",
+      "xfa.host.gotoURL",
+      "xfa.host.importData",
+      "xfa.host.messageBox",
+      "xfa.host.openList",
+      "xfa.host.pageDown",
+      "xfa.host.pageUp",
+      "xfa.host.print",
+      "xfa.host.resetData",
+      "xfa.host.setFocus",
+      "xfa.host.response",
+      "xfa.resolveNode",
+  };
+
+  std::string xfa_string = data_provider->PickValueInArray(kXfaJSFuncs);
+  xfa_string += "(";
+
+  // Get the params
+  size_t param_count = data_provider->ConsumeIntegralInRange<size_t>(0, 3);
+  for (size_t i = 0; i < param_count; i++) {
+    if (i != 0) {
+      xfa_string += ",";
+    }
+    xfa_string += GenXfaJSRValue(data_provider);
+  }
+  xfa_string += ")";
+  return xfa_string;
+}
+
+// This is a simple generator of xfa-based javascript. The function creates
+// simple javascript statements that are related to XFA logic and the goal is
+// not to create fully-fleged javascript programs but rather use simple
+// statements to ensure XFA code is covered.
+enum XFAJSStatement {
+  kAssignment = 0,
+  kJSMethodCall,
+  kJSObjectCall,
+  kMaxValue = kJSObjectCall
+};
+
+std::string GenXfaJSScript(FuzzedDataProvider* data_provider) {
+  std::string xfa_string;
+
+  size_t num_stmts = data_provider->ConsumeIntegralInRange<size_t>(1, 10);
+  for (size_t i = 0; i < num_stmts; i++) {
+    XFAJSStatement stmt = data_provider->ConsumeEnum<XFAJSStatement>();
+    switch (stmt) {
+      case kAssignment:
+        xfa_string += GenXfaJSAssignment(data_provider);
+        break;
+      case kJSMethodCall:
+        xfa_string += GenXfaJSMethodCall(data_provider);
+        break;
+      case kJSObjectCall:
+        xfa_string += GenXfaName(data_provider);
+        xfa_string += ".";
+        xfa_string += GenXfaJSMethodCall(data_provider);
+        break;
+    }
+    xfa_string += ";\n";
+  }
+  return xfa_string;
+}
+
+std::string GenXfacript(FuzzedDataProvider* data_provider) {
+  // Determine if this should be a FormCalc script or Javascript, 50/50 chance
+  // for each.
+  if (data_provider->ConsumeBool()) {
+    return GenXfaFormCalcScript(data_provider);
+  }
+  return GenXfaJSScript(data_provider);
+}
+
+// Will create a single XFA attributes, with both lhs and rhs.
+std::string getXfaElemAttributes(FuzzedDataProvider* data_provider) {
+  // Generate a set of tags, and a set of values for the tags.
+  return GenXfaTagName(data_provider) + "=" + GenXfaTagValue(data_provider);
+}
+
+// Creates an XFA structure wrapped in <xdp tags.
+std::string GenXfaTree(FuzzedDataProvider* data_provider) {
+  std::string xfa_string = "<xdp xmlns=\"http://ns.adobe.com/xdp/\">";
+
+  // One stack iteration
+  int stack_iterations = data_provider->ConsumeIntegralInRange(1, 3);
+  for (int si = 0; si < stack_iterations; si++) {
+    int elem_count = data_provider->ConsumeIntegralInRange(1, 6);
+    std::vector<std::string> xml_stack;
+    xml_stack.reserve(elem_count);
+    for (int i = 0; i < elem_count; i++) {
+      std::string tag = GenXfaTag(data_provider);
+      xfa_string += "<" + tag;
+
+      // in 30% of cases, add attributes
+      if (data_provider->ConsumeIntegralInRange(1, 100) > 70) {
+        size_t attribute_count = data_provider->ConsumeIntegralInRange(1, 5);
+        for (; 0 < attribute_count; attribute_count--) {
+          xfa_string += " " + getXfaElemAttributes(data_provider);
+        }
+      }
+      xfa_string += ">";
+
+      // If needed, add a body to the tag
+      if (tag == "script") {
+        xfa_string += GenXfacript(data_provider);
+      }
+
+      // Push the tag to the stack so we can close it when done
+      xml_stack.push_back(tag);
+    }
+    for (const std::string& tag : pdfium::base::Reversed(xml_stack)) {
+      xfa_string += "</" + tag + ">";
+    }
+  }
+  xfa_string += "</xdp>";
+  return xfa_string;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  FuzzedDataProvider data_provider(data, size);
+  std::string xfa_string = GenXfaTree(&data_provider);
+
+  // Add 1 for newline before endstream.
+  std::string xfa_stream_len = std::to_string(xfa_string.size() + 1);
+
+  // Compose the fuzzer
+  std::string xfa_final_str = std::string(kSimplePdfTemplate);
+  xfa_final_str.replace(xfa_final_str.find("$1"), 2, xfa_stream_len);
+  xfa_final_str.replace(xfa_final_str.find("$2"), 2, xfa_string);
+
+#ifdef PDFIUM_FUZZER_DUMP
+  for (size_t i = 0; i < xfa_final_str.size(); i++) {
+    putc(xfa_final_str[i], stdout);
+  }
+#endif
+
+  PDFiumXFAFuzzer fuzzer;
+  fuzzer.SetFdp(&data_provider);
+  fuzzer.RenderPdf(xfa_final_str.c_str(), xfa_final_str.size());
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_xfa_raw_fuzzer.cc b/testing/fuzzers/pdf_xfa_raw_fuzzer.cc
new file mode 100644
index 0000000..8dce02f
--- /dev/null
+++ b/testing/fuzzers/pdf_xfa_raw_fuzzer.cc
@@ -0,0 +1,101 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <cctype>
+#include <string>
+
+#include "public/fpdf_formfill.h"
+#include "testing/fuzzers/pdf_fuzzer_templates.h"
+#include "testing/fuzzers/pdfium_fuzzer_helper.h"
+
+class PDFiumXFAFuzzer : public PDFiumFuzzerHelper {
+ public:
+  PDFiumXFAFuzzer() = default;
+  ~PDFiumXFAFuzzer() override = default;
+
+  int GetFormCallbackVersion() const override { return 2; }
+
+  // Return false if XFA doesn't load as otherwise we're duplicating the work
+  // done by the non-xfa fuzzer.
+  bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc) override {
+    int form_type = FPDF_GetFormType(doc);
+    if (form_type != FORMTYPE_XFA_FULL && form_type != FORMTYPE_XFA_FOREGROUND)
+      return false;
+    return FPDF_LoadXFA(doc);
+  }
+};
+
+bool IsValidForFuzzing(const uint8_t* data, size_t size) {
+  if (size > 2048) {
+    return false;
+  }
+
+  const char* ptr = reinterpret_cast<const char*>(data);
+  bool is_open = false;
+  size_t tag_size = 0;
+  for (size_t i = 0; i < size; i++) {
+    if (!std::isspace(ptr[i]) && !std::isprint(ptr[i])) {
+      return false;
+    }
+
+    // We do not want any script tags. The reason is this fuzzer
+    // should avoid exploring v8 code. Avoiding anything with "script"
+    // is an over-approximation, in that some inputs may contain "script"
+    // and still be a valid fuzz-case. However, this over-approximation is
+    // used to enforce strict constraints and avoid cases where whitespace
+    // may play a role, or other tags, e.g. "Javascript" will end up triggering
+    // large explorations of v8 code. The alternative we considered were
+    // "<script"
+    if (i + 6 < size && memcmp(ptr + i, "script", 6) == 0) {
+      return false;
+    }
+
+    if (ptr[i] == '<') {
+      if (is_open) {
+        return false;
+      }
+      is_open = true;
+      tag_size = 0;
+    } else if (ptr[i] == '>') {
+      if (!is_open || tag_size == 0) {
+        return false;
+      }
+      is_open = false;
+    } else if (is_open) {
+      tag_size++;
+    }
+  }
+  // we must close the last bracket.
+  return !is_open;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  // Filter the string to reduce the state space exploration.
+  if (!IsValidForFuzzing(data, size)) {
+    return 0;
+  }
+  std::string xfa_string = "<xdp xmlns=\"http://ns.adobe.com/xdp/\">";
+  xfa_string += std::string(reinterpret_cast<const char*>(data), size);
+  xfa_string += "</xdp>";
+
+  // Add 1 for newline before endstream.
+  std::string xfa_stream_len = std::to_string(xfa_string.size() + 1);
+
+  // Compose the fuzzer
+  std::string xfa_final_str = std::string(kSimplePdfTemplate);
+  xfa_final_str.replace(xfa_final_str.find("$1"), 2, xfa_stream_len);
+  xfa_final_str.replace(xfa_final_str.find("$2"), 2, xfa_string);
+
+#ifdef PDFIUM_FUZZER_DUMP
+  for (size_t i = 0; i < xfa_final_str.size(); i++) {
+    putc(xfa_final_str[i], stdout);
+  }
+#endif
+
+  PDFiumXFAFuzzer fuzzer;
+  fuzzer.RenderPdf(xfa_final_str.c_str(), xfa_final_str.size());
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_xfa_xdp_fdp_fuzzer.cc b/testing/fuzzers/pdf_xfa_xdp_fdp_fuzzer.cc
new file mode 100644
index 0000000..8917ce7
--- /dev/null
+++ b/testing/fuzzers/pdf_xfa_xdp_fdp_fuzzer.cc
@@ -0,0 +1,206 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <string>
+
+#include "public/fpdf_formfill.h"
+#include "testing/fuzzers/pdf_fuzzer_templates.h"
+#include "testing/fuzzers/pdfium_fuzzer_helper.h"
+
+class PDFiumXDPFuzzer : public PDFiumFuzzerHelper {
+ public:
+  PDFiumXDPFuzzer() = default;
+  ~PDFiumXDPFuzzer() override = default;
+
+  int GetFormCallbackVersion() const override { return 2; }
+
+  bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc) override {
+    int form_type = FPDF_GetFormType(doc);
+    if (form_type != FORMTYPE_XFA_FULL && form_type != FORMTYPE_XFA_FOREGROUND)
+      return false;
+    return FPDF_LoadXFA(doc);
+  }
+};
+
+struct Tag {
+  const char* tag_name;
+  const char* tag_start;
+  const char* tag_end;
+};
+
+const Tag kTagData[]{
+    {.tag_name = "config",
+     .tag_start =
+         R""(<xfa:config xmlns:xfa="http://www.xfa.org/schema/xci/3.1/">)"",
+     .tag_end = "</xfa:config>"},
+    {.tag_name = "template",
+     .tag_start =
+         R""(<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">)"",
+     .tag_end = "</template>"},
+    {.tag_name = "sourceSet",
+     .tag_start =
+         R""(<sourceSet xmlns="http://www.xfa.org/schema/xfa-source-set/2.7/">)"",
+     .tag_end = "</sourceSet>"},
+    {.tag_name = "localeSet",
+     .tag_start =
+         R""(<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">)"",
+     .tag_end = "</localeSet>"},
+    {.tag_name = "dataSet",
+     .tag_start =
+         R""(<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">)"",
+     .tag_end = "</xfa:datasets>"},
+    {.tag_name = "connectionSet",
+     .tag_start =
+         R""(<connectionSet xmlns="http://www.xfa.org/schema/xfa-connection-set/2.8/">)"",
+     .tag_end = "</connectionSet>"},
+    {.tag_name = "xdc",
+     .tag_start =
+         R""(<xsl:xdc xmlns:xdc="http://www.xfa.org/schema/xdc/1.0/">)"",
+     .tag_end = "</xsl:xdc>"},
+    {.tag_name = "signature",
+     .tag_start = R""(<signature xmlns="http://www.w3.org/2000/09/xmldsig#">)"",
+     .tag_end = "</signature>"},
+    {.tag_name = "stylesheet",
+     .tag_start =
+         R""(<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" id="identifier">)"",
+     .tag_end = "</stylesheet>"},
+    {.tag_name = "xfdf",
+     .tag_start =
+         R""(<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">)"",
+     .tag_end = "</xfdf>"},
+    {.tag_name = "xmpmeta",
+     .tag_start =
+         R""(<xmpmeta xmlns="http://ns.adobe.com/xmpmeta/" xml:space="preserve">)"",
+     .tag_end = "</xmpmeta>"}};
+
+std::string CreateObject(int obj_num, const std::string& body) {
+  std::string obj_template = R""($1 0 obj
+$2
+endobj
+)"";
+
+  obj_template.replace(obj_template.find("$1"), 2, std::to_string(obj_num));
+  obj_template.replace(obj_template.find("$2"), 2, body);
+  return obj_template;
+}
+
+std::string CreateStreamObject(int obj_num, const std::string& body) {
+  std::string obj_template = R""($1 0 obj
+<</Length $2>>
+stream
+$3
+endstream
+endobj
+)"";
+
+  obj_template.replace(obj_template.find("$1"), 2, std::to_string(obj_num));
+  obj_template.replace(obj_template.find("$2"), 2,
+                       std::to_string(body.size() + 1));
+  obj_template.replace(obj_template.find("$3"), 2, body);
+
+  return obj_template;
+}
+
+std::string GenXrefEntry(size_t offset) {
+  return std::string(10 - std::to_string(offset).size(), '0') +
+         std::to_string(offset) + " 00000 n\n";
+}
+
+std::string GenTagBody(const Tag& tag, FuzzedDataProvider* data_provider) {
+  std::string tag_content = data_provider->ConsumeRandomLengthString();
+  return tag.tag_start + tag_content + tag.tag_end;
+}
+
+std::string GenXDPPdfFile(FuzzedDataProvider* data_provider) {
+  std::vector<std::string> pdf_objects;
+  std::string pdf_header =
+      std::string(reinterpret_cast<const char*>(kSimplePdfHeader),
+                  sizeof(kSimplePdfHeader));
+
+  pdf_objects.push_back(CreateObject(1, kCatalog));
+
+  std::string xfa_obj = kSimpleXfaObjWrapper;
+  Tag tag1 = data_provider->PickValueInArray(kTagData);
+  Tag tag2 = data_provider->PickValueInArray(kTagData);
+  Tag tag3 = data_provider->PickValueInArray(kTagData);
+  xfa_obj.replace(xfa_obj.find("$1"), 2, tag1.tag_name);
+  xfa_obj.replace(xfa_obj.find("$2"), 2, tag2.tag_name);
+  xfa_obj.replace(xfa_obj.find("$3"), 2, tag3.tag_name);
+  pdf_objects.push_back(CreateObject(2, xfa_obj));
+  pdf_objects.push_back(CreateObject(3, kSimplePagesObj));
+  pdf_objects.push_back(CreateObject(4, kSimplePageObj));
+
+  // preamble
+  pdf_objects.push_back(CreateStreamObject(5, kSimplePreamble));
+
+  // The three XFA tags
+  pdf_objects.push_back(CreateStreamObject(6, GenTagBody(tag1, data_provider)));
+  pdf_objects.push_back(CreateStreamObject(7, GenTagBody(tag2, data_provider)));
+  pdf_objects.push_back(CreateStreamObject(8, GenTagBody(tag3, data_provider)));
+
+  // postamble
+  pdf_objects.push_back(CreateStreamObject(9, kSimplePostamble));
+
+  // Create the xref table
+  std::string xref = R""(xref
+0 10
+0000000000 65535 f
+)"";
+
+  // Add xref entries
+  size_t curr_offset = pdf_header.size();
+  for (const auto& ostr : pdf_objects) {
+    xref += GenXrefEntry(curr_offset);
+    curr_offset += ostr.size();
+  }
+
+  std::string footer = R""(trailer
+<</Root 1 0 R /Size 10>>
+startxref
+$1
+%%EOF)"";
+  footer.replace(footer.find("$1"), 2, std::to_string(curr_offset));
+
+  std::string pdf_core;
+  for (const auto& ostr : pdf_objects) {
+    pdf_core += ostr;
+  }
+
+  // Return the full PDF
+  return pdf_header + pdf_core + xref + footer;
+}
+
+bool IsValidForFuzzing(const uint8_t* data, size_t size) {
+  if (size > 2048) {
+    return false;
+  }
+  const char* ptr = reinterpret_cast<const char*>(data);
+  for (size_t i = 0; i < size; i++) {
+    if (!std::isspace(ptr[i]) && !std::isprint(ptr[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (!IsValidForFuzzing(data, size)) {
+    return 0;
+  }
+
+  FuzzedDataProvider data_provider(data, size);
+  std::string xfa_final_str = GenXDPPdfFile(&data_provider);
+
+#ifdef PDFIUM_FUZZER_DUMP
+  for (size_t i = 0; i < xfa_final_str.size(); i++) {
+    putc(xfa_final_str[i], stdout);
+  }
+#endif
+
+  PDFiumXDPFuzzer fuzzer;
+  fuzzer.RenderPdf(xfa_final_str.c_str(), xfa_final_str.size());
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_xml_fuzzer.cc b/testing/fuzzers/pdf_xml_fuzzer.cc
index e858f5b..e908a7e 100644
--- a/testing/fuzzers/pdf_xml_fuzzer.cc
+++ b/testing/fuzzers/pdf_xml_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,21 +6,21 @@
 #include <cstdint>
 #include <memory>
 
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/xml/cfx_xmldocument.h"
 #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;
   if (!safe_size.IsValid())
     return 0;
 
-  auto stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-      pdfium::make_span(data, size));
+  auto stream =
+      pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(data, size));
   CFX_XMLParser parser(stream);
   std::unique_ptr<CFX_XMLDocument> doc = parser.Parse();
   if (!doc || !doc->GetRoot())
diff --git a/testing/fuzzers/pdfium_fuzzer.cc b/testing/fuzzers/pdfium_fuzzer.cc
index dc15378..e70702a 100644
--- a/testing/fuzzers/pdfium_fuzzer.cc
+++ b/testing/fuzzers/pdfium_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/fuzzers/pdfium_fuzzer_helper.cc b/testing/fuzzers/pdfium_fuzzer_helper.cc
index 266666d..1d26787 100644
--- a/testing/fuzzers/pdfium_fuzzer_helper.cc
+++ b/testing/fuzzers/pdfium_fuzzer_helper.cc
@@ -1,19 +1,16 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "testing/fuzzers/pdfium_fuzzer_helper.h"
 
 #include <assert.h>
-#include <limits.h>
-
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
-#include <memory>
 #include <sstream>
 #include <string>
 #include <tuple>
@@ -23,7 +20,9 @@
 #include "public/fpdf_dataavail.h"
 #include "public/fpdf_ext.h"
 #include "public/fpdf_text.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 {
 
@@ -36,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;
@@ -95,7 +93,7 @@
 std::pair<int, int> GetRenderingAndFormFlagFromData(const char* data,
                                                     size_t len) {
   std::string data_str = std::string(data, len);
-  std::size_t data_hash = std::hash<std::string>()(data_str);
+  size_t data_hash = std::hash<std::string>()(data_str);
 
   // The largest flag value is 0x4FFF, so just take 16 bits from |data_hash| at
   // a time.
@@ -218,6 +216,8 @@
   FORM_OnAfterLoadPage(page.get(), form);
   FORM_DoPageAAction(page.get(), form, FPDFPAGE_AACTION_OPEN);
 
+  FormActionHandler(form, doc, page.get());
+
   const double scale = 1.0;
   int width = static_cast<int>(FPDF_GetPageWidthF(page.get()) * scale);
   int height = static_cast<int>(FPDF_GetPageHeightF(page.get()) * scale);
diff --git a/testing/fuzzers/pdfium_fuzzer_helper.h b/testing/fuzzers/pdfium_fuzzer_helper.h
index af14941..900f55d 100644
--- a/testing/fuzzers/pdfium_fuzzer_helper.h
+++ b/testing/fuzzers/pdfium_fuzzer_helper.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,6 +16,9 @@
   virtual int GetFormCallbackVersion() const = 0;
   virtual bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc);
   virtual void OnRenderFinished(FPDF_DOCUMENT doc) {}
+  virtual void FormActionHandler(FPDF_FORMHANDLE form,
+                                 FPDF_DOCUMENT doc,
+                                 FPDF_PAGE page) {}
 
  protected:
   PDFiumFuzzerHelper();
diff --git a/testing/fuzzers/pdfium_fuzzer_util.cc b/testing/fuzzers/pdfium_fuzzer_util.cc
index 9238f0e..f8f8286 100644
--- a/testing/fuzzers/pdfium_fuzzer_util.cc
+++ b/testing/fuzzers/pdfium_fuzzer_util.cc
@@ -1,9 +1,21 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "testing/fuzzers/pdfium_fuzzer_util.h"
 
+namespace {
+void* g_fuzzer_init_per_process_state = nullptr;
+}  // namespace
+
 int GetInteger(const uint8_t* data) {
   return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
 }
+
+FPDF_EXPORT void FPDF_CALLCONV FPDF_SetFuzzerPerProcessState(void* state) {
+  g_fuzzer_init_per_process_state = state;
+}
+
+FPDF_EXPORT void* FPDF_CALLCONV FPDF_GetFuzzerPerProcessState() {
+  return g_fuzzer_init_per_process_state;
+}
diff --git a/testing/fuzzers/pdfium_fuzzer_util.h b/testing/fuzzers/pdfium_fuzzer_util.h
index d82f44b..a37544d 100644
--- a/testing/fuzzers/pdfium_fuzzer_util.h
+++ b/testing/fuzzers/pdfium_fuzzer_util.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,19 @@
 
 #include <stdint.h>
 
+#include "public/fpdfview.h"
+
 // Returns an integer from the first 4 bytes of |data|.
 int GetInteger(const uint8_t* data);
 
+// Plumb access to any context created by fuzzer initialization into
+// the LLVMFuzzerTestOneInput() function, as that function does not
+// allow for additional parameters, nor can it reach back up to the
+// top-level fuzzer shim during a component build (see the comment
+// in BUILD.gn about splitting fuzzers into _impl and _src targets).
+extern "C" {
+FPDF_EXPORT void FPDF_CALLCONV FPDF_SetFuzzerPerProcessState(void* state);
+FPDF_EXPORT void* FPDF_CALLCONV FPDF_GetFuzzerPerProcessState();
+}  // extern "C"
+
 #endif  // TESTING_FUZZERS_PDFIUM_FUZZER_UTIL_H_
diff --git a/testing/fuzzers/pdfium_xfa_fuzzer.cc b/testing/fuzzers/pdfium_xfa_fuzzer.cc
index f9a69d4..1ba78cc 100644
--- a/testing/fuzzers/pdfium_xfa_fuzzer.cc
+++ b/testing/fuzzers/pdfium_xfa_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.cc b/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.cc
index c4b55b6..9f6c78a 100644
--- a/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.cc
+++ b/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.h b/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.h
index bb6bf1d..a7c2ec0 100644
--- a/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.h
+++ b/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/fuzzers/xfa_codec_fuzzer.h b/testing/fuzzers/xfa_codec_fuzzer.h
index 4f5668d..aaa884b 100644
--- a/testing/fuzzers/xfa_codec_fuzzer.h
+++ b/testing/fuzzers/xfa_codec_fuzzer.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,13 +6,15 @@
 #define TESTING_FUZZERS_XFA_CODEC_FUZZER_H_
 
 #include <memory>
+#include <utility>
 
 #include "core/fxcodec/fx_codec.h"
-#include "core/fxcodec/progressivedecoder.h"
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcodec/progressive_decoder.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 // Support up to 64 MB. This prevents trivial OOM when MSAN is on and
 // time outs.
@@ -21,14 +23,13 @@
 class XFACodecFuzzer {
  public:
   static int Fuzz(const uint8_t* data, size_t size, FXCODEC_IMAGE_TYPE type) {
-    auto* mgr = fxcodec::ModuleMgr::GetInstance();
-    std::unique_ptr<ProgressiveDecoder> decoder =
-        mgr->CreateProgressiveDecoder();
-    auto source = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+    auto decoder = std::make_unique<ProgressiveDecoder>();
+    auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
         pdfium::make_span(data, size));
     CFX_DIBAttribute attr;
-    FXCODEC_STATUS status = decoder->LoadImageInfo(source, type, &attr, true);
-    if (status != FXCODEC_STATUS_FRAME_READY)
+    FXCODEC_STATUS status =
+        decoder->LoadImageInfo(std::move(source), type, &attr, true);
+    if (status != FXCODEC_STATUS::kFrameReady)
       return 0;
 
     // Skipping very large images, since they will take a long time and may lead
@@ -42,16 +43,17 @@
     }
 
     auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-    bitmap->Create(decoder->GetWidth(), decoder->GetHeight(), FXDIB_Argb);
+    bitmap->Create(decoder->GetWidth(), decoder->GetHeight(),
+                   FXDIB_Format::kArgb);
 
     size_t frames;
     std::tie(status, frames) = decoder->GetFrames();
-    if (status != FXCODEC_STATUS_DECODE_READY || frames == 0)
+    if (status != FXCODEC_STATUS::kDecodeReady || frames == 0)
       return 0;
 
     status = decoder->StartDecode(bitmap, 0, 0, bitmap->GetWidth(),
                                   bitmap->GetHeight());
-    while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE)
+    while (status == FXCODEC_STATUS::kDecodeToBeContinued)
       status = decoder->ContinueDecode();
 
     return 0;
diff --git a/testing/fuzzers/xfa_process_state.cc b/testing/fuzzers/xfa_process_state.cc
new file mode 100644
index 0000000..f768fea
--- /dev/null
+++ b/testing/fuzzers/xfa_process_state.cc
@@ -0,0 +1,25 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/fuzzers/xfa_process_state.h"
+
+#include "fxjs/gc/heap.h"
+#include "v8/include/libplatform/libplatform.h"
+
+XFAProcessState::XFAProcessState(v8::Platform* platform, v8::Isolate* isolate)
+    : platform_(platform), isolate_(isolate), heap_(FXGC_CreateHeap()) {}
+
+XFAProcessState::~XFAProcessState() {
+  FXGC_ForceGarbageCollection(heap_.get());
+}
+
+cppgc::Heap* XFAProcessState::GetHeap() const {
+  return heap_.get();
+}
+
+void XFAProcessState::ForceGCAndPump() {
+  FXGC_ForceGarbageCollection(heap_.get());
+  while (v8::platform::PumpMessageLoop(platform_, isolate_))
+    continue;
+}
diff --git a/testing/fuzzers/xfa_process_state.h b/testing/fuzzers/xfa_process_state.h
new file mode 100644
index 0000000..12d8a7d
--- /dev/null
+++ b/testing/fuzzers/xfa_process_state.h
@@ -0,0 +1,33 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_FUZZERS_XFA_PROCESS_STATE_H_
+#define TESTING_FUZZERS_XFA_PROCESS_STATE_H_
+
+#if !defined(PDF_ENABLE_XFA)
+#error "XFA only"
+#endif
+
+#include "fxjs/gc/heap.h"
+
+namespace v8 {
+class Isolate;
+class Platform;
+}  // namespace v8
+
+class XFAProcessState {
+ public:
+  XFAProcessState(v8::Platform* platform, v8::Isolate* isolate);
+  ~XFAProcessState();
+
+  cppgc::Heap* GetHeap() const;
+  void ForceGCAndPump();
+
+ private:
+  v8::Platform* const platform_;
+  v8::Isolate* const isolate_;
+  FXGCScopedHeap heap_;
+};
+
+#endif  // TESTING_FUZZERS_XFA_PROCESS_STATE_H_
diff --git a/testing/fx_string_testhelpers.cpp b/testing/fx_string_testhelpers.cpp
index 4a7bda7..09f7653 100644
--- a/testing/fx_string_testhelpers.cpp
+++ b/testing/fx_string_testhelpers.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,12 @@
 
 #include <iomanip>
 #include <ios>
+#include <ostream>
 
+#include "core/fxcrt/cfx_datetime.h"
 #include "core/fxcrt/fx_string.h"
-#include "third_party/base/span.h"
+#include "third_party/base/check_op.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()) << "-"
@@ -22,7 +25,7 @@
 std::vector<std::string> StringSplit(const std::string& str, char delimiter) {
   std::vector<std::string> result;
   size_t pos = 0;
-  while (1) {
+  while (true) {
     size_t found = str.find(delimiter, pos);
     if (found == std::string::npos)
       break;
@@ -48,16 +51,17 @@
   while (wstr[characters])
     ++characters;
 
-  std::wstring platform_string(characters, L'\0');
-  for (size_t i = 0; i < characters + 1; ++i) {
+  std::wstring platform_string;
+  platform_string.reserve(characters);
+  for (size_t i = 0; i < characters; ++i) {
     const unsigned char* ptr = reinterpret_cast<const unsigned char*>(&wstr[i]);
-    platform_string[i] = ptr[0] + 256 * ptr[1];
+    platform_string.push_back(ptr[0] + 256 * ptr[1]);
   }
   return platform_string;
 }
 
 ScopedFPDFWideString GetFPDFWideString(const std::wstring& wstr) {
-  size_t length = sizeof(uint16_t) * (wstr.length() + 1);
+  size_t length = sizeof(uint16_t) * (wstr.size() + 1);
   ScopedFPDFWideString result(static_cast<FPDF_WCHAR*>(malloc(length)));
   pdfium::span<uint8_t> result_span(reinterpret_cast<uint8_t*>(result.get()),
                                     length);
@@ -72,6 +76,6 @@
 }
 
 std::vector<FPDF_WCHAR> GetFPDFWideStringBuffer(size_t length_bytes) {
-  ASSERT(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/fx_string_testhelpers.h b/testing/fx_string_testhelpers.h
index b90838a..207947c 100644
--- a/testing/fx_string_testhelpers.h
+++ b/testing/fx_string_testhelpers.h
@@ -1,19 +1,20 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef TESTING_FX_STRING_TESTHELPERS_H_
 #define TESTING_FX_STRING_TESTHELPERS_H_
 
+#include <iosfwd>
 #include <memory>
-#include <ostream>
 #include <string>
 #include <vector>
 
-#include "core/fxcrt/cfx_datetime.h"
 #include "public/fpdfview.h"
 #include "testing/free_deleter.h"
 
+class CFX_DateTime;
+
 // Output stream operator so GTEST macros work with CFX_DateTime objects.
 std::ostream& operator<<(std::ostream& os, const CFX_DateTime& dt);
 
diff --git a/testing/fxgc_unittest.cpp b/testing/fxgc_unittest.cpp
new file mode 100644
index 0000000..cc01ab4
--- /dev/null
+++ b/testing/fxgc_unittest.cpp
@@ -0,0 +1,39 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/fxgc_unittest.h"
+
+#include "fxjs/gc/heap.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/v8_test_environment.h"
+#include "v8/include/libplatform/libplatform.h"
+
+FXGCUnitTest::FXGCUnitTest() = default;
+
+FXGCUnitTest::~FXGCUnitTest() = default;
+
+void FXGCUnitTest::SetUp() {
+  ::testing::Test::SetUp();
+  auto* env = V8TestEnvironment::GetInstance();
+  FXGC_Initialize(env->platform(), env->isolate());
+  heap_ = FXGC_CreateHeap();
+  ASSERT_TRUE(heap_);
+}
+
+void FXGCUnitTest::TearDown() {
+  ForceGCAndPump();
+  heap_.reset();
+  FXGC_Release();
+  ::testing::Test::TearDown();
+}
+
+void FXGCUnitTest::ForceGCAndPump() {
+  FXGC_ForceGarbageCollection(heap_.get());
+  Pump();
+}
+
+void FXGCUnitTest::Pump() {
+  V8TestEnvironment::PumpPlatformMessageLoop(
+      V8TestEnvironment::GetInstance()->isolate());
+}
diff --git a/testing/fxgc_unittest.h b/testing/fxgc_unittest.h
new file mode 100644
index 0000000..0625190
--- /dev/null
+++ b/testing/fxgc_unittest.h
@@ -0,0 +1,28 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_FXGC_UNITTEST_H_
+#define TESTING_FXGC_UNITTEST_H_
+
+#include "fxjs/gc/heap.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class FXGCUnitTest : public ::testing::Test {
+ public:
+  FXGCUnitTest();
+  ~FXGCUnitTest() override;
+
+  // testing::Test:
+  void SetUp() override;
+  void TearDown() override;
+
+  cppgc::Heap* heap() const { return heap_.get(); }
+  void ForceGCAndPump();
+  void Pump();
+
+ private:
+  FXGCScopedHeap heap_;
+};
+
+#endif  // TESTING_FXGC_UNITTEST_H_
diff --git a/testing/fxv8_unittest.cpp b/testing/fxv8_unittest.cpp
new file mode 100644
index 0000000..fdd4c08
--- /dev/null
+++ b/testing/fxv8_unittest.cpp
@@ -0,0 +1,26 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/fxv8_unittest.h"
+
+#include <memory>
+
+#include "fxjs/cfx_v8_array_buffer_allocator.h"
+#include "v8/include/v8-isolate.h"
+
+void FXV8UnitTest::V8IsolateDeleter::operator()(v8::Isolate* ptr) const {
+  ptr->Dispose();
+}
+
+FXV8UnitTest::FXV8UnitTest() = default;
+
+FXV8UnitTest::~FXV8UnitTest() = default;
+
+void FXV8UnitTest::SetUp() {
+  array_buffer_allocator_ = std::make_unique<CFX_V8ArrayBufferAllocator>();
+
+  v8::Isolate::CreateParams params;
+  params.array_buffer_allocator = array_buffer_allocator_.get();
+  isolate_.reset(v8::Isolate::New(params));
+}
diff --git a/testing/fxv8_unittest.h b/testing/fxv8_unittest.h
new file mode 100644
index 0000000..0757c73
--- /dev/null
+++ b/testing/fxv8_unittest.h
@@ -0,0 +1,37 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_FXV8_UNITTEST_H_
+#define TESTING_FXV8_UNITTEST_H_
+
+#include <memory>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+class CFX_V8;
+class CFX_V8ArrayBufferAllocator;
+
+namespace v8 {
+class Isolate;
+}  // namespace v8
+
+class FXV8UnitTest : public ::testing::Test {
+ public:
+  struct V8IsolateDeleter {
+    void operator()(v8::Isolate* ptr) const;
+  };
+
+  FXV8UnitTest();
+  ~FXV8UnitTest() override;
+
+  void SetUp() override;
+
+  v8::Isolate* isolate() const { return isolate_.get(); }
+
+ protected:
+  std::unique_ptr<CFX_V8ArrayBufferAllocator> array_buffer_allocator_;
+  std::unique_ptr<v8::Isolate, V8IsolateDeleter> isolate_;
+};
+
+#endif  // TESTING_FXV8_UNITTEST_H_
diff --git a/testing/gmock/BUILD.gn b/testing/gmock/BUILD.gn
index d7de2e9..feb8240 100644
--- a/testing/gmock/BUILD.gn
+++ b/testing/gmock/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -6,31 +6,14 @@
 # it stabilizes, Chromium code MUST use this target instead of reaching directly
 # into //third_party/googletest.
 
-import("//build_overrides/build.gni")
-
 source_set("gmock") {
   testonly = true
   sources = [
     "include/gmock/gmock-actions.h",
-    "include/gmock/gmock-generated-function-mockers.h",
     "include/gmock/gmock-matchers.h",
     "include/gmock/gmock.h",
   ]
-  deps = [ "//third_party/googletest:gmock" ]
-
-  # TODO(crbug.com/806952): Depending on gmock_mutant only if build_with_chromium,
-  # because gmock_mutant depends on //base which uses C++14. Since gmock is a
-  # third_party library used by other projects it should not include C++14 only code.
-  if (build_with_chromium) {
-    # Allow Chromium targets depending on gmock to #include testing/gmock_mutant.h
-    # without triggering a `gn check` error.
-    public_deps = [ "//testing:gmock_mutant" ]
-  }
-
-  public_configs = [
-    "//third_party/googletest:gmock_config",
-    "//third_party/googletest:gtest_config",
-  ]
+  public_deps = [ "//third_party/googletest:gmock" ]
 }
 
 # The file/directory layout of Google Test is not yet considered stable. Until
diff --git a/testing/gmock/include/gmock/gmock-actions.h b/testing/gmock/include/gmock/gmock-actions.h
index fb193e5..21fc6b0 100644
--- a/testing/gmock/include/gmock/gmock-actions.h
+++ b/testing/gmock/include/gmock/gmock-actions.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/gmock/include/gmock/gmock-generated-function-mockers.h b/testing/gmock/include/gmock/gmock-generated-function-mockers.h
index 57cbc0a..8ce51a0 100644
--- a/testing/gmock/include/gmock/gmock-generated-function-mockers.h
+++ b/testing/gmock/include/gmock/gmock-generated-function-mockers.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/gmock/include/gmock/gmock-matchers.h b/testing/gmock/include/gmock/gmock-matchers.h
index 25d25e9..ca7cf0e 100644
--- a/testing/gmock/include/gmock/gmock-matchers.h
+++ b/testing/gmock/include/gmock/gmock-matchers.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/gmock/include/gmock/gmock.h b/testing/gmock/include/gmock/gmock.h
index a344bcb..dfcd2d4 100644
--- a/testing/gmock/include/gmock/gmock.h
+++ b/testing/gmock/include/gmock/gmock.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/gtest/BUILD.gn b/testing/gtest/BUILD.gn
index ad0b269..f6b98df 100644
--- a/testing/gtest/BUILD.gn
+++ b/testing/gtest/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -55,10 +55,7 @@
     sources += [ "../platform_test.h" ]
   }
 
-  if ((is_mac || is_ios) && gtest_include_objc_support) {
-    if (is_ios) {
-      set_sources_assignment_filter([])
-    }
+  if (is_apple && gtest_include_objc_support) {
     sources += [
       "../gtest_mac.h",
       "../gtest_mac.mm",
@@ -66,15 +63,10 @@
     if (gtest_include_platform_test) {
       sources += [ "../platform_test_mac.mm" ]
     }
-    set_sources_assignment_filter(sources_assignment_filter)
   }
 
   if (is_ios && gtest_include_ios_coverage) {
-    sources += [
-      "../coverage_util_ios.h",
-      "../coverage_util_ios.mm",
-    ]
-    deps = [ ":ios_enable_coverage" ]
+    public_deps += [ ":ios_coverage_utils" ]
   }
 }
 
@@ -87,6 +79,16 @@
 }
 
 if (is_ios) {
+  # These headers are needed in some non test targets for iOS code coverage. So
+  # can not be testonly.
+  source_set("ios_coverage_utils") {
+    sources = [
+      "../coverage_util_ios.h",
+      "../coverage_util_ios.mm",
+    ]
+    deps = [ ":ios_enable_coverage" ]
+  }
+
   buildflag_header("ios_enable_coverage") {
     header = "ios_enable_coverage.h"
     flags = [ "IOS_ENABLE_COVERAGE=$use_clang_coverage" ]
diff --git a/testing/gtest/empty.cc b/testing/gtest/empty.cc
index 5186b59..7cfe79f 100644
--- a/testing/gtest/empty.cc
+++ b/testing/gtest/empty.cc
@@ -1,3 +1,3 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
diff --git a/testing/gtest/include/gtest/gtest-death-test.h b/testing/gtest/include/gtest/gtest-death-test.h
new file mode 100644
index 0000000..6e79c82
--- /dev/null
+++ b/testing/gtest/include/gtest/gtest-death-test.h
@@ -0,0 +1,10 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The file/directory layout of Google Test is not yet considered stable. Until
+// it stabilizes, PDFium code will use forwarding headers in testing/gtest
+// and testing/gmock, instead of directly including files in
+// third_party/googletest.
+
+#include "third_party/googletest/src/googletest/include/gtest/gtest-death-test.h"
diff --git a/testing/gtest/include/gtest/gtest-message.h b/testing/gtest/include/gtest/gtest-message.h
new file mode 100644
index 0000000..7859730
--- /dev/null
+++ b/testing/gtest/include/gtest/gtest-message.h
@@ -0,0 +1,10 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The file/directory layout of Google Test is not yet considered stable. Until
+// it stabilizes, PDFium code will use forwarding headers in testing/gtest
+// and testing/gmock, instead of directly including files in
+// third_party/googletest.
+
+#include "third_party/googletest/src/googletest/include/gtest/gtest-message.h"
diff --git a/testing/gtest/include/gtest/gtest-param-test.h b/testing/gtest/include/gtest/gtest-param-test.h
new file mode 100644
index 0000000..3d681e4
--- /dev/null
+++ b/testing/gtest/include/gtest/gtest-param-test.h
@@ -0,0 +1,10 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The file/directory layout of Google Test is not yet considered stable. Until
+// it stabilizes, PDFium code will use forwarding headers in testing/gtest
+// and testing/gmock, instead of directly including files in
+// third_party/googletest.
+
+#include "third_party/googletest/src/googletest/include/gtest/gtest-param-test.h"
diff --git a/testing/gtest/include/gtest/gtest-spi.h b/testing/gtest/include/gtest/gtest-spi.h
new file mode 100644
index 0000000..24ba5ee
--- /dev/null
+++ b/testing/gtest/include/gtest/gtest-spi.h
@@ -0,0 +1,10 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The file/directory layout of Google Test is not yet considered stable. Until
+// it stabilizes, PDFium code will use forwarding headers in testing/gtest
+// and testing/gmock, instead of directly including files in
+// third_party/googletest.
+
+#include "third_party/googletest/src/googletest/include/gtest/gtest-spi.h"
diff --git a/testing/gtest/include/gtest/gtest.h b/testing/gtest/include/gtest/gtest.h
index 9425b25..4706280 100644
--- a/testing/gtest/include/gtest/gtest.h
+++ b/testing/gtest/include/gtest/gtest.h
@@ -1,9 +1,9 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 // The file/directory layout of Google Test is not yet considered stable. Until
-// it stabilizes, Chromium code will use forwarding headers in testing/gtest
+// it stabilizes, PDFium code will use forwarding headers in testing/gtest
 // and testing/gmock, instead of directly including files in
 // third_party/googletest.
 
diff --git a/testing/gtest/include/gtest/gtest_prod.h b/testing/gtest/include/gtest/gtest_prod.h
index 2d67b42..703a176 100644
--- a/testing/gtest/include/gtest/gtest_prod.h
+++ b/testing/gtest/include/gtest/gtest_prod.h
@@ -1,9 +1,9 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 // The file/directory layout of Google Test is not yet considered stable. Until
-// it stabilizes, Chromium code will use forwarding headers in testing/gtest
+// it stabilizes, PDFium code will use forwarding headers in testing/gtest
 // and testing/gmock, instead of directly including files in
 // third_party/googletest.
 
diff --git a/testing/gtest_mac.h b/testing/gtest_mac.h
index 0c0b655..95d7d32 100644
--- a/testing/gtest_mac.h
+++ b/testing/gtest_mac.h
@@ -1,6 +1,7 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright 2010 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+
 #ifndef TESTING_GTEST_MAC_H_
 #define TESTING_GTEST_MAC_H_
 #include <gtest/internal/gtest-port.h>
diff --git a/testing/gtest_mac.mm b/testing/gtest_mac.mm
index b490f55..47912c7 100644
--- a/testing/gtest_mac.mm
+++ b/testing/gtest_mac.mm
@@ -1,6 +1,7 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright 2010 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+
 #import "gtest_mac.h"
 #include <string>
 #include <gtest/gtest.h>
diff --git a/testing/image_diff/BUILD.gn b/testing/image_diff/BUILD.gn
index 141769b..0f9b71b 100644
--- a/testing/image_diff/BUILD.gn
+++ b/testing/image_diff/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -10,7 +10,10 @@
     "image_diff_png.cpp",
     "image_diff_png.h",
   ]
-  configs += [ "../../:pdfium_core_config" ]
+  configs += [
+    "../../:pdfium_strict_config",
+    "../../:pdfium_noshorten_config",
+  ]
   deps = [
     "../../third_party:pdfium_base",
     "../../third_party:png",
diff --git a/testing/image_diff/DEPS b/testing/image_diff/DEPS
index 4bd2335..fcac201 100644
--- a/testing/image_diff/DEPS
+++ b/testing/image_diff/DEPS
@@ -1,5 +1,5 @@
 include_rules = [
-  '+third_party/libpng16',
+  '+third_party/libpng',
   '+third_party/zlib',
 ]
 
diff --git a/testing/image_diff/image_diff.cpp b/testing/image_diff/image_diff.cpp
index c8f9caf..a203bf5 100644
--- a/testing/image_diff/image_diff.cpp
+++ b/testing/image_diff/image_diff.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,12 +8,12 @@
 // The exact format of this tool's output to stdout is important, to match
 // what the run-webkit-tests script expects.
 
-#include <assert.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
 
 #include <algorithm>
+#include <cmath>
 #include <map>
 #include <string>
 #include <vector>
@@ -21,10 +21,9 @@
 #include "core/fxcrt/fx_memory.h"
 #include "testing/image_diff/image_diff_png.h"
 #include "testing/utils/path_service.h"
-#include "third_party/base/logging.h"
 #include "third_party/base/numerics/safe_conversions.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include <windows.h>
 #endif
 
@@ -39,8 +38,9 @@
 
 class Image {
  public:
-  Image() : w_(0), h_(0) {}
-  Image(const Image& image) : w_(image.w_), h_(image.h_), data_(image.data_) {}
+  Image() = default;
+  Image(const Image& image) = default;
+  Image& operator=(const Image& other) = default;
 
   bool has_image() const { return w_ > 0 && h_ > 0; }
   int w() const { return w_; }
@@ -110,8 +110,8 @@
   size_t pixel_address(int x, int y) const { return (y * w_ + x) * 4; }
 
   // Pixel dimensions of the image.
-  int w_;
-  int h_;
+  int w_ = 0;
+  int h_ = 0;
 
   std::vector<uint8_t> data_;
 };
@@ -143,7 +143,36 @@
   *pixels_different += (max_h - h) * max_w;
 }
 
-float PercentageDifferent(const Image& baseline, const Image& actual) {
+struct UnpackedPixel {
+  explicit UnpackedPixel(uint32_t packed)
+      : red(packed & 0xff),
+        green((packed >> 8) & 0xff),
+        blue((packed >> 16) & 0xff),
+        alpha((packed >> 24) & 0xff) {}
+
+  uint8_t red;
+  uint8_t green;
+  uint8_t blue;
+  uint8_t alpha;
+};
+
+uint8_t ChannelDelta(uint8_t baseline_channel, uint8_t actual_channel) {
+  // No casts are necessary because arithmetic operators implicitly convert
+  // `uint8_t` to `int` first. The final delta is always in the range 0 to 255.
+  return std::abs(baseline_channel - actual_channel);
+}
+
+uint8_t MaxPixelPerChannelDelta(const UnpackedPixel& baseline_pixel,
+                                const UnpackedPixel& actual_pixel) {
+  return std::max({ChannelDelta(baseline_pixel.red, actual_pixel.red),
+                   ChannelDelta(baseline_pixel.green, actual_pixel.green),
+                   ChannelDelta(baseline_pixel.blue, actual_pixel.blue),
+                   ChannelDelta(baseline_pixel.alpha, actual_pixel.alpha)});
+}
+
+float PercentageDifferent(const Image& baseline,
+                          const Image& actual,
+                          uint8_t max_pixel_per_channel_delta) {
   int w = std::min(baseline.w(), actual.w());
   int h = std::min(baseline.h(), actual.h());
 
@@ -151,8 +180,17 @@
   int pixels_different = 0;
   for (int y = 0; y < h; ++y) {
     for (int x = 0; x < w; ++x) {
-      if (baseline.pixel_at(x, y) != actual.pixel_at(x, y))
+      const uint32_t baseline_pixel = baseline.pixel_at(x, y);
+      const uint32_t actual_pixel = actual.pixel_at(x, y);
+      if (baseline_pixel == actual_pixel) {
+        continue;
+      }
+
+      if (MaxPixelPerChannelDelta(UnpackedPixel(baseline_pixel),
+                                  UnpackedPixel(actual_pixel)) >
+          max_pixel_per_channel_delta) {
         ++pixels_different;
+      }
     }
   }
 
@@ -198,23 +236,31 @@
   fprintf(
       stderr,
       "Usage:\n"
-      "  %s OPTIONS <compare file> <reference file>\n"
-      "    Compares two files on disk, returning 0 when they are the same;\n"
+      "  %s OPTIONS <compare_file> <reference_file>\n"
+      "    Compares two files on disk, returning 0 when they are the same.\n"
       "    Passing \"--histogram\" additionally calculates a diff of the\n"
-      "    RGBA value histograms. (which is resistant to shifts in layout)\n"
-      "    Passing \"--reverse-byte-order\" additionally assumes the compare\n"
-      "    file has BGRA byte ordering.\n"
-      "  %s --diff <compare file> <reference file> <output file>\n"
-      "    Compares two files on disk, outputs an image that visualizes the\n"
-      "    difference to <output file>\n",
-      binary_name.c_str(), binary_name.c_str());
+      "    RGBA value histograms (which is resistant to shifts in layout).\n"
+      "    Passing \"--reverse-byte-order\" additionally assumes the\n"
+      "    compare file has BGRA byte ordering.\n"
+      "    Passing \"--fuzzy\" additionally allows individual pixels to\n"
+      "    differ by at most 1 on each channel.\n\n"
+      "  %s --diff <compare_file> <reference_file> <output_file>\n"
+      "    Compares two files on disk, and if they differ, outputs an image\n"
+      "    to <output_file> that visualizes the differing pixels as red\n"
+      "    dots.\n\n"
+      "  %s --subtract <compare_file> <reference_file> <output_file>\n"
+      "    Compares two files on disk, and if they differ, outputs an image\n"
+      "    to <output_file> that visualizes the difference as a scaled\n"
+      "    subtraction of pixel values.\n",
+      binary_name.c_str(), binary_name.c_str(), binary_name.c_str());
 }
 
 int CompareImages(const std::string& binary_name,
                   const std::string& file1,
                   const std::string& file2,
                   bool compare_histograms,
-                  bool reverse_byte_order) {
+                  bool reverse_byte_order,
+                  uint8_t max_pixel_per_channel_delta) {
   Image actual_image;
   Image baseline_image;
 
@@ -240,7 +286,8 @@
   }
 
   const char* const diff_name = compare_histograms ? "exact diff" : "diff";
-  float percent = PercentageDifferent(actual_image, baseline_image);
+  float percent = PercentageDifferent(actual_image, baseline_image,
+                                      max_pixel_per_channel_delta);
   const char* const passed = percent > 0.0 ? "failed" : "passed";
   printf("%s: %01.2f%% %s\n", diff_name, percent, passed);
 
@@ -280,14 +327,58 @@
   return same;
 }
 
+bool SubtractImages(const Image& image1, const Image& image2, Image* out) {
+  int w = std::min(image1.w(), image2.w());
+  int h = std::min(image1.h(), image2.h());
+  *out = Image(image1);
+  bool same = (image1.w() == image2.w()) && (image1.h() == image2.h());
+
+  for (int y = 0; y < h; ++y) {
+    for (int x = 0; x < w; ++x) {
+      uint32_t pixel1 = image1.pixel_at(x, y);
+      int32_t r1 = pixel1 & 0xff;
+      int32_t g1 = (pixel1 >> 8) & 0xff;
+      int32_t b1 = (pixel1 >> 16) & 0xff;
+
+      uint32_t pixel2 = image2.pixel_at(x, y);
+      int32_t r2 = pixel2 & 0xff;
+      int32_t g2 = (pixel2 >> 8) & 0xff;
+      int32_t b2 = (pixel2 >> 16) & 0xff;
+
+      int32_t delta_r = r1 - r2;
+      int32_t delta_g = g1 - g2;
+      int32_t delta_b = b1 - b2;
+      same &= (delta_r == 0 && delta_g == 0 && delta_b == 0);
+
+      delta_r = 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;
+      new_pixel |= (delta_g << 8);
+      new_pixel |= (delta_b << 16);
+      out->set_pixel_at(x, y, new_pixel);
+    }
+  }
+  return same;
+}
+
 int DiffImages(const std::string& binary_name,
                const std::string& file1,
                const std::string& file2,
-               const std::string& out_file) {
+               const std::string& out_file,
+               bool do_subtraction,
+               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;
@@ -299,7 +390,9 @@
   }
 
   Image diff_image;
-  bool same = CreateImageDiff(baseline_image, actual_image, &diff_image);
+  bool same = do_subtraction
+                  ? SubtractImages(baseline_image, actual_image, &diff_image)
+                  : CreateImageDiff(baseline_image, actual_image, &diff_image);
   if (same)
     return kStatusSame;
 
@@ -321,11 +414,13 @@
 }
 
 int main(int argc, const char* argv[]) {
-  FXMEM_InitializePartitionAlloc();
+  FX_InitializeMemoryAllocators();
 
   bool histograms = false;
   bool produce_diff_image = false;
+  bool produce_image_subtraction = false;
   bool reverse_byte_order = false;
+  uint8_t max_pixel_per_channel_delta = 0;
   std::string filename1;
   std::string filename2;
   std::string diff_filename;
@@ -343,8 +438,12 @@
       histograms = true;
     } else if (strcmp(arg, "--diff") == 0) {
       produce_diff_image = true;
+    } else if (strcmp(arg, "--subtract") == 0) {
+      produce_image_subtraction = true;
     } else if (strcmp(arg, "--reverse-byte-order") == 0) {
       reverse_byte_order = true;
+    } else if (strcmp(arg, "--fuzzy") == 0) {
+      max_pixel_per_channel_delta = 1;
     }
   }
   if (i < argc)
@@ -354,13 +453,14 @@
   if (i < argc)
     diff_filename = argv[i++];
 
-  if (produce_diff_image) {
+  if (produce_diff_image || produce_image_subtraction) {
     if (!diff_filename.empty()) {
-      return DiffImages(binary_name, filename1, filename2, diff_filename);
+      return DiffImages(binary_name, filename1, filename2, diff_filename,
+                        produce_image_subtraction, reverse_byte_order);
     }
   } else if (!filename2.empty()) {
     return CompareImages(binary_name, filename1, filename2, histograms,
-                         reverse_byte_order);
+                         reverse_byte_order, max_pixel_per_channel_delta);
   }
 
   PrintHelp(binary_name);
diff --git a/testing/image_diff/image_diff_png.cpp b/testing/image_diff/image_diff_png.cpp
index dcd9d69..5a89e35 100644
--- a/testing/image_diff/image_diff_png.cpp
+++ b/testing/image_diff/image_diff_png.cpp
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,8 +16,8 @@
 
 #include <string>
 
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/logging.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/notreached.h"
 
 #ifdef USE_SYSTEM_ZLIB
 #include <zlib.h>
@@ -28,7 +28,7 @@
 #ifdef USE_SYSTEM_LIBPNG
 #include <png.h>
 #else
-#include "third_party/libpng16/png.h"
+#include "third_party/libpng/png.h"
 #endif
 
 namespace image_diff_png {
@@ -92,12 +92,12 @@
                       int pixel_width,
                       uint8_t* rgb,
                       bool* is_opaque) {
+  const uint8_t* pixel_in = rgba;
+  uint8_t* pixel_out = rgb;
   for (int x = 0; x < pixel_width; x++) {
-    const uint8_t* pixel_in = &rgba[x * 4];
-    uint8_t* pixel_out = &rgb[x * 3];
-    pixel_out[0] = pixel_in[0];
-    pixel_out[1] = pixel_in[1];
-    pixel_out[2] = pixel_in[2];
+    memcpy(pixel_out, pixel_in, 3);
+    pixel_in += 4;
+    pixel_out += 3;
   }
 }
 
@@ -148,13 +148,13 @@
                       int pixel_width,
                       uint8_t* rgba,
                       bool* is_opaque) {
+  const uint8_t* pixel_in = rgb;
+  uint8_t* pixel_out = rgba;
   for (int x = 0; x < pixel_width; x++) {
-    const uint8_t* pixel_in = &rgb[x * 3];
-    uint8_t* pixel_out = &rgba[x * 4];
-    pixel_out[0] = pixel_in[0];
-    pixel_out[1] = pixel_in[1];
-    pixel_out[2] = pixel_in[2];
+    memcpy(pixel_out, pixel_in, 3);
     pixel_out[3] = 0xff;
+    pixel_in += 3;
+    pixel_out += 4;
   }
 }
 
@@ -254,8 +254,7 @@
         state->output_channels = 1;
         break;
       default:
-        NOTREACHED();
-        break;
+        NOTREACHED_NORETURN();
     }
   } else if (channels == 4) {
     switch (state->output_format) {
@@ -272,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);
@@ -289,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();
@@ -428,7 +421,7 @@
 #ifdef PNG_TEXT_SUPPORTED
 
 inline char* strdup(const char* str) {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   return _strdup(str);
 #else
   return ::strdup(str);
@@ -461,11 +454,11 @@
   void AddComment(size_t pos, const Comment& comment) {
     png_text_[pos].compression = PNG_TEXT_COMPRESSION_NONE;
     // A PNG comment's key can only be 79 characters long.
-    if (comment.key.length() > 79)
+    if (comment.key.size() > 79)
       return;
     png_text_[pos].key = strdup(comment.key.substr(0, 78).c_str());
     png_text_[pos].text = strdup(comment.text.c_str());
-    png_text_[pos].text_length = comment.text.length();
+    png_text_[pos].text_length = comment.text.size();
 #ifdef PNG_iTXt_SUPPORTED
     png_text_[pos].itxt_length = 0;
     png_text_[pos].lang = 0;
@@ -571,7 +564,7 @@
   switch (format) {
     case FORMAT_BGR:
       converter = ConvertBGRtoRGB;
-      FALLTHROUGH;
+      [[fallthrough]];
 
     case FORMAT_RGB:
       input_color_components = 3;
@@ -610,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 2c39bf0..5d54c95 100644
--- a/testing/image_diff/image_diff_png.h
+++ b/testing/image_diff/image_diff_png.h
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -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/invalid_seekable_read_stream.cpp b/testing/invalid_seekable_read_stream.cpp
index a4116b0..778783c 100644
--- a/testing/invalid_seekable_read_stream.cpp
+++ b/testing/invalid_seekable_read_stream.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,9 +9,8 @@
 
 InvalidSeekableReadStream::~InvalidSeekableReadStream() = default;
 
-bool InvalidSeekableReadStream::ReadBlockAtOffset(void* buffer,
-                                                  FX_FILESIZE offset,
-                                                  size_t size) {
+bool InvalidSeekableReadStream::ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                                                  FX_FILESIZE offset) {
   return false;
 }
 
diff --git a/testing/invalid_seekable_read_stream.h b/testing/invalid_seekable_read_stream.h
index 9322bc6..ae24b1a 100644
--- a/testing/invalid_seekable_read_stream.h
+++ b/testing/invalid_seekable_read_stream.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,17 +6,16 @@
 #define TESTING_INVALID_SEEKABLE_READ_STREAM_H_
 
 #include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/retain_ptr.h"
 
 // A stream used for testing where reads always fail.
 class InvalidSeekableReadStream final : public IFX_SeekableReadStream {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   // IFX_SeekableReadStream overrides:
-  bool ReadBlockAtOffset(void* buffer,
-                         FX_FILESIZE offset,
-                         size_t size) override;
+  bool ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+                         FX_FILESIZE offset) override;
   FX_FILESIZE GetSize() override;
 
  private:
diff --git a/testing/js_embedder_test.cpp b/testing/js_embedder_test.cpp
index 57e6c4d..d6f800c 100644
--- a/testing/js_embedder_test.cpp
+++ b/testing/js_embedder_test.cpp
@@ -1,40 +1,15 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "testing/js_embedder_test.h"
 
-#include "fxjs/cfxjs_engine.h"
-#include "third_party/base/ptr_util.h"
+#include "testing/v8_test_environment.h"
 
-JSEmbedderTest::JSEmbedderTest()
-    : m_pArrayBufferAllocator(
-          pdfium::MakeUnique<CFX_V8ArrayBufferAllocator>()) {}
+JSEmbedderTest::JSEmbedderTest() = default;
 
-JSEmbedderTest::~JSEmbedderTest() {}
+JSEmbedderTest::~JSEmbedderTest() = default;
 
-void JSEmbedderTest::SetUp() {
-  v8::Isolate::CreateParams params;
-  params.array_buffer_allocator = m_pArrayBufferAllocator.get();
-  m_pIsolate.reset(v8::Isolate::New(params));
-
-  EmbedderTest::SetExternalIsolate(isolate());
-  EmbedderTest::SetUp();
-
-  v8::Isolate::Scope isolate_scope(isolate());
-  v8::HandleScope handle_scope(isolate());
-  FXJS_PerIsolateData::SetUp(isolate());
-  m_Engine = pdfium::MakeUnique<CFXJS_Engine>(isolate());
-  m_Engine->InitializeEngine();
-}
-
-void JSEmbedderTest::TearDown() {
-  m_Engine->ReleaseEngine();
-  m_Engine.reset();
-  EmbedderTest::TearDown();
-  m_pIsolate.reset();
-}
-
-v8::Local<v8::Context> JSEmbedderTest::GetV8Context() {
-  return m_Engine->GetV8Context();
+v8::Isolate* JSEmbedderTest::isolate() const {
+  return V8TestEnvironment::GetInstance()->isolate();
 }
diff --git a/testing/js_embedder_test.h b/testing/js_embedder_test.h
index dca0bd7..dda285a 100644
--- a/testing/js_embedder_test.h
+++ b/testing/js_embedder_test.h
@@ -1,35 +1,22 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef TESTING_JS_EMBEDDER_TEST_H_
 #define TESTING_JS_EMBEDDER_TEST_H_
 
-#include <memory>
-
-#include "fxjs/cfx_v8.h"
 #include "testing/embedder_test.h"
-#include "v8/include/v8.h"
 
-class CFXJS_Engine;
-class CFX_V8ArrayBufferAllocator;
+namespace v8 {
+class Isolate;
+}  // namespace v8
 
 class JSEmbedderTest : public EmbedderTest {
  public:
   JSEmbedderTest();
   ~JSEmbedderTest() override;
 
-  void SetUp() override;
-  void TearDown() override;
-
-  v8::Isolate* isolate() const { return m_pIsolate.get(); }
-  CFXJS_Engine* engine() const { return m_Engine.get(); }
-  v8::Local<v8::Context> GetV8Context();
-
- private:
-  std::unique_ptr<CFX_V8ArrayBufferAllocator> m_pArrayBufferAllocator;
-  std::unique_ptr<v8::Isolate, CFX_V8IsolateDeleter> m_pIsolate;
-  std::unique_ptr<CFXJS_Engine> m_Engine;
+  v8::Isolate* isolate() const;
 };
 
 #endif  // TESTING_JS_EMBEDDER_TEST_H_
diff --git a/testing/pdf_test_environment.cpp b/testing/pdf_test_environment.cpp
new file mode 100644
index 0000000..2865027
--- /dev/null
+++ b/testing/pdf_test_environment.cpp
@@ -0,0 +1,20 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/pdf_test_environment.h"
+
+#include "core/fxge/cfx_gemodule.h"
+
+PDFTestEnvironment::PDFTestEnvironment() = default;
+
+PDFTestEnvironment::~PDFTestEnvironment() = default;
+
+// testing::Environment:
+void PDFTestEnvironment::SetUp() {
+  CFX_GEModule::Create(test_fonts_.font_paths());
+}
+
+void PDFTestEnvironment::TearDown() {
+  CFX_GEModule::Destroy();
+}
diff --git a/testing/pdf_test_environment.h b/testing/pdf_test_environment.h
new file mode 100644
index 0000000..818d8e4
--- /dev/null
+++ b/testing/pdf_test_environment.h
@@ -0,0 +1,24 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_PDF_TEST_ENVIRONMENT_H_
+#define TESTING_PDF_TEST_ENVIRONMENT_H_
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/test_fonts.h"
+
+class PDFTestEnvironment : public testing::Environment {
+ public:
+  PDFTestEnvironment();
+  ~PDFTestEnvironment() override;
+
+  // testing::Environment:
+  void SetUp() override;
+  void TearDown() override;
+
+ private:
+  TestFonts test_fonts_;
+};
+
+#endif  // TESTING_PDF_TEST_ENVIRONMENT_H_
diff --git a/testing/pseudo_retainable.h b/testing/pseudo_retainable.h
index c4390d6..6dbfffc 100644
--- a/testing/pseudo_retainable.h
+++ b/testing/pseudo_retainable.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,8 +8,8 @@
 class PseudoRetainable {
  public:
   PseudoRetainable() = default;
-  void Retain() { ++retain_count_; }
-  void Release() {
+  void Retain() const { ++retain_count_; }
+  void Release() const {
     if (++release_count_ == retain_count_)
       alive_ = false;
   }
@@ -18,9 +18,9 @@
   int release_count() const { return release_count_; }
 
  private:
-  bool alive_ = true;
-  int retain_count_ = 0;
-  int release_count_ = 0;
+  mutable bool alive_ = true;
+  mutable int retain_count_ = 0;
+  mutable int release_count_ = 0;
 };
 
 #endif  // TESTING_PSEUDO_RETAINABLE_H_
diff --git a/testing/range_set.cpp b/testing/range_set.cpp
index 2fc540f..725fd7d 100644
--- a/testing/range_set.cpp
+++ b/testing/range_set.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,9 +7,11 @@
 #include <algorithm>
 
 #include "core/fxcrt/fx_system.h"
+#include "third_party/base/check.h"
 
-RangeSet::RangeSet() {}
-RangeSet::~RangeSet() {}
+RangeSet::RangeSet() = default;
+
+RangeSet::~RangeSet() = default;
 
 bool RangeSet::Contains(const Range& range) const {
   if (IsEmptyRange(range))
@@ -51,15 +53,14 @@
 
   --end;
 
-  const int new_start = std::min<size_t>(start->first, fixed_range.first);
-  const int new_end = std::max(end->second, fixed_range.second);
-
+  const size_t new_start = std::min(start->first, fixed_range.first);
+  const size_t new_end = std::max(end->second, fixed_range.second);
   ranges_.erase(start, ++end);
   ranges_.insert(Range(new_start, new_end));
 }
 
 void RangeSet::Union(const RangeSet& range_set) {
-  ASSERT(&range_set != this);
+  DCHECK(&range_set != this);
   for (const auto& it : range_set.ranges())
     Union(it);
 }
diff --git a/testing/range_set.h b/testing/range_set.h
index 6ed24bd..7ba10df 100644
--- a/testing/range_set.h
+++ b/testing/range_set.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/resources/CMYK-alpha.jpf b/testing/resources/CMYK-alpha.jpf
new file mode 100644
index 0000000..538e6d6
--- /dev/null
+++ b/testing/resources/CMYK-alpha.jpf
Binary files differ
diff --git a/testing/resources/CMYK.jpf b/testing/resources/CMYK.jpf
new file mode 100644
index 0000000..00f2ef9
--- /dev/null
+++ b/testing/resources/CMYK.jpf
Binary files differ
diff --git a/testing/resources/RGB-alpha.jp2 b/testing/resources/RGB-alpha.jp2
new file mode 100644
index 0000000..4ab41da
--- /dev/null
+++ b/testing/resources/RGB-alpha.jp2
Binary files differ
diff --git a/testing/resources/RGB.jp2 b/testing/resources/RGB.jp2
new file mode 100644
index 0000000..7b7d428
--- /dev/null
+++ b/testing/resources/RGB.jp2
Binary files differ
diff --git a/testing/resources/annot_javascript.in b/testing/resources/annot_javascript.in
new file mode 100644
index 0000000..fe9ac46
--- /dev/null
+++ b/testing/resources/annot_javascript.in
@@ -0,0 +1,42 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [4 0 R]
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 612 792]
+  /Kids [3 0 R]
+  /Count 1
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /Annots [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /Rect [85 721 153 735]
+  /FT /Tx
+  /P 3 0 R
+  /T (Widget)
+  /AA <<
+    /F <<
+      /JS (AFDate_FormatEx\("yyyy-mm-dd"\);)
+      /S /JavaScript
+    >>
+  >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/annot_javascript.pdf b/testing/resources/annot_javascript.pdf
new file mode 100644
index 0000000..fbeb5df
--- /dev/null
+++ b/testing/resources/annot_javascript.pdf
@@ -0,0 +1,53 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [4 0 R]
+  >>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [0 0 612 792]
+  /Kids [3 0 R]
+  /Count 1
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /Annots [4 0 R]
+>>
+endobj
+4 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /Rect [85 721 153 735]
+  /FT /Tx
+  /P 3 0 R
+  /T (Widget)
+  /AA <<
+    /F <<
+      /JS (AFDate_FormatEx\("yyyy-mm-dd"\);)
+      /S /JavaScript
+    >>
+  >>
+>>
+endobj
+xref
+0 5
+0000000000 65535 f 
+0000000015 00000 n 
+0000000108 00000 n 
+0000000197 00000 n 
+0000000292 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 5
+>>
+startxref
+504
+%%EOF
diff --git a/testing/resources/annots.in b/testing/resources/annots.in
new file mode 100644
index 0000000..cf575f5
--- /dev/null
+++ b/testing/resources/annots.in
@@ -0,0 +1,362 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [23 0 R]
+    /DR <<
+      /Font <<
+        /F1 7 0 R
+      >>
+    >>
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 2
+  /Kids [3 0 R 4 0 R]
+  /MediaBox [0 0 612 792]
+  /CropBox [0 0 612 792]
+  /Resources <<
+    /Font <<
+      /F1 7 0 R
+      /F2 8 0 R
+    >>
+    /ProcSet [/PDF /Text /ImageC]
+    /ExtGState <<
+      /GS0 24 0 R
+    >>
+  >>
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 5 0 R
+  /Annots [15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 6 0 R
+  /Annots [15 0 R 16 0 R 26 0 R]
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+70 700 Td
+/F1 18 Tf
+(Link Annotations - Page 1) Tj
+0 -65 Td
+/F2 14 Tf
+(1. Link with destination to first page) Tj
+10 -20 Td
+/F2 14 Tf
+(2. Link with destination to second page) Tj
+-12 -84 Td
+/F2 10 Tf
+(PDF Reference, Version 1.7, Section 8.4.5 defines Annotations) Tj
+2 -53 Td
+(3.  An example of Highlight with text notes) Tj
+0 -18 Td
+(https://pdfium.googlesource.com/pdfium is link in plain text, not link annotation. These are referred to) Tj
+0 -17 Td
+(as WebLinks in PDFium.)Tj
+ET
+endstream
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+70 700 Td
+/F1 18 Tf
+(Link Annotations - Page 2) Tj
+0 -65 Td
+/F2 14 Tf
+(1. Link with destination to first page) Tj
+10 -20 Td
+/F2 14 Tf
+(2. Link with destination to second page) Tj
+ET
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 9 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  {{streamlen}}
+  /BBox [293 530 349 542]
+  /Resources <<
+    /XObject <<
+      /Form0 10 0 R
+    >>
+    /ExtGState <<
+      /GS0 25 0 R
+    >>
+  >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+{{object 10 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Group <<
+    /S /Transparency
+  >>
+  {{streamlen}}
+  /BBox [293 530 349 542]
+>>
+stream
+1.0 1.0 0.0 rg
+293 530 m
+349 530 l
+349 542 l
+293 542 l
+h f
+endstream
+endobj
+{{object 11 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  {{streamlen}}
+  /BBox [83 440 178 453]
+  /Resources <<
+    /XObject <<
+      /Form0 12 0 R
+    >>
+    /ExtGState <<
+      /GS0 25 0 R
+    >>
+  >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+{{object 12 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Group <<
+    /S /Transparency
+  >>
+  {{streamlen}}
+  /BBox [83 440 178 453]
+>>
+stream
+0.0 1.0 1.0 rg
+83 440 m
+178 440 l
+178 453 l
+83 453 l
+h f
+endstream
+endobj
+{{object 13 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  {{streamlen}}
+  /BBox [149 476 191 487]
+  /Resources <<
+    /XObject <<
+      /Form0 14 0 R
+    >>
+    /ExtGState <<
+      /GS0 25 0 R
+    >>
+  >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+{{object 14 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Group <<
+    /S /Transparency
+  >>
+  {{streamlen}}
+  /BBox [149 476 191 487]
+>>
+stream
+0.0 1.0 0.0 rg
+149 476 m
+191 476 l
+191 487 l
+149 487 l
+h f
+endstream
+endobj
+{{object 15 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [69 633 542 653]
+  /Dest [3 0 R /XYZ 200 725 0]
+  /F 4
+>>
+endobj
+{{object 16 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [80 613 542 633]
+  /Dest [4 0 R /XYZ 200 725 0]
+  /F 4
+>>
+endobj
+{{object 17 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [66 529 196 544]
+  /A <<
+    /Type /Action
+    /URI (https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf)
+    /S /URI
+  >>
+  /F 4
+>>
+endobj
+{{object 18 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [83 440 178 453]
+  /QuadPoints [83 453 178 453 83 440 178 440]
+  /A <<
+    /Type /Action
+    /URI (https://cs.chromium.org/chromium/src/third_party/pdfium/public/fpdf_text.h)
+    /S /URI
+  >>
+  /F 4
+>>
+endobj
+{{object 19 0}} <<
+  /Type /Annot
+  /Subtype /Highlight
+  /AP <<
+    /N 9 0 R
+  >>
+  /NM (Highlight-1)
+  /F 4
+  /QuadPoints [293 542 349 542 293 530 349 530]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+{{object 20 0}} <<
+  /Type /Annot
+  /Subtype /Highlight
+  /AP <<
+    /N 11 0 R
+  >>
+  /NM (Highlight-2)
+  /F 4
+  /QuadPoints [83 453 178 453 83 440 178 440]
+  /P 3 0 R
+  /C [0.26667 0.78431 0.96078]
+  /Rect [83 440 178 453]
+>>
+endobj
+{{object 21 0}} <<
+  /Type /Annot
+  /Subtype /Popup
+  /Parent 22 0 R
+  /Rect [191 377 443 488]
+>>
+endobj
+{{object 22 0}} <<
+  /Type /Annot
+  /Subtype /Highlight
+  /Popup 21 0 R
+  /AP <<
+    /N 13 0 R
+  >>
+  /NM (Highlight-With-Popup-1)
+  /Contents (Text Note)
+  /QuadPoints [149 487 191 487 149 476 191 476]
+  /P 3 0 R
+  /C [0.14902 0.90196 0]
+  /Rect [149 476 191 487]
+  /F 4
+>>
+endobj
+{{object 23 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 131072
+  /T (Combo1)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [70 350 170 380]
+  /Opt [(Highlight) (Link) (Popup) (Widget)]
+>>
+endobj
+{{object 24 0}} <<
+  /ca 1
+  /Type /ExtGState
+  /CA 1
+  /BM /Normal
+>>
+endobj
+{{object 25 0}} <<
+  /ca 1
+  /Type /ExtGState
+  /CA 1
+  /AIS false
+  /BM /Multiply
+>>
+endobj
+{{object 26 0}} <<
+  /Type /Annot
+  /Subtype /Square
+  /Border [0 0 2]
+  /C [1 0 0]
+  /F 4
+  /P 3 0 R
+  /Rect [50 100 60 120]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/annots.pdf b/testing/resources/annots.pdf
new file mode 100644
index 0000000..745346c
--- /dev/null
+++ b/testing/resources/annots.pdf
@@ -0,0 +1,395 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [23 0 R]
+    /DR <<
+      /Font <<
+        /F1 7 0 R
+      >>
+    >>
+  >>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 2
+  /Kids [3 0 R 4 0 R]
+  /MediaBox [0 0 612 792]
+  /CropBox [0 0 612 792]
+  /Resources <<
+    /Font <<
+      /F1 7 0 R
+      /F2 8 0 R
+    >>
+    /ProcSet [/PDF /Text /ImageC]
+    /ExtGState <<
+      /GS0 24 0 R
+    >>
+  >>
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 5 0 R
+  /Annots [15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R]
+>>
+endobj
+4 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 6 0 R
+  /Annots [15 0 R 16 0 R 26 0 R]
+>>
+endobj
+5 0 obj <<
+  /Length 486
+>>
+stream
+BT
+70 700 Td
+/F1 18 Tf
+(Link Annotations - Page 1) Tj
+0 -65 Td
+/F2 14 Tf
+(1. Link with destination to first page) Tj
+10 -20 Td
+/F2 14 Tf
+(2. Link with destination to second page) Tj
+-12 -84 Td
+/F2 10 Tf
+(PDF Reference, Version 1.7, Section 8.4.5 defines Annotations) Tj
+2 -53 Td
+(3.  An example of Highlight with text notes) Tj
+0 -18 Td
+(https://pdfium.googlesource.com/pdfium is link in plain text, not link annotation. These are referred to) Tj
+0 -17 Td
+(as WebLinks in PDFium.)Tj
+ET
+endstream
+endobj
+6 0 obj <<
+  /Length 185
+>>
+stream
+BT
+70 700 Td
+/F1 18 Tf
+(Link Annotations - Page 2) Tj
+0 -65 Td
+/F2 14 Tf
+(1. Link with destination to first page) Tj
+10 -20 Td
+/F2 14 Tf
+(2. Link with destination to second page) Tj
+ET
+endstream
+endobj
+7 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+8 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+9 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Length 18
+  /BBox [293 530 349 542]
+  /Resources <<
+    /XObject <<
+      /Form0 10 0 R
+    >>
+    /ExtGState <<
+      /GS0 25 0 R
+    >>
+  >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+10 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Group <<
+    /S /Transparency
+  >>
+  /Length 59
+  /BBox [293 530 349 542]
+>>
+stream
+1.0 1.0 0.0 rg
+293 530 m
+349 530 l
+349 542 l
+293 542 l
+h f
+endstream
+endobj
+11 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Length 18
+  /BBox [83 440 178 453]
+  /Resources <<
+    /XObject <<
+      /Form0 12 0 R
+    >>
+    /ExtGState <<
+      /GS0 25 0 R
+    >>
+  >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+12 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Group <<
+    /S /Transparency
+  >>
+  /Length 57
+  /BBox [83 440 178 453]
+>>
+stream
+0.0 1.0 1.0 rg
+83 440 m
+178 440 l
+178 453 l
+83 453 l
+h f
+endstream
+endobj
+13 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Length 18
+  /BBox [149 476 191 487]
+  /Resources <<
+    /XObject <<
+      /Form0 14 0 R
+    >>
+    /ExtGState <<
+      /GS0 25 0 R
+    >>
+  >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+14 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Group <<
+    /S /Transparency
+  >>
+  /Length 59
+  /BBox [149 476 191 487]
+>>
+stream
+0.0 1.0 0.0 rg
+149 476 m
+191 476 l
+191 487 l
+149 487 l
+h f
+endstream
+endobj
+15 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [69 633 542 653]
+  /Dest [3 0 R /XYZ 200 725 0]
+  /F 4
+>>
+endobj
+16 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [80 613 542 633]
+  /Dest [4 0 R /XYZ 200 725 0]
+  /F 4
+>>
+endobj
+17 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [66 529 196 544]
+  /A <<
+    /Type /Action
+    /URI (https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf)
+    /S /URI
+  >>
+  /F 4
+>>
+endobj
+18 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [83 440 178 453]
+  /QuadPoints [83 453 178 453 83 440 178 440]
+  /A <<
+    /Type /Action
+    /URI (https://cs.chromium.org/chromium/src/third_party/pdfium/public/fpdf_text.h)
+    /S /URI
+  >>
+  /F 4
+>>
+endobj
+19 0 obj <<
+  /Type /Annot
+  /Subtype /Highlight
+  /AP <<
+    /N 9 0 R
+  >>
+  /NM (Highlight-1)
+  /F 4
+  /QuadPoints [293 542 349 542 293 530 349 530]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+20 0 obj <<
+  /Type /Annot
+  /Subtype /Highlight
+  /AP <<
+    /N 11 0 R
+  >>
+  /NM (Highlight-2)
+  /F 4
+  /QuadPoints [83 453 178 453 83 440 178 440]
+  /P 3 0 R
+  /C [0.26667 0.78431 0.96078]
+  /Rect [83 440 178 453]
+>>
+endobj
+21 0 obj <<
+  /Type /Annot
+  /Subtype /Popup
+  /Parent 22 0 R
+  /Rect [191 377 443 488]
+>>
+endobj
+22 0 obj <<
+  /Type /Annot
+  /Subtype /Highlight
+  /Popup 21 0 R
+  /AP <<
+    /N 13 0 R
+  >>
+  /NM (Highlight-With-Popup-1)
+  /Contents (Text Note)
+  /QuadPoints [149 487 191 487 149 476 191 476]
+  /P 3 0 R
+  /C [0.14902 0.90196 0]
+  /Rect [149 476 191 487]
+  /F 4
+>>
+endobj
+23 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 131072
+  /T (Combo1)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [70 350 170 380]
+  /Opt [(Highlight) (Link) (Popup) (Widget)]
+>>
+endobj
+24 0 obj <<
+  /ca 1
+  /Type /ExtGState
+  /CA 1
+  /BM /Normal
+>>
+endobj
+25 0 obj <<
+  /ca 1
+  /Type /ExtGState
+  /CA 1
+  /AIS false
+  /BM /Multiply
+>>
+endobj
+26 0 obj <<
+  /Type /Annot
+  /Subtype /Square
+  /Border [0 0 2]
+  /C [1 0 0]
+  /F 4
+  /P 3 0 R
+  /Rect [50 100 60 120]
+>>
+endobj
+xref
+0 27
+0000000000 65535 f 
+0000000015 00000 n 
+0000000169 00000 n 
+0000000439 00000 n 
+0000000583 00000 n 
+0000000685 00000 n 
+0000001223 00000 n 
+0000001460 00000 n 
+0000001538 00000 n 
+0000001614 00000 n 
+0000001864 00000 n 
+0000002087 00000 n 
+0000002337 00000 n 
+0000002557 00000 n 
+0000002808 00000 n 
+0000003031 00000 n 
+0000003171 00000 n 
+0000003311 00000 n 
+0000003558 00000 n 
+0000003842 00000 n 
+0000004059 00000 n 
+0000004286 00000 n 
+0000004384 00000 n 
+0000004659 00000 n 
+0000004849 00000 n 
+0000004920 00000 n 
+0000005006 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 27
+>>
+startxref
+5135
+%%EOF
diff --git a/testing/resources/annots_action_handling.in b/testing/resources/annots_action_handling.in
new file mode 100644
index 0000000..e5c728b
--- /dev/null
+++ b/testing/resources/annots_action_handling.in
@@ -0,0 +1,132 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /Names 12 0 R
+  /AcroForm [6 0 R 7 0 R]
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 2
+  /Kids [3 0 R 4 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R]
+  /Contents 5 0 R
+  /Tabs /R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Tabs /R
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+70 340 Td
+14 Tf
+(External Link ) Tj
+0 -35 Td
+14 Tf
+(Internal Link ) Tj
+0 -35 Td
+14 Tf
+(Link1 to top ) Tj
+0 -35 Td
+14 Tf
+(Link2 to top ) Tj
+ET
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /Parent 3 0 R
+  /T (TextField)
+  /Rect [69 670 220 690]
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /Rect [69 360 220 380]
+  /A <<
+    /URI (https://www.google.com)
+    /S /URI
+  >>
+  /F 4
+  /T (button)
+  /Ff 65536
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [69 338 180 358]
+  /A <<
+    /Type /Action
+    /S /URI
+    /URI (https://cs.chromium.org/)
+  >>
+  /F 4
+>>
+endobj
+{{object 9 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [69 305 180 325]
+  /BS <<
+    /W 0
+  >>
+  /Dest [4 0 R /XYZ 200 725 0]
+  /F 4
+>>
+{{object 10 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [69 270 180 290]
+  /BS <<
+    /W 0
+  >>
+  /Dest /top
+  /F 4
+>>
+{{object 11 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [69 235 180 255]
+  /BS <<
+    /W 0
+  >>
+  /Dest (target10)
+  /F 4
+>>
+{{object 12 0}} <<
+  /Dests 13 0 R
+>>
+endobj
+{{object 13 0}} <<
+  /Names [
+    (target10) 14 0 R
+    /top 14 0 R
+  ]
+>>
+endobj
+{{object 14 0}} <<
+  /D [3 0 R /XYZ 100 200 0]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/annots_action_handling.pdf b/testing/resources/annots_action_handling.pdf
new file mode 100644
index 0000000..b8b6c2a
--- /dev/null
+++ b/testing/resources/annots_action_handling.pdf
@@ -0,0 +1,153 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /Names 12 0 R
+  /AcroForm [6 0 R 7 0 R]
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 2
+  /Kids [3 0 R 4 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R]
+  /Contents 5 0 R
+  /Tabs /R
+>>
+endobj
+4 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Tabs /R
+>>
+endobj
+5 0 obj <<
+  /Length 145
+>>
+stream
+BT
+70 340 Td
+14 Tf
+(External Link ) Tj
+0 -35 Td
+14 Tf
+(Internal Link ) Tj
+0 -35 Td
+14 Tf
+(Link1 to top ) Tj
+0 -35 Td
+14 Tf
+(Link2 to top ) Tj
+ET
+endstream
+endobj
+6 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /Parent 3 0 R
+  /T (TextField)
+  /Rect [69 670 220 690]
+>>
+endobj
+7 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /Rect [69 360 220 380]
+  /A <<
+    /URI (https://www.google.com)
+    /S /URI
+  >>
+  /F 4
+  /T (button)
+  /Ff 65536
+>>
+endobj
+8 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [69 338 180 358]
+  /A <<
+    /Type /Action
+    /S /URI
+    /URI (https://cs.chromium.org/)
+  >>
+  /F 4
+>>
+endobj
+9 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [69 305 180 325]
+  /BS <<
+    /W 0
+  >>
+  /Dest [4 0 R /XYZ 200 725 0]
+  /F 4
+>>
+10 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [69 270 180 290]
+  /BS <<
+    /W 0
+  >>
+  /Dest /top
+  /F 4
+>>
+11 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [69 235 180 255]
+  /BS <<
+    /W 0
+  >>
+  /Dest (target10)
+  /F 4
+>>
+12 0 obj <<
+  /Dests 13 0 R
+>>
+endobj
+13 0 obj <<
+  /Names [
+    (target10) 14 0 R
+    /top 14 0 R
+  ]
+>>
+endobj
+14 0 obj <<
+  /D [3 0 R /XYZ 100 200 0]
+>>
+endobj
+xref
+0 15
+0000000000 65535 f 
+0000000015 00000 n 
+0000000110 00000 n 
+0000000179 00000 n 
+0000000309 00000 n 
+0000000371 00000 n 
+0000000568 00000 n 
+0000000691 00000 n 
+0000000874 00000 n 
+0000001038 00000 n 
+0000001170 00000 n 
+0000001285 00000 n 
+0000001406 00000 n 
+0000001444 00000 n 
+0000001519 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 15
+>>
+startxref
+1569
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/bad_dict_keys.in b/testing/resources/bad_dict_keys.in
new file mode 100644
index 0000000..3f53c28
--- /dev/null
+++ b/testing/resources/bad_dict_keys.in
@@ -0,0 +1,23 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 300 300]
+  /Count 1
+  /Kids [3 0 R]
+  / [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bad_dict_keys.pdf b/testing/resources/bad_dict_keys.pdf
new file mode 100644
index 0000000..b55c434
--- /dev/null
+++ b/testing/resources/bad_dict_keys.pdf
@@ -0,0 +1,33 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [0 0 300 300]
+  /Count 1
+  /Kids [3 0 R]
+  / [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+>>
+endobj
+xref
+0 4
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000169 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 4
+>>
+startxref
+220
+%%EOF
diff --git a/testing/resources/bigtable_mini.in b/testing/resources/bigtable_mini.in
new file mode 100644
index 0000000..7e80992
--- /dev/null
+++ b/testing/resources/bigtable_mini.in
@@ -0,0 +1,113 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+  /Resources <<
+    /ProcSet [/PDF /ImageB /Text]
+    /Font <<
+      /F1 5 0 R
+      /F2 6 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q BT
+1 0 0 1 250 667 Tm
+/F2 8.96638 Tf
+-243.635 -15.84 Td
+(f)Tj
+/F1 8.96638 Tf
+4.55491 0 Td
+(f)Tj
+2.87476 0 Td
+(ay)Tj
+7.79253 0 Td
+(,jef)Tj
+11.506 0 Td
+(f,sanjay)Tj
+27.4558 0 Td
+(,wilsonh,k)Tj
+37.1801 0 Td
+(err)Tj
+9.58372 0 Td
+(,m3b,tushar)Tj
+41.8537 0 Td
+(,k)Tj
+11.6349 0 Td
+(es,gruber)Tj
+/F2 8.96638 Tf
+32.9694 0 Td
+(g)Tj
+/F1 8.96638 Tf
+4.55491 0 Td
+(@google.com)Tj
+ET Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /FirstChar 102
+  /BaseFont /RFSQHQ+CMSY9
+  /FontDescriptor 7 0 R
+  /LastChar 103
+  /Widths [508 508]
+>>
+endobj
+{{object 7 0}} <<
+  /Type /FontDescriptor
+  /Ascent 750
+  /CapHeight 750
+  /CharSet (/braceleft/braceright)
+  /Descent -250
+  /Flags 4
+  /FontBBox [0 -250 440 750]
+  /FontFile3 8 0 R
+  /FontName /RFSQHQ+CMSY9
+  /ItalicAngle 0
+  /StemV 65
+>>
+endobj
+{{object 8 0}} <<
+  /Subtype /Type1C
+  /Filter [/ASCII85Decode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+GhQY<?t!MPA7Xa1EXNBL#h4%k%?@du#q5_5B)EgioL"p)pKs!j9@_A!X@n"A#^s/nr,0aY?!i,5R?>nA
+43BRuTX#t%4ekD2_c]pSd*g?I_(dmV-o3k<:Veup,U50*Ylr.i;$bHCc:fghe5L>1a\`<FkpTR<8hEdi
+.SEJ:>O%`N>>SLd>,:)GT9<BBa2#L+Pa9V1&Ao("^^P</!u&Ql7PhdY9T5#6IePGiOgOaNdLsRg)OHi+
+n+a.f[/4_7lnp:OXP)%6XER"WK`:C2*&F%pl[L]/LH0S#rJk:S[coEjaA0p=l`BI4BP[$LCn09862jKs
+Y)F7W^!5B(h.[kDUY*1W\e@l8mE%N?^8RN2qNUCce!bWPjLtF8=4Zg,R,1!:HR6^V/b\TIh0Z`11`hZZ
+*BO'Za1C[ph)dSm>aE#>k-A'_h4)!bqJD$jjj@/_bu*BN?.Ud@HV0Y&l1Vg$1F)e^]:6ER3IV(Tbj1;S
+BposQ0Df/MDiTcoYO4BSY\&*%<aJs1;J/:.>?>$UG7-pseF)R.T'a;@,PN5BX]sWQf<*o6H:RGZ*f^Pj
+qkd,*mVA#1lAJeH\%d!)GAm3[%_gAF5OB8mr>(pE^5_Fj[i?3Bq0X7/D)6E](`9a_gaUoK~>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bigtable_mini.pdf b/testing/resources/bigtable_mini.pdf
new file mode 100644
index 0000000..adb3e02
--- /dev/null
+++ b/testing/resources/bigtable_mini.pdf
@@ -0,0 +1,128 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+  /Resources <<
+    /ProcSet [/PDF /ImageB /Text]
+    /Font <<
+      /F1 5 0 R
+      /F2 6 0 R
+    >>
+  >>
+>>
+endobj
+4 0 obj <<
+  /Length 374
+>>
+stream
+q BT
+1 0 0 1 250 667 Tm
+/F2 8.96638 Tf
+-243.635 -15.84 Td
+(f)Tj
+/F1 8.96638 Tf
+4.55491 0 Td
+(f)Tj
+2.87476 0 Td
+(ay)Tj
+7.79253 0 Td
+(,jef)Tj
+11.506 0 Td
+(f,sanjay)Tj
+27.4558 0 Td
+(,wilsonh,k)Tj
+37.1801 0 Td
+(err)Tj
+9.58372 0 Td
+(,m3b,tushar)Tj
+41.8537 0 Td
+(,k)Tj
+11.6349 0 Td
+(es,gruber)Tj
+/F2 8.96638 Tf
+32.9694 0 Td
+(g)Tj
+/F1 8.96638 Tf
+4.55491 0 Td
+(@google.com)Tj
+ET Q
+endstream
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+6 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /FirstChar 102
+  /BaseFont /RFSQHQ+CMSY9
+  /FontDescriptor 7 0 R
+  /LastChar 103
+  /Widths [508 508]
+>>
+endobj
+7 0 obj <<
+  /Type /FontDescriptor
+  /Ascent 750
+  /CapHeight 750
+  /CharSet (/braceleft/braceright)
+  /Descent -250
+  /Flags 4
+  /FontBBox [0 -250 440 750]
+  /FontFile3 8 0 R
+  /FontName /RFSQHQ+CMSY9
+  /ItalicAngle 0
+  /StemV 65
+>>
+endobj
+8 0 obj <<
+  /Subtype /Type1C
+  /Filter [/ASCII85Decode /FlateDecode]
+  /Length 640
+>>
+stream
+GhQY<?t!MPA7Xa1EXNBL#h4%k%?@du#q5_5B)EgioL"p)pKs!j9@_A!X@n"A#^s/nr,0aY?!i,5R?>nA
+43BRuTX#t%4ekD2_c]pSd*g?I_(dmV-o3k<:Veup,U50*Ylr.i;$bHCc:fghe5L>1a\`<FkpTR<8hEdi
+.SEJ:>O%`N>>SLd>,:)GT9<BBa2#L+Pa9V1&Ao("^^P</!u&Ql7PhdY9T5#6IePGiOgOaNdLsRg)OHi+
+n+a.f[/4_7lnp:OXP)%6XER"WK`:C2*&F%pl[L]/LH0S#rJk:S[coEjaA0p=l`BI4BP[$LCn09862jKs
+Y)F7W^!5B(h.[kDUY*1W\e@l8mE%N?^8RN2qNUCce!bWPjLtF8=4Zg,R,1!:HR6^V/b\TIh0Z`11`hZZ
+*BO'Za1C[ph)dSm>aE#>k-A'_h4)!bqJD$jjj@/_bu*BN?.Ud@HV0Y&l1Vg$1F)e^]:6ER3IV(Tbj1;S
+BposQ0Df/MDiTcoYO4BSY\&*%<aJs1;J/:.>?>$UG7-pseF)R.T'a;@,PN5BX]sWQf<*o6H:RGZ*f^Pj
+qkd,*mVA#1lAJeH\%d!)GAm3[%_gAF5OB8mr>(pE^5_Fj[i?3Bq0X7/D)6E](`9a_gaUoK~>
+endstream
+endobj
+xref
+0 9
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000333 00000 n 
+0000000759 00000 n 
+0000000837 00000 n 
+0000000993 00000 n 
+0000001234 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 9
+>>
+startxref
+1985
+%%EOF
diff --git a/testing/resources/bookmarks.in b/testing/resources/bookmarks.in
index 793f6ae..b0489a5 100644
--- a/testing/resources/bookmarks.in
+++ b/testing/resources/bookmarks.in
@@ -2,7 +2,7 @@
 {{object 1 0}} <<
   /Type /Catalog
   /Pages 2 0 R
-  /Outlines 14 0 R
+  /Outlines 8 0 R
 >>
 endobj
 {{object 2 0}} <<
@@ -19,9 +19,11 @@
   /Type /Page
   /Parent 2 0 R
   /Resources <<
-    /Font <</F1 15 0 R>>
+    /Font <<
+      /F1 5 0 R
+    >>
   >>
-  /Contents [21 0 R]
+  /Contents [6 0 R]
   /MediaBox [0 0 612 792]
 >>
 endobj
@@ -30,46 +32,24 @@
   /Type /Page
   /Parent 2 0 R
   /Resources  <<
-    /Font <</F1 15 0 R>>
+    /Font <<
+      /F1 5 0 R
+    >>
   >>
-  /Contents [22 0 R]
+  /Contents [7 0 R]
   /MediaBox [0 0 612 792]
 >>
 endobj
-% First bookmark
-{{object 10 0}} <<
-  /Title (A Good Beginning)
-  /Parent 14 0 R
-  /Next 11 0 R
-  /Dest (foo)
->>
-endobj
-% Last bookmark
-{{object 11 0}} <<
-  /Title (A Good Ending)
-  /Parent 14 0 R
-  /Prev 10 0 R
-  /Dest (bar)
->>
-endobj
-% Root bookmark
-{{object 14 0}} <<
-  /Type /Outlines
-  /First 10 0 R
-  /Last  11 0 R
-  /Count 2
->>
-endobj
 % Font resource.
-{{object 15 0}} <<
+{{object 5 0}} <<
   /Type /Font
   /Subtype /Type1
   /BaseFont /Arial
 >>
 endobj
 % Content for page 0.
-{{object 21 0}} <<
-  /Length 0
+{{object 6 0}} <<
+  {{streamlen}}
 >>
 stream
 BT
@@ -79,8 +59,8 @@
 endstream
 endobj
 % Content for page 1.
-{{object 22 0}} <<
-  /Length 0
+{{object 7 0}} <<
+  {{streamlen}}
 >>
 stream
 BT
@@ -89,6 +69,72 @@
 ET
 endstream
 endobj
+% Root bookmark
+{{object 8 0}} <<
+  /Type /Outlines
+  /Count 3
+  /First 9 0 R
+  /Last 12 0 R
+>>
+endobj
+% First child bookmark (leaf node)
+{{object 9 0}} <<
+  /Title (A Good Beginning)
+  /Parent 8 0 R
+  /Next 10 0 R
+  /Dest (foo)
+>>
+endobj
+% Second child bookmark (open)
+{{object 10 0}} <<
+  /Title (Open Middle)
+  /Parent 8 0 R
+  /First 11 0 R
+  /Last 11 0 R
+  /Prev 9 0 R
+  /Next 12 0 R
+  /Count 1
+  /A <<
+    /Type /Action
+    /S /URI
+    /URI (https://theplay.test)
+  >>
+>>
+endobj
+% First grandchild bookmark
+{{object 11 0}} <<
+  /Title (Open Middle Descendant)
+  /Parent 10 0 R
+  /Dest [3 0 R /XYZ 100 200 0]
+>>
+endobj
+% Third child bookmark (closed)
+{{object 12 0}} <<
+  /Title (A Good Closed Ending)
+  /Parent 8 0 R
+  /First 13 0 R
+  /Last 14 0 R
+  /Prev 10 0 R
+  /Count -2
+  /Dest (bar)
+>>
+endobj
+% Second grandchild bookmark
+{{object 13 0}} <<
+  /Title (A Good Closed Ending Descendant)
+  /Parent 12 0 R
+  /Next 14 0 R
+  /Dest (bar)
+>>
+endobj
+% Third grandchild bookmark
+{{object 14 0}} <<
+  /Title (A Good Closed Ending Descendant 2)
+  /Parent 12 0 R
+  /Prev 13 0 R
+  /Dest (bar)
+>>
+endobj
 {{xref}}
 {{trailer}}
 {{startxref}}
diff --git a/testing/resources/bookmarks.pdf b/testing/resources/bookmarks.pdf
index 8c2eb5a..757f859 100644
--- a/testing/resources/bookmarks.pdf
+++ b/testing/resources/bookmarks.pdf
@@ -3,7 +3,7 @@
 1 0 obj <<
   /Type /Catalog
   /Pages 2 0 R
-  /Outlines 14 0 R
+  /Outlines 8 0 R
 >>
 endobj
 2 0 obj <<
@@ -20,9 +20,11 @@
   /Type /Page
   /Parent 2 0 R
   /Resources <<
-    /Font <</F1 15 0 R>>
+    /Font <<
+      /F1 5 0 R
+    >>
   >>
-  /Contents [21 0 R]
+  /Contents [6 0 R]
   /MediaBox [0 0 612 792]
 >>
 endobj
@@ -31,46 +33,24 @@
   /Type /Page
   /Parent 2 0 R
   /Resources  <<
-    /Font <</F1 15 0 R>>
+    /Font <<
+      /F1 5 0 R
+    >>
   >>
-  /Contents [22 0 R]
+  /Contents [7 0 R]
   /MediaBox [0 0 612 792]
 >>
 endobj
-% First bookmark
-10 0 obj <<
-  /Title (A Good Beginning)
-  /Parent 14 0 R
-  /Next 11 0 R
-  /Dest (foo)
->>
-endobj
-% Last bookmark
-11 0 obj <<
-  /Title (A Good Ending)
-  /Parent 14 0 R
-  /Prev 10 0 R
-  /Dest (bar)
->>
-endobj
-% Root bookmark
-14 0 obj <<
-  /Type /Outlines
-  /First 10 0 R
-  /Last  11 0 R
-  /Count 2
->>
-endobj
 % Font resource.
-15 0 obj <<
+5 0 obj <<
   /Type /Font
   /Subtype /Type1
   /BaseFont /Arial
 >>
 endobj
 % Content for page 0.
-21 0 obj <<
-  /Length 0
+6 0 obj <<
+  /Length 37
 >>
 stream
 BT
@@ -80,8 +60,8 @@
 endstream
 endobj
 % Content for page 1.
-22 0 obj <<
-  /Length 0
+7 0 obj <<
+  /Length 37
 >>
 stream
 BT
@@ -90,32 +70,93 @@
 ET
 endstream
 endobj
+% Root bookmark
+8 0 obj <<
+  /Type /Outlines
+  /Count 3
+  /First 9 0 R
+  /Last 12 0 R
+>>
+endobj
+% First child bookmark (leaf node)
+9 0 obj <<
+  /Title (A Good Beginning)
+  /Parent 8 0 R
+  /Next 10 0 R
+  /Dest (foo)
+>>
+endobj
+% Second child bookmark (open)
+10 0 obj <<
+  /Title (Open Middle)
+  /Parent 8 0 R
+  /First 11 0 R
+  /Last 11 0 R
+  /Prev 9 0 R
+  /Next 12 0 R
+  /Count 1
+  /A <<
+    /Type /Action
+    /S /URI
+    /URI (https://theplay.test)
+  >>
+>>
+endobj
+% First grandchild bookmark
+11 0 obj <<
+  /Title (Open Middle Descendant)
+  /Parent 10 0 R
+  /Dest [3 0 R /XYZ 100 200 0]
+>>
+endobj
+% Third child bookmark (closed)
+12 0 obj <<
+  /Title (A Good Closed Ending)
+  /Parent 8 0 R
+  /First 13 0 R
+  /Last 14 0 R
+  /Prev 10 0 R
+  /Count -2
+  /Dest (bar)
+>>
+endobj
+% Second grandchild bookmark
+13 0 obj <<
+  /Title (A Good Closed Ending Descendant)
+  /Parent 12 0 R
+  /Next 14 0 R
+  /Dest (bar)
+>>
+endobj
+% Third grandchild bookmark
+14 0 obj <<
+  /Title (A Good Closed Ending Descendant 2)
+  /Parent 12 0 R
+  /Prev 13 0 R
+  /Dest (bar)
+>>
+endobj
 xref
-0 23
+0 15
 0000000000 65535 f 
 0000000015 00000 n 
-0000000087 00000 n 
-0000000185 00000 n 
-0000000346 00000 n 
-0000000000 65535 f 
-0000000000 65535 f 
-0000000000 65535 f 
-0000000000 65535 f 
-0000000000 65535 f 
-0000000508 00000 n 
-0000000620 00000 n 
-0000000000 65535 f 
-0000000000 65535 f 
-0000000729 00000 n 
-0000000829 00000 n 
-0000000000 65535 f 
-0000000000 65535 f 
-0000000000 65535 f 
-0000000000 65535 f 
-0000000000 65535 f 
-0000000924 00000 n 
-0000001034 00000 n 
-trailer<< /Root 1 0 R /Size 23 >>
+0000000086 00000 n 
+0000000184 00000 n 
+0000000355 00000 n 
+0000000527 00000 n 
+0000000621 00000 n 
+0000000731 00000 n 
+0000000835 00000 n 
+0000000950 00000 n 
+0000001075 00000 n 
+0000001310 00000 n 
+0000001446 00000 n 
+0000001617 00000 n 
+0000001756 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 15
+>>
 startxref
-1122
+1869
 %%EOF
diff --git a/testing/resources/bug_1055869.in b/testing/resources/bug_1055869.in
new file mode 100644
index 0000000..26cbf5f
--- /dev/null
+++ b/testing/resources/bug_1055869.in
@@ -0,0 +1,62 @@
+{{header}}
+{{include xfa_catalog_1_0.fragment}}
+{{include xfa_object_2_0.fragment}}
+{{include xfa_preamble_3_0.fragment}}
+{{include xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <subform h="10.5in" w="8in" name="subform2">
+      <field h="10mm" name="choiceList0" w="50mm" x="5mm" y="50mm">
+        <ui>
+          <choiceList/>
+        </ui>
+        <items>
+          <text>Single</text>
+        </items>
+      </field>
+      <field name="choiceList1" h="200mm" w="200mm" x="1mm" y="1mm">
+        <ui>
+          <textEdit/>
+        </ui>
+        <value>
+          <text>pdfium</text>
+        </value>
+        <event activity="change">
+          <script contentType="application/x-javascript">
+            change_count += 1;
+            if (change_count == 2) {
+                f1 = xfa.resolveNode("xfa.form..choiceList0");
+                xfa.host.setFocus(f1);
+                xfa.template.remerge();
+                xfa.host.openList(f1);
+            }
+          </script>
+        </event>
+      </field>
+    </subform>
+    <event activity="docReady">
+      <script contentType="application/x-javascript">
+        change_count = 0;
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include xfa_locale_6_0.fragment}}
+{{include xfa_postamble_7_0.fragment}}
+{{include xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1055869.pdf b/testing/resources/bug_1055869.pdf
new file mode 100644
index 0000000..832d029
--- /dev/null
+++ b/testing/resources/bug_1055869.pdf
@@ -0,0 +1,271 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /AcroForm 2 0 R
+  /Extensions <<
+    /ADBE <<
+      /BaseVersion /1.7
+      /ExtensionLevel 8
+    >>
+  >>
+  /NeedsRendering true
+  /Pages 8 0 R
+  /Type /Catalog
+>>
+endobj
+2 0 obj <<
+  /XFA [
+    (preamble)
+    3 0 R
+    (config)
+    4 0 R
+    (template)
+    5 0 R
+    (localeSet)
+    6 0 R
+    (postamble)
+    7 0 R
+  ]
+>>
+endobj
+3 0 obj <<
+  /Length 124
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2018-02-23T21:37:11Z" uuid="21482798-7bf0-40a4-bc5d-3cefdccf32b5">
+endstream
+endobj
+4 0 obj <<
+  /Length 642
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+  <destination>pdf</destination>
+  <pdf>
+    <fontInfo/>
+  </pdf>
+</agent>
+<present>
+  <pdf>
+    <version>1.7</version>
+    <adobeExtensionLevel>8</adobeExtensionLevel>
+    <renderPolicy>client</renderPolicy>
+    <scriptModel>XFA</scriptModel>
+    <interactive>1</interactive>
+  </pdf>
+  <xdp>
+    <packets>*</packets>
+  </xdp>
+  <destination>pdf</destination>
+  <script>
+    <runScripts>server</runScripts>
+  </script>
+</present>
+<acrobat>
+  <acrobat7>
+    <dynamicRender>required</dynamicRender>
+  </acrobat7>
+  <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+5 0 obj <<
+  /Length 1361
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <subform h="10.5in" w="8in" name="subform2">
+      <field h="10mm" name="choiceList0" w="50mm" x="5mm" y="50mm">
+        <ui>
+          <choiceList/>
+        </ui>
+        <items>
+          <text>Single</text>
+        </items>
+      </field>
+      <field name="choiceList1" h="200mm" w="200mm" x="1mm" y="1mm">
+        <ui>
+          <textEdit/>
+        </ui>
+        <value>
+          <text>pdfium</text>
+        </value>
+        <event activity="change">
+          <script contentType="application/x-javascript">
+            change_count += 1;
+            if (change_count == 2) {
+                f1 = xfa.resolveNode("xfa.form..choiceList0");
+                xfa.host.setFocus(f1);
+                xfa.template.remerge();
+                xfa.host.openList(f1);
+            }
+          </script>
+        </event>
+      </field>
+    </subform>
+    <event activity="docReady">
+      <script contentType="application/x-javascript">
+        change_count = 0;
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+6 0 obj <<
+  /Length 3455
+>>
+stream
+<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">
+  <locale name="en_US" desc="English (United States)">
+    <calendarSymbols name="gregorian">
+      <monthNames>
+        <month>January</month>
+        <month>February</month>
+        <month>March</month>
+        <month>April</month>
+        <month>May</month>
+        <month>June</month>
+        <month>July</month>
+        <month>August</month>
+        <month>September</month>
+        <month>October</month>
+        <month>November</month>
+        <month>December</month>
+      </monthNames>
+      <monthNames abbr="1">
+        <month>Jan</month>
+        <month>Feb</month>
+        <month>Mar</month>
+        <month>Apr</month>
+        <month>May</month>
+        <month>Jun</month>
+        <month>Jul</month>
+        <month>Aug</month>
+        <month>Sep</month>
+        <month>Oct</month>
+        <month>Nov</month>
+        <month>Dec</month>
+      </monthNames>
+      <dayNames>
+        <day>Sunday</day>
+        <day>Monday</day>
+        <day>Tuesday</day>
+        <day>Wednesday</day>
+        <day>Thursday</day>
+        <day>Friday</day>
+        <day>Saturday</day>
+      </dayNames>
+      <dayNames abbr="1">
+        <day>Sun</day>
+        <day>Mon</day>
+        <day>Tue</day>
+        <day>Wed</day>
+        <day>Thu</day>
+        <day>Fri</day>
+        <day>Sat</day>
+      </dayNames>
+      <meridiemNames>
+        <meridiem>AM</meridiem>
+        <meridiem>PM</meridiem>
+      </meridiemNames>
+      <eraNames>
+        <era>BC</era>
+        <era>AD</era>
+      </eraNames>
+    </calendarSymbols>
+    <datePatterns>
+      <datePattern name="full">EEEE, MMMM D, YYYY</datePattern>
+      <datePattern name="long">MMMM D, YYYY</datePattern>
+      <datePattern name="med">MMM D, YYYY</datePattern>
+      <datePattern name="short">M/D/YY</datePattern>
+    </datePatterns>
+    <timePatterns>
+      <timePattern name="full">h:MM:SS A Z</timePattern>
+      <timePattern name="long">h:MM:SS A Z</timePattern>
+      <timePattern name="med">h:MM:SS A</timePattern>
+      <timePattern name="short">h:MM A</timePattern>
+    </timePatterns>
+    <dateTimeSymbols>GyMdkHmsSEDFwWahKzZ</dateTimeSymbols>
+    <numberPatterns>
+      <numberPattern name="numeric">z,zz9.zzz</numberPattern>
+      <numberPattern name="currency">$z,zz9.99|($z,zz9.99)</numberPattern>
+      <numberPattern name="percent">z,zz9%</numberPattern>
+    </numberPatterns>
+    <numberSymbols>
+      <numberSymbol name="decimal">.</numberSymbol>
+      <numberSymbol name="grouping">,</numberSymbol>
+      <numberSymbol name="percent">%</numberSymbol>
+      <numberSymbol name="minus">-</numberSymbol>
+      <numberSymbol name="zero">0</numberSymbol>
+    </numberSymbols>
+    <currencySymbols>
+      <currencySymbol name="symbol">$</currencySymbol>
+      <currencySymbol name="isoname">USD</currencySymbol>
+      <currencySymbol name="decimal">.</currencySymbol>
+    </currencySymbols>
+    <typefaces>
+      <typeface name="Myriad Pro"/>
+      <typeface name="Minion Pro"/>
+      <typeface name="Courier Std"/>
+      <typeface name="Adobe Pi Std"/>
+      <typeface name="Adobe Hebrew"/>
+      <typeface name="Adobe Arabic"/>
+      <typeface name="Adobe Thai"/>
+      <typeface name="Kozuka Gothic Pro-VI M"/>
+      <typeface name="Kozuka Mincho Pro-VI R"/>
+      <typeface name="Adobe Ming Std L"/>
+      <typeface name="Adobe Song Std L"/>
+      <typeface name="Adobe Myungjo Std M"/>
+    </typefaces>
+  </locale>
+</localeSet>
+endstream
+endobj
+7 0 obj <<
+  /Length 11
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+8 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [9 0 R]
+>>
+endobj
+9 0 obj <<
+  /Type /Page
+  /Parent 8 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000199 00000 n 
+0000000358 00000 n 
+0000000534 00000 n 
+0000001228 00000 n 
+0000002642 00000 n 
+0000006150 00000 n 
+0000006212 00000 n 
+0000006275 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+startxref
+6352
+%%EOF
diff --git a/testing/resources/bug_1058653.in b/testing/resources/bug_1058653.in
new file mode 100644
index 0000000..5973c54
--- /dev/null
+++ b/testing/resources/bug_1058653.in
@@ -0,0 +1,73 @@
+{{header}}
+{{include xfa_catalog_1_0.fragment}}
+{{include xfa_object_2_0.fragment}}
+{{include xfa_preamble_3_0.fragment}}
+{{include xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <subform h="10.5in" w="8in" name="subform2">
+      <field name="f1" h="10mm" w="10mm" x="20mm" y="20mm">
+        <ui>
+          <choiceList/>
+        </ui>
+        <items save="1">
+          <text>aaaaaaaaaa</text>
+        </items>
+      </field>
+      <subform name="f4" x="1mm" y="1mm">
+        <occur max="-1"/>
+        <field name="f2" h="350mm" w="200mm">
+          <ui>
+            <choiceList textEntry="1">
+            </choiceList>
+          </ui>
+          <items>
+                <text>Albania</text>
+                <text>Andorra</text>
+                <text>Armenia</text>
+          </items>
+          <event activity="change">
+            <script contentType="application/x-javascript">
+              a += 1;
+              if (a == 2) {
+                  c = xfa.resolveNode("xfa.form..f1");
+                  xfa.host.setFocus(c);
+                  d = xfa.resolveNode("xfa.form..f4");
+                  d.instanceManager.addInstance(1);
+                  d.instanceManager.removeInstance(0);
+                  xfa.host.openList(c);
+              }
+            </script>
+          </event>
+        </field>
+      </subform>
+    </subform>
+    <event activity="docReady">
+      <script contentType="application/x-javascript">
+        a = 0;
+        f2 = xfa.resolveNode("xfa.form..f2");
+        f2.rawValue = "minhtttttt";
+        xfa.host.setFocus(f2);
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include xfa_locale_6_0.fragment}}
+{{include xfa_postamble_7_0.fragment}}
+{{include xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1058653.pdf b/testing/resources/bug_1058653.pdf
new file mode 100644
index 0000000..f278dfb
--- /dev/null
+++ b/testing/resources/bug_1058653.pdf
@@ -0,0 +1,282 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /AcroForm 2 0 R
+  /Extensions <<
+    /ADBE <<
+      /BaseVersion /1.7
+      /ExtensionLevel 8
+    >>
+  >>
+  /NeedsRendering true
+  /Pages 8 0 R
+  /Type /Catalog
+>>
+endobj
+2 0 obj <<
+  /XFA [
+    (preamble)
+    3 0 R
+    (config)
+    4 0 R
+    (template)
+    5 0 R
+    (localeSet)
+    6 0 R
+    (postamble)
+    7 0 R
+  ]
+>>
+endobj
+3 0 obj <<
+  /Length 124
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2018-02-23T21:37:11Z" uuid="21482798-7bf0-40a4-bc5d-3cefdccf32b5">
+endstream
+endobj
+4 0 obj <<
+  /Length 642
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+  <destination>pdf</destination>
+  <pdf>
+    <fontInfo/>
+  </pdf>
+</agent>
+<present>
+  <pdf>
+    <version>1.7</version>
+    <adobeExtensionLevel>8</adobeExtensionLevel>
+    <renderPolicy>client</renderPolicy>
+    <scriptModel>XFA</scriptModel>
+    <interactive>1</interactive>
+  </pdf>
+  <xdp>
+    <packets>*</packets>
+  </xdp>
+  <destination>pdf</destination>
+  <script>
+    <runScripts>server</runScripts>
+  </script>
+</present>
+<acrobat>
+  <acrobat7>
+    <dynamicRender>required</dynamicRender>
+  </acrobat7>
+  <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+5 0 obj <<
+  /Length 1772
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <subform h="10.5in" w="8in" name="subform2">
+      <field name="f1" h="10mm" w="10mm" x="20mm" y="20mm">
+        <ui>
+          <choiceList/>
+        </ui>
+        <items save="1">
+          <text>aaaaaaaaaa</text>
+        </items>
+      </field>
+      <subform name="f4" x="1mm" y="1mm">
+        <occur max="-1"/>
+        <field name="f2" h="350mm" w="200mm">
+          <ui>
+            <choiceList textEntry="1">
+            </choiceList>
+          </ui>
+          <items>
+                <text>Albania</text>
+                <text>Andorra</text>
+                <text>Armenia</text>
+          </items>
+          <event activity="change">
+            <script contentType="application/x-javascript">
+              a += 1;
+              if (a == 2) {
+                  c = xfa.resolveNode("xfa.form..f1");
+                  xfa.host.setFocus(c);
+                  d = xfa.resolveNode("xfa.form..f4");
+                  d.instanceManager.addInstance(1);
+                  d.instanceManager.removeInstance(0);
+                  xfa.host.openList(c);
+              }
+            </script>
+          </event>
+        </field>
+      </subform>
+    </subform>
+    <event activity="docReady">
+      <script contentType="application/x-javascript">
+        a = 0;
+        f2 = xfa.resolveNode("xfa.form..f2");
+        f2.rawValue = "minhtttttt";
+        xfa.host.setFocus(f2);
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+6 0 obj <<
+  /Length 3455
+>>
+stream
+<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">
+  <locale name="en_US" desc="English (United States)">
+    <calendarSymbols name="gregorian">
+      <monthNames>
+        <month>January</month>
+        <month>February</month>
+        <month>March</month>
+        <month>April</month>
+        <month>May</month>
+        <month>June</month>
+        <month>July</month>
+        <month>August</month>
+        <month>September</month>
+        <month>October</month>
+        <month>November</month>
+        <month>December</month>
+      </monthNames>
+      <monthNames abbr="1">
+        <month>Jan</month>
+        <month>Feb</month>
+        <month>Mar</month>
+        <month>Apr</month>
+        <month>May</month>
+        <month>Jun</month>
+        <month>Jul</month>
+        <month>Aug</month>
+        <month>Sep</month>
+        <month>Oct</month>
+        <month>Nov</month>
+        <month>Dec</month>
+      </monthNames>
+      <dayNames>
+        <day>Sunday</day>
+        <day>Monday</day>
+        <day>Tuesday</day>
+        <day>Wednesday</day>
+        <day>Thursday</day>
+        <day>Friday</day>
+        <day>Saturday</day>
+      </dayNames>
+      <dayNames abbr="1">
+        <day>Sun</day>
+        <day>Mon</day>
+        <day>Tue</day>
+        <day>Wed</day>
+        <day>Thu</day>
+        <day>Fri</day>
+        <day>Sat</day>
+      </dayNames>
+      <meridiemNames>
+        <meridiem>AM</meridiem>
+        <meridiem>PM</meridiem>
+      </meridiemNames>
+      <eraNames>
+        <era>BC</era>
+        <era>AD</era>
+      </eraNames>
+    </calendarSymbols>
+    <datePatterns>
+      <datePattern name="full">EEEE, MMMM D, YYYY</datePattern>
+      <datePattern name="long">MMMM D, YYYY</datePattern>
+      <datePattern name="med">MMM D, YYYY</datePattern>
+      <datePattern name="short">M/D/YY</datePattern>
+    </datePatterns>
+    <timePatterns>
+      <timePattern name="full">h:MM:SS A Z</timePattern>
+      <timePattern name="long">h:MM:SS A Z</timePattern>
+      <timePattern name="med">h:MM:SS A</timePattern>
+      <timePattern name="short">h:MM A</timePattern>
+    </timePatterns>
+    <dateTimeSymbols>GyMdkHmsSEDFwWahKzZ</dateTimeSymbols>
+    <numberPatterns>
+      <numberPattern name="numeric">z,zz9.zzz</numberPattern>
+      <numberPattern name="currency">$z,zz9.99|($z,zz9.99)</numberPattern>
+      <numberPattern name="percent">z,zz9%</numberPattern>
+    </numberPatterns>
+    <numberSymbols>
+      <numberSymbol name="decimal">.</numberSymbol>
+      <numberSymbol name="grouping">,</numberSymbol>
+      <numberSymbol name="percent">%</numberSymbol>
+      <numberSymbol name="minus">-</numberSymbol>
+      <numberSymbol name="zero">0</numberSymbol>
+    </numberSymbols>
+    <currencySymbols>
+      <currencySymbol name="symbol">$</currencySymbol>
+      <currencySymbol name="isoname">USD</currencySymbol>
+      <currencySymbol name="decimal">.</currencySymbol>
+    </currencySymbols>
+    <typefaces>
+      <typeface name="Myriad Pro"/>
+      <typeface name="Minion Pro"/>
+      <typeface name="Courier Std"/>
+      <typeface name="Adobe Pi Std"/>
+      <typeface name="Adobe Hebrew"/>
+      <typeface name="Adobe Arabic"/>
+      <typeface name="Adobe Thai"/>
+      <typeface name="Kozuka Gothic Pro-VI M"/>
+      <typeface name="Kozuka Mincho Pro-VI R"/>
+      <typeface name="Adobe Ming Std L"/>
+      <typeface name="Adobe Song Std L"/>
+      <typeface name="Adobe Myungjo Std M"/>
+    </typefaces>
+  </locale>
+</localeSet>
+endstream
+endobj
+7 0 obj <<
+  /Length 11
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+8 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [9 0 R]
+>>
+endobj
+9 0 obj <<
+  /Type /Page
+  /Parent 8 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000199 00000 n 
+0000000358 00000 n 
+0000000534 00000 n 
+0000001228 00000 n 
+0000003053 00000 n 
+0000006561 00000 n 
+0000006623 00000 n 
+0000006686 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+startxref
+6763
+%%EOF
diff --git a/testing/resources/bug_1124998.pdf b/testing/resources/bug_1124998.pdf
new file mode 100644
index 0000000..4368222
--- /dev/null
+++ b/testing/resources/bug_1124998.pdf
Binary files differ
diff --git a/testing/resources/bug_1229106.in b/testing/resources/bug_1229106.in
new file mode 100644
index 0000000..7ffe9bf
--- /dev/null
+++ b/testing/resources/bug_1229106.in
@@ -0,0 +1,68 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 4
+  /Kids [3 0 R 3 0 R 5 0 R 5 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /Rotate 90
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+0 1 -1 0 792 0 cm
+0 0 0 rg
+100 400 150 50 re f
+1 0 0 rg
+0 180 100 50 re f
+0 1 0 rg
+0 742 100 50 re f
+0 0 1 rg
+692 742 100 50 re f
+1 0 1 rg
+692 180 100 50 re f
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 792 612]
+  /Contents 6 0 R
+>>
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+0 0 0 rg
+100 220 150 50 re f
+1 0 0 rg
+0 0 100 50 re f
+0 1 0 rg
+0 562 100 50 re f
+0 0 1 rg
+692 562 100 50 re f
+1 0 1 rg
+692 0 100 50 re f
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1229106.pdf b/testing/resources/bug_1229106.pdf
new file mode 100644
index 0000000..e0d625c
--- /dev/null
+++ b/testing/resources/bug_1229106.pdf
@@ -0,0 +1,81 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 4
+  /Kids [3 0 R 3 0 R 5 0 R 5 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /Rotate 90
+  /Contents 4 0 R
+>>
+endobj
+4 0 obj <<
+  /Length 163
+>>
+stream
+q
+0 1 -1 0 792 0 cm
+0 0 0 rg
+100 400 150 50 re f
+1 0 0 rg
+0 180 100 50 re f
+0 1 0 rg
+0 742 100 50 re f
+0 0 1 rg
+692 742 100 50 re f
+1 0 1 rg
+692 180 100 50 re f
+Q
+endstream
+endobj
+5 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 792 612]
+  /Contents 6 0 R
+>>
+endobj
+6 0 obj <<
+  /Length 141
+>>
+stream
+q
+0 0 0 rg
+100 220 150 50 re f
+1 0 0 rg
+0 0 100 50 re f
+0 1 0 rg
+0 562 100 50 re f
+0 0 1 rg
+692 562 100 50 re f
+1 0 1 rg
+692 0 100 50 re f
+Q
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000149 00000 n 
+0000000257 00000 n 
+0000000472 00000 n 
+0000000567 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+760
+%%EOF
diff --git a/testing/resources/bug_1296920.in b/testing/resources/bug_1296920.in
new file mode 100644
index 0000000..5c9da58
--- /dev/null
+++ b/testing/resources/bug_1296920.in
@@ -0,0 +1,115 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /StructTreeRoot 6 0 R
+  /MarkInfo <<
+    /Marked true
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Contents 4 0 R
+  /MediaBox [0 0 100 100]
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+  /StructParents 0
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+/P <</MCID 1>> BDC
+BT
+/F1 12 Tf
+1 0 0 1 20 50 Tm
+(Hello) Tj
+ET
+EMC
+/P <</MCID 2>> BDC
+BT
+/F1 12 Tf
+1 0 0 1 50 50 Tm
+(World) Tj
+ET
+EMC
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 6 0}} <<
+  /Type /StructTreeRoot
+  /K [9 0 R]
+  /ParentTree 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+  /Nums [0 8 0 R 1 10 0 R]
+>>
+endobj
+{{object 8 0}}
+[12 0 R 13 0 R]
+endobj
+{{object 9 0}} <<
+  /Type /StructElem
+  /S /Document
+  /P 6 0 R
+  /K [10 0 R]
+>>
+endobj
+{{object 10 0}} <<
+  /Type /StructElem
+  /S /Part
+  /P 9 0 R
+  /K [11 0 R]
+>>
+endobj
+{{object 11 0}} <<
+  /Type /StructElem
+  /S /Div
+  /P 10 0 R
+  /K [12 0 R 13 0 R 14 0 R]
+>>
+endobj
+{{object 12 0}} <<
+  /Type /StructElem
+  /S /P
+  /P 11 0 R
+  /K 1
+  /Pg 3 0 R
+>>
+endobj
+{{object 13 0}} <<
+  /Type /StructElem
+  /S /P
+  /P 11 0 R
+  /K 2
+  /Pg 3 0 R
+>>
+endobj
+{{object 14 0}} <<
+  /Type /StructElem
+  /S /Div
+  /P 11 0 R
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1296920.pdf b/testing/resources/bug_1296920.pdf
new file mode 100644
index 0000000..92d778c
--- /dev/null
+++ b/testing/resources/bug_1296920.pdf
@@ -0,0 +1,136 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /StructTreeRoot 6 0 R
+  /MarkInfo <<
+    /Marked true
+  >>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Contents 4 0 R
+  /MediaBox [0 0 100 100]
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+  /StructParents 0
+>>
+endobj
+4 0 obj <<
+  /Length 134
+>>
+stream
+/P <</MCID 1>> BDC
+BT
+/F1 12 Tf
+1 0 0 1 20 50 Tm
+(Hello) Tj
+ET
+EMC
+/P <</MCID 2>> BDC
+BT
+/F1 12 Tf
+1 0 0 1 50 50 Tm
+(World) Tj
+ET
+EMC
+endstream
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+6 0 obj <<
+  /Type /StructTreeRoot
+  /K [9 0 R]
+  /ParentTree 7 0 R
+>>
+endobj
+7 0 obj <<
+  /Nums [0 8 0 R 1 10 0 R]
+>>
+endobj
+8 0 obj
+[12 0 R 13 0 R]
+endobj
+9 0 obj <<
+  /Type /StructElem
+  /S /Document
+  /P 6 0 R
+  /K [10 0 R]
+>>
+endobj
+10 0 obj <<
+  /Type /StructElem
+  /S /Part
+  /P 9 0 R
+  /K [11 0 R]
+>>
+endobj
+11 0 obj <<
+  /Type /StructElem
+  /S /Div
+  /P 10 0 R
+  /K [12 0 R 13 0 R 14 0 R]
+>>
+endobj
+12 0 obj <<
+  /Type /StructElem
+  /S /P
+  /P 11 0 R
+  /K 1
+  /Pg 3 0 R
+>>
+endobj
+13 0 obj <<
+  /Type /StructElem
+  /S /P
+  /P 11 0 R
+  /K 2
+  /Pg 3 0 R
+>>
+endobj
+14 0 obj <<
+  /Type /StructElem
+  /S /Div
+  /P 11 0 R
+>>
+endobj
+xref
+0 15
+0000000000 65535 f 
+0000000015 00000 n 
+0000000129 00000 n 
+0000000192 00000 n 
+0000000363 00000 n 
+0000000549 00000 n 
+0000000625 00000 n 
+0000000703 00000 n 
+0000000751 00000 n 
+0000000782 00000 n 
+0000000863 00000 n 
+0000000941 00000 n 
+0000001033 00000 n 
+0000001114 00000 n 
+0000001195 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 15
+>>
+startxref
+1259
+%%EOF
diff --git a/testing/resources/bug_1301.pdf b/testing/resources/bug_1301.pdf
index 285c7f7..8c6e45f 100644
--- a/testing/resources/bug_1301.pdf
+++ b/testing/resources/bug_1301.pdf
@@ -217,7 +217,7 @@
 endobj
 9 0 obj <<
   /Type /Page
-  /Parent 2 0 R
+  /Parent 8 0 R
   /MediaBox [0 0 612 792]
 >>
 endobj
diff --git a/testing/resources/bug_1302455.in b/testing/resources/bug_1302455.in
new file mode 100644
index 0000000..1e18f12
--- /dev/null
+++ b/testing/resources/bug_1302455.in
@@ -0,0 +1,75 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /DR 4 0 R
+    /Fields [6 0 R 7 0 R]
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Kids [3 0 R]
+  /Count 1
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [6 0 R 7 0 R]
+  /MediaBox [0 0 300 300]
+  /Resources 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Font <<
+    /F1 5 0 R
+  >>
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /AP <<
+    /N 8 0 R
+  >>
+  /F 4
+  /Rect [100 100 200 130]
+  /T (Text Box 1)
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /AP <<
+    /N 8 0 R
+  >>
+  /F 4
+  /Rect [100 160 200 190]
+  /T (Text Box 2)
+>>
+endobj
+{{object 8 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 100 30]
+  {{streamlen}}
+>>
+stream
+1 0 0 rg
+0 0 100 30 re f
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1302455.pdf b/testing/resources/bug_1302455.pdf
new file mode 100644
index 0000000..a9782c4
--- /dev/null
+++ b/testing/resources/bug_1302455.pdf
@@ -0,0 +1,90 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /DR 4 0 R
+    /Fields [6 0 R 7 0 R]
+  >>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Kids [3 0 R]
+  /Count 1
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [6 0 R 7 0 R]
+  /MediaBox [0 0 300 300]
+  /Resources 4 0 R
+>>
+endobj
+4 0 obj <<
+  /Font <<
+    /F1 5 0 R
+  >>
+>>
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+6 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /AP <<
+    /N 8 0 R
+  >>
+  /F 4
+  /Rect [100 100 200 130]
+  /T (Text Box 1)
+>>
+endobj
+7 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /AP <<
+    /N 8 0 R
+  >>
+  /F 4
+  /Rect [100 160 200 190]
+  /T (Text Box 2)
+>>
+endobj
+8 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 100 30]
+  /Length 25
+>>
+stream
+1 0 0 rg
+0 0 100 30 re f
+endstream
+endobj
+xref
+0 9
+0000000000 65535 f 
+0000000015 00000 n 
+0000000128 00000 n 
+0000000191 00000 n 
+0000000311 00000 n 
+0000000362 00000 n 
+0000000438 00000 n 
+0000000581 00000 n 
+0000000724 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 9
+>>
+startxref
+855
+%%EOF
diff --git a/testing/resources/bug_1324189.in b/testing/resources/bug_1324189.in
new file mode 100644
index 0000000..45fceba
--- /dev/null
+++ b/testing/resources/bug_1324189.in
@@ -0,0 +1,16 @@
+{{header}}
+{{object 1 0}} <
+  /Type /Catalog
+>>
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
+{{xref}}
+trailer <<
+  /Root 1 0 R
+  /Prev -200
+  {{trailersize}}
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1324189.pdf b/testing/resources/bug_1324189.pdf
new file mode 100644
index 0000000..9652398
--- /dev/null
+++ b/testing/resources/bug_1324189.pdf
@@ -0,0 +1,28 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <
+  /Type /Catalog
+>>
+xref
+0 2
+0000000000 65535 f 
+0000000015 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 2
+>>
+startxref
+45
+%%EOF
+xref
+0 2
+0000000000 65535 f 
+0000000015 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Prev -200
+  /Size 2
+>>
+startxref
+151
+%%EOF
diff --git a/testing/resources/bug_1324503.in b/testing/resources/bug_1324503.in
new file mode 100644
index 0000000..a46d142
--- /dev/null
+++ b/testing/resources/bug_1324503.in
@@ -0,0 +1,16 @@
+{{header}}
+{{object 1 0}} <
+  /Type /Catalog
+>>
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
+{{xref}}
+trailer <<
+  /Root 1 0 R
+  /XRefStm -1
+  {{trailersize}}
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1324503.pdf b/testing/resources/bug_1324503.pdf
new file mode 100644
index 0000000..2ce7425
--- /dev/null
+++ b/testing/resources/bug_1324503.pdf
@@ -0,0 +1,28 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <
+  /Type /Catalog
+>>
+xref
+0 2
+0000000000 65535 f 
+0000000015 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 2
+>>
+startxref
+45
+%%EOF
+xref
+0 2
+0000000000 65535 f 
+0000000015 00000 n 
+trailer <<
+  /Root 1 0 R
+  /XRefStm -1
+  /Size 2
+>>
+startxref
+151
+%%EOF
diff --git a/testing/resources/bug_1327884.pdf b/testing/resources/bug_1327884.pdf
new file mode 100644
index 0000000..0e28c06
--- /dev/null
+++ b/testing/resources/bug_1327884.pdf
@@ -0,0 +1,6 @@
+%PDF
+1 0 obj<</Pages 2 0 R/AcroForm<</XFA 30 0 R>>>>2 0 obj<</>
+30 0 obj<<>>stream
+<xdp xmlns="http://ns.adobe.com/xdp/"><con0ig><acrobat><acrobat0></acrobat0></acrobat></con0ig><template><desc e=""use=" .[use=&quot;*&quot;]"
+endobj
+trailer<</Root 1 0 R>>
\ No newline at end of file
diff --git a/testing/resources/bug_1328389.in b/testing/resources/bug_1328389.in
new file mode 100644
index 0000000..3370ce7
--- /dev/null
+++ b/testing/resources/bug_1328389.in
@@ -0,0 +1,24 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Kids [3 0 R]
+  /Count 1
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 100 100]
+  % https://crbug.com/1328389
+  /Foo /
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1328389.pdf b/testing/resources/bug_1328389.pdf
new file mode 100644
index 0000000..e45a7a8
--- /dev/null
+++ b/testing/resources/bug_1328389.pdf
@@ -0,0 +1,34 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Kids [3 0 R]
+  /Count 1
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 100 100]
+  % https://crbug.com/1328389
+  /Foo /
+>>
+endobj
+xref
+0 4
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 4
+>>
+startxref
+247
+%%EOF
diff --git a/testing/resources/bug_1333298.in b/testing/resources/bug_1333298.in
new file mode 100644
index 0000000..5b45ed9
--- /dev/null
+++ b/testing/resources/bug_1333298.in
@@ -0,0 +1,28 @@
+{{header}}
+{{include xfa_catalog_1_0.fragment}}
+{{include xfa_object_2_0.fragment}}
+{{include xfa_preamble_3_0.fragment}}
+{{include xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform>
+    <desc name="N01" use=" .[N01.use=&quot; .[N01.#use]&quot;]"/>
+    <proto>
+      <proto>
+        <bindItems/>
+      </proto>
+    </proto>
+  </subform>
+</template>
+endstream
+endobj
+{{include xfa_locale_6_0.fragment}}
+{{include xfa_postamble_7_0.fragment}}
+{{include xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1333298.pdf b/testing/resources/bug_1333298.pdf
new file mode 100644
index 0000000..c003ca9
--- /dev/null
+++ b/testing/resources/bug_1333298.pdf
@@ -0,0 +1,237 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /AcroForm 2 0 R
+  /Extensions <<
+    /ADBE <<
+      /BaseVersion /1.7
+      /ExtensionLevel 8
+    >>
+  >>
+  /NeedsRendering true
+  /Pages 8 0 R
+  /Type /Catalog
+>>
+endobj
+2 0 obj <<
+  /XFA [
+    (preamble)
+    3 0 R
+    (config)
+    4 0 R
+    (template)
+    5 0 R
+    (localeSet)
+    6 0 R
+    (postamble)
+    7 0 R
+  ]
+>>
+endobj
+3 0 obj <<
+  /Length 124
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2018-02-23T21:37:11Z" uuid="21482798-7bf0-40a4-bc5d-3cefdccf32b5">
+endstream
+endobj
+4 0 obj <<
+  /Length 642
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+  <destination>pdf</destination>
+  <pdf>
+    <fontInfo/>
+  </pdf>
+</agent>
+<present>
+  <pdf>
+    <version>1.7</version>
+    <adobeExtensionLevel>8</adobeExtensionLevel>
+    <renderPolicy>client</renderPolicy>
+    <scriptModel>XFA</scriptModel>
+    <interactive>1</interactive>
+  </pdf>
+  <xdp>
+    <packets>*</packets>
+  </xdp>
+  <destination>pdf</destination>
+  <script>
+    <runScripts>server</runScripts>
+  </script>
+</present>
+<acrobat>
+  <acrobat7>
+    <dynamicRender>required</dynamicRender>
+  </acrobat7>
+  <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+5 0 obj <<
+  /Length 189
+>>
+stream
+<template>
+  <subform>
+    <desc name="N01" use=" .[N01.use=&quot; .[N01.#use]&quot;]"/>
+    <proto>
+      <proto>
+        <bindItems/>
+      </proto>
+    </proto>
+  </subform>
+</template>
+endstream
+endobj
+6 0 obj <<
+  /Length 3455
+>>
+stream
+<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">
+  <locale name="en_US" desc="English (United States)">
+    <calendarSymbols name="gregorian">
+      <monthNames>
+        <month>January</month>
+        <month>February</month>
+        <month>March</month>
+        <month>April</month>
+        <month>May</month>
+        <month>June</month>
+        <month>July</month>
+        <month>August</month>
+        <month>September</month>
+        <month>October</month>
+        <month>November</month>
+        <month>December</month>
+      </monthNames>
+      <monthNames abbr="1">
+        <month>Jan</month>
+        <month>Feb</month>
+        <month>Mar</month>
+        <month>Apr</month>
+        <month>May</month>
+        <month>Jun</month>
+        <month>Jul</month>
+        <month>Aug</month>
+        <month>Sep</month>
+        <month>Oct</month>
+        <month>Nov</month>
+        <month>Dec</month>
+      </monthNames>
+      <dayNames>
+        <day>Sunday</day>
+        <day>Monday</day>
+        <day>Tuesday</day>
+        <day>Wednesday</day>
+        <day>Thursday</day>
+        <day>Friday</day>
+        <day>Saturday</day>
+      </dayNames>
+      <dayNames abbr="1">
+        <day>Sun</day>
+        <day>Mon</day>
+        <day>Tue</day>
+        <day>Wed</day>
+        <day>Thu</day>
+        <day>Fri</day>
+        <day>Sat</day>
+      </dayNames>
+      <meridiemNames>
+        <meridiem>AM</meridiem>
+        <meridiem>PM</meridiem>
+      </meridiemNames>
+      <eraNames>
+        <era>BC</era>
+        <era>AD</era>
+      </eraNames>
+    </calendarSymbols>
+    <datePatterns>
+      <datePattern name="full">EEEE, MMMM D, YYYY</datePattern>
+      <datePattern name="long">MMMM D, YYYY</datePattern>
+      <datePattern name="med">MMM D, YYYY</datePattern>
+      <datePattern name="short">M/D/YY</datePattern>
+    </datePatterns>
+    <timePatterns>
+      <timePattern name="full">h:MM:SS A Z</timePattern>
+      <timePattern name="long">h:MM:SS A Z</timePattern>
+      <timePattern name="med">h:MM:SS A</timePattern>
+      <timePattern name="short">h:MM A</timePattern>
+    </timePatterns>
+    <dateTimeSymbols>GyMdkHmsSEDFwWahKzZ</dateTimeSymbols>
+    <numberPatterns>
+      <numberPattern name="numeric">z,zz9.zzz</numberPattern>
+      <numberPattern name="currency">$z,zz9.99|($z,zz9.99)</numberPattern>
+      <numberPattern name="percent">z,zz9%</numberPattern>
+    </numberPatterns>
+    <numberSymbols>
+      <numberSymbol name="decimal">.</numberSymbol>
+      <numberSymbol name="grouping">,</numberSymbol>
+      <numberSymbol name="percent">%</numberSymbol>
+      <numberSymbol name="minus">-</numberSymbol>
+      <numberSymbol name="zero">0</numberSymbol>
+    </numberSymbols>
+    <currencySymbols>
+      <currencySymbol name="symbol">$</currencySymbol>
+      <currencySymbol name="isoname">USD</currencySymbol>
+      <currencySymbol name="decimal">.</currencySymbol>
+    </currencySymbols>
+    <typefaces>
+      <typeface name="Myriad Pro"/>
+      <typeface name="Minion Pro"/>
+      <typeface name="Courier Std"/>
+      <typeface name="Adobe Pi Std"/>
+      <typeface name="Adobe Hebrew"/>
+      <typeface name="Adobe Arabic"/>
+      <typeface name="Adobe Thai"/>
+      <typeface name="Kozuka Gothic Pro-VI M"/>
+      <typeface name="Kozuka Mincho Pro-VI R"/>
+      <typeface name="Adobe Ming Std L"/>
+      <typeface name="Adobe Song Std L"/>
+      <typeface name="Adobe Myungjo Std M"/>
+    </typefaces>
+  </locale>
+</localeSet>
+endstream
+endobj
+7 0 obj <<
+  /Length 11
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+8 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [9 0 R]
+>>
+endobj
+9 0 obj <<
+  /Type /Page
+  /Parent 8 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000199 00000 n 
+0000000358 00000 n 
+0000000534 00000 n 
+0000001228 00000 n 
+0000001469 00000 n 
+0000004977 00000 n 
+0000005039 00000 n 
+0000005102 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+startxref
+5179
+%%EOF
diff --git a/testing/resources/bug_1388_2.pdf b/testing/resources/bug_1388_2.pdf
new file mode 100644
index 0000000..1fee088
--- /dev/null
+++ b/testing/resources/bug_1388_2.pdf
@@ -0,0 +1,80 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 200 200]
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /TT2 5 0 R
+    >>
+  >>
+>>
+endobj
+4 0 obj <<
+  /Length 40
+>>
+stream
+BT
+/TT2 12 Tf
+40 100 Td
+[(X  X)] TJ
+ET
+endstream
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /TimesNewRomanPSMT
+  /Encoding /WinAnsiEncoding
+  /FirstChar 31
+  /FontDescriptor 6 0 R
+  /LastChar 252
+>>
+endobj
+6 0 obj <<
+  /Type /FontDescriptor
+  /Ascent 891
+  /CapHeight 656
+  /Descent -216
+  /Flags 34
+  /FontBBox [-568 -307 2000 1007]
+  /FontFamily (Times New Roman)
+  /FontName /TimesNewRomanPSMT
+  /FontStretch /Normal
+  /FontWeight 400
+  /ItalicAngle 0
+  /MissingWidth 778
+  /StemV 82
+  /XHeight -546
+>>
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000310 00000 n 
+0000000401 00000 n 
+0000000573 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+880
+%%EOF
diff --git a/testing/resources/bug_1396264.in b/testing/resources/bug_1396264.in
new file mode 100644
index 0000000..db1fc5a
--- /dev/null
+++ b/testing/resources/bug_1396264.in
@@ -0,0 +1,86 @@
+{{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 100 200]
+  /Resources <<
+    /XObject <<
+      % Both images are BGRA.
+      /ImBlue 5 0 R
+      /ImRed 6 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+100 0 0 100 0 0 cm
+/ImBlue Do
+Q
+q
+100 0 0 100 0 100 cm
+/ImRed Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /JPXDecode]
+  /Height 64
+  /Width 64
+  {{streamlen}}
+>>
+stream
+0000000c6a5020200d0a870a00000014667479706a703220000000006a7032200000004f6a7032
+68000000166968647200000040000000400004070700000000000f636f6c720100000000001000
+000022636465660004000000000001000100000002000200000003000300010000000000c06a70
+3263ff4fff51003200000000004000000040000000000000000000000040000000400000000000
+0000000004070101070101070101070101ff52000c00000001010504040001ff5c001340404848
+50484850484850484850484850ff640025000143726561746564206279204f70656e4a50454720
+76657273696f6e20322e332e30ff90000a0000000000360001ff93cfb41008908a6fdf801801ca
+bf00cfb40c01cabf0000000000000000000000000000000000000000ffd9
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /JPXDecode]
+  /Height 64
+  /Width 64
+  {{streamlen}}
+>>
+stream
+0000000c6a5020200d0a870a00000014667479706a703220000000006a7032200000004f6a7032
+68000000166968647200000040000000400004070700000000000f636f6c720100000000001000
+000022636465660004000000000001000100000002000200000003000300010000000000c06a70
+3263ff4fff51003200000000004000000040000000000000000000000040000000400000000000
+0000000004070101070101070101070101ff52000c00000001010504040001ff5c001340404848
+50484850484850484850484850ff640025000143726561746564206279204f70656e4a50454720
+76657273696f6e20322e332e30ff90000a0000000000360001ff93cfb41008908a6f00df801801
+cabfcfb40c01cabf0000000000000000000000000000000000000000ffd9
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1396264.pdf b/testing/resources/bug_1396264.pdf
new file mode 100644
index 0000000..9c2bddc
--- /dev/null
+++ b/testing/resources/bug_1396264.pdf
@@ -0,0 +1,99 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 100 200]
+  /Resources <<
+    /XObject <<
+      % Both images are BGRA.
+      /ImBlue 5 0 R
+      /ImRed 6 0 R
+    >>
+  >>
+>>
+endobj
+4 0 obj <<
+  /Length 69
+>>
+stream
+q
+100 0 0 100 0 0 cm
+/ImBlue Do
+Q
+q
+100 0 0 100 0 100 cm
+/ImRed Do
+Q
+endstream
+endobj
+5 0 obj <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /JPXDecode]
+  /Height 64
+  /Width 64
+  /Length 614
+>>
+stream
+0000000c6a5020200d0a870a00000014667479706a703220000000006a7032200000004f6a7032
+68000000166968647200000040000000400004070700000000000f636f6c720100000000001000
+000022636465660004000000000001000100000002000200000003000300010000000000c06a70
+3263ff4fff51003200000000004000000040000000000000000000000040000000400000000000
+0000000004070101070101070101070101ff52000c00000001010504040001ff5c001340404848
+50484850484850484850484850ff640025000143726561746564206279204f70656e4a50454720
+76657273696f6e20322e332e30ff90000a0000000000360001ff93cfb41008908a6fdf801801ca
+bf00cfb40c01cabf0000000000000000000000000000000000000000ffd9
+endstream
+endobj
+6 0 obj <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /JPXDecode]
+  /Height 64
+  /Width 64
+  /Length 614
+>>
+stream
+0000000c6a5020200d0a870a00000014667479706a703220000000006a7032200000004f6a7032
+68000000166968647200000040000000400004070700000000000f636f6c720100000000001000
+000022636465660004000000000001000100000002000200000003000300010000000000c06a70
+3263ff4fff51003200000000004000000040000000000000000000000040000000400000000000
+0000000004070101070101070101070101ff52000c00000001010504040001ff5c001340404848
+50484850484850484850484850ff640025000143726561746564206279204f70656e4a50454720
+76657273696f6e20322e332e30ff90000a0000000000360001ff93cfb41008908a6f00df801801
+cabfcfb40c01cabf0000000000000000000000000000000000000000ffd9
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000339 00000 n 
+0000000459 00000 n 
+0000001271 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+2083
+%%EOF
diff --git a/testing/resources/bug_1469.jp2 b/testing/resources/bug_1469.jp2
new file mode 100644
index 0000000..b84a029
--- /dev/null
+++ b/testing/resources/bug_1469.jp2
Binary files differ
diff --git a/testing/resources/bug_1506.in b/testing/resources/bug_1506.in
new file mode 100644
index 0000000..63c4c4d
--- /dev/null
+++ b/testing/resources/bug_1506.in
@@ -0,0 +1,60 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /Names <<
+    /Dests 7 0 R
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 4
+  /Kids [
+    3 0 R
+    4 0 R
+  ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Pages
+  /Parent 2 0 R
+  /Count 2
+  /Kids [
+    5 0 R
+    5 0 R
+  ]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Pages
+  /Parent 2 0 R
+  /Count 2
+  /Kids [
+    5 0 R
+    6 0 R
+  ]
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 100 200]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 100 300]
+>>
+endobj
+{{object 7 0}} <<
+  /Names [
+    (First) [6 0 R]
+  ]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1506.pdf b/testing/resources/bug_1506.pdf
new file mode 100644
index 0000000..7395fe1
--- /dev/null
+++ b/testing/resources/bug_1506.pdf
@@ -0,0 +1,74 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /Names <<
+    /Dests 7 0 R
+  >>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 4
+  /Kids [
+    3 0 R
+    4 0 R
+  ]
+>>
+endobj
+3 0 obj <<
+  /Type /Pages
+  /Parent 2 0 R
+  /Count 2
+  /Kids [
+    5 0 R
+    5 0 R
+  ]
+>>
+endobj
+4 0 obj <<
+  /Type /Pages
+  /Parent 2 0 R
+  /Count 2
+  /Kids [
+    5 0 R
+    6 0 R
+  ]
+>>
+endobj
+5 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 100 200]
+>>
+endobj
+6 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 100 300]
+>>
+endobj
+7 0 obj <<
+  /Names [
+    (First) [6 0 R]
+  ]
+>>
+endobj
+xref
+0 8
+0000000000 65535 f 
+0000000015 00000 n 
+0000000102 00000 n 
+0000000183 00000 n 
+0000000280 00000 n 
+0000000377 00000 n 
+0000000454 00000 n 
+0000000531 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 8
+>>
+startxref
+587
+%%EOF
diff --git a/testing/resources/bug_1549.in b/testing/resources/bug_1549.in
new file mode 100644
index 0000000..18285ca
--- /dev/null
+++ b/testing/resources/bug_1549.in
@@ -0,0 +1,63 @@
+{{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
+  /MediaBox [0 0 100 150]
+  /Contents 4 0 R
+  /Resources 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+20 0 0 20 0 100 cm
+/Im1 Do
+Q
+BT
+/Cs1 cs
+1 0 0 0 scn
+30 20 50 40 re f
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /ColorSpace <<
+    /Cs1 /DeviceCMYK
+  >>
+  /ProcSet [/PDF /ImageB]
+  /XObject <<
+    /Im1 6 0 R
+  >>
+>>
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 1
+  /Height 1
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter /ASCIIHexDecode
+  {{streamlen}}
+>>
+stream
+33
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1549.pdf b/testing/resources/bug_1549.pdf
new file mode 100644
index 0000000..946ba43
--- /dev/null
+++ b/testing/resources/bug_1549.pdf
@@ -0,0 +1,76 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 100 150]
+  /Contents 4 0 R
+  /Resources 5 0 R
+>>
+endobj
+4 0 obj <<
+  /Length 74
+>>
+stream
+q
+20 0 0 20 0 100 cm
+/Im1 Do
+Q
+BT
+/Cs1 cs
+1 0 0 0 scn
+30 20 50 40 re f
+ET
+endstream
+endobj
+5 0 obj <<
+  /ColorSpace <<
+    /Cs1 /DeviceCMYK
+  >>
+  /ProcSet [/PDF /ImageB]
+  /XObject <<
+    /Im1 6 0 R
+  >>
+>>
+endobj
+6 0 obj <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 1
+  /Height 1
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter /ASCIIHexDecode
+  /Length 3
+>>
+stream
+33
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000245 00000 n 
+0000000370 00000 n 
+0000000494 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+679
+%%EOF
diff --git a/testing/resources/bug_1558.in b/testing/resources/bug_1558.in
new file mode 100644
index 0000000..439038c
--- /dev/null
+++ b/testing/resources/bug_1558.in
@@ -0,0 +1,54 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 200 200]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+12 40 100 30 re
+W
+n
+0 g
+BT
+20 100 Td
+/F1 12 Tf
+(Clipped Text) Tj
+0 -50 Td
+/F1 12 Tf
+(Unclipped Text) Tj
+ET
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1558.pdf b/testing/resources/bug_1558.pdf
new file mode 100644
index 0000000..2feec46
--- /dev/null
+++ b/testing/resources/bug_1558.pdf
@@ -0,0 +1,66 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [0 0 200 200]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+5 0 obj <<
+  /Length 111
+>>
+stream
+q
+12 40 100 30 re
+W
+n
+0 g
+BT
+20 100 Td
+/F1 12 Tf
+(Clipped Text) Tj
+0 -50 Td
+/F1 12 Tf
+(Unclipped Text) Tj
+ET
+Q
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000157 00000 n 
+0000000283 00000 n 
+0000000361 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+524
+%%EOF
diff --git a/testing/resources/bug_1574.in b/testing/resources/bug_1574.in
new file mode 100644
index 0000000..5d7a08d
--- /dev/null
+++ b/testing/resources/bug_1574.in
@@ -0,0 +1,60 @@
+{{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
+  /MediaBox [0 0 200 300]
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+0 0 50 75 re
+W* n
+1 1 1 rg
+0 150 m
+100 150 l
+100 0 l
+0 0 l
+0 150 l
+h
+f*
+q
+0 0 0 rg
+BT
+20 50 Td
+/F1 12 Tf
+(Text) Tj
+ET
+Q
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1574.pdf b/testing/resources/bug_1574.pdf
new file mode 100644
index 0000000..df27418
--- /dev/null
+++ b/testing/resources/bug_1574.pdf
@@ -0,0 +1,72 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 200 300]
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+5 0 obj <<
+  /Length 124
+>>
+stream
+q
+0 0 50 75 re
+W* n
+1 1 1 rg
+0 150 m
+100 150 l
+100 0 l
+0 0 l
+0 150 l
+h
+f*
+q
+0 0 0 rg
+BT
+20 50 Td
+/F1 12 Tf
+(Text) Tj
+ET
+Q
+Q
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000283 00000 n 
+0000000361 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+537
+%%EOF
diff --git a/testing/resources/bug_1591.in b/testing/resources/bug_1591.in
new file mode 100644
index 0000000..7632e13
--- /dev/null
+++ b/testing/resources/bug_1591.in
@@ -0,0 +1,106 @@
+{{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 160 400]
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+0 0 0 rg
+BT
+/F1 1 Tf
+240 0 0 240 50 50 Tm
+0.2 Tw
+0.05 0 Td
+(1) Tj
+0.1 0 Td
+(2) Tj
+0.05 0 Td
+(1) Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type3
+  /CharProcs 6 0 R
+  /Encoding 7 0 R
+  /FirstChar 49
+  /FontBBox [-1 -8 28 27]
+  /FontMatrix [0.001 0 0 0.001 0 0]
+  /LastChar 50
+  /Name /F1
+  /Resources <<
+    /ProcSet [/PDF /ImageB]
+  >>
+  /Widths [8 10]
+>>
+endobj
+{{object 6 0}} <<
+  /uniE022 8 0 R
+  /uniE023 9 0 R
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Encoding
+  /Differences [49 /uniE022 /uniE023]
+>>
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+6 0 0 48 6 0 cm
+BI
+/W 6
+/H 48
+/BPC 1
+/IM true
+ID
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+EI
+Q
+endstream
+endobj
+{{object 9 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+BI
+/W 4
+/H 4
+/BPC 1
+/IM true
+ID
+xx
+EI
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1591.pdf b/testing/resources/bug_1591.pdf
new file mode 100644
index 0000000..3ec0a13
--- /dev/null
+++ b/testing/resources/bug_1591.pdf
@@ -0,0 +1,122 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 160 400]
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+>>
+endobj
+4 0 obj <<
+  /Length 102
+>>
+stream
+0 0 0 rg
+BT
+/F1 1 Tf
+240 0 0 240 50 50 Tm
+0.2 Tw
+0.05 0 Td
+(1) Tj
+0.1 0 Td
+(2) Tj
+0.05 0 Td
+(1) Tj
+ET
+endstream
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type3
+  /CharProcs 6 0 R
+  /Encoding 7 0 R
+  /FirstChar 49
+  /FontBBox [-1 -8 28 27]
+  /FontMatrix [0.001 0 0 0.001 0 0]
+  /LastChar 50
+  /Name /F1
+  /Resources <<
+    /ProcSet [/PDF /ImageB]
+  >>
+  /Widths [8 10]
+>>
+endobj
+6 0 obj <<
+  /uniE022 8 0 R
+  /uniE023 9 0 R
+>>
+endobj
+7 0 obj <<
+  /Type /Encoding
+  /Differences [49 /uniE022 /uniE023]
+>>
+endobj
+8 0 obj <<
+  /Length 105
+>>
+stream
+q
+6 0 0 48 6 0 cm
+BI
+/W 6
+/H 48
+/BPC 1
+/IM true
+ID
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+EI
+Q
+endstream
+endobj
+9 0 obj <<
+  /Length 42
+>>
+stream
+q
+BI
+/W 4
+/H 4
+/BPC 1
+/IM true
+ID
+xx
+EI
+Q
+endstream
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000309 00000 n 
+0000000463 00000 n 
+0000000724 00000 n 
+0000000779 00000 n 
+0000000856 00000 n 
+0000001013 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+startxref
+1106
+%%EOF
diff --git a/testing/resources/bug_1646.pdf b/testing/resources/bug_1646.pdf
new file mode 100644
index 0000000..8728c1b
--- /dev/null
+++ b/testing/resources/bug_1646.pdf
@@ -0,0 +1,77 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids[ 3 0 R ]
+>>
+endobj
+2 0 obj <<
+  /Type /Catalog
+  /Pages 1 0 R
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 1 0 R
+  /Resources 6 0 R
+  /MediaBox[ 0 0 64 64]
+  /Contents[ 5 0 R ]
+>>
+endobj
+4 0 obj <<
+  /Type /XObject
+  /Subtype /Image
+  /Name /I1
+  /Width 5000
+  /Height 5000
+  /BitsPerComponent 8
+  /ColorSpace /DeviceCMYK
+  /Filter [/ASCIIHexDecode /FlateDecode /DCTDecode]
+  /Decode [1.0 0.0 1.0 0.0 1.0 0.0 1.0 0.0]
+  /Length 734
+>>
+stream
+789cedcd3d4ec2001806e00f680529d4165a643671f112c6901095b09838b01983899bd7e02c9ec
+2c143f8b37802af50cba81770799eef9ddebcc9d7bc355f515c2faf96d1e974e2b2bd68be23bfd8
+3edd3fc436f69acf5844afdbdda795b44907699a24e9b0df3f188c86a35136ccb2713e29c679996
+759312bca6955d7f5e8e8783eabe693aaae9a97a806d3dd74972c4e625dc6a68c5519cd6bd4ede3
+f8edb0adabe26f1bcd47e4c922d6b18955c4793cdf9ddedc9e3dfe9d01000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000000fc835ef
+3fe03ce2e1eba
+endstream
+endobj
+5 0 obj <<
+/Length 28
+>>
+stream
+q
+64 0 0 64 0 0 cm
+/I1 Do
+Q
+endstream
+endobj
+6 0 obj <<
+  /ProcSet [/PDF /ImageC]
+  /XObject<</I1 4 0 R >>
+>>
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000079 00000 n 
+0000000132 00000 n 
+0000000247 00000 n 
+0000001253 00000 n 
+0000001330 00000 n 
+trailer <<
+  /Size 7
+  /Root 2 0 R
+>>
+startxref
+1402
+%%EOF
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_1752_truetype_font.fragment b/testing/resources/bug_1752_truetype_font.fragment
new file mode 100644
index 0000000..5dc8ea7
--- /dev/null
+++ b/testing/resources/bug_1752_truetype_font.fragment
@@ -0,0 +1,184 @@
+789ced5b0b7054d779fecf7decae9ebb1292102c4877b968179090848431
+60d95aa45d21211e42d2925df1b056da95b4a0d7689757c60fe23c4cd638
+76d2c44d1c9a382e75e28431579452d9a169ea3ab1f31aa74e26f5b46eea
+f1d86de201c7751c9a3ab0dbfffcf7ac9040c4c4339d691b16eefdbff39f
+73feff3fff77cebde7de0bc000a0104f0a682d2ddd5b3efc93879e00b865
+2d6a176df4f95b6039d4022cf82996dd1b3bb675bd19cc6e07a83f07c0ee
+dad81568ea3fb7e202d65fc2e35bdbba6aea0e157ce1d758f7356cdfdb3f
+121ecf5d9c857512da604dfd07125ad9b79d5900793e2c4b03e383236b72
+4a7201e40480f5cc60383e0e25a0a3ade7b0bf6370f8f0c06b254f3e0890
+df8b012e1b8a8623d26b3ff80bec5b85f56b865091f74b3989e50896970e
+8d240e2d5fabf4a03f1b96970d8ff587d5f50ab6cd6fc572c948f8d0b8d4
+0c7f86ed8f62591b0d8f446b7bbe588709c0b2551a1f8b27c00eff82fe8f
+71fbe313d1f181334ffe3b40ce32b4b91078ae58f6ed2b7f7bfcc777da1b
+7e03e5dc0dc08b2bbe1912722740fabcdaa3fe008b3690c0fc613fdb8397
+3127ea3ff2a2da439666feb249930d4b40a6b2040e980fbbb0ebf7a08f34
+b23a9f3d0c2ad8d447d57ad4979b52fe071890c0264939aaa4a88a2429af
+c27d692f1c4a631f17efb865dbd66de0850db0d18cc1f6a014d4807df95f
+311665937a96670272a4f3e023c75fc7f816e3510fcfabf9703746fcbc12
+c7c383e59fc0dd3c12712c12a368c012a332ce21e03349a1f2c6348f6143
+3a9d7e36335e6681e9a13389c639fb77755ee6fac98a6ab1dab26ea0e5ff
+d29f77e3ce9e5030d0ddd5b9bd63dbd62d9bdb37b5b56e6cf1fb9a9b3678
+1befb8bde1b6f5ebd6debae69655b535d52bab9679dc154bf525aef2d2a2
+02873d3f2f273bcb66b5a88a2c31a8d20cd6eb37e40aada025acfbf570eb
+ca2acd5f3ae45b59e5d75b7a0d2dac192814b7deda4a2a3d6c68bd9ae146
+119ea1ee35bcd872e0aa965eb3a577ba2573680dd0c05de89af1239fae4d
+b19eed41c40ffaf490665c20bc85b0e2a6421e165c2eec4151f16835bfd1
+726028e9efc518d9644e76b3de1ccd5e590593d93908731019cbf4f149b6
+ec0e46405ae65f3f89133c8fbbc591fac311a3637bd0ef73ba5ca195556d
+46beeea32a68269386a5d9b092492dc6438707b4c9aa6f278f4d39a0afb7
+3237a247c2bb82861cc6be49d99f4cde6f14541acb759fb1fcc3af97e2c8
+a34695eef31b95dc6a7be7b49ff62b2e99a15638742df91bc0e1e817cecf
+d68485c652e1f80d706848cd06eb0cbaf8cfd982b94e265b74ad25d99b0c
+4fa58ff4e99a434f4ee6e626c7fd986ee808a289a9f4330f388d966321c3
+d13bc4d687c4d05b3adb8d79db77060da9a2451b0aa306ff36eaaeb54e57
+c1749b8eeb5503a605938319d6781a3b8249cd9f7cc0e772bafc3eac7619
+47b607797a1e98d2a0cf791abc35952143eae535dfced4140778cd914c8d
+a9f4626275e4b8bd2b9834948ab688eec7cc3f10368ef4e12cdbcb09d21d
+46fe45a74b4f161668eb6a42d49647d116896986eac66461af991d70fef0
+2e490715f22f9ae282131db80b0ab5753a9ae176fcbabf57fc3d30548a06
+344c786ba53921ba8386d787c01b16ccf9276b6bb047b817898bf98854a3
+461f378af4a669962939b1ae207511dd8ca26603ef66a29751e3a7f585e9
+ebf59921705bfaf6e0d3509f7e7572b5e6fccb7a580d211f6f5cd28cb3cd
+ed4f0623034679af3382eb6f400b3a5d8637844c87f46034c4a71f6668f9
+ab4e9a24219a33ddc1f62ebd7d7b4f70ad08c4ace0e6940aff5566f4a0d3
+348313d1b055d8b4a0e49443d8d0810aad0581ded48067c35a61c3c38109
+272d9fc04d0d5a903921d31ac330966bfea84fb4e3e55946553ead9a5b33
+d62cbc88769a5b9dae90cbfcadac92b05a138eb1878d27b5355385972bac
+b0e13c6d6e2515cf65299ffc5a508fea217d4833bc1d413e369e1ecab248
+06e55c70d53dab3423599826706175a6c09369b4543a6726d7d84865be6c
+0ca95bc7c982ebea085675188c37d8490bd6897329e49cad479569e9f7f7
+751a6dd7766d339df29e499bdede95e431eb224ec08414e0d1b61753d185
+73b777ae0a9cb2deb0a8d0927a5b24a977051b9c9483cee0ddce0ff31116
+423b6bef6e5a593529b1a6499d1ddd3ee96547bb7a8278256d7ada8137fc
+a3ddc1d312939a7b9b42934bb13ef8b486f725d24a5ccb95bca0f102b7d6
+89051bb5773eed053842b50a29a8dc3fc58074b68c8e41ff9464ea1ca623
+3739f2e24dbf7f4a316bbc99d60aea6ca6ee08e9e837099c2c3e0abe0e4b
+879064bcadf8b5089f2077858692bd21bec8a004d3837f99c1f43b305dfa
+1d934cb2e41ad97ab4c9c8d19bb8be91eb1b4dbd85ebad38355909c3eeb3
+b9c3a5a82dfc9e33e9b8c05319c28b49d2f1c64a6f3acb55af054063bfd2
+d8c31abb57631a2b0b94d7cb81f232662f6b2c3b5526efece80934f66ceb
+917a583010ea08066a83de205e9183ca873a7604b41deced1d6c07eb0e04
+3aba03e3ddacbc9bd5767bbb8f74ffb8fbd56eb5760bdb226d0d6c8cb404
+1a5b4eb5482dac3db0a9a32dd086725bc7d640fd56b615e1e68ef640793b
+b3b737b69f6a97bb3a3a03b775b24ed611d8ded111b87529bb271c290fde
+cd0ec6cbca0fc43794ef8fd79527f0f8d3389b8ab397e36c417d69c08a51
+5be474f9486c41f970cc5dbe2f767bf9de586bf95763acc7cd6cee56b7d4
+ea3eea7ec12ddfab3fa4bfa8cbf3ea0bbd230195c901850f586676b9513e
+25cb91ddabcb0ff5b3bedd7a7978777d79efee15e577e2b107f1d4ee1776
+4b2feffec56e6951c41928a92f0e38eaed01bb7d9b5d2ab7bf6897ecf6b4
+5db2e02626809bf7c018dc0ba7e057a038801d29612a9b620f4f76775556
+b64f59d378dfcbead869b0a34645173f7bb7f71896a306047a76062719fb
+54e8e30f3e084d8bdb8d3a5c36bd8b43ed4684af1f0e8e20702c9e2c81a6
+503c5e59b9279ed85f5959194f54f25fdc04f17822612a78cd7e5193a98e
+9b656a453dafd4a0aab2740f80a50872a00377d2f3613f9d67fd945628e2
+327d7ef6395594be88fbecb330efd267d31752f7a6f6a6cea537a53ef0ae
+d27c1661f5c077c617211f2de7a5ce5c4aa5df4df7a6df9525c865f7a7df
+91bd902fdd92fe35eef3f3b0dde7e021b81f77f5c7e104bc010f4c1b7b1c
+9e8047b1eea3b0775af705f812b6fa347cfc8a4f560c5f049f5a40852afa
+13811fb25cd6c292ec59898f5be2cf1552440de0538215aa2719d4349cb6
+2af75ea89bb4a8af349c962584302973b5cad5a7ad9623971a4e33aeaf2f
+701554b80a5c3e494b2d659f4f0da981f7bee1537e04e2c94175aae7d06a
+1e54794b14ab2d4fceb7672b6a9e140be5e529aa0c4a2134d63716aeaba9
+2c28847505856cfebaba55b52ed925ebac3e8bc91e8b5556761997cf3df5
+8c74db13d2fa54cf899632d713ec3ba9dbd473eff9a421f6cd7f3ed49ff2
+037fe2791eb9fc0e66d50ea5f864d5e7bdc56ab31539162eb42b45396565
+457679896e5de0702cd8177ad9c1d63b986c7730bbea70d8014af78540b6
+2fdc17b263409550da48f194d6dcb967f79dbb775f09aea0feca9f55b5f3
+5c9a525c64b11697b1e2224577cd73d5adb965b5bb922d6026d29f675f61
+6ea67eeae8935f4ebdfbf6e5d414cb3f79fc1b4fbfc33cc6d7cf9c51cf9e
+fae67d4f2ec85e6c7cfab957e4d6d18f1cda7bf97397df3a9afce8ddfc49
+ebeef479e5457c529d0f6e6f916c2fceb2cba50be6412c344fc9b5c442b9
+f3a643a5c856d5b22550e080faba5b8bf399ae41c1eac2a5f575f3ad6e79
+ddf9d43bccf2cef1bfdf7d723cf537a93fff2a6b7cf91727379d5297a79e
+4bbd957a23f5a3f58f2d59c03ec9865e63dd533b8e77107fcff32761cca7
+0d9f7597798bf215c852b20a0ad5bc7d215556f2f785942bc962ebd69939
+610e58ce0a5c1ac80e70e905f59ada93da9f7a281563cfb200bbebd9d485
+d49b6f3f2f754abf4c3d92fa887a36f5c9d409369f95ff8ecf5ac67dcabf
+459f3910f0aeb264673305170e9373f32c59b1d0cb16b6dec2b22ccc0e96
+c72c52a16cb130495263214966d9b110bb86bb3dbb89bc69e256d5f2d88a
+5de621fff6d28bf2aacbf74bdd974f4a87d5b38fa7d63e76f922cc8a230b
+5abc95fca158c128b27378146de8dfd268b9d3326679c8f2658b2507c390
+58a1198719014e68f43ec335ce693de398dd3b455e3b2f3f853e2fbff715
+c1b57a1b72ed84dbbd5abe546493162c70dae4458bc1190b210fb9b905c8
+b8a2ce8f85d479b386397b7ccc55309b7b8e9189ba5b71bc4decbf126f7f
+29753af599b3ec43bf7cf3fb2d7f3795fa5dea9fd84296f727c7527f2b2b
+97eb2bdcec18eb7f83edf8ab1d8f75d3e4f879ea259d9de6592963a3d3cf
+ff35d02b30c31c9d1058c2b5fe2d816558c432ef0b70e6b05502ab789d0b
+086c416e13025ba153d205b641b1f4b8c0595024bd26703668f23a8173a0
+4cfebcc0b9e05336089c07d5caeb02e7c30a35c6df7e28fc9abb87a2e298
+41213c2cb084ad9e125886b5f05d811528648b0556a194dd26b0051cac4f
+602b9c60770b6c8315d20181b360993425703678a5ff1438071ae41e8173
+e1a8fcd702e7c12ee51302e7433732dc0c63300e8761026230084390000d
+eaa01656e159838d583b86fa618862a90d46a11faa116d40cd30cacee95e
+712a455146d1d6013c47b025348f8d1f9e880d0e25b4bada5575dac6b1b1
+c1e1a8d636da5fad6d181ed63a79555ceb8cc6a31307a211ecb015fd25f0
+d0a00bc2e82f8eaab1c498d6151e45c83d0cc27ef41d462fd0191ddc3f1c
+46b0057b8c52cfc3381a33d6116c3388f18de259839578cc617bcbd8e858
+e2f0388634121e8c8d0e6a2bb519ee7e7f303b68a87174c19d6b98b26a4c
+dc6aac884ec46363a3daaaeadad5578ccc3271b5fd1825308c47026d8631
+79511ac004ec43dd180cfc1e32b8d9084adefe30ca3eaa9fa05471bb098a
+334a6d6214693f6912d8de2cefc5944e50db089efbb1c61c539c5348238e
+c5b5b096980847a223e1897ddad8c02c2ac3a3116d247c58eb8b6a13d1c1
+583c119d8846b4d8a8d61f9d488451eedd3f118b4762fd094c4bbcfa46f8
+ca0cef5a86b8c7b92cf868f6f1f9384a698ca2bd9119fd7dd1786c70544b
+44c3a89dd9d8cc4c1fe5ee5ab391b9cd9ad670947d87b5691791992ef8aa
+48a091f5780dabc1b18c91a96a347485c66ad28f60fd381ea3623ad460e7
+44627c7d4d4dff58245a3d4899aeee1f1ba919af414f6335571b3f487faa
+d1d0ece863b3927ac55907b2cdc3d94f5cc7716a9a8b364633238e2dba85
+85d9799ac09643d8f320b60bd352cf047af0e0c1ea11918698491405dc31
+3116d9df9f8877e1fa8ef547e335ddd840e46ea26b68ec607f388e56360b
+d7a36494d3b19f263577c9933e44546cc090c2d8ce2ccdee53859aab1764
+1d2d48d88c8e47e348d5fed14874424b0c45b50de3e17e14a2a64acbacd8
+baeadaebe5363cc379353a98c09cd420893383e099db8c53b719fcb8b6bb
+f0bc3213c48c3c85c979f5d8c460cdb01940bc66735bb37f6b977f250530
+fd363c3d800b7fae5f36dd0f25580a15e0060f2c83e5b0827432de1fb2f1
+da6e4da7717fcae89e05500265d89ad7f27b92223c3080e97b2bb779045e
+07b5fff0045ed923d103755085e730b40c4e44f7c19ee17062142f140ac5
+95265bd9c0b66ce8d47834a851454c0aa2d935a63f05ef68337bcfc4d670
+df813854f50d1f1c80f5788e83b7bf7f641c368fc71371e89cd8373100c1
+03e1c47ebab7aad45bc511570a9ffcce5f85c9aee66b677a4cb57466a2cd
+2a9c4fc348ec5df03138069fa5279d937006cec173f043f829bc82a33f0f
+efc225a6e0734b115bc496b22ab69a35301fdbccbad92e1661c32cc1ee62
+1fc3adcb67d97176829d6467d839f61cfb21fb297b85bd8e23445feca899
+0bf688287f5e94bf2fe4bf9952924c69f98c29ad6b85fcb9296d1f13f267
+a6cc8a08f98e29b38f9832275fc8e3a6cc5d21e4b890c25fde2e215f3265
+7eab90df32a57db5905f37a54313f21121df356541a7908690174d59d820
+e47d42be60ca7922be79dd423e2ae4aba62c720b392ca4a82f12fd8b579b
+792c16f5c58f09993265499d908f9bed4a4ed0572986ab80efbfaecc7366
+d683f955ad0816e1fae173a76a468bb90e45cc23b3673edae4f617ced1ce
+fc92f5fe6d78ab20ecc2750b109973857fd0df4c6fadf0199ce54fc069dc
+2b7f1f7e06afc15bf01eb3b04256c656b035ac896d653d6c804db07bf029
+fd11f6387b8a3dc3becb5e623f676fb28b9224e54b0b25b75427dd216d92
+82d2809490ee931e96be249d949e915e907e26bd2ebd23839c2f2f9257c8
+6be516b95bee93c7e57be463f2a3f2d7e4b3f273f24bf2abf25bf225255b
+2955dc740de2e35f2eae461cc994158e2d8455c256c216c236c256c25984
+6d84b3096711ce219c4d3897700ee13cc2b984f309e711b613ce27ec206c
+275c40d841b8907001e179844d568b08cf235c4cb8887009e162c2f30997
+102e253c9ff002c2a5627670bc80b093f042f13d956327617a32200d3e17
+115e4cb89c7019618d70396117618df012c22ec23a61f3abf252c23ae10a
+c24b09bb095710f61076135e46df7115d2015dc5394b1ec193423c790453
+0a31e5115c29c49547b0a5105b1ec197427c7904630a31e6119c29c49947
+b0a6106b1ec19b42bc7904730a31e711dc29c49d47b0a7107b1ec19f42fc
+7904830a31e8111c2ac4a147b0a8108b1ec1a3423c7a04930a31e9115c2a
+c4a547b0a9109b1ec1a7427c7a04a30a31ea119c2ac4a947b0aa10ab1ec1
+ab42bc7a04b30a31eb11dc2ac4ad47b0ab10bb1ec1af42fc7a04c30a31ec
+111c2bc4b147b0ac10cb1ec1b3423c7b04d312c645f76dba365dffda928b
+635b8277e03adc2635c126dc30eec1add104de5db91d09cfa654845485b4
+086915d226760759624720d1acc920751a59a691751ad96e46fb3f1aed42
+9c87593463ae1fab8ad19a77d2aa1b8eeec62c67d3aed58d7bba357007de
+cb3af17e3970c33e149101791a29d3489d4696697433aa9b51fd7145558a
+ab967f2a5a7add98148caa90ae45ef17c78d58b3095bfcbad6002dd0f1be
+5615c85ccd32489946ea34bae9fda6f79bdeffb8bc97a0652bf03df0dcbe
+2df884605e4f6b710f753dbf0a64aea2ca740419a4fe3ff352848cf1b71f
+da9c3e3257faa5d7d857a63952a63dddb476d3daff656b85b89ef81bc3b2
+396c49b4de3276326faee50fdcab0cd7750e9657c0b5bf9916b2708c0b71
+7c6e7c8eaa83b5b807f441e66d22902500f3dda14a670b9dad7436ff7f41
+169db3df375b32fd3b8bd2eb5e49fed077a33c42f3dd28ccba2facc15d6c
+cf7572957bd548de5f9379476825cd152e9661b6e67ec7aacc8966eec66b
+e9d97a748e1833ed2d94ad7cccc7b539ff43da70f441de3dcb74175d046c
+3ac38cb26d2259a0cc13c400dc37c7f79bb5f4fdc647df6f3ae0ca571736
+03c375309b85a5482c9e80fc7dd18951cc3f40e66deecc5925d1584b690e
+306812f23e60f44e8af35984f9e707ff22a5a6cf43161e38163a67beec58
+684edf789ef87c71ccc8d2ec3979e376f89584c7b662ce8c9b6b3ba35567
+651f5eb9a7f43fb89cf97f80fe1b5550eca6
diff --git a/testing/resources/bug_1768.in b/testing/resources/bug_1768.in
new file mode 100644
index 0000000..3f06f7f
--- /dev/null
+++ b/testing/resources/bug_1768.in
@@ -0,0 +1,317 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /MarkInfo <<
+    /Marked true
+  >>
+  /StructTreeRoot 6 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+  /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q /P << /MCID 0 >> BDC Q
+q
+0 0 612 792 re W n
+BT 8 0 0 8 72 712.75 Tm /F1 1 Tf (AAA) Tj ET EMC
+/P << /MCID 1 >> BDC BT 8 0 0 8 532.8 712.75 Tm /F1 1 Tf (111) Tj ET EMC
+
+0.2835 w 2 J
+72 659.03 332.64 24.56 re S
+72 622.98 332.64 36.06 re S
+404.64 622.98 85.68 36.06 re S
+404.64 659.03 85.68 24.56 re S
+490.32 622.98 85.68 36.06 re S
+490.32 659.03 85.68 24.56 re S
+
+/P << /MCID 2 >> BDC BT 10 0 0 10 257.37 667.84 Tm /F1 1 Tf (2) Tj ET EMC
+/P << /MCID 3 >> BDC BT 10 0 0 10 428.73 667.84 Tm /F1 1 Tf (3) Tj ET EMC
+/P << /MCID 4 >> BDC BT 10 0 0 10 514.41 667.84 Tm /F1 1 Tf (4) Tj ET EMC
+/P << /MCID 5 >> BDC BT 10 0 0 10 259.87 637.54 Tm /F1 1 Tf (BB) Tj ET EMC
+/P << /MCID 6 >> BDC BT 10 0 0 10 431.23 637.54 Tm /F1 1 Tf (CC) Tj ET EMC
+/P << /MCID 7 >> BDC BT 10 0 0 10 528.16 637.54 Tm /F1 1 Tf (DD) Tj ET EMC
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 6 0}} <<
+  /Type /StructTreeRoot
+  /K 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+  /Type /StructElem
+  /S /Document
+  /P 6 0 R
+  /K [8 0 R]
+>>
+endobj
+{{object 8 0}} <<
+  /Type /StructElem
+  /S /Document
+  /P 7 0 R
+  /K [9 0 R 10 0 R]
+>>
+endobj
+{{object 9 0}} <<
+  /Type /StructElem
+  /S /Table
+  /P 8 0 R
+  /K [11 0 R]
+>>
+endobj
+{{object 10 0}} <<
+  /Type /StructElem
+  /S /Table
+  /P 8 0 R
+  /K [21 0 R 22 0 R]
+>>
+endobj
+{{object 11 0}} <<
+  /Type /StructElem
+  /S /TR
+  /P 9 0 R
+  /K [12 0 R]
+>>
+endobj
+{{object 12 0}} <<
+  /Type /StructElem
+  /S /TD
+  /P 11 0 R
+  /K [13 0 R]
+>>
+endobj
+{{object 13 0}} <<
+  /Type /StructElem
+  /S /Table
+  /P 12 0 R
+  /K [14 0 R]
+>>
+endobj
+{{object 14 0}} <<
+  /Type /StructElem
+  /S /TR
+  /P 13 0 R
+  /K [15 0 R 16 0 R]
+>>
+endobj
+{{object 15 0}} <<
+  /Type /StructElem
+  /S /TD
+  /P 14 0 R
+  /K [17 0 R]
+>>
+endobj
+{{object 16 0}} <<
+  /Type /StructElem
+  /S /TD
+  /P 14 0 R
+  /K [19 0 R]
+>>
+endobj
+{{object 17 0}} <<
+  /Type /StructElem
+  /S /P
+  /P 15 0 R
+  /K [18 0 R]
+>>
+endobj
+{{object 18 0}} <<
+  /Type /StructElem
+  /S /Span
+  /Pg 3 0 R
+  /P 17 0 R
+  /K 0
+>>
+endobj
+{{object 19 0}} <<
+  /Type /StructElem
+  /S /P
+  /P 16 0 R
+  /K [20 0 R]
+>>
+endobj
+{{object 20 0}} <<
+  /Type /StructElem
+  /S /Span
+  /Pg 3 0 R
+  /P 19 0 R
+  /K 1
+>>
+endobj
+{{object 21 0}} <<
+  /Type /StructElem
+  /S /TR
+  /P 10 0 R
+  /K [23 0 R 24 0 R 25 0 R]
+>>
+endobj
+{{object 22 0}} <<
+  /Type /StructElem
+  /S /TR
+  /P 10 0 R
+  /K [32 0 R 33 0 R 34 0 R]
+>>
+endobj
+{{object 23 0}} <<
+  /Type /StructElem
+  /S /TD
+  /P 21 0 R
+  /K [26 0 R]
+>>
+endobj
+{{object 24 0}} <<
+  /Type /StructElem
+  /S /TD
+  /P 21 0 R
+  /K [28 0 R]
+>>
+endobj
+{{object 25 0}} <<
+  /Type /StructElem
+  /S /TD
+  /P 21 0 R
+  /K [30 0 R]
+>>
+endobj
+{{object 26 0}} <<
+  /Type /StructElem
+  /S /P
+  /P 23 0 R
+  /K [27 0 R]
+>>
+endobj
+{{object 27 0}} <<
+  /Type /StructElem
+  /S /Span
+  /Pg 3 0 R
+  /P 26 0 R
+  /K 2
+>>
+endobj
+{{object 28 0}} <<
+  /Type /StructElem
+  /S /P
+  /P 24 0 R
+  /K [29 0 R]
+>>
+endobj
+{{object 29 0}} <<
+  /Type /StructElem
+  /S /Span
+  /Pg 3 0 R
+  /P 28 0 R
+  /K 3
+>>
+endobj
+{{object 30 0}} <<
+  /Type /StructElem
+  /S /P
+  /P 25 0 R
+  /K [31 0 R]
+>>
+endobj
+{{object 31 0}} <<
+  /Type /StructElem
+  /S /Span
+  /Pg 3 0 R
+  /P 30 0 R
+  /K 4
+>>
+endobj
+{{object 32 0}} <<
+  /Type /StructElem
+  /S /TD
+  /P 22 0 R
+  /K [35 0 R]
+>>
+endobj
+{{object 33 0}} <<
+  /Type /StructElem
+  /S /TD
+  /P 22 0 R
+  /K [37 0 R]
+>>
+endobj
+{{object 34 0}} <<
+  /Type /StructElem
+  /S /TD
+  /P 22 0 R
+  /K [39 0 R]
+>>
+endobj
+{{object 35 0}} <<
+  /Type /StructElem
+  /S /P
+  /P 32 0 R
+  /K [36 0 R]
+>>
+endobj
+{{object 36 0}} <<
+  /Type /StructElem
+  /S /Span
+  /Pg 3 0 R
+  /P 35 0 R
+  /K 5
+>>
+endobj
+{{object 37 0}} <<
+  /Type /StructElem
+  /S /P
+  /P 33 0 R
+  /K [38 0 R]
+>>
+endobj
+{{object 38 0}} <<
+  /Type /StructElem
+  /S /Span
+  /Pg 3 0 R
+  /P 37 0 R
+  /K 6
+>>
+endobj
+{{object 39 0}} <<
+  /Type /StructElem
+  /S /P
+  /P 34 0 R
+  /K [40 0 R]
+>>
+endobj
+{{object 40 0}} <<
+  /Type /StructElem
+  /S /Span
+  /Pg 3 0 R
+  /P 39 0 R
+  /K 7
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1768.pdf b/testing/resources/bug_1768.pdf
new file mode 100644
index 0000000..32b1644
--- /dev/null
+++ b/testing/resources/bug_1768.pdf
@@ -0,0 +1,364 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /MarkInfo <<
+    /Marked true
+  >>
+  /StructTreeRoot 6 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+  /MediaBox [0 0 612 792]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+>>
+endobj
+4 0 obj <<
+  /Length 812
+>>
+stream
+q /P << /MCID 0 >> BDC Q
+q
+0 0 612 792 re W n
+BT 8 0 0 8 72 712.75 Tm /F1 1 Tf (AAA) Tj ET EMC
+/P << /MCID 1 >> BDC BT 8 0 0 8 532.8 712.75 Tm /F1 1 Tf (111) Tj ET EMC
+
+0.2835 w 2 J
+72 659.03 332.64 24.56 re S
+72 622.98 332.64 36.06 re S
+404.64 622.98 85.68 36.06 re S
+404.64 659.03 85.68 24.56 re S
+490.32 622.98 85.68 36.06 re S
+490.32 659.03 85.68 24.56 re S
+
+/P << /MCID 2 >> BDC BT 10 0 0 10 257.37 667.84 Tm /F1 1 Tf (2) Tj ET EMC
+/P << /MCID 3 >> BDC BT 10 0 0 10 428.73 667.84 Tm /F1 1 Tf (3) Tj ET EMC
+/P << /MCID 4 >> BDC BT 10 0 0 10 514.41 667.84 Tm /F1 1 Tf (4) Tj ET EMC
+/P << /MCID 5 >> BDC BT 10 0 0 10 259.87 637.54 Tm /F1 1 Tf (BB) Tj ET EMC
+/P << /MCID 6 >> BDC BT 10 0 0 10 431.23 637.54 Tm /F1 1 Tf (CC) Tj ET EMC
+/P << /MCID 7 >> BDC BT 10 0 0 10 528.16 637.54 Tm /F1 1 Tf (DD) Tj ET EMC
+Q
+endstream
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+6 0 obj <<
+  /Type /StructTreeRoot
+  /K 7 0 R
+>>
+endobj
+7 0 obj <<
+  /Type /StructElem
+  /S /Document
+  /P 6 0 R
+  /K [8 0 R]
+>>
+endobj
+8 0 obj <<
+  /Type /StructElem
+  /S /Document
+  /P 7 0 R
+  /K [9 0 R 10 0 R]
+>>
+endobj
+9 0 obj <<
+  /Type /StructElem
+  /S /Table
+  /P 8 0 R
+  /K [11 0 R]
+>>
+endobj
+10 0 obj <<
+  /Type /StructElem
+  /S /Table
+  /P 8 0 R
+  /K [21 0 R 22 0 R]
+>>
+endobj
+11 0 obj <<
+  /Type /StructElem
+  /S /TR
+  /P 9 0 R
+  /K [12 0 R]
+>>
+endobj
+12 0 obj <<
+  /Type /StructElem
+  /S /TD
+  /P 11 0 R
+  /K [13 0 R]
+>>
+endobj
+13 0 obj <<
+  /Type /StructElem
+  /S /Table
+  /P 12 0 R
+  /K [14 0 R]
+>>
+endobj
+14 0 obj <<
+  /Type /StructElem
+  /S /TR
+  /P 13 0 R
+  /K [15 0 R 16 0 R]
+>>
+endobj
+15 0 obj <<
+  /Type /StructElem
+  /S /TD
+  /P 14 0 R
+  /K [17 0 R]
+>>
+endobj
+16 0 obj <<
+  /Type /StructElem
+  /S /TD
+  /P 14 0 R
+  /K [19 0 R]
+>>
+endobj
+17 0 obj <<
+  /Type /StructElem
+  /S /P
+  /P 15 0 R
+  /K [18 0 R]
+>>
+endobj
+18 0 obj <<
+  /Type /StructElem
+  /S /Span
+  /Pg 3 0 R
+  /P 17 0 R
+  /K 0
+>>
+endobj
+19 0 obj <<
+  /Type /StructElem
+  /S /P
+  /P 16 0 R
+  /K [20 0 R]
+>>
+endobj
+20 0 obj <<
+  /Type /StructElem
+  /S /Span
+  /Pg 3 0 R
+  /P 19 0 R
+  /K 1
+>>
+endobj
+21 0 obj <<
+  /Type /StructElem
+  /S /TR
+  /P 10 0 R
+  /K [23 0 R 24 0 R 25 0 R]
+>>
+endobj
+22 0 obj <<
+  /Type /StructElem
+  /S /TR
+  /P 10 0 R
+  /K [32 0 R 33 0 R 34 0 R]
+>>
+endobj
+23 0 obj <<
+  /Type /StructElem
+  /S /TD
+  /P 21 0 R
+  /K [26 0 R]
+>>
+endobj
+24 0 obj <<
+  /Type /StructElem
+  /S /TD
+  /P 21 0 R
+  /K [28 0 R]
+>>
+endobj
+25 0 obj <<
+  /Type /StructElem
+  /S /TD
+  /P 21 0 R
+  /K [30 0 R]
+>>
+endobj
+26 0 obj <<
+  /Type /StructElem
+  /S /P
+  /P 23 0 R
+  /K [27 0 R]
+>>
+endobj
+27 0 obj <<
+  /Type /StructElem
+  /S /Span
+  /Pg 3 0 R
+  /P 26 0 R
+  /K 2
+>>
+endobj
+28 0 obj <<
+  /Type /StructElem
+  /S /P
+  /P 24 0 R
+  /K [29 0 R]
+>>
+endobj
+29 0 obj <<
+  /Type /StructElem
+  /S /Span
+  /Pg 3 0 R
+  /P 28 0 R
+  /K 3
+>>
+endobj
+30 0 obj <<
+  /Type /StructElem
+  /S /P
+  /P 25 0 R
+  /K [31 0 R]
+>>
+endobj
+31 0 obj <<
+  /Type /StructElem
+  /S /Span
+  /Pg 3 0 R
+  /P 30 0 R
+  /K 4
+>>
+endobj
+32 0 obj <<
+  /Type /StructElem
+  /S /TD
+  /P 22 0 R
+  /K [35 0 R]
+>>
+endobj
+33 0 obj <<
+  /Type /StructElem
+  /S /TD
+  /P 22 0 R
+  /K [37 0 R]
+>>
+endobj
+34 0 obj <<
+  /Type /StructElem
+  /S /TD
+  /P 22 0 R
+  /K [39 0 R]
+>>
+endobj
+35 0 obj <<
+  /Type /StructElem
+  /S /P
+  /P 32 0 R
+  /K [36 0 R]
+>>
+endobj
+36 0 obj <<
+  /Type /StructElem
+  /S /Span
+  /Pg 3 0 R
+  /P 35 0 R
+  /K 5
+>>
+endobj
+37 0 obj <<
+  /Type /StructElem
+  /S /P
+  /P 33 0 R
+  /K [38 0 R]
+>>
+endobj
+38 0 obj <<
+  /Type /StructElem
+  /S /Span
+  /Pg 3 0 R
+  /P 37 0 R
+  /K 6
+>>
+endobj
+39 0 obj <<
+  /Type /StructElem
+  /S /P
+  /P 34 0 R
+  /K [40 0 R]
+>>
+endobj
+40 0 obj <<
+  /Type /StructElem
+  /S /Span
+  /Pg 3 0 R
+  /P 39 0 R
+  /K 7
+>>
+endobj
+xref
+0 41
+0000000000 65535 f 
+0000000015 00000 n 
+0000000129 00000 n 
+0000000218 00000 n 
+0000000370 00000 n 
+0000001234 00000 n 
+0000001312 00000 n 
+0000001368 00000 n 
+0000001448 00000 n 
+0000001535 00000 n 
+0000001613 00000 n 
+0000001699 00000 n 
+0000001775 00000 n 
+0000001852 00000 n 
+0000001932 00000 n 
+0000002016 00000 n 
+0000002093 00000 n 
+0000002170 00000 n 
+0000002246 00000 n 
+0000002330 00000 n 
+0000002406 00000 n 
+0000002490 00000 n 
+0000002581 00000 n 
+0000002672 00000 n 
+0000002749 00000 n 
+0000002826 00000 n 
+0000002903 00000 n 
+0000002979 00000 n 
+0000003063 00000 n 
+0000003139 00000 n 
+0000003223 00000 n 
+0000003299 00000 n 
+0000003383 00000 n 
+0000003460 00000 n 
+0000003537 00000 n 
+0000003614 00000 n 
+0000003690 00000 n 
+0000003774 00000 n 
+0000003850 00000 n 
+0000003934 00000 n 
+0000004010 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 41
+>>
+startxref
+4094
+%%EOF
diff --git a/testing/resources/bug_1769.in b/testing/resources/bug_1769.in
new file mode 100644
index 0000000..a6f6682
--- /dev/null
+++ b/testing/resources/bug_1769.in
@@ -0,0 +1,72 @@
+{{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 <<
+      /Im1 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+1000 0 0 1000 50 100 cm
+/Im1 Do
+Q
+q
+1 0 0 1 200 100 cm
+/Im1 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 100 100]
+  /Resources <<
+    /Font <<
+      /F1 6 0 R
+    >>
+  >>
+{{streamlen}}
+>>
+stream
+0.001 0 0 0.001 0 0 cm
+BT
+/F1 24 Tf
+(w) Tj
+(o) Tj
+(r) Tj
+(l) Tj
+(d) Tj
+ET
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1769.pdf b/testing/resources/bug_1769.pdf
new file mode 100644
index 0000000..3a9fba1
--- /dev/null
+++ b/testing/resources/bug_1769.pdf
@@ -0,0 +1,85 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+  /Resources <<
+    /XObject <<
+      /Im1 5 0 R
+    >>
+  >>
+>>
+endobj
+4 0 obj <<
+  /Length 67
+>>
+stream
+q
+1000 0 0 1000 50 100 cm
+/Im1 Do
+Q
+q
+1 0 0 1 200 100 cm
+/Im1 Do
+Q
+endstream
+endobj
+5 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 100 100]
+  /Resources <<
+    /Font <<
+      /F1 6 0 R
+    >>
+  >>
+/Length 74
+>>
+stream
+0.001 0 0 0.001 0 0 cm
+BT
+/F1 24 Tf
+(w) Tj
+(o) Tj
+(r) Tj
+(l) Tj
+(d) Tj
+ET
+endstream
+endobj
+6 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000287 00000 n 
+0000000405 00000 n 
+0000000655 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+731
+%%EOF
diff --git a/testing/resources/bug_1919.pdf b/testing/resources/bug_1919.pdf
new file mode 100644
index 0000000..8331e0d
--- /dev/null
+++ b/testing/resources/bug_1919.pdf
Binary files differ
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/bug_306123.pdf b/testing/resources/bug_306123.pdf
index 5bd1900..8b542cc 100644
--- a/testing/resources/bug_306123.pdf
+++ b/testing/resources/bug_306123.pdf
@@ -1243,7 +1243,7 @@
 endobj
 9 0 obj <<
   /Type /Page
-  /Parent 2 0 R
+  /Parent 8 0 R
   /MediaBox [0 0 612 792]
 >>
 endobj
diff --git a/testing/resources/bug_481363.in b/testing/resources/bug_481363.in
index 32a724d..1e05346 100644
--- a/testing/resources/bug_481363.in
+++ b/testing/resources/bug_481363.in
@@ -29,7 +29,7 @@
 100 500 100 100 re b
 endstream
 endobj
-{{object 5 0)) <<
+{{object 5 0}} <<
   /Type /Font
   /Subtype /Type1
   /BaseFont /He
diff --git a/testing/resources/bug_481363.pdf b/testing/resources/bug_481363.pdf
index 53468a0..8263b3f 100644
--- a/testing/resources/bug_481363.pdf
+++ b/testing/resources/bug_481363.pdf
@@ -30,7 +30,7 @@
 100 500 100 100 re b
 endstream
 endobj
-{{object 5 0)) <<
+5 0 obj <<
   /Type /Font
   /Subtype /Type1
   /BaseFont /He
@@ -51,12 +51,12 @@
 0000000078 00000 n 
 0000000253 00000 n 
 0000000306 00000 n 
-0000000000 65535 f 
-0000000517 00000 n 
+0000000400 00000 n 
+0000000510 00000 n 
 trailer <<
   /Size 0
   /Root 3 0 R
 >>
 startxref
-621
+614
 %%EOF
diff --git a/testing/resources/bug_674771.in b/testing/resources/bug_674771.in
new file mode 100644
index 0000000..3352f30
--- /dev/null
+++ b/testing/resources/bug_674771.in
@@ -0,0 +1,56 @@
+{{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 792 612]
+  /Resources <<
+    /ProcSet [/PDF /ImageB]
+    /XObject <<
+      /Im0 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+400 0 0 300 60 100 cm
+/Im0 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 1
+  /Decode [0.0 1.0]
+  /Filter [/ASCIIHexDecode /JBIG2Decode]
+  /Height 400
+  /ImageMask true
+  /Width 400
+  {{streamlen}}
+>>
+stream
+000000003000010000001300000dea00000353000017110000171151000000000001260001000000
+3500000dea000003530000000000000000020003fffdff02fefefeff7f86530fb6c922cfff7fff7f
+ff7fff7fff7fff7fff7fff7fffac
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_674771.pdf b/testing/resources/bug_674771.pdf
new file mode 100644
index 0000000..6b2dab8
--- /dev/null
+++ b/testing/resources/bug_674771.pdf
@@ -0,0 +1,68 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents [4 0 R]
+  /MediaBox [0 0 792 612]
+  /Resources <<
+    /ProcSet [/PDF /ImageB]
+    /XObject <<
+      /Im0 5 0 R
+    >>
+  >>
+>>
+endobj
+4 0 obj <<
+  /Length 34
+>>
+stream
+q
+400 0 0 300 60 100 cm
+/Im0 Do
+Q
+endstream
+endobj
+5 0 obj <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 1
+  /Decode [0.0 1.0]
+  /Filter [/ASCIIHexDecode /JBIG2Decode]
+  /Height 400
+  /ImageMask true
+  /Width 400
+  /Length 191
+>>
+stream
+000000003000010000001300000dea00000353000017110000171151000000000001260001000000
+3500000dea000003530000000000000000020003fffdff02fefefeff7f86530fb6c922cfff7fff7f
+ff7fff7fff7fff7fff7fff7fffac
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000317 00000 n 
+0000000402 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+808
+%%EOF
diff --git a/testing/resources/bug_680376.in b/testing/resources/bug_680376.in
index c21df24..32daee1 100644
--- a/testing/resources/bug_680376.in
+++ b/testing/resources/bug_680376.in
@@ -85,8 +85,8 @@
 >>
 endobj
 % Old-style top-level Dests dictionary. Note that FirstAlternate
-% intentionally references non-exisstant page 11 and LastAlternate
-% intentionally references non-existant object 999.
+% intentionally references non-existent page 11 and LastAlternate
+% intentionally references non-existent object 999.
 {{object 14 0}} <<
   /FirstAlternate [11 /XYZ 200 400 800]
   /LastAlternate  <</D [999 0 R /XYZ 0 0 -200]>>
diff --git a/testing/resources/bug_680376.pdf b/testing/resources/bug_680376.pdf
index fb01c57..8d7a884 100644
--- a/testing/resources/bug_680376.pdf
+++ b/testing/resources/bug_680376.pdf
@@ -1,5 +1,5 @@
 %PDF-1.7
-% ò¤ô
+% ò¤ô
 1 0 obj <<
   /Type /Catalog
   /Pages 2 0 R
@@ -86,8 +86,8 @@
 >>
 endobj
 % Old-style top-level Dests dictionary. Note that FirstAlternate
-% intentionally references non-exisstant page 11 and LastAlternate
-% intentionally references non-existant object 999.
+% intentionally references non-existent page 11 and LastAlternate
+% intentionally references non-existent object 999.
 14 0 obj <<
   /FirstAlternate [11 /XYZ 200 400 800]
   /LastAlternate  <</D [999 0 R /XYZ 0 0 -200]>>
@@ -138,19 +138,19 @@
 0000000903 00000 n 
 0000000993 00000 n 
 0000000000 65535 f 
-0000001287 00000 n 
-0000001415 00000 n 
+0000001286 00000 n 
+0000001414 00000 n 
 0000000000 65535 f 
 0000000000 65535 f 
 0000000000 65535 f 
 0000000000 65535 f 
 0000000000 65535 f 
-0000001510 00000 n 
-0000001620 00000 n 
+0000001509 00000 n 
+0000001619 00000 n 
 trailer <<
   /Size 6
   /Root 1 0 R
 >>
 startxref
-1708
+1707
 %%EOF
diff --git a/testing/resources/bug_765384.in b/testing/resources/bug_765384.in
index e7e46c7..69256bc 100644
--- a/testing/resources/bug_765384.in
+++ b/testing/resources/bug_765384.in
@@ -66,7 +66,7 @@
   /JS 21 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 21 0}} <<
 >>
 stream
diff --git a/testing/resources/bug_765384.pdf b/testing/resources/bug_765384.pdf
index e820fcc..41e042d 100644
--- a/testing/resources/bug_765384.pdf
+++ b/testing/resources/bug_765384.pdf
@@ -67,7 +67,7 @@
   /JS 21 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 21 0 obj <<
 >>
 stream
diff --git a/testing/resources/bug_821454.in b/testing/resources/bug_821454.in
index 168c983..ee7f20c 100644
--- a/testing/resources/bug_821454.in
+++ b/testing/resources/bug_821454.in
@@ -1,71 +1,63 @@
 {{header}}
-{{object 1 0}}
-<<
+{{object 1 0}} <<
   /Type /Catalog
   /Pages 2 0 R
-  /Names 14 0 R
-  /AcroForm << /Fields [ 8 0 R 9 0 R ] >>
+  /Names 7 0 R
+  /AcroForm <<
+    /Fields [ 5 0 R 6 0 R ]
+  >>
 >>
 endobj
-{{object 2 0}}
-<<
+{{object 2 0}} <<
   /Type /Pages
   /Count 1
   /Kids [ 3 0 R ]
 >>
 endobj
-{{object 3 0}}
-<<
+{{object 3 0}} <<
   /Type /Page
   /Parent 2 0 R
-  /Contents 6 0 R
+  /Contents 4 0 R
   /MediaBox [ 0 0 300 600 ]
-  /Annots [ 8 0 R 9 0 R ]
+  /Annots [ 5 0 R 6 0 R ]
 >>
 endobj
-{{object 6 0}}
-<<
-{{streamlen}}
+{{object 4 0}} <<
+  {{streamlen}}
 >>
 stream
 endstream
 endobj
-{{object 8 0}}
-<<
+{{object 5 0}} <<
   /Type /Annot
   /Subtype /Link
   /Rect [ 100 350 200 380 ]
   /Dest (target10)
 >>
 endobj
-{{object 9 0}}
-<<
+{{object 6 0}} <<
   /Type /Annot
   /Subtype /Link
   /Rect [ 100 400 200 430 ]
   /Dest (target100)
 >>
 endobj
-{{object 14 0}}
-<<
-  /Dests 15 0 R
+{{object 7 0}} <<
+  /Dests 8 0 R
 >>
 endobj
-{{object 15 0}}
-<<
+{{object 8 0}} <<
   /Names [
-    (target10) 16 0 R
-    (target100) 17 0 R
+    (target10) 9 0 R
+    (target100) 10 0 R
   ]
 >>
 endobj
-{{object 16 0}}
-<<
+{{object 9 0}} <<
   /D [3 0 R /XYZ 100 200 0]
 >>
 endobj
-{{object 17 0}}
-<<
+{{object 10 0}} <<
   /D [3 0 R /XYZ 150 250 0]
 >>
 endobj
diff --git a/testing/resources/bug_821454.pdf b/testing/resources/bug_821454.pdf
index d6229a5..1e11c13 100644
--- a/testing/resources/bug_821454.pdf
+++ b/testing/resources/bug_821454.pdf
@@ -1,96 +1,84 @@
 %PDF-1.7
 % ò¤ô
-1 0 obj
-<<
+1 0 obj <<
   /Type /Catalog
   /Pages 2 0 R
-  /Names 14 0 R
-  /AcroForm << /Fields [ 8 0 R 9 0 R ] >>
+  /Names 7 0 R
+  /AcroForm <<
+    /Fields [ 5 0 R 6 0 R ]
+  >>
 >>
 endobj
-2 0 obj
-<<
+2 0 obj <<
   /Type /Pages
   /Count 1
   /Kids [ 3 0 R ]
 >>
 endobj
-3 0 obj
-<<
+3 0 obj <<
   /Type /Page
   /Parent 2 0 R
-  /Contents 6 0 R
+  /Contents 4 0 R
   /MediaBox [ 0 0 300 600 ]
-  /Annots [ 8 0 R 9 0 R ]
+  /Annots [ 5 0 R 6 0 R ]
 >>
 endobj
-6 0 obj
-<<
-/Length 0
+4 0 obj <<
+  /Length 0
 >>
 stream
 endstream
 endobj
-8 0 obj
-<<
+5 0 obj <<
   /Type /Annot
   /Subtype /Link
   /Rect [ 100 350 200 380 ]
   /Dest (target10)
 >>
 endobj
-9 0 obj
-<<
+6 0 obj <<
   /Type /Annot
   /Subtype /Link
   /Rect [ 100 400 200 430 ]
   /Dest (target100)
 >>
 endobj
-14 0 obj
-<<
-  /Dests 15 0 R
+7 0 obj <<
+  /Dests 8 0 R
 >>
 endobj
-15 0 obj
-<<
+8 0 obj <<
   /Names [
-    (target10) 16 0 R
-    (target100) 17 0 R
+    (target10) 9 0 R
+    (target100) 10 0 R
   ]
 >>
 endobj
-16 0 obj
-<<
+9 0 obj <<
   /D [3 0 R /XYZ 100 200 0]
 >>
 endobj
-17 0 obj
-<<
+10 0 obj <<
   /D [3 0 R /XYZ 150 250 0]
 >>
 endobj
 xref
-0 18
+0 11
 0000000000 65535 f 
 0000000015 00000 n 
-0000000126 00000 n 
-0000000191 00000 n 
-0000000000 65535 f 
-0000000000 65535 f 
-0000000314 00000 n 
-0000000000 65535 f 
-0000000362 00000 n 
-0000000462 00000 n 
-0000000000 65535 f 
-0000000000 65535 f 
-0000000000 65535 f 
-0000000000 65535 f 
-0000000563 00000 n 
-0000000601 00000 n 
-0000000683 00000 n 
-0000000733 00000 n 
-trailer<< /Root 1 0 R /Size 18 >>
+0000000131 00000 n 
+0000000196 00000 n 
+0000000319 00000 n 
+0000000369 00000 n 
+0000000469 00000 n 
+0000000570 00000 n 
+0000000606 00000 n 
+0000000686 00000 n 
+0000000735 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 11
+>>
 startxref
-783
+785
 %%EOF
diff --git a/testing/resources/bug_889099.in b/testing/resources/bug_889099.in
new file mode 100644
index 0000000..77ae5f4
--- /dev/null
+++ b/testing/resources/bug_889099.in
@@ -0,0 +1,64 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [4 0 R]
+    /DR 5 0 R
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 5 0 R
+  /MediaBox [0 0 300 -400]
+  /Contents 7 0 R
+  /Annots [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /DA (0 0 0 rg /F1 12 Tf)
+  /F 4
+  /Rect [100 100 200 -130]
+  /T (Text Box)
+  /V (Good :])
+>>
+endobj
+{{object 5 0}} <<
+  /Font <<
+    /F1 6 0 R
+  >>
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+0 0 0 rg
+/F1 12 Tf
+100 150 Td
+(Bad :[) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_889099.pdf b/testing/resources/bug_889099.pdf
new file mode 100644
index 0000000..a406cd1
--- /dev/null
+++ b/testing/resources/bug_889099.pdf
@@ -0,0 +1,78 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [4 0 R]
+    /DR 5 0 R
+  >>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 5 0 R
+  /MediaBox [0 0 300 -400]
+  /Contents 7 0 R
+  /Annots [4 0 R]
+>>
+endobj
+4 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /DA (0 0 0 rg /F1 12 Tf)
+  /F 4
+  /Rect [100 100 200 -130]
+  /T (Text Box)
+  /V (Good :])
+>>
+endobj
+5 0 obj <<
+  /Font <<
+    /F1 6 0 R
+  >>
+>>
+endobj
+6 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+7 0 obj <<
+  /Length 48
+>>
+stream
+BT
+0 0 0 rg
+/F1 12 Tf
+100 150 Td
+(Bad :[) Tj
+ET
+endstream
+endobj
+xref
+0 8
+0000000000 65535 f 
+0000000015 00000 n 
+0000000122 00000 n 
+0000000185 00000 n 
+0000000318 00000 n 
+0000000475 00000 n 
+0000000526 00000 n 
+0000000602 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 8
+>>
+startxref
+701
+%%EOF
diff --git a/testing/resources/bug_921.in b/testing/resources/bug_921.in
index a0c2e73..ccd44fb 100644
--- a/testing/resources/bug_921.in
+++ b/testing/resources/bug_921.in
@@ -43,7 +43,7 @@
 >>
 endobj
 {{object 6 0}} <<
-{{streamlen}}
+  {{streamlen}}
 >>
 stream
 BT
diff --git a/testing/resources/bug_921.pdf b/testing/resources/bug_921.pdf
index fb3950e..84e0755 100644
--- a/testing/resources/bug_921.pdf
+++ b/testing/resources/bug_921.pdf
@@ -44,7 +44,7 @@
 >>
 endobj
 6 0 obj <<
-/Length 2784
+  /Length 2784
 >>
 stream
 BT
@@ -68,7 +68,10 @@
 0000000287 00000 n 
 0000000412 00000 n 
 0000000593 00000 n 
-trailer<< /Root 1 0 R /Size 7 >>
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
 startxref
-3428
+3430
 %%EOF
diff --git a/testing/resources/bug_925981.in b/testing/resources/bug_925981.in
index 3d6e37a..f5f8ebe 100644
--- a/testing/resources/bug_925981.in
+++ b/testing/resources/bug_925981.in
@@ -36,7 +36,7 @@
 >>
 endobj
 {{object 6 0}} <<
-{{streamlen}}
+  {{streamlen}}
 >>
 stream
 BT
diff --git a/testing/resources/bug_925981.pdf b/testing/resources/bug_925981.pdf
index 8d856e1..5c35e38 100644
--- a/testing/resources/bug_925981.pdf
+++ b/testing/resources/bug_925981.pdf
@@ -37,7 +37,7 @@
 >>
 endobj
 6 0 obj <<
-/Length 83
+  /Length 83
 >>
 stream
 BT
@@ -64,11 +64,11 @@
 0000000309 00000 n 
 0000000387 00000 n 
 0000000463 00000 n 
-0000000595 00000 n 
+0000000597 00000 n 
 trailer <<
   /Root 1 0 R
   /Size 8
 >>
 startxref
-646
+648
 %%EOF
diff --git a/testing/resources/bug_972518.pdf b/testing/resources/bug_972518.pdf
index 51185f5..fde54f8 100644
--- a/testing/resources/bug_972518.pdf
+++ b/testing/resources/bug_972518.pdf
@@ -60,7 +60,7 @@
 endobj
 9 0 obj <<
   /Type /Page
-  /Parent 2 0 R
+  /Parent 8 0 R
   /MediaBox [0 0 612 792]
 >>
 endobj
diff --git a/testing/resources/dashed_lines.in b/testing/resources/dashed_lines.in
new file mode 100644
index 0000000..b4f74bb
--- /dev/null
+++ b/testing/resources/dashed_lines.in
@@ -0,0 +1,37 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 200 100]
+  /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
+10 25 m 190 25 l S
+[6 5 4 3 2 1] 5 d
+10 50 m 190 50 l S
+[] 0 d
+10 75 m 190 75 l S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/dashed_lines.pdf b/testing/resources/dashed_lines.pdf
new file mode 100644
index 0000000..f40a591
--- /dev/null
+++ b/testing/resources/dashed_lines.pdf
@@ -0,0 +1,48 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [0 0 200 100]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+>>
+endobj
+4 0 obj <<
+  /Length 95
+>>
+stream
+q
+0 0 0 rg
+10 25 m 190 25 l S
+[6 5 4 3 2 1] 5 d
+10 50 m 190 50 l S
+[] 0 d
+10 75 m 190 75 l S
+Q
+endstream
+endobj
+xref
+0 5
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000157 00000 n 
+0000000226 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 5
+>>
+startxref
+372
+%%EOF
diff --git a/testing/resources/docmdp.in b/testing/resources/docmdp.in
new file mode 100644
index 0000000..a8fe283
--- /dev/null
+++ b/testing/resources/docmdp.in
@@ -0,0 +1,88 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [7 0 R]
+    /SigFlags 3
+  >>
+>>
+endobj
+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
+  /Annots [7 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
+{{object 5 0}} <<
+  /Type /Sig
+  /Reference [
+    <<
+      /Type /SigRef
+      /TransformMethod /DocMDP
+      /TransformParams <<
+        /Type /TransformParams
+        /P 1
+        /V /1.2
+      >>
+    >>
+  ]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 0 0]
+  /Length 0
+>>
+stream
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Sig
+  /F 132
+  /Rect [0 0 0 0]
+  /P 3 0 R
+  /T (Signature1)
+  /V 5 0 R
+  /DV 5 0 R
+  /AP <<
+    /N 6 0 R
+  >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/docmdp.pdf b/testing/resources/docmdp.pdf
new file mode 100644
index 0000000..0191a00
--- /dev/null
+++ b/testing/resources/docmdp.pdf
@@ -0,0 +1,102 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [7 0 R]
+    /SigFlags 3
+  >>
+>>
+endobj
+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
+  /Annots [7 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
+5 0 obj <<
+  /Type /Sig
+  /Reference [
+    <<
+      /Type /SigRef
+      /TransformMethod /DocMDP
+      /TransformParams <<
+        /Type /TransformParams
+        /P 1
+        /V /1.2
+      >>
+    >>
+  ]
+>>
+endobj
+6 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 0 0]
+  /Length 0
+>>
+stream
+endstream
+endobj
+7 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Sig
+  /F 132
+  /Rect [0 0 0 0]
+  /P 3 0 R
+  /T (Signature1)
+  /V 5 0 R
+  /DV 5 0 R
+  /AP <<
+    /N 6 0 R
+  >>
+>>
+endobj
+xref
+0 8
+0000000000 65535 f 
+0000000015 00000 n 
+0000000131 00000 n 
+0000000220 00000 n 
+0000000307 00000 n 
+0000000547 00000 n 
+0000000760 00000 n 
+0000000862 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 8
+>>
+startxref
+1034
+%%EOF
diff --git a/testing/resources/embedded_attachments_invalid_data.in b/testing/resources/embedded_attachments_invalid_data.in
new file mode 100644
index 0000000..26545a4
--- /dev/null
+++ b/testing/resources/embedded_attachments_invalid_data.in
@@ -0,0 +1,41 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Names 4 0 R
+  /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
+  /MediaBox [0 0 612 792]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  /EmbeddedFiles 5 0 R
+>>
+endobj
+{{object 5 0}} <<
+  /Names [(1.txt) 6 0 R]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Filespec
+  /Desc ()
+  /F (1.txt)
+  /UF (1.txt)
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/embedded_attachments_invalid_data.pdf b/testing/resources/embedded_attachments_invalid_data.pdf
new file mode 100644
index 0000000..8a830e2
--- /dev/null
+++ b/testing/resources/embedded_attachments_invalid_data.pdf
@@ -0,0 +1,54 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Names 4 0 R
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+>>
+endobj
+4 0 obj <<
+  /EmbeddedFiles 5 0 R
+>>
+endobj
+5 0 obj <<
+  /Names [(1.txt) 6 0 R]
+>>
+endobj
+6 0 obj <<
+  /Type /Filespec
+  /Desc ()
+  /F (1.txt)
+  /UF (1.txt)
+>>
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000083 00000 n 
+0000000146 00000 n 
+0000000264 00000 n 
+0000000308 00000 n 
+0000000354 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+431
+%%EOF
diff --git a/testing/resources/find_text_consecutive.in b/testing/resources/find_text_consecutive.in
index 9e35e1d..d484584 100644
--- a/testing/resources/find_text_consecutive.in
+++ b/testing/resources/find_text_consecutive.in
@@ -29,7 +29,7 @@
 >>
 endobj
 {{object 5 0}} <<
-{{streamlen}}
+  {{streamlen}}
 >>
 stream
 BT
diff --git a/testing/resources/find_text_consecutive.pdf b/testing/resources/find_text_consecutive.pdf
index 5083d75..bd78380 100644
--- a/testing/resources/find_text_consecutive.pdf
+++ b/testing/resources/find_text_consecutive.pdf
@@ -30,7 +30,7 @@
 >>
 endobj
 5 0 obj <<
-/Length 51
+  /Length 51
 >>
 stream
 BT
@@ -53,5 +53,5 @@
   /Size 6
 >>
 startxref
-465
+467
 %%EOF
diff --git a/testing/resources/fonts/SymbolNeu.ttf b/testing/resources/fonts/SymbolNeu.ttf
new file mode 100644
index 0000000..08932c9
--- /dev/null
+++ b/testing/resources/fonts/SymbolNeu.ttf
Binary files differ
diff --git a/testing/resources/get_page_aaction.in b/testing/resources/get_page_aaction.in
new file mode 100644
index 0000000..83f0c01
--- /dev/null
+++ b/testing/resources/get_page_aaction.in
@@ -0,0 +1,50 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Kids [
+    3 0 R
+    4 0 R
+  ]
+  /Count 2
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /AA <<
+    /O <<
+      /F (\\\\127.0.0.1\\c$\\Program Files\\test.exe)
+      /D [1 /Fit]
+      /S /GoToE
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /AA <<
+    /O <<
+      /F (\\\\127.0.0.1\\c$\\Program Files\\test_page_two_open.exe)
+      /D [1 /Fit]
+      /S /GoToE
+    >>
+    /C <<
+      /F (\\\\127.0.0.1\\c$\\Program Files\\test_page_two_close.exe)
+      /D [1 /Fit]
+      /S /GoToE
+    >>
+  >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/get_page_aaction.pdf b/testing/resources/get_page_aaction.pdf
new file mode 100644
index 0000000..7d46651
--- /dev/null
+++ b/testing/resources/get_page_aaction.pdf
@@ -0,0 +1,61 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Kids [
+    3 0 R
+    4 0 R
+  ]
+  /Count 2
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /AA <<
+    /O <<
+      /F (\\\\127.0.0.1\\c$\\Program Files\\test.exe)
+      /D [1 /Fit]
+      /S /GoToE
+    >>
+  >>
+>>
+endobj
+4 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /AA <<
+    /O <<
+      /F (\\\\127.0.0.1\\c$\\Program Files\\test_page_two_open.exe)
+      /D [1 /Fit]
+      /S /GoToE
+    >>
+    /C <<
+      /F (\\\\127.0.0.1\\c$\\Program Files\\test_page_two_close.exe)
+      /D [1 /Fit]
+      /S /GoToE
+    >>
+  >>
+>>
+endobj
+xref
+0 5
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000149 00000 n 
+0000000345 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 5
+>>
+startxref
+675
+%%EOF
diff --git a/testing/resources/gotoe_action.in b/testing/resources/gotoe_action.in
new file mode 100644
index 0000000..04b7cf0
--- /dev/null
+++ b/testing/resources/gotoe_action.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 200]
+  /Count 1
+  /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 /Link
+  /FT /Tx
+  /Ff 29360128
+  /A 5 0 R
+  /T (txtName)
+  /F 4
+  /M (D:20150514070426+05'30')
+  /Rect [1 1 199 199]
+  /BS  <<
+    /W 1
+    /S /S
+  >>
+  /DA (/Helv 0 Tf 0 0 0 rg)
+  /V ()
+>>
+endobj
+{{object 5 0}} <<
+  /S /GoToE
+  /D [1 /Fit]
+  /F (ExampleFile.pdf)
+>>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/gotoe_action.pdf b/testing/resources/gotoe_action.pdf
new file mode 100644
index 0000000..8d588e0
--- /dev/null
+++ b/testing/resources/gotoe_action.pdf
@@ -0,0 +1,60 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [0 0 200 200]
+  /Count 1
+  /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 /Link
+  /FT /Tx
+  /Ff 29360128
+  /A 5 0 R
+  /T (txtName)
+  /F 4
+  /M (D:20150514070426+05'30')
+  /Rect [1 1 199 199]
+  /BS  <<
+    /W 1
+    /S /S
+  >>
+  /DA (/Helv 0 Tf 0 0 0 rg)
+  /V ()
+>>
+endobj
+5 0 obj <<
+  /S /GoToE
+  /D [1 /Fit]
+  /F (ExampleFile.pdf)
+>>
+>>
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000157 00000 n 
+0000000226 00000 n 
+0000000460 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+533
+%%EOF
diff --git a/testing/resources/gray-alpha.jp2 b/testing/resources/gray-alpha.jp2
new file mode 100644
index 0000000..f5e3f34
--- /dev/null
+++ b/testing/resources/gray-alpha.jp2
Binary files differ
diff --git a/testing/resources/gray.jp2 b/testing/resources/gray.jp2
new file mode 100644
index 0000000..53293bd
--- /dev/null
+++ b/testing/resources/gray.jp2
Binary files differ
diff --git a/testing/resources/hebrew_mirrored.in b/testing/resources/hebrew_mirrored.in
new file mode 100644
index 0000000..09ede5d
--- /dev/null
+++ b/testing/resources/hebrew_mirrored.in
@@ -0,0 +1,160 @@
+{{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 200]
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+/F1 12 Tf
+150 160 Td
+[<01>2<02>2<03>-4<02>2<04>5<05>]TJ
+ET
+
+BT
+0 100 Td
+-1 0 0 1 50 40 Tm
+[<01>5<05>]TJ
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /BAAAAA+NotoSansHebrew-Regular
+  /FirstChar 0
+  /FontDescriptor 6 0 R
+  /LastChar 5
+  /ToUnicode 8 0 R
+  /Widths [600 294 235 645 397 542]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /FontDescriptor
+  /Ascent 1069
+  /CapHeight 869
+  /Descent -293
+  /Flags 4
+  /FontBBox [-210 -252 716 869]
+  /FontFile2 7 0 R
+  /FontName /BAAAAA+NotoSansHebrew-Regular
+  /ItalicAngle 0
+  /StemV 80
+>>
+endobj
+{{object 7 0}} <<
+  /Filter [/ASCII85Decode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+GhVOeCMXsCFZt#(%p<i9AU.uO6]Ye/ifKHXbLV!I1<gsgHY$Eg-tbPt@rk=Ap?l+FQ/u;^8JASXVQS>V
+nuG<j"nF$,Rii%*89oFrq6<34,1m)cJ0IF5(22*&"c<?^q>^jZEkc[.-qdl^dk1F&,fKT9F2)eaH/1QX
+<gN4)W0n]g:798;]<j42dYR+b7&$II`er0Do2PVi55OH.DU&3oF7^D>=p%W;pT6OOkC.gL0;aQZ%EQ]$
+CMpO/ouiOe*=oqMFW5p^ge<JWIt[XVg[cqr8aR%^g[TKTpYK&S\F4)WJbd&hhE(,./q'ef/\k-FOKm`h
+f=q)o6_0Sd-Ap"u/`'bn6!o+8Zu;5]Q^2D[J,6jbf.d4Ck't7.im"Z=^^/7D4VXdnJ*hX.ia&@S<Zl_D
+Xq`#Xlf3.[Q6GL-.IS+nj<JEQ1+=HefoeH8Qi0[)c<.\^P$4LTD?DamV./;?-PDR^8[b$k8'^LB2C4&'
+NUg=HC6k8A'.Knb7Wh)elDCX74)L`tc?`Z\Sb;i$>AElUWZdj+!8FWu:gnKPdkmGSM;%q39@mC[7<ZEH
+BP,jX746Y[8VL'e=mlrkg+[&/4Xn#1'(72pCc`cXNN+/Z0?DM&g%BgA@co7e)9J3X;X\\(5@"=N^/d""
+T`@poCcT@G`,Q43B>^KnLk;$<#C1`ZRI:qa#-tMr)R?VE.RR\7=sKe@N)#YuA]JfMaHgYN/!?c&OsZTe
+^cXe0%l-1*krrj[$8W`Wl)*K?>t9dJirfrR7MYH\=XPN5Q2qL_I4eudg4b]KRT\<OAl/T-PZ'?3dZ$p;
+<A/YSC[`O')u&1O:[moi)'2=Vd5KR72=0E6LAR(+/#@80mS5hidn)NRbS3]PMfW2+=&LegaZXffSM\+*
+%@ESe'CB7@?)cYMPG3X<A98q?pK.WHgPW4%FOOV*1/i@`;U-_0)m/crX^pV$",X/"BsMn?;R::1,iO?7
+d*"n-c#gLp.bPO!Jk.*r-gP*5[&0>a[qOG`>on_9`Lhgl?)c@P!H#2#PFffIEAQP^TS_DS:Xem?lK2!M
+h#c+\A=NN1)ZW-WA38,70l'k`MSW-$8rA0><3W]ffk"ZqCrdIAQBE9>j@mZ'<HHGofhsV-;mOl@]Mb1L
+qSIIYnkLM"Z97EVYElh]%[>>bb#jgf**OlY[IHaK5,@C./bHbDalE":hWu.1N,@;S&gl,n3:<uV$;N?L
+*(2O_s!JrF=fbn$ki"!LOY9qefX_V\<NFjuO0ngY8TpJ(,;39KIP'3^N)o:sU^S>oeCuK%7\,dgAiUN1
+E!0r_=$ol73>k&7]_*E"mUSrX[62980+uUk50ME#I*V:L4N8s+bi=/qMstu,ntJ5f[(HSa5@JhaGYD:Q
+]auqs7`nFM/]DAtfVG=ND6[_hnTB=9[;GHTf&F!MBNQP7P.LnAZ2VP"Ploh`?NTa8nEUYFj3^8B4<?t>
+4SBp^*fhF-*fhL/*r?NkO.Sgf4:WJt*n-E0W]KmAKl)1[L<V@'WjXmHBl;cn9--aK@lTjrE@*#k?-6on
+,:;=;+3B=[oI&q3p0&\dDEW8m^^H(gHjR8,7ldq.4hG]/>/mf;Hl1F^:B&.Xiqu!h(RA"GpXOmI2eJ(p
+-Ufu7H4'I5!71?\VoB`KajX::mq>=Rr&Q;g]E7s$-kZ.E)/3T4mfoT]_V3^s3J#ALKI7nF?TacHK]qhj
+L<ncjl]HN[$fPFZ;`(\GZEhSIf%/9H@>qA*jpO7b7bY=!,<-,5gQ6!\>V]AQ)^-GB`&C"Q_Tt5F^2b04
+H^dXF*&#KkrUdWPo<maGSjIV2C<A6%Y#l1eDB-11=5MCe/mQ;H^`5k(:$aRZO01rVm/hX\fYmD8f*Dj@
+M@i=qKg0)_(>4O.G!a5^E2>bO$]QiDc\2C6pdgJSEti>7EGJ-(WqC@QJiDd@NbtVSKCL`LBr7+Q7e(#%
+mfT9"IKcR0OZpuAD#Y..(\(Ya*`U>DQ9W"+'>%SR14Rbu3okUD1UoZtSAfKYf.IftGdnZe:>-X$>r]=V
+G-"HZ$ThCqcd_nA%GR%YOrq)?cB9h+fAIVB(kUJ,MLq_e+>iX=AF@bgIcap`;p5$2F+]o@5&$NLhu:.I
+2<(<.=8A484n4$P`>fQKA"tBRWbq]tDur6ODulS&f!BB<ZPSc21WW3#buP0(+9gU#Z,bH/SFFh24G?`5
+dOt)^QkNbrnhfi)90SEq&n`q[F;s7b!nXc$g#@aPQN3qbFeWl+!e=Zt`EF'M1(Bm%7HR8DbV4gg<TZ"1
+'QY,(Ci/)_["1h766XD'&ADrraHq=DH%OF$jsfp?jsbDCGm`Bc4nAYbhNNN6HSW+QHSVNRk<)"p3=KZH
+@5,_A#.Kh(3M>-L01N,n^DOYar*o;enr(m9jVO\;jVOZqjVO[\jEPdIJ4jW(1Ou\+n/U@A-XG!&R,,`f
+2W1\3O?*%L7g,>@T`!f8eMl#PBrq/m2=C@'l+[6\!TC@KAA[MWaFrFRGAPIZkLE8R^YhJV5O]iFIg_\G
+iO=6VK\u;C'>=b?fe%PhWL9K1abNRi>otj#+Z';<n+i%OCHc:GQ`]p-obCOtip.a^cf<UjY!FG)PRTLL
++"jYG+4_li`+jB7`VE<s"4AVj".>pGko/fq8/"=:R5@Xn#Q]c2U1bel!_n`0,C>VAYsbZH&PH@FJ$f#/
+0td=Kg'>AR25F7&YOo!<mI]$*rRjT2aR!Q]%Fi)aOtJSn?+UM2'/_)[:p`0l2g4bJBatZEkin+Um_FDG
+q;M6)[I9OhMIS=<lFaD[8\^p:_H)J0l0'R9&ZuGO_>dFFCb.uJQlb(Q4<5Au742_H^C[$nr;0XJ4b3Tg
+[;48b%t<.Gdkk?8S<Vi`;Poe&S_S0PK8iOs+@Vd0N*gN+=Plp50G^#i&Kg"CSmrqAknTi"D:*>YSUrma
+g=r&8<Pj&ND`$13'HCOjG_kBC'T)*ekL$!:6XuRpIX?)+^V5+S2cXNJB2ec80tlUL?G#q-m,hbS%$"V@
+p"JQ?igh1:m'G\$*s_?+,4a,JI@3GnX*+U"_Zr.(+*>1jkFAnU9>ZXAU\g3ArglE9&,uBU"0[/n3+!B'
+W[)MC"?*YACpqa=bi'p-F[R+Y&Jh':>DS9I;-PEL7\\5FC1<[_U$dQmm+CP5IQgiD>SQGm;oRG4k5.U/
+0D?:mI/C=6)f;AM:/Ut^\F':\li2"qaZh<t\"cP;!(<3T?G(>oo5D,:=$o+C?]KqCU?geEFjWG&V"PRp
+&9$#`m3'G9"&tosqAZg/@%\C2I=Z6p1:0Yl"+@)T)so1U@.,fWm/arHR8d:MJXMQVC8K5]=Jm_KIXjdB
+83[K$Qla[fJ0ST]0*O=-3O'X`"r\'Q&`>h%Y8I@YUo\`'^eHY_XTc5&7^\ik\4-8s4VZgTU@:6TdipBk
+I9NLLkSE!O'mP*-*Z_.eTF%\P6@O])QsDsH.[1,u#K\!Fdp$""eZ=M1#$Z$(ZO5i+BWp@?TH^qnB%D7/
+h#sWFD^p`NF:Yfb85;E[:q^5Oc;Ss`!7br9/Ss$74_<U(m:SQ#s-3*J/tuGNgXsJtKtqF1bXL_J,7?@i
+'BEj1Ilon*aW+o61dn83AfE7FcXE_u9)ggC=\p"(7u!%6Y7^]'TMPP?@sg_akMgC`EYfUsdCeJO#2(2D
+hQ;Br<4m&15-"(TYi[P!iN8/Ok#@VVm[UO"bZs@LQZDI&pjXBX+4dX`csI+Oe#J=,^c=d&n:"<LdpP?/
+57NYh_**Nu%GYGtPrZ:2pA@k$5I#/DZ?u(p.mKdY_mpagr"[aHSA,e@EE+%EftN*$X1t;gS`@?f>ZH3j
+dG.dX,4]pij-88?@>Es&4"OKq4Pb_H)9:8fq`(0Ie[V>KnkIYS^43jk=+).,aXsOX(4cqM6KaT:7HMS5
+5VqW;.CtD!kNC3=e+gS7q$"nkh'kl'g`'1Rk*3jhL&JE!FZWhT#NI(1R)?@JnB0)&ZC7m>1Wud3mOB<F
+<?1UlEeaPIK4D$_#X<cE%HM2B7nrD$SI/J?MJ9$\[%L0[X#c.:>Z)WgX'F4+E];'H25pPfMT*QlPBH<B
+2jEaIeeu%@g_SZi^/+`r>koI@22thoJ$T#-Ig:8=C[r)9A@gh.nJ+P$rtk!a7oB995m%NRR_tlI7kn;C
+L?s85#0T`VHa<+&O5K'$Eo(6pn-XUq4Y6-9Re-B$rhtBq!Tfi]GEbPb2*A\G,P/,p;d\J77]S.F#9J`B
+DopA\Sjp8ME&Btm*&[^S4k/0Z~>
+endstream
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+/CIDInit/ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo<<
+/Registry (Adobe)
+/Ordering (UCS)
+/Supplement 0
+>> def
+/CMapName/Adobe-Identity-UCS def
+/CMapType 2 def
+1 begincodespacerange
+<00> <FF>
+endcodespacerange
+5 beginbfchar
+<01> <05DF>
+<02> <05D9>
+<03> <05DE>
+<04> <05E0>
+<05> <05D1>
+endbfchar
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/hebrew_mirrored.pdf b/testing/resources/hebrew_mirrored.pdf
new file mode 100644
index 0000000..1134350
--- /dev/null
+++ b/testing/resources/hebrew_mirrored.pdf
@@ -0,0 +1,175 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 200 200]
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+>>
+endobj
+4 0 obj <<
+  /Length 110
+>>
+stream
+BT
+/F1 12 Tf
+150 160 Td
+[<01>2<02>2<03>-4<02>2<04>5<05>]TJ
+ET
+
+BT
+0 100 Td
+-1 0 0 1 50 40 Tm
+[<01>5<05>]TJ
+ET
+endstream
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /BAAAAA+NotoSansHebrew-Regular
+  /FirstChar 0
+  /FontDescriptor 6 0 R
+  /LastChar 5
+  /ToUnicode 8 0 R
+  /Widths [600 294 235 645 397 542]
+>>
+endobj
+6 0 obj <<
+  /Type /FontDescriptor
+  /Ascent 1069
+  /CapHeight 869
+  /Descent -293
+  /Flags 4
+  /FontBBox [-210 -252 716 869]
+  /FontFile2 7 0 R
+  /FontName /BAAAAA+NotoSansHebrew-Regular
+  /ItalicAngle 0
+  /StemV 80
+>>
+endobj
+7 0 obj <<
+  /Filter [/ASCII85Decode /FlateDecode]
+  /Length 4240
+>>
+stream
+GhVOeCMXsCFZt#(%p<i9AU.uO6]Ye/ifKHXbLV!I1<gsgHY$Eg-tbPt@rk=Ap?l+FQ/u;^8JASXVQS>V
+nuG<j"nF$,Rii%*89oFrq6<34,1m)cJ0IF5(22*&"c<?^q>^jZEkc[.-qdl^dk1F&,fKT9F2)eaH/1QX
+<gN4)W0n]g:798;]<j42dYR+b7&$II`er0Do2PVi55OH.DU&3oF7^D>=p%W;pT6OOkC.gL0;aQZ%EQ]$
+CMpO/ouiOe*=oqMFW5p^ge<JWIt[XVg[cqr8aR%^g[TKTpYK&S\F4)WJbd&hhE(,./q'ef/\k-FOKm`h
+f=q)o6_0Sd-Ap"u/`'bn6!o+8Zu;5]Q^2D[J,6jbf.d4Ck't7.im"Z=^^/7D4VXdnJ*hX.ia&@S<Zl_D
+Xq`#Xlf3.[Q6GL-.IS+nj<JEQ1+=HefoeH8Qi0[)c<.\^P$4LTD?DamV./;?-PDR^8[b$k8'^LB2C4&'
+NUg=HC6k8A'.Knb7Wh)elDCX74)L`tc?`Z\Sb;i$>AElUWZdj+!8FWu:gnKPdkmGSM;%q39@mC[7<ZEH
+BP,jX746Y[8VL'e=mlrkg+[&/4Xn#1'(72pCc`cXNN+/Z0?DM&g%BgA@co7e)9J3X;X\\(5@"=N^/d""
+T`@poCcT@G`,Q43B>^KnLk;$<#C1`ZRI:qa#-tMr)R?VE.RR\7=sKe@N)#YuA]JfMaHgYN/!?c&OsZTe
+^cXe0%l-1*krrj[$8W`Wl)*K?>t9dJirfrR7MYH\=XPN5Q2qL_I4eudg4b]KRT\<OAl/T-PZ'?3dZ$p;
+<A/YSC[`O')u&1O:[moi)'2=Vd5KR72=0E6LAR(+/#@80mS5hidn)NRbS3]PMfW2+=&LegaZXffSM\+*
+%@ESe'CB7@?)cYMPG3X<A98q?pK.WHgPW4%FOOV*1/i@`;U-_0)m/crX^pV$",X/"BsMn?;R::1,iO?7
+d*"n-c#gLp.bPO!Jk.*r-gP*5[&0>a[qOG`>on_9`Lhgl?)c@P!H#2#PFffIEAQP^TS_DS:Xem?lK2!M
+h#c+\A=NN1)ZW-WA38,70l'k`MSW-$8rA0><3W]ffk"ZqCrdIAQBE9>j@mZ'<HHGofhsV-;mOl@]Mb1L
+qSIIYnkLM"Z97EVYElh]%[>>bb#jgf**OlY[IHaK5,@C./bHbDalE":hWu.1N,@;S&gl,n3:<uV$;N?L
+*(2O_s!JrF=fbn$ki"!LOY9qefX_V\<NFjuO0ngY8TpJ(,;39KIP'3^N)o:sU^S>oeCuK%7\,dgAiUN1
+E!0r_=$ol73>k&7]_*E"mUSrX[62980+uUk50ME#I*V:L4N8s+bi=/qMstu,ntJ5f[(HSa5@JhaGYD:Q
+]auqs7`nFM/]DAtfVG=ND6[_hnTB=9[;GHTf&F!MBNQP7P.LnAZ2VP"Ploh`?NTa8nEUYFj3^8B4<?t>
+4SBp^*fhF-*fhL/*r?NkO.Sgf4:WJt*n-E0W]KmAKl)1[L<V@'WjXmHBl;cn9--aK@lTjrE@*#k?-6on
+,:;=;+3B=[oI&q3p0&\dDEW8m^^H(gHjR8,7ldq.4hG]/>/mf;Hl1F^:B&.Xiqu!h(RA"GpXOmI2eJ(p
+-Ufu7H4'I5!71?\VoB`KajX::mq>=Rr&Q;g]E7s$-kZ.E)/3T4mfoT]_V3^s3J#ALKI7nF?TacHK]qhj
+L<ncjl]HN[$fPFZ;`(\GZEhSIf%/9H@>qA*jpO7b7bY=!,<-,5gQ6!\>V]AQ)^-GB`&C"Q_Tt5F^2b04
+H^dXF*&#KkrUdWPo<maGSjIV2C<A6%Y#l1eDB-11=5MCe/mQ;H^`5k(:$aRZO01rVm/hX\fYmD8f*Dj@
+M@i=qKg0)_(>4O.G!a5^E2>bO$]QiDc\2C6pdgJSEti>7EGJ-(WqC@QJiDd@NbtVSKCL`LBr7+Q7e(#%
+mfT9"IKcR0OZpuAD#Y..(\(Ya*`U>DQ9W"+'>%SR14Rbu3okUD1UoZtSAfKYf.IftGdnZe:>-X$>r]=V
+G-"HZ$ThCqcd_nA%GR%YOrq)?cB9h+fAIVB(kUJ,MLq_e+>iX=AF@bgIcap`;p5$2F+]o@5&$NLhu:.I
+2<(<.=8A484n4$P`>fQKA"tBRWbq]tDur6ODulS&f!BB<ZPSc21WW3#buP0(+9gU#Z,bH/SFFh24G?`5
+dOt)^QkNbrnhfi)90SEq&n`q[F;s7b!nXc$g#@aPQN3qbFeWl+!e=Zt`EF'M1(Bm%7HR8DbV4gg<TZ"1
+'QY,(Ci/)_["1h766XD'&ADrraHq=DH%OF$jsfp?jsbDCGm`Bc4nAYbhNNN6HSW+QHSVNRk<)"p3=KZH
+@5,_A#.Kh(3M>-L01N,n^DOYar*o;enr(m9jVO\;jVOZqjVO[\jEPdIJ4jW(1Ou\+n/U@A-XG!&R,,`f
+2W1\3O?*%L7g,>@T`!f8eMl#PBrq/m2=C@'l+[6\!TC@KAA[MWaFrFRGAPIZkLE8R^YhJV5O]iFIg_\G
+iO=6VK\u;C'>=b?fe%PhWL9K1abNRi>otj#+Z';<n+i%OCHc:GQ`]p-obCOtip.a^cf<UjY!FG)PRTLL
++"jYG+4_li`+jB7`VE<s"4AVj".>pGko/fq8/"=:R5@Xn#Q]c2U1bel!_n`0,C>VAYsbZH&PH@FJ$f#/
+0td=Kg'>AR25F7&YOo!<mI]$*rRjT2aR!Q]%Fi)aOtJSn?+UM2'/_)[:p`0l2g4bJBatZEkin+Um_FDG
+q;M6)[I9OhMIS=<lFaD[8\^p:_H)J0l0'R9&ZuGO_>dFFCb.uJQlb(Q4<5Au742_H^C[$nr;0XJ4b3Tg
+[;48b%t<.Gdkk?8S<Vi`;Poe&S_S0PK8iOs+@Vd0N*gN+=Plp50G^#i&Kg"CSmrqAknTi"D:*>YSUrma
+g=r&8<Pj&ND`$13'HCOjG_kBC'T)*ekL$!:6XuRpIX?)+^V5+S2cXNJB2ec80tlUL?G#q-m,hbS%$"V@
+p"JQ?igh1:m'G\$*s_?+,4a,JI@3GnX*+U"_Zr.(+*>1jkFAnU9>ZXAU\g3ArglE9&,uBU"0[/n3+!B'
+W[)MC"?*YACpqa=bi'p-F[R+Y&Jh':>DS9I;-PEL7\\5FC1<[_U$dQmm+CP5IQgiD>SQGm;oRG4k5.U/
+0D?:mI/C=6)f;AM:/Ut^\F':\li2"qaZh<t\"cP;!(<3T?G(>oo5D,:=$o+C?]KqCU?geEFjWG&V"PRp
+&9$#`m3'G9"&tosqAZg/@%\C2I=Z6p1:0Yl"+@)T)so1U@.,fWm/arHR8d:MJXMQVC8K5]=Jm_KIXjdB
+83[K$Qla[fJ0ST]0*O=-3O'X`"r\'Q&`>h%Y8I@YUo\`'^eHY_XTc5&7^\ik\4-8s4VZgTU@:6TdipBk
+I9NLLkSE!O'mP*-*Z_.eTF%\P6@O])QsDsH.[1,u#K\!Fdp$""eZ=M1#$Z$(ZO5i+BWp@?TH^qnB%D7/
+h#sWFD^p`NF:Yfb85;E[:q^5Oc;Ss`!7br9/Ss$74_<U(m:SQ#s-3*J/tuGNgXsJtKtqF1bXL_J,7?@i
+'BEj1Ilon*aW+o61dn83AfE7FcXE_u9)ggC=\p"(7u!%6Y7^]'TMPP?@sg_akMgC`EYfUsdCeJO#2(2D
+hQ;Br<4m&15-"(TYi[P!iN8/Ok#@VVm[UO"bZs@LQZDI&pjXBX+4dX`csI+Oe#J=,^c=d&n:"<LdpP?/
+57NYh_**Nu%GYGtPrZ:2pA@k$5I#/DZ?u(p.mKdY_mpagr"[aHSA,e@EE+%EftN*$X1t;gS`@?f>ZH3j
+dG.dX,4]pij-88?@>Es&4"OKq4Pb_H)9:8fq`(0Ie[V>KnkIYS^43jk=+).,aXsOX(4cqM6KaT:7HMS5
+5VqW;.CtD!kNC3=e+gS7q$"nkh'kl'g`'1Rk*3jhL&JE!FZWhT#NI(1R)?@JnB0)&ZC7m>1Wud3mOB<F
+<?1UlEeaPIK4D$_#X<cE%HM2B7nrD$SI/J?MJ9$\[%L0[X#c.:>Z)WgX'F4+E];'H25pPfMT*QlPBH<B
+2jEaIeeu%@g_SZi^/+`r>koI@22thoJ$T#-Ig:8=C[r)9A@gh.nJ+P$rtk!a7oB995m%NRR_tlI7kn;C
+L?s85#0T`VHa<+&O5K'$Eo(6pn-XUq4Y6-9Re-B$rhtBq!Tfi]GEbPb2*A\G,P/,p;d\J77]S.F#9J`B
+DopA\Sjp8ME&Btm*&[^S4k/0Z~>
+endstream
+endobj
+8 0 obj <<
+  /Length 377
+>>
+stream
+/CIDInit/ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo<<
+/Registry (Adobe)
+/Ordering (UCS)
+/Supplement 0
+>> def
+/CMapName/Adobe-Identity-UCS def
+/CMapType 2 def
+1 begincodespacerange
+<00> <FF>
+endcodespacerange
+5 beginbfchar
+<01> <05DF>
+<02> <05D9>
+<03> <05DE>
+<04> <05E0>
+<05> <05D1>
+endbfchar
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+endstream
+endobj
+xref
+0 9
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000309 00000 n 
+0000000471 00000 n 
+0000000678 00000 n 
+0000000905 00000 n 
+0000005238 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 9
+>>
+startxref
+5667
+%%EOF
diff --git a/testing/resources/hello_world.in b/testing/resources/hello_world.in
index d3cf265..754820a 100644
--- a/testing/resources/hello_world.in
+++ b/testing/resources/hello_world.in
@@ -6,9 +6,9 @@
 endobj
 {{object 2 0}} <<
   /Type /Pages
-  /MediaBox [ 0 0 200 200 ]
+  /MediaBox [0 0 200 200]
   /Count 1
-  /Kids [ 3 0 R ]
+  /Kids [3 0 R]
 >>
 endobj
 {{object 3 0}} <<
@@ -36,6 +36,7 @@
 >>
 endobj
 {{object 6 0}} <<
+  % Note this object deliberately does not use {{streamlen}}.
 >>
 stream
 BT
diff --git a/testing/resources/hello_world.pdf b/testing/resources/hello_world.pdf
index dcde8f7..48c714b 100644
--- a/testing/resources/hello_world.pdf
+++ b/testing/resources/hello_world.pdf
@@ -7,9 +7,9 @@
 endobj
 2 0 obj <<
   /Type /Pages
-  /MediaBox [ 0 0 200 200 ]
+  /MediaBox [0 0 200 200]
   /Count 1
-  /Kids [ 3 0 R ]
+  /Kids [3 0 R]
 >>
 endobj
 3 0 obj <<
@@ -37,6 +37,7 @@
 >>
 endobj
 6 0 obj <<
+  % Note this object deliberately does not use /Length 83.
 >>
 stream
 BT
@@ -54,11 +55,14 @@
 0000000000 65535 f 
 0000000015 00000 n 
 0000000068 00000 n 
-0000000161 00000 n 
-0000000303 00000 n 
-0000000381 00000 n 
-0000000457 00000 n 
-trailer<< /Root 1 0 R /Size 7 >>
+0000000157 00000 n 
+0000000299 00000 n 
+0000000377 00000 n 
+0000000453 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
 startxref
-578
+633
 %%EOF
diff --git a/testing/resources/hello_world_2_pages.in b/testing/resources/hello_world_2_pages.in
new file mode 100644
index 0000000..ec33354
--- /dev/null
+++ b/testing/resources/hello_world_2_pages.in
@@ -0,0 +1,67 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 2
+  /Kids [3 0 R 4 0 R]
+  /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+      /F2 6 0 R
+    >>
+  >>
+  /Contents 7 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+      /F2 6 0 R
+    >>
+  >>
+  /Contents 7 0 R
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/hello_world_2_pages.pdf b/testing/resources/hello_world_2_pages.pdf
new file mode 100644
index 0000000..4942e3a
--- /dev/null
+++ b/testing/resources/hello_world_2_pages.pdf
@@ -0,0 +1,81 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [0 0 200 200]
+  /Count 2
+  /Kids [3 0 R 4 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+      /F2 6 0 R
+    >>
+  >>
+  /Contents 7 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+      /F2 6 0 R
+    >>
+  >>
+  /Contents 7 0 R
+>>
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+6 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+7 0 obj <<
+  /Length 83
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+xref
+0 8
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000163 00000 n 
+0000000305 00000 n 
+0000000447 00000 n 
+0000000525 00000 n 
+0000000601 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 8
+>>
+startxref
+735
+%%EOF
diff --git a/testing/resources/hello_world_2_pages_shared_resources_dict.in b/testing/resources/hello_world_2_pages_shared_resources_dict.in
new file mode 100644
index 0000000..9c30780
--- /dev/null
+++ b/testing/resources/hello_world_2_pages_shared_resources_dict.in
@@ -0,0 +1,64 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 2
+  /Kids [3 0 R 4 0 R]
+  /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 5 0 R
+  /Contents 8 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 5 0 R
+  /Contents 8 0 R
+>>
+endobj
+{{object 5 0}} <<
+  /Font <<
+    /F1 6 0 R
+    /F2 7 0 R
+  >>
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/hello_world_2_pages_shared_resources_dict.pdf b/testing/resources/hello_world_2_pages_shared_resources_dict.pdf
new file mode 100644
index 0000000..06b2531
--- /dev/null
+++ b/testing/resources/hello_world_2_pages_shared_resources_dict.pdf
@@ -0,0 +1,79 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 2
+  /Kids [3 0 R 4 0 R]
+  /MediaBox [0 0 200 200]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 5 0 R
+  /Contents 8 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 5 0 R
+  /Contents 8 0 R
+>>
+endobj
+5 0 obj <<
+  /Font <<
+    /F1 6 0 R
+    /F2 7 0 R
+  >>
+>>
+endobj
+6 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+7 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+8 0 obj <<
+  /Length 83
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+xref
+0 9
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000163 00000 n 
+0000000251 00000 n 
+0000000339 00000 n 
+0000000404 00000 n 
+0000000482 00000 n 
+0000000558 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 9
+>>
+startxref
+692
+%%EOF
diff --git a/testing/resources/hello_world_2_pages_split_streams.in b/testing/resources/hello_world_2_pages_split_streams.in
new file mode 100644
index 0000000..ef68f5c
--- /dev/null
+++ b/testing/resources/hello_world_2_pages_split_streams.in
@@ -0,0 +1,75 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 2
+  /Kids [3 0 R 4 0 R]
+  /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+      /F2 6 0 R
+    >>
+  >>
+  /Contents 7 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+      /F2 6 0 R
+    >>
+  >>
+  /Contents 7 0 R
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 7 0}} [8 0 R 9 0 R]
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+endstream
+endobj
+{{object 9 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 100 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/hello_world_2_pages_split_streams.pdf b/testing/resources/hello_world_2_pages_split_streams.pdf
new file mode 100644
index 0000000..3ac829f
--- /dev/null
+++ b/testing/resources/hello_world_2_pages_split_streams.pdf
@@ -0,0 +1,91 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 2
+  /Kids [3 0 R 4 0 R]
+  /MediaBox [0 0 200 200]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+      /F2 6 0 R
+    >>
+  >>
+  /Contents 7 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+      /F2 6 0 R
+    >>
+  >>
+  /Contents 7 0 R
+>>
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+6 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+7 0 obj [8 0 R 9 0 R]
+8 0 obj <<
+  /Length 41
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+endstream
+endobj
+9 0 obj <<
+  /Length 47
+>>
+stream
+BT
+20 100 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000163 00000 n 
+0000000305 00000 n 
+0000000447 00000 n 
+0000000525 00000 n 
+0000000601 00000 n 
+0000000623 00000 n 
+0000000715 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+startxref
+813
+%%EOF
diff --git a/testing/resources/ink_annot.in b/testing/resources/ink_annot.in
new file mode 100644
index 0000000..da90b29
--- /dev/null
+++ b/testing/resources/ink_annot.in
@@ -0,0 +1,48 @@
+{{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
+  /MediaBox [0 0 612 792]
+  /Annots [
+    4 0 R 5 0 R
+  ]
+  /Tabs /R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Ink
+  /NM (Ink-1)
+  /F 4
+  /InkList [[159 296 350 411 472 243.42]]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Annot
+  /Subtype /Ink
+  /NM (Ink-2)
+  /F 4
+  /InkList [[259 396 450 511 572 343 42]]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/ink_annot.pdf b/testing/resources/ink_annot.pdf
new file mode 100644
index 0000000..306f28d
--- /dev/null
+++ b/testing/resources/ink_annot.pdf
@@ -0,0 +1,60 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /Annots [
+    4 0 R 5 0 R
+  ]
+  /Tabs /R
+>>
+endobj
+4 0 obj <<
+  /Type /Annot
+  /Subtype /Ink
+  /NM (Ink-1)
+  /F 4
+  /InkList [[159 296 350 411 472 243.42]]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+5 0 obj <<
+  /Type /Annot
+  /Subtype /Ink
+  /NM (Ink-2)
+  /F 4
+  /InkList [[259 396 450 511 572 343 42]]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000251 00000 n 
+0000000422 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+593
+%%EOF
diff --git a/testing/resources/javascript/annot_properties.in b/testing/resources/javascript/annot_properties.in
index 939365c..e143ac8 100644
--- a/testing/resources/javascript/annot_properties.in
+++ b/testing/resources/javascript/annot_properties.in
@@ -74,6 +74,15 @@
 } catch (e) {
   app.alert("ERROR: " + e);
 }
+app.alert("Test setting empty name under changed name");
+try {
+  var annot = this.getAnnot(0, "nonesuch");
+  annot.name = "";
+  app.alert("name after empty name change: " + annot.name);
+} catch (e) {
+  app.alert("ERROR: " + e);
+}
+
 endstream
 endobj
 {{object 22 0}} <<
diff --git a/testing/resources/javascript/annot_properties_expected.txt b/testing/resources/javascript/annot_properties_expected.txt
index 54548e6..252fd2b 100644
--- a/testing/resources/javascript/annot_properties_expected.txt
+++ b/testing/resources/javascript/annot_properties_expected.txt
@@ -14,3 +14,5 @@
 Alert: SUCCESS: Document.getAnnot: Object no longer exists.
 Alert: Test lookup under changed name
 Alert: nonesuch after name change: object
+Alert: Test setting empty name under changed name
+Alert: name after empty name change: 
diff --git a/testing/resources/javascript/app_methods.in b/testing/resources/javascript/app_methods.in
index 81fa660..1c01e84 100644
--- a/testing/resources/javascript/app_methods.in
+++ b/testing/resources/javascript/app_methods.in
@@ -19,6 +19,7 @@
   /Parent 2 0 R
   /MediaBox [0 0 612 792]
 >>
+endobj
 % OpenAction action
 {{object 10 0}} <<
   /Type /Action
@@ -26,7 +27,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
@@ -34,7 +35,8 @@
 {{include expect.js}}
 
 try {
-  expect("app.alert('message', 1, 2, 'title')", 0);
+  // Test unicode support, no particular reason for these CJK characters.
+  expect("app.alert('message \u4023', 1, 2, 'title \u4024')", 0);
   expect("app.alert({'cMsg': 'message', 'cTitle': 'title'})", 0);
   expect("app.alert({'cMsg': 'message', 'cTitle': 'title', 'nIcon': 3, 'nType': 4})", 0);
   expect("app.alert(undefined)", 0);
diff --git a/testing/resources/javascript/app_methods_expected.txt b/testing/resources/javascript/app_methods_expected.txt
index 015ff28..86fb3a9 100644
--- a/testing/resources/javascript/app_methods_expected.txt
+++ b/testing/resources/javascript/app_methods_expected.txt
@@ -1,5 +1,5 @@
-title[icon=1,type=2]: message
-Alert: PASS: app.alert('message', 1, 2, 'title') = 0
+title 䀤[icon=1,type=2]: message 䀣
+Alert: PASS: app.alert('message 䀣', 1, 2, 'title 䀤') = 0
 title: message
 Alert: PASS: app.alert({'cMsg': 'message', 'cTitle': 'title'}) = 0
 title[icon=3,type=4]: message
diff --git a/testing/resources/javascript/app_properties.in b/testing/resources/javascript/app_properties.in
index 1355e64..290fc24 100644
--- a/testing/resources/javascript/app_properties.in
+++ b/testing/resources/javascript/app_properties.in
@@ -25,6 +25,7 @@
   >>
   /MediaBox [0 0 612 792]
 >>
+endobj
 % Page number 1.
 {{object 4 0}} <<
   /Type /Page
@@ -34,6 +35,7 @@
   >>
   /MediaBox [0 0 612 792]
 >>
+endobj
 % Page number 2.
 {{object 5 0}} <<
   /Type /Page
@@ -43,6 +45,7 @@
   >>
   /MediaBox [0 0 612 792]
 >>
+endobj
 % Page number 3.
 {{object 6 0}} <<
   /Type /Page
@@ -52,6 +55,7 @@
   >>
   /MediaBox [0 0 612 792]
 >>
+endobj
 
 % Info
 {{object 9 0}} <<
@@ -66,7 +70,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/apply.in b/testing/resources/javascript/apply.in
index d944e83..e8ec5e2 100644
--- a/testing/resources/javascript/apply.in
+++ b/testing/resources/javascript/apply.in
@@ -23,6 +23,7 @@
   /Contents [21 0 R]
   /MediaBox [0 0 612 792]
 >>
+endobj
 % OpenAction action
 {{object 10 0}} <<
   /Type /Action
@@ -30,7 +31,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/array_buffer.in b/testing/resources/javascript/array_buffer.in
index 06371ba..3790c6c 100644
--- a/testing/resources/javascript/array_buffer.in
+++ b/testing/resources/javascript/array_buffer.in
@@ -23,6 +23,7 @@
   /Contents [21 0 R]
   /MediaBox [0 0 612 792]
 >>
+endobj
 % OpenAction action
 {{object 10 0}} <<
   /Type /Action
@@ -30,7 +31,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/bug_1098213.in b/testing/resources/javascript/bug_1098213.in
new file mode 100644
index 0000000..0c99311
--- /dev/null
+++ b/testing/resources/javascript/bug_1098213.in
@@ -0,0 +1,65 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 8 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 2
+  /Kids [
+    3 0 R
+    4 0 R
+  ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /CropBox [179 173 200 75]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /CropBox [127 214 186 29]
+  /Annots [
+    5 0 R
+    6 0 R
+    7 0 R
+  ]
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Annot
+>
+endobj
+{{object 6 0}} <<
+  /Type /Annot
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Annot
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS (
+    Object.defineProperty(Array.prototype, 1, {
+      set: (v) => {
+        Object.defineProperty(Array.prototype, 1, {
+          get: () => {}
+        });
+      }
+    });
+    try { this.getAnnots(); } catch (e) { app.alert('Caught: ' + e); }
+    app.alert('Done');
+  )
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/bug_1098213_expected.txt b/testing/resources/javascript/bug_1098213_expected.txt
new file mode 100644
index 0000000..9de1818
--- /dev/null
+++ b/testing/resources/javascript/bug_1098213_expected.txt
@@ -0,0 +1 @@
+Alert: Done
diff --git a/testing/resources/javascript/bug_1142688.in b/testing/resources/javascript/bug_1142688.in
new file mode 100644
index 0000000..6d3825d
--- /dev/null
+++ b/testing/resources/javascript/bug_1142688.in
@@ -0,0 +1,78 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 5 0 R
+  /AcroForm <<
+    /Fields [
+      3 0 R
+      2 0 R
+    ]
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /T (tf1)
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /T (tf0)
+  /AA <<
+    /F 10 0 R
+  >>
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Pages
+  /Count 2
+  /Kids [
+    8 0 R
+    9 0 R
+  ]
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Page
+  /Parent 5 0 R
+  /Annots [3 0 R]
+>>
+endobj
+{{object 9 0}} <<
+  /Type /Page
+  /Parent 5 0 R
+  /Annots [2 0 R]
+>>
+endobj
+{{object 10 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 13 0 R
+>>
+{{object 13 0}} <<
+  {{streamlen}}
+>>
+stream
+function f3() {
+  // Setup dubious values in event recorder.
+  try { AFSpecial_Format(2); } catch(e) {}
+  try { AFNumber_Format(-302907477,0,1,-6,"",true); } catch(e) {}
+
+  // Exhaust call stack, then do work upon exiting each frame. The
+  // objective is to get any call() made under the covers to throw
+  // with a stack size exception.
+  try { f3(); } catch(e) {}
+  try { AFDate_Keystroke("yymm-dd"); } catch(e) {}
+}
+f3();
+app.alert('Done.');
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/bug_1142688_expected.txt b/testing/resources/javascript/bug_1142688_expected.txt
new file mode 100644
index 0000000..7cb32ca
--- /dev/null
+++ b/testing/resources/javascript/bug_1142688_expected.txt
@@ -0,0 +1 @@
+Alert: Done.
diff --git a/testing/resources/javascript/bug_1314658.in b/testing/resources/javascript/bug_1314658.in
new file mode 100644
index 0000000..f05b3ed
--- /dev/null
+++ b/testing/resources/javascript/bug_1314658.in
@@ -0,0 +1,29 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /]
+  /Subtype /Widget
+  /T (0)
+  /FT /Ch
+  /AA <<
+    /F 5 0 R
+  >>
+  /Annots 4 0 R
+4 0 obj [3 0 R /Ff 393216]
+  /AP<</N<<>>
+stream
+{{object 5 0}} <<
+  /JS (app.alert('done'))
+>>
+endobj
+{{trailer}}
+%%EOF
diff --git a/testing/resources/javascript/bug_1314658_expected.txt b/testing/resources/javascript/bug_1314658_expected.txt
new file mode 100644
index 0000000..daa1eca
--- /dev/null
+++ b/testing/resources/javascript/bug_1314658_expected.txt
@@ -0,0 +1 @@
+Alert: done
diff --git a/testing/resources/javascript/bug_1335681.in b/testing/resources/javascript/bug_1335681.in
new file mode 100644
index 0000000..254e596
--- /dev/null
+++ b/testing/resources/javascript/bug_1335681.in
@@ -0,0 +1,38 @@
+{{header}}
+{{object 1 0}} <<
+  /Pages 1 0 R
+  /OpenAction 2 0 R
+  /Names <<
+      /Dests 3 0 R
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS (
+    app.alert\("Starting"\);
+    this.gotoNamedDest\(""\);
+  )
+>>
+endobj
+{{object 3 0}} <<
+  /Kids 4 0 R
+>>
+endobj
+{{object 4 0}} [
+  << >>
+  << >>
+  <<
+    /Kids [
+      <<
+        /Limits 4 0 R
+      >>
+    ]
+  >>
+]
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/bug_1335681_expected.txt b/testing/resources/javascript/bug_1335681_expected.txt
new file mode 100644
index 0000000..80a6951
--- /dev/null
+++ b/testing/resources/javascript/bug_1335681_expected.txt
@@ -0,0 +1 @@
+Alert: Starting
diff --git a/testing/resources/javascript/bug_1358075.in b/testing/resources/javascript/bug_1358075.in
new file mode 100644
index 0000000..b503bf2
--- /dev/null
+++ b/testing/resources/javascript/bug_1358075.in
@@ -0,0 +1,39 @@
+{{header}}
+{{object 1 0}} <<
+  /Pages 1 0 R
+  /OpenAction 2 0 R
+  /Names <<
+    /Dests 3 0 R
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS (
+        this.gotoNamedDest\("2"\);
+        app.alert\("completed"\);
+  )
+>>
+endobj
+{{object 3 0}} <<
+  /Kids 4 0 R
+>>
+endobj
+{{object 4 0}} [
+  (1)
+  (3)
+  <<
+    /Kids [
+      <<
+        /Limits 4 0 R
+        /Names [(2) []]
+      >>
+    ]
+  >>
+]
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/bug_1358075_expected.txt b/testing/resources/javascript/bug_1358075_expected.txt
new file mode 100644
index 0000000..13d460b
--- /dev/null
+++ b/testing/resources/javascript/bug_1358075_expected.txt
@@ -0,0 +1 @@
+Alert: completed
diff --git a/testing/resources/javascript/bug_1445426.evt b/testing/resources/javascript/bug_1445426.evt
new file mode 100644
index 0000000..265e85b
--- /dev/null
+++ b/testing/resources/javascript/bug_1445426.evt
@@ -0,0 +1,3 @@
+mousedown,left,202,697
+mouseup,left,202,697
+keycode,40
diff --git a/testing/resources/javascript/bug_1445426.in b/testing/resources/javascript/bug_1445426.in
new file mode 100644
index 0000000..1483da7
--- /dev/null
+++ b/testing/resources/javascript/bug_1445426.in
@@ -0,0 +1,114 @@
+{{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 2
+  /Kids [
+    32 0 R
+    34 0 R
+  ]
+>>
+endobj
+% Forms
+{{object 4 0}} <<
+  /Fields [
+    10 0 R
+    11 0 R
+  ]
+>>
+endobj
+% Fields
+{{object 10 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /T (Field_TextEdit)
+  /Rect [0 0 612 792]
+>>
+{{object 11 0}} <<
+  /T (Field_ComboBox)
+  /Parent 4 0 R
+  /Kids [12 0 R]
+  /Opt [(a) (b) (c) (d)]
+  /V [(a)]
+>>
+endobj
+{{object 12 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 131072
+  /Parent 11 0 R
+  /Kids [13 0 R]
+>>
+endobj
+{{object 13 0}} <<
+  /Parent 12 0 R
+  /Type /Annot
+  /Subtype /Widget
+  /Rect [0 0 612 792]
+  /AA << /K 20 0 R >>
+>>
+endobj
+% Pages
+{{object 32 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /Annots [13 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
+% JS program to exexute
+{{object 41 0}} <<
+  {{streamlen}}
+>>
+stream
+var field_text = this.getField("Field_TextEdit");
+var field_combobox = this.getField("Field_ComboBox");
+field_combobox.setFocus();
+this.__defineGetter__("filesize", function new_getter(){
+                                    field_text.setFocus();
+                                    field_combobox.borderStyle="dashed";
+                                    field_combobox.setFocus();
+                                  });
+endstream
+endobj
+% OpenAction action
+{{object 20 0}} <<
+  /S /JavaScript
+  /JS 21 0 R
+>>
+endobj
+% JS program to exexute
+{{object 21 0}} <<
+  {{streamlen}}
+>>
+stream
+var t = this.filesize;
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
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/javascript/bug_361.in b/testing/resources/javascript/bug_361.in
index 4af86a6..386142a 100644
--- a/testing/resources/javascript/bug_361.in
+++ b/testing/resources/javascript/bug_361.in
@@ -24,10 +24,12 @@
   /Contents [21 0 R]
   /MediaBox [0 0 612 792]
 >>
+endobj
 % Forms
 {{object 4 0}} <<
   /Fields [5 0 R]
 >>
+endobj
 % Field
 {{object 5 0}} <<
  /FT /Tx
@@ -36,6 +38,7 @@
  /Subtype /Widget
  /Rect [100 200 150 250]
 >>
+endobj
 % OpenAction action
 {{object 10 0}} <<
   /Type /Action
@@ -43,7 +46,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/bug_492_1.in b/testing/resources/javascript/bug_492_1.in
index 9d48ad5..4b8026f 100644
--- a/testing/resources/javascript/bug_492_1.in
+++ b/testing/resources/javascript/bug_492_1.in
@@ -50,7 +50,7 @@
   /JS 21 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 21 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/bug_494057.in b/testing/resources/javascript/bug_494057.in
index bbfa13e..3615d03 100644
--- a/testing/resources/javascript/bug_494057.in
+++ b/testing/resources/javascript/bug_494057.in
@@ -87,7 +87,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/bug_679642.in b/testing/resources/javascript/bug_679642.in
index 644a259..b63cb70 100644
--- a/testing/resources/javascript/bug_679642.in
+++ b/testing/resources/javascript/bug_679642.in
@@ -86,7 +86,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/bug_679643.in b/testing/resources/javascript/bug_679643.in
index c60fc0f..5ebe312 100644
--- a/testing/resources/javascript/bug_679643.in
+++ b/testing/resources/javascript/bug_679643.in
@@ -86,7 +86,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/bug_695826.in b/testing/resources/javascript/bug_695826.in
index e17eaf1..9d32be7 100644
--- a/testing/resources/javascript/bug_695826.in
+++ b/testing/resources/javascript/bug_695826.in
@@ -23,6 +23,7 @@
   /Contents [21 0 R]
   /MediaBox [0 0 612 792]
 >>
+endobj
 % OpenAction action
 {{object 10 0}} <<
   /Type /Action
@@ -30,7 +31,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/bug_735912.in b/testing/resources/javascript/bug_735912.in
index 06bd9a3..f3db5a2 100644
--- a/testing/resources/javascript/bug_735912.in
+++ b/testing/resources/javascript/bug_735912.in
@@ -53,7 +53,7 @@
   /JS 31 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 31 0}} <<
   {{streamlen}}
 >>
@@ -69,7 +69,7 @@
   /JS 35 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 35 0}} <<
   {{streamlen}}
 >>
@@ -85,7 +85,7 @@
   /JS 37 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 37 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/bug_740166.in b/testing/resources/javascript/bug_740166.in
index 425374d..6f0c829 100644
--- a/testing/resources/javascript/bug_740166.in
+++ b/testing/resources/javascript/bug_740166.in
@@ -24,10 +24,12 @@
   /Contents [21 0 R]
   /MediaBox [0 0 612 792]
 >>
+endobj
 % Forms
 {{object 4 0}} <<
   /Fields [5 0 R]
 >>
+endobj
 % Field
 {{object 5 0}} <<
  /FT /Tx
@@ -36,6 +38,7 @@
  /Subtype /Widget
  /Rect [100 200 150 250]
 >>
+endobj
 % OpenAction action
 {{object 10 0}} <<
   /Type /Action
@@ -43,7 +46,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/bug_959274_1.in b/testing/resources/javascript/bug_959274_1.in
index 74909c0..9efb9dc 100644
--- a/testing/resources/javascript/bug_959274_1.in
+++ b/testing/resources/javascript/bug_959274_1.in
@@ -23,6 +23,7 @@
   /Contents [21 0 R]
   /MediaBox [0 0 612 792]
 >>
+endobj
 % OpenAction action
 {{object 10 0}} <<
   /Type /Action
diff --git a/testing/resources/javascript/color_methods.in b/testing/resources/javascript/color_methods.in
index bf03bb4..44afced 100644
--- a/testing/resources/javascript/color_methods.in
+++ b/testing/resources/javascript/color_methods.in
@@ -27,7 +27,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/color_properties.in b/testing/resources/javascript/color_properties.in
index 7d09656..0fea81e 100644
--- a/testing/resources/javascript/color_properties.in
+++ b/testing/resources/javascript/color_properties.in
@@ -20,6 +20,7 @@
   /Contents [21 0 R]
   /MediaBox [0 0 612 792]
 >>
+endobj
 % OpenAction action
 {{object 10 0}} <<
   /Type /Action
@@ -27,7 +28,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/console_methods.in b/testing/resources/javascript/console_methods.in
index 0fbba3f..0dc74e3 100644
--- a/testing/resources/javascript/console_methods.in
+++ b/testing/resources/javascript/console_methods.in
@@ -27,7 +27,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/constructor.in b/testing/resources/javascript/constructor.in
index 1211f89..90c41c0 100644
--- a/testing/resources/javascript/constructor.in
+++ b/testing/resources/javascript/constructor.in
@@ -19,6 +19,7 @@
   /Parent 2 0 R
   /MediaBox [0 0 612 792]
 >>
+endobj
 % OpenAction action
 {{object 10 0}} <<
   /Type /Action
@@ -26,34 +27,18 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
 stream
-function testIllegalConstructor(name, allowed) {
-  const constructorString = name + ".constructor";
-  let constructor;
-  try {
-    constructor = eval(constructorString);
-  } catch (e) {
-    app.alert("FAIL: No such " + constructorString);
-    return;
-  }
-  try {
-    constructor();
-    app.alert("FAIL: " + constructorString + "(): returned");
-  } catch (e) {
-    app.alert("PASS: " + constructorString + "(): " + e);
-  }
-  try {
-    new constructor;
-    app.alert("FAIL: new " + constructorString + ": returned");
-  } catch (e) {
-    app.alert("PASS: new " + constructorString + ": " + e);
-  }
-}
+{{include constructor.js}}
+
+// Global objects
 testIllegalConstructor("this");
+testIllegalConstructor("globalThis");
+
+// Static objects
 testIllegalConstructor("app");
 testIllegalConstructor("event");
 testIllegalConstructor("font");
@@ -69,6 +54,11 @@
 testIllegalConstructor("zoomtype");
 testIllegalConstructor("scaleHow");
 testIllegalConstructor("scaleWhen");
+
+// Dynamic objects
+timer1 = app.setTimeOut("var marked = true", 1000);
+testLegalConstructor("timer1");
+
 endstream
 endobj
 {{xref}}
diff --git a/testing/resources/javascript/constructor.js b/testing/resources/javascript/constructor.js
new file mode 100644
index 0000000..f8c98aa
--- /dev/null
+++ b/testing/resources/javascript/constructor.js
@@ -0,0 +1,49 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+function testLegalConstructor(name, allowed) {
+  const constructorString = name + ".constructor";
+  var constructor;
+  try {
+    constructor = eval(constructorString);
+  } catch (e) {
+    app.alert("FAIL: No such " + constructorString);
+    return;
+  }
+  try {
+    constructor();
+    app.alert("FAIL: " + constructorString + "(): returned");
+  } catch (e) {
+    app.alert("PASS: " + constructorString + "(): " + e);
+  }
+  try {
+    var thing = new constructor;
+    app.alert("PASS: new " + constructorString + ": " + thing);
+  } catch (e) {
+    app.alert("FAIL: new " + constructorString + ": " + e);
+  }
+}
+
+function testIllegalConstructor(name, allowed) {
+  const constructorString = name + ".constructor";
+  var constructor;
+  try {
+    constructor = eval(constructorString);
+  } catch (e) {
+    app.alert("FAIL: No such " + constructorString);
+    return;
+  }
+  try {
+    constructor();
+    app.alert("FAIL: " + constructorString + "(): returned");
+  } catch (e) {
+    app.alert("PASS: " + constructorString + "(): " + e);
+  }
+  try {
+    new constructor;
+    app.alert("FAIL: new " + constructorString + ": returned");
+  } catch (e) {
+    app.alert("PASS: new " + constructorString + ": " + e);
+  }
+}
diff --git a/testing/resources/javascript/constructor_expected.txt b/testing/resources/javascript/constructor_expected.txt
index af03337..8c58aad 100644
--- a/testing/resources/javascript/constructor_expected.txt
+++ b/testing/resources/javascript/constructor_expected.txt
@@ -1,5 +1,7 @@
 Alert: PASS: this.constructor(): illegal constructor
 Alert: PASS: new this.constructor: not a dynamic object
+Alert: PASS: globalThis.constructor(): illegal constructor
+Alert: PASS: new globalThis.constructor: not a dynamic object
 Alert: PASS: app.constructor(): illegal constructor
 Alert: PASS: new app.constructor: not a dynamic object
 Alert: PASS: event.constructor(): illegal constructor
@@ -30,3 +32,5 @@
 Alert: PASS: new scaleHow.constructor: not a dynamic object
 Alert: PASS: scaleWhen.constructor(): illegal constructor
 Alert: PASS: new scaleWhen.constructor: not a dynamic object
+Alert: PASS: timer1.constructor(): illegal constructor
+Alert: PASS: new timer1.constructor: [object Object]
diff --git a/testing/resources/javascript/consts.in b/testing/resources/javascript/consts.in
index dbfd579..b4180ff 100644
--- a/testing/resources/javascript/consts.in
+++ b/testing/resources/javascript/consts.in
@@ -23,6 +23,7 @@
   /Contents [21 0 R]
   /MediaBox [0 0 612 792]
 >>
+endobj
 % OpenAction action
 {{object 10 0}} <<
   /Type /Action
@@ -30,7 +31,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/document_methods.in b/testing/resources/javascript/document_methods.in
index 9571e55..5db9775 100644
--- a/testing/resources/javascript/document_methods.in
+++ b/testing/resources/javascript/document_methods.in
@@ -26,6 +26,7 @@
   /MediaBox [0 0 612 792]
   /Contents 8 0 R
 >>
+endobj
 % Page number 1.
 {{object 4 0}} <<
   /Type /Page
@@ -35,6 +36,7 @@
   >>
   /MediaBox [0 0 612 792]
 >>
+endobj
 % Page number 2.
 {{object 5 0}} <<
   /Type /Page
@@ -44,6 +46,7 @@
   >>
   /MediaBox [0 0 612 792]
 >>
+endobj
 % Page number 3.
 {{object 6 0}} <<
   /Type /Page
@@ -53,6 +56,7 @@
   >>
   /MediaBox [0 0 612 792]
 >>
+endobj
 % Contents of the page.
 {{object 8 0}} <<
   {{streamlen}}
@@ -78,7 +82,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/document_properties.in b/testing/resources/javascript/document_properties.in
index fa7d92a..44a108b 100644
--- a/testing/resources/javascript/document_properties.in
+++ b/testing/resources/javascript/document_properties.in
@@ -25,6 +25,7 @@
   >>
   /MediaBox [0 0 612 792]
 >>
+endobj
 % Page number 1.
 {{object 4 0}} <<
   /Type /Page
@@ -34,6 +35,7 @@
   >>
   /MediaBox [0 0 612 792]
 >>
+endobj
 % Page number 2.
 {{object 5 0}} <<
   /Type /Page
@@ -43,6 +45,7 @@
   >>
   /MediaBox [0 0 612 792]
 >>
+endobj
 % Page number 3.
 {{object 6 0}} <<
   /Type /Page
@@ -52,6 +55,7 @@
   >>
   /MediaBox [0 0 612 792]
 >>
+endobj
 
 % Info
 {{object 9 0}} <<
@@ -66,7 +70,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/document_properties_expected.txt b/testing/resources/javascript/document_properties_expected.txt
index 5bab82f..ba89da0 100644
--- a/testing/resources/javascript/document_properties_expected.txt
+++ b/testing/resources/javascript/document_properties_expected.txt
@@ -237,13 +237,13 @@
 Alert: this.zoomType = 3; yields 3
 Alert: *** Getting properties ***
 Alert: this.ADBE is undefined undefined
-Alert: this.author is string 3
+Alert: this.author is string Joe Random Author
 Alert: this.baseURL is string 3
 Alert: this.bookmarkRoot is undefined undefined
 Alert: this.calculate is boolean true
 Alert: this.Collab is undefined undefined
-Alert: this.creationDate is string 3
-Alert: this.creator is string 3
+Alert: this.creationDate is string 
+Alert: this.creator is string Joe Random Creator
 Alert: this.delay is boolean true
 Alert: this.dirty is boolean true
 Alert: this.documentFileName is string 
@@ -251,10 +251,10 @@
 Alert: this.filesize is number 0
 Alert: this.icons is undefined undefined
 Alert: this.info is object [object Object]
-Alert: this.keywords is string 3
+Alert: this.keywords is string 
 Alert: this.layout is undefined undefined
 Alert: this.media is undefined undefined
-Alert: this.modDate is string 3
+Alert: this.modDate is string 
 Alert: this.mouseX is undefined undefined
 Alert: this.mouseY is undefined undefined
 Alert: this.numFields is number 0
@@ -262,9 +262,9 @@
 Alert: this.pageNum is undefined undefined
 Alert: this.pageWindowRect is undefined undefined
 Alert: this.path is string /myfile.pdf
-Alert: this.producer is string 3
-Alert: this.subject is string 3
-Alert: this.title is string 3
+Alert: this.producer is string 
+Alert: this.subject is string 
+Alert: this.title is string 
 Alert: this.URL is string myfile.pdf
 Alert: this.zoom is undefined undefined
 Alert: this.zoomType is undefined undefined
diff --git a/testing/resources/javascript/expect.js b/testing/resources/javascript/expect.js
index 832f1d6..a7a5643 100644
--- a/testing/resources/javascript/expect.js
+++ b/testing/resources/javascript/expect.js
@@ -1,3 +1,7 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
 function expect(expression, expected) {
   try {
     var actual = eval(expression);
diff --git a/testing/resources/javascript/field.fragment b/testing/resources/javascript/field.fragment
index 335e1ae..ff8f7f4 100644
--- a/testing/resources/javascript/field.fragment
+++ b/testing/resources/javascript/field.fragment
@@ -46,6 +46,7 @@
     10 0 R
     11 0 R
     12 0 R
+    13 0 R
   ]
 >>
 endobj
@@ -68,6 +69,9 @@
   /Parent 5 0 R
   /T (MyPushButton)
   /Rect [220 221 240 241]
+  /MK <<
+    /TP 4
+  >>
 >>
 endobj
 {{object 8 0}} <<
@@ -132,6 +136,20 @@
   /V ("/path/to/file.pdf")
 >>
 endobj
+% Questionable TP value.
+{{object 13 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /Ff 65536
+  /Parent 5 0 R
+  /T (MyBadPushButton)
+  /Rect [400 421 440 441]
+  /MK <<
+    /TP 7
+  >>
+>>
+endobj
 % OpenAction action
 {{object 15 0}} <<
   /Type /Action
diff --git a/testing/resources/javascript/field_methods.in b/testing/resources/javascript/field_methods.in
index 81b9eed..4684552 100644
--- a/testing/resources/javascript/field_methods.in
+++ b/testing/resources/javascript/field_methods.in
@@ -1,7 +1,7 @@
 {{header}}
 {{include field.fragment}}
 
-% JS program to exexute
+% JS program to execute
 {{object 16 0}} <<
   {{streamlen}}
 >>
@@ -51,7 +51,18 @@
 testGetArray();
 
 expect("this.getField('MyField.MyPushButton').buttonGetCaption()", "");
+expect("this.getField('MyField.MyPushButton').buttonGetCaption(0)", "");
+expect("this.getField('MyField.MyPushButton').buttonGetCaption(1)", "");
+expect("this.getField('MyField.MyPushButton').buttonGetCaption(2)", "");
+expectError("this.getField('MyField.MyPushButton').buttonGetCaption(3)")
+expectError("this.getField('MyField.MyMultiSelect').buttonGetCaption()")
+
 expect("this.getField('MyField.MyPushButton').buttonGetIcon()", "[object Object]");
+expect("this.getField('MyField.MyPushButton').buttonGetIcon(0)", "[object Object]");
+expect("this.getField('MyField.MyPushButton').buttonGetIcon(1)", "[object Object]");
+expectError("this.getField('MyField.MyPushButton').buttonGetIcon(3)");
+expectError("this.getField('MyField.MyMultiSelect').buttonGetIcon()");
+
 expect("this.getField('MyField.MyPushButton').buttonImportIcon()", undefined);
 
 expect("this.getField('MyField.MyFile').browseForFileToSubmit()", undefined);
diff --git a/testing/resources/javascript/field_methods_expected.txt b/testing/resources/javascript/field_methods_expected.txt
index 8d7ec84..99dbf09 100644
--- a/testing/resources/javascript/field_methods_expected.txt
+++ b/testing/resources/javascript/field_methods_expected.txt
@@ -8,7 +8,8 @@
 Alert: dotdot1 is 
 Alert: dotdot2 is MyField.MyPushButton
 Alert: dotdot3 is MyField
-Alert: found 7 sub-fields:
+Alert: found 8 sub-fields:
+Alert: MyField.MyBadPushButton
 Alert: MyField.MyCheckBox
 Alert: MyField.MyFile
 Alert: MyField.MyMultiSelect
@@ -17,7 +18,16 @@
 Alert: MyField.MySingleSelect
 Alert: MyField.MyText
 Alert: PASS: this.getField('MyField.MyPushButton').buttonGetCaption() = 
+Alert: PASS: this.getField('MyField.MyPushButton').buttonGetCaption(0) = 
+Alert: PASS: this.getField('MyField.MyPushButton').buttonGetCaption(1) = 
+Alert: PASS: this.getField('MyField.MyPushButton').buttonGetCaption(2) = 
+Alert: PASS: this.getField('MyField.MyPushButton').buttonGetCaption(3) threw Field.buttonGetCaption: Incorrect parameter value.
+Alert: PASS: this.getField('MyField.MyMultiSelect').buttonGetCaption() threw Field.buttonGetCaption: Object is of the wrong type.
 Alert: PASS: this.getField('MyField.MyPushButton').buttonGetIcon() = [object Object]
+Alert: PASS: this.getField('MyField.MyPushButton').buttonGetIcon(0) = [object Object]
+Alert: PASS: this.getField('MyField.MyPushButton').buttonGetIcon(1) = [object Object]
+Alert: PASS: this.getField('MyField.MyPushButton').buttonGetIcon(3) threw Field.buttonGetIcon: Incorrect parameter value.
+Alert: PASS: this.getField('MyField.MyMultiSelect').buttonGetIcon() threw Field.buttonGetIcon: Object is of the wrong type.
 Alert: PASS: this.getField('MyField.MyPushButton').buttonImportIcon() = undefined
 Alert: PASS: this.getField('MyField.MyFile').browseForFileToSubmit() = undefined
 Alert: PASS: this.getField('MyField.MyMultiSelect').getItemAt(0) = foo
diff --git a/testing/resources/javascript/field_properties.in b/testing/resources/javascript/field_properties.in
index 2126384..3887787 100644
--- a/testing/resources/javascript/field_properties.in
+++ b/testing/resources/javascript/field_properties.in
@@ -1,6 +1,6 @@
 {{header}}
 {{include field.fragment}}
-% JS program to exexute
+% JS program to execute
 {{object 16 0}} <<
   {{streamlen}}
 >>
@@ -11,6 +11,7 @@
     var field = this.getField("MyField");
     var text = this.getField("MyField.MyText");
     var button = this.getField("MyField.MyPushButton");
+    var badbutton = this.getField("MyField.MyBadPushButton");
     var radio = this.getField("MyField.MyRadio");
     var list = this.getField("MyField.MyMultiSelect");
     var check = this.getField("MyField.MyCheckBox");
@@ -23,6 +24,7 @@
     testFieldPropertiesCase(field);
     testTextPropertiesCase(text);
     testPushButtonPropertiesCase(button);
+    testBadPushButtonPropertiesCase(badbutton);
     testRadioButtonPropertiesCase(radio);
     testCheckBoxPropertiesCase(check);
     testListBoxPropertiesCase(list);
@@ -68,7 +70,7 @@
     testROProperty(field, "page", 0);
     testRIProperty(field, "password", false, 42);
     testRWProperty(field, "print", true, false);
-    testRIProperty(field, "readonly", false, true);
+    testRWProperty(field, "readonly", false, true);
     testROProperty(field, "rect", [200,221,220,201]);
     // testROProperty(field, "required", "clams");
     testRIProperty(field, "richText", false, true);
@@ -94,7 +96,7 @@
     testRIProperty(field, "buttonAlignX", 0, 50);
     testRIProperty(field, "buttonAlignY", 0, 50);
     testRIProperty(field, "buttonFitBounds", false);
-    testRIProperty(field, "buttonPosition", 0);
+    testRIProperty(field, "buttonPosition", 4);
     testRIProperty(field, "buttonScaleHow", 0);
     testRIProperty(field, "buttonScaleWhen", 0);
     testRIProperty(field, "highlight", "invert");
@@ -104,11 +106,19 @@
   }
 }
 
+function testBadPushButtonPropertiesCase(field) {
+  try {
+    testRIProperty(field, "buttonPosition", 7); // not checked.
+  } catch (e) {
+    app.alert("Unexpected error: " + e);
+  }
+}
+
 function testRadioButtonPropertiesCase(field) {
   try {
-    testROProperty(field, "exportValues", "N");
+    testROProperty(field, "exportValues", "Yes");
     testRIProperty(field, "radiosInUnison", false);
-    testRIProperty(field, "style", "check");
+    testRIProperty(field, "style", "circle");
     testROProperty(field, "type", "radiobutton");
     testRIProperty(field, "value", "Off");
     testROProperty(field, "valueAsString", "Off");
@@ -119,7 +129,7 @@
 
 function testCheckBoxPropertiesCase(field) {
   try {
-    testROProperty(field, "exportValues", "N");
+    testROProperty(field, "exportValues", "Yes");
     testRIProperty(field, "style", "check");
     testROProperty(field, "type", "checkbox");
     testRIProperty(field, "value", "Off");
diff --git a/testing/resources/javascript/field_properties_expected.txt b/testing/resources/javascript/field_properties_expected.txt
index 3b2e6f6..2c57daf 100644
--- a/testing/resources/javascript/field_properties_expected.txt
+++ b/testing/resources/javascript/field_properties_expected.txt
@@ -51,7 +51,7 @@
 Alert: PASS: print = true
 Alert: PASS: print = false
 Alert: PASS: readonly = false
-Alert: PASS: readonly = false
+Alert: PASS: readonly = true
 Alert: PASS: rect = 200,221,220,201
 Alert: PASS: rect threw Field.rect: Incorrect parameter value.
 Alert: PASS: richText = false
@@ -84,8 +84,8 @@
 Alert: PASS: buttonAlignY = 0
 Alert: PASS: buttonFitBounds = false
 Alert: PASS: buttonFitBounds = false
-Alert: PASS: buttonPosition = 0
-Alert: PASS: buttonPosition = 0
+Alert: PASS: buttonPosition = 4
+Alert: PASS: buttonPosition = 4
 Alert: PASS: buttonScaleHow = false
 Alert: PASS: buttonScaleHow = false
 Alert: PASS: buttonScaleWhen = 0
@@ -94,19 +94,21 @@
 Alert: PASS: highlight = invert
 Alert: PASS: type = button
 Alert: PASS: type threw Field.type: Operation not supported.
-Alert: PASS: exportValues = N
+Alert: PASS: buttonPosition = 7
+Alert: PASS: buttonPosition = 7
+Alert: PASS: exportValues = Yes
 Alert: PASS: exportValues threw Field.exportValues: Object no longer exists.
 Alert: PASS: radiosInUnison = false
 Alert: PASS: radiosInUnison = false
-Alert: PASS: style = check
-Alert: PASS: style = check
+Alert: PASS: style = circle
+Alert: PASS: style = circle
 Alert: PASS: type = radiobutton
 Alert: PASS: type threw Field.type: Operation not supported.
 Alert: PASS: value = Off
 Alert: PASS: value = Off
 Alert: PASS: valueAsString = Off
 Alert: PASS: valueAsString threw Field.valueAsString: Operation not supported.
-Alert: PASS: exportValues = N
+Alert: PASS: exportValues = Yes
 Alert: PASS: exportValues threw Field.exportValues: Object no longer exists.
 Alert: PASS: style = check
 Alert: PASS: style = check
diff --git a/testing/resources/javascript/foreground_task.in b/testing/resources/javascript/foreground_task.in
new file mode 100644
index 0000000..dd5444a
--- /dev/null
+++ b/testing/resources/javascript/foreground_task.in
@@ -0,0 +1,43 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [
+    3 0 R
+  ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 10 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 11 0 R
+>>
+endobj
+{{object 11 0}} <<
+  {{streamlen}}
+>>
+stream
+try {
+  gc({type: 'major', execution: 'async'}).then(() => { app.alert('Resolved') });
+  app.alert('Posted');
+} catch (e) {
+  app.alert('Error: ' + e);
+}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/foreground_task_expected.txt b/testing/resources/javascript/foreground_task_expected.txt
new file mode 100644
index 0000000..d40e100
--- /dev/null
+++ b/testing/resources/javascript/foreground_task_expected.txt
@@ -0,0 +1,2 @@
+Alert: Posted
+Alert: Resolved
diff --git a/testing/resources/javascript/globals.in b/testing/resources/javascript/globals.in
index de67392..bbca813 100644
--- a/testing/resources/javascript/globals.in
+++ b/testing/resources/javascript/globals.in
@@ -23,6 +23,7 @@
   /Contents [21 0 R]
   /MediaBox [0 0 612 792]
 >>
+endobj
 % OpenAction action
 {{object 10 0}} <<
   /Type /Action
@@ -30,7 +31,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
@@ -58,7 +59,11 @@
 
   // Test null and undefined.
   { "name": "null_var", "value": null },
-  { "name": "undefined_var", "value": undefined }
+  { "name": "undefined_var", "value": undefined },
+
+  // Test to show unicode currently handled.
+  { "name": "unicode_var", "value": "\u4025\u4026_string" },
+  { "name": "\u4025\u4026_var", "value": "string" },
 ];
 
 function setup_global() {
diff --git a/testing/resources/javascript/globals_expected.txt b/testing/resources/javascript/globals_expected.txt
index fcebd70..9e80a62 100644
--- a/testing/resources/javascript/globals_expected.txt
+++ b/testing/resources/javascript/globals_expected.txt
@@ -10,17 +10,20 @@
 Alert:   object_var = undefined
 Alert:   null_var = undefined
 Alert:   undefined_var = undefined
+Alert:   unicode_var = undefined
+Alert:   䀥䀦_var = undefined
 Alert: ************ After Setup ************
 Alert: Enumerable Globals:
 Alert:   setPersistent = function setPersistent() { [native code] }, own property = true
-Alert:   true_var = true, own property = true
 Alert:   false_var = false, own property = true
-Alert:   zero_var = 0, own property = true
-Alert:   number_var = -3.918, own property = true
-Alert:   string_var = This is a string, own property = true
-Alert:   object_var = [object Object], own property = true
 Alert:   null_var = null, own property = true
-Alert:   undefined_var = undefined, own property = true
+Alert:   number_var = -3.918, own property = true
+Alert:   object_var = [object Object], own property = true
+Alert:   string_var = This is a string, own property = true
+Alert:   true_var = true, own property = true
+Alert:   unicode_var = 䀥䀦_string, own property = true
+Alert:   zero_var = 0, own property = true
+Alert:   䀥䀦_var = string, own property = true
 Alert: Expected Globals:
 Alert:   true_var = true
 Alert:   false_var = false
@@ -33,6 +36,8 @@
 Alert:     blue
 Alert:   null_var = null
 Alert:   undefined_var = undefined
+Alert:   unicode_var = 䀥䀦_string
+Alert:   䀥䀦_var = string
 Alert: ************ After Deletion ************
 Alert: Enumerable Globals:
 Alert:   setPersistent = function setPersistent() { [native code] }, own property = true
@@ -45,18 +50,21 @@
 Alert:   object_var = undefined
 Alert:   null_var = undefined
 Alert:   undefined_var = undefined
+Alert:   unicode_var = undefined
+Alert:   䀥䀦_var = undefined
 Alert: For undefined_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
 Alert: ************ After Setup and Persist false ************
 Alert: Enumerable Globals:
 Alert:   setPersistent = function setPersistent() { [native code] }, own property = true
-Alert:   true_var = true, own property = true
 Alert:   false_var = false, own property = true
-Alert:   zero_var = 0, own property = true
-Alert:   number_var = -3.918, own property = true
-Alert:   string_var = This is a string, own property = true
-Alert:   object_var = [object Object], own property = true
 Alert:   null_var = null, own property = true
-Alert:   undefined_var = undefined, own property = true
+Alert:   number_var = -3.918, own property = true
+Alert:   object_var = [object Object], own property = true
+Alert:   string_var = This is a string, own property = true
+Alert:   true_var = true, own property = true
+Alert:   unicode_var = 䀥䀦_string, own property = true
+Alert:   zero_var = 0, own property = true
+Alert:   䀥䀦_var = string, own property = true
 Alert: Expected Globals:
 Alert:   true_var = true
 Alert:   false_var = false
@@ -69,6 +77,8 @@
 Alert:     blue
 Alert:   null_var = null
 Alert:   undefined_var = undefined
+Alert:   unicode_var = 䀥䀦_string
+Alert:   䀥䀦_var = string
 Alert: For true_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
 Alert: For false_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
 Alert: For zero_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
@@ -77,6 +87,8 @@
 Alert: For object_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
 Alert: For null_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
 Alert: For undefined_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
+Alert: For unicode_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
+Alert: For 䀥䀦_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
 Alert: ************ After Delete and Persist ************
 Alert: Enumerable Globals:
 Alert:   setPersistent = function setPersistent() { [native code] }, own property = true
@@ -89,18 +101,21 @@
 Alert:   object_var = undefined
 Alert:   null_var = undefined
 Alert:   undefined_var = undefined
+Alert:   unicode_var = undefined
+Alert:   䀥䀦_var = undefined
 Alert: For undefined_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
 Alert: ************ After Setup and Persist true ************
 Alert: Enumerable Globals:
 Alert:   setPersistent = function setPersistent() { [native code] }, own property = true
-Alert:   true_var = true, own property = true
 Alert:   false_var = false, own property = true
-Alert:   zero_var = 0, own property = true
-Alert:   number_var = -3.918, own property = true
-Alert:   string_var = This is a string, own property = true
-Alert:   object_var = [object Object], own property = true
 Alert:   null_var = null, own property = true
-Alert:   undefined_var = undefined, own property = true
+Alert:   number_var = -3.918, own property = true
+Alert:   object_var = [object Object], own property = true
+Alert:   string_var = This is a string, own property = true
+Alert:   true_var = true, own property = true
+Alert:   unicode_var = 䀥䀦_string, own property = true
+Alert:   zero_var = 0, own property = true
+Alert:   䀥䀦_var = string, own property = true
 Alert: Expected Globals:
 Alert:   true_var = true
 Alert:   false_var = false
@@ -113,3 +128,5 @@
 Alert:     blue
 Alert:   null_var = null
 Alert:   undefined_var = undefined
+Alert:   unicode_var = 䀥䀦_string
+Alert:   䀥䀦_var = string
diff --git a/testing/resources/javascript/immutable_proto.in b/testing/resources/javascript/immutable_proto.in
new file mode 100644
index 0000000..61885c5
--- /dev/null
+++ b/testing/resources/javascript/immutable_proto.in
@@ -0,0 +1,43 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [
+    3 0 R
+  ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+% OpenAction action
+{{object 10 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 11 0 R
+>>
+endobj
+{{object 11 0}} <<
+  {{streamlen}}
+>>
+stream
+{{include expect.js}}
+expect("this.__proto__", "[object Object]");
+expect("app.__proto__", "[object Object]");
+expectError("this.__proto__ = {}");
+expectError("app.__proto__ = this");
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/immutable_proto_expected.txt b/testing/resources/javascript/immutable_proto_expected.txt
new file mode 100644
index 0000000..7e7c670
--- /dev/null
+++ b/testing/resources/javascript/immutable_proto_expected.txt
@@ -0,0 +1,4 @@
+Alert: PASS: this.__proto__ = [object Object]
+Alert: PASS: app.__proto__ = [object Object]
+Alert: PASS: this.__proto__ = {} threw TypeError: Immutable prototype object '[object global]' cannot have their prototype set
+Alert: PASS: app.__proto__ = this threw TypeError: Immutable prototype object '[object Object]' cannot have their prototype set
diff --git a/testing/resources/javascript/mouse_events.in b/testing/resources/javascript/mouse_events.in
index 4c6935e..b7b5a6d 100644
--- a/testing/resources/javascript/mouse_events.in
+++ b/testing/resources/javascript/mouse_events.in
@@ -24,6 +24,7 @@
   /Contents [21 0 R]
   /MediaBox [0 0 612 792]
 >>
+endobj
 % Forms
 {{object 4 0}} <<
   /Fields [
@@ -32,6 +33,7 @@
     7 0 R
   ]
 >>
+endobj
 % Field with actions:
 % Cursor enter: E
 % Cursor exit: X
@@ -54,6 +56,7 @@
    /Bl 15 0 R
  >>
 >>
+endobj
 {{object 6 0}} <<
  /Type /Annot
  /Subtype /Widget
diff --git a/testing/resources/javascript/named_action.in b/testing/resources/javascript/named_action.in
new file mode 100644
index 0000000..ba8d28b
--- /dev/null
+++ b/testing/resources/javascript/named_action.in
@@ -0,0 +1,33 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [
+    3 0 R
+  ]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+% OpenAction action - not really JS, but generates text under test env.
+{{object 10 0}} <<
+  /Type /Action
+  /S /Named
+  /N (Print)
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/named_action_expected.txt b/testing/resources/javascript/named_action_expected.txt
new file mode 100644
index 0000000..1099a7f
--- /dev/null
+++ b/testing/resources/javascript/named_action_expected.txt
@@ -0,0 +1 @@
+Execute named action: Print
diff --git a/testing/resources/javascript/property_test_helpers.js b/testing/resources/javascript/property_test_helpers.js
index 47a25ef..0e405fc 100644
--- a/testing/resources/javascript/property_test_helpers.js
+++ b/testing/resources/javascript/property_test_helpers.js
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/resources/javascript/public_methods_expected.txt b/testing/resources/javascript/public_methods_expected.txt
index 17abeff..70cd8ee 100644
--- a/testing/resources/javascript/public_methods_expected.txt
+++ b/testing/resources/javascript/public_methods_expected.txt
@@ -21,11 +21,11 @@
 Alert: **********************
 Alert: PASS: AFDate_KeystrokeEx() threw AFDate_KeystrokeEx: AFDate_KeystrokeEx's parameter size not correct
 Alert: PASS: AFDate_KeystrokeEx(1, 2) threw AFDate_KeystrokeEx: AFDate_KeystrokeEx's parameter size not correct
-[icon=3,type=0]: The input value can't be parsed as a valid date/time (2).
+AFDate_KeystrokeEx[icon=3,type=0]: The input value can't be parsed as a valid date/time (2).
 Alert: PASS: AFDate_KeystrokeEx(2) = x
-[icon=3,type=0]: The input value can't be parsed as a valid date/time (blooey).
+AFDate_KeystrokeEx[icon=3,type=0]: The input value can't be parsed as a valid date/time (blooey).
 Alert: PASS: AFDate_KeystrokeEx('blooey') = x
-[icon=3,type=0]: The input value can't be parsed as a valid date/time (m/d).
+AFDate_KeystrokeEx[icon=3,type=0]: The input value can't be parsed as a valid date/time (m/d).
 Alert: PASS: AFDate_KeystrokeEx('m/d') = x
 Alert: **********************
 Alert: PASS: AFExtractNums() threw AFExtractNums: Incorrect number of parameters passed to function.
@@ -50,7 +50,7 @@
 Alert: **********************
 Alert: PASS: AFNumber_Keystroke() threw AFNumber_Keystroke: Incorrect number of parameters passed to function.
 Alert: PASS: AFNumber_Keystroke(1) threw AFNumber_Keystroke: Incorrect number of parameters passed to function.
-[icon=3,type=0]: The input value is invalid.
+AFNumber_Keystroke[icon=3,type=0]: The input value is invalid.
 Alert: PASS: AFNumber_Keystroke(1, 2) threw AFNumber_Keystroke: The input value is invalid.
 Alert: PASS: AFNumber_Keystroke(1, 2) = 123
 Alert: PASS: AFNumber_Keystroke(1, 2, 3) = 123
@@ -277,19 +277,19 @@
 Alert: **********************
 Alert: PASS: AFPercent_Keystroke() threw AFPercent_Keystroke: Incorrect number of parameters passed to function.
 Alert: PASS: AFPercent_Keystroke(1) threw AFPercent_Keystroke: Incorrect number of parameters passed to function.
-[icon=3,type=0]: The input value is invalid.
+AFNumber_Keystroke[icon=3,type=0]: The input value is invalid.
 Alert: PASS: AFPercent_Keystroke(1, 0) threw AFPercent_Keystroke: The input value is invalid.
 Alert: PASS: AFPercent_Keystroke(1, 0) = .123
 Alert: **********************
 Alert: PASS: AFRange_Validate() threw AFRange_Validate: Incorrect number of parameters passed to function.
 Alert: PASS: AFRange_Validate(1, 2, 3, 4, 5) threw AFRange_Validate: Incorrect number of parameters passed to function.
-[icon=3,type=0]: The input value must be greater than or equal to 2 and less than or equal to 4.
+AFRange_Validate[icon=3,type=0]: The input value must be greater than or equal to 2 and less than or equal to 4.
 Alert: PASS: AFRange_Validate(true, 2, true, 4) = 1
-[icon=3,type=0]: The input value must be greater than or equal to 2 and less than or equal to 4.
+AFRange_Validate[icon=3,type=0]: The input value must be greater than or equal to 2 and less than or equal to 4.
 Alert: PASS: AFRange_Validate(true, 2, true, 4) = 5
-[icon=3,type=0]: The input value must be greater than or equal to 2.
+AFRange_Validate[icon=3,type=0]: The input value must be greater than or equal to 2.
 Alert: PASS: AFRange_Validate(true, 2, false, 4) = 1
-[icon=3,type=0]: The input value must be less than or equal to 4.
+AFRange_Validate[icon=3,type=0]: The input value must be less than or equal to 4.
 Alert: PASS: AFRange_Validate(false, 2, true, 4) = 5
 Alert: PASS: AFRange_Validate(true, 2, true, 4) = 3
 Alert: PASS: AFRange_Validate(false, 2, true, 4) = 1
@@ -345,11 +345,11 @@
 Alert: **********************
 Alert: PASS: AFSpecial_KeystrokeEx() threw AFSpecial_KeystrokeEx: Incorrect number of parameters passed to function.
 Alert: PASS: AFSpecial_KeystrokeEx('') = 12345
-[icon=3,type=0]: The input value is invalid.
+AFSpecial_KeystrokeEx[icon=3,type=0]: The input value is invalid.
 Alert: PASS: AFSpecial_KeystrokeEx('9999') = 123
-[icon=3,type=0]: The input value is too long.
+AFSpecial_KeystrokeEx[icon=3,type=0]: The input value is too long.
 Alert: PASS: AFSpecial_KeystrokeEx('9999') = 12345
-[icon=3,type=0]: The input value is invalid.
+AFSpecial_KeystrokeEx[icon=3,type=0]: The input value is invalid.
 Alert: PASS: AFSpecial_KeystrokeEx('9999') = abcd
 Alert: PASS: AFSpecial_KeystrokeEx('9999') = 1234
 Alert: PASS: AFSpecial_KeystrokeEx('XXXX') = abcd
@@ -385,13 +385,13 @@
 Alert: PASS: AFSpecial_KeystrokeEx() threw AFSpecial_KeystrokeEx: Incorrect number of parameters passed to function.
 Alert: PASS: AFSpecial_KeystrokeEx('') = 12345
 Alert: PASS: AFSpecial_KeystrokeEx('9999') = 123
-[icon=3,type=0]: The input value is too long.
+AFSpecial_KeystrokeEx[icon=3,type=0]: The input value is too long.
 Alert: PASS: AFSpecial_KeystrokeEx('9999') = 12345
-[icon=3,type=0]: The input value is too long.
+AFSpecial_KeystrokeEx[icon=3,type=0]: The input value is too long.
 Alert: PASS: AFSpecial_KeystrokeEx('9999') = abcd
-[icon=3,type=0]: The input value is too long.
+AFSpecial_KeystrokeEx[icon=3,type=0]: The input value is too long.
 Alert: PASS: AFSpecial_KeystrokeEx('9999') = 1234
-[icon=3,type=0]: The input value is too long.
+AFSpecial_KeystrokeEx[icon=3,type=0]: The input value is too long.
 Alert: PASS: AFSpecial_KeystrokeEx('XXXX') = abcd
 Alert: **********************
 Alert: PASS: AFMergeChange() threw AFMergeChange: Incorrect number of parameters passed to function.
diff --git a/testing/resources/javascript/unsupported.in b/testing/resources/javascript/unsupported.in
index 4d1ad5e..eccfbab 100644
--- a/testing/resources/javascript/unsupported.in
+++ b/testing/resources/javascript/unsupported.in
@@ -19,6 +19,7 @@
   /Parent 2 0 R
   /MediaBox [0 0 612 792]
 >>
+endobj
 % OpenAction action
 {{object 10 0}} <<
   /Type /Action
@@ -26,7 +27,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/util_bytetochar.in b/testing/resources/javascript/util_bytetochar.in
index 531e679..23dc423 100644
--- a/testing/resources/javascript/util_bytetochar.in
+++ b/testing/resources/javascript/util_bytetochar.in
@@ -23,6 +23,7 @@
   /Contents [21 0 R]
   /MediaBox [0 0 612 792]
 >>
+endobj
 % OpenAction action
 {{object 10 0}} <<
   /Type /Action
@@ -30,7 +31,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/util_printd.in b/testing/resources/javascript/util_printd.in
index e4c8afc..d0d17b4 100644
--- a/testing/resources/javascript/util_printd.in
+++ b/testing/resources/javascript/util_printd.in
@@ -23,6 +23,7 @@
   /Contents [21 0 R]
   /MediaBox [0 0 612 792]
 >>
+endobj
 % OpenAction action
 {{object 10 0}} <<
   /Type /Action
@@ -30,7 +31,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/util_printx.in b/testing/resources/javascript/util_printx.in
index 3002051..84d4846 100644
--- a/testing/resources/javascript/util_printx.in
+++ b/testing/resources/javascript/util_printx.in
@@ -23,6 +23,7 @@
   /Contents [21 0 R]
   /MediaBox [0 0 612 792]
 >>
+endobj
 % OpenAction action
 {{object 10 0}} <<
   /Type /Action
@@ -30,7 +31,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/util_scand.in b/testing/resources/javascript/util_scand.in
index 25462d0..344aacd 100644
--- a/testing/resources/javascript/util_scand.in
+++ b/testing/resources/javascript/util_scand.in
@@ -27,7 +27,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/v8_features.in b/testing/resources/javascript/v8_features.in
index 3d90473..8a5b6af 100644
--- a/testing/resources/javascript/v8_features.in
+++ b/testing/resources/javascript/v8_features.in
@@ -19,6 +19,7 @@
   /Parent 2 0 R
   /MediaBox [0 0 612 792]
 >>
+endobj
 % OpenAction action
 {{object 10 0}} <<
   /Type /Action
@@ -26,7 +27,7 @@
   /JS 11 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 11 0}} <<
   {{streamlen}}
 >>
diff --git a/testing/resources/javascript/xfa_specific/bug_1004106.in b/testing/resources/javascript/xfa_specific/bug_1004106.in
new file mode 100644
index 0000000..926c39b
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1004106.in
@@ -0,0 +1,65 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="subform1">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+        <medium long="297mm" short="210mm" stock="a4"/>
+      </pageArea>
+    </pageSet>
+    <subform layout="tb" name="subform2">
+      <variables name="vara">
+      </variables>
+      <occur initial="1" max="10" min="0" name="occur1">
+      </occur>
+      <field h="10mm" name="field1" w="40mm" x="10mm" y="10mm">
+        <event activity="ready" ref="$form">
+          <script contentType="application/x-javascript">
+            var ref = [];
+
+            app.test = function () {
+              var arr = []
+              var v1 = 1;
+              st = {};
+              var v2 = [0, st, 2, 3];
+              var v3 = [0, "poc", {}];
+              st.toString = function(){
+                for (var i = 0; i != 8; i++) v3.push({});
+                return "poc";
+              }
+
+              // Trigger
+              pfm_rt.Oneof(1, v1, v2, v3);
+            }
+          </script>
+        </event>
+      </field>
+      <field h="10mm" name="field2" w="40mm" x="10mm" y="10mm">
+        <event activity="ready" ref="$form">
+          <script contentType="application/x-formcalc">
+            app.test();
+          </script>
+        </event>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1026991.evt b/testing/resources/javascript/xfa_specific/bug_1026991.evt
new file mode 100644
index 0000000..37d9022
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1026991.evt
@@ -0,0 +1,5 @@
+mousemove,0,0
+mousedown,left,0,0
+mouseup,left,0,0
+charcode,80
+mousemove,0,200
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1026991.in b/testing/resources/javascript/xfa_specific/bug_1026991.in
new file mode 100644
index 0000000..4fb7657
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1026991.in
@@ -0,0 +1,65 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /XFA 4 0 R
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp xmlns="http://ns.adobe.com/xdp/">
+  <config>
+    <acrobat>
+      <acrobat7>
+        <dynamicRender>required</dynamicRender>
+      </acrobat7>
+    </acrobat>
+    <present>
+      <pdf>
+        <interactive>1</interactive>
+      </pdf>
+    </present>
+  </config>
+  <template>
+    <subform>
+      <bookend leader="$"/>
+      <keep intact="none" previous="contentArea"/>
+      <field name="N01" minH="32in">
+        <ui>
+          <choiceList>
+            <margin rightInset="8in"/>
+          </choiceList>
+        </ui>
+      </field>
+      <field minH="32in">
+        <event activity="change">
+          <script>
+            $host.setFocus("N01")
+          </script>
+        </event>
+      </field>
+    </subform>
+  </template>
+</xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1039629.evt b/testing/resources/javascript/xfa_specific/bug_1039629.evt
new file mode 100644
index 0000000..96d37be
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1039629.evt
@@ -0,0 +1,3 @@
+mousedown,left,295,187
+mouseup,left,295,187
+keycode,40
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1039629.in b/testing/resources/javascript/xfa_specific/bug_1039629.in
new file mode 100644
index 0000000..a911db6
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1039629.in
@@ -0,0 +1,188 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /AcroForm 3 0 R
+  /Pages 5 0 R
+  /NeedsRendering true
+>>
+endobj
+{{object 3 0}} <<
+  /XFA [
+    (preamble) 7 0 R
+    (config) 8 0 R
+    (template) 9 0 R
+    (form) 10 0 R
+    (postamble) 11 0 R
+  ]
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 12 0 R ]
+>>
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+<?xml version="1.0" encoding="UTF-8"?>
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2007-08-07T18:35:24Z" uuid="3b6c6ea3-3edf-40f5-90ff-51ee202e15b4">
+endstream
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/2.6/">
+  <agent name="designer">
+    <destination>pdf</destination>
+    <pdf>
+      <!--  [0..n]  -->
+      <fontInfo/>
+    </pdf>
+  </agent>
+  <present>
+    <!--  [0..n]  -->
+    <pdf>
+      <!--  [0..n]  -->
+      <version>1.7</version>
+      <renderPolicy>client</renderPolicy>
+      <creator>Adobe LiveCycle Designer ES 8.1</creator>
+      <producer>Adobe LiveCycle Designer ES 8.1</producer>
+      <scriptModel>XFA</scriptModel>
+      <interactive>1</interactive>
+      <tagged>1</tagged>
+      <fontInfo>
+        <embed>1</embed>
+      </fontInfo>
+      <compression>
+        <level>6</level>
+        <compressLogicalStructure>1</compressLogicalStructure>
+      </compression>
+      <linearized>1</linearized>
+      <viewerPreferences>
+        <addViewerPreferences>0</addViewerPreferences>
+        <duplexOption>simplex</duplexOption>
+        <numberOfCopies>0</numberOfCopies>
+      </viewerPreferences>
+    </pdf>
+    <cache>
+      <macroCache/>
+      <renderCache/>
+    </cache>
+    <incrementalMerge/>
+    <script>
+      <runScripts/>
+      <exclude/>
+      <currentPage/>
+    </script>
+    <copies/>
+    <layout/>
+    <xdp>
+      <packets>*</packets>
+    </xdp>
+    <destination>pdf</destination>
+  </present>
+  <psMap/>
+  <acrobat>
+    <acrobat7>
+      <dynamicRender>required</dynamicRender>
+    </acrobat7>
+  </acrobat>
+</config>
+endstream
+endobj
+{{object 9 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform name="form1">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <subform h="10.5in" w="8in">
+      <field h="9.0001mm" name="ChoiceList" w="47.625mm" x="6.35mm" y="92.075mm">
+        <ui>
+          <choiceList/>
+        </ui>
+        <items>
+          <text>Foo</text>
+        </items>
+      </field>
+      <field h="100mm" name="ChoiceList1" w="100mm" x="0mm" y="0mm">
+      <ui>
+        <choiceList open="onEntry"/>
+      </ui>
+      <items>
+        <text>1111111111111</text>
+        <text>222222222222222</text>
+      </items>
+      <event activity="change">
+        <script contentType="application/x-javascript">
+        a += 1;
+        if (a == 2) {
+            list1 = xfa.resolveNode("ChoiceList");
+            xfa.host.setFocus(list1);
+            xfa.template.remerge();
+            xfa.host.openList(list1);
+        }
+        </script>
+      </event>
+    </field>
+  </subform>
+    <event activity="docReady">
+      <script contentType="application/x-javascript">
+        a = 0;
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{object 10 0}} <<
+  {{streamlen}}
+>>
+stream
+<form xmlns="http://www.xfa.org/schema/xfa-form/2.6/">
+</form>
+endstream
+endobj
+{{object 11 0}} <<
+  {{streamlen}}
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+{{object 12 0}} <<
+  /Type /Page
+  /Contents 13 0 R
+  /CropBox [ 0.0 0.0 612.0 792.0 ]
+  /MediaBox [ 0.0 0.0 612.0 792.0 ]
+  /Parent 5 0 R
+  /Resources <<
+    /Font <<
+      /T1_0 14 0 R
+    >>
+    /ProcSet [ /PDF /Text ]
+  >>
+  /Rotate 0
+  /StructParents 0
+>>
+endobj
+{{object 14 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+  /Encoding /WinAnsiEncoding
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1040329.in b/testing/resources/javascript/xfa_specific/bug_1040329.in
new file mode 100644
index 0000000..7117084
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1040329.in
@@ -0,0 +1,99 @@
+{{header}}
+{{object 1 0}} <<
+  /Pages 2 0 R
+  /NeedsRendering true
+  /AcroForm <</XFA 5 0 R>>
+  /OpenAction 3 0 R
+  /Type /Catalog
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 4 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /S /JavaScript
+  /JS (
+  )
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [ 0 0 795 842 ]
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+<template >
+  <subform allowMacro="0" layout="lr" mergeMode="consumeData" name="subform_0" >
+    <pageSet >
+      <occur initial="1" max="10" min="0" ></occur>
+      <pageArea >
+        <contentArea h="0.75in" w="0.25in" x="0.25in" y="0.25in" ></contentArea>
+        <subform allowMacro="0" layout="row" mergeMode="consumeData" name="subform_1" >
+          <event activity="initialize" >
+            <script contentType="application/x-javascript">
+            </script>
+          </event>
+          <field name="field_1" >
+            <bind match="dataRef" ref="a" ></bind>
+            <calculate >
+              <script contentType="application/x-javascript">
+                var f=xfa.resolveNode("xfa.form.subform_0");
+                xfa.host.setFocus(f);
+                f.mark ="square";
+              </script>
+            </calculate>
+            <event activity="docReady" >
+              <script contentType="application/x-javascript">
+                xfa.host.resetData(this);
+              </script>
+            </event>
+          </field>
+        </subform>
+      </pageArea>
+    </pageSet>
+    <field name="field_2" >
+      <bind match="dataRef" ref="a" ></bind>
+      <calculate >
+        <script contentType="application/x-javascript">
+          xfa.host.setFocus(this);
+        </script>
+      </calculate>
+      <items presence="hidden" save="0" >
+        <text >a</text>
+        <text >b</text>
+        <text >c</text>
+      </items>
+      <ui >
+        <choiceList commitOn="exit" open="onEntry" textEntry="1" ></choiceList>
+      </ui>
+      <value override="0" >
+        <text >a</text>
+      </value>
+      <event activity="initialize" >
+        <script contentType="application/x-javascript">
+        </script>
+      </event>
+      <event activity="change" >
+        <script contentType="application/x-javascript">
+          xfa.form.remerge();
+          xfa.host.openList(this);
+        </script>
+      </event>
+    </field>
+  </subform>
+</template>
+</xdp:xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1042915.evt b/testing/resources/javascript/xfa_specific/bug_1042915.evt
new file mode 100644
index 0000000..c0cea89
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1042915.evt
@@ -0,0 +1,8 @@
+mousedown,left,0,0
+mouseup,left,0,0
+keycode,9
+keycode,9
+mousedown,left,0,0
+mouseup,left,0,0
+mousedown,left,0,0
+mouseup,left,0,0
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1042915.pdf b/testing/resources/javascript/xfa_specific/bug_1042915.pdf
new file mode 100644
index 0000000..36a2c8b
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1042915.pdf
Binary files differ
diff --git a/testing/resources/javascript/xfa_specific/bug_1042915_expected.txt b/testing/resources/javascript/xfa_specific/bug_1042915_expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1042915_expected.txt
diff --git a/testing/resources/javascript/xfa_specific/bug_1043508.in b/testing/resources/javascript/xfa_specific/bug_1043508.in
new file mode 100644
index 0000000..2afe129
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1043508.in
@@ -0,0 +1,62 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /XFA 4 0 R
+  >>
+  /NeedsRendering true
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 3 3]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp xmlns="http://ns.adobe.com/xdp/">
+<config>
+  <acrobat>
+    <acrobat7>
+      <dynamicRender>required</dynamicRender>
+    </acrobat7>
+  </acrobat>
+</config>
+<template>
+  <subform>
+    <breakBefore>
+      <script>
+        $.#field[1]=2
+      </script>
+    </breakBefore>
+    <field>
+      <ui>
+        <choiceList open="multiSelect"></choiceList>
+      </ui>
+    </field>
+    <field>
+      <event activity="change">
+        <script>
+          $host.openList(&quot;$form.#field[0]&quot;)
+        </script>
+      </event>
+    </field>
+  </subform>
+</template>
+</xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1043510.evt b/testing/resources/javascript/xfa_specific/bug_1043510.evt
index 891a49d..08e98cf 100644
--- a/testing/resources/javascript/xfa_specific/bug_1043510.evt
+++ b/testing/resources/javascript/xfa_specific/bug_1043510.evt
@@ -1 +1 @@
-mousedoubleclick,left,0,0
\ No newline at end of file
+mousedoubleclick,left,0,0
diff --git a/testing/resources/javascript/xfa_specific/bug_1047914.evt b/testing/resources/javascript/xfa_specific/bug_1047914.evt
new file mode 100644
index 0000000..45b948d
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1047914.evt
@@ -0,0 +1,4 @@
+mousedown,left,0,2
+mouseup,left,0,2
+mousedown,left,0,2
+mouseup,left,0,2
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1047914.in b/testing/resources/javascript/xfa_specific/bug_1047914.in
new file mode 100644
index 0000000..969d7a0
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1047914.in
@@ -0,0 +1,47 @@
+{{header}}
+{{object 1 0}} <<
+  /AcroForm <<
+    /XFA 2 0 R
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp xmlns="http://ns.adobe.com/xdp/">
+  <config>
+    <acrobat>
+      <acrobat7>
+        <dynamicRender>required</dynamicRender>
+      </acrobat7>
+    </acrobat>
+    <present>
+      <pdf>
+        <interactive>1</interactive>
+      </pdf>
+    </present>
+  </config>
+  <template>
+    <subform>
+      <bookend leader="$"></bookend>
+      <keep previous="pageArea"></keep>
+      <field anchorType="middleCenter">
+        <ui>
+          <choiceList open="multiSelect">
+            <border><edge stroke="raised"></edge></border>
+            <margin></margin>
+          </choiceList>
+        </ui>
+        <items save="1"><float>41.0</float></items>
+        <items><float>43.0</float><integer>44</integer></items>
+      </field>
+    </subform>
+  </template>
+</xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1052651.evt b/testing/resources/javascript/xfa_specific/bug_1052651.evt
new file mode 100644
index 0000000..e88ef06
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1052651.evt
@@ -0,0 +1,4 @@
+mousemove,100,100
+mousedown,left,100,100
+mouseup,left,100,100
+charcode,96
diff --git a/testing/resources/javascript/xfa_specific/bug_1052651.in b/testing/resources/javascript/xfa_specific/bug_1052651.in
new file mode 100644
index 0000000..a26312d
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1052651.in
@@ -0,0 +1,168 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /AcroForm 3 0 R
+  /Pages 5 0 R
+  /NeedsRendering true
+>>
+endobj
+{{object 3 0}} <<
+  /DA (/Helv 0 Tf 0 g )
+  /DR <<
+    /Font <<
+      /Helv 9 0 R
+    >>
+  >>
+  /XFA [
+    (preamble) 10 0 R
+    (config) 11 0 R
+    (template) 12 0 R
+    (form) 13 0 R
+    (postamble) 14 0 R
+  ]
+>>
+endobj
+{{object 5 0}}  <<
+  /Type /Pages
+  /Count 1
+  /Kids [6 0 R]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Page
+  /CropBox [0.0 0.0 612.0 792.0]
+  /MediaBox [0.0 0.0 612.0 792.0]
+  /Parent 5 0 R
+  /Resources <<
+    /ProcSet [/PDF /Text]
+  >>
+  /Rotate 0
+  /StructParents 0
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+  /Encoding /WinAnsiEncoding
+>>
+endobj
+{{object 9 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+  /Encoding /WinAnsiEncoding
+>>
+endobj
+{{object 10 0}} <<
+  {{streamlen}}
+>>
+stream
+<?xml version="1.0" encoding="UTF-8"?>
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+endstream
+endobj
+{{object 11 0}} <<
+  {{streamlen}}
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/2.6/">
+  <agent name="designer">
+    <destination>pdf</destination>
+    <pdf>
+      <fontInfo/>
+    </pdf>
+  </agent>
+  <present>
+    <pdf>
+      <version>1.7</version>
+      <renderPolicy>client</renderPolicy>
+      <scriptModel>XFA</scriptModel>
+      <interactive>1</interactive>
+    </pdf>
+    <xdp>
+      <packets>*</packets>
+    </xdp>
+    <destination>pdf</destination>
+  </present>
+  <acrobat>
+    <acrobat7>
+      <dynamicRender>required</dynamicRender>
+    </acrobat7>
+  </acrobat>
+</config>
+endstream
+endobj
+{{object 12 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <subform h="10.5in" w="8in" name="subform2">
+      <field h="9.0001mm" name="field1" w="47.625mm" x="6.35mm" y="92.075mm">
+        <ui>
+          <choiceList/>
+        </ui>
+        <items save="1">
+          <text>Single</text>
+          <text>Married</text>
+          <text>Other</text>
+        </items>
+      </field>
+      <field name="field2" h="200mm" w="200mm" x="1mm" y="1mm">
+        <ui>
+          <textEdit/>
+        </ui>
+        <value>
+          <text>pdfium</text>
+        </value>
+        <event activity="change">
+          <script contentType="application/x-javascript">
+            f2_change = f2_change + 1;
+            if (f2_change == 2) {
+                xfa.event.cancelAction = true;
+                f1 = xfa.resolveNode("xfa.form..field1");
+                xfa.host.setFocus(f1);
+                xfa.template.remerge();
+                xfa.host.openList(f1);
+            }
+          </script>
+        </event>
+      </field>
+    </subform>
+    <event activity="docReady">
+      <script contentType="application/x-javascript">
+        f2_change = 0;
+        f2 = xfa.resolveNode("xfa.form..field2");
+        xfa.host.setFocus(f2);
+     </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{object 13 0}} <<
+  {{streamlen}}
+>>
+stream
+<form xmlns="http://www.xfa.org/schema/xfa-form/2.6/"></form>
+endstream
+endobj
+{{object 14 0}} <<
+ {{streamlen}}
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1052786.in b/testing/resources/javascript/xfa_specific/bug_1052786.in
new file mode 100644
index 0000000..b9dd5e9
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1052786.in
@@ -0,0 +1,168 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /AcroForm 3 0 R
+  /Pages 5 0 R
+  /NeedsRendering true
+>>
+endobj
+{{object 3 0}} <<
+  /DA (/Helv 0 Tf 0 g )
+  /DR <<
+    /Font <<
+      /Helv 9 0 R
+    >>
+  >>
+  /XFA [
+    (preamble) 10 0 R
+    (config) 11 0 R
+    (template) 12 0 R
+    (form) 13 0 R
+    (postamble) 14 0 R
+  ]
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [6 0 R]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Page
+  /CropBox [0.0 0.0 612.0 792.0]
+  /MediaBox [0.0 0.0 612.0 792.0]
+  /Parent 5 0 R
+  /Resources <<
+    /ProcSet [/PDF /Text]
+  >>
+  /Rotate 0
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+  /Encoding /WinAnsiEncoding
+>>
+endobj
+{{object 9 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+  /Encoding /WinAnsiEncoding
+>>
+endobj
+{{object 10 0}} <<
+  {{streamlen}}
+>>
+stream
+<?xml version="1.0" encoding="UTF-8"?>
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+endstream
+endobj
+{{object 11 0}} <<
+  {{streamlen}}
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/2.6/">
+  <agent name="designer">
+    <destination>pdf</destination>
+    <pdf>
+      <fontInfo/>
+    </pdf>
+  </agent>
+  <present>
+    <pdf>
+      <version>1.7</version>
+      <renderPolicy>client</renderPolicy>
+      <scriptModel>XFA</scriptModel>
+      <interactive>1</interactive>
+    </pdf>
+    <xdp>
+      <packets>*</packets>
+    </xdp>
+    <destination>pdf</destination>
+  </present>
+  <psMap/>
+  <acrobat>
+    <acrobat7>
+      <dynamicRender>required</dynamicRender>
+    </acrobat7>
+  </acrobat>
+</config>
+endstream
+endobj
+{{object 12 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <subform h="10.5in" w="8in" name="subform2">
+      <field h="9.0001mm" name="choiceList0" w="47.625mm" x="6.35mm" y="92.075mm">
+        <ui>
+          <choiceList/>
+        </ui>
+        <items save="1">
+          <text>Single</text>
+          <text>Married</text>
+          <text>Other</text>
+        </items>
+      </field>
+      <field name="choiceList1" h="10mm" w="1mm" x="5mm" y="50mm">
+        <ui>
+          <textEdit hScrollPolicy="off" vScrollPolicy="off" multiLine="0"/>
+        </ui>
+      <value>
+        <text maxChars="6">pdfium</text>
+      </value>
+      <event activity="full">
+        <script contentType="application/x-javascript">
+          full_count += 1;
+          if (full_count == 2) {
+            f1 = xfa.resolveNode("xfa.form..choiceList0");
+            xfa.host.setFocus(f1);
+            xfa.template.remerge();
+            xfa.host.openList(f1);
+          }
+        </script>
+      </event>
+    </field>
+  </subform>
+    <event activity="docReady">
+      <script contentType="application/x-javascript">
+        full_count = 0;
+        choiceList1 = xfa.resolveNode("xfa.form..choiceList1");
+        choiceList1.rawValue = "12345888888888888888";
+        xfa.host.setFocus(choiceList1);
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{object 13 0}} <<
+  {{streamlen}}
+>>
+stream
+<form xmlns="http://www.xfa.org/schema/xfa-form/2.6/"></form>
+endstream
+endobj
+{{object 14 0}} <<
+  {{streamlen}}
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1053617.in b/testing/resources/javascript/xfa_specific/bug_1053617.in
new file mode 100644
index 0000000..1e98bdc
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1053617.in
@@ -0,0 +1,155 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /AcroForm 2 0 R
+  /Pages 3 0 R
+  /Extensions <<
+    /ADBE <<
+      /BaseVersion /1.7
+      /ExtensionLevel 8
+    >>
+  >>
+  /NeedsRendering true
+>>
+endobj
+{{object 2 0}} <<
+  /XFA [
+    (preamble) 5 0 R
+    (config) 6 0 R
+    (template) 7 0 R
+    (postamble) 9 0 R
+  ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+endstream
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+  <destination>pdf</destination>
+  <pdf>
+    <fontInfo/>
+  </pdf>
+</agent>
+<present>
+  <pdf>
+    <version>1.7</version>
+    <adobeExtensionLevel>8</adobeExtensionLevel>
+    <renderPolicy>client</renderPolicy>
+    <scriptModel>XFA</scriptModel>
+    <interactive>1</interactive>
+  </pdf>
+  <xdp>
+    <packets>*</packets>
+  </xdp>
+  <destination>pdf</destination>
+  <script>
+    <runScripts>server</runScripts>
+  </script>
+</present>
+<acrobat>
+  <acrobat7>
+    <dynamicRender>required</dynamicRender>
+  </acrobat7>
+  <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform name="form1">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <subform h="10.5in" w="8in" name="subform2">
+      <field name="f1" h="10mm" w="10mm" x="20mm" y="20mm">
+        <ui>
+          <choiceList/>
+        </ui>
+        <items save="1">
+          <text>Single</text>
+        </items>
+      </field>
+      <subform name="f4" x="5mm" y="5mm">
+          <occur max="-1"/>
+          <field name="f2" h="10mm" w="10mm" x="1mm" y="1mm">
+            <ui>
+              <dateTimeEdit/>
+            </ui>
+            <value>
+              <date/>
+            </value>
+            <format>
+              <picture>date{MM/DD/YY}</picture>
+            </format>
+            <event activity="initialize">
+              <script contentType="application/x-javascript">
+                this.rawValue = "2020" + "-" + "12" + "-" + "10";
+              </script>
+            </event>
+            <event activity="change">
+              <script contentType="application/x-javascript">
+                a = a + 1;
+                if (a == 2) {
+                  xfa.event.cancelAction = true;
+                  c = xfa.resolveNode("xfa.form..f1");
+                  xfa.host.setFocus(c);
+                  d = xfa.resolveNode("xfa.form..f4");
+                  d.instanceManager.addInstance(1);
+                  d.instanceManager.removeInstance(0);
+                  xfa.host.openList(c);
+                }
+              </script>
+            </event>
+          </field>
+      </subform>
+    </subform>
+    <event activity="docReady">
+      <script contentType="application/x-javascript">
+        a = 0;
+        b = xfa.resolveNode("xfa.form..f2");
+        xfa.host.setFocus(b);
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{object 9 0}} <<
+  {{streamlen}}
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1054429.evt b/testing/resources/javascript/xfa_specific/bug_1054429.evt
new file mode 100644
index 0000000..86e0905
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1054429.evt
@@ -0,0 +1,2 @@
+mousedown,left,61,51
+keycode,46
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1054429.in b/testing/resources/javascript/xfa_specific/bug_1054429.in
new file mode 100644
index 0000000..7295e5f
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1054429.in
@@ -0,0 +1,147 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /AcroForm 2 0 R
+  /Pages 3 0 R
+  /Extensions <<
+    /ADBE <<
+      /BaseVersion /1.7
+      /ExtensionLevel 8
+    >>
+  >>
+  /NeedsRendering true
+>>
+endobj
+{{object 2 0}} <<
+  /XFA [
+    (preamble) 5 0 R
+    (config) 6 0 R
+    (template) 7 0 R
+    (postamble) 9 0 R
+  ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+endstream
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+  <destination>pdf</destination>
+  <pdf>
+    <fontInfo/>
+  </pdf>
+</agent>
+<present>
+  <pdf>
+    <version>1.7</version>
+    <adobeExtensionLevel>8</adobeExtensionLevel>
+    <renderPolicy>client</renderPolicy>
+    <scriptModel>XFA</scriptModel>
+    <interactive>1</interactive>
+  </pdf>
+  <xdp>
+    <packets>*</packets>
+  </xdp>
+  <destination>pdf</destination>
+  <script>
+    <runScripts>server</runScripts>
+  </script>
+</present>
+<acrobat>
+  <acrobat7>
+    <dynamicRender>required</dynamicRender>
+  </acrobat7>
+  <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <subform h="10.5in" w="8in" name="subform2">
+      <field h="10mm" name="f1" w="40mm" x="5mm" y="90mm">
+        <ui>
+          <choiceList/>
+        </ui>
+        <items save="1">
+          <text>Single</text>
+        </items>
+      </field>
+      <subform name="f4" x="5mm" y="5mm">
+          <occur max="-1"/>
+          <field name="f2" h="300mm" w="300mm" x="1mm" y="1mm">
+            <ui>
+              <textEdit/>
+            </ui>
+            <value>
+              <text>pdfiummmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm</text>
+            </value>
+            <event activity="change">
+              <script contentType="application/x-javascript">
+                a = a + 1;
+                if (a == 2)  {
+                    xfa.event.cancelAction = true;
+                    b = xfa.resolveNode("xfa.form..f1");
+                    xfa.host.setFocus(b);
+                    d = xfa.resolveNode("xfa.form..f4");
+                    d.instanceManager.addInstance(1);
+                    d.instanceManager.removeInstance(0);
+                    xfa.host.openList(b);
+                }
+              </script>
+            </event>
+          </field>
+      </subform>
+    </subform>
+    <event activity="docReady">
+      <script contentType="application/x-javascript">
+        a = 0;
+        c = xfa.resolveNode("xfa.form..f2");
+        xfa.host.setFocus(c);
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{object 9 0}} <<
+  {{streamlen}}
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1060549.evt b/testing/resources/javascript/xfa_specific/bug_1060549.evt
new file mode 100644
index 0000000..cee75e2
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1060549.evt
@@ -0,0 +1,3 @@
+mousedown,left,82,130
+mousedown,left,82,130
+keycode,09
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1060549.in b/testing/resources/javascript/xfa_specific/bug_1060549.in
new file mode 100644
index 0000000..2652548
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1060549.in
@@ -0,0 +1,59 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+    <subform h="10.5in" w="8in" name="subform2">
+      <field h="10mm" name="field1" w="10mm" x="0mm" y="20mm">
+        <ui>
+          <choiceList/>
+        </ui>
+        <items>
+          <text>aaaaaaaaaaaaaaaaaaaaa</text>
+        </items>
+      </field>
+      <field name="field2" h="20mm" w="50mm" x="0mm" y="30mm">
+        <ui>
+            <textEdit>
+            </textEdit>
+        </ui>
+      </field>
+      <subform name="subform3" x="0mm" y="5mm">
+        <occur max="-1"/>
+          <traversal>
+              <traverse operation="next" ref="$xfa.(eval('if (aa == 1) {aa=2;xfa.host.setFocus(cc);bb();xfa.host.openList(cc);}') == 0)"/>
+          </traversal>
+      </subform>
+    </subform>
+    <event activity="initialize">
+      <script contentType="application/x-javascript">
+        aa = 1;
+      </script>
+    </event>
+    <event activity="docReady">
+      <script contentType="application/x-javascript">
+        bb = function() {
+            dd = xfa.resolveNode("xfa.form..subform3");
+            dd.instanceManager.addInstance(1);
+            dd.instanceManager.removeInstance(0);
+        }
+        cc = xfa.resolveNode("xfa.form..field1");
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1069700.evt b/testing/resources/javascript/xfa_specific/bug_1069700.evt
new file mode 100644
index 0000000..069f712
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1069700.evt
@@ -0,0 +1,3 @@
+mousedown,left,200,200
+mouseup,left,200,200
+keycode,9,shift
diff --git a/testing/resources/javascript/xfa_specific/bug_1069700.in b/testing/resources/javascript/xfa_specific/bug_1069700.in
new file mode 100644
index 0000000..1dd6ba8
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1069700.in
@@ -0,0 +1,73 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform name="form1">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <subform h="10.5in" w="8in" name="subform2">
+      <field h="10mm" name="DropDownList1" w="10mm" x="0mm" y="20mm">
+        <ui>
+          <choiceList/>
+        </ui>
+        <items save="1">
+          <text>Single</text>
+          <text>Married</text>
+          <text>Other</text>
+        </items>
+      </field>
+      <field h="200mm" name="DropDownList2" w="2mm" x="0mm" y="30mm">
+        <ui>
+          <textEdit/>
+       </ui>
+      </field>
+      <subform name="subform3" x="0mm" y="5mm">
+        <occur max="-1"/>
+        <traversal>
+          <traverse operation="next" ref="$xfa.(eval('try { if (aaaa == 1) {bb();} } catch(e){}') == 0)"/>
+        </traversal>
+      </subform>
+      <bookend leader="$"/>
+      <keep intact="none" previous="contentArea"/>
+    </subform>
+    <subform name="subform4" x="0mm">
+      <occur max="-1"/>
+      <field name="choiceList3" minH="32in">
+        <ui>
+          <choiceList/>
+        </ui>
+      </field>
+    </subform>
+    <event activity="docReady">
+      <script contentType="application/x-javascript">
+        aaaa = 1;
+        bb = function() {
+          xfa.host.setFocus(f1);
+          d = xfa.resolveNode("xfa.form..subform4");
+          d.instanceManager.addInstance(1);
+          xfa.host.openList(f1);
+        }
+        f1 = xfa.resolveNode("xfa.form..DropDownList1");
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1069789.evt b/testing/resources/javascript/xfa_specific/bug_1069789.evt
new file mode 100644
index 0000000..768f6a9
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1069789.evt
@@ -0,0 +1 @@
+mousedown,right,200,200
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1069789.in b/testing/resources/javascript/xfa_specific/bug_1069789.in
new file mode 100644
index 0000000..2cb09db
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1069789.in
@@ -0,0 +1,51 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform name="form1">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <subform h="10.5in" w="8in" name="subform2">
+      <field h="10mm" name="DropDownList1" w="10mm" x="0mm" y="20mm">
+        <ui>
+          <choiceList/>
+        </ui>
+        <items>
+          <text>Single</text>
+        </items>
+      </field>
+      <field h="200mm" name="DropDownList2" w="200mm" x="0mm" y="30mm">
+        <ui>
+          <textEdit/>
+        </ui>
+        <event activity="enter">
+          <script contentType="application/x-javascript">
+            f1 = xfa.resolveNode("xfa.form..DropDownList1");
+            xfa.host.setFocus(f1);
+            xfa.template.remerge();
+            xfa.host.openList(f1);
+          </script>
+        </event>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1082597.in b/testing/resources/javascript/xfa_specific/bug_1082597.in
new file mode 100644
index 0000000..91a0ef0
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1082597.in
@@ -0,0 +1,150 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streanlen}}
+>>
+stream
+<template>
+ <subform layout="tb" name="my_doc">
+  <pageSet id="page" relation="orderedOccurrence">
+   <occur initial="1" max="4" min="1"/>
+   <pageArea id="Page1" name="Page1">
+    <occur max="1" min="1"/>
+    <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+    <medium long="297mm" short="210mm" stock="a4"/>
+   </pageArea>
+   <pageArea id="Page2" name="Page2">
+    <occur max="1" min="1"/>
+    <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+    <medium long="297mm" short="210mm" stock="a4"/>
+   </pageArea>
+  </pageSet>
+  <event activity="docReady" ref="$host">
+   <script contentType="application/x-javascript">
+    xfa.event.emit();
+    var f=xfa.resolveNode("xfa.form.my_doc.page1_form.combox");
+    xfa.record.nodes.append(this);
+    xfa.form.remerge();
+    xfa.host.setFocus(f);
+    app.alert('Done');
+   </script>
+  </event>
+  <subform layout="tb" name="subform_push_button_1">
+   <occur initial="1" max="10" min="0" name="occur_subform_push_button_1"/>
+   <field h="10mm" name="push_button" w="40mm" x="30mm" y="500mm">
+    <ui>
+     <button highlight="push"/>
+    </ui>
+    <caption>
+     <value>
+      <text>Button</text>
+     </value>
+    </caption>
+    <items>
+     <text name="down">Down Text</text>
+     <text name="rollover">Rollover Text</text>
+    </items>
+   </field>
+  </subform>
+  <subform layout="tb" name="subform_checkbutton_group_0">
+   <occur initial="1" max="10" min="0" name="occur_subform_checkbutton_group_0"/>
+   <exclGroup layout="tb" name="checkbutton_group">
+    <field h="10mm" name="checkbutton_check1" w="40mm" x="30mm" y="600mm">
+     <ui>
+      <checkButton shape="round">
+       <border>
+        <edge/>
+       </border>
+      </checkButton>
+     </ui>
+     <items>
+      <integer>1</integer>
+     </items>
+     <value>
+      <text>Select 1</text>
+     </value>
+     <caption placement="left">
+      <value>
+       <text>Option 1</text>
+      </value>
+     </caption>
+     <event activity="ready" ref="$layout">
+      <script contentType="application/x-javascript">
+      </script>
+     </event>
+    </field>
+    <field h="10mm" name="checkbutton_check2" w="40mm" x="30mm" y="600mm">
+     <ui>
+      <checkButton shape="round">
+       <border>
+        <edge/>
+       </border>
+      </checkButton>
+     </ui>
+     <items>
+      <integer>2</integer>
+     </items>
+     <value>
+      <text>Select 2</text>
+     </value>
+     <caption placement="left">
+      <value>
+       <text>Option 2</text>
+      </value>
+     </caption>
+    </field>
+   </exclGroup>
+  </subform>
+  <subform layout="tb" name="page2_form">
+   <occur initial="1" max="3" min="0"/>
+   <field h="10mm" name="textedit" w="40mm" x="20mm" y="20mm">
+    <ui>
+     <textEdit/>
+    </ui>
+    <value>
+     <text>CLGT.</text>
+    </value>
+   </field>
+  </subform>
+  <subform layout="tb" name="page1_form">
+   <occur initial="1" max="3" min="0"/>
+   <field h="10mm" name="combox" w="40mm" x="10mm" y="10mm">
+    <ui>
+     <choiceList open="onEntry">
+      <border>
+       <edge/>
+      </border>
+     </choiceList>
+    </ui>
+    <items save="1">
+     <text>apples</text>
+     <text>bananas</text>
+     <text>pears</text>
+    </items>
+    <value>
+     <text>
+      apples
+     </text>
+    </value>
+    <event activity="enter" name="event__enter">
+     <script contentType="application/x-javascript">
+      xfa.host.setFocus(0);
+      xfa.host.openList(this);
+     </script>
+    </event>
+   </field>
+  </subform>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1082597_expected.txt b/testing/resources/javascript/xfa_specific/bug_1082597_expected.txt
new file mode 100644
index 0000000..3cb772d
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1082597_expected.txt
@@ -0,0 +1,3 @@
+Alert: Done
+Alert: Done
+Alert: Done
diff --git a/testing/resources/javascript/xfa_specific/bug_1109108.evt b/testing/resources/javascript/xfa_specific/bug_1109108.evt
new file mode 100644
index 0000000..7d58927
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1109108.evt
@@ -0,0 +1 @@
+keycode,9
diff --git a/testing/resources/javascript/xfa_specific/bug_1109108.in b/testing/resources/javascript/xfa_specific/bug_1109108.in
new file mode 100644
index 0000000..550112e
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1109108.in
@@ -0,0 +1,64 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform name="s1">
+    <subform name="s5">
+      <field name="field_28">
+        <calculate>
+          <script contentType="application/x-javascript">
+            var f=xfa.resolveNode("xfa.form.s1.s5.s6.field_35");
+            f.relevant ="print";
+          </script>
+        </calculate>
+        <ui>
+          <textEdit/>
+        </ui>
+        <value>
+          <text>CLGT.</text>
+        </value>
+      </field>
+      <subform name="s6">
+        <field name="field_35">
+          <ui>
+            <choiceList open="onEntry">
+              <border>
+                <edge/>
+              </border>
+            </choiceList>
+          </ui>
+          <items save="1">
+            <text>apples</text>
+            <text>bananas</text>
+            <text>pears</text>
+          </items>
+          <value>
+            <text>apples</text>
+          </value>
+          <event activity="change">
+            <script contentType="application/x-javascript">
+              xfa.template.remerge();
+              xfa.host.openList(this);
+              app.alert('remerged');
+            </script>
+          </event>
+        </field>
+      </subform>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1109108_expected.txt b/testing/resources/javascript/xfa_specific/bug_1109108_expected.txt
new file mode 100644
index 0000000..86c4845
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1109108_expected.txt
@@ -0,0 +1,2 @@
+Alert: remerged
+Alert: remerged
diff --git a/testing/resources/javascript/xfa_specific/bug_1137668.evt b/testing/resources/javascript/xfa_specific/bug_1137668.evt
new file mode 100644
index 0000000..d4b67d8
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1137668.evt
@@ -0,0 +1,9 @@
+keycode,9,shift
+mousedown,left,50,50
+keycode,9
+mousemove,50,50
+mousedown,left,50,50
+keycode,9
+mousemove,20,20
+mousedown,left,20,20
+mousedown,right,20,20
diff --git a/testing/resources/javascript/xfa_specific/bug_1137668.in b/testing/resources/javascript/xfa_specific/bug_1137668.in
new file mode 100644
index 0000000..11014e7
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1137668.in
@@ -0,0 +1,338 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform name="s1">
+    <subform name="s5">
+      <subform name="s6">
+        <event activity="enter">
+          <script contentType="application/x-javascript">
+            xfa.template.remerge();
+            xfa.form.remerge();
+          </script>
+        </event>
+        <field name="field_31">
+          <ui>
+            <button highlight="push"/>
+          </ui>
+          <caption>
+            <value>
+              <text>
+                Button
+              </text>
+            </value>
+          </caption>
+          <items>
+            <text name="down">
+              Down Text
+            </text>
+            <text name="rollover">
+              Rollover Text
+            </text>
+          </items>
+        </field>
+        <field name="field_32">
+          <ui>
+            <barcode/>
+          </ui>
+          <value>
+            <text>
+              test
+            </text>
+          </value>
+        </field>
+        <field name="field_33">
+          <ui>
+            <checkButton shape="round">
+              <border>
+                <edge/>
+              </border>
+            </checkButton>
+          </ui>
+          <items>
+            <integer>
+              2
+            </integer>
+          </items>
+          <value>
+            <text>
+              Select 2
+            </text>
+          </value>
+          <caption placement="left">
+            <value>
+              <text>
+                Option 2
+              </text>
+            </value>
+          </caption>
+        </field>
+        <field name="field_34">
+          <ui>
+            <textEdit/>
+          </ui>
+          <value>
+            <text>
+              CLGT.
+            </text>
+          </value>
+          <event activity="ready">
+            <script contentType="application/x-javascript">
+            </script>
+          </event>
+          <event activity="enter">
+            <script contentType="application/x-javascript">
+            </script>
+          </event>
+          <event activity="initialize">
+            <script contentType="application/x-javascript">
+            </script>
+          </event>
+        </field>
+        <field name="field_35">
+          <validate>
+            <script contentType="application/x-javascript">
+            </script>
+          </validate>
+          <calculate>
+            <script contentType="application/x-javascript">
+            </script>
+          </calculate>
+          <ui>
+            <choiceList open="onEntry">
+              <border><edge/></border>
+            </choiceList>
+          </ui>
+          <items save="1">
+            <text>apples</text>
+            <text>bananas</text>
+            <text>pears</text>
+          </items>
+          <value>
+            <text>apples</text>
+          </value>
+          <event activity="docClose">
+            <script contentType="application/x-javascript">
+            </script>
+          </event>
+          <event activity="initialize">
+            <script contentType="application/x-javascript">
+            </script>
+          </event>
+          <event activity="change">
+            <script contentType="application/x-javascript">
+            </script>
+          </event>
+        </field>
+        <subform name="s7">
+          <field name="field_36">
+            <ui>
+              <dateTimeEdit>
+                <comb numberOfCells="8"/>
+                <border hand="right">
+                  <edge/>
+                </border>
+              </dateTimeEdit>
+            </ui>
+          </field>
+          <field name="field_37">
+            <validate>
+              <script contentType="application/x-javascript">
+              </script>
+            </validate>
+            <calculate>
+              <script contentType="application/x-javascript">
+              </script>
+            </calculate>
+            <ui>
+              <button highlight="push"/>
+            </ui>
+            <caption>
+              <value>
+                <text>
+                  Button
+                </text>
+              </value>
+            </caption>
+            <items>
+              <text name="down">
+                Down Text
+              </text>
+              <text name="rollover">
+                Rollover Text
+              </text>
+            </items>
+            <event activity="docClose">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="change">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="exit">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+          </field>
+          <field name="field_38">
+            <validate>
+              <script contentType="application/x-javascript">
+              </script>
+            </validate>
+            <calculate>
+              <script contentType="application/x-javascript">
+              </script>
+            </calculate>
+            <ui>
+              <barcode/>
+            </ui>
+            <value>
+              <text>
+                test
+              </text>
+            </value>
+            <event activity="initialize">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="change">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="ready">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+          </field>
+          <field name="field_39">
+            <validate>
+              <script contentType="application/x-javascript">
+              </script>
+            </validate>
+            <calculate>
+              <script contentType="application/x-javascript">
+              </script>
+            </calculate>
+            <ui>
+              <checkButton shape="round">
+                <border>
+                  <edge/>
+                </border>
+              </checkButton>
+            </ui>
+            <items>
+              <integer>
+                2
+              </integer>
+            </items>
+            <value>
+              <text>
+                Select 2
+              </text>
+            </value>
+            <caption placement="left">
+              <value>
+                <text>
+                  Option 2
+                </text>
+              </value>
+            </caption>
+            <event activity="enter">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="ready">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="change">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+          </field>
+          <field name="field_40">
+            <validate>
+              <script contentType="application/x-javascript">
+              </script>
+            </validate>
+            <calculate>
+              <script contentType="application/x-javascript">
+              </script>
+            </calculate>
+            <ui>
+              <textEdit/>
+            </ui>
+            <value>
+              <text>
+                CLGT.
+              </text>
+            </value>
+            <event activity="ready">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="docClose">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="exit">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+          </field>
+          <field name="field_41">
+            <validate>
+              <script contentType="application/x-javascript">
+              </script>
+            </validate>
+            <calculate>
+              <script contentType="application/x-javascript">
+              </script>
+            </calculate>
+            <ui>
+              <choiceList open="onEntry">
+                <border><edge/></border>
+              </choiceList>
+            </ui>
+            <items save="1">
+              <text>apples</text>
+              <text>bananas</text>
+              <text>pears</text>
+            </items>
+            <value>
+              <text>apples</text>
+            </value>
+            <event activity="docClose">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="initialize">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="initialize">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+          </field>
+        </subform>
+      </subform>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1164158.in b/testing/resources/javascript/xfa_specific/bug_1164158.in
new file mode 100644
index 0000000..28b7eff
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1164158.in
@@ -0,0 +1,37 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform>
+    <pageSet relation="simplexPaginated">
+      <pageArea pagePosition="last">
+        <subform>
+          <subform layout="table">
+            <subform layout="row">
+              <field />
+              <field colSpan="4294967295" presence="inactive" />
+            </subform>
+          </subform>
+        </subform>
+      </pageArea>
+      <pageArea>
+        <contentArea />
+      </pageArea>
+    </pageSet>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1248901.in b/testing/resources/javascript/xfa_specific/bug_1248901.in
new file mode 100644
index 0000000..8e2638a
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1248901.in
@@ -0,0 +1,47 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <subform h="10.5in" w="8in" name="subform2">
+      <field h="9.0001mm" name="field1" w="47.625mm" x="6.35mm" y="92.075mm">
+        <ui>
+          <choiceList/>
+        </ui>
+      </field>
+    </subform>
+    <event activity="docReady" ref="$host">
+      <script contentType="application/x-javascript">
+        try {
+          var form1 = xfa.resolveNode("xfa.form.form1");
+          var tmp = xfa.resolveNode("xfa.form..field1");
+          tmp.nodes.append(this);
+        } catch (e) {
+          app.alert("PASS: Caught: " + e);
+        }
+     </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1248901_expected.txt b/testing/resources/javascript/xfa_specific/bug_1248901_expected.txt
new file mode 100644
index 0000000..abf828c
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1248901_expected.txt
@@ -0,0 +1 @@
+Alert: PASS: Caught: XFAObject.append: Operation would create a cycle.
diff --git a/testing/resources/javascript/xfa_specific/bug_1312736.in b/testing/resources/javascript/xfa_specific/bug_1312736.in
new file mode 100644
index 0000000..f388810
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1312736.in
@@ -0,0 +1,40 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{object 3 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+<template x="">
+  <subform>
+    <pageSet>
+      <pageArea>
+        <contentArea/>
+        <exclGroup name="0">
+          <field>
+            <ui><checkButton/></ui>
+            <items><textEdit/></items>
+          </field>
+        </exclGroup>
+        <subform name="Sho0">
+          <event activity="initialize">
+            <script contentType="application/x-javascript">
+              Sho0.presence=0;
+              app.alert("done");
+            </script>
+          </event>
+        </subform>
+      </pageArea>
+    </pageSet>
+  </subform>
+</template>
+endstream
+endobj
+{{object 8 0} <<
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1312736_expected.txt b/testing/resources/javascript/xfa_specific/bug_1312736_expected.txt
new file mode 100644
index 0000000..daa1eca
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1312736_expected.txt
@@ -0,0 +1 @@
+Alert: done
diff --git a/testing/resources/javascript/xfa_specific/bug_1444238.evt b/testing/resources/javascript/xfa_specific/bug_1444238.evt
new file mode 100644
index 0000000..adca35a
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1444238.evt
@@ -0,0 +1,3 @@
+mousedown,left,91,539
+mouseup,left,91,539
+charcode,32
diff --git a/testing/resources/javascript/xfa_specific/bug_1444238.in b/testing/resources/javascript/xfa_specific/bug_1444238.in
new file mode 100644
index 0000000..675178c
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1444238.in
@@ -0,0 +1,149 @@
+{{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 2
+  /Kids [
+    32 0 R
+    34 0 R
+  ]
+>>
+endobj
+% Forms
+{{object 4 0}} <<
+  /XFA 43 0 R
+  /Fields [
+    10 0 R
+    11 0 R
+  ]
+>>
+endobj
+% Fields
+{{object 10 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /T (MyField5)
+  /V (myfield_5)
+  /Rect [0 500 600 600]
+>>
+% Fields
+{{object 11 0}} <<
+  /T (MyField3)
+  /Parent 4 0 R
+  /Kids [12 0 R]
+  /Opt [(a) (b) (c) (d)]
+  /V [(a) (b) (c)]
+>>
+endobj
+% Fields
+{{object 12 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 131072
+  /Parent 11 0 R
+  /Kids [13 0 R]
+>>
+endobj
+% Fields
+{{object 13 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /Parent 12 0 R
+  /Rect [0 400 600 600]
+>>
+endobj
+% Fields
+{{object 14 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /Parent 12 0 R
+  /Rect [100 400 500 500]
+>>
+endobj
+% Page number 2.
+{{object 32 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /Annots [13 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
+% JS program to exexute
+{{object 41 0}} <<
+>>
+stream
+var f5 = this.getField("MyField5");
+var f3 = this.getField("MyField3");
+f3.setFocus();
+this.__defineGetter__("pageNum",function o(){f5.setFocus(); f3.borderStyle="dashed"; f3.setFocus();});
+endstream
+endobj
+{{object 43 0}} <<
+  {{streamlen}}
+>>
+stream
+<?xml version="1.0" encoding="UTF-8"?>
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+<config></config>
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.8/">
+  <subform layout="tb" locale="en_US">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="268.939mm" w="203.2mm" x="6.35mm" y="6.35mm"/>
+        <medium long="792pt" short="612pt" stock="default"/>
+      </pageArea>
+    </pageSet>
+    <field h="9.0001mm" name="MyField3" w="47.625mm" x="120mm" y="120mm">
+      <ui>
+        <choiceList open="onEntry">
+          <border>
+            <edge/>
+          </border>
+        </choiceList>
+      </ui>
+      <items save="1">
+        <text>apples</text>
+        <text>bananas</text>
+        <text>pears</text>
+      </items>
+      <value>
+        <text>apples</text>
+      </value>
+      <event activity="preOpen">
+        <script contentType="application/x-javascript">
+            var aa = this.pageNum;
+        </script>
+      </event>
+    </field>
+  </subform>
+</template>
+</xdp:xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_980116.evt b/testing/resources/javascript/xfa_specific/bug_980116.evt
new file mode 100644
index 0000000..a5025fa
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_980116.evt
@@ -0,0 +1,4 @@
+mousemove,50,50
+mousedown,left,50,50
+mouseup,left,50,50
+keycode,9
diff --git a/testing/resources/javascript/xfa_specific/bug_980116.in b/testing/resources/javascript/xfa_specific/bug_980116.in
new file mode 100644
index 0000000..11bcbf2
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_980116.in
@@ -0,0 +1,58 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <subform h="10.5in" w="8in">
+      <field h="3mm" name="DropDownList1" w="3mm" x="0mm" y="1mm">
+        <ui>
+          <choiceList/>
+        </ui>
+        <items save="1">
+          <text>Single</text>
+          <text>Married</text>
+          <text>Other</text>
+        </items>
+      </field>
+      <field h="500.0001mm" name="DropDownList2" w="500.625mm" x="0mm" y="0mm">
+        <ui>
+          <textEdit></textEdit>
+        </ui>
+        <value>
+          <text>Employee</text>
+        </value>
+      </field>
+    </subform>
+    <traversal>
+        <traverse operation="first" ref="$xfa.(eval('xfa.host.setFocus(field_DropDownList1); xfa.template.remerge(); xfa.host.openList(field_DropDownList1);') == 0)"/>
+    </traversal>
+    <event activity="docReady">
+      <script contentType="application/x-javascript">
+        field_DropDownList1 = xfa.resolveNode("xfa.form..DropDownList1");
+        field_DropDownList2 = xfa.resolveNode("xfa.form..DropDownList2");
+        xfa.host.setFocus(field_DropDownList2);
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_980161.evt b/testing/resources/javascript/xfa_specific/bug_980161.evt
new file mode 100644
index 0000000..a5025fa
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_980161.evt
@@ -0,0 +1,4 @@
+mousemove,50,50
+mousedown,left,50,50
+mouseup,left,50,50
+keycode,9
diff --git a/testing/resources/javascript/xfa_specific/bug_980161.in b/testing/resources/javascript/xfa_specific/bug_980161.in
new file mode 100644
index 0000000..058444d
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_980161.in
@@ -0,0 +1,55 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <subform h="10.5in" w="8in">
+      <field h="3mm" name="DropDownList1" w="3mm" x="0mm" y="1mm">
+        <ui>
+          <choiceList/>
+        </ui>
+        <items save="1">
+          <text>Single</text>
+          <text>Married</text>
+          <text>Other</text>
+        </items>
+      </field>
+      <field h="500.0001mm" name="DropDownList2" w="500.625mm" x="0mm" y="0mm">
+        <ui>
+          <textEdit></textEdit>
+        </ui>
+        <traversal>
+          <traverse operation="next" ref="$xfa.(eval('xfa.host.setFocus(field_DropDownList1); xfa.template.remerge(); xfa.host.openList(field_DropDownList1);') == 0)"/>
+        </traversal>
+      </field>
+    </subform>
+    <event activity="docReady">
+      <script contentType="application/x-javascript">
+        field_DropDownList1 = xfa.resolveNode("xfa.form..DropDownList1");
+        field_DropDownList2 = xfa.resolveNode("xfa.form..DropDownList2");
+        xfa.host.setFocus(field_DropDownList2);
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/cross_engine_apply.in b/testing/resources/javascript/xfa_specific/cross_engine_apply.in
index cdaad2a..3a41708 100644
--- a/testing/resources/javascript/xfa_specific/cross_engine_apply.in
+++ b/testing/resources/javascript/xfa_specific/cross_engine_apply.in
@@ -46,6 +46,7 @@
 endobj
 {{include ../../xfa_locale_6_0.fragment}}
 {{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
 {{xref}}
 {{trailer}}
 {{startxref}}
diff --git a/testing/resources/javascript/xfa_specific/dump_tree.js b/testing/resources/javascript/xfa_specific/dump_tree.js
new file mode 100644
index 0000000..171c72a
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/dump_tree.js
@@ -0,0 +1,22 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Note: Be sure that this file is only included inside a CDATA block,
+// otherwise the less-than comparision below will break the XML parse.
+
+function dumpTree(node, level) {
+  level = level || 0;
+  var indentation = "| ".repeat(level);
+  try {
+    app.alert(indentation + node.className);
+    var children = node.nodes;
+    if (children) {
+      for (var i = 0; i < children.length; ++i) {
+        dumpTree(children.item(i), level + 1);
+      }
+    }
+  } catch (e) {
+    app.alert(indentation + "Error: " + e);
+  }
+}
diff --git a/testing/resources/javascript/xfa_specific/instance_manager.in b/testing/resources/javascript/xfa_specific/instance_manager.in
new file mode 100644
index 0000000..fe3d776
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/instance_manager.in
@@ -0,0 +1,99 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <subform h="10.5in" w="8in" name="subform2">
+      <field h="9.0001mm" name="field1" w="47.625mm" x="6.35mm" y="92.075mm">
+        <ui>
+          <choiceList/>
+        </ui>
+        <items save="1">
+          <text>Single</text>
+          <text>Married</text>
+          <text>Other</text>
+        </items>
+      </field>
+      <field name="field3" h="10.625mm" w="30.625mm" x="5mm" y="50mm">
+      </field>
+      <subform name="field4" x="5mm" y="5mm">
+        <occur max="-1"/>
+        <field name="field5" w="64.77mm" h="6.35mm">
+        </field>
+      </subform>
+    </subform>
+    <event activity="docReady">
+      <script contentType="application/x-javascript"><![CDATA[
+        {{include ../expect.js}}
+        {{include ../property_test_helpers.js}}
+        {{include dump_tree.js}}
+
+        var mgr = xfa.resolveNode("xfa.form..field4").instanceManager;
+        dumpTree(mgr);
+
+        testRWProperty(mgr, "count", 1, 12);
+        testROProperty(mgr, "min", 1);
+        testROProperty(mgr, "max", -1);
+
+        expectError("mgr.setInstances()");
+        expectError("mgr.setInstances(-10)");
+        expectError("mgr.setInstances('clams')");
+        expectError("mgr.setInstances([1, 2, 3])");
+        // setInstances(10000000) will hang or hit OOM.
+        expect("mgr.setInstances(4)", undefined);
+        expect("mgr.count", 4);
+        expect("mgr.setInstances(2)", undefined);
+        expect("mgr.count", 2);
+
+        expectError("mgr.moveInstance()");
+        expectError("mgr.moveInstance(0)");
+        expectError("mgr.moveInstance('clams')");
+        expectError("mgr.moveInstance([1, 2, 3])");
+        expect("mgr.moveInstance(0, 1)", undefined);
+        expect("mgr.count", 2);
+
+        expectError("mgr.addInstance(1, 2, 3)");
+        expect("mgr.addInstance().className", "subform");
+        expect("mgr.addInstance(true).className", "subform");
+        expect("mgr.count", 4);
+
+        expectError("mgr.insertInstance()");
+        expectError("mgr.insertInstance(1, 2, 3)");
+        expect("mgr.insertInstance(1, true).className", "subform");
+        expect("mgr.count", 5);
+
+        expectError("mgr.removeInstance()");
+        expectError("mgr.removeInstance(1, 2)");
+        expect("mgr.removeInstance(0)", undefined);
+        expect("mgr.removeInstance(0)", undefined);
+        expect("mgr.removeInstance(0)", undefined);
+        expect("mgr.removeInstance(0)", undefined);
+        expect("mgr.count", 1);
+
+        expectError("mgr.removeInstance(0)");
+        expect("mgr.count", 1);
+      ]]></script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/instance_manager_expected.txt b/testing/resources/javascript/xfa_specific/instance_manager_expected.txt
new file mode 100644
index 0000000..d39657f
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/instance_manager_expected.txt
@@ -0,0 +1,39 @@
+Alert: instanceManager
+Alert: | occur
+Alert: PASS: count = 1
+Alert: PASS: count = 12
+Alert: PASS: min = 1
+Alert: PASS: min threw Error: Invalid property set operation.
+Alert: PASS: max = -1
+Alert: PASS: max threw Error: Invalid property set operation.
+Alert: PASS: mgr.setInstances() threw XFAObject.setInstances: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.setInstances(-10) threw Error: The element [min] has violated its allowable number of occurrences.
+Alert: PASS: mgr.setInstances('clams') threw Error: The element [min] has violated its allowable number of occurrences.
+Alert: PASS: mgr.setInstances([1, 2, 3]) threw Error: The element [min] has violated its allowable number of occurrences.
+Alert: PASS: mgr.setInstances(4) = undefined
+Alert: PASS: mgr.count = 4
+Alert: PASS: mgr.setInstances(2) = undefined
+Alert: PASS: mgr.count = 2
+Alert: PASS: mgr.moveInstance() threw XFAObject.moveInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.moveInstance(0) threw XFAObject.moveInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.moveInstance('clams') threw XFAObject.moveInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.moveInstance([1, 2, 3]) threw XFAObject.moveInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.moveInstance(0, 1) = undefined
+Alert: PASS: mgr.count = 2
+Alert: PASS: mgr.addInstance(1, 2, 3) threw XFAObject.addInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.addInstance().className = subform
+Alert: PASS: mgr.addInstance(true).className = subform
+Alert: PASS: mgr.count = 4
+Alert: PASS: mgr.insertInstance() threw XFAObject.insertInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.insertInstance(1, 2, 3) threw XFAObject.insertInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.insertInstance(1, true).className = subform
+Alert: PASS: mgr.count = 5
+Alert: PASS: mgr.removeInstance() threw XFAObject.removeInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.removeInstance(1, 2) threw XFAObject.removeInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.removeInstance(0) = undefined
+Alert: PASS: mgr.removeInstance(0) = undefined
+Alert: PASS: mgr.removeInstance(0) = undefined
+Alert: PASS: mgr.removeInstance(0) = undefined
+Alert: PASS: mgr.count = 1
+Alert: PASS: mgr.removeInstance(0) threw XFAObject.removeInstance: Too many occurrences.
+Alert: PASS: mgr.count = 1
diff --git a/testing/resources/javascript/xfa_specific/mixed_widgets.evt b/testing/resources/javascript/xfa_specific/mixed_widgets.evt
new file mode 100644
index 0000000..f4ee07d
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/mixed_widgets.evt
@@ -0,0 +1,5 @@
+mousemove,302,302
+mousedown,left,302,302
+mouseup,left,302,302
+charcode,65
+charcode,66
diff --git a/testing/resources/javascript/xfa_specific/mixed_widgets.in b/testing/resources/javascript/xfa_specific/mixed_widgets.in
new file mode 100644
index 0000000..2974112
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/mixed_widgets.in
@@ -0,0 +1,48 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /NeedsRendering true
+  /AcroForm <<
+    /Fields [6 0 R]
+    /XFA 5 0 R
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 512 512]
+  /Annots [6 0 R]
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp xmlns="http://ns.adobe.com/xdp/">
+  <template>
+    <subform x="0pt" y="0pt" w="198pt" h="198pt">
+      <field name="MyBox"/>
+    </subform>
+  </template>
+</xdp>
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /Rect [200 200 512 512]
+  /T (MyBox)
+>>
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/popup_menu.evt b/testing/resources/javascript/xfa_specific/popup_menu.evt
new file mode 100644
index 0000000..7820426
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/popup_menu.evt
@@ -0,0 +1,5 @@
+mousemove,20,20
+mousedown,left,20,20
+mouseup,left,20,20
+mousedown,right,20,20
+mouseup,right,20,20
diff --git a/testing/resources/javascript/xfa_specific/popup_menu.in b/testing/resources/javascript/xfa_specific/popup_menu.in
new file mode 100644
index 0000000..52f7a45
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/popup_menu.in
@@ -0,0 +1,40 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="ImageField1" y="0pt" x="0pt" w="425pt" h="80pt">
+        <ui>
+          <choiceList open="userControl"/>
+        </ui>
+        <items save="1">
+          <text>clams</text>
+          <text>oysters</text>
+          <text>crabs</text>
+        </items>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/popup_menu_expected.txt b/testing/resources/javascript/xfa_specific/popup_menu_expected.txt
new file mode 100644
index 0000000..dc856e9
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/popup_menu_expected.txt
@@ -0,0 +1 @@
+Popup: x=20.0, y=20.0, flags=0x0
diff --git a/testing/resources/javascript/xfa_specific/xfa_exclgroup.in b/testing/resources/javascript/xfa_specific/xfa_exclgroup.in
new file mode 100644
index 0000000..1a29352
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_exclgroup.in
@@ -0,0 +1,86 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb">
+    <exclGroup name="RadioButtonList" layout="lr-tb">
+      <field w="30mm" h="6mm" name="yes">
+        <ui>
+          <checkButton shape="round">
+          </checkButton>
+        </ui>
+        <items>
+          <text>Agree</text>
+        </items>
+      </field>
+      <field w="30mm" h="6mm" name="no">
+        <ui>
+          <checkButton shape="round">
+          </checkButton>
+        </ui>
+        <items>
+          <text>Disagree</text>
+        </items>
+      </field>
+      <validate nullTest="error"/>
+    </exclGroup>
+    <event activity="initialize">
+      <script contentType="application/x-javascript"><![CDATA[
+        {{include ../expect.js}}
+        expect("RadioButtonList.className", "exclGroup");
+
+        expectError("RadioButtonList.execEvent()");
+        expectError("RadioButtonList.execEvent(1, 2)");
+        expect("RadioButtonList.execEvent('nonesuch')", null);
+        expect("RadioButtonList.execEvent('calculate')", null);
+        expect("RadioButtonList.execEvent('change')", null);
+        expect("RadioButtonList.execEvent('click')", null);
+        expect("RadioButtonList.execEvent('enter')", null);
+        expect("RadioButtonList.execEvent('exit')", null);
+        expect("RadioButtonList.execEvent('full')", null);
+        expect("RadioButtonList.execEvent('indexChange')", null);
+        expect("RadioButtonList.execEvent('initialize')", null);
+        expect("RadioButtonList.execEvent('mouseDown')", null);
+        expect("RadioButtonList.execEvent('mouseEnter')", null);
+        expect("RadioButtonList.execEvent('mouseExit')", null);
+        expect("RadioButtonList.execEvent('mouseUp')", null);
+        expect("RadioButtonList.execEvent('postOpen')", null);
+        expect("RadioButtonList.execEvent('preOpen')", null);
+        expect("RadioButtonList.execEvent('preSign')", null);
+        expect("RadioButtonList.execEvent('validate')", null);
+
+        expectError("RadioButtonList.execInitialize('badarg')");
+        expect("RadioButtonList.execInitialize()", null);
+
+        expectError("RadioButtonList.execCalculate('badarg')");
+        expect("RadioButtonList.execCalculate()", null);
+
+        expectError("RadioButtonList.execValidate('badarg')");
+        expect("RadioButtonList.execValidate()", true);
+
+        expectError("RadioButtonList.selectedMember('badarg')");
+        expect("RadioButtonList.selectedMember()", null);
+
+        expect("RadioButtonList.defaultValue",  undefined);
+        expect("RadioButtonList.rawValue", null);
+        expect("RadioButtonList.transient", undefined);
+        expect("RadioButtonList.errorText", undefined);
+      ]]></script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_exclgroup_expected.txt b/testing/resources/javascript/xfa_specific/xfa_exclgroup_expected.txt
new file mode 100644
index 0000000..7ca5a78
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_exclgroup_expected.txt
@@ -0,0 +1,32 @@
+Alert: PASS: RadioButtonList.className = exclGroup
+Alert: PASS: RadioButtonList.execEvent() threw XFAObject.execEvent: Incorrect number of parameters passed to function.
+Alert: PASS: RadioButtonList.execEvent(1, 2) threw XFAObject.execEvent: Incorrect number of parameters passed to function.
+Alert: PASS: RadioButtonList.execEvent('nonesuch') = undefined
+Alert: PASS: RadioButtonList.execEvent('calculate') = undefined
+Alert: PASS: RadioButtonList.execEvent('change') = undefined
+Alert: PASS: RadioButtonList.execEvent('click') = undefined
+Alert: PASS: RadioButtonList.execEvent('enter') = undefined
+Alert: PASS: RadioButtonList.execEvent('exit') = undefined
+Alert: PASS: RadioButtonList.execEvent('full') = undefined
+Alert: PASS: RadioButtonList.execEvent('indexChange') = undefined
+Alert: PASS: RadioButtonList.execEvent('initialize') = undefined
+Alert: PASS: RadioButtonList.execEvent('mouseDown') = undefined
+Alert: PASS: RadioButtonList.execEvent('mouseEnter') = undefined
+Alert: PASS: RadioButtonList.execEvent('mouseExit') = undefined
+Alert: PASS: RadioButtonList.execEvent('mouseUp') = undefined
+Alert: PASS: RadioButtonList.execEvent('postOpen') = undefined
+Alert: PASS: RadioButtonList.execEvent('preOpen') = undefined
+Alert: PASS: RadioButtonList.execEvent('preSign') = undefined
+Alert: PASS: RadioButtonList.execEvent('validate') = undefined
+Alert: PASS: RadioButtonList.execInitialize('badarg') threw XFAObject.execInitialize: Incorrect number of parameters passed to function.
+Alert: PASS: RadioButtonList.execInitialize() = undefined
+Alert: PASS: RadioButtonList.execCalculate('badarg') threw XFAObject.execCalculate: Incorrect number of parameters passed to function.
+Alert: PASS: RadioButtonList.execCalculate() = undefined
+Alert: PASS: RadioButtonList.execValidate('badarg') threw XFAObject.execValidate: Incorrect number of parameters passed to function.
+Alert: PASS: RadioButtonList.execValidate() = true
+Alert: PASS: RadioButtonList.selectedMember('badarg') threw XFAObject.selectedMember: Incorrect number of parameters passed to function.
+Alert: PASS: RadioButtonList.selectedMember() = null
+Alert: PASS: RadioButtonList.defaultValue = undefined
+Alert: PASS: RadioButtonList.rawValue = null
+Alert: PASS: RadioButtonList.transient = undefined
+Alert: PASS: RadioButtonList.errorText = undefined
diff --git a/testing/resources/javascript/xfa_specific/xfa_field.in b/testing/resources/javascript/xfa_specific/xfa_field.in
new file mode 100644
index 0000000..a358921
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_field.in
@@ -0,0 +1,92 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="subform1">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+        <medium long="297mm" short="210mm" stock="a4"/>
+      </pageArea>
+    </pageSet>
+    <subform layout="tb" name="subform2">
+      <occur initial="1" max="10" min="0" name="occur1">
+      </occur>
+      <field name="field1" h="10mm"  w="40mm" x="10mm" y="12mm" border="solid">
+        <items>
+          <text>and a one</text>
+          <text>and a two</text>
+        </items>
+        <event activity="ready" ref="$form">
+          <script contentType="application/x-javascript"><![CDATA[
+            {{include ../expect.js}}
+            {{include ../property_test_helpers.js}}
+            var field = xfa.resolveNode("field1");
+            testRWProperty(field, "x", "10mm", "11mm");
+            testRWProperty(field, "y", "12mm", "13mm");
+            testRWProperty(field, "h", "10mm", "2in");
+            testRWProperty(field, "w", "40mm", "3in");
+            testRWProperty(field, "fontColor", "0,0,0", "42,62,4");
+            testRWProperty(field, "fillColor", "255,255,255", "41,61,11");
+            testRWProperty(field, "borderColor", "0,0,0", "241,161,11");
+            // TODO(tsepez): find a way to make this be defined.
+            // testRWProperty(field, "borderWidth", "1", "4");
+            testRWProperty(field, "mandatory", "disabled", "solid");
+            testRWProperty(field, "mandatoryMessage", "", "keep out");
+            testROProperty(field, "dataNode", "[object XFAObject]");
+            testROProperty(field, "length", 2);
+
+            expectError("field.execInitialize('phooey')");
+            expect("field.execInitialize()", undefined);
+
+            expectError("field.execEvent()");
+            expectError("field.execEvent(1, 2)");
+            expect("field.execEvent('validate')", true);
+
+            expectError("field.deleteItem()");
+            expectError("field.deleteItem(1, 2)");
+            expect("field.deleteItem(1)", true);
+            expect("field.deleteItem(137)", true);  // silently ignored?
+
+            expectError("field.getSaveItem()");
+            expectError("field.getSaveItem(1, 2)");
+            expect("field.getSaveItem(0)", "and a one");
+            expect("field.getSaveItem(137)", null);
+
+            expectError("field.getItemState()");
+            expectError("field.getItemState(1, 2)");
+            expect("field.getItemState(0)", false);
+            expect("field.getItemState(1)", false);
+            expect("field.getItemState(137)", false);
+            expect("field.getItemState(-137)", false);
+
+            expectError("field.setItemState()");
+            expectError("field.setItemState(1, 2, 3)");
+            expect("field.setItemState(0, 1)", undefined);
+            expect("field.getItemState(0)", true);
+            expect("field.setItemState(0, 0)", undefined);
+            expect("field.getItemState(0)", false);
+
+          ]]></script>
+        </event>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_field_expected.txt b/testing/resources/javascript/xfa_specific/xfa_field_expected.txt
new file mode 100644
index 0000000..645518a
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_field_expected.txt
@@ -0,0 +1,47 @@
+Alert: PASS: x = 10mm
+Alert: PASS: x = 11mm
+Alert: PASS: y = 12mm
+Alert: PASS: y = 13mm
+Alert: PASS: h = 10mm
+Alert: PASS: h = 2in
+Alert: PASS: w = 40mm
+Alert: PASS: w = 3in
+Alert: PASS: fontColor = 0,0,0
+Alert: PASS: fontColor = 42,62,4
+Alert: PASS: fillColor = 255,255,255
+Alert: PASS: fillColor = 41,61,11
+Alert: PASS: borderColor = 0,0,0
+Alert: PASS: borderColor = 241,161,11
+Alert: PASS: mandatory = disabled
+Alert: PASS: mandatory = solid
+Alert: PASS: mandatoryMessage = 
+Alert: PASS: mandatoryMessage = keep out
+Alert: PASS: dataNode = [object XFAObject]
+Alert: PASS: dataNode threw Error: Invalid property set operation.
+Alert: PASS: length = 2
+Alert: PASS: length threw Error: Invalid property set operation.
+Alert: PASS: field.execInitialize('phooey') threw XFAObject.execInitialize: Incorrect number of parameters passed to function.
+Alert: PASS: field.execInitialize() = undefined
+Alert: PASS: field.execEvent() threw XFAObject.execEvent: Incorrect number of parameters passed to function.
+Alert: PASS: field.execEvent(1, 2) threw XFAObject.execEvent: Incorrect number of parameters passed to function.
+Alert: PASS: field.execEvent('validate') = true
+Alert: PASS: field.deleteItem() threw XFAObject.deleteItem: Incorrect number of parameters passed to function.
+Alert: PASS: field.deleteItem(1, 2) threw XFAObject.deleteItem: Incorrect number of parameters passed to function.
+Alert: PASS: field.deleteItem(1) = true
+Alert: PASS: field.deleteItem(137) = true
+Alert: PASS: field.getSaveItem() threw XFAObject.getSaveItem: Incorrect number of parameters passed to function.
+Alert: PASS: field.getSaveItem(1, 2) threw XFAObject.getSaveItem: Incorrect number of parameters passed to function.
+Alert: PASS: field.getSaveItem(0) = and a one
+Alert: PASS: field.getSaveItem(137) = null
+Alert: PASS: field.getItemState() threw XFAObject.getItemState: Incorrect number of parameters passed to function.
+Alert: PASS: field.getItemState(1, 2) threw XFAObject.getItemState: Incorrect number of parameters passed to function.
+Alert: PASS: field.getItemState(0) = false
+Alert: PASS: field.getItemState(1) = false
+Alert: PASS: field.getItemState(137) = false
+Alert: PASS: field.getItemState(-137) = false
+Alert: PASS: field.setItemState() threw XFAObject.setItemState: Incorrect number of parameters passed to function.
+Alert: PASS: field.setItemState(1, 2, 3) threw XFAObject.setItemState: Incorrect number of parameters passed to function.
+Alert: PASS: field.setItemState(0, 1) = undefined
+Alert: PASS: field.getItemState(0) = true
+Alert: PASS: field.setItemState(0, 0) = undefined
+Alert: PASS: field.getItemState(0) = false
diff --git a/testing/resources/javascript/xfa_specific/xfa_form.in b/testing/resources/javascript/xfa_specific/xfa_form.in
new file mode 100644
index 0000000..e21688f
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_form.in
@@ -0,0 +1,61 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<form>
+</form>
+<template>
+  <subform layout="tb" name="my_subform">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+      <pageArea id="Page2" name="Page2">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+    </pageSet>
+    <event activity="docReady" ref="$host">
+      <script contentType="application/x-javascript">
+        {{include ../expect.js}}
+        {{include ../property_test_helpers.js}}
+        var my_form = xfa.resolveNode("#form");
+        testRWProperty(my_form, "checksum", "", "11");
+        expect("typeof my_form.execInitialize", "function");
+        expect("typeof my_form.execCalculate", "function");
+        expect("typeof my_form.execValidate", "function");
+        expectError("my_form.execInitialize('nonesuch')");
+        expect("my_form.execInitialize()", undefined);
+        expectError("my_form.execCalculate('nonesuch')");
+        expect("my_form.execCalculate()", undefined);
+        expectError("my_form.execValidate('nonesuch')");
+        expect("my_form.execValidate()", true);
+        expectError("my_form.formNodes()");
+        expectError("my_form.formNodes(1, 2)");
+        expectError("my_form.formNodes('nonesuch')");
+        expect("my_form.formNodes(my_subform)", "[object XFAObject]");
+        expectError("my_form.remerge(1)");
+        expect("my_form.remerge()", true);
+        expectError("my_form.recalculate()");
+        expectError("my_form.recalculate(1, 2)");
+        expect("my_form.recalculate(0)", undefined);
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_form_expected.txt b/testing/resources/javascript/xfa_specific/xfa_form_expected.txt
new file mode 100644
index 0000000..191d702
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_form_expected.txt
@@ -0,0 +1,20 @@
+Alert: PASS: checksum = 
+Alert: PASS: checksum = 11
+Alert: PASS: typeof my_form.execInitialize = function
+Alert: PASS: typeof my_form.execCalculate = function
+Alert: PASS: typeof my_form.execValidate = function
+Alert: PASS: my_form.execInitialize('nonesuch') threw XFAObject.execInitialize: Incorrect number of parameters passed to function.
+Alert: PASS: my_form.execInitialize() = undefined
+Alert: PASS: my_form.execCalculate('nonesuch') threw XFAObject.execCalculate: Incorrect number of parameters passed to function.
+Alert: PASS: my_form.execCalculate() = undefined
+Alert: PASS: my_form.execValidate('nonesuch') threw XFAObject.execValidate: Incorrect number of parameters passed to function.
+Alert: PASS: my_form.execValidate() = true
+Alert: PASS: my_form.formNodes() threw XFAObject.formNodes: Incorrect number of parameters passed to function.
+Alert: PASS: my_form.formNodes(1, 2) threw XFAObject.formNodes: Incorrect number of parameters passed to function.
+Alert: PASS: my_form.formNodes('nonesuch') threw XFAObject.formNodes: Incorrect parameter value.
+Alert: PASS: my_form.formNodes(my_subform) = [object XFAObject]
+Alert: PASS: my_form.remerge(1) threw XFAObject.remerge: Incorrect number of parameters passed to function.
+Alert: FAIL: my_form.remerge() = undefined, expected true 
+Alert: PASS: my_form.recalculate() threw XFAObject.recalculate: Incorrect number of parameters passed to function.
+Alert: PASS: my_form.recalculate(1, 2) threw XFAObject.recalculate: Incorrect number of parameters passed to function.
+Alert: PASS: my_form.recalculate(0) = undefined
diff --git a/testing/resources/javascript/xfa_specific/xfa_globalobject.in b/testing/resources/javascript/xfa_specific/xfa_globalobject.in
new file mode 100644
index 0000000..be1f3be
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_globalobject.in
@@ -0,0 +1,41 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="my_doc">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+      <pageArea id="Page2" name="Page2">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+    </pageSet>
+    <event activity="docReady" ref="$host">
+      <script contentType="application/x-javascript">
+        app.alert('We search non-xfa global space on missing prop: ' + (app == xfa.app));
+        app.alert('Global toString says ' + toString());
+        app.alert('toString.apply to bad object is ' + toString.apply(xfa));
+        app.alert('toString.apply to bad non-xfa object is ' + toString.apply(app));
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_globalobject_expected.txt b/testing/resources/javascript/xfa_specific/xfa_globalobject_expected.txt
new file mode 100644
index 0000000..6384afe
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_globalobject_expected.txt
@@ -0,0 +1,4 @@
+Alert: We search non-xfa global space on missing prop: true
+Alert: Global toString says [object Root]
+Alert: toString.apply to bad object is [object Root]
+Alert: toString.apply to bad non-xfa object is [object Root]
diff --git a/testing/resources/javascript/xfa_specific/xfa_host_pseudomodel_expected.txt b/testing/resources/javascript/xfa_specific/xfa_host_pseudomodel_expected.txt
index 3835e4a..6c1359d 100644
--- a/testing/resources/javascript/xfa_specific/xfa_host_pseudomodel_expected.txt
+++ b/testing/resources/javascript/xfa_specific/xfa_host_pseudomodel_expected.txt
@@ -52,7 +52,7 @@
 Alert: PASS: xfa.host.pageDown('ignored arg') = undefined
 Alert: PASS: xfa.host.print(1, 2, 3, 4, 5, 6, 7) threw XFAObject.print: Incorrect number of parameters passed to function.
 Alert: PASS: xfa.host.print(1, 2, 3, 4, 5, 6, 7, 8, 9) threw XFAObject.print: Incorrect number of parameters passed to function.
-Doc Print: 1, 1, 1, 2, 4, 8, 16, 32
+Doc Print: 1, 1, 1, 1, 1, 1, 1, 1
 Alert: PASS: xfa.host.print(true, 1, 1, true, true, true, true, true) = undefined
 Alert: PASS: xfa.host.response() threw XFAObject.response: Incorrect number of parameters passed to function.
 Alert: PASS: xfa.host.response(1, 2, 3, 4, 5) threw XFAObject.response: Incorrect number of parameters passed to function.
diff --git a/testing/resources/javascript/xfa_specific/xfa_items.in b/testing/resources/javascript/xfa_specific/xfa_items.in
new file mode 100644
index 0000000..f1ee317
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_items.in
@@ -0,0 +1,196 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform name="form1">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <field h="9.0001mm" name="field1" w="47.625mm" x="6.35mm" y="92.075mm">
+      <ui>
+        <choiceList/>
+      </ui>
+      <items nonesuch="3">
+        <arc name="arc1"></arc>
+        <boolean name="bool0">0</boolean>
+        <boolean name="bool1">1</boolean>
+        <boolean name="boolbad">bad</boolean>
+        <boolean name="booltruenottrue">true</boolean>
+        <date name="date0"></date>
+        <date name="date1">2020-02-02</date>
+        <date name="date2">2039-12-01</date>
+        <date name="datebad">bad</date>
+        <dateTime name="datetime0"></dateTime>
+        <dateTime name="datetime1">2020-02-02T12:34:56</dateTime>
+        <dateTime name="datetime2">2039-12-01T12:34:56</dateTime>
+        <dateTime name="datetimebad">bad</dateTime>
+        <decimal name="decimal0"></decimal>
+        <decimal name="decimal1">42.0000000000000000001</decimal>
+        <decimal name="decimalbad">bad</decimal>
+        <exData name="ex0"></exData>
+        <exData name="ex1"><![CDATA[YZYZYZYZYZYZYZYZYZYZYZYZYZYZ]]></exData>
+        <float name="float0">-12.34</float>
+        <float name="float1">-12.34</float>
+        <float name="floatbad">bad</float>
+        <image name="image0">ABABABABABABABABA</image>
+        <image name="image1"><![CDATA[ABABABABABABABABA]]></image>
+        <integer name="int0"></integer>
+        <integer name="int1">1234</integer>
+        <integer name="intbad">bad</integer>
+        <line name="line0"></line>
+        <rectangle name="rect0"></rectangle>
+        <text name="text0"></text>
+        <text name="text1">Ahoy !!!</text>
+        <time name="time0"></time>
+        <time name="time1">12:34:56</time>
+        <goop name="goop0">Nonsense nodes not allowed here</goop>
+      </items>
+    </field>
+    <event activity="docReady">
+      <script contentType="application/x-javascript"><![CDATA[
+        {{include ../expect.js}}
+        {{include dump_tree.js}}
+
+        itemlist = xfa.resolveNode("form1.field1.#items");
+        dumpTree(itemlist);
+
+        arc1 = itemlist.resolveNode("arc1");
+
+        bool0 = itemlist.resolveNode("bool0");
+        expect("bool0.value", false);
+        bool0.value = 1;
+        expect("bool0.value", true);
+
+        bool1 = itemlist.resolveNode("bool1");
+        expect("bool1.value", true);
+        bool1.value = 0;
+        expect("bool1.value", false);
+
+        boolbad = itemlist.resolveNode("boolbad");
+        expect("boolbad.value", false);
+
+        booltruenottrue = itemlist.resolveNode("booltruenottrue");
+        expect("booltruenottrue.value", false);
+
+        // TODO(tsepez): confirm if this is correct.
+        booltruenottrue.value = true;
+        expect("booltruenottrue.value", false);
+
+        booltruenottrue.value = "zerp";
+        expect("booltruenottrue.value", false);
+        booltruenottrue.value = "1";
+        expect("booltruenottrue.value", true);
+        booltruenottrue.value = "10";
+        expect("booltruenottrue.value", true);
+        booltruenottrue.value = "1zerp";
+        expect("booltruenottrue.value", true);
+
+        // Date is just a node, and allows any text within.
+        date0 = itemlist.resolveNode("date0");
+        expect("date0.value", null);
+
+        date1 = itemlist.resolveNode("date1");
+        expect("date1.value", "2020-02-02");
+
+        date2 = itemlist.resolveNode("date2");
+        expect("date2.value", "2039-12-01");
+
+        datebad = itemlist.resolveNode("datebad");
+        expect("datebad.value", "bad");
+
+        // These are pretty much just nodes, and allow any text within.
+        // Just check that they parsed and that we can retrieve them.
+        datetime0 = itemlist.resolveNode("datetime0");
+        expect("datetime0", "[object XFAObject]");
+
+        datetime1 = itemlist.resolveNode("datetime1");
+        expect("datetime1", "[object XFAObject]");
+
+        datetime2 = itemlist.resolveNode("datetime2");
+        expect("datetime2", "[object XFAObject]");
+
+        datetimebad = itemlist.resolveNode("datetimebad");
+        expect("datetimebad", "[object XFAObject]");
+
+        decimal0 = itemlist.resolveNode("decimal0");
+        expect("decimal0", "[object XFAObject]");
+
+        decimal1 = itemlist.resolveNode("decimal1");
+        expect("decimal1", "[object XFAObject]");
+
+        decimalbad = itemlist.resolveNode("decimalbad");
+        expect("decimalbad", "[object XFAObject]");
+
+        ex0 = itemlist.resolveNode("ex0");
+        expect("ex0", "[object XFAObject]");
+
+        ex1 = itemlist.resolveNode("ex1");
+        expect("ex1", "[object XFAObject]");
+
+        float0 = itemlist.resolveNode("float0");
+        expect("float0", "[object XFAObject]");
+
+        float1 = itemlist.resolveNode("float1");
+        expect("float1", "[object XFAObject]");
+
+        floatbad = itemlist.resolveNode("floatbad");
+        expect("floatbad", "[object XFAObject]");
+
+        image0 = itemlist.resolveNode("image0");
+        expect("image0", "[object XFAObject]");
+
+        image1 = itemlist.resolveNode("image1");
+        expect("image1", "[object XFAObject]");
+
+        int0 = itemlist.resolveNode("int0");
+        expect("int0", "[object XFAObject]");
+
+        int1 = itemlist.resolveNode("int1");
+        expect("int1", "[object XFAObject]");
+
+        intbad = itemlist.resolveNode("intbad");
+        expect("intbad", "[object XFAObject]");
+
+        line0 = itemlist.resolveNode("line0");
+        expect("line0", "[object XFAObject]");
+
+        rect0 = itemlist.resolveNode("rect0");
+        expect("rect0", "[object XFAObject]");
+
+        text0 = itemlist.resolveNode("text0");
+        expect("text0", "[object XFAObject]");
+
+        text1 = itemlist.resolveNode("text1");
+        expect("text1", "[object XFAObject]");
+
+        time0 = itemlist.resolveNode("time0");
+        expect("time0", "[object XFAObject]");
+
+        time1 = itemlist.resolveNode("time1");
+        expect("time1", "[object XFAObject]");
+
+        // The parser is picky and won't let fake nodes in here.
+        goop0 = itemlist.resolveNode("goop0");
+        expect("goop0", null);
+      ]]></script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_items_expected.txt b/testing/resources/javascript/xfa_specific/xfa_items_expected.txt
new file mode 100644
index 0000000..cc0c7c5
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_items_expected.txt
@@ -0,0 +1,80 @@
+Alert: items
+Alert: | arc
+Alert: | boolean
+Alert: | boolean
+Alert: | boolean
+Alert: | boolean
+Alert: | date
+Alert: | date
+Alert: | | #text
+Alert: | date
+Alert: | | #text
+Alert: | date
+Alert: | | #text
+Alert: | dateTime
+Alert: | dateTime
+Alert: | | #text
+Alert: | dateTime
+Alert: | | #text
+Alert: | dateTime
+Alert: | | #text
+Alert: | decimal
+Alert: | decimal
+Alert: | decimal
+Alert: | exData
+Alert: | exData
+Alert: | float
+Alert: | float
+Alert: | float
+Alert: | image
+Alert: | | #text
+Alert: | image
+Alert: | | #text
+Alert: | integer
+Alert: | integer
+Alert: | integer
+Alert: | line
+Alert: | rectangle
+Alert: | text
+Alert: | text
+Alert: | time
+Alert: | time
+Alert: PASS: bool0.value = false
+Alert: PASS: bool0.value = true
+Alert: PASS: bool1.value = true
+Alert: PASS: bool1.value = false
+Alert: PASS: boolbad.value = false
+Alert: PASS: booltruenottrue.value = false
+Alert: PASS: booltruenottrue.value = false
+Alert: PASS: booltruenottrue.value = false
+Alert: PASS: booltruenottrue.value = true
+Alert: PASS: booltruenottrue.value = true
+Alert: PASS: booltruenottrue.value = true
+Alert: PASS: date0.value = null
+Alert: PASS: date1.value = 2020-02-02
+Alert: PASS: date2.value = 2039-12-01
+Alert: PASS: datebad.value = bad
+Alert: PASS: datetime0 = [object XFAObject]
+Alert: PASS: datetime1 = [object XFAObject]
+Alert: PASS: datetime2 = [object XFAObject]
+Alert: PASS: datetimebad = [object XFAObject]
+Alert: PASS: decimal0 = [object XFAObject]
+Alert: PASS: decimal1 = [object XFAObject]
+Alert: PASS: decimalbad = [object XFAObject]
+Alert: PASS: ex0 = [object XFAObject]
+Alert: PASS: ex1 = [object XFAObject]
+Alert: PASS: float0 = [object XFAObject]
+Alert: PASS: float1 = [object XFAObject]
+Alert: PASS: floatbad = [object XFAObject]
+Alert: PASS: image0 = [object XFAObject]
+Alert: PASS: image1 = [object XFAObject]
+Alert: PASS: int0 = [object XFAObject]
+Alert: PASS: int1 = [object XFAObject]
+Alert: PASS: intbad = [object XFAObject]
+Alert: PASS: line0 = [object XFAObject]
+Alert: PASS: rect0 = [object XFAObject]
+Alert: PASS: text0 = [object XFAObject]
+Alert: PASS: text1 = [object XFAObject]
+Alert: PASS: time0 = [object XFAObject]
+Alert: PASS: time1 = [object XFAObject]
+Alert: PASS: goop0 = null
diff --git a/testing/resources/javascript/xfa_specific/xfa_log_pseudomodel.in b/testing/resources/javascript/xfa_specific/xfa_log_pseudomodel.in
index 2de417e..a3f28fc 100644
--- a/testing/resources/javascript/xfa_specific/xfa_log_pseudomodel.in
+++ b/testing/resources/javascript/xfa_specific/xfa_log_pseudomodel.in
@@ -40,6 +40,7 @@
 endobj
 {{include ../../xfa_locale_6_0.fragment}}
 {{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
 {{xref}}
 {{trailer}}
 {{startxref}}
diff --git a/testing/resources/javascript/xfa_specific/xfa_node.in b/testing/resources/javascript/xfa_specific/xfa_node.in
index 7157993..a0e9b25 100644
--- a/testing/resources/javascript/xfa_specific/xfa_node.in
+++ b/testing/resources/javascript/xfa_specific/xfa_node.in
@@ -7,6 +7,7 @@
   {{streamlen}}
 >>
 stream
+<goop viscosity="sludge">Spurious Nonsense</goop>
 <template>
   <subform layout="tb" name="my_doc">
     <pageSet id="page" relation="orderedOccurrence">
@@ -21,9 +22,13 @@
       </pageArea>
     </pageSet>
     <event activity="docReady" ref="$host">
-      <script contentType="application/x-javascript">
+      <script contentType="application/x-javascript"><![CDATA[
         {{include ../expect.js}}
         {{include ../property_test_helpers.js}}
+        {{include dump_tree.js}}
+
+        dumpTree(xfa);
+
         testROProperty(my_doc, "isContainer", true);
         testROProperty(my_doc, "isNull", false);
         testROProperty(my_doc, "model", "[object XFAObject]");
@@ -74,7 +79,35 @@
         // Test free-form attributes outside of the XFA schema.
         expect("my_doc.setAttribute('fake_value', 'fake_attr')", undefined);
         expect("my_doc.getAttribute('fake_attr')", 'fake_value');
-      </script>
+
+        // Test magic "xfa" property bound to all nodes.
+        expect("my_doc.xfa == xfa", true);
+
+        // Test "packet" nodes which are unrecognized tags at XDP level.
+        expect("goop", undefined);
+        var goop = this.resolveNode('#packet');
+        expect("goop.className", "packet");
+        expect("goop.viscosity", undefined);
+        expectError("goop.getAttribute()");
+        expectError("goop.getAttribute(1, 2)");
+        expect("goop.getAttribute('nonesuch')", "");
+        expect("goop.getAttribute('viscosity')", "sludge");
+        expectError("goop.setAttribute()");
+        expectError("goop.setAttribute(1)");
+        expectError("goop.setAttribute(1, 2, 3)");
+
+        // Remember, args to setAttribute() are backwards from what one might
+        // typically expect ...
+        expect("goop.setAttribute(7, 'ph')", undefined);
+        expect("goop.getAttribute('ph')", 7);
+
+        expect("goop.content", "Spurious Nonsense");
+        expect("goop.content = ' for All'", " for All");
+
+        // TODO(tsepez): assigning new content seems to append although it
+        // wasn't included in the return value of the assignment above.
+        expect("goop.content", "Spurious Nonsense for All");
+      ]]></script>
     </event>
   </subform>
 </template>
diff --git a/testing/resources/javascript/xfa_specific/xfa_node_expected.txt b/testing/resources/javascript/xfa_specific/xfa_node_expected.txt
index 68b73a9..8455004 100644
--- a/testing/resources/javascript/xfa_specific/xfa_node_expected.txt
+++ b/testing/resources/javascript/xfa_specific/xfa_node_expected.txt
@@ -1,3 +1,200 @@
+Alert: xfa
+Alert: | config
+Alert: | | agent
+Alert: | | | destination
+Alert: | | | | #text
+Alert: | | | pdf
+Alert: | | | | fontInfo
+Alert: | | present
+Alert: | | | pdf
+Alert: | | | | version
+Alert: | | | | | #text
+Alert: | | | | adobeExtensionLevel
+Alert: | | | | | #text
+Alert: | | | | renderPolicy
+Alert: | | | | | #text
+Alert: | | | | scriptModel
+Alert: | | | | | #text
+Alert: | | | | interactive
+Alert: | | | | | #text
+Alert: | | | xdp
+Alert: | | | | packets
+Alert: | | | | | #text
+Alert: | | | destination
+Alert: | | | | #text
+Alert: | | | script
+Alert: | | | | #text
+Alert: | | acrobat
+Alert: | | | acrobat7
+Alert: | | | | dynamicRender
+Alert: | | | | | #text
+Alert: | | | validate
+Alert: | | | | #text
+Alert: | packet
+Alert: | template
+Alert: | | subform
+Alert: | | | event
+Alert: | | | | script
+Alert: | | | | | #text
+Alert: | localeSet
+Alert: | | locale
+Alert: | | | calendarSymbols
+Alert: | | | | monthNames
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | monthNames
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | dayNames
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | dayNames
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | meridiemNames
+Alert: | | | | | meridiem
+Alert: | | | | | | #text
+Alert: | | | | | meridiem
+Alert: | | | | | | #text
+Alert: | | | | eraNames
+Alert: | | | | | era
+Alert: | | | | | | #text
+Alert: | | | | | era
+Alert: | | | | | | #text
+Alert: | | | datePatterns
+Alert: | | | | datePattern
+Alert: | | | | | #text
+Alert: | | | | datePattern
+Alert: | | | | | #text
+Alert: | | | | datePattern
+Alert: | | | | | #text
+Alert: | | | | datePattern
+Alert: | | | | | #text
+Alert: | | | timePatterns
+Alert: | | | | timePattern
+Alert: | | | | | #text
+Alert: | | | | timePattern
+Alert: | | | | | #text
+Alert: | | | | timePattern
+Alert: | | | | | #text
+Alert: | | | | timePattern
+Alert: | | | | | #text
+Alert: | | | dateTimeSymbols
+Alert: | | | | #text
+Alert: | | | numberPatterns
+Alert: | | | | numberPattern
+Alert: | | | | | #text
+Alert: | | | | numberPattern
+Alert: | | | | | #text
+Alert: | | | | numberPattern
+Alert: | | | | | #text
+Alert: | | | numberSymbols
+Alert: | | | | numberSymbol
+Alert: | | | | | #text
+Alert: | | | | numberSymbol
+Alert: | | | | | #text
+Alert: | | | | numberSymbol
+Alert: | | | | | #text
+Alert: | | | | numberSymbol
+Alert: | | | | | #text
+Alert: | | | | numberSymbol
+Alert: | | | | | #text
+Alert: | | | currencySymbols
+Alert: | | | | currencySymbol
+Alert: | | | | | #text
+Alert: | | | | currencySymbol
+Alert: | | | | | #text
+Alert: | | | | currencySymbol
+Alert: | | | | | #text
+Alert: | | | typefaces
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | dataModel
+Alert: | | dataGroup
+Alert: | | | dataGroup
+Alert: | form
+Alert: | | subform
+Alert: | | | event
+Alert: | | | | script
+Alert: | | | | | #text
 Alert: PASS: isContainer = true
 Alert: PASS: isContainer threw Error: Invalid property set operation.
 Alert: PASS: isNull = false
@@ -49,3 +246,19 @@
 Alert: PASS: my_doc.getAttribute('ns') = something
 Alert: PASS: my_doc.setAttribute('fake_value', 'fake_attr') = undefined
 Alert: PASS: my_doc.getAttribute('fake_attr') = fake_value
+Alert: PASS: my_doc.xfa == xfa = true
+Alert: PASS: goop = undefined
+Alert: PASS: goop.className = packet
+Alert: PASS: goop.viscosity = undefined
+Alert: PASS: goop.getAttribute() threw XFAObject.getAttribute: Incorrect number of parameters passed to function.
+Alert: PASS: goop.getAttribute(1, 2) threw XFAObject.getAttribute: Incorrect number of parameters passed to function.
+Alert: PASS: goop.getAttribute('nonesuch') = 
+Alert: PASS: goop.getAttribute('viscosity') = sludge
+Alert: PASS: goop.setAttribute() threw XFAObject.setAttribute: Incorrect number of parameters passed to function.
+Alert: PASS: goop.setAttribute(1) threw XFAObject.setAttribute: Incorrect number of parameters passed to function.
+Alert: PASS: goop.setAttribute(1, 2, 3) threw XFAObject.setAttribute: Incorrect number of parameters passed to function.
+Alert: PASS: goop.setAttribute(7, 'ph') = null
+Alert: PASS: goop.getAttribute('ph') = 7
+Alert: PASS: goop.content = Spurious Nonsense
+Alert: PASS: goop.content = ' for All' =  for All
+Alert: PASS: goop.content = Spurious Nonsense for All
diff --git a/testing/resources/javascript/xfa_specific/xfa_template.in b/testing/resources/javascript/xfa_specific/xfa_template.in
new file mode 100644
index 0000000..60ad46d
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_template.in
@@ -0,0 +1,60 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="my_doc">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+      <pageArea id="Page2" name="Page2">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+    </pageSet>
+    <event activity="docReady" ref="$host">
+      <script contentType="application/x-javascript"><![CDATA[
+        {{include ../expect.js}}
+        template0 = xfa.resolveNode("#template");
+        expect("template0.className", "template");
+
+        expectError("template0.execCalculate(1)");
+        expect("template0.execCalculate()", false);
+
+        expectError("template0.execInitialize(1)", false);
+        expect("template0.execInitialize()", false);
+
+        expectError("template0.execValidate(1)", false);
+        expect("template0.execValidate()", false);
+
+        expectError("template0.formNodes()", undefined);
+        expectError("template0.formNodes(1, 2)", undefined);
+        expect("template0.formNodes(true)", true);
+
+        expectError("template0.recalculate()", undefined);
+        expectError("template0.recalculate(1, 2)", undefined);
+        expect("template0.recalculate(true)", true);
+
+        expectError("template0.remerge(1)", undefined);
+        expect("template0.remerge()", undefined);
+      ]]></script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_template_expected.txt b/testing/resources/javascript/xfa_specific/xfa_template_expected.txt
new file mode 100644
index 0000000..9bb1ffd
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_template_expected.txt
@@ -0,0 +1,15 @@
+Alert: PASS: template0.className = template
+Alert: PASS: template0.execCalculate(1) threw XFAObject.execCalculate: Incorrect number of parameters passed to function.
+Alert: PASS: template0.execCalculate() = false
+Alert: PASS: template0.execInitialize(1) threw XFAObject.execInitialize: Incorrect number of parameters passed to function.
+Alert: PASS: template0.execInitialize() = false
+Alert: PASS: template0.execValidate(1) threw XFAObject.execValidate: Incorrect number of parameters passed to function.
+Alert: PASS: template0.execValidate() = false
+Alert: PASS: template0.formNodes() threw XFAObject.formNodes: Incorrect number of parameters passed to function.
+Alert: PASS: template0.formNodes(1, 2) threw XFAObject.formNodes: Incorrect number of parameters passed to function.
+Alert: PASS: template0.formNodes(true) = true
+Alert: PASS: template0.recalculate() threw XFAObject.recalculate: Incorrect number of parameters passed to function.
+Alert: PASS: template0.recalculate(1, 2) threw XFAObject.recalculate: Incorrect number of parameters passed to function.
+Alert: PASS: template0.recalculate(true) = true
+Alert: PASS: template0.remerge(1) threw XFAObject.remerge: Incorrect number of parameters passed to function.
+Alert: PASS: template0.remerge() = undefined
diff --git a/testing/resources/javascript/xfa_specific/xfa_variables.in b/testing/resources/javascript/xfa_specific/xfa_variables.in
new file mode 100644
index 0000000..6bfdb97
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_variables.in
@@ -0,0 +1,76 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="my_doc">
+    <variables>
+      <text name="xx01">123</text>
+      <text name="xx02">456</text>
+      <integer name="xx03">123</integer>
+      <integer name="xx04">456</integer>
+    </variables>
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+      <pageArea id="Page2" name="Page2">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+    </pageSet>
+    <event activity="docReady" ref="$host">
+      <script name="my_script" contentType="application/x-javascript"><![CDATA[
+        {{include ../expect.js}}
+        try {
+          var script1 = xfa.resolveNode('template..my_script');
+          var script2 = xfa.resolveNode('template..their_script');
+          var script3 = xfa.resolveNode('template..other_script');
+          app.alert('First, poke at a script node itsef');
+          expect('script1.stateless', '0');
+          expectError('script1.stateless = 42');
+          app.alert('We search variables context ' + (xx01.value + xx02.value));
+          app.alert('We search variables context ' + (xx03.value + xx04.value));
+          app.alert('We resolve off of script1 ' + (script1.xx01.value + script1.xx02.value));
+          app.alert('We resolve off of script2 ' + (script2.xx01.value + script2.xx02.value));
+          app.alert('We resolve off of script3 ' + (script3.xx01.value + script3.xx02.value));
+          app.alert('We resolve off of script1 ' + script1.nonesuch);
+          app.alert('We resolve off of script2 ' + script2.nonesuch);
+          app.alert('We resolve off of script3 ' + script3.nonesuch);
+        } catch (e) {
+          app.alert('Error: ' + e);
+        }
+      ]]></script>
+    </event>
+  </subform>
+  <subform layout="tb" name="their_doc">
+    <variables>
+      <text name="xx01">78</text>
+      <text name="xx02">90</text>
+      <integer name="xx03">78</integer>
+      <integer name="xx04">90</integer>
+      <script name="other_script">
+        var xx01 = "chips";
+      </script>
+    </variables>
+    <script name="their_script">
+      var xx01 = "clams";
+    </script>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_variables_expected.txt b/testing/resources/javascript/xfa_specific/xfa_variables_expected.txt
new file mode 100644
index 0000000..75a457f
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_variables_expected.txt
@@ -0,0 +1,11 @@
+Alert: First, poke at a script node itsef
+Alert: PASS: script1.stateless = 0
+Alert: PASS: script1.stateless = 42 threw Error: Invalid property set operation.
+Alert: We search variables context 123456
+Alert: We search variables context 579
+Alert: We resolve off of script1 123456
+Alert: We resolve off of script2 7890
+Alert: We resolve off of script3 7890
+Alert: We resolve off of script1 undefined
+Alert: We resolve off of script2 undefined
+Alert: We resolve off of script3 undefined
diff --git a/testing/resources/jpx_lzw.in b/testing/resources/jpx_lzw.in
new file mode 100644
index 0000000..46914c1
--- /dev/null
+++ b/testing/resources/jpx_lzw.in
@@ -0,0 +1,55 @@
+{{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 <<
+      /Im0 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+612 0 0 792 0 0 cm
+/Im0 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Filter [/ASCIIHexDecode /LZWDecode /JPXDecode]
+  /Width 612
+  /Height 792
+  {{streamlen}}
+>>
+stream
+80002040c351404020068290e0a8100028663a1e4e06a380c8410d004522d0d16c68d10d0b1a4d06
+439408061881008c8000181c0f180003cc66f361c80330084358d1a31bfc9eff288005f189549a51
+30a24ae5947a44b00e0100d3ea3507f948000c86d400a040200002ff2e0002640201209050b35a2c
+f69b4bfe5625af10ce465309d0ca6410188f220279c0ca6e251408a47101d8ca72399a4de6e100c8
+5c33170c1fe9000432303faf3fd26cf5a14022502a2bdfe7f369a0b480980c12097d1801be801c05
+0005c57bf818daa6ee5fed9801
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/jpx_lzw.pdf b/testing/resources/jpx_lzw.pdf
new file mode 100644
index 0000000..8735018
--- /dev/null
+++ b/testing/resources/jpx_lzw.pdf
@@ -0,0 +1,67 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+  /Resources <<
+    /XObject <<
+      /Im0 5 0 R
+    >>
+  >>
+>>
+endobj
+4 0 obj <<
+  /Length 31
+>>
+stream
+q
+612 0 0 792 0 0 cm
+/Im0 Do
+Q
+endstream
+endobj
+5 0 obj <<
+  /Type /XObject
+  /Subtype /Image
+  /Filter [/ASCIIHexDecode /LZWDecode /JPXDecode]
+  /Width 612
+  /Height 792
+  /Length 432
+>>
+stream
+80002040c351404020068290e0a8100028663a1e4e06a380c8410d004522d0d16c68d10d0b1a4d06
+439408061881008c8000181c0f180003cc66f361c80330084358d1a31bfc9eff288005f189549a51
+30a24ae5947a44b00e0100d3ea3507f948000c86d400a040200002ff2e0002640201209050b35a2c
+f69b4bfe5625af10ce465309d0ca6410188f220279c0ca6e251408a47101d8ca72399a4de6e100c8
+5c33170c1fe9000432303faf3fd26cf5a14022502a2bdfe7f369a0b480980c12097d1801be801c05
+0005c57bf818daa6ee5fed9801
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000287 00000 n 
+0000000369 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+965
+%%EOF
diff --git a/testing/resources/js.pdf b/testing/resources/js.pdf
index 07e9b75..8d64866 100644
--- a/testing/resources/js.pdf
+++ b/testing/resources/js.pdf
@@ -22,7 +22,6 @@
 4 0 obj <<
   /Names [
     (normal) 5 0 R
-    (encoded_subtype) 6 0 R
     (no_type) 7 0 R
     (wrongtype) 8 0 R
     (wrongsubtype) 9 0 R
@@ -36,12 +35,6 @@
   /JS 11 0 R
 >>
 endobj
-6 0 obj <<
-  /Type /Action
-  /S /J#61v#61Script
-  /JS 11 0 R
->>
-endobj
 7 0 obj <<
   /S /JavaScript
   /JS 12 0 R
diff --git a/testing/resources/line_annot.in b/testing/resources/line_annot.in
new file mode 100644
index 0000000..0778169
--- /dev/null
+++ b/testing/resources/line_annot.in
@@ -0,0 +1,50 @@
+{{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
+  /MediaBox [0 0 612 792]
+  /Annots [
+    4 0 R 5 0 R
+  ]
+  /Tabs /R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Line
+  /NM (Line-1)
+  /F 4
+  /L [159 296 472 243.42]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+  /Border [0.25 0.5 2]
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Annot
+  /Subtype /Line
+  /NM (Line-2)
+  /F 4
+  /L [159 296 472]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+  /Border [0.25 0.5]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/line_annot.pdf b/testing/resources/line_annot.pdf
new file mode 100644
index 0000000..7a7fee3
--- /dev/null
+++ b/testing/resources/line_annot.pdf
@@ -0,0 +1,62 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /Annots [
+    4 0 R 5 0 R
+  ]
+  /Tabs /R
+>>
+endobj
+4 0 obj <<
+  /Type /Annot
+  /Subtype /Line
+  /NM (Line-1)
+  /F 4
+  /L [159 296 472 243.42]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+  /Border [0.25 0.5 2]
+>>
+endobj
+5 0 obj <<
+  /Type /Annot
+  /Subtype /Line
+  /NM (Line-2)
+  /F 4
+  /L [159 296 472]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+  /Border [0.25 0.5]
+>>
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000251 00000 n 
+0000000431 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+602
+%%EOF
diff --git a/testing/resources/link_annots.in b/testing/resources/link_annots.in
deleted file mode 100644
index 076069c..0000000
--- a/testing/resources/link_annots.in
+++ /dev/null
@@ -1,333 +0,0 @@
-{{header}}
-{{object 1 0}} <<
-  /Type /Catalog
-  /Pages 2 0 R
->>
-endobj
-{{object 2 0}} <<
-  /Type /Pages
-  /Count 2
-  /Kids [3 0 R 4 0 R]
-  /MediaBox [0 0 612 792]
-  /CropBox [0 0 612 792]
-  /Resources <<
-    /Font <<
-      /F1 7 0 R
-      /F2 8 0 R
-    >>
-    /ProcSet [/PDF /Text /ImageC]
-    /ExtGState <<
-      /GS0 23 0 R
-    >>
-  >>
->>
-endobj
-{{object 3 0}} <<
-  /Type /Page
-  /Parent 2 0 R
-  /Contents 5 0 R
-  /Annots [15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R]
->>
-endobj
-{{object 4 0}} <<
-  /Type /Page
-  /Parent 2 0 R
-  /Contents 6 0 R
-  /Annots [15 0 R 16 0 R]
->>
-endobj
-{{object 5 0}} <<
-  {{streamlen}}
->>
-stream
-BT
-70 700 Td
-/F1 18 Tf
-(Link Annotations - Page 1) Tj
-0 -65 Td
-/F2 14 Tf
-(1. Link with destination to first page) Tj
-10 -20 Td
-/F2 14 Tf
-(2. Link with destination to second page) Tj
--12 -84 Td
-/F2 10 Tf
-(PDF Reference, Version 1.7, Section 8.4.5 defines Annotations) Tj
-2 -53 Td
-(3.  An example of Highlight with text notes) Tj
-0 -18 Td
-(https://pdfium.googlesource.com/pdfium is link in plain text, not link annotation. These are referred to) Tj
-0 -17 Td
-(as WebLinks in PDFium.)Tj
-ET
-endstream
-endobj
-{{object 6 0}} <<
-  {{streamlen}}
->>
-stream
-BT
-70 700 Td
-/F1 18 Tf
-(Link Annotations - Page 2) Tj
-0 -65 Td
-/F2 14 Tf
-(1. Link with destination to first page) Tj
-10 -20 Td
-/F2 14 Tf
-(2. Link with destination to second page) Tj
-ET
-endstream
-endobj
-{{object 7 0}} <<
-  /Type /Font
-  /Subtype /Type1
-  /BaseFont /Times-Roman
->>
-endobj
-{{object 8 0}} <<
-  /Type /Font
-  /Subtype /Type1
-  /BaseFont /Helvetica
->>
-endobj
-{{object 9 0}} <<
-  /Type /XObject
-  /Subtype /Form
-  /FormType 1
-  {{streamlen}}
-  /BBox [293 530 349 542]
-  /Resources <<
-    /XObject <<
-      /Form0 10 0 R
-    >>
-    /ExtGState <<
-      /GS0 24 0 R
-    >>
-  >>
->>
-stream
-/GS0 gs
-/Form0 Do
-endstream
-endobj
-{{object 10 0}} <<
-  /Type /XObject
-  /Subtype /Form
-  /FormType 1
-  /Group <<
-    /S /Transparency
-  >>
-  {{streamlen}}
-  /BBox [293 530 349 542]
->>
-stream
-1.0 1.0 0.0 rg
-293 530 m
-349 530 l
-349 542 l
-293 542 l
-h f
-endstream
-endobj
-{{object 11 0}} <<
-  /Type /XObject
-  /Subtype /Form
-  /FormType 1
-  {{streamlen}}
-  /BBox [83 440 178 453]
-  /Resources <<
-    /XObject <<
-      /Form0 12 0 R
-    >>
-    /ExtGState <<
-      /GS0 24 0 R
-    >>
-  >>
->>
-stream
-/GS0 gs
-/Form0 Do
-endstream
-endobj
-{{object 12 0}} <<
-  /Type /XObject
-  /Subtype /Form
-  /FormType 1
-  /Group <<
-    /S /Transparency
-  >>
-  {{streamlen}}
-  /BBox [83 440 178 453]
->>
-stream
-0.0 1.0 1.0 rg
-83 440 m
-178 440 l
-178 453 l
-83 453 l
-h f
-endstream
-endobj
-{{object 13 0}} <<
-  /Type /XObject
-  /Subtype /Form
-  /FormType 1
-  {{streamlen}}
-  /BBox [149 476 191 487]
-  /Resources <<
-    /XObject <<
-      /Form0 14 0 R
-    >>
-    /ExtGState <<
-      /GS0 24 0 R
-    >>
-  >>
->>
-stream
-/GS0 gs
-/Form0 Do
-endstream
-endobj
-{{object 14 0}} <<
-  /Type /XObject
-  /Subtype /Form
-  /FormType 1
-  /Group <<
-    /S /Transparency
-  >>
-  {{streamlen}}
-  /BBox [149 476 191 487]
->>
-stream
-0.0 1.0 0.0 rg
-149 476 m
-191 476 l
-191 487 l
-149 487 l
-h f
-endstream
-endobj
-{{object 15 0}} <<
-  /Type /Annot
-  /Subtype /Link
-  /BS <<
-    /W 0
-  >>
-  /Rect [69 633 542 653]
-  /Dest [3 0 R /XYZ 200 725 0]
-  /F 4
->>
-endobj
-{{object 16 0}} <<
-  /Type /Annot
-  /Subtype /Link
-  /BS <<
-    /W 0
-  >>
-  /Rect [80 613 542 633]
-  /Dest [4 0 R /XYZ 200 725 0]
-  /F 4
->>
-endobj
-{{object 17 0}} <<
-  /Type /Annot
-  /Subtype /Link
-  /BS <<
-    /W 0
-  >>
-  /Rect [66 529 196 544]
-  /A <<
-    /Type /Action
-    /URI (https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf)
-    /S /URI
-  >>
-  /F 4
->>
-endobj
-{{object 18 0}} <<
-  /Type /Annot
-  /Subtype /Link
-  /BS <<
-    /W 0
-  >>
-  /Rect [83 440 178 453]
-  /QuadPoints [83 453 178 453 83 440 178 440]
-  /A <<
-    /Type /Action
-    /URI (https://cs.chromium.org/chromium/src/third_party/pdfium/public/fpdf_text.h)
-    /S /URI
-  >>
-  /F 4
->>
-endobj
-{{object 19 0}} <<
-  /Type /Annot
-  /Subtype /Highlight
-  /AP <<
-    /N 9 0 R
-  >>
-  /NM (Highlight-1)
-  /F 4
-  /QuadPoints [293 542 349 542 293 530 349 530]
-  /P 3 0 R
-  /C [1 0.90196 0]
-  /Rect [293 530 349 542]
->>
-endobj
-{{object 20 0}} <<
-  /Type /Annot
-  /Subtype /Highlight
-  /AP <<
-    /N 11 0 R
-  >>
-  /NM (Highlight-2)
-  /F 4
-  /QuadPoints [83 453 178 453 83 440 178 440]
-  /P 3 0 R
-  /C [0.26667 0.78431 0.96078]
-  /Rect [83 440 178 453]
->>
-endobj
-{{object 21 0}} <<
-  /Type /Annot
-  /Subtype /Popup
-  /Parent 22 0 R
-  /Rect [191 377 443 488]
->>
-endobj
-{{object 22 0}} <<
-  /Type /Annot
-  /Subtype /Highlight
-  /Popup 21 0 R
-  /AP <<
-    /N 13 0 R
-  >>
-  /NM (Highlight-With-Popup-1)
-  /Contents (Text Note)
-  /QuadPoints [149 487 191 487 149 476 191 476]
-  /P 3 0 R
-  /C [0.14902 0.90196 0]
-  /Rect [149 476 191 487]
-  /F 4
->>
-endobj
-{{object 23 0}} <<
-  /ca 1
-  /Type /ExtGState
-  /CA 1
-  /BM /Normal
->>
-endobj
-{{object 24 0}} <<
-  /ca 1
-  /Type /ExtGState
-  /CA 1
-  /AIS false
-  /BM /Multiply
->>
-endobj
-{{xref}}
-{{trailer}}
-{{startxref}}
-%%EOF
diff --git a/testing/resources/link_annots.pdf b/testing/resources/link_annots.pdf
deleted file mode 100644
index b964bf5..0000000
--- a/testing/resources/link_annots.pdf
+++ /dev/null
@@ -1,364 +0,0 @@
-%PDF-1.7
-% ò¤ô
-1 0 obj <<
-  /Type /Catalog
-  /Pages 2 0 R
->>
-endobj
-2 0 obj <<
-  /Type /Pages
-  /Count 2
-  /Kids [3 0 R 4 0 R]
-  /MediaBox [0 0 612 792]
-  /CropBox [0 0 612 792]
-  /Resources <<
-    /Font <<
-      /F1 7 0 R
-      /F2 8 0 R
-    >>
-    /ProcSet [/PDF /Text /ImageC]
-    /ExtGState <<
-      /GS0 23 0 R
-    >>
-  >>
->>
-endobj
-3 0 obj <<
-  /Type /Page
-  /Parent 2 0 R
-  /Contents 5 0 R
-  /Annots [15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R]
->>
-endobj
-4 0 obj <<
-  /Type /Page
-  /Parent 2 0 R
-  /Contents 6 0 R
-  /Annots [15 0 R 16 0 R]
->>
-endobj
-5 0 obj <<
-  /Length 486
->>
-stream
-BT
-70 700 Td
-/F1 18 Tf
-(Link Annotations - Page 1) Tj
-0 -65 Td
-/F2 14 Tf
-(1. Link with destination to first page) Tj
-10 -20 Td
-/F2 14 Tf
-(2. Link with destination to second page) Tj
--12 -84 Td
-/F2 10 Tf
-(PDF Reference, Version 1.7, Section 8.4.5 defines Annotations) Tj
-2 -53 Td
-(3.  An example of Highlight with text notes) Tj
-0 -18 Td
-(https://pdfium.googlesource.com/pdfium is link in plain text, not link annotation. These are referred to) Tj
-0 -17 Td
-(as WebLinks in PDFium.)Tj
-ET
-endstream
-endobj
-6 0 obj <<
-  /Length 185
->>
-stream
-BT
-70 700 Td
-/F1 18 Tf
-(Link Annotations - Page 2) Tj
-0 -65 Td
-/F2 14 Tf
-(1. Link with destination to first page) Tj
-10 -20 Td
-/F2 14 Tf
-(2. Link with destination to second page) Tj
-ET
-endstream
-endobj
-7 0 obj <<
-  /Type /Font
-  /Subtype /Type1
-  /BaseFont /Times-Roman
->>
-endobj
-8 0 obj <<
-  /Type /Font
-  /Subtype /Type1
-  /BaseFont /Helvetica
->>
-endobj
-9 0 obj <<
-  /Type /XObject
-  /Subtype /Form
-  /FormType 1
-  /Length 18
-  /BBox [293 530 349 542]
-  /Resources <<
-    /XObject <<
-      /Form0 10 0 R
-    >>
-    /ExtGState <<
-      /GS0 24 0 R
-    >>
-  >>
->>
-stream
-/GS0 gs
-/Form0 Do
-endstream
-endobj
-10 0 obj <<
-  /Type /XObject
-  /Subtype /Form
-  /FormType 1
-  /Group <<
-    /S /Transparency
-  >>
-  /Length 59
-  /BBox [293 530 349 542]
->>
-stream
-1.0 1.0 0.0 rg
-293 530 m
-349 530 l
-349 542 l
-293 542 l
-h f
-endstream
-endobj
-11 0 obj <<
-  /Type /XObject
-  /Subtype /Form
-  /FormType 1
-  /Length 18
-  /BBox [83 440 178 453]
-  /Resources <<
-    /XObject <<
-      /Form0 12 0 R
-    >>
-    /ExtGState <<
-      /GS0 24 0 R
-    >>
-  >>
->>
-stream
-/GS0 gs
-/Form0 Do
-endstream
-endobj
-12 0 obj <<
-  /Type /XObject
-  /Subtype /Form
-  /FormType 1
-  /Group <<
-    /S /Transparency
-  >>
-  /Length 57
-  /BBox [83 440 178 453]
->>
-stream
-0.0 1.0 1.0 rg
-83 440 m
-178 440 l
-178 453 l
-83 453 l
-h f
-endstream
-endobj
-13 0 obj <<
-  /Type /XObject
-  /Subtype /Form
-  /FormType 1
-  /Length 18
-  /BBox [149 476 191 487]
-  /Resources <<
-    /XObject <<
-      /Form0 14 0 R
-    >>
-    /ExtGState <<
-      /GS0 24 0 R
-    >>
-  >>
->>
-stream
-/GS0 gs
-/Form0 Do
-endstream
-endobj
-14 0 obj <<
-  /Type /XObject
-  /Subtype /Form
-  /FormType 1
-  /Group <<
-    /S /Transparency
-  >>
-  /Length 59
-  /BBox [149 476 191 487]
->>
-stream
-0.0 1.0 0.0 rg
-149 476 m
-191 476 l
-191 487 l
-149 487 l
-h f
-endstream
-endobj
-15 0 obj <<
-  /Type /Annot
-  /Subtype /Link
-  /BS <<
-    /W 0
-  >>
-  /Rect [69 633 542 653]
-  /Dest [3 0 R /XYZ 200 725 0]
-  /F 4
->>
-endobj
-16 0 obj <<
-  /Type /Annot
-  /Subtype /Link
-  /BS <<
-    /W 0
-  >>
-  /Rect [80 613 542 633]
-  /Dest [4 0 R /XYZ 200 725 0]
-  /F 4
->>
-endobj
-17 0 obj <<
-  /Type /Annot
-  /Subtype /Link
-  /BS <<
-    /W 0
-  >>
-  /Rect [66 529 196 544]
-  /A <<
-    /Type /Action
-    /URI (https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf)
-    /S /URI
-  >>
-  /F 4
->>
-endobj
-18 0 obj <<
-  /Type /Annot
-  /Subtype /Link
-  /BS <<
-    /W 0
-  >>
-  /Rect [83 440 178 453]
-  /QuadPoints [83 453 178 453 83 440 178 440]
-  /A <<
-    /Type /Action
-    /URI (https://cs.chromium.org/chromium/src/third_party/pdfium/public/fpdf_text.h)
-    /S /URI
-  >>
-  /F 4
->>
-endobj
-19 0 obj <<
-  /Type /Annot
-  /Subtype /Highlight
-  /AP <<
-    /N 9 0 R
-  >>
-  /NM (Highlight-1)
-  /F 4
-  /QuadPoints [293 542 349 542 293 530 349 530]
-  /P 3 0 R
-  /C [1 0.90196 0]
-  /Rect [293 530 349 542]
->>
-endobj
-20 0 obj <<
-  /Type /Annot
-  /Subtype /Highlight
-  /AP <<
-    /N 11 0 R
-  >>
-  /NM (Highlight-2)
-  /F 4
-  /QuadPoints [83 453 178 453 83 440 178 440]
-  /P 3 0 R
-  /C [0.26667 0.78431 0.96078]
-  /Rect [83 440 178 453]
->>
-endobj
-21 0 obj <<
-  /Type /Annot
-  /Subtype /Popup
-  /Parent 22 0 R
-  /Rect [191 377 443 488]
->>
-endobj
-22 0 obj <<
-  /Type /Annot
-  /Subtype /Highlight
-  /Popup 21 0 R
-  /AP <<
-    /N 13 0 R
-  >>
-  /NM (Highlight-With-Popup-1)
-  /Contents (Text Note)
-  /QuadPoints [149 487 191 487 149 476 191 476]
-  /P 3 0 R
-  /C [0.14902 0.90196 0]
-  /Rect [149 476 191 487]
-  /F 4
->>
-endobj
-23 0 obj <<
-  /ca 1
-  /Type /ExtGState
-  /CA 1
-  /BM /Normal
->>
-endobj
-24 0 obj <<
-  /ca 1
-  /Type /ExtGState
-  /CA 1
-  /AIS false
-  /BM /Multiply
->>
-endobj
-xref
-0 25
-0000000000 65535 f 
-0000000015 00000 n 
-0000000068 00000 n 
-0000000338 00000 n 
-0000000475 00000 n 
-0000000570 00000 n 
-0000001108 00000 n 
-0000001345 00000 n 
-0000001423 00000 n 
-0000001499 00000 n 
-0000001749 00000 n 
-0000001972 00000 n 
-0000002222 00000 n 
-0000002442 00000 n 
-0000002693 00000 n 
-0000002916 00000 n 
-0000003056 00000 n 
-0000003196 00000 n 
-0000003443 00000 n 
-0000003727 00000 n 
-0000003944 00000 n 
-0000004171 00000 n 
-0000004269 00000 n 
-0000004544 00000 n 
-0000004615 00000 n 
-trailer <<
-  /Root 1 0 R
-  /Size 25
->>
-startxref
-4701
-%%EOF
diff --git a/testing/resources/listbox_form.in b/testing/resources/listbox_form.in
index 354d841..054f21b 100644
--- a/testing/resources/listbox_form.in
+++ b/testing/resources/listbox_form.in
@@ -3,7 +3,7 @@
   /Type /Catalog
   /Pages 2 0 R
   /AcroForm <<
-    /Fields [ 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R ]
+    /Fields [8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R]
     /DR 4 0 R
   >>
 >>
@@ -11,16 +11,16 @@
 {{object 2 0}} <<
   /Type /Pages
   /Count 1
-  /Kids [ 3 0 R ]
+  /Kids [3 0 R]
 >>
 endobj
 {{object 3 0}} <<
   /Type /Page
   /Parent 2 0 R
   /Resources 4 0 R
-  /MediaBox [ 0 0 300 600 ]
+  /MediaBox [0 0 300 600]
   /Contents 7 0 R
-  /Annots [ 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R ]
+  /Annots [8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R]
 >>
 endobj
 {{object 4 0}} <<
@@ -56,7 +56,7 @@
   /Ff 0
   /T (Listbox_SingleSelect)
   /DA (0 0 0 rg /F1 12 Tf)
-  /Rect [ 100 350 200 380 ]
+  /Rect [100 350 200 380]
   /Opt [[(foo) (Foo)] [(bar) (Bar)] [(qux) (Qux)]]
 >>
 endobj
@@ -67,7 +67,7 @@
   /Ff 2097152
   /T (Listbox_MultiSelect)
   /DA (0 0 0 rg /F1 12 Tf)
-  /Rect [ 100 400 200 430 ]
+  /Rect [100 400 200 430]
   /Opt [(Apple) (Banana) (Cherry) (Date) (Elderberry) (Fig) (Guava) (Honeydew)
         (Indian Fig) (Jackfruit) (Kiwi) (Lemon) (Mango) (Nectarine) (Orange)
         (Persimmon) (Quince) (Raspberry) (Strawberry) (Tamarind) (Ugli Fruit)
@@ -82,7 +82,7 @@
   /Ff 1
   /T (Listbox_ReadOnly)
   /DA (0 0 0 rg /F1 12 Tf)
-  /Rect [ 100 500 200 530 ]
+  /Rect [100 500 200 530]
   /Opt [(Dog) (Elephant) (Frog)]
 >>
 endobj
@@ -91,24 +91,49 @@
   /Subtype /Widget
   /FT /Ch
   /Ff 2097152
-  /T (Listbox_MultiSelectMultipleSelected)
+  /T (Listbox_MultiSelectMultipleIndices)
   /DA (0 0 0 rg /F1 12 Tf)
-  /Rect [ 100 200 200 230 ]
-  /Opt [(Alpha) (Beta) (Gamma) (Delta) (Epsilon)]
-  /V [(Epsilon) (Gamma)]
+  /Rect [100 250 200 280]
+  /Opt [(Albania) (Belgium) (Croatia) (Denmark) (Estonia)]
+  /I [1 3]
 >>
 endobj
 {{object 12 0}} <<
   /Type /Annot
   /Subtype /Widget
   /FT /Ch
+  /Ff 2097152
+  /T (Listbox_MultiSelectMultipleValues)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [100 200 200 230]
+  /Opt [(Alpha) (Beta) (Gamma) (Delta) (Epsilon)]
+  /V [(Epsilon) (Gamma)]
+>>
+endobj
+{{object 13 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 2097152
+  /T (Listbox_MultiSelectMultipleMismatch)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [100 150 200 180]
+  /Opt [(Alligator) (Bear) (Cougar) (Deer) (Echidna)]
+  /V [(Alligator) (Cougar)]
+  /I [1 3 4]
+>>
+endobj
+{{object 14 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
   /Ff 0
   /T (Listbox_SingleSelectLastSelected)
   /DA (0 0 0 rg /F1 12 Tf)
-  /Rect [ 100 100 200 130 ]
+  /Rect [100 100 200 130]
   /Opt [(Alberta) (British Columbia) (Manitoba) (New Brunswick)
         (Newfoundland and Labrador) (Nova Scotia) (Ontario)
-        (Prince Edward Island) (Quebec) (Saskatchewan) ]
+        (Prince Edward Island) (Quebec) (Saskatchewan)]
   /V (Saskatchewan)
   /TI 9
 >>
diff --git a/testing/resources/listbox_form.pdf b/testing/resources/listbox_form.pdf
index 9e1393e..10c5702 100644
--- a/testing/resources/listbox_form.pdf
+++ b/testing/resources/listbox_form.pdf
@@ -4,7 +4,7 @@
   /Type /Catalog
   /Pages 2 0 R
   /AcroForm <<
-    /Fields [ 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R ]
+    /Fields [8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R]
     /DR 4 0 R
   >>
 >>
@@ -12,16 +12,16 @@
 2 0 obj <<
   /Type /Pages
   /Count 1
-  /Kids [ 3 0 R ]
+  /Kids [3 0 R]
 >>
 endobj
 3 0 obj <<
   /Type /Page
   /Parent 2 0 R
   /Resources 4 0 R
-  /MediaBox [ 0 0 300 600 ]
+  /MediaBox [0 0 300 600]
   /Contents 7 0 R
-  /Annots [ 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R ]
+  /Annots [8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R]
 >>
 endobj
 4 0 obj <<
@@ -57,7 +57,7 @@
   /Ff 0
   /T (Listbox_SingleSelect)
   /DA (0 0 0 rg /F1 12 Tf)
-  /Rect [ 100 350 200 380 ]
+  /Rect [100 350 200 380]
   /Opt [[(foo) (Foo)] [(bar) (Bar)] [(qux) (Qux)]]
 >>
 endobj
@@ -68,7 +68,7 @@
   /Ff 2097152
   /T (Listbox_MultiSelect)
   /DA (0 0 0 rg /F1 12 Tf)
-  /Rect [ 100 400 200 430 ]
+  /Rect [100 400 200 430]
   /Opt [(Apple) (Banana) (Cherry) (Date) (Elderberry) (Fig) (Guava) (Honeydew)
         (Indian Fig) (Jackfruit) (Kiwi) (Lemon) (Mango) (Nectarine) (Orange)
         (Persimmon) (Quince) (Raspberry) (Strawberry) (Tamarind) (Ugli Fruit)
@@ -83,7 +83,7 @@
   /Ff 1
   /T (Listbox_ReadOnly)
   /DA (0 0 0 rg /F1 12 Tf)
-  /Rect [ 100 500 200 530 ]
+  /Rect [100 500 200 530]
   /Opt [(Dog) (Elephant) (Frog)]
 >>
 endobj
@@ -92,47 +92,74 @@
   /Subtype /Widget
   /FT /Ch
   /Ff 2097152
-  /T (Listbox_MultiSelectMultipleSelected)
+  /T (Listbox_MultiSelectMultipleIndices)
   /DA (0 0 0 rg /F1 12 Tf)
-  /Rect [ 100 200 200 230 ]
-  /Opt [(Alpha) (Beta) (Gamma) (Delta) (Epsilon)]
-  /V [(Epsilon) (Gamma)]
+  /Rect [100 250 200 280]
+  /Opt [(Albania) (Belgium) (Croatia) (Denmark) (Estonia)]
+  /I [1 3]
 >>
 endobj
 12 0 obj <<
   /Type /Annot
   /Subtype /Widget
   /FT /Ch
+  /Ff 2097152
+  /T (Listbox_MultiSelectMultipleValues)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [100 200 200 230]
+  /Opt [(Alpha) (Beta) (Gamma) (Delta) (Epsilon)]
+  /V [(Epsilon) (Gamma)]
+>>
+endobj
+13 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 2097152
+  /T (Listbox_MultiSelectMultipleMismatch)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [100 150 200 180]
+  /Opt [(Alligator) (Bear) (Cougar) (Deer) (Echidna)]
+  /V [(Alligator) (Cougar)]
+  /I [1 3 4]
+>>
+endobj
+14 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
   /Ff 0
   /T (Listbox_SingleSelectLastSelected)
   /DA (0 0 0 rg /F1 12 Tf)
-  /Rect [ 100 100 200 130 ]
+  /Rect [100 100 200 130]
   /Opt [(Alberta) (British Columbia) (Manitoba) (New Brunswick)
         (Newfoundland and Labrador) (Nova Scotia) (Ontario)
-        (Prince Edward Island) (Quebec) (Saskatchewan) ]
+        (Prince Edward Island) (Quebec) (Saskatchewan)]
   /V (Saskatchewan)
   /TI 9
 >>
 endobj
 xref
-0 13
+0 15
 0000000000 65535 f 
 0000000015 00000 n 
-0000000151 00000 n 
-0000000216 00000 n 
-0000000379 00000 n 
-0000000414 00000 n 
-0000000447 00000 n 
-0000000523 00000 n 
-0000000625 00000 n 
-0000000832 00000 n 
-0000001302 00000 n 
-0000001488 00000 n 
-0000001741 00000 n 
+0000000163 00000 n 
+0000000226 00000 n 
+0000000399 00000 n 
+0000000434 00000 n 
+0000000467 00000 n 
+0000000543 00000 n 
+0000000645 00000 n 
+0000000850 00000 n 
+0000001318 00000 n 
+0000001502 00000 n 
+0000001747 00000 n 
+0000001996 00000 n 
+0000002267 00000 n 
 trailer <<
   /Root 1 0 R
-  /Size 13
+  /Size 15
 >>
 startxref
-2119
+2642
 %%EOF
diff --git a/testing/resources/marked_content_id.in b/testing/resources/marked_content_id.in
new file mode 100644
index 0000000..ebc217f
--- /dev/null
+++ b/testing/resources/marked_content_id.in
@@ -0,0 +1,235 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /MarkInfo <<
+    /Marked true
+  >>
+  /Pages 2 0 R
+  /StructTreeRoot 11 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+  /Parent 2 0 R
+  /Resources <<
+    /ProcSet [/PDF /Text /ImageC]
+    /ColorSpace <<
+      /CS0 /DeviceRGB
+    >>
+    /Font <<
+      /TT0 5 0 R
+    >>
+    /XObject <<
+      /Im0 9 0 R
+    >>
+  >>
+  /StructParents 0
+  /Tabs /S
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+/Artifact <<
+  /Type /Layout
+  /BBox [408 516 411 522 ]
+>>
+BDC
+q
+18 40 576 734 re
+W n
+BT
+/CS0 cs 0 0 0  scn
+/TT0 1 Tf
+3 Tr 12 0 0 12 408.3599 516 Tm
+(!)Tj
+ET
+EMC
+/Figure <<
+  /MCID 0
+>>
+BDC
+/ClipSpan
+BMC
+Q
+q
+/Clip
+BMC
+203.52 516 204.96 204 re
+W n
+EMC
+q
+/Perceptual ri
+204.96 0 0 204 203.52 516 cm
+/Im0 Do
+Q
+EMC
+EMC
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /KPRGOG+Calibri
+  /FirstChar 33
+  /FontDescriptor 6 0 R
+  /LastChar 33
+  /ToUnicode 8 0 R
+  /Widths [226]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /FontDescriptor
+  /Ascent 952
+  /AvgWidth 521
+  /CapHeight 644
+  /Descent -269
+  /Flags 4
+  /FontBBox [-503 -307 1240 1026]
+  /FontFile2 7 0 R
+  /FontName /KPRGOG+Calibri
+  /ItalicAngle 0
+  /MaxWidth 1328
+  /StemV 0
+  /XHeight 476
+>>
+endobj
+{{object 7 0}} <<
+  /Filter [/ASCII85Decode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+GhOYs.SXL4?t`qUAme0T9e9ZP"3(Hd+dJWtR0Mc^!/VL0WJN&85/SHb2Tl(-5ik^jKM"^O8Hk>#!@0XF
+nrmMQ2*caF2mEF!5/SB`<ldqX9ndPSE^5#N*5h&A+<]SBUVVNeJe#T[$]OJp46d]&Jj98Q)'RR_N+Q$s
+&7@KA^s9^uV"GVZTScj%Z64ENVLB-%'.NJL2I@=I3P\P)?e:eKd2..*\c;[%\!usuLDc2s#rI26AhNtn
+^UG$s\/,#AokOTk)[8bg-SVgp!L942WeSUc0X(R4"+qe&KRog"#T4I])&W>f1LFVQ"3(C(K-c.'K-*pZ
+TV<PGMsrI+Z\u%:/*SdVWiMO*)]RHbR@:''/+G?F`D"M"m?ilmjHu<'?m9bf+?h%AV*@i$rdWUglBL\R
+N*58<pdKG\=IE\FKQM'1"bNLLD=hAlRG$8\=ZP+lbak!2F'OuJYm\^3MTH3FE!XRCM9612s1Q\"pZiJC
+hf!.nSDR84frS]M<L@E<EA4Z%Z0dX:OOUR:oF0ar(1?):((_2??l'I:d;Xs<gEnBP1.+N1'OoX#?V"9)
+62f*K!":%>J09&cGAZN]SBRe'9J-<tdVIk~>
+endstream
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo <<
+  /Registry (Adobe)
+  /Ordering (UCS)
+  /Supplement 0
+>> def
+/CMapName /Adobe-Identity-UCS def
+/CMapType 2 def
+1 begincodespacerange
+<00><FF>
+endcodespacerange
+1 beginbfrange
+<21><21><0009>
+endbfrange
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+endstream
+endobj
+{{object 9 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCII85Decode /FlateDecode]
+  /Height 272
+  /Intent /Perceptual
+  /Interpolate true
+  /SMask 10 0 R
+  /Width 273
+  {{streamlen}}
+>>
+stream
+GhVQ=BrkT/*<>Tnp)[K];NNO';(,Y,e3[reZBQQ?W!Kf<[A?;p.U_Il/M348;ME%+5X5%[`$K.l1b(tG
+TLMZ2+XWWp'13=j`\iPk)E1o?VW[ZsH+/!EYO=ZTq+Z;[m[Z0.gc9$0IqH7cL&Ks6zzzzzzzzzzzzzzz
+zzzzzzzzzzzi5kWPD5!Dac`&SeQ0\"gG?d2ed.;V%H`m^)\?kGADd=@:VD4jXEa<PJG,W`gbI)ZrQ!rE
+mq<cX8_p>e&W:iIMjU]Ac;PpMX%H8<GhV0ff3+WZ!HIIKD_2kcp(LEsJoBS&([m(jN>?9X4Isl\@B)VH
+ei?PN:-bUd:es1a46`.c4#oSK[[&N_Hpg#E(2et8dNjOo4Y\o6mC?8&0=r[0BSDQNBl[LHrrinESN2aM
+H]X[shFSd6Yp6u;/h'+G@dCrba%Fh+VIeW!SI"c<255j-(s+rVNHr1p8m8/;H/)Q#PhA/;iZ1`M;dHTj
+,*\s38\+d-:.HfJ"C2Z>LZMr;Er#sg8DXB?B*?@&/POJ=2Rp.acH1S'c:0h%Qf1/p(_WUYP..Hoq_lC7
+UqW'>J9UIja/=74H/IfJ0UNSK>[EA-iM;_7B;s#=jF65n=VC>ZS])C]u!<LrVP)Et-hQ^Ki?[<aU2WMi
+S[rGk5[#g-0I/C3k+d75h;$Ti8O+(Nt:dX;_GD?E)V!:\B9uBJN?e@>&4d!kgpFji7r5#KakOp4$nkmf
+'eOsCc@\^uX=/66#_jB5In+iQ57QP'hMiSe,i3Xk*8\MecZ[,s'^[&c+h!T9k85jhM&'r-%R25'SVHGB
+9k#XV_9R?#BDhfJLT,ZreD`jrr^qk`H-eIGW^[0@]]-7aBA\.>nnFsQ4`1G];\9@kt4\_Dgq4'8WPj-$
+u_lHNB^YCR9OEk;6NgF<)V&<.s$'T[U:/UA%GD$h=^Ge@VWtFutQ*iBll8J2VF878mS2e6qqssAI](5J
+ultb2`,%T8rR55Z(#hPT3c<3mQMD:rt@imcuhsQg'BB=mCc:obN[AW,VDr*/VCRGZ"VEEVs;/)_0AF;K
+CQS/T404*C@0Nr`kqmB+F&\k[a]3Mg4VAc.Jp05hk`Z?*&@jO2FR5eB8Qb7E`RXE\'mVP$=jZKk*a-=<
+o%<4Nj@9i1H+#gG_VS)(L\KPin1iabgE7Rl\`Hnt3e%u-@inXMl4,7JE5H'\>oG^]C/R82DqN.[/Z5TT
+Abj7/s5$+-\4KuJ!k$S"+oTc3@p]Cu:HCjQ\("LiV_\<pFcgMmJOGPL:Eg0]dUOb12i[T3@EV3=[Gl<O
+319HoCeUAPU>_cY5<G-Cl1?HM]9Fn;I+]GPt/j6b2q=os6KKn7dH[!G?F5"qpMQYU9=f^:m*H#&h#at!
+.Sc!,=c/W3>>8DB5aX)9EcKD*"9$<a19Z+H\fSQr'66$]N*AZ0%faVTGMT):eO*;([PPH$Lo>Ia.Dap7
+iQ7:[SR.Kt$nE;_eKDNmrSVYbTnei/am)MoEVY'#Z=EA*'B!![Bi\U1Xrq<r\/OJT(2s*J#Au5LrZ)c>
+-(V<:Ebf4dId6D-GdA-8uEa81%C#-u'fc&FYfU)VT2jqjEd^V#G65A,L)s5aq"(iVq5.b\DbPtR`F6:H
+J_p+*[1W>Oo_ofZZj>'MId`I8I*8epRQi,B$O&pXGCu"2s2f@43DHi6pg+r;^M=rN<4OWmC'u"Zq,9h=
+.;lAdFZM77U$fM9<Elh+ZD,n'-&",C^cCE>XU>iMX=?LaD1=,@qo8dg/Ses-NXqkT%9NRcJ^$.bA@T-;
+dBg6iBe%]J62X9a(XZ!F3XY7EgQG`YM1Hp%jI;53^`H0$jf:6@!PibJ?l[O&ZY(FDnq(4%U8'gO<>.9V
+)RHV,l_Y.'.$;Bk^_tl+Je#)m:?Iroo^Y)DNJ)(dpH!)%Bit]Q!Ramb%Qf#Dd5][2Kzzzzzzzzzzzzzz
+zzzzzzzzzz!!!#Ws20Ni!j00"QN~>
+endstream
+endobj
+{{object 10 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter [/ASCII85Decode /FlateDecode]
+  /Height 272
+  /Interpolate true
+  /Width 273
+  {{streamlen}}
+>>
+stream
+GhVQ$0b"*_!!8hNrdD20"'P[)zzzzzzzzzzzzzzzzz!8pQUgs,iQ~>
+endstream
+endobj
+{{object 11 0}} <<
+  /Type /StructTreeRoot
+  /K 12 0 R
+  /ParentTree <<
+    /Nums [0 13 0 R]
+  >>
+  /ParentTreeNextKey 1
+>>
+endobj
+{{object 12 0}} <<
+  /S /Figure
+  /P 11 0 R
+  /A 14 0 R
+  /Alt (Hello!\000)
+  /K 0
+  /Pg 3 0 R
+>>
+endobj
+{{object 13 0}}
+  [12 0 R]
+endobj
+{{object 14 0}} <<
+  /BBox [204 516 408 720]
+  /InlineAlign /Center
+  /O /Layout
+  /Placement /Block
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/marked_content_id.pdf b/testing/resources/marked_content_id.pdf
index ddbf11f..cf80171 100644
--- a/testing/resources/marked_content_id.pdf
+++ b/testing/resources/marked_content_id.pdf
Binary files differ
diff --git a/testing/resources/matte.in b/testing/resources/matte.in
new file mode 100644
index 0000000..57d8e13
--- /dev/null
+++ b/testing/resources/matte.in
@@ -0,0 +1,172 @@
+{{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
+  /MediaBox [0 0 100 150]
+  /Contents [4 0 R]
+  /Resources <<
+    /XObject <<
+      /Mask 5 0 R
+      /NoMask 7 0 R
+      /MaskTooFewComps 9 0 R
+      /MaskTooManyComps 11 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+40 0 0 60 0 0 cm
+/Mask Do
+Q
+q
+40 0 0 60 50 0 cm
+/NoMask Do
+Q
+q
+40 0 0 60 0 90 cm
+/MaskTooFewComps Do
+Q
+q
+40 0 0 60 50 90 cm
+/MaskTooManyComps Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /SMask 6 0 R
+  {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Matte [0.0 0.2 1]
+  {{streamlen}}
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /SMask 8 0 R
+  {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{object 8 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+{{object 9 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /SMask 10 0 R
+  {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{object 10 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Matte [0.0 0.2]
+  {{streamlen}}
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+{{object 11 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /SMask 12 0 R
+  {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{object 12 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Matte [0.0 0.2 1 0.5]
+  {{streamlen}}
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/matte.pdf b/testing/resources/matte.pdf
new file mode 100644
index 0000000..05d1ecd
--- /dev/null
+++ b/testing/resources/matte.pdf
@@ -0,0 +1,191 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 100 150]
+  /Contents [4 0 R]
+  /Resources <<
+    /XObject <<
+      /Mask 5 0 R
+      /NoMask 7 0 R
+      /MaskTooFewComps 9 0 R
+      /MaskTooManyComps 11 0 R
+    >>
+  >>
+>>
+endobj
+4 0 obj <<
+  /Length 149
+>>
+stream
+q
+40 0 0 60 0 0 cm
+/Mask Do
+Q
+q
+40 0 0 60 50 0 cm
+/NoMask Do
+Q
+q
+40 0 0 60 0 90 cm
+/MaskTooFewComps Do
+Q
+q
+40 0 0 60 50 90 cm
+/MaskTooManyComps Do
+Q
+endstream
+endobj
+5 0 obj <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /SMask 6 0 R
+  /Length 71
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+6 0 obj <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Matte [0.0 0.2 1]
+  /Length 71
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+7 0 obj <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /SMask 8 0 R
+  /Length 71
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+8 0 obj <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Length 71
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+9 0 obj <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /SMask 10 0 R
+  /Length 71
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+10 0 obj <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Matte [0.0 0.2]
+  /Length 71
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+11 0 obj <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /SMask 12 0 R
+  /Length 71
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+12 0 obj <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Matte [0.0 0.2 1 0.5]
+  /Length 71
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+xref
+0 13
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000370 00000 n 
+0000000571 00000 n 
+0000000856 00000 n 
+0000001148 00000 n 
+0000001433 00000 n 
+0000001704 00000 n 
+0000001990 00000 n 
+0000002281 00000 n 
+0000002568 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 13
+>>
+startxref
+2865
+%%EOF
diff --git a/testing/resources/mona_lisa.fragment b/testing/resources/mona_lisa.fragment
new file mode 100644
index 0000000..873677d
--- /dev/null
+++ b/testing/resources/mona_lisa.fragment
@@ -0,0 +1,109 @@
+/9j/4AAQSkZJRgABAQEAZABkAAD//gBSRmlsZSBzb3VyY2U6IGh0dHA6Ly9jb21tb25zLndpa2lt
+ZWRpYS5vcmcvd2lraS9GaWxlOk1vbmFfTGlzYV9mYWNlXzgwMHg4MDBweC5qcGf/2wBDAAYEBQYF
+BAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUo
+KSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgo
+KCgoKCgoKCgoKCgoKCj/wAARCAB4AHgDAREAAhEBAxEB/8QAHAAAAgIDAQEAAAAAAAAAAAAABQYE
+BwEDCAIA/8QAOxAAAgECBQIEBAQFAwMFAAAAAQIDBBEABRIhMQZBEyJRYQcycYEUkaGxFUJiwfAj
+JOEWF9ElM1LC8f/EABoBAAIDAQEAAAAAAAAAAAAAAAIDAQQFAAb/xAAvEQACAgICAgAEBQQCAwAA
+AAAAAQIRAyESMQRBBRMiUTJhcaGxFCNSgZHwweHx/9oADAMBAAIRAxEAPwBqiIChT5t7gEYwX1ss
+JUbY0ErKBHpFu498Q3fRKTMmJVNm2POo9/tiG2kTFG+FfIAq9t2ta2BtvsKtkqKmDE2UgMDiUn2C
+3ugTnvVGRdPy6M2zKFJxv+HivLN6i6LuPvbDsalPaRPy29Cw/wAYOn1kPgUmYS3OxZUjJvwbFrgY
+fHx8hziqCVF8V+namVUnSvojzqlp9SkD3QkYGWKa62SsY5ZVmVDmlMKnLamCqgvu8bBgPYjkH64U
+3x/FpkcWF4yT8oG4337YBzdUgVHezEwN1N1BxzurslJGI1uSpJG3bvjlew2bYUN+fS+Cj12DJkqE
+WNgOeL4OM66AaNscXkb2vziVK47ZD0IkcG1r/X/PthVWFZLRNKqAAdiCT9cC19jk77MRpqILKNjf
+i/tbCpcug40SooAz6tNyfLsOPbBQhslsrH4n/ECXLa+Tp7IZvBrxaOprEXU0Fx8if1+/a/ri3i8e
+1zn1/P8A6Ba6oR8h6YqY8szKshpauaWdNCzBHkZifmYk778ffAZvIU5Qg3pMsQg4xbS2KmcZQKPU
+aqmqItVhqeEqL9rnGhjzc3SaZXyY63QGDvSuJaScofVfMp57YsOPLUkLUnHcQplXUFfQZrFV5bK2
+XZkll8SEWRx6MnBB9D+mEywppqW0N+by/U6Y+EvXdP1rlrJKkVNnVMAtVTLsrDgSJ30nj2O3pjLz
+4XhlXr0d+JWh6eJQCy2b6DEOKAT+55hQrYsNrW98TH2S2ToAATYG5GwI4wUqSYPZuSEBNXAIBO2E
+8eO0TZ8UZVtf5hvbE7RHYkQRG6kix7f2wuMnexjr0SvDZiqpf6dsH30D12bIKe77gna7W7bYHbbC
+tIFdf9R0/R3S9VmcrIJFHh06t/PKR5Rbk+v0GLMISk1CK2wE03begP8ACHommhy6LM8zjhqM0qf9
+eeoljEjhm3sL7D7b++KmXK8+Tin9K0l+hcUflwTrbLXFHCABftYdvtthbjRHNgjOsoiqKaaJ9Tqy
+kFWAKn88Vsip6HwlfZzV8R+kKeikeSjS128wCD+2NT4f57yfTP0I8jCltFWyJJEXul0vYkLce4Jx
+tqSfRRoJ9K5/WdNdSUOb5a7iWmbUVPEkZI1o3qCt/wBMBmxLLjcP+P1IjOpWztvJa+lzTJ6PMqF9
+dJVxLLG47qwuPpbi2MqP0Opd9ByV9E2KNWF97AbNiWlu2crN8cIABIFxyL7YW2yTZGhlNr7eltsc
+nZD0ZkpyI++1+cMcVVg3sUUWMxRtHpbVc3GKr4voYrM063k32YEi9t9zhiTXRDJVHAzSB3uoK2F/
+bEwm4u7OaT0UL8cs9TqHPsqyZEH+nWxaWvuus7KB6keYn6Dti5gySm5Zn0kyflrGlBdtl7dNWp6F
+FY2awU39RjFxx1aNHJ9g6pdWa5LX3J9MS27aE0uyHXiSRCFN9rG3OEThyGxaQi9UdOpXwyCW1muD
+b/PbC4J4ZckNdTVM5u63y5cpzeemilCQDzebuff9bY9R4eV5cak+zMzxUJUhVmbUNQt6W9bC++Lp
+WaSOjvgT1JK/w+ho5mtHl9Y1OD6o1pB9hrP5Yy/Mhxm697Gp3Wy0aXNhNMsIYi29u1j2v37Yq7fZ
+LetB01arE5DKVQaRY8m+/wDbHUxTnvQRplvbcaTuL+/GIXdMY2bwbxFd2wXNtUD07KMyvqid3WGn
+LvqszF0PIB8o/LfEairZNT9D109mYzSmMiRaJUkMbjmxIDCx+hwacpNEKadk3Nq38HlE8oGpvw7s
+thvcKdsLlG6TCUt0jkbrCsaXramzCGSNr10MmkfNrUqtxfsQBtjS8eP9hwf2f7jJ25KS+6Lv/wC6
+VBDlQq0lSipEm/DhqpZSddr2IRG08H5iDtxjGXi5XJQjt1fa6L7yQrkx1n6p0dKSZwIwAE4vca+B
+Y973B++KXJyfFd9B/LS2xLTq7NKbIWzvOfHpaeRxGFWkknILfKCFsBe3rg/lKWR48cr/ADbpHcmo
+3JAOLrjqHPcwio8uyeaohcj/AHUaSJGBv5jrUabW4P64tT8XHjjynk39tX+wr5km6jErP4oUM8Od
++JUfM/zC99/rjT+GZYvHSRV8lNSViSmmF2XUrM1wQefe2NTsp7uix/hJWFKDNaUMViM0E5AYgsNJ
+Ui/2GKHmR/C/ZPWi5MkzgykxMYlABszCxuP32xVUFsGWR+w1Lm0cNJHGAJRIw3U302bfbne2Ipds
+SOXT9UKqEsHLJqBH0HbFKVqSTLqaadByJ/D1FgbW2vhuO07BbXRydQZocvqqclJCW31XA3IsSe1z
+fbFiWHndC1krQfyLrl6TI5KKFNFTGDaULfU1yGJ/NQCe2AyYJJ/T0Hjkktk2l6tmoTmVI0zSrBEq
+wySG5RmZthc8AXIxPy3KCZy1Oijxoi67yiKUf7eHNI1Z73DDxRck+v8A4xou/wCnlXbi/wCBqf1x
+17OwP4TBW05RJqiJTJ4jBQu7X3O4Nj7jHmFBTjyTNNzcHRo6ioUk6ckprBYGlBFxfYf/AIMdwaWi
+LTeyblGXRnLYYWqaiOwsdDDf87jELFyVSOlNx/Caa5abKKdjCZGL3LNI5YnC5pQdRQULkrZyz8Yc
+wNZ1G3hFrIeB2/y2PRfCYccNv2Z/lu5UhEcuXErWYHdibbE/2xq1ZRHz4VpNP1BVU1I0i+PRlyq2
+a5V1P/2/XFPyopQTfoJLk9Fy5TRVkMjvLTVMpA3ZrHew/I/bvijyW9kSg0uiWIK2PSYaGpYA7Cwv
+xyT3xzURLvsaukq2opJSKiCWJGJtZCdz9sJlFdpjMc2tMbJMxQqbyMCb8jAPI1sclZyPmMBZ2ZZW
+GkeJqLaix4A+mNODr0IaNbMYiDDJIAbgMBbUpAO4F9r3/K+J2yaT7AGeVdTYtHIG1Pu3qTcbn7n8
+sWMUVQSb5C3mFayrGYXa8bhyrdiDdbHvxh0YJ6GznpUdk9I9QJmuUUciOC00aEXO1iAf748jNPHc
+H6dGy6nUgN8QOuafpqloqGty+tkr5m3iiFw68albhhxxv64b4+CedaaSX3ETlHG7e7GLpPMzU9OU
+lZVRS0krhv8AQlPnUajpv72thTSg2rug/wASTFzrTNZTCywgkr9sLjHnJX6Dk6WjmLrCoaozeWR7
+sdR2btvwceq8VVjSRlZvxAaWcNEpK2f+axPGLCEMNdLVMkWYRmJn8UkqpHuLf87emByxuLvoh7Lb
+ynM8wphrSSrjB8xW1lJ2/wA7YoNKnQDeg7H1DXooAmrFckWUbn9rdsQ8a9CqDtB1XUJvUzh1202s
+Dv625tgJwXVEptK7CY6jlqoisTygggGy7geuFcFdkcm/pbOdpa7xpQzCXzeQG2s27WONCMKHHppn
+lHhqg1KbhhtfjkG37euJBe2Q81y92y8vM6HUy2AI8p3P9t//ADgoZVy0iYJ3QqThI4NBVZJmc3bk
+qB6fXFlNt2Ntca9l5fA7qWlzTp6PJ6ipNPmVIDGrgjV4d/Kwvza9vtjznxXBKGR5K+mX8ml4mVTg
+oe0WR1OlR4UUZrcwqHUXSdcvjk29yLAfljN0pW/5LXoh5DA1NMZszrKyrn07LKiokYHoFHJuNyTi
+ZSjJrVV+oKX52LvXGcwDxI42BYgjbfe22H+Pi5y2LyTpUc/Z9KZcwZ2Yl2a/t7DHqMKSjSMvK9gy
+VlvZCRtp+vqcNQtumFMhDfjIWifS9wb3taxwvJJKLOirLlpljjdYiulnF/KfNuNhx9+cZnPTdC3H
+VBCSjUxqZFOlPMCU77knb2xHNN7YFM30ckEQIAXTfY6fMLd8Tr2CyfJnsUKlIULNfsPl55PpxiJQ
+V2FG0ikqKoLlRpRlbSCjC4O/Fr8k+98aEo/caHaWYyOVMOXBRIWCSHYj6ffCZxXabIoL5hl0/wD0
+5WySJQNEkQmURhiWCkG1jyQL++EJrmtjIqip8x8JpnnkRXZyQNPC27n12tjSiq0TrtkGOtlocziq
+sullp54SGjkDWa+3+Wwc4KUeM9pkW4SuLOi+kfjrlz9PxwZ0r0+ZIgWTSl0f+ofX0x5/P8Kywf8A
+a3H+DRx+VCf4tMEZ/wDEuOoB/A6UjI80kjC/5emBx/DZ95Dp511ErvMuojVvppr1EzXJfgL32vzj
+Sx+Lw70V5ZL62LVXSy+O5ZjIxNy9tgMXLVUV3FoinQiG+7WNiB++D7AJuWzGJ1K3BUcn7EYiULVE
+xdOy6qLNoTl1LIWEr2XSVIsGNr77d7jb9cZrwtXE7l7DdVXxVETeV4ybKoQ727m3bfCfkxUrBcrV
+UQBXwq7GJZmXRqKActf0+ww2Ka0C4Jn1TnETVI0sCC1iBGPU/p+3ptjpJvs5QS9i1Q0IVUctAlmu
+PCjB/ckg4a5xYwPUpiiZ1srPqHnbw9TahyABzta+Ikv8URFm+oqA0Eo8VDGV0MqkEDm4P14++EO0
+9DE12UbnlGtPn1VTh2aNJLIVTc+VSARtv5gMa2KVwUgX2BZUu6nURcXJY73w7sGSoO9ARUcnWuUQ
+5giyUk1QIXRvl84Ki/3IxW8vl8mTi6dB4GlkXItXqb4WU8JY0EcSWO5AuAMZGH4nKL4z2Wsnjp7R
+AynonTIgnjUpcCygj8v3wefzk1pnQxv2TOrOlBT5S/gRqiqoJ2tbfck+mEeN5vLJ9TGSxaKlqKfw
+igdGUgHy25B4xvRmn0UZRNog8GjSWQeQFjbttYYlTTlR0o0rLL6OzCiqMshK1FPSLpWO7garj5u3
+YnnFDKppvt//AEFRTHVYacgQ0udQSMynWGS4tzzqxUtydyQTiq0YfpseHLWawaYC6u3kPoCvub4O
+OWvpbI4ezbTdJxC0r1XhIx1aW8oUn1v/AJvivPzI39DsdHx5PtUVPoneOVpayRKlKZfCaJiCdyTv
+ck2F/uMaXKnpavYiiMnW1Y9OwqxTtO6C0ynQoIuo1AA78/8AGHy8aL6YN0eM16rr5qeHxggpp0Cz
+aYwLMrndTyLGx9MQvHjvfR1+hPramevr6iaSZpJJZS2vgsext9hixGKjFJE22fZbltZmtctLQwy1
+FQ+yJGNRv+1vfHTyQhHnkdImMJSfFLZevw3+EUcEyV2czh6lfkVR5EbtYnk+/tjB8n4m8v0YVS/c
+u4vGUPql2XFJl5MAWYaibD5f8tjIk0XEjNNkSIxa2jfa/wCWBa+xNkHPMnSajdbgEIwIbv6XHpgV
+qXJBJXo5zqsnEWdGjIWS8oVSG+XU9h9bb3HbHpoZ+WHn+RQljqVEHqOkENFDSqiMq1UxW3JF7H7X
+Xj64d487lyv0hWRar8wJk0arVCJ2RUJJ1MdlbixPobYt5HcW0V0iysiyTMa6ro2pqOi0RvdHjk1K
++2wIBJIxk5pwjabewoxd2hpHRvU1TmFFXVGZapoTdjqKge3Nj2H0AxXl5eOMWlHsOOCTY7ZjUTUm
+VwrLL+JrVGyKCdbD1P8ALz3OMzDH63JaTLkp8Y17KG6k8OOKopKGIvII1GuxBtxb2G55/vj0HjqT
+lym/ZTlVUhPqqA/wWREVS8Uha+m+w2IB/LjGgprmhDTI2awuuVUxmLqI49Kqex1E2PvucdGVzdE1
+SsG0ccktSqxX1Dvbgep/PBt0rOirejpj4C9IJQ5TLWTxXnqW1K9iCYx8ot6Hn7jHmfifkPNlUU9L
++TVwY/lw/Nl009Ivg2CgWsRccHFSNpWiW7dGWijuBGLXbAPYa12bWj0wGx78WxMlUQU9i91AqpSz
+CUnwyp498VcjldD8Zz7EIaTOqd6dkll/EukjWuCQbkqSeNyfXtvj0D5TxvkvVlZ1FiP1DXvJmwic
+qRCWN/fUf73xqePjUcdr2UMsvqPa5LVVVG88dNK8RYBNKni3Jt62wXz4xbi2FxbV0Fum80qKevSn
+qZXpp1kChilib8bYR5GFSi5R2LafRfGW9RR1FHFBUxzyGMJTuIxpAfgAXtfb19748/ODT2WYz0e6
+l6GOnWVzXxr4nhp8urUb3BBa/wBzhP1ctfuE6rZVOe0aNCskauXSO7Kw0rIO2/pe36Y1sGRp02Vp
+xdWKeeS02V5PGs0aLUMRaPuSMaGLnlyWnoBpRVsQayplq5DJMQVDHSnZfYY0IxSWhTdlp/B3pMZt
+RzVksPiXa2+1gP2A2NxvjH+JeY8clCJf8TEmuTOm+k8pGU5VBT69ciLubW1H1AxhOTcm37LcpWHw
+BYDa/e+Du9AL7mDGRp3IOq9sQ4vom/Z9UJqW1rEbHvgZHJ7EvrVo48qqBJJa6FTcHk9vU4RdzikW
+I9MpSopZ6itCUdGoUEkuyhUDXW9zfbgbcXxtQmlG5S3/AOCrkT6iiZknw5paurmq5JRmFbK5Z1SI
+yxq1727Ltfe57YXk+JTSUIql+4C8ZW5NhvOsloaOkZMwzGKIR/8AtJNVrCL8GyxrueRtfAYcuSTk
+4x3+l/yFOMUtspvq5MtjrUejqF8YfM0c0rgenzqP0ON7xnkcWpr+DPy1eiT0v17XZUwhqJmeEEBJ
+lVQwP9RNwdu/IwryPAhkuUdMnHlcdMtTJ/iHPDCGzFEZJPKr+EmpbDctpA59TvjMn4bb+mi1GQKp
+0myd2rM8rqengC6pUldW2K7WUEkX+vNhbA5az/Rhi2/0ohfRuWim+r83Gd59PVxK0dKvkp47Wsg9
+vfnHofGwfIxqDdv2Uck+UrQMjjaQrCpbzDg84baQNM6J+Due02Vwfgc5XRS2DxS6SFF+dQ/Kx+2P
+M/EcDlk+bD/Zq+PlShxZfFPXUtTRGqpqiOSMjV4isCCPrx3xm8lX2D4uyTSFpRrIIU4OKs6TrROA
+TQe/IGHqOhLbs1SeGgYHn62H1wuSDTbK862rqGOOZDLrYA/JJpAJG4v22v72xWWJzmmh/wAxQVMq
+PMetMloJJHaMZhU2IjhtaFe3H83bnGvj+HZsi/xX7lV+RFO3sWM++JOdVkfgzVy5fSqDppqRLEDi
+5tsP0Pti/g+F4obrl+bEy8lvrQjVuc1Uxv8AiZyp/pA3+u+NKOCEVVFdzb7YPeeRyLs7G9zra+DS
+SAb9mI9QG1irHcDg46jhx6Unlqf/AEzYTIC8bjctYElSODtc7+n0xUz41H+5/wB/UtYJuX0P/v5A
+3rmeq/i38PqleMU41OjC12Nz+gNh9cT4UY8OcfYnNafF+hZbbzXv7W74t0IDmQQJJI81TfSGAuDx
+te+/+cYRlbWkGl9xsjzYitpIqYMksqDQNfyKCbk32vYHn/5DFT5Sabl6GqdPQcyjNJTI6ZfO1KsV
+tbU8ugsx33HDADfj88V8mBNXJXY6GV9Idss+IWf5epi/iaT6AAyVFOCRe1hdbeo5v2xVfiQ7imv9
+jVlT7C8HxZro95IKCcKbF4y4J/Q/TAf0kv8AIJzh3REz74pVE1NIbU9DDGt5JSS+/ZFIFy1+w3+g
+3wEfDcpV2BLyIpfSVD1D1f40FU5m8JpzaOJV8SZ151MSdMYNztuxxq4PD4ta6/4/19ytPNYjPXSy
+E+CCt99iSxPqTycaKikV07C2X5bClI1bVgtHTjdb7SPvZfzt+uFSnLkox7YVLshVcaRKseka7NyN
+gbL/AM4bFtnPQLWOQm6q5ABJ8p49T7e+D5AbZ6VvmNh2sL8Y6yVYRy+qemqaadLiSFw4sObdt8RJ
+cotP2HGTi7XZ/9k=
diff --git a/testing/resources/multiple_form_types.in b/testing/resources/multiple_form_types.in
new file mode 100644
index 0000000..ec6af63
--- /dev/null
+++ b/testing/resources/multiple_form_types.in
@@ -0,0 +1,131 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [8 0 R 9 0 R 10 0 R 11 0 R 12 0 R]
+    /DR 4 0 R
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 4 0 R
+  /MediaBox [0 0 300 600]
+  /Contents 6 0 R
+  /Annots [7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 13 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /Font <<
+    /F1 5 0 R
+  >>
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+0 0 0 rg
+/F1 12 Tf
+100 450 Td
+(Test Form) Tj
+ET
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [100 450 160 460]
+  /A <<
+    /Type /Action
+    /URI (https://www.cs.chromium.org/)
+    /S /URI
+  >>
+  /F 4
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 393216
+  /T (Combo_Editable)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [100 400 200 430]
+  /Opt [[(foo) (Foo)] [(bar) (Bar)] [(qux) (Qux)]]
+>>
+endobj
+{{object 9 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 2097152
+  /T (Listbox_MultiSelectMultipleSelected)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [100 350 200 380]
+  /Opt [(Alpha) (Beta) (Gamma) (Delta) (Epsilon)]
+  /V [(Epsilon) (Gamma)]
+>>
+endobj
+{{object 10 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /T (Text Box)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [100 300 200 330]
+>>
+endobj
+{{object 11 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /P 3 0 R
+  /Rect [135 250 155 270]
+  /T (Checkbox)
+>>
+endobj
+{{object 12 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /Ff 32768
+  /T (radioButton)
+  /TU (radioButton1)
+  /Kids [13 0 R]
+  /V /value3
+>>
+endobj
+{{object 13 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /P 3 0 R
+  /Parent 12 0 R
+  /Rect [85 50 105 70]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/multiple_form_types.pdf b/testing/resources/multiple_form_types.pdf
new file mode 100644
index 0000000..da83c9e
--- /dev/null
+++ b/testing/resources/multiple_form_types.pdf
@@ -0,0 +1,151 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [8 0 R 9 0 R 10 0 R 11 0 R 12 0 R]
+    /DR 4 0 R
+  >>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 4 0 R
+  /MediaBox [0 0 300 600]
+  /Contents 6 0 R
+  /Annots [7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 13 0 R]
+>>
+endobj
+4 0 obj <<
+  /Font <<
+    /F1 5 0 R
+  >>
+>>
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+6 0 obj <<
+  /Length 51
+>>
+stream
+BT
+0 0 0 rg
+/F1 12 Tf
+100 450 Td
+(Test Form) Tj
+ET
+endstream
+endobj
+7 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [100 450 160 460]
+  /A <<
+    /Type /Action
+    /URI (https://www.cs.chromium.org/)
+    /S /URI
+  >>
+  /F 4
+>>
+endobj
+8 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 393216
+  /T (Combo_Editable)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [100 400 200 430]
+  /Opt [[(foo) (Foo)] [(bar) (Bar)] [(qux) (Qux)]]
+>>
+endobj
+9 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 2097152
+  /T (Listbox_MultiSelectMultipleSelected)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [100 350 200 380]
+  /Opt [(Alpha) (Beta) (Gamma) (Delta) (Epsilon)]
+  /V [(Epsilon) (Gamma)]
+>>
+endobj
+10 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /T (Text Box)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [100 300 200 330]
+>>
+endobj
+11 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /P 3 0 R
+  /Rect [135 250 155 270]
+  /T (Checkbox)
+>>
+endobj
+12 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /Ff 32768
+  /T (radioButton)
+  /TU (radioButton1)
+  /Kids [13 0 R]
+  /V /value3
+>>
+endobj
+13 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /P 3 0 R
+  /Parent 12 0 R
+  /Rect [85 50 105 70]
+>>
+endobj
+xref
+0 14
+0000000000 65535 f 
+0000000015 00000 n 
+0000000149 00000 n 
+0000000212 00000 n 
+0000000377 00000 n 
+0000000428 00000 n 
+0000000504 00000 n 
+0000000606 00000 n 
+0000000798 00000 n 
+0000001002 00000 n 
+0000001252 00000 n 
+0000001387 00000 n 
+0000001514 00000 n 
+0000001663 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 14
+>>
+startxref
+1788
+%%EOF
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/named_dests.in b/testing/resources/named_dests.in
index a97aebe..c71f2cc 100644
--- a/testing/resources/named_dests.in
+++ b/testing/resources/named_dests.in
@@ -64,8 +64,8 @@
 >>
 endobj
 % Old-style top-level Dests dictionary. Note that FirstAlternate
-% intentionally references non-exisstant page 11 and LastAlternate
-% intentionally references non-existant object 999.
+% intentionally references non-existent page 11 and LastAlternate
+% intentionally references non-existent object 999.
 {{object 14 0}} <<
   /FirstAlternate [11 /XYZ 200 400 800]
   /LastAlternate  <</D [999 0 R /XYZ 0 0 -200]>>
diff --git a/testing/resources/named_dests.pdf b/testing/resources/named_dests.pdf
index b99cead..ddcc985 100644
--- a/testing/resources/named_dests.pdf
+++ b/testing/resources/named_dests.pdf
@@ -65,8 +65,8 @@
 >>
 endobj
 % Old-style top-level Dests dictionary. Note that FirstAlternate
-% intentionally references non-exisstant page 11 and LastAlternate
-% intentionally references non-existant object 999.
+% intentionally references non-existent page 11 and LastAlternate
+% intentionally references non-existent object 999.
 14 0 obj <<
   /FirstAlternate [11 /XYZ 200 400 800]
   /LastAlternate  <</D [999 0 R /XYZ 0 0 -200]>>
@@ -117,16 +117,19 @@
 0000000638 00000 n 
 0000000766 00000 n 
 0000000000 65535 f 
-0000001060 00000 n 
-0000001188 00000 n 
+0000001059 00000 n 
+0000001187 00000 n 
 0000000000 65535 f 
 0000000000 65535 f 
 0000000000 65535 f 
 0000000000 65535 f 
 0000000000 65535 f 
-0000001283 00000 n 
-0000001393 00000 n 
-trailer<< /Root 1 0 R /Size 23 >>
+0000001282 00000 n 
+0000001392 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 23
+>>
 startxref
-1481
+1480
 %%EOF
diff --git a/testing/resources/named_dests_old_style.in b/testing/resources/named_dests_old_style.in
new file mode 100644
index 0000000..70e45ed
--- /dev/null
+++ b/testing/resources/named_dests_old_style.in
@@ -0,0 +1,55 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /Dests 6 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 2
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+  /Contents [4 0 R]
+  /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+/F1 20 Tf
+100 600 TD (Page1)Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Arial
+>>
+endobj
+% Old-style top-level Dests dictionary. Note that FirstAlternate
+% intentionally references non-existent page 11 and LastAlternate
+% intentionally references non-existent object 999.
+{{object 6 0}} <<
+  /FirstAlternate [11 /XYZ 200 400 800]
+  /LastAlternate  <<
+    /D [999 0 R /XYZ 0 0 -200]
+  >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/named_dests_old_style.pdf b/testing/resources/named_dests_old_style.pdf
new file mode 100644
index 0000000..df7f04a
--- /dev/null
+++ b/testing/resources/named_dests_old_style.pdf
@@ -0,0 +1,68 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /Dests 6 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 2
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+  /Contents [4 0 R]
+  /MediaBox [0 0 612 792]
+>>
+endobj
+4 0 obj <<
+  /Length 37
+>>
+stream
+BT
+/F1 20 Tf
+100 600 TD (Page1)Tj
+ET
+endstream
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Arial
+>>
+endobj
+% Old-style top-level Dests dictionary. Note that FirstAlternate
+% intentionally references non-existent page 11 and LastAlternate
+% intentionally references non-existent object 999.
+6 0 obj <<
+  /FirstAlternate [11 /XYZ 200 400 800]
+  /LastAlternate  <<
+    /D [999 0 R /XYZ 0 0 -200]
+  >>
+>>
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000083 00000 n 
+0000000146 00000 n 
+0000000300 00000 n 
+0000000388 00000 n 
+0000000643 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+761
+%%EOF
diff --git a/testing/resources/no_page_count.in b/testing/resources/no_page_count.in
new file mode 100644
index 0000000..e6b1a2d
--- /dev/null
+++ b/testing/resources/no_page_count.in
@@ -0,0 +1,50 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 200 200]
+  /Kids [3 0 R 3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Pages
+  /Kids [4 0 R 4 0 R 4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/no_page_count.pdf b/testing/resources/no_page_count.pdf
new file mode 100644
index 0000000..885df8c
--- /dev/null
+++ b/testing/resources/no_page_count.pdf
@@ -0,0 +1,63 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [0 0 200 200]
+  /Kids [3 0 R 3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Pages
+  /Kids [4 0 R 4 0 R 4 0 R]
+>>
+endobj
+4 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+
+6 0 obj <<
+  /Length 44
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+ET
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000152 00000 n 
+0000000216 00000 n 
+0000000342 00000 n 
+0000000421 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+516
+%%EOF
diff --git a/testing/resources/non_hex_file_id.in b/testing/resources/non_hex_file_id.in
new file mode 100644
index 0000000..7a93439
--- /dev/null
+++ b/testing/resources/non_hex_file_id.in
@@ -0,0 +1,26 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+>>
+endobj
+{{xref}}
+trailer <<
+  /Root 1 0 R
+  /ID [(permanent non-hex) (changing non-hex)]
+  {{trailersize}}
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/non_hex_file_id.pdf b/testing/resources/non_hex_file_id.pdf
new file mode 100644
index 0000000..6f11eb6
--- /dev/null
+++ b/testing/resources/non_hex_file_id.pdf
@@ -0,0 +1,33 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+>>
+endobj
+xref
+0 4
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+trailer <<
+  /Root 1 0 R
+  /ID [(permanent non-hex) (changing non-hex)]
+  /Size 4
+>>
+startxref
+212
+%%EOF
diff --git a/testing/resources/page_tree_empty_node.in b/testing/resources/page_tree_empty_node.in
new file mode 100644
index 0000000..4a9b901
--- /dev/null
+++ b/testing/resources/page_tree_empty_node.in
@@ -0,0 +1,40 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 200 200]
+  /Kids [3 0 R 4 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Pages
+  /Count 0
+  /Kids []
+  /Parent 2 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Pages
+  /Count 2
+  /Kids [5 0 R 6 0 R]
+  /Parent 2 0 R
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Page
+  /Parent 4 0 R
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Page
+  /Parent 4 0 R
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/page_tree_empty_node.pdf b/testing/resources/page_tree_empty_node.pdf
new file mode 100644
index 0000000..bf80a1a
--- /dev/null
+++ b/testing/resources/page_tree_empty_node.pdf
@@ -0,0 +1,53 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [0 0 200 200]
+  /Kids [3 0 R 4 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Pages
+  /Count 0
+  /Kids []
+  /Parent 2 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Pages
+  /Count 2
+  /Kids [5 0 R 6 0 R]
+  /Parent 2 0 R
+>>
+endobj
+5 0 obj <<
+  /Type /Page
+  /Parent 4 0 R
+>>
+endobj
+6 0 obj <<
+  /Type /Page
+  /Parent 4 0 R
+>>
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000152 00000 n 
+0000000226 00000 n 
+0000000311 00000 n 
+0000000362 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+413
+%%EOF
diff --git a/testing/resources/pixel/axial_shading_point_at_border_no_extend_expected_skia.pdf.0.png b/testing/resources/pixel/axial_shading_point_at_border_no_extend_expected_skia.pdf.0.png
new file mode 100644
index 0000000..6c4c9b7
--- /dev/null
+++ b/testing/resources/pixel/axial_shading_point_at_border_no_extend_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1012369.in b/testing/resources/pixel/bug_1012369.in
index 7f56d35..db1fc5a 100644
--- a/testing/resources/pixel/bug_1012369.in
+++ b/testing/resources/pixel/bug_1012369.in
@@ -46,7 +46,7 @@
   /Filter [/ASCIIHexDecode /JPXDecode]
   /Height 64
   /Width 64
-{{streamlen}}
+  {{streamlen}}
 >>
 stream
 0000000c6a5020200d0a870a00000014667479706a703220000000006a7032200000004f6a7032
@@ -67,7 +67,7 @@
   /Filter [/ASCIIHexDecode /JPXDecode]
   /Height 64
   /Width 64
-{{streamlen}}
+  {{streamlen}}
 >>
 stream
 0000000c6a5020200d0a870a00000014667479706a703220000000006a7032200000004f6a7032
diff --git a/testing/resources/pixel/bug_1015233.in b/testing/resources/pixel/bug_1015233.in
new file mode 100644
index 0000000..aba521f
--- /dev/null
+++ b/testing/resources/pixel/bug_1015233.in
@@ -0,0 +1,113 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 600 200]
+  /Resources <<
+    /ProcSet [/PDF /ImageC]
+    /ColorSpace <<
+      /C1 [/Pattern]
+    >>
+    /Pattern <<
+      /P1 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+0.24 0 0 0.24 0 0 cm
+/C1 cs
+/P1 scn
+888 400 108 60 re
+f*
+0 0 0 rg
+888 460 m
+888 400 l
+S
+/C1 cs
+/P1 scn
+1536 400 108 60 re
+f*
+0 0 0 rg
+1536 460 m
+1536 400 l
+S
+/C1 cs
+/P1 scn
+2184 400 108 60 re
+f*
+0 0 0 rg
+2184 460 m
+2184 400 l
+S
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Pattern
+  /PaintType 1
+  /PatternType 1
+  /TilingType 1
+  /BBox [0 0 128 32]
+  /Matrix [0.202488 0 0 -0.449947 0 791.999]
+  /Resources <<
+    /ProcSet [/PDF /ImageC]
+    /XObject <<
+      /R17 6 0 R
+    >>
+  >>
+  /XStep 128
+  /YStep 32
+  {{streamlen}}
+>>
+stream
+0.24 0 0 0.24 0 0 cm
+q
+0 134 534 -134 re
+W n
+q 533.333 0 0 -133.333 0 133.333 cm
+/R17 Do
+Q
+Q
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /DecodeParms <<
+    /Columns 128
+    /Colors 3
+    /Predictor 15
+  >>
+  /Filter [/ASCII85Decode /FlateDecode]
+  /Height 32
+  /Width 128
+  {{streamlen}}
+>>
+stream
+Gb"0JYmJ3"&-Ti)eYrN1j:2;&T!_s\O.TqV*k'Yd:%,W`r[n(QqR?*8l/0q(V12/<QMrK7>Pbi$Ds?0-
+^SQEPrg*O8rEK--oPa!Od_DW.CH2@W5h#c57fSeR/cAoM\+FV'hpVPkGl`cd*^p
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1015233_expected.pdf.0.png b/testing/resources/pixel/bug_1015233_expected.pdf.0.png
new file mode 100644
index 0000000..aa39e7e
--- /dev/null
+++ b/testing/resources/pixel/bug_1015233_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1015233_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1015233_expected_skia.pdf.0.png
new file mode 100644
index 0000000..1a2a736
--- /dev/null
+++ b/testing/resources/pixel/bug_1015233_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1021762_expected.pdf.0.png b/testing/resources/pixel/bug_1021762_expected.pdf.0.png
index 39d531a..6c436c7 100644
--- a/testing/resources/pixel/bug_1021762_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_1021762_expected.pdf.0.png
Binary files differ
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.in b/testing/resources/pixel/bug_1072440.in
new file mode 100644
index 0000000..1ef4fd6
--- /dev/null
+++ b/testing/resources/pixel/bug_1072440.in
@@ -0,0 +1,39 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [4 0 R]
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [4 0 R]
+  /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /F 4
+  /Ff 8388608
+  /MaxLen 2
+  /P 3 0 R
+  /Rect [70 90 130 110]
+  /T (zip code)
+  /V (11111)
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1072440_expected.pdf.0.png b/testing/resources/pixel/bug_1072440_expected.pdf.0.png
new file mode 100644
index 0000000..ef8b63d
--- /dev/null
+++ b/testing/resources/pixel/bug_1072440_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1072440_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1072440_expected_mac.pdf.0.png
new file mode 100644
index 0000000..28d1ef0
--- /dev/null
+++ b/testing/resources/pixel/bug_1072440_expected_mac.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
new file mode 100644
index 0000000..b0d2d8d
--- /dev/null
+++ 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_1099446.in b/testing/resources/pixel/bug_1099446.in
new file mode 100644
index 0000000..49d6dae
--- /dev/null
+++ b/testing/resources/pixel/bug_1099446.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
+  /Contents 4 0 R
+  /MediaBox [0 0 200 200]
+  /Resources <<
+    /ExtGState <<
+      /G1 5 0 R
+    >>
+    /Pattern <<
+      /P1 6 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+200 0 m
+0 200 l
+/G1 gs
+/P1 SCN
+/P1 S
+endstream
+endobj
+{{object 5 0}} <<
+  /BM /Diff
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Pattern
+  /PatternType 1
+  /Resources <<
+    /ExtGState <<
+      /G1 5 0 R
+    >>
+    /Pattern <<
+      /P1 6 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+200 0 m
+0 200 l
+/G3 gs
+/P1 S
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1099446_expected.pdf.0.png b/testing/resources/pixel/bug_1099446_expected.pdf.0.png
new file mode 100644
index 0000000..1074bd8
--- /dev/null
+++ b/testing/resources/pixel/bug_1099446_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1116869.in b/testing/resources/pixel/bug_1116869.in
new file mode 100644
index 0000000..d123e38
--- /dev/null
+++ b/testing/resources/pixel/bug_1116869.in
@@ -0,0 +1,117 @@
+{{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 5 0 R
+  /MediaBox [0 0 400 400]
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+    /Pattern <<
+      /P1 6 0 R
+    >>
+    /XObject <<
+      /X10 9 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+/X10 Do
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /Pattern
+  /PatternType 2
+  /Shading 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+  /ShadingType 2
+  /ColorSpace /DeviceCMYK
+  /Coords [0.0 0.0 1.0 0.0]
+  /Extend [true true]
+  /Function 8 0 R
+>>
+endobj
+{{object 8 0}} <<
+  /FunctionType 0
+  /BitsPerSample 1
+  /Domain [0.0 1.0]
+  /Filter /ASCIIHexDecode
+  /Range [0.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0]
+  /Size [2]
+  {{streamlen}}
+>>
+stream
+ff
+endstream
+endobj
+{{object 9 0}} <<
+  /Subtype /Form
+  /Resources <<
+    /ExtGState <<
+      /G7 10 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+q
+0 0 m
+3 31 l
+/G7 gs
+f
+endstream
+endobj
+{{object 10 0}} <<
+  /SMas <<
+    /G 11 0 R
+  >>
+>>
+endobj
+{{object 11 0}} <<
+  {{streamlen}}
+>>
+stream
+1 1 2 2 re
+W
+100 100 200 200 re s
+W
+0 g
+/F1 12 Tf
+0 0 60 30 re
+W
+m 100 100
+130 150 200 200 250 230 c
+/Pattern cs
+/P1 scn
+(ABC) Tj
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1116869_expected.pdf.0.png b/testing/resources/pixel/bug_1116869_expected.pdf.0.png
new file mode 100644
index 0000000..1ff3f1f
--- /dev/null
+++ b/testing/resources/pixel/bug_1116869_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1128284.in b/testing/resources/pixel/bug_1128284.in
new file mode 100644
index 0000000..0dfda97
--- /dev/null
+++ b/testing/resources/pixel/bug_1128284.in
@@ -0,0 +1,94 @@
+{{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
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+  /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+0 g
+BT
+/F1 .1 Tf 1 0 0 1 100 100 Tm
+[(a)16000(b)17000(c)]TJ
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type3
+  /CharProcs <<
+    /a97 6 0 R
+    /a98 7 0 R
+    /a99 6 0 R
+  >>
+  /Encoding 8 0 R
+  /FirstChar 97
+  /FontBBox [0 0 100 100]
+  /LastChar 99
+  /Widths [-100 -50 -75]
+>>
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+6 0 0 48 6 0 cm
+BI
+/W 6
+/H 48
+/BPC 1
+/IM true
+ID
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+EI
+Q
+endstream
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+6 0 0 48 6 0 cm
+BI
+/W 6
+/H 48
+/BPC 1
+/IM true
+ID
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+EI
+Q
+endstream
+endobj
+{{object 8 0}} <<
+  /Type /Encoding
+  /BaseEncoding /WinAnsiEncoding
+  /Differences [97 /a97 /a98 /a99]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1128284_expected.pdf.0.png b/testing/resources/pixel/bug_1128284_expected.pdf.0.png
new file mode 100644
index 0000000..6a0df39
--- /dev/null
+++ b/testing/resources/pixel/bug_1128284_expected.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.pdf.0.png b/testing/resources/pixel/bug_113910_expected.pdf.0.png
index 57c6f08..250ea73 100644
--- a/testing/resources/pixel/bug_113910_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_113910_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_113910_expected_mac.pdf.0.png b/testing/resources/pixel/bug_113910_expected_mac.pdf.0.png
index 5b21f5a..f6d8fca 100644
--- a/testing/resources/pixel/bug_113910_expected_mac.pdf.0.png
+++ b/testing/resources/pixel/bug_113910_expected_mac.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
new file mode 100644
index 0000000..4eb3fa0
--- /dev/null
+++ b/testing/resources/pixel/bug_113910_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_113910_expected_win.pdf.0.png b/testing/resources/pixel/bug_113910_expected_win.pdf.0.png
deleted file mode 100644
index 1ff9194..0000000
--- a/testing/resources/pixel/bug_113910_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_1161.in b/testing/resources/pixel/bug_1161.in
index 1f02ab5..f3317e1 100644
--- a/testing/resources/pixel/bug_1161.in
+++ b/testing/resources/pixel/bug_1161.in
@@ -21,7 +21,7 @@
 >>
 endobj
 {{object 5 0}} <<
-{{streamlen}}
+  {{streamlen}}
 >>
 stream
 q 1 0 0 1 50 50 cm /X0 Do Q
@@ -48,20 +48,20 @@
 endstream
 endobj
 {{object 7 0}} <<
-/Type /ExtGState
-/ca 0.2
-/CA 1
+  /Type /ExtGState
+  /ca 0.2
+  /CA 1
 >>
 endobj
 {{object 8 0}} <<
-/Type /Pattern
-/TilingType 1
-/PatternType 1
-/BBox [0 0 18 18]
-/PaintType 1
-/YStep 18
-/XStep 18
-{{streamlen}}
+  /Type /Pattern
+  /TilingType 1
+  /PatternType 1
+  /BBox [0 0 18 18]
+  /PaintType 1
+  /YStep 18
+  /XStep 18
+  {{streamlen}}
 >>
 stream
 0 0 0 rg
diff --git a/testing/resources/pixel/bug_1161_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1161_expected_skia.pdf.0.png
new file mode 100644
index 0000000..f6a4fee
--- /dev/null
+++ b/testing/resources/pixel/bug_1161_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1236805.in b/testing/resources/pixel/bug_1236805.in
new file mode 100644
index 0000000..564f048
--- /dev/null
+++ b/testing/resources/pixel/bug_1236805.in
@@ -0,0 +1,86 @@
+{{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
+  /Contents 6 0 R
+  /CropBox [0 0 612 792]
+  /MediaBox [0 0 612 792]
+  /Parent 2 0 R
+  /Resources 7 0 R
+>>
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+ >>
+stream
+BT
+/T4 1 Tf
+0.12 0 0 -0.12 99.84 684.1801 Tm
+0 g
+/GS1 gs
+0 Tc
+0 Tw
+()Tj
+ET
+endstream
+endobj
+{{object 7 0}} <<
+  /Font <<
+    /T4 11 0 R
+  >>
+  /ProcSet [/PDF /Text]
+>>
+endobj
+{{object 11 0}} <<
+  /Type /Font
+  /Subtype /Type3
+  /CharProcs 12 0 R
+  /Encoding 13 0 R
+  /FirstChar 0
+  /FontBBox [4 -19 53 56]
+  /FontMatrix [1 0.0112920878908041593329204334 0 -1 0 0]
+  /LastChar 4
+  /Name /T4
+  /Widths [60 38 38 38 21]
+>>
+endobj
+{{object 12 0}} <<
+   /CV 17 0 R
+>>
+endobj
+{{object 13 0}} <<
+   /Type /Encoding
+   /Differences [0 /A0 /CU /CV /D2 /CY]
+ >>
+endobj
+{{object 17 0}} <<
+  {{streamlen}}
+>>
+stream
+38 0 5 -19 32 56 d1
+q
+27 0 0 75 5.1 -19.1 cm
+BI
+/W 27
+/H 75
+/BPC 1
+/IM true
+ID
+EI
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1236805_expected.pdf.0.png b/testing/resources/pixel/bug_1236805_expected.pdf.0.png
new file mode 100644
index 0000000..7d4eebe
--- /dev/null
+++ b/testing/resources/pixel/bug_1236805_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1236_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1236_expected_skia.pdf.0.png
new file mode 100644
index 0000000..248125f
--- /dev/null
+++ b/testing/resources/pixel/bug_1236_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1237898.in b/testing/resources/pixel/bug_1237898.in
new file mode 100644
index 0000000..c45e4c6
--- /dev/null
+++ b/testing/resources/pixel/bug_1237898.in
@@ -0,0 +1,60 @@
+{{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
+  /Annots [4 0 R]
+  /MediaBox [0 0 100 100]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /Ff 65536
+  /P 3 0 R
+  /BS <<
+    /S /S
+    /W 1
+  >>
+  /MK <<
+    /I 5 0 R
+  >>
+  /N 6 0 R
+  /Rect [1 2 3 4]
+  /T (foo)
+  /TU (bar)
+>>
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Name /F#e9M
+  /FormType 1
+  {{streamlen}}
+>>
+stream
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  {{streamlen}}
+>>
+stream
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1237898_expected.pdf.0.png b/testing/resources/pixel/bug_1237898_expected.pdf.0.png
new file mode 100644
index 0000000..319932a
--- /dev/null
+++ b/testing/resources/pixel/bug_1237898_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1241587.in b/testing/resources/pixel/bug_1241587.in
new file mode 100644
index 0000000..61b1637
--- /dev/null
+++ b/testing/resources/pixel/bug_1241587.in
@@ -0,0 +1,78 @@
+{{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
+  /MediaBox [0 0 100 100]
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+/F1 1 Tf
+(s) Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type3
+  /CharProcs <<
+    /s 6 0 R
+  >>
+  /Encoding <<
+    /Differences [115 /s]
+  >>
+  /FirstChar 115
+  /FontBBox [0 0 1 2]
+  /LastChar 115
+  /Resources <<
+    /XObject <<
+      /Im1 7 0 R
+    >>
+  >>
+>>
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+/Im1 Do
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1241587_expected.pdf.0.png b/testing/resources/pixel/bug_1241587_expected.pdf.0.png
new file mode 100644
index 0000000..319932a
--- /dev/null
+++ b/testing/resources/pixel/bug_1241587_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1258634.in b/testing/resources/pixel/bug_1258634.in
new file mode 100644
index 0000000..4f6c778
--- /dev/null
+++ b/testing/resources/pixel/bug_1258634.in
@@ -0,0 +1,52 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Kids [3 0 R]
+  /Count 1
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /XObject <</Im25 5 0 R>>
+    /ProcSet [/PDF /Text /ImageC /ImageI]
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+50 0 0 50 0 0 cm
+/Im25 Do
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 447
+  /BitsPerComponent 8
+  /Name /X
+  /Height 150
+  /Decode [0.0 255.0]
+  /Filter [/ASCII85Decode /FlateDecode /JPXDecode]
+  {{streamlen}}
+>>
+stream
+GhQG_Yti1j&;L4+&hHI;8ZC3n%U"m$jU!\c,9XuY,s^4`#\,T>=L,1U+n6>\!><`+M:^s'mTe/j=LRYs
+s8.3L+D^;KgeV9U!5mR3B@E<uP']`:c-mCld>-<04O@hs(t9_.OHhdR,`!m>B3Y]5fsTr<!RNbrCD*'p
+pBsV>I8B!]N0Qp5:2ZMS"Y$P]`Nj1F-AG$1/&,f>CDqEZc>b%B?K"ua*C"PE8JAWBAF'tm-j$q6qGfBk
+e[sGH+nbX3p>#O@GMK5P5JOj(TmQF;al^NZ[gDFm:K9qc8>D89+cSi-LGT>%=t\92mfZn.~>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1258634_expected.pdf.0.png b/testing/resources/pixel/bug_1258634_expected.pdf.0.png
new file mode 100644
index 0000000..f6d66eb
--- /dev/null
+++ b/testing/resources/pixel/bug_1258634_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1258968.in b/testing/resources/pixel/bug_1258968.in
new file mode 100644
index 0000000..bb1318f
--- /dev/null
+++ b/testing/resources/pixel/bug_1258968.in
@@ -0,0 +1,211 @@
+{{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
+  /Contents 12 0 R
+  /MediaBox [0 0 720 540]
+  /Parent 2 0 R
+  /Resources <<
+    /Pattern <<
+      /P1 10 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Mask
+  /BC [1.0]
+  /S /Luminosity
+>>
+endobj
+{{object 5 0}} <<
+  /Type /ExtGState
+  /AIS true
+  /BM /Normal
+  /CA 1.0
+  /OP false
+  /OPM 1
+  /SA true
+  /SMask 4 0 R
+  /ca 1.0
+  /op false
+>>
+endobj
+{{object 6 0}} <<
+  /Bounds [0.300003]
+  /Domain [0.0 1.0]
+  /Encode [0.0 1.0 0.0 1.0]
+  /FunctionType 3
+  /Functions [8 0 R 9 0 R]
+  /Range [0.0 1.0 0.0 1.0 0.0 1.0]
+>>
+endobj
+{{object 7 0}} <<
+  /Filter [/ASCII85Decode /FlateDecode]
+  /N 3
+  {{streamlen}}
+>>
+stream
+GhS^SG>qPn(5L5LGC#t-0Q\.Xmqi'h]h+J9$H('f6Q':!Tns3j'o47^;QNDg`.65@8?@VV+Wr1@%nI7.
+@PE\<W+8BL8pko0n)`[*a1^BaBCBp9fmB@!/c[/h!%oo,Q3&2"-fBc+]@!CJA*DQ/!!>@!!-8.-XR+0H
+0X#F<P-:cF4^6tMr^F#G!@3-O1A2$Oe%u&3BiZQf!!!]a@S8U,RjI''&Z=^t1<&ri&Q9?@`=/N[bSMQR
+@X0[UI:f6M5+gLsbj9Em>K#Nk1YM#/0mAP#F,l>,@O+4W,ro4*Y)%"S==[+5c5#jgEGR[kR10.R,dn(?
+XCMp4M3u3l%s8]Y&ccmHUb=^d$Pm^P"QDeZB4U/r\OjmE\fOM:DJj+-T_i\Kdl7D[EA3TAb2fnnmMY8W
+9O9m_T?mF,1?.$977FX>\$N=o[P0H9U/:l4nS_%TgX#LIjAXAI>43QA^[fQ]!.ZdOWm.t:C^IKLZu6(4
+rn\_TXo/5jMYboTs2B7sid"*2#VtdA9lkAs9t21/M7B4nl[Ltn%=Rb0'Cg7\Q\Y"F8Rr1W"E\Y>=;bfj
+69[OoPKDh2NW2,Zs#BMOo.UO5,]`Z"SgYdL1hBouP,R2!2HR]=i6f*Js4"u:)"o>*SnjH:N?80Yc3-]2
+;ZZH100iBj!/]M^meZC#$c#74n]ctkS,>DCT*5,iY&K*iOj-Vs'^$MU:1jn/3M4pAf+O_W!D!8A:]RL\
+'BfI,?ib(p")n:4^^gR!E"EIZ!Z"<2l$s:b5p9>@8jF9H#'PSZYR`RB-I;fhJ9Gl9>Qe-D%<)shJbrgh
+9Rpb`(;'hZ&-QDb1.6[-+>l\/15%2$#35o4630n2'Fi#K8IuCF6A<bjW!o\ZlC\5U'D*ku)S-2[K*J!\
+cknSKr<g5j&XW]"@22lL2ME$Q/*me%_^jXj3"%?hMrCdW%GUq*KA5=Bn-sUJ*eg6&EsP$e#RsX$N"H9R
+NW^L[80Yo@ef^e[2:@#^+Iu%6EXNaRArsJM%#TY2UPr%[-m-qo'FU1[>(\:peY/"rg/-V(>Gs:+/F"IK
+N)iCeU@$@BkU:Nc3Yu(RY!sL"T0cmS&"tAtY;$k&%ASid`V<i80e&JiQu^Cl9Io6,AcOWIRDWL=$<TZs
+,;ohN`!W^pfNRVrm!7q?FoqOF8&VCc/9"q*(`X&fKWE*5`LXmUi[/XQGU^9`hB!blBB/UR(&17G-b^Ze
+JR9Nj'6FP@KQ'6b@6L<=cpG69GX-LF,SDtjG/Oq9Qai&X6Te,V8fT(i--;5W8FE9UP*!&9FI70$FI2W_
+9IB(e?-"a2!I63+M0t0XM#9_o,%2\S/&@NR.o@tI-8?q=>S8]h/a4aq;R/Qn>4SJ"^Z:=f?:m`1?&C)=
+7]Ke[[+'o]S2<[g>WPD:?+O;;W:Vt3.2f4GNf9kE>GbNbTZe^ME=,%[Ups?*PRWjHb%8]u'gpNJqGL7j
+A^kXsIFMD&)-JKC#h_>nHJ,X1UGRY$6h@.Nd4/fWBZ%3$E[l+4EZDVAE\2<aEqAionh0JQq,7#hq'#QX
+$?QGIKiEcn$LJ02'iBhmMBRkfML-'k/']P?PoQ#j8u5--PjU!<ap-"^b,^a;.ebF=WMlhAX(86[Wa&Gi
+;5L8'W2?LoWLd%Z<hlTRWH$IgeBY*XV9@:ml"Tc.H8g7Q?!5uR?)W3;]ha&B6W[cc7Zg0@`^<IP-:9Rd
+ZV%PsF&kq..o<9CX'+b[2I;lHSZKLt]173S7oq'9B9Q*&cBPKEY-2_oDR8a54gLZc?C"oMhJI\"5!qP7
+IujS'"5(EC+PEFET\g;=Zn^V[mP,\866"SqnVeBF7Z9pYY/WKAc,O6?1m7p',*R26kGDF[0rE]"HV?Ym
+=du7U24E>[QSJ4j0/DuQs4,!%Jn7etESHXml".EVZGZ6eQ(\r5C9Q/.CPm8-od8uWlOPn^2LLu0RqZ(Y
+gN_K/[Nc21,FD?R\8$f-F0f(dGK0'Q]oB_@re15'%`T!X%S%P:QKTs[/88?Em\o*cF?JJ@l9oiRGM;LB
+lE'EtFDc&Hm%Wa]FueV=l)O3@id/iA\XT!+hsHZ_]XtlSCA/)WLkiH0WCNSYN9;tB3mU<&)I?!Ac!Ia5
+f&!I?0DM2gr-c$NN;0FJZ@&-hBDlZF<r7%:PPi>rlhhh0(^0=13Wn>+^VCk@&!gr`jXk<[!^XOd4+\LX
+lNUu*Iij7K#nEpFGnOj5=r>s,7L=t\3u[fRgb$/_r;JIbA\r"a&X!a2&PWl/OWKXMOeYdN;6-I06n:`J
+UH;qh0bH-q@ciu'0&O3e,1\f`,^+EdmY\Xlf2aB(9Nud#:,7hIG;:3[*uTPbr]N&nfta+Nn4hQ+0["KL
+m:&4g]<J?gdi:kQ-"f!\0sNKR.`jDZ8()LbF)lEo%+h%Sa*j*0PHliQ4uFb]E9^n9A6Nb0/j2Ds1f&jm
+b3R>Lc,e'Fa>D*laW/R2ce=dEbP10Ab(d3'A9S<XU7\DkTq64uE%K$YV`s9<DFEp74R"t7?>mPDrg*K(
+!j@3Z=.r["<co.W(ToD2>OB#u^`$l\_O@X8RVRi5:9=%nG@WjFIHpYNMTgr-MdC7VgsB3*;S9/WI&$GU
+jq<L`I`]YXLJ@Y(UGd0PpMI1j;S2%#.D,4uR9<[8g&#ejq=Ur8^T.*EEGORb^'^?e^"Y6qGh[t^4)[>]
+.:$(Q3PH_W7p?_AWjM'AcFBFsWi`;qm3:m0mM`26W9L82Wng_Q^+Qs6W8=YjFgpr0/\C3?YkJRoFkPNT
+:M!nd^3BIbqsPNo51%lRdnkH(F"le8PK&EuT!'#=2]YdY<-IKC@G]tj,G@W=Rr"R_N[c-QBSN"1c-s^B
+O59`Rj-u&Ej-BeL[+;PCi4/:Odr49]*BrG3U?a)bABT2NhsETr3E=c;[H4mB[O8PFG!pN_SG#&#e`kG+
+l?e``&"c]`;h%jACrJEO\`.[gk6oS]?-'Z4F"uq=GZS/%rC^"SDKgkXpKdL-I.`BKPtO9C4m0"0?9NAS
+rn?W&N9fjiCi6WQ>5+Wb_>8pcI5ZLjrO2YF2skL+%P/JW2s<AKnWq4t/%9qFh\]NX1bcRYps)FsaQOZ<
+SZ058G@0.K\[QO-h#m;%dYC^2WuicTCA:[+a4=+WdBd=(A%dr@*oA&PNBd:hqks0%`D=QNW[H=DS#.a6
+c:iRZhgE$5l,9pPB+$LA5>2$hqmkSTms]9PV0=h4G2`]*`T")_0,7e7IV&2PCG'l"dHgGV$hL35']'gT
+bBVe50(RiZ6LK3eqjJ&l+$)X_IseJf^:6`/%I9DR"T~>
+endstream
+endobj
+{{object 8 0}} <<
+  /BitsPerSample 8
+  /Decode [0.0 1.0 0.0 1.0 0.0 1.0]
+  /Domain [0.0 1.0]
+  /Encode [0.0 255.0]
+  /FunctionType 0
+  /Order 1
+  /Range [0.0 1.0 0.0 1.0 0.0 1.0]
+  /Size [256]
+  /Filter [/ASCII85Decode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+GhP$l@s8#P'EukPrU7.2gU>qX[P\^,dq&..1\:=oP+ts]GDY.=PP0gGgdV+ET?;OHG1/`sLjOlq&KI(L
+f!-\HkIq7B&uVaYlSQoV!1g7^3I>]d(N>LlkRoLPlerR``AJ^1laUAaDW2FL2e\0<i3#5WL\l>3'D.A9
+M'G,`P1C:/jXGM1k<+/Y<ITd+C5-&Q2`uGFGN-<%UC[-5cTgAE^1[]9/&(8Tpb%$RVd?*)i\rOj:o!r(
+Y;jmc/hX\'%se]<CW\igDcUQPK,?@ZehBR,ZQ@)Q]b/9f-Fo9Pdsk?A?l!fgUNnD0]niMN^"bAV"XS'/
+=;>VRB!'mj@tXe1W1qj\;-c$4D6r7E%]l8Y~>
+endstream
+endobj
+{{object 9 0}} <<
+  /BitsPerSample 8
+  /Decode [0.0 1.0 0.0 1.0 0.0 1.0]
+  /Domain [0.0 1.0]
+  /Encode [0.0 255.0]
+  /FunctionType 0
+  /Order 1
+  /Range [0.0 1.0 0.0 1.0 0.0 1.0]
+  /Size [256]
+  /Filter [/ASCII85Decode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+GhW&pn(7TS56iq>1%`hbn&#3T)[V<.!'@^]o`~>
+endstream
+endobj
+{{object 10 0}} <<
+  /Type /Pattern
+  /BBox [-79.0947 -75.3227 29.2131 32.9851]
+  /Matrix [2.58008 0.0 0.0 -2.58008 0.0 540.0]
+  /PaintType 1
+  /PatternType 1
+  /Resources <<
+    /ExtGState << /GS0 5 0 R >>
+    /Shading << /Sh0 13 0 R >>
+  >>
+  /TilingType 3
+  /XStep 108.308
+  /YStep 108.308
+  {{streamlen}}
+>>
+stream
+/GS0 gs
+BX /Sh0 sh EX Q
+Q
+endstream
+endobj
+{{object 11 0}} <<
+  /Type /Pattern
+  /BBox [0.0 0.0 10.0 10.0]
+  /Matrix [0.899994 0.0 0.0 -0.899994 0.0 540.0]
+  /PaintType 1
+  /PatternType 1
+  /Resources <<
+    /ExtGState << /GS0 15 0 R >>
+    /ProcSet [/PDF /ImageC]
+  >>
+  /TilingType 3
+  /XStep 10.0
+  /YStep 10.0
+  {{streamlen}}
+>>
+stream
+/GS0 gs
+endstream
+endobj
+{{object 12 0}} <<
+  {{streamlen}}
+>>
+stream
+/P1 scn
+q 1 0 0 1 24.0804 441.1595 cm
+0 0 m
+14.956 18.738 42.588 21.551 61.719 6.282 c
+B*
+endstream
+endobj
+{{object 13 0}} <<
+  /ShadingType 4
+  /AntiAlias false
+  /BitsPerComponent 8
+  /BitsPerCoordinate 32
+  /BitsPerFlag 8
+  /ColorSpace [/ICCBased 7 0 R]
+  /Decode [-79.0947 49.8358 -75.3227 76.1016 0.0 1.0]
+  /Function 6 0 R
+  /Filter [/ASCII85Decode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+GhQY8J:de0msApO^RfKC56Jf5[]KL9Ib"F1@(I<ko)G&.2NW(&f'iVl^SO3H`a9ee^Y3Q0ZIY89s8Vk4
+0+)K'[da=[+W`o"m.eU#2uc&GYr(>85FqT^,V,YdJ+(;u+D](<_>cUbetB6*(-VW2^AJb.F\q5Ps&D?-
+HS=&+rElD6$-7Elci&@,$-39'=C1p_<'1hJopO.gs'@^7+R>rd\c8R"8jKJ.V#NA6dQt1DRfB!CA-K9s
+^;S%N!W[6dO+%~>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1258968_expected.pdf.0.png b/testing/resources/pixel/bug_1258968_expected.pdf.0.png
new file mode 100644
index 0000000..0770f77
--- /dev/null
+++ b/testing/resources/pixel/bug_1258968_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1258968_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1258968_expected_skia.pdf.0.png
new file mode 100644
index 0000000..18f4279
--- /dev/null
+++ b/testing/resources/pixel/bug_1258968_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1271578.in b/testing/resources/pixel/bug_1271578.in
new file mode 100644
index 0000000..8837f5b
--- /dev/null
+++ b/testing/resources/pixel/bug_1271578.in
@@ -0,0 +1,95 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+  /MediaBox [0 0 100 100]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+40 30 Td
+/F1 36 Tf
+(!)Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /Test
+  /FirstChar 33
+  /FontDescriptor 6 0 R
+  /LastChar 33
+  /ToUnicode 7 0 R
+  /Widths [415]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /FontDescriptor
+  /Ascent 922
+  /CapHeight 1151
+  /Descent -196
+  /Flags 4
+  /FontBBox [0 -228 2000 921]
+  /FontFile2 8 0 R
+  /FontName /Test
+  /ItalicAngle 0
+  /MissingWidth 750
+  /StemV 0
+>>
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+/CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo <<
+/Registry (Test) /Ordering (UCS) /Supplement 0 >> def
+/CMapName /Test def
+1 begincodespacerange <21> <21> endcodespacerange
+1 beginbfrange
+<21> <21> <0629>
+endbfrange
+endcmap CMapName currentdict /CMap defineresource pop end end
+endstream
+endobj
+{{object 8 0}} <<
+  /Filter [/ASCII85Decode /FlateDecode]
+  {{streamlen}}
+  /Length1 1152
+>>
+stream
+GhV7Y6"got'R_dDS#gHM71g``Z@S[j_4%a(QV`9fkbUmea@WX0RPr-&&s@b[7(`ka2\?Q-3Jf-R,)CL5
++:'#&%pm0q>4<`1=:YRPqIXpiXEsF'?ZtVG5P+m"s7:FX!"@g9'FMfgC$OLFobW-97rRDZR3Q3#5!Kte
+`(BOQMXNDWo^*Ch6f,j"]QUX5UCu-\c8uD,Qf^N7h9H,n#J"'>H?6.=dl3,__9j/A".)HC>)PLCo/k-1
+"r["/>//RcGSl0.rOV.*\(nn.31Ne1G]h?9?>MjaN`CZpVqGa-MKe)6q>QV=&"Q?-pmAY>4Whjs`uq4c
+mV;`O*^=oD*[@G-\RA3RXTOF,W/FDeT#)as6^&HTUgHh!*X1;3AD^HB/!QKb:H@mrGO0<EmLMh\#4llE
+A';]e-+,IKG=UlFe>*J=FbK^:F'=Mm-cDb]6r/02KD#tL3CHuIn4i%?QsJjr7)5oI[)It$92\:,ce-B`
+4sGT-C-n@OG38UMfPkfOIQ-Jj@F(Y.hW2(Iq>+`)XgHXjHl9Ce9a"N+(bCO<)6AYC#4'DR1YAS5CiG&V
+?_<Lh/*K&3/;[j@/)h.n/`OYPgPCV49H9b@JeG(sUVkBV[e96!BUQ4ra!)G#ZWhGe794V,\_:dO2X50t
+2!b.710r>Oi8%b@WH:'*(#][r=j)N:=BIHST\St9pK-?]J,^S!!-4ih@f~>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1271578_expected.pdf.0.png b/testing/resources/pixel/bug_1271578_expected.pdf.0.png
new file mode 100644
index 0000000..410bbdb
--- /dev/null
+++ b/testing/resources/pixel/bug_1271578_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1271578_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_1271578_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..7318ef2
--- /dev/null
+++ b/testing/resources/pixel/bug_1271578_expected_agg_mac.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_1286_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1286_expected_skia.pdf.0.png
new file mode 100644
index 0000000..b4442e4
--- /dev/null
+++ b/testing/resources/pixel/bug_1286_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1287409.pdf b/testing/resources/pixel/bug_1287409.pdf
new file mode 100644
index 0000000..3973822
--- /dev/null
+++ b/testing/resources/pixel/bug_1287409.pdf
Binary files differ
diff --git a/testing/resources/pixel/bug_1287409_expected.pdf.0.png b/testing/resources/pixel/bug_1287409_expected.pdf.0.png
new file mode 100644
index 0000000..60507e1
--- /dev/null
+++ b/testing/resources/pixel/bug_1287409_expected.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_1287409_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1287409_expected_skia.pdf.0.png
new file mode 100644
index 0000000..0b00554
--- /dev/null
+++ b/testing/resources/pixel/bug_1287409_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1288_1_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1288_1_expected_skia.pdf.0.png
new file mode 100644
index 0000000..df6b87e
--- /dev/null
+++ b/testing/resources/pixel/bug_1288_1_expected_skia.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_1288_2_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1288_2_expected_skia.pdf.0.png
new file mode 100644
index 0000000..3a8cbee
--- /dev/null
+++ b/testing/resources/pixel/bug_1288_2_expected_skia.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_1296_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1296_expected_skia.pdf.0.png
new file mode 100644
index 0000000..828856c
--- /dev/null
+++ b/testing/resources/pixel/bug_1296_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1304714.in b/testing/resources/pixel/bug_1304714.in
new file mode 100644
index 0000000..976d901
--- /dev/null
+++ b/testing/resources/pixel/bug_1304714.in
@@ -0,0 +1,107 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /AcroForm [4 0 R 6 0 R 8 0 R]
+  /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
+  /Annots [4 0 R 6 0 R 8 0 R]
+  /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /P 3 0 R
+  /AP <<
+    /N 5 0 R
+  >>
+  /F 4
+  /Ff 1
+  /Rect [20 10 180 190]
+  /T (Red bottom layer)
+>>
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 160 180]
+  {{streamlen}}
+>>
+stream
+1 0 0 rg
+0 0 160 180 re
+f
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /P 3 0 R
+  /AP <<
+    /N 7 0 R
+  >>
+  /F 4
+  /Ff 1
+  /Rect [80 90 130 130]
+  /T (Green middle layer)
+>>
+endobj
+{{object 7 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0.0 0.0 50 40]
+  {{streamlen}}
+>>
+stream
+0 1 0 rg
+0 0 50 40 re
+f
+Q
+endstream
+endobj
+{{object 8 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /P 3 0 R
+  /AP <<
+    /N 9 0 R
+  >>
+  /F 4
+  /Ff 1
+  /Rect [90 100 120 120]
+  /T (Blue top layer)
+>>
+endobj
+{{object 9 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0.0 0.0 30 20]
+  {{streamlen}}
+>>
+stream
+0 0 1 rg
+0 0 30 20 re
+f
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1304714_expected.pdf.0.png b/testing/resources/pixel/bug_1304714_expected.pdf.0.png
new file mode 100644
index 0000000..58b7600
--- /dev/null
+++ b/testing/resources/pixel/bug_1304714_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1308_1_expected.pdf.0.png b/testing/resources/pixel/bug_1308_1_expected.pdf.0.png
index 44c0446..33a45c0 100644
--- a/testing/resources/pixel/bug_1308_1_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_1308_1_expected.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_1_expected_win.pdf.0.png b/testing/resources/pixel/bug_1308_1_expected_win.pdf.0.png
deleted file mode 100644
index 70d0f23..0000000
--- a/testing/resources/pixel/bug_1308_1_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_1308_expected.pdf.0.png b/testing/resources/pixel/bug_1308_expected.pdf.0.png
index 1c36944..1013fb1 100644
--- a/testing/resources/pixel/bug_1308_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_1308_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1308_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1308_expected_mac.pdf.0.png
deleted file mode 100644
index 570f992..0000000
--- a/testing/resources/pixel/bug_1308_expected_mac.pdf.0.png
+++ /dev/null
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_1308_expected_win.pdf.0.png b/testing/resources/pixel/bug_1308_expected_win.pdf.0.png
deleted file mode 100644
index 0acd825..0000000
--- a/testing/resources/pixel/bug_1308_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_1330_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1330_expected_skia.pdf.0.png
new file mode 100644
index 0000000..0ae57c7
--- /dev/null
+++ b/testing/resources/pixel/bug_1330_expected_skia.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_1338_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1338_expected_skia.pdf.0.png
new file mode 100644
index 0000000..7698e64
--- /dev/null
+++ b/testing/resources/pixel/bug_1338_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1355.in b/testing/resources/pixel/bug_1355.in
new file mode 100644
index 0000000..a00fcf1
--- /dev/null
+++ b/testing/resources/pixel/bug_1355.in
@@ -0,0 +1,85 @@
+{{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 [6 0 R]
+  /Resources 4 0 R
+  /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+  /ProcSet [/PDF /Text]
+  /Font <<
+    /F1 5 0 R
+  >>
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type0
+  /BaseFont /HonMincho-M
+  /Encoding /90pv-RKSJ-H
+  /DescendantFonts [7 0 R]
+>>
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+/F1 4 Tf
+6 0 0 6 150 150 Tm
+0 0 0 1 k
+<eb5b>Tj
+-10 0 TD
+<eb69>Tj
+0 -8 TD
+<eb6a>Tj
+ET
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /Font
+  /BaseFont /HonMincho-M
+  /CIDSystemInfo <<
+    /Ordering (Japan1)
+    /Registry (Adobe)
+    /Supplement 2
+  >>
+  /DW 1000
+  /FontDescriptor 8 0 R
+  /Subtype /CIDFontType2
+  /W [1 [250] 18 19 500 54 [833] 81 [583]]
+>>
+endobj
+{{object 8 0}} <<
+  /Type /FontDescriptor
+  /Ascent 880
+  /CapHeight 709
+  /Descent -120
+  /Flags 6
+  /FontBBox [-165 -221 1066 952]
+  /FontName /HonMincho-M
+  /ItalicAngle 0
+  /StemV 81
+  /Style <<
+    /Panose <010502020500000000000000>
+  >>
+  /XHeight 450
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1355_expected.pdf.0.png b/testing/resources/pixel/bug_1355_expected.pdf.0.png
new file mode 100644
index 0000000..3bd23af
--- /dev/null
+++ b/testing/resources/pixel/bug_1355_expected.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
new file mode 100644
index 0000000..f097788
--- /dev/null
+++ 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.in b/testing/resources/pixel/bug_1356149.in
new file mode 100644
index 0000000..6dd6502
--- /dev/null
+++ b/testing/resources/pixel/bug_1356149.in
@@ -0,0 +1,216 @@
+{{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 100 100]
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /T1_0 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+/T1_0 10 Tf
+-0.02 Tc 0.02 Tw
+1 0 0 1 10 20 Tm
+[(\201)-3.6(\027\036)-5.4(\007)-6.6(\007)-5(\026)-2.5(\034\033)]TJ
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /JXUIQN+BernhardGothic-Hvy
+  /Encoding 6 0 R
+  /FirstChar 1
+  /FontDescriptor 7 0 R
+  /LastChar 173
+  /Widths [665 576 576 625 576 255 535 616 247 0 250 529 970 540 534 529 531 542
+           518 714 522 238 447 352 372 487 200 399 235 538 499 0 0 0 0 0 0 0 0 0
+           0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+           0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+           0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 543 576 535 0 0 0 0 0 0 0 0 0 0 0 620
+           0 635 561 0 0 0 0 0 0 0 0 0 0 0 0 542 0 0 332 0 0 0 0 0 0 0 0 0 0 0 0
+           500]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Encoding
+  /BaseEncoding /WinAnsiEncoding
+  /Differences [1 /H /zero /two /N /one /period /o /V /comma /.notdef
+                /quotesingle /E /W /n /d /b /u /p /fi /w /e /l /c /t /r /a
+                /space /s /i /h /T 127 /g /three /S 141 /C 143 /D /B 157 /P 160
+                /f 173 /bullet]
+>>
+endobj
+{{object 7 0}} <<
+  /Type /FontDescriptor
+  /Ascent 1000
+  /CapHeight 666
+  /CharSet (/H /zero /two /N /one /period /o /V /comma /quotesingle /E /W /n /d
+            /b /u /p /fi /w /e /l /c /t /r /a /space /s /i /h /T /g /three /S /C
+            /D /B /P /f /bullet)
+  /Descent -500
+  /Flags 4
+  /FontBBox [-500 -500 1500 1000]
+  /FontFamily (Bernhard Gothic)
+  /FontFile3 8 0 R
+  /FontName /JXUIQN+BernhardGothic-Hvy
+  /FontStretch /Normal
+  /FontWeight 700
+  /ItalicAngle 0
+  /StemV 144
+  /XHeight 472
+>>
+endobj
+{{object 8 0}} <<
+  /Subtype /Type1C
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+789c959769541467bac7ab68abab156d17ac11aab4aa1171178c1963d48c
+0b6e88a8b8a008ca8e022a48d3b2ef427757757575030aa24023c8ee8202
+c60ddce3763489a249266372332766726ecc7827b9f129eecb9c735f265f
+e6e3bda73f749fd3d5effb2cffe7f7fc9b2446b8112449b24161a1ebb76c
+9a1390604c498c31c6af4b352526c5cd0bccc81efed6a072a43a7984cd66
+97a92984c773710c788d8590719031be63f2d8f3130837927cffc399ab66
+bdb778f102c3f6c404c3dad414936155eac1433129d986f529717e730d2b
+83830d5b93f6259ad20d5b13d2138c1909f1810931f80282c42f229120c6
+10c4588218af217c34c42c82f02788f708e28f24b188240274c44682d8ec
+466cd3106104612288830439c140cc20e6127ec47c4d802690d8448410db
+c81d648cee0091e266d46412cdc43704311327488cc087fb1211442ef184
+f4218bc8bfb9e5b97da9d9a2691f9138e247ea20f5423b439bae6da359da
+49bfd0edd6758e9c3cf2e4a88051a65197dcd7ba978f2647078fee1a238c
+393be63ff46efaeca17e3d7a6773a9752e528feeaa9ed041c242d5936981
+0e0a79685b5007a547f76dae5f5c24903f68de210373a2c6e170b5eaba6e
+d6f6f4b277133ede1599941c15dd76e8aa99b7da6c56f67046943185d7a3
+1bc53083bc053334b0e731d3b3bf333636393926b6f340cfc7eded1ff3fa
+12f05057c224b2037828006f8d3a03be61901017b06c59dc000830e5d2d7
+2f5ef62e43061ebcab1988f40516f947ecc94b8de31787c4f9223f16ed79
+8b26821f2cfc193c2195d7db5c83340ed30453342a31e8cfa0590594d526
+5b5951ce510ef2ca7e7b52226ba645ba0cfd932aa5cb964b26314f2af392
+a4e1b06181a2154f4a8d52874ebc606b73b14edaa94a94de0663d525e045
+c25fc1a0f95c6d627e5e43dd69e9eeece1ce9f4a8f4f2d4b294c13920382
+9066f7fae27ccf3988a3d4955a074ca4ac8793c4744ed44e95cf5faaaabd
+f8d4ebcedb8647032c908b5e21b779013b6645f3fa23e0a5ae803f90f02d
+0efaefea1de6f54a473975b9a3b1b69d6b6f4a4f29114b2c2542ce5ee3a1
+d521668be79b8cfb3b37b091f1f1bb6362da6f24f348bb9e9adbf74de88f
+1c3040fc02e39e27df09ec16ae5caa6efe84fd61c997b357fc69d7ca045e
+5f5100132110f4e003e3c947608059e0a38118e8619c4d47ed8ddcb32b7b
+b787462705a615dbcb0f09a2ba8a71ba2ae57aee41cfa1d8a8c8031bf24b
+65255b90c08b42f3806464cb7055e5627ba6fda0e3434f9156d01794486f
+34af284be09d1295fbe8f191071cac00b7ef807d917c2ba853e86d6dba76
+8305adcf1768feb2546bd6525effa400387529f06433ccd2a89fabc0f444
+f977ace45010f247efa3cd681bf8a1f721f8bb9fcedfbe27982bf6f5e734
+992b8b4e7945ed0949dfc819967c0fa361dcabd730e25a6751c23dc1ee6c
+bb79b2d55959d3e0a5ff4b014c563f82a9e4139c7205f86a20013c18f57d
+dad17c546ee0c0fdcf0bd144e4b1f843e4915d6a73640af84a77e6984815
+3e7d547a8f83482cabd9b00b76a00f60210adbb2a1387bad5062a5647158
+2a4a913d4fc9d239f62be9d96c093de44b9be938cbfe23f1bcde88e5f20e
+3c60032ef43d98a9514d83f398a1008bd619ac6ca4cae8217f1a2b4fca90
+f6f262b24d140bd4a59267034c4097acd43bfa4799aad47ea57daa2dd7d6
+c044542f51104443af9dd283c6e6c239b070de45de86b920819f4645837e
+0c5aacc53189ac5c2217c8d9ba4c39af982da5511cb243245d41dbe55a7b
+072fd7c9c7ed4775b2cde610d0222d1e530b2b9be5623957673f2ce795b2
+39f4d0329c84b8564ac1b1157ad96c36c9ccc342ad1d7fb0f3e275e90655
+41c348b0cdc2f3532a658a49bc94226648f93adbf061b0f45f4fcabc784c
+aa954e8b4f3d155a5d4d2bf439c74d471daf7f86679375611a18a018cf7a
+df60005346cb5becc19499b60fed938ba503522c65068383b6dea72c0f6d
+c7ebd9725a863075d2d024aadc26490a5f2d9d96ba79e933ca49cbea24aa
+92969fcbe76c0d72b917be5a61d12c91c29590582b2e4406af2c1f3e1a65
+0d79a834a52fc4d59b83db22c244f231cc80e55872b04f3dcb381b2bb0ce
+07ae47042c0bdfbd24a75476e6086821fcc0c8a5d4b13067dc7e16f7cc9d
+2ea38b715d0ef0e27e5b9654aa33831f8d2f9559a9523a2135880f3c71b0
+f7a82aa77494fbe46675c365e155f79dbffdc4c25434e20d5ab424d5629c
+cdebb30a30f2d661e4f5c14c588869a2dad5d7cc27cb57b47ec4a1f133e6
+a251de8f83607694f02ca3293b894d8a3f101dbee3745f30bf783ab5fec5
+b7897fe1c0ebcd6b18db59da56d02c18afed699fdd51e8f05cd598d27481
+bd78e674df8d4b99117dbc7e3be679394c21610d9e72b5188b64280719ca
+b5caf5069ceb51ed1374517b84368991d675bc75b7b85fcad15960312d39
+ca5e5a7e93ef7b3969e8fc8e56b497ac4d92c3562d7ae91ffede3ef5b7c1
+090cf2be265973e333b68816340235a271d02d3a73bb727b2505cd89b69b
+5dd16d9b6533d887ae8053bd2a2b8d7dc75b2b4e2a0e2f9b6cb397f37af5
+8ff8305f17f93f3b077906f15791ff902f0a56bdd1e6210f345dd5a319f1
+d0f9cff770eb59081f3240d7e087943eb0e01fe0fdfac66f640b1e011f98
+a481bbb080893b9812179f74f642d799ce0b3d1d076279e4842ee6dc2caa
+f1d629e74dee6e8b69478550179b569ec84d5fb6d67ff6972bde5ceda969
+3d23d4ed068fceccaea2dbd2632f3c49d4597353a18bbf7c6855c79fb82d
+9b8b8c6b04bdb100c64229c6ea661779135f1a02ac0636c12ba6b6a3c6d1
+c17d7a39724d40f8ceb529058e5a4c4c08a72a609041f3d27fdf38b2552e
+b267ebec26b9c08c15543eb49a92e81831ab348d8fbcf355da0b0ee6fef7
+2f58919cff2f687474be35374c704006a5371460484b308eac87c91a75b9
+ba943917b5b1610b87a6181081783405084482d7ad87aeae5bc2a6b754da
+3e634932b76ee7cdcf9ef5dd7c72ee6c5e5ab3a0cf2c80f138740f0875e1
+73a6c2c6e1d0ff0e9399ccb47c0b7e3aeadaf3671f5f7f7ca6c69cd122cc
+c7dace9773e52c5e4eb71d29c1d555fb701e26ca422be19412291b8fb056
+5a0ea04e44e7d9e338346e1a72435e681a68a6c2cc81db7537be151c0a55
+5c6f3e738bb5d38aea4e3968e50bc7234abf3f1ff3e238b893f0015c606e
+357434f571fd1753c24f08ed680e9399baaf288e0b0e6fbd9e25c0b73255
+de2657b5b020f8ff2762d174a4c1e97a23ef77de30f5c5d7a7bbfbb19f30
+6259b7e045fc04788d537d8f910d9f48148aa625b31821c6486514ccd65a
+6b6de552934e6ab0553b5905d340e165488291b20c247297a9e71849e7eb
+9a9a9b9a0bb24ef2576024752e34f1782c977aa834df246cda971e8c2694
+d9cacab04dd017a1ede0fe14c640094cc075e4612b081a780bdf33682b6d
+4cca2b4be44276f73f1ce8bf31d0566dcd3f2dc0fc01e6f2aaa5a7f10a9b
+8d081f9c85e7db69e071b1d651d72b6c908cc6f55c6874d5e908210269a8
+d4fb7fce7dcec1b4e72f618ee0901c8e7ff12e9952e8fb158f8ed5f2fa5c
+8cacbfc224d8e822af8337e605ded851836b19b428156b6c98f847ec0572
+8e4ed9604fb2b2c318e5b1c6443a473a2ca5f252a6942b15e94498435b1a
+ac772e6037037be9dab34d156d5c7fa3718b60a525e48b5d90159ba0581e
+ff26bf8cc53e482ae3c14fd1e2c93fefc2114112b691ed96969253fcbd8c
+e09eb5dcce5c8b69bdf02b2ee319a5b7fc0a6ecb73dc963c08247bb0d3bb
+a8c6338ea1780a046db51a4fc9c03157a22fed898c8fd91dd11d7de56a77
+ef75fc8bcf300078177915f7b117db34b94c0e561660bda1ffc22b689198
+644ee261a6432bb6494f1fb0325d0e6f30f21d3f295fe13734df44e9f371
+69be86b178c17ac302bc4eba551fe65846e5ae70161f1284056db2e1e479
+b35428e5f056c02bebb8e4909a75d239a9a61a9fa8403a75b2fdb8d2ce3d
+3895b24428a3d130e18ba41cc9385c895ca94457063369a94a6aedc64550
+7063e653e5f4515be551b641aa976af8ef8b97f5233db7687566989f300c
+f67160c5aba51ea6401a4e4b1d0385ccfad02d9bb686df7efc59ff9dc70f
+2e87ade5815718988ce7974493f12c5348870c68caafd3b0b47c40036ee0
+cdeb0b4b602cd20ea73791bc8b5d70083e0dc2c09b91611b55d5d6e068e4
+9e5e8d58b72e223c2835bdb2de24a0f97014275f11138735e0c0cbd24cc7
+d25952a1f9f7bdec8b511c421575d558cf73c0bdfd070e904704b6f11f2d
+4addb55ad0a3205ccb077859dc012f0d04aa7b19643051d5bb1d9bd03c36
+48dc2a86f345e04b1f819d94c3e674b02f3b2edf3c75cc92d7c077c354aa
+ea708992c2ed4accdab3171fe55f80b3680237f20cf09f02a751ddd58f98
+0b5bf7d486723e4bd62c58752bf467935067a19acaaa0bb3d8cc9cc2d4d8
+9893adc9fc763f2aa6ff5ee63deed7af3f7d7de7e0951dcd427ad5e1cabd
+55ba4395c59527d8fafa93a7da2f651feac2da892c05bdbacd457e3a1cee
+80ba9a413abbfc127b930af9d871d65e6babacc41842067998c3128b9d6a
+899c2fc77896d2d23a2a582ba1118f44096da5acf4cacf37bdeaedade9e8
+e5f7fd48e51db048995c5266cb15018fe1090537438bbdbfc9827df3808b
+ec065f0c6b4ce434358e41be5a7b96dd64648fd0324aa7d2d212f3f67351
+b9cd0382030be5031c4bb9adba9e95ebf09fcc2a1d9a61d6ca264a2eb665
+e7b2221ebacd784ae32b228fa6f121aefb7b9f71575b2acfdf152a1c2776
+c21fdaa8c20bc50f7b70067658441da36be526b99d975df65ab95aa707bc
+39d5401739b813d7158dbe3a1d19d158c8441e28632ea4226e0f240f6dc5
+c430832712c1a4eea0f4a160844c1226438606ec6a28b3312c6c63707fd8
+c3477dfd8f1eeeea0f16f4fbfe8daa10a52e6006feef88dc24fdbf185c8e
+ee0d93a14a5d40e9b3ebd47845390e1b6acb152dcab8dcaad0e0310a7877
+7560a2dacf8cfa5fd09b4ba2
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1356149_expected.pdf.0.png b/testing/resources/pixel/bug_1356149_expected.pdf.0.png
new file mode 100644
index 0000000..93827a8
--- /dev/null
+++ b/testing/resources/pixel/bug_1356149_expected.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.evt b/testing/resources/pixel/bug_1372651.evt
new file mode 100644
index 0000000..eb762eb
--- /dev/null
+++ b/testing/resources/pixel/bug_1372651.evt
@@ -0,0 +1,4 @@
+# Open the dropdown
+mousemove,140,145
+mousedown,left,140,145
+mouseup,left,140,145
diff --git a/testing/resources/pixel/bug_1372651.in b/testing/resources/pixel/bug_1372651.in
new file mode 100644
index 0000000..95b1948
--- /dev/null
+++ b/testing/resources/pixel/bug_1372651.in
@@ -0,0 +1,71 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /AcroForm <<
+    /DA (/Helv 0 Tf 0 g)
+    /DR <<
+      /Font <<
+        /Helv 4 0 R
+      >>
+    >>
+    /Fields [5 0 R 6 0 R]
+  >>
+  /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
+  /Annots [5 0 R 6 0 R]
+  /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /DV (Item3)
+  /F 4
+  /Ff 131072
+  /I [2]
+  /MK <<
+    /BG [1.0]
+  >>
+  /Opt [(Item1) (Item2) (Item3)]
+  /P 3 0 R
+  /Rect [70 135 150 155]
+  /T (Dropdown)
+  /V (Item3)
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /Ff 65536
+  /MK <<
+    /BG [0.5]
+  >>
+  /P 3 0 R
+  /Rect [50 90 150 110]
+  /T (Button)
+  /TU (Button Tooltip)
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1372651_expected.pdf.0.png b/testing/resources/pixel/bug_1372651_expected.pdf.0.png
new file mode 100644
index 0000000..c7386a1
--- /dev/null
+++ b/testing/resources/pixel/bug_1372651_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1372651_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1372651_expected_mac.pdf.0.png
new file mode 100644
index 0000000..a749d63
--- /dev/null
+++ b/testing/resources/pixel/bug_1372651_expected_mac.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
new file mode 100644
index 0000000..e7c07b7
--- /dev/null
+++ b/testing/resources/pixel/bug_1372651_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1383708.in b/testing/resources/pixel/bug_1383708.in
new file mode 100644
index 0000000..77114f6
--- /dev/null
+++ b/testing/resources/pixel/bug_1383708.in
@@ -0,0 +1,100 @@
+{{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 200]
+  /Resources 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+100 0 0 20 50 50 cm
+/I1 Do
+Q
+q
+80 0 0 20 50 100 cm
+/I2 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /ProcSet [/PDF /ImageB /ImageC /ImageI]
+  /XObject <<
+    /I1 6 0 R
+    /I2 8 0 R
+  >>
+>>
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 1
+  /ColorSpace [/Indexed /DeviceRGB 1 7 0 R]
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Height 5
+  /Width 177
+  {{streamlen}}
+>>
+stream
+789cfbff1f1b6860c00eb02afedf80431887215c0094ac4349
+endstream
+endobj
+{{object 7 0}} <<
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+789cbb70e5c6ffffffb90017e80584
+endstream
+endobj
+{{object 8 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace [/Indexed /DeviceRGB 39 9 0 R]
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Height 15
+  /Width 58
+  {{streamlen}}
+>>
+stream
+789c9d928b12822010454d7c5510a2166a61befdff3f0c310151a7c93bb0
+ecec70b82c03845c411405f09086b21c0e814e68dba17384ecdf5cfda264
+592a935a818d9b0196b9cd3e39174cd8470830847c9d532ebae74c7ed73c
+a514304ad37ce539c6694e984126711c3f6f3c243ffb945591e49de77997
+2b0f5d0e8dedca77e93925f82185b75f65c793bcce5c808df14564ddf083
+1b9ed57d146062a9d6a418f2f27a0ba03d49b560975cb621442a9cd47e51
+1475822ba236e8e75beab08d8ff4bf3e7d492308
+endstream
+endobj
+{{object 9 0}} <<
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+789c01780087ff2c3339ffcc00ffffcca97600ebd17fe7c855ffdd48cc99
+00f6e9bfffd31dd4a92398873dffffffffee93655e3eb68300ebd067d8bf
+7fdda900ffe361be8b00ffcf0dffd831ffe87ae3bd3dfff3aaddc37fbe91
+18d9a90f333333d5a100fff8beffffddae7b00c89400e7cd7feab600deb2
+24d4a000dba70092a54914
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1383708_expected.pdf.0.png b/testing/resources/pixel/bug_1383708_expected.pdf.0.png
new file mode 100644
index 0000000..800eedc
--- /dev/null
+++ b/testing/resources/pixel/bug_1383708_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1383708_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1383708_expected_skia.pdf.0.png
new file mode 100644
index 0000000..5487865
--- /dev/null
+++ b/testing/resources/pixel/bug_1383708_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_2_expected.pdf.0.png b/testing/resources/pixel/bug_1388_2_expected.pdf.0.png
index 422a7e2..347f3e0 100644
--- a/testing/resources/pixel/bug_1388_2_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_1388_2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_2_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_1388_2_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..2293e85
--- /dev/null
+++ b/testing/resources/pixel/bug_1388_2_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_2_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1388_2_expected_mac.pdf.0.png
deleted file mode 100644
index b1a4843..0000000
--- a/testing/resources/pixel/bug_1388_2_expected_mac.pdf.0.png
+++ /dev/null
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_2_expected_win.pdf.0.png b/testing/resources/pixel/bug_1388_2_expected_win.pdf.0.png
deleted file mode 100644
index 58a14f4..0000000
--- a/testing/resources/pixel/bug_1388_2_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_3_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_1388_3_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..3727440
--- /dev/null
+++ b/testing/resources/pixel/bug_1388_3_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_3_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1388_3_expected_mac.pdf.0.png
deleted file mode 100644
index bb97a96..0000000
--- a/testing/resources/pixel/bug_1388_3_expected_mac.pdf.0.png
+++ /dev/null
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_1388_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1388_expected_skia.pdf.0.png
new file mode 100644
index 0000000..eb348c8
--- /dev/null
+++ b/testing/resources/pixel/bug_1388_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1395648.in b/testing/resources/pixel/bug_1395648.in
new file mode 100644
index 0000000..6c38fb1
--- /dev/null
+++ b/testing/resources/pixel/bug_1395648.in
@@ -0,0 +1,67 @@
+{{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 80 80]
+  /Resources <<
+    /XObject <<
+      /Mask 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+40 5 0 60 10 10 cm
+/Mask Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Height 50
+  /SMask 6 0 R
+  /Width 50
+  {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Height 50
+  /Matte [0.0 0.2 1]
+  /Width 50
+  {{streamlen}}
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809246c128a027e0020065c9c90d
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1395648_expected.pdf.0.png b/testing/resources/pixel/bug_1395648_expected.pdf.0.png
new file mode 100644
index 0000000..4aca65e
--- /dev/null
+++ b/testing/resources/pixel/bug_1395648_expected.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
new file mode 100644
index 0000000..e62f06b
--- /dev/null
+++ b/testing/resources/pixel/bug_1395648_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1396266.in b/testing/resources/pixel/bug_1396266.in
new file mode 100644
index 0000000..fdeaf43
--- /dev/null
+++ b/testing/resources/pixel/bug_1396266.in
@@ -0,0 +1,98 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+
+% A page with scaled copies of a masked image. Any scaled image with an area
+% less than 8 times the stencil mask's original area (64x64 * 8 = 32,768) forces
+% bilinear interpolation of the stencil mask, which triggers the bug.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 432 288]
+  /Resources <<
+    /XObject <<
+      /Masked 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+
+% 32x32 image has an area of 1,024, so it is interpolated.
+q
+  32 0 0 32 64 240 cm
+  /Masked Do
+Q
+
+% 64x64 image has an area of 4,096, so it is interpolated.
+q
+  64 0 0 64 48 160 cm
+  /Masked Do
+Q
+
+% 128x128 image has an area of 16,384, so it is interpolated.
+q
+  128 0 0 128 16 16 cm
+  /Masked Do
+Q
+
+% 256x256 image has an area of 65,536, so it is not interpolated.
+q
+  256 0 0 256 160 16 cm
+  /Masked Do
+Q
+
+endstream
+endobj
+
+% A 3x3 base image with a stencil mask.
+{{object 5 0}} <<
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter /ASCIIHexDecode
+  /Height 3
+  /Mask 6 0 R
+  /Width 3
+  {{streamlen}}
+>>
+stream
+B8C3E9 7793DB C3CEEF
+8CADF2 1B74E8 BFD5FB
+C2D3FA 9FBDF8 D9E5FC
+endstream
+endobj
+
+% A 64x64 stencil mask with horizontal and vertical lines.
+{{object 6 0}} <<
+  /Subtype /Image
+  /BitsPerComponent 1
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Height 64
+  /ImageMask true
+  /Width 64
+  {{streamlen}}
+>>
+stream
+78DA63608080FAFF10E000E5C3687FA8388CF6818A63D0500330E802A83C41BA1EAA9E105D03554F
+36CD0C35876CFA0FD41CDAD3F2FF694D03002512CA0A
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1396266_expected.pdf.0.png b/testing/resources/pixel/bug_1396266_expected.pdf.0.png
new file mode 100644
index 0000000..6cacddf
--- /dev/null
+++ b/testing/resources/pixel/bug_1396266_expected.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
new file mode 100644
index 0000000..4d483cb
--- /dev/null
+++ b/testing/resources/pixel/bug_1396266_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1402_expected.pdf.0.png b/testing/resources/pixel/bug_1402_expected.pdf.0.png
index cf913bd..751d63f 100644
--- a/testing/resources/pixel/bug_1402_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_1402_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1402_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1402_expected_mac.pdf.0.png
deleted file mode 100644
index 9602820..0000000
--- a/testing/resources/pixel/bug_1402_expected_mac.pdf.0.png
+++ /dev/null
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
new file mode 100644
index 0000000..9d2c92e
--- /dev/null
+++ 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_1430333.in b/testing/resources/pixel/bug_1430333.in
new file mode 100644
index 0000000..e715f8f
--- /dev/null
+++ b/testing/resources/pixel/bug_1430333.in
@@ -0,0 +1,44 @@
+{{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 200]
+  /Resources <<
+    /ExtGState <<
+      /A3 <<
+        /Type /ExtGState
+        /CA 0.5
+        /ca 0.5
+      >>
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+/A3 gs
+-100 10 m
+140 20 l
+b
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1430333_expected.pdf.0.png b/testing/resources/pixel/bug_1430333_expected.pdf.0.png
new file mode 100644
index 0000000..b9fc3e9
--- /dev/null
+++ b/testing/resources/pixel/bug_1430333_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1430333_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1430333_expected_skia.pdf.0.png
new file mode 100644
index 0000000..e6a1827
--- /dev/null
+++ b/testing/resources/pixel/bug_1430333_expected_skia.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_1469.in b/testing/resources/pixel/bug_1469.in
new file mode 100644
index 0000000..8eb970d
--- /dev/null
+++ b/testing/resources/pixel/bug_1469.in
@@ -0,0 +1,98 @@
+{{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 300 100]
+  /Resources <<
+    /XObject <<
+      % All 3 images are BGRA with the same data but interpreted differently.
+      /ImNoSMaskInData 5 0 R
+      /ImSMaskInData0 6 0 R
+      /ImSMaskInData1 7 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+0 1 0 rg
+0 0 300 100 re f
+Q
+q
+64 0 0 64 0 0 cm
+/ImNoSMaskInData Do
+Q
+q
+64 0 0 64 100 0 cm
+/ImSMaskInData0 Do
+Q
+q
+64 0 0 64 200 0 cm
+/ImSMaskInData1 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter /JPXDecode
+  /Height 64
+  % No /SMaskInData here.
+  /Width 64
+  {{streamlen}}
+>>
+stream
+{{include ../bug_1469.jp2}}
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter /JPXDecode
+  /Height 64
+  /SMaskInData 0
+  /Width 64
+  {{streamlen}}
+>>
+stream
+{{include ../bug_1469.jp2}}
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter /JPXDecode
+  /Height 64
+  /SMaskInData 1
+  /Width 64
+  {{streamlen}}
+>>
+stream
+{{include ../bug_1469.jp2}}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1469_expected.pdf.0.png b/testing/resources/pixel/bug_1469_expected.pdf.0.png
new file mode 100644
index 0000000..a87b79d
--- /dev/null
+++ b/testing/resources/pixel/bug_1469_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1491.in b/testing/resources/pixel/bug_1491.in
new file mode 100644
index 0000000..b973b2b
--- /dev/null
+++ b/testing/resources/pixel/bug_1491.in
@@ -0,0 +1,44 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 6 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Pages
+  /Parent 2 0 R
+  /Count 1
+  /Kids [ 4 0 R ]
+>>
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 3 0 R
+  /Contents 5 0 R
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+0.5 0.5 0.5 rg
+50 50 100 100 re B
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /Action
+  /S /GoTo
+  /D [ 3 0 R /Fit ]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1491_expected.pdf.0.png b/testing/resources/pixel/bug_1491_expected.pdf.0.png
new file mode 100644
index 0000000..21e9375
--- /dev/null
+++ b/testing/resources/pixel/bug_1491_expected.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_1491_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1491_expected_skia.pdf.0.png
new file mode 100644
index 0000000..69c5f08
--- /dev/null
+++ b/testing/resources/pixel/bug_1491_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1519.in b/testing/resources/pixel/bug_1519.in
new file mode 100644
index 0000000..d681156
--- /dev/null
+++ b/testing/resources/pixel/bug_1519.in
@@ -0,0 +1,48 @@
+{{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 400 400]
+  /Resources <<
+    /ExtGState <<
+      /GS1 <<
+        /ca 0.5
+      >>
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+/GS1 gs
+0 0 1 rg
+0 0 m
+130 130 l
+150 150 180 180 200 200 c
+400 400 l
+300 300 l
+400 400 l
+300 300 l
+f
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1519_expected.pdf.0.png b/testing/resources/pixel/bug_1519_expected.pdf.0.png
new file mode 100644
index 0000000..4a6cc19
--- /dev/null
+++ b/testing/resources/pixel/bug_1519_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1546.in b/testing/resources/pixel/bug_1546.in
new file mode 100644
index 0000000..fb423d0
--- /dev/null
+++ b/testing/resources/pixel/bug_1546.in
@@ -0,0 +1,77 @@
+{{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
+  /Resources <<
+    /Shading <<
+      /S0 5 0 R
+    >>
+  >>
+  /Contents 4 0 R
+  /MediaBox [0 0 200 300]
+>>
+endobj
+{{object 4 0}} <<
+  {streamlen}}
+>>
+stream
+/S0 sh
+endstream
+endobj
+{{object 5 0}} <<
+  /ShadingType 4
+  /BitsPerComponent 8
+  /BitsPerCoordinate 16
+  /BitsPerFlag 8
+  /ColorSpace /DeviceRGB
+  /Decode [-32768 32767 -32768 32767 0 1]
+  /Filter /ASCIIHexDecode
+  /Function 6 0 R
+  {{streamlen}}
+>>
+stream
+00800a800a0000800a80c8ff00806480c800
+endstream
+endobj
+{{object 6 0}} <<
+  /FunctionType 3
+  /Bounds [0.5]
+  /Domain [0 1]
+  /Encode [0 1 0 1]
+  /Functions [7 0 R 8 0 R]
+  /Range [0 1 0 1 0 1]
+>>
+endobj
+{{object 7 0}} <<
+  /FunctionType 2
+  /C0 [1 0 0]
+  /C1 [0 1 0]
+  /Domain [0 1]
+  /N 1
+  /Range [0 1 0 1 0 1]
+>>
+endobj
+{{object 8 0}} <<
+  /FunctionType 2
+  /C0 [0 1 0]
+  /C1 [0 0 1]
+  /Domain [0 1]
+  /N 1
+  /Range [0 1 0 1 0 1]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1546_expected.pdf.0.png b/testing/resources/pixel/bug_1546_expected.pdf.0.png
new file mode 100644
index 0000000..e993bf1
--- /dev/null
+++ b/testing/resources/pixel/bug_1546_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1571.in b/testing/resources/pixel/bug_1571.in
new file mode 100644
index 0000000..b4a94e1
--- /dev/null
+++ b/testing/resources/pixel/bug_1571.in
@@ -0,0 +1,112 @@
+{{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
+  /MediaBox [0 0 200 200]
+  /Contents 4 0 R
+  /Resources <<
+    /ProcSet [/PDF /ImageB /ImageC /ImageI]
+    /ExtGState <<
+      /GS1 5 0 R
+      /GS3 6 0 R
+    >>
+    /Pattern <<
+      /Pa2 7 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+1 0 0 1 50 100 cm
+1 1 1 RG
+/GS3 gs
+/Pattern cs
+/Pa2 scn
+/GS1 gs
+-7 7 m
+-7 -7 l
+-7 -7 l
+7 -7 l
+7 -7 l
+7 7 l
+7 7 l
+-7 7 l
+h
+B*
+Q
+q
+1 0 0 1 150 100 cm
+/Pattern cs
+/Pa2 scn
+/GS1 gs
+-7 7 m
+-7 -7 l
+-7 -7 l
+7 -7 l
+7 -7 l
+7 7 l
+7 7 l
+-7 7 l
+h
+B*
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /ExtGState
+  /ca 1
+>>
+endobj
+{{object 6 0}} <<
+  /Type /ExtGState
+  /CA 0
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Pattern
+  /PaintType 1
+  /PatternType 1
+  /TilingType 2
+  /BBox [0 0 0.25 0.25]
+  /Matrix [1.414 -1.414 1.414 1.414 -95.043 -381.294]
+  /PaintType 1
+  /Resources <<
+    % Acrobat requires this dictionary, even if it is empty.
+  >>
+  /XStep 0.25
+  /YStep 0.25
+  {{streamlen}}
+>>
+stream
+/DeviceRGB CS 0.941 0 0 SC
+/DeviceRGB cs 1 1 1 sc
+0 0 0.25 0.25 re
+f
+0.094 w
+0 0 m
+0.25 0 l
+0.094 w
+0.125 0 m
+0.125 0.25 l
+S
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1571_expected.pdf.0.png b/testing/resources/pixel/bug_1571_expected.pdf.0.png
new file mode 100644
index 0000000..e3d54b8
--- /dev/null
+++ b/testing/resources/pixel/bug_1571_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1638.in b/testing/resources/pixel/bug_1638.in
new file mode 100644
index 0000000..f6dea38
--- /dev/null
+++ b/testing/resources/pixel/bug_1638.in
@@ -0,0 +1,57 @@
+{{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 400 400]
+  /Resources <<
+    /ExtGState <<
+      /GS1 <<
+        /ca 1.0
+      >>
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+/GS1 gs
+0 0 1 rg
+% The 1st sub path.
+50 350 m
+200 320 l
+50 350 l
+% The 2nd sub path.
+100 300 m
+50 300 l
+200 300 l
+300 200 l
+270 230 l
+% The 3rd sub path.
+200 50 m
+350 50 l
+200 50 l
+200 100 l
+200 50 l
+f
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1638_expected.pdf.0.png b/testing/resources/pixel/bug_1638_expected.pdf.0.png
new file mode 100644
index 0000000..23a39d5
--- /dev/null
+++ b/testing/resources/pixel/bug_1638_expected.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_1638_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1638_expected_skia.pdf.0.png
new file mode 100644
index 0000000..7ef152f
--- /dev/null
+++ b/testing/resources/pixel/bug_1638_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1639_1.in b/testing/resources/pixel/bug_1639_1.in
new file mode 100644
index 0000000..f283af7
--- /dev/null
+++ b/testing/resources/pixel/bug_1639_1.in
@@ -0,0 +1,50 @@
+{{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 200]
+  /Resources <<
+    /ExtGState <<
+      /GS1 <<
+        /ca 1.0
+      >>
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+/GS1 gs
+0 0 1 rg
+% A horizontal path.
+20 180 m
+180 180 l
+% A vertical path.
+50 160 m
+50 100 l
+% A diagonal path.
+20 50 m
+180 100 l
+f
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1639_1_expected.pdf.0.png b/testing/resources/pixel/bug_1639_1_expected.pdf.0.png
new file mode 100644
index 0000000..2734462
--- /dev/null
+++ b/testing/resources/pixel/bug_1639_1_expected.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_1639_1_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1639_1_expected_skia.pdf.0.png
new file mode 100644
index 0000000..fa53f12
--- /dev/null
+++ b/testing/resources/pixel/bug_1639_1_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1693.in b/testing/resources/pixel/bug_1693.in
new file mode 100644
index 0000000..e59b5f8
--- /dev/null
+++ b/testing/resources/pixel/bug_1693.in
@@ -0,0 +1,101 @@
+{{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
+  /MediaBox [0 0 200 200]
+  /Resources <<
+    /ProcSet [/PDF /ImageC]
+    /ExtGState <<
+      /G3 4 0 R
+      /G8 5 0 R
+    >>
+    /Pattern <<
+      /P7 6 0 R
+    >>
+  >>
+  /Contents 7 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /BM /Normal
+  /ca 1
+>>
+endobj
+{{object 5 0}} <<
+  /BM /Normal
+  /ca .5
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Pattern
+  /PaintType 1
+  /PatternType 1
+  /TilingType 1
+  /BBox [0 0 400 400]
+  /Matrix [256 0 0 32 0 0]
+  /XStep 512
+  /YStep 512
+  /Resources <<
+    /ProcSet [/PDF /ImageC]
+    /ExtGState <<
+      /G3 4 0 R
+    >>
+    /XObject <<
+      /X4 8 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 0 0 -8 0 8 cm
+/G3 gs
+/X4 Do
+Q
+endstream
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+1 1 1 RG
+1 1 1 rg
+/G3 gs
+/Pattern CS
+/Pattern cs
+/P7 SCN
+/P7 scn
+/G8 gs
+0 0 200 150 re
+f
+endstream
+endobj
+{{object 8 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1693_expected.pdf.0.png b/testing/resources/pixel/bug_1693_expected.pdf.0.png
new file mode 100644
index 0000000..d65bd0d
--- /dev/null
+++ b/testing/resources/pixel/bug_1693_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1723.in b/testing/resources/pixel/bug_1723.in
new file mode 100644
index 0000000..aac04d2
--- /dev/null
+++ b/testing/resources/pixel/bug_1723.in
@@ -0,0 +1,61 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+  /MediaBox [0 0 300 400]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents [4 0 R]
+  /Resources <<
+    /ColorSpace <<
+      /DefaultCMYK /DeviceRGB
+    >>
+    /XObject <<
+      /Img 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+200 0 0 300 50 50 cm
+/Img Do
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceCMYK
+  /Filter /ASCIIHexDecode
+  /Height 8
+  /Width 8
+  {{streamlen}}
+>>
+stream
+FF000000 FF000000 FF000000 FF000000   00FF0000 00FF0000 00FF0000 00FF0000
+FF000000 FF000000 FF000000 FF000000   00FF0000 00FF0000 00FF0000 00FF0000
+FF000000 FF000000 FF000000 FF000000   00FF0000 00FF0000 00FF0000 00FF0000
+FF000000 FF000000 FF000000 FF000000   00FF0000 00FF0000 00FF0000 00FF0000
+
+0000FF00 0000FF00 0000FF00 0000FF00   000000FF 000000FF 000000FF 000000FF
+0000FF00 0000FF00 0000FF00 0000FF00   000000FF 000000FF 000000FF 000000FF
+0000FF00 0000FF00 0000FF00 0000FF00   000000FF 000000FF 000000FF 000000FF
+0000FF00 0000FF00 0000FF00 0000FF00   000000FF 000000FF 000000FF 000000FF
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1723_expected.pdf.0.png b/testing/resources/pixel/bug_1723_expected.pdf.0.png
new file mode 100644
index 0000000..46dceb7
--- /dev/null
+++ b/testing/resources/pixel/bug_1723_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1733.in b/testing/resources/pixel/bug_1733.in
new file mode 100644
index 0000000..9812a03
--- /dev/null
+++ b/testing/resources/pixel/bug_1733.in
@@ -0,0 +1,107 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 100 100]
+  /Count 2
+  /Kids [3 0 R 4 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 5 0 R
+  /Resources <<
+    /XObject 9 0 R
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 5 0 R
+  /Resources <<
+    /XObject 10 0 R
+  >>
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+100 0 0 100 0 0 cm
+/X1 Do
+Q
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 1
+  /Height 1
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter /ASCIIHexDecode
+  {{streamlen}}
+>>
+stream
+FF0000
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 1
+  /Height 1
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter /ASCIIHexDecode
+  {{streamlen}}
+>>
+stream
+FFFF00
+endstream
+endobj
+% This object stream contains two different copies of object 9 and object 10.
+% To disambiguate them, a parser must use object 11 0 below, the cross-reference
+% stream, to look up the type 2 entries. Those entries reference this object,
+% and the position of the objects within this object.
+{{object 8 0}} <<
+  /Type /ObjStm
+  /N 4
+  /First 20
+  {{streamlen}}
+>>
+stream
+9 0 9 13 10 26 10 39<</X1 6 0 R>><</X1 7 0 R>><</X1 6 0 R>><</X1 7 0 R>>
+endstream
+endobj
+{{object 11 0}} <<
+  /Type /XRef
+  /Filter /ASCIIHexDecode
+  /Root 1 0 R
+  /Size 11
+  /W [1 2 2]
+  {{streamlen}}
+>>
+stream
+00 0000 FFFF
+01 000F 0000
+01 0044 0000
+01 00A3 0000
+01 0110 0000
+01 017E 0000
+01 01CF 0000
+01 028B 0000
+01 046A 0000
+02 0008 0000
+02 0008 0003
+endstream
+endobj
+{{startxrefobj 11 0}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1733_expected.pdf.0.png b/testing/resources/pixel/bug_1733_expected.pdf.0.png
new file mode 100644
index 0000000..d96c847
--- /dev/null
+++ b/testing/resources/pixel/bug_1733_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1733_expected.pdf.1.png b/testing/resources/pixel/bug_1733_expected.pdf.1.png
new file mode 100644
index 0000000..d1490da
--- /dev/null
+++ b/testing/resources/pixel/bug_1733_expected.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1746.in b/testing/resources/pixel/bug_1746.in
new file mode 100644
index 0000000..ca2a4b2
--- /dev/null
+++ b/testing/resources/pixel/bug_1746.in
@@ -0,0 +1,122 @@
+{{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 <<
+    /ExtGState <<
+      /GS0 8 0 R
+    >>
+    /Font <<
+      /F1 5 0 R
+    >>
+    /XObject <<
+      /Img 9 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+/GS0 gs
+q
+0 1 0 rg
+BT
+/F1 2 Tf
+1 0 0 1 50 50 Tm
+(a)Tj
+ET
+Q
+q
+0 0 1 rg
+BT
+/F1 2 Tf
+1 0 0 1 72 80 Tm
+(a)Tj
+ET
+Q
+Q
+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 /ExtGState
+  /CA 0.5
+  /ca 0.5
+>>
+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_1746_expected.pdf.0.png b/testing/resources/pixel/bug_1746_expected.pdf.0.png
new file mode 100644
index 0000000..08c41d1
--- /dev/null
+++ b/testing/resources/pixel/bug_1746_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1746_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1746_expected_skia.pdf.0.png
new file mode 100644
index 0000000..0ab21f9
--- /dev/null
+++ b/testing/resources/pixel/bug_1746_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1750.in b/testing/resources/pixel/bug_1750.in
new file mode 100644
index 0000000..ce0f46f
--- /dev/null
+++ b/testing/resources/pixel/bug_1750.in
@@ -0,0 +1,83 @@
+{{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 <<
+    /XObject <<
+      /Img1 6 0 R
+      /Img2 8 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+60 0 0 60 20 20 cm
+/Img1 Do
+Q
+q
+60 0 0 60 120 20 cm
+/Img2 Do
+Q
+endstream
+endobj
+{{object 5 0}} [
+  /Indexed
+  /DeviceGray
+  1
+  <88 cc>
+]
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 1
+  /ColorSpace 5 0 R
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+0000
+endstream
+endobj
+{{object 7 0}} [
+  /Indexed
+  /DeviceGray
+  3
+  <11 44 88 cc>
+]
+endobj
+{{object 8 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 1
+  /ColorSpace 7 0 R
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+0000
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1750_expected.pdf.0.png b/testing/resources/pixel/bug_1750_expected.pdf.0.png
new file mode 100644
index 0000000..b574e46
--- /dev/null
+++ b/testing/resources/pixel/bug_1750_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1752.in b/testing/resources/pixel/bug_1752.in
new file mode 100644
index 0000000..0ba4a41
--- /dev/null
+++ b/testing/resources/pixel/bug_1752.in
@@ -0,0 +1,75 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 200 200]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /NotoSansRegular
+  /Encoding /WinAnsiEncoding
+  /FirstChar 65
+  /LastChar 71
+  /FontDescriptor 5 0 R
+  /Name /F1
+  /Widths [250 250 250 250 800 800 800]
+>>
+endobj
+{{object 5 0}} <<
+  /Type /FontDescriptor
+  /CapHeight 750
+  /Descent -250
+  /Flags 32
+  /FontBBox [-503 -250 1240 750]
+  /FontFile2 7 0 R
+  /FontName /NotoSansRegular
+  /FontWeight 400
+  /ItalicAngle 0
+  /StemV 52
+>>
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 100 Td
+/F1 70 Tf
+(ABCD) Tj
+0 -80 Td
+(EFG) Tj
+ET
+endstream
+endobj
+{{object 7 0}} <<
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+  /Length1 11200
+>>
+stream
+{{include ../bug_1752_truetype_font.fragment}}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1752_expected.pdf.0.png b/testing/resources/pixel/bug_1752_expected.pdf.0.png
new file mode 100644
index 0000000..ed9be10
--- /dev/null
+++ b/testing/resources/pixel/bug_1752_expected.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_mac.pdf.0.png b/testing/resources/pixel/bug_1752_expected_mac.pdf.0.png
new file mode 100644
index 0000000..a8cb3ba
--- /dev/null
+++ b/testing/resources/pixel/bug_1752_expected_mac.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
new file mode 100644
index 0000000..6c129ef
--- /dev/null
+++ b/testing/resources/pixel/bug_1752_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1772.in b/testing/resources/pixel/bug_1772.in
new file mode 100644
index 0000000..026b713
--- /dev/null
+++ b/testing/resources/pixel/bug_1772.in
@@ -0,0 +1,93 @@
+{{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 100 100]
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /ColorSpace <<
+      /CS1 [/Pattern]
+    >>
+    /Font <<
+      /F1 5 0 R
+    >>
+    /Pattern <<
+      /P1 6 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+/CS1 CS
+/P1 SCN
+/F1 1 Tf
+1 Tr
+20 0 0 20 20 30 Tm
+(S) Tj
+ET
+BT
+/CS1 CS
+/P1 SCN
+/F1 4 Tf
+1 Tr
+5 0 0 5 50 80 Tm
+(S) Tj
+ET
+BT
+/CS1 CS
+/P1 SCN
+/F1 20 Tf
+1 Tr
+1 0 0 1 70 40 Tm
+(S) Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Pattern
+  /PatternType 2
+  /Shading 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+  /ShadingType 3
+  /ColorSpace /DeviceRGB
+  /Coords [0.0 0.0 0.0 0.0 0.0 0.005] % Concentric circles
+  /Function 8 0 R
+  /Extend [true true]
+ >>
+endobj
+{{object 8 0}} <<
+  /FunctionType 2
+  /Domain [0.0 1.0]
+  /C0 [0 0 0]
+  /C1 [0 1 0]
+  /N 1
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1772_expected.pdf.0.png b/testing/resources/pixel/bug_1772_expected.pdf.0.png
new file mode 100644
index 0000000..7056d87
--- /dev/null
+++ b/testing/resources/pixel/bug_1772_expected.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_1772_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1772_expected_skia.pdf.0.png
new file mode 100644
index 0000000..477b2a2
--- /dev/null
+++ b/testing/resources/pixel/bug_1772_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1774.in b/testing/resources/pixel/bug_1774.in
new file mode 100644
index 0000000..d80b429
--- /dev/null
+++ b/testing/resources/pixel/bug_1774.in
@@ -0,0 +1,47 @@
+{{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
+  /MediaBox [0 0 200 200]
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+0.001 w
+30 150 m
+170 150 l
+S
+Q
+q
+0.01 w
+30 95 m
+170 95 l
+S
+Q
+q
+0.1 w
+30 40 m
+170 40 l
+S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1774_expected.pdf.0.png b/testing/resources/pixel/bug_1774_expected.pdf.0.png
new file mode 100644
index 0000000..32bcba3
--- /dev/null
+++ b/testing/resources/pixel/bug_1774_expected.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_1774_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1774_expected_skia.pdf.0.png
new file mode 100644
index 0000000..40ec74e
--- /dev/null
+++ b/testing/resources/pixel/bug_1774_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1822.in b/testing/resources/pixel/bug_1822.in
new file mode 100644
index 0000000..4075bb7
--- /dev/null
+++ b/testing/resources/pixel/bug_1822.in
@@ -0,0 +1,34 @@
+{{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 400 400]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+[1.8 1.8] 96636760 d
+1 i
+100 300 m
+200 300 l
+S
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1822_expected.pdf.0.png b/testing/resources/pixel/bug_1822_expected.pdf.0.png
new file mode 100644
index 0000000..913f33f
--- /dev/null
+++ b/testing/resources/pixel/bug_1822_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1822_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1822_expected_skia.pdf.0.png
new file mode 100644
index 0000000..2467b79
--- /dev/null
+++ b/testing/resources/pixel/bug_1822_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1845.in b/testing/resources/pixel/bug_1845.in
new file mode 100644
index 0000000..9fd8052
--- /dev/null
+++ b/testing/resources/pixel/bug_1845.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 96 96]
+  /Resources <<
+    /XObject <<
+      /X0 5 0 R
+    >>
+  >>
+>>
+endobj
+
+% Cross formed by red horizontal rectangle overlaid with blue vertical
+% rectangle.
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+  % Red horizontal rectangle defined by clip.
+  8 32 80 32 re W* n
+  1 0 0 rg
+  0 0 96 96 re f
+Q
+q
+  % Blue vertical rectangle defined by masked image.
+  32 0 0 80 32 8 cm
+  /X0 Do
+Q
+endstream
+endobj
+
+% Single pixel image with /SMask to trigger masked image rendering.
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 1
+  /Height 1
+  /ColorSpace /DeviceRGB
+  /BitsPerComponent 8
+  /SMask 6 0 R
+  /Filter /ASCIIHexDecode
+  {{streamlen}}
+>>
+stream
+00 00 FF
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 1
+  /Height 1
+  /ColorSpace /DeviceGray
+  /BitsPerComponent 8
+  /Filter /ASCIIHexDecode
+  {{streamlen}}
+>>
+stream
+80
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1845_expected.pdf.0.png b/testing/resources/pixel/bug_1845_expected.pdf.0.png
new file mode 100644
index 0000000..b246272
--- /dev/null
+++ b/testing/resources/pixel/bug_1845_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1847.in b/testing/resources/pixel/bug_1847.in
new file mode 100644
index 0000000..42d4ef9
--- /dev/null
+++ b/testing/resources/pixel/bug_1847.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 4
+  /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/pixel/bug_1847_expected.pdf.0.png b/testing/resources/pixel/bug_1847_expected.pdf.0.png
new file mode 100644
index 0000000..1e9d7fb
--- /dev/null
+++ b/testing/resources/pixel/bug_1847_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1847_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1847_expected_skia.pdf.0.png
new file mode 100644
index 0000000..f201f8e
--- /dev/null
+++ b/testing/resources/pixel/bug_1847_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883.in b/testing/resources/pixel/bug_1883.in
new file mode 100644
index 0000000..79593bb
--- /dev/null
+++ b/testing/resources/pixel/bug_1883.in
@@ -0,0 +1,92 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 4
+  /Kids [3 0 R 4 0 R 5 0 R 6 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Annots [7 0 R 8 0 R]
+  /MediaBox [0 0 612 792]
+  /Parent 2 0 R
+  /Rotate 0
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Annots [7 0 R 8 0 R]
+  /MediaBox [0 0 792 612]
+  /Parent 2 0 R
+  /Rotate 90
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Page
+  /Annots [7 0 R 8 0 R]
+  /MediaBox [0 0 612 792]
+  /Parent 2 0 R
+  /Rotate 180
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Page
+  /Annots [7 0 R 8 0 R]
+  /MediaBox [0 0 792 612]
+  /Parent 2 0 R
+  /Rotate 270
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Annot
+  /Subtype /Line
+  /AP <<
+    /N 9 0 R
+  >>
+  % NoRotate
+  /F 16
+  /L [100 50 100 150]
+  /P 3 0 R
+  /Rect [90 370 270 500]
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Annot
+  /Subtype /Line
+  /AP <<
+    /N 9 0 R
+  >>
+  /F 0
+  /L [100 50 100 150]
+  /P 3 0 R
+  /Rect [190 470 370 600]
+>>
+endobj
+{{object 9 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 360 130]
+  {{streamlen}}
+>>
+stream
+10 w
+1 0 0 RG
+30 65 m
+150 65 l
+S
+120 45 m
+150 65 l
+120 85 l
+S
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1883_expected.pdf.0.png b/testing/resources/pixel/bug_1883_expected.pdf.0.png
new file mode 100644
index 0000000..4bc6992
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883_expected.pdf.1.png b/testing/resources/pixel/bug_1883_expected.pdf.1.png
new file mode 100644
index 0000000..f18c1d7
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883_expected.pdf.2.png b/testing/resources/pixel/bug_1883_expected.pdf.2.png
new file mode 100644
index 0000000..a3093ff
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected.pdf.2.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883_expected.pdf.3.png b/testing/resources/pixel/bug_1883_expected.pdf.3.png
new file mode 100644
index 0000000..e6e3988
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected.pdf.3.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_1883_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1883_expected_skia.pdf.0.png
new file mode 100644
index 0000000..8560bfe
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883_expected_skia.pdf.1.png b/testing/resources/pixel/bug_1883_expected_skia.pdf.1.png
new file mode 100644
index 0000000..0660877
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected_skia.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883_expected_skia.pdf.2.png b/testing/resources/pixel/bug_1883_expected_skia.pdf.2.png
new file mode 100644
index 0000000..f0050f8
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected_skia.pdf.2.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883_expected_skia.pdf.3.png b/testing/resources/pixel/bug_1883_expected_skia.pdf.3.png
new file mode 100644
index 0000000..88d3e25
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected_skia.pdf.3.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1922.in b/testing/resources/pixel/bug_1922.in
new file mode 100644
index 0000000..c7b005b
--- /dev/null
+++ b/testing/resources/pixel/bug_1922.in
@@ -0,0 +1,66 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 100 100]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 5 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /TimesNewRoman,Bold
+  /Encoding /WinAnsiEncoding
+  /FirstChar 32
+  /LastChar 121
+  /Widths [
+    230.769 307.692 538.462 538.462 538.462 1153.846 846.154 307.692
+    307.692 307.692 538.462 538.462 230.769 307.692 230.769 307.692
+    538.462 538.462 538.462 538.462 538.462 538.462 538.462 538.462
+    538.462 538.462 307.692 307.692 538.462 538.462 538.462 538.462
+    923.077 692.308 692.308 692.308 692.308 538.462 538.462 692.308
+    692.308 307.692 538.462 692.308 615.385 846.154 692.308 769.231
+    615.385 769.231 692.308 615.385 615.385 615.385 692.308 1000.000
+    692.308 615.385 615.385 307.692 307.692 307.692 615.385 538.462
+    307.692 461.538 461.538 461.538 461.538 461.538 307.692 538.462
+    538.462 307.692 307.692 615.385 307.692 769.231 538.462 461.538
+    461.538 461.538 461.538 461.538 307.692 538.462 384.615 615.385
+    461.538 461.538
+  ]
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+2 J
+BT
+0 0 0 rg
+/F1 20 Tf
+10 40 Td
+[(U) -107.72357178 (n) -14.68963623 (i) 31.01132202
+(v) -125.67745972 (e) 14.6895752 (r) 35.90786743 (s) 57.12615967
+(i) 31.01132202 (t) -11.42520142 (y) -48.96524048]TJ
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/pixel/bug_1922_expected.pdf.0.png b/testing/resources/pixel/bug_1922_expected.pdf.0.png
new file mode 100644
index 0000000..2242e85
--- /dev/null
+++ b/testing/resources/pixel/bug_1922_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1922_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_1922_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..2caa41a
--- /dev/null
+++ b/testing/resources/pixel/bug_1922_expected_agg_mac.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_1949.in b/testing/resources/pixel/bug_1949.in
new file mode 100644
index 0000000..5e7afd3
--- /dev/null
+++ b/testing/resources/pixel/bug_1949.in
@@ -0,0 +1,133 @@
+{{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 44 24]
+  /Resources <<
+    /ExtGState <<
+      /GSHalfAlphaConstant 5 0 R
+      /GSHalfAlphaMask 6 0 R
+    >>
+    /XObject <<
+      /HalfAlphaSquare 8 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+
+% Opaque black background.
+q
+  0 0 0 rg
+  0 0 44 24 re
+  f
+Q
+
+% Transparency group drawn with constant alpha.
+q
+  1 0 0 1 4 4 cm
+  /GSHalfAlphaConstant gs
+
+  /HalfAlphaSquare Do
+Q
+
+% Transparency group drawn with a soft mask.
+q
+  1 0 0 1 24 4 cm
+  /GSHalfAlphaMask gs
+
+  /HalfAlphaSquare Do
+Q
+
+endstream
+endobj
+
+% Graphics state with a 0.5 non-stroking alpha constant.
+{{object 5 0}} <<
+  /Type /ExtGState
+  /ca 0.5
+>>
+endobj
+
+% Graphics state with a soft mask containing a 0.5 alpha central hole.
+{{object 6 0}} <<
+  /Type /ExtGState
+  /SMask <<
+    /Type /Mask
+    /S /Luminosity
+    /G 7 0 R
+  >>
+>>
+endobj
+{{object 7 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 16 16]
+  /Group <<
+    /Type /Group
+    /S /Transparency
+    /CS /DeviceGray
+    /I true
+  >>
+  {{streamlen}}
+>>
+stream
+q
+  1 g
+  0 0 16 16 re
+  f
+
+  0.5 g
+  4 4 8 8 re
+  f
+Q
+endstream
+endobj
+
+% Transparency group containing a 0.5 alpha white square.
+{{object 8 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 16 16]
+  /Group <<
+    /Type /Group
+    /S /Transparency
+    /I true
+  >>
+  /Resources <<
+    /ExtGState <<
+      /GSHalfAlphaConstant 5 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+q
+  /GSHalfAlphaConstant gs
+
+  1 1 1 rg
+  0 0 16 16 re
+  f
+Q
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1949_expected.pdf.0.png b/testing/resources/pixel/bug_1949_expected.pdf.0.png
new file mode 100644
index 0000000..c215044
--- /dev/null
+++ b/testing/resources/pixel/bug_1949_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1949_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1949_expected_skia.pdf.0.png
new file mode 100644
index 0000000..6d4ea6d
--- /dev/null
+++ b/testing/resources/pixel/bug_1949_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1963.in b/testing/resources/pixel/bug_1963.in
new file mode 100644
index 0000000..d79560b
--- /dev/null
+++ b/testing/resources/pixel/bug_1963.in
@@ -0,0 +1,73 @@
+{{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 [300 500 350 530]
+  /Resources <<
+    /XObject <<
+      /X1 5 0 R
+      /X2 6 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+  1 0 0 1 301.65 515.4 cm
+  /X1 Do
+Q
+q
+  1 0 0 1 304.05 503.7 cm
+  /X2 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [301.65 515.4 342.75 527.4]
+  /Matrix [1 0 0 1 -301.65 -515.4]
+  {{streamlen}}
+>>
+stream
+1 0 0 RG
+2 w
+305.400 521.400 m
+339.000 521.400 l
+S
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [304.05 503.7 345.15 515.7]
+  /Matrix [1 0 0 1 -304.05 -503.7]
+  {{streamlen}}
+>>
+stream
+1 0 0 RG
+2 w
+307.800 510.000 m
+341.400 509.400 l
+S
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1963_expected.pdf.0.png b/testing/resources/pixel/bug_1963_expected.pdf.0.png
new file mode 100644
index 0000000..a2aec8c
--- /dev/null
+++ b/testing/resources/pixel/bug_1963_expected.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_1963_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1963_expected_skia.pdf.0.png
new file mode 100644
index 0000000..2d9ddae
--- /dev/null
+++ b/testing/resources/pixel/bug_1963_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1966.in b/testing/resources/pixel/bug_1966.in
new file mode 100644
index 0000000..b52a6cb
--- /dev/null
+++ b/testing/resources/pixel/bug_1966.in
@@ -0,0 +1,121 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+  /MediaBox [0 0 128.571 128.571]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 7 0 R
+  /Resources <<
+    /XObject <<
+      /Im1 4 0 R
+    >>
+    /Pattern <<
+      /P1 5 0 R
+      /P2 6 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 1
+  /Filter /ASCIIHexDecode
+  /Height 40
+  /ImageMask true
+  /Width 80
+  {{streamlen}}
+>>
+stream
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFF
+FFFF000000000000FFFFFFFF0FFFFFFFFFF0FFFFFFFF0FFFFFFFFFF0FFFF
+FFFF0FFFFFFFFFF0FFFFFFFF0FFFFFFFFFF0FFFFFFFF000000000000FFFF
+FFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+FFFF000000000000FFFFFFFF000000000000FFFFFFFF0FFFFFFFFFF0FFFF
+FFFF0FFFFFFFFFF0FFFFFFFF0FFFFFFFFFF0FFFFFFFF0FFFFFFFFFF0FFFF
+FFFF000000000000FFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFF
+FFFFFFFFFFFFFFFFFFFF
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Pattern
+  /PaintType 1
+  /PatternType 1
+  /TilingType 1
+  /BBox [0 0 128.571 128.571]
+  /XStep 128.571
+  /YStep 128.571
+  {{streamlen}}
+>>
+stream
+q
+0.0 0.0 0.0 rg
+0 128.571 m
+128.571 128.571 l
+128.571 64 l
+0 64 l
+f
+Q
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /Pattern
+  /PaintType 1
+  /PatternType 1
+  /TilingType 1
+  /BBox [0 0 128.571 128.571]
+  /XStep 128.571
+  /YStep 128.571
+  {{streamlen}}
+>>
+stream
+% second pattern
+q
+0.0 0.0 0.0 rg
+0 64 m
+128.571 64 l
+128.571 0 l
+0 0 l
+f
+Q
+endstream
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+/Pattern cs
+/P1 scn
+q
+128.571 0 0 128.571 0 0 cm
+/Im1 Do
+Q
+/P2 scn
+q
+128.571 0 0 128.571 0 0 cm
+/Im1 Do
+Q
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/pixel/bug_1966_expected.pdf.0.png b/testing/resources/pixel/bug_1966_expected.pdf.0.png
new file mode 100644
index 0000000..47d12bd
--- /dev/null
+++ b/testing/resources/pixel/bug_1966_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1966_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1966_expected_skia.pdf.0.png
new file mode 100644
index 0000000..134ca64
--- /dev/null
+++ b/testing/resources/pixel/bug_1966_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1972_1.in b/testing/resources/pixel/bug_1972_1.in
new file mode 100644
index 0000000..e537e94
--- /dev/null
+++ b/testing/resources/pixel/bug_1972_1.in
@@ -0,0 +1,36 @@
+{{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
+  /MediaBox [0 0 200 100]
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+5 0 0 5 0 0 cm
+0 1 0 RG
+0 0 m
+16000 1420 l
+S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1972_1_expected.pdf.0.png b/testing/resources/pixel/bug_1972_1_expected.pdf.0.png
new file mode 100644
index 0000000..bca30df
--- /dev/null
+++ b/testing/resources/pixel/bug_1972_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1972_2.in b/testing/resources/pixel/bug_1972_2.in
new file mode 100644
index 0000000..4c1ad95
--- /dev/null
+++ b/testing/resources/pixel/bug_1972_2.in
@@ -0,0 +1,36 @@
+{{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
+  /MediaBox [0 0 200 100]
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+5 0 0 5 0 0 cm
+0 1 0 RG
+-9000 -9000 m
+500 500 l
+S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1972_2_expected.pdf.0.png b/testing/resources/pixel/bug_1972_2_expected.pdf.0.png
new file mode 100644
index 0000000..19b8c8a
--- /dev/null
+++ b/testing/resources/pixel/bug_1972_2_expected.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_1972_3.in b/testing/resources/pixel/bug_1972_3.in
new file mode 100644
index 0000000..5d84df7
--- /dev/null
+++ b/testing/resources/pixel/bug_1972_3.in
@@ -0,0 +1,36 @@
+{{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
+  /MediaBox [0 0 200 100]
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+5 0 0 5 0 0 cm
+0 1 0 RG
+0 0 m
+2 10000 20 9000 40 -10000 c
+S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1972_3_expected.pdf.0.png b/testing/resources/pixel/bug_1972_3_expected.pdf.0.png
new file mode 100644
index 0000000..d0f97c9
--- /dev/null
+++ b/testing/resources/pixel/bug_1972_3_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1973.in b/testing/resources/pixel/bug_1973.in
new file mode 100644
index 0000000..1d5602d
--- /dev/null
+++ b/testing/resources/pixel/bug_1973.in
@@ -0,0 +1,81 @@
+{{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 [ 240 570 345 650 ]
+  /Resources <<
+    /XObject <<
+      /Img 5 0 R
+    >>
+  >>
+>>
+endobj
+
+% Content stream matching coordinates in form_button0.pdf.
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+  89.555968 0 0 67.166976 247.7222 576.8333 cm
+  q
+    1 0 0 rg
+    0 0 1 1 re
+    f
+  Q
+  /Img Do
+Q
+endstream
+endobj
+
+% A 1024x768 image with a single (0x00), indexed color (0x00FF00).
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace [ /Indexed /DeviceRGB 0 <00FF00> ]
+  /Filter [ /ASCIIHexDecode /FlateDecode ]
+  /Height 768
+  /Width 1024
+  {{streamlen}}
+>>
+stream
+78DAEDC13101000000C2A0F54F6D067FA00000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000003E0300B40001
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1973_expected.pdf.0.png b/testing/resources/pixel/bug_1973_expected.pdf.0.png
new file mode 100644
index 0000000..8d75b62
--- /dev/null
+++ b/testing/resources/pixel/bug_1973_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1976.in b/testing/resources/pixel/bug_1976.in
new file mode 100644
index 0000000..696f5da
--- /dev/null
+++ b/testing/resources/pixel/bug_1976.in
@@ -0,0 +1,83 @@
+{{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 96 96]
+  /Resources <<
+    /XObject <<
+      /X0 5 0 R
+    >>
+  >>
+>>
+endobj
+
+% Green and red checkerboard. When scaled down, the green and red should blend
+% together into a darker yellow.
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+  % Solid green square.
+  0 1 0 rg
+  16 16 64 64 re f
+Q
+q
+  % Transparent and red checkerboard.
+  64 0 0 64 16 16 cm
+  /X0 Do
+Q
+endstream
+endobj
+
+% Single pixel image with /SMask to trigger masked image rendering.
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter /ASCIIHexDecode
+  /Height 1
+  /SMask 6 0 R
+  /Width 1
+  {{streamlen}}
+>>
+stream
+FF 00 00
+endstream
+endobj
+
+% Grayscale checkerboard to trigger 8-bit to 8-bit scaling. Scaling down by a
+% multiple of 2 using nearest neighbor gives particularly bad results.
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter [/ASCII85Decode /FlateDecode]
+  /Height 128
+  /Width 128
+  {{streamlen}}
+>>
+stream
+GhVQ20b"*_#f&.lRiX?CBI7,$dqQl"iofLfkND$kkND$kkND$kkND$kkND$kkND$kkND$kkND$kkND$k
+kND$kkND$kkND$kkND$kkND$kkN@?Q?he3kdJ~>
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1976_expected.pdf.0.png b/testing/resources/pixel/bug_1976_expected.pdf.0.png
new file mode 100644
index 0000000..59bfc84
--- /dev/null
+++ b/testing/resources/pixel/bug_1976_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1983.in b/testing/resources/pixel/bug_1983.in
new file mode 100644
index 0000000..0167258
--- /dev/null
+++ b/testing/resources/pixel/bug_1983.in
@@ -0,0 +1,43 @@
+{{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 100 100]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+20 w
+q
+1 0 0 1 20 10 cm
+1 J
+% The first dot
+10 40 m
+h
+S
+% The second dot
+50 40 m
+50 40 l
+h
+S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1983_expected.pdf.0.png b/testing/resources/pixel/bug_1983_expected.pdf.0.png
new file mode 100644
index 0000000..ea59974
--- /dev/null
+++ b/testing/resources/pixel/bug_1983_expected.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_1983_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1983_expected_skia.pdf.0.png
new file mode 100644
index 0000000..1e341ac
--- /dev/null
+++ b/testing/resources/pixel/bug_1983_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1986.in b/testing/resources/pixel/bug_1986.in
new file mode 100644
index 0000000..9e35fb3
--- /dev/null
+++ b/testing/resources/pixel/bug_1986.in
@@ -0,0 +1,61 @@
+{{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 <<
+      /Im0 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+612 0 0 792 0 0 cm
+/Im0 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Filter [6 0 R /LZWDecode 7 0 R]
+  /Width 612
+  /Height 792
+  {{streamlen}}
+>>
+stream
+80002040c351404020068290e0a8100028663a1e4e06a380c8410d004522d0d16c68d10d0b1a4d06
+439408061881008c8000181c0f180003cc66f361c80330084358d1a31bfc9eff288005f189549a51
+30a24ae5947a44b00e0100d3ea3507f948000c86d400a040200002ff2e0002640201209050b35a2c
+f69b4bfe5625af10ce465309d0ca6410188f220279c0ca6e251408a47101d8ca72399a4de6e100c8
+5c33170c1fe9000432303faf3fd26cf5a14022502a2bdfe7f369a0b480980c12097d1801be801c05
+0005c57bf818daa6ee5fed9801
+endstream
+endobj
+{{object 6 0}}
+/ASCIIHexDecode
+enbobj
+{{object 7 0}}
+/JPXDecode
+enbobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1986_expected.pdf.0.png b/testing/resources/pixel/bug_1986_expected.pdf.0.png
new file mode 100644
index 0000000..e1b3d43
--- /dev/null
+++ b/testing/resources/pixel/bug_1986_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1995.in b/testing/resources/pixel/bug_1995.in
new file mode 100644
index 0000000..448fa58
--- /dev/null
+++ b/testing/resources/pixel/bug_1995.in
@@ -0,0 +1,81 @@
+{{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
+  /Group <<
+    /Type /Group
+    /S /Transparency
+  >>
+  /MediaBox [0 0 40 40]
+  /Resources <<
+    /ExtGState <<
+      /GSMask <<
+        /SMask <<
+          /Type /Mask
+          /S /Alpha
+          /G 5 0 R
+        >>
+      >>
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+  1 0 0 1 5 5 cm
+
+  % Red square.
+  1 0 0 rg
+  0 0 30 30 re
+  f
+
+  % Blue square with soft mask dictionary.
+  /GSMask gs
+  0 0 1 rg
+  0 0 30 30 re
+  f
+Q
+endstream
+endobj
+
+% Soft mask dictionary with a central hole.
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 30 30]
+  /Group <<
+    /Type /Group
+    /S /Transparency
+    /I true
+  >>
+  {{streamlen}}
+>>
+stream
+q
+  0 0 0 RG
+  15 w
+  0 0 30 30 re
+  S
+Q
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1995_expected.pdf.0.png b/testing/resources/pixel/bug_1995_expected.pdf.0.png
new file mode 100644
index 0000000..daff3e9
--- /dev/null
+++ b/testing/resources/pixel/bug_1995_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1995_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1995_expected_skia.pdf.0.png
new file mode 100644
index 0000000..97ac01f
--- /dev/null
+++ b/testing/resources/pixel/bug_1995_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_2001.pdf b/testing/resources/pixel/bug_2001.pdf
new file mode 100644
index 0000000..f73831d
--- /dev/null
+++ b/testing/resources/pixel/bug_2001.pdf
Binary files differ
diff --git a/testing/resources/pixel/bug_2001_expected.pdf.0.png b/testing/resources/pixel/bug_2001_expected.pdf.0.png
new file mode 100644
index 0000000..fd353a9
--- /dev/null
+++ b/testing/resources/pixel/bug_2001_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_237527_1.in b/testing/resources/pixel/bug_237527_1.in
new file mode 100644
index 0000000..503fa2a
--- /dev/null
+++ b/testing/resources/pixel/bug_237527_1.in
@@ -0,0 +1,37 @@
+{{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
+  /Annots [4 0 R]
+  /MediaBox [0 0 300 300]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /A <<
+    /Type /Action
+    /S /URI
+    /URI (www.google.com)
+  >>
+  /Border [0 0 1]
+  /C [0 1 1]
+  /H /I
+  /Rect [100 100 200 120]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_237527_1_expected.pdf.0.png b/testing/resources/pixel/bug_237527_1_expected.pdf.0.png
new file mode 100644
index 0000000..d8c8148
--- /dev/null
+++ b/testing/resources/pixel/bug_237527_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_237527_1_expected_skia.pdf.0.png b/testing/resources/pixel/bug_237527_1_expected_skia.pdf.0.png
new file mode 100644
index 0000000..eef577e
--- /dev/null
+++ b/testing/resources/pixel/bug_237527_1_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_237527_2.in b/testing/resources/pixel/bug_237527_2.in
new file mode 100644
index 0000000..fd4eefa
--- /dev/null
+++ b/testing/resources/pixel/bug_237527_2.in
@@ -0,0 +1,42 @@
+{{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
+  /Annots [4 0 R]
+  /MediaBox [0 0 300 300]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /A <<
+    /Type /Action
+    /S /URI
+    /URI (http://www.google.com)
+  >>
+  /BS <<
+    /Type /Border
+    /S /U
+    /W 1
+  >>
+  /Border [0 0 1]
+  /C [0 1 1]
+  /H /I
+  /Rect [100 100 200 120]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_237527_2_expected.pdf.0.png b/testing/resources/pixel/bug_237527_2_expected.pdf.0.png
new file mode 100644
index 0000000..14b04de
--- /dev/null
+++ b/testing/resources/pixel/bug_237527_2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_304_expected.pdf.0.png b/testing/resources/pixel/bug_304_expected.pdf.0.png
index e1b3d43..62de87b 100644
--- a/testing/resources/pixel/bug_304_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_304_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_451366_expected_skia.pdf.0.png b/testing/resources/pixel/bug_451366_expected_skia.pdf.0.png
new file mode 100644
index 0000000..94b4524
--- /dev/null
+++ b/testing/resources/pixel/bug_451366_expected_skia.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_invisible_expected_skia.pdf.0.png b/testing/resources/pixel/bug_491_invisible_expected_skia.pdf.0.png
new file mode 100644
index 0000000..518a266
--- /dev/null
+++ b/testing/resources/pixel/bug_491_invisible_expected_skia.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_unspecified_expected_skia.pdf.0.png b/testing/resources/pixel/bug_491_unspecified_expected_skia.pdf.0.png
new file mode 100644
index 0000000..10665ac
--- /dev/null
+++ b/testing/resources/pixel/bug_491_unspecified_expected_skia.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_491_visible_expected_skia.pdf.0.png b/testing/resources/pixel/bug_491_visible_expected_skia.pdf.0.png
new file mode 100644
index 0000000..10665ac
--- /dev/null
+++ b/testing/resources/pixel/bug_491_visible_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_492.in b/testing/resources/pixel/bug_492.in
index 45c4a6f..e2922db 100644
--- a/testing/resources/pixel/bug_492.in
+++ b/testing/resources/pixel/bug_492.in
@@ -35,7 +35,7 @@
   /JS 21 0 R
 >>
 endobj
-% JS program to exexute
+% JS program to execute
 {{object 21 0}} <<
 >>
 stream
diff --git a/testing/resources/pixel/bug_524043_1_expected.pdf.0.png b/testing/resources/pixel/bug_524043_1_expected.pdf.0.png
index eabf957..a7e1fd7 100644
--- a/testing/resources/pixel/bug_524043_1_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_524043_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_1_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_524043_1_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..438e9f3
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_1_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_1_expected_mac.pdf.0.png b/testing/resources/pixel/bug_524043_1_expected_mac.pdf.0.png
deleted file mode 100644
index b85b9d0..0000000
--- a/testing/resources/pixel/bug_524043_1_expected_mac.pdf.0.png
+++ /dev/null
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_1_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_1_expected_win.pdf.0.png
deleted file mode 100644
index b2c134a..0000000
--- a/testing/resources/pixel/bug_524043_1_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_2_expected.pdf.0.png b/testing/resources/pixel/bug_524043_2_expected.pdf.0.png
index 04304fe..4dd2672 100644
--- a/testing/resources/pixel/bug_524043_2_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_524043_2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_2_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_524043_2_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..e3bf21f
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_2_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_2_expected_mac.pdf.0.png b/testing/resources/pixel/bug_524043_2_expected_mac.pdf.0.png
deleted file mode 100644
index ecede7d..0000000
--- a/testing/resources/pixel/bug_524043_2_expected_mac.pdf.0.png
+++ /dev/null
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_2_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_2_expected_win.pdf.0.png
deleted file mode 100644
index 786d963..0000000
--- a/testing/resources/pixel/bug_524043_2_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_3_expected.pdf.0.png b/testing/resources/pixel/bug_524043_3_expected.pdf.0.png
index eabf957..a7e1fd7 100644
--- a/testing/resources/pixel/bug_524043_3_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_524043_3_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_3_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_524043_3_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..e794355
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_3_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_3_expected_mac.pdf.0.png b/testing/resources/pixel/bug_524043_3_expected_mac.pdf.0.png
deleted file mode 100644
index b85b9d0..0000000
--- a/testing/resources/pixel/bug_524043_3_expected_mac.pdf.0.png
+++ /dev/null
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_3_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_3_expected_win.pdf.0.png
deleted file mode 100644
index b2c134a..0000000
--- a/testing/resources/pixel/bug_524043_3_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_4_expected.pdf.0.png b/testing/resources/pixel/bug_524043_4_expected.pdf.0.png
index eabf957..a7e1fd7 100644
--- a/testing/resources/pixel/bug_524043_4_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_524043_4_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_4_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_524043_4_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..e794355
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_4_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_4_expected_mac.pdf.0.png b/testing/resources/pixel/bug_524043_4_expected_mac.pdf.0.png
deleted file mode 100644
index b85b9d0..0000000
--- a/testing/resources/pixel/bug_524043_4_expected_mac.pdf.0.png
+++ /dev/null
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_4_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_4_expected_win.pdf.0.png
deleted file mode 100644
index b2c134a..0000000
--- a/testing/resources/pixel/bug_524043_4_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_5_expected.pdf.0.png b/testing/resources/pixel/bug_524043_5_expected.pdf.0.png
index eabf957..a7e1fd7 100644
--- a/testing/resources/pixel/bug_524043_5_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_524043_5_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_5_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_524043_5_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..e794355
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_5_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_5_expected_mac.pdf.0.png b/testing/resources/pixel/bug_524043_5_expected_mac.pdf.0.png
deleted file mode 100644
index b85b9d0..0000000
--- a/testing/resources/pixel/bug_524043_5_expected_mac.pdf.0.png
+++ /dev/null
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_5_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_5_expected_win.pdf.0.png
deleted file mode 100644
index b2c134a..0000000
--- a/testing/resources/pixel/bug_524043_5_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_6_expected_mac.pdf.0.png b/testing/resources/pixel/bug_524043_6_expected_mac.pdf.0.png
deleted file mode 100644
index 3edcc2d..0000000
--- a/testing/resources/pixel/bug_524043_6_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_7_expected.pdf.0.png b/testing/resources/pixel/bug_524043_7_expected.pdf.0.png
index 04304fe..4dd2672 100644
--- a/testing/resources/pixel/bug_524043_7_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_524043_7_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_7_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_524043_7_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..e3bf21f
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_7_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_7_expected_mac.pdf.0.png b/testing/resources/pixel/bug_524043_7_expected_mac.pdf.0.png
deleted file mode 100644
index ecede7d..0000000
--- a/testing/resources/pixel/bug_524043_7_expected_mac.pdf.0.png
+++ /dev/null
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_524043_7_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_7_expected_win.pdf.0.png
deleted file mode 100644
index 786d963..0000000
--- a/testing/resources/pixel/bug_524043_7_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_528103_expected.pdf.0.png b/testing/resources/pixel/bug_528103_expected.pdf.0.png
index 5c0c254..79240dd 100644
--- a/testing/resources/pixel/bug_528103_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_528103_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_528103_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_528103_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..0d23b55
--- /dev/null
+++ b/testing/resources/pixel/bug_528103_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_528103_expected_mac.pdf.0.png b/testing/resources/pixel/bug_528103_expected_mac.pdf.0.png
deleted file mode 100644
index cb028f9..0000000
--- a/testing/resources/pixel/bug_528103_expected_mac.pdf.0.png
+++ /dev/null
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_528103_expected_win.pdf.0.png b/testing/resources/pixel/bug_528103_expected_win.pdf.0.png
deleted file mode 100644
index 8ccda23..0000000
--- a/testing/resources/pixel/bug_528103_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_543018_1_expected.pdf.0.png b/testing/resources/pixel/bug_543018_1_expected.pdf.0.png
index eabf957..a7e1fd7 100644
--- a/testing/resources/pixel/bug_543018_1_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_543018_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_543018_1_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_543018_1_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..e794355
--- /dev/null
+++ b/testing/resources/pixel/bug_543018_1_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_543018_1_expected_mac.pdf.0.png b/testing/resources/pixel/bug_543018_1_expected_mac.pdf.0.png
deleted file mode 100644
index b85b9d0..0000000
--- a/testing/resources/pixel/bug_543018_1_expected_mac.pdf.0.png
+++ /dev/null
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_1_expected_win.pdf.0.png b/testing/resources/pixel/bug_543018_1_expected_win.pdf.0.png
deleted file mode 100644
index b2c134a..0000000
--- a/testing/resources/pixel/bug_543018_1_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_543018_2_expected.pdf.0.png b/testing/resources/pixel/bug_543018_2_expected.pdf.0.png
index eabf957..a7e1fd7 100644
--- a/testing/resources/pixel/bug_543018_2_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_543018_2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_543018_2_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_543018_2_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..e794355
--- /dev/null
+++ b/testing/resources/pixel/bug_543018_2_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_543018_2_expected_mac.pdf.0.png b/testing/resources/pixel/bug_543018_2_expected_mac.pdf.0.png
deleted file mode 100644
index b85b9d0..0000000
--- a/testing/resources/pixel/bug_543018_2_expected_mac.pdf.0.png
+++ /dev/null
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_543018_2_expected_win.pdf.0.png b/testing/resources/pixel/bug_543018_2_expected_win.pdf.0.png
deleted file mode 100644
index b2c134a..0000000
--- a/testing/resources/pixel/bug_543018_2_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_551258_1_expected.pdf.0.png b/testing/resources/pixel/bug_551258_1_expected.pdf.0.png
index eabf957..a7e1fd7 100644
--- a/testing/resources/pixel/bug_551258_1_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_551258_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_551258_1_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_551258_1_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..e794355
--- /dev/null
+++ b/testing/resources/pixel/bug_551258_1_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_551258_1_expected_mac.pdf.0.png b/testing/resources/pixel/bug_551258_1_expected_mac.pdf.0.png
deleted file mode 100644
index b85b9d0..0000000
--- a/testing/resources/pixel/bug_551258_1_expected_mac.pdf.0.png
+++ /dev/null
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_551258_1_expected_win.pdf.0.png b/testing/resources/pixel/bug_551258_1_expected_win.pdf.0.png
deleted file mode 100644
index b2c134a..0000000
--- a/testing/resources/pixel/bug_551258_1_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_554151_expected.pdf.0.png b/testing/resources/pixel/bug_554151_expected.pdf.0.png
index 08c11b0..5b99a4d 100644
--- a/testing/resources/pixel/bug_554151_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_554151_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_557223.in b/testing/resources/pixel/bug_557223.in
index acb1ff2..c0c06b6 100644
--- a/testing/resources/pixel/bug_557223.in
+++ b/testing/resources/pixel/bug_557223.in
@@ -75,7 +75,7 @@
 trailer <<
     /Info 6 0 R
     /Root 1 0 R
-    /Size 7
+    {{trailersize}}
 >>
 {{startxref}}
 %%EOF
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_585_expected_skia.pdf.0.png b/testing/resources/pixel/bug_585_expected_skia.pdf.0.png
new file mode 100644
index 0000000..c49e21e
--- /dev/null
+++ b/testing/resources/pixel/bug_585_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_591137_expected.pdf.0.png b/testing/resources/pixel/bug_591137_expected.pdf.0.png
index 265e682..74c5c64 100644
--- a/testing/resources/pixel/bug_591137_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_591137_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_601362_expected.pdf.0.png b/testing/resources/pixel/bug_601362_expected.pdf.0.png
index 5d868d5..49db8d9 100644
--- a/testing/resources/pixel/bug_601362_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_601362_expected.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_601362_expected_skia.pdf.0.png b/testing/resources/pixel/bug_601362_expected_skia.pdf.0.png
new file mode 100644
index 0000000..d371b3a
--- /dev/null
+++ b/testing/resources/pixel/bug_601362_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_632_expected.pdf.0.png b/testing/resources/pixel/bug_632_expected.pdf.0.png
index 3807d93..4fc6081 100644
--- a/testing/resources/pixel/bug_632_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_632_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_632_expected.pdf.1.png b/testing/resources/pixel/bug_632_expected.pdf.1.png
index 141f3d8..ee11ce6 100644
--- a/testing/resources/pixel/bug_632_expected.pdf.1.png
+++ b/testing/resources/pixel/bug_632_expected.pdf.1.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
new file mode 100644
index 0000000..3e66da2
--- /dev/null
+++ b/testing/resources/pixel/bug_632_expected_skia.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/bug_660850_expected_skia.pdf.0.png b/testing/resources/pixel/bug_660850_expected_skia.pdf.0.png
new file mode 100644
index 0000000..254f45b
--- /dev/null
+++ b/testing/resources/pixel/bug_660850_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_665467.in b/testing/resources/pixel/bug_665467.in
index 6ef2c1c..289e8de 100644
--- a/testing/resources/pixel/bug_665467.in
+++ b/testing/resources/pixel/bug_665467.in
@@ -4,96 +4,66 @@
   /Pages 2 0 R
 >>
 endobj
-
 {{object 2 0}} <<
   /Type /Pages
-  /MediaBox [ 0 0 100 100 ]
   /Count 1
-  /Kids [ 3 0 R ]
+  /Kids [3 0 R]
+  /MediaBox [0 0 100 100]
 >>
 endobj
-
 {{object 3 0}} <<
   /Type /Page
   /Parent 2 0 R
-  /Resources
-  <<
-    /Font << /F1 4 0 R >>
+  /Contents 4 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+    >>
   >>
-  /Contents 8 0 R
 >>
 endobj
-
 {{object 4 0}} <<
-  /Type /Font
-  /Subtype /TrueType
-  /BaseFont /ChromeSansMM
-  /Encoding 5 0 R
-  /FirstChar 32
-  /LastChar 255
-  /Name /F1
-  /ToUnicode 6 0 R
-  /FontDescriptor 7 0 R
->>
-endobj
-
-{{object 5 0}} <<
-  /Differences [ 161 /someunknownname ]
-  /Type /Encoding
->>
-endobj
-
-{{object 6 0}} <<
->>
-stream
-/CIDInit /ProcSet findresource begin
-12 dict begin
-begincmap
-/CIDSystemInfo
-<</Registry (Adobe)
-/Ordering (Identity)
-/Supplement 0
->> def
-/CMapName /Adobe-Identity-H def
-CMapType 2 def
-1 begincodespacerange
-<00> <FF>
-endcodespacerange
-1 beginbfchar
-<A1> <043B>
-endbfchar
-endcmap
-CMapName currentdict /CMap defineresource pop
-end
-end
-endstream
-endobj
-
-{{object 7 0}} <<
-  << /Ascent 1000
-  /CapHeight 0
-  /Descent -200
-  /Flags 32
-  /FontBBox [ -599 -207 1338 1034 ]
-  /FontName /ChromeSansMM
-  /ItalicAngle 0
-  /StemV 0
-  /Type /FontDescriptor
->>
-endobj
-
-{{object 8 0}} <<
+  {{streamlen}}
 >>
 stream
 BT
-50 50 Td /F1 15 Tf <A1> Tj
+50 50 Td
+/F1 15 Tf
+[<8C>]TJ
 ET
 endstream
 endobj
-
-{{xref}}
-trailer <<
-  /Root 1 0 R
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /Tahoma
+  /Encoding 6 0 R
+  /FirstChar 139
+  /FontDescriptor 7 0 R
+  /LastChar 140
+  /Widths [943 677]
 >>
+endobj
+{{object 6 0}} <<
+  /Type /Encoding
+  /Differences [
+    140 /Elcyrillic
+  ]
+>>
+endobj
+{{object 7 0}} <<
+  /Type /FontDescriptor
+  /Ascent 1034
+  /CapHeight 0
+  /Descent -207
+  /Flags 32
+  /FontBBox [-599 -207 1338 1034]
+  /FontName /Tahoma
+  /ItalicAngle 0
+  /StemV 0
+>>
+endobj
+{{xref}}
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/pixel/bug_665467_expected.pdf.0.png b/testing/resources/pixel/bug_665467_expected.pdf.0.png
index e3b37b4..4532565 100644
--- a/testing/resources/pixel/bug_665467_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_665467_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_665467_expected_mac.pdf.0.png b/testing/resources/pixel/bug_665467_expected_mac.pdf.0.png
index 9f51dcb..0faddf8 100644
--- a/testing/resources/pixel/bug_665467_expected_mac.pdf.0.png
+++ b/testing/resources/pixel/bug_665467_expected_mac.pdf.0.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
new file mode 100644
index 0000000..2e9c915
--- /dev/null
+++ b/testing/resources/pixel/bug_665467_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_665467_expected_win.pdf.0.png b/testing/resources/pixel/bug_665467_expected_win.pdf.0.png
deleted file mode 100644
index 8feb717..0000000
--- a/testing/resources/pixel/bug_665467_expected_win.pdf.0.png
+++ /dev/null
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_714187_expected_skia.pdf.0.png b/testing/resources/pixel/bug_714187_expected_skia.pdf.0.png
new file mode 100644
index 0000000..80cac57
--- /dev/null
+++ b/testing/resources/pixel/bug_714187_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_725389.in b/testing/resources/pixel/bug_725389.in
new file mode 100644
index 0000000..0d0818b
--- /dev/null
+++ b/testing/resources/pixel/bug_725389.in
@@ -0,0 +1,58 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /AcroForm <<
+    /Fields [4 0 R]
+  >>
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Annots [4 0 R]
+  /Parent 2 0 R
+  /MediaBox [0 0 200 100]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /BS <<
+    /S /S
+    /W 1
+  >>
+  /DA (/Times-Roman 12 Tf 0 0 0 rg)
+  /DR <<
+    /Font <<
+      /Times-Roman 5 0 R
+    >>
+  >>
+  /DV ()
+  /F 4
+  /Ff 0
+  /MK <<
+    /BG [1 1 1]
+  >>
+  /P 3 0 R
+  /T (Default)
+  /V (\376\377\5\321\5\327\5\350\0.\0.\0.)
+  /Rect [50 40 150 70]
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_725389_expected.pdf.0.png b/testing/resources/pixel/bug_725389_expected.pdf.0.png
new file mode 100644
index 0000000..6da1b77
--- /dev/null
+++ b/testing/resources/pixel/bug_725389_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_725389_expected_mac.pdf.0.png b/testing/resources/pixel/bug_725389_expected_mac.pdf.0.png
new file mode 100644
index 0000000..453c2c3
--- /dev/null
+++ b/testing/resources/pixel/bug_725389_expected_mac.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
new file mode 100644
index 0000000..0cdd2cc
--- /dev/null
+++ 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
new file mode 100644
index 0000000..48a2087
--- /dev/null
+++ 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.pdf.0.png b/testing/resources/pixel/bug_733528_expected.pdf.0.png
index eaf48b5..5e178da 100644
--- a/testing/resources/pixel/bug_733528_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_733528_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_733528_expected_mac.pdf.0.png b/testing/resources/pixel/bug_733528_expected_mac.pdf.0.png
index f7c8302..992e488 100644
--- a/testing/resources/pixel/bug_733528_expected_mac.pdf.0.png
+++ b/testing/resources/pixel/bug_733528_expected_mac.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
new file mode 100644
index 0000000..7e13f31
--- /dev/null
+++ b/testing/resources/pixel/bug_733528_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_733528_expected_win.pdf.0.png b/testing/resources/pixel/bug_733528_expected_win.pdf.0.png
deleted file mode 100644
index a44b932..0000000
--- a/testing/resources/pixel/bug_733528_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_1_expected.pdf.0.png b/testing/resources/pixel/bug_736695_1_expected.pdf.0.png
index 99d7bd7..10bbb0d 100644
--- a/testing/resources/pixel/bug_736695_1_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_736695_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_1_expected_skia.pdf.0.png b/testing/resources/pixel/bug_736695_1_expected_skia.pdf.0.png
new file mode 100644
index 0000000..ecb7d2b
--- /dev/null
+++ b/testing/resources/pixel/bug_736695_1_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_2_expected.pdf.0.png b/testing/resources/pixel/bug_736695_2_expected.pdf.0.png
index 33ea077..4fe962d 100644
--- a/testing/resources/pixel/bug_736695_2_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_736695_2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_2_expected_mac.pdf.0.png b/testing/resources/pixel/bug_736695_2_expected_mac.pdf.0.png
index 46c7085..e46e02d 100644
--- a/testing/resources/pixel/bug_736695_2_expected_mac.pdf.0.png
+++ b/testing/resources/pixel/bug_736695_2_expected_mac.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
new file mode 100644
index 0000000..3cddbc0
--- /dev/null
+++ b/testing/resources/pixel/bug_736695_2_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_2_expected_win.pdf.0.png b/testing/resources/pixel/bug_736695_2_expected_win.pdf.0.png
deleted file mode 100644
index ffa94f0..0000000
--- a/testing/resources/pixel/bug_736695_2_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_3_expected.pdf.0.png b/testing/resources/pixel/bug_736695_3_expected.pdf.0.png
index cbffe0a..13c9d0a 100644
--- a/testing/resources/pixel/bug_736695_3_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_736695_3_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_3_expected_mac.pdf.0.png b/testing/resources/pixel/bug_736695_3_expected_mac.pdf.0.png
index 1abcd18..f15d038 100644
--- a/testing/resources/pixel/bug_736695_3_expected_mac.pdf.0.png
+++ b/testing/resources/pixel/bug_736695_3_expected_mac.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
new file mode 100644
index 0000000..8f668d0
--- /dev/null
+++ b/testing/resources/pixel/bug_736695_3_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_3_expected_win.pdf.0.png b/testing/resources/pixel/bug_736695_3_expected_win.pdf.0.png
deleted file mode 100644
index d27a9b4..0000000
--- a/testing/resources/pixel/bug_736695_3_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_4_expected.pdf.0.png b/testing/resources/pixel/bug_736695_4_expected.pdf.0.png
index 99d7bd7..10bbb0d 100644
--- a/testing/resources/pixel/bug_736695_4_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_736695_4_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_4_expected_skia.pdf.0.png b/testing/resources/pixel/bug_736695_4_expected_skia.pdf.0.png
new file mode 100644
index 0000000..ecb7d2b
--- /dev/null
+++ b/testing/resources/pixel/bug_736695_4_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736703.in b/testing/resources/pixel/bug_736703.in
new file mode 100644
index 0000000..05c4a35
--- /dev/null
+++ b/testing/resources/pixel/bug_736703.in
@@ -0,0 +1,73 @@
+{{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
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /F4 5 0 R
+    >>
+  >>
+  /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+BT
+/F4 30 Tf
+0 0 0 rg
+1 0 0 1 30 100 Tm
+[(honorably)]TJ
+ET
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /AGaramond-Regular
+  /Encoding /WinAnsiEncoding
+  /FirstChar 32
+  /FontDescriptor 6 0 R
+  /LastChar 125
+  /Widths [250 220 404 500 500 844 818 235 320 320 394 500 250 320 250 327 500
+   500 500 500 500 500 500 500 500 500 250 250 500 500 500 321 765 623 605 696
+   780 584 538 747 806 338 345 675 553 912 783 795 549 795 645 489 660 746 676
+   960 643 574 641 320 309 320 500 500 360 404 500 400 509 396 290 446 515 257
+   253 482 247 787 525 486 507 497 332 323 307 512 432 660 432 438 377 320 239
+   320]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /FontDescriptor
+  /Ascent 720
+  /CapHeight 663
+  /Descent -270
+  /Flags 34
+  /FontBBox [-183 -269 1099 851]
+  /FontName /AGaramond-Regular
+  /ItalicAngle 0
+  /StemH 40
+  /StemV 74
+  /XHeight 397
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_736703_expected.pdf.0.png b/testing/resources/pixel/bug_736703_expected.pdf.0.png
new file mode 100644
index 0000000..f2fe3a1
--- /dev/null
+++ b/testing/resources/pixel/bug_736703_expected.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.pdf.0.png b/testing/resources/pixel/bug_820345_expected.pdf.0.png
index e3c8e24..e89a680 100644
--- a/testing/resources/pixel/bug_820345_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_820345_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_820345_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_820345_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..6013c5c
--- /dev/null
+++ b/testing/resources/pixel/bug_820345_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_820345_expected_mac.pdf.0.png b/testing/resources/pixel/bug_820345_expected_mac.pdf.0.png
deleted file mode 100644
index 6e79402..0000000
--- a/testing/resources/pixel/bug_820345_expected_mac.pdf.0.png
+++ /dev/null
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_820345_expected_win.pdf.0.png b/testing/resources/pixel/bug_820345_expected_win.pdf.0.png
deleted file mode 100644
index c3b8b43..0000000
--- a/testing/resources/pixel/bug_820345_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_842.in b/testing/resources/pixel/bug_842.in
index 255a902..71f0104 100644
--- a/testing/resources/pixel/bug_842.in
+++ b/testing/resources/pixel/bug_842.in
@@ -24,7 +24,7 @@
 >>
 endobj
 {{object 4 0}} <<
-{{streamlen}}
+  {{streamlen}}
 >>
 stream
 q
diff --git a/testing/resources/pixel/bug_842_expected_skia.pdf.0.png b/testing/resources/pixel/bug_842_expected_skia.pdf.0.png
new file mode 100644
index 0000000..eb10aa7
--- /dev/null
+++ b/testing/resources/pixel/bug_842_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_843_expected_skia.pdf.0.png b/testing/resources/pixel/bug_843_expected_skia.pdf.0.png
new file mode 100644
index 0000000..f0c8193
--- /dev/null
+++ b/testing/resources/pixel/bug_843_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_845697_expected.pdf.0.png b/testing/resources/pixel/bug_845697_expected.pdf.0.png
index 3bc224b..15ec4dd 100644
--- a/testing/resources/pixel/bug_845697_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_845697_expected.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.in b/testing/resources/pixel/bug_846.in
new file mode 100644
index 0000000..e405d67
--- /dev/null
+++ b/testing/resources/pixel/bug_846.in
@@ -0,0 +1,160 @@
+{{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
+  /Resources 4 0 R
+  /Contents 15 0 R
+  /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+  /ProcSet [/PDF /Text]
+  /Font <<
+    /F1 5 0 R
+    /F2 7 0 R
+    /F3 9 0 R
+    /F4 11 0 R
+  >>
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /Arial-Bold
+  /Encoding /WinAnsiEncoding
+  /FirstChar 65
+  /FontDescriptor 6 0 R
+  /LastChar 65
+  /Widths [778]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /FontDescriptor
+  /Ascent 923
+  /CapHeight 687
+  /Descent -282
+  /Flags 34
+  /FontName /Arial-Bold
+  /FontBBox 14 0 R
+  /ItalicAngle 0
+  /StemV 140.269
+  /XHeight 0
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /Arial-Bold,Bold
+  /Encoding /WinAnsiEncoding
+  /FirstChar 65
+  /FontDescriptor 8 0 R
+  /LastChar 65
+  /Widths [778]
+>>
+endobj
+{{object 8 0}} <<
+  /Type /FontDescriptor
+  /Ascent 923
+  /CapHeight 687
+  /Descent -282
+  /Flags 34
+  /FontName /Arial-Bold,Bold
+  /FontBBox 14 0 R
+  /ItalicAngle 0
+  /StemV 140.269
+  /XHeight 0
+>>
+endobj
+{{object 9 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /DejaVuSans-Bold
+  /Encoding /WinAnsiEncoding
+  /FirstChar 58
+  /FontDescriptor 10 0 R
+  /LastChar 65
+  /Widths 13 0 R
+>>
+endobj
+{{object 10 0}} <<
+  /Type /FontDescriptor
+  /Ascent 923
+  /CapHeight 687
+  /Descent -282
+  /Flags 34
+  /FontName /DejaVuSans-Bold
+  /FontBBox 14 0 R
+  /ItalicAngle 0
+  /StemV 140.269
+  /XHeight 0
+>>
+endobj
+{{object 11 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /DejaVuSans-Bold,Bold
+  /Encoding /WinAnsiEncoding
+  /FirstChar 58
+  /FontDescriptor 12 0 R
+  /LastChar 65
+  /Widths 13 0 R
+>>
+endobj
+{{object 12 0}} <<
+  /Type /FontDescriptor
+  /Ascent 923
+  /CapHeight 687
+  /Descent -282
+  /Flags 34
+  /FontName /DejaVuSans-Bold,Bold
+  /FontBBox 14 0 R
+  /ItalicAngle 0
+  /StemV 140.269
+  /XHeight 0
+>>
+endobj
+{{object 13 0}} [250 0 0 0 0 0 0 778]
+endobj
+{{object 14 0}} [-134 -337 1193 1021]
+endobj
+{{object 15 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+/F1 2 Tf
+18 0 0 18 50 120 Tm
+(A)Tj
+ET
+BT
+/F2 2 Tf
+18 0 0 18 120 120 Tm
+(A)Tj
+ET
+BT
+/F3 2 Tf
+18 0 0 18 50 60 Tm
+(A)Tj
+ET
+BT
+/F4 2 Tf
+18 0 0 18 120 60 Tm
+(A)Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_846_expected.pdf.0.png b/testing/resources/pixel/bug_846_expected.pdf.0.png
new file mode 100644
index 0000000..1ff3944
--- /dev/null
+++ b/testing/resources/pixel/bug_846_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_846_expected_mac.pdf.0.png b/testing/resources/pixel/bug_846_expected_mac.pdf.0.png
new file mode 100644
index 0000000..b405dce
--- /dev/null
+++ b/testing/resources/pixel/bug_846_expected_mac.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
new file mode 100644
index 0000000..10cde92
--- /dev/null
+++ 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
new file mode 100644
index 0000000..0f361df
--- /dev/null
+++ 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
new file mode 100644
index 0000000..0f361df
--- /dev/null
+++ b/testing/resources/pixel/bug_846_expected_skia_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_909762_expected.pdf.0.png b/testing/resources/pixel/bug_909762_expected.pdf.0.png
index 219c163..d01a7fc 100644
--- a/testing/resources/pixel/bug_909762_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_909762_expected.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_909762_expected_win.pdf.0.png b/testing/resources/pixel/bug_909762_expected_win.pdf.0.png
deleted file mode 100644
index c6d2727..0000000
--- a/testing/resources/pixel/bug_909762_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_925736_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_925736_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..1763451
--- /dev/null
+++ b/testing/resources/pixel/bug_925736_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_925736_expected_mac.pdf.0.png b/testing/resources/pixel/bug_925736_expected_mac.pdf.0.png
deleted file mode 100644
index f71efde..0000000
--- a/testing/resources/pixel/bug_925736_expected_mac.pdf.0.png
+++ /dev/null
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_966263.in b/testing/resources/pixel/bug_966263.in
new file mode 100644
index 0000000..bddf328
--- /dev/null
+++ b/testing/resources/pixel/bug_966263.in
@@ -0,0 +1,132 @@
+{{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 1 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 200 400]
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+/F1 1 Tf
+1 0 0 1 20 55 Tm
+(ABCD)Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type3
+  /FontBBox [0 0 600 600]
+  /FontMatrix [1 0 0 -100 0 0]
+  /CharProcs <<
+    /CA 6 0 R
+    /CB 7 0 R
+    /CC 8 0 R
+    /CD 9 0 R
+  >>
+  /Encoding <<
+    /Type /Encoding
+    /Differences [65 /CA 66 /CB 67 /CC 68 /CD]
+  >>
+  /FirstChar 65
+  /LastChar 68
+  /Widths [18 1000 18 18]
+  /Resources <<
+    /ProcSet [/PDF /ImageB]
+  >>
+>>
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+d0 0 0
+1 0 0 1 6 0 cm
+BI
+/W 1
+/H 1
+/BPC 1
+/IM true
+ID
+x
+EI
+Q
+endstream
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+6 0 0 48 6 0 cm
+BI
+/W 6
+/H 48
+/BPC 1
+/IM true
+ID
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+EI
+Q
+endstream
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+6 0 0 1 2147483570 0 cm
+BI
+/W 6
+/H 1
+/BPC 1
+/IM true
+ID
+x
+EI
+Q
+endstream
+endobj
+{{object 9 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+1 0 0 6 0 21474834 cm
+BI
+/W 1
+/H 1
+/BPC 1
+/IM true
+ID
+x
+EI
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_966263_expected.pdf.0.png b/testing/resources/pixel/bug_966263_expected.pdf.0.png
new file mode 100644
index 0000000..84650b6
--- /dev/null
+++ b/testing/resources/pixel/bug_966263_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_972999.in b/testing/resources/pixel/bug_972999.in
new file mode 100644
index 0000000..6e928da
--- /dev/null
+++ b/testing/resources/pixel/bug_972999.in
@@ -0,0 +1,603 @@
+{{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 300]
+  /Resources <<
+    /XObject <<
+      /X1 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 6 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 7 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 8 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 8 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 9 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 9 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 10 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 10 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 11 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 11 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 12 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 12 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 13 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 13 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 14 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 14 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 15 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 15 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 16 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 16 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 17 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 17 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 18 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 18 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 19 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 19 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 20 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 20 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 21 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 21 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 22 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 22 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 23 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 23 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 24 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 24 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 25 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 25 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 26 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 26 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 27 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 27 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 28 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 28 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 29 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 29 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 30 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 30 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 31 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 31 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 32 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 32 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 33 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 33 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 34 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 34 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 35 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 35 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 36 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 36 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 37 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 37 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 38 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 38 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  /Resources <<
+    /XObject <<
+      /X1 39 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 39 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 200 300]
+  {{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/pixel/bug_972999_expected.pdf.0.png b/testing/resources/pixel/bug_972999_expected.pdf.0.png
new file mode 100644
index 0000000..ee652fa
--- /dev/null
+++ b/testing/resources/pixel/bug_972999_expected.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_972999_expected_skia.pdf.0.png b/testing/resources/pixel/bug_972999_expected_skia.pdf.0.png
new file mode 100644
index 0000000..8a6b8ed
--- /dev/null
+++ b/testing/resources/pixel/bug_972999_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_983289.in b/testing/resources/pixel/bug_983289.in
new file mode 100644
index 0000000..438af5d
--- /dev/null
+++ b/testing/resources/pixel/bug_983289.in
@@ -0,0 +1,35 @@
+{{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
+  /MediaBox [0 0 612 792]
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+2 0 0 2 0 0 cm
+-10926 -22396 m
+11272 23097 l
+S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_983289_expected.pdf.0.png b/testing/resources/pixel/bug_983289_expected.pdf.0.png
new file mode 100644
index 0000000..27049d5
--- /dev/null
+++ b/testing/resources/pixel/bug_983289_expected.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_agg_mac.pdf.0.png b/testing/resources/pixel/bug_984811_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..4a217fa
--- /dev/null
+++ b/testing/resources/pixel/bug_984811_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_984811_expected_mac.pdf.0.png b/testing/resources/pixel/bug_984811_expected_mac.pdf.0.png
deleted file mode 100644
index e1ae9f2..0000000
--- a/testing/resources/pixel/bug_984811_expected_mac.pdf.0.png
+++ /dev/null
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/bug_986108.in b/testing/resources/pixel/bug_986108.in
new file mode 100644
index 0000000..7af65f4
--- /dev/null
+++ b/testing/resources/pixel/bug_986108.in
@@ -0,0 +1,122 @@
+{{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
+  /MediaBox [0 0 200 200]
+  /Contents 4 0 R
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /ExtGState <<
+      /GS0 5 0 R
+    >>
+    /XObject <<
+      /Fm0 6 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+/GS0 gs
+/Fm0 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /ExtGState
+  /OP false
+  /op false
+  /AIS false
+  /BM /Normal
+  /OPM 0
+  /CA 1.0
+  /SA true
+  /ca 1.0
+  /SMask 7 0 R
+>>
+endobj
+{{object 6 0}} <<
+  /Subtype /Form
+  {{streamlen}}
+>>
+stream
+q
+0 0 50 60 re f
+Q
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /Mask
+  /BC [0.0]
+  /S /Luminosity
+  /G 8 0 R
+>>
+endobj
+{{object 8 0}} <<
+  /Subtype /Form
+  /BBox [0 411 621 -10]
+  /Resources <<
+    /XObject <<
+      /Fm0 9 0 R
+    >>
+  >>
+{{streamlen}}
+>>
+stream
+q
+0 410 500 -440 re f
+/Fm0 Do
+Q
+endstream
+endobj
+{{object 9 0}} <<
+  /Subtype /Form
+  /Resources <<
+    /Shading <<
+      /Sh0 10 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+q
+/Sh0 sh
+Q
+endstream
+endobj
+{{object 10 0}} <<
+  /ShadingType 2
+  /ColorSpace /DeviceRGB
+  /Coords [0.0 0.0 1.0 0.0]
+  /BBox [-0.1 -0.9 1.1 0.87]
+  /Extend [true true]
+  /Function 11 0 R
+  /Domain [0.0 1.0]
+>>
+endobj
+{{object 11 0}} <<
+  /FunctionType 2
+  /Domain [0.0 1.0]
+  /C0 [0.8 0.1 0.2]
+  /C1 [0.2 0.025 0.05]
+  /N 1
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_986108_expected.pdf.0.png b/testing/resources/pixel/bug_986108_expected.pdf.0.png
new file mode 100644
index 0000000..f97e340
--- /dev/null
+++ b/testing/resources/pixel/bug_986108_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/checkbox_radiobutton.evt b/testing/resources/pixel/checkbox_radiobutton.evt
new file mode 100644
index 0000000..c519dcb
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton.evt
@@ -0,0 +1,11 @@
+# Check the checkbox
+mousemove,145,220
+mousedown,left,145,220
+mouseup,left,145,220
+
+# Tab to radio button from checkbox
+keycode,9
+keycode,9
+
+# Press Enter to select radio button
+charcode,13
\ No newline at end of file
diff --git a/testing/resources/pixel/checkbox_radiobutton.fragment b/testing/resources/pixel/checkbox_radiobutton.fragment
new file mode 100644
index 0000000..013992e
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton.fragment
@@ -0,0 +1,187 @@
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 300 300]
+  /Contents 4 0 R
+  /Annots [5 0 R 6 0 R 8 0 R 10 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+q
+1 w
+1 0 1 RG
+1 .752941 .796078 rg
+n 135.5 250.5 19 19 re B*
+Q
+q
+2 w
+.1 .1 .1 RG
+.8 .843 1 rg
+n 136 211 18 18 re B*
+Q
+q
+2 w
+0 .501961 0 RG
+0 0 1 rg
+n
+104 110 m
+104 114.9706 99.97056 119 95 119 c
+90.02944 119 86 114.9706 86 110 c
+86 105.0294 90.02944 101 95 101 c
+99.97056 101 104 105.0294 104 110 c
+B*
+Q
+q
+2 w
+0 .501961 0 RG
+0 0 1 rg
+n
+104 60 m
+104 64.97056 99.97056 69 95 69 c
+90.02944 69 86 64.97056 86 60 c
+86 55.02944 90.02944 51 95 51 c
+99.97056 51 104 55.02944 104 60 c
+B*
+Q
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /Ff 1
+  /Rect [135 250 155 270]
+  /T (readOnlyCheckbox)
+  /AP <<
+    /N <<
+      /Yes 11 0 R
+    >>
+  >>
+  /AS /Off
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /Ff 2
+  /Rect [135 210 155 230]
+  /T (checkbox)
+  /AP <<
+    /N <<
+      /Yes 11 0 R
+    >>
+  >>
+  /AS /Off
+>>
+endobj
+{{object 7 0}} <<
+  /FT /Btn
+  /Ff 49153
+  /T (readOnlyRadioButton)
+  /Kids [8 0 R]
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /T (Widget8)
+  /FT /Btn
+  /Parent 7 0 R
+  /Rect [85 100 105 120]
+  /AP <<
+    /N <<
+      /value1 12 0 R
+    >>
+  >>
+  /AS /Off
+>>
+endobj
+{{object 9 0}} <<
+  /FT /Btn
+  /Ff 49154
+  /T (radioButton)
+  /Kids [10 0 R]
+>>
+endobj
+{{object 10 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /Parent 9 0 R
+  /Rect [85 50 105 70]
+  /AP <<
+    /N <<
+      /value1 12 0 R
+    >>
+  >>
+  /AS /Off
+>>
+endobj
+{{object 11 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  {{streamlen}}
+>>
+stream
+q
+.1 .1 .1 rg .1 .1 .1 RG
+17.2 15.95145 m
+11.20694 10 l
+17.2 4.027746 l
+15.97225 2.8 l
+10 8.793064 l
+4.027746 2.8 l
+2.8 4.027746 l
+8.813873 10 l
+2.8 15.97225 l
+4.027746 17.2 l
+10 11.20694 l
+15.97225 17.2 l
+17.2 15.95145 l
+h
+f
+Q
+endstream
+endobj
+{{object 12 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  {{streamlen}}
+>>
+stream
+q
+1 .752941 .796078 rg 1 .752941 .796078 RG
+17.2 15.95145 m
+11.20694 10 l
+17.2 4.027746 l
+15.97225 2.8 l
+10 8.793064 l
+4.027746 2.8 l
+2.8 4.027746 l
+8.813873 10 l
+2.8 15.97225 l
+4.027746 17.2 l
+10 11.20694 l
+15.97225 17.2 l
+17.2 15.95145 l
+h
+f
+Q
+endstream
+endobj
diff --git a/testing/resources/pixel/checkbox_radiobutton.in b/testing/resources/pixel/checkbox_radiobutton.in
new file mode 100644
index 0000000..116c638
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton.in
@@ -0,0 +1,14 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [5 0 R 6 0 R 7 0 R 9 0 R]
+  >>
+>>
+endobj
+{{include checkbox_radiobutton.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/checkbox_radiobutton_expected.pdf.0.png b/testing/resources/pixel/checkbox_radiobutton_expected.pdf.0.png
new file mode 100644
index 0000000..8baabb7
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/checkbox_radiobutton_expected_skia.pdf.0.png b/testing/resources/pixel/checkbox_radiobutton_expected_skia.pdf.0.png
new file mode 100644
index 0000000..fe713a6
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/checkbox_radiobutton_hide.in b/testing/resources/pixel/checkbox_radiobutton_hide.in
new file mode 100644
index 0000000..340b6d5
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton_hide.in
@@ -0,0 +1,23 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [5 0 R 6 0 R 7 0 R 9 0 R]
+  >>
+  /OpenAction 13 0 R
+>>
+endobj
+{{include checkbox_radiobutton.fragment}}
+% Make some buttons be hidden. Note that /T as an array of dict references
+% does not seem to work as expected.
+{{object 13 0}} <<
+  /Type /Action
+  /S /Hide
+  /T [(readOnlyRadioButton.Widget8) (readOnlyCheckbox)]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/checkbox_radiobutton_hide_expected.pdf.0.png b/testing/resources/pixel/checkbox_radiobutton_hide_expected.pdf.0.png
new file mode 100644
index 0000000..361c05f
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton_hide_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/checkbox_radiobutton_hide_expected_skia.pdf.0.png b/testing/resources/pixel/checkbox_radiobutton_hide_expected_skia.pdf.0.png
new file mode 100644
index 0000000..bdb2d6e
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton_hide_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/checkbox_radiobutton_reset.in b/testing/resources/pixel/checkbox_radiobutton_reset.in
new file mode 100644
index 0000000..869107a
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton_reset.in
@@ -0,0 +1,25 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [5 0 R 6 0 R 7 0 R 9 0 R]
+  >>
+  /OpenAction 13 0 R
+>>
+endobj
+{{include checkbox_radiobutton.fragment}}
+% Make some buttons be reset. Note that /T as an array of dict references
+% does not seem to work as expected. /Flags 1 means all fields *except*
+% those listed.
+{{object 13 0}} <<
+  /Type /Action
+  /S /ResetForm
+  /Fields [(readOnlyRadioButton.Widget8) (readOnlyCheckbox)]
+  /Flags 1
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/checkbox_radiobutton_reset_expected.pdf.0.png b/testing/resources/pixel/checkbox_radiobutton_reset_expected.pdf.0.png
new file mode 100644
index 0000000..dd94188
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton_reset_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/checkbox_radiobutton_reset_expected_skia.pdf.0.png b/testing/resources/pixel/checkbox_radiobutton_reset_expected_skia.pdf.0.png
new file mode 100644
index 0000000..057ecda
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton_reset_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/combobox_form.evt b/testing/resources/pixel/combobox_form.evt
new file mode 100644
index 0000000..8211937
--- /dev/null
+++ b/testing/resources/pixel/combobox_form.evt
@@ -0,0 +1,10 @@
+# Check the combobox control
+mousemove,102,410
+mousedown,left,102,410
+mouseup,left,102,410
+
+# Press Enter to open the pop-up menu
+charcode,13
+
+# Press Space to make sure that the pop-up is not dismissed
+charcode,32
\ No newline at end of file
diff --git a/testing/resources/pixel/combobox_form.in b/testing/resources/pixel/combobox_form.in
new file mode 100644
index 0000000..56adea4
--- /dev/null
+++ b/testing/resources/pixel/combobox_form.in
@@ -0,0 +1,35 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [4 0 R]
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /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 /Widget
+  /FT /Ch
+  /Ff 131072
+  /T (Combo)
+  /Rect [100 400 200 430]
+  /Opt [() () ()]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/combobox_form_expected.pdf.0.png b/testing/resources/pixel/combobox_form_expected.pdf.0.png
new file mode 100644
index 0000000..7ecd75d
--- /dev/null
+++ b/testing/resources/pixel/combobox_form_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/combobox_form_expected_skia.pdf.0.png b/testing/resources/pixel/combobox_form_expected_skia.pdf.0.png
new file mode 100644
index 0000000..4686e96
--- /dev/null
+++ b/testing/resources/pixel/combobox_form_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/font_size_expected.pdf.0.png b/testing/resources/pixel/font_size_expected.pdf.0.png
index b4506e0..91828b9 100644
--- a/testing/resources/pixel/font_size_expected.pdf.0.png
+++ b/testing/resources/pixel/font_size_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/font_size_expected_agg_mac.pdf.0.png b/testing/resources/pixel/font_size_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..02e91d4
--- /dev/null
+++ b/testing/resources/pixel/font_size_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/font_size_expected_mac.pdf.0.png b/testing/resources/pixel/font_size_expected_mac.pdf.0.png
deleted file mode 100644
index dfd0690..0000000
--- a/testing/resources/pixel/font_size_expected_mac.pdf.0.png
+++ /dev/null
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/font_size_expected_win.pdf.0.png b/testing/resources/pixel/font_size_expected_win.pdf.0.png
deleted file mode 100644
index a0d56aa..0000000
--- a/testing/resources/pixel/font_size_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers1_expected.pdf.0.png b/testing/resources/pixel/generation_numbers1_expected.pdf.0.png
index f2d3ba9..522a738 100644
--- a/testing/resources/pixel/generation_numbers1_expected.pdf.0.png
+++ b/testing/resources/pixel/generation_numbers1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers1_expected_agg_mac.pdf.0.png b/testing/resources/pixel/generation_numbers1_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..c946aa7
--- /dev/null
+++ b/testing/resources/pixel/generation_numbers1_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers1_expected_mac.pdf.0.png b/testing/resources/pixel/generation_numbers1_expected_mac.pdf.0.png
deleted file mode 100644
index 8d973ac..0000000
--- a/testing/resources/pixel/generation_numbers1_expected_mac.pdf.0.png
+++ /dev/null
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_numbers1_expected_win.pdf.0.png b/testing/resources/pixel/generation_numbers1_expected_win.pdf.0.png
deleted file mode 100644
index 7f1047c..0000000
--- a/testing/resources/pixel/generation_numbers1_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers2_expected.pdf.0.png b/testing/resources/pixel/generation_numbers2_expected.pdf.0.png
index f2d3ba9..522a738 100644
--- a/testing/resources/pixel/generation_numbers2_expected.pdf.0.png
+++ b/testing/resources/pixel/generation_numbers2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers2_expected_agg_mac.pdf.0.png b/testing/resources/pixel/generation_numbers2_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..c946aa7
--- /dev/null
+++ b/testing/resources/pixel/generation_numbers2_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers2_expected_mac.pdf.0.png b/testing/resources/pixel/generation_numbers2_expected_mac.pdf.0.png
deleted file mode 100644
index 8d973ac..0000000
--- a/testing/resources/pixel/generation_numbers2_expected_mac.pdf.0.png
+++ /dev/null
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/generation_numbers2_expected_win.pdf.0.png b/testing/resources/pixel/generation_numbers2_expected_win.pdf.0.png
deleted file mode 100644
index 7f1047c..0000000
--- a/testing/resources/pixel/generation_numbers2_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/image_transformer_other.in b/testing/resources/pixel/image_transformer_other.in
new file mode 100644
index 0000000..5d1f580
--- /dev/null
+++ b/testing/resources/pixel/image_transformer_other.in
@@ -0,0 +1,128 @@
+{{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 200]
+  /Resources <<
+    /ColorSpace <<
+      /CS0 5 0 R
+    >>
+    /XObject <<
+      /ImMonoPalette 6 0 R
+      /ImMono 7 0 R
+      /ImColor 8 0 R
+      /ImColorWithMask 9 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+-16 64 64 0 20 20 cm
+/ImMonoPalette Do
+Q
+q
+16 64 64 0 120 20 cm
+/ImMono Do
+Q
+q
+16 64 64 16 20 120 cm
+/ImColor Do
+Q
+q
+16 64 -64 16 120 120 cm
+/ImColorWithMask Do
+Q
+endstream
+endobj
+{{object 5 0}} [
+  /Indexed
+  /DeviceGray
+  0
+  <cc>
+]
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 1
+  /ColorSpace 5 0 R
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+0000
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 4
+  /Height 4
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter /ASCIIHexDecode
+  {{streamlen}}
+>>
+stream
+00000000
+33333333
+99999999
+cccccccc
+endstream
+endobj
+{{object 8 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter /ASCIIHexDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+ff0000ff0000ff0000ff0000
+ff0000ff0000ff0000ff0000
+00ff0000ff0000ff0000ff00
+00ff0000ff0000ff0000ff00
+endstream
+endobj
+{{object 9 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter /ASCIIHexDecode
+  /Height 4
+  /Width 4
+  /Mask [0 255 0 0 0 0]
+  {{streamlen}}
+>>
+stream
+ff0000ff0000ff0000ff0000
+ff0000ff0000ff0000ff0000
+00ff0000ff0000ff0000ff00
+00ff0000ff0000ff0000ff00
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/image_transformer_other_expected.pdf.0.png b/testing/resources/pixel/image_transformer_other_expected.pdf.0.png
new file mode 100644
index 0000000..e9aab95
--- /dev/null
+++ b/testing/resources/pixel/image_transformer_other_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/image_transformer_other_expected_skia.pdf.0.png b/testing/resources/pixel/image_transformer_other_expected_skia.pdf.0.png
new file mode 100644
index 0000000..e65f919
--- /dev/null
+++ b/testing/resources/pixel/image_transformer_other_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/jpxdecode.in b/testing/resources/pixel/jpxdecode.in
new file mode 100644
index 0000000..d69b058
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode.in
@@ -0,0 +1,177 @@
+{{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 88 128]
+  /Resources <<
+    /XObject <<
+      /ImGray 5 0 R
+      /ImGrayAlpha 6 0 R
+      /ImRGB 7 0 R
+      /ImRGBAlpha 8 0 R
+      /ImCMYK 9 0 R
+      /ImCMYKAlpha 10 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+
+% 50% gray background rectangle
+q
+  0.5 0.5 0.5 rg
+  0 0 88 128 re
+  f
+Q
+
+% grayscale, grayscale with alpha
+q
+  32 0 0 32 8 88 cm
+  /ImGray Do
+Q
+q
+  32 0 0 32 48 88 cm
+  /ImGrayAlpha Do
+Q
+
+% RGB, RGB with alpha
+q
+  32 0 0 32 8 48 cm
+  /ImRGB Do
+Q
+q
+  32 0 0 32 48 48 cm
+  /ImRGBAlpha Do
+Q
+
+% CMYK, CMYK with alpha
+q
+  32 0 0 32 8 8 cm
+  /ImCMYK Do
+Q
+q
+  32 0 0 32 48 8 cm
+  /ImCMYKAlpha Do
+Q
+
+endstream
+endobj
+
+% grayscale
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../gray.jp2}}
+endstream
+endobj
+
+% grayscale with opacity
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter /JPXDecode
+  /Height 4
+  /SMaskInData 1
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../gray-alpha.jp2}}
+endstream
+endobj
+
+% RGB
+{{object 7 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../RGB.jp2}}
+endstream
+endobj
+
+% RGB with opacity
+{{object 8 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter /JPXDecode
+  /Height 4
+  /SMaskInData 1
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../RGB-alpha.jp2}}
+endstream
+endobj
+
+% CMYK
+{{object 9 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceCMYK
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../CMYK.jpf}}
+endstream
+endobj
+
+% CMYK with opacity.
+{{object 10 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceCMYK
+  /Filter /JPXDecode
+  /Height 4
+  /SMaskInData 1
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../CMYK-alpha.jpf}}
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/jpxdecode_expected.pdf.0.png b/testing/resources/pixel/jpxdecode_expected.pdf.0.png
new file mode 100644
index 0000000..9051cb0
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/jpxdecode_with_mismatch_colorspace.in b/testing/resources/pixel/jpxdecode_with_mismatch_colorspace.in
new file mode 100644
index 0000000..ddf3068
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_with_mismatch_colorspace.in
@@ -0,0 +1,180 @@
+{{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 88 128]
+  /Resources <<
+    /XObject <<
+      /ImGrayCsRGB 5 0 R
+      /ImGrayCsCMYK 6 0 R
+      /ImRGBCsGray 7 0 R
+      /ImRGBCsCMYK 8 0 R
+      /ImCMYKCsGray 9 0 R
+      /ImCMYKCsRGB 10 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+
+% 50% gray background rectangle
+q
+  0.5 0.5 0.5 rg
+  0 0 88 128 re
+  f
+Q
+
+% grayscale image with RGB /ColorSpace entry
+q
+  32 0 0 32 8 88 cm
+  /ImGrayCsRGB Do
+Q
+
+% grayscale image with CMYK /ColorSpace entry
+q
+  32 0 0 32 48 88 cm
+  /ImGrayCsCMYK Do
+Q
+
+% RGB image with grayscale /ColorSpace entry
+q
+  32 0 0 32 8 48 cm
+  /ImRGBCsGray Do
+Q
+
+% RGB image with CMYK /ColorSpace entry
+q
+  32 0 0 32 48 48 cm
+  /ImRGBCsCMYK Do
+Q
+
+% CMYK image with grayscale /ColorSpace entry
+q
+  32 0 0 32 8 8 cm
+  /ImCMYKCsGray Do
+Q
+
+% CMYK image with RGB /ColorSpace entry
+q
+  32 0 0 32 48 8 cm
+  /ImCMYKCsRGB Do
+Q
+
+endstream
+endobj
+
+% grayscale image with RGB /ColorSpace
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../gray.jp2}}
+endstream
+endobj
+
+% grayscale image with CMYK /ColorSpace
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceCMYK
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../gray.jp2}}
+endstream
+endobj
+
+% RGB image with grayscale /ColorSpace
+{{object 7 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../RGB.jp2}}
+endstream
+endobj
+
+% RGB image with CMYK /ColorSpace
+{{object 8 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceCMYK
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../RGB.jp2}}
+endstream
+endobj
+
+% CMYK image with grayscale /ColorSpace
+{{object 9 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../CMYK.jpf}}
+endstream
+endobj
+
+% CMYK image with RGB /ColorSpace
+{{object 10 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../CMYK.jpf}}
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/jpxdecode_with_mismatch_colorspace_expected.pdf.0.png b/testing/resources/pixel/jpxdecode_with_mismatch_colorspace_expected.pdf.0.png
new file mode 100644
index 0000000..65743e0
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_with_mismatch_colorspace_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/jpxdecode_without_bitspercomponent.in b/testing/resources/pixel/jpxdecode_without_bitspercomponent.in
new file mode 100644
index 0000000..59b516f
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_without_bitspercomponent.in
@@ -0,0 +1,171 @@
+{{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 88 128]
+  /Resources <<
+    /XObject <<
+      /ImGray 5 0 R
+      /ImGrayAlpha 6 0 R
+      /ImRGB 7 0 R
+      /ImRGBAlpha 8 0 R
+      /ImCMYK 9 0 R
+      /ImCMYKAlpha 10 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+
+% 50% gray background rectangle
+q
+  0.5 0.5 0.5 rg
+  0 0 88 128 re
+  f
+Q
+
+% grayscale, grayscale with alpha
+q
+  32 0 0 32 8 88 cm
+  /ImGray Do
+Q
+q
+  32 0 0 32 48 88 cm
+  /ImGrayAlpha Do
+Q
+
+% RGB, RGB with alpha
+q
+  32 0 0 32 8 48 cm
+  /ImRGB Do
+Q
+q
+  32 0 0 32 48 48 cm
+  /ImRGBAlpha Do
+Q
+
+% CMYK, CMYK with alpha
+q
+  32 0 0 32 8 8 cm
+  /ImCMYK Do
+Q
+q
+  32 0 0 32 48 8 cm
+  /ImCMYKAlpha Do
+Q
+
+endstream
+endobj
+
+% grayscale
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /ColorSpace /DeviceGray
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../gray.jp2}}
+endstream
+endobj
+
+% grayscale with opacity
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /ColorSpace /DeviceGray
+  /Filter /JPXDecode
+  /Height 4
+  /SMaskInData 1
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../gray-alpha.jp2}}
+endstream
+endobj
+
+% RGB
+{{object 7 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /ColorSpace /DeviceRGB
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../RGB.jp2}}
+endstream
+endobj
+
+% RGB with opacity
+{{object 8 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /ColorSpace /DeviceRGB
+  /Filter /JPXDecode
+  /Height 4
+  /SMaskInData 1
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../RGB-alpha.jp2}}
+endstream
+endobj
+
+% CMYK
+{{object 9 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /ColorSpace /DeviceCMYK
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../CMYK.jpf}}
+endstream
+endobj
+
+% CMYK with opacity.
+{{object 10 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /ColorSpace /DeviceCMYK
+  /Filter /JPXDecode
+  /Height 4
+  /SMaskInData 1
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../CMYK-alpha.jpf}}
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/jpxdecode_without_bitspercomponent_expected.pdf.0.png b/testing/resources/pixel/jpxdecode_without_bitspercomponent_expected.pdf.0.png
new file mode 100644
index 0000000..9051cb0
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_without_bitspercomponent_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/jpxdecode_without_colorspace.in b/testing/resources/pixel/jpxdecode_without_colorspace.in
new file mode 100644
index 0000000..0ffb64f
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_without_colorspace.in
@@ -0,0 +1,171 @@
+{{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 88 128]
+  /Resources <<
+    /XObject <<
+      /ImGray 5 0 R
+      /ImGrayAlpha 6 0 R
+      /ImRGB 7 0 R
+      /ImRGBAlpha 8 0 R
+      /ImCMYK 9 0 R
+      /ImCMYKAlpha 10 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+
+% 50% gray background rectangle
+q
+  0.5 0.5 0.5 rg
+  0 0 88 128 re
+  f
+Q
+
+% grayscale, grayscale with alpha
+q
+  32 0 0 32 8 88 cm
+  /ImGray Do
+Q
+q
+  32 0 0 32 48 88 cm
+  /ImGrayAlpha Do
+Q
+
+% RGB, RGB with alpha
+q
+  32 0 0 32 8 48 cm
+  /ImRGB Do
+Q
+q
+  32 0 0 32 48 48 cm
+  /ImRGBAlpha Do
+Q
+
+% CMYK, CMYK with alpha
+q
+  32 0 0 32 8 8 cm
+  /ImCMYK Do
+Q
+q
+  32 0 0 32 48 8 cm
+  /ImCMYKAlpha Do
+Q
+
+endstream
+endobj
+
+% grayscale
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../gray.jp2}}
+endstream
+endobj
+
+% grayscale with opacity
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /Filter /JPXDecode
+  /Height 4
+  /SMaskInData 1
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../gray-alpha.jp2}}
+endstream
+endobj
+
+% RGB
+{{object 7 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../RGB.jp2}}
+endstream
+endobj
+
+% RGB with opacity
+{{object 8 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /Filter /JPXDecode
+  /Height 4
+  /SMaskInData 1
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../RGB-alpha.jp2}}
+endstream
+endobj
+
+% CMYK
+{{object 9 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../CMYK.jpf}}
+endstream
+endobj
+
+% CMYK with opacity.
+{{object 10 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /Filter /JPXDecode
+  /Height 4
+  /SMaskInData 1
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../CMYK-alpha.jpf}}
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/jpxdecode_without_colorspace_expected.pdf.0.png b/testing/resources/pixel/jpxdecode_without_colorspace_expected.pdf.0.png
new file mode 100644
index 0000000..9051cb0
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_without_colorspace_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/jpxdecode_without_smaskindata.in b/testing/resources/pixel/jpxdecode_without_smaskindata.in
new file mode 100644
index 0000000..ab29a60
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_without_smaskindata.in
@@ -0,0 +1,111 @@
+{{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 48 128]
+  /Resources <<
+    /XObject <<
+      /ImGrayAlpha 5 0 R
+      /ImRGBAlpha 6 0 R
+      /ImCMYKAlpha 7 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+
+% 50% gray background rectangle
+q
+  0.5 0.5 0.5 rg
+  0 0 48 128 re
+  f
+Q
+
+% grayscale with ignored alpha
+q
+  32 0 0 32 8 88 cm
+  /ImGrayAlpha Do
+Q
+
+% RGB with ignored alpha
+q
+  32 0 0 32 8 48 cm
+  /ImRGBAlpha Do
+Q
+
+% CMYK with ignored alpha
+q
+  32 0 0 32 8 8 cm
+  /ImCMYKAlpha Do
+Q
+
+endstream
+endobj
+
+% grayscale with opacity, but the soft-mask information is not used.
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../gray-alpha.jp2}}
+endstream
+endobj
+
+% RGB with opacity, but the soft-mask information is not used.
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../RGB-alpha.jp2}}
+endstream
+endobj
+
+% CMYK with opacity, but the soft-mask information is not used.
+{{object 7 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceCMYK
+  /Filter /JPXDecode
+  /Height 4
+  /Width 4
+  {{streamlen}}
+>>
+stream
+{{include ../CMYK-alpha.jpf}}
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/jpxdecode_without_smaskindata_expected.pdf.0.png b/testing/resources/pixel/jpxdecode_without_smaskindata_expected.pdf.0.png
new file mode 100644
index 0000000..5405d1e
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_without_smaskindata_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/long_dashed_line.in b/testing/resources/pixel/long_dashed_line.in
new file mode 100644
index 0000000..623adfa
--- /dev/null
+++ b/testing/resources/pixel/long_dashed_line.in
@@ -0,0 +1,33 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 800 800]
+  /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
+[2 2] 0 d
+100 100 m
+100 -75697008 l
+S
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/long_dashed_line_expected.pdf.0.png b/testing/resources/pixel/long_dashed_line_expected.pdf.0.png
new file mode 100644
index 0000000..41b3efb
--- /dev/null
+++ b/testing/resources/pixel/long_dashed_line_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/long_dashed_line_expected_skia.pdf.0.png b/testing/resources/pixel/long_dashed_line_expected_skia.pdf.0.png
new file mode 100644
index 0000000..3ffc2d6
--- /dev/null
+++ b/testing/resources/pixel/long_dashed_line_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
new file mode 100644
index 0000000..97fa17e
--- /dev/null
+++ b/testing/resources/pixel/matte_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/password.evt b/testing/resources/pixel/password.evt
new file mode 100644
index 0000000..3051015
--- /dev/null
+++ b/testing/resources/pixel/password.evt
@@ -0,0 +1,26 @@
+mousemove,102,102
+mousedown,left,102,102
+mouseup,left,102,102
+charcode,116
+charcode,105
+charcode,103
+charcode,101
+charcode,114
+charcode,115
+charcode,115
+charcode,115
+charcode,115
+
+mousemove,102,162
+mousedown,left,102,162
+mouseup,left,102,162
+charcode,116
+charcode,105
+charcode,103
+charcode,101
+charcode,114
+charcode,115
+charcode,115
+charcode,115
+charcode,115
+
diff --git a/testing/resources/pixel/password.in b/testing/resources/pixel/password.in
new file mode 100644
index 0000000..a50f0af
--- /dev/null
+++ b/testing/resources/pixel/password.in
@@ -0,0 +1,54 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [
+      4 0 R
+      5 0 R
+    ]
+    /NeedAppearances true
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [ 0 0 300 200 ]
+  /Annots [
+    4 0 R
+    5 0 R
+  ]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /T (Text Box)
+  /Rect [ 100 100 200 130 ]
+  /MaxLen 5
+  /Q 1
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /Ff 8192
+  /T (Password Box)
+  /Rect [ 100 160 200 190 ]
+  /MaxLen 5
+  /Q 2
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/password_expected.pdf.0.png b/testing/resources/pixel/password_expected.pdf.0.png
new file mode 100644
index 0000000..5040ef6
--- /dev/null
+++ b/testing/resources/pixel/password_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/password_expected_mac.pdf.0.png b/testing/resources/pixel/password_expected_mac.pdf.0.png
new file mode 100644
index 0000000..2f4239f
--- /dev/null
+++ b/testing/resources/pixel/password_expected_mac.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
new file mode 100644
index 0000000..c5c0b23
--- /dev/null
+++ b/testing/resources/pixel/password_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/radial_shading_point_at_border_expected_skia.pdf.0.png b/testing/resources/pixel/radial_shading_point_at_border_expected_skia.pdf.0.png
new file mode 100644
index 0000000..92f2349
--- /dev/null
+++ b/testing/resources/pixel/radial_shading_point_at_border_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/radial_shading_point_at_border_no_extend_expected_skia.pdf.0.png b/testing/resources/pixel/radial_shading_point_at_border_no_extend_expected_skia.pdf.0.png
new file mode 100644
index 0000000..846a9ea
--- /dev/null
+++ b/testing/resources/pixel/radial_shading_point_at_border_no_extend_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/radial_shading_point_at_center_expected_skia.pdf.0.png b/testing/resources/pixel/radial_shading_point_at_center_expected_skia.pdf.0.png
new file mode 100644
index 0000000..756ddb8
--- /dev/null
+++ b/testing/resources/pixel/radial_shading_point_at_center_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/reset_button.evt b/testing/resources/pixel/reset_button.evt
new file mode 100644
index 0000000..1606926
--- /dev/null
+++ b/testing/resources/pixel/reset_button.evt
@@ -0,0 +1,10 @@
+# Check the checkbox
+mousemove,145,220
+mousedown,left,145,220
+mouseup,left,145,220
+
+# Tab to reset button from checkbox
+keycode,9
+
+# Press Enter to reset the form
+charcode,13
\ No newline at end of file
diff --git a/testing/resources/pixel/reset_button.in b/testing/resources/pixel/reset_button.in
new file mode 100644
index 0000000..6f93570
--- /dev/null
+++ b/testing/resources/pixel/reset_button.in
@@ -0,0 +1,97 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [5 0 R 6 0 R]
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 300 300]
+  /Contents 4 0 R
+  /Annots [5 0 R 6 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+2 w
+.1 .1 .1 RG
+.8 .843 1 rg
+n 136 211 18 18 re B*
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /Ff 2
+  /P 3 0 R
+  /Rect [135 210 155 230]
+  /T (checkbox)
+  /AP <<
+    /N <<
+      /Yes 7 0 R
+    >>
+  >>
+  /AS /Off
+  /V /Off
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /P 3 0 R
+  /Rect [135 110 185 150]
+  /T (Reset button)
+  /Ff 65536
+  /A <<
+   /S /ResetForm
+   /Fields [5 0 R]
+   /Flags 1
+  >>
+  /MK <<
+    /BC [0.0 0.0 0.4]
+    /BG [0.9 0.9 0.9]
+  >>
+>>
+endobj
+{{object 7 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  {{streamlen}}
+>>
+stream
+q
+17.2 15.95145 m
+17.2 4.027746 l
+15.97225 2.8 l
+4.027746 2.8 l
+4.027746 17.2 l
+15.97225 17.2 l
+h
+f
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/pixel/reset_button_expected.pdf.0.png b/testing/resources/pixel/reset_button_expected.pdf.0.png
new file mode 100644
index 0000000..e36c30d
--- /dev/null
+++ b/testing/resources/pixel/reset_button_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/scrollable_widgets1.evt b/testing/resources/pixel/scrollable_widgets1.evt
new file mode 100644
index 0000000..c595cd7
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets1.evt
@@ -0,0 +1,29 @@
+# Must move the mouse and click to give widget focus.
+mousemove,150,415
+mousedown,left,150,415
+# Scroll all the way down.
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
diff --git a/testing/resources/pixel/scrollable_widgets1.in b/testing/resources/pixel/scrollable_widgets1.in
new file mode 100644
index 0000000..9453b7c
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets1.in
@@ -0,0 +1,52 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [5 0 R]
+    /DR <<
+      /Font <<
+        /F1 4 0 R
+      >>
+    >>
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 300 600]
+  /Annots [5 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 2097152
+  /T (Listbox_MultiSelect)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [100 400 200 430]
+  /Opt [(Apple) (Banana) (Cherry) (Date) (Elderberry) (Fig) (Guava) (Honeydew)
+        (Indian Fig) (Jackfruit) (Kiwi) (Lemon) (Mango) (Nectarine) (Orange)
+        (Persimmon) (Quince) (Raspberry) (Strawberry) (Tamarind) (Ugli Fruit)
+        (Voavanga) (Wolfberry) (Xigua) (Yangmei) (Zucchini)]
+  /V (Banana)
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/scrollable_widgets1_expected.pdf.0.png b/testing/resources/pixel/scrollable_widgets1_expected.pdf.0.png
new file mode 100644
index 0000000..7760af7
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/scrollable_widgets1_expected_mac.pdf.0.png b/testing/resources/pixel/scrollable_widgets1_expected_mac.pdf.0.png
new file mode 100644
index 0000000..5dbdc5a
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets1_expected_mac.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
new file mode 100644
index 0000000..348c1a5
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets1_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/scrollable_widgets2.evt b/testing/resources/pixel/scrollable_widgets2.evt
new file mode 100644
index 0000000..59b8a4b
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets2.evt
@@ -0,0 +1,34 @@
+# Must move the mouse and click to give widget focus.
+mousemove,150,415
+mousedown,left,150,415
+# Scroll all the way down and then scroll back up a bit.
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,1
+mousewheel,150,415,0,1
+mousewheel,150,415,0,1
+mousewheel,150,415,0,1
+mousewheel,150,415,0,1
diff --git a/testing/resources/pixel/scrollable_widgets2.in b/testing/resources/pixel/scrollable_widgets2.in
new file mode 100644
index 0000000..9453b7c
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets2.in
@@ -0,0 +1,52 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [5 0 R]
+    /DR <<
+      /Font <<
+        /F1 4 0 R
+      >>
+    >>
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 300 600]
+  /Annots [5 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 2097152
+  /T (Listbox_MultiSelect)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [100 400 200 430]
+  /Opt [(Apple) (Banana) (Cherry) (Date) (Elderberry) (Fig) (Guava) (Honeydew)
+        (Indian Fig) (Jackfruit) (Kiwi) (Lemon) (Mango) (Nectarine) (Orange)
+        (Persimmon) (Quince) (Raspberry) (Strawberry) (Tamarind) (Ugli Fruit)
+        (Voavanga) (Wolfberry) (Xigua) (Yangmei) (Zucchini)]
+  /V (Banana)
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/scrollable_widgets2_expected.pdf.0.png b/testing/resources/pixel/scrollable_widgets2_expected.pdf.0.png
new file mode 100644
index 0000000..06e06ec
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/scrollable_widgets2_expected_mac.pdf.0.png b/testing/resources/pixel/scrollable_widgets2_expected_mac.pdf.0.png
new file mode 100644
index 0000000..aa4917f
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets2_expected_mac.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
new file mode 100644
index 0000000..79ca0f5
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets2_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/text_form_custom_font.in b/testing/resources/pixel/text_form_custom_font.in
new file mode 100644
index 0000000..af404e4
--- /dev/null
+++ b/testing/resources/pixel/text_form_custom_font.in
@@ -0,0 +1,81 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /DR <<
+      /Font <<
+        /TT0 5 0 R
+      >>
+    >>
+    /Fields [4 0 R]
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [4 0 R]
+  /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /DA (1 0 0 rg /TT0 16 Tf)
+  /Rect [50 100 150 130]
+  /T (Text Box)
+  /V (      )
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /FirstChar 32
+  /BaseFont /AAAAAD+Test
+  /FontDescriptor 6 0 R
+  /ToUnicode 7 0 R
+  /LastChar 32
+  /Widths [1055]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /FontDescriptor
+  /Descent -68
+  /MissingWidth 1000
+  /CapHeight 1149
+  /StemV 0
+  /FontFile2 8 0 R
+  /Flags 4
+  /FontBBox [0 -215 1000 932]
+  /FontName /AAAAAD+Test
+  /ItalicAngle 0
+  /Ascent 933
+>>
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+{{include ../bug_1388_cmap.fragment}}
+endstream
+endobj
+{{object 8 0}} <<
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+{{include ../bug_1388_truetype_font.fragment}}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/text_form_custom_font_expected.pdf.0.png b/testing/resources/pixel/text_form_custom_font_expected.pdf.0.png
new file mode 100644
index 0000000..3894ad5
--- /dev/null
+++ b/testing/resources/pixel/text_form_custom_font_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/text_form_custom_font_expected_mac.pdf.0.png b/testing/resources/pixel/text_form_custom_font_expected_mac.pdf.0.png
new file mode 100644
index 0000000..01b3dbf
--- /dev/null
+++ b/testing/resources/pixel/text_form_custom_font_expected_mac.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
new file mode 100644
index 0000000..5f898f8
--- /dev/null
+++ b/testing/resources/pixel/text_form_custom_font_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/type3_expected.pdf.0.png b/testing/resources/pixel/type3_expected.pdf.0.png
index 6837bb8..63d9e57 100644
--- a/testing/resources/pixel/type3_expected.pdf.0.png
+++ b/testing/resources/pixel/type3_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/use_symbolneu/bug_1449.in b/testing/resources/pixel/use_symbolneu/bug_1449.in
new file mode 100644
index 0000000..603eb56
--- /dev/null
+++ b/testing/resources/pixel/use_symbolneu/bug_1449.in
@@ -0,0 +1,47 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 100 100 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F0 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /Symbol
+  /FirstChar 31
+  /LastChar 255
+  /Name /F0
+>>
+endobj
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+25 25 Td /F0 12 Tf
+-0.036  Tc (\265) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/use_symbolneu/bug_1449_expected.pdf.0.png b/testing/resources/pixel/use_symbolneu/bug_1449_expected.pdf.0.png
new file mode 100644
index 0000000..319932a
--- /dev/null
+++ b/testing/resources/pixel/use_symbolneu/bug_1449_expected.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
new file mode 100644
index 0000000..e144c04
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/barcode_test_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/barcode_test_expected_win.pdf.0.png b/testing/resources/pixel/xfa_specific/barcode_test_expected_win.pdf.0.png
deleted file mode 100644
index 23b80a7..0000000
--- a/testing/resources/pixel/xfa_specific/barcode_test_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1282_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_1282_expected_skia.pdf.0.png
new file mode 100644
index 0000000..a29272f
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1282_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1286970.in b/testing/resources/pixel/xfa_specific/bug_1286970.in
new file mode 100644
index 0000000..4454de7
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1286970.in
@@ -0,0 +1,31 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform layout="rl-tb" name="subform1">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="72pt" h="72pt"/>
+        <medium stock="default" short="72pt" long="72pt"/>
+      </pageArea>
+    </pageSet>
+    <subformSet>
+      <subform><field h="73pt"></field></subform>
+    </subformSet>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/bug_1286970_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_1286970_expected.pdf.0.png
new file mode 100644
index 0000000..9b4c2d8
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1286970_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1337.in b/testing/resources/pixel/xfa_specific/bug_1337.in
new file mode 100644
index 0000000..d44a496
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1337.in
@@ -0,0 +1,38 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <draw name="StaticImage1" w="250pt" h="250pt" x="10pt" y="10pt">
+        <value>
+          <image contentType="image/gif">R0lGODdh+gD6AIABAP8AAP///ywAAAAA+gD6AAAC/oSPqcvtD6OctNqLs968+w+G4kiW5omm6sq27gvH8kzX9o3n+s73/g8MCofEovGITCqXzKbzCY1Kp9Sq9YrNarfcrvcLDovH5LL5jE6r1+y2+w2Py+f0uv2Oz+v3/L7/DxgoOEhYaHiImKi4yNjo+AgZKTlJWWl5iZmpucnZ6fkJGio6SlpqeoqaqrrK2ur6ChsrO0tba3uLm6u7y9vr+wscLDxMXGx8jJysvMzc7PwMHS09TV1tfY2drb3N3e39DR4uPk5ebn6Onq6+zt7u/g4fLz9PX29/j5+vv8/f7/8PMKDAgQQLGjyIMKHChQwbOnwIMaLEiRQrWryIMaPGV40cO3r8CDKkyJEkS5o8iTKlypUsW7p8CTOmzJk0a9q8iTOnzp08e/r8CTSo0KFEixo9ijSp0qVMmzp9CjWq1KlUq1q9ijWr1q1cu3r9Cjas2LFky14oAAA7</image>
+        </value>
+        <ui>
+          <imageEdit/>
+        </ui>
+      </draw>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/bug_1337_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_1337_expected.pdf.0.png
new file mode 100644
index 0000000..c0d8380
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1337_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_983137_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_983137_expected_skia.pdf.0.png
new file mode 100644
index 0000000..7c19b45
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_983137_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
new file mode 100644
index 0000000..da9e4b3
--- /dev/null
+++ 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
new file mode 100644
index 0000000..6adaba6
--- /dev/null
+++ 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.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected.pdf.0.png
index d20d7ea..c1cebee 100644
--- a/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected.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
new file mode 100644
index 0000000..b4b8c0d
--- /dev/null
+++ 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_password_field_background_fill_expected_win.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected_win.pdf.0.png
deleted file mode 100644
index 734b7e4..0000000
--- a/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected_win.pdf.0.png
+++ /dev/null
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
new file mode 100644
index 0000000..ab5214f
--- /dev/null
+++ 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/dynamic_table_color_and_width_expected_win.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected_win.pdf.0.png
deleted file mode 100644
index eae383c..0000000
--- a/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected.pdf.0.png
index a3d9830..adfabbc 100644
--- a/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected.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
new file mode 100644
index 0000000..d90125f
--- /dev/null
+++ 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/standard_symbols_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/standard_symbols_expected.pdf.0.png
new file mode 100644
index 0000000..62f3156
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/standard_symbols_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/standard_symbols_expected.pdf.1.png b/testing/resources/pixel/xfa_specific/standard_symbols_expected.pdf.1.png
new file mode 100644
index 0000000..e262e83
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/standard_symbols_expected.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_mac.pdf.0.png b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_mac.pdf.0.png
index 7cba6b9..b9066f4 100644
--- a/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_mac.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_mac.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
new file mode 100644
index 0000000..a2c47a3
--- /dev/null
+++ 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_list_box_caption_expected_win.pdf.0.png b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_win.pdf.0.png
deleted file mode 100644
index 71edafd..0000000
--- a/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.0.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.0.png
index 58aee9e..bc87726 100644
--- a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.1.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.1.png
index ad36aa9..4dbd85a 100644
--- a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.1.png
+++ b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.1.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
new file mode 100644
index 0000000..0a9ff37
--- /dev/null
+++ 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
new file mode 100644
index 0000000..fa7e97e
--- /dev/null
+++ 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/static_password_field_rotate_expected_win.pdf.0.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.0.png
deleted file mode 100644
index ea939ac..0000000
--- a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.1.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.1.png
deleted file mode 100644
index c73f1a9..0000000
--- a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.1.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/README.md b/testing/resources/pixel/xfa_specific/use_ahem/README.md
new file mode 100644
index 0000000..f3126e2
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/README.md
@@ -0,0 +1,2 @@
+The expected results in this directory should match those generated by running
+`pdfium_test` with the option `--font-dir=/path/to/pdfium/testing/resources/fonts`.
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/bug_997412.in b/testing/resources/pixel/xfa_specific/use_ahem/bug_997412.in
new file mode 100644
index 0000000..ecda561
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/bug_997412.in
@@ -0,0 +1,35 @@
+{{header}}
+{{include ../../../xfa_catalog_1_0.fragment}}
+{{include ../../../xfa_object_2_0.fragment}}
+{{include ../../../xfa_preamble_3_0.fragment}}
+{{include ../../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform layout="rl-tb" name="subform1">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <field h="3000pt" name="Field1">
+      <font typeface="Ahem" size="20pt"/>
+      <value>
+        <text>foo
+<!-- Intentionally formatted to trigger bug //--></text>
+      </value>
+    </field>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../../xfa_locale_6_0.fragment}}
+{{include ../../../xfa_postamble_7_0.fragment}}
+{{include ../../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected.pdf.1.png b/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected.pdf.1.png
new file mode 100644
index 0000000..7734ff7
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected_skia.pdf.1.png b/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected_skia.pdf.1.png
new file mode 100644
index 0000000..d4da28c
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected_skia.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/xfa_example_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/use_ahem/xfa_example_expected_skia.pdf.0.png
new file mode 100644
index 0000000..5a3eb9f
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/xfa_example_expected_skia.pdf.0.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
new file mode 100644
index 0000000..00bad26
--- /dev/null
+++ 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_bmp_image.in b/testing/resources/pixel/xfa_specific/xfa_bmp_image.in
index 9b444f1..dfd9026 100644
--- a/testing/resources/pixel/xfa_specific/xfa_bmp_image.in
+++ b/testing/resources/pixel/xfa_specific/xfa_bmp_image.in
@@ -18,7 +18,7 @@
     <subform w="576pt" h="756pt" name="Page1">
       <field name="ImageField1" w="250pt" h="250pt">
         <value>
-          <image contentType="image/bmp">Qk0qHgAAAAAAAHoAAABsAAAAMgAAADIAAAABABgAAAAAALAdAAATCwAAEwsAAAAAAAAAAAAAQkdScwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA</image>
+          <image contentType="image/bmp">Qk1mCgAAAAAAAD4AAAAoAAAAMgAAADIAAAABAAgAAAAAACgKAAATCwAAEwsAAAIAAAACAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==</image>
         </value>
         <border>
           <edge thickness="0.254mm"/>
diff --git a/testing/resources/pixel/xfa_specific/xfa_bmp_image_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_bmp_image_expected_skia.pdf.0.png
new file mode 100644
index 0000000..2f77624
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_bmp_image_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_gif_image_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_gif_image_expected_skia.pdf.0.png
new file mode 100644
index 0000000..2f77624
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_gif_image_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_jpg_image_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_jpg_image_expected_skia.pdf.0.png
new file mode 100644
index 0000000..70dcd8e
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_jpg_image_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.0.png
index 6855361..4bfe7c8 100644
--- a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.1.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.1.png
index 1f5c1bb..44aa0b1 100644
--- a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.1.png
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_mac.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_mac.pdf.0.png
new file mode 100644
index 0000000..4912da0
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_mac.pdf.1.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_mac.pdf.1.png
new file mode 100644
index 0000000..427b5b3
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_mac.pdf.1.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
new file mode 100644
index 0000000..ffc1b80
--- /dev/null
+++ 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
new file mode 100644
index 0000000..13174e5
--- /dev/null
+++ 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
new file mode 100644
index 0000000..52e6d96
--- /dev/null
+++ 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
new file mode 100644
index 0000000..e7fb0f3
--- /dev/null
+++ 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
new file mode 100644
index 0000000..a974ba2
--- /dev/null
+++ 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
new file mode 100644
index 0000000..605ed4c
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_win.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_win.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_win.pdf.0.png
new file mode 100644
index 0000000..ffa36fe
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_win.pdf.1.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_win.pdf.1.png
new file mode 100644
index 0000000..2448322
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_win.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_png_image_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_png_image_expected_skia.pdf.0.png
new file mode 100644
index 0000000..2f77624
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_png_image_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_rectangle_node_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_rectangle_node_expected_skia.pdf.0.png
new file mode 100644
index 0000000..99cee53
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_rectangle_node_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image.in b/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image.in
deleted file mode 100644
index 797f84c..0000000
--- a/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image.in
+++ /dev/null
@@ -1,39 +0,0 @@
-{{header}}
-{{include ../../xfa_catalog_1_0.fragment}}
-{{include ../../xfa_object_2_0.fragment}}
-{{include ../../xfa_preamble_3_0.fragment}}
-{{include ../../xfa_config_4_0.fragment}}
-{{object 5 0}} <<
-  {{streamlen}}
->>
-stream
-<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
-  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
-    <pageSet>
-      <pageArea name="Page1" id="Page1">
-        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
-        <medium stock="default" short="612pt" long="792pt"/>
-      </pageArea>
-    </pageSet>
-    <subform w="576pt" h="756pt" name="Page1">
-      <field name="ImageField1" w="250pt" h="250pt">
-        <value>
-          <image contentType="image/tiff">SUkqAAQDAAB4nO3SMQEAMAyAsPo33brYDhIFHOxAxf4OgGfcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ2OAxGIP8F4nO3SMQEAMAyAsPo33brYDhIFHOxAxf4OgGfcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ2OAxGIP8F4nO3SMQEAMAyAsPo33brYDhIFHOxAxf4OgGfcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ2OAxGIP8F4nO3SMQEAMAyAsPo33brYDhIFHOxAxf4OgGfcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjoO69g5xxIA/gAEAAEAAAAAAAAAAAEDAAEAAAD6AAAAAQEDAAEAAAD6AAAAAgEDAAMAAADyAwAAAwEDAAEAAAAIAAAABgEDAAEAAAACAAAADQECAEIAAAAYBAAADgECABIAAABaBAAAEQEEAAQAAAAIBAAAEgEDAAEAAAABAAAAFQEDAAEAAAADAAAAFgEDAAEAAABAAAAAFwEEAAQAAAD4AwAAGgEFAAEAAADiAwAAGwEFAAEAAADqAwAAHAEDAAEAAAABAAAAKAEDAAEAAAACAAAAPQEDAAEAAAACAAAAAAAAAEgAAAABAAAASAAAAAEAAAAIAAgACADDAAAAwwAAAMMAAACzAAAACAAAAMsAAACOAQAAUQIAAC91c3IvbG9jYWwvZ29vZ2xlL2hvbWUvcmhhcnJpc29uL1BpY3R1cmVzL1JlZF9TcXVhcmVfZGVmbGF0ZS50aWZmAENyZWF0ZWQgd2l0aCBHSU1QAA==</image>
-        </value>
-        <border>
-          <edge thickness="0.254mm"/>
-          <corner thickness="0.254mm"/>
-        </border>
-      </field>
-    </subform>
-  </subform>
-</template>
-endstream
-endobj
-{{include ../../xfa_locale_6_0.fragment}}
-{{include ../../xfa_postamble_7_0.fragment}}
-{{include ../../xfa_pages_8_0.fragment}}
-{{xref}}
-{{trailer}}
-{{startxref}}
-%%EOF
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image_expected.pdf.0.png
deleted file mode 100644
index 5204fd7..0000000
--- a/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image_expected.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_image_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_tiff_image_expected_skia.pdf.0.png
new file mode 100644
index 0000000..2f77624
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_tiff_image_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_lzw_image_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_tiff_lzw_image_expected_skia.pdf.0.png
new file mode 100644
index 0000000..2f77624
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_tiff_lzw_image_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_packbits_image_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_tiff_packbits_image_expected_skia.pdf.0.png
new file mode 100644
index 0000000..2f77624
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_tiff_packbits_image_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/polygon_annot.in b/testing/resources/polygon_annot.in
new file mode 100644
index 0000000..069ddc1
--- /dev/null
+++ b/testing/resources/polygon_annot.in
@@ -0,0 +1,48 @@
+{{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
+  /MediaBox [0 0 612 792]
+  /Annots [
+    4 0 R 5 0 R
+  ]
+  /Tabs /R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Polygon
+  /NM (Polygon-1)
+  /F 4
+  /Vertices [159 296 350 411 472 243.42]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Annot
+  /Subtype /Polygon
+  /NM (Polygon-2)
+  /F 4
+  /Vertices [259 396 450 511 572 343 42]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/polygon_annot.pdf b/testing/resources/polygon_annot.pdf
new file mode 100644
index 0000000..8efaa27
--- /dev/null
+++ b/testing/resources/polygon_annot.pdf
@@ -0,0 +1,60 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /Annots [
+    4 0 R 5 0 R
+  ]
+  /Tabs /R
+>>
+endobj
+4 0 obj <<
+  /Type /Annot
+  /Subtype /Polygon
+  /NM (Polygon-1)
+  /F 4
+  /Vertices [159 296 350 411 472 243.42]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+5 0 obj <<
+  /Type /Annot
+  /Subtype /Polygon
+  /NM (Polygon-2)
+  /F 4
+  /Vertices [259 396 450 511 572 343 42]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000251 00000 n 
+0000000429 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+607
+%%EOF
diff --git a/testing/resources/rectangles.in b/testing/resources/rectangles.in
index 49932ff..7334516 100644
--- a/testing/resources/rectangles.in
+++ b/testing/resources/rectangles.in
@@ -6,9 +6,9 @@
 endobj
 {{object 2 0}} <<
   /Type /Pages
-  /MediaBox [ 0 0 200 300 ]
+  /MediaBox [0 0 200 300]
   /Count 1
-  /Kids [ 3 0 R ]
+  /Kids [3 0 R]
 >>
 endobj
 {{object 3 0}} <<
@@ -18,6 +18,7 @@
 >>
 endobj
 {{object 4 0}} <<
+  {{streamlen}}
 >>
 stream
 q
diff --git a/testing/resources/rectangles.pdf b/testing/resources/rectangles.pdf
index 7bad251..6911d22 100644
--- a/testing/resources/rectangles.pdf
+++ b/testing/resources/rectangles.pdf
@@ -7,9 +7,9 @@
 endobj
 2 0 obj <<
   /Type /Pages
-  /MediaBox [ 0 0 200 300 ]
+  /MediaBox [0 0 200 300]
   /Count 1
-  /Kids [ 3 0 R ]
+  /Kids [3 0 R]
 >>
 endobj
 3 0 obj <<
@@ -19,6 +19,7 @@
 >>
 endobj
 4 0 obj <<
+  /Length 188
 >>
 stream
 q
@@ -42,9 +43,12 @@
 0000000000 65535 f 
 0000000015 00000 n 
 0000000068 00000 n 
-0000000161 00000 n 
-0000000230 00000 n 
-trailer<< /Root 1 0 R /Size 5 >>
+0000000157 00000 n 
+0000000226 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 5
+>>
 startxref
-456
+466
 %%EOF
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/redact_annot.in b/testing/resources/redact_annot.in
new file mode 100644
index 0000000..99b7b4e
--- /dev/null
+++ b/testing/resources/redact_annot.in
@@ -0,0 +1,44 @@
+{{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]
+  /Annots [
+    5 0 R
+  ]
+  /Tabs /R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Annot
+  /Subtype /Redact
+  /NM (Redact-1)
+  /F 4
+  /QuadPoints [293 542 349 542 293 530 349 530]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/redact_annot.pdf b/testing/resources/redact_annot.pdf
new file mode 100644
index 0000000..76c2607
--- /dev/null
+++ b/testing/resources/redact_annot.pdf
@@ -0,0 +1,56 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+  /Annots [
+    5 0 R
+  ]
+  /Tabs /R
+>>
+endobj
+4 0 obj <<
+  /Length 0
+>>
+stream
+endstream
+endobj
+5 0 obj <<
+  /Type /Annot
+  /Subtype /Redact
+  /NM (Redact-1)
+  /F 4
+  /QuadPoints [293 542 349 542 293 530 349 530]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000263 00000 n 
+0000000313 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+496
+%%EOF
diff --git a/testing/resources/rotated_image.in b/testing/resources/rotated_image.in
new file mode 100644
index 0000000..cf82fe8
--- /dev/null
+++ b/testing/resources/rotated_image.in
@@ -0,0 +1,52 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 200 200]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /XObject <<
+      /Img 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+30 -30 40 40 100 100 cm
+/Img Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/rotated_image.pdf b/testing/resources/rotated_image.pdf
new file mode 100644
index 0000000..5bb1836
--- /dev/null
+++ b/testing/resources/rotated_image.pdf
@@ -0,0 +1,64 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [0 0 200 200]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /XObject <<
+      /Img 5 0 R
+    >>
+  >>
+>>
+endobj
+4 0 obj <<
+  /Length 36
+>>
+stream
+q
+30 -30 40 40 100 100 cm
+/Img Do
+Q
+endstream
+endobj
+5 0 obj <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Length 71
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000157 00000 n 
+0000000287 00000 n 
+0000000374 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+644
+%%EOF
diff --git a/testing/resources/signature_no_sub_filter.in b/testing/resources/signature_no_sub_filter.in
new file mode 100644
index 0000000..5c1d1d8
--- /dev/null
+++ b/testing/resources/signature_no_sub_filter.in
@@ -0,0 +1,100 @@
+{{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}}
+{{startxref}}
+%%EOF
+
+%% First incremental update adds an initial signature and update objects to
+%% refer to it.
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [7 0 R]
+    /SigFlags 3
+  >>
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Annots [7 0 R]
+>>
+endobj
+%% ByteRange is a pairs of integers (starting byte offset, length in bytes)
+{{object 5 0}} <<
+  /Type /Sig
+  /Filter /Adobe.PPKMS
+  % Intentionally no /SubFilter
+  /ByteRange [0 10 30 10]
+  /Contents <308006092A864886F70D010702A0803080020101>
+  /M (D:20200624093114+02'00')
+>>
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 0 0]
+  /Length 0
+>>
+stream
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Sig
+  /F 132
+  /Rect [0 0 0 0]
+  /P 3 0 R
+  /T (Signature1)
+  /V 5 0 R
+  /DV 5 0 R
+  /AP <<
+    /N 6 0 R
+  >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/signature_no_sub_filter.pdf b/testing/resources/signature_no_sub_filter.pdf
new file mode 100644
index 0000000..ed305ef
--- /dev/null
+++ b/testing/resources/signature_no_sub_filter.pdf
@@ -0,0 +1,124 @@
+%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
+>>
+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 
+0000000226 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 5
+>>
+startxref
+466
+%%EOF
+
+%% First incremental update adds an initial signature and update objects to
+%% refer to it.
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [7 0 R]
+    /SigFlags 3
+  >>
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Annots [7 0 R]
+>>
+endobj
+%% ByteRange is a pairs of integers (starting byte offset, length in bytes)
+5 0 obj <<
+  /Type /Sig
+  /Filter /Adobe.PPKMS
+  % Intentionally no /SubFilter
+  /ByteRange [0 10 30 10]
+  /Contents <308006092A864886F70D010702A0803080020101>
+  /M (D:20200624093114+02'00')
+>>
+endobj
+6 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 0 0]
+  /Length 0
+>>
+stream
+endstream
+endobj
+7 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Sig
+  /F 132
+  /Rect [0 0 0 0]
+  /P 3 0 R
+  /T (Signature1)
+  /V 5 0 R
+  /DV 5 0 R
+  /AP <<
+    /N 6 0 R
+  >>
+>>
+endobj
+xref
+0 8
+0000000000 65535 f 
+0000000726 00000 n 
+0000000068 00000 n 
+0000000835 00000 n 
+0000000226 00000 n 
+0000000998 00000 n 
+0000001199 00000 n 
+0000001301 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 8
+>>
+startxref
+1473
+%%EOF
diff --git a/testing/resources/signature_reason.in b/testing/resources/signature_reason.in
new file mode 100644
index 0000000..2c5d0f9
--- /dev/null
+++ b/testing/resources/signature_reason.in
@@ -0,0 +1,101 @@
+{{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}}
+{{startxref}}
+%%EOF
+
+%% First incremental update adds an initial signature and update objects to
+%% refer to it.
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [7 0 R]
+    /SigFlags 3
+  >>
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Annots [7 0 R]
+>>
+endobj
+%% ByteRange is a pairs of integers (starting byte offset, length in bytes)
+{{object 5 0}} <<
+  /Type /Sig
+  /Filter /Adobe.PPKMS
+  /SubFilter /ETSI.CAdES.detached
+  /ByteRange [0 10 30 10]
+  /Contents <308006092A864886F70D010702A0803080020101>
+  /M (D:20200624093114+02'00')
+  /Reason <FEFF007400650073007400200072006500610073006F006E>
+>>
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 0 0]
+  /Length 0
+>>
+stream
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Sig
+  /F 132
+  /Rect [0 0 0 0]
+  /P 3 0 R
+  /T (Signature1)
+  /V 5 0 R
+  /DV 5 0 R
+  /AP <<
+    /N 6 0 R
+  >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/signature_reason.pdf b/testing/resources/signature_reason.pdf
new file mode 100644
index 0000000..3ad0205
--- /dev/null
+++ b/testing/resources/signature_reason.pdf
@@ -0,0 +1,125 @@
+%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
+>>
+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 
+0000000226 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 5
+>>
+startxref
+466
+%%EOF
+
+%% First incremental update adds an initial signature and update objects to
+%% refer to it.
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [7 0 R]
+    /SigFlags 3
+  >>
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Annots [7 0 R]
+>>
+endobj
+%% ByteRange is a pairs of integers (starting byte offset, length in bytes)
+5 0 obj <<
+  /Type /Sig
+  /Filter /Adobe.PPKMS
+  /SubFilter /ETSI.CAdES.detached
+  /ByteRange [0 10 30 10]
+  /Contents <308006092A864886F70D010702A0803080020101>
+  /M (D:20200624093114+02'00')
+  /Reason <FEFF007400650073007400200072006500610073006F006E>
+>>
+endobj
+6 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 0 0]
+  /Length 0
+>>
+stream
+endstream
+endobj
+7 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Sig
+  /F 132
+  /Rect [0 0 0 0]
+  /P 3 0 R
+  /T (Signature1)
+  /V 5 0 R
+  /DV 5 0 R
+  /AP <<
+    /N 6 0 R
+  >>
+>>
+endobj
+xref
+0 8
+0000000000 65535 f 
+0000000726 00000 n 
+0000000068 00000 n 
+0000000835 00000 n 
+0000000226 00000 n 
+0000000998 00000 n 
+0000001262 00000 n 
+0000001364 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 8
+>>
+startxref
+1536
+%%EOF
diff --git a/testing/resources/simple_xfa.in b/testing/resources/simple_xfa.in
index b20c746..d3c2d00 100644
--- a/testing/resources/simple_xfa.in
+++ b/testing/resources/simple_xfa.in
@@ -12,6 +12,7 @@
     <pageSet>
       <pageArea name="Page1" id="Page1">
         <contentArea x="0.25in" y="0.25in" w="8in" h="10.5in" />
+        <medium long="11in" short="8.5in" stock="letter"/>
       </pageArea>
     </pageSet>
     <field name="TextField1" y="31.75mm" x="44.45mm" w="114.291mm" h="12.7mm">
diff --git a/testing/resources/simple_xfa.pdf b/testing/resources/simple_xfa.pdf
index 89f9aea..8ff5b12 100644
--- a/testing/resources/simple_xfa.pdf
+++ b/testing/resources/simple_xfa.pdf
@@ -72,7 +72,7 @@
 endstream
 endobj
 5 0 obj <<
-  /Length 482
+  /Length 541
 >>
 stream
 <template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
@@ -80,6 +80,7 @@
     <pageSet>
       <pageArea name="Page1" id="Page1">
         <contentArea x="0.25in" y="0.25in" w="8in" h="10.5in" />
+        <medium long="11in" short="8.5in" stock="letter"/>
       </pageArea>
     </pageSet>
     <field name="TextField1" y="31.75mm" x="44.45mm" w="114.291mm" h="12.7mm">
@@ -215,7 +216,7 @@
 endobj
 9 0 obj <<
   /Type /Page
-  /Parent 2 0 R
+  /Parent 8 0 R
   /MediaBox [0 0 612 792]
 >>
 endobj
@@ -227,14 +228,14 @@
 0000000358 00000 n 
 0000000534 00000 n 
 0000001228 00000 n 
-0000001762 00000 n 
-0000005270 00000 n 
-0000005332 00000 n 
-0000005395 00000 n 
+0000001821 00000 n 
+0000005329 00000 n 
+0000005391 00000 n 
+0000005454 00000 n 
 trailer <<
   /Root 1 0 R
   /Size 10
 >>
 startxref
-5472
+5531
 %%EOF
diff --git a/testing/resources/split_streams.in b/testing/resources/split_streams.in
index b134769..729c67a 100644
--- a/testing/resources/split_streams.in
+++ b/testing/resources/split_streams.in
@@ -117,7 +117,7 @@
 {{xref}}
 trailer <<
   /Root 1 0 R
-  /Size 15
+  {{trailersize}}
   /ID [<f341ae654a77acd5065a7645e596e6e6><bc37298a3f87f479229bce997ca791f7>]
 >>
 {{startxref}}
diff --git a/testing/resources/tagged_actual_text.in b/testing/resources/tagged_actual_text.in
new file mode 100644
index 0000000..9bb917b
--- /dev/null
+++ b/testing/resources/tagged_actual_text.in
@@ -0,0 +1,162 @@
+{{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 [11 0 R]
+  /RoleMap <<
+    /Document /Document
+    /Standard /P
+    /Figure /Figure
+  >>
+>>
+endobj
+{{object 9 0}} <<
+  /Nums [0 [10 0 R]]
+>>
+endobj
+{{object 10 0}} <<
+  /Type /StructElem
+  /S /Figure
+  /A 13 0 R
+  /K [0]
+  /P 12 0 R
+  /ActualText <feff00410063007400750061006c00200054006500780074>
+  /Pg 3 0 R
+>>
+endobj
+{{object 11 0}} <<
+  /Type /StructElem
+  /S /Document
+  /K [12 0 R]
+  /P 8 0 R
+  /T (TitleText)
+  /Pg 3 0 R
+>>
+endobj
+{{object 12 0}} <<
+  /Type /StructElem
+  /S /Standard
+  /A 14 0 R
+  /K [10 0 R]
+  /P 11 0 R
+  /T <feff00730079006d0062006f006c003a0020003100300030006b>
+  /Pg 3 0 R
+>>
+endobj
+{{object 13 0}} <<
+  /O /Layout
+  /Placement /Block
+  /BBox [281.1 685.3 331.1 735.3]
+  /Width 99.9
+  /Height 99.9
+>>
+endobj
+{{object 14 0}} <<
+  /O /Layout
+  /Placement /Block
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/tagged_actual_text.pdf b/testing/resources/tagged_actual_text.pdf
new file mode 100644
index 0000000..634e333
--- /dev/null
+++ b/testing/resources/tagged_actual_text.pdf
@@ -0,0 +1,183 @@
+%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 [11 0 R]
+  /RoleMap <<
+    /Document /Document
+    /Standard /P
+    /Figure /Figure
+  >>
+>>
+endobj
+9 0 obj <<
+  /Nums [0 [10 0 R]]
+>>
+endobj
+10 0 obj <<
+  /Type /StructElem
+  /S /Figure
+  /A 13 0 R
+  /K [0]
+  /P 12 0 R
+  /ActualText <feff00410063007400750061006c00200054006500780074>
+  /Pg 3 0 R
+>>
+endobj
+11 0 obj <<
+  /Type /StructElem
+  /S /Document
+  /K [12 0 R]
+  /P 8 0 R
+  /T (TitleText)
+  /Pg 3 0 R
+>>
+endobj
+12 0 obj <<
+  /Type /StructElem
+  /S /Standard
+  /A 14 0 R
+  /K [10 0 R]
+  /P 11 0 R
+  /T <feff00730079006d0062006f006c003a0020003100300030006b>
+  /Pg 3 0 R
+>>
+endobj
+13 0 obj <<
+  /O /Layout
+  /Placement /Block
+  /BBox [281.1 685.3 331.1 735.3]
+  /Width 99.9
+  /Height 99.9
+>>
+endobj
+14 0 obj <<
+  /O /Layout
+  /Placement /Block
+>>
+endobj
+xref
+0 15
+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 
+0000001454 00000 n 
+0000001619 00000 n 
+0000001730 00000 n 
+0000001897 00000 n 
+0000002015 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 15
+>>
+startxref
+2070
+%%EOF
diff --git a/testing/resources/tagged_marked_content.in b/testing/resources/tagged_marked_content.in
new file mode 100644
index 0000000..a8ea64b
--- /dev/null
+++ b/testing/resources/tagged_marked_content.in
@@ -0,0 +1,140 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /StructTreeRoot 7 0 R
+  /MarkInfo <<
+    /Type /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
+  /StructParents 0
+  /Annots [4 0 R]
+  /Contents 5 0 R
+  /MediaBox [0 0 612 792]
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /F4 6 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /Border [0 0 0]
+  /Dest /top
+  /F 4
+  /Rect [20 46 68 61]
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+BT
+/P <</MCID 0 >>BDC
+/F4 16 Tf
+20 650 Td
+(Top Left) Tj
+EMC
+ET
+BT
+/P <</MCID 1 >>BDC
+/F4 16 Tf
+20 50 Td
+(Bottom Left) Tj
+EMC
+ET
+BT
+/P <</MCID 2 >>BDC
+/F4 16 Tf
+400 50 Td
+(Bottom Right) Tj
+EMC
+ET
+BT
+/P <</MCID 3 >>BDC
+/F4 16 Tf
+400 650 Td
+(Top Right) Tj
+EMC
+ET
+Q
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 7 0}} <<
+  /Type /StructTreeRoot
+  /K [9 0 R 10 0 R 11 0 R 12 0 R]
+  /ParentTree 8 0 R
+  /ParentTreeNextKey 1
+>>
+endobj
+{{object 8 0}} <<
+  /Type /ParentTree
+  /Nums [0 [9 0 R 10 0 R 11 0 R 12 0 R]]
+>>
+endobj
+{{object 9 0}} <<
+  /Type /StructElem
+  /S /NonStruct
+  /P 7 0 R
+  /K 0
+  /ID /3
+>>
+endobj
+{{object 10 0}} <<
+  /Type /StructElem
+  /S /NonStruct
+  /P 7 0 R
+  /K <<
+      /Type /MCR
+      /MCID 1
+      /Pg 3 0 R
+     >>
+  /ID /4
+>>
+endobj
+{{object 11 0}} <<
+  /Type /StructElem
+  /S /NonStruct
+  /P 7 0 R
+  /K [
+      <<
+       /Type /MCR
+       /MCID 2
+       /Pg 3 0 R
+      >>
+      3]
+  /ID /5
+>>
+endobj
+{{object 12 0}} <<
+  /Type /StructElem
+  /S /NonStruct
+  /P 7 0 R
+  /ID /6
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/tagged_marked_content.pdf b/testing/resources/tagged_marked_content.pdf
new file mode 100644
index 0000000..92f731d
--- /dev/null
+++ b/testing/resources/tagged_marked_content.pdf
@@ -0,0 +1,159 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /StructTreeRoot 7 0 R
+  /MarkInfo <<
+    /Type /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
+  /StructParents 0
+  /Annots [4 0 R]
+  /Contents 5 0 R
+  /MediaBox [0 0 612 792]
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /F4 6 0 R
+    >>
+  >>
+>>
+endobj
+4 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /Border [0 0 0]
+  /Dest /top
+  /F 4
+  /Rect [20 46 68 61]
+>>
+endobj
+5 0 obj <<
+  /Length 264
+>>
+stream
+q
+BT
+/P <</MCID 0 >>BDC
+/F4 16 Tf
+20 650 Td
+(Top Left) Tj
+EMC
+ET
+BT
+/P <</MCID 1 >>BDC
+/F4 16 Tf
+20 50 Td
+(Bottom Left) Tj
+EMC
+ET
+BT
+/P <</MCID 2 >>BDC
+/F4 16 Tf
+400 50 Td
+(Bottom Right) Tj
+EMC
+ET
+BT
+/P <</MCID 3 >>BDC
+/F4 16 Tf
+400 650 Td
+(Top Right) Tj
+EMC
+ET
+Q
+endstream
+endobj
+6 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+7 0 obj <<
+  /Type /StructTreeRoot
+  /K [9 0 R 10 0 R 11 0 R 12 0 R]
+  /ParentTree 8 0 R
+  /ParentTreeNextKey 1
+>>
+endobj
+8 0 obj <<
+  /Type /ParentTree
+  /Nums [0 [9 0 R 10 0 R 11 0 R 12 0 R]]
+>>
+endobj
+9 0 obj <<
+  /Type /StructElem
+  /S /NonStruct
+  /P 7 0 R
+  /K 0
+  /ID /3
+>>
+endobj
+10 0 obj <<
+  /Type /StructElem
+  /S /NonStruct
+  /P 7 0 R
+  /K <<
+      /Type /MCR
+      /MCID 1
+      /Pg 3 0 R
+     >>
+  /ID /4
+>>
+endobj
+11 0 obj <<
+  /Type /StructElem
+  /S /NonStruct
+  /P 7 0 R
+  /K [
+      <<
+       /Type /MCR
+       /MCID 2
+       /Pg 3 0 R
+      >>
+      3]
+  /ID /5
+>>
+endobj
+12 0 obj <<
+  /Type /StructElem
+  /S /NonStruct
+  /P 7 0 R
+  /ID /6
+>>
+endobj
+xref
+0 13
+0000000000 65535 f 
+0000000015 00000 n 
+0000000149 00000 n 
+0000000212 00000 n 
+0000000427 00000 n 
+0000000540 00000 n 
+0000000856 00000 n 
+0000000934 00000 n 
+0000001056 00000 n 
+0000001138 00000 n 
+0000001222 00000 n 
+0000001363 00000 n 
+0000001525 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 13
+>>
+startxref
+1603
+%%EOF
diff --git a/testing/resources/tagged_mcr_objr.in b/testing/resources/tagged_mcr_objr.in
new file mode 100644
index 0000000..3394633
--- /dev/null
+++ b/testing/resources/tagged_mcr_objr.in
@@ -0,0 +1,160 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /StructTreeRoot 7 0 R
+  /MarkInfo <<
+    /Type /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
+  /StructParents 0
+  /Annots [4 0 R]
+  /Contents 5 0 R
+  /MediaBox [0 0 612 792]
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /F4 6 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /Border [0 0 0]
+  /Dest /top
+  /F 4
+  /Rect [20 46 68 61]
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+BT
+/P <</MCID 0 >>BDC
+/F4 16 Tf
+20 650 Td
+(Hello, world!) Tj
+EMC
+ET
+BT
+/P <</MCID 1 >>BDC
+/F4 16 Tf
+20 50 Td
+(Link to top) Tj
+EMC
+ET
+Q
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 7 0}} <<
+  /Type /StructTreeRoot
+  /K 8 0 R
+  /ParentTree 9 0 R
+  /ParentTreeNextKey 1
+>>
+endobj
+{{object 8 0}} <<
+  /Type /StructElem
+  /S /Document
+  /P 7 0 R
+  /K [10 0 R 11 0 R]
+  /ID /2
+  /Lang (en-US)
+>>
+endobj
+{{object 9 0}} <<
+  /Type /ParentTree
+  /Nums [0 [13 0 R 15 0 R]]
+>>
+endobj
+{{object 10 0}} <<
+  /Type /StructElem
+  /S /NonStruct
+  /P 8 0 R
+  /K [12 0 R]
+  /ID /6
+>>
+endobj
+{{object 11 0}} <<
+  /Type /StructElem
+  /S /P
+  /P 8 0 R
+  /K [14 0 R]
+  /ID /4
+>>
+endobj
+{{object 12 0}} <<
+  /Type /StructElem
+  /S /P
+  /P 10 0 R
+  /K [13 0 R]
+  /ID /3
+>>
+endobj
+{{object 13 0}} <<
+  /Type /StructElem
+  /S /NonStruct
+  /P 12 0 R
+  /K [
+    <<
+      /Type /MCR
+      /MCID 0
+      /Pg 3 0 R
+    >>
+  ]
+  /ID /7
+>>
+endobj
+{{object 14 0}} <<
+  /Type /StructElem
+  /S /NonStruct
+  /P 11 0 R
+  /K [
+    15 0 R
+    <<
+      /Type /OBJR
+      /Obj 4 0 R
+    >>
+  ]
+  /ID /9
+>>
+endobj
+{{object 15 0}} <<
+  /Type /StructElem
+  /S /NonStruct
+  /P 14 0 R
+  /K [
+    <<
+      /Type /MCR
+      /Pg 3 0 R
+      /MCID 1
+    >>
+  ]
+  /ID /10
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/tagged_mcr_objr.pdf b/testing/resources/tagged_mcr_objr.pdf
new file mode 100644
index 0000000..a86faa7
--- /dev/null
+++ b/testing/resources/tagged_mcr_objr.pdf
@@ -0,0 +1,182 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /StructTreeRoot 7 0 R
+  /MarkInfo <<
+    /Type /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
+  /StructParents 0
+  /Annots [4 0 R]
+  /Contents 5 0 R
+  /MediaBox [0 0 612 792]
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /F4 6 0 R
+    >>
+  >>
+>>
+endobj
+4 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /Border [0 0 0]
+  /Dest /top
+  /F 4
+  /Rect [20 46 68 61]
+>>
+endobj
+5 0 obj <<
+  /Length 137
+>>
+stream
+q
+BT
+/P <</MCID 0 >>BDC
+/F4 16 Tf
+20 650 Td
+(Hello, world!) Tj
+EMC
+ET
+BT
+/P <</MCID 1 >>BDC
+/F4 16 Tf
+20 50 Td
+(Link to top) Tj
+EMC
+ET
+Q
+endstream
+endobj
+6 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+7 0 obj <<
+  /Type /StructTreeRoot
+  /K 8 0 R
+  /ParentTree 9 0 R
+  /ParentTreeNextKey 1
+>>
+endobj
+8 0 obj <<
+  /Type /StructElem
+  /S /Document
+  /P 7 0 R
+  /K [10 0 R 11 0 R]
+  /ID /2
+  /Lang (en-US)
+>>
+endobj
+9 0 obj <<
+  /Type /ParentTree
+  /Nums [0 [13 0 R 15 0 R]]
+>>
+endobj
+10 0 obj <<
+  /Type /StructElem
+  /S /NonStruct
+  /P 8 0 R
+  /K [12 0 R]
+  /ID /6
+>>
+endobj
+11 0 obj <<
+  /Type /StructElem
+  /S /P
+  /P 8 0 R
+  /K [14 0 R]
+  /ID /4
+>>
+endobj
+12 0 obj <<
+  /Type /StructElem
+  /S /P
+  /P 10 0 R
+  /K [13 0 R]
+  /ID /3
+>>
+endobj
+13 0 obj <<
+  /Type /StructElem
+  /S /NonStruct
+  /P 12 0 R
+  /K [
+    <<
+      /Type /MCR
+      /MCID 0
+      /Pg 3 0 R
+    >>
+  ]
+  /ID /7
+>>
+endobj
+14 0 obj <<
+  /Type /StructElem
+  /S /NonStruct
+  /P 11 0 R
+  /K [
+    15 0 R
+    <<
+      /Type /OBJR
+      /Obj 4 0 R
+    >>
+  ]
+  /ID /9
+>>
+endobj
+15 0 obj <<
+  /Type /StructElem
+  /S /NonStruct
+  /P 14 0 R
+  /K [
+    <<
+      /Type /MCR
+      /Pg 3 0 R
+      /MCID 1
+    >>
+  ]
+  /ID /10
+>>
+endobj
+xref
+0 16
+0000000000 65535 f 
+0000000015 00000 n 
+0000000149 00000 n 
+0000000212 00000 n 
+0000000427 00000 n 
+0000000540 00000 n 
+0000000729 00000 n 
+0000000807 00000 n 
+0000000906 00000 n 
+0000001019 00000 n 
+0000001088 00000 n 
+0000001180 00000 n 
+0000001264 00000 n 
+0000001349 00000 n 
+0000001500 00000 n 
+0000001650 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 16
+>>
+startxref
+1802
+%%EOF
diff --git a/testing/resources/tagged_nested.in b/testing/resources/tagged_nested.in
new file mode 100644
index 0000000..b7d2022
--- /dev/null
+++ b/testing/resources/tagged_nested.in
@@ -0,0 +1,346 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /StructTreeRoot 5 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]
+  /StructParents 0
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+/Para<</MCID 0>>
+BMC
+0 0 Td
+/F1 18 Tf
+(Sample Text) Tj
+EMC
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /StructTreeRoot
+  /ParentTree 6 0 R
+  /K [8 0 R]
+  /RoleMap <<
+    /Document /Document
+    /Para /Para
+  >>
+>>
+endobj
+{{object 6 0}} <<
+  /Nums [0 [7 0 R]]
+>>
+endobj
+{{object 7 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [0]
+  /P 42 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 8 0}} <<
+  /Type /StructElem
+  /S /Document
+  /K [9 0 R]
+  /P 5 0 R
+  /T (TitleText)
+  /Pg 3 0 R
+>>
+endobj
+{{object 9 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [10 0 R]
+  /P 8 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 10 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [11 0 R]
+  /P 9 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 11 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [12 0 R]
+  /P 10 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 12 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [13 0 R]
+  /P 11 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 13 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [14 0 R]
+  /P 12 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 14 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [15 0 R]
+  /P 13 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 15 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [16 0 R]
+  /P 14 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 16 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [17 0 R]
+  /P 15 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 17 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [18 0 R]
+  /P 16 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 18 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [19 0 R]
+  /P 17 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 19 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [20 0 R]
+  /P 18 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 20 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [21 0 R]
+  /P 19 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 21 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [22 0 R]
+  /P 20 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 22 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [23 0 R]
+  /P 21 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 23 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [24 0 R]
+  /P 22 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 24 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [25 0 R]
+  /P 23 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 25 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [26 0 R]
+  /P 24 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 26 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [27 0 R]
+  /P 25 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 27 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [28 0 R]
+  /P 26 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 28 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [29 0 R]
+  /P 27 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 29 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [30 0 R]
+  /P 28 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 30 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [31 0 R]
+  /P 29 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 31 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [32 0 R]
+  /P 30 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 32 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [33 0 R]
+  /P 31 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 33 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [34 0 R]
+  /P 32 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 34 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [35 0 R]
+  /P 33 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 35 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [36 0 R]
+  /P 34 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 36 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [37 0 R]
+  /P 35 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 37 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [38 0 R]
+  /P 36 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 38 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [39 0 R]
+  /P 37 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 39 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [40 0 R]
+  /P 38 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 40 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [41 0 R]
+  /P 39 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 41 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [42 0 R]
+  /P 40 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{object 42 0}} <<
+  /Type /StructElem
+  /S /Para
+  /K [7 0 R]
+  /P 41 0 R
+  /Pg 3 0 R
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/tagged_nested.pdf b/testing/resources/tagged_nested.pdf
new file mode 100644
index 0000000..8e09360
--- /dev/null
+++ b/testing/resources/tagged_nested.pdf
@@ -0,0 +1,395 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /StructTreeRoot 5 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]
+  /StructParents 0
+>>
+endobj
+4 0 obj <<
+  /Length 65
+>>
+stream
+BT
+/Para<</MCID 0>>
+BMC
+0 0 Td
+/F1 18 Tf
+(Sample Text) Tj
+EMC
+ET
+endstream
+endobj
+5 0 obj <<
+  /Type /StructTreeRoot
+  /ParentTree 6 0 R
+  /K [8 0 R]
+  /RoleMap <<
+    /Document /Document
+    /Para /Para
+  >>
+>>
+endobj
+6 0 obj <<
+  /Nums [0 [7 0 R]]
+>>
+endobj
+7 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [0]
+  /P 42 0 R
+  /Pg 3 0 R
+>>
+endobj
+8 0 obj <<
+  /Type /StructElem
+  /S /Document
+  /K [9 0 R]
+  /P 5 0 R
+  /T (TitleText)
+  /Pg 3 0 R
+>>
+endobj
+9 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [10 0 R]
+  /P 8 0 R
+  /Pg 3 0 R
+>>
+endobj
+10 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [11 0 R]
+  /P 9 0 R
+  /Pg 3 0 R
+>>
+endobj
+11 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [12 0 R]
+  /P 10 0 R
+  /Pg 3 0 R
+>>
+endobj
+12 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [13 0 R]
+  /P 11 0 R
+  /Pg 3 0 R
+>>
+endobj
+13 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [14 0 R]
+  /P 12 0 R
+  /Pg 3 0 R
+>>
+endobj
+14 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [15 0 R]
+  /P 13 0 R
+  /Pg 3 0 R
+>>
+endobj
+15 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [16 0 R]
+  /P 14 0 R
+  /Pg 3 0 R
+>>
+endobj
+16 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [17 0 R]
+  /P 15 0 R
+  /Pg 3 0 R
+>>
+endobj
+17 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [18 0 R]
+  /P 16 0 R
+  /Pg 3 0 R
+>>
+endobj
+18 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [19 0 R]
+  /P 17 0 R
+  /Pg 3 0 R
+>>
+endobj
+19 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [20 0 R]
+  /P 18 0 R
+  /Pg 3 0 R
+>>
+endobj
+20 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [21 0 R]
+  /P 19 0 R
+  /Pg 3 0 R
+>>
+endobj
+21 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [22 0 R]
+  /P 20 0 R
+  /Pg 3 0 R
+>>
+endobj
+22 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [23 0 R]
+  /P 21 0 R
+  /Pg 3 0 R
+>>
+endobj
+23 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [24 0 R]
+  /P 22 0 R
+  /Pg 3 0 R
+>>
+endobj
+24 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [25 0 R]
+  /P 23 0 R
+  /Pg 3 0 R
+>>
+endobj
+25 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [26 0 R]
+  /P 24 0 R
+  /Pg 3 0 R
+>>
+endobj
+26 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [27 0 R]
+  /P 25 0 R
+  /Pg 3 0 R
+>>
+endobj
+27 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [28 0 R]
+  /P 26 0 R
+  /Pg 3 0 R
+>>
+endobj
+28 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [29 0 R]
+  /P 27 0 R
+  /Pg 3 0 R
+>>
+endobj
+29 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [30 0 R]
+  /P 28 0 R
+  /Pg 3 0 R
+>>
+endobj
+30 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [31 0 R]
+  /P 29 0 R
+  /Pg 3 0 R
+>>
+endobj
+31 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [32 0 R]
+  /P 30 0 R
+  /Pg 3 0 R
+>>
+endobj
+32 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [33 0 R]
+  /P 31 0 R
+  /Pg 3 0 R
+>>
+endobj
+33 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [34 0 R]
+  /P 32 0 R
+  /Pg 3 0 R
+>>
+endobj
+34 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [35 0 R]
+  /P 33 0 R
+  /Pg 3 0 R
+>>
+endobj
+35 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [36 0 R]
+  /P 34 0 R
+  /Pg 3 0 R
+>>
+endobj
+36 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [37 0 R]
+  /P 35 0 R
+  /Pg 3 0 R
+>>
+endobj
+37 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [38 0 R]
+  /P 36 0 R
+  /Pg 3 0 R
+>>
+endobj
+38 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [39 0 R]
+  /P 37 0 R
+  /Pg 3 0 R
+>>
+endobj
+39 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [40 0 R]
+  /P 38 0 R
+  /Pg 3 0 R
+>>
+endobj
+40 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [41 0 R]
+  /P 39 0 R
+  /Pg 3 0 R
+>>
+endobj
+41 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [42 0 R]
+  /P 40 0 R
+  /Pg 3 0 R
+>>
+endobj
+42 0 obj <<
+  /Type /StructElem
+  /S /Para
+  /K [7 0 R]
+  /P 41 0 R
+  /Pg 3 0 R
+>>
+endobj
+xref
+0 43
+0000000000 65535 f 
+0000000015 00000 n 
+0000000145 00000 n 
+0000000208 00000 n 
+0000000322 00000 n 
+0000000438 00000 n 
+0000000575 00000 n 
+0000000616 00000 n 
+0000000701 00000 n 
+0000000810 00000 n 
+0000000899 00000 n 
+0000000989 00000 n 
+0000001080 00000 n 
+0000001171 00000 n 
+0000001262 00000 n 
+0000001353 00000 n 
+0000001444 00000 n 
+0000001535 00000 n 
+0000001626 00000 n 
+0000001717 00000 n 
+0000001808 00000 n 
+0000001899 00000 n 
+0000001990 00000 n 
+0000002081 00000 n 
+0000002172 00000 n 
+0000002263 00000 n 
+0000002354 00000 n 
+0000002445 00000 n 
+0000002536 00000 n 
+0000002627 00000 n 
+0000002718 00000 n 
+0000002809 00000 n 
+0000002900 00000 n 
+0000002991 00000 n 
+0000003082 00000 n 
+0000003173 00000 n 
+0000003264 00000 n 
+0000003355 00000 n 
+0000003446 00000 n 
+0000003537 00000 n 
+0000003628 00000 n 
+0000003719 00000 n 
+0000003810 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 43
+>>
+startxref
+3900
+%%EOF
diff --git a/testing/resources/tagged_table.in b/testing/resources/tagged_table.in
new file mode 100644
index 0000000..6e2ddc0
--- /dev/null
+++ b/testing/resources/tagged_table.in
@@ -0,0 +1,225 @@
+{{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 13 0 R]
+  /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 17 0 R]
+  /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 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}}
+%%EOF
diff --git a/testing/resources/tagged_table.pdf b/testing/resources/tagged_table.pdf
new file mode 100644
index 0000000..6e2bf9e
--- /dev/null
+++ b/testing/resources/tagged_table.pdf
@@ -0,0 +1,251 @@
+%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 13 0 R]
+  /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 17 0 R]
+  /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 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 20
+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 
+0000001826 00000 n 
+0000001931 00000 n 
+0000002074 00000 n 
+0000002276 00000 n 
+0000002366 00000 n 
+0000002513 00000 n 
+0000002615 00000 n 
+0000002715 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 20
+>>
+startxref
+2735
+%%EOF
diff --git a/testing/resources/tagged_table_bad_elem.in b/testing/resources/tagged_table_bad_elem.in
new file mode 100644
index 0000000..c502047
--- /dev/null
+++ b/testing/resources/tagged_table_bad_elem.in
@@ -0,0 +1,153 @@
+{{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]
+  ]
+>>
+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}} <<
+  % Deliberately missing /Type
+  /S /Table
+  /K [12 0 R]
+  /P 10 0 R
+  /Pg 3 0 R
+  /A [(bogus type)]
+  /ID (node12)
+  /Lang (hu)
+>>
+endobj
+{{object 12 0}} <<
+  % Deliberately bad /Type.
+  /Type /NotStructElem
+  /S /TR
+  /P 11 0 R
+  /Pg 3 0 R
+  /ID ()
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/tagged_table_bad_elem.pdf b/testing/resources/tagged_table_bad_elem.pdf
new file mode 100644
index 0000000..62beee0
--- /dev/null
+++ b/testing/resources/tagged_table_bad_elem.pdf
@@ -0,0 +1,172 @@
+%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]
+  ]
+>>
+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 <<
+  % Deliberately missing /Type
+  /S /Table
+  /K [12 0 R]
+  /P 10 0 R
+  /Pg 3 0 R
+  /A [(bogus type)]
+  /ID (node12)
+  /Lang (hu)
+>>
+endobj
+12 0 obj <<
+  % Deliberately bad /Type.
+  /Type /NotStructElem
+  /S /TR
+  /P 11 0 R
+  /Pg 3 0 R
+  /ID ()
+>>
+endobj
+xref
+0 13
+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 
+0000001480 00000 n 
+0000001607 00000 n 
+0000001758 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 13
+>>
+startxref
+1873
+%%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/resources/text_in_page_marked.in b/testing/resources/text_in_page_marked.in
index e338479..5978110 100644
--- a/testing/resources/text_in_page_marked.in
+++ b/testing/resources/text_in_page_marked.in
@@ -131,7 +131,7 @@
 {{xref}}
 trailer <<
   /Root 1 0 R
-  /Size 13
+  {{trailersize}}
   /ID [<f341ae654a77acd5065a7645e596e6e6><bc37298a3f87f479229bce997ca791f7>]
 >>
 {{startxref}}
diff --git a/testing/resources/text_in_page_marked_indirect.in b/testing/resources/text_in_page_marked_indirect.in
index 1f989f6..6004ad4 100644
--- a/testing/resources/text_in_page_marked_indirect.in
+++ b/testing/resources/text_in_page_marked_indirect.in
@@ -137,7 +137,7 @@
 {{xref}}
 trailer <<
   /Root 1 0 R
-  /Size 14
+  {{trailersize}}
   /ID [<f341ae654a77acd5065a7645e596e6e6><bc37298a3f87f479229bce997ca791f7>]
 >>
 {{startxref}}
diff --git a/testing/resources/trailer_end_trailing_space.in b/testing/resources/trailer_end_trailing_space.in
new file mode 100644
index 0000000..0233b75
--- /dev/null
+++ b/testing/resources/trailer_end_trailing_space.in
@@ -0,0 +1,86 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 200 200]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+      /F2 5 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+% Single space after endstream and endobj.
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream 
+endobj 
+% Multiple spaces after endstream and endobj.
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream  
+endobj  
+% Tab after endstream and endobj.
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream	
+endobj	
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/trailer_end_trailing_space.pdf b/testing/resources/trailer_end_trailing_space.pdf
new file mode 100644
index 0000000..e0ec6e4
--- /dev/null
+++ b/testing/resources/trailer_end_trailing_space.pdf
@@ -0,0 +1,99 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [0 0 200 200]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+      /F2 5 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+% Single space after endstream and endobj.
+6 0 obj <<
+  /Length 83
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream 
+endobj 
+% Multiple spaces after endstream and endobj.
+6 0 obj <<
+  /Length 83
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream  
+endobj  
+% Tab after endstream and endobj.
+6 0 obj <<
+  /Length 83
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream	
+endobj	
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000157 00000 n 
+0000000299 00000 n 
+0000000910 00000 n 
+0000000774 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+986
+%%EOF
diff --git a/testing/resources/two_signatures.in b/testing/resources/two_signatures.in
new file mode 100644
index 0000000..f18495f
--- /dev/null
+++ b/testing/resources/two_signatures.in
@@ -0,0 +1,155 @@
+{{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}}
+{{startxref}}
+%%EOF
+
+%% First incremental update adds an initial signature and update objects to
+%% refer to it.
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [7 0 R]
+    /SigFlags 3
+  >>
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Annots [7 0 R]
+>>
+endobj
+%% ByteRange is a pairs of integers (starting byte offset, length in bytes)
+{{object 5 0}} <<
+  /Type /Sig
+  /Filter /Adobe.PPKMS
+  /SubFilter /ETSI.CAdES.detached
+  /ByteRange [0 10 30 10]
+  /Contents <308006092A864886F70D010702A0803080020101>
+  /M (D:20200624093114+02'00')
+>>
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 0 0]
+  /Length 0
+>>
+stream
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Sig
+  /F 132
+  /Rect [0 0 0 0]
+  /P 3 0 R
+  /T (Signature1)
+  /V 5 0 R
+  /DV 5 0 R
+  /AP <<
+    /N 6 0 R
+  >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
+
+%% Second incremental update adds a next signature and update objects once again to refer to it.
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [7 0 R 10 0 R]
+    /SigFlags 3
+  >>
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Annots [7 0 R 10 0 R]
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Sig
+  /Filter /Adobe.PPKMS
+  /SubFilter /ETSI.CAdES.detached
+  /ByteRange [0 40 50 10]
+  /Contents <308006092A864886F70D010702A080308002010131>
+  /M (D:20200624093118+02'00')
+>>
+endobj
+{{object 9 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 0 0]
+  /Length 0
+>>
+stream
+endstream
+endobj
+{{object 10 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Sig
+  /F 132
+  /Rect [0 0 0 0]
+  /P 3 0 R
+  /T (Signature2)
+  /V 8 0 R
+  /DV 8 0 R
+  /AP <<
+    /N 9 0 R
+  >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/two_signatures.pdf b/testing/resources/two_signatures.pdf
new file mode 100644
index 0000000..9a033d6
--- /dev/null
+++ b/testing/resources/two_signatures.pdf
@@ -0,0 +1,195 @@
+%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
+>>
+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 
+0000000226 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 5
+>>
+startxref
+466
+%%EOF
+
+%% First incremental update adds an initial signature and update objects to
+%% refer to it.
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [7 0 R]
+    /SigFlags 3
+  >>
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Annots [7 0 R]
+>>
+endobj
+%% ByteRange is a pairs of integers (starting byte offset, length in bytes)
+5 0 obj <<
+  /Type /Sig
+  /Filter /Adobe.PPKMS
+  /SubFilter /ETSI.CAdES.detached
+  /ByteRange [0 10 30 10]
+  /Contents <308006092A864886F70D010702A0803080020101>
+  /M (D:20200624093114+02'00')
+>>
+endobj
+6 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 0 0]
+  /Length 0
+>>
+stream
+endstream
+endobj
+7 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Sig
+  /F 132
+  /Rect [0 0 0 0]
+  /P 3 0 R
+  /T (Signature1)
+  /V 5 0 R
+  /DV 5 0 R
+  /AP <<
+    /N 6 0 R
+  >>
+>>
+endobj
+xref
+0 8
+0000000000 65535 f 
+0000000726 00000 n 
+0000000068 00000 n 
+0000000835 00000 n 
+0000000226 00000 n 
+0000000998 00000 n 
+0000001201 00000 n 
+0000001303 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 8
+>>
+startxref
+1475
+%%EOF
+
+%% Second incremental update adds a next signature and update objects once again to refer to it.
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [7 0 R 10 0 R]
+    /SigFlags 3
+  >>
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Annots [7 0 R 10 0 R]
+>>
+endobj
+8 0 obj <<
+  /Type /Sig
+  /Filter /Adobe.PPKMS
+  /SubFilter /ETSI.CAdES.detached
+  /ByteRange [0 40 50 10]
+  /Contents <308006092A864886F70D010702A080308002010131>
+  /M (D:20200624093118+02'00')
+>>
+endobj
+9 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 0 0]
+  /Length 0
+>>
+stream
+endstream
+endobj
+10 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Sig
+  /F 132
+  /Rect [0 0 0 0]
+  /P 3 0 R
+  /T (Signature2)
+  /V 8 0 R
+  /DV 8 0 R
+  /AP <<
+    /N 9 0 R
+  >>
+>>
+endobj
+xref
+0 11
+0000000000 65535 f 
+0000001801 00000 n 
+0000000068 00000 n 
+0000001917 00000 n 
+0000000226 00000 n 
+0000000998 00000 n 
+0000001201 00000 n 
+0000001303 00000 n 
+0000002011 00000 n 
+0000002216 00000 n 
+0000002318 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 11
+>>
+startxref
+2491
+%%EOF
diff --git a/testing/resources/uri_action_nonascii.in b/testing/resources/uri_action_nonascii.in
new file mode 100644
index 0000000..d2ec540
--- /dev/null
+++ b/testing/resources/uri_action_nonascii.in
@@ -0,0 +1,44 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 200 200]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /A 5 0 R
+  /FT /Tx
+  /Ff 29360128
+  /Type /Annot
+  /Subtype /Link
+  /F 4
+  /Rect [1 1 199 199]
+  /BS  <<
+    /W 1
+    /S /S
+  >>
+  /DA (/Helv 0 Tf 0 0 0 rg)
+  /V ()
+>>
+endobj
+{{object 5 0}} <<
+  /S /URI
+  /URI (https://example.com/\245octal\307chars)
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/uri_action_nonascii.pdf b/testing/resources/uri_action_nonascii.pdf
new file mode 100644
index 0000000..ffc084a
--- /dev/null
+++ b/testing/resources/uri_action_nonascii.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 200]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [4 0 R]
+>>
+endobj
+4 0 obj <<
+  /A 5 0 R
+  /FT /Tx
+  /Ff 29360128
+  /Type /Annot
+  /Subtype /Link
+  /F 4
+  /Rect [1 1 199 199]
+  /BS  <<
+    /W 1
+    /S /S
+  >>
+  /DA (/Helv 0 Tf 0 0 0 rg)
+  /V ()
+>>
+endobj
+5 0 obj <<
+  /S /URI
+  /URI (https://example.com/\245octal\307chars)
+>>
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000157 00000 n 
+0000000226 00000 n 
+0000000414 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+493
+%%EOF
diff --git a/testing/resources/viewer_pref_types.in b/testing/resources/viewer_pref_types.in
new file mode 100644
index 0000000..11b8034
--- /dev/null
+++ b/testing/resources/viewer_pref_types.in
@@ -0,0 +1,36 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /ViewerPreferences <<
+    /Bool true
+    /Num 1
+    /Str (str)
+    /Name /name
+    /Null null
+    /Ref 3 0 R
+    /EmptyArray []
+    /GoodArray [true 1 (str) /name]
+    /BadArray1 [true []]
+    /BadArray2 [1 <<>>]
+    /BadArray3 [/name 3 0 R]
+    /Dict <<
+    >>
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/viewer_pref_types.pdf b/testing/resources/viewer_pref_types.pdf
new file mode 100644
index 0000000..f74ca6f
--- /dev/null
+++ b/testing/resources/viewer_pref_types.pdf
@@ -0,0 +1,46 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /ViewerPreferences <<
+    /Bool true
+    /Num 1
+    /Str (str)
+    /Name /name
+    /Null null
+    /Ref 3 0 R
+    /EmptyArray []
+    /GoodArray [true 1 (str) /name]
+    /BadArray1 [true []]
+    /BadArray2 [1 <<>>]
+    /BadArray3 [/name 3 0 R]
+    /Dict <<
+    >>
+  >>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+>>
+endobj
+xref
+0 4
+0000000000 65535 f 
+0000000015 00000 n 
+0000000337 00000 n 
+0000000400 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 4
+>>
+startxref
+451
+%%EOF
diff --git a/testing/resources/xfa/xfa_break_before_after.in b/testing/resources/xfa/xfa_break_before_after.in
new file mode 100644
index 0000000..1ff2f3a
--- /dev/null
+++ b/testing/resources/xfa/xfa_break_before_after.in
@@ -0,0 +1,54 @@
+{{header}}
+{{include ../xfa_catalog_1_0.fragment}}
+{{include ../xfa_object_2_0.fragment}}
+{{include ../xfa_preamble_3_0.fragment}}
+{{include ../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.0/">
+  <subform name="Form01" layout="tb" locale="en_ZA" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1" initialNumber="1">
+        <contentArea x="10.455mm" w="190.5mm" h="286mm"/>
+        <occur min="1" max="1"/>
+      </pageArea>
+    </pageSet>
+    <subform w="190.5mm" h="286mm" name="TForm1SubForm1">
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakBefore targetType="contentArea" />
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakAfter targetType="contentArea" />
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakBefore targetType="pageArea" />
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakAfter targetType="pageArea" />
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakBefore targetType="pageEven" />
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakAfter targetType="pageEven" />
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakBefore targetType="pageOdd" />
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakAfter targetType="pageOdd" />
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../xfa_locale_6_0.fragment}}
+{{include ../xfa_postamble_7_0.fragment}}
+{{include ../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{endxref}}
+%%EOF
diff --git a/testing/resources/xfa/xfa_break_before_after.pdf b/testing/resources/xfa/xfa_break_before_after.pdf
new file mode 100644
index 0000000..2235016
--- /dev/null
+++ b/testing/resources/xfa/xfa_break_before_after.pdf
@@ -0,0 +1,262 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /AcroForm 2 0 R
+  /Extensions <<
+    /ADBE <<
+      /BaseVersion /1.7
+      /ExtensionLevel 8
+    >>
+  >>
+  /NeedsRendering true
+  /Pages 8 0 R
+  /Type /Catalog
+>>
+endobj
+2 0 obj <<
+  /XFA [
+    (preamble)
+    3 0 R
+    (config)
+    4 0 R
+    (template)
+    5 0 R
+    (localeSet)
+    6 0 R
+    (postamble)
+    7 0 R
+  ]
+>>
+endobj
+3 0 obj <<
+  /Length 124
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2018-02-23T21:37:11Z" uuid="21482798-7bf0-40a4-bc5d-3cefdccf32b5">
+endstream
+endobj
+4 0 obj <<
+  /Length 642
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+  <destination>pdf</destination>
+  <pdf>
+    <fontInfo/>
+  </pdf>
+</agent>
+<present>
+  <pdf>
+    <version>1.7</version>
+    <adobeExtensionLevel>8</adobeExtensionLevel>
+    <renderPolicy>client</renderPolicy>
+    <scriptModel>XFA</scriptModel>
+    <interactive>1</interactive>
+  </pdf>
+  <xdp>
+    <packets>*</packets>
+  </xdp>
+  <destination>pdf</destination>
+  <script>
+    <runScripts>server</runScripts>
+  </script>
+</present>
+<acrobat>
+  <acrobat7>
+    <dynamicRender>required</dynamicRender>
+  </acrobat7>
+  <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+5 0 obj <<
+  /Length 1191
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.0/">
+  <subform name="Form01" layout="tb" locale="en_ZA" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1" initialNumber="1">
+        <contentArea x="10.455mm" w="190.5mm" h="286mm"/>
+        <occur min="1" max="1"/>
+      </pageArea>
+    </pageSet>
+    <subform w="190.5mm" h="286mm" name="TForm1SubForm1">
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakBefore targetType="contentArea" />
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakAfter targetType="contentArea" />
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakBefore targetType="pageArea" />
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakAfter targetType="pageArea" />
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakBefore targetType="pageEven" />
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakAfter targetType="pageEven" />
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakBefore targetType="pageOdd" />
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakAfter targetType="pageOdd" />
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+6 0 obj <<
+  /Length 3455
+>>
+stream
+<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">
+  <locale name="en_US" desc="English (United States)">
+    <calendarSymbols name="gregorian">
+      <monthNames>
+        <month>January</month>
+        <month>February</month>
+        <month>March</month>
+        <month>April</month>
+        <month>May</month>
+        <month>June</month>
+        <month>July</month>
+        <month>August</month>
+        <month>September</month>
+        <month>October</month>
+        <month>November</month>
+        <month>December</month>
+      </monthNames>
+      <monthNames abbr="1">
+        <month>Jan</month>
+        <month>Feb</month>
+        <month>Mar</month>
+        <month>Apr</month>
+        <month>May</month>
+        <month>Jun</month>
+        <month>Jul</month>
+        <month>Aug</month>
+        <month>Sep</month>
+        <month>Oct</month>
+        <month>Nov</month>
+        <month>Dec</month>
+      </monthNames>
+      <dayNames>
+        <day>Sunday</day>
+        <day>Monday</day>
+        <day>Tuesday</day>
+        <day>Wednesday</day>
+        <day>Thursday</day>
+        <day>Friday</day>
+        <day>Saturday</day>
+      </dayNames>
+      <dayNames abbr="1">
+        <day>Sun</day>
+        <day>Mon</day>
+        <day>Tue</day>
+        <day>Wed</day>
+        <day>Thu</day>
+        <day>Fri</day>
+        <day>Sat</day>
+      </dayNames>
+      <meridiemNames>
+        <meridiem>AM</meridiem>
+        <meridiem>PM</meridiem>
+      </meridiemNames>
+      <eraNames>
+        <era>BC</era>
+        <era>AD</era>
+      </eraNames>
+    </calendarSymbols>
+    <datePatterns>
+      <datePattern name="full">EEEE, MMMM D, YYYY</datePattern>
+      <datePattern name="long">MMMM D, YYYY</datePattern>
+      <datePattern name="med">MMM D, YYYY</datePattern>
+      <datePattern name="short">M/D/YY</datePattern>
+    </datePatterns>
+    <timePatterns>
+      <timePattern name="full">h:MM:SS A Z</timePattern>
+      <timePattern name="long">h:MM:SS A Z</timePattern>
+      <timePattern name="med">h:MM:SS A</timePattern>
+      <timePattern name="short">h:MM A</timePattern>
+    </timePatterns>
+    <dateTimeSymbols>GyMdkHmsSEDFwWahKzZ</dateTimeSymbols>
+    <numberPatterns>
+      <numberPattern name="numeric">z,zz9.zzz</numberPattern>
+      <numberPattern name="currency">$z,zz9.99|($z,zz9.99)</numberPattern>
+      <numberPattern name="percent">z,zz9%</numberPattern>
+    </numberPatterns>
+    <numberSymbols>
+      <numberSymbol name="decimal">.</numberSymbol>
+      <numberSymbol name="grouping">,</numberSymbol>
+      <numberSymbol name="percent">%</numberSymbol>
+      <numberSymbol name="minus">-</numberSymbol>
+      <numberSymbol name="zero">0</numberSymbol>
+    </numberSymbols>
+    <currencySymbols>
+      <currencySymbol name="symbol">$</currencySymbol>
+      <currencySymbol name="isoname">USD</currencySymbol>
+      <currencySymbol name="decimal">.</currencySymbol>
+    </currencySymbols>
+    <typefaces>
+      <typeface name="Myriad Pro"/>
+      <typeface name="Minion Pro"/>
+      <typeface name="Courier Std"/>
+      <typeface name="Adobe Pi Std"/>
+      <typeface name="Adobe Hebrew"/>
+      <typeface name="Adobe Arabic"/>
+      <typeface name="Adobe Thai"/>
+      <typeface name="Kozuka Gothic Pro-VI M"/>
+      <typeface name="Kozuka Mincho Pro-VI R"/>
+      <typeface name="Adobe Ming Std L"/>
+      <typeface name="Adobe Song Std L"/>
+      <typeface name="Adobe Myungjo Std M"/>
+    </typefaces>
+  </locale>
+</localeSet>
+endstream
+endobj
+7 0 obj <<
+  /Length 11
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+8 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [9 0 R]
+>>
+endobj
+9 0 obj <<
+  /Type /Page
+  /Parent 8 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000199 00000 n 
+0000000358 00000 n 
+0000000534 00000 n 
+0000001228 00000 n 
+0000002472 00000 n 
+0000005980 00000 n 
+0000006042 00000 n 
+0000006105 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+{{endxref}}
+%%EOF
diff --git a/testing/resources/xfa/xfa_combobox.pdf b/testing/resources/xfa/xfa_combobox.pdf
index 0989361..a4b606c 100644
--- a/testing/resources/xfa/xfa_combobox.pdf
+++ b/testing/resources/xfa/xfa_combobox.pdf
@@ -224,7 +224,7 @@
 endobj
 9 0 obj <<
   /Type /Page
-  /Parent 2 0 R
+  /Parent 8 0 R
   /MediaBox [0 0 612 792]
 >>
 endobj
diff --git a/testing/resources/xfa/xfa_date_time_edit.pdf b/testing/resources/xfa/xfa_date_time_edit.pdf
index 04ea239..33f02bd 100644
--- a/testing/resources/xfa/xfa_date_time_edit.pdf
+++ b/testing/resources/xfa/xfa_date_time_edit.pdf
@@ -219,7 +219,7 @@
 endobj
 9 0 obj <<
   /Type /Page
-  /Parent 2 0 R
+  /Parent 8 0 R
   /MediaBox [0 0 612 792]
 >>
 endobj
diff --git a/testing/resources/xfa/xfa_image_edit.in b/testing/resources/xfa/xfa_image_edit.in
index 0e36fdd..a3bdae6 100644
--- a/testing/resources/xfa/xfa_image_edit.in
+++ b/testing/resources/xfa/xfa_image_edit.in
@@ -21,7 +21,9 @@
           <imageEdit data="embed"/>
         </ui>
         <value>
-          <image contentType="image/jpg">FOOOEY</image>
+          <image contentType="image/jpg">
+            {{include ../mona_lisa.fragment}}
+          </image>
         </value>
       </field>
     </subform>
diff --git a/testing/resources/xfa/xfa_image_edit.pdf b/testing/resources/xfa/xfa_image_edit.pdf
index 7496367..d35ef5d 100644
--- a/testing/resources/xfa/xfa_image_edit.pdf
+++ b/testing/resources/xfa/xfa_image_edit.pdf
@@ -72,7 +72,7 @@
 endstream
 endobj
 5 0 obj <<
-  /Length 667
+  /Length 9005
 >>
 stream
 <template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
@@ -89,7 +89,117 @@
           <imageEdit data="embed"/>
         </ui>
         <value>
-          <image contentType="image/jpg">FOOOEY</image>
+          <image contentType="image/jpg">
+/9j/4AAQSkZJRgABAQEAZABkAAD//gBSRmlsZSBzb3VyY2U6IGh0dHA6Ly9jb21tb25zLndpa2lt
+ZWRpYS5vcmcvd2lraS9GaWxlOk1vbmFfTGlzYV9mYWNlXzgwMHg4MDBweC5qcGf/2wBDAAYEBQYF
+BAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUo
+KSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgo
+KCgoKCgoKCgoKCgoKCj/wAARCAB4AHgDAREAAhEBAxEB/8QAHAAAAgIDAQEAAAAAAAAAAAAABQYE
+BwEDCAIA/8QAOxAAAgECBQIEBAQFAwMFAAAAAQIDBBEABRIhMQZBEyJRYQcycYEUkaGxFUJiwfAj
+JOEWF9ElM1LC8f/EABoBAAIDAQEAAAAAAAAAAAAAAAIDAQQFAAb/xAAvEQACAgICAgAEBQQCAwAA
+AAAAAQIRAyESMQRBBRMiUTJhcaGxFCNSgZHwweHx/9oADAMBAAIRAxEAPwBqiIChT5t7gEYwX1ss
+JUbY0ErKBHpFu498Q3fRKTMmJVNm2POo9/tiG2kTFG+FfIAq9t2ta2BtvsKtkqKmDE2UgMDiUn2C
+3ugTnvVGRdPy6M2zKFJxv+HivLN6i6LuPvbDsalPaRPy29Cw/wAYOn1kPgUmYS3OxZUjJvwbFrgY
+fHx8hziqCVF8V+namVUnSvojzqlp9SkD3QkYGWKa62SsY5ZVmVDmlMKnLamCqgvu8bBgPYjkH64U
+3x/FpkcWF4yT8oG4337YBzdUgVHezEwN1N1BxzurslJGI1uSpJG3bvjlew2bYUN+fS+Cj12DJkqE
+WNgOeL4OM66AaNscXkb2vziVK47ZD0IkcG1r/X/PthVWFZLRNKqAAdiCT9cC19jk77MRpqILKNjf
+i/tbCpcug40SooAz6tNyfLsOPbBQhslsrH4n/ECXLa+Tp7IZvBrxaOprEXU0Fx8if1+/a/ri3i8e
+1zn1/P8A6Ba6oR8h6YqY8szKshpauaWdNCzBHkZifmYk778ffAZvIU5Qg3pMsQg4xbS2KmcZQKPU
+aqmqItVhqeEqL9rnGhjzc3SaZXyY63QGDvSuJaScofVfMp57YsOPLUkLUnHcQplXUFfQZrFV5bK2
+XZkll8SEWRx6MnBB9D+mEywppqW0N+by/U6Y+EvXdP1rlrJKkVNnVMAtVTLsrDgSJ30nj2O3pjLz
+4XhlXr0d+JWh6eJQCy2b6DEOKAT+55hQrYsNrW98TH2S2ToAATYG5GwI4wUqSYPZuSEBNXAIBO2E
+8eO0TZ8UZVtf5hvbE7RHYkQRG6kix7f2wuMnexjr0SvDZiqpf6dsH30D12bIKe77gna7W7bYHbbC
+tIFdf9R0/R3S9VmcrIJFHh06t/PKR5Rbk+v0GLMISk1CK2wE03begP8ACHommhy6LM8zjhqM0qf9
+eeoljEjhm3sL7D7b++KmXK8+Tin9K0l+hcUflwTrbLXFHCABftYdvtthbjRHNgjOsoiqKaaJ9Tqy
+kFWAKn88Vsip6HwlfZzV8R+kKeikeSjS128wCD+2NT4f57yfTP0I8jCltFWyJJEXul0vYkLce4Jx
+tqSfRRoJ9K5/WdNdSUOb5a7iWmbUVPEkZI1o3qCt/wBMBmxLLjcP+P1IjOpWztvJa+lzTJ6PMqF9
+dJVxLLG47qwuPpbi2MqP0Opd9ByV9E2KNWF97AbNiWlu2crN8cIABIFxyL7YW2yTZGhlNr7eltsc
+nZD0ZkpyI++1+cMcVVg3sUUWMxRtHpbVc3GKr4voYrM063k32YEi9t9zhiTXRDJVHAzSB3uoK2F/
+bEwm4u7OaT0UL8cs9TqHPsqyZEH+nWxaWvuus7KB6keYn6Dti5gySm5Zn0kyflrGlBdtl7dNWp6F
+FY2awU39RjFxx1aNHJ9g6pdWa5LX3J9MS27aE0uyHXiSRCFN9rG3OEThyGxaQi9UdOpXwyCW1muD
+b/PbC4J4ZckNdTVM5u63y5cpzeemilCQDzebuff9bY9R4eV5cak+zMzxUJUhVmbUNQt6W9bC++Lp
+WaSOjvgT1JK/w+ho5mtHl9Y1OD6o1pB9hrP5Yy/Mhxm697Gp3Wy0aXNhNMsIYi29u1j2v37Yq7fZ
+LetB01arE5DKVQaRY8m+/wDbHUxTnvQRplvbcaTuL+/GIXdMY2bwbxFd2wXNtUD07KMyvqid3WGn
+LvqszF0PIB8o/LfEairZNT9D109mYzSmMiRaJUkMbjmxIDCx+hwacpNEKadk3Nq38HlE8oGpvw7s
+thvcKdsLlG6TCUt0jkbrCsaXramzCGSNr10MmkfNrUqtxfsQBtjS8eP9hwf2f7jJ25KS+6Lv/wC6
+VBDlQq0lSipEm/DhqpZSddr2IRG08H5iDtxjGXi5XJQjt1fa6L7yQrkx1n6p0dKSZwIwAE4vca+B
+Y973B++KXJyfFd9B/LS2xLTq7NKbIWzvOfHpaeRxGFWkknILfKCFsBe3rg/lKWR48cr/ADbpHcmo
+3JAOLrjqHPcwio8uyeaohcj/AHUaSJGBv5jrUabW4P64tT8XHjjynk39tX+wr5km6jErP4oUM8Od
++JUfM/zC99/rjT+GZYvHSRV8lNSViSmmF2XUrM1wQefe2NTsp7uix/hJWFKDNaUMViM0E5AYgsNJ
+Ui/2GKHmR/C/ZPWi5MkzgykxMYlABszCxuP32xVUFsGWR+w1Lm0cNJHGAJRIw3U302bfbne2Ipds
+SOXT9UKqEsHLJqBH0HbFKVqSTLqaadByJ/D1FgbW2vhuO07BbXRydQZocvqqclJCW31XA3IsSe1z
+fbFiWHndC1krQfyLrl6TI5KKFNFTGDaULfU1yGJ/NQCe2AyYJJ/T0Hjkktk2l6tmoTmVI0zSrBEq
+wySG5RmZthc8AXIxPy3KCZy1Oijxoi67yiKUf7eHNI1Z73DDxRck+v8A4xou/wCnlXbi/wCBqf1x
+17OwP4TBW05RJqiJTJ4jBQu7X3O4Nj7jHmFBTjyTNNzcHRo6ioUk6ckprBYGlBFxfYf/AIMdwaWi
+LTeyblGXRnLYYWqaiOwsdDDf87jELFyVSOlNx/Caa5abKKdjCZGL3LNI5YnC5pQdRQULkrZyz8Yc
+wNZ1G3hFrIeB2/y2PRfCYccNv2Z/lu5UhEcuXErWYHdibbE/2xq1ZRHz4VpNP1BVU1I0i+PRlyq2
+a5V1P/2/XFPyopQTfoJLk9Fy5TRVkMjvLTVMpA3ZrHew/I/bvijyW9kSg0uiWIK2PSYaGpYA7Cwv
+xyT3xzURLvsaukq2opJSKiCWJGJtZCdz9sJlFdpjMc2tMbJMxQqbyMCb8jAPI1sclZyPmMBZ2ZZW
+GkeJqLaix4A+mNODr0IaNbMYiDDJIAbgMBbUpAO4F9r3/K+J2yaT7AGeVdTYtHIG1Pu3qTcbn7n8
+sWMUVQSb5C3mFayrGYXa8bhyrdiDdbHvxh0YJ6GznpUdk9I9QJmuUUciOC00aEXO1iAf748jNPHc
+H6dGy6nUgN8QOuafpqloqGty+tkr5m3iiFw68albhhxxv64b4+CedaaSX3ETlHG7e7GLpPMzU9OU
+lZVRS0krhv8AQlPnUajpv72thTSg2rug/wASTFzrTNZTCywgkr9sLjHnJX6Dk6WjmLrCoaozeWR7
+sdR2btvwceq8VVjSRlZvxAaWcNEpK2f+axPGLCEMNdLVMkWYRmJn8UkqpHuLf87emByxuLvoh7Lb
+ynM8wphrSSrjB8xW1lJ2/wA7YoNKnQDeg7H1DXooAmrFckWUbn9rdsQ8a9CqDtB1XUJvUzh1202s
+Dv625tgJwXVEptK7CY6jlqoisTygggGy7geuFcFdkcm/pbOdpa7xpQzCXzeQG2s27WONCMKHHppn
+lHhqg1KbhhtfjkG37euJBe2Q81y92y8vM6HUy2AI8p3P9t//ADgoZVy0iYJ3QqThI4NBVZJmc3bk
+qB6fXFlNt2Ntca9l5fA7qWlzTp6PJ6ipNPmVIDGrgjV4d/Kwvza9vtjznxXBKGR5K+mX8ml4mVTg
+oe0WR1OlR4UUZrcwqHUXSdcvjk29yLAfljN0pW/5LXoh5DA1NMZszrKyrn07LKiokYHoFHJuNyTi
+ZSjJrVV+oKX52LvXGcwDxI42BYgjbfe22H+Pi5y2LyTpUc/Z9KZcwZ2Yl2a/t7DHqMKSjSMvK9gy
+VlvZCRtp+vqcNQtumFMhDfjIWifS9wb3taxwvJJKLOirLlpljjdYiulnF/KfNuNhx9+cZnPTdC3H
+VBCSjUxqZFOlPMCU77knb2xHNN7YFM30ckEQIAXTfY6fMLd8Tr2CyfJnsUKlIULNfsPl55PpxiJQ
+V2FG0ikqKoLlRpRlbSCjC4O/Fr8k+98aEo/caHaWYyOVMOXBRIWCSHYj6ffCZxXabIoL5hl0/wD0
+5WySJQNEkQmURhiWCkG1jyQL++EJrmtjIqip8x8JpnnkRXZyQNPC27n12tjSiq0TrtkGOtlocziq
+sullp54SGjkDWa+3+Wwc4KUeM9pkW4SuLOi+kfjrlz9PxwZ0r0+ZIgWTSl0f+ofX0x5/P8Kywf8A
+a3H+DRx+VCf4tMEZ/wDEuOoB/A6UjI80kjC/5emBx/DZ95Dp511ErvMuojVvppr1EzXJfgL32vzj
+Sx+Lw70V5ZL62LVXSy+O5ZjIxNy9tgMXLVUV3FoinQiG+7WNiB++D7AJuWzGJ1K3BUcn7EYiULVE
+xdOy6qLNoTl1LIWEr2XSVIsGNr77d7jb9cZrwtXE7l7DdVXxVETeV4ybKoQ727m3bfCfkxUrBcrV
+UQBXwq7GJZmXRqKActf0+ww2Ka0C4Jn1TnETVI0sCC1iBGPU/p+3ptjpJvs5QS9i1Q0IVUctAlmu
+PCjB/ckg4a5xYwPUpiiZ1srPqHnbw9TahyABzta+Ikv8URFm+oqA0Eo8VDGV0MqkEDm4P14++EO0
+9DE12UbnlGtPn1VTh2aNJLIVTc+VSARtv5gMa2KVwUgX2BZUu6nURcXJY73w7sGSoO9ARUcnWuUQ
+5giyUk1QIXRvl84Ki/3IxW8vl8mTi6dB4GlkXItXqb4WU8JY0EcSWO5AuAMZGH4nKL4z2Wsnjp7R
+AynonTIgnjUpcCygj8v3wefzk1pnQxv2TOrOlBT5S/gRqiqoJ2tbfck+mEeN5vLJ9TGSxaKlqKfw
+igdGUgHy25B4xvRmn0UZRNog8GjSWQeQFjbttYYlTTlR0o0rLL6OzCiqMshK1FPSLpWO7garj5u3
+YnnFDKppvt//AEFRTHVYacgQ0udQSMynWGS4tzzqxUtydyQTiq0YfpseHLWawaYC6u3kPoCvub4O
+OWvpbI4ezbTdJxC0r1XhIx1aW8oUn1v/AJvivPzI39DsdHx5PtUVPoneOVpayRKlKZfCaJiCdyTv
+ck2F/uMaXKnpavYiiMnW1Y9OwqxTtO6C0ynQoIuo1AA78/8AGHy8aL6YN0eM16rr5qeHxggpp0Cz
+aYwLMrndTyLGx9MQvHjvfR1+hPramevr6iaSZpJJZS2vgsext9hixGKjFJE22fZbltZmtctLQwy1
+FQ+yJGNRv+1vfHTyQhHnkdImMJSfFLZevw3+EUcEyV2czh6lfkVR5EbtYnk+/tjB8n4m8v0YVS/c
+u4vGUPql2XFJl5MAWYaibD5f8tjIk0XEjNNkSIxa2jfa/wCWBa+xNkHPMnSajdbgEIwIbv6XHpgV
+qXJBJXo5zqsnEWdGjIWS8oVSG+XU9h9bb3HbHpoZ+WHn+RQljqVEHqOkENFDSqiMq1UxW3JF7H7X
+Xj64d487lyv0hWRar8wJk0arVCJ2RUJJ1MdlbixPobYt5HcW0V0iysiyTMa6ro2pqOi0RvdHjk1K
++2wIBJIxk5pwjabewoxd2hpHRvU1TmFFXVGZapoTdjqKge3Nj2H0AxXl5eOMWlHsOOCTY7ZjUTUm
+VwrLL+JrVGyKCdbD1P8ALz3OMzDH63JaTLkp8Y17KG6k8OOKopKGIvII1GuxBtxb2G55/vj0HjqT
+lym/ZTlVUhPqqA/wWREVS8Uha+m+w2IB/LjGgprmhDTI2awuuVUxmLqI49Kqex1E2PvucdGVzdE1
+SsG0ccktSqxX1Dvbgep/PBt0rOirejpj4C9IJQ5TLWTxXnqW1K9iCYx8ot6Hn7jHmfifkPNlUU9L
++TVwY/lw/Nl009Ivg2CgWsRccHFSNpWiW7dGWijuBGLXbAPYa12bWj0wGx78WxMlUQU9i91AqpSz
+CUnwyp498VcjldD8Zz7EIaTOqd6dkll/EukjWuCQbkqSeNyfXtvj0D5TxvkvVlZ1FiP1DXvJmwic
+qRCWN/fUf73xqePjUcdr2UMsvqPa5LVVVG88dNK8RYBNKni3Jt62wXz4xbi2FxbV0Fum80qKevSn
+qZXpp1kChilib8bYR5GFSi5R2LafRfGW9RR1FHFBUxzyGMJTuIxpAfgAXtfb19748/ODT2WYz0e6
+l6GOnWVzXxr4nhp8urUb3BBa/wBzhP1ctfuE6rZVOe0aNCskauXSO7Kw0rIO2/pe36Y1sGRp02Vp
+xdWKeeS02V5PGs0aLUMRaPuSMaGLnlyWnoBpRVsQayplq5DJMQVDHSnZfYY0IxSWhTdlp/B3pMZt
+RzVksPiXa2+1gP2A2NxvjH+JeY8clCJf8TEmuTOm+k8pGU5VBT69ciLubW1H1AxhOTcm37LcpWHw
+BYDa/e+Du9AL7mDGRp3IOq9sQ4vom/Z9UJqW1rEbHvgZHJ7EvrVo48qqBJJa6FTcHk9vU4RdzikW
+I9MpSopZ6itCUdGoUEkuyhUDXW9zfbgbcXxtQmlG5S3/AOCrkT6iiZknw5paurmq5JRmFbK5Z1SI
+yxq1727Ltfe57YXk+JTSUIql+4C8ZW5NhvOsloaOkZMwzGKIR/8AtJNVrCL8GyxrueRtfAYcuSTk
+4x3+l/yFOMUtspvq5MtjrUejqF8YfM0c0rgenzqP0ON7xnkcWpr+DPy1eiT0v17XZUwhqJmeEEBJ
+lVQwP9RNwdu/IwryPAhkuUdMnHlcdMtTJ/iHPDCGzFEZJPKr+EmpbDctpA59TvjMn4bb+mi1GQKp
+0myd2rM8rqengC6pUldW2K7WUEkX+vNhbA5az/Rhi2/0ohfRuWim+r83Gd59PVxK0dKvkp47Wsg9
+vfnHofGwfIxqDdv2Uck+UrQMjjaQrCpbzDg84baQNM6J+Due02Vwfgc5XRS2DxS6SFF+dQ/Kx+2P
+M/EcDlk+bD/Zq+PlShxZfFPXUtTRGqpqiOSMjV4isCCPrx3xm8lX2D4uyTSFpRrIIU4OKs6TrROA
+TQe/IGHqOhLbs1SeGgYHn62H1wuSDTbK862rqGOOZDLrYA/JJpAJG4v22v72xWWJzmmh/wAxQVMq
+PMetMloJJHaMZhU2IjhtaFe3H83bnGvj+HZsi/xX7lV+RFO3sWM++JOdVkfgzVy5fSqDppqRLEDi
+5tsP0Pti/g+F4obrl+bEy8lvrQjVuc1Uxv8AiZyp/pA3+u+NKOCEVVFdzb7YPeeRyLs7G9zra+DS
+SAb9mI9QG1irHcDg46jhx6Unlqf/AEzYTIC8bjctYElSODtc7+n0xUz41H+5/wB/UtYJuX0P/v5A
+3rmeq/i38PqleMU41OjC12Nz+gNh9cT4UY8OcfYnNafF+hZbbzXv7W74t0IDmQQJJI81TfSGAuDx
+te+/+cYRlbWkGl9xsjzYitpIqYMksqDQNfyKCbk32vYHn/5DFT5Sabl6GqdPQcyjNJTI6ZfO1KsV
+tbU8ugsx33HDADfj88V8mBNXJXY6GV9Idss+IWf5epi/iaT6AAyVFOCRe1hdbeo5v2xVfiQ7imv9
+jVlT7C8HxZro95IKCcKbF4y4J/Q/TAf0kv8AIJzh3REz74pVE1NIbU9DDGt5JSS+/ZFIFy1+w3+g
+3wEfDcpV2BLyIpfSVD1D1f40FU5m8JpzaOJV8SZ151MSdMYNztuxxq4PD4ta6/4/19ytPNYjPXSy
+E+CCt99iSxPqTycaKikV07C2X5bClI1bVgtHTjdb7SPvZfzt+uFSnLkox7YVLshVcaRKseka7NyN
+gbL/AM4bFtnPQLWOQm6q5ABJ8p49T7e+D5AbZ6VvmNh2sL8Y6yVYRy+qemqaadLiSFw4sObdt8RJ
+cotP2HGTi7XZ/9k=
+          </image>
         </value>
       </field>
     </subform>
@@ -222,7 +332,7 @@
 endobj
 9 0 obj <<
   /Type /Page
-  /Parent 2 0 R
+  /Parent 8 0 R
   /MediaBox [0 0 612 792]
 >>
 endobj
@@ -234,14 +344,14 @@
 0000000358 00000 n 
 0000000534 00000 n 
 0000001228 00000 n 
-0000001947 00000 n 
-0000005455 00000 n 
-0000005517 00000 n 
-0000005580 00000 n 
+0000010286 00000 n 
+0000013794 00000 n 
+0000013856 00000 n 
+0000013919 00000 n 
 trailer <<
   /Root 1 0 R
   /Size 10
 >>
 startxref
-5657
+13996
 %%EOF
diff --git a/testing/resources/xfa/xfa_multiline_textfield.pdf b/testing/resources/xfa/xfa_multiline_textfield.pdf
index 6b7eae4..eedd6a2 100644
--- a/testing/resources/xfa/xfa_multiline_textfield.pdf
+++ b/testing/resources/xfa/xfa_multiline_textfield.pdf
@@ -221,7 +221,7 @@
 endobj
 9 0 obj <<
   /Type /Page
-  /Parent 2 0 R
+  /Parent 8 0 R
   /MediaBox [0 0 612 792]
 >>
 endobj
diff --git a/testing/resources/xfa_pages_8_0.fragment b/testing/resources/xfa_pages_8_0.fragment
index ce089c4..2a8a910 100644
--- a/testing/resources/xfa_pages_8_0.fragment
+++ b/testing/resources/xfa_pages_8_0.fragment
@@ -6,7 +6,7 @@
 endobj
 {{object 9 0}} <<
   /Type /Page
-  /Parent 2 0 R
+  /Parent 8 0 R
   /MediaBox [0 0 612 792]
 >>
 endobj
diff --git a/testing/scoped_set_tz.cpp b/testing/scoped_set_tz.cpp
new file mode 100644
index 0000000..06b70dc
--- /dev/null
+++ b/testing/scoped_set_tz.cpp
@@ -0,0 +1,44 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/scoped_set_tz.h"
+
+#include <stdlib.h>
+#include <time.h>
+
+#include "build/build_config.h"
+#include "third_party/base/check_op.h"
+
+namespace {
+
+constexpr char kTZ[] = "TZ";
+
+#if BUILDFLAG(IS_WIN)
+#define SETENV(name, value) _putenv_s(name, value)
+#define TZSET _tzset
+#define UNSETENV(name) _putenv_s(name, "")
+#else
+#define SETENV(name, value) setenv(name, value, 1)
+#define TZSET tzset
+#define UNSETENV(name) unsetenv(name)
+#endif
+
+}  // namespace
+
+ScopedSetTZ::ScopedSetTZ(const std::string& tz) {
+  const char* old_tz = getenv(kTZ);
+  if (old_tz)
+    old_tz_ = old_tz;
+
+  CHECK_EQ(0, SETENV(kTZ, tz.c_str()));
+  TZSET();
+}
+
+ScopedSetTZ::~ScopedSetTZ() {
+  if (old_tz_.has_value())
+    CHECK_EQ(0, SETENV(kTZ, old_tz_.value().c_str()));
+  else
+    CHECK_EQ(0, UNSETENV(kTZ));
+  TZSET();
+}
diff --git a/testing/scoped_set_tz.h b/testing/scoped_set_tz.h
new file mode 100644
index 0000000..5928958
--- /dev/null
+++ b/testing/scoped_set_tz.h
@@ -0,0 +1,26 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_SCOPED_SET_TZ_H_
+#define TESTING_SCOPED_SET_TZ_H_
+
+#include <string>
+
+#include "core/fxcrt/fx_memory.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+class ScopedSetTZ {
+ public:
+  FX_STACK_ALLOCATED();
+
+  explicit ScopedSetTZ(const std::string& tz);
+  ScopedSetTZ(const ScopedSetTZ&) = delete;
+  ScopedSetTZ& operator=(const ScopedSetTZ&) = delete;
+  ~ScopedSetTZ();
+
+ private:
+  absl::optional<std::string> old_tz_;
+};
+
+#endif  // TESTING_SCOPED_SET_TZ_H_
diff --git a/testing/string_write_stream.cpp b/testing/string_write_stream.cpp
index 53141eb..1a99d9a 100644
--- a/testing/string_write_stream.cpp
+++ b/testing/string_write_stream.cpp
@@ -1,8 +1,9 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "testing/string_write_stream.h"
+
 #include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/widestring.h"
 
@@ -10,23 +11,7 @@
 
 StringWriteStream::~StringWriteStream() = default;
 
-FX_FILESIZE StringWriteStream::GetSize() {
-  return stream_.tellp();
-}
-
-bool StringWriteStream::Flush() {
-  return true;
-}
-
-bool StringWriteStream::WriteBlockAtOffset(const void* pData,
-                                           FX_FILESIZE offset,
-                                           size_t size) {
-  ASSERT(offset == 0);
-  stream_.write(static_cast<const char*>(pData), size);
-  return true;
-}
-
-bool StringWriteStream::WriteString(ByteStringView str) {
-  stream_.write(str.unterminated_c_str(), str.GetLength());
+bool StringWriteStream::WriteBlock(pdfium::span<const uint8_t> buffer) {
+  stream_.write(reinterpret_cast<const char*>(buffer.data()), buffer.size());
   return true;
 }
diff --git a/testing/string_write_stream.h b/testing/string_write_stream.h
index 08a2174..fc4dc02 100644
--- a/testing/string_write_stream.h
+++ b/testing/string_write_stream.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,18 +10,13 @@
 
 #include "core/fxcrt/fx_stream.h"
 
-class StringWriteStream final : public IFX_SeekableWriteStream {
+class StringWriteStream final : public IFX_RetainableWriteStream {
  public:
   StringWriteStream();
   ~StringWriteStream() override;
 
-  // IFX_SeekableWriteStream
-  FX_FILESIZE GetSize() override;
-  bool Flush() override;
-  bool WriteBlockAtOffset(const void* pData,
-                          FX_FILESIZE offset,
-                          size_t size) override;
-  bool WriteString(ByteStringView str) override;
+  // IFX_WriteStream:
+  bool WriteBlock(pdfium::span<const uint8_t> buffer) override;
 
   std::string ToString() const { return stream_.str(); }
 
diff --git a/testing/test.gni b/testing/test.gni
index 5a8505f..6ad2c2d 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -1,4 +1,4 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
+# Copyright 2015 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -14,6 +14,7 @@
 template("test") {
   if (is_android) {
     import("//build/config/android/config.gni")
+    import("//build/config/android/internal_rules.gni")
     import("//build/config/android/rules.gni")
 
     _use_raw_android_executable = defined(invoker.use_raw_android_executable) &&
@@ -53,12 +54,9 @@
         # the default shared_library configs rather than executable configs.
         configs -= [
           "//build/config:shared_library_config",
-          "//build/config/android:hide_all_but_jni_onload",
-        ]
-        configs += [
-          "//build/config:executable_config",
           "//build/config/android:hide_all_but_jni",
         ]
+        configs += [ "//build/config:executable_config" ]
 
         # Don't output to the root or else conflict with the group() below.
         output_name = rebase_path(_exec_output, root_out_dir)
@@ -68,26 +66,41 @@
         testonly = true
         dist_dir = "$root_out_dir/$target_name"
         binary = _exec_output
-        deps = [
-          ":$_exec_target",
-        ]
+        deps = [ ":$_exec_target" ]
         if (defined(invoker.extra_dist_files)) {
           extra_files = invoker.extra_dist_files
         }
       }
     } else {
-      _library_target = "_${target_name}__library"
-      _apk_target = "${target_name}_apk"
+      _library_target = "${target_name}__library"
+      _apk_target = "${target_name}__apk"
       _apk_specific_vars = [
         "android_manifest",
         "android_manifest_dep",
         "enable_multidex",
+        "product_config_java_packages",
+        "min_sdk_version",
         "proguard_configs",
         "proguard_enabled",
+        "shared_resources",
+        "srcjar_deps",
+        "target_sdk_version",
         "use_default_launcher",
-        "write_asset_list",
         "use_native_activity",
       ]
+
+      # Adds the unwind tables from unstripped binary as an asset file in the
+      # apk, if |add_unwind_tables_in_apk| is specified by the test.
+      if (defined(invoker.add_unwind_tables_in_apk) &&
+          invoker.add_unwind_tables_in_apk) {
+        _unwind_table_asset_name = "${target_name}_unwind_assets"
+        unwind_table_asset(_unwind_table_asset_name) {
+          testonly = true
+          library_target = _library_target
+          deps = [ ":$_library_target" ]
+        }
+      }
+
       shared_library(_library_target) {
         # Configs will always be defined since we set_defaults in BUILDCONFIG.gn.
         configs = []  # Prevent list overwriting warning.
@@ -106,7 +119,7 @@
         }
       }
       unittest_apk(_apk_target) {
-        forward_variables_from(invoker, _apk_specific_vars + [ "deps" ])
+        forward_variables_from(invoker, _apk_specific_vars)
         shared_library = ":$_library_target"
         apk_name = invoker.target_name
         if (defined(invoker.output_name)) {
@@ -114,74 +127,59 @@
           install_script_name = "install_${invoker.output_name}"
         }
 
-        # TODO(agrieve): Remove this data_dep once bots don't build the _apk
-        #     target (post-GYP).
-        # It's a bit backwards for the apk to depend on the runner script, since
-        # the apk is conceptually a runtime_dep of the script. However, it is
-        # currently necessary because the bots build this _apk target directly
-        # rather than the group() below.
-        data_deps = [
-          ":$_test_runner_target",
-        ]
-      }
+        if (defined(invoker.deps)) {
+          deps = invoker.deps
+        } else {
+          deps = []
+        }
 
-      # Incremental test targets work only for .apks.
-      _incremental_test_runner_target =
-          "${_output_name}_incremental__test_runner_script"
-      test_runner_script(_incremental_test_runner_target) {
-        forward_variables_from(invoker,
-                               _wrapper_script_vars + [
-                                     "data",
-                                     "data_deps",
-                                     "deps",
-                                     "public_deps",
-                                   ])
-        apk_target = ":$_apk_target"
-        test_name = "${_output_name}_incremental"
-        test_type = "gtest"
-        test_suite = _output_name
-        incremental_install = true
-      }
-      group("${target_name}_incremental") {
-        testonly = true
-        datadeps = [
-          ":$_incremental_test_runner_target",
-        ]
-        deps = [
-          ":${_apk_target}_incremental",
-        ]
+        # Add the Java classes so that each target does not have to do it.
+        deps += [ "//base/test:test_support_java" ]
+
+        if (defined(_unwind_table_asset_name)) {
+          deps += [ ":${_unwind_table_asset_name}" ]
+        }
       }
     }
 
-    _test_runner_target = "${_output_name}__test_runner_script"
     test_runner_script(_test_runner_target) {
-      forward_variables_from(invoker,
-                             _wrapper_script_vars + [
-                                   "data",
-                                   "data_deps",
-                                   "deps",
-                                   "public_deps",
-                                 ])
+      forward_variables_from(invoker, _wrapper_script_vars)
 
       if (_use_raw_android_executable) {
         executable_dist_dir = "$root_out_dir/$_dist_target"
+        data_deps = [ ":$_exec_target" ]
       } else {
         apk_target = ":$_apk_target"
+        incremental_apk = incremental_install
+
+        # Dep needed for the test runner .runtime_deps file to pick up data
+        # deps from the forward_variables_from(invoker, "*") on the library.
+        data_deps = [ ":$_library_target" ]
       }
       test_name = _output_name
-      test_type = "gtest"
       test_suite = _output_name
+      test_type = "gtest"
     }
 
-    group(target_name) {
+    # Create a wrapper script rather than using a group() in order to ensure
+    # "ninja $target_name" always works. If this was a group(), then GN would
+    # not create a top-level alias for it if a target exists in another
+    # directory with the same $target_name.
+    # Also - bots run this script directly for "components_perftests".
+    generate_wrapper(target_name) {
       testonly = true
-      deps = [
-        ":$_test_runner_target",
-      ]
+      executable = "$root_build_dir/bin/run_$_output_name"
+      wrapper_script = "$root_build_dir/$_output_name"
+      deps = [ ":$_test_runner_target" ]
       if (_use_raw_android_executable) {
         deps += [ ":$_dist_target" ]
       } else {
-        deps += [ ":$_apk_target" ]
+        # Dep needed for the swarming .isolate file to pick up data
+        # deps from the forward_variables_from(invoker, "*") on the library.
+        deps += [
+          ":$_apk_target",
+          ":$_library_target",
+        ]
       }
     }
   } else if (is_ios) {
@@ -192,19 +190,14 @@
 
     bundle_data(_resources_bundle_data) {
       visibility = [ ":$_test_target" ]
-      sources = [
-        "//testing/gtest_ios/Default.png",
-      ]
-      outputs = [
-        "{{bundle_resources_dir}}/{{source_file_part}}",
-      ]
+      sources = [ "//testing/gtest_ios/Default.png" ]
+      outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ]
     }
 
     ios_app_bundle(_test_target) {
       testonly = true
 
       # See above call.
-      set_sources_assignment_filter([])
       forward_variables_from(invoker, "*", [ "testonly" ])
 
       # Provide sensible defaults in case invoker did not define any of those
@@ -244,9 +237,7 @@
     if (defined(invoker.output_name) && target_name != invoker.output_name) {
       group("${invoker.output_name}_run") {
         testonly = true
-        deps = [
-          ":${invoker.target_name}",
-        ]
+        deps = [ ":${invoker.target_name}" ]
       }
     }
   }
@@ -256,6 +247,8 @@
 set_defaults("test") {
   if (is_android) {
     configs = default_shared_library_configs
+    configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
+    configs += [ "//build/config/android:hide_all_but_jni" ]
   } else {
     configs = default_executable_configs
   }
@@ -271,9 +264,7 @@
     if (defined(invoker.configs)) {
       configs += invoker.configs
     }
-    deps = [
-      _pdfium_root_dir + ":pdfium_unittest_deps",
-    ]
+    deps = [ _pdfium_root_dir + ":pdfium_unittest_deps" ]
     if (defined(invoker.deps)) {
       deps += invoker.deps
     }
@@ -292,9 +283,7 @@
     if (defined(invoker.configs)) {
       configs += invoker.configs
     }
-    deps = [
-      _pdfium_root_dir + ":pdfium_embeddertest_deps",
-    ]
+    deps = [ _pdfium_root_dir + ":pdfium_embeddertest_deps" ]
     if (defined(invoker.deps)) {
       deps += invoker.deps
     }
diff --git a/testing/test_fonts.cpp b/testing/test_fonts.cpp
new file mode 100644
index 0000000..e3b6559
--- /dev/null
+++ b/testing/test_fonts.cpp
@@ -0,0 +1,116 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/test_fonts.h"
+
+#include <set>
+#include <utility>
+
+#include "core/fxge/cfx_fontmapper.h"
+#include "core/fxge/cfx_fontmgr.h"
+#include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/systemfontinfo_iface.h"
+#include "testing/utils/path_service.h"
+
+namespace {
+
+ByteString RenameFontForTesting(const ByteString& face) {
+  ByteString result;
+  if (face.Contains("Arial") || face.Contains("Calibri") ||
+      face.Contains("Helvetica")) {
+    // Sans
+    result = "Arimo";
+  } else if (face.IsEmpty() || face.Contains("Times")) {
+    // Serif
+    result = "Tinos";
+  } else if (face.Contains("Courier")) {
+    // Mono
+    result = "Cousine";
+  } else {
+    // Some tests expect the fallback font.
+    return face;
+  }
+
+  if (face.Contains("Bold"))
+    result += " Bold";
+
+  if (face.Contains("Italic") || face.Contains("Oblique"))
+    result += " Italic";
+
+  return result;
+}
+
+// Intercepts font requests and renames font faces to those in test_fonts.
+class SystemFontInfoWrapper : public SystemFontInfoIface {
+ public:
+  explicit SystemFontInfoWrapper(std::unique_ptr<SystemFontInfoIface> impl)
+      : impl_(std::move(impl)) {}
+  ~SystemFontInfoWrapper() { CHECK(active_fonts_.empty()); }
+
+  bool EnumFontList(CFX_FontMapper* pMapper) override {
+    return impl_->EnumFontList(pMapper);
+  }
+  void* MapFont(int weight,
+                bool bItalic,
+                FX_Charset charset,
+                int pitch_family,
+                const ByteString& face) override {
+    void* font = impl_->MapFont(weight, bItalic, charset, pitch_family,
+                                RenameFontForTesting(face));
+    if (font) {
+      bool inserted = active_fonts_.insert(font).second;
+      CHECK(inserted);
+    }
+    return font;
+  }
+  void* GetFont(const ByteString& face) override {
+    return impl_->GetFont(RenameFontForTesting(face));
+  }
+  size_t GetFontData(void* hFont,
+                     uint32_t table,
+                     pdfium::span<uint8_t> buffer) override {
+    return impl_->GetFontData(hFont, table, buffer);
+  }
+  bool GetFaceName(void* hFont, ByteString* name) override {
+    auto face = RenameFontForTesting(*name);
+    return impl_->GetFaceName(hFont, &face);
+  }
+  bool GetFontCharset(void* hFont, FX_Charset* charset) override {
+    return impl_->GetFontCharset(hFont, charset);
+  }
+  void DeleteFont(void* hFont) override {
+    CHECK(active_fonts_.erase(hFont));
+    impl_->DeleteFont(hFont);
+  }
+
+ private:
+  std::unique_ptr<SystemFontInfoIface> impl_;
+  std::set<void*> active_fonts_;
+};
+
+}  // namespace
+
+TestFonts::TestFonts() {
+  if (!PathService::GetExecutableDir(&font_path_))
+    return;
+  font_path_.push_back(PATH_SEPARATOR);
+  font_path_.append("test_fonts");
+  font_paths_ = std::make_unique<const char*[]>(2);
+  font_paths_[0] = font_path_.c_str();
+  font_paths_[1] = nullptr;
+}
+
+TestFonts::~TestFonts() = default;
+
+void TestFonts::InstallFontMapper() {
+  auto* font_mapper = CFX_GEModule::Get()->GetFontMgr()->GetBuiltinMapper();
+  font_mapper->SetSystemFontInfo(std::make_unique<SystemFontInfoWrapper>(
+      font_mapper->TakeSystemFontInfo()));
+}
+
+// static
+std::string TestFonts::RenameFont(const char* face) {
+  ByteString renamed_face = RenameFontForTesting(face);
+  return std::string(renamed_face.c_str());
+}
diff --git a/testing/test_fonts.h b/testing/test_fonts.h
new file mode 100644
index 0000000..4e2a822
--- /dev/null
+++ b/testing/test_fonts.h
@@ -0,0 +1,27 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_TEST_FONTS_H_
+#define TESTING_TEST_FONTS_H_
+
+#include <memory>
+#include <string>
+
+class TestFonts {
+ public:
+  TestFonts();
+  ~TestFonts();
+
+  const char** font_paths() const { return font_paths_.get(); }
+
+  void InstallFontMapper();
+
+  static std::string RenameFont(const char* face);
+
+ private:
+  std::string font_path_;
+  std::unique_ptr<const char*[]> font_paths_;
+};
+
+#endif  // TESTING_TEST_FONTS_H_
diff --git a/testing/test_loader.cpp b/testing/test_loader.cpp
index 33ee331..a3fad8a 100644
--- a/testing/test_loader.cpp
+++ b/testing/test_loader.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,8 @@
 
 #include <string.h>
 
-#include "third_party/base/logging.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 17ca9e9..d0e83c5 100644
--- a/testing/test_loader.h
+++ b/testing/test_loader.h
@@ -1,11 +1,11 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #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/test_support.cpp b/testing/test_support.cpp
deleted file mode 100644
index 94ce528..0000000
--- a/testing/test_support.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "testing/test_support.h"
-
-#include "core/fxge/cfx_gemodule.h"
-
-void InitializePDFTestEnvironment() {
-  CFX_GEModule::Create(nullptr);
-}
-
-void DestroyPDFTestEnvironment() {
-  CFX_GEModule::Destroy();
-}
diff --git a/testing/test_support.h b/testing/test_support.h
index f237ea5..1faf1a8 100644
--- a/testing/test_support.h
+++ b/testing/test_support.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -46,7 +46,4 @@
 
 }  // namespace pdfium
 
-void InitializePDFTestEnvironment();
-void DestroyPDFTestEnvironment();
-
 #endif  // TESTING_TEST_SUPPORT_H_
diff --git a/testing/tools/.style.yapf b/testing/tools/.style.yapf
deleted file mode 100644
index de0c6a7..0000000
--- a/testing/tools/.style.yapf
+++ /dev/null
@@ -1,2 +0,0 @@
-[style]
-based_on_style = chromium
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/PRESUBMIT.py b/testing/tools/PRESUBMIT.py
index 59a3858..0b89656 100644
--- a/testing/tools/PRESUBMIT.py
+++ b/testing/tools/PRESUBMIT.py
@@ -1,16 +1,20 @@
-# Copyright 2019 The PDFium Authors. All rights reserved.
+# Copyright 2019 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
+
 """Presubmit script for PDFium testing tools.
 
 See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
 for more details on the presubmit API built into depot_tools.
 """
 
+USE_PYTHON3 = True
+
 
 def _CommonChecks(input_api, output_api):
   tests = []
-  tests.extend(input_api.canned_checks.GetPylint(input_api, output_api))
+  tests.extend(
+      input_api.canned_checks.GetPylint(input_api, output_api, version='2.7'))
   return tests
 
 
diff --git a/testing/tools/api_check.py b/testing/tools/api_check.py
index 66b3077..bea8845 100755
--- a/testing/tools/api_check.py
+++ b/testing/tools/api_check.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2017 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2017 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Verifies exported functions in public/*.h are in fpdf_view_c_api_test.c.
@@ -99,16 +99,16 @@
 
 
 def _FindDuplicates(functions):
-  return set([f for f in functions if functions.count(f) > 1])
+  return {f for f in functions if functions.count(f) > 1}
 
 
 def _CheckAndPrintFailures(failure_list, failure_message):
   if not failure_list:
     return True
 
-  print '%s:' % failure_message
+  print('%s:' % failure_message)
   for f in sorted(failure_list):
-    print f
+    print(f)
   return False
 
 
diff --git a/testing/tools/common.py b/testing/tools/common.py
index 108fcfd..80b7815 100755
--- a/testing/tools/common.py
+++ b/testing/tools/common.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2015 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2015 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -10,6 +10,8 @@
 import subprocess
 import sys
 
+import pdfium_root
+
 
 def os_name():
   if sys.platform.startswith('linux'):
@@ -21,14 +23,6 @@
   raise Exception('Confused, can not determine OS, aborting.')
 
 
-def RunCommand(cmd):
-  try:
-    subprocess.check_call(cmd)
-    return None
-  except subprocess.CalledProcessError as e:
-    return e
-
-
 def RunCommandPropagateErr(cmd,
                            stdout_has_errors=False,
                            exit_status_on_error=None):
@@ -61,50 +55,17 @@
   return output
 
 
-# RunCommandExtractHashedFiles returns a tuple: (raised_exception, hashed_files)
-# It runs the given command. If it fails it will return an exception and None.
-# If it succeeds it will return None and the list of processed files extracted
-# from the output of the command. It expects lines in this format:
-#    MD5:<path_to_image_file>:<md5_hash_in_hex>
-# The returned hashed_files is a list of (file_path, MD5-hash) pairs.
-def RunCommandExtractHashedFiles(cmd):
-  try:
-    output = subprocess.check_output(cmd, universal_newlines=True)
-    ret = []
-    for line in output.split('\n'):
-      line = line.strip()
-      if line.startswith("MD5:"):
-        ret.append([x.strip() for x in line.lstrip("MD5:").rsplit(":", 1)])
-    return None, ret
-  except subprocess.CalledProcessError as e:
-    return e, None
-
-
 class DirectoryFinder:
   '''A class for finding directories and paths under either a standalone
   checkout or a chromium checkout of PDFium.'''
 
   def __init__(self, build_location):
-    # |build_location| is typically "out/Debug" or "out/Release".
-    # Expect |my_dir| to be .../pdfium/testing/tools.
-    self.my_dir = os.path.dirname(os.path.realpath(__file__))
-    self.testing_dir = os.path.dirname(self.my_dir)
-    if (os.path.basename(self.my_dir) != 'tools' or
-        os.path.basename(self.testing_dir) != 'testing'):
-      raise Exception('Confused, can not find pdfium root directory, aborting.')
-    self.pdfium_dir = os.path.dirname(self.testing_dir)
-    # Find path to build directory.  This depends on whether this is a
-    # standalone build vs. a build as part of a chromium checkout. For
-    # standalone, we expect a path like .../pdfium/out/Debug, but for
-    # chromium, we expect a path like .../src/out/Debug two levels
-    # higher (to skip over the third_party/pdfium path component under
-    # which chromium sticks pdfium).
-    self.base_dir = self.pdfium_dir
-    one_up_dir = os.path.dirname(self.base_dir)
-    two_up_dir = os.path.dirname(one_up_dir)
-    if (os.path.basename(two_up_dir) == 'src' and
-        os.path.basename(one_up_dir) == 'third_party'):
-      self.base_dir = two_up_dir
+    # `build_location` is typically "out/Debug" or "out/Release".
+    root_finder = pdfium_root.RootDirectoryFinder()
+    self.testing_dir = os.path.join(root_finder.pdfium_root, 'testing')
+    self.my_dir = os.path.join(self.testing_dir, 'tools')
+    self.pdfium_dir = root_finder.pdfium_root
+    self.base_dir = root_finder.source_root
     self.build_dir = os.path.join(self.base_dir, build_location)
     self.os_name = os_name()
 
@@ -133,26 +94,31 @@
       result = os.path.join(result, other_components)
     return result
 
+  def ThirdPartyFontsDir(self):
+    '''Finds directory with the test fonts.'''
+    return os.path.join(self.base_dir, 'third_party', 'test_fonts')
+
 
 def GetBooleanGnArg(arg_name, build_dir, verbose=False):
   '''Extract the value of a boolean flag in args.gn'''
   cwd = os.getcwd()
   os.chdir(build_dir)
   gn_args_output = subprocess.check_output(
-      ['gn', 'args', '.', '--list=%s' % arg_name, '--short'])
+      ['gn', 'args', '.', '--list=%s' % arg_name, '--short']).decode('utf8')
   os.chdir(cwd)
   arg_match_output = re.search('%s = (.*)' % arg_name, gn_args_output).group(1)
   if verbose:
-    print >> sys.stderr, "Found '%s' for value of %s" % (arg_match_output,
-                                                         arg_name)
+    print(
+        "Found '%s' for value of %s" % (arg_match_output, arg_name),
+        file=sys.stderr)
   return arg_match_output == 'true'
 
 
 def PrintWithTime(s):
   """Prints s prepended by a timestamp."""
-  print '[%s] %s' % (datetime.datetime.now().strftime("%Y%m%d %H:%M:%S"), s)
+  print('[%s] %s' % (datetime.datetime.now().strftime("%Y%m%d %H:%M:%S"), s))
 
 
 def PrintErr(s):
   """Prints s to stderr."""
-  print >> sys.stderr, s
+  print(s, file=sys.stderr)
diff --git a/testing/tools/coverage/coverage_report.py b/testing/tools/coverage/coverage_report.py
index 5785eab..2c57946 100755
--- a/testing/tools/coverage/coverage_report.py
+++ b/testing/tools/coverage/coverage_report.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2017 The PDFium Authors. All rights reserved.
+#!/usr/bin/env vpython3
+# Copyright 2017 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Generates a coverage report for given tests.
@@ -16,14 +16,11 @@
 import subprocess
 import sys
 
-# Add src dir to path to avoid having to set PYTHONPATH.
+# Add parent dir to avoid having to set PYTHONPATH.
 sys.path.append(
-    os.path.abspath(
-        os.path.join(
-            os.path.dirname(__file__), os.path.pardir, os.path.pardir,
-            os.path.pardir)))
+    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
 
-from testing.tools.common import GetBooleanGnArg
+import common
 
 # 'binary' is the file that is to be run for the test.
 # 'use_test_runner' indicates if 'binary' depends on test_runner.py and thus
@@ -41,18 +38,32 @@
         TestSpec('run_corpus_tests.py', True, []),
     'corpus_tests_javascript_disabled':
         TestSpec('run_corpus_tests.py', True, ['--disable-javascript']),
+    'corpus_tests_xfa_disabled':
+        TestSpec('run_corpus_tests.py', True, ['--disable-xfa']),
+    'corpus_tests_render_oneshot':
+        TestSpec('run_corpus_tests.py', True, ['--render-oneshot']),
+    'corpus_tests_reverse_byte_order':
+        TestSpec('run_corpus_tests.py', True, ['--reverse-byte-order']),
     'javascript_tests':
         TestSpec('run_javascript_tests.py', True, []),
     'javascript_tests_javascript_disabled':
         TestSpec('run_javascript_tests.py', True, ['--disable-javascript']),
+    'javascript_tests_xfa_disabled':
+        TestSpec('run_javascript_tests.py', True, ['--disable-xfa']),
     'pixel_tests':
         TestSpec('run_pixel_tests.py', True, []),
     'pixel_tests_javascript_disabled':
         TestSpec('run_pixel_tests.py', True, ['--disable-javascript']),
+    'pixel_tests_xfa_disabled':
+        TestSpec('run_pixel_tests.py', True, ['--disable-xfa']),
+    'pixel_tests_render_oneshot':
+        TestSpec('run_pixel_tests.py', True, ['--render-oneshot']),
+    'pixel_tests_reverse_byte_order':
+        TestSpec('run_pixel_tests.py', True, ['--reverse-byte-order']),
 }
 
 
-class CoverageExecutor(object):
+class CoverageExecutor:
 
   def __init__(self, parser, args):
     """Initialize executor based on the current script environment
@@ -85,14 +96,14 @@
           'No valid tests in set to be run. This is likely due to bad command '
           'line arguments')
 
-    if not GetBooleanGnArg('use_clang_coverage', self.build_directory,
-                           self.verbose):
+    if not common.GetBooleanGnArg('use_clang_coverage', self.build_directory,
+                                  self.verbose):
       parser.error(
           'use_clang_coverage does not appear to be set to true for build, but '
           'is needed')
 
-    self.use_goma = GetBooleanGnArg('use_goma', self.build_directory,
-                                    self.verbose)
+    self.use_goma = common.GetBooleanGnArg('use_goma', self.build_directory,
+                                           self.verbose)
 
     self.output_directory = args['output_directory']
     if not os.path.exists(self.output_directory):
@@ -109,38 +120,38 @@
   def check_output(self, args, dry_run=False, env=None):
     """Dry run aware wrapper of subprocess.check_output()"""
     if dry_run:
-      print "Would have run '%s'" % ' '.join(args)
+      print("Would have run '%s'" % ' '.join(args))
       return ''
 
     output = subprocess.check_output(args, env=env)
 
     if self.verbose:
-      print "check_output(%s) returned '%s'" % (args, output)
+      print("check_output(%s) returned '%s'" % (args, output))
     return output
 
   def call(self, args, dry_run=False, env=None):
     """Dry run aware wrapper of subprocess.call()"""
     if dry_run:
-      print "Would have run '%s'" % ' '.join(args)
+      print("Would have run '%s'" % ' '.join(args))
       return 0
 
     output = subprocess.call(args, env=env)
 
     if self.verbose:
-      print 'call(%s) returned %s' % (args, output)
+      print('call(%s) returned %s' % (args, output))
     return output
 
   def call_silent(self, args, dry_run=False, env=None):
     """Dry run aware wrapper of subprocess.call() that eats output from call"""
     if dry_run:
-      print "Would have run '%s'" % ' '.join(args)
+      print("Would have run '%s'" % ' '.join(args))
       return 0
 
     with open(os.devnull, 'w') as f:
       output = subprocess.call(args, env=env, stdout=f)
 
     if self.verbose:
-      print 'call_silent(%s) returned %s' % (args, output)
+      print('call_silent(%s) returned %s' % (args, output))
     return output
 
   def calculate_coverage_tests(self, args):
@@ -186,10 +197,10 @@
         spec: Tuple containing the TestSpec.
     """
     if self.verbose:
-      print "Generating coverage for test '%s', using data '%s'" % (name, spec)
+      print("Generating coverage for test '%s', using data '%s'" % (name, spec))
     if not os.path.exists(spec.binary):
       print('Unable to generate coverage for %s, since it appears to not exist'
-            ' @ %s') % (name, spec.binary)
+            ' @ %s' % (name, spec.binary))
       return False
 
     binary_args = [spec.binary]
@@ -212,7 +223,7 @@
       binary_args.extend(['-j', '8', '--build-dir', self.build_directory])
     if self.call(binary_args, dry_run=self.dry_run, env=env) and self.verbose:
       print('Running %s appears to have failed, which might affect '
-            'results') % spec.binary
+            'results' % spec.binary)
 
     return True
 
@@ -241,7 +252,7 @@
     coverage_args += ['-o', report_directory]
     coverage_args += self.build_targets
 
-    # Whitelist the directories of interest
+    # Only analyze the directories of interest.
     coverage_args += ['-f', 'core']
     coverage_args += ['-f', 'fpdfsdk']
     coverage_args += ['-f', 'fxbarcode']
@@ -250,7 +261,7 @@
     coverage_args += ['-f', 'samples']
     coverage_args += ['-f', 'xfa']
 
-    # Blacklist test files
+    # Ignore test files.
     coverage_args += ['-i', '.*test.*']
 
     # Component view is only useful for Chromium
@@ -261,24 +272,24 @@
   def run(self):
     """Setup environment, execute the tests and generate coverage report"""
     if not self.fetch_profiling_tools():
-      print 'Unable to fetch profiling tools'
+      print('Unable to fetch profiling tools')
       return False
 
     if not self.build_binaries():
-      print 'Failed to successfully build binaries'
+      print('Failed to successfully build binaries')
       return False
 
-    for name in self.coverage_tests.keys():
+    for name in self.coverage_tests:
       if not self.generate_coverage(name, self.coverage_tests[name]):
-        print 'Failed to successfully generate coverage data'
+        print('Failed to successfully generate coverage data')
         return False
 
     if not self.merge_raw_coverage_results():
-      print 'Failed to successfully merge raw coverage results'
+      print('Failed to successfully merge raw coverage results')
       return False
 
     if not self.generate_html_report():
-      print 'Failed to successfully generate HTML report'
+      print('Failed to successfully generate HTML report')
       return False
 
     return True
diff --git a/testing/tools/encode_pdf_filter.py b/testing/tools/encode_pdf_filter.py
index 2d56543..1985fb7 100755
--- a/testing/tools/encode_pdf_filter.py
+++ b/testing/tools/encode_pdf_filter.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python3
-# Copyright 2019 The PDFium Authors. All rights reserved.
+# Copyright 2019 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Encodes binary data using one or more PDF stream filters.
@@ -13,6 +13,7 @@
 """
 
 import argparse
+from abc import ABCMeta, abstractmethod
 import base64
 import collections
 import collections.abc
@@ -21,10 +22,22 @@
 import zlib
 
 
-class _PdfStream:
+class _PdfStream(metaclass=ABCMeta):
   _unique_filter_classes = []
   _filter_classes = {}
 
+  @classmethod
+  @property
+  @abstractmethod
+  def name(cls):
+    pass
+
+  @classmethod
+  @property
+  @abstractmethod
+  def aliases(cls):
+    pass
+
   @staticmethod
   def GetFilterByName(name):
     # Tolerate any case-insensitive match for "/Name" or "Name", or an alias.
@@ -38,18 +51,24 @@
 
     return filter_class
 
-  @staticmethod
-  def RegisterFilter(filter_class):
-    assert filter_class not in _PdfStream._unique_filter_classes
-    _PdfStream._unique_filter_classes.append(filter_class)
+  @classmethod
+  def Register(cls):
+    assert cls not in _PdfStream._unique_filter_classes
+    _PdfStream._unique_filter_classes.append(cls)
+    cls.RegisterByName()
+    cls.RegisterByAliases()
 
-    assert filter_class.name[0] == '/'
-    lower_name = filter_class.name.lower()
-    _PdfStream._filter_classes[lower_name] = filter_class
-    _PdfStream._filter_classes[lower_name[1:]] = filter_class
+  @classmethod
+  def RegisterByName(cls):
+    assert cls.name[0] == '/'
+    lower_name = cls.name.lower()
+    _PdfStream._filter_classes[lower_name] = cls
+    _PdfStream._filter_classes[lower_name[1:]] = cls
 
-    for alias in filter_class.aliases:
-      _PdfStream._filter_classes[alias.lower()] = filter_class
+  @classmethod
+  def RegisterByAliases(cls):
+    for alias in cls.aliases:
+      _PdfStream._filter_classes[alias.lower()] = cls
 
   @staticmethod
   def GetHelp():
@@ -59,6 +78,21 @@
                                             ', '.join(filter_class.aliases))
     return text
 
+  @classmethod
+  def AddEntries(cls, entries):
+    _PdfStream.AddListEntry(entries, 'Filter', cls.name)
+
+  @staticmethod
+  def AddListEntry(entries, key, value):
+    old_value = entries.get(key)
+    if old_value is None:
+      entries[key] = value
+    else:
+      if not isinstance(old_value, collections.abc.MutableSequence):
+        old_value = [old_value]
+        entries[key] = old_value
+      old_value.append(value)
+
   def __init__(self, out_buffer, **kwargs):
     del kwargs
     self.buffer = out_buffer
@@ -78,6 +112,22 @@
   def __init__(self):
     super().__init__(io.BytesIO())
 
+  @classmethod
+  @property
+  def name(cls):
+    # Return an invalid name, so as to ensure _SinkPdfStream.Register()
+    # cannot be called. This method has to be implemented, because this
+    # script create `_SinkPdfStream` instances.
+    return ''
+
+  @classmethod
+  @property
+  def aliases(cls):
+    # Return an invalid aliases, so as to ensure _SinkPdfStream.Register()
+    # cannot be called. This method has to be implemented, because this
+    # script create `_SinkPdfStream` instances.
+    return ()
+
   def close(self):
     # Don't call io.BytesIO.close(); this deallocates the written data.
     self.flush()
@@ -93,6 +143,18 @@
     self.wrapcol = wrapcol
     self.column = 0
 
+  @classmethod
+  @property
+  @abstractmethod
+  def name(cls):
+    pass
+
+  @classmethod
+  @property
+  @abstractmethod
+  def aliases(cls):
+    pass
+
   def write(self, data):
     if not self.wrapcol:
       self.buffer.write(data)
@@ -113,8 +175,18 @@
 
 
 class _Ascii85DecodePdfStream(_AsciiPdfStream):
-  name = '/ASCII85Decode'
-  aliases = ('ascii85', 'base85')
+  _name = '/ASCII85Decode'
+  _aliases = ('ascii85', 'base85')
+
+  @classmethod
+  @property
+  def name(cls):
+    return cls._name
+
+  @classmethod
+  @property
+  def aliases(cls):
+    return cls._aliases
 
   def __init__(self, out_buffer, **kwargs):
     super().__init__(out_buffer, **kwargs)
@@ -137,8 +209,18 @@
 
 
 class _AsciiHexDecodePdfStream(_AsciiPdfStream):
-  name = '/ASCIIHexDecode'
-  aliases = ('base16', 'hex', 'hexadecimal')
+  _name = '/ASCIIHexDecode'
+  _aliases = ('base16', 'hex', 'hexadecimal')
+
+  @classmethod
+  @property
+  def name(cls):
+    return cls._name
+
+  @classmethod
+  @property
+  def aliases(cls):
+    return cls._aliases
 
   def __init__(self, out_buffer, **kwargs):
     super().__init__(out_buffer, **kwargs)
@@ -148,13 +230,23 @@
 
 
 class _FlateDecodePdfStream(_PdfStream):
-  name = '/FlateDecode'
-  aliases = ('deflate', 'flate', 'zlib')
+  _name = '/FlateDecode'
+  _aliases = ('deflate', 'flate', 'zlib')
 
   def __init__(self, out_buffer, **kwargs):
     super().__init__(out_buffer, **kwargs)
     self.deflate = zlib.compressobj(level=9, memLevel=9)
 
+  @classmethod
+  @property
+  def name(cls):
+    return cls._name
+
+  @classmethod
+  @property
+  def aliases(cls):
+    return cls._aliases
+
   def write(self, data):
     self.buffer.write(self.deflate.compress(data))
 
@@ -166,9 +258,126 @@
     self.buffer.close()
 
 
-_PdfStream.RegisterFilter(_Ascii85DecodePdfStream)
-_PdfStream.RegisterFilter(_AsciiHexDecodePdfStream)
-_PdfStream.RegisterFilter(_FlateDecodePdfStream)
+class _VirtualPdfStream(_PdfStream):
+
+  @classmethod
+  @property
+  @abstractmethod
+  def name(cls):
+    pass
+
+  @classmethod
+  @property
+  @abstractmethod
+  def aliases(cls):
+    pass
+
+  @classmethod
+  def RegisterByName(cls):
+    pass
+
+  @classmethod
+  def AddEntries(cls, entries):
+    pass
+
+
+class _PassthroughPdfStream(_VirtualPdfStream):
+  _name = '(virtual) passthrough'
+  _aliases = ('noop', 'passthrough')
+
+  @classmethod
+  @property
+  def name(cls):
+    return cls._name
+
+  @classmethod
+  @property
+  def aliases(cls):
+    return cls._aliases
+
+
+class _PngIdatPdfStream(_VirtualPdfStream):
+  _name = '(virtual) PNG IDAT'
+  _aliases = ('png',)
+
+  _EXPECT_HEADER = -1
+  _EXPECT_LENGTH = -2
+  _EXPECT_CHUNK_TYPE = -3
+  _EXPECT_CRC = -4
+
+  _PNG_HEADER = 0x89504E470D0A1A0A
+  _PNG_CHUNK_IDAT = 0x49444154
+
+  @classmethod
+  @property
+  def name(cls):
+    return cls._name
+
+  @classmethod
+  @property
+  def aliases(cls):
+    return cls._aliases
+
+  @classmethod
+  def AddEntries(cls, entries):
+    # Technically only true for compression method 0 (zlib), but no other
+    # methods have been standardized.
+    _PdfStream.AddListEntry(entries, 'Filter', '/FlateDecode')
+
+  def __init__(self, out_buffer, **kwargs):
+    super().__init__(out_buffer, **kwargs)
+    self.chunk = _PngIdatPdfStream._EXPECT_HEADER
+    self.remaining = 8
+    self.accumulator = 0
+    self.length = 0
+
+  def write(self, data):
+    position = 0
+    while position < len(data):
+      if self.chunk >= 0:
+        # Only pass through IDAT chunk data.
+        read_size = min(self.remaining, len(data) - position)
+        if self.chunk == _PngIdatPdfStream._PNG_CHUNK_IDAT:
+          self.buffer.write(data[position:position + read_size])
+        self.remaining -= read_size
+        if self.remaining == 0:
+          self.ResetAccumulator(_PngIdatPdfStream._EXPECT_CRC, 4)
+        position += read_size
+      else:
+        # As far as we're concerned, PNG files are just a header followed by a
+        # series of (length, chunk type, data[length], CRC) chunks.
+        if self.AccumulateByte(data[position]):
+          if self.chunk == _PngIdatPdfStream._EXPECT_HEADER:
+            if self.accumulator != _PngIdatPdfStream._PNG_HEADER:
+              raise ValueError('Invalid PNG header', self.accumulator)
+            self.ResetAccumulator(_PngIdatPdfStream._EXPECT_LENGTH, 4)
+          elif self.chunk == _PngIdatPdfStream._EXPECT_LENGTH:
+            self.length = self.accumulator
+            self.ResetAccumulator(_PngIdatPdfStream._EXPECT_CHUNK_TYPE, 4)
+          elif self.chunk == _PngIdatPdfStream._EXPECT_CHUNK_TYPE:
+            self.ResetAccumulator(self.accumulator, self.length)
+          elif self.chunk == _PngIdatPdfStream._EXPECT_CRC:
+            # Don't care if the CRC is correct.
+            self.ResetAccumulator(_PngIdatPdfStream._EXPECT_LENGTH, 4)
+        position += 1
+
+  def ResetAccumulator(self, chunk, remaining):
+    self.chunk = chunk
+    self.remaining = remaining
+    self.accumulator = 0
+
+  def AccumulateByte(self, byte):
+    assert self.remaining > 0
+    self.accumulator = self.accumulator << 8 | byte
+    self.remaining -= 1
+    return self.remaining == 0
+
+
+_Ascii85DecodePdfStream.Register()
+_AsciiHexDecodePdfStream.Register()
+_FlateDecodePdfStream.Register()
+_PassthroughPdfStream.Register()
+_PngIdatPdfStream.Register()
 
 _DEFAULT_FILTERS = (_Ascii85DecodePdfStream, _FlateDecodePdfStream)
 
@@ -261,7 +470,7 @@
 
 
 def _EncodePdfValue(value):
-  if isinstance(value, collections.abc.Sequence):
+  if isinstance(value, collections.abc.MutableSequence):
     value = '[' + ' '.join(value) + ']'
   return value
 
@@ -276,7 +485,8 @@
     out_buffer.close()
 
   entries = collections.OrderedDict()
-  entries['Filter'] = [f.name for f in args.filter]
+  for f in args.filter:
+    f.AddEntries(entries)
   _WritePdfStreamObject(
       args.outfile.buffer,
       data=encoded_sink.getbuffer(),
diff --git a/testing/tools/fixup_pdf_template.py b/testing/tools/fixup_pdf_template.py
index ee47c4b..da2d608 100755
--- a/testing/tools/fixup_pdf_template.py
+++ b/testing/tools/fixup_pdf_template.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2014 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2014 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Expands a hand-written PDF testcase (template) into a valid PDF file.
@@ -11,17 +11,32 @@
   {{header}} - expands to the header comment required for PDF files.
   {{xref}} - expands to a generated xref table, noting the offset.
   {{trailer}} - expands to a standard trailer with "1 0 R" as the /Root.
+  {{trailersize}} - expands to `/Size n`, to be used in non-standard trailers.
   {{startxref} - expands to a startxref directive followed by correct offset.
-  {{object x y}} - expands to |x y obj| declaration, noting the offset.
-  {{streamlen}} - expands to |/Length n|.
+  {{startxrefobj x y} - expands to a startxref directive followed by correct
+                        offset pointing to the start of `x y obj`.
+  {{object x y}} - expands to `x y obj` declaration, noting the offset.
+  {{streamlen}} - expands to `/Length n`.
 """
 
-import cStringIO
+import io
 import optparse
 import os
 import re
 import sys
 
+# Line Endings.
+WINDOWS_LINE_ENDING = b'\r\n'
+UNIX_LINE_ENDING = b'\n'
+
+# List of extensions whose line endings should be modified after parsing.
+EXTENSION_OVERRIDE_LINE_ENDINGS = [
+    '.js',
+    '.fragment',
+    '.in',
+    '.xml',
+]
+
 
 class StreamLenState:
   START = 1
@@ -30,28 +45,33 @@
 
 
 class TemplateProcessor:
-  HEADER_TOKEN = '{{header}}'
-  HEADER_REPLACEMENT = '%PDF-1.7\n%\xa0\xf2\xa4\xf4'
+  HEADER_TOKEN = b'{{header}}'
+  HEADER_REPLACEMENT = b'%PDF-1.7\n%\xa0\xf2\xa4\xf4'
 
-  XREF_TOKEN = '{{xref}}'
-  XREF_REPLACEMENT = 'xref\n%d %d\n'
+  XREF_TOKEN = b'{{xref}}'
+  XREF_REPLACEMENT = b'xref\n%d %d\n'
 
-  XREF_REPLACEMENT_N = '%010d %05d n \n'
-  XREF_REPLACEMENT_F = '0000000000 65535 f \n'
+  XREF_REPLACEMENT_N = b'%010d %05d n \n'
+  XREF_REPLACEMENT_F = b'0000000000 65535 f \n'
   # XREF rows must be exactly 20 bytes - space required.
   assert len(XREF_REPLACEMENT_F) == 20
 
-  TRAILER_TOKEN = '{{trailer}}'
-  TRAILER_REPLACEMENT = 'trailer <<\n  /Root 1 0 R\n  /Size %d\n>>'
+  TRAILER_TOKEN = b'{{trailer}}'
+  TRAILER_REPLACEMENT = b'trailer <<\n  /Root 1 0 R\n  /Size %d\n>>'
 
-  STARTXREF_TOKEN = '{{startxref}}'
-  STARTXREF_REPLACEMENT = 'startxref\n%d'
+  TRAILERSIZE_TOKEN = b'{{trailersize}}'
+  TRAILERSIZE_REPLACEMENT = b'/Size %d'
 
-  OBJECT_PATTERN = r'\{\{object\s+(\d+)\s+(\d+)\}\}'
-  OBJECT_REPLACEMENT = r'\1 \2 obj'
+  STARTXREF_TOKEN = b'{{startxref}}'
+  STARTXREF_REPLACEMENT = b'startxref\n%d'
 
-  STREAMLEN_TOKEN = '{{streamlen}}'
-  STREAMLEN_REPLACEMENT = '/Length %d'
+  STARTXREFOBJ_PATTERN = b'\{\{startxrefobj\s+(\d+)\s+(\d+)\}\}'
+
+  OBJECT_PATTERN = b'\{\{object\s+(\d+)\s+(\d+)\}\}'
+  OBJECT_REPLACEMENT = b'\g<1> \g<2> obj'
+
+  STREAMLEN_TOKEN = b'{{streamlen}}'
+  STREAMLEN_REPLACEMENT = b'/Length %d'
 
   def __init__(self):
     self.streamlen_state = StreamLenState.START
@@ -82,12 +102,12 @@
       return
 
     if (self.streamlen_state == StreamLenState.FIND_STREAM and
-        line.rstrip() == 'stream'):
+        line.rstrip() == b'stream'):
       self.streamlen_state = StreamLenState.FIND_ENDSTREAM
       return
 
     if self.streamlen_state == StreamLenState.FIND_ENDSTREAM:
-      if line.rstrip() == 'endstream':
+      if line.rstrip() == b'endstream':
         self.streamlen_state = StreamLenState.START
       else:
         self.streamlens[-1] += len(line)
@@ -104,6 +124,9 @@
     if self.TRAILER_TOKEN in line:
       replacement = self.TRAILER_REPLACEMENT % (self.max_object_number + 1)
       line = line.replace(self.TRAILER_TOKEN, replacement)
+    if self.TRAILERSIZE_TOKEN in line:
+      replacement = self.TRAILERSIZE_REPLACEMENT % (self.max_object_number + 1)
+      line = line.replace(self.TRAILERSIZE_TOKEN, replacement)
     if self.STARTXREF_TOKEN in line:
       replacement = self.STARTXREF_REPLACEMENT % self.xref_offset
       line = line.replace(self.STARTXREF_TOKEN, replacement)
@@ -111,6 +134,12 @@
     if match:
       self.insert_xref_entry(int(match.group(1)), int(match.group(2)))
       line = re.sub(self.OBJECT_PATTERN, self.OBJECT_REPLACEMENT, line)
+    match = re.match(self.STARTXREFOBJ_PATTERN, line)
+    if match:
+      (offset, generation_number) = self.objects[int(match.group(1))]
+      assert int(match.group(2)) == generation_number
+      replacement = self.STARTXREF_REPLACEMENT % offset
+      line = re.sub(self.STARTXREFOBJ_PATTERN, replacement, line)
     self.offset += len(line)
     return line
 
@@ -119,7 +148,7 @@
   processor = TemplateProcessor()
   try:
     with open(output_path, 'wb') as outfile:
-      preprocessed = cStringIO.StringIO()
+      preprocessed = io.BytesIO()
       for line in infile:
         preprocessed.write(line)
         processor.preprocess_line(line)
@@ -127,27 +156,43 @@
       for line in preprocessed:
         outfile.write(processor.process_line(line))
   except IOError:
-    print >> sys.stderr, 'failed to process %s' % input_path
+    print('failed to process %s' % input_path, file=sys.stderr)
 
 
 def insert_includes(input_path, output_file, visited_set):
   input_path = os.path.normpath(input_path)
   if input_path in visited_set:
-    print >> sys.stderr, 'Circular inclusion %s, ignoring' % input_path
+    print('Circular inclusion %s, ignoring' % input_path, file=sys.stderr)
     return
   visited_set.add(input_path)
   try:
+    _, file_extension = os.path.splitext(input_path)
+    override_line_endings = (file_extension in EXTENSION_OVERRIDE_LINE_ENDINGS)
+
+    end_of_file_line_ending = False
     with open(input_path, 'rb') as infile:
       for line in infile:
-        match = re.match(r'\s*\{\{include\s+(.+)\}\}', line)
+        match = re.match(b'\s*\{\{include\s+(.+)\}\}', line)
         if match:
           insert_includes(
-              os.path.join(os.path.dirname(input_path), match.group(1)),
-              output_file, visited_set)
+              os.path.join(
+                  os.path.dirname(input_path),
+                  match.group(1).decode('utf-8')), output_file, visited_set)
         else:
+          if override_line_endings:
+            # Replace CRLF with LF line endings for .in files.
+            if line.endswith(WINDOWS_LINE_ENDING):
+              line = line.removesuffix(WINDOWS_LINE_ENDING) + UNIX_LINE_ENDING
+              end_of_file_line_ending = True
+            else:
+              end_of_file_line_ending = line.endswith(UNIX_LINE_ENDING)
           output_file.write(line)
+
+    # Ensure the include ends on its own line.
+    if not end_of_file_line_ending:
+      output_file.write(UNIX_LINE_ENDING)
   except IOError:
-    print >> sys.stderr, 'failed to include %s' % input_path
+    print('failed to include %s' % input_path, file=sys.stderr)
     raise
   visited_set.discard(input_path)
 
@@ -162,7 +207,7 @@
     output_dir = os.path.dirname(testcase_path)
     if options.output_dir:
       output_dir = options.output_dir
-    intermediate_stream = cStringIO.StringIO()
+    intermediate_stream = io.BytesIO()
     insert_includes(testcase_path, intermediate_stream, set())
     intermediate_stream.seek(0)
     output_path = os.path.join(output_dir, testcase_root + '.pdf')
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/githelper.py b/testing/tools/githelper.py
index 91a6c71..19071b0 100644
--- a/testing/tools/githelper.py
+++ b/testing/tools/githelper.py
@@ -1,15 +1,14 @@
-# Copyright 2017 The PDFium Authors. All rights reserved.
+# Copyright 2017 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Classes for dealing with git."""
 
 import subprocess
 
-# pylint: disable=relative-import
 from common import RunCommandPropagateErr
 
 
-class GitHelper(object):
+class GitHelper:
   """Issues git commands. Stateful."""
 
   def __init__(self):
@@ -20,8 +19,8 @@
     RunCommandPropagateErr(['git', 'checkout', branch], exit_status_on_error=1)
 
   def FetchOriginMaster(self):
-    """Fetches new changes on origin/master."""
-    RunCommandPropagateErr(['git', 'fetch', 'origin', 'master'],
+    """Fetches new changes on origin/main."""
+    RunCommandPropagateErr(['git', 'fetch', 'origin', 'main'],
                            exit_status_on_error=1)
 
   def StashPush(self):
diff --git a/testing/tools/gold.py b/testing/tools/gold.py
deleted file mode 100644
index c686bba..0000000
--- a/testing/tools/gold.py
+++ /dev/null
@@ -1,284 +0,0 @@
-# Copyright 2015 The PDFium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import json
-import os
-import shlex
-import shutil
-import ssl
-import urllib2
-
-
-def _ParseKeyValuePairs(kv_str):
-  """
-  Parses a string of the type 'key1 value1 key2 value2' into a dict.
-  """
-  kv_pairs = shlex.split(kv_str)
-  if len(kv_pairs) % 2:
-    raise ValueError('Uneven number of key/value pairs. Got %s' % kv_str)
-  return {kv_pairs[i]: kv_pairs[i + 1] for i in xrange(0, len(kv_pairs), 2)}
-
-
-# This module downloads a json provided by Skia Gold with the expected baselines
-# for each test file.
-#
-# The expected format for the json is:
-# {
-#   "commit": {
-#     "author": "John Doe (jdoe@chromium.org)",
-#     "commit_time": 1510598123,
-#     "hash": "cee39e6e90c219cc91f2c94a912a06977f4461a0"
-#   },
-#   "master": {
-#     "abc.pdf.1": {
-#       "0ec3d86f545052acd7c9a16fde8ca9d4": 1,
-#       "80455b71673becc9fbc100d6da56ca65": 1,
-#       "b68e2ecb80090b4502ec89ad1be2322c": 1
-#      },
-#     "defgh.pdf.0": {
-#       "01e020cd4cd05c6738e479a46a506044": 1,
-#       "b68e2ecb80090b4502ec89ad1be2322c": 1
-#     }
-#   },
-#   "changeLists": {
-#     "18499" : {
-#       "abc.pdf.1": {
-#         "d5dd649124cf1779152253dc8fb239c5": 1,
-#         "42a270581930579cdb0f28674972fb1a": 1,
-#       }
-#     }
-#   }
-# }
-class GoldBaseline(object):
-
-  def __init__(self, properties_str):
-    """
-    properties_str is a string with space separated key/value pairs that
-               is used to find the cl number for which to baseline
-    """
-    self._properties = _ParseKeyValuePairs(properties_str)
-    self._baselines = self._LoadSkiaGoldBaselines()
-
-  def _LoadSkiaGoldBaselines(self):
-    """
-    Download the baseline json and return a list of the two baselines that
-    should be used to match hashes (master and cl#).
-    """
-    GOLD_BASELINE_URL = 'https://pdfium-gold.skia.org/json/baseline'
-
-    # If we have an issue number add it to the baseline URL
-    cl_number_str = self._properties.get('issue', None)
-    url = GOLD_BASELINE_URL + ('/' + cl_number_str if cl_number_str else '')
-
-    json_data = ''
-    MAX_TIMEOUT = 33  # 5 tries. (2, 4, 8, 16, 32)
-    timeout = 2
-    while True:
-      try:
-        response = urllib2.urlopen(url, timeout=timeout)
-        c_type = response.headers.get('Content-type', '')
-        EXPECTED_CONTENT_TYPE = 'application/json'
-        if c_type != EXPECTED_CONTENT_TYPE:
-          raise ValueError('Invalid content type. Got %s instead of %s' %
-                           (c_type, EXPECTED_CONTENT_TYPE))
-        json_data = response.read()
-        break  # If this line is reached, then no exception occurred.
-      except (ssl.SSLError, urllib2.HTTPError, urllib2.URLError) as e:
-        timeout *= 2
-        if timeout < MAX_TIMEOUT:
-          continue
-        print('Error: Unable to read skia gold json from %s: %s' % (url, e))
-        return None
-
-    try:
-      data = json.loads(json_data)
-    except ValueError as e:
-      print 'Error: Malformed json read from %s: %s' % (url, e)
-      return None
-
-    return data.get('master', {})
-
-  # Return values for MatchLocalResult().
-  MATCH = 'match'
-  MISMATCH = 'mismatch'
-  NO_BASELINE = 'no_baseline'
-  BASELINE_DOWNLOAD_FAILED = 'baseline_download_failed'
-
-  def MatchLocalResult(self, test_name, md5_hash):
-    """
-    Match a locally generated hash of a test cases rendered image with the
-    expected hashes downloaded in the baselines json.
-
-    Each baseline is a dict mapping the test case name to a dict with the
-    expected hashes as keys. Therefore, this list of baselines should be
-    searched until the test case name is found, then the hash should be matched
-    with the options in that dict. If the hashes don't match, it should be
-    considered a failure and we should not continue searching the baseline list.
-
-    Returns MATCH if the md5 provided matches the ones in the baseline json,
-    MISMATCH if it does not, NO_BASELINE if the test case has no baseline, or
-    BASELINE_DOWNLOAD_FAILED if the baseline could not be downloaded and parsed.
-    """
-    if self._baselines is None:
-      return GoldBaseline.BASELINE_DOWNLOAD_FAILED
-
-    found_test_case = False
-    if test_name in self._baselines:
-      found_test_case = True
-      if md5_hash in self._baselines[test_name]:
-        return GoldBaseline.MATCH
-
-    return (GoldBaseline.MISMATCH
-            if found_test_case else GoldBaseline.NO_BASELINE)
-
-
-# This module collects and writes output in a format expected by the
-# Gold baseline tool. Based on meta data provided explicitly and by
-# adding a series of test results it can be used to produce
-# a JSON file that is uploaded to Google Storage and ingested by Gold.
-#
-# The output will look similar this:
-#
-# {
-#    "build_number" : "2",
-#    "gitHash" : "a4a338179013b029d6dd55e737b5bd648a9fb68c",
-#    "key" : {
-#       "arch" : "arm64",
-#       "compiler" : "Clang",
-#    },
-#    "results" : [
-#       {
-#          "key" : {
-#             "config" : "vk",
-#             "name" : "yuv_nv12_to_rgb_effect",
-#             "source_type" : "gm"
-#          },
-#          "md5" : "7db34da246868d50ab9ddd776ce6d779",
-#          "options" : {
-#             "ext" : "png",
-#             "gamma_correct" : "no"
-#          }
-#       },
-#       {
-#          "key" : {
-#             "config" : "vk",
-#             "name" : "yuv_to_rgb_effect",
-#             "source_type" : "gm"
-#          },
-#          "md5" : "0b955f387740c66eb23bf0e253c80d64",
-#          "options" : {
-#             "ext" : "png",
-#             "gamma_correct" : "no"
-#          }
-#       }
-#    ],
-# }
-#
-class GoldResults(object):
-
-  def __init__(self, source_type, output_dir, properties_str, key_str,
-               ignore_hashes_file):
-    """
-    source_type is the source_type (=corpus) field used for all results.
-    output_dir is the directory where the resulting images are copied and
-               the dm.json file is written. If the directory exists it will
-               be removed and recreated.
-    properties_str is a string with space separated key/value pairs that
-               is used to set the top level fields in the output JSON file.
-    key_str is a string with space separated key/value pairs that
-               is used to set the 'key' field in the output JSON file.
-    ignore_hashes_file is a file that contains a list of image hashes
-               that should be ignored.
-    """
-    self._source_type = source_type
-    self._properties = _ParseKeyValuePairs(properties_str)
-    self._properties['key'] = _ParseKeyValuePairs(key_str)
-    self._results = []
-    self._passfail = []
-    self._output_dir = output_dir
-
-    # make sure the output directory exists and is empty.
-    if os.path.exists(output_dir):
-      shutil.rmtree(output_dir, ignore_errors=True)
-    os.makedirs(output_dir)
-
-    self._ignore_hashes = set()
-    if ignore_hashes_file:
-      with open(ignore_hashes_file, 'r') as ig_file:
-        hashes = [x.strip() for x in ig_file.readlines() if x.strip()]
-        self._ignore_hashes = set(hashes)
-
-  def AddTestResult(self, testName, md5Hash, outputImagePath, matchResult):
-    # If the hash is in the list of hashes to ignore then we don'try
-    # make a copy, but add it to the result.
-    imgExt = os.path.splitext(outputImagePath)[1].lstrip('.')
-    if md5Hash not in self._ignore_hashes:
-      # Copy the image to <output_dir>/<md5Hash>.<image_extension>
-      if not imgExt:
-        raise ValueError('File %s does not have an extension' % outputImagePath)
-      newFilePath = os.path.join(self._output_dir, md5Hash + '.' + imgExt)
-      shutil.copy2(outputImagePath, newFilePath)
-
-    # Add an entry to the list of test results
-    self._results.append({
-        'key': {
-            'name': testName,
-            'source_type': self._source_type,
-        },
-        'md5': md5Hash,
-        'options': {
-            'ext': imgExt,
-            'gamma_correct': 'no'
-        }
-    })
-
-    self._passfail.append((testName, matchResult))
-
-  def WriteResults(self):
-    self._properties.update({'results': self._results})
-
-    output_file_name = os.path.join(self._output_dir, 'dm.json')
-    with open(output_file_name, 'wb') as outfile:
-      json.dump(self._properties, outfile, indent=1)
-      outfile.write('\n')
-
-    output_file_name = os.path.join(self._output_dir, 'passfail.json')
-    with open(output_file_name, 'wb') as outfile:
-      json.dump(self._passfail, outfile, indent=1)
-      outfile.write('\n')
-
-
-# Produce example output for manual testing.
-def _Example():
-  # Create a test directory with three empty 'image' files.
-  test_dir = './testdirectory'
-  if not os.path.exists(test_dir):
-    os.makedirs(test_dir)
-  open(os.path.join(test_dir, 'image1.png'), 'wb').close()
-  open(os.path.join(test_dir, 'image2.png'), 'wb').close()
-  open(os.path.join(test_dir, 'image3.png'), 'wb').close()
-
-  # Create an instance and add results.
-  prop_str = 'build_number 2 "builder name" Builder-Name gitHash ' \
-      'a4a338179013b029d6dd55e737b5bd648a9fb68c'
-
-  key_str = 'arch arm64 compiler Clang configuration Debug'
-
-  hash_file = os.path.join(test_dir, 'ignore_hashes.txt')
-  with open(hash_file, 'wb') as f:
-    f.write('\n'.join(['hash-1', 'hash-4']) + '\n')
-
-  output_dir = './output_directory'
-  gr = GoldResults('pdfium', output_dir, prop_str, key_str, hash_file)
-  gr.AddTestResult('test-1', 'hash-1', os.path.join(test_dir, 'image1.png'),
-                   GoldBaseline.MATCH)
-  gr.AddTestResult('test-2', 'hash-2', os.path.join(test_dir, 'image2.png'),
-                   GoldBaseline.MATCH)
-  gr.AddTestResult('test-3', 'hash-3', os.path.join(test_dir, 'image3.png'),
-                   GoldBaseline.MISMATCH)
-  gr.WriteResults()
-
-
-if __name__ == '__main__':
-  _Example()
diff --git a/testing/tools/libcxx_check.py b/testing/tools/libcxx_check.py
new file mode 100755
index 0000000..ad6abea
--- /dev/null
+++ b/testing/tools/libcxx_check.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+# Copyright 2022 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Verifies libcxx_revision entries are in sync.
+
+DEPS and buildtools/deps_revisions.gni both have libcxx_revision entries.
+Check that they are in sync.
+"""
+
+import re
+import sys
+
+
+def _ExtractRevisionFromFile(path, regex):
+  """Gets the revision by reading path and searching the lines using regex."""
+  data = open(path, 'rb').read().splitlines()
+  revision = None
+  for line in data:
+    match = regex.match(line)
+    if not match:
+      continue
+    if revision:
+      return None
+    revision = match.group(1)
+  return revision
+
+
+def _GetDepsLibcxxRevision(deps_path):
+  """Gets the libcxx_revision from DEPS."""
+  regex = re.compile(b"^  'libcxx_revision': '(.*)',$")
+  return _ExtractRevisionFromFile(deps_path, regex)
+
+
+def _GetBuildtoolsLibcxxRevision(buildtools_deps_path):
+  """Gets the libcxx_revision from buildtools/deps_revisions.gni."""
+  regex = re.compile(b'^  libcxx_revision = "(.*)"$')
+  return _ExtractRevisionFromFile(buildtools_deps_path, regex)
+
+
+def main():
+  if len(sys.argv) != 3:
+    print('Wrong number of arguments')
+    return 0
+
+  deps_path = sys.argv[1]
+  deps_revision = _GetDepsLibcxxRevision(deps_path)
+  if not deps_revision:
+    print('Cannot parse', deps_path)
+    return 0
+
+  buildtools_deps_path = sys.argv[2]
+  buildtools_revision = _GetBuildtoolsLibcxxRevision(buildtools_deps_path)
+  if not buildtools_revision:
+    print('Cannot parse', buildtools_deps_path)
+    return 0
+
+  if deps_revision != buildtools_revision:
+    print('libcxx_revision mismatch between %s and %s: %s vs. %s' %
+          (deps_path, buildtools_deps_path, deps_revision, buildtools_revision))
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/testing/tools/make_expected.sh b/testing/tools/make_expected.sh
index 5237e51..8f8238c 100755
--- a/testing/tools/make_expected.sh
+++ b/testing/tools/make_expected.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 #
-# Copyright 2015 PDFium Authors. All rights reserved.
+# Copyright 2015 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 #
@@ -23,7 +23,9 @@
   if [ -f "$EVTFILE" ]; then
     SEND_EVENTS="--send-events"
   fi
-  out/Debug/pdfium_test $SEND_EVENTS --time=$TEST_SEED_TIME --png $INFILE
+  FONT_DIR=`readlink -f third_party/test_fonts`
+  out/Debug/pdfium_test $SEND_EVENTS --time=$TEST_SEED_TIME --png \
+      --croscore-font-names --font-dir=$FONT_DIR $INFILE
   RESULTS="$INFILE.*.png"
   for RESULT in $RESULTS ; do
     EXPECTED=`echo -n $RESULT | sed 's/[.]pdf[.]/_expected.pdf./'`
diff --git a/testing/tools/pdfium_root.py b/testing/tools/pdfium_root.py
new file mode 100644
index 0000000..7c4c861
--- /dev/null
+++ b/testing/tools/pdfium_root.py
@@ -0,0 +1,53 @@
+# Copyright 2022 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Utilities for working with the PDFium tree's root directory."""
+
+import os
+import sys
+
+
+class RootDirectoryFinder:
+  """Finds the PDFium tree's root directories.
+
+  The implementation expects that either:
+  1. PDFium is a standalone checkout.
+  2. PDFium is part of another tree within a "third_party/pdfium" directory.
+
+  Attributes:
+      pdfium_root: The path to the root of the PDFium tree.
+      source_root: The path to the root of the source tree. May differ from
+        `pdfium_root` if PDFium is a third-party dependency in another tree.
+  """
+
+  def __init__(self):
+    # Expect `self_dir` to be ".../testing/tools".
+    self_dir = os.path.dirname(os.path.realpath(__file__))
+
+    self.pdfium_root = _remove_path_suffix(self_dir, ('testing', 'tools'))
+    if not self.pdfium_root:
+      raise Exception('Cannot find testing/tools within PDFium root directory')
+
+    # In a Chromium checkout, expect `self.pdfium_root` to be
+    # ".../third_party/pdfium".
+    self.source_root = _remove_path_suffix(self.pdfium_root,
+                                           ('third_party', 'pdfium'))
+    if not self.source_root:
+      self.source_root = self.pdfium_root
+
+
+def _remove_path_suffix(path, expected_suffix):
+  for expected_part in reversed(expected_suffix):
+    if os.path.basename(path) != expected_part:
+      return None
+    path = os.path.dirname(path)
+  return path
+
+
+def add_source_directory_to_import_path(source_directory_path):
+  """Adds a source root-relative directory to end of the import path."""
+  root_finder = RootDirectoryFinder()
+  path = os.path.realpath(
+      os.path.join(root_finder.source_root, source_directory_path))
+  if path not in sys.path:
+    sys.path.append(path)
diff --git a/testing/tools/pngdiffer.py b/testing/tools/pngdiffer.py
index a469506..ceb4022 100755
--- a/testing/tools/pngdiffer.py
+++ b/testing/tools/pngdiffer.py
@@ -1,139 +1,247 @@
-#!/usr/bin/env python
-# Copyright 2015 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2015 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import distutils.spawn
+from dataclasses import dataclass
 import itertools
 import os
 import shutil
+import subprocess
 import sys
 
-# pylint: disable=relative-import
-import common
+EXACT_MATCHING = 'exact'
+FUZZY_MATCHING = 'fuzzy'
 
+_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
+
+
+@dataclass
+class ImageDiff:
+  """Details about an image diff.
+
+  Attributes:
+    actual_path: Path to the actual image file.
+    expected_path: Path to the expected image file, or `None` if no matches.
+    diff_path: Path to the diff image file, or `None` if no diff.
+    reason: Optional reason for the diff.
+  """
+  actual_path: str
+  expected_path: str = None
+  diff_path: str = None
+  reason: str = None
 
 class PNGDiffer():
 
-  def __init__(self, finder, 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 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:
+      raise ValueError(f'rendering_option={rendering_option}')
+
   def CheckMissingTools(self, regenerate_expected):
-    if (regenerate_expected and self.os_name == 'linux' and
-        not distutils.spawn.find_executable('optipng')):
-      return 'Please install "optipng" to regenerate expected images.'
+    if regenerate_expected and not shutil.which(_PNG_OPTIMIZER):
+      return f'Please install "{_PNG_OPTIMIZER}" to regenerate expected images.'
     return None
 
   def GetActualFiles(self, input_filename, source_dir, working_dir):
     actual_paths = []
-    path_templates = PathTemplates(input_filename, source_dir, working_dir)
+    path_templates = _PathTemplates(input_filename, source_dir, working_dir,
+                                    self.os_name, self.suffix_order)
 
     for page in itertools.count():
       actual_path = path_templates.GetActualPath(page)
-      expected_path = path_templates.GetExpectedPath(page)
-      platform_expected_path = path_templates.GetPlatformExpectedPath(
-          self.os_name, page)
-      if os.path.exists(platform_expected_path):
-        expected_path = platform_expected_path
-      elif not os.path.exists(expected_path):
+      if path_templates.GetExpectedPath(page, default_to_base=False):
+        actual_paths.append(actual_path)
+      else:
         break
-      actual_paths.append(actual_path)
-
     return actual_paths
 
-  def HasDifferences(self, input_filename, source_dir, working_dir):
-    path_templates = PathTemplates(input_filename, source_dir, working_dir)
+  def _RunCommand(self, cmd):
+    try:
+      subprocess.run(cmd, capture_output=True, check=True)
+      return None
+    except subprocess.CalledProcessError as e:
+      return e
 
+  def _RunImageCompareCommand(self, image_diff, image_matching_algorithm):
+    cmd = [self.pdfium_diff_path]
+    if self.reverse_byte_order:
+      cmd.append('--reverse-byte-order')
+    if image_matching_algorithm == FUZZY_MATCHING:
+      cmd.append('--fuzzy')
+    cmd.extend([image_diff.actual_path, image_diff.expected_path])
+    return self._RunCommand(cmd)
+
+  def _RunImageDiffCommand(self, image_diff):
+    # TODO(crbug.com/pdfium/1925): Diff mode ignores --reverse-byte-order.
+    return self._RunCommand([
+        self.pdfium_diff_path, '--subtract', image_diff.actual_path,
+        image_diff.expected_path, image_diff.diff_path
+    ])
+
+  def ComputeDifferences(self, input_filename, source_dir, working_dir,
+                         image_matching_algorithm):
+    """Computes differences between actual and expected image files.
+
+    Returns:
+      A list of `ImageDiff` instances, one per differing page.
+    """
+    image_diffs = []
+
+    path_templates = _PathTemplates(input_filename, source_dir, working_dir,
+                                    self.os_name, self.suffix_order)
     for page in itertools.count():
-      actual_path = path_templates.GetActualPath(page)
+      page_diff = ImageDiff(actual_path=path_templates.GetActualPath(page))
+      if not os.path.exists(page_diff.actual_path):
+        # No more actual pages.
+        break
+
       expected_path = path_templates.GetExpectedPath(page)
-      # PDFium tests should be platform independent. Platform based results are
-      # used to capture platform dependent implementations.
-      platform_expected_path = path_templates.GetPlatformExpectedPath(
-          self.os_name, page)
-      if (not os.path.exists(expected_path) and
-          not os.path.exists(platform_expected_path)):
-        if page == 0:
-          print "WARNING: no expected results files for " + input_filename
-        if os.path.exists(actual_path):
-          print('FAILURE: Missing expected result for 0-based page %d of %s' %
-                (page, input_filename))
-          return True
-        break
-      print "Checking " + actual_path
-      sys.stdout.flush()
       if os.path.exists(expected_path):
-        cmd = [self.pdfium_diff_path]
-        if self.reverse_byte_order:
-          cmd.append('--reverse-byte-order')
-        cmd.extend([expected_path, actual_path])
-        error = common.RunCommand(cmd)
+        page_diff.expected_path = expected_path
+
+        compare_error = self._RunImageCompareCommand(page_diff,
+                                                     image_matching_algorithm)
+        if compare_error:
+          page_diff.reason = str(compare_error)
+
+          # TODO(crbug.com/pdfium/1925): Compare and diff simultaneously.
+          page_diff.diff_path = path_templates.GetDiffPath(page)
+          if not self._RunImageDiffCommand(page_diff):
+            print(f'WARNING: No diff for {page_diff.actual_path}')
+            page_diff.diff_path = None
+        else:
+          # Validate that no other paths match.
+          for unexpected_path in path_templates.GetExpectedPaths(page)[1:]:
+            page_diff.expected_path = unexpected_path
+            if not self._RunImageCompareCommand(page_diff,
+                                                image_matching_algorithm):
+              page_diff.reason = f'Also matches {unexpected_path}'
+              break
+          page_diff.expected_path = expected_path
       else:
-        error = 1
-      if error:
-        # When failed, we check against platform based results.
-        if os.path.exists(platform_expected_path):
-          cmd = [self.pdfium_diff_path]
-          if self.reverse_byte_order:
-            cmd.append('--reverse-byte-order')
-          cmd.extend([platform_expected_path, actual_path])
-          error = common.RunCommand(cmd)
-        if error:
-          print "FAILURE: " + input_filename + "; " + str(error)
-          return True
+        if page == 0:
+          print(f'WARNING: no expected results files for {input_filename}')
+        page_diff.reason = f'{expected_path} does not exist'
 
-    return False
+      if page_diff.reason:
+        image_diffs.append(page_diff)
 
-  def Regenerate(self, input_filename, source_dir, working_dir, platform_only):
-    path_templates = PathTemplates(input_filename, source_dir, working_dir)
+    return image_diffs
 
+  def Regenerate(self, input_filename, source_dir, working_dir,
+                 image_matching_algorithm):
+    path_templates = _PathTemplates(input_filename, source_dir, working_dir,
+                                    self.os_name, self.suffix_order)
     for page in itertools.count():
-      # Loop through the generated page images. Stop when there is a page
-      # missing a png, which means the document ended.
-      actual_path = path_templates.GetActualPath(page)
-      if not os.path.isfile(actual_path):
+      expected_paths = path_templates.GetExpectedPaths(page)
+
+      first_match = None
+      last_match = None
+      page_diff = ImageDiff(actual_path=path_templates.GetActualPath(page))
+      if os.path.exists(page_diff.actual_path):
+        # Match against all expected page images.
+        for index, expected_path in enumerate(expected_paths):
+          page_diff.expected_path = expected_path
+          if not self._RunImageCompareCommand(page_diff,
+                                              image_matching_algorithm):
+            if first_match is None:
+              first_match = index
+            last_match = index
+
+        if last_match == 0:
+          # Regeneration not needed. This case may be reached if only some, but
+          # not all, pages need to be regenerated.
+          continue
+      elif expected_paths:
+        # Remove all expected page images.
+        print(f'WARNING: {input_filename} has extra expected page {page}')
+        first_match = 0
+        last_match = len(expected_paths)
+      else:
+        # No more expected or actual pages.
         break
 
-      platform_expected_path = path_templates.GetPlatformExpectedPath(
-          self.os_name, page)
+      # Try to reuse expectations by removing intervening non-matches.
+      #
+      # TODO(crbug.com/pdfium/1988): This can make mistakes due to a lack of
+      # global knowledge about other test configurations, which is why it just
+      # creates backup files rather than immediately removing files.
+      if last_match is not None:
+        if first_match > 1:
+          print(f'WARNING: {input_filename}.{page} has non-adjacent match')
+        if first_match != last_match:
+          print(f'WARNING: {input_filename}.{page} has redundant matches')
 
-      # If there is a platform expected png, we will overwrite it. Otherwise,
-      # overwrite the generic png in "all" mode, or do nothing in "platform"
-      # mode.
-      if os.path.exists(platform_expected_path):
-        expected_path = platform_expected_path
-      elif not platform_only:
-        expected_path = path_templates.GetExpectedPath(page)
-      else:
+        for expected_path in expected_paths[:last_match]:
+          os.rename(expected_path, expected_path + '.bak')
         continue
 
-      shutil.copyfile(actual_path, expected_path)
-      common.RunCommand(['optipng', expected_path])
+      # Regenerate the most specific expected path that exists. If there are no
+      # existing expectations, regenerate the base case.
+      expected_path = path_templates.GetExpectedPath(page)
+      shutil.copyfile(page_diff.actual_path, expected_path)
+      self._RunCommand([_PNG_OPTIMIZER, expected_path])
 
 
-ACTUAL_TEMPLATE = '.pdf.%d.png'
-EXPECTED_TEMPLATE = '_expected' + ACTUAL_TEMPLATE
-PLATFORM_EXPECTED_TEMPLATE = '_expected_%s' + ACTUAL_TEMPLATE
+_ACTUAL_TEMPLATE = '.pdf.%d.png'
+_DIFF_TEMPLATE = '.pdf.%d.diff.png'
 
 
-class PathTemplates(object):
+class _PathTemplates:
 
-  def __init__(self, input_filename, source_dir, working_dir):
+  def __init__(self, input_filename, source_dir, working_dir, os_name,
+               suffix_order):
     input_root, _ = os.path.splitext(input_filename)
     self.actual_path_template = os.path.join(working_dir,
-                                             input_root + ACTUAL_TEMPLATE)
-    self.expected_path = os.path.join(source_dir,
-                                      input_root + EXPECTED_TEMPLATE)
-    self.platform_expected_path = os.path.join(
-        source_dir, input_root + PLATFORM_EXPECTED_TEMPLATE)
+                                             input_root + _ACTUAL_TEMPLATE)
+    self.diff_path_template = os.path.join(working_dir,
+                                           input_root + _DIFF_TEMPLATE)
+
+    # Pre-create the available templates from most to least specific. We
+    # generally expect the most specific case to match first.
+    self.expected_templates = []
+    for suffix in suffix_order:
+      formatted_suffix = suffix.format(os=os_name)
+      self.expected_templates.append(
+          os.path.join(
+              source_dir,
+              f'{input_root}_expected{formatted_suffix}{_ACTUAL_TEMPLATE}'))
+    assert self.expected_templates
 
   def GetActualPath(self, page):
     return self.actual_path_template % page
 
-  def GetExpectedPath(self, page):
-    return self.expected_path % page
+  def GetDiffPath(self, page):
+    return self.diff_path_template % page
 
-  def GetPlatformExpectedPath(self, platform, page):
-    return self.platform_expected_path % (platform, page)
+  def _GetPossibleExpectedPaths(self, page):
+    return [template % page for template in self.expected_templates]
+
+  def GetExpectedPaths(self, page):
+    return list(filter(os.path.exists, self._GetPossibleExpectedPaths(page)))
+
+  def GetExpectedPath(self, page, default_to_base=True):
+    """Returns the most specific expected path that exists."""
+    last_not_found_expected_path = None
+    for expected_path in self._GetPossibleExpectedPaths(page):
+      if os.path.exists(expected_path):
+        return expected_path
+      last_not_found_expected_path = expected_path
+    return last_not_found_expected_path if default_to_base else None
diff --git a/testing/tools/run_corpus_tests.py b/testing/tools/run_corpus_tests.py
index c1bec3a..c9ad185 100755
--- a/testing/tools/run_corpus_tests.py
+++ b/testing/tools/run_corpus_tests.py
@@ -1,18 +1,16 @@
-#!/usr/bin/env python
-# Copyright 2015 The PDFium Authors. All rights reserved.
+#!/usr/bin/env vpython3
+# Copyright 2015 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
 import sys
 
-# pylint: disable=relative-import
 import test_runner
 
 
 def main():
   runner = test_runner.TestRunner('corpus')
   runner.SetEnforceExpectedImages(True)
-  runner.SetOneShotRenderer(True)
   return runner.Run()
 
 
diff --git a/testing/tools/run_javascript_tests.py b/testing/tools/run_javascript_tests.py
index 948c1af..013fe0b 100755
--- a/testing/tools/run_javascript_tests.py
+++ b/testing/tools/run_javascript_tests.py
@@ -1,11 +1,10 @@
-#!/usr/bin/env python
-# Copyright 2015 The PDFium Authors. All rights reserved.
+#!/usr/bin/env vpython3
+# Copyright 2015 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
 import sys
 
-# pylint: disable=relative-import
 import test_runner
 
 
diff --git a/testing/tools/run_pixel_tests.py b/testing/tools/run_pixel_tests.py
index 0992e0a..fb355b8 100755
--- a/testing/tools/run_pixel_tests.py
+++ b/testing/tools/run_pixel_tests.py
@@ -1,11 +1,10 @@
-#!/usr/bin/env python
-# Copyright 2015 The PDFium Authors. All rights reserved.
+#!/usr/bin/env vpython3
+# Copyright 2015 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
 import sys
 
-# pylint: disable=relative-import
 import test_runner
 
 
diff --git a/testing/tools/safetynet_compare.py b/testing/tools/safetynet_compare.py
index c76ce44..e1b5c85 100755
--- a/testing/tools/safetynet_compare.py
+++ b/testing/tools/safetynet_compare.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2017 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2017 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Compares the performance of two versions of the pdfium code."""
@@ -16,7 +16,6 @@
 import sys
 import tempfile
 
-# pylint: disable=relative-import
 from common import GetBooleanGnArg
 from common import PrintErr
 from common import RunCommandPropagateErr
@@ -33,7 +32,7 @@
   return (test_case, result)
 
 
-class CompareRun(object):
+class CompareRun:
   """A comparison between two branches of pdfium."""
 
   def __init__(self, args):
@@ -527,8 +526,7 @@
       output_filename = (
           'callgrind.out.%s.%s' % (test_case.replace('/', '_'), run_label))
       return os.path.join(self.args.output_dir, output_filename)
-    else:
-      return None
+    return None
 
   def _DrawConclusions(self, times_before_branch, times_after_branch):
     """Draws conclusions comparing results of test runs in two branches.
@@ -564,7 +562,7 @@
           ComparisonConclusions.GetOutputDict().
     """
     if self.args.machine_readable:
-      print json.dumps(conclusions_dict)
+      print(json.dumps(conclusions_dict))
     else:
       PrintConclusionsDictHumanReadable(
           conclusions_dict, colored=True, key=self.args.case_order)
diff --git a/testing/tools/safetynet_conclusions.py b/testing/tools/safetynet_conclusions.py
index 8f0b28c..b5be14a 100644
--- a/testing/tools/safetynet_conclusions.py
+++ b/testing/tools/safetynet_conclusions.py
@@ -1,4 +1,4 @@
-# Copyright 2017 The PDFium Authors. All rights reserved.
+# Copyright 2017 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Classes that draw conclusions out of a comparison and represent them."""
@@ -31,7 +31,7 @@
 }
 
 
-class ComparisonConclusions(object):
+class ComparisonConclusions:
   """All conclusions drawn from a comparison.
 
   This is initialized empty and then processes pairs of results for each test
@@ -182,7 +182,7 @@
     return output_dict
 
 
-class ComparisonSummary(object):
+class ComparisonSummary:
   """Totals computed for a comparison."""
 
   def __init__(self):
@@ -207,7 +207,7 @@
     return result
 
 
-class CaseResult(object):
+class CaseResult:
   """The conclusion for the comparison of a single test case."""
 
   def __init__(self, case_name, before, after, ratio, rating):
@@ -245,9 +245,9 @@
     key: String with the CaseResult dictionary key to sort the cases.
   """
   # Print header
-  print '=' * 80
-  print '{0:>11s} {1:>15s}  {2}'.format('% Change', 'Time after', 'Test case')
-  print '-' * 80
+  print('=' * 80)
+  print('{0:>11s} {1:>15s}  {2}'.format('% Change', 'Time after', 'Test case'))
+  print('-' * 80)
 
   color = FORMAT_NORMAL
 
@@ -264,18 +264,18 @@
       color = RATING_TO_COLOR[case_dict['rating']]
 
     if case_dict['rating'] == RATING_FAILURE:
-      print u'{} to measure time for {}'.format(
-          color.format('Failed'), case_name).encode('utf-8')
+      print(u'{} to measure time for {}'.format(
+          color.format('Failed'), case_name).encode('utf-8'))
       continue
 
-    print u'{0} {1:15,d}  {2}'.format(
+    print(u'{0} {1:15,d}  {2}'.format(
         color.format('{:+11.4%}'.format(case_dict['ratio'])),
-        case_dict['after'], case_name).encode('utf-8')
+        case_dict['after'], case_name).encode('utf-8'))
 
   # Print totals
   totals = conclusions_dict['summary']
-  print '=' * 80
-  print 'Test cases run: %d' % totals['total']
+  print('=' * 80)
+  print('Test cases run: %d' % totals['total'])
 
   if colored:
     color = FORMAT_MAGENTA if totals[RATING_FAILURE] else FORMAT_GREEN
diff --git a/testing/tools/safetynet_image.py b/testing/tools/safetynet_image.py
index f300615..3295387 100644
--- a/testing/tools/safetynet_image.py
+++ b/testing/tools/safetynet_image.py
@@ -1,4 +1,4 @@
-# Copyright 2017 The PDFium Authors. All rights reserved.
+# Copyright 2017 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Compares pairs of page images and generates an HTML to look at differences.
@@ -13,7 +13,6 @@
 import sys
 import webbrowser
 
-# pylint: disable=relative-import
 from common import DirectoryFinder
 
 
@@ -21,7 +20,7 @@
   return image_comparison.GenerateOneDiff(image)
 
 
-class ImageComparison(object):
+class ImageComparison:
   """Compares pairs of page images and generates an HTML to look at differences.
 
   The images are all assumed to have the same name and be in two directories:
@@ -63,7 +62,7 @@
     # pylint: disable=attribute-defined-outside-init
 
     if len(self.two_labels) != 2:
-      print >> sys.stderr, 'two_labels must be a tuple of length 2'
+      print('two_labels must be a tuple of length 2', file=sys.stderr)
       return 1
 
     finder = DirectoryFinder(self.build_dir)
@@ -88,7 +87,7 @@
       for image in self.image_locations.Images():
         diff = difference[image]
         if diff is None:
-          print >> sys.stderr, 'Failed to compare image %s' % image
+          print('Failed to compare image %s' % image, file=sys.stderr)
         elif diff > self.threshold:
           self._WriteImageRows(f, image, diff)
         else:
@@ -170,7 +169,7 @@
     except subprocess.CalledProcessError as e:
       return image, percentage_change
     else:
-      print >> sys.stderr, 'Warning: Should have failed the previous diff.'
+      print('Warning: Should have failed the previous diff.', file=sys.stderr)
       return image, 0
 
   def _GetRelativePath(self, absolute_path):
@@ -239,7 +238,7 @@
       f.write('</td></tr>')
 
 
-class ImageLocations(object):
+class ImageLocations:
   """Contains the locations of input and output image files.
   """
 
diff --git a/testing/tools/safetynet_job.py b/testing/tools/safetynet_job.py
index 9b5cbfd..bbb3cca 100755
--- a/testing/tools/safetynet_job.py
+++ b/testing/tools/safetynet_job.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2017 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2017 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Looks for performance regressions on all pushes since the last run.
@@ -15,14 +15,13 @@
 import os
 import sys
 
-# pylint: disable=relative-import
 from common import PrintWithTime
 from common import RunCommandPropagateErr
 from githelper import GitHelper
 from safetynet_conclusions import PrintConclusionsDictHumanReadable
 
 
-class JobContext(object):
+class JobContext:
   """Context for a single run, including name and directory paths."""
 
   def __init__(self, args):
@@ -36,7 +35,7 @@
                                             '%s.log' % self.datetime)
 
 
-class JobRun(object):
+class JobRun:
   """A single run looking for regressions since the last one."""
 
   def __init__(self, args, context):
@@ -69,7 +68,7 @@
 
     if not self.args.no_checkout:
       self.git.FetchOriginMaster()
-      self.git.Checkout('origin/master')
+      self.git.Checkout('origin/main')
 
     # Make sure results dir exists
     if not os.path.exists(self.context.results_dir):
@@ -200,7 +199,7 @@
   parser.add_argument(
       '--no-checkout',
       action='store_true',
-      help='whether to skip checking out origin/master. Use '
+      help='whether to skip checking out origin/main. Use '
       'for script debugging.')
   parser.add_argument(
       '--no-checkpoint',
diff --git a/testing/tools/safetynet_measure.py b/testing/tools/safetynet_measure.py
index 3577189..cc83b1d 100755
--- a/testing/tools/safetynet_measure.py
+++ b/testing/tools/safetynet_measure.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2017 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2017 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Measures performance for rendering a single test case with pdfium.
@@ -13,7 +13,6 @@
 import subprocess
 import sys
 
-# pylint: disable=relative-import
 from common import PrintErr
 
 CALLGRIND_PROFILER = 'callgrind'
@@ -23,7 +22,7 @@
 PDFIUM_TEST = 'pdfium_test'
 
 
-class PerformanceRun(object):
+class PerformanceRun:
   """A single measurement of a test case."""
 
   def __init__(self, args):
@@ -65,7 +64,7 @@
     if time is None:
       return 1
 
-    print time
+    print(time)
     return 0
 
   def _RunCallgrind(self):
@@ -84,7 +83,8 @@
         '--instr-atstart=%s' % instrument_at_start,
         '--callgrind-out-file=%s' % output_path
     ] + self._BuildTestHarnessCommand())
-    output = subprocess.check_output(valgrind_cmd, stderr=subprocess.STDOUT)
+    output = subprocess.check_output(
+        valgrind_cmd, stderr=subprocess.STDOUT).decode('utf-8')
 
     # Match the line with the instruction count, eg.
     # '==98765== Collected : 12345'
@@ -100,7 +100,8 @@
     # -einstructions: print only instruction count
     cmd_to_run = (['perf', 'stat', '--no-big-num', '-einstructions'] +
                   self._BuildTestHarnessCommand())
-    output = subprocess.check_output(cmd_to_run, stderr=subprocess.STDOUT)
+    output = subprocess.check_output(
+        cmd_to_run, stderr=subprocess.STDOUT).decode('utf-8')
 
     # Match the line with the instruction count, eg.
     # '        12345      instructions'
diff --git a/testing/tools/skia_gold/__init__.py b/testing/tools/skia_gold/__init__.py
new file mode 100644
index 0000000..39381b9
--- /dev/null
+++ b/testing/tools/skia_gold/__init__.py
@@ -0,0 +1,7 @@
+# Copyright 2022 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import pdfium_root
+
+pdfium_root.add_source_directory_to_import_path('build')
diff --git a/testing/tools/skia_gold/pdfium_skia_gold_properties.py b/testing/tools/skia_gold/pdfium_skia_gold_properties.py
new file mode 100644
index 0000000..874e643
--- /dev/null
+++ b/testing/tools/skia_gold/pdfium_skia_gold_properties.py
@@ -0,0 +1,14 @@
+# Copyright 2021 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""PDFium implementation of //build/skia_gold_common/skia_gold_properties.py."""
+
+import pdfium_root
+from skia_gold_common import skia_gold_properties
+
+
+class PDFiumSkiaGoldProperties(skia_gold_properties.SkiaGoldProperties):
+
+  def _GetGitRepoDirectory(self):
+    root_finder = pdfium_root.RootDirectoryFinder()
+    return root_finder.pdfium_root
diff --git a/testing/tools/skia_gold/pdfium_skia_gold_session.py b/testing/tools/skia_gold/pdfium_skia_gold_session.py
new file mode 100644
index 0000000..98a7ef2
--- /dev/null
+++ b/testing/tools/skia_gold/pdfium_skia_gold_session.py
@@ -0,0 +1,29 @@
+# Copyright 2021 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""PDFium implementation of //build/skia_gold_common/skia_gold_session.py."""
+
+from skia_gold_common import output_managerless_skia_gold_session as omsgs
+
+
+# ComparisonResults nested inside the SkiaGoldSession causes issues with
+# multiprocessing and pickling, so it was moved out here.
+class PDFiumComparisonResults:
+  """Struct-like object for storing results of an image comparison."""
+
+  def __init__(self):
+    self.public_triage_link = None
+    self.internal_triage_link = None
+    self.triage_link_omission_reason = None
+    self.local_diff_given_image = None
+    self.local_diff_closest_image = None
+    self.local_diff_diff_image = None
+
+
+class PDFiumSkiaGoldSession(omsgs.OutputManagerlessSkiaGoldSession):
+
+  def _GetDiffGoldInstance(self):
+    return str(self._instance)
+
+  def ComparisonResults(self):
+    return PDFiumComparisonResults()
diff --git a/testing/tools/skia_gold/pdfium_skia_gold_session_manager.py b/testing/tools/skia_gold/pdfium_skia_gold_session_manager.py
new file mode 100644
index 0000000..af6cc5e
--- /dev/null
+++ b/testing/tools/skia_gold/pdfium_skia_gold_session_manager.py
@@ -0,0 +1,21 @@
+# Copyright 2021 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""PDFium implementation of
+//build/skia_gold_common/skia_gold_session_manager.py."""
+
+from . import pdfium_skia_gold_session
+from skia_gold_common import skia_gold_session_manager as sgsm
+
+SKIA_PDF_INSTANCE = 'pdfium'
+
+
+class PDFiumSkiaGoldSessionManager(sgsm.SkiaGoldSessionManager):
+
+  @staticmethod
+  def GetSessionClass():
+    return pdfium_skia_gold_session.PDFiumSkiaGoldSession
+
+  @staticmethod
+  def _GetDefaultInstance():
+    return SKIA_PDF_INSTANCE
diff --git a/testing/tools/skia_gold/skia_gold.py b/testing/tools/skia_gold/skia_gold.py
new file mode 100644
index 0000000..9eb9871
--- /dev/null
+++ b/testing/tools/skia_gold/skia_gold.py
@@ -0,0 +1,213 @@
+# Copyright 2021 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import logging
+import shlex
+import shutil
+
+from . import pdfium_skia_gold_properties
+from . import pdfium_skia_gold_session_manager
+
+GS_BUCKET = 'skia-pdfium-gm'
+
+
+def _ParseKeyValuePairs(kv_str):
+  """
+  Parses a string of the type 'key1 value1 key2 value2' into a dict.
+  """
+  kv_pairs = shlex.split(kv_str)
+  if len(kv_pairs) % 2:
+    raise ValueError('Uneven number of key/value pairs. Got %s' % kv_str)
+  return {kv_pairs[i]: kv_pairs[i + 1] for i in range(0, len(kv_pairs), 2)}
+
+
+def add_skia_gold_args(parser):
+  group = parser.add_argument_group('Skia Gold Arguments')
+  group.add_argument(
+      '--git-revision', help='Revision being tested.', default=None)
+  group.add_argument(
+      '--gerrit-issue',
+      help='For Skia Gold integration. Gerrit issue ID.',
+      default='')
+  group.add_argument(
+      '--gerrit-patchset',
+      help='For Skia Gold integration. Gerrit patch set number.',
+      default='')
+  group.add_argument(
+      '--buildbucket-id',
+      help='For Skia Gold integration. Buildbucket build ID.',
+      default='')
+  group.add_argument(
+      '--bypass-skia-gold-functionality',
+      action='store_true',
+      default=False,
+      help='Bypass all interaction with Skia Gold, effectively disabling the '
+      'image comparison portion of any tests that use Gold. Only meant to '
+      'be used in case a Gold outage occurs and cannot be fixed quickly.')
+  local_group = group.add_mutually_exclusive_group()
+  local_group.add_argument(
+      '--local-pixel-tests',
+      action='store_true',
+      default=None,
+      help='Specifies to run the test harness in local run mode or not. When '
+      'run in local mode, uploading to Gold is disabled and links to '
+      'help with local debugging are output. Running in local mode also '
+      'implies --no-luci-auth. If both this and --no-local-pixel-tests are '
+      'left unset, the test harness will attempt to detect whether it is '
+      'running on a workstation or not and set this option accordingly.')
+  local_group.add_argument(
+      '--no-local-pixel-tests',
+      action='store_false',
+      dest='local_pixel_tests',
+      help='Specifies to run the test harness in non-local (bot) mode. When '
+      'run in this mode, data is actually uploaded to Gold and triage links '
+      'arge generated. If both this and --local-pixel-tests are left unset, '
+      'the test harness will attempt to detect whether it is running on a '
+      'workstation or not and set this option accordingly.')
+  group.add_argument(
+      '--no-luci-auth',
+      action='store_true',
+      default=False,
+      help='Don\'t use the service account provided by LUCI for '
+      'authentication for Skia Gold, instead relying on gsutil to be '
+      'pre-authenticated. Meant for testing locally instead of on the bots.')
+
+  group.add_argument(
+      '--gold_key',
+      default='',
+      dest="gold_key",
+      help='Key value pairs of config data such like the hardware/software '
+      'configuration the image was produced on.')
+  group.add_argument(
+      '--gold_output_dir',
+      default='',
+      dest="gold_output_dir",
+      help='Path to the dir where diff output image files are saved, '
+      'if running locally. If this is a tryjob run, will contain link to skia '
+      'gold CL triage link. Required with --run-skia-gold.')
+
+
+def clear_gold_output_dir(output_dir):
+  # make sure the output directory exists and is empty.
+  if os.path.exists(output_dir):
+    shutil.rmtree(output_dir, ignore_errors=True)
+  os.makedirs(output_dir)
+
+
+class SkiaGoldTester:
+
+  def __init__(self, source_type, skia_gold_args, process_name=None):
+    """
+    source_type: source_type (=corpus) field used for all results.
+    skia_gold_args: Parsed arguments from argparse.ArgumentParser.
+    process_name: Unique name of current process, if multiprocessing is on.
+    """
+    self._source_type = source_type
+    self._output_dir = skia_gold_args.gold_output_dir
+    # goldctl is not thread safe, so each process needs its own directory
+    if process_name:
+      self._output_dir = os.path.join(skia_gold_args.gold_output_dir,
+                                      process_name)
+      clear_gold_output_dir(self._output_dir)
+    self._keys = _ParseKeyValuePairs(skia_gold_args.gold_key)
+    self._skia_gold_args = skia_gold_args
+    self._skia_gold_session_manager = None
+    self._skia_gold_properties = None
+
+  def WriteCLTriageLink(self, link):
+    # pdfium recipe will read from this file and display the link in the step
+    # presentation
+    assert isinstance(link, str)
+    output_file_name = os.path.join(self._output_dir, 'cl_triage_link.txt')
+    if os.path.exists(output_file_name):
+      os.remove(output_file_name)
+    with open(output_file_name, 'wb') as outfile:
+      outfile.write(link.encode('utf8'))
+
+  def GetSkiaGoldProperties(self):
+    if not self._skia_gold_properties:
+      if self._skia_gold_args.local_pixel_tests is None:
+        self._skia_gold_args.local_pixel_tests = 'SWARMING_SERVER' \
+            not in os.environ
+
+      self._skia_gold_properties = pdfium_skia_gold_properties\
+          .PDFiumSkiaGoldProperties(self._skia_gold_args)
+    return self._skia_gold_properties
+
+  def GetSkiaGoldSessionManager(self):
+    if not self._skia_gold_session_manager:
+      self._skia_gold_session_manager = pdfium_skia_gold_session_manager\
+          .PDFiumSkiaGoldSessionManager(self._output_dir,
+                                        self.GetSkiaGoldProperties())
+    return self._skia_gold_session_manager
+
+  def IsTryjobRun(self):
+    return self.GetSkiaGoldProperties().IsTryjobRun()
+
+  def GetCLTriageLink(self):
+    return 'https://pdfium-gold.skia.org/search?issue={issue}&crs=gerrit&'\
+    'corpus={source_type}'.format(
+        issue=self.GetSkiaGoldProperties().issue, source_type=self._source_type)
+
+  def UploadTestResultToSkiaGold(self, image_name, image_path):
+    gold_properties = self.GetSkiaGoldProperties()
+    use_luci = not (gold_properties.local_pixel_tests or
+                    gold_properties.no_luci_auth)
+    gold_session = self.GetSkiaGoldSessionManager()\
+        .GetSkiaGoldSession(self._keys, corpus=self._source_type,
+                            bucket=GS_BUCKET)
+
+    status, error = gold_session.RunComparison(
+        name=image_name, png_file=image_path, use_luci=use_luci)
+
+    status_codes =\
+        self.GetSkiaGoldSessionManager().GetSessionClass().StatusCodes
+    if status == status_codes.SUCCESS:
+      return True
+    if status == status_codes.AUTH_FAILURE:
+      logging.error('Gold authentication failed with output %s', error)
+    elif status == status_codes.INIT_FAILURE:
+      logging.error('Gold initialization failed with output %s', error)
+    elif status == status_codes.COMPARISON_FAILURE_REMOTE:
+      logging.error('Remote comparison failed. See outputted triage links.')
+    elif status == status_codes.COMPARISON_FAILURE_LOCAL:
+      logging.error('Local comparison failed. Local diff files:')
+      _OutputLocalDiffFiles(gold_session, image_name)
+      print()
+    elif status == status_codes.LOCAL_DIFF_FAILURE:
+      logging.error(
+          'Local comparison failed and an error occurred during diff '
+          'generation: %s', error)
+      # There might be some files, so try outputting them.
+      logging.error('Local diff files:')
+      _OutputLocalDiffFiles(gold_session, image_name)
+      print()
+    else:
+      logging.error(
+          'Given unhandled SkiaGoldSession StatusCode %s with error %s', status,
+          error)
+
+    return False
+
+
+def _OutputLocalDiffFiles(gold_session, image_name):
+  """Logs the local diff image files from the given SkiaGoldSession.
+
+  Args:
+    gold_session: A skia_gold_session.SkiaGoldSession instance to pull files
+        from.
+    image_name: A string containing the name of the image/test that was
+        compared.
+  """
+  given_file = gold_session.GetGivenImageLink(image_name)
+  closest_file = gold_session.GetClosestImageLink(image_name)
+  diff_file = gold_session.GetDiffImageLink(image_name)
+  failure_message = 'Unable to retrieve link'
+  logging.error('Generated image for %s: %s', image_name, given_file or
+                failure_message)
+  logging.error('Closest image for %s: %s', image_name, closest_file or
+                failure_message)
+  logging.error('Diff image for %s: %s', image_name, diff_file or
+                failure_message)
diff --git a/testing/tools/strip_jp2_comments.py b/testing/tools/strip_jp2_comments.py
new file mode 100755
index 0000000..eb03cdc
--- /dev/null
+++ b/testing/tools/strip_jp2_comments.py
@@ -0,0 +1,124 @@
+#!/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.
+"""Strips comments from a JP2 file.
+
+This is a simple filter script to strip comments from a JP2 file, in order to
+save a few bytes from the final file size.
+"""
+
+import struct
+import sys
+
+BOX_HEADER_SIZE = 8
+BOX_TAG_JP2C = b'jp2c'
+
+MARKER_SIZE = 2
+MARKER_START = 0xff
+MARKER_TAG_IGNORE = 0x00
+MARKER_TAG_COMMENT = 0x64
+MARKER_TAG_FILL = 0xff
+
+
+def parse_box(buffer, offset):
+  """Parses the next box in a JP2 file.
+
+  Args:
+    buffer: A buffer containing the JP2 file contents.
+    offset: The starting offset into the buffer.
+
+  Returns:
+    A tuple (next_offset, tag) where next_offset is the ending offset, and tag
+    is the type tag. The box contents will be buffer[offset + 8:next_offset].
+  """
+  length, tag = struct.unpack_from('>I4s', buffer, offset)
+  return offset + length, tag
+
+
+def parse_marker(buffer, offset):
+  """Parses the next marker in a codestream.
+
+  Args:
+    buffer: A buffer containing the codestream.
+    offset: The starting offset into the buffer.
+
+  Returns:
+    A tuple (next_offset, tag) where next_offset is the offset after the marker,
+    and tag is the type tag. If no marker was found, next_offset will point to
+    the end of the buffer, and tag will be None. A marker is always 2 bytes.
+  """
+  while True:
+    # Search for start of marker.
+    next_offset = buffer.find(MARKER_START, offset)
+    if next_offset == -1:
+      next_offset = len(buffer)
+      break
+    next_offset += 1
+
+    # Parse marker.
+    if next_offset == len(buffer):
+      break
+    tag = buffer[next_offset]
+    if tag == MARKER_TAG_FILL:
+      # Possible fill byte, reparse as start of marker.
+      continue
+    next_offset += 1
+
+    if tag == MARKER_TAG_IGNORE:
+      # Not a real marker.
+      continue
+    return next_offset, tag
+
+  return next_offset
+
+
+def rewrite_jp2c(buffer):
+  rewrite_buffer = bytearray(BOX_HEADER_SIZE)
+
+  offset = 0
+  start_offset = offset
+  while offset < len(buffer):
+    next_offset, marker = parse_marker(buffer, offset)
+    if marker == MARKER_TAG_COMMENT:
+      # Flush the codestream before the comment.
+      rewrite_buffer.extend(buffer[start_offset:next_offset - MARKER_SIZE])
+
+      # Find the next marker, skipping the comment.
+      next_offset, marker = parse_marker(buffer, next_offset)
+      if marker is not None:
+        # Reparse the marker.
+        next_offset -= MARKER_SIZE
+      start_offset = next_offset
+    else:
+      # Pass through other markers.
+      pass
+    offset = next_offset
+
+  # Flush the tail of the codestream.
+  rewrite_buffer.extend(buffer[start_offset:])
+
+  struct.pack_into('>I4s', rewrite_buffer, 0, len(rewrite_buffer), BOX_TAG_JP2C)
+  return rewrite_buffer
+
+
+def main(in_file, out_file):
+  buffer = in_file.read()
+
+  # Scan through JP2 boxes.
+  offset = 0
+  while offset < len(buffer):
+    next_offset, tag = parse_box(buffer, offset)
+    if tag == BOX_TAG_JP2C:
+      # Rewrite "jp2c" (codestream) box.
+      out_file.write(rewrite_jp2c(buffer[offset + BOX_HEADER_SIZE:next_offset]))
+    else:
+      # Pass through other boxes.
+      out_file.write(buffer[offset:next_offset])
+    offset = next_offset
+
+  out_file.flush()
+
+
+if __name__ == '__main__':
+  main(sys.stdin.buffer, sys.stdout.buffer)
diff --git a/testing/tools/suppressor.py b/testing/tools/suppressor.py
index 70eef99..6fea735 100755
--- a/testing/tools/suppressor.py
+++ b/testing/tools/suppressor.py
@@ -1,24 +1,26 @@
-#!/usr/bin/env python
-# Copyright 2015 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2015 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
 import os
 
-# pylint: disable=relative-import
 import common
+import pngdiffer
 
 
 class Suppressor:
 
-  def __init__(self, finder, feature_string, js_disabled, xfa_disabled):
-    feature_vector = feature_string.strip().split(",")
-    self.has_v8 = not js_disabled and "V8" in feature_vector
-    self.has_xfa = (not js_disabled and not xfa_disabled and
-                    "XFA" in feature_vector)
+  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.rendering_option = rendering_option
     self.suppression_set = self._LoadSuppressedSet('SUPPRESSIONS', finder)
     self.image_suppression_set = self._LoadSuppressedSet(
         'SUPPRESSIONS_IMAGE_DIFF', finder)
+    self.exact_matching_suppression_set = self._LoadSuppressedSet(
+        'SUPPRESSIONS_EXACT_MATCHING', finder)
 
   def _LoadSuppressedSet(self, suppressions_filename, finder):
     v8_option = "v8" if self.has_v8 else "nov8"
@@ -26,6 +28,7 @@
     with open(os.path.join(finder.TestingDir(), suppressions_filename)) as f:
       return set(
           self._FilterSuppressions(common.os_name(), v8_option, xfa_option,
+                                   self.rendering_option,
                                    self._ExtractSuppressions(f)))
 
   def _ExtractSuppressions(self, f):
@@ -34,35 +37,45 @@
                                for x in f.readlines()] if y
     ]
 
-  def _FilterSuppressions(self, os_name, js, xfa, unfiltered_list):
+  def _FilterSuppressions(self, os_name, js, xfa, rendering_option,
+                          unfiltered_list):
     return [
         x[0]
         for x in unfiltered_list
-        if self._MatchSuppression(x, os_name, js, xfa)
+        if self._MatchSuppression(x, os_name, js, xfa, rendering_option)
     ]
 
-  def _MatchSuppression(self, item, os_name, js, xfa):
+  def _MatchSuppression(self, item, os_name, js, xfa, rendering_option):
     os_column = item[1].split(",")
     js_column = item[2].split(",")
     xfa_column = item[3].split(",")
+    rendering_option_column = item[4].split(",")
     return (('*' in os_column or os_name in os_column) and
             ('*' in js_column or js in js_column) and
-            ('*' in xfa_column or xfa in xfa_column))
+            ('*' in xfa_column or xfa in xfa_column) and
+            ('*' in rendering_option_column or
+             rendering_option in rendering_option_column))
 
   def IsResultSuppressed(self, input_filename):
     if input_filename in self.suppression_set:
-      print "%s result is suppressed" % input_filename
+      print("%s result is suppressed" % input_filename)
       return True
     return False
 
   def IsExecutionSuppressed(self, input_filepath):
     if "xfa_specific" in input_filepath and not self.has_xfa:
-      print "%s execution is suppressed" % input_filepath
+      print("%s execution is suppressed" % input_filepath)
       return True
     return False
 
   def IsImageDiffSuppressed(self, input_filename):
     if input_filename in self.image_suppression_set:
-      print "%s image diff comparison is suppressed" % input_filename
+      print("%s image diff comparison is suppressed" % input_filename)
       return True
     return False
+
+  def GetImageMatchingAlgorithm(self, input_filename):
+    if input_filename in self.exact_matching_suppression_set:
+      print(f"{input_filename} image diff comparison is fuzzy")
+      return pngdiffer.FUZZY_MATCHING
+    return pngdiffer.EXACT_MATCHING
diff --git a/testing/tools/test_runner.py b/testing/tools/test_runner.py
index d3640d6..c7f8aa7 100644
--- a/testing/tools/test_runner.py
+++ b/testing/tools/test_runner.py
@@ -1,23 +1,30 @@
-#!/usr/bin/env python
-# Copyright 2016 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2016 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import functools
+import argparse
+from dataclasses import dataclass, field
+from datetime import timedelta
+from io import BytesIO
 import multiprocessing
-import optparse
 import os
 import re
 import shutil
 import subprocess
 import sys
+import time
 
-# pylint: disable=relative-import
 import common
-import gold
+import pdfium_root
 import pngdiffer
+from skia_gold import skia_gold
 import suppressor
 
+pdfium_root.add_source_directory_to_import_path(os.path.join('build', 'util'))
+from lib.results import result_sink, result_types
+
+
 # Arbitrary timestamp, expressed in seconds since the epoch, used to make sure
 # that tests that depend on the current time are stable. Happens to be the
 # timestamp of the first commit to repo, 2014/5/9 17:48:50.
@@ -26,33 +33,10 @@
 # List of test types that should run text tests instead of pixel tests.
 TEXT_TESTS = ['javascript']
 
-
-class KeyboardInterruptError(Exception):
-  pass
-
-
-# Nomenclature:
-#   x_root - "x"
-#   x_filename - "x.ext"
-#   x_path - "path/to/a/b/c/x.ext"
-#   c_dir - "path/to/a/b/c"
-
-
-def TestOneFileParallel(this, test_case):
-  """Wrapper to call GenerateAndTest() and redirect output to stdout."""
-  try:
-    input_filename, source_dir = test_case
-    result = this.GenerateAndTest(input_filename, source_dir)
-    return (result, input_filename, source_dir)
-  except KeyboardInterrupt:
-    raise KeyboardInterruptError()
-
-
-def DeleteFiles(files):
-  """Utility function to delete a list of files"""
-  for f in files:
-    if os.path.exists(f):
-      os.remove(f)
+# Timeout (in seconds) for individual test commands.
+# TODO(crbug.com/pdfium/1967): array_buffer.in is slow under MSan, so need a
+# very generous 5 minute timeout for now.
+TEST_TIMEOUT = timedelta(minutes=5).total_seconds()
 
 
 class TestRunner:
@@ -62,391 +46,298 @@
     # which all correspond directly to the type for the test being run. In the
     # future if there are tests that don't have this clean correspondence, then
     # an argument for the type will need to be added.
-    self.test_dir = dirname
-    self.test_type = dirname
-    self.delete_output_on_success = False
-    self.enforce_expected_images = False
-    self.oneshot_renderer = False
+    self.per_process_config = _PerProcessConfig(
+        test_dir=dirname, test_type=dirname)
 
-  # GenerateAndTest returns a tuple <success, outputfiles> where
-  # success is a boolean indicating whether the tests passed comparison
-  # tests and outputfiles is a list tuples:
-  #          (path_to_image, md5_hash_of_pixelbuffer)
-  def GenerateAndTest(self, input_filename, source_dir):
-    input_root, _ = os.path.splitext(input_filename)
-    pdf_path = os.path.join(self.working_dir, input_root + '.pdf')
+  @property
+  def options(self):
+    return self.per_process_config.options
 
-    # Remove any existing generated images from previous runs.
-    actual_images = self.image_differ.GetActualFiles(input_filename, source_dir,
-                                                     self.working_dir)
-    DeleteFiles(actual_images)
+  def IsSkiaGoldEnabled(self):
+    return (self.options.run_skia_gold and
+            not self.per_process_config.test_type in TEXT_TESTS)
 
-    sys.stdout.flush()
+  def IsExecutionSuppressed(self, input_path):
+    return self.per_process_state.test_suppressor.IsExecutionSuppressed(
+        input_path)
 
-    raised_exception = self.Generate(source_dir, input_filename, input_root,
-                                     pdf_path)
+  def IsResultSuppressed(self, input_filename):
+    return self.per_process_state.test_suppressor.IsResultSuppressed(
+        input_filename)
 
-    if raised_exception is not None:
-      print 'FAILURE: %s; %s' % (input_filename, raised_exception)
-      return False, []
+  def HandleResult(self, test_case, test_result):
+    input_filename = os.path.basename(test_case.input_path)
 
-    results = []
-    if self.test_type in TEXT_TESTS:
-      expected_txt_path = os.path.join(source_dir, input_root + '_expected.txt')
-      raised_exception = self.TestText(input_filename, input_root,
-                                       expected_txt_path, pdf_path)
-    else:
-      use_ahem = 'use_ahem' in source_dir
-      raised_exception, results = self.TestPixel(pdf_path, use_ahem)
-
-    if raised_exception is not None:
-      print 'FAILURE: %s; %s' % (input_filename, raised_exception)
-      return False, results
-
-    if actual_images:
-      if self.image_differ.HasDifferences(input_filename, source_dir,
-                                          self.working_dir):
-        self.RegenerateIfNeeded_(input_filename, source_dir)
-        return False, results
-    else:
-      if (self.enforce_expected_images and
-          not self.test_suppressor.IsImageDiffSuppressed(input_filename)):
-        self.RegenerateIfNeeded_(input_filename, source_dir)
-        print 'FAILURE: %s; Missing expected images' % input_filename
-        return False, results
-
-    if self.delete_output_on_success:
-      DeleteFiles(actual_images)
-    return True, results
-
-  def RegenerateIfNeeded_(self, input_filename, source_dir):
-    if (not self.options.regenerate_expected or
-        self.test_suppressor.IsResultSuppressed(input_filename) or
-        self.test_suppressor.IsImageDiffSuppressed(input_filename)):
-      return
-
-    platform_only = (self.options.regenerate_expected == 'platform')
-    self.image_differ.Regenerate(input_filename, source_dir, self.working_dir,
-                                 platform_only)
-
-  def Generate(self, source_dir, input_filename, input_root, pdf_path):
-    original_path = os.path.join(source_dir, input_filename)
-    input_path = os.path.join(source_dir, input_root + '.in')
-
-    input_event_path = os.path.join(source_dir, input_root + '.evt')
-    if os.path.exists(input_event_path):
-      output_event_path = os.path.splitext(pdf_path)[0] + '.evt'
-      shutil.copyfile(input_event_path, output_event_path)
-
-    if not os.path.exists(input_path):
-      if os.path.exists(original_path):
-        shutil.copyfile(original_path, pdf_path)
-      return None
-
-    sys.stdout.flush()
-
-    return common.RunCommand([
-        sys.executable, self.fixup_path, '--output-dir=' + self.working_dir,
-        input_path
-    ])
-
-  def TestText(self, input_filename, input_root, expected_txt_path, pdf_path):
-    txt_path = os.path.join(self.working_dir, input_root + '.txt')
-
-    with open(txt_path, 'w') as outfile:
-      cmd_to_run = [
-          self.pdfium_test_path, '--send-events', '--time=' + TEST_SEED_TIME
-      ]
-
-      if self.options.disable_javascript:
-        cmd_to_run.append('--disable-javascript')
-
-      if self.options.disable_xfa:
-        cmd_to_run.append('--disable-xfa')
-
-      cmd_to_run.append(pdf_path)
-      subprocess.check_call(cmd_to_run, stdout=outfile)
-
-    # If the expected file does not exist, the output is expected to be empty.
-    if not os.path.exists(expected_txt_path):
-      return self._VerifyEmptyText(txt_path)
-
-    # If JavaScript is disabled, the output should be empty.
-    # However, if the test is suppressed and JavaScript is disabled, do not
-    # verify that the text is empty so the suppressed test does not surprise.
-    if (self.options.disable_javascript and
-        not self.test_suppressor.IsResultSuppressed(input_filename)):
-      return self._VerifyEmptyText(txt_path)
-
-    cmd = [sys.executable, self.text_diff_path, expected_txt_path, txt_path]
-    return common.RunCommand(cmd)
-
-  def _VerifyEmptyText(self, txt_path):
-    try:
-      with open(txt_path, "r") as txt_file:
-        txt_data = txt_file.readlines()
-      if not len(txt_data):
-        return None
-      sys.stdout.write('Unexpected output:\n')
-      for line in txt_data:
-        sys.stdout.write(line)
-      raise Exception('%s should be empty.' % txt_path)
-    except Exception as e:
-      return e
-
-  def TestPixel(self, pdf_path, use_ahem):
-    cmd_to_run = [
-        self.pdfium_test_path, '--send-events', '--png', '--md5',
-        '--time=' + TEST_SEED_TIME
-    ]
-
-    if self.oneshot_renderer:
-      cmd_to_run.append('--render-oneshot')
-
-    if use_ahem:
-      cmd_to_run.append('--font-dir=%s' % self.font_dir)
-
-    if self.options.disable_javascript:
-      cmd_to_run.append('--disable-javascript')
-
-    if self.options.disable_xfa:
-      cmd_to_run.append('--disable-xfa')
-
-    if self.options.reverse_byte_order:
-      cmd_to_run.append('--reverse-byte-order')
-
-    cmd_to_run.append(pdf_path)
-    return common.RunCommandExtractHashedFiles(cmd_to_run)
-
-  def HandleResult(self, input_filename, input_path, result):
-    success, image_paths = result
-
-    if image_paths:
-      for img_path, md5_hash in image_paths:
-        # The output filename without image extension becomes the test name.
-        # For example, "/path/to/.../testing/corpus/example_005.pdf.0.png"
-        # becomes "example_005.pdf.0".
-        test_name = os.path.splitext(os.path.split(img_path)[1])[0]
-
-        matched = "suppressed"
-        if not self.test_suppressor.IsResultSuppressed(input_filename):
-          matched = self.gold_baseline.MatchLocalResult(test_name, md5_hash)
-          if matched == gold.GoldBaseline.MISMATCH:
-            print 'Skia Gold hash mismatch for test case: %s' % test_name
-          elif matched == gold.GoldBaseline.NO_BASELINE:
-            print 'No Skia Gold baseline found for test case: %s' % test_name
-
-        if self.gold_results:
-          self.gold_results.AddTestResult(test_name, md5_hash, img_path,
-                                          matched)
-
-    if self.test_suppressor.IsResultSuppressed(input_filename):
+    test_result.status = self._SuppressStatus(input_filename,
+                                              test_result.status)
+    if test_result.status == result_types.UNKNOWN:
       self.result_suppressed_cases.append(input_filename)
-      if success:
-        self.surprises.append(input_path)
-    else:
-      if not success:
-        self.failures.append(input_path)
+      self.surprises.append(test_case.input_path)
+    elif test_result.status == result_types.SKIP:
+      self.result_suppressed_cases.append(input_filename)
+    elif not test_result.IsPass():
+      self.failures.append(test_case.input_path)
+
+    for artifact in test_result.image_artifacts:
+      if artifact.skia_gold_status == result_types.PASS:
+        if self.IsResultSuppressed(artifact.image_path):
+          self.skia_gold_unexpected_successes.append(artifact.GetSkiaGoldId())
+        else:
+          self.skia_gold_successes.append(artifact.GetSkiaGoldId())
+      elif artifact.skia_gold_status == result_types.FAIL:
+        self.skia_gold_failures.append(artifact.GetSkiaGoldId())
+
+    # Log test result.
+    print(f'{test_result.status}: {test_result.test_id}')
+    if not test_result.IsPass():
+      if test_result.reason:
+        print(f'Failure reason: {test_result.reason}')
+      if test_result.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}')
+        if artifact.image_diff:
+          print(f'Failed image diff: {artifact.image_diff.reason}')
+
+    # Report test result to ResultDB.
+    if self.resultdb:
+      only_artifacts = None
+      only_failure_reason = test_result.reason
+      if len(test_result.image_artifacts) == 1:
+        only = test_result.image_artifacts[0]
+        only_artifacts = only.GetDiffArtifacts()
+        if only.GetDiffReason():
+          only_failure_reason += f': {only.GetDiffReason()}'
+      self.resultdb.Post(
+          test_id=test_result.test_id,
+          status=test_result.status,
+          duration=test_result.duration_milliseconds,
+          test_log=test_result.log,
+          test_file=None,
+          artifacts=only_artifacts,
+          failure_reason=only_failure_reason)
+
+      # Milo only supports a single diff per test, so if we have multiple pages,
+      # report each page as its own "test."
+      if len(test_result.image_artifacts) > 1:
+        for page, artifact in enumerate(test_result.image_artifacts):
+          self.resultdb.Post(
+              test_id=f'{test_result.test_id}/{page}',
+              status=self._SuppressArtifactStatus(test_result,
+                                                  artifact.GetDiffStatus()),
+              duration=None,
+              test_log=None,
+              test_file=None,
+              artifacts=artifact.GetDiffArtifacts(),
+              failure_reason=artifact.GetDiffReason())
+
+  def _SuppressStatus(self, input_filename, status):
+    if not self.IsResultSuppressed(input_filename):
+      return status
+
+    if status == result_types.PASS:
+      # There isn't an actual status for succeeded-but-ignored, so use the
+      # "abort" status to differentiate this from failed-but-ignored.
+      #
+      # Note that this appears as a preliminary failure in Gerrit.
+      return result_types.UNKNOWN
+
+    # There isn't an actual status for failed-but-ignored, so use the "skip"
+    # status to differentiate this from succeeded-but-ignored.
+    return result_types.SKIP
+
+  def _SuppressArtifactStatus(self, test_result, status):
+    if status != result_types.FAIL:
+      return status
+
+    if test_result.status != result_types.SKIP:
+      return status
+
+    return result_types.SKIP
 
   def Run(self):
     # Running a test defines a number of attributes on the fly.
     # pylint: disable=attribute-defined-outside-init
 
-    parser = optparse.OptionParser()
+    relative_test_dir = self.per_process_config.test_dir
+    if relative_test_dir != 'corpus':
+      relative_test_dir = os.path.join('resources', relative_test_dir)
 
-    parser.add_option(
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument(
         '--build-dir',
         default=os.path.join('out', 'Debug'),
         help='relative path from the base source directory')
 
-    parser.add_option(
+    parser.add_argument(
         '-j',
         default=multiprocessing.cpu_count(),
         dest='num_workers',
-        type='int',
+        type=int,
         help='run NUM_WORKERS jobs in parallel')
 
-    parser.add_option(
+    parser.add_argument(
         '--disable-javascript',
-        action="store_true",
-        dest="disable_javascript",
+        action='store_true',
         help='Prevents JavaScript from executing in PDF files.')
 
-    parser.add_option(
+    parser.add_argument(
         '--disable-xfa',
-        action="store_true",
-        dest="disable_xfa",
+        action='store_true',
         help='Prevents processing XFA forms.')
 
-    parser.add_option(
-        '--gold_properties',
-        default='',
-        dest="gold_properties",
-        help='Key value pairs that are written to the top level '
-        'of the JSON file that is ingested by Gold.')
+    parser.add_argument(
+        '--render-oneshot',
+        action='store_true',
+        help='Sets whether to use the oneshot renderer.')
 
-    parser.add_option(
-        '--gold_key',
-        default='',
-        dest="gold_key",
-        help='Key value pairs that are added to the "key" field '
-        'of the JSON file that is ingested by Gold.')
+    parser.add_argument(
+        '--run-skia-gold',
+        action='store_true',
+        default=False,
+        help='When flag is on, skia gold tests will be run.')
 
-    parser.add_option(
-        '--gold_output_dir',
-        default='',
-        dest="gold_output_dir",
-        help='Path of where to write the JSON output to be '
-        'uploaded to Gold.')
-
-    parser.add_option(
-        '--gold_ignore_hashes',
-        default='',
-        dest="gold_ignore_hashes",
-        help='Path to a file with MD5 hashes we wish to ignore.')
-
-    parser.add_option(
+    parser.add_argument(
         '--regenerate_expected',
-        default='',
-        dest="regenerate_expected",
-        help='Regenerates expected images. Valid values are '
-        '"all" to regenerate all expected pngs, and '
-        '"platform" to regenerate only platform-specific '
-        'expected pngs.')
+        action='store_true',
+        help='Regenerates expected images. For each failing image diff, this '
+        'will regenerate the most specific expected image file that exists. '
+        'This also will suggest removals of unnecessary expected image files '
+        'by renaming them with an additional ".bak" extension, although these '
+        'removals should be reviewed manually. Use "git clean" to quickly deal '
+        'with any ".bak" files.')
 
-    parser.add_option(
+    parser.add_argument(
         '--reverse-byte-order',
         action='store_true',
-        dest="reverse_byte_order",
         help='Run image-based tests using --reverse-byte-order.')
 
-    parser.add_option(
+    parser.add_argument(
         '--ignore_errors',
-        action="store_true",
-        dest="ignore_errors",
+        action='store_true',
         help='Prevents the return value from being non-zero '
         'when image comparison fails.')
 
-    self.options, self.args = parser.parse_args()
+    parser.add_argument(
+        '--use-renderer',
+        choices=('agg', 'gdi', 'skia'),
+        help='Forces the renderer to use.')
 
-    if (self.options.regenerate_expected and
-        self.options.regenerate_expected not in ['all', 'platform']):
-      print 'FAILURE: --regenerate_expected must be "all" or "platform"'
+    parser.add_argument(
+        'inputted_file_paths',
+        nargs='*',
+        help='Path to test files to run, relative to '
+        f'testing/{relative_test_dir}. If omitted, runs all test files under '
+        f'testing/{relative_test_dir}.',
+        metavar='relative/test/path')
+
+    skia_gold.add_skia_gold_args(parser)
+
+    self.per_process_config.options = parser.parse_args()
+
+    finder = self.per_process_config.NewFinder()
+    pdfium_test_path = self.per_process_config.GetPdfiumTestPath(finder)
+    if not os.path.exists(pdfium_test_path):
+      print(f"FAILURE: Can't find test executable '{pdfium_test_path}'")
+      print('Use --build-dir to specify its location.')
       return 1
 
-    finder = common.DirectoryFinder(self.options.build_dir)
-    self.fixup_path = finder.ScriptPath('fixup_pdf_template.py')
-    self.text_diff_path = finder.ScriptPath('text_diff.py')
-    self.font_dir = os.path.join(finder.TestingDir(), 'resources', 'fonts')
-
-    self.source_dir = finder.TestingDir()
-    if self.test_dir != 'corpus':
-      test_dir = finder.TestingDir(os.path.join('resources', self.test_dir))
-    else:
-      test_dir = finder.TestingDir(self.test_dir)
-
-    self.pdfium_test_path = finder.ExecutablePath('pdfium_test')
-    if not os.path.exists(self.pdfium_test_path):
-      print "FAILURE: Can't find test executable '%s'" % self.pdfium_test_path
-      print 'Use --build-dir to specify its location.'
+    error_message = self.per_process_config.InitializeFeatures(pdfium_test_path)
+    if error_message:
+      print('FAILURE:', error_message)
       return 1
 
-    self.working_dir = finder.WorkingDir(os.path.join('testing', self.test_dir))
-    shutil.rmtree(self.working_dir, ignore_errors=True)
-    os.makedirs(self.working_dir)
+    self.per_process_state = _PerProcessState(self.per_process_config)
+    shutil.rmtree(self.per_process_state.working_dir, ignore_errors=True)
+    os.makedirs(self.per_process_state.working_dir)
 
-    self.feature_string = subprocess.check_output(
-        [self.pdfium_test_path, '--show-config'])
-    self.test_suppressor = suppressor.Suppressor(
-        finder, self.feature_string, self.options.disable_javascript,
-        self.options.disable_xfa)
-    self.image_differ = pngdiffer.PNGDiffer(finder,
-                                            self.options.reverse_byte_order)
-    error_message = self.image_differ.CheckMissingTools(
+    error_message = self.per_process_state.image_differ.CheckMissingTools(
         self.options.regenerate_expected)
     if error_message:
-      print "FAILURE: %s" % error_message
+      print('FAILURE:', error_message)
       return 1
 
-    self.gold_baseline = gold.GoldBaseline(self.options.gold_properties)
+    self.resultdb = result_sink.TryInitClient()
+    if self.resultdb:
+      print('Detected ResultSink environment')
 
-    walk_from_dir = finder.TestingDir(test_dir)
+    # Collect test cases.
+    walk_from_dir = finder.TestingDir(relative_test_dir)
 
-    self.test_cases = []
+    self.test_cases = TestCaseManager()
     self.execution_suppressed_cases = []
     input_file_re = re.compile('^.+[.](in|pdf)$')
-    if self.args:
-      for file_name in self.args:
-        file_name.replace('.pdf', '.in')
+    if self.options.inputted_file_paths:
+      for file_name in self.options.inputted_file_paths:
         input_path = os.path.join(walk_from_dir, file_name)
         if not os.path.isfile(input_path):
-          print "Can't find test file '%s'" % file_name
+          print(f"Can't find test file '{file_name}'")
           return 1
 
-        self.test_cases.append((os.path.basename(input_path),
-                                os.path.dirname(input_path)))
+        self.test_cases.NewTestCase(input_path)
     else:
       for file_dir, _, filename_list in os.walk(walk_from_dir):
         for input_filename in filename_list:
           if input_file_re.match(input_filename):
             input_path = os.path.join(file_dir, input_filename)
-            if self.test_suppressor.IsExecutionSuppressed(input_path):
+            if self.IsExecutionSuppressed(input_path):
               self.execution_suppressed_cases.append(input_path)
-            else:
-              if os.path.isfile(input_path):
-                self.test_cases.append((input_filename, file_dir))
+              continue
+            if not os.path.isfile(input_path):
+              continue
 
-    self.test_cases.sort()
+            self.test_cases.NewTestCase(input_path)
+
+    # Execute test cases.
     self.failures = []
     self.surprises = []
+    self.skia_gold_successes = []
+    self.skia_gold_unexpected_successes = []
+    self.skia_gold_failures = []
     self.result_suppressed_cases = []
 
-    # Collect Gold results if an output directory was named.
-    self.gold_results = None
-    if self.options.gold_output_dir:
-      self.gold_results = gold.GoldResults(
-          self.test_type, self.options.gold_output_dir,
-          self.options.gold_properties, self.options.gold_key,
-          self.options.gold_ignore_hashes)
+    if self.IsSkiaGoldEnabled():
+      assert self.options.gold_output_dir
+      # Clear out and create top level gold output directory before starting
+      skia_gold.clear_gold_output_dir(self.options.gold_output_dir)
 
-    if self.options.num_workers > 1 and len(self.test_cases) > 1:
-      try:
-        pool = multiprocessing.Pool(self.options.num_workers)
-        worker_func = functools.partial(TestOneFileParallel, self)
+    with multiprocessing.Pool(
+        processes=self.options.num_workers,
+        initializer=_InitializePerProcessState,
+        initargs=[self.per_process_config]) as pool:
+      if self.per_process_config.test_type in TEXT_TESTS:
+        test_function = _RunTextTest
+      else:
+        test_function = _RunPixelTest
+      for result in pool.imap(test_function, self.test_cases):
+        self.HandleResult(self.test_cases.GetTestCase(result.test_id), result)
 
-        worker_results = pool.imap(worker_func, self.test_cases)
-        for worker_result in worker_results:
-          result, input_filename, source_dir = worker_result
-          input_path = os.path.join(source_dir, input_filename)
-
-          self.HandleResult(input_filename, input_path, result)
-
-      except KeyboardInterrupt:
-        pool.terminate()
-      finally:
-        pool.close()
-        pool.join()
-    else:
-      for test_case in self.test_cases:
-        input_filename, input_file_dir = test_case
-        result = self.GenerateAndTest(input_filename, input_file_dir)
-        self.HandleResult(input_filename,
-                          os.path.join(input_file_dir, input_filename), result)
-
-    if self.gold_results:
-      self.gold_results.WriteResults()
-
+    # Report test results.
     if self.surprises:
       self.surprises.sort()
-      print '\n\nUnexpected Successes:'
+      print('\nUnexpected Successes:')
       for surprise in self.surprises:
-        print surprise
+        print(surprise)
 
     if self.failures:
       self.failures.sort()
-      print '\n\nSummary of Failures:'
+      print('\nSummary of Failures:')
       for failure in self.failures:
-        print failure
+        print(failure)
+
+    if self.skia_gold_unexpected_successes:
+      self.skia_gold_unexpected_successes.sort()
+      print('\nUnexpected Skia Gold Successes:')
+      for surprise in self.skia_gold_unexpected_successes:
+        print(surprise)
+
+    if self.skia_gold_failures:
+      self.skia_gold_failures.sort()
+      print('\nSummary of Skia Gold Failures:')
+      for failure in self.skia_gold_failures:
+        print(failure)
 
     self._PrintSummary()
 
@@ -462,23 +353,578 @@
     number_suppressed = len(self.result_suppressed_cases)
     number_successes = number_test_cases - number_failures - number_suppressed
     number_surprises = len(self.surprises)
-    print
-    print 'Test cases executed: %d' % number_test_cases
-    print '  Successes: %d' % number_successes
-    print '  Suppressed: %d' % number_suppressed
-    print '    Surprises: %d' % number_surprises
-    print '  Failures: %d' % number_failures
-    print
-    print 'Test cases not executed: %d' % len(self.execution_suppressed_cases)
+    print('\nTest cases executed:', number_test_cases)
+    print('  Successes:', number_successes)
+    print('  Suppressed:', number_suppressed)
+    print('  Surprises:', number_surprises)
+    print('  Failures:', number_failures)
+    if self.IsSkiaGoldEnabled():
+      number_gold_failures = len(self.skia_gold_failures)
+      number_gold_successes = len(self.skia_gold_successes)
+      number_gold_surprises = len(self.skia_gold_unexpected_successes)
+      number_total_gold_tests = sum(
+          [number_gold_failures, number_gold_successes, number_gold_surprises])
+      print('\nSkia Gold Test cases executed:', number_total_gold_tests)
+      print('  Skia Gold Successes:', number_gold_successes)
+      print('  Skia Gold Surprises:', number_gold_surprises)
+      print('  Skia Gold Failures:', number_gold_failures)
+      skia_tester = self.per_process_state.GetSkiaGoldTester()
+      if self.skia_gold_failures and skia_tester.IsTryjobRun():
+        cl_triage_link = skia_tester.GetCLTriageLink()
+        print('  Triage link for CL:', cl_triage_link)
+        skia_tester.WriteCLTriageLink(cl_triage_link)
+    print()
+    print('Test cases not executed:', len(self.execution_suppressed_cases))
 
   def SetDeleteOutputOnSuccess(self, new_value):
     """Set whether to delete generated output if the test passes."""
-    self.delete_output_on_success = new_value
+    self.per_process_config.delete_output_on_success = new_value
 
   def SetEnforceExpectedImages(self, new_value):
     """Set whether to enforce that each test case provide an expected image."""
-    self.enforce_expected_images = new_value
+    self.per_process_config.enforce_expected_images = new_value
 
-  def SetOneShotRenderer(self, new_value):
-    """Set whether to use the oneshot renderer. """
-    self.oneshot_renderer = new_value
+
+def _RunTextTest(test_case):
+  """Runs a text test case."""
+  test_case_runner = _TestCaseRunner(test_case)
+  with test_case_runner:
+    test_case_runner.test_result = test_case_runner.GenerateAndTest(
+        test_case_runner.TestText)
+  return test_case_runner.test_result
+
+
+def _RunPixelTest(test_case):
+  """Runs a pixel test case."""
+  test_case_runner = _TestCaseRunner(test_case)
+  with test_case_runner:
+    test_case_runner.test_result = test_case_runner.GenerateAndTest(
+        test_case_runner.TestPixel)
+  return test_case_runner.test_result
+
+
+# `_PerProcessState` singleton. This is initialized when creating the
+# `multiprocessing.Pool()`. `TestRunner.Run()` creates its own separate
+# instance of `_PerProcessState` as well.
+_per_process_state = None
+
+
+def _InitializePerProcessState(config):
+  """Initializes the `_per_process_state` singleton."""
+  global _per_process_state
+  assert not _per_process_state
+  _per_process_state = _PerProcessState(config)
+
+
+@dataclass
+class _PerProcessConfig:
+  """Configuration for initializing `_PerProcessState`.
+
+  Attributes:
+    test_dir: The name of the test directory.
+    test_type: The test type.
+    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 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: set = None
+  rendering_option: str = None
+
+  def NewFinder(self):
+    return common.DirectoryFinder(self.options.build_dir)
+
+  def GetPdfiumTestPath(self, finder):
+    return finder.ExecutablePath('pdfium_test')
+
+  def InitializeFeatures(self, pdfium_test_path):
+    output = subprocess.check_output([pdfium_test_path, '--show-config'],
+                                     timeout=TEST_TIMEOUT)
+    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:
+  """State defined per process."""
+
+  def __init__(self, config):
+    self.test_dir = config.test_dir
+    self.test_type = config.test_type
+    self.delete_output_on_success = config.delete_output_on_success
+    self.enforce_expected_images = config.enforce_expected_images
+    self.options = config.options
+    self.features = config.features
+
+    finder = config.NewFinder()
+    self.pdfium_test_path = config.GetPdfiumTestPath(finder)
+    self.fixup_path = finder.ScriptPath('fixup_pdf_template.py')
+    self.text_diff_path = finder.ScriptPath('text_diff.py')
+    self.font_dir = os.path.join(finder.TestingDir(), 'resources', 'fonts')
+    self.third_party_font_dir = finder.ThirdPartyFontsDir()
+
+    self.source_dir = finder.TestingDir()
+    self.working_dir = finder.WorkingDir(os.path.join('testing', self.test_dir))
+
+    self.test_suppressor = suppressor.Suppressor(
+        finder, self.features, self.options.disable_javascript,
+        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
+
+  def __getstate__(self):
+    raise RuntimeError('Cannot pickle per-process state')
+
+  def GetSkiaGoldTester(self):
+    """Gets the `SkiaGoldTester` singleton for this worker."""
+    if not self.skia_tester:
+      self.skia_tester = skia_gold.SkiaGoldTester(
+          source_type=self.test_type,
+          skia_gold_args=self.options,
+          process_name=self.process_name)
+    return self.skia_tester
+
+
+class _TestCaseRunner:
+  """Runner for a single test case."""
+
+  def __init__(self, test_case):
+    self.test_case = test_case
+    self.test_result = None
+    self.duration_start = 0
+
+    self.source_dir, self.input_filename = os.path.split(
+        self.test_case.input_path)
+    self.pdf_path = os.path.join(self.working_dir, f'{self.test_id}.pdf')
+    self.actual_images = None
+
+  def __enter__(self):
+    self.duration_start = time.perf_counter_ns()
+    return self
+
+  def __exit__(self, exc_type, exc_value, traceback):
+    if not self.test_result:
+      self.test_result = self.test_case.NewResult(
+          result_types.UNKNOWN, reason='No test result recorded')
+    duration = time.perf_counter_ns() - self.duration_start
+    self.test_result.duration_milliseconds = duration * 1e-6
+
+  @property
+  def options(self):
+    return _per_process_state.options
+
+  @property
+  def test_id(self):
+    return self.test_case.test_id
+
+  @property
+  def working_dir(self):
+    return _per_process_state.working_dir
+
+  def IsResultSuppressed(self):
+    return _per_process_state.test_suppressor.IsResultSuppressed(
+        self.input_filename)
+
+  def IsImageDiffSuppressed(self):
+    return _per_process_state.test_suppressor.IsImageDiffSuppressed(
+        self.input_filename)
+
+  def GetImageMatchingAlgorithm(self):
+    return _per_process_state.test_suppressor.GetImageMatchingAlgorithm(
+        self.input_filename)
+
+  def RunCommand(self, command, stdout=None):
+    """Runs a test command.
+
+    Args:
+      command: The list of command arguments.
+      stdout: Optional `file`-like object to send standard output.
+
+    Returns:
+      The test result.
+    """
+
+    # Standard output and error are directed to the test log. If `stdout` was
+    # provided, redirect standard output to it instead.
+    if stdout:
+      assert stdout != subprocess.PIPE
+      try:
+        stdout.fileno()
+      except OSError:
+        # `stdout` doesn't have a file descriptor, so it can't be passed to
+        # `subprocess.run()` directly.
+        original_stdout = stdout
+        stdout = subprocess.PIPE
+      stderr = subprocess.PIPE
+    else:
+      stdout = subprocess.PIPE
+      stderr = subprocess.STDOUT
+
+    test_result = self.test_case.NewResult(result_types.PASS)
+    try:
+      run_result = subprocess.run(
+          command,
+          stdout=stdout,
+          stderr=stderr,
+          timeout=TEST_TIMEOUT,
+          check=False)
+      if run_result.returncode != 0:
+        test_result.status = result_types.FAIL
+        test_result.reason = 'Command {} exited with code {}'.format(
+            run_result.args, run_result.returncode)
+    except subprocess.TimeoutExpired as timeout_expired:
+      run_result = timeout_expired
+      test_result.status = result_types.TIMEOUT
+      test_result.reason = 'Command {} timed out'.format(run_result.cmd)
+
+    if stdout == subprocess.PIPE and stderr == subprocess.PIPE:
+      # Copy captured standard output, if any, to the original `stdout`.
+      if run_result.stdout:
+        original_stdout.write(run_result.stdout)
+
+    if not test_result.IsPass():
+      # On failure, report captured output to the test log.
+      if stderr == subprocess.STDOUT:
+        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):
+    """Generate test input and run pdfium_test."""
+    test_result = self.Generate()
+    if not test_result.IsPass():
+      return test_result
+
+    return test_function()
+
+  def _RegenerateIfNeeded(self):
+    if not self.options.regenerate_expected:
+      return
+    if self.IsResultSuppressed() or self.IsImageDiffSuppressed():
+      return
+    _per_process_state.image_differ.Regenerate(
+        self.input_filename,
+        self.source_dir,
+        self.working_dir,
+        image_matching_algorithm=self.GetImageMatchingAlgorithm())
+
+  def Generate(self):
+    input_event_path = os.path.join(self.source_dir, f'{self.test_id}.evt')
+    if os.path.exists(input_event_path):
+      output_event_path = f'{os.path.splitext(self.pdf_path)[0]}.evt'
+      shutil.copyfile(input_event_path, output_event_path)
+
+    template_path = os.path.join(self.source_dir, f'{self.test_id}.in')
+    if not os.path.exists(template_path):
+      if os.path.exists(self.test_case.input_path):
+        shutil.copyfile(self.test_case.input_path, self.pdf_path)
+      return self.test_case.NewResult(result_types.PASS)
+
+    return self.RunCommand([
+        sys.executable, _per_process_state.fixup_path,
+        f'--output-dir={self.working_dir}', template_path
+    ])
+
+  def TestText(self):
+    txt_path = os.path.join(self.working_dir, f'{self.test_id}.txt')
+    with open(txt_path, 'w') as outfile:
+      cmd_to_run = [
+          _per_process_state.pdfium_test_path, '--send-events',
+          f'--time={TEST_SEED_TIME}'
+      ]
+
+      if self.options.disable_javascript:
+        cmd_to_run.append('--disable-javascript')
+
+      if self.options.disable_xfa:
+        cmd_to_run.append('--disable-xfa')
+
+      cmd_to_run.append(self.pdf_path)
+      test_result = self.RunCommand(cmd_to_run, stdout=outfile)
+      if not test_result.IsPass():
+        return test_result
+
+    # If the expected file does not exist, the output is expected to be empty.
+    expected_txt_path = os.path.join(self.source_dir,
+                                     f'{self.test_id}_expected.txt')
+    if not os.path.exists(expected_txt_path):
+      return self._VerifyEmptyText(txt_path)
+
+    # If JavaScript is disabled, the output should be empty.
+    # However, if the test is suppressed and JavaScript is disabled, do not
+    # verify that the text is empty so the suppressed test does not surprise.
+    if self.options.disable_javascript and not self.IsResultSuppressed():
+      return self._VerifyEmptyText(txt_path)
+
+    return self.RunCommand([
+        sys.executable, _per_process_state.text_diff_path, expected_txt_path,
+        txt_path
+    ])
+
+  def _VerifyEmptyText(self, txt_path):
+    with open(txt_path, "rb") as txt_file:
+      txt_data = txt_file.read()
+
+    if txt_data:
+      return self.test_case.NewResult(
+          result_types.FAIL,
+          log=txt_data.decode(errors='backslashreplace'),
+          reason=f'{txt_path} should be empty')
+
+    return self.test_case.NewResult(result_types.PASS)
+
+  # TODO(crbug.com/pdfium/1656): Remove when ready to fully switch over to
+  # Skia Gold
+  def TestPixel(self):
+    # Remove any existing generated images from previous runs.
+    self.actual_images = _per_process_state.image_differ.GetActualFiles(
+        self.input_filename, self.source_dir, self.working_dir)
+    self._CleanupPixelTest()
+
+    # Generate images.
+    cmd_to_run = [
+        _per_process_state.pdfium_test_path, '--send-events', '--png', '--md5',
+        f'--time={TEST_SEED_TIME}'
+    ]
+
+    if 'use_ahem' in self.source_dir or 'use_symbolneu' in self.source_dir:
+      cmd_to_run.append(f'--font-dir={_per_process_state.font_dir}')
+    else:
+      cmd_to_run.append(f'--font-dir={_per_process_state.third_party_font_dir}')
+      cmd_to_run.append('--croscore-font-names')
+
+    if self.options.disable_javascript:
+      cmd_to_run.append('--disable-javascript')
+
+    if self.options.disable_xfa:
+      cmd_to_run.append('--disable-xfa')
+
+    if self.options.render_oneshot:
+      cmd_to_run.append('--render-oneshot')
+
+    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:
+      test_result = self.RunCommand(cmd_to_run, stdout=command_output)
+      if not test_result.IsPass():
+        return test_result
+
+      test_result.image_artifacts = []
+      for line in command_output.getvalue().splitlines():
+        # Expect this format: MD5:<path to image file>:<hexadecimal MD5 hash>
+        line = bytes.decode(line).strip()
+        if line.startswith('MD5:'):
+          image_path, md5_hash = line[4:].rsplit(':', 1)
+          test_result.image_artifacts.append(
+              self._NewImageArtifact(
+                  image_path=image_path.strip(), md5_hash=md5_hash.strip()))
+
+    if self.actual_images:
+      image_diffs = _per_process_state.image_differ.ComputeDifferences(
+          self.input_filename,
+          self.source_dir,
+          self.working_dir,
+          image_matching_algorithm=self.GetImageMatchingAlgorithm())
+      if image_diffs:
+        test_result.status = result_types.FAIL
+        test_result.reason = 'Images differ'
+
+        # Merge image diffs into test result.
+        diff_map = {}
+        diff_log = []
+        for diff in image_diffs:
+          diff_map[diff.actual_path] = diff
+          diff_log.append(f'{os.path.basename(diff.actual_path)} vs. ')
+          if diff.expected_path:
+            diff_log.append(f'{os.path.basename(diff.expected_path)}\n')
+          else:
+            diff_log.append('missing expected file\n')
+
+        for artifact in test_result.image_artifacts:
+          artifact.image_diff = diff_map.get(artifact.image_path)
+        test_result.log = ''.join(diff_log)
+
+    elif _per_process_state.enforce_expected_images:
+      if not self.IsImageDiffSuppressed():
+        test_result.status = result_types.FAIL
+        test_result.reason = 'Missing expected images'
+
+    if not test_result.IsPass():
+      self._RegenerateIfNeeded()
+      return test_result
+
+    if _per_process_state.delete_output_on_success:
+      self._CleanupPixelTest()
+    return test_result
+
+  def _NewImageArtifact(self, *, image_path, md5_hash):
+    artifact = ImageArtifact(image_path=image_path, md5_hash=md5_hash)
+
+    if self.options.run_skia_gold:
+      if _per_process_state.GetSkiaGoldTester().UploadTestResultToSkiaGold(
+          artifact.GetSkiaGoldId(), artifact.image_path):
+        artifact.skia_gold_status = result_types.PASS
+      else:
+        artifact.skia_gold_status = result_types.FAIL
+
+    return artifact
+
+  def _CleanupPixelTest(self):
+    for image_file in self.actual_images:
+      if os.path.exists(image_file):
+        os.remove(image_file)
+
+
+@dataclass
+class TestCase:
+  """Description of a test case to run.
+
+  Attributes:
+    test_id: A unique identifier for the test.
+    input_path: The absolute path to the test file.
+  """
+  test_id: str
+  input_path: str
+
+  def NewResult(self, status, **kwargs):
+    """Derives a new test result corresponding to this test case."""
+    return TestResult(test_id=self.test_id, status=status, **kwargs)
+
+
+@dataclass
+class TestResult:
+  """Results from running a test case.
+
+  Attributes:
+    test_id: The corresponding test case ID.
+    status: The overall `result_types` status.
+    duration_milliseconds: Test time in milliseconds.
+    log: Optional log of the test's output.
+    image_artfacts: Optional list of image artifacts.
+    reason: Optional reason why the test failed.
+  """
+  test_id: str
+  status: str
+  duration_milliseconds: float = None
+  log: str = None
+  image_artifacts: list = field(default_factory=list)
+  reason: str = None
+
+  def IsPass(self):
+    """Whether the test passed."""
+    return self.status == result_types.PASS
+
+
+@dataclass
+class ImageArtifact:
+  """Image artifact for a test result.
+
+  Attributes:
+    image_path: The absolute path to the image file.
+    md5_hash: The MD5 hash of the pixel buffer.
+    skia_gold_status: Optional Skia Gold status.
+    image_diff: Optional image diff.
+  """
+  image_path: str
+  md5_hash: str
+  skia_gold_status: str = None
+  image_diff: pngdiffer.ImageDiff = None
+
+  def GetSkiaGoldId(self):
+    # The output filename without image extension becomes the test ID. For
+    # example, "/path/to/.../testing/corpus/example_005.pdf.0.png" becomes
+    # "example_005.pdf.0".
+    return _GetTestId(os.path.basename(self.image_path))
+
+  def GetDiffStatus(self):
+    return result_types.FAIL if self.image_diff else result_types.PASS
+
+  def GetDiffReason(self):
+    return self.image_diff.reason if self.image_diff else None
+
+  def GetDiffArtifacts(self):
+    if not self.image_diff:
+      return None
+    if not self.image_diff.expected_path or not self.image_diff.diff_path:
+      return None
+    return {
+        'actual_image':
+            _GetArtifactFromFilePath(self.image_path),
+        'expected_image':
+            _GetArtifactFromFilePath(self.image_diff.expected_path),
+        'image_diff':
+            _GetArtifactFromFilePath(self.image_diff.diff_path)
+    }
+
+
+class TestCaseManager:
+  """Manages a collection of test cases."""
+
+  def __init__(self):
+    self.test_cases = {}
+
+  def __len__(self):
+    return len(self.test_cases)
+
+  def __iter__(self):
+    return iter(self.test_cases.values())
+
+  def NewTestCase(self, input_path, **kwargs):
+    """Creates and registers a new test case."""
+    input_basename = os.path.basename(input_path)
+    test_id = _GetTestId(input_basename)
+    if test_id in self.test_cases:
+      raise ValueError(
+          f'Test ID "{test_id}" derived from "{input_basename}" must be unique')
+
+    test_case = TestCase(test_id=test_id, input_path=input_path, **kwargs)
+    self.test_cases[test_id] = test_case
+    return test_case
+
+  def GetTestCase(self, test_id):
+    """Looks up a test case previously registered by `NewTestCase()`."""
+    return self.test_cases[test_id]
+
+
+def _GetTestId(input_basename):
+  """Constructs a test ID by stripping the last extension from the basename."""
+  return os.path.splitext(input_basename)[0]
+
+
+def _GetArtifactFromFilePath(file_path):
+  """Constructs a ResultSink artifact from a file path."""
+  return {'filePath': file_path}
diff --git a/testing/tools/text_diff.py b/testing/tools/text_diff.py
index fdf45a0..7dcb1c2 100755
--- a/testing/tools/text_diff.py
+++ b/testing/tools/text_diff.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2015 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2015 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 
 def main(argv):
   if len(argv) != 3:
-    print '%s: invalid arguments' % argv[0]
+    print('%s: invalid arguments' % argv[0])
     return 2
   filename1 = argv[1]
   filename2 = argv[2]
@@ -21,7 +21,7 @@
     diffs = difflib.unified_diff(
         str1, str2, fromfile=filename1, tofile=filename2)
   except Exception as e:
-    print "something went astray: %s" % e
+    print("something went astray: %s" % e)
     return 1
   status_code = 0
   for diff in diffs:
diff --git a/testing/unit_test_main.cpp b/testing/unit_test_main.cpp
index 5d50249..28476b0 100644
--- a/testing/unit_test_main.cpp
+++ b/testing/unit_test_main.cpp
@@ -1,57 +1,46 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
-#include <string>
-
 #include "core/fxcrt/fx_memory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.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_initializer.h"
-#include "v8/include/v8-platform.h"
-#include "v8/include/v8.h"
-#endif  // PDF_ENABLE_V8
-
+#include "testing/v8_test_environment.h"
 #ifdef PDF_ENABLE_XFA
-#include "testing/xfa_unit_test_support.h"
+#include "testing/xfa_test_environment.h"
 #endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
 
 // Can't use gtest-provided main since we need to initialize partition
-// alloc before invoking any test.
+// alloc before invoking any test, and add test environments.
 int main(int argc, char** argv) {
-  FXMEM_InitializePartitionAlloc();
+#if defined(PDF_USE_PARTITION_ALLOC)
+  pdfium::ConfigurePartitionAllocShimPartitionForTest();
+#endif  // defined(PDF_USE_PARTITION_ALLOC)
+
+  FX_InitializeMemoryAllocators();
+
+  // PDF test environment will be deleted by gtest.
+  AddGlobalTestEnvironment(new PDFTestEnvironment());
 
 #ifdef PDF_ENABLE_V8
-  std::unique_ptr<v8::Platform> platform;
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
-  static v8::StartupData* snapshot = new v8::StartupData;
-  platform =
-      InitializeV8ForPDFiumWithStartupData(argv[0], std::string(), snapshot);
-#else  // V8_USE_EXTERNAL_STARTUP_DATA
-  platform = InitializeV8ForPDFium(argv[0]);
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-#endif  // PDF_ENABLE_V8
-
-  InitializePDFTestEnvironment();
+  // V8 test environment will be deleted by gtest.
+  AddGlobalTestEnvironment(new V8TestEnvironment(argv[0]));
 #ifdef PDF_ENABLE_XFA
-  InitializeXFATestEnvironment();
+  // XFA test environment will be deleted by gtest.
+  AddGlobalTestEnvironment(new XFATestEnvironment());
 #endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
 
   testing::InitGoogleTest(&argc, argv);
   testing::InitGoogleMock(&argc, argv);
 
-  int ret_val = RUN_ALL_TESTS();
-
-  DestroyPDFTestEnvironment();
-  // NOTE: XFA test environment, if present, destroyed by gtest.
-
-#ifdef PDF_ENABLE_V8
-  v8::V8::ShutdownPlatform();
-#endif  // PDF_ENABLE_V8
-
-  return ret_val;
+  return RUN_ALL_TESTS();
 }
diff --git a/testing/utils/bitmap_saver.cpp b/testing/utils/bitmap_saver.cpp
index 524a9f6..40a1339 100644
--- a/testing/utils/bitmap_saver.cpp
+++ b/testing/utils/bitmap_saver.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 
 #include "core/fxcrt/fx_safe_types.h"
 #include "testing/image_diff/image_diff_png.h"
-#include "third_party/base/logging.h"
+#include "third_party/base/check.h"
 
 // static
 void BitmapSaver::WriteBitmapToPng(FPDF_BITMAP bitmap,
@@ -27,8 +27,11 @@
       pdfium::base::ValueOrDieForType<size_t>(size));
 
   std::vector<uint8_t> png;
-  if (FPDFBitmap_GetFormat(bitmap) == FPDFBitmap_Gray) {
+  int format = FPDFBitmap_GetFormat(bitmap);
+  if (format == FPDFBitmap_Gray) {
     png = image_diff_png::EncodeGrayPNG(input, width, height, stride);
+  } else if (format == FPDFBitmap_BGR) {
+    png = image_diff_png::EncodeBGRPNG(input, width, height, stride);
   } else {
     png = image_diff_png::EncodeBGRAPNG(input, width, height, stride,
                                         /*discard_transparency=*/false);
diff --git a/testing/utils/bitmap_saver.h b/testing/utils/bitmap_saver.h
index 9f931fc..8a9b2c6 100644
--- a/testing/utils/bitmap_saver.h
+++ b/testing/utils/bitmap_saver.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/utils/file_util.cpp b/testing/utils/file_util.cpp
index 5d26d98..6998f0e 100644
--- a/testing/utils/file_util.cpp
+++ b/testing/utils/file_util.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,6 +8,7 @@
 #include <string.h>
 
 #include "testing/utils/path_service.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 std::unique_ptr<char, pdfium::FreeDeleter> GetFileContents(const char* filename,
                                                            size_t* retlen) {
@@ -55,7 +56,7 @@
                                        unsigned char* pBuf,
                                        unsigned long size) {
   memcpy(pBuf, file_contents_.get() + pos, size);
-  return size;
+  return pdfium::base::checked_cast<int>(size);
 }
 
 // static
diff --git a/testing/utils/file_util.h b/testing/utils/file_util.h
index 352b882..e91f3d4 100644
--- a/testing/utils/file_util.h
+++ b/testing/utils/file_util.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/testing/utils/hash.cpp b/testing/utils/hash.cpp
index 3170de2..5b4a2cf 100644
--- a/testing/utils/hash.cpp
+++ b/testing/utils/hash.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,8 +18,8 @@
   return ret;
 }
 
-std::string GenerateMD5Base16(const uint8_t* data, uint32_t size) {
+std::string GenerateMD5Base16(pdfium::span<const uint8_t> data) {
   uint8_t digest[16];
-  CRYPT_MD5Generate({data, size}, digest);
+  CRYPT_MD5Generate(data, digest);
   return CryptToBase16(digest);
 }
diff --git a/testing/utils/hash.h b/testing/utils/hash.h
index 0e55165..e508993 100644
--- a/testing/utils/hash.h
+++ b/testing/utils/hash.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,9 @@
 
 #include <string>
 
+#include "third_party/base/containers/span.h"
+
 std::string CryptToBase16(const uint8_t* digest);
-std::string GenerateMD5Base16(const uint8_t* data, uint32_t size);
+std::string GenerateMD5Base16(pdfium::span<const uint8_t> data);
 
 #endif  // TESTING_UTILS_HASH_H_
diff --git a/testing/utils/path_service.cpp b/testing/utils/path_service.cpp
index 5e1ce39..01a1bfe 100644
--- a/testing/utils/path_service.cpp
+++ b/testing/utils/path_service.cpp
@@ -1,14 +1,19 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "testing/utils/path_service.h"
 
+#include <stddef.h>
+
 #ifdef _WIN32
 #include <Windows.h>
 #elif defined(__APPLE__)
 #include <mach-o/dyld.h>
 #include <sys/stat.h>
+#elif defined(__Fuchsia__)
+#include <sys/stat.h>
+#include <unistd.h>
 #else  // Linux
 #include <linux/limits.h>
 #include <sys/stat.h>
@@ -18,10 +23,12 @@
 #include <string>
 
 #include "core/fxcrt/fx_system.h"
+#include "third_party/base/check.h"
 
 namespace {
 
-#if defined(__APPLE__) || (defined(ANDROID) && __ANDROID_API__ < 21)
+#if defined(__APPLE__) || defined(__Fuchsia__) || \
+    (defined(ANDROID) && __ANDROID_API__ < 21)
 using stat_wrapper_t = struct stat;
 
 int CallStat(const char* path, stat_wrapper_t* sb) {
@@ -68,7 +75,7 @@
     return false;
   *path = std::string(path_buffer);
 #elif defined(__APPLE__)
-  ASSERT(path);
+  DCHECK(path);
   unsigned int path_length = 0;
   _NSGetExecutablePath(NULL, &path_length);
   if (path_length == 0)
@@ -89,10 +96,10 @@
 #endif  // _WIN32
 
   // Get the directory path.
-  std::size_t pos = path->size() - 1;
+  size_t pos = path->size() - 1;
   if (EndsWithSeparator(*path))
     pos--;
-  std::size_t found = path->find_last_of(PATH_SEPARATOR, pos);
+  size_t found = path->find_last_of(PATH_SEPARATOR, pos);
   if (found == std::string::npos)
     return false;
   path->resize(found);
@@ -160,3 +167,39 @@
   path->append(file_name);
   return true;
 }
+
+// static
+bool PathService::GetThirdPartyFilePath(const std::string& file_name,
+                                        std::string* path) {
+  if (!GetSourceDir(path))
+    return false;
+
+  if (!EndsWithSeparator(*path))
+    path->push_back(PATH_SEPARATOR);
+
+  std::string potential_path = *path;
+  potential_path.append("third_party");
+
+  // Use third_party/bigint as a way to distinguish PDFium's vs. other's.
+  std::string bigint = potential_path + PATH_SEPARATOR + "bigint";
+  if (PathService::DirectoryExists(bigint)) {
+    *path = potential_path;
+    path->append(PATH_SEPARATOR + file_name);
+    return true;
+  }
+
+  potential_path = *path;
+  potential_path.append("third_party");
+  potential_path.push_back(PATH_SEPARATOR);
+  potential_path.append("pdfium");
+  potential_path.push_back(PATH_SEPARATOR);
+  potential_path.append("third_party");
+  bigint = potential_path + PATH_SEPARATOR + "bigint";
+  if (PathService::DirectoryExists(potential_path)) {
+    *path = potential_path;
+    path->append(PATH_SEPARATOR + file_name);
+    return true;
+  }
+
+  return false;
+}
diff --git a/testing/utils/path_service.h b/testing/utils/path_service.h
index 63df808..a6582c3 100644
--- a/testing/utils/path_service.h
+++ b/testing/utils/path_service.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -37,5 +37,9 @@
 
   // Get the full path for a test file under the test data directory.
   static bool GetTestFilePath(const std::string& file_name, std::string* path);
+
+  // Get the full path for a file under the third-party directory.
+  static bool GetThirdPartyFilePath(const std::string& file_name,
+                                    std::string* path);
 };
 #endif  // TESTING_UTILS_PATH_SERVICE_H_
diff --git a/testing/v8_initializer.cpp b/testing/v8_initializer.cpp
index 8564b2e..f4ed611 100644
--- a/testing/v8_initializer.cpp
+++ b/testing/v8_initializer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,8 +9,14 @@
 #include "public/fpdfview.h"
 #include "testing/utils/file_util.h"
 #include "testing/utils/path_service.h"
+#include "third_party/base/numerics/safe_conversions.h"
 #include "v8/include/libplatform/libplatform.h"
-#include "v8/include/v8.h"
+#include "v8/include/v8-initialization.h"
+#include "v8/include/v8-snapshot.h"
+
+#ifdef PDF_ENABLE_XFA
+#include "v8/include/cppgc/platform.h"
+#endif
 
 namespace {
 
@@ -49,20 +55,27 @@
     return false;
 
   result_data->data = data_buffer.release();
-  result_data->raw_size = data_length;
+  result_data->raw_size = pdfium::base::checked_cast<int>(data_length);
   return true;
 }
 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
 
-std::unique_ptr<v8::Platform> InitializeV8Common(const std::string& exe_path) {
+std::unique_ptr<v8::Platform> InitializeV8Common(const std::string& exe_path,
+                                                 const std::string& js_flags) {
   v8::V8::InitializeICUDefaultLocation(exe_path.c_str());
 
   std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
   v8::V8::InitializePlatform(platform.get());
+#ifdef PDF_ENABLE_XFA
+  cppgc::InitializeProcess(platform->GetPageAllocator());
+#endif
 
   const char* recommended_v8_flags = FPDF_GetRecommendedV8Flags();
   v8::V8::SetFlagsFromString(recommended_v8_flags);
 
+  if (!js_flags.empty())
+    v8::V8::SetFlagsFromString(js_flags.c_str());
+
   // By enabling predictable mode, V8 won't post any background tasks.
   // By enabling GC, it makes it easier to chase use-after-free.
   static const char kAdditionalV8Flags[] = "--predictable --expose-gc";
@@ -77,9 +90,11 @@
 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
 std::unique_ptr<v8::Platform> InitializeV8ForPDFiumWithStartupData(
     const std::string& exe_path,
+    const std::string& js_flags,
     const std::string& bin_dir,
     v8::StartupData* snapshot_blob) {
-  std::unique_ptr<v8::Platform> platform = InitializeV8Common(exe_path);
+  std::unique_ptr<v8::Platform> platform =
+      InitializeV8Common(exe_path, js_flags);
   if (snapshot_blob) {
     if (!GetExternalData(exe_path, bin_dir, "snapshot_blob.bin", snapshot_blob))
       return nullptr;
@@ -89,7 +104,16 @@
 }
 #else   // V8_USE_EXTERNAL_STARTUP_DATA
 std::unique_ptr<v8::Platform> InitializeV8ForPDFium(
-    const std::string& exe_path) {
-  return InitializeV8Common(exe_path);
+    const std::string& exe_path,
+    const std::string& js_flags) {
+  return InitializeV8Common(exe_path, js_flags);
 }
 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
+
+void ShutdownV8ForPDFium() {
+#ifdef PDF_ENABLE_XFA
+  cppgc::ShutdownProcess();
+#endif
+  v8::V8::Dispose();
+  v8::V8::DisposePlatform();
+}
diff --git a/testing/v8_initializer.h b/testing/v8_initializer.h
index bd2301c..ecd5911 100644
--- a/testing/v8_initializer.h
+++ b/testing/v8_initializer.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,11 +23,13 @@
 // |snapshot_blob| is an optional out parameter.
 std::unique_ptr<v8::Platform> InitializeV8ForPDFiumWithStartupData(
     const std::string& exe_path,
+    const std::string& js_flags,
     const std::string& bin_dir,
     v8::StartupData* snapshot_blob);
 #else
 std::unique_ptr<v8::Platform> InitializeV8ForPDFium(
+    const std::string& js_flags,
     const std::string& exe_path);
 #endif
-
+void ShutdownV8ForPDFium();
 #endif  // TESTING_V8_INITIALIZER_H_
diff --git a/testing/v8_test_environment.cpp b/testing/v8_test_environment.cpp
new file mode 100644
index 0000000..56eead7
--- /dev/null
+++ b/testing/v8_test_environment.cpp
@@ -0,0 +1,76 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/v8_test_environment.h"
+
+#include <memory>
+#include <string>
+
+#include "core/fxcrt/fx_system.h"
+#include "testing/v8_initializer.h"
+#include "third_party/base/check.h"
+#include "v8/include/libplatform/libplatform.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-platform.h"
+#include "v8/include/v8-snapshot.h"
+
+namespace {
+
+V8TestEnvironment* g_environment = nullptr;
+
+}  // namespace
+
+V8TestEnvironment::V8TestEnvironment(const char* exe_name)
+    : exe_path_(exe_name),
+      array_buffer_allocator_(std::make_unique<CFX_V8ArrayBufferAllocator>()) {
+  DCHECK(!g_environment);
+  g_environment = this;
+}
+
+V8TestEnvironment::~V8TestEnvironment() {
+  DCHECK(g_environment);
+
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+  if (startup_data_)
+    free(const_cast<char*>(startup_data_->data));
+#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+
+  g_environment = nullptr;
+}
+
+// static
+V8TestEnvironment* V8TestEnvironment::GetInstance() {
+  return g_environment;
+}
+
+// static
+void V8TestEnvironment::PumpPlatformMessageLoop(v8::Isolate* isolate) {
+  v8::Platform* platform = GetInstance()->platform();
+  while (v8::platform::PumpMessageLoop(platform, isolate))
+    continue;
+}
+
+void V8TestEnvironment::SetUp() {
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+  if (startup_data_) {
+    platform_ = InitializeV8ForPDFiumWithStartupData(exe_path_, std::string(),
+                                                     std::string(), nullptr);
+  } else {
+    startup_data_ = std::make_unique<v8::StartupData>();
+    platform_ = InitializeV8ForPDFiumWithStartupData(
+        exe_path_, std::string(), std::string(), startup_data_.get());
+  }
+#else
+  platform_ = InitializeV8ForPDFium(std::string(), exe_path_);
+#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+
+  v8::Isolate::CreateParams params;
+  params.array_buffer_allocator = array_buffer_allocator_.get();
+  isolate_.reset(v8::Isolate::New(params));
+}
+
+void V8TestEnvironment::TearDown() {
+  isolate_.reset();
+  ShutdownV8ForPDFium();
+}
diff --git a/testing/v8_test_environment.h b/testing/v8_test_environment.h
new file mode 100644
index 0000000..7a030e4
--- /dev/null
+++ b/testing/v8_test_environment.h
@@ -0,0 +1,51 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_V8_TEST_ENVIRONMENT_H_
+#define TESTING_V8_TEST_ENVIRONMENT_H_
+
+#ifndef PDF_ENABLE_V8
+#error "V8 must be enabled"
+#endif  // PDF_ENABLE_V8
+
+#include <memory>
+
+#include "fxjs/cfx_v8.h"
+#include "fxjs/cfx_v8_array_buffer_allocator.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace v8 {
+class Isolate;
+class Platform;
+class StartupData;
+}  // namespace v8
+
+class TestLoader;
+
+class V8TestEnvironment : public testing::Environment {
+ public:
+  explicit V8TestEnvironment(const char* exe_path);
+  ~V8TestEnvironment() override;
+
+  // Note: GetInstance() does not create one if it does not exist,
+  // so the main program must explicitly add this enviroment.
+  static V8TestEnvironment* GetInstance();
+  static void PumpPlatformMessageLoop(v8::Isolate* pIsolate);
+
+  // testing::Environment:
+  void SetUp() override;
+  void TearDown() override;
+
+  v8::Platform* platform() const { return platform_.get(); }
+  v8::Isolate* isolate() const { return isolate_.get(); }
+
+ private:
+  const char* const exe_path_;
+  std::unique_ptr<v8::StartupData> startup_data_;
+  std::unique_ptr<v8::Platform> platform_;
+  std::unique_ptr<CFX_V8ArrayBufferAllocator> array_buffer_allocator_;
+  std::unique_ptr<v8::Isolate, CFX_V8IsolateDeleter> isolate_;
+};
+
+#endif  // TESTING_V8_TEST_ENVIRONMENT_H_
diff --git a/testing/xfa_js_embedder_test.cpp b/testing/xfa_js_embedder_test.cpp
index 2f9cc67..2647157 100644
--- a/testing/xfa_js_embedder_test.cpp
+++ b/testing/xfa_js_embedder_test.cpp
@@ -1,47 +1,37 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "testing/xfa_js_embedder_test.h"
 
+#include <memory>
 #include <string>
 
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_isolatetracker.h"
 #include "fxjs/xfa/cfxjse_value.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fxfa/cxfa_ffapp.h"
+#include "third_party/base/check_op.h"
+#include "v8/include/v8-container.h"
+#include "v8/include/v8-local-handle.h"
+#include "v8/include/v8-value.h"
 
-XFAJSEmbedderTest::XFAJSEmbedderTest()
-    : array_buffer_allocator_(
-          pdfium::MakeUnique<CFX_V8ArrayBufferAllocator>()) {}
+XFAJSEmbedderTest::XFAJSEmbedderTest() = default;
 
-XFAJSEmbedderTest::~XFAJSEmbedderTest() {}
+XFAJSEmbedderTest::~XFAJSEmbedderTest() = default;
 
 void XFAJSEmbedderTest::SetUp() {
-  v8::Isolate::CreateParams params;
-  params.array_buffer_allocator = array_buffer_allocator_.get();
-  isolate_ = v8::Isolate::New(params);
-  ASSERT_TRUE(isolate_);
-
-  EmbedderTest::SetExternalIsolate(isolate_);
-  EmbedderTest::SetUp();
-
-  CXFA_FFApp::SkipFontLoadForTesting(true);
+  JSEmbedderTest::SetUp();
 }
 
 void XFAJSEmbedderTest::TearDown() {
-  CXFA_FFApp::SkipFontLoadForTesting(false);
-
-  value_.reset();
+  value_.Reset();
   script_context_ = nullptr;
 
-  EmbedderTest::TearDown();
-
-  isolate_->Dispose();
-  isolate_ = nullptr;
+  JSEmbedderTest::TearDown();
 }
 
 CXFA_Document* XFAJSEmbedderTest::GetXFADocument() const {
@@ -53,7 +43,15 @@
   if (!pContext)
     return nullptr;
 
-  return pContext->GetXFADoc()->GetXFADoc();
+  CXFA_FFDoc* pFFDoc = pContext->GetXFADoc();
+  if (!pFFDoc)
+    return nullptr;
+
+  return pFFDoc->GetXFADoc();
+}
+
+v8::Local<v8::Value> XFAJSEmbedderTest::GetValue() const {
+  return v8::Local<v8::Value>::New(isolate(), value_);
 }
 
 bool XFAJSEmbedderTest::OpenDocumentWithOptions(
@@ -62,37 +60,54 @@
     LinearizeOption linearize_option,
     JavaScriptOption javascript_option) {
   // JS required for XFA.
-  ASSERT(javascript_option == JavaScriptOption::kEnableJavaScript);
+  DCHECK_EQ(javascript_option, JavaScriptOption::kEnableJavaScript);
   if (!EmbedderTest::OpenDocumentWithOptions(
           filename, password, linearize_option, javascript_option)) {
     return false;
   }
-  script_context_ = GetXFADocument()->GetScriptContext();
+  CXFA_Document* pDoc = GetXFADocument();
+  if (!pDoc)
+    return false;
+
+  script_context_ = pDoc->GetScriptContext();
   return true;
 }
 
 bool XFAJSEmbedderTest::Execute(ByteStringView input) {
+  CFXJSE_ScopeUtil_IsolateHandleContext scope(
+      script_context_->GetJseContextForTest());
   if (ExecuteHelper(input))
     return true;
 
-  CFXJSE_Value msg(GetIsolate());
-  value_->GetObjectPropertyByIdx(1, &msg);
   fprintf(stderr, "FormCalc: %.*s\n", static_cast<int>(input.GetLength()),
           input.unterminated_c_str());
-  // If the parsing of the input fails, then v8 will not run, so there will be
-  // no value here to print.
-  if (msg.IsString() && !msg.ToWideString().IsEmpty())
-    fprintf(stderr, "JS ERROR: %ls\n", msg.ToWideString().c_str());
+
+  v8::Local<v8::Value> result = GetValue();
+  if (!fxv8::IsArray(result))
+    return false;
+
+  v8::Local<v8::Value> msg = fxv8::ReentrantGetArrayElementHelper(
+      isolate(), result.As<v8::Array>(), 1);
+  if (!fxv8::IsString(msg))
+    return false;
+
+  WideString str = fxv8::ReentrantToWideStringHelper(isolate(), msg);
+  if (!str.IsEmpty())
+    fprintf(stderr, "JS ERROR: %ls\n", str.c_str());
   return false;
 }
 
 bool XFAJSEmbedderTest::ExecuteSilenceFailure(ByteStringView input) {
+  CFXJSE_ScopeUtil_IsolateHandleContext scope(
+      script_context_->GetJseContextForTest());
   return ExecuteHelper(input);
 }
 
 bool XFAJSEmbedderTest::ExecuteHelper(ByteStringView input) {
-  value_ = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
-  return script_context_->RunScript(CXFA_Script::Type::Formcalc,
-                                    WideString::FromUTF8(input).AsStringView(),
-                                    value_.get(), GetXFADocument()->GetRoot());
+  auto value = std::make_unique<CFXJSE_Value>();
+  bool ret = script_context_->RunScript(
+      CXFA_Script::Type::Formcalc, WideString::FromUTF8(input).AsStringView(),
+      value.get(), GetXFADocument()->GetRoot());
+  value_.Reset(isolate(), value->GetValue(isolate()));
+  return ret;
 }
diff --git a/testing/xfa_js_embedder_test.h b/testing/xfa_js_embedder_test.h
index 412ffd9..9c14867 100644
--- a/testing/xfa_js_embedder_test.h
+++ b/testing/xfa_js_embedder_test.h
@@ -1,22 +1,22 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef TESTING_XFA_JS_EMBEDDER_TEST_H_
 #define TESTING_XFA_JS_EMBEDDER_TEST_H_
 
-#include <memory>
 #include <string>
 
-#include "testing/embedder_test.h"
-#include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
+#include "core/fxcrt/string_view_template.h"
+#include "testing/js_embedder_test.h"
+#include "v8/include/v8-local-handle.h"
+#include "v8/include/v8-persistent-handle.h"
+#include "v8/include/v8-value.h"
 
 class CFXJSE_Engine;
-class CFXJSE_Value;
-class CFX_V8ArrayBufferAllocator;
+class CXFA_Document;
 
-class XFAJSEmbedderTest : public EmbedderTest {
+class XFAJSEmbedderTest : public JSEmbedderTest {
  public:
   XFAJSEmbedderTest();
   ~XFAJSEmbedderTest() override;
@@ -29,22 +29,18 @@
                                LinearizeOption linearize_option,
                                JavaScriptOption javascript_option) override;
 
-  v8::Isolate* GetIsolate() const { return isolate_; }
   CXFA_Document* GetXFADocument() const;
+  CFXJSE_Engine* GetScriptContext() const { return script_context_; }
+  v8::Local<v8::Value> GetValue() const;
 
   bool Execute(ByteStringView input);
   bool ExecuteSilenceFailure(ByteStringView input);
 
-  CFXJSE_Engine* GetScriptContext() const { return script_context_; }
-  CFXJSE_Value* GetValue() const { return value_.get(); }
-
  private:
-  std::unique_ptr<CFX_V8ArrayBufferAllocator> array_buffer_allocator_;
-  std::unique_ptr<CFXJSE_Value> value_;
-  v8::Isolate* isolate_ = nullptr;
-  CFXJSE_Engine* script_context_ = nullptr;
-
   bool ExecuteHelper(ByteStringView input);
+
+  v8::Global<v8::Value> value_;
+  CFXJSE_Engine* script_context_ = nullptr;
 };
 
 #endif  // TESTING_XFA_JS_EMBEDDER_TEST_H_
diff --git a/testing/xfa_test_environment.cpp b/testing/xfa_test_environment.cpp
new file mode 100644
index 0000000..07f09b6
--- /dev/null
+++ b/testing/xfa_test_environment.cpp
@@ -0,0 +1,40 @@
+// Copyright 2019 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/xfa_test_environment.h"
+
+#include "core/fxge/cfx_fontmgr.h"
+#include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/systemfontinfo_iface.h"
+#include "third_party/base/check.h"
+#include "xfa/fgas/font/cfgas_gemodule.h"
+
+namespace {
+
+XFATestEnvironment* g_env = nullptr;
+
+}  // namespace
+
+XFATestEnvironment::XFATestEnvironment() {
+  DCHECK(!g_env);
+  g_env = this;
+}
+
+XFATestEnvironment::~XFATestEnvironment() {
+  DCHECK(g_env);
+  g_env = nullptr;
+}
+
+void XFATestEnvironment::SetUp() {
+  CFX_GEModule::Get()->GetFontMgr()->GetBuiltinMapper()->SetSystemFontInfo(
+      CFX_GEModule::Get()->GetPlatform()->CreateDefaultSystemFontInfo());
+
+  // The font loading that takes place in CFGAS_GEModule::Create() is slow,
+  // but we do it only once per binary execution, not once per test.
+  CFGAS_GEModule::Create();
+}
+
+void XFATestEnvironment::TearDown() {
+  CFGAS_GEModule::Destroy();
+}
diff --git a/testing/xfa_test_environment.h b/testing/xfa_test_environment.h
new file mode 100644
index 0000000..f8356bd
--- /dev/null
+++ b/testing/xfa_test_environment.h
@@ -0,0 +1,24 @@
+// Copyright 2019 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_XFA_TEST_ENVIRONMENT_H_
+#define TESTING_XFA_TEST_ENVIRONMENT_H_
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#ifndef PDF_ENABLE_XFA
+#error "XFA must be enabled"
+#endif
+
+class XFATestEnvironment : public testing::Environment {
+ public:
+  XFATestEnvironment();
+  ~XFATestEnvironment();
+
+  // testing::TestEnvironment:
+  void SetUp() override;
+  void TearDown() override;
+};
+
+#endif  // TESTING_XFA_TEST_ENVIRONMENT_H_
diff --git a/testing/xfa_unit_test_support.cpp b/testing/xfa_unit_test_support.cpp
deleted file mode 100644
index 8cc2d6c..0000000
--- a/testing/xfa_unit_test_support.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "testing/xfa_unit_test_support.h"
-
-#include <memory>
-#include <utility>
-
-#include "core/fxge/cfx_fontmgr.h"
-#include "core/fxge/cfx_gemodule.h"
-#include "core/fxge/systemfontinfo_iface.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fgas/font/cfgas_fontmgr.h"
-
-namespace {
-
-// The loading time of the CFGAS_FontMgr is linear in the number of times it is
-// loaded. So, if a test suite has a lot of tests that need a font manager they
-// can end up executing very, very slowly.
-class XFATestEnvironment final : public testing::Environment {
- public:
-  // testing::Environment:
-  void SetUp() override {
-    auto font_mgr = pdfium::MakeUnique<CFGAS_FontMgr>();
-    if (font_mgr->EnumFonts())
-      font_mgr_ = std::move(font_mgr);
-  }
-  void TearDown() override { font_mgr_.reset(); }
-
-  CFGAS_FontMgr* FontManager() const { return font_mgr_.get(); }
-
- private:
-  std::unique_ptr<CFGAS_FontMgr> font_mgr_;
-};
-
-XFATestEnvironment* g_env = nullptr;
-
-}  // namespace
-
-void InitializeXFATestEnvironment() {
-  // |g_env| will be deleted by gtest.
-  g_env = new XFATestEnvironment();
-  AddGlobalTestEnvironment(g_env);
-
-  // TODO(dsinclair): This font loading is slow. We should make a test font
-  // loader which loads up a single font we use in all tests.
-  CFX_GEModule::Get()->GetFontMgr()->SetSystemFontInfo(
-      SystemFontInfoIface::CreateDefault(nullptr));
-}
-
-CFGAS_FontMgr* GetGlobalFontManager() {
-  return g_env->FontManager();
-}
diff --git a/testing/xfa_unit_test_support.h b/testing/xfa_unit_test_support.h
deleted file mode 100644
index 0a54778..0000000
--- a/testing/xfa_unit_test_support.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef TESTING_XFA_UNIT_TEST_SUPPORT_H_
-#define TESTING_XFA_UNIT_TEST_SUPPORT_H_
-
-#ifndef PDF_ENABLE_XFA
-#error "XFA must be enabled"
-#endif
-
-class CFGAS_FontMgr;
-
-void InitializeXFATestEnvironment();
-
-CFGAS_FontMgr* GetGlobalFontManager();
-
-#endif  // TESTING_XFA_UNIT_TEST_SUPPORT_H_
diff --git a/third_party/Android.bp b/third_party/Android.bp
index 1c8e251..083ed4e 100644
--- a/third_party/Android.bp
+++ b/third_party/Android.bp
@@ -61,6 +61,7 @@
 
     srcs: [
         "base/debug/*.cc",
+        "base/memory/*.cc",
         "base/allocator/partition_allocator/*.cc",
     ],
 }
@@ -89,11 +90,12 @@
     visibility: ["//cts/hostsidetests/securitybulletin/securityPatch/CVE-2016-8332"],
 
     exclude_srcs: [
-        "libopenjpeg20/t1_generate_luts.c",
+        "libopenjpeg/t1_generate_luts.c",
     ],
 
     srcs: [
-        "libopenjpeg20/*.c",
+        "libopenjpeg/*.c",
+        "libopenjpeg/*.cc",
     ],
 }
 
diff --git a/third_party/BUILD.gn b/third_party/BUILD.gn
index 887dacb..3683ab8 100644
--- a/third_party/BUILD.gn
+++ b/third_party/BUILD.gn
@@ -1,22 +1,13 @@
-# Copyright 2014 PDFium Authors. All rights reserved.
+# Copyright 2014 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
 import("//build/config/arm.gni")
 import("//build/config/linux/pkg_config.gni")
+import("//build/config/mips.gni")
 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",
@@ -47,20 +38,6 @@
 }
 
 if (pdf_bundle_freetype) {
-  config("fx_freetype_warnings") {
-    visibility = [ ":*" ]
-    if (is_clang) {
-      # open_face_PS_from_sfnt_stream() and open_face_from_buffer() in
-      # ftbase.h are unused.
-      #
-      # ttgload.c casts from unsigned int to unsigned long to void*.
-      cflags = [
-        "-Wno-unused-function",
-        "-Wno-int-to-void-pointer-cast",
-      ]
-    }
-  }
-
   config("freetype_public_includes_config") {
     # The relative freetype/include path points to PDFium's custom config.
     # The absolute path points to whatever copy of FreeType is in
@@ -78,16 +55,21 @@
     ]
   }
 
+  config("freetype_private_config") {
+    if (is_clang) {
+      # Multiple functions in freetype/src/src/truetype/ttobjs.h are unused.
+      cflags = [ "-Wno-unused-function" ]
+    }
+  }
+
   # Tests may link against this even if the production library doesn't,
   # so it needs to be separate from it.
   source_set("fx_freetype") {
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [
       "//build/config/compiler:no_chromium_code",
+      ":freetype_private_config",
       ":pdfium_third_party_config",
-
-      # Must be after no_chromium_code for warning flags to be ordered correctly.
-      ":fx_freetype_warnings",
     ]
 
     public_configs = [ ":freetype_public_includes_config" ]
@@ -156,7 +138,6 @@
       "//third_party/freetype/src/include/freetype/internal/ftstream.h",
       "//third_party/freetype/src/include/freetype/internal/fttrace.h",
       "//third_party/freetype/src/include/freetype/internal/ftvalid.h",
-      "//third_party/freetype/src/include/freetype/internal/internal.h",
       "//third_party/freetype/src/include/freetype/internal/psaux.h",
       "//third_party/freetype/src/include/freetype/internal/pshints.h",
       "//third_party/freetype/src/include/freetype/internal/services/svbdf.h",
@@ -191,6 +172,7 @@
       "//third_party/freetype/src/src/base/ftbase.h",
       "//third_party/freetype/src/src/base/ftbitmap.c",
       "//third_party/freetype/src/src/base/ftdebug.c",
+      "//third_party/freetype/src/src/base/ftfstype.c",
       "//third_party/freetype/src/src/base/ftglyph.c",
       "//third_party/freetype/src/src/base/ftinit.c",
       "//third_party/freetype/src/src/base/ftmm.c",
@@ -207,55 +189,72 @@
       "//third_party/freetype/src/src/truetype/truetype.c",
       "//third_party/freetype/src/src/type1/type1.c",
     ]
-    if (pdf_use_skia || pdf_use_skia_paths) {
+    if (pdf_use_skia) {
       sources += [
         "//third_party/freetype/src/include/freetype/ftsynth.h",
-        "//third_party/freetype/src/src/base/ftfstype.c",
         "//third_party/freetype/src/src/base/fttype1.c",
       ]
     }
   }
 }
 
-if (!pdf_use_skia && !pdf_use_skia_paths) {
-  config("fx_agg_warnings") {
-    visibility = [ ":*" ]
-    if (is_clang) {
-      # calc_butt_cap() in agg_vcgen_stroke.cpp is unused.
-      cflags = [ "-Wno-unused-function" ]
-    }
+if (is_linux || is_chromeos) {
+  config("system_fontconfig") {
+    libs = [ "fontconfig" ]
   }
-
-  source_set("fx_agg") {
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      "//build/config/compiler:no_chromium_code",
-      ":pdfium_third_party_config",
-
-      # Must be after no_chromium_code for warning flags to be ordered correctly.
-      ":fx_agg_warnings",
-    ]
-    sources = [
-      "agg23/agg_basics.h",
-      "agg23/agg_clip_liang_barsky.h",
-      "agg23/agg_conv_dash.h",
-      "agg23/agg_conv_stroke.h",
-      "agg23/agg_curves.cpp",
-      "agg23/agg_curves.h",
-      "agg23/agg_path_storage.cpp",
-      "agg23/agg_path_storage.h",
-      "agg23/agg_rasterizer_scanline_aa.cpp",
-      "agg23/agg_rasterizer_scanline_aa.h",
-      "agg23/agg_renderer_scanline.h",
-      "agg23/agg_rendering_buffer.h",
-      "agg23/agg_scanline_u.h",
-      "agg23/agg_vcgen_dash.cpp",
-      "agg23/agg_vcgen_stroke.cpp",
-    ]
-    deps = [ "../core/fxcrt" ]
+  group("fontconfig") {
+    public_configs = [ ":system_fontconfig" ]
   }
 }
 
+config("fx_agg_warnings") {
+  visibility = [ ":fx_agg" ]
+  if (is_clang) {
+    # calc_butt_cap() in agg_vcgen_stroke.cpp is unused.
+    cflags = [ "-Wno-unused-function" ]
+  }
+}
+source_set("fx_agg") {
+  configs -= [ "//build/config/compiler:chromium_code" ]
+  configs += [
+    "//build/config/compiler:no_chromium_code",
+    ":pdfium_third_party_config",
+
+    # Must be after no_chromium_code for warning flags to be ordered correctly.
+    ":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" ]
+}
+
 config("fx_lcms2_warnings") {
   visibility = [ ":*" ]
   if (is_clang) {
@@ -263,9 +262,6 @@
       # cmslut.cc is sloppy with aggregate initialization. Version 2.7 of this
       # library doesn't appear to have this problem.
       "-Wno-missing-braces",
-
-      # FindPrev() in cmsplugin.c is unused.
-      "-Wno-unused-function",
     ]
   }
 }
@@ -390,7 +386,12 @@
 
 config("fx_libopenjpeg_warnings") {
   visibility = [ ":*" ]
-  if (is_win) {
+  if (is_clang) {
+    cflags = [
+      # Various files convert MAX_INT to double and similar without casting.
+      "-Wno-implicit-int-float-conversion",
+    ]
+  } else if (is_win && target_cpu == "x86") {
     cflags = [
       # Signed/unsigned comparisons.
       "/wd4018",
@@ -409,28 +410,34 @@
     ":fx_libopenjpeg_warnings",
   ]
   sources = [
-    "libopenjpeg20/bio.c",
-    "libopenjpeg20/cio.c",
-    "libopenjpeg20/dwt.c",
-    "libopenjpeg20/event.c",
-    "libopenjpeg20/function_list.c",
-    "libopenjpeg20/image.c",
-    "libopenjpeg20/invert.c",
-    "libopenjpeg20/j2k.c",
-    "libopenjpeg20/jp2.c",
-    "libopenjpeg20/mct.c",
-    "libopenjpeg20/mqc.c",
-    "libopenjpeg20/openjpeg.c",
-    "libopenjpeg20/opj_clock.c",
-    "libopenjpeg20/pi.c",
-    "libopenjpeg20/sparse_array.c",
-    "libopenjpeg20/sparse_array.h",
-    "libopenjpeg20/t1.c",
-    "libopenjpeg20/t2.c",
-    "libopenjpeg20/tcd.c",
-    "libopenjpeg20/tgt.c",
-    "libopenjpeg20/thread.c",
+    "libopenjpeg/bio.c",
+    "libopenjpeg/cio.c",
+    "libopenjpeg/dwt.c",
+    "libopenjpeg/event.c",
+    "libopenjpeg/function_list.c",
+    "libopenjpeg/ht_dec.c",
+    "libopenjpeg/image.c",
+    "libopenjpeg/invert.c",
+    "libopenjpeg/j2k.c",
+    "libopenjpeg/jp2.c",
+    "libopenjpeg/mct.c",
+    "libopenjpeg/mqc.c",
+    "libopenjpeg/openjpeg.c",
+
+    # NOTE: libopenjpeg/opj_clock.c is not used.
+    "libopenjpeg/opj_malloc.cc",
+    "libopenjpeg/opj_malloc.h",
+    "libopenjpeg/pi.c",
+    "libopenjpeg/sparse_array.c",
+    "libopenjpeg/sparse_array.h",
+    "libopenjpeg/t1.c",
+    "libopenjpeg/t1_ht_luts.h",
+    "libopenjpeg/t2.c",
+    "libopenjpeg/tcd.c",
+    "libopenjpeg/tgt.c",
+    "libopenjpeg/thread.c",
   ]
+  deps = [ "../core/fxcrt" ]
 }
 
 config("system_libpng_config") {
@@ -442,127 +449,89 @@
   if (use_system_libpng) {
     public_configs = [ ":system_libpng_config" ]
   } else {
-    public_deps = [ ":fx_lpng" ]
+    public_deps = [ "//third_party/libpng" ]
   }
 }
 
-source_set("fx_lpng") {
+if (pdf_enable_xfa_tiff) {
+  if (use_system_libtiff) {
+    config("system_tiff_config") {
+      libs = [ "tiff" ]
+    }
+  } else {
+    source_set("fx_tiff") {
+      configs -= [ "//build/config/compiler:chromium_code" ]
+      configs += [
+        "//build/config/compiler:no_chromium_code",
+        ":pdfium_third_party_config",
+      ]
+      if (is_win) {
+        # Need to undefine the macro since it is redefined in tif_jpeg.c.
+        configs -= [ "//build/config/win:lean_and_mean" ]
+      }
+      deps = [
+        ":zlib",
+        "../core/fxcrt",
+        "//third_party:jpeg",
+      ]
+      sources = [
+        "libtiff/tif_aux.c",
+        "libtiff/tif_close.c",
+        "libtiff/tif_codec.c",
+        "libtiff/tif_color.c",
+        "libtiff/tif_compress.c",
+        "libtiff/tif_dir.c",
+        "libtiff/tif_dirinfo.c",
+        "libtiff/tif_dirread.c",
+        "libtiff/tif_dirwrite.c",
+        "libtiff/tif_dumpmode.c",
+        "libtiff/tif_error.c",
+        "libtiff/tif_extension.c",
+        "libtiff/tif_fax3.c",
+        "libtiff/tif_fax3sm.c",
+        "libtiff/tif_flush.c",
+        "libtiff/tif_getimage.c",
+        "libtiff/tif_hash_set.c",
+        "libtiff/tif_hash_set.h",
+        "libtiff/tif_jpeg.c",
+        "libtiff/tif_luv.c",
+        "libtiff/tif_lzw.c",
+        "libtiff/tif_next.c",
+        "libtiff/tif_open.c",
+        "libtiff/tif_packbits.c",
+        "libtiff/tif_pixarlog.c",
+        "libtiff/tif_predict.c",
+        "libtiff/tif_print.c",
+        "libtiff/tif_read.c",
+        "libtiff/tif_strip.c",
+        "libtiff/tif_swab.c",
+        "libtiff/tif_thunder.c",
+        "libtiff/tif_tile.c",
+        "libtiff/tif_version.c",
+        "libtiff/tif_warning.c",
+        "libtiff/tif_write.c",
+        "libtiff/tiffiop.h",
+        "libtiff/tiffvers.h",
+      ]
+    }
+  }
+
+  group("tiff") {
+    if (use_system_libtiff) {
+      public_configs = [ ":system_tiff_config" ]
+    } else {
+      public_deps = [ ":fx_tiff" ]
+    }
+  }
+}
+
+source_set("pdfium_compiler_specific") {
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [
     "//build/config/compiler:no_chromium_code",
     ":pdfium_third_party_config",
   ]
-  sources = [
-    "libpng16/png.c",
-    "libpng16/png.h",
-    "libpng16/pngconf.h",
-    "libpng16/pngdebug.h",
-    "libpng16/pngerror.c",
-    "libpng16/pngget.c",
-    "libpng16/pnginfo.h",
-    "libpng16/pnglibconf.h",
-    "libpng16/pngmem.c",
-    "libpng16/pngpread.c",
-    "libpng16/pngprefix.h",
-    "libpng16/pngpriv.h",
-    "libpng16/pngread.c",
-    "libpng16/pngrio.c",
-    "libpng16/pngrtran.c",
-    "libpng16/pngrutil.c",
-    "libpng16/pngset.c",
-    "libpng16/pngstruct.h",
-    "libpng16/pngtrans.c",
-    "libpng16/pngwio.c",
-    "libpng16/pngwrite.c",
-    "libpng16/pngwtran.c",
-    "libpng16/pngwutil.c",
-  ]
-
-  defines = []
-  cflags = []
-  deps = [ ":zlib" ]
-
-  if (current_cpu == "x86" || current_cpu == "x64") {
-    sources += [
-      "libpng16/intel/filter_sse2_intrinsics.c",
-      "libpng16/intel/intel_init.c",
-    ]
-    defines += [ "PNG_INTEL_SSE_OPT=1" ]
-  } else if ((current_cpu == "arm" || current_cpu == "arm64") && arm_use_neon) {
-    sources += [
-      "libpng16/arm/arm_init.c",
-      "libpng16/arm/filter_neon_intrinsics.c",
-      "libpng16/arm/palette_neon_intrinsics.c",
-    ]
-    defines += [
-      "PNG_ARM_NEON_OPT=2",
-      "PNG_ARM_NEON_IMPLEMENTATION=1",
-    ]
-  }
-
-  if (is_win) {
-    # Unary minus applied to unsigned type.
-    cflags += [ "/wd4146" ]
-  }
-}
-
-if (pdf_enable_xfa_tiff) {
-  source_set("fx_tiff") {
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      "//build/config/compiler:no_chromium_code",
-      ":pdfium_third_party_config",
-    ]
-    if (is_win) {
-      # Need to undefine the macro since it is redefined in
-      # tif_ojpeg.c and tif_jpeg.c.
-      configs -= [ "//build/config/win:lean_and_mean" ]
-    }
-    deps = [
-      ":zlib",
-      "../core/fxcrt",
-      "//third_party:jpeg",
-    ]
-    sources = [
-      "libtiff/tif_aux.c",
-      "libtiff/tif_close.c",
-      "libtiff/tif_codec.c",
-      "libtiff/tif_color.c",
-      "libtiff/tif_compress.c",
-      "libtiff/tif_dir.c",
-      "libtiff/tif_dirinfo.c",
-      "libtiff/tif_dirread.c",
-      "libtiff/tif_dirwrite.c",
-      "libtiff/tif_dumpmode.c",
-      "libtiff/tif_error.c",
-      "libtiff/tif_extension.c",
-      "libtiff/tif_fax3.c",
-      "libtiff/tif_fax3sm.c",
-      "libtiff/tif_flush.c",
-      "libtiff/tif_getimage.c",
-      "libtiff/tif_jpeg.c",
-      "libtiff/tif_luv.c",
-      "libtiff/tif_lzw.c",
-      "libtiff/tif_next.c",
-      "libtiff/tif_ojpeg.c",
-      "libtiff/tif_open.c",
-      "libtiff/tif_packbits.c",
-      "libtiff/tif_pixarlog.c",
-      "libtiff/tif_predict.c",
-      "libtiff/tif_print.c",
-      "libtiff/tif_read.c",
-      "libtiff/tif_strip.c",
-      "libtiff/tif_swab.c",
-      "libtiff/tif_thunder.c",
-      "libtiff/tif_tile.c",
-      "libtiff/tif_version.c",
-      "libtiff/tif_warning.c",
-      "libtiff/tif_write.c",
-      "libtiff/tif_zip.c",
-      "libtiff/tiffiop.h",
-      "libtiff/tiffvers.h",
-    ]
-  }
+  sources = [ "base/compiler_specific.h" ]
 }
 
 source_set("pdfium_base") {
@@ -572,56 +541,44 @@
     ":pdfium_third_party_config",
   ]
   sources = [
-    "base/allocator/partition_allocator/address_space_randomization.cc",
-    "base/allocator/partition_allocator/address_space_randomization.h",
-    "base/allocator/partition_allocator/oom.h",
-    "base/allocator/partition_allocator/oom_callback.cc",
-    "base/allocator/partition_allocator/oom_callback.h",
-    "base/allocator/partition_allocator/page_allocator.cc",
-    "base/allocator/partition_allocator/page_allocator.h",
-    "base/allocator/partition_allocator/page_allocator_constants.h",
-    "base/allocator/partition_allocator/page_allocator_internal.h",
-    "base/allocator/partition_allocator/page_allocator_internals_posix.h",
-    "base/allocator/partition_allocator/page_allocator_internals_win.h",
-    "base/allocator/partition_allocator/partition_alloc.cc",
-    "base/allocator/partition_allocator/partition_alloc.h",
-    "base/allocator/partition_allocator/partition_alloc_constants.h",
-    "base/allocator/partition_allocator/partition_bucket.cc",
-    "base/allocator/partition_allocator/partition_bucket.h",
-    "base/allocator/partition_allocator/partition_cookie.h",
-    "base/allocator/partition_allocator/partition_direct_map_extent.h",
-    "base/allocator/partition_allocator/partition_freelist_entry.h",
-    "base/allocator/partition_allocator/partition_oom.cc",
-    "base/allocator/partition_allocator/partition_oom.h",
-    "base/allocator/partition_allocator/partition_page.cc",
-    "base/allocator/partition_allocator/partition_page.h",
-    "base/allocator/partition_allocator/partition_root_base.cc",
-    "base/allocator/partition_allocator/partition_root_base.h",
-    "base/allocator/partition_allocator/random.cc",
-    "base/allocator/partition_allocator/random.h",
-    "base/allocator/partition_allocator/spin_lock.cc",
-    "base/allocator/partition_allocator/spin_lock.h",
     "base/base_export.h",
     "base/bits.h",
-    "base/compiler_specific.h",
+    "base/check.h",
+    "base/check_op.h",
+    "base/component_export.h",
+    "base/containers/adapters.h",
+    "base/containers/contains.h",
+    "base/containers/span.h",
     "base/debug/alias.cc",
     "base/debug/alias.h",
-    "base/logging.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",
+    "base/numerics/checked_math_impl.h",
+    "base/numerics/clamped_math.h",
+    "base/numerics/clamped_math_impl.h",
     "base/numerics/safe_conversions.h",
     "base/numerics/safe_conversions_arm_impl.h",
     "base/numerics/safe_conversions_impl.h",
     "base/numerics/safe_math.h",
-    "base/numerics/safe_math_impl.h",
-    "base/optional.h",
-    "base/span.h",
-    "base/stl_util.h",
+    "base/numerics/safe_math_arm_impl.h",
+    "base/numerics/safe_math_clang_gcc_impl.h",
+    "base/numerics/safe_math_shared_impl.h",
     "base/sys_byteorder.h",
     "base/template_util.h",
   ]
-
+  public_deps = [
+    ":pdfium_compiler_specific",
+    "../core/fxcrt:unowned_ptr",
+    "//third_party/abseil-cpp:absl",
+  ]
   if (is_win) {
     sources += [
+      "base/win/scoped_select_object.h",
       "base/win/win_util.cc",
       "base/win/win_util.h",
     ]
@@ -630,14 +587,12 @@
 
 source_set("pdfium_base_test_support") {
   testonly = true
-  configs -= [ "//build/config/compiler:chromium_code" ]
-  configs += [
-    "//build/config/compiler:no_chromium_code",
-    ":pdfium_third_party_config",
-  ]
   sources = []
+  configs += [
+    "../:pdfium_strict_config",
+    "../:pdfium_noshorten_config",
+  ]
   deps = []
-
   if (is_posix || is_fuchsia) {
     sources += [
       "base/test/scoped_locale.cc",
@@ -646,15 +601,3 @@
     deps += [ "//testing/gtest" ]
   }
 }
-
-source_set("skia_shared") {
-  configs -= [ "//build/config/compiler:chromium_code" ]
-  configs += [
-    "//build/config/compiler:no_chromium_code",
-    ":pdfium_third_party_config",
-  ]
-  sources = [
-    "skia_shared/SkFloatToDecimal.cpp",
-    "skia_shared/SkFloatToDecimal.h",
-  ]
-}
diff --git a/third_party/NotoSansCJK/LICENSE b/third_party/NotoSansCJK/LICENSE
new file mode 100644
index 0000000..70fbc7f
--- /dev/null
+++ b/third_party/NotoSansCJK/LICENSE
@@ -0,0 +1,78 @@
+Copyright 2018 The Noto Project Authors (github.com/googlei18n/noto-fonts)
+
+This Font Software is licensed under the SIL Open Font License,
+Version 1.1.
+
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font
+creation efforts of academic and linguistic communities, and to
+provide a free and open framework in which fonts may be shared and
+improved in partnership with others.
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply to
+any document created using the fonts or their derivatives.
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+"Original Version" refers to the collection of Font Software
+components as distributed by the Copyright Holder(s).
+"Modified Version" refers to any derivative made by adding to,
+deleting, or substituting -- in part or in whole -- any of the
+components of the Original Version, by changing formats or by porting
+the Font Software to a new environment.
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed,
+modify, redistribute, and sell modified and unmodified copies of the
+Font Software, subject to the following conditions:
+1) Neither the Font Software nor any of its individual components, in
+Original or Modified Versions, may be sold by itself.
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the
+corresponding Copyright Holder. This restriction only applies to the
+primary font name as presented to the users.
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created using
+the Font Software.
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/third_party/NotoSansCJK/NotoSansSC-Regular.subset.otf b/third_party/NotoSansCJK/NotoSansSC-Regular.subset.otf
new file mode 100644
index 0000000..050c8b2
--- /dev/null
+++ b/third_party/NotoSansCJK/NotoSansSC-Regular.subset.otf
Binary files differ
diff --git a/third_party/NotoSansCJK/README.pdfium b/third_party/NotoSansCJK/README.pdfium
new file mode 100644
index 0000000..5c8ba5a
--- /dev/null
+++ b/third_party/NotoSansCJK/README.pdfium
@@ -0,0 +1,24 @@
+Name: Noto Sans CJK Font
+URL: https://github.com/googlefonts/noto-cjk
+Version: Noto Sans CJK V2.001
+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.
+This font file is used for the test FPDFEditEmbedderTest.EmbedNotoSansSCFont,
+and it is included as a real-world example for testing PDFium's API to embed a
+CJK font into a PDF file, which contains multiple unicodes mapped to the same
+CID. See crbug.com/pdfium/1608 for details.
+
+Local Modifications:
+
+NotoSansSC-Regular.subset.otf is a subset of NotoSansSC-Regular font.
+Command to generate this font subset:
+$ pyftsubset NotoSansSC-Regular.otf --unicodes="U+0000884C,U+0000FA08, \
+U+0000F906,U+000053E5,U+00008FD9,U+0000662F,U+00007B2C,U+00004E00,U+00003002, \
+U+00004E8C,U+00002F00,U+00002F06"
+
+where pyftsubset comes from https://github.com/behdad/fonttools (version 4.16.1).
diff --git a/third_party/abseil-cpp/absl/base/attributes.h b/third_party/abseil-cpp/absl/base/attributes.h
new file mode 100644
index 0000000..05a8855
--- /dev/null
+++ b/third_party/abseil-cpp/absl/base/attributes.h
@@ -0,0 +1,783 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This header file defines macros for declaring attributes for functions,
+// types, and variables.
+//
+// These macros are used within Abseil and allow the compiler to optimize, where
+// applicable, certain function calls.
+//
+// Most macros here are exposing GCC or Clang features, and are stubbed out for
+// other compilers.
+//
+// GCC attributes documentation:
+//   https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html
+//   https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Variable-Attributes.html
+//   https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Type-Attributes.html
+//
+// Most attributes in this file are already supported by GCC 4.7. However, some
+// of them are not supported in older version of Clang. Thus, we check
+// `__has_attribute()` first. If the check fails, we check if we are on GCC and
+// assume the attribute exists on GCC (which is verified on GCC 4.7).
+
+#ifndef ABSL_BASE_ATTRIBUTES_H_
+#define ABSL_BASE_ATTRIBUTES_H_
+
+#include "third_party/abseil-cpp/absl/base/config.h"
+
+// ABSL_HAVE_ATTRIBUTE
+//
+// A function-like feature checking macro that is a wrapper around
+// `__has_attribute`, which is defined by GCC 5+ and Clang and evaluates to a
+// nonzero constant integer if the attribute is supported or 0 if not.
+//
+// It evaluates to zero if `__has_attribute` is not defined by the compiler.
+//
+// GCC: https://gcc.gnu.org/gcc-5/changes.html
+// Clang: https://clang.llvm.org/docs/LanguageExtensions.html
+#ifdef __has_attribute
+#define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x)
+#else
+#define ABSL_HAVE_ATTRIBUTE(x) 0
+#endif
+
+// ABSL_HAVE_CPP_ATTRIBUTE
+//
+// A function-like feature checking macro that accepts C++11 style attributes.
+// It's a wrapper around `__has_cpp_attribute`, defined by ISO C++ SD-6
+// (https://en.cppreference.com/w/cpp/experimental/feature_test). If we don't
+// find `__has_cpp_attribute`, will evaluate to 0.
+#if defined(__cplusplus) && defined(__has_cpp_attribute)
+// NOTE: requiring __cplusplus above should not be necessary, but
+// works around https://bugs.llvm.org/show_bug.cgi?id=23435.
+#define ABSL_HAVE_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
+#else
+#define ABSL_HAVE_CPP_ATTRIBUTE(x) 0
+#endif
+
+// -----------------------------------------------------------------------------
+// Function Attributes
+// -----------------------------------------------------------------------------
+//
+// GCC: https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
+// Clang: https://clang.llvm.org/docs/AttributeReference.html
+
+// ABSL_PRINTF_ATTRIBUTE
+// ABSL_SCANF_ATTRIBUTE
+//
+// Tells the compiler to perform `printf` format string checking if the
+// compiler supports it; see the 'format' attribute in
+// <https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html>.
+//
+// Note: As the GCC manual states, "[s]ince non-static C++ methods
+// have an implicit 'this' argument, the arguments of such methods
+// should be counted from two, not one."
+#if ABSL_HAVE_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) \
+  __attribute__((__format__(__printf__, string_index, first_to_check)))
+#define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) \
+  __attribute__((__format__(__scanf__, string_index, first_to_check)))
+#else
+#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check)
+#define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check)
+#endif
+
+// ABSL_ATTRIBUTE_ALWAYS_INLINE
+// ABSL_ATTRIBUTE_NOINLINE
+//
+// Forces functions to either inline or not inline. Introduced in gcc 3.1.
+#if ABSL_HAVE_ATTRIBUTE(always_inline) || \
+    (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline))
+#define ABSL_HAVE_ATTRIBUTE_ALWAYS_INLINE 1
+#else
+#define ABSL_ATTRIBUTE_ALWAYS_INLINE
+#endif
+
+#if ABSL_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_NOINLINE __attribute__((noinline))
+#define ABSL_HAVE_ATTRIBUTE_NOINLINE 1
+#else
+#define ABSL_ATTRIBUTE_NOINLINE
+#endif
+
+// ABSL_ATTRIBUTE_NO_TAIL_CALL
+//
+// Prevents the compiler from optimizing away stack frames for functions which
+// end in a call to another function.
+#if ABSL_HAVE_ATTRIBUTE(disable_tail_calls)
+#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1
+#define ABSL_ATTRIBUTE_NO_TAIL_CALL __attribute__((disable_tail_calls))
+#elif defined(__GNUC__) && !defined(__clang__) && !defined(__e2k__)
+#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1
+#define ABSL_ATTRIBUTE_NO_TAIL_CALL \
+  __attribute__((optimize("no-optimize-sibling-calls")))
+#else
+#define ABSL_ATTRIBUTE_NO_TAIL_CALL
+#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 0
+#endif
+
+// ABSL_ATTRIBUTE_WEAK
+//
+// Tags a function as weak for the purposes of compilation and linking.
+// Weak attributes did not work properly in LLVM's Windows backend before
+// 9.0.0, so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598
+// for further information.
+// The MinGW compiler doesn't complain about the weak attribute until the link
+// step, presumably because Windows doesn't use ELF binaries.
+#if (ABSL_HAVE_ATTRIBUTE(weak) ||                                         \
+     (defined(__GNUC__) && !defined(__clang__))) &&                       \
+    (!defined(_WIN32) || (defined(__clang__) && __clang_major__ >= 9)) && \
+    !defined(__MINGW32__)
+#undef ABSL_ATTRIBUTE_WEAK
+#define ABSL_ATTRIBUTE_WEAK __attribute__((weak))
+#define ABSL_HAVE_ATTRIBUTE_WEAK 1
+#else
+#define ABSL_ATTRIBUTE_WEAK
+#define ABSL_HAVE_ATTRIBUTE_WEAK 0
+#endif
+
+// ABSL_ATTRIBUTE_NONNULL
+//
+// Tells the compiler either (a) that a particular function parameter
+// should be a non-null pointer, or (b) that all pointer arguments should
+// be non-null.
+//
+// Note: As the GCC manual states, "[s]ince non-static C++ methods
+// have an implicit 'this' argument, the arguments of such methods
+// should be counted from two, not one."
+//
+// Args are indexed starting at 1.
+//
+// For non-static class member functions, the implicit `this` argument
+// is arg 1, and the first explicit argument is arg 2. For static class member
+// functions, there is no implicit `this`, and the first explicit argument is
+// arg 1.
+//
+// Example:
+//
+//   /* arg_a cannot be null, but arg_b can */
+//   void Function(void* arg_a, void* arg_b) ABSL_ATTRIBUTE_NONNULL(1);
+//
+//   class C {
+//     /* arg_a cannot be null, but arg_b can */
+//     void Method(void* arg_a, void* arg_b) ABSL_ATTRIBUTE_NONNULL(2);
+//
+//     /* arg_a cannot be null, but arg_b can */
+//     static void StaticMethod(void* arg_a, void* arg_b)
+//     ABSL_ATTRIBUTE_NONNULL(1);
+//   };
+//
+// If no arguments are provided, then all pointer arguments should be non-null.
+//
+//  /* No pointer arguments may be null. */
+//  void Function(void* arg_a, void* arg_b, int arg_c) ABSL_ATTRIBUTE_NONNULL();
+//
+// NOTE: The GCC nonnull attribute actually accepts a list of arguments, but
+// ABSL_ATTRIBUTE_NONNULL does not.
+#if ABSL_HAVE_ATTRIBUTE(nonnull) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_NONNULL(arg_index) __attribute__((nonnull(arg_index)))
+#else
+#define ABSL_ATTRIBUTE_NONNULL(...)
+#endif
+
+// ABSL_ATTRIBUTE_NORETURN
+//
+// Tells the compiler that a given function never returns.
+#if ABSL_HAVE_ATTRIBUTE(noreturn) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_NORETURN __attribute__((noreturn))
+#elif defined(_MSC_VER)
+#define ABSL_ATTRIBUTE_NORETURN __declspec(noreturn)
+#else
+#define ABSL_ATTRIBUTE_NORETURN
+#endif
+
+// ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS
+//
+// Tells the AddressSanitizer (or other memory testing tools) to ignore a given
+// function. Useful for cases when a function reads random locations on stack,
+// calls _exit from a cloned subprocess, deliberately accesses buffer
+// out of bounds or does other scary things with memory.
+// NOTE: GCC supports AddressSanitizer(asan) since 4.8.
+// https://gcc.gnu.org/gcc-4.8/changes.html
+#if ABSL_HAVE_ATTRIBUTE(no_sanitize_address)
+#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
+#elif defined(_MSC_VER) && _MSC_VER >= 1928
+// https://docs.microsoft.com/en-us/cpp/cpp/no-sanitize-address
+#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __declspec(no_sanitize_address)
+#else
+#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS
+#endif
+
+// ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY
+//
+// Tells the MemorySanitizer to relax the handling of a given function. All "Use
+// of uninitialized value" warnings from such functions will be suppressed, and
+// all values loaded from memory will be considered fully initialized.  This
+// attribute is similar to the ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS attribute
+// above, but deals with initialized-ness rather than addressability issues.
+// NOTE: MemorySanitizer(msan) is supported by Clang but not GCC.
+#if ABSL_HAVE_ATTRIBUTE(no_sanitize_memory)
+#define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
+#else
+#define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY
+#endif
+
+// ABSL_ATTRIBUTE_NO_SANITIZE_THREAD
+//
+// Tells the ThreadSanitizer to not instrument a given function.
+// NOTE: GCC supports ThreadSanitizer(tsan) since 4.8.
+// https://gcc.gnu.org/gcc-4.8/changes.html
+#if ABSL_HAVE_ATTRIBUTE(no_sanitize_thread)
+#define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
+#else
+#define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD
+#endif
+
+// ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED
+//
+// Tells the UndefinedSanitizer to ignore a given function. Useful for cases
+// where certain behavior (eg. division by zero) is being used intentionally.
+// NOTE: GCC supports UndefinedBehaviorSanitizer(ubsan) since 4.9.
+// https://gcc.gnu.org/gcc-4.9/changes.html
+#if ABSL_HAVE_ATTRIBUTE(no_sanitize_undefined)
+#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \
+  __attribute__((no_sanitize_undefined))
+#elif ABSL_HAVE_ATTRIBUTE(no_sanitize)
+#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \
+  __attribute__((no_sanitize("undefined")))
+#else
+#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED
+#endif
+
+// ABSL_ATTRIBUTE_NO_SANITIZE_CFI
+//
+// Tells the ControlFlowIntegrity sanitizer to not instrument a given function.
+// See https://clang.llvm.org/docs/ControlFlowIntegrity.html for details.
+#if ABSL_HAVE_ATTRIBUTE(no_sanitize)
+#define ABSL_ATTRIBUTE_NO_SANITIZE_CFI __attribute__((no_sanitize("cfi")))
+#else
+#define ABSL_ATTRIBUTE_NO_SANITIZE_CFI
+#endif
+
+// ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK
+//
+// Tells the SafeStack to not instrument a given function.
+// See https://clang.llvm.org/docs/SafeStack.html for details.
+#if ABSL_HAVE_ATTRIBUTE(no_sanitize)
+#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK \
+  __attribute__((no_sanitize("safe-stack")))
+#else
+#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK
+#endif
+
+// ABSL_ATTRIBUTE_RETURNS_NONNULL
+//
+// Tells the compiler that a particular function never returns a null pointer.
+#if ABSL_HAVE_ATTRIBUTE(returns_nonnull)
+#define ABSL_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull))
+#else
+#define ABSL_ATTRIBUTE_RETURNS_NONNULL
+#endif
+
+// ABSL_HAVE_ATTRIBUTE_SECTION
+//
+// Indicates whether labeled sections are supported. Weak symbol support is
+// a prerequisite. Labeled sections are not supported on Darwin/iOS.
+#ifdef ABSL_HAVE_ATTRIBUTE_SECTION
+#error ABSL_HAVE_ATTRIBUTE_SECTION cannot be directly set
+#elif (ABSL_HAVE_ATTRIBUTE(section) ||                \
+       (defined(__GNUC__) && !defined(__clang__))) && \
+    !defined(__APPLE__) && ABSL_HAVE_ATTRIBUTE_WEAK
+#define ABSL_HAVE_ATTRIBUTE_SECTION 1
+
+// ABSL_ATTRIBUTE_SECTION
+//
+// Tells the compiler/linker to put a given function into a section and define
+// `__start_ ## name` and `__stop_ ## name` symbols to bracket the section.
+// This functionality is supported by GNU linker.  Any function annotated with
+// `ABSL_ATTRIBUTE_SECTION` must not be inlined, or it will be placed into
+// whatever section its caller is placed into.
+//
+#ifndef ABSL_ATTRIBUTE_SECTION
+#define ABSL_ATTRIBUTE_SECTION(name) \
+  __attribute__((section(#name))) __attribute__((noinline))
+#endif
+
+// ABSL_ATTRIBUTE_SECTION_VARIABLE
+//
+// Tells the compiler/linker to put a given variable into a section and define
+// `__start_ ## name` and `__stop_ ## name` symbols to bracket the section.
+// This functionality is supported by GNU linker.
+#ifndef ABSL_ATTRIBUTE_SECTION_VARIABLE
+#ifdef _AIX
+// __attribute__((section(#name))) on AIX is achived by using the `.csect` psudo
+// op which includes an additional integer as part of its syntax indcating
+// alignment. If data fall under different alignments then you might get a
+// compilation error indicating a `Section type conflict`.
+#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name)
+#else
+#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name)))
+#endif
+#endif
+
+// ABSL_DECLARE_ATTRIBUTE_SECTION_VARS
+//
+// A weak section declaration to be used as a global declaration
+// for ABSL_ATTRIBUTE_SECTION_START|STOP(name) to compile and link
+// even without functions with ABSL_ATTRIBUTE_SECTION(name).
+// ABSL_DEFINE_ATTRIBUTE_SECTION should be in the exactly one file; it's
+// a no-op on ELF but not on Mach-O.
+//
+#ifndef ABSL_DECLARE_ATTRIBUTE_SECTION_VARS
+#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name)   \
+  extern char __start_##name[] ABSL_ATTRIBUTE_WEAK; \
+  extern char __stop_##name[] ABSL_ATTRIBUTE_WEAK
+#endif
+#ifndef ABSL_DEFINE_ATTRIBUTE_SECTION_VARS
+#define ABSL_INIT_ATTRIBUTE_SECTION_VARS(name)
+#define ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(name)
+#endif
+
+// ABSL_ATTRIBUTE_SECTION_START
+//
+// Returns `void*` pointers to start/end of a section of code with
+// functions having ABSL_ATTRIBUTE_SECTION(name).
+// Returns 0 if no such functions exist.
+// One must ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) for this to compile and
+// link.
+//
+#define ABSL_ATTRIBUTE_SECTION_START(name) \
+  (reinterpret_cast<void *>(__start_##name))
+#define ABSL_ATTRIBUTE_SECTION_STOP(name) \
+  (reinterpret_cast<void *>(__stop_##name))
+
+#else  // !ABSL_HAVE_ATTRIBUTE_SECTION
+
+#define ABSL_HAVE_ATTRIBUTE_SECTION 0
+
+// provide dummy definitions
+#define ABSL_ATTRIBUTE_SECTION(name)
+#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name)
+#define ABSL_INIT_ATTRIBUTE_SECTION_VARS(name)
+#define ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(name)
+#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name)
+#define ABSL_ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void *>(0))
+#define ABSL_ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void *>(0))
+
+#endif  // ABSL_ATTRIBUTE_SECTION
+
+// ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
+//
+// Support for aligning the stack on 32-bit x86.
+#if ABSL_HAVE_ATTRIBUTE(force_align_arg_pointer) || \
+    (defined(__GNUC__) && !defined(__clang__))
+#if defined(__i386__)
+#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC \
+  __attribute__((force_align_arg_pointer))
+#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
+#elif defined(__x86_64__)
+#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (1)
+#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
+#else  // !__i386__ && !__x86_64
+#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
+#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
+#endif  // __i386__
+#else
+#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
+#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
+#endif
+
+// ABSL_MUST_USE_RESULT
+//
+// Tells the compiler to warn about unused results.
+//
+// For code or headers that are assured to only build with C++17 and up, prefer
+// just using the standard `[[nodiscard]]` directly over this macro.
+//
+// When annotating a function, it must appear as the first part of the
+// declaration or definition. The compiler will warn if the return value from
+// such a function is unused:
+//
+//   ABSL_MUST_USE_RESULT Sprocket* AllocateSprocket();
+//   AllocateSprocket();  // Triggers a warning.
+//
+// When annotating a class, it is equivalent to annotating every function which
+// returns an instance.
+//
+//   class ABSL_MUST_USE_RESULT Sprocket {};
+//   Sprocket();  // Triggers a warning.
+//
+//   Sprocket MakeSprocket();
+//   MakeSprocket();  // Triggers a warning.
+//
+// Note that references and pointers are not instances:
+//
+//   Sprocket* SprocketPointer();
+//   SprocketPointer();  // Does *not* trigger a warning.
+//
+// ABSL_MUST_USE_RESULT allows using cast-to-void to suppress the unused result
+// warning. For that, warn_unused_result is used only for clang but not for gcc.
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425
+//
+// Note: past advice was to place the macro after the argument list.
+//
+// TODO(b/176172494): Use ABSL_HAVE_CPP_ATTRIBUTE(nodiscard) when all code is
+// compliant with the stricter [[nodiscard]].
+#if defined(__clang__) && ABSL_HAVE_ATTRIBUTE(warn_unused_result)
+#define ABSL_MUST_USE_RESULT __attribute__((warn_unused_result))
+#else
+#define ABSL_MUST_USE_RESULT
+#endif
+
+// ABSL_ATTRIBUTE_HOT, ABSL_ATTRIBUTE_COLD
+//
+// Tells GCC that a function is hot or cold. GCC can use this information to
+// improve static analysis, i.e. a conditional branch to a cold function
+// is likely to be not-taken.
+// This annotation is used for function declarations.
+//
+// Example:
+//
+//   int foo() ABSL_ATTRIBUTE_HOT;
+#if ABSL_HAVE_ATTRIBUTE(hot) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_HOT __attribute__((hot))
+#else
+#define ABSL_ATTRIBUTE_HOT
+#endif
+
+#if ABSL_HAVE_ATTRIBUTE(cold) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_COLD __attribute__((cold))
+#else
+#define ABSL_ATTRIBUTE_COLD
+#endif
+
+// ABSL_XRAY_ALWAYS_INSTRUMENT, ABSL_XRAY_NEVER_INSTRUMENT, ABSL_XRAY_LOG_ARGS
+//
+// We define the ABSL_XRAY_ALWAYS_INSTRUMENT and ABSL_XRAY_NEVER_INSTRUMENT
+// macro used as an attribute to mark functions that must always or never be
+// instrumented by XRay. Currently, this is only supported in Clang/LLVM.
+//
+// For reference on the LLVM XRay instrumentation, see
+// http://llvm.org/docs/XRay.html.
+//
+// A function with the XRAY_ALWAYS_INSTRUMENT macro attribute in its declaration
+// will always get the XRay instrumentation sleds. These sleds may introduce
+// some binary size and runtime overhead and must be used sparingly.
+//
+// These attributes only take effect when the following conditions are met:
+//
+//   * The file/target is built in at least C++11 mode, with a Clang compiler
+//     that supports XRay attributes.
+//   * The file/target is built with the -fxray-instrument flag set for the
+//     Clang/LLVM compiler.
+//   * The function is defined in the translation unit (the compiler honors the
+//     attribute in either the definition or the declaration, and must match).
+//
+// There are cases when, even when building with XRay instrumentation, users
+// might want to control specifically which functions are instrumented for a
+// particular build using special-case lists provided to the compiler. These
+// special case lists are provided to Clang via the
+// -fxray-always-instrument=... and -fxray-never-instrument=... flags. The
+// attributes in source take precedence over these special-case lists.
+//
+// To disable the XRay attributes at build-time, users may define
+// ABSL_NO_XRAY_ATTRIBUTES. Do NOT define ABSL_NO_XRAY_ATTRIBUTES on specific
+// packages/targets, as this may lead to conflicting definitions of functions at
+// link-time.
+//
+// XRay isn't currently supported on Android:
+// https://github.com/android/ndk/issues/368
+#if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_always_instrument) && \
+    !defined(ABSL_NO_XRAY_ATTRIBUTES) && !defined(__ANDROID__)
+#define ABSL_XRAY_ALWAYS_INSTRUMENT [[clang::xray_always_instrument]]
+#define ABSL_XRAY_NEVER_INSTRUMENT [[clang::xray_never_instrument]]
+#if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_log_args)
+#define ABSL_XRAY_LOG_ARGS(N) \
+  [[clang::xray_always_instrument, clang::xray_log_args(N)]]
+#else
+#define ABSL_XRAY_LOG_ARGS(N) [[clang::xray_always_instrument]]
+#endif
+#else
+#define ABSL_XRAY_ALWAYS_INSTRUMENT
+#define ABSL_XRAY_NEVER_INSTRUMENT
+#define ABSL_XRAY_LOG_ARGS(N)
+#endif
+
+// ABSL_ATTRIBUTE_REINITIALIZES
+//
+// Indicates that a member function reinitializes the entire object to a known
+// state, independent of the previous state of the object.
+//
+// The clang-tidy check bugprone-use-after-move allows member functions marked
+// with this attribute to be called on objects that have been moved from;
+// without the attribute, this would result in a use-after-move warning.
+#if ABSL_HAVE_CPP_ATTRIBUTE(clang::reinitializes)
+#define ABSL_ATTRIBUTE_REINITIALIZES [[clang::reinitializes]]
+#else
+#define ABSL_ATTRIBUTE_REINITIALIZES
+#endif
+
+// -----------------------------------------------------------------------------
+// Variable Attributes
+// -----------------------------------------------------------------------------
+
+// ABSL_ATTRIBUTE_UNUSED
+//
+// Prevents the compiler from complaining about variables that appear unused.
+//
+// For code or headers that are assured to only build with C++17 and up, prefer
+// just using the standard '[[maybe_unused]]' directly over this macro.
+//
+// Due to differences in positioning requirements between the old, compiler
+// specific __attribute__ syntax and the now standard [[maybe_unused]], this
+// macro does not attempt to take advantage of '[[maybe_unused]]'.
+#if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__))
+#undef ABSL_ATTRIBUTE_UNUSED
+#define ABSL_ATTRIBUTE_UNUSED __attribute__((__unused__))
+#else
+#define ABSL_ATTRIBUTE_UNUSED
+#endif
+
+// ABSL_ATTRIBUTE_INITIAL_EXEC
+//
+// Tells the compiler to use "initial-exec" mode for a thread-local variable.
+// See http://people.redhat.com/drepper/tls.pdf for the gory details.
+#if ABSL_HAVE_ATTRIBUTE(tls_model) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_INITIAL_EXEC __attribute__((tls_model("initial-exec")))
+#else
+#define ABSL_ATTRIBUTE_INITIAL_EXEC
+#endif
+
+// ABSL_ATTRIBUTE_PACKED
+//
+// Instructs the compiler not to use natural alignment for a tagged data
+// structure, but instead to reduce its alignment to 1.
+//
+// Therefore, DO NOT APPLY THIS ATTRIBUTE TO STRUCTS CONTAINING ATOMICS. Doing
+// so can cause atomic variables to be mis-aligned and silently violate
+// atomicity on x86.
+//
+// This attribute can either be applied to members of a structure or to a
+// structure in its entirety. Applying this attribute (judiciously) to a
+// structure in its entirety to optimize the memory footprint of very
+// commonly-used structs is fine. Do not apply this attribute to a structure in
+// its entirety if the purpose is to control the offsets of the members in the
+// structure. Instead, apply this attribute only to structure members that need
+// it.
+//
+// When applying ABSL_ATTRIBUTE_PACKED only to specific structure members the
+// natural alignment of structure members not annotated is preserved. Aligned
+// member accesses are faster than non-aligned member accesses even if the
+// targeted microprocessor supports non-aligned accesses.
+#if ABSL_HAVE_ATTRIBUTE(packed) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_PACKED __attribute__((__packed__))
+#else
+#define ABSL_ATTRIBUTE_PACKED
+#endif
+
+// ABSL_ATTRIBUTE_FUNC_ALIGN
+//
+// Tells the compiler to align the function start at least to certain
+// alignment boundary
+#if ABSL_HAVE_ATTRIBUTE(aligned) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes) __attribute__((aligned(bytes)))
+#else
+#define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes)
+#endif
+
+// ABSL_FALLTHROUGH_INTENDED
+//
+// Annotates implicit fall-through between switch labels, allowing a case to
+// indicate intentional fallthrough and turn off warnings about any lack of a
+// `break` statement. The ABSL_FALLTHROUGH_INTENDED macro should be followed by
+// a semicolon and can be used in most places where `break` can, provided that
+// no statements exist between it and the next switch label.
+//
+// Example:
+//
+//  switch (x) {
+//    case 40:
+//    case 41:
+//      if (truth_is_out_there) {
+//        ++x;
+//        ABSL_FALLTHROUGH_INTENDED;  // Use instead of/along with annotations
+//                                    // in comments
+//      } else {
+//        return x;
+//      }
+//    case 42:
+//      ...
+//
+// Notes: When supported, GCC and Clang can issue a warning on switch labels
+// with unannotated fallthrough using the warning `-Wimplicit-fallthrough`. See
+// clang documentation on language extensions for details:
+// https://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough
+//
+// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro has
+// no effect on diagnostics. In any case this macro has no effect on runtime
+// behavior and performance of code.
+
+#ifdef ABSL_FALLTHROUGH_INTENDED
+#error "ABSL_FALLTHROUGH_INTENDED should not be defined."
+#elif ABSL_HAVE_CPP_ATTRIBUTE(fallthrough)
+#define ABSL_FALLTHROUGH_INTENDED [[fallthrough]]
+#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::fallthrough)
+#define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]]
+#elif ABSL_HAVE_CPP_ATTRIBUTE(gnu::fallthrough)
+#define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]]
+#else
+#define ABSL_FALLTHROUGH_INTENDED \
+  do {                            \
+  } while (0)
+#endif
+
+// ABSL_DEPRECATED()
+//
+// Marks a deprecated class, struct, enum, function, method and variable
+// declarations. The macro argument is used as a custom diagnostic message (e.g.
+// suggestion of a better alternative).
+//
+// For code or headers that are assured to only build with C++14 and up, prefer
+// just using the standard `[[deprecated("message")]]` directly over this macro.
+//
+// Examples:
+//
+//   class ABSL_DEPRECATED("Use Bar instead") Foo {...};
+//
+//   ABSL_DEPRECATED("Use Baz() instead") void Bar() {...}
+//
+//   template <typename T>
+//   ABSL_DEPRECATED("Use DoThat() instead")
+//   void DoThis();
+//
+//   enum FooEnum {
+//     kBar ABSL_DEPRECATED("Use kBaz instead"),
+//   };
+//
+// Every usage of a deprecated entity will trigger a warning when compiled with
+// GCC/Clang's `-Wdeprecated-declarations` option. Google's production toolchain
+// turns this warning off by default, instead relying on clang-tidy to report
+// new uses of deprecated code.
+#if ABSL_HAVE_ATTRIBUTE(deprecated)
+#define ABSL_DEPRECATED(message) __attribute__((deprecated(message)))
+#else
+#define ABSL_DEPRECATED(message)
+#endif
+
+// ABSL_CONST_INIT
+//
+// A variable declaration annotated with the `ABSL_CONST_INIT` attribute will
+// not compile (on supported platforms) unless the variable has a constant
+// initializer. This is useful for variables with static and thread storage
+// duration, because it guarantees that they will not suffer from the so-called
+// "static init order fiasco".
+//
+// This attribute must be placed on the initializing declaration of the
+// variable. Some compilers will give a -Wmissing-constinit warning when this
+// attribute is placed on some other declaration but missing from the
+// initializing declaration.
+//
+// In some cases (notably with thread_local variables), `ABSL_CONST_INIT` can
+// also be used in a non-initializing declaration to tell the compiler that a
+// variable is already initialized, reducing overhead that would otherwise be
+// incurred by a hidden guard variable. Thus annotating all declarations with
+// this attribute is recommended to potentially enhance optimization.
+//
+// Example:
+//
+//   class MyClass {
+//    public:
+//     ABSL_CONST_INIT static MyType my_var;
+//   };
+//
+//   ABSL_CONST_INIT MyType MyClass::my_var = MakeMyType(...);
+//
+// For code or headers that are assured to only build with C++20 and up, prefer
+// just using the standard `constinit` keyword directly over this macro.
+//
+// Note that this attribute is redundant if the variable is declared constexpr.
+#if defined(__cpp_constinit) && __cpp_constinit >= 201907L
+#define ABSL_CONST_INIT constinit
+#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization)
+#define ABSL_CONST_INIT [[clang::require_constant_initialization]]
+#else
+#define ABSL_CONST_INIT
+#endif
+
+// These annotations are not available yet due to fear of breaking code.
+#define ABSL_ATTRIBUTE_PURE_FUNCTION
+#define ABSL_ATTRIBUTE_CONST_FUNCTION
+
+// ABSL_ATTRIBUTE_LIFETIME_BOUND indicates that a resource owned by a function
+// parameter or implicit object parameter is retained by the return value of the
+// annotated function (or, for a parameter of a constructor, in the value of the
+// constructed object). This attribute causes warnings to be produced if a
+// temporary object does not live long enough.
+//
+// When applied to a reference parameter, the referenced object is assumed to be
+// retained by the return value of the function. When applied to a non-reference
+// parameter (for example, a pointer or a class type), all temporaries
+// referenced by the parameter are assumed to be retained by the return value of
+// the function.
+//
+// See also the upstream documentation:
+// https://clang.llvm.org/docs/AttributeReference.html#lifetimebound
+#if ABSL_HAVE_CPP_ATTRIBUTE(clang::lifetimebound)
+#define ABSL_ATTRIBUTE_LIFETIME_BOUND [[clang::lifetimebound]]
+#elif ABSL_HAVE_ATTRIBUTE(lifetimebound)
+#define ABSL_ATTRIBUTE_LIFETIME_BOUND __attribute__((lifetimebound))
+#else
+#define ABSL_ATTRIBUTE_LIFETIME_BOUND
+#endif
+
+// ABSL_ATTRIBUTE_TRIVIAL_ABI
+// Indicates that a type is "trivially relocatable" -- meaning it can be
+// relocated without invoking the constructor/destructor, using a form of move
+// elision.
+//
+// From a memory safety point of view, putting aside destructor ordering, it's
+// safe to apply ABSL_ATTRIBUTE_TRIVIAL_ABI if an object's location
+// can change over the course of its lifetime: if a constructor can be run one
+// place, and then the object magically teleports to another place where some
+// methods are run, and then the object teleports to yet another place where it
+// is destroyed. This is notably not true for self-referential types, where the
+// move-constructor must keep the self-reference up to date. If the type changed
+// location without invoking the move constructor, it would have a dangling
+// self-reference.
+//
+// The use of this teleporting machinery means that the number of paired
+// move/destroy operations can change, and so it is a bad idea to apply this to
+// a type meant to count the number of moves.
+//
+// Warning: applying this can, rarely, break callers. Objects passed by value
+// will be destroyed at the end of the call, instead of the end of the
+// full-expression containing the call. In addition, it changes the ABI
+// of functions accepting this type by value (e.g. to pass in registers).
+//
+// See also the upstream documentation:
+// https://clang.llvm.org/docs/AttributeReference.html#trivial-abi
+//
+#if ABSL_HAVE_CPP_ATTRIBUTE(clang::trivial_abi)
+#define ABSL_ATTRIBUTE_TRIVIAL_ABI [[clang::trivial_abi]]
+#define ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI 1
+#elif ABSL_HAVE_ATTRIBUTE(trivial_abi)
+#define ABSL_ATTRIBUTE_TRIVIAL_ABI __attribute__((trivial_abi))
+#define ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI 1
+#else
+#define ABSL_ATTRIBUTE_TRIVIAL_ABI
+#endif
+
+#endif  // ABSL_BASE_ATTRIBUTES_H_
+
diff --git a/third_party/abseil-cpp/absl/base/config.h b/third_party/abseil-cpp/absl/base/config.h
new file mode 100644
index 0000000..613057f
--- /dev/null
+++ b/third_party/abseil-cpp/absl/base/config.h
@@ -0,0 +1,954 @@
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: config.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines a set of macros for checking the presence of
+// important compiler and platform features. Such macros can be used to
+// produce portable code by parameterizing compilation based on the presence or
+// lack of a given feature.
+//
+// We define a "feature" as some interface we wish to program to: for example,
+// a library function or system call. A value of `1` indicates support for
+// that feature; any other value indicates the feature support is undefined.
+//
+// Example:
+//
+// Suppose a programmer wants to write a program that uses the 'mmap()' system
+// call. The Abseil macro for that feature (`ABSL_HAVE_MMAP`) allows you to
+// selectively include the `mmap.h` header and bracket code using that feature
+// in the macro:
+//
+//   #include "absl/base/config.h"
+//
+//   #ifdef ABSL_HAVE_MMAP
+//   #include "sys/mman.h"
+//   #endif  //ABSL_HAVE_MMAP
+//
+//   ...
+//   #ifdef ABSL_HAVE_MMAP
+//   void *ptr = mmap(...);
+//   ...
+//   #endif  // ABSL_HAVE_MMAP
+
+#ifndef ABSL_BASE_CONFIG_H_
+#define ABSL_BASE_CONFIG_H_
+
+// Included for the __GLIBC__ macro (or similar macros on other systems).
+#include <limits.h>
+
+#ifdef __cplusplus
+// Included for __GLIBCXX__, _LIBCPP_VERSION
+#include <cstddef>
+#endif  // __cplusplus
+
+// ABSL_INTERNAL_CPLUSPLUS_LANG
+//
+// MSVC does not set the value of __cplusplus correctly, but instead uses
+// _MSVC_LANG as a stand-in.
+// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
+//
+// However, there are reports that MSVC even sets _MSVC_LANG incorrectly at
+// times, for example:
+// https://github.com/microsoft/vscode-cpptools/issues/1770
+// https://reviews.llvm.org/D70996
+//
+// For this reason, this symbol is considered INTERNAL and code outside of
+// Abseil must not use it.
+#if defined(_MSVC_LANG)
+#define ABSL_INTERNAL_CPLUSPLUS_LANG _MSVC_LANG
+#elif defined(__cplusplus)
+#define ABSL_INTERNAL_CPLUSPLUS_LANG __cplusplus
+#endif
+
+#if defined(__APPLE__)
+// Included for TARGET_OS_IPHONE, __IPHONE_OS_VERSION_MIN_REQUIRED,
+// __IPHONE_8_0.
+#include <Availability.h>
+#include <TargetConditionals.h>
+#endif
+
+#include "third_party/abseil-cpp/absl/base/options.h"
+#include "third_party/abseil-cpp/absl/base/policy_checks.h"
+
+// Abseil long-term support (LTS) releases will define
+// `ABSL_LTS_RELEASE_VERSION` to the integer representing the date string of the
+// LTS release version, and will define `ABSL_LTS_RELEASE_PATCH_LEVEL` to the
+// integer representing the patch-level for that release.
+//
+// For example, for LTS release version "20300401.2", this would give us
+// ABSL_LTS_RELEASE_VERSION == 20300401 && ABSL_LTS_RELEASE_PATCH_LEVEL == 2
+//
+// These symbols will not be defined in non-LTS code.
+//
+// Abseil recommends that clients live-at-head. Therefore, if you are using
+// these symbols to assert a minimum version requirement, we recommend you do it
+// as
+//
+// #if defined(ABSL_LTS_RELEASE_VERSION) && ABSL_LTS_RELEASE_VERSION < 20300401
+// #error Project foo requires Abseil LTS version >= 20300401
+// #endif
+//
+// The `defined(ABSL_LTS_RELEASE_VERSION)` part of the check excludes
+// live-at-head clients from the minimum version assertion.
+//
+// See https://abseil.io/about/releases for more information on Abseil release
+// management.
+//
+// LTS releases can be obtained from
+// https://github.com/abseil/abseil-cpp/releases.
+#define ABSL_LTS_RELEASE_VERSION 20230125
+#define ABSL_LTS_RELEASE_PATCH_LEVEL 2
+
+// Helper macro to convert a CPP variable to a string literal.
+#define ABSL_INTERNAL_DO_TOKEN_STR(x) #x
+#define ABSL_INTERNAL_TOKEN_STR(x) ABSL_INTERNAL_DO_TOKEN_STR(x)
+
+// -----------------------------------------------------------------------------
+// Abseil namespace annotations
+// -----------------------------------------------------------------------------
+
+// ABSL_NAMESPACE_BEGIN/ABSL_NAMESPACE_END
+//
+// An annotation placed at the beginning/end of each `namespace absl` scope.
+// This is used to inject an inline namespace.
+//
+// The proper way to write Abseil code in the `absl` namespace is:
+//
+// namespace absl {
+// ABSL_NAMESPACE_BEGIN
+//
+// void Foo();  // absl::Foo().
+//
+// ABSL_NAMESPACE_END
+// }  // namespace absl
+//
+// Users of Abseil should not use these macros, because users of Abseil should
+// not write `namespace absl {` in their own code for any reason.  (Abseil does
+// not support forward declarations of its own types, nor does it support
+// user-provided specialization of Abseil templates.  Code that violates these
+// rules may be broken without warning.)
+#if !defined(ABSL_OPTION_USE_INLINE_NAMESPACE) || \
+    !defined(ABSL_OPTION_INLINE_NAMESPACE_NAME)
+#error options.h is misconfigured.
+#endif
+
+// Check that ABSL_OPTION_INLINE_NAMESPACE_NAME is neither "head" nor ""
+#if defined(__cplusplus) && ABSL_OPTION_USE_INLINE_NAMESPACE == 1
+
+#define ABSL_INTERNAL_INLINE_NAMESPACE_STR \
+  ABSL_INTERNAL_TOKEN_STR(ABSL_OPTION_INLINE_NAMESPACE_NAME)
+
+static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != '\0',
+              "options.h misconfigured: ABSL_OPTION_INLINE_NAMESPACE_NAME must "
+              "not be empty.");
+static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
+                  ABSL_INTERNAL_INLINE_NAMESPACE_STR[1] != 'e' ||
+                  ABSL_INTERNAL_INLINE_NAMESPACE_STR[2] != 'a' ||
+                  ABSL_INTERNAL_INLINE_NAMESPACE_STR[3] != 'd' ||
+                  ABSL_INTERNAL_INLINE_NAMESPACE_STR[4] != '\0',
+              "options.h misconfigured: ABSL_OPTION_INLINE_NAMESPACE_NAME must "
+              "be changed to a new, unique identifier name.");
+
+#endif
+
+#if ABSL_OPTION_USE_INLINE_NAMESPACE == 0
+#define ABSL_NAMESPACE_BEGIN
+#define ABSL_NAMESPACE_END
+#define ABSL_INTERNAL_C_SYMBOL(x) x
+#elif ABSL_OPTION_USE_INLINE_NAMESPACE == 1
+#define ABSL_NAMESPACE_BEGIN \
+  inline namespace ABSL_OPTION_INLINE_NAMESPACE_NAME {
+#define ABSL_NAMESPACE_END }
+#define ABSL_INTERNAL_C_SYMBOL_HELPER_2(x, v) x##_##v
+#define ABSL_INTERNAL_C_SYMBOL_HELPER_1(x, v) \
+  ABSL_INTERNAL_C_SYMBOL_HELPER_2(x, v)
+#define ABSL_INTERNAL_C_SYMBOL(x) \
+  ABSL_INTERNAL_C_SYMBOL_HELPER_1(x, ABSL_OPTION_INLINE_NAMESPACE_NAME)
+#else
+#error options.h is misconfigured.
+#endif
+
+// -----------------------------------------------------------------------------
+// Compiler Feature Checks
+// -----------------------------------------------------------------------------
+
+// ABSL_HAVE_BUILTIN()
+//
+// Checks whether the compiler supports a Clang Feature Checking Macro, and if
+// so, checks whether it supports the provided builtin function "x" where x
+// is one of the functions noted in
+// https://clang.llvm.org/docs/LanguageExtensions.html
+//
+// Note: Use this macro to avoid an extra level of #ifdef __has_builtin check.
+// http://releases.llvm.org/3.3/tools/clang/docs/LanguageExtensions.html
+#ifdef __has_builtin
+#define ABSL_HAVE_BUILTIN(x) __has_builtin(x)
+#else
+#define ABSL_HAVE_BUILTIN(x) 0
+#endif
+
+#ifdef __has_feature
+#define ABSL_HAVE_FEATURE(f) __has_feature(f)
+#else
+#define ABSL_HAVE_FEATURE(f) 0
+#endif
+
+// Portable check for GCC minimum version:
+// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
+#if defined(__GNUC__) && defined(__GNUC_MINOR__)
+#define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) \
+  (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y))
+#else
+#define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) 0
+#endif
+
+#if defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__)
+#define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) \
+  (__clang_major__ > (x) || __clang_major__ == (x) && __clang_minor__ >= (y))
+#else
+#define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) 0
+#endif
+
+// ABSL_HAVE_TLS is defined to 1 when __thread should be supported.
+// We assume __thread is supported on Linux or Asylo when compiled with Clang or
+// compiled against libstdc++ with _GLIBCXX_HAVE_TLS defined.
+#ifdef ABSL_HAVE_TLS
+#error ABSL_HAVE_TLS cannot be directly set
+#elif (defined(__linux__) || defined(__ASYLO__)) && \
+    (defined(__clang__) || defined(_GLIBCXX_HAVE_TLS))
+#define ABSL_HAVE_TLS 1
+#endif
+
+// ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
+//
+// Checks whether `std::is_trivially_destructible<T>` is supported.
+//
+// Notes: All supported compilers using libc++ support this feature, as does
+// gcc >= 4.8.1 using libstdc++, and Visual Studio.
+#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
+#error ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE cannot be directly set
+#elif defined(_LIBCPP_VERSION) || defined(_MSC_VER) || \
+    (defined(__clang__) && __clang_major__ >= 15) ||   \
+    (!defined(__clang__) && defined(__GLIBCXX__) &&    \
+     ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(4, 8))
+#define ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1
+#endif
+
+// ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
+//
+// Checks whether `std::is_trivially_default_constructible<T>` and
+// `std::is_trivially_copy_constructible<T>` are supported.
+
+// ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
+//
+// Checks whether `std::is_trivially_copy_assignable<T>` is supported.
+
+// Notes: Clang with libc++ supports these features, as does gcc >= 7.4 with
+// libstdc++, or gcc >= 8.2 with libc++, and Visual Studio (but not NVCC).
+#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE)
+#error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set
+#elif defined(ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE)
+#error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set
+#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) ||                    \
+    (defined(__clang__) && __clang_major__ >= 15) ||                         \
+    (!defined(__clang__) &&                                                  \
+     ((ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(7, 4) && defined(__GLIBCXX__)) || \
+      (ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(8, 2) &&                          \
+       defined(_LIBCPP_VERSION)))) ||                                        \
+    (defined(_MSC_VER) && !defined(__NVCC__) && !defined(__clang__))
+#define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1
+#define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1
+#endif
+
+// ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE
+//
+// Checks whether `std::is_trivially_copyable<T>` is supported.
+//
+// Notes: Clang 15+ with libc++ supports these features, GCC hasn't been tested.
+#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE)
+#error ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE cannot be directly set
+#elif defined(__clang__) && (__clang_major__ >= 15)
+#define ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE 1
+#endif
+
+// ABSL_HAVE_THREAD_LOCAL
+//
+// Checks whether C++11's `thread_local` storage duration specifier is
+// supported.
+#ifdef ABSL_HAVE_THREAD_LOCAL
+#error ABSL_HAVE_THREAD_LOCAL cannot be directly set
+#elif defined(__APPLE__)
+// Notes:
+// * Xcode's clang did not support `thread_local` until version 8, and
+//   even then not for all iOS < 9.0.
+// * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator
+//   targeting iOS 9.x.
+// * Xcode 10 moves the deployment target check for iOS < 9.0 to link time
+//   making ABSL_HAVE_FEATURE unreliable there.
+//
+#if ABSL_HAVE_FEATURE(cxx_thread_local) && \
+    !(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)
+#define ABSL_HAVE_THREAD_LOCAL 1
+#endif
+#else  // !defined(__APPLE__)
+#define ABSL_HAVE_THREAD_LOCAL 1
+#endif
+
+// There are platforms for which TLS should not be used even though the compiler
+// makes it seem like it's supported (Android NDK < r12b for example).
+// This is primarily because of linker problems and toolchain misconfiguration:
+// Abseil does not intend to support this indefinitely. Currently, the newest
+// toolchain that we intend to support that requires this behavior is the
+// r11 NDK - allowing for a 5 year support window on that means this option
+// is likely to be removed around June of 2021.
+// TLS isn't supported until NDK r12b per
+// https://developer.android.com/ndk/downloads/revision_history.html
+// Since NDK r16, `__NDK_MAJOR__` and `__NDK_MINOR__` are defined in
+// <android/ndk-version.h>. For NDK < r16, users should define these macros,
+// e.g. `-D__NDK_MAJOR__=11 -D__NKD_MINOR__=0` for NDK r11.
+#if defined(__ANDROID__) && defined(__clang__)
+#if __has_include(<android/ndk-version.h>)
+#include <android/ndk-version.h>
+#endif  // __has_include(<android/ndk-version.h>)
+#if defined(__ANDROID__) && defined(__clang__) && defined(__NDK_MAJOR__) && \
+    defined(__NDK_MINOR__) &&                                               \
+    ((__NDK_MAJOR__ < 12) || ((__NDK_MAJOR__ == 12) && (__NDK_MINOR__ < 1)))
+#undef ABSL_HAVE_TLS
+#undef ABSL_HAVE_THREAD_LOCAL
+#endif
+#endif  // defined(__ANDROID__) && defined(__clang__)
+
+// ABSL_HAVE_INTRINSIC_INT128
+//
+// Checks whether the __int128 compiler extension for a 128-bit integral type is
+// supported.
+//
+// Note: __SIZEOF_INT128__ is defined by Clang and GCC when __int128 is
+// supported, but we avoid using it in certain cases:
+// * On Clang:
+//   * Building using Clang for Windows, where the Clang runtime library has
+//     128-bit support only on LP64 architectures, but Windows is LLP64.
+// * On Nvidia's nvcc:
+//   * nvcc also defines __GNUC__ and __SIZEOF_INT128__, but not all versions
+//     actually support __int128.
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+#error ABSL_HAVE_INTRINSIC_INT128 cannot be directly set
+#elif defined(__SIZEOF_INT128__)
+#if (defined(__clang__) && !defined(_WIN32)) || \
+    (defined(__CUDACC__) && __CUDACC_VER_MAJOR__ >= 9) ||                \
+    (defined(__GNUC__) && !defined(__clang__) && !defined(__CUDACC__))
+#define ABSL_HAVE_INTRINSIC_INT128 1
+#elif defined(__CUDACC__)
+// __CUDACC_VER__ is a full version number before CUDA 9, and is defined to a
+// string explaining that it has been removed starting with CUDA 9. We use
+// nested #ifs because there is no short-circuiting in the preprocessor.
+// NOTE: `__CUDACC__` could be undefined while `__CUDACC_VER__` is defined.
+#if __CUDACC_VER__ >= 70000
+#define ABSL_HAVE_INTRINSIC_INT128 1
+#endif  // __CUDACC_VER__ >= 70000
+#endif  // defined(__CUDACC__)
+#endif  // ABSL_HAVE_INTRINSIC_INT128
+
+// ABSL_HAVE_EXCEPTIONS
+//
+// Checks whether the compiler both supports and enables exceptions. Many
+// compilers support a "no exceptions" mode that disables exceptions.
+//
+// Generally, when ABSL_HAVE_EXCEPTIONS is not defined:
+//
+// * Code using `throw` and `try` may not compile.
+// * The `noexcept` specifier will still compile and behave as normal.
+// * The `noexcept` operator may still return `false`.
+//
+// For further details, consult the compiler's documentation.
+#ifdef ABSL_HAVE_EXCEPTIONS
+#error ABSL_HAVE_EXCEPTIONS cannot be directly set.
+#elif ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(3, 6)
+// Clang >= 3.6
+#if ABSL_HAVE_FEATURE(cxx_exceptions)
+#define ABSL_HAVE_EXCEPTIONS 1
+#endif  // ABSL_HAVE_FEATURE(cxx_exceptions)
+#elif defined(__clang__)
+// Clang < 3.6
+// http://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html#the-exceptions-macro
+#if defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions)
+#define ABSL_HAVE_EXCEPTIONS 1
+#endif  // defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions)
+// Handle remaining special cases and default to exceptions being supported.
+#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \
+    !(ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(5, 0) &&                        \
+      !defined(__cpp_exceptions)) &&                                      \
+    !(defined(_MSC_VER) && !defined(_CPPUNWIND))
+#define ABSL_HAVE_EXCEPTIONS 1
+#endif
+
+// -----------------------------------------------------------------------------
+// Platform Feature Checks
+// -----------------------------------------------------------------------------
+
+// Currently supported operating systems and associated preprocessor
+// symbols:
+//
+//   Linux and Linux-derived           __linux__
+//   Android                           __ANDROID__ (implies __linux__)
+//   Linux (non-Android)               __linux__ && !__ANDROID__
+//   Darwin (macOS and iOS)            __APPLE__
+//   Akaros (http://akaros.org)        __ros__
+//   Windows                           _WIN32
+//   NaCL                              __native_client__
+//   AsmJS                             __asmjs__
+//   WebAssembly                       __wasm__
+//   Fuchsia                           __Fuchsia__
+//
+// Note that since Android defines both __ANDROID__ and __linux__, one
+// may probe for either Linux or Android by simply testing for __linux__.
+
+// ABSL_HAVE_MMAP
+//
+// Checks whether the platform has an mmap(2) implementation as defined in
+// POSIX.1-2001.
+#ifdef ABSL_HAVE_MMAP
+#error ABSL_HAVE_MMAP cannot be directly set
+#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
+    defined(_AIX) || defined(__ros__) || defined(__native_client__) ||    \
+    defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__) ||    \
+    defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) ||       \
+    defined(__HAIKU__) || defined(__OpenBSD__) || defined(__NetBSD__) ||  \
+    defined(__QNX__)
+#define ABSL_HAVE_MMAP 1
+#endif
+
+// ABSL_HAVE_PTHREAD_GETSCHEDPARAM
+//
+// Checks whether the platform implements the pthread_(get|set)schedparam(3)
+// functions as defined in POSIX.1-2001.
+#ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM
+#error ABSL_HAVE_PTHREAD_GETSCHEDPARAM cannot be directly set
+#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
+    defined(_AIX) || defined(__ros__) || defined(__OpenBSD__) ||          \
+    defined(__NetBSD__)
+#define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1
+#endif
+
+// ABSL_HAVE_SCHED_GETCPU
+//
+// Checks whether sched_getcpu is available.
+#ifdef ABSL_HAVE_SCHED_GETCPU
+#error ABSL_HAVE_SCHED_GETCPU cannot be directly set
+#elif defined(__linux__)
+#define ABSL_HAVE_SCHED_GETCPU 1
+#endif
+
+// ABSL_HAVE_SCHED_YIELD
+//
+// Checks whether the platform implements sched_yield(2) as defined in
+// POSIX.1-2001.
+#ifdef ABSL_HAVE_SCHED_YIELD
+#error ABSL_HAVE_SCHED_YIELD cannot be directly set
+#elif defined(__linux__) || defined(__ros__) || defined(__native_client__)
+#define ABSL_HAVE_SCHED_YIELD 1
+#endif
+
+// ABSL_HAVE_SEMAPHORE_H
+//
+// Checks whether the platform supports the <semaphore.h> header and sem_init(3)
+// family of functions as standardized in POSIX.1-2001.
+//
+// Note: While Apple provides <semaphore.h> for both iOS and macOS, it is
+// explicitly deprecated and will cause build failures if enabled for those
+// platforms.  We side-step the issue by not defining it here for Apple
+// platforms.
+#ifdef ABSL_HAVE_SEMAPHORE_H
+#error ABSL_HAVE_SEMAPHORE_H cannot be directly set
+#elif defined(__linux__) || defined(__ros__)
+#define ABSL_HAVE_SEMAPHORE_H 1
+#endif
+
+// ABSL_HAVE_ALARM
+//
+// Checks whether the platform supports the <signal.h> header and alarm(2)
+// function as standardized in POSIX.1-2001.
+#ifdef ABSL_HAVE_ALARM
+#error ABSL_HAVE_ALARM cannot be directly set
+#elif defined(__GOOGLE_GRTE_VERSION__)
+// feature tests for Google's GRTE
+#define ABSL_HAVE_ALARM 1
+#elif defined(__GLIBC__)
+// feature test for glibc
+#define ABSL_HAVE_ALARM 1
+#elif defined(_MSC_VER)
+// feature tests for Microsoft's library
+#elif defined(__MINGW32__)
+// mingw32 doesn't provide alarm(2):
+// https://osdn.net/projects/mingw/scm/git/mingw-org-wsl/blobs/5.2-trunk/mingwrt/include/unistd.h
+// mingw-w64 provides a no-op implementation:
+// https://sourceforge.net/p/mingw-w64/mingw-w64/ci/master/tree/mingw-w64-crt/misc/alarm.c
+#elif defined(__EMSCRIPTEN__)
+// emscripten doesn't support signals
+#elif defined(__Fuchsia__)
+// Signals don't exist on fuchsia.
+#elif defined(__native_client__)
+#else
+// other standard libraries
+#define ABSL_HAVE_ALARM 1
+#endif
+
+// ABSL_IS_LITTLE_ENDIAN
+// ABSL_IS_BIG_ENDIAN
+//
+// Checks the endianness of the platform.
+//
+// Notes: uses the built in endian macros provided by GCC (since 4.6) and
+// Clang (since 3.2); see
+// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html.
+// Otherwise, if _WIN32, assume little endian. Otherwise, bail with an error.
+#if defined(ABSL_IS_BIG_ENDIAN)
+#error "ABSL_IS_BIG_ENDIAN cannot be directly set."
+#endif
+#if defined(ABSL_IS_LITTLE_ENDIAN)
+#error "ABSL_IS_LITTLE_ENDIAN cannot be directly set."
+#endif
+
+#if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
+     __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+#define ABSL_IS_LITTLE_ENDIAN 1
+#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
+    __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define ABSL_IS_BIG_ENDIAN 1
+#elif defined(_WIN32)
+#define ABSL_IS_LITTLE_ENDIAN 1
+#else
+#error "absl endian detection needs to be set up for your compiler"
+#endif
+
+// macOS < 10.13 and iOS < 11 don't let you use <any>, <optional>, or <variant>
+// even though the headers exist and are publicly noted to work, because the
+// libc++ shared library shipped on the system doesn't have the requisite
+// exported symbols.  See https://github.com/abseil/abseil-cpp/issues/207 and
+// https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes
+//
+// libc++ spells out the availability requirements in the file
+// llvm-project/libcxx/include/__config via the #define
+// _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS.
+//
+// Unfortunately, Apple initially mis-stated the requirements as macOS < 10.14
+// and iOS < 12 in the libc++ headers. This was corrected by
+// https://github.com/llvm/llvm-project/commit/7fb40e1569dd66292b647f4501b85517e9247953
+// which subsequently made it into the XCode 12.5 release. We need to match the
+// old (incorrect) conditions when built with old XCode, but can use the
+// corrected earlier versions with new XCode.
+#if defined(__APPLE__) && defined(_LIBCPP_VERSION) &&               \
+    ((_LIBCPP_VERSION >= 11000 && /* XCode 12.5 or later: */        \
+      ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) &&   \
+        __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101300) ||  \
+       (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) &&  \
+        __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 110000) || \
+       (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) &&   \
+        __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 40000) ||   \
+       (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) &&      \
+        __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 110000))) ||   \
+     (_LIBCPP_VERSION < 11000 && /* Pre-XCode 12.5: */              \
+      ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) &&   \
+        __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101400) ||  \
+       (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) &&  \
+        __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 120000) || \
+       (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) &&   \
+        __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 50000) ||   \
+       (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) &&      \
+        __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 120000))))
+#define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 1
+#else
+#define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 0
+#endif
+
+// ABSL_HAVE_STD_ANY
+//
+// Checks whether C++17 std::any is available by checking whether <any> exists.
+#ifdef ABSL_HAVE_STD_ANY
+#error "ABSL_HAVE_STD_ANY cannot be directly set."
+#endif
+
+#ifdef __has_include
+#if __has_include(<any>) && defined(__cplusplus) && __cplusplus >= 201703L && \
+    !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
+#define ABSL_HAVE_STD_ANY 1
+#endif
+#endif
+
+// ABSL_HAVE_STD_OPTIONAL
+//
+// Checks whether C++17 std::optional is available.
+#ifdef ABSL_HAVE_STD_OPTIONAL
+#error "ABSL_HAVE_STD_OPTIONAL cannot be directly set."
+#endif
+
+#ifdef __has_include
+#if __has_include(<optional>) && defined(__cplusplus) && \
+    __cplusplus >= 201703L && !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
+#define ABSL_HAVE_STD_OPTIONAL 1
+#endif
+#endif
+
+// ABSL_HAVE_STD_VARIANT
+//
+// Checks whether C++17 std::variant is available.
+#ifdef ABSL_HAVE_STD_VARIANT
+#error "ABSL_HAVE_STD_VARIANT cannot be directly set."
+#endif
+
+#ifdef __has_include
+#if __has_include(<variant>) && defined(__cplusplus) && \
+    __cplusplus >= 201703L && !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
+#define ABSL_HAVE_STD_VARIANT 1
+#endif
+#endif
+
+// ABSL_HAVE_STD_STRING_VIEW
+//
+// Checks whether C++17 std::string_view is available.
+#ifdef ABSL_HAVE_STD_STRING_VIEW
+#error "ABSL_HAVE_STD_STRING_VIEW cannot be directly set."
+#endif
+
+#ifdef __has_include
+#if __has_include(<string_view>) && defined(__cplusplus) && \
+    __cplusplus >= 201703L
+#define ABSL_HAVE_STD_STRING_VIEW 1
+#endif
+#endif
+
+// For MSVC, `__has_include` is supported in VS 2017 15.3, which is later than
+// the support for <optional>, <any>, <string_view>, <variant>. So we use
+// _MSC_VER to check whether we have VS 2017 RTM (when <optional>, <any>,
+// <string_view>, <variant> is implemented) or higher. Also, `__cplusplus` is
+// not correctly set by MSVC, so we use `_MSVC_LANG` to check the language
+// version.
+// TODO(zhangxy): fix tests before enabling aliasing for `std::any`.
+#if defined(_MSC_VER) && _MSC_VER >= 1910 &&         \
+    ((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || \
+     (defined(__cplusplus) && __cplusplus > 201402))
+// #define ABSL_HAVE_STD_ANY 1
+#define ABSL_HAVE_STD_OPTIONAL 1
+#define ABSL_HAVE_STD_VARIANT 1
+#define ABSL_HAVE_STD_STRING_VIEW 1
+#endif
+
+// ABSL_USES_STD_ANY
+//
+// Indicates whether absl::any is an alias for std::any.
+#if !defined(ABSL_OPTION_USE_STD_ANY)
+#error options.h is misconfigured.
+#elif ABSL_OPTION_USE_STD_ANY == 0 || \
+    (ABSL_OPTION_USE_STD_ANY == 2 && !defined(ABSL_HAVE_STD_ANY))
+#undef ABSL_USES_STD_ANY
+#elif ABSL_OPTION_USE_STD_ANY == 1 || \
+    (ABSL_OPTION_USE_STD_ANY == 2 && defined(ABSL_HAVE_STD_ANY))
+#define ABSL_USES_STD_ANY 1
+#else
+#error options.h is misconfigured.
+#endif
+
+// ABSL_USES_STD_OPTIONAL
+//
+// Indicates whether absl::optional is an alias for std::optional.
+#if !defined(ABSL_OPTION_USE_STD_OPTIONAL)
+#error options.h is misconfigured.
+#elif ABSL_OPTION_USE_STD_OPTIONAL == 0 || \
+    (ABSL_OPTION_USE_STD_OPTIONAL == 2 && !defined(ABSL_HAVE_STD_OPTIONAL))
+#undef ABSL_USES_STD_OPTIONAL
+#elif ABSL_OPTION_USE_STD_OPTIONAL == 1 || \
+    (ABSL_OPTION_USE_STD_OPTIONAL == 2 && defined(ABSL_HAVE_STD_OPTIONAL))
+#define ABSL_USES_STD_OPTIONAL 1
+#else
+#error options.h is misconfigured.
+#endif
+
+// ABSL_USES_STD_VARIANT
+//
+// Indicates whether absl::variant is an alias for std::variant.
+#if !defined(ABSL_OPTION_USE_STD_VARIANT)
+#error options.h is misconfigured.
+#elif ABSL_OPTION_USE_STD_VARIANT == 0 || \
+    (ABSL_OPTION_USE_STD_VARIANT == 2 && !defined(ABSL_HAVE_STD_VARIANT))
+#undef ABSL_USES_STD_VARIANT
+#elif ABSL_OPTION_USE_STD_VARIANT == 1 || \
+    (ABSL_OPTION_USE_STD_VARIANT == 2 && defined(ABSL_HAVE_STD_VARIANT))
+#define ABSL_USES_STD_VARIANT 1
+#else
+#error options.h is misconfigured.
+#endif
+
+// ABSL_USES_STD_STRING_VIEW
+//
+// Indicates whether absl::string_view is an alias for std::string_view.
+#if !defined(ABSL_OPTION_USE_STD_STRING_VIEW)
+#error options.h is misconfigured.
+#elif ABSL_OPTION_USE_STD_STRING_VIEW == 0 || \
+    (ABSL_OPTION_USE_STD_STRING_VIEW == 2 &&  \
+     !defined(ABSL_HAVE_STD_STRING_VIEW))
+#undef ABSL_USES_STD_STRING_VIEW
+#elif ABSL_OPTION_USE_STD_STRING_VIEW == 1 || \
+    (ABSL_OPTION_USE_STD_STRING_VIEW == 2 &&  \
+     defined(ABSL_HAVE_STD_STRING_VIEW))
+#define ABSL_USES_STD_STRING_VIEW 1
+#else
+#error options.h is misconfigured.
+#endif
+
+// In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION
+// SEH exception from emplace for variant<SomeStruct> when constructing the
+// struct can throw. This defeats some of variant_test and
+// variant_exception_safety_test.
+#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_DEBUG)
+#define ABSL_INTERNAL_MSVC_2017_DBG_MODE
+#endif
+
+// ABSL_INTERNAL_MANGLED_NS
+// ABSL_INTERNAL_MANGLED_BACKREFERENCE
+//
+// Internal macros for building up mangled names in our internal fork of CCTZ.
+// This implementation detail is only needed and provided for the MSVC build.
+//
+// These macros both expand to string literals.  ABSL_INTERNAL_MANGLED_NS is
+// the mangled spelling of the `absl` namespace, and
+// ABSL_INTERNAL_MANGLED_BACKREFERENCE is a back-reference integer representing
+// the proper count to skip past the CCTZ fork namespace names.  (This number
+// is one larger when there is an inline namespace name to skip.)
+#if defined(_MSC_VER)
+#if ABSL_OPTION_USE_INLINE_NAMESPACE == 0
+#define ABSL_INTERNAL_MANGLED_NS "absl"
+#define ABSL_INTERNAL_MANGLED_BACKREFERENCE "5"
+#else
+#define ABSL_INTERNAL_MANGLED_NS \
+  ABSL_INTERNAL_TOKEN_STR(ABSL_OPTION_INLINE_NAMESPACE_NAME) "@absl"
+#define ABSL_INTERNAL_MANGLED_BACKREFERENCE "6"
+#endif
+#endif
+
+// ABSL_DLL
+//
+// When building Abseil as a DLL, this macro expands to `__declspec(dllexport)`
+// so we can annotate symbols appropriately as being exported. When used in
+// headers consuming a DLL, this macro expands to `__declspec(dllimport)` so
+// that consumers know the symbol is defined inside the DLL. In all other cases,
+// the macro expands to nothing.
+#if defined(_MSC_VER)
+#if defined(ABSL_BUILD_DLL)
+#define ABSL_DLL __declspec(dllexport)
+#elif defined(ABSL_CONSUME_DLL)
+#define ABSL_DLL __declspec(dllimport)
+#else
+#define ABSL_DLL
+#endif
+#else
+#define ABSL_DLL
+#endif  // defined(_MSC_VER)
+
+#if defined(_MSC_VER)
+#if defined(ABSL_BUILD_TEST_DLL)
+#define ABSL_TEST_DLL __declspec(dllexport)
+#elif defined(ABSL_CONSUME_TEST_DLL)
+#define ABSL_TEST_DLL __declspec(dllimport)
+#else
+#define ABSL_TEST_DLL
+#endif
+#else
+#define ABSL_TEST_DLL
+#endif  // defined(_MSC_VER)
+
+// ABSL_HAVE_MEMORY_SANITIZER
+//
+// MemorySanitizer (MSan) is a detector of uninitialized reads. It consists of
+// a compiler instrumentation module and a run-time library.
+#ifdef ABSL_HAVE_MEMORY_SANITIZER
+#error "ABSL_HAVE_MEMORY_SANITIZER cannot be directly set."
+#elif !defined(__native_client__) && ABSL_HAVE_FEATURE(memory_sanitizer)
+#define ABSL_HAVE_MEMORY_SANITIZER 1
+#endif
+
+// ABSL_HAVE_THREAD_SANITIZER
+//
+// ThreadSanitizer (TSan) is a fast data race detector.
+#ifdef ABSL_HAVE_THREAD_SANITIZER
+#error "ABSL_HAVE_THREAD_SANITIZER cannot be directly set."
+#elif defined(__SANITIZE_THREAD__)
+#define ABSL_HAVE_THREAD_SANITIZER 1
+#elif ABSL_HAVE_FEATURE(thread_sanitizer)
+#define ABSL_HAVE_THREAD_SANITIZER 1
+#endif
+
+// ABSL_HAVE_ADDRESS_SANITIZER
+//
+// AddressSanitizer (ASan) is a fast memory error detector.
+#ifdef ABSL_HAVE_ADDRESS_SANITIZER
+#error "ABSL_HAVE_ADDRESS_SANITIZER cannot be directly set."
+#elif defined(__SANITIZE_ADDRESS__)
+#define ABSL_HAVE_ADDRESS_SANITIZER 1
+#elif ABSL_HAVE_FEATURE(address_sanitizer)
+#define ABSL_HAVE_ADDRESS_SANITIZER 1
+#endif
+
+// ABSL_HAVE_HWADDRESS_SANITIZER
+//
+// Hardware-Assisted AddressSanitizer (or HWASAN) is even faster than asan
+// memory error detector which can use CPU features like ARM TBI, Intel LAM or
+// AMD UAI.
+#ifdef ABSL_HAVE_HWADDRESS_SANITIZER
+#error "ABSL_HAVE_HWADDRESS_SANITIZER cannot be directly set."
+#elif defined(__SANITIZE_HWADDRESS__)
+#define ABSL_HAVE_HWADDRESS_SANITIZER 1
+#elif ABSL_HAVE_FEATURE(hwaddress_sanitizer)
+#define ABSL_HAVE_HWADDRESS_SANITIZER 1
+#endif
+
+// ABSL_HAVE_LEAK_SANITIZER
+//
+// LeakSanitizer (or lsan) is a detector of memory leaks.
+// https://clang.llvm.org/docs/LeakSanitizer.html
+// https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer
+//
+// The macro ABSL_HAVE_LEAK_SANITIZER can be used to detect at compile-time
+// whether the LeakSanitizer is potentially available. However, just because the
+// LeakSanitizer is available does not mean it is active. Use the
+// always-available run-time interface in //absl/debugging/leak_check.h for
+// interacting with LeakSanitizer.
+#ifdef ABSL_HAVE_LEAK_SANITIZER
+#error "ABSL_HAVE_LEAK_SANITIZER cannot be directly set."
+#elif defined(LEAK_SANITIZER)
+// GCC provides no method for detecting the presense of the standalone
+// LeakSanitizer (-fsanitize=leak), so GCC users of -fsanitize=leak should also
+// use -DLEAK_SANITIZER.
+#define ABSL_HAVE_LEAK_SANITIZER 1
+// Clang standalone LeakSanitizer (-fsanitize=leak)
+#elif ABSL_HAVE_FEATURE(leak_sanitizer)
+#define ABSL_HAVE_LEAK_SANITIZER 1
+#elif defined(ABSL_HAVE_ADDRESS_SANITIZER)
+// GCC or Clang using the LeakSanitizer integrated into AddressSanitizer.
+#define ABSL_HAVE_LEAK_SANITIZER 1
+#endif
+
+// ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION
+//
+// Class template argument deduction is a language feature added in C++17.
+#ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION
+#error "ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION cannot be directly set."
+#elif defined(__cpp_deduction_guides)
+#define ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION 1
+#endif
+
+// ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
+//
+// Prior to C++17, static constexpr variables defined in classes required a
+// separate definition outside of the class body, for example:
+//
+// class Foo {
+//   static constexpr int kBar = 0;
+// };
+// constexpr int Foo::kBar;
+//
+// In C++17, these variables defined in classes are considered inline variables,
+// and the extra declaration is redundant. Since some compilers warn on the
+// extra declarations, ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL can be used
+// conditionally ignore them:
+//
+// #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
+// constexpr int Foo::kBar;
+// #endif
+#if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
+    ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L
+#define ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL 1
+#endif
+
+// `ABSL_INTERNAL_HAS_RTTI` determines whether abseil is being compiled with
+// RTTI support.
+#ifdef ABSL_INTERNAL_HAS_RTTI
+#error ABSL_INTERNAL_HAS_RTTI cannot be directly set
+#elif !defined(__GNUC__) || defined(__GXX_RTTI)
+#define ABSL_INTERNAL_HAS_RTTI 1
+#endif  // !defined(__GNUC__) || defined(__GXX_RTTI)
+
+// ABSL_INTERNAL_HAVE_SSE is used for compile-time detection of SSE support.
+// See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of
+// which architectures support the various x86 instruction sets.
+#ifdef ABSL_INTERNAL_HAVE_SSE
+#error ABSL_INTERNAL_HAVE_SSE cannot be directly set
+#elif defined(__SSE__)
+#define ABSL_INTERNAL_HAVE_SSE 1
+#elif defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 1)
+// MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 1
+// indicates that at least SSE was targeted with the /arch:SSE option.
+// All x86-64 processors support SSE, so support can be assumed.
+// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
+#define ABSL_INTERNAL_HAVE_SSE 1
+#endif
+
+// ABSL_INTERNAL_HAVE_SSE2 is used for compile-time detection of SSE2 support.
+// See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of
+// which architectures support the various x86 instruction sets.
+#ifdef ABSL_INTERNAL_HAVE_SSE2
+#error ABSL_INTERNAL_HAVE_SSE2 cannot be directly set
+#elif defined(__SSE2__)
+#define ABSL_INTERNAL_HAVE_SSE2 1
+#elif defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
+// MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 2
+// indicates that at least SSE2 was targeted with the /arch:SSE2 option.
+// All x86-64 processors support SSE2, so support can be assumed.
+// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
+#define ABSL_INTERNAL_HAVE_SSE2 1
+#endif
+
+// ABSL_INTERNAL_HAVE_SSSE3 is used for compile-time detection of SSSE3 support.
+// See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of
+// which architectures support the various x86 instruction sets.
+//
+// MSVC does not have a mode that targets SSSE3 at compile-time. To use SSSE3
+// with MSVC requires either assuming that the code will only every run on CPUs
+// that support SSSE3, otherwise __cpuid() can be used to detect support at
+// runtime and fallback to a non-SSSE3 implementation when SSSE3 is unsupported
+// by the CPU.
+#ifdef ABSL_INTERNAL_HAVE_SSSE3
+#error ABSL_INTERNAL_HAVE_SSSE3 cannot be directly set
+#elif defined(__SSSE3__)
+#define ABSL_INTERNAL_HAVE_SSSE3 1
+#endif
+
+// ABSL_INTERNAL_HAVE_ARM_NEON is used for compile-time detection of NEON (ARM
+// SIMD).
+//
+// If __CUDA_ARCH__ is defined, then we are compiling CUDA code in device mode.
+// In device mode, NEON intrinsics are not available, regardless of host
+// platform.
+// https://llvm.org/docs/CompileCudaWithLLVM.html#detecting-clang-vs-nvcc-from-code
+#ifdef ABSL_INTERNAL_HAVE_ARM_NEON
+#error ABSL_INTERNAL_HAVE_ARM_NEON cannot be directly set
+#elif defined(__ARM_NEON) && !defined(__CUDA_ARCH__)
+#define ABSL_INTERNAL_HAVE_ARM_NEON 1
+#endif
+
+// ABSL_HAVE_CONSTANT_EVALUATED is used for compile-time detection of
+// constant evaluation support through `absl::is_constant_evaluated`.
+#ifdef ABSL_HAVE_CONSTANT_EVALUATED
+#error ABSL_HAVE_CONSTANT_EVALUATED cannot be directly set
+#endif
+#ifdef __cpp_lib_is_constant_evaluated
+#define ABSL_HAVE_CONSTANT_EVALUATED 1
+#elif ABSL_HAVE_BUILTIN(__builtin_is_constant_evaluated)
+#define ABSL_HAVE_CONSTANT_EVALUATED 1
+#endif
+
+#endif  // ABSL_BASE_CONFIG_H_
diff --git a/third_party/abseil-cpp/absl/base/internal/identity.h b/third_party/abseil-cpp/absl/base/internal/identity.h
new file mode 100644
index 0000000..a397aaa
--- /dev/null
+++ b/third_party/abseil-cpp/absl/base/internal/identity.h
@@ -0,0 +1,37 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef ABSL_BASE_INTERNAL_IDENTITY_H_
+#define ABSL_BASE_INTERNAL_IDENTITY_H_
+
+#include "third_party/abseil-cpp/absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace internal {
+
+template <typename T>
+struct identity {
+  typedef T type;
+};
+
+template <typename T>
+using identity_t = typename identity<T>::type;
+
+}  // namespace internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_BASE_INTERNAL_IDENTITY_H_
diff --git a/third_party/abseil-cpp/absl/base/internal/inline_variable.h b/third_party/abseil-cpp/absl/base/internal/inline_variable.h
new file mode 100644
index 0000000..2c03dff
--- /dev/null
+++ b/third_party/abseil-cpp/absl/base/internal/inline_variable.h
@@ -0,0 +1,107 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_BASE_INTERNAL_INLINE_VARIABLE_H_
+#define ABSL_BASE_INTERNAL_INLINE_VARIABLE_H_
+
+#include <type_traits>
+
+#include "third_party/abseil-cpp/absl/base/internal/identity.h"
+
+// File:
+//   This file define a macro that allows the creation of or emulation of C++17
+//   inline variables based on whether or not the feature is supported.
+
+////////////////////////////////////////////////////////////////////////////////
+// Macro: ABSL_INTERNAL_INLINE_CONSTEXPR(type, name, init)
+//
+// Description:
+//   Expands to the equivalent of an inline constexpr instance of the specified
+//   `type` and `name`, initialized to the value `init`. If the compiler being
+//   used is detected as supporting actual inline variables as a language
+//   feature, then the macro expands to an actual inline variable definition.
+//
+// Requires:
+//   `type` is a type that is usable in an extern variable declaration.
+//
+// Requires: `name` is a valid identifier
+//
+// Requires:
+//   `init` is an expression that can be used in the following definition:
+//     constexpr type name = init;
+//
+// Usage:
+//
+//   // Equivalent to: `inline constexpr size_t variant_npos = -1;`
+//   ABSL_INTERNAL_INLINE_CONSTEXPR(size_t, variant_npos, -1);
+//
+// Differences in implementation:
+//   For a direct, language-level inline variable, decltype(name) will be the
+//   type that was specified along with const qualification, whereas for
+//   emulated inline variables, decltype(name) may be different (in practice
+//   it will likely be a reference type).
+////////////////////////////////////////////////////////////////////////////////
+
+#ifdef __cpp_inline_variables
+
+// Clang's -Wmissing-variable-declarations option erroneously warned that
+// inline constexpr objects need to be pre-declared. This has now been fixed,
+// but we will need to support this workaround for people building with older
+// versions of clang.
+//
+// Bug: https://bugs.llvm.org/show_bug.cgi?id=35862
+//
+// Note:
+//   identity_t is used here so that the const and name are in the
+//   appropriate place for pointer types, reference types, function pointer
+//   types, etc..
+#if defined(__clang__)
+#define ABSL_INTERNAL_EXTERN_DECL(type, name) \
+  extern const ::absl::internal::identity_t<type> name;
+#else  // Otherwise, just define the macro to do nothing.
+#define ABSL_INTERNAL_EXTERN_DECL(type, name)
+#endif  // defined(__clang__)
+
+// See above comment at top of file for details.
+#define ABSL_INTERNAL_INLINE_CONSTEXPR(type, name, init) \
+  ABSL_INTERNAL_EXTERN_DECL(type, name)                  \
+  inline constexpr ::absl::internal::identity_t<type> name = init
+
+#else
+
+// See above comment at top of file for details.
+//
+// Note:
+//   identity_t is used here so that the const and name are in the
+//   appropriate place for pointer types, reference types, function pointer
+//   types, etc..
+#define ABSL_INTERNAL_INLINE_CONSTEXPR(var_type, name, init)                  \
+  template <class /*AbslInternalDummy*/ = void>                               \
+  struct AbslInternalInlineVariableHolder##name {                             \
+    static constexpr ::absl::internal::identity_t<var_type> kInstance = init; \
+  };                                                                          \
+                                                                              \
+  template <class AbslInternalDummy>                                          \
+  constexpr ::absl::internal::identity_t<var_type>                            \
+      AbslInternalInlineVariableHolder##name<AbslInternalDummy>::kInstance;   \
+                                                                              \
+  static constexpr const ::absl::internal::identity_t<var_type>&              \
+      name = /* NOLINT */                                                     \
+      AbslInternalInlineVariableHolder##name<>::kInstance;                    \
+  static_assert(sizeof(void (*)(decltype(name))) != 0,                        \
+                "Silence unused variable warnings.")
+
+#endif  // __cpp_inline_variables
+
+#endif  // ABSL_BASE_INTERNAL_INLINE_VARIABLE_H_
diff --git a/third_party/abseil-cpp/absl/base/internal/invoke.h b/third_party/abseil-cpp/absl/base/internal/invoke.h
new file mode 100644
index 0000000..0933032
--- /dev/null
+++ b/third_party/abseil-cpp/absl/base/internal/invoke.h
@@ -0,0 +1,241 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// absl::base_internal::invoke(f, args...) is an implementation of
+// INVOKE(f, args...) from section [func.require] of the C++ standard.
+// When compiled as C++17 and later versions, it is implemented as an alias of
+// std::invoke.
+//
+// [func.require]
+// Define INVOKE (f, t1, t2, ..., tN) as follows:
+// 1. (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T
+//    and t1 is an object of type T or a reference to an object of type T or a
+//    reference to an object of a type derived from T;
+// 2. ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a
+//    class T and t1 is not one of the types described in the previous item;
+// 3. t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is
+//    an object of type T or a reference to an object of type T or a reference
+//    to an object of a type derived from T;
+// 4. (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1
+//    is not one of the types described in the previous item;
+// 5. f(t1, t2, ..., tN) in all other cases.
+//
+// The implementation is SFINAE-friendly: substitution failure within invoke()
+// isn't an error.
+
+#ifndef ABSL_BASE_INTERNAL_INVOKE_H_
+#define ABSL_BASE_INTERNAL_INVOKE_H_
+
+#include "third_party/abseil-cpp/absl/base/config.h"
+
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
+
+#include <functional>
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace base_internal {
+
+using std::invoke;
+using std::invoke_result_t;
+using std::is_invocable_r;
+
+}  // namespace base_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#else  // ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
+
+#include <algorithm>
+#include <type_traits>
+#include <utility>
+
+#include "third_party/absl/meta/type_traits.h"
+
+// The following code is internal implementation detail.  See the comment at the
+// top of this file for the API documentation.
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace base_internal {
+
+// The five classes below each implement one of the clauses from the definition
+// of INVOKE. The inner class template Accept<F, Args...> checks whether the
+// clause is applicable; static function template Invoke(f, args...) does the
+// invocation.
+//
+// By separating the clause selection logic from invocation we make sure that
+// Invoke() does exactly what the standard says.
+
+template <typename Derived>
+struct StrippedAccept {
+  template <typename... Args>
+  struct Accept : Derived::template AcceptImpl<typename std::remove_cv<
+                      typename std::remove_reference<Args>::type>::type...> {};
+};
+
+// (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T
+// and t1 is an object of type T or a reference to an object of type T or a
+// reference to an object of a type derived from T.
+struct MemFunAndRef : StrippedAccept<MemFunAndRef> {
+  template <typename... Args>
+  struct AcceptImpl : std::false_type {};
+
+  template <typename MemFunType, typename C, typename Obj, typename... Args>
+  struct AcceptImpl<MemFunType C::*, Obj, Args...>
+      : std::integral_constant<bool, std::is_base_of<C, Obj>::value &&
+                                         absl::is_function<MemFunType>::value> {
+  };
+
+  template <typename MemFun, typename Obj, typename... Args>
+  static decltype((std::declval<Obj>().*
+                   std::declval<MemFun>())(std::declval<Args>()...))
+  Invoke(MemFun&& mem_fun, Obj&& obj, Args&&... args) {
+// Ignore bogus GCC warnings on this line.
+// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101436 for similar example.
+#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(11, 0)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+    return (std::forward<Obj>(obj).*
+            std::forward<MemFun>(mem_fun))(std::forward<Args>(args)...);
+#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(11, 0)
+#pragma GCC diagnostic pop
+#endif
+  }
+};
+
+// ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a
+// class T and t1 is not one of the types described in the previous item.
+struct MemFunAndPtr : StrippedAccept<MemFunAndPtr> {
+  template <typename... Args>
+  struct AcceptImpl : std::false_type {};
+
+  template <typename MemFunType, typename C, typename Ptr, typename... Args>
+  struct AcceptImpl<MemFunType C::*, Ptr, Args...>
+      : std::integral_constant<bool, !std::is_base_of<C, Ptr>::value &&
+                                         absl::is_function<MemFunType>::value> {
+  };
+
+  template <typename MemFun, typename Ptr, typename... Args>
+  static decltype(((*std::declval<Ptr>()).*
+                   std::declval<MemFun>())(std::declval<Args>()...))
+  Invoke(MemFun&& mem_fun, Ptr&& ptr, Args&&... args) {
+    return ((*std::forward<Ptr>(ptr)).*
+            std::forward<MemFun>(mem_fun))(std::forward<Args>(args)...);
+  }
+};
+
+// t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is
+// an object of type T or a reference to an object of type T or a reference
+// to an object of a type derived from T.
+struct DataMemAndRef : StrippedAccept<DataMemAndRef> {
+  template <typename... Args>
+  struct AcceptImpl : std::false_type {};
+
+  template <typename R, typename C, typename Obj>
+  struct AcceptImpl<R C::*, Obj>
+      : std::integral_constant<bool, std::is_base_of<C, Obj>::value &&
+                                         !absl::is_function<R>::value> {};
+
+  template <typename DataMem, typename Ref>
+  static decltype(std::declval<Ref>().*std::declval<DataMem>()) Invoke(
+      DataMem&& data_mem, Ref&& ref) {
+    return std::forward<Ref>(ref).*std::forward<DataMem>(data_mem);
+  }
+};
+
+// (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1
+// is not one of the types described in the previous item.
+struct DataMemAndPtr : StrippedAccept<DataMemAndPtr> {
+  template <typename... Args>
+  struct AcceptImpl : std::false_type {};
+
+  template <typename R, typename C, typename Ptr>
+  struct AcceptImpl<R C::*, Ptr>
+      : std::integral_constant<bool, !std::is_base_of<C, Ptr>::value &&
+                                         !absl::is_function<R>::value> {};
+
+  template <typename DataMem, typename Ptr>
+  static decltype((*std::declval<Ptr>()).*std::declval<DataMem>()) Invoke(
+      DataMem&& data_mem, Ptr&& ptr) {
+    return (*std::forward<Ptr>(ptr)).*std::forward<DataMem>(data_mem);
+  }
+};
+
+// f(t1, t2, ..., tN) in all other cases.
+struct Callable {
+  // Callable doesn't have Accept because it's the last clause that gets picked
+  // when none of the previous clauses are applicable.
+  template <typename F, typename... Args>
+  static decltype(std::declval<F>()(std::declval<Args>()...)) Invoke(
+      F&& f, Args&&... args) {
+    return std::forward<F>(f)(std::forward<Args>(args)...);
+  }
+};
+
+// Resolves to the first matching clause.
+template <typename... Args>
+struct Invoker {
+  typedef typename std::conditional<
+      MemFunAndRef::Accept<Args...>::value, MemFunAndRef,
+      typename std::conditional<
+          MemFunAndPtr::Accept<Args...>::value, MemFunAndPtr,
+          typename std::conditional<
+              DataMemAndRef::Accept<Args...>::value, DataMemAndRef,
+              typename std::conditional<DataMemAndPtr::Accept<Args...>::value,
+                                        DataMemAndPtr, Callable>::type>::type>::
+          type>::type type;
+};
+
+// The result type of Invoke<F, Args...>.
+template <typename F, typename... Args>
+using invoke_result_t = decltype(Invoker<F, Args...>::type::Invoke(
+    std::declval<F>(), std::declval<Args>()...));
+
+// Invoke(f, args...) is an implementation of INVOKE(f, args...) from section
+// [func.require] of the C++ standard.
+template <typename F, typename... Args>
+invoke_result_t<F, Args...> invoke(F&& f, Args&&... args) {
+  return Invoker<F, Args...>::type::Invoke(std::forward<F>(f),
+                                           std::forward<Args>(args)...);
+}
+
+template <typename AlwaysVoid, typename, typename, typename...>
+struct IsInvocableRImpl : std::false_type {};
+
+template <typename R, typename F, typename... Args>
+struct IsInvocableRImpl<
+    absl::void_t<absl::base_internal::invoke_result_t<F, Args...> >, R, F,
+    Args...>
+    : std::integral_constant<
+          bool,
+          std::is_convertible<absl::base_internal::invoke_result_t<F, Args...>,
+                              R>::value ||
+              std::is_void<R>::value> {};
+
+// Type trait whose member `value` is true if invoking `F` with `Args` is valid,
+// and either the return type is convertible to `R`, or `R` is void.
+// C++11-compatible version of `std::is_invocable_r`.
+template <typename R, typename F, typename... Args>
+using is_invocable_r = IsInvocableRImpl<void, R, F, Args...>;
+
+}  // namespace base_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
+
+#endif  // ABSL_BASE_INTERNAL_INVOKE_H_
diff --git a/third_party/abseil-cpp/absl/base/options.h b/third_party/abseil-cpp/absl/base/options.h
new file mode 100644
index 0000000..d5300c5
--- /dev/null
+++ b/third_party/abseil-cpp/absl/base/options.h
@@ -0,0 +1,232 @@
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: options.h
+// -----------------------------------------------------------------------------
+//
+// This file contains Abseil configuration options for setting specific
+// implementations instead of letting Abseil determine which implementation to
+// use at compile-time. Setting these options may be useful for package or build
+// managers who wish to guarantee ABI stability within binary builds (which are
+// otherwise difficult to enforce).
+//
+// *** IMPORTANT NOTICE FOR PACKAGE MANAGERS:  It is important that
+// maintainers of package managers who wish to package Abseil read and
+// understand this file! ***
+//
+// Abseil contains a number of possible configuration endpoints, based on
+// parameters such as the detected platform, language version, or command-line
+// flags used to invoke the underlying binary. As is the case with all
+// libraries, binaries which contain Abseil code must ensure that separate
+// packages use the same compiled copy of Abseil to avoid a diamond dependency
+// problem, which can occur if two packages built with different Abseil
+// configuration settings are linked together. Diamond dependency problems in
+// C++ may manifest as violations to the One Definition Rule (ODR) (resulting in
+// linker errors), or undefined behavior (resulting in crashes).
+//
+// Diamond dependency problems can be avoided if all packages utilize the same
+// exact version of Abseil. Building from source code with the same compilation
+// parameters is the easiest way to avoid such dependency problems. However, for
+// package managers who cannot control such compilation parameters, we are
+// providing the file to allow you to inject ABI (Application Binary Interface)
+// stability across builds. Settings options in this file will neither change
+// API nor ABI, providing a stable copy of Abseil between packages.
+//
+// Care must be taken to keep options within these configurations isolated
+// from any other dynamic settings, such as command-line flags which could alter
+// these options. This file is provided specifically to help build and package
+// managers provide a stable copy of Abseil within their libraries and binaries;
+// other developers should not have need to alter the contents of this file.
+//
+// -----------------------------------------------------------------------------
+// Usage
+// -----------------------------------------------------------------------------
+//
+// For any particular package release, set the appropriate definitions within
+// this file to whatever value makes the most sense for your package(s). Note
+// that, by default, most of these options, at the moment, affect the
+// implementation of types; future options may affect other implementation
+// details.
+//
+// NOTE: the defaults within this file all assume that Abseil can select the
+// proper Abseil implementation at compile-time, which will not be sufficient
+// to guarantee ABI stability to package managers.
+
+#ifndef ABSL_BASE_OPTIONS_H_
+#define ABSL_BASE_OPTIONS_H_
+
+// -----------------------------------------------------------------------------
+// Type Compatibility Options
+// -----------------------------------------------------------------------------
+//
+// ABSL_OPTION_USE_STD_ANY
+//
+// This option controls whether absl::any is implemented as an alias to
+// std::any, or as an independent implementation.
+//
+// A value of 0 means to use Abseil's implementation.  This requires only C++11
+// support, and is expected to work on every toolchain we support.
+//
+// A value of 1 means to use an alias to std::any.  This requires that all code
+// using Abseil is built in C++17 mode or later.
+//
+// A value of 2 means to detect the C++ version being used to compile Abseil,
+// and use an alias only if a working std::any is available.  This option is
+// useful when you are building your entire program, including all of its
+// dependencies, from source.  It should not be used otherwise -- for example,
+// if you are distributing Abseil in a binary package manager -- since in
+// mode 2, absl::any will name a different type, with a different mangled name
+// and binary layout, depending on the compiler flags passed by the end user.
+// For more info, see https://abseil.io/about/design/dropin-types.
+//
+// User code should not inspect this macro.  To check in the preprocessor if
+// absl::any is a typedef of std::any, use the feature macro ABSL_USES_STD_ANY.
+
+#define ABSL_OPTION_USE_STD_ANY 1
+
+
+// ABSL_OPTION_USE_STD_OPTIONAL
+//
+// This option controls whether absl::optional is implemented as an alias to
+// std::optional, or as an independent implementation.
+//
+// A value of 0 means to use Abseil's implementation.  This requires only C++11
+// support, and is expected to work on every toolchain we support.
+//
+// A value of 1 means to use an alias to std::optional.  This requires that all
+// code using Abseil is built in C++17 mode or later.
+//
+// A value of 2 means to detect the C++ version being used to compile Abseil,
+// and use an alias only if a working std::optional is available.  This option
+// is useful when you are building your program from source.  It should not be
+// used otherwise -- for example, if you are distributing Abseil in a binary
+// package manager -- since in mode 2, absl::optional will name a different
+// type, with a different mangled name and binary layout, depending on the
+// compiler flags passed by the end user.  For more info, see
+// https://abseil.io/about/design/dropin-types.
+
+// User code should not inspect this macro.  To check in the preprocessor if
+// absl::optional is a typedef of std::optional, use the feature macro
+// ABSL_USES_STD_OPTIONAL.
+
+#define ABSL_OPTION_USE_STD_OPTIONAL 1
+
+
+// ABSL_OPTION_USE_STD_STRING_VIEW
+//
+// This option controls whether absl::string_view is implemented as an alias to
+// std::string_view, or as an independent implementation.
+//
+// A value of 0 means to use Abseil's implementation.  This requires only C++11
+// support, and is expected to work on every toolchain we support.
+//
+// A value of 1 means to use an alias to std::string_view.  This requires that
+// all code using Abseil is built in C++17 mode or later.
+//
+// A value of 2 means to detect the C++ version being used to compile Abseil,
+// and use an alias only if a working std::string_view is available.  This
+// option is useful when you are building your program from source.  It should
+// not be used otherwise -- for example, if you are distributing Abseil in a
+// binary package manager -- since in mode 2, absl::string_view will name a
+// different type, with a different mangled name and binary layout, depending on
+// the compiler flags passed by the end user.  For more info, see
+// https://abseil.io/about/design/dropin-types.
+//
+// User code should not inspect this macro.  To check in the preprocessor if
+// absl::string_view is a typedef of std::string_view, use the feature macro
+// ABSL_USES_STD_STRING_VIEW.
+
+#define ABSL_OPTION_USE_STD_STRING_VIEW 1
+
+// ABSL_OPTION_USE_STD_VARIANT
+//
+// This option controls whether absl::variant is implemented as an alias to
+// std::variant, or as an independent implementation.
+//
+// A value of 0 means to use Abseil's implementation.  This requires only C++11
+// support, and is expected to work on every toolchain we support.
+//
+// A value of 1 means to use an alias to std::variant.  This requires that all
+// code using Abseil is built in C++17 mode or later.
+//
+// A value of 2 means to detect the C++ version being used to compile Abseil,
+// and use an alias only if a working std::variant is available.  This option
+// is useful when you are building your program from source.  It should not be
+// used otherwise -- for example, if you are distributing Abseil in a binary
+// package manager -- since in mode 2, absl::variant will name a different
+// type, with a different mangled name and binary layout, depending on the
+// compiler flags passed by the end user.  For more info, see
+// https://abseil.io/about/design/dropin-types.
+//
+// User code should not inspect this macro.  To check in the preprocessor if
+// absl::variant is a typedef of std::variant, use the feature macro
+// ABSL_USES_STD_VARIANT.
+
+#define ABSL_OPTION_USE_STD_VARIANT 1
+
+
+// ABSL_OPTION_USE_INLINE_NAMESPACE
+// ABSL_OPTION_INLINE_NAMESPACE_NAME
+//
+// These options controls whether all entities in the absl namespace are
+// contained within an inner inline namespace.  This does not affect the
+// user-visible API of Abseil, but it changes the mangled names of all symbols.
+//
+// This can be useful as a version tag if you are distributing Abseil in
+// precompiled form.  This will prevent a binary library build of Abseil with
+// one inline namespace being used with headers configured with a different
+// inline namespace name.  Binary packagers are reminded that Abseil does not
+// guarantee any ABI stability in Abseil, so any update of Abseil or
+// configuration change in such a binary package should be combined with a
+// new, unique value for the inline namespace name.
+//
+// A value of 0 means not to use inline namespaces.
+//
+// A value of 1 means to use an inline namespace with the given name inside
+// namespace absl.  If this is set, ABSL_OPTION_INLINE_NAMESPACE_NAME must also
+// be changed to a new, unique identifier name.  In particular "head" is not
+// allowed.
+
+#define ABSL_OPTION_USE_INLINE_NAMESPACE 1
+#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20230125
+
+// ABSL_OPTION_HARDENED
+//
+// This option enables a "hardened" build in release mode (in this context,
+// release mode is defined as a build where the `NDEBUG` macro is defined).
+//
+// A value of 0 means that "hardened" mode is not enabled.
+//
+// A value of 1 means that "hardened" mode is enabled.
+//
+// Hardened builds have additional security checks enabled when `NDEBUG` is
+// defined. Defining `NDEBUG` is normally used to turn `assert()` macro into a
+// no-op, as well as disabling other bespoke program consistency checks. By
+// defining ABSL_OPTION_HARDENED to 1, a select set of checks remain enabled in
+// release mode. These checks guard against programming errors that may lead to
+// security vulnerabilities. In release mode, when one of these programming
+// errors is encountered, the program will immediately abort, possibly without
+// any attempt at logging.
+//
+// The checks enabled by this option are not free; they do incur runtime cost.
+//
+// The checks enabled by this option are always active when `NDEBUG` is not
+// defined, even in the case when ABSL_OPTION_HARDENED is defined to 0. The
+// checks enabled by this option may abort the program in a different way and
+// log additional information when `NDEBUG` is not defined.
+
+#define ABSL_OPTION_HARDENED 0
+
+#endif  // ABSL_BASE_OPTIONS_H_
diff --git a/third_party/abseil-cpp/absl/base/policy_checks.h b/third_party/abseil-cpp/absl/base/policy_checks.h
new file mode 100644
index 0000000..b8cd4c9
--- /dev/null
+++ b/third_party/abseil-cpp/absl/base/policy_checks.h
@@ -0,0 +1,113 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: policy_checks.h
+// -----------------------------------------------------------------------------
+//
+// This header enforces a minimum set of policies at build time, such as the
+// supported compiler and library versions. Unsupported configurations are
+// reported with `#error`. This enforcement is best effort, so successfully
+// compiling this header does not guarantee a supported configuration.
+
+#ifndef ABSL_BASE_POLICY_CHECKS_H_
+#define ABSL_BASE_POLICY_CHECKS_H_
+
+// Included for the __GLIBC_PREREQ macro used below.
+#include <limits.h>
+
+// Included for the _STLPORT_VERSION macro used below.
+#if defined(__cplusplus)
+#include <cstddef>
+#endif
+
+// -----------------------------------------------------------------------------
+// Operating System Check
+// -----------------------------------------------------------------------------
+
+#if defined(__CYGWIN__)
+#error "Cygwin is not supported."
+#endif
+
+// -----------------------------------------------------------------------------
+// Toolchain Check
+// -----------------------------------------------------------------------------
+
+// We support Visual Studio 2017 (MSVC++ 15.0) and later.
+// This minimum will go up.
+#if defined(_MSC_VER) && _MSC_VER < 1910 && !defined(__clang__)
+#error "This package requires Visual Studio 2017 (MSVC++ 15.0) or higher."
+#endif
+
+// We support GCC 7 and later.
+// This minimum will go up.
+#if defined(__GNUC__) && !defined(__clang__)
+#if __GNUC__ < 7
+#error "This package requires GCC 7 or higher."
+#endif
+#endif
+
+// We support Apple Xcode clang 4.2.1 (version 421.11.65) and later.
+// This corresponds to Apple Xcode version 4.5.
+// This minimum will go up.
+#if defined(__apple_build_version__) && __apple_build_version__ < 4211165
+#error "This package requires __apple_build_version__ of 4211165 or higher."
+#endif
+
+// -----------------------------------------------------------------------------
+// C++ Version Check
+// -----------------------------------------------------------------------------
+
+// Enforce C++14 as the minimum.
+#if defined(_MSVC_LANG)
+#if _MSVC_LANG < 201402L
+#error "C++ versions less than C++14 are not supported."
+#endif  // _MSVC_LANG < 201402L
+#elif defined(__cplusplus)
+#if __cplusplus < 201402L
+#error "C++ versions less than C++14 are not supported."
+#endif  // __cplusplus < 201402L
+#endif
+
+// -----------------------------------------------------------------------------
+// Standard Library Check
+// -----------------------------------------------------------------------------
+
+#if defined(_STLPORT_VERSION)
+#error "STLPort is not supported."
+#endif
+
+// -----------------------------------------------------------------------------
+// `char` Size Check
+// -----------------------------------------------------------------------------
+
+// Abseil currently assumes CHAR_BIT == 8. If you would like to use Abseil on a
+// platform where this is not the case, please provide us with the details about
+// your platform so we can consider relaxing this requirement.
+#if CHAR_BIT != 8
+#error "Abseil assumes CHAR_BIT == 8."
+#endif
+
+// -----------------------------------------------------------------------------
+// `int` Size Check
+// -----------------------------------------------------------------------------
+
+// Abseil currently assumes that an int is 4 bytes. If you would like to use
+// Abseil on a platform where this is not the case, please provide us with the
+// details about your platform so we can consider relaxing this requirement.
+#if INT_MAX < 2147483647
+#error "Abseil assumes that int is at least 4 bytes. "
+#endif
+
+#endif  // ABSL_BASE_POLICY_CHECKS_H_
diff --git a/third_party/abseil-cpp/absl/meta/type_traits.h b/third_party/abseil-cpp/absl/meta/type_traits.h
new file mode 100644
index 0000000..cda6e0b
--- /dev/null
+++ b/third_party/abseil-cpp/absl/meta/type_traits.h
@@ -0,0 +1,889 @@
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// type_traits.h
+// -----------------------------------------------------------------------------
+//
+// This file contains C++11-compatible versions of standard <type_traits> API
+// functions for determining the characteristics of types. Such traits can
+// support type inference, classification, and transformation, as well as
+// make it easier to write templates based on generic type behavior.
+//
+// See https://en.cppreference.com/w/cpp/header/type_traits
+//
+// WARNING: use of many of the constructs in this header will count as "complex
+// template metaprogramming", so before proceeding, please carefully consider
+// https://google.github.io/styleguide/cppguide.html#Template_metaprogramming
+//
+// WARNING: using template metaprogramming to detect or depend on API
+// features is brittle and not guaranteed. Neither the standard library nor
+// Abseil provides any guarantee that APIs are stable in the face of template
+// metaprogramming. Use with caution.
+#ifndef ABSL_META_TYPE_TRAITS_H_
+#define ABSL_META_TYPE_TRAITS_H_
+
+#include <cstddef>
+#include <functional>
+#include <type_traits>
+
+#include "third_party/abseil-cpp/absl/base/config.h"
+
+// MSVC constructibility traits do not detect destructor properties and so our
+// implementations should not use them as a source-of-truth.
+#if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__)
+#define ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION 1
+#endif
+
+// Defines the default alignment. `__STDCPP_DEFAULT_NEW_ALIGNMENT__` is a C++17
+// feature.
+#if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
+#define ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT __STDCPP_DEFAULT_NEW_ALIGNMENT__
+#else  // defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
+#define ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT alignof(std::max_align_t)
+#endif  // defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// Defined and documented later on in this file.
+template <typename T>
+struct is_trivially_destructible;
+
+// Defined and documented later on in this file.
+template <typename T>
+struct is_trivially_move_assignable;
+
+namespace type_traits_internal {
+
+// Silence MSVC warnings about the destructor being defined as deleted.
+#if defined(_MSC_VER) && !defined(__GNUC__)
+#pragma warning(push)
+#pragma warning(disable : 4624)
+#endif  // defined(_MSC_VER) && !defined(__GNUC__)
+
+template <class T>
+union SingleMemberUnion {
+  T t;
+};
+
+// Restore the state of the destructor warning that was silenced above.
+#if defined(_MSC_VER) && !defined(__GNUC__)
+#pragma warning(pop)
+#endif  // defined(_MSC_VER) && !defined(__GNUC__)
+
+template <class T>
+struct IsTriviallyMoveConstructibleObject
+    : std::integral_constant<
+          bool, std::is_move_constructible<
+                    type_traits_internal::SingleMemberUnion<T>>::value &&
+                    absl::is_trivially_destructible<T>::value> {};
+
+template <class T>
+struct IsTriviallyCopyConstructibleObject
+    : std::integral_constant<
+          bool, std::is_copy_constructible<
+                    type_traits_internal::SingleMemberUnion<T>>::value &&
+                    absl::is_trivially_destructible<T>::value> {};
+
+template <class T>
+struct IsTriviallyMoveAssignableReference : std::false_type {};
+
+template <class T>
+struct IsTriviallyMoveAssignableReference<T&>
+    : absl::is_trivially_move_assignable<T>::type {};
+
+template <class T>
+struct IsTriviallyMoveAssignableReference<T&&>
+    : absl::is_trivially_move_assignable<T>::type {};
+
+template <typename... Ts>
+struct VoidTImpl {
+  using type = void;
+};
+
+////////////////////////////////
+// Library Fundamentals V2 TS //
+////////////////////////////////
+
+// NOTE: The `is_detected` family of templates here differ from the library
+// fundamentals specification in that for library fundamentals, `Op<Args...>` is
+// evaluated as soon as the type `is_detected<Op, Args...>` undergoes
+// substitution, regardless of whether or not the `::value` is accessed. That
+// is inconsistent with all other standard traits and prevents lazy evaluation
+// in larger contexts (such as if the `is_detected` check is a trailing argument
+// of a `conjunction`. This implementation opts to instead be lazy in the same
+// way that the standard traits are (this "defect" of the detection idiom
+// specifications has been reported).
+
+template <class Enabler, template <class...> class Op, class... Args>
+struct is_detected_impl {
+  using type = std::false_type;
+};
+
+template <template <class...> class Op, class... Args>
+struct is_detected_impl<typename VoidTImpl<Op<Args...>>::type, Op, Args...> {
+  using type = std::true_type;
+};
+
+template <template <class...> class Op, class... Args>
+struct is_detected : is_detected_impl<void, Op, Args...>::type {};
+
+template <class Enabler, class To, template <class...> class Op, class... Args>
+struct is_detected_convertible_impl {
+  using type = std::false_type;
+};
+
+template <class To, template <class...> class Op, class... Args>
+struct is_detected_convertible_impl<
+    typename std::enable_if<std::is_convertible<Op<Args...>, To>::value>::type,
+    To, Op, Args...> {
+  using type = std::true_type;
+};
+
+template <class To, template <class...> class Op, class... Args>
+struct is_detected_convertible
+    : is_detected_convertible_impl<void, To, Op, Args...>::type {};
+
+template <typename T>
+using IsCopyAssignableImpl =
+    decltype(std::declval<T&>() = std::declval<const T&>());
+
+template <typename T>
+using IsMoveAssignableImpl = decltype(std::declval<T&>() = std::declval<T&&>());
+
+}  // namespace type_traits_internal
+
+// MSVC 19.20 has a regression that causes our workarounds to fail, but their
+// std forms now appear to be compliant.
+#if defined(_MSC_VER) && !defined(__clang__) && (_MSC_VER >= 1920)
+
+template <typename T>
+using is_copy_assignable = std::is_copy_assignable<T>;
+
+template <typename T>
+using is_move_assignable = std::is_move_assignable<T>;
+
+#else
+
+template <typename T>
+struct is_copy_assignable : type_traits_internal::is_detected<
+                                type_traits_internal::IsCopyAssignableImpl, T> {
+};
+
+template <typename T>
+struct is_move_assignable : type_traits_internal::is_detected<
+                                type_traits_internal::IsMoveAssignableImpl, T> {
+};
+
+#endif
+
+// void_t()
+//
+// Ignores the type of any its arguments and returns `void`. In general, this
+// metafunction allows you to create a general case that maps to `void` while
+// allowing specializations that map to specific types.
+//
+// This metafunction is designed to be a drop-in replacement for the C++17
+// `std::void_t` metafunction.
+//
+// NOTE: `absl::void_t` does not use the standard-specified implementation so
+// that it can remain compatible with gcc < 5.1. This can introduce slightly
+// different behavior, such as when ordering partial specializations.
+template <typename... Ts>
+using void_t = typename type_traits_internal::VoidTImpl<Ts...>::type;
+
+// conjunction
+//
+// Performs a compile-time logical AND operation on the passed types (which
+// must have  `::value` members convertible to `bool`. Short-circuits if it
+// encounters any `false` members (and does not compare the `::value` members
+// of any remaining arguments).
+//
+// This metafunction is designed to be a drop-in replacement for the C++17
+// `std::conjunction` metafunction.
+template <typename... Ts>
+struct conjunction : std::true_type {};
+
+template <typename T, typename... Ts>
+struct conjunction<T, Ts...>
+    : std::conditional<T::value, conjunction<Ts...>, T>::type {};
+
+template <typename T>
+struct conjunction<T> : T {};
+
+// disjunction
+//
+// Performs a compile-time logical OR operation on the passed types (which
+// must have  `::value` members convertible to `bool`. Short-circuits if it
+// encounters any `true` members (and does not compare the `::value` members
+// of any remaining arguments).
+//
+// This metafunction is designed to be a drop-in replacement for the C++17
+// `std::disjunction` metafunction.
+template <typename... Ts>
+struct disjunction : std::false_type {};
+
+template <typename T, typename... Ts>
+struct disjunction<T, Ts...> :
+      std::conditional<T::value, T, disjunction<Ts...>>::type {};
+
+template <typename T>
+struct disjunction<T> : T {};
+
+// negation
+//
+// Performs a compile-time logical NOT operation on the passed type (which
+// must have  `::value` members convertible to `bool`.
+//
+// This metafunction is designed to be a drop-in replacement for the C++17
+// `std::negation` metafunction.
+template <typename T>
+struct negation : std::integral_constant<bool, !T::value> {};
+
+// is_function()
+//
+// Determines whether the passed type `T` is a function type.
+//
+// This metafunction is designed to be a drop-in replacement for the C++11
+// `std::is_function()` metafunction for platforms that have incomplete C++11
+// support (such as libstdc++ 4.x).
+//
+// This metafunction works because appending `const` to a type does nothing to
+// function types and reference types (and forms a const-qualified type
+// otherwise).
+template <typename T>
+struct is_function
+    : std::integral_constant<
+          bool, !(std::is_reference<T>::value ||
+                  std::is_const<typename std::add_const<T>::type>::value)> {};
+
+// is_trivially_destructible()
+//
+// Determines whether the passed type `T` is trivially destructible.
+//
+// This metafunction is designed to be a drop-in replacement for the C++11
+// `std::is_trivially_destructible()` metafunction for platforms that have
+// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
+// fully support C++11, we check whether this yields the same result as the std
+// implementation.
+//
+// NOTE: the extensions (__has_trivial_xxx) are implemented in gcc (version >=
+// 4.3) and clang. Since we are supporting libstdc++ > 4.7, they should always
+// be present. These  extensions are documented at
+// https://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html#Type-Traits.
+template <typename T>
+struct is_trivially_destructible
+#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
+    : std::is_trivially_destructible<T> {
+#else
+    : std::integral_constant<bool, __has_trivial_destructor(T) &&
+                                   std::is_destructible<T>::value> {
+#endif
+#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
+ private:
+  static constexpr bool compliant = std::is_trivially_destructible<T>::value ==
+                                    is_trivially_destructible::value;
+  static_assert(compliant || std::is_trivially_destructible<T>::value,
+                "Not compliant with std::is_trivially_destructible; "
+                "Standard: false, Implementation: true");
+  static_assert(compliant || !std::is_trivially_destructible<T>::value,
+                "Not compliant with std::is_trivially_destructible; "
+                "Standard: true, Implementation: false");
+#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
+};
+
+// is_trivially_default_constructible()
+//
+// Determines whether the passed type `T` is trivially default constructible.
+//
+// This metafunction is designed to be a drop-in replacement for the C++11
+// `std::is_trivially_default_constructible()` metafunction for platforms that
+// have incomplete C++11 support (such as libstdc++ 4.x). On any platforms that
+// do fully support C++11, we check whether this yields the same result as the
+// std implementation.
+//
+// NOTE: according to the C++ standard, Section: 20.15.4.3 [meta.unary.prop]
+// "The predicate condition for a template specialization is_constructible<T,
+// Args...> shall be satisfied if and only if the following variable
+// definition would be well-formed for some invented variable t:
+//
+// T t(declval<Args>()...);
+//
+// is_trivially_constructible<T, Args...> additionally requires that the
+// variable definition does not call any operation that is not trivial.
+// For the purposes of this check, the call to std::declval is considered
+// trivial."
+//
+// Notes from https://en.cppreference.com/w/cpp/types/is_constructible:
+// In many implementations, is_nothrow_constructible also checks if the
+// destructor throws because it is effectively noexcept(T(arg)). Same
+// applies to is_trivially_constructible, which, in these implementations, also
+// requires that the destructor is trivial.
+// GCC bug 51452: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51452
+// LWG issue 2116: http://cplusplus.github.io/LWG/lwg-active.html#2116.
+//
+// "T obj();" need to be well-formed and not call any nontrivial operation.
+// Nontrivially destructible types will cause the expression to be nontrivial.
+template <typename T>
+struct is_trivially_default_constructible
+#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE)
+    : std::is_trivially_default_constructible<T> {
+#else
+    : std::integral_constant<bool, __has_trivial_constructor(T) &&
+                                   std::is_default_constructible<T>::value &&
+                                   is_trivially_destructible<T>::value> {
+#endif
+#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \
+    !defined(                                            \
+        ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION)
+ private:
+  static constexpr bool compliant =
+      std::is_trivially_default_constructible<T>::value ==
+      is_trivially_default_constructible::value;
+  static_assert(compliant || std::is_trivially_default_constructible<T>::value,
+                "Not compliant with std::is_trivially_default_constructible; "
+                "Standard: false, Implementation: true");
+  static_assert(compliant || !std::is_trivially_default_constructible<T>::value,
+                "Not compliant with std::is_trivially_default_constructible; "
+                "Standard: true, Implementation: false");
+#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
+};
+
+// is_trivially_move_constructible()
+//
+// Determines whether the passed type `T` is trivially move constructible.
+//
+// This metafunction is designed to be a drop-in replacement for the C++11
+// `std::is_trivially_move_constructible()` metafunction for platforms that have
+// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
+// fully support C++11, we check whether this yields the same result as the std
+// implementation.
+//
+// NOTE: `T obj(declval<T>());` needs to be well-formed and not call any
+// nontrivial operation.  Nontrivially destructible types will cause the
+// expression to be nontrivial.
+template <typename T>
+struct is_trivially_move_constructible
+#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE)
+    : std::is_trivially_move_constructible<T> {
+#else
+    : std::conditional<
+          std::is_object<T>::value && !std::is_array<T>::value,
+          type_traits_internal::IsTriviallyMoveConstructibleObject<T>,
+          std::is_reference<T>>::type::type {
+#endif
+#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \
+    !defined(                                            \
+        ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION)
+ private:
+  static constexpr bool compliant =
+      std::is_trivially_move_constructible<T>::value ==
+      is_trivially_move_constructible::value;
+  static_assert(compliant || std::is_trivially_move_constructible<T>::value,
+                "Not compliant with std::is_trivially_move_constructible; "
+                "Standard: false, Implementation: true");
+  static_assert(compliant || !std::is_trivially_move_constructible<T>::value,
+                "Not compliant with std::is_trivially_move_constructible; "
+                "Standard: true, Implementation: false");
+#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
+};
+
+// is_trivially_copy_constructible()
+//
+// Determines whether the passed type `T` is trivially copy constructible.
+//
+// This metafunction is designed to be a drop-in replacement for the C++11
+// `std::is_trivially_copy_constructible()` metafunction for platforms that have
+// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
+// fully support C++11, we check whether this yields the same result as the std
+// implementation.
+//
+// NOTE: `T obj(declval<const T&>());` needs to be well-formed and not call any
+// nontrivial operation.  Nontrivially destructible types will cause the
+// expression to be nontrivial.
+template <typename T>
+struct is_trivially_copy_constructible
+    : std::conditional<
+          std::is_object<T>::value && !std::is_array<T>::value,
+          type_traits_internal::IsTriviallyCopyConstructibleObject<T>,
+          std::is_lvalue_reference<T>>::type::type {
+#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \
+    !defined(                                            \
+        ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION)
+ private:
+  static constexpr bool compliant =
+      std::is_trivially_copy_constructible<T>::value ==
+      is_trivially_copy_constructible::value;
+  static_assert(compliant || std::is_trivially_copy_constructible<T>::value,
+                "Not compliant with std::is_trivially_copy_constructible; "
+                "Standard: false, Implementation: true");
+  static_assert(compliant || !std::is_trivially_copy_constructible<T>::value,
+                "Not compliant with std::is_trivially_copy_constructible; "
+                "Standard: true, Implementation: false");
+#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
+};
+
+// is_trivially_move_assignable()
+//
+// Determines whether the passed type `T` is trivially move assignable.
+//
+// This metafunction is designed to be a drop-in replacement for the C++11
+// `std::is_trivially_move_assignable()` metafunction for platforms that have
+// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
+// fully support C++11, we check whether this yields the same result as the std
+// implementation.
+//
+// NOTE: `is_assignable<T, U>::value` is `true` if the expression
+// `declval<T>() = declval<U>()` is well-formed when treated as an unevaluated
+// operand. `is_trivially_assignable<T, U>` requires the assignment to call no
+// operation that is not trivial. `is_trivially_copy_assignable<T>` is simply
+// `is_trivially_assignable<T&, T>`.
+template <typename T>
+struct is_trivially_move_assignable
+    : std::conditional<
+          std::is_object<T>::value && !std::is_array<T>::value &&
+              std::is_move_assignable<T>::value,
+          std::is_move_assignable<type_traits_internal::SingleMemberUnion<T>>,
+          type_traits_internal::IsTriviallyMoveAssignableReference<T>>::type::
+          type {
+#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
+ private:
+  static constexpr bool compliant =
+      std::is_trivially_move_assignable<T>::value ==
+      is_trivially_move_assignable::value;
+  static_assert(compliant || std::is_trivially_move_assignable<T>::value,
+                "Not compliant with std::is_trivially_move_assignable; "
+                "Standard: false, Implementation: true");
+  static_assert(compliant || !std::is_trivially_move_assignable<T>::value,
+                "Not compliant with std::is_trivially_move_assignable; "
+                "Standard: true, Implementation: false");
+#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
+};
+
+// is_trivially_copy_assignable()
+//
+// Determines whether the passed type `T` is trivially copy assignable.
+//
+// This metafunction is designed to be a drop-in replacement for the C++11
+// `std::is_trivially_copy_assignable()` metafunction for platforms that have
+// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
+// fully support C++11, we check whether this yields the same result as the std
+// implementation.
+//
+// NOTE: `is_assignable<T, U>::value` is `true` if the expression
+// `declval<T>() = declval<U>()` is well-formed when treated as an unevaluated
+// operand. `is_trivially_assignable<T, U>` requires the assignment to call no
+// operation that is not trivial. `is_trivially_copy_assignable<T>` is simply
+// `is_trivially_assignable<T&, const T&>`.
+template <typename T>
+struct is_trivially_copy_assignable
+#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
+    : std::is_trivially_copy_assignable<T> {
+#else
+    : std::integral_constant<
+          bool, __has_trivial_assign(typename std::remove_reference<T>::type) &&
+                    absl::is_copy_assignable<T>::value> {
+#endif
+#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
+ private:
+  static constexpr bool compliant =
+      std::is_trivially_copy_assignable<T>::value ==
+      is_trivially_copy_assignable::value;
+  static_assert(compliant || std::is_trivially_copy_assignable<T>::value,
+                "Not compliant with std::is_trivially_copy_assignable; "
+                "Standard: false, Implementation: true");
+  static_assert(compliant || !std::is_trivially_copy_assignable<T>::value,
+                "Not compliant with std::is_trivially_copy_assignable; "
+                "Standard: true, Implementation: false");
+#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
+};
+
+#if defined(__cpp_lib_remove_cvref) && __cpp_lib_remove_cvref >= 201711L
+template <typename T>
+using remove_cvref = std::remove_cvref<T>;
+
+template <typename T>
+using remove_cvref_t = typename std::remove_cvref<T>::type;
+#else
+// remove_cvref()
+//
+// C++11 compatible implementation of std::remove_cvref which was added in
+// C++20.
+template <typename T>
+struct remove_cvref {
+  using type =
+      typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+};
+
+template <typename T>
+using remove_cvref_t = typename remove_cvref<T>::type;
+#endif
+
+namespace type_traits_internal {
+// is_trivially_copyable()
+//
+// Determines whether the passed type `T` is trivially copyable.
+//
+// This metafunction is designed to be a drop-in replacement for the C++11
+// `std::is_trivially_copyable()` metafunction for platforms that have
+// incomplete C++11 support (such as libstdc++ 4.x). We use the C++17 definition
+// of TriviallyCopyable.
+//
+// NOTE: `is_trivially_copyable<T>::value` is `true` if all of T's copy/move
+// constructors/assignment operators are trivial or deleted, T has at least
+// one non-deleted copy/move constructor/assignment operator, and T is trivially
+// destructible. Arrays of trivially copyable types are trivially copyable.
+//
+// We expose this metafunction only for internal use within absl.
+
+#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE)
+template <typename T>
+struct is_trivially_copyable : std::is_trivially_copyable<T> {};
+#else
+template <typename T>
+class is_trivially_copyable_impl {
+  using ExtentsRemoved = typename std::remove_all_extents<T>::type;
+  static constexpr bool kIsCopyOrMoveConstructible =
+      std::is_copy_constructible<ExtentsRemoved>::value ||
+      std::is_move_constructible<ExtentsRemoved>::value;
+  static constexpr bool kIsCopyOrMoveAssignable =
+      absl::is_copy_assignable<ExtentsRemoved>::value ||
+      absl::is_move_assignable<ExtentsRemoved>::value;
+
+ public:
+  static constexpr bool kValue =
+      (__has_trivial_copy(ExtentsRemoved) || !kIsCopyOrMoveConstructible) &&
+      (__has_trivial_assign(ExtentsRemoved) || !kIsCopyOrMoveAssignable) &&
+      (kIsCopyOrMoveConstructible || kIsCopyOrMoveAssignable) &&
+      is_trivially_destructible<ExtentsRemoved>::value &&
+      // We need to check for this explicitly because otherwise we'll say
+      // references are trivial copyable when compiled by MSVC.
+      !std::is_reference<ExtentsRemoved>::value;
+};
+
+template <typename T>
+struct is_trivially_copyable
+    : std::integral_constant<
+          bool, type_traits_internal::is_trivially_copyable_impl<T>::kValue> {};
+#endif
+}  // namespace type_traits_internal
+
+// -----------------------------------------------------------------------------
+// C++14 "_t" trait aliases
+// -----------------------------------------------------------------------------
+
+template <typename T>
+using remove_cv_t = typename std::remove_cv<T>::type;
+
+template <typename T>
+using remove_const_t = typename std::remove_const<T>::type;
+
+template <typename T>
+using remove_volatile_t = typename std::remove_volatile<T>::type;
+
+template <typename T>
+using add_cv_t = typename std::add_cv<T>::type;
+
+template <typename T>
+using add_const_t = typename std::add_const<T>::type;
+
+template <typename T>
+using add_volatile_t = typename std::add_volatile<T>::type;
+
+template <typename T>
+using remove_reference_t = typename std::remove_reference<T>::type;
+
+template <typename T>
+using add_lvalue_reference_t = typename std::add_lvalue_reference<T>::type;
+
+template <typename T>
+using add_rvalue_reference_t = typename std::add_rvalue_reference<T>::type;
+
+template <typename T>
+using remove_pointer_t = typename std::remove_pointer<T>::type;
+
+template <typename T>
+using add_pointer_t = typename std::add_pointer<T>::type;
+
+template <typename T>
+using make_signed_t = typename std::make_signed<T>::type;
+
+template <typename T>
+using make_unsigned_t = typename std::make_unsigned<T>::type;
+
+template <typename T>
+using remove_extent_t = typename std::remove_extent<T>::type;
+
+template <typename T>
+using remove_all_extents_t = typename std::remove_all_extents<T>::type;
+
+namespace type_traits_internal {
+// This trick to retrieve a default alignment is necessary for our
+// implementation of aligned_storage_t to be consistent with any
+// implementation of std::aligned_storage.
+template <size_t Len, typename T = std::aligned_storage<Len>>
+struct default_alignment_of_aligned_storage;
+
+template <size_t Len, size_t Align>
+struct default_alignment_of_aligned_storage<
+    Len, std::aligned_storage<Len, Align>> {
+  static constexpr size_t value = Align;
+};
+}  // namespace type_traits_internal
+
+// TODO(b/260219225): std::aligned_storage(_t) is deprecated in C++23.
+template <size_t Len, size_t Align = type_traits_internal::
+                          default_alignment_of_aligned_storage<Len>::value>
+using aligned_storage_t = typename std::aligned_storage<Len, Align>::type;
+
+template <typename T>
+using decay_t = typename std::decay<T>::type;
+
+template <bool B, typename T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+template <bool B, typename T, typename F>
+using conditional_t = typename std::conditional<B, T, F>::type;
+
+template <typename... T>
+using common_type_t = typename std::common_type<T...>::type;
+
+template <typename T>
+using underlying_type_t = typename std::underlying_type<T>::type;
+
+
+namespace type_traits_internal {
+
+#if (defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703L) || \
+    (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+// std::result_of is deprecated (C++17) or removed (C++20)
+template<typename> struct result_of;
+template<typename F, typename... Args>
+struct result_of<F(Args...)> : std::invoke_result<F, Args...> {};
+#else
+template<typename F> using result_of = std::result_of<F>;
+#endif
+
+}  // namespace type_traits_internal
+
+template<typename F>
+using result_of_t = typename type_traits_internal::result_of<F>::type;
+
+namespace type_traits_internal {
+// In MSVC we can't probe std::hash or stdext::hash because it triggers a
+// static_assert instead of failing substitution. Libc++ prior to 4.0
+// also used a static_assert.
+//
+#if defined(_MSC_VER) || (defined(_LIBCPP_VERSION) && \
+                          _LIBCPP_VERSION < 4000 && _LIBCPP_STD_VER > 11)
+#define ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ 0
+#else
+#define ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ 1
+#endif
+
+#if !ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
+template <typename Key, typename = size_t>
+struct IsHashable : std::true_type {};
+#else   // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
+template <typename Key, typename = void>
+struct IsHashable : std::false_type {};
+
+template <typename Key>
+struct IsHashable<
+    Key,
+    absl::enable_if_t<std::is_convertible<
+        decltype(std::declval<std::hash<Key>&>()(std::declval<Key const&>())),
+        std::size_t>::value>> : std::true_type {};
+#endif  // !ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
+
+struct AssertHashEnabledHelper {
+ private:
+  static void Sink(...) {}
+  struct NAT {};
+
+  template <class Key>
+  static auto GetReturnType(int)
+      -> decltype(std::declval<std::hash<Key>>()(std::declval<Key const&>()));
+  template <class Key>
+  static NAT GetReturnType(...);
+
+  template <class Key>
+  static std::nullptr_t DoIt() {
+    static_assert(IsHashable<Key>::value,
+                  "std::hash<Key> does not provide a call operator");
+    static_assert(
+        std::is_default_constructible<std::hash<Key>>::value,
+        "std::hash<Key> must be default constructible when it is enabled");
+    static_assert(
+        std::is_copy_constructible<std::hash<Key>>::value,
+        "std::hash<Key> must be copy constructible when it is enabled");
+    static_assert(absl::is_copy_assignable<std::hash<Key>>::value,
+                  "std::hash<Key> must be copy assignable when it is enabled");
+    // is_destructible is unchecked as it's implied by each of the
+    // is_constructible checks.
+    using ReturnType = decltype(GetReturnType<Key>(0));
+    static_assert(std::is_same<ReturnType, NAT>::value ||
+                      std::is_same<ReturnType, size_t>::value,
+                  "std::hash<Key> must return size_t");
+    return nullptr;
+  }
+
+  template <class... Ts>
+  friend void AssertHashEnabled();
+};
+
+template <class... Ts>
+inline void AssertHashEnabled() {
+  using Helper = AssertHashEnabledHelper;
+  Helper::Sink(Helper::DoIt<Ts>()...);
+}
+
+}  // namespace type_traits_internal
+
+// An internal namespace that is required to implement the C++17 swap traits.
+// It is not further nested in type_traits_internal to avoid long symbol names.
+namespace swap_internal {
+
+// Necessary for the traits.
+using std::swap;
+
+// This declaration prevents global `swap` and `absl::swap` overloads from being
+// considered unless ADL picks them up.
+void swap();
+
+template <class T>
+using IsSwappableImpl = decltype(swap(std::declval<T&>(), std::declval<T&>()));
+
+// NOTE: This dance with the default template parameter is for MSVC.
+template <class T,
+          class IsNoexcept = std::integral_constant<
+              bool, noexcept(swap(std::declval<T&>(), std::declval<T&>()))>>
+using IsNothrowSwappableImpl = typename std::enable_if<IsNoexcept::value>::type;
+
+// IsSwappable
+//
+// Determines whether the standard swap idiom is a valid expression for
+// arguments of type `T`.
+template <class T>
+struct IsSwappable
+    : absl::type_traits_internal::is_detected<IsSwappableImpl, T> {};
+
+// IsNothrowSwappable
+//
+// Determines whether the standard swap idiom is a valid expression for
+// arguments of type `T` and is noexcept.
+template <class T>
+struct IsNothrowSwappable
+    : absl::type_traits_internal::is_detected<IsNothrowSwappableImpl, T> {};
+
+// Swap()
+//
+// Performs the swap idiom from a namespace where valid candidates may only be
+// found in `std` or via ADL.
+template <class T, absl::enable_if_t<IsSwappable<T>::value, int> = 0>
+void Swap(T& lhs, T& rhs) noexcept(IsNothrowSwappable<T>::value) {
+  swap(lhs, rhs);
+}
+
+// StdSwapIsUnconstrained
+//
+// Some standard library implementations are broken in that they do not
+// constrain `std::swap`. This will effectively tell us if we are dealing with
+// one of those implementations.
+using StdSwapIsUnconstrained = IsSwappable<void()>;
+
+}  // namespace swap_internal
+
+namespace type_traits_internal {
+
+// Make the swap-related traits/function accessible from this namespace.
+using swap_internal::IsNothrowSwappable;
+using swap_internal::IsSwappable;
+using swap_internal::Swap;
+using swap_internal::StdSwapIsUnconstrained;
+
+}  // namespace type_traits_internal
+
+// absl::is_trivially_relocatable<T>
+// Detects whether a type is "trivially relocatable" -- meaning it can be
+// relocated without invoking the constructor/destructor, using a form of move
+// elision.
+//
+// Example:
+//
+// if constexpr (absl::is_trivially_relocatable<T>::value) {
+//   memcpy(new_location, old_location, sizeof(T));
+// } else {
+//   new(new_location) T(std::move(*old_location));
+//   old_location->~T();
+// }
+//
+// Upstream documentation:
+//
+// https://clang.llvm.org/docs/LanguageExtensions.html#:~:text=__is_trivially_relocatable
+//
+#if ABSL_HAVE_BUILTIN(__is_trivially_relocatable)
+template <class T>
+struct is_trivially_relocatable
+    : std::integral_constant<bool, __is_trivially_relocatable(T)> {};
+#else
+template <class T>
+struct is_trivially_relocatable : std::integral_constant<bool, false> {};
+#endif
+
+// absl::is_constant_evaluated()
+//
+// Detects whether the function call occurs within a constant-evaluated context.
+// Returns true if the evaluation of the call occurs within the evaluation of an
+// expression or conversion that is manifestly constant-evaluated; otherwise
+// returns false.
+//
+// This function is implemented in terms of `std::is_constant_evaluated` for
+// c++20 and up. For older c++ versions, the function is implemented in terms
+// of `__builtin_is_constant_evaluated` if available, otherwise the function
+// will fail to compile.
+//
+// Applications can inspect `ABSL_HAVE_CONSTANT_EVALUATED` at compile time
+// to check if this function is supported.
+//
+// Example:
+//
+// constexpr MyClass::MyClass(int param) {
+// #ifdef ABSL_HAVE_CONSTANT_EVALUATED
+//   if (!absl::is_constant_evaluated()) {
+//     ABSL_LOG(INFO) << "MyClass(" << param << ")";
+//   }
+// #endif  // ABSL_HAVE_CONSTANT_EVALUATED
+// }
+//
+// Upstream documentation:
+//
+// http://en.cppreference.com/w/cpp/types/is_constant_evaluated
+// http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#:~:text=__builtin_is_constant_evaluated
+//
+#if defined(ABSL_HAVE_CONSTANT_EVALUATED)
+constexpr bool is_constant_evaluated() noexcept {
+#ifdef __cpp_lib_is_constant_evaluated
+  return std::is_constant_evaluated();
+#elif ABSL_HAVE_BUILTIN(__builtin_is_constant_evaluated)
+  return __builtin_is_constant_evaluated();
+#endif
+}
+#endif  // ABSL_HAVE_CONSTANT_EVALUATED
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_META_TYPE_TRAITS_H_
diff --git a/third_party/abseil-cpp/absl/types/optional.h b/third_party/abseil-cpp/absl/types/optional.h
new file mode 100644
index 0000000..c519aea
--- /dev/null
+++ b/third_party/abseil-cpp/absl/types/optional.h
@@ -0,0 +1,779 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// optional.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines the `absl::optional` type for holding a value which
+// may or may not be present. This type is useful for providing value semantics
+// for operations that may either wish to return or hold "something-or-nothing".
+//
+// Example:
+//
+//   // A common way to signal operation failure is to provide an output
+//   // parameter and a bool return type:
+//   bool AcquireResource(const Input&, Resource * out);
+//
+//   // Providing an absl::optional return type provides a cleaner API:
+//   absl::optional<Resource> AcquireResource(const Input&);
+//
+// `absl::optional` is a C++11 compatible version of the C++17 `std::optional`
+// abstraction and is designed to be a drop-in replacement for code compliant
+// with C++17.
+#ifndef ABSL_TYPES_OPTIONAL_H_
+#define ABSL_TYPES_OPTIONAL_H_
+
+#include "third_party/abseil-cpp/absl/base/config.h"   // TODO(calabrese) IWYU removal?
+#include "third_party/abseil-cpp/absl/utility/utility.h"
+
+#ifdef ABSL_USES_STD_OPTIONAL
+
+#include <optional>  // IWYU pragma: export
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+using std::bad_optional_access;
+using std::optional;
+using std::make_optional;
+using std::nullopt_t;
+using std::nullopt;
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#else  // ABSL_USES_STD_OPTIONAL
+
+#include <cassert>
+#include <functional>
+#include <initializer_list>
+#include <type_traits>
+#include <utility>
+
+#include "third_party/absl/base/attributes.h"
+#include "third_party/absl/base/internal/inline_variable.h"
+#include "third_party/absl/meta/type_traits.h"
+#include "third_party/absl/types/bad_optional_access.h"
+#include "third_party/absl/types/internal/optional.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// nullopt_t
+//
+// Class type for `absl::nullopt` used to indicate an `absl::optional<T>` type
+// that does not contain a value.
+struct nullopt_t {
+  // It must not be default-constructible to avoid ambiguity for opt = {}.
+  explicit constexpr nullopt_t(optional_internal::init_t) noexcept {}
+};
+
+// nullopt
+//
+// A tag constant of type `absl::nullopt_t` used to indicate an empty
+// `absl::optional` in certain functions, such as construction or assignment.
+ABSL_INTERNAL_INLINE_CONSTEXPR(nullopt_t, nullopt,
+                               nullopt_t(optional_internal::init_t()));
+
+// -----------------------------------------------------------------------------
+// absl::optional
+// -----------------------------------------------------------------------------
+//
+// A value of type `absl::optional<T>` holds either a value of `T` or an
+// "empty" value.  When it holds a value of `T`, it stores it as a direct
+// sub-object, so `sizeof(optional<T>)` is approximately
+// `sizeof(T) + sizeof(bool)`.
+//
+// This implementation is based on the specification in the latest draft of the
+// C++17 `std::optional` specification as of May 2017, section 20.6.
+//
+// Differences between `absl::optional<T>` and `std::optional<T>` include:
+//
+//    * `constexpr` is not used for non-const member functions.
+//      (dependency on some differences between C++11 and C++14.)
+//    * `absl::nullopt` and `absl::in_place` are not declared `constexpr`. We
+//      need the inline variable support in C++17 for external linkage.
+//    * Throws `absl::bad_optional_access` instead of
+//      `std::bad_optional_access`.
+//    * `make_optional()` cannot be declared `constexpr` due to the absence of
+//      guaranteed copy elision.
+//    * The move constructor's `noexcept` specification is stronger, i.e. if the
+//      default allocator is non-throwing (via setting
+//      `ABSL_ALLOCATOR_NOTHROW`), it evaluates to `noexcept(true)`, because
+//      we assume
+//       a) move constructors should only throw due to allocation failure and
+//       b) if T's move constructor allocates, it uses the same allocation
+//          function as the default allocator.
+//
+template <typename T>
+class optional : private optional_internal::optional_data<T>,
+                 private optional_internal::optional_ctor_base<
+                     optional_internal::ctor_copy_traits<T>::traits>,
+                 private optional_internal::optional_assign_base<
+                     optional_internal::assign_copy_traits<T>::traits> {
+  using data_base = optional_internal::optional_data<T>;
+
+ public:
+  typedef T value_type;
+
+  // Constructors
+
+  // Constructs an `optional` holding an empty value, NOT a default constructed
+  // `T`.
+  constexpr optional() noexcept {}
+
+  // Constructs an `optional` initialized with `nullopt` to hold an empty value.
+  constexpr optional(nullopt_t) noexcept {}  // NOLINT(runtime/explicit)
+
+  // Copy constructor, standard semantics
+  optional(const optional&) = default;
+
+  // Move constructor, standard semantics
+  optional(optional&&) = default;
+
+  // Constructs a non-empty `optional` direct-initialized value of type `T` from
+  // the arguments `std::forward<Args>(args)...`  within the `optional`.
+  // (The `in_place_t` is a tag used to indicate that the contained object
+  // should be constructed in-place.)
+  template <typename InPlaceT, typename... Args,
+            absl::enable_if_t<absl::conjunction<
+                std::is_same<InPlaceT, in_place_t>,
+                std::is_constructible<T, Args&&...> >::value>* = nullptr>
+  constexpr explicit optional(InPlaceT, Args&&... args)
+      : data_base(in_place_t(), absl::forward<Args>(args)...) {}
+
+  // Constructs a non-empty `optional` direct-initialized value of type `T` from
+  // the arguments of an initializer_list and `std::forward<Args>(args)...`.
+  // (The `in_place_t` is a tag used to indicate that the contained object
+  // should be constructed in-place.)
+  template <typename U, typename... Args,
+            typename = typename std::enable_if<std::is_constructible<
+                T, std::initializer_list<U>&, Args&&...>::value>::type>
+  constexpr explicit optional(in_place_t, std::initializer_list<U> il,
+                              Args&&... args)
+      : data_base(in_place_t(), il, absl::forward<Args>(args)...) {
+  }
+
+  // Value constructor (implicit)
+  template <
+      typename U = T,
+      typename std::enable_if<
+          absl::conjunction<absl::negation<std::is_same<
+                                in_place_t, typename std::decay<U>::type> >,
+                            absl::negation<std::is_same<
+                                optional<T>, typename std::decay<U>::type> >,
+                            std::is_convertible<U&&, T>,
+                            std::is_constructible<T, U&&> >::value,
+          bool>::type = false>
+  constexpr optional(U&& v) : data_base(in_place_t(), absl::forward<U>(v)) {}
+
+  // Value constructor (explicit)
+  template <
+      typename U = T,
+      typename std::enable_if<
+          absl::conjunction<absl::negation<std::is_same<
+                                in_place_t, typename std::decay<U>::type>>,
+                            absl::negation<std::is_same<
+                                optional<T>, typename std::decay<U>::type>>,
+                            absl::negation<std::is_convertible<U&&, T>>,
+                            std::is_constructible<T, U&&>>::value,
+          bool>::type = false>
+  explicit constexpr optional(U&& v)
+      : data_base(in_place_t(), absl::forward<U>(v)) {}
+
+  // Converting copy constructor (implicit)
+  template <typename U,
+            typename std::enable_if<
+                absl::conjunction<
+                    absl::negation<std::is_same<T, U> >,
+                    std::is_constructible<T, const U&>,
+                    absl::negation<
+                        optional_internal::
+                            is_constructible_convertible_from_optional<T, U> >,
+                    std::is_convertible<const U&, T> >::value,
+                bool>::type = false>
+  optional(const optional<U>& rhs) {
+    if (rhs) {
+      this->construct(*rhs);
+    }
+  }
+
+  // Converting copy constructor (explicit)
+  template <typename U,
+            typename std::enable_if<
+                absl::conjunction<
+                    absl::negation<std::is_same<T, U>>,
+                    std::is_constructible<T, const U&>,
+                    absl::negation<
+                        optional_internal::
+                            is_constructible_convertible_from_optional<T, U>>,
+                    absl::negation<std::is_convertible<const U&, T>>>::value,
+                bool>::type = false>
+  explicit optional(const optional<U>& rhs) {
+    if (rhs) {
+      this->construct(*rhs);
+    }
+  }
+
+  // Converting move constructor (implicit)
+  template <typename U,
+            typename std::enable_if<
+                absl::conjunction<
+                    absl::negation<std::is_same<T, U> >,
+                    std::is_constructible<T, U&&>,
+                    absl::negation<
+                        optional_internal::
+                            is_constructible_convertible_from_optional<T, U> >,
+                    std::is_convertible<U&&, T> >::value,
+                bool>::type = false>
+  optional(optional<U>&& rhs) {
+    if (rhs) {
+      this->construct(std::move(*rhs));
+    }
+  }
+
+  // Converting move constructor (explicit)
+  template <
+      typename U,
+      typename std::enable_if<
+          absl::conjunction<
+              absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>,
+              absl::negation<
+                  optional_internal::is_constructible_convertible_from_optional<
+                      T, U>>,
+              absl::negation<std::is_convertible<U&&, T>>>::value,
+          bool>::type = false>
+  explicit optional(optional<U>&& rhs) {
+    if (rhs) {
+      this->construct(std::move(*rhs));
+    }
+  }
+
+  // Destructor. Trivial if `T` is trivially destructible.
+  ~optional() = default;
+
+  // Assignment Operators
+
+  // Assignment from `nullopt`
+  //
+  // Example:
+  //
+  //   struct S { int value; };
+  //   optional<S> opt = absl::nullopt;  // Could also use opt = { };
+  optional& operator=(nullopt_t) noexcept {
+    this->destruct();
+    return *this;
+  }
+
+  // Copy assignment operator, standard semantics
+  optional& operator=(const optional& src) = default;
+
+  // Move assignment operator, standard semantics
+  optional& operator=(optional&& src) = default;
+
+  // Value assignment operators
+  template <typename U = T,
+            int&...,  // Workaround an internal compiler error in GCC 5 to 10.
+            typename = typename std::enable_if<absl::conjunction<
+                absl::negation<
+                    std::is_same<optional<T>, typename std::decay<U>::type> >,
+                absl::negation<absl::conjunction<
+                    std::is_scalar<T>,
+                    std::is_same<T, typename std::decay<U>::type> > >,
+                std::is_constructible<T, U>,
+                std::is_assignable<T&, U> >::value>::type>
+  optional& operator=(U&& v) {
+    this->assign(std::forward<U>(v));
+    return *this;
+  }
+
+  template <
+      typename U,
+      int&...,  // Workaround an internal compiler error in GCC 5 to 10.
+      typename = typename std::enable_if<absl::conjunction<
+          absl::negation<std::is_same<T, U> >,
+          std::is_constructible<T, const U&>, std::is_assignable<T&, const U&>,
+          absl::negation<
+              optional_internal::
+                  is_constructible_convertible_assignable_from_optional<
+                      T, U> > >::value>::type>
+  optional& operator=(const optional<U>& rhs) {
+    if (rhs) {
+      this->assign(*rhs);
+    } else {
+      this->destruct();
+    }
+    return *this;
+  }
+
+  template <typename U,
+            int&...,  // Workaround an internal compiler error in GCC 5 to 10.
+            typename = typename std::enable_if<absl::conjunction<
+                absl::negation<std::is_same<T, U> >,
+                std::is_constructible<T, U>, std::is_assignable<T&, U>,
+                absl::negation<
+                    optional_internal::
+                        is_constructible_convertible_assignable_from_optional<
+                            T, U> > >::value>::type>
+  optional& operator=(optional<U>&& rhs) {
+    if (rhs) {
+      this->assign(std::move(*rhs));
+    } else {
+      this->destruct();
+    }
+    return *this;
+  }
+
+  // Modifiers
+
+  // optional::reset()
+  //
+  // Destroys the inner `T` value of an `absl::optional` if one is present.
+  ABSL_ATTRIBUTE_REINITIALIZES void reset() noexcept { this->destruct(); }
+
+  // optional::emplace()
+  //
+  // (Re)constructs the underlying `T` in-place with the given forwarded
+  // arguments.
+  //
+  // Example:
+  //
+  //   optional<Foo> opt;
+  //   opt.emplace(arg1,arg2,arg3);  // Constructs Foo(arg1,arg2,arg3)
+  //
+  // If the optional is non-empty, and the `args` refer to subobjects of the
+  // current object, then behaviour is undefined, because the current object
+  // will be destructed before the new object is constructed with `args`.
+  template <typename... Args,
+            typename = typename std::enable_if<
+                std::is_constructible<T, Args&&...>::value>::type>
+  T& emplace(Args&&... args) {
+    this->destruct();
+    this->construct(std::forward<Args>(args)...);
+    return reference();
+  }
+
+  // Emplace reconstruction overload for an initializer list and the given
+  // forwarded arguments.
+  //
+  // Example:
+  //
+  //   struct Foo {
+  //     Foo(std::initializer_list<int>);
+  //   };
+  //
+  //   optional<Foo> opt;
+  //   opt.emplace({1,2,3});  // Constructs Foo({1,2,3})
+  template <typename U, typename... Args,
+            typename = typename std::enable_if<std::is_constructible<
+                T, std::initializer_list<U>&, Args&&...>::value>::type>
+  T& emplace(std::initializer_list<U> il, Args&&... args) {
+    this->destruct();
+    this->construct(il, std::forward<Args>(args)...);
+    return reference();
+  }
+
+  // Swaps
+
+  // Swap, standard semantics
+  void swap(optional& rhs) noexcept(
+      std::is_nothrow_move_constructible<T>::value&&
+          type_traits_internal::IsNothrowSwappable<T>::value) {
+    if (*this) {
+      if (rhs) {
+        type_traits_internal::Swap(**this, *rhs);
+      } else {
+        rhs.construct(std::move(**this));
+        this->destruct();
+      }
+    } else {
+      if (rhs) {
+        this->construct(std::move(*rhs));
+        rhs.destruct();
+      } else {
+        // No effect (swap(disengaged, disengaged)).
+      }
+    }
+  }
+
+  // Observers
+
+  // optional::operator->()
+  //
+  // Accesses the underlying `T` value's member `m` of an `optional`. If the
+  // `optional` is empty, behavior is undefined.
+  //
+  // If you need myOpt->foo in constexpr, use (*myOpt).foo instead.
+  const T* operator->() const {
+    ABSL_HARDENING_ASSERT(this->engaged_);
+    return std::addressof(this->data_);
+  }
+  T* operator->() {
+    ABSL_HARDENING_ASSERT(this->engaged_);
+    return std::addressof(this->data_);
+  }
+
+  // optional::operator*()
+  //
+  // Accesses the underlying `T` value of an `optional`. If the `optional` is
+  // empty, behavior is undefined.
+  constexpr const T& operator*() const& {
+    return ABSL_HARDENING_ASSERT(this->engaged_), reference();
+  }
+  T& operator*() & {
+    ABSL_HARDENING_ASSERT(this->engaged_);
+    return reference();
+  }
+  constexpr const T&& operator*() const && {
+    return ABSL_HARDENING_ASSERT(this->engaged_), absl::move(reference());
+  }
+  T&& operator*() && {
+    ABSL_HARDENING_ASSERT(this->engaged_);
+    return std::move(reference());
+  }
+
+  // optional::operator bool()
+  //
+  // Returns false if and only if the `optional` is empty.
+  //
+  //   if (opt) {
+  //     // do something with *opt or opt->;
+  //   } else {
+  //     // opt is empty.
+  //   }
+  //
+  constexpr explicit operator bool() const noexcept { return this->engaged_; }
+
+  // optional::has_value()
+  //
+  // Determines whether the `optional` contains a value. Returns `false` if and
+  // only if `*this` is empty.
+  constexpr bool has_value() const noexcept { return this->engaged_; }
+
+// Suppress bogus warning on MSVC: MSVC complains call to reference() after
+// throw_bad_optional_access() is unreachable.
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4702)
+#endif  // _MSC_VER
+  // optional::value()
+  //
+  // Returns a reference to an `optional`s underlying value. The constness
+  // and lvalue/rvalue-ness of the `optional` is preserved to the view of
+  // the `T` sub-object. Throws `absl::bad_optional_access` when the `optional`
+  // is empty.
+  constexpr const T& value() const & {
+    return static_cast<bool>(*this)
+               ? reference()
+               : (optional_internal::throw_bad_optional_access(), reference());
+  }
+  T& value() & {
+    return static_cast<bool>(*this)
+               ? reference()
+               : (optional_internal::throw_bad_optional_access(), reference());
+  }
+  T&& value() && {  // NOLINT(build/c++11)
+    return std::move(
+        static_cast<bool>(*this)
+            ? reference()
+            : (optional_internal::throw_bad_optional_access(), reference()));
+  }
+  constexpr const T&& value() const && {  // NOLINT(build/c++11)
+    return absl::move(
+        static_cast<bool>(*this)
+            ? reference()
+            : (optional_internal::throw_bad_optional_access(), reference()));
+  }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif  // _MSC_VER
+
+  // optional::value_or()
+  //
+  // Returns either the value of `T` or a passed default `v` if the `optional`
+  // is empty.
+  template <typename U>
+  constexpr T value_or(U&& v) const& {
+    static_assert(std::is_copy_constructible<value_type>::value,
+                  "optional<T>::value_or: T must be copy constructible");
+    static_assert(std::is_convertible<U&&, value_type>::value,
+                  "optional<T>::value_or: U must be convertible to T");
+    return static_cast<bool>(*this)
+               ? **this
+               : static_cast<T>(absl::forward<U>(v));
+  }
+  template <typename U>
+  T value_or(U&& v) && {  // NOLINT(build/c++11)
+    static_assert(std::is_move_constructible<value_type>::value,
+                  "optional<T>::value_or: T must be move constructible");
+    static_assert(std::is_convertible<U&&, value_type>::value,
+                  "optional<T>::value_or: U must be convertible to T");
+    return static_cast<bool>(*this) ? std::move(**this)
+                                    : static_cast<T>(std::forward<U>(v));
+  }
+
+ private:
+  // Private accessors for internal storage viewed as reference to T.
+  constexpr const T& reference() const { return this->data_; }
+  T& reference() { return this->data_; }
+
+  // T constraint checks.  You can't have an optional of nullopt_t, in_place_t
+  // or a reference.
+  static_assert(
+      !std::is_same<nullopt_t, typename std::remove_cv<T>::type>::value,
+      "optional<nullopt_t> is not allowed.");
+  static_assert(
+      !std::is_same<in_place_t, typename std::remove_cv<T>::type>::value,
+      "optional<in_place_t> is not allowed.");
+  static_assert(!std::is_reference<T>::value,
+                "optional<reference> is not allowed.");
+};
+
+// Non-member functions
+
+// swap()
+//
+// Performs a swap between two `absl::optional` objects, using standard
+// semantics.
+template <typename T, typename std::enable_if<
+                          std::is_move_constructible<T>::value &&
+                              type_traits_internal::IsSwappable<T>::value,
+                          bool>::type = false>
+void swap(optional<T>& a, optional<T>& b) noexcept(noexcept(a.swap(b))) {
+  a.swap(b);
+}
+
+// make_optional()
+//
+// Creates a non-empty `optional<T>` where the type of `T` is deduced. An
+// `absl::optional` can also be explicitly instantiated with
+// `make_optional<T>(v)`.
+//
+// Note: `make_optional()` constructions may be declared `constexpr` for
+// trivially copyable types `T`. Non-trivial types require copy elision
+// support in C++17 for `make_optional` to support `constexpr` on such
+// non-trivial types.
+//
+// Example:
+//
+//   constexpr absl::optional<int> opt = absl::make_optional(1);
+//   static_assert(opt.value() == 1, "");
+template <typename T>
+constexpr optional<typename std::decay<T>::type> make_optional(T&& v) {
+  return optional<typename std::decay<T>::type>(absl::forward<T>(v));
+}
+
+template <typename T, typename... Args>
+constexpr optional<T> make_optional(Args&&... args) {
+  return optional<T>(in_place_t(), absl::forward<Args>(args)...);
+}
+
+template <typename T, typename U, typename... Args>
+constexpr optional<T> make_optional(std::initializer_list<U> il,
+                                    Args&&... args) {
+  return optional<T>(in_place_t(), il,
+                     absl::forward<Args>(args)...);
+}
+
+// Relational operators [optional.relops]
+
+// Empty optionals are considered equal to each other and less than non-empty
+// optionals. Supports relations between optional<T> and optional<U>, between
+// optional<T> and U, and between optional<T> and nullopt.
+//
+// Note: We're careful to support T having non-bool relationals.
+
+// Requires: The expression, e.g. "*x == *y" shall be well-formed and its result
+// shall be convertible to bool.
+// The C++17 (N4606) "Returns:" statements are translated into
+// code in an obvious way here, and the original text retained as function docs.
+// Returns: If bool(x) != bool(y), false; otherwise if bool(x) == false, true;
+// otherwise *x == *y.
+template <typename T, typename U>
+constexpr auto operator==(const optional<T>& x, const optional<U>& y)
+    -> decltype(optional_internal::convertible_to_bool(*x == *y)) {
+  return static_cast<bool>(x) != static_cast<bool>(y)
+             ? false
+             : static_cast<bool>(x) == false ? true
+                                             : static_cast<bool>(*x == *y);
+}
+
+// Returns: If bool(x) != bool(y), true; otherwise, if bool(x) == false, false;
+// otherwise *x != *y.
+template <typename T, typename U>
+constexpr auto operator!=(const optional<T>& x, const optional<U>& y)
+    -> decltype(optional_internal::convertible_to_bool(*x != *y)) {
+  return static_cast<bool>(x) != static_cast<bool>(y)
+             ? true
+             : static_cast<bool>(x) == false ? false
+                                             : static_cast<bool>(*x != *y);
+}
+// Returns: If !y, false; otherwise, if !x, true; otherwise *x < *y.
+template <typename T, typename U>
+constexpr auto operator<(const optional<T>& x, const optional<U>& y)
+    -> decltype(optional_internal::convertible_to_bool(*x < *y)) {
+  return !y ? false : !x ? true : static_cast<bool>(*x < *y);
+}
+// Returns: If !x, false; otherwise, if !y, true; otherwise *x > *y.
+template <typename T, typename U>
+constexpr auto operator>(const optional<T>& x, const optional<U>& y)
+    -> decltype(optional_internal::convertible_to_bool(*x > *y)) {
+  return !x ? false : !y ? true : static_cast<bool>(*x > *y);
+}
+// Returns: If !x, true; otherwise, if !y, false; otherwise *x <= *y.
+template <typename T, typename U>
+constexpr auto operator<=(const optional<T>& x, const optional<U>& y)
+    -> decltype(optional_internal::convertible_to_bool(*x <= *y)) {
+  return !x ? true : !y ? false : static_cast<bool>(*x <= *y);
+}
+// Returns: If !y, true; otherwise, if !x, false; otherwise *x >= *y.
+template <typename T, typename U>
+constexpr auto operator>=(const optional<T>& x, const optional<U>& y)
+    -> decltype(optional_internal::convertible_to_bool(*x >= *y)) {
+  return !y ? true : !x ? false : static_cast<bool>(*x >= *y);
+}
+
+// Comparison with nullopt [optional.nullops]
+// The C++17 (N4606) "Returns:" statements are used directly here.
+template <typename T>
+constexpr bool operator==(const optional<T>& x, nullopt_t) noexcept {
+  return !x;
+}
+template <typename T>
+constexpr bool operator==(nullopt_t, const optional<T>& x) noexcept {
+  return !x;
+}
+template <typename T>
+constexpr bool operator!=(const optional<T>& x, nullopt_t) noexcept {
+  return static_cast<bool>(x);
+}
+template <typename T>
+constexpr bool operator!=(nullopt_t, const optional<T>& x) noexcept {
+  return static_cast<bool>(x);
+}
+template <typename T>
+constexpr bool operator<(const optional<T>&, nullopt_t) noexcept {
+  return false;
+}
+template <typename T>
+constexpr bool operator<(nullopt_t, const optional<T>& x) noexcept {
+  return static_cast<bool>(x);
+}
+template <typename T>
+constexpr bool operator<=(const optional<T>& x, nullopt_t) noexcept {
+  return !x;
+}
+template <typename T>
+constexpr bool operator<=(nullopt_t, const optional<T>&) noexcept {
+  return true;
+}
+template <typename T>
+constexpr bool operator>(const optional<T>& x, nullopt_t) noexcept {
+  return static_cast<bool>(x);
+}
+template <typename T>
+constexpr bool operator>(nullopt_t, const optional<T>&) noexcept {
+  return false;
+}
+template <typename T>
+constexpr bool operator>=(const optional<T>&, nullopt_t) noexcept {
+  return true;
+}
+template <typename T>
+constexpr bool operator>=(nullopt_t, const optional<T>& x) noexcept {
+  return !x;
+}
+
+// Comparison with T [optional.comp_with_t]
+
+// Requires: The expression, e.g. "*x == v" shall be well-formed and its result
+// shall be convertible to bool.
+// The C++17 (N4606) "Equivalent to:" statements are used directly here.
+template <typename T, typename U>
+constexpr auto operator==(const optional<T>& x, const U& v)
+    -> decltype(optional_internal::convertible_to_bool(*x == v)) {
+  return static_cast<bool>(x) ? static_cast<bool>(*x == v) : false;
+}
+template <typename T, typename U>
+constexpr auto operator==(const U& v, const optional<T>& x)
+    -> decltype(optional_internal::convertible_to_bool(v == *x)) {
+  return static_cast<bool>(x) ? static_cast<bool>(v == *x) : false;
+}
+template <typename T, typename U>
+constexpr auto operator!=(const optional<T>& x, const U& v)
+    -> decltype(optional_internal::convertible_to_bool(*x != v)) {
+  return static_cast<bool>(x) ? static_cast<bool>(*x != v) : true;
+}
+template <typename T, typename U>
+constexpr auto operator!=(const U& v, const optional<T>& x)
+    -> decltype(optional_internal::convertible_to_bool(v != *x)) {
+  return static_cast<bool>(x) ? static_cast<bool>(v != *x) : true;
+}
+template <typename T, typename U>
+constexpr auto operator<(const optional<T>& x, const U& v)
+    -> decltype(optional_internal::convertible_to_bool(*x < v)) {
+  return static_cast<bool>(x) ? static_cast<bool>(*x < v) : true;
+}
+template <typename T, typename U>
+constexpr auto operator<(const U& v, const optional<T>& x)
+    -> decltype(optional_internal::convertible_to_bool(v < *x)) {
+  return static_cast<bool>(x) ? static_cast<bool>(v < *x) : false;
+}
+template <typename T, typename U>
+constexpr auto operator<=(const optional<T>& x, const U& v)
+    -> decltype(optional_internal::convertible_to_bool(*x <= v)) {
+  return static_cast<bool>(x) ? static_cast<bool>(*x <= v) : true;
+}
+template <typename T, typename U>
+constexpr auto operator<=(const U& v, const optional<T>& x)
+    -> decltype(optional_internal::convertible_to_bool(v <= *x)) {
+  return static_cast<bool>(x) ? static_cast<bool>(v <= *x) : false;
+}
+template <typename T, typename U>
+constexpr auto operator>(const optional<T>& x, const U& v)
+    -> decltype(optional_internal::convertible_to_bool(*x > v)) {
+  return static_cast<bool>(x) ? static_cast<bool>(*x > v) : false;
+}
+template <typename T, typename U>
+constexpr auto operator>(const U& v, const optional<T>& x)
+    -> decltype(optional_internal::convertible_to_bool(v > *x)) {
+  return static_cast<bool>(x) ? static_cast<bool>(v > *x) : true;
+}
+template <typename T, typename U>
+constexpr auto operator>=(const optional<T>& x, const U& v)
+    -> decltype(optional_internal::convertible_to_bool(*x >= v)) {
+  return static_cast<bool>(x) ? static_cast<bool>(*x >= v) : false;
+}
+template <typename T, typename U>
+constexpr auto operator>=(const U& v, const optional<T>& x)
+    -> decltype(optional_internal::convertible_to_bool(v >= *x)) {
+  return static_cast<bool>(x) ? static_cast<bool>(v >= *x) : true;
+}
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+namespace std {
+
+// std::hash specialization for absl::optional.
+template <typename T>
+struct hash<absl::optional<T> >
+    : absl::optional_internal::optional_hash_base<T> {};
+
+}  // namespace std
+
+#undef ABSL_MSVC_CONSTEXPR_BUG_IN_UNION_LIKE_CLASS
+
+#endif  // ABSL_USES_STD_OPTIONAL
+
+#endif  // ABSL_TYPES_OPTIONAL_H_
diff --git a/third_party/abseil-cpp/absl/types/variant.h b/third_party/abseil-cpp/absl/types/variant.h
new file mode 100644
index 0000000..aa541ac
--- /dev/null
+++ b/third_party/abseil-cpp/absl/types/variant.h
@@ -0,0 +1,866 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// variant.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines an `absl::variant` type for holding a type-safe
+// value of some prescribed set of types (noted as alternative types), and
+// associated functions for managing variants.
+//
+// The `absl::variant` type is a form of type-safe union. An `absl::variant`
+// should always hold a value of one of its alternative types (except in the
+// "valueless by exception state" -- see below). A default-constructed
+// `absl::variant` will hold the value of its first alternative type, provided
+// it is default-constructible.
+//
+// In exceptional cases due to error, an `absl::variant` can hold no
+// value (known as a "valueless by exception" state), though this is not the
+// norm.
+//
+// As with `absl::optional`, an `absl::variant` -- when it holds a value --
+// allocates a value of that type directly within the `variant` itself; it
+// cannot hold a reference, array, or the type `void`; it can, however, hold a
+// pointer to externally managed memory.
+//
+// `absl::variant` is a C++11 compatible version of the C++17 `std::variant`
+// abstraction and is designed to be a drop-in replacement for code compliant
+// with C++17.
+
+#ifndef ABSL_TYPES_VARIANT_H_
+#define ABSL_TYPES_VARIANT_H_
+
+#include "third_party/abseil-cpp/absl/base/config.h"
+#include "third_party/abseil-cpp/absl/utility/utility.h"
+
+#ifdef ABSL_USES_STD_VARIANT
+
+#include <variant>  // IWYU pragma: export
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+using std::bad_variant_access;
+using std::get;
+using std::get_if;
+using std::holds_alternative;
+using std::monostate;
+using std::variant;
+using std::variant_alternative;
+using std::variant_alternative_t;
+using std::variant_npos;
+using std::variant_size;
+using std::variant_size_v;
+using std::visit;
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#else  // ABSL_USES_STD_VARIANT
+
+#include <functional>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+#include "third_party/absl/base/macros.h"
+#include "third_party/absl/base/port.h"
+#include "third_party/absl/meta/type_traits.h"
+#include "third_party/absl/types/internal/variant.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// -----------------------------------------------------------------------------
+// absl::variant
+// -----------------------------------------------------------------------------
+//
+// An `absl::variant` type is a form of type-safe union. An `absl::variant` --
+// except in exceptional cases -- always holds a value of one of its alternative
+// types.
+//
+// Example:
+//
+//   // Construct a variant that holds either an integer or a std::string and
+//   // assign it to a std::string.
+//   absl::variant<int, std::string> v = std::string("abc");
+//
+//   // A default-constructed variant will hold a value-initialized value of
+//   // the first alternative type.
+//   auto a = absl::variant<int, std::string>();   // Holds an int of value '0'.
+//
+//   // variants are assignable.
+//
+//   // copy assignment
+//   auto v1 = absl::variant<int, std::string>("abc");
+//   auto v2 = absl::variant<int, std::string>(10);
+//   v2 = v1;  // copy assign
+//
+//   // move assignment
+//   auto v1 = absl::variant<int, std::string>("abc");
+//   v1 = absl::variant<int, std::string>(10);
+//
+//   // assignment through type conversion
+//   a = 128;         // variant contains int
+//   a = "128";       // variant contains std::string
+//
+// An `absl::variant` holding a value of one of its alternative types `T` holds
+// an allocation of `T` directly within the variant itself. An `absl::variant`
+// is not allowed to allocate additional storage, such as dynamic memory, to
+// allocate the contained value. The contained value shall be allocated in a
+// region of the variant storage suitably aligned for all alternative types.
+template <typename... Ts>
+class variant;
+
+// swap()
+//
+// Swaps two `absl::variant` values. This function is equivalent to `v.swap(w)`
+// where `v` and `w` are `absl::variant` types.
+//
+// Note that this function requires all alternative types to be both swappable
+// and move-constructible, because any two variants may refer to either the same
+// type (in which case, they will be swapped) or to two different types (in
+// which case the values will need to be moved).
+//
+template <
+    typename... Ts,
+    absl::enable_if_t<
+        absl::conjunction<std::is_move_constructible<Ts>...,
+                          type_traits_internal::IsSwappable<Ts>...>::value,
+        int> = 0>
+void swap(variant<Ts...>& v, variant<Ts...>& w) noexcept(noexcept(v.swap(w))) {
+  v.swap(w);
+}
+
+// variant_size
+//
+// Returns the number of alternative types available for a given `absl::variant`
+// type as a compile-time constant expression. As this is a class template, it
+// is not generally useful for accessing the number of alternative types of
+// any given `absl::variant` instance.
+//
+// Example:
+//
+//   auto a = absl::variant<int, std::string>;
+//   constexpr int num_types =
+//       absl::variant_size<absl::variant<int, std::string>>();
+//
+//   // You can also use the member constant `value`.
+//   constexpr int num_types =
+//       absl::variant_size<absl::variant<int, std::string>>::value;
+//
+//   // `absl::variant_size` is more valuable for use in generic code:
+//   template <typename Variant>
+//   constexpr bool IsVariantMultivalue() {
+//       return absl::variant_size<Variant>() > 1;
+//   }
+//
+// Note that the set of cv-qualified specializations of `variant_size` are
+// provided to ensure that those specializations compile (especially when passed
+// within template logic).
+template <class T>
+struct variant_size;
+
+template <class... Ts>
+struct variant_size<variant<Ts...>>
+    : std::integral_constant<std::size_t, sizeof...(Ts)> {};
+
+// Specialization of `variant_size` for const qualified variants.
+template <class T>
+struct variant_size<const T> : variant_size<T>::type {};
+
+// Specialization of `variant_size` for volatile qualified variants.
+template <class T>
+struct variant_size<volatile T> : variant_size<T>::type {};
+
+// Specialization of `variant_size` for const volatile qualified variants.
+template <class T>
+struct variant_size<const volatile T> : variant_size<T>::type {};
+
+// variant_alternative
+//
+// Returns the alternative type for a given `absl::variant` at the passed
+// index value as a compile-time constant expression. As this is a class
+// template resulting in a type, it is not useful for access of the run-time
+// value of any given `absl::variant` variable.
+//
+// Example:
+//
+//   // The type of the 0th alternative is "int".
+//   using alternative_type_0
+//     = absl::variant_alternative<0, absl::variant<int, std::string>>::type;
+//
+//   static_assert(std::is_same<alternative_type_0, int>::value, "");
+//
+//   // `absl::variant_alternative` is more valuable for use in generic code:
+//   template <typename Variant>
+//   constexpr bool IsFirstElementTrivial() {
+//       return std::is_trivial_v<variant_alternative<0, Variant>::type>;
+//   }
+//
+// Note that the set of cv-qualified specializations of `variant_alternative`
+// are provided to ensure that those specializations compile (especially when
+// passed within template logic).
+template <std::size_t I, class T>
+struct variant_alternative;
+
+template <std::size_t I, class... Types>
+struct variant_alternative<I, variant<Types...>> {
+  using type =
+      variant_internal::VariantAlternativeSfinaeT<I, variant<Types...>>;
+};
+
+// Specialization of `variant_alternative` for const qualified variants.
+template <std::size_t I, class T>
+struct variant_alternative<I, const T> {
+  using type = const typename variant_alternative<I, T>::type;
+};
+
+// Specialization of `variant_alternative` for volatile qualified variants.
+template <std::size_t I, class T>
+struct variant_alternative<I, volatile T> {
+  using type = volatile typename variant_alternative<I, T>::type;
+};
+
+// Specialization of `variant_alternative` for const volatile qualified
+// variants.
+template <std::size_t I, class T>
+struct variant_alternative<I, const volatile T> {
+  using type = const volatile typename variant_alternative<I, T>::type;
+};
+
+// Template type alias for variant_alternative<I, T>::type.
+//
+// Example:
+//
+//   using alternative_type_0
+//     = absl::variant_alternative_t<0, absl::variant<int, std::string>>;
+//   static_assert(std::is_same<alternative_type_0, int>::value, "");
+template <std::size_t I, class T>
+using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+// holds_alternative()
+//
+// Checks whether the given variant currently holds a given alternative type,
+// returning `true` if so.
+//
+// Example:
+//
+//   absl::variant<int, std::string> foo = 42;
+//   if (absl::holds_alternative<int>(foo)) {
+//       std::cout << "The variant holds an integer";
+//   }
+template <class T, class... Types>
+constexpr bool holds_alternative(const variant<Types...>& v) noexcept {
+  static_assert(
+      variant_internal::UnambiguousIndexOfImpl<variant<Types...>, T,
+                                               0>::value != sizeof...(Types),
+      "The type T must occur exactly once in Types...");
+  return v.index() ==
+         variant_internal::UnambiguousIndexOf<variant<Types...>, T>::value;
+}
+
+// get()
+//
+// Returns a reference to the value currently within a given variant, using
+// either a unique alternative type amongst the variant's set of alternative
+// types, or the variant's index value. Attempting to get a variant's value
+// using a type that is not unique within the variant's set of alternative types
+// is a compile-time error. If the index of the alternative being specified is
+// different from the index of the alternative that is currently stored, throws
+// `absl::bad_variant_access`.
+//
+// Example:
+//
+//   auto a = absl::variant<int, std::string>;
+//
+//   // Get the value by type (if unique).
+//   int i = absl::get<int>(a);
+//
+//   auto b = absl::variant<int, int>;
+//
+//   // Getting the value by a type that is not unique is ill-formed.
+//   int j = absl::get<int>(b);     // Compile Error!
+//
+//   // Getting value by index not ambiguous and allowed.
+//   int k = absl::get<1>(b);
+
+// Overload for getting a variant's lvalue by type.
+template <class T, class... Types>
+constexpr T& get(variant<Types...>& v) {  // NOLINT
+  return variant_internal::VariantCoreAccess::CheckedAccess<
+      variant_internal::IndexOf<T, Types...>::value>(v);
+}
+
+// Overload for getting a variant's rvalue by type.
+// Note: `absl::move()` is required to allow use of constexpr in C++11.
+template <class T, class... Types>
+constexpr T&& get(variant<Types...>&& v) {
+  return variant_internal::VariantCoreAccess::CheckedAccess<
+      variant_internal::IndexOf<T, Types...>::value>(absl::move(v));
+}
+
+// Overload for getting a variant's const lvalue by type.
+template <class T, class... Types>
+constexpr const T& get(const variant<Types...>& v) {
+  return variant_internal::VariantCoreAccess::CheckedAccess<
+      variant_internal::IndexOf<T, Types...>::value>(v);
+}
+
+// Overload for getting a variant's const rvalue by type.
+// Note: `absl::move()` is required to allow use of constexpr in C++11.
+template <class T, class... Types>
+constexpr const T&& get(const variant<Types...>&& v) {
+  return variant_internal::VariantCoreAccess::CheckedAccess<
+      variant_internal::IndexOf<T, Types...>::value>(absl::move(v));
+}
+
+// Overload for getting a variant's lvalue by index.
+template <std::size_t I, class... Types>
+constexpr variant_alternative_t<I, variant<Types...>>& get(
+    variant<Types...>& v) {  // NOLINT
+  return variant_internal::VariantCoreAccess::CheckedAccess<I>(v);
+}
+
+// Overload for getting a variant's rvalue by index.
+// Note: `absl::move()` is required to allow use of constexpr in C++11.
+template <std::size_t I, class... Types>
+constexpr variant_alternative_t<I, variant<Types...>>&& get(
+    variant<Types...>&& v) {
+  return variant_internal::VariantCoreAccess::CheckedAccess<I>(absl::move(v));
+}
+
+// Overload for getting a variant's const lvalue by index.
+template <std::size_t I, class... Types>
+constexpr const variant_alternative_t<I, variant<Types...>>& get(
+    const variant<Types...>& v) {
+  return variant_internal::VariantCoreAccess::CheckedAccess<I>(v);
+}
+
+// Overload for getting a variant's const rvalue by index.
+// Note: `absl::move()` is required to allow use of constexpr in C++11.
+template <std::size_t I, class... Types>
+constexpr const variant_alternative_t<I, variant<Types...>>&& get(
+    const variant<Types...>&& v) {
+  return variant_internal::VariantCoreAccess::CheckedAccess<I>(absl::move(v));
+}
+
+// get_if()
+//
+// Returns a pointer to the value currently stored within a given variant, if
+// present, using either a unique alternative type amongst the variant's set of
+// alternative types, or the variant's index value. If such a value does not
+// exist, returns `nullptr`.
+//
+// As with `get`, attempting to get a variant's value using a type that is not
+// unique within the variant's set of alternative types is a compile-time error.
+
+// Overload for getting a pointer to the value stored in the given variant by
+// index.
+template <std::size_t I, class... Types>
+constexpr absl::add_pointer_t<variant_alternative_t<I, variant<Types...>>>
+get_if(variant<Types...>* v) noexcept {
+  return (v != nullptr && v->index() == I)
+             ? std::addressof(
+                   variant_internal::VariantCoreAccess::Access<I>(*v))
+             : nullptr;
+}
+
+// Overload for getting a pointer to the const value stored in the given
+// variant by index.
+template <std::size_t I, class... Types>
+constexpr absl::add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
+get_if(const variant<Types...>* v) noexcept {
+  return (v != nullptr && v->index() == I)
+             ? std::addressof(
+                   variant_internal::VariantCoreAccess::Access<I>(*v))
+             : nullptr;
+}
+
+// Overload for getting a pointer to the value stored in the given variant by
+// type.
+template <class T, class... Types>
+constexpr absl::add_pointer_t<T> get_if(variant<Types...>* v) noexcept {
+  return absl::get_if<variant_internal::IndexOf<T, Types...>::value>(v);
+}
+
+// Overload for getting a pointer to the const value stored in the given variant
+// by type.
+template <class T, class... Types>
+constexpr absl::add_pointer_t<const T> get_if(
+    const variant<Types...>* v) noexcept {
+  return absl::get_if<variant_internal::IndexOf<T, Types...>::value>(v);
+}
+
+// visit()
+//
+// Calls a provided functor on a given set of variants. `absl::visit()` is
+// commonly used to conditionally inspect the state of a given variant (or set
+// of variants).
+//
+// The functor must return the same type when called with any of the variants'
+// alternatives.
+//
+// Example:
+//
+//   // Define a visitor functor
+//   struct GetVariant {
+//       template<typename T>
+//       void operator()(const T& i) const {
+//         std::cout << "The variant's value is: " << i;
+//       }
+//   };
+//
+//   // Declare our variant, and call `absl::visit()` on it.
+//   // Note that `GetVariant()` returns void in either case.
+//   absl::variant<int, std::string> foo = std::string("foo");
+//   GetVariant visitor;
+//   absl::visit(visitor, foo);  // Prints `The variant's value is: foo'
+template <typename Visitor, typename... Variants>
+variant_internal::VisitResult<Visitor, Variants...> visit(Visitor&& vis,
+                                                          Variants&&... vars) {
+  return variant_internal::
+      VisitIndices<variant_size<absl::decay_t<Variants> >::value...>::Run(
+          variant_internal::PerformVisitation<Visitor, Variants...>{
+              std::forward_as_tuple(absl::forward<Variants>(vars)...),
+              absl::forward<Visitor>(vis)},
+          vars.index()...);
+}
+
+// monostate
+//
+// The monostate class serves as a first alternative type for a variant for
+// which the first variant type is otherwise not default-constructible.
+struct monostate {};
+
+// `absl::monostate` Relational Operators
+
+constexpr bool operator<(monostate, monostate) noexcept { return false; }
+constexpr bool operator>(monostate, monostate) noexcept { return false; }
+constexpr bool operator<=(monostate, monostate) noexcept { return true; }
+constexpr bool operator>=(monostate, monostate) noexcept { return true; }
+constexpr bool operator==(monostate, monostate) noexcept { return true; }
+constexpr bool operator!=(monostate, monostate) noexcept { return false; }
+
+
+//------------------------------------------------------------------------------
+// `absl::variant` Template Definition
+//------------------------------------------------------------------------------
+template <typename T0, typename... Tn>
+class variant<T0, Tn...> : private variant_internal::VariantBase<T0, Tn...> {
+  static_assert(absl::conjunction<std::is_object<T0>,
+                                  std::is_object<Tn>...>::value,
+                "Attempted to instantiate a variant containing a non-object "
+                "type.");
+  // Intentionally not qualifying `negation` with `absl::` to work around a bug
+  // in MSVC 2015 with inline namespace and variadic template.
+  static_assert(absl::conjunction<negation<std::is_array<T0> >,
+                                  negation<std::is_array<Tn> >...>::value,
+                "Attempted to instantiate a variant containing an array type.");
+  static_assert(absl::conjunction<std::is_nothrow_destructible<T0>,
+                                  std::is_nothrow_destructible<Tn>...>::value,
+                "Attempted to instantiate a variant containing a non-nothrow "
+                "destructible type.");
+
+  friend struct variant_internal::VariantCoreAccess;
+
+ private:
+  using Base = variant_internal::VariantBase<T0, Tn...>;
+
+ public:
+  // Constructors
+
+  // Constructs a variant holding a default-initialized value of the first
+  // alternative type.
+  constexpr variant() /*noexcept(see 111above)*/ = default;
+
+  // Copy constructor, standard semantics
+  variant(const variant& other) = default;
+
+  // Move constructor, standard semantics
+  variant(variant&& other) /*noexcept(see above)*/ = default;
+
+  // Constructs a variant of an alternative type specified by overload
+  // resolution of the provided forwarding arguments through
+  // direct-initialization.
+  //
+  // Note: If the selected constructor is a constexpr constructor, this
+  // constructor shall be a constexpr constructor.
+  //
+  // NOTE: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0608r1.html
+  // has been voted passed the design phase in the C++ standard meeting in Mar
+  // 2018. It will be implemented and integrated into `absl::variant`.
+  template <
+      class T,
+      std::size_t I = std::enable_if<
+          variant_internal::IsNeitherSelfNorInPlace<variant,
+                                                    absl::decay_t<T>>::value,
+          variant_internal::IndexOfConstructedType<variant, T>>::type::value,
+      class Tj = absl::variant_alternative_t<I, variant>,
+      absl::enable_if_t<std::is_constructible<Tj, T>::value>* =
+          nullptr>
+  constexpr variant(T&& t) noexcept(std::is_nothrow_constructible<Tj, T>::value)
+      : Base(variant_internal::EmplaceTag<I>(), absl::forward<T>(t)) {}
+
+  // Constructs a variant of an alternative type from the arguments through
+  // direct-initialization.
+  //
+  // Note: If the selected constructor is a constexpr constructor, this
+  // constructor shall be a constexpr constructor.
+  template <class T, class... Args,
+            typename std::enable_if<std::is_constructible<
+                variant_internal::UnambiguousTypeOfT<variant, T>,
+                Args...>::value>::type* = nullptr>
+  constexpr explicit variant(in_place_type_t<T>, Args&&... args)
+      : Base(variant_internal::EmplaceTag<
+                 variant_internal::UnambiguousIndexOf<variant, T>::value>(),
+             absl::forward<Args>(args)...) {}
+
+  // Constructs a variant of an alternative type from an initializer list
+  // and other arguments through direct-initialization.
+  //
+  // Note: If the selected constructor is a constexpr constructor, this
+  // constructor shall be a constexpr constructor.
+  template <class T, class U, class... Args,
+            typename std::enable_if<std::is_constructible<
+                variant_internal::UnambiguousTypeOfT<variant, T>,
+                std::initializer_list<U>&, Args...>::value>::type* = nullptr>
+  constexpr explicit variant(in_place_type_t<T>, std::initializer_list<U> il,
+                             Args&&... args)
+      : Base(variant_internal::EmplaceTag<
+                 variant_internal::UnambiguousIndexOf<variant, T>::value>(),
+             il, absl::forward<Args>(args)...) {}
+
+  // Constructs a variant of an alternative type from a provided index,
+  // through value-initialization using the provided forwarded arguments.
+  template <std::size_t I, class... Args,
+            typename std::enable_if<std::is_constructible<
+                variant_internal::VariantAlternativeSfinaeT<I, variant>,
+                Args...>::value>::type* = nullptr>
+  constexpr explicit variant(in_place_index_t<I>, Args&&... args)
+      : Base(variant_internal::EmplaceTag<I>(), absl::forward<Args>(args)...) {}
+
+  // Constructs a variant of an alternative type from a provided index,
+  // through value-initialization of an initializer list and the provided
+  // forwarded arguments.
+  template <std::size_t I, class U, class... Args,
+            typename std::enable_if<std::is_constructible<
+                variant_internal::VariantAlternativeSfinaeT<I, variant>,
+                std::initializer_list<U>&, Args...>::value>::type* = nullptr>
+  constexpr explicit variant(in_place_index_t<I>, std::initializer_list<U> il,
+                             Args&&... args)
+      : Base(variant_internal::EmplaceTag<I>(), il,
+             absl::forward<Args>(args)...) {}
+
+  // Destructors
+
+  // Destroys the variant's currently contained value, provided that
+  // `absl::valueless_by_exception()` is false.
+  ~variant() = default;
+
+  // Assignment Operators
+
+  // Copy assignment operator
+  variant& operator=(const variant& other) = default;
+
+  // Move assignment operator
+  variant& operator=(variant&& other) /*noexcept(see above)*/ = default;
+
+  // Converting assignment operator
+  //
+  // NOTE: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0608r1.html
+  // has been voted passed the design phase in the C++ standard meeting in Mar
+  // 2018. It will be implemented and integrated into `absl::variant`.
+  template <
+      class T,
+      std::size_t I = std::enable_if<
+          !std::is_same<absl::decay_t<T>, variant>::value,
+          variant_internal::IndexOfConstructedType<variant, T>>::type::value,
+      class Tj = absl::variant_alternative_t<I, variant>,
+      typename std::enable_if<std::is_assignable<Tj&, T>::value &&
+                              std::is_constructible<Tj, T>::value>::type* =
+          nullptr>
+  variant& operator=(T&& t) noexcept(
+      std::is_nothrow_assignable<Tj&, T>::value&&
+          std::is_nothrow_constructible<Tj, T>::value) {
+    variant_internal::VisitIndices<sizeof...(Tn) + 1>::Run(
+        variant_internal::VariantCoreAccess::MakeConversionAssignVisitor(
+            this, absl::forward<T>(t)),
+        index());
+
+    return *this;
+  }
+
+
+  // emplace() Functions
+
+  // Constructs a value of the given alternative type T within the variant. The
+  // existing value of the variant is destroyed first (provided that
+  // `absl::valueless_by_exception()` is false). Requires that T is unambiguous
+  // in the variant.
+  //
+  // Example:
+  //
+  //   absl::variant<std::vector<int>, int, std::string> v;
+  //   v.emplace<int>(99);
+  //   v.emplace<std::string>("abc");
+  template <
+      class T, class... Args,
+      typename std::enable_if<std::is_constructible<
+          absl::variant_alternative_t<
+              variant_internal::UnambiguousIndexOf<variant, T>::value, variant>,
+          Args...>::value>::type* = nullptr>
+  T& emplace(Args&&... args) {
+    return variant_internal::VariantCoreAccess::Replace<
+        variant_internal::UnambiguousIndexOf<variant, T>::value>(
+        this, absl::forward<Args>(args)...);
+  }
+
+  // Constructs a value of the given alternative type T within the variant using
+  // an initializer list. The existing value of the variant is destroyed first
+  // (provided that `absl::valueless_by_exception()` is false). Requires that T
+  // is unambiguous in the variant.
+  //
+  // Example:
+  //
+  //   absl::variant<std::vector<int>, int, std::string> v;
+  //   v.emplace<std::vector<int>>({0, 1, 2});
+  template <
+      class T, class U, class... Args,
+      typename std::enable_if<std::is_constructible<
+          absl::variant_alternative_t<
+              variant_internal::UnambiguousIndexOf<variant, T>::value, variant>,
+          std::initializer_list<U>&, Args...>::value>::type* = nullptr>
+  T& emplace(std::initializer_list<U> il, Args&&... args) {
+    return variant_internal::VariantCoreAccess::Replace<
+        variant_internal::UnambiguousIndexOf<variant, T>::value>(
+        this, il, absl::forward<Args>(args)...);
+  }
+
+  // Destroys the current value of the variant (provided that
+  // `absl::valueless_by_exception()` is false) and constructs a new value at
+  // the given index.
+  //
+  // Example:
+  //
+  //   absl::variant<std::vector<int>, int, int> v;
+  //   v.emplace<1>(99);
+  //   v.emplace<2>(98);
+  //   v.emplace<int>(99);  // Won't compile. 'int' isn't a unique type.
+  template <std::size_t I, class... Args,
+            typename std::enable_if<
+                std::is_constructible<absl::variant_alternative_t<I, variant>,
+                                      Args...>::value>::type* = nullptr>
+  absl::variant_alternative_t<I, variant>& emplace(Args&&... args) {
+    return variant_internal::VariantCoreAccess::Replace<I>(
+        this, absl::forward<Args>(args)...);
+  }
+
+  // Destroys the current value of the variant (provided that
+  // `absl::valueless_by_exception()` is false) and constructs a new value at
+  // the given index using an initializer list and the provided arguments.
+  //
+  // Example:
+  //
+  //   absl::variant<std::vector<int>, int, int> v;
+  //   v.emplace<0>({0, 1, 2});
+  template <std::size_t I, class U, class... Args,
+            typename std::enable_if<std::is_constructible<
+                absl::variant_alternative_t<I, variant>,
+                std::initializer_list<U>&, Args...>::value>::type* = nullptr>
+  absl::variant_alternative_t<I, variant>& emplace(std::initializer_list<U> il,
+                                                   Args&&... args) {
+    return variant_internal::VariantCoreAccess::Replace<I>(
+        this, il, absl::forward<Args>(args)...);
+  }
+
+  // variant::valueless_by_exception()
+  //
+  // Returns false if and only if the variant currently holds a valid value.
+  constexpr bool valueless_by_exception() const noexcept {
+    return this->index_ == absl::variant_npos;
+  }
+
+  // variant::index()
+  //
+  // Returns the index value of the variant's currently selected alternative
+  // type.
+  constexpr std::size_t index() const noexcept { return this->index_; }
+
+  // variant::swap()
+  //
+  // Swaps the values of two variant objects.
+  //
+  void swap(variant& rhs) noexcept(
+      absl::conjunction<
+          std::is_nothrow_move_constructible<T0>,
+          std::is_nothrow_move_constructible<Tn>...,
+          type_traits_internal::IsNothrowSwappable<T0>,
+          type_traits_internal::IsNothrowSwappable<Tn>...>::value) {
+    return variant_internal::VisitIndices<sizeof...(Tn) + 1>::Run(
+        variant_internal::Swap<T0, Tn...>{this, &rhs}, rhs.index());
+  }
+};
+
+// We need a valid declaration of variant<> for SFINAE and overload resolution
+// to work properly above, but we don't need a full declaration since this type
+// will never be constructed. This declaration, though incomplete, suffices.
+template <>
+class variant<>;
+
+//------------------------------------------------------------------------------
+// Relational Operators
+//------------------------------------------------------------------------------
+//
+// If neither operand is in the `variant::valueless_by_exception` state:
+//
+//   * If the index of both variants is the same, the relational operator
+//     returns the result of the corresponding relational operator for the
+//     corresponding alternative type.
+//   * If the index of both variants is not the same, the relational operator
+//     returns the result of that operation applied to the value of the left
+//     operand's index and the value of the right operand's index.
+//   * If at least one operand is in the valueless_by_exception state:
+//     - A variant in the valueless_by_exception state is only considered equal
+//       to another variant in the valueless_by_exception state.
+//     - If exactly one operand is in the valueless_by_exception state, the
+//       variant in the valueless_by_exception state is less than the variant
+//       that is not in the valueless_by_exception state.
+//
+// Note: The value 1 is added to each index in the relational comparisons such
+// that the index corresponding to the valueless_by_exception state wraps around
+// to 0 (the lowest value for the index type), and the remaining indices stay in
+// the same relative order.
+
+// Equal-to operator
+template <typename... Types>
+constexpr variant_internal::RequireAllHaveEqualT<Types...> operator==(
+    const variant<Types...>& a, const variant<Types...>& b) {
+  return (a.index() == b.index()) &&
+         variant_internal::VisitIndices<sizeof...(Types)>::Run(
+             variant_internal::EqualsOp<Types...>{&a, &b}, a.index());
+}
+
+// Not equal operator
+template <typename... Types>
+constexpr variant_internal::RequireAllHaveNotEqualT<Types...> operator!=(
+    const variant<Types...>& a, const variant<Types...>& b) {
+  return (a.index() != b.index()) ||
+         variant_internal::VisitIndices<sizeof...(Types)>::Run(
+             variant_internal::NotEqualsOp<Types...>{&a, &b}, a.index());
+}
+
+// Less-than operator
+template <typename... Types>
+constexpr variant_internal::RequireAllHaveLessThanT<Types...> operator<(
+    const variant<Types...>& a, const variant<Types...>& b) {
+  return (a.index() != b.index())
+             ? (a.index() + 1) < (b.index() + 1)
+             : variant_internal::VisitIndices<sizeof...(Types)>::Run(
+                   variant_internal::LessThanOp<Types...>{&a, &b}, a.index());
+}
+
+// Greater-than operator
+template <typename... Types>
+constexpr variant_internal::RequireAllHaveGreaterThanT<Types...> operator>(
+    const variant<Types...>& a, const variant<Types...>& b) {
+  return (a.index() != b.index())
+             ? (a.index() + 1) > (b.index() + 1)
+             : variant_internal::VisitIndices<sizeof...(Types)>::Run(
+                   variant_internal::GreaterThanOp<Types...>{&a, &b},
+                   a.index());
+}
+
+// Less-than or equal-to operator
+template <typename... Types>
+constexpr variant_internal::RequireAllHaveLessThanOrEqualT<Types...> operator<=(
+    const variant<Types...>& a, const variant<Types...>& b) {
+  return (a.index() != b.index())
+             ? (a.index() + 1) < (b.index() + 1)
+             : variant_internal::VisitIndices<sizeof...(Types)>::Run(
+                   variant_internal::LessThanOrEqualsOp<Types...>{&a, &b},
+                   a.index());
+}
+
+// Greater-than or equal-to operator
+template <typename... Types>
+constexpr variant_internal::RequireAllHaveGreaterThanOrEqualT<Types...>
+operator>=(const variant<Types...>& a, const variant<Types...>& b) {
+  return (a.index() != b.index())
+             ? (a.index() + 1) > (b.index() + 1)
+             : variant_internal::VisitIndices<sizeof...(Types)>::Run(
+                   variant_internal::GreaterThanOrEqualsOp<Types...>{&a, &b},
+                   a.index());
+}
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+namespace std {
+
+// hash()
+template <>  // NOLINT
+struct hash<absl::monostate> {
+  std::size_t operator()(absl::monostate) const { return 0; }
+};
+
+template <class... T>  // NOLINT
+struct hash<absl::variant<T...>>
+    : absl::variant_internal::VariantHashBase<absl::variant<T...>, void,
+                                              absl::remove_const_t<T>...> {};
+
+}  // namespace std
+
+#endif  // ABSL_USES_STD_VARIANT
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace variant_internal {
+
+// Helper visitor for converting a variant<Ts...>` into another type (mostly
+// variant) that can be constructed from any type.
+template <typename To>
+struct ConversionVisitor {
+  template <typename T>
+  To operator()(T&& v) const {
+    return To(std::forward<T>(v));
+  }
+};
+
+}  // namespace variant_internal
+
+// ConvertVariantTo()
+//
+// Helper functions to convert an `absl::variant` to a variant of another set of
+// types, provided that the alternative type of the new variant type can be
+// converted from any type in the source variant.
+//
+// Example:
+//
+//   absl::variant<name1, name2, float> InternalReq(const Req&);
+//
+//   // name1 and name2 are convertible to name
+//   absl::variant<name, float> ExternalReq(const Req& req) {
+//     return absl::ConvertVariantTo<absl::variant<name, float>>(
+//              InternalReq(req));
+//   }
+template <typename To, typename Variant>
+To ConvertVariantTo(Variant&& variant) {
+  return absl::visit(variant_internal::ConversionVisitor<To>{},
+                     std::forward<Variant>(variant));
+}
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_TYPES_VARIANT_H_
diff --git a/third_party/abseil-cpp/absl/utility/utility.h b/third_party/abseil-cpp/absl/utility/utility.h
new file mode 100644
index 0000000..a9e49ab
--- /dev/null
+++ b/third_party/abseil-cpp/absl/utility/utility.h
@@ -0,0 +1,350 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This header file contains C++11 versions of standard <utility> header
+// abstractions available within C++14 and C++17, and are designed to be drop-in
+// replacement for code compliant with C++14 and C++17.
+//
+// The following abstractions are defined:
+//
+//   * integer_sequence<T, Ints...>  == std::integer_sequence<T, Ints...>
+//   * index_sequence<Ints...>       == std::index_sequence<Ints...>
+//   * make_integer_sequence<T, N>   == std::make_integer_sequence<T, N>
+//   * make_index_sequence<N>        == std::make_index_sequence<N>
+//   * index_sequence_for<Ts...>     == std::index_sequence_for<Ts...>
+//   * apply<Functor, Tuple>         == std::apply<Functor, Tuple>
+//   * exchange<T>                   == std::exchange<T>
+//   * make_from_tuple<T>            == std::make_from_tuple<T>
+//
+// This header file also provides the tag types `in_place_t`, `in_place_type_t`,
+// and `in_place_index_t`, as well as the constant `in_place`, and
+// `constexpr` `std::move()` and `std::forward()` implementations in C++11.
+//
+// References:
+//
+//  https://en.cppreference.com/w/cpp/utility/integer_sequence
+//  https://en.cppreference.com/w/cpp/utility/apply
+//  http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html
+
+#ifndef ABSL_UTILITY_UTILITY_H_
+#define ABSL_UTILITY_UTILITY_H_
+
+#include <cstddef>
+#include <cstdlib>
+#include <tuple>
+#include <utility>
+
+#include "third_party/abseil-cpp/absl/base/config.h"
+#include "third_party/abseil-cpp/absl/base/internal/inline_variable.h"
+#include "third_party/abseil-cpp/absl/base/internal/invoke.h"
+#include "third_party/abseil-cpp/absl/meta/type_traits.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// integer_sequence
+//
+// Class template representing a compile-time integer sequence. An instantiation
+// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its
+// type through its template arguments (which is a common need when
+// working with C++11 variadic templates). `absl::integer_sequence` is designed
+// to be a drop-in replacement for C++14's `std::integer_sequence`.
+//
+// Example:
+//
+//   template< class T, T... Ints >
+//   void user_function(integer_sequence<T, Ints...>);
+//
+//   int main()
+//   {
+//     // user_function's `T` will be deduced to `int` and `Ints...`
+//     // will be deduced to `0, 1, 2, 3, 4`.
+//     user_function(make_integer_sequence<int, 5>());
+//   }
+template <typename T, T... Ints>
+struct integer_sequence {
+  using value_type = T;
+  static constexpr size_t size() noexcept { return sizeof...(Ints); }
+};
+
+// index_sequence
+//
+// A helper template for an `integer_sequence` of `size_t`,
+// `absl::index_sequence` is designed to be a drop-in replacement for C++14's
+// `std::index_sequence`.
+template <size_t... Ints>
+using index_sequence = integer_sequence<size_t, Ints...>;
+
+namespace utility_internal {
+
+template <typename Seq, size_t SeqSize, size_t Rem>
+struct Extend;
+
+// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency.
+template <typename T, T... Ints, size_t SeqSize>
+struct Extend<integer_sequence<T, Ints...>, SeqSize, 0> {
+  using type = integer_sequence<T, Ints..., (Ints + SeqSize)...>;
+};
+
+template <typename T, T... Ints, size_t SeqSize>
+struct Extend<integer_sequence<T, Ints...>, SeqSize, 1> {
+  using type = integer_sequence<T, Ints..., (Ints + SeqSize)..., 2 * SeqSize>;
+};
+
+// Recursion helper for 'make_integer_sequence<T, N>'.
+// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'.
+template <typename T, size_t N>
+struct Gen {
+  using type =
+      typename Extend<typename Gen<T, N / 2>::type, N / 2, N % 2>::type;
+};
+
+template <typename T>
+struct Gen<T, 0> {
+  using type = integer_sequence<T>;
+};
+
+template <typename T>
+struct InPlaceTypeTag {
+  explicit InPlaceTypeTag() = delete;
+  InPlaceTypeTag(const InPlaceTypeTag&) = delete;
+  InPlaceTypeTag& operator=(const InPlaceTypeTag&) = delete;
+};
+
+template <size_t I>
+struct InPlaceIndexTag {
+  explicit InPlaceIndexTag() = delete;
+  InPlaceIndexTag(const InPlaceIndexTag&) = delete;
+  InPlaceIndexTag& operator=(const InPlaceIndexTag&) = delete;
+};
+
+}  // namespace utility_internal
+
+// Compile-time sequences of integers
+
+// make_integer_sequence
+//
+// This template alias is equivalent to
+// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in
+// replacement for C++14's `std::make_integer_sequence`.
+template <typename T, T N>
+using make_integer_sequence = typename utility_internal::Gen<T, N>::type;
+
+// make_index_sequence
+//
+// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`,
+// and is designed to be a drop-in replacement for C++14's
+// `std::make_index_sequence`.
+template <size_t N>
+using make_index_sequence = make_integer_sequence<size_t, N>;
+
+// index_sequence_for
+//
+// Converts a typename pack into an index sequence of the same length, and
+// is designed to be a drop-in replacement for C++14's
+// `std::index_sequence_for()`
+template <typename... Ts>
+using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+
+// Tag types
+
+#ifdef ABSL_USES_STD_OPTIONAL
+
+using std::in_place_t;
+using std::in_place;
+
+#else  // ABSL_USES_STD_OPTIONAL
+
+// in_place_t
+//
+// Tag type used to specify in-place construction, such as with
+// `absl::optional`, designed to be a drop-in replacement for C++17's
+// `std::in_place_t`.
+struct in_place_t {};
+
+ABSL_INTERNAL_INLINE_CONSTEXPR(in_place_t, in_place, {});
+
+#endif  // ABSL_USES_STD_OPTIONAL
+
+#if defined(ABSL_USES_STD_ANY) || defined(ABSL_USES_STD_VARIANT)
+using std::in_place_type;
+using std::in_place_type_t;
+#else
+
+// in_place_type_t
+//
+// Tag type used for in-place construction when the type to construct needs to
+// be specified, such as with `absl::any`, designed to be a drop-in replacement
+// for C++17's `std::in_place_type_t`.
+template <typename T>
+using in_place_type_t = void (*)(utility_internal::InPlaceTypeTag<T>);
+
+template <typename T>
+void in_place_type(utility_internal::InPlaceTypeTag<T>) {}
+#endif  // ABSL_USES_STD_ANY || ABSL_USES_STD_VARIANT
+
+#ifdef ABSL_USES_STD_VARIANT
+using std::in_place_index;
+using std::in_place_index_t;
+#else
+
+// in_place_index_t
+//
+// Tag type used for in-place construction when the type to construct needs to
+// be specified, such as with `absl::any`, designed to be a drop-in replacement
+// for C++17's `std::in_place_index_t`.
+template <size_t I>
+using in_place_index_t = void (*)(utility_internal::InPlaceIndexTag<I>);
+
+template <size_t I>
+void in_place_index(utility_internal::InPlaceIndexTag<I>) {}
+#endif  // ABSL_USES_STD_VARIANT
+
+// Constexpr move and forward
+
+// move()
+//
+// A constexpr version of `std::move()`, designed to be a drop-in replacement
+// for C++14's `std::move()`.
+template <typename T>
+constexpr absl::remove_reference_t<T>&& move(T&& t) noexcept {
+  return static_cast<absl::remove_reference_t<T>&&>(t);
+}
+
+// forward()
+//
+// A constexpr version of `std::forward()`, designed to be a drop-in replacement
+// for C++14's `std::forward()`.
+template <typename T>
+constexpr T&& forward(
+    absl::remove_reference_t<T>& t) noexcept {  // NOLINT(runtime/references)
+  return static_cast<T&&>(t);
+}
+
+namespace utility_internal {
+// Helper method for expanding tuple into a called method.
+template <typename Functor, typename Tuple, std::size_t... Indexes>
+auto apply_helper(Functor&& functor, Tuple&& t, index_sequence<Indexes...>)
+    -> decltype(absl::base_internal::invoke(
+        absl::forward<Functor>(functor),
+        std::get<Indexes>(absl::forward<Tuple>(t))...)) {
+  return absl::base_internal::invoke(
+      absl::forward<Functor>(functor),
+      std::get<Indexes>(absl::forward<Tuple>(t))...);
+}
+
+}  // namespace utility_internal
+
+// apply
+//
+// Invokes a Callable using elements of a tuple as its arguments.
+// Each element of the tuple corresponds to an argument of the call (in order).
+// Both the Callable argument and the tuple argument are perfect-forwarded.
+// For member-function Callables, the first tuple element acts as the `this`
+// pointer. `absl::apply` is designed to be a drop-in replacement for C++17's
+// `std::apply`. Unlike C++17's `std::apply`, this is not currently `constexpr`.
+//
+// Example:
+//
+//   class Foo {
+//    public:
+//     void Bar(int);
+//   };
+//   void user_function1(int, std::string);
+//   void user_function2(std::unique_ptr<Foo>);
+//   auto user_lambda = [](int, int) {};
+//
+//   int main()
+//   {
+//       std::tuple<int, std::string> tuple1(42, "bar");
+//       // Invokes the first user function on int, std::string.
+//       absl::apply(&user_function1, tuple1);
+//
+//       std::tuple<std::unique_ptr<Foo>> tuple2(absl::make_unique<Foo>());
+//       // Invokes the user function that takes ownership of the unique
+//       // pointer.
+//       absl::apply(&user_function2, std::move(tuple2));
+//
+//       auto foo = absl::make_unique<Foo>();
+//       std::tuple<Foo*, int> tuple3(foo.get(), 42);
+//       // Invokes the method Bar on foo with one argument, 42.
+//       absl::apply(&Foo::Bar, tuple3);
+//
+//       std::tuple<int, int> tuple4(8, 9);
+//       // Invokes a lambda.
+//       absl::apply(user_lambda, tuple4);
+//   }
+template <typename Functor, typename Tuple>
+auto apply(Functor&& functor, Tuple&& t)
+    -> decltype(utility_internal::apply_helper(
+        absl::forward<Functor>(functor), absl::forward<Tuple>(t),
+        absl::make_index_sequence<std::tuple_size<
+            typename std::remove_reference<Tuple>::type>::value>{})) {
+  return utility_internal::apply_helper(
+      absl::forward<Functor>(functor), absl::forward<Tuple>(t),
+      absl::make_index_sequence<std::tuple_size<
+          typename std::remove_reference<Tuple>::type>::value>{});
+}
+
+// exchange
+//
+// Replaces the value of `obj` with `new_value` and returns the old value of
+// `obj`.  `absl::exchange` is designed to be a drop-in replacement for C++14's
+// `std::exchange`.
+//
+// Example:
+//
+//   Foo& operator=(Foo&& other) {
+//     ptr1_ = absl::exchange(other.ptr1_, nullptr);
+//     int1_ = absl::exchange(other.int1_, -1);
+//     return *this;
+//   }
+template <typename T, typename U = T>
+T exchange(T& obj, U&& new_value) {
+  T old_value = absl::move(obj);
+  obj = absl::forward<U>(new_value);
+  return old_value;
+}
+
+namespace utility_internal {
+template <typename T, typename Tuple, size_t... I>
+T make_from_tuple_impl(Tuple&& tup, absl::index_sequence<I...>) {
+  return T(std::get<I>(std::forward<Tuple>(tup))...);
+}
+}  // namespace utility_internal
+
+// make_from_tuple
+//
+// Given the template parameter type `T` and a tuple of arguments
+// `std::tuple(arg0, arg1, ..., argN)` constructs an object of type `T` as if by
+// calling `T(arg0, arg1, ..., argN)`.
+//
+// Example:
+//
+//   std::tuple<const char*, size_t> args("hello world", 5);
+//   auto s = absl::make_from_tuple<std::string>(args);
+//   assert(s == "hello");
+//
+template <typename T, typename Tuple>
+constexpr T make_from_tuple(Tuple&& tup) {
+  return utility_internal::make_from_tuple_impl<T>(
+      std::forward<Tuple>(tup),
+      absl::make_index_sequence<
+          std::tuple_size<absl::decay_t<Tuple>>::value>{});
+}
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_UTILITY_UTILITY_H_
diff --git a/third_party/agg23/0000-bug-466.patch b/third_party/agg23/0000-bug-466.patch
index e761198..5d62dcf 100644
--- a/third_party/agg23/0000-bug-466.patch
+++ b/third_party/agg23/0000-bug-466.patch
@@ -8,11 +8,11 @@
      out_vertices.add(coord_type(x + dx1, y + dy1));
 -    if(!ccw) {
 -        if(a1 > a2) {
--            a2 += 2 * FX_PI;
+-            a2 += 2 * FXSYS_PI;
 +    if (da > 0) {
 +      if (!ccw) {
 +        if (a1 > a2) {
-+          a2 += 2 * FX_PI;
++          a2 += 2 * FXSYS_PI;
          }
          a2 -= da / 4;
          a1 += da;
@@ -25,10 +25,10 @@
          }
 -    } else {
 -        if(a1 < a2) {
--            a2 -= 2 * FX_PI;
+-            a2 -= 2 * FXSYS_PI;
 +      } else {
 +        if (a1 < a2) {
-+          a2 -= 2 * FX_PI;
++          a2 -= 2 * FXSYS_PI;
          }
          a2 += da / 4;
          a1 -= da;
diff --git a/third_party/agg23/0008-namespace.patch b/third_party/agg23/0008-namespace.patch
new file mode 100644
index 0000000..d2566fe
--- /dev/null
+++ b/third_party/agg23/0008-namespace.patch
@@ -0,0 +1,508 @@
+diff --git a/third_party/agg23/agg_array.h b/third_party/agg23/agg_array.h
+index fba41a7eb..b82d95296 100644
+--- a/third_party/agg23/agg_array.h
++++ b/third_party/agg23/agg_array.h
+@@ -19,6 +19,8 @@
+ #include "agg_basics.h"
+ #include "core/fxcrt/fx_memory.h"  // For FXSYS_* macros.
+ 
++namespace pdfium
++{
+ namespace agg
+ {
+ template <class T>
+@@ -499,4 +501,5 @@ template<class T> inline void swap_elements(T& a, T& b)
+     b = temp;
+ }
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_basics.h b/third_party/agg23/agg_basics.h
+index 2a1c2af2f..eb6f35686 100644
+--- a/third_party/agg23/agg_basics.h
++++ b/third_party/agg23/agg_basics.h
+@@ -43,6 +43,8 @@
+ 
+ #include "core/fxcrt/fx_system.h"
+ 
++namespace pdfium
++{
+ namespace agg
+ {
+ typedef AGG_INT8   int8;
+@@ -274,4 +276,5 @@ struct vertex_type  {
+         x(x_), y(y_), cmd(cmd_) {}
+ };
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_clip_liang_barsky.h b/third_party/agg23/agg_clip_liang_barsky.h
+index 31b35fe96..7b865fd20 100644
+--- a/third_party/agg23/agg_clip_liang_barsky.h
++++ b/third_party/agg23/agg_clip_liang_barsky.h
+@@ -21,6 +21,8 @@
+ #define AGG_CLIP_LIANG_BARSKY_INCLUDED
+ #include "agg_basics.h"
+ #include "third_party/base/numerics/safe_math.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ template<class T>
+@@ -133,4 +135,5 @@ inline unsigned clip_liang_barsky(T x1, T y1, T x2, T y2,
+     return np;
+ }
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_color_gray.h b/third_party/agg23/agg_color_gray.h
+index 5db7bcaf2..c1b6eabd0 100644
+--- a/third_party/agg23/agg_color_gray.h
++++ b/third_party/agg23/agg_color_gray.h
+@@ -28,6 +28,8 @@
+ #ifndef AGG_COLOR_GRAY_INCLUDED
+ #define AGG_COLOR_GRAY_INCLUDED
+ #include "agg_basics.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ struct gray8 {
+@@ -47,4 +49,5 @@ struct gray8 {
+         v(int8u(v_)), a(int8u(a_)) {}
+ };
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_conv_adaptor_vcgen.h b/third_party/agg23/agg_conv_adaptor_vcgen.h
+index be4dc2d60..343c4e10b 100644
+--- a/third_party/agg23/agg_conv_adaptor_vcgen.h
++++ b/third_party/agg23/agg_conv_adaptor_vcgen.h
+@@ -16,6 +16,8 @@
+ #ifndef AGG_CONV_ADAPTOR_VCGEN_INCLUDED
+ #define AGG_CONV_ADAPTOR_VCGEN_INCLUDED
+ #include "agg_basics.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ struct null_markers  {
+@@ -135,4 +137,5 @@ unsigned conv_adaptor_vcgen<VertexSource, Generator, Markers>::vertex(float* x,
+     return cmd;
+ }
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_conv_dash.h b/third_party/agg23/agg_conv_dash.h
+index f87eccc3b..3a45d5563 100644
+--- a/third_party/agg23/agg_conv_dash.h
++++ b/third_party/agg23/agg_conv_dash.h
+@@ -22,6 +22,8 @@
+ #include "agg_basics.h"
+ #include "agg_vcgen_dash.h"
+ #include "agg_conv_adaptor_vcgen.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ template<class VertexSource, class Markers = null_markers>
+@@ -58,4 +60,5 @@ private:
+     operator = (const conv_dash<VertexSource, Markers>&);
+ };
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_conv_stroke.h b/third_party/agg23/agg_conv_stroke.h
+index 82268ddec..a65fe3e48 100644
+--- a/third_party/agg23/agg_conv_stroke.h
++++ b/third_party/agg23/agg_conv_stroke.h
+@@ -22,6 +22,8 @@
+ #include "agg_basics.h"
+ #include "agg_vcgen_stroke.h"
+ #include "agg_conv_adaptor_vcgen.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ template<class VertexSource, class Markers = null_markers>
+@@ -107,4 +109,5 @@ private:
+     operator = (const conv_stroke<VertexSource, Markers>&);
+ };
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_curves.cpp b/third_party/agg23/agg_curves.cpp
+index 41900c81f..be89752e0 100644
+--- a/third_party/agg23/agg_curves.cpp
++++ b/third_party/agg23/agg_curves.cpp
+@@ -22,6 +22,8 @@
+ #include "agg_curves.h"
+ #include "agg_math.h"
+ 
++namespace pdfium
++{
+ namespace agg
+ {
+ const float curve_collinearity_epsilon              = 1e-30f;
+@@ -107,3 +109,4 @@ void curve4_div::bezier(float x1, float y1,
+     m_points.add(point_type(x4, y4));
+ }
+ }
++}  // namespace pdfium
+diff --git a/third_party/agg23/agg_curves.h b/third_party/agg23/agg_curves.h
+index 488db4a1f..908bd9a5a 100644
+--- a/third_party/agg23/agg_curves.h
++++ b/third_party/agg23/agg_curves.h
+@@ -17,6 +17,8 @@
+ #ifndef AGG_CURVES_INCLUDED
+ #define AGG_CURVES_INCLUDED
+ #include "agg_array.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ struct curve4_points  {
+@@ -185,4 +187,5 @@ private:
+     curve4_div m_curve_div;
+ };
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_math.h b/third_party/agg23/agg_math.h
+index 6d5e39ac3..15617b2e8 100644
+--- a/third_party/agg23/agg_math.h
++++ b/third_party/agg23/agg_math.h
+@@ -19,6 +19,8 @@
+ #ifndef AGG_MATH_INCLUDED
+ #define AGG_MATH_INCLUDED
+ #include "agg_basics.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ const float intersection_epsilon = 1.0e-30f;
+@@ -60,4 +62,5 @@ AGG_INLINE bool calc_intersection(float ax, float ay, float bx, float by,
+     return true;
+ }
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_math_stroke.h b/third_party/agg23/agg_math_stroke.h
+index 82df8080c..8515d2b16 100644
+--- a/third_party/agg23/agg_math_stroke.h
++++ b/third_party/agg23/agg_math_stroke.h
+@@ -21,6 +21,8 @@
+ #define AGG_STROKE_MATH_INCLUDED
+ #include "agg_math.h"
+ #include "agg_vertex_sequence.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ enum line_cap_e {
+@@ -270,4 +272,5 @@ void stroke_calc_join(VertexConsumer& out_vertices,
+     }
+ }
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_path_storage.cpp b/third_party/agg23/agg_path_storage.cpp
+index 063ece542..2cd0caed1 100644
+--- a/third_party/agg23/agg_path_storage.cpp
++++ b/third_party/agg23/agg_path_storage.cpp
+@@ -28,6 +28,8 @@
+ #include "agg_math.h"
+ #include "core/fxcrt/fx_memory.h"
+ 
++namespace pdfium
++{
+ namespace agg
+ {
+ path_storage::~path_storage()
+@@ -95,3 +97,4 @@ void path_storage::end_poly()
+     }
+ }
+ }
++}  // namespace pdfium
+diff --git a/third_party/agg23/agg_path_storage.h b/third_party/agg23/agg_path_storage.h
+index 17e82d73e..55d6df001 100644
+--- a/third_party/agg23/agg_path_storage.h
++++ b/third_party/agg23/agg_path_storage.h
+@@ -16,6 +16,8 @@
+ #ifndef AGG_PATH_STORAGE_INCLUDED
+ #define AGG_PATH_STORAGE_INCLUDED
+ #include "agg_basics.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ class path_storage 
+@@ -169,4 +171,5 @@ inline void path_storage::line_to(float x, float y)
+     add_vertex(x, y, path_cmd_line_to);
+ }
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_pixfmt_gray.h b/third_party/agg23/agg_pixfmt_gray.h
+index 5a8093547..561bb7179 100644
+--- a/third_party/agg23/agg_pixfmt_gray.h
++++ b/third_party/agg23/agg_pixfmt_gray.h
+@@ -26,6 +26,8 @@
+ #include "agg_basics.h"
+ #include "agg_color_gray.h"
+ #include "agg_rendering_buffer.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ template<class ColorT> struct blender_gray  {
+@@ -174,4 +176,5 @@ private:
+ typedef blender_gray<gray8>      blender_gray8;
+ typedef pixel_formats_gray<blender_gray8, 1, 0> pixfmt_gray8;
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_rasterizer_scanline_aa.cpp b/third_party/agg23/agg_rasterizer_scanline_aa.cpp
+index 7850225d1..d2b6a46e4 100644
+--- a/third_party/agg23/agg_rasterizer_scanline_aa.cpp
++++ b/third_party/agg23/agg_rasterizer_scanline_aa.cpp
+@@ -49,6 +49,8 @@
+ #include <limits.h>
+ #include "agg_rasterizer_scanline_aa.h"
+ #include "third_party/base/numerics/safe_math.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ AGG_INLINE void cell_aa::set_cover(int c, int a)
+@@ -515,3 +517,4 @@ bool rasterizer_scanline_aa::safe_add(int* op1, int op2)
+     return true;
+ }
+ }
++}  // namespace pdfium
+diff --git a/third_party/agg23/agg_rasterizer_scanline_aa.h b/third_party/agg23/agg_rasterizer_scanline_aa.h
+index eade78333..133d66c4f 100644
+--- a/third_party/agg23/agg_rasterizer_scanline_aa.h
++++ b/third_party/agg23/agg_rasterizer_scanline_aa.h
+@@ -38,6 +38,8 @@
+ #include "core/fxcrt/fx_coordinates.h"
+ #include "core/fxcrt/fx_memory.h"
+ 
++namespace pdfium
++{
+ namespace agg
+ {
+ enum poly_base_scale_e {
+@@ -495,4 +497,5 @@ private:
+     int            m_cur_y;
+ };
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_render_scanlines.h b/third_party/agg23/agg_render_scanlines.h
+index 0dfd6d259..03ec683eb 100644
+--- a/third_party/agg23/agg_render_scanlines.h
++++ b/third_party/agg23/agg_render_scanlines.h
+@@ -16,6 +16,8 @@
+ #ifndef AGG_RENDER_SCANLINES_INCLUDED
+ #define AGG_RENDER_SCANLINES_INCLUDED
+ #include "agg_basics.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ template<class Rasterizer, class Scanline, class Renderer>
+@@ -47,4 +49,5 @@ void render_all_paths(Rasterizer& ras,
+     }
+ }
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_renderer_base.h b/third_party/agg23/agg_renderer_base.h
+index bd1b203b9..d637be262 100644
+--- a/third_party/agg23/agg_renderer_base.h
++++ b/third_party/agg23/agg_renderer_base.h
+@@ -21,6 +21,8 @@
+ #define AGG_RENDERER_BASE_INCLUDED
+ #include "agg_basics.h"
+ #include "agg_rendering_buffer.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ template<class PixelFormat> class renderer_base 
+@@ -160,4 +162,5 @@ private:
+     rect         m_clip_box;
+ };
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_renderer_scanline.h b/third_party/agg23/agg_renderer_scanline.h
+index 62d104f7f..32db738d3 100644
+--- a/third_party/agg23/agg_renderer_scanline.h
++++ b/third_party/agg23/agg_renderer_scanline.h
+@@ -18,6 +18,8 @@
+ #include "agg_basics.h"
+ #include "agg_renderer_base.h"
+ #include "agg_render_scanlines.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ template<class BaseRenderer, class SpanGenerator> class renderer_scanline_aa 
+@@ -90,4 +92,5 @@ private:
+     SpanGenerator* m_span_gen;
+ };
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_rendering_buffer.h b/third_party/agg23/agg_rendering_buffer.h
+index 9c1c0c689..4c80160b7 100644
+--- a/third_party/agg23/agg_rendering_buffer.h
++++ b/third_party/agg23/agg_rendering_buffer.h
+@@ -20,6 +20,8 @@
+ #ifndef AGG_RENDERING_BUFFER_INCLUDED
+ #define AGG_RENDERING_BUFFER_INCLUDED
+ #include "agg_basics.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ class rendering_buffer 
+@@ -142,4 +144,5 @@ private:
+     unsigned m_max_height;
+ };
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_scanline_u.h b/third_party/agg23/agg_scanline_u.h
+index 844dc9aad..1db31c3e3 100644
+--- a/third_party/agg23/agg_scanline_u.h
++++ b/third_party/agg23/agg_scanline_u.h
+@@ -24,6 +24,8 @@
+ #ifndef AGG_SCANLINE_U_INCLUDED
+ #define AGG_SCANLINE_U_INCLUDED
+ #include "agg_array.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ template<class CoverT> class scanline_u 
+@@ -147,4 +149,5 @@ private:
+ };
+ typedef scanline_u<int8u> scanline_u8;
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_shorten_path.h b/third_party/agg23/agg_shorten_path.h
+index 2f62ec52d..280d1688e 100644
+--- a/third_party/agg23/agg_shorten_path.h
++++ b/third_party/agg23/agg_shorten_path.h
+@@ -17,6 +17,8 @@
+ #define AGG_SHORTEN_PATH_INCLUDED
+ #include "agg_basics.h"
+ #include "agg_vertex_sequence.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ template<class VertexSequence>
+@@ -54,4 +56,5 @@ void shorten_path(VertexSequence& vs, float s, unsigned closed = 0)
+     }
+ }
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_vcgen_dash.cpp b/third_party/agg23/agg_vcgen_dash.cpp
+index fdbd2aa7f..f690760b0 100644
+--- a/third_party/agg23/agg_vcgen_dash.cpp
++++ b/third_party/agg23/agg_vcgen_dash.cpp
+@@ -21,6 +21,8 @@
+ #include "agg_shorten_path.h"
+ #include "agg_vcgen_dash.h"
+ 
++namespace pdfium
++{
+ namespace agg
+ {
+ vcgen_dash::vcgen_dash() :
+@@ -175,3 +177,4 @@ unsigned vcgen_dash::vertex(float* x, float* y)
+     return path_cmd_stop;
+ }
+ }
++}  // namespace pdfium
+diff --git a/third_party/agg23/agg_vcgen_dash.h b/third_party/agg23/agg_vcgen_dash.h
+index 7702fa7ad..2a4c94eab 100644
+--- a/third_party/agg23/agg_vcgen_dash.h
++++ b/third_party/agg23/agg_vcgen_dash.h
+@@ -21,6 +21,8 @@
+ #define AGG_VCGEN_DASH_INCLUDED
+ #include "agg_basics.h"
+ #include "agg_vertex_sequence.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ class vcgen_dash 
+@@ -72,4 +74,5 @@ private:
+     unsigned       m_src_vertex;
+ };
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_vcgen_stroke.cpp b/third_party/agg23/agg_vcgen_stroke.cpp
+index 3e97a3147..f65eac55f 100644
+--- a/third_party/agg23/agg_vcgen_stroke.cpp
++++ b/third_party/agg23/agg_vcgen_stroke.cpp
+@@ -25,6 +25,8 @@
+ 
+ #include "agg_vcgen_stroke.h"
+ 
++namespace pdfium
++{
+ namespace agg
+ {
+ 
+@@ -212,3 +214,4 @@ unsigned vcgen_stroke::vertex(float* x, float* y)
+     return cmd;
+ }
+ }
++}  // namespace pdfium
+diff --git a/third_party/agg23/agg_vcgen_stroke.h b/third_party/agg23/agg_vcgen_stroke.h
+index 23142d37f..ed9bb416f 100644
+--- a/third_party/agg23/agg_vcgen_stroke.h
++++ b/third_party/agg23/agg_vcgen_stroke.h
+@@ -16,6 +16,8 @@
+ #ifndef AGG_VCGEN_STROKE_INCLUDED
+ #define AGG_VCGEN_STROKE_INCLUDED
+ #include "agg_math_stroke.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ class vcgen_stroke 
+@@ -117,4 +119,5 @@ private:
+     unsigned       m_out_vertex;
+ };
+ }
++}  // namespace pdfium
+ #endif
+diff --git a/third_party/agg23/agg_vertex_sequence.h b/third_party/agg23/agg_vertex_sequence.h
+index 80eabbb95..dc729d55c 100644
+--- a/third_party/agg23/agg_vertex_sequence.h
++++ b/third_party/agg23/agg_vertex_sequence.h
+@@ -22,6 +22,8 @@
+ #include "agg_basics.h"
+ #include "agg_array.h"
+ #include "agg_math.h"
++namespace pdfium
++{
+ namespace agg
+ {
+ template<class T, unsigned S = 6>
+@@ -97,4 +99,5 @@ struct vertex_dist_cmd : public vertex_dist {
+     }
+ };
+ }
++}  // namespace pdfium
+ #endif
diff --git a/third_party/agg23/0009-infinite-loop.patch b/third_party/agg23/0009-infinite-loop.patch
new file mode 100644
index 0000000..ffa540f
--- /dev/null
+++ b/third_party/agg23/0009-infinite-loop.patch
@@ -0,0 +1,25 @@
+diff --git a/third_party/agg23/agg_math_stroke.h b/third_party/agg23/agg_math_stroke.h
+index 8515d2b16..70d1f2cf9 100644
+--- a/third_party/agg23/agg_math_stroke.h
++++ b/third_party/agg23/agg_math_stroke.h
+@@ -173,6 +173,9 @@ void stroke_calc_cap(VertexConsumer& out_vertices,
+       float a1 = atan2(dy1, -dx1);
+       float a2 = a1 + FXSYS_PI;
+       float da = acos(width / (width + ((1.0f / 8) / approximation_scale))) * 2;
++      if (da < stroke_theta) {
++        da = stroke_theta;
++      }
+       out_vertices.add(coord_type(v0.x - dx1, v0.y + dy1));
+       a1 += da;
+       a2 -= da / 4;
+@@ -180,8 +183,8 @@ void stroke_calc_cap(VertexConsumer& out_vertices,
+         out_vertices.add(
+             coord_type(v0.x + (width * cos(a1)), v0.y + (width * sin(a1))));
+         a1 += da;
+-        }
+-        out_vertices.add(coord_type(v0.x + dx1, v0.y - dy1));
++      }
++      out_vertices.add(coord_type(v0.x + dx1, v0.y - dy1));
+     }
+ }
+ template<class VertexConsumer>
diff --git a/third_party/agg23/0010-math.patch b/third_party/agg23/0010-math.patch
new file mode 100644
index 0000000..ae5d6a3
--- /dev/null
+++ b/third_party/agg23/0010-math.patch
@@ -0,0 +1,12 @@
+diff --git a/third_party/agg23/agg_basics.h b/third_party/agg23/agg_basics.h
+index eb6f35686..e7583e308 100644
+--- a/third_party/agg23/agg_basics.h
++++ b/third_party/agg23/agg_basics.h
+@@ -41,6 +41,7 @@
+ #endif
+ #define AGG_INLINE inline
+ 
++#include <math.h>
+ #include "core/fxcrt/fx_system.h"
+ 
+ namespace pdfium
diff --git a/third_party/agg23/0011-path-storage-move-ctor.patch b/third_party/agg23/0011-path-storage-move-ctor.patch
new file mode 100644
index 0000000..c0fbee5
--- /dev/null
+++ b/third_party/agg23/0011-path-storage-move-ctor.patch
@@ -0,0 +1,67 @@
+diff --git a/third_party/agg23/agg_path_storage.cpp b/third_party/agg23/agg_path_storage.cpp
+index 2cd0caed1..1491e9e33 100644
+--- a/third_party/agg23/agg_path_storage.cpp
++++ b/third_party/agg23/agg_path_storage.cpp
+@@ -43,14 +43,20 @@ path_storage::~path_storage()
+         FX_Free(m_coord_blocks);
+     }
+ }
+-path_storage::path_storage() :
+-    m_total_vertices(0),
+-    m_total_blocks(0),
+-    m_max_blocks(0),
+-    m_coord_blocks(0),
+-    m_cmd_blocks(0),
+-    m_iterator(0)
+-{
++path_storage::path_storage() = default;
++path_storage::path_storage(path_storage&& other) {
++    m_total_vertices = other.m_total_vertices;
++    m_total_blocks = other.m_total_blocks;
++    m_max_blocks = other.m_max_blocks;
++    m_coord_blocks = other.m_coord_blocks;
++    m_cmd_blocks = other.m_cmd_blocks;
++    m_iterator = other.m_iterator;
++    other.m_total_vertices = 0;
++    other.m_total_blocks = 0;
++    other.m_max_blocks = 0;
++    other.m_coord_blocks = nullptr;
++    other.m_cmd_blocks = nullptr;
++    other.m_iterator = 0;
+ }
+ void path_storage::allocate_block(unsigned nb)
+ {
+diff --git a/third_party/agg23/agg_path_storage.h b/third_party/agg23/agg_path_storage.h
+index 55d6df001..8f10ff36d 100644
+--- a/third_party/agg23/agg_path_storage.h
++++ b/third_party/agg23/agg_path_storage.h
+@@ -50,6 +50,10 @@ public:
+     };
+     ~path_storage();
+     path_storage();
++    path_storage(path_storage&& other);
++    path_storage& operator=(path_storage&&) = delete;
++    path_storage(const path_storage&) = delete;
++    path_storage& operator=(const path_storage&) = delete;
+     unsigned last_vertex(float* x, float* y) const;
+     unsigned prev_vertex(float* x, float* y) const;
+     void move_to(float x, float y);
+@@ -116,12 +120,12 @@ private:
+     void allocate_block(unsigned nb);
+     unsigned char* storage_ptrs(float** xy_ptr);
+ private:
+-    unsigned        m_total_vertices;
+-    unsigned        m_total_blocks;
+-    unsigned        m_max_blocks;
+-    float**   m_coord_blocks;
+-    unsigned char** m_cmd_blocks;
+-    unsigned        m_iterator;
++    unsigned        m_total_vertices = 0;
++    unsigned        m_total_blocks = 0;
++    unsigned        m_max_blocks = 0;
++    float**         m_coord_blocks = nullptr;
++    unsigned char** m_cmd_blocks = nullptr;
++    unsigned        m_iterator = 0;
+ };
+ inline unsigned path_storage::vertex(float* x, float* y)
+ {
diff --git a/third_party/agg23/0012-infinite-loop.patch b/third_party/agg23/0012-infinite-loop.patch
new file mode 100644
index 0000000..ce1b690
--- /dev/null
+++ b/third_party/agg23/0012-infinite-loop.patch
@@ -0,0 +1,25 @@
+diff --git a/third_party/agg23/agg_vcgen_dash.cpp b/third_party/agg23/agg_vcgen_dash.cpp
+index f690760b0..d44fca178 100644
+--- a/third_party/agg23/agg_vcgen_dash.cpp
++++ b/third_party/agg23/agg_vcgen_dash.cpp
+@@ -18,8 +18,11 @@
+ //
+ //----------------------------------------------------------------------------
+ 
++#include <cmath>
++
+ #include "agg_shorten_path.h"
+ #include "agg_vcgen_dash.h"
++#include "third_party/base/check_op.h"
+ 
+ namespace pdfium
+ {
+@@ -60,6 +63,8 @@ void vcgen_dash::dash_start(float ds)
+ }
+ void vcgen_dash::calc_dash_start(float ds)
+ {
++    DCHECK_GT(m_total_dash_len, 0);
++    ds -= floor(ds / m_total_dash_len) * m_total_dash_len;
+     m_curr_dash = 0;
+     m_curr_dash_start = 0;
+     while(ds > 0) {
diff --git a/third_party/agg23/0013-cxx20.patch b/third_party/agg23/0013-cxx20.patch
new file mode 100644
index 0000000..1a77349
--- /dev/null
+++ b/third_party/agg23/0013-cxx20.patch
@@ -0,0 +1,56 @@
+diff --git a/third_party/agg23/agg_basics.h b/third_party/agg23/agg_basics.h
+index e7583e308..84313db5b 100644
+--- a/third_party/agg23/agg_basics.h
++++ b/third_party/agg23/agg_basics.h
+@@ -216,7 +216,7 @@ inline bool is_close(unsigned c)
+ {
+     c &= ~path_flags_jr;
+     return (c & ~(path_flags_cw | path_flags_ccw)) ==
+-           (path_cmd_end_poly | path_flags_close);
++           (unsigned{path_cmd_end_poly} | path_flags_close);
+ }
+ inline bool is_next_poly(unsigned c)
+ {
+diff --git a/third_party/agg23/agg_path_storage.cpp b/third_party/agg23/agg_path_storage.cpp
+index 1491e9e33..2981e9c0c 100644
+--- a/third_party/agg23/agg_path_storage.cpp
++++ b/third_party/agg23/agg_path_storage.cpp
+@@ -98,7 +98,7 @@ void path_storage::end_poly()
+ {
+     if(m_total_vertices) {
+         if(is_vertex(command(m_total_vertices - 1))) {
+-            add_vertex(0, 0, path_cmd_end_poly | path_flags_close);
++            add_vertex(0, 0, unsigned{path_cmd_end_poly} | path_flags_close);
+         }
+     }
+ }
+diff --git a/third_party/agg23/agg_rasterizer_scanline_aa.h b/third_party/agg23/agg_rasterizer_scanline_aa.h
+index 133d66c4f..dd0d00076 100644
+--- a/third_party/agg23/agg_rasterizer_scanline_aa.h
++++ b/third_party/agg23/agg_rasterizer_scanline_aa.h
+@@ -49,7 +49,7 @@ enum poly_base_scale_e {
+ };
+ inline int poly_coord(float c)
+ {
+-    return int(c * poly_base_size);
++    return int(c * float{poly_base_size});
+ }
+ struct cell_aa  {
+     int x;
+diff --git a/third_party/agg23/agg_vcgen_stroke.cpp b/third_party/agg23/agg_vcgen_stroke.cpp
+index f65eac55f..b0f8a50e2 100644
+--- a/third_party/agg23/agg_vcgen_stroke.cpp
++++ b/third_party/agg23/agg_vcgen_stroke.cpp
+@@ -202,10 +202,10 @@ unsigned vcgen_stroke::vertex(float* x, float* y)
+                 break;
+             case end_poly1:
+                 m_status = m_prev_status;
+-                return path_cmd_end_poly | path_flags_close | path_flags_ccw;
++                return unsigned{path_cmd_end_poly} | path_flags_close | path_flags_ccw;
+             case end_poly2:
+                 m_status = m_prev_status;
+-                return path_cmd_end_poly | path_flags_close | path_flags_cw;
++                return unsigned{path_cmd_end_poly} | path_flags_close | path_flags_cw;
+             case stop:
+                 cmd = path_cmd_stop;
+                 break;
diff --git a/third_party/agg23/0014-ubsan-render-line.patch b/third_party/agg23/0014-ubsan-render-line.patch
new file mode 100644
index 0000000..b766eb2
--- /dev/null
+++ b/third_party/agg23/0014-ubsan-render-line.patch
@@ -0,0 +1,35 @@
+diff --git a/third_party/agg23/agg_rasterizer_scanline_aa.cpp b/third_party/agg23/agg_rasterizer_scanline_aa.cpp
+index d2b6a46e4..2f19a1816 100644
+--- a/third_party/agg23/agg_rasterizer_scanline_aa.cpp
++++ b/third_party/agg23/agg_rasterizer_scanline_aa.cpp
+@@ -227,10 +227,27 @@ AGG_INLINE void outline_aa::render_hline(int ey, int x1, int y1, int x2, int y2)
+ void outline_aa::render_line(int x1, int y1, int x2, int y2)
+ {
+     enum dx_limit_e { dx_limit = 16384 << poly_base_shift };
+-    int dx = x2 - x1;
++    pdfium::base::CheckedNumeric<int> safe_dx = x2;
++    safe_dx -= x1;
++    if (!safe_dx.IsValid())
++        return;
++
++    int dx = safe_dx.ValueOrDie();
+     if(dx >= dx_limit || dx <= -dx_limit) {
+-        int cx = (x1 + x2) >> 1;
+-        int cy = (y1 + y2) >> 1;
++        pdfium::base::CheckedNumeric<int> safe_cx = x1;
++        safe_cx += x2;
++        safe_cx /= 2;
++        if (!safe_cx.IsValid())
++            return;
++
++        pdfium::base::CheckedNumeric<int> safe_cy = y1;
++        safe_cy += y2;
++        safe_cy /= 2;
++        if (!safe_cy.IsValid())
++            return;
++
++        int cx = safe_cx.ValueOrDie();
++        int cy = safe_cy.ValueOrDie();
+         render_line(x1, y1, cx, cy);
+         render_line(cx, cy, x2, y2);
+     }
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 0319903..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.
@@ -14,7 +15,7 @@
 Possibly more?
 0001-gcc-warning.patch: Fix a GCC warning about both enumeral and
 non-enumeral type in conditional.
-0002-ubsan-error-fixes.path: Fix UBSan errors for overflows.
+0002-ubsan-error-fixes.patch: Fix UBSan errors for overflows.
 0003-ubsan-render-line-error.patch: Fix UBSan overflow error in render_line.
 0004-ubsan-sweep-scanline-error.patch: Fix UBSan left shift of negative value
 error in sweep_scanline.
@@ -24,3 +25,12 @@
 sweep_scanline.
 0007-unused-struct.patch: Remove unused struct point_type_flag, which has a
 shadow variable.
+0008-namespace.patch: Wrap all AGG code in namespace pdfium.
+0009-infinite-loop.patch: avoid hang in agg_math_stroke.h
+0010-math.patch: includes <math.h>
+0011-path-storage-move-ctor.patch: Add a move ctor for path_storage.
+0012-infinite-loop.patch: Fix an infinite loop in calc_dash_start().
+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 8dcb0af..ed22c40 100644
--- a/third_party/agg23/agg_array.h
+++ b/third_party/agg23/agg_array.h
@@ -16,9 +16,13 @@
 #ifndef AGG_ARRAY_INCLUDED
 #define AGG_ARRAY_INCLUDED
 
+#include <string.h>
+
 #include "agg_basics.h"
 #include "core/fxcrt/fx_memory.h"  // For FXSYS_* macros.
 
+namespace pdfium
+{
 namespace agg
 {
 template <class T>
@@ -126,7 +130,7 @@
 {
     if(new_size > m_size) {
         if(new_size > m_capacity) {
-            T* data = FX_Alloc(T, new_size);
+            T* data = FX_AllocUninit(T, new_size);
             memcpy(data, m_array, m_size * sizeof(T));
             FX_Free(m_array);
             m_array = data;
@@ -318,7 +322,7 @@
 {
     unsigned i;
     for(i = 0; i < v.m_num_blocks; ++i) {
-        m_blocks[i] = FX_Alloc(T, block_size);
+        m_blocks[i] = FX_AllocUninit(T, block_size);
         memcpy(m_blocks[i], v.m_blocks[i], block_size * sizeof(T));
     }
 }
@@ -499,4 +503,5 @@
     b = temp;
 }
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_basics.h b/third_party/agg23/agg_basics.h
index 2a1c2af..84313db 100644
--- a/third_party/agg23/agg_basics.h
+++ b/third_party/agg23/agg_basics.h
@@ -41,8 +41,11 @@
 #endif
 #define AGG_INLINE inline
 
+#include <math.h>
 #include "core/fxcrt/fx_system.h"
 
+namespace pdfium
+{
 namespace agg
 {
 typedef AGG_INT8   int8;
@@ -213,7 +216,7 @@
 {
     c &= ~path_flags_jr;
     return (c & ~(path_flags_cw | path_flags_ccw)) ==
-           (path_cmd_end_poly | path_flags_close);
+           (unsigned{path_cmd_end_poly} | path_flags_close);
 }
 inline bool is_next_poly(unsigned c)
 {
@@ -274,4 +277,5 @@
         x(x_), y(y_), cmd(cmd_) {}
 };
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_clip_liang_barsky.h b/third_party/agg23/agg_clip_liang_barsky.h
index 31b35fe..7b865fd 100644
--- a/third_party/agg23/agg_clip_liang_barsky.h
+++ b/third_party/agg23/agg_clip_liang_barsky.h
@@ -21,6 +21,8 @@
 #define AGG_CLIP_LIANG_BARSKY_INCLUDED
 #include "agg_basics.h"
 #include "third_party/base/numerics/safe_math.h"
+namespace pdfium
+{
 namespace agg
 {
 template<class T>
@@ -133,4 +135,5 @@
     return np;
 }
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_color_gray.h b/third_party/agg23/agg_color_gray.h
index 5db7bca..c1b6eab 100644
--- a/third_party/agg23/agg_color_gray.h
+++ b/third_party/agg23/agg_color_gray.h
@@ -28,6 +28,8 @@
 #ifndef AGG_COLOR_GRAY_INCLUDED
 #define AGG_COLOR_GRAY_INCLUDED
 #include "agg_basics.h"
+namespace pdfium
+{
 namespace agg
 {
 struct gray8 {
@@ -47,4 +49,5 @@
         v(int8u(v_)), a(int8u(a_)) {}
 };
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_conv_adaptor_vcgen.h b/third_party/agg23/agg_conv_adaptor_vcgen.h
index be4dc2d..343c4e1 100644
--- a/third_party/agg23/agg_conv_adaptor_vcgen.h
+++ b/third_party/agg23/agg_conv_adaptor_vcgen.h
@@ -16,6 +16,8 @@
 #ifndef AGG_CONV_ADAPTOR_VCGEN_INCLUDED
 #define AGG_CONV_ADAPTOR_VCGEN_INCLUDED
 #include "agg_basics.h"
+namespace pdfium
+{
 namespace agg
 {
 struct null_markers  {
@@ -135,4 +137,5 @@
     return cmd;
 }
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_conv_dash.h b/third_party/agg23/agg_conv_dash.h
index f87eccc..3a45d55 100644
--- a/third_party/agg23/agg_conv_dash.h
+++ b/third_party/agg23/agg_conv_dash.h
@@ -22,6 +22,8 @@
 #include "agg_basics.h"
 #include "agg_vcgen_dash.h"
 #include "agg_conv_adaptor_vcgen.h"
+namespace pdfium
+{
 namespace agg
 {
 template<class VertexSource, class Markers = null_markers>
@@ -58,4 +60,5 @@
     operator = (const conv_dash<VertexSource, Markers>&);
 };
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_conv_stroke.h b/third_party/agg23/agg_conv_stroke.h
index 82268dd..a65fe3e 100644
--- a/third_party/agg23/agg_conv_stroke.h
+++ b/third_party/agg23/agg_conv_stroke.h
@@ -22,6 +22,8 @@
 #include "agg_basics.h"
 #include "agg_vcgen_stroke.h"
 #include "agg_conv_adaptor_vcgen.h"
+namespace pdfium
+{
 namespace agg
 {
 template<class VertexSource, class Markers = null_markers>
@@ -107,4 +109,5 @@
     operator = (const conv_stroke<VertexSource, Markers>&);
 };
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_curves.cpp b/third_party/agg23/agg_curves.cpp
index 41900c8..be89752 100644
--- a/third_party/agg23/agg_curves.cpp
+++ b/third_party/agg23/agg_curves.cpp
@@ -22,6 +22,8 @@
 #include "agg_curves.h"
 #include "agg_math.h"
 
+namespace pdfium
+{
 namespace agg
 {
 const float curve_collinearity_epsilon              = 1e-30f;
@@ -107,3 +109,4 @@
     m_points.add(point_type(x4, y4));
 }
 }
+}  // namespace pdfium
diff --git a/third_party/agg23/agg_curves.h b/third_party/agg23/agg_curves.h
index 488db4a..205128a 100644
--- a/third_party/agg23/agg_curves.h
+++ b/third_party/agg23/agg_curves.h
@@ -17,6 +17,8 @@
 #ifndef AGG_CURVES_INCLUDED
 #define AGG_CURVES_INCLUDED
 #include "agg_array.h"
+namespace pdfium
+{
 namespace agg
 {
 struct curve4_points  {
@@ -105,21 +107,6 @@
         *y = p.y;
         return (m_count == 1) ? path_cmd_move_to : path_cmd_line_to;
     }
-    unsigned vertex_flag(float* x, float* y, int& flag)
-    {
-        if(m_count >= m_points.size()) {
-            return path_cmd_stop;
-        }
-        const point_type& p = m_points[m_count++];
-        *x = p.x;
-        *y = p.y;
-        flag = p.flag;
-        return (m_count == 1) ? path_cmd_move_to : path_cmd_line_to;
-    }
-    int count()
-    {
-        return m_points.size();
-    }
 private:
     void bezier(float x1, float y1,
                 float x2, float y2,
@@ -173,16 +160,9 @@
     {
         return m_curve_div.vertex(x, y);
     }
-    unsigned vertex_curve_flag(float* x, float* y, int& flag)
-    {
-        return m_curve_div.vertex_flag(x, y, flag);
-    }
-    int count()
-    {
-        return m_curve_div.count();
-    }
 private:
     curve4_div m_curve_div;
 };
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_math.h b/third_party/agg23/agg_math.h
index 6d5e39a..15617b2 100644
--- a/third_party/agg23/agg_math.h
+++ b/third_party/agg23/agg_math.h
@@ -19,6 +19,8 @@
 #ifndef AGG_MATH_INCLUDED
 #define AGG_MATH_INCLUDED
 #include "agg_basics.h"
+namespace pdfium
+{
 namespace agg
 {
 const float intersection_epsilon = 1.0e-30f;
@@ -60,4 +62,5 @@
     return true;
 }
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_math_stroke.h b/third_party/agg23/agg_math_stroke.h
index 82df808..e1d5fda 100644
--- a/third_party/agg23/agg_math_stroke.h
+++ b/third_party/agg23/agg_math_stroke.h
@@ -21,6 +21,8 @@
 #define AGG_STROKE_MATH_INCLUDED
 #include "agg_math.h"
 #include "agg_vertex_sequence.h"
+namespace pdfium
+{
 namespace agg
 {
 enum line_cap_e {
@@ -54,7 +56,7 @@
     float a1 = atan2(dy1, dx1);
     float a2 = atan2(dy2, dx2);
     float da = a1 - a2;
-    bool ccw = da > 0 && da < FX_PI;
+    bool ccw = da > 0 && da < FXSYS_PI;
     if(width < 0) {
         width = -width;
     }
@@ -63,7 +65,7 @@
     if (da > 0) {
       if (!ccw) {
         if (a1 > a2) {
-          a2 += 2 * FX_PI;
+          a2 += 2 * FXSYS_PI;
         }
         a2 -= da / 4;
         a1 += da;
@@ -74,7 +76,7 @@
         }
       } else {
         if (a1 < a2) {
-          a2 -= 2 * FX_PI;
+          a2 -= 2 * FXSYS_PI;
         }
         a2 += da / 4;
         a1 -= da;
@@ -169,8 +171,11 @@
         out_vertices.add(coord_type(v0.x + dx1 - dx2, v0.y - dy1 - dy2));
     } else {
       float a1 = atan2(dy1, -dx1);
-      float a2 = a1 + FX_PI;
+      float a2 = a1 + FXSYS_PI;
       float da = acos(width / (width + ((1.0f / 8) / approximation_scale))) * 2;
+      if (da < stroke_theta) {
+        da = stroke_theta;
+      }
       out_vertices.add(coord_type(v0.x - dx1, v0.y + dy1));
       a1 += da;
       a2 -= da / 4;
@@ -178,8 +183,8 @@
         out_vertices.add(
             coord_type(v0.x + (width * cos(a1)), v0.y + (width * sin(a1))));
         a1 += da;
-        }
-        out_vertices.add(coord_type(v0.x + dx1, v0.y - dy1));
+      }
+      out_vertices.add(coord_type(v0.x + dx1, v0.y - dy1));
     }
 }
 template<class VertexConsumer>
@@ -270,4 +275,5 @@
     }
 }
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_path_storage.cpp b/third_party/agg23/agg_path_storage.cpp
index 063ece5..d422512 100644
--- a/third_party/agg23/agg_path_storage.cpp
+++ b/third_party/agg23/agg_path_storage.cpp
@@ -25,9 +25,13 @@
 
 #include "agg_path_storage.h"
 
+#include <string.h>
+
 #include "agg_math.h"
 #include "core/fxcrt/fx_memory.h"
 
+namespace pdfium
+{
 namespace agg
 {
 path_storage::~path_storage()
@@ -41,14 +45,20 @@
         FX_Free(m_coord_blocks);
     }
 }
-path_storage::path_storage() :
-    m_total_vertices(0),
-    m_total_blocks(0),
-    m_max_blocks(0),
-    m_coord_blocks(0),
-    m_cmd_blocks(0),
-    m_iterator(0)
-{
+path_storage::path_storage() = default;
+path_storage::path_storage(path_storage&& other) {
+    m_total_vertices = other.m_total_vertices;
+    m_total_blocks = other.m_total_blocks;
+    m_max_blocks = other.m_max_blocks;
+    m_coord_blocks = other.m_coord_blocks;
+    m_cmd_blocks = other.m_cmd_blocks;
+    m_iterator = other.m_iterator;
+    other.m_total_vertices = 0;
+    other.m_total_blocks = 0;
+    other.m_max_blocks = 0;
+    other.m_coord_blocks = nullptr;
+    other.m_cmd_blocks = nullptr;
+    other.m_iterator = 0;
 }
 void path_storage::allocate_block(unsigned nb)
 {
@@ -90,8 +100,9 @@
 {
     if(m_total_vertices) {
         if(is_vertex(command(m_total_vertices - 1))) {
-            add_vertex(0, 0, path_cmd_end_poly | path_flags_close);
+            add_vertex(0, 0, unsigned{path_cmd_end_poly} | path_flags_close);
         }
     }
 }
 }
+}  // namespace pdfium
diff --git a/third_party/agg23/agg_path_storage.h b/third_party/agg23/agg_path_storage.h
index 17e82d7..8214806 100644
--- a/third_party/agg23/agg_path_storage.h
+++ b/third_party/agg23/agg_path_storage.h
@@ -16,6 +16,8 @@
 #ifndef AGG_PATH_STORAGE_INCLUDED
 #define AGG_PATH_STORAGE_INCLUDED
 #include "agg_basics.h"
+namespace pdfium
+{
 namespace agg
 {
 class path_storage 
@@ -48,6 +50,10 @@
     };
     ~path_storage();
     path_storage();
+    path_storage(path_storage&& other);
+    path_storage& operator=(path_storage&&) = delete;
+    path_storage(const path_storage&) = delete;
+    path_storage& operator=(const path_storage&) = delete;
     unsigned last_vertex(float* x, float* y) const;
     unsigned prev_vertex(float* x, float* y) const;
     void move_to(float x, float y);
@@ -70,22 +76,6 @@
             add_vertex(x, y, cmd);
         }
     }
-    template<class VertexSource>
-    void add_path_curve(VertexSource& vs,
-                        unsigned path_id = 0,
-                        bool solid_path = true)
-    {
-        float x, y;
-        unsigned cmd;
-        int flag;
-        vs.rewind(path_id);
-        while(!is_stop(cmd = vs.vertex_curve_flag(&x, &y, flag))) {
-            if(is_move_to(cmd) && solid_path && m_total_vertices) {
-                cmd = path_cmd_line_to | flag;
-            }
-            add_vertex(x, y, cmd | flag);
-        }
-    }
     unsigned total_vertices() const
     {
         return m_total_vertices;
@@ -114,12 +104,12 @@
     void allocate_block(unsigned nb);
     unsigned char* storage_ptrs(float** xy_ptr);
 private:
-    unsigned        m_total_vertices;
-    unsigned        m_total_blocks;
-    unsigned        m_max_blocks;
-    float**   m_coord_blocks;
-    unsigned char** m_cmd_blocks;
-    unsigned        m_iterator;
+    unsigned        m_total_vertices = 0;
+    unsigned        m_total_blocks = 0;
+    unsigned        m_max_blocks = 0;
+    float**         m_coord_blocks = nullptr;
+    unsigned char** m_cmd_blocks = nullptr;
+    unsigned        m_iterator = 0;
 };
 inline unsigned path_storage::vertex(float* x, float* y)
 {
@@ -169,4 +159,5 @@
     add_vertex(x, y, path_cmd_line_to);
 }
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_pixfmt_gray.h b/third_party/agg23/agg_pixfmt_gray.h
index 5a80935..561bb71 100644
--- a/third_party/agg23/agg_pixfmt_gray.h
+++ b/third_party/agg23/agg_pixfmt_gray.h
@@ -26,6 +26,8 @@
 #include "agg_basics.h"
 #include "agg_color_gray.h"
 #include "agg_rendering_buffer.h"
+namespace pdfium
+{
 namespace agg
 {
 template<class ColorT> struct blender_gray  {
@@ -174,4 +176,5 @@
 typedef blender_gray<gray8>      blender_gray8;
 typedef pixel_formats_gray<blender_gray8, 1, 0> pixfmt_gray8;
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_rasterizer_scanline_aa.cpp b/third_party/agg23/agg_rasterizer_scanline_aa.cpp
index 9254d83..392f302 100644
--- a/third_party/agg23/agg_rasterizer_scanline_aa.cpp
+++ b/third_party/agg23/agg_rasterizer_scanline_aa.cpp
@@ -49,6 +49,8 @@
 #include <limits.h>
 #include "agg_rasterizer_scanline_aa.h"
 #include "third_party/base/numerics/safe_math.h"
+namespace pdfium
+{
 namespace agg
 {
 AGG_INLINE void cell_aa::set_cover(int c, int a)
@@ -124,7 +126,7 @@
             m_cells = new_cells;
             m_max_blocks += cell_block_pool;
         }
-        m_cells[m_num_blocks++] = FX_Alloc(cell_aa, cell_block_size);
+        m_cells[m_num_blocks++] = FX_AllocUninit(cell_aa, cell_block_size);
     }
     m_cur_cell_ptr = m_cells[m_cur_block++];
 }
@@ -225,10 +227,27 @@
 void outline_aa::render_line(int x1, int y1, int x2, int y2)
 {
     enum dx_limit_e { dx_limit = 16384 << poly_base_shift };
-    int dx = x2 - x1;
+    pdfium::base::CheckedNumeric<int> safe_dx = x2;
+    safe_dx -= x1;
+    if (!safe_dx.IsValid())
+        return;
+
+    int dx = safe_dx.ValueOrDie();
     if(dx >= dx_limit || dx <= -dx_limit) {
-        int cx = (x1 + x2) >> 1;
-        int cy = (y1 + y2) >> 1;
+        pdfium::base::CheckedNumeric<int> safe_cx = x1;
+        safe_cx += x2;
+        safe_cx /= 2;
+        if (!safe_cx.IsValid())
+            return;
+
+        pdfium::base::CheckedNumeric<int> safe_cy = y1;
+        safe_cy += y2;
+        safe_cy /= 2;
+        if (!safe_cy.IsValid())
+            return;
+
+        int cx = safe_cx.ValueOrDie();
+        int cy = safe_cy.ValueOrDie();
         render_line(x1, y1, cx, cy);
         render_line(cx, cy, x2, y2);
     }
@@ -515,3 +534,4 @@
     return true;
 }
 }
+}  // namespace pdfium
diff --git a/third_party/agg23/agg_rasterizer_scanline_aa.h b/third_party/agg23/agg_rasterizer_scanline_aa.h
index eade783..dd0d000 100644
--- a/third_party/agg23/agg_rasterizer_scanline_aa.h
+++ b/third_party/agg23/agg_rasterizer_scanline_aa.h
@@ -38,6 +38,8 @@
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_memory.h"
 
+namespace pdfium
+{
 namespace agg
 {
 enum poly_base_scale_e {
@@ -47,7 +49,7 @@
 };
 inline int poly_coord(float c)
 {
-    return int(c * poly_base_size);
+    return int(c * float{poly_base_size});
 }
 struct cell_aa  {
     int x;
@@ -495,4 +497,5 @@
     int            m_cur_y;
 };
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_render_scanlines.h b/third_party/agg23/agg_render_scanlines.h
index 0dfd6d2..03ec683 100644
--- a/third_party/agg23/agg_render_scanlines.h
+++ b/third_party/agg23/agg_render_scanlines.h
@@ -16,6 +16,8 @@
 #ifndef AGG_RENDER_SCANLINES_INCLUDED
 #define AGG_RENDER_SCANLINES_INCLUDED
 #include "agg_basics.h"
+namespace pdfium
+{
 namespace agg
 {
 template<class Rasterizer, class Scanline, class Renderer>
@@ -47,4 +49,5 @@
     }
 }
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_renderer_base.h b/third_party/agg23/agg_renderer_base.h
index bd1b203..d637be2 100644
--- a/third_party/agg23/agg_renderer_base.h
+++ b/third_party/agg23/agg_renderer_base.h
@@ -21,6 +21,8 @@
 #define AGG_RENDERER_BASE_INCLUDED
 #include "agg_basics.h"
 #include "agg_rendering_buffer.h"
+namespace pdfium
+{
 namespace agg
 {
 template<class PixelFormat> class renderer_base 
@@ -160,4 +162,5 @@
     rect         m_clip_box;
 };
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_renderer_scanline.h b/third_party/agg23/agg_renderer_scanline.h
index 62d104f..32db738 100644
--- a/third_party/agg23/agg_renderer_scanline.h
+++ b/third_party/agg23/agg_renderer_scanline.h
@@ -18,6 +18,8 @@
 #include "agg_basics.h"
 #include "agg_renderer_base.h"
 #include "agg_render_scanlines.h"
+namespace pdfium
+{
 namespace agg
 {
 template<class BaseRenderer, class SpanGenerator> class renderer_scanline_aa 
@@ -90,4 +92,5 @@
     SpanGenerator* m_span_gen;
 };
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_rendering_buffer.h b/third_party/agg23/agg_rendering_buffer.h
index 9c1c0c6..4c80160 100644
--- a/third_party/agg23/agg_rendering_buffer.h
+++ b/third_party/agg23/agg_rendering_buffer.h
@@ -20,6 +20,8 @@
 #ifndef AGG_RENDERING_BUFFER_INCLUDED
 #define AGG_RENDERING_BUFFER_INCLUDED
 #include "agg_basics.h"
+namespace pdfium
+{
 namespace agg
 {
 class rendering_buffer 
@@ -142,4 +144,5 @@
     unsigned m_max_height;
 };
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_scanline_u.h b/third_party/agg23/agg_scanline_u.h
index 844dc9a..1db31c3 100644
--- a/third_party/agg23/agg_scanline_u.h
+++ b/third_party/agg23/agg_scanline_u.h
@@ -24,6 +24,8 @@
 #ifndef AGG_SCANLINE_U_INCLUDED
 #define AGG_SCANLINE_U_INCLUDED
 #include "agg_array.h"
+namespace pdfium
+{
 namespace agg
 {
 template<class CoverT> class scanline_u 
@@ -147,4 +149,5 @@
 };
 typedef scanline_u<int8u> scanline_u8;
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_shorten_path.h b/third_party/agg23/agg_shorten_path.h
index 2f62ec5..280d168 100644
--- a/third_party/agg23/agg_shorten_path.h
+++ b/third_party/agg23/agg_shorten_path.h
@@ -17,6 +17,8 @@
 #define AGG_SHORTEN_PATH_INCLUDED
 #include "agg_basics.h"
 #include "agg_vertex_sequence.h"
+namespace pdfium
+{
 namespace agg
 {
 template<class VertexSequence>
@@ -54,4 +56,5 @@
     }
 }
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_vcgen_dash.cpp b/third_party/agg23/agg_vcgen_dash.cpp
index fdbd2aa..d44fca1 100644
--- a/third_party/agg23/agg_vcgen_dash.cpp
+++ b/third_party/agg23/agg_vcgen_dash.cpp
@@ -18,9 +18,14 @@
 //
 //----------------------------------------------------------------------------
 
+#include <cmath>
+
 #include "agg_shorten_path.h"
 #include "agg_vcgen_dash.h"
+#include "third_party/base/check_op.h"
 
+namespace pdfium
+{
 namespace agg
 {
 vcgen_dash::vcgen_dash() :
@@ -58,6 +63,8 @@
 }
 void vcgen_dash::calc_dash_start(float ds)
 {
+    DCHECK_GT(m_total_dash_len, 0);
+    ds -= floor(ds / m_total_dash_len) * m_total_dash_len;
     m_curr_dash = 0;
     m_curr_dash_start = 0;
     while(ds > 0) {
@@ -175,3 +182,4 @@
     return path_cmd_stop;
 }
 }
+}  // namespace pdfium
diff --git a/third_party/agg23/agg_vcgen_dash.h b/third_party/agg23/agg_vcgen_dash.h
index 7702fa7..2a4c94e 100644
--- a/third_party/agg23/agg_vcgen_dash.h
+++ b/third_party/agg23/agg_vcgen_dash.h
@@ -21,6 +21,8 @@
 #define AGG_VCGEN_DASH_INCLUDED
 #include "agg_basics.h"
 #include "agg_vertex_sequence.h"
+namespace pdfium
+{
 namespace agg
 {
 class vcgen_dash 
@@ -72,4 +74,5 @@
     unsigned       m_src_vertex;
 };
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_vcgen_stroke.cpp b/third_party/agg23/agg_vcgen_stroke.cpp
index 3e97a31..b0f8a50 100644
--- a/third_party/agg23/agg_vcgen_stroke.cpp
+++ b/third_party/agg23/agg_vcgen_stroke.cpp
@@ -25,6 +25,8 @@
 
 #include "agg_vcgen_stroke.h"
 
+namespace pdfium
+{
 namespace agg
 {
 
@@ -200,10 +202,10 @@
                 break;
             case end_poly1:
                 m_status = m_prev_status;
-                return path_cmd_end_poly | path_flags_close | path_flags_ccw;
+                return unsigned{path_cmd_end_poly} | path_flags_close | path_flags_ccw;
             case end_poly2:
                 m_status = m_prev_status;
-                return path_cmd_end_poly | path_flags_close | path_flags_cw;
+                return unsigned{path_cmd_end_poly} | path_flags_close | path_flags_cw;
             case stop:
                 cmd = path_cmd_stop;
                 break;
@@ -212,3 +214,4 @@
     return cmd;
 }
 }
+}  // namespace pdfium
diff --git a/third_party/agg23/agg_vcgen_stroke.h b/third_party/agg23/agg_vcgen_stroke.h
index 23142d3..ed9bb41 100644
--- a/third_party/agg23/agg_vcgen_stroke.h
+++ b/third_party/agg23/agg_vcgen_stroke.h
@@ -16,6 +16,8 @@
 #ifndef AGG_VCGEN_STROKE_INCLUDED
 #define AGG_VCGEN_STROKE_INCLUDED
 #include "agg_math_stroke.h"
+namespace pdfium
+{
 namespace agg
 {
 class vcgen_stroke 
@@ -117,4 +119,5 @@
     unsigned       m_out_vertex;
 };
 }
+}  // namespace pdfium
 #endif
diff --git a/third_party/agg23/agg_vertex_sequence.h b/third_party/agg23/agg_vertex_sequence.h
index 80eabbb..dc729d5 100644
--- a/third_party/agg23/agg_vertex_sequence.h
+++ b/third_party/agg23/agg_vertex_sequence.h
@@ -22,6 +22,8 @@
 #include "agg_basics.h"
 #include "agg_array.h"
 #include "agg_math.h"
+namespace pdfium
+{
 namespace agg
 {
 template<class T, unsigned S = 6>
@@ -97,4 +99,5 @@
     }
 };
 }
+}  // namespace pdfium
 #endif
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/allocator/partition_allocator/address_space_randomization.cc b/third_party/base/allocator/partition_allocator/address_space_randomization.cc
deleted file mode 100644
index c6f268f..0000000
--- a/third_party/base/allocator/partition_allocator/address_space_randomization.cc
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2014 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.
-
-#include "third_party/base/allocator/partition_allocator/address_space_randomization.h"
-
-#include "build/build_config.h"
-#include "third_party/base/allocator/partition_allocator/page_allocator.h"
-#include "third_party/base/allocator/partition_allocator/random.h"
-#include "third_party/base/allocator/partition_allocator/spin_lock.h"
-#include "third_party/base/logging.h"
-
-#if defined(OS_WIN)
-#include <windows.h>  // Must be in front of other Windows header files.
-
-#include <VersionHelpers.h>
-#endif
-
-namespace pdfium {
-namespace base {
-
-void* GetRandomPageBase() {
-  uintptr_t random = static_cast<uintptr_t>(RandomValue());
-
-#if defined(ARCH_CPU_64_BITS)
-  random <<= 32ULL;
-  random |= static_cast<uintptr_t>(RandomValue());
-
-// The kASLRMask and kASLROffset constants will be suitable for the
-// OS and build configuration.
-#if defined(OS_WIN) && !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-  // Windows >= 8.1 has the full 47 bits. Use them where available.
-  static bool windows_81 = false;
-  static bool windows_81_initialized = false;
-  if (!windows_81_initialized) {
-    windows_81 = IsWindows8Point1OrGreater();
-    windows_81_initialized = true;
-  }
-  if (!windows_81) {
-    random &= internal::kASLRMaskBefore8_10;
-  } else {
-    random &= internal::kASLRMask;
-  }
-  random += internal::kASLROffset;
-#else
-  random &= internal::kASLRMask;
-  random += internal::kASLROffset;
-#endif  // defined(OS_WIN) && !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-#else   // defined(ARCH_CPU_32_BITS)
-#if defined(OS_WIN)
-  // On win32 host systems the randomization plus huge alignment causes
-  // excessive fragmentation. Plus most of these systems lack ASLR, so the
-  // randomization isn't buying anything. In that case we just skip it.
-  // TODO(palmer): Just dump the randomization when HE-ASLR is present.
-  static BOOL is_wow64 = -1;
-  if (is_wow64 == -1 && !IsWow64Process(GetCurrentProcess(), &is_wow64))
-    is_wow64 = FALSE;
-  if (!is_wow64)
-    return nullptr;
-#endif  // defined(OS_WIN)
-  random &= internal::kASLRMask;
-  random += internal::kASLROffset;
-#endif  // defined(ARCH_CPU_32_BITS)
-
-  DCHECK_EQ(0ULL, (random & kPageAllocationGranularityOffsetMask));
-  return reinterpret_cast<void*>(random);
-}
-
-}  // namespace base
-}  // namespace pdfium
diff --git a/third_party/base/allocator/partition_allocator/address_space_randomization.h b/third_party/base/allocator/partition_allocator/address_space_randomization.h
deleted file mode 100644
index 7ed7f6b..0000000
--- a/third_party/base/allocator/partition_allocator/address_space_randomization.h
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright 2014 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_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION_H_
-
-#include "build/build_config.h"
-#include "third_party/base/allocator/partition_allocator/page_allocator.h"
-#include "third_party/base/base_export.h"
-
-namespace pdfium {
-namespace base {
-
-// Calculates a random preferred mapping address. In calculating an address, we
-// balance good ASLR against not fragmenting the address space too badly.
-BASE_EXPORT void* GetRandomPageBase();
-
-namespace internal {
-
-constexpr uintptr_t AslrAddress(uintptr_t mask) {
-  return mask & kPageAllocationGranularityBaseMask;
-}
-constexpr uintptr_t AslrMask(uintptr_t bits) {
-  return AslrAddress((1ULL << bits) - 1ULL);
-}
-
-// Turn off formatting, because the thicket of nested ifdefs below is
-// incomprehensible without indentation. It is also incomprehensible with
-// indentation, but the only other option is a combinatorial explosion of
-// *_{win,linux,mac,foo}_{32,64}.h files.
-//
-// clang-format off
-
-#if defined(ARCH_CPU_64_BITS)
-
-  #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-
-    // We shouldn't allocate system pages at all for sanitizer builds. However,
-    // we do, and if random hint addresses interfere with address ranges
-    // hard-coded in those tools, bad things happen. This address range is
-    // copied from TSAN source but works with all tools. See
-    // https://crbug.com/539863.
-    constexpr uintptr_t kASLRMask = AslrAddress(0x007fffffffffULL);
-    constexpr uintptr_t kASLROffset = AslrAddress(0x7e8000000000ULL);
-
-  #elif defined(OS_WIN)
-
-    // Windows 8.10 and newer support the full 48 bit address range. Older
-    // versions of Windows only support 44 bits. Since kASLROffset is non-zero
-    // and may cause a carry, use 47 and 43 bit masks. See
-    // http://www.alex-ionescu.com/?p=246
-    constexpr uintptr_t kASLRMask = AslrMask(47);
-    constexpr uintptr_t kASLRMaskBefore8_10 = AslrMask(43);
-    // Try not to map pages into the range where Windows loads DLLs by default.
-    constexpr uintptr_t kASLROffset = 0x80000000ULL;
-
-  #elif defined(OS_MACOSX)
-
-    // macOS as of 10.12.5 does not clean up entries in page map levels 3/4
-    // [PDP/PML4] created from mmap or mach_vm_allocate, even after the region
-    // is destroyed. Using a virtual address space that is too large causes a
-    // leak of about 1 wired [can never be paged out] page per call to mmap. The
-    // page is only reclaimed when the process is killed. Confine the hint to a
-    // 39-bit section of the virtual address space.
-    //
-    // This implementation adapted from
-    // https://chromium-review.googlesource.com/c/v8/v8/+/557958. The difference
-    // is that here we clamp to 39 bits, not 32.
-    //
-    // TODO(crbug.com/738925): Remove this limitation if/when the macOS behavior
-    // changes.
-    constexpr uintptr_t kASLRMask = AslrMask(38);
-    constexpr uintptr_t kASLROffset = AslrAddress(0x1000000000ULL);
-
-  #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-
-    #if defined(ARCH_CPU_X86_64)
-
-      // Linux (and macOS) support the full 47-bit user space of x64 processors.
-      // Use only 46 to allow the kernel a chance to fulfill the request.
-      constexpr uintptr_t kASLRMask = AslrMask(46);
-      constexpr uintptr_t kASLROffset = AslrAddress(0);
-
-    #elif defined(ARCH_CPU_ARM64)
-
-      #if defined(OS_ANDROID)
-
-      // Restrict the address range on Android to avoid a large performance
-      // regression in single-process WebViews. See https://crbug.com/837640.
-      constexpr uintptr_t kASLRMask = AslrMask(30);
-      constexpr uintptr_t kASLROffset = AslrAddress(0x20000000ULL);
-
-      #else
-
-      // ARM64 on Linux has 39-bit user space. Use 38 bits since kASLROffset
-      // could cause a carry.
-      constexpr uintptr_t kASLRMask = AslrMask(38);
-      constexpr uintptr_t kASLROffset = AslrAddress(0x1000000000ULL);
-
-      #endif
-
-    #elif defined(ARCH_CPU_RISCV64)
-
-      // RISCV64 on Linux has 39-bit user space.
-      constexpr uintptr_t kASLRMask = AslrMask(38);
-      constexpr uintptr_t kASLROffset = AslrAddress(0x1000000000ULL);
-
-    #elif defined(ARCH_CPU_PPC64)
-
-      #if defined(OS_AIX)
-
-        // AIX has 64 bits of virtual addressing, but we limit the address range
-        // to (a) minimize segment lookaside buffer (SLB) misses; and (b) use
-        // extra address space to isolate the mmap regions.
-        constexpr uintptr_t kASLRMask = AslrMask(30);
-        constexpr uintptr_t kASLROffset = AslrAddress(0x400000000000ULL);
-
-      #elif defined(ARCH_CPU_BIG_ENDIAN)
-
-        // Big-endian Linux PPC has 44 bits of virtual addressing. Use 42.
-        constexpr uintptr_t kASLRMask = AslrMask(42);
-        constexpr uintptr_t kASLROffset = AslrAddress(0);
-
-      #else  // !defined(OS_AIX) && !defined(ARCH_CPU_BIG_ENDIAN)
-
-        // Little-endian Linux PPC has 48 bits of virtual addressing. Use 46.
-        constexpr uintptr_t kASLRMask = AslrMask(46);
-        constexpr uintptr_t kASLROffset = AslrAddress(0);
-
-      #endif  // !defined(OS_AIX) && !defined(ARCH_CPU_BIG_ENDIAN)
-
-    #elif defined(ARCH_CPU_S390X)
-
-      // Linux on Z uses bits 22 - 32 for Region Indexing, which translates to
-      // 42 bits of virtual addressing. Truncate to 40 bits to allow kernel a
-      // chance to fulfill the request.
-      constexpr uintptr_t kASLRMask = AslrMask(40);
-      constexpr uintptr_t kASLROffset = AslrAddress(0);
-
-    #elif defined(ARCH_CPU_S390)
-
-      // 31 bits of virtual addressing. Truncate to 29 bits to allow the kernel
-      // a chance to fulfill the request.
-      constexpr uintptr_t kASLRMask = AslrMask(29);
-      constexpr uintptr_t kASLROffset = AslrAddress(0);
-
-    #else  // !defined(ARCH_CPU_X86_64) && !defined(ARCH_CPU_PPC64) &&
-           // !defined(ARCH_CPU_S390X) && !defined(ARCH_CPU_S390)
-
-      // For all other POSIX variants, use 30 bits.
-      constexpr uintptr_t kASLRMask = AslrMask(30);
-
-      #if defined(OS_SOLARIS)
-
-        // For our Solaris/illumos mmap hint, we pick a random address in the
-        // bottom half of the top half of the address space (that is, the third
-        // quarter). Because we do not MAP_FIXED, this will be treated only as a
-        // hint -- the system will not fail to mmap because something else
-        // happens to already be mapped at our random address. We deliberately
-        // set the hint high enough to get well above the system's break (that
-        // is, the heap); Solaris and illumos will try the hint and if that
-        // fails allocate as if there were no hint at all. The high hint
-        // prevents the break from getting hemmed in at low values, ceding half
-        // of the address space to the system heap.
-        constexpr uintptr_t kASLROffset = AslrAddress(0x80000000ULL);
-
-      #elif defined(OS_AIX)
-
-        // The range 0x30000000 - 0xD0000000 is available on AIX; choose the
-        // upper range.
-        constexpr uintptr_t kASLROffset = AslrAddress(0x90000000ULL);
-
-      #else  // !defined(OS_SOLARIS) && !defined(OS_AIX)
-
-        // The range 0x20000000 - 0x60000000 is relatively unpopulated across a
-        // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macOS
-        // 10.6 and 10.7.
-        constexpr uintptr_t kASLROffset = AslrAddress(0x20000000ULL);
-
-      #endif  // !defined(OS_SOLARIS) && !defined(OS_AIX)
-
-    #endif  // !defined(ARCH_CPU_X86_64) && !defined(ARCH_CPU_PPC64) &&
-            // !defined(ARCH_CPU_S390X) && !defined(ARCH_CPU_S390)
-
-  #endif  // defined(OS_POSIX)
-
-#elif defined(ARCH_CPU_32_BITS)
-
-  // This is a good range on 32-bit Windows and Android (the only platforms on
-  // which we support 32-bitness). Allocates in the 0.5 - 1.5 GiB region. There
-  // is no issue with carries here.
-  constexpr uintptr_t kASLRMask = AslrMask(30);
-  constexpr uintptr_t kASLROffset = AslrAddress(0x20000000ULL);
-
-#else
-
-  #error Please tell us about your exotic hardware! Sounds interesting.
-
-#endif  // defined(ARCH_CPU_32_BITS)
-
-// clang-format on
-
-}  // namespace internal
-
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION_H_
diff --git a/third_party/base/allocator/partition_allocator/oom.h b/third_party/base/allocator/partition_allocator/oom.h
deleted file mode 100644
index bbd1ead..0000000
--- a/third_party/base/allocator/partition_allocator/oom.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (c) 2016 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_ALLOCATOR_PARTITION_ALLOCATOR_OOM_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_OOM_H_
-
-#include "third_party/base/allocator/partition_allocator/oom_callback.h"
-#include "third_party/base/logging.h"
-
-#if defined(OS_WIN)
-#include <windows.h>
-#endif
-
-// Do not want trivial entry points just calling OOM_CRASH() to be
-// commoned up by linker icf/comdat folding.
-#define OOM_CRASH_PREVENT_ICF()                  \
-  volatile int oom_crash_inhibit_icf = __LINE__; \
-  ALLOW_UNUSED_LOCAL(oom_crash_inhibit_icf)
-
-// OOM_CRASH() - Specialization of IMMEDIATE_CRASH which will raise a custom
-// exception on Windows to signal this is OOM and not a normal assert.
-#if defined(OS_WIN)
-#define OOM_CRASH()                                                     \
-  do {                                                                  \
-    OOM_CRASH_PREVENT_ICF();                                            \
-    base::internal::RunPartitionAllocOomCallback();                     \
-    ::RaiseException(0xE0000008, EXCEPTION_NONCONTINUABLE, 0, nullptr); \
-    IMMEDIATE_CRASH();                                                  \
-  } while (0)
-#else
-#define OOM_CRASH()                                 \
-  do {                                              \
-    base::internal::RunPartitionAllocOomCallback(); \
-    OOM_CRASH_PREVENT_ICF();                        \
-    IMMEDIATE_CRASH();                              \
-  } while (0)
-#endif
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_OOM_H_
diff --git a/third_party/base/allocator/partition_allocator/oom_callback.cc b/third_party/base/allocator/partition_allocator/oom_callback.cc
deleted file mode 100644
index 9143438..0000000
--- a/third_party/base/allocator/partition_allocator/oom_callback.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2018 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.
-
-#include "third_party/base/allocator/partition_allocator/oom_callback.h"
-
-#include "third_party/base/logging.h"
-
-namespace pdfium {
-namespace base {
-
-namespace {
-PartitionAllocOomCallback g_oom_callback;
-}  // namespace
-
-void SetPartitionAllocOomCallback(PartitionAllocOomCallback callback) {
-  DCHECK(!g_oom_callback);
-  g_oom_callback = callback;
-}
-
-namespace internal {
-void RunPartitionAllocOomCallback() {
-  if (g_oom_callback)
-    g_oom_callback();
-}
-}  // namespace internal
-
-}  // namespace base
-}  // namespace pdfium
diff --git a/third_party/base/allocator/partition_allocator/oom_callback.h b/third_party/base/allocator/partition_allocator/oom_callback.h
deleted file mode 100644
index 044b167..0000000
--- a/third_party/base/allocator/partition_allocator/oom_callback.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2018 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_ALLOCATOR_PARTITION_ALLOCATOR_OOM_CALLBACK_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_OOM_CALLBACK_H_
-
-#include "third_party/base/base_export.h"
-
-namespace pdfium {
-namespace base {
-typedef void (*PartitionAllocOomCallback)();
-// Registers a callback to be invoked during an OOM_CRASH(). OOM_CRASH is
-// invoked by users of PageAllocator (including PartitionAlloc) to signify an
-// allocation failure from the platform.
-BASE_EXPORT void SetPartitionAllocOomCallback(
-    PartitionAllocOomCallback callback);
-
-namespace internal {
-BASE_EXPORT void RunPartitionAllocOomCallback();
-}  // namespace internal
-
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_OOM_CALLBACK_H_
diff --git a/third_party/base/allocator/partition_allocator/page_allocator.cc b/third_party/base/allocator/partition_allocator/page_allocator.cc
deleted file mode 100644
index 14e9379..0000000
--- a/third_party/base/allocator/partition_allocator/page_allocator.cc
+++ /dev/null
@@ -1,257 +0,0 @@
-// Copyright (c) 2013 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.
-
-#include "third_party/base/allocator/partition_allocator/page_allocator.h"
-
-#include <limits.h>
-
-#include <atomic>
-
-#include "build/build_config.h"
-#include "third_party/base/allocator/partition_allocator/address_space_randomization.h"
-#include "third_party/base/allocator/partition_allocator/page_allocator_internal.h"
-#include "third_party/base/allocator/partition_allocator/spin_lock.h"
-#include "third_party/base/bits.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/numerics/safe_math.h"
-
-#if defined(OS_WIN)
-#include <windows.h>
-#endif
-
-#if defined(OS_WIN)
-#include "third_party/base/allocator/partition_allocator/page_allocator_internals_win.h"
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-#include "third_party/base/allocator/partition_allocator/page_allocator_internals_posix.h"
-#else
-#error Platform not supported.
-#endif
-
-namespace pdfium {
-namespace base {
-
-namespace {
-
-// We may reserve/release address space on different threads.
-subtle::SpinLock* GetReserveLock() {
-  static subtle::SpinLock* s_reserveLock = nullptr;
-  if (!s_reserveLock)
-    s_reserveLock = new subtle::SpinLock();
-  return s_reserveLock;
-}
-
-// We only support a single block of reserved address space.
-void* s_reservation_address = nullptr;
-size_t s_reservation_size = 0;
-
-void* AllocPagesIncludingReserved(void* address,
-                                  size_t length,
-                                  PageAccessibilityConfiguration accessibility,
-                                  PageTag page_tag,
-                                  bool commit) {
-  void* ret =
-      SystemAllocPages(address, length, accessibility, page_tag, commit);
-  if (ret == nullptr) {
-    const bool cant_alloc_length = kHintIsAdvisory || address == nullptr;
-    if (cant_alloc_length) {
-      // The system cannot allocate |length| bytes. Release any reserved address
-      // space and try once more.
-      ReleaseReservation();
-      ret = SystemAllocPages(address, length, accessibility, page_tag, commit);
-    }
-  }
-  return ret;
-}
-
-// Trims |base| to given |trim_length| and |alignment|.
-//
-// On failure, on Windows, this function returns nullptr and frees |base|.
-void* TrimMapping(void* base,
-                  size_t base_length,
-                  size_t trim_length,
-                  uintptr_t alignment,
-                  PageAccessibilityConfiguration accessibility,
-                  bool commit) {
-  size_t pre_slack = reinterpret_cast<uintptr_t>(base) & (alignment - 1);
-  if (pre_slack) {
-    pre_slack = alignment - pre_slack;
-  }
-  size_t post_slack = base_length - pre_slack - trim_length;
-  DCHECK(base_length >= trim_length || pre_slack || post_slack);
-  DCHECK(pre_slack < base_length);
-  DCHECK(post_slack < base_length);
-  return TrimMappingInternal(base, base_length, trim_length, accessibility,
-                             commit, pre_slack, post_slack);
-}
-
-}  // namespace
-
-void* SystemAllocPages(void* hint,
-                       size_t length,
-                       PageAccessibilityConfiguration accessibility,
-                       PageTag page_tag,
-                       bool commit) {
-  DCHECK(!(length & kPageAllocationGranularityOffsetMask));
-  DCHECK(!(reinterpret_cast<uintptr_t>(hint) &
-           kPageAllocationGranularityOffsetMask));
-  DCHECK(commit || accessibility == PageInaccessible);
-  return SystemAllocPagesInternal(hint, length, accessibility, page_tag,
-                                  commit);
-}
-
-void* AllocPages(void* address,
-                 size_t length,
-                 size_t align,
-                 PageAccessibilityConfiguration accessibility,
-                 PageTag page_tag,
-                 bool commit) {
-  DCHECK(length >= kPageAllocationGranularity);
-  DCHECK(!(length & kPageAllocationGranularityOffsetMask));
-  DCHECK(align >= kPageAllocationGranularity);
-  // Alignment must be power of 2 for masking math to work.
-  DCHECK(pdfium::base::bits::IsPowerOfTwo(align));
-  DCHECK(!(reinterpret_cast<uintptr_t>(address) &
-           kPageAllocationGranularityOffsetMask));
-  uintptr_t align_offset_mask = align - 1;
-  uintptr_t align_base_mask = ~align_offset_mask;
-  DCHECK(!(reinterpret_cast<uintptr_t>(address) & align_offset_mask));
-
-  // If the client passed null as the address, choose a good one.
-  if (address == nullptr) {
-    address = GetRandomPageBase();
-    address = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(address) &
-                                      align_base_mask);
-  }
-
-  // First try to force an exact-size, aligned allocation from our random base.
-#if defined(ARCH_CPU_32_BITS)
-  // On 32 bit systems, first try one random aligned address, and then try an
-  // aligned address derived from the value of |ret|.
-  constexpr int kExactSizeTries = 2;
-#else
-  // On 64 bit systems, try 3 random aligned addresses.
-  constexpr int kExactSizeTries = 3;
-#endif
-
-  for (int i = 0; i < kExactSizeTries; ++i) {
-    void* ret = AllocPagesIncludingReserved(address, length, accessibility,
-                                            page_tag, commit);
-    if (ret != nullptr) {
-      // If the alignment is to our liking, we're done.
-      if (!(reinterpret_cast<uintptr_t>(ret) & align_offset_mask))
-        return ret;
-      // Free the memory and try again.
-      FreePages(ret, length);
-    } else {
-      // |ret| is null; if this try was unhinted, we're OOM.
-      if (kHintIsAdvisory || address == nullptr)
-        return nullptr;
-    }
-
-#if defined(ARCH_CPU_32_BITS)
-    // For small address spaces, try the first aligned address >= |ret|. Note
-    // |ret| may be null, in which case |address| becomes null.
-    address = reinterpret_cast<void*>(
-        (reinterpret_cast<uintptr_t>(ret) + align_offset_mask) &
-        align_base_mask);
-#else  // defined(ARCH_CPU_64_BITS)
-    // Keep trying random addresses on systems that have a large address space.
-    address = GetRandomPageBase();
-    address = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(address) &
-                                      align_base_mask);
-#endif
-  }
-
-  // Make a larger allocation so we can force alignment.
-  size_t try_length = length + (align - kPageAllocationGranularity);
-  CHECK(try_length >= length);
-  void* ret;
-
-  do {
-    // Continue randomizing only on POSIX.
-    address = kHintIsAdvisory ? GetRandomPageBase() : nullptr;
-    ret = AllocPagesIncludingReserved(address, try_length, accessibility,
-                                      page_tag, commit);
-    // The retries are for Windows, where a race can steal our mapping on
-    // resize.
-  } while (ret != nullptr &&
-           (ret = TrimMapping(ret, try_length, length, align, accessibility,
-                              commit)) == nullptr);
-
-  return ret;
-}
-
-void FreePages(void* address, size_t length) {
-  DCHECK(!(reinterpret_cast<uintptr_t>(address) &
-           kPageAllocationGranularityOffsetMask));
-  DCHECK(!(length & kPageAllocationGranularityOffsetMask));
-  FreePagesInternal(address, length);
-}
-
-bool TrySetSystemPagesAccess(void* address,
-                             size_t length,
-                             PageAccessibilityConfiguration accessibility) {
-  DCHECK(!(length & kSystemPageOffsetMask));
-  return TrySetSystemPagesAccessInternal(address, length, accessibility);
-}
-
-void SetSystemPagesAccess(void* address,
-                          size_t length,
-                          PageAccessibilityConfiguration accessibility) {
-  DCHECK(!(length & kSystemPageOffsetMask));
-  SetSystemPagesAccessInternal(address, length, accessibility);
-}
-
-void DecommitSystemPages(void* address, size_t length) {
-  DCHECK_EQ(0UL, length & kSystemPageOffsetMask);
-  DecommitSystemPagesInternal(address, length);
-}
-
-bool RecommitSystemPages(void* address,
-                         size_t length,
-                         PageAccessibilityConfiguration accessibility) {
-  DCHECK_EQ(0UL, length & kSystemPageOffsetMask);
-  DCHECK(PageInaccessible != accessibility);
-  return RecommitSystemPagesInternal(address, length, accessibility);
-}
-
-void DiscardSystemPages(void* address, size_t length) {
-  DCHECK_EQ(0UL, length & kSystemPageOffsetMask);
-  DiscardSystemPagesInternal(address, length);
-}
-
-bool ReserveAddressSpace(size_t size) {
-  // To avoid deadlock, call only SystemAllocPages.
-  subtle::SpinLock::Guard guard(*GetReserveLock());
-  if (s_reservation_address == nullptr) {
-    void* mem = SystemAllocPages(nullptr, size, PageInaccessible,
-                                 PageTag::kChromium, false);
-    if (mem != nullptr) {
-      // We guarantee this alignment when reserving address space.
-      DCHECK(!(reinterpret_cast<uintptr_t>(mem) &
-               kPageAllocationGranularityOffsetMask));
-      s_reservation_address = mem;
-      s_reservation_size = size;
-      return true;
-    }
-  }
-  return false;
-}
-
-void ReleaseReservation() {
-  // To avoid deadlock, call only FreePages.
-  subtle::SpinLock::Guard guard(*GetReserveLock());
-  if (s_reservation_address != nullptr) {
-    FreePages(s_reservation_address, s_reservation_size);
-    s_reservation_address = nullptr;
-    s_reservation_size = 0;
-  }
-}
-
-uint32_t GetAllocPageErrorCode() {
-  return s_allocPageErrorCode;
-}
-
-}  // namespace base
-}  // namespace pdfium
diff --git a/third_party/base/allocator/partition_allocator/page_allocator.h b/third_party/base/allocator/partition_allocator/page_allocator.h
deleted file mode 100644
index 9b076f9..0000000
--- a/third_party/base/allocator/partition_allocator/page_allocator.h
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright (c) 2013 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_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H_
-
-#include <stdint.h>
-
-#include <cstddef>
-
-#include "build/build_config.h"
-#include "third_party/base/allocator/partition_allocator/page_allocator_constants.h"
-#include "third_party/base/base_export.h"
-#include "third_party/base/compiler_specific.h"
-
-namespace pdfium {
-namespace base {
-
-enum PageAccessibilityConfiguration {
-  PageInaccessible,
-  PageRead,
-  PageReadWrite,
-  PageReadExecute,
-  // This flag is deprecated and will go away soon.
-  // TODO(bbudge) Remove this as soon as V8 doesn't need RWX pages.
-  PageReadWriteExecute,
-};
-
-// macOS supports tagged memory regions, to help in debugging. On Android,
-// these tags are used to name anonymous mappings.
-enum class PageTag {
-  kFirst = 240,           // Minimum tag value.
-  kBlinkGC = 252,         // Blink GC pages.
-  kPartitionAlloc = 253,  // PartitionAlloc, no matter the partition.
-  kChromium = 254,        // Chromium page.
-  kV8 = 255,              // V8 heap pages.
-  kLast = kV8             // Maximum tag value.
-};
-
-// Allocate one or more pages.
-//
-// The requested |address| is just a hint; the actual address returned may
-// differ. The returned address will be aligned at least to |align| bytes.
-// |length| is in bytes, and must be a multiple of |kPageAllocationGranularity|.
-// |align| is in bytes, and must be a power-of-two multiple of
-// |kPageAllocationGranularity|.
-//
-// If |address| is null, then a suitable and randomized address will be chosen
-// automatically.
-//
-// |page_accessibility| controls the permission of the allocated pages.
-// |page_tag| is used on some platforms to identify the source of the
-// allocation. Use PageTag::kChromium as a catch-all category.
-//
-// This call will return null if the allocation cannot be satisfied.
-BASE_EXPORT void* AllocPages(void* address,
-                             size_t length,
-                             size_t align,
-                             PageAccessibilityConfiguration page_accessibility,
-                             PageTag tag,
-                             bool commit = true);
-
-// Free one or more pages starting at |address| and continuing for |length|
-// bytes.
-//
-// |address| and |length| must match a previous call to |AllocPages|. Therefore,
-// |address| must be aligned to |kPageAllocationGranularity| bytes, and |length|
-// must be a multiple of |kPageAllocationGranularity|.
-BASE_EXPORT void FreePages(void* address, size_t length);
-
-// Mark one or more system pages, starting at |address| with the given
-// |page_accessibility|. |length| must be a multiple of |kSystemPageSize| bytes.
-//
-// Returns true if the permission change succeeded. In most cases you must
-// |CHECK| the result.
-BASE_EXPORT WARN_UNUSED_RESULT bool TrySetSystemPagesAccess(
-    void* address,
-    size_t length,
-    PageAccessibilityConfiguration page_accessibility);
-
-// Mark one or more system pages, starting at |address| with the given
-// |page_accessibility|. |length| must be a multiple of |kSystemPageSize| bytes.
-//
-// Performs a CHECK that the operation succeeds.
-BASE_EXPORT void SetSystemPagesAccess(
-    void* address,
-    size_t length,
-    PageAccessibilityConfiguration page_accessibility);
-
-// Decommit one or more system pages starting at |address| and continuing for
-// |length| bytes. |length| must be a multiple of |kSystemPageSize|.
-//
-// Decommitted means that physical resources (RAM or swap) backing the allocated
-// virtual address range are released back to the system, but the address space
-// is still allocated to the process (possibly using up page table entries or
-// other accounting resources). Any access to a decommitted region of memory
-// is an error and will generate a fault.
-//
-// This operation is not atomic on all platforms.
-//
-// Note: "Committed memory" is a Windows Memory Subsystem concept that ensures
-// processes will not fault when touching a committed memory region. There is
-// no analogue in the POSIX memory API where virtual memory pages are
-// best-effort allocated resources on the first touch. To create a
-// platform-agnostic abstraction, this API simulates the Windows "decommit"
-// state by both discarding the region (allowing the OS to avoid swap
-// operations) and changing the page protections so accesses fault.
-//
-// TODO(ajwong): This currently does not change page protections on POSIX
-// systems due to a perf regression. Tracked at http://crbug.com/766882.
-BASE_EXPORT void DecommitSystemPages(void* address, size_t length);
-
-// Recommit one or more system pages, starting at |address| and continuing for
-// |length| bytes with the given |page_accessibility|. |length| must be a
-// multiple of |kSystemPageSize|.
-//
-// Decommitted system pages must be recommitted with their original permissions
-// before they are used again.
-//
-// Returns true if the recommit change succeeded. In most cases you must |CHECK|
-// the result.
-BASE_EXPORT WARN_UNUSED_RESULT bool RecommitSystemPages(
-    void* address,
-    size_t length,
-    PageAccessibilityConfiguration page_accessibility);
-
-// Discard one or more system pages starting at |address| and continuing for
-// |length| bytes. |length| must be a multiple of |kSystemPageSize|.
-//
-// Discarding is a hint to the system that the page is no longer required. The
-// hint may:
-//   - Do nothing.
-//   - Discard the page immediately, freeing up physical pages.
-//   - Discard the page at some time in the future in response to memory
-//   pressure.
-//
-// Only committed pages should be discarded. Discarding a page does not decommit
-// it, and it is valid to discard an already-discarded page. A read or write to
-// a discarded page will not fault.
-//
-// Reading from a discarded page may return the original page content, or a page
-// full of zeroes.
-//
-// Writing to a discarded page is the only guaranteed way to tell the system
-// that the page is required again. Once written to, the content of the page is
-// guaranteed stable once more. After being written to, the page content may be
-// based on the original page content, or a page of zeroes.
-BASE_EXPORT void DiscardSystemPages(void* address, size_t length);
-
-// Rounds up |address| to the next multiple of |kSystemPageSize|. Returns
-// 0 for an |address| of 0.
-constexpr ALWAYS_INLINE uintptr_t RoundUpToSystemPage(uintptr_t address) {
-  return (address + kSystemPageOffsetMask) & kSystemPageBaseMask;
-}
-
-// Rounds down |address| to the previous multiple of |kSystemPageSize|. Returns
-// 0 for an |address| of 0.
-constexpr ALWAYS_INLINE uintptr_t RoundDownToSystemPage(uintptr_t address) {
-  return address & kSystemPageBaseMask;
-}
-
-// Rounds up |address| to the next multiple of |kPageAllocationGranularity|.
-// Returns 0 for an |address| of 0.
-constexpr ALWAYS_INLINE uintptr_t
-RoundUpToPageAllocationGranularity(uintptr_t address) {
-  return (address + kPageAllocationGranularityOffsetMask) &
-         kPageAllocationGranularityBaseMask;
-}
-
-// Rounds down |address| to the previous multiple of
-// |kPageAllocationGranularity|. Returns 0 for an |address| of 0.
-constexpr ALWAYS_INLINE uintptr_t
-RoundDownToPageAllocationGranularity(uintptr_t address) {
-  return address & kPageAllocationGranularityBaseMask;
-}
-
-// Reserves (at least) |size| bytes of address space, aligned to
-// |kPageAllocationGranularity|. This can be called early on to make it more
-// likely that large allocations will succeed. Returns true if the reservation
-// succeeded, false if the reservation failed or a reservation was already made.
-BASE_EXPORT bool ReserveAddressSpace(size_t size);
-
-// Releases any reserved address space. |AllocPages| calls this automatically on
-// an allocation failure. External allocators may also call this on failure.
-BASE_EXPORT void ReleaseReservation();
-
-// Returns |errno| (POSIX) or the result of |GetLastError| (Windows) when |mmap|
-// (POSIX) or |VirtualAlloc| (Windows) fails.
-BASE_EXPORT uint32_t GetAllocPageErrorCode();
-
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H_
diff --git a/third_party/base/allocator/partition_allocator/page_allocator_constants.h b/third_party/base/allocator/partition_allocator/page_allocator_constants.h
deleted file mode 100644
index 567e3a3..0000000
--- a/third_party/base/allocator/partition_allocator/page_allocator_constants.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) 2018 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_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_CONSTANTS_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_CONSTANTS_H_
-
-#include <stddef.h>
-
-#include "build/build_config.h"
-
-namespace pdfium {
-namespace base {
-#if defined(OS_WIN) || defined(ARCH_CPU_PPC64)
-static constexpr size_t kPageAllocationGranularityShift = 16;  // 64KB
-#elif defined(_MIPS_ARCH_LOONGSON)
-static constexpr size_t kPageAllocationGranularityShift = 14;  // 16KB
-#else
-static constexpr size_t kPageAllocationGranularityShift = 12;  // 4KB
-#endif
-static constexpr size_t kPageAllocationGranularity =
-    1 << kPageAllocationGranularityShift;
-static constexpr size_t kPageAllocationGranularityOffsetMask =
-    kPageAllocationGranularity - 1;
-static constexpr size_t kPageAllocationGranularityBaseMask =
-    ~kPageAllocationGranularityOffsetMask;
-
-#if defined(_MIPS_ARCH_LOONGSON)
-static constexpr size_t kSystemPageSize = 16384;
-#elif defined(ARCH_CPU_PPC64)
-// Modern ppc64 systems support 4KB and 64KB page sizes.
-// Since 64KB is the de-facto standard on the platform
-// and binaries compiled for 64KB are likely to work on 4KB systems,
-// 64KB is a good choice here.
-static constexpr size_t kSystemPageSize = 65536;
-#else
-static constexpr size_t kSystemPageSize = 4096;
-#endif
-static constexpr size_t kSystemPageOffsetMask = kSystemPageSize - 1;
-static_assert((kSystemPageSize & (kSystemPageSize - 1)) == 0,
-              "kSystemPageSize must be power of 2");
-static constexpr size_t kSystemPageBaseMask = ~kSystemPageOffsetMask;
-
-static constexpr size_t kPageMetadataShift = 5;  // 32 bytes per partition page.
-static constexpr size_t kPageMetadataSize = 1 << kPageMetadataShift;
-
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_CONSTANTS_H_
diff --git a/third_party/base/allocator/partition_allocator/page_allocator_internal.h b/third_party/base/allocator/partition_allocator/page_allocator_internal.h
deleted file mode 100644
index 2284314..0000000
--- a/third_party/base/allocator/partition_allocator/page_allocator_internal.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) 2018 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_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNAL_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNAL_H_
-
-namespace pdfium {
-namespace base {
-
-void* SystemAllocPages(void* hint,
-                       size_t length,
-                       PageAccessibilityConfiguration accessibility,
-                       PageTag page_tag,
-                       bool commit);
-
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNAL_H_
diff --git a/third_party/base/allocator/partition_allocator/page_allocator_internals_posix.h b/third_party/base/allocator/partition_allocator/page_allocator_internals_posix.h
deleted file mode 100644
index 2d62cb8..0000000
--- a/third_party/base/allocator/partition_allocator/page_allocator_internals_posix.h
+++ /dev/null
@@ -1,216 +0,0 @@
-// Copyright (c) 2018 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_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_POSIX_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_POSIX_H_
-
-#include <errno.h>
-#include <sys/mman.h>
-
-#include "build/build_config.h"
-
-#if defined(OS_MACOSX)
-#include <mach/mach.h>
-#endif
-#if defined(OS_ANDROID)
-#include <sys/prctl.h>
-#endif
-#if defined(OS_LINUX)
-#include <sys/resource.h>
-
-#include <algorithm>
-#endif
-
-#include "third_party/base/allocator/partition_allocator/page_allocator.h"
-
-#ifndef MAP_ANONYMOUS
-#define MAP_ANONYMOUS MAP_ANON
-#endif
-
-namespace pdfium {
-namespace base {
-
-#if defined(OS_ANDROID)
-namespace {
-const char* PageTagToName(PageTag tag) {
-  // Important: All the names should be string literals. As per prctl.h in
-  // //third_party/android_ndk the kernel keeps a pointer to the name instead
-  // of copying it.
-  //
-  // Having the name in .rodata ensures that the pointer remains valid as
-  // long as the mapping is alive.
-  switch (tag) {
-    case PageTag::kBlinkGC:
-      return "blink_gc";
-    case PageTag::kPartitionAlloc:
-      return "partition_alloc";
-    case PageTag::kChromium:
-      return "chromium";
-    case PageTag::kV8:
-      return "v8";
-    default:
-      DCHECK(false);
-      return "";
-  }
-}
-}  // namespace
-#endif  // defined(OS_ANDROID)
-
-// |mmap| uses a nearby address if the hint address is blocked.
-constexpr bool kHintIsAdvisory = true;
-std::atomic<int32_t> s_allocPageErrorCode{0};
-
-int GetAccessFlags(PageAccessibilityConfiguration accessibility) {
-  switch (accessibility) {
-    case PageRead:
-      return PROT_READ;
-    case PageReadWrite:
-      return PROT_READ | PROT_WRITE;
-    case PageReadExecute:
-      return PROT_READ | PROT_EXEC;
-    case PageReadWriteExecute:
-      return PROT_READ | PROT_WRITE | PROT_EXEC;
-    default:
-      NOTREACHED();
-      FALLTHROUGH;
-    case PageInaccessible:
-      return PROT_NONE;
-  }
-}
-
-void* SystemAllocPagesInternal(void* hint,
-                               size_t length,
-                               PageAccessibilityConfiguration accessibility,
-                               PageTag page_tag,
-                               bool commit) {
-#if defined(OS_MACOSX)
-  // Use a custom tag to make it easier to distinguish Partition Alloc regions
-  // in vmmap(1). Tags between 240-255 are supported.
-  DCHECK(PageTag::kFirst <= page_tag);
-  DCHECK(PageTag::kLast >= page_tag);
-  int fd = VM_MAKE_TAG(static_cast<int>(page_tag));
-#else
-  int fd = -1;
-#endif
-
-  int access_flag = GetAccessFlags(accessibility);
-  int map_flags = MAP_ANONYMOUS | MAP_PRIVATE;
-
-  // TODO(https://crbug.com/927411): Remove once Fuchsia uses a native page
-  // allocator, rather than relying on POSIX compatibility.
-#if defined(OS_FUCHSIA)
-  if (page_tag == PageTag::kV8) {
-    map_flags |= MAP_JIT;
-  }
-#endif
-
-  void* ret = mmap(hint, length, access_flag, map_flags, fd, 0);
-  if (ret == MAP_FAILED) {
-    s_allocPageErrorCode = errno;
-    ret = nullptr;
-  }
-
-#if defined(OS_ANDROID)
-  // On Android, anonymous mappings can have a name attached to them. This is
-  // useful for debugging, and double-checking memory attribution.
-  if (ret) {
-    // No error checking on purpose, testing only.
-    prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ret, length,
-          PageTagToName(page_tag));
-  }
-#endif
-
-  return ret;
-}
-
-void* TrimMappingInternal(void* base,
-                          size_t base_length,
-                          size_t trim_length,
-                          PageAccessibilityConfiguration accessibility,
-                          bool commit,
-                          size_t pre_slack,
-                          size_t post_slack) {
-  void* ret = base;
-  // We can resize the allocation run. Release unneeded memory before and after
-  // the aligned range.
-  if (pre_slack) {
-    int res = munmap(base, pre_slack);
-    CHECK(!res);
-    ret = reinterpret_cast<char*>(base) + pre_slack;
-  }
-  if (post_slack) {
-    int res = munmap(reinterpret_cast<char*>(ret) + trim_length, post_slack);
-    CHECK(!res);
-  }
-  return ret;
-}
-
-bool TrySetSystemPagesAccessInternal(
-    void* address,
-    size_t length,
-    PageAccessibilityConfiguration accessibility) {
-  return 0 == mprotect(address, length, GetAccessFlags(accessibility));
-}
-
-void SetSystemPagesAccessInternal(
-    void* address,
-    size_t length,
-    PageAccessibilityConfiguration accessibility) {
-  CHECK_EQ(0, mprotect(address, length, GetAccessFlags(accessibility)));
-}
-
-void FreePagesInternal(void* address, size_t length) {
-  CHECK(!munmap(address, length));
-}
-
-void DecommitSystemPagesInternal(void* address, size_t length) {
-  // In POSIX, there is no decommit concept. Discarding is an effective way of
-  // implementing the Windows semantics where the OS is allowed to not swap the
-  // pages in the region.
-  //
-  // TODO(ajwong): Also explore setting PageInaccessible to make the protection
-  // semantics consistent between Windows and POSIX. This might have a perf cost
-  // though as both decommit and recommit would incur an extra syscall.
-  // http://crbug.com/766882
-  DiscardSystemPages(address, length);
-}
-
-bool RecommitSystemPagesInternal(void* address,
-                                 size_t length,
-                                 PageAccessibilityConfiguration accessibility) {
-#if defined(OS_MACOSX)
-  // On macOS, to update accounting, we need to make another syscall. For more
-  // details, see https://crbug.com/823915.
-  madvise(address, length, MADV_FREE_REUSE);
-#endif
-
-  // On POSIX systems, the caller need simply read the memory to recommit it.
-  // This has the correct behavior because the API requires the permissions to
-  // be the same as before decommitting and all configurations can read.
-  return true;
-}
-
-void DiscardSystemPagesInternal(void* address, size_t length) {
-#if defined(OS_MACOSX)
-  int ret = madvise(address, length, MADV_FREE_REUSABLE);
-  if (ret) {
-    // MADV_FREE_REUSABLE sometimes fails, so fall back to MADV_DONTNEED.
-    ret = madvise(address, length, MADV_DONTNEED);
-  }
-  CHECK(0 == ret);
-#else
-  // We have experimented with other flags, but with suboptimal results.
-  //
-  // MADV_FREE (Linux): Makes our memory measurements less predictable;
-  // performance benefits unclear.
-  //
-  // Therefore, we just do the simple thing: MADV_DONTNEED.
-  CHECK(!madvise(address, length, MADV_DONTNEED));
-#endif
-}
-
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_POSIX_H_
diff --git a/third_party/base/allocator/partition_allocator/page_allocator_internals_win.h b/third_party/base/allocator/partition_allocator/page_allocator_internals_win.h
deleted file mode 100644
index 2ba0a25..0000000
--- a/third_party/base/allocator/partition_allocator/page_allocator_internals_win.h
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright (c) 2018 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_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_
-
-#include "third_party/base/allocator/partition_allocator/oom.h"
-#include "third_party/base/allocator/partition_allocator/page_allocator_internal.h"
-
-namespace pdfium {
-namespace base {
-
-// |VirtualAlloc| will fail if allocation at the hint address is blocked.
-constexpr bool kHintIsAdvisory = false;
-std::atomic<int32_t> s_allocPageErrorCode{ERROR_SUCCESS};
-
-int GetAccessFlags(PageAccessibilityConfiguration accessibility) {
-  switch (accessibility) {
-    case PageRead:
-      return PAGE_READONLY;
-    case PageReadWrite:
-      return PAGE_READWRITE;
-    case PageReadExecute:
-      return PAGE_EXECUTE_READ;
-    case PageReadWriteExecute:
-      return PAGE_EXECUTE_READWRITE;
-    default:
-      NOTREACHED();
-      FALLTHROUGH;
-    case PageInaccessible:
-      return PAGE_NOACCESS;
-  }
-}
-
-void* SystemAllocPagesInternal(void* hint,
-                               size_t length,
-                               PageAccessibilityConfiguration accessibility,
-                               PageTag page_tag,
-                               bool commit) {
-  DWORD access_flag = GetAccessFlags(accessibility);
-  const DWORD type_flags = commit ? (MEM_RESERVE | MEM_COMMIT) : MEM_RESERVE;
-  void* ret = VirtualAlloc(hint, length, type_flags, access_flag);
-  if (ret == nullptr) {
-    s_allocPageErrorCode = GetLastError();
-  }
-  return ret;
-}
-
-void* TrimMappingInternal(void* base,
-                          size_t base_length,
-                          size_t trim_length,
-                          PageAccessibilityConfiguration accessibility,
-                          bool commit,
-                          size_t pre_slack,
-                          size_t post_slack) {
-  void* ret = base;
-  if (pre_slack || post_slack) {
-    // We cannot resize the allocation run. Free it and retry at the aligned
-    // address within the freed range.
-    ret = reinterpret_cast<char*>(base) + pre_slack;
-    FreePages(base, base_length);
-    ret = SystemAllocPages(ret, trim_length, accessibility, PageTag::kChromium,
-                           commit);
-  }
-  return ret;
-}
-
-bool TrySetSystemPagesAccessInternal(
-    void* address,
-    size_t length,
-    PageAccessibilityConfiguration accessibility) {
-  if (accessibility == PageInaccessible)
-    return VirtualFree(address, length, MEM_DECOMMIT) != 0;
-  return nullptr != VirtualAlloc(address, length, MEM_COMMIT,
-                                 GetAccessFlags(accessibility));
-}
-
-void SetSystemPagesAccessInternal(
-    void* address,
-    size_t length,
-    PageAccessibilityConfiguration accessibility) {
-  if (accessibility == PageInaccessible) {
-    if (!VirtualFree(address, length, MEM_DECOMMIT)) {
-      // We check `GetLastError` for `ERROR_SUCCESS` here so that in a crash
-      // report we get the error number.
-      CHECK_EQ(static_cast<uint32_t>(ERROR_SUCCESS), GetLastError());
-    }
-  } else {
-    if (!VirtualAlloc(address, length, MEM_COMMIT,
-                      GetAccessFlags(accessibility))) {
-      int32_t error = GetLastError();
-      if (error == ERROR_COMMITMENT_LIMIT)
-        OOM_CRASH();
-      // We check `GetLastError` for `ERROR_SUCCESS` here so that in a crash
-      // report we get the error number.
-      CHECK_EQ(ERROR_SUCCESS, error);
-    }
-  }
-}
-
-void FreePagesInternal(void* address, size_t length) {
-  CHECK(VirtualFree(address, 0, MEM_RELEASE));
-}
-
-void DecommitSystemPagesInternal(void* address, size_t length) {
-  SetSystemPagesAccess(address, length, PageInaccessible);
-}
-
-bool RecommitSystemPagesInternal(void* address,
-                                 size_t length,
-                                 PageAccessibilityConfiguration accessibility) {
-  return TrySetSystemPagesAccess(address, length, accessibility);
-}
-
-void DiscardSystemPagesInternal(void* address, size_t length) {
-  // On Windows, discarded pages are not returned to the system immediately and
-  // not guaranteed to be zeroed when returned to the application.
-  using DiscardVirtualMemoryFunction =
-      DWORD(WINAPI*)(PVOID virtualAddress, SIZE_T size);
-  static DiscardVirtualMemoryFunction discard_virtual_memory =
-      reinterpret_cast<DiscardVirtualMemoryFunction>(-1);
-  if (discard_virtual_memory ==
-      reinterpret_cast<DiscardVirtualMemoryFunction>(-1))
-    discard_virtual_memory =
-        reinterpret_cast<DiscardVirtualMemoryFunction>(GetProcAddress(
-            GetModuleHandle(L"Kernel32.dll"), "DiscardVirtualMemory"));
-  // Use DiscardVirtualMemory when available because it releases faster than
-  // MEM_RESET.
-  DWORD ret = 1;
-  if (discard_virtual_memory) {
-    ret = discard_virtual_memory(address, length);
-  }
-  // DiscardVirtualMemory is buggy in Win10 SP0, so fall back to MEM_RESET on
-  // failure.
-  if (ret) {
-    void* ptr = VirtualAlloc(address, length, MEM_RESET, PAGE_READWRITE);
-    CHECK(ptr);
-  }
-}
-
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_alloc.cc b/third_party/base/allocator/partition_allocator/partition_alloc.cc
deleted file mode 100644
index 19ce17b..0000000
--- a/third_party/base/allocator/partition_allocator/partition_alloc.cc
+++ /dev/null
@@ -1,846 +0,0 @@
-// Copyright (c) 2013 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.
-
-#include "third_party/base/allocator/partition_allocator/partition_alloc.h"
-
-#include <string.h>
-
-#include <memory>
-#include <type_traits>
-
-#include "third_party/base/allocator/partition_allocator/partition_direct_map_extent.h"
-#include "third_party/base/allocator/partition_allocator/partition_oom.h"
-#include "third_party/base/allocator/partition_allocator/partition_page.h"
-#include "third_party/base/allocator/partition_allocator/spin_lock.h"
-
-namespace pdfium {
-namespace base {
-
-// Two partition pages are used as guard / metadata page so make sure the super
-// page size is bigger.
-static_assert(kPartitionPageSize * 4 <= kSuperPageSize, "ok super page size");
-static_assert(!(kSuperPageSize % kPartitionPageSize), "ok super page multiple");
-// Four system pages gives us room to hack out a still-guard-paged piece
-// of metadata in the middle of a guard partition page.
-static_assert(kSystemPageSize * 4 <= kPartitionPageSize,
-              "ok partition page size");
-static_assert(!(kPartitionPageSize % kSystemPageSize),
-              "ok partition page multiple");
-static_assert(sizeof(internal::PartitionPage) <= kPageMetadataSize,
-              "PartitionPage should not be too big");
-static_assert(sizeof(internal::PartitionBucket) <= kPageMetadataSize,
-              "PartitionBucket should not be too big");
-static_assert(sizeof(internal::PartitionSuperPageExtentEntry) <=
-                  kPageMetadataSize,
-              "PartitionSuperPageExtentEntry should not be too big");
-static_assert(kPageMetadataSize * kNumPartitionPagesPerSuperPage <=
-                  kSystemPageSize,
-              "page metadata fits in hole");
-// Limit to prevent callers accidentally overflowing an int size.
-static_assert(kGenericMaxDirectMapped <=
-                  (1UL << 31) + kPageAllocationGranularity,
-              "maximum direct mapped allocation");
-// Check that some of our zanier calculations worked out as expected.
-static_assert(kGenericSmallestBucket == 8, "generic smallest bucket");
-static_assert(kGenericMaxBucketed == 983040, "generic max bucketed");
-static_assert(kMaxSystemPagesPerSlotSpan < (1 << 8),
-              "System pages per slot span must be less than 128.");
-
-internal::PartitionRootBase::PartitionRootBase() = default;
-internal::PartitionRootBase::~PartitionRootBase() = default;
-PartitionRoot::PartitionRoot() = default;
-PartitionRoot::~PartitionRoot() = default;
-PartitionRootGeneric::PartitionRootGeneric() = default;
-PartitionRootGeneric::~PartitionRootGeneric() = default;
-PartitionAllocatorGeneric::PartitionAllocatorGeneric() = default;
-PartitionAllocatorGeneric::~PartitionAllocatorGeneric() = default;
-
-subtle::SpinLock* GetLock() {
-  static subtle::SpinLock* s_initialized_lock = nullptr;
-  if (!s_initialized_lock)
-    s_initialized_lock = new subtle::SpinLock();
-  return s_initialized_lock;
-}
-
-static bool g_initialized = false;
-
-void (*internal::PartitionRootBase::gOomHandlingFunction)() = nullptr;
-std::atomic<bool> PartitionAllocHooks::hooks_enabled_(false);
-subtle::SpinLock PartitionAllocHooks::set_hooks_lock_;
-std::atomic<PartitionAllocHooks::AllocationObserverHook*>
-    PartitionAllocHooks::allocation_observer_hook_(nullptr);
-std::atomic<PartitionAllocHooks::FreeObserverHook*>
-    PartitionAllocHooks::free_observer_hook_(nullptr);
-std::atomic<PartitionAllocHooks::AllocationOverrideHook*>
-    PartitionAllocHooks::allocation_override_hook_(nullptr);
-std::atomic<PartitionAllocHooks::FreeOverrideHook*>
-    PartitionAllocHooks::free_override_hook_(nullptr);
-std::atomic<PartitionAllocHooks::ReallocOverrideHook*>
-    PartitionAllocHooks::realloc_override_hook_(nullptr);
-
-void PartitionAllocHooks::SetObserverHooks(AllocationObserverHook* alloc_hook,
-                                           FreeObserverHook* free_hook) {
-  subtle::SpinLock::Guard guard(set_hooks_lock_);
-
-  // Chained hooks are not supported. Registering a non-null hook when a
-  // non-null hook is already registered indicates somebody is trying to
-  // overwrite a hook.
-  CHECK((!allocation_observer_hook_ && !free_observer_hook_) ||
-        (!alloc_hook && !free_hook));
-  allocation_observer_hook_ = alloc_hook;
-  free_observer_hook_ = free_hook;
-
-  hooks_enabled_ = allocation_observer_hook_ || allocation_override_hook_;
-}
-
-void PartitionAllocHooks::SetOverrideHooks(AllocationOverrideHook* alloc_hook,
-                                           FreeOverrideHook* free_hook,
-                                           ReallocOverrideHook realloc_hook) {
-  subtle::SpinLock::Guard guard(set_hooks_lock_);
-
-  CHECK((!allocation_override_hook_ && !free_override_hook_ &&
-         !realloc_override_hook_) ||
-        (!alloc_hook && !free_hook && !realloc_hook));
-  allocation_override_hook_ = alloc_hook;
-  free_override_hook_ = free_hook;
-  realloc_override_hook_ = realloc_hook;
-
-  hooks_enabled_ = allocation_observer_hook_ || allocation_override_hook_;
-}
-
-void PartitionAllocHooks::AllocationObserverHookIfEnabled(
-    void* address,
-    size_t size,
-    const char* type_name) {
-  if (AllocationObserverHook* hook =
-          allocation_observer_hook_.load(std::memory_order_relaxed)) {
-    hook(address, size, type_name);
-  }
-}
-
-bool PartitionAllocHooks::AllocationOverrideHookIfEnabled(
-    void** out,
-    int flags,
-    size_t size,
-    const char* type_name) {
-  if (AllocationOverrideHook* hook =
-          allocation_override_hook_.load(std::memory_order_relaxed)) {
-    return hook(out, flags, size, type_name);
-  }
-  return false;
-}
-
-void PartitionAllocHooks::FreeObserverHookIfEnabled(void* address) {
-  if (FreeObserverHook* hook =
-          free_observer_hook_.load(std::memory_order_relaxed)) {
-    hook(address);
-  }
-}
-
-bool PartitionAllocHooks::FreeOverrideHookIfEnabled(void* address) {
-  if (FreeOverrideHook* hook =
-          free_override_hook_.load(std::memory_order_relaxed)) {
-    return hook(address);
-  }
-  return false;
-}
-
-void PartitionAllocHooks::ReallocObserverHookIfEnabled(void* old_address,
-                                                       void* new_address,
-                                                       size_t size,
-                                                       const char* type_name) {
-  // Report a reallocation as a free followed by an allocation.
-  AllocationObserverHook* allocation_hook =
-      allocation_observer_hook_.load(std::memory_order_relaxed);
-  FreeObserverHook* free_hook =
-      free_observer_hook_.load(std::memory_order_relaxed);
-  if (allocation_hook && free_hook) {
-    free_hook(old_address);
-    allocation_hook(new_address, size, type_name);
-  }
-}
-bool PartitionAllocHooks::ReallocOverrideHookIfEnabled(size_t* out,
-                                                       void* address) {
-  if (ReallocOverrideHook* hook =
-          realloc_override_hook_.load(std::memory_order_relaxed)) {
-    return hook(out, address);
-  }
-  return false;
-}
-
-static void PartitionAllocBaseInit(internal::PartitionRootBase* root) {
-  DCHECK(!root->initialized);
-  {
-    subtle::SpinLock::Guard guard(*GetLock());
-    if (!g_initialized) {
-      g_initialized = true;
-      // We mark the sentinel bucket/page as free to make sure it is skipped by
-      // our logic to find a new active page.
-      internal::PartitionBucket::get_sentinel_bucket()->active_pages_head =
-          internal::PartitionPage::get_sentinel_page();
-    }
-  }
-
-  root->initialized = true;
-
-  // This is a "magic" value so we can test if a root pointer is valid.
-  root->inverted_self = ~reinterpret_cast<uintptr_t>(root);
-}
-
-void PartitionAllocGlobalInit(void (*oom_handling_function)()) {
-  DCHECK(oom_handling_function);
-  internal::PartitionRootBase::gOomHandlingFunction = oom_handling_function;
-}
-
-void PartitionRoot::Init(size_t bucket_count, size_t maximum_allocation) {
-  PartitionAllocBaseInit(this);
-
-  num_buckets = bucket_count;
-  max_allocation = maximum_allocation;
-  for (size_t i = 0; i < num_buckets; ++i) {
-    internal::PartitionBucket& bucket = buckets()[i];
-    bucket.Init(i == 0 ? kAllocationGranularity : (i << kBucketShift));
-  }
-}
-
-void PartitionRootGeneric::Init() {
-  subtle::SpinLock::Guard guard(lock);
-
-  PartitionAllocBaseInit(this);
-
-  // Precalculate some shift and mask constants used in the hot path.
-  // Example: malloc(41) == 101001 binary.
-  // Order is 6 (1 << 6-1) == 32 is highest bit set.
-  // order_index is the next three MSB == 010 == 2.
-  // sub_order_index_mask is a mask for the remaining bits == 11 (masking to 01
-  // for
-  // the sub_order_index).
-  size_t order;
-  for (order = 0; order <= kBitsPerSizeT; ++order) {
-    size_t order_index_shift;
-    if (order < kGenericNumBucketsPerOrderBits + 1)
-      order_index_shift = 0;
-    else
-      order_index_shift = order - (kGenericNumBucketsPerOrderBits + 1);
-    order_index_shifts[order] = order_index_shift;
-    size_t sub_order_index_mask;
-    if (order == kBitsPerSizeT) {
-      // This avoids invoking undefined behavior for an excessive shift.
-      sub_order_index_mask =
-          static_cast<size_t>(-1) >> (kGenericNumBucketsPerOrderBits + 1);
-    } else {
-      sub_order_index_mask = ((static_cast<size_t>(1) << order) - 1) >>
-                             (kGenericNumBucketsPerOrderBits + 1);
-    }
-    order_sub_index_masks[order] = sub_order_index_mask;
-  }
-
-  // Set up the actual usable buckets first.
-  // Note that typical values (i.e. min allocation size of 8) will result in
-  // pseudo buckets (size==9 etc. or more generally, size is not a multiple
-  // of the smallest allocation granularity).
-  // We avoid them in the bucket lookup map, but we tolerate them to keep the
-  // code simpler and the structures more generic.
-  size_t i, j;
-  size_t current_size = kGenericSmallestBucket;
-  size_t current_increment =
-      kGenericSmallestBucket >> kGenericNumBucketsPerOrderBits;
-  internal::PartitionBucket* bucket = &buckets[0];
-  for (i = 0; i < kGenericNumBucketedOrders; ++i) {
-    for (j = 0; j < kGenericNumBucketsPerOrder; ++j) {
-      bucket->Init(current_size);
-      // Disable psuedo buckets so that touching them faults.
-      if (current_size % kGenericSmallestBucket)
-        bucket->active_pages_head = nullptr;
-      current_size += current_increment;
-      ++bucket;
-    }
-    current_increment <<= 1;
-  }
-  DCHECK(current_size == 1 << kGenericMaxBucketedOrder);
-  DCHECK(bucket == &buckets[0] + kGenericNumBuckets);
-
-  // Then set up the fast size -> bucket lookup table.
-  bucket = &buckets[0];
-  internal::PartitionBucket** bucket_ptr = &bucket_lookups[0];
-  for (order = 0; order <= kBitsPerSizeT; ++order) {
-    for (j = 0; j < kGenericNumBucketsPerOrder; ++j) {
-      if (order < kGenericMinBucketedOrder) {
-        // Use the bucket of the finest granularity for malloc(0) etc.
-        *bucket_ptr++ = &buckets[0];
-      } else if (order > kGenericMaxBucketedOrder) {
-        *bucket_ptr++ = internal::PartitionBucket::get_sentinel_bucket();
-      } else {
-        internal::PartitionBucket* valid_bucket = bucket;
-        // Skip over invalid buckets.
-        while (valid_bucket->slot_size % kGenericSmallestBucket)
-          valid_bucket++;
-        *bucket_ptr++ = valid_bucket;
-        bucket++;
-      }
-    }
-  }
-  DCHECK(bucket == &buckets[0] + kGenericNumBuckets);
-  DCHECK(bucket_ptr == &bucket_lookups[0] +
-                           ((kBitsPerSizeT + 1) * kGenericNumBucketsPerOrder));
-  // And there's one last bucket lookup that will be hit for e.g. malloc(-1),
-  // which tries to overflow to a non-existant order.
-  *bucket_ptr = internal::PartitionBucket::get_sentinel_bucket();
-}
-
-bool PartitionReallocDirectMappedInPlace(PartitionRootGeneric* root,
-                                         internal::PartitionPage* page,
-                                         size_t raw_size) {
-  DCHECK(page->bucket->is_direct_mapped());
-
-  raw_size = internal::PartitionCookieSizeAdjustAdd(raw_size);
-
-  // Note that the new size might be a bucketed size; this function is called
-  // whenever we're reallocating a direct mapped allocation.
-  size_t new_size = internal::PartitionBucket::get_direct_map_size(raw_size);
-  if (new_size < kGenericMinDirectMappedDownsize)
-    return false;
-
-  // bucket->slot_size is the current size of the allocation.
-  size_t current_size = page->bucket->slot_size;
-  char* char_ptr = static_cast<char*>(internal::PartitionPage::ToPointer(page));
-  if (new_size == current_size) {
-    // No need to move any memory around, but update size and cookie below.
-  } else if (new_size < current_size) {
-    size_t map_size =
-        internal::PartitionDirectMapExtent::FromPage(page)->map_size;
-
-    // Don't reallocate in-place if new size is less than 80 % of the full
-    // map size, to avoid holding on to too much unused address space.
-    if ((new_size / kSystemPageSize) * 5 < (map_size / kSystemPageSize) * 4)
-      return false;
-
-    // Shrink by decommitting unneeded pages and making them inaccessible.
-    size_t decommit_size = current_size - new_size;
-    root->DecommitSystemPages(char_ptr + new_size, decommit_size);
-    SetSystemPagesAccess(char_ptr + new_size, decommit_size, PageInaccessible);
-  } else if (new_size <=
-             internal::PartitionDirectMapExtent::FromPage(page)->map_size) {
-    // Grow within the actually allocated memory. Just need to make the
-    // pages accessible again.
-    size_t recommit_size = new_size - current_size;
-    SetSystemPagesAccess(char_ptr + current_size, recommit_size, PageReadWrite);
-    root->RecommitSystemPages(char_ptr + current_size, recommit_size);
-
-#if DCHECK_IS_ON()
-    memset(char_ptr + current_size, kUninitializedByte, recommit_size);
-#endif
-  } else {
-    // We can't perform the realloc in-place.
-    // TODO: support this too when possible.
-    return false;
-  }
-
-#if DCHECK_IS_ON()
-  // Write a new trailing cookie.
-  internal::PartitionCookieWriteValue(char_ptr + raw_size -
-                                      internal::kCookieSize);
-#endif
-
-  page->set_raw_size(raw_size);
-  DCHECK(page->get_raw_size() == raw_size);
-
-  page->bucket->slot_size = new_size;
-  return true;
-}
-
-void* PartitionReallocGenericFlags(PartitionRootGeneric* root,
-                                   int flags,
-                                   void* ptr,
-                                   size_t new_size,
-                                   const char* type_name) {
-#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-  CHECK_MAX_SIZE_OR_RETURN_NULLPTR(new_size, flags);
-  void* result = realloc(ptr, new_size);
-  CHECK(result || flags & PartitionAllocReturnNull);
-  return result;
-#else
-  if (UNLIKELY(!ptr))
-    return PartitionAllocGenericFlags(root, flags, new_size, type_name);
-  if (UNLIKELY(!new_size)) {
-    root->Free(ptr);
-    return nullptr;
-  }
-
-  if (new_size > kGenericMaxDirectMapped) {
-    if (flags & PartitionAllocReturnNull)
-      return nullptr;
-    internal::PartitionExcessiveAllocationSize();
-  }
-
-  const bool hooks_enabled = PartitionAllocHooks::AreHooksEnabled();
-  bool overridden = false;
-  size_t actual_old_size;
-  if (UNLIKELY(hooks_enabled)) {
-    overridden = PartitionAllocHooks::ReallocOverrideHookIfEnabled(
-        &actual_old_size, ptr);
-  }
-  if (LIKELY(!overridden)) {
-    internal::PartitionPage* page = internal::PartitionPage::FromPointer(
-        internal::PartitionCookieFreePointerAdjust(ptr));
-    // TODO(palmer): See if we can afford to make this a CHECK.
-    DCHECK(root->IsValidPage(page));
-
-    if (UNLIKELY(page->bucket->is_direct_mapped())) {
-      // We may be able to perform the realloc in place by changing the
-      // accessibility of memory pages and, if reducing the size, decommitting
-      // them.
-      if (PartitionReallocDirectMappedInPlace(root, page, new_size)) {
-        if (UNLIKELY(hooks_enabled)) {
-          PartitionAllocHooks::ReallocObserverHookIfEnabled(ptr, ptr, new_size,
-                                                            type_name);
-        }
-        return ptr;
-      }
-    }
-
-    const size_t actual_new_size = root->ActualSize(new_size);
-    actual_old_size = PartitionAllocGetSize(ptr);
-
-    // TODO: note that tcmalloc will "ignore" a downsizing realloc() unless the
-    // new size is a significant percentage smaller. We could do the same if we
-    // determine it is a win.
-    if (actual_new_size == actual_old_size) {
-      // Trying to allocate a block of size |new_size| would give us a block of
-      // the same size as the one we've already got, so re-use the allocation
-      // after updating statistics (and cookies, if present).
-      page->set_raw_size(internal::PartitionCookieSizeAdjustAdd(new_size));
-#if DCHECK_IS_ON()
-      // Write a new trailing cookie when it is possible to keep track of
-      // |new_size| via the raw size pointer.
-      if (page->get_raw_size_ptr())
-        internal::PartitionCookieWriteValue(static_cast<char*>(ptr) + new_size);
-#endif
-      return ptr;
-    }
-  }
-
-  // This realloc cannot be resized in-place. Sadness.
-  void* ret = PartitionAllocGenericFlags(root, flags, new_size, type_name);
-  if (!ret) {
-    if (flags & PartitionAllocReturnNull)
-      return nullptr;
-    internal::PartitionExcessiveAllocationSize();
-  }
-
-  size_t copy_size = actual_old_size;
-  if (new_size < copy_size)
-    copy_size = new_size;
-
-  memcpy(ret, ptr, copy_size);
-  root->Free(ptr);
-  return ret;
-#endif
-}
-
-void* PartitionRootGeneric::Realloc(void* ptr,
-                                    size_t new_size,
-                                    const char* type_name) {
-  return PartitionReallocGenericFlags(this, 0, ptr, new_size, type_name);
-}
-
-void* PartitionRootGeneric::TryRealloc(void* ptr,
-                                       size_t new_size,
-                                       const char* type_name) {
-  return PartitionReallocGenericFlags(this, PartitionAllocReturnNull, ptr,
-                                      new_size, type_name);
-}
-
-static size_t PartitionPurgePage(internal::PartitionPage* page, bool discard) {
-  const internal::PartitionBucket* bucket = page->bucket;
-  size_t slot_size = bucket->slot_size;
-  if (slot_size < kSystemPageSize || !page->num_allocated_slots)
-    return 0;
-
-  size_t bucket_num_slots = bucket->get_slots_per_span();
-  size_t discardable_bytes = 0;
-
-  size_t raw_size = page->get_raw_size();
-  if (raw_size) {
-    uint32_t used_bytes = static_cast<uint32_t>(RoundUpToSystemPage(raw_size));
-    discardable_bytes = bucket->slot_size - used_bytes;
-    if (discardable_bytes && discard) {
-      char* ptr =
-          reinterpret_cast<char*>(internal::PartitionPage::ToPointer(page));
-      ptr += used_bytes;
-      DiscardSystemPages(ptr, discardable_bytes);
-    }
-    return discardable_bytes;
-  }
-
-  constexpr size_t kMaxSlotCount =
-      (kPartitionPageSize * kMaxPartitionPagesPerSlotSpan) / kSystemPageSize;
-  DCHECK(bucket_num_slots <= kMaxSlotCount);
-  DCHECK(page->num_unprovisioned_slots < bucket_num_slots);
-  size_t num_slots = bucket_num_slots - page->num_unprovisioned_slots;
-  char slot_usage[kMaxSlotCount];
-#if !defined(OS_WIN)
-  // The last freelist entry should not be discarded when using OS_WIN.
-  // DiscardVirtualMemory makes the contents of discarded memory undefined.
-  size_t last_slot = static_cast<size_t>(-1);
-#endif
-  memset(slot_usage, 1, num_slots);
-  char* ptr = reinterpret_cast<char*>(internal::PartitionPage::ToPointer(page));
-  // First, walk the freelist for this page and make a bitmap of which slots
-  // are not in use.
-  for (internal::PartitionFreelistEntry* entry = page->freelist_head; entry;
-       /**/) {
-    size_t slot_index = (reinterpret_cast<char*>(entry) - ptr) / slot_size;
-    DCHECK(slot_index < num_slots);
-    slot_usage[slot_index] = 0;
-    entry = internal::EncodedPartitionFreelistEntry::Decode(entry->next);
-#if !defined(OS_WIN)
-    // If we have a slot where the masked freelist entry is 0, we can actually
-    // discard that freelist entry because touching a discarded page is
-    // guaranteed to return original content or 0. (Note that this optimization
-    // won't fire on big-endian machines because the masking function is
-    // negation.)
-    if (!internal::PartitionFreelistEntry::Encode(entry))
-      last_slot = slot_index;
-#endif
-  }
-
-  // If the slot(s) at the end of the slot span are not in used, we can truncate
-  // them entirely and rewrite the freelist.
-  size_t truncated_slots = 0;
-  while (!slot_usage[num_slots - 1]) {
-    truncated_slots++;
-    num_slots--;
-    DCHECK(num_slots);
-  }
-  // First, do the work of calculating the discardable bytes. Don't actually
-  // discard anything unless the discard flag was passed in.
-  if (truncated_slots) {
-    size_t unprovisioned_bytes = 0;
-    char* begin_ptr = ptr + (num_slots * slot_size);
-    char* end_ptr = begin_ptr + (slot_size * truncated_slots);
-    begin_ptr = reinterpret_cast<char*>(
-        RoundUpToSystemPage(reinterpret_cast<size_t>(begin_ptr)));
-    // We round the end pointer here up and not down because we're at the end of
-    // a slot span, so we "own" all the way up the page boundary.
-    end_ptr = reinterpret_cast<char*>(
-        RoundUpToSystemPage(reinterpret_cast<size_t>(end_ptr)));
-    DCHECK(end_ptr <= ptr + bucket->get_bytes_per_span());
-    if (begin_ptr < end_ptr) {
-      unprovisioned_bytes = end_ptr - begin_ptr;
-      discardable_bytes += unprovisioned_bytes;
-    }
-    if (unprovisioned_bytes && discard) {
-      DCHECK(truncated_slots > 0);
-      size_t num_new_entries = 0;
-      page->num_unprovisioned_slots += static_cast<uint16_t>(truncated_slots);
-
-      // Rewrite the freelist.
-      internal::PartitionFreelistEntry* head = nullptr;
-      internal::PartitionFreelistEntry* back = head;
-      for (size_t slot_index = 0; slot_index < num_slots; ++slot_index) {
-        if (slot_usage[slot_index])
-          continue;
-
-        auto* entry = reinterpret_cast<internal::PartitionFreelistEntry*>(
-            ptr + (slot_size * slot_index));
-        if (!head) {
-          head = entry;
-          back = entry;
-        } else {
-          back->next = internal::PartitionFreelistEntry::Encode(entry);
-          back = entry;
-        }
-        num_new_entries++;
-#if !defined(OS_WIN)
-        last_slot = slot_index;
-#endif
-      }
-
-      page->freelist_head = head;
-      if (back)
-        back->next = internal::PartitionFreelistEntry::Encode(nullptr);
-
-      DCHECK(num_new_entries == num_slots - page->num_allocated_slots);
-      // Discard the memory.
-      DiscardSystemPages(begin_ptr, unprovisioned_bytes);
-    }
-  }
-
-  // Next, walk the slots and for any not in use, consider where the system page
-  // boundaries occur. We can release any system pages back to the system as
-  // long as we don't interfere with a freelist pointer or an adjacent slot.
-  for (size_t i = 0; i < num_slots; ++i) {
-    if (slot_usage[i])
-      continue;
-    // The first address we can safely discard is just after the freelist
-    // pointer. There's one quirk: if the freelist pointer is actually NULL, we
-    // can discard that pointer value too.
-    char* begin_ptr = ptr + (i * slot_size);
-    char* end_ptr = begin_ptr + slot_size;
-#if !defined(OS_WIN)
-    if (i != last_slot)
-      begin_ptr += sizeof(internal::PartitionFreelistEntry);
-#else
-    begin_ptr += sizeof(internal::PartitionFreelistEntry);
-#endif
-    begin_ptr = reinterpret_cast<char*>(
-        RoundUpToSystemPage(reinterpret_cast<size_t>(begin_ptr)));
-    end_ptr = reinterpret_cast<char*>(
-        RoundDownToSystemPage(reinterpret_cast<size_t>(end_ptr)));
-    if (begin_ptr < end_ptr) {
-      size_t partial_slot_bytes = end_ptr - begin_ptr;
-      discardable_bytes += partial_slot_bytes;
-      if (discard)
-        DiscardSystemPages(begin_ptr, partial_slot_bytes);
-    }
-  }
-  return discardable_bytes;
-}
-
-static void PartitionPurgeBucket(internal::PartitionBucket* bucket) {
-  if (bucket->active_pages_head !=
-      internal::PartitionPage::get_sentinel_page()) {
-    for (internal::PartitionPage* page = bucket->active_pages_head; page;
-         page = page->next_page) {
-      DCHECK(page != internal::PartitionPage::get_sentinel_page());
-      PartitionPurgePage(page, true);
-    }
-  }
-}
-
-void PartitionRoot::PurgeMemory(int flags) {
-  if (flags & PartitionPurgeDecommitEmptyPages)
-    DecommitEmptyPages();
-  // We don't currently do anything for PartitionPurgeDiscardUnusedSystemPages
-  // here because that flag is only useful for allocations >= system page size.
-  // We only have allocations that large inside generic partitions at the
-  // moment.
-}
-
-void PartitionRootGeneric::PurgeMemory(int flags) {
-  subtle::SpinLock::Guard guard(lock);
-  if (flags & PartitionPurgeDecommitEmptyPages)
-    DecommitEmptyPages();
-  if (flags & PartitionPurgeDiscardUnusedSystemPages) {
-    for (size_t i = 0; i < kGenericNumBuckets; ++i) {
-      internal::PartitionBucket* bucket = &buckets[i];
-      if (bucket->slot_size >= kSystemPageSize)
-        PartitionPurgeBucket(bucket);
-    }
-  }
-}
-
-static void PartitionDumpPageStats(PartitionBucketMemoryStats* stats_out,
-                                   internal::PartitionPage* page) {
-  uint16_t bucket_num_slots = page->bucket->get_slots_per_span();
-
-  if (page->is_decommitted()) {
-    ++stats_out->num_decommitted_pages;
-    return;
-  }
-
-  stats_out->discardable_bytes += PartitionPurgePage(page, false);
-
-  size_t raw_size = page->get_raw_size();
-  if (raw_size) {
-    stats_out->active_bytes += static_cast<uint32_t>(raw_size);
-  } else {
-    stats_out->active_bytes +=
-        (page->num_allocated_slots * stats_out->bucket_slot_size);
-  }
-
-  size_t page_bytes_resident =
-      RoundUpToSystemPage((bucket_num_slots - page->num_unprovisioned_slots) *
-                          stats_out->bucket_slot_size);
-  stats_out->resident_bytes += page_bytes_resident;
-  if (page->is_empty()) {
-    stats_out->decommittable_bytes += page_bytes_resident;
-    ++stats_out->num_empty_pages;
-  } else if (page->is_full()) {
-    ++stats_out->num_full_pages;
-  } else {
-    DCHECK(page->is_active());
-    ++stats_out->num_active_pages;
-  }
-}
-
-static void PartitionDumpBucketStats(PartitionBucketMemoryStats* stats_out,
-                                     const internal::PartitionBucket* bucket) {
-  DCHECK(!bucket->is_direct_mapped());
-  stats_out->is_valid = false;
-  // If the active page list is empty (==
-  // internal::PartitionPage::get_sentinel_page()), the bucket might still need
-  // to be reported if it has a list of empty, decommitted or full pages.
-  if (bucket->active_pages_head ==
-          internal::PartitionPage::get_sentinel_page() &&
-      !bucket->empty_pages_head && !bucket->decommitted_pages_head &&
-      !bucket->num_full_pages)
-    return;
-
-  memset(stats_out, '\0', sizeof(*stats_out));
-  stats_out->is_valid = true;
-  stats_out->is_direct_map = false;
-  stats_out->num_full_pages = static_cast<size_t>(bucket->num_full_pages);
-  stats_out->bucket_slot_size = bucket->slot_size;
-  uint16_t bucket_num_slots = bucket->get_slots_per_span();
-  size_t bucket_useful_storage = stats_out->bucket_slot_size * bucket_num_slots;
-  stats_out->allocated_page_size = bucket->get_bytes_per_span();
-  stats_out->active_bytes = bucket->num_full_pages * bucket_useful_storage;
-  stats_out->resident_bytes =
-      bucket->num_full_pages * stats_out->allocated_page_size;
-
-  for (internal::PartitionPage* page = bucket->empty_pages_head; page;
-       page = page->next_page) {
-    DCHECK(page->is_empty() || page->is_decommitted());
-    PartitionDumpPageStats(stats_out, page);
-  }
-  for (internal::PartitionPage* page = bucket->decommitted_pages_head; page;
-       page = page->next_page) {
-    DCHECK(page->is_decommitted());
-    PartitionDumpPageStats(stats_out, page);
-  }
-
-  if (bucket->active_pages_head !=
-      internal::PartitionPage::get_sentinel_page()) {
-    for (internal::PartitionPage* page = bucket->active_pages_head; page;
-         page = page->next_page) {
-      DCHECK(page != internal::PartitionPage::get_sentinel_page());
-      PartitionDumpPageStats(stats_out, page);
-    }
-  }
-}
-
-void PartitionRootGeneric::DumpStats(const char* partition_name,
-                                     bool is_light_dump,
-                                     PartitionStatsDumper* dumper) {
-  PartitionMemoryStats stats = {0};
-  stats.total_mmapped_bytes =
-      total_size_of_super_pages + total_size_of_direct_mapped_pages;
-  stats.total_committed_bytes = total_size_of_committed_pages;
-
-  size_t direct_mapped_allocations_total_size = 0;
-
-  static const size_t kMaxReportableDirectMaps = 4096;
-
-  // Allocate on the heap rather than on the stack to avoid stack overflow
-  // skirmishes (on Windows, in particular).
-  std::unique_ptr<uint32_t[]> direct_map_lengths = nullptr;
-  if (!is_light_dump) {
-    direct_map_lengths =
-        std::unique_ptr<uint32_t[]>(new uint32_t[kMaxReportableDirectMaps]);
-  }
-
-  PartitionBucketMemoryStats bucket_stats[kGenericNumBuckets];
-  size_t num_direct_mapped_allocations = 0;
-  {
-    subtle::SpinLock::Guard guard(lock);
-
-    for (size_t i = 0; i < kGenericNumBuckets; ++i) {
-      const internal::PartitionBucket* bucket = &buckets[i];
-      // Don't report the pseudo buckets that the generic allocator sets up in
-      // order to preserve a fast size->bucket map (see
-      // PartitionRootGeneric::Init() for details).
-      if (!bucket->active_pages_head)
-        bucket_stats[i].is_valid = false;
-      else
-        PartitionDumpBucketStats(&bucket_stats[i], bucket);
-      if (bucket_stats[i].is_valid) {
-        stats.total_resident_bytes += bucket_stats[i].resident_bytes;
-        stats.total_active_bytes += bucket_stats[i].active_bytes;
-        stats.total_decommittable_bytes += bucket_stats[i].decommittable_bytes;
-        stats.total_discardable_bytes += bucket_stats[i].discardable_bytes;
-      }
-    }
-
-    for (internal::PartitionDirectMapExtent* extent = direct_map_list;
-         extent && num_direct_mapped_allocations < kMaxReportableDirectMaps;
-         extent = extent->next_extent, ++num_direct_mapped_allocations) {
-      DCHECK(!extent->next_extent ||
-             extent->next_extent->prev_extent == extent);
-      size_t slot_size = extent->bucket->slot_size;
-      direct_mapped_allocations_total_size += slot_size;
-      if (is_light_dump)
-        continue;
-      direct_map_lengths[num_direct_mapped_allocations] = slot_size;
-    }
-  }
-
-  if (!is_light_dump) {
-    // Call |PartitionsDumpBucketStats| after collecting stats because it can
-    // try to allocate using |PartitionRootGeneric::Alloc()| and it can't
-    // obtain the lock.
-    for (size_t i = 0; i < kGenericNumBuckets; ++i) {
-      if (bucket_stats[i].is_valid)
-        dumper->PartitionsDumpBucketStats(partition_name, &bucket_stats[i]);
-    }
-
-    for (size_t i = 0; i < num_direct_mapped_allocations; ++i) {
-      uint32_t size = direct_map_lengths[i];
-
-      PartitionBucketMemoryStats mapped_stats = {};
-      mapped_stats.is_valid = true;
-      mapped_stats.is_direct_map = true;
-      mapped_stats.num_full_pages = 1;
-      mapped_stats.allocated_page_size = size;
-      mapped_stats.bucket_slot_size = size;
-      mapped_stats.active_bytes = size;
-      mapped_stats.resident_bytes = size;
-      dumper->PartitionsDumpBucketStats(partition_name, &mapped_stats);
-    }
-  }
-
-  stats.total_resident_bytes += direct_mapped_allocations_total_size;
-  stats.total_active_bytes += direct_mapped_allocations_total_size;
-  dumper->PartitionDumpTotals(partition_name, &stats);
-}
-
-void PartitionRoot::DumpStats(const char* partition_name,
-                              bool is_light_dump,
-                              PartitionStatsDumper* dumper) {
-  PartitionMemoryStats stats = {0};
-  stats.total_mmapped_bytes = total_size_of_super_pages;
-  stats.total_committed_bytes = total_size_of_committed_pages;
-  DCHECK(!total_size_of_direct_mapped_pages);
-
-  static constexpr size_t kMaxReportableBuckets = 4096 / sizeof(void*);
-  std::unique_ptr<PartitionBucketMemoryStats[]> memory_stats;
-  if (!is_light_dump) {
-    memory_stats = std::unique_ptr<PartitionBucketMemoryStats[]>(
-        new PartitionBucketMemoryStats[kMaxReportableBuckets]);
-  }
-
-  const size_t partition_num_buckets = num_buckets;
-  DCHECK(partition_num_buckets <= kMaxReportableBuckets);
-
-  for (size_t i = 0; i < partition_num_buckets; ++i) {
-    PartitionBucketMemoryStats bucket_stats = {0};
-    PartitionDumpBucketStats(&bucket_stats, &buckets()[i]);
-    if (bucket_stats.is_valid) {
-      stats.total_resident_bytes += bucket_stats.resident_bytes;
-      stats.total_active_bytes += bucket_stats.active_bytes;
-      stats.total_decommittable_bytes += bucket_stats.decommittable_bytes;
-      stats.total_discardable_bytes += bucket_stats.discardable_bytes;
-    }
-    if (!is_light_dump) {
-      if (bucket_stats.is_valid)
-        memory_stats[i] = bucket_stats;
-      else
-        memory_stats[i].is_valid = false;
-    }
-  }
-  if (!is_light_dump) {
-    // PartitionsDumpBucketStats is called after collecting stats because it
-    // can use PartitionRoot::Alloc() to allocate and this can affect the
-    // statistics.
-    for (size_t i = 0; i < partition_num_buckets; ++i) {
-      if (memory_stats[i].is_valid)
-        dumper->PartitionsDumpBucketStats(partition_name, &memory_stats[i]);
-    }
-  }
-  dumper->PartitionDumpTotals(partition_name, &stats);
-}
-
-}  // namespace base
-}  // namespace pdfium
diff --git a/third_party/base/allocator/partition_allocator/partition_alloc.h b/third_party/base/allocator/partition_allocator/partition_alloc.h
deleted file mode 100644
index e3ce36c..0000000
--- a/third_party/base/allocator/partition_allocator/partition_alloc.h
+++ /dev/null
@@ -1,529 +0,0 @@
-// Copyright (c) 2013 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_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_H_
-
-// DESCRIPTION
-// PartitionRoot::Alloc() / PartitionRootGeneric::Alloc() and PartitionFree() /
-// PartitionRootGeneric::Free() are approximately analagous to malloc() and
-// free().
-//
-// The main difference is that a PartitionRoot / PartitionRootGeneric object
-// must be supplied to these functions, representing a specific "heap partition"
-// that will be used to satisfy the allocation. Different partitions are
-// guaranteed to exist in separate address spaces, including being separate from
-// the main system heap. If the contained objects are all freed, physical memory
-// is returned to the system but the address space remains reserved.
-// See PartitionAlloc.md for other security properties PartitionAlloc provides.
-//
-// THE ONLY LEGITIMATE WAY TO OBTAIN A PartitionRoot IS THROUGH THE
-// SizeSpecificPartitionAllocator / PartitionAllocatorGeneric classes. To
-// minimize the instruction count to the fullest extent possible, the
-// PartitionRoot is really just a header adjacent to other data areas provided
-// by the allocator class.
-//
-// The PartitionRoot::Alloc() variant of the API has the following caveats:
-// - Allocations and frees against a single partition must be single threaded.
-// - Allocations must not exceed a max size, chosen at compile-time via a
-// templated parameter to PartitionAllocator.
-// - Allocation sizes must be aligned to the system pointer size.
-// - Allocations are bucketed exactly according to size.
-//
-// And for PartitionRootGeneric::Alloc():
-// - Multi-threaded use against a single partition is ok; locking is handled.
-// - Allocations of any arbitrary size can be handled (subject to a limit of
-// INT_MAX bytes for security reasons).
-// - Bucketing is by approximate size, for example an allocation of 4000 bytes
-// might be placed into a 4096-byte bucket. Bucket sizes are chosen to try and
-// keep worst-case waste to ~10%.
-//
-// The allocators are designed to be extremely fast, thanks to the following
-// properties and design:
-// - Just two single (reasonably predicatable) branches in the hot / fast path
-//   for both allocating and (significantly) freeing.
-// - A minimal number of operations in the hot / fast path, with the slow paths
-//   in separate functions, leading to the possibility of inlining.
-// - Each partition page (which is usually multiple physical pages) has a
-//   metadata structure which allows fast mapping of free() address to an
-//   underlying bucket.
-// - Supports a lock-free API for fast performance in single-threaded cases.
-// - The freelist for a given bucket is split across a number of partition
-//   pages, enabling various simple tricks to try and minimize fragmentation.
-// - Fine-grained bucket sizes leading to less waste and better packing.
-//
-// The following security properties could be investigated in the future:
-// - Per-object bucketing (instead of per-size) is mostly available at the API,
-// but not used yet.
-// - No randomness of freelist entries or bucket position.
-// - Better checking for wild pointers in free().
-// - Better freelist masking function to guarantee fault on 32-bit.
-
-#include <limits.h>
-#include <string.h>
-
-#include "build/build_config.h"
-#include "third_party/base/allocator/partition_allocator/page_allocator.h"
-#include "third_party/base/allocator/partition_allocator/partition_alloc_constants.h"
-#include "third_party/base/allocator/partition_allocator/partition_bucket.h"
-#include "third_party/base/allocator/partition_allocator/partition_cookie.h"
-#include "third_party/base/allocator/partition_allocator/partition_page.h"
-#include "third_party/base/allocator/partition_allocator/partition_root_base.h"
-#include "third_party/base/allocator/partition_allocator/spin_lock.h"
-#include "third_party/base/base_export.h"
-#include "third_party/base/bits.h"
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/stl_util.h"
-#include "third_party/base/sys_byteorder.h"
-
-#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-#include <stdlib.h>
-#endif
-
-// We use this to make MEMORY_TOOL_REPLACES_ALLOCATOR behave the same for max
-// size as other alloc code.
-#define CHECK_MAX_SIZE_OR_RETURN_NULLPTR(size, flags) \
-  if (size > kGenericMaxDirectMapped) {               \
-    if (flags & PartitionAllocReturnNull) {           \
-      return nullptr;                                 \
-    }                                                 \
-    CHECK(false);                                     \
-  }
-
-namespace pdfium {
-namespace base {
-
-class PartitionStatsDumper;
-
-enum PartitionPurgeFlags {
-  // Decommitting the ring list of empty pages is reasonably fast.
-  PartitionPurgeDecommitEmptyPages = 1 << 0,
-  // Discarding unused system pages is slower, because it involves walking all
-  // freelists in all active partition pages of all buckets >= system page
-  // size. It often frees a similar amount of memory to decommitting the empty
-  // pages, though.
-  PartitionPurgeDiscardUnusedSystemPages = 1 << 1,
-};
-
-// Never instantiate a PartitionRoot directly, instead use PartitionAlloc.
-struct BASE_EXPORT PartitionRoot : public internal::PartitionRootBase {
-  PartitionRoot();
-  ~PartitionRoot() override;
-  // This references the buckets OFF the edge of this struct. All uses of
-  // PartitionRoot must have the bucket array come right after.
-  //
-  // The PartitionAlloc templated class ensures the following is correct.
-  ALWAYS_INLINE internal::PartitionBucket* buckets() {
-    return reinterpret_cast<internal::PartitionBucket*>(this + 1);
-  }
-  ALWAYS_INLINE const internal::PartitionBucket* buckets() const {
-    return reinterpret_cast<const internal::PartitionBucket*>(this + 1);
-  }
-
-  void Init(size_t bucket_count, size_t maximum_allocation);
-
-  ALWAYS_INLINE void* Alloc(size_t size, const char* type_name);
-  ALWAYS_INLINE void* AllocFlags(int flags, size_t size, const char* type_name);
-
-  void PurgeMemory(int flags) override;
-
-  void DumpStats(const char* partition_name,
-                 bool is_light_dump,
-                 PartitionStatsDumper* dumper);
-};
-
-// Never instantiate a PartitionRootGeneric directly, instead use
-// PartitionAllocatorGeneric.
-struct BASE_EXPORT PartitionRootGeneric : public internal::PartitionRootBase {
-  PartitionRootGeneric();
-  ~PartitionRootGeneric() override;
-  subtle::SpinLock lock;
-  // Some pre-computed constants.
-  size_t order_index_shifts[kBitsPerSizeT + 1] = {};
-  size_t order_sub_index_masks[kBitsPerSizeT + 1] = {};
-  // The bucket lookup table lets us map a size_t to a bucket quickly.
-  // The trailing +1 caters for the overflow case for very large allocation
-  // sizes.  It is one flat array instead of a 2D array because in the 2D
-  // world, we'd need to index array[blah][max+1] which risks undefined
-  // behavior.
-  internal::PartitionBucket*
-      bucket_lookups[((kBitsPerSizeT + 1) * kGenericNumBucketsPerOrder) + 1] =
-          {};
-  internal::PartitionBucket buckets[kGenericNumBuckets] = {};
-
-  // Public API.
-  void Init();
-
-  ALWAYS_INLINE void* Alloc(size_t size, const char* type_name);
-  ALWAYS_INLINE void* AllocFlags(int flags, size_t size, const char* type_name);
-  ALWAYS_INLINE void Free(void* ptr);
-
-  NOINLINE void* Realloc(void* ptr, size_t new_size, const char* type_name);
-  // Overload that may return nullptr if reallocation isn't possible. In this
-  // case, |ptr| remains valid.
-  NOINLINE void* TryRealloc(void* ptr, size_t new_size, const char* type_name);
-
-  ALWAYS_INLINE size_t ActualSize(size_t size);
-
-  void PurgeMemory(int flags) override;
-
-  void DumpStats(const char* partition_name,
-                 bool is_light_dump,
-                 PartitionStatsDumper* partition_stats_dumper);
-};
-
-// Struct used to retrieve total memory usage of a partition. Used by
-// PartitionStatsDumper implementation.
-struct PartitionMemoryStats {
-  size_t total_mmapped_bytes;    // Total bytes mmaped from the system.
-  size_t total_committed_bytes;  // Total size of commmitted pages.
-  size_t total_resident_bytes;   // Total bytes provisioned by the partition.
-  size_t total_active_bytes;     // Total active bytes in the partition.
-  size_t total_decommittable_bytes;  // Total bytes that could be decommitted.
-  size_t total_discardable_bytes;    // Total bytes that could be discarded.
-};
-
-// Struct used to retrieve memory statistics about a partition bucket. Used by
-// PartitionStatsDumper implementation.
-struct PartitionBucketMemoryStats {
-  bool is_valid;       // Used to check if the stats is valid.
-  bool is_direct_map;  // True if this is a direct mapping; size will not be
-                       // unique.
-  uint32_t bucket_slot_size;     // The size of the slot in bytes.
-  uint32_t allocated_page_size;  // Total size the partition page allocated from
-                                 // the system.
-  uint32_t active_bytes;         // Total active bytes used in the bucket.
-  uint32_t resident_bytes;       // Total bytes provisioned in the bucket.
-  uint32_t decommittable_bytes;  // Total bytes that could be decommitted.
-  uint32_t discardable_bytes;    // Total bytes that could be discarded.
-  uint32_t num_full_pages;       // Number of pages with all slots allocated.
-  uint32_t num_active_pages;     // Number of pages that have at least one
-                                 // provisioned slot.
-  uint32_t num_empty_pages;      // Number of pages that are empty
-                                 // but not decommitted.
-  uint32_t num_decommitted_pages;  // Number of pages that are empty
-                                   // and decommitted.
-};
-
-// Interface that is passed to PartitionDumpStats and
-// PartitionDumpStatsGeneric for using the memory statistics.
-class BASE_EXPORT PartitionStatsDumper {
- public:
-  // Called to dump total memory used by partition, once per partition.
-  virtual void PartitionDumpTotals(const char* partition_name,
-                                   const PartitionMemoryStats*) = 0;
-
-  // Called to dump stats about buckets, for each bucket.
-  virtual void PartitionsDumpBucketStats(const char* partition_name,
-                                         const PartitionBucketMemoryStats*) = 0;
-};
-
-BASE_EXPORT void PartitionAllocGlobalInit(void (*oom_handling_function)());
-
-// PartitionAlloc supports setting hooks to observe allocations/frees as they
-// occur as well as 'override' hooks that allow overriding those operations.
-class BASE_EXPORT PartitionAllocHooks {
- public:
-  // Log allocation and free events.
-  typedef void AllocationObserverHook(void* address,
-                                      size_t size,
-                                      const char* type_name);
-  typedef void FreeObserverHook(void* address);
-
-  // If it returns true, the allocation has been overridden with the pointer in
-  // *out.
-  typedef bool AllocationOverrideHook(void** out,
-                                      int flags,
-                                      size_t size,
-                                      const char* type_name);
-  // If it returns true, then the allocation was overridden and has been freed.
-  typedef bool FreeOverrideHook(void* address);
-  // If it returns true, the underlying allocation is overridden and *out holds
-  // the size of the underlying allocation.
-  typedef bool ReallocOverrideHook(size_t* out, void* address);
-
-  // To unhook, call Set*Hooks with nullptrs.
-  static void SetObserverHooks(AllocationObserverHook* alloc_hook,
-                               FreeObserverHook* free_hook);
-  static void SetOverrideHooks(AllocationOverrideHook* alloc_hook,
-                               FreeOverrideHook* free_hook,
-                               ReallocOverrideHook realloc_hook);
-
-  // Helper method to check whether hooks are enabled. This is an optimization
-  // so that if a function needs to call observer and override hooks in two
-  // different places this value can be cached and only loaded once.
-  static bool AreHooksEnabled() {
-    return hooks_enabled_.load(std::memory_order_relaxed);
-  }
-
-  static void AllocationObserverHookIfEnabled(void* address,
-                                              size_t size,
-                                              const char* type_name);
-  static bool AllocationOverrideHookIfEnabled(void** out,
-                                              int flags,
-                                              size_t size,
-                                              const char* type_name);
-
-  static void FreeObserverHookIfEnabled(void* address);
-  static bool FreeOverrideHookIfEnabled(void* address);
-
-  static void ReallocObserverHookIfEnabled(void* old_address,
-                                           void* new_address,
-                                           size_t size,
-                                           const char* type_name);
-  static bool ReallocOverrideHookIfEnabled(size_t* out, void* address);
-
- private:
-  // Single bool that is used to indicate whether observer or allocation hooks
-  // are set to reduce the numbers of loads required to check whether hooking is
-  // enabled.
-  static std::atomic<bool> hooks_enabled_;
-
-  // Lock used to synchronize Set*Hooks calls.
-  static subtle::SpinLock set_hooks_lock_;
-
-  static std::atomic<AllocationObserverHook*> allocation_observer_hook_;
-  static std::atomic<FreeObserverHook*> free_observer_hook_;
-
-  static std::atomic<AllocationOverrideHook*> allocation_override_hook_;
-  static std::atomic<FreeOverrideHook*> free_override_hook_;
-  static std::atomic<ReallocOverrideHook*> realloc_override_hook_;
-};
-
-ALWAYS_INLINE void* PartitionRoot::Alloc(size_t size, const char* type_name) {
-  return AllocFlags(0, size, type_name);
-}
-
-ALWAYS_INLINE void* PartitionRoot::AllocFlags(int flags,
-                                              size_t size,
-                                              const char* type_name) {
-#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-  CHECK_MAX_SIZE_OR_RETURN_NULLPTR(size, flags);
-  void* result = malloc(size);
-  CHECK(result);
-  return result;
-#else
-  DCHECK(max_allocation == 0 || size <= max_allocation);
-  void* result;
-  const bool hooks_enabled = PartitionAllocHooks::AreHooksEnabled();
-  if (UNLIKELY(hooks_enabled)) {
-    if (PartitionAllocHooks::AllocationOverrideHookIfEnabled(&result, flags,
-                                                             size, type_name)) {
-      PartitionAllocHooks::AllocationObserverHookIfEnabled(result, size,
-                                                           type_name);
-      return result;
-    }
-  }
-  size_t requested_size = size;
-  size = internal::PartitionCookieSizeAdjustAdd(size);
-  DCHECK(initialized);
-  size_t index = size >> kBucketShift;
-  DCHECK(index < num_buckets);
-  DCHECK(size == index << kBucketShift);
-  internal::PartitionBucket* bucket = &buckets()[index];
-  result = AllocFromBucket(bucket, flags, size);
-  if (UNLIKELY(hooks_enabled)) {
-    PartitionAllocHooks::AllocationObserverHookIfEnabled(result, requested_size,
-                                                         type_name);
-  }
-  return result;
-#endif  // defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-}
-
-ALWAYS_INLINE bool PartitionAllocSupportsGetSize() {
-#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-  return false;
-#else
-  return true;
-#endif
-}
-
-ALWAYS_INLINE size_t PartitionAllocGetSize(void* ptr) {
-  // No need to lock here. Only |ptr| being freed by another thread could
-  // cause trouble, and the caller is responsible for that not happening.
-  DCHECK(PartitionAllocSupportsGetSize());
-  ptr = internal::PartitionCookieFreePointerAdjust(ptr);
-  internal::PartitionPage* page = internal::PartitionPage::FromPointer(ptr);
-  // TODO(palmer): See if we can afford to make this a CHECK.
-  DCHECK(internal::PartitionRootBase::IsValidPage(page));
-  size_t size = page->bucket->slot_size;
-  return internal::PartitionCookieSizeAdjustSubtract(size);
-}
-
-ALWAYS_INLINE void PartitionFree(void* ptr) {
-#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-  free(ptr);
-#else
-  // TODO(palmer): Check ptr alignment before continuing. Shall we do the check
-  // inside PartitionCookieFreePointerAdjust?
-  if (PartitionAllocHooks::AreHooksEnabled()) {
-    PartitionAllocHooks::FreeObserverHookIfEnabled(ptr);
-    if (PartitionAllocHooks::FreeOverrideHookIfEnabled(ptr))
-      return;
-  }
-
-  ptr = internal::PartitionCookieFreePointerAdjust(ptr);
-  internal::PartitionPage* page = internal::PartitionPage::FromPointer(ptr);
-  // TODO(palmer): See if we can afford to make this a CHECK.
-  DCHECK(internal::PartitionRootBase::IsValidPage(page));
-  page->Free(ptr);
-#endif
-}
-
-ALWAYS_INLINE internal::PartitionBucket* PartitionGenericSizeToBucket(
-    PartitionRootGeneric* root,
-    size_t size) {
-  size_t order = kBitsPerSizeT - bits::CountLeadingZeroBitsSizeT(size);
-  // The order index is simply the next few bits after the most significant bit.
-  size_t order_index = (size >> root->order_index_shifts[order]) &
-                       (kGenericNumBucketsPerOrder - 1);
-  // And if the remaining bits are non-zero we must bump the bucket up.
-  size_t sub_order_index = size & root->order_sub_index_masks[order];
-  internal::PartitionBucket* bucket =
-      root->bucket_lookups[(order << kGenericNumBucketsPerOrderBits) +
-                           order_index + !!sub_order_index];
-  CHECK(bucket);
-  DCHECK(!bucket->slot_size || bucket->slot_size >= size);
-  DCHECK(!(bucket->slot_size % kGenericSmallestBucket));
-  return bucket;
-}
-
-ALWAYS_INLINE void* PartitionAllocGenericFlags(PartitionRootGeneric* root,
-                                               int flags,
-                                               size_t size,
-                                               const char* type_name) {
-  DCHECK(flags < PartitionAllocLastFlag << 1);
-
-#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-  CHECK_MAX_SIZE_OR_RETURN_NULLPTR(size, flags);
-  const bool zero_fill = flags & PartitionAllocZeroFill;
-  void* result = zero_fill ? calloc(1, size) : malloc(size);
-  CHECK(result || flags & PartitionAllocReturnNull);
-  return result;
-#else
-  DCHECK(root->initialized);
-  // Only SizeSpecificPartitionAllocator should use max_allocation.
-  DCHECK(root->max_allocation == 0);
-  void* result;
-  const bool hooks_enabled = PartitionAllocHooks::AreHooksEnabled();
-  if (UNLIKELY(hooks_enabled)) {
-    if (PartitionAllocHooks::AllocationOverrideHookIfEnabled(&result, flags,
-                                                             size, type_name)) {
-      PartitionAllocHooks::AllocationObserverHookIfEnabled(result, size,
-                                                           type_name);
-      return result;
-    }
-  }
-  size_t requested_size = size;
-  size = internal::PartitionCookieSizeAdjustAdd(size);
-  internal::PartitionBucket* bucket = PartitionGenericSizeToBucket(root, size);
-  {
-    subtle::SpinLock::Guard guard(root->lock);
-    result = root->AllocFromBucket(bucket, flags, size);
-  }
-  if (UNLIKELY(hooks_enabled)) {
-    PartitionAllocHooks::AllocationObserverHookIfEnabled(result, requested_size,
-                                                         type_name);
-  }
-
-  return result;
-#endif
-}
-
-ALWAYS_INLINE void* PartitionRootGeneric::Alloc(size_t size,
-                                                const char* type_name) {
-  return PartitionAllocGenericFlags(this, 0, size, type_name);
-}
-
-ALWAYS_INLINE void* PartitionRootGeneric::AllocFlags(int flags,
-                                                     size_t size,
-                                                     const char* type_name) {
-  return PartitionAllocGenericFlags(this, flags, size, type_name);
-}
-
-ALWAYS_INLINE void PartitionRootGeneric::Free(void* ptr) {
-#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-  free(ptr);
-#else
-  DCHECK(initialized);
-
-  if (UNLIKELY(!ptr))
-    return;
-
-  if (PartitionAllocHooks::AreHooksEnabled()) {
-    PartitionAllocHooks::FreeObserverHookIfEnabled(ptr);
-    if (PartitionAllocHooks::FreeOverrideHookIfEnabled(ptr))
-      return;
-  }
-
-  ptr = internal::PartitionCookieFreePointerAdjust(ptr);
-  internal::PartitionPage* page = internal::PartitionPage::FromPointer(ptr);
-  // TODO(palmer): See if we can afford to make this a CHECK.
-  DCHECK(IsValidPage(page));
-  {
-    subtle::SpinLock::Guard guard(lock);
-    page->Free(ptr);
-  }
-#endif
-}
-
-BASE_EXPORT void* PartitionReallocGenericFlags(PartitionRootGeneric* root,
-                                               int flags,
-                                               void* ptr,
-                                               size_t new_size,
-                                               const char* type_name);
-
-ALWAYS_INLINE size_t PartitionRootGeneric::ActualSize(size_t size) {
-#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-  return size;
-#else
-  DCHECK(initialized);
-  size = internal::PartitionCookieSizeAdjustAdd(size);
-  internal::PartitionBucket* bucket = PartitionGenericSizeToBucket(this, size);
-  if (LIKELY(!bucket->is_direct_mapped())) {
-    size = bucket->slot_size;
-  } else if (size > kGenericMaxDirectMapped) {
-    // Too large to allocate => return the size unchanged.
-  } else {
-    size = internal::PartitionBucket::get_direct_map_size(size);
-  }
-  return internal::PartitionCookieSizeAdjustSubtract(size);
-#endif
-}
-
-template <size_t N>
-class SizeSpecificPartitionAllocator {
- public:
-  SizeSpecificPartitionAllocator() {
-    memset(actual_buckets_, 0,
-           sizeof(internal::PartitionBucket) * pdfium::size(actual_buckets_));
-  }
-  ~SizeSpecificPartitionAllocator() = default;
-  static const size_t kMaxAllocation = N - kAllocationGranularity;
-  static const size_t kNumBuckets = N / kAllocationGranularity;
-  void init() { partition_root_.Init(kNumBuckets, kMaxAllocation); }
-  ALWAYS_INLINE PartitionRoot* root() { return &partition_root_; }
-
- private:
-  PartitionRoot partition_root_;
-  internal::PartitionBucket actual_buckets_[kNumBuckets];
-};
-
-class BASE_EXPORT PartitionAllocatorGeneric {
- public:
-  PartitionAllocatorGeneric();
-  ~PartitionAllocatorGeneric();
-
-  void init() { partition_root_.Init(); }
-  ALWAYS_INLINE PartitionRootGeneric* root() { return &partition_root_; }
-
- private:
-  PartitionRootGeneric partition_root_;
-};
-
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_alloc_constants.h b/third_party/base/allocator/partition_allocator/partition_alloc_constants.h
deleted file mode 100644
index 8ebc4f5..0000000
--- a/third_party/base/allocator/partition_allocator/partition_alloc_constants.h
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright (c) 2018 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_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CONSTANTS_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CONSTANTS_H_
-
-#include <limits.h>
-
-#include "build/build_config.h"
-#include "third_party/base/allocator/partition_allocator/page_allocator_constants.h"
-#include "third_party/base/logging.h"
-
-namespace pdfium {
-namespace base {
-
-// Allocation granularity of sizeof(void*) bytes.
-static const size_t kAllocationGranularity = sizeof(void*);
-static const size_t kAllocationGranularityMask = kAllocationGranularity - 1;
-static const size_t kBucketShift = (kAllocationGranularity == 8) ? 3 : 2;
-
-// Underlying partition storage pages (`PartitionPage`s) are a power-of-2 size.
-// It is typical for a `PartitionPage` to be based on multiple system pages.
-// Most references to "page" refer to `PartitionPage`s.
-//
-// *Super pages* are the underlying system allocations we make. Super pages
-// contain multiple partition pages and include space for a small amount of
-// metadata per partition page.
-//
-// Inside super pages, we store *slot spans*. A slot span is a continguous range
-// of one or more `PartitionPage`s that stores allocations of the same size.
-// Slot span sizes are adjusted depending on the allocation size, to make sure
-// the packing does not lead to unused (wasted) space at the end of the last
-// system page of the span. For our current maximum slot span size of 64 KiB and
-// other constant values, we pack _all_ `PartitionRootGeneric::Alloc` sizes
-// perfectly up against the end of a system page.
-
-#if defined(_MIPS_ARCH_LOONGSON)
-static const size_t kPartitionPageShift = 16;  // 64 KiB
-#elif defined(ARCH_CPU_PPC64)
-static const size_t kPartitionPageShift = 18;  // 256 KiB
-#else
-static const size_t kPartitionPageShift = 14;  // 16 KiB
-#endif
-static const size_t kPartitionPageSize = 1 << kPartitionPageShift;
-static const size_t kPartitionPageOffsetMask = kPartitionPageSize - 1;
-static const size_t kPartitionPageBaseMask = ~kPartitionPageOffsetMask;
-// TODO: Should this be 1 if defined(_MIPS_ARCH_LOONGSON)?
-static const size_t kMaxPartitionPagesPerSlotSpan = 4;
-
-// To avoid fragmentation via never-used freelist entries, we hand out partition
-// freelist sections gradually, in units of the dominant system page size. What
-// we're actually doing is avoiding filling the full `PartitionPage` (16 KiB)
-// with freelist pointers right away. Writing freelist pointers will fault and
-// dirty a private page, which is very wasteful if we never actually store
-// objects there.
-
-static const size_t kNumSystemPagesPerPartitionPage =
-    kPartitionPageSize / kSystemPageSize;
-static const size_t kMaxSystemPagesPerSlotSpan =
-    kNumSystemPagesPerPartitionPage * kMaxPartitionPagesPerSlotSpan;
-
-// We reserve virtual address space in 2 MiB chunks (aligned to 2 MiB as well).
-// These chunks are called *super pages*. We do this so that we can store
-// metadata in the first few pages of each 2 MiB-aligned section. This makes
-// freeing memory very fast. We specifically choose 2 MiB because this virtual
-// address block represents a full but single PTE allocation on ARM, ia32 and
-// x64.
-//
-// The layout of the super page is as follows. The sizes below are the same for
-// 32- and 64-bit platforms.
-//
-//     +-----------------------+
-//     | Guard page (4 KiB)    |
-//     | Metadata page (4 KiB) |
-//     | Guard pages (8 KiB)   |
-//     | Slot span             |
-//     | Slot span             |
-//     | ...                   |
-//     | Slot span             |
-//     | Guard page (4 KiB)    |
-//     +-----------------------+
-//
-// Each slot span is a contiguous range of one or more `PartitionPage`s.
-//
-// The metadata page has the following format. Note that the `PartitionPage`
-// that is not at the head of a slot span is "unused". In other words, the
-// metadata for the slot span is stored only in the first `PartitionPage` of the
-// slot span. Metadata accesses to other `PartitionPage`s are redirected to the
-// first `PartitionPage`.
-//
-//     +---------------------------------------------+
-//     | SuperPageExtentEntry (32 B)                 |
-//     | PartitionPage of slot span 1 (32 B, used)   |
-//     | PartitionPage of slot span 1 (32 B, unused) |
-//     | PartitionPage of slot span 1 (32 B, unused) |
-//     | PartitionPage of slot span 2 (32 B, used)   |
-//     | PartitionPage of slot span 3 (32 B, used)   |
-//     | ...                                         |
-//     | PartitionPage of slot span N (32 B, unused) |
-//     +---------------------------------------------+
-//
-// A direct-mapped page has a similar layout to fake it looking like a super
-// page:
-//
-//     +-----------------------+
-//     | Guard page (4 KiB)    |
-//     | Metadata page (4 KiB) |
-//     | Guard pages (8 KiB)   |
-//     | Direct mapped object  |
-//     | Guard page (4 KiB)    |
-//     +-----------------------+
-//
-// A direct-mapped page's metadata page has the following layout:
-//
-//     +--------------------------------+
-//     | SuperPageExtentEntry (32 B)    |
-//     | PartitionPage (32 B)           |
-//     | PartitionBucket (32 B)         |
-//     | PartitionDirectMapExtent (8 B) |
-//     +--------------------------------+
-
-static const size_t kSuperPageShift = 21;  // 2 MiB
-static const size_t kSuperPageSize = 1 << kSuperPageShift;
-static const size_t kSuperPageOffsetMask = kSuperPageSize - 1;
-static const size_t kSuperPageBaseMask = ~kSuperPageOffsetMask;
-static const size_t kNumPartitionPagesPerSuperPage =
-    kSuperPageSize / kPartitionPageSize;
-
-// The following kGeneric* constants apply to the generic variants of the API.
-// The "order" of an allocation is closely related to the power-of-1 size of the
-// allocation. More precisely, the order is the bit index of the
-// most-significant-bit in the allocation size, where the bit numbers starts at
-// index 1 for the least-significant-bit.
-//
-// In terms of allocation sizes, order 0 covers 0, order 1 covers 1, order 2
-// covers 2->3, order 3 covers 4->7, order 4 covers 8->15.
-
-static const size_t kGenericMinBucketedOrder = 4;  // 8 bytes.
-// The largest bucketed order is 1 << (20 - 1), storing [512 KiB, 1 MiB):
-static const size_t kGenericMaxBucketedOrder = 20;
-static const size_t kGenericNumBucketedOrders =
-    (kGenericMaxBucketedOrder - kGenericMinBucketedOrder) + 1;
-// Eight buckets per order (for the higher orders), e.g. order 8 is 128, 144,
-// 160, ..., 240:
-static const size_t kGenericNumBucketsPerOrderBits = 3;
-static const size_t kGenericNumBucketsPerOrder =
-    1 << kGenericNumBucketsPerOrderBits;
-static const size_t kGenericNumBuckets =
-    kGenericNumBucketedOrders * kGenericNumBucketsPerOrder;
-static const size_t kGenericSmallestBucket = 1
-                                             << (kGenericMinBucketedOrder - 1);
-static const size_t kGenericMaxBucketSpacing =
-    1 << ((kGenericMaxBucketedOrder - 1) - kGenericNumBucketsPerOrderBits);
-static const size_t kGenericMaxBucketed =
-    (1 << (kGenericMaxBucketedOrder - 1)) +
-    ((kGenericNumBucketsPerOrder - 1) * kGenericMaxBucketSpacing);
-// Limit when downsizing a direct mapping using `realloc`:
-static const size_t kGenericMinDirectMappedDownsize = kGenericMaxBucketed + 1;
-static const size_t kGenericMaxDirectMapped =
-    (1UL << 31) + kPageAllocationGranularity;  // 2 GiB plus 1 more page.
-static const size_t kBitsPerSizeT = sizeof(void*) * CHAR_BIT;
-
-// Constant for the memory reclaim logic.
-static const size_t kMaxFreeableSpans = 16;
-
-// If the total size in bytes of allocated but not committed pages exceeds this
-// value (probably it is a "out of virtual address space" crash), a special
-// crash stack trace is generated at
-// `PartitionOutOfMemoryWithLotsOfUncommitedPages`. This is to distinguish "out
-// of virtual address space" from "out of physical memory" in crash reports.
-static const size_t kReasonableSizeOfUnusedPages = 1024 * 1024 * 1024;  // 1 GiB
-
-// These byte values match tcmalloc.
-static const unsigned char kUninitializedByte = 0xAB;
-static const unsigned char kFreedByte = 0xCD;
-
-// Flags for `PartitionAllocGenericFlags`.
-enum PartitionAllocFlags {
-  PartitionAllocReturnNull = 1 << 0,
-  PartitionAllocZeroFill = 1 << 1,
-
-  PartitionAllocLastFlag = PartitionAllocZeroFill
-};
-
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CONSTANTS_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_bucket.cc b/third_party/base/allocator/partition_allocator/partition_bucket.cc
deleted file mode 100644
index 7b24c90..0000000
--- a/third_party/base/allocator/partition_allocator/partition_bucket.cc
+++ /dev/null
@@ -1,569 +0,0 @@
-// Copyright (c) 2018 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.
-
-#include "third_party/base/allocator/partition_allocator/partition_bucket.h"
-
-#include "build/build_config.h"
-#include "third_party/base/allocator/partition_allocator/oom.h"
-#include "third_party/base/allocator/partition_allocator/page_allocator.h"
-#include "third_party/base/allocator/partition_allocator/partition_alloc_constants.h"
-#include "third_party/base/allocator/partition_allocator/partition_direct_map_extent.h"
-#include "third_party/base/allocator/partition_allocator/partition_oom.h"
-#include "third_party/base/allocator/partition_allocator/partition_page.h"
-#include "third_party/base/allocator/partition_allocator/partition_root_base.h"
-
-namespace pdfium {
-namespace base {
-namespace internal {
-
-namespace {
-
-ALWAYS_INLINE PartitionPage* PartitionDirectMap(PartitionRootBase* root,
-                                                int flags,
-                                                size_t raw_size) {
-  size_t size = PartitionBucket::get_direct_map_size(raw_size);
-
-  // Because we need to fake looking like a super page, we need to allocate
-  // a bunch of system pages more than "size":
-  // - The first few system pages are the partition page in which the super
-  // page metadata is stored. We fault just one system page out of a partition
-  // page sized clump.
-  // - We add a trailing guard page on 32-bit (on 64-bit we rely on the
-  // massive address space plus randomization instead).
-  size_t map_size = size + kPartitionPageSize;
-#if !defined(ARCH_CPU_64_BITS)
-  map_size += kSystemPageSize;
-#endif
-  // Round up to the allocation granularity.
-  map_size += kPageAllocationGranularityOffsetMask;
-  map_size &= kPageAllocationGranularityBaseMask;
-
-  char* ptr = reinterpret_cast<char*>(AllocPages(nullptr, map_size,
-                                                 kSuperPageSize, PageReadWrite,
-                                                 PageTag::kPartitionAlloc));
-  if (UNLIKELY(!ptr))
-    return nullptr;
-
-  size_t committed_page_size = size + kSystemPageSize;
-  root->total_size_of_direct_mapped_pages += committed_page_size;
-  root->IncreaseCommittedPages(committed_page_size);
-
-  char* slot = ptr + kPartitionPageSize;
-  SetSystemPagesAccess(ptr + (kSystemPageSize * 2),
-                       kPartitionPageSize - (kSystemPageSize * 2),
-                       PageInaccessible);
-#if !defined(ARCH_CPU_64_BITS)
-  SetSystemPagesAccess(ptr, kSystemPageSize, PageInaccessible);
-  SetSystemPagesAccess(slot + size, kSystemPageSize, PageInaccessible);
-#endif
-
-  PartitionSuperPageExtentEntry* extent =
-      reinterpret_cast<PartitionSuperPageExtentEntry*>(
-          PartitionSuperPageToMetadataArea(ptr));
-  extent->root = root;
-  // The new structures are all located inside a fresh system page so they
-  // will all be zeroed out. These DCHECKs are for documentation.
-  DCHECK(!extent->super_page_base);
-  DCHECK(!extent->super_pages_end);
-  DCHECK(!extent->next);
-  PartitionPage* page = PartitionPage::FromPointerNoAlignmentCheck(slot);
-  PartitionBucket* bucket = reinterpret_cast<PartitionBucket*>(
-      reinterpret_cast<char*>(page) + (kPageMetadataSize * 2));
-  DCHECK(!page->next_page);
-  DCHECK(!page->num_allocated_slots);
-  DCHECK(!page->num_unprovisioned_slots);
-  DCHECK(!page->page_offset);
-  DCHECK(!page->empty_cache_index);
-  page->bucket = bucket;
-  page->freelist_head = reinterpret_cast<PartitionFreelistEntry*>(slot);
-  PartitionFreelistEntry* next_entry =
-      reinterpret_cast<PartitionFreelistEntry*>(slot);
-  next_entry->next = PartitionFreelistEntry::Encode(nullptr);
-
-  DCHECK(!bucket->active_pages_head);
-  DCHECK(!bucket->empty_pages_head);
-  DCHECK(!bucket->decommitted_pages_head);
-  DCHECK(!bucket->num_system_pages_per_slot_span);
-  DCHECK(!bucket->num_full_pages);
-  bucket->slot_size = size;
-
-  PartitionDirectMapExtent* map_extent =
-      PartitionDirectMapExtent::FromPage(page);
-  map_extent->map_size = map_size - kPartitionPageSize - kSystemPageSize;
-  map_extent->bucket = bucket;
-
-  // Maintain the doubly-linked list of all direct mappings.
-  map_extent->next_extent = root->direct_map_list;
-  if (map_extent->next_extent)
-    map_extent->next_extent->prev_extent = map_extent;
-  map_extent->prev_extent = nullptr;
-  root->direct_map_list = map_extent;
-
-  return page;
-}
-
-}  // namespace
-
-// static
-PartitionBucket PartitionBucket::sentinel_bucket_;
-
-PartitionBucket* PartitionBucket::get_sentinel_bucket() {
-  return &sentinel_bucket_;
-}
-
-// TODO(ajwong): This seems to interact badly with
-// get_pages_per_slot_span() which rounds the value from this up to a
-// multiple of kNumSystemPagesPerPartitionPage (aka 4) anyways.
-// http://crbug.com/776537
-//
-// TODO(ajwong): The waste calculation seems wrong. The PTE usage should cover
-// both used and unsed pages.
-// http://crbug.com/776537
-uint8_t PartitionBucket::get_system_pages_per_slot_span() {
-  // This works out reasonably for the current bucket sizes of the generic
-  // allocator, and the current values of partition page size and constants.
-  // Specifically, we have enough room to always pack the slots perfectly into
-  // some number of system pages. The only waste is the waste associated with
-  // unfaulted pages (i.e. wasted address space).
-  // TODO: we end up using a lot of system pages for very small sizes. For
-  // example, we'll use 12 system pages for slot size 24. The slot size is
-  // so small that the waste would be tiny with just 4, or 1, system pages.
-  // Later, we can investigate whether there are anti-fragmentation benefits
-  // to using fewer system pages.
-  double best_waste_ratio = 1.0f;
-  uint16_t best_pages = 0;
-  if (slot_size > kMaxSystemPagesPerSlotSpan * kSystemPageSize) {
-    // TODO(ajwong): Why is there a DCHECK here for this?
-    // http://crbug.com/776537
-    DCHECK(!(slot_size % kSystemPageSize));
-    best_pages = static_cast<uint16_t>(slot_size / kSystemPageSize);
-    // TODO(ajwong): Should this be checking against
-    // kMaxSystemPagesPerSlotSpan or numeric_limits<uint8_t>::max?
-    // http://crbug.com/776537
-    CHECK(best_pages < (1 << 8));
-    return static_cast<uint8_t>(best_pages);
-  }
-  DCHECK(slot_size <= kMaxSystemPagesPerSlotSpan * kSystemPageSize);
-  for (uint16_t i = kNumSystemPagesPerPartitionPage - 1;
-       i <= kMaxSystemPagesPerSlotSpan; ++i) {
-    size_t page_size = kSystemPageSize * i;
-    size_t num_slots = page_size / slot_size;
-    size_t waste = page_size - (num_slots * slot_size);
-    // Leaving a page unfaulted is not free; the page will occupy an empty page
-    // table entry.  Make a simple attempt to account for that.
-    //
-    // TODO(ajwong): This looks wrong. PTEs are allocated for all pages
-    // regardless of whether or not they are wasted. Should it just
-    // be waste += i * sizeof(void*)?
-    // http://crbug.com/776537
-    size_t num_remainder_pages = i & (kNumSystemPagesPerPartitionPage - 1);
-    size_t num_unfaulted_pages =
-        num_remainder_pages
-            ? (kNumSystemPagesPerPartitionPage - num_remainder_pages)
-            : 0;
-    waste += sizeof(void*) * num_unfaulted_pages;
-    double waste_ratio =
-        static_cast<double>(waste) / static_cast<double>(page_size);
-    if (waste_ratio < best_waste_ratio) {
-      best_waste_ratio = waste_ratio;
-      best_pages = i;
-    }
-  }
-  DCHECK(best_pages > 0);
-  CHECK(best_pages <= kMaxSystemPagesPerSlotSpan);
-  return static_cast<uint8_t>(best_pages);
-}
-
-void PartitionBucket::Init(uint32_t new_slot_size) {
-  slot_size = new_slot_size;
-  active_pages_head = PartitionPage::get_sentinel_page();
-  empty_pages_head = nullptr;
-  decommitted_pages_head = nullptr;
-  num_full_pages = 0;
-  num_system_pages_per_slot_span = get_system_pages_per_slot_span();
-}
-
-NOINLINE void PartitionBucket::OnFull() {
-  OOM_CRASH();
-}
-
-ALWAYS_INLINE void* PartitionBucket::AllocNewSlotSpan(
-    PartitionRootBase* root,
-    int flags,
-    uint16_t num_partition_pages) {
-  DCHECK(!(reinterpret_cast<uintptr_t>(root->next_partition_page) %
-           kPartitionPageSize));
-  DCHECK(!(reinterpret_cast<uintptr_t>(root->next_partition_page_end) %
-           kPartitionPageSize));
-  DCHECK(num_partition_pages <= kNumPartitionPagesPerSuperPage);
-  size_t total_size = kPartitionPageSize * num_partition_pages;
-  size_t num_partition_pages_left =
-      (root->next_partition_page_end - root->next_partition_page) >>
-      kPartitionPageShift;
-  if (LIKELY(num_partition_pages_left >= num_partition_pages)) {
-    // In this case, we can still hand out pages from the current super page
-    // allocation.
-    char* ret = root->next_partition_page;
-
-    // Fresh System Pages in the SuperPages are decommited. Commit them
-    // before vending them back.
-    SetSystemPagesAccess(ret, total_size, PageReadWrite);
-
-    root->next_partition_page += total_size;
-    root->IncreaseCommittedPages(total_size);
-    return ret;
-  }
-
-  // Need a new super page. We want to allocate super pages in a continguous
-  // address region as much as possible. This is important for not causing
-  // page table bloat and not fragmenting address spaces in 32 bit
-  // architectures.
-  char* requested_address = root->next_super_page;
-  char* super_page = reinterpret_cast<char*>(
-      AllocPages(requested_address, kSuperPageSize, kSuperPageSize,
-                 PageReadWrite, PageTag::kPartitionAlloc));
-  if (UNLIKELY(!super_page))
-    return nullptr;
-
-  root->total_size_of_super_pages += kSuperPageSize;
-  root->IncreaseCommittedPages(total_size);
-
-  // |total_size| MUST be less than kSuperPageSize - (kPartitionPageSize*2).
-  // This is a trustworthy value because num_partition_pages is not user
-  // controlled.
-  //
-  // TODO(ajwong): Introduce a DCHECK.
-  root->next_super_page = super_page + kSuperPageSize;
-  char* ret = super_page + kPartitionPageSize;
-  root->next_partition_page = ret + total_size;
-  root->next_partition_page_end = root->next_super_page - kPartitionPageSize;
-  // Make the first partition page in the super page a guard page, but leave a
-  // hole in the middle.
-  // This is where we put page metadata and also a tiny amount of extent
-  // metadata.
-  SetSystemPagesAccess(super_page, kSystemPageSize, PageInaccessible);
-  SetSystemPagesAccess(super_page + (kSystemPageSize * 2),
-                       kPartitionPageSize - (kSystemPageSize * 2),
-                       PageInaccessible);
-  //  SetSystemPagesAccess(super_page + (kSuperPageSize -
-  //  kPartitionPageSize),
-  //                             kPartitionPageSize, PageInaccessible);
-  // All remaining slotspans for the unallocated PartitionPages inside the
-  // SuperPage are conceptually decommitted. Correctly set the state here
-  // so they do not occupy resources.
-  //
-  // TODO(ajwong): Refactor Page Allocator API so the SuperPage comes in
-  // decommited initially.
-  SetSystemPagesAccess(super_page + kPartitionPageSize + total_size,
-                       (kSuperPageSize - kPartitionPageSize - total_size),
-                       PageInaccessible);
-
-  // If we were after a specific address, but didn't get it, assume that
-  // the system chose a lousy address. Here most OS'es have a default
-  // algorithm that isn't randomized. For example, most Linux
-  // distributions will allocate the mapping directly before the last
-  // successful mapping, which is far from random. So we just get fresh
-  // randomness for the next mapping attempt.
-  if (requested_address && requested_address != super_page)
-    root->next_super_page = nullptr;
-
-  // We allocated a new super page so update super page metadata.
-  // First check if this is a new extent or not.
-  PartitionSuperPageExtentEntry* latest_extent =
-      reinterpret_cast<PartitionSuperPageExtentEntry*>(
-          PartitionSuperPageToMetadataArea(super_page));
-  // By storing the root in every extent metadata object, we have a fast way
-  // to go from a pointer within the partition to the root object.
-  latest_extent->root = root;
-  // Most new extents will be part of a larger extent, and these three fields
-  // are unused, but we initialize them to 0 so that we get a clear signal
-  // in case they are accidentally used.
-  latest_extent->super_page_base = nullptr;
-  latest_extent->super_pages_end = nullptr;
-  latest_extent->next = nullptr;
-
-  PartitionSuperPageExtentEntry* current_extent = root->current_extent;
-  bool is_new_extent = (super_page != requested_address);
-  if (UNLIKELY(is_new_extent)) {
-    if (UNLIKELY(!current_extent)) {
-      DCHECK(!root->first_extent);
-      root->first_extent = latest_extent;
-    } else {
-      DCHECK(current_extent->super_page_base);
-      current_extent->next = latest_extent;
-    }
-    root->current_extent = latest_extent;
-    latest_extent->super_page_base = super_page;
-    latest_extent->super_pages_end = super_page + kSuperPageSize;
-  } else {
-    // We allocated next to an existing extent so just nudge the size up a
-    // little.
-    DCHECK(current_extent->super_pages_end);
-    current_extent->super_pages_end += kSuperPageSize;
-    DCHECK(ret >= current_extent->super_page_base &&
-           ret < current_extent->super_pages_end);
-  }
-  return ret;
-}
-
-ALWAYS_INLINE uint16_t PartitionBucket::get_pages_per_slot_span() {
-  // Rounds up to nearest multiple of kNumSystemPagesPerPartitionPage.
-  return (num_system_pages_per_slot_span +
-          (kNumSystemPagesPerPartitionPage - 1)) /
-         kNumSystemPagesPerPartitionPage;
-}
-
-ALWAYS_INLINE void PartitionBucket::InitializeSlotSpan(PartitionPage* page) {
-  // The bucket never changes. We set it up once.
-  page->bucket = this;
-  page->empty_cache_index = -1;
-
-  page->Reset();
-
-  // If this page has just a single slot, do not set up page offsets for any
-  // page metadata other than the first one. This ensures that attempts to
-  // touch invalid page metadata fail.
-  if (page->num_unprovisioned_slots == 1)
-    return;
-
-  uint16_t num_partition_pages = get_pages_per_slot_span();
-  char* page_char_ptr = reinterpret_cast<char*>(page);
-  for (uint16_t i = 1; i < num_partition_pages; ++i) {
-    page_char_ptr += kPageMetadataSize;
-    PartitionPage* secondary_page =
-        reinterpret_cast<PartitionPage*>(page_char_ptr);
-    secondary_page->page_offset = i;
-  }
-}
-
-ALWAYS_INLINE char* PartitionBucket::AllocAndFillFreelist(PartitionPage* page) {
-  DCHECK(page != PartitionPage::get_sentinel_page());
-  uint16_t num_slots = page->num_unprovisioned_slots;
-  DCHECK(num_slots);
-  // We should only get here when _every_ slot is either used or unprovisioned.
-  // (The third state is "on the freelist". If we have a non-empty freelist, we
-  // should not get here.)
-  DCHECK(num_slots + page->num_allocated_slots == get_slots_per_span());
-  // Similarly, make explicitly sure that the freelist is empty.
-  DCHECK(!page->freelist_head);
-  DCHECK(page->num_allocated_slots >= 0);
-
-  size_t size = slot_size;
-  char* base = reinterpret_cast<char*>(PartitionPage::ToPointer(page));
-  char* return_object = base + (size * page->num_allocated_slots);
-  char* first_freelist_pointer = return_object + size;
-  char* first_freelist_pointer_extent =
-      first_freelist_pointer + sizeof(PartitionFreelistEntry*);
-  // Our goal is to fault as few system pages as possible. We calculate the
-  // page containing the "end" of the returned slot, and then allow freelist
-  // pointers to be written up to the end of that page.
-  char* sub_page_limit = reinterpret_cast<char*>(
-      RoundUpToSystemPage(reinterpret_cast<size_t>(first_freelist_pointer)));
-  char* slots_limit = return_object + (size * num_slots);
-  char* freelist_limit = sub_page_limit;
-  if (UNLIKELY(slots_limit < freelist_limit))
-    freelist_limit = slots_limit;
-
-  uint16_t num_new_freelist_entries = 0;
-  if (LIKELY(first_freelist_pointer_extent <= freelist_limit)) {
-    // Only consider used space in the slot span. If we consider wasted
-    // space, we may get an off-by-one when a freelist pointer fits in the
-    // wasted space, but a slot does not.
-    // We know we can fit at least one freelist pointer.
-    num_new_freelist_entries = 1;
-    // Any further entries require space for the whole slot span.
-    num_new_freelist_entries += static_cast<uint16_t>(
-        (freelist_limit - first_freelist_pointer_extent) / size);
-  }
-
-  // We always return an object slot -- that's the +1 below.
-  // We do not neccessarily create any new freelist entries, because we cross
-  // sub page boundaries frequently for large bucket sizes.
-  DCHECK(num_new_freelist_entries + 1 <= num_slots);
-  num_slots -= (num_new_freelist_entries + 1);
-  page->num_unprovisioned_slots = num_slots;
-  page->num_allocated_slots++;
-
-  if (LIKELY(num_new_freelist_entries)) {
-    char* freelist_pointer = first_freelist_pointer;
-    PartitionFreelistEntry* entry =
-        reinterpret_cast<PartitionFreelistEntry*>(freelist_pointer);
-    page->freelist_head = entry;
-    while (--num_new_freelist_entries) {
-      freelist_pointer += size;
-      PartitionFreelistEntry* next_entry =
-          reinterpret_cast<PartitionFreelistEntry*>(freelist_pointer);
-      entry->next = PartitionFreelistEntry::Encode(next_entry);
-      entry = next_entry;
-    }
-    entry->next = PartitionFreelistEntry::Encode(nullptr);
-  } else {
-    page->freelist_head = nullptr;
-  }
-  return return_object;
-}
-
-bool PartitionBucket::SetNewActivePage() {
-  PartitionPage* page = active_pages_head;
-  if (page == PartitionPage::get_sentinel_page())
-    return false;
-
-  PartitionPage* next_page;
-
-  for (; page; page = next_page) {
-    next_page = page->next_page;
-    DCHECK(page->bucket == this);
-    DCHECK(page != empty_pages_head);
-    DCHECK(page != decommitted_pages_head);
-
-    if (LIKELY(page->is_active())) {
-      // This page is usable because it has freelist entries, or has
-      // unprovisioned slots we can create freelist entries from.
-      active_pages_head = page;
-      return true;
-    }
-
-    // Deal with empty and decommitted pages.
-    if (LIKELY(page->is_empty())) {
-      page->next_page = empty_pages_head;
-      empty_pages_head = page;
-    } else if (LIKELY(page->is_decommitted())) {
-      page->next_page = decommitted_pages_head;
-      decommitted_pages_head = page;
-    } else {
-      DCHECK(page->is_full());
-      // If we get here, we found a full page. Skip over it too, and also
-      // tag it as full (via a negative value). We need it tagged so that
-      // free'ing can tell, and move it back into the active page list.
-      page->num_allocated_slots = -page->num_allocated_slots;
-      ++num_full_pages;
-      // num_full_pages is a uint16_t for efficient packing so guard against
-      // overflow to be safe.
-      if (UNLIKELY(!num_full_pages))
-        OnFull();
-      // Not necessary but might help stop accidents.
-      page->next_page = nullptr;
-    }
-  }
-
-  active_pages_head = PartitionPage::get_sentinel_page();
-  return false;
-}
-
-void* PartitionBucket::SlowPathAlloc(PartitionRootBase* root,
-                                     int flags,
-                                     size_t size,
-                                     bool* is_already_zeroed) {
-  // The slow path is called when the freelist is empty.
-  DCHECK(!active_pages_head->freelist_head);
-
-  PartitionPage* new_page = nullptr;
-  *is_already_zeroed = false;
-
-  // For the PartitionRootGeneric::Alloc() API, we have a bunch of buckets
-  // marked as special cases. We bounce them through to the slow path so that
-  // we can still have a blazing fast hot path due to lack of corner-case
-  // branches.
-  //
-  // Note: The ordering of the conditionals matter! In particular,
-  // SetNewActivePage() has a side-effect even when returning
-  // false where it sweeps the active page list and may move things into
-  // the empty or decommitted lists which affects the subsequent conditional.
-  bool return_null = flags & PartitionAllocReturnNull;
-  if (UNLIKELY(is_direct_mapped())) {
-    DCHECK(size > kGenericMaxBucketed);
-    DCHECK(this == get_sentinel_bucket());
-    DCHECK(active_pages_head == PartitionPage::get_sentinel_page());
-    if (size > kGenericMaxDirectMapped) {
-      if (return_null)
-        return nullptr;
-      PartitionExcessiveAllocationSize();
-    }
-    new_page = PartitionDirectMap(root, flags, size);
-#if !defined(OS_MACOSX)
-    // Turn off the optimization to see if it helps https://crbug.com/892550.
-    *is_already_zeroed = true;
-#endif
-  } else if (LIKELY(SetNewActivePage())) {
-    // First, did we find an active page in the active pages list?
-    new_page = active_pages_head;
-    DCHECK(new_page->is_active());
-  } else if (LIKELY(empty_pages_head != nullptr) ||
-             LIKELY(decommitted_pages_head != nullptr)) {
-    // Second, look in our lists of empty and decommitted pages.
-    // Check empty pages first, which are preferred, but beware that an
-    // empty page might have been decommitted.
-    while (LIKELY((new_page = empty_pages_head) != nullptr)) {
-      DCHECK(new_page->bucket == this);
-      DCHECK(new_page->is_empty() || new_page->is_decommitted());
-      empty_pages_head = new_page->next_page;
-      // Accept the empty page unless it got decommitted.
-      if (new_page->freelist_head) {
-        new_page->next_page = nullptr;
-        break;
-      }
-      DCHECK(new_page->is_decommitted());
-      new_page->next_page = decommitted_pages_head;
-      decommitted_pages_head = new_page;
-    }
-    if (UNLIKELY(!new_page) && LIKELY(decommitted_pages_head != nullptr)) {
-      new_page = decommitted_pages_head;
-      DCHECK(new_page->bucket == this);
-      DCHECK(new_page->is_decommitted());
-      decommitted_pages_head = new_page->next_page;
-      void* addr = PartitionPage::ToPointer(new_page);
-      root->RecommitSystemPages(addr, new_page->bucket->get_bytes_per_span());
-      new_page->Reset();
-      // TODO(https://crbug.com/890752): Optimizing here might cause pages to
-      // not be zeroed.
-      // *is_already_zeroed = true;
-    }
-    DCHECK(new_page);
-  } else {
-    // Third. If we get here, we need a brand new page.
-    uint16_t num_partition_pages = get_pages_per_slot_span();
-    void* raw_pages = AllocNewSlotSpan(root, flags, num_partition_pages);
-    if (LIKELY(raw_pages != nullptr)) {
-      new_page = PartitionPage::FromPointerNoAlignmentCheck(raw_pages);
-      InitializeSlotSpan(new_page);
-      // TODO(https://crbug.com/890752): Optimizing here causes pages to not be
-      // zeroed on at least macOS.
-      // *is_already_zeroed = true;
-    }
-  }
-
-  // Bail if we had a memory allocation failure.
-  if (UNLIKELY(!new_page)) {
-    DCHECK(active_pages_head == PartitionPage::get_sentinel_page());
-    if (return_null)
-      return nullptr;
-    root->OutOfMemory();
-  }
-
-  // TODO(ajwong): Is there a way to avoid the reading of bucket here?
-  // It seems like in many of the conditional branches above, |this| ==
-  // |new_page->bucket|. Maybe pull this into another function?
-  PartitionBucket* bucket = new_page->bucket;
-  DCHECK(bucket != get_sentinel_bucket());
-  bucket->active_pages_head = new_page;
-  new_page->set_raw_size(size);
-
-  // If we found an active page with free slots, or an empty page, we have a
-  // usable freelist head.
-  if (LIKELY(new_page->freelist_head != nullptr)) {
-    PartitionFreelistEntry* entry = new_page->freelist_head;
-    PartitionFreelistEntry* new_head =
-        EncodedPartitionFreelistEntry::Decode(entry->next);
-    new_page->freelist_head = new_head;
-    new_page->num_allocated_slots++;
-    return entry;
-  }
-  // Otherwise, we need to build the freelist.
-  DCHECK(new_page->num_unprovisioned_slots);
-  return AllocAndFillFreelist(new_page);
-}
-
-}  // namespace internal
-}  // namespace base
-}  // namespace pdfium
diff --git a/third_party/base/allocator/partition_allocator/partition_bucket.h b/third_party/base/allocator/partition_allocator/partition_bucket.h
deleted file mode 100644
index a89099b..0000000
--- a/third_party/base/allocator/partition_allocator/partition_bucket.h
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (c) 2018 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_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "third_party/base/allocator/partition_allocator/partition_alloc_constants.h"
-#include "third_party/base/base_export.h"
-#include "third_party/base/compiler_specific.h"
-
-namespace pdfium {
-namespace base {
-namespace internal {
-
-struct PartitionPage;
-struct PartitionRootBase;
-
-struct PartitionBucket {
-  // Accessed most in hot path => goes first.
-  PartitionPage* active_pages_head;
-
-  PartitionPage* empty_pages_head;
-  PartitionPage* decommitted_pages_head;
-  uint32_t slot_size;
-  uint32_t num_system_pages_per_slot_span : 8;
-  uint32_t num_full_pages : 24;
-
-  // Public API.
-  void Init(uint32_t new_slot_size);
-
-  // Sets |is_already_zeroed| to true if the allocation was satisfied by
-  // requesting (a) new page(s) from the operating system, or false otherwise.
-  // This enables an optimization for when callers use |PartitionAllocZeroFill|:
-  // there is no need to call memset on fresh pages; the OS has already zeroed
-  // them. (See |PartitionRootBase::AllocFromBucket|.)
-  //
-  // Note the matching Free() functions are in PartitionPage.
-  BASE_EXPORT NOINLINE void* SlowPathAlloc(PartitionRootBase* root,
-                                           int flags,
-                                           size_t size,
-                                           bool* is_already_zeroed);
-
-  ALWAYS_INLINE bool is_direct_mapped() const {
-    return !num_system_pages_per_slot_span;
-  }
-  ALWAYS_INLINE size_t get_bytes_per_span() const {
-    // TODO(ajwong): Change to CheckedMul. https://crbug.com/787153
-    // https://crbug.com/680657
-    return num_system_pages_per_slot_span * kSystemPageSize;
-  }
-  ALWAYS_INLINE uint16_t get_slots_per_span() const {
-    // TODO(ajwong): Change to CheckedMul. https://crbug.com/787153
-    // https://crbug.com/680657
-    return static_cast<uint16_t>(get_bytes_per_span() / slot_size);
-  }
-
-  static ALWAYS_INLINE size_t get_direct_map_size(size_t size) {
-    // Caller must check that the size is not above the kGenericMaxDirectMapped
-    // limit before calling. This also guards against integer overflow in the
-    // calculation here.
-    DCHECK(size <= kGenericMaxDirectMapped);
-    return (size + kSystemPageOffsetMask) & kSystemPageBaseMask;
-  }
-
-  // TODO(ajwong): Can this be made private?  https://crbug.com/787153
-  static PartitionBucket* get_sentinel_bucket();
-
-  // This helper function scans a bucket's active page list for a suitable new
-  // active page.  When it finds a suitable new active page (one that has
-  // free slots and is not empty), it is set as the new active page. If there
-  // is no suitable new active page, the current active page is set to
-  // PartitionPage::get_sentinel_page(). As potential pages are scanned, they
-  // are tidied up according to their state. Empty pages are swept on to the
-  // empty page list, decommitted pages on to the decommitted page list and full
-  // pages are unlinked from any list.
-  //
-  // This is where the guts of the bucket maintenance is done!
-  bool SetNewActivePage();
-
- private:
-  static void OutOfMemory(const PartitionRootBase* root);
-  static void OutOfMemoryWithLotsOfUncommitedPages();
-
-  static NOINLINE void OnFull();
-
-  // Returns a natural number of PartitionPages (calculated by
-  // get_system_pages_per_slot_span()) to allocate from the current
-  // SuperPage when the bucket runs out of slots.
-  ALWAYS_INLINE uint16_t get_pages_per_slot_span();
-
-  // Returns the number of system pages in a slot span.
-  //
-  // The calculation attemps to find the best number of System Pages to
-  // allocate for the given slot_size to minimize wasted space. It uses a
-  // heuristic that looks at number of bytes wasted after the last slot and
-  // attempts to account for the PTE usage of each System Page.
-  uint8_t get_system_pages_per_slot_span();
-
-  // Allocates a new slot span with size |num_partition_pages| from the
-  // current extent. Metadata within this slot span will be uninitialized.
-  // Returns nullptr on error.
-  ALWAYS_INLINE void* AllocNewSlotSpan(PartitionRootBase* root,
-                                       int flags,
-                                       uint16_t num_partition_pages);
-
-  // Each bucket allocates a slot span when it runs out of slots.
-  // A slot span's size is equal to get_pages_per_slot_span() number of
-  // PartitionPages. This function initializes all PartitionPage within the
-  // span to point to the first PartitionPage which holds all the metadata
-  // for the span and registers this bucket as the owner of the span. It does
-  // NOT put the slots into the bucket's freelist.
-  ALWAYS_INLINE void InitializeSlotSpan(PartitionPage* page);
-
-  // Allocates one slot from the given |page| and then adds the remainder to
-  // the current bucket. If the |page| was freshly allocated, it must have been
-  // passed through InitializeSlotSpan() first.
-  ALWAYS_INLINE char* AllocAndFillFreelist(PartitionPage* page);
-
-  static PartitionBucket sentinel_bucket_;
-};
-
-}  // namespace internal
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_cookie.h b/third_party/base/allocator/partition_allocator/partition_cookie.h
deleted file mode 100644
index 7cf4e84..0000000
--- a/third_party/base/allocator/partition_allocator/partition_cookie.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) 2018 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_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_COOKIE_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_COOKIE_H_
-
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/logging.h"
-
-namespace pdfium {
-namespace base {
-namespace internal {
-
-#if DCHECK_IS_ON()
-// Handles alignment up to XMM instructions on Intel.
-static constexpr size_t kCookieSize = 16;
-
-static constexpr unsigned char kCookieValue[kCookieSize] = {
-    0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xD0, 0x0D,
-    0x13, 0x37, 0xF0, 0x05, 0xBA, 0x11, 0xAB, 0x1E};
-#endif
-
-ALWAYS_INLINE void PartitionCookieCheckValue(void* ptr) {
-#if DCHECK_IS_ON()
-  unsigned char* cookie_ptr = reinterpret_cast<unsigned char*>(ptr);
-  for (size_t i = 0; i < kCookieSize; ++i, ++cookie_ptr)
-    DCHECK(*cookie_ptr == kCookieValue[i]);
-#endif
-}
-
-ALWAYS_INLINE size_t PartitionCookieSizeAdjustAdd(size_t size) {
-#if DCHECK_IS_ON()
-  // Add space for cookies, checking for integer overflow. TODO(palmer):
-  // Investigate the performance and code size implications of using
-  // CheckedNumeric throughout PA.
-  DCHECK(size + (2 * kCookieSize) > size);
-  size += 2 * kCookieSize;
-#endif
-  return size;
-}
-
-ALWAYS_INLINE void* PartitionCookieFreePointerAdjust(void* ptr) {
-#if DCHECK_IS_ON()
-  // The value given to the application is actually just after the cookie.
-  ptr = static_cast<char*>(ptr) - kCookieSize;
-#endif
-  return ptr;
-}
-
-ALWAYS_INLINE size_t PartitionCookieSizeAdjustSubtract(size_t size) {
-#if DCHECK_IS_ON()
-  // Remove space for cookies.
-  DCHECK(size >= 2 * kCookieSize);
-  size -= 2 * kCookieSize;
-#endif
-  return size;
-}
-
-ALWAYS_INLINE void PartitionCookieWriteValue(void* ptr) {
-#if DCHECK_IS_ON()
-  unsigned char* cookie_ptr = reinterpret_cast<unsigned char*>(ptr);
-  for (size_t i = 0; i < kCookieSize; ++i, ++cookie_ptr)
-    *cookie_ptr = kCookieValue[i];
-#endif
-}
-
-}  // namespace internal
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_COOKIE_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_direct_map_extent.h b/third_party/base/allocator/partition_allocator/partition_direct_map_extent.h
deleted file mode 100644
index 192c5b4..0000000
--- a/third_party/base/allocator/partition_allocator/partition_direct_map_extent.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2018 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_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_DIRECT_MAP_EXTENT_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_DIRECT_MAP_EXTENT_H_
-
-#include "third_party/base/allocator/partition_allocator/partition_bucket.h"
-#include "third_party/base/allocator/partition_allocator/partition_page.h"
-
-namespace pdfium {
-namespace base {
-namespace internal {
-
-struct PartitionDirectMapExtent {
-  PartitionDirectMapExtent* next_extent;
-  PartitionDirectMapExtent* prev_extent;
-  PartitionBucket* bucket;
-  size_t map_size;  // Mapped size, not including guard pages and meta-data.
-
-  ALWAYS_INLINE static PartitionDirectMapExtent* FromPage(PartitionPage* page);
-};
-
-ALWAYS_INLINE PartitionDirectMapExtent* PartitionDirectMapExtent::FromPage(
-    PartitionPage* page) {
-  DCHECK(page->bucket->is_direct_mapped());
-  return reinterpret_cast<PartitionDirectMapExtent*>(
-      reinterpret_cast<char*>(page) + 3 * kPageMetadataSize);
-}
-
-}  // namespace internal
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_DIRECT_MAP_EXTENT_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_freelist_entry.h b/third_party/base/allocator/partition_allocator/partition_freelist_entry.h
deleted file mode 100644
index 5d46f0f..0000000
--- a/third_party/base/allocator/partition_allocator/partition_freelist_entry.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (c) 2018 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_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_FREELIST_ENTRY_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_FREELIST_ENTRY_H_
-
-#include <stdint.h>
-
-#include "build/build_config.h"
-#include "third_party/base/allocator/partition_allocator/partition_alloc_constants.h"
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/sys_byteorder.h"
-
-namespace pdfium {
-namespace base {
-namespace internal {
-
-struct EncodedPartitionFreelistEntry;
-
-struct PartitionFreelistEntry {
-  EncodedPartitionFreelistEntry* next;
-
-  PartitionFreelistEntry() = delete;
-  ~PartitionFreelistEntry() = delete;
-
-  ALWAYS_INLINE static EncodedPartitionFreelistEntry* Encode(
-      PartitionFreelistEntry* ptr) {
-    return reinterpret_cast<EncodedPartitionFreelistEntry*>(Transform(ptr));
-  }
-
- private:
-  friend struct EncodedPartitionFreelistEntry;
-  static ALWAYS_INLINE void* Transform(void* ptr) {
-    // We use bswap on little endian as a fast mask for two reasons:
-    // 1) If an object is freed and its vtable used where the attacker doesn't
-    // get the chance to run allocations between the free and use, the vtable
-    // dereference is likely to fault.
-    // 2) If the attacker has a linear buffer overflow and elects to try and
-    // corrupt a freelist pointer, partial pointer overwrite attacks are
-    // thwarted.
-    // For big endian, similar guarantees are arrived at with a negation.
-#if defined(ARCH_CPU_BIG_ENDIAN)
-    uintptr_t masked = ~reinterpret_cast<uintptr_t>(ptr);
-#else
-    uintptr_t masked = ByteSwapUintPtrT(reinterpret_cast<uintptr_t>(ptr));
-#endif
-    return reinterpret_cast<void*>(masked);
-  }
-};
-
-struct EncodedPartitionFreelistEntry {
-  char scrambled[sizeof(PartitionFreelistEntry*)];
-
-  EncodedPartitionFreelistEntry() = delete;
-  ~EncodedPartitionFreelistEntry() = delete;
-
-  ALWAYS_INLINE static PartitionFreelistEntry* Decode(
-      EncodedPartitionFreelistEntry* ptr) {
-    return reinterpret_cast<PartitionFreelistEntry*>(
-        PartitionFreelistEntry::Transform(ptr));
-  }
-};
-
-static_assert(sizeof(PartitionFreelistEntry) ==
-                  sizeof(EncodedPartitionFreelistEntry),
-              "Should not have padding");
-
-}  // namespace internal
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_FREELIST_ENTRY_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_oom.cc b/third_party/base/allocator/partition_allocator/partition_oom.cc
deleted file mode 100644
index a4052d1..0000000
--- a/third_party/base/allocator/partition_allocator/partition_oom.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2018 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.
-
-#include "third_party/base/allocator/partition_allocator/partition_oom.h"
-
-#include "build/build_config.h"
-#include "third_party/base/allocator/partition_allocator/oom.h"
-
-namespace pdfium {
-namespace base {
-namespace internal {
-
-void NOINLINE PartitionExcessiveAllocationSize() {
-  OOM_CRASH();
-}
-
-#if !defined(ARCH_CPU_64_BITS)
-NOINLINE void PartitionOutOfMemoryWithLotsOfUncommitedPages() {
-  OOM_CRASH();
-}
-#endif
-
-}  // namespace internal
-}  // namespace base
-}  // namespace pdfium
diff --git a/third_party/base/allocator/partition_allocator/partition_oom.h b/third_party/base/allocator/partition_allocator/partition_oom.h
deleted file mode 100644
index be43ff3..0000000
--- a/third_party/base/allocator/partition_allocator/partition_oom.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2018 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.
-
-// Holds functions for generating OOM errors from PartitionAlloc. This is
-// distinct from oom.h in that it is meant only for use in PartitionAlloc.
-
-#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_OOM_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_OOM_H_
-
-#include "build/build_config.h"
-#include "third_party/base/compiler_specific.h"
-
-namespace pdfium {
-namespace base {
-namespace internal {
-
-NOINLINE void PartitionExcessiveAllocationSize();
-
-#if !defined(ARCH_CPU_64_BITS)
-NOINLINE void PartitionOutOfMemoryWithLotsOfUncommitedPages();
-#endif
-
-}  // namespace internal
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_OOM_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_page.cc b/third_party/base/allocator/partition_allocator/partition_page.cc
deleted file mode 100644
index 8b507c3..0000000
--- a/third_party/base/allocator/partition_allocator/partition_page.cc
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright (c) 2018 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.
-
-#include "third_party/base/allocator/partition_allocator/partition_page.h"
-
-#include "third_party/base/allocator/partition_allocator/partition_direct_map_extent.h"
-#include "third_party/base/allocator/partition_allocator/partition_root_base.h"
-
-namespace pdfium {
-namespace base {
-namespace internal {
-
-namespace {
-
-ALWAYS_INLINE void PartitionDirectUnmap(PartitionPage* page) {
-  PartitionRootBase* root = PartitionRootBase::FromPage(page);
-  const PartitionDirectMapExtent* extent =
-      PartitionDirectMapExtent::FromPage(page);
-  size_t unmap_size = extent->map_size;
-
-  // Maintain the doubly-linked list of all direct mappings.
-  if (extent->prev_extent) {
-    DCHECK(extent->prev_extent->next_extent == extent);
-    extent->prev_extent->next_extent = extent->next_extent;
-  } else {
-    root->direct_map_list = extent->next_extent;
-  }
-  if (extent->next_extent) {
-    DCHECK(extent->next_extent->prev_extent == extent);
-    extent->next_extent->prev_extent = extent->prev_extent;
-  }
-
-  // Add on the size of the trailing guard page and preceeding partition
-  // page.
-  unmap_size += kPartitionPageSize + kSystemPageSize;
-
-  size_t uncommitted_page_size = page->bucket->slot_size + kSystemPageSize;
-  root->DecreaseCommittedPages(uncommitted_page_size);
-  DCHECK(root->total_size_of_direct_mapped_pages >= uncommitted_page_size);
-  root->total_size_of_direct_mapped_pages -= uncommitted_page_size;
-
-  DCHECK(!(unmap_size & kPageAllocationGranularityOffsetMask));
-
-  char* ptr = reinterpret_cast<char*>(PartitionPage::ToPointer(page));
-  // Account for the mapping starting a partition page before the actual
-  // allocation address.
-  ptr -= kPartitionPageSize;
-
-  FreePages(ptr, unmap_size);
-}
-
-ALWAYS_INLINE void PartitionRegisterEmptyPage(PartitionPage* page) {
-  DCHECK(page->is_empty());
-  PartitionRootBase* root = PartitionRootBase::FromPage(page);
-
-  // If the page is already registered as empty, give it another life.
-  if (page->empty_cache_index != -1) {
-    DCHECK(page->empty_cache_index >= 0);
-    DCHECK(static_cast<unsigned>(page->empty_cache_index) < kMaxFreeableSpans);
-    DCHECK(root->global_empty_page_ring[page->empty_cache_index] == page);
-    root->global_empty_page_ring[page->empty_cache_index] = nullptr;
-  }
-
-  int16_t current_index = root->global_empty_page_ring_index;
-  PartitionPage* page_to_decommit = root->global_empty_page_ring[current_index];
-  // The page might well have been re-activated, filled up, etc. before we get
-  // around to looking at it here.
-  if (page_to_decommit)
-    page_to_decommit->DecommitIfPossible(root);
-
-  // We put the empty slot span on our global list of "pages that were once
-  // empty". thus providing it a bit of breathing room to get re-used before
-  // we really free it. This improves performance, particularly on Mac OS X
-  // which has subpar memory management performance.
-  root->global_empty_page_ring[current_index] = page;
-  page->empty_cache_index = current_index;
-  ++current_index;
-  if (current_index == kMaxFreeableSpans)
-    current_index = 0;
-  root->global_empty_page_ring_index = current_index;
-}
-
-}  // namespace
-
-// static
-PartitionPage PartitionPage::sentinel_page_;
-
-PartitionPage* PartitionPage::get_sentinel_page() {
-  return &sentinel_page_;
-}
-
-void PartitionPage::FreeSlowPath() {
-  DCHECK(this != get_sentinel_page());
-  if (LIKELY(num_allocated_slots == 0)) {
-    // Page became fully unused.
-    if (UNLIKELY(bucket->is_direct_mapped())) {
-      PartitionDirectUnmap(this);
-      return;
-    }
-    // If it's the current active page, change it. We bounce the page to
-    // the empty list as a force towards defragmentation.
-    if (LIKELY(this == bucket->active_pages_head))
-      bucket->SetNewActivePage();
-    DCHECK(bucket->active_pages_head != this);
-
-    set_raw_size(0);
-    DCHECK(!get_raw_size());
-
-    PartitionRegisterEmptyPage(this);
-  } else {
-    DCHECK(!bucket->is_direct_mapped());
-    // Ensure that the page is full. That's the only valid case if we
-    // arrive here.
-    DCHECK(num_allocated_slots < 0);
-    // A transition of num_allocated_slots from 0 to -1 is not legal, and
-    // likely indicates a double-free.
-    CHECK(num_allocated_slots != -1);
-    num_allocated_slots = -num_allocated_slots - 2;
-    DCHECK(num_allocated_slots == bucket->get_slots_per_span() - 1);
-    // Fully used page became partially used. It must be put back on the
-    // non-full page list. Also make it the current page to increase the
-    // chances of it being filled up again. The old current page will be
-    // the next page.
-    DCHECK(!next_page);
-    if (LIKELY(bucket->active_pages_head != get_sentinel_page()))
-      next_page = bucket->active_pages_head;
-    bucket->active_pages_head = this;
-    --bucket->num_full_pages;
-    // Special case: for a partition page with just a single slot, it may
-    // now be empty and we want to run it through the empty logic.
-    if (UNLIKELY(num_allocated_slots == 0))
-      FreeSlowPath();
-  }
-}
-
-void PartitionPage::Decommit(PartitionRootBase* root) {
-  DCHECK(is_empty());
-  DCHECK(!bucket->is_direct_mapped());
-  void* addr = PartitionPage::ToPointer(this);
-  root->DecommitSystemPages(addr, bucket->get_bytes_per_span());
-
-  // We actually leave the decommitted page in the active list. We'll sweep
-  // it on to the decommitted page list when we next walk the active page
-  // list.
-  // Pulling this trick enables us to use a singly-linked page list for all
-  // cases, which is critical in keeping the page metadata structure down to
-  // 32 bytes in size.
-  freelist_head = nullptr;
-  num_unprovisioned_slots = 0;
-  DCHECK(is_decommitted());
-}
-
-void PartitionPage::DecommitIfPossible(PartitionRootBase* root) {
-  DCHECK(empty_cache_index >= 0);
-  DCHECK(static_cast<unsigned>(empty_cache_index) < kMaxFreeableSpans);
-  DCHECK(this == root->global_empty_page_ring[empty_cache_index]);
-  empty_cache_index = -1;
-  if (is_empty())
-    Decommit(root);
-}
-
-}  // namespace internal
-}  // namespace base
-}  // namespace pdfium
diff --git a/third_party/base/allocator/partition_allocator/partition_page.h b/third_party/base/allocator/partition_allocator/partition_page.h
deleted file mode 100644
index 4bbb76b..0000000
--- a/third_party/base/allocator/partition_allocator/partition_page.h
+++ /dev/null
@@ -1,294 +0,0 @@
-// Copyright (c) 2018 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_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_PAGE_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_PAGE_H_
-
-#include <string.h>
-
-#include "third_party/base/allocator/partition_allocator/partition_alloc_constants.h"
-#include "third_party/base/allocator/partition_allocator/partition_bucket.h"
-#include "third_party/base/allocator/partition_allocator/partition_cookie.h"
-#include "third_party/base/allocator/partition_allocator/partition_freelist_entry.h"
-#include "third_party/base/allocator/partition_allocator/random.h"
-
-namespace pdfium {
-namespace base {
-namespace internal {
-
-struct PartitionRootBase;
-
-// Some notes on page states. A page can be in one of four major states:
-// 1) Active.
-// 2) Full.
-// 3) Empty.
-// 4) Decommitted.
-// An active page has available free slots. A full page has no free slots. An
-// empty page has no free slots, and a decommitted page is an empty page that
-// had its backing memory released back to the system.
-// There are two linked lists tracking the pages. The "active page" list is an
-// approximation of a list of active pages. It is an approximation because
-// full, empty and decommitted pages may briefly be present in the list until
-// we next do a scan over it.
-// The "empty page" list is an accurate list of pages which are either empty
-// or decommitted.
-//
-// The significant page transitions are:
-// - free() will detect when a full page has a slot free()'d and immediately
-// return the page to the head of the active list.
-// - free() will detect when a page is fully emptied. It _may_ add it to the
-// empty list or it _may_ leave it on the active list until a future list scan.
-// - malloc() _may_ scan the active page list in order to fulfil the request.
-// If it does this, full, empty and decommitted pages encountered will be
-// booted out of the active list. If there are no suitable active pages found,
-// an empty or decommitted page (if one exists) will be pulled from the empty
-// list on to the active list.
-//
-// TODO(ajwong): Evaluate if this should be named PartitionSlotSpanMetadata or
-// similar. If so, all uses of the term "page" in comments, member variables,
-// local variables, and documentation that refer to this concept should be
-// updated.
-struct PartitionPage {
-  PartitionFreelistEntry* freelist_head;
-  PartitionPage* next_page;
-  PartitionBucket* bucket;
-  // Deliberately signed, 0 for empty or decommitted page, -n for full pages:
-  int16_t num_allocated_slots;
-  uint16_t num_unprovisioned_slots;
-  uint16_t page_offset;
-  int16_t empty_cache_index;  // -1 if not in the empty cache.
-
-  // Public API
-
-  // Note the matching Alloc() functions are in PartitionPage.
-  BASE_EXPORT NOINLINE void FreeSlowPath();
-  ALWAYS_INLINE void Free(void* ptr);
-
-  void Decommit(PartitionRootBase* root);
-  void DecommitIfPossible(PartitionRootBase* root);
-
-  // Pointer manipulation functions. These must be static as the input |page|
-  // pointer may be the result of an offset calculation and therefore cannot
-  // be trusted. The objective of these functions is to sanitize this input.
-  ALWAYS_INLINE static void* ToPointer(const PartitionPage* page);
-  ALWAYS_INLINE static PartitionPage* FromPointerNoAlignmentCheck(void* ptr);
-  ALWAYS_INLINE static PartitionPage* FromPointer(void* ptr);
-
-  ALWAYS_INLINE const size_t* get_raw_size_ptr() const;
-  ALWAYS_INLINE size_t* get_raw_size_ptr() {
-    return const_cast<size_t*>(
-        const_cast<const PartitionPage*>(this)->get_raw_size_ptr());
-  }
-
-  ALWAYS_INLINE size_t get_raw_size() const;
-  ALWAYS_INLINE void set_raw_size(size_t size);
-
-  ALWAYS_INLINE void Reset();
-
-  // TODO(ajwong): Can this be made private?  https://crbug.com/787153
-  BASE_EXPORT static PartitionPage* get_sentinel_page();
-
-  // Page State accessors.
-  // Note that it's only valid to call these functions on pages found on one of
-  // the page lists. Specifically, you can't call these functions on full pages
-  // that were detached from the active list.
-  //
-  // This restriction provides the flexibity for some of the status fields to
-  // be repurposed when a page is taken off a list. See the negation of
-  // |num_allocated_slots| when a full page is removed from the active list
-  // for an example of such repurposing.
-  ALWAYS_INLINE bool is_active() const;
-  ALWAYS_INLINE bool is_full() const;
-  ALWAYS_INLINE bool is_empty() const;
-  ALWAYS_INLINE bool is_decommitted() const;
-
- private:
-  // g_sentinel_page is used as a sentinel to indicate that there is no page
-  // in the active page list. We can use nullptr, but in that case we need
-  // to add a null-check branch to the hot allocation path. We want to avoid
-  // that.
-  //
-  // Note, this declaration is kept in the header as opposed to an anonymous
-  // namespace so the getter can be fully inlined.
-  static PartitionPage sentinel_page_;
-};
-static_assert(sizeof(PartitionPage) <= kPageMetadataSize,
-              "PartitionPage must be able to fit in a metadata slot");
-
-ALWAYS_INLINE char* PartitionSuperPageToMetadataArea(char* ptr) {
-  uintptr_t pointer_as_uint = reinterpret_cast<uintptr_t>(ptr);
-  DCHECK(!(pointer_as_uint & kSuperPageOffsetMask));
-  // The metadata area is exactly one system page (the guard page) into the
-  // super page.
-  return reinterpret_cast<char*>(pointer_as_uint + kSystemPageSize);
-}
-
-ALWAYS_INLINE PartitionPage* PartitionPage::FromPointerNoAlignmentCheck(
-    void* ptr) {
-  uintptr_t pointer_as_uint = reinterpret_cast<uintptr_t>(ptr);
-  char* super_page_ptr =
-      reinterpret_cast<char*>(pointer_as_uint & kSuperPageBaseMask);
-  uintptr_t partition_page_index =
-      (pointer_as_uint & kSuperPageOffsetMask) >> kPartitionPageShift;
-  // Index 0 is invalid because it is the metadata and guard area and
-  // the last index is invalid because it is a guard page.
-  DCHECK(partition_page_index);
-  DCHECK(partition_page_index < kNumPartitionPagesPerSuperPage - 1);
-  PartitionPage* page = reinterpret_cast<PartitionPage*>(
-      PartitionSuperPageToMetadataArea(super_page_ptr) +
-      (partition_page_index << kPageMetadataShift));
-  // Partition pages in the same slot span can share the same page object.
-  // Adjust for that.
-  size_t delta = page->page_offset << kPageMetadataShift;
-  page =
-      reinterpret_cast<PartitionPage*>(reinterpret_cast<char*>(page) - delta);
-  return page;
-}
-
-// Resturns start of the slot span for the PartitionPage.
-ALWAYS_INLINE void* PartitionPage::ToPointer(const PartitionPage* page) {
-  uintptr_t pointer_as_uint = reinterpret_cast<uintptr_t>(page);
-
-  uintptr_t super_page_offset = (pointer_as_uint & kSuperPageOffsetMask);
-
-  // A valid |page| must be past the first guard System page and within
-  // the following metadata region.
-  DCHECK(super_page_offset > kSystemPageSize);
-  // Must be less than total metadata region.
-  DCHECK(super_page_offset < kSystemPageSize + (kNumPartitionPagesPerSuperPage *
-                                                kPageMetadataSize));
-  uintptr_t partition_page_index =
-      (super_page_offset - kSystemPageSize) >> kPageMetadataShift;
-  // Index 0 is invalid because it is the superpage extent metadata and the
-  // last index is invalid because the whole PartitionPage is set as guard
-  // pages for the metadata region.
-  DCHECK(partition_page_index);
-  DCHECK(partition_page_index < kNumPartitionPagesPerSuperPage - 1);
-  uintptr_t super_page_base = (pointer_as_uint & kSuperPageBaseMask);
-  void* ret = reinterpret_cast<void*>(
-      super_page_base + (partition_page_index << kPartitionPageShift));
-  return ret;
-}
-
-ALWAYS_INLINE PartitionPage* PartitionPage::FromPointer(void* ptr) {
-  PartitionPage* page = PartitionPage::FromPointerNoAlignmentCheck(ptr);
-  // Checks that the pointer is a multiple of bucket size.
-  DCHECK(!((reinterpret_cast<uintptr_t>(ptr) -
-            reinterpret_cast<uintptr_t>(PartitionPage::ToPointer(page))) %
-           page->bucket->slot_size));
-  return page;
-}
-
-ALWAYS_INLINE const size_t* PartitionPage::get_raw_size_ptr() const {
-  // For single-slot buckets which span more than one partition page, we
-  // have some spare metadata space to store the raw allocation size. We
-  // can use this to report better statistics.
-  if (bucket->slot_size <= kMaxSystemPagesPerSlotSpan * kSystemPageSize)
-    return nullptr;
-
-  DCHECK((bucket->slot_size % kSystemPageSize) == 0);
-  DCHECK(bucket->is_direct_mapped() || bucket->get_slots_per_span() == 1);
-
-  const PartitionPage* the_next_page = this + 1;
-  return reinterpret_cast<const size_t*>(&the_next_page->freelist_head);
-}
-
-ALWAYS_INLINE size_t PartitionPage::get_raw_size() const {
-  const size_t* ptr = get_raw_size_ptr();
-  if (UNLIKELY(ptr != nullptr))
-    return *ptr;
-  return 0;
-}
-
-ALWAYS_INLINE void PartitionPage::Free(void* ptr) {
-#if DCHECK_IS_ON()
-  size_t slot_size = bucket->slot_size;
-  const size_t raw_size = get_raw_size();
-  if (raw_size) {
-    slot_size = raw_size;
-  }
-
-  // If these asserts fire, you probably corrupted memory.
-  PartitionCookieCheckValue(ptr);
-  PartitionCookieCheckValue(reinterpret_cast<char*>(ptr) + slot_size -
-                            kCookieSize);
-
-  memset(ptr, kFreedByte, slot_size);
-#endif
-
-  DCHECK(num_allocated_slots);
-  // Catches an immediate double free.
-  CHECK(ptr != freelist_head);
-  // Look for double free one level deeper in debug.
-  DCHECK(!freelist_head ||
-         ptr != EncodedPartitionFreelistEntry::Decode(freelist_head->next));
-  internal::PartitionFreelistEntry* entry =
-      static_cast<internal::PartitionFreelistEntry*>(ptr);
-  entry->next = internal::PartitionFreelistEntry::Encode(freelist_head);
-  freelist_head = entry;
-  --num_allocated_slots;
-  if (UNLIKELY(num_allocated_slots <= 0)) {
-    FreeSlowPath();
-  } else {
-    // All single-slot allocations must go through the slow path to
-    // correctly update the size metadata.
-    DCHECK(get_raw_size() == 0);
-  }
-}
-
-ALWAYS_INLINE bool PartitionPage::is_active() const {
-  DCHECK(this != get_sentinel_page());
-  DCHECK(!page_offset);
-  return (num_allocated_slots > 0 &&
-          (freelist_head || num_unprovisioned_slots));
-}
-
-ALWAYS_INLINE bool PartitionPage::is_full() const {
-  DCHECK(this != get_sentinel_page());
-  DCHECK(!page_offset);
-  bool ret = (num_allocated_slots == bucket->get_slots_per_span());
-  if (ret) {
-    DCHECK(!freelist_head);
-    DCHECK(!num_unprovisioned_slots);
-  }
-  return ret;
-}
-
-ALWAYS_INLINE bool PartitionPage::is_empty() const {
-  DCHECK(this != get_sentinel_page());
-  DCHECK(!page_offset);
-  return (!num_allocated_slots && freelist_head);
-}
-
-ALWAYS_INLINE bool PartitionPage::is_decommitted() const {
-  DCHECK(this != get_sentinel_page());
-  DCHECK(!page_offset);
-  bool ret = (!num_allocated_slots && !freelist_head);
-  if (ret) {
-    DCHECK(!num_unprovisioned_slots);
-    DCHECK(empty_cache_index == -1);
-  }
-  return ret;
-}
-
-ALWAYS_INLINE void PartitionPage::set_raw_size(size_t size) {
-  size_t* raw_size_ptr = get_raw_size_ptr();
-  if (UNLIKELY(raw_size_ptr != nullptr))
-    *raw_size_ptr = size;
-}
-
-ALWAYS_INLINE void PartitionPage::Reset() {
-  DCHECK(is_decommitted());
-
-  num_unprovisioned_slots = bucket->get_slots_per_span();
-  DCHECK(num_unprovisioned_slots);
-
-  next_page = nullptr;
-}
-
-}  // namespace internal
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_PAGE_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_root_base.cc b/third_party/base/allocator/partition_allocator/partition_root_base.cc
deleted file mode 100644
index 3136584..0000000
--- a/third_party/base/allocator/partition_allocator/partition_root_base.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2018 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.
-
-#include "third_party/base/allocator/partition_allocator/partition_root_base.h"
-
-#include "build/build_config.h"
-#include "third_party/base/allocator/partition_allocator/oom.h"
-#include "third_party/base/allocator/partition_allocator/partition_oom.h"
-#include "third_party/base/allocator/partition_allocator/partition_page.h"
-
-namespace pdfium {
-namespace base {
-namespace internal {
-
-NOINLINE void PartitionRootBase::OutOfMemory() {
-#if !defined(ARCH_CPU_64_BITS)
-  // Check whether this OOM is due to a lot of super pages that are allocated
-  // but not committed, probably due to http://crbug.com/421387.
-  if (total_size_of_super_pages + total_size_of_direct_mapped_pages -
-          total_size_of_committed_pages >
-      kReasonableSizeOfUnusedPages) {
-    PartitionOutOfMemoryWithLotsOfUncommitedPages();
-  }
-#endif
-  if (PartitionRootBase::gOomHandlingFunction)
-    (*PartitionRootBase::gOomHandlingFunction)();
-  OOM_CRASH();
-}
-
-void PartitionRootBase::DecommitEmptyPages() {
-  for (size_t i = 0; i < kMaxFreeableSpans; ++i) {
-    internal::PartitionPage* page = global_empty_page_ring[i];
-    if (page)
-      page->DecommitIfPossible(this);
-    global_empty_page_ring[i] = nullptr;
-  }
-}
-
-}  // namespace internal
-}  // namespace base
-}  // namespace pdfium
diff --git a/third_party/base/allocator/partition_allocator/partition_root_base.h b/third_party/base/allocator/partition_allocator/partition_root_base.h
deleted file mode 100644
index 2f4b70e..0000000
--- a/third_party/base/allocator/partition_allocator/partition_root_base.h
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright (c) 2018 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_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ROOT_BASE_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ROOT_BASE_H_
-
-#include "build/build_config.h"
-#include "third_party/base/allocator/partition_allocator/page_allocator.h"
-#include "third_party/base/allocator/partition_allocator/partition_alloc_constants.h"
-#include "third_party/base/allocator/partition_allocator/partition_bucket.h"
-#include "third_party/base/allocator/partition_allocator/partition_direct_map_extent.h"
-#include "third_party/base/allocator/partition_allocator/partition_page.h"
-
-namespace pdfium {
-namespace base {
-namespace internal {
-
-struct PartitionPage;
-struct PartitionRootBase;
-
-// An "extent" is a span of consecutive superpages. We link to the partition's
-// next extent (if there is one) to the very start of a superpage's metadata
-// area.
-struct PartitionSuperPageExtentEntry {
-  PartitionRootBase* root;
-  char* super_page_base;
-  char* super_pages_end;
-  PartitionSuperPageExtentEntry* next;
-};
-static_assert(
-    sizeof(PartitionSuperPageExtentEntry) <= kPageMetadataSize,
-    "PartitionSuperPageExtentEntry must be able to fit in a metadata slot");
-
-struct BASE_EXPORT PartitionRootBase {
-  PartitionRootBase();
-  virtual ~PartitionRootBase();
-  size_t total_size_of_committed_pages = 0;
-  size_t total_size_of_super_pages = 0;
-  size_t total_size_of_direct_mapped_pages = 0;
-  // Invariant: total_size_of_committed_pages <=
-  //                total_size_of_super_pages +
-  //                total_size_of_direct_mapped_pages.
-  unsigned num_buckets = 0;
-  unsigned max_allocation = 0;
-  bool initialized = false;
-  char* next_super_page = nullptr;
-  char* next_partition_page = nullptr;
-  char* next_partition_page_end = nullptr;
-  PartitionSuperPageExtentEntry* current_extent = nullptr;
-  PartitionSuperPageExtentEntry* first_extent = nullptr;
-  PartitionDirectMapExtent* direct_map_list = nullptr;
-  PartitionPage* global_empty_page_ring[kMaxFreeableSpans] = {};
-  int16_t global_empty_page_ring_index = 0;
-  uintptr_t inverted_self = 0;
-
-  // Public API
-
-  // Allocates out of the given bucket. Properly, this function should probably
-  // be in PartitionBucket, but because the implementation needs to be inlined
-  // for performance, and because it needs to inspect PartitionPage,
-  // it becomes impossible to have it in PartitionBucket as this causes a
-  // cyclical dependency on PartitionPage function implementations.
-  //
-  // Moving it a layer lower couples PartitionRootBase and PartitionBucket, but
-  // preserves the layering of the includes.
-  //
-  // Note the matching Free() functions are in PartitionPage.
-  ALWAYS_INLINE void* AllocFromBucket(PartitionBucket* bucket,
-                                      int flags,
-                                      size_t size);
-
-  ALWAYS_INLINE static bool IsValidPage(PartitionPage* page);
-  ALWAYS_INLINE static PartitionRootBase* FromPage(PartitionPage* page);
-
-  // gOomHandlingFunction is invoked when PartitionAlloc hits OutOfMemory.
-  static void (*gOomHandlingFunction)();
-  NOINLINE void OutOfMemory();
-
-  ALWAYS_INLINE void IncreaseCommittedPages(size_t len);
-  ALWAYS_INLINE void DecreaseCommittedPages(size_t len);
-  ALWAYS_INLINE void DecommitSystemPages(void* address, size_t length);
-  ALWAYS_INLINE void RecommitSystemPages(void* address, size_t length);
-
-  // Frees memory from this partition, if possible, by decommitting pages.
-  // |flags| is an OR of base::PartitionPurgeFlags.
-  virtual void PurgeMemory(int flags) = 0;
-  void DecommitEmptyPages();
-};
-
-ALWAYS_INLINE void* PartitionRootBase::AllocFromBucket(PartitionBucket* bucket,
-                                                       int flags,
-                                                       size_t size) {
-  bool zero_fill = flags & PartitionAllocZeroFill;
-  bool is_already_zeroed = false;
-
-  PartitionPage* page = bucket->active_pages_head;
-  // Check that this page is neither full nor freed.
-  DCHECK(page->num_allocated_slots >= 0);
-  void* ret = page->freelist_head;
-  if (LIKELY(ret != 0)) {
-    // If these DCHECKs fire, you probably corrupted memory. TODO(palmer): See
-    // if we can afford to make these CHECKs.
-    DCHECK(PartitionRootBase::IsValidPage(page));
-
-    // All large allocations must go through the slow path to correctly update
-    // the size metadata.
-    DCHECK(page->get_raw_size() == 0);
-    internal::PartitionFreelistEntry* new_head =
-        internal::EncodedPartitionFreelistEntry::Decode(
-            page->freelist_head->next);
-    page->freelist_head = new_head;
-    page->num_allocated_slots++;
-  } else {
-    ret = bucket->SlowPathAlloc(this, flags, size, &is_already_zeroed);
-    // TODO(palmer): See if we can afford to make this a CHECK.
-    DCHECK(!ret ||
-           PartitionRootBase::IsValidPage(PartitionPage::FromPointer(ret)));
-  }
-
-#if DCHECK_IS_ON()
-  if (!ret) {
-    return nullptr;
-  }
-
-  page = PartitionPage::FromPointer(ret);
-  // TODO(ajwong): Can |page->bucket| ever not be |this|? If not, can this just
-  // be bucket->slot_size?
-  size_t new_slot_size = page->bucket->slot_size;
-  size_t raw_size = page->get_raw_size();
-  if (raw_size) {
-    DCHECK(raw_size == size);
-    new_slot_size = raw_size;
-  }
-  size_t no_cookie_size = PartitionCookieSizeAdjustSubtract(new_slot_size);
-  char* char_ret = static_cast<char*>(ret);
-  // The value given to the application is actually just after the cookie.
-  ret = char_ret + kCookieSize;
-
-  // Fill the region kUninitializedByte or 0, and surround it with 2 cookies.
-  PartitionCookieWriteValue(char_ret);
-  if (!zero_fill) {
-    memset(ret, kUninitializedByte, no_cookie_size);
-  } else if (!is_already_zeroed) {
-    memset(ret, 0, no_cookie_size);
-  }
-  PartitionCookieWriteValue(char_ret + kCookieSize + no_cookie_size);
-#else
-  if (ret && zero_fill && !is_already_zeroed) {
-    memset(ret, 0, size);
-  }
-#endif
-
-  return ret;
-}
-
-ALWAYS_INLINE bool PartitionRootBase::IsValidPage(PartitionPage* page) {
-  PartitionRootBase* root = PartitionRootBase::FromPage(page);
-  return root->inverted_self == ~reinterpret_cast<uintptr_t>(root);
-}
-
-ALWAYS_INLINE PartitionRootBase* PartitionRootBase::FromPage(
-    PartitionPage* page) {
-  PartitionSuperPageExtentEntry* extent_entry =
-      reinterpret_cast<PartitionSuperPageExtentEntry*>(
-          reinterpret_cast<uintptr_t>(page) & kSystemPageBaseMask);
-  return extent_entry->root;
-}
-
-ALWAYS_INLINE void PartitionRootBase::IncreaseCommittedPages(size_t len) {
-  total_size_of_committed_pages += len;
-  DCHECK(total_size_of_committed_pages <=
-         total_size_of_super_pages + total_size_of_direct_mapped_pages);
-}
-
-ALWAYS_INLINE void PartitionRootBase::DecreaseCommittedPages(size_t len) {
-  total_size_of_committed_pages -= len;
-  DCHECK(total_size_of_committed_pages <=
-         total_size_of_super_pages + total_size_of_direct_mapped_pages);
-}
-
-ALWAYS_INLINE void PartitionRootBase::DecommitSystemPages(void* address,
-                                                          size_t length) {
-  ::pdfium::base::DecommitSystemPages(address, length);
-  DecreaseCommittedPages(length);
-}
-
-ALWAYS_INLINE void PartitionRootBase::RecommitSystemPages(void* address,
-                                                          size_t length) {
-  CHECK(::pdfium::base::RecommitSystemPages(address, length, PageReadWrite));
-  IncreaseCommittedPages(length);
-}
-
-}  // namespace internal
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ROOT_BASE_H_
diff --git a/third_party/base/allocator/partition_allocator/random.cc b/third_party/base/allocator/partition_allocator/random.cc
deleted file mode 100644
index 7a13855..0000000
--- a/third_party/base/allocator/partition_allocator/random.cc
+++ /dev/null
@@ -1,96 +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.
-
-#include "third_party/base/allocator/partition_allocator/random.h"
-
-#include "build/build_config.h"
-#include "third_party/base/allocator/partition_allocator/spin_lock.h"
-#include "third_party/base/no_destructor.h"
-
-#if defined(OS_WIN)
-#include <windows.h>
-#else
-#include <sys/time.h>
-#include <unistd.h>
-#endif
-
-namespace pdfium {
-namespace base {
-
-// This is the same PRNG as used by tcmalloc for mapping address randomness;
-// see http://burtleburtle.net/bob/rand/smallprng.html.
-struct RandomContext {
-  subtle::SpinLock lock;
-  bool initialized;
-  uint32_t a;
-  uint32_t b;
-  uint32_t c;
-  uint32_t d;
-};
-
-namespace {
-
-#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
-
-uint32_t RandomValueInternal(RandomContext* x) {
-  uint32_t e = x->a - rot(x->b, 27);
-  x->a = x->b ^ rot(x->c, 17);
-  x->b = x->c + x->d;
-  x->c = x->d + e;
-  x->d = e + x->a;
-  return x->d;
-}
-
-#undef rot
-
-RandomContext* GetRandomContext() {
-  static NoDestructor<RandomContext> g_random_context;
-  RandomContext* x = g_random_context.get();
-  subtle::SpinLock::Guard guard(x->lock);
-  if (UNLIKELY(!x->initialized)) {
-    x->initialized = true;
-    char c;
-    uint32_t seed = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&c));
-    uint32_t pid;
-    uint32_t usec;
-#if defined(OS_WIN)
-    pid = GetCurrentProcessId();
-    SYSTEMTIME st;
-    GetSystemTime(&st);
-    usec = static_cast<uint32_t>(st.wMilliseconds * 1000);
-#else
-    pid = static_cast<uint32_t>(getpid());
-    struct timeval tv;
-    gettimeofday(&tv, 0);
-    usec = static_cast<uint32_t>(tv.tv_usec);
-#endif
-    seed ^= pid;
-    seed ^= usec;
-    x->a = 0xf1ea5eed;
-    x->b = x->c = x->d = seed;
-    for (int i = 0; i < 20; ++i) {
-      RandomValueInternal(x);
-    }
-  }
-  return x;
-}
-
-}  // namespace
-
-uint32_t RandomValue() {
-  RandomContext* x = GetRandomContext();
-  subtle::SpinLock::Guard guard(x->lock);
-  return RandomValueInternal(x);
-}
-
-void SetMmapSeedForTesting(int64_t seed) {
-  RandomContext* x = GetRandomContext();
-  subtle::SpinLock::Guard guard(x->lock);
-  x->a = x->b = static_cast<uint32_t>(seed);
-  x->c = x->d = static_cast<uint32_t>(seed >> 32);
-  x->initialized = true;
-}
-
-}  // namespace base
-}  // namespace pdfium
diff --git a/third_party/base/allocator/partition_allocator/random.h b/third_party/base/allocator/partition_allocator/random.h
deleted file mode 100644
index e485d6d..0000000
--- a/third_party/base/allocator/partition_allocator/random.h
+++ /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.
-
-#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_RANDOM_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_RANDOM_H_
-
-#include <stdint.h>
-
-#include "third_party/base/base_export.h"
-
-namespace pdfium {
-namespace base {
-
-// Returns a random value. The generator's internal state is initialized with
-// `base::RandUint64` which is very unpredictable, but which is expensive due to
-// the need to call into the kernel. Therefore this generator uses a fast,
-// entirely user-space function after initialization.
-BASE_EXPORT uint32_t RandomValue();
-
-// Sets the seed for the random number generator to a known value, to cause the
-// RNG to generate a predictable sequence of outputs. May be called multiple
-// times.
-BASE_EXPORT void SetMmapSeedForTesting(uint64_t seed);
-
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_RANDOM_H_
diff --git a/third_party/base/allocator/partition_allocator/spin_lock.cc b/third_party/base/allocator/partition_allocator/spin_lock.cc
deleted file mode 100644
index 99e56b2..0000000
--- a/third_party/base/allocator/partition_allocator/spin_lock.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2015 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.
-
-#include "third_party/base/allocator/partition_allocator/spin_lock.h"
-
-#include "build/build_config.h"
-#include "third_party/base/logging.h"
-
-#if defined(OS_WIN)
-#include <windows.h>
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-#include <sched.h>
-#endif
-
-// The YIELD_PROCESSOR macro wraps an architecture specific-instruction that
-// informs the processor we're in a busy wait, so it can handle the branch more
-// intelligently and e.g. reduce power to our core or give more resources to the
-// other hyper-thread on this core. See the following for context:
-// https://software.intel.com/en-us/articles/benefitting-power-and-performance-sleep-loops
-//
-// The YIELD_THREAD macro tells the OS to relinquish our quantum. This is
-// basically a worst-case fallback, and if you're hitting it with any frequency
-// you really should be using a proper lock (such as |base::Lock|)rather than
-// these spinlocks.
-#if defined(OS_WIN)
-
-#define YIELD_PROCESSOR YieldProcessor()
-#define YIELD_THREAD SwitchToThread()
-
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-
-#if defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_X86)
-#define YIELD_PROCESSOR __asm__ __volatile__("pause")
-#elif (defined(ARCH_CPU_ARMEL) && __ARM_ARCH >= 6) || defined(ARCH_CPU_ARM64)
-#define YIELD_PROCESSOR __asm__ __volatile__("yield")
-#elif defined(ARCH_CPU_MIPSEL)
-// The MIPS32 docs state that the PAUSE instruction is a no-op on older
-// architectures (first added in MIPS32r2). To avoid assembler errors when
-// targeting pre-r2, we must encode the instruction manually.
-#define YIELD_PROCESSOR __asm__ __volatile__(".word 0x00000140")
-#elif defined(ARCH_CPU_MIPS64EL) && __mips_isa_rev >= 2
-// Don't bother doing using .word here since r2 is the lowest supported mips64
-// that Chromium supports.
-#define YIELD_PROCESSOR __asm__ __volatile__("pause")
-#elif defined(ARCH_CPU_PPC64_FAMILY)
-#define YIELD_PROCESSOR __asm__ __volatile__("or 31,31,31")
-#elif defined(ARCH_CPU_RISCV64)
-// Encoding of the pause instruction from the Zihintpause extension.  This
-// is a noop on cpus that don't have the Zihintpause extension.
-#define YIELD_PROCESSOR __asm__ __volatile__ (".4byte 0x100000F");
-#elif defined(ARCH_CPU_S390_FAMILY)
-// just do nothing
-#define YIELD_PROCESSOR ((void)0)
-#endif  // ARCH
-
-#ifndef YIELD_PROCESSOR
-#warning "Processor yield not supported on this architecture."
-#define YIELD_PROCESSOR ((void)0)
-#endif
-
-#define YIELD_THREAD sched_yield()
-
-#else  // Other OS
-
-#warning "Thread yield not supported on this OS."
-#define YIELD_THREAD ((void)0)
-
-#endif  // OS_WIN
-
-namespace pdfium {
-namespace base {
-namespace subtle {
-
-void SpinLock::LockSlow() {
-  // The value of |kYieldProcessorTries| is cargo culted from TCMalloc, Windows
-  // critical section defaults, and various other recommendations.
-  // TODO(jschuh): Further tuning may be warranted.
-  static const int kYieldProcessorTries = 1000;
-  // The value of |kYieldThreadTries| is completely made up.
-  static const int kYieldThreadTries = 10;
-  int yield_thread_count = 0;
-  do {
-    do {
-      for (int count = 0; count < kYieldProcessorTries; ++count) {
-        // Let the processor know we're spinning.
-        YIELD_PROCESSOR;
-        if (!lock_.load(std::memory_order_relaxed) &&
-            LIKELY(!lock_.exchange(true, std::memory_order_acquire)))
-          return;
-      }
-
-      if (yield_thread_count < kYieldThreadTries) {
-        ++yield_thread_count;
-        // Give the OS a chance to schedule something on this core.
-        YIELD_THREAD;
-      } else {
-        // At this point, it's likely that the lock is held by a lower priority
-        // thread that is unavailable to finish its work because of higher
-        // priority threads spinning here. Sleeping should ensure that they make
-        // progress.
-        NOTREACHED();
-      }
-    } while (lock_.load(std::memory_order_relaxed));
-  } while (UNLIKELY(lock_.exchange(true, std::memory_order_acquire)));
-}
-
-}  // namespace subtle
-}  // namespace base
-}  // namespace pdfium
diff --git a/third_party/base/allocator/partition_allocator/spin_lock.h b/third_party/base/allocator/partition_allocator/spin_lock.h
deleted file mode 100644
index 5613fd1..0000000
--- a/third_party/base/allocator/partition_allocator/spin_lock.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2013 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_ALLOCATOR_PARTITION_ALLOCATOR_SPIN_LOCK_H_
-#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_SPIN_LOCK_H_
-
-#include <atomic>
-#include <memory>
-#include <mutex>
-
-#include "third_party/base/base_export.h"
-#include "third_party/base/compiler_specific.h"
-
-// Spinlock is a simple spinlock class based on the standard CPU primitive of
-// atomic increment and decrement of an int at a given memory address. These are
-// intended only for very short duration locks and assume a system with multiple
-// cores. For any potentially longer wait you should use a real lock, such as
-// |base::Lock|.
-namespace pdfium {
-namespace base {
-namespace subtle {
-
-class BASE_EXPORT SpinLock {
- public:
-  constexpr SpinLock() = default;
-  ~SpinLock() = default;
-  using Guard = std::lock_guard<SpinLock>;
-
-  ALWAYS_INLINE void lock() {
-    static_assert(sizeof(lock_) == sizeof(int),
-                  "int and lock_ are different sizes");
-    if (LIKELY(!lock_.exchange(true, std::memory_order_acquire)))
-      return;
-    LockSlow();
-  }
-
-  ALWAYS_INLINE void unlock() { lock_.store(false, std::memory_order_release); }
-
- private:
-  // This is called if the initial attempt to acquire the lock fails. It's
-  // slower, but has a much better scheduling and power consumption behavior.
-  void LockSlow();
-
-  std::atomic_int lock_{0};
-};
-
-}  // namespace subtle
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_SPIN_LOCK_H_
diff --git a/third_party/base/bits.h b/third_party/base/bits.h
index 1253683..b726f2f 100644
--- a/third_party/base/bits.h
+++ b/third_party/base/bits.h
@@ -7,25 +7,15 @@
 #ifndef THIRD_PARTY_BASE_BITS_H_
 #define THIRD_PARTY_BASE_BITS_H_
 
-#include <stddef.h>
-#include <stdint.h>
-
 #include <type_traits>
 
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/logging.h"
-
-#if defined(COMPILER_MSVC)
-#include <intrin.h>
-#endif
-
 namespace pdfium {
 namespace base {
 namespace bits {
 
+// TODO(thestig): When C++20 is required, replace with std::has_single_bit().
 // Returns true iff |value| is a power of 2.
-template <typename T,
-          typename = typename std::enable_if<std::is_integral<T>::value>>
+template <typename T, typename = std::enable_if<std::is_integral<T>::value>>
 constexpr inline bool IsPowerOfTwo(T value) {
   // From "Hacker's Delight": Section 2.1 Manipulating Rightmost Bits.
   //
@@ -36,150 +26,6 @@
   return value > 0 && (value & (value - 1)) == 0;
 }
 
-// Round up |size| to a multiple of alignment, which must be a power of two.
-inline size_t Align(size_t size, size_t alignment) {
-  DCHECK(IsPowerOfTwo(alignment));
-  return (size + alignment - 1) & ~(alignment - 1);
-}
-
-// CountLeadingZeroBits(value) returns the number of zero bits following the
-// most significant 1 bit in |value| if |value| is non-zero, otherwise it
-// returns {sizeof(T) * 8}.
-// Example: 00100010 -> 2
-//
-// CountTrailingZeroBits(value) returns the number of zero bits preceding the
-// least significant 1 bit in |value| if |value| is non-zero, otherwise it
-// returns {sizeof(T) * 8}.
-// Example: 00100010 -> 1
-//
-// C does not have an operator to do this, but fortunately the various
-// compilers have built-ins that map to fast underlying processor instructions.
-#if defined(COMPILER_MSVC)
-
-template <typename T, unsigned bits = sizeof(T) * 8>
-ALWAYS_INLINE
-    typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 4,
-                            unsigned>::type
-    CountLeadingZeroBits(T x) {
-  static_assert(bits > 0, "invalid instantiation");
-  unsigned long index;
-  return LIKELY(_BitScanReverse(&index, static_cast<uint32_t>(x)))
-             ? (31 - index - (32 - bits))
-             : bits;
-}
-
-template <typename T, unsigned bits = sizeof(T) * 8>
-ALWAYS_INLINE
-    typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) == 8,
-                            unsigned>::type
-    CountLeadingZeroBits(T x) {
-  static_assert(bits > 0, "invalid instantiation");
-  unsigned long index;
-  return LIKELY(_BitScanReverse64(&index, static_cast<uint64_t>(x)))
-             ? (63 - index)
-             : 64;
-}
-
-template <typename T, unsigned bits = sizeof(T) * 8>
-ALWAYS_INLINE
-    typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 4,
-                            unsigned>::type
-    CountTrailingZeroBits(T x) {
-  static_assert(bits > 0, "invalid instantiation");
-  unsigned long index;
-  return LIKELY(_BitScanForward(&index, static_cast<uint32_t>(x))) ? index
-                                                                   : bits;
-}
-
-template <typename T, unsigned bits = sizeof(T) * 8>
-ALWAYS_INLINE
-    typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) == 8,
-                            unsigned>::type
-    CountTrailingZeroBits(T x) {
-  static_assert(bits > 0, "invalid instantiation");
-  unsigned long index;
-  return LIKELY(_BitScanForward64(&index, static_cast<uint64_t>(x))) ? index
-                                                                     : 64;
-}
-
-ALWAYS_INLINE uint32_t CountLeadingZeroBits32(uint32_t x) {
-  return CountLeadingZeroBits(x);
-}
-
-#if defined(ARCH_CPU_64_BITS)
-
-// MSVC only supplies _BitScanForward64 when building for a 64-bit target.
-ALWAYS_INLINE uint64_t CountLeadingZeroBits64(uint64_t x) {
-  return CountLeadingZeroBits(x);
-}
-
-#endif
-
-#elif defined(COMPILER_GCC)
-
-// __builtin_clz has undefined behaviour for an input of 0, even though there's
-// clearly a return value that makes sense, and even though some processor clz
-// instructions have defined behaviour for 0. We could drop to raw __asm__ to
-// do better, but we'll avoid doing that unless we see proof that we need to.
-template <typename T, unsigned bits = sizeof(T) * 8>
-ALWAYS_INLINE
-    typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8,
-                            unsigned>::type
-    CountLeadingZeroBits(T value) {
-  static_assert(bits > 0, "invalid instantiation");
-  return LIKELY(value)
-             ? bits == 64
-                   ? __builtin_clzll(static_cast<uint64_t>(value))
-                   : __builtin_clz(static_cast<uint32_t>(value)) - (32 - bits)
-             : bits;
-}
-
-template <typename T, unsigned bits = sizeof(T) * 8>
-ALWAYS_INLINE
-    typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8,
-                            unsigned>::type
-    CountTrailingZeroBits(T value) {
-  return LIKELY(value) ? bits == 64
-                             ? __builtin_ctzll(static_cast<uint64_t>(value))
-                             : __builtin_ctz(static_cast<uint32_t>(value))
-                       : bits;
-}
-
-ALWAYS_INLINE uint32_t CountLeadingZeroBits32(uint32_t x) {
-  return CountLeadingZeroBits(x);
-}
-
-#if defined(ARCH_CPU_64_BITS)
-
-ALWAYS_INLINE uint64_t CountLeadingZeroBits64(uint64_t x) {
-  return CountLeadingZeroBits(x);
-}
-
-#endif
-
-#endif
-
-ALWAYS_INLINE size_t CountLeadingZeroBitsSizeT(size_t x) {
-  return CountLeadingZeroBits(x);
-}
-
-ALWAYS_INLINE size_t CountTrailingZeroBitsSizeT(size_t x) {
-  return CountTrailingZeroBits(x);
-}
-
-// Returns the integer i such as 2^i <= n < 2^(i+1)
-inline int Log2Floor(uint32_t n) {
-  return 31 - CountLeadingZeroBits(n);
-}
-
-// Returns the integer i such as 2^(i-1) < n <= 2^i
-inline int Log2Ceiling(uint32_t n) {
-  // When n == 0, we want the function to return -1.
-  // When n == 0, (n - 1) will underflow to 0xFFFFFFFF, which is
-  // why the statement below starts with (n ? 32 : -1).
-  return (n ? 32 : -1) - CountLeadingZeroBits(n - 1);
-}
-
 }  // namespace bits
 }  // namespace base
 }  // namespace pdfium
diff --git a/third_party/base/check.h b/third_party/base/check.h
new file mode 100644
index 0000000..ecfeed8
--- /dev/null
+++ b/third_party/base/check.h
@@ -0,0 +1,36 @@
+// Copyright 2020 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_CHECK_H_
+#define THIRD_PARTY_BASE_CHECK_H_
+
+#include <assert.h>
+
+#include "build/build_config.h"
+#include "third_party/base/compiler_specific.h"
+#include "third_party/base/immediate_crash.h"
+
+#define CHECK(condition)              \
+  do {                                \
+    if (UNLIKELY(!(condition))) {     \
+      pdfium::base::ImmediateCrash(); \
+    }                                 \
+  } while (0)
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+#define DCHECK_IS_ON() 0
+#else
+#define DCHECK_IS_ON() 1
+#endif
+
+// Debug mode: Use assert() for better diagnostics
+// Release mode, DCHECK_ALWAYS_ON: Use CHECK() since assert() is a no-op.
+// Release mode, no DCHECK_ALWAYS_ON: Use assert(), which is a no-op.
+#if defined(NDEBUG) && defined(DCHECK_ALWAYS_ON)
+#define DCHECK CHECK
+#else
+#define DCHECK assert
+#endif
+
+#endif  // THIRD_PARTY_BASE_CHECK_H_
diff --git a/third_party/base/check_op.h b/third_party/base/check_op.h
new file mode 100644
index 0000000..4722421
--- /dev/null
+++ b/third_party/base/check_op.h
@@ -0,0 +1,24 @@
+// Copyright 2020 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_CHECK_OP_H_
+#define THIRD_PARTY_BASE_CHECK_OP_H_
+
+#include "third_party/base/check.h"
+
+#define CHECK_EQ(x, y) CHECK((x) == (y))
+#define CHECK_NE(x, y) CHECK((x) != (y))
+#define CHECK_LT(x, y) CHECK((x) < (y))
+#define CHECK_GT(x, y) CHECK((x) > (y))
+#define CHECK_LE(x, y) CHECK((x) <= (y))
+#define CHECK_GE(x, y) CHECK((x) >= (y))
+
+#define DCHECK_EQ(x, y) DCHECK((x) == (y))
+#define DCHECK_NE(x, y) DCHECK((x) != (y))
+#define DCHECK_LT(x, y) DCHECK((x) < (y))
+#define DCHECK_GT(x, y) DCHECK((x) > (y))
+#define DCHECK_LE(x, y) DCHECK((x) <= (y))
+#define DCHECK_GE(x, y) DCHECK((x) >= (y))
+
+#endif  // THIRD_PARTY_BASE_CHECK_OP_H_
diff --git a/third_party/base/compiler_specific.h b/third_party/base/compiler_specific.h
index 947cbf3..e11deb6 100644
--- a/third_party/base/compiler_specific.h
+++ b/third_party/base/compiler_specific.h
@@ -7,27 +7,19 @@
 
 #include "build/build_config.h"
 
-// Annotate a variable indicating it's ok if the variable is not used.
-// (Typically used to silence a compiler warning when the assignment
-// is important for some other reason.)
-// Use like:
-//   int x = ...;
-//   ALLOW_UNUSED_LOCAL(x);
-#define ALLOW_UNUSED_LOCAL(x) (void)x
-
-// Annotate a typedef or function indicating it's ok if it's not used.
-// Use like:
-//   typedef Foo Bar ALLOW_UNUSED_TYPE;
-#if defined(COMPILER_GCC) || defined(__clang__)
-#define ALLOW_UNUSED_TYPE __attribute__((unused))
+// A wrapper around `__has_attribute`, similar to HAS_CPP_ATTRIBUTE.
+#if defined(__has_attribute)
+#define HAS_ATTRIBUTE(x) __has_attribute(x)
 #else
-#define ALLOW_UNUSED_TYPE
+#define HAS_ATTRIBUTE(x) 0
 #endif
 
 // Annotate a function indicating it should not be inlined.
 // Use like:
 //   NOINLINE void DoStuff() { ... }
-#if defined(COMPILER_GCC)
+#if defined(__clang__) && HAS_ATTRIBUTE(noinline)
+#define NOINLINE [[clang::noinline]]
+#elif defined(COMPILER_GCC) && HAS_ATTRIBUTE(noinline)
 #define NOINLINE __attribute__((noinline))
 #elif defined(COMPILER_MSVC)
 #define NOINLINE __declspec(noinline)
@@ -35,119 +27,6 @@
 #define NOINLINE
 #endif
 
-#if defined(COMPILER_GCC) && defined(NDEBUG)
-#define ALWAYS_INLINE inline __attribute__((__always_inline__))
-#elif defined(COMPILER_MSVC) && defined(NDEBUG)
-#define ALWAYS_INLINE __forceinline
-#else
-#define ALWAYS_INLINE inline
-#endif
-
-// Specify memory alignment for structs, classes, etc.
-// Use like:
-//   class ALIGNAS(16) MyClass { ... }
-//   ALIGNAS(16) int array[4];
-//
-// In most places you can use the C++11 keyword "alignas", which is preferred.
-//
-// But compilers have trouble mixing __attribute__((...)) syntax with
-// alignas(...) syntax.
-//
-// Doesn't work in clang or gcc:
-//   struct alignas(16) __attribute__((packed)) S { char c; };
-// Works in clang but not gcc:
-//   struct __attribute__((packed)) alignas(16) S2 { char c; };
-// Works in clang and gcc:
-//   struct alignas(16) S3 { char c; } __attribute__((packed));
-//
-// There are also some attributes that must be specified *before* a class
-// definition: visibility (used for exporting functions/classes) is one of
-// these attributes. This means that it is not possible to use alignas() with a
-// class that is marked as exported.
-#if defined(COMPILER_MSVC)
-#define ALIGNAS(byte_alignment) __declspec(align(byte_alignment))
-#elif defined(COMPILER_GCC)
-#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
-#endif
-
-// Annotate a function indicating the caller must examine the return value.
-// Use like:
-//   int foo() WARN_UNUSED_RESULT;
-// To explicitly ignore a result, see |ignore_result()| in base/macros.h.
-#undef WARN_UNUSED_RESULT
-#if defined(COMPILER_GCC) || defined(__clang__)
-#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
-#else
-#define WARN_UNUSED_RESULT
-#endif
-
-// Tell the compiler a function is using a printf-style format string.
-// |format_param| is the one-based index of the format string parameter;
-// |dots_param| is the one-based index of the "..." parameter.
-// For v*printf functions (which take a va_list), pass 0 for dots_param.
-// (This is undocumented but matches what the system C headers do.)
-#if defined(COMPILER_GCC) || defined(__clang__)
-#define PRINTF_FORMAT(format_param, dots_param) \
-    __attribute__((format(printf, format_param, dots_param)))
-#else
-#define PRINTF_FORMAT(format_param, dots_param)
-#endif
-
-// WPRINTF_FORMAT is the same, but for wide format strings.
-// This doesn't appear to yet be implemented in any compiler.
-// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38308 .
-#define WPRINTF_FORMAT(format_param, dots_param)
-// If available, it would look like:
-//   __attribute__((format(wprintf, format_param, dots_param)))
-
-// Sanitizers annotations.
-#if defined(__has_attribute)
-#if __has_attribute(no_sanitize)
-#define NO_SANITIZE(what) __attribute__((no_sanitize(what)))
-#endif
-#endif
-#if !defined(NO_SANITIZE)
-#define NO_SANITIZE(what)
-#endif
-
-// MemorySanitizer annotations.
-#if defined(MEMORY_SANITIZER) && !defined(OS_NACL)
-#include <sanitizer/msan_interface.h>
-
-// Mark a memory region fully initialized.
-// Use this to annotate code that deliberately reads uninitialized data, for
-// example a GC scavenging root set pointers from the stack.
-#define MSAN_UNPOISON(p, size)  __msan_unpoison(p, size)
-
-// Check a memory region for initializedness, as if it was being used here.
-// If any bits are uninitialized, crash with an MSan report.
-// Use this to sanitize data which MSan won't be able to track, e.g. before
-// passing data to another process via shared memory.
-#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) \
-    __msan_check_mem_is_initialized(p, size)
-#else  // MEMORY_SANITIZER
-#define MSAN_UNPOISON(p, size)
-#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size)
-#endif  // MEMORY_SANITIZER
-
-// DISABLE_CFI_PERF -- Disable Control Flow Integrity for perf reasons.
-#if !defined(DISABLE_CFI_PERF)
-#if defined(__clang__) && defined(OFFICIAL_BUILD)
-#define DISABLE_CFI_PERF __attribute__((no_sanitize("cfi")))
-#else
-#define DISABLE_CFI_PERF
-#endif
-#endif
-
-// Macro useful for writing cross-platform function pointers.
-#if !defined(CDECL)
-#if defined(OS_WIN)
-#define CDECL __cdecl
-#else  // defined(OS_WIN)
-#define CDECL
-#endif  // defined(OS_WIN)
-#endif  // !defined(CDECL)
-
 // Macro for hinting that an expression is likely to be false.
 #if !defined(UNLIKELY)
 #if defined(COMPILER_GCC) || defined(__clang__)
@@ -165,19 +44,37 @@
 #endif  // defined(COMPILER_GCC)
 #endif  // !defined(LIKELY)
 
-// Compiler feature-detection.
-// clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension
-#if defined(__has_feature)
-#define HAS_FEATURE(FEATURE) __has_feature(FEATURE)
+// Marks a type as being eligible for the "trivial" ABI despite having a
+// non-trivial destructor or copy/move constructor. Such types can be relocated
+// after construction by simply copying their memory, which makes them eligible
+// to be passed in registers. The canonical example is std::unique_ptr.
+//
+// Use with caution; this has some subtle effects on constructor/destructor
+// ordering and will be very incorrect if the type relies on its address
+// remaining constant. When used as a function argument (by value), the value
+// may be constructed in the caller's stack frame, passed in a register, and
+// then used and destructed in the callee's stack frame. A similar thing can
+// occur when values are returned.
+//
+// TRIVIAL_ABI is not needed for types which have a trivial destructor and
+// copy/move constructors, such as base::TimeTicks and other POD.
+//
+// It is also not likely to be effective on types too large to be passed in one
+// or two registers on typical target ABIs.
+//
+// See also:
+//   https://clang.llvm.org/docs/AttributeReference.html#trivial-abi
+//   https://libcxx.llvm.org/docs/DesignDocs/UniquePtrTrivialAbi.html
+#if defined(__clang__) && HAS_ATTRIBUTE(trivial_abi)
+#define TRIVIAL_ABI [[clang::trivial_abi]]
 #else
-#define HAS_FEATURE(FEATURE) 0
+#define TRIVIAL_ABI
 #endif
 
-// Macro for telling -Wimplicit-fallthrough that a fallthrough is intentional.
 #if defined(__clang__)
-#define FALLTHROUGH [[clang::fallthrough]]
+#define GSL_POINTER [[gsl::Pointer]]
 #else
-#define FALLTHROUGH
+#define GSL_POINTER
 #endif
 
 #endif  // THIRD_PARTY_BASE_COMPILER_SPECIFIC_H_
diff --git a/third_party/base/component_export.h b/third_party/base/component_export.h
new file mode 100644
index 0000000..65c142f
--- /dev/null
+++ b/third_party/base/component_export.h
@@ -0,0 +1,76 @@
+// Copyright 2018 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_COMPONENT_EXPORT_H_
+#define THIRD_PARTY_BASE_COMPONENT_EXPORT_H_
+
+// Used to annotate symbols which are exported by the component named
+// |component|. Note that this only does the right thing if the corresponding
+// component target's sources are compiled with |IS_$component_IMPL| defined
+// as 1. For example:
+//
+//   class COMPONENT_EXPORT(FOO) Bar {};
+//
+// If IS_FOO_IMPL=1 at compile time, then Bar will be annotated using the
+// COMPONENT_EXPORT_ANNOTATION macro defined below. Otherwise it will be
+// annotated using the COMPONENT_IMPORT_ANNOTATION macro.
+#define COMPONENT_EXPORT(component)                         \
+  COMPONENT_MACRO_CONDITIONAL_(IS_##component##_IMPL,       \
+                               COMPONENT_EXPORT_ANNOTATION, \
+                               COMPONENT_IMPORT_ANNOTATION)
+
+// Indicates whether the current compilation unit is being compiled as part of
+// the implementation of the component named |component|. Expands to |1| if
+// |IS_$component_IMPL| is defined as |1|; expands to |0| otherwise.
+//
+// Note in particular that if |IS_$component_IMPL| is not defined at all, it is
+// still fine to test INSIDE_COMPONENT_IMPL(component), which expands to |0| as
+// expected.
+#define INSIDE_COMPONENT_IMPL(component) \
+  COMPONENT_MACRO_CONDITIONAL_(IS_##component##_IMPL, 1, 0)
+
+// Compiler-specific macros to annotate for export or import of a symbol. No-op
+// in non-component builds. These should not see much if any direct use.
+// Instead use the COMPONENT_EXPORT macro defined above.
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+#define COMPONENT_EXPORT_ANNOTATION __declspec(dllexport)
+#define COMPONENT_IMPORT_ANNOTATION __declspec(dllimport)
+#else  // defined(WIN32)
+#define COMPONENT_EXPORT_ANNOTATION __attribute__((visibility("default")))
+#define COMPONENT_IMPORT_ANNOTATION
+#endif  // defined(WIN32)
+#else   // defined(COMPONENT_BUILD)
+#define COMPONENT_EXPORT_ANNOTATION
+#define COMPONENT_IMPORT_ANNOTATION
+#endif  // defined(COMPONENT_BUILD)
+
+// Below this point are several internal utility macros used for the
+// implementation of the above macros. Not intended for external use.
+
+// Helper for conditional expansion to one of two token strings. If |condition|
+// expands to |1| then this macro expands to |consequent|; otherwise it expands
+// to |alternate|.
+#define COMPONENT_MACRO_CONDITIONAL_(condition, consequent, alternate) \
+  COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_(                              \
+      COMPONENT_MACRO_CONDITIONAL_COMMA_(condition), consequent, alternate)
+
+// Expands to a comma (,) iff its first argument expands to |1|. Used in
+// conjunction with |COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_()|, as the presence
+// or absense of an extra comma can be used to conditionally shift subsequent
+// argument positions and thus influence which argument is selected.
+#define COMPONENT_MACRO_CONDITIONAL_COMMA_(...) \
+  COMPONENT_MACRO_CONDITIONAL_COMMA_IMPL_(__VA_ARGS__, )
+#define COMPONENT_MACRO_CONDITIONAL_COMMA_IMPL_(x, ...) \
+  COMPONENT_MACRO_CONDITIONAL_COMMA_##x##_
+#define COMPONENT_MACRO_CONDITIONAL_COMMA_1_ ,
+
+// Helper which simply selects its third argument. Used in conjunction with
+// |COMPONENT_MACRO_CONDITIONAL_COMMA_()| above to implement conditional macro
+// expansion.
+#define COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_(...) \
+  COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_IMPL_(__VA_ARGS__)
+#define COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_IMPL_(a, b, c, ...) c
+
+#endif  // THIRD_PARTY_BASE_COMPONENT_EXPORT_H_
diff --git a/third_party/base/containers/adapters.h b/third_party/base/containers/adapters.h
new file mode 100644
index 0000000..0f65ea0
--- /dev/null
+++ b/third_party/base/containers/adapters.h
@@ -0,0 +1,54 @@
+// Copyright 2020 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_CONTAINERS_ADAPTERS_H_
+#define THIRD_PARTY_BASE_CONTAINERS_ADAPTERS_H_
+
+#include <stddef.h>
+
+#include <iterator>
+#include <utility>
+
+namespace pdfium {
+namespace base {
+
+namespace internal {
+
+// Internal adapter class for implementing base::Reversed.
+template <typename T>
+class ReversedAdapter {
+ public:
+  using Iterator = decltype(std::rbegin(std::declval<T&>()));
+
+  explicit ReversedAdapter(T& t) : t_(t) {}
+  ReversedAdapter(const ReversedAdapter& ra) : t_(ra.t_) {}
+  ReversedAdapter& operator=(const ReversedAdapter&) = delete;
+
+  Iterator begin() const { return std::rbegin(t_); }
+  Iterator end() const { return std::rend(t_); }
+
+ private:
+  T& t_;
+};
+
+}  // namespace internal
+
+// Reversed returns a container adapter usable in a range-based "for" statement
+// for iterating a reversible container in reverse order.
+//
+// Example:
+//
+//   std::vector<int> v = ...;
+//   for (int i : base::Reversed(v)) {
+//     // iterates through v from back to front
+//   }
+template <typename T>
+internal::ReversedAdapter<T> Reversed(T& t) {
+  return internal::ReversedAdapter<T>(t);
+}
+
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_CONTAINERS_ADAPTERS_H_
diff --git a/third_party/base/containers/contains.h b/third_party/base/containers/contains.h
new file mode 100644
index 0000000..63a06ef
--- /dev/null
+++ b/third_party/base/containers/contains.h
@@ -0,0 +1,114 @@
+// 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_CONTAINERS_CONTAINS_H_
+#define THIRD_PARTY_BASE_CONTAINERS_CONTAINS_H_
+
+#include <algorithm>
+#include <iterator>
+#include <type_traits>
+
+#include "third_party/base/template_util.h"
+
+namespace pdfium {
+
+namespace internal {
+
+// Small helper to detect whether a given type has a nested `key_type` typedef.
+// Used below to catch misuses of the API for associative containers.
+template <typename T, typename SFINAE = void>
+struct HasKeyType : std::false_type {};
+
+template <typename T>
+struct HasKeyType<T, std::void_t<typename T::key_type>> : std::true_type {};
+
+// Utility type traits used for specializing base::Contains() below.
+template <typename Container, typename Element, typename = void>
+struct HasFindWithNpos : std::false_type {};
+
+template <typename Container, typename Element>
+struct HasFindWithNpos<
+    Container,
+    Element,
+    std::void_t<decltype(std::declval<const Container&>().find(
+                             std::declval<const Element&>()) !=
+                         Container::npos)>> : std::true_type {};
+
+template <typename Container, typename Element, typename = void>
+struct HasFindWithEnd : std::false_type {};
+
+template <typename Container, typename Element>
+struct HasFindWithEnd<
+    Container,
+    Element,
+    std::void_t<decltype(std::declval<const Container&>().find(
+                             std::declval<const Element&>()) !=
+                         std::declval<const Container&>().end())>>
+    : std::true_type {};
+
+template <typename Container, typename Element, typename = void>
+struct HasContains : std::false_type {};
+
+template <typename Container, typename Element>
+struct HasContains<
+    Container,
+    Element,
+    std::void_t<decltype(std::declval<const Container&>().contains(
+        std::declval<const Element&>()))>> : std::true_type {};
+
+}  // namespace internal
+
+// General purpose implementation to check if |container| contains |value|.
+template <typename Container,
+          typename Value,
+          std::enable_if_t<
+              !internal::HasFindWithNpos<Container, Value>::value &&
+              !internal::HasFindWithEnd<Container, Value>::value &&
+              !internal::HasContains<Container, Value>::value>* = nullptr>
+bool Contains(const Container& container, const Value& value) {
+  static_assert(
+      !internal::HasKeyType<Container>::value,
+      "Error: About to perform linear search on an associative container. "
+      "Either use a more generic comparator (e.g. std::less<>) or, if a linear "
+      "search is desired, provide an explicit projection parameter.");
+  using std::begin;
+  using std::end;
+  return std::find(begin(container), end(container), value) != end(container);
+}
+
+// Specialized Contains() implementation for when |container| has a find()
+// member function and a static npos member, but no contains() member function.
+template <typename Container,
+          typename Value,
+          std::enable_if_t<internal::HasFindWithNpos<Container, Value>::value &&
+                           !internal::HasContains<Container, Value>::value>* =
+              nullptr>
+bool Contains(const Container& container, const Value& value) {
+  return container.find(value) != Container::npos;
+}
+
+// Specialized Contains() implementation for when |container| has a find()
+// and end() member function, but no contains() member function.
+template <typename Container,
+          typename Value,
+          std::enable_if_t<internal::HasFindWithEnd<Container, Value>::value &&
+                           !internal::HasContains<Container, Value>::value>* =
+              nullptr>
+bool Contains(const Container& container, const Value& value) {
+  return container.find(value) != container.end();
+}
+
+// Specialized Contains() implementation for when |container| has a contains()
+// member function.
+template <
+    typename Container,
+    typename Value,
+    std::enable_if_t<internal::HasContains<Container, Value>::value>* = nullptr>
+bool Contains(const Container& container, const Value& value) {
+  return container.contains(value);
+}
+
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_CONTAINERS_CONTAINS_H_
diff --git a/third_party/base/containers/span.h b/third_party/base/containers/span.h
new file mode 100644
index 0000000..660d92c
--- /dev/null
+++ b/third_party/base/containers/span.h
@@ -0,0 +1,398 @@
+// Copyright 2017 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_CONTAINERS_SPAN_H_
+#define THIRD_PARTY_BASE_CONTAINERS_SPAN_H_
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <array>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/check.h"
+#include "third_party/base/compiler_specific.h"
+
+namespace pdfium {
+
+constexpr size_t dynamic_extent = static_cast<size_t>(-1);
+
+template <typename T>
+class span;
+
+namespace internal {
+
+template <typename T>
+struct IsSpanImpl : std::false_type {};
+
+template <typename T>
+struct IsSpanImpl<span<T>> : std::true_type {};
+
+template <typename T>
+using IsSpan = IsSpanImpl<typename std::decay<T>::type>;
+
+template <typename T>
+struct IsStdArrayImpl : std::false_type {};
+
+template <typename T, size_t N>
+struct IsStdArrayImpl<std::array<T, N>> : std::true_type {};
+
+template <typename T>
+using IsStdArray = IsStdArrayImpl<typename std::decay<T>::type>;
+
+template <typename From, typename To>
+using IsLegalSpanConversion = std::is_convertible<From*, To*>;
+
+template <typename Container, typename T>
+using ContainerHasConvertibleData =
+    IsLegalSpanConversion<typename std::remove_pointer<decltype(
+                              std::declval<Container>().data())>::type,
+                          T>;
+template <typename Container>
+using ContainerHasIntegralSize =
+    std::is_integral<decltype(std::declval<Container>().size())>;
+
+template <typename From, typename To>
+using EnableIfLegalSpanConversion =
+    typename std::enable_if<IsLegalSpanConversion<From, To>::value>::type;
+
+// SFINAE check if Container can be converted to a span<T>. Note that the
+// implementation details of this check differ slightly from the requirements in
+// the working group proposal: in particular, the proposal also requires that
+// the container conversion constructor participate in overload resolution only
+// if two additional conditions are true:
+//
+//   1. Container implements operator[].
+//   2. Container::value_type matches remove_const_t<element_type>.
+//
+// The requirements are relaxed slightly here: in particular, not requiring (2)
+// means that an immutable span can be easily constructed from a mutable
+// container.
+template <typename Container, typename T>
+using EnableIfSpanCompatibleContainer =
+    typename std::enable_if<!internal::IsSpan<Container>::value &&
+                            !internal::IsStdArray<Container>::value &&
+                            ContainerHasConvertibleData<Container, T>::value &&
+                            ContainerHasIntegralSize<Container>::value>::type;
+
+template <typename Container, typename T>
+using EnableIfConstSpanCompatibleContainer =
+    typename std::enable_if<std::is_const<T>::value &&
+                            !internal::IsSpan<Container>::value &&
+                            !internal::IsStdArray<Container>::value &&
+                            ContainerHasConvertibleData<Container, T>::value &&
+                            ContainerHasIntegralSize<Container>::value>::type;
+
+}  // namespace internal
+
+// A span is a value type that represents an array of elements of type T. Since
+// it only consists of a pointer to memory with an associated size, it is very
+// light-weight. It is cheap to construct, copy, move and use spans, so that
+// users are encouraged to use it as a pass-by-value parameter. A span does not
+// own the underlying memory, so care must be taken to ensure that a span does
+// not outlive the backing store.
+//
+// span is somewhat analogous to StringPiece, but with arbitrary element types,
+// allowing mutation if T is non-const.
+//
+// span is implicitly convertible from C++ arrays, as well as most [1]
+// container-like types that provide a data() and size() method (such as
+// std::vector<T>). A mutable span<T> can also be implicitly converted to an
+// immutable span<const T>.
+//
+// Consider using a span for functions that take a data pointer and size
+// parameter: it allows the function to still act on an array-like type, while
+// allowing the caller code to be a bit more concise.
+//
+// For read-only data access pass a span<const T>: the caller can supply either
+// a span<const T> or a span<T>, while the callee will have a read-only view.
+// For read-write access a mutable span<T> is required.
+//
+// Without span:
+//   Read-Only:
+//     // std::string HexEncode(const uint8_t* data, size_t size);
+//     std::vector<uint8_t> data_buffer = GenerateData();
+//     std::string r = HexEncode(data_buffer.data(), data_buffer.size());
+//
+//  Mutable:
+//     // ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args...);
+//     char str_buffer[100];
+//     SafeSNPrintf(str_buffer, sizeof(str_buffer), "Pi ~= %lf", 3.14);
+//
+// With span:
+//   Read-Only:
+//     // std::string HexEncode(base::span<const uint8_t> data);
+//     std::vector<uint8_t> data_buffer = GenerateData();
+//     std::string r = HexEncode(data_buffer);
+//
+//  Mutable:
+//     // ssize_t SafeSNPrintf(base::span<char>, const char* fmt, Args...);
+//     char str_buffer[100];
+//     SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14);
+//
+// Spans with "const" and pointers
+// -------------------------------
+//
+// Const and pointers can get confusing. Here are vectors of pointers and their
+// corresponding spans (you can always make the span "more const" too):
+//
+//   const std::vector<int*>        =>  base::span<int* const>
+//   std::vector<const int*>        =>  base::span<const int*>
+//   const std::vector<const int*>  =>  base::span<const int* const>
+//
+// Differences from the working group proposal
+// -------------------------------------------
+//
+// https://wg21.link/P0122 is the latest working group proposal, Chromium
+// currently implements R6. The biggest difference is span does not support a
+// static extent template parameter. Other differences are documented in
+// subsections below.
+//
+// Differences in constants and types:
+// - no element_type type alias
+// - no index_type type alias
+// - no different_type type alias
+// - no extent constant
+//
+// Differences from [span.cons]:
+// - no constructor from a pointer range
+//
+// Differences from [span.sub]:
+// - no templated first()
+// - no templated last()
+// - no templated subspan()
+// - using size_t instead of ptrdiff_t for indexing
+//
+// Differences from [span.obs]:
+// - using size_t instead of ptrdiff_t to represent size()
+//
+// Differences from [span.elem]:
+// - no operator ()()
+// - using size_t instead of ptrdiff_t for indexing
+
+// [span], class template span
+template <typename T>
+class TRIVIAL_ABI GSL_POINTER span {
+ public:
+  using value_type = typename std::remove_cv<T>::type;
+  using pointer = T*;
+  using reference = T&;
+  using iterator = T*;
+  using const_iterator = const T*;
+  using reverse_iterator = std::reverse_iterator<iterator>;
+  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+  // [span.cons], span constructors, copy, assignment, and destructor
+  constexpr span() noexcept : data_(nullptr), size_(0) {}
+  constexpr span(T* data, size_t size) noexcept : data_(data), size_(size) {
+    DCHECK(data_ || size_ == 0);
+  }
+
+  // TODO(dcheng): Implement construction from a |begin| and |end| pointer.
+  template <size_t N>
+  constexpr span(T (&array)[N]) noexcept : span(array, N) {}
+
+  template <size_t N>
+  constexpr span(std::array<T, N>& array) noexcept : span(array.data(), N) {}
+
+  // Conversion from a container that provides |T* data()| and |integral_type
+  // 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>>
+  constexpr span(const span<U>& other) : span(other.data(), other.size()) {}
+  span& operator=(const span& other) noexcept {
+    if (this != &other) {
+      ReleaseEmptySpan();
+      data_ = other.data_;
+      size_ = other.size_;
+    }
+    return *this;
+  }
+  ~span() noexcept { ReleaseEmptySpan(); }
+
+  // [span.sub], span subviews
+  const span first(size_t count) const {
+    CHECK(count <= size_);
+    return span(static_cast<T*>(data_), count);
+  }
+
+  const span last(size_t count) const {
+    CHECK(count <= size_);
+    return span(static_cast<T*>(data_) + (size_ - count), count);
+  }
+
+  const span subspan(size_t pos, size_t count = dynamic_extent) const {
+    CHECK(pos <= size_);
+    CHECK(count == dynamic_extent || count <= size_ - pos);
+    return span(static_cast<T*>(data_) + pos,
+                count == dynamic_extent ? size_ - pos : count);
+  }
+
+  // [span.obs], span observers
+  constexpr size_t size() const noexcept { return size_; }
+  constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); }
+  constexpr bool empty() const noexcept { return size_ == 0; }
+
+  // [span.elem], span element access
+  T& operator[](size_t index) const noexcept {
+    CHECK(index < size_);
+    return static_cast<T*>(data_)[index];
+  }
+
+  constexpr T& front() const noexcept {
+    CHECK(!empty());
+    return *data();
+  }
+
+  constexpr T& back() const noexcept {
+    CHECK(!empty());
+    return *(data() + size() - 1);
+  }
+
+  constexpr T* data() const noexcept { return static_cast<T*>(data_); }
+
+  // [span.iter], span iterator support
+  constexpr iterator begin() const noexcept { return static_cast<T*>(data_); }
+  constexpr iterator end() const noexcept { return begin() + size_; }
+
+  constexpr const_iterator cbegin() const noexcept { return begin(); }
+  constexpr const_iterator cend() const noexcept { return end(); }
+
+  constexpr reverse_iterator rbegin() const noexcept {
+    return reverse_iterator(end());
+  }
+  constexpr reverse_iterator rend() const noexcept {
+    return reverse_iterator(begin());
+  }
+
+  constexpr const_reverse_iterator crbegin() const noexcept {
+    return const_reverse_iterator(cend());
+  }
+  constexpr const_reverse_iterator crend() const noexcept {
+    return const_reverse_iterator(cbegin());
+  }
+
+ 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_;
+};
+
+// [span.comparison], span comparison operators
+// Relational operators. Equality is a element-wise comparison.
+template <typename T>
+constexpr bool operator==(span<T> lhs, span<T> rhs) noexcept {
+  return lhs.size() == rhs.size() &&
+         std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin());
+}
+
+template <typename T>
+constexpr bool operator!=(span<T> lhs, span<T> rhs) noexcept {
+  return !(lhs == rhs);
+}
+
+template <typename T>
+constexpr bool operator<(span<T> lhs, span<T> rhs) noexcept {
+  return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(),
+                                      rhs.cend());
+}
+
+template <typename T>
+constexpr bool operator<=(span<T> lhs, span<T> rhs) noexcept {
+  return !(rhs < lhs);
+}
+
+template <typename T>
+constexpr bool operator>(span<T> lhs, span<T> rhs) noexcept {
+  return rhs < lhs;
+}
+
+template <typename T>
+constexpr bool operator>=(span<T> lhs, span<T> rhs) noexcept {
+  return !(lhs < rhs);
+}
+
+// [span.objectrep], views of object representation
+template <typename T>
+span<const uint8_t> as_bytes(span<T> s) noexcept {
+  return {reinterpret_cast<const uint8_t*>(s.data()), s.size_bytes()};
+}
+
+template <typename T,
+          typename U = typename std::enable_if<!std::is_const<T>::value>::type>
+span<uint8_t> as_writable_bytes(span<T> s) noexcept {
+  return {reinterpret_cast<uint8_t*>(s.data()), s.size_bytes()};
+}
+
+// Type-deducing helpers for constructing a span.
+template <typename T>
+constexpr span<T> make_span(T* data, size_t size) noexcept {
+  return span<T>(data, size);
+}
+
+template <typename T, size_t N>
+constexpr span<T> make_span(T (&array)[N]) noexcept {
+  return span<T>(array);
+}
+
+template <typename T, size_t N>
+constexpr span<T> make_span(std::array<T, N>& array) noexcept {
+  return span<T>(array);
+}
+
+template <typename Container,
+          typename T = typename Container::value_type,
+          typename = internal::EnableIfSpanCompatibleContainer<Container, T>>
+constexpr span<T> make_span(Container& container) {
+  return span<T>(container);
+}
+
+template <
+    typename Container,
+    typename T = typename std::add_const<typename Container::value_type>::type,
+    typename = internal::EnableIfConstSpanCompatibleContainer<Container, T>>
+constexpr span<T> make_span(const Container& container) {
+  return span<T>(container);
+}
+
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_CONTAINERS_SPAN_H_
diff --git a/third_party/base/immediate_crash.h b/third_party/base/immediate_crash.h
new file mode 100644
index 0000000..36aca7b
--- /dev/null
+++ b/third_party/base/immediate_crash.h
@@ -0,0 +1,154 @@
+// Copyright 2019 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_IMMEDIATE_CRASH_H_
+#define THIRD_PARTY_BASE_IMMEDIATE_CRASH_H_
+
+#include "build/build_config.h"
+
+// Crashes in the fastest possible way with no attempt at logging.
+// There are several constraints; see http://crbug.com/664209 for more context.
+//
+// - TRAP_SEQUENCE_() must be fatal. It should not be possible to ignore the
+//   resulting exception or simply hit 'continue' to skip over it in a debugger.
+// - Different instances of TRAP_SEQUENCE_() must not be folded together, to
+//   ensure crash reports are debuggable. Unlike __builtin_trap(), asm volatile
+//   blocks will not be folded together.
+//   Note: TRAP_SEQUENCE_() previously required an instruction with a unique
+//   nonce since unlike clang, GCC folds together identical asm volatile
+//   blocks.
+// - TRAP_SEQUENCE_() must produce a signal that is distinct from an invalid
+//   memory access.
+// - TRAP_SEQUENCE_() must be treated as a set of noreturn instructions.
+//   __builtin_unreachable() is used to provide that hint here. clang also uses
+//   this as a heuristic to pack the instructions in the function epilogue to
+//   improve code density.
+//
+// Additional properties that are nice to have:
+// - TRAP_SEQUENCE_() should be as compact as possible.
+// - The first instruction of TRAP_SEQUENCE_() should not change, to avoid
+//   shifting crash reporting clusters. As a consequence of this, explicit
+//   assembly is preferred over intrinsics.
+//   Note: this last bullet point may no longer be true, and may be removed in
+//   the future.
+
+// Note: TRAP_SEQUENCE Is currently split into two macro helpers due to the fact
+// that clang emits an actual instruction for __builtin_unreachable() on certain
+// platforms (see https://crbug.com/958675). In addition, the int3/bkpt/brk will
+// be removed in followups, so splitting it up like this now makes it easy to
+// land the followups.
+
+#if defined(COMPILER_GCC)
+
+#if BUILDFLAG(IS_NACL)
+
+// Crash report accuracy is not guaranteed on NaCl.
+#define TRAP_SEQUENCE1_() __builtin_trap()
+#define TRAP_SEQUENCE2_() asm volatile("")
+
+#elif defined(ARCH_CPU_X86_FAMILY)
+
+// TODO(https://crbug.com/958675): In theory, it should be possible to use just
+// int3. However, there are a number of crashes with SIGILL as the exception
+// code, so it seems likely that there's a signal handler that allows execution
+// to continue after SIGTRAP.
+#define TRAP_SEQUENCE1_() asm volatile("int3")
+
+#if BUILDFLAG(IS_APPLE)
+// Intentionally empty: __builtin_unreachable() is always part of the sequence
+// (see IMMEDIATE_CRASH below) and already emits a ud2 on Mac.
+#define TRAP_SEQUENCE2_() asm volatile("")
+#else
+#define TRAP_SEQUENCE2_() asm volatile("ud2")
+#endif  // BUILDFLAG(IS_APPLE)
+
+#elif defined(ARCH_CPU_ARMEL)
+
+// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running
+// as a 32 bit userspace app on arm64. There doesn't seem to be any way to
+// cause a SIGTRAP from userspace without using a syscall (which would be a
+// problem for sandboxing).
+// TODO(https://crbug.com/958675): Remove bkpt from this sequence.
+#define TRAP_SEQUENCE1_() asm volatile("bkpt #0")
+#define TRAP_SEQUENCE2_() asm volatile("udf #0")
+
+#elif defined(ARCH_CPU_ARM64)
+
+// This will always generate a SIGTRAP on arm64.
+// TODO(https://crbug.com/958675): Remove brk from this sequence.
+#define TRAP_SEQUENCE1_() asm volatile("brk #0")
+#define TRAP_SEQUENCE2_() asm volatile("hlt #0")
+
+#else
+
+// Crash report accuracy will not be guaranteed on other architectures, but at
+// least this will crash as expected.
+#define TRAP_SEQUENCE1_() __builtin_trap()
+#define TRAP_SEQUENCE2_() asm volatile("")
+
+#endif  // ARCH_CPU_*
+
+#elif defined(COMPILER_MSVC)
+
+#if !defined(__clang__)
+
+// MSVC x64 doesn't support inline asm, so use the MSVC intrinsic.
+#define TRAP_SEQUENCE1_() __debugbreak()
+#define TRAP_SEQUENCE2_()
+
+#elif defined(ARCH_CPU_ARM64)
+
+// Windows ARM64 uses "BRK #F000" as its breakpoint instruction, and
+// __debugbreak() generates that in both VC++ and clang.
+#define TRAP_SEQUENCE1_() __debugbreak()
+// Intentionally empty: __builtin_unreachable() is always part of the sequence
+// (see IMMEDIATE_CRASH below) and already emits a ud2 on Win64,
+// https://crbug.com/958373
+#define TRAP_SEQUENCE2_() __asm volatile("")
+
+#else
+
+#define TRAP_SEQUENCE1_() asm volatile("int3")
+#define TRAP_SEQUENCE2_() asm volatile("ud2")
+
+#endif  // __clang__
+
+#else
+
+#error No supported trap sequence!
+
+#endif  // COMPILER_GCC
+
+#define TRAP_SEQUENCE_() \
+  do {                   \
+    TRAP_SEQUENCE1_();   \
+    TRAP_SEQUENCE2_();   \
+  } while (false)
+
+// This version of ALWAYS_INLINE inlines even in is_debug=true.
+// TODO(pbos): See if NDEBUG can be dropped from ALWAYS_INLINE as well, and if
+// so merge. Otherwise document why it cannot inline in debug in
+// base/compiler_specific.h.
+#if defined(COMPILER_GCC)
+#define IMMEDIATE_CRASH_ALWAYS_INLINE inline __attribute__((__always_inline__))
+#elif defined(COMPILER_MSVC)
+#define IMMEDIATE_CRASH_ALWAYS_INLINE __forceinline
+#else
+#define IMMEDIATE_CRASH_ALWAYS_INLINE inline
+#endif
+
+namespace pdfium {
+namespace base {
+
+[[noreturn]] IMMEDIATE_CRASH_ALWAYS_INLINE void ImmediateCrash() {
+  TRAP_SEQUENCE_();
+#if defined(__clang__) || defined(COMPILER_GCC)
+  __builtin_unreachable();
+#endif  // defined(__clang__) || defined(COMPILER_GCC)
+}
+
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_IMMEDIATE_CRASH_H_
diff --git a/third_party/base/logging.h b/third_party/base/logging.h
deleted file mode 100644
index 9c0039d..0000000
--- a/third_party/base/logging.h
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (c) 2014 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_LOGGING_H_
-#define THIRD_PARTY_BASE_LOGGING_H_
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include "build/build_config.h"
-#include "third_party/base/compiler_specific.h"
-
-#if defined(COMPILER_GCC)
-
-#if defined(ARCH_CPU_X86_FAMILY)
-// int 3 will generate a SIGTRAP.
-#define TRAP_SEQUENCE() \
-  asm volatile(         \
-      "int3; ud2; push %0;" ::"i"(static_cast<unsigned char>(__COUNTER__)))
-
-#elif defined(ARCH_CPU_ARMEL)
-// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running
-// as a 32 bit userspace app on arm64. There doesn't seem to be any way to
-// cause a SIGTRAP from userspace without using a syscall (which would be a
-// problem for sandboxing).
-#define TRAP_SEQUENCE() \
-  asm volatile("bkpt #0; udf %0;" ::"i"(__COUNTER__ % 256))
-
-#elif defined(ARCH_CPU_ARM64)
-// This will always generate a SIGTRAP on arm64.
-#define TRAP_SEQUENCE() \
-  asm volatile("brk #0; hlt %0;" ::"i"(__COUNTER__ % 65536))
-
-#else
-// Crash report accuracy will not be guaranteed on other architectures, but at
-// least this will crash as expected.
-#define TRAP_SEQUENCE() __builtin_trap()
-#endif  // ARCH_CPU_*
-
-#elif defined(COMPILER_MSVC)
-
-// Clang is cleverer about coalescing int3s, so we need to add a unique-ish
-// instruction following the __debugbreak() to have it emit distinct locations
-// for CHECKs rather than collapsing them all together. It would be nice to use
-// a short intrinsic to do this (and perhaps have only one implementation for
-// both clang and MSVC), however clang-cl currently does not support intrinsics.
-// On the flip side, MSVC x64 doesn't support inline asm. So, we have to have
-// two implementations. Normally clang-cl's version will be 5 bytes (1 for
-// `int3`, 2 for `ud2`, 2 for `push byte imm`, however, TODO(scottmg):
-// https://crbug.com/694670 clang-cl doesn't currently support %'ing
-// __COUNTER__, so eventually it will emit the dword form of push.
-// TODO(scottmg): Reinvestigate a short sequence that will work on both
-// compilers once clang supports more intrinsics. See https://crbug.com/693713.
-#if !defined(__clang__)
-#define TRAP_SEQUENCE() __debugbreak()
-#elif defined(ARCH_CPU_ARM64)
-#define TRAP_SEQUENCE() \
-  __asm volatile("brk #0\n hlt %0\n" ::"i"(__COUNTER__ % 65536));
-#else
-#define TRAP_SEQUENCE() ({ {__asm int 3 __asm ud2 __asm push __COUNTER__}; })
-#endif  // __clang__
-
-#else
-#error Port
-#endif  // COMPILER_GCC
-
-// CHECK() and the trap sequence can be invoked from a constexpr function.
-// This could make compilation fail on GCC, as it forbids directly using inline
-// asm inside a constexpr function. However, it allows calling a lambda
-// expression including the same asm.
-// The side effect is that the top of the stacktrace will not point to the
-// calling function, but to this anonymous lambda. This is still useful as the
-// full name of the lambda will typically include the name of the function that
-// calls CHECK() and the debugger will still break at the right line of code.
-#if !defined(COMPILER_GCC)
-#define WRAPPED_TRAP_SEQUENCE() TRAP_SEQUENCE()
-#else
-#define WRAPPED_TRAP_SEQUENCE() \
-  do {                          \
-    [] { TRAP_SEQUENCE(); }();  \
-  } while (false)
-#endif
-
-#if defined(__clang__) || defined(COMPILER_GCC)
-#define IMMEDIATE_CRASH()    \
-  ({                         \
-    WRAPPED_TRAP_SEQUENCE(); \
-    __builtin_unreachable(); \
-  })
-#else
-// This is supporting non-chromium user of logging.h to build with MSVC, like
-// pdfium. On MSVC there is no __builtin_unreachable().
-#define IMMEDIATE_CRASH() WRAPPED_TRAP_SEQUENCE()
-#endif
-
-#define CHECK(condition)          \
-  do {                            \
-    if (UNLIKELY(!(condition))) { \
-      IMMEDIATE_CRASH();          \
-    }                             \
-  } while (0)
-
-#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
-#define DCHECK_IS_ON() 0
-#else
-#define DCHECK_IS_ON() 1
-#endif
-
-// Debug mode: Use assert() for better diagnostics
-// Release mode, DCHECK_ALWAYS_ON: Use CHECK() since assert() is a no-op.
-// Release mode, no DCHECK_ALWAYS_ON: Use assert(), which is a no-op.
-#if defined(NDEBUG) && defined(DCHECK_ALWAYS_ON)
-#define DCHECK CHECK
-#else
-#define DCHECK assert
-#endif
-
-#define CHECK_EQ(x, y) CHECK((x) == (y))
-#define CHECK_NE(x, y) CHECK((x) != (y))
-#define DCHECK_EQ(x, y) DCHECK((x) == (y))
-#define DCHECK_NE(x, y) DCHECK((x) != (y))
-#define NOTREACHED() DCHECK(false)
-
-#endif  // THIRD_PARTY_BASE_LOGGING_H_
diff --git a/third_party/base/memory/aligned_memory.cc b/third_party/base/memory/aligned_memory.cc
new file mode 100644
index 0000000..10884e8
--- /dev/null
+++ b/third_party/base/memory/aligned_memory.cc
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 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.
+
+#include "third_party/base/memory/aligned_memory.h"
+
+#include "build/build_config.h"
+#include "third_party/base/check_op.h"
+
+#if BUILDFLAG(IS_ANDROID)
+#include <malloc.h>
+#endif
+
+namespace pdfium {
+namespace base {
+
+void* AlignedAlloc(size_t size, size_t alignment) {
+  DCHECK(size > 0U);
+  DCHECK(bits::IsPowerOfTwo(alignment));
+  DCHECK_EQ(alignment % sizeof(void*), 0U);
+  void* ptr = nullptr;
+#if defined(COMPILER_MSVC)
+  ptr = _aligned_malloc(size, alignment);
+#elif BUILDFLAG(IS_ANDROID)
+  // Android technically supports posix_memalign(), but does not expose it in
+  // the current version of the library headers used by Chrome.  Luckily,
+  // memalign() on Android returns pointers which can safely be used with
+  // free(), so we can use it instead.  Issue filed to document this:
+  // http://code.google.com/p/android/issues/detail?id=35391
+  ptr = memalign(alignment, size);
+#else
+  int ret = posix_memalign(&ptr, alignment, size);
+  if (ret != 0) {
+    ptr = nullptr;
+  }
+#endif
+
+  // Since aligned allocations may fail for non-memory related reasons, force a
+  // crash if we encounter a failed allocation; maintaining consistent behavior
+  // with a normal allocation failure in Chrome.
+  if (!ptr) {
+    CHECK(false);
+  }
+  // Sanity check alignment just to be safe.
+  DCHECK(IsAligned(ptr, alignment));
+  return ptr;
+}
+
+}  // namespace base
+}  // namespace pdfium
diff --git a/third_party/base/memory/aligned_memory.h b/third_party/base/memory/aligned_memory.h
new file mode 100644
index 0000000..26a2ff2
--- /dev/null
+++ b/third_party/base/memory/aligned_memory.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2012 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_MEMORY_ALIGNED_MEMORY_H_
+#define THIRD_PARTY_BASE_MEMORY_ALIGNED_MEMORY_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <type_traits>
+
+#include "build/build_config.h"
+#include "third_party/base/base_export.h"
+#include "third_party/base/bits.h"
+#include "third_party/base/check.h"
+
+#if defined(COMPILER_MSVC)
+#include <malloc.h>
+#else
+#include <stdlib.h>
+#endif
+
+// A runtime sized aligned allocation can be created:
+//
+//   float* my_array = static_cast<float*>(AlignedAlloc(size, alignment));
+//
+//   // ... later, to release the memory:
+//   AlignedFree(my_array);
+//
+// Or using unique_ptr:
+//
+//   std::unique_ptr<float, AlignedFreeDeleter> my_array(
+//       static_cast<float*>(AlignedAlloc(size, alignment)));
+
+namespace pdfium {
+namespace base {
+
+// This can be replaced with std::aligned_alloc when we have C++17.
+// Caveat: std::aligned_alloc requires the size parameter be an integral
+// multiple of alignment.
+BASE_EXPORT void* AlignedAlloc(size_t size, size_t alignment);
+
+inline void AlignedFree(void* ptr) {
+#if defined(COMPILER_MSVC)
+  _aligned_free(ptr);
+#else
+  free(ptr);
+#endif
+}
+
+// Deleter for use with unique_ptr. E.g., use as
+//   std::unique_ptr<Foo, base::AlignedFreeDeleter> foo;
+struct AlignedFreeDeleter {
+  inline void operator()(void* ptr) const {
+    AlignedFree(ptr);
+  }
+};
+
+#ifdef __has_builtin
+#define SUPPORTS_BUILTIN_IS_ALIGNED (__has_builtin(__builtin_is_aligned))
+#else
+#define SUPPORTS_BUILTIN_IS_ALIGNED 0
+#endif
+
+inline bool IsAligned(uintptr_t val, size_t alignment) {
+  // If the compiler supports builtin alignment checks prefer them.
+#if SUPPORTS_BUILTIN_IS_ALIGNED
+  return __builtin_is_aligned(val, alignment);
+#else
+  DCHECK(bits::IsPowerOfTwo(alignment));
+  return (val & (alignment - 1)) == 0;
+#endif
+}
+
+#undef SUPPORTS_BUILTIN_IS_ALIGNED
+
+inline bool IsAligned(void* val, size_t alignment) {
+  return IsAligned(reinterpret_cast<uintptr_t>(val), alignment);
+}
+
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_MEMORY_ALIGNED_MEMORY_H_
diff --git a/third_party/base/memory/ptr_util.h b/third_party/base/memory/ptr_util.h
new file mode 100644
index 0000000..c9df831
--- /dev/null
+++ b/third_party/base/memory/ptr_util.h
@@ -0,0 +1,22 @@
+// Copyright 2016 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_MEMORY_PTR_UTIL_H_
+#define THIRD_PARTY_BASE_MEMORY_PTR_UTIL_H_
+
+#include <memory>
+
+namespace pdfium {
+
+// Helper to transfer ownership of a raw pointer to a std::unique_ptr<T>.
+// Note that std::unique_ptr<T> has very different semantics from
+// std::unique_ptr<T[]>: do not use this helper for array allocations.
+template <typename T>
+std::unique_ptr<T> WrapUnique(T* ptr) {
+  return std::unique_ptr<T>(ptr);
+}
+
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_MEMORY_PTR_UTIL_H_
diff --git a/third_party/base/no_destructor.h b/third_party/base/no_destructor.h
index d9ff32e..5d125a9 100644
--- a/third_party/base/no_destructor.h
+++ b/third_party/base/no_destructor.h
@@ -6,27 +6,55 @@
 #define THIRD_PARTY_BASE_NO_DESTRUCTOR_H_
 
 #include <new>
+#include <type_traits>
 #include <utility>
 
 namespace pdfium {
 namespace base {
 
-// A wrapper that makes it easy to create an object of type T with static
-// storage duration that:
-// - is only constructed on first access
-// - never invokes the destructor
-// in order to satisfy the styleguide ban on global constructors and
-// destructors.
+// Helper type to create a function-local static variable of type `T` when `T`
+// has a non-trivial destructor. Storing a `T` in a `base::NoDestructor<T>` will
+// prevent `~T()` from running, even when the variable goes out of scope.
 //
-// Runtime constant example:
-// const std::string& GetLineSeparator() {
-//  // Forwards to std::string(size_t, char, const Allocator&) constructor.
-//   static const base::NoDestructor<std::string> s(5, '-');
+// Useful when a variable has static storage duration but its type has a
+// non-trivial destructor. Chromium bans global constructors and destructors:
+// using a function-local static variable prevents the former, while using
+// `base::NoDestructor<T>` prevents the latter.
+//
+// ## Caveats
+//
+// - Must only be used as a function-local static variable. Declaring a global
+//   variable of type `base::NoDestructor<T>` will still generate a global
+//   constructor; declaring a local or member variable will lead to memory leaks
+//   or other surprising and undesirable behaviour.
+//
+// - If the data is rarely used, consider creating it on demand rather than
+//   caching it for the lifetime of the program. Though `base::NoDestructor<T>`
+//   does not heap allocate, the compiler still reserves space in bss for
+//   storing `T`, which costs memory at runtime.
+//
+// - If `T` is trivially destructible, do not use `base::NoDestructor<T>`:
+//
+//     const uint64_t GetUnstableSessionSeed() {
+//       // No need to use `base::NoDestructor<T>` as `uint64_t` is trivially
+//       // destructible and does not require a global destructor.
+//       static const uint64_t kSessionSeed = base::RandUint64();
+//       return kSessionSeed;
+//     }
+//
+// ## Example Usage
+//
+// const std::string& GetDefaultText() {
+//   // Required since `static const std::string` requires a global destructor.
+//   static const base::NoDestructor<std::string> s("Hello world!");
 //   return *s;
 // }
 //
-// More complex initialization with a lambda:
-// const std::string& GetSessionNonce() {
+// More complex initialization using a lambda:
+//
+// const std::string& GetRandomNonce() {
+//   // `nonce` is initialized with random data the first time this function is
+//   // called, but its value is fixed thereafter.
 //   static const base::NoDestructor<std::string> nonce([] {
 //     std::string s(16);
 //     crypto::RandString(s.data(), s.size());
@@ -35,19 +63,25 @@
 //   return *nonce;
 // }
 //
-// NoDestructor<T> stores the object inline, so it also avoids a pointer
-// indirection and a malloc. Also note that since C++11 static local variable
-// initialization is thread-safe and so is this pattern. Code should prefer to
-// use NoDestructor<T> over:
-// - A function scoped static T* or T& that is dynamically initialized.
-// - A global base::LazyInstance<T>.
+// ## Thread safety
 //
-// Note that since the destructor is never run, this *will* leak memory if used
-// as a stack or member variable. Furthermore, a NoDestructor<T> should never
-// have global scope as that may require a static initializer.
+// Initialisation of function-local static variables is thread-safe since C++11.
+// The standard guarantees that:
+//
+// - function-local static variables will be initialised the first time
+//   execution passes through the declaration.
+//
+// - if another thread's execution concurrently passes through the declaration
+//   in the middle of initialisation, that thread will wait for the in-progress
+//   initialisation to complete.
 template <typename T>
 class NoDestructor {
  public:
+  static_assert(
+      !std::is_trivially_destructible_v<T>,
+      "T is trivially destructible; please use a function-local static "
+      "of type T directly instead");
+
   // Not constexpr; just write static constexpr T x = ...; if the value should
   // be a constexpr.
   template <typename... Args>
diff --git a/third_party/base/notreached.h b/third_party/base/notreached.h
new file mode 100644
index 0000000..124abd3
--- /dev/null
+++ b/third_party/base/notreached.h
@@ -0,0 +1,24 @@
+// Copyright 2020 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_NOTREACHED_H_
+#define THIRD_PARTY_BASE_NOTREACHED_H_
+
+#include <assert.h>
+
+#include "third_party/base/check.h"
+
+// TODO(crbug.com/pdfium/2008): Migrate NOTREACHED() callers to
+// NOTREACHED_NORETURN() which is [[noreturn]] and always FATAL. Once that's
+// done, rename NOTREACHED_NORETURN() back to NOTREACHED() and remove the
+// non-FATAL version.
+#define NOTREACHED() DCHECK(false)
+
+// NOTREACHED_NORETURN() annotates paths that are supposed to be unreachable.
+// They crash if they are ever hit.
+// TODO(crbug.com/pdfium/2008): Rename back to NOTREACHED() once there are no
+// callers of the old non-CHECK-fatal macro.
+#define NOTREACHED_NORETURN() CHECK(false)
+
+#endif  // THIRD_PARTY_BASE_NOTREACHED_H_
diff --git a/third_party/base/numerics/README.pdfium b/third_party/base/numerics/README.pdfium
new file mode 100644
index 0000000..c1ad6be
--- /dev/null
+++ b/third_party/base/numerics/README.pdfium
@@ -0,0 +1,8 @@
+Shipped: yes
+
+Description:
+Chromium's safe numerics library
+
+Local Modifications:
+
+Namespaced from base:: to pdfium::base:: in all files.
diff --git a/third_party/base/numerics/checked_math.h b/third_party/base/numerics/checked_math.h
new file mode 100644
index 0000000..9eb5b07
--- /dev/null
+++ b/third_party/base/numerics/checked_math.h
@@ -0,0 +1,382 @@
+// Copyright 2017 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_NUMERICS_CHECKED_MATH_H_
+#define THIRD_PARTY_BASE_NUMERICS_CHECKED_MATH_H_
+
+#include <stddef.h>
+
+#include <limits>
+#include <type_traits>
+
+#include "third_party/base/numerics/checked_math_impl.h"
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+template <typename T>
+class CheckedNumeric {
+  static_assert(std::is_arithmetic<T>::value,
+                "CheckedNumeric<T>: T must be a numeric type.");
+
+ public:
+  template <typename Src>
+  friend class CheckedNumeric;
+
+  using type = T;
+
+  constexpr CheckedNumeric() = default;
+
+  // Copy constructor.
+  template <typename Src>
+  constexpr CheckedNumeric(const CheckedNumeric<Src>& rhs)
+      : state_(rhs.state_.value(), rhs.IsValid()) {}
+
+  // Strictly speaking, this is not necessary, but declaring this allows class
+  // template argument deduction to be used so that it is possible to simply
+  // write `CheckedNumeric(777)` instead of `CheckedNumeric<int>(777)`.
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  constexpr CheckedNumeric(T value) : state_(value) {}
+
+  // This is not an explicit constructor because we implicitly upgrade regular
+  // numerics to CheckedNumerics to make them easier to use.
+  template <typename Src,
+            typename = std::enable_if_t<std::is_arithmetic<Src>::value>>
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  constexpr CheckedNumeric(Src value) : state_(value) {}
+
+  // This is not an explicit constructor because we want a seamless conversion
+  // from StrictNumeric types.
+  template <typename Src>
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  constexpr CheckedNumeric(StrictNumeric<Src> value)
+      : state_(static_cast<Src>(value)) {}
+
+  // IsValid() - The public API to test if a CheckedNumeric is currently valid.
+  // A range checked destination type can be supplied using the Dst template
+  // parameter.
+  template <typename Dst = T>
+  constexpr bool IsValid() const {
+    return state_.is_valid() &&
+           IsValueInRangeForNumericType<Dst>(state_.value());
+  }
+
+  // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid
+  // and is within the range supported by the destination type. Returns true if
+  // successful and false otherwise.
+  template <typename Dst>
+#if defined(__clang__) || defined(__GNUC__)
+  __attribute__((warn_unused_result))
+#elif defined(_MSC_VER)
+  _Check_return_
+#endif
+  constexpr bool
+  AssignIfValid(Dst* result) const {
+    return BASE_NUMERICS_LIKELY(IsValid<Dst>())
+               ? ((*result = static_cast<Dst>(state_.value())), true)
+               : false;
+  }
+
+  // ValueOrDie() - The primary accessor for the underlying value. If the
+  // current state is not valid it will CHECK and crash.
+  // A range checked destination type can be supplied using the Dst template
+  // parameter, which will trigger a CHECK if the value is not in bounds for
+  // the destination.
+  // The CHECK behavior can be overridden by supplying a handler as a
+  // template parameter, for test code, etc. However, the handler cannot access
+  // the underlying value, and it is not available through other means.
+  template <typename Dst = T, class CheckHandler = CheckOnFailure>
+  constexpr StrictNumeric<Dst> ValueOrDie() const {
+    return BASE_NUMERICS_LIKELY(IsValid<Dst>())
+               ? static_cast<Dst>(state_.value())
+               : CheckHandler::template HandleFailure<Dst>();
+  }
+
+  // ValueOrDefault(T default_value) - A convenience method that returns the
+  // current value if the state is valid, and the supplied default_value for
+  // any other state.
+  // A range checked destination type can be supplied using the Dst template
+  // parameter. WARNING: This function may fail to compile or CHECK at runtime
+  // if the supplied default_value is not within range of the destination type.
+  template <typename Dst = T, typename Src>
+  constexpr StrictNumeric<Dst> ValueOrDefault(const Src default_value) const {
+    return BASE_NUMERICS_LIKELY(IsValid<Dst>())
+               ? static_cast<Dst>(state_.value())
+               : checked_cast<Dst>(default_value);
+  }
+
+  // Returns a checked numeric of the specified type, cast from the current
+  // CheckedNumeric. If the current state is invalid or the destination cannot
+  // represent the result then the returned CheckedNumeric will be invalid.
+  template <typename Dst>
+  constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
+    return *this;
+  }
+
+  // This friend method is available solely for providing more detailed logging
+  // in the tests. Do not implement it in production code, because the
+  // underlying values may change at any time.
+  template <typename U>
+  friend U GetNumericValueForTest(const CheckedNumeric<U>& src);
+
+  // Prototypes for the supported arithmetic operator overloads.
+  template <typename Src>
+  constexpr CheckedNumeric& operator+=(const Src rhs);
+  template <typename Src>
+  constexpr CheckedNumeric& operator-=(const Src rhs);
+  template <typename Src>
+  constexpr CheckedNumeric& operator*=(const Src rhs);
+  template <typename Src>
+  constexpr CheckedNumeric& operator/=(const Src rhs);
+  template <typename Src>
+  constexpr CheckedNumeric& operator%=(const Src rhs);
+  template <typename Src>
+  constexpr CheckedNumeric& operator<<=(const Src rhs);
+  template <typename Src>
+  constexpr CheckedNumeric& operator>>=(const Src rhs);
+  template <typename Src>
+  constexpr CheckedNumeric& operator&=(const Src rhs);
+  template <typename Src>
+  constexpr CheckedNumeric& operator|=(const Src rhs);
+  template <typename Src>
+  constexpr CheckedNumeric& operator^=(const Src rhs);
+
+  constexpr CheckedNumeric operator-() const {
+    // Use an optimized code path for a known run-time variable.
+    if (!IsConstantEvaluated() && std::is_signed<T>::value &&
+        std::is_floating_point<T>::value) {
+      return FastRuntimeNegate();
+    }
+    // The negation of two's complement int min is int min.
+    const bool is_valid =
+        IsValid() &&
+        (!std::is_signed<T>::value || std::is_floating_point<T>::value ||
+         NegateWrapper(state_.value()) != std::numeric_limits<T>::lowest());
+    return CheckedNumeric<T>(NegateWrapper(state_.value()), is_valid);
+  }
+
+  constexpr CheckedNumeric operator~() const {
+    return CheckedNumeric<decltype(InvertWrapper(T()))>(
+        InvertWrapper(state_.value()), IsValid());
+  }
+
+  constexpr CheckedNumeric Abs() const {
+    return !IsValueNegative(state_.value()) ? *this : -*this;
+  }
+
+  template <typename U>
+  constexpr CheckedNumeric<typename MathWrapper<CheckedMaxOp, T, U>::type> Max(
+      const U rhs) const {
+    return CheckMax(*this, rhs);
+  }
+
+  template <typename U>
+  constexpr CheckedNumeric<typename MathWrapper<CheckedMinOp, T, U>::type> Min(
+      const U rhs) const {
+    return CheckMin(*this, rhs);
+  }
+
+  // This function is available only for integral types. It returns an unsigned
+  // integer of the same width as the source type, containing the absolute value
+  // of the source, and properly handling signed min.
+  constexpr CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>
+  UnsignedAbs() const {
+    return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>(
+        SafeUnsignedAbs(state_.value()), state_.is_valid());
+  }
+
+  constexpr CheckedNumeric& operator++() {
+    *this += 1;
+    return *this;
+  }
+
+  constexpr CheckedNumeric operator++(int) {
+    CheckedNumeric value = *this;
+    *this += 1;
+    return value;
+  }
+
+  constexpr CheckedNumeric& operator--() {
+    *this -= 1;
+    return *this;
+  }
+
+  constexpr CheckedNumeric operator--(int) {
+    // TODO(pkasting): Consider std::exchange() once it's constexpr in C++20.
+    const CheckedNumeric value = *this;
+    *this -= 1;
+    return value;
+  }
+
+  // These perform the actual math operations on the CheckedNumerics.
+  // Binary arithmetic operations.
+  template <template <typename, typename, typename> class M,
+            typename L,
+            typename R>
+  static constexpr CheckedNumeric MathOp(const L lhs, const R rhs) {
+    using Math = typename MathWrapper<M, L, R>::math;
+    T result = 0;
+    const bool is_valid =
+        Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) &&
+        Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result);
+    return CheckedNumeric<T>(result, is_valid);
+  }
+
+  // Assignment arithmetic operations.
+  template <template <typename, typename, typename> class M, typename R>
+  constexpr CheckedNumeric& MathOp(const R rhs) {
+    using Math = typename MathWrapper<M, T, R>::math;
+    T result = 0;  // Using T as the destination saves a range check.
+    const bool is_valid =
+        state_.is_valid() && Wrapper<R>::is_valid(rhs) &&
+        Math::Do(state_.value(), Wrapper<R>::value(rhs), &result);
+    *this = CheckedNumeric<T>(result, is_valid);
+    return *this;
+  }
+
+ private:
+  CheckedNumericState<T> state_;
+
+  CheckedNumeric FastRuntimeNegate() const {
+    T result;
+    const bool success = CheckedSubOp<T, T>::Do(T(0), state_.value(), &result);
+    return CheckedNumeric<T>(result, IsValid() && success);
+  }
+
+  template <typename Src>
+  constexpr CheckedNumeric(Src value, bool is_valid)
+      : state_(value, is_valid) {}
+
+  // These wrappers allow us to handle state the same way for both
+  // CheckedNumeric and POD arithmetic types.
+  template <typename Src>
+  struct Wrapper {
+    static constexpr bool is_valid(Src) { return true; }
+    static constexpr Src value(Src value) { return value; }
+  };
+
+  template <typename Src>
+  struct Wrapper<CheckedNumeric<Src>> {
+    static constexpr bool is_valid(const CheckedNumeric<Src> v) {
+      return v.IsValid();
+    }
+    static constexpr Src value(const CheckedNumeric<Src> v) {
+      return v.state_.value();
+    }
+  };
+
+  template <typename Src>
+  struct Wrapper<StrictNumeric<Src>> {
+    static constexpr bool is_valid(const StrictNumeric<Src>) { return true; }
+    static constexpr Src value(const StrictNumeric<Src> v) {
+      return static_cast<Src>(v);
+    }
+  };
+};
+
+// Convenience functions to avoid the ugly template disambiguator syntax.
+template <typename Dst, typename Src>
+constexpr bool IsValidForType(const CheckedNumeric<Src> value) {
+  return value.template IsValid<Dst>();
+}
+
+template <typename Dst, typename Src>
+constexpr StrictNumeric<Dst> ValueOrDieForType(
+    const CheckedNumeric<Src> value) {
+  return value.template ValueOrDie<Dst>();
+}
+
+template <typename Dst, typename Src, typename Default>
+constexpr StrictNumeric<Dst> ValueOrDefaultForType(
+    const CheckedNumeric<Src> value,
+    const Default default_value) {
+  return value.template ValueOrDefault<Dst>(default_value);
+}
+
+// Convenience wrapper to return a new CheckedNumeric from the provided
+// arithmetic or CheckedNumericType.
+template <typename T>
+constexpr CheckedNumeric<typename UnderlyingType<T>::type> MakeCheckedNum(
+    const T value) {
+  return value;
+}
+
+// These implement the variadic wrapper for the math operations.
+template <template <typename, typename, typename> class M,
+          typename L,
+          typename R>
+constexpr CheckedNumeric<typename MathWrapper<M, L, R>::type> CheckMathOp(
+    const L lhs,
+    const R rhs) {
+  using Math = typename MathWrapper<M, L, R>::math;
+  return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
+                                                                        rhs);
+}
+
+// General purpose wrapper template for arithmetic operations.
+template <template <typename, typename, typename> class M,
+          typename L,
+          typename R,
+          typename... Args>
+constexpr auto CheckMathOp(const L lhs, const R rhs, const Args... args) {
+  return CheckMathOp<M>(CheckMathOp<M>(lhs, rhs), args...);
+}
+
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Add, +, +=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Sub, -, -=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mul, *, *=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Div, /, /=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mod, %, %=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Lsh, <<, <<=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Rsh, >>, >>=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, And, &, &=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Or, |, |=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Xor, ^, ^=)
+BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Max)
+BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Min)
+
+// These are some extra StrictNumeric operators to support simple pointer
+// arithmetic with our result types. Since wrapping on a pointer is always
+// bad, we trigger the CHECK condition here.
+template <typename L, typename R>
+L* operator+(L* lhs, const StrictNumeric<R> rhs) {
+  const uintptr_t result = CheckAdd(reinterpret_cast<uintptr_t>(lhs),
+                                    CheckMul(sizeof(L), static_cast<R>(rhs)))
+                               .template ValueOrDie<uintptr_t>();
+  return reinterpret_cast<L*>(result);
+}
+
+template <typename L, typename R>
+L* operator-(L* lhs, const StrictNumeric<R> rhs) {
+  const uintptr_t result = CheckSub(reinterpret_cast<uintptr_t>(lhs),
+                                    CheckMul(sizeof(L), static_cast<R>(rhs)))
+                               .template ValueOrDie<uintptr_t>();
+  return reinterpret_cast<L*>(result);
+}
+
+}  // namespace internal
+
+using internal::CheckAdd;
+using internal::CheckAnd;
+using internal::CheckDiv;
+using internal::CheckedNumeric;
+using internal::CheckLsh;
+using internal::CheckMax;
+using internal::CheckMin;
+using internal::CheckMod;
+using internal::CheckMul;
+using internal::CheckOr;
+using internal::CheckRsh;
+using internal::CheckSub;
+using internal::CheckXor;
+using internal::IsValidForType;
+using internal::MakeCheckedNum;
+using internal::ValueOrDefaultForType;
+using internal::ValueOrDieForType;
+
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_NUMERICS_CHECKED_MATH_H_
diff --git a/third_party/base/numerics/checked_math_impl.h b/third_party/base/numerics/checked_math_impl.h
new file mode 100644
index 0000000..47c73d5
--- /dev/null
+++ b/third_party/base/numerics/checked_math_impl.h
@@ -0,0 +1,595 @@
+// Copyright 2017 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_NUMERICS_CHECKED_MATH_IMPL_H_
+#define THIRD_PARTY_BASE_NUMERICS_CHECKED_MATH_IMPL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <climits>
+#include <cmath>
+#include <cstdlib>
+#include <limits>
+#include <type_traits>
+
+#include "third_party/base/numerics/safe_conversions.h"
+#include "third_party/base/numerics/safe_math_shared_impl.h"
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+template <typename T>
+constexpr bool CheckedAddImpl(T x, T y, T* result) {
+  static_assert(std::is_integral<T>::value, "Type must be integral");
+  // Since the value of x+y is undefined if we have a signed type, we compute
+  // it using the unsigned type of the same size.
+  using UnsignedDst = typename std::make_unsigned<T>::type;
+  using SignedDst = typename std::make_signed<T>::type;
+  const UnsignedDst ux = static_cast<UnsignedDst>(x);
+  const UnsignedDst uy = static_cast<UnsignedDst>(y);
+  const UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
+  // Addition is valid if the sign of (x + y) is equal to either that of x or
+  // that of y.
+  if (std::is_signed<T>::value
+          ? static_cast<SignedDst>((uresult ^ ux) & (uresult ^ uy)) < 0
+          : uresult < uy)  // Unsigned is either valid or underflow.
+    return false;
+  *result = static_cast<T>(uresult);
+  return true;
+}
+
+template <typename T, typename U, class Enable = void>
+struct CheckedAddOp {};
+
+template <typename T, typename U>
+struct CheckedAddOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = typename MaxExponentPromotion<T, U>::type;
+  template <typename V>
+  static constexpr bool Do(T x, U y, V* result) {
+    if constexpr (CheckedAddFastOp<T, U>::is_supported)
+      return CheckedAddFastOp<T, U>::Do(x, y, result);
+
+    // Double the underlying type up to a full machine word.
+    using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+    using Promotion =
+        typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value >
+                                   IntegerBitsPlusSign<intptr_t>::value),
+                                  typename BigEnoughPromotion<T, U>::type,
+                                  FastPromotion>::type;
+    // Fail if either operand is out of range for the promoted type.
+    // TODO(jschuh): This could be made to work for a broader range of values.
+    if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||
+                               !IsValueInRangeForNumericType<Promotion>(y))) {
+      return false;
+    }
+
+    Promotion presult = {};
+    bool is_valid = true;
+    if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
+      presult = static_cast<Promotion>(x) + static_cast<Promotion>(y);
+    } else {
+      is_valid = CheckedAddImpl(static_cast<Promotion>(x),
+                                static_cast<Promotion>(y), &presult);
+    }
+    if (!is_valid || !IsValueInRangeForNumericType<V>(presult))
+      return false;
+    *result = static_cast<V>(presult);
+    return true;
+  }
+};
+
+template <typename T>
+constexpr bool CheckedSubImpl(T x, T y, T* result) {
+  static_assert(std::is_integral<T>::value, "Type must be integral");
+  // Since the value of x+y is undefined if we have a signed type, we compute
+  // it using the unsigned type of the same size.
+  using UnsignedDst = typename std::make_unsigned<T>::type;
+  using SignedDst = typename std::make_signed<T>::type;
+  const UnsignedDst ux = static_cast<UnsignedDst>(x);
+  const UnsignedDst uy = static_cast<UnsignedDst>(y);
+  const UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
+  // Subtraction is valid if either x and y have same sign, or (x-y) and x have
+  // the same sign.
+  if (std::is_signed<T>::value
+          ? static_cast<SignedDst>((uresult ^ ux) & (ux ^ uy)) < 0
+          : x < y)
+    return false;
+  *result = static_cast<T>(uresult);
+  return true;
+}
+
+template <typename T, typename U, class Enable = void>
+struct CheckedSubOp {};
+
+template <typename T, typename U>
+struct CheckedSubOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = typename MaxExponentPromotion<T, U>::type;
+  template <typename V>
+  static constexpr bool Do(T x, U y, V* result) {
+    if constexpr (CheckedSubFastOp<T, U>::is_supported)
+      return CheckedSubFastOp<T, U>::Do(x, y, result);
+
+    // Double the underlying type up to a full machine word.
+    using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+    using Promotion =
+        typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value >
+                                   IntegerBitsPlusSign<intptr_t>::value),
+                                  typename BigEnoughPromotion<T, U>::type,
+                                  FastPromotion>::type;
+    // Fail if either operand is out of range for the promoted type.
+    // TODO(jschuh): This could be made to work for a broader range of values.
+    if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||
+                               !IsValueInRangeForNumericType<Promotion>(y))) {
+      return false;
+    }
+
+    Promotion presult = {};
+    bool is_valid = true;
+    if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
+      presult = static_cast<Promotion>(x) - static_cast<Promotion>(y);
+    } else {
+      is_valid = CheckedSubImpl(static_cast<Promotion>(x),
+                                static_cast<Promotion>(y), &presult);
+    }
+    if (!is_valid || !IsValueInRangeForNumericType<V>(presult))
+      return false;
+    *result = static_cast<V>(presult);
+    return true;
+  }
+};
+
+template <typename T>
+constexpr bool CheckedMulImpl(T x, T y, T* result) {
+  static_assert(std::is_integral<T>::value, "Type must be integral");
+  // Since the value of x*y is potentially undefined if we have a signed type,
+  // we compute it using the unsigned type of the same size.
+  using UnsignedDst = typename std::make_unsigned<T>::type;
+  using SignedDst = typename std::make_signed<T>::type;
+  const UnsignedDst ux = SafeUnsignedAbs(x);
+  const UnsignedDst uy = SafeUnsignedAbs(y);
+  const UnsignedDst uresult = static_cast<UnsignedDst>(ux * uy);
+  const bool is_negative =
+      std::is_signed<T>::value && static_cast<SignedDst>(x ^ y) < 0;
+  // We have a fast out for unsigned identity or zero on the second operand.
+  // After that it's an unsigned overflow check on the absolute value, with
+  // a +1 bound for a negative result.
+  if (uy > UnsignedDst(!std::is_signed<T>::value || is_negative) &&
+      ux > (std::numeric_limits<T>::max() + UnsignedDst(is_negative)) / uy)
+    return false;
+  *result = static_cast<T>(is_negative ? 0 - uresult : uresult);
+  return true;
+}
+
+template <typename T, typename U, class Enable = void>
+struct CheckedMulOp {};
+
+template <typename T, typename U>
+struct CheckedMulOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = typename MaxExponentPromotion<T, U>::type;
+  template <typename V>
+  static constexpr bool Do(T x, U y, V* result) {
+    if constexpr (CheckedMulFastOp<T, U>::is_supported)
+      return CheckedMulFastOp<T, U>::Do(x, y, result);
+
+    using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+    // Verify the destination type can hold the result (always true for 0).
+    if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||
+                                !IsValueInRangeForNumericType<Promotion>(y)) &&
+                               x && y)) {
+      return false;
+    }
+
+    Promotion presult = {};
+    bool is_valid = true;
+    if (CheckedMulFastOp<Promotion, Promotion>::is_supported) {
+      // The fast op may be available with the promoted type.
+      // The casts here are safe because of the "value in range" conditional
+      // above.
+      is_valid = CheckedMulFastOp<Promotion, Promotion>::Do(
+          static_cast<Promotion>(x), static_cast<Promotion>(y), &presult);
+    } else if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
+      presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
+    } else {
+      is_valid = CheckedMulImpl(static_cast<Promotion>(x),
+                                static_cast<Promotion>(y), &presult);
+    }
+    if (!is_valid || !IsValueInRangeForNumericType<V>(presult))
+      return false;
+    *result = static_cast<V>(presult);
+    return true;
+  }
+};
+
+// Division just requires a check for a zero denominator or an invalid negation
+// on signed min/-1.
+template <typename T, typename U, class Enable = void>
+struct CheckedDivOp {};
+
+template <typename T, typename U>
+struct CheckedDivOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = typename MaxExponentPromotion<T, U>::type;
+  template <typename V>
+  static constexpr bool Do(T x, U y, V* result) {
+    if (BASE_NUMERICS_UNLIKELY(!y))
+      return false;
+
+    // The overflow check can be compiled away if we don't have the exact
+    // combination of types needed to trigger this case.
+    using Promotion = typename BigEnoughPromotion<T, U>::type;
+    if (BASE_NUMERICS_UNLIKELY(
+            (std::is_signed<T>::value && std::is_signed<U>::value &&
+             IsTypeInRangeForNumericType<T, Promotion>::value &&
+             static_cast<Promotion>(x) ==
+                 std::numeric_limits<Promotion>::lowest() &&
+             y == static_cast<U>(-1)))) {
+      return false;
+    }
+
+    // This branch always compiles away if the above branch wasn't removed.
+    if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||
+                                !IsValueInRangeForNumericType<Promotion>(y)) &&
+                               x)) {
+      return false;
+    }
+
+    const Promotion presult = Promotion(x) / Promotion(y);
+    if (!IsValueInRangeForNumericType<V>(presult))
+      return false;
+    *result = static_cast<V>(presult);
+    return true;
+  }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedModOp {};
+
+template <typename T, typename U>
+struct CheckedModOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = typename MaxExponentPromotion<T, U>::type;
+  template <typename V>
+  static constexpr bool Do(T x, U y, V* result) {
+    if (BASE_NUMERICS_UNLIKELY(!y))
+      return false;
+
+    using Promotion = typename BigEnoughPromotion<T, U>::type;
+    if (BASE_NUMERICS_UNLIKELY(
+            (std::is_signed<T>::value && std::is_signed<U>::value &&
+             IsTypeInRangeForNumericType<T, Promotion>::value &&
+             static_cast<Promotion>(x) ==
+                 std::numeric_limits<Promotion>::lowest() &&
+             y == static_cast<U>(-1)))) {
+      *result = 0;
+      return true;
+    }
+
+    const Promotion presult =
+        static_cast<Promotion>(x) % static_cast<Promotion>(y);
+    if (!IsValueInRangeForNumericType<V>(presult))
+      return false;
+    *result = static_cast<Promotion>(presult);
+    return true;
+  }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedLshOp {};
+
+// Left shift. Shifts less than 0 or greater than or equal to the number
+// of bits in the promoted type are undefined. Shifts of negative values
+// are undefined. Otherwise it is defined when the result fits.
+template <typename T, typename U>
+struct CheckedLshOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = T;
+  template <typename V>
+  static constexpr bool Do(T x, U shift, V* result) {
+    // Disallow negative numbers and verify the shift is in bounds.
+    if (BASE_NUMERICS_LIKELY(!IsValueNegative(x) &&
+                             as_unsigned(shift) <
+                                 as_unsigned(std::numeric_limits<T>::digits))) {
+      // Shift as unsigned to avoid undefined behavior.
+      *result = static_cast<V>(as_unsigned(x) << shift);
+      // If the shift can be reversed, we know it was valid.
+      return *result >> shift == x;
+    }
+
+    // Handle the legal corner-case of a full-width signed shift of zero.
+    if (!std::is_signed<T>::value || x ||
+        as_unsigned(shift) != as_unsigned(std::numeric_limits<T>::digits))
+      return false;
+    *result = 0;
+    return true;
+  }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedRshOp {};
+
+// Right shift. Shifts less than 0 or greater than or equal to the number
+// of bits in the promoted type are undefined. Otherwise, it is always defined,
+// but a right shift of a negative value is implementation-dependent.
+template <typename T, typename U>
+struct CheckedRshOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = T;
+  template <typename V>
+  static constexpr bool Do(T x, U shift, V* result) {
+    // Use sign conversion to push negative values out of range.
+    if (BASE_NUMERICS_UNLIKELY(as_unsigned(shift) >=
+                               IntegerBitsPlusSign<T>::value)) {
+      return false;
+    }
+
+    const T tmp = x >> shift;
+    if (!IsValueInRangeForNumericType<V>(tmp))
+      return false;
+    *result = static_cast<V>(tmp);
+    return true;
+  }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedAndOp {};
+
+// For simplicity we support only unsigned integer results.
+template <typename T, typename U>
+struct CheckedAndOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = typename std::make_unsigned<
+      typename MaxExponentPromotion<T, U>::type>::type;
+  template <typename V>
+  static constexpr bool Do(T x, U y, V* result) {
+    const result_type tmp =
+        static_cast<result_type>(x) & static_cast<result_type>(y);
+    if (!IsValueInRangeForNumericType<V>(tmp))
+      return false;
+    *result = static_cast<V>(tmp);
+    return true;
+  }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedOrOp {};
+
+// For simplicity we support only unsigned integers.
+template <typename T, typename U>
+struct CheckedOrOp<T,
+                   U,
+                   typename std::enable_if<std::is_integral<T>::value &&
+                                           std::is_integral<U>::value>::type> {
+  using result_type = typename std::make_unsigned<
+      typename MaxExponentPromotion<T, U>::type>::type;
+  template <typename V>
+  static constexpr bool Do(T x, U y, V* result) {
+    const result_type tmp =
+        static_cast<result_type>(x) | static_cast<result_type>(y);
+    if (!IsValueInRangeForNumericType<V>(tmp))
+      return false;
+    *result = static_cast<V>(tmp);
+    return true;
+  }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedXorOp {};
+
+// For simplicity we support only unsigned integers.
+template <typename T, typename U>
+struct CheckedXorOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = typename std::make_unsigned<
+      typename MaxExponentPromotion<T, U>::type>::type;
+  template <typename V>
+  static constexpr bool Do(T x, U y, V* result) {
+    const result_type tmp =
+        static_cast<result_type>(x) ^ static_cast<result_type>(y);
+    if (!IsValueInRangeForNumericType<V>(tmp))
+      return false;
+    *result = static_cast<V>(tmp);
+    return true;
+  }
+};
+
+// Max doesn't really need to be implemented this way because it can't fail,
+// but it makes the code much cleaner to use the MathOp wrappers.
+template <typename T, typename U, class Enable = void>
+struct CheckedMaxOp {};
+
+template <typename T, typename U>
+struct CheckedMaxOp<
+    T,
+    U,
+    typename std::enable_if<std::is_arithmetic<T>::value &&
+                            std::is_arithmetic<U>::value>::type> {
+  using result_type = typename MaxExponentPromotion<T, U>::type;
+  template <typename V>
+  static constexpr bool Do(T x, U y, V* result) {
+    const result_type tmp = IsGreater<T, U>::Test(x, y)
+                                ? static_cast<result_type>(x)
+                                : static_cast<result_type>(y);
+    if (!IsValueInRangeForNumericType<V>(tmp))
+      return false;
+    *result = static_cast<V>(tmp);
+    return true;
+  }
+};
+
+// Min doesn't really need to be implemented this way because it can't fail,
+// but it makes the code much cleaner to use the MathOp wrappers.
+template <typename T, typename U, class Enable = void>
+struct CheckedMinOp {};
+
+template <typename T, typename U>
+struct CheckedMinOp<
+    T,
+    U,
+    typename std::enable_if<std::is_arithmetic<T>::value &&
+                            std::is_arithmetic<U>::value>::type> {
+  using result_type = typename LowestValuePromotion<T, U>::type;
+  template <typename V>
+  static constexpr bool Do(T x, U y, V* result) {
+    const result_type tmp = IsLess<T, U>::Test(x, y)
+                                ? static_cast<result_type>(x)
+                                : static_cast<result_type>(y);
+    if (!IsValueInRangeForNumericType<V>(tmp))
+      return false;
+    *result = static_cast<V>(tmp);
+    return true;
+  }
+};
+
+// This is just boilerplate that wraps the standard floating point arithmetic.
+// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
+#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP)                              \
+  template <typename T, typename U>                                      \
+  struct Checked##NAME##Op<                                              \
+      T, U,                                                              \
+      typename std::enable_if<std::is_floating_point<T>::value ||        \
+                              std::is_floating_point<U>::value>::type> { \
+    using result_type = typename MaxExponentPromotion<T, U>::type;       \
+    template <typename V>                                                \
+    static constexpr bool Do(T x, U y, V* result) {                      \
+      using Promotion = typename MaxExponentPromotion<T, U>::type;       \
+      const Promotion presult = x OP y;                                  \
+      if (!IsValueInRangeForNumericType<V>(presult))                     \
+        return false;                                                    \
+      *result = static_cast<V>(presult);                                 \
+      return true;                                                       \
+    }                                                                    \
+  };
+
+BASE_FLOAT_ARITHMETIC_OPS(Add, +)
+BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
+BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
+BASE_FLOAT_ARITHMETIC_OPS(Div, /)
+
+#undef BASE_FLOAT_ARITHMETIC_OPS
+
+// Floats carry around their validity state with them, but integers do not. So,
+// we wrap the underlying value in a specialization in order to hide that detail
+// and expose an interface via accessors.
+enum NumericRepresentation {
+  NUMERIC_INTEGER,
+  NUMERIC_FLOATING,
+  NUMERIC_UNKNOWN
+};
+
+template <typename NumericType>
+struct GetNumericRepresentation {
+  static const NumericRepresentation value =
+      std::is_integral<NumericType>::value
+          ? NUMERIC_INTEGER
+          : (std::is_floating_point<NumericType>::value ? NUMERIC_FLOATING
+                                                        : NUMERIC_UNKNOWN);
+};
+
+template <typename T,
+          NumericRepresentation type = GetNumericRepresentation<T>::value>
+class CheckedNumericState {};
+
+// Integrals require quite a bit of additional housekeeping to manage state.
+template <typename T>
+class CheckedNumericState<T, NUMERIC_INTEGER> {
+ public:
+  template <typename Src = int>
+  constexpr explicit CheckedNumericState(Src value = 0, bool is_valid = true)
+      : is_valid_(is_valid && IsValueInRangeForNumericType<T>(value)),
+        value_(WellDefinedConversionOrZero(value, is_valid_)) {
+    static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
+  }
+
+  template <typename Src>
+  constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
+      : CheckedNumericState(rhs.value(), rhs.is_valid()) {}
+
+  constexpr bool is_valid() const { return is_valid_; }
+
+  constexpr T value() const { return value_; }
+
+ private:
+  // Ensures that a type conversion does not trigger undefined behavior.
+  template <typename Src>
+  static constexpr T WellDefinedConversionOrZero(Src value, bool is_valid) {
+    using SrcType = typename internal::UnderlyingType<Src>::type;
+    return (std::is_integral<SrcType>::value || is_valid)
+               ? static_cast<T>(value)
+               : 0;
+  }
+
+  // is_valid_ precedes value_ because member initializers in the constructors
+  // are evaluated in field order, and is_valid_ must be read when initializing
+  // value_.
+  bool is_valid_;
+  T value_;
+};
+
+// Floating points maintain their own validity, but need translation wrappers.
+template <typename T>
+class CheckedNumericState<T, NUMERIC_FLOATING> {
+ public:
+  template <typename Src = double>
+  constexpr explicit CheckedNumericState(Src value = 0.0, bool is_valid = true)
+      : value_(WellDefinedConversionOrNaN(
+            value,
+            is_valid && IsValueInRangeForNumericType<T>(value))) {}
+
+  template <typename Src>
+  constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
+      : CheckedNumericState(rhs.value(), rhs.is_valid()) {}
+
+  constexpr bool is_valid() const {
+    // Written this way because std::isfinite is not reliably constexpr.
+    return IsConstantEvaluated()
+               ? value_ <= std::numeric_limits<T>::max() &&
+                     value_ >= std::numeric_limits<T>::lowest()
+               : std::isfinite(value_);
+  }
+
+  constexpr T value() const { return value_; }
+
+ private:
+  // Ensures that a type conversion does not trigger undefined behavior.
+  template <typename Src>
+  static constexpr T WellDefinedConversionOrNaN(Src value, bool is_valid) {
+    using SrcType = typename internal::UnderlyingType<Src>::type;
+    return (StaticDstRangeRelationToSrcRange<T, SrcType>::value ==
+                NUMERIC_RANGE_CONTAINED ||
+            is_valid)
+               ? static_cast<T>(value)
+               : std::numeric_limits<T>::quiet_NaN();
+  }
+
+  T value_;
+};
+
+}  // namespace internal
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_NUMERICS_CHECKED_MATH_IMPL_H_
diff --git a/third_party/base/numerics/clamped_math.h b/third_party/base/numerics/clamped_math.h
new file mode 100644
index 0000000..019bc31
--- /dev/null
+++ b/third_party/base/numerics/clamped_math.h
@@ -0,0 +1,262 @@
+// Copyright 2017 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_NUMERICS_CLAMPED_MATH_H_
+#define THIRD_PARTY_BASE_NUMERICS_CLAMPED_MATH_H_
+
+#include <stddef.h>
+
+#include <limits>
+#include <type_traits>
+
+#include "third_party/base/numerics/clamped_math_impl.h"
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+template <typename T>
+class ClampedNumeric {
+  static_assert(std::is_arithmetic<T>::value,
+                "ClampedNumeric<T>: T must be a numeric type.");
+
+ public:
+  using type = T;
+
+  constexpr ClampedNumeric() : value_(0) {}
+
+  // Copy constructor.
+  template <typename Src>
+  constexpr ClampedNumeric(const ClampedNumeric<Src>& rhs)
+      : value_(saturated_cast<T>(rhs.value_)) {}
+
+  template <typename Src>
+  friend class ClampedNumeric;
+
+  // Strictly speaking, this is not necessary, but declaring this allows class
+  // template argument deduction to be used so that it is possible to simply
+  // write `ClampedNumeric(777)` instead of `ClampedNumeric<int>(777)`.
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  constexpr ClampedNumeric(T value) : value_(value) {}
+
+  // This is not an explicit constructor because we implicitly upgrade regular
+  // numerics to ClampedNumerics to make them easier to use.
+  template <typename Src>
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  constexpr ClampedNumeric(Src value) : value_(saturated_cast<T>(value)) {
+    static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric.");
+  }
+
+  // This is not an explicit constructor because we want a seamless conversion
+  // from StrictNumeric types.
+  template <typename Src>
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  constexpr ClampedNumeric(StrictNumeric<Src> value)
+      : value_(saturated_cast<T>(static_cast<Src>(value))) {}
+
+  // Returns a ClampedNumeric of the specified type, cast from the current
+  // ClampedNumeric, and saturated to the destination type.
+  template <typename Dst>
+  constexpr ClampedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
+    return *this;
+  }
+
+  // Prototypes for the supported arithmetic operator overloads.
+  template <typename Src>
+  constexpr ClampedNumeric& operator+=(const Src rhs);
+  template <typename Src>
+  constexpr ClampedNumeric& operator-=(const Src rhs);
+  template <typename Src>
+  constexpr ClampedNumeric& operator*=(const Src rhs);
+  template <typename Src>
+  constexpr ClampedNumeric& operator/=(const Src rhs);
+  template <typename Src>
+  constexpr ClampedNumeric& operator%=(const Src rhs);
+  template <typename Src>
+  constexpr ClampedNumeric& operator<<=(const Src rhs);
+  template <typename Src>
+  constexpr ClampedNumeric& operator>>=(const Src rhs);
+  template <typename Src>
+  constexpr ClampedNumeric& operator&=(const Src rhs);
+  template <typename Src>
+  constexpr ClampedNumeric& operator|=(const Src rhs);
+  template <typename Src>
+  constexpr ClampedNumeric& operator^=(const Src rhs);
+
+  constexpr ClampedNumeric operator-() const {
+    // The negation of two's complement int min is int min, so that's the
+    // only overflow case where we will saturate.
+    return ClampedNumeric<T>(SaturatedNegWrapper(value_));
+  }
+
+  constexpr ClampedNumeric operator~() const {
+    return ClampedNumeric<decltype(InvertWrapper(T()))>(InvertWrapper(value_));
+  }
+
+  constexpr ClampedNumeric Abs() const {
+    // The negation of two's complement int min is int min, so that's the
+    // only overflow case where we will saturate.
+    return ClampedNumeric<T>(SaturatedAbsWrapper(value_));
+  }
+
+  template <typename U>
+  constexpr ClampedNumeric<typename MathWrapper<ClampedMaxOp, T, U>::type> Max(
+      const U rhs) const {
+    using result_type = typename MathWrapper<ClampedMaxOp, T, U>::type;
+    return ClampedNumeric<result_type>(
+        ClampedMaxOp<T, U>::Do(value_, Wrapper<U>::value(rhs)));
+  }
+
+  template <typename U>
+  constexpr ClampedNumeric<typename MathWrapper<ClampedMinOp, T, U>::type> Min(
+      const U rhs) const {
+    using result_type = typename MathWrapper<ClampedMinOp, T, U>::type;
+    return ClampedNumeric<result_type>(
+        ClampedMinOp<T, U>::Do(value_, Wrapper<U>::value(rhs)));
+  }
+
+  // This function is available only for integral types. It returns an unsigned
+  // integer of the same width as the source type, containing the absolute value
+  // of the source, and properly handling signed min.
+  constexpr ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>
+  UnsignedAbs() const {
+    return ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>(
+        SafeUnsignedAbs(value_));
+  }
+
+  constexpr ClampedNumeric& operator++() {
+    *this += 1;
+    return *this;
+  }
+
+  constexpr ClampedNumeric operator++(int) {
+    ClampedNumeric value = *this;
+    *this += 1;
+    return value;
+  }
+
+  constexpr ClampedNumeric& operator--() {
+    *this -= 1;
+    return *this;
+  }
+
+  constexpr ClampedNumeric operator--(int) {
+    ClampedNumeric value = *this;
+    *this -= 1;
+    return value;
+  }
+
+  // These perform the actual math operations on the ClampedNumerics.
+  // Binary arithmetic operations.
+  template <template <typename, typename, typename> class M,
+            typename L,
+            typename R>
+  static constexpr ClampedNumeric MathOp(const L lhs, const R rhs) {
+    using Math = typename MathWrapper<M, L, R>::math;
+    return ClampedNumeric<T>(
+        Math::template Do<T>(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs)));
+  }
+
+  // Assignment arithmetic operations.
+  template <template <typename, typename, typename> class M, typename R>
+  constexpr ClampedNumeric& MathOp(const R rhs) {
+    using Math = typename MathWrapper<M, T, R>::math;
+    *this =
+        ClampedNumeric<T>(Math::template Do<T>(value_, Wrapper<R>::value(rhs)));
+    return *this;
+  }
+
+  template <typename Dst>
+  constexpr operator Dst() const {
+    return saturated_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(
+        value_);
+  }
+
+  // This method extracts the raw integer value without saturating it to the
+  // destination type as the conversion operator does. This is useful when
+  // e.g. assigning to an auto type or passing as a deduced template parameter.
+  constexpr T RawValue() const { return value_; }
+
+ private:
+  T value_;
+
+  // These wrappers allow us to handle state the same way for both
+  // ClampedNumeric and POD arithmetic types.
+  template <typename Src>
+  struct Wrapper {
+    static constexpr typename UnderlyingType<Src>::type value(Src value) {
+      return value;
+    }
+  };
+};
+
+// Convenience wrapper to return a new ClampedNumeric from the provided
+// arithmetic or ClampedNumericType.
+template <typename T>
+constexpr ClampedNumeric<typename UnderlyingType<T>::type> MakeClampedNum(
+    const T value) {
+  return value;
+}
+
+// These implement the variadic wrapper for the math operations.
+template <template <typename, typename, typename> class M,
+          typename L,
+          typename R>
+constexpr ClampedNumeric<typename MathWrapper<M, L, R>::type> ClampMathOp(
+    const L lhs,
+    const R rhs) {
+  using Math = typename MathWrapper<M, L, R>::math;
+  return ClampedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
+                                                                        rhs);
+}
+
+// General purpose wrapper template for arithmetic operations.
+template <template <typename, typename, typename> class M,
+          typename L,
+          typename R,
+          typename... Args>
+constexpr auto ClampMathOp(const L lhs, const R rhs, const Args... args) {
+  return ClampMathOp<M>(ClampMathOp<M>(lhs, rhs), args...);
+}
+
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Add, +, +=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Sub, -, -=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mul, *, *=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Div, /, /=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mod, %, %=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Lsh, <<, <<=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Rsh, >>, >>=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, And, &, &=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Or, |, |=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Xor, ^, ^=)
+BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Max)
+BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Min)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLess, <)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLessOrEqual, <=)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreater, >)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreaterOrEqual, >=)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsEqual, ==)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsNotEqual, !=)
+
+}  // namespace internal
+
+using internal::ClampAdd;
+using internal::ClampAnd;
+using internal::ClampDiv;
+using internal::ClampedNumeric;
+using internal::ClampLsh;
+using internal::ClampMax;
+using internal::ClampMin;
+using internal::ClampMod;
+using internal::ClampMul;
+using internal::ClampOr;
+using internal::ClampRsh;
+using internal::ClampSub;
+using internal::ClampXor;
+using internal::MakeClampedNum;
+
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_NUMERICS_CLAMPED_MATH_H_
diff --git a/third_party/base/numerics/clamped_math_impl.h b/third_party/base/numerics/clamped_math_impl.h
new file mode 100644
index 0000000..a49309e
--- /dev/null
+++ b/third_party/base/numerics/clamped_math_impl.h
@@ -0,0 +1,342 @@
+// Copyright 2017 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_NUMERICS_CLAMPED_MATH_IMPL_H_
+#define THIRD_PARTY_BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <climits>
+#include <cmath>
+#include <cstdlib>
+#include <limits>
+#include <type_traits>
+
+#include "third_party/base/numerics/checked_math.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "third_party/base/numerics/safe_math_shared_impl.h"
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value &&
+                                  std::is_signed<T>::value>::type* = nullptr>
+constexpr T SaturatedNegWrapper(T value) {
+  return IsConstantEvaluated() || !ClampedNegFastOp<T>::is_supported
+             ? (NegateWrapper(value) != std::numeric_limits<T>::lowest()
+                    ? NegateWrapper(value)
+                    : std::numeric_limits<T>::max())
+             : ClampedNegFastOp<T>::Do(value);
+}
+
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value &&
+                                  !std::is_signed<T>::value>::type* = nullptr>
+constexpr T SaturatedNegWrapper(T value) {
+  return T(0);
+}
+
+template <
+    typename T,
+    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+constexpr T SaturatedNegWrapper(T value) {
+  return -value;
+}
+
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+constexpr T SaturatedAbsWrapper(T value) {
+  // The calculation below is a static identity for unsigned types, but for
+  // signed integer types it provides a non-branching, saturated absolute value.
+  // This works because SafeUnsignedAbs() returns an unsigned type, which can
+  // represent the absolute value of all negative numbers of an equal-width
+  // integer type. The call to IsValueNegative() then detects overflow in the
+  // special case of numeric_limits<T>::min(), by evaluating the bit pattern as
+  // a signed integer value. If it is the overflow case, we end up subtracting
+  // one from the unsigned result, thus saturating to numeric_limits<T>::max().
+  return static_cast<T>(
+      SafeUnsignedAbs(value) -
+      IsValueNegative<T>(static_cast<T>(SafeUnsignedAbs(value))));
+}
+
+template <
+    typename T,
+    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+constexpr T SaturatedAbsWrapper(T value) {
+  return value < 0 ? -value : value;
+}
+
+template <typename T, typename U, class Enable = void>
+struct ClampedAddOp {};
+
+template <typename T, typename U>
+struct ClampedAddOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = typename MaxExponentPromotion<T, U>::type;
+  template <typename V = result_type>
+  static constexpr V Do(T x, U y) {
+    if (!IsConstantEvaluated() && ClampedAddFastOp<T, U>::is_supported)
+      return ClampedAddFastOp<T, U>::template Do<V>(x, y);
+
+    static_assert(std::is_same<V, result_type>::value ||
+                      IsTypeInRangeForNumericType<U, V>::value,
+                  "The saturation result cannot be determined from the "
+                  "provided types.");
+    const V saturated = CommonMaxOrMin<V>(IsValueNegative(y));
+    V result = {};
+    return BASE_NUMERICS_LIKELY((CheckedAddOp<T, U>::Do(x, y, &result)))
+               ? result
+               : saturated;
+  }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedSubOp {};
+
+template <typename T, typename U>
+struct ClampedSubOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = typename MaxExponentPromotion<T, U>::type;
+  template <typename V = result_type>
+  static constexpr V Do(T x, U y) {
+    if (!IsConstantEvaluated() && ClampedSubFastOp<T, U>::is_supported)
+      return ClampedSubFastOp<T, U>::template Do<V>(x, y);
+
+    static_assert(std::is_same<V, result_type>::value ||
+                      IsTypeInRangeForNumericType<U, V>::value,
+                  "The saturation result cannot be determined from the "
+                  "provided types.");
+    const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y));
+    V result = {};
+    return BASE_NUMERICS_LIKELY((CheckedSubOp<T, U>::Do(x, y, &result)))
+               ? result
+               : saturated;
+  }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedMulOp {};
+
+template <typename T, typename U>
+struct ClampedMulOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = typename MaxExponentPromotion<T, U>::type;
+  template <typename V = result_type>
+  static constexpr V Do(T x, U y) {
+    if (!IsConstantEvaluated() && ClampedMulFastOp<T, U>::is_supported)
+      return ClampedMulFastOp<T, U>::template Do<V>(x, y);
+
+    V result = {};
+    const V saturated =
+        CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
+    return BASE_NUMERICS_LIKELY((CheckedMulOp<T, U>::Do(x, y, &result)))
+               ? result
+               : saturated;
+  }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedDivOp {};
+
+template <typename T, typename U>
+struct ClampedDivOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = typename MaxExponentPromotion<T, U>::type;
+  template <typename V = result_type>
+  static constexpr V Do(T x, U y) {
+    V result = {};
+    if (BASE_NUMERICS_LIKELY((CheckedDivOp<T, U>::Do(x, y, &result))))
+      return result;
+    // Saturation goes to max, min, or NaN (if x is zero).
+    return x ? CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y))
+             : SaturationDefaultLimits<V>::NaN();
+  }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedModOp {};
+
+template <typename T, typename U>
+struct ClampedModOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = typename MaxExponentPromotion<T, U>::type;
+  template <typename V = result_type>
+  static constexpr V Do(T x, U y) {
+    V result = {};
+    return BASE_NUMERICS_LIKELY((CheckedModOp<T, U>::Do(x, y, &result)))
+               ? result
+               : x;
+  }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedLshOp {};
+
+// Left shift. Non-zero values saturate in the direction of the sign. A zero
+// shifted by any value always results in zero.
+template <typename T, typename U>
+struct ClampedLshOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = T;
+  template <typename V = result_type>
+  static constexpr V Do(T x, U shift) {
+    static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
+    if (BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) {
+      // Shift as unsigned to avoid undefined behavior.
+      V result = static_cast<V>(as_unsigned(x) << shift);
+      // If the shift can be reversed, we know it was valid.
+      if (BASE_NUMERICS_LIKELY(result >> shift == x))
+        return result;
+    }
+    return x ? CommonMaxOrMin<V>(IsValueNegative(x)) : 0;
+  }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedRshOp {};
+
+// Right shift. Negative values saturate to -1. Positive or 0 saturates to 0.
+template <typename T, typename U>
+struct ClampedRshOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = T;
+  template <typename V = result_type>
+  static constexpr V Do(T x, U shift) {
+    static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
+    // Signed right shift is odd, because it saturates to -1 or 0.
+    const V saturated = as_unsigned(V(0)) - IsValueNegative(x);
+    return BASE_NUMERICS_LIKELY(shift < IntegerBitsPlusSign<T>::value)
+               ? saturated_cast<V>(x >> shift)
+               : saturated;
+  }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedAndOp {};
+
+template <typename T, typename U>
+struct ClampedAndOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = typename std::make_unsigned<
+      typename MaxExponentPromotion<T, U>::type>::type;
+  template <typename V>
+  static constexpr V Do(T x, U y) {
+    return static_cast<result_type>(x) & static_cast<result_type>(y);
+  }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedOrOp {};
+
+// For simplicity we promote to unsigned integers.
+template <typename T, typename U>
+struct ClampedOrOp<T,
+                   U,
+                   typename std::enable_if<std::is_integral<T>::value &&
+                                           std::is_integral<U>::value>::type> {
+  using result_type = typename std::make_unsigned<
+      typename MaxExponentPromotion<T, U>::type>::type;
+  template <typename V>
+  static constexpr V Do(T x, U y) {
+    return static_cast<result_type>(x) | static_cast<result_type>(y);
+  }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedXorOp {};
+
+// For simplicity we support only unsigned integers.
+template <typename T, typename U>
+struct ClampedXorOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
+  using result_type = typename std::make_unsigned<
+      typename MaxExponentPromotion<T, U>::type>::type;
+  template <typename V>
+  static constexpr V Do(T x, U y) {
+    return static_cast<result_type>(x) ^ static_cast<result_type>(y);
+  }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedMaxOp {};
+
+template <typename T, typename U>
+struct ClampedMaxOp<
+    T,
+    U,
+    typename std::enable_if<std::is_arithmetic<T>::value &&
+                            std::is_arithmetic<U>::value>::type> {
+  using result_type = typename MaxExponentPromotion<T, U>::type;
+  template <typename V = result_type>
+  static constexpr V Do(T x, U y) {
+    return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x)
+                                       : saturated_cast<V>(y);
+  }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedMinOp {};
+
+template <typename T, typename U>
+struct ClampedMinOp<
+    T,
+    U,
+    typename std::enable_if<std::is_arithmetic<T>::value &&
+                            std::is_arithmetic<U>::value>::type> {
+  using result_type = typename LowestValuePromotion<T, U>::type;
+  template <typename V = result_type>
+  static constexpr V Do(T x, U y) {
+    return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x)
+                                    : saturated_cast<V>(y);
+  }
+};
+
+// This is just boilerplate that wraps the standard floating point arithmetic.
+// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
+#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP)                              \
+  template <typename T, typename U>                                      \
+  struct Clamped##NAME##Op<                                              \
+      T, U,                                                              \
+      typename std::enable_if<std::is_floating_point<T>::value ||        \
+                              std::is_floating_point<U>::value>::type> { \
+    using result_type = typename MaxExponentPromotion<T, U>::type;       \
+    template <typename V = result_type>                                  \
+    static constexpr V Do(T x, U y) {                                    \
+      return saturated_cast<V>(x OP y);                                  \
+    }                                                                    \
+  };
+
+BASE_FLOAT_ARITHMETIC_OPS(Add, +)
+BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
+BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
+BASE_FLOAT_ARITHMETIC_OPS(Div, /)
+
+#undef BASE_FLOAT_ARITHMETIC_OPS
+
+}  // namespace internal
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
diff --git a/third_party/base/numerics/safe_conversions.h b/third_party/base/numerics/safe_conversions.h
index bb18562..77ec8ce 100644
--- a/third_party/base/numerics/safe_conversions.h
+++ b/third_party/base/numerics/safe_conversions.h
@@ -7,13 +7,13 @@
 
 #include <stddef.h>
 
+#include <cmath>
 #include <limits>
-#include <ostream>
 #include <type_traits>
 
 #include "third_party/base/numerics/safe_conversions_impl.h"
 
-#if defined(__ARMEL__) || defined(__arch64__)
+#if defined(__ARMEL__) && !defined(__native_client__)
 #include "third_party/base/numerics/safe_conversions_arm_impl.h"
 #define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (1)
 #else
@@ -27,7 +27,7 @@
 #if !BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
 template <typename Dst, typename Src>
 struct SaturateFastAsmOp {
-  static const bool is_supported = false;
+  static constexpr bool is_supported = false;
   static constexpr Dst Do(Src) {
     // Force a compile failure if instantiated.
     return CheckOnFailure::template HandleFailure<Dst>();
@@ -40,7 +40,7 @@
 // eke out better performance than range checking.
 template <typename Dst, typename Src, typename Enable = void>
 struct IsValueInRangeFastOp {
-  static const bool is_supported = false;
+  static constexpr bool is_supported = false;
   static constexpr bool Do(Src value) {
     // Force a compile failure if instantiated.
     return CheckOnFailure::template HandleFailure<bool>();
@@ -56,7 +56,7 @@
         std::is_integral<Dst>::value && std::is_integral<Src>::value &&
         std::is_signed<Dst>::value && std::is_signed<Src>::value &&
         !IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
-  static const bool is_supported = true;
+  static constexpr bool is_supported = true;
 
   static constexpr bool Do(Src value) {
     // Just downcast to the smaller type, sign extend it back to the original
@@ -74,7 +74,7 @@
         std::is_integral<Dst>::value && std::is_integral<Src>::value &&
         !std::is_signed<Dst>::value && std::is_signed<Src>::value &&
         !IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
-  static const bool is_supported = true;
+  static constexpr bool is_supported = true;
 
   static constexpr bool Do(Src value) {
     // We cast a signed as unsigned to overflow negative values to the top,
@@ -117,21 +117,27 @@
 template <typename T>
 struct SaturationDefaultLimits : public std::numeric_limits<T> {
   static constexpr T NaN() {
-    return std::numeric_limits<T>::has_quiet_NaN
-               ? std::numeric_limits<T>::quiet_NaN()
-               : T();
+    if constexpr (std::numeric_limits<T>::has_quiet_NaN) {
+      return std::numeric_limits<T>::quiet_NaN();
+    } else {
+      return T();
+    }
   }
   using std::numeric_limits<T>::max;
   static constexpr T Overflow() {
-    return std::numeric_limits<T>::has_infinity
-               ? std::numeric_limits<T>::infinity()
-               : std::numeric_limits<T>::max();
+    if constexpr (std::numeric_limits<T>::has_infinity) {
+      return std::numeric_limits<T>::infinity();
+    } else {
+      return std::numeric_limits<T>::max();
+    }
   }
   using std::numeric_limits<T>::lowest;
   static constexpr T Underflow() {
-    return std::numeric_limits<T>::has_infinity
-               ? std::numeric_limits<T>::infinity() * -1
-               : std::numeric_limits<T>::lowest();
+    if constexpr (std::numeric_limits<T>::has_infinity) {
+      return std::numeric_limits<T>::infinity() * -1;
+    } else {
+      return std::numeric_limits<T>::lowest();
+    }
   }
 };
 
@@ -153,7 +159,7 @@
 // Arm, we can use the optimized saturation instructions.
 template <typename Dst, typename Src, typename Enable = void>
 struct SaturateFastOp {
-  static const bool is_supported = false;
+  static constexpr bool is_supported = false;
   static constexpr Dst Do(Src value) {
     // Force a compile failure if instantiated.
     return CheckOnFailure::template HandleFailure<Dst>();
@@ -167,8 +173,10 @@
     typename std::enable_if<std::is_integral<Src>::value &&
                             std::is_integral<Dst>::value &&
                             SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
-  static const bool is_supported = true;
-  static Dst Do(Src value) { return SaturateFastAsmOp<Dst, Src>::Do(value); }
+  static constexpr bool is_supported = true;
+  static constexpr Dst Do(Src value) {
+    return SaturateFastAsmOp<Dst, Src>::Do(value);
+  }
 };
 
 template <typename Dst, typename Src>
@@ -178,12 +186,12 @@
     typename std::enable_if<std::is_integral<Src>::value &&
                             std::is_integral<Dst>::value &&
                             !SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
-  static const bool is_supported = true;
-  static Dst Do(Src value) {
+  static constexpr bool is_supported = true;
+  static constexpr Dst Do(Src value) {
     // The exact order of the following is structured to hit the correct
     // optimization heuristics across compilers. Do not change without
     // checking the emitted code.
-    Dst saturated = CommonMaxOrMin<Dst, Src>(
+    const Dst saturated = CommonMaxOrMin<Dst, Src>(
         IsMaxInRangeForNumericType<Dst, Src>() ||
         (!IsMinInRangeForNumericType<Dst, Src>() && IsValueNegative(value)));
     return BASE_NUMERICS_LIKELY(IsValueInRangeForNumericType<Dst>(value))
@@ -195,14 +203,13 @@
 // saturated_cast<> is analogous to static_cast<> for numeric types, except
 // that the specified numeric conversion will saturate by default rather than
 // overflow or underflow, and NaN assignment to an integral will return 0.
-// All boundary condition behaviors can be overriden with a custom handler.
+// All boundary condition behaviors can be overridden with a custom handler.
 template <typename Dst,
           template <typename> class SaturationHandler = SaturationDefaultLimits,
           typename Src>
 constexpr Dst saturated_cast(Src value) {
   using SrcType = typename UnderlyingType<Src>::type;
-  return !IsCompileTimeConstant(value) &&
-                 SaturateFastOp<Dst, SrcType>::is_supported &&
+  return !IsConstantEvaluated() && SaturateFastOp<Dst, SrcType>::is_supported &&
                  std::is_same<SaturationHandler<Dst>,
                               SaturationDefaultLimits<Dst>>::value
              ? SaturateFastOp<Dst, SrcType>::Do(static_cast<SrcType>(value))
@@ -238,7 +245,7 @@
 // Some wrappers to statically check that a type is in range.
 template <typename Dst, typename Src, class Enable = void>
 struct IsNumericRangeContained {
-  static const bool value = false;
+  static constexpr bool value = false;
 };
 
 template <typename Dst, typename Src>
@@ -247,8 +254,9 @@
     Src,
     typename std::enable_if<ArithmeticOrUnderlyingEnum<Dst>::value &&
                             ArithmeticOrUnderlyingEnum<Src>::value>::type> {
-  static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
-                            NUMERIC_RANGE_CONTAINED;
+  static constexpr bool value =
+      StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
+      NUMERIC_RANGE_CONTAINED;
 };
 
 // StrictNumeric implements compile time range checking between numeric types by
@@ -273,11 +281,17 @@
   constexpr StrictNumeric(const StrictNumeric<Src>& rhs)
       : value_(strict_cast<T>(rhs.value_)) {}
 
+  // Strictly speaking, this is not necessary, but declaring this allows class
+  // template argument deduction to be used so that it is possible to simply
+  // write `StrictNumeric(777)` instead of `StrictNumeric<int>(777)`.
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  constexpr StrictNumeric(T value) : value_(value) {}
+
   // This is not an explicit constructor because we implicitly upgrade regular
   // numerics to StrictNumerics to make them easier to use.
   template <typename Src>
-  constexpr StrictNumeric(Src value)  // NOLINT(runtime/explicit)
-      : value_(strict_cast<T>(value)) {}
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  constexpr StrictNumeric(Src value) : value_(strict_cast<T>(value)) {}
 
   // If you got here from a compiler error, it's because you tried to assign
   // from a source type to a destination type that has insufficient range.
@@ -302,20 +316,14 @@
   const T value_;
 };
 
-// Convience wrapper returns a StrictNumeric from the provided arithmetic type.
+// Convenience wrapper returns a StrictNumeric from the provided arithmetic
+// type.
 template <typename T>
 constexpr StrictNumeric<typename UnderlyingType<T>::type> MakeStrictNum(
     const T value) {
   return value;
 }
 
-// Overload the ostream output operator to make logging work nicely.
-template <typename T>
-std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {
-  os << static_cast<T>(value);
-  return os;
-}
-
 #define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP)              \
   template <typename L, typename R,                                     \
             typename std::enable_if<                                    \
@@ -337,18 +345,45 @@
 using internal::as_signed;
 using internal::as_unsigned;
 using internal::checked_cast;
-using internal::strict_cast;
-using internal::saturated_cast;
-using internal::SafeUnsignedAbs;
-using internal::StrictNumeric;
-using internal::MakeStrictNum;
-using internal::IsValueInRangeForNumericType;
 using internal::IsTypeInRangeForNumericType;
+using internal::IsValueInRangeForNumericType;
 using internal::IsValueNegative;
+using internal::MakeStrictNum;
+using internal::SafeUnsignedAbs;
+using internal::saturated_cast;
+using internal::strict_cast;
+using internal::StrictNumeric;
 
 // Explicitly make a shorter size_t alias for convenience.
 using SizeT = StrictNumeric<size_t>;
 
+// floating -> integral conversions that saturate and thus can actually return
+// an integral type.  In most cases, these should be preferred over the std::
+// versions.
+template <typename Dst = int,
+          typename Src,
+          typename = std::enable_if_t<std::is_integral<Dst>::value &&
+                                      std::is_floating_point<Src>::value>>
+Dst ClampFloor(Src value) {
+  return saturated_cast<Dst>(std::floor(value));
+}
+template <typename Dst = int,
+          typename Src,
+          typename = std::enable_if_t<std::is_integral<Dst>::value &&
+                                      std::is_floating_point<Src>::value>>
+Dst ClampCeil(Src value) {
+  return saturated_cast<Dst>(std::ceil(value));
+}
+template <typename Dst = int,
+          typename Src,
+          typename = std::enable_if_t<std::is_integral<Dst>::value &&
+                                      std::is_floating_point<Src>::value>>
+Dst ClampRound(Src value) {
+  const Src rounded =
+      (value >= 0.0f) ? std::floor(value + 0.5f) : std::ceil(value - 0.5f);
+  return saturated_cast<Dst>(rounded);
+}
+
 }  // namespace base
 }  // namespace pdfium
 
diff --git a/third_party/base/numerics/safe_conversions_arm_impl.h b/third_party/base/numerics/safe_conversions_arm_impl.h
index f5e4c4e..426118a 100644
--- a/third_party/base/numerics/safe_conversions_arm_impl.h
+++ b/third_party/base/numerics/safe_conversions_arm_impl.h
@@ -19,8 +19,8 @@
 template <typename Dst, typename Src>
 struct SaturateFastAsmOp {
   static constexpr bool is_supported =
-      std::is_signed<Src>::value && std::is_integral<Dst>::value &&
-      std::is_integral<Src>::value &&
+      kEnableAsmCode && std::is_signed<Src>::value &&
+      std::is_integral<Dst>::value && std::is_integral<Src>::value &&
       IntegerBitsPlusSign<Src>::value <= IntegerBitsPlusSign<int32_t>::value &&
       IntegerBitsPlusSign<Dst>::value <= IntegerBitsPlusSign<int32_t>::value &&
       !IsTypeInRangeForNumericType<Dst, Src>::value;
diff --git a/third_party/base/numerics/safe_conversions_impl.h b/third_party/base/numerics/safe_conversions_impl.h
index bdad911..44c921a 100644
--- a/third_party/base/numerics/safe_conversions_impl.h
+++ b/third_party/base/numerics/safe_conversions_impl.h
@@ -72,8 +72,9 @@
   static_assert(std::is_integral<T>::value, "Type must be integral");
   using SignedT = typename std::make_signed<T>::type;
   using UnsignedT = typename std::make_unsigned<T>::type;
-  return static_cast<SignedT>(
-      (static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative);
+  return static_cast<SignedT>((static_cast<UnsignedT>(x) ^
+                               static_cast<UnsignedT>(-SignedT(is_negative))) +
+                              is_negative);
 }
 
 // This performs a safe, absolute value via unsigned overflow.
@@ -81,34 +82,23 @@
 constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
   static_assert(std::is_integral<T>::value, "Type must be integral");
   using UnsignedT = typename std::make_unsigned<T>::type;
-  return IsValueNegative(value) ? 0 - static_cast<UnsignedT>(value)
-                                : static_cast<UnsignedT>(value);
+  return IsValueNegative(value)
+             ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value))
+             : static_cast<UnsignedT>(value);
 }
 
-// This allows us to switch paths on known compile-time constants.
-#if defined(__clang__) || defined(__GNUC__)
-constexpr bool CanDetectCompileTimeConstant() {
-  return true;
-}
-template <typename T>
-constexpr bool IsCompileTimeConstant(const T v) {
-  return __builtin_constant_p(v);
-}
+// TODO(jschuh): Switch to std::is_constant_evaluated() once C++20 is supported.
+// Alternately, the usage could be restructured for "consteval if" in C++23.
+#define IsConstantEvaluated() (__builtin_is_constant_evaluated())
+
+// TODO(jschuh): Debug builds don't reliably propagate constants, so we restrict
+// some accelerated runtime paths to release builds until this can be forced
+// with consteval support in C++20 or C++23.
+#if defined(NDEBUG)
+constexpr bool kEnableAsmCode = true;
 #else
-constexpr bool CanDetectCompileTimeConstant() {
-  return false;
-}
-template <typename T>
-constexpr bool IsCompileTimeConstant(const T) {
-  return false;
-}
+constexpr bool kEnableAsmCode = false;
 #endif
-template <typename T>
-constexpr bool MustTreatAsConstexpr(const T v) {
-  // Either we can't detect a compile-time constant, and must always use the
-  // constexpr path, or we know we have a compile-time constant.
-  return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v);
-}
 
 // Forces a crash, like a CHECK(false). Used for numeric boundary errors.
 // Also used in a constexpr template to trigger a compilation failure on
@@ -193,7 +183,7 @@
  public:
   constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
       : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
-  constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {}
+  constexpr RangeCheck() : is_underflow_(false), is_overflow_(false) {}
   constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
   constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
   constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
@@ -256,9 +246,10 @@
   static constexpr T Adjust(T value) {
     static_assert(std::is_same<T, Dst>::value, "");
     static_assert(kShift < DstLimits::digits, "");
-    return static_cast<T>(
-        ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)),
-                          IsValueNegative(value)));
+    using UnsignedDst = typename std::make_unsigned_t<T>;
+    return static_cast<T>(ConditionalNegate(
+        SafeUnsignedAbs(value) & ~((UnsignedDst{1} << kShift) - UnsignedDst{1}),
+        IsValueNegative(value)));
   }
 
   template <typename T,
@@ -276,7 +267,8 @@
 
 template <typename Dst,
           typename Src,
-          template <typename> class Bounds,
+          template <typename>
+          class Bounds,
           IntegerRepresentation DstSign = std::is_signed<Dst>::value
                                               ? INTEGER_REPRESENTATION_SIGNED
                                               : INTEGER_REPRESENTATION_UNSIGNED,
@@ -294,7 +286,8 @@
 // Same sign narrowing: The range is contained for normal limits.
 template <typename Dst,
           typename Src,
-          template <typename> class Bounds,
+          template <typename>
+          class Bounds,
           IntegerRepresentation DstSign,
           IntegerRepresentation SrcSign>
 struct DstRangeRelationToSrcRangeImpl<Dst,
@@ -378,9 +371,17 @@
     using SrcLimits = std::numeric_limits<Src>;
     using DstLimits = NarrowingRange<Dst, Src, Bounds>;
     using Promotion = decltype(Src() + Dst());
+    bool ge_zero = false;
+    // Converting floating-point to integer will discard fractional part, so
+    // values in (-1.0, -0.0) will truncate to 0 and fit in Dst.
+    if (std::is_floating_point<Src>::value) {
+      ge_zero = value > Src(-1);
+    } else {
+      ge_zero = value >= Src(0);
+    }
     return RangeCheck(
-        value >= Src(0) && (DstLimits::lowest() == 0 ||
-                            static_cast<Dst>(value) >= DstLimits::lowest()),
+        ge_zero && (DstLimits::lowest() == 0 ||
+                    static_cast<Dst>(value) >= DstLimits::lowest()),
         static_cast<Promotion>(SrcLimits::max()) <=
                 static_cast<Promotion>(DstLimits::max()) ||
             static_cast<Promotion>(value) <=
@@ -692,9 +693,8 @@
                           const RangeCheck l_range,
                           const RangeCheck r_range) {
   return l_range.IsUnderflow() || r_range.IsOverflow() ||
-         (l_range == r_range &&
-          static_cast<decltype(lhs + rhs)>(lhs) <
-              static_cast<decltype(lhs + rhs)>(rhs));
+         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <
+                                    static_cast<decltype(lhs + rhs)>(rhs));
 }
 
 template <typename L, typename R>
@@ -713,9 +713,8 @@
                                  const RangeCheck l_range,
                                  const RangeCheck r_range) {
   return l_range.IsUnderflow() || r_range.IsOverflow() ||
-         (l_range == r_range &&
-          static_cast<decltype(lhs + rhs)>(lhs) <=
-              static_cast<decltype(lhs + rhs)>(rhs));
+         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <=
+                                    static_cast<decltype(lhs + rhs)>(rhs));
 }
 
 template <typename L, typename R>
@@ -734,9 +733,8 @@
                              const RangeCheck l_range,
                              const RangeCheck r_range) {
   return l_range.IsOverflow() || r_range.IsUnderflow() ||
-         (l_range == r_range &&
-          static_cast<decltype(lhs + rhs)>(lhs) >
-              static_cast<decltype(lhs + rhs)>(rhs));
+         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >
+                                    static_cast<decltype(lhs + rhs)>(rhs));
 }
 
 template <typename L, typename R>
@@ -755,9 +753,8 @@
                                     const RangeCheck l_range,
                                     const RangeCheck r_range) {
   return l_range.IsOverflow() || r_range.IsUnderflow() ||
-         (l_range == r_range &&
-          static_cast<decltype(lhs + rhs)>(lhs) >=
-              static_cast<decltype(lhs + rhs)>(rhs));
+         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >=
+                                    static_cast<decltype(lhs + rhs)>(rhs));
 }
 
 template <typename L, typename R>
diff --git a/third_party/base/numerics/safe_math.h b/third_party/base/numerics/safe_math.h
index 3007cd7..0ecc15a 100644
--- a/third_party/base/numerics/safe_math.h
+++ b/third_party/base/numerics/safe_math.h
@@ -1,510 +1,12 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2017 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_NUMERICS_SAFE_MATH_H_
 #define THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_H_
 
-#include <stddef.h>
-
-#include <limits>
-#include <type_traits>
-
-#include "third_party/base/numerics/safe_math_impl.h"
-
-namespace pdfium {
-namespace base {
-namespace internal {
-
-// CheckedNumeric<> implements all the logic and operators for detecting integer
-// boundary conditions such as overflow, underflow, and invalid conversions.
-// The CheckedNumeric type implicitly converts from floating point and integer
-// data types, and contains overloads for basic arithmetic operations (i.e.: +,
-// -, *, / for all types and %, <<, >>, &, |, ^ for integers). Type promotions
-// are a slightly modified version of the standard C arithmetic rules with the
-// two differences being that there is no default promotion to int and bitwise
-// logical operations always return an unsigned of the wider type.
-//
-// You may also use one of the variadic convenience functions, which accept
-// standard arithmetic or CheckedNumeric types, perform arithmetic operations,
-// and return a CheckedNumeric result. The supported functions are:
-//  CheckAdd() - Addition.
-//  CheckSub() - Subtraction.
-//  CheckMul() - Multiplication.
-//  CheckDiv() - Division.
-//  CheckMod() - Modulous (integer only).
-//  CheckLsh() - Left integer shift (integer only).
-//  CheckRsh() - Right integer shift (integer only).
-//  CheckAnd() - Bitwise AND (integer only with unsigned result).
-//  CheckOr()  - Bitwise OR (integer only with unsigned result).
-//  CheckXor() - Bitwise XOR (integer only with unsigned result).
-//  CheckMax() - Maximum of supplied arguments.
-//  CheckMin() - Minimum of supplied arguments.
-//
-// The unary negation, increment, and decrement operators are supported, along
-// with the following unary arithmetic methods, which return a new
-// CheckedNumeric as a result of the operation:
-//  Abs() - Absolute value.
-//  UnsignedAbs() - Absolute value as an equal-width unsigned underlying type
-//          (valid for only integral types).
-//  Max() - Returns whichever is greater of the current instance or argument.
-//          The underlying return type is whichever has the greatest magnitude.
-//  Min() - Returns whichever is lowest of the current instance or argument.
-//          The underlying return type is whichever has can represent the lowest
-//          number in the smallest width (e.g. int8_t over unsigned, int over
-//          int8_t, and float over int).
-//
-// The following methods convert from CheckedNumeric to standard numeric values:
-//  AssignIfValid() - Assigns the underlying value to the supplied destination
-//          pointer if the value is currently valid and within the range
-//          supported by the destination type. Returns true on success.
-//  ****************************************************************************
-//  *  WARNING: All of the following functions return a StrictNumeric, which   *
-//  *  is valid for comparison and assignment operations, but will trigger a   *
-//  *  compile failure on attempts to assign to a type of insufficient range.  *
-//  ****************************************************************************
-//  IsValid() - Returns true if the underlying numeric value is valid (i.e. has
-//          has not wrapped and is not the result of an invalid conversion).
-//  ValueOrDie() - Returns the underlying value. If the state is not valid this
-//          call will crash on a CHECK.
-//  ValueOrDefault() - Returns the current value, or the supplied default if the
-//          state is not valid (will not trigger a CHECK).
-//
-// The following wrapper functions can be used to avoid the template
-// disambiguator syntax when converting a destination type.
-//   IsValidForType<>() in place of: a.template IsValid<Dst>()
-//   ValueOrDieForType<>() in place of: a.template ValueOrDie()
-//   ValueOrDefaultForType<>() in place of: a.template ValueOrDefault(default)
-//
-// The following are general utility methods that are useful for converting
-// between arithmetic types and CheckedNumeric types:
-//  CheckedNumeric::Cast<Dst>() - Instance method returning a CheckedNumeric
-//          derived from casting the current instance to a CheckedNumeric of
-//          the supplied destination type.
-//  MakeCheckedNum() - Creates a new CheckedNumeric from the underlying type of
-//          the supplied arithmetic, CheckedNumeric, or StrictNumeric type.
-//
-// Comparison operations are explicitly not supported because they could result
-// in a crash on an unexpected CHECK condition. You should use patterns like the
-// following for comparisons:
-//   CheckedNumeric<size_t> checked_size = untrusted_input_value;
-//   checked_size += HEADER LENGTH;
-//   if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size)
-//     Do stuff...
-
-template <typename T>
-class CheckedNumeric {
-  static_assert(std::is_arithmetic<T>::value,
-                "CheckedNumeric<T>: T must be a numeric type.");
-
- public:
-  using type = T;
-
-  constexpr CheckedNumeric() = default;
-
-  // Copy constructor.
-  template <typename Src>
-  constexpr CheckedNumeric(const CheckedNumeric<Src>& rhs)
-      : state_(rhs.state_.value(), rhs.IsValid()) {}
-
-  template <typename Src>
-  friend class CheckedNumeric;
-
-  // This is not an explicit constructor because we implicitly upgrade regular
-  // numerics to CheckedNumerics to make them easier to use.
-  template <typename Src>
-  constexpr CheckedNumeric(Src value)  // NOLINT(runtime/explicit)
-      : state_(value) {
-    static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
-  }
-
-  // This is not an explicit constructor because we want a seamless conversion
-  // from StrictNumeric types.
-  template <typename Src>
-  constexpr CheckedNumeric(
-      StrictNumeric<Src> value)  // NOLINT(runtime/explicit)
-      : state_(static_cast<Src>(value)) {}
-
-  // IsValid() - The public API to test if a CheckedNumeric is currently valid.
-  // A range checked destination type can be supplied using the Dst template
-  // parameter.
-  template <typename Dst = T>
-  constexpr bool IsValid() const {
-    return state_.is_valid() &&
-           IsValueInRangeForNumericType<Dst>(state_.value());
-  }
-
-  // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid
-  // and is within the range supported by the destination type. Returns true if
-  // successful and false otherwise.
-  template <typename Dst>
-  constexpr bool AssignIfValid(Dst* result) const {
-    return IsValid<Dst>() ? ((*result = static_cast<Dst>(state_.value())), true)
-                          : false;
-  }
-
-  // ValueOrDie() - The primary accessor for the underlying value. If the
-  // current state is not valid it will CHECK and crash.
-  // A range checked destination type can be supplied using the Dst template
-  // parameter, which will trigger a CHECK if the value is not in bounds for
-  // the destination.
-  // The CHECK behavior can be overridden by supplying a handler as a
-  // template parameter, for test code, etc. However, the handler cannot access
-  // the underlying value, and it is not available through other means.
-  template <typename Dst = T, class CheckHandler = CheckOnFailure>
-  constexpr StrictNumeric<Dst> ValueOrDie() const {
-    return IsValid<Dst>() ? static_cast<Dst>(state_.value())
-                          : CheckHandler::template HandleFailure<Dst>();
-  }
-
-  // ValueOrDefault(T default_value) - A convenience method that returns the
-  // current value if the state is valid, and the supplied default_value for
-  // any other state.
-  // A range checked destination type can be supplied using the Dst template
-  // parameter. WARNING: This function may fail to compile or CHECK at runtime
-  // if the supplied default_value is not within range of the destination type.
-  template <typename Dst = T, typename Src>
-  constexpr StrictNumeric<Dst> ValueOrDefault(const Src default_value) const {
-    return IsValid<Dst>() ? static_cast<Dst>(state_.value())
-                          : checked_cast<Dst>(default_value);
-  }
-
-  // Returns a checked numeric of the specified type, cast from the current
-  // CheckedNumeric. If the current state is invalid or the destination cannot
-  // represent the result then the returned CheckedNumeric will be invalid.
-  template <typename Dst>
-  constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
-    return *this;
-  }
-
-  // This friend method is available solely for providing more detailed logging
-  // in the tests. Do not implement it in production code, because the
-  // underlying values may change at any time.
-  template <typename U>
-  friend U GetNumericValueForTest(const CheckedNumeric<U>& src);
-
-  // Prototypes for the supported arithmetic operator overloads.
-  template <typename Src>
-  CheckedNumeric& operator+=(const Src rhs);
-  template <typename Src>
-  CheckedNumeric& operator-=(const Src rhs);
-  template <typename Src>
-  CheckedNumeric& operator*=(const Src rhs);
-  template <typename Src>
-  CheckedNumeric& operator/=(const Src rhs);
-  template <typename Src>
-  CheckedNumeric& operator%=(const Src rhs);
-  template <typename Src>
-  CheckedNumeric& operator<<=(const Src rhs);
-  template <typename Src>
-  CheckedNumeric& operator>>=(const Src rhs);
-  template <typename Src>
-  CheckedNumeric& operator&=(const Src rhs);
-  template <typename Src>
-  CheckedNumeric& operator|=(const Src rhs);
-  template <typename Src>
-  CheckedNumeric& operator^=(const Src rhs);
-
-  constexpr CheckedNumeric operator-() const {
-    return CheckedNumeric<T>(
-        NegateWrapper(state_.value()),
-        IsValid() &&
-            (!std::is_signed<T>::value || std::is_floating_point<T>::value ||
-             NegateWrapper(state_.value()) !=
-                 std::numeric_limits<T>::lowest()));
-  }
-
-  constexpr CheckedNumeric operator~() const {
-    return CheckedNumeric<decltype(InvertWrapper(T()))>(
-        InvertWrapper(state_.value()), IsValid());
-  }
-
-  constexpr CheckedNumeric Abs() const {
-    return CheckedNumeric<T>(
-        AbsWrapper(state_.value()),
-        IsValid() &&
-            (!std::is_signed<T>::value || std::is_floating_point<T>::value ||
-             AbsWrapper(state_.value()) != std::numeric_limits<T>::lowest()));
-  }
-
-  template <typename U>
-  constexpr CheckedNumeric<typename MathWrapper<CheckedMaxOp, T, U>::type> Max(
-      const U rhs) const {
-    using R = typename UnderlyingType<U>::type;
-    using result_type = typename MathWrapper<CheckedMaxOp, T, U>::type;
-    // TODO(jschuh): This can be converted to the MathOp version and remain
-    // constexpr once we have C++14 support.
-    return CheckedNumeric<result_type>(
-        static_cast<result_type>(
-            IsGreater<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
-                ? state_.value()
-                : Wrapper<U>::value(rhs)),
-        state_.is_valid() && Wrapper<U>::is_valid(rhs));
-  }
-
-  template <typename U>
-  constexpr CheckedNumeric<typename MathWrapper<CheckedMinOp, T, U>::type> Min(
-      const U rhs) const {
-    using R = typename UnderlyingType<U>::type;
-    using result_type = typename MathWrapper<CheckedMinOp, T, U>::type;
-    // TODO(jschuh): This can be converted to the MathOp version and remain
-    // constexpr once we have C++14 support.
-    return CheckedNumeric<result_type>(
-        static_cast<result_type>(
-            IsLess<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
-                ? state_.value()
-                : Wrapper<U>::value(rhs)),
-        state_.is_valid() && Wrapper<U>::is_valid(rhs));
-  }
-
-  // This function is available only for integral types. It returns an unsigned
-  // integer of the same width as the source type, containing the absolute value
-  // of the source, and properly handling signed min.
-  constexpr CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>
-  UnsignedAbs() const {
-    return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>(
-        SafeUnsignedAbs(state_.value()), state_.is_valid());
-  }
-
-  CheckedNumeric& operator++() {
-    *this += 1;
-    return *this;
-  }
-
-  CheckedNumeric operator++(int) {
-    CheckedNumeric value = *this;
-    *this += 1;
-    return value;
-  }
-
-  CheckedNumeric& operator--() {
-    *this -= 1;
-    return *this;
-  }
-
-  CheckedNumeric operator--(int) {
-    CheckedNumeric value = *this;
-    *this -= 1;
-    return value;
-  }
-
-  // These perform the actual math operations on the CheckedNumerics.
-  // Binary arithmetic operations.
-  template <template <typename, typename, typename> class M,
-            typename L,
-            typename R>
-  static CheckedNumeric MathOp(const L lhs, const R rhs) {
-    using Math = typename MathWrapper<M, L, R>::math;
-    T result = 0;
-    bool is_valid =
-        Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) &&
-        Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result);
-    return CheckedNumeric<T>(result, is_valid);
-  }
-
-  // Assignment arithmetic operations.
-  template <template <typename, typename, typename> class M, typename R>
-  CheckedNumeric& MathOp(const R rhs) {
-    using Math = typename MathWrapper<M, T, R>::math;
-    T result = 0;  // Using T as the destination saves a range check.
-    bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) &&
-                    Math::Do(state_.value(), Wrapper<R>::value(rhs), &result);
-    *this = CheckedNumeric<T>(result, is_valid);
-    return *this;
-  }
-
- private:
-  CheckedNumericState<T> state_;
-
-  template <typename Src>
-  constexpr CheckedNumeric(Src value, bool is_valid)
-      : state_(value, is_valid) {}
-
-  // These wrappers allow us to handle state the same way for both
-  // CheckedNumeric and POD arithmetic types.
-  template <typename Src>
-  struct Wrapper {
-    static constexpr bool is_valid(Src) { return true; }
-    static constexpr Src value(Src value) { return value; }
-  };
-
-  template <typename Src>
-  struct Wrapper<CheckedNumeric<Src>> {
-    static constexpr bool is_valid(const CheckedNumeric<Src> v) {
-      return v.IsValid();
-    }
-    static constexpr Src value(const CheckedNumeric<Src> v) {
-      return v.state_.value();
-    }
-  };
-
-  template <typename Src>
-  struct Wrapper<StrictNumeric<Src>> {
-    static constexpr bool is_valid(const StrictNumeric<Src>) { return true; }
-    static constexpr Src value(const StrictNumeric<Src> v) {
-      return static_cast<Src>(v);
-    }
-  };
-};
-
-// Convenience functions to avoid the ugly template disambiguator syntax.
-template <typename Dst, typename Src>
-constexpr bool IsValidForType(const CheckedNumeric<Src> value) {
-  return value.template IsValid<Dst>();
-}
-
-template <typename Dst, typename Src>
-constexpr StrictNumeric<Dst> ValueOrDieForType(
-    const CheckedNumeric<Src> value) {
-  return value.template ValueOrDie<Dst>();
-}
-
-template <typename Dst, typename Src, typename Default>
-constexpr StrictNumeric<Dst> ValueOrDefaultForType(
-    const CheckedNumeric<Src> value,
-    const Default default_value) {
-  return value.template ValueOrDefault<Dst>(default_value);
-}
-
-// These variadic templates work out the return types.
-// TODO(jschuh): Rip all this out once we have C++14 non-trailing auto support.
-template <template <typename, typename, typename> class M,
-          typename L,
-          typename R,
-          typename... Args>
-struct ResultType;
-
-template <template <typename, typename, typename> class M,
-          typename L,
-          typename R>
-struct ResultType<M, L, R> {
-  using type = typename MathWrapper<M, L, R>::type;
-};
-
-template <template <typename, typename, typename> class M,
-          typename L,
-          typename R,
-          typename... Args>
-struct ResultType {
-  using type =
-      typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type;
-};
-
-// Convience wrapper to return a new CheckedNumeric from the provided arithmetic
-// or CheckedNumericType.
-template <typename T>
-constexpr CheckedNumeric<typename UnderlyingType<T>::type> MakeCheckedNum(
-    const T value) {
-  return value;
-}
-
-// These implement the variadic wrapper for the math operations.
-template <template <typename, typename, typename> class M,
-          typename L,
-          typename R>
-CheckedNumeric<typename MathWrapper<M, L, R>::type> ChkMathOp(const L lhs,
-                                                              const R rhs) {
-  using Math = typename MathWrapper<M, L, R>::math;
-  return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
-                                                                        rhs);
-}
-
-// General purpose wrapper template for arithmetic operations.
-template <template <typename, typename, typename> class M,
-          typename L,
-          typename R,
-          typename... Args>
-CheckedNumeric<typename ResultType<M, L, R, Args...>::type>
-ChkMathOp(const L lhs, const R rhs, const Args... args) {
-  auto tmp = ChkMathOp<M>(lhs, rhs);
-  return tmp.IsValid() ? ChkMathOp<M>(tmp, args...)
-                       : decltype(ChkMathOp<M>(tmp, args...))(tmp);
-}
-
-// The following macros are just boilerplate for the standard arithmetic
-// operator overloads and variadic function templates. A macro isn't the nicest
-// solution, but it beats rewriting these over and over again.
-#define BASE_NUMERIC_ARITHMETIC_VARIADIC(NAME)                                \
-  template <typename L, typename R, typename... Args>                         \
-  CheckedNumeric<typename ResultType<Checked##NAME##Op, L, R, Args...>::type> \
-      Check##NAME(const L lhs, const R rhs, const Args... args) {             \
-    return ChkMathOp<Checked##NAME##Op, L, R, Args...>(lhs, rhs, args...);    \
-  }
-
-#define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP)               \
-  /* Binary arithmetic operator for all CheckedNumeric operations. */          \
-  template <typename L, typename R,                                            \
-            typename std::enable_if<IsCheckedOp<L, R>::value>::type* =         \
-                nullptr>                                                       \
-  CheckedNumeric<typename MathWrapper<Checked##NAME##Op, L, R>::type>          \
-  operator OP(const L lhs, const R rhs) {                                      \
-    return decltype(lhs OP rhs)::template MathOp<Checked##NAME##Op>(lhs, rhs); \
-  }                                                                            \
-  /* Assignment arithmetic operator implementation from CheckedNumeric. */     \
-  template <typename L>                                                        \
-  template <typename R>                                                        \
-  CheckedNumeric<L>& CheckedNumeric<L>::operator COMPOUND_OP(const R rhs) {    \
-    return MathOp<Checked##NAME##Op>(rhs);                                     \
-  }                                                                            \
-  /* Variadic arithmetic functions that return CheckedNumeric. */              \
-  BASE_NUMERIC_ARITHMETIC_VARIADIC(NAME)
-
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Lsh, <<, <<=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Rsh, >>, >>=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(And, &, &=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Or, |, |=)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Xor, ^, ^=)
-BASE_NUMERIC_ARITHMETIC_VARIADIC(Max)
-BASE_NUMERIC_ARITHMETIC_VARIADIC(Min)
-
-#undef BASE_NUMERIC_ARITHMETIC_VARIADIC
-#undef BASE_NUMERIC_ARITHMETIC_OPERATORS
-
-// These are some extra StrictNumeric operators to support simple pointer
-// arithmetic with our result types. Since wrapping on a pointer is always
-// bad, we trigger the CHECK condition here.
-template <typename L, typename R>
-L* operator+(L* lhs, const StrictNumeric<R> rhs) {
-  uintptr_t result = CheckAdd(reinterpret_cast<uintptr_t>(lhs),
-                              CheckMul(sizeof(L), static_cast<R>(rhs)))
-                         .template ValueOrDie<uintptr_t>();
-  return reinterpret_cast<L*>(result);
-}
-
-template <typename L, typename R>
-L* operator-(L* lhs, const StrictNumeric<R> rhs) {
-  uintptr_t result = CheckSub(reinterpret_cast<uintptr_t>(lhs),
-                              CheckMul(sizeof(L), static_cast<R>(rhs)))
-                         .template ValueOrDie<uintptr_t>();
-  return reinterpret_cast<L*>(result);
-}
-
-}  // namespace internal
-
-using internal::CheckedNumeric;
-using internal::IsValidForType;
-using internal::ValueOrDieForType;
-using internal::ValueOrDefaultForType;
-using internal::MakeCheckedNum;
-using internal::CheckMax;
-using internal::CheckMin;
-using internal::CheckAdd;
-using internal::CheckSub;
-using internal::CheckMul;
-using internal::CheckDiv;
-using internal::CheckMod;
-using internal::CheckLsh;
-using internal::CheckRsh;
-using internal::CheckAnd;
-using internal::CheckOr;
-using internal::CheckXor;
-
-}  // namespace base
-}  // namespace pdfium
+#include "third_party/base/numerics/checked_math.h"
+#include "third_party/base/numerics/clamped_math.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 #endif  // THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_H_
diff --git a/third_party/base/numerics/safe_math_arm_impl.h b/third_party/base/numerics/safe_math_arm_impl.h
new file mode 100644
index 0000000..ff97333
--- /dev/null
+++ b/third_party/base/numerics/safe_math_arm_impl.h
@@ -0,0 +1,127 @@
+// Copyright 2017 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_NUMERICS_SAFE_MATH_ARM_IMPL_H_
+#define THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_
+
+#include <cassert>
+#include <type_traits>
+
+#include "third_party/base/numerics/safe_conversions.h"
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+template <typename T, typename U>
+struct CheckedMulFastAsmOp {
+  static const bool is_supported =
+      kEnableAsmCode && FastIntegerArithmeticPromotion<T, U>::is_contained;
+
+  // The following is not an assembler routine and is thus constexpr safe, it
+  // just emits much more efficient code than the Clang and GCC builtins for
+  // performing overflow-checked multiplication when a twice wider type is
+  // available. The below compiles down to 2-3 instructions, depending on the
+  // width of the types in use.
+  // As an example, an int32_t multiply compiles to:
+  //    smull   r0, r1, r0, r1
+  //    cmp     r1, r1, asr #31
+  // And an int16_t multiply compiles to:
+  //    smulbb  r1, r1, r0
+  //    asr     r2, r1, #16
+  //    cmp     r2, r1, asr #15
+  template <typename V>
+  static constexpr bool Do(T x, U y, V* result) {
+    using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+    Promotion presult;
+
+    presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
+    if (!IsValueInRangeForNumericType<V>(presult))
+      return false;
+    *result = static_cast<V>(presult);
+    return true;
+  }
+};
+
+template <typename T, typename U>
+struct ClampedAddFastAsmOp {
+  static const bool is_supported =
+      kEnableAsmCode && BigEnoughPromotion<T, U>::is_contained &&
+      IsTypeInRangeForNumericType<
+          int32_t,
+          typename BigEnoughPromotion<T, U>::type>::value;
+
+  template <typename V>
+  __attribute__((always_inline)) static V Do(T x, U y) {
+    // This will get promoted to an int, so let the compiler do whatever is
+    // clever and rely on the saturated cast to bounds check.
+    if (IsIntegerArithmeticSafe<int, T, U>::value)
+      return saturated_cast<V>(static_cast<int>(x) + static_cast<int>(y));
+
+    int32_t result;
+    int32_t x_i32 = checked_cast<int32_t>(x);
+    int32_t y_i32 = checked_cast<int32_t>(y);
+
+    asm("qadd %[result], %[first], %[second]"
+        : [result] "=r"(result)
+        : [first] "r"(x_i32), [second] "r"(y_i32));
+    return saturated_cast<V>(result);
+  }
+};
+
+template <typename T, typename U>
+struct ClampedSubFastAsmOp {
+  static const bool is_supported =
+      kEnableAsmCode && BigEnoughPromotion<T, U>::is_contained &&
+      IsTypeInRangeForNumericType<
+          int32_t,
+          typename BigEnoughPromotion<T, U>::type>::value;
+
+  template <typename V>
+  __attribute__((always_inline)) static V Do(T x, U y) {
+    // This will get promoted to an int, so let the compiler do whatever is
+    // clever and rely on the saturated cast to bounds check.
+    if (IsIntegerArithmeticSafe<int, T, U>::value)
+      return saturated_cast<V>(static_cast<int>(x) - static_cast<int>(y));
+
+    int32_t result;
+    int32_t x_i32 = checked_cast<int32_t>(x);
+    int32_t y_i32 = checked_cast<int32_t>(y);
+
+    asm("qsub %[result], %[first], %[second]"
+        : [result] "=r"(result)
+        : [first] "r"(x_i32), [second] "r"(y_i32));
+    return saturated_cast<V>(result);
+  }
+};
+
+template <typename T, typename U>
+struct ClampedMulFastAsmOp {
+  static const bool is_supported =
+      kEnableAsmCode && CheckedMulFastAsmOp<T, U>::is_supported;
+
+  template <typename V>
+  __attribute__((always_inline)) static V Do(T x, U y) {
+    // Use the CheckedMulFastAsmOp for full-width 32-bit values, because
+    // it's fewer instructions than promoting and then saturating.
+    if (!IsIntegerArithmeticSafe<int32_t, T, U>::value &&
+        !IsIntegerArithmeticSafe<uint32_t, T, U>::value) {
+      V result;
+      return CheckedMulFastAsmOp<T, U>::Do(x, y, &result)
+                 ? result
+                 : CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
+    }
+
+    assert((FastIntegerArithmeticPromotion<T, U>::is_contained));
+    using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+    return saturated_cast<V>(static_cast<Promotion>(x) *
+                             static_cast<Promotion>(y));
+  }
+};
+
+}  // namespace internal
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_
diff --git a/third_party/base/numerics/safe_math_clang_gcc_impl.h b/third_party/base/numerics/safe_math_clang_gcc_impl.h
new file mode 100644
index 0000000..256d103
--- /dev/null
+++ b/third_party/base/numerics/safe_math_clang_gcc_impl.h
@@ -0,0 +1,159 @@
+// Copyright 2017 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_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
+#define THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
+
+#include <cassert>
+#include <limits>
+#include <type_traits>
+
+#include "third_party/base/numerics/safe_conversions.h"
+
+#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__))
+#include "third_party/base/numerics/safe_math_arm_impl.h"
+#define BASE_HAS_ASSEMBLER_SAFE_MATH (1)
+#else
+#define BASE_HAS_ASSEMBLER_SAFE_MATH (0)
+#endif
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+// These are the non-functioning boilerplate implementations of the optimized
+// safe math routines.
+#if !BASE_HAS_ASSEMBLER_SAFE_MATH
+template <typename T, typename U>
+struct CheckedMulFastAsmOp {
+  static const bool is_supported = false;
+  template <typename V>
+  static constexpr bool Do(T, U, V*) {
+    // Force a compile failure if instantiated.
+    return CheckOnFailure::template HandleFailure<bool>();
+  }
+};
+
+template <typename T, typename U>
+struct ClampedAddFastAsmOp {
+  static const bool is_supported = false;
+  template <typename V>
+  static constexpr V Do(T, U) {
+    // Force a compile failure if instantiated.
+    return CheckOnFailure::template HandleFailure<V>();
+  }
+};
+
+template <typename T, typename U>
+struct ClampedSubFastAsmOp {
+  static const bool is_supported = false;
+  template <typename V>
+  static constexpr V Do(T, U) {
+    // Force a compile failure if instantiated.
+    return CheckOnFailure::template HandleFailure<V>();
+  }
+};
+
+template <typename T, typename U>
+struct ClampedMulFastAsmOp {
+  static const bool is_supported = false;
+  template <typename V>
+  static constexpr V Do(T, U) {
+    // Force a compile failure if instantiated.
+    return CheckOnFailure::template HandleFailure<V>();
+  }
+};
+#endif  // BASE_HAS_ASSEMBLER_SAFE_MATH
+#undef BASE_HAS_ASSEMBLER_SAFE_MATH
+
+template <typename T, typename U>
+struct CheckedAddFastOp {
+  static const bool is_supported = true;
+  template <typename V>
+  __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
+    return !__builtin_add_overflow(x, y, result);
+  }
+};
+
+template <typename T, typename U>
+struct CheckedSubFastOp {
+  static const bool is_supported = true;
+  template <typename V>
+  __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
+    return !__builtin_sub_overflow(x, y, result);
+  }
+};
+
+template <typename T, typename U>
+struct CheckedMulFastOp {
+#if defined(__clang__)
+  // TODO(jschuh): Get the Clang runtime library issues sorted out so we can
+  // support full-width, mixed-sign multiply builtins.
+  // https://crbug.com/613003
+  // We can support intptr_t, uintptr_t, or a smaller common type.
+  static const bool is_supported =
+      (IsTypeInRangeForNumericType<intptr_t, T>::value &&
+       IsTypeInRangeForNumericType<intptr_t, U>::value) ||
+      (IsTypeInRangeForNumericType<uintptr_t, T>::value &&
+       IsTypeInRangeForNumericType<uintptr_t, U>::value);
+#else
+  static const bool is_supported = true;
+#endif
+  template <typename V>
+  __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
+    return CheckedMulFastAsmOp<T, U>::is_supported
+               ? CheckedMulFastAsmOp<T, U>::Do(x, y, result)
+               : !__builtin_mul_overflow(x, y, result);
+  }
+};
+
+template <typename T, typename U>
+struct ClampedAddFastOp {
+  static const bool is_supported = ClampedAddFastAsmOp<T, U>::is_supported;
+  template <typename V>
+  __attribute__((always_inline)) static V Do(T x, U y) {
+    return ClampedAddFastAsmOp<T, U>::template Do<V>(x, y);
+  }
+};
+
+template <typename T, typename U>
+struct ClampedSubFastOp {
+  static const bool is_supported = ClampedSubFastAsmOp<T, U>::is_supported;
+  template <typename V>
+  __attribute__((always_inline)) static V Do(T x, U y) {
+    return ClampedSubFastAsmOp<T, U>::template Do<V>(x, y);
+  }
+};
+
+template <typename T, typename U>
+struct ClampedMulFastOp {
+  static const bool is_supported = ClampedMulFastAsmOp<T, U>::is_supported;
+  template <typename V>
+  __attribute__((always_inline)) static V Do(T x, U y) {
+    return ClampedMulFastAsmOp<T, U>::template Do<V>(x, y);
+  }
+};
+
+template <typename T>
+struct ClampedNegFastOp {
+  static const bool is_supported = std::is_signed<T>::value;
+  __attribute__((always_inline)) static T Do(T value) {
+    // Use this when there is no assembler path available.
+    if (!ClampedSubFastAsmOp<T, T>::is_supported) {
+      T result;
+      return !__builtin_sub_overflow(T(0), value, &result)
+                 ? result
+                 : std::numeric_limits<T>::max();
+    }
+
+    // Fallback to the normal subtraction path.
+    return ClampedSubFastOp<T, T>::template Do<T>(T(0), value);
+  }
+};
+
+}  // namespace internal
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
diff --git a/third_party/base/numerics/safe_math_impl.h b/third_party/base/numerics/safe_math_impl.h
deleted file mode 100644
index d91dd2b..0000000
--- a/third_party/base/numerics/safe_math_impl.h
+++ /dev/null
@@ -1,647 +0,0 @@
-// Copyright 2014 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_NUMERICS_SAFE_MATH_IMPL_H_
-#define THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_IMPL_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <climits>
-#include <cmath>
-#include <cstdlib>
-#include <limits>
-#include <type_traits>
-
-#include "third_party/base/numerics/safe_conversions.h"
-
-namespace pdfium {
-namespace base {
-namespace internal {
-
-// Everything from here up to the floating point operations is portable C++,
-// but it may not be fast. This code could be split based on
-// platform/architecture and replaced with potentially faster implementations.
-
-// This is used for UnsignedAbs, where we need to support floating-point
-// template instantiations even though we don't actually support the operations.
-// However, there is no corresponding implementation of e.g. SafeUnsignedAbs,
-// so the float versions will not compile.
-template <typename Numeric,
-          bool IsInteger = std::is_integral<Numeric>::value,
-          bool IsFloat = std::is_floating_point<Numeric>::value>
-struct UnsignedOrFloatForSize;
-
-template <typename Numeric>
-struct UnsignedOrFloatForSize<Numeric, true, false> {
-  using type = typename std::make_unsigned<Numeric>::type;
-};
-
-template <typename Numeric>
-struct UnsignedOrFloatForSize<Numeric, false, true> {
-  using type = Numeric;
-};
-
-// Probe for builtin math overflow support on Clang and version check on GCC.
-#if defined(EMSCRIPTEN)
-// Emscripten Clang reports that it has the builtins, it may be lowered to an
-// instruction that is unsupported in asm.js
-#define USE_OVERFLOW_BUILTINS (0)
-#elif defined(__has_builtin)
-#define USE_OVERFLOW_BUILTINS (__has_builtin(__builtin_add_overflow))
-#elif defined(__GNUC__)
-#define USE_OVERFLOW_BUILTINS (__GNUC__ >= 5)
-#else
-#define USE_OVERFLOW_BUILTINS (0)
-#endif
-
-template <typename T>
-bool CheckedAddImpl(T x, T y, T* result) {
-  static_assert(std::is_integral<T>::value, "Type must be integral");
-  // Since the value of x+y is undefined if we have a signed type, we compute
-  // it using the unsigned type of the same size.
-  using UnsignedDst = typename std::make_unsigned<T>::type;
-  using SignedDst = typename std::make_signed<T>::type;
-  auto ux = static_cast<UnsignedDst>(x);
-  auto uy = static_cast<UnsignedDst>(y);
-  auto uresult = static_cast<UnsignedDst>(ux + uy);
-  *result = static_cast<T>(uresult);
-  // Addition is valid if the sign of (x + y) is equal to either that of x or
-  // that of y.
-  return (std::is_signed<T>::value)
-             ? static_cast<SignedDst>((uresult ^ ux) & (uresult ^ uy)) >= 0
-             : uresult >= uy;  // Unsigned is either valid or underflow.
-}
-
-template <typename T, typename U, class Enable = void>
-struct CheckedAddOp {};
-
-template <typename T, typename U>
-struct CheckedAddOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
-  template <typename V>
-  static bool Do(T x, U y, V* result) {
-#if USE_OVERFLOW_BUILTINS
-    return !__builtin_add_overflow(x, y, result);
-#else
-    using Promotion = typename BigEnoughPromotion<T, U>::type;
-    Promotion presult;
-    // Fail if either operand is out of range for the promoted type.
-    // TODO(jschuh): This could be made to work for a broader range of values.
-    bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
-                    IsValueInRangeForNumericType<Promotion>(y);
-
-    if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
-      presult = static_cast<Promotion>(x) + static_cast<Promotion>(y);
-    } else {
-      is_valid &= CheckedAddImpl(static_cast<Promotion>(x),
-                                 static_cast<Promotion>(y), &presult);
-    }
-    *result = static_cast<V>(presult);
-    return is_valid && IsValueInRangeForNumericType<V>(presult);
-#endif
-  }
-};
-
-template <typename T>
-bool CheckedSubImpl(T x, T y, T* result) {
-  static_assert(std::is_integral<T>::value, "Type must be integral");
-  // Since the value of x+y is undefined if we have a signed type, we compute
-  // it using the unsigned type of the same size.
-  using UnsignedDst = typename std::make_unsigned<T>::type;
-  using SignedDst = typename std::make_signed<T>::type;
-  auto ux = static_cast<UnsignedDst>(x);
-  auto uy = static_cast<UnsignedDst>(y);
-  auto uresult = static_cast<UnsignedDst>(ux - uy);
-  *result = static_cast<T>(uresult);
-  // Subtraction is valid if either x and y have same sign, or (x-y) and x have
-  // the same sign.
-  return (std::is_signed<T>::value)
-             ? static_cast<SignedDst>((uresult ^ ux) & (ux ^ uy)) >= 0
-             : x >= y;
-}
-
-template <typename T, typename U, class Enable = void>
-struct CheckedSubOp {};
-
-template <typename T, typename U>
-struct CheckedSubOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
-  template <typename V>
-  static bool Do(T x, U y, V* result) {
-#if USE_OVERFLOW_BUILTINS
-    return !__builtin_sub_overflow(x, y, result);
-#else
-    using Promotion = typename BigEnoughPromotion<T, U>::type;
-    Promotion presult;
-    // Fail if either operand is out of range for the promoted type.
-    // TODO(jschuh): This could be made to work for a broader range of values.
-    bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
-                    IsValueInRangeForNumericType<Promotion>(y);
-
-    if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
-      presult = static_cast<Promotion>(x) - static_cast<Promotion>(y);
-    } else {
-      is_valid &= CheckedSubImpl(static_cast<Promotion>(x),
-                                 static_cast<Promotion>(y), &presult);
-    }
-    *result = static_cast<V>(presult);
-    return is_valid && IsValueInRangeForNumericType<V>(presult);
-#endif
-  }
-};
-
-template <typename T>
-bool CheckedMulImpl(T x, T y, T* result) {
-  static_assert(std::is_integral<T>::value, "Type must be integral");
-  // Since the value of x*y is potentially undefined if we have a signed type,
-  // we compute it using the unsigned type of the same size.
-  using UnsignedDst = typename std::make_unsigned<T>::type;
-  using SignedDst = typename std::make_signed<T>::type;
-  const UnsignedDst ux = SafeUnsignedAbs(x);
-  const UnsignedDst uy = SafeUnsignedAbs(y);
-  auto uresult = static_cast<UnsignedDst>(ux * uy);
-  const bool is_negative =
-      std::is_signed<T>::value && static_cast<SignedDst>(x ^ y) < 0;
-  *result = is_negative ? 0 - uresult : uresult;
-  // We have a fast out for unsigned identity or zero on the second operand.
-  // After that it's an unsigned overflow check on the absolute value, with
-  // a +1 bound for a negative result.
-  return uy <= UnsignedDst(!std::is_signed<T>::value || is_negative) ||
-         ux <= (std::numeric_limits<T>::max() + UnsignedDst(is_negative)) / uy;
-}
-
-template <typename T, typename U, class Enable = void>
-struct CheckedMulOp {};
-
-template <typename T, typename U>
-struct CheckedMulOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
-  template <typename V>
-  static bool Do(T x, U y, V* result) {
-#if USE_OVERFLOW_BUILTINS
-#if defined(__clang__)
-    // TODO(jschuh): Get the Clang runtime library issues sorted out so we can
-    // support full-width, mixed-sign multiply builtins.
-    // https://crbug.com/613003
-    static const bool kUseMaxInt =
-        // Narrower type than uintptr_t is always safe.
-        std::numeric_limits<__typeof__(x * y)>::digits <
-            std::numeric_limits<intptr_t>::digits ||
-        // Safe for intptr_t and uintptr_t if the sign matches.
-        (IntegerBitsPlusSign<__typeof__(x * y)>::value ==
-             IntegerBitsPlusSign<intptr_t>::value &&
-         std::is_signed<T>::value == std::is_signed<U>::value);
-#else
-    static const bool kUseMaxInt = true;
-#endif
-    if (kUseMaxInt)
-      return !__builtin_mul_overflow(x, y, result);
-#endif
-    using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
-    Promotion presult;
-    // Fail if either operand is out of range for the promoted type.
-    // TODO(jschuh): This could be made to work for a broader range of values.
-    bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
-                    IsValueInRangeForNumericType<Promotion>(y);
-
-    if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
-      presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
-    } else {
-      is_valid &= CheckedMulImpl(static_cast<Promotion>(x),
-                                 static_cast<Promotion>(y), &presult);
-    }
-    *result = static_cast<V>(presult);
-    return is_valid && IsValueInRangeForNumericType<V>(presult);
-  }
-};
-
-// Avoid poluting the namespace once we're done with the macro.
-#undef USE_OVERFLOW_BUILTINS
-
-// Division just requires a check for a zero denominator or an invalid negation
-// on signed min/-1.
-template <typename T>
-bool CheckedDivImpl(T x, T y, T* result) {
-  static_assert(std::is_integral<T>::value, "Type must be integral");
-  if (y && (!std::is_signed<T>::value ||
-            x != std::numeric_limits<T>::lowest() || y != static_cast<T>(-1))) {
-    *result = x / y;
-    return true;
-  }
-  return false;
-}
-
-template <typename T, typename U, class Enable = void>
-struct CheckedDivOp {};
-
-template <typename T, typename U>
-struct CheckedDivOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
-  template <typename V>
-  static bool Do(T x, U y, V* result) {
-    using Promotion = typename BigEnoughPromotion<T, U>::type;
-    Promotion presult;
-    // Fail if either operand is out of range for the promoted type.
-    // TODO(jschuh): This could be made to work for a broader range of values.
-    bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
-                    IsValueInRangeForNumericType<Promotion>(y);
-    is_valid &= CheckedDivImpl(static_cast<Promotion>(x),
-                               static_cast<Promotion>(y), &presult);
-    *result = static_cast<V>(presult);
-    return is_valid && IsValueInRangeForNumericType<V>(presult);
-  }
-};
-
-template <typename T>
-bool CheckedModImpl(T x, T y, T* result) {
-  static_assert(std::is_integral<T>::value, "Type must be integral");
-  if (y > 0) {
-    *result = static_cast<T>(x % y);
-    return true;
-  }
-  return false;
-}
-
-template <typename T, typename U, class Enable = void>
-struct CheckedModOp {};
-
-template <typename T, typename U>
-struct CheckedModOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
-  template <typename V>
-  static bool Do(T x, U y, V* result) {
-    using Promotion = typename BigEnoughPromotion<T, U>::type;
-    Promotion presult;
-    bool is_valid = CheckedModImpl(static_cast<Promotion>(x),
-                                   static_cast<Promotion>(y), &presult);
-    *result = static_cast<V>(presult);
-    return is_valid && IsValueInRangeForNumericType<V>(presult);
-  }
-};
-
-template <typename T, typename U, class Enable = void>
-struct CheckedLshOp {};
-
-// Left shift. Shifts less than 0 or greater than or equal to the number
-// of bits in the promoted type are undefined. Shifts of negative values
-// are undefined. Otherwise it is defined when the result fits.
-template <typename T, typename U>
-struct CheckedLshOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = T;
-  template <typename V>
-  static bool Do(T x, U shift, V* result) {
-    using ShiftType = typename std::make_unsigned<T>::type;
-    static const ShiftType kBitWidth = IntegerBitsPlusSign<T>::value;
-    const auto real_shift = static_cast<ShiftType>(shift);
-    // Signed shift is not legal on negative values.
-    if (!IsValueNegative(x) && real_shift < kBitWidth) {
-      // Just use a multiplication because it's easy.
-      // TODO(jschuh): This could probably be made more efficient.
-      if (!std::is_signed<T>::value || real_shift != kBitWidth - 1)
-        return CheckedMulOp<T, T>::Do(x, static_cast<T>(1) << shift, result);
-      return !x;  // Special case zero for a full width signed shift.
-    }
-    return false;
-  }
-};
-
-template <typename T, typename U, class Enable = void>
-struct CheckedRshOp {};
-
-// Right shift. Shifts less than 0 or greater than or equal to the number
-// of bits in the promoted type are undefined. Otherwise, it is always defined,
-// but a right shift of a negative value is implementation-dependent.
-template <typename T, typename U>
-struct CheckedRshOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = T;
-  template <typename V = result_type>
-  static bool Do(T x, U shift, V* result) {
-    // Use the type conversion push negative values out of range.
-    using ShiftType = typename std::make_unsigned<T>::type;
-    if (static_cast<ShiftType>(shift) < IntegerBitsPlusSign<T>::value) {
-      T tmp = x >> shift;
-      *result = static_cast<V>(tmp);
-      return IsValueInRangeForNumericType<V>(tmp);
-    }
-    return false;
-  }
-};
-
-template <typename T, typename U, class Enable = void>
-struct CheckedAndOp {};
-
-// For simplicity we support only unsigned integer results.
-template <typename T, typename U>
-struct CheckedAndOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename std::make_unsigned<
-      typename MaxExponentPromotion<T, U>::type>::type;
-  template <typename V = result_type>
-  static bool Do(T x, U y, V* result) {
-    result_type tmp = static_cast<result_type>(x) & static_cast<result_type>(y);
-    *result = static_cast<V>(tmp);
-    return IsValueInRangeForNumericType<V>(tmp);
-  }
-};
-
-template <typename T, typename U, class Enable = void>
-struct CheckedOrOp {};
-
-// For simplicity we support only unsigned integers.
-template <typename T, typename U>
-struct CheckedOrOp<T,
-                   U,
-                   typename std::enable_if<std::is_integral<T>::value &&
-                                           std::is_integral<U>::value>::type> {
-  using result_type = typename std::make_unsigned<
-      typename MaxExponentPromotion<T, U>::type>::type;
-  template <typename V = result_type>
-  static bool Do(T x, U y, V* result) {
-    result_type tmp = static_cast<result_type>(x) | static_cast<result_type>(y);
-    *result = static_cast<V>(tmp);
-    return IsValueInRangeForNumericType<V>(tmp);
-  }
-};
-
-template <typename T, typename U, class Enable = void>
-struct CheckedXorOp {};
-
-// For simplicity we support only unsigned integers.
-template <typename T, typename U>
-struct CheckedXorOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename std::make_unsigned<
-      typename MaxExponentPromotion<T, U>::type>::type;
-  template <typename V = result_type>
-  static bool Do(T x, U y, V* result) {
-    result_type tmp = static_cast<result_type>(x) ^ static_cast<result_type>(y);
-    *result = static_cast<V>(tmp);
-    return IsValueInRangeForNumericType<V>(tmp);
-  }
-};
-
-// Max doesn't really need to be implemented this way because it can't fail,
-// but it makes the code much cleaner to use the MathOp wrappers.
-template <typename T, typename U, class Enable = void>
-struct CheckedMaxOp {};
-
-template <typename T, typename U>
-struct CheckedMaxOp<
-    T,
-    U,
-    typename std::enable_if<std::is_arithmetic<T>::value &&
-                            std::is_arithmetic<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
-  template <typename V = result_type>
-  static bool Do(T x, U y, V* result) {
-    *result = IsGreater<T, U>::Test(x, y) ? static_cast<result_type>(x)
-                                          : static_cast<result_type>(y);
-    return true;
-  }
-};
-
-// Min doesn't really need to be implemented this way because it can't fail,
-// but it makes the code much cleaner to use the MathOp wrappers.
-template <typename T, typename U, class Enable = void>
-struct CheckedMinOp {};
-
-template <typename T, typename U>
-struct CheckedMinOp<
-    T,
-    U,
-    typename std::enable_if<std::is_arithmetic<T>::value &&
-                            std::is_arithmetic<U>::value>::type> {
-  using result_type = typename LowestValuePromotion<T, U>::type;
-  template <typename V = result_type>
-  static bool Do(T x, U y, V* result) {
-    *result = IsLess<T, U>::Test(x, y) ? static_cast<result_type>(x)
-                                       : static_cast<result_type>(y);
-    return true;
-  }
-};
-
-// This is just boilerplate that wraps the standard floating point arithmetic.
-// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
-#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP)                                    \
-  template <typename T, typename U>                                            \
-  struct Checked##NAME##Op<                                                    \
-      T, U, typename std::enable_if<std::is_floating_point<T>::value ||        \
-                                    std::is_floating_point<U>::value>::type> { \
-    using result_type = typename MaxExponentPromotion<T, U>::type;             \
-    template <typename V>                                                      \
-    static bool Do(T x, U y, V* result) {                                      \
-      using Promotion = typename MaxExponentPromotion<T, U>::type;             \
-      Promotion presult = x OP y;                                              \
-      *result = static_cast<V>(presult);                                       \
-      return IsValueInRangeForNumericType<V>(presult);                         \
-    }                                                                          \
-  };
-
-BASE_FLOAT_ARITHMETIC_OPS(Add, +)
-BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
-BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
-BASE_FLOAT_ARITHMETIC_OPS(Div, /)
-
-#undef BASE_FLOAT_ARITHMETIC_OPS
-
-// Wrap the unary operations to allow SFINAE when instantiating integrals versus
-// floating points. These don't perform any overflow checking. Rather, they
-// exhibit well-defined overflow semantics and rely on the caller to detect
-// if an overflow occured.
-
-template <typename T,
-          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
-constexpr T NegateWrapper(T value) {
-  using UnsignedT = typename std::make_unsigned<T>::type;
-  // This will compile to a NEG on Intel, and is normal negation on ARM.
-  return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value));
-}
-
-template <
-    typename T,
-    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
-constexpr T NegateWrapper(T value) {
-  return -value;
-}
-
-template <typename T,
-          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
-constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) {
-  return ~value;
-}
-
-template <typename T,
-          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
-constexpr T AbsWrapper(T value) {
-  return static_cast<T>(SafeUnsignedAbs(value));
-}
-
-template <
-    typename T,
-    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
-constexpr T AbsWrapper(T value) {
-  return value < 0 ? -value : value;
-}
-
-// Floats carry around their validity state with them, but integers do not. So,
-// we wrap the underlying value in a specialization in order to hide that detail
-// and expose an interface via accessors.
-enum NumericRepresentation {
-  NUMERIC_INTEGER,
-  NUMERIC_FLOATING,
-  NUMERIC_UNKNOWN
-};
-
-template <typename NumericType>
-struct GetNumericRepresentation {
-  static const NumericRepresentation value =
-      std::is_integral<NumericType>::value
-          ? NUMERIC_INTEGER
-          : (std::is_floating_point<NumericType>::value ? NUMERIC_FLOATING
-                                                        : NUMERIC_UNKNOWN);
-};
-
-template <typename T, NumericRepresentation type =
-                          GetNumericRepresentation<T>::value>
-class CheckedNumericState {};
-
-// Integrals require quite a bit of additional housekeeping to manage state.
-template <typename T>
-class CheckedNumericState<T, NUMERIC_INTEGER> {
- private:
-  // is_valid_ precedes value_ because member intializers in the constructors
-  // are evaluated in field order, and is_valid_ must be read when initializing
-  // value_.
-  bool is_valid_;
-  T value_;
-
-  // Ensures that a type conversion does not trigger undefined behavior.
-  template <typename Src>
-  static constexpr T WellDefinedConversionOrZero(const Src value,
-                                                 const bool is_valid) {
-    using SrcType = typename internal::UnderlyingType<Src>::type;
-    return (std::is_integral<SrcType>::value || is_valid)
-               ? static_cast<T>(value)
-               : static_cast<T>(0);
-  }
-
- public:
-  template <typename Src, NumericRepresentation type>
-  friend class CheckedNumericState;
-
-  constexpr CheckedNumericState() : is_valid_(true), value_(0) {}
-
-  template <typename Src>
-  constexpr CheckedNumericState(Src value, bool is_valid)
-      : is_valid_(is_valid && IsValueInRangeForNumericType<T>(value)),
-        value_(WellDefinedConversionOrZero(value, is_valid_)) {
-    static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
-  }
-
-  // Copy constructor.
-  template <typename Src>
-  constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
-      : is_valid_(rhs.IsValid()),
-        value_(WellDefinedConversionOrZero(rhs.value(), is_valid_)) {}
-
-  template <typename Src>
-  constexpr explicit CheckedNumericState(Src value)
-      : is_valid_(IsValueInRangeForNumericType<T>(value)),
-        value_(WellDefinedConversionOrZero(value, is_valid_)) {}
-
-  constexpr bool is_valid() const { return is_valid_; }
-  constexpr T value() const { return value_; }
-};
-
-// Floating points maintain their own validity, but need translation wrappers.
-template <typename T>
-class CheckedNumericState<T, NUMERIC_FLOATING> {
- private:
-  T value_;
-
-  // Ensures that a type conversion does not trigger undefined behavior.
-  template <typename Src>
-  static constexpr T WellDefinedConversionOrNaN(const Src value,
-                                                const bool is_valid) {
-    using SrcType = typename internal::UnderlyingType<Src>::type;
-    return (StaticDstRangeRelationToSrcRange<T, SrcType>::value ==
-                NUMERIC_RANGE_CONTAINED ||
-            is_valid)
-               ? static_cast<T>(value)
-               : std::numeric_limits<T>::quiet_NaN();
-  }
-
- public:
-  template <typename Src, NumericRepresentation type>
-  friend class CheckedNumericState;
-
-  constexpr CheckedNumericState() : value_(0.0) {}
-
-  template <typename Src>
-  constexpr CheckedNumericState(Src value, bool is_valid)
-      : value_(WellDefinedConversionOrNaN(value, is_valid)) {}
-
-  template <typename Src>
-  constexpr explicit CheckedNumericState(Src value)
-      : value_(WellDefinedConversionOrNaN(
-            value,
-            IsValueInRangeForNumericType<T>(value))) {}
-
-  // Copy constructor.
-  template <typename Src>
-  constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
-      : value_(WellDefinedConversionOrNaN(
-            rhs.value(),
-            rhs.is_valid() && IsValueInRangeForNumericType<T>(rhs.value()))) {}
-
-  constexpr bool is_valid() const {
-    // Written this way because std::isfinite is not reliably constexpr.
-    // TODO(jschuh): Fix this if the libraries ever get fixed.
-    return value_ <= std::numeric_limits<T>::max() &&
-           value_ >= std::numeric_limits<T>::lowest();
-  }
-  constexpr T value() const { return value_; }
-};
-
-template <template <typename, typename, typename> class M,
-          typename L,
-          typename R>
-struct MathWrapper {
-  using math = M<typename UnderlyingType<L>::type,
-                 typename UnderlyingType<R>::type,
-                 void>;
-  using type = typename math::result_type;
-};
-
-}  // namespace internal
-}  // namespace base
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_IMPL_H_
diff --git a/third_party/base/numerics/safe_math_shared_impl.h b/third_party/base/numerics/safe_math_shared_impl.h
new file mode 100644
index 0000000..871906d
--- /dev/null
+++ b/third_party/base/numerics/safe_math_shared_impl.h
@@ -0,0 +1,218 @@
+// Copyright 2017 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_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
+#define THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <cassert>
+#include <climits>
+#include <cmath>
+#include <cstdlib>
+#include <limits>
+#include <type_traits>
+
+#include "build/build_config.h"
+#include "third_party/base/numerics/safe_conversions.h"
+
+#if BUILDFLAG(IS_ASMJS)
+// Optimized safe math instructions are incompatible with asmjs.
+#define BASE_HAS_OPTIMIZED_SAFE_MATH (0)
+// Where available use builtin math overflow support on Clang and GCC.
+#elif !defined(__native_client__) &&                       \
+    ((defined(__clang__) &&                                \
+      ((__clang_major__ > 3) ||                            \
+       (__clang_major__ == 3 && __clang_minor__ >= 4))) || \
+     (defined(__GNUC__) && __GNUC__ >= 5))
+#include "third_party/base/numerics/safe_math_clang_gcc_impl.h"
+#define BASE_HAS_OPTIMIZED_SAFE_MATH (1)
+#else
+#define BASE_HAS_OPTIMIZED_SAFE_MATH (0)
+#endif
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+// These are the non-functioning boilerplate implementations of the optimized
+// safe math routines.
+#if !BASE_HAS_OPTIMIZED_SAFE_MATH
+template <typename T, typename U>
+struct CheckedAddFastOp {
+  static const bool is_supported = false;
+  template <typename V>
+  static constexpr bool Do(T, U, V*) {
+    // Force a compile failure if instantiated.
+    return CheckOnFailure::template HandleFailure<bool>();
+  }
+};
+
+template <typename T, typename U>
+struct CheckedSubFastOp {
+  static const bool is_supported = false;
+  template <typename V>
+  static constexpr bool Do(T, U, V*) {
+    // Force a compile failure if instantiated.
+    return CheckOnFailure::template HandleFailure<bool>();
+  }
+};
+
+template <typename T, typename U>
+struct CheckedMulFastOp {
+  static const bool is_supported = false;
+  template <typename V>
+  static constexpr bool Do(T, U, V*) {
+    // Force a compile failure if instantiated.
+    return CheckOnFailure::template HandleFailure<bool>();
+  }
+};
+
+template <typename T, typename U>
+struct ClampedAddFastOp {
+  static const bool is_supported = false;
+  template <typename V>
+  static constexpr V Do(T, U) {
+    // Force a compile failure if instantiated.
+    return CheckOnFailure::template HandleFailure<V>();
+  }
+};
+
+template <typename T, typename U>
+struct ClampedSubFastOp {
+  static const bool is_supported = false;
+  template <typename V>
+  static constexpr V Do(T, U) {
+    // Force a compile failure if instantiated.
+    return CheckOnFailure::template HandleFailure<V>();
+  }
+};
+
+template <typename T, typename U>
+struct ClampedMulFastOp {
+  static const bool is_supported = false;
+  template <typename V>
+  static constexpr V Do(T, U) {
+    // Force a compile failure if instantiated.
+    return CheckOnFailure::template HandleFailure<V>();
+  }
+};
+
+template <typename T>
+struct ClampedNegFastOp {
+  static const bool is_supported = false;
+  static constexpr T Do(T) {
+    // Force a compile failure if instantiated.
+    return CheckOnFailure::template HandleFailure<T>();
+  }
+};
+#endif  // BASE_HAS_OPTIMIZED_SAFE_MATH
+#undef BASE_HAS_OPTIMIZED_SAFE_MATH
+
+// This is used for UnsignedAbs, where we need to support floating-point
+// template instantiations even though we don't actually support the operations.
+// However, there is no corresponding implementation of e.g. SafeUnsignedAbs,
+// so the float versions will not compile.
+template <typename Numeric,
+          bool IsInteger = std::is_integral<Numeric>::value,
+          bool IsFloat = std::is_floating_point<Numeric>::value>
+struct UnsignedOrFloatForSize;
+
+template <typename Numeric>
+struct UnsignedOrFloatForSize<Numeric, true, false> {
+  using type = typename std::make_unsigned<Numeric>::type;
+};
+
+template <typename Numeric>
+struct UnsignedOrFloatForSize<Numeric, false, true> {
+  using type = Numeric;
+};
+
+// Wrap the unary operations to allow SFINAE when instantiating integrals versus
+// floating points. These don't perform any overflow checking. Rather, they
+// exhibit well-defined overflow semantics and rely on the caller to detect
+// if an overflow occurred.
+
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+constexpr T NegateWrapper(T value) {
+  using UnsignedT = typename std::make_unsigned<T>::type;
+  // This will compile to a NEG on Intel, and is normal negation on ARM.
+  return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value));
+}
+
+template <
+    typename T,
+    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+constexpr T NegateWrapper(T value) {
+  return -value;
+}
+
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) {
+  return ~value;
+}
+
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+constexpr T AbsWrapper(T value) {
+  return static_cast<T>(SafeUnsignedAbs(value));
+}
+
+template <
+    typename T,
+    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+constexpr T AbsWrapper(T value) {
+  return value < 0 ? -value : value;
+}
+
+template <template <typename, typename, typename> class M,
+          typename L,
+          typename R>
+struct MathWrapper {
+  using math = M<typename UnderlyingType<L>::type,
+                 typename UnderlyingType<R>::type,
+                 void>;
+  using type = typename math::result_type;
+};
+
+// The following macros are just boilerplate for the standard arithmetic
+// operator overloads and variadic function templates. A macro isn't the nicest
+// solution, but it beats rewriting these over and over again.
+#define BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME)       \
+  template <typename L, typename R, typename... Args>                   \
+  constexpr auto CL_ABBR##OP_NAME(const L lhs, const R rhs,             \
+                                  const Args... args) {                 \
+    return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \
+                                                              args...); \
+  }
+
+#define BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, CMP_OP) \
+  /* Binary arithmetic operator for all CLASS##Numeric operations. */          \
+  template <typename L, typename R,                                            \
+            typename std::enable_if<Is##CLASS##Op<L, R>::value>::type* =       \
+                nullptr>                                                       \
+  constexpr CLASS##Numeric<                                                    \
+      typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type>                    \
+  operator OP(const L lhs, const R rhs) {                                      \
+    return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs,      \
+                                                                     rhs);     \
+  }                                                                            \
+  /* Assignment arithmetic operator implementation from CLASS##Numeric. */     \
+  template <typename L>                                                        \
+  template <typename R>                                                        \
+  constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP(             \
+      const R rhs) {                                                           \
+    return MathOp<CLASS##OP_NAME##Op>(rhs);                                    \
+  }                                                                            \
+  /* Variadic arithmetic functions that return CLASS##Numeric. */              \
+  BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME)
+
+}  // namespace internal
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
diff --git a/third_party/base/optional.h b/third_party/base/optional.h
deleted file mode 100644
index 5998105..0000000
--- a/third_party/base/optional.h
+++ /dev/null
@@ -1,511 +0,0 @@
-// Copyright 2016 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_OPTIONAL_H_
-#define THIRD_PARTY_BASE_OPTIONAL_H_
-
-#include <functional>
-#include <type_traits>
-#include <utility>
-
-#include "third_party/base/logging.h"
-
-namespace pdfium {
-
-// Specification:
-// http://en.cppreference.com/w/cpp/utility/optional/in_place_t
-struct in_place_t {};
-
-// Specification:
-// http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
-struct nullopt_t {
-  constexpr explicit nullopt_t(int) {}
-};
-
-// Specification:
-// http://en.cppreference.com/w/cpp/utility/optional/in_place
-constexpr in_place_t in_place = {};
-
-// Specification:
-// http://en.cppreference.com/w/cpp/utility/optional/nullopt
-constexpr nullopt_t nullopt(0);
-
-namespace internal {
-
-template <typename T, bool = std::is_trivially_destructible<T>::value>
-struct OptionalStorage {
-  // Initializing |empty_| here instead of using default member initializing
-  // to avoid errors in g++ 4.8.
-  constexpr OptionalStorage() : empty_('\0') {}
-
-  constexpr explicit OptionalStorage(const T& value)
-      : is_null_(false), value_(value) {}
-
-  // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
-  explicit OptionalStorage(T&& value)
-      : is_null_(false), value_(std::move(value)) {}
-
-  // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
-  template <class... Args>
-  explicit OptionalStorage(in_place_t, Args&&... args)
-      : is_null_(false), value_(std::forward<Args>(args)...) {}
-
-  // When T is not trivially destructible we must call its
-  // destructor before deallocating its memory.
-  ~OptionalStorage() {
-    if (!is_null_)
-      value_.~T();
-  }
-
-  bool is_null_ = true;
-  union {
-    // |empty_| exists so that the union will always be initialized, even when
-    // it doesn't contain a value. Union members must be initialized for the
-    // constructor to be 'constexpr'.
-    char empty_;
-    T value_;
-  };
-};
-
-template <typename T>
-struct OptionalStorage<T, true> {
-  // Initializing |empty_| here instead of using default member initializing
-  // to avoid errors in g++ 4.8.
-  constexpr OptionalStorage() : empty_('\0') {}
-
-  constexpr explicit OptionalStorage(const T& value)
-      : is_null_(false), value_(value) {}
-
-  // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
-  explicit OptionalStorage(T&& value)
-      : is_null_(false), value_(std::move(value)) {}
-
-  // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
-  template <class... Args>
-  explicit OptionalStorage(in_place_t, Args&&... args)
-      : is_null_(false), value_(std::forward<Args>(args)...) {}
-
-  // When T is trivially destructible (i.e. its destructor does nothing) there
-  // is no need to call it. Explicitly defaulting the destructor means it's not
-  // user-provided. Those two together make this destructor trivial.
-  ~OptionalStorage() = default;
-
-  bool is_null_ = true;
-  union {
-    // |empty_| exists so that the union will always be initialized, even when
-    // it doesn't contain a value. Union members must be initialized for the
-    // constructor to be 'constexpr'.
-    char empty_;
-    T value_;
-  };
-};
-
-}  // namespace internal
-
-// pdfium::Optional is a PDFium version of the C++17 optional class,
-// based on the Chromium version:
-// std::optional documentation:
-// http://en.cppreference.com/w/cpp/utility/optional
-// Chromium documentation:
-// https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md
-//
-// These are the differences between the specification and the implementation:
-// - The constructor and emplace method using initializer_list are not
-//   implemented because 'initializer_list' is banned from Chromium.
-// - Constructors do not use 'constexpr' as it is a C++14 extension.
-// - 'constexpr' might be missing in some places for reasons specified locally.
-// - No exceptions are thrown, because they are banned from Chromium.
-// - All the non-members are in the 'pdifum' namespace instead of 'std'.
-template <typename T>
-class Optional {
- public:
-  using value_type = T;
-
-  constexpr Optional() = default;
-
-  constexpr Optional(nullopt_t) {}
-
-  Optional(const Optional& other) {
-    if (!other.storage_.is_null_)
-      Init(other.value());
-  }
-
-  Optional(Optional&& other) {
-    if (!other.storage_.is_null_)
-      Init(std::move(other.value()));
-  }
-
-  constexpr Optional(const T& value) : storage_(value) {}
-
-  // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
-  Optional(T&& value) : storage_(std::move(value)) {}
-
-  // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
-  template <class... Args>
-  explicit Optional(in_place_t, Args&&... args)
-      : storage_(in_place, std::forward<Args>(args)...) {}
-
-  ~Optional() = default;
-
-  Optional& operator=(nullopt_t) {
-    FreeIfNeeded();
-    return *this;
-  }
-
-  Optional& operator=(const Optional& other) {
-    if (other.storage_.is_null_) {
-      FreeIfNeeded();
-      return *this;
-    }
-
-    InitOrAssign(other.value());
-    return *this;
-  }
-
-  Optional& operator=(Optional&& other) {
-    if (other.storage_.is_null_) {
-      FreeIfNeeded();
-      return *this;
-    }
-
-    InitOrAssign(std::move(other.value()));
-    return *this;
-  }
-
-  template <class U>
-  typename std::enable_if<std::is_same<std::decay<U>, T>::value,
-                          Optional&>::type
-  operator=(U&& value) {
-    InitOrAssign(std::forward<U>(value));
-    return *this;
-  }
-
-  // TODO(mlamouri): can't use 'constexpr' with DCHECK.
-  const T* operator->() const {
-    DCHECK(!storage_.is_null_);
-    return &value();
-  }
-
-  // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
-  // meant to be 'constexpr const'.
-  T* operator->() {
-    DCHECK(!storage_.is_null_);
-    return &value();
-  }
-
-  constexpr const T& operator*() const& { return value(); }
-
-  // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
-  // meant to be 'constexpr const'.
-  T& operator*() & { return value(); }
-
-  constexpr const T&& operator*() const&& { return std::move(value()); }
-
-  // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
-  // meant to be 'constexpr const'.
-  T&& operator*() && { return std::move(value()); }
-
-  constexpr explicit operator bool() const { return !storage_.is_null_; }
-
-  constexpr bool has_value() const { return !storage_.is_null_; }
-
-  // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
-  // meant to be 'constexpr const'.
-  T& value() & {
-    DCHECK(!storage_.is_null_);
-    return storage_.value_;
-  }
-
-  // TODO(mlamouri): can't use 'constexpr' with DCHECK.
-  const T& value() const& {
-    DCHECK(!storage_.is_null_);
-    return storage_.value_;
-  }
-
-  // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
-  // meant to be 'constexpr const'.
-  T&& value() && {
-    DCHECK(!storage_.is_null_);
-    return std::move(storage_.value_);
-  }
-
-  // TODO(mlamouri): can't use 'constexpr' with DCHECK.
-  const T&& value() const&& {
-    DCHECK(!storage_.is_null_);
-    return std::move(storage_.value_);
-  }
-
-  template <class U>
-  constexpr T value_or(U&& default_value) const& {
-    // TODO(mlamouri): add the following assert when possible:
-    // static_assert(std::is_copy_constructible<T>::value,
-    //               "T must be copy constructible");
-    static_assert(std::is_convertible<U, T>::value,
-                  "U must be convertible to T");
-    return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
-                             : value();
-  }
-
-  template <class U>
-  T value_or(U&& default_value) && {
-    // TODO(mlamouri): add the following assert when possible:
-    // static_assert(std::is_move_constructible<T>::value,
-    //               "T must be move constructible");
-    static_assert(std::is_convertible<U, T>::value,
-                  "U must be convertible to T");
-    return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
-                             : std::move(value());
-  }
-
-  void swap(Optional& other) {
-    if (storage_.is_null_ && other.storage_.is_null_)
-      return;
-
-    if (storage_.is_null_ != other.storage_.is_null_) {
-      if (storage_.is_null_) {
-        Init(std::move(other.storage_.value_));
-        other.FreeIfNeeded();
-      } else {
-        other.Init(std::move(storage_.value_));
-        FreeIfNeeded();
-      }
-      return;
-    }
-
-    DCHECK(!storage_.is_null_ && !other.storage_.is_null_);
-    using std::swap;
-    swap(**this, *other);
-  }
-
-  void reset() {
-    FreeIfNeeded();
-  }
-
-  template <class... Args>
-  void emplace(Args&&... args) {
-    FreeIfNeeded();
-    Init(std::forward<Args>(args)...);
-  }
-
- private:
-  void Init(const T& value) {
-    DCHECK(storage_.is_null_);
-    new (&storage_.value_) T(value);
-    storage_.is_null_ = false;
-  }
-
-  void Init(T&& value) {
-    DCHECK(storage_.is_null_);
-    new (&storage_.value_) T(std::move(value));
-    storage_.is_null_ = false;
-  }
-
-  template <class... Args>
-  void Init(Args&&... args) {
-    DCHECK(storage_.is_null_);
-    new (&storage_.value_) T(std::forward<Args>(args)...);
-    storage_.is_null_ = false;
-  }
-
-  void InitOrAssign(const T& value) {
-    if (storage_.is_null_)
-      Init(value);
-    else
-      storage_.value_ = value;
-  }
-
-  void InitOrAssign(T&& value) {
-    if (storage_.is_null_)
-      Init(std::move(value));
-    else
-      storage_.value_ = std::move(value);
-  }
-
-  void FreeIfNeeded() {
-    if (storage_.is_null_)
-      return;
-    storage_.value_.~T();
-    storage_.is_null_ = true;
-  }
-
-  internal::OptionalStorage<T> storage_;
-};
-
-template <class T>
-constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) {
-  return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs);
-}
-
-template <class T>
-constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) {
-  return !(lhs == rhs);
-}
-
-template <class T>
-constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) {
-  return rhs == nullopt ? false : (lhs == nullopt ? true : *lhs < *rhs);
-}
-
-template <class T>
-constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) {
-  return !(rhs < lhs);
-}
-
-template <class T>
-constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) {
-  return rhs < lhs;
-}
-
-template <class T>
-constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) {
-  return !(lhs < rhs);
-}
-
-template <class T>
-constexpr bool operator==(const Optional<T>& opt, nullopt_t) {
-  return !opt;
-}
-
-template <class T>
-constexpr bool operator==(nullopt_t, const Optional<T>& opt) {
-  return !opt;
-}
-
-template <class T>
-constexpr bool operator!=(const Optional<T>& opt, nullopt_t) {
-  return !!opt;
-}
-
-template <class T>
-constexpr bool operator!=(nullopt_t, const Optional<T>& opt) {
-  return !!opt;
-}
-
-template <class T>
-constexpr bool operator<(const Optional<T>& opt, nullopt_t) {
-  return false;
-}
-
-template <class T>
-constexpr bool operator<(nullopt_t, const Optional<T>& opt) {
-  return !!opt;
-}
-
-template <class T>
-constexpr bool operator<=(const Optional<T>& opt, nullopt_t) {
-  return !opt;
-}
-
-template <class T>
-constexpr bool operator<=(nullopt_t, const Optional<T>& opt) {
-  return true;
-}
-
-template <class T>
-constexpr bool operator>(const Optional<T>& opt, nullopt_t) {
-  return !!opt;
-}
-
-template <class T>
-constexpr bool operator>(nullopt_t, const Optional<T>& opt) {
-  return false;
-}
-
-template <class T>
-constexpr bool operator>=(const Optional<T>& opt, nullopt_t) {
-  return true;
-}
-
-template <class T>
-constexpr bool operator>=(nullopt_t, const Optional<T>& opt) {
-  return !opt;
-}
-
-template <class T>
-constexpr bool operator==(const Optional<T>& opt, const T& value) {
-  return opt != nullopt ? *opt == value : false;
-}
-
-template <class T>
-constexpr bool operator==(const T& value, const Optional<T>& opt) {
-  return opt == value;
-}
-
-template <class T>
-constexpr bool operator!=(const Optional<T>& opt, const T& value) {
-  return !(opt == value);
-}
-
-template <class T>
-constexpr bool operator!=(const T& value, const Optional<T>& opt) {
-  return !(opt == value);
-}
-
-template <class T>
-constexpr bool operator<(const Optional<T>& opt, const T& value) {
-  return opt != nullopt ? *opt < value : true;
-}
-
-template <class T>
-constexpr bool operator<(const T& value, const Optional<T>& opt) {
-  return opt != nullopt ? value < *opt : false;
-}
-
-template <class T>
-constexpr bool operator<=(const Optional<T>& opt, const T& value) {
-  return !(opt > value);
-}
-
-template <class T>
-constexpr bool operator<=(const T& value, const Optional<T>& opt) {
-  return !(value > opt);
-}
-
-template <class T>
-constexpr bool operator>(const Optional<T>& opt, const T& value) {
-  return value < opt;
-}
-
-template <class T>
-constexpr bool operator>(const T& value, const Optional<T>& opt) {
-  return opt < value;
-}
-
-template <class T>
-constexpr bool operator>=(const Optional<T>& opt, const T& value) {
-  return !(opt < value);
-}
-
-template <class T>
-constexpr bool operator>=(const T& value, const Optional<T>& opt) {
-  return !(value < opt);
-}
-
-template <class T>
-constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) {
-  return Optional<typename std::decay<T>::type>(std::forward<T>(value));
-}
-
-template <class T>
-void swap(Optional<T>& lhs, Optional<T>& rhs) {
-  lhs.swap(rhs);
-}
-
-}  // namespace pdfium
-
-namespace std {
-
-template <class T>
-struct hash<pdfium::Optional<T>> {
-  size_t operator()(const pdfium::Optional<T>& opt) const {
-    return opt == pdfium::nullopt ? 0 : std::hash<T>()(*opt);
-  }
-};
-
-}  // namespace std
-
-template <class T>
-using Optional = pdfium::Optional<T>;
-
-#endif  // THIRD_PARTY_BASE_OPTIONAL_H_
diff --git a/third_party/base/ptr_util.h b/third_party/base/ptr_util.h
deleted file mode 100644
index 36f0920..0000000
--- a/third_party/base/ptr_util.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2016 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_PTR_UTIL_H_
-#define THIRD_PARTY_BASE_PTR_UTIL_H_
-
-#include <memory>
-#include <utility>
-
-namespace pdfium {
-
-// Helper to transfer ownership of a raw pointer to a std::unique_ptr<T>.
-// Note that std::unique_ptr<T> has very different semantics from
-// std::unique_ptr<T[]>: do not use this helper for array allocations.
-template <typename T>
-std::unique_ptr<T> WrapUnique(T* ptr) {
-  return std::unique_ptr<T>(ptr);
-}
-
-namespace internal {
-
-template <typename T>
-struct MakeUniqueResult {
-  using Scalar = std::unique_ptr<T>;
-};
-
-template <typename T>
-struct MakeUniqueResult<T[]> {
-  using Array = std::unique_ptr<T[]>;
-};
-
-template <typename T, size_t N>
-struct MakeUniqueResult<T[N]> {
-  using Invalid = void;
-};
-
-}  // namespace internal
-
-// Helper to construct an object wrapped in a std::unique_ptr. This is an
-// implementation of C++14's std::MakeUnique that can be used in Chrome.
-//
-// MakeUnique<T>(args) should be preferred over WrapUnique(new T(args)): bare
-// calls to `new` should be treated with scrutiny.
-//
-// Usage:
-//   // ptr is a std::unique_ptr<std::string>
-//   auto ptr = MakeUnique<std::string>("hello world!");
-//
-//   // arr is a std::unique_ptr<int[]>
-//   auto arr = MakeUnique<int[]>(5);
-
-// Overload for non-array types. Arguments are forwarded to T's constructor.
-template <typename T, typename... Args>
-typename internal::MakeUniqueResult<T>::Scalar MakeUnique(Args&&... args) {
-  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-}
-
-// Overload for array types of unknown bound, e.g. T[]. The array is allocated
-// with `new T[n]()` and value-initialized: note that this is distinct from
-// `new T[n]`, which default-initializes.
-template <typename T>
-typename internal::MakeUniqueResult<T>::Array MakeUnique(size_t size) {
-  return std::unique_ptr<T>(new typename std::remove_extent<T>::type[size]());
-}
-
-// Overload to reject array types of known bound, e.g. T[n].
-template <typename T, typename... Args>
-typename internal::MakeUniqueResult<T>::Invalid MakeUnique(Args&&... args) =
-    delete;
-
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_PTR_UTIL_H_
diff --git a/third_party/base/span.h b/third_party/base/span.h
deleted file mode 100644
index bb07f43..0000000
--- a/third_party/base/span.h
+++ /dev/null
@@ -1,352 +0,0 @@
-// Copyright 2017 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_SPAN_H_
-#define THIRD_PARTY_BASE_SPAN_H_
-
-#include <stddef.h>
-
-#include <algorithm>
-#include <array>
-#include <iterator>
-#include <type_traits>
-#include <utility>
-
-#include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/logging.h"
-
-namespace pdfium {
-
-template <typename T>
-class span;
-
-namespace internal {
-
-template <typename T>
-struct IsSpanImpl : std::false_type {};
-
-template <typename T>
-struct IsSpanImpl<span<T>> : std::true_type {};
-
-template <typename T>
-using IsSpan = IsSpanImpl<typename std::decay<T>::type>;
-
-template <typename T>
-struct IsStdArrayImpl : std::false_type {};
-
-template <typename T, size_t N>
-struct IsStdArrayImpl<std::array<T, N>> : std::true_type {};
-
-template <typename T>
-using IsStdArray = IsStdArrayImpl<typename std::decay<T>::type>;
-
-template <typename From, typename To>
-using IsLegalSpanConversion = std::is_convertible<From*, To*>;
-
-template <typename Container, typename T>
-using ContainerHasConvertibleData =
-    IsLegalSpanConversion<typename std::remove_pointer<decltype(
-                              std::declval<Container>().data())>::type,
-                          T>;
-template <typename Container>
-using ContainerHasIntegralSize =
-    std::is_integral<decltype(std::declval<Container>().size())>;
-
-template <typename From, typename To>
-using EnableIfLegalSpanConversion =
-    typename std::enable_if<IsLegalSpanConversion<From, To>::value>::type;
-
-// SFINAE check if Container can be converted to a span<T>. Note that the
-// implementation details of this check differ slightly from the requirements in
-// the working group proposal: in particular, the proposal also requires that
-// the container conversion constructor participate in overload resolution only
-// if two additional conditions are true:
-//
-//   1. Container implements operator[].
-//   2. Container::value_type matches remove_const_t<element_type>.
-//
-// The requirements are relaxed slightly here: in particular, not requiring (2)
-// means that an immutable span can be easily constructed from a mutable
-// container.
-template <typename Container, typename T>
-using EnableIfSpanCompatibleContainer =
-    typename std::enable_if<!internal::IsSpan<Container>::value &&
-                            !internal::IsStdArray<Container>::value &&
-                            ContainerHasConvertibleData<Container, T>::value &&
-                            ContainerHasIntegralSize<Container>::value>::type;
-
-template <typename Container, typename T>
-using EnableIfConstSpanCompatibleContainer =
-    typename std::enable_if<std::is_const<T>::value &&
-                            !internal::IsSpan<Container>::value &&
-                            !internal::IsStdArray<Container>::value &&
-                            ContainerHasConvertibleData<Container, T>::value &&
-                            ContainerHasIntegralSize<Container>::value>::type;
-
-}  // namespace internal
-
-// A span is a value type that represents an array of elements of type T. Since
-// it only consists of a pointer to memory with an associated size, it is very
-// light-weight. It is cheap to construct, copy, move and use spans, so that
-// users are encouraged to use it as a pass-by-value parameter. A span does not
-// own the underlying memory, so care must be taken to ensure that a span does
-// not outlive the backing store.
-//
-// span is somewhat analogous to StringPiece, but with arbitrary element types,
-// allowing mutation if T is non-const.
-//
-// span is implicitly convertible from C++ arrays, as well as most [1]
-// container-like types that provide a data() and size() method (such as
-// std::vector<T>). A mutable span<T> can also be implicitly converted to an
-// immutable span<const T>.
-//
-// Consider using a span for functions that take a data pointer and size
-// parameter: it allows the function to still act on an array-like type, while
-// allowing the caller code to be a bit more concise.
-//
-// For read-only data access pass a span<const T>: the caller can supply either
-// a span<const T> or a span<T>, while the callee will have a read-only view.
-// For read-write access a mutable span<T> is required.
-//
-// Without span:
-//   Read-Only:
-//     // std::string HexEncode(const uint8_t* data, size_t size);
-//     std::vector<uint8_t> data_buffer = GenerateData();
-//     std::string r = HexEncode(data_buffer.data(), data_buffer.size());
-//
-//  Mutable:
-//     // ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args...);
-//     char str_buffer[100];
-//     SafeSNPrintf(str_buffer, sizeof(str_buffer), "Pi ~= %lf", 3.14);
-//
-// With span:
-//   Read-Only:
-//     // std::string HexEncode(base::span<const uint8_t> data);
-//     std::vector<uint8_t> data_buffer = GenerateData();
-//     std::string r = HexEncode(data_buffer);
-//
-//  Mutable:
-//     // ssize_t SafeSNPrintf(base::span<char>, const char* fmt, Args...);
-//     char str_buffer[100];
-//     SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14);
-//
-// Spans with "const" and pointers
-// -------------------------------
-//
-// Const and pointers can get confusing. Here are vectors of pointers and their
-// corresponding spans (you can always make the span "more const" too):
-//
-//   const std::vector<int*>        =>  base::span<int* const>
-//   std::vector<const int*>        =>  base::span<const int*>
-//   const std::vector<const int*>  =>  base::span<const int* const>
-//
-// Differences from the working group proposal
-// -------------------------------------------
-//
-// https://wg21.link/P0122 is the latest working group proposal, Chromium
-// currently implements R6. The biggest difference is span does not support a
-// static extent template parameter. Other differences are documented in
-// subsections below.
-//
-// Differences from [views.constants]:
-// - no dynamic_extent constant
-//
-// Differences in constants and types:
-// - no element_type type alias
-// - no index_type type alias
-// - no different_type type alias
-// - no extent constant
-//
-// Differences from [span.cons]:
-// - no constructor from a pointer range
-// - no constructor from std::array
-//
-// Differences from [span.sub]:
-// - no templated first()
-// - no templated last()
-// - no templated subspan()
-// - using size_t instead of ptrdiff_t for indexing
-//
-// Differences from [span.obs]:
-// - using size_t instead of ptrdiff_t to represent size()
-//
-// Differences from [span.elem]:
-// - no operator ()()
-// - using size_t instead of ptrdiff_t for indexing
-
-// [span], class template span
-template <typename T>
-class span {
- public:
-  using value_type = typename std::remove_cv<T>::type;
-  using pointer = T*;
-  using reference = T&;
-  using iterator = T*;
-  using const_iterator = const T*;
-  using reverse_iterator = std::reverse_iterator<iterator>;
-  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
-
-  // [span.cons], span constructors, copy, assignment, and destructor
-  constexpr span() noexcept : data_(nullptr), size_(0) {}
-  constexpr span(T* data, size_t size) noexcept : data_(data), size_(size) {}
-
-  // TODO(dcheng): Implement construction from a |begin| and |end| pointer.
-  template <size_t N>
-  constexpr span(T (&array)[N]) noexcept : span(array, N) {}
-  // TODO(dcheng): Implement construction from std::array.
-  // Conversion from a container that provides |T* data()| and |integral_type
-  // size()|.
-  template <typename Container,
-            typename = internal::EnableIfSpanCompatibleContainer<Container, T>>
-  constexpr span(Container& container)
-      : span(container.data(), container.size()) {}
-  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>>
-  constexpr span(const span<U>& other) : span(other.data(), other.size()) {}
-  span& operator=(const span& other) noexcept = default;
-  ~span() noexcept {
-    if (!size_) {
-      // Empty spans might point to byte N+1 of a N-byte object, legal for
-      // C pointers but not UnownedPtrs.
-      data_.ReleaseBadPointer();
-    }
-  }
-
-  // [span.sub], span subviews
-  const span first(size_t count) const {
-    CHECK(count <= size_);
-    return span(data_.Get(), count);
-  }
-
-  const span last(size_t count) const {
-    CHECK(count <= size_);
-    return span(data_.Get() + (size_ - count), count);
-  }
-
-  const span subspan(size_t pos, size_t count = -1) const {
-    const auto npos = static_cast<size_t>(-1);
-    CHECK(pos <= size_);
-    CHECK(count == npos || count <= size_ - pos);
-    return span(data_.Get() + pos, count == npos ? size_ - pos : count);
-  }
-
-  // [span.obs], span observers
-  constexpr size_t size() const noexcept { return size_; }
-  constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); }
-  constexpr bool empty() const noexcept { return size_ == 0; }
-
-  // [span.elem], span element access
-  T& operator[](size_t index) const noexcept {
-    CHECK(index < size_);
-    return data_.Get()[index];
-  }
-  constexpr T* data() const noexcept { return data_.Get(); }
-
-  // [span.iter], span iterator support
-  constexpr iterator begin() const noexcept { return data_.Get(); }
-  constexpr iterator end() const noexcept { return data_.Get() + size_; }
-
-  constexpr const_iterator cbegin() const noexcept { return begin(); }
-  constexpr const_iterator cend() const noexcept { return end(); }
-
-  constexpr reverse_iterator rbegin() const noexcept {
-    return reverse_iterator(end());
-  }
-  constexpr reverse_iterator rend() const noexcept {
-    return reverse_iterator(begin());
-  }
-
-  constexpr const_reverse_iterator crbegin() const noexcept {
-    return const_reverse_iterator(cend());
-  }
-  constexpr const_reverse_iterator crend() const noexcept {
-    return const_reverse_iterator(cbegin());
-  }
-
- private:
-  UnownedPtr<T> data_;
-  size_t size_;
-};
-
-// [span.comparison], span comparison operators
-// Relational operators. Equality is a element-wise comparison.
-template <typename T>
-constexpr bool operator==(span<T> lhs, span<T> rhs) noexcept {
-  return lhs.size() == rhs.size() &&
-         std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin());
-}
-
-template <typename T>
-constexpr bool operator!=(span<T> lhs, span<T> rhs) noexcept {
-  return !(lhs == rhs);
-}
-
-template <typename T>
-constexpr bool operator<(span<T> lhs, span<T> rhs) noexcept {
-  return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(),
-                                      rhs.cend());
-}
-
-template <typename T>
-constexpr bool operator<=(span<T> lhs, span<T> rhs) noexcept {
-  return !(rhs < lhs);
-}
-
-template <typename T>
-constexpr bool operator>(span<T> lhs, span<T> rhs) noexcept {
-  return rhs < lhs;
-}
-
-template <typename T>
-constexpr bool operator>=(span<T> lhs, span<T> rhs) noexcept {
-  return !(lhs < rhs);
-}
-
-// [span.objectrep], views of object representation
-template <typename T>
-span<const uint8_t> as_bytes(span<T> s) noexcept {
-  return {reinterpret_cast<const uint8_t*>(s.data()), s.size_bytes()};
-}
-
-template <typename T,
-          typename U = typename std::enable_if<!std::is_const<T>::value>::type>
-span<uint8_t> as_writable_bytes(span<T> s) noexcept {
-  return {reinterpret_cast<uint8_t*>(s.data()), s.size_bytes()};
-}
-
-// Type-deducing helpers for constructing a span.
-template <typename T>
-constexpr span<T> make_span(T* data, size_t size) noexcept {
-  return span<T>(data, size);
-}
-
-template <typename T, size_t N>
-constexpr span<T> make_span(T (&array)[N]) noexcept {
-  return span<T>(array);
-}
-
-template <typename Container,
-          typename T = typename Container::value_type,
-          typename = internal::EnableIfSpanCompatibleContainer<Container, T>>
-constexpr span<T> make_span(Container& container) {
-  return span<T>(container);
-}
-
-template <
-    typename Container,
-    typename T = typename std::add_const<typename Container::value_type>::type,
-    typename = internal::EnableIfConstSpanCompatibleContainer<Container, T>>
-constexpr span<T> make_span(const Container& container) {
-  return span<T>(container);
-}
-
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_SPAN_H_
diff --git a/third_party/base/stl_util.h b/third_party/base/stl_util.h
deleted file mode 100644
index 925c96a..0000000
--- a/third_party/base/stl_util.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2015 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_STL_UTIL_H_
-#define THIRD_PARTY_BASE_STL_UTIL_H_
-
-#include <algorithm>
-#include <iterator>
-#include <memory>
-#include <set>
-#include <vector>
-
-#include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/numerics/safe_math.h"
-
-namespace pdfium {
-
-// C++11 implementation of C++17's std::size():
-// http://en.cppreference.com/w/cpp/iterator/size
-template <typename Container>
-constexpr auto size(const Container& c) -> decltype(c.size()) {
-  return c.size();
-}
-
-template <typename T, size_t N>
-constexpr size_t size(const T (&array)[N]) noexcept {
-  return N;
-}
-
-// Test to see if a set, map, hash_set or hash_map contains a particular key.
-// Returns true if the key is in the collection.
-template <typename Collection, typename Key>
-bool ContainsKey(const Collection& collection, const Key& key) {
-  return collection.find(key) != collection.end();
-}
-
-// Test to see if a collection like a vector contains a particular value.
-// Returns true if the value is in the collection.
-template <typename Collection, typename Value>
-bool ContainsValue(const Collection& collection, const Value& value) {
-  return std::find(std::begin(collection), std::end(collection), value) !=
-         std::end(collection);
-}
-
-// Means of generating a key for searching STL collections of std::unique_ptr
-// that avoids the side effect of deleting the pointer.
-template <class T>
-class FakeUniquePtr : public std::unique_ptr<T> {
- public:
-  using std::unique_ptr<T>::unique_ptr;
-  ~FakeUniquePtr() { std::unique_ptr<T>::release(); }
-};
-
-// Convenience routine for "int-fected" code, so that the stl collection
-// size_t size() method return values will be checked.
-template <typename ResultType, typename Collection>
-ResultType CollectionSize(const Collection& collection) {
-  return pdfium::base::checked_cast<ResultType>(collection.size());
-}
-
-// Convenience routine for "int-fected" code, to handle signed indicies. The
-// compiler can deduce the type, making this more convenient than the above.
-template <typename IndexType, typename Collection>
-bool IndexInBounds(const Collection& collection, IndexType index) {
-  return index >= 0 && index < CollectionSize<IndexType>(collection);
-}
-
-// Track the addition of an object to a set, removing it automatically when
-// the ScopedSetInsertion goes out of scope.
-template <typename T>
-class ScopedSetInsertion {
- public:
-  ScopedSetInsertion(std::set<T>* org_set, T elem)
-      : m_Set(org_set), m_Entry(elem) {
-    m_Set->insert(m_Entry);
-  }
-  ~ScopedSetInsertion() { m_Set->erase(m_Entry); }
-
- private:
-  std::set<T>* const m_Set;
-  const T m_Entry;
-};
-
-// std::clamp(), some day.
-template <class T>
-constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
-  return std::min(std::max(v, lo), hi);
-}
-
-// Safely allocate a 1-dim vector big enough for |w| by |h| or die.
-template <typename T, typename A = std::allocator<T>>
-std::vector<T, A> Vector2D(size_t w, size_t h) {
-  pdfium::base::CheckedNumeric<size_t> safe_size = w;
-  safe_size *= h;
-  return std::vector<T, A>(safe_size.ValueOrDie());
-}
-
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_STL_UTIL_H_
diff --git a/third_party/base/sys_byteorder.h b/third_party/base/sys_byteorder.h
index 8b252f1..490e064 100644
--- a/third_party/base/sys_byteorder.h
+++ b/third_party/base/sys_byteorder.h
@@ -14,7 +14,7 @@
 #include <stdint.h>
 
 #include "build/build_config.h"
-#include "third_party/base/logging.h"
+#include "third_party/base/notreached.h"
 
 #if defined(COMPILER_MSVC)
 #include <stdlib.h>
diff --git a/third_party/base/template_util.h b/third_party/base/template_util.h
index 6f31f47..f0c2564 100644
--- a/third_party/base/template_util.h
+++ b/third_party/base/template_util.h
@@ -10,33 +10,10 @@
 #include <iterator>
 #include <type_traits>
 #include <utility>
-#include <vector>
 
 #include "build/build_config.h"
 
-// Some versions of libstdc++ have partial support for type_traits, but misses
-// a smaller subset while removing some of the older non-standard stuff. Assume
-// that all versions below 5.0 fall in this category, along with one 5.0
-// experimental release. Test for this by consulting compiler major version,
-// the only reliable option available, so theoretically this could fail should
-// you attempt to mix an earlier version of libstdc++ with >= GCC5. But
-// that's unlikely to work out, especially as GCC5 changed ABI.
-#define CR_GLIBCXX_5_0_0 20150123
-#if (defined(__GNUC__) && __GNUC__ < 5) || \
-    (defined(__GLIBCXX__) && __GLIBCXX__ == CR_GLIBCXX_5_0_0)
-#define CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX
-#endif
-
-// This hacks around using gcc with libc++ which has some incompatibilies.
-// - is_trivially_* doesn't work: https://llvm.org/bugs/show_bug.cgi?id=27538
-// TODO(danakj): Remove this when android builders are all using a newer version
-// of gcc, or the android ndk is updated to a newer libc++ that works with older
-// gcc versions.
-#if !defined(__clang__) && defined(_LIBCPP_VERSION)
-#define CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX
-#endif
-
-namespace base {
+namespace pdfium {
 
 template <class T> struct is_non_const_reference : std::false_type {};
 template <class T> struct is_non_const_reference<T&> : std::true_type {};
@@ -44,25 +21,6 @@
 
 namespace internal {
 
-// Implementation detail of base::void_t below.
-template <typename...>
-struct make_void {
-  using type = void;
-};
-
-}  // namespace internal
-
-// base::void_t is an implementation of std::void_t from C++17.
-//
-// We use |base::internal::make_void| as a helper struct to avoid a C++14
-// defect:
-//   http://en.cppreference.com/w/cpp/types/void_t
-//   http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1558
-template <typename... Ts>
-using void_t = typename ::base::internal::make_void<Ts...>::type;
-
-namespace internal {
-
 // Uses expression SFINAE to detect whether using operator<< would work.
 template <typename T, typename = void>
 struct SupportsOstreamOperator : std::false_type {};
@@ -79,75 +37,13 @@
 struct is_iterator : std::false_type {};
 
 template <typename T>
-struct is_iterator<T,
-                   void_t<typename std::iterator_traits<T>::iterator_category>>
+struct is_iterator<
+    T,
+    std::void_t<typename std::iterator_traits<T>::iterator_category>>
     : std::true_type {};
 
 }  // namespace internal
 
-// is_trivially_copyable is especially hard to get right.
-// - Older versions of libstdc++ will fail to have it like they do for other
-//   type traits. This has become a subset of the second point, but used to be
-//   handled independently.
-// - An experimental release of gcc includes most of type_traits but misses
-//   is_trivially_copyable, so we still have to avoid using libstdc++ in this
-//   case, which is covered by CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX.
-// - When compiling libc++ from before r239653, with a gcc compiler, the
-//   std::is_trivially_copyable can fail. So we need to work around that by not
-//   using the one in libc++ in this case. This is covered by the
-//   CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX define, and is discussed in
-//   https://llvm.org/bugs/show_bug.cgi?id=27538#c1 where they point out that
-//   in libc++'s commit r239653 this is fixed by libc++ checking for gcc 5.1.
-// - In both of the above cases we are using the gcc compiler. When defining
-//   this ourselves on compiler intrinsics, the __is_trivially_copyable()
-//   intrinsic is not available on gcc before version 5.1 (see the discussion in
-//   https://llvm.org/bugs/show_bug.cgi?id=27538#c1 again), so we must check for
-//   that version.
-// - When __is_trivially_copyable() is not available because we are on gcc older
-//   than 5.1, we need to fall back to something, so we use __has_trivial_copy()
-//   instead based on what was done one-off in bit_cast() previously.
-
-// TODO(crbug.com/554293): Remove this when all platforms have this in the std
-// namespace and it works with gcc as needed.
-#if defined(CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX) || \
-    defined(CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX)
-template <typename T>
-struct is_trivially_copyable {
-// TODO(danakj): Remove this when android builders are all using a newer version
-// of gcc, or the android ndk is updated to a newer libc++ that does this for
-// us.
-#if _GNUC_VER >= 501
-  static constexpr bool value = __is_trivially_copyable(T);
-#else
-  static constexpr bool value =
-      __has_trivial_copy(T) && __has_trivial_destructor(T);
-#endif
-};
-#else
-template <class T>
-using is_trivially_copyable = std::is_trivially_copyable<T>;
-#endif
-
-#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 7
-// Workaround for g++7 and earlier family.
-// Due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80654, without this
-// Optional<std::vector<T>> where T is non-copyable causes a compile error.
-// As we know it is not trivially copy constructible, explicitly declare so.
-template <typename T>
-struct is_trivially_copy_constructible
-    : std::is_trivially_copy_constructible<T> {};
-
-template <typename... T>
-struct is_trivially_copy_constructible<std::vector<T...>> : std::false_type {};
-#else
-// Otherwise use std::is_trivially_copy_constructible as is.
-template <typename T>
-using is_trivially_copy_constructible = std::is_trivially_copy_constructible<T>;
-#endif
-
-}  // namespace base
-
-#undef CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX
-#undef CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX
+}  // namespace pdfium
 
 #endif  // THIRD_PARTY_BASE_TEMPLATE_UTIL_H_
diff --git a/third_party/base/win/scoped_select_object.h b/third_party/base/win/scoped_select_object.h
new file mode 100644
index 0000000..89a5f1b
--- /dev/null
+++ b/third_party/base/win/scoped_select_object.h
@@ -0,0 +1,45 @@
+// Copyright 2011 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_WIN_SCOPED_SELECT_OBJECT_H_
+#define THIRD_PARTY_BASE_WIN_SCOPED_SELECT_OBJECT_H_
+
+#include <windows.h>
+
+#include "third_party/base/check.h"
+
+namespace pdfium {
+namespace base {
+namespace win {
+
+// Helper class for deselecting object from DC.
+class ScopedSelectObject {
+ public:
+  ScopedSelectObject(HDC hdc, HGDIOBJ object)
+      : hdc_(hdc), oldobj_(SelectObject(hdc, object)) {
+    DCHECK(hdc_);
+    DCHECK(object);
+    DCHECK(oldobj_);
+    DCHECK(oldobj_ != HGDI_ERROR);
+  }
+
+  ScopedSelectObject(const ScopedSelectObject&) = delete;
+  ScopedSelectObject& operator=(const ScopedSelectObject&) = delete;
+
+  ~ScopedSelectObject() {
+    [[maybe_unused]] HGDIOBJ object = SelectObject(hdc_, oldobj_);
+    DCHECK((GetObjectType(oldobj_) != OBJ_REGION && object) ||
+           (GetObjectType(oldobj_) == OBJ_REGION && object != HGDI_ERROR));
+  }
+
+ private:
+  const HDC hdc_;
+  const HGDIOBJ oldobj_;
+};
+
+}  // namespace win
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_WIN_SCOPED_SELECT_OBJECT_H_
diff --git a/third_party/bigint/BigInteger.cc b/third_party/bigint/BigInteger.cc
index 476f3e6..3588495 100644
--- a/third_party/bigint/BigInteger.cc
+++ b/third_party/bigint/BigInteger.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/third_party/bigint/BigInteger.hh b/third_party/bigint/BigInteger.hh
index b5d3518..5ffd021 100644
--- a/third_party/bigint/BigInteger.hh
+++ b/third_party/bigint/BigInteger.hh
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/third_party/bigint/BigIntegerLibrary.hh b/third_party/bigint/BigIntegerLibrary.hh
index 2027804..8984fe3 100644
--- a/third_party/bigint/BigIntegerLibrary.hh
+++ b/third_party/bigint/BigIntegerLibrary.hh
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/third_party/bigint/BigIntegerUtils.cc b/third_party/bigint/BigIntegerUtils.cc
index f48334d..11039d2 100644
--- a/third_party/bigint/BigIntegerUtils.cc
+++ b/third_party/bigint/BigIntegerUtils.cc
@@ -1,9 +1,11 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 // Original code by Matt McCutchen, see the LICENSE file.
 
+#include <ostream>
+
 #include "BigIntegerUtils.hh"
 #include "BigUnsignedInABase.hh"
 
diff --git a/third_party/bigint/BigIntegerUtils.hh b/third_party/bigint/BigIntegerUtils.hh
index 999cdd2..44b3679 100644
--- a/third_party/bigint/BigIntegerUtils.hh
+++ b/third_party/bigint/BigIntegerUtils.hh
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 
 #include "BigInteger.hh"
 #include <string>
-#include <ostream>
+#include <iosfwd>
 
 /* This file provides:
  * - Convenient std::string <-> BigUnsigned/BigInteger conversion routines
diff --git a/third_party/bigint/BigUnsigned.cc b/third_party/bigint/BigUnsigned.cc
index 0713632..d3b4d07 100644
--- a/third_party/bigint/BigUnsigned.cc
+++ b/third_party/bigint/BigUnsigned.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/third_party/bigint/BigUnsigned.hh b/third_party/bigint/BigUnsigned.hh
index 56da651..31b11a9 100644
--- a/third_party/bigint/BigUnsigned.hh
+++ b/third_party/bigint/BigUnsigned.hh
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/third_party/bigint/BigUnsignedInABase.cc b/third_party/bigint/BigUnsignedInABase.cc
index 72fe256..150549b 100644
--- a/third_party/bigint/BigUnsignedInABase.cc
+++ b/third_party/bigint/BigUnsignedInABase.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/third_party/bigint/BigUnsignedInABase.hh b/third_party/bigint/BigUnsignedInABase.hh
index fb293ba..2ee8532 100644
--- a/third_party/bigint/BigUnsignedInABase.hh
+++ b/third_party/bigint/BigUnsignedInABase.hh
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/third_party/bigint/NumberlikeArray.hh b/third_party/bigint/NumberlikeArray.hh
index df58dae..3402711 100644
--- a/third_party/bigint/NumberlikeArray.hh
+++ b/third_party/bigint/NumberlikeArray.hh
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/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/eu-strip/README.pdfium b/third_party/eu-strip/README.pdfium
deleted file mode 100644
index e84974d..0000000
--- a/third_party/eu-strip/README.pdfium
+++ /dev/null
@@ -1,24 +0,0 @@
-Name: eu-strip
-URL: https://sourceware.org/elfutils/
-Version: 0.158
-Security Critical: no
-License: LGPL 3
-License File: NOT_SHIPPED
-
-Description:
-
-Patched eu-strip from elfutils.
-
-Build instructions (on Trusty; note that this will build the
-Ubuntu-patched version of elfutils):
-$ mkdir elfutils
-$ cd elfutils
-$ apt-get source elfutils
-$ cd elfutils-0.158
-[ Edit libelf/elf_end.c and remove the free() on line 164. ]
-$ ./configure
-$ make
-$ gcc -std=gnu99 -Wall -Wshadow -Wunused -Wextra -fgnu89-inline
-  -Wformat=2 -Werror -g -O2 -Wl,-rpath-link,libelf:libdw -o eu-strip
-  src/strip.o libebl/libebl.a libelf/libelf.a lib/libeu.a -ldl
-$ eu-strip ./eu-strip  # Keep the binary small, please.
diff --git a/third_party/eu-strip/bin/eu-strip b/third_party/eu-strip/bin/eu-strip
deleted file mode 100755
index 994e226..0000000
--- a/third_party/eu-strip/bin/eu-strip
+++ /dev/null
Binary files differ
diff --git a/third_party/freetype/0000-include.patch b/third_party/freetype/0000-include.patch
index d6c8374..b4c8736 100644
--- a/third_party/freetype/0000-include.patch
+++ b/third_party/freetype/0000-include.patch
@@ -21,10 +21,10 @@
  FT_USE_MODULE( FT_Module_Class, psaux_module_class )
  FT_USE_MODULE( FT_Module_Class, psnames_module_class )
  FT_USE_MODULE( FT_Module_Class, pshinter_module_class )
-@@ -27,6 +27,6 @@
+@@ -27,6 +27,4 @@
  FT_USE_MODULE( FT_Renderer_Class, ft_smooth_renderer_class )
- FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcd_renderer_class )
- FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcdv_renderer_class )
+-FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcd_renderer_class )
+-FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcdv_renderer_class )
 -FT_USE_MODULE( FT_Driver_ClassRec, bdf_driver_class )
 +//FT_USE_MODULE( FT_Driver_ClassRec, bdf_driver_class )
  
diff --git a/third_party/freetype/README.pdfium b/third_party/freetype/README.pdfium
index 56c5aa5..22fdfc9 100644
--- a/third_party/freetype/README.pdfium
+++ b/third_party/freetype/README.pdfium
@@ -1,8 +1,10 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-10-1-97
-Revision: e5038be70414cf66da6c4d5ce4e30375884c30d8
+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
 
@@ -13,7 +15,9 @@
 
 include/pstables.h: A copy of freetype/src/psnames/pstables.h.  This file is not
 part of the public Freetype API, but pdfium needs it.  Since it won't be
-avilable when building with the system Freetype, we provide it as a convenience.
+available when building with the system Freetype, we provide it as a
+convenience. See https://gitlab.freedesktop.org/freetype/freetype/-/issues/731
+for the request to make this a public Freetype API.
 
 0000-include.patch: Modifications to configuration header files.
 
diff --git a/third_party/freetype/include/freetype-custom-config/ftmodule.h b/third_party/freetype/include/freetype-custom-config/ftmodule.h
index 0d31ce6..71f36b0 100644
--- a/third_party/freetype/include/freetype-custom-config/ftmodule.h
+++ b/third_party/freetype/include/freetype-custom-config/ftmodule.h
@@ -25,8 +25,6 @@
 FT_USE_MODULE( FT_Renderer_Class, ft_raster1_renderer_class )
 FT_USE_MODULE( FT_Module_Class, sfnt_module_class )
 FT_USE_MODULE( FT_Renderer_Class, ft_smooth_renderer_class )
-FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcd_renderer_class )
-FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcdv_renderer_class )
 //FT_USE_MODULE( FT_Driver_ClassRec, bdf_driver_class )
 
 /* EOF */
diff --git a/third_party/freetype/include/freetype-custom-config/ftoption.h b/third_party/freetype/include/freetype-custom-config/ftoption.h
index d3f43a0..73abe17 100644
--- a/third_party/freetype/include/freetype-custom-config/ftoption.h
+++ b/third_party/freetype/include/freetype-custom-config/ftoption.h
@@ -700,6 +700,24 @@
 #undef TT_CONFIG_OPTION_BDF
 
 
+  /**************************************************************************
+   *
+   * Define `TT_CONFIG_OPTION_NO_BORING_EXPANSION` if you want to exclude
+   * support for 'boring' OpenType specification expansions.
+   *
+   *   https://github.com/harfbuzz/boring-expansion-spec
+   *
+   * Right now, the following features are covered:
+   *
+   *   - 'avar' version 2.0
+   *
+   * Most likely, this is a temporary configuration option to be removed in
+   * the near future, since it is assumed that eventually those features are
+   * added to the OpenType standard.
+   */
+#define TT_CONFIG_OPTION_NO_BORING_EXPANSION
+
+
   /*************************************************************************/
   /*                                                                       */
   /* Option TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES controls the maximum     */
diff --git a/third_party/freetype/include/pstables.h b/third_party/freetype/include/pstables.h
index c215f16..7f92cce 100644
--- a/third_party/freetype/include/pstables.h
+++ b/third_party/freetype/include/pstables.h
@@ -4,7 +4,7 @@
  *
  *   PostScript glyph names.
  *
- * Copyright (C) 2005-2020 by
+ * Copyright (C) 2005-2023 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/third_party/freetype/roll-freetype.sh b/third_party/freetype/roll-freetype.sh
index fd2c293..f842036 100755
--- a/third_party/freetype/roll-freetype.sh
+++ b/third_party/freetype/roll-freetype.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 #
-# Copyright 2017 The PDFium Authors. All rights reserved.
+# Copyright 2017 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -10,18 +10,21 @@
 
 REVIEWERS=`paste -s -d, third_party/freetype/OWNERS`
 roll-dep -r "${REVIEWERS}" "$@" third_party/freetype/src/
-FTVERSION=`git -C third_party/freetype/src/ describe --long`
-FTCOMMIT=`git -C third_party/freetype/src/ rev-parse HEAD`
+FT_VERSION=`git -C third_party/freetype/src/ describe --long`
+FT_COMMIT=`git -C third_party/freetype/src/ rev-parse HEAD`
+FT_CPE_VERSION=$(echo ${FT_VERSION} | sed -r -e's/^VER-([0-9]+)-([0-9]+)-([0-9]+)-[0-9]+-g[0-9a-f]+$/\1.\2.\3/')
 
 # Make sure our copy of pstables.h matches the one in freetype/src.
 # May need to --bypass-hooks to prevent formatting of this file.
 cp third_party/freetype/src/src/psnames/pstables.h \
   third_party/freetype/include/pstables.h
 
-sed -i "s/^Version: .*\$/Version: ${FTVERSION%-*}/" \
-  third_party/freetype/README.pdfium
-sed -i "s/^Revision: .*\$/Revision: ${FTCOMMIT}/" \
-  third_party/freetype/README.pdfium
+sed \
+  -e "s/^Version: .*\$/Version: ${FT_VERSION%-*}/" \
+  -e "s/^Revision: .*\$/Revision: ${FT_COMMIT}/" \
+  -e "s@^CPEPrefix: cpe:/a:freetype:freetype:.*\$@CPEPrefix: cpe:/a:freetype:freetype:${FT_CPE_VERSION}@" \
+  third_party/freetype/README.pdfium > third_party/freetype/README.pdfium.new
+mv third_party/freetype/README.pdfium.new third_party/freetype/README.pdfium
 
 git add third_party/freetype/README.pdfium
 git add third_party/freetype/include/pstables.h
diff --git a/third_party/fuchsia-sdk/DIR_METADATA b/third_party/fuchsia-sdk/DIR_METADATA
new file mode 100644
index 0000000..937c0c9
--- /dev/null
+++ b/third_party/fuchsia-sdk/DIR_METADATA
@@ -0,0 +1,2 @@
+team_email: "fuchsia-dev@chromium.org"
+os: FUCHSIA
diff --git a/third_party/fuchsia-sdk/README.chromium b/third_party/fuchsia-sdk/README.chromium
new file mode 100644
index 0000000..4bdbe99
--- /dev/null
+++ b/third_party/fuchsia-sdk/README.chromium
@@ -0,0 +1,12 @@
+Name: Fuchsia SDK
+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
+
+Description:
+This directory contains the current Fuchsia SDK. The SDK contains headers,
+libraries and tools that are needed to build Chromium for Fuchsia. It also
+contains boot images that are used to run tests on Fuchsia in emulation.
diff --git a/third_party/googletest/BUILD.gn b/third_party/googletest/BUILD.gn
index dbfc334..fb7b182 100644
--- a/third_party/googletest/BUILD.gn
+++ b/third_party/googletest/BUILD.gn
@@ -1,9 +1,13 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build_overrides/build.gni")
+
 config("gtest_config") {
-  visibility = [ ":*" ]  # gmock also shares this config.
+  visibility = [
+    ":*",  # gmock also shares this config.
+  ]
 
   defines = [
     # Chromium always links googletest statically, so no API qualifier is
@@ -23,11 +27,19 @@
   ]
 
   # Gtest headers need to be able to find themselves.
-  include_dirs = [ "src/googletest/include" ]
+  include_dirs = [
+    "custom",
+    "src/googletest/include",
+  ]
 
   if (is_win) {
     cflags = [ "/wd4800" ]  # Unused variable warning.
   }
+
+  if (gtest_enable_absl_printers) {
+    configs = [ "//third_party/abseil-cpp:absl_include_config" ]
+    defines += [ "GTEST_HAS_ABSL=1" ]
+  }
 }
 
 config("gmock_config") {
@@ -36,6 +48,12 @@
     "custom",
     "src/googlemock/include",
   ]
+
+  if (is_clang) {
+    # TODO(tikuta): remove this when the issue is fixed.
+    # https://github.com/google/googletest/issues/533
+    cflags = [ "-Wno-inconsistent-missing-override" ]
+  }
 }
 
 # Do NOT depend on this directly. Use //testing/gtest instead.
@@ -43,6 +61,17 @@
 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",
+
+    # TODO(crbug.com/1009553): Remove this wrapper and custom temp dir
+    # after plumbing a workable temporary path into googletest on Android.
+    "custom/gtest/internal/custom/gtest_port_wrapper.cc",
+    "custom/gtest/internal/custom/pdfium_custom_temp_dir.cc",
+    "custom/gtest/internal/custom/pdfium_custom_temp_dir.h",
+    "src/googletest/include/gtest/gtest-assertion-result.h",
     "src/googletest/include/gtest/gtest-death-test.h",
     "src/googletest/include/gtest/gtest-matchers.h",
     "src/googletest/include/gtest/gtest-message.h",
@@ -53,23 +82,31 @@
     "src/googletest/include/gtest/gtest-typed-test.h",
     "src/googletest/include/gtest/gtest.h",
     "src/googletest/include/gtest/gtest_pred_impl.h",
+    "src/googletest/include/gtest/gtest_prod.h",
+
+    #"src/googletest/include/gtest/internal/custom/gtest.h",  # Superseded.
+    "src/googletest/include/gtest/internal/custom/gtest-port.h",
+    "src/googletest/include/gtest/internal/custom/gtest-printers.h",
     "src/googletest/include/gtest/internal/gtest-death-test-internal.h",
     "src/googletest/include/gtest/internal/gtest-filepath.h",
     "src/googletest/include/gtest/internal/gtest-internal.h",
-    "src/googletest/include/gtest/internal/gtest-linked_ptr.h",
-    "src/googletest/include/gtest/internal/gtest-param-util-generated.h",
     "src/googletest/include/gtest/internal/gtest-param-util.h",
+    "src/googletest/include/gtest/internal/gtest-port-arch.h",
     "src/googletest/include/gtest/internal/gtest-port.h",
     "src/googletest/include/gtest/internal/gtest-string.h",
-    "src/googletest/include/gtest/internal/gtest-tuple.h",
     "src/googletest/include/gtest/internal/gtest-type-util.h",
 
     #"src/googletest/src/gtest-all.cc",  # Not needed by our build.
+    "src/googletest/src/gtest-assertion-result.cc",
     "src/googletest/src/gtest-death-test.cc",
     "src/googletest/src/gtest-filepath.cc",
     "src/googletest/src/gtest-internal-inl.h",
     "src/googletest/src/gtest-matchers.cc",
-    "src/googletest/src/gtest-port.cc",
+
+    # gtest_port_wrapper.cc is used instead of gtest-port.cc.
+    # TODO(crbug.com/1009553): Re-enable this file after plumbing a workable
+    #                          temporary path into googletest on Android.
+    #"src/googletest/src/gtest-port.cc",
     "src/googletest/src/gtest-printers.cc",
     "src/googletest/src/gtest-test-part.cc",
     "src/googletest/src/gtest-typed-test.cc",
@@ -79,10 +116,29 @@
   # Some files include "src/gtest-internal-inl.h".
   include_dirs = [ "src/googletest" ]
 
-  all_dependent_configs = [ ":gtest_config" ]
+  public_configs = [ ":gtest_config" ]
 
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
+
+  defines = [ "GTEST_DISABLE_PRINT_STACK_TRACE" ]
+  sources -= [
+    "custom/gtest/internal/custom/stack_trace_getter.cc",
+    "custom/gtest/internal/custom/stack_trace_getter.h",
+  ]
+
+  deps = []
+
+  if (is_fuchsia) {
+    deps += [
+      "//third_party/fuchsia-sdk/sdk/pkg/fdio",
+      "//third_party/fuchsia-sdk/sdk/pkg/zx",
+    ]
+  }
+
+  if (gtest_enable_absl_printers) {
+    deps += [ "//third_party/abseil-cpp:absl" ]
+  }
 }
 
 # Do NOT depend on this directly. Use //testing/gtest:gtest_main instead.
@@ -100,19 +156,19 @@
   sources = [
     "src/googlemock/include/gmock/gmock-actions.h",
     "src/googlemock/include/gmock/gmock-cardinalities.h",
-    "src/googlemock/include/gmock/gmock-generated-actions.h",
-    "src/googlemock/include/gmock/gmock-generated-function-mockers.h",
-    "src/googlemock/include/gmock/gmock-generated-matchers.h",
-    "src/googlemock/include/gmock/gmock-generated-nice-strict.h",
+    "src/googlemock/include/gmock/gmock-function-mocker.h",
     "src/googlemock/include/gmock/gmock-matchers.h",
+    "src/googlemock/include/gmock/gmock-more-matchers.h",
+    "src/googlemock/include/gmock/gmock-nice-strict.h",
     "src/googlemock/include/gmock/gmock-spec-builders.h",
     "src/googlemock/include/gmock/gmock.h",
-    "src/googlemock/include/gmock/internal/gmock-generated-internal-utils.h",
+
+    #"src/googlemock/include/gmock/internal/custom/gmock-port.h",  # Superseded.
+    "src/googlemock/include/gmock/internal/custom/gmock-generated-actions.h",
+    "src/googlemock/include/gmock/internal/custom/gmock-matchers.h",
     "src/googlemock/include/gmock/internal/gmock-internal-utils.h",
     "src/googlemock/include/gmock/internal/gmock-port.h",
-
-    # gmock helpers.
-    "custom/gmock/internal/custom/gmock-port.h",
+    "src/googlemock/include/gmock/internal/gmock-pp.h",
 
     #"src/googlemock/src/gmock-all.cc",  # Not needed by our build.
     "src/googlemock/src/gmock-cardinalities.cc",
@@ -122,10 +178,9 @@
     "src/googlemock/src/gmock.cc",
   ]
 
-  public_configs = [
-    ":gmock_config",
-    ":gtest_config",
-  ]
+  public_deps = [ ":gtest" ]
+
+  public_configs = [ ":gmock_config" ]
 }
 
 # Do NOT depend on this directly. Use //testing/gmock:gmock_main instead.
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/googletest/custom/gtest/internal/custom/gtest.h b/third_party/googletest/custom/gtest/internal/custom/gtest.h
new file mode 100644
index 0000000..bdccb34
--- /dev/null
+++ b/third_party/googletest/custom/gtest/internal/custom/gtest.h
@@ -0,0 +1,23 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_GOOGLETEST_CUSTOM_GTEST_INTERNAL_CUSTOM_GTEST_H_
+#define THIRD_PARTY_GOOGLETEST_CUSTOM_GTEST_INTERNAL_CUSTOM_GTEST_H_
+
+#include "build/build_config.h"
+#include "third_party/googletest/custom/gtest/internal/custom/pdfium_custom_temp_dir.h"
+
+#if !defined(GTEST_DISABLE_PRINT_STACK_TRACE)
+#include "third_party/googletest/custom/gtest/internal/custom/stack_trace_getter.h"
+
+// Tell Google Test to use a stack trace getter based on Chromium's
+// base::debug::StackTrace.
+#define GTEST_OS_STACK_TRACE_GETTER_ StackTraceGetter
+#endif  // defined(GTEST_DISABLE_PRINT_STACK_TRACE)
+
+// TODO(crbug.com/1009553): Remove once googletest android temporary path is
+// fixed.
+#define GTEST_CUSTOM_TEMPDIR_FUNCTION_ ChromeCustomTempDir
+
+#endif  // THIRD_PARTY_GOOGLETEST_CUSTOM_GTEST_INTERNAL_CUSTOM_GTEST_H_
diff --git a/third_party/googletest/custom/gtest/internal/custom/gtest_port_wrapper.cc b/third_party/googletest/custom/gtest/internal/custom/gtest_port_wrapper.cc
new file mode 100644
index 0000000..6d632f4
--- /dev/null
+++ b/third_party/googletest/custom/gtest/internal/custom/gtest_port_wrapper.cc
@@ -0,0 +1,151 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(crbug.com/1009553): Remove this wrapper after finding a way to plumb a
+// workable temporary path into googletest on Android.
+
+// This wrapper lets us compile gtest-port.cc without its stream redirection
+// code. We replace this code with a variant that works on all Chrome platforms.
+// This is a temporary workaround until we get good code upstream.
+//
+// Stream redirection requires the ability to create files in a temporary
+// directory. Traditionally, this directory has been /sdcard on Android.
+// Commit bf0fe874a27bd6c9a4a35b98e662d2d02f8879a2 changed the Android
+// directory to /data/local/tmp, which is not writable in Chrome's testing
+// setup. We work around this problem by using the old code for now.
+//
+// It is tempting to consider disabling the stream redirection code altogether,
+// by setting GTEST_HAS_STREAM_REDIRECTION to 0 in googletest's BUILD.gn.
+// This breaks gtest-death-test.cc, which assumes the existence of
+// testing::internal::{GetCapturedStderr,CaptureStderr} without any macro
+// checking.
+
+#define GTEST_HAS_STREAM_REDIRECTION 0
+#include "third_party/googletest/src/googletest/src/gtest-port.cc"
+
+namespace testing {
+namespace internal {
+
+// Object that captures an output stream (stdout/stderr).
+class CapturedStream {
+ public:
+  // The ctor redirects the stream to a temporary file.
+  explicit CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) {
+    std::string temp_dir = ::testing::TempDir();
+
+    // testing::TempDir() should return a directory with a path separator.
+    // However, this rule was documented fairly recently, so we normalize across
+    // implementations with and without a trailing path separator.
+    if (temp_dir.back() != GTEST_PATH_SEP_[0])
+      temp_dir.push_back(GTEST_PATH_SEP_[0]);
+
+#if GTEST_OS_WINDOWS
+    char temp_file_path[MAX_PATH + 1] = {'\0'};  // NOLINT
+    const UINT success = ::GetTempFileNameA(temp_dir.c_str(), "gtest_redir",
+                                            0,  // Generate unique file name.
+                                            temp_file_path);
+    GTEST_CHECK_(success != 0)
+        << "Unable to create a temporary file in " << temp_dir;
+    const int captured_fd = creat(temp_file_path, _S_IREAD | _S_IWRITE);
+    GTEST_CHECK_(captured_fd != -1)
+        << "Unable to open temporary file " << temp_file_path;
+    filename_ = temp_file_path;
+#else
+    std::string name_template = temp_dir + "gtest_captured_stream.XXXXXX";
+
+    // mkstemp() modifies the string bytes in place, and does not go beyond the
+    // string's length. This results in well-defined behavior in C++17.
+    //
+    // The const_cast is needed below C++17. The constraints on std::string
+    // implementations in C++11 and above make assumption behind the const_cast
+    // fairly safe.
+    const int captured_fd = ::mkstemp(const_cast<char*>(name_template.data()));
+    GTEST_CHECK_(captured_fd != -1)
+        << "Failed to create tmp file " << name_template
+        << " for test; does the test have write access to the directory?";
+    filename_ = std::move(name_template);
+#endif  // GTEST_OS_WINDOWS
+    fflush(nullptr);
+    dup2(captured_fd, fd_);
+    close(captured_fd);
+  }
+
+  ~CapturedStream() { remove(filename_.c_str()); }
+
+  std::string GetCapturedString() {
+    if (uncaptured_fd_ != -1) {
+      // Restores the original stream.
+      fflush(nullptr);
+      dup2(uncaptured_fd_, fd_);
+      close(uncaptured_fd_);
+      uncaptured_fd_ = -1;
+    }
+
+    FILE* const file = posix::FOpen(filename_.c_str(), "r");
+    if (file == nullptr) {
+      GTEST_LOG_(FATAL) << "Failed to open tmp file " << filename_
+                        << " for capturing stream.";
+    }
+    const std::string content = ReadEntireFile(file);
+    posix::FClose(file);
+    return content;
+  }
+
+ private:
+  const int fd_;  // A stream to capture.
+  int uncaptured_fd_;
+  // Name of the temporary file holding the stderr output.
+  ::std::string filename_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream);
+};
+
+GTEST_DISABLE_MSC_DEPRECATED_POP_()
+
+static CapturedStream* g_captured_stderr = nullptr;
+static CapturedStream* g_captured_stdout = nullptr;
+
+// Starts capturing an output stream (stdout/stderr).
+static void CaptureStream(int fd,
+                          const char* stream_name,
+                          CapturedStream** stream) {
+  if (*stream != nullptr) {
+    GTEST_LOG_(FATAL) << "Only one " << stream_name
+                      << " capturer can exist at a time.";
+  }
+  *stream = new CapturedStream(fd);
+}
+
+// Stops capturing the output stream and returns the captured string.
+static std::string GetCapturedStream(CapturedStream** captured_stream) {
+  const std::string content = (*captured_stream)->GetCapturedString();
+
+  delete *captured_stream;
+  *captured_stream = nullptr;
+
+  return content;
+}
+
+// Starts capturing stdout.
+void CaptureStdout() {
+  CaptureStream(kStdOutFileno, "stdout", &g_captured_stdout);
+}
+
+// Starts capturing stderr.
+void CaptureStderr() {
+  CaptureStream(kStdErrFileno, "stderr", &g_captured_stderr);
+}
+
+// Stops capturing stdout and returns the captured string.
+std::string GetCapturedStdout() {
+  return GetCapturedStream(&g_captured_stdout);
+}
+
+// Stops capturing stderr and returns the captured string.
+std::string GetCapturedStderr() {
+  return GetCapturedStream(&g_captured_stderr);
+}
+
+}  // namespace internal
+}  // namespace testing
diff --git a/third_party/googletest/custom/gtest/internal/custom/pdfium_custom_temp_dir.cc b/third_party/googletest/custom/gtest/internal/custom/pdfium_custom_temp_dir.cc
new file mode 100644
index 0000000..6b6d6b6
--- /dev/null
+++ b/third_party/googletest/custom/gtest/internal/custom/pdfium_custom_temp_dir.cc
@@ -0,0 +1,82 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/googletest/custom/gtest/internal/custom/pdfium_custom_temp_dir.h"
+#include "third_party/googletest/src/googletest/include/gtest/internal/gtest-port.h"
+#if GTEST_OS_WINDOWS
+#include <windows.h>
+#endif  // GTEST_OS_WINDOWS
+
+namespace testing {
+
+namespace {
+
+// The temporary directory read from the OS canonical environment variable.
+//
+// Returns an empty string if the environment variable is not set. The returned
+// string may or may not end with the OS-specific path separator. The path is
+// not guaranteed to point to an existing directory. The directory it points to
+// is not guaranteed to be writable by the application.
+std::string ChromeGetEnvTempDir() {
+#if GTEST_OS_WINDOWS_MOBILE
+  const char* env_result = internal::posix::GetEnv("TEMP");
+#elif GTEST_OS_WINDOWS
+  char temp_dir_path[_MAX_PATH + 1] = {'\0'};  // NOLINT
+  if (::GetTempPathA(sizeof(temp_dir_path), temp_dir_path) != 0)
+    return temp_dir_path;
+  const char* env_result = internal::posix::GetEnv("TEMP");
+#else
+  const char* env_result = internal::posix::GetEnv("TMPDIR");
+#endif  // GETST_OS_WINDOWS
+
+  if (env_result == nullptr)
+    return std::string();
+  return env_result;
+}
+
+}  // namespace
+
+// returns temp directory for tests.
+std::string ChromeCustomTempDir() {
+  std::string temp_dir = ChromeGetEnvTempDir();
+  if (!temp_dir.empty()) {
+    if (temp_dir.back() != GTEST_PATH_SEP_[0])
+      temp_dir.push_back(GTEST_PATH_SEP_[0]);
+    return temp_dir;
+  }
+
+#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS
+  return "\\temp\\";
+#elif GTEST_OS_LINUX_ANDROID
+  // Android applications are expected to call the framework's
+  // Context.getExternalStorageDirectory() method through JNI to get the
+  // location of the world-writable SD Card directory. However, this requires a
+  // Context handle, which cannot be retrieved globally from native code. Doing
+  // so also precludes running the code as part of a regular standalone
+  // executable, which doesn't run in a Dalvik process (e.g. when running it
+  // through 'adb shell').
+  //
+  // Starting from Android O, the recommended generic temporary directory is
+  // '/data/local/tmp'. The recommended fallback is the current directory,
+  // which is usually accessible in app context.
+  if (::access("/data/local/tmp", R_OK | W_OK | X_OK) == 0)
+    return "/data/local/tmp/";
+  const char* current_dir = ::getcwd(nullptr, 0);
+  if (current_dir != nullptr &&
+      ::access(current_dir, R_OK | W_OK | X_OK) == 0) {
+    temp_dir = current_dir;
+    temp_dir.push_back(GTEST_PATH_SEP_[0]);
+    return temp_dir;
+  }
+  // Before Android O, /sdcard is usually available.
+  if (::access("/sdcard", R_OK | W_OK | X_OK) == 0)
+    return "/sdcard/";
+  // Generic POSIX fallback.
+  return "/tmp/";
+#else
+  return "/tmp/";
+#endif  // GTEST_OS_WINDOWS_MOBILE
+}
+
+}  // namespace testing
diff --git a/third_party/googletest/custom/gtest/internal/custom/pdfium_custom_temp_dir.h b/third_party/googletest/custom/gtest/internal/custom/pdfium_custom_temp_dir.h
new file mode 100644
index 0000000..baf1b3b
--- /dev/null
+++ b/third_party/googletest/custom/gtest/internal/custom/pdfium_custom_temp_dir.h
@@ -0,0 +1,15 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_GOOGLETEST_CUSTOM_GTEST_INTERNAL_CUSTOM_CHROME_CUSTOM_TEMP_DIR_H_
+#define THIRD_PARTY_GOOGLETEST_CUSTOM_GTEST_INTERNAL_CUSTOM_CHROME_CUSTOM_TEMP_DIR_H_
+
+#include <string>
+
+namespace testing {
+// Returns alternate temp directory for gtest.
+std::string ChromeCustomTempDir();
+}  // namespace testing
+
+#endif  // THIRD_PARTY_GOOGLETEST_CUSTOM_GTEST_INTERNAL_CUSTOM_CHROME_CUSTOM_TEMP_DIR_H_
diff --git a/third_party/lcms/0000-cmserr-changes.patch b/third_party/lcms/0000-cmserr-changes.patch
index 31f56fd..13a854a 100644
--- a/third_party/lcms/0000-cmserr-changes.patch
+++ b/third_party/lcms/0000-cmserr-changes.patch
@@ -27,7 +27,7 @@
 -
 -// User may override this behaviour by using a memory plug-in, which basically replaces
 -// the default memory management functions. In this case, no check is performed and it
--// is up to the plug-in writter to keep in the safe side. There are only three functions
+-// is up to the plug-in writer to keep in the safe side. There are only three functions
 -// required to be implemented: malloc, realloc and free, although the user may want to
 -// replace the optional mallocZero, calloc and dup as well.
 -
diff --git a/third_party/lcms/0002-old-performance-fix.patch b/third_party/lcms/0002-old-performance-fix.patch
deleted file mode 100644
index cea2bee..0000000
--- a/third_party/lcms/0002-old-performance-fix.patch
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/third_party/lcms/src/cmsgamma.c b/third_party/lcms/src/cmsgamma.c
-index edb8f6bba..eb3dd881c 100644
---- a/third_party/lcms/src/cmsgamma.c
-+++ b/third_party/lcms/src/cmsgamma.c
-@@ -799,7 +799,7 @@ void CMSEXPORT cmsFreeToneCurveTriple(cmsToneCurve* Curve[3])
- // Duplicate a gamma table
- cmsToneCurve* CMSEXPORT cmsDupToneCurve(const cmsToneCurve* In)
- {
--    if (In == NULL) return NULL;
-+    if (In == NULL || In ->Segments == NULL || In ->Table16 == NULL) return NULL;
- 
-     return  AllocateToneCurveStruct(In ->InterpParams ->ContextID, In ->nEntries, In ->nSegments, In ->Segments, In ->Table16);
- }
diff --git a/third_party/lcms/0005-old-fix-e-with-tilde.patch b/third_party/lcms/0005-old-fix-e-with-tilde.patch
deleted file mode 100644
index 9a389bc..0000000
--- a/third_party/lcms/0005-old-fix-e-with-tilde.patch
+++ /dev/null
@@ -1,61 +0,0 @@
-diff --git a/third_party/lcms/src/cmsxform.c b/third_party/lcms/src/cmsxform.c
-index 6b2950e4e..508117bd3 100644
---- a/third_party/lcms/src/cmsxform.c
-+++ b/third_party/lcms/src/cmsxform.c
-@@ -341,7 +341,7 @@ void NullFloatXFORM(_cmsTRANSFORM* p,
- 
- // 16 bit precision -----------------------------------------------------------------------------------------------------------
- 
--// Null transformation, only applies formatters. No caché
-+// Null transformation, only applies formatters. No cache
- static
- void NullXFORM(_cmsTRANSFORM* p,
-                const void* in,
-@@ -442,7 +442,7 @@ void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
-         p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
- }
- 
--// Gamut check, No caché, 16 bits.
-+// Gamut check, No cache, 16 bits.
- static
- void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
-                                   const void* in,
-@@ -481,7 +481,7 @@ void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
- }
- 
- 
--// No gamut check, Caché, 16 bits,
-+// No gamut check, Cache, 16 bits,
- static
- void CachedXFORM(_cmsTRANSFORM* p,
-                  const void* in,
-@@ -839,7 +839,7 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
-             p ->xform = NullFloatXFORM;
-         }
-         else {
--            // Float transforms don't use caché, always are non-NULL
-+            // Float transforms don't use cache, always are non-NULL
-             p ->xform = FloatXFORM;
-         }
- 
-@@ -878,16 +878,16 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
-             if (*dwFlags & cmsFLAGS_NOCACHE) {
- 
-                 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
--                    p ->xform = PrecalculatedXFORMGamutCheck;  // Gamut check, no caché
-+                    p ->xform = PrecalculatedXFORMGamutCheck;  // Gamut check, no cache
-                 else
--                    p ->xform = PrecalculatedXFORM;  // No caché, no gamut check
-+                    p ->xform = PrecalculatedXFORM;  // No cache, no gamut check
-             }
-             else {
- 
-                 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
--                    p ->xform = CachedXFORMGamutCheck;    // Gamut check, caché
-+                    p ->xform = CachedXFORMGamutCheck;    // Gamut check, cache
-                 else
--                    p ->xform = CachedXFORM;  // No gamut check, caché
-+                    p ->xform = CachedXFORM;  // No gamut check, cache
- 
-             }
-         }
diff --git a/third_party/lcms/0008-infinite-loop-GrowNamedColorList.patch b/third_party/lcms/0008-infinite-loop-GrowNamedColorList.patch
deleted file mode 100644
index 26f5938..0000000
--- a/third_party/lcms/0008-infinite-loop-GrowNamedColorList.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-diff --git a/third_party/lcms/src/cmsnamed.c b/third_party/lcms/src/cmsnamed.c
-index 42bd36530..9cfd2282f 100644
---- a/third_party/lcms/src/cmsnamed.c
-+++ b/third_party/lcms/src/cmsnamed.c
-@@ -546,7 +546,7 @@ cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUIn
- 
-     while (v -> Allocated < n) {
-         if (!GrowNamedColorList(v)) {
--            _cmsFree(ContextID, (void*) v);
-+            cmsFreeNamedColorList(v);
-             return NULL;
-         }
-     }
-@@ -579,7 +579,10 @@ cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v)
- 
-     // For really large tables we need this
-     while (NewNC ->Allocated < v ->Allocated){
--        if (!GrowNamedColorList(NewNC)) return NULL;
-+        if (!GrowNamedColorList(NewNC)) {
-+            cmsFreeNamedColorList(NewNC);
-+            return NULL;
-+        }
-     }
- 
-     memmove(NewNC ->Prefix, v ->Prefix, sizeof(v ->Prefix));
diff --git a/third_party/lcms/0019-utf8.patch b/third_party/lcms/0019-utf8.patch
deleted file mode 100644
index 11293c9..0000000
--- a/third_party/lcms/0019-utf8.patch
+++ /dev/null
@@ -1,98 +0,0 @@
-diff --git a/third_party/lcms/src/cmscgats.c b/third_party/lcms/src/cmscgats.c
-index 7df5bbc4d..49cfbbe8c 100644
---- a/third_party/lcms/src/cmscgats.c
-+++ b/third_party/lcms/src/cmscgats.c
-@@ -256,7 +256,7 @@ static PROPERTY PredefinedProperties[] = {
-                                                    // needed.
- 
-         {"SAMPLE_BACKING",   WRITE_STRINGIFY},     // Identifies the backing material used behind the sample during
--                                                   // measurement. Allowed values are ?black?, ?white?, or {"na".
-+                                                   // measurement. Allowed values are "black", "white", or {"na".
-                                                   
-         {"CHISQ_DOF",        WRITE_STRINGIFY},     // Degrees of freedom associated with the Chi squared statistic
-                                                    // below properties are new in recent specs:
-@@ -271,7 +271,7 @@ static PROPERTY PredefinedProperties[] = {
-                                                    // denote the use of filters such as none, D65, Red, Green or Blue.
-                                                   
-        {"POLARIZATION",      WRITE_STRINGIFY},     // Identifies the use of a physical polarization filter during measurement. Allowed
--                                                   // values are {"yes?, ?white?, ?none? or ?na?.
-+                                                   // values are "yes", "white", "none" or "na".
- 
-        {"WEIGHTING_FUNCTION", WRITE_PAIR},         // Indicates such functions as: the CIE standard observer functions used in the
-                                                    // calculation of various data parameters (2 degree and 10 degree), CIE standard
-diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
-index 6cb8d4be3..e95e6d910 100644
---- a/third_party/lcms/src/cmstypes.c
-+++ b/third_party/lcms/src/cmstypes.c
-@@ -974,7 +974,7 @@ cmsBool  Type_Text_Description_Write(struct _cms_typehandler_struct* self, cmsIO
-     len = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0);
- 
-     // Specification ICC.1:2001-04 (v2.4.0): It has been found that textDescriptionType can contain misaligned data
--    //(see clause 4.1 for the definition of ?aligned?). Because the Unicode language
-+    //(see clause 4.1 for the definition of "aligned"). Because the Unicode language
-     // code and Unicode count immediately follow the ASCII description, their
-     // alignment is not correct if the ASCII count is not a multiple of four. The
-     // ScriptCode code is misaligned when the ASCII count is odd. Profile reading and
-@@ -3091,10 +3091,10 @@ void Type_ColorantTable_Free(struct _cms_typehandler_struct* self, void* Ptr)
- //The namedColor2Type is a count value and array of structures that provide color
- //coordinates for 7-bit ASCII color names. For each named color, a PCS and optional
- //device representation of the color are given. Both representations are 16-bit values.
--//The device representation corresponds to the header?s ?color space of data? field.
--//This representation should be consistent with the ?number of device components?
-+//The device representation corresponds to the header's "color space of data" field.
-+//This representation should be consistent with the "number of device components"
- //field in the namedColor2Type. If this field is 0, device coordinates are not provided.
--//The PCS representation corresponds to the header?s PCS field. The PCS representation
-+//The PCS representation corresponds to the header's PCS field. The PCS representation
- //is always provided. Color names are fixed-length, 32-byte fields including null
- //termination. In order to maintain maximum portability, it is strongly recommended
- //that special characters of the 7-bit ASCII set not be used.
-@@ -3839,7 +3839,7 @@ void Type_Screening_Free(struct _cms_typehandler_struct* self, void* Ptr)
- // ********************************************************************************
- //
- //This type represents a set of viewing condition parameters including:
--//CIE ?absolute? illuminant white point tristimulus values and CIE ?absolute?
-+//CIE 'absolute' illuminant white point tristimulus values and CIE 'absolute'
- //surround tristimulus values.
- 
- static
-@@ -3926,7 +3926,7 @@ void GenericMPEfree(struct _cms_typehandler_struct* self, void *Ptr)
- }
- 
- // Each curve is stored in one or more curve segments, with break-points specified between curve segments.
--// The first curve segment always starts at ?Infinity, and the last curve segment always ends at +Infinity. The
-+// The first curve segment always starts at -Infinity, and the last curve segment always ends at +Infinity. The
- // first and last curve segments shall be specified in terms of a formula, whereas the other segments shall be
- // specified either in terms of a formula, or by a sampled curve.
- 
-diff --git a/third_party/lcms/src/cmsvirt.c b/third_party/lcms/src/cmsvirt.c
-index 19e0cafb1..935effc66 100644
---- a/third_party/lcms/src/cmsvirt.c
-+++ b/third_party/lcms/src/cmsvirt.c
-@@ -612,18 +612,18 @@ cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void)
- 
- //sRGB Curves are defined by:
- //
--//If  R?sRGB,G?sRGB, B?sRGB < 0.04045
-+//If  R'sRGB,G'sRGB, B'sRGB < 0.04045
- //
--//    R =  R?sRGB / 12.92
--//    G =  G?sRGB / 12.92
--//    B =  B?sRGB / 12.92
-+//    R =  R'sRGB / 12.92
-+//    G =  G'sRGB / 12.92
-+//    B =  B'sRGB / 12.92
- //
- //
--//else if  R?sRGB,G?sRGB, B?sRGB >= 0.04045
-+//else if  R'sRGB,G'sRGB, B'sRGB >= 0.04045
- //
--//    R = ((R?sRGB + 0.055) / 1.055)^2.4
--//    G = ((G?sRGB + 0.055) / 1.055)^2.4
--//    B = ((B?sRGB + 0.055) / 1.055)^2.4
-+//    R = ((R'sRGB + 0.055) / 1.055)^2.4
-+//    G = ((G'sRGB + 0.055) / 1.055)^2.4
-+//    B = ((B'sRGB + 0.055) / 1.055)^2.4
- 
- static
- cmsToneCurve* Build_sRGBGamma(cmsContext ContextID)
diff --git a/third_party/lcms/0026-more-unsupported-characters.patch b/third_party/lcms/0026-more-unsupported-characters.patch
deleted file mode 100644
index 66e9239..0000000
--- a/third_party/lcms/0026-more-unsupported-characters.patch
+++ /dev/null
@@ -1,41 +0,0 @@
-diff --git a/third_party/lcms/src/cmsopt.c b/third_party/lcms/src/cmsopt.c
-index f838b6eb7..5ea1b4c85 100644
---- a/third_party/lcms/src/cmsopt.c
-+++ b/third_party/lcms/src/cmsopt.c
-@@ -1764,8 +1764,8 @@ cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
-         _cmsStageToneCurvesData* mpeC1 = (_cmsStageToneCurvesData*) cmsStageData(Curve1);
-         _cmsStageToneCurvesData* mpeC2 = (_cmsStageToneCurvesData*) cmsStageData(Curve2);
- 
--        // In this particular optimization, caché does not help as it takes more time to deal with
--        // the caché that with the pixel handling
-+        // In this particular optimization, cache does not help as it takes more time to deal with
-+        // the cache that with the pixel handling
-         *dwFlags |= cmsFLAGS_NOCACHE;
- 
-         // Setup the optimizarion routines
-diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
-index e95e6d910..0c24da162 100644
---- a/third_party/lcms/src/cmstypes.c
-+++ b/third_party/lcms/src/cmstypes.c
-@@ -4207,7 +4207,7 @@ cmsBool  Type_MPEcurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER*
- // The matrix is organized as an array of PxQ+Q elements, where P is the number of input channels to the
- // matrix, and Q is the number of output channels. The matrix elements are each float32Numbers. The array
- // is organized as follows:
--// array = [e11, e12, …, e1P, e21, e22, …, e2P, …, eQ1, eQ2, …, eQP, e1, e2, …, eQ]
-+// array = [e11, e12, ..., e1P, e21, e22, ..., e2P, ..., eQ1, eQ2, ..., eQP, e1, e2, ..., eQ]
- 
- static
- void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
-@@ -4730,10 +4730,10 @@ void *Type_vcgt_Read(struct _cms_typehandler_struct* self,
-             // Y = cX + f             | X < d
- 
-             // vcgt formula is:
--            // Y = (Max – Min) * (X ^ Gamma) + Min
-+            // Y = (Max - Min) * (X ^ Gamma) + Min
- 
-             // So, the translation is
--            // a = (Max – Min) ^ ( 1 / Gamma)
-+            // a = (Max - Min) ^ ( 1 / Gamma)
-             // e = Min
-             // b=c=d=f=0
- 
diff --git a/third_party/lcms/0027-changes-from-beginning-of-time.patch b/third_party/lcms/0027-changes-from-beginning-of-time.patch
index 9eeedd8..7d9264e 100644
--- a/third_party/lcms/0027-changes-from-beginning-of-time.patch
+++ b/third_party/lcms/0027-changes-from-beginning-of-time.patch
@@ -40,7 +40,7 @@
  
      if (Curve ->Segments) {
  
-@@ -766,20 +771,30 @@ void CMSEXPORT cmsFreeToneCurve(cmsToneCurve* Curve)
+@@ -876,18 +881,25 @@ void CMSEXPORT cmsFreeToneCurve(cmsToneCurve* Curve)
  
              if (Curve ->Segments[i].SampledPoints) {
                  _cmsFree(ContextID, Curve ->Segments[i].SampledPoints);
@@ -66,25 +66,7 @@
 +        Curve -> Evals = NULL;
 +    }
  
--    if (Curve) _cmsFree(ContextID, Curve);
-+    if (Curve) {
-+        _cmsFree(ContextID, Curve);
-+        Curve = NULL;
-+    }
- }
- 
- // Utility function, free 3 gamma tables
-@@ -799,7 +814,10 @@ void CMSEXPORT cmsFreeToneCurveTriple(cmsToneCurve* Curve[3])
- // Duplicate a gamma table
- cmsToneCurve* CMSEXPORT cmsDupToneCurve(const cmsToneCurve* In)
- {
--    if (In == NULL || In ->Segments == NULL || In ->Table16 == NULL) return NULL;
-+    // Xiaochuan Liu
-+    // fix openpdf bug(mantis id:0055683, google id:360198)
-+    // the function CurveSetElemTypeFree in cmslut.c also needs to check pointer
-+    if (In == NULL || In ->InterpParams == NULL || In ->Segments == NULL || In ->Table16 == NULL) return NULL;
- 
-     return  AllocateToneCurveStruct(In ->InterpParams ->ContextID, In ->nEntries, In ->nSegments, In ->Segments, In ->Table16);
+     _cmsFree(ContextID, Curve);
  }
 diff --git a/third_party/lcms/src/cmsio0.c b/third_party/lcms/src/cmsio0.c
 index 1b026488d..cc5f89064 100644
@@ -133,20 +115,3 @@
  
  // Low-level save to disk.
  cmsBool  CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName)
-diff --git a/third_party/lcms/src/cmsio1.c b/third_party/lcms/src/cmsio1.c
-index 364741c9e..4b12ae18e 100644
---- a/third_party/lcms/src/cmsio1.c
-+++ b/third_party/lcms/src/cmsio1.c
-@@ -201,7 +201,11 @@ cmsPipeline* BuildGrayInputMatrixPipeline(cmsHPROFILE hProfile)
-     return Lut;
- 
- Error:
--    cmsFreeToneCurve(GrayTRC);
-+    // memory pointed by GrayTRC is not a new malloc memory, so don't free it here, 
-+    // memory pointed by GrayTRC will be freed when hProfile is closed.
-+    // test file :0047776_Pocket Medicine_ The Massachusetts General Hospital Handbook of Internal Medicine-2.pdf
-+    // Xiaochuan Liu, 20140421
-+    //cmsFreeToneCurve(GrayTRC);
-     cmsPipelineFree(Lut);
-     return NULL;
- }
diff --git a/third_party/lcms/0029-drop-register-keyword.patch b/third_party/lcms/0029-drop-register-keyword.patch
index 7680f2c..d2e9dfa 100644
--- a/third_party/lcms/0029-drop-register-keyword.patch
+++ b/third_party/lcms/0029-drop-register-keyword.patch
@@ -1,24 +1,13 @@
 diff --git a/third_party/lcms/include/lcms2.h b/third_party/lcms/include/lcms2.h
-index c84a4fd93..2bf6f2472 100644
+index f65b3b758..1ace75d35 100644
 --- a/third_party/lcms/include/lcms2.h
 +++ b/third_party/lcms/include/lcms2.h
-@@ -1239,13 +1239,13 @@ CMSAPI cmsStageSignature CMSEXPORT cmsStageType(const cmsStage* mpe);
- CMSAPI void*             CMSEXPORT cmsStageData(const cmsStage* mpe);
+@@ -62,7 +62,7 @@
+ // #define CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
  
- // Sampling
--typedef cmsInt32Number (* cmsSAMPLER16)   (register const cmsUInt16Number In[],
--                                            register cmsUInt16Number Out[],
--                                            register void * Cargo);
-+typedef cmsInt32Number (*cmsSAMPLER16)(const cmsUInt16Number In[],
-+                                       cmsUInt16Number Out[],
-+                                       void* Cargo);
+ // Uncomment this to remove the "register" storage class
+-// #define CMS_NO_REGISTER_KEYWORD 1
++#define CMS_NO_REGISTER_KEYWORD 1
  
--typedef cmsInt32Number (* cmsSAMPLERFLOAT)(register const cmsFloat32Number In[],
--                                            register cmsFloat32Number Out[],
--                                            register void * Cargo);
-+typedef cmsInt32Number (*cmsSAMPLERFLOAT)(const cmsFloat32Number In[],
-+                                          cmsFloat32Number Out[],
-+                                          void* Cargo);
+ // ********** End of configuration toggles ******************************
  
- // Use this flag to prevent changes being written to destination
- #define SAMPLER_INSPECT     0x01000000
diff --git a/third_party/lcms/0030-const-data.patch b/third_party/lcms/0030-const-data.patch
index dc8e37c..0c42dde 100644
--- a/third_party/lcms/0030-const-data.patch
+++ b/third_party/lcms/0030-const-data.patch
@@ -1,30 +1,30 @@
 diff --git a/third_party/lcms/src/cmsalpha.c b/third_party/lcms/src/cmsalpha.c
-index 7d6aa345f..566f5fe9b 100644
+index 374555658..a2113180c 100644
 --- a/third_party/lcms/src/cmsalpha.c
 +++ b/third_party/lcms/src/cmsalpha.c
-@@ -252,7 +252,7 @@ int FormatterPos(cmsUInt32Number frm)
+@@ -377,7 +377,7 @@ int FormatterPos(cmsUInt32Number frm)
  static
  cmsFormatterAlphaFn _cmsGetFormatterAlpha(cmsContext id, cmsUInt32Number in, cmsUInt32Number out)
  {
--static cmsFormatterAlphaFn FormattersAlpha[5][5] = {
-+static const cmsFormatterAlphaFn FormattersAlpha[5][5] = {
+-static cmsFormatterAlphaFn FormattersAlpha[6][6] = {
++static const cmsFormatterAlphaFn FormattersAlpha[6][6] = {
  
-        /* from 8 */  { copy8,      from8to16,   from8toHLF,   from8toFLT,   from8toDBL   },
-        /* from 16*/  { from16to8,  copy16,      from16toHLF,  from16toFLT,  from16toDBL  },
+        /* from 8 */  { copy8,       from8to16,   from8to16SE,   from8toHLF,   from8toFLT,    from8toDBL    },
+        /* from 16*/  { from16to8,   copy16,      from16to16,    from16toHLF,  from16toFLT,   from16toDBL   },
 diff --git a/third_party/lcms/src/cmsgamma.c b/third_party/lcms/src/cmsgamma.c
-index 6e36cf462..eadbed852 100644
+index 54dd78912..f15a5f1a8 100644
 --- a/third_party/lcms/src/cmsgamma.c
 +++ b/third_party/lcms/src/cmsgamma.c
-@@ -57,7 +57,7 @@ typedef struct _cmsParametricCurvesCollection_st {
+@@ -58,7 +58,7 @@ typedef struct _cmsParametricCurvesCollection_st {
  static cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R);
  
  // The built-in list
 -static _cmsParametricCurvesCollection DefaultCurves = {
 +static const _cmsParametricCurvesCollection DefaultCurves = {
-     9,                                  // # of curve types
-     { 1, 2, 3, 4, 5, 6, 7, 8, 108 },    // Parametric curve ID
-     { 1, 3, 4, 5, 7, 4, 5, 5, 1 },      // Parameters by type
-@@ -161,7 +161,7 @@ cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase*
+     10,                                      // # of curve types
+     { 1, 2, 3, 4, 5, 6, 7, 8, 108, 109 },    // Parametric curve ID
+     { 1, 3, 4, 5, 7, 4, 5, 5,   1,   1 },    // Parameters by type
+@@ -162,7 +162,7 @@ cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase*
  
  // Search in type list, return position or -1 if not found
  static
@@ -33,7 +33,7 @@
  {
      int i;
  
-@@ -174,9 +174,9 @@ int IsInSet(int Type, _cmsParametricCurvesCollection* c)
+@@ -175,9 +175,9 @@ int IsInSet(int Type, _cmsParametricCurvesCollection* c)
  
  // Search for the collection which contains a specific type
  static
@@ -45,7 +45,7 @@
      int Position;
      _cmsCurvesPluginChunkType* ctx = ( _cmsCurvesPluginChunkType*) _cmsContextGetClientChunk(ContextID, CurvesPlugin);
  
-@@ -269,7 +269,7 @@ cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsInt32Number nEntr
+@@ -270,7 +270,7 @@ cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsUInt32Number nEnt
      // is placed in advance to maximize performance.
      if (Segments != NULL && (nSegments > 0)) {
  
@@ -54,7 +54,7 @@
  
          p ->SegInterp = (cmsInterpParams**) _cmsCalloc(ContextID, nSegments, sizeof(cmsInterpParams*));
          if (p ->SegInterp == NULL) goto Error;
-@@ -714,7 +714,7 @@ cmsToneCurve* CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt
+@@ -824,7 +824,7 @@ cmsToneCurve* CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt
      cmsCurveSegment Seg0;
      int Pos = 0;
      cmsUInt32Number size;
@@ -63,52 +63,3 @@
  
      _cmsAssert(Params != NULL);
  
-diff --git a/third_party/lcms/src/cmshalf.c b/third_party/lcms/src/cmshalf.c
-index cdd4e37b7..cceb6f987 100644
---- a/third_party/lcms/src/cmshalf.c
-+++ b/third_party/lcms/src/cmshalf.c
-@@ -31,7 +31,7 @@
- // This code is inspired in the paper "Fast Half Float Conversions"
- // by Jeroen van der Zijp
- 
--static cmsUInt32Number Mantissa[2048] = {
-+static const cmsUInt32Number Mantissa[2048] = {
- 
- 0x00000000, 0x33800000, 0x34000000, 0x34400000, 0x34800000, 0x34a00000,
- 0x34c00000, 0x34e00000, 0x35000000, 0x35100000, 0x35200000, 0x35300000,
-@@ -377,7 +377,7 @@ static cmsUInt32Number Mantissa[2048] = {
- 0x387fc000, 0x387fe000
- };
- 
--static cmsUInt16Number Offset[64] = {
-+static const cmsUInt16Number Offset[64] = {
- 0x0000, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
- 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
- 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
-@@ -391,7 +391,7 @@ static cmsUInt16Number Offset[64] = {
- 0x0400, 0x0400, 0x0400, 0x0400
- };
- 
--static cmsUInt32Number Exponent[64] = {
-+static const cmsUInt32Number Exponent[64] = {
- 0x00000000, 0x00800000, 0x01000000, 0x01800000, 0x02000000, 0x02800000,
- 0x03000000, 0x03800000, 0x04000000, 0x04800000, 0x05000000, 0x05800000,
- 0x06000000, 0x06800000, 0x07000000, 0x07800000, 0x08000000, 0x08800000,
-@@ -405,7 +405,7 @@ static cmsUInt32Number Exponent[64] = {
- 0x8e000000, 0x8e800000, 0x8f000000, 0xc7800000
- };
- 
--static cmsUInt16Number Base[512] = {
-+static const cmsUInt16Number Base[512] = {
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-@@ -460,7 +460,7 @@ static cmsUInt16Number Base[512] = {
- 0xfc00, 0xfc00
- };
- 
--static cmsUInt8Number  Shift[512] = {
-+static const cmsUInt8Number  Shift[512] = {
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
diff --git a/third_party/lcms/0031-wrong-tag-element-count.patch b/third_party/lcms/0031-wrong-tag-element-count.patch
deleted file mode 100644
index a62bc3d..0000000
--- a/third_party/lcms/0031-wrong-tag-element-count.patch
+++ /dev/null
@@ -1,12 +0,0 @@
-diff --git a/third_party/lcms/src/cmsio0.c b/third_party/lcms/src/cmsio0.c
-index cc5f89064..63bbe36a8 100644
---- a/third_party/lcms/src/cmsio0.c
-+++ b/third_party/lcms/src/cmsio0.c
-@@ -1616,6 +1616,7 @@ void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig)
-         _cmsTagSignature2String(String, sig);
-         cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d",
-             String, TagDescriptor ->ElemCount, ElemCount);
-+        goto Error;
-     }
- 
- 
diff --git a/third_party/lcms/0032-cgats-allocation.patch b/third_party/lcms/0032-cgats-allocation.patch
deleted file mode 100644
index 08204b5..0000000
--- a/third_party/lcms/0032-cgats-allocation.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-diff --git a/third_party/lcms/src/cmscgats.c b/third_party/lcms/src/cmscgats.c
-index 55f74ede8..0738a1cce 100644
---- a/third_party/lcms/src/cmscgats.c
-+++ b/third_party/lcms/src/cmscgats.c
-@@ -1504,10 +1504,16 @@ void AllocateDataSet(cmsIT8* it8)
-     t-> nSamples   = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
-     t-> nPatches   = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
- 
--    t-> Data = (char**)AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * ((cmsUInt32Number) t->nPatches + 1) *sizeof (char*));
--    if (t->Data == NULL) {
-+    if (t -> nSamples < 0 || t->nSamples > 0x7ffe || t->nPatches < 0 || t->nPatches > 0x7ffe)
-+    {
-+        SynError(it8, "AllocateDataSet: too much data");
-+    }
-+    else {
-+        t->Data = (char**)AllocChunk(it8, ((cmsUInt32Number)t->nSamples + 1) * ((cmsUInt32Number)t->nPatches + 1) * sizeof(char*));
-+        if (t->Data == NULL) {
- 
--        SynError(it8, "AllocateDataSet: Unable to allocate data array");
-+            SynError(it8, "AllocateDataSet: Unable to allocate data array");
-+        }
-     }
- 
- }
diff --git a/third_party/lcms/0034-dead-code.patch b/third_party/lcms/0034-dead-code.patch
new file mode 100644
index 0000000..d02e60b
--- /dev/null
+++ b/third_party/lcms/0034-dead-code.patch
@@ -0,0 +1,14 @@
+diff --git a/third_party/lcms/src/cmspack.c b/third_party/lcms/src/cmspack.c
+index 3982cc564..7f55ce304 100644
+--- a/third_party/lcms/src/cmspack.c
++++ b/third_party/lcms/src/cmspack.c
+@@ -3864,9 +3864,6 @@ cmsUInt32Number CMSEXPORT cmsFormatterForPCSOfProfile(cmsHPROFILE hProfile, cmsU
+     cmsUInt32Number nOutputChans = cmsChannelsOf(ColorSpace);
+     cmsUInt32Number Float = lIsFloat ? 1U : 0;
+ 
+-    // Unsupported color space?
+-    if (nOutputChans < 0) return 0;
+-
+     // Create a fake formatter for result
+     return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans);
+ }
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 5f0025b..ad93835 100644
--- a/third_party/lcms/README.pdfium
+++ b/third_party/lcms/README.pdfium
@@ -1,7 +1,8 @@
 Name: Little CMS
 URL: http://www.littlecms.com/
-Version: 2.9
+Version: 2.15
 Security Critical: yes
+Shipped: yes
 License: MIT License
 
 Description:
@@ -11,18 +12,13 @@
 
 0000-cmserr-changes.patch: change LCMS memory management methods to use PDFium's.
 0001-fix-include.patch: fix include in lcms2_internal.h.
-0002-old-performance-fix.patch: https://codereview.chromium.org/534363002/
 0003-old-uninitialized-in-LUTevalFloat.patch: https://codereview.chromium.org/380293002/
 0004-old-uninitialized-in-LUTeval16.patch: https://codereview.chromium.org/387273002/
-0005-old-fix-e-with-tilde.patch: like https://codereview.chromium.org/2411123003/ but better.
 0006-tag-type-confusion.patch: Fix a type confusion.
-0008-infinite-loop-GrowNamedColorList.patch: Fix infinite loop when calling GrowNamedColorList.
-0019-utf8.patch: Encode source files as utf-8.
-0026-more-unsupported-characters.patch: remove other unsupported characters.
 0027-changes-from-beginning-of-time.patch: commented changes from initial commit.
 0028-do-not-quickfloor.patch: flooring errors may cause heap-buffer-overflow.
 0029-drop-register-keyword.patch: Remove deprecated 'register' keyword.
 0030-const-data.patch: Mark many data structures as const.
-0031-wrong-tag-element-count.patch: Handle tag element count mismatch as an error.
-0032-cgats-allocation.patch: Add check on CGATS memory allocation.
 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/include/lcms2.h b/third_party/lcms/include/lcms2.h
index 7653f8e..75451bf 100644
--- a/third_party/lcms/include/lcms2.h
+++ b/third_party/lcms/include/lcms2.h
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -23,7 +23,7 @@
 //
 //---------------------------------------------------------------------------------
 //
-// Version 2.9rc3
+// Version 2.15 
 //
 
 #ifndef _lcms2_H
@@ -38,7 +38,7 @@
 // #define CMS_DONT_USE_INT64        1
 
 // Uncomment this if your compiler doesn't work with fast floor function
-// #define CMS_DONT_USE_FAST_FLOOR 1
+#define CMS_DONT_USE_FAST_FLOOR 1
 
 // Uncomment this line if you want lcms to use the black point tag in profile,
 // if commented, lcms will compute the black point by its own.
@@ -61,6 +61,9 @@
 // Uncomment this for special windows mutex initialization (see lcms2_internal.h)
 // #define CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
 
+// Uncomment this to remove the "register" storage class
+#define CMS_NO_REGISTER_KEYWORD 1
+
 // ********** End of configuration toggles ******************************
 
 // Needed for streams
@@ -78,7 +81,7 @@
 #endif
 
 // Version/release
-#define LCMS_VERSION        2090
+#define LCMS_VERSION        2150
 
 // I will give the chance of redefining basic types for compilers that are not fully C99 compliant
 #ifndef CMS_BASIC_TYPES_ALREADY_DEFINED
@@ -148,6 +151,13 @@
 #endif
 #endif
 
+// Handle "register" keyword
+#if defined(CMS_NO_REGISTER_KEYWORD)
+#  define CMSREGISTER
+#else
+#  define CMSREGISTER register
+#endif
+
 // In the case 64 bit numbers are not supported by the compiler
 #ifdef CMS_DONT_USE_INT64
     typedef cmsUInt32Number      cmsUInt64Number[2];
@@ -217,7 +227,7 @@
 
 
 // Calling convention -- this is hardly platform and compiler dependent
-#ifdef CMS_IS_WINDOWS_
+#if defined(CMS_IS_WINDOWS_) && !defined(__GNUC__)
 #  if defined(CMS_DLL) || defined(CMS_DLL_BUILD)
 #     ifdef __BORLANDC__
 #        define CMSEXPORT       __stdcall _export
@@ -280,6 +290,7 @@
 // Base ICC type definitions
 typedef enum {
     cmsSigChromaticityType                  = 0x6368726D,  // 'chrm'
+    cmsSigcicpType                          = 0x63696370,  // 'cicp' 
     cmsSigColorantOrderType                 = 0x636C726F,  // 'clro'
     cmsSigColorantTableType                 = 0x636C7274,  // 'clrt'
     cmsSigCrdInfoType                       = 0x63726469,  // 'crdi'
@@ -391,6 +402,7 @@
     cmsSigViewingConditionsTag              = 0x76696577,  // 'view'
     cmsSigVcgtTag                           = 0x76636774,  // 'vcgt'
     cmsSigMetaTag                           = 0x6D657461,  // 'meta'
+    cmsSigcicpTag                           = 0x63696370,  // 'cicp'
     cmsSigArgyllArtsTag                     = 0x61727473   // 'arts'
 
 } cmsTagSignature;
@@ -656,9 +668,10 @@
 // Format of pixel is defined by one cmsUInt32Number, using bit fields as follows
 //
 //                               2                1          0
-//                          3 2 10987 6 5 4 3 2 1 098 7654 321
-//                          A O TTTTT U Y F P X S EEE CCCC BBB
+//                        4 3 2 10987 6 5 4 3 2 1 098 7654 321
+//                        M A O TTTTT U Y F P X S EEE CCCC BBB
 //
+//            M: Premultiplied alpha (only works when extra samples is 1)
 //            A: Floating point -- With this flag we can differentiate 16 bits as float and as int
 //            O: Optimized -- previous optimization already returns the final 8-bit value
 //            T: Pixeltype
@@ -671,6 +684,7 @@
 //            B: bytes per sample
 //            Y: Swap first - changes ABGR to BGRA and KCMY to CMYK
 
+#define PREMUL_SH(m)           ((m) << 23)
 #define FLOAT_SH(a)            ((a) << 22)
 #define OPTIMIZED_SH(s)        ((s) << 21)
 #define COLORSPACE_SH(s)       ((s) << 16)
@@ -684,6 +698,7 @@
 #define BYTES_SH(b)            (b)
 
 // These macros unpack format specifiers into integers
+#define T_PREMUL(m)           (((m)>>23)&1)
 #define T_FLOAT(a)            (((a)>>22)&1)
 #define T_OPTIMIZED(o)        (((o)>>21)&1)
 #define T_COLORSPACE(s)       (((s)>>16)&31)
@@ -712,7 +727,6 @@
 #define PT_HSV       12
 #define PT_HLS       13
 #define PT_Yxy       14
-
 #define PT_MCH1      15
 #define PT_MCH2      16
 #define PT_MCH3      17
@@ -728,7 +742,6 @@
 #define PT_MCH13     27
 #define PT_MCH14     28
 #define PT_MCH15     29
-
 #define PT_LabV2     30     // Identical to PT_Lab, but using the V2 old encoding
 
 // Some (not all!) representations
@@ -742,7 +755,9 @@
 #define TYPE_GRAY_16_REV       (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1))
 #define TYPE_GRAY_16_SE        (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1))
 #define TYPE_GRAYA_8           (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1))
+#define TYPE_GRAYA_8_PREMUL    (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1)|PREMUL_SH(1))
 #define TYPE_GRAYA_16          (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2))
+#define TYPE_GRAYA_16_PREMUL   (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|PREMUL_SH(1))
 #define TYPE_GRAYA_16_SE       (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1))
 #define TYPE_GRAYA_8_PLANAR    (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1)|PLANAR_SH(1))
 #define TYPE_GRAYA_16_PLANAR   (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|PLANAR_SH(1))
@@ -759,24 +774,32 @@
 #define TYPE_BGR_16_SE         (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1))
 
 #define TYPE_RGBA_8            (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1))
+#define TYPE_RGBA_8_PREMUL     (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|PREMUL_SH(1))
 #define TYPE_RGBA_8_PLANAR     (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1))
 #define TYPE_RGBA_16           (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2))
+#define TYPE_RGBA_16_PREMUL    (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|PREMUL_SH(1))
 #define TYPE_RGBA_16_PLANAR    (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1))
 #define TYPE_RGBA_16_SE        (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1))
 
 #define TYPE_ARGB_8            (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1))
+#define TYPE_ARGB_8_PREMUL     (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1)|PREMUL_SH(1))
 #define TYPE_ARGB_8_PLANAR     (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1)|PLANAR_SH(1))
 #define TYPE_ARGB_16           (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|SWAPFIRST_SH(1))
+#define TYPE_ARGB_16_PREMUL    (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|SWAPFIRST_SH(1)|PREMUL_SH(1))
 
 #define TYPE_ABGR_8            (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1))
+#define TYPE_ABGR_8_PREMUL     (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|PREMUL_SH(1))
 #define TYPE_ABGR_8_PLANAR     (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|PLANAR_SH(1))
 #define TYPE_ABGR_16           (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1))
+#define TYPE_ABGR_16_PREMUL    (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|PREMUL_SH(1))
 #define TYPE_ABGR_16_PLANAR    (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|PLANAR_SH(1))
 #define TYPE_ABGR_16_SE        (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1))
 
 #define TYPE_BGRA_8            (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1))
+#define TYPE_BGRA_8_PREMUL     (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|PREMUL_SH(1))
 #define TYPE_BGRA_8_PLANAR     (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|PLANAR_SH(1))
 #define TYPE_BGRA_16           (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1))
+#define TYPE_BGRA_16_PREMUL    (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|PREMUL_SH(1))
 #define TYPE_BGRA_16_SE        (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1))
 
 #define TYPE_CMY_8             (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(1))
@@ -893,7 +916,7 @@
 #define TYPE_HSV_16_PLANAR     (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1))
 #define TYPE_HSV_16_SE         (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1))
 
-// Named color index. Only 16 bits allowed (don't check colorspace)
+// Named color index. Only 16 bits is allowed (don't check colorspace)
 #define TYPE_NAMED_COLOR_INDEX (CHANNELS_SH(1)|BYTES_SH(2))
 
 // Float formatters.
@@ -901,13 +924,19 @@
 #define TYPE_Lab_FLT          (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(4))
 #define TYPE_LabA_FLT         (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4))
 #define TYPE_GRAY_FLT         (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(4))
+#define TYPE_GRAYA_FLT        (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(4)|EXTRA_SH(1))
+#define TYPE_GRAYA_FLT_PREMUL (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(4)|EXTRA_SH(1)|PREMUL_SH(1))
 #define TYPE_RGB_FLT          (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4))
 
 #define TYPE_RGBA_FLT         (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4))
+#define TYPE_RGBA_FLT_PREMUL  (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|PREMUL_SH(1))
 #define TYPE_ARGB_FLT         (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|SWAPFIRST_SH(1))
+#define TYPE_ARGB_FLT_PREMUL  (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|SWAPFIRST_SH(1)|PREMUL_SH(1))
 #define TYPE_BGR_FLT          (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1))
 #define TYPE_BGRA_FLT         (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)|SWAPFIRST_SH(1))
+#define TYPE_BGRA_FLT_PREMUL  (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|PREMUL_SH(1))
 #define TYPE_ABGR_FLT         (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1))
+#define TYPE_ABGR_FLT_PREMUL  (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)|PREMUL_SH(1))
 
 #define TYPE_CMYK_FLT         (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(4))
 
@@ -1011,6 +1040,16 @@
 
     } cmsICCViewingConditions;
 
+typedef struct {
+    cmsUInt8Number  ColourPrimaries;            // Recommendation ITU-T H.273
+    cmsUInt8Number  TransferCharacteristics;    //  (ISO/IEC 23091-2)
+    cmsUInt8Number  MatrixCoefficients;
+    cmsUInt8Number  VideoFullRangeFlag;
+
+} cmsVideoSignalType;
+
+
+
 // Get LittleCMS version (for shared objects) -----------------------------------------------------------------------------
 
 CMSAPI int               CMSEXPORT cmsGetEncodedCMMversion(void);
@@ -1029,7 +1068,7 @@
 typedef struct _cmsContext_struct* cmsContext;
 
 CMSAPI cmsContext       CMSEXPORT cmsCreateContext(void* Plugin, void* UserData);
-CMSAPI void             CMSEXPORT cmsDeleteContext(cmsContext ContexID);
+CMSAPI void             CMSEXPORT cmsDeleteContext(cmsContext ContextID);
 CMSAPI cmsContext       CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData);
 CMSAPI void*            CMSEXPORT cmsGetContextUserData(cmsContext ContextID);
 
@@ -1181,6 +1220,7 @@
 CMSAPI cmsBool           CMSEXPORT cmsIsToneCurveDescending(const cmsToneCurve* t);
 CMSAPI cmsInt32Number    CMSEXPORT cmsGetToneCurveParametricType(const cmsToneCurve* t);
 CMSAPI cmsFloat64Number  CMSEXPORT cmsEstimateGamma(const cmsToneCurve* t, cmsFloat64Number Precision);
+CMSAPI cmsFloat64Number* CMSEXPORT cmsGetToneCurveParams(const cmsToneCurve* t);
 
 // Tone curve tabular estimation
 CMSAPI cmsUInt32Number         CMSEXPORT cmsGetToneCurveEstimatedTableEntries(const cmsToneCurve* t);
@@ -1245,21 +1285,22 @@
 CMSAPI cmsUInt32Number   CMSEXPORT cmsStageOutputChannels(const cmsStage* mpe);
 CMSAPI cmsStageSignature CMSEXPORT cmsStageType(const cmsStage* mpe);
 CMSAPI void*             CMSEXPORT cmsStageData(const cmsStage* mpe);
+CMSAPI cmsContext        CMSEXPORT cmsGetStageContextID(const cmsStage* mpe);
 
 // Sampling
-typedef cmsInt32Number (*cmsSAMPLER16)(const cmsUInt16Number In[],
-                                       cmsUInt16Number Out[],
-                                       void* Cargo);
+typedef cmsInt32Number (* cmsSAMPLER16)   (CMSREGISTER const cmsUInt16Number In[],
+                                           CMSREGISTER cmsUInt16Number Out[],
+                                           CMSREGISTER void * Cargo);
 
-typedef cmsInt32Number (*cmsSAMPLERFLOAT)(const cmsFloat32Number In[],
-                                          cmsFloat32Number Out[],
-                                          void* Cargo);
+typedef cmsInt32Number (* cmsSAMPLERFLOAT)(CMSREGISTER const cmsFloat32Number In[],
+                                           CMSREGISTER cmsFloat32Number Out[],
+                                           CMSREGISTER void * Cargo);
 
 // Use this flag to prevent changes being written to destination
 #define SAMPLER_INSPECT     0x01000000
 
 // For CLUT only
-CMSAPI cmsBool           CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe,    cmsSAMPLER16 Sampler, void* Cargo, cmsUInt32Number dwFlags);
+CMSAPI cmsBool           CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe, cmsSAMPLER16 Sampler, void* Cargo, cmsUInt32Number dwFlags);
 CMSAPI cmsBool           CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler, void* Cargo, cmsUInt32Number dwFlags);
 
 // Slicers
@@ -1491,8 +1532,12 @@
 CMSAPI cmsColorSpaceSignature   CMSEXPORT _cmsICCcolorSpace(int OurNotation);
 CMSAPI int                      CMSEXPORT _cmsLCMScolorSpace(cmsColorSpaceSignature ProfileSpace);
 
+// Deprecated, use cmsChannelsOfColorSpace instead
 CMSAPI cmsUInt32Number   CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace);
 
+// Get number of channels of color space or -1 if color space is not listed/supported
+CMSAPI cmsInt32Number CMSEXPORT cmsChannelsOfColorSpace(cmsColorSpaceSignature ColorSpace);
+
 // Build a suitable formatter for the colorspace of this profile. nBytes=1 means 8 bits, nBytes=2 means 16 bits. 
 CMSAPI cmsUInt32Number   CMSEXPORT cmsFormatterForColorspaceOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat);
 CMSAPI cmsUInt32Number   CMSEXPORT cmsFormatterForPCSOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat);
@@ -1644,7 +1689,7 @@
 // Misc
 #define cmsFLAGS_BLACKPOINTCOMPENSATION   0x2000
 #define cmsFLAGS_NOWHITEONWHITEFIXUP      0x0004    // Don't fix scum dot
-#define cmsFLAGS_HIGHRESPRECALC           0x0400    // Use more memory to give better accurancy
+#define cmsFLAGS_HIGHRESPRECALC           0x0400    // Use more memory to give better accuracy
 #define cmsFLAGS_LOWRESPRECALC            0x0800    // Use less memory to minimize resources
 
 // For devicelink creation
@@ -1895,6 +1940,8 @@
 // Estimate total area coverage
 CMSAPI cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile);
 
+// Estimate gamma space, always positive. Returns -1 on error.
+CMSAPI cmsFloat64Number CMSEXPORT cmsDetectRGBProfileGamma(cmsHPROFILE hProfile, cmsFloat64Number threshold);
 
 // Poor man's gamut mapping
 CMSAPI cmsBool          CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab,
diff --git a/third_party/lcms/include/lcms2_plugin.h b/third_party/lcms/include/lcms2_plugin.h
index 17bec42..906defb 100644
--- a/third_party/lcms/include/lcms2_plugin.h
+++ b/third_party/lcms/include/lcms2_plugin.h
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -24,7 +24,7 @@
 //---------------------------------------------------------------------------------
 //
 // This is the plug-in header file. Normal LittleCMS clients should not use it.
-// It is provided for plug-in writters that may want to access the support
+// It is provided for plug-in writers that may want to access the support
 // functions to do low level operations. All plug-in related structures
 // are defined here. Including this file forces to include the standard API too.
 
@@ -94,6 +94,12 @@
 CMSAPI void               CMSEXPORT _cmsMAT3eval(cmsVEC3* r, const cmsMAT3* a, const cmsVEC3* v);
 
 
+// MD5 low level  -------------------------------------------------------------------------------------
+
+CMSAPI cmsHANDLE          CMSEXPORT cmsMD5alloc(cmsContext ContextID);
+CMSAPI void               CMSEXPORT cmsMD5add(cmsHANDLE Handle, const cmsUInt8Number* buf, cmsUInt32Number len);
+CMSAPI void               CMSEXPORT cmsMD5finish(cmsProfileID* ProfileID, cmsHANDLE Handle);
+
 // Error logging  -------------------------------------------------------------------------------------
 
 CMSAPI void               CMSEXPORT  cmsSignalError(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *ErrorText, ...);
@@ -203,6 +209,7 @@
 #define cmsPluginOptimizationSig             0x6F707448     // 'optH'
 #define cmsPluginTransformSig                0x7A666D48     // 'xfmH'
 #define cmsPluginMutexSig                    0x6D747A48     // 'mtxH'
+#define cmsPluginParalellizationSig          0x70726C48     // 'prlH
 
 typedef struct _cmsPluginBaseStruct {
 
@@ -255,9 +262,9 @@
 // 16 bits forward interpolation. This function performs precision-limited linear interpolation
 // and is supposed to be quite fast. Implementation may be tetrahedral or trilinear, and plug-ins may
 // choose to implement any other interpolation algorithm.
-typedef void (* _cmsInterpFn16)(register const cmsUInt16Number Input[],
-                                register cmsUInt16Number Output[],
-                                register const struct _cms_interp_struc* p);
+typedef void (* _cmsInterpFn16)(CMSREGISTER const cmsUInt16Number Input[],
+                                CMSREGISTER cmsUInt16Number Output[],
+                                CMSREGISTER const struct _cms_interp_struc* p);
 
 // Floating point forward interpolation. Full precision interpolation using floats. This is not a
 // time critical function. Implementation may be tetrahedral or trilinear, and plug-ins may
@@ -280,7 +287,7 @@
 #define CMS_LERP_FLAGS_TRILINEAR          0x0100        // Hint only
 
 
-#define MAX_INPUT_DIMENSIONS 8
+#define MAX_INPUT_DIMENSIONS 15
 
 typedef struct _cms_interp_struc {  // Used on all interpolations. Supplied by lcms2 when calling the interpolation function
 
@@ -340,10 +347,10 @@
 
 struct _cmstransform_struct;
 
-typedef cmsUInt8Number* (* cmsFormatter16)(register struct _cmstransform_struct* CMMcargo,
-                                           register cmsUInt16Number Values[],
-                                           register cmsUInt8Number* Buffer,
-                                           register cmsUInt32Number Stride);
+typedef cmsUInt8Number* (* cmsFormatter16)(CMSREGISTER struct _cmstransform_struct* CMMcargo,
+                                           CMSREGISTER cmsUInt16Number Values[],
+                                           CMSREGISTER cmsUInt8Number* Buffer,
+                                           CMSREGISTER cmsUInt32Number Stride);
 
 typedef cmsUInt8Number* (* cmsFormatterFloat)(struct _cmstransform_struct* CMMcargo,
                                               cmsFloat32Number Values[],
@@ -541,22 +548,28 @@
 // the optimization  search. Or FALSE if it is unable to optimize and want to give a chance
 // to the rest of optimizers.
 
-typedef void     (* _cmsOPTeval16Fn)(register const cmsUInt16Number In[],
-                                     register cmsUInt16Number Out[],
-                                     register const void* Data);
-
-
 typedef cmsBool  (* _cmsOPToptimizeFn)(cmsPipeline** Lut,
                                        cmsUInt32Number  Intent,
                                        cmsUInt32Number* InputFormat,
                                        cmsUInt32Number* OutputFormat,
                                        cmsUInt32Number* dwFlags);
 
+// Pipeline Evaluator (in 16 bits)
+typedef void (* _cmsPipelineEval16Fn)(CMSREGISTER const cmsUInt16Number In[],
+                                     CMSREGISTER cmsUInt16Number Out[],
+                                     const void* Data);
+
+// Pipeline Evaluator (in floating point)
+typedef void (* _cmsPipelineEvalFloatFn)(const cmsFloat32Number In[],
+                                         cmsFloat32Number Out[],
+                                         const void* Data);
+
+
 // This function may be used to set the optional evaluator and a block of private data. If private data is being used, an optional
 // duplicator and free functions should also be specified in order to duplicate the LUT construct. Use NULL to inhibit such functionality.
 
 CMSAPI void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut,
-                                               _cmsOPTeval16Fn Eval16,
+                                               _cmsPipelineEval16Fn Eval16,
                                                void* PrivateData,
                                                _cmsFreeUserDataFn FreePrivateDataFn,
                                                _cmsDupUserDataFn DupPrivateDataFn);
@@ -584,7 +597,7 @@
                                      const void* InputBuffer,
                                      void* OutputBuffer,
                                      cmsUInt32Number Size,
-                                     cmsUInt32Number Stride);                 // Stride in bytes to the next plana in planar formats
+                                     cmsUInt32Number Stride);                 // Stride in bytes to the next plane in planar formats
 
 
 typedef void     (*_cmsTransform2Fn)(struct _cmstransform_struct *CMMcargo,
@@ -620,6 +633,9 @@
 CMSAPI void   CMSEXPORT _cmsGetTransformFormatters16   (struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput);
 CMSAPI void   CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput);
 
+// Retrieve original flags
+CMSAPI cmsUInt32Number CMSEXPORT _cmsGetTransformFlags(struct _cmstransform_struct* CMMcargo);
+
 typedef struct {
       cmsPluginBase     base;
 
@@ -654,6 +670,25 @@
 CMSAPI cmsBool CMSEXPORT _cmsLockMutex(cmsContext ContextID, void* mtx);
 CMSAPI void    CMSEXPORT _cmsUnlockMutex(cmsContext ContextID, void* mtx);
 
+//----------------------------------------------------------------------------------------------------------
+// Parallelization 
+
+CMSAPI _cmsTransform2Fn CMSEXPORT _cmsGetTransformWorker(struct _cmstransform_struct* CMMcargo);
+CMSAPI cmsInt32Number   CMSEXPORT _cmsGetTransformMaxWorkers(struct _cmstransform_struct* CMMcargo);
+CMSAPI cmsUInt32Number  CMSEXPORT _cmsGetTransformWorkerFlags(struct _cmstransform_struct* CMMcargo);
+
+// Let's plug-in to guess the best number of workers
+#define CMS_GUESS_MAX_WORKERS -1
+
+typedef struct {
+    cmsPluginBase       base;
+
+    cmsInt32Number      MaxWorkers;       // Number of starts to do as maximum
+    cmsUInt32Number     WorkerFlags;      // Reserved
+    _cmsTransform2Fn    SchedulerFn;      // callback to setup functions     
+
+}  cmsPluginParalellization;
+
 
 #ifndef CMS_USE_CPP_API
 #   ifdef __cplusplus
diff --git a/third_party/lcms/src/cmsalpha.c b/third_party/lcms/src/cmsalpha.c
index 3e6b7c3..7c6a6d2 100644
--- a/third_party/lcms/src/cmsalpha.c
+++ b/third_party/lcms/src/cmsalpha.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -28,6 +28,10 @@
 
 // Alpha copy ------------------------------------------------------------------------------------------------------------------
 
+// This macro return words stored as big endian
+#define CHANGE_ENDIAN(w)    (cmsUInt16Number) ((cmsUInt16Number) ((w)<<8)|((w)>>8))
+
+
 // Floor to byte, taking care of saturation
 cmsINLINE cmsUInt8Number _cmsQuickSaturateByte(cmsFloat64Number d)
 {
@@ -71,19 +75,26 @@
 void from8to16(void* dst, const void* src)
 {
        cmsUInt8Number n = *(cmsUInt8Number*)src;
-       *(cmsUInt16Number*) dst = FROM_8_TO_16(n);
+       *(cmsUInt16Number*) dst = (cmsUInt16Number) FROM_8_TO_16(n);
+}
+
+static
+void from8to16SE(void* dst, const void* src)
+{
+    cmsUInt8Number n = *(cmsUInt8Number*)src;    
+    *(cmsUInt16Number*)dst = CHANGE_ENDIAN(FROM_8_TO_16(n));
 }
 
 static
 void from8toFLT(void* dst, const void* src)
 {
-       *(cmsFloat32Number*)dst = (*(cmsUInt8Number*)src) / 255.0f;
+       *(cmsFloat32Number*)dst = (cmsFloat32Number) (*(cmsUInt8Number*)src) / 255.0f;
 }
 
 static
 void from8toDBL(void* dst, const void* src)
 {
-       *(cmsFloat64Number*)dst = (*(cmsUInt8Number*)src) / 255.0;
+       *(cmsFloat64Number*)dst = (cmsFloat64Number) (*(cmsUInt8Number*)src) / 255.0;
 }
 
 static
@@ -108,19 +119,47 @@
 }
 
 static
+void from16SEto8(void* dst, const void* src)
+{
+    cmsUInt16Number n = *(cmsUInt16Number*)src;
+    *(cmsUInt8Number*)dst = FROM_16_TO_8(CHANGE_ENDIAN(n));
+}
+
+static
 void copy16(void* dst, const void* src)
 {
        memmove(dst, src, 2);
 }
 
+static
+void from16to16(void* dst, const void* src)
+{
+    cmsUInt16Number n = *(cmsUInt16Number*)src;
+    *(cmsUInt16Number*)dst = CHANGE_ENDIAN(n);
+}
+
+static
 void from16toFLT(void* dst, const void* src)
 {
        *(cmsFloat32Number*)dst = (*(cmsUInt16Number*)src) / 65535.0f;
 }
 
+static
+void from16SEtoFLT(void* dst, const void* src)
+{
+    *(cmsFloat32Number*)dst = (CHANGE_ENDIAN(*(cmsUInt16Number*)src)) / 65535.0f;
+}
+
+static
 void from16toDBL(void* dst, const void* src)
 {
-       *(cmsFloat64Number*)dst = (*(cmsUInt16Number*)src) / 65535.0f;
+       *(cmsFloat64Number*)dst = (cmsFloat64Number) (*(cmsUInt16Number*)src) / 65535.0;
+}
+
+static
+void from16SEtoDBL(void* dst, const void* src)
+{
+    *(cmsFloat64Number*)dst = (cmsFloat64Number) (CHANGE_ENDIAN(*(cmsUInt16Number*)src)) / 65535.0;
 }
 
 static
@@ -135,33 +174,53 @@
 #endif
 }
 
+static
+void from16SEtoHLF(void* dst, const void* src)
+{
+#ifndef CMS_NO_HALF_SUPPORT
+    cmsFloat32Number n = (CHANGE_ENDIAN(*(cmsUInt16Number*)src)) / 65535.0f;
+    *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
+#else
+    cmsUNUSED_PARAMETER(dst);
+    cmsUNUSED_PARAMETER(src);
+#endif
+}
 // From Float
 
-static 
+static
 void fromFLTto8(void* dst, const void* src)
 {
-       cmsFloat32Number n = *(cmsFloat32Number*)src;   
-       *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f);
+    cmsFloat32Number n = *(cmsFloat32Number*)src;
+    *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0);
 }
 
 static
 void fromFLTto16(void* dst, const void* src)
 {
-       cmsFloat32Number n = *(cmsFloat32Number*)src;      
-       *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f);
+    cmsFloat32Number n = *(cmsFloat32Number*)src;
+    *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0);
+}
+
+static
+void fromFLTto16SE(void* dst, const void* src)
+{
+    cmsFloat32Number n = *(cmsFloat32Number*)src;
+    cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0);
+
+    *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i);
 }
 
 static
 void copy32(void* dst, const void* src)
 {
-       memmove(dst, src, sizeof(cmsFloat32Number));
+    memmove(dst, src, sizeof(cmsFloat32Number));
 }
 
 static
 void fromFLTtoDBL(void* dst, const void* src)
 {
-       cmsFloat32Number n = *(cmsFloat32Number*)src;
-       *(cmsFloat64Number*)dst = (cmsFloat64Number)n;
+    cmsFloat32Number n = *(cmsFloat32Number*)src;
+    *(cmsFloat64Number*)dst = (cmsFloat64Number)n;
 }
 
 static
@@ -184,7 +243,7 @@
 {
 #ifndef CMS_NO_HALF_SUPPORT
        cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
-       *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f);
+       *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0);
 #else
     cmsUNUSED_PARAMETER(dst);
     cmsUNUSED_PARAMETER(src);
@@ -197,7 +256,20 @@
 {
 #ifndef CMS_NO_HALF_SUPPORT
        cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
-       *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f);
+       *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0);
+#else
+    cmsUNUSED_PARAMETER(dst);
+    cmsUNUSED_PARAMETER(src);
+#endif
+}
+
+static
+void fromHLFto16SE(void* dst, const void* src)
+{
+#ifndef CMS_NO_HALF_SUPPORT
+    cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
+    cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0);
+    *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i);
 #else
     cmsUNUSED_PARAMETER(dst);
     cmsUNUSED_PARAMETER(src);
@@ -242,6 +314,14 @@
 }
 
 static
+void fromDBLto16SE(void* dst, const void* src)
+{
+    cmsFloat64Number n = *(cmsFloat64Number*)src;
+    cmsUInt16Number  i = _cmsQuickSaturateWord(n * 65535.0f);
+    *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i);
+}
+
+static
 void fromDBLtoFLT(void* dst, const void* src)
 {
        cmsFloat64Number n = *(cmsFloat64Number*)src;
@@ -274,37 +354,42 @@
     cmsUInt32Number  b = T_BYTES(frm);
 
     if (b == 0 && T_FLOAT(frm))
-        return 4; // DBL
+        return 5; // DBL
 #ifndef CMS_NO_HALF_SUPPORT
     if (b == 2 && T_FLOAT(frm))
-        return 2; // HLF
+        return 3; // HLF
 #endif
     if (b == 4 && T_FLOAT(frm))
-        return 3; // FLT
+        return 4; // FLT
     if (b == 2 && !T_FLOAT(frm))
-        return 1; // 16
+    {
+        if (T_ENDIAN16(frm))
+            return 2; // 16SE
+        else
+            return 1; // 16
+    }
     if (b == 1 && !T_FLOAT(frm))
         return 0; // 8
-
     return -1; // not recognized
 }
 
-// Obtains a alpha-to-alpha funmction formatter
+// Obtains an alpha-to-alpha function formatter
 static
 cmsFormatterAlphaFn _cmsGetFormatterAlpha(cmsContext id, cmsUInt32Number in, cmsUInt32Number out)
 {
-static const cmsFormatterAlphaFn FormattersAlpha[5][5] = {
+static const cmsFormatterAlphaFn FormattersAlpha[6][6] = {
 
-       /* from 8 */  { copy8,      from8to16,   from8toHLF,   from8toFLT,   from8toDBL   },
-       /* from 16*/  { from16to8,  copy16,      from16toHLF,  from16toFLT,  from16toDBL  },
-       /* from HLF*/ { fromHLFto8, fromHLFto16, copy16,       fromHLFtoFLT, fromHLFtoDBL },
-       /* from FLT*/ { fromFLTto8, fromFLTto16, fromFLTtoHLF, copy32,       fromFLTtoDBL },
-       /* from DBL*/ { fromDBLto8, fromDBLto16, fromDBLtoHLF, fromDBLtoFLT, copy64 }};
+       /* from 8 */  { copy8,       from8to16,   from8to16SE,   from8toHLF,   from8toFLT,    from8toDBL    },
+       /* from 16*/  { from16to8,   copy16,      from16to16,    from16toHLF,  from16toFLT,   from16toDBL   },
+       /* from 16SE*/{ from16SEto8, from16to16,  copy16,        from16SEtoHLF,from16SEtoFLT, from16SEtoDBL },
+       /* from HLF*/ { fromHLFto8,  fromHLFto16, fromHLFto16SE, copy16,       fromHLFtoFLT,  fromHLFtoDBL  },
+       /* from FLT*/ { fromFLTto8,  fromFLTto16, fromFLTto16SE, fromFLTtoHLF, copy32,        fromFLTtoDBL  },
+       /* from DBL*/ { fromDBLto8,  fromDBLto16, fromDBLto16SE, fromDBLtoHLF, fromDBLtoFLT,  copy64 }};
 
         int in_n  = FormatterPos(in);
         int out_n = FormatterPos(out);
 
-        if (in_n < 0 || out_n < 0 || in_n > 4 || out_n > 4) {
+        if (in_n < 0 || out_n < 0 || in_n > 5 || out_n > 5) {
 
                cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized alpha channel width");
                return NULL;
@@ -329,9 +414,9 @@
        cmsUInt32Number channelSize = trueBytesSize(Format);
        cmsUInt32Number pixelSize = channelSize * total_chans;
        
-	   // Sanity check
-	   if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS)
-		   return;
+       // Sanity check
+       if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS)
+           return;
 
         memset(channels, 0, sizeof(channels));
 
@@ -486,6 +571,8 @@
 
     // Check for conversions 8, 16, half, float, dbl
     copyValueFn = _cmsGetFormatterAlpha(p->ContextID, p->InputFormat, p->OutputFormat);
+    if (copyValueFn == NULL) 
+        return;
 
     if (nExtra == 1) { // Optimized routine for copying a single extra channel quickly
 
diff --git a/third_party/lcms/src/cmscam02.c b/third_party/lcms/src/cmscam02.c
index 9cc49fb..3f080a1 100644
--- a/third_party/lcms/src/cmscam02.c
+++ b/third_party/lcms/src/cmscam02.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -324,7 +324,7 @@
     M[5] = ((-0.7036 *  0.201908) + (1.6975 * 0.000008) + 0.0061);
     M[6] = (( 0.0030 *  1.910197) + (0.0136 * 0.370950));
     M[7] = (( 0.0030 * -1.112124) + (0.0136 * 0.629054));
-    M[8] = (( 0.0030 *  0.201908) + (0.0136 * 0.000008) + 0.9834);;
+    M[8] = (( 0.0030 *  0.201908) + (0.0136 * 0.000008) + 0.9834);
 
     clr.RGBc[0] = (clr.RGBp[0] * M[0]) + (clr.RGBp[1] * M[1]) + (clr.RGBp[2] * M[2]);
     clr.RGBc[1] = (clr.RGBp[0] * M[3]) + (clr.RGBp[1] * M[4]) + (clr.RGBp[2] * M[5]);
diff --git a/third_party/lcms/src/cmscgats.c b/third_party/lcms/src/cmscgats.c
index dba77f1..7169264 100644
--- a/third_party/lcms/src/cmscgats.c
+++ b/third_party/lcms/src/cmscgats.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -132,13 +132,22 @@
 
 // File stream being parsed
 typedef struct _FileContext {
-        char           FileName[cmsMAX_PATH];    // File name if being readed from file
+        char           FileName[cmsMAX_PATH];    // File name if being read from file
         FILE*          Stream;                   // File stream or NULL if holded in memory
     } FILECTX;
 
-// This struct hold all information about an open IT8 handler.
+//Very simple string 
 typedef struct {
 
+        struct struct_it8* it8;
+        cmsInt32Number max;
+        cmsInt32Number len;
+        char* begin;
+    } string;
+
+
+// This struct hold all information about an open IT8 handler.
+typedef struct struct_it8 {
 
         cmsUInt32Number  TablesCount;                     // How many tables in this stream
         cmsUInt32Number  nTable;                          // The actual table
@@ -156,8 +165,8 @@
         cmsInt32Number     inum;              // integer value
         cmsFloat64Number   dnum;              // real value
 
-        char           id[MAXID];             // identifier
-        char           str[MAXSTR];           // string
+        string*        id;            // identifier
+        string*        str;           // string
 
         // Allowed keywords & datasets. They have visibility on whole stream
         KEYVALUE*      ValidKeywords;
@@ -242,7 +251,7 @@
 
         {"MATERIAL",         WRITE_STRINGIFY},    // Identifies the material on which the target was produced using a code
                                                   // uniquely identifying th e material. This is intend ed to be used for IT8.7
-                                                  // physical targets only (i.e . IT8.7/1 a nd IT8.7/2).
+                                                  // physical targets only (i.e . IT8.7/1 and IT8.7/2).
 
         {"INSTRUMENTATION",  WRITE_STRINGIFY},    // Used to report the specific instrumentation used (manufacturer and
                                                   // model number) to generate the data reported. This data will often
@@ -274,7 +283,7 @@
                                                    // denote the use of filters such as none, D65, Red, Green or Blue.
                                                   
        {"POLARIZATION",      WRITE_STRINGIFY},     // Identifies the use of a physical polarization filter during measurement. Allowed
-                                                   // values are "yes", "white", "none" or "na".
+                                                   // values are {"yes", "white", "none" or "na".
 
        {"WEIGHTING_FUNCTION", WRITE_PAIR},         // Indicates such functions as: the CIE standard observer functions used in the
                                                    // calculation of various data parameters (2 degree and 10 degree), CIE standard
@@ -357,6 +366,65 @@
 //Forward declaration of some internal functions
 static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size);
 
+static
+string* StringAlloc(cmsIT8* it8, int max)
+{
+    string* s = (string*) AllocChunk(it8, sizeof(string));
+    if (s == NULL) return NULL;
+
+    s->it8 = it8;
+    s->max = max;
+    s->len = 0;
+    s->begin = (char*) AllocChunk(it8, s->max);
+
+    return s;
+}
+
+static
+void StringClear(string* s)
+{
+    s->len = 0;
+}
+
+static
+void StringAppend(string* s, char c)
+{
+    if (s->len + 1 >= s->max)
+    {
+        char* new_ptr;
+
+        s->max *= 10;
+        new_ptr = (char*) AllocChunk(s->it8, s->max);
+        if (new_ptr != NULL && s->begin != NULL)
+            memcpy(new_ptr, s->begin, s->len);
+
+        s->begin = new_ptr;
+    }
+
+    if (s->begin != NULL)
+    {
+        s->begin[s->len++] = c;
+        s->begin[s->len] = 0;
+    }
+}
+
+static
+char* StringPtr(string* s)
+{
+    return s->begin;
+}
+
+static
+void StringCat(string* s, const char* c)
+{
+    while (*c)
+    {
+        StringAppend(s, *c);
+        c++;
+    }
+}
+
+
 // Checks whatever c is a separator
 static
 cmsBool isseparator(int c)
@@ -682,14 +750,44 @@
 }
 
 
+// Reads a string, special case to avoid infinite resursion on .include
+static
+void InStringSymbol(cmsIT8* it8)
+{
+    while (isseparator(it8->ch))
+        NextCh(it8);
+
+    if (it8->ch == '\'' || it8->ch == '\"')
+    {
+        int sng;
+
+        sng = it8->ch;
+        StringClear(it8->str);
+
+        NextCh(it8);
+
+        while (it8->ch != sng) {
+
+            if (it8->ch == '\n' || it8->ch == '\r' || it8->ch == 0) break;
+            else {
+                StringAppend(it8->str, (char)it8->ch);
+                NextCh(it8);
+            }
+        }
+
+        it8->sy = SSTRING;
+        NextCh(it8);        
+    }
+    else
+        SynError(it8, "String expected");
+
+}
+
 // Reads next symbol
 static
 void InSymbol(cmsIT8* it8)
 {
-    register char *idptr;
-    register int k;
     SYMBOL key;
-    int sng;
     
     do {
 
@@ -698,21 +796,18 @@
 
         if (isfirstidchar(it8->ch)) {          // Identifier
 
-            k = 0;
-            idptr = it8->id;
+            StringClear(it8->id);
 
             do {
 
-                if (++k < MAXID) *idptr++ = (char) it8->ch;
+                StringAppend(it8->id, (char) it8->ch);
 
                 NextCh(it8);
 
             } while (isidchar(it8->ch));
 
-            *idptr = '\0';
 
-
-            key = BinSrchKey(it8->id);
+            key = BinSrchKey(StringPtr(it8->id));
             if (key == SUNDEFINED) it8->sy = SIDENT;
             else it8->sy = key;
 
@@ -747,6 +842,7 @@
                             if ((cmsFloat64Number) it8->inum * 16.0 + (cmsFloat64Number) j > (cmsFloat64Number)+2147483647.0)
                             {
                                 SynError(it8, "Invalid hexadecimal number");
+                                it8->sy = SEOF;
                                 return;
                             }
 
@@ -768,6 +864,7 @@
                             if ((cmsFloat64Number) it8->inum * 2.0 + j > (cmsFloat64Number)+2147483647.0)
                             {
                                 SynError(it8, "Invalid binary number");
+                                it8->sy = SEOF;
                                 return;
                             }
 
@@ -808,26 +905,27 @@
 
                 if (isidchar(it8 ->ch)) {
 
+                    char buffer[127];
+
                     if (it8 ->sy == SINUM) {
 
-                        snprintf(it8->id, 127, "%d", it8->inum);
+                        snprintf(buffer, sizeof(buffer), "%d", it8->inum);
                     }
                     else {
 
-                        snprintf(it8->id, 127, it8 ->DoubleFormatter, it8->dnum);
+                        snprintf(buffer, sizeof(buffer), it8 ->DoubleFormatter, it8->dnum);
                     }
 
-                    k = (int) strlen(it8 ->id);
-                    idptr = it8 ->id + k;
+                    StringCat(it8->id, buffer);
+
                     do {
 
-                        if (++k < MAXID) *idptr++ = (char) it8->ch;
+                        StringAppend(it8->id, (char) it8->ch);
 
                         NextCh(it8);
 
                     } while (isidchar(it8->ch));
 
-                    *idptr = '\0';
                     it8->sy = SIDENT;
                 }
                 return;
@@ -835,13 +933,9 @@
             }
             else
                 switch ((int) it8->ch) {
-
-        // EOF marker -- ignore it
-        case '\x1a':
-            NextCh(it8);
-            break;
-
+        
         // Eof stream markers
+        case '\x1a':
         case 0:
         case -1:
             it8->sy = SEOF;
@@ -875,29 +969,13 @@
         // String.
         case '\'':
         case '\"':
-            idptr = it8->str;
-            sng = it8->ch;
-            k = 0;
-            NextCh(it8);
-
-            while (k < (MAXSTR-1) && it8->ch != sng) {
-
-                if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1;
-                else {
-                    *idptr++ = (char) it8->ch;
-                    NextCh(it8);
-                    k++;
-                }
-            }
-
-            it8->sy = SSTRING;
-            *idptr = '\0';
-            NextCh(it8);
+            InStringSymbol(it8);
             break;
 
 
         default:
             SynError(it8, "Unrecognized character: 0x%x", it8 ->ch);
+            it8->sy = SEOF;
             return;
             }
 
@@ -912,24 +990,33 @@
                 if(it8 -> IncludeSP >= (MAXINCLUDE-1)) {
 
                     SynError(it8, "Too many recursion levels");
+                    it8->sy = SEOF;
                     return;
                 }
 
-                InSymbol(it8);
-                if (!Check(it8, SSTRING, "Filename expected")) return;
+                InStringSymbol(it8);
+                if (!Check(it8, SSTRING, "Filename expected"))
+                {
+                    it8->sy = SEOF;
+                    return;
+                }
 
                 FileNest = it8 -> FileStack[it8 -> IncludeSP + 1];
                 if(FileNest == NULL) {
 
                     FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));
-                    //if(FileNest == NULL)
-                    //  TODO: how to manage out-of-memory conditions?
+                    if (FileNest == NULL) {
+                        SynError(it8, "Out of memory");
+                        it8->sy = SEOF;
+                        return;
+                    }
                 }
 
-                if (BuildAbsolutePath(it8->str,
+                if (BuildAbsolutePath(StringPtr(it8->str),
                                       it8->FileStack[it8->IncludeSP]->FileName,
                                       FileNest->FileName, cmsMAX_PATH-1) == FALSE) {
                     SynError(it8, "File path too long");
+                    it8->sy = SEOF;
                     return;
                 }
 
@@ -937,6 +1024,7 @@
                 if (FileNest->Stream == NULL) {
 
                         SynError(it8, "File %s not found", FileNest->FileName);
+                        it8->sy = SEOF;
                         return;
                 }
                 it8->IncludeSP++;
@@ -987,12 +1075,12 @@
     case SEOLN:   // Empty value
                   Buffer[0]=0;
                   break;
-    case SIDENT:  strncpy(Buffer, it8->id, max);
+    case SIDENT:  strncpy(Buffer, StringPtr(it8->id), max);
                   Buffer[max-1]=0;
                   break;
     case SINUM:   snprintf(Buffer, max, "%d", it8 -> inum); break;
     case SDNUM:   snprintf(Buffer, max, it8->DoubleFormatter, it8 -> dnum); break;
-    case SSTRING: strncpy(Buffer, it8->str, max);
+    case SSTRING: strncpy(Buffer, StringPtr(it8->str), max);
                   Buffer[max-1] = 0;
                   break;
 
@@ -1097,9 +1185,12 @@
                 it8 ->Allocator.BlockSize = size;
 
         it8 ->Allocator.Used = 0;
-        it8 ->Allocator.Block = (cmsUInt8Number*)  AllocBigBlock(it8, it8 ->Allocator.BlockSize);
+        it8 ->Allocator.Block = (cmsUInt8Number*) AllocBigBlock(it8, it8 ->Allocator.BlockSize);       
     }
 
+    if (it8->Allocator.Block == NULL)
+        return NULL;
+
     ptr = it8 ->Allocator.Block + it8 ->Allocator.Used;
     it8 ->Allocator.Used += size;
 
@@ -1117,7 +1208,7 @@
 
 
     ptr = (char *) AllocChunk(it8, Size);
-    if (ptr) strncpy (ptr, str, Size-1);
+    if (ptr) memcpy(ptr, str, Size-1);
 
     return ptr;
 }
@@ -1316,6 +1407,9 @@
     it8->IncludeSP   = 0;
     it8 -> lineno = 1;
 
+    it8->id = StringAlloc(it8, MAXSTR);
+    it8->str = StringAlloc(it8, MAXSTR);
+
     strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
     cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17");
 
@@ -1437,28 +1531,45 @@
 
 // ----------------------------------------------------------------- Datasets
 
+// A safe atoi that returns 0 when NULL input is given
+static
+cmsInt32Number satoi(const char* b)
+{
+    int n;
+
+    if (b == NULL) return 0;
+
+    n = atoi(b);
+    if (n > 0x7fffffffL) return 0x7fffffffL;
+    if (n < -0x7ffffffeL) return -0x7ffffffeL;
+
+    return (cmsInt32Number)n;
+}
+
 
 static
-void AllocateDataFormat(cmsIT8* it8)
+cmsBool AllocateDataFormat(cmsIT8* it8)
 {
     TABLE* t = GetTable(it8);
 
-    if (t -> DataFormat) return;    // Already allocated
+    if (t -> DataFormat) return TRUE;    // Already allocated
 
-    t -> nSamples  = (int) cmsIT8GetPropertyDbl(it8, "NUMBER_OF_FIELDS");
+    t -> nSamples  = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
 
     if (t -> nSamples <= 0) {
 
         SynError(it8, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS");
-        t -> nSamples = 10;
+        return FALSE;        
         }
 
     t -> DataFormat = (char**) AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * sizeof(char *));
     if (t->DataFormat == NULL) {
 
         SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array");
+        return FALSE;
     }
 
+    return TRUE;
 }
 
 static
@@ -1477,8 +1588,11 @@
 {
     TABLE* t = GetTable(it8);
 
-    if (!t->DataFormat)
-        AllocateDataFormat(it8);
+    if (!t->DataFormat) {
+
+        if (!AllocateDataFormat(it8))
+            return FALSE;
+    }
 
     if (n > t -> nSamples) {
         SynError(it8, "More than NUMBER_OF_FIELDS fields.");
@@ -1487,6 +1601,7 @@
 
     if (t->DataFormat) {
         t->DataFormat[n] = AllocString(it8, label);
+        if (t->DataFormat[n] == NULL) return FALSE;
     }
 
     return TRUE;
@@ -1499,28 +1614,51 @@
     return SetDataFormat(it8, n, Sample);
 }
 
+// Convert to binary
 static
-void AllocateDataSet(cmsIT8* it8)
+const char* satob(const char* v)
+{
+    cmsUInt32Number x;
+    static char buf[33];
+    char *s = buf + 33;
+    
+    if (v == NULL) return "0";
+    
+    x = atoi(v);
+    *--s = 0;
+    if (!x) *--s = '0';
+    for (; x; x /= 2) *--s = '0' + x%2;
+    
+    return s;
+}
+
+
+static
+cmsBool AllocateDataSet(cmsIT8* it8)
 {
     TABLE* t = GetTable(it8);
 
-    if (t -> Data) return;    // Already allocated
+    if (t -> Data) return TRUE;    // Already allocated
 
-    t-> nSamples   = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
-    t-> nPatches   = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
+    t-> nSamples   = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
+    t-> nPatches   = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
 
     if (t -> nSamples < 0 || t->nSamples > 0x7ffe || t->nPatches < 0 || t->nPatches > 0x7ffe)
     {
         SynError(it8, "AllocateDataSet: too much data");
+        return FALSE;
     }
     else {
+        // Some dumb analizers warns of possible overflow here, just take a look couple of lines above.
         t->Data = (char**)AllocChunk(it8, ((cmsUInt32Number)t->nSamples + 1) * ((cmsUInt32Number)t->nPatches + 1) * sizeof(char*));
         if (t->Data == NULL) {
 
             SynError(it8, "AllocateDataSet: Unable to allocate data array");
+            return FALSE;
         }
     }
 
+    return TRUE;
 }
 
 static
@@ -1542,8 +1680,9 @@
 {
     TABLE* t = GetTable(it8);
 
-    if (!t->Data)
-        AllocateDataSet(it8);
+    if (!t->Data) {
+        if (!AllocateDataSet(it8)) return FALSE;
+    }
 
     if (!t->Data) return FALSE;
 
@@ -1679,11 +1818,11 @@
                     break;
 
             case WRITE_HEXADECIMAL:
-                    Writef(fp, "\t0x%X", atoi(p ->Value));
+                    Writef(fp, "\t0x%X", satoi(p ->Value));
                     break;
 
             case WRITE_BINARY:
-                    Writef(fp, "\t0x%B", atoi(p ->Value));
+                    Writef(fp, "\t0b%s", satob(p ->Value));
                     break;
 
             case WRITE_PAIR:
@@ -1712,7 +1851,7 @@
 
        WriteStr(fp, "BEGIN_DATA_FORMAT\n");
        WriteStr(fp, " ");
-       nSamples = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
+       nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
 
        for (i = 0; i < nSamples; i++) {
 
@@ -1735,7 +1874,7 @@
 
        WriteStr (fp, "BEGIN_DATA\n");
 
-       t->nPatches = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
+       t->nPatches = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
 
        for (i = 0; i < t-> nPatches; i++) {
 
@@ -1803,13 +1942,15 @@
     memset(&sd, 0, sizeof(sd));
 
     sd.stream = NULL;
-    sd.Base   = (cmsUInt8Number*)  MemPtr;
+    sd.Base   = (cmsUInt8Number*) MemPtr;
     sd.Ptr    = sd.Base;
 
     sd.Used = 0;
 
-    if (sd.Base)
-        sd.Max  = *BytesNeeded;     // Write to memory?
+    if (sd.Base && (*BytesNeeded > 0)) {
+
+        sd.Max = (*BytesNeeded) - 1;     // Write to memory?
+    }
     else
         sd.Max  = 0;                // Just counting the needed bytes
 
@@ -1853,7 +1994,7 @@
                 return SynError(it8, "Sample type expected");
             }
 
-            if (!SetDataFormat(it8, iField, it8->id)) return FALSE;
+            if (!SetDataFormat(it8, iField, StringPtr(it8->id))) return FALSE;
             iField++;
 
             InSymbol(it8);
@@ -1886,8 +2027,9 @@
     InSymbol(it8);   // Eats "BEGIN_DATA"
     CheckEOLN(it8);
 
-    if (!t->Data)
-        AllocateDataSet(it8);
+    if (!t->Data) {
+        if (!AllocateDataSet(it8)) return FALSE;
+    }
 
     while (it8->sy != SEND_DATA && it8->sy != SEOF)
     {
@@ -1899,11 +2041,28 @@
 
         if (it8->sy != SEND_DATA && it8->sy != SEOF) {
 
+            switch (it8->sy)
+            {
+
+            // To keep very long data
+            case SIDENT:  
+                if (!SetData(it8, iSet, iField, StringPtr(it8->id)))
+                    return FALSE;
+                break;
+
+            case SSTRING:
+                if (!SetData(it8, iSet, iField, StringPtr(it8->str)))
+                    return FALSE;
+                break;
+
+            default:
+
             if (!GetVal(it8, Buffer, 255, "Sample data expected"))
                 return FALSE;
 
             if (!SetData(it8, iSet, iField, Buffer))
                 return FALSE;
+            }
 
             iField++;
 
@@ -1959,7 +2118,7 @@
 
 
         case SIDENT:
-            strncpy(VarName, it8->id, MAXID - 1);
+            strncpy(VarName, StringPtr(it8->id), MAXID - 1);
             VarName[MAXID - 1] = 0;
 
             if (!IsAvailableOnList(it8->ValidKeywords, VarName, NULL, &Key)) {
@@ -2103,7 +2262,7 @@
                                      // If a newline is found, then this is a type string
                                     if (it8 ->ch == '\n' || it8->ch == '\r') {
 
-                                         cmsIT8SetSheetType(it8, it8 ->id);
+                                         cmsIT8SetSheetType(it8, StringPtr(it8 ->id));
                                          InSymbol(it8);
                                     }
                                     else
@@ -2115,7 +2274,7 @@
                                 else
                                     // Validate quoted strings
                                     if (it8 ->sy == SSTRING) {
-                                        cmsIT8SetSheetType(it8, it8 ->str);
+                                        cmsIT8SetSheetType(it8, StringPtr(it8 ->str));
                                         InSymbol(it8);
                                     }
                            }
@@ -2168,72 +2327,54 @@
 
         if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) {
 
-            t -> SampleID = idField;
-
-            for (i=0; i < t -> nPatches; i++) {
-
-                char *Data = GetData(it8, i, idField);
-                if (Data) {
-                    char Buffer[256];
-
-                    strncpy(Buffer, Data, 255);
-                    Buffer[255] = 0;
-
-                    if (strlen(Buffer) <= strlen(Data))
-                        strcpy(Data, Buffer);
-                    else
-                        SetData(it8, i, idField, Buffer);
-
-                }
-            }
-
+            t -> SampleID = idField;            
         }
 
         // "LABEL" is an extension. It keeps references to forward tables
 
-        if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$' ) {
+        if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$') {
 
-                    // Search for table references...
-                    for (i=0; i < t -> nPatches; i++) {
+            // Search for table references...
+            for (i = 0; i < t->nPatches; i++) {
 
-                            char *Label = GetData(it8, i, idField);
+                char* Label = GetData(it8, i, idField);
 
-                            if (Label) {
+                if (Label) {
 
-                                cmsUInt32Number k;
+                    cmsUInt32Number k;
 
-                                // This is the label, search for a table containing
-                                // this property
+                    // This is the label, search for a table containing
+                    // this property
 
-                                for (k=0; k < it8 ->TablesCount; k++) {
+                    for (k = 0; k < it8->TablesCount; k++) {
 
-                                    TABLE* Table = it8 ->Tab + k;
-                                    KEYVALUE* p;
+                        TABLE* Table = it8->Tab + k;
+                        KEYVALUE* p;
 
-                                    if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) {
+                        if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) {
 
-                                        // Available, keep type and table
-                                        char Buffer[256];
+                            // Available, keep type and table
+                            char Buffer[256];
 
-                                        char *Type  = p ->Value;
-                                        int  nTable = (int) k;
+                            char* Type = p->Value;
+                            int  nTable = (int)k;
 
-                                        snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type );
+                            snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type);
 
-                                        SetData(it8, i, idField, Buffer);
-                                    }
-                                }
+                            SetData(it8, i, idField, Buffer);
+						}
+					}
 
 
-                            }
+				}
 
-                    }
+			}
 
 
-        }
+		}
 
-    }
-    }
+	}
+	}
 
     it8 ->nTable = nOldTable;
 }
@@ -2323,6 +2464,11 @@
 
     it8 = (cmsIT8*) hIT8;
     it8 ->MemoryBlock = (char*) _cmsMalloc(ContextID, len + 1);
+    if (it8->MemoryBlock == NULL)
+    {
+        cmsIT8Free(hIT8);
+        return FALSE;
+    }
 
     strncpy(it8 ->MemoryBlock, (const char*) Ptr, len);
     it8 ->MemoryBlock[len] = 0;
@@ -2430,15 +2576,18 @@
     }
 
 
-    Props = (char **) AllocChunk(it8, sizeof(char *) * n);
+	Props = (char**)AllocChunk(it8, sizeof(char*) * n);
+	if (Props != NULL) {
 
-    // Pass#2 - Fill pointers
-    n = 0;
-    for (p = t -> HeaderList;  p != NULL; p = p->Next) {
-        Props[n++] = p -> Keyword;
-    }
+		// Pass#2 - Fill pointers
+		n = 0;
+		for (p = t->HeaderList; p != NULL; p = p->Next) {
+			Props[n++] = p->Keyword;
+		}
 
-    *PropertyNames = Props;
+	}
+	*PropertyNames = Props;
+
     return n;
 }
 
@@ -2470,12 +2619,14 @@
 
 
     Props = (const char **) AllocChunk(it8, sizeof(char *) * n);
+    if (Props != NULL) {
 
-    // Pass#2 - Fill pointers
-    n = 0;
-    for (tmp = p;  tmp != NULL; tmp = tmp->NextSubkey) {
-        if(tmp->Subkey != NULL)
-            Props[n++] = p ->Subkey;
+        // Pass#2 - Fill pointers
+        n = 0;
+        for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) {
+            if (tmp->Subkey != NULL)
+                Props[n++] = p->Subkey;
+        }
     }
 
     *SubpropertyNames = Props;
@@ -2651,8 +2802,12 @@
 
     if (t-> nPatches == 0) {
 
-        AllocateDataFormat(it8);
-        AllocateDataSet(it8);
+        if (!AllocateDataFormat(it8))
+            return FALSE;
+
+        if (!AllocateDataSet(it8))
+            return FALSE;
+
         CookPointers(it8);
     }
 
diff --git a/third_party/lcms/src/cmscnvrt.c b/third_party/lcms/src/cmscnvrt.c
index 082f1f8..de63975 100644
--- a/third_party/lcms/src/cmscnvrt.c
+++ b/third_party/lcms/src/cmscnvrt.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -27,19 +27,6 @@
 #include "lcms2_internal.h"
 
 
-// Link several profiles to obtain a single LUT modelling the whole color transform. Intents, Black point
-// compensation and Adaptation parameters may vary across profiles. BPC and Adaptation refers to the PCS
-// after the profile. I.e, BPC[0] refers to connexion between profile(0) and profile(1)
-cmsPipeline* _cmsLinkProfiles(cmsContext     ContextID,
-                              cmsUInt32Number nProfiles,
-                              cmsUInt32Number Intents[],
-                              cmsHPROFILE     hProfiles[],
-                              cmsBool         BPC[],
-                              cmsFloat64Number AdaptationStates[],
-                              cmsUInt32Number dwFlags);
-
-//---------------------------------------------------------------------------------
-
 // This is the default routine for ICC-style intents. A user may decide to override it by using a plugin.
 // Supported intents are perceptual, relative colorimetric, saturation and ICC-absolute colorimetric
 static
@@ -247,7 +234,7 @@
 
 // Compute a CHAD based on a given temperature
 static
-    void Temp2CHAD(cmsMAT3* Chad, cmsFloat64Number Temp)
+void Temp2CHAD(cmsMAT3* Chad, cmsFloat64Number Temp)
 {
     cmsCIEXYZ White;
     cmsCIExyY ChromaticityOfWhite;
@@ -383,11 +370,11 @@
         cmsCIEXYZ WhitePointIn, WhitePointOut;
         cmsMAT3 ChromaticAdaptationMatrixIn, ChromaticAdaptationMatrixOut;
 
-        _cmsReadMediaWhitePoint(&WhitePointIn,  hProfiles[i-1]);
-        _cmsReadCHAD(&ChromaticAdaptationMatrixIn, hProfiles[i-1]);
+        if (!_cmsReadMediaWhitePoint(&WhitePointIn, hProfiles[i - 1])) return FALSE;
+        if (!_cmsReadCHAD(&ChromaticAdaptationMatrixIn, hProfiles[i - 1])) return FALSE;
 
-        _cmsReadMediaWhitePoint(&WhitePointOut,  hProfiles[i]);
-        _cmsReadCHAD(&ChromaticAdaptationMatrixOut, hProfiles[i]);
+        if (!_cmsReadMediaWhitePoint(&WhitePointOut, hProfiles[i])) return FALSE;
+        if (!_cmsReadCHAD(&ChromaticAdaptationMatrixOut, hProfiles[i])) return FALSE;
 
         if (!ComputeAbsoluteIntent(AdaptationState,
                                   &WhitePointIn,  &ChromaticAdaptationMatrixIn,
@@ -399,7 +386,7 @@
 
         if (BPC) {
 
-            cmsCIEXYZ BlackPointIn, BlackPointOut;
+            cmsCIEXYZ BlackPointIn = { 0, 0, 0}, BlackPointOut = { 0, 0, 0 };
 
             cmsDetectBlackPoint(&BlackPointIn,  hProfiles[i-1], Intent, 0);
             cmsDetectDestinationBlackPoint(&BlackPointOut, hProfiles[i], Intent, 0);
@@ -643,7 +630,7 @@
                   ColorSpaceOut == cmsSigRgbData ||
                   ColorSpaceOut == cmsSigCmykData) {
 
-                  cmsStage* clip = _cmsStageClipNegatives(Result->ContextID, cmsChannelsOf(ColorSpaceOut));
+                  cmsStage* clip = _cmsStageClipNegatives(Result->ContextID, cmsChannelsOfColorSpace(ColorSpaceOut));
                   if (clip == NULL) goto Error;
 
                   if (!cmsPipelineInsertStage(Result, cmsAT_END, clip))
@@ -710,7 +697,7 @@
 
 // Preserve black only if that is the only ink used
 static
-int BlackPreservingGrayOnlySampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
+int BlackPreservingGrayOnlySampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
 {
     GrayOnlyParams* bp = (GrayOnlyParams*) Cargo;
 
@@ -743,6 +730,9 @@
     cmsUInt32Number ICCIntents[256];
     cmsStage*         CLUT;
     cmsUInt32Number i, nGridPoints;
+    cmsUInt32Number lastProfilePos;
+    cmsUInt32Number preservationProfilesCount;
+    cmsHPROFILE hLastProfile;
 
 
     // Sanity check
@@ -752,20 +742,36 @@
     for (i=0; i < nProfiles; i++)
         ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]);
 
+
+    // Trim all CMYK devicelinks at the end  
+    lastProfilePos = nProfiles - 1;
+    hLastProfile = hProfiles[lastProfilePos];
+
+    while (lastProfilePos > 1)
+    {
+        hLastProfile = hProfiles[--lastProfilePos];
+        if (cmsGetColorSpace(hLastProfile) != cmsSigCmykData ||
+            cmsGetDeviceClass(hLastProfile) != cmsSigLinkClass)
+            break;
+    }
+
+    preservationProfilesCount = lastProfilePos + 1;
+
     // Check for non-cmyk profiles
     if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData ||
-        cmsGetColorSpace(hProfiles[nProfiles-1]) != cmsSigCmykData)
+        !(cmsGetColorSpace(hLastProfile) == cmsSigCmykData ||
+        cmsGetDeviceClass(hLastProfile) == cmsSigOutputClass))
            return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags);
 
-    memset(&bp, 0, sizeof(bp));
-
     // Allocate an empty LUT for holding the result
     Result = cmsPipelineAlloc(ContextID, 4, 4);
     if (Result == NULL) return NULL;
 
+    memset(&bp, 0, sizeof(bp));
+
     // Create a LUT holding normal ICC transform
     bp.cmyk2cmyk = DefaultICCintents(ContextID,
-        nProfiles,
+                                     preservationProfilesCount,
         ICCIntents,
         hProfiles,
         BPC,
@@ -777,7 +783,7 @@
     // Now, compute the tone curve
     bp.KTone = _cmsBuildKToneCurve(ContextID,
         4096,
-        nProfiles,
+                                    preservationProfilesCount,
         ICCIntents,
         hProfiles,
         BPC,
@@ -802,6 +808,19 @@
     if (!cmsStageSampleCLut16bit(CLUT, BlackPreservingGrayOnlySampler, (void*) &bp, 0))
         goto Error;
 
+    
+    // Insert possible devicelinks at the end
+    for (i = lastProfilePos + 1; i < nProfiles; i++)
+    {
+        cmsPipeline* devlink = _cmsReadDevicelinkLUT(hProfiles[i], ICCIntents[i]);
+        if (devlink == NULL)
+            goto Error;
+
+        if (!cmsPipelineCat(Result, devlink))
+            goto Error;
+    }
+
+
     // Get rid of xform and tone curve
     cmsPipelineFree(bp.cmyk2cmyk);
     cmsFreeToneCurve(bp.KTone);
@@ -837,7 +856,7 @@
 
 // The CLUT will be stored at 16 bits, but calculations are performed at cmsFloat32Number precision
 static
-int BlackPreservingSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
+int BlackPreservingSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
 {
     int i;
     cmsFloat32Number Inf[4], Outf[4];
@@ -862,18 +881,18 @@
     }
 
     // Try the original transform,
-    cmsPipelineEvalFloat( Inf, Outf, bp ->cmyk2cmyk);
+    cmsPipelineEvalFloat(Inf, Outf, bp ->cmyk2cmyk);
 
     // Store a copy of the floating point result into 16-bit
     for (i=0; i < 4; i++)
             Out[i] = _cmsQuickSaturateWord(Outf[i] * 65535.0);
 
     // Maybe K is already ok (mostly on K=0)
-    if ( fabs(Outf[3] - LabK[3]) < (3.0 / 65535.0) ) {
+    if (fabsf(Outf[3] - LabK[3]) < (3.0 / 65535.0)) {
         return TRUE;
     }
 
-    // K differ, mesure and keep Lab measurement for further usage
+    // K differ, measure and keep Lab measurement for further usage
     // this is done in relative colorimetric intent
     cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1);
 
@@ -894,7 +913,7 @@
     Outf[3] = LabK[3];
 
     // Apply TAC if needed
-    SumCMY   = Outf[0]  + Outf[1] + Outf[2];
+    SumCMY   = (cmsFloat64Number) Outf[0]  + Outf[1] + Outf[2];
     SumCMYK  = SumCMY + Outf[3];
 
     if (SumCMYK > bp ->MaxTAC) {
@@ -920,6 +939,8 @@
     return TRUE;
 }
 
+
+
 // This is the entry for black-plane preserving, which are non-ICC
 static
 cmsPipeline* BlackPreservingKPlaneIntents(cmsContext     ContextID,
@@ -931,10 +952,14 @@
                                           cmsUInt32Number dwFlags)
 {
     PreserveKPlaneParams bp;
+
     cmsPipeline*    Result = NULL;
     cmsUInt32Number ICCIntents[256];
     cmsStage*         CLUT;
     cmsUInt32Number i, nGridPoints;
+    cmsUInt32Number lastProfilePos;
+    cmsUInt32Number preservationProfilesCount;
+    cmsHPROFILE hLastProfile;
     cmsHPROFILE hLab;
 
     // Sanity check
@@ -944,32 +969,45 @@
     for (i=0; i < nProfiles; i++)
         ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]);
 
+    // Trim all CMYK devicelinks at the end  
+    lastProfilePos = nProfiles - 1;
+    hLastProfile = hProfiles[lastProfilePos];
+
+    while (lastProfilePos > 1)
+    {   
+        hLastProfile = hProfiles[--lastProfilePos];
+        if (cmsGetColorSpace(hLastProfile) != cmsSigCmykData ||
+            cmsGetDeviceClass(hLastProfile) != cmsSigLinkClass)
+            break;        
+    } 
+
+    preservationProfilesCount = lastProfilePos + 1;
+
     // Check for non-cmyk profiles
     if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData ||
-        !(cmsGetColorSpace(hProfiles[nProfiles-1]) == cmsSigCmykData ||
-        cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigOutputClass))
+        !(cmsGetColorSpace(hLastProfile) == cmsSigCmykData ||
+        cmsGetDeviceClass(hLastProfile) == cmsSigOutputClass))
            return  DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags);
 
     // Allocate an empty LUT for holding the result
     Result = cmsPipelineAlloc(ContextID, 4, 4);
     if (Result == NULL) return NULL;
 
-
     memset(&bp, 0, sizeof(bp));
 
     // We need the input LUT of the last profile, assuming this one is responsible of
     // black generation. This LUT will be searched in inverse order.
-    bp.LabK2cmyk = _cmsReadInputLUT(hProfiles[nProfiles-1], INTENT_RELATIVE_COLORIMETRIC);
+    bp.LabK2cmyk = _cmsReadInputLUT(hLastProfile, INTENT_RELATIVE_COLORIMETRIC);
     if (bp.LabK2cmyk == NULL) goto Cleanup;
 
     // Get total area coverage (in 0..1 domain)
-    bp.MaxTAC = cmsDetectTAC(hProfiles[nProfiles-1]) / 100.0;
+    bp.MaxTAC = cmsDetectTAC(hLastProfile) / 100.0;
     if (bp.MaxTAC <= 0) goto Cleanup;
 
 
     // Create a LUT holding normal ICC transform
     bp.cmyk2cmyk = DefaultICCintents(ContextID,
-                                         nProfiles,
+                                         preservationProfilesCount,
                                          ICCIntents,
                                          hProfiles,
                                          BPC,
@@ -978,7 +1016,7 @@
     if (bp.cmyk2cmyk == NULL) goto Cleanup;
 
     // Now the tone curve
-    bp.KTone = _cmsBuildKToneCurve(ContextID, 4096, nProfiles,
+    bp.KTone = _cmsBuildKToneCurve(ContextID, 4096, preservationProfilesCount,
                                    ICCIntents,
                                    hProfiles,
                                    BPC,
@@ -988,14 +1026,14 @@
 
     // To measure the output, Last profile to Lab
     hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);
-    bp.hProofOutput = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1],
+    bp.hProofOutput = cmsCreateTransformTHR(ContextID, hLastProfile,
                                          CHANNELS_SH(4)|BYTES_SH(2), hLab, TYPE_Lab_DBL,
                                          INTENT_RELATIVE_COLORIMETRIC,
                                          cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE);
     if ( bp.hProofOutput == NULL) goto Cleanup;
 
     // Same as anterior, but lab in the 0..1 range
-    bp.cmyk2Lab = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1],
+    bp.cmyk2Lab = cmsCreateTransformTHR(ContextID, hLastProfile,
                                          FLOAT_SH(1)|CHANNELS_SH(4)|BYTES_SH(4), hLab,
                                          FLOAT_SH(1)|CHANNELS_SH(3)|BYTES_SH(4),
                                          INTENT_RELATIVE_COLORIMETRIC,
@@ -1018,6 +1056,18 @@
 
     cmsStageSampleCLut16bit(CLUT, BlackPreservingSampler, (void*) &bp, 0);
 
+    // Insert possible devicelinks at the end    
+    for (i = lastProfilePos + 1; i < nProfiles; i++)
+    {        
+        cmsPipeline* devlink = _cmsReadDevicelinkLUT(hProfiles[i], ICCIntents[i]);
+        if (devlink == NULL)
+            goto Cleanup;
+
+        if (!cmsPipelineCat(Result, devlink))
+            goto Cleanup;
+    }
+
+
 Cleanup:
 
     if (bp.cmyk2cmyk) cmsPipelineFree(bp.cmyk2cmyk);
@@ -1030,6 +1080,8 @@
     return Result;
 }
 
+
+
 // Link routines ------------------------------------------------------------------------------------------------------
 
 // Chain several profiles into a single LUT. It just checks the parameters and then calls the handler
diff --git a/third_party/lcms/src/cmserr.c b/third_party/lcms/src/cmserr.c
index d549f27..902b776 100644
--- a/third_party/lcms/src/cmserr.c
+++ b/third_party/lcms/src/cmserr.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -40,7 +40,7 @@
 // compare two strings ignoring case
 int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2)
 {
-    register const unsigned char *us1 = (const unsigned char *)s1,
+    CMSREGISTER const unsigned char *us1 = (const unsigned char *)s1,
                                  *us2 = (const unsigned char *)s2;
 
     while (toupper(*us1) == toupper(*us2++))
@@ -166,7 +166,7 @@
 
 // Sub allocation takes care of many pointers of small size. The memory allocated in
 // this way have be freed at once. Next function allocates a single chunk for linked list
-// I prefer this method over realloc due to the big inpact on xput realloc may have if
+// I prefer this method over realloc due to the big impact on xput realloc may have if
 // memory is being swapped to disk. This approach is safer (although that may not be true on all platforms)
 static
 _cmsSubAllocator_chunk* _cmsCreateSubAllocChunk(cmsContext ContextID, cmsUInt32Number Initial)
@@ -293,7 +293,7 @@
 // There is no error handling at all. When a function fails, it returns proper value.
 // For example, all create functions does return NULL on failure. Other return FALSE
 // It may be interesting, for the developer, to know why the function is failing.
-// for that reason, lcms2 does offer a logging function. This function does recive
+// for that reason, lcms2 does offer a logging function. This function does receive
 // a ENGLISH string with some clues on what is going wrong. You can show this
 // info to the end user, or just create some sort of log.
 // The logging function should NOT terminate the program, as this obviously can leave
@@ -471,7 +471,6 @@
     if (Plugin ->CreateMutexPtr == NULL || Plugin ->DestroyMutexPtr == NULL || 
         Plugin ->LockMutexPtr == NULL || Plugin ->UnlockMutexPtr == NULL) return FALSE;
 
-
     ctx->CreateMutexPtr  = Plugin->CreateMutexPtr;
     ctx->DestroyMutexPtr = Plugin ->DestroyMutexPtr;
     ctx ->LockMutexPtr   = Plugin ->LockMutexPtr;
@@ -519,3 +518,47 @@
         ptr ->UnlockMutexPtr(ContextID, mtx);
     }
 }
+
+// The global Context0 storage for parallelization plug-in
+ _cmsParallelizationPluginChunkType _cmsParallelizationPluginChunk = { 0 };
+
+// Allocate parallelization container.
+void _cmsAllocParallelizationPluginChunk(struct _cmsContext_struct* ctx,
+                                         const struct _cmsContext_struct* src)
+{    
+    if (src != NULL) {
+        void* from = src->chunks[ParallelizationPlugin];
+        ctx->chunks[ParallelizationPlugin] = _cmsSubAllocDup(ctx->MemPool, from, sizeof(_cmsParallelizationPluginChunkType));
+    }
+    else {        
+        _cmsParallelizationPluginChunkType ParallelizationPluginChunk = { 0 };
+        ctx->chunks[ParallelizationPlugin] = _cmsSubAllocDup(ctx->MemPool, &ParallelizationPluginChunk, sizeof(_cmsParallelizationPluginChunkType));
+    }         
+}
+
+// Register parallel processing
+cmsBool _cmsRegisterParallelizationPlugin(cmsContext ContextID, cmsPluginBase* Data)
+{
+    cmsPluginParalellization* Plugin = (cmsPluginParalellization*)Data;
+    _cmsParallelizationPluginChunkType* ctx = (_cmsParallelizationPluginChunkType*)_cmsContextGetClientChunk(ContextID, ParallelizationPlugin);
+
+    if (Data == NULL) {
+
+        // No parallelization routines
+        ctx->MaxWorkers = 0;
+        ctx->WorkerFlags = 0;
+        ctx->SchedulerFn = NULL;        
+        return TRUE;
+    }
+
+    // callback is required
+    if (Plugin->SchedulerFn == NULL) return FALSE;
+
+    ctx->MaxWorkers = Plugin->MaxWorkers;
+    ctx->WorkerFlags = Plugin->WorkerFlags;
+    ctx->SchedulerFn = Plugin->SchedulerFn;
+    
+    // All is ok
+    return TRUE;
+}
+
diff --git a/third_party/lcms/src/cmsgamma.c b/third_party/lcms/src/cmsgamma.c
index b2d3e11..0c8d0f3 100644
--- a/third_party/lcms/src/cmsgamma.c
+++ b/third_party/lcms/src/cmsgamma.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2013 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -59,11 +59,11 @@
 
 // The built-in list
 static const _cmsParametricCurvesCollection DefaultCurves = {
-    9,                                  // # of curve types
-    { 1, 2, 3, 4, 5, 6, 7, 8, 108 },    // Parametric curve ID
-    { 1, 3, 4, 5, 7, 4, 5, 5, 1 },      // Parameters by type
-    DefaultEvalParametricFn,            // Evaluator
-    NULL                                // Next in chain
+    10,                                      // # of curve types
+    { 1, 2, 3, 4, 5, 6, 7, 8, 108, 109 },    // Parametric curve ID
+    { 1, 3, 4, 5, 7, 4, 5, 5,   1,   1 },    // Parameters by type
+    DefaultEvalParametricFn,                 // Evaluator
+    NULL                                     // Next in chain
 };
 
 // Duplicates the zone of memory used by the plug-in in the new context
@@ -207,7 +207,7 @@
 }
 
 // Low level allocate, which takes care of memory details. nEntries may be zero, and in this case
-// no optimation curve is computed. nSegments may also be zero in the inverse case, where only the
+// no optimization curve is computed. nSegments may also be zero in the inverse case, where only the
 // optimization curve is given. Both features simultaneously is an error
 static
 cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsUInt32Number nEntries,
@@ -300,7 +300,8 @@
         return p;
 
 Error:
-    if (p -> Segments) _cmsFree(ContextID, p ->Segments);
+    if (p -> SegInterp) _cmsFree(ContextID, p -> SegInterp);
+    if (p -> Segments) _cmsFree(ContextID, p -> Segments);
     if (p -> Evals) _cmsFree(ContextID, p -> Evals);
     if (p ->Table16) _cmsFree(ContextID, p ->Table16);
     _cmsFree(ContextID, p);
@@ -308,6 +309,32 @@
 }
 
 
+// Generates a sigmoidal function with desired steepness.
+cmsINLINE double sigmoid_base(double k, double t)
+{
+    return (1.0 / (1.0 + exp(-k * t))) - 0.5;
+}
+
+cmsINLINE double inverted_sigmoid_base(double k, double t)
+{
+    return -log((1.0 / (t + 0.5)) - 1.0) / k;
+}
+
+cmsINLINE double sigmoid_factory(double k, double t)
+{
+    double correction = 0.5 / sigmoid_base(k, 1);
+
+    return correction * sigmoid_base(k, 2.0 * t - 1.0) + 0.5;
+}
+
+cmsINLINE double inverse_sigmoid_factory(double k, double t)
+{
+    double correction = 0.5 / sigmoid_base(k, 1);
+
+    return (inverted_sigmoid_base(k, (t - 0.5) / correction) + 1.0) / 2.0;
+}
+
+
 // Parametric Fn using floating point
 static
 cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R)
@@ -400,8 +427,8 @@
 
 
     // IEC 61966-3
-    // Y = (aX + b)^Gamma | X <= -b/a
-    // Y = c              | else
+    // Y = (aX + b)^Gamma + c | X <= -b/a
+    // Y = c                  | else
     case 3:
     {
         if (fabs(Params[1]) < MATRIX_DET_TOLERANCE)
@@ -435,7 +462,8 @@
     // X=-b/a                   | (Y<c)
     case -3:
     {
-        if (fabs(Params[1]) < MATRIX_DET_TOLERANCE)
+        if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||
+            fabs(Params[1]) < MATRIX_DET_TOLERANCE)
         {
             Val = 0;
         }
@@ -480,28 +508,31 @@
     // X=Y/c              | Y< (ad+b)^g
     case -4:
     {
-        if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||
-            fabs(Params[1]) < MATRIX_DET_TOLERANCE ||
-            fabs(Params[3]) < MATRIX_DET_TOLERANCE)
-        {
-            Val = 0;
-        }
+
+        e = Params[1] * Params[4] + Params[2];
+        if (e < 0)
+            disc = 0;
         else
-        {
-            e = Params[1] * Params[4] + Params[2];
-            if (e < 0)
-                disc = 0;
+            disc = pow(e, Params[0]);
+
+        if (R >= disc) {
+
+            if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||
+                fabs(Params[1]) < MATRIX_DET_TOLERANCE)
+
+                Val = 0;
+
             else
-                disc = pow(e, Params[0]);
-
-            if (R >= disc) {
-
                 Val = (pow(R, 1.0 / Params[0]) - Params[2]) / Params[1];
-            }
-            else {
-                Val = R / Params[3];
-            }
         }
+        else {
+
+            if (fabs(Params[3]) < MATRIX_DET_TOLERANCE)
+                Val = 0;
+            else
+                Val = R / Params[3];
+        }
+
     }
     break;
 
@@ -528,26 +559,29 @@
     // X=(Y-f)/c          | else
     case -5:
     {
-        if (fabs(Params[1]) < MATRIX_DET_TOLERANCE ||
-            fabs(Params[3]) < MATRIX_DET_TOLERANCE)
-        {
-            Val = 0;
-        }
-        else
-        {
-            disc = Params[3] * Params[4] + Params[6];
-            if (R >= disc) {
+        disc = Params[3] * Params[4] + Params[6];
+        if (R >= disc) {
 
-                e = R - Params[5];
-                if (e < 0)
+            e = R - Params[5];
+            if (e < 0)
+                Val = 0;
+            else
+            {
+                if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||
+                    fabs(Params[1]) < MATRIX_DET_TOLERANCE)
+
                     Val = 0;
                 else
                     Val = (pow(e, 1.0 / Params[0]) - Params[2]) / Params[1];
             }
-            else {
-                Val = (R - Params[6]) / Params[3];
-            }
         }
+        else {
+            if (fabs(Params[3]) < MATRIX_DET_TOLERANCE)
+                Val = 0;
+            else
+                Val = (R - Params[6]) / Params[3];
+        }
+
     }
     break;
 
@@ -568,7 +602,8 @@
     // ((Y - c) ^1/Gamma - b) / a
     case -6:
     {
-        if (fabs(Params[1]) < MATRIX_DET_TOLERANCE)
+        if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||
+            fabs(Params[1]) < MATRIX_DET_TOLERANCE)
         {
             Val = 0;
         }
@@ -639,6 +674,7 @@
        }
        break;
 
+
    // S-Shaped: (1 - (1-x)^1/g)^1/g
    case 108:
        if (fabs(Params[0]) < MATRIX_DET_TOLERANCE)
@@ -656,6 +692,15 @@
         Val = 1 - pow(1 - pow(R, Params[0]), Params[0]);
         break;
 
+    // Sigmoidals
+    case 109:
+        Val = sigmoid_factory(Params[0], R);
+        break;
+
+    case -109:
+        Val = inverse_sigmoid_factory(Params[0], R);
+        break;
+
     default:
         // Unsupported parametric curve. Should never reach here
         return 0;
@@ -778,6 +823,10 @@
 {
     cmsCurveSegment Seg[3];
 
+    // Do some housekeeping
+    if (nEntries == 0 || values == NULL)
+        return NULL;
+
     // A segmented tone curve should have function segments in the first and last positions
     // Initialize segmented curve part up to 0 to constant value = samples[0]
     Seg[0].x0 = MINUS_INF;
@@ -817,7 +866,7 @@
 //
 // Parameters goes as: Curve, a, b, c, d, e, f
 // Type is the ICC type +1
-// if type is negative, then the curve is analyticaly inverted
+// if type is negative, then the curve is analytically inverted
 cmsToneCurve* CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt32Number Type, const cmsFloat64Number Params[])
 {
     cmsCurveSegment Seg0;
@@ -900,10 +949,7 @@
         Curve -> Evals = NULL;
     }
 
-    if (Curve) {
-        _cmsFree(ContextID, Curve);
-        Curve = NULL;
-    }
+    _cmsFree(ContextID, Curve);
 }
 
 // Utility function, free 3 gamma tables
@@ -923,10 +969,7 @@
 // Duplicate a gamma table
 cmsToneCurve* CMSEXPORT cmsDupToneCurve(const cmsToneCurve* In)
 {
-    // Xiaochuan Liu
-    // fix openpdf bug(mantis id:0055683, google id:360198)
-    // the function CurveSetElemTypeFree in cmslut.c also needs to check pointer
-    if (In == NULL || In ->InterpParams == NULL || In ->Segments == NULL || In ->Table16 == NULL) return NULL;
+    if (In == NULL) return NULL;
 
     return  AllocateToneCurveStruct(In ->InterpParams ->ContextID, In ->nEntries, In ->nSegments, In ->Segments, In ->Table16);
 }
@@ -959,7 +1002,7 @@
     //Iterate
     for (i=0; i <  nResultingPoints; i++) {
 
-        t = (cmsFloat32Number) i / (nResultingPoints-1);
+        t = (cmsFloat32Number) i / (cmsFloat32Number)(nResultingPoints-1);
         x = cmsEvalToneCurveFloat(X,  t);
         Res[i] = cmsEvalToneCurveFloat(Yreversed, x);
     }
@@ -1173,6 +1216,7 @@
     cmsBool SuccessStatus = TRUE;
     cmsFloat32Number *w, *y, *z;
     cmsUInt32Number i, nItems, Zeros, Poles;
+    cmsBool notCheck = FALSE;
 
     if (Tab != NULL && Tab->InterpParams != NULL)
     {
@@ -1200,6 +1244,12 @@
                         w[i + 1] = 1.0;
                     }
 
+                    if (lambda < 0)
+                    {
+                        notCheck = TRUE;
+                        lambda = -lambda;
+                    }
+
                     if (smooth2(ContextID, w, y, z, (cmsFloat32Number)lambda, (int)nItems))
                     {
                         // Do some reality - checking...
@@ -1212,7 +1262,7 @@
                             if (z[i] < z[i - 1])
                             {
                                 cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Non-Monotonic.");
-                                SuccessStatus = FALSE;
+                                SuccessStatus = notCheck;
                                 break;
                             }
                         }
@@ -1220,13 +1270,13 @@
                         if (SuccessStatus && Zeros > (nItems / 3))
                         {
                             cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly zeros.");
-                            SuccessStatus = FALSE;
+                            SuccessStatus = notCheck;
                         }
 
                         if (SuccessStatus && Poles > (nItems / 3))
                         {
                             cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly poles.");
-                            SuccessStatus = FALSE;
+                            SuccessStatus = notCheck;
                         }
 
                         if (SuccessStatus) // Seems ok
@@ -1276,7 +1326,7 @@
 }
 
 // Is a table linear? Do not use parametric since we cannot guarantee some weird parameters resulting
-// in a linear table. This way assures it is linear in 12 bits, which should be enought in most cases.
+// in a linear table. This way assures it is linear in 12 bits, which should be enough in most cases.
 cmsBool CMSEXPORT cmsIsToneCurveLinear(const cmsToneCurve* Curve)
 {
     int i;
@@ -1441,6 +1491,9 @@
         }
     }
 
+    // We need enough valid samples
+    if (n <= 1) return -1.0;
+
     // Take a look on SD to see if gamma isn't exponential at all
     Std = sqrt((n * sum2 - sum * sum) / (n*(n-1)));
 
@@ -1449,3 +1502,14 @@
 
     return (sum / n);   // The mean
 }
+
+
+// Retrieve parameters on one-segment tone curves
+
+cmsFloat64Number* CMSEXPORT cmsGetToneCurveParams(const cmsToneCurve* t)
+{
+    _cmsAssert(t != NULL);
+
+    if (t->nSegments != 1) return NULL;
+    return t->Segments[0].Params;
+}
diff --git a/third_party/lcms/src/cmsgmt.c b/third_party/lcms/src/cmsgmt.c
index 027f201..5109bfa 100644
--- a/third_party/lcms/src/cmsgmt.c
+++ b/third_party/lcms/src/cmsgmt.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2021 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -199,19 +199,19 @@
 
     cmsHTRANSFORM hInput;               // From whatever input color space. 16 bits to DBL
     cmsHTRANSFORM hForward, hReverse;   // Transforms going from Lab to colorant and back
-    cmsFloat64Number Thereshold;        // The thereshold after which is considered out of gamut
+    cmsFloat64Number Threshold;         // The threshold after which is considered out of gamut
 
     } GAMUTCHAIN;
 
 // This sampler does compute gamut boundaries by comparing original
-// values with a transform going back and forth. Values above ERR_THERESHOLD
+// values with a transform going back and forth. Values above ERR_THRESHOLD
 // of maximum are considered out of gamut.
 
-#define ERR_THERESHOLD      5
+#define ERR_THRESHOLD      5
 
 
 static
-int GamutSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
+int GamutSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
 {
     GAMUTCHAIN*  t = (GAMUTCHAIN* ) Cargo;
     cmsCIELab LabIn1, LabOut1;
@@ -246,17 +246,17 @@
 
 
     // if dE1 is small and dE2 is small, value is likely to be in gamut
-    if (dE1 < t->Thereshold && dE2 < t->Thereshold)
+    if (dE1 < t->Threshold && dE2 < t->Threshold)
         Out[0] = 0;
     else {
 
         // if dE1 is small and dE2 is big, undefined. Assume in gamut
-        if (dE1 < t->Thereshold && dE2 > t->Thereshold)
+        if (dE1 < t->Threshold && dE2 > t->Threshold)
             Out[0] = 0;
         else
             // dE1 is big and dE2 is small, clearly out of gamut
-            if (dE1 > t->Thereshold && dE2 < t->Thereshold)
-                Out[0] = (cmsUInt16Number) _cmsQuickFloor((dE1 - t->Thereshold) + .5);
+            if (dE1 > t->Threshold && dE2 < t->Threshold)
+                Out[0] = (cmsUInt16Number) _cmsQuickFloor((dE1 - t->Threshold) + .5);
             else  {
 
                 // dE1 is big and dE2 is also big, could be due to perceptual mapping
@@ -266,8 +266,8 @@
                 else
                     ErrorRatio = dE1 / dE2;
 
-                if (ErrorRatio > t->Thereshold)
-                    Out[0] = (cmsUInt16Number)  _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5);
+                if (ErrorRatio > t->Threshold)
+                    Out[0] = (cmsUInt16Number)  _cmsQuickFloor((ErrorRatio - t->Threshold) + .5);
                 else
                     Out[0] = 0;
             }
@@ -297,7 +297,8 @@
     cmsStage* CLUT;
     cmsUInt32Number dwFormat;
     GAMUTCHAIN Chain;
-    cmsUInt32Number nChannels, nGridpoints;
+    cmsUInt32Number nGridpoints;
+    cmsInt32Number nChannels;
     cmsColorSpaceSignature ColorSpace;
     cmsUInt32Number i;
     cmsHPROFILE ProfileList[256];
@@ -323,10 +324,10 @@
 
     if (cmsIsMatrixShaper(hGamut)) {
 
-        Chain.Thereshold = 1.0;
+        Chain.Threshold = 1.0;
     }
     else {
-        Chain.Thereshold = ERR_THERESHOLD;
+        Chain.Threshold = ERR_THRESHOLD;
     }
 
 
@@ -346,8 +347,7 @@
 
 
     ColorSpace  = cmsGetColorSpace(hGamut);
-
-    nChannels   = cmsChannelsOf(ColorSpace);
+    nChannels   = cmsChannelsOfColorSpace(ColorSpace);
     nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC);
     dwFormat    = (CHANNELS_SH(nChannels)|BYTES_SH(2));
 
@@ -424,7 +424,7 @@
 // This callback just accounts the maximum ink dropped in the given node. It does not populate any
 // memory, as the destination table is NULL. Its only purpose it to know the global maximum.
 static
-int EstimateTAC(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void * Cargo)
+int EstimateTAC(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void * Cargo)
 {
     cmsTACestimator* bp = (cmsTACestimator*) Cargo;
     cmsFloat32Number RoundTrip[cmsMAXCHANNELS];
@@ -472,6 +472,9 @@
     // Create a fake formatter for result
     dwFormatter = cmsFormatterForColorspaceOfProfile(hProfile, 4, TRUE);
 
+    // Unsupported color space?
+    if (dwFormatter == 0) return 0;
+
     bp.nOutputChans = T_CHANNELS(dwFormatter);
     bp.MaxTAC = 0;    // Initial TAC is 0
 
@@ -588,3 +591,69 @@
 
     return TRUE;
 }
+
+// Detect whatever a given ICC profile works in linear (gamma 1.0) space
+// Actually, doing that "well" is quite hard, since every component may behave completely different.
+// Since the true point of this function is to detect suitable optimizations, I am imposing some requirements 
+// that simplifies things: only RGB, and only profiles that can got in both directions.
+// The algorithm obtains Y from a syntetical gray R=G=B. Then least squares fitting is used to estimate gamma. 
+// For gamma close to 1.0, RGB is linear. On profiles not supported, -1 is returned.
+
+cmsFloat64Number CMSEXPORT cmsDetectRGBProfileGamma(cmsHPROFILE hProfile, cmsFloat64Number threshold)
+{
+    cmsContext ContextID;
+    cmsHPROFILE hXYZ;
+    cmsHTRANSFORM xform;
+    cmsToneCurve* Y_curve;
+    cmsUInt16Number rgb[256][3];
+    cmsCIEXYZ XYZ[256];
+    cmsFloat32Number Y_normalized[256];
+    cmsFloat64Number gamma;
+    cmsProfileClassSignature cl;
+    int i;
+
+    if (cmsGetColorSpace(hProfile) != cmsSigRgbData)
+        return -1;
+
+    cl = cmsGetDeviceClass(hProfile);
+    if (cl != cmsSigInputClass && cl != cmsSigDisplayClass && 
+        cl != cmsSigOutputClass && cl != cmsSigColorSpaceClass)
+        return -1;
+
+    ContextID = cmsGetProfileContextID(hProfile);
+    hXYZ = cmsCreateXYZProfileTHR(ContextID);
+    if (hXYZ == NULL)
+        return -1;
+    xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_RGB_16, hXYZ, TYPE_XYZ_DBL, 
+                                    INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE);
+
+    if (xform == NULL) { // If not RGB or forward direction is not supported, regret with the previous error
+
+        cmsCloseProfile(hXYZ);        
+        return -1;
+    }
+
+    for (i = 0; i < 256; i++) {
+        rgb[i][0] = rgb[i][1] = rgb[i][2] = FROM_8_TO_16(i);       
+    }
+
+    cmsDoTransform(xform, rgb, XYZ, 256);
+
+    cmsDeleteTransform(xform);
+    cmsCloseProfile(hXYZ);
+
+    for (i = 0; i < 256; i++) {
+        Y_normalized[i] = (cmsFloat32Number) XYZ[i].Y;
+    }
+
+    Y_curve = cmsBuildTabulatedToneCurveFloat(ContextID, 256, Y_normalized);
+    if (Y_curve == NULL)     
+        return -1;
+    
+    gamma = cmsEstimateGamma(Y_curve, threshold);
+
+    cmsFreeToneCurve(Y_curve);
+
+    return gamma;
+}
+
diff --git a/third_party/lcms/src/cmshalf.c b/third_party/lcms/src/cmshalf.c
index 935273d..043ee53 100644
--- a/third_party/lcms/src/cmshalf.c
+++ b/third_party/lcms/src/cmshalf.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -460,7 +460,7 @@
 0xfc00, 0xfc00
 };
 
-static const cmsUInt8Number  Shift[512] = {
+static const cmsUInt8Number Shift[512] = {
 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
@@ -503,7 +503,7 @@
 0x18, 0x18, 0x18, 0x18, 0x0d
 };
 
-cmsFloat32Number _cmsHalf2Float(cmsUInt16Number h)
+cmsFloat32Number CMSEXPORT _cmsHalf2Float(cmsUInt16Number h)
 {
     union {
         cmsFloat32Number flt;
@@ -516,7 +516,7 @@
     return out.flt;
 }
 
-cmsUInt16Number _cmsFloat2Half(cmsFloat32Number flt)
+cmsUInt16Number CMSEXPORT _cmsFloat2Half(cmsFloat32Number flt)
 {
     union {
         cmsFloat32Number flt;
diff --git a/third_party/lcms/src/cmsintrp.c b/third_party/lcms/src/cmsintrp.c
index e44ab3e..2b5c634 100644
--- a/third_party/lcms/src/cmsintrp.c
+++ b/third_party/lcms/src/cmsintrp.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -158,8 +158,8 @@
 
 
 // This one is a wrapper on the anterior, but assuming all directions have same number of nodes
-cmsInterpParams* _cmsComputeInterpParams(cmsContext ContextID, cmsUInt32Number nSamples, 
-                                         cmsUInt32Number InputChan, cmsUInt32Number OutputChan, const void* Table, cmsUInt32Number dwFlags)
+cmsInterpParams* CMSEXPORT _cmsComputeInterpParams(cmsContext ContextID, cmsUInt32Number nSamples, 
+                                                   cmsUInt32Number InputChan, cmsUInt32Number OutputChan, const void* Table, cmsUInt32Number dwFlags)
 {
     int i;
     cmsUInt32Number Samples[MAX_INPUT_DIMENSIONS];
@@ -174,14 +174,14 @@
 
 
 // Free all associated memory
-void _cmsFreeInterpParams(cmsInterpParams* p)
+void CMSEXPORT _cmsFreeInterpParams(cmsInterpParams* p)
 {
     if (p != NULL) _cmsFree(p ->ContextID, p);
 }
 
 
 // Inline fixed point interpolation
-cmsINLINE cmsUInt16Number LinearInterp(cmsS15Fixed16Number a, cmsS15Fixed16Number l, cmsS15Fixed16Number h)
+cmsINLINE CMS_NO_SANITIZE cmsUInt16Number LinearInterp(cmsS15Fixed16Number a, cmsS15Fixed16Number l, cmsS15Fixed16Number h)
 {
     cmsUInt32Number dif = (cmsUInt32Number) (h - l) * a + 0x8000;
     dif = (dif >> 16) + l;
@@ -191,33 +191,33 @@
 
 //  Linear interpolation (Fixed-point optimized)
 static
-void LinLerp1D(register const cmsUInt16Number Value[],
-               register cmsUInt16Number Output[],
-               register const cmsInterpParams* p)
+void LinLerp1D(CMSREGISTER const cmsUInt16Number Value[],
+               CMSREGISTER cmsUInt16Number Output[],
+               CMSREGISTER const cmsInterpParams* p)
 {
     cmsUInt16Number y1, y0;
     int cell0, rest;
     int val3;
     const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table;
 
-    // if last value...
-    if (Value[0] == 0xffff) {
+    // if last value or just one point
+    if (Value[0] == 0xffff || p->Domain[0] == 0) {
 
-        Output[0] = LutTable[p -> Domain[0]];
-        return;
+        Output[0] = LutTable[p -> Domain[0]];      
     }
+    else
+    {
+        val3 = p->Domain[0] * Value[0];
+        val3 = _cmsToFixedDomain(val3);    // To fixed 15.16
 
-    val3 = p -> Domain[0] * Value[0];
-    val3 = _cmsToFixedDomain(val3);    // To fixed 15.16
+        cell0 = FIXED_TO_INT(val3);             // Cell is 16 MSB bits
+        rest = FIXED_REST_TO_INT(val3);        // Rest is 16 LSB bits
 
-    cell0 = FIXED_TO_INT(val3);             // Cell is 16 MSB bits
-    rest  = FIXED_REST_TO_INT(val3);        // Rest is 16 LSB bits
+        y0 = LutTable[cell0];
+        y1 = LutTable[cell0 + 1];
 
-    y0 = LutTable[cell0];
-    y1 = LutTable[cell0+1];
-
-
-    Output[0] = LinearInterp(rest, y0, y1);
+        Output[0] = LinearInterp(rest, y0, y1);
+    }
 }
 
 // To prevent out of bounds indexing
@@ -240,32 +240,33 @@
        val2 = fclamp(Value[0]);
 
        // if last value...
-       if (val2 == 1.0) {
-           Output[0] = LutTable[p -> Domain[0]];
-           return;
+       if (val2 == 1.0 || p->Domain[0] == 0) {
+           Output[0] = LutTable[p -> Domain[0]];          
        }
+       else
+       {
+           val2 *= p->Domain[0];
 
-       val2 *= p -> Domain[0];
+           cell0 = (int)floor(val2);
+           cell1 = (int)ceil(val2);
 
-       cell0 = (int) floor(val2);
-       cell1 = (int) ceil(val2);
+           // Rest is 16 LSB bits
+           rest = val2 - cell0;
 
-       // Rest is 16 LSB bits
-       rest = val2 - cell0;
+           y0 = LutTable[cell0];
+           y1 = LutTable[cell1];
 
-       y0 = LutTable[cell0] ;
-       y1 = LutTable[cell1] ;
-
-       Output[0] = y0 + (y1 - y0) * rest;
+           Output[0] = y0 + (y1 - y0) * rest;
+       }
 }
 
 
 
 // Eval gray LUT having only one input channel
-static
-void Eval1Input(register const cmsUInt16Number Input[],
-                register cmsUInt16Number Output[],
-                register const cmsInterpParams* p16)
+static CMS_NO_SANITIZE
+void Eval1Input(CMSREGISTER const cmsUInt16Number Input[],
+                CMSREGISTER cmsUInt16Number Output[],
+                CMSREGISTER const cmsInterpParams* p16)
 {
        cmsS15Fixed16Number fk;
        cmsS15Fixed16Number k0, k1, rk, K0, K1;
@@ -273,20 +274,34 @@
        cmsUInt32Number OutChan;
        const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table;
 
-       v = Input[0] * p16 -> Domain[0];
-       fk = _cmsToFixedDomain(v);
 
-       k0 = FIXED_TO_INT(fk);
-       rk = (cmsUInt16Number) FIXED_REST_TO_INT(fk);
+       // if last value...
+       if (Input[0] == 0xffff || p16->Domain[0] == 0) {
 
-       k1 = k0 + (Input[0] != 0xFFFFU ? 1 : 0);
+           cmsUInt32Number y0 = p16->Domain[0] * p16->opta[0];
+           
+           for (OutChan = 0; OutChan < p16->nOutputs; OutChan++) {
+               Output[OutChan] = LutTable[y0 + OutChan];
+           }
+       }
+       else
+       {
 
-       K0 = p16 -> opta[0] * k0;
-       K1 = p16 -> opta[0] * k1;
+           v = Input[0] * p16->Domain[0];
+           fk = _cmsToFixedDomain(v);
 
-       for (OutChan=0; OutChan < p16->nOutputs; OutChan++) {
+           k0 = FIXED_TO_INT(fk);
+           rk = (cmsUInt16Number)FIXED_REST_TO_INT(fk);
 
-           Output[OutChan] = LinearInterp(rk, LutTable[K0+OutChan], LutTable[K1+OutChan]);
+           k1 = k0 + (Input[0] != 0xFFFFU ? 1 : 0);
+
+           K0 = p16->opta[0] * k0;
+           K1 = p16->opta[0] * k1;
+
+           for (OutChan = 0; OutChan < p16->nOutputs; OutChan++) {
+
+               Output[OutChan] = LinearInterp(rk, LutTable[K0 + OutChan], LutTable[K1 + OutChan]);
+           }
        }
 }
 
@@ -306,30 +321,36 @@
 
     val2 = fclamp(Value[0]);
 
-        // if last value...
-       if (val2 == 1.0) {
-           Output[0] = LutTable[p -> Domain[0]];
-           return;
-       }
+    // if last value...
+    if (val2 == 1.0 || p->Domain[0] == 0) {
 
-       val2 *= p -> Domain[0];
+        cmsUInt32Number start = p->Domain[0] * p->opta[0];
 
-       cell0 = (int) floor(val2);
-       cell1 = (int) ceil(val2);
+        for (OutChan = 0; OutChan < p->nOutputs; OutChan++) {
+            Output[OutChan] = LutTable[start + OutChan];
+        }        
+    }
+    else
+    {
+        val2 *= p->Domain[0];
 
-       // Rest is 16 LSB bits
-       rest = val2 - cell0;
+        cell0 = (int)floor(val2);
+        cell1 = (int)ceil(val2);
 
-       cell0 *= p -> opta[0];
-       cell1 *= p -> opta[0];
+        // Rest is 16 LSB bits
+        rest = val2 - cell0;
 
-       for (OutChan=0; OutChan < p->nOutputs; OutChan++) {
+        cell0 *= p->opta[0];
+        cell1 *= p->opta[0];
 
-            y0 = LutTable[cell0 + OutChan] ;
-            y1 = LutTable[cell1 + OutChan] ;
+        for (OutChan = 0; OutChan < p->nOutputs; OutChan++) {
+
+            y0 = LutTable[cell0 + OutChan];
+            y1 = LutTable[cell1 + OutChan];
 
             Output[OutChan] = y0 + (y1 - y0) * rest;
-       }
+        }
+    }
 }
 
 // Bilinear interpolation (16 bits) - cmsFloat32Number version
@@ -386,10 +407,10 @@
 }
 
 // Bilinear interpolation (16 bits) - optimized version
-static
-void BilinearInterp16(register const cmsUInt16Number Input[],
-                      register cmsUInt16Number Output[],
-                      register const cmsInterpParams* p)
+static CMS_NO_SANITIZE
+void BilinearInterp16(CMSREGISTER const cmsUInt16Number Input[],
+                      CMSREGISTER cmsUInt16Number Output[],
+                      CMSREGISTER const cmsInterpParams* p)
 
 {
 #define DENS(i,j) (LutTable[(i)+(j)+OutChan])
@@ -398,12 +419,13 @@
            const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table;
            int        OutChan, TotalOut;
            cmsS15Fixed16Number    fx, fy;
-  register int        rx, ry;
-           int        x0, y0;
-  register int        X0, X1, Y0, Y1;
-           int        d00, d01, d10, d11,
-                      dx0, dx1,
-                      dxy;
+           CMSREGISTER int        rx, ry;
+           int                    x0, y0;
+           CMSREGISTER int        X0, X1, Y0, Y1;
+
+           int                    d00, d01, d10, d11,
+                                  dx0, dx1,
+                                  dxy;
 
     TotalOut   = p -> nOutputs;
 
@@ -459,11 +481,12 @@
     int        x0, y0, z0,
                X0, Y0, Z0, X1, Y1, Z1;
     int        TotalOut, OutChan;
+
     cmsFloat32Number      fx, fy, fz,
-        d000, d001, d010, d011,
-        d100, d101, d110, d111,
-        dx00, dx01, dx10, dx11,
-        dxy0, dxy1, dxyz;
+                          d000, d001, d010, d011,
+                          d100, d101, d110, d111,
+                          dx00, dx01, dx10, dx11,
+                          dxy0, dxy1, dxyz;
 
     TotalOut   = p -> nOutputs;
 
@@ -517,10 +540,10 @@
 }
 
 // Trilinear interpolation (16 bits) - optimized version
-static
-void TrilinearInterp16(register const cmsUInt16Number Input[],
-                       register cmsUInt16Number Output[],
-                       register const cmsInterpParams* p)
+static CMS_NO_SANITIZE
+void TrilinearInterp16(CMSREGISTER const cmsUInt16Number Input[],
+                       CMSREGISTER cmsUInt16Number Output[],
+                       CMSREGISTER const cmsInterpParams* p)
 
 {
 #define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan])
@@ -529,13 +552,13 @@
            const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table;
            int        OutChan, TotalOut;
            cmsS15Fixed16Number    fx, fy, fz;
-  register int        rx, ry, rz;
-           int        x0, y0, z0;
-  register int        X0, X1, Y0, Y1, Z0, Z1;
-           int        d000, d001, d010, d011,
-                      d100, d101, d110, d111,
-                      dx00, dx01, dx10, dx11,
-                      dxy0, dxy1, dxyz;
+           CMSREGISTER int        rx, ry, rz;
+           int                    x0, y0, z0;
+           CMSREGISTER int        X0, X1, Y0, Y1, Z0, Z1;
+           int                    d000, d001, d010, d011,
+                                  d100, d101, d110, d111,
+                                  dx00, dx01, dx10, dx11,
+                                  dxy0, dxy1, dxyz;
 
     TotalOut   = p -> nOutputs;
 
@@ -603,8 +626,8 @@
 {
     const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table;
     cmsFloat32Number     px, py, pz;
-    int        x0, y0, z0,
-               X0, Y0, Z0, X1, Y1, Z1;
+    int                  x0, y0, z0,
+                         X0, Y0, Z0, X1, Y1, Z1;
     cmsFloat32Number     rx, ry, rz;
     cmsFloat32Number     c0, c1=0, c2=0, c3=0;
     int                  OutChan, TotalOut;
@@ -694,20 +717,17 @@
 
 #undef DENS
 
-
-
-
-static
-void TetrahedralInterp16(register const cmsUInt16Number Input[],
-                         register cmsUInt16Number Output[],
-                         register const cmsInterpParams* p)
+static CMS_NO_SANITIZE
+void TetrahedralInterp16(CMSREGISTER const cmsUInt16Number Input[],
+                         CMSREGISTER cmsUInt16Number Output[],
+                         CMSREGISTER const cmsInterpParams* p)
 {
     const cmsUInt16Number* LutTable = (cmsUInt16Number*) p -> Table;
     cmsS15Fixed16Number fx, fy, fz;
     cmsS15Fixed16Number rx, ry, rz;
     int x0, y0, z0;
     cmsS15Fixed16Number c0, c1, c2, c3, Rest;
-    cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1;
+    cmsUInt32Number X0, X1, Y0, Y1, Z0, Z1;
     cmsUInt32Number TotalOut = p -> nOutputs;
 
     fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]);
@@ -730,8 +750,8 @@
 
     Z0 = p -> opta[0] * z0;
     Z1 = (Input[2] == 0xFFFFU ? 0 : p->opta[0]);
-
-    LutTable = &LutTable[X0+Y0+Z0];
+    
+    LutTable += X0+Y0+Z0;
 
     // Output should be computed as x = ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest))
     // which expands as: x = (Rest + ((Rest+0x7fff)/0xFFFF) + 0x8000)>>16
@@ -831,10 +851,10 @@
 
 
 #define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan])
-static
-void Eval4Inputs(register const cmsUInt16Number Input[],
-                     register cmsUInt16Number Output[],
-                     register const cmsInterpParams* p16)
+static CMS_NO_SANITIZE
+void Eval4Inputs(CMSREGISTER const cmsUInt16Number Input[],
+                     CMSREGISTER cmsUInt16Number Output[],
+                     CMSREGISTER const cmsInterpParams* p16)
 {
     const cmsUInt16Number* LutTable;
     cmsS15Fixed16Number fk;
@@ -935,9 +955,9 @@
                                 c1 = c2 = c3 = 0;
                             }
 
-                            Rest = c1 * rx + c2 * ry + c3 * rz;
+        Rest = c1 * rx + c2 * ry + c3 * rz;
 
-                            Tmp1[OutChan] = (cmsUInt16Number)(c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)));
+        Tmp1[OutChan] = (cmsUInt16Number)(c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)));
     }
 
 
@@ -999,9 +1019,9 @@
                                 c1 = c2 = c3 = 0;
                             }
 
-                            Rest = c1 * rx + c2 * ry + c3 * rz;
+        Rest = c1 * rx + c2 * ry + c3 * rz;
 
-                            Tmp2[OutChan] = (cmsUInt16Number) (c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)));
+        Tmp2[OutChan] = (cmsUInt16Number) (c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)));
     }
 
 
@@ -1015,8 +1035,6 @@
 
 // For more that 3 inputs (i.e., CMYK)
 // evaluate two 3-dimensional interpolations and then linearly interpolate between them.
-
-
 static
 void Eval4InputsFloat(const cmsFloat32Number Input[],
                       cmsFloat32Number Output[],
@@ -1059,351 +1077,102 @@
        }
 }
 
-
-static
-void Eval5Inputs(register const cmsUInt16Number Input[],
-                 register cmsUInt16Number Output[],
-
-                 register const cmsInterpParams* p16)
-{
-       const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table;
-       cmsS15Fixed16Number fk;
-       cmsS15Fixed16Number k0, rk;
-       int K0, K1;
-       const cmsUInt16Number* T;
-       cmsUInt32Number i;
-       cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
-       cmsInterpParams p1;
-
-
-       fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]);
-       k0 = FIXED_TO_INT(fk);
-       rk = FIXED_REST_TO_INT(fk);
-
-       K0 = p16 -> opta[4] * k0;
-       K1 = p16 -> opta[4] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0));
-
-       p1 = *p16;
-       memmove(&p1.Domain[0], &p16 ->Domain[1], 4*sizeof(cmsUInt32Number));
-
-       T = LutTable + K0;
-       p1.Table = T;
-
-       Eval4Inputs(Input + 1, Tmp1, &p1);
-
-       T = LutTable + K1;
-       p1.Table = T;
-
-       Eval4Inputs(Input + 1, Tmp2, &p1);
-
-       for (i=0; i < p16 -> nOutputs; i++) {
-
-              Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]);
-       }
-
+#define EVAL_FNS(N,NM) static CMS_NO_SANITIZE \
+void Eval##N##Inputs(CMSREGISTER const cmsUInt16Number Input[], CMSREGISTER cmsUInt16Number Output[], CMSREGISTER const cmsInterpParams* p16)\
+{\
+       const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table;\
+       cmsS15Fixed16Number fk;\
+       cmsS15Fixed16Number k0, rk;\
+       int K0, K1;\
+       const cmsUInt16Number* T;\
+       cmsUInt32Number i;\
+       cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];\
+       cmsInterpParams p1;\
+\
+       fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]);\
+       k0 = FIXED_TO_INT(fk);\
+       rk = FIXED_REST_TO_INT(fk);\
+\
+       K0 = p16 -> opta[NM] * k0;\
+       K1 = p16 -> opta[NM] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0));\
+\
+       p1 = *p16;\
+       memmove(&p1.Domain[0], &p16 ->Domain[1], NM*sizeof(cmsUInt32Number));\
+\
+       T = LutTable + K0;\
+       p1.Table = T;\
+\
+       Eval##NM##Inputs(Input + 1, Tmp1, &p1);\
+\
+       T = LutTable + K1;\
+       p1.Table = T;\
+\
+       Eval##NM##Inputs(Input + 1, Tmp2, &p1);\
+\
+       for (i=0; i < p16 -> nOutputs; i++) {\
+\
+              Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]);\
+       }\
+}\
+\
+static void Eval##N##InputsFloat(const cmsFloat32Number Input[], \
+                                 cmsFloat32Number Output[],\
+                                 const cmsInterpParams * p)\
+{\
+       const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table;\
+       cmsFloat32Number rest;\
+       cmsFloat32Number pk;\
+       int k0, K0, K1;\
+       const cmsFloat32Number* T;\
+       cmsUInt32Number i;\
+       cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];\
+       cmsInterpParams p1;\
+\
+       pk = fclamp(Input[0]) * p->Domain[0];\
+       k0 = _cmsQuickFloor(pk);\
+       rest = pk - (cmsFloat32Number) k0;\
+\
+       K0 = p -> opta[NM] * k0;\
+       K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[NM]);\
+\
+       p1 = *p;\
+       memmove(&p1.Domain[0], &p ->Domain[1], NM*sizeof(cmsUInt32Number));\
+\
+       T = LutTable + K0;\
+       p1.Table = T;\
+\
+       Eval##NM##InputsFloat(Input + 1, Tmp1, &p1);\
+\
+       T = LutTable + K1;\
+       p1.Table = T;\
+\
+       Eval##NM##InputsFloat(Input + 1, Tmp2, &p1);\
+\
+       for (i=0; i < p -> nOutputs; i++) {\
+\
+              cmsFloat32Number y0 = Tmp1[i];\
+              cmsFloat32Number y1 = Tmp2[i];\
+\
+              Output[i] = y0 + (y1 - y0) * rest;\
+       }\
 }
 
 
-static
-void Eval5InputsFloat(const cmsFloat32Number Input[],
-                      cmsFloat32Number Output[],
-                      const cmsInterpParams* p)
-{
-       const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table;
-       cmsFloat32Number rest;
-       cmsFloat32Number pk;
-       int k0, K0, K1;
-       const cmsFloat32Number* T;
-       cmsUInt32Number i;
-       cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
-       cmsInterpParams p1;
-
-       pk = fclamp(Input[0]) * p->Domain[0];
-       k0 = _cmsQuickFloor(pk);
-       rest = pk - (cmsFloat32Number) k0;
-
-       K0 = p -> opta[4] * k0;
-       K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[4]);
-
-       p1 = *p;
-       memmove(&p1.Domain[0], &p ->Domain[1], 4*sizeof(cmsUInt32Number));
-
-       T = LutTable + K0;
-       p1.Table = T;
-
-       Eval4InputsFloat(Input + 1,  Tmp1, &p1);
-
-       T = LutTable + K1;
-       p1.Table = T;
-
-       Eval4InputsFloat(Input + 1,  Tmp2, &p1);
-
-       for (i=0; i < p -> nOutputs; i++) {
-
-              cmsFloat32Number y0 = Tmp1[i];
-              cmsFloat32Number y1 = Tmp2[i];
-
-              Output[i] = y0 + (y1 - y0) * rest;
-       }
-}
-
-
-
-static
-void Eval6Inputs(register const cmsUInt16Number Input[],
-                 register cmsUInt16Number Output[],
-                 register const cmsInterpParams* p16)
-{
-       const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table;
-       cmsS15Fixed16Number fk;
-       cmsS15Fixed16Number k0, rk;
-       int K0, K1;
-       const cmsUInt16Number* T;
-       cmsUInt32Number i;
-       cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
-       cmsInterpParams p1;
-
-       fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]);
-       k0 = FIXED_TO_INT(fk);
-       rk = FIXED_REST_TO_INT(fk);
-
-       K0 = p16 -> opta[5] * k0;
-       K1 = p16 -> opta[5] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0));
-
-       p1 = *p16;
-       memmove(&p1.Domain[0], &p16 ->Domain[1], 5*sizeof(cmsUInt32Number));
-
-       T = LutTable + K0;
-       p1.Table = T;
-
-       Eval5Inputs(Input + 1, Tmp1, &p1);
-
-       T = LutTable + K1;
-       p1.Table = T;
-
-       Eval5Inputs(Input + 1, Tmp2, &p1);
-
-       for (i=0; i < p16 -> nOutputs; i++) {
-
-              Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]);
-       }
-
-}
-
-
-static
-void Eval6InputsFloat(const cmsFloat32Number Input[],
-                      cmsFloat32Number Output[],
-                      const cmsInterpParams* p)
-{
-       const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table;
-       cmsFloat32Number rest;
-       cmsFloat32Number pk;
-       int k0, K0, K1;
-       const cmsFloat32Number* T;
-       cmsUInt32Number i;
-       cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
-       cmsInterpParams p1;
-
-       pk = fclamp(Input[0]) * p->Domain[0];
-       k0 = _cmsQuickFloor(pk);
-       rest = pk - (cmsFloat32Number) k0;
-
-       K0 = p -> opta[5] * k0;
-       K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[5]);
-
-       p1 = *p;
-       memmove(&p1.Domain[0], &p ->Domain[1], 5*sizeof(cmsUInt32Number));
-
-       T = LutTable + K0;
-       p1.Table = T;
-
-       Eval5InputsFloat(Input + 1,  Tmp1, &p1);
-
-       T = LutTable + K1;
-       p1.Table = T;
-
-       Eval5InputsFloat(Input + 1,  Tmp2, &p1);
-
-       for (i=0; i < p -> nOutputs; i++) {
-
-              cmsFloat32Number y0 = Tmp1[i];
-              cmsFloat32Number y1 = Tmp2[i];
-
-              Output[i] = y0 + (y1 - y0) * rest;
-       }
-}
-
-
-static
-void Eval7Inputs(register const cmsUInt16Number Input[],
-                 register cmsUInt16Number Output[],
-                 register const cmsInterpParams* p16)
-{
-       const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table;
-       cmsS15Fixed16Number fk;
-       cmsS15Fixed16Number k0, rk;
-       int K0, K1;
-       const cmsUInt16Number* T;
-       cmsUInt32Number i;
-       cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
-       cmsInterpParams p1;
-
-
-       fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]);
-       k0 = FIXED_TO_INT(fk);
-       rk = FIXED_REST_TO_INT(fk);
-
-       K0 = p16 -> opta[6] * k0;
-       K1 = p16 -> opta[6] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0));
-
-       p1 = *p16;
-       memmove(&p1.Domain[0], &p16 ->Domain[1], 6*sizeof(cmsUInt32Number));
-
-       T = LutTable + K0;
-       p1.Table = T;
-
-       Eval6Inputs(Input + 1, Tmp1, &p1);
-
-       T = LutTable + K1;
-       p1.Table = T;
-
-       Eval6Inputs(Input + 1, Tmp2, &p1);
-
-       for (i=0; i < p16 -> nOutputs; i++) {
-              Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]);
-       }
-}
-
-
-static
-void Eval7InputsFloat(const cmsFloat32Number Input[],
-                      cmsFloat32Number Output[],
-                      const cmsInterpParams* p)
-{
-       const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table;
-       cmsFloat32Number rest;
-       cmsFloat32Number pk;
-       int k0, K0, K1;
-       const cmsFloat32Number* T;
-       cmsUInt32Number i;
-       cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
-       cmsInterpParams p1;
-
-       pk = fclamp(Input[0]) * p->Domain[0];
-       k0 = _cmsQuickFloor(pk);
-       rest = pk - (cmsFloat32Number) k0;
-
-       K0 = p -> opta[6] * k0;
-       K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[6]);
-
-       p1 = *p;
-       memmove(&p1.Domain[0], &p ->Domain[1], 6*sizeof(cmsUInt32Number));
-
-       T = LutTable + K0;
-       p1.Table = T;
-
-       Eval6InputsFloat(Input + 1,  Tmp1, &p1);
-
-       T = LutTable + K1;
-       p1.Table = T;
-
-       Eval6InputsFloat(Input + 1,  Tmp2, &p1);
-
-
-       for (i=0; i < p -> nOutputs; i++) {
-
-              cmsFloat32Number y0 = Tmp1[i];
-              cmsFloat32Number y1 = Tmp2[i];
-
-              Output[i] = y0 + (y1 - y0) * rest;
-
-       }
-}
-
-static
-void Eval8Inputs(register const cmsUInt16Number Input[],
-                 register cmsUInt16Number Output[],
-                 register const cmsInterpParams* p16)
-{
-       const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table;
-       cmsS15Fixed16Number fk;
-       cmsS15Fixed16Number k0, rk;
-       int K0, K1;
-       const cmsUInt16Number* T;
-       cmsUInt32Number i;
-       cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
-       cmsInterpParams p1;
-
-       fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]);
-       k0 = FIXED_TO_INT(fk);
-       rk = FIXED_REST_TO_INT(fk);
-
-       K0 = p16 -> opta[7] * k0;
-       K1 = p16 -> opta[7] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0));
-
-       p1 = *p16;
-       memmove(&p1.Domain[0], &p16 ->Domain[1], 7*sizeof(cmsUInt32Number));
-
-       T = LutTable + K0;
-       p1.Table = T;
-
-       Eval7Inputs(Input + 1, Tmp1, &p1);
-
-       T = LutTable + K1;
-       p1.Table = T;
-       Eval7Inputs(Input + 1, Tmp2, &p1);
-
-       for (i=0; i < p16 -> nOutputs; i++) {
-              Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]);
-       }
-}
-
-
-
-static
-void Eval8InputsFloat(const cmsFloat32Number Input[],
-                      cmsFloat32Number Output[],
-                      const cmsInterpParams* p)
-{
-       const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table;
-       cmsFloat32Number rest;
-       cmsFloat32Number pk;
-       int k0, K0, K1;
-       const cmsFloat32Number* T;
-       cmsUInt32Number i;
-       cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
-       cmsInterpParams p1;
-
-       pk = fclamp(Input[0]) * p->Domain[0];
-       k0 = _cmsQuickFloor(pk);
-       rest = pk - (cmsFloat32Number) k0;
-
-       K0 = p -> opta[7] * k0;
-       K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[7]);
-
-       p1 = *p;
-       memmove(&p1.Domain[0], &p ->Domain[1], 7*sizeof(cmsUInt32Number));
-
-       T = LutTable + K0;
-       p1.Table = T;
-
-       Eval7InputsFloat(Input + 1,  Tmp1, &p1);
-
-       T = LutTable + K1;
-       p1.Table = T;
-
-       Eval7InputsFloat(Input + 1,  Tmp2, &p1);
-
-
-       for (i=0; i < p -> nOutputs; i++) {
-
-              cmsFloat32Number y0 = Tmp1[i];
-              cmsFloat32Number y1 = Tmp2[i];
+/**
+* Thanks to Carles Llopis for the templating idea
+*/
+EVAL_FNS(5, 4)
+EVAL_FNS(6, 5)
+EVAL_FNS(7, 6)
+EVAL_FNS(8, 7)
+EVAL_FNS(9, 8)
+EVAL_FNS(10, 9)
+EVAL_FNS(11, 10)
+EVAL_FNS(12, 11)
+EVAL_FNS(13, 12)
+EVAL_FNS(14, 13)
+EVAL_FNS(15, 14)
 
-              Output[i] = y0 + (y1 - y0) * rest;
-       }
-}
 
 // The default factory
 static
@@ -1504,6 +1273,53 @@
                    Interpolation.Lerp16    =  Eval8Inputs;
                break;
 
+           case 9: 
+               if (IsFloat)
+                   Interpolation.LerpFloat = Eval9InputsFloat;
+               else
+                   Interpolation.Lerp16 = Eval9Inputs;
+               break;
+
+           case 10: 
+               if (IsFloat)
+                   Interpolation.LerpFloat = Eval10InputsFloat;
+               else
+                   Interpolation.Lerp16 = Eval10Inputs;
+               break;
+
+           case 11:
+               if (IsFloat)
+                   Interpolation.LerpFloat = Eval11InputsFloat;
+               else
+                   Interpolation.Lerp16 = Eval11Inputs;
+               break;
+
+           case 12: 
+               if (IsFloat)
+                   Interpolation.LerpFloat = Eval12InputsFloat;
+               else
+                   Interpolation.Lerp16 = Eval12Inputs;
+               break;
+
+           case 13: 
+               if (IsFloat)
+                   Interpolation.LerpFloat = Eval13InputsFloat;
+               else
+                   Interpolation.Lerp16 = Eval13Inputs;
+               break;
+
+           case 14: 
+               if (IsFloat)
+                   Interpolation.LerpFloat = Eval14InputsFloat;
+               else
+                   Interpolation.Lerp16 = Eval14Inputs;
+               break;
+
+           case 15: 
+               if (IsFloat)
+                   Interpolation.LerpFloat = Eval15InputsFloat;
+               else
+                   Interpolation.Lerp16 = Eval15Inputs;
                break;
 
            default:
diff --git a/third_party/lcms/src/cmsio0.c b/third_party/lcms/src/cmsio0.c
index 1b4942c..c45022f 100644
--- a/third_party/lcms/src/cmsio0.c
+++ b/third_party/lcms/src/cmsio0.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -234,7 +234,7 @@
 
 // Create a iohandler for memory block. AccessMode=='r' assumes the iohandler is going to read, and makes
 // a copy of the memory block for letting user to free the memory after invoking open profile. In write
-// mode ("w"), Buffere points to the begin of memory block to be written.
+// mode ("w"), Buffer points to the begin of memory block to be written.
 cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromMem(cmsContext ContextID, void *Buffer, cmsUInt32Number size, const char* AccessMode)
 {
     cmsIOHANDLER* iohandler = NULL;
@@ -261,7 +261,7 @@
 
             _cmsFree(ContextID, fm);
             _cmsFree(ContextID, iohandler);
-            cmsSignalError(ContextID, cmsERROR_READ, "Couldn't allocate %ld bytes for profile", size);
+            cmsSignalError(ContextID, cmsERROR_READ, "Couldn't allocate %ld bytes for profile", (long) size);
             return NULL;
         }
 
@@ -374,24 +374,58 @@
 {
     cmsIOHANDLER* iohandler = NULL;
     FILE* fm = NULL;
-    cmsInt32Number fileLen;
+    cmsInt32Number fileLen;    
+    char mode[4] = { 0,0,0,0 };
 
     _cmsAssert(FileName != NULL);
     _cmsAssert(AccessMode != NULL);
 
     iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER));
     if (iohandler == NULL) return NULL;
+           
+    // Validate access mode
+    while (*AccessMode) {
 
-    switch (*AccessMode) {
+        switch (*AccessMode)
+        {        
+        case 'r':
+        case 'w':
+
+            if (mode[0] == 0) {
+                mode[0] = *AccessMode;
+                mode[1] = 'b';                
+            }
+            else {
+                _cmsFree(ContextID, iohandler);
+                cmsSignalError(ContextID, cmsERROR_FILE, "Access mode already specified '%c'", *AccessMode);
+                return NULL;
+            }
+            break;
+
+        // Close on exec. Not all runtime supports that. Up to the caller to decide.
+        case 'e':
+            mode[2] = 'e';
+            break;
+
+        default:
+            _cmsFree(ContextID, iohandler);
+            cmsSignalError(ContextID, cmsERROR_FILE, "Wrong access mode '%c'", *AccessMode);
+            return NULL;
+        }
+
+        AccessMode++;
+    }
+        
+    switch (mode[0]) {
 
     case 'r':
-        fm = fopen(FileName, "rb");
+        fm = fopen(FileName, mode);
         if (fm == NULL) {
             _cmsFree(ContextID, iohandler);
              cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' not found", FileName);
             return NULL;
         }                                     
-        fileLen = cmsfilelength(fm);
+        fileLen = (cmsInt32Number)cmsfilelength(fm);
         if (fileLen < 0)
         {
             fclose(fm);
@@ -399,12 +433,11 @@
             cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of file '%s'", FileName);
             return NULL;
         }
-
         iohandler -> ReportedSize = (cmsUInt32Number) fileLen;
         break;
 
     case 'w':
-        fm = fopen(FileName, "wb");
+        fm = fopen(FileName, mode);
         if (fm == NULL) {
             _cmsFree(ContextID, iohandler);
              cmsSignalError(ContextID, cmsERROR_FILE, "Couldn't create '%s'", FileName);
@@ -414,8 +447,7 @@
         break;
 
     default:
-        _cmsFree(ContextID, iohandler);
-         cmsSignalError(ContextID, cmsERROR_FILE, "Unknown access mode '%c'", *AccessMode);
+        _cmsFree(ContextID, iohandler);   // Would never reach      
         return NULL;
     }
 
@@ -442,7 +474,7 @@
     cmsIOHANDLER* iohandler = NULL;
     cmsInt32Number fileSize;
 
-    fileSize = cmsfilelength(Stream);
+    fileSize = (cmsInt32Number)cmsfilelength(Stream);
     if (fileSize < 0)
     {
         cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of stream");
@@ -479,10 +511,10 @@
 
 cmsIOHANDLER* CMSEXPORT cmsGetProfileIOhandler(cmsHPROFILE hProfile)
 {
-	_cmsICCPROFILE* Icc = (_cmsICCPROFILE*)hProfile;
+    _cmsICCPROFILE* Icc = (_cmsICCPROFILE*)hProfile;
 
-	if (Icc == NULL) return NULL;
-	return Icc->IOhandler;
+    if (Icc == NULL) return NULL;
+    return Icc->IOhandler;
 }
 
 #ifdef _WIN32_WCE
@@ -496,7 +528,6 @@
 // Creates an empty structure holding all required parameters
 cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID)
 {
-    time_t now = time(NULL);
     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) _cmsMallocZero(ContextID, sizeof(_cmsICCPROFILE));
     if (Icc == NULL) return NULL;
 
@@ -507,15 +538,23 @@
 
     // Set default version
     Icc ->Version =  0x02100000;
+    
+    // Set default device class
+    Icc->DeviceClass = cmsSigDisplayClass;
 
     // Set creation date/time
-    memmove(&Icc ->Created, gmtime(&now), sizeof(Icc ->Created));
+    if (!_cmsGetTime(&Icc->Created))
+        goto Error;
 
     // Create a mutex if the user provided proper plugin. NULL otherwise
     Icc ->UsrMutex = _cmsCreateMutex(ContextID);
 
     // Return the handle
     return (cmsHPROFILE) Icc;
+
+Error:
+    _cmsFree(ContextID, Icc);
+    return NULL;
 }
 
 cmsContext CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile)
@@ -649,7 +688,6 @@
     else  {
 
         // No, make a new one
-
         if (Icc -> TagCount >= MAX_TABLE_TAG) {
             cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG);
             return FALSE;
@@ -672,6 +710,25 @@
 
 
 
+// Checks for link compatibility
+static
+cmsBool CompatibleTypes(const cmsTagDescriptor* desc1, const cmsTagDescriptor* desc2)
+{
+    cmsUInt32Number i;
+
+    if (desc1 == NULL || desc2 == NULL) return FALSE;
+
+    if (desc1->nSupportedTypes != desc2->nSupportedTypes) return FALSE;
+    if (desc1->ElemCount != desc2->ElemCount) return FALSE;
+
+    for (i = 0; i < desc1->nSupportedTypes; i++)
+    {
+        if (desc1->SupportedTypes[i] != desc2->SupportedTypes[i]) return FALSE;
+    }
+
+    return TRUE;
+}
+
 // Enforces that the profile version is per. spec.
 // Operates on the big endian bytes from the profile.
 // Called before converting to platform endianness.
@@ -697,6 +754,29 @@
     return DWord;
 }
 
+// Check device class
+static 
+cmsBool validDeviceClass(cmsProfileClassSignature cl)
+{
+    if ((int)cl == 0) return TRUE; // We allow zero because older lcms versions defaulted to that.
+
+    switch (cl)
+    {    
+    case cmsSigInputClass:
+    case cmsSigDisplayClass:
+    case cmsSigOutputClass:
+    case cmsSigLinkClass:
+    case cmsSigAbstractClass:
+    case cmsSigColorSpaceClass:
+    case cmsSigNamedColorClass:
+        return TRUE;
+
+    default:
+        return FALSE;
+    }
+
+}
+
 // Read profile header and validate it
 cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc)
 {
@@ -733,6 +813,16 @@
     _cmsAdjustEndianess64(&Icc -> attributes, &Header.attributes);
     Icc -> Version         = _cmsAdjustEndianess32(_validatedVersion(Header.version));
 
+    if (Icc->Version > 0x5000000) {
+        cmsSignalError(Icc->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported profile version '0x%x'", Icc->Version);
+        return FALSE;
+    }
+
+    if (!validDeviceClass(Icc->DeviceClass)) {
+        cmsSignalError(Icc->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported device class '0x%x'", Icc->DeviceClass);
+        return FALSE;
+    }
+
     // Get size as reported in header
     HeaderSize = _cmsAdjustEndianess32(Header.size);
 
@@ -766,6 +856,7 @@
         if (!_cmsReadUInt32Number(io, &Tag.size)) return FALSE;
 
         // Perform some sanity check. Offset + size should fall inside file.
+        if (Tag.size == 0 || Tag.offset == 0) continue;
         if (Tag.offset + Tag.size > HeaderSize ||
             Tag.offset + Tag.size < Tag.offset)
                   continue;
@@ -776,12 +867,17 @@
 
        // Search for links
         for (j=0; j < Icc ->TagCount; j++) {
-
+           
             if ((Icc ->TagOffsets[j] == Tag.offset) &&
                 (Icc ->TagSizes[j]   == Tag.size) &&
                 (Icc ->TagNames[j]   == Tag.sig)) {
 
-                Icc ->TagLinked[Icc ->TagCount] = Icc ->TagNames[j];
+                // Check types. 
+                if (CompatibleTypes(_cmsGetTagDescriptor(Icc->ContextID, Icc->TagNames[j]),
+                                    _cmsGetTagDescriptor(Icc->ContextID, Tag.sig))) {
+
+                    Icc->TagLinked[Icc->TagCount] = Icc->TagNames[j];
+                }
             }
 
         }
@@ -789,6 +885,19 @@
         Icc ->TagCount++;
     }
 
+
+    for (i = 0; i < Icc->TagCount; i++) {
+        for (j = 0; j < Icc->TagCount; j++) {
+
+            // Tags cannot be duplicate
+            if ((i != j) && (Icc->TagNames[i] == Icc->TagNames[j])) {
+                cmsSignalError(Icc->ContextID, cmsERROR_RANGE, "Duplicate tag found");
+                return FALSE;
+            }
+
+        }
+    }
+
     return TRUE;
 }
 
@@ -1223,25 +1332,28 @@
             // In this case a blind copy of the block data is performed
             if (FileOrig != NULL && Icc -> TagOffsets[i]) {
 
-                cmsUInt32Number TagSize   = FileOrig -> TagSizes[i];
-                cmsUInt32Number TagOffset = FileOrig -> TagOffsets[i];
-                void* Mem;
+                if (FileOrig->IOhandler != NULL)
+                {
+                    cmsUInt32Number TagSize = FileOrig->TagSizes[i];
+                    cmsUInt32Number TagOffset = FileOrig->TagOffsets[i];
+                    void* Mem;
 
-                if (!FileOrig ->IOhandler->Seek(FileOrig ->IOhandler, TagOffset)) return FALSE;
+                    if (!FileOrig->IOhandler->Seek(FileOrig->IOhandler, TagOffset)) return FALSE;
 
-                Mem = _cmsMalloc(Icc ->ContextID, TagSize);
-                if (Mem == NULL) return FALSE;
+                    Mem = _cmsMalloc(Icc->ContextID, TagSize);
+                    if (Mem == NULL) return FALSE;
 
-                if (FileOrig ->IOhandler->Read(FileOrig->IOhandler, Mem, TagSize, 1) != 1) return FALSE;
-                if (!io ->Write(io, TagSize, Mem)) return FALSE;
-                _cmsFree(Icc ->ContextID, Mem);
+                    if (FileOrig->IOhandler->Read(FileOrig->IOhandler, Mem, TagSize, 1) != 1) return FALSE;
+                    if (!io->Write(io, TagSize, Mem)) return FALSE;
+                    _cmsFree(Icc->ContextID, Mem);
 
-                Icc -> TagSizes[i] = (io ->UsedSpace - Begin);
+                    Icc->TagSizes[i] = (io->UsedSpace - Begin);
 
 
-                // Align to 32 bit boundary.
-                if (! _cmsWriteAlignment(io))
-                    return FALSE;
+                    // Align to 32 bit boundary.
+                    if (!_cmsWriteAlignment(io))
+                        return FALSE;
+                }
             }
 
             continue;
@@ -1453,7 +1565,25 @@
     return rc;
 }
 
+// Free one tag contents
+static
+void freeOneTag(_cmsICCPROFILE* Icc, cmsUInt32Number i)
+{
+    if (Icc->TagPtrs[i]) {
 
+        cmsTagTypeHandler* TypeHandler = Icc->TagTypeHandlers[i];
+
+        if (TypeHandler != NULL) {
+            cmsTagTypeHandler LocalTypeHandler = *TypeHandler;
+
+            LocalTypeHandler.ContextID = Icc->ContextID;             
+            LocalTypeHandler.ICCVersion = Icc->Version;
+            LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc->TagPtrs[i]);
+        }
+        else
+            _cmsFree(Icc->ContextID, Icc->TagPtrs[i]);
+    }
+}
 
 // Closes a profile freeing any involved resources
 cmsBool  CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile)
@@ -1473,20 +1603,7 @@
 
     for (i=0; i < Icc -> TagCount; i++) {
 
-        if (Icc -> TagPtrs[i]) {
-
-            cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i];
-
-            if (TypeHandler != NULL) {
-                cmsTagTypeHandler LocalTypeHandler = *TypeHandler;
-
-                LocalTypeHandler.ContextID = Icc ->ContextID;              // As an additional parameters
-                LocalTypeHandler.ICCVersion = Icc ->Version;
-                LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]);
-            }
-            else
-                _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]);
-        }
+        freeOneTag(Icc, i);        
     }
 
     if (Icc ->IOhandler != NULL) {
@@ -1526,7 +1643,7 @@
 void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig)
 {
     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
-    cmsIOHANDLER* io = Icc ->IOhandler;
+    cmsIOHANDLER* io;
     cmsTagTypeHandler* TypeHandler;
     cmsTagTypeHandler LocalTypeHandler;
     cmsTagDescriptor*  TagDescriptor;
@@ -1538,8 +1655,12 @@
     if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return NULL;
 
     n = _cmsSearchTag(Icc, sig, TRUE);
-    if (n < 0) goto Error;               // Not found, return NULL
-
+    if (n < 0)
+    {
+        // Not found, return NULL
+        _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);
+        return NULL;
+    }
 
     // If the element is already in memory, return the pointer
     if (Icc -> TagPtrs[n]) {
@@ -1567,6 +1688,14 @@
 
     if (TagSize < 8) goto Error;
 
+    io = Icc ->IOhandler;
+
+    if (io == NULL) { // This is a built-in profile that has been manipulated, abort early
+
+        cmsSignalError(Icc->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted built-in profile.");
+        goto Error;
+    }
+
     // Seek to its location
     if (!io -> Seek(io, Offset))
         goto Error;
@@ -1590,7 +1719,7 @@
 
     if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error;
    
-    TagSize  -= 8;       // Alredy read by the type base logic
+    TagSize  -= 8;       // Already read by the type base logic
 
     // Get type handler
     TypeHandler = _cmsGetTagTypeHandler(Icc ->ContextID, BaseType);
@@ -1634,8 +1763,12 @@
     return Icc -> TagPtrs[n];
 
 
-    // Return error and unlock tha data
+    // Return error and unlock the data
 Error:
+
+    freeOneTag(Icc, n);    
+    Icc->TagPtrs[n] = NULL;
+    
     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
     return NULL;
 }
@@ -1774,11 +1907,9 @@
 
 }
 
-// Read and write raw data. The only way those function would work and keep consistence with normal read and write
-// is to do an additional step of serialization. That means, readRaw would issue a normal read and then convert the obtained
-// data to raw bytes by using the "write" serialization logic. And vice-versa. I know this may end in situations where
-// raw data written does not exactly correspond with the raw data proposed to cmsWriteRaw data, but this approach allows
-// to write a tag as raw data and the read it as handled.
+// Read and write raw data. Read/Write Raw/cooked pairs try to maintain consistency within the pair. Some sequences
+// raw/cooked would work, but at a cost. Data "cooked" may be converted to "raw" by using the "write" serialization logic.
+// In general it is better to avoid mixing pairs.
 
 cmsUInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* data, cmsUInt32Number BufferSize)
 {
@@ -1792,20 +1923,25 @@
     cmsUInt32Number rc;
     cmsUInt32Number Offset, TagSize;
 
+    // Sanity check
+    if (data != NULL && BufferSize == 0) return 0;
+
     if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0;
 
     // Search for given tag in ICC profile directory
+    
     i = _cmsSearchTag(Icc, sig, TRUE);
     if (i < 0) goto Error;                 // Not found, 
 
     // It is already read?
     if (Icc -> TagPtrs[i] == NULL) {
 
-        // No yet, get original position
+        // Not yet, get original position
         Offset   = Icc ->TagOffsets[i];
         TagSize  = Icc ->TagSizes[i];
 
         // read the data directly, don't keep copy
+        
         if (data != NULL) {
 
             if (BufferSize < TagSize)
@@ -1822,8 +1958,9 @@
         return Icc ->TagSizes[i];
     }
 
-    // The data has been already read, or written. But wait!, maybe the user choosed to save as
+    // The data has been already read, or written. But wait!, maybe the user choose to save as
     // raw data. In this case, return the raw data directly
+    
     if (Icc ->TagSaveAsRaw[i]) {
 
         if (data != NULL)  {
@@ -1842,9 +1979,9 @@
         return Icc ->TagSizes[i];
     }
 
-    // Already readed, or previously set by cmsWriteTag(). We need to serialize that
-    // data to raw in order to maintain consistency.
-
+    // Already read, or previously set by cmsWriteTag(). We need to serialize that
+    // data to raw to get something that makes sense
+    
     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
     Object = cmsReadTag(hProfile, sig);
     if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0;
diff --git a/third_party/lcms/src/cmsio1.c b/third_party/lcms/src/cmsio1.c
index 1343f2b..b923739 100644
--- a/third_party/lcms/src/cmsio1.c
+++ b/third_party/lcms/src/cmsio1.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -200,12 +200,7 @@
 
     return Lut;
 
-Error:
-    // memory pointed by GrayTRC is not a new malloc memory, so don't free it here, 
-    // memory pointed by GrayTRC will be freed when hProfile is closed.
-    // test file :0047776_Pocket Medicine_ The Massachusetts General Hospital Handbook of Internal Medicine-2.pdf
-    // Xiaochuan Liu, 20140421
-    //cmsFreeToneCurve(GrayTRC);
+Error:    
     cmsPipelineFree(Lut);
     return NULL;
 }
@@ -265,7 +260,7 @@
 
 
 
-// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded
+// Read the DToAX tag, adjusting the encoding of Lab or XYZ if needed
 static
 cmsPipeline* _cmsReadFloatInputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
 {
@@ -311,7 +306,7 @@
 // Read and create a BRAND NEW MPE LUT from a given profile. All stuff dependent of version, etc
 // is adjusted here in order to create a LUT that takes care of all those details.
 // We add intent = 0xffffffff as a way to read matrix shaper always, no matter of other LUT
-cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent)
+cmsPipeline* CMSEXPORT _cmsReadInputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent)
 {
     cmsTagTypeSignature OriginalType;
     cmsTagSignature tag16;
@@ -327,10 +322,8 @@
         if (nc == NULL) return NULL;
 
         Lut = cmsPipelineAlloc(ContextID, 0, 0);
-        if (Lut == NULL) {
-            cmsFreeNamedColorList(nc);
-            return NULL;
-        }
+        if (Lut == NULL)            
+            return NULL;        
 
         if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, TRUE)) ||
             !cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) {
@@ -541,7 +534,7 @@
 }
 
 
-// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded
+// Read the DToAX tag, adjusting the encoding of Lab or XYZ if needed
 static
 cmsPipeline* _cmsReadFloatOutputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
 {
@@ -586,7 +579,7 @@
 }
 
 // Create an output MPE LUT from agiven profile. Version mismatches are handled here
-cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent)
+cmsPipeline* CMSEXPORT _cmsReadOutputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent)
 {
     cmsTagTypeSignature OriginalType;
     cmsTagSignature tag16;
@@ -666,7 +659,7 @@
 
 // ---------------------------------------------------------------------------------------------------------------
 
-// Read the AToD0 tag, adjusting the encoding of Lab or XYZ if neded
+// Read the AToD0 tag, adjusting the encoding of Lab or XYZ if needed
 static
 cmsPipeline* _cmsReadFloatDevicelinkTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
 {
@@ -709,7 +702,7 @@
 
 // This one includes abstract profiles as well. Matrix-shaper cannot be obtained on that device class. The
 // tag name here may default to AToB0
-cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent)
+cmsPipeline* CMSEXPORT _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent)
 {
     cmsPipeline* Lut;
     cmsTagTypeSignature OriginalType;
@@ -744,8 +737,7 @@
 
         return Lut;
     Error:
-        cmsPipelineFree(Lut);
-        cmsFreeNamedColorList(nc);
+        cmsPipelineFree(Lut);        
         return NULL;
     }
 
@@ -859,6 +851,10 @@
            return FALSE;
     }
 
+    // Extended intents are not strictly CLUT-based
+    if (Intent > INTENT_ABSOLUTE_COLORIMETRIC)
+        return FALSE;
+
     return cmsIsTag(hProfile, TagTable[Intent]);
 
 }
diff --git a/third_party/lcms/src/cmslut.c b/third_party/lcms/src/cmslut.c
index 1c1e18f..22a16b3 100644
--- a/third_party/lcms/src/cmslut.c
+++ b/third_party/lcms/src/cmslut.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -292,7 +292,7 @@
 
 
 // Create a bunch of identity curves
-cmsStage* _cmsStageAllocIdentityCurves(cmsContext ContextID, cmsUInt32Number nChannels)
+cmsStage* CMSEXPORT _cmsStageAllocIdentityCurves(cmsContext ContextID, cmsUInt32Number nChannels)
 {
     cmsStage* mpe = cmsStageAllocToneCurves(ContextID, nChannels, NULL);
 
@@ -397,37 +397,31 @@
 
 
     NewElem = (_cmsStageMatrixData*) _cmsMallocZero(ContextID, sizeof(_cmsStageMatrixData));
-    if (NewElem == NULL) return NULL;
-
+    if (NewElem == NULL) goto Error;
+    NewMPE->Data = (void*)NewElem;
 
     NewElem ->Double = (cmsFloat64Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat64Number));
-
-    if (NewElem->Double == NULL) {
-        MatrixElemTypeFree(NewMPE);
-        return NULL;
-    }
-
+    if (NewElem->Double == NULL) goto Error;
+   
     for (i=0; i < n; i++) {
         NewElem ->Double[i] = Matrix[i];
     }
 
-
     if (Offset != NULL) {
 
         NewElem ->Offset = (cmsFloat64Number*) _cmsCalloc(ContextID, Rows, sizeof(cmsFloat64Number));
-        if (NewElem->Offset == NULL) {
-           MatrixElemTypeFree(NewMPE);
-           return NULL;
-        }
-
+        if (NewElem->Offset == NULL) goto Error;
+           
         for (i=0; i < Rows; i++) {
                 NewElem ->Offset[i] = Offset[i];
         }
-
     }
-
-    NewMPE ->Data  = (void*) NewElem;
+    
     return NewMPE;
+
+Error:
+    cmsStageFree(NewMPE);
+    return NULL;
 }
 
 
@@ -473,7 +467,7 @@
     for (rv = 1; b > 0; b--) {
 
         dim = Dims[b-1];
-        if (dim == 0) return 0;  // Error
+        if (dim <= 1) return 0;  // Error
 
         rv *= dim;
 
@@ -700,7 +694,7 @@
 
 
 static
-int IdentitySampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void * Cargo)
+int IdentitySampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void * Cargo)
 {
     int nChan = *(int*) Cargo;
     int i;
@@ -712,7 +706,7 @@
 }
 
 // Creates an MPE that just copies input to output
-cmsStage* _cmsStageAllocIdentityCLut(cmsContext ContextID, cmsUInt32Number nChan)
+cmsStage* CMSEXPORT _cmsStageAllocIdentityCLut(cmsContext ContextID, cmsUInt32Number nChan)
 {
     cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
     cmsStage* mpe ;
@@ -736,7 +730,7 @@
 
 
 // Quantize a value 0 <= i < MaxSamples to 0..0xffff
-cmsUInt16Number _cmsQuantizeVal(cmsFloat64Number i, cmsUInt32Number MaxSamples)
+cmsUInt16Number CMSEXPORT _cmsQuantizeVal(cmsFloat64Number i, cmsUInt32Number MaxSamples)
 {
     cmsFloat64Number x;
 
@@ -969,7 +963,7 @@
 
 
 // No dup or free routines needed, as the structure has no pointers in it.
-cmsStage* _cmsStageAllocLab2XYZ(cmsContext ContextID)
+cmsStage* CMSEXPORT _cmsStageAllocLab2XYZ(cmsContext ContextID)
 {
     return _cmsStageAllocPlaceholder(ContextID, cmsSigLab2XYZElemType, 3, 3, EvaluateLab2XYZ, NULL, NULL, NULL);
 }
@@ -1020,7 +1014,7 @@
 // ********************************************************************************
 
 // Matrix-based conversion, which is more accurate, but slower and cannot properly be saved in devicelink profiles
-cmsStage* _cmsStageAllocLabV2ToV4(cmsContext ContextID)
+cmsStage* CMSEXPORT _cmsStageAllocLabV2ToV4(cmsContext ContextID)
 {
     static const cmsFloat64Number V2ToV4[] = { 65535.0/65280.0, 0, 0,
                                      0, 65535.0/65280.0, 0,
@@ -1036,7 +1030,7 @@
 
 
 // Reverse direction
-cmsStage* _cmsStageAllocLabV4ToV2(cmsContext ContextID)
+cmsStage* CMSEXPORT _cmsStageAllocLabV4ToV2(cmsContext ContextID)
 {
     static const cmsFloat64Number V4ToV2[] = { 65280.0/65535.0, 0, 0,
                                      0, 65280.0/65535.0, 0,
@@ -1179,7 +1173,7 @@
     cmsUNUSED_PARAMETER(mpe);
 }
 
-cmsStage* _cmsStageAllocXYZ2Lab(cmsContext ContextID)
+cmsStage* CMSEXPORT _cmsStageAllocXYZ2Lab(cmsContext ContextID)
 {
     return _cmsStageAllocPlaceholder(ContextID, cmsSigXYZ2LabElemType, 3, 3, EvaluateXYZ2Lab, NULL, NULL, NULL);
 
@@ -1232,6 +1226,11 @@
     return mpe -> Data;
 }
 
+cmsContext CMSEXPORT cmsGetStageContextID(const cmsStage* mpe)
+{
+    return mpe -> ContextID;
+}
+
 cmsStage*  CMSEXPORT cmsStageNext(const cmsStage* mpe)
 {
     return mpe -> Next;
@@ -1317,7 +1316,7 @@
 
 // Default to evaluate the LUT on 16 bit-basis. Precision is retained.
 static
-void _LUTeval16(register const cmsUInt16Number In[], register cmsUInt16Number Out[],  register const void* D)
+void _LUTeval16(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[],  CMSREGISTER const void* D)
 {
     cmsPipeline* lut = (cmsPipeline*) D;
     cmsStage *mpe;
@@ -1343,7 +1342,7 @@
 
 // Does evaluate the LUT on cmsFloat32Number-basis.
 static
-void _LUTevalFloat(register const cmsFloat32Number In[], register cmsFloat32Number Out[], const void* D)
+void _LUTevalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const void* D)
 {
     cmsPipeline* lut = (cmsPipeline*) D;
     cmsStage *mpe;
@@ -1664,7 +1663,7 @@
 // This function may be used to set the optional evaluator and a block of private data. If private data is being used, an optional
 // duplicator and free functions should also be specified in order to duplicate the LUT construct. Use NULL to inhibit such functionality.
 void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut,
-                                        _cmsOPTeval16Fn Eval16,
+                                        _cmsPipelineEval16Fn Eval16,
                                         void* PrivateData,
                                         _cmsFreeUserDataFn FreePrivateDataFn,
                                         _cmsDupUserDataFn  DupPrivateDataFn)
diff --git a/third_party/lcms/src/cmsmd5.c b/third_party/lcms/src/cmsmd5.c
index 4b16ad4..cd6ce97 100644
--- a/third_party/lcms/src/cmsmd5.c
+++ b/third_party/lcms/src/cmsmd5.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -65,10 +65,9 @@
 
 
 static
-void MD5_Transform(cmsUInt32Number buf[4], cmsUInt32Number in[16])
-
+void cmsMD5_Transform(cmsUInt32Number buf[4], cmsUInt32Number in[16])
 {
-    register cmsUInt32Number a, b, c, d;
+    CMSREGISTER cmsUInt32Number a, b, c, d;
 
     a = buf[0];
     b = buf[1];
@@ -151,8 +150,8 @@
 
 
 // Create a MD5 object
-static
-cmsHANDLE  MD5alloc(cmsContext ContextID)
+
+cmsHANDLE CMSEXPORT cmsMD5alloc(cmsContext ContextID)
 {
     _cmsMD5* ctx = (_cmsMD5*) _cmsMallocZero(ContextID, sizeof(_cmsMD5));
     if (ctx == NULL) return NULL;
@@ -170,9 +169,7 @@
     return (cmsHANDLE) ctx;
 }
 
-
-static
-void MD5add(cmsHANDLE Handle, cmsUInt8Number* buf, cmsUInt32Number len)
+void CMSEXPORT cmsMD5add(cmsHANDLE Handle, const cmsUInt8Number* buf, cmsUInt32Number len)
 {
     _cmsMD5* ctx = (_cmsMD5*) Handle;
     cmsUInt32Number t;
@@ -198,7 +195,7 @@
         memmove(p, buf, t);
         byteReverse(ctx->in, 16);
 
-        MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
+        cmsMD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
         buf += t;
         len -= t;
     }
@@ -206,7 +203,7 @@
     while (len >= 64) {
         memmove(ctx->in, buf, 64);
         byteReverse(ctx->in, 16);
-        MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
+        cmsMD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
         buf += 64;
         len -= 64;
     }
@@ -215,8 +212,7 @@
 }
 
 // Destroy the object and return the checksum
-static
-void MD5finish(cmsProfileID* ProfileID,  cmsHANDLE Handle)
+void CMSEXPORT cmsMD5finish(cmsProfileID* ProfileID,  cmsHANDLE Handle)
 {
     _cmsMD5* ctx = (_cmsMD5*) Handle;
     cmsUInt32Number count;
@@ -233,7 +229,7 @@
 
         memset(p, 0, count);
         byteReverse(ctx->in, 16);
-        MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
+        cmsMD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
 
         memset(ctx->in, 0, 56);
     } else {
@@ -244,7 +240,7 @@
     ((cmsUInt32Number *) ctx->in)[14] = ctx->bits[0];
     ((cmsUInt32Number *) ctx->in)[15] = ctx->bits[1];
 
-    MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
+    cmsMD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
 
     byteReverse((cmsUInt8Number *) ctx->buf, 4);
     memmove(ProfileID ->ID8, ctx->buf, 16);
@@ -290,11 +286,11 @@
     if (!cmsSaveProfileToMem(hProfile, Mem, &BytesNeeded)) goto Error;
 
     // Create MD5 object
-    MD5 = MD5alloc(ContextID);
+    MD5 = cmsMD5alloc(ContextID);
     if (MD5 == NULL) goto Error;
 
     // Add all bytes
-    MD5add(MD5, Mem, BytesNeeded);
+    cmsMD5add(MD5, Mem, BytesNeeded);
 
     // Temp storage is no longer needed
     _cmsFree(ContextID, Mem);
@@ -303,7 +299,7 @@
     memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
 
     // And store the ID
-    MD5finish(&Icc ->ProfileID,  MD5);
+    cmsMD5finish(&Icc ->ProfileID,  MD5);
     return TRUE;
 
 Error:
diff --git a/third_party/lcms/src/cmsmtrx.c b/third_party/lcms/src/cmsmtrx.c
index a83d39d..5f5a3c8 100644
--- a/third_party/lcms/src/cmsmtrx.c
+++ b/third_party/lcms/src/cmsmtrx.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
diff --git a/third_party/lcms/src/cmsnamed.c b/third_party/lcms/src/cmsnamed.c
index 9cfd228..54d1abf 100644
--- a/third_party/lcms/src/cmsnamed.c
+++ b/third_party/lcms/src/cmsnamed.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -180,34 +180,31 @@
 
 // Convert from a 3-char code to a cmsUInt16Number. It is done in this way because some
 // compilers don't properly align beginning of strings
-
 static
 cmsUInt16Number strTo16(const char str[3])
-{
-    const cmsUInt8Number* ptr8 = (const cmsUInt8Number*)str;
-    cmsUInt16Number n = (cmsUInt16Number) (((cmsUInt16Number) ptr8[1] << 8) | ptr8[0]);
+{   
+    const cmsUInt8Number* ptr8;
+    cmsUInt16Number n;
 
-    return _cmsAdjustEndianess16(n);
+    // For non-existent strings
+    if (str == NULL) return 0;
+    ptr8 = (const cmsUInt8Number*)str;
+    n = (cmsUInt16Number)(((cmsUInt16Number)ptr8[0] << 8) | ptr8[1]);
+
+    return n;
 }
 
 static
 void strFrom16(char str[3], cmsUInt16Number n)
 {
-    // Assuming this would be aligned
-    union {
-
-       cmsUInt16Number n;
-       cmsUInt8Number str[2];
-       
-    } c;
-
-    c.n = _cmsAdjustEndianess16(n);  
-
-    str[0] = (char) c.str[0]; str[1] = (char) c.str[1]; str[2] = (char) 0;
+    str[0] = (char)(n >> 8);
+    str[1] = (char)n;
+    str[2] = (char)0;
 
 }
 
 // Add an ASCII entry. Do not add any \0 termination (ICC1v43_2010-12.pdf page 61)
+// In the case the user explicitly sets an empty string, we force a \0
 cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* ASCIIString)
 {
     cmsUInt32Number i, len = (cmsUInt32Number) strlen(ASCIIString);
@@ -218,6 +215,12 @@
 
     if (mlu == NULL) return FALSE;
 
+    // len == 0 would prevent operation, so we set a empty string pointing to zero
+    if (len == 0)
+    {
+        len = 1;
+    }
+
     WStr = (wchar_t*) _cmsCalloc(mlu ->ContextID, len,  sizeof(wchar_t));
     if (WStr == NULL) return FALSE;
 
@@ -255,6 +258,9 @@
     if (WideString == NULL) return FALSE;
 
     len = (cmsUInt32Number) (mywcslen(WideString)) * sizeof(wchar_t);
+    if (len == 0)
+        len = sizeof(wchar_t);
+
     return AddMLUBlock(mlu, len, WideString, Lang, Cntry);
 }
 
@@ -364,6 +370,8 @@
 
     if (len != NULL) *len   = v ->Len;
 
+    if (v->StrW + v->Len > mlu->PoolSize) return NULL;
+
     return(wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v ->StrW);
 }
 
@@ -536,10 +544,14 @@
 // Allocate a list for n elements
 cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUInt32Number n, cmsUInt32Number ColorantCount, const char* Prefix, const char* Suffix)
 {
-    cmsNAMEDCOLORLIST* v = (cmsNAMEDCOLORLIST*) _cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST));
-
+    cmsNAMEDCOLORLIST* v;
+    
+    if (ColorantCount > cmsMAXCHANNELS) 
+        return NULL;
+   
+    v = (cmsNAMEDCOLORLIST*)_cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST));
     if (v == NULL) return NULL;
-
+    
     v ->List      = NULL;
     v ->nColors   = 0;
     v ->ContextID  = ContextID;
@@ -579,7 +591,8 @@
 
     // For really large tables we need this
     while (NewNC ->Allocated < v ->Allocated){
-        if (!GrowNamedColorList(NewNC)) {
+        if (!GrowNamedColorList(NewNC))
+        {
             cmsFreeNamedColorList(NewNC);
             return NULL;
         }
@@ -634,7 +647,7 @@
      return NamedColorList ->nColors;
 }
 
-// Info aboout a given color
+// Info about a given color
 cmsBool  CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor,
                                      char* Name,
                                      char* Prefix,
@@ -733,7 +746,7 @@
 
 
 // Named color lookup element
-cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS)
+cmsStage* CMSEXPORT _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS)
 {
     return _cmsStageAllocPlaceholder(NamedColorList ->ContextID,
                                    cmsSigNamedColorElemType,
@@ -750,7 +763,13 @@
 cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform)
 {
     _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
-    cmsStage* mpe  = v ->Lut->Elements;
+    cmsStage* mpe;
+    
+    if (v == NULL) return NULL;
+    if (v->Lut == NULL) return NULL;
+
+    mpe = v->Lut->Elements;
+    if (mpe == NULL) return NULL;
 
     if (mpe ->Type != cmsSigNamedColorElemType) return NULL;
     return (cmsNAMEDCOLORLIST*) mpe ->Data;
diff --git a/third_party/lcms/src/cmsopt.c b/third_party/lcms/src/cmsopt.c
index 8a11718..a547570 100644
--- a/third_party/lcms/src/cmsopt.c
+++ b/third_party/lcms/src/cmsopt.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -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 ----------------------------------------------------------------------------------------------------------
 
@@ -321,9 +330,9 @@
 }
 
 static
-void Eval16nop1D(register const cmsUInt16Number Input[],
-                 register cmsUInt16Number Output[],
-                 register const struct _cms_interp_struc* p)
+void Eval16nop1D(CMSREGISTER const cmsUInt16Number Input[],
+                 CMSREGISTER cmsUInt16Number Output[],
+                 CMSREGISTER const struct _cms_interp_struc* p)
 {
     Output[0] = Input[0];
 
@@ -331,9 +340,9 @@
 }
 
 static
-void PrelinEval16(register const cmsUInt16Number Input[],
-                  register cmsUInt16Number Output[],
-                  register const void* D)
+void PrelinEval16(CMSREGISTER const cmsUInt16Number Input[],
+                  CMSREGISTER cmsUInt16Number Output[],
+                  CMSREGISTER const void* D)
 {
     Prelin16Data* p16 = (Prelin16Data*) D;
     cmsUInt16Number  StageABC[MAX_INPUT_DIMENSIONS];
@@ -412,7 +421,20 @@
 
 
     p16 -> EvalCurveOut16 = (_cmsInterpFn16*) _cmsCalloc(ContextID, nOutputs, sizeof(_cmsInterpFn16));
+    if (p16->EvalCurveOut16 == NULL)
+    {
+        _cmsFree(ContextID, p16);
+        return NULL;
+    }
+
     p16 -> ParamsCurveOut16 = (cmsInterpParams**) _cmsCalloc(ContextID, nOutputs, sizeof(cmsInterpParams* ));
+    if (p16->ParamsCurveOut16 == NULL)
+    {
+
+        _cmsFree(ContextID, p16->EvalCurveOut16);
+        _cmsFree(ContextID, p16);
+        return NULL;
+    }
 
     for (i=0; i < nOutputs; i++) {
 
@@ -439,7 +461,9 @@
 // Sampler implemented by another LUT. This is a clean way to precalculate the devicelink 3D CLUT for
 // almost any transform. We use floating point precision and then convert from floating point to 16 bits.
 static
-cmsInt32Number XFormSampler16(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
+cmsInt32Number XFormSampler16(CMSREGISTER const cmsUInt16Number In[], 
+                              CMSREGISTER cmsUInt16Number Out[], 
+                              CMSREGISTER void* Cargo)
 {
     cmsPipeline* Lut = (cmsPipeline*) Cargo;
     cmsFloat32Number InFloat[cmsMAXCHANNELS], OutFloat[cmsMAXCHANNELS];
@@ -556,10 +580,10 @@
                 return FALSE;
             }
 
-            for (i = 0; i < (int) nChannelsOut; i++)
-                Grid->Tab.T[index + i] = Value[i];
+    for (i = 0; i < (int) nChannelsOut; i++)
+        Grid->Tab.T[index + i] = Value[i];
 
-            return TRUE;
+    return TRUE;
 }
 
 // Auxiliary, to see if two values are equal or very different
@@ -665,7 +689,6 @@
 {
     cmsPipeline* Src = NULL;
     cmsPipeline* Dest = NULL;
-    cmsStage* mpe;
     cmsStage* CLUT;
     cmsStage *KeepPreLin = NULL, *KeepPostLin = NULL;
     cmsUInt32Number nGridPoints;
@@ -677,7 +700,7 @@
     cmsToneCurve** DataSetOut;
     Prelin16Data* p16;
 
-    // This is a loosy optimization! does not apply in floating-point cases
+    // This is a lossy optimization! does not apply in floating-point cases
     if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE;
 
     ColorSpace       = _cmsICCcolorSpace((int) T_COLORSPACE(*InputFormat));
@@ -687,7 +710,7 @@
     if (ColorSpace == (cmsColorSpaceSignature)0 ||
         OutputColorSpace == (cmsColorSpaceSignature)0) return FALSE;
 
-    nGridPoints      = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags);
+    nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags);
 
     // For empty LUTs, 2 points are enough
     if (cmsPipelineStageCount(*Lut) == 0)
@@ -695,13 +718,6 @@
 
     Src = *Lut;
 
-    // Named color pipelines cannot be optimized either
-    for (mpe = cmsPipelineGetPtrToFirstStage(Src);
-        mpe != NULL;
-        mpe = cmsStageNext(mpe)) {
-            if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE;
-    }
-
     // Allocate an empty LUT
     Dest =  cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels);
     if (!Dest) return FALSE;
@@ -798,7 +814,7 @@
 
     if (DataSetIn == NULL && DataSetOut == NULL) {
 
-        _cmsPipelineSetOptimizationParameters(Dest, (_cmsOPTeval16Fn) DataCLUT->Params->Interpolation.Lerp16, DataCLUT->Params, NULL, NULL);
+        _cmsPipelineSetOptimizationParameters(Dest, Lerp16Adapter, DataCLUT->Params, NULL, NULL);
     }
     else {
 
@@ -941,19 +957,19 @@
 
 // A optimized interpolation for 8-bit input.
 #define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan])
-static
-void PrelinEval8(register const cmsUInt16Number Input[],
-                  register cmsUInt16Number Output[],
-                  register const void* D)
+static CMS_NO_SANITIZE
+void PrelinEval8(CMSREGISTER const cmsUInt16Number Input[],
+                 CMSREGISTER cmsUInt16Number Output[],
+                 CMSREGISTER const void* D)
 {
 
     cmsUInt8Number         r, g, b;
     cmsS15Fixed16Number    rx, ry, rz;
     cmsS15Fixed16Number    c0, c1, c2, c3, Rest;
     int                    OutChan;
-    register cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1;
+    CMSREGISTER cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1;
     Prelin8Data* p8 = (Prelin8Data*) D;
-    register const cmsInterpParams* p = p8 ->p;
+    CMSREGISTER const cmsInterpParams* p = p8 ->p;
     int                    TotalOut = (int) p -> nOutputs;
     const cmsUInt16Number* LutTable = (const cmsUInt16Number*) p->Table;
 
@@ -961,9 +977,9 @@
     g = (cmsUInt8Number) (Input[1] >> 8);
     b = (cmsUInt8Number) (Input[2] >> 8);
 
-    X0 = X1 = (cmsS15Fixed16Number) p8->X0[r];
-    Y0 = Y1 = (cmsS15Fixed16Number) p8->Y0[g];
-    Z0 = Z1 = (cmsS15Fixed16Number) p8->Z0[b];
+    X0 = (cmsS15Fixed16Number) p8->X0[r];
+    Y0 = (cmsS15Fixed16Number) p8->Y0[g];
+    Z0 = (cmsS15Fixed16Number) p8->Z0[b];
 
     rx = p8 ->rx[r];
     ry = p8 ->ry[g];
@@ -1024,8 +1040,8 @@
                                 c1 = c2 = c3 = 0;
                             }
 
-                            Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
-                            Output[OutChan] = (cmsUInt16Number) (c0 + ((Rest + (Rest >> 16)) >> 16));
+        Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
+        Output[OutChan] = (cmsUInt16Number) (c0 + ((Rest + (Rest >> 16)) >> 16));
 
     }
 }
@@ -1069,12 +1085,11 @@
     cmsStage* OptimizedCLUTmpe;
     cmsColorSpaceSignature ColorSpace, OutputColorSpace;
     cmsStage* OptimizedPrelinMpe;
-    cmsStage* mpe;
     cmsToneCurve** OptimizedPrelinCurves;
     _cmsStageCLutData* OptimizedPrelinCLUT;
 
 
-    // This is a loosy optimization! does not apply in floating-point cases
+    // This is a lossy optimization! does not apply in floating-point cases
     if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE;
 
     // Only on chunky RGB
@@ -1090,14 +1105,7 @@
     }
 
     OriginalLut = *Lut;
-
-   // Named color pipelines cannot be optimized either
-   for (mpe = cmsPipelineGetPtrToFirstStage(OriginalLut);
-         mpe != NULL;
-         mpe = cmsStageNext(mpe)) {
-            if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE;
-    }
-
+   
     ColorSpace       = _cmsICCcolorSpace((int) T_COLORSPACE(*InputFormat));
     OutputColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*OutputFormat));
 
@@ -1117,6 +1125,7 @@
     {
         cmsStage* last = cmsPipelineGetPtrToLastStage(OriginalLut);
 
+        if (last == NULL) goto Error;
         if (cmsStageType(last) == cmsSigCurveSetElemType) {
 
             _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*)cmsStageData(last);
@@ -1363,9 +1372,9 @@
 }
 
 static
-void FastEvaluateCurves8(register const cmsUInt16Number In[],
-                          register cmsUInt16Number Out[],
-                          register const void* D)
+void FastEvaluateCurves8(CMSREGISTER const cmsUInt16Number In[],
+                         CMSREGISTER cmsUInt16Number Out[],
+                         CMSREGISTER const void* D)
 {
     Curves16Data* Data = (Curves16Data*) D;
     int x;
@@ -1380,9 +1389,9 @@
 
 
 static
-void FastEvaluateCurves16(register const cmsUInt16Number In[],
-                          register cmsUInt16Number Out[],
-                          register const void* D)
+void FastEvaluateCurves16(CMSREGISTER const cmsUInt16Number In[],
+                          CMSREGISTER cmsUInt16Number Out[],
+                          CMSREGISTER const void* D)
 {
     Curves16Data* Data = (Curves16Data*) D;
     cmsUInt32Number i;
@@ -1394,9 +1403,9 @@
 
 
 static
-void FastIdentity16(register const cmsUInt16Number In[],
-                    register cmsUInt16Number Out[],
-                    register const void* D)
+void FastIdentity16(CMSREGISTER const cmsUInt16Number In[],
+                    CMSREGISTER cmsUInt16Number Out[],
+                    CMSREGISTER const void* D)
 {
     cmsPipeline* Lut = (cmsPipeline*) D;
     cmsUInt32Number i;
@@ -1421,7 +1430,7 @@
     cmsStage* ObtainedCurves = NULL;
 
 
-    // This is a loosy optimization! does not apply in floating-point cases
+    // This is a lossy optimization! does not apply in floating-point cases
     if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE;
 
     //  Only curves in this LUT?
@@ -1471,27 +1480,26 @@
 
     // Maybe the curves are linear at the end
     if (!AllCurvesAreLinear(ObtainedCurves)) {
+       _cmsStageToneCurvesData* Data;
 
         if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, ObtainedCurves))
             goto Error;
+        Data = (_cmsStageToneCurvesData*) cmsStageData(ObtainedCurves);
+        ObtainedCurves = NULL;
 
         // If the curves are to be applied in 8 bits, we can save memory
         if (_cmsFormatterIs8bit(*InputFormat)) {
-
-            _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) ObtainedCurves ->Data;
              Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 256, Data ->TheCurves);
 
-             if (c16 == NULL) goto Error; 
+             if (c16 == NULL) goto Error;
              *dwFlags |= cmsFLAGS_NOCACHE;
             _cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves8, c16, CurvesFree, CurvesDup);
 
         }
         else {
-
-            _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) cmsStageData(ObtainedCurves);
              Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 65536, Data ->TheCurves);
 
-             if (c16 == NULL) goto Error; 
+             if (c16 == NULL) goto Error;
              *dwFlags |= cmsFLAGS_NOCACHE;
             _cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves16, c16, CurvesFree, CurvesDup);
         }
@@ -1551,13 +1559,13 @@
 }
 
 
-// A fast matrix-shaper evaluator for 8 bits. This is a bit ticky since I'm using 1.14 signed fixed point
+// A fast matrix-shaper evaluator for 8 bits. This is a bit tricky since I'm using 1.14 signed fixed point
 // to accomplish some performance. Actually it takes 256x3 16 bits tables and 16385 x 3 tables of 8 bits,
 // in total about 50K, and the performance boost is huge!
-static
-void MatShaperEval16(register const cmsUInt16Number In[],
-                     register cmsUInt16Number Out[],
-                     register const void* D)
+static CMS_NO_SANITIZE
+void MatShaperEval16(CMSREGISTER const cmsUInt16Number In[],
+                     CMSREGISTER cmsUInt16Number Out[],
+                     CMSREGISTER const void* D)
 {
     MatShaper8Data* p = (MatShaper8Data*) D;
     cmsS1Fixed14Number r, g, b;
@@ -1730,6 +1738,10 @@
               _cmsStageMatrixData* Data1 = (_cmsStageMatrixData*)cmsStageData(Matrix1);
               _cmsStageMatrixData* Data2 = (_cmsStageMatrixData*)cmsStageData(Matrix2);
 
+              // Only RGB to RGB
+              if (Matrix1->InputChannels != 3 || Matrix1->OutputChannels != 3 ||
+                  Matrix2->InputChannels != 3 || Matrix2->OutputChannels != 3) return FALSE;
+
               // Input offset should be zero
               if (Data1->Offset != NULL) return FALSE;
 
@@ -1927,7 +1939,7 @@
 }
 
 // The entry point for LUT optimization
-cmsBool _cmsOptimizePipeline(cmsContext ContextID,
+cmsBool CMSEXPORT _cmsOptimizePipeline(cmsContext ContextID,
                              cmsPipeline**    PtrLut,
                              cmsUInt32Number  Intent,
                              cmsUInt32Number* InputFormat,
@@ -1937,6 +1949,7 @@
     _cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin);
     _cmsOptimizationCollection* Opts;
     cmsBool AnySuccess = FALSE;
+    cmsStage* mpe;
 
     // A CLUT is being asked, so force this specific optimization
     if (*dwFlags & cmsFLAGS_FORCE_CLUT) {
@@ -1951,6 +1964,13 @@
         return TRUE;
     }
 
+    // Named color pipelines cannot be optimized 
+    for (mpe = cmsPipelineGetPtrToFirstStage(*PtrLut);
+        mpe != NULL;
+        mpe = cmsStageNext(mpe)) {
+        if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE;
+    }
+
     // Try to get rid of identities and trivial conversions.
     AnySuccess = PreOptimize(*PtrLut);
 
diff --git a/third_party/lcms/src/cmspack.c b/third_party/lcms/src/cmspack.c
index 84b0097..7b92846 100644
--- a/third_party/lcms/src/cmspack.c
+++ b/third_party/lcms/src/cmspack.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -79,6 +79,7 @@
 #define ANYSWAP         DOSWAP_SH(1)
 #define ANYSWAPFIRST    SWAPFIRST_SH(1)
 #define ANYFLAVOR       FLAVOR_SH(1)
+#define ANYPREMUL       PREMUL_SH(1)
 
 
 // Suppress waning about info never being used
@@ -92,30 +93,50 @@
 
 // Does almost everything but is slow
 static
-cmsUInt8Number* UnrollChunkyBytes(register _cmsTRANSFORM* info,
-                                  register cmsUInt16Number wIn[],
-                                  register cmsUInt8Number* accum,
-                                  register cmsUInt32Number Stride)
+cmsUInt8Number* UnrollChunkyBytes(CMSREGISTER _cmsTRANSFORM* info,
+                                  CMSREGISTER cmsUInt16Number wIn[],
+                                  CMSREGISTER cmsUInt8Number* accum,
+                                  CMSREGISTER cmsUInt32Number Stride)
 {
     cmsUInt32Number nChan      = T_CHANNELS(info -> InputFormat);
     cmsUInt32Number DoSwap     = T_DOSWAP(info ->InputFormat);
     cmsUInt32Number Reverse    = T_FLAVOR(info ->InputFormat);
     cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> InputFormat);
     cmsUInt32Number Extra      = T_EXTRA(info -> InputFormat);
+    cmsUInt32Number Premul     = T_PREMUL(info->InputFormat);
+
     cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
-    cmsUInt16Number v;
-    cmsUInt32Number i;
+    cmsUInt32Number v;
+    cmsUInt32Number i;  
+    cmsUInt32Number alpha_factor = 1;
 
     if (ExtraFirst) {
+        
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(accum[0]));
+
         accum += Extra;
     }
+    else
+    {
+        if (Premul && Extra)        
+            alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(accum[nChan]));
+    }
 
     for (i=0; i < nChan; i++) {
+
         cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
         v = FROM_8_TO_16(*accum);
         v = Reverse ? REVERSE_FLAVOR_16(v) : v;
-        wIn[index] = v;
+
+        if (Premul && alpha_factor > 0)
+        {
+            v = ((cmsUInt32Number)((cmsUInt32Number)v << 16) / alpha_factor);
+            if (v > 0xffff) v = 0xffff;
+        }
+
+        wIn[index] = (cmsUInt16Number) v;
         accum++;
     }
 
@@ -137,42 +158,66 @@
 
 }
 
+
 // Extra channels are just ignored because come in the next planes
 static
-cmsUInt8Number* UnrollPlanarBytes(register _cmsTRANSFORM* info,
-                                  register cmsUInt16Number wIn[],
-                                  register cmsUInt8Number* accum,
-                                  register cmsUInt32Number Stride)
+cmsUInt8Number* UnrollPlanarBytes(CMSREGISTER _cmsTRANSFORM* info,
+                                  CMSREGISTER cmsUInt16Number wIn[],
+                                  CMSREGISTER cmsUInt8Number* accum,
+                                  CMSREGISTER cmsUInt32Number Stride)
 {
     cmsUInt32Number nChan     = T_CHANNELS(info -> InputFormat);
     cmsUInt32Number DoSwap    = T_DOSWAP(info ->InputFormat);
     cmsUInt32Number SwapFirst = T_SWAPFIRST(info ->InputFormat);
     cmsUInt32Number Reverse   = T_FLAVOR(info ->InputFormat);
     cmsUInt32Number i;
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number Extra = T_EXTRA(info->InputFormat);
+    cmsUInt32Number Premul = T_PREMUL(info->InputFormat);
     cmsUInt8Number* Init = accum;
+    cmsUInt32Number alpha_factor = 1;
 
-    if (DoSwap ^ SwapFirst) {
-        accum += T_EXTRA(info -> InputFormat) * Stride;
+    if (ExtraFirst) {
+
+        if (Premul && Extra)        
+            alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(accum[0]));
+
+
+        accum += Extra * Stride;
+    }
+    else
+    {
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(accum[(nChan) * Stride]));
     }
 
     for (i=0; i < nChan; i++) {
 
         cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
-        cmsUInt16Number v = FROM_8_TO_16(*accum);
+        cmsUInt32Number v = FROM_8_TO_16(*accum);
+        
+        v = Reverse ? REVERSE_FLAVOR_16(v) : v;
 
-        wIn[index] = Reverse ? REVERSE_FLAVOR_16(v) : v;
+        if (Premul && alpha_factor > 0)
+        {
+            v = ((cmsUInt32Number)((cmsUInt32Number)v << 16) / alpha_factor);
+            if (v > 0xffff) v = 0xffff;
+        }
+
+        wIn[index] = (cmsUInt16Number) v;
         accum += Stride;
     }
 
     return (Init + 1);
 }
 
+
 // Special cases, provided for performance
 static
-cmsUInt8Number* Unroll4Bytes(register _cmsTRANSFORM* info,
-                             register cmsUInt16Number wIn[],
-                             register cmsUInt8Number* accum,
-                             register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll4Bytes(CMSREGISTER _cmsTRANSFORM* info,
+                             CMSREGISTER cmsUInt16Number wIn[],
+                             CMSREGISTER cmsUInt8Number* accum,
+                             CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[0] = FROM_8_TO_16(*accum); accum++; // C
     wIn[1] = FROM_8_TO_16(*accum); accum++; // M
@@ -186,10 +231,10 @@
 }
 
 static
-cmsUInt8Number* Unroll4BytesReverse(register _cmsTRANSFORM* info,
-                                    register cmsUInt16Number wIn[],
-                                    register cmsUInt8Number* accum,
-                                    register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll4BytesReverse(CMSREGISTER _cmsTRANSFORM* info,
+                                    CMSREGISTER cmsUInt16Number wIn[],
+                                    CMSREGISTER cmsUInt8Number* accum,
+                                    CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[0] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // C
     wIn[1] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // M
@@ -203,10 +248,10 @@
 }
 
 static
-cmsUInt8Number* Unroll4BytesSwapFirst(register _cmsTRANSFORM* info,
-                                      register cmsUInt16Number wIn[],
-                                      register cmsUInt8Number* accum,
-                                      register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll4BytesSwapFirst(CMSREGISTER _cmsTRANSFORM* info,
+                                      CMSREGISTER cmsUInt16Number wIn[],
+                                      CMSREGISTER cmsUInt8Number* accum,
+                                      CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[3] = FROM_8_TO_16(*accum); accum++; // K
     wIn[0] = FROM_8_TO_16(*accum); accum++; // C
@@ -221,10 +266,10 @@
 
 // KYMC
 static
-cmsUInt8Number* Unroll4BytesSwap(register _cmsTRANSFORM* info,
-                                 register cmsUInt16Number wIn[],
-                                 register cmsUInt8Number* accum,
-                                 register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll4BytesSwap(CMSREGISTER _cmsTRANSFORM* info,
+                                 CMSREGISTER cmsUInt16Number wIn[],
+                                 CMSREGISTER cmsUInt8Number* accum,
+                                 CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[3] = FROM_8_TO_16(*accum); accum++;  // K
     wIn[2] = FROM_8_TO_16(*accum); accum++;  // Y
@@ -238,10 +283,10 @@
 }
 
 static
-cmsUInt8Number* Unroll4BytesSwapSwapFirst(register _cmsTRANSFORM* info,
-                                          register cmsUInt16Number wIn[],
-                                          register cmsUInt8Number* accum,
-                                          register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll4BytesSwapSwapFirst(CMSREGISTER _cmsTRANSFORM* info,
+                                          CMSREGISTER cmsUInt16Number wIn[],
+                                          CMSREGISTER cmsUInt8Number* accum,
+                                          CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[2] = FROM_8_TO_16(*accum); accum++;  // K
     wIn[1] = FROM_8_TO_16(*accum); accum++;  // Y
@@ -255,10 +300,10 @@
 }
 
 static
-cmsUInt8Number* Unroll3Bytes(register _cmsTRANSFORM* info,
-                             register cmsUInt16Number wIn[],
-                             register cmsUInt8Number* accum,
-                             register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll3Bytes(CMSREGISTER _cmsTRANSFORM* info,
+                             CMSREGISTER cmsUInt16Number wIn[],
+                             CMSREGISTER cmsUInt8Number* accum,
+                             CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[0] = FROM_8_TO_16(*accum); accum++;     // R
     wIn[1] = FROM_8_TO_16(*accum); accum++;     // G
@@ -271,10 +316,10 @@
 }
 
 static
-cmsUInt8Number* Unroll3BytesSkip1Swap(register _cmsTRANSFORM* info,
-                                      register cmsUInt16Number wIn[],
-                                      register cmsUInt8Number* accum,
-                                      register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll3BytesSkip1Swap(CMSREGISTER _cmsTRANSFORM* info,
+                                      CMSREGISTER cmsUInt16Number wIn[],
+                                      CMSREGISTER cmsUInt8Number* accum,
+                                      CMSREGISTER cmsUInt32Number Stride)
 {
     accum++; // A
     wIn[2] = FROM_8_TO_16(*accum); accum++; // B
@@ -288,10 +333,10 @@
 }
 
 static
-cmsUInt8Number* Unroll3BytesSkip1SwapSwapFirst(register _cmsTRANSFORM* info, 
-                                              register cmsUInt16Number wIn[], 
-                                              register cmsUInt8Number* accum,
-                                              register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll3BytesSkip1SwapSwapFirst(CMSREGISTER _cmsTRANSFORM* info, 
+                                              CMSREGISTER cmsUInt16Number wIn[], 
+                                              CMSREGISTER cmsUInt8Number* accum,
+                                              CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[2] = FROM_8_TO_16(*accum); accum++; // B
     wIn[1] = FROM_8_TO_16(*accum); accum++; // G
@@ -305,10 +350,10 @@
 }
 
 static
-cmsUInt8Number* Unroll3BytesSkip1SwapFirst(register _cmsTRANSFORM* info, 
-                                           register cmsUInt16Number wIn[], 
-                                           register cmsUInt8Number* accum,
-                                           register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll3BytesSkip1SwapFirst(CMSREGISTER _cmsTRANSFORM* info, 
+                                           CMSREGISTER cmsUInt16Number wIn[], 
+                                           CMSREGISTER cmsUInt8Number* accum,
+                                           CMSREGISTER cmsUInt32Number Stride)
 {
     accum++; // A
     wIn[0] = FROM_8_TO_16(*accum); accum++; // R
@@ -324,10 +369,10 @@
 
 // BRG
 static
-cmsUInt8Number* Unroll3BytesSwap(register _cmsTRANSFORM* info,
-                                 register cmsUInt16Number wIn[],
-                                 register cmsUInt8Number* accum,
-                                 register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll3BytesSwap(CMSREGISTER _cmsTRANSFORM* info,
+                                 CMSREGISTER cmsUInt16Number wIn[],
+                                 CMSREGISTER cmsUInt8Number* accum,
+                                 CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[2] = FROM_8_TO_16(*accum); accum++;     // B
     wIn[1] = FROM_8_TO_16(*accum); accum++;     // G
@@ -340,10 +385,10 @@
 }
 
 static
-cmsUInt8Number* UnrollLabV2_8(register _cmsTRANSFORM* info,
-                              register cmsUInt16Number wIn[],
-                              register cmsUInt8Number* accum,
-                              register cmsUInt32Number Stride)
+cmsUInt8Number* UnrollLabV2_8(CMSREGISTER _cmsTRANSFORM* info,
+                              CMSREGISTER cmsUInt16Number wIn[],
+                              CMSREGISTER cmsUInt8Number* accum,
+                              CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[0] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++;     // L
     wIn[1] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++;     // a
@@ -356,10 +401,10 @@
 }
 
 static
-cmsUInt8Number* UnrollALabV2_8(register _cmsTRANSFORM* info,
-                               register cmsUInt16Number wIn[],
-                               register cmsUInt8Number* accum,
-                               register cmsUInt32Number Stride)
+cmsUInt8Number* UnrollALabV2_8(CMSREGISTER _cmsTRANSFORM* info,
+                               CMSREGISTER cmsUInt16Number wIn[],
+                               CMSREGISTER cmsUInt8Number* accum,
+                               CMSREGISTER cmsUInt32Number Stride)
 {
     accum++;  // A
     wIn[0] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++;     // L
@@ -373,10 +418,10 @@
 }
 
 static
-cmsUInt8Number* UnrollLabV2_16(register _cmsTRANSFORM* info,
-                               register cmsUInt16Number wIn[],
-                               register cmsUInt8Number* accum,
-                               register cmsUInt32Number Stride)
+cmsUInt8Number* UnrollLabV2_16(CMSREGISTER _cmsTRANSFORM* info,
+                               CMSREGISTER cmsUInt16Number wIn[],
+                               CMSREGISTER cmsUInt8Number* accum,
+                               CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[0] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2;     // L
     wIn[1] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2;     // a
@@ -390,10 +435,10 @@
 
 // for duplex
 static
-cmsUInt8Number* Unroll2Bytes(register _cmsTRANSFORM* info,
-                                     register cmsUInt16Number wIn[],
-                                     register cmsUInt8Number* accum,
-                                     register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll2Bytes(CMSREGISTER _cmsTRANSFORM* info,
+                                     CMSREGISTER cmsUInt16Number wIn[],
+                                     CMSREGISTER cmsUInt8Number* accum,
+                                     CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[0] = FROM_8_TO_16(*accum); accum++;     // ch1
     wIn[1] = FROM_8_TO_16(*accum); accum++;     // ch2
@@ -409,10 +454,10 @@
 
 // Monochrome duplicates L into RGB for null-transforms
 static
-cmsUInt8Number* Unroll1Byte(register _cmsTRANSFORM* info,
-                            register cmsUInt16Number wIn[],
-                            register cmsUInt8Number* accum,
-                            register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll1Byte(CMSREGISTER _cmsTRANSFORM* info,
+                            CMSREGISTER cmsUInt16Number wIn[],
+                            CMSREGISTER cmsUInt8Number* accum,
+                            CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[0] = wIn[1] = wIn[2] = FROM_8_TO_16(*accum); accum++;     // L
 
@@ -424,10 +469,10 @@
 
 
 static
-cmsUInt8Number* Unroll1ByteSkip1(register _cmsTRANSFORM* info,
-                                 register cmsUInt16Number wIn[],
-                                 register cmsUInt8Number* accum,
-                                 register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll1ByteSkip1(CMSREGISTER _cmsTRANSFORM* info,
+                                 CMSREGISTER cmsUInt16Number wIn[],
+                                 CMSREGISTER cmsUInt8Number* accum,
+                                 CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[0] = wIn[1] = wIn[2] = FROM_8_TO_16(*accum); accum++;     // L
     accum += 1;
@@ -439,10 +484,10 @@
 }
 
 static
-cmsUInt8Number* Unroll1ByteSkip2(register _cmsTRANSFORM* info,
-                                 register cmsUInt16Number wIn[],
-                                 register cmsUInt8Number* accum,
-                                 register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll1ByteSkip2(CMSREGISTER _cmsTRANSFORM* info,
+                                 CMSREGISTER cmsUInt16Number wIn[],
+                                 CMSREGISTER cmsUInt8Number* accum,
+                                 CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[0] = wIn[1] = wIn[2] = FROM_8_TO_16(*accum); accum++;     // L
     accum += 2;
@@ -454,10 +499,10 @@
 }
 
 static
-cmsUInt8Number* Unroll1ByteReversed(register _cmsTRANSFORM* info,
-                                    register cmsUInt16Number wIn[],
-                                    register cmsUInt8Number* accum,
-                                    register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll1ByteReversed(CMSREGISTER _cmsTRANSFORM* info,
+                                    CMSREGISTER cmsUInt16Number wIn[],
+                                    CMSREGISTER cmsUInt8Number* accum,
+                                    CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[0] = wIn[1] = wIn[2] = REVERSE_FLAVOR_16(FROM_8_TO_16(*accum)); accum++;     // L
 
@@ -469,10 +514,10 @@
 
 
 static
-cmsUInt8Number* UnrollAnyWords(register _cmsTRANSFORM* info,
-                               register cmsUInt16Number wIn[],
-                               register cmsUInt8Number* accum,
-                               register cmsUInt32Number Stride)
+cmsUInt8Number* UnrollAnyWords(CMSREGISTER _cmsTRANSFORM* info,
+                               CMSREGISTER cmsUInt16Number wIn[],
+                               CMSREGISTER cmsUInt8Number* accum,
+                               CMSREGISTER cmsUInt32Number Stride)
 {
    cmsUInt32Number nChan       = T_CHANNELS(info -> InputFormat);
    cmsUInt32Number SwapEndian  = T_ENDIAN16(info -> InputFormat);
@@ -517,11 +562,63 @@
     cmsUNUSED_PARAMETER(Stride);
 }
 
+
 static
-cmsUInt8Number* UnrollPlanarWords(register _cmsTRANSFORM* info,
-                                  register cmsUInt16Number wIn[],
-                                  register cmsUInt8Number* accum,
-                                  register cmsUInt32Number Stride)
+cmsUInt8Number* UnrollAnyWordsPremul(CMSREGISTER _cmsTRANSFORM* info,
+                                     CMSREGISTER cmsUInt16Number wIn[],
+                                     CMSREGISTER cmsUInt8Number* accum,
+                                     CMSREGISTER cmsUInt32Number Stride)
+{
+   cmsUInt32Number nChan       = T_CHANNELS(info -> InputFormat);
+   cmsUInt32Number SwapEndian  = T_ENDIAN16(info -> InputFormat);
+   cmsUInt32Number DoSwap      = T_DOSWAP(info ->InputFormat);
+   cmsUInt32Number Reverse     = T_FLAVOR(info ->InputFormat);
+   cmsUInt32Number SwapFirst   = T_SWAPFIRST(info -> InputFormat);   
+   cmsUInt32Number ExtraFirst  = DoSwap ^ SwapFirst;
+   cmsUInt32Number i;
+
+   cmsUInt16Number alpha = (ExtraFirst ? accum[0] : accum[nChan - 1]);
+   cmsUInt32Number alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(alpha));
+
+    if (ExtraFirst) {
+        accum += sizeof(cmsUInt16Number);
+    }
+
+    for (i=0; i < nChan; i++) {
+
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number v = *(cmsUInt16Number*) accum;
+
+        if (SwapEndian)
+            v = CHANGE_ENDIAN(v);
+
+        if (alpha_factor > 0) {
+
+            v = (v << 16) / alpha_factor;
+            if (v > 0xffff) v = 0xffff;
+        }
+
+        wIn[index] = (cmsUInt16Number) (Reverse ? REVERSE_FLAVOR_16(v) : v);
+
+        accum += sizeof(cmsUInt16Number);
+    }
+
+    if (!ExtraFirst) {
+        accum += sizeof(cmsUInt16Number);
+    }
+
+    return accum;
+
+    cmsUNUSED_PARAMETER(Stride);
+}
+
+
+
+static
+cmsUInt8Number* UnrollPlanarWords(CMSREGISTER _cmsTRANSFORM* info,
+                                  CMSREGISTER cmsUInt16Number wIn[],
+                                  CMSREGISTER cmsUInt8Number* accum,
+                                  CMSREGISTER cmsUInt32Number Stride)
 {
     cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat);
     cmsUInt32Number DoSwap= T_DOSWAP(info ->InputFormat);
@@ -531,7 +628,7 @@
     cmsUInt8Number* Init = accum;
 
     if (DoSwap) {
-        accum += T_EXTRA(info -> InputFormat) * Stride * sizeof(cmsUInt16Number);
+        accum += T_EXTRA(info -> InputFormat) * Stride;
     }
 
     for (i=0; i < nChan; i++) {
@@ -544,18 +641,61 @@
 
         wIn[index] = Reverse ? REVERSE_FLAVOR_16(v) : v;
 
-        accum +=  Stride * sizeof(cmsUInt16Number);
+        accum +=  Stride;
     }
 
     return (Init + sizeof(cmsUInt16Number));
 }
 
+static
+cmsUInt8Number* UnrollPlanarWordsPremul(CMSREGISTER _cmsTRANSFORM* info,
+                                        CMSREGISTER cmsUInt16Number wIn[],
+                                        CMSREGISTER cmsUInt8Number* accum,
+                                        CMSREGISTER cmsUInt32Number Stride)
+{
+    cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat);
+    cmsUInt32Number DoSwap= T_DOSWAP(info ->InputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->InputFormat);
+    cmsUInt32Number Reverse= T_FLAVOR(info ->InputFormat);
+    cmsUInt32Number SwapEndian = T_ENDIAN16(info -> InputFormat);
+    cmsUInt32Number i;
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt8Number* Init = accum;
+
+    cmsUInt16Number  alpha = (ExtraFirst ? accum[0] : accum[(nChan - 1) * Stride]);
+    cmsUInt32Number alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(alpha));
+
+    if (ExtraFirst) {
+        accum += Stride;
+    }
+
+    for (i=0; i < nChan; i++) {
+
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number v = (cmsUInt32Number) *(cmsUInt16Number*) accum;
+
+        if (SwapEndian)
+            v = CHANGE_ENDIAN(v);
+
+        if (alpha_factor > 0) {
+
+            v = (v << 16) / alpha_factor;
+            if (v > 0xffff) v = 0xffff;
+        }
+
+        wIn[index] = (cmsUInt16Number) (Reverse ? REVERSE_FLAVOR_16(v) : v);
+
+        accum +=  Stride;
+    }
+
+    return (Init + sizeof(cmsUInt16Number));
+}
 
 static
-cmsUInt8Number* Unroll4Words(register _cmsTRANSFORM* info,
-                             register cmsUInt16Number wIn[],
-                             register cmsUInt8Number* accum,
-                             register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll4Words(CMSREGISTER _cmsTRANSFORM* info,
+                             CMSREGISTER cmsUInt16Number wIn[],
+                             CMSREGISTER cmsUInt8Number* accum,
+                             CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C
     wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M
@@ -569,10 +709,10 @@
 }
 
 static
-cmsUInt8Number* Unroll4WordsReverse(register _cmsTRANSFORM* info,
-                                    register cmsUInt16Number wIn[],
-                                    register cmsUInt8Number* accum,
-                                    register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll4WordsReverse(CMSREGISTER _cmsTRANSFORM* info,
+                                    CMSREGISTER cmsUInt16Number wIn[],
+                                    CMSREGISTER cmsUInt8Number* accum,
+                                    CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[0] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // C
     wIn[1] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // M
@@ -586,10 +726,10 @@
 }
 
 static
-cmsUInt8Number* Unroll4WordsSwapFirst(register _cmsTRANSFORM* info,
-                                      register cmsUInt16Number wIn[],
-                                      register cmsUInt8Number* accum,
-                                      register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll4WordsSwapFirst(CMSREGISTER _cmsTRANSFORM* info,
+                                      CMSREGISTER cmsUInt16Number wIn[],
+                                      CMSREGISTER cmsUInt8Number* accum,
+                                      CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // K
     wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C
@@ -604,10 +744,10 @@
 
 // KYMC
 static
-cmsUInt8Number* Unroll4WordsSwap(register _cmsTRANSFORM* info,
-                                 register cmsUInt16Number wIn[],
-                                 register cmsUInt8Number* accum,
-                                 register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll4WordsSwap(CMSREGISTER _cmsTRANSFORM* info,
+                                 CMSREGISTER cmsUInt16Number wIn[],
+                                 CMSREGISTER cmsUInt8Number* accum,
+                                 CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // K
     wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // Y
@@ -621,10 +761,10 @@
 }
 
 static
-cmsUInt8Number* Unroll4WordsSwapSwapFirst(register _cmsTRANSFORM* info,
-                                          register cmsUInt16Number wIn[],
-                                          register cmsUInt8Number* accum,
-                                          register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll4WordsSwapSwapFirst(CMSREGISTER _cmsTRANSFORM* info,
+                                          CMSREGISTER cmsUInt16Number wIn[],
+                                          CMSREGISTER cmsUInt8Number* accum,
+                                          CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // K
     wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // Y
@@ -638,10 +778,10 @@
 }
 
 static
-cmsUInt8Number* Unroll3Words(register _cmsTRANSFORM* info,
-                             register cmsUInt16Number wIn[],
-                             register cmsUInt8Number* accum,
-                             register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll3Words(CMSREGISTER _cmsTRANSFORM* info,
+                             CMSREGISTER cmsUInt16Number wIn[],
+                             CMSREGISTER cmsUInt8Number* accum,
+                             CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[0] = *(cmsUInt16Number*) accum; accum+= 2;  // C R
     wIn[1] = *(cmsUInt16Number*) accum; accum+= 2;  // M G
@@ -654,10 +794,10 @@
 }
 
 static
-cmsUInt8Number* Unroll3WordsSwap(register _cmsTRANSFORM* info,
-                                 register cmsUInt16Number wIn[],
-                                 register cmsUInt8Number* accum,
-                                 register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll3WordsSwap(CMSREGISTER _cmsTRANSFORM* info,
+                                 CMSREGISTER cmsUInt16Number wIn[],
+                                 CMSREGISTER cmsUInt8Number* accum,
+                                 CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[2] = *(cmsUInt16Number*) accum; accum+= 2;  // C R
     wIn[1] = *(cmsUInt16Number*) accum; accum+= 2;  // M G
@@ -670,10 +810,10 @@
 }
 
 static
-cmsUInt8Number* Unroll3WordsSkip1Swap(register _cmsTRANSFORM* info,
-                                      register cmsUInt16Number wIn[],
-                                      register cmsUInt8Number* accum,
-                                      register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll3WordsSkip1Swap(CMSREGISTER _cmsTRANSFORM* info,
+                                      CMSREGISTER cmsUInt16Number wIn[],
+                                      CMSREGISTER cmsUInt8Number* accum,
+                                      CMSREGISTER cmsUInt32Number Stride)
 {
     accum += 2; // A
     wIn[2] = *(cmsUInt16Number*) accum; accum += 2; // R
@@ -687,10 +827,10 @@
 }
 
 static
-cmsUInt8Number* Unroll3WordsSkip1SwapFirst(register _cmsTRANSFORM* info,
-                                           register cmsUInt16Number wIn[],
-                                           register cmsUInt8Number* accum,
-                                           register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll3WordsSkip1SwapFirst(CMSREGISTER _cmsTRANSFORM* info,
+                                           CMSREGISTER cmsUInt16Number wIn[],
+                                           CMSREGISTER cmsUInt8Number* accum,
+                                           CMSREGISTER cmsUInt32Number Stride)
 {
     accum += 2; // A
     wIn[0] = *(cmsUInt16Number*) accum; accum += 2; // R
@@ -704,10 +844,10 @@
 }
 
 static
-cmsUInt8Number* Unroll1Word(register _cmsTRANSFORM* info,
-                            register cmsUInt16Number wIn[],
-                            register cmsUInt8Number* accum,
-                            register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll1Word(CMSREGISTER _cmsTRANSFORM* info,
+                            CMSREGISTER cmsUInt16Number wIn[],
+                            CMSREGISTER cmsUInt8Number* accum,
+                            CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[0] = wIn[1] = wIn[2] = *(cmsUInt16Number*) accum; accum+= 2;   // L
 
@@ -718,10 +858,10 @@
 }
 
 static
-cmsUInt8Number* Unroll1WordReversed(register _cmsTRANSFORM* info,
-                                    register cmsUInt16Number wIn[],
-                                    register cmsUInt8Number* accum,
-                                    register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll1WordReversed(CMSREGISTER _cmsTRANSFORM* info,
+                                    CMSREGISTER cmsUInt16Number wIn[],
+                                    CMSREGISTER cmsUInt8Number* accum,
+                                    CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[0] = wIn[1] = wIn[2] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2;
 
@@ -732,10 +872,10 @@
 }
 
 static
-cmsUInt8Number* Unroll1WordSkip3(register _cmsTRANSFORM* info,
-                                 register cmsUInt16Number wIn[],
-                                 register cmsUInt8Number* accum,
-                                 register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll1WordSkip3(CMSREGISTER _cmsTRANSFORM* info,
+                                 CMSREGISTER cmsUInt16Number wIn[],
+                                 CMSREGISTER cmsUInt8Number* accum,
+                                 CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[0] = wIn[1] = wIn[2] = *(cmsUInt16Number*) accum;
 
@@ -748,10 +888,10 @@
 }
 
 static
-cmsUInt8Number* Unroll2Words(register _cmsTRANSFORM* info,
-                                     register cmsUInt16Number wIn[],
-                                     register cmsUInt8Number* accum,
-                                     register cmsUInt32Number Stride)
+cmsUInt8Number* Unroll2Words(CMSREGISTER _cmsTRANSFORM* info,
+                                     CMSREGISTER cmsUInt16Number wIn[],
+                                     CMSREGISTER cmsUInt8Number* accum,
+                                     CMSREGISTER cmsUInt32Number Stride)
 {
     wIn[0] = *(cmsUInt16Number*) accum; accum += 2;    // ch1
     wIn[1] = *(cmsUInt16Number*) accum; accum += 2;    // ch2
@@ -765,20 +905,25 @@
 
 // This is a conversion of Lab double to 16 bits
 static
-cmsUInt8Number* UnrollLabDoubleTo16(register _cmsTRANSFORM* info,
-                                    register cmsUInt16Number wIn[],
-                                    register cmsUInt8Number* accum,
-                                    register cmsUInt32Number  Stride)
+cmsUInt8Number* UnrollLabDoubleTo16(CMSREGISTER _cmsTRANSFORM* info,
+                                    CMSREGISTER cmsUInt16Number wIn[],
+                                    CMSREGISTER cmsUInt8Number* accum,
+                                    CMSREGISTER cmsUInt32Number  Stride)
 {
     if (T_PLANAR(info -> InputFormat)) {
 
-        cmsFloat64Number* Pt = (cmsFloat64Number*) accum;
-
         cmsCIELab Lab;
+        cmsUInt8Number* pos_L;
+        cmsUInt8Number* pos_a;
+        cmsUInt8Number* pos_b;
+        
+        pos_L = accum;
+        pos_a = accum + Stride;
+        pos_b = accum + Stride * 2;
 
-        Lab.L = Pt[0];
-        Lab.a = Pt[Stride];
-        Lab.b = Pt[Stride*2];
+        Lab.L = *(cmsFloat64Number*) pos_L;
+        Lab.a = *(cmsFloat64Number*) pos_a;
+        Lab.b = *(cmsFloat64Number*) pos_b;
 
         cmsFloat2LabEncoded(wIn, &Lab);
         return accum + sizeof(cmsFloat64Number);
@@ -794,21 +939,26 @@
 
 // This is a conversion of Lab float to 16 bits
 static
-cmsUInt8Number* UnrollLabFloatTo16(register _cmsTRANSFORM* info,
-                                    register cmsUInt16Number wIn[],
-                                    register cmsUInt8Number* accum,
-                                    register cmsUInt32Number  Stride)
+cmsUInt8Number* UnrollLabFloatTo16(CMSREGISTER _cmsTRANSFORM* info,
+                                    CMSREGISTER cmsUInt16Number wIn[],
+                                    CMSREGISTER cmsUInt8Number* accum,
+                                    CMSREGISTER cmsUInt32Number  Stride)
 {
     cmsCIELab Lab;
     
     if (T_PLANAR(info -> InputFormat)) {
 
-        cmsFloat32Number* Pt = (cmsFloat32Number*) accum;
+        cmsUInt8Number* pos_L;
+        cmsUInt8Number* pos_a;
+        cmsUInt8Number* pos_b;
 
-     
-        Lab.L = Pt[0];
-        Lab.a = Pt[Stride];
-        Lab.b = Pt[Stride*2];
+        pos_L = accum;
+        pos_a = accum + Stride;
+        pos_b = accum + Stride * 2;
+
+        Lab.L = *(cmsFloat32Number*)pos_L;
+        Lab.a = *(cmsFloat32Number*)pos_a;
+        Lab.b = *(cmsFloat32Number*)pos_b;
 
         cmsFloat2LabEncoded(wIn, &Lab);
         return accum + sizeof(cmsFloat32Number);
@@ -827,19 +977,26 @@
 
 // This is a conversion of XYZ double to 16 bits
 static
-cmsUInt8Number* UnrollXYZDoubleTo16(register _cmsTRANSFORM* info,
-                                    register cmsUInt16Number wIn[],
-                                    register cmsUInt8Number* accum,
-                                    register cmsUInt32Number Stride)
+cmsUInt8Number* UnrollXYZDoubleTo16(CMSREGISTER _cmsTRANSFORM* info,
+                                    CMSREGISTER cmsUInt16Number wIn[],
+                                    CMSREGISTER cmsUInt8Number* accum,
+                                    CMSREGISTER cmsUInt32Number Stride)
 {
     if (T_PLANAR(info -> InputFormat)) {
 
-        cmsFloat64Number* Pt = (cmsFloat64Number*) accum;
         cmsCIEXYZ XYZ;
+        cmsUInt8Number* pos_X;
+        cmsUInt8Number* pos_Y;
+        cmsUInt8Number* pos_Z;
 
-        XYZ.X = Pt[0];
-        XYZ.Y = Pt[Stride];
-        XYZ.Z = Pt[Stride*2];
+        pos_X = accum;
+        pos_Y = accum + Stride;
+        pos_Z = accum + Stride * 2;
+
+        XYZ.X = *(cmsFloat64Number*)pos_X;
+        XYZ.Y = *(cmsFloat64Number*)pos_Y;
+        XYZ.Z = *(cmsFloat64Number*)pos_Z;
+
         cmsFloat2XYZEncoded(wIn, &XYZ);
 
         return accum + sizeof(cmsFloat64Number);
@@ -856,19 +1013,26 @@
 
 // This is a conversion of XYZ float to 16 bits
 static
-cmsUInt8Number* UnrollXYZFloatTo16(register _cmsTRANSFORM* info,
-                                   register cmsUInt16Number wIn[],
-                                   register cmsUInt8Number* accum,
-                                   register cmsUInt32Number Stride)
+cmsUInt8Number* UnrollXYZFloatTo16(CMSREGISTER _cmsTRANSFORM* info,
+                                   CMSREGISTER cmsUInt16Number wIn[],
+                                   CMSREGISTER cmsUInt8Number* accum,
+                                   CMSREGISTER cmsUInt32Number Stride)
 {
     if (T_PLANAR(info -> InputFormat)) {
 
-        cmsFloat32Number* Pt = (cmsFloat32Number*) accum;
         cmsCIEXYZ XYZ;
+        cmsUInt8Number* pos_X;
+        cmsUInt8Number* pos_Y;
+        cmsUInt8Number* pos_Z;
 
-        XYZ.X = Pt[0];
-        XYZ.Y = Pt[Stride];
-        XYZ.Z = Pt[Stride*2];
+        pos_X = accum;
+        pos_Y = accum + Stride;
+        pos_Z = accum + Stride * 2;
+
+        XYZ.X = *(cmsFloat32Number*)pos_X;
+        XYZ.Y = *(cmsFloat32Number*)pos_Y;
+        XYZ.Z = *(cmsFloat32Number*)pos_Z;
+
         cmsFloat2XYZEncoded(wIn, &XYZ);
 
         return accum + sizeof(cmsFloat32Number);
@@ -913,12 +1077,26 @@
     }
 }
 
+// Return the size in bytes of a given formatter
+static
+cmsUInt32Number PixelSize(cmsUInt32Number Format)
+{
+    cmsUInt32Number fmt_bytes = T_BYTES(Format);
+
+    // For double, the T_BYTES field is zero
+    if (fmt_bytes == 0)
+        return sizeof(cmsUInt64Number);
+
+    // Otherwise, it is already correct for all formats
+    return fmt_bytes;
+}
+
 // Inks does come in percentage, remaining cases are between 0..1.0, again to 16 bits
 static
-cmsUInt8Number* UnrollDoubleTo16(register _cmsTRANSFORM* info,
-                                register cmsUInt16Number wIn[],
-                                register cmsUInt8Number* accum,
-                                register cmsUInt32Number Stride)
+cmsUInt8Number* UnrollDoubleTo16(CMSREGISTER _cmsTRANSFORM* info,
+                                CMSREGISTER cmsUInt16Number wIn[],
+                                CMSREGISTER cmsUInt8Number* accum,
+                                CMSREGISTER cmsUInt32Number Stride)
 {
 
     cmsUInt32Number nChan      = T_CHANNELS(info -> InputFormat);
@@ -934,6 +1112,8 @@
     cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 655.35 : 65535.0;
 
 
+    Stride /= PixelSize(info->InputFormat);
+
     if (ExtraFirst)
             start = Extra;
 
@@ -971,10 +1151,10 @@
 
 
 static
-cmsUInt8Number* UnrollFloatTo16(register _cmsTRANSFORM* info,
-                                register cmsUInt16Number wIn[],
-                                register cmsUInt8Number* accum,
-                                register cmsUInt32Number Stride)
+cmsUInt8Number* UnrollFloatTo16(CMSREGISTER _cmsTRANSFORM* info,
+                                CMSREGISTER cmsUInt16Number wIn[],
+                                CMSREGISTER cmsUInt8Number* accum,
+                                CMSREGISTER cmsUInt32Number Stride)
 {
 
     cmsUInt32Number nChan  = T_CHANNELS(info -> InputFormat);
@@ -989,6 +1169,7 @@
     cmsUInt32Number i, start = 0;
     cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 655.35 : 65535.0;
 
+    Stride /= PixelSize(info->InputFormat);
 
     if (ExtraFirst)
             start = Extra;
@@ -1029,10 +1210,10 @@
 
 // For 1 channel, we need to duplicate data (it comes in 0..1.0 range)
 static
-cmsUInt8Number* UnrollDouble1Chan(register _cmsTRANSFORM* info,
-                                  register cmsUInt16Number wIn[],
-                                  register cmsUInt8Number* accum,
-                                  register cmsUInt32Number Stride)
+cmsUInt8Number* UnrollDouble1Chan(CMSREGISTER _cmsTRANSFORM* info,
+                                  CMSREGISTER cmsUInt16Number wIn[],
+                                  CMSREGISTER cmsUInt8Number* accum,
+                                  CMSREGISTER cmsUInt32Number Stride)
 {
     cmsFloat64Number* Inks = (cmsFloat64Number*) accum;
 
@@ -1046,6 +1227,110 @@
 
 //-------------------------------------------------------------------------------------------------------------------
 
+// For anything going from cmsUInt8Number
+static
+cmsUInt8Number* Unroll8ToFloat(_cmsTRANSFORM* info,
+                               cmsFloat32Number wIn[],
+                               cmsUInt8Number* accum,
+                               cmsUInt32Number Stride)
+{
+
+    cmsUInt32Number nChan = T_CHANNELS(info->InputFormat);
+    cmsUInt32Number DoSwap = T_DOSWAP(info->InputFormat);
+    cmsUInt32Number Reverse = T_FLAVOR(info->InputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->InputFormat);
+    cmsUInt32Number Extra = T_EXTRA(info->InputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number Planar = T_PLANAR(info->InputFormat);
+    cmsFloat32Number v;
+    cmsUInt32Number i, start = 0;
+    
+    Stride /= PixelSize(info->InputFormat);
+
+    if (ExtraFirst)
+        start = Extra;
+
+    for (i = 0; i < nChan; i++) {
+
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
+
+        if (Planar)
+            v = (cmsFloat32Number) ((cmsUInt8Number *)accum)[(i + start) * Stride];
+        else
+            v = (cmsFloat32Number) ((cmsUInt8Number *)accum)[i + start];
+
+        v /= 255.0F;
+
+        wIn[index] = Reverse ? 1 - v : v;
+    }
+
+
+    if (Extra == 0 && SwapFirst) {
+        cmsFloat32Number tmp = wIn[0];
+
+        memmove(&wIn[0], &wIn[1], (nChan - 1) * sizeof(cmsFloat32Number));
+        wIn[nChan - 1] = tmp;
+    }
+
+    if (T_PLANAR(info->InputFormat))
+        return accum + sizeof(cmsUInt8Number);
+    else
+        return accum + (nChan + Extra) * sizeof(cmsUInt8Number);
+}
+
+
+// For anything going from cmsUInt16Number
+static
+cmsUInt8Number* Unroll16ToFloat(_cmsTRANSFORM* info,
+                                cmsFloat32Number wIn[],
+                                cmsUInt8Number* accum,
+                                cmsUInt32Number Stride)
+{
+
+    cmsUInt32Number nChan = T_CHANNELS(info->InputFormat);
+    cmsUInt32Number DoSwap = T_DOSWAP(info->InputFormat);
+    cmsUInt32Number Reverse = T_FLAVOR(info->InputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->InputFormat);
+    cmsUInt32Number Extra = T_EXTRA(info->InputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number Planar = T_PLANAR(info->InputFormat);
+    cmsFloat32Number v;
+    cmsUInt32Number i, start = 0;
+
+    Stride /= PixelSize(info->InputFormat);
+
+    if (ExtraFirst)
+        start = Extra;
+
+    for (i = 0; i < nChan; i++) {
+
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
+
+        if (Planar)
+            v = (cmsFloat32Number)((cmsUInt16Number*)accum)[(i + start) * Stride];
+        else
+            v = (cmsFloat32Number)((cmsUInt16Number*)accum)[i + start];
+
+        v /= 65535.0F;
+
+        wIn[index] = Reverse ? 1 - v : v;
+    }
+
+
+    if (Extra == 0 && SwapFirst) {
+        cmsFloat32Number tmp = wIn[0];
+
+        memmove(&wIn[0], &wIn[1], (nChan - 1) * sizeof(cmsFloat32Number));
+        wIn[nChan - 1] = tmp;
+    }
+
+    if (T_PLANAR(info->InputFormat))
+        return accum + sizeof(cmsUInt16Number);
+    else
+        return accum + (nChan + Extra) * sizeof(cmsUInt16Number);
+}
+
+
 // For anything going from cmsFloat32Number
 static
 cmsUInt8Number* UnrollFloatsToFloat(_cmsTRANSFORM* info,
@@ -1054,17 +1339,29 @@
                                     cmsUInt32Number Stride)
 {
 
-    cmsUInt32Number nChan  = T_CHANNELS(info -> InputFormat);
-    cmsUInt32Number DoSwap   = T_DOSWAP(info ->InputFormat);
-    cmsUInt32Number Reverse    = T_FLAVOR(info ->InputFormat);
-    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> InputFormat);
-    cmsUInt32Number Extra   = T_EXTRA(info -> InputFormat);
+    cmsUInt32Number nChan = T_CHANNELS(info->InputFormat);
+    cmsUInt32Number DoSwap = T_DOSWAP(info->InputFormat);
+    cmsUInt32Number Reverse = T_FLAVOR(info->InputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->InputFormat);
+    cmsUInt32Number Extra = T_EXTRA(info->InputFormat);
     cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
-    cmsUInt32Number Planar     = T_PLANAR(info -> InputFormat);
+    cmsUInt32Number Planar = T_PLANAR(info->InputFormat);
+    cmsUInt32Number Premul = T_PREMUL(info->InputFormat);
     cmsFloat32Number v;
     cmsUInt32Number i, start = 0;
-    cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 100.0F : 1.0F;
+    cmsFloat32Number maximum = IsInkSpace(info->InputFormat) ? 100.0F : 1.0F;
+    cmsFloat32Number alpha_factor = 1.0f;
+    cmsFloat32Number* ptr = (cmsFloat32Number*)accum;
 
+    Stride /= PixelSize(info->InputFormat);
+
+    if (Premul && Extra)
+    {        
+        if (Planar)
+            alpha_factor = (ExtraFirst ? ptr[0] : ptr[nChan * Stride]) / maximum;            
+        else
+            alpha_factor = (ExtraFirst ? ptr[0] : ptr[nChan]) / maximum;        
+    }
 
     if (ExtraFirst)
             start = Extra;
@@ -1074,9 +1371,12 @@
         cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
         if (Planar)
-            v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[(i + start) * Stride];
+            v = ptr[(i + start) * Stride];
         else
-            v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[i + start];
+            v = ptr[i + start];
+
+        if (Premul && alpha_factor > 0)
+            v /= alpha_factor;
 
         v /= maximum;
 
@@ -1106,18 +1406,30 @@
                                     cmsUInt32Number Stride)
 {
 
-    cmsUInt32Number nChan  = T_CHANNELS(info -> InputFormat);
-    cmsUInt32Number DoSwap   = T_DOSWAP(info ->InputFormat);
-    cmsUInt32Number Reverse    = T_FLAVOR(info ->InputFormat);
-    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> InputFormat);
-    cmsUInt32Number Extra   = T_EXTRA(info -> InputFormat);
+    cmsUInt32Number nChan = T_CHANNELS(info->InputFormat);
+    cmsUInt32Number DoSwap = T_DOSWAP(info->InputFormat);
+    cmsUInt32Number Reverse = T_FLAVOR(info->InputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->InputFormat);
+    cmsUInt32Number Extra = T_EXTRA(info->InputFormat);
     cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
-    cmsUInt32Number Planar     = T_PLANAR(info -> InputFormat);
+    cmsUInt32Number Planar = T_PLANAR(info->InputFormat);
+    cmsUInt32Number Premul = T_PREMUL(info->InputFormat);
     cmsFloat64Number v;
     cmsUInt32Number i, start = 0;
     cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 100.0 : 1.0;
+    cmsFloat64Number alpha_factor = 1.0;
+    cmsFloat64Number* ptr = (cmsFloat64Number*)accum;
 
+    Stride /= PixelSize(info->InputFormat);
 
+    if (Premul && Extra)
+    {
+        if (Planar)
+            alpha_factor = (ExtraFirst ? ptr[0] : ptr[(nChan) * Stride]) / maximum;
+        else
+            alpha_factor = (ExtraFirst ? ptr[0] : ptr[nChan]) / maximum;
+    }
+   
     if (ExtraFirst)
             start = Extra;
 
@@ -1130,6 +1442,10 @@
         else
             v = (cmsFloat64Number) ((cmsFloat64Number*) accum)[i + start];
 
+
+        if (Premul && alpha_factor > 0)
+            v /= alpha_factor;
+
         v /= maximum;
 
         wIn[index] = (cmsFloat32Number) (Reverse ? 1.0 - v : v);
@@ -1162,7 +1478,9 @@
 
     if (T_PLANAR(info -> InputFormat)) {
 
-        wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0);                            // from 0..100 to 0..1
+        Stride /= PixelSize(info->InputFormat);
+
+        wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0);                 // from 0..100 to 0..1
         wIn[1] = (cmsFloat32Number) ((Pt[Stride] + 128) / 255.0);    // form -128..+127 to 0..1
         wIn[2] = (cmsFloat32Number) ((Pt[Stride*2] + 128) / 255.0);
 
@@ -1190,6 +1508,8 @@
 
     if (T_PLANAR(info -> InputFormat)) {
 
+        Stride /= PixelSize(info->InputFormat);
+
         wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0);                 // from 0..100 to 0..1
         wIn[1] = (cmsFloat32Number) ((Pt[Stride] + 128) / 255.0);    // form -128..+127 to 0..1
         wIn[2] = (cmsFloat32Number) ((Pt[Stride*2] + 128) / 255.0);
@@ -1207,8 +1527,6 @@
     }
 }
 
-
-
 // 1.15 fixed point, that means maximum value is MAX_ENCODEABLE_XYZ (0xFFFF)
 static
 cmsUInt8Number* UnrollXYZDoubleToFloat(_cmsTRANSFORM* info,
@@ -1220,6 +1538,8 @@
 
     if (T_PLANAR(info -> InputFormat)) {
 
+        Stride /= PixelSize(info->InputFormat);
+
         wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ);
         wIn[1] = (cmsFloat32Number) (Pt[Stride] / MAX_ENCODEABLE_XYZ);
         wIn[2] = (cmsFloat32Number) (Pt[Stride*2] / MAX_ENCODEABLE_XYZ);
@@ -1247,6 +1567,8 @@
 
     if (T_PLANAR(info -> InputFormat)) {
 
+        Stride /= PixelSize(info->InputFormat);
+
         wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ);
         wIn[1] = (cmsFloat32Number) (Pt[Stride] / MAX_ENCODEABLE_XYZ);
         wIn[2] = (cmsFloat32Number) (Pt[Stride*2] / MAX_ENCODEABLE_XYZ);
@@ -1265,44 +1587,132 @@
 }
 
 
+cmsINLINE void lab4toFloat(cmsFloat32Number wIn[], cmsUInt16Number lab4[3])
+{        
+    cmsFloat32Number L = (cmsFloat32Number) lab4[0] / 655.35F;
+    cmsFloat32Number a = ((cmsFloat32Number) lab4[1] / 257.0F) - 128.0F;
+    cmsFloat32Number b = ((cmsFloat32Number) lab4[2] / 257.0F) - 128.0F;
+    
+    wIn[0] = (L / 100.0F);                    // from 0..100 to 0..1
+    wIn[1] = ((a + 128.0F) / 255.0F);         // form -128..+127 to 0..1
+    wIn[2] = ((b + 128.0F) / 255.0F);
+
+}
+
+static
+cmsUInt8Number* UnrollLabV2_8ToFloat(_cmsTRANSFORM* info,
+                                      cmsFloat32Number wIn[],
+                                      cmsUInt8Number* accum,
+                                      cmsUInt32Number Stride)
+{
+    cmsUInt16Number lab4[3];
+
+    lab4[0] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++;     // L
+    lab4[1] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++;     // a
+    lab4[2] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++;     // b
+
+    lab4toFloat(wIn, lab4);
+
+    return accum;
+
+    cmsUNUSED_PARAMETER(info);
+    cmsUNUSED_PARAMETER(Stride);
+}
+
+static
+cmsUInt8Number* UnrollALabV2_8ToFloat(_cmsTRANSFORM* info,
+                                      cmsFloat32Number wIn[],
+                                      cmsUInt8Number* accum,
+                                      cmsUInt32Number Stride)
+{
+    cmsUInt16Number lab4[3];
+
+    accum++;  // A
+    lab4[0] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++;     // L
+    lab4[1] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++;     // a
+    lab4[2] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++;     // b
+
+    lab4toFloat(wIn, lab4);
+
+    return accum;
+
+    cmsUNUSED_PARAMETER(info);
+    cmsUNUSED_PARAMETER(Stride);
+}
+
+static
+cmsUInt8Number* UnrollLabV2_16ToFloat(_cmsTRANSFORM* info,
+                                      cmsFloat32Number wIn[],
+                                      cmsUInt8Number* accum,
+                                      cmsUInt32Number Stride)
+{
+    cmsUInt16Number lab4[3];
+
+    lab4[0] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2;     // L
+    lab4[1] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2;     // a
+    lab4[2] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2;     // b
+
+    lab4toFloat(wIn, lab4);
+
+    return accum;
+
+    cmsUNUSED_PARAMETER(info);
+    cmsUNUSED_PARAMETER(Stride);
+}
+
 
 // Packing routines -----------------------------------------------------------------------------------------------------------
 
 
 // Generic chunky for byte
-
 static
-cmsUInt8Number* PackAnyBytes(register _cmsTRANSFORM* info,
-                             register cmsUInt16Number wOut[],
-                             register cmsUInt8Number* output,
-                             register cmsUInt32Number Stride)
+cmsUInt8Number* PackChunkyBytes(CMSREGISTER _cmsTRANSFORM* info,
+                                CMSREGISTER cmsUInt16Number wOut[],
+                                CMSREGISTER cmsUInt8Number* output,
+                                CMSREGISTER cmsUInt32Number Stride)
 {
-    cmsUInt32Number nChan  = T_CHANNELS(info -> OutputFormat);
-    cmsUInt32Number DoSwap   = T_DOSWAP(info ->OutputFormat);
-    cmsUInt32Number Reverse    = T_FLAVOR(info ->OutputFormat);
-    cmsUInt32Number Extra   = T_EXTRA(info -> OutputFormat);
-    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> OutputFormat);
+    cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat);
+    cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat);
+    cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat);
+    cmsUInt32Number Extra = T_EXTRA(info->OutputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat);
+    cmsUInt32Number Premul = T_PREMUL(info->OutputFormat);
     cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
     cmsUInt8Number* swap1;
-    cmsUInt8Number v = 0;
+    cmsUInt16Number v = 0;
     cmsUInt32Number i;
+    cmsUInt32Number alpha_factor = 0;
 
     swap1 = output;
 
     if (ExtraFirst) {
+        
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(output[0]));
+
         output += Extra;
     }
+    else
+    {
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(output[nChan]));
+    }
 
     for (i=0; i < nChan; i++) {
 
         cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
-        v = FROM_16_TO_8(wOut[index]);
+        v = wOut[index];
 
         if (Reverse)
-            v = REVERSE_FLAVOR_8(v);
+            v = REVERSE_FLAVOR_16(v);
 
-        *output++ = v;
+        if (Premul)
+        {
+            v = (cmsUInt16Number)((cmsUInt32Number)((cmsUInt32Number)v * alpha_factor + 0x8000) >> 16);            
+        }
+
+        *output++ = FROM_16_TO_8(v);
     }
 
     if (!ExtraFirst) {
@@ -1312,39 +1722,47 @@
     if (Extra == 0 && SwapFirst) {
 
         memmove(swap1 + 1, swap1, nChan-1);
-        *swap1 = v;
+        *swap1 = FROM_16_TO_8(v);
     }
 
-
     return output;
 
     cmsUNUSED_PARAMETER(Stride);
 }
 
-
-
 static
-cmsUInt8Number* PackAnyWords(register _cmsTRANSFORM* info,
-                             register cmsUInt16Number wOut[],
-                             register cmsUInt8Number* output,
-                             register cmsUInt32Number Stride)
+cmsUInt8Number* PackChunkyWords(CMSREGISTER _cmsTRANSFORM* info,
+                                CMSREGISTER cmsUInt16Number wOut[],
+                                CMSREGISTER cmsUInt8Number* output,
+                                CMSREGISTER cmsUInt32Number Stride)
 {
-    cmsUInt32Number nChan  = T_CHANNELS(info -> OutputFormat);
-    cmsUInt32Number SwapEndian = T_ENDIAN16(info -> OutputFormat);
-    cmsUInt32Number DoSwap   = T_DOSWAP(info ->OutputFormat);
-    cmsUInt32Number Reverse    = T_FLAVOR(info ->OutputFormat);
-    cmsUInt32Number Extra   = T_EXTRA(info -> OutputFormat);
-    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> OutputFormat);
+    cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat);
+    cmsUInt32Number SwapEndian = T_ENDIAN16(info->OutputFormat);
+    cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat);
+    cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat);
+    cmsUInt32Number Extra = T_EXTRA(info->OutputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat);
+    cmsUInt32Number Premul = T_PREMUL(info->OutputFormat);
     cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
     cmsUInt16Number* swap1;
     cmsUInt16Number v = 0;
     cmsUInt32Number i;
-
+    cmsUInt32Number alpha_factor = 0;
+    
     swap1 = (cmsUInt16Number*) output;
 
     if (ExtraFirst) {
+
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(*(cmsUInt16Number*) output);
+
         output += Extra * sizeof(cmsUInt16Number);
     }
+    else
+    {
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(((cmsUInt16Number*) output)[nChan]);
+    }
 
     for (i=0; i < nChan; i++) {
 
@@ -1358,6 +1776,11 @@
         if (Reverse)
             v = REVERSE_FLAVOR_16(v);
 
+        if (Premul)
+        {
+            v = (cmsUInt16Number)((cmsUInt32Number)((cmsUInt32Number)v * alpha_factor + 0x8000) >> 16);
+        }
+
         *(cmsUInt16Number*) output = v;
 
         output += sizeof(cmsUInt16Number);
@@ -1373,38 +1796,60 @@
         *swap1 = v;
     }
 
-
     return output;
 
     cmsUNUSED_PARAMETER(Stride);
 }
 
 
+
 static
-cmsUInt8Number* PackPlanarBytes(register _cmsTRANSFORM* info,
-                                register cmsUInt16Number wOut[],
-                                register cmsUInt8Number* output,
-                                register cmsUInt32Number Stride)
+cmsUInt8Number* PackPlanarBytes(CMSREGISTER _cmsTRANSFORM* info,
+                                CMSREGISTER cmsUInt16Number wOut[],
+                                CMSREGISTER cmsUInt8Number* output,
+                                CMSREGISTER cmsUInt32Number Stride)
 {
-    cmsUInt32Number nChan     = T_CHANNELS(info -> OutputFormat);
-    cmsUInt32Number DoSwap    = T_DOSWAP(info ->OutputFormat);
-    cmsUInt32Number SwapFirst = T_SWAPFIRST(info ->OutputFormat);
-    cmsUInt32Number Reverse   = T_FLAVOR(info ->OutputFormat);
+    cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat);
+    cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat);
+    cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat);
+    cmsUInt32Number Extra = T_EXTRA(info->OutputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number Premul = T_PREMUL(info->OutputFormat);
     cmsUInt32Number i;
     cmsUInt8Number* Init = output;
+    cmsUInt32Number alpha_factor = 0;
 
 
-    if (DoSwap ^ SwapFirst) {
-        output += T_EXTRA(info -> OutputFormat) * Stride;
+    if (ExtraFirst) {
+
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(output[0]));
+
+        output += Extra * Stride;
+    }
+    else
+    {
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(output[nChan * Stride]));
     }
 
 
     for (i=0; i < nChan; i++) {
 
         cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
-        cmsUInt8Number v = FROM_16_TO_8(wOut[index]);
+        cmsUInt16Number v = wOut[index];
 
-        *(cmsUInt8Number*)  output = (cmsUInt8Number) (Reverse ? REVERSE_FLAVOR_8(v) : v);
+        if (Reverse)
+            v = REVERSE_FLAVOR_16(v);
+
+        if (Premul)
+        {
+            v = (cmsUInt16Number)((cmsUInt32Number)((cmsUInt32Number)v * alpha_factor + 0x8000) >> 16);
+        }
+
+        *(cmsUInt8Number*)output = FROM_16_TO_8(v);
+
         output += Stride;
     }
 
@@ -1415,21 +1860,35 @@
 
 
 static
-cmsUInt8Number* PackPlanarWords(register _cmsTRANSFORM* info,
-                                register cmsUInt16Number wOut[],
-                                register cmsUInt8Number* output,
-                                register cmsUInt32Number Stride)
+cmsUInt8Number* PackPlanarWords(CMSREGISTER _cmsTRANSFORM* info,
+                                CMSREGISTER cmsUInt16Number wOut[],
+                                CMSREGISTER cmsUInt8Number* output,
+                                CMSREGISTER cmsUInt32Number Stride)
 {
-    cmsUInt32Number nChan      = T_CHANNELS(info -> OutputFormat);
-    cmsUInt32Number DoSwap     = T_DOSWAP(info ->OutputFormat);
-    cmsUInt32Number Reverse    = T_FLAVOR(info ->OutputFormat);
-    cmsUInt32Number SwapEndian = T_ENDIAN16(info -> OutputFormat);
+    cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat);
+    cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat);
+    cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat);
+    cmsUInt32Number Extra = T_EXTRA(info->OutputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number Premul = T_PREMUL(info->OutputFormat);
+    cmsUInt32Number SwapEndian = T_ENDIAN16(info->OutputFormat);
     cmsUInt32Number i;
     cmsUInt8Number* Init = output;
     cmsUInt16Number v;
+    cmsUInt32Number alpha_factor = 0;
 
-    if (DoSwap) {
-        output += T_EXTRA(info -> OutputFormat) * Stride * sizeof(cmsUInt16Number);
+    if (ExtraFirst) {
+
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(((cmsUInt16Number*) output)[0]);
+
+        output += Extra * Stride;
+    }
+    else
+    {
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(((cmsUInt16Number*)output)[nChan * Stride]);
     }
 
     for (i=0; i < nChan; i++) {
@@ -1444,8 +1903,13 @@
         if (Reverse)
             v =  REVERSE_FLAVOR_16(v);
 
+        if (Premul)
+        {
+            v = (cmsUInt16Number)((cmsUInt32Number)((cmsUInt32Number)v * alpha_factor + 0x8000) >> 16);
+        }
+
         *(cmsUInt16Number*) output = v;
-        output += (Stride * sizeof(cmsUInt16Number));
+        output += Stride;
     }
 
     return (Init + sizeof(cmsUInt16Number));
@@ -1454,10 +1918,10 @@
 // CMYKcm (unrolled for speed)
 
 static
-cmsUInt8Number* Pack6Bytes(register _cmsTRANSFORM* info,
-                           register cmsUInt16Number wOut[],
-                           register cmsUInt8Number* output,
-                           register cmsUInt32Number Stride)
+cmsUInt8Number* Pack6Bytes(CMSREGISTER _cmsTRANSFORM* info,
+                           CMSREGISTER cmsUInt16Number wOut[],
+                           CMSREGISTER cmsUInt8Number* output,
+                           CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = FROM_16_TO_8(wOut[0]);
     *output++ = FROM_16_TO_8(wOut[1]);
@@ -1475,10 +1939,10 @@
 // KCMYcm
 
 static
-cmsUInt8Number* Pack6BytesSwap(register _cmsTRANSFORM* info,
-                               register cmsUInt16Number wOut[],
-                               register cmsUInt8Number* output,
-                               register cmsUInt32Number Stride)
+cmsUInt8Number* Pack6BytesSwap(CMSREGISTER _cmsTRANSFORM* info,
+                               CMSREGISTER cmsUInt16Number wOut[],
+                               CMSREGISTER cmsUInt8Number* output,
+                               CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = FROM_16_TO_8(wOut[5]);
     *output++ = FROM_16_TO_8(wOut[4]);
@@ -1495,10 +1959,10 @@
 
 // CMYKcm
 static
-cmsUInt8Number* Pack6Words(register _cmsTRANSFORM* info,
-                           register cmsUInt16Number wOut[],
-                           register cmsUInt8Number* output,
-                           register cmsUInt32Number Stride)
+cmsUInt8Number* Pack6Words(CMSREGISTER _cmsTRANSFORM* info,
+                           CMSREGISTER cmsUInt16Number wOut[],
+                           CMSREGISTER cmsUInt8Number* output,
+                           CMSREGISTER cmsUInt32Number Stride)
 {
     *(cmsUInt16Number*) output = wOut[0];
     output+= 2;
@@ -1521,10 +1985,10 @@
 
 // KCMYcm
 static
-cmsUInt8Number* Pack6WordsSwap(register _cmsTRANSFORM* info,
-                               register cmsUInt16Number wOut[],
-                               register cmsUInt8Number* output,
-                               register cmsUInt32Number Stride)
+cmsUInt8Number* Pack6WordsSwap(CMSREGISTER _cmsTRANSFORM* info,
+                               CMSREGISTER cmsUInt16Number wOut[],
+                               CMSREGISTER cmsUInt8Number* output,
+                               CMSREGISTER cmsUInt32Number Stride)
 {
     *(cmsUInt16Number*) output = wOut[5];
     output+= 2;
@@ -1547,10 +2011,10 @@
 
 
 static
-cmsUInt8Number* Pack4Bytes(register _cmsTRANSFORM* info,
-                           register cmsUInt16Number wOut[],
-                           register cmsUInt8Number* output,
-                           register cmsUInt32Number Stride)
+cmsUInt8Number* Pack4Bytes(CMSREGISTER _cmsTRANSFORM* info,
+                           CMSREGISTER cmsUInt16Number wOut[],
+                           CMSREGISTER cmsUInt8Number* output,
+                           CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = FROM_16_TO_8(wOut[0]);
     *output++ = FROM_16_TO_8(wOut[1]);
@@ -1564,10 +2028,10 @@
 }
 
 static
-cmsUInt8Number* Pack4BytesReverse(register _cmsTRANSFORM* info,
-                                  register cmsUInt16Number wOut[],
-                                  register cmsUInt8Number* output,
-                                  register cmsUInt32Number Stride)
+cmsUInt8Number* Pack4BytesReverse(CMSREGISTER _cmsTRANSFORM* info,
+                                  CMSREGISTER cmsUInt16Number wOut[],
+                                  CMSREGISTER cmsUInt8Number* output,
+                                  CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[0]));
     *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[1]));
@@ -1582,10 +2046,10 @@
 
 
 static
-cmsUInt8Number* Pack4BytesSwapFirst(register _cmsTRANSFORM* info,
-                                    register cmsUInt16Number wOut[],
-                                    register cmsUInt8Number* output,
-                                    register cmsUInt32Number Stride)
+cmsUInt8Number* Pack4BytesSwapFirst(CMSREGISTER _cmsTRANSFORM* info,
+                                    CMSREGISTER cmsUInt16Number wOut[],
+                                    CMSREGISTER cmsUInt8Number* output,
+                                    CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = FROM_16_TO_8(wOut[3]);
     *output++ = FROM_16_TO_8(wOut[0]);
@@ -1600,10 +2064,10 @@
 
 // ABGR
 static
-cmsUInt8Number* Pack4BytesSwap(register _cmsTRANSFORM* info,
-                               register cmsUInt16Number wOut[],
-                               register cmsUInt8Number* output,
-                               register cmsUInt32Number Stride)
+cmsUInt8Number* Pack4BytesSwap(CMSREGISTER _cmsTRANSFORM* info,
+                               CMSREGISTER cmsUInt16Number wOut[],
+                               CMSREGISTER cmsUInt8Number* output,
+                               CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = FROM_16_TO_8(wOut[3]);
     *output++ = FROM_16_TO_8(wOut[2]);
@@ -1617,10 +2081,10 @@
 }
 
 static
-cmsUInt8Number* Pack4BytesSwapSwapFirst(register _cmsTRANSFORM* info,
-                                        register cmsUInt16Number wOut[],
-                                        register cmsUInt8Number* output,
-                                        register cmsUInt32Number Stride)
+cmsUInt8Number* Pack4BytesSwapSwapFirst(CMSREGISTER _cmsTRANSFORM* info,
+                                        CMSREGISTER cmsUInt16Number wOut[],
+                                        CMSREGISTER cmsUInt8Number* output,
+                                        CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = FROM_16_TO_8(wOut[2]);
     *output++ = FROM_16_TO_8(wOut[1]);
@@ -1634,10 +2098,10 @@
 }
 
 static
-cmsUInt8Number* Pack4Words(register _cmsTRANSFORM* info,
-                           register cmsUInt16Number wOut[],
-                           register cmsUInt8Number* output,
-                           register cmsUInt32Number Stride)
+cmsUInt8Number* Pack4Words(CMSREGISTER _cmsTRANSFORM* info,
+                           CMSREGISTER cmsUInt16Number wOut[],
+                           CMSREGISTER cmsUInt8Number* output,
+                           CMSREGISTER cmsUInt32Number Stride)
 {
     *(cmsUInt16Number*) output = wOut[0];
     output+= 2;
@@ -1655,10 +2119,10 @@
 }
 
 static
-cmsUInt8Number* Pack4WordsReverse(register _cmsTRANSFORM* info,
-                                  register cmsUInt16Number wOut[],
-                                  register cmsUInt8Number* output,
-                                  register cmsUInt32Number Stride)
+cmsUInt8Number* Pack4WordsReverse(CMSREGISTER _cmsTRANSFORM* info,
+                                  CMSREGISTER cmsUInt16Number wOut[],
+                                  CMSREGISTER cmsUInt8Number* output,
+                                  CMSREGISTER cmsUInt32Number Stride)
 {
     *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[0]);
     output+= 2;
@@ -1677,10 +2141,10 @@
 
 // ABGR
 static
-cmsUInt8Number* Pack4WordsSwap(register _cmsTRANSFORM* info,
-                               register cmsUInt16Number wOut[],
-                               register cmsUInt8Number* output,
-                               register cmsUInt32Number Stride)
+cmsUInt8Number* Pack4WordsSwap(CMSREGISTER _cmsTRANSFORM* info,
+                               CMSREGISTER cmsUInt16Number wOut[],
+                               CMSREGISTER cmsUInt8Number* output,
+                               CMSREGISTER cmsUInt32Number Stride)
 {
     *(cmsUInt16Number*) output = wOut[3];
     output+= 2;
@@ -1699,10 +2163,10 @@
 
 // CMYK
 static
-cmsUInt8Number* Pack4WordsBigEndian(register _cmsTRANSFORM* info,
-                                    register cmsUInt16Number wOut[],
-                                    register cmsUInt8Number* output,
-                                    register cmsUInt32Number Stride)
+cmsUInt8Number* Pack4WordsBigEndian(CMSREGISTER _cmsTRANSFORM* info,
+                                    CMSREGISTER cmsUInt16Number wOut[],
+                                    CMSREGISTER cmsUInt8Number* output,
+                                    CMSREGISTER cmsUInt32Number Stride)
 {
     *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[0]);
     output+= 2;
@@ -1721,10 +2185,10 @@
 
 
 static
-cmsUInt8Number* PackLabV2_8(register _cmsTRANSFORM* info,
-                            register cmsUInt16Number wOut[],
-                            register cmsUInt8Number* output,
-                            register cmsUInt32Number Stride)
+cmsUInt8Number* PackLabV2_8(CMSREGISTER _cmsTRANSFORM* info,
+                            CMSREGISTER cmsUInt16Number wOut[],
+                            CMSREGISTER cmsUInt8Number* output,
+                            CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[0]));
     *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[1]));
@@ -1737,10 +2201,10 @@
 }
 
 static
-cmsUInt8Number* PackALabV2_8(register _cmsTRANSFORM* info,
-                             register cmsUInt16Number wOut[],
-                             register cmsUInt8Number* output,
-                             register cmsUInt32Number Stride)
+cmsUInt8Number* PackALabV2_8(CMSREGISTER _cmsTRANSFORM* info,
+                             CMSREGISTER cmsUInt16Number wOut[],
+                             CMSREGISTER cmsUInt8Number* output,
+                             CMSREGISTER cmsUInt32Number Stride)
 {
     output++;
     *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[0]));
@@ -1754,10 +2218,10 @@
 }
 
 static
-cmsUInt8Number* PackLabV2_16(register _cmsTRANSFORM* info,
-                             register cmsUInt16Number wOut[],
-                             register cmsUInt8Number* output,
-                             register cmsUInt32Number Stride)
+cmsUInt8Number* PackLabV2_16(CMSREGISTER _cmsTRANSFORM* info,
+                             CMSREGISTER cmsUInt16Number wOut[],
+                             CMSREGISTER cmsUInt8Number* output,
+                             CMSREGISTER cmsUInt32Number Stride)
 {
     *(cmsUInt16Number*) output = FomLabV4ToLabV2(wOut[0]);
     output += 2;
@@ -1773,10 +2237,10 @@
 }
 
 static
-cmsUInt8Number* Pack3Bytes(register _cmsTRANSFORM* info,
-                           register cmsUInt16Number wOut[],
-                           register cmsUInt8Number* output,
-                           register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3Bytes(CMSREGISTER _cmsTRANSFORM* info,
+                           CMSREGISTER cmsUInt16Number wOut[],
+                           CMSREGISTER cmsUInt8Number* output,
+                           CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = FROM_16_TO_8(wOut[0]);
     *output++ = FROM_16_TO_8(wOut[1]);
@@ -1789,10 +2253,10 @@
 }
 
 static
-cmsUInt8Number* Pack3BytesOptimized(register _cmsTRANSFORM* info,
-                                    register cmsUInt16Number wOut[],
-                                    register cmsUInt8Number* output,
-                                    register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3BytesOptimized(CMSREGISTER _cmsTRANSFORM* info,
+                                    CMSREGISTER cmsUInt16Number wOut[],
+                                    CMSREGISTER cmsUInt8Number* output,
+                                    CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = (wOut[0] & 0xFFU);
     *output++ = (wOut[1] & 0xFFU);
@@ -1805,10 +2269,10 @@
 }
 
 static
-cmsUInt8Number* Pack3BytesSwap(register _cmsTRANSFORM* info,
-                               register cmsUInt16Number wOut[],
-                               register cmsUInt8Number* output,
-                               register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3BytesSwap(CMSREGISTER _cmsTRANSFORM* info,
+                               CMSREGISTER cmsUInt16Number wOut[],
+                               CMSREGISTER cmsUInt8Number* output,
+                               CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = FROM_16_TO_8(wOut[2]);
     *output++ = FROM_16_TO_8(wOut[1]);
@@ -1821,10 +2285,10 @@
 }
 
 static
-cmsUInt8Number* Pack3BytesSwapOptimized(register _cmsTRANSFORM* info,
-                                        register cmsUInt16Number wOut[],
-                                        register cmsUInt8Number* output,
-                                        register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3BytesSwapOptimized(CMSREGISTER _cmsTRANSFORM* info,
+                                        CMSREGISTER cmsUInt16Number wOut[],
+                                        CMSREGISTER cmsUInt8Number* output,
+                                        CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = (wOut[2] & 0xFFU);
     *output++ = (wOut[1] & 0xFFU);
@@ -1838,10 +2302,10 @@
 
 
 static
-cmsUInt8Number* Pack3Words(register _cmsTRANSFORM* info,
-                           register cmsUInt16Number wOut[],
-                           register cmsUInt8Number* output,
-                           register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3Words(CMSREGISTER _cmsTRANSFORM* info,
+                           CMSREGISTER cmsUInt16Number wOut[],
+                           CMSREGISTER cmsUInt8Number* output,
+                           CMSREGISTER cmsUInt32Number Stride)
 {
     *(cmsUInt16Number*) output = wOut[0];
     output+= 2;
@@ -1857,10 +2321,10 @@
 }
 
 static
-cmsUInt8Number* Pack3WordsSwap(register _cmsTRANSFORM* info,
-                               register cmsUInt16Number wOut[],
-                               register cmsUInt8Number* output,
-                               register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3WordsSwap(CMSREGISTER _cmsTRANSFORM* info,
+                               CMSREGISTER cmsUInt16Number wOut[],
+                               CMSREGISTER cmsUInt8Number* output,
+                               CMSREGISTER cmsUInt32Number Stride)
 {
     *(cmsUInt16Number*) output = wOut[2];
     output+= 2;
@@ -1876,10 +2340,10 @@
 }
 
 static
-cmsUInt8Number* Pack3WordsBigEndian(register _cmsTRANSFORM* info,
-                                    register cmsUInt16Number wOut[],
-                                    register cmsUInt8Number* output,
-                                    register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3WordsBigEndian(CMSREGISTER _cmsTRANSFORM* info,
+                                    CMSREGISTER cmsUInt16Number wOut[],
+                                    CMSREGISTER cmsUInt8Number* output,
+                                    CMSREGISTER cmsUInt32Number Stride)
 {
     *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[0]);
     output+= 2;
@@ -1895,10 +2359,10 @@
 }
 
 static
-cmsUInt8Number* Pack3BytesAndSkip1(register _cmsTRANSFORM* info,
-                                   register cmsUInt16Number wOut[],
-                                   register cmsUInt8Number* output,
-                                   register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3BytesAndSkip1(CMSREGISTER _cmsTRANSFORM* info,
+                                   CMSREGISTER cmsUInt16Number wOut[],
+                                   CMSREGISTER cmsUInt8Number* output,
+                                   CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = FROM_16_TO_8(wOut[0]);
     *output++ = FROM_16_TO_8(wOut[1]);
@@ -1912,10 +2376,10 @@
 }
 
 static
-cmsUInt8Number* Pack3BytesAndSkip1Optimized(register _cmsTRANSFORM* info,
-                                            register cmsUInt16Number wOut[],
-                                            register cmsUInt8Number* output,
-                                            register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3BytesAndSkip1Optimized(CMSREGISTER _cmsTRANSFORM* info,
+                                            CMSREGISTER cmsUInt16Number wOut[],
+                                            CMSREGISTER cmsUInt8Number* output,
+                                            CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = (wOut[0] & 0xFFU);
     *output++ = (wOut[1] & 0xFFU);
@@ -1930,10 +2394,10 @@
 
 
 static
-cmsUInt8Number* Pack3BytesAndSkip1SwapFirst(register _cmsTRANSFORM* info,
-                                            register cmsUInt16Number wOut[],
-                                            register cmsUInt8Number* output,
-                                            register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3BytesAndSkip1SwapFirst(CMSREGISTER _cmsTRANSFORM* info,
+                                            CMSREGISTER cmsUInt16Number wOut[],
+                                            CMSREGISTER cmsUInt8Number* output,
+                                            CMSREGISTER cmsUInt32Number Stride)
 {
     output++;
     *output++ = FROM_16_TO_8(wOut[0]);
@@ -1947,10 +2411,10 @@
 }
 
 static
-cmsUInt8Number* Pack3BytesAndSkip1SwapFirstOptimized(register _cmsTRANSFORM* info,
-                                                     register cmsUInt16Number wOut[],
-                                                     register cmsUInt8Number* output,
-                                                     register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3BytesAndSkip1SwapFirstOptimized(CMSREGISTER _cmsTRANSFORM* info,
+                                                     CMSREGISTER cmsUInt16Number wOut[],
+                                                     CMSREGISTER cmsUInt8Number* output,
+                                                     CMSREGISTER cmsUInt32Number Stride)
 {
     output++;
     *output++ = (wOut[0] & 0xFFU);
@@ -1964,10 +2428,10 @@
 }
 
 static
-cmsUInt8Number* Pack3BytesAndSkip1Swap(register _cmsTRANSFORM* info,
-                                       register cmsUInt16Number wOut[],
-                                       register cmsUInt8Number* output,
-                                       register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3BytesAndSkip1Swap(CMSREGISTER _cmsTRANSFORM* info,
+                                       CMSREGISTER cmsUInt16Number wOut[],
+                                       CMSREGISTER cmsUInt8Number* output,
+                                       CMSREGISTER cmsUInt32Number Stride)
 {
     output++;
     *output++ = FROM_16_TO_8(wOut[2]);
@@ -1981,10 +2445,10 @@
 }
 
 static
-cmsUInt8Number* Pack3BytesAndSkip1SwapOptimized(register _cmsTRANSFORM* info,
-                                                register cmsUInt16Number wOut[],
-                                                register cmsUInt8Number* output,
-                                                register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3BytesAndSkip1SwapOptimized(CMSREGISTER _cmsTRANSFORM* info,
+                                                CMSREGISTER cmsUInt16Number wOut[],
+                                                CMSREGISTER cmsUInt8Number* output,
+                                                CMSREGISTER cmsUInt32Number Stride)
 {
     output++;
     *output++ = (wOut[2] & 0xFFU);
@@ -1999,10 +2463,10 @@
 
 
 static
-cmsUInt8Number* Pack3BytesAndSkip1SwapSwapFirst(register _cmsTRANSFORM* info,
-                                                register cmsUInt16Number wOut[],
-                                                register cmsUInt8Number* output,
-                                                register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3BytesAndSkip1SwapSwapFirst(CMSREGISTER _cmsTRANSFORM* info,
+                                                CMSREGISTER cmsUInt16Number wOut[],
+                                                CMSREGISTER cmsUInt8Number* output,
+                                                CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = FROM_16_TO_8(wOut[2]);
     *output++ = FROM_16_TO_8(wOut[1]);
@@ -2016,10 +2480,10 @@
 }
 
 static
-cmsUInt8Number* Pack3BytesAndSkip1SwapSwapFirstOptimized(register _cmsTRANSFORM* info,
-                                                         register cmsUInt16Number wOut[],
-                                                         register cmsUInt8Number* output,
-                                                         register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3BytesAndSkip1SwapSwapFirstOptimized(CMSREGISTER _cmsTRANSFORM* info,
+                                                         CMSREGISTER cmsUInt16Number wOut[],
+                                                         CMSREGISTER cmsUInt8Number* output,
+                                                         CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = (wOut[2] & 0xFFU);
     *output++ = (wOut[1] & 0xFFU);
@@ -2033,10 +2497,10 @@
 }
 
 static
-cmsUInt8Number* Pack3WordsAndSkip1(register _cmsTRANSFORM* info,
-                                   register cmsUInt16Number wOut[],
-                                   register cmsUInt8Number* output,
-                                   register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3WordsAndSkip1(CMSREGISTER _cmsTRANSFORM* info,
+                                   CMSREGISTER cmsUInt16Number wOut[],
+                                   CMSREGISTER cmsUInt8Number* output,
+                                   CMSREGISTER cmsUInt32Number Stride)
 {
     *(cmsUInt16Number*) output = wOut[0];
     output+= 2;
@@ -2053,10 +2517,10 @@
 }
 
 static
-cmsUInt8Number* Pack3WordsAndSkip1Swap(register _cmsTRANSFORM* info,
-                                       register cmsUInt16Number wOut[],
-                                       register cmsUInt8Number* output,
-                                       register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3WordsAndSkip1Swap(CMSREGISTER _cmsTRANSFORM* info,
+                                       CMSREGISTER cmsUInt16Number wOut[],
+                                       CMSREGISTER cmsUInt8Number* output,
+                                       CMSREGISTER cmsUInt32Number Stride)
 {
     output+= 2;
     *(cmsUInt16Number*) output = wOut[2];
@@ -2074,10 +2538,10 @@
 
 
 static
-cmsUInt8Number* Pack3WordsAndSkip1SwapFirst(register _cmsTRANSFORM* info,
-                                            register cmsUInt16Number wOut[],
-                                            register cmsUInt8Number* output,
-                                            register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3WordsAndSkip1SwapFirst(CMSREGISTER _cmsTRANSFORM* info,
+                                            CMSREGISTER cmsUInt16Number wOut[],
+                                            CMSREGISTER cmsUInt8Number* output,
+                                            CMSREGISTER cmsUInt32Number Stride)
 {
     output+= 2;
     *(cmsUInt16Number*) output = wOut[0];
@@ -2095,10 +2559,10 @@
 
 
 static
-cmsUInt8Number* Pack3WordsAndSkip1SwapSwapFirst(register _cmsTRANSFORM* info,
-                                                register cmsUInt16Number wOut[],
-                                                register cmsUInt8Number* output,
-                                                register cmsUInt32Number Stride)
+cmsUInt8Number* Pack3WordsAndSkip1SwapSwapFirst(CMSREGISTER _cmsTRANSFORM* info,
+                                                CMSREGISTER cmsUInt16Number wOut[],
+                                                CMSREGISTER cmsUInt8Number* output,
+                                                CMSREGISTER cmsUInt32Number Stride)
 {
     *(cmsUInt16Number*) output = wOut[2];
     output+= 2;
@@ -2117,10 +2581,10 @@
 
 
 static
-cmsUInt8Number* Pack1Byte(register _cmsTRANSFORM* info,
-                          register cmsUInt16Number wOut[],
-                          register cmsUInt8Number* output,
-                          register cmsUInt32Number Stride)
+cmsUInt8Number* Pack1Byte(CMSREGISTER _cmsTRANSFORM* info,
+                          CMSREGISTER cmsUInt16Number wOut[],
+                          CMSREGISTER cmsUInt8Number* output,
+                          CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = FROM_16_TO_8(wOut[0]);
 
@@ -2132,10 +2596,10 @@
 
 
 static
-cmsUInt8Number* Pack1ByteReversed(register _cmsTRANSFORM* info,
-                                  register cmsUInt16Number wOut[],
-                                  register cmsUInt8Number* output,
-                                  register cmsUInt32Number Stride)
+cmsUInt8Number* Pack1ByteReversed(CMSREGISTER _cmsTRANSFORM* info,
+                                  CMSREGISTER cmsUInt16Number wOut[],
+                                  CMSREGISTER cmsUInt8Number* output,
+                                  CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = FROM_16_TO_8(REVERSE_FLAVOR_16(wOut[0]));
 
@@ -2147,10 +2611,10 @@
 
 
 static
-cmsUInt8Number* Pack1ByteSkip1(register _cmsTRANSFORM* info,
-                               register cmsUInt16Number wOut[],
-                               register cmsUInt8Number* output,
-                               register cmsUInt32Number Stride)
+cmsUInt8Number* Pack1ByteSkip1(CMSREGISTER _cmsTRANSFORM* info,
+                               CMSREGISTER cmsUInt16Number wOut[],
+                               CMSREGISTER cmsUInt8Number* output,
+                               CMSREGISTER cmsUInt32Number Stride)
 {
     *output++ = FROM_16_TO_8(wOut[0]);
     output++;
@@ -2163,10 +2627,10 @@
 
 
 static
-cmsUInt8Number* Pack1ByteSkip1SwapFirst(register _cmsTRANSFORM* info,
-                                        register cmsUInt16Number wOut[],
-                                        register cmsUInt8Number* output,
-                                        register cmsUInt32Number Stride)
+cmsUInt8Number* Pack1ByteSkip1SwapFirst(CMSREGISTER _cmsTRANSFORM* info,
+                                        CMSREGISTER cmsUInt16Number wOut[],
+                                        CMSREGISTER cmsUInt8Number* output,
+                                        CMSREGISTER cmsUInt32Number Stride)
 {
     output++;
     *output++ = FROM_16_TO_8(wOut[0]);
@@ -2178,10 +2642,10 @@
 }
 
 static
-cmsUInt8Number* Pack1Word(register _cmsTRANSFORM* info,
-                          register cmsUInt16Number wOut[],
-                          register cmsUInt8Number* output,
-                          register cmsUInt32Number Stride)
+cmsUInt8Number* Pack1Word(CMSREGISTER _cmsTRANSFORM* info,
+                          CMSREGISTER cmsUInt16Number wOut[],
+                          CMSREGISTER cmsUInt8Number* output,
+                          CMSREGISTER cmsUInt32Number Stride)
 {
     *(cmsUInt16Number*) output = wOut[0];
     output+= 2;
@@ -2194,10 +2658,10 @@
 
 
 static
-cmsUInt8Number* Pack1WordReversed(register _cmsTRANSFORM* info,
-                                  register cmsUInt16Number wOut[],
-                                  register cmsUInt8Number* output,
-                                  register cmsUInt32Number Stride)
+cmsUInt8Number* Pack1WordReversed(CMSREGISTER _cmsTRANSFORM* info,
+                                  CMSREGISTER cmsUInt16Number wOut[],
+                                  CMSREGISTER cmsUInt8Number* output,
+                                  CMSREGISTER cmsUInt32Number Stride)
 {
     *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[0]);
     output+= 2;
@@ -2209,10 +2673,10 @@
 }
 
 static
-cmsUInt8Number* Pack1WordBigEndian(register _cmsTRANSFORM* info,
-                                   register cmsUInt16Number wOut[],
-                                   register cmsUInt8Number* output,
-                                   register cmsUInt32Number Stride)
+cmsUInt8Number* Pack1WordBigEndian(CMSREGISTER _cmsTRANSFORM* info,
+                                   CMSREGISTER cmsUInt16Number wOut[],
+                                   CMSREGISTER cmsUInt8Number* output,
+                                   CMSREGISTER cmsUInt32Number Stride)
 {
     *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[0]);
     output+= 2;
@@ -2225,10 +2689,10 @@
 
 
 static
-cmsUInt8Number* Pack1WordSkip1(register _cmsTRANSFORM* info,
-                               register cmsUInt16Number wOut[],
-                               register cmsUInt8Number* output,
-                               register cmsUInt32Number Stride)
+cmsUInt8Number* Pack1WordSkip1(CMSREGISTER _cmsTRANSFORM* info,
+                               CMSREGISTER cmsUInt16Number wOut[],
+                               CMSREGISTER cmsUInt8Number* output,
+                               CMSREGISTER cmsUInt32Number Stride)
 {
     *(cmsUInt16Number*) output = wOut[0];
     output+= 4;
@@ -2240,10 +2704,10 @@
 }
 
 static
-cmsUInt8Number* Pack1WordSkip1SwapFirst(register _cmsTRANSFORM* info,
-                                        register cmsUInt16Number wOut[],
-                                        register cmsUInt8Number* output,
-                                        register cmsUInt32Number Stride)
+cmsUInt8Number* Pack1WordSkip1SwapFirst(CMSREGISTER _cmsTRANSFORM* info,
+                                        CMSREGISTER cmsUInt16Number wOut[],
+                                        CMSREGISTER cmsUInt8Number* output,
+                                        CMSREGISTER cmsUInt32Number Stride)
 {
     output += 2;
     *(cmsUInt16Number*) output = wOut[0];
@@ -2258,10 +2722,10 @@
 
 // Unencoded Float values -- don't try optimize speed
 static
-cmsUInt8Number* PackLabDoubleFrom16(register _cmsTRANSFORM* info,
-                                    register cmsUInt16Number wOut[],
-                                    register cmsUInt8Number* output,
-                                    register cmsUInt32Number Stride)
+cmsUInt8Number* PackLabDoubleFrom16(CMSREGISTER _cmsTRANSFORM* info,
+                                    CMSREGISTER cmsUInt16Number wOut[],
+                                    CMSREGISTER cmsUInt8Number* output,
+                                    CMSREGISTER cmsUInt32Number Stride)
 {
 
     if (T_PLANAR(info -> OutputFormat)) {
@@ -2285,10 +2749,10 @@
 
 
 static
-cmsUInt8Number* PackLabFloatFrom16(register _cmsTRANSFORM* info,
-                                    register cmsUInt16Number wOut[],
-                                    register cmsUInt8Number* output,
-                                    register cmsUInt32Number Stride)
+cmsUInt8Number* PackLabFloatFrom16(CMSREGISTER _cmsTRANSFORM* info,
+                                    CMSREGISTER cmsUInt16Number wOut[],
+                                    CMSREGISTER cmsUInt8Number* output,
+                                    CMSREGISTER cmsUInt32Number Stride)
 {
     cmsCIELab  Lab;
     cmsLabEncoded2Float(&Lab, wOut);
@@ -2297,6 +2761,8 @@
        
         cmsFloat32Number* Out = (cmsFloat32Number*) output;
     
+        Stride /= PixelSize(info->OutputFormat);
+
         Out[0]        = (cmsFloat32Number)Lab.L;
         Out[Stride]   = (cmsFloat32Number)Lab.a;
         Out[Stride*2] = (cmsFloat32Number)Lab.b;
@@ -2314,10 +2780,10 @@
 }
 
 static
-cmsUInt8Number* PackXYZDoubleFrom16(register _cmsTRANSFORM* Info,
-                                    register cmsUInt16Number wOut[],
-                                    register cmsUInt8Number* output,
-                                    register cmsUInt32Number Stride)
+cmsUInt8Number* PackXYZDoubleFrom16(CMSREGISTER _cmsTRANSFORM* Info,
+                                    CMSREGISTER cmsUInt16Number wOut[],
+                                    CMSREGISTER cmsUInt8Number* output,
+                                    CMSREGISTER cmsUInt32Number Stride)
 {
     if (T_PLANAR(Info -> OutputFormat)) {
 
@@ -2325,6 +2791,8 @@
         cmsFloat64Number* Out = (cmsFloat64Number*) output;
         cmsXYZEncoded2Float(&XYZ, wOut);
 
+        Stride /= PixelSize(Info->OutputFormat);
+
         Out[0]        = XYZ.X;
         Out[Stride]   = XYZ.Y;
         Out[Stride*2] = XYZ.Z;
@@ -2341,10 +2809,10 @@
 }
 
 static
-cmsUInt8Number* PackXYZFloatFrom16(register _cmsTRANSFORM* Info,
-                                   register cmsUInt16Number wOut[],
-                                   register cmsUInt8Number* output,
-                                   register cmsUInt32Number Stride)
+cmsUInt8Number* PackXYZFloatFrom16(CMSREGISTER _cmsTRANSFORM* Info,
+                                   CMSREGISTER cmsUInt16Number wOut[],
+                                   CMSREGISTER cmsUInt8Number* output,
+                                   CMSREGISTER cmsUInt32Number Stride)
 {
     if (T_PLANAR(Info -> OutputFormat)) {
 
@@ -2352,6 +2820,8 @@
         cmsFloat32Number* Out = (cmsFloat32Number*) output;
         cmsXYZEncoded2Float(&XYZ, wOut);
 
+        Stride /= PixelSize(Info->OutputFormat);
+
         Out[0]        = (cmsFloat32Number) XYZ.X;
         Out[Stride]   = (cmsFloat32Number) XYZ.Y;
         Out[Stride*2] = (cmsFloat32Number) XYZ.Z;
@@ -2374,10 +2844,10 @@
 }
 
 static
-cmsUInt8Number* PackDoubleFrom16(register _cmsTRANSFORM* info,
-                                register cmsUInt16Number wOut[],
-                                register cmsUInt8Number* output,
-                                register cmsUInt32Number Stride)
+cmsUInt8Number* PackDoubleFrom16(CMSREGISTER _cmsTRANSFORM* info,
+                                CMSREGISTER cmsUInt16Number wOut[],
+                                CMSREGISTER cmsUInt8Number* output,
+                                CMSREGISTER cmsUInt32Number Stride)
 {
     cmsUInt32Number nChan      = T_CHANNELS(info -> OutputFormat);
     cmsUInt32Number DoSwap     = T_DOSWAP(info ->OutputFormat);
@@ -2391,6 +2861,8 @@
     cmsFloat64Number* swap1 = (cmsFloat64Number*) output;
     cmsUInt32Number i, start = 0;
 
+    Stride /= PixelSize(info->OutputFormat);
+
     if (ExtraFirst)
         start = Extra;
 
@@ -2425,10 +2897,10 @@
 
 
 static
-cmsUInt8Number* PackFloatFrom16(register _cmsTRANSFORM* info,
-                                register cmsUInt16Number wOut[],
-                                register cmsUInt8Number* output,
-                                register cmsUInt32Number Stride)
+cmsUInt8Number* PackFloatFrom16(CMSREGISTER _cmsTRANSFORM* info,
+                                CMSREGISTER cmsUInt16Number wOut[],
+                                CMSREGISTER cmsUInt8Number* output,
+                                CMSREGISTER cmsUInt32Number Stride)
 {
        cmsUInt32Number nChan      = T_CHANNELS(info->OutputFormat);
        cmsUInt32Number DoSwap     = T_DOSWAP(info->OutputFormat);
@@ -2442,6 +2914,8 @@
        cmsFloat32Number* swap1 = (cmsFloat32Number*)output;
        cmsUInt32Number i, start = 0;
 
+       Stride /= PixelSize(info->OutputFormat);
+
        if (ExtraFirst)
               start = Extra;
 
@@ -2495,6 +2969,8 @@
        cmsFloat64Number v = 0;
        cmsUInt32Number i, start = 0;
 
+       Stride /= PixelSize(info->OutputFormat);
+
        if (ExtraFirst)
               start = Extra;
 
@@ -2544,6 +3020,8 @@
        cmsFloat64Number* swap1 = (cmsFloat64Number*)output;
        cmsUInt32Number i, start = 0;
 
+       Stride /= PixelSize(info->OutputFormat);
+
        if (ExtraFirst)
               start = Extra;
 
@@ -2576,10 +3054,6 @@
 
 }
 
-
-
-
-
 static
 cmsUInt8Number* PackLabFloatFromFloat(_cmsTRANSFORM* Info,
                                       cmsFloat32Number wOut[],
@@ -2590,6 +3064,8 @@
 
     if (T_PLANAR(Info -> OutputFormat)) {
 
+        Stride /= PixelSize(Info->OutputFormat);
+
         Out[0]        = (cmsFloat32Number) (wOut[0] * 100.0);
         Out[Stride]   = (cmsFloat32Number) (wOut[1] * 255.0 - 128.0);
         Out[Stride*2] = (cmsFloat32Number) (wOut[2] * 255.0 - 128.0);
@@ -2618,6 +3094,8 @@
 
     if (T_PLANAR(Info -> OutputFormat)) {
 
+        Stride /= PixelSize(Info->OutputFormat);
+
         Out[0]        = (cmsFloat64Number) (wOut[0] * 100.0);
         Out[Stride]   = (cmsFloat64Number) (wOut[1] * 255.0 - 128.0);
         Out[Stride*2] = (cmsFloat64Number) (wOut[2] * 255.0 - 128.0);
@@ -2647,6 +3125,8 @@
 
     if (T_PLANAR(Info -> OutputFormat)) {
 
+        Stride /= PixelSize(Info->OutputFormat);
+
         Out[0]        = (cmsFloat32Number) (wOut[0] * MAX_ENCODEABLE_XYZ);
         Out[Stride]   = (cmsFloat32Number) (wOut[1] * MAX_ENCODEABLE_XYZ);
         Out[Stride*2] = (cmsFloat32Number) (wOut[2] * MAX_ENCODEABLE_XYZ);
@@ -2675,6 +3155,8 @@
 
     if (T_PLANAR(Info -> OutputFormat)) {
 
+        Stride /= PixelSize(Info->OutputFormat);
+
         Out[0]        = (cmsFloat64Number) (wOut[0] * MAX_ENCODEABLE_XYZ);
         Out[Stride]   = (cmsFloat64Number) (wOut[1] * MAX_ENCODEABLE_XYZ);
         Out[Stride*2] = (cmsFloat64Number) (wOut[2] * MAX_ENCODEABLE_XYZ);
@@ -2700,10 +3182,10 @@
 // Decodes an stream of half floats to wIn[] described by input format
 
 static
-cmsUInt8Number* UnrollHalfTo16(register _cmsTRANSFORM* info,
-                                register cmsUInt16Number wIn[],
-                                register cmsUInt8Number* accum,
-                                register cmsUInt32Number Stride)
+cmsUInt8Number* UnrollHalfTo16(CMSREGISTER _cmsTRANSFORM* info,
+                                CMSREGISTER cmsUInt16Number wIn[],
+                                CMSREGISTER cmsUInt8Number* accum,
+                                CMSREGISTER cmsUInt32Number Stride)
 {
 
     cmsUInt32Number nChan      = T_CHANNELS(info -> InputFormat);
@@ -2718,6 +3200,8 @@
     cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 655.35F : 65535.0F;
 
 
+    Stride /= PixelSize(info->OutputFormat);
+
     if (ExtraFirst)
             start = Extra;
 
@@ -2732,7 +3216,7 @@
 
         if (Reverse) v = maximum - v;
 
-        wIn[index] = _cmsQuickSaturateWord(v * maximum);
+        wIn[index] = _cmsQuickSaturateWord((cmsFloat64Number) v * maximum);
     }
 
 
@@ -2769,6 +3253,7 @@
     cmsUInt32Number i, start = 0;
     cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 100.0F : 1.0F;
 
+    Stride /= PixelSize(info->OutputFormat);
 
     if (ExtraFirst)
             start = Extra;
@@ -2803,10 +3288,10 @@
 
 
 static
-cmsUInt8Number* PackHalfFrom16(register _cmsTRANSFORM* info,
-                                register cmsUInt16Number wOut[],
-                                register cmsUInt8Number* output,
-                                register cmsUInt32Number Stride)
+cmsUInt8Number* PackHalfFrom16(CMSREGISTER _cmsTRANSFORM* info,
+                                CMSREGISTER cmsUInt16Number wOut[],
+                                CMSREGISTER cmsUInt8Number* output,
+                                CMSREGISTER cmsUInt32Number Stride)
 {
        cmsUInt32Number nChan      = T_CHANNELS(info->OutputFormat);
        cmsUInt32Number DoSwap     = T_DOSWAP(info->OutputFormat);
@@ -2820,6 +3305,8 @@
        cmsUInt16Number* swap1 = (cmsUInt16Number*)output;
        cmsUInt32Number i, start = 0;
 
+       Stride /= PixelSize(info->OutputFormat);
+
        if (ExtraFirst)
               start = Extra;
 
@@ -2871,6 +3358,8 @@
        cmsFloat32Number v = 0;
        cmsUInt32Number i, start = 0;
 
+       Stride /= PixelSize(info->OutputFormat);
+
        if (ExtraFirst)
               start = Extra;
 
@@ -2949,12 +3438,12 @@
     { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1),                 ANYSPACE,  Unroll4BytesSwap},
     { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), ANYSPACE,  Unroll4BytesSwapSwapFirst},
 
-    { BYTES_SH(1)|PLANAR_SH(1), ANYFLAVOR|ANYSWAPFIRST|
+    { BYTES_SH(1)|PLANAR_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYPREMUL|
                                    ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollPlanarBytes},
 
-    { BYTES_SH(1),    ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|
+    { BYTES_SH(1),    ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYPREMUL|
                                            ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollChunkyBytes},
-
+    
     { CHANNELS_SH(1)|BYTES_SH(2),                              ANYSPACE,  Unroll1Word},
     { CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1),                 ANYSPACE,  Unroll1WordReversed},
     { CHANNELS_SH(1)|BYTES_SH(2)|EXTRA_SH(3),                  ANYSPACE,  Unroll1WordSkip3},
@@ -2974,6 +3463,10 @@
 
     { BYTES_SH(2)|PLANAR_SH(1),  ANYFLAVOR|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE,  UnrollPlanarWords},
     { BYTES_SH(2),  ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE,  UnrollAnyWords},
+
+    { BYTES_SH(2)|PLANAR_SH(1),  ANYFLAVOR|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE|PREMUL_SH(1),  UnrollPlanarWordsPremul},
+    { BYTES_SH(2),  ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE|PREMUL_SH(1),  UnrollAnyWordsPremul}
+
 };
 
 
@@ -2989,13 +3482,23 @@
     {     TYPE_XYZ_FLT,                                ANYPLANAR|ANYEXTRA,   UnrollXYZFloatToFloat},
 
     {     FLOAT_SH(1)|BYTES_SH(4), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|
-                                                      ANYCHANNELS|ANYSPACE,  UnrollFloatsToFloat},
+                                            ANYPREMUL|ANYCHANNELS|ANYSPACE,  UnrollFloatsToFloat},
 
     {     FLOAT_SH(1)|BYTES_SH(0), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|
-                                                        ANYCHANNELS|ANYSPACE,  UnrollDoublesToFloat},
+                                              ANYCHANNELS|ANYSPACE|ANYPREMUL, UnrollDoublesToFloat},
+
+    {     TYPE_LabV2_8,                                                   0,  UnrollLabV2_8ToFloat },
+    {     TYPE_ALabV2_8,                                                  0,  UnrollALabV2_8ToFloat },
+    {     TYPE_LabV2_16,                                                  0,  UnrollLabV2_16ToFloat },
+
+    {     BYTES_SH(1),              ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|
+                                                        ANYCHANNELS|ANYSPACE, Unroll8ToFloat},
+
+    {     BYTES_SH(2),              ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|
+                                                        ANYCHANNELS|ANYSPACE, Unroll16ToFloat},
 #ifndef CMS_NO_HALF_SUPPORT 
     {     FLOAT_SH(1)|BYTES_SH(2), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|
-                                                        ANYCHANNELS|ANYSPACE,  UnrollHalfToFloat},
+                                                        ANYCHANNELS|ANYSPACE, UnrollHalfToFloat},
 #endif
 };
 
@@ -3089,16 +3592,20 @@
                                                                    ANYSPACE,  Pack3BytesAndSkip1SwapSwapFirst},
     { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|EXTRA_SH(1),         ANYSPACE,  Pack3BytesAndSkip1Swap},
     { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1),                     ANYSPACE,  Pack3BytesSwap},
-    { CHANNELS_SH(6)|BYTES_SH(1),                                  ANYSPACE,  Pack6Bytes},
-    { CHANNELS_SH(6)|BYTES_SH(1)|DOSWAP_SH(1),                     ANYSPACE,  Pack6BytesSwap},
     { CHANNELS_SH(4)|BYTES_SH(1),                                  ANYSPACE,  Pack4Bytes},
     { CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1),                     ANYSPACE,  Pack4BytesReverse},
     { CHANNELS_SH(4)|BYTES_SH(1)|SWAPFIRST_SH(1),                  ANYSPACE,  Pack4BytesSwapFirst},
     { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1),                     ANYSPACE,  Pack4BytesSwap},
     { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1),     ANYSPACE,  Pack4BytesSwapSwapFirst},
+    { CHANNELS_SH(6)|BYTES_SH(1),                                  ANYSPACE,  Pack6Bytes},
+    { CHANNELS_SH(6)|BYTES_SH(1)|DOSWAP_SH(1),                     ANYSPACE,  Pack6BytesSwap},
 
-    { BYTES_SH(1),                 ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackAnyBytes},
-    { BYTES_SH(1)|PLANAR_SH(1),    ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackPlanarBytes},
+    { BYTES_SH(1),    ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|
+                                                          ANYSPACE|ANYPREMUL, PackChunkyBytes},
+
+    { BYTES_SH(1)|PLANAR_SH(1),    ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|
+                                              ANYCHANNELS|ANYSPACE|ANYPREMUL, PackPlanarBytes},
+        
 
     { CHANNELS_SH(1)|BYTES_SH(2),                                  ANYSPACE,  Pack1Word},
     { CHANNELS_SH(1)|BYTES_SH(2)|EXTRA_SH(1),                      ANYSPACE,  Pack1WordSkip1},
@@ -3123,9 +3630,11 @@
     { CHANNELS_SH(6)|BYTES_SH(2),                                  ANYSPACE,  Pack6Words},
     { CHANNELS_SH(6)|BYTES_SH(2)|DOSWAP_SH(1),                     ANYSPACE,  Pack6WordsSwap},
 
-    { BYTES_SH(2)|PLANAR_SH(1),     ANYFLAVOR|ANYENDIAN|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackPlanarWords},
-    { BYTES_SH(2),                  ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackAnyWords}
-
+    { BYTES_SH(2),                  ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|
+                                     ANYEXTRA|ANYCHANNELS|ANYSPACE|ANYPREMUL, PackChunkyWords},
+    { BYTES_SH(2)|PLANAR_SH(1),     ANYFLAVOR|ANYENDIAN|ANYSWAP|ANYEXTRA|
+                                     ANYCHANNELS|ANYSPACE|ANYPREMUL,          PackPlanarWords}
+    
 };
 
 
@@ -3288,14 +3797,19 @@
     return TRUE;
 }
 
-cmsFormatter _cmsGetFormatter(cmsContext ContextID,
-                             cmsUInt32Number Type,         // Specific type, i.e. TYPE_RGB_8
-                             cmsFormatterDirection Dir,
-                             cmsUInt32Number dwFlags)
+cmsFormatter CMSEXPORT _cmsGetFormatter(cmsContext ContextID,
+                                        cmsUInt32Number Type,         // Specific type, i.e. TYPE_RGB_8
+                                        cmsFormatterDirection Dir,
+                                        cmsUInt32Number dwFlags)
 {
     _cmsFormattersPluginChunkType* ctx = ( _cmsFormattersPluginChunkType*) _cmsContextGetClientChunk(ContextID, FormattersPlugin);
     cmsFormattersFactoryList* f;
 
+    if (T_CHANNELS(Type) == 0) {
+        static const cmsFormatter nullFormatter = { 0 };
+        return nullFormatter;
+    }
+
     for (f =ctx->FactoryList; f != NULL; f = f ->Next) {
 
         cmsFormatter fn = f ->Factory(Type, Dir, dwFlags);
@@ -3330,9 +3844,12 @@
 
     cmsColorSpaceSignature ColorSpace      = cmsGetColorSpace(hProfile);
     cmsUInt32Number        ColorSpaceBits  = (cmsUInt32Number) _cmsLCMScolorSpace(ColorSpace);
-    cmsUInt32Number        nOutputChans    = cmsChannelsOf(ColorSpace);
+    cmsInt32Number         nOutputChans    = cmsChannelsOfColorSpace(ColorSpace);
     cmsUInt32Number        Float           = lIsFloat ? 1U : 0;
 
+    // Unsupported color space?
+    if (nOutputChans < 0) return 0;
+
     // Create a fake formatter for result
     return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans);
 }
diff --git a/third_party/lcms/src/cmspcs.c b/third_party/lcms/src/cmspcs.c
index ea70484..e11f87c 100644
--- a/third_party/lcms/src/cmspcs.c
+++ b/third_party/lcms/src/cmspcs.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -656,7 +656,7 @@
 
 // This function returns a number of gridpoints to be used as LUT table. It assumes same number
 // of gripdpoints in all dimensions. Flags may override the choice.
-cmsUInt32Number _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags)
+cmsUInt32Number CMSEXPORT _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags)
 {
     cmsUInt32Number nChannels;
 
@@ -874,7 +874,7 @@
 }
 
 
-cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace)
+cmsInt32Number CMSEXPORT cmsChannelsOfColorSpace(cmsColorSpaceSignature ColorSpace)
 {
     switch (ColorSpace) {
 
@@ -935,6 +935,16 @@
     case cmsSigMCHFData:
     case cmsSig15colorData: return 15;
 
-    default: return 3;
+    default: return -1;
     }
 }
+
+/**
+* DEPRECATED: Provided for compatibility only
+*/
+cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace)
+{
+    int n = cmsChannelsOfColorSpace(ColorSpace);
+    if (n < 0) return 3;
+    return (cmsUInt32Number)n;
+}
\ No newline at end of file
diff --git a/third_party/lcms/src/cmsplugin.c b/third_party/lcms/src/cmsplugin.c
index d54b4d1..3876506 100644
--- a/third_party/lcms/src/cmsplugin.c
+++ b/third_party/lcms/src/cmsplugin.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -33,7 +33,7 @@
 
 //      Little-Endian to Big-Endian
 
-// Adjust a word value after being readed/ before being written from/to an ICC profile
+// Adjust a word value after being read/ before being written from/to an ICC profile
 cmsUInt16Number CMSEXPORT  _cmsAdjustEndianess16(cmsUInt16Number Word)
 {
 #ifndef CMS_USE_BIG_ENDIAN
@@ -99,8 +99,8 @@
     _cmsAssert(Result != NULL);
 
 #  ifdef CMS_DONT_USE_INT64
-    (*Result)[0] = QWord[0];
-    (*Result)[1] = QWord[1];
+    (*Result)[0] = (*QWord)[0];
+    (*Result)[1] = (*QWord)[1];
 #  else
     *Result = *QWord;
 #  endif
@@ -168,18 +168,21 @@
 
 cmsBool CMSEXPORT  _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n)
 {
-    cmsUInt32Number tmp;
+    union typeConverter {
+        cmsUInt32Number integer;
+        cmsFloat32Number floating_point;
+    } tmp;
 
     _cmsAssert(io != NULL);
 
-    if (io->Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
+    if (io->Read(io, &tmp.integer, sizeof(cmsUInt32Number), 1) != 1)
         return FALSE;
 
     if (n != NULL) {
 
-        tmp = _cmsAdjustEndianess32(tmp);
-        *n = *(cmsFloat32Number*)(void*)&tmp;
-        
+        tmp.integer = _cmsAdjustEndianess32(tmp.integer);
+        *n = tmp.floating_point;
+
         // Safeguard which covers against absurd values
         if (*n > 1E+20 || *n < -1E+20) return FALSE;
 
@@ -187,6 +190,8 @@
            return TRUE;
         #elif defined (__BORLANDC__)
            return TRUE;
+        #elif !defined(_MSC_VER) && (defined(__STDC_VERSION__) && __STDC_VERSION__ < 199901L)
+           return TRUE;
         #else
 
            // fpclassify() required by C99 (only provided by MSVC >= 1800, VS2013 onwards)
@@ -303,13 +308,14 @@
 
 cmsBool CMSEXPORT  _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n)
 {
-    cmsUInt32Number tmp;
+    union typeConverter {
+        cmsUInt32Number integer;
+        cmsFloat32Number floating_point;
+    } tmp;
 
-    _cmsAssert(io != NULL);
-
-    tmp = *(cmsUInt32Number*) (void*) &n;
-    tmp = _cmsAdjustEndianess32(tmp);
-    if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
+    tmp.floating_point = n;
+    tmp.integer = _cmsAdjustEndianess32(tmp.integer);
+    if (io -> Write(io, sizeof(cmsUInt32Number), &tmp.integer) != 1)
             return FALSE;
 
     return TRUE;
@@ -496,6 +502,7 @@
     int len;
     cmsUInt8Number Buffer[2048];
     cmsBool rc;
+    cmsUInt8Number* ptr;
 
     _cmsAssert(io != NULL);
     _cmsAssert(frm != NULL);
@@ -508,6 +515,13 @@
         return FALSE;   // Truncated, which is a fatal error for us
     }
 
+    // setlocale may be active, no commas are needed in PS generator
+    // and PS generator is our only client
+    for (ptr = Buffer; *ptr; ptr++)
+    {
+        if (*ptr == ',') *ptr = '.';
+    }
+
     rc = io ->Write(io, (cmsUInt32Number) len, Buffer);
 
     va_end(args);
@@ -611,6 +625,10 @@
                     if (!_cmsRegisterMutexPlugin(id, Plugin)) return FALSE;
                     break;
 
+                case cmsPluginParalellizationSig:
+                    if (!_cmsRegisterParallelizationPlugin(id, Plugin)) return FALSE;
+                    break;
+
                 default:
                     cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type);
                     return FALSE;
@@ -633,24 +651,25 @@
 // pointers structure. All global vars are referenced here.
 static struct _cmsContext_struct globalContext = {
 
-    NULL,                              // Not in the linked list
-    NULL,                              // No suballocator
-    {
-        NULL,                          //  UserPtr,            
-        &_cmsLogErrorChunk,            //  Logger,
-        &_cmsAlarmCodesChunk,          //  AlarmCodes,
-        &_cmsAdaptationStateChunk,     //  AdaptationState, 
-        &_cmsMemPluginChunk,           //  MemPlugin,
-        &_cmsInterpPluginChunk,        //  InterpPlugin,
-        &_cmsCurvesPluginChunk,        //  CurvesPlugin,
-        &_cmsFormattersPluginChunk,    //  FormattersPlugin,
-        &_cmsTagTypePluginChunk,       //  TagTypePlugin,
-        &_cmsTagPluginChunk,           //  TagPlugin,
-        &_cmsIntentsPluginChunk,       //  IntentPlugin,
-        &_cmsMPETypePluginChunk,       //  MPEPlugin,
-        &_cmsOptimizationPluginChunk,  //  OptimizationPlugin,
-        &_cmsTransformPluginChunk,     //  TransformPlugin,
-        &_cmsMutexPluginChunk          //  MutexPlugin
+    NULL,                                // Not in the linked list
+    NULL,                                // No suballocator
+    {                                    
+        NULL,                            //  UserPtr,            
+        &_cmsLogErrorChunk,              //  Logger,
+        &_cmsAlarmCodesChunk,            //  AlarmCodes,
+        &_cmsAdaptationStateChunk,       //  AdaptationState, 
+        &_cmsMemPluginChunk,             //  MemPlugin,
+        &_cmsInterpPluginChunk,          //  InterpPlugin,
+        &_cmsCurvesPluginChunk,          //  CurvesPlugin,
+        &_cmsFormattersPluginChunk,      //  FormattersPlugin,
+        &_cmsTagTypePluginChunk,         //  TagTypePlugin,
+        &_cmsTagPluginChunk,             //  TagPlugin,
+        &_cmsIntentsPluginChunk,         //  IntentPlugin,
+        &_cmsMPETypePluginChunk,         //  MPEPlugin,
+        &_cmsOptimizationPluginChunk,    //  OptimizationPlugin,
+        &_cmsTransformPluginChunk,       //  TransformPlugin,
+        &_cmsMutexPluginChunk,           //  MutexPlugin,
+        &_cmsParallelizationPluginChunk  //  ParallelizationPlugin
     },
     
     { NULL, NULL, NULL, NULL, NULL, NULL } // The default memory allocator is not used for context 0
@@ -661,27 +680,81 @@
 static _cmsMutex _cmsContextPoolHeadMutex = CMS_MUTEX_INITIALIZER;
 static struct _cmsContext_struct* _cmsContextPoolHead = NULL;
 
+
+// Make sure context is initialized (needed on windows)
+static
+cmsBool InitContextMutex(void)
+{
+    // See the comments regarding locking in lcms2_internal.h
+    // for an explanation of why we need the following code.
+#ifndef CMS_NO_PTHREADS
+#ifdef CMS_IS_WINDOWS_
+#ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
+
+    static cmsBool already_initialized = FALSE;
+
+    if (!already_initialized)
+    {
+        static HANDLE _cmsWindowsInitMutex = NULL;
+        static volatile HANDLE* mutex = &_cmsWindowsInitMutex;
+
+        if (*mutex == NULL)
+        {
+            HANDLE p = CreateMutex(NULL, FALSE, NULL);
+            if (p && InterlockedCompareExchangePointer((void**)mutex, (void*)p, NULL) != NULL)
+                CloseHandle(p);
+        }
+        if (*mutex == NULL || WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED)
+        {
+            cmsSignalError(0, cmsERROR_INTERNAL, "Mutex lock failed");
+            return FALSE;
+        }
+        if (((void**)&_cmsContextPoolHeadMutex)[0] == NULL)
+            InitializeCriticalSection(&_cmsContextPoolHeadMutex);
+        if (*mutex == NULL || !ReleaseMutex(*mutex))
+        {
+            cmsSignalError(0, cmsERROR_INTERNAL, "Mutex unlock failed");
+            return FALSE;
+        }
+        already_initialized = TRUE;
+    }
+#endif
+#endif
+#endif
+
+    return TRUE;
+}
+
+
+
 // Internal, get associated pointer, with guessing. Never returns NULL.
 struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID)
 {
     struct _cmsContext_struct* id = (struct _cmsContext_struct*) ContextID;
     struct _cmsContext_struct* ctx;
 
-
     // On 0, use global settings
     if (id == NULL) 
         return &globalContext;
 
+    InitContextMutex();
+
     // Search
+    _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
+
     for (ctx = _cmsContextPoolHead;
          ctx != NULL;
          ctx = ctx ->Next) {
 
             // Found it?
-            if (id == ctx)
-                return ctx; // New-style context, 
+        if (id == ctx)
+        {
+            _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
+            return ctx; // New-style context
+        }
     }
 
+    _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
     return &globalContext;
 }
 
@@ -722,7 +795,7 @@
 // many different plug-ins simultaneously, then there is no way to 
 // identify which plug-in to unregister.
 void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID)
-{
+{    
     _cmsRegisterMemHandlerPlugin(ContextID, NULL);
     _cmsRegisterInterpPlugin(ContextID, NULL);
     _cmsRegisterTagTypePlugin(ContextID, NULL);
@@ -734,6 +807,8 @@
     _cmsRegisterOptimizationPlugin(ContextID, NULL);
     _cmsRegisterTransformPlugin(ContextID, NULL);    
     _cmsRegisterMutexPlugin(ContextID, NULL);
+    _cmsRegisterParallelizationPlugin(ContextID, NULL);
+
 }
 
 
@@ -768,29 +843,7 @@
     struct _cmsContext_struct* ctx;
     struct _cmsContext_struct  fakeContext;
         
-    // See the comments regarding locking in lcms2_internal.h
-    // for an explanation of why we need the following code.
-#ifdef CMS_IS_WINDOWS_
-#ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
-    {
-        static HANDLE _cmsWindowsInitMutex = NULL;
-        static volatile HANDLE* mutex = &_cmsWindowsInitMutex;
-
-        if (*mutex == NULL)
-        {
-            HANDLE p = CreateMutex(NULL, FALSE, NULL);
-            if (p && InterlockedCompareExchangePointer((void **)mutex, (void*)p, NULL) != NULL)
-                CloseHandle(p);
-        }
-        if (*mutex == NULL || WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED)
-            return NULL;
-        if (((void **)&_cmsContextPoolHeadMutex)[0] == NULL)
-            InitializeCriticalSection(&_cmsContextPoolHeadMutex);
-        if (*mutex == NULL || !ReleaseMutex(*mutex))
-            return NULL;
-    }
-#endif
-#endif
+    if (!InitContextMutex()) return NULL;
 
     _cmsInstallAllocFunctions(_cmsFindMemoryPlugin(Plugin), &fakeContext.DefaultMemoryManager);
     
@@ -839,6 +892,7 @@
     _cmsAllocOptimizationPluginChunk(ctx, NULL);
     _cmsAllocTransformPluginChunk(ctx, NULL);
     _cmsAllocMutexPluginChunk(ctx, NULL);
+    _cmsAllocParallelizationPluginChunk(ctx, NULL);
 
     // Setup the plug-ins
     if (!cmsPluginTHR(ctx, Plugin)) {
@@ -866,6 +920,8 @@
     if (ctx == NULL)   
         return NULL;     // Something very wrong happened
 
+    if (!InitContextMutex()) return NULL;
+
     // Setup default memory allocators
     memcpy(&ctx->DefaultMemoryManager, &src->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
 
@@ -900,6 +956,7 @@
     _cmsAllocOptimizationPluginChunk(ctx, src);
     _cmsAllocTransformPluginChunk(ctx, src);
     _cmsAllocMutexPluginChunk(ctx, src);
+    _cmsAllocParallelizationPluginChunk(ctx, src);
 
     // Make sure no one failed
     for (i=Logger; i < MemoryClientMax; i++) {
@@ -914,36 +971,27 @@
 }
 
 
-/*
-static
-struct _cmsContext_struct* FindPrev(struct _cmsContext_struct* id)
-{
-    struct _cmsContext_struct* prev;
-
-    // Search for previous
-    for (prev = _cmsContextPoolHead; 
-             prev != NULL;
-             prev = prev ->Next)
-    {
-        if (prev ->Next == id)
-            return prev;
-    }
-
-    return NULL;  // List is empty or only one element!
-}
-*/
-
 // Frees any resources associated with the given context, 
 // and destroys the context placeholder. 
 // The ContextID can no longer be used in any THR operation.  
 void CMSEXPORT cmsDeleteContext(cmsContext ContextID)
 {
-    if (ContextID != NULL) {
+    if (ContextID == NULL) {
+
+        cmsUnregisterPlugins();
+        if (globalContext.MemPool != NULL)
+            _cmsSubAllocDestroy(globalContext.MemPool);
+        globalContext.MemPool = NULL;
+    }
+    else {
 
         struct _cmsContext_struct* ctx = (struct _cmsContext_struct*) ContextID;              
         struct _cmsContext_struct  fakeContext;  
         struct _cmsContext_struct* prev;
 
+
+        InitContextMutex();
+
         memcpy(&fakeContext.DefaultMemoryManager, &ctx->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
 
         fakeContext.chunks[UserPtr]     = ctx ->chunks[UserPtr];
@@ -990,3 +1038,32 @@
 }
 
 
+// Use context mutex to provide thread-safe time
+cmsBool _cmsGetTime(struct tm* ptr_time)
+{
+    struct tm* t;
+#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
+    struct tm tm;
+#endif
+
+    time_t now = time(NULL);
+
+#ifdef HAVE_GMTIME_R
+    t = gmtime_r(&now, &tm);
+#elif defined(HAVE_GMTIME_S)
+    t = gmtime_s(&tm, &now) == 0 ? &tm : NULL;
+#else
+    if (!InitContextMutex()) return FALSE;
+
+    _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
+    t = gmtime(&now);
+    _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
+#endif
+
+    if (t == NULL) 
+        return FALSE;
+    else {
+        *ptr_time = *t;
+        return TRUE;
+    }
+}
diff --git a/third_party/lcms/src/cmsps2.c b/third_party/lcms/src/cmsps2.c
index 5802a14..9aeea36 100644
--- a/third_party/lcms/src/cmsps2.c
+++ b/third_party/lcms/src/cmsps2.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -78,8 +78,8 @@
    Matrix-shaper based
    -------------------
 
-   This is implemented both with /CIEBasedABC or /CIEBasedDEF on dependig
-   of profile implementation. Since here there are no interpolation tables, I do
+   This is implemented both with /CIEBasedABC or /CIEBasedDEF depending on the
+   profile implementation. Since here there are no interpolation tables, I do
    the conversion directly to XYZ
 
 
@@ -295,21 +295,7 @@
 }
 
 
-// Convert to byte (using ICC2 notation)
-/*
-static
-cmsUInt8Number L2Byte(cmsUInt16Number w)
-{
-    int ww = w + 0x0080;
-
-    if (ww > 0xFFFF) return 0xFF;
-
-    return (cmsUInt8Number) ((cmsUInt16Number) (ww >> 8) & 0xFF);
-}
-*/
-
 // Write a cooked byte
-
 static
 void WriteByte(cmsIOHANDLER* m, cmsUInt8Number b)
 {
@@ -326,7 +312,8 @@
 // ----------------------------------------------------------------- PostScript generation
 
 
-// Removes offending Carriage returns
+// Removes offending carriage returns
+
 static
 char* RemoveCR(const char* txt)
 {
@@ -424,21 +411,6 @@
 //        = Yn*( L* / 116) / 7.787      if (L*) < 6 / 29
 //
 
-/*
-static
-void EmitL2Y(cmsIOHANDLER* m)
-{
-    _cmsIOPrintf(m,
-            "{ "
-                "100 mul 16 add 116 div "               // (L * 100 + 16) / 116
-                 "dup 6 29 div ge "                     // >= 6 / 29 ?
-                 "{ dup dup mul mul } "                 // yes, ^3 and done
-                 "{ 4 29 div sub 108 841 div mul } "    // no, slope limiting
-            "ifelse } bind ");
-}
-*/
-
-
 // Lab -> XYZ, see the discussion above
 
 static
@@ -459,12 +431,28 @@
     _cmsIOPrintf(m, "]\n");
 }
 
+static
+void EmitSafeGuardBegin(cmsIOHANDLER* m, const char* name)
+{
+    _cmsIOPrintf(m, "%%LCMS2: Save previous definition of %s on the operand stack\n", name);
+    _cmsIOPrintf(m, "currentdict /%s known { /%s load } { null } ifelse\n", name, name);
+}
 
+static
+void EmitSafeGuardEnd(cmsIOHANDLER* m, const char* name, int depth)
+{
+    _cmsIOPrintf(m, "%%LCMS2: Restore previous definition of %s\n", name);
+    if (depth > 1) {
+        // cycle topmost items on the stack to bring the previous definition to the front
+        _cmsIOPrintf(m, "%d -1 roll ", depth);
+    }
+    _cmsIOPrintf(m, "dup null eq { pop currentdict /%s undef } { /%s exch def } ifelse\n", name, name);
+}
 
 // Outputs a table of words. It does use 16 bits
 
 static
-void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table)
+void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table, const char* name)
 {
     cmsUInt32Number i;
     cmsFloat64Number gamma;
@@ -479,28 +467,33 @@
     // Check if is really an exponential. If so, emit "exp"
     gamma = cmsEstimateGamma(Table, 0.001);
      if (gamma > 0) {
-            _cmsIOPrintf(m, "{ %g exp } bind ", gamma);
+            _cmsIOPrintf(m, "/%s { %g exp } bind def\n", name, gamma);
             return;
      }
 
-    _cmsIOPrintf(m, "{ ");
+    EmitSafeGuardBegin(m, "lcms2gammatable");
+    _cmsIOPrintf(m, "/lcms2gammatable [");
+
+    for (i=0; i < Table->nEntries; i++) {
+    if (i % 10 == 0)
+            _cmsIOPrintf(m, "\n  ");
+        _cmsIOPrintf(m, "%d ", Table->Table16[i]);
+    }
+
+    _cmsIOPrintf(m, "] def\n");
+
+
+    // Emit interpolation code
+
+    // PostScript code                            Stack
+    // ===============                            ========================
+                                                  // v
+    _cmsIOPrintf(m, "/%s {\n  ", name);
 
     // Bounds check
     EmitRangeCheck(m);
 
-    // Emit intepolation code
-
-    // PostScript code                      Stack
-    // ===============                      ========================
-                                            // v
-    _cmsIOPrintf(m, " [");
-
-    for (i=0; i < Table->nEntries; i++) {
-        _cmsIOPrintf(m, "%d ", Table->Table16[i]);
-    }
-
-    _cmsIOPrintf(m, "] ");                        // v tab
-
+    _cmsIOPrintf(m, "\n  //lcms2gammatable ");    // v tab
     _cmsIOPrintf(m, "dup ");                      // v tab tab
     _cmsIOPrintf(m, "length 1 sub ");             // v tab dom
     _cmsIOPrintf(m, "3 -1 roll ");                // tab dom v
@@ -512,7 +505,7 @@
     _cmsIOPrintf(m, "ceiling cvi ");              // tab val2 cell0 cell1
     _cmsIOPrintf(m, "3 index ");                  // tab val2 cell0 cell1 tab
     _cmsIOPrintf(m, "exch ");                     // tab val2 cell0 tab cell1
-    _cmsIOPrintf(m, "get ");                      // tab val2 cell0 y1
+    _cmsIOPrintf(m, "get\n  ");                   // tab val2 cell0 y1
     _cmsIOPrintf(m, "4 -1 roll ");                // val2 cell0 y1 tab
     _cmsIOPrintf(m, "3 -1 roll ");                // val2 y1 tab cell0
     _cmsIOPrintf(m, "get ");                      // val2 y1 y0
@@ -525,47 +518,50 @@
     _cmsIOPrintf(m, "sub ");                      // y0 (y1-y0) rest
     _cmsIOPrintf(m, "mul ");                      // y0 t1
     _cmsIOPrintf(m, "add ");                      // y
-    _cmsIOPrintf(m, "65535 div ");                // result
+    _cmsIOPrintf(m, "65535 div\n");               // result
 
-    _cmsIOPrintf(m, " } bind ");
+    _cmsIOPrintf(m, "} bind def\n");
+
+    EmitSafeGuardEnd(m, "lcms2gammatable", 1);
 }
 
 
 // Compare gamma table
 
 static
-cmsBool GammaTableEquals(cmsUInt16Number* g1, cmsUInt16Number* g2, cmsUInt32Number nEntries)
+cmsBool GammaTableEquals(cmsUInt16Number* g1, cmsUInt16Number* g2, cmsUInt32Number nG1, cmsUInt32Number nG2)
 {
-    return memcmp(g1, g2, nEntries* sizeof(cmsUInt16Number)) == 0;
+    if (nG1 != nG2) return FALSE;
+    return memcmp(g1, g2, nG1 * sizeof(cmsUInt16Number)) == 0;
 }
 
 
 // Does write a set of gamma curves
 
 static
-void EmitNGamma(cmsIOHANDLER* m, cmsUInt32Number n, cmsToneCurve* g[])
+void EmitNGamma(cmsIOHANDLER* m, cmsUInt32Number n, cmsToneCurve* g[], const char* nameprefix)
 {
     cmsUInt32Number i;
+    static char buffer[2048];
 
     for( i=0; i < n; i++ )
     {
         if (g[i] == NULL) return; // Error
 
-        if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i]->nEntries)) {
+        if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i-1]->nEntries, g[i]->nEntries)) {
 
-            _cmsIOPrintf(m, "dup ");
+            _cmsIOPrintf(m, "/%s%d /%s%d load def\n", nameprefix, i, nameprefix, i-1);
         }
         else {
-            Emit1Gamma(m, g[i]);
+            snprintf(buffer, sizeof(buffer), "%s%d", nameprefix, (int) i);
+        buffer[sizeof(buffer)-1] = '\0';
+            Emit1Gamma(m, g[i], buffer);
         }
     }
 
 }
 
 
-
-
-
 // Following code dumps a LUT onto memory stream
 
 
@@ -582,7 +578,7 @@
 //  component. -1 is used to mark beginning of whole block.
 
 static
-int OutputValueSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
+int OutputValueSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
 {
     cmsPsSamplerCargo* sc = (cmsPsSamplerCargo*) Cargo;
     cmsUInt32Number i;
@@ -662,11 +658,11 @@
 
 static
 void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj,
-                                             const char* PostMaj,
-                                             const char* PreMin,
-                                             const char* PostMin,
-                                             int FixWhite,
-                                             cmsColorSpaceSignature ColorSpace)
+                                               const char* PostMaj,
+                                               const char* PreMin,
+                                               const char* PostMin,
+                                               int FixWhite,
+                                               cmsColorSpaceSignature ColorSpace)
 {
     cmsUInt32Number i;
     cmsPsSamplerCargo sc;
@@ -708,11 +704,11 @@
     _cmsIOPrintf(m, "[ /CIEBasedA\n");
     _cmsIOPrintf(m, "  <<\n");
 
-    _cmsIOPrintf(m, "/DecodeA ");
+    EmitSafeGuardBegin(m, "lcms2gammaproc");
+    Emit1Gamma(m, Curve, "lcms2gammaproc");
 
-    Emit1Gamma(m, Curve);
-
-    _cmsIOPrintf(m, " \n");
+    _cmsIOPrintf(m, "/DecodeA /lcms2gammaproc load\n");
+    EmitSafeGuardEnd(m, "lcms2gammaproc", 3);
 
     _cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n");
     _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
@@ -736,11 +732,19 @@
 
     _cmsIOPrintf(m, "[ /CIEBasedABC\n");
     _cmsIOPrintf(m, "<<\n");
-    _cmsIOPrintf(m, "/DecodeABC [ ");
 
-    EmitNGamma(m, 3, CurveSet);
-
+    EmitSafeGuardBegin(m, "lcms2gammaproc0");
+    EmitSafeGuardBegin(m, "lcms2gammaproc1");
+    EmitSafeGuardBegin(m, "lcms2gammaproc2");
+    EmitNGamma(m, 3, CurveSet, "lcms2gammaproc");
+    _cmsIOPrintf(m, "/DecodeABC [\n");
+    _cmsIOPrintf(m, "   /lcms2gammaproc0 load\n");
+    _cmsIOPrintf(m, "   /lcms2gammaproc1 load\n");
+    _cmsIOPrintf(m, "   /lcms2gammaproc2 load\n");
     _cmsIOPrintf(m, "]\n");
+    EmitSafeGuardEnd(m, "lcms2gammaproc2", 3);
+    EmitSafeGuardEnd(m, "lcms2gammaproc1", 3);
+    EmitSafeGuardEnd(m, "lcms2gammaproc0", 3);
 
     _cmsIOPrintf(m, "/MatrixABC [ " );
 
@@ -772,28 +776,31 @@
 {
     const char* PreMaj;
     const char* PostMaj;
-    const char* PreMin, *PostMin;
+    const char* PreMin, * PostMin;
     cmsStage* mpe;
+    int i, numchans;
+    static char buffer[2048];
 
-    mpe = Pipeline ->Elements;
+    mpe = Pipeline->Elements;
 
     switch (cmsStageInputChannels(mpe)) {
     case 3:
+        _cmsIOPrintf(m, "[ /CIEBasedDEF\n");
+        PreMaj = "<";
+        PostMaj = ">\n";
+        PreMin = PostMin = "";
+        break;
 
-            _cmsIOPrintf(m, "[ /CIEBasedDEF\n");
-            PreMaj ="<";
-            PostMaj= ">\n";
-            PreMin = PostMin = "";
-            break;
     case 4:
-            _cmsIOPrintf(m, "[ /CIEBasedDEFG\n");
-            PreMaj = "[";
-            PostMaj = "]\n";
-            PreMin = "<";
-            PostMin = ">\n";
-            break;
+        _cmsIOPrintf(m, "[ /CIEBasedDEFG\n");
+        PreMaj = "[";
+        PostMaj = "]\n";
+        PreMin = "<";
+        PostMin = ">\n";
+        break;
+
     default:
-            return 0;
+        return 0;
 
     }
 
@@ -801,18 +808,34 @@
 
     if (cmsStageType(mpe) == cmsSigCurveSetElemType) {
 
-        _cmsIOPrintf(m, "/DecodeDEF [ ");
-        EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe));
+        numchans = (int) cmsStageOutputChannels(mpe);
+        for (i = 0; i < numchans; ++i) {
+            snprintf(buffer, sizeof(buffer), "lcms2gammaproc%d", i);
+            buffer[sizeof(buffer) - 1] = '\0';
+            EmitSafeGuardBegin(m, buffer);
+        }
+        EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe), "lcms2gammaproc");
+        _cmsIOPrintf(m, "/DecodeDEF [\n");
+        for (i = 0; i < numchans; ++i) {
+            snprintf(buffer, sizeof(buffer), "  /lcms2gammaproc%d load\n", i);
+            buffer[sizeof(buffer) - 1] = '\0';
+            _cmsIOPrintf(m, buffer);
+        }
         _cmsIOPrintf(m, "]\n");
+        for (i = numchans - 1; i >= 0; --i) {
+            snprintf(buffer, sizeof(buffer), "lcms2gammaproc%d", i);
+            buffer[sizeof(buffer) - 1] = '\0';
+            EmitSafeGuardEnd(m, buffer, 3);
+        }
 
-        mpe = mpe ->Next;
+        mpe = mpe->Next;
     }
 
     if (cmsStageType(mpe) == cmsSigCLutElemType) {
 
-            _cmsIOPrintf(m, "/Table ");
-            WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature) 0);
-            _cmsIOPrintf(m, "]\n");
+        _cmsIOPrintf(m, "/Table ");
+        WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature)0);
+        _cmsIOPrintf(m, "]\n");
     }
 
     EmitLab2XYZ(m);
@@ -923,7 +946,7 @@
 
     default:
 
-        cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels);
+        cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels are supported for CSA. This profile has %d channels.", nChannels);
         return 0;
     }
 
@@ -982,7 +1005,7 @@
             return 0;
         }
 
-        return rc;
+    return rc;
 }
 
 
@@ -1239,8 +1262,6 @@
                     "exch pop exch pop exch pop exch pop } bind\n]\n");
 
         }
-
-
 }
 
 
@@ -1270,7 +1291,7 @@
 // Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces
 // I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted
 // space on 3D CLUT, but since space seems not to be a problem here, 33 points
-// would give a reasonable accurancy. Note also that CRD tables must operate in
+// would give a reasonable accuracy. Note also that CRD tables must operate in
 // 8 bits.
 
 static
@@ -1414,7 +1435,7 @@
     cmsUInt32Number i, nColors, nColorant;
     cmsUInt32Number OutputFormat;
     char ColorName[cmsMAX_PATH];
-    char Colorant[128];
+    char Colorant[512];
     cmsNAMEDCOLORLIST* NamedColorList;
 
 
diff --git a/third_party/lcms/src/cmssamp.c b/third_party/lcms/src/cmssamp.c
index 1fc5f5d..4888f1e 100644
--- a/third_party/lcms/src/cmssamp.c
+++ b/third_party/lcms/src/cmssamp.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -83,10 +83,10 @@
         return FALSE;
     }
 
-    // Create a formatter which has n channels and floating point
+    // Create a formatter which has n channels and no floating point
     dwFormat = cmsFormatterForColorspaceOfProfile(hInput, 2, FALSE);
 
-   // Try to get black by using black colorant
+    // Try to get black by using black colorant
     Space = cmsGetColorSpace(hInput);
 
     // This function returns darker colorant in 16 bits for several spaces
@@ -123,9 +123,9 @@
     // Convert black to Lab
     cmsDoTransform(xform, Black, &Lab, 1);
 
-    // Force it to be neutral, clip to max. L* of 50
+    // Force it to be neutral, check for inconsistences
     Lab.a = Lab.b = 0;
-    if (Lab.L > 50) Lab.L = 50;
+    if (Lab.L > 50 || Lab.L < 0) Lab.L = 0;
 
     // Free the resources
     cmsDeleteTransform(xform);
@@ -322,6 +322,7 @@
 
     if (fabs(a) < 1.0E-10) {
     
+        if (fabs(b) < 1.0E-10) return 0;
         return cmsmin(0, cmsmax(50, -c/b ));
     }
     else {
@@ -332,7 +333,11 @@
          }
          else {
 
-             double rt = (-b + sqrt(d)) / (2.0 * a);
+             double rt;
+             
+             if (fabs(a) < 1.0E-10) return 0;
+
+             rt = (-b + sqrt(d)) / (2.0 * a);
 
              return cmsmax(0, cmsmin(50, rt));
          }
diff --git a/third_party/lcms/src/cmssm.c b/third_party/lcms/src/cmssm.c
index a0fdbc8..5c1e430 100644
--- a/third_party/lcms/src/cmssm.c
+++ b/third_party/lcms/src/cmssm.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
index 0c24da1..ec1909f 100644
--- a/third_party/lcms/src/cmstypes.c
+++ b/third_party/lcms/src/cmstypes.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -134,15 +134,62 @@
     return TRUE;
 }
 
+// Try to promote correctly to wchar_t when 32 bits
+cmsINLINE cmsBool is_surrogate(cmsUInt32Number uc) { return (uc - 0xd800u) < 2048u; }
+cmsINLINE cmsBool is_high_surrogate(cmsUInt32Number uc) { return (uc & 0xfffffc00) == 0xd800; }
+cmsINLINE cmsBool is_low_surrogate(cmsUInt32Number uc)  { return (uc & 0xfffffc00) == 0xdc00; }
+
+cmsINLINE cmsUInt32Number surrogate_to_utf32(cmsUInt32Number high, cmsUInt32Number low)
+{
+    return (high << 10) + low - 0x35fdc00;
+}
+
+cmsINLINE cmsBool convert_utf16_to_utf32(cmsIOHANDLER* io, cmsInt32Number n, wchar_t* output)
+{
+    cmsUInt16Number uc;
+
+    while (n > 0)
+    {
+        if (!_cmsReadUInt16Number(io, &uc)) return FALSE;
+        n--;
+
+        if (!is_surrogate(uc))
+        {
+            *output++ = (wchar_t)uc;
+        }
+        else {
+
+            cmsUInt16Number low;
+
+            if (!_cmsReadUInt16Number(io, &low)) return FALSE;
+            n--;
+
+            if (is_high_surrogate(uc) && is_low_surrogate(low))
+                *output++ = (wchar_t)surrogate_to_utf32(uc, low);
+            else
+                return FALSE;   // Corrupted string, just ignore
+        }
+    }
+
+    return TRUE;
+}
+
+
 // Auxiliary to read an array of wchar_t
 static
 cmsBool _cmsReadWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, wchar_t* Array)
 {
     cmsUInt32Number i;
     cmsUInt16Number tmp;
+    cmsBool is32 = sizeof(wchar_t) > sizeof(cmsUInt16Number);
 
     _cmsAssert(io != NULL);
 
+    if (is32 && Array != NULL)
+    {
+        return convert_utf16_to_utf32(io, n, Array);
+    }
+
     for (i=0; i < n; i++) {
 
         if (Array != NULL) {
@@ -166,7 +213,7 @@
                                              cmsUInt32Number SizeOfTag);
 
 // Helper function to deal with position tables as described in ICC spec 4.3
-// A table of n elements is readed, where first comes n records containing offsets and sizes and
+// A table of n elements is read, where first comes n records containing offsets and sizes and
 // then a block containing the data itself. This allows to reuse same data in more than one entry
 static
 cmsBool ReadPositionTable(struct _cms_typehandler_struct* self,
@@ -974,7 +1021,7 @@
     len = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0);
 
     // Specification ICC.1:2001-04 (v2.4.0): It has been found that textDescriptionType can contain misaligned data
-    //(see clause 4.1 for the definition of "aligned"). Because the Unicode language
+    //(see clause 4.1 for the definition of 'aligned'). Because the Unicode language
     // code and Unicode count immediately follow the ASCII description, their
     // alignment is not correct if the ASCII count is not a multiple of four. The
     // ScriptCode code is misaligned when the ASCII count is odd. Profile reading and
@@ -1307,7 +1354,7 @@
 //
 // All the dateTimeNumber values in a profile shall be in Coordinated Universal Time
 // (UTC, also known as GMT or ZULU Time). Profile writers are required to convert local
-// time to UTC when setting these values. Programmes that display these values may show
+// time to UTC when setting these values. Programs that display these values may show
 // the dateTimeNumber as UTC, show the equivalent local time (at current locale), or
 // display both UTC and local versions of the dateTimeNumber.
 
@@ -1378,9 +1425,9 @@
 {
     cmsICCMeasurementConditions mc;
 
-	
+    
     memset(&mc, 0, sizeof(mc));
-	
+    
     if (!_cmsReadUInt32Number(io, &mc.Observer)) return NULL;
     if (!_cmsReadXYZNumber(io,    &mc.Backing)) return NULL;
     if (!_cmsReadUInt32Number(io, &mc.Geometry)) return NULL;
@@ -1473,6 +1520,12 @@
         if (!_cmsReadUInt32Number(io, &Len)) goto Error;
         if (!_cmsReadUInt32Number(io, &Offset)) goto Error;
 
+        // Offset MUST be even because it indexes a block of utf16 chars. 
+        // Tricky profiles that uses odd positions will not work anyway
+        // because the whole utf16 block is previously converted to wchar_t 
+        // and sizeof this type may be of 4 bytes. On Linux systems, for example.
+        if (Offset & 1) goto Error;
+
         // Check for overflow
         if (Offset < (SizeOfHeader + 8)) goto Error;        
         if (((Offset + Len) < Len) || ((Offset + Len) > SizeOfTag + 8)) goto Error;
@@ -1480,7 +1533,7 @@
         // True begin of the string
         BeginOfThisString = Offset - SizeOfHeader - 8;
 
-        // Ajust to wchar_t elements
+        // Adjust to wchar_t elements
         mlu ->Entries[i].Len = (Len * sizeof(wchar_t)) / sizeof(cmsUInt16Number);
         mlu ->Entries[i].StrW = (BeginOfThisString * sizeof(wchar_t)) / sizeof(cmsUInt16Number);
 
@@ -1500,10 +1553,17 @@
     }
     else
     {
-        Block = (wchar_t*) _cmsMalloc(self ->ContextID, SizeOfTag);
+        // Make sure this is an even utf16 size.
+        if (SizeOfTag & 1) goto Error;
+
+        Block = (wchar_t*) _cmsCalloc(self ->ContextID, 1, SizeOfTag);
         if (Block == NULL) goto Error;
+       
         NumOfWchar = SizeOfTag / sizeof(wchar_t);
-        if (!_cmsReadWCharArray(io, NumOfWchar, Block)) goto Error;
+        if (!_cmsReadWCharArray(io, NumOfWchar, Block)) {
+            _cmsFree(self->ContextID, Block);
+            goto Error;
+        }
     }
 
     mlu ->MemPool  = Block;
@@ -1746,7 +1806,7 @@
 
 
 // That will create a MPE LUT with Matrix, pre tables, CLUT and post tables.
-// 8 bit lut may be scaled easely to v4 PCS, but we need also to properly adjust
+// 8 bit lut may be scaled easily to v4 PCS, but we need also to properly adjust
 // PCS on BToAxx tags and AtoB if abstract. We need to fix input direction.
 
 static
@@ -1853,7 +1913,7 @@
 static
 cmsBool  Type_LUT8_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 {
-    cmsUInt32Number j, nTabSize;
+    cmsUInt32Number j, nTabSize, i;
     cmsUInt8Number  val;
     cmsPipeline* NewLUT = (cmsPipeline*) Ptr;
     cmsStage* mpe;
@@ -1866,6 +1926,7 @@
     mpe = NewLUT -> Elements;
     if (mpe ->Type == cmsSigMatrixElemType) {
 
+        if (mpe->InputChannels != 3 || mpe->OutputChannels != 3) return FALSE;
         MatMPE = (_cmsStageMatrixData*) mpe ->Data;
         mpe = mpe -> Next;
     }
@@ -1887,37 +1948,37 @@
 
     // That should be all
     if (mpe != NULL) {
-        cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT8");
+        cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT8");
         return FALSE;
     }
 
-
     if (clut == NULL)
         clutPoints = 0;
-    else
-        clutPoints    = clut->Params->nSamples[0];
-
-    if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->InputChannels)) return FALSE;
-    if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->OutputChannels)) return FALSE;
+    else {
+        // Lut8 only allows same CLUT points in all dimensions        
+        clutPoints = clut->Params->nSamples[0];
+        for (i = 1; i < cmsPipelineInputChannels(NewLUT); i++) {
+            if (clut->Params->nSamples[i] != clutPoints) {
+                cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT with different samples per dimension not suitable to be saved as LUT16");
+                return FALSE;
+            }
+        }
+    }
+        
+    if (!_cmsWriteUInt8Number(io, (cmsUInt8Number)cmsPipelineInputChannels(NewLUT))) return FALSE;
+    if (!_cmsWriteUInt8Number(io, (cmsUInt8Number)cmsPipelineOutputChannels(NewLUT))) return FALSE;
     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE;
     if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding
 
-
     if (MatMPE != NULL) {
-
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[0])) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[1])) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[2])) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[3])) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[4])) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[5])) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[6])) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[7])) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[8])) return FALSE;
-
+        
+        for (i = 0; i < 9; i++)
+        {
+            if (!_cmsWrite15Fixed16Number(io, MatMPE->Double[i])) return FALSE;
+        }
     }
     else {
-
+        
         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
@@ -2031,11 +2092,11 @@
     cmsUInt32Number nEntries;
 
     _cmsAssert(Tables != NULL);
-
-    nEntries = Tables->TheCurves[0]->nEntries;
-
+   
     for (i=0; i < Tables ->nCurves; i++) {
 
+        nEntries = Tables->TheCurves[i]->nEntries;
+
         for (j=0; j < nEntries; j++) {
 
             val = Tables->TheCurves[i]->Table16[j];        
@@ -2141,7 +2202,7 @@
 // Some empty defaults are created for missing parts
 
 static
-cmsBool  Type_LUT16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
+cmsBool Type_LUT16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 {
     cmsUInt32Number nTabSize;
     cmsPipeline* NewLUT = (cmsPipeline*) Ptr;
@@ -2156,6 +2217,7 @@
     if (mpe != NULL && mpe ->Type == cmsSigMatrixElemType) {
 
         MatMPE = (_cmsStageMatrixData*) mpe ->Data;
+        if (mpe->InputChannels != 3 || mpe->OutputChannels != 3) return FALSE;
         mpe = mpe -> Next;
     }
 
@@ -2177,7 +2239,7 @@
 
     // That should be all
     if (mpe != NULL) {
-        cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT16");
+        cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT16");
         return FALSE;
     }
 
@@ -2186,29 +2248,32 @@
 
     if (clut == NULL)
         clutPoints = 0;
-    else
-        clutPoints    = clut->Params->nSamples[0];
+    else {
+        // Lut16 only allows same CLUT points in all dimensions        
+        clutPoints = clut->Params->nSamples[0];
+        for (i = 1; i < InputChannels; i++) {
+            if (clut->Params->nSamples[i] != clutPoints) {
+                cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT with different samples per dimension not suitable to be saved as LUT16");
+                return FALSE;
+            }
+        }
+    }
 
     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) InputChannels)) return FALSE;
     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) OutputChannels)) return FALSE;
     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE;
     if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding
-
-
+    
     if (MatMPE != NULL) {
-
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[0])) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[1])) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[2])) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[3])) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[4])) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[5])) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[6])) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[7])) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[8])) return FALSE;
+                
+        for (i = 0; i < 9; i++)
+        {
+            if (!_cmsWrite15Fixed16Number(io, MatMPE->Double[i])) return FALSE;
+        }
+      
     }
     else {
-
+        
         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
@@ -2394,7 +2459,7 @@
             return NULL;
         }
 
-        return CLUT;
+    return CLUT;
 }
 
 static
@@ -2550,30 +2615,30 @@
 static
 cmsBool  WriteMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsStage* mpe)
 {
+    cmsUInt32Number i, n;
+
     _cmsStageMatrixData* m = (_cmsStageMatrixData*) mpe -> Data;
 
+    n = mpe->InputChannels * mpe->OutputChannels;
+
     // Write the Matrix
-    if (!_cmsWrite15Fixed16Number(io, m -> Double[0])) return FALSE;
-    if (!_cmsWrite15Fixed16Number(io, m -> Double[1])) return FALSE;
-    if (!_cmsWrite15Fixed16Number(io, m -> Double[2])) return FALSE;
-    if (!_cmsWrite15Fixed16Number(io, m -> Double[3])) return FALSE;
-    if (!_cmsWrite15Fixed16Number(io, m -> Double[4])) return FALSE;
-    if (!_cmsWrite15Fixed16Number(io, m -> Double[5])) return FALSE;
-    if (!_cmsWrite15Fixed16Number(io, m -> Double[6])) return FALSE;
-    if (!_cmsWrite15Fixed16Number(io, m -> Double[7])) return FALSE;
-    if (!_cmsWrite15Fixed16Number(io, m -> Double[8])) return FALSE;
+    for (i = 0; i < n; i++)
+    {
+        if (!_cmsWrite15Fixed16Number(io, m->Double[i])) return FALSE;
+    }
 
-    if (m ->Offset != NULL) {
+    if (m->Offset != NULL) {
 
-    if (!_cmsWrite15Fixed16Number(io, m -> Offset[0])) return FALSE;
-    if (!_cmsWrite15Fixed16Number(io, m -> Offset[1])) return FALSE;
-    if (!_cmsWrite15Fixed16Number(io, m -> Offset[2])) return FALSE;
+        for (i = 0; i < mpe->OutputChannels; i++)
+        {
+            if (!_cmsWrite15Fixed16Number(io, m->Offset[i])) return FALSE;
+        }
     }
     else {
-        if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
-        if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
-
+        for (i = 0; i < mpe->OutputChannels; i++)
+        {
+            if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
+        }
     }
 
 
@@ -2678,9 +2743,9 @@
             return FALSE;
         }
 
-        if (!_cmsWriteAlignment(io)) return FALSE;
+    if (!_cmsWriteAlignment(io)) return FALSE;
 
-        return TRUE;
+    return TRUE;
 }
 
 
@@ -3007,6 +3072,9 @@
     }
 
     List = cmsAllocNamedColorList(self ->ContextID, Count, 0, "", "");
+    if (List == NULL)           
+        return NULL; 
+        
     for (i=0; i < Count; i++) {
 
         if (io ->Read(io, Name, 32, 1) != 1) goto Error;
@@ -3091,8 +3159,8 @@
 //The namedColor2Type is a count value and array of structures that provide color
 //coordinates for 7-bit ASCII color names. For each named color, a PCS and optional
 //device representation of the color are given. Both representations are 16-bit values.
-//The device representation corresponds to the header's "color space of data" field.
-//This representation should be consistent with the "number of device components"
+//The device representation corresponds to the header's 'color space of data' field.
+//This representation should be consistent with the 'number of device components'
 //field in the namedColor2Type. If this field is 0, device coordinates are not provided.
 //The PCS representation corresponds to the header's PCS field. The PCS representation
 //is always provided. Color names are fixed-length, 32-byte fields including null
@@ -3102,7 +3170,6 @@
 static
 void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
 {
-
     cmsUInt32Number      vendorFlag;     // Bottom 16 bits for ICC use
     cmsUInt32Number      count;          // Count of named colors
     cmsUInt32Number      nDeviceCoords;  // Num of device coordinates
@@ -3174,8 +3241,8 @@
     if (!_cmsWriteUInt32Number(io, nColors)) return FALSE;
     if (!_cmsWriteUInt32Number(io, NamedColorList ->ColorantCount)) return FALSE;
 
-    strncpy(prefix, (const char*) NamedColorList->Prefix, 32);
-    strncpy(suffix, (const char*) NamedColorList->Suffix, 32);
+    memcpy(prefix, (const char*) NamedColorList->Prefix, sizeof(prefix));
+    memcpy(suffix, (const char*) NamedColorList->Suffix, sizeof(suffix));
 
     suffix[32] = prefix[32] = 0;
 
@@ -3188,6 +3255,10 @@
        cmsUInt16Number Colorant[cmsMAXCHANNELS];
        char Root[cmsMAX_PATH];
 
+       memset(Root, 0, sizeof(Root));
+       memset(PCS, 0, sizeof(PCS));
+       memset(Colorant, 0, sizeof(Colorant));
+
         if (!cmsNamedColorInfo(NamedColorList, i, Root, NULL, NULL, PCS, Colorant)) return 0;
         Root[32] = 0;
         if (!io ->Write(io, 32 , Root)) return FALSE;
@@ -3428,7 +3499,6 @@
 
     // Get table count
     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
-    SizeOfTag -= sizeof(cmsUInt32Number);
 
     // Allocate an empty structure
     OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count);
@@ -3446,6 +3516,7 @@
     *nItems = 1;
     return OutSeq;
 
+    cmsUNUSED_PARAMETER(SizeOfTag);
 }
 
 
@@ -3521,47 +3592,68 @@
 {
     cmsUcrBg* n = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg));
     cmsUInt32Number CountUcr, CountBg;
+    cmsInt32Number SignedSizeOfTag = (cmsInt32Number)SizeOfTag;
     char* ASCIIString;
 
     *nItems = 0;
     if (n == NULL) return NULL;
 
     // First curve is Under color removal
+
+    if (SignedSizeOfTag < (cmsInt32Number) sizeof(cmsUInt32Number)) return NULL;
     if (!_cmsReadUInt32Number(io, &CountUcr)) return NULL;
-    if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
-    SizeOfTag -= sizeof(cmsUInt32Number);
+    SignedSizeOfTag -= sizeof(cmsUInt32Number);
 
     n ->Ucr = cmsBuildTabulatedToneCurve16(self ->ContextID, CountUcr, NULL);
-    if (n ->Ucr == NULL) return NULL;
+    if (n ->Ucr == NULL) goto error;
 
-    if (!_cmsReadUInt16Array(io, CountUcr, n ->Ucr->Table16)) return NULL;
-    if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
-    SizeOfTag -= CountUcr * sizeof(cmsUInt16Number);
+    if (SignedSizeOfTag < (cmsInt32Number)(CountUcr * sizeof(cmsUInt16Number))) goto error;
+    if (!_cmsReadUInt16Array(io, CountUcr, n ->Ucr->Table16)) goto error;
+  
+    SignedSizeOfTag -= CountUcr * sizeof(cmsUInt16Number);
 
     // Second curve is Black generation
-    if (!_cmsReadUInt32Number(io, &CountBg)) return NULL;
-    if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
-    SizeOfTag -= sizeof(cmsUInt32Number);
+
+    if (SignedSizeOfTag < (cmsInt32Number)sizeof(cmsUInt32Number)) goto error;
+    if (!_cmsReadUInt32Number(io, &CountBg)) goto error;
+    SignedSizeOfTag -= sizeof(cmsUInt32Number);
 
     n ->Bg = cmsBuildTabulatedToneCurve16(self ->ContextID, CountBg, NULL);
-    if (n ->Bg == NULL) return NULL;
-    if (!_cmsReadUInt16Array(io, CountBg, n ->Bg->Table16)) return NULL;
-    if (SizeOfTag < CountBg * sizeof(cmsUInt16Number)) return NULL;
-    SizeOfTag -= CountBg * sizeof(cmsUInt16Number);
-    if (SizeOfTag == UINT_MAX) return NULL;
+    if (n ->Bg == NULL) goto error;
+
+    if (SignedSizeOfTag < (cmsInt32Number) (CountBg * sizeof(cmsUInt16Number))) goto error;
+    if (!_cmsReadUInt16Array(io, CountBg, n ->Bg->Table16)) goto error;
+    SignedSizeOfTag -= CountBg * sizeof(cmsUInt16Number);
+
+    if (SignedSizeOfTag < 0 || SignedSizeOfTag > 32000) goto error;
 
     // Now comes the text. The length is specified by the tag size
     n ->Desc = cmsMLUalloc(self ->ContextID, 1);
-    if (n ->Desc == NULL) return NULL;
+    if (n ->Desc == NULL) goto error;
 
-    ASCIIString = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1);
-    if (io ->Read(io, ASCIIString, sizeof(char), SizeOfTag) != SizeOfTag) return NULL;
-    ASCIIString[SizeOfTag] = 0;
+    ASCIIString = (char*) _cmsMalloc(self ->ContextID, SignedSizeOfTag + 1);
+    if (io->Read(io, ASCIIString, sizeof(char), SignedSizeOfTag) != (cmsUInt32Number)SignedSizeOfTag)
+    {
+        _cmsFree(self->ContextID, ASCIIString);
+        goto error;
+    }
+
+    ASCIIString[SignedSizeOfTag] = 0;
     cmsMLUsetASCII(n ->Desc, cmsNoLanguage, cmsNoCountry, ASCIIString);
     _cmsFree(self ->ContextID, ASCIIString);
 
     *nItems = 1;
     return (void*) n;
+
+error:
+
+    if (n->Ucr) cmsFreeToneCurve(n->Ucr);
+    if (n->Bg) cmsFreeToneCurve(n->Bg);
+    if (n->Desc) cmsMLUfree(n->Desc);
+    _cmsFree(self->ContextID, n);
+    *nItems = 0;
+    return NULL;
+
 }
 
 static
@@ -3642,7 +3734,7 @@
 
 // Auxiliary, read an string specified as count + string
 static
-cmsBool  ReadCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, cmsUInt32Number* SizeOfTag, const char* Section)
+cmsBool  ReadCountAndString(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, cmsUInt32Number* SizeOfTag, const char* Section)
 {
     cmsUInt32Number Count;
     char* Text;
@@ -3672,7 +3764,7 @@
 }
 
 static
-cmsBool  WriteCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, const char* Section)
+cmsBool  WriteCountAndString(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, const char* Section)
 {
  cmsUInt32Number TextSize;
  char* Text;
@@ -3696,11 +3788,11 @@
     cmsMLU* mlu = cmsMLUalloc(self ->ContextID, 5);
 
     *nItems = 0;
-    if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "nm")) goto Error;
-    if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#0")) goto Error;
-    if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#1")) goto Error;
-    if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#2")) goto Error;
-    if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#3")) goto Error;
+    if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "nm")) goto Error;
+    if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "#0")) goto Error;
+    if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "#1")) goto Error;
+    if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "#2")) goto Error;
+    if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "#3")) goto Error;
 
     *nItems = 1;
     return (void*) mlu;
@@ -3717,11 +3809,11 @@
 
     cmsMLU* mlu = (cmsMLU*) Ptr;
 
-    if (!WriteCountAndSting(self, io, mlu, "nm")) goto Error;
-    if (!WriteCountAndSting(self, io, mlu, "#0")) goto Error;
-    if (!WriteCountAndSting(self, io, mlu, "#1")) goto Error;
-    if (!WriteCountAndSting(self, io, mlu, "#2")) goto Error;
-    if (!WriteCountAndSting(self, io, mlu, "#3")) goto Error;
+    if (!WriteCountAndString(self, io, mlu, "nm")) goto Error;
+    if (!WriteCountAndString(self, io, mlu, "#0")) goto Error;
+    if (!WriteCountAndString(self, io, mlu, "#1")) goto Error;
+    if (!WriteCountAndString(self, io, mlu, "#2")) goto Error;
+    if (!WriteCountAndString(self, io, mlu, "#3")) goto Error;
 
     return TRUE;
 
@@ -3975,41 +4067,44 @@
 
            switch (ElementSig) {
 
-            case cmsSigFormulaCurveSeg: {
+           case cmsSigFormulaCurveSeg: {
 
-                cmsUInt16Number Type;
-                cmsUInt32Number ParamsByType[] = {4, 5, 5 };
+               cmsUInt16Number Type;
+               cmsUInt32Number ParamsByType[] = { 4, 5, 5 };
 
-                if (!_cmsReadUInt16Number(io, &Type)) goto Error;
-                if (!_cmsReadUInt16Number(io, NULL)) goto Error;
+               if (!_cmsReadUInt16Number(io, &Type)) goto Error;
+               if (!_cmsReadUInt16Number(io, NULL)) goto Error;
 
-                Segments[i].Type = Type + 6;
-                if (Type > 2) goto Error;
+               Segments[i].Type = Type + 6;
+               if (Type > 2) goto Error;
 
-                for (j=0; j < ParamsByType[Type]; j++) {
+               for (j = 0; j < ParamsByType[Type]; j++) {
 
-                    cmsFloat32Number f;
-                    if (!_cmsReadFloat32Number(io, &f)) goto Error;
-                    Segments[i].Params[j] = f;
-                }
-                }
-                break;
+                   cmsFloat32Number f;
+                   if (!_cmsReadFloat32Number(io, &f)) goto Error;
+                   Segments[i].Params[j] = f;
+               }
+           }
+           break;
 
 
-            case cmsSigSampledCurveSeg: {
-                cmsUInt32Number Count;
+           case cmsSigSampledCurveSeg: {
+               cmsUInt32Number Count;
 
-                if (!_cmsReadUInt32Number(io, &Count)) goto Error;
+               if (!_cmsReadUInt32Number(io, &Count)) goto Error;
 
-                Segments[i].nGridPoints = Count;
-                Segments[i].SampledPoints = (cmsFloat32Number*) _cmsCalloc(self ->ContextID, Count, sizeof(cmsFloat32Number));
-                if (Segments[i].SampledPoints == NULL) goto Error;
+               // The first point is implicit in the last stage, we allocate an extra note to be populated latter on
+               Count++;
+               Segments[i].nGridPoints = Count;
+               Segments[i].SampledPoints = (cmsFloat32Number*)_cmsCalloc(self->ContextID, Count, sizeof(cmsFloat32Number));
+               if (Segments[i].SampledPoints == NULL) goto Error;
 
-                for (j=0; j < Count; j++) {
-                    if (!_cmsReadFloat32Number(io, &Segments[i].SampledPoints[j])) goto Error;
-                }
-                }
-                break;
+               Segments[i].SampledPoints[0] = 0;
+               for (j = 1; j < Count; j++) {
+                   if (!_cmsReadFloat32Number(io, &Segments[i].SampledPoints[j])) goto Error;
+               }
+           }
+           break;
 
             default:
                 {
@@ -4029,6 +4124,17 @@
          if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints);
      }
      _cmsFree(self ->ContextID, Segments);
+
+     // Explore for missing implicit points 
+     for (i = 0; i < nSegments; i++) {
+
+         // If sampled curve, fix it
+         if (Curve->Segments[i].Type == 0) {
+
+             Curve->Segments[i].SampledPoints[0] = cmsEvalToneCurveFloat(Curve, Curve->Segments[i].x0);
+         }
+     }
+
      return Curve;
 
 Error:
@@ -4123,12 +4229,12 @@
 
         if (ActualSeg -> Type == 0) {
 
-            // This is a sampled curve
+            // This is a sampled curve. First point is implicit in the ICC format, but not in our representation
             if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigSampledCurveSeg)) goto Error;
             if (!_cmsWriteUInt32Number(io, 0)) goto Error;
-            if (!_cmsWriteUInt32Number(io, ActualSeg -> nGridPoints)) goto Error;
+            if (!_cmsWriteUInt32Number(io, ActualSeg -> nGridPoints - 1)) goto Error;
 
-            for (j=0; j < g ->Segments[i].nGridPoints; j++) {
+            for (j=1; j < g ->Segments[i].nGridPoints; j++) {
                 if (!_cmsWriteFloat32Number(io, ActualSeg -> SampledPoints[j])) goto Error;
             }
 
@@ -4505,7 +4611,7 @@
 
 
 
-// This one is a liitle bit more complex, so we don't use position tables this time.
+// This one is a little bit more complex, so we don't use position tables this time.
 static
 cmsBool Type_MPE_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 {
@@ -4937,7 +5043,7 @@
     if (!_cmsReadUInt32Number(io, &e->Offsets[i])) return FALSE;
     if (!_cmsReadUInt32Number(io, &e ->Sizes[i])) return FALSE;
 
-    // An offset of zero has special meaning and shal be preserved
+    // An offset of zero has special meaning and shall be preserved
     if (e ->Offsets[i] > 0)
         e ->Offsets[i] += BaseOffset;
     return TRUE;
@@ -4945,27 +5051,41 @@
 
 
 static
-cmsBool ReadOffsetArray(cmsIOHANDLER* io,  _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length, cmsUInt32Number BaseOffset)
+cmsBool ReadOffsetArray(cmsIOHANDLER* io,  _cmsDICarray* a, 
+                        cmsUInt32Number Count, cmsUInt32Number Length, cmsUInt32Number BaseOffset,
+                        cmsInt32Number* SignedSizeOfTagPtr)
 {
     cmsUInt32Number i;
+    cmsInt32Number SignedSizeOfTag = *SignedSizeOfTagPtr;
 
     // Read column arrays
     for (i=0; i < Count; i++) {
 
+        if (SignedSizeOfTag < 4 * (cmsInt32Number) sizeof(cmsUInt32Number)) return FALSE;
+        SignedSizeOfTag -= 4 * sizeof(cmsUInt32Number);
+
         if (!ReadOneElem(io, &a -> Name, i, BaseOffset)) return FALSE;
         if (!ReadOneElem(io, &a -> Value, i, BaseOffset)) return FALSE;
 
         if (Length > 16) {
 
+            if (SignedSizeOfTag < 2 * (cmsInt32Number) sizeof(cmsUInt32Number)) return FALSE;
+            SignedSizeOfTag -= 2 * sizeof(cmsUInt32Number);
+
             if (!ReadOneElem(io, &a ->DisplayName, i, BaseOffset)) return FALSE;
 
         }
 
         if (Length > 24) {
 
+            if (SignedSizeOfTag < 2 * (cmsInt32Number) sizeof(cmsUInt32Number)) return FALSE;
+            SignedSizeOfTag -= 2 * (cmsInt32Number) sizeof(cmsUInt32Number);
+
             if (!ReadOneElem(io, & a -> DisplayValue, i, BaseOffset)) return FALSE;
         }
     }
+
+    *SignedSizeOfTagPtr = SignedSizeOfTag;
     return TRUE;
 }
 
@@ -5113,26 +5233,31 @@
 static
 void *Type_Dictionary_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
 {
-   cmsHANDLE hDict;
+   cmsHANDLE hDict = NULL;
    cmsUInt32Number i, Count, Length;
    cmsUInt32Number BaseOffset;
    _cmsDICarray a;
    wchar_t *NameWCS = NULL, *ValueWCS = NULL;
    cmsMLU *DisplayNameMLU = NULL, *DisplayValueMLU=NULL;
    cmsBool rc;
+   cmsInt32Number SignedSizeOfTag = (cmsInt32Number)SizeOfTag;
 
     *nItems = 0;
+    memset(&a, 0, sizeof(a));
 
     // Get actual position as a basis for element offsets
     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
 
     // Get name-value record count
+    SignedSizeOfTag -= sizeof(cmsUInt32Number);
+    if (SignedSizeOfTag < 0) goto Error;
     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
-    SizeOfTag -= sizeof(cmsUInt32Number);
-
+    
     // Get rec length
+    SignedSizeOfTag -= sizeof(cmsUInt32Number);
+    if (SignedSizeOfTag < 0) goto Error;
     if (!_cmsReadUInt32Number(io, &Length)) return NULL;
-    SizeOfTag -= sizeof(cmsUInt32Number);
+    
 
     // Check for valid lengths
     if (Length != 16 && Length != 24 && Length != 32) {
@@ -5148,7 +5273,7 @@
     if (!AllocArray(self -> ContextID, &a, Count, Length)) goto Error;
 
     // Read column arrays
-    if (!ReadOffsetArray(io, &a, Count, Length, BaseOffset)) goto Error;
+    if (!ReadOffsetArray(io, &a, Count, Length, BaseOffset, &SignedSizeOfTag)) goto Error;
 
     // Seek to each element and read it
     for (i=0; i < Count; i++) {
@@ -5188,7 +5313,7 @@
 
 Error:
    FreeArray(&a);
-   cmsDictFree(hDict);
+   if (hDict != NULL) cmsDictFree(hDict);
    return NULL;
 }
 
@@ -5286,6 +5411,64 @@
     cmsUNUSED_PARAMETER(self);
 }
 
+// cicp VideoSignalType
+
+static
+void* Type_VideoSignal_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
+{
+    cmsVideoSignalType* cicp = NULL;
+
+    if (SizeOfTag != 8) return NULL; 
+
+    if (!_cmsReadUInt32Number(io, NULL)) return NULL;
+
+    cicp = (cmsVideoSignalType*)_cmsCalloc(self->ContextID, 1, sizeof(cmsVideoSignalType));
+    if (cicp == NULL) return NULL;
+
+    if (!_cmsReadUInt8Number(io, &cicp->ColourPrimaries)) goto Error;
+    if (!_cmsReadUInt8Number(io, &cicp->TransferCharacteristics)) goto Error;
+    if (!_cmsReadUInt8Number(io, &cicp->MatrixCoefficients)) goto Error;
+    if (!_cmsReadUInt8Number(io, &cicp->VideoFullRangeFlag)) goto Error;
+
+    // Success
+    *nItems = 1;
+    return cicp;
+
+Error:
+    if (cicp != NULL) _cmsFree(self->ContextID, cicp);
+    return NULL;
+}
+
+static
+cmsBool Type_VideoSignal_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
+{
+    cmsVideoSignalType* cicp = (cmsVideoSignalType*)Ptr;
+
+    if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
+    if (!_cmsWriteUInt8Number(io, cicp->ColourPrimaries)) return FALSE;
+    if (!_cmsWriteUInt8Number(io, cicp->TransferCharacteristics)) return FALSE;
+    if (!_cmsWriteUInt8Number(io, cicp->MatrixCoefficients)) return FALSE;
+    if (!_cmsWriteUInt8Number(io, cicp->VideoFullRangeFlag)) return FALSE;
+
+    return TRUE;
+
+    cmsUNUSED_PARAMETER(self);
+    cmsUNUSED_PARAMETER(nItems);
+}
+
+void* Type_VideoSignal_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
+{
+    return _cmsDupMem(self->ContextID, Ptr, sizeof(cmsVideoSignalType));
+
+    cmsUNUSED_PARAMETER(n);
+}
+
+
+static
+void Type_VideoSignal_Free(struct _cms_typehandler_struct* self, void* Ptr)
+{
+    _cmsFree(self->ContextID, Ptr);
+}
 
 // ********************************************************************************
 // Type support main routines
@@ -5325,6 +5508,7 @@
 {TYPE_HANDLER(cmsMonacoBrokenCurveType,        Curve),              (_cmsTagTypeLinkedList*) &SupportedTagTypes[28] },
 {TYPE_HANDLER(cmsSigProfileSequenceIdType,     ProfileSequenceId),  (_cmsTagTypeLinkedList*) &SupportedTagTypes[29] },
 {TYPE_HANDLER(cmsSigDictType,                  Dictionary),         (_cmsTagTypeLinkedList*) &SupportedTagTypes[30] },
+{TYPE_HANDLER(cmsSigcicpType,                  VideoSignal),        (_cmsTagTypeLinkedList*) &SupportedTagTypes[31] },
 {TYPE_HANDLER(cmsSigVcgtType,                  vcgt),                NULL }
 };
 
@@ -5519,6 +5703,8 @@
     { cmsSigProfileSequenceIdTag,   { 1, 1, { cmsSigProfileSequenceIdType},  NULL }, &SupportedTags[62]},
 
     { cmsSigProfileDescriptionMLTag,{ 1, 1, { cmsSigMultiLocalizedUnicodeType}, NULL}, &SupportedTags[63]},
+    { cmsSigcicpTag,                { 1, 1, { cmsSigcicpType},               NULL },   &SupportedTags[64]},
+
     { cmsSigArgyllArtsTag,          { 9, 1, { cmsSigS15Fixed16ArrayType},    NULL}, NULL}
 
 };
diff --git a/third_party/lcms/src/cmsvirt.c b/third_party/lcms/src/cmsvirt.c
index 935effc..951a8ee 100644
--- a/third_party/lcms/src/cmsvirt.c
+++ b/third_party/lcms/src/cmsvirt.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -114,7 +114,7 @@
     if (!hICC)                          // can't allocate
         return NULL;
 
-    cmsSetProfileVersion(hICC, 4.3);
+    cmsSetProfileVersion(hICC, 4.4);
 
     cmsSetDeviceClass(hICC,      cmsSigDisplayClass);
     cmsSetColorSpace(hICC,       cmsSigRgbData);
@@ -235,7 +235,7 @@
     if (!hICC)                          // can't allocate
         return NULL;
 
-    cmsSetProfileVersion(hICC, 4.3);
+    cmsSetProfileVersion(hICC, 4.4);
 
     cmsSetDeviceClass(hICC,      cmsSigDisplayClass);
     cmsSetColorSpace(hICC,       cmsSigGrayData);
@@ -291,13 +291,13 @@
 {
     cmsHPROFILE hICC;
     cmsPipeline* Pipeline;
-    cmsUInt32Number nChannels;
+    cmsInt32Number nChannels;
 
     hICC = cmsCreateProfilePlaceholder(ContextID);
     if (!hICC)
         return NULL;
 
-    cmsSetProfileVersion(hICC, 4.3);
+    cmsSetProfileVersion(hICC, 4.4);
 
     cmsSetDeviceClass(hICC,      cmsSigLinkClass);
     cmsSetColorSpace(hICC,       ColorSpace);
@@ -306,7 +306,7 @@
     cmsSetHeaderRenderingIntent(hICC,  INTENT_PERCEPTUAL);
 
     // Set up channels
-    nChannels = cmsChannelsOf(ColorSpace);
+    nChannels = cmsChannelsOfColorSpace(ColorSpace);
 
     // Creates a Pipeline with prelinearization step only
     Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels);
@@ -361,14 +361,14 @@
 //     K: Does not change
 
 static
-int InkLimitingSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
+int InkLimitingSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
 {
     cmsFloat64Number InkLimit = *(cmsFloat64Number *) Cargo;
     cmsFloat64Number SumCMY, SumCMYK, Ratio;
 
     InkLimit = (InkLimit * 655.35);
 
-    SumCMY   = In[0]  + In[1] + In[2];
+    SumCMY   = (cmsFloat64Number) In[0]  + In[1] + In[2];
     SumCMYK  = SumCMY + In[3];
 
     if (SumCMYK > InkLimit) {
@@ -397,7 +397,7 @@
     cmsHPROFILE hICC;
     cmsPipeline* LUT;
     cmsStage* CLUT;
-    cmsUInt32Number nChannels;
+    cmsInt32Number nChannels;
 
     if (ColorSpace != cmsSigCmykData) {
         cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported");
@@ -416,7 +416,7 @@
     if (!hICC)                          // can't allocate
         return NULL;
 
-    cmsSetProfileVersion(hICC, 4.3);
+    cmsSetProfileVersion(hICC, 4.4);
 
     cmsSetDeviceClass(hICC,      cmsSigLinkClass);
     cmsSetColorSpace(hICC,       ColorSpace);
@@ -526,7 +526,7 @@
     hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
     if (hProfile == NULL) return NULL;
 
-    cmsSetProfileVersion(hProfile, 4.3);
+    cmsSetProfileVersion(hProfile, 4.4);
 
     cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
     cmsSetColorSpace(hProfile,  cmsSigLabData);
@@ -572,7 +572,7 @@
     hProfile = cmsCreateRGBProfileTHR(ContextID, cmsD50_xyY(), NULL, NULL);
     if (hProfile == NULL) return NULL;
 
-    cmsSetProfileVersion(hProfile, 4.3);
+    cmsSetProfileVersion(hProfile, 4.4);
 
     cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
     cmsSetColorSpace(hProfile,  cmsSigXYZData);
@@ -686,7 +686,7 @@
 
 
 static
-int bchswSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
+int bchswSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
 {
     cmsCIELab LabIn, LabOut;
     cmsCIELCh LChIn, LChOut;
@@ -839,7 +839,7 @@
     if (!hProfile)                          // can't allocate
         return NULL;
 
-    cmsSetProfileVersion(hProfile, 4.3);
+    cmsSetProfileVersion(hProfile, 4.4);
 
     if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error;
 
@@ -974,7 +974,7 @@
     // Make sure we have proper formatters
     cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX,
         FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace))
-        | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace)));
+        | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOfColorSpace(v ->ExitColorSpace)));
 
     // Apply the transfor to colorants.
     for (i=0; i < nColors; i++) {
@@ -1062,8 +1062,9 @@
 cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags)
 {
     cmsHPROFILE hProfile = NULL;
-    cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut;
-    int ColorSpaceBitsIn, ColorSpaceBitsOut;
+	cmsUInt32Number FrmIn, FrmOut;
+	cmsInt32Number ChansIn, ChansOut;
+	int ColorSpaceBitsIn, ColorSpaceBitsOut;
     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
     cmsPipeline* LUT = NULL;
     cmsStage* mpe;
@@ -1096,9 +1097,10 @@
             goto Error;
     }
 
-    // On the output side too
+    // On the output side too. Note that due to V2/V4 PCS encoding on lab we cannot fix white misalignments
     if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) {
 
+        dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP;
         if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID)))
             goto Error;
     }
@@ -1113,8 +1115,8 @@
 
     // Optimize the LUT and precalculate a devicelink
 
-    ChansIn  = cmsChannelsOf(xform -> EntryColorSpace);
-    ChansOut = cmsChannelsOf(xform -> ExitColorSpace);
+    ChansIn  = cmsChannelsOfColorSpace(xform -> EntryColorSpace);
+    ChansOut = cmsChannelsOfColorSpace(xform -> ExitColorSpace);
 
     ColorSpaceBitsIn  = _cmsLCMScolorSpace(xform -> EntryColorSpace);
     ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace);
diff --git a/third_party/lcms/src/cmswtpnt.c b/third_party/lcms/src/cmswtpnt.c
index 9f90d6a..a73eaa7 100644
--- a/third_party/lcms/src/cmswtpnt.c
+++ b/third_party/lcms/src/cmswtpnt.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -75,19 +75,19 @@
             return FALSE;
         }
 
-        // Obtain y(x)
-        y = -3.000*(x*x) + 2.870*x - 0.275;
+    // Obtain y(x)
+    y = -3.000*(x*x) + 2.870*x - 0.275;
 
-        // wave factors (not used, but here for futures extensions)
+    // wave factors (not used, but here for futures extensions)
 
-        // M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y);
-        // M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y);
+    // M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y);
+    // M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y);
 
-        WhitePoint -> x = x;
-        WhitePoint -> y = y;
-        WhitePoint -> Y = 1.0;
+    WhitePoint -> x = x;
+    WhitePoint -> y = y;
+    WhitePoint -> Y = 1.0;
 
-        return TRUE;
+    return TRUE;
 }
 
 
@@ -215,12 +215,15 @@
     _cmsMAT3eval(&ConeSourceRGB, Chad, &ConeSourceXYZ);
     _cmsMAT3eval(&ConeDestRGB,   Chad, &ConeDestXYZ);
 
+    if ((fabs(ConeSourceRGB.n[0]) < MATRIX_DET_TOLERANCE) ||
+        (fabs(ConeSourceRGB.n[1]) < MATRIX_DET_TOLERANCE) ||
+        (fabs(ConeSourceRGB.n[2]) < MATRIX_DET_TOLERANCE)) return FALSE;
+
     // Build matrix
     _cmsVEC3init(&Cone.v[0], ConeDestRGB.n[0]/ConeSourceRGB.n[0],    0.0,  0.0);
     _cmsVEC3init(&Cone.v[1], 0.0,   ConeDestRGB.n[1]/ConeSourceRGB.n[1],   0.0);
     _cmsVEC3init(&Cone.v[2], 0.0,   0.0,   ConeDestRGB.n[2]/ConeSourceRGB.n[2]);
 
-
     // Normalize
     _cmsMAT3per(&Tmp, &Cone, Chad);
     _cmsMAT3per(Conversion, &Chad_Inv, &Tmp);
@@ -264,16 +267,16 @@
 
 // Build a White point, primary chromas transfer matrix from RGB to CIE XYZ
 // This is just an approximation, I am not handling all the non-linear
-// aspects of the RGB to XYZ process, and assumming that the gamma correction
+// aspects of the RGB to XYZ process, and assuming that the gamma correction
 // has transitive property in the transformation chain.
 //
-// the alghoritm:
+// the algorithm:
 //
 //            - First I build the absolute conversion matrix using
 //              primaries in XYZ. This matrix is next inverted
 //            - Then I eval the source white point across this matrix
-//              obtaining the coeficients of the transformation
-//            - Then, I apply these coeficients to the original matrix
+//              obtaining the coefficients of the transformation
+//            - Then, I apply these coefficients to the original matrix
 //
 cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePt, const cmsCIExyYTRIPLE* Primrs)
 {
diff --git a/third_party/lcms/src/cmsxform.c b/third_party/lcms/src/cmsxform.c
index 508117b..c70b7cb 100644
--- a/third_party/lcms/src/cmsxform.c
+++ b/third_party/lcms/src/cmsxform.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -171,6 +171,23 @@
     _cmsFree(p ->ContextID, (void *) p);
 }
 
+
+static
+cmsUInt32Number PixelSize(cmsUInt32Number Format)
+{
+    cmsUInt32Number fmt_bytes = T_BYTES(Format);
+
+    // For double, the T_BYTES field is zero
+    if (fmt_bytes == 0)
+        return sizeof(cmsUInt64Number);
+
+    // Otherwise, it is already correct for all formats
+    return fmt_bytes;
+}
+
+
+
+
 // Apply transform.
 void CMSEXPORT cmsDoTransform(cmsHTRANSFORM  Transform,
                               const void* InputBuffer,
@@ -183,8 +200,8 @@
 
     stride.BytesPerLineIn = 0;  // Not used
     stride.BytesPerLineOut = 0;
-    stride.BytesPerPlaneIn = Size;
-    stride.BytesPerPlaneOut = Size;
+    stride.BytesPerPlaneIn = Size * PixelSize(p->InputFormat);
+    stride.BytesPerPlaneOut = Size * PixelSize(p->OutputFormat);
            
     p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride);
 }
@@ -256,7 +273,7 @@
     strideIn = 0;
     strideOut = 0;
     memset(fIn, 0, sizeof(fIn));
-    memset(fOut, 0, sizeof(fIn));
+    memset(fOut, 0, sizeof(fOut));
 
     for (i = 0; i < LineCount; i++) {
 
@@ -267,7 +284,7 @@
 
             accum = p->FromInputFloat(p, fIn, accum, Stride->BytesPerPlaneIn);
 
-            // Any gamut chack to do?
+            // Any gamut check to do?
             if (p->GamutCheck != NULL) {
 
                 // Evaluate gamut marker.
@@ -388,8 +405,8 @@
                         cmsUInt32Number LineCount,
                         const cmsStride* Stride)
 {
-    register cmsUInt8Number* accum;
-    register cmsUInt8Number* output;
+    CMSREGISTER cmsUInt8Number* accum;
+    CMSREGISTER cmsUInt8Number* output;
     cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
     cmsUInt32Number i, j, strideIn, strideOut;
 
@@ -430,7 +447,7 @@
     p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data);
     if (wOutOfGamut >= 1) {
 
-        cmsUInt16Number i;
+        cmsUInt32Number i;
         _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext);        
 
         for (i=0; i < p ->Lut->OutputChannels; i++) {
@@ -757,6 +774,79 @@
      if (ToOutput)  *ToOutput  = CMMcargo ->ToOutputFloat;
 }
 
+// returns original flags
+cmsUInt32Number CMSEXPORT _cmsGetTransformFlags(struct _cmstransform_struct* CMMcargo)
+{
+    _cmsAssert(CMMcargo != NULL);
+    return CMMcargo->dwOriginalFlags;
+}
+
+// Returns the worker callback for parallelization plug-ins
+_cmsTransform2Fn CMSEXPORT _cmsGetTransformWorker(struct _cmstransform_struct* CMMcargo)
+{
+    _cmsAssert(CMMcargo != NULL);
+    return CMMcargo->Worker;
+}
+
+// This field holds maximum number of workers or -1 to auto 
+cmsInt32Number CMSEXPORT _cmsGetTransformMaxWorkers(struct _cmstransform_struct* CMMcargo)
+{
+    _cmsAssert(CMMcargo != NULL);
+    return CMMcargo->MaxWorkers;
+}
+
+// This field is actually unused and reserved
+cmsUInt32Number CMSEXPORT _cmsGetTransformWorkerFlags(struct _cmstransform_struct* CMMcargo)
+{
+    _cmsAssert(CMMcargo != NULL);
+    return CMMcargo->WorkerFlags;
+}
+
+// In the case there is a parallelization plug-in, let it to do its job
+static
+void ParalellizeIfSuitable(_cmsTRANSFORM* p)
+{
+    _cmsParallelizationPluginChunkType* ctx = (_cmsParallelizationPluginChunkType*)_cmsContextGetClientChunk(p->ContextID, ParallelizationPlugin);
+
+    _cmsAssert(p != NULL);
+    if (ctx != NULL && ctx->SchedulerFn != NULL) {
+
+        p->Worker = p->xform;
+        p->xform = ctx->SchedulerFn;
+        p->MaxWorkers = ctx->MaxWorkers;
+        p->WorkerFlags = ctx->WorkerFlags;
+    }
+}
+
+
+/**
+* An empty unroll to avoid a check with NULL on cmsDoTransform()
+*/
+static
+cmsUInt8Number* UnrollNothing(CMSREGISTER _cmsTRANSFORM* info,
+                              CMSREGISTER cmsUInt16Number wIn[],
+                              CMSREGISTER cmsUInt8Number* accum,
+                              CMSREGISTER cmsUInt32Number Stride)
+{    
+    return accum;
+
+    cmsUNUSED_PARAMETER(info);
+    cmsUNUSED_PARAMETER(wIn);
+    cmsUNUSED_PARAMETER(Stride);
+}
+
+static
+cmsUInt8Number* PackNothing(CMSREGISTER _cmsTRANSFORM* info,
+                           CMSREGISTER cmsUInt16Number wOut[],
+                           CMSREGISTER cmsUInt8Number* output,
+                           CMSREGISTER cmsUInt32Number Stride)
+{
+    return output;
+
+    cmsUNUSED_PARAMETER(info);
+    cmsUNUSED_PARAMETER(wOut);
+    cmsUNUSED_PARAMETER(Stride);
+}
 
 // Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper
 // for separated transforms. If this is the case,
@@ -780,47 +870,51 @@
        // Let's see if any plug-in want to do the transform by itself
        if (p->Lut != NULL) {
 
-              for (Plugin = ctx->TransformCollection;
-                     Plugin != NULL;
-                     Plugin = Plugin->Next) {
+           if (!(*dwFlags & cmsFLAGS_NOOPTIMIZE))
+           {
+               for (Plugin = ctx->TransformCollection;
+                   Plugin != NULL;
+                   Plugin = Plugin->Next) {
 
-                     if (Plugin->Factory(&p->xform, &p->UserData, &p->FreeUserData, &p->Lut, InputFormat, OutputFormat, dwFlags)) {
+                   if (Plugin->Factory(&p->xform, &p->UserData, &p->FreeUserData, &p->Lut, InputFormat, OutputFormat, dwFlags)) {
 
-                            // Last plugin in the declaration order takes control. We just keep
-                            // the original parameters as a logging. 
-                            // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default 
-                            // an optimized transform is not reusable. The plug-in can, however, change
-                            // the flags and make it suitable.
+                       // Last plugin in the declaration order takes control. We just keep
+                       // the original parameters as a logging. 
+                       // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default 
+                       // an optimized transform is not reusable. The plug-in can, however, change
+                       // the flags and make it suitable.
 
-                            p->ContextID = ContextID;
-                            p->InputFormat = *InputFormat;
-                            p->OutputFormat = *OutputFormat;
-                            p->dwOriginalFlags = *dwFlags;
+                       p->ContextID = ContextID;
+                       p->InputFormat = *InputFormat;
+                       p->OutputFormat = *OutputFormat;
+                       p->dwOriginalFlags = *dwFlags;
 
-                            // Fill the formatters just in case the optimized routine is interested.
-                            // No error is thrown if the formatter doesn't exist. It is up to the optimization 
-                            // factory to decide what to do in those cases.
-                            p->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
-                            p->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
-                            p->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
-                            p->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
+                       // Fill the formatters just in case the optimized routine is interested.
+                       // No error is thrown if the formatter doesn't exist. It is up to the optimization 
+                       // factory to decide what to do in those cases.
+                       p->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
+                       p->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
+                       p->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
+                       p->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
 
-                            // Save the day? (Ignore the warning)
-                            if (Plugin->OldXform) {
-                                   p->OldXform = (_cmsTransformFn) p->xform;
-                                   p->xform = _cmsTransform2toTransformAdaptor;
-                            }
-                             
-                            return p;
-                     }
-              }
+                       // Save the day? (Ignore the warning)
+                       if (Plugin->OldXform) {
+                           p->OldXform = (_cmsTransformFn)(void*)p->xform;
+                           p->xform = _cmsTransform2toTransformAdaptor;
+                       }
 
-              // Not suitable for the transform plug-in, let's check  the pipeline plug-in
-              _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags);
+                       ParalellizeIfSuitable(p);
+                       return p;
+                   }
+               }
+           }
+
+           // Not suitable for the transform plug-in, let's check  the pipeline plug-in
+           _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags);
        }
 
     // Check whatever this is a true floating point transform
-    if (_cmsFormatterIsFloat(*InputFormat) && _cmsFormatterIsFloat(*OutputFormat)) {
+    if (_cmsFormatterIsFloat(*OutputFormat)) {
 
         // Get formatter function always return a valid union, but the contents of this union may be NULL.
         p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
@@ -846,8 +940,10 @@
     }
     else {
 
+        // Formats are intended to be changed before use
         if (*InputFormat == 0 && *OutputFormat == 0) {
-            p ->FromInput = p ->ToOutput = NULL;
+            p->FromInput = UnrollNothing;
+            p->ToOutput = PackNothing;
             *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
         }
         else {
@@ -864,7 +960,7 @@
                 return NULL;
             }
 
-            BytesPerPixelInput = T_BYTES(p ->InputFormat);
+            BytesPerPixelInput = T_BYTES(*InputFormat);
             if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
                    *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
 
@@ -898,6 +994,7 @@
     p ->dwOriginalFlags = *dwFlags;
     p ->ContextID       = ContextID;
     p ->UserData        = NULL;
+    ParalellizeIfSuitable(p);
     return p;
 }
 
@@ -1055,6 +1152,15 @@
         return NULL;
     }
 
+    // Check whatever the transform is 16 bits and involves linear RGB in first profile. If so, disable optimizations
+    if (EntryColorSpace == cmsSigRgbData && T_BYTES(InputFormat) == 2 && !(dwFlags & cmsFLAGS_NOOPTIMIZE))
+    {
+        cmsFloat64Number gamma = cmsDetectRGBProfileGamma(hProfiles[0], 0.1);
+
+        if (gamma > 0 && gamma < 1.6)
+            dwFlags |= cmsFLAGS_NOOPTIMIZE;
+    }
+
     // Create a pipeline with all transformations
     Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
     if (Lut == NULL) {
@@ -1063,8 +1169,8 @@
     }
 
     // Check channel count
-    if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) ||
-        (cmsChannelsOf(ExitColorSpace)  != cmsPipelineOutputChannels(Lut))) {
+    if ((cmsChannelsOfColorSpace(EntryColorSpace) != (cmsInt32Number) cmsPipelineInputChannels(Lut)) ||
+        (cmsChannelsOfColorSpace(ExitColorSpace)  != (cmsInt32Number) cmsPipelineOutputChannels(Lut))) {
         cmsPipelineFree(Lut);
         cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
         return NULL;
diff --git a/third_party/lcms/src/lcms2_internal.h b/third_party/lcms/src/lcms2_internal.h
index bd1c86b..937c6e5 100644
--- a/third_party/lcms/src/lcms2_internal.h
+++ b/third_party/lcms/src/lcms2_internal.h
@@ -1,7 +1,7 @@
 
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2017 Marti Maria Saguer
+//  Copyright (c) 1998-2023 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -47,7 +47,7 @@
 #endif
 
 // BorlandC 5.5, VC2003 are broken on that
-#if defined(__BORLANDC__) || (_MSC_VER < 1400) // 1400 == VC++ 8.0
+#if defined(__BORLANDC__) || (defined(_MSC_VER) && (_MSC_VER < 1400)) // 1400 == VC++ 8.0
 #define sinf(x) (float)sin((float)x)
 #define sqrtf(x) (float)sqrt((float)x)
 #endif
@@ -88,6 +88,13 @@
 #   define cmsINLINE static inline
 #endif
 
+// Allow signed overflow, we know this is harmless in this particular context 
+#if defined(__clang__)
+#   define CMS_NO_SANITIZE __attribute__((no_sanitize("signed-integer-overflow")))
+#else
+#   define CMS_NO_SANITIZE 
+#endif
+
 // Other replacement functions
 #ifdef _MSC_VER
 # ifndef snprintf
@@ -99,12 +106,19 @@
 
 /// Properly define some macros to accommodate
 /// older MSVC versions.
-# if _MSC_VER <= 1700
+# if defined(_MSC_VER) && _MSC_VER <= 1700
         #include <float.h>
         #define isnan _isnan
         #define isinf(x) (!_finite((x)))
 # endif
 
+#if !defined(_MSC_VER) && (defined(__STDC_VERSION__) && __STDC_VERSION__ < 199901L)
+        #if !defined(isinf)
+        #define isinf(x) (!finite((x)))
+        #endif
+#endif
+
+
 #endif
 
 // A fast way to convert from/to 16 <-> 8 bits
@@ -180,13 +194,14 @@
     return _cmsQuickFloorWord(d);
 }
 
+// Test bed entry points---------------------------------------------------------------
+#define CMSCHECKPOINT CMSAPI
 
 // Pthread support --------------------------------------------------------------------
 #ifndef CMS_NO_PTHREADS
 
 // This is the threading support. Unfortunately, it has to be platform-dependent because 
 // windows does not support pthreads. 
-
 #ifdef CMS_IS_WINDOWS_
 
 #define WIN32_LEAN_AND_MEAN 1
@@ -268,38 +283,38 @@
 
 cmsINLINE int _cmsLockPrimitive(_cmsMutex *m)
 {
-	EnterCriticalSection(m);
-	return 0;
+    EnterCriticalSection(m);
+    return 0;
 }
 
 cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m)
 {
-	LeaveCriticalSection(m);
-	return 0;
+    LeaveCriticalSection(m);
+    return 0;
 }
-	
+    
 cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m)
 {
-	InitializeCriticalSection(m);
-	return 0;
+    InitializeCriticalSection(m);
+    return 0;
 }
 
 cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m)
 {
-	DeleteCriticalSection(m);
-	return 0;
+    DeleteCriticalSection(m);
+    return 0;
 }
 
 cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m)
 {
-	EnterCriticalSection(m);
-	return 0;
+    EnterCriticalSection(m);
+    return 0;
 }
 
 cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m)
 {
-	LeaveCriticalSection(m);
-	return 0;
+    LeaveCriticalSection(m);
+    return 0;
 }
 
 #else
@@ -313,32 +328,32 @@
 
 cmsINLINE int _cmsLockPrimitive(_cmsMutex *m)
 {
-	return pthread_mutex_lock(m);
+    return pthread_mutex_lock(m);
 }
 
 cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m)
 {
-	return pthread_mutex_unlock(m);
+    return pthread_mutex_unlock(m);
 }
-	
+    
 cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m)
 {
-	return pthread_mutex_init(m, NULL);
+    return pthread_mutex_init(m, NULL);
 }
 
 cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m)
 {
-	return pthread_mutex_destroy(m);
+    return pthread_mutex_destroy(m);
 }
 
 cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m)
 {
-	return pthread_mutex_lock(m);
+    return pthread_mutex_lock(m);
 }
 
 cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m)
 {
-	return pthread_mutex_unlock(m);
+    return pthread_mutex_unlock(m);
 }
 
 #endif
@@ -351,37 +366,37 @@
 cmsINLINE int _cmsLockPrimitive(_cmsMutex *m)
 {
     cmsUNUSED_PARAMETER(m);
-	return 0;
+    return 0;
 }
 
 cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m)
 {
     cmsUNUSED_PARAMETER(m);
-	return 0;
+    return 0;
 }
-	
+    
 cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m)
 {
     cmsUNUSED_PARAMETER(m);
-	return 0;
+    return 0;
 }
 
 cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m)
 {
     cmsUNUSED_PARAMETER(m);
-	return 0;
+    return 0;
 }
 
 cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m)
 {
     cmsUNUSED_PARAMETER(m);
-	return 0;
+    return 0;
 }
 
 cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m)
 {
     cmsUNUSED_PARAMETER(m);
-	return 0;
+    return 0;
 }
 #endif
 
@@ -423,6 +438,9 @@
 // Mutex
 cmsBool _cmsRegisterMutexPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
 
+// Paralellization
+cmsBool _cmsRegisterParallelizationPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
+
 // ---------------------------------------------------------------------------------------------------------
 
 // Suballocators. 
@@ -470,6 +488,7 @@
     OptimizationPlugin,
     TransformPlugin,
     MutexPlugin,
+    ParallelizationPlugin,
 
     // Last in list
     MemoryClientMax
@@ -705,6 +724,24 @@
 void _cmsAllocMutexPluginChunk(struct _cmsContext_struct* ctx, 
                                         const struct _cmsContext_struct* src);
 
+// Container for parallelization plug-in
+typedef struct {
+
+    cmsInt32Number      MaxWorkers;       // Number of workers to do as maximum
+    cmsInt32Number      WorkerFlags;      // reserved
+    _cmsTransform2Fn    SchedulerFn;      // callback to setup functions 
+    
+} _cmsParallelizationPluginChunkType;
+
+// The global Context0 storage for parallelization plug-in
+extern  _cmsParallelizationPluginChunkType _cmsParallelizationPluginChunk;
+
+// Allocate parallelization container.
+void _cmsAllocParallelizationPluginChunk(struct _cmsContext_struct* ctx,
+                                         const struct _cmsContext_struct* src);
+
+
+
 // ----------------------------------------------------------------------------------
 // MLU internal representation
 typedef struct {
@@ -823,10 +860,10 @@
 
 // Interpolation ---------------------------------------------------------------------------------------------------------
 
-cmsInterpParams*     _cmsComputeInterpParams(cmsContext ContextID, cmsUInt32Number nSamples, cmsUInt32Number InputChan, cmsUInt32Number OutputChan, const void* Table, cmsUInt32Number dwFlags);
-cmsInterpParams*     _cmsComputeInterpParamsEx(cmsContext ContextID, const cmsUInt32Number nSamples[], cmsUInt32Number InputChan, cmsUInt32Number OutputChan, const void* Table, cmsUInt32Number dwFlags);
-void                 _cmsFreeInterpParams(cmsInterpParams* p);
-cmsBool              _cmsSetInterpolationRoutine(cmsContext ContextID, cmsInterpParams* p);
+CMSCHECKPOINT cmsInterpParams* CMSEXPORT _cmsComputeInterpParams(cmsContext ContextID, cmsUInt32Number nSamples, cmsUInt32Number InputChan, cmsUInt32Number OutputChan, const void* Table, cmsUInt32Number dwFlags);
+cmsInterpParams*                         _cmsComputeInterpParamsEx(cmsContext ContextID, const cmsUInt32Number nSamples[], cmsUInt32Number InputChan, cmsUInt32Number OutputChan, const void* Table, cmsUInt32Number dwFlags);
+CMSCHECKPOINT void             CMSEXPORT _cmsFreeInterpParams(cmsInterpParams* p);
+cmsBool                                  _cmsSetInterpolationRoutine(cmsContext ContextID, cmsInterpParams* p);
 
 // Curves ----------------------------------------------------------------------------------------------------------------
 
@@ -876,30 +913,24 @@
 
 
 // Special Stages (cannot be saved)
-cmsStage*        _cmsStageAllocLab2XYZ(cmsContext ContextID);
-cmsStage*        _cmsStageAllocXYZ2Lab(cmsContext ContextID);
-cmsStage*        _cmsStageAllocLabPrelin(cmsContext ContextID);
-cmsStage*        _cmsStageAllocLabV2ToV4(cmsContext ContextID);
-cmsStage*        _cmsStageAllocLabV2ToV4curves(cmsContext ContextID);
-cmsStage*        _cmsStageAllocLabV4ToV2(cmsContext ContextID);
-cmsStage*        _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS);
-cmsStage*        _cmsStageAllocIdentityCurves(cmsContext ContextID, cmsUInt32Number nChannels);
-cmsStage*        _cmsStageAllocIdentityCLut(cmsContext ContextID, cmsUInt32Number nChan);
-cmsStage*        _cmsStageNormalizeFromLabFloat(cmsContext ContextID);
-cmsStage*        _cmsStageNormalizeFromXyzFloat(cmsContext ContextID);
-cmsStage*        _cmsStageNormalizeToLabFloat(cmsContext ContextID);
-cmsStage*        _cmsStageNormalizeToXyzFloat(cmsContext ContextID);
-cmsStage*        _cmsStageClipNegatives(cmsContext ContextID, cmsUInt32Number nChannels);
+CMSCHECKPOINT cmsStage*  CMSEXPORT _cmsStageAllocLab2XYZ(cmsContext ContextID);
+CMSCHECKPOINT cmsStage*  CMSEXPORT _cmsStageAllocXYZ2Lab(cmsContext ContextID);
+cmsStage*                          _cmsStageAllocLabPrelin(cmsContext ContextID);
+CMSCHECKPOINT cmsStage*  CMSEXPORT _cmsStageAllocLabV2ToV4(cmsContext ContextID);
+cmsStage*                          _cmsStageAllocLabV2ToV4curves(cmsContext ContextID);
+CMSCHECKPOINT cmsStage*  CMSEXPORT _cmsStageAllocLabV4ToV2(cmsContext ContextID);
+CMSCHECKPOINT cmsStage*  CMSEXPORT _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS);
+CMSCHECKPOINT cmsStage*  CMSEXPORT _cmsStageAllocIdentityCurves(cmsContext ContextID, cmsUInt32Number nChannels);
+CMSCHECKPOINT cmsStage*  CMSEXPORT _cmsStageAllocIdentityCLut(cmsContext ContextID, cmsUInt32Number nChan);
+cmsStage*                          _cmsStageNormalizeFromLabFloat(cmsContext ContextID);
+cmsStage*                          _cmsStageNormalizeFromXyzFloat(cmsContext ContextID);
+cmsStage*                          _cmsStageNormalizeToLabFloat(cmsContext ContextID);
+cmsStage*                          _cmsStageNormalizeToXyzFloat(cmsContext ContextID);
+cmsStage*                          _cmsStageClipNegatives(cmsContext ContextID, cmsUInt32Number nChannels);
 
 
 // For curve set only
-cmsToneCurve**     _cmsStageGetPtrToCurveSet(const cmsStage* mpe);
-
-
-// Pipeline Evaluator (in floating point)
-typedef void (* _cmsPipelineEvalFloatFn)(const cmsFloat32Number In[],
-                                         cmsFloat32Number Out[],
-                                         const void* Data);
+cmsToneCurve**  _cmsStageGetPtrToCurveSet(const cmsStage* mpe);
 
 struct _cmsPipeline_struct {
 
@@ -909,7 +940,7 @@
     // Data & evaluators
     void *Data;
 
-   _cmsOPTeval16Fn         Eval16Fn;
+   _cmsPipelineEval16Fn    Eval16Fn;
    _cmsPipelineEvalFloatFn EvalFloatFn;
    _cmsFreeUserDataFn      FreeDataFn;
    _cmsDupUserDataFn       DupDataFn;
@@ -924,9 +955,9 @@
 // Read tags using low-level function, provide necessary glue code to adapt versions, etc. All those return a brand new copy
 // of the LUTS, since ownership of original is up to the profile. The user should free allocated resources.
 
-cmsPipeline*      _cmsReadInputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent);
-cmsPipeline*      _cmsReadOutputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent);
-cmsPipeline*      _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent);
+CMSCHECKPOINT cmsPipeline* CMSEXPORT _cmsReadInputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent);
+CMSCHECKPOINT cmsPipeline* CMSEXPORT _cmsReadOutputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent);
+CMSCHECKPOINT cmsPipeline* CMSEXPORT _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent);
 
 // Special values
 cmsBool           _cmsReadMediaWhitePoint(cmsCIEXYZ* Dest, cmsHPROFILE hProfile);
@@ -934,6 +965,9 @@
 
 // Profile linker --------------------------------------------------------------------------------------------------
 
+// Link several profiles to obtain a single LUT modelling the whole color transform. Intents, Black point
+// compensation and Adaptation parameters may vary across profiles. BPC and Adaptation refers to the PCS
+// after the profile. I.e, BPC[0] refers to connexion between profile(0) and profile(1)
 cmsPipeline* _cmsLinkProfiles(cmsContext         ContextID,
                               cmsUInt32Number    nProfiles,
                               cmsUInt32Number    TheIntents[],
@@ -951,15 +985,16 @@
 
 // LUT optimization ------------------------------------------------------------------------------------------------
 
-cmsUInt16Number  _cmsQuantizeVal(cmsFloat64Number i, cmsUInt32Number MaxSamples);
-cmsUInt32Number  _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags);
+CMSCHECKPOINT cmsUInt16Number  CMSEXPORT _cmsQuantizeVal(cmsFloat64Number i, cmsUInt32Number MaxSamples);
+
+CMSAPI cmsUInt32Number  CMSEXPORT _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags);
 
 cmsBool          _cmsEndPointsBySpace(cmsColorSpaceSignature Space,
                                       cmsUInt16Number **White,
                                       cmsUInt16Number **Black,
                                       cmsUInt32Number *nOutputs);
 
-cmsBool          _cmsOptimizePipeline(cmsContext ContextID,
+CMSAPI cmsBool CMSEXPORT _cmsOptimizePipeline(cmsContext ContextID,
                                       cmsPipeline**    Lut,
                                       cmsUInt32Number  Intent,
                                       cmsUInt32Number* InputFormat,
@@ -985,17 +1020,17 @@
 cmsBool         _cmsFormatterIsFloat(cmsUInt32Number Type);
 cmsBool         _cmsFormatterIs8bit(cmsUInt32Number Type);
 
-cmsFormatter    _cmsGetFormatter(cmsContext ContextID,
-                                 cmsUInt32Number Type,          // Specific type, i.e. TYPE_RGB_8
-                                 cmsFormatterDirection Dir,
-                                 cmsUInt32Number dwFlags);
+CMSCHECKPOINT cmsFormatter CMSEXPORT _cmsGetFormatter(cmsContext ContextID,
+                                                      cmsUInt32Number Type,          // Specific type, i.e. TYPE_RGB_8
+                                                      cmsFormatterDirection Dir,
+                                                      cmsUInt32Number dwFlags);
 
 
 #ifndef CMS_NO_HALF_SUPPORT 
 
 // Half float
-cmsFloat32Number _cmsHalf2Float(cmsUInt16Number h);
-cmsUInt16Number  _cmsFloat2Half(cmsFloat32Number flt);
+CMSCHECKPOINT cmsFloat32Number CMSEXPORT _cmsHalf2Float(cmsUInt16Number h);
+CMSCHECKPOINT cmsUInt16Number  CMSEXPORT _cmsFloat2Half(cmsFloat32Number flt);
 
 #endif
 
@@ -1068,6 +1103,11 @@
     // A way to provide backwards compatibility with full xform plugins
     _cmsTransformFn OldXform;
 
+    // A one-worker transform entry for parallelization 
+    _cmsTransform2Fn Worker;
+    cmsInt32Number   MaxWorkers;
+    cmsUInt32Number  WorkerFlags;
+
 } _cmsTRANSFORM;
 
 // Copies extra channels from input to output if the original flags in the transform structure
@@ -1105,5 +1145,8 @@
 cmsBool   _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePoint, const cmsCIExyYTRIPLE* Primaries);
 
 
+// thread-safe gettime
+cmsBool _cmsGetTime(struct tm* ptr_time);
+
 #define _lcms_internal_H
 #endif
diff --git a/third_party/libopenjpeg/0003-dwt-decode.patch b/third_party/libopenjpeg/0003-dwt-decode.patch
new file mode 100644
index 0000000..3d43a89
--- /dev/null
+++ b/third_party/libopenjpeg/0003-dwt-decode.patch
@@ -0,0 +1,161 @@
+diff --git a/third_party/libopenjpeg/dwt.c b/third_party/libopenjpeg/dwt.c
+index 4164ba090..a36b7ed10 100644
+--- a/third_party/libopenjpeg/dwt.c
++++ b/third_party/libopenjpeg/dwt.c
+@@ -63,9 +63,6 @@
+ /** @defgroup DWT DWT - Implementation of a discrete wavelet transform */
+ /*@{*/
+ 
+-#define OPJ_WS(i) v->mem[(i)*2]
+-#define OPJ_WD(i) v->mem[(1+(i)*2)]
+-
+ #ifdef __AVX2__
+ /** Number of int32 values in a AVX2 register */
+ #define VREG_INT_COUNT       8
+@@ -82,6 +79,7 @@
+ 
+ typedef struct dwt_local {
+     OPJ_INT32* mem;
++    OPJ_SIZE_T mem_count;
+     OPJ_INT32 dn;   /* number of elements in high pass band */
+     OPJ_INT32 sn;   /* number of elements in low pass band */
+     OPJ_INT32 cas;  /* 0 = start on even coord, 1 = start on odd coord */
+@@ -140,7 +138,7 @@ static void opj_dwt_encode_stepsize(OPJ_INT32 stepsize, OPJ_INT32 numbps,
+ Inverse wavelet transform in 2-D.
+ */
+ static OPJ_BOOL opj_dwt_decode_tile(opj_thread_pool_t* tp,
+-                                    opj_tcd_tilecomp_t* tilec, OPJ_UINT32 i);
++                                    const opj_tcd_tilecomp_t* tilec, OPJ_UINT32 i);
+ 
+ static OPJ_BOOL opj_dwt_decode_partial_tile(
+     opj_tcd_tilecomp_t* tilec,
+@@ -181,13 +179,20 @@ static OPJ_UINT32 opj_dwt_max_resolution(opj_tcd_resolution_t* OPJ_RESTRICT r,
+ 
+ /*@}*/
+ 
+-#define OPJ_S(i) a[(i)*2]
+-#define OPJ_D(i) a[(1+(i)*2)]
+-#define OPJ_S_(i) ((i)<0?OPJ_S(0):((i)>=sn?OPJ_S(sn-1):OPJ_S(i)))
+-#define OPJ_D_(i) ((i)<0?OPJ_D(0):((i)>=dn?OPJ_D(dn-1):OPJ_D(i)))
++#define IDX_S(i) (i)*2
++#define IDX_D(i) 1 + (i)* 2
++#define UNDERFLOW_SN(i) ((i) >= sn&&sn>0)
++#define UNDERFLOW_DN(i) ((i) >= dn&&dn>0)
++#define OVERFLOW_S(i) (IDX_S(i) >= a_count)
++#define OVERFLOW_D(i) (IDX_D(i) >= a_count)
++
++#define OPJ_S(i) a[IDX_S(i)]
++#define OPJ_D(i) a[IDX_D(i)]
++#define OPJ_S_(i) ((i)<0 ? OPJ_S(0) : (UNDERFLOW_SN(i) ? OPJ_S(sn - 1) : OVERFLOW_S(i) ? OPJ_S(i - 1) : OPJ_S(i)))
++#define OPJ_D_(i) ((i)<0 ? OPJ_D(0) : (UNDERFLOW_DN(i) ? OPJ_D(dn - 1) : OVERFLOW_D(i) ? OPJ_D(i - 1) : OPJ_D(i)))
+ /* new */
+-#define OPJ_SS_(i) ((i)<0?OPJ_S(0):((i)>=dn?OPJ_S(dn-1):OPJ_S(i)))
+-#define OPJ_DD_(i) ((i)<0?OPJ_D(0):((i)>=sn?OPJ_D(sn-1):OPJ_D(i)))
++#define OPJ_SS_(i) ((i)<0 ? OPJ_S(0) : (UNDERFLOW_DN(i) ? OPJ_S(dn - 1) : OVERFLOW_S(i) ? OPJ_S(i - 1) : OPJ_S(i)))
++#define OPJ_DD_(i) ((i)<0 ? OPJ_D(0) : (UNDERFLOW_SN(i) ? OPJ_D(sn - 1) : OVERFLOW_D(i) ? OPJ_D(i - 1) : OPJ_D(i)))
+ 
+ /* <summary>                                                              */
+ /* This table contains the norms of the 5-3 wavelets for different bands. */
+@@ -296,8 +301,8 @@ static void opj_dwt_interleave_v(const opj_dwt_t* v, OPJ_INT32 *a, OPJ_INT32 x)
+ /* <summary>                            */
+ /* Inverse 5-3 wavelet transform in 1-D. */
+ /* </summary>                           */
+-static void opj_dwt_decode_1_(OPJ_INT32 *a, OPJ_INT32 dn, OPJ_INT32 sn,
+-                              OPJ_INT32 cas)
++static void opj_dwt_decode_1_(OPJ_INT32 *a, OPJ_SIZE_T a_count, OPJ_INT32 dn,
++                              OPJ_INT32 sn, OPJ_INT32 cas)
+ {
+     OPJ_INT32 i;
+ 
+@@ -326,7 +331,7 @@ static void opj_dwt_decode_1_(OPJ_INT32 *a, OPJ_INT32 dn, OPJ_INT32 sn,
+ 
+ static void opj_dwt_decode_1(const opj_dwt_t *v)
+ {
+-    opj_dwt_decode_1_(v->mem, v->dn, v->sn, v->cas);
++    opj_dwt_decode_1_(v->mem, v->mem_count, v->dn, v->sn, v->cas);
+ }
+ 
+ #endif /* STANDARD_SLOW_VERSION */
+@@ -2062,7 +2067,7 @@ static void opj_dwt_decode_v_func(void* user_data, opj_tls_t* tls)
+ /* Inverse wavelet transform in 2-D.    */
+ /* </summary>                           */
+ static OPJ_BOOL opj_dwt_decode_tile(opj_thread_pool_t* tp,
+-                                    opj_tcd_tilecomp_t* tilec, OPJ_UINT32 numres)
++        const opj_tcd_tilecomp_t* tilec, OPJ_UINT32 numres)
+ {
+     opj_dwt_t h;
+     opj_dwt_t v;
+@@ -2084,22 +2089,23 @@ static OPJ_BOOL opj_dwt_decode_tile(opj_thread_pool_t* tp,
+         return OPJ_TRUE;
+     }
+     num_threads = opj_thread_pool_get_thread_count(tp);
+-    h_mem_size = opj_dwt_max_resolution(tr, numres);
++    h.mem_count = opj_dwt_max_resolution(tr, numres);
+     /* overflow check */
+-    if (h_mem_size > (SIZE_MAX / PARALLEL_COLS_53 / sizeof(OPJ_INT32))) {
++    if (h.mem_count > (SIZE_MAX / PARALLEL_COLS_53 / sizeof(OPJ_INT32))) {
+         /* FIXME event manager error callback */
+         return OPJ_FALSE;
+     }
+     /* We need PARALLEL_COLS_53 times the height of the array, */
+     /* since for the vertical pass */
+     /* we process PARALLEL_COLS_53 columns at a time */
+-    h_mem_size *= PARALLEL_COLS_53 * sizeof(OPJ_INT32);
++    h_mem_size = h.mem_count * PARALLEL_COLS_53 * sizeof(OPJ_INT32);
+     h.mem = (OPJ_INT32*)opj_aligned_32_malloc(h_mem_size);
+     if (! h.mem) {
+         /* FIXME event manager error callback */
+         return OPJ_FALSE;
+     }
+ 
++    v.mem_count = h.mem_count;
+     v.mem = h.mem;
+ 
+     while (--numres) {
+@@ -2277,7 +2283,8 @@ static void opj_dwt_interleave_partial_v(OPJ_INT32 *dest,
+     OPJ_UNUSED(ret);
+ }
+ 
+-static void opj_dwt_decode_partial_1(OPJ_INT32 *a, OPJ_INT32 dn, OPJ_INT32 sn,
++static void opj_dwt_decode_partial_1(OPJ_INT32 *a, OPJ_SIZE_T a_count,
++                                     OPJ_INT32 dn, OPJ_INT32 sn,
+                                      OPJ_INT32 cas,
+                                      OPJ_INT32 win_l_x0,
+                                      OPJ_INT32 win_l_x1,
+@@ -2657,16 +2664,16 @@ static OPJ_BOOL opj_dwt_decode_partial_tile(
+         opj_sparse_array_int32_free(sa);
+         return OPJ_TRUE;
+     }
+-    h_mem_size = opj_dwt_max_resolution(tr, numres);
++    h.mem_count = opj_dwt_max_resolution(tr, numres);
+     /* overflow check */
+     /* in vertical pass, we process 4 columns at a time */
+-    if (h_mem_size > (SIZE_MAX / (4 * sizeof(OPJ_INT32)))) {
++    if (h.mem_count > (SIZE_MAX / (4 * sizeof(OPJ_INT32)))) {
+         /* FIXME event manager error callback */
+         opj_sparse_array_int32_free(sa);
+         return OPJ_FALSE;
+     }
+ 
+-    h_mem_size *= 4 * sizeof(OPJ_INT32);
++    h_mem_size = h.mem_count * 4 * sizeof(OPJ_INT32);
+     h.mem = (OPJ_INT32*)opj_aligned_32_malloc(h_mem_size);
+     if (! h.mem) {
+         /* FIXME event manager error callback */
+@@ -2674,6 +2681,7 @@ static OPJ_BOOL opj_dwt_decode_partial_tile(
+         return OPJ_FALSE;
+     }
+ 
++    v.mem_count = h.mem_count;
+     v.mem = h.mem;
+ 
+     for (resno = 1; resno < numres; resno ++) {
+@@ -2784,7 +2792,7 @@ static OPJ_BOOL opj_dwt_decode_partial_tile(
+                                              win_ll_x1,
+                                              win_hl_x0,
+                                              win_hl_x1);
+-                opj_dwt_decode_partial_1(h.mem, h.dn, h.sn, h.cas,
++                opj_dwt_decode_partial_1(h.mem, h.mem_count, h.dn, h.sn, h.cas,
+                                          (OPJ_INT32)win_ll_x0,
+                                          (OPJ_INT32)win_ll_x1,
+                                          (OPJ_INT32)win_hl_x0,
diff --git a/third_party/libopenjpeg/0005-jp2_apply_pclr.patch b/third_party/libopenjpeg/0005-jp2_apply_pclr.patch
new file mode 100644
index 0000000..85e0698
--- /dev/null
+++ b/third_party/libopenjpeg/0005-jp2_apply_pclr.patch
@@ -0,0 +1,35 @@
+diff --git a/third_party/libopenjpeg/jp2.c b/third_party/libopenjpeg/jp2.c
+index 7c065ba74..a5790b267 100644
+--- a/third_party/libopenjpeg/jp2.c
++++ b/third_party/libopenjpeg/jp2.c
+@@ -1079,8 +1079,8 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
+             assert(pcol == 0);
+             new_comps[i] = old_comps[cmp];
+         } else {
+-            assert(i == pcol);
+-            new_comps[pcol] = old_comps[cmp];
++            assert( i == pcol ); // probably wrong?
++            new_comps[i] = old_comps[cmp];
+         }
+ 
+         /* Palette mapping: */
+@@ -1108,7 +1108,7 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
+         pcol = cmap[i].pcol;
+         src = old_comps[cmp].data;
+         assert(src); /* verified above */
+-        max = new_comps[pcol].w * new_comps[pcol].h;
++        max = new_comps[i].w * new_comps[i].h;
+ 
+         /* Direct use: */
+         if (cmap[i].mtyp == 0) {
+@@ -1118,8 +1118,8 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
+                 dst[j] = src[j];
+             }
+         } else {
+-            assert(i == pcol);
+-            dst = new_comps[pcol].data;
++            assert( i == pcol ); // probably wrong?
++            dst = new_comps[i].data;
+             assert(dst);
+             for (j = 0; j < max; ++j) {
+                 /* The index */
diff --git a/third_party/libopenjpeg/0006-tcd_init_tile.patch b/third_party/libopenjpeg/0006-tcd_init_tile.patch
new file mode 100644
index 0000000..19532ea
--- /dev/null
+++ b/third_party/libopenjpeg/0006-tcd_init_tile.patch
@@ -0,0 +1,16 @@
+diff --git a/third_party/libopenjpeg/tcd.c b/third_party/libopenjpeg/tcd.c
+index 6442669d6..4c728d4c6 100644
+--- a/third_party/libopenjpeg/tcd.c
++++ b/third_party/libopenjpeg/tcd.c
+@@ -819,6 +819,11 @@ static INLINE OPJ_BOOL opj_tcd_init_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
+         if (isEncoder) {
+             OPJ_SIZE_T l_tile_data_size;
+ 
++            if (l_tilec->x0 >= l_tilec->x1 || l_tilec->y0 >= l_tilec->y1) {
++                opj_event_msg(manager, EVT_ERROR, "Invalid tile data\n");
++                return OPJ_FALSE;
++            }
++
+             /* compute l_data_size with overflow check */
+             OPJ_SIZE_T w = (OPJ_SIZE_T)(l_tilec->x1 - l_tilec->x0);
+             OPJ_SIZE_T h = (OPJ_SIZE_T)(l_tilec->y1 - l_tilec->y0);
diff --git a/third_party/libopenjpeg/0007-jp2_read_cmap.patch b/third_party/libopenjpeg/0007-jp2_read_cmap.patch
new file mode 100644
index 0000000..02293ed
--- /dev/null
+++ b/third_party/libopenjpeg/0007-jp2_read_cmap.patch
@@ -0,0 +1,13 @@
+diff --git a/third_party/libopenjpeg/jp2.c b/third_party/libopenjpeg/jp2.c
+index a5790b267..a0f639d8e 100644
+--- a/third_party/libopenjpeg/jp2.c
++++ b/third_party/libopenjpeg/jp2.c
+@@ -1308,7 +1308,7 @@ static OPJ_BOOL opj_jp2_read_cmap(opj_jp2_t * jp2,
+ 
+ 
+     for (i = 0; i < nr_channels; ++i) {
+-        opj_read_bytes(p_cmap_header_data, &l_value, 2);            /* CMP^i */
++        opj_read_bytes_BE(p_cmap_header_data, &l_value, 2);         /* CMP^i */
+         p_cmap_header_data += 2;
+         cmap[i].cmp = (OPJ_UINT16) l_value;
+ 
diff --git a/third_party/libopenjpeg/0009-opj_pi_next.patch b/third_party/libopenjpeg/0009-opj_pi_next.patch
new file mode 100644
index 0000000..8aa80c1
--- /dev/null
+++ b/third_party/libopenjpeg/0009-opj_pi_next.patch
@@ -0,0 +1,34 @@
+diff --git a/third_party/libopenjpeg/pi.c b/third_party/libopenjpeg/pi.c
+index 4f7dd50f1..1430d12a9 100644
+--- a/third_party/libopenjpeg/pi.c
++++ b/third_party/libopenjpeg/pi.c
+@@ -464,6 +464,9 @@ static OPJ_BOOL opj_pi_next_rpcl(opj_pi_iterator_t * pi)
+                                                  (comp->dy << levelno)), res->pdy)
+                            - opj_uint_floordivpow2(try0, res->pdy);
+                     pi->precno = prci + prcj * res->pw;
++                    if (pi->precno >= res->pw * res->ph) {
++                      return OPJ_FALSE;
++                    }
+                     for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) {
+                         index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno *
+                                 pi->step_c + pi->precno * pi->step_p;
+@@ -602,6 +605,9 @@ static OPJ_BOOL opj_pi_next_pcrl(opj_pi_iterator_t * pi)
+                                                  (comp->dy << levelno)), res->pdy)
+                            - opj_uint_floordivpow2(try0, res->pdy);
+                     pi->precno = prci + prcj * res->pw;
++                    if (pi->precno >= res->pw * res->ph) {
++                      return OPJ_FALSE;
++                    }
+                     for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) {
+                         index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno *
+                                 pi->step_c + pi->precno * pi->step_p;
+@@ -737,6 +743,9 @@ static OPJ_BOOL opj_pi_next_cprl(opj_pi_iterator_t * pi)
+                                                  (comp->dy << levelno)), res->pdy)
+                            - opj_uint_floordivpow2(try0, res->pdy);
+                     pi->precno = (OPJ_UINT32)(prci + prcj * res->pw);
++                    if (pi->precno >= res->pw * res->ph) {
++                      return OPJ_FALSE;
++                    }
+                     for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) {
+                         index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno *
+                                 pi->step_c + pi->precno * pi->step_p;
diff --git a/third_party/libopenjpeg/0011-j2k_update_image_data.patch b/third_party/libopenjpeg/0011-j2k_update_image_data.patch
new file mode 100644
index 0000000..3314495
--- /dev/null
+++ b/third_party/libopenjpeg/0011-j2k_update_image_data.patch
@@ -0,0 +1,17 @@
+diff --git a/third_party/libopenjpeg/j2k.c b/third_party/libopenjpeg/j2k.c
+index 8e343ab2e..5e1494394 100644
+--- a/third_party/libopenjpeg/j2k.c
++++ b/third_party/libopenjpeg/j2k.c
+@@ -9882,6 +9882,12 @@ static OPJ_BOOL opj_j2k_update_image_data(opj_tcd_t * p_tcd,
+          * */
+         assert(res_x0 >= 0);
+         assert(res_x1 >= 0);
++
++        /* Prevent bad casting to unsigned values in the subsequent lines. */
++        if ( res_x0 < 0 || res_x1 < 0 || res_y0 < 0 || res_y1 < 0 ) {
++            return OPJ_FALSE;
++        }
++
+         if (l_x0_dest < (OPJ_UINT32)res_x0) {
+             l_start_x_dest = (OPJ_UINT32)res_x0 - l_x0_dest;
+             l_offset_x0_src = 0;
diff --git a/third_party/libopenjpeg/0012-mct_sse.patch b/third_party/libopenjpeg/0012-mct_sse.patch
new file mode 100644
index 0000000..3010975
--- /dev/null
+++ b/third_party/libopenjpeg/0012-mct_sse.patch
@@ -0,0 +1,59 @@
+diff --git a/third_party/libopenjpeg/mct.c b/third_party/libopenjpeg/mct.c
+index 88c8f4092..c4c2e732e 100644
+--- a/third_party/libopenjpeg/mct.c
++++ b/third_party/libopenjpeg/mct.c
+@@ -37,13 +37,15 @@
+  * POSSIBILITY OF SUCH DAMAGE.
+  */
+ 
+-#ifdef __SSE__
++#if defined(__SSE__) && !defined(_M_IX86) && !defined(__i386)
++#define USE_SSE
+ #include <xmmintrin.h>
+ #endif
+-#ifdef __SSE2__
++#if defined(__SSE2__) && !defined(_M_IX86) && !defined(__i386)
++#define USE_SSE2
+ #include <emmintrin.h>
+ #endif
+-#ifdef __SSE4_1__
++#if defined(__SSE4_1__) && !defined(_M_IX86) && !defined(__i386)
+ #include <smmintrin.h>
+ #endif
+ 
+@@ -72,7 +74,7 @@ const OPJ_FLOAT64 * opj_mct_get_mct_norms_real()
+ /* <summary> */
+ /* Forward reversible MCT. */
+ /* </summary> */
+-#ifdef __SSE2__
++#ifdef USE_SSE2
+ void opj_mct_encode(
+     OPJ_INT32* OPJ_RESTRICT c0,
+     OPJ_INT32* OPJ_RESTRICT c1,
+@@ -141,7 +143,7 @@ void opj_mct_encode(
+ /* <summary> */
+ /* Inverse reversible MCT. */
+ /* </summary> */
+-#ifdef __SSE2__
++#ifdef USE_SSE2
+ void opj_mct_decode(
+     OPJ_INT32* OPJ_RESTRICT c0,
+     OPJ_INT32* OPJ_RESTRICT c1,
+@@ -216,7 +218,7 @@ void opj_mct_encode_real(
+     OPJ_SIZE_T n)
+ {
+     OPJ_SIZE_T i;
+-#ifdef __SSE__
++#ifdef USE_SSE
+     const __m128 YR = _mm_set1_ps(0.299f);
+     const __m128 YG = _mm_set1_ps(0.587f);
+     const __m128 YB = _mm_set1_ps(0.114f);
+@@ -286,7 +288,7 @@ void opj_mct_decode_real(
+     OPJ_SIZE_T n)
+ {
+     OPJ_SIZE_T i;
+-#ifdef __SSE__
++#ifdef USE_SSE
+     __m128 vrv, vgu, vgv, vbu;
+     vrv = _mm_set1_ps(1.402f);
+     vgu = _mm_set1_ps(0.34413f);
diff --git a/third_party/libopenjpeg/0014-opj_jp2_read_ihdr_leak.patch b/third_party/libopenjpeg/0014-opj_jp2_read_ihdr_leak.patch
new file mode 100644
index 0000000..c221701
--- /dev/null
+++ b/third_party/libopenjpeg/0014-opj_jp2_read_ihdr_leak.patch
@@ -0,0 +1,20 @@
+diff --git a/third_party/libopenjpeg/jp2.c b/third_party/libopenjpeg/jp2.c
+index a0f639d8e..38715b80f 100644
+--- a/third_party/libopenjpeg/jp2.c
++++ b/third_party/libopenjpeg/jp2.c
+@@ -599,6 +599,7 @@ static OPJ_BOOL opj_jp2_read_ihdr(opj_jp2_t *jp2,
+     }
+ 
+     /* allocate memory for components */
++    opj_free(jp2->comps);
+     jp2->comps = (opj_jp2_comps_t*) opj_calloc(jp2->numcomps,
+                  sizeof(opj_jp2_comps_t));
+     if (jp2->comps == 0) {
+@@ -1897,6 +1898,7 @@ void opj_jp2_setup_decoder(opj_jp2_t *jp2, opj_dparameters_t *parameters)
+ 
+     /* further JP2 initializations go here */
+     jp2->color.jp2_has_colr = 0;
++    jp2->comps = NULL;
+     jp2->ignore_pclr_cmap_cdef = parameters->flags &
+                                  OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG;
+ }
diff --git a/third_party/libopenjpeg/0015-read_SPCod_SPCoc_overflow.patch b/third_party/libopenjpeg/0015-read_SPCod_SPCoc_overflow.patch
new file mode 100644
index 0000000..41c9ecd
--- /dev/null
+++ b/third_party/libopenjpeg/0015-read_SPCod_SPCoc_overflow.patch
@@ -0,0 +1,15 @@
+diff --git a/third_party/libopenjpeg/j2k.c b/third_party/libopenjpeg/j2k.c
+index 5e1494394..413dbdd9f 100644
+--- a/third_party/libopenjpeg/j2k.c
++++ b/third_party/libopenjpeg/j2k.c
+@@ -10537,6 +10537,10 @@ static OPJ_BOOL opj_j2k_read_SPCod_SPCoc(opj_j2k_t *p_j2k,
+             p_j2k->m_specific_param.m_decoder.m_default_tcp;
+ 
+     /* precondition again */
++    if (compno >= p_j2k->m_private_image->numcomps) {
++        return OPJ_FALSE;
++    }
++
+     assert(compno < p_j2k->m_private_image->numcomps);
+ 
+     l_tccp = &l_tcp->tccps[compno];
diff --git a/third_party/libopenjpeg/0016-read_SQcd_SQcc_overflow.patch b/third_party/libopenjpeg/0016-read_SQcd_SQcc_overflow.patch
new file mode 100644
index 0000000..b14d553
--- /dev/null
+++ b/third_party/libopenjpeg/0016-read_SQcd_SQcc_overflow.patch
@@ -0,0 +1,15 @@
+diff --git a/third_party/libopenjpeg/j2k.c b/third_party/libopenjpeg/j2k.c
+index 413dbdd9f..1932fe20c 100644
+--- a/third_party/libopenjpeg/j2k.c
++++ b/third_party/libopenjpeg/j2k.c
+@@ -10885,7 +10885,9 @@ static OPJ_BOOL opj_j2k_read_SQcd_SQcc(opj_j2k_t *p_j2k,
+             p_j2k->m_specific_param.m_decoder.m_default_tcp;
+ 
+     /* precondition again*/
+-    assert(p_comp_no <  p_j2k->m_private_image->numcomps);
++    if (p_comp_no >=  p_j2k->m_private_image->numcomps) {
++        return OPJ_FALSE;
++    }
+ 
+     l_tccp = &l_tcp->tccps[p_comp_no];
+     l_current_ptr = p_header_data;
diff --git a/third_party/libopenjpeg/0019-tcd_init_tile.patch b/third_party/libopenjpeg/0019-tcd_init_tile.patch
new file mode 100644
index 0000000..d6995a7
--- /dev/null
+++ b/third_party/libopenjpeg/0019-tcd_init_tile.patch
@@ -0,0 +1,14 @@
+diff --git a/third_party/libopenjpeg/tcd.c b/third_party/libopenjpeg/tcd.c
+index 4c728d4c6..b9f571410 100644
+--- a/third_party/libopenjpeg/tcd.c
++++ b/third_party/libopenjpeg/tcd.c
+@@ -1094,6 +1094,9 @@ static INLINE OPJ_BOOL opj_tcd_init_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
+                     l_current_precinct->ch = (OPJ_UINT32)((brcblkyend - tlcblkystart) >>
+                                                           cblkheightexpn);
+ 
++                    if (l_current_precinct->cw && ((OPJ_UINT32)-1) / l_current_precinct->cw < l_current_precinct->ch) {
++                        return OPJ_FALSE;
++                    }
+                     l_nb_code_blocks = l_current_precinct->cw * l_current_precinct->ch;
+                     /*fprintf(stderr, "\t\t\t\t precinct_cw = %d x recinct_ch = %d\n",l_current_precinct->cw, l_current_precinct->ch);      */
+                     if ((((OPJ_UINT32) - 1) / (OPJ_UINT32)sizeof_block) <
diff --git a/third_party/libopenjpeg/0022-jp2_apply_pclr_overflow.patch b/third_party/libopenjpeg/0022-jp2_apply_pclr_overflow.patch
new file mode 100644
index 0000000..d978f06
--- /dev/null
+++ b/third_party/libopenjpeg/0022-jp2_apply_pclr_overflow.patch
@@ -0,0 +1,51 @@
+diff --git a/third_party/libopenjpeg/jp2.c b/third_party/libopenjpeg/jp2.c
+index 38715b80f..dcaf3872c 100644
+--- a/third_party/libopenjpeg/jp2.c
++++ b/third_party/libopenjpeg/jp2.c
+@@ -1064,6 +1064,14 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
+     }
+ 
+     old_comps = image->comps;
++    /* Overflow check: prevent integer overflow */
++    for (i = 0; i < nr_channels; ++i) {
++      cmp = cmap[i].cmp;
++      if (old_comps[cmp].h == 0 || old_comps[cmp].w > ((OPJ_UINT32)-1) / sizeof(OPJ_INT32) / old_comps[cmp].h) {
++        return OPJ_FALSE;
++      }
++    }
++
+     new_comps = (opj_image_comp_t*)
+                 opj_malloc(nr_channels * sizeof(opj_image_comp_t));
+     if (!new_comps) {
+@@ -1108,20 +1116,26 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
+         cmp = cmap[i].cmp;
+         pcol = cmap[i].pcol;
+         src = old_comps[cmp].data;
+-        assert(src); /* verified above */
++        dst = new_comps[i].data;
+         max = new_comps[i].w * new_comps[i].h;
+ 
++        /* Prevent null pointer access */
++        if (!src || !dst) {
++          for (j = 0; j < nr_channels; ++j) {
++            opj_free(new_comps[j].data);
++          }
++          opj_free(new_comps);
++          new_comps = NULL;
++          return OPJ_FALSE;
++        }
++
+         /* Direct use: */
+         if (cmap[i].mtyp == 0) {
+-            dst = new_comps[i].data;
+-            assert(dst);
+             for (j = 0; j < max; ++j) {
+                 dst[j] = src[j];
+             }
+         } else {
+             assert( i == pcol ); // probably wrong?
+-            dst = new_comps[i].data;
+-            assert(dst);
+             for (j = 0; j < max; ++j) {
+                 /* The index */
+                 if ((k = src[j]) < 0) {
diff --git a/third_party/libopenjpeg/0023-opj_j2k_read_mct_records.patch b/third_party/libopenjpeg/0023-opj_j2k_read_mct_records.patch
new file mode 100644
index 0000000..674d4c1
--- /dev/null
+++ b/third_party/libopenjpeg/0023-opj_j2k_read_mct_records.patch
@@ -0,0 +1,31 @@
+diff --git a/third_party/libopenjpeg/j2k.c b/third_party/libopenjpeg/j2k.c
+index 1932fe20c..d24564cc2 100644
+--- a/third_party/libopenjpeg/j2k.c
++++ b/third_party/libopenjpeg/j2k.c
+@@ -5749,6 +5749,7 @@ static OPJ_BOOL opj_j2k_read_mct(opj_j2k_t *p_j2k,
+     OPJ_UINT32 l_tmp;
+     OPJ_UINT32 l_indix;
+     opj_mct_data_t * l_mct_data;
++    OPJ_BOOL new_mct = OPJ_FALSE;
+ 
+     /* preconditions */
+     assert(p_header_data != 00);
+@@ -5836,7 +5837,7 @@ static OPJ_BOOL opj_j2k_read_mct(opj_j2k_t *p_j2k,
+         }
+ 
+         l_mct_data = l_tcp->m_mct_records + l_tcp->m_nb_mct_records;
+-        ++l_tcp->m_nb_mct_records;
++        new_mct = OPJ_TRUE;
+     }
+ 
+     if (l_mct_data->m_data) {
+@@ -5868,6 +5869,9 @@ static OPJ_BOOL opj_j2k_read_mct(opj_j2k_t *p_j2k,
+ 
+     l_mct_data->m_data_size = p_header_size;
+ 
++    if (new_mct) {
++            ++l_tcp->m_nb_mct_records;
++    }
+     return OPJ_TRUE;
+ }
+ 
diff --git a/third_party/libopenjpeg/0025-opj_j2k_add_mct_null_data.patch b/third_party/libopenjpeg/0025-opj_j2k_add_mct_null_data.patch
new file mode 100644
index 0000000..1027138
--- /dev/null
+++ b/third_party/libopenjpeg/0025-opj_j2k_add_mct_null_data.patch
@@ -0,0 +1,22 @@
+diff --git a/third_party/libopenjpeg/j2k.c b/third_party/libopenjpeg/j2k.c
+index d24564cc2..889c2cfc8 100644
+--- a/third_party/libopenjpeg/j2k.c
++++ b/third_party/libopenjpeg/j2k.c
+@@ -6397,7 +6397,7 @@ static OPJ_BOOL opj_j2k_add_mct(opj_tcp_t * p_tcp, opj_image_t * p_image,
+     if (l_deco_array) {
+         l_data_size = MCT_ELEMENT_SIZE[l_deco_array->m_element_type] * p_image->numcomps
+                       * p_image->numcomps;
+-        if (l_deco_array->m_data_size != l_data_size) {
++        if (l_deco_array->m_data_size != l_data_size || ! l_deco_array->m_data) {
+             return OPJ_FALSE;
+         }
+ 
+@@ -6418,7 +6418,7 @@ static OPJ_BOOL opj_j2k_add_mct(opj_tcp_t * p_tcp, opj_image_t * p_image,
+     if (l_offset_array) {
+         l_data_size = MCT_ELEMENT_SIZE[l_offset_array->m_element_type] *
+                       p_image->numcomps;
+-        if (l_offset_array->m_data_size != l_data_size) {
++        if (l_offset_array->m_data_size != l_data_size || ! l_offset_array->m_data) {
+             return OPJ_FALSE;
+         }
+ 
diff --git a/third_party/libopenjpeg/0026-use_opj_uint_ceildiv.patch b/third_party/libopenjpeg/0026-use_opj_uint_ceildiv.patch
new file mode 100644
index 0000000..e65f4c6
--- /dev/null
+++ b/third_party/libopenjpeg/0026-use_opj_uint_ceildiv.patch
@@ -0,0 +1,73 @@
+diff --git a/third_party/libopenjpeg/j2k.c b/third_party/libopenjpeg/j2k.c
+index 889c2cfc8..711dd73e8 100644
+--- a/third_party/libopenjpeg/j2k.c
++++ b/third_party/libopenjpeg/j2k.c
+@@ -2299,10 +2299,8 @@ static OPJ_BOOL opj_j2k_read_siz(opj_j2k_t *p_j2k,
+     }
+ 
+     /* Compute the number of tiles */
+-    l_cp->tw = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)(l_image->x1 - l_cp->tx0),
+-                                           (OPJ_INT32)l_cp->tdx);
+-    l_cp->th = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)(l_image->y1 - l_cp->ty0),
+-                                           (OPJ_INT32)l_cp->tdy);
++    l_cp->tw = opj_uint_ceildiv(l_image->x1 - l_cp->tx0, l_cp->tdx);
++    l_cp->th = opj_uint_ceildiv(l_image->y1 - l_cp->ty0, l_cp->tdy);
+ 
+     /* Check that the number of tiles is valid */
+     if (l_cp->tw == 0 || l_cp->th == 0 || l_cp->tw > 65535 / l_cp->th) {
+@@ -2319,12 +2317,10 @@ static OPJ_BOOL opj_j2k_read_siz(opj_j2k_t *p_j2k,
+             (p_j2k->m_specific_param.m_decoder.m_start_tile_x - l_cp->tx0) / l_cp->tdx;
+         p_j2k->m_specific_param.m_decoder.m_start_tile_y =
+             (p_j2k->m_specific_param.m_decoder.m_start_tile_y - l_cp->ty0) / l_cp->tdy;
+-        p_j2k->m_specific_param.m_decoder.m_end_tile_x = (OPJ_UINT32)opj_int_ceildiv((
+-                    OPJ_INT32)(p_j2k->m_specific_param.m_decoder.m_end_tile_x - l_cp->tx0),
+-                (OPJ_INT32)l_cp->tdx);
+-        p_j2k->m_specific_param.m_decoder.m_end_tile_y = (OPJ_UINT32)opj_int_ceildiv((
+-                    OPJ_INT32)(p_j2k->m_specific_param.m_decoder.m_end_tile_y - l_cp->ty0),
+-                (OPJ_INT32)l_cp->tdy);
++        p_j2k->m_specific_param.m_decoder.m_end_tile_x = opj_uint_ceildiv(
++            p_j2k->m_specific_param.m_decoder.m_end_tile_x - l_cp->tx0, l_cp->tdx);
++        p_j2k->m_specific_param.m_decoder.m_end_tile_y = opj_uint_ceildiv(
++            p_j2k->m_specific_param.m_decoder.m_end_tile_y - l_cp->ty0, l_cp->tdy);
+     } else {
+         p_j2k->m_specific_param.m_decoder.m_start_tile_x = 0;
+         p_j2k->m_specific_param.m_decoder.m_start_tile_y = 0;
+@@ -10035,10 +10029,8 @@ static OPJ_BOOL opj_j2k_update_image_dimensions(opj_image_t* p_image,
+             return OPJ_FALSE;
+         }
+ 
+-        l_img_comp->x0 = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)p_image->x0,
+-                         (OPJ_INT32)l_img_comp->dx);
+-        l_img_comp->y0 = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)p_image->y0,
+-                         (OPJ_INT32)l_img_comp->dy);
++        l_img_comp->x0 = opj_uint_ceildiv(p_image->x0, l_img_comp->dx);
++        l_img_comp->y0 = opj_uint_ceildiv(p_image->y0, l_img_comp->dy);
+         l_comp_x1 = opj_int_ceildiv((OPJ_INT32)p_image->x1, (OPJ_INT32)l_img_comp->dx);
+         l_comp_y1 = opj_int_ceildiv((OPJ_INT32)p_image->y1, (OPJ_INT32)l_img_comp->dy);
+ 
+@@ -11950,10 +11942,8 @@ OPJ_BOOL opj_j2k_get_tile(opj_j2k_t *p_j2k,
+ 
+         l_img_comp->factor = p_j2k->m_private_image->comps[compno].factor;
+ 
+-        l_img_comp->x0 = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)p_image->x0,
+-                         (OPJ_INT32)l_img_comp->dx);
+-        l_img_comp->y0 = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)p_image->y0,
+-                         (OPJ_INT32)l_img_comp->dy);
++        l_img_comp->x0 = opj_uint_ceildiv(p_image->x0, l_img_comp->dx);
++        l_img_comp->y0 = opj_uint_ceildiv(p_image->y0, l_img_comp->dy);
+         l_comp_x1 = opj_int_ceildiv((OPJ_INT32)p_image->x1, (OPJ_INT32)l_img_comp->dx);
+         l_comp_y1 = opj_int_ceildiv((OPJ_INT32)p_image->y1, (OPJ_INT32)l_img_comp->dy);
+ 
+@@ -12304,10 +12294,8 @@ static void opj_get_tile_dimensions(opj_image_t * l_image,
+ 
+     *l_width  = (OPJ_UINT32)(l_tilec->x1 - l_tilec->x0);
+     *l_height = (OPJ_UINT32)(l_tilec->y1 - l_tilec->y0);
+-    *l_offset_x = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)l_image->x0,
+-                  (OPJ_INT32)l_img_comp->dx);
+-    *l_offset_y = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)l_image->y0,
+-                  (OPJ_INT32)l_img_comp->dy);
++    *l_offset_x = opj_uint_ceildiv(l_image->x0, l_img_comp->dx);
++    *l_offset_y = opj_uint_ceildiv(l_image->y0, l_img_comp->dy);
+     *l_image_width = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)l_image->x1 -
+                      (OPJ_INT32)l_image->x0, (OPJ_INT32)l_img_comp->dx);
+     *l_stride = *l_image_width - *l_width;
diff --git a/third_party/libopenjpeg/0034-opj_malloc.patch b/third_party/libopenjpeg/0034-opj_malloc.patch
new file mode 100644
index 0000000..3ad0d57
--- /dev/null
+++ b/third_party/libopenjpeg/0034-opj_malloc.patch
@@ -0,0 +1,88 @@
+diff --git a/third_party/libopenjpeg/opj_malloc.cc b/third_party/libopenjpeg/opj_malloc.cc
+new file mode 100644
+--- /dev/null
++++ b/third_party/libopenjpeg/opj_malloc.cc
+@@ -0,0 +1,42 @@
++// Copyright 2020 The PDFium Authors
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++// Deliberately not including opj_malloc.h, which has poisoned malloc and
++// friends.
++
++#include "core/fxcrt/fx_memory.h"
++#include "third_party/base/memory/aligned_memory.h"
++
++extern "C" {
++
++void* opj_malloc(size_t size) {
++  return FXMEM_DefaultAlloc(size);
++}
++
++void* opj_calloc(size_t numOfElements, size_t sizeOfElements) {
++  return FXMEM_DefaultCalloc(numOfElements, sizeOfElements);
++}
++
++void* opj_aligned_malloc(size_t size) {
++  return size ? pdfium::base::AlignedAlloc(size, 16) : nullptr;
++}
++
++void opj_aligned_free(void* ptr) {
++  pdfium::base::AlignedFree(ptr);
++}
++
++void* opj_aligned_32_malloc(size_t size) {
++  return size ? pdfium::base::AlignedAlloc(size, 32) : nullptr;
++}
++
++void* opj_realloc(void* m, size_t s) {
++  return FXMEM_DefaultRealloc(m, s);
++}
++
++void opj_free(void* m) {
++  if (m)
++    FXMEM_DefaultFree(m);
++}
++
++}  // extern "C"
+diff --git a/third_party/libopenjpeg/opj_malloc.h b/third_party/libopenjpeg/opj_malloc.h
+--- a/third_party/libopenjpeg/opj_malloc.h
++++ b/third_party/libopenjpeg/opj_malloc.h
+@@ -33,6 +33,11 @@
+ #define OPJ_MALLOC_H
+ 
+ #include <stddef.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
+ /**
+ @file opj_malloc.h
+ @brief Internal functions
+@@ -68,7 +73,6 @@ Allocate memory aligned to a 16 byte bou
+ @return Returns a void pointer to the allocated space, or NULL if there is insufficient memory available
+ */
+ void * opj_aligned_malloc(size_t size);
+-void * opj_aligned_realloc(void *ptr, size_t size);
+ void opj_aligned_free(void* ptr);
+ 
+ /**
+@@ -77,7 +81,6 @@ Allocate memory aligned to a 32 byte bou
+ @return Returns a void pointer to the allocated space, or NULL if there is insufficient memory available
+ */
+ void * opj_aligned_32_malloc(size_t size);
+-void * opj_aligned_32_realloc(void *ptr, size_t size);
+ 
+ /**
+ Reallocate memory blocks.
+@@ -102,5 +105,8 @@ void opj_free(void * m);
+ 
+ /*@}*/
+ 
+-#endif /* OPJ_MALLOC_H */
++#ifdef __cplusplus
++}  // extern "C"
++#endif
+ 
++#endif /* OPJ_MALLOC_H */
diff --git a/third_party/libopenjpeg/0035-opj_image_data_free.patch b/third_party/libopenjpeg/0035-opj_image_data_free.patch
new file mode 100644
index 0000000..436b31d
--- /dev/null
+++ b/third_party/libopenjpeg/0035-opj_image_data_free.patch
@@ -0,0 +1,13 @@
+diff --git a/third_party/libopenjpeg/jp2.c b/third_party/libopenjpeg/jp2.c
+index dcaf3872c..02f3d04c7 100644
+--- a/third_party/libopenjpeg/jp2.c
++++ b/third_party/libopenjpeg/jp2.c
+@@ -1122,7 +1122,7 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
+         /* Prevent null pointer access */
+         if (!src || !dst) {
+           for (j = 0; j < nr_channels; ++j) {
+-            opj_free(new_comps[j].data);
++            opj_image_data_free(new_comps[j].data);
+           }
+           opj_free(new_comps);
+           new_comps = NULL;
diff --git a/third_party/libopenjpeg/0039-opj_mqc_renorme.patch b/third_party/libopenjpeg/0039-opj_mqc_renorme.patch
new file mode 100644
index 0000000..feac335
--- /dev/null
+++ b/third_party/libopenjpeg/0039-opj_mqc_renorme.patch
@@ -0,0 +1,16 @@
+diff --git a/third_party/libopenjpeg/mqc.c b/third_party/libopenjpeg/mqc.c
+index 4cbfabd03..3caab9e7c 100644
+--- a/third_party/libopenjpeg/mqc.c
++++ b/third_party/libopenjpeg/mqc.c
+@@ -370,11 +370,6 @@ void opj_mqc_erterm_enc(opj_mqc_t *mqc)
+     }
+ }
+ 
+-static INLINE void opj_mqc_renorme(opj_mqc_t *mqc)
+-{
+-    opj_mqc_renorme_macro(mqc, mqc->a, mqc->c, mqc->ct);
+-}
+-
+ /**
+ Encode the most probable symbol
+ @param mqc MQC handle
diff --git a/third_party/libopenjpeg/0041-remove_opj_clock.patch b/third_party/libopenjpeg/0041-remove_opj_clock.patch
new file mode 100644
index 0000000..036a3bf
--- /dev/null
+++ b/third_party/libopenjpeg/0041-remove_opj_clock.patch
@@ -0,0 +1,12 @@
+diff --git a/third_party/libopenjpeg/opj_includes.h b/third_party/libopenjpeg/opj_includes.h
+index 0a8628c96..43c00a556 100644
+--- a/third_party/libopenjpeg/opj_includes.h
++++ b/third_party/libopenjpeg/opj_includes.h
+@@ -219,7 +219,6 @@ typedef unsigned int OPJ_BITFIELD;
+ #define OPJ_UNUSED(x) (void)x
+ 
+ #include "opj_inttypes.h"
+-#include "opj_clock.h"
+ #include "opj_malloc.h"
+ #include "event.h"
+ #include "function_list.h"
diff --git a/third_party/libopenjpeg/0042-popcnt-windows-arm64.patch b/third_party/libopenjpeg/0042-popcnt-windows-arm64.patch
new file mode 100644
index 0000000..93606e3
--- /dev/null
+++ b/third_party/libopenjpeg/0042-popcnt-windows-arm64.patch
@@ -0,0 +1,22 @@
+From 098bb874db85c185e2e3598f735fece7552e6dca Mon Sep 17 00:00:00 2001
+From: Alexander Neumann <30894796+Neumann-A@users.noreply.github.com>
+Date: Mon, 16 May 2022 23:10:26 +0200
+Subject: [PATCH] Fix windows arm builds
+
+---
+ src/lib/openjp2/ht_dec.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/lib/openjp2/ht_dec.c b/src/lib/openjp2/ht_dec.c
+index 1eb4d525f..e2f3afd6a 100644
+--- a/src/lib/openjp2/ht_dec.c
++++ b/src/lib/openjp2/ht_dec.c
+@@ -69,7 +69,7 @@ static OPJ_BOOL only_cleanup_pass_is_decoded = OPJ_FALSE;
+ static INLINE
+ OPJ_UINT32 population_count(OPJ_UINT32 val)
+ {
+-#ifdef OPJ_COMPILER_MSVC
++#if defined(OPJ_COMPILER_MSVC) && (defined(_M_IX86) || defined(_M_AMD64))
+     return (OPJ_UINT32)__popcnt(val);
+ #elif (defined OPJ_COMPILER_GNUC)
+     return (OPJ_UINT32)__builtin_popcount(val);
diff --git a/third_party/libopenjpeg/0043-mel_init.patch b/third_party/libopenjpeg/0043-mel_init.patch
new file mode 100644
index 0000000..7197bac
--- /dev/null
+++ b/third_party/libopenjpeg/0043-mel_init.patch
@@ -0,0 +1,59 @@
+commit 4da04cd3e88a0280be526e16077c540a45cbbfa8
+Author: Aous Naman <aous72@yahoo.com>
+Date:   Fri Aug 12 02:29:40 2022 +1000
+
+    Replace the assert in mel_init to an if statement to address an issue with fuzzing. (#1436)
+    
+    Modified the mel_init code to replace the assert statement with an if statement, returning false when an incorrect sequence of bytes are encountered in the MEL segment.  Similar code should be added to the main MEL decoding subrountine, but the change is more involved; in any case, an incorrect sequence produces incorrect results, but should not be harmful or cause a crash.
+
+diff --git a/src/lib/openjp2/ht_dec.c b/src/lib/openjp2/ht_dec.c
+index a803d1bb..62a6c9e1 100644
+--- a/src/lib/openjp2/ht_dec.c
++++ b/src/lib/openjp2/ht_dec.c
+@@ -294,7 +294,7 @@ void mel_decode(dec_mel_t *melp)
+   *  @param [in]  scup is the length of MEL+VLC segments
+   */
+ static INLINE
+-void mel_init(dec_mel_t *melp, OPJ_UINT8* bbuf, int lcup, int scup)
++OPJ_BOOL mel_init(dec_mel_t *melp, OPJ_UINT8* bbuf, int lcup, int scup)
+ {
+     int num;
+     int i;
+@@ -316,7 +316,9 @@ void mel_init(dec_mel_t *melp, OPJ_UINT8* bbuf, int lcup, int scup)
+         OPJ_UINT64 d;
+         int d_bits;
+ 
+-        assert(melp->unstuff == OPJ_FALSE || melp->data[0] <= 0x8F);
++        if (melp->unstuff == OPJ_TRUE && melp->data[0] > 0x8F) {
++            return OPJ_FALSE;
++        }
+         d = (melp->size > 0) ? *melp->data : 0xFF; // if buffer is consumed
+         // set data to 0xFF
+         if (melp->size == 1) {
+@@ -332,6 +334,7 @@ void mel_init(dec_mel_t *melp, OPJ_UINT8* bbuf, int lcup, int scup)
+     }
+     melp->tmp <<= (64 - melp->bits); //push all the way up so the first bit
+     // is the MSB
++    return OPJ_TRUE;
+ }
+ 
+ //************************************************************************/
+@@ -1374,7 +1377,17 @@ OPJ_BOOL opj_t1_ht_decode_cblk(opj_t1_t *t1,
+     }
+ 
+     // init structures
+-    mel_init(&mel, coded_data, lcup, scup);
++    if (mel_init(&mel, coded_data, lcup, scup) == OPJ_FALSE) {
++        if (p_manager_mutex) {
++            opj_mutex_lock(p_manager_mutex);
++        }
++        opj_event_msg(p_manager, EVT_ERROR, "Malformed HT codeblock. "
++                      "Incorrect MEL segment sequence.\n");
++        if (p_manager_mutex) {
++            opj_mutex_unlock(p_manager_mutex);
++        }
++        return OPJ_FALSE;
++    }
+     rev_init(&vlc, coded_data, lcup, scup);
+     frwd_init(&magsgn, coded_data, lcup - scup, 0xFF);
+     if (num_passes > 1) { // needs to be tested
diff --git a/third_party/libopenjpeg/0044-opj_t1_allocate_buffers.patch b/third_party/libopenjpeg/0044-opj_t1_allocate_buffers.patch
new file mode 100644
index 0000000..3d77b74
--- /dev/null
+++ b/third_party/libopenjpeg/0044-opj_t1_allocate_buffers.patch
@@ -0,0 +1,28 @@
+commit 0535bfc3b7d5cd6fc73a7d4a6749a338fc5d7703
+Author: Yuan <zodf0055980@gmail.com>
+Date:   Tue May 31 17:55:12 2022 +0800
+
+    HT_DEC: Fix opj_t1_allocate_buffers malloc size error (#1426) (fixes #1413)
+
+diff --git a/src/lib/openjp2/ht_dec.c b/src/lib/openjp2/ht_dec.c
+index e2f3afd6..a803d1bb 100644
+--- a/src/lib/openjp2/ht_dec.c
++++ b/src/lib/openjp2/ht_dec.c
+@@ -1063,7 +1063,7 @@ static OPJ_BOOL opj_t1_allocate_buffers(
+         if (flagssize > t1->flagssize) {
+ 
+             opj_aligned_free(t1->flags);
+-            t1->flags = (opj_flag_t*) opj_aligned_malloc(flagssize);
++            t1->flags = (opj_flag_t*) opj_aligned_malloc(flagssize * sizeof(opj_flag_t));
+             if (!t1->flags) {
+                 /* FIXME event manager error callback */
+                 return OPJ_FALSE;
+@@ -1071,7 +1071,7 @@ static OPJ_BOOL opj_t1_allocate_buffers(
+         }
+         t1->flagssize = flagssize;
+ 
+-        memset(t1->flags, 0, flagssize);
++        memset(t1->flags, 0, flagssize * sizeof(opj_flag_t));
+     }
+ 
+     t1->w = w;
diff --git a/third_party/libopenjpeg/0045-openjp2-j2k-replace-sprintf-calls-with-snprintf.patch b/third_party/libopenjpeg/0045-openjp2-j2k-replace-sprintf-calls-with-snprintf.patch
new file mode 100644
index 0000000..01ef487
--- /dev/null
+++ b/third_party/libopenjpeg/0045-openjp2-j2k-replace-sprintf-calls-with-snprintf.patch
@@ -0,0 +1,82 @@
+https://github.com/uclouvain/openjpeg/pull/1450
+https://patch-diff.githubusercontent.com/raw/uclouvain/openjpeg/pull/1450.patch
+
+From 093ccb0ecdba7d5c4b5363e7dda33b1769fcc08a Mon Sep 17 00:00:00 2001
+From: Mark Mentovai <mark@chromium.org>
+Date: Mon, 7 Nov 2022 09:32:02 -0500
+Subject: [PATCH] openjp2/j2k: replace sprintf calls with snprintf
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This makes it possible to build j2k.c without warnings using the macOS
+13 SDK. Calls to sprintf are replaced with snprintf, passing appropriate
+buffer sizes.
+
+It doesn’t appear that any of the changed uses of sprintf were actually
+unsafe, so no behavior change is expected aside from SDK compatibility.
+
+The macOS 13 SDK deprecates sprintf as it’s difficult to use safely. The
+deprecation warning message is visible when building C++, but it is not
+normally visible when building plain C code due to a quirk in how
+sprintf is declared in the SDK. However, the deprecation message is
+visible when building plain C under Address Sanitizer
+(-fsanitize=address). This discrepancy was discovered at
+https://crbug.com/1381706 and reported to Apple with a copy at
+https://openradar.appspot.com/FB11761475.
+
+The macOS 13 SDK is packaged in Xcode 14.1, released on 2022-11-01. This
+also affects the iOS 16 SDK and other 2022-era Apple OS SDKs packaged in
+Xcode 14.0, released on 2022-09-12.
+
+j2k.c is visible to the Chromium build via PDFium, and this change is
+needed to allow Chromium to move forward to the macOS 13 SDK.
+
+This change is limited to src/lib/openjp2. Other uses of sprintf were
+found throughout openjpeg.
+---
+ src/lib/openjp2/j2k.c | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/src/lib/openjp2/j2k.c b/src/lib/openjp2/j2k.c
+index 923bd8916..354415df7 100644
+--- a/src/lib/openjp2/j2k.c
++++ b/src/lib/openjp2/j2k.c
+@@ -7954,21 +7954,24 @@ OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k,
+ 
+         /* UniPG>> */
+ #ifdef USE_JPWL
+-        cp->comment = (char*)opj_malloc(clen + strlen(version) + 11);
++        const size_t cp_comment_buf_size = clen + strlen(version) + 11;
++        cp->comment = (char*)opj_malloc(cp_comment_buf_size);
+         if (!cp->comment) {
+             opj_event_msg(p_manager, EVT_ERROR,
+                           "Not enough memory to allocate comment string\n");
+             return OPJ_FALSE;
+         }
+-        sprintf(cp->comment, "%s%s with JPWL", comment, version);
++        snprintf(cp->comment, cp_comment_buf_size, "%s%s with JPWL",
++                 comment, version);
+ #else
+-        cp->comment = (char*)opj_malloc(clen + strlen(version) + 1);
++        const size_t cp_comment_buf_size = clen + strlen(version) + 1;
++        cp->comment = (char*)opj_malloc(cp_comment_buf_size);
+         if (!cp->comment) {
+             opj_event_msg(p_manager, EVT_ERROR,
+                           "Not enough memory to allocate comment string\n");
+             return OPJ_FALSE;
+         }
+-        sprintf(cp->comment, "%s%s", comment, version);
++        snprintf(cp->comment, cp_comment_buf_size, "%s%s", comment, version);
+ #endif
+         /* <<UniPG */
+     }
+@@ -11973,7 +11976,7 @@ static OPJ_BOOL opj_j2k_move_data_from_codec_to_output_image(opj_j2k_t * p_j2k,
+             p_image->comps[compno].data = p_j2k->m_output_image->comps[compno].data;
+ #if 0
+             char fn[256];
+-            sprintf(fn, "/tmp/%d.raw", compno);
++            snprintf(fn, sizeof fn, "/tmp/%d.raw", compno);
+             FILE *debug = fopen(fn, "wb");
+             fwrite(p_image->comps[compno].data, sizeof(OPJ_INT32),
+                    p_image->comps[compno].w * p_image->comps[compno].h, debug);
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
new file mode 100644
index 0000000..638c817
--- /dev/null
+++ b/third_party/libopenjpeg/README.pdfium
@@ -0,0 +1,37 @@
+Name: OpenJPEG
+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
+
+Description:
+JPEG 2000 library.
+
+Local Modifications:
+
+0003-dwt-decode.patch: Check array bounds for opj_dwt_decode_1() and friends.
+0005-jp2_apply_pclr.patch: Fix out of bounds access.
+0006-tcd_init_tile.patch: Fix a divide by zero bug in opj_tcd_init_tile().
+0007-jp2_read_cmap.patch: Fix wrong rendering on greyscale images with index colorspace.
+0009-opj_pi_next.patch: Fix potential bad precno value in opj_pi_next* functions.
+0011-j2k_update_image_data.patch: Prevent bad signed -> unsigned casting.
+0012-mct_sse.patch: Don't use SSE intrinsics in 32-bit builds.
+0014-opj_jp2_read_ihdr_leak.patch: Memory leak in opj_jp2_read_ihdr().
+0015-read_SPCod_SPCoc_overflow.patch: Prevent a buffer overflow in opj_j2k_read_SPCod_SPCoc.
+0016-read_SQcd_SQcc_overflow.patch: Prevent a buffer overflow in opj_j2k_read_SQcd_SQcc.
+0019-tcd_init_tile.patch: Prevent integer overflows during calculation of |l_nb_code_blocks_size|.
+0022-jp2_apply_pclr_overflow.patch: Prevent integer overflow in opj_jp2_apply_pclr.
+0023-opj_j2k_read_mct_records.patch: Fix opj_j2k_read to prevent heap-use-after-free.
+0025-opj_j2k_add_mct_null_data.patch: Check m_data != null before trying to read from it.
+0026-use_opj_uint_ceildiv.patch: Remove (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)a, (OPJ_INT32) b).
+0034-opj_malloc.patch: PDFium changes in opj_malloc.
+0035-opj_image_data_free.patch: Use the right free function in opj_jp2_apply_pclr.
+0039-opj_mqc_renorme.patch: Remove unused opj_mqc_renorme().
+0041-remove_opj_clock.patch: Remove unused opj_clock.h include.
+0042-popcnt-windows-arm64.patch: Backport to fix Windows arm64 build.
+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/libopenjpeg20/bio.c b/third_party/libopenjpeg/bio.c
similarity index 100%
rename from third_party/libopenjpeg20/bio.c
rename to third_party/libopenjpeg/bio.c
diff --git a/third_party/libopenjpeg20/bio.h b/third_party/libopenjpeg/bio.h
similarity index 100%
rename from third_party/libopenjpeg20/bio.h
rename to third_party/libopenjpeg/bio.h
diff --git a/third_party/libopenjpeg20/cio.c b/third_party/libopenjpeg/cio.c
similarity index 100%
rename from third_party/libopenjpeg20/cio.c
rename to third_party/libopenjpeg/cio.c
diff --git a/third_party/libopenjpeg/cio.h b/third_party/libopenjpeg/cio.h
new file mode 100644
index 0000000..7caee30
--- /dev/null
+++ b/third_party/libopenjpeg/cio.h
@@ -0,0 +1,412 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
+ * Copyright (c) 2012, CS Systemes d'Information, France
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef OPJ_CIO_H
+#define OPJ_CIO_H
+/**
+@file cio.h
+@brief Implementation of a byte input-output process (CIO)
+
+The functions in CIO.C have for goal to realize a byte input / output process.
+*/
+
+/** @defgroup CIO CIO - byte input-output stream */
+/*@{*/
+
+#include "opj_config_private.h"
+
+/* ----------------------------------------------------------------------- */
+
+#if defined(OPJ_BIG_ENDIAN)
+#define opj_write_bytes     opj_write_bytes_BE
+#define opj_read_bytes      opj_read_bytes_BE
+#define opj_write_double    opj_write_double_BE
+#define opj_read_double     opj_read_double_BE
+#define opj_write_float     opj_write_float_BE
+#define opj_read_float      opj_read_float_BE
+#else
+#define opj_write_bytes     opj_write_bytes_LE
+#define opj_read_bytes      opj_read_bytes_LE
+#define opj_write_double    opj_write_double_LE
+#define opj_read_double     opj_read_double_LE
+#define opj_write_float     opj_write_float_LE
+#define opj_read_float      opj_read_float_LE
+#endif
+
+
+#define OPJ_STREAM_STATUS_OUTPUT  0x1U
+#define OPJ_STREAM_STATUS_INPUT   0x2U
+#define OPJ_STREAM_STATUS_END     0x4U
+#define OPJ_STREAM_STATUS_ERROR   0x8U
+
+/**
+Byte input-output stream.
+*/
+typedef struct opj_stream_private {
+    /**
+     * User data, be it files, ... The actual data depends on the type of the stream.
+     */
+    void *                  m_user_data;
+
+    /**
+     * Pointer to function to free m_user_data (NULL at initialization)
+     * when destroying the stream. If pointer is NULL the function is not
+     * called and the m_user_data is not freed (even if non-NULL).
+     */
+    opj_stream_free_user_data_fn        m_free_user_data_fn;
+
+    /**
+     * User data length
+     */
+    OPJ_UINT64              m_user_data_length;
+
+    /**
+     * Pointer to actual read function (NULL at the initialization of the cio.
+     */
+    opj_stream_read_fn      m_read_fn;
+
+    /**
+     * Pointer to actual write function (NULL at the initialization of the cio.
+     */
+    opj_stream_write_fn     m_write_fn;
+
+    /**
+     * Pointer to actual skip function (NULL at the initialization of the cio.
+     * There is no seek function to prevent from back and forth slow procedures.
+     */
+    opj_stream_skip_fn      m_skip_fn;
+
+    /**
+     * Pointer to actual seek function (if available).
+     */
+    opj_stream_seek_fn      m_seek_fn;
+
+    /**
+     * Actual data stored into the stream if read from. Data is read by chunk of fixed size.
+     * you should never access this data directly.
+     */
+    OPJ_BYTE *                  m_stored_data;
+
+    /**
+     * Pointer to the current read data.
+     */
+    OPJ_BYTE *                  m_current_data;
+
+    /**
+    * FIXME DOC.
+    */
+    OPJ_OFF_T(* m_opj_skip)(struct opj_stream_private *, OPJ_OFF_T,
+                            struct opj_event_mgr *);
+
+    /**
+    * FIXME DOC.
+    */
+    OPJ_BOOL(* m_opj_seek)(struct opj_stream_private *, OPJ_OFF_T,
+                           struct opj_event_mgr *);
+
+    /**
+     * number of bytes containing in the buffer.
+     */
+    OPJ_SIZE_T          m_bytes_in_buffer;
+
+    /**
+     * The number of bytes read/written from the beginning of the stream
+     */
+    OPJ_OFF_T           m_byte_offset;
+
+    /**
+     * The size of the buffer.
+     */
+    OPJ_SIZE_T          m_buffer_size;
+
+    /**
+     * Flags to tell the status of the stream.
+     * Used with OPJ_STREAM_STATUS_* defines.
+     */
+    OPJ_UINT32 m_status;
+
+}
+opj_stream_private_t;
+
+/** @name Exported functions (see also openjpeg.h) */
+/*@{*/
+/* ----------------------------------------------------------------------- */
+/**
+ * Write some bytes to the given data buffer, this function is used in Big Endian cpus.
+ * @param p_buffer      pointer the data buffer to write data to.
+ * @param p_value       the value to write
+ * @param p_nb_bytes    the number of bytes to write
+*/
+void opj_write_bytes_BE(OPJ_BYTE * p_buffer, OPJ_UINT32 p_value,
+                        OPJ_UINT32 p_nb_bytes);
+
+/**
+ * Reads some bytes from the given data buffer, this function is used in Big Endian cpus.
+ * @param p_buffer      pointer the data buffer to read data from.
+ * @param p_value       pointer to the value that will store the data.
+ * @param p_nb_bytes    the nb bytes to read.
+ * @return              the number of bytes read or -1 if an error occurred.
+ */
+void opj_read_bytes_BE(const OPJ_BYTE * p_buffer, OPJ_UINT32 * p_value,
+                       OPJ_UINT32 p_nb_bytes);
+
+/**
+ * Write some bytes to the given data buffer, this function is used in Little Endian cpus.
+ * @param p_buffer      pointer the data buffer to write data to.
+ * @param p_value       the value to write
+ * @param p_nb_bytes    the number of bytes to write
+ * @return              the number of bytes written or -1 if an error occurred
+*/
+void opj_write_bytes_LE(OPJ_BYTE * p_buffer, OPJ_UINT32 p_value,
+                        OPJ_UINT32 p_nb_bytes);
+
+/**
+ * Reads some bytes from the given data buffer, this function is used in Little Endian cpus.
+ * @param p_buffer      pointer the data buffer to read data from.
+ * @param p_value       pointer to the value that will store the data.
+ * @param p_nb_bytes    the nb bytes to read.
+ * @return              the number of bytes read or -1 if an error occurred.
+ */
+void opj_read_bytes_LE(const OPJ_BYTE * p_buffer, OPJ_UINT32 * p_value,
+                       OPJ_UINT32 p_nb_bytes);
+
+
+/**
+ * Write some bytes to the given data buffer, this function is used in Little Endian cpus.
+ * @param p_buffer      pointer the data buffer to write data to.
+ * @param p_value       the value to write
+ */
+void opj_write_double_LE(OPJ_BYTE * p_buffer, OPJ_FLOAT64 p_value);
+
+/***
+ * Write some bytes to the given data buffer, this function is used in Big Endian cpus.
+ * @param p_buffer      pointer the data buffer to write data to.
+ * @param p_value       the value to write
+ */
+void opj_write_double_BE(OPJ_BYTE * p_buffer, OPJ_FLOAT64 p_value);
+
+/**
+ * Reads some bytes from the given data buffer, this function is used in Little Endian cpus.
+ * @param p_buffer      pointer the data buffer to read data from.
+ * @param p_value       pointer to the value that will store the data.
+ */
+void opj_read_double_LE(const OPJ_BYTE * p_buffer, OPJ_FLOAT64 * p_value);
+
+/**
+ * Reads some bytes from the given data buffer, this function is used in Big Endian cpus.
+ * @param p_buffer      pointer the data buffer to read data from.
+ * @param p_value       pointer to the value that will store the data.
+ */
+void opj_read_double_BE(const OPJ_BYTE * p_buffer, OPJ_FLOAT64 * p_value);
+
+/**
+ * Reads some bytes from the given data buffer, this function is used in Little Endian cpus.
+ * @param p_buffer      pointer the data buffer to read data from.
+ * @param p_value       pointer to the value that will store the data.
+ */
+void opj_read_float_LE(const OPJ_BYTE * p_buffer, OPJ_FLOAT32 * p_value);
+
+/**
+ * Reads some bytes from the given data buffer, this function is used in Big Endian cpus.
+ * @param p_buffer      pointer the data buffer to read data from.
+ * @param p_value       pointer to the value that will store the data.
+ */
+void opj_read_float_BE(const OPJ_BYTE * p_buffer, OPJ_FLOAT32 * p_value);
+
+/**
+ * Write some bytes to the given data buffer, this function is used in Little Endian cpus.
+ * @param p_buffer      pointer the data buffer to write data to.
+ * @param p_value       the value to write
+ */
+void opj_write_float_LE(OPJ_BYTE * p_buffer, OPJ_FLOAT32 p_value);
+
+/***
+ * Write some bytes to the given data buffer, this function is used in Big Endian cpus.
+ * @param p_buffer      pointer the data buffer to write data to.
+ * @param p_value       the value to write
+ */
+void opj_write_float_BE(OPJ_BYTE * p_buffer, OPJ_FLOAT32 p_value);
+
+/**
+ * Reads some bytes from the stream.
+ * @param       p_stream    the stream to read data from.
+ * @param       p_buffer    pointer to the data buffer that will receive the data.
+ * @param       p_size      number of bytes to read.
+ * @param       p_event_mgr the user event manager to be notified of special events.
+ * @return      the number of bytes read, or -1 if an error occurred or if the stream is at the end.
+ */
+OPJ_SIZE_T opj_stream_read_data(opj_stream_private_t * p_stream,
+                                OPJ_BYTE * p_buffer, OPJ_SIZE_T p_size, struct opj_event_mgr * p_event_mgr);
+
+/**
+ * Writes some bytes to the stream.
+ * @param       p_stream    the stream to write data to.
+ * @param       p_buffer    pointer to the data buffer holds the data to be writtent.
+ * @param       p_size      number of bytes to write.
+ * @param       p_event_mgr the user event manager to be notified of special events.
+ * @return      the number of bytes writtent, or -1 if an error occurred.
+ */
+OPJ_SIZE_T opj_stream_write_data(opj_stream_private_t * p_stream,
+                                 const OPJ_BYTE * p_buffer, OPJ_SIZE_T p_size,
+                                 struct opj_event_mgr * p_event_mgr);
+
+/**
+ * Writes the content of the stream buffer to the stream.
+ * @param       p_stream    the stream to write data to.
+ * @param       p_event_mgr the user event manager to be notified of special events.
+ * @return      true if the data could be flushed, false else.
+ */
+OPJ_BOOL opj_stream_flush(opj_stream_private_t * p_stream,
+                          struct opj_event_mgr * p_event_mgr);
+
+/**
+ * Skips a number of bytes from the stream.
+ * @param       p_stream    the stream to skip data from.
+ * @param       p_size      the number of bytes to skip.
+ * @param       p_event_mgr the user event manager to be notified of special events.
+ * @return      the number of bytes skipped, or -1 if an error occurred.
+ */
+OPJ_OFF_T opj_stream_skip(opj_stream_private_t * p_stream, OPJ_OFF_T p_size,
+                          struct opj_event_mgr * p_event_mgr);
+
+/**
+ * Tells the byte offset on the stream (similar to ftell).
+ *
+ * @param       p_stream    the stream to get the information from.
+ *
+ * @return      the current position o fthe stream.
+ */
+OPJ_OFF_T opj_stream_tell(const opj_stream_private_t * p_stream);
+
+
+/**
+ * Get the number of bytes left before the end of the stream (similar to cio_numbytesleft).
+ *
+ * @param       p_stream    the stream to get the information from.
+ *
+ * @return      Number of bytes left before the end of the stream.
+ */
+OPJ_OFF_T opj_stream_get_number_byte_left(const opj_stream_private_t *
+        p_stream);
+
+/**
+ * Skips a number of bytes from the stream.
+ * @param       p_stream    the stream to skip data from.
+ * @param       p_size      the number of bytes to skip.
+ * @param       p_event_mgr the user event manager to be notified of special events.
+ * @return      the number of bytes skipped, or -1 if an error occurred.
+ */
+OPJ_OFF_T opj_stream_write_skip(opj_stream_private_t * p_stream,
+                                OPJ_OFF_T p_size, struct opj_event_mgr * p_event_mgr);
+
+/**
+ * Skips a number of bytes from the stream.
+ * @param       p_stream    the stream to skip data from.
+ * @param       p_size      the number of bytes to skip.
+ * @param       p_event_mgr the user event manager to be notified of special events.
+ * @return      the number of bytes skipped, or -1 if an error occurred.
+ */
+OPJ_OFF_T opj_stream_read_skip(opj_stream_private_t * p_stream,
+                               OPJ_OFF_T p_size, struct opj_event_mgr * p_event_mgr);
+
+/**
+ * Skips a number of bytes from the stream.
+ * @param       p_stream    the stream to skip data from.
+ * @param       p_size      the number of bytes to skip.
+ * @param       p_event_mgr the user event manager to be notified of special events.
+ * @return      OPJ_TRUE if success, or OPJ_FALSE if an error occurred.
+ */
+OPJ_BOOL opj_stream_read_seek(opj_stream_private_t * p_stream, OPJ_OFF_T p_size,
+                              struct opj_event_mgr * p_event_mgr);
+
+/**
+ * Skips a number of bytes from the stream.
+ * @param       p_stream    the stream to skip data from.
+ * @param       p_size      the number of bytes to skip.
+ * @param       p_event_mgr the user event manager to be notified of special events.
+ * @return      the number of bytes skipped, or -1 if an error occurred.
+ */
+OPJ_BOOL opj_stream_write_seek(opj_stream_private_t * p_stream,
+                               OPJ_OFF_T p_size, struct opj_event_mgr * p_event_mgr);
+
+/**
+ * Seeks a number of bytes from the stream.
+ * @param       p_stream    the stream to skip data from.
+ * @param       p_size      the number of bytes to skip.
+ * @param       p_event_mgr the user event manager to be notified of special events.
+ * @return      true if the stream is seekable.
+ */
+OPJ_BOOL opj_stream_seek(opj_stream_private_t * p_stream, OPJ_OFF_T p_size,
+                         struct opj_event_mgr * p_event_mgr);
+
+/**
+ * Tells if the given stream is seekable.
+ */
+OPJ_BOOL opj_stream_has_seek(const opj_stream_private_t * p_stream);
+
+/**
+ * FIXME DOC.
+ */
+OPJ_SIZE_T opj_stream_default_read(void * p_buffer, OPJ_SIZE_T p_nb_bytes,
+                                   void * p_user_data);
+
+/**
+ * FIXME DOC.
+ */
+OPJ_SIZE_T opj_stream_default_write(void * p_buffer, OPJ_SIZE_T p_nb_bytes,
+                                    void * p_user_data);
+
+/**
+ * FIXME DOC.
+ */
+OPJ_OFF_T opj_stream_default_skip(OPJ_OFF_T p_nb_bytes, void * p_user_data);
+
+/**
+ * FIXME DOC.
+ */
+OPJ_BOOL opj_stream_default_seek(OPJ_OFF_T p_nb_bytes, void * p_user_data);
+
+/* ----------------------------------------------------------------------- */
+/*@}*/
+
+/*@}*/
+
+
+#endif /* OPJ_CIO_H */
+
diff --git a/third_party/libopenjpeg/dwt.c b/third_party/libopenjpeg/dwt.c
new file mode 100644
index 0000000..0184aa1
--- /dev/null
+++ b/third_party/libopenjpeg/dwt.c
@@ -0,0 +1,3775 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2007, Jonathan Ballard <dzonatas@dzonux.net>
+ * Copyright (c) 2007, Callum Lerwick <seg@haxxed.com>
+ * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <assert.h>
+
+#define OPJ_SKIP_POISON
+#include "opj_includes.h"
+
+#ifdef __SSE__
+#include <xmmintrin.h>
+#endif
+#ifdef __SSE2__
+#include <emmintrin.h>
+#endif
+#ifdef __SSSE3__
+#include <tmmintrin.h>
+#endif
+#ifdef __AVX2__
+#include <immintrin.h>
+#endif
+
+#if defined(__GNUC__)
+#pragma GCC poison malloc calloc realloc free
+#endif
+
+/** @defgroup DWT DWT - Implementation of a discrete wavelet transform */
+/*@{*/
+
+#ifdef __AVX2__
+/** Number of int32 values in a AVX2 register */
+#define VREG_INT_COUNT       8
+#else
+/** Number of int32 values in a SSE2 register */
+#define VREG_INT_COUNT       4
+#endif
+
+/** Number of columns that we can process in parallel in the vertical pass */
+#define PARALLEL_COLS_53     (2*VREG_INT_COUNT)
+
+/** @name Local data structures */
+/*@{*/
+
+typedef struct dwt_local {
+    OPJ_INT32* mem;
+    OPJ_SIZE_T mem_count;
+    OPJ_INT32 dn;   /* number of elements in high pass band */
+    OPJ_INT32 sn;   /* number of elements in low pass band */
+    OPJ_INT32 cas;  /* 0 = start on even coord, 1 = start on odd coord */
+} opj_dwt_t;
+
+#define NB_ELTS_V8  8
+
+typedef union {
+    OPJ_FLOAT32 f[NB_ELTS_V8];
+} opj_v8_t;
+
+typedef struct v8dwt_local {
+    opj_v8_t*   wavelet ;
+    OPJ_INT32       dn ;  /* number of elements in high pass band */
+    OPJ_INT32       sn ;  /* number of elements in low pass band */
+    OPJ_INT32       cas ; /* 0 = start on even coord, 1 = start on odd coord */
+    OPJ_UINT32      win_l_x0; /* start coord in low pass band */
+    OPJ_UINT32      win_l_x1; /* end coord in low pass band */
+    OPJ_UINT32      win_h_x0; /* start coord in high pass band */
+    OPJ_UINT32      win_h_x1; /* end coord in high pass band */
+} opj_v8dwt_t ;
+
+/* From table F.4 from the standard */
+static const OPJ_FLOAT32 opj_dwt_alpha =  -1.586134342f;
+static const OPJ_FLOAT32 opj_dwt_beta  =  -0.052980118f;
+static const OPJ_FLOAT32 opj_dwt_gamma = 0.882911075f;
+static const OPJ_FLOAT32 opj_dwt_delta = 0.443506852f;
+
+static const OPJ_FLOAT32 opj_K      = 1.230174105f;
+static const OPJ_FLOAT32 opj_invK   = (OPJ_FLOAT32)(1.0 / 1.230174105);
+
+/*@}*/
+
+/** @name Local static functions */
+/*@{*/
+
+/**
+Forward lazy transform (horizontal)
+*/
+static void opj_dwt_deinterleave_h(const OPJ_INT32 * OPJ_RESTRICT a,
+                                   OPJ_INT32 * OPJ_RESTRICT b,
+                                   OPJ_INT32 dn,
+                                   OPJ_INT32 sn, OPJ_INT32 cas);
+
+/**
+Forward 9-7 wavelet transform in 1-D
+*/
+static void opj_dwt_encode_1_real(void *a, OPJ_INT32 dn, OPJ_INT32 sn,
+                                  OPJ_INT32 cas);
+/**
+Explicit calculation of the Quantization Stepsizes
+*/
+static void opj_dwt_encode_stepsize(OPJ_INT32 stepsize, OPJ_INT32 numbps,
+                                    opj_stepsize_t *bandno_stepsize);
+/**
+Inverse wavelet transform in 2-D.
+*/
+static OPJ_BOOL opj_dwt_decode_tile(opj_thread_pool_t* tp,
+                                    const opj_tcd_tilecomp_t* tilec, OPJ_UINT32 i);
+
+static OPJ_BOOL opj_dwt_decode_partial_tile(
+    opj_tcd_tilecomp_t* tilec,
+    OPJ_UINT32 numres);
+
+/* Forward transform, for the vertical pass, processing cols columns */
+/* where cols <= NB_ELTS_V8 */
+/* Where void* is a OPJ_INT32* for 5x3 and OPJ_FLOAT32* for 9x7 */
+typedef void (*opj_encode_and_deinterleave_v_fnptr_type)(
+    void *array,
+    void *tmp,
+    OPJ_UINT32 height,
+    OPJ_BOOL even,
+    OPJ_UINT32 stride_width,
+    OPJ_UINT32 cols);
+
+/* Where void* is a OPJ_INT32* for 5x3 and OPJ_FLOAT32* for 9x7 */
+typedef void (*opj_encode_and_deinterleave_h_one_row_fnptr_type)(
+    void *row,
+    void *tmp,
+    OPJ_UINT32 width,
+    OPJ_BOOL even);
+
+static OPJ_BOOL opj_dwt_encode_procedure(opj_thread_pool_t* tp,
+        opj_tcd_tilecomp_t * tilec,
+        opj_encode_and_deinterleave_v_fnptr_type p_encode_and_deinterleave_v,
+        opj_encode_and_deinterleave_h_one_row_fnptr_type
+        p_encode_and_deinterleave_h_one_row);
+
+static OPJ_UINT32 opj_dwt_max_resolution(opj_tcd_resolution_t* OPJ_RESTRICT r,
+        OPJ_UINT32 i);
+
+/* <summary>                             */
+/* Inverse 9-7 wavelet transform in 1-D. */
+/* </summary>                            */
+
+/*@}*/
+
+/*@}*/
+
+#define IDX_S(i) (i)*2
+#define IDX_D(i) 1 + (i)* 2
+#define UNDERFLOW_SN(i) ((i) >= sn&&sn>0)
+#define UNDERFLOW_DN(i) ((i) >= dn&&dn>0)
+#define OVERFLOW_S(i) (IDX_S(i) >= a_count)
+#define OVERFLOW_D(i) (IDX_D(i) >= a_count)
+
+#define OPJ_S(i) a[IDX_S(i)]
+#define OPJ_D(i) a[IDX_D(i)]
+#define OPJ_S_(i) ((i)<0 ? OPJ_S(0) : (UNDERFLOW_SN(i) ? OPJ_S(sn - 1) : OVERFLOW_S(i) ? OPJ_S(i - 1) : OPJ_S(i)))
+#define OPJ_D_(i) ((i)<0 ? OPJ_D(0) : (UNDERFLOW_DN(i) ? OPJ_D(dn - 1) : OVERFLOW_D(i) ? OPJ_D(i - 1) : OPJ_D(i)))
+/* new */
+#define OPJ_SS_(i) ((i)<0 ? OPJ_S(0) : (UNDERFLOW_DN(i) ? OPJ_S(dn - 1) : OVERFLOW_S(i) ? OPJ_S(i - 1) : OPJ_S(i)))
+#define OPJ_DD_(i) ((i)<0 ? OPJ_D(0) : (UNDERFLOW_SN(i) ? OPJ_D(sn - 1) : OVERFLOW_D(i) ? OPJ_D(i - 1) : OPJ_D(i)))
+
+/* <summary>                                                              */
+/* This table contains the norms of the 5-3 wavelets for different bands. */
+/* </summary>                                                             */
+/* FIXME! the array should really be extended up to 33 resolution levels */
+/* See https://github.com/uclouvain/openjpeg/issues/493 */
+static const OPJ_FLOAT64 opj_dwt_norms[4][10] = {
+    {1.000, 1.500, 2.750, 5.375, 10.68, 21.34, 42.67, 85.33, 170.7, 341.3},
+    {1.038, 1.592, 2.919, 5.703, 11.33, 22.64, 45.25, 90.48, 180.9},
+    {1.038, 1.592, 2.919, 5.703, 11.33, 22.64, 45.25, 90.48, 180.9},
+    {.7186, .9218, 1.586, 3.043, 6.019, 12.01, 24.00, 47.97, 95.93}
+};
+
+/* <summary>                                                              */
+/* This table contains the norms of the 9-7 wavelets for different bands. */
+/* </summary>                                                             */
+/* FIXME! the array should really be extended up to 33 resolution levels */
+/* See https://github.com/uclouvain/openjpeg/issues/493 */
+static const OPJ_FLOAT64 opj_dwt_norms_real[4][10] = {
+    {1.000, 1.965, 4.177, 8.403, 16.90, 33.84, 67.69, 135.3, 270.6, 540.9},
+    {2.022, 3.989, 8.355, 17.04, 34.27, 68.63, 137.3, 274.6, 549.0},
+    {2.022, 3.989, 8.355, 17.04, 34.27, 68.63, 137.3, 274.6, 549.0},
+    {2.080, 3.865, 8.307, 17.18, 34.71, 69.59, 139.3, 278.6, 557.2}
+};
+
+/*
+==========================================================
+   local functions
+==========================================================
+*/
+
+/* <summary>                             */
+/* Forward lazy transform (horizontal).  */
+/* </summary>                            */
+static void opj_dwt_deinterleave_h(const OPJ_INT32 * OPJ_RESTRICT a,
+                                   OPJ_INT32 * OPJ_RESTRICT b,
+                                   OPJ_INT32 dn,
+                                   OPJ_INT32 sn, OPJ_INT32 cas)
+{
+    OPJ_INT32 i;
+    OPJ_INT32 * OPJ_RESTRICT l_dest = b;
+    const OPJ_INT32 * OPJ_RESTRICT l_src = a + cas;
+
+    for (i = 0; i < sn; ++i) {
+        *l_dest++ = *l_src;
+        l_src += 2;
+    }
+
+    l_dest = b + sn;
+    l_src = a + 1 - cas;
+
+    for (i = 0; i < dn; ++i)  {
+        *l_dest++ = *l_src;
+        l_src += 2;
+    }
+}
+
+#ifdef STANDARD_SLOW_VERSION
+/* <summary>                             */
+/* Inverse lazy transform (horizontal).  */
+/* </summary>                            */
+static void opj_dwt_interleave_h(const opj_dwt_t* h, OPJ_INT32 *a)
+{
+    const OPJ_INT32 *ai = a;
+    OPJ_INT32 *bi = h->mem + h->cas;
+    OPJ_INT32  i    = h->sn;
+    while (i--) {
+        *bi = *(ai++);
+        bi += 2;
+    }
+    ai  = a + h->sn;
+    bi  = h->mem + 1 - h->cas;
+    i   = h->dn ;
+    while (i--) {
+        *bi = *(ai++);
+        bi += 2;
+    }
+}
+
+/* <summary>                             */
+/* Inverse lazy transform (vertical).    */
+/* </summary>                            */
+static void opj_dwt_interleave_v(const opj_dwt_t* v, OPJ_INT32 *a, OPJ_INT32 x)
+{
+    const OPJ_INT32 *ai = a;
+    OPJ_INT32 *bi = v->mem + v->cas;
+    OPJ_INT32  i = v->sn;
+    while (i--) {
+        *bi = *ai;
+        bi += 2;
+        ai += x;
+    }
+    ai = a + (v->sn * (OPJ_SIZE_T)x);
+    bi = v->mem + 1 - v->cas;
+    i = v->dn ;
+    while (i--) {
+        *bi = *ai;
+        bi += 2;
+        ai += x;
+    }
+}
+
+#endif /* STANDARD_SLOW_VERSION */
+
+#ifdef STANDARD_SLOW_VERSION
+/* <summary>                            */
+/* Inverse 5-3 wavelet transform in 1-D. */
+/* </summary>                           */
+static void opj_dwt_decode_1_(OPJ_INT32 *a, OPJ_SIZE_T a_count, OPJ_INT32 dn,
+                              OPJ_INT32 sn, OPJ_INT32 cas)
+{
+    OPJ_INT32 i;
+
+    if (!cas) {
+        if ((dn > 0) || (sn > 1)) { /* NEW :  CASE ONE ELEMENT */
+            for (i = 0; i < sn; i++) {
+                OPJ_S(i) -= (OPJ_D_(i - 1) + OPJ_D_(i) + 2) >> 2;
+            }
+            for (i = 0; i < dn; i++) {
+                OPJ_D(i) += (OPJ_S_(i) + OPJ_S_(i + 1)) >> 1;
+            }
+        }
+    } else {
+        if (!sn  && dn == 1) {        /* NEW :  CASE ONE ELEMENT */
+            OPJ_S(0) /= 2;
+        } else {
+            for (i = 0; i < sn; i++) {
+                OPJ_D(i) -= (OPJ_SS_(i) + OPJ_SS_(i + 1) + 2) >> 2;
+            }
+            for (i = 0; i < dn; i++) {
+                OPJ_S(i) += (OPJ_DD_(i) + OPJ_DD_(i - 1)) >> 1;
+            }
+        }
+    }
+}
+
+static void opj_dwt_decode_1(const opj_dwt_t *v)
+{
+    opj_dwt_decode_1_(v->mem, v->mem_count, v->dn, v->sn, v->cas);
+}
+
+#endif /* STANDARD_SLOW_VERSION */
+
+#if !defined(STANDARD_SLOW_VERSION)
+static void  opj_idwt53_h_cas0(OPJ_INT32* tmp,
+                               const OPJ_INT32 sn,
+                               const OPJ_INT32 len,
+                               OPJ_INT32* tiledp)
+{
+    OPJ_INT32 i, j;
+    const OPJ_INT32* in_even = &tiledp[0];
+    const OPJ_INT32* in_odd = &tiledp[sn];
+
+#ifdef TWO_PASS_VERSION
+    /* For documentation purpose: performs lifting in two iterations, */
+    /* but without explicit interleaving */
+
+    assert(len > 1);
+
+    /* Even */
+    tmp[0] = in_even[0] - ((in_odd[0] + 1) >> 1);
+    for (i = 2, j = 0; i <= len - 2; i += 2, j++) {
+        tmp[i] = in_even[j + 1] - ((in_odd[j] + in_odd[j + 1] + 2) >> 2);
+    }
+    if (len & 1) { /* if len is odd */
+        tmp[len - 1] = in_even[(len - 1) / 2] - ((in_odd[(len - 2) / 2] + 1) >> 1);
+    }
+
+    /* Odd */
+    for (i = 1, j = 0; i < len - 1; i += 2, j++) {
+        tmp[i] = in_odd[j] + ((tmp[i - 1] + tmp[i + 1]) >> 1);
+    }
+    if (!(len & 1)) { /* if len is even */
+        tmp[len - 1] = in_odd[(len - 1) / 2] + tmp[len - 2];
+    }
+#else
+    OPJ_INT32 d1c, d1n, s1n, s0c, s0n;
+
+    assert(len > 1);
+
+    /* Improved version of the TWO_PASS_VERSION: */
+    /* Performs lifting in one single iteration. Saves memory */
+    /* accesses and explicit interleaving. */
+    s1n = in_even[0];
+    d1n = in_odd[0];
+    s0n = s1n - ((d1n + 1) >> 1);
+
+    for (i = 0, j = 1; i < (len - 3); i += 2, j++) {
+        d1c = d1n;
+        s0c = s0n;
+
+        s1n = in_even[j];
+        d1n = in_odd[j];
+
+        s0n = s1n - ((d1c + d1n + 2) >> 2);
+
+        tmp[i  ] = s0c;
+        tmp[i + 1] = opj_int_add_no_overflow(d1c, opj_int_add_no_overflow(s0c,
+                                             s0n) >> 1);
+    }
+
+    tmp[i] = s0n;
+
+    if (len & 1) {
+        tmp[len - 1] = in_even[(len - 1) / 2] - ((d1n + 1) >> 1);
+        tmp[len - 2] = d1n + ((s0n + tmp[len - 1]) >> 1);
+    } else {
+        tmp[len - 1] = d1n + s0n;
+    }
+#endif
+    memcpy(tiledp, tmp, (OPJ_UINT32)len * sizeof(OPJ_INT32));
+}
+
+static void  opj_idwt53_h_cas1(OPJ_INT32* tmp,
+                               const OPJ_INT32 sn,
+                               const OPJ_INT32 len,
+                               OPJ_INT32* tiledp)
+{
+    OPJ_INT32 i, j;
+    const OPJ_INT32* in_even = &tiledp[sn];
+    const OPJ_INT32* in_odd = &tiledp[0];
+
+#ifdef TWO_PASS_VERSION
+    /* For documentation purpose: performs lifting in two iterations, */
+    /* but without explicit interleaving */
+
+    assert(len > 2);
+
+    /* Odd */
+    for (i = 1, j = 0; i < len - 1; i += 2, j++) {
+        tmp[i] = in_odd[j] - ((in_even[j] + in_even[j + 1] + 2) >> 2);
+    }
+    if (!(len & 1)) {
+        tmp[len - 1] = in_odd[len / 2 - 1] - ((in_even[len / 2 - 1] + 1) >> 1);
+    }
+
+    /* Even */
+    tmp[0] = in_even[0] + tmp[1];
+    for (i = 2, j = 1; i < len - 1; i += 2, j++) {
+        tmp[i] = in_even[j] + ((tmp[i + 1] + tmp[i - 1]) >> 1);
+    }
+    if (len & 1) {
+        tmp[len - 1] = in_even[len / 2] + tmp[len - 2];
+    }
+#else
+    OPJ_INT32 s1, s2, dc, dn;
+
+    assert(len > 2);
+
+    /* Improved version of the TWO_PASS_VERSION: */
+    /* Performs lifting in one single iteration. Saves memory */
+    /* accesses and explicit interleaving. */
+
+    s1 = in_even[1];
+    dc = in_odd[0] - ((in_even[0] + s1 + 2) >> 2);
+    tmp[0] = in_even[0] + dc;
+
+    for (i = 1, j = 1; i < (len - 2 - !(len & 1)); i += 2, j++) {
+
+        s2 = in_even[j + 1];
+
+        dn = in_odd[j] - ((s1 + s2 + 2) >> 2);
+        tmp[i  ] = dc;
+        tmp[i + 1] = opj_int_add_no_overflow(s1, opj_int_add_no_overflow(dn, dc) >> 1);
+
+        dc = dn;
+        s1 = s2;
+    }
+
+    tmp[i] = dc;
+
+    if (!(len & 1)) {
+        dn = in_odd[len / 2 - 1] - ((s1 + 1) >> 1);
+        tmp[len - 2] = s1 + ((dn + dc) >> 1);
+        tmp[len - 1] = dn;
+    } else {
+        tmp[len - 1] = s1 + dc;
+    }
+#endif
+    memcpy(tiledp, tmp, (OPJ_UINT32)len * sizeof(OPJ_INT32));
+}
+
+
+#endif /* !defined(STANDARD_SLOW_VERSION) */
+
+/* <summary>                            */
+/* Inverse 5-3 wavelet transform in 1-D for one row. */
+/* </summary>                           */
+/* Performs interleave, inverse wavelet transform and copy back to buffer */
+static void opj_idwt53_h(const opj_dwt_t *dwt,
+                         OPJ_INT32* tiledp)
+{
+#ifdef STANDARD_SLOW_VERSION
+    /* For documentation purpose */
+    opj_dwt_interleave_h(dwt, tiledp);
+    opj_dwt_decode_1(dwt);
+    memcpy(tiledp, dwt->mem, (OPJ_UINT32)(dwt->sn + dwt->dn) * sizeof(OPJ_INT32));
+#else
+    const OPJ_INT32 sn = dwt->sn;
+    const OPJ_INT32 len = sn + dwt->dn;
+    if (dwt->cas == 0) { /* Left-most sample is on even coordinate */
+        if (len > 1) {
+            opj_idwt53_h_cas0(dwt->mem, sn, len, tiledp);
+        } else {
+            /* Unmodified value */
+        }
+    } else { /* Left-most sample is on odd coordinate */
+        if (len == 1) {
+            tiledp[0] /= 2;
+        } else if (len == 2) {
+            OPJ_INT32* out = dwt->mem;
+            const OPJ_INT32* in_even = &tiledp[sn];
+            const OPJ_INT32* in_odd = &tiledp[0];
+            out[1] = in_odd[0] - ((in_even[0] + 1) >> 1);
+            out[0] = in_even[0] + out[1];
+            memcpy(tiledp, dwt->mem, (OPJ_UINT32)len * sizeof(OPJ_INT32));
+        } else if (len > 2) {
+            opj_idwt53_h_cas1(dwt->mem, sn, len, tiledp);
+        }
+    }
+#endif
+}
+
+#if (defined(__SSE2__) || defined(__AVX2__)) && !defined(STANDARD_SLOW_VERSION)
+
+/* Conveniency macros to improve the readability of the formulas */
+#if __AVX2__
+#define VREG        __m256i
+#define LOAD_CST(x) _mm256_set1_epi32(x)
+#define LOAD(x)     _mm256_load_si256((const VREG*)(x))
+#define LOADU(x)    _mm256_loadu_si256((const VREG*)(x))
+#define STORE(x,y)  _mm256_store_si256((VREG*)(x),(y))
+#define STOREU(x,y) _mm256_storeu_si256((VREG*)(x),(y))
+#define ADD(x,y)    _mm256_add_epi32((x),(y))
+#define SUB(x,y)    _mm256_sub_epi32((x),(y))
+#define SAR(x,y)    _mm256_srai_epi32((x),(y))
+#else
+#define VREG        __m128i
+#define LOAD_CST(x) _mm_set1_epi32(x)
+#define LOAD(x)     _mm_load_si128((const VREG*)(x))
+#define LOADU(x)    _mm_loadu_si128((const VREG*)(x))
+#define STORE(x,y)  _mm_store_si128((VREG*)(x),(y))
+#define STOREU(x,y) _mm_storeu_si128((VREG*)(x),(y))
+#define ADD(x,y)    _mm_add_epi32((x),(y))
+#define SUB(x,y)    _mm_sub_epi32((x),(y))
+#define SAR(x,y)    _mm_srai_epi32((x),(y))
+#endif
+#define ADD3(x,y,z) ADD(ADD(x,y),z)
+
+static
+void opj_idwt53_v_final_memcpy(OPJ_INT32* tiledp_col,
+                               const OPJ_INT32* tmp,
+                               OPJ_INT32 len,
+                               OPJ_SIZE_T stride)
+{
+    OPJ_INT32 i;
+    for (i = 0; i < len; ++i) {
+        /* A memcpy(&tiledp_col[i * stride + 0],
+                    &tmp[PARALLEL_COLS_53 * i + 0],
+                    PARALLEL_COLS_53 * sizeof(OPJ_INT32))
+           would do but would be a tiny bit slower.
+           We can take here advantage of our knowledge of alignment */
+        STOREU(&tiledp_col[(OPJ_SIZE_T)i * stride + 0],
+               LOAD(&tmp[PARALLEL_COLS_53 * i + 0]));
+        STOREU(&tiledp_col[(OPJ_SIZE_T)i * stride + VREG_INT_COUNT],
+               LOAD(&tmp[PARALLEL_COLS_53 * i + VREG_INT_COUNT]));
+    }
+}
+
+/** Vertical inverse 5x3 wavelet transform for 8 columns in SSE2, or
+ * 16 in AVX2, when top-most pixel is on even coordinate */
+static void opj_idwt53_v_cas0_mcols_SSE2_OR_AVX2(
+    OPJ_INT32* tmp,
+    const OPJ_INT32 sn,
+    const OPJ_INT32 len,
+    OPJ_INT32* tiledp_col,
+    const OPJ_SIZE_T stride)
+{
+    const OPJ_INT32* in_even = &tiledp_col[0];
+    const OPJ_INT32* in_odd = &tiledp_col[(OPJ_SIZE_T)sn * stride];
+
+    OPJ_INT32 i;
+    OPJ_SIZE_T j;
+    VREG d1c_0, d1n_0, s1n_0, s0c_0, s0n_0;
+    VREG d1c_1, d1n_1, s1n_1, s0c_1, s0n_1;
+    const VREG two = LOAD_CST(2);
+
+    assert(len > 1);
+#if __AVX2__
+    assert(PARALLEL_COLS_53 == 16);
+    assert(VREG_INT_COUNT == 8);
+#else
+    assert(PARALLEL_COLS_53 == 8);
+    assert(VREG_INT_COUNT == 4);
+#endif
+
+    /* Note: loads of input even/odd values must be done in a unaligned */
+    /* fashion. But stores in tmp can be done with aligned store, since */
+    /* the temporary buffer is properly aligned */
+    assert((OPJ_SIZE_T)tmp % (sizeof(OPJ_INT32) * VREG_INT_COUNT) == 0);
+
+    s1n_0 = LOADU(in_even + 0);
+    s1n_1 = LOADU(in_even + VREG_INT_COUNT);
+    d1n_0 = LOADU(in_odd);
+    d1n_1 = LOADU(in_odd + VREG_INT_COUNT);
+
+    /* s0n = s1n - ((d1n + 1) >> 1); <==> */
+    /* s0n = s1n - ((d1n + d1n + 2) >> 2); */
+    s0n_0 = SUB(s1n_0, SAR(ADD3(d1n_0, d1n_0, two), 2));
+    s0n_1 = SUB(s1n_1, SAR(ADD3(d1n_1, d1n_1, two), 2));
+
+    for (i = 0, j = 1; i < (len - 3); i += 2, j++) {
+        d1c_0 = d1n_0;
+        s0c_0 = s0n_0;
+        d1c_1 = d1n_1;
+        s0c_1 = s0n_1;
+
+        s1n_0 = LOADU(in_even + j * stride);
+        s1n_1 = LOADU(in_even + j * stride + VREG_INT_COUNT);
+        d1n_0 = LOADU(in_odd + j * stride);
+        d1n_1 = LOADU(in_odd + j * stride + VREG_INT_COUNT);
+
+        /*s0n = s1n - ((d1c + d1n + 2) >> 2);*/
+        s0n_0 = SUB(s1n_0, SAR(ADD3(d1c_0, d1n_0, two), 2));
+        s0n_1 = SUB(s1n_1, SAR(ADD3(d1c_1, d1n_1, two), 2));
+
+        STORE(tmp + PARALLEL_COLS_53 * (i + 0), s0c_0);
+        STORE(tmp + PARALLEL_COLS_53 * (i + 0) + VREG_INT_COUNT, s0c_1);
+
+        /* d1c + ((s0c + s0n) >> 1) */
+        STORE(tmp + PARALLEL_COLS_53 * (i + 1) + 0,
+              ADD(d1c_0, SAR(ADD(s0c_0, s0n_0), 1)));
+        STORE(tmp + PARALLEL_COLS_53 * (i + 1) + VREG_INT_COUNT,
+              ADD(d1c_1, SAR(ADD(s0c_1, s0n_1), 1)));
+    }
+
+    STORE(tmp + PARALLEL_COLS_53 * (i + 0) + 0, s0n_0);
+    STORE(tmp + PARALLEL_COLS_53 * (i + 0) + VREG_INT_COUNT, s0n_1);
+
+    if (len & 1) {
+        VREG tmp_len_minus_1;
+        s1n_0 = LOADU(in_even + (OPJ_SIZE_T)((len - 1) / 2) * stride);
+        /* tmp_len_minus_1 = s1n - ((d1n + 1) >> 1); */
+        tmp_len_minus_1 = SUB(s1n_0, SAR(ADD3(d1n_0, d1n_0, two), 2));
+        STORE(tmp + PARALLEL_COLS_53 * (len - 1), tmp_len_minus_1);
+        /* d1n + ((s0n + tmp_len_minus_1) >> 1) */
+        STORE(tmp + PARALLEL_COLS_53 * (len - 2),
+              ADD(d1n_0, SAR(ADD(s0n_0, tmp_len_minus_1), 1)));
+
+        s1n_1 = LOADU(in_even + (OPJ_SIZE_T)((len - 1) / 2) * stride + VREG_INT_COUNT);
+        /* tmp_len_minus_1 = s1n - ((d1n + 1) >> 1); */
+        tmp_len_minus_1 = SUB(s1n_1, SAR(ADD3(d1n_1, d1n_1, two), 2));
+        STORE(tmp + PARALLEL_COLS_53 * (len - 1) + VREG_INT_COUNT,
+              tmp_len_minus_1);
+        /* d1n + ((s0n + tmp_len_minus_1) >> 1) */
+        STORE(tmp + PARALLEL_COLS_53 * (len - 2) + VREG_INT_COUNT,
+              ADD(d1n_1, SAR(ADD(s0n_1, tmp_len_minus_1), 1)));
+
+
+    } else {
+        STORE(tmp + PARALLEL_COLS_53 * (len - 1) + 0,
+              ADD(d1n_0, s0n_0));
+        STORE(tmp + PARALLEL_COLS_53 * (len - 1) + VREG_INT_COUNT,
+              ADD(d1n_1, s0n_1));
+    }
+
+    opj_idwt53_v_final_memcpy(tiledp_col, tmp, len, stride);
+}
+
+
+/** Vertical inverse 5x3 wavelet transform for 8 columns in SSE2, or
+ * 16 in AVX2, when top-most pixel is on odd coordinate */
+static void opj_idwt53_v_cas1_mcols_SSE2_OR_AVX2(
+    OPJ_INT32* tmp,
+    const OPJ_INT32 sn,
+    const OPJ_INT32 len,
+    OPJ_INT32* tiledp_col,
+    const OPJ_SIZE_T stride)
+{
+    OPJ_INT32 i;
+    OPJ_SIZE_T j;
+
+    VREG s1_0, s2_0, dc_0, dn_0;
+    VREG s1_1, s2_1, dc_1, dn_1;
+    const VREG two = LOAD_CST(2);
+
+    const OPJ_INT32* in_even = &tiledp_col[(OPJ_SIZE_T)sn * stride];
+    const OPJ_INT32* in_odd = &tiledp_col[0];
+
+    assert(len > 2);
+#if __AVX2__
+    assert(PARALLEL_COLS_53 == 16);
+    assert(VREG_INT_COUNT == 8);
+#else
+    assert(PARALLEL_COLS_53 == 8);
+    assert(VREG_INT_COUNT == 4);
+#endif
+
+    /* Note: loads of input even/odd values must be done in a unaligned */
+    /* fashion. But stores in tmp can be done with aligned store, since */
+    /* the temporary buffer is properly aligned */
+    assert((OPJ_SIZE_T)tmp % (sizeof(OPJ_INT32) * VREG_INT_COUNT) == 0);
+
+    s1_0 = LOADU(in_even + stride);
+    /* in_odd[0] - ((in_even[0] + s1 + 2) >> 2); */
+    dc_0 = SUB(LOADU(in_odd + 0),
+               SAR(ADD3(LOADU(in_even + 0), s1_0, two), 2));
+    STORE(tmp + PARALLEL_COLS_53 * 0, ADD(LOADU(in_even + 0), dc_0));
+
+    s1_1 = LOADU(in_even + stride + VREG_INT_COUNT);
+    /* in_odd[0] - ((in_even[0] + s1 + 2) >> 2); */
+    dc_1 = SUB(LOADU(in_odd + VREG_INT_COUNT),
+               SAR(ADD3(LOADU(in_even + VREG_INT_COUNT), s1_1, two), 2));
+    STORE(tmp + PARALLEL_COLS_53 * 0 + VREG_INT_COUNT,
+          ADD(LOADU(in_even + VREG_INT_COUNT), dc_1));
+
+    for (i = 1, j = 1; i < (len - 2 - !(len & 1)); i += 2, j++) {
+
+        s2_0 = LOADU(in_even + (j + 1) * stride);
+        s2_1 = LOADU(in_even + (j + 1) * stride + VREG_INT_COUNT);
+
+        /* dn = in_odd[j * stride] - ((s1 + s2 + 2) >> 2); */
+        dn_0 = SUB(LOADU(in_odd + j * stride),
+                   SAR(ADD3(s1_0, s2_0, two), 2));
+        dn_1 = SUB(LOADU(in_odd + j * stride + VREG_INT_COUNT),
+                   SAR(ADD3(s1_1, s2_1, two), 2));
+
+        STORE(tmp + PARALLEL_COLS_53 * i, dc_0);
+        STORE(tmp + PARALLEL_COLS_53 * i + VREG_INT_COUNT, dc_1);
+
+        /* tmp[i + 1] = s1 + ((dn + dc) >> 1); */
+        STORE(tmp + PARALLEL_COLS_53 * (i + 1) + 0,
+              ADD(s1_0, SAR(ADD(dn_0, dc_0), 1)));
+        STORE(tmp + PARALLEL_COLS_53 * (i + 1) + VREG_INT_COUNT,
+              ADD(s1_1, SAR(ADD(dn_1, dc_1), 1)));
+
+        dc_0 = dn_0;
+        s1_0 = s2_0;
+        dc_1 = dn_1;
+        s1_1 = s2_1;
+    }
+    STORE(tmp + PARALLEL_COLS_53 * i, dc_0);
+    STORE(tmp + PARALLEL_COLS_53 * i + VREG_INT_COUNT, dc_1);
+
+    if (!(len & 1)) {
+        /*dn = in_odd[(len / 2 - 1) * stride] - ((s1 + 1) >> 1); */
+        dn_0 = SUB(LOADU(in_odd + (OPJ_SIZE_T)(len / 2 - 1) * stride),
+                   SAR(ADD3(s1_0, s1_0, two), 2));
+        dn_1 = SUB(LOADU(in_odd + (OPJ_SIZE_T)(len / 2 - 1) * stride + VREG_INT_COUNT),
+                   SAR(ADD3(s1_1, s1_1, two), 2));
+
+        /* tmp[len - 2] = s1 + ((dn + dc) >> 1); */
+        STORE(tmp + PARALLEL_COLS_53 * (len - 2) + 0,
+              ADD(s1_0, SAR(ADD(dn_0, dc_0), 1)));
+        STORE(tmp + PARALLEL_COLS_53 * (len - 2) + VREG_INT_COUNT,
+              ADD(s1_1, SAR(ADD(dn_1, dc_1), 1)));
+
+        STORE(tmp + PARALLEL_COLS_53 * (len - 1) + 0, dn_0);
+        STORE(tmp + PARALLEL_COLS_53 * (len - 1) + VREG_INT_COUNT, dn_1);
+    } else {
+        STORE(tmp + PARALLEL_COLS_53 * (len - 1) + 0, ADD(s1_0, dc_0));
+        STORE(tmp + PARALLEL_COLS_53 * (len - 1) + VREG_INT_COUNT,
+              ADD(s1_1, dc_1));
+    }
+
+    opj_idwt53_v_final_memcpy(tiledp_col, tmp, len, stride);
+}
+
+#undef VREG
+#undef LOAD_CST
+#undef LOADU
+#undef LOAD
+#undef STORE
+#undef STOREU
+#undef ADD
+#undef ADD3
+#undef SUB
+#undef SAR
+
+#endif /* (defined(__SSE2__) || defined(__AVX2__)) && !defined(STANDARD_SLOW_VERSION) */
+
+#if !defined(STANDARD_SLOW_VERSION)
+/** Vertical inverse 5x3 wavelet transform for one column, when top-most
+ * pixel is on even coordinate */
+static void opj_idwt3_v_cas0(OPJ_INT32* tmp,
+                             const OPJ_INT32 sn,
+                             const OPJ_INT32 len,
+                             OPJ_INT32* tiledp_col,
+                             const OPJ_SIZE_T stride)
+{
+    OPJ_INT32 i, j;
+    OPJ_INT32 d1c, d1n, s1n, s0c, s0n;
+
+    assert(len > 1);
+
+    /* Performs lifting in one single iteration. Saves memory */
+    /* accesses and explicit interleaving. */
+
+    s1n = tiledp_col[0];
+    d1n = tiledp_col[(OPJ_SIZE_T)sn * stride];
+    s0n = s1n - ((d1n + 1) >> 1);
+
+    for (i = 0, j = 0; i < (len - 3); i += 2, j++) {
+        d1c = d1n;
+        s0c = s0n;
+
+        s1n = tiledp_col[(OPJ_SIZE_T)(j + 1) * stride];
+        d1n = tiledp_col[(OPJ_SIZE_T)(sn + j + 1) * stride];
+
+        s0n = opj_int_sub_no_overflow(s1n,
+                                      opj_int_add_no_overflow(opj_int_add_no_overflow(d1c, d1n), 2) >> 2);
+
+        tmp[i  ] = s0c;
+        tmp[i + 1] = opj_int_add_no_overflow(d1c, opj_int_add_no_overflow(s0c,
+                                             s0n) >> 1);
+    }
+
+    tmp[i] = s0n;
+
+    if (len & 1) {
+        tmp[len - 1] =
+            tiledp_col[(OPJ_SIZE_T)((len - 1) / 2) * stride] -
+            ((d1n + 1) >> 1);
+        tmp[len - 2] = d1n + ((s0n + tmp[len - 1]) >> 1);
+    } else {
+        tmp[len - 1] = d1n + s0n;
+    }
+
+    for (i = 0; i < len; ++i) {
+        tiledp_col[(OPJ_SIZE_T)i * stride] = tmp[i];
+    }
+}
+
+
+/** Vertical inverse 5x3 wavelet transform for one column, when top-most
+ * pixel is on odd coordinate */
+static void opj_idwt3_v_cas1(OPJ_INT32* tmp,
+                             const OPJ_INT32 sn,
+                             const OPJ_INT32 len,
+                             OPJ_INT32* tiledp_col,
+                             const OPJ_SIZE_T stride)
+{
+    OPJ_INT32 i, j;
+    OPJ_INT32 s1, s2, dc, dn;
+    const OPJ_INT32* in_even = &tiledp_col[(OPJ_SIZE_T)sn * stride];
+    const OPJ_INT32* in_odd = &tiledp_col[0];
+
+    assert(len > 2);
+
+    /* Performs lifting in one single iteration. Saves memory */
+    /* accesses and explicit interleaving. */
+
+    s1 = in_even[stride];
+    dc = in_odd[0] - ((in_even[0] + s1 + 2) >> 2);
+    tmp[0] = in_even[0] + dc;
+    for (i = 1, j = 1; i < (len - 2 - !(len & 1)); i += 2, j++) {
+
+        s2 = in_even[(OPJ_SIZE_T)(j + 1) * stride];
+
+        dn = in_odd[(OPJ_SIZE_T)j * stride] - ((s1 + s2 + 2) >> 2);
+        tmp[i  ] = dc;
+        tmp[i + 1] = s1 + ((dn + dc) >> 1);
+
+        dc = dn;
+        s1 = s2;
+    }
+    tmp[i] = dc;
+    if (!(len & 1)) {
+        dn = in_odd[(OPJ_SIZE_T)(len / 2 - 1) * stride] - ((s1 + 1) >> 1);
+        tmp[len - 2] = s1 + ((dn + dc) >> 1);
+        tmp[len - 1] = dn;
+    } else {
+        tmp[len - 1] = s1 + dc;
+    }
+
+    for (i = 0; i < len; ++i) {
+        tiledp_col[(OPJ_SIZE_T)i * stride] = tmp[i];
+    }
+}
+#endif /* !defined(STANDARD_SLOW_VERSION) */
+
+/* <summary>                            */
+/* Inverse vertical 5-3 wavelet transform in 1-D for several columns. */
+/* </summary>                           */
+/* Performs interleave, inverse wavelet transform and copy back to buffer */
+static void opj_idwt53_v(const opj_dwt_t *dwt,
+                         OPJ_INT32* tiledp_col,
+                         OPJ_SIZE_T stride,
+                         OPJ_INT32 nb_cols)
+{
+#ifdef STANDARD_SLOW_VERSION
+    /* For documentation purpose */
+    OPJ_INT32 k, c;
+    for (c = 0; c < nb_cols; c ++) {
+        opj_dwt_interleave_v(dwt, tiledp_col + c, stride);
+        opj_dwt_decode_1(dwt);
+        for (k = 0; k < dwt->sn + dwt->dn; ++k) {
+            tiledp_col[c + k * stride] = dwt->mem[k];
+        }
+    }
+#else
+    const OPJ_INT32 sn = dwt->sn;
+    const OPJ_INT32 len = sn + dwt->dn;
+    if (dwt->cas == 0) {
+        /* If len == 1, unmodified value */
+
+#if (defined(__SSE2__) || defined(__AVX2__))
+        if (len > 1 && nb_cols == PARALLEL_COLS_53) {
+            /* Same as below general case, except that thanks to SSE2/AVX2 */
+            /* we can efficiently process 8/16 columns in parallel */
+            opj_idwt53_v_cas0_mcols_SSE2_OR_AVX2(dwt->mem, sn, len, tiledp_col, stride);
+            return;
+        }
+#endif
+        if (len > 1) {
+            OPJ_INT32 c;
+            for (c = 0; c < nb_cols; c++, tiledp_col++) {
+                opj_idwt3_v_cas0(dwt->mem, sn, len, tiledp_col, stride);
+            }
+            return;
+        }
+    } else {
+        if (len == 1) {
+            OPJ_INT32 c;
+            for (c = 0; c < nb_cols; c++, tiledp_col++) {
+                tiledp_col[0] /= 2;
+            }
+            return;
+        }
+
+        if (len == 2) {
+            OPJ_INT32 c;
+            OPJ_INT32* out = dwt->mem;
+            for (c = 0; c < nb_cols; c++, tiledp_col++) {
+                OPJ_INT32 i;
+                const OPJ_INT32* in_even = &tiledp_col[(OPJ_SIZE_T)sn * stride];
+                const OPJ_INT32* in_odd = &tiledp_col[0];
+
+                out[1] = in_odd[0] - ((in_even[0] + 1) >> 1);
+                out[0] = in_even[0] + out[1];
+
+                for (i = 0; i < len; ++i) {
+                    tiledp_col[(OPJ_SIZE_T)i * stride] = out[i];
+                }
+            }
+
+            return;
+        }
+
+#if (defined(__SSE2__) || defined(__AVX2__))
+        if (len > 2 && nb_cols == PARALLEL_COLS_53) {
+            /* Same as below general case, except that thanks to SSE2/AVX2 */
+            /* we can efficiently process 8/16 columns in parallel */
+            opj_idwt53_v_cas1_mcols_SSE2_OR_AVX2(dwt->mem, sn, len, tiledp_col, stride);
+            return;
+        }
+#endif
+        if (len > 2) {
+            OPJ_INT32 c;
+            for (c = 0; c < nb_cols; c++, tiledp_col++) {
+                opj_idwt3_v_cas1(dwt->mem, sn, len, tiledp_col, stride);
+            }
+            return;
+        }
+    }
+#endif
+}
+
+#if 0
+static void opj_dwt_encode_step1(OPJ_FLOAT32* fw,
+                                 OPJ_UINT32 end,
+                                 const OPJ_FLOAT32 c)
+{
+    OPJ_UINT32 i = 0;
+    for (; i < end; ++i) {
+        fw[0] *= c;
+        fw += 2;
+    }
+}
+#else
+static void opj_dwt_encode_step1_combined(OPJ_FLOAT32* fw,
+        OPJ_UINT32 iters_c1,
+        OPJ_UINT32 iters_c2,
+        const OPJ_FLOAT32 c1,
+        const OPJ_FLOAT32 c2)
+{
+    OPJ_UINT32 i = 0;
+    const OPJ_UINT32 iters_common =  opj_uint_min(iters_c1, iters_c2);
+    assert((((OPJ_SIZE_T)fw) & 0xf) == 0);
+    assert(opj_int_abs((OPJ_INT32)iters_c1 - (OPJ_INT32)iters_c2) <= 1);
+    for (; i + 3 < iters_common; i += 4) {
+#ifdef __SSE__
+        const __m128 vcst = _mm_set_ps(c2, c1, c2, c1);
+        *(__m128*)fw = _mm_mul_ps(*(__m128*)fw, vcst);
+        *(__m128*)(fw + 4) = _mm_mul_ps(*(__m128*)(fw + 4), vcst);
+#else
+        fw[0] *= c1;
+        fw[1] *= c2;
+        fw[2] *= c1;
+        fw[3] *= c2;
+        fw[4] *= c1;
+        fw[5] *= c2;
+        fw[6] *= c1;
+        fw[7] *= c2;
+#endif
+        fw += 8;
+    }
+    for (; i < iters_common; i++) {
+        fw[0] *= c1;
+        fw[1] *= c2;
+        fw += 2;
+    }
+    if (i < iters_c1) {
+        fw[0] *= c1;
+    } else if (i < iters_c2) {
+        fw[1] *= c2;
+    }
+}
+
+#endif
+
+static void opj_dwt_encode_step2(OPJ_FLOAT32* fl, OPJ_FLOAT32* fw,
+                                 OPJ_UINT32 end,
+                                 OPJ_UINT32 m,
+                                 OPJ_FLOAT32 c)
+{
+    OPJ_UINT32 i;
+    OPJ_UINT32 imax = opj_uint_min(end, m);
+    if (imax > 0) {
+        fw[-1] += (fl[0] + fw[0]) * c;
+        fw += 2;
+        i = 1;
+        for (; i + 3 < imax; i += 4) {
+            fw[-1] += (fw[-2] + fw[0]) * c;
+            fw[1] += (fw[0] + fw[2]) * c;
+            fw[3] += (fw[2] + fw[4]) * c;
+            fw[5] += (fw[4] + fw[6]) * c;
+            fw += 8;
+        }
+        for (; i < imax; ++i) {
+            fw[-1] += (fw[-2] + fw[0]) * c;
+            fw += 2;
+        }
+    }
+    if (m < end) {
+        assert(m + 1 == end);
+        fw[-1] += (2 * fw[-2]) * c;
+    }
+}
+
+static void opj_dwt_encode_1_real(void *aIn, OPJ_INT32 dn, OPJ_INT32 sn,
+                                  OPJ_INT32 cas)
+{
+    OPJ_FLOAT32* w = (OPJ_FLOAT32*)aIn;
+    OPJ_INT32 a, b;
+    assert(dn + sn > 1);
+    if (cas == 0) {
+        a = 0;
+        b = 1;
+    } else {
+        a = 1;
+        b = 0;
+    }
+    opj_dwt_encode_step2(w + a, w + b + 1,
+                         (OPJ_UINT32)dn,
+                         (OPJ_UINT32)opj_int_min(dn, sn - b),
+                         opj_dwt_alpha);
+    opj_dwt_encode_step2(w + b, w + a + 1,
+                         (OPJ_UINT32)sn,
+                         (OPJ_UINT32)opj_int_min(sn, dn - a),
+                         opj_dwt_beta);
+    opj_dwt_encode_step2(w + a, w + b + 1,
+                         (OPJ_UINT32)dn,
+                         (OPJ_UINT32)opj_int_min(dn, sn - b),
+                         opj_dwt_gamma);
+    opj_dwt_encode_step2(w + b, w + a + 1,
+                         (OPJ_UINT32)sn,
+                         (OPJ_UINT32)opj_int_min(sn, dn - a),
+                         opj_dwt_delta);
+#if 0
+    opj_dwt_encode_step1(w + b, (OPJ_UINT32)dn,
+                         opj_K);
+    opj_dwt_encode_step1(w + a, (OPJ_UINT32)sn,
+                         opj_invK);
+#else
+    if (a == 0) {
+        opj_dwt_encode_step1_combined(w,
+                                      (OPJ_UINT32)sn,
+                                      (OPJ_UINT32)dn,
+                                      opj_invK,
+                                      opj_K);
+    } else {
+        opj_dwt_encode_step1_combined(w,
+                                      (OPJ_UINT32)dn,
+                                      (OPJ_UINT32)sn,
+                                      opj_K,
+                                      opj_invK);
+    }
+#endif
+}
+
+static void opj_dwt_encode_stepsize(OPJ_INT32 stepsize, OPJ_INT32 numbps,
+                                    opj_stepsize_t *bandno_stepsize)
+{
+    OPJ_INT32 p, n;
+    p = opj_int_floorlog2(stepsize) - 13;
+    n = 11 - opj_int_floorlog2(stepsize);
+    bandno_stepsize->mant = (n < 0 ? stepsize >> -n : stepsize << n) & 0x7ff;
+    bandno_stepsize->expn = numbps - p;
+}
+
+/*
+==========================================================
+   DWT interface
+==========================================================
+*/
+
+/** Process one line for the horizontal pass of the 5x3 forward transform */
+static
+void opj_dwt_encode_and_deinterleave_h_one_row(void* rowIn,
+        void* tmpIn,
+        OPJ_UINT32 width,
+        OPJ_BOOL even)
+{
+    OPJ_INT32* OPJ_RESTRICT row = (OPJ_INT32*)rowIn;
+    OPJ_INT32* OPJ_RESTRICT tmp = (OPJ_INT32*)tmpIn;
+    const OPJ_INT32 sn = (OPJ_INT32)((width + (even ? 1 : 0)) >> 1);
+    const OPJ_INT32 dn = (OPJ_INT32)(width - (OPJ_UINT32)sn);
+
+    if (even) {
+        if (width > 1) {
+            OPJ_INT32 i;
+            for (i = 0; i < sn - 1; i++) {
+                tmp[sn + i] = row[2 * i + 1] - ((row[(i) * 2] + row[(i + 1) * 2]) >> 1);
+            }
+            if ((width % 2) == 0) {
+                tmp[sn + i] = row[2 * i + 1] - row[(i) * 2];
+            }
+            row[0] += (tmp[sn] + tmp[sn] + 2) >> 2;
+            for (i = 1; i < dn; i++) {
+                row[i] = row[2 * i] + ((tmp[sn + (i - 1)] + tmp[sn + i] + 2) >> 2);
+            }
+            if ((width % 2) == 1) {
+                row[i] = row[2 * i] + ((tmp[sn + (i - 1)] + tmp[sn + (i - 1)] + 2) >> 2);
+            }
+            memcpy(row + sn, tmp + sn, (OPJ_SIZE_T)dn * sizeof(OPJ_INT32));
+        }
+    } else {
+        if (width == 1) {
+            row[0] *= 2;
+        } else {
+            OPJ_INT32 i;
+            tmp[sn + 0] = row[0] - row[1];
+            for (i = 1; i < sn; i++) {
+                tmp[sn + i] = row[2 * i] - ((row[2 * i + 1] + row[2 * (i - 1) + 1]) >> 1);
+            }
+            if ((width % 2) == 1) {
+                tmp[sn + i] = row[2 * i] - row[2 * (i - 1) + 1];
+            }
+
+            for (i = 0; i < dn - 1; i++) {
+                row[i] = row[2 * i + 1] + ((tmp[sn + i] + tmp[sn + i + 1] + 2) >> 2);
+            }
+            if ((width % 2) == 0) {
+                row[i] = row[2 * i + 1] + ((tmp[sn + i] + tmp[sn + i] + 2) >> 2);
+            }
+            memcpy(row + sn, tmp + sn, (OPJ_SIZE_T)dn * sizeof(OPJ_INT32));
+        }
+    }
+}
+
+/** Process one line for the horizontal pass of the 9x7 forward transform */
+static
+void opj_dwt_encode_and_deinterleave_h_one_row_real(void* rowIn,
+        void* tmpIn,
+        OPJ_UINT32 width,
+        OPJ_BOOL even)
+{
+    OPJ_FLOAT32* OPJ_RESTRICT row = (OPJ_FLOAT32*)rowIn;
+    OPJ_FLOAT32* OPJ_RESTRICT tmp = (OPJ_FLOAT32*)tmpIn;
+    const OPJ_INT32 sn = (OPJ_INT32)((width + (even ? 1 : 0)) >> 1);
+    const OPJ_INT32 dn = (OPJ_INT32)(width - (OPJ_UINT32)sn);
+    if (width == 1) {
+        return;
+    }
+    memcpy(tmp, row, width * sizeof(OPJ_FLOAT32));
+    opj_dwt_encode_1_real(tmp, dn, sn, even ? 0 : 1);
+    opj_dwt_deinterleave_h((OPJ_INT32 * OPJ_RESTRICT)tmp,
+                           (OPJ_INT32 * OPJ_RESTRICT)row,
+                           dn, sn, even ? 0 : 1);
+}
+
+typedef struct {
+    opj_dwt_t h;
+    OPJ_UINT32 rw; /* Width of the resolution to process */
+    OPJ_UINT32 w; /* Width of tiledp */
+    OPJ_INT32 * OPJ_RESTRICT tiledp;
+    OPJ_UINT32 min_j;
+    OPJ_UINT32 max_j;
+    opj_encode_and_deinterleave_h_one_row_fnptr_type p_function;
+} opj_dwt_encode_h_job_t;
+
+static void opj_dwt_encode_h_func(void* user_data, opj_tls_t* tls)
+{
+    OPJ_UINT32 j;
+    opj_dwt_encode_h_job_t* job;
+    (void)tls;
+
+    job = (opj_dwt_encode_h_job_t*)user_data;
+    for (j = job->min_j; j < job->max_j; j++) {
+        OPJ_INT32* OPJ_RESTRICT aj = job->tiledp + j * job->w;
+        (*job->p_function)(aj, job->h.mem, job->rw,
+                           job->h.cas == 0 ? OPJ_TRUE : OPJ_FALSE);
+    }
+
+    opj_aligned_free(job->h.mem);
+    opj_free(job);
+}
+
+typedef struct {
+    opj_dwt_t v;
+    OPJ_UINT32 rh;
+    OPJ_UINT32 w;
+    OPJ_INT32 * OPJ_RESTRICT tiledp;
+    OPJ_UINT32 min_j;
+    OPJ_UINT32 max_j;
+    opj_encode_and_deinterleave_v_fnptr_type p_encode_and_deinterleave_v;
+} opj_dwt_encode_v_job_t;
+
+static void opj_dwt_encode_v_func(void* user_data, opj_tls_t* tls)
+{
+    OPJ_UINT32 j;
+    opj_dwt_encode_v_job_t* job;
+    (void)tls;
+
+    job = (opj_dwt_encode_v_job_t*)user_data;
+    for (j = job->min_j; j + NB_ELTS_V8 - 1 < job->max_j; j += NB_ELTS_V8) {
+        (*job->p_encode_and_deinterleave_v)(job->tiledp + j,
+                                            job->v.mem,
+                                            job->rh,
+                                            job->v.cas == 0,
+                                            job->w,
+                                            NB_ELTS_V8);
+    }
+    if (j < job->max_j) {
+        (*job->p_encode_and_deinterleave_v)(job->tiledp + j,
+                                            job->v.mem,
+                                            job->rh,
+                                            job->v.cas == 0,
+                                            job->w,
+                                            job->max_j - j);
+    }
+
+    opj_aligned_free(job->v.mem);
+    opj_free(job);
+}
+
+/** Fetch up to cols <= NB_ELTS_V8 for each line, and put them in tmpOut */
+/* that has a NB_ELTS_V8 interleave factor. */
+static void opj_dwt_fetch_cols_vertical_pass(const void *arrayIn,
+        void *tmpOut,
+        OPJ_UINT32 height,
+        OPJ_UINT32 stride_width,
+        OPJ_UINT32 cols)
+{
+    const OPJ_INT32* OPJ_RESTRICT array = (const OPJ_INT32 * OPJ_RESTRICT)arrayIn;
+    OPJ_INT32* OPJ_RESTRICT tmp = (OPJ_INT32 * OPJ_RESTRICT)tmpOut;
+    if (cols == NB_ELTS_V8) {
+        OPJ_UINT32 k;
+        for (k = 0; k < height; ++k) {
+            memcpy(tmp + NB_ELTS_V8 * k,
+                   array + k * stride_width,
+                   NB_ELTS_V8 * sizeof(OPJ_INT32));
+        }
+    } else {
+        OPJ_UINT32 k;
+        for (k = 0; k < height; ++k) {
+            OPJ_UINT32 c;
+            for (c = 0; c < cols; c++) {
+                tmp[NB_ELTS_V8 * k + c] = array[c + k * stride_width];
+            }
+            for (; c < NB_ELTS_V8; c++) {
+                tmp[NB_ELTS_V8 * k + c] = 0;
+            }
+        }
+    }
+}
+
+/* Deinterleave result of forward transform, where cols <= NB_ELTS_V8 */
+/* and src contains NB_ELTS_V8 consecutive values for up to NB_ELTS_V8 */
+/* columns. */
+static INLINE void opj_dwt_deinterleave_v_cols(
+    const OPJ_INT32 * OPJ_RESTRICT src,
+    OPJ_INT32 * OPJ_RESTRICT dst,
+    OPJ_INT32 dn,
+    OPJ_INT32 sn,
+    OPJ_UINT32 stride_width,
+    OPJ_INT32 cas,
+    OPJ_UINT32 cols)
+{
+    OPJ_INT32 k;
+    OPJ_INT32 i = sn;
+    OPJ_INT32 * OPJ_RESTRICT l_dest = dst;
+    const OPJ_INT32 * OPJ_RESTRICT l_src = src + cas * NB_ELTS_V8;
+    OPJ_UINT32 c;
+
+    for (k = 0; k < 2; k++) {
+        while (i--) {
+            if (cols == NB_ELTS_V8) {
+                memcpy(l_dest, l_src, NB_ELTS_V8 * sizeof(OPJ_INT32));
+            } else {
+                c = 0;
+                switch (cols) {
+                case 7:
+                    l_dest[c] = l_src[c];
+                    c++; /* fallthru */
+                case 6:
+                    l_dest[c] = l_src[c];
+                    c++; /* fallthru */
+                case 5:
+                    l_dest[c] = l_src[c];
+                    c++; /* fallthru */
+                case 4:
+                    l_dest[c] = l_src[c];
+                    c++; /* fallthru */
+                case 3:
+                    l_dest[c] = l_src[c];
+                    c++; /* fallthru */
+                case 2:
+                    l_dest[c] = l_src[c];
+                    c++; /* fallthru */
+                default:
+                    l_dest[c] = l_src[c];
+                    break;
+                }
+            }
+            l_dest += stride_width;
+            l_src += 2 * NB_ELTS_V8;
+        }
+
+        l_dest = dst + (OPJ_SIZE_T)sn * (OPJ_SIZE_T)stride_width;
+        l_src = src + (1 - cas) * NB_ELTS_V8;
+        i = dn;
+    }
+}
+
+
+/* Forward 5-3 transform, for the vertical pass, processing cols columns */
+/* where cols <= NB_ELTS_V8 */
+static void opj_dwt_encode_and_deinterleave_v(
+    void *arrayIn,
+    void *tmpIn,
+    OPJ_UINT32 height,
+    OPJ_BOOL even,
+    OPJ_UINT32 stride_width,
+    OPJ_UINT32 cols)
+{
+    OPJ_INT32* OPJ_RESTRICT array = (OPJ_INT32 * OPJ_RESTRICT)arrayIn;
+    OPJ_INT32* OPJ_RESTRICT tmp = (OPJ_INT32 * OPJ_RESTRICT)tmpIn;
+    const OPJ_UINT32 sn = (height + (even ? 1 : 0)) >> 1;
+    const OPJ_UINT32 dn = height - sn;
+
+    opj_dwt_fetch_cols_vertical_pass(arrayIn, tmpIn, height, stride_width, cols);
+
+#define OPJ_Sc(i) tmp[(i)*2* NB_ELTS_V8 + c]
+#define OPJ_Dc(i) tmp[((1+(i)*2))* NB_ELTS_V8 + c]
+
+#ifdef __SSE2__
+    if (height == 1) {
+        if (!even) {
+            OPJ_UINT32 c;
+            for (c = 0; c < NB_ELTS_V8; c++) {
+                tmp[c] *= 2;
+            }
+        }
+    } else if (even) {
+        OPJ_UINT32 c;
+        OPJ_UINT32 i;
+        i = 0;
+        if (i + 1 < sn) {
+            __m128i xmm_Si_0 = *(const __m128i*)(tmp + 4 * 0);
+            __m128i xmm_Si_1 = *(const __m128i*)(tmp + 4 * 1);
+            for (; i + 1 < sn; i++) {
+                __m128i xmm_Sip1_0 = *(const __m128i*)(tmp +
+                                                       (i + 1) * 2 * NB_ELTS_V8 + 4 * 0);
+                __m128i xmm_Sip1_1 = *(const __m128i*)(tmp +
+                                                       (i + 1) * 2 * NB_ELTS_V8 + 4 * 1);
+                __m128i xmm_Di_0 = *(const __m128i*)(tmp +
+                                                     (1 + i * 2) * NB_ELTS_V8 + 4 * 0);
+                __m128i xmm_Di_1 = *(const __m128i*)(tmp +
+                                                     (1 + i * 2) * NB_ELTS_V8 + 4 * 1);
+                xmm_Di_0 = _mm_sub_epi32(xmm_Di_0,
+                                         _mm_srai_epi32(_mm_add_epi32(xmm_Si_0, xmm_Sip1_0), 1));
+                xmm_Di_1 = _mm_sub_epi32(xmm_Di_1,
+                                         _mm_srai_epi32(_mm_add_epi32(xmm_Si_1, xmm_Sip1_1), 1));
+                *(__m128i*)(tmp + (1 + i * 2) * NB_ELTS_V8 + 4 * 0) =  xmm_Di_0;
+                *(__m128i*)(tmp + (1 + i * 2) * NB_ELTS_V8 + 4 * 1) =  xmm_Di_1;
+                xmm_Si_0 = xmm_Sip1_0;
+                xmm_Si_1 = xmm_Sip1_1;
+            }
+        }
+        if (((height) % 2) == 0) {
+            for (c = 0; c < NB_ELTS_V8; c++) {
+                OPJ_Dc(i) -= OPJ_Sc(i);
+            }
+        }
+        for (c = 0; c < NB_ELTS_V8; c++) {
+            OPJ_Sc(0) += (OPJ_Dc(0) + OPJ_Dc(0) + 2) >> 2;
+        }
+        i = 1;
+        if (i < dn) {
+            __m128i xmm_Dim1_0 = *(const __m128i*)(tmp + (1 +
+                                                   (i - 1) * 2) * NB_ELTS_V8 + 4 * 0);
+            __m128i xmm_Dim1_1 = *(const __m128i*)(tmp + (1 +
+                                                   (i - 1) * 2) * NB_ELTS_V8 + 4 * 1);
+            const __m128i xmm_two = _mm_set1_epi32(2);
+            for (; i < dn; i++) {
+                __m128i xmm_Di_0 = *(const __m128i*)(tmp +
+                                                     (1 + i * 2) * NB_ELTS_V8 + 4 * 0);
+                __m128i xmm_Di_1 = *(const __m128i*)(tmp +
+                                                     (1 + i * 2) * NB_ELTS_V8 + 4 * 1);
+                __m128i xmm_Si_0 = *(const __m128i*)(tmp +
+                                                     (i * 2) * NB_ELTS_V8 + 4 * 0);
+                __m128i xmm_Si_1 = *(const __m128i*)(tmp +
+                                                     (i * 2) * NB_ELTS_V8 + 4 * 1);
+                xmm_Si_0 = _mm_add_epi32(xmm_Si_0,
+                                         _mm_srai_epi32(_mm_add_epi32(_mm_add_epi32(xmm_Dim1_0, xmm_Di_0), xmm_two), 2));
+                xmm_Si_1 = _mm_add_epi32(xmm_Si_1,
+                                         _mm_srai_epi32(_mm_add_epi32(_mm_add_epi32(xmm_Dim1_1, xmm_Di_1), xmm_two), 2));
+                *(__m128i*)(tmp + (i * 2) * NB_ELTS_V8 + 4 * 0) = xmm_Si_0;
+                *(__m128i*)(tmp + (i * 2) * NB_ELTS_V8 + 4 * 1) = xmm_Si_1;
+                xmm_Dim1_0 = xmm_Di_0;
+                xmm_Dim1_1 = xmm_Di_1;
+            }
+        }
+        if (((height) % 2) == 1) {
+            for (c = 0; c < NB_ELTS_V8; c++) {
+                OPJ_Sc(i) += (OPJ_Dc(i - 1) + OPJ_Dc(i - 1) + 2) >> 2;
+            }
+        }
+    } else {
+        OPJ_UINT32 c;
+        OPJ_UINT32 i;
+        for (c = 0; c < NB_ELTS_V8; c++) {
+            OPJ_Sc(0) -= OPJ_Dc(0);
+        }
+        i = 1;
+        if (i < sn) {
+            __m128i xmm_Dim1_0 = *(const __m128i*)(tmp + (1 +
+                                                   (i - 1) * 2) * NB_ELTS_V8 + 4 * 0);
+            __m128i xmm_Dim1_1 = *(const __m128i*)(tmp + (1 +
+                                                   (i - 1) * 2) * NB_ELTS_V8 + 4 * 1);
+            for (; i < sn; i++) {
+                __m128i xmm_Di_0 = *(const __m128i*)(tmp +
+                                                     (1 + i * 2) * NB_ELTS_V8 + 4 * 0);
+                __m128i xmm_Di_1 = *(const __m128i*)(tmp +
+                                                     (1 + i * 2) * NB_ELTS_V8 + 4 * 1);
+                __m128i xmm_Si_0 = *(const __m128i*)(tmp +
+                                                     (i * 2) * NB_ELTS_V8 + 4 * 0);
+                __m128i xmm_Si_1 = *(const __m128i*)(tmp +
+                                                     (i * 2) * NB_ELTS_V8 + 4 * 1);
+                xmm_Si_0 = _mm_sub_epi32(xmm_Si_0,
+                                         _mm_srai_epi32(_mm_add_epi32(xmm_Di_0, xmm_Dim1_0), 1));
+                xmm_Si_1 = _mm_sub_epi32(xmm_Si_1,
+                                         _mm_srai_epi32(_mm_add_epi32(xmm_Di_1, xmm_Dim1_1), 1));
+                *(__m128i*)(tmp + (i * 2) * NB_ELTS_V8 + 4 * 0) = xmm_Si_0;
+                *(__m128i*)(tmp + (i * 2) * NB_ELTS_V8 + 4 * 1) = xmm_Si_1;
+                xmm_Dim1_0 = xmm_Di_0;
+                xmm_Dim1_1 = xmm_Di_1;
+            }
+        }
+        if (((height) % 2) == 1) {
+            for (c = 0; c < NB_ELTS_V8; c++) {
+                OPJ_Sc(i) -= OPJ_Dc(i - 1);
+            }
+        }
+        i = 0;
+        if (i + 1 < dn) {
+            __m128i xmm_Si_0 = *((const __m128i*)(tmp + 4 * 0));
+            __m128i xmm_Si_1 = *((const __m128i*)(tmp + 4 * 1));
+            const __m128i xmm_two = _mm_set1_epi32(2);
+            for (; i + 1 < dn; i++) {
+                __m128i xmm_Sip1_0 = *(const __m128i*)(tmp +
+                                                       (i + 1) * 2 * NB_ELTS_V8 + 4 * 0);
+                __m128i xmm_Sip1_1 = *(const __m128i*)(tmp +
+                                                       (i + 1) * 2 * NB_ELTS_V8 + 4 * 1);
+                __m128i xmm_Di_0 = *(const __m128i*)(tmp +
+                                                     (1 + i * 2) * NB_ELTS_V8 + 4 * 0);
+                __m128i xmm_Di_1 = *(const __m128i*)(tmp +
+                                                     (1 + i * 2) * NB_ELTS_V8 + 4 * 1);
+                xmm_Di_0 = _mm_add_epi32(xmm_Di_0,
+                                         _mm_srai_epi32(_mm_add_epi32(_mm_add_epi32(xmm_Si_0, xmm_Sip1_0), xmm_two), 2));
+                xmm_Di_1 = _mm_add_epi32(xmm_Di_1,
+                                         _mm_srai_epi32(_mm_add_epi32(_mm_add_epi32(xmm_Si_1, xmm_Sip1_1), xmm_two), 2));
+                *(__m128i*)(tmp + (1 + i * 2) * NB_ELTS_V8 + 4 * 0) = xmm_Di_0;
+                *(__m128i*)(tmp + (1 + i * 2) * NB_ELTS_V8 + 4 * 1) = xmm_Di_1;
+                xmm_Si_0 = xmm_Sip1_0;
+                xmm_Si_1 = xmm_Sip1_1;
+            }
+        }
+        if (((height) % 2) == 0) {
+            for (c = 0; c < NB_ELTS_V8; c++) {
+                OPJ_Dc(i) += (OPJ_Sc(i) + OPJ_Sc(i) + 2) >> 2;
+            }
+        }
+    }
+#else
+    if (even) {
+        OPJ_UINT32 c;
+        if (height > 1) {
+            OPJ_UINT32 i;
+            for (i = 0; i + 1 < sn; i++) {
+                for (c = 0; c < NB_ELTS_V8; c++) {
+                    OPJ_Dc(i) -= (OPJ_Sc(i) + OPJ_Sc(i + 1)) >> 1;
+                }
+            }
+            if (((height) % 2) == 0) {
+                for (c = 0; c < NB_ELTS_V8; c++) {
+                    OPJ_Dc(i) -= OPJ_Sc(i);
+                }
+            }
+            for (c = 0; c < NB_ELTS_V8; c++) {
+                OPJ_Sc(0) += (OPJ_Dc(0) + OPJ_Dc(0) + 2) >> 2;
+            }
+            for (i = 1; i < dn; i++) {
+                for (c = 0; c < NB_ELTS_V8; c++) {
+                    OPJ_Sc(i) += (OPJ_Dc(i - 1) + OPJ_Dc(i) + 2) >> 2;
+                }
+            }
+            if (((height) % 2) == 1) {
+                for (c = 0; c < NB_ELTS_V8; c++) {
+                    OPJ_Sc(i) += (OPJ_Dc(i - 1) + OPJ_Dc(i - 1) + 2) >> 2;
+                }
+            }
+        }
+    } else {
+        OPJ_UINT32 c;
+        if (height == 1) {
+            for (c = 0; c < NB_ELTS_V8; c++) {
+                OPJ_Sc(0) *= 2;
+            }
+        } else {
+            OPJ_UINT32 i;
+            for (c = 0; c < NB_ELTS_V8; c++) {
+                OPJ_Sc(0) -= OPJ_Dc(0);
+            }
+            for (i = 1; i < sn; i++) {
+                for (c = 0; c < NB_ELTS_V8; c++) {
+                    OPJ_Sc(i) -= (OPJ_Dc(i) + OPJ_Dc(i - 1)) >> 1;
+                }
+            }
+            if (((height) % 2) == 1) {
+                for (c = 0; c < NB_ELTS_V8; c++) {
+                    OPJ_Sc(i) -= OPJ_Dc(i - 1);
+                }
+            }
+            for (i = 0; i + 1 < dn; i++) {
+                for (c = 0; c < NB_ELTS_V8; c++) {
+                    OPJ_Dc(i) += (OPJ_Sc(i) + OPJ_Sc(i + 1) + 2) >> 2;
+                }
+            }
+            if (((height) % 2) == 0) {
+                for (c = 0; c < NB_ELTS_V8; c++) {
+                    OPJ_Dc(i) += (OPJ_Sc(i) + OPJ_Sc(i) + 2) >> 2;
+                }
+            }
+        }
+    }
+#endif
+
+    if (cols == NB_ELTS_V8) {
+        opj_dwt_deinterleave_v_cols(tmp, array, (OPJ_INT32)dn, (OPJ_INT32)sn,
+                                    stride_width, even ? 0 : 1, NB_ELTS_V8);
+    } else {
+        opj_dwt_deinterleave_v_cols(tmp, array, (OPJ_INT32)dn, (OPJ_INT32)sn,
+                                    stride_width, even ? 0 : 1, cols);
+    }
+}
+
+static void opj_v8dwt_encode_step1(OPJ_FLOAT32* fw,
+                                   OPJ_UINT32 end,
+                                   const OPJ_FLOAT32 cst)
+{
+    OPJ_UINT32 i;
+#ifdef __SSE__
+    __m128* vw = (__m128*) fw;
+    const __m128 vcst = _mm_set1_ps(cst);
+    for (i = 0; i < end; ++i) {
+        vw[0] = _mm_mul_ps(vw[0], vcst);
+        vw[1] = _mm_mul_ps(vw[1], vcst);
+        vw += 2 * (NB_ELTS_V8 * sizeof(OPJ_FLOAT32) / sizeof(__m128));
+    }
+#else
+    OPJ_UINT32 c;
+    for (i = 0; i < end; ++i) {
+        for (c = 0; c < NB_ELTS_V8; c++) {
+            fw[i * 2 * NB_ELTS_V8 + c] *= cst;
+        }
+    }
+#endif
+}
+
+static void opj_v8dwt_encode_step2(OPJ_FLOAT32* fl, OPJ_FLOAT32* fw,
+                                   OPJ_UINT32 end,
+                                   OPJ_UINT32 m,
+                                   OPJ_FLOAT32 cst)
+{
+    OPJ_UINT32 i;
+    OPJ_UINT32 imax = opj_uint_min(end, m);
+#ifdef __SSE__
+    __m128* vw = (__m128*) fw;
+    __m128 vcst = _mm_set1_ps(cst);
+    if (imax > 0) {
+        __m128* vl = (__m128*) fl;
+        vw[-2] = _mm_add_ps(vw[-2], _mm_mul_ps(_mm_add_ps(vl[0], vw[0]), vcst));
+        vw[-1] = _mm_add_ps(vw[-1], _mm_mul_ps(_mm_add_ps(vl[1], vw[1]), vcst));
+        vw += 2 * (NB_ELTS_V8 * sizeof(OPJ_FLOAT32) / sizeof(__m128));
+        i = 1;
+
+        for (; i < imax; ++i) {
+            vw[-2] = _mm_add_ps(vw[-2], _mm_mul_ps(_mm_add_ps(vw[-4], vw[0]), vcst));
+            vw[-1] = _mm_add_ps(vw[-1], _mm_mul_ps(_mm_add_ps(vw[-3], vw[1]), vcst));
+            vw += 2 * (NB_ELTS_V8 * sizeof(OPJ_FLOAT32) / sizeof(__m128));
+        }
+    }
+    if (m < end) {
+        assert(m + 1 == end);
+        vcst = _mm_add_ps(vcst, vcst);
+        vw[-2] = _mm_add_ps(vw[-2], _mm_mul_ps(vw[-4], vcst));
+        vw[-1] = _mm_add_ps(vw[-1], _mm_mul_ps(vw[-3], vcst));
+    }
+#else
+    OPJ_INT32 c;
+    if (imax > 0) {
+        for (c = 0; c < NB_ELTS_V8; c++) {
+            fw[-1 * NB_ELTS_V8 + c] += (fl[0 * NB_ELTS_V8 + c] + fw[0 * NB_ELTS_V8 + c]) *
+                                       cst;
+        }
+        fw += 2 * NB_ELTS_V8;
+        i = 1;
+        for (; i < imax; ++i) {
+            for (c = 0; c < NB_ELTS_V8; c++) {
+                fw[-1 * NB_ELTS_V8 + c] += (fw[-2 * NB_ELTS_V8 + c] + fw[0 * NB_ELTS_V8 + c]) *
+                                           cst;
+            }
+            fw += 2 * NB_ELTS_V8;
+        }
+    }
+    if (m < end) {
+        assert(m + 1 == end);
+        for (c = 0; c < NB_ELTS_V8; c++) {
+            fw[-1 * NB_ELTS_V8 + c] += (2 * fw[-2 * NB_ELTS_V8 + c]) * cst;
+        }
+    }
+#endif
+}
+
+/* Forward 9-7 transform, for the vertical pass, processing cols columns */
+/* where cols <= NB_ELTS_V8 */
+static void opj_dwt_encode_and_deinterleave_v_real(
+    void *arrayIn,
+    void *tmpIn,
+    OPJ_UINT32 height,
+    OPJ_BOOL even,
+    OPJ_UINT32 stride_width,
+    OPJ_UINT32 cols)
+{
+    OPJ_FLOAT32* OPJ_RESTRICT array = (OPJ_FLOAT32 * OPJ_RESTRICT)arrayIn;
+    OPJ_FLOAT32* OPJ_RESTRICT tmp = (OPJ_FLOAT32 * OPJ_RESTRICT)tmpIn;
+    const OPJ_INT32 sn = (OPJ_INT32)((height + (even ? 1 : 0)) >> 1);
+    const OPJ_INT32 dn = (OPJ_INT32)(height - (OPJ_UINT32)sn);
+    OPJ_INT32 a, b;
+
+    if (height == 1) {
+        return;
+    }
+
+    opj_dwt_fetch_cols_vertical_pass(arrayIn, tmpIn, height, stride_width, cols);
+
+    if (even) {
+        a = 0;
+        b = 1;
+    } else {
+        a = 1;
+        b = 0;
+    }
+    opj_v8dwt_encode_step2(tmp + a * NB_ELTS_V8,
+                           tmp + (b + 1) * NB_ELTS_V8,
+                           (OPJ_UINT32)dn,
+                           (OPJ_UINT32)opj_int_min(dn, sn - b),
+                           opj_dwt_alpha);
+    opj_v8dwt_encode_step2(tmp + b * NB_ELTS_V8,
+                           tmp + (a + 1) * NB_ELTS_V8,
+                           (OPJ_UINT32)sn,
+                           (OPJ_UINT32)opj_int_min(sn, dn - a),
+                           opj_dwt_beta);
+    opj_v8dwt_encode_step2(tmp + a * NB_ELTS_V8,
+                           tmp + (b + 1) * NB_ELTS_V8,
+                           (OPJ_UINT32)dn,
+                           (OPJ_UINT32)opj_int_min(dn, sn - b),
+                           opj_dwt_gamma);
+    opj_v8dwt_encode_step2(tmp + b * NB_ELTS_V8,
+                           tmp + (a + 1) * NB_ELTS_V8,
+                           (OPJ_UINT32)sn,
+                           (OPJ_UINT32)opj_int_min(sn, dn - a),
+                           opj_dwt_delta);
+    opj_v8dwt_encode_step1(tmp + b * NB_ELTS_V8, (OPJ_UINT32)dn,
+                           opj_K);
+    opj_v8dwt_encode_step1(tmp + a * NB_ELTS_V8, (OPJ_UINT32)sn,
+                           opj_invK);
+
+
+    if (cols == NB_ELTS_V8) {
+        opj_dwt_deinterleave_v_cols((OPJ_INT32*)tmp,
+                                    (OPJ_INT32*)array,
+                                    (OPJ_INT32)dn, (OPJ_INT32)sn,
+                                    stride_width, even ? 0 : 1, NB_ELTS_V8);
+    } else {
+        opj_dwt_deinterleave_v_cols((OPJ_INT32*)tmp,
+                                    (OPJ_INT32*)array,
+                                    (OPJ_INT32)dn, (OPJ_INT32)sn,
+                                    stride_width, even ? 0 : 1, cols);
+    }
+}
+
+
+/* <summary>                            */
+/* Forward 5-3 wavelet transform in 2-D. */
+/* </summary>                           */
+static INLINE OPJ_BOOL opj_dwt_encode_procedure(opj_thread_pool_t* tp,
+        opj_tcd_tilecomp_t * tilec,
+        opj_encode_and_deinterleave_v_fnptr_type p_encode_and_deinterleave_v,
+        opj_encode_and_deinterleave_h_one_row_fnptr_type
+        p_encode_and_deinterleave_h_one_row)
+{
+    OPJ_INT32 i;
+    OPJ_INT32 *bj = 00;
+    OPJ_UINT32 w;
+    OPJ_INT32 l;
+
+    OPJ_SIZE_T l_data_size;
+
+    opj_tcd_resolution_t * l_cur_res = 0;
+    opj_tcd_resolution_t * l_last_res = 0;
+    const int num_threads = opj_thread_pool_get_thread_count(tp);
+    OPJ_INT32 * OPJ_RESTRICT tiledp = tilec->data;
+
+    w = (OPJ_UINT32)(tilec->x1 - tilec->x0);
+    l = (OPJ_INT32)tilec->numresolutions - 1;
+
+    l_cur_res = tilec->resolutions + l;
+    l_last_res = l_cur_res - 1;
+
+    l_data_size = opj_dwt_max_resolution(tilec->resolutions, tilec->numresolutions);
+    /* overflow check */
+    if (l_data_size > (SIZE_MAX / (NB_ELTS_V8 * sizeof(OPJ_INT32)))) {
+        /* FIXME event manager error callback */
+        return OPJ_FALSE;
+    }
+    l_data_size *= NB_ELTS_V8 * sizeof(OPJ_INT32);
+    bj = (OPJ_INT32*)opj_aligned_32_malloc(l_data_size);
+    /* l_data_size is equal to 0 when numresolutions == 1 but bj is not used */
+    /* in that case, so do not error out */
+    if (l_data_size != 0 && ! bj) {
+        return OPJ_FALSE;
+    }
+    i = l;
+
+    while (i--) {
+        OPJ_UINT32 j;
+        OPJ_UINT32 rw;           /* width of the resolution level computed   */
+        OPJ_UINT32 rh;           /* height of the resolution level computed  */
+        OPJ_UINT32
+        rw1;      /* width of the resolution level once lower than computed one                                       */
+        OPJ_UINT32
+        rh1;      /* height of the resolution level once lower than computed one                                      */
+        OPJ_INT32 cas_col;  /* 0 = non inversion on horizontal filtering 1 = inversion between low-pass and high-pass filtering */
+        OPJ_INT32 cas_row;  /* 0 = non inversion on vertical filtering 1 = inversion between low-pass and high-pass filtering   */
+        OPJ_INT32 dn, sn;
+
+        rw  = (OPJ_UINT32)(l_cur_res->x1 - l_cur_res->x0);
+        rh  = (OPJ_UINT32)(l_cur_res->y1 - l_cur_res->y0);
+        rw1 = (OPJ_UINT32)(l_last_res->x1 - l_last_res->x0);
+        rh1 = (OPJ_UINT32)(l_last_res->y1 - l_last_res->y0);
+
+        cas_row = l_cur_res->x0 & 1;
+        cas_col = l_cur_res->y0 & 1;
+
+        sn = (OPJ_INT32)rh1;
+        dn = (OPJ_INT32)(rh - rh1);
+
+        /* Perform vertical pass */
+        if (num_threads <= 1 || rw < 2 * NB_ELTS_V8) {
+            for (j = 0; j + NB_ELTS_V8 - 1 < rw; j += NB_ELTS_V8) {
+                p_encode_and_deinterleave_v(tiledp + j,
+                                            bj,
+                                            rh,
+                                            cas_col == 0,
+                                            w,
+                                            NB_ELTS_V8);
+            }
+            if (j < rw) {
+                p_encode_and_deinterleave_v(tiledp + j,
+                                            bj,
+                                            rh,
+                                            cas_col == 0,
+                                            w,
+                                            rw - j);
+            }
+        }  else {
+            OPJ_UINT32 num_jobs = (OPJ_UINT32)num_threads;
+            OPJ_UINT32 step_j;
+
+            if (rw < num_jobs) {
+                num_jobs = rw;
+            }
+            step_j = ((rw / num_jobs) / NB_ELTS_V8) * NB_ELTS_V8;
+
+            for (j = 0; j < num_jobs; j++) {
+                opj_dwt_encode_v_job_t* job;
+
+                job = (opj_dwt_encode_v_job_t*) opj_malloc(sizeof(opj_dwt_encode_v_job_t));
+                if (!job) {
+                    opj_thread_pool_wait_completion(tp, 0);
+                    opj_aligned_free(bj);
+                    return OPJ_FALSE;
+                }
+                job->v.mem = (OPJ_INT32*)opj_aligned_32_malloc(l_data_size);
+                if (!job->v.mem) {
+                    opj_thread_pool_wait_completion(tp, 0);
+                    opj_free(job);
+                    opj_aligned_free(bj);
+                    return OPJ_FALSE;
+                }
+                job->v.dn = dn;
+                job->v.sn = sn;
+                job->v.cas = cas_col;
+                job->rh = rh;
+                job->w = w;
+                job->tiledp = tiledp;
+                job->min_j = j * step_j;
+                job->max_j = (j + 1 == num_jobs) ? rw : (j + 1) * step_j;
+                job->p_encode_and_deinterleave_v = p_encode_and_deinterleave_v;
+                opj_thread_pool_submit_job(tp, opj_dwt_encode_v_func, job);
+            }
+            opj_thread_pool_wait_completion(tp, 0);
+        }
+
+        sn = (OPJ_INT32)rw1;
+        dn = (OPJ_INT32)(rw - rw1);
+
+        /* Perform horizontal pass */
+        if (num_threads <= 1 || rh <= 1) {
+            for (j = 0; j < rh; j++) {
+                OPJ_INT32* OPJ_RESTRICT aj = tiledp + j * w;
+                (*p_encode_and_deinterleave_h_one_row)(aj, bj, rw,
+                                                       cas_row == 0 ? OPJ_TRUE : OPJ_FALSE);
+            }
+        }  else {
+            OPJ_UINT32 num_jobs = (OPJ_UINT32)num_threads;
+            OPJ_UINT32 step_j;
+
+            if (rh < num_jobs) {
+                num_jobs = rh;
+            }
+            step_j = (rh / num_jobs);
+
+            for (j = 0; j < num_jobs; j++) {
+                opj_dwt_encode_h_job_t* job;
+
+                job = (opj_dwt_encode_h_job_t*) opj_malloc(sizeof(opj_dwt_encode_h_job_t));
+                if (!job) {
+                    opj_thread_pool_wait_completion(tp, 0);
+                    opj_aligned_free(bj);
+                    return OPJ_FALSE;
+                }
+                job->h.mem = (OPJ_INT32*)opj_aligned_32_malloc(l_data_size);
+                if (!job->h.mem) {
+                    opj_thread_pool_wait_completion(tp, 0);
+                    opj_free(job);
+                    opj_aligned_free(bj);
+                    return OPJ_FALSE;
+                }
+                job->h.dn = dn;
+                job->h.sn = sn;
+                job->h.cas = cas_row;
+                job->rw = rw;
+                job->w = w;
+                job->tiledp = tiledp;
+                job->min_j = j * step_j;
+                job->max_j = (j + 1U) * step_j; /* this can overflow */
+                if (j == (num_jobs - 1U)) {  /* this will take care of the overflow */
+                    job->max_j = rh;
+                }
+                job->p_function = p_encode_and_deinterleave_h_one_row;
+                opj_thread_pool_submit_job(tp, opj_dwt_encode_h_func, job);
+            }
+            opj_thread_pool_wait_completion(tp, 0);
+        }
+
+        l_cur_res = l_last_res;
+
+        --l_last_res;
+    }
+
+    opj_aligned_free(bj);
+    return OPJ_TRUE;
+}
+
+/* Forward 5-3 wavelet transform in 2-D. */
+/* </summary>                           */
+OPJ_BOOL opj_dwt_encode(opj_tcd_t *p_tcd,
+                        opj_tcd_tilecomp_t * tilec)
+{
+    return opj_dwt_encode_procedure(p_tcd->thread_pool, tilec,
+                                    opj_dwt_encode_and_deinterleave_v,
+                                    opj_dwt_encode_and_deinterleave_h_one_row);
+}
+
+/* <summary>                            */
+/* Inverse 5-3 wavelet transform in 2-D. */
+/* </summary>                           */
+OPJ_BOOL opj_dwt_decode(opj_tcd_t *p_tcd, opj_tcd_tilecomp_t* tilec,
+                        OPJ_UINT32 numres)
+{
+    if (p_tcd->whole_tile_decoding) {
+        return opj_dwt_decode_tile(p_tcd->thread_pool, tilec, numres);
+    } else {
+        return opj_dwt_decode_partial_tile(tilec, numres);
+    }
+}
+
+/* <summary>                */
+/* Get norm of 5-3 wavelet. */
+/* </summary>               */
+OPJ_FLOAT64 opj_dwt_getnorm(OPJ_UINT32 level, OPJ_UINT32 orient)
+{
+    /* FIXME ! This is just a band-aid to avoid a buffer overflow */
+    /* but the array should really be extended up to 33 resolution levels */
+    /* See https://github.com/uclouvain/openjpeg/issues/493 */
+    if (orient == 0 && level >= 10) {
+        level = 9;
+    } else if (orient > 0 && level >= 9) {
+        level = 8;
+    }
+    return opj_dwt_norms[orient][level];
+}
+
+/* <summary>                             */
+/* Forward 9-7 wavelet transform in 2-D. */
+/* </summary>                            */
+OPJ_BOOL opj_dwt_encode_real(opj_tcd_t *p_tcd,
+                             opj_tcd_tilecomp_t * tilec)
+{
+    return opj_dwt_encode_procedure(p_tcd->thread_pool, tilec,
+                                    opj_dwt_encode_and_deinterleave_v_real,
+                                    opj_dwt_encode_and_deinterleave_h_one_row_real);
+}
+
+/* <summary>                */
+/* Get norm of 9-7 wavelet. */
+/* </summary>               */
+OPJ_FLOAT64 opj_dwt_getnorm_real(OPJ_UINT32 level, OPJ_UINT32 orient)
+{
+    /* FIXME ! This is just a band-aid to avoid a buffer overflow */
+    /* but the array should really be extended up to 33 resolution levels */
+    /* See https://github.com/uclouvain/openjpeg/issues/493 */
+    if (orient == 0 && level >= 10) {
+        level = 9;
+    } else if (orient > 0 && level >= 9) {
+        level = 8;
+    }
+    return opj_dwt_norms_real[orient][level];
+}
+
+void opj_dwt_calc_explicit_stepsizes(opj_tccp_t * tccp, OPJ_UINT32 prec)
+{
+    OPJ_UINT32 numbands, bandno;
+    numbands = 3 * tccp->numresolutions - 2;
+    for (bandno = 0; bandno < numbands; bandno++) {
+        OPJ_FLOAT64 stepsize;
+        OPJ_UINT32 resno, level, orient, gain;
+
+        resno = (bandno == 0) ? 0 : ((bandno - 1) / 3 + 1);
+        orient = (bandno == 0) ? 0 : ((bandno - 1) % 3 + 1);
+        level = tccp->numresolutions - 1 - resno;
+        gain = (tccp->qmfbid == 0) ? 0 : ((orient == 0) ? 0 : (((orient == 1) ||
+                                          (orient == 2)) ? 1 : 2));
+        if (tccp->qntsty == J2K_CCP_QNTSTY_NOQNT) {
+            stepsize = 1.0;
+        } else {
+            OPJ_FLOAT64 norm = opj_dwt_getnorm_real(level, orient);
+            stepsize = (1 << (gain)) / norm;
+        }
+        opj_dwt_encode_stepsize((OPJ_INT32) floor(stepsize * 8192.0),
+                                (OPJ_INT32)(prec + gain), &tccp->stepsizes[bandno]);
+    }
+}
+
+/* <summary>                             */
+/* Determine maximum computed resolution level for inverse wavelet transform */
+/* </summary>                            */
+static OPJ_UINT32 opj_dwt_max_resolution(opj_tcd_resolution_t* OPJ_RESTRICT r,
+        OPJ_UINT32 i)
+{
+    OPJ_UINT32 mr   = 0;
+    OPJ_UINT32 w;
+    while (--i) {
+        ++r;
+        if (mr < (w = (OPJ_UINT32)(r->x1 - r->x0))) {
+            mr = w ;
+        }
+        if (mr < (w = (OPJ_UINT32)(r->y1 - r->y0))) {
+            mr = w ;
+        }
+    }
+    return mr ;
+}
+
+typedef struct {
+    opj_dwt_t h;
+    OPJ_UINT32 rw;
+    OPJ_UINT32 w;
+    OPJ_INT32 * OPJ_RESTRICT tiledp;
+    OPJ_UINT32 min_j;
+    OPJ_UINT32 max_j;
+} opj_dwt_decode_h_job_t;
+
+static void opj_dwt_decode_h_func(void* user_data, opj_tls_t* tls)
+{
+    OPJ_UINT32 j;
+    opj_dwt_decode_h_job_t* job;
+    (void)tls;
+
+    job = (opj_dwt_decode_h_job_t*)user_data;
+    for (j = job->min_j; j < job->max_j; j++) {
+        opj_idwt53_h(&job->h, &job->tiledp[j * job->w]);
+    }
+
+    opj_aligned_free(job->h.mem);
+    opj_free(job);
+}
+
+typedef struct {
+    opj_dwt_t v;
+    OPJ_UINT32 rh;
+    OPJ_UINT32 w;
+    OPJ_INT32 * OPJ_RESTRICT tiledp;
+    OPJ_UINT32 min_j;
+    OPJ_UINT32 max_j;
+} opj_dwt_decode_v_job_t;
+
+static void opj_dwt_decode_v_func(void* user_data, opj_tls_t* tls)
+{
+    OPJ_UINT32 j;
+    opj_dwt_decode_v_job_t* job;
+    (void)tls;
+
+    job = (opj_dwt_decode_v_job_t*)user_data;
+    for (j = job->min_j; j + PARALLEL_COLS_53 <= job->max_j;
+            j += PARALLEL_COLS_53) {
+        opj_idwt53_v(&job->v, &job->tiledp[j], (OPJ_SIZE_T)job->w,
+                     PARALLEL_COLS_53);
+    }
+    if (j < job->max_j)
+        opj_idwt53_v(&job->v, &job->tiledp[j], (OPJ_SIZE_T)job->w,
+                     (OPJ_INT32)(job->max_j - j));
+
+    opj_aligned_free(job->v.mem);
+    opj_free(job);
+}
+
+
+/* <summary>                            */
+/* Inverse wavelet transform in 2-D.    */
+/* </summary>                           */
+static OPJ_BOOL opj_dwt_decode_tile(opj_thread_pool_t* tp,
+        const opj_tcd_tilecomp_t* tilec, OPJ_UINT32 numres)
+{
+    opj_dwt_t h;
+    opj_dwt_t v;
+
+    opj_tcd_resolution_t* tr = tilec->resolutions;
+
+    OPJ_UINT32 rw = (OPJ_UINT32)(tr->x1 -
+                                 tr->x0);  /* width of the resolution level computed */
+    OPJ_UINT32 rh = (OPJ_UINT32)(tr->y1 -
+                                 tr->y0);  /* height of the resolution level computed */
+
+    OPJ_UINT32 w = (OPJ_UINT32)(tilec->resolutions[tilec->minimum_num_resolutions -
+                                                               1].x1 -
+                                tilec->resolutions[tilec->minimum_num_resolutions - 1].x0);
+    OPJ_SIZE_T h_mem_size;
+    int num_threads;
+
+    if (numres == 1U) {
+        return OPJ_TRUE;
+    }
+    num_threads = opj_thread_pool_get_thread_count(tp);
+    h.mem_count = opj_dwt_max_resolution(tr, numres);
+    /* overflow check */
+    if (h.mem_count > (SIZE_MAX / PARALLEL_COLS_53 / sizeof(OPJ_INT32))) {
+        /* FIXME event manager error callback */
+        return OPJ_FALSE;
+    }
+    /* We need PARALLEL_COLS_53 times the height of the array, */
+    /* since for the vertical pass */
+    /* we process PARALLEL_COLS_53 columns at a time */
+    h_mem_size = h.mem_count * PARALLEL_COLS_53 * sizeof(OPJ_INT32);
+    h.mem = (OPJ_INT32*)opj_aligned_32_malloc(h_mem_size);
+    if (! h.mem) {
+        /* FIXME event manager error callback */
+        return OPJ_FALSE;
+    }
+
+    v.mem_count = h.mem_count;
+    v.mem = h.mem;
+
+    while (--numres) {
+        OPJ_INT32 * OPJ_RESTRICT tiledp = tilec->data;
+        OPJ_UINT32 j;
+
+        ++tr;
+        h.sn = (OPJ_INT32)rw;
+        v.sn = (OPJ_INT32)rh;
+
+        rw = (OPJ_UINT32)(tr->x1 - tr->x0);
+        rh = (OPJ_UINT32)(tr->y1 - tr->y0);
+
+        h.dn = (OPJ_INT32)(rw - (OPJ_UINT32)h.sn);
+        h.cas = tr->x0 % 2;
+
+        if (num_threads <= 1 || rh <= 1) {
+            for (j = 0; j < rh; ++j) {
+                opj_idwt53_h(&h, &tiledp[(OPJ_SIZE_T)j * w]);
+            }
+        } else {
+            OPJ_UINT32 num_jobs = (OPJ_UINT32)num_threads;
+            OPJ_UINT32 step_j;
+
+            if (rh < num_jobs) {
+                num_jobs = rh;
+            }
+            step_j = (rh / num_jobs);
+
+            for (j = 0; j < num_jobs; j++) {
+                opj_dwt_decode_h_job_t* job;
+
+                job = (opj_dwt_decode_h_job_t*) opj_malloc(sizeof(opj_dwt_decode_h_job_t));
+                if (!job) {
+                    /* It would be nice to fallback to single thread case, but */
+                    /* unfortunately some jobs may be launched and have modified */
+                    /* tiledp, so it is not practical to recover from that error */
+                    /* FIXME event manager error callback */
+                    opj_thread_pool_wait_completion(tp, 0);
+                    opj_aligned_free(h.mem);
+                    return OPJ_FALSE;
+                }
+                job->h = h;
+                job->rw = rw;
+                job->w = w;
+                job->tiledp = tiledp;
+                job->min_j = j * step_j;
+                job->max_j = (j + 1U) * step_j; /* this can overflow */
+                if (j == (num_jobs - 1U)) {  /* this will take care of the overflow */
+                    job->max_j = rh;
+                }
+                job->h.mem = (OPJ_INT32*)opj_aligned_32_malloc(h_mem_size);
+                if (!job->h.mem) {
+                    /* FIXME event manager error callback */
+                    opj_thread_pool_wait_completion(tp, 0);
+                    opj_free(job);
+                    opj_aligned_free(h.mem);
+                    return OPJ_FALSE;
+                }
+                opj_thread_pool_submit_job(tp, opj_dwt_decode_h_func, job);
+            }
+            opj_thread_pool_wait_completion(tp, 0);
+        }
+
+        v.dn = (OPJ_INT32)(rh - (OPJ_UINT32)v.sn);
+        v.cas = tr->y0 % 2;
+
+        if (num_threads <= 1 || rw <= 1) {
+            for (j = 0; j + PARALLEL_COLS_53 <= rw;
+                    j += PARALLEL_COLS_53) {
+                opj_idwt53_v(&v, &tiledp[j], (OPJ_SIZE_T)w, PARALLEL_COLS_53);
+            }
+            if (j < rw) {
+                opj_idwt53_v(&v, &tiledp[j], (OPJ_SIZE_T)w, (OPJ_INT32)(rw - j));
+            }
+        } else {
+            OPJ_UINT32 num_jobs = (OPJ_UINT32)num_threads;
+            OPJ_UINT32 step_j;
+
+            if (rw < num_jobs) {
+                num_jobs = rw;
+            }
+            step_j = (rw / num_jobs);
+
+            for (j = 0; j < num_jobs; j++) {
+                opj_dwt_decode_v_job_t* job;
+
+                job = (opj_dwt_decode_v_job_t*) opj_malloc(sizeof(opj_dwt_decode_v_job_t));
+                if (!job) {
+                    /* It would be nice to fallback to single thread case, but */
+                    /* unfortunately some jobs may be launched and have modified */
+                    /* tiledp, so it is not practical to recover from that error */
+                    /* FIXME event manager error callback */
+                    opj_thread_pool_wait_completion(tp, 0);
+                    opj_aligned_free(v.mem);
+                    return OPJ_FALSE;
+                }
+                job->v = v;
+                job->rh = rh;
+                job->w = w;
+                job->tiledp = tiledp;
+                job->min_j = j * step_j;
+                job->max_j = (j + 1U) * step_j; /* this can overflow */
+                if (j == (num_jobs - 1U)) {  /* this will take care of the overflow */
+                    job->max_j = rw;
+                }
+                job->v.mem = (OPJ_INT32*)opj_aligned_32_malloc(h_mem_size);
+                if (!job->v.mem) {
+                    /* FIXME event manager error callback */
+                    opj_thread_pool_wait_completion(tp, 0);
+                    opj_free(job);
+                    opj_aligned_free(v.mem);
+                    return OPJ_FALSE;
+                }
+                opj_thread_pool_submit_job(tp, opj_dwt_decode_v_func, job);
+            }
+            opj_thread_pool_wait_completion(tp, 0);
+        }
+    }
+    opj_aligned_free(h.mem);
+    return OPJ_TRUE;
+}
+
+static void opj_dwt_interleave_partial_h(OPJ_INT32 *dest,
+        OPJ_INT32 cas,
+        opj_sparse_array_int32_t* sa,
+        OPJ_UINT32 sa_line,
+        OPJ_UINT32 sn,
+        OPJ_UINT32 win_l_x0,
+        OPJ_UINT32 win_l_x1,
+        OPJ_UINT32 win_h_x0,
+        OPJ_UINT32 win_h_x1)
+{
+    OPJ_BOOL ret;
+    ret = opj_sparse_array_int32_read(sa,
+                                      win_l_x0, sa_line,
+                                      win_l_x1, sa_line + 1,
+                                      dest + cas + 2 * win_l_x0,
+                                      2, 0, OPJ_TRUE);
+    assert(ret);
+    ret = opj_sparse_array_int32_read(sa,
+                                      sn + win_h_x0, sa_line,
+                                      sn + win_h_x1, sa_line + 1,
+                                      dest + 1 - cas + 2 * win_h_x0,
+                                      2, 0, OPJ_TRUE);
+    assert(ret);
+    OPJ_UNUSED(ret);
+}
+
+
+static void opj_dwt_interleave_partial_v(OPJ_INT32 *dest,
+        OPJ_INT32 cas,
+        opj_sparse_array_int32_t* sa,
+        OPJ_UINT32 sa_col,
+        OPJ_UINT32 nb_cols,
+        OPJ_UINT32 sn,
+        OPJ_UINT32 win_l_y0,
+        OPJ_UINT32 win_l_y1,
+        OPJ_UINT32 win_h_y0,
+        OPJ_UINT32 win_h_y1)
+{
+    OPJ_BOOL ret;
+    ret  = opj_sparse_array_int32_read(sa,
+                                       sa_col, win_l_y0,
+                                       sa_col + nb_cols, win_l_y1,
+                                       dest + cas * 4 + 2 * 4 * win_l_y0,
+                                       1, 2 * 4, OPJ_TRUE);
+    assert(ret);
+    ret = opj_sparse_array_int32_read(sa,
+                                      sa_col, sn + win_h_y0,
+                                      sa_col + nb_cols, sn + win_h_y1,
+                                      dest + (1 - cas) * 4 + 2 * 4 * win_h_y0,
+                                      1, 2 * 4, OPJ_TRUE);
+    assert(ret);
+    OPJ_UNUSED(ret);
+}
+
+static void opj_dwt_decode_partial_1(OPJ_INT32 *a, OPJ_SIZE_T a_count,
+                                     OPJ_INT32 dn, OPJ_INT32 sn,
+                                     OPJ_INT32 cas,
+                                     OPJ_INT32 win_l_x0,
+                                     OPJ_INT32 win_l_x1,
+                                     OPJ_INT32 win_h_x0,
+                                     OPJ_INT32 win_h_x1)
+{
+    OPJ_INT32 i;
+
+    if (!cas) {
+        if ((dn > 0) || (sn > 1)) { /* NEW :  CASE ONE ELEMENT */
+
+            /* Naive version is :
+            for (i = win_l_x0; i < i_max; i++) {
+                OPJ_S(i) -= (OPJ_D_(i - 1) + OPJ_D_(i) + 2) >> 2;
+            }
+            for (i = win_h_x0; i < win_h_x1; i++) {
+                OPJ_D(i) += (OPJ_S_(i) + OPJ_S_(i + 1)) >> 1;
+            }
+            but the compiler doesn't manage to unroll it to avoid bound
+            checking in OPJ_S_ and OPJ_D_ macros
+            */
+
+            i = win_l_x0;
+            if (i < win_l_x1) {
+                OPJ_INT32 i_max;
+
+                /* Left-most case */
+                OPJ_S(i) -= (OPJ_D_(i - 1) + OPJ_D_(i) + 2) >> 2;
+                i ++;
+
+                i_max = win_l_x1;
+                if (i_max > dn) {
+                    i_max = dn;
+                }
+                for (; i < i_max; i++) {
+                    /* No bound checking */
+                    OPJ_S(i) -= (OPJ_D(i - 1) + OPJ_D(i) + 2) >> 2;
+                }
+                for (; i < win_l_x1; i++) {
+                    /* Right-most case */
+                    OPJ_S(i) -= (OPJ_D_(i - 1) + OPJ_D_(i) + 2) >> 2;
+                }
+            }
+
+            i = win_h_x0;
+            if (i < win_h_x1) {
+                OPJ_INT32 i_max = win_h_x1;
+                if (i_max >= sn) {
+                    i_max = sn - 1;
+                }
+                for (; i < i_max; i++) {
+                    /* No bound checking */
+                    OPJ_D(i) += (OPJ_S(i) + OPJ_S(i + 1)) >> 1;
+                }
+                for (; i < win_h_x1; i++) {
+                    /* Right-most case */
+                    OPJ_D(i) += (OPJ_S_(i) + OPJ_S_(i + 1)) >> 1;
+                }
+            }
+        }
+    } else {
+        if (!sn  && dn == 1) {        /* NEW :  CASE ONE ELEMENT */
+            OPJ_S(0) /= 2;
+        } else {
+            for (i = win_l_x0; i < win_l_x1; i++) {
+                OPJ_D(i) = opj_int_sub_no_overflow(OPJ_D(i),
+                                                   opj_int_add_no_overflow(opj_int_add_no_overflow(OPJ_SS_(i), OPJ_SS_(i + 1)),
+                                                           2) >> 2);
+            }
+            for (i = win_h_x0; i < win_h_x1; i++) {
+                OPJ_S(i) = opj_int_add_no_overflow(OPJ_S(i),
+                                                   opj_int_add_no_overflow(OPJ_DD_(i), OPJ_DD_(i - 1)) >> 1);
+            }
+        }
+    }
+}
+
+#define OPJ_S_off(i,off) a[(OPJ_UINT32)(i)*2*4+off]
+#define OPJ_D_off(i,off) a[(1+(OPJ_UINT32)(i)*2)*4+off]
+#define OPJ_S__off(i,off) ((i)<0?OPJ_S_off(0,off):((i)>=sn?OPJ_S_off(sn-1,off):OPJ_S_off(i,off)))
+#define OPJ_D__off(i,off) ((i)<0?OPJ_D_off(0,off):((i)>=dn?OPJ_D_off(dn-1,off):OPJ_D_off(i,off)))
+#define OPJ_SS__off(i,off) ((i)<0?OPJ_S_off(0,off):((i)>=dn?OPJ_S_off(dn-1,off):OPJ_S_off(i,off)))
+#define OPJ_DD__off(i,off) ((i)<0?OPJ_D_off(0,off):((i)>=sn?OPJ_D_off(sn-1,off):OPJ_D_off(i,off)))
+
+static void opj_dwt_decode_partial_1_parallel(OPJ_INT32 *a,
+        OPJ_UINT32 nb_cols,
+        OPJ_INT32 dn, OPJ_INT32 sn,
+        OPJ_INT32 cas,
+        OPJ_INT32 win_l_x0,
+        OPJ_INT32 win_l_x1,
+        OPJ_INT32 win_h_x0,
+        OPJ_INT32 win_h_x1)
+{
+    OPJ_INT32 i;
+    OPJ_UINT32 off;
+
+    (void)nb_cols;
+
+    if (!cas) {
+        if ((dn > 0) || (sn > 1)) { /* NEW :  CASE ONE ELEMENT */
+
+            /* Naive version is :
+            for (i = win_l_x0; i < i_max; i++) {
+                OPJ_S(i) -= (OPJ_D_(i - 1) + OPJ_D_(i) + 2) >> 2;
+            }
+            for (i = win_h_x0; i < win_h_x1; i++) {
+                OPJ_D(i) += (OPJ_S_(i) + OPJ_S_(i + 1)) >> 1;
+            }
+            but the compiler doesn't manage to unroll it to avoid bound
+            checking in OPJ_S_ and OPJ_D_ macros
+            */
+
+            i = win_l_x0;
+            if (i < win_l_x1) {
+                OPJ_INT32 i_max;
+
+                /* Left-most case */
+                for (off = 0; off < 4; off++) {
+                    OPJ_S_off(i, off) -= (OPJ_D__off(i - 1, off) + OPJ_D__off(i, off) + 2) >> 2;
+                }
+                i ++;
+
+                i_max = win_l_x1;
+                if (i_max > dn) {
+                    i_max = dn;
+                }
+
+#ifdef __SSE2__
+                if (i + 1 < i_max) {
+                    const __m128i two = _mm_set1_epi32(2);
+                    __m128i Dm1 = _mm_load_si128((__m128i * const)(a + 4 + (i - 1) * 8));
+                    for (; i + 1 < i_max; i += 2) {
+                        /* No bound checking */
+                        __m128i S = _mm_load_si128((__m128i * const)(a + i * 8));
+                        __m128i D = _mm_load_si128((__m128i * const)(a + 4 + i * 8));
+                        __m128i S1 = _mm_load_si128((__m128i * const)(a + (i + 1) * 8));
+                        __m128i D1 = _mm_load_si128((__m128i * const)(a + 4 + (i + 1) * 8));
+                        S = _mm_sub_epi32(S,
+                                          _mm_srai_epi32(_mm_add_epi32(_mm_add_epi32(Dm1, D), two), 2));
+                        S1 = _mm_sub_epi32(S1,
+                                           _mm_srai_epi32(_mm_add_epi32(_mm_add_epi32(D, D1), two), 2));
+                        _mm_store_si128((__m128i*)(a + i * 8), S);
+                        _mm_store_si128((__m128i*)(a + (i + 1) * 8), S1);
+                        Dm1 = D1;
+                    }
+                }
+#endif
+
+                for (; i < i_max; i++) {
+                    /* No bound checking */
+                    for (off = 0; off < 4; off++) {
+                        OPJ_S_off(i, off) -= (OPJ_D_off(i - 1, off) + OPJ_D_off(i, off) + 2) >> 2;
+                    }
+                }
+                for (; i < win_l_x1; i++) {
+                    /* Right-most case */
+                    for (off = 0; off < 4; off++) {
+                        OPJ_S_off(i, off) -= (OPJ_D__off(i - 1, off) + OPJ_D__off(i, off) + 2) >> 2;
+                    }
+                }
+            }
+
+            i = win_h_x0;
+            if (i < win_h_x1) {
+                OPJ_INT32 i_max = win_h_x1;
+                if (i_max >= sn) {
+                    i_max = sn - 1;
+                }
+
+#ifdef __SSE2__
+                if (i + 1 < i_max) {
+                    __m128i S =  _mm_load_si128((__m128i * const)(a + i * 8));
+                    for (; i + 1 < i_max; i += 2) {
+                        /* No bound checking */
+                        __m128i D = _mm_load_si128((__m128i * const)(a + 4 + i * 8));
+                        __m128i S1 = _mm_load_si128((__m128i * const)(a + (i + 1) * 8));
+                        __m128i D1 = _mm_load_si128((__m128i * const)(a + 4 + (i + 1) * 8));
+                        __m128i S2 = _mm_load_si128((__m128i * const)(a + (i + 2) * 8));
+                        D = _mm_add_epi32(D, _mm_srai_epi32(_mm_add_epi32(S, S1), 1));
+                        D1 = _mm_add_epi32(D1, _mm_srai_epi32(_mm_add_epi32(S1, S2), 1));
+                        _mm_store_si128((__m128i*)(a + 4 + i * 8), D);
+                        _mm_store_si128((__m128i*)(a + 4 + (i + 1) * 8), D1);
+                        S = S2;
+                    }
+                }
+#endif
+
+                for (; i < i_max; i++) {
+                    /* No bound checking */
+                    for (off = 0; off < 4; off++) {
+                        OPJ_D_off(i, off) += (OPJ_S_off(i, off) + OPJ_S_off(i + 1, off)) >> 1;
+                    }
+                }
+                for (; i < win_h_x1; i++) {
+                    /* Right-most case */
+                    for (off = 0; off < 4; off++) {
+                        OPJ_D_off(i, off) += (OPJ_S__off(i, off) + OPJ_S__off(i + 1, off)) >> 1;
+                    }
+                }
+            }
+        }
+    } else {
+        if (!sn  && dn == 1) {        /* NEW :  CASE ONE ELEMENT */
+            for (off = 0; off < 4; off++) {
+                OPJ_S_off(0, off) /= 2;
+            }
+        } else {
+            for (i = win_l_x0; i < win_l_x1; i++) {
+                for (off = 0; off < 4; off++) {
+                    OPJ_D_off(i, off) = opj_int_sub_no_overflow(
+                                            OPJ_D_off(i, off),
+                                            opj_int_add_no_overflow(
+                                                opj_int_add_no_overflow(OPJ_SS__off(i, off), OPJ_SS__off(i + 1, off)), 2) >> 2);
+                }
+            }
+            for (i = win_h_x0; i < win_h_x1; i++) {
+                for (off = 0; off < 4; off++) {
+                    OPJ_S_off(i, off) = opj_int_add_no_overflow(
+                                            OPJ_S_off(i, off),
+                                            opj_int_add_no_overflow(OPJ_DD__off(i, off), OPJ_DD__off(i - 1, off)) >> 1);
+                }
+            }
+        }
+    }
+}
+
+static void opj_dwt_get_band_coordinates(opj_tcd_tilecomp_t* tilec,
+        OPJ_UINT32 resno,
+        OPJ_UINT32 bandno,
+        OPJ_UINT32 tcx0,
+        OPJ_UINT32 tcy0,
+        OPJ_UINT32 tcx1,
+        OPJ_UINT32 tcy1,
+        OPJ_UINT32* tbx0,
+        OPJ_UINT32* tby0,
+        OPJ_UINT32* tbx1,
+        OPJ_UINT32* tby1)
+{
+    /* Compute number of decomposition for this band. See table F-1 */
+    OPJ_UINT32 nb = (resno == 0) ?
+                    tilec->numresolutions - 1 :
+                    tilec->numresolutions - resno;
+    /* Map above tile-based coordinates to sub-band-based coordinates per */
+    /* equation B-15 of the standard */
+    OPJ_UINT32 x0b = bandno & 1;
+    OPJ_UINT32 y0b = bandno >> 1;
+    if (tbx0) {
+        *tbx0 = (nb == 0) ? tcx0 :
+                (tcx0 <= (1U << (nb - 1)) * x0b) ? 0 :
+                opj_uint_ceildivpow2(tcx0 - (1U << (nb - 1)) * x0b, nb);
+    }
+    if (tby0) {
+        *tby0 = (nb == 0) ? tcy0 :
+                (tcy0 <= (1U << (nb - 1)) * y0b) ? 0 :
+                opj_uint_ceildivpow2(tcy0 - (1U << (nb - 1)) * y0b, nb);
+    }
+    if (tbx1) {
+        *tbx1 = (nb == 0) ? tcx1 :
+                (tcx1 <= (1U << (nb - 1)) * x0b) ? 0 :
+                opj_uint_ceildivpow2(tcx1 - (1U << (nb - 1)) * x0b, nb);
+    }
+    if (tby1) {
+        *tby1 = (nb == 0) ? tcy1 :
+                (tcy1 <= (1U << (nb - 1)) * y0b) ? 0 :
+                opj_uint_ceildivpow2(tcy1 - (1U << (nb - 1)) * y0b, nb);
+    }
+}
+
+static void opj_dwt_segment_grow(OPJ_UINT32 filter_width,
+                                 OPJ_UINT32 max_size,
+                                 OPJ_UINT32* start,
+                                 OPJ_UINT32* end)
+{
+    *start = opj_uint_subs(*start, filter_width);
+    *end = opj_uint_adds(*end, filter_width);
+    *end = opj_uint_min(*end, max_size);
+}
+
+
+static opj_sparse_array_int32_t* opj_dwt_init_sparse_array(
+    opj_tcd_tilecomp_t* tilec,
+    OPJ_UINT32 numres)
+{
+    opj_tcd_resolution_t* tr_max = &(tilec->resolutions[numres - 1]);
+    OPJ_UINT32 w = (OPJ_UINT32)(tr_max->x1 - tr_max->x0);
+    OPJ_UINT32 h = (OPJ_UINT32)(tr_max->y1 - tr_max->y0);
+    OPJ_UINT32 resno, bandno, precno, cblkno;
+    opj_sparse_array_int32_t* sa = opj_sparse_array_int32_create(
+                                       w, h, opj_uint_min(w, 64), opj_uint_min(h, 64));
+    if (sa == NULL) {
+        return NULL;
+    }
+
+    for (resno = 0; resno < numres; ++resno) {
+        opj_tcd_resolution_t* res = &tilec->resolutions[resno];
+
+        for (bandno = 0; bandno < res->numbands; ++bandno) {
+            opj_tcd_band_t* band = &res->bands[bandno];
+
+            for (precno = 0; precno < res->pw * res->ph; ++precno) {
+                opj_tcd_precinct_t* precinct = &band->precincts[precno];
+                for (cblkno = 0; cblkno < precinct->cw * precinct->ch; ++cblkno) {
+                    opj_tcd_cblk_dec_t* cblk = &precinct->cblks.dec[cblkno];
+                    if (cblk->decoded_data != NULL) {
+                        OPJ_UINT32 x = (OPJ_UINT32)(cblk->x0 - band->x0);
+                        OPJ_UINT32 y = (OPJ_UINT32)(cblk->y0 - band->y0);
+                        OPJ_UINT32 cblk_w = (OPJ_UINT32)(cblk->x1 - cblk->x0);
+                        OPJ_UINT32 cblk_h = (OPJ_UINT32)(cblk->y1 - cblk->y0);
+
+                        if (band->bandno & 1) {
+                            opj_tcd_resolution_t* pres = &tilec->resolutions[resno - 1];
+                            x += (OPJ_UINT32)(pres->x1 - pres->x0);
+                        }
+                        if (band->bandno & 2) {
+                            opj_tcd_resolution_t* pres = &tilec->resolutions[resno - 1];
+                            y += (OPJ_UINT32)(pres->y1 - pres->y0);
+                        }
+
+                        if (!opj_sparse_array_int32_write(sa, x, y,
+                                                          x + cblk_w, y + cblk_h,
+                                                          cblk->decoded_data,
+                                                          1, cblk_w, OPJ_TRUE)) {
+                            opj_sparse_array_int32_free(sa);
+                            return NULL;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    return sa;
+}
+
+
+static OPJ_BOOL opj_dwt_decode_partial_tile(
+    opj_tcd_tilecomp_t* tilec,
+    OPJ_UINT32 numres)
+{
+    opj_sparse_array_int32_t* sa;
+    opj_dwt_t h;
+    opj_dwt_t v;
+    OPJ_UINT32 resno;
+    /* This value matches the maximum left/right extension given in tables */
+    /* F.2 and F.3 of the standard. */
+    const OPJ_UINT32 filter_width = 2U;
+
+    opj_tcd_resolution_t* tr = tilec->resolutions;
+    opj_tcd_resolution_t* tr_max = &(tilec->resolutions[numres - 1]);
+
+    OPJ_UINT32 rw = (OPJ_UINT32)(tr->x1 -
+                                 tr->x0);  /* width of the resolution level computed */
+    OPJ_UINT32 rh = (OPJ_UINT32)(tr->y1 -
+                                 tr->y0);  /* height of the resolution level computed */
+
+    OPJ_SIZE_T h_mem_size;
+
+    /* Compute the intersection of the area of interest, expressed in tile coordinates */
+    /* with the tile coordinates */
+    OPJ_UINT32 win_tcx0 = tilec->win_x0;
+    OPJ_UINT32 win_tcy0 = tilec->win_y0;
+    OPJ_UINT32 win_tcx1 = tilec->win_x1;
+    OPJ_UINT32 win_tcy1 = tilec->win_y1;
+
+    if (tr_max->x0 == tr_max->x1 || tr_max->y0 == tr_max->y1) {
+        return OPJ_TRUE;
+    }
+
+    sa = opj_dwt_init_sparse_array(tilec, numres);
+    if (sa == NULL) {
+        return OPJ_FALSE;
+    }
+
+    if (numres == 1U) {
+        OPJ_BOOL ret = opj_sparse_array_int32_read(sa,
+                       tr_max->win_x0 - (OPJ_UINT32)tr_max->x0,
+                       tr_max->win_y0 - (OPJ_UINT32)tr_max->y0,
+                       tr_max->win_x1 - (OPJ_UINT32)tr_max->x0,
+                       tr_max->win_y1 - (OPJ_UINT32)tr_max->y0,
+                       tilec->data_win,
+                       1, tr_max->win_x1 - tr_max->win_x0,
+                       OPJ_TRUE);
+        assert(ret);
+        OPJ_UNUSED(ret);
+        opj_sparse_array_int32_free(sa);
+        return OPJ_TRUE;
+    }
+    h.mem_count = opj_dwt_max_resolution(tr, numres);
+    /* overflow check */
+    /* in vertical pass, we process 4 columns at a time */
+    if (h.mem_count > (SIZE_MAX / (4 * sizeof(OPJ_INT32)))) {
+        /* FIXME event manager error callback */
+        opj_sparse_array_int32_free(sa);
+        return OPJ_FALSE;
+    }
+
+    h_mem_size = h.mem_count * 4 * sizeof(OPJ_INT32);
+    h.mem = (OPJ_INT32*)opj_aligned_32_malloc(h_mem_size);
+    if (! h.mem) {
+        /* FIXME event manager error callback */
+        opj_sparse_array_int32_free(sa);
+        return OPJ_FALSE;
+    }
+
+    v.mem_count = h.mem_count;
+    v.mem = h.mem;
+
+    for (resno = 1; resno < numres; resno ++) {
+        OPJ_UINT32 i, j;
+        /* Window of interest subband-based coordinates */
+        OPJ_UINT32 win_ll_x0, win_ll_y0, win_ll_x1, win_ll_y1;
+        OPJ_UINT32 win_hl_x0, win_hl_x1;
+        OPJ_UINT32 win_lh_y0, win_lh_y1;
+        /* Window of interest tile-resolution-based coordinates */
+        OPJ_UINT32 win_tr_x0, win_tr_x1, win_tr_y0, win_tr_y1;
+        /* Tile-resolution subband-based coordinates */
+        OPJ_UINT32 tr_ll_x0, tr_ll_y0, tr_hl_x0, tr_lh_y0;
+
+        ++tr;
+
+        h.sn = (OPJ_INT32)rw;
+        v.sn = (OPJ_INT32)rh;
+
+        rw = (OPJ_UINT32)(tr->x1 - tr->x0);
+        rh = (OPJ_UINT32)(tr->y1 - tr->y0);
+
+        h.dn = (OPJ_INT32)(rw - (OPJ_UINT32)h.sn);
+        h.cas = tr->x0 % 2;
+
+        v.dn = (OPJ_INT32)(rh - (OPJ_UINT32)v.sn);
+        v.cas = tr->y0 % 2;
+
+        /* Get the subband coordinates for the window of interest */
+        /* LL band */
+        opj_dwt_get_band_coordinates(tilec, resno, 0,
+                                     win_tcx0, win_tcy0, win_tcx1, win_tcy1,
+                                     &win_ll_x0, &win_ll_y0,
+                                     &win_ll_x1, &win_ll_y1);
+
+        /* HL band */
+        opj_dwt_get_band_coordinates(tilec, resno, 1,
+                                     win_tcx0, win_tcy0, win_tcx1, win_tcy1,
+                                     &win_hl_x0, NULL, &win_hl_x1, NULL);
+
+        /* LH band */
+        opj_dwt_get_band_coordinates(tilec, resno, 2,
+                                     win_tcx0, win_tcy0, win_tcx1, win_tcy1,
+                                     NULL, &win_lh_y0, NULL, &win_lh_y1);
+
+        /* Beware: band index for non-LL0 resolution are 0=HL, 1=LH and 2=HH */
+        tr_ll_x0 = (OPJ_UINT32)tr->bands[1].x0;
+        tr_ll_y0 = (OPJ_UINT32)tr->bands[0].y0;
+        tr_hl_x0 = (OPJ_UINT32)tr->bands[0].x0;
+        tr_lh_y0 = (OPJ_UINT32)tr->bands[1].y0;
+
+        /* Subtract the origin of the bands for this tile, to the subwindow */
+        /* of interest band coordinates, so as to get them relative to the */
+        /* tile */
+        win_ll_x0 = opj_uint_subs(win_ll_x0, tr_ll_x0);
+        win_ll_y0 = opj_uint_subs(win_ll_y0, tr_ll_y0);
+        win_ll_x1 = opj_uint_subs(win_ll_x1, tr_ll_x0);
+        win_ll_y1 = opj_uint_subs(win_ll_y1, tr_ll_y0);
+        win_hl_x0 = opj_uint_subs(win_hl_x0, tr_hl_x0);
+        win_hl_x1 = opj_uint_subs(win_hl_x1, tr_hl_x0);
+        win_lh_y0 = opj_uint_subs(win_lh_y0, tr_lh_y0);
+        win_lh_y1 = opj_uint_subs(win_lh_y1, tr_lh_y0);
+
+        opj_dwt_segment_grow(filter_width, (OPJ_UINT32)h.sn, &win_ll_x0, &win_ll_x1);
+        opj_dwt_segment_grow(filter_width, (OPJ_UINT32)h.dn, &win_hl_x0, &win_hl_x1);
+
+        opj_dwt_segment_grow(filter_width, (OPJ_UINT32)v.sn, &win_ll_y0, &win_ll_y1);
+        opj_dwt_segment_grow(filter_width, (OPJ_UINT32)v.dn, &win_lh_y0, &win_lh_y1);
+
+        /* Compute the tile-resolution-based coordinates for the window of interest */
+        if (h.cas == 0) {
+            win_tr_x0 = opj_uint_min(2 * win_ll_x0, 2 * win_hl_x0 + 1);
+            win_tr_x1 = opj_uint_min(opj_uint_max(2 * win_ll_x1, 2 * win_hl_x1 + 1), rw);
+        } else {
+            win_tr_x0 = opj_uint_min(2 * win_hl_x0, 2 * win_ll_x0 + 1);
+            win_tr_x1 = opj_uint_min(opj_uint_max(2 * win_hl_x1, 2 * win_ll_x1 + 1), rw);
+        }
+
+        if (v.cas == 0) {
+            win_tr_y0 = opj_uint_min(2 * win_ll_y0, 2 * win_lh_y0 + 1);
+            win_tr_y1 = opj_uint_min(opj_uint_max(2 * win_ll_y1, 2 * win_lh_y1 + 1), rh);
+        } else {
+            win_tr_y0 = opj_uint_min(2 * win_lh_y0, 2 * win_ll_y0 + 1);
+            win_tr_y1 = opj_uint_min(opj_uint_max(2 * win_lh_y1, 2 * win_ll_y1 + 1), rh);
+        }
+
+        for (j = 0; j < rh; ++j) {
+            if ((j >= win_ll_y0 && j < win_ll_y1) ||
+                    (j >= win_lh_y0 + (OPJ_UINT32)v.sn && j < win_lh_y1 + (OPJ_UINT32)v.sn)) {
+
+                /* Avoids dwt.c:1584:44 (in opj_dwt_decode_partial_1): runtime error: */
+                /* signed integer overflow: -1094795586 + -1094795586 cannot be represented in type 'int' */
+                /* on opj_decompress -i  ../../openjpeg/MAPA.jp2 -o out.tif -d 0,0,256,256 */
+                /* This is less extreme than memsetting the whole buffer to 0 */
+                /* although we could potentially do better with better handling of edge conditions */
+                if (win_tr_x1 >= 1 && win_tr_x1 < rw) {
+                    h.mem[win_tr_x1 - 1] = 0;
+                }
+                if (win_tr_x1 < rw) {
+                    h.mem[win_tr_x1] = 0;
+                }
+
+                opj_dwt_interleave_partial_h(h.mem,
+                                             h.cas,
+                                             sa,
+                                             j,
+                                             (OPJ_UINT32)h.sn,
+                                             win_ll_x0,
+                                             win_ll_x1,
+                                             win_hl_x0,
+                                             win_hl_x1);
+                opj_dwt_decode_partial_1(h.mem, h.mem_count, h.dn, h.sn, h.cas,
+                                         (OPJ_INT32)win_ll_x0,
+                                         (OPJ_INT32)win_ll_x1,
+                                         (OPJ_INT32)win_hl_x0,
+                                         (OPJ_INT32)win_hl_x1);
+                if (!opj_sparse_array_int32_write(sa,
+                                                  win_tr_x0, j,
+                                                  win_tr_x1, j + 1,
+                                                  h.mem + win_tr_x0,
+                                                  1, 0, OPJ_TRUE)) {
+                    /* FIXME event manager error callback */
+                    opj_sparse_array_int32_free(sa);
+                    opj_aligned_free(h.mem);
+                    return OPJ_FALSE;
+                }
+            }
+        }
+
+        for (i = win_tr_x0; i < win_tr_x1;) {
+            OPJ_UINT32 nb_cols = opj_uint_min(4U, win_tr_x1 - i);
+            opj_dwt_interleave_partial_v(v.mem,
+                                         v.cas,
+                                         sa,
+                                         i,
+                                         nb_cols,
+                                         (OPJ_UINT32)v.sn,
+                                         win_ll_y0,
+                                         win_ll_y1,
+                                         win_lh_y0,
+                                         win_lh_y1);
+            opj_dwt_decode_partial_1_parallel(v.mem, nb_cols, v.dn, v.sn, v.cas,
+                                              (OPJ_INT32)win_ll_y0,
+                                              (OPJ_INT32)win_ll_y1,
+                                              (OPJ_INT32)win_lh_y0,
+                                              (OPJ_INT32)win_lh_y1);
+            if (!opj_sparse_array_int32_write(sa,
+                                              i, win_tr_y0,
+                                              i + nb_cols, win_tr_y1,
+                                              v.mem + 4 * win_tr_y0,
+                                              1, 4, OPJ_TRUE)) {
+                /* FIXME event manager error callback */
+                opj_sparse_array_int32_free(sa);
+                opj_aligned_free(h.mem);
+                return OPJ_FALSE;
+            }
+
+            i += nb_cols;
+        }
+    }
+    opj_aligned_free(h.mem);
+
+    {
+        OPJ_BOOL ret = opj_sparse_array_int32_read(sa,
+                       tr_max->win_x0 - (OPJ_UINT32)tr_max->x0,
+                       tr_max->win_y0 - (OPJ_UINT32)tr_max->y0,
+                       tr_max->win_x1 - (OPJ_UINT32)tr_max->x0,
+                       tr_max->win_y1 - (OPJ_UINT32)tr_max->y0,
+                       tilec->data_win,
+                       1, tr_max->win_x1 - tr_max->win_x0,
+                       OPJ_TRUE);
+        assert(ret);
+        OPJ_UNUSED(ret);
+    }
+    opj_sparse_array_int32_free(sa);
+    return OPJ_TRUE;
+}
+
+static void opj_v8dwt_interleave_h(opj_v8dwt_t* OPJ_RESTRICT dwt,
+                                   OPJ_FLOAT32* OPJ_RESTRICT a,
+                                   OPJ_UINT32 width,
+                                   OPJ_UINT32 remaining_height)
+{
+    OPJ_FLOAT32* OPJ_RESTRICT bi = (OPJ_FLOAT32*)(dwt->wavelet + dwt->cas);
+    OPJ_UINT32 i, k;
+    OPJ_UINT32 x0 = dwt->win_l_x0;
+    OPJ_UINT32 x1 = dwt->win_l_x1;
+
+    for (k = 0; k < 2; ++k) {
+        if (remaining_height >= NB_ELTS_V8 && ((OPJ_SIZE_T) a & 0x0f) == 0 &&
+                ((OPJ_SIZE_T) bi & 0x0f) == 0) {
+            /* Fast code path */
+            for (i = x0; i < x1; ++i) {
+                OPJ_UINT32 j = i;
+                OPJ_FLOAT32* OPJ_RESTRICT dst = bi + i * 2 * NB_ELTS_V8;
+                dst[0] = a[j];
+                j += width;
+                dst[1] = a[j];
+                j += width;
+                dst[2] = a[j];
+                j += width;
+                dst[3] = a[j];
+                j += width;
+                dst[4] = a[j];
+                j += width;
+                dst[5] = a[j];
+                j += width;
+                dst[6] = a[j];
+                j += width;
+                dst[7] = a[j];
+            }
+        } else {
+            /* Slow code path */
+            for (i = x0; i < x1; ++i) {
+                OPJ_UINT32 j = i;
+                OPJ_FLOAT32* OPJ_RESTRICT dst = bi + i * 2 * NB_ELTS_V8;
+                dst[0] = a[j];
+                j += width;
+                if (remaining_height == 1) {
+                    continue;
+                }
+                dst[1] = a[j];
+                j += width;
+                if (remaining_height == 2) {
+                    continue;
+                }
+                dst[2] = a[j];
+                j += width;
+                if (remaining_height == 3) {
+                    continue;
+                }
+                dst[3] = a[j];
+                j += width;
+                if (remaining_height == 4) {
+                    continue;
+                }
+                dst[4] = a[j];
+                j += width;
+                if (remaining_height == 5) {
+                    continue;
+                }
+                dst[5] = a[j];
+                j += width;
+                if (remaining_height == 6) {
+                    continue;
+                }
+                dst[6] = a[j];
+                j += width;
+                if (remaining_height == 7) {
+                    continue;
+                }
+                dst[7] = a[j];
+            }
+        }
+
+        bi = (OPJ_FLOAT32*)(dwt->wavelet + 1 - dwt->cas);
+        a += dwt->sn;
+        x0 = dwt->win_h_x0;
+        x1 = dwt->win_h_x1;
+    }
+}
+
+static void opj_v8dwt_interleave_partial_h(opj_v8dwt_t* dwt,
+        opj_sparse_array_int32_t* sa,
+        OPJ_UINT32 sa_line,
+        OPJ_UINT32 remaining_height)
+{
+    OPJ_UINT32 i;
+    for (i = 0; i < remaining_height; i++) {
+        OPJ_BOOL ret;
+        ret = opj_sparse_array_int32_read(sa,
+                                          dwt->win_l_x0, sa_line + i,
+                                          dwt->win_l_x1, sa_line + i + 1,
+                                          /* Nasty cast from float* to int32* */
+                                          (OPJ_INT32*)(dwt->wavelet + dwt->cas + 2 * dwt->win_l_x0) + i,
+                                          2 * NB_ELTS_V8, 0, OPJ_TRUE);
+        assert(ret);
+        ret = opj_sparse_array_int32_read(sa,
+                                          (OPJ_UINT32)dwt->sn + dwt->win_h_x0, sa_line + i,
+                                          (OPJ_UINT32)dwt->sn + dwt->win_h_x1, sa_line + i + 1,
+                                          /* Nasty cast from float* to int32* */
+                                          (OPJ_INT32*)(dwt->wavelet + 1 - dwt->cas + 2 * dwt->win_h_x0) + i,
+                                          2 * NB_ELTS_V8, 0, OPJ_TRUE);
+        assert(ret);
+        OPJ_UNUSED(ret);
+    }
+}
+
+static INLINE void opj_v8dwt_interleave_v(opj_v8dwt_t* OPJ_RESTRICT dwt,
+        OPJ_FLOAT32* OPJ_RESTRICT a,
+        OPJ_UINT32 width,
+        OPJ_UINT32 nb_elts_read)
+{
+    opj_v8_t* OPJ_RESTRICT bi = dwt->wavelet + dwt->cas;
+    OPJ_UINT32 i;
+
+    for (i = dwt->win_l_x0; i < dwt->win_l_x1; ++i) {
+        memcpy(&bi[i * 2], &a[i * (OPJ_SIZE_T)width],
+               (OPJ_SIZE_T)nb_elts_read * sizeof(OPJ_FLOAT32));
+    }
+
+    a += (OPJ_UINT32)dwt->sn * (OPJ_SIZE_T)width;
+    bi = dwt->wavelet + 1 - dwt->cas;
+
+    for (i = dwt->win_h_x0; i < dwt->win_h_x1; ++i) {
+        memcpy(&bi[i * 2], &a[i * (OPJ_SIZE_T)width],
+               (OPJ_SIZE_T)nb_elts_read * sizeof(OPJ_FLOAT32));
+    }
+}
+
+static void opj_v8dwt_interleave_partial_v(opj_v8dwt_t* OPJ_RESTRICT dwt,
+        opj_sparse_array_int32_t* sa,
+        OPJ_UINT32 sa_col,
+        OPJ_UINT32 nb_elts_read)
+{
+    OPJ_BOOL ret;
+    ret = opj_sparse_array_int32_read(sa,
+                                      sa_col, dwt->win_l_x0,
+                                      sa_col + nb_elts_read, dwt->win_l_x1,
+                                      (OPJ_INT32*)(dwt->wavelet + dwt->cas + 2 * dwt->win_l_x0),
+                                      1, 2 * NB_ELTS_V8, OPJ_TRUE);
+    assert(ret);
+    ret = opj_sparse_array_int32_read(sa,
+                                      sa_col, (OPJ_UINT32)dwt->sn + dwt->win_h_x0,
+                                      sa_col + nb_elts_read, (OPJ_UINT32)dwt->sn + dwt->win_h_x1,
+                                      (OPJ_INT32*)(dwt->wavelet + 1 - dwt->cas + 2 * dwt->win_h_x0),
+                                      1, 2 * NB_ELTS_V8, OPJ_TRUE);
+    assert(ret);
+    OPJ_UNUSED(ret);
+}
+
+#ifdef __SSE__
+
+static void opj_v8dwt_decode_step1_sse(opj_v8_t* w,
+                                       OPJ_UINT32 start,
+                                       OPJ_UINT32 end,
+                                       const __m128 c)
+{
+    __m128* OPJ_RESTRICT vw = (__m128*) w;
+    OPJ_UINT32 i = start;
+    /* To be adapted if NB_ELTS_V8 changes */
+    vw += 4 * start;
+    /* Note: attempt at loop unrolling x2 doesn't help */
+    for (; i < end; ++i, vw += 4) {
+        vw[0] = _mm_mul_ps(vw[0], c);
+        vw[1] = _mm_mul_ps(vw[1], c);
+    }
+}
+
+static void opj_v8dwt_decode_step2_sse(opj_v8_t* l, opj_v8_t* w,
+                                       OPJ_UINT32 start,
+                                       OPJ_UINT32 end,
+                                       OPJ_UINT32 m,
+                                       __m128 c)
+{
+    __m128* OPJ_RESTRICT vl = (__m128*) l;
+    __m128* OPJ_RESTRICT vw = (__m128*) w;
+    /* To be adapted if NB_ELTS_V8 changes */
+    OPJ_UINT32 i;
+    OPJ_UINT32 imax = opj_uint_min(end, m);
+    if (start == 0) {
+        if (imax >= 1) {
+            vw[-2] = _mm_add_ps(vw[-2], _mm_mul_ps(_mm_add_ps(vl[0], vw[0]), c));
+            vw[-1] = _mm_add_ps(vw[-1], _mm_mul_ps(_mm_add_ps(vl[1], vw[1]), c));
+            vw += 4;
+            start = 1;
+        }
+    } else {
+        vw += start * 4;
+    }
+
+    i = start;
+    /* Note: attempt at loop unrolling x2 doesn't help */
+    for (; i < imax; ++i) {
+        vw[-2] = _mm_add_ps(vw[-2], _mm_mul_ps(_mm_add_ps(vw[-4], vw[0]), c));
+        vw[-1] = _mm_add_ps(vw[-1], _mm_mul_ps(_mm_add_ps(vw[-3], vw[1]), c));
+        vw += 4;
+    }
+    if (m < end) {
+        assert(m + 1 == end);
+        c = _mm_add_ps(c, c);
+        vw[-2] = _mm_add_ps(vw[-2], _mm_mul_ps(c, vw[-4]));
+        vw[-1] = _mm_add_ps(vw[-1], _mm_mul_ps(c, vw[-3]));
+    }
+}
+
+#else
+
+static void opj_v8dwt_decode_step1(opj_v8_t* w,
+                                   OPJ_UINT32 start,
+                                   OPJ_UINT32 end,
+                                   const OPJ_FLOAT32 c)
+{
+    OPJ_FLOAT32* OPJ_RESTRICT fw = (OPJ_FLOAT32*) w;
+    OPJ_UINT32 i;
+    /* To be adapted if NB_ELTS_V8 changes */
+    for (i = start; i < end; ++i) {
+        fw[i * 2 * 8    ] = fw[i * 2 * 8    ] * c;
+        fw[i * 2 * 8 + 1] = fw[i * 2 * 8 + 1] * c;
+        fw[i * 2 * 8 + 2] = fw[i * 2 * 8 + 2] * c;
+        fw[i * 2 * 8 + 3] = fw[i * 2 * 8 + 3] * c;
+        fw[i * 2 * 8 + 4] = fw[i * 2 * 8 + 4] * c;
+        fw[i * 2 * 8 + 5] = fw[i * 2 * 8 + 5] * c;
+        fw[i * 2 * 8 + 6] = fw[i * 2 * 8 + 6] * c;
+        fw[i * 2 * 8 + 7] = fw[i * 2 * 8 + 7] * c;
+    }
+}
+
+static void opj_v8dwt_decode_step2(opj_v8_t* l, opj_v8_t* w,
+                                   OPJ_UINT32 start,
+                                   OPJ_UINT32 end,
+                                   OPJ_UINT32 m,
+                                   OPJ_FLOAT32 c)
+{
+    OPJ_FLOAT32* fl = (OPJ_FLOAT32*) l;
+    OPJ_FLOAT32* fw = (OPJ_FLOAT32*) w;
+    OPJ_UINT32 i;
+    OPJ_UINT32 imax = opj_uint_min(end, m);
+    if (start > 0) {
+        fw += 2 * NB_ELTS_V8 * start;
+        fl = fw - 2 * NB_ELTS_V8;
+    }
+    /* To be adapted if NB_ELTS_V8 changes */
+    for (i = start; i < imax; ++i) {
+        fw[-8] = fw[-8] + ((fl[0] + fw[0]) * c);
+        fw[-7] = fw[-7] + ((fl[1] + fw[1]) * c);
+        fw[-6] = fw[-6] + ((fl[2] + fw[2]) * c);
+        fw[-5] = fw[-5] + ((fl[3] + fw[3]) * c);
+        fw[-4] = fw[-4] + ((fl[4] + fw[4]) * c);
+        fw[-3] = fw[-3] + ((fl[5] + fw[5]) * c);
+        fw[-2] = fw[-2] + ((fl[6] + fw[6]) * c);
+        fw[-1] = fw[-1] + ((fl[7] + fw[7]) * c);
+        fl = fw;
+        fw += 2 * NB_ELTS_V8;
+    }
+    if (m < end) {
+        assert(m + 1 == end);
+        c += c;
+        fw[-8] = fw[-8] + fl[0] * c;
+        fw[-7] = fw[-7] + fl[1] * c;
+        fw[-6] = fw[-6] + fl[2] * c;
+        fw[-5] = fw[-5] + fl[3] * c;
+        fw[-4] = fw[-4] + fl[4] * c;
+        fw[-3] = fw[-3] + fl[5] * c;
+        fw[-2] = fw[-2] + fl[6] * c;
+        fw[-1] = fw[-1] + fl[7] * c;
+    }
+}
+
+#endif
+
+/* <summary>                             */
+/* Inverse 9-7 wavelet transform in 1-D. */
+/* </summary>                            */
+static void opj_v8dwt_decode(opj_v8dwt_t* OPJ_RESTRICT dwt)
+{
+    OPJ_INT32 a, b;
+    /* BUG_WEIRD_TWO_INVK (look for this identifier in tcd.c) */
+    /* Historic value for 2 / opj_invK */
+    /* Normally, we should use invK, but if we do so, we have failures in the */
+    /* conformance test, due to MSE and peak errors significantly higher than */
+    /* accepted value */
+    /* Due to using two_invK instead of invK, we have to compensate in tcd.c */
+    /* the computation of the stepsize for the non LL subbands */
+    const float two_invK = 1.625732422f;
+    if (dwt->cas == 0) {
+        if (!((dwt->dn > 0) || (dwt->sn > 1))) {
+            return;
+        }
+        a = 0;
+        b = 1;
+    } else {
+        if (!((dwt->sn > 0) || (dwt->dn > 1))) {
+            return;
+        }
+        a = 1;
+        b = 0;
+    }
+#ifdef __SSE__
+    opj_v8dwt_decode_step1_sse(dwt->wavelet + a, dwt->win_l_x0, dwt->win_l_x1,
+                               _mm_set1_ps(opj_K));
+    opj_v8dwt_decode_step1_sse(dwt->wavelet + b, dwt->win_h_x0, dwt->win_h_x1,
+                               _mm_set1_ps(two_invK));
+    opj_v8dwt_decode_step2_sse(dwt->wavelet + b, dwt->wavelet + a + 1,
+                               dwt->win_l_x0, dwt->win_l_x1,
+                               (OPJ_UINT32)opj_int_min(dwt->sn, dwt->dn - a),
+                               _mm_set1_ps(-opj_dwt_delta));
+    opj_v8dwt_decode_step2_sse(dwt->wavelet + a, dwt->wavelet + b + 1,
+                               dwt->win_h_x0, dwt->win_h_x1,
+                               (OPJ_UINT32)opj_int_min(dwt->dn, dwt->sn - b),
+                               _mm_set1_ps(-opj_dwt_gamma));
+    opj_v8dwt_decode_step2_sse(dwt->wavelet + b, dwt->wavelet + a + 1,
+                               dwt->win_l_x0, dwt->win_l_x1,
+                               (OPJ_UINT32)opj_int_min(dwt->sn, dwt->dn - a),
+                               _mm_set1_ps(-opj_dwt_beta));
+    opj_v8dwt_decode_step2_sse(dwt->wavelet + a, dwt->wavelet + b + 1,
+                               dwt->win_h_x0, dwt->win_h_x1,
+                               (OPJ_UINT32)opj_int_min(dwt->dn, dwt->sn - b),
+                               _mm_set1_ps(-opj_dwt_alpha));
+#else
+    opj_v8dwt_decode_step1(dwt->wavelet + a, dwt->win_l_x0, dwt->win_l_x1,
+                           opj_K);
+    opj_v8dwt_decode_step1(dwt->wavelet + b, dwt->win_h_x0, dwt->win_h_x1,
+                           two_invK);
+    opj_v8dwt_decode_step2(dwt->wavelet + b, dwt->wavelet + a + 1,
+                           dwt->win_l_x0, dwt->win_l_x1,
+                           (OPJ_UINT32)opj_int_min(dwt->sn, dwt->dn - a),
+                           -opj_dwt_delta);
+    opj_v8dwt_decode_step2(dwt->wavelet + a, dwt->wavelet + b + 1,
+                           dwt->win_h_x0, dwt->win_h_x1,
+                           (OPJ_UINT32)opj_int_min(dwt->dn, dwt->sn - b),
+                           -opj_dwt_gamma);
+    opj_v8dwt_decode_step2(dwt->wavelet + b, dwt->wavelet + a + 1,
+                           dwt->win_l_x0, dwt->win_l_x1,
+                           (OPJ_UINT32)opj_int_min(dwt->sn, dwt->dn - a),
+                           -opj_dwt_beta);
+    opj_v8dwt_decode_step2(dwt->wavelet + a, dwt->wavelet + b + 1,
+                           dwt->win_h_x0, dwt->win_h_x1,
+                           (OPJ_UINT32)opj_int_min(dwt->dn, dwt->sn - b),
+                           -opj_dwt_alpha);
+#endif
+}
+
+typedef struct {
+    opj_v8dwt_t h;
+    OPJ_UINT32 rw;
+    OPJ_UINT32 w;
+    OPJ_FLOAT32 * OPJ_RESTRICT aj;
+    OPJ_UINT32 nb_rows;
+} opj_dwt97_decode_h_job_t;
+
+static void opj_dwt97_decode_h_func(void* user_data, opj_tls_t* tls)
+{
+    OPJ_UINT32 j;
+    opj_dwt97_decode_h_job_t* job;
+    OPJ_FLOAT32 * OPJ_RESTRICT aj;
+    OPJ_UINT32 w;
+    (void)tls;
+
+    job = (opj_dwt97_decode_h_job_t*)user_data;
+    w = job->w;
+
+    assert((job->nb_rows % NB_ELTS_V8) == 0);
+
+    aj = job->aj;
+    for (j = 0; j + NB_ELTS_V8 <= job->nb_rows; j += NB_ELTS_V8) {
+        OPJ_UINT32 k;
+        opj_v8dwt_interleave_h(&job->h, aj, job->w, NB_ELTS_V8);
+        opj_v8dwt_decode(&job->h);
+
+        /* To be adapted if NB_ELTS_V8 changes */
+        for (k = 0; k < job->rw; k++) {
+            aj[k      ] = job->h.wavelet[k].f[0];
+            aj[k + (OPJ_SIZE_T)w  ] = job->h.wavelet[k].f[1];
+            aj[k + (OPJ_SIZE_T)w * 2] = job->h.wavelet[k].f[2];
+            aj[k + (OPJ_SIZE_T)w * 3] = job->h.wavelet[k].f[3];
+        }
+        for (k = 0; k < job->rw; k++) {
+            aj[k + (OPJ_SIZE_T)w * 4] = job->h.wavelet[k].f[4];
+            aj[k + (OPJ_SIZE_T)w * 5] = job->h.wavelet[k].f[5];
+            aj[k + (OPJ_SIZE_T)w * 6] = job->h.wavelet[k].f[6];
+            aj[k + (OPJ_SIZE_T)w * 7] = job->h.wavelet[k].f[7];
+        }
+
+        aj += w * NB_ELTS_V8;
+    }
+
+    opj_aligned_free(job->h.wavelet);
+    opj_free(job);
+}
+
+
+typedef struct {
+    opj_v8dwt_t v;
+    OPJ_UINT32 rh;
+    OPJ_UINT32 w;
+    OPJ_FLOAT32 * OPJ_RESTRICT aj;
+    OPJ_UINT32 nb_columns;
+} opj_dwt97_decode_v_job_t;
+
+static void opj_dwt97_decode_v_func(void* user_data, opj_tls_t* tls)
+{
+    OPJ_UINT32 j;
+    opj_dwt97_decode_v_job_t* job;
+    OPJ_FLOAT32 * OPJ_RESTRICT aj;
+    (void)tls;
+
+    job = (opj_dwt97_decode_v_job_t*)user_data;
+
+    assert((job->nb_columns % NB_ELTS_V8) == 0);
+
+    aj = job->aj;
+    for (j = 0; j + NB_ELTS_V8 <= job->nb_columns; j += NB_ELTS_V8) {
+        OPJ_UINT32 k;
+
+        opj_v8dwt_interleave_v(&job->v, aj, job->w, NB_ELTS_V8);
+        opj_v8dwt_decode(&job->v);
+
+        for (k = 0; k < job->rh; ++k) {
+            memcpy(&aj[k * (OPJ_SIZE_T)job->w], &job->v.wavelet[k],
+                   NB_ELTS_V8 * sizeof(OPJ_FLOAT32));
+        }
+        aj += NB_ELTS_V8;
+    }
+
+    opj_aligned_free(job->v.wavelet);
+    opj_free(job);
+}
+
+
+/* <summary>                             */
+/* Inverse 9-7 wavelet transform in 2-D. */
+/* </summary>                            */
+static
+OPJ_BOOL opj_dwt_decode_tile_97(opj_thread_pool_t* tp,
+                                opj_tcd_tilecomp_t* OPJ_RESTRICT tilec,
+                                OPJ_UINT32 numres)
+{
+    opj_v8dwt_t h;
+    opj_v8dwt_t v;
+
+    opj_tcd_resolution_t* res = tilec->resolutions;
+
+    OPJ_UINT32 rw = (OPJ_UINT32)(res->x1 -
+                                 res->x0);    /* width of the resolution level computed */
+    OPJ_UINT32 rh = (OPJ_UINT32)(res->y1 -
+                                 res->y0);    /* height of the resolution level computed */
+
+    OPJ_UINT32 w = (OPJ_UINT32)(tilec->resolutions[tilec->minimum_num_resolutions -
+                                                               1].x1 -
+                                tilec->resolutions[tilec->minimum_num_resolutions - 1].x0);
+
+    OPJ_SIZE_T l_data_size;
+    const int num_threads = opj_thread_pool_get_thread_count(tp);
+
+    if (numres == 1) {
+        return OPJ_TRUE;
+    }
+
+    l_data_size = opj_dwt_max_resolution(res, numres);
+    /* overflow check */
+    if (l_data_size > (SIZE_MAX / sizeof(opj_v8_t))) {
+        /* FIXME event manager error callback */
+        return OPJ_FALSE;
+    }
+    h.wavelet = (opj_v8_t*) opj_aligned_malloc(l_data_size * sizeof(opj_v8_t));
+    if (!h.wavelet) {
+        /* FIXME event manager error callback */
+        return OPJ_FALSE;
+    }
+    v.wavelet = h.wavelet;
+
+    while (--numres) {
+        OPJ_FLOAT32 * OPJ_RESTRICT aj = (OPJ_FLOAT32*) tilec->data;
+        OPJ_UINT32 j;
+
+        h.sn = (OPJ_INT32)rw;
+        v.sn = (OPJ_INT32)rh;
+
+        ++res;
+
+        rw = (OPJ_UINT32)(res->x1 -
+                          res->x0);   /* width of the resolution level computed */
+        rh = (OPJ_UINT32)(res->y1 -
+                          res->y0);   /* height of the resolution level computed */
+
+        h.dn = (OPJ_INT32)(rw - (OPJ_UINT32)h.sn);
+        h.cas = res->x0 % 2;
+
+        h.win_l_x0 = 0;
+        h.win_l_x1 = (OPJ_UINT32)h.sn;
+        h.win_h_x0 = 0;
+        h.win_h_x1 = (OPJ_UINT32)h.dn;
+
+        if (num_threads <= 1 || rh < 2 * NB_ELTS_V8) {
+            for (j = 0; j + (NB_ELTS_V8 - 1) < rh; j += NB_ELTS_V8) {
+                OPJ_UINT32 k;
+                opj_v8dwt_interleave_h(&h, aj, w, NB_ELTS_V8);
+                opj_v8dwt_decode(&h);
+
+                /* To be adapted if NB_ELTS_V8 changes */
+                for (k = 0; k < rw; k++) {
+                    aj[k      ] = h.wavelet[k].f[0];
+                    aj[k + (OPJ_SIZE_T)w  ] = h.wavelet[k].f[1];
+                    aj[k + (OPJ_SIZE_T)w * 2] = h.wavelet[k].f[2];
+                    aj[k + (OPJ_SIZE_T)w * 3] = h.wavelet[k].f[3];
+                }
+                for (k = 0; k < rw; k++) {
+                    aj[k + (OPJ_SIZE_T)w * 4] = h.wavelet[k].f[4];
+                    aj[k + (OPJ_SIZE_T)w * 5] = h.wavelet[k].f[5];
+                    aj[k + (OPJ_SIZE_T)w * 6] = h.wavelet[k].f[6];
+                    aj[k + (OPJ_SIZE_T)w * 7] = h.wavelet[k].f[7];
+                }
+
+                aj += w * NB_ELTS_V8;
+            }
+        } else {
+            OPJ_UINT32 num_jobs = (OPJ_UINT32)num_threads;
+            OPJ_UINT32 step_j;
+
+            if ((rh / NB_ELTS_V8) < num_jobs) {
+                num_jobs = rh / NB_ELTS_V8;
+            }
+            step_j = ((rh / num_jobs) / NB_ELTS_V8) * NB_ELTS_V8;
+            for (j = 0; j < num_jobs; j++) {
+                opj_dwt97_decode_h_job_t* job;
+
+                job = (opj_dwt97_decode_h_job_t*) opj_malloc(sizeof(opj_dwt97_decode_h_job_t));
+                if (!job) {
+                    opj_thread_pool_wait_completion(tp, 0);
+                    opj_aligned_free(h.wavelet);
+                    return OPJ_FALSE;
+                }
+                job->h.wavelet = (opj_v8_t*)opj_aligned_malloc(l_data_size * sizeof(opj_v8_t));
+                if (!job->h.wavelet) {
+                    opj_thread_pool_wait_completion(tp, 0);
+                    opj_free(job);
+                    opj_aligned_free(h.wavelet);
+                    return OPJ_FALSE;
+                }
+                job->h.dn = h.dn;
+                job->h.sn = h.sn;
+                job->h.cas = h.cas;
+                job->h.win_l_x0 = h.win_l_x0;
+                job->h.win_l_x1 = h.win_l_x1;
+                job->h.win_h_x0 = h.win_h_x0;
+                job->h.win_h_x1 = h.win_h_x1;
+                job->rw = rw;
+                job->w = w;
+                job->aj = aj;
+                job->nb_rows = (j + 1 == num_jobs) ? (rh & (OPJ_UINT32)~
+                                                      (NB_ELTS_V8 - 1)) - j * step_j : step_j;
+                aj += w * job->nb_rows;
+                opj_thread_pool_submit_job(tp, opj_dwt97_decode_h_func, job);
+            }
+            opj_thread_pool_wait_completion(tp, 0);
+            j = rh & (OPJ_UINT32)~(NB_ELTS_V8 - 1);
+        }
+
+        if (j < rh) {
+            OPJ_UINT32 k;
+            opj_v8dwt_interleave_h(&h, aj, w, rh - j);
+            opj_v8dwt_decode(&h);
+            for (k = 0; k < rw; k++) {
+                OPJ_UINT32 l;
+                for (l = 0; l < rh - j; l++) {
+                    aj[k + (OPJ_SIZE_T)w  * l ] = h.wavelet[k].f[l];
+                }
+            }
+        }
+
+        v.dn = (OPJ_INT32)(rh - (OPJ_UINT32)v.sn);
+        v.cas = res->y0 % 2;
+        v.win_l_x0 = 0;
+        v.win_l_x1 = (OPJ_UINT32)v.sn;
+        v.win_h_x0 = 0;
+        v.win_h_x1 = (OPJ_UINT32)v.dn;
+
+        aj = (OPJ_FLOAT32*) tilec->data;
+        if (num_threads <= 1 || rw < 2 * NB_ELTS_V8) {
+            for (j = rw; j > (NB_ELTS_V8 - 1); j -= NB_ELTS_V8) {
+                OPJ_UINT32 k;
+
+                opj_v8dwt_interleave_v(&v, aj, w, NB_ELTS_V8);
+                opj_v8dwt_decode(&v);
+
+                for (k = 0; k < rh; ++k) {
+                    memcpy(&aj[k * (OPJ_SIZE_T)w], &v.wavelet[k], NB_ELTS_V8 * sizeof(OPJ_FLOAT32));
+                }
+                aj += NB_ELTS_V8;
+            }
+        } else {
+            /* "bench_dwt -I" shows that scaling is poor, likely due to RAM
+                transfer being the limiting factor. So limit the number of
+                threads.
+             */
+            OPJ_UINT32 num_jobs = opj_uint_max((OPJ_UINT32)num_threads / 2, 2U);
+            OPJ_UINT32 step_j;
+
+            if ((rw / NB_ELTS_V8) < num_jobs) {
+                num_jobs = rw / NB_ELTS_V8;
+            }
+            step_j = ((rw / num_jobs) / NB_ELTS_V8) * NB_ELTS_V8;
+            for (j = 0; j < num_jobs; j++) {
+                opj_dwt97_decode_v_job_t* job;
+
+                job = (opj_dwt97_decode_v_job_t*) opj_malloc(sizeof(opj_dwt97_decode_v_job_t));
+                if (!job) {
+                    opj_thread_pool_wait_completion(tp, 0);
+                    opj_aligned_free(h.wavelet);
+                    return OPJ_FALSE;
+                }
+                job->v.wavelet = (opj_v8_t*)opj_aligned_malloc(l_data_size * sizeof(opj_v8_t));
+                if (!job->v.wavelet) {
+                    opj_thread_pool_wait_completion(tp, 0);
+                    opj_free(job);
+                    opj_aligned_free(h.wavelet);
+                    return OPJ_FALSE;
+                }
+                job->v.dn = v.dn;
+                job->v.sn = v.sn;
+                job->v.cas = v.cas;
+                job->v.win_l_x0 = v.win_l_x0;
+                job->v.win_l_x1 = v.win_l_x1;
+                job->v.win_h_x0 = v.win_h_x0;
+                job->v.win_h_x1 = v.win_h_x1;
+                job->rh = rh;
+                job->w = w;
+                job->aj = aj;
+                job->nb_columns = (j + 1 == num_jobs) ? (rw & (OPJ_UINT32)~
+                                  (NB_ELTS_V8 - 1)) - j * step_j : step_j;
+                aj += job->nb_columns;
+                opj_thread_pool_submit_job(tp, opj_dwt97_decode_v_func, job);
+            }
+            opj_thread_pool_wait_completion(tp, 0);
+        }
+
+        if (rw & (NB_ELTS_V8 - 1)) {
+            OPJ_UINT32 k;
+
+            j = rw & (NB_ELTS_V8 - 1);
+
+            opj_v8dwt_interleave_v(&v, aj, w, j);
+            opj_v8dwt_decode(&v);
+
+            for (k = 0; k < rh; ++k) {
+                memcpy(&aj[k * (OPJ_SIZE_T)w], &v.wavelet[k],
+                       (OPJ_SIZE_T)j * sizeof(OPJ_FLOAT32));
+            }
+        }
+    }
+
+    opj_aligned_free(h.wavelet);
+    return OPJ_TRUE;
+}
+
+static
+OPJ_BOOL opj_dwt_decode_partial_97(opj_tcd_tilecomp_t* OPJ_RESTRICT tilec,
+                                   OPJ_UINT32 numres)
+{
+    opj_sparse_array_int32_t* sa;
+    opj_v8dwt_t h;
+    opj_v8dwt_t v;
+    OPJ_UINT32 resno;
+    /* This value matches the maximum left/right extension given in tables */
+    /* F.2 and F.3 of the standard. Note: in opj_tcd_is_subband_area_of_interest() */
+    /* we currently use 3. */
+    const OPJ_UINT32 filter_width = 4U;
+
+    opj_tcd_resolution_t* tr = tilec->resolutions;
+    opj_tcd_resolution_t* tr_max = &(tilec->resolutions[numres - 1]);
+
+    OPJ_UINT32 rw = (OPJ_UINT32)(tr->x1 -
+                                 tr->x0);    /* width of the resolution level computed */
+    OPJ_UINT32 rh = (OPJ_UINT32)(tr->y1 -
+                                 tr->y0);    /* height of the resolution level computed */
+
+    OPJ_SIZE_T l_data_size;
+
+    /* Compute the intersection of the area of interest, expressed in tile coordinates */
+    /* with the tile coordinates */
+    OPJ_UINT32 win_tcx0 = tilec->win_x0;
+    OPJ_UINT32 win_tcy0 = tilec->win_y0;
+    OPJ_UINT32 win_tcx1 = tilec->win_x1;
+    OPJ_UINT32 win_tcy1 = tilec->win_y1;
+
+    if (tr_max->x0 == tr_max->x1 || tr_max->y0 == tr_max->y1) {
+        return OPJ_TRUE;
+    }
+
+    sa = opj_dwt_init_sparse_array(tilec, numres);
+    if (sa == NULL) {
+        return OPJ_FALSE;
+    }
+
+    if (numres == 1U) {
+        OPJ_BOOL ret = opj_sparse_array_int32_read(sa,
+                       tr_max->win_x0 - (OPJ_UINT32)tr_max->x0,
+                       tr_max->win_y0 - (OPJ_UINT32)tr_max->y0,
+                       tr_max->win_x1 - (OPJ_UINT32)tr_max->x0,
+                       tr_max->win_y1 - (OPJ_UINT32)tr_max->y0,
+                       tilec->data_win,
+                       1, tr_max->win_x1 - tr_max->win_x0,
+                       OPJ_TRUE);
+        assert(ret);
+        OPJ_UNUSED(ret);
+        opj_sparse_array_int32_free(sa);
+        return OPJ_TRUE;
+    }
+
+    l_data_size = opj_dwt_max_resolution(tr, numres);
+    /* overflow check */
+    if (l_data_size > (SIZE_MAX / sizeof(opj_v8_t))) {
+        /* FIXME event manager error callback */
+        opj_sparse_array_int32_free(sa);
+        return OPJ_FALSE;
+    }
+    h.wavelet = (opj_v8_t*) opj_aligned_malloc(l_data_size * sizeof(opj_v8_t));
+    if (!h.wavelet) {
+        /* FIXME event manager error callback */
+        opj_sparse_array_int32_free(sa);
+        return OPJ_FALSE;
+    }
+    v.wavelet = h.wavelet;
+
+    for (resno = 1; resno < numres; resno ++) {
+        OPJ_UINT32 j;
+        /* Window of interest subband-based coordinates */
+        OPJ_UINT32 win_ll_x0, win_ll_y0, win_ll_x1, win_ll_y1;
+        OPJ_UINT32 win_hl_x0, win_hl_x1;
+        OPJ_UINT32 win_lh_y0, win_lh_y1;
+        /* Window of interest tile-resolution-based coordinates */
+        OPJ_UINT32 win_tr_x0, win_tr_x1, win_tr_y0, win_tr_y1;
+        /* Tile-resolution subband-based coordinates */
+        OPJ_UINT32 tr_ll_x0, tr_ll_y0, tr_hl_x0, tr_lh_y0;
+
+        ++tr;
+
+        h.sn = (OPJ_INT32)rw;
+        v.sn = (OPJ_INT32)rh;
+
+        rw = (OPJ_UINT32)(tr->x1 - tr->x0);
+        rh = (OPJ_UINT32)(tr->y1 - tr->y0);
+
+        h.dn = (OPJ_INT32)(rw - (OPJ_UINT32)h.sn);
+        h.cas = tr->x0 % 2;
+
+        v.dn = (OPJ_INT32)(rh - (OPJ_UINT32)v.sn);
+        v.cas = tr->y0 % 2;
+
+        /* Get the subband coordinates for the window of interest */
+        /* LL band */
+        opj_dwt_get_band_coordinates(tilec, resno, 0,
+                                     win_tcx0, win_tcy0, win_tcx1, win_tcy1,
+                                     &win_ll_x0, &win_ll_y0,
+                                     &win_ll_x1, &win_ll_y1);
+
+        /* HL band */
+        opj_dwt_get_band_coordinates(tilec, resno, 1,
+                                     win_tcx0, win_tcy0, win_tcx1, win_tcy1,
+                                     &win_hl_x0, NULL, &win_hl_x1, NULL);
+
+        /* LH band */
+        opj_dwt_get_band_coordinates(tilec, resno, 2,
+                                     win_tcx0, win_tcy0, win_tcx1, win_tcy1,
+                                     NULL, &win_lh_y0, NULL, &win_lh_y1);
+
+        /* Beware: band index for non-LL0 resolution are 0=HL, 1=LH and 2=HH */
+        tr_ll_x0 = (OPJ_UINT32)tr->bands[1].x0;
+        tr_ll_y0 = (OPJ_UINT32)tr->bands[0].y0;
+        tr_hl_x0 = (OPJ_UINT32)tr->bands[0].x0;
+        tr_lh_y0 = (OPJ_UINT32)tr->bands[1].y0;
+
+        /* Subtract the origin of the bands for this tile, to the subwindow */
+        /* of interest band coordinates, so as to get them relative to the */
+        /* tile */
+        win_ll_x0 = opj_uint_subs(win_ll_x0, tr_ll_x0);
+        win_ll_y0 = opj_uint_subs(win_ll_y0, tr_ll_y0);
+        win_ll_x1 = opj_uint_subs(win_ll_x1, tr_ll_x0);
+        win_ll_y1 = opj_uint_subs(win_ll_y1, tr_ll_y0);
+        win_hl_x0 = opj_uint_subs(win_hl_x0, tr_hl_x0);
+        win_hl_x1 = opj_uint_subs(win_hl_x1, tr_hl_x0);
+        win_lh_y0 = opj_uint_subs(win_lh_y0, tr_lh_y0);
+        win_lh_y1 = opj_uint_subs(win_lh_y1, tr_lh_y0);
+
+        opj_dwt_segment_grow(filter_width, (OPJ_UINT32)h.sn, &win_ll_x0, &win_ll_x1);
+        opj_dwt_segment_grow(filter_width, (OPJ_UINT32)h.dn, &win_hl_x0, &win_hl_x1);
+
+        opj_dwt_segment_grow(filter_width, (OPJ_UINT32)v.sn, &win_ll_y0, &win_ll_y1);
+        opj_dwt_segment_grow(filter_width, (OPJ_UINT32)v.dn, &win_lh_y0, &win_lh_y1);
+
+        /* Compute the tile-resolution-based coordinates for the window of interest */
+        if (h.cas == 0) {
+            win_tr_x0 = opj_uint_min(2 * win_ll_x0, 2 * win_hl_x0 + 1);
+            win_tr_x1 = opj_uint_min(opj_uint_max(2 * win_ll_x1, 2 * win_hl_x1 + 1), rw);
+        } else {
+            win_tr_x0 = opj_uint_min(2 * win_hl_x0, 2 * win_ll_x0 + 1);
+            win_tr_x1 = opj_uint_min(opj_uint_max(2 * win_hl_x1, 2 * win_ll_x1 + 1), rw);
+        }
+
+        if (v.cas == 0) {
+            win_tr_y0 = opj_uint_min(2 * win_ll_y0, 2 * win_lh_y0 + 1);
+            win_tr_y1 = opj_uint_min(opj_uint_max(2 * win_ll_y1, 2 * win_lh_y1 + 1), rh);
+        } else {
+            win_tr_y0 = opj_uint_min(2 * win_lh_y0, 2 * win_ll_y0 + 1);
+            win_tr_y1 = opj_uint_min(opj_uint_max(2 * win_lh_y1, 2 * win_ll_y1 + 1), rh);
+        }
+
+        h.win_l_x0 = win_ll_x0;
+        h.win_l_x1 = win_ll_x1;
+        h.win_h_x0 = win_hl_x0;
+        h.win_h_x1 = win_hl_x1;
+        for (j = 0; j + (NB_ELTS_V8 - 1) < rh; j += NB_ELTS_V8) {
+            if ((j + (NB_ELTS_V8 - 1) >= win_ll_y0 && j < win_ll_y1) ||
+                    (j + (NB_ELTS_V8 - 1) >= win_lh_y0 + (OPJ_UINT32)v.sn &&
+                     j < win_lh_y1 + (OPJ_UINT32)v.sn)) {
+                opj_v8dwt_interleave_partial_h(&h, sa, j, opj_uint_min(NB_ELTS_V8, rh - j));
+                opj_v8dwt_decode(&h);
+                if (!opj_sparse_array_int32_write(sa,
+                                                  win_tr_x0, j,
+                                                  win_tr_x1, j + NB_ELTS_V8,
+                                                  (OPJ_INT32*)&h.wavelet[win_tr_x0].f[0],
+                                                  NB_ELTS_V8, 1, OPJ_TRUE)) {
+                    /* FIXME event manager error callback */
+                    opj_sparse_array_int32_free(sa);
+                    opj_aligned_free(h.wavelet);
+                    return OPJ_FALSE;
+                }
+            }
+        }
+
+        if (j < rh &&
+                ((j + (NB_ELTS_V8 - 1) >= win_ll_y0 && j < win_ll_y1) ||
+                 (j + (NB_ELTS_V8 - 1) >= win_lh_y0 + (OPJ_UINT32)v.sn &&
+                  j < win_lh_y1 + (OPJ_UINT32)v.sn))) {
+            opj_v8dwt_interleave_partial_h(&h, sa, j, rh - j);
+            opj_v8dwt_decode(&h);
+            if (!opj_sparse_array_int32_write(sa,
+                                              win_tr_x0, j,
+                                              win_tr_x1, rh,
+                                              (OPJ_INT32*)&h.wavelet[win_tr_x0].f[0],
+                                              NB_ELTS_V8, 1, OPJ_TRUE)) {
+                /* FIXME event manager error callback */
+                opj_sparse_array_int32_free(sa);
+                opj_aligned_free(h.wavelet);
+                return OPJ_FALSE;
+            }
+        }
+
+        v.win_l_x0 = win_ll_y0;
+        v.win_l_x1 = win_ll_y1;
+        v.win_h_x0 = win_lh_y0;
+        v.win_h_x1 = win_lh_y1;
+        for (j = win_tr_x0; j < win_tr_x1; j += NB_ELTS_V8) {
+            OPJ_UINT32 nb_elts = opj_uint_min(NB_ELTS_V8, win_tr_x1 - j);
+
+            opj_v8dwt_interleave_partial_v(&v, sa, j, nb_elts);
+            opj_v8dwt_decode(&v);
+
+            if (!opj_sparse_array_int32_write(sa,
+                                              j, win_tr_y0,
+                                              j + nb_elts, win_tr_y1,
+                                              (OPJ_INT32*)&h.wavelet[win_tr_y0].f[0],
+                                              1, NB_ELTS_V8, OPJ_TRUE)) {
+                /* FIXME event manager error callback */
+                opj_sparse_array_int32_free(sa);
+                opj_aligned_free(h.wavelet);
+                return OPJ_FALSE;
+            }
+        }
+    }
+
+    {
+        OPJ_BOOL ret = opj_sparse_array_int32_read(sa,
+                       tr_max->win_x0 - (OPJ_UINT32)tr_max->x0,
+                       tr_max->win_y0 - (OPJ_UINT32)tr_max->y0,
+                       tr_max->win_x1 - (OPJ_UINT32)tr_max->x0,
+                       tr_max->win_y1 - (OPJ_UINT32)tr_max->y0,
+                       tilec->data_win,
+                       1, tr_max->win_x1 - tr_max->win_x0,
+                       OPJ_TRUE);
+        assert(ret);
+        OPJ_UNUSED(ret);
+    }
+    opj_sparse_array_int32_free(sa);
+
+    opj_aligned_free(h.wavelet);
+    return OPJ_TRUE;
+}
+
+
+OPJ_BOOL opj_dwt_decode_real(opj_tcd_t *p_tcd,
+                             opj_tcd_tilecomp_t* OPJ_RESTRICT tilec,
+                             OPJ_UINT32 numres)
+{
+    if (p_tcd->whole_tile_decoding) {
+        return opj_dwt_decode_tile_97(p_tcd->thread_pool, tilec, numres);
+    } else {
+        return opj_dwt_decode_partial_97(tilec, numres);
+    }
+}
diff --git a/third_party/libopenjpeg/dwt.h b/third_party/libopenjpeg/dwt.h
new file mode 100644
index 0000000..215061e
--- /dev/null
+++ b/third_party/libopenjpeg/dwt.h
@@ -0,0 +1,120 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef OPJ_DWT_H
+#define OPJ_DWT_H
+/**
+@file dwt.h
+@brief Implementation of a discrete wavelet transform (DWT)
+
+The functions in DWT.C have for goal to realize forward and inverse discret wavelet
+transform with filter 5-3 (reversible) and filter 9-7 (irreversible). The functions in
+DWT.C are used by some function in TCD.C.
+*/
+
+/** @defgroup DWT DWT - Implementation of a discrete wavelet transform */
+/*@{*/
+
+
+/** @name Exported functions */
+/*@{*/
+/* ----------------------------------------------------------------------- */
+/**
+Forward 5-3 wavelet transform in 2-D.
+Apply a reversible DWT transform to a component of an image.
+@param p_tcd TCD handle
+@param tilec Tile component information (current tile)
+*/
+OPJ_BOOL opj_dwt_encode(opj_tcd_t *p_tcd,
+                        opj_tcd_tilecomp_t * tilec);
+
+/**
+Inverse 5-3 wavelet transform in 2-D.
+Apply a reversible inverse DWT transform to a component of an image.
+@param p_tcd TCD handle
+@param tilec Tile component information (current tile)
+@param numres Number of resolution levels to decode
+*/
+OPJ_BOOL opj_dwt_decode(opj_tcd_t *p_tcd,
+                        opj_tcd_tilecomp_t* tilec,
+                        OPJ_UINT32 numres);
+
+/**
+Get the norm of a wavelet function of a subband at a specified level for the reversible 5-3 DWT.
+@param level Level of the wavelet function
+@param orient Band of the wavelet function
+@return Returns the norm of the wavelet function
+*/
+OPJ_FLOAT64 opj_dwt_getnorm(OPJ_UINT32 level, OPJ_UINT32 orient);
+/**
+Forward 9-7 wavelet transform in 2-D.
+Apply an irreversible DWT transform to a component of an image.
+@param p_tcd TCD handle
+@param tilec Tile component information (current tile)
+*/
+OPJ_BOOL opj_dwt_encode_real(opj_tcd_t *p_tcd,
+                             opj_tcd_tilecomp_t * tilec);
+/**
+Inverse 9-7 wavelet transform in 2-D.
+Apply an irreversible inverse DWT transform to a component of an image.
+@param p_tcd TCD handle
+@param tilec Tile component information (current tile)
+@param numres Number of resolution levels to decode
+*/
+OPJ_BOOL opj_dwt_decode_real(opj_tcd_t *p_tcd,
+                             opj_tcd_tilecomp_t* OPJ_RESTRICT tilec,
+                             OPJ_UINT32 numres);
+
+/**
+Get the norm of a wavelet function of a subband at a specified level for the irreversible 9-7 DWT
+@param level Level of the wavelet function
+@param orient Band of the wavelet function
+@return Returns the norm of the 9-7 wavelet
+*/
+OPJ_FLOAT64 opj_dwt_getnorm_real(OPJ_UINT32 level, OPJ_UINT32 orient);
+/**
+Explicit calculation of the Quantization Stepsizes
+@param tccp Tile-component coding parameters
+@param prec Precint analyzed
+*/
+void opj_dwt_calc_explicit_stepsizes(opj_tccp_t * tccp, OPJ_UINT32 prec);
+/* ----------------------------------------------------------------------- */
+/*@}*/
+
+/*@}*/
+
+#endif /* OPJ_DWT_H */
diff --git a/third_party/libopenjpeg20/event.c b/third_party/libopenjpeg/event.c
similarity index 100%
rename from third_party/libopenjpeg20/event.c
rename to third_party/libopenjpeg/event.c
diff --git a/third_party/libopenjpeg20/event.h b/third_party/libopenjpeg/event.h
similarity index 100%
rename from third_party/libopenjpeg20/event.h
rename to third_party/libopenjpeg/event.h
diff --git a/third_party/libopenjpeg20/function_list.c b/third_party/libopenjpeg/function_list.c
similarity index 100%
rename from third_party/libopenjpeg20/function_list.c
rename to third_party/libopenjpeg/function_list.c
diff --git a/third_party/libopenjpeg20/function_list.h b/third_party/libopenjpeg/function_list.h
similarity index 100%
rename from third_party/libopenjpeg20/function_list.h
rename to third_party/libopenjpeg/function_list.h
diff --git a/third_party/libopenjpeg/ht_dec.c b/third_party/libopenjpeg/ht_dec.c
new file mode 100644
index 0000000..62a6c9e
--- /dev/null
+++ b/third_party/libopenjpeg/ht_dec.c
@@ -0,0 +1,2653 @@
+//***************************************************************************/
+// This software is released under the 2-Clause BSD license, included
+// below.
+//
+// Copyright (c) 2021, Aous Naman
+// Copyright (c) 2021, Kakadu Software Pty Ltd, Australia
+// Copyright (c) 2021, The University of New South Wales, Australia
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//***************************************************************************/
+// This file is part of the OpenJpeg software implementation.
+// File: ht_dec.c
+// Author: Aous Naman
+// Date: 01 September 2021
+//***************************************************************************/
+
+//***************************************************************************/
+/** @file ht_dec.c
+ *  @brief implements HTJ2K block decoder
+ */
+
+#include <assert.h>
+#include <string.h>
+#include "opj_includes.h"
+
+#include "t1_ht_luts.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// compiler detection
+/////////////////////////////////////////////////////////////////////////////
+#ifdef _MSC_VER
+#define OPJ_COMPILER_MSVC
+#elif (defined __GNUC__)
+#define OPJ_COMPILER_GNUC
+#endif
+
+//************************************************************************/
+/** @brief Displays the error message for disabling the decoding of SPP and
+  * MRP passes
+  */
+static OPJ_BOOL only_cleanup_pass_is_decoded = OPJ_FALSE;
+
+//************************************************************************/
+/** @brief Generates population count (i.e., the number of set bits)
+  *
+  *   @param [in]  val is the value for which population count is sought
+  */
+static INLINE
+OPJ_UINT32 population_count(OPJ_UINT32 val)
+{
+#if defined(OPJ_COMPILER_MSVC) && (defined(_M_IX86) || defined(_M_AMD64))
+    return (OPJ_UINT32)__popcnt(val);
+#elif (defined OPJ_COMPILER_GNUC)
+    return (OPJ_UINT32)__builtin_popcount(val);
+#else
+    val -= ((val >> 1) & 0x55555555);
+    val = (((val >> 2) & 0x33333333) + (val & 0x33333333));
+    val = (((val >> 4) + val) & 0x0f0f0f0f);
+    val += (val >> 8);
+    val += (val >> 16);
+    return (OPJ_UINT32)(val & 0x0000003f);
+#endif
+}
+
+//************************************************************************/
+/** @brief Counts the number of leading zeros
+  *
+  *   @param [in]  val is the value for which leading zero count is sought
+  */
+#ifdef OPJ_COMPILER_MSVC
+#pragma intrinsic(_BitScanReverse)
+#endif
+static INLINE
+OPJ_UINT32 count_leading_zeros(OPJ_UINT32 val)
+{
+#ifdef OPJ_COMPILER_MSVC
+    unsigned long result = 0;
+    _BitScanReverse(&result, val);
+    return 31U ^ (OPJ_UINT32)result;
+#elif (defined OPJ_COMPILER_GNUC)
+    return (OPJ_UINT32)__builtin_clz(val);
+#else
+    val |= (val >> 1);
+    val |= (val >> 2);
+    val |= (val >> 4);
+    val |= (val >> 8);
+    val |= (val >> 16);
+    return 32U - population_count(val);
+#endif
+}
+
+//************************************************************************/
+/** @brief Read a little-endian serialized UINT32.
+  *
+  *   @param [in]  dataIn pointer to byte stream to read from
+  */
+static INLINE OPJ_UINT32 read_le_uint32(const void* dataIn)
+{
+#if defined(OPJ_BIG_ENDIAN)
+    const OPJ_UINT8* data = (const OPJ_UINT8*)dataIn;
+    return ((OPJ_UINT32)data[0]) | (OPJ_UINT32)(data[1] << 8) | (OPJ_UINT32)(
+               data[2] << 16) | (((
+                                      OPJ_UINT32)data[3]) <<
+                                 24U);
+#else
+    return *(OPJ_UINT32*)dataIn;
+#endif
+}
+
+//************************************************************************/
+/** @brief MEL state structure for reading and decoding the MEL bitstream
+  *
+  *  A number of events is decoded from the MEL bitstream ahead of time
+  *  and stored in run/num_runs.
+  *  Each run represents the number of zero events before a one event.
+  */
+typedef struct dec_mel {
+    // data decoding machinery
+    OPJ_UINT8* data;  //!<the address of data (or bitstream)
+    OPJ_UINT64 tmp;   //!<temporary buffer for read data
+    int bits;         //!<number of bits stored in tmp
+    int size;         //!<number of bytes in MEL code
+    OPJ_BOOL unstuff; //!<true if the next bit needs to be unstuffed
+    int k;            //!<state of MEL decoder
+
+    // queue of decoded runs
+    int num_runs;    //!<number of decoded runs left in runs (maximum 8)
+    OPJ_UINT64 runs; //!<runs of decoded MEL codewords (7 bits/run)
+} dec_mel_t;
+
+//************************************************************************/
+/** @brief Reads and unstuffs the MEL bitstream
+  *
+  *  This design needs more bytes in the codeblock buffer than the length
+  *  of the cleanup pass by up to 2 bytes.
+  *
+  *  Unstuffing removes the MSB of the byte following a byte whose
+  *  value is 0xFF; this prevents sequences larger than 0xFF7F in value
+  *  from appearing the bitstream.
+  *
+  *  @param [in]  melp is a pointer to dec_mel_t structure
+  */
+static INLINE
+void mel_read(dec_mel_t *melp)
+{
+    OPJ_UINT32 val;
+    int bits;
+    OPJ_UINT32 t;
+    OPJ_BOOL unstuff;
+
+    if (melp->bits > 32) { //there are enough bits in the tmp variable
+        return;    // return without reading new data
+    }
+
+    val = 0xFFFFFFFF;      // feed in 0xFF if buffer is exhausted
+    if (melp->size > 4) {  // if there is more than 4 bytes the MEL segment
+        val = read_le_uint32(melp->data);  // read 32 bits from MEL data
+        melp->data += 4;           // advance pointer
+        melp->size -= 4;           // reduce counter
+    } else if (melp->size > 0) { // 4 or less
+        OPJ_UINT32 m, v;
+        int i = 0;
+        while (melp->size > 1) {
+            OPJ_UINT32 v = *melp->data++; // read one byte at a time
+            OPJ_UINT32 m = ~(0xFFu << i); // mask of location
+            val = (val & m) | (v << i);   // put byte in its correct location
+            --melp->size;
+            i += 8;
+        }
+        // size equal to 1
+        v = *melp->data++;  // the one before the last is different
+        v |= 0xF;                         // MEL and VLC segments can overlap
+        m = ~(0xFFu << i);
+        val = (val & m) | (v << i);
+        --melp->size;
+    }
+
+    // next we unstuff them before adding them to the buffer
+    bits = 32 - melp->unstuff;      // number of bits in val, subtract 1 if
+    // the previously read byte requires
+    // unstuffing
+
+    // data is unstuffed and accumulated in t
+    // bits has the number of bits in t
+    t = val & 0xFF;
+    unstuff = ((val & 0xFF) == 0xFF); // true if the byte needs unstuffing
+    bits -= unstuff; // there is one less bit in t if unstuffing is needed
+    t = t << (8 - unstuff); // move up to make room for the next byte
+
+    //this is a repeat of the above
+    t |= (val >> 8) & 0xFF;
+    unstuff = (((val >> 8) & 0xFF) == 0xFF);
+    bits -= unstuff;
+    t = t << (8 - unstuff);
+
+    t |= (val >> 16) & 0xFF;
+    unstuff = (((val >> 16) & 0xFF) == 0xFF);
+    bits -= unstuff;
+    t = t << (8 - unstuff);
+
+    t |= (val >> 24) & 0xFF;
+    melp->unstuff = (((val >> 24) & 0xFF) == 0xFF);
+
+    // move t to tmp, and push the result all the way up, so we read from
+    // the MSB
+    melp->tmp |= ((OPJ_UINT64)t) << (64 - bits - melp->bits);
+    melp->bits += bits; //increment the number of bits in tmp
+}
+
+//************************************************************************/
+/** @brief Decodes unstuffed MEL segment bits stored in tmp to runs
+  *
+  *  Runs are stored in "runs" and the number of runs in "num_runs".
+  *  Each run represents a number of zero events that may or may not
+  *  terminate in a 1 event.
+  *  Each run is stored in 7 bits.  The LSB is 1 if the run terminates in
+  *  a 1 event, 0 otherwise.  The next 6 bits, for the case terminating
+  *  with 1, contain the number of consecutive 0 zero events * 2; for the
+  *  case terminating with 0, they store (number of consecutive 0 zero
+  *  events - 1) * 2.
+  *  A total of 6 bits (made up of 1 + 5) should have been enough.
+  *
+  *  @param [in]  melp is a pointer to dec_mel_t structure
+  */
+static INLINE
+void mel_decode(dec_mel_t *melp)
+{
+    static const int mel_exp[13] = { //MEL exponents
+        0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 4, 5
+    };
+
+    if (melp->bits < 6) { // if there are less than 6 bits in tmp
+        mel_read(melp);    // then read from the MEL bitstream
+    }
+    // 6 bits is the largest decodable MEL cwd
+
+    //repeat so long that there is enough decodable bits in tmp,
+    // and the runs store is not full (num_runs < 8)
+    while (melp->bits >= 6 && melp->num_runs < 8) {
+        int eval = mel_exp[melp->k]; // number of bits associated with state
+        int run = 0;
+        if (melp->tmp & (1ull << 63)) { //The next bit to decode (stored in MSB)
+            //one is found
+            run = 1 << eval;
+            run--; // consecutive runs of 0 events - 1
+            melp->k = melp->k + 1 < 12 ? melp->k + 1 : 12;//increment, max is 12
+            melp->tmp <<= 1; // consume one bit from tmp
+            melp->bits -= 1;
+            run = run << 1; // a stretch of zeros not terminating in one
+        } else {
+            //0 is found
+            run = (int)(melp->tmp >> (63 - eval)) & ((1 << eval) - 1);
+            melp->k = melp->k - 1 > 0 ? melp->k - 1 : 0; //decrement, min is 0
+            melp->tmp <<= eval + 1; //consume eval + 1 bits (max is 6)
+            melp->bits -= eval + 1;
+            run = (run << 1) + 1; // a stretch of zeros terminating with one
+        }
+        eval = melp->num_runs * 7;                 // 7 bits per run
+        melp->runs &= ~((OPJ_UINT64)0x3F << eval); // 6 bits are sufficient
+        melp->runs |= ((OPJ_UINT64)run) << eval;   // store the value in runs
+        melp->num_runs++;                          // increment count
+    }
+}
+
+//************************************************************************/
+/** @brief Initiates a dec_mel_t structure for MEL decoding and reads
+  *         some bytes in order to get the read address to a multiple
+  *         of 4
+  *
+  *  @param [in]  melp is a pointer to dec_mel_t structure
+  *  @param [in]  bbuf is a pointer to byte buffer
+  *  @param [in]  lcup is the length of MagSgn+MEL+VLC segments
+  *  @param [in]  scup is the length of MEL+VLC segments
+  */
+static INLINE
+OPJ_BOOL mel_init(dec_mel_t *melp, OPJ_UINT8* bbuf, int lcup, int scup)
+{
+    int num;
+    int i;
+
+    melp->data = bbuf + lcup - scup; // move the pointer to the start of MEL
+    melp->bits = 0;                  // 0 bits in tmp
+    melp->tmp = 0;                   //
+    melp->unstuff = OPJ_FALSE;       // no unstuffing
+    melp->size = scup - 1;           // size is the length of MEL+VLC-1
+    melp->k = 0;                     // 0 for state
+    melp->num_runs = 0;              // num_runs is 0
+    melp->runs = 0;                  //
+
+    //This code is borrowed; original is for a different architecture
+    //These few lines take care of the case where data is not at a multiple
+    // of 4 boundary.  It reads 1,2,3 up to 4 bytes from the MEL segment
+    num = 4 - (int)((intptr_t)(melp->data) & 0x3);
+    for (i = 0; i < num; ++i) { // this code is similar to mel_read
+        OPJ_UINT64 d;
+        int d_bits;
+
+        if (melp->unstuff == OPJ_TRUE && melp->data[0] > 0x8F) {
+            return OPJ_FALSE;
+        }
+        d = (melp->size > 0) ? *melp->data : 0xFF; // if buffer is consumed
+        // set data to 0xFF
+        if (melp->size == 1) {
+            d |= 0xF;    //if this is MEL+VLC-1, set LSBs to 0xF
+        }
+        // see the standard
+        melp->data += melp->size-- > 0; //increment if the end is not reached
+        d_bits = 8 - melp->unstuff; //if unstuffing is needed, reduce by 1
+        melp->tmp = (melp->tmp << d_bits) | d; //store bits in tmp
+        melp->bits += d_bits;  //increment tmp by number of bits
+        melp->unstuff = ((d & 0xFF) == 0xFF); //true of next byte needs
+        //unstuffing
+    }
+    melp->tmp <<= (64 - melp->bits); //push all the way up so the first bit
+    // is the MSB
+    return OPJ_TRUE;
+}
+
+//************************************************************************/
+/** @brief Retrieves one run from dec_mel_t; if there are no runs stored
+  *         MEL segment is decoded
+  *
+  * @param [in]  melp is a pointer to dec_mel_t structure
+  */
+static INLINE
+int mel_get_run(dec_mel_t *melp)
+{
+    int t;
+    if (melp->num_runs == 0) { //if no runs, decode more bit from MEL segment
+        mel_decode(melp);
+    }
+
+    t = melp->runs & 0x7F; //retrieve one run
+    melp->runs >>= 7;  // remove the retrieved run
+    melp->num_runs--;
+    return t; // return run
+}
+
+//************************************************************************/
+/** @brief A structure for reading and unstuffing a segment that grows
+  *         backward, such as VLC and MRP
+  */
+typedef struct rev_struct {
+    //storage
+    OPJ_UINT8* data;  //!<pointer to where to read data
+    OPJ_UINT64 tmp;     //!<temporary buffer of read data
+    OPJ_UINT32 bits;  //!<number of bits stored in tmp
+    int size;         //!<number of bytes left
+    OPJ_BOOL unstuff; //!<true if the last byte is more than 0x8F
+    //!<then the current byte is unstuffed if it is 0x7F
+} rev_struct_t;
+
+//************************************************************************/
+/** @brief Read and unstuff data from a backwardly-growing segment
+  *
+  *  This reader can read up to 8 bytes from before the VLC segment.
+  *  Care must be taken not read from unreadable memory, causing a
+  *  segmentation fault.
+  *
+  *  Note that there is another subroutine rev_read_mrp that is slightly
+  *  different.  The other one fills zeros when the buffer is exhausted.
+  *  This one basically does not care if the bytes are consumed, because
+  *  any extra data should not be used in the actual decoding.
+  *
+  *  Unstuffing is needed to prevent sequences more than 0xFF8F from
+  *  appearing in the bits stream; since we are reading backward, we keep
+  *  watch when a value larger than 0x8F appears in the bitstream.
+  *  If the byte following this is 0x7F, we unstuff this byte (ignore the
+  *  MSB of that byte, which should be 0).
+  *
+  *  @param [in]  vlcp is a pointer to rev_struct_t structure
+  */
+static INLINE
+void rev_read(rev_struct_t *vlcp)
+{
+    OPJ_UINT32 val;
+    OPJ_UINT32 tmp;
+    OPJ_UINT32 bits;
+    OPJ_BOOL unstuff;
+
+    //process 4 bytes at a time
+    if (vlcp->bits > 32) { // if there are more than 32 bits in tmp, then
+        return;    // reading 32 bits can overflow vlcp->tmp
+    }
+    val = 0;
+    //the next line (the if statement) needs to be tested first
+    if (vlcp->size > 3) { // if there are more than 3 bytes left in VLC
+        // (vlcp->data - 3) move pointer back to read 32 bits at once
+        val = read_le_uint32(vlcp->data - 3); // then read 32 bits
+        vlcp->data -= 4;                // move data pointer back by 4
+        vlcp->size -= 4;                // reduce available byte by 4
+    } else if (vlcp->size > 0) { // 4 or less
+        int i = 24;
+        while (vlcp->size > 0) {
+            OPJ_UINT32 v = *vlcp->data--; // read one byte at a time
+            val |= (v << i);              // put byte in its correct location
+            --vlcp->size;
+            i -= 8;
+        }
+    }
+
+    //accumulate in tmp, number of bits in tmp are stored in bits
+    tmp = val >> 24;  //start with the MSB byte
+
+    // test unstuff (previous byte is >0x8F), and this byte is 0x7F
+    bits = 8u - ((vlcp->unstuff && (((val >> 24) & 0x7F) == 0x7F)) ? 1u : 0u);
+    unstuff = (val >> 24) > 0x8F; //this is for the next byte
+
+    tmp |= ((val >> 16) & 0xFF) << bits; //process the next byte
+    bits += 8u - ((unstuff && (((val >> 16) & 0x7F) == 0x7F)) ? 1u : 0u);
+    unstuff = ((val >> 16) & 0xFF) > 0x8F;
+
+    tmp |= ((val >> 8) & 0xFF) << bits;
+    bits += 8u - ((unstuff && (((val >> 8) & 0x7F) == 0x7F)) ? 1u : 0u);
+    unstuff = ((val >> 8) & 0xFF) > 0x8F;
+
+    tmp |= (val & 0xFF) << bits;
+    bits += 8u - ((unstuff && ((val & 0x7F) == 0x7F)) ? 1u : 0u);
+    unstuff = (val & 0xFF) > 0x8F;
+
+    // now move the read and unstuffed bits into vlcp->tmp
+    vlcp->tmp |= (OPJ_UINT64)tmp << vlcp->bits;
+    vlcp->bits += bits;
+    vlcp->unstuff = unstuff; // this for the next read
+}
+
+//************************************************************************/
+/** @brief Initiates the rev_struct_t structure and reads a few bytes to
+  *         move the read address to multiple of 4
+  *
+  *  There is another similar rev_init_mrp subroutine.  The difference is
+  *  that this one, rev_init, discards the first 12 bits (they have the
+  *  sum of the lengths of VLC and MEL segments), and first unstuff depends
+  *  on first 4 bits.
+  *
+  *  @param [in]  vlcp is a pointer to rev_struct_t structure
+  *  @param [in]  data is a pointer to byte at the start of the cleanup pass
+  *  @param [in]  lcup is the length of MagSgn+MEL+VLC segments
+  *  @param [in]  scup is the length of MEL+VLC segments
+  */
+static INLINE
+void rev_init(rev_struct_t *vlcp, OPJ_UINT8* data, int lcup, int scup)
+{
+    OPJ_UINT32 d;
+    int num, tnum, i;
+
+    //first byte has only the upper 4 bits
+    vlcp->data = data + lcup - 2;
+
+    //size can not be larger than this, in fact it should be smaller
+    vlcp->size = scup - 2;
+
+    d = *vlcp->data--;            // read one byte (this is a half byte)
+    vlcp->tmp = d >> 4;           // both initialize and set
+    vlcp->bits = 4 - ((vlcp->tmp & 7) == 7); //check standard
+    vlcp->unstuff = (d | 0xF) > 0x8F; //this is useful for the next byte
+
+    //This code is designed for an architecture that read address should
+    // align to the read size (address multiple of 4 if read size is 4)
+    //These few lines take care of the case where data is not at a multiple
+    // of 4 boundary. It reads 1,2,3 up to 4 bytes from the VLC bitstream.
+    // To read 32 bits, read from (vlcp->data - 3)
+    num = 1 + (int)((intptr_t)(vlcp->data) & 0x3);
+    tnum = num < vlcp->size ? num : vlcp->size;
+    for (i = 0; i < tnum; ++i) {
+        OPJ_UINT64 d;
+        OPJ_UINT32 d_bits;
+        d = *vlcp->data--;  // read one byte and move read pointer
+        //check if the last byte was >0x8F (unstuff == true) and this is 0x7F
+        d_bits = 8u - ((vlcp->unstuff && ((d & 0x7F) == 0x7F)) ? 1u : 0u);
+        vlcp->tmp |= d << vlcp->bits; // move data to vlcp->tmp
+        vlcp->bits += d_bits;
+        vlcp->unstuff = d > 0x8F; // for next byte
+    }
+    vlcp->size -= tnum;
+    rev_read(vlcp);  // read another 32 buts
+}
+
+//************************************************************************/
+/** @brief Retrieves 32 bits from the head of a rev_struct structure
+  *
+  *  By the end of this call, vlcp->tmp must have no less than 33 bits
+  *
+  *  @param [in]  vlcp is a pointer to rev_struct structure
+  */
+static INLINE
+OPJ_UINT32 rev_fetch(rev_struct_t *vlcp)
+{
+    if (vlcp->bits < 32) { // if there are less then 32 bits, read more
+        rev_read(vlcp);     // read 32 bits, but unstuffing might reduce this
+        if (vlcp->bits < 32) { // if there is still space in vlcp->tmp for 32 bits
+            rev_read(vlcp);    // read another 32
+        }
+    }
+    return (OPJ_UINT32)vlcp->tmp; // return the head (bottom-most) of vlcp->tmp
+}
+
+//************************************************************************/
+/** @brief Consumes num_bits from a rev_struct structure
+  *
+  *  @param [in]  vlcp is a pointer to rev_struct structure
+  *  @param [in]  num_bits is the number of bits to be removed
+  */
+static INLINE
+OPJ_UINT32 rev_advance(rev_struct_t *vlcp, OPJ_UINT32 num_bits)
+{
+    assert(num_bits <= vlcp->bits); // vlcp->tmp must have more than num_bits
+    vlcp->tmp >>= num_bits;         // remove bits
+    vlcp->bits -= num_bits;         // decrement the number of bits
+    return (OPJ_UINT32)vlcp->tmp;
+}
+
+//************************************************************************/
+/** @brief Reads and unstuffs from rev_struct
+  *
+  *  This is different than rev_read in that this fills in zeros when the
+  *  the available data is consumed.  The other does not care about the
+  *  values when all data is consumed.
+  *
+  *  See rev_read for more information about unstuffing
+  *
+  *  @param [in]  mrp is a pointer to rev_struct structure
+  */
+static INLINE
+void rev_read_mrp(rev_struct_t *mrp)
+{
+    OPJ_UINT32 val;
+    OPJ_UINT32 tmp;
+    OPJ_UINT32 bits;
+    OPJ_BOOL unstuff;
+
+    //process 4 bytes at a time
+    if (mrp->bits > 32) {
+        return;
+    }
+    val = 0;
+    if (mrp->size > 3) { // If there are 3 byte or more
+        // (mrp->data - 3) move pointer back to read 32 bits at once
+        val = read_le_uint32(mrp->data - 3); // read 32 bits
+        mrp->data -= 4;                      // move back pointer
+        mrp->size -= 4;                      // reduce count
+    } else if (mrp->size > 0) {
+        int i = 24;
+        while (mrp->size > 0) {
+            OPJ_UINT32 v = *mrp->data--; // read one byte at a time
+            val |= (v << i);             // put byte in its correct location
+            --mrp->size;
+            i -= 8;
+        }
+    }
+
+
+    //accumulate in tmp, and keep count in bits
+    tmp = val >> 24;
+
+    //test if the last byte > 0x8F (unstuff must be true) and this is 0x7F
+    bits = 8u - ((mrp->unstuff && (((val >> 24) & 0x7F) == 0x7F)) ? 1u : 0u);
+    unstuff = (val >> 24) > 0x8F;
+
+    //process the next byte
+    tmp |= ((val >> 16) & 0xFF) << bits;
+    bits += 8u - ((unstuff && (((val >> 16) & 0x7F) == 0x7F)) ? 1u : 0u);
+    unstuff = ((val >> 16) & 0xFF) > 0x8F;
+
+    tmp |= ((val >> 8) & 0xFF) << bits;
+    bits += 8u - ((unstuff && (((val >> 8) & 0x7F) == 0x7F)) ? 1u : 0u);
+    unstuff = ((val >> 8) & 0xFF) > 0x8F;
+
+    tmp |= (val & 0xFF) << bits;
+    bits += 8u - ((unstuff && ((val & 0x7F) == 0x7F)) ? 1u : 0u);
+    unstuff = (val & 0xFF) > 0x8F;
+
+    mrp->tmp |= (OPJ_UINT64)tmp << mrp->bits; // move data to mrp pointer
+    mrp->bits += bits;
+    mrp->unstuff = unstuff;                   // next byte
+}
+
+//************************************************************************/
+/** @brief Initialized rev_struct structure for MRP segment, and reads
+  *         a number of bytes such that the next 32 bits read are from
+  *         an address that is a multiple of 4. Note this is designed for
+  *         an architecture that read size must be compatible with the
+  *         alignment of the read address
+  *
+  *  There is another similar subroutine rev_init.  This subroutine does
+  *  NOT skip the first 12 bits, and starts with unstuff set to true.
+  *
+  *  @param [in]  mrp is a pointer to rev_struct structure
+  *  @param [in]  data is a pointer to byte at the start of the cleanup pass
+  *  @param [in]  lcup is the length of MagSgn+MEL+VLC segments
+  *  @param [in]  len2 is the length of SPP+MRP segments
+  */
+static INLINE
+void rev_init_mrp(rev_struct_t *mrp, OPJ_UINT8* data, int lcup, int len2)
+{
+    int num, i;
+
+    mrp->data = data + lcup + len2 - 1;
+    mrp->size = len2;
+    mrp->unstuff = OPJ_TRUE;
+    mrp->bits = 0;
+    mrp->tmp = 0;
+
+    //This code is designed for an architecture that read address should
+    // align to the read size (address multiple of 4 if read size is 4)
+    //These few lines take care of the case where data is not at a multiple
+    // of 4 boundary.  It reads 1,2,3 up to 4 bytes from the MRP stream
+    num = 1 + (int)((intptr_t)(mrp->data) & 0x3);
+    for (i = 0; i < num; ++i) {
+        OPJ_UINT64 d;
+        OPJ_UINT32 d_bits;
+
+        //read a byte, 0 if no more data
+        d = (mrp->size-- > 0) ? *mrp->data-- : 0;
+        //check if unstuffing is needed
+        d_bits = 8u - ((mrp->unstuff && ((d & 0x7F) == 0x7F)) ? 1u : 0u);
+        mrp->tmp |= d << mrp->bits; // move data to vlcp->tmp
+        mrp->bits += d_bits;
+        mrp->unstuff = d > 0x8F; // for next byte
+    }
+    rev_read_mrp(mrp);
+}
+
+//************************************************************************/
+/** @brief Retrieves 32 bits from the head of a rev_struct structure
+  *
+  *  By the end of this call, mrp->tmp must have no less than 33 bits
+  *
+  *  @param [in]  mrp is a pointer to rev_struct structure
+  */
+static INLINE
+OPJ_UINT32 rev_fetch_mrp(rev_struct_t *mrp)
+{
+    if (mrp->bits < 32) { // if there are less than 32 bits in mrp->tmp
+        rev_read_mrp(mrp);    // read 30-32 bits from mrp
+        if (mrp->bits < 32) { // if there is a space of 32 bits
+            rev_read_mrp(mrp);    // read more
+        }
+    }
+    return (OPJ_UINT32)mrp->tmp;  // return the head of mrp->tmp
+}
+
+//************************************************************************/
+/** @brief Consumes num_bits from a rev_struct structure
+  *
+  *  @param [in]  mrp is a pointer to rev_struct structure
+  *  @param [in]  num_bits is the number of bits to be removed
+  */
+static INLINE
+OPJ_UINT32 rev_advance_mrp(rev_struct_t *mrp, OPJ_UINT32 num_bits)
+{
+    assert(num_bits <= mrp->bits); // we must not consume more than mrp->bits
+    mrp->tmp >>= num_bits;         // discard the lowest num_bits bits
+    mrp->bits -= num_bits;
+    return (OPJ_UINT32)mrp->tmp;   // return data after consumption
+}
+
+//************************************************************************/
+/** @brief Decode initial UVLC to get the u value (or u_q)
+  *
+  *  @param [in]  vlc is the head of the VLC bitstream
+  *  @param [in]  mode is 0, 1, 2, 3, or 4. Values in 0 to 3 are composed of
+  *               u_off of 1st quad and 2nd quad of a quad pair.  The value
+  *               4 occurs when both bits are 1, and the event decoded
+  *               from MEL bitstream is also 1.
+  *  @param [out] u is the u value (or u_q) + 1.  Note: we produce u + 1;
+  *               this value is a partial calculation of u + kappa.
+  */
+static INLINE
+OPJ_UINT32 decode_init_uvlc(OPJ_UINT32 vlc, OPJ_UINT32 mode, OPJ_UINT32 *u)
+{
+    //table stores possible decoding three bits from vlc
+    // there are 8 entries for xx1, x10, 100, 000, where x means do not care
+    // table value is made up of
+    // 2 bits in the LSB for prefix length
+    // 3 bits for suffix length
+    // 3 bits in the MSB for prefix value (u_pfx in Table 3 of ITU T.814)
+    static const OPJ_UINT8 dec[8] = { // the index is the prefix codeword
+        3 | (5 << 2) | (5 << 5),        //000 == 000, prefix codeword "000"
+        1 | (0 << 2) | (1 << 5),        //001 == xx1, prefix codeword "1"
+        2 | (0 << 2) | (2 << 5),        //010 == x10, prefix codeword "01"
+        1 | (0 << 2) | (1 << 5),        //011 == xx1, prefix codeword "1"
+        3 | (1 << 2) | (3 << 5),        //100 == 100, prefix codeword "001"
+        1 | (0 << 2) | (1 << 5),        //101 == xx1, prefix codeword "1"
+        2 | (0 << 2) | (2 << 5),        //110 == x10, prefix codeword "01"
+        1 | (0 << 2) | (1 << 5)         //111 == xx1, prefix codeword "1"
+    };
+
+    OPJ_UINT32 consumed_bits = 0;
+    if (mode == 0) { // both u_off are 0
+        u[0] = u[1] = 1; //Kappa is 1 for initial line
+    } else if (mode <= 2) { // u_off are either 01 or 10
+        OPJ_UINT32 d;
+        OPJ_UINT32 suffix_len;
+
+        d = dec[vlc & 0x7];   //look at the least significant 3 bits
+        vlc >>= d & 0x3;                 //prefix length
+        consumed_bits += d & 0x3;
+
+        suffix_len = ((d >> 2) & 0x7);
+        consumed_bits += suffix_len;
+
+        d = (d >> 5) + (vlc & ((1U << suffix_len) - 1)); // u value
+        u[0] = (mode == 1) ? d + 1 : 1; // kappa is 1 for initial line
+        u[1] = (mode == 1) ? 1 : d + 1; // kappa is 1 for initial line
+    } else if (mode == 3) { // both u_off are 1, and MEL event is 0
+        OPJ_UINT32 d1 = dec[vlc & 0x7];  // LSBs of VLC are prefix codeword
+        vlc >>= d1 & 0x3;                // Consume bits
+        consumed_bits += d1 & 0x3;
+
+        if ((d1 & 0x3) > 2) {
+            OPJ_UINT32 suffix_len;
+
+            //u_{q_2} prefix
+            u[1] = (vlc & 1) + 1 + 1; //Kappa is 1 for initial line
+            ++consumed_bits;
+            vlc >>= 1;
+
+            suffix_len = ((d1 >> 2) & 0x7);
+            consumed_bits += suffix_len;
+            d1 = (d1 >> 5) + (vlc & ((1U << suffix_len) - 1)); // u value
+            u[0] = d1 + 1; //Kappa is 1 for initial line
+        } else {
+            OPJ_UINT32 d2;
+            OPJ_UINT32 suffix_len;
+
+            d2 = dec[vlc & 0x7];  // LSBs of VLC are prefix codeword
+            vlc >>= d2 & 0x3;                // Consume bits
+            consumed_bits += d2 & 0x3;
+
+            suffix_len = ((d1 >> 2) & 0x7);
+            consumed_bits += suffix_len;
+
+            d1 = (d1 >> 5) + (vlc & ((1U << suffix_len) - 1)); // u value
+            u[0] = d1 + 1; //Kappa is 1 for initial line
+            vlc >>= suffix_len;
+
+            suffix_len = ((d2 >> 2) & 0x7);
+            consumed_bits += suffix_len;
+
+            d2 = (d2 >> 5) + (vlc & ((1U << suffix_len) - 1)); // u value
+            u[1] = d2 + 1; //Kappa is 1 for initial line
+        }
+    } else if (mode == 4) { // both u_off are 1, and MEL event is 1
+        OPJ_UINT32 d1;
+        OPJ_UINT32 d2;
+        OPJ_UINT32 suffix_len;
+
+        d1 = dec[vlc & 0x7];  // LSBs of VLC are prefix codeword
+        vlc >>= d1 & 0x3;                // Consume bits
+        consumed_bits += d1 & 0x3;
+
+        d2 = dec[vlc & 0x7];  // LSBs of VLC are prefix codeword
+        vlc >>= d2 & 0x3;                // Consume bits
+        consumed_bits += d2 & 0x3;
+
+        suffix_len = ((d1 >> 2) & 0x7);
+        consumed_bits += suffix_len;
+
+        d1 = (d1 >> 5) + (vlc & ((1U << suffix_len) - 1)); // u value
+        u[0] = d1 + 3; // add 2+kappa
+        vlc >>= suffix_len;
+
+        suffix_len = ((d2 >> 2) & 0x7);
+        consumed_bits += suffix_len;
+
+        d2 = (d2 >> 5) + (vlc & ((1U << suffix_len) - 1)); // u value
+        u[1] = d2 + 3; // add 2+kappa
+    }
+    return consumed_bits;
+}
+
+//************************************************************************/
+/** @brief Decode non-initial UVLC to get the u value (or u_q)
+  *
+  *  @param [in]  vlc is the head of the VLC bitstream
+  *  @param [in]  mode is 0, 1, 2, or 3. The 1st bit is u_off of 1st quad
+  *               and 2nd for 2nd quad of a quad pair
+  *  @param [out] u is the u value (or u_q) + 1.  Note: we produce u + 1;
+  *               this value is a partial calculation of u + kappa.
+  */
+static INLINE
+OPJ_UINT32 decode_noninit_uvlc(OPJ_UINT32 vlc, OPJ_UINT32 mode, OPJ_UINT32 *u)
+{
+    //table stores possible decoding three bits from vlc
+    // there are 8 entries for xx1, x10, 100, 000, where x means do not care
+    // table value is made up of
+    // 2 bits in the LSB for prefix length
+    // 3 bits for suffix length
+    // 3 bits in the MSB for prefix value (u_pfx in Table 3 of ITU T.814)
+    static const OPJ_UINT8 dec[8] = {
+        3 | (5 << 2) | (5 << 5), //000 == 000, prefix codeword "000"
+        1 | (0 << 2) | (1 << 5), //001 == xx1, prefix codeword "1"
+        2 | (0 << 2) | (2 << 5), //010 == x10, prefix codeword "01"
+        1 | (0 << 2) | (1 << 5), //011 == xx1, prefix codeword "1"
+        3 | (1 << 2) | (3 << 5), //100 == 100, prefix codeword "001"
+        1 | (0 << 2) | (1 << 5), //101 == xx1, prefix codeword "1"
+        2 | (0 << 2) | (2 << 5), //110 == x10, prefix codeword "01"
+        1 | (0 << 2) | (1 << 5)  //111 == xx1, prefix codeword "1"
+    };
+
+    OPJ_UINT32 consumed_bits = 0;
+    if (mode == 0) {
+        u[0] = u[1] = 1; //for kappa
+    } else if (mode <= 2) { //u_off are either 01 or 10
+        OPJ_UINT32 d;
+        OPJ_UINT32 suffix_len;
+
+        d = dec[vlc & 0x7];  //look at the least significant 3 bits
+        vlc >>= d & 0x3;                //prefix length
+        consumed_bits += d & 0x3;
+
+        suffix_len = ((d >> 2) & 0x7);
+        consumed_bits += suffix_len;
+
+        d = (d >> 5) + (vlc & ((1U << suffix_len) - 1)); // u value
+        u[0] = (mode == 1) ? d + 1 : 1; //for kappa
+        u[1] = (mode == 1) ? 1 : d + 1; //for kappa
+    } else if (mode == 3) { // both u_off are 1
+        OPJ_UINT32 d1;
+        OPJ_UINT32 d2;
+        OPJ_UINT32 suffix_len;
+
+        d1 = dec[vlc & 0x7];  // LSBs of VLC are prefix codeword
+        vlc >>= d1 & 0x3;                // Consume bits
+        consumed_bits += d1 & 0x3;
+
+        d2 = dec[vlc & 0x7];  // LSBs of VLC are prefix codeword
+        vlc >>= d2 & 0x3;                // Consume bits
+        consumed_bits += d2 & 0x3;
+
+        suffix_len = ((d1 >> 2) & 0x7);
+        consumed_bits += suffix_len;
+
+        d1 = (d1 >> 5) + (vlc & ((1U << suffix_len) - 1)); // u value
+        u[0] = d1 + 1;  //1 for kappa
+        vlc >>= suffix_len;
+
+        suffix_len = ((d2 >> 2) & 0x7);
+        consumed_bits += suffix_len;
+
+        d2 = (d2 >> 5) + (vlc & ((1U << suffix_len) - 1)); // u value
+        u[1] = d2 + 1;  //1 for kappa
+    }
+    return consumed_bits;
+}
+
+//************************************************************************/
+/** @brief State structure for reading and unstuffing of forward-growing
+  *         bitstreams; these are: MagSgn and SPP bitstreams
+  */
+typedef struct frwd_struct {
+    const OPJ_UINT8* data; //!<pointer to bitstream
+    OPJ_UINT64 tmp;        //!<temporary buffer of read data
+    OPJ_UINT32 bits;       //!<number of bits stored in tmp
+    OPJ_BOOL unstuff;      //!<true if a bit needs to be unstuffed from next byte
+    int size;              //!<size of data
+    OPJ_UINT32 X;          //!<0 or 0xFF, X's are inserted at end of bitstream
+} frwd_struct_t;
+
+//************************************************************************/
+/** @brief Read and unstuffs 32 bits from forward-growing bitstream
+  *
+  *  A subroutine to read from both the MagSgn or SPP bitstreams;
+  *  in particular, when MagSgn bitstream is consumed, 0xFF's are fed,
+  *  while when SPP is exhausted 0's are fed in.
+  *  X controls this value.
+  *
+  *  Unstuffing prevent sequences that are more than 0xFF7F from appearing
+  *  in the conpressed sequence.  So whenever a value of 0xFF is coded, the
+  *  MSB of the next byte is set 0 and must be ignored during decoding.
+  *
+  *  Reading can go beyond the end of buffer by up to 3 bytes.
+  *
+  *  @param  [in]  msp is a pointer to frwd_struct_t structure
+  *
+  */
+static INLINE
+void frwd_read(frwd_struct_t *msp)
+{
+    OPJ_UINT32 val;
+    OPJ_UINT32 bits;
+    OPJ_UINT32 t;
+    OPJ_BOOL unstuff;
+
+    assert(msp->bits <= 32); // assert that there is a space for 32 bits
+
+    val = 0u;
+    if (msp->size > 3) {
+        val = read_le_uint32(msp->data);  // read 32 bits
+        msp->data += 4;           // increment pointer
+        msp->size -= 4;           // reduce size
+    } else if (msp->size > 0) {
+        int i = 0;
+        val = msp->X != 0 ? 0xFFFFFFFFu : 0;
+        while (msp->size > 0) {
+            OPJ_UINT32 v = *msp->data++;  // read one byte at a time
+            OPJ_UINT32 m = ~(0xFFu << i); // mask of location
+            val = (val & m) | (v << i);   // put one byte in its correct location
+            --msp->size;
+            i += 8;
+        }
+    } else {
+        val = msp->X != 0 ? 0xFFFFFFFFu : 0;
+    }
+
+    // we accumulate in t and keep a count of the number of bits in bits
+    bits = 8u - (msp->unstuff ? 1u : 0u);
+    t = val & 0xFF;
+    unstuff = ((val & 0xFF) == 0xFF);  // Do we need unstuffing next?
+
+    t |= ((val >> 8) & 0xFF) << bits;
+    bits += 8u - (unstuff ? 1u : 0u);
+    unstuff = (((val >> 8) & 0xFF) == 0xFF);
+
+    t |= ((val >> 16) & 0xFF) << bits;
+    bits += 8u - (unstuff ? 1u : 0u);
+    unstuff = (((val >> 16) & 0xFF) == 0xFF);
+
+    t |= ((val >> 24) & 0xFF) << bits;
+    bits += 8u - (unstuff ? 1u : 0u);
+    msp->unstuff = (((val >> 24) & 0xFF) == 0xFF); // for next byte
+
+    msp->tmp |= ((OPJ_UINT64)t) << msp->bits;  // move data to msp->tmp
+    msp->bits += bits;
+}
+
+//************************************************************************/
+/** @brief Initialize frwd_struct_t struct and reads some bytes
+  *
+  *  @param [in]  msp is a pointer to frwd_struct_t
+  *  @param [in]  data is a pointer to the start of data
+  *  @param [in]  size is the number of byte in the bitstream
+  *  @param [in]  X is the value fed in when the bitstream is exhausted.
+  *               See frwd_read.
+  */
+static INLINE
+void frwd_init(frwd_struct_t *msp, const OPJ_UINT8* data, int size,
+               OPJ_UINT32 X)
+{
+    int num, i;
+
+    msp->data = data;
+    msp->tmp = 0;
+    msp->bits = 0;
+    msp->unstuff = OPJ_FALSE;
+    msp->size = size;
+    msp->X = X;
+    assert(msp->X == 0 || msp->X == 0xFF);
+
+    //This code is designed for an architecture that read address should
+    // align to the read size (address multiple of 4 if read size is 4)
+    //These few lines take care of the case where data is not at a multiple
+    // of 4 boundary.  It reads 1,2,3 up to 4 bytes from the bitstream
+    num = 4 - (int)((intptr_t)(msp->data) & 0x3);
+    for (i = 0; i < num; ++i) {
+        OPJ_UINT64 d;
+        //read a byte if the buffer is not exhausted, otherwise set it to X
+        d = msp->size-- > 0 ? *msp->data++ : msp->X;
+        msp->tmp |= (d << msp->bits);      // store data in msp->tmp
+        msp->bits += 8u - (msp->unstuff ? 1u : 0u); // number of bits added to msp->tmp
+        msp->unstuff = ((d & 0xFF) == 0xFF); // unstuffing for next byte
+    }
+    frwd_read(msp); // read 32 bits more
+}
+
+//************************************************************************/
+/** @brief Consume num_bits bits from the bitstream of frwd_struct_t
+  *
+  *  @param [in]  msp is a pointer to frwd_struct_t
+  *  @param [in]  num_bits is the number of bit to consume
+  */
+static INLINE
+void frwd_advance(frwd_struct_t *msp, OPJ_UINT32 num_bits)
+{
+    assert(num_bits <= msp->bits);
+    msp->tmp >>= num_bits;  // consume num_bits
+    msp->bits -= num_bits;
+}
+
+//************************************************************************/
+/** @brief Fetches 32 bits from the frwd_struct_t bitstream
+  *
+  *  @param [in]  msp is a pointer to frwd_struct_t
+  */
+static INLINE
+OPJ_UINT32 frwd_fetch(frwd_struct_t *msp)
+{
+    if (msp->bits < 32) {
+        frwd_read(msp);
+        if (msp->bits < 32) { //need to test
+            frwd_read(msp);
+        }
+    }
+    return (OPJ_UINT32)msp->tmp;
+}
+
+//************************************************************************/
+/** @brief Allocates T1 buffers
+  *
+  *  @param [in, out]  t1 is codeblock cofficients storage
+  *  @param [in]       w is codeblock width
+  *  @param [in]       h is codeblock height
+  */
+static OPJ_BOOL opj_t1_allocate_buffers(
+    opj_t1_t *t1,
+    OPJ_UINT32 w,
+    OPJ_UINT32 h)
+{
+    OPJ_UINT32 flagssize;
+
+    /* No risk of overflow. Prior checks ensure those assert are met */
+    /* They are per the specification */
+    assert(w <= 1024);
+    assert(h <= 1024);
+    assert(w * h <= 4096);
+
+    /* encoder uses tile buffer, so no need to allocate */
+    {
+        OPJ_UINT32 datasize = w * h;
+
+        if (datasize > t1->datasize) {
+            opj_aligned_free(t1->data);
+            t1->data = (OPJ_INT32*)
+                       opj_aligned_malloc(datasize * sizeof(OPJ_INT32));
+            if (!t1->data) {
+                /* FIXME event manager error callback */
+                return OPJ_FALSE;
+            }
+            t1->datasize = datasize;
+        }
+        /* memset first arg is declared to never be null by gcc */
+        if (t1->data != NULL) {
+            memset(t1->data, 0, datasize * sizeof(OPJ_INT32));
+        }
+    }
+
+    // We expand these buffers to multiples of 16 bytes.
+    // We need 4 buffers of 129 integers each, expanded to 132 integers each
+    // We also need 514 bytes of buffer, expanded to 528 bytes
+    flagssize = 132U * sizeof(OPJ_UINT32) * 4U; // expanded to multiple of 16
+    flagssize += 528U; // 514 expanded to multiples of 16
+
+    {
+        if (flagssize > t1->flagssize) {
+
+            opj_aligned_free(t1->flags);
+            t1->flags = (opj_flag_t*) opj_aligned_malloc(flagssize * sizeof(opj_flag_t));
+            if (!t1->flags) {
+                /* FIXME event manager error callback */
+                return OPJ_FALSE;
+            }
+        }
+        t1->flagssize = flagssize;
+
+        memset(t1->flags, 0, flagssize * sizeof(opj_flag_t));
+    }
+
+    t1->w = w;
+    t1->h = h;
+
+    return OPJ_TRUE;
+}
+
+//************************************************************************/
+/** @brief Decodes one codeblock, processing the cleanup, siginificance
+  *         propagation, and magnitude refinement pass
+  *
+  *  @param [in, out]  t1 is codeblock cofficients storage
+  *  @param [in]       cblk is codeblock properties
+  *  @param [in]       orient is the subband to which the codeblock belongs (not needed)
+  *  @param [in]       roishift is region of interest shift
+  *  @param [in]       cblksty is codeblock style
+  *  @param [in]       p_manager is events print manager
+  *  @param [in]       p_manager_mutex a mutex to control access to p_manager
+  *  @param [in]       check_pterm: check termination (not used)
+  */
+OPJ_BOOL opj_t1_ht_decode_cblk(opj_t1_t *t1,
+                               opj_tcd_cblk_dec_t* cblk,
+                               OPJ_UINT32 orient,
+                               OPJ_UINT32 roishift,
+                               OPJ_UINT32 cblksty,
+                               opj_event_mgr_t *p_manager,
+                               opj_mutex_t* p_manager_mutex,
+                               OPJ_BOOL check_pterm)
+{
+    OPJ_BYTE* cblkdata = NULL;
+    OPJ_UINT8* coded_data;
+    OPJ_UINT32* decoded_data;
+    OPJ_UINT32 zero_bplanes;
+    OPJ_UINT32 num_passes;
+    OPJ_UINT32 lengths1;
+    OPJ_UINT32 lengths2;
+    OPJ_INT32 width;
+    OPJ_INT32 height;
+    OPJ_INT32 stride;
+    OPJ_UINT32 *pflags, *sigma1, *sigma2, *mbr1, *mbr2, *sip, sip_shift;
+    OPJ_UINT32 p;
+    OPJ_UINT32 zero_bplanes_p1;
+    int lcup, scup;
+    dec_mel_t mel;
+    rev_struct_t vlc;
+    frwd_struct_t magsgn;
+    frwd_struct_t sigprop;
+    rev_struct_t magref;
+    OPJ_UINT8 *lsp, *line_state;
+    int run;
+    OPJ_UINT32 vlc_val;              // fetched data from VLC bitstream
+    OPJ_UINT32 qinf[2];
+    OPJ_UINT32 c_q;
+    OPJ_UINT32* sp;
+    OPJ_INT32 x, y; // loop indices
+    OPJ_BOOL stripe_causal = (cblksty & J2K_CCP_CBLKSTY_VSC) != 0;
+    OPJ_UINT32 cblk_len = 0;
+
+    (void)(orient);      // stops unused parameter message
+    (void)(check_pterm); // stops unused parameter message
+
+    // We ignor orient, because the same decoder is used for all subbands
+    // We also ignore check_pterm, because I am not sure how it applies
+    if (roishift != 0) {
+        if (p_manager_mutex) {
+            opj_mutex_lock(p_manager_mutex);
+        }
+        opj_event_msg(p_manager, EVT_ERROR, "We do not support ROI in decoding "
+                      "HT codeblocks\n");
+        if (p_manager_mutex) {
+            opj_mutex_unlock(p_manager_mutex);
+        }
+        return OPJ_FALSE;
+    }
+
+    if (!opj_t1_allocate_buffers(
+                t1,
+                (OPJ_UINT32)(cblk->x1 - cblk->x0),
+                (OPJ_UINT32)(cblk->y1 - cblk->y0))) {
+        return OPJ_FALSE;
+    }
+
+    if (cblk->Mb == 0) {
+        return OPJ_TRUE;
+    }
+
+    /* numbps = Mb + 1 - zero_bplanes, Mb = Kmax, zero_bplanes = missing_msbs */
+    zero_bplanes = (cblk->Mb + 1) - cblk->numbps;
+
+    /* Compute whole codeblock length from chunk lengths */
+    cblk_len = 0;
+    {
+        OPJ_UINT32 i;
+        for (i = 0; i < cblk->numchunks; i++) {
+            cblk_len += cblk->chunks[i].len;
+        }
+    }
+
+    if (cblk->numchunks > 1 || t1->mustuse_cblkdatabuffer) {
+        OPJ_UINT32 i;
+
+        /* Allocate temporary memory if needed */
+        if (cblk_len > t1->cblkdatabuffersize) {
+            cblkdata = (OPJ_BYTE*)opj_realloc(
+                           t1->cblkdatabuffer, cblk_len);
+            if (cblkdata == NULL) {
+                return OPJ_FALSE;
+            }
+            t1->cblkdatabuffer = cblkdata;
+            t1->cblkdatabuffersize = cblk_len;
+        }
+
+        /* Concatenate all chunks */
+        cblkdata = t1->cblkdatabuffer;
+        cblk_len = 0;
+        for (i = 0; i < cblk->numchunks; i++) {
+            memcpy(cblkdata + cblk_len, cblk->chunks[i].data, cblk->chunks[i].len);
+            cblk_len += cblk->chunks[i].len;
+        }
+    } else if (cblk->numchunks == 1) {
+        cblkdata = cblk->chunks[0].data;
+    } else {
+        /* Not sure if that can happen in practice, but avoid Coverity to */
+        /* think we will dereference a null cblkdta pointer */
+        return OPJ_TRUE;
+    }
+
+    // OPJ_BYTE* coded_data is a pointer to bitstream
+    coded_data = cblkdata;
+    // OPJ_UINT32* decoded_data is a pointer to decoded codeblock data buf.
+    decoded_data = (OPJ_UINT32*)t1->data;
+    // OPJ_UINT32 num_passes is the number of passes: 1 if CUP only, 2 for
+    // CUP+SPP, and 3 for CUP+SPP+MRP
+    num_passes = cblk->numsegs > 0 ? cblk->segs[0].real_num_passes : 0;
+    num_passes += cblk->numsegs > 1 ? cblk->segs[1].real_num_passes : 0;
+    // OPJ_UINT32 lengths1 is the length of cleanup pass
+    lengths1 = num_passes > 0 ? cblk->segs[0].len : 0;
+    // OPJ_UINT32 lengths2 is the length of refinement passes (either SPP only or SPP+MRP)
+    lengths2 = num_passes > 1 ? cblk->segs[1].len : 0;
+    // OPJ_INT32 width is the decoded codeblock width
+    width = cblk->x1 - cblk->x0;
+    // OPJ_INT32 height is the decoded codeblock height
+    height = cblk->y1 - cblk->y0;
+    // OPJ_INT32 stride is the decoded codeblock buffer stride
+    stride = width;
+
+    /*  sigma1 and sigma2 contains significant (i.e., non-zero) pixel
+     *  locations.  The buffers are used interchangeably, because we need
+     *  more than 4 rows of significance information at a given time.
+     *  Each 32 bits contain significance information for 4 rows of 8
+     *  columns each.  If we denote 32 bits by 0xaaaaaaaa, the each "a" is
+     *  called a nibble and has significance information for 4 rows.
+     *  The least significant nibble has information for the first column,
+     *  and so on. The nibble's LSB is for the first row, and so on.
+     *  Since, at most, we can have 1024 columns in a quad, we need 128
+     *  entries; we added 1 for convenience when propagation of signifcance
+     *  goes outside the structure
+     *  To work in OpenJPEG these buffers has been expanded to 132.
+     */
+    // OPJ_UINT32 *pflags, *sigma1, *sigma2, *mbr1, *mbr2, *sip, sip_shift;
+    pflags = (OPJ_UINT32 *)t1->flags;
+    sigma1 = pflags;
+    sigma2 = sigma1 + 132;
+    // mbr arrangement is similar to sigma; mbr contains locations
+    // that become significant during significance propagation pass
+    mbr1 = sigma2 + 132;
+    mbr2 = mbr1 + 132;
+    //a pointer to sigma
+    sip = sigma1;  //pointers to arrays to be used interchangeably
+    sip_shift = 0; //the amount of shift needed for sigma
+
+    if (num_passes > 1 && lengths2 == 0) {
+        if (p_manager_mutex) {
+            opj_mutex_lock(p_manager_mutex);
+        }
+        opj_event_msg(p_manager, EVT_WARNING, "A malformed codeblock that has "
+                      "more than one coding pass, but zero length for "
+                      "2nd and potentially the 3rd pass in an HT codeblock.\n");
+        if (p_manager_mutex) {
+            opj_mutex_unlock(p_manager_mutex);
+        }
+        num_passes = 1;
+    }
+    if (num_passes > 3) {
+        if (p_manager_mutex) {
+            opj_mutex_lock(p_manager_mutex);
+        }
+        opj_event_msg(p_manager, EVT_ERROR, "We do not support more than 3 "
+                      "coding passes in an HT codeblock; This codeblocks has "
+                      "%d passes.\n", num_passes);
+        if (p_manager_mutex) {
+            opj_mutex_unlock(p_manager_mutex);
+        }
+        return OPJ_FALSE;
+    }
+
+    if (cblk->Mb > 30) {
+        /* This check is better moved to opj_t2_read_packet_header() in t2.c
+           We do not have enough precision to decode any passes
+           The design of openjpeg assumes that the bits of a 32-bit integer are
+           assigned as follows:
+           bit 31 is for sign
+           bits 30-1 are for magnitude
+           bit 0 is for the center of the quantization bin
+           Therefore we can only do values of cblk->Mb <= 30
+         */
+        if (p_manager_mutex) {
+            opj_mutex_lock(p_manager_mutex);
+        }
+        opj_event_msg(p_manager, EVT_ERROR, "32 bits are not enough to "
+                      "decode this codeblock, since the number of "
+                      "bitplane, %d, is larger than 30.\n", cblk->Mb);
+        if (p_manager_mutex) {
+            opj_mutex_unlock(p_manager_mutex);
+        }
+        return OPJ_FALSE;
+    }
+    if (zero_bplanes > cblk->Mb) {
+        /* This check is better moved to opj_t2_read_packet_header() in t2.c,
+           in the line "l_cblk->numbps = (OPJ_UINT32)l_band->numbps + 1 - i;"
+           where i is the zero bitplanes, and should be no larger than cblk->Mb
+           We cannot have more zero bitplanes than there are planes. */
+        if (p_manager_mutex) {
+            opj_mutex_lock(p_manager_mutex);
+        }
+        opj_event_msg(p_manager, EVT_ERROR, "Malformed HT codeblock. "
+                      "Decoding this codeblock is stopped. There are "
+                      "%d zero bitplanes in %d bitplanes.\n",
+                      zero_bplanes, cblk->Mb);
+
+        if (p_manager_mutex) {
+            opj_mutex_unlock(p_manager_mutex);
+        }
+        return OPJ_FALSE;
+    } else if (zero_bplanes == cblk->Mb && num_passes > 1) {
+        /* When the number of zero bitplanes is equal to the number of bitplanes,
+           only the cleanup pass makes sense*/
+        if (only_cleanup_pass_is_decoded == OPJ_FALSE) {
+            if (p_manager_mutex) {
+                opj_mutex_lock(p_manager_mutex);
+            }
+            /* We have a second check to prevent the possibility of an overrun condition,
+               in the very unlikely event of a second thread discovering that
+               only_cleanup_pass_is_decoded is false before the first thread changing
+               the condition. */
+            if (only_cleanup_pass_is_decoded == OPJ_FALSE) {
+                only_cleanup_pass_is_decoded = OPJ_TRUE;
+                opj_event_msg(p_manager, EVT_WARNING, "Malformed HT codeblock. "
+                              "When the number of zero planes bitplanes is "
+                              "equal to the number of bitplanes, only the cleanup "
+                              "pass makes sense, but we have %d passes in this "
+                              "codeblock. Therefore, only the cleanup pass will be "
+                              "decoded. This message will not be displayed again.\n",
+                              num_passes);
+            }
+            if (p_manager_mutex) {
+                opj_mutex_unlock(p_manager_mutex);
+            }
+        }
+        num_passes = 1;
+    }
+
+    /* OPJ_UINT32 */
+    p = cblk->numbps;
+
+    // OPJ_UINT32 zero planes plus 1
+    zero_bplanes_p1 = zero_bplanes + 1;
+
+    if (lengths1 < 2 || (OPJ_UINT32)lengths1 > cblk_len ||
+            (OPJ_UINT32)(lengths1 + lengths2) > cblk_len) {
+        if (p_manager_mutex) {
+            opj_mutex_lock(p_manager_mutex);
+        }
+        opj_event_msg(p_manager, EVT_ERROR, "Malformed HT codeblock. "
+                      "Invalid codeblock length values.\n");
+
+        if (p_manager_mutex) {
+            opj_mutex_unlock(p_manager_mutex);
+        }
+        return OPJ_FALSE;
+    }
+    // read scup and fix the bytes there
+    lcup = (int)lengths1;  // length of CUP
+    //scup is the length of MEL + VLC
+    scup = (((int)coded_data[lcup - 1]) << 4) + (coded_data[lcup - 2] & 0xF);
+    if (scup < 2 || scup > lcup || scup > 4079) { //something is wrong
+        /* The standard stipulates 2 <= Scup <= min(Lcup, 4079) */
+        if (p_manager_mutex) {
+            opj_mutex_lock(p_manager_mutex);
+        }
+        opj_event_msg(p_manager, EVT_ERROR, "Malformed HT codeblock. "
+                      "One of the following condition is not met: "
+                      "2 <= Scup <= min(Lcup, 4079)\n");
+
+        if (p_manager_mutex) {
+            opj_mutex_unlock(p_manager_mutex);
+        }
+        return OPJ_FALSE;
+    }
+
+    // init structures
+    if (mel_init(&mel, coded_data, lcup, scup) == OPJ_FALSE) {
+        if (p_manager_mutex) {
+            opj_mutex_lock(p_manager_mutex);
+        }
+        opj_event_msg(p_manager, EVT_ERROR, "Malformed HT codeblock. "
+                      "Incorrect MEL segment sequence.\n");
+        if (p_manager_mutex) {
+            opj_mutex_unlock(p_manager_mutex);
+        }
+        return OPJ_FALSE;
+    }
+    rev_init(&vlc, coded_data, lcup, scup);
+    frwd_init(&magsgn, coded_data, lcup - scup, 0xFF);
+    if (num_passes > 1) { // needs to be tested
+        frwd_init(&sigprop, coded_data + lengths1, (int)lengths2, 0);
+    }
+    if (num_passes > 2) {
+        rev_init_mrp(&magref, coded_data, (int)lengths1, (int)lengths2);
+    }
+
+    /** State storage
+      *  One byte per quad; for 1024 columns, or 512 quads, we need
+      *  512 bytes. We are using 2 extra bytes one on the left and one on
+      *  the right for convenience.
+      *
+      *  The MSB bit in each byte is (\sigma^nw | \sigma^n), and the 7 LSBs
+      *  contain max(E^nw | E^n)
+      */
+
+    // 514 is enough for a block width of 1024, +2 extra
+    // here expanded to 528
+    line_state = (OPJ_UINT8 *)(mbr2 + 132);
+
+    //initial 2 lines
+    /////////////////
+    lsp = line_state;              // point to line state
+    lsp[0] = 0;                    // for initial row of quad, we set to 0
+    run = mel_get_run(&mel);    // decode runs of events from MEL bitstrm
+    // data represented as runs of 0 events
+    // See mel_decode description
+    qinf[0] = qinf[1] = 0;      // quad info decoded from VLC bitstream
+    c_q = 0;                    // context for quad q
+    sp = decoded_data;          // decoded codeblock samples
+    // vlc_val;                 // fetched data from VLC bitstream
+
+    for (x = 0; x < width; x += 4) { // one iteration per quad pair
+        OPJ_UINT32 U_q[2]; // u values for the quad pair
+        OPJ_UINT32 uvlc_mode;
+        OPJ_UINT32 consumed_bits;
+        OPJ_UINT32 m_n, v_n;
+        OPJ_UINT32 ms_val;
+        OPJ_UINT32 locs;
+
+        // decode VLC
+        /////////////
+
+        //first quad
+        // Get the head of the VLC bitstream. One fetch is enough for two
+        // quads, since the largest VLC code is 7 bits, and maximum number of
+        // bits used for u is 8.  Therefore for two quads we need 30 bits
+        // (if we include unstuffing, then 32 bits are enough, since we have
+        // a maximum of one stuffing per two bytes)
+        vlc_val = rev_fetch(&vlc);
+
+        //decode VLC using the context c_q and the head of the VLC bitstream
+        qinf[0] = vlc_tbl0[(c_q << 7) | (vlc_val & 0x7F) ];
+
+        if (c_q == 0) { // if zero context, we need to use one MEL event
+            run -= 2; //the number of 0 events is multiplied by 2, so subtract 2
+
+            // Is the run terminated in 1? if so, use decoded VLC code,
+            // otherwise, discard decoded data, since we will decoded again
+            // using a different context
+            qinf[0] = (run == -1) ? qinf[0] : 0;
+
+            // is run -1 or -2? this means a run has been consumed
+            if (run < 0) {
+                run = mel_get_run(&mel);    // get another run
+            }
+        }
+
+        // prepare context for the next quad; eqn. 1 in ITU T.814
+        c_q = ((qinf[0] & 0x10) >> 4) | ((qinf[0] & 0xE0) >> 5);
+
+        //remove data from vlc stream (0 bits are removed if qinf is not used)
+        vlc_val = rev_advance(&vlc, qinf[0] & 0x7);
+
+        //update sigma
+        // The update depends on the value of x; consider one OPJ_UINT32
+        // if x is 0, 8, 16 and so on, then this line update c locations
+        //      nibble (4 bits) number   0 1 2 3 4 5 6 7
+        //                         LSB   c c 0 0 0 0 0 0
+        //                               c c 0 0 0 0 0 0
+        //                               0 0 0 0 0 0 0 0
+        //                               0 0 0 0 0 0 0 0
+        // if x is 4, 12, 20, then this line update locations c
+        //      nibble (4 bits) number   0 1 2 3 4 5 6 7
+        //                         LSB   0 0 0 0 c c 0 0
+        //                               0 0 0 0 c c 0 0
+        //                               0 0 0 0 0 0 0 0
+        //                               0 0 0 0 0 0 0 0
+        *sip |= (((qinf[0] & 0x30) >> 4) | ((qinf[0] & 0xC0) >> 2)) << sip_shift;
+
+        //second quad
+        qinf[1] = 0;
+        if (x + 2 < width) { // do not run if codeblock is narrower
+            //decode VLC using the context c_q and the head of the VLC bitstream
+            qinf[1] = vlc_tbl0[(c_q << 7) | (vlc_val & 0x7F)];
+
+            // if context is zero, use one MEL event
+            if (c_q == 0) { //zero context
+                run -= 2; //subtract 2, since events number if multiplied by 2
+
+                // if event is 0, discard decoded qinf
+                qinf[1] = (run == -1) ? qinf[1] : 0;
+
+                if (run < 0) { // have we consumed all events in a run
+                    run = mel_get_run(&mel);    // if yes, then get another run
+                }
+            }
+
+            //prepare context for the next quad, eqn. 1 in ITU T.814
+            c_q = ((qinf[1] & 0x10) >> 4) | ((qinf[1] & 0xE0) >> 5);
+
+            //remove data from vlc stream, if qinf is not used, cwdlen is 0
+            vlc_val = rev_advance(&vlc, qinf[1] & 0x7);
+        }
+
+        //update sigma
+        // The update depends on the value of x; consider one OPJ_UINT32
+        // if x is 0, 8, 16 and so on, then this line update c locations
+        //      nibble (4 bits) number   0 1 2 3 4 5 6 7
+        //                         LSB   0 0 c c 0 0 0 0
+        //                               0 0 c c 0 0 0 0
+        //                               0 0 0 0 0 0 0 0
+        //                               0 0 0 0 0 0 0 0
+        // if x is 4, 12, 20, then this line update locations c
+        //      nibble (4 bits) number   0 1 2 3 4 5 6 7
+        //                         LSB   0 0 0 0 0 0 c c
+        //                               0 0 0 0 0 0 c c
+        //                               0 0 0 0 0 0 0 0
+        //                               0 0 0 0 0 0 0 0
+        *sip |= (((qinf[1] & 0x30) | ((qinf[1] & 0xC0) << 2))) << (4 + sip_shift);
+
+        sip += x & 0x7 ? 1 : 0; // move sigma pointer to next entry
+        sip_shift ^= 0x10;      // increment/decrement sip_shift by 16
+
+        // retrieve u
+        /////////////
+
+        // uvlc_mode is made up of u_offset bits from the quad pair
+        uvlc_mode = ((qinf[0] & 0x8) >> 3) | ((qinf[1] & 0x8) >> 2);
+        if (uvlc_mode == 3) { // if both u_offset are set, get an event from
+            // the MEL run of events
+            run -= 2; //subtract 2, since events number if multiplied by 2
+            uvlc_mode += (run == -1) ? 1 : 0; //increment uvlc_mode if event is 1
+            if (run < 0) { // if run is consumed (run is -1 or -2), get another run
+                run = mel_get_run(&mel);
+            }
+        }
+        //decode uvlc_mode to get u for both quads
+        consumed_bits = decode_init_uvlc(vlc_val, uvlc_mode, U_q);
+        if (U_q[0] > zero_bplanes_p1 || U_q[1] > zero_bplanes_p1) {
+            if (p_manager_mutex) {
+                opj_mutex_lock(p_manager_mutex);
+            }
+            opj_event_msg(p_manager, EVT_ERROR, "Malformed HT codeblock. Decoding "
+                          "this codeblock is stopped. U_q is larger than zero "
+                          "bitplanes + 1 \n");
+            if (p_manager_mutex) {
+                opj_mutex_unlock(p_manager_mutex);
+            }
+            return OPJ_FALSE;
+        }
+
+        //consume u bits in the VLC code
+        vlc_val = rev_advance(&vlc, consumed_bits);
+
+        //decode magsgn and update line_state
+        /////////////////////////////////////
+
+        //We obtain a mask for the samples locations that needs evaluation
+        locs = 0xFF;
+        if (x + 4 > width) {
+            locs >>= (x + 4 - width) << 1;    // limits width
+        }
+        locs = height > 1 ? locs : (locs & 0x55);         // limits height
+
+        if ((((qinf[0] & 0xF0) >> 4) | (qinf[1] & 0xF0)) & ~locs) {
+            if (p_manager_mutex) {
+                opj_mutex_lock(p_manager_mutex);
+            }
+            opj_event_msg(p_manager, EVT_ERROR, "Malformed HT codeblock. "
+                          "VLC code produces significant samples outside "
+                          "the codeblock area.\n");
+            if (p_manager_mutex) {
+                opj_mutex_unlock(p_manager_mutex);
+            }
+            return OPJ_FALSE;
+        }
+
+        //first quad, starting at first sample in quad and moving on
+        if (qinf[0] & 0x10) { //is it significant? (sigma_n)
+            OPJ_UINT32 val;
+
+            ms_val = frwd_fetch(&magsgn);         //get 32 bits of magsgn data
+            m_n = U_q[0] - ((qinf[0] >> 12) & 1); //evaluate m_n (number of bits
+            // to read from bitstream), using EMB e_k
+            frwd_advance(&magsgn, m_n);         //consume m_n
+            val = ms_val << 31;                 //get sign bit
+            v_n = ms_val & ((1U << m_n) - 1);   //keep only m_n bits
+            v_n |= ((qinf[0] & 0x100) >> 8) << m_n;  //add EMB e_1 as MSB
+            v_n |= 1;                                //add center of bin
+            //v_n now has 2 * (\mu - 1) + 0.5 with correct sign bit
+            //add 2 to make it 2*\mu+0.5, shift it up to missing MSBs
+            sp[0] = val | ((v_n + 2) << (p - 1));
+        } else if (locs & 0x1) { // if this is inside the codeblock, set the
+            sp[0] = 0;           // sample to zero
+        }
+
+        if (qinf[0] & 0x20) { //sigma_n
+            OPJ_UINT32 val, t;
+
+            ms_val = frwd_fetch(&magsgn);         //get 32 bits
+            m_n = U_q[0] - ((qinf[0] >> 13) & 1); //m_n, uses EMB e_k
+            frwd_advance(&magsgn, m_n);           //consume m_n
+            val = ms_val << 31;                   //get sign bit
+            v_n = ms_val & ((1U << m_n) - 1);     //keep only m_n bits
+            v_n |= ((qinf[0] & 0x200) >> 9) << m_n; //add EMB e_1
+            v_n |= 1;                               //bin center
+            //v_n now has 2 * (\mu - 1) + 0.5 with correct sign bit
+            //add 2 to make it 2*\mu+0.5, shift it up to missing MSBs
+            sp[stride] = val | ((v_n + 2) << (p - 1));
+
+            //update line_state: bit 7 (\sigma^N), and E^N
+            t = lsp[0] & 0x7F;       // keep E^NW
+            v_n = 32 - count_leading_zeros(v_n);
+            lsp[0] = (OPJ_UINT8)(0x80 | (t > v_n ? t : v_n)); //max(E^NW, E^N) | s
+        } else if (locs & 0x2) { // if this is inside the codeblock, set the
+            sp[stride] = 0;      // sample to zero
+        }
+
+        ++lsp; // move to next quad information
+        ++sp;  // move to next column of samples
+
+        //this is similar to the above two samples
+        if (qinf[0] & 0x40) {
+            OPJ_UINT32 val;
+
+            ms_val = frwd_fetch(&magsgn);
+            m_n = U_q[0] - ((qinf[0] >> 14) & 1);
+            frwd_advance(&magsgn, m_n);
+            val = ms_val << 31;
+            v_n = ms_val & ((1U << m_n) - 1);
+            v_n |= (((qinf[0] & 0x400) >> 10) << m_n);
+            v_n |= 1;
+            sp[0] = val | ((v_n + 2) << (p - 1));
+        } else if (locs & 0x4) {
+            sp[0] = 0;
+        }
+
+        lsp[0] = 0;
+        if (qinf[0] & 0x80) {
+            OPJ_UINT32 val;
+            ms_val = frwd_fetch(&magsgn);
+            m_n = U_q[0] - ((qinf[0] >> 15) & 1); //m_n
+            frwd_advance(&magsgn, m_n);
+            val = ms_val << 31;
+            v_n = ms_val & ((1U << m_n) - 1);
+            v_n |= ((qinf[0] & 0x800) >> 11) << m_n;
+            v_n |= 1; //center of bin
+            sp[stride] = val | ((v_n + 2) << (p - 1));
+
+            //line_state: bit 7 (\sigma^NW), and E^NW for next quad
+            lsp[0] = (OPJ_UINT8)(0x80 | (32 - count_leading_zeros(v_n)));
+        } else if (locs & 0x8) { //if outside set to 0
+            sp[stride] = 0;
+        }
+
+        ++sp; //move to next column
+
+        //second quad
+        if (qinf[1] & 0x10) {
+            OPJ_UINT32 val;
+
+            ms_val = frwd_fetch(&magsgn);
+            m_n = U_q[1] - ((qinf[1] >> 12) & 1); //m_n
+            frwd_advance(&magsgn, m_n);
+            val = ms_val << 31;
+            v_n = ms_val & ((1U << m_n) - 1);
+            v_n |= (((qinf[1] & 0x100) >> 8) << m_n);
+            v_n |= 1;
+            sp[0] = val | ((v_n + 2) << (p - 1));
+        } else if (locs & 0x10) {
+            sp[0] = 0;
+        }
+
+        if (qinf[1] & 0x20) {
+            OPJ_UINT32 val, t;
+
+            ms_val = frwd_fetch(&magsgn);
+            m_n = U_q[1] - ((qinf[1] >> 13) & 1); //m_n
+            frwd_advance(&magsgn, m_n);
+            val = ms_val << 31;
+            v_n = ms_val & ((1U << m_n) - 1);
+            v_n |= (((qinf[1] & 0x200) >> 9) << m_n);
+            v_n |= 1;
+            sp[stride] = val | ((v_n + 2) << (p - 1));
+
+            //update line_state: bit 7 (\sigma^N), and E^N
+            t = lsp[0] & 0x7F;            //E^NW
+            v_n = 32 - count_leading_zeros(v_n);     //E^N
+            lsp[0] = (OPJ_UINT8)(0x80 | (t > v_n ? t : v_n)); //max(E^NW, E^N) | s
+        } else if (locs & 0x20) {
+            sp[stride] = 0;    //no need to update line_state
+        }
+
+        ++lsp; //move line state to next quad
+        ++sp;  //move to next sample
+
+        if (qinf[1] & 0x40) {
+            OPJ_UINT32 val;
+
+            ms_val = frwd_fetch(&magsgn);
+            m_n = U_q[1] - ((qinf[1] >> 14) & 1); //m_n
+            frwd_advance(&magsgn, m_n);
+            val = ms_val << 31;
+            v_n = ms_val & ((1U << m_n) - 1);
+            v_n |= (((qinf[1] & 0x400) >> 10) << m_n);
+            v_n |= 1;
+            sp[0] = val | ((v_n + 2) << (p - 1));
+        } else if (locs & 0x40) {
+            sp[0] = 0;
+        }
+
+        lsp[0] = 0;
+        if (qinf[1] & 0x80) {
+            OPJ_UINT32 val;
+
+            ms_val = frwd_fetch(&magsgn);
+            m_n = U_q[1] - ((qinf[1] >> 15) & 1); //m_n
+            frwd_advance(&magsgn, m_n);
+            val = ms_val << 31;
+            v_n = ms_val & ((1U << m_n) - 1);
+            v_n |= (((qinf[1] & 0x800) >> 11) << m_n);
+            v_n |= 1; //center of bin
+            sp[stride] = val | ((v_n + 2) << (p - 1));
+
+            //line_state: bit 7 (\sigma^NW), and E^NW for next quad
+            lsp[0] = (OPJ_UINT8)(0x80 | (32 - count_leading_zeros(v_n)));
+        } else if (locs & 0x80) {
+            sp[stride] = 0;
+        }
+
+        ++sp;
+    }
+
+    //non-initial lines
+    //////////////////////////
+    for (y = 2; y < height; /*done at the end of loop*/) {
+        OPJ_UINT32 *sip;
+        OPJ_UINT8 ls0;
+        OPJ_INT32 x;
+
+        sip_shift ^= 0x2;  // shift sigma to the upper half od the nibble
+        sip_shift &= 0xFFFFFFEFU; //move back to 0 (it might have been at 0x10)
+        sip = y & 0x4 ? sigma2 : sigma1; //choose sigma array
+
+        lsp = line_state;
+        ls0 = lsp[0];                   // read the line state value
+        lsp[0] = 0;                     // and set it to zero
+        sp = decoded_data + y * stride; // generated samples
+        c_q = 0;                        // context
+        for (x = 0; x < width; x += 4) {
+            OPJ_UINT32 U_q[2];
+            OPJ_UINT32 uvlc_mode, consumed_bits;
+            OPJ_UINT32 m_n, v_n;
+            OPJ_UINT32 ms_val;
+            OPJ_UINT32 locs;
+
+            // decode vlc
+            /////////////
+
+            //first quad
+            // get context, eqn. 2 ITU T.814
+            // c_q has \sigma^W | \sigma^SW
+            c_q |= (ls0 >> 7);          //\sigma^NW | \sigma^N
+            c_q |= (lsp[1] >> 5) & 0x4; //\sigma^NE | \sigma^NF
+
+            //the following is very similar to previous code, so please refer to
+            // that
+            vlc_val = rev_fetch(&vlc);
+            qinf[0] = vlc_tbl1[(c_q << 7) | (vlc_val & 0x7F)];
+            if (c_q == 0) { //zero context
+                run -= 2;
+                qinf[0] = (run == -1) ? qinf[0] : 0;
+                if (run < 0) {
+                    run = mel_get_run(&mel);
+                }
+            }
+            //prepare context for the next quad, \sigma^W | \sigma^SW
+            c_q = ((qinf[0] & 0x40) >> 5) | ((qinf[0] & 0x80) >> 6);
+
+            //remove data from vlc stream
+            vlc_val = rev_advance(&vlc, qinf[0] & 0x7);
+
+            //update sigma
+            // The update depends on the value of x and y; consider one OPJ_UINT32
+            // if x is 0, 8, 16 and so on, and y is 2, 6, etc., then this
+            // line update c locations
+            //      nibble (4 bits) number   0 1 2 3 4 5 6 7
+            //                         LSB   0 0 0 0 0 0 0 0
+            //                               0 0 0 0 0 0 0 0
+            //                               c c 0 0 0 0 0 0
+            //                               c c 0 0 0 0 0 0
+            *sip |= (((qinf[0] & 0x30) >> 4) | ((qinf[0] & 0xC0) >> 2)) << sip_shift;
+
+            //second quad
+            qinf[1] = 0;
+            if (x + 2 < width) {
+                c_q |= (lsp[1] >> 7);
+                c_q |= (lsp[2] >> 5) & 0x4;
+                qinf[1] = vlc_tbl1[(c_q << 7) | (vlc_val & 0x7F)];
+                if (c_q == 0) { //zero context
+                    run -= 2;
+                    qinf[1] = (run == -1) ? qinf[1] : 0;
+                    if (run < 0) {
+                        run = mel_get_run(&mel);
+                    }
+                }
+                //prepare context for the next quad
+                c_q = ((qinf[1] & 0x40) >> 5) | ((qinf[1] & 0x80) >> 6);
+                //remove data from vlc stream
+                vlc_val = rev_advance(&vlc, qinf[1] & 0x7);
+            }
+
+            //update sigma
+            *sip |= (((qinf[1] & 0x30) | ((qinf[1] & 0xC0) << 2))) << (4 + sip_shift);
+
+            sip += x & 0x7 ? 1 : 0;
+            sip_shift ^= 0x10;
+
+            //retrieve u
+            ////////////
+            uvlc_mode = ((qinf[0] & 0x8) >> 3) | ((qinf[1] & 0x8) >> 2);
+            consumed_bits = decode_noninit_uvlc(vlc_val, uvlc_mode, U_q);
+            vlc_val = rev_advance(&vlc, consumed_bits);
+
+            //calculate E^max and add it to U_q, eqns 5 and 6 in ITU T.814
+            if ((qinf[0] & 0xF0) & ((qinf[0] & 0xF0) - 1)) { // is \gamma_q 1?
+                OPJ_UINT32 E = (ls0 & 0x7Fu);
+                E = E > (lsp[1] & 0x7Fu) ? E : (lsp[1] & 0x7Fu); //max(E, E^NE, E^NF)
+                //since U_q already has u_q + 1, we subtract 2 instead of 1
+                U_q[0] += E > 2 ? E - 2 : 0;
+            }
+
+            if ((qinf[1] & 0xF0) & ((qinf[1] & 0xF0) - 1)) { //is \gamma_q 1?
+                OPJ_UINT32 E = (lsp[1] & 0x7Fu);
+                E = E > (lsp[2] & 0x7Fu) ? E : (lsp[2] & 0x7Fu); //max(E, E^NE, E^NF)
+                //since U_q already has u_q + 1, we subtract 2 instead of 1
+                U_q[1] += E > 2 ? E - 2 : 0;
+            }
+
+            if (U_q[0] > zero_bplanes_p1 || U_q[1] > zero_bplanes_p1) {
+                if (p_manager_mutex) {
+                    opj_mutex_lock(p_manager_mutex);
+                }
+                opj_event_msg(p_manager, EVT_ERROR, "Malformed HT codeblock. "
+                              "Decoding this codeblock is stopped. U_q is"
+                              "larger than bitplanes + 1 \n");
+                if (p_manager_mutex) {
+                    opj_mutex_unlock(p_manager_mutex);
+                }
+                return OPJ_FALSE;
+            }
+
+            ls0 = lsp[2]; //for next double quad
+            lsp[1] = lsp[2] = 0;
+
+            //decode magsgn and update line_state
+            /////////////////////////////////////
+
+            //locations where samples need update
+            locs = 0xFF;
+            if (x + 4 > width) {
+                locs >>= (x + 4 - width) << 1;
+            }
+            locs = y + 2 <= height ? locs : (locs & 0x55);
+
+            if ((((qinf[0] & 0xF0) >> 4) | (qinf[1] & 0xF0)) & ~locs) {
+                if (p_manager_mutex) {
+                    opj_mutex_lock(p_manager_mutex);
+                }
+                opj_event_msg(p_manager, EVT_ERROR, "Malformed HT codeblock. "
+                              "VLC code produces significant samples outside "
+                              "the codeblock area.\n");
+                if (p_manager_mutex) {
+                    opj_mutex_unlock(p_manager_mutex);
+                }
+                return OPJ_FALSE;
+            }
+
+
+
+            if (qinf[0] & 0x10) { //sigma_n
+                OPJ_UINT32 val;
+
+                ms_val = frwd_fetch(&magsgn);
+                m_n = U_q[0] - ((qinf[0] >> 12) & 1); //m_n
+                frwd_advance(&magsgn, m_n);
+                val = ms_val << 31;
+                v_n = ms_val & ((1U << m_n) - 1);
+                v_n |= ((qinf[0] & 0x100) >> 8) << m_n;
+                v_n |= 1; //center of bin
+                sp[0] = val | ((v_n + 2) << (p - 1));
+            } else if (locs & 0x1) {
+                sp[0] = 0;
+            }
+
+            if (qinf[0] & 0x20) { //sigma_n
+                OPJ_UINT32 val, t;
+
+                ms_val = frwd_fetch(&magsgn);
+                m_n = U_q[0] - ((qinf[0] >> 13) & 1); //m_n
+                frwd_advance(&magsgn, m_n);
+                val = ms_val << 31;
+                v_n = ms_val & ((1U << m_n) - 1);
+                v_n |= ((qinf[0] & 0x200) >> 9) << m_n;
+                v_n |= 1; //center of bin
+                sp[stride] = val | ((v_n + 2) << (p - 1));
+
+                //update line_state: bit 7 (\sigma^N), and E^N
+                t = lsp[0] & 0x7F;          //E^NW
+                v_n = 32 - count_leading_zeros(v_n);
+                lsp[0] = (OPJ_UINT8)(0x80 | (t > v_n ? t : v_n));
+            } else if (locs & 0x2) {
+                sp[stride] = 0;    //no need to update line_state
+            }
+
+            ++lsp;
+            ++sp;
+
+            if (qinf[0] & 0x40) { //sigma_n
+                OPJ_UINT32 val;
+
+                ms_val = frwd_fetch(&magsgn);
+                m_n = U_q[0] - ((qinf[0] >> 14) & 1); //m_n
+                frwd_advance(&magsgn, m_n);
+                val = ms_val << 31;
+                v_n = ms_val & ((1U << m_n) - 1);
+                v_n |= (((qinf[0] & 0x400) >> 10) << m_n);
+                v_n |= 1;                            //center of bin
+                sp[0] = val | ((v_n + 2) << (p - 1));
+            } else if (locs & 0x4) {
+                sp[0] = 0;
+            }
+
+            if (qinf[0] & 0x80) { //sigma_n
+                OPJ_UINT32 val;
+
+                ms_val = frwd_fetch(&magsgn);
+                m_n = U_q[0] - ((qinf[0] >> 15) & 1); //m_n
+                frwd_advance(&magsgn, m_n);
+                val = ms_val << 31;
+                v_n = ms_val & ((1U << m_n) - 1);
+                v_n |= ((qinf[0] & 0x800) >> 11) << m_n;
+                v_n |= 1; //center of bin
+                sp[stride] = val | ((v_n + 2) << (p - 1));
+
+                //update line_state: bit 7 (\sigma^NW), and E^NW for next quad
+                lsp[0] = (OPJ_UINT8)(0x80 | (32 - count_leading_zeros(v_n)));
+            } else if (locs & 0x8) {
+                sp[stride] = 0;
+            }
+
+            ++sp;
+
+            if (qinf[1] & 0x10) { //sigma_n
+                OPJ_UINT32 val;
+
+                ms_val = frwd_fetch(&magsgn);
+                m_n = U_q[1] - ((qinf[1] >> 12) & 1); //m_n
+                frwd_advance(&magsgn, m_n);
+                val = ms_val << 31;
+                v_n = ms_val & ((1U << m_n) - 1);
+                v_n |= (((qinf[1] & 0x100) >> 8) << m_n);
+                v_n |= 1;                            //center of bin
+                sp[0] = val | ((v_n + 2) << (p - 1));
+            } else if (locs & 0x10) {
+                sp[0] = 0;
+            }
+
+            if (qinf[1] & 0x20) { //sigma_n
+                OPJ_UINT32 val, t;
+
+                ms_val = frwd_fetch(&magsgn);
+                m_n = U_q[1] - ((qinf[1] >> 13) & 1); //m_n
+                frwd_advance(&magsgn, m_n);
+                val = ms_val << 31;
+                v_n = ms_val & ((1U << m_n) - 1);
+                v_n |= (((qinf[1] & 0x200) >> 9) << m_n);
+                v_n |= 1; //center of bin
+                sp[stride] = val | ((v_n + 2) << (p - 1));
+
+                //update line_state: bit 7 (\sigma^N), and E^N
+                t = lsp[0] & 0x7F;          //E^NW
+                v_n = 32 - count_leading_zeros(v_n);
+                lsp[0] = (OPJ_UINT8)(0x80 | (t > v_n ? t : v_n));
+            } else if (locs & 0x20) {
+                sp[stride] = 0;    //no need to update line_state
+            }
+
+            ++lsp;
+            ++sp;
+
+            if (qinf[1] & 0x40) { //sigma_n
+                OPJ_UINT32 val;
+
+                ms_val = frwd_fetch(&magsgn);
+                m_n = U_q[1] - ((qinf[1] >> 14) & 1); //m_n
+                frwd_advance(&magsgn, m_n);
+                val = ms_val << 31;
+                v_n = ms_val & ((1U << m_n) - 1);
+                v_n |= (((qinf[1] & 0x400) >> 10) << m_n);
+                v_n |= 1;                            //center of bin
+                sp[0] = val | ((v_n + 2) << (p - 1));
+            } else if (locs & 0x40) {
+                sp[0] = 0;
+            }
+
+            if (qinf[1] & 0x80) { //sigma_n
+                OPJ_UINT32 val;
+
+                ms_val = frwd_fetch(&magsgn);
+                m_n = U_q[1] - ((qinf[1] >> 15) & 1); //m_n
+                frwd_advance(&magsgn, m_n);
+                val = ms_val << 31;
+                v_n = ms_val & ((1U << m_n) - 1);
+                v_n |= (((qinf[1] & 0x800) >> 11) << m_n);
+                v_n |= 1; //center of bin
+                sp[stride] = val | ((v_n + 2) << (p - 1));
+
+                //update line_state: bit 7 (\sigma^NW), and E^NW for next quad
+                lsp[0] = (OPJ_UINT8)(0x80 | (32 - count_leading_zeros(v_n)));
+            } else if (locs & 0x80) {
+                sp[stride] = 0;
+            }
+
+            ++sp;
+        }
+
+        y += 2;
+        if (num_passes > 1 && (y & 3) == 0) { //executed at multiples of 4
+            // This is for SPP and potentially MRP
+
+            if (num_passes > 2) { //do MRP
+                // select the current stripe
+                OPJ_UINT32 *cur_sig = y & 0x4 ? sigma1 : sigma2;
+                // the address of the data that needs updating
+                OPJ_UINT32 *dpp = decoded_data + (y - 4) * stride;
+                OPJ_UINT32 half = 1u << (p - 2); // half the center of the bin
+                OPJ_INT32 i;
+                for (i = 0; i < width; i += 8) {
+                    //Process one entry from sigma array at a time
+                    // Each nibble (4 bits) in the sigma array represents 4 rows,
+                    // and the 32 bits contain 8 columns
+                    OPJ_UINT32 cwd = rev_fetch_mrp(&magref); // get 32 bit data
+                    OPJ_UINT32 sig = *cur_sig++; // 32 bit that will be processed now
+                    OPJ_UINT32 col_mask = 0xFu;  // a mask for a column in sig
+                    OPJ_UINT32 *dp = dpp + i;    // next column in decode samples
+                    if (sig) { // if any of the 32 bits are set
+                        int j;
+                        for (j = 0; j < 8; ++j, dp++) { //one column at a time
+                            if (sig & col_mask) { // lowest nibble
+                                OPJ_UINT32 sample_mask = 0x11111111u & col_mask; //LSB
+
+                                if (sig & sample_mask) { //if LSB is set
+                                    OPJ_UINT32 sym;
+
+                                    assert(dp[0] != 0); // decoded value cannot be zero
+                                    sym = cwd & 1; // get it value
+                                    // remove center of bin if sym is 0
+                                    dp[0] ^= (1 - sym) << (p - 1);
+                                    dp[0] |= half;      // put half the center of bin
+                                    cwd >>= 1;          //consume word
+                                }
+                                sample_mask += sample_mask; //next row
+
+                                if (sig & sample_mask) {
+                                    OPJ_UINT32 sym;
+
+                                    assert(dp[stride] != 0);
+                                    sym = cwd & 1;
+                                    dp[stride] ^= (1 - sym) << (p - 1);
+                                    dp[stride] |= half;
+                                    cwd >>= 1;
+                                }
+                                sample_mask += sample_mask;
+
+                                if (sig & sample_mask) {
+                                    OPJ_UINT32 sym;
+
+                                    assert(dp[2 * stride] != 0);
+                                    sym = cwd & 1;
+                                    dp[2 * stride] ^= (1 - sym) << (p - 1);
+                                    dp[2 * stride] |= half;
+                                    cwd >>= 1;
+                                }
+                                sample_mask += sample_mask;
+
+                                if (sig & sample_mask) {
+                                    OPJ_UINT32 sym;
+
+                                    assert(dp[3 * stride] != 0);
+                                    sym = cwd & 1;
+                                    dp[3 * stride] ^= (1 - sym) << (p - 1);
+                                    dp[3 * stride] |= half;
+                                    cwd >>= 1;
+                                }
+                                sample_mask += sample_mask;
+                            }
+                            col_mask <<= 4; //next column
+                        }
+                    }
+                    // consume data according to the number of bits set
+                    rev_advance_mrp(&magref, population_count(sig));
+                }
+            }
+
+            if (y >= 4) { // update mbr array at the end of each stripe
+                //generate mbr corresponding to a stripe
+                OPJ_UINT32 *sig = y & 0x4 ? sigma1 : sigma2;
+                OPJ_UINT32 *mbr = y & 0x4 ? mbr1 : mbr2;
+
+                //data is processed in patches of 8 columns, each
+                // each 32 bits in sigma1 or mbr1 represent 4 rows
+
+                //integrate horizontally
+                OPJ_UINT32 prev = 0; // previous columns
+                OPJ_INT32 i;
+                for (i = 0; i < width; i += 8, mbr++, sig++) {
+                    OPJ_UINT32 t, z;
+
+                    mbr[0] = sig[0];         //start with significant samples
+                    mbr[0] |= prev >> 28;    //for first column, left neighbors
+                    mbr[0] |= sig[0] << 4;   //left neighbors
+                    mbr[0] |= sig[0] >> 4;   //right neighbors
+                    mbr[0] |= sig[1] << 28;  //for last column, right neighbors
+                    prev = sig[0];           // for next group of columns
+
+                    //integrate vertically
+                    t = mbr[0], z = mbr[0];
+                    z |= (t & 0x77777777) << 1; //above neighbors
+                    z |= (t & 0xEEEEEEEE) >> 1; //below neighbors
+                    mbr[0] = z & ~sig[0]; //remove already significance samples
+                }
+            }
+
+            if (y >= 8) { //wait until 8 rows has been processed
+                OPJ_UINT32 *cur_sig, *cur_mbr, *nxt_sig, *nxt_mbr;
+                OPJ_UINT32 prev;
+                OPJ_UINT32 val;
+                OPJ_INT32 i;
+
+                // add membership from the next stripe, obtained above
+                cur_sig = y & 0x4 ? sigma2 : sigma1;
+                cur_mbr = y & 0x4 ? mbr2 : mbr1;
+                nxt_sig = y & 0x4 ? sigma1 : sigma2;  //future samples
+                prev = 0; // the columns before these group of 8 columns
+                for (i = 0; i < width; i += 8, cur_mbr++, cur_sig++, nxt_sig++) {
+                    OPJ_UINT32 t = nxt_sig[0];
+                    t |= prev >> 28;        //for first column, left neighbors
+                    t |= nxt_sig[0] << 4;   //left neighbors
+                    t |= nxt_sig[0] >> 4;   //right neighbors
+                    t |= nxt_sig[1] << 28;  //for last column, right neighbors
+                    prev = nxt_sig[0];      // for next group of columns
+
+                    if (!stripe_causal) {
+                        cur_mbr[0] |= (t & 0x11111111u) << 3; //propagate up to cur_mbr
+                    }
+                    cur_mbr[0] &= ~cur_sig[0]; //remove already significance samples
+                }
+
+                //find new locations and get signs
+                cur_sig = y & 0x4 ? sigma2 : sigma1;
+                cur_mbr = y & 0x4 ? mbr2 : mbr1;
+                nxt_sig = y & 0x4 ? sigma1 : sigma2; //future samples
+                nxt_mbr = y & 0x4 ? mbr1 : mbr2;     //future samples
+                val = 3u << (p - 2); // sample values for newly discovered
+                // significant samples including the bin center
+                for (i = 0; i < width;
+                        i += 8, cur_sig++, cur_mbr++, nxt_sig++, nxt_mbr++) {
+                    OPJ_UINT32 ux, tx;
+                    OPJ_UINT32 mbr = *cur_mbr;
+                    OPJ_UINT32 new_sig = 0;
+                    if (mbr) { //are there any samples that might be significant
+                        OPJ_INT32 n;
+                        for (n = 0; n < 8; n += 4) {
+                            OPJ_UINT32 col_mask;
+                            OPJ_UINT32 inv_sig;
+                            OPJ_INT32 end;
+                            OPJ_INT32 j;
+
+                            OPJ_UINT32 cwd = frwd_fetch(&sigprop); //get 32 bits
+                            OPJ_UINT32 cnt = 0;
+
+                            OPJ_UINT32 *dp = decoded_data + (y - 8) * stride;
+                            dp += i + n; //address for decoded samples
+
+                            col_mask = 0xFu << (4 * n); //a mask to select a column
+
+                            inv_sig = ~cur_sig[0]; // insignificant samples
+
+                            //find the last sample we operate on
+                            end = n + 4 + i < width ? n + 4 : width - i;
+
+                            for (j = n; j < end; ++j, ++dp, col_mask <<= 4) {
+                                OPJ_UINT32 sample_mask;
+
+                                if ((col_mask & mbr) == 0) { //no samples need checking
+                                    continue;
+                                }
+
+                                //scan mbr to find a new significant sample
+                                sample_mask = 0x11111111u & col_mask; // LSB
+                                if (mbr & sample_mask) {
+                                    assert(dp[0] == 0); // the sample must have been 0
+                                    if (cwd & 1) { //if this sample has become significant
+                                        // must propagate it to nearby samples
+                                        OPJ_UINT32 t;
+                                        new_sig |= sample_mask;  // new significant samples
+                                        t = 0x32u << (j * 4);// propagation to neighbors
+                                        mbr |= t & inv_sig; //remove already significant samples
+                                    }
+                                    cwd >>= 1;
+                                    ++cnt; //consume bit and increment number of
+                                    //consumed bits
+                                }
+
+                                sample_mask += sample_mask;  // next row
+                                if (mbr & sample_mask) {
+                                    assert(dp[stride] == 0);
+                                    if (cwd & 1) {
+                                        OPJ_UINT32 t;
+                                        new_sig |= sample_mask;
+                                        t = 0x74u << (j * 4);
+                                        mbr |= t & inv_sig;
+                                    }
+                                    cwd >>= 1;
+                                    ++cnt;
+                                }
+
+                                sample_mask += sample_mask;
+                                if (mbr & sample_mask) {
+                                    assert(dp[2 * stride] == 0);
+                                    if (cwd & 1) {
+                                        OPJ_UINT32 t;
+                                        new_sig |= sample_mask;
+                                        t = 0xE8u << (j * 4);
+                                        mbr |= t & inv_sig;
+                                    }
+                                    cwd >>= 1;
+                                    ++cnt;
+                                }
+
+                                sample_mask += sample_mask;
+                                if (mbr & sample_mask) {
+                                    assert(dp[3 * stride] == 0);
+                                    if (cwd & 1) {
+                                        OPJ_UINT32 t;
+                                        new_sig |= sample_mask;
+                                        t = 0xC0u << (j * 4);
+                                        mbr |= t & inv_sig;
+                                    }
+                                    cwd >>= 1;
+                                    ++cnt;
+                                }
+                            }
+
+                            //obtain signs here
+                            if (new_sig & (0xFFFFu << (4 * n))) { //if any
+                                OPJ_UINT32 col_mask;
+                                OPJ_INT32 j;
+                                OPJ_UINT32 *dp = decoded_data + (y - 8) * stride;
+                                dp += i + n; // decoded samples address
+                                col_mask = 0xFu << (4 * n); //mask to select a column
+
+                                for (j = n; j < end; ++j, ++dp, col_mask <<= 4) {
+                                    OPJ_UINT32 sample_mask;
+
+                                    if ((col_mask & new_sig) == 0) { //if non is significant
+                                        continue;
+                                    }
+
+                                    //scan 4 signs
+                                    sample_mask = 0x11111111u & col_mask;
+                                    if (new_sig & sample_mask) {
+                                        assert(dp[0] == 0);
+                                        dp[0] |= ((cwd & 1) << 31) | val; //put value and sign
+                                        cwd >>= 1;
+                                        ++cnt; //consume bit and increment number
+                                        //of consumed bits
+                                    }
+
+                                    sample_mask += sample_mask;
+                                    if (new_sig & sample_mask) {
+                                        assert(dp[stride] == 0);
+                                        dp[stride] |= ((cwd & 1) << 31) | val;
+                                        cwd >>= 1;
+                                        ++cnt;
+                                    }
+
+                                    sample_mask += sample_mask;
+                                    if (new_sig & sample_mask) {
+                                        assert(dp[2 * stride] == 0);
+                                        dp[2 * stride] |= ((cwd & 1) << 31) | val;
+                                        cwd >>= 1;
+                                        ++cnt;
+                                    }
+
+                                    sample_mask += sample_mask;
+                                    if (new_sig & sample_mask) {
+                                        assert(dp[3 * stride] == 0);
+                                        dp[3 * stride] |= ((cwd & 1) << 31) | val;
+                                        cwd >>= 1;
+                                        ++cnt;
+                                    }
+                                }
+
+                            }
+                            frwd_advance(&sigprop, cnt); //consume the bits from bitstrm
+                            cnt = 0;
+
+                            //update the next 8 columns
+                            if (n == 4) {
+                                //horizontally
+                                OPJ_UINT32 t = new_sig >> 28;
+                                t |= ((t & 0xE) >> 1) | ((t & 7) << 1);
+                                cur_mbr[1] |= t & ~cur_sig[1];
+                            }
+                        }
+                    }
+                    //update the next stripe (vertically propagation)
+                    new_sig |= cur_sig[0];
+                    ux = (new_sig & 0x88888888) >> 3;
+                    tx = ux | (ux << 4) | (ux >> 4); //left and right neighbors
+                    if (i > 0) {
+                        nxt_mbr[-1] |= (ux << 28) & ~nxt_sig[-1];
+                    }
+                    nxt_mbr[0] |= tx & ~nxt_sig[0];
+                    nxt_mbr[1] |= (ux >> 28) & ~nxt_sig[1];
+                }
+
+                //clear current sigma
+                //mbr need not be cleared because it is overwritten
+                cur_sig = y & 0x4 ? sigma2 : sigma1;
+                memset(cur_sig, 0, ((((OPJ_UINT32)width + 7u) >> 3) + 1u) << 2);
+            }
+        }
+    }
+
+    //terminating
+    if (num_passes > 1) {
+        OPJ_INT32 st, y;
+
+        if (num_passes > 2 && ((height & 3) == 1 || (height & 3) == 2)) {
+            //do magref
+            OPJ_UINT32 *cur_sig = height & 0x4 ? sigma2 : sigma1; //reversed
+            OPJ_UINT32 *dpp = decoded_data + (height & 0xFFFFFC) * stride;
+            OPJ_UINT32 half = 1u << (p - 2);
+            OPJ_INT32 i;
+            for (i = 0; i < width; i += 8) {
+                OPJ_UINT32 cwd = rev_fetch_mrp(&magref);
+                OPJ_UINT32 sig = *cur_sig++;
+                OPJ_UINT32 col_mask = 0xF;
+                OPJ_UINT32 *dp = dpp + i;
+                if (sig) {
+                    int j;
+                    for (j = 0; j < 8; ++j, dp++) {
+                        if (sig & col_mask) {
+                            OPJ_UINT32 sample_mask = 0x11111111 & col_mask;
+
+                            if (sig & sample_mask) {
+                                OPJ_UINT32 sym;
+                                assert(dp[0] != 0);
+                                sym = cwd & 1;
+                                dp[0] ^= (1 - sym) << (p - 1);
+                                dp[0] |= half;
+                                cwd >>= 1;
+                            }
+                            sample_mask += sample_mask;
+
+                            if (sig & sample_mask) {
+                                OPJ_UINT32 sym;
+                                assert(dp[stride] != 0);
+                                sym = cwd & 1;
+                                dp[stride] ^= (1 - sym) << (p - 1);
+                                dp[stride] |= half;
+                                cwd >>= 1;
+                            }
+                            sample_mask += sample_mask;
+
+                            if (sig & sample_mask) {
+                                OPJ_UINT32 sym;
+                                assert(dp[2 * stride] != 0);
+                                sym = cwd & 1;
+                                dp[2 * stride] ^= (1 - sym) << (p - 1);
+                                dp[2 * stride] |= half;
+                                cwd >>= 1;
+                            }
+                            sample_mask += sample_mask;
+
+                            if (sig & sample_mask) {
+                                OPJ_UINT32 sym;
+                                assert(dp[3 * stride] != 0);
+                                sym = cwd & 1;
+                                dp[3 * stride] ^= (1 - sym) << (p - 1);
+                                dp[3 * stride] |= half;
+                                cwd >>= 1;
+                            }
+                            sample_mask += sample_mask;
+                        }
+                        col_mask <<= 4;
+                    }
+                }
+                rev_advance_mrp(&magref, population_count(sig));
+            }
+        }
+
+        //do the last incomplete stripe
+        // for cases of (height & 3) == 0 and 3
+        // the should have been processed previously
+        if ((height & 3) == 1 || (height & 3) == 2) {
+            //generate mbr of first stripe
+            OPJ_UINT32 *sig = height & 0x4 ? sigma2 : sigma1;
+            OPJ_UINT32 *mbr = height & 0x4 ? mbr2 : mbr1;
+            //integrate horizontally
+            OPJ_UINT32 prev = 0;
+            OPJ_INT32 i;
+            for (i = 0; i < width; i += 8, mbr++, sig++) {
+                OPJ_UINT32 t, z;
+
+                mbr[0] = sig[0];
+                mbr[0] |= prev >> 28;    //for first column, left neighbors
+                mbr[0] |= sig[0] << 4;   //left neighbors
+                mbr[0] |= sig[0] >> 4;   //left neighbors
+                mbr[0] |= sig[1] << 28;  //for last column, right neighbors
+                prev = sig[0];
+
+                //integrate vertically
+                t = mbr[0], z = mbr[0];
+                z |= (t & 0x77777777) << 1; //above neighbors
+                z |= (t & 0xEEEEEEEE) >> 1; //below neighbors
+                mbr[0] = z & ~sig[0]; //remove already significance samples
+            }
+        }
+
+        st = height;
+        st -= height > 6 ? (((height + 1) & 3) + 3) : height;
+        for (y = st; y < height; y += 4) {
+            OPJ_UINT32 *cur_sig, *cur_mbr, *nxt_sig, *nxt_mbr;
+            OPJ_UINT32 val;
+            OPJ_INT32 i;
+
+            OPJ_UINT32 pattern = 0xFFFFFFFFu; // a pattern needed samples
+            if (height - y == 3) {
+                pattern = 0x77777777u;
+            } else if (height - y == 2) {
+                pattern = 0x33333333u;
+            } else if (height - y == 1) {
+                pattern = 0x11111111u;
+            }
+
+            //add membership from the next stripe, obtained above
+            if (height - y > 4) {
+                OPJ_UINT32 prev = 0;
+                OPJ_INT32 i;
+                cur_sig = y & 0x4 ? sigma2 : sigma1;
+                cur_mbr = y & 0x4 ? mbr2 : mbr1;
+                nxt_sig = y & 0x4 ? sigma1 : sigma2;
+                for (i = 0; i < width; i += 8, cur_mbr++, cur_sig++, nxt_sig++) {
+                    OPJ_UINT32 t = nxt_sig[0];
+                    t |= prev >> 28;     //for first column, left neighbors
+                    t |= nxt_sig[0] << 4;   //left neighbors
+                    t |= nxt_sig[0] >> 4;   //left neighbors
+                    t |= nxt_sig[1] << 28;  //for last column, right neighbors
+                    prev = nxt_sig[0];
+
+                    if (!stripe_causal) {
+                        cur_mbr[0] |= (t & 0x11111111u) << 3;
+                    }
+                    //remove already significance samples
+                    cur_mbr[0] &= ~cur_sig[0];
+                }
+            }
+
+            //find new locations and get signs
+            cur_sig = y & 0x4 ? sigma2 : sigma1;
+            cur_mbr = y & 0x4 ? mbr2 : mbr1;
+            nxt_sig = y & 0x4 ? sigma1 : sigma2;
+            nxt_mbr = y & 0x4 ? mbr1 : mbr2;
+            val = 3u << (p - 2);
+            for (i = 0; i < width; i += 8,
+                    cur_sig++, cur_mbr++, nxt_sig++, nxt_mbr++) {
+                OPJ_UINT32 mbr = *cur_mbr & pattern; //skip unneeded samples
+                OPJ_UINT32 new_sig = 0;
+                OPJ_UINT32 ux, tx;
+                if (mbr) {
+                    OPJ_INT32 n;
+                    for (n = 0; n < 8; n += 4) {
+                        OPJ_UINT32 col_mask;
+                        OPJ_UINT32 inv_sig;
+                        OPJ_INT32 end;
+                        OPJ_INT32 j;
+
+                        OPJ_UINT32 cwd = frwd_fetch(&sigprop);
+                        OPJ_UINT32 cnt = 0;
+
+                        OPJ_UINT32 *dp = decoded_data + y * stride;
+                        dp += i + n;
+
+                        col_mask = 0xFu << (4 * n);
+
+                        inv_sig = ~cur_sig[0] & pattern;
+
+                        end = n + 4 + i < width ? n + 4 : width - i;
+                        for (j = n; j < end; ++j, ++dp, col_mask <<= 4) {
+                            OPJ_UINT32 sample_mask;
+
+                            if ((col_mask & mbr) == 0) {
+                                continue;
+                            }
+
+                            //scan 4 mbr
+                            sample_mask = 0x11111111u & col_mask;
+                            if (mbr & sample_mask) {
+                                assert(dp[0] == 0);
+                                if (cwd & 1) {
+                                    OPJ_UINT32 t;
+                                    new_sig |= sample_mask;
+                                    t = 0x32u << (j * 4);
+                                    mbr |= t & inv_sig;
+                                }
+                                cwd >>= 1;
+                                ++cnt;
+                            }
+
+                            sample_mask += sample_mask;
+                            if (mbr & sample_mask) {
+                                assert(dp[stride] == 0);
+                                if (cwd & 1) {
+                                    OPJ_UINT32 t;
+                                    new_sig |= sample_mask;
+                                    t = 0x74u << (j * 4);
+                                    mbr |= t & inv_sig;
+                                }
+                                cwd >>= 1;
+                                ++cnt;
+                            }
+
+                            sample_mask += sample_mask;
+                            if (mbr & sample_mask) {
+                                assert(dp[2 * stride] == 0);
+                                if (cwd & 1) {
+                                    OPJ_UINT32 t;
+                                    new_sig |= sample_mask;
+                                    t = 0xE8u << (j * 4);
+                                    mbr |= t & inv_sig;
+                                }
+                                cwd >>= 1;
+                                ++cnt;
+                            }
+
+                            sample_mask += sample_mask;
+                            if (mbr & sample_mask) {
+                                assert(dp[3 * stride] == 0);
+                                if (cwd & 1) {
+                                    OPJ_UINT32 t;
+                                    new_sig |= sample_mask;
+                                    t = 0xC0u << (j * 4);
+                                    mbr |= t & inv_sig;
+                                }
+                                cwd >>= 1;
+                                ++cnt;
+                            }
+                        }
+
+                        //signs here
+                        if (new_sig & (0xFFFFu << (4 * n))) {
+                            OPJ_UINT32 col_mask;
+                            OPJ_INT32 j;
+                            OPJ_UINT32 *dp = decoded_data + y * stride;
+                            dp += i + n;
+                            col_mask = 0xFu << (4 * n);
+
+                            for (j = n; j < end; ++j, ++dp, col_mask <<= 4) {
+                                OPJ_UINT32 sample_mask;
+                                if ((col_mask & new_sig) == 0) {
+                                    continue;
+                                }
+
+                                //scan 4 signs
+                                sample_mask = 0x11111111u & col_mask;
+                                if (new_sig & sample_mask) {
+                                    assert(dp[0] == 0);
+                                    dp[0] |= ((cwd & 1) << 31) | val;
+                                    cwd >>= 1;
+                                    ++cnt;
+                                }
+
+                                sample_mask += sample_mask;
+                                if (new_sig & sample_mask) {
+                                    assert(dp[stride] == 0);
+                                    dp[stride] |= ((cwd & 1) << 31) | val;
+                                    cwd >>= 1;
+                                    ++cnt;
+                                }
+
+                                sample_mask += sample_mask;
+                                if (new_sig & sample_mask) {
+                                    assert(dp[2 * stride] == 0);
+                                    dp[2 * stride] |= ((cwd & 1) << 31) | val;
+                                    cwd >>= 1;
+                                    ++cnt;
+                                }
+
+                                sample_mask += sample_mask;
+                                if (new_sig & sample_mask) {
+                                    assert(dp[3 * stride] == 0);
+                                    dp[3 * stride] |= ((cwd & 1) << 31) | val;
+                                    cwd >>= 1;
+                                    ++cnt;
+                                }
+                            }
+
+                        }
+                        frwd_advance(&sigprop, cnt);
+                        cnt = 0;
+
+                        //update next columns
+                        if (n == 4) {
+                            //horizontally
+                            OPJ_UINT32 t = new_sig >> 28;
+                            t |= ((t & 0xE) >> 1) | ((t & 7) << 1);
+                            cur_mbr[1] |= t & ~cur_sig[1];
+                        }
+                    }
+                }
+                //propagate down (vertically propagation)
+                new_sig |= cur_sig[0];
+                ux = (new_sig & 0x88888888) >> 3;
+                tx = ux | (ux << 4) | (ux >> 4);
+                if (i > 0) {
+                    nxt_mbr[-1] |= (ux << 28) & ~nxt_sig[-1];
+                }
+                nxt_mbr[0] |= tx & ~nxt_sig[0];
+                nxt_mbr[1] |= (ux >> 28) & ~nxt_sig[1];
+            }
+        }
+    }
+
+    {
+        OPJ_INT32 x, y;
+        for (y = 0; y < height; ++y) {
+            OPJ_INT32* sp = (OPJ_INT32*)decoded_data + y * stride;
+            for (x = 0; x < width; ++x, ++sp) {
+                OPJ_INT32 val = (*sp & 0x7FFFFFFF);
+                *sp = ((OPJ_UINT32) * sp & 0x80000000) ? -val : val;
+            }
+        }
+    }
+
+    return OPJ_TRUE;
+}
diff --git a/third_party/libopenjpeg/image.c b/third_party/libopenjpeg/image.c
new file mode 100644
index 0000000..017201a
--- /dev/null
+++ b/third_party/libopenjpeg/image.c
@@ -0,0 +1,263 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "opj_includes.h"
+
+opj_image_t* opj_image_create0(void)
+{
+    opj_image_t *image = (opj_image_t*)opj_calloc(1, sizeof(opj_image_t));
+    return image;
+}
+
+opj_image_t* OPJ_CALLCONV opj_image_create(OPJ_UINT32 numcmpts,
+        opj_image_cmptparm_t *cmptparms, OPJ_COLOR_SPACE clrspc)
+{
+    OPJ_UINT32 compno;
+    opj_image_t *image = NULL;
+
+    image = (opj_image_t*) opj_calloc(1, sizeof(opj_image_t));
+    if (image) {
+        image->color_space = clrspc;
+        image->numcomps = numcmpts;
+        /* allocate memory for the per-component information */
+        image->comps = (opj_image_comp_t*)opj_calloc(image->numcomps,
+                       sizeof(opj_image_comp_t));
+        if (!image->comps) {
+            /* TODO replace with event manager, breaks API */
+            /* fprintf(stderr,"Unable to allocate memory for image.\n"); */
+            opj_image_destroy(image);
+            return NULL;
+        }
+        /* create the individual image components */
+        for (compno = 0; compno < numcmpts; compno++) {
+            opj_image_comp_t *comp = &image->comps[compno];
+            comp->dx = cmptparms[compno].dx;
+            comp->dy = cmptparms[compno].dy;
+            comp->w = cmptparms[compno].w;
+            comp->h = cmptparms[compno].h;
+            comp->x0 = cmptparms[compno].x0;
+            comp->y0 = cmptparms[compno].y0;
+            comp->prec = cmptparms[compno].prec;
+            comp->sgnd = cmptparms[compno].sgnd;
+            if (comp->h != 0 &&
+                    (OPJ_SIZE_T)comp->w > SIZE_MAX / comp->h / sizeof(OPJ_INT32)) {
+                /* TODO event manager */
+                opj_image_destroy(image);
+                return NULL;
+            }
+            comp->data = (OPJ_INT32*) opj_image_data_alloc(
+                             (size_t)comp->w * comp->h * sizeof(OPJ_INT32));
+            if (!comp->data) {
+                /* TODO replace with event manager, breaks API */
+                /* fprintf(stderr,"Unable to allocate memory for image.\n"); */
+                opj_image_destroy(image);
+                return NULL;
+            }
+            memset(comp->data, 0, (size_t)comp->w * comp->h * sizeof(OPJ_INT32));
+        }
+    }
+
+    return image;
+}
+
+void OPJ_CALLCONV opj_image_destroy(opj_image_t *image)
+{
+    if (image) {
+        if (image->comps) {
+            OPJ_UINT32 compno;
+
+            /* image components */
+            for (compno = 0; compno < image->numcomps; compno++) {
+                opj_image_comp_t *image_comp = &(image->comps[compno]);
+                if (image_comp->data) {
+                    opj_image_data_free(image_comp->data);
+                }
+            }
+            opj_free(image->comps);
+        }
+
+        if (image->icc_profile_buf) {
+            opj_free(image->icc_profile_buf);
+        }
+
+        opj_free(image);
+    }
+}
+
+/**
+ * Updates the components characteristics of the image from the coding parameters.
+ *
+ * @param p_image_header    the image header to update.
+ * @param p_cp              the coding parameters from which to update the image.
+ */
+void opj_image_comp_header_update(opj_image_t * p_image_header,
+                                  const struct opj_cp * p_cp)
+{
+    OPJ_UINT32 i, l_width, l_height;
+    OPJ_UINT32 l_x0, l_y0, l_x1, l_y1;
+    OPJ_UINT32 l_comp_x0, l_comp_y0, l_comp_x1, l_comp_y1;
+    opj_image_comp_t* l_img_comp = NULL;
+
+    l_x0 = opj_uint_max(p_cp->tx0, p_image_header->x0);
+    l_y0 = opj_uint_max(p_cp->ty0, p_image_header->y0);
+    l_x1 = p_cp->tx0 + (p_cp->tw - 1U) *
+           p_cp->tdx; /* validity of p_cp members used here checked in opj_j2k_read_siz. Can't overflow. */
+    l_y1 = p_cp->ty0 + (p_cp->th - 1U) * p_cp->tdy; /* can't overflow */
+    l_x1 = opj_uint_min(opj_uint_adds(l_x1, p_cp->tdx),
+                        p_image_header->x1); /* use add saturated to prevent overflow */
+    l_y1 = opj_uint_min(opj_uint_adds(l_y1, p_cp->tdy),
+                        p_image_header->y1); /* use add saturated to prevent overflow */
+
+    l_img_comp = p_image_header->comps;
+    for (i = 0; i < p_image_header->numcomps; ++i) {
+        l_comp_x0 = opj_uint_ceildiv(l_x0, l_img_comp->dx);
+        l_comp_y0 = opj_uint_ceildiv(l_y0, l_img_comp->dy);
+        l_comp_x1 = opj_uint_ceildiv(l_x1, l_img_comp->dx);
+        l_comp_y1 = opj_uint_ceildiv(l_y1, l_img_comp->dy);
+        l_width   = opj_uint_ceildivpow2(l_comp_x1 - l_comp_x0, l_img_comp->factor);
+        l_height  = opj_uint_ceildivpow2(l_comp_y1 - l_comp_y0, l_img_comp->factor);
+        l_img_comp->w = l_width;
+        l_img_comp->h = l_height;
+        l_img_comp->x0 = l_comp_x0;
+        l_img_comp->y0 = l_comp_y0;
+        ++l_img_comp;
+    }
+}
+
+
+/**
+ * Copy only header of image and its component header (no data are copied)
+ * if dest image have data, they will be freed
+ *
+ * @param   p_image_src     the src image
+ * @param   p_image_dest    the dest image
+ *
+ */
+void opj_copy_image_header(const opj_image_t* p_image_src,
+                           opj_image_t* p_image_dest)
+{
+    OPJ_UINT32 compno;
+
+    /* preconditions */
+    assert(p_image_src != 00);
+    assert(p_image_dest != 00);
+
+    p_image_dest->x0 = p_image_src->x0;
+    p_image_dest->y0 = p_image_src->y0;
+    p_image_dest->x1 = p_image_src->x1;
+    p_image_dest->y1 = p_image_src->y1;
+
+    if (p_image_dest->comps) {
+        for (compno = 0; compno < p_image_dest->numcomps; compno++) {
+            opj_image_comp_t *image_comp = &(p_image_dest->comps[compno]);
+            if (image_comp->data) {
+                opj_image_data_free(image_comp->data);
+            }
+        }
+        opj_free(p_image_dest->comps);
+        p_image_dest->comps = NULL;
+    }
+
+    p_image_dest->numcomps = p_image_src->numcomps;
+
+    p_image_dest->comps = (opj_image_comp_t*) opj_malloc(p_image_dest->numcomps *
+                          sizeof(opj_image_comp_t));
+    if (!p_image_dest->comps) {
+        p_image_dest->comps = NULL;
+        p_image_dest->numcomps = 0;
+        return;
+    }
+
+    for (compno = 0; compno < p_image_dest->numcomps; compno++) {
+        memcpy(&(p_image_dest->comps[compno]),
+               &(p_image_src->comps[compno]),
+               sizeof(opj_image_comp_t));
+        p_image_dest->comps[compno].data = NULL;
+    }
+
+    p_image_dest->color_space = p_image_src->color_space;
+    p_image_dest->icc_profile_len = p_image_src->icc_profile_len;
+
+    if (p_image_dest->icc_profile_len) {
+        p_image_dest->icc_profile_buf = (OPJ_BYTE*)opj_malloc(
+                                            p_image_dest->icc_profile_len);
+        if (!p_image_dest->icc_profile_buf) {
+            p_image_dest->icc_profile_buf = NULL;
+            p_image_dest->icc_profile_len = 0;
+            return;
+        }
+        memcpy(p_image_dest->icc_profile_buf,
+               p_image_src->icc_profile_buf,
+               p_image_src->icc_profile_len);
+    } else {
+        p_image_dest->icc_profile_buf = NULL;
+    }
+
+    return;
+}
+
+opj_image_t* OPJ_CALLCONV opj_image_tile_create(OPJ_UINT32 numcmpts,
+        opj_image_cmptparm_t *cmptparms, OPJ_COLOR_SPACE clrspc)
+{
+    OPJ_UINT32 compno;
+    opj_image_t *image = 00;
+
+    image = (opj_image_t*) opj_calloc(1, sizeof(opj_image_t));
+    if (image) {
+
+        image->color_space = clrspc;
+        image->numcomps = numcmpts;
+
+        /* allocate memory for the per-component information */
+        image->comps = (opj_image_comp_t*)opj_calloc(image->numcomps,
+                       sizeof(opj_image_comp_t));
+        if (!image->comps) {
+            opj_image_destroy(image);
+            return 00;
+        }
+
+        /* create the individual image components */
+        for (compno = 0; compno < numcmpts; compno++) {
+            opj_image_comp_t *comp = &image->comps[compno];
+            comp->dx = cmptparms[compno].dx;
+            comp->dy = cmptparms[compno].dy;
+            comp->w = cmptparms[compno].w;
+            comp->h = cmptparms[compno].h;
+            comp->x0 = cmptparms[compno].x0;
+            comp->y0 = cmptparms[compno].y0;
+            comp->prec = cmptparms[compno].prec;
+            comp->sgnd = cmptparms[compno].sgnd;
+            comp->data = 0;
+        }
+    }
+
+    return image;
+}
diff --git a/third_party/libopenjpeg20/image.h b/third_party/libopenjpeg/image.h
similarity index 100%
rename from third_party/libopenjpeg20/image.h
rename to third_party/libopenjpeg/image.h
diff --git a/third_party/libopenjpeg20/invert.c b/third_party/libopenjpeg/invert.c
similarity index 100%
rename from third_party/libopenjpeg20/invert.c
rename to third_party/libopenjpeg/invert.c
diff --git a/third_party/libopenjpeg20/invert.h b/third_party/libopenjpeg/invert.h
similarity index 100%
rename from third_party/libopenjpeg20/invert.h
rename to third_party/libopenjpeg/invert.h
diff --git a/third_party/libopenjpeg/j2k.c b/third_party/libopenjpeg/j2k.c
new file mode 100644
index 0000000..e2e0487
--- /dev/null
+++ b/third_party/libopenjpeg/j2k.c
@@ -0,0 +1,13215 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2008, Jerome Fimes, Communications & Systemes <jerome.fimes@c-s.fr>
+ * Copyright (c) 2006-2007, Parvatha Elangovan
+ * Copyright (c) 2010-2011, Kaori Hagihara
+ * Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France
+ * Copyright (c) 2012, CS Systemes d'Information, France
+ * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "opj_includes.h"
+
+/** @defgroup J2K J2K - JPEG-2000 codestream reader/writer */
+/*@{*/
+
+/** @name Local static functions */
+/*@{*/
+
+/**
+ * Sets up the procedures to do on reading header. Developers wanting to extend the library can add their own reading procedures.
+ */
+static OPJ_BOOL opj_j2k_setup_header_reading(opj_j2k_t *p_j2k,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * The read header procedure.
+ */
+static OPJ_BOOL opj_j2k_read_header_procedure(opj_j2k_t *p_j2k,
+        opj_stream_private_t *p_stream,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * The default encoding validation procedure without any extension.
+ *
+ * @param       p_j2k                   the jpeg2000 codec to validate.
+ * @param       p_stream                the input stream to validate.
+ * @param       p_manager               the user event manager.
+ *
+ * @return true if the parameters are correct.
+ */
+static OPJ_BOOL opj_j2k_encoding_validation(opj_j2k_t * p_j2k,
+        opj_stream_private_t *p_stream,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * The default decoding validation procedure without any extension.
+ *
+ * @param       p_j2k                   the jpeg2000 codec to validate.
+ * @param       p_stream                                the input stream to validate.
+ * @param       p_manager               the user event manager.
+ *
+ * @return true if the parameters are correct.
+ */
+static OPJ_BOOL opj_j2k_decoding_validation(opj_j2k_t * p_j2k,
+        opj_stream_private_t *p_stream,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Sets up the validation ,i.e. adds the procedures to launch to make sure the codec parameters
+ * are valid. Developers wanting to extend the library can add their own validation procedures.
+ */
+static OPJ_BOOL opj_j2k_setup_encoding_validation(opj_j2k_t *p_j2k,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Sets up the validation ,i.e. adds the procedures to launch to make sure the codec parameters
+ * are valid. Developers wanting to extend the library can add their own validation procedures.
+ */
+static OPJ_BOOL opj_j2k_setup_decoding_validation(opj_j2k_t *p_j2k,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Sets up the validation ,i.e. adds the procedures to launch to make sure the codec parameters
+ * are valid. Developers wanting to extend the library can add their own validation procedures.
+ */
+static OPJ_BOOL opj_j2k_setup_end_compress(opj_j2k_t *p_j2k,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * The mct encoding validation procedure.
+ *
+ * @param       p_j2k                   the jpeg2000 codec to validate.
+ * @param       p_stream                                the input stream to validate.
+ * @param       p_manager               the user event manager.
+ *
+ * @return true if the parameters are correct.
+ */
+static OPJ_BOOL opj_j2k_mct_validation(opj_j2k_t * p_j2k,
+                                       opj_stream_private_t *p_stream,
+                                       opj_event_mgr_t * p_manager);
+
+/**
+ * Builds the tcd decoder to use to decode tile.
+ */
+static OPJ_BOOL opj_j2k_build_decoder(opj_j2k_t * p_j2k,
+                                      opj_stream_private_t *p_stream,
+                                      opj_event_mgr_t * p_manager);
+/**
+ * Builds the tcd encoder to use to encode tile.
+ */
+static OPJ_BOOL opj_j2k_build_encoder(opj_j2k_t * p_j2k,
+                                      opj_stream_private_t *p_stream,
+                                      opj_event_mgr_t * p_manager);
+
+/**
+ * Creates a tile-coder encoder.
+ *
+ * @param       p_stream                        the stream to write data to.
+ * @param       p_j2k                           J2K codec.
+ * @param       p_manager                   the user event manager.
+*/
+static OPJ_BOOL opj_j2k_create_tcd(opj_j2k_t *p_j2k,
+                                   opj_stream_private_t *p_stream,
+                                   opj_event_mgr_t * p_manager);
+
+/**
+ * Executes the given procedures on the given codec.
+ *
+ * @param       p_procedure_list        the list of procedures to execute
+ * @param       p_j2k                           the jpeg2000 codec to execute the procedures on.
+ * @param       p_stream                        the stream to execute the procedures on.
+ * @param       p_manager                       the user manager.
+ *
+ * @return      true                            if all the procedures were successfully executed.
+ */
+static OPJ_BOOL opj_j2k_exec(opj_j2k_t * p_j2k,
+                             opj_procedure_list_t * p_procedure_list,
+                             opj_stream_private_t *p_stream,
+                             opj_event_mgr_t * p_manager);
+
+/**
+ * Updates the rates of the tcp.
+ *
+ * @param       p_stream                                the stream to write data to.
+ * @param       p_j2k                           J2K codec.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_update_rates(opj_j2k_t *p_j2k,
+                                     opj_stream_private_t *p_stream,
+                                     opj_event_mgr_t * p_manager);
+
+/**
+ * Copies the decoding tile parameters onto all the tile parameters.
+ * Creates also the tile decoder.
+ */
+static OPJ_BOOL opj_j2k_copy_default_tcp_and_create_tcd(opj_j2k_t * p_j2k,
+        opj_stream_private_t *p_stream,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Destroys the memory associated with the decoding of headers.
+ */
+static OPJ_BOOL opj_j2k_destroy_header_memory(opj_j2k_t * p_j2k,
+        opj_stream_private_t *p_stream,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Reads the lookup table containing all the marker, status and action, and returns the handler associated
+ * with the marker value.
+ * @param       p_id            Marker value to look up
+ *
+ * @return      the handler associated with the id.
+*/
+static const struct opj_dec_memory_marker_handler * opj_j2k_get_marker_handler(
+    OPJ_UINT32 p_id);
+
+/**
+ * Destroys a tile coding parameter structure.
+ *
+ * @param       p_tcp           the tile coding parameter to destroy.
+ */
+static void opj_j2k_tcp_destroy(opj_tcp_t *p_tcp);
+
+/**
+ * Destroys the data inside a tile coding parameter structure.
+ *
+ * @param       p_tcp           the tile coding parameter which contain data to destroy.
+ */
+static void opj_j2k_tcp_data_destroy(opj_tcp_t *p_tcp);
+
+/**
+ * Destroys a coding parameter structure.
+ *
+ * @param       p_cp            the coding parameter to destroy.
+ */
+static void opj_j2k_cp_destroy(opj_cp_t *p_cp);
+
+/**
+ * Compare 2 a SPCod/ SPCoc elements, i.e. the coding style of a given component of a tile.
+ *
+ * @param       p_j2k            J2K codec.
+ * @param       p_tile_no        Tile number
+ * @param       p_first_comp_no  The 1st component number to compare.
+ * @param       p_second_comp_no The 1st component number to compare.
+ *
+ * @return OPJ_TRUE if SPCdod are equals.
+ */
+static OPJ_BOOL opj_j2k_compare_SPCod_SPCoc(opj_j2k_t *p_j2k,
+        OPJ_UINT32 p_tile_no, OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no);
+
+/**
+ * Writes a SPCod or SPCoc element, i.e. the coding style of a given component of a tile.
+ *
+ * @param       p_j2k           J2K codec.
+ * @param       p_tile_no       FIXME DOC
+ * @param       p_comp_no       the component number to output.
+ * @param       p_data          FIXME DOC
+ * @param       p_header_size   FIXME DOC
+ * @param       p_manager       the user event manager.
+ *
+ * @return FIXME DOC
+*/
+static OPJ_BOOL opj_j2k_write_SPCod_SPCoc(opj_j2k_t *p_j2k,
+        OPJ_UINT32 p_tile_no,
+        OPJ_UINT32 p_comp_no,
+        OPJ_BYTE * p_data,
+        OPJ_UINT32 * p_header_size,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Gets the size taken by writing a SPCod or SPCoc for the given tile and component.
+ *
+ * @param       p_j2k                   the J2K codec.
+ * @param       p_tile_no               the tile index.
+ * @param       p_comp_no               the component being outputted.
+ *
+ * @return      the number of bytes taken by the SPCod element.
+ */
+static OPJ_UINT32 opj_j2k_get_SPCod_SPCoc_size(opj_j2k_t *p_j2k,
+        OPJ_UINT32 p_tile_no,
+        OPJ_UINT32 p_comp_no);
+
+/**
+ * Reads a SPCod or SPCoc element, i.e. the coding style of a given component of a tile.
+ * @param       p_j2k           the jpeg2000 codec.
+ * @param       compno          FIXME DOC
+ * @param       p_header_data   the data contained in the COM box.
+ * @param       p_header_size   the size of the data contained in the COM marker.
+ * @param       p_manager       the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_SPCod_SPCoc(opj_j2k_t *p_j2k,
+        OPJ_UINT32 compno,
+        OPJ_BYTE * p_header_data,
+        OPJ_UINT32 * p_header_size,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Gets the size taken by writing SQcd or SQcc element, i.e. the quantization values of a band in the QCD or QCC.
+ *
+ * @param       p_tile_no               the tile index.
+ * @param       p_comp_no               the component being outputted.
+ * @param       p_j2k                   the J2K codec.
+ *
+ * @return      the number of bytes taken by the SPCod element.
+ */
+static OPJ_UINT32 opj_j2k_get_SQcd_SQcc_size(opj_j2k_t *p_j2k,
+        OPJ_UINT32 p_tile_no,
+        OPJ_UINT32 p_comp_no);
+
+/**
+ * Compares 2 SQcd or SQcc element, i.e. the quantization values of a band in the QCD or QCC.
+ *
+ * @param       p_j2k                   J2K codec.
+ * @param       p_tile_no               the tile to output.
+ * @param       p_first_comp_no         the first component number to compare.
+ * @param       p_second_comp_no        the second component number to compare.
+ *
+ * @return OPJ_TRUE if equals.
+ */
+static OPJ_BOOL opj_j2k_compare_SQcd_SQcc(opj_j2k_t *p_j2k,
+        OPJ_UINT32 p_tile_no, OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no);
+
+
+/**
+ * Writes a SQcd or SQcc element, i.e. the quantization values of a band in the QCD or QCC.
+ *
+ * @param       p_tile_no               the tile to output.
+ * @param       p_comp_no               the component number to output.
+ * @param       p_data                  the data buffer.
+ * @param       p_header_size   pointer to the size of the data buffer, it is changed by the function.
+ * @param       p_j2k                   J2K codec.
+ * @param       p_manager               the user event manager.
+ *
+*/
+static OPJ_BOOL opj_j2k_write_SQcd_SQcc(opj_j2k_t *p_j2k,
+                                        OPJ_UINT32 p_tile_no,
+                                        OPJ_UINT32 p_comp_no,
+                                        OPJ_BYTE * p_data,
+                                        OPJ_UINT32 * p_header_size,
+                                        opj_event_mgr_t * p_manager);
+
+/**
+ * Updates the Tile Length Marker.
+ */
+static void opj_j2k_update_tlm(opj_j2k_t * p_j2k, OPJ_UINT32 p_tile_part_size);
+
+/**
+ * Reads a SQcd or SQcc element, i.e. the quantization values of a band in the QCD or QCC.
+ *
+ * @param       p_j2k           J2K codec.
+ * @param       compno          the component number to output.
+ * @param       p_header_data   the data buffer.
+ * @param       p_header_size   pointer to the size of the data buffer, it is changed by the function.
+ * @param       p_manager       the user event manager.
+ *
+*/
+static OPJ_BOOL opj_j2k_read_SQcd_SQcc(opj_j2k_t *p_j2k,
+                                       OPJ_UINT32 compno,
+                                       OPJ_BYTE * p_header_data,
+                                       OPJ_UINT32 * p_header_size,
+                                       opj_event_mgr_t * p_manager);
+
+/**
+ * Copies the tile component parameters of all the component from the first tile component.
+ *
+ * @param               p_j2k           the J2k codec.
+ */
+static void opj_j2k_copy_tile_component_parameters(opj_j2k_t *p_j2k);
+
+/**
+ * Copies the tile quantization parameters of all the component from the first tile component.
+ *
+ * @param               p_j2k           the J2k codec.
+ */
+static void opj_j2k_copy_tile_quantization_parameters(opj_j2k_t *p_j2k);
+
+/**
+ * Reads the tiles.
+ */
+static OPJ_BOOL opj_j2k_decode_tiles(opj_j2k_t *p_j2k,
+                                     opj_stream_private_t *p_stream,
+                                     opj_event_mgr_t * p_manager);
+
+static OPJ_BOOL opj_j2k_pre_write_tile(opj_j2k_t * p_j2k,
+                                       OPJ_UINT32 p_tile_index,
+                                       opj_stream_private_t *p_stream,
+                                       opj_event_mgr_t * p_manager);
+
+static OPJ_BOOL opj_j2k_update_image_data(opj_tcd_t * p_tcd,
+        opj_image_t* p_output_image);
+
+static void opj_get_tile_dimensions(opj_image_t * l_image,
+                                    opj_tcd_tilecomp_t * l_tilec,
+                                    opj_image_comp_t * l_img_comp,
+                                    OPJ_UINT32* l_size_comp,
+                                    OPJ_UINT32* l_width,
+                                    OPJ_UINT32* l_height,
+                                    OPJ_UINT32* l_offset_x,
+                                    OPJ_UINT32* l_offset_y,
+                                    OPJ_UINT32* l_image_width,
+                                    OPJ_UINT32* l_stride,
+                                    OPJ_UINT32* l_tile_offset);
+
+static void opj_j2k_get_tile_data(opj_tcd_t * p_tcd, OPJ_BYTE * p_data);
+
+static OPJ_BOOL opj_j2k_post_write_tile(opj_j2k_t * p_j2k,
+                                        opj_stream_private_t *p_stream,
+                                        opj_event_mgr_t * p_manager);
+
+/**
+ * Sets up the procedures to do on writing header.
+ * Developers wanting to extend the library can add their own writing procedures.
+ */
+static OPJ_BOOL opj_j2k_setup_header_writing(opj_j2k_t *p_j2k,
+        opj_event_mgr_t * p_manager);
+
+static OPJ_BOOL opj_j2k_write_first_tile_part(opj_j2k_t *p_j2k,
+        OPJ_BYTE * p_data,
+        OPJ_UINT32 * p_data_written,
+        OPJ_UINT32 total_data_size,
+        opj_stream_private_t *p_stream,
+        struct opj_event_mgr * p_manager);
+
+static OPJ_BOOL opj_j2k_write_all_tile_parts(opj_j2k_t *p_j2k,
+        OPJ_BYTE * p_data,
+        OPJ_UINT32 * p_data_written,
+        OPJ_UINT32 total_data_size,
+        opj_stream_private_t *p_stream,
+        struct opj_event_mgr * p_manager);
+
+/**
+ * Gets the offset of the header.
+ *
+ * @param       p_stream                the stream to write data to.
+ * @param       p_j2k                   J2K codec.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_get_end_header(opj_j2k_t *p_j2k,
+                                       opj_stream_private_t *p_stream,
+                                       opj_event_mgr_t * p_manager);
+
+static OPJ_BOOL opj_j2k_allocate_tile_element_cstr_index(opj_j2k_t *p_j2k);
+
+/*
+ * -----------------------------------------------------------------------
+ * -----------------------------------------------------------------------
+ * -----------------------------------------------------------------------
+ */
+
+/**
+ * Writes the SOC marker (Start Of Codestream)
+ *
+ * @param       p_stream                        the stream to write data to.
+ * @param       p_j2k                   J2K codec.
+ * @param       p_manager       the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_soc(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a SOC marker (Start of Codestream)
+ * @param       p_j2k           the jpeg2000 file codec.
+ * @param       p_stream        XXX needs data
+ * @param       p_manager       the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_soc(opj_j2k_t *p_j2k,
+                                 opj_stream_private_t *p_stream,
+                                 opj_event_mgr_t * p_manager);
+
+/**
+ * Writes the SIZ marker (image and tile size)
+ *
+ * @param       p_j2k           J2K codec.
+ * @param       p_stream        the stream to write data to.
+ * @param       p_manager       the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_siz(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a SIZ marker (image and tile size)
+ * @param       p_j2k           the jpeg2000 file codec.
+ * @param       p_header_data   the data contained in the SIZ box.
+ * @param       p_header_size   the size of the data contained in the SIZ marker.
+ * @param       p_manager       the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_siz(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+
+/**
+ * Writes the COM marker (comment)
+ *
+ * @param       p_stream                        the stream to write data to.
+ * @param       p_j2k                   J2K codec.
+ * @param       p_manager       the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_com(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a COM marker (comments)
+ * @param       p_j2k           the jpeg2000 file codec.
+ * @param       p_header_data   the data contained in the COM box.
+ * @param       p_header_size   the size of the data contained in the COM marker.
+ * @param       p_manager       the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_com(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+/**
+ * Writes the COD marker (Coding style default)
+ *
+ * @param       p_stream                        the stream to write data to.
+ * @param       p_j2k                   J2K codec.
+ * @param       p_manager       the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_cod(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a COD marker (Coding style defaults)
+ * @param       p_header_data   the data contained in the COD box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the COD marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_cod(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+
+/**
+ * Compares 2 COC markers (Coding style component)
+ *
+ * @param       p_j2k            J2K codec.
+ * @param       p_first_comp_no  the index of the first component to compare.
+ * @param       p_second_comp_no the index of the second component to compare.
+ *
+ * @return      OPJ_TRUE if equals
+ */
+static OPJ_BOOL opj_j2k_compare_coc(opj_j2k_t *p_j2k,
+                                    OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no);
+
+/**
+ * Writes the COC marker (Coding style component)
+ *
+ * @param       p_j2k       J2K codec.
+ * @param       p_comp_no   the index of the component to output.
+ * @param       p_stream    the stream to write data to.
+ * @param       p_manager   the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_coc(opj_j2k_t *p_j2k,
+                                  OPJ_UINT32 p_comp_no,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Writes the COC marker (Coding style component)
+ *
+ * @param       p_j2k                   J2K codec.
+ * @param       p_comp_no               the index of the component to output.
+ * @param       p_data          FIXME DOC
+ * @param       p_data_written  FIXME DOC
+ * @param       p_manager               the user event manager.
+*/
+static void opj_j2k_write_coc_in_memory(opj_j2k_t *p_j2k,
+                                        OPJ_UINT32 p_comp_no,
+                                        OPJ_BYTE * p_data,
+                                        OPJ_UINT32 * p_data_written,
+                                        opj_event_mgr_t * p_manager);
+
+/**
+ * Gets the maximum size taken by a coc.
+ *
+ * @param       p_j2k   the jpeg2000 codec to use.
+ */
+static OPJ_UINT32 opj_j2k_get_max_coc_size(opj_j2k_t *p_j2k);
+
+/**
+ * Reads a COC marker (Coding Style Component)
+ * @param       p_header_data   the data contained in the COC box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the COC marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_coc(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+
+/**
+ * Writes the QCD marker (quantization default)
+ *
+ * @param       p_j2k                   J2K codec.
+ * @param       p_stream                the stream to write data to.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_qcd(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a QCD marker (Quantization defaults)
+ * @param       p_header_data   the data contained in the QCD box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the QCD marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_qcd(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+
+/**
+ * Compare QCC markers (quantization component)
+ *
+ * @param       p_j2k                 J2K codec.
+ * @param       p_first_comp_no       the index of the first component to compare.
+ * @param       p_second_comp_no      the index of the second component to compare.
+ *
+ * @return OPJ_TRUE if equals.
+ */
+static OPJ_BOOL opj_j2k_compare_qcc(opj_j2k_t *p_j2k,
+                                    OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no);
+
+/**
+ * Writes the QCC marker (quantization component)
+ *
+ * @param       p_comp_no       the index of the component to output.
+ * @param       p_stream                the stream to write data to.
+ * @param       p_j2k                   J2K codec.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_qcc(opj_j2k_t *p_j2k,
+                                  OPJ_UINT32 p_comp_no,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Writes the QCC marker (quantization component)
+ *
+ * @param       p_j2k           J2K codec.
+ * @param       p_comp_no       the index of the component to output.
+ * @param       p_data          FIXME DOC
+ * @param       p_data_written  the stream to write data to.
+ * @param       p_manager       the user event manager.
+*/
+static void opj_j2k_write_qcc_in_memory(opj_j2k_t *p_j2k,
+                                        OPJ_UINT32 p_comp_no,
+                                        OPJ_BYTE * p_data,
+                                        OPJ_UINT32 * p_data_written,
+                                        opj_event_mgr_t * p_manager);
+
+/**
+ * Gets the maximum size taken by a qcc.
+ */
+static OPJ_UINT32 opj_j2k_get_max_qcc_size(opj_j2k_t *p_j2k);
+
+/**
+ * Reads a QCC marker (Quantization component)
+ * @param       p_header_data   the data contained in the QCC box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the QCC marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_qcc(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+/**
+ * Writes the POC marker (Progression Order Change)
+ *
+ * @param       p_stream                                the stream to write data to.
+ * @param       p_j2k                           J2K codec.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_poc(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+/**
+ * Writes the POC marker (Progression Order Change)
+ *
+ * @param       p_j2k          J2K codec.
+ * @param       p_data         FIXME DOC
+ * @param       p_data_written the stream to write data to.
+ * @param       p_manager      the user event manager.
+ */
+static void opj_j2k_write_poc_in_memory(opj_j2k_t *p_j2k,
+                                        OPJ_BYTE * p_data,
+                                        OPJ_UINT32 * p_data_written,
+                                        opj_event_mgr_t * p_manager);
+/**
+ * Gets the maximum size taken by the writing of a POC.
+ */
+static OPJ_UINT32 opj_j2k_get_max_poc_size(opj_j2k_t *p_j2k);
+
+/**
+ * Reads a POC marker (Progression Order Change)
+ *
+ * @param       p_header_data   the data contained in the POC box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the POC marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_poc(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+
+/**
+ * Gets the maximum size taken by the toc headers of all the tile parts of any given tile.
+ */
+static OPJ_UINT32 opj_j2k_get_max_toc_size(opj_j2k_t *p_j2k);
+
+/**
+ * Gets the maximum size taken by the headers of the SOT.
+ *
+ * @param       p_j2k   the jpeg2000 codec to use.
+ */
+static OPJ_UINT32 opj_j2k_get_specific_header_sizes(opj_j2k_t *p_j2k);
+
+/**
+ * Reads a CRG marker (Component registration)
+ *
+ * @param       p_header_data   the data contained in the TLM box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the TLM marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_crg(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+/**
+ * Reads a TLM marker (Tile Length Marker)
+ *
+ * @param       p_header_data   the data contained in the TLM box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the TLM marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_tlm(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+
+/**
+ * Writes the updated tlm.
+ *
+ * @param       p_stream                the stream to write data to.
+ * @param       p_j2k                   J2K codec.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_updated_tlm(opj_j2k_t *p_j2k,
+        opj_stream_private_t *p_stream,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a PLM marker (Packet length, main header marker)
+ *
+ * @param       p_header_data   the data contained in the TLM box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the TLM marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_plm(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+/**
+ * Reads a PLT marker (Packet length, tile-part header)
+ *
+ * @param       p_header_data   the data contained in the PLT box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the PLT marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_plt(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a PPM marker (Packed headers, main header)
+ *
+ * @param       p_header_data   the data contained in the POC box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the POC marker.
+ * @param       p_manager               the user event manager.
+ */
+
+static OPJ_BOOL opj_j2k_read_ppm(
+    opj_j2k_t *p_j2k,
+    OPJ_BYTE * p_header_data,
+    OPJ_UINT32 p_header_size,
+    opj_event_mgr_t * p_manager);
+
+/**
+ * Merges all PPM markers read (Packed headers, main header)
+ *
+ * @param       p_cp      main coding parameters.
+ * @param       p_manager the user event manager.
+ */
+static OPJ_BOOL opj_j2k_merge_ppm(opj_cp_t *p_cp, opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a PPT marker (Packed packet headers, tile-part header)
+ *
+ * @param       p_header_data   the data contained in the PPT box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the PPT marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_ppt(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+
+/**
+ * Merges all PPT markers read (Packed headers, tile-part header)
+ *
+ * @param       p_tcp   the tile.
+ * @param       p_manager               the user event manager.
+ */
+static OPJ_BOOL opj_j2k_merge_ppt(opj_tcp_t *p_tcp,
+                                  opj_event_mgr_t * p_manager);
+
+
+/**
+ * Writes the TLM marker (Tile Length Marker)
+ *
+ * @param       p_stream                                the stream to write data to.
+ * @param       p_j2k                           J2K codec.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_tlm(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Writes the SOT marker (Start of tile-part)
+ *
+ * @param       p_j2k            J2K codec.
+ * @param       p_data           Output buffer
+ * @param       total_data_size  Output buffer size
+ * @param       p_data_written   Number of bytes written into stream
+ * @param       p_stream         the stream to write data to.
+ * @param       p_manager        the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_sot(opj_j2k_t *p_j2k,
+                                  OPJ_BYTE * p_data,
+                                  OPJ_UINT32 total_data_size,
+                                  OPJ_UINT32 * p_data_written,
+                                  const opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Reads values from a SOT marker (Start of tile-part)
+ *
+ * the j2k decoder state is not affected. No side effects, no checks except for p_header_size.
+ *
+ * @param       p_header_data   the data contained in the SOT marker.
+ * @param       p_header_size   the size of the data contained in the SOT marker.
+ * @param       p_tile_no       Isot.
+ * @param       p_tot_len       Psot.
+ * @param       p_current_part  TPsot.
+ * @param       p_num_parts     TNsot.
+ * @param       p_manager       the user event manager.
+ */
+static OPJ_BOOL opj_j2k_get_sot_values(OPJ_BYTE *  p_header_data,
+                                       OPJ_UINT32  p_header_size,
+                                       OPJ_UINT32* p_tile_no,
+                                       OPJ_UINT32* p_tot_len,
+                                       OPJ_UINT32* p_current_part,
+                                       OPJ_UINT32* p_num_parts,
+                                       opj_event_mgr_t * p_manager);
+/**
+ * Reads a SOT marker (Start of tile-part)
+ *
+ * @param       p_header_data   the data contained in the SOT marker.
+ * @param       p_j2k           the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the PPT marker.
+ * @param       p_manager       the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_sot(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+/**
+ * Writes the SOD marker (Start of data)
+ *
+ * This also writes optional PLT markers (before SOD)
+ *
+ * @param       p_j2k               J2K codec.
+ * @param       p_tile_coder        FIXME DOC
+ * @param       p_data              FIXME DOC
+ * @param       p_data_written      FIXME DOC
+ * @param       total_data_size   FIXME DOC
+ * @param       p_stream            the stream to write data to.
+ * @param       p_manager           the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_sod(opj_j2k_t *p_j2k,
+                                  opj_tcd_t * p_tile_coder,
+                                  OPJ_BYTE * p_data,
+                                  OPJ_UINT32 * p_data_written,
+                                  OPJ_UINT32 total_data_size,
+                                  const opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a SOD marker (Start Of Data)
+ *
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_stream                FIXME DOC
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_sod(opj_j2k_t *p_j2k,
+                                 opj_stream_private_t *p_stream,
+                                 opj_event_mgr_t * p_manager);
+
+static void opj_j2k_update_tlm(opj_j2k_t * p_j2k, OPJ_UINT32 p_tile_part_size)
+{
+    if (p_j2k->m_specific_param.m_encoder.m_Ttlmi_is_byte) {
+        opj_write_bytes(p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current,
+                        p_j2k->m_current_tile_number, 1);
+        p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current += 1;
+    } else {
+        opj_write_bytes(p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current,
+                        p_j2k->m_current_tile_number, 2);
+        p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current += 2;
+    }
+
+    opj_write_bytes(p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current,
+                    p_tile_part_size, 4);                                       /* PSOT */
+    p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current += 4;
+}
+
+/**
+ * Writes the RGN marker (Region Of Interest)
+ *
+ * @param       p_tile_no               the tile to output
+ * @param       p_comp_no               the component to output
+ * @param       nb_comps                the number of components
+ * @param       p_stream                the stream to write data to.
+ * @param       p_j2k                   J2K codec.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_rgn(opj_j2k_t *p_j2k,
+                                  OPJ_UINT32 p_tile_no,
+                                  OPJ_UINT32 p_comp_no,
+                                  OPJ_UINT32 nb_comps,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a RGN marker (Region Of Interest)
+ *
+ * @param       p_header_data   the data contained in the POC box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the POC marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_rgn(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+
+/**
+ * Writes the EOC marker (End of Codestream)
+ *
+ * @param       p_stream                the stream to write data to.
+ * @param       p_j2k                   J2K codec.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_eoc(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+#if 0
+/**
+ * Reads a EOC marker (End Of Codestream)
+ *
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_stream                FIXME DOC
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_eoc(opj_j2k_t *p_j2k,
+                                 opj_stream_private_t *p_stream,
+                                 opj_event_mgr_t * p_manager);
+#endif
+
+/**
+ * Writes the CBD-MCT-MCC-MCO markers (Multi components transform)
+ *
+ * @param       p_stream                        the stream to write data to.
+ * @param       p_j2k                   J2K codec.
+ * @param       p_manager       the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_mct_data_group(opj_j2k_t *p_j2k,
+        opj_stream_private_t *p_stream,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Inits the Info
+ *
+ * @param       p_stream                the stream to write data to.
+ * @param       p_j2k                   J2K codec.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_init_info(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+Add main header marker information
+@param cstr_index    Codestream information structure
+@param type         marker type
+@param pos          byte offset of marker segment
+@param len          length of marker segment
+ */
+static OPJ_BOOL opj_j2k_add_mhmarker(opj_codestream_index_t *cstr_index,
+                                     OPJ_UINT32 type, OPJ_OFF_T pos, OPJ_UINT32 len) ;
+/**
+Add tile header marker information
+@param tileno       tile index number
+@param cstr_index   Codestream information structure
+@param type         marker type
+@param pos          byte offset of marker segment
+@param len          length of marker segment
+ */
+static OPJ_BOOL opj_j2k_add_tlmarker(OPJ_UINT32 tileno,
+                                     opj_codestream_index_t *cstr_index, OPJ_UINT32 type, OPJ_OFF_T pos,
+                                     OPJ_UINT32 len);
+
+/**
+ * Reads an unknown marker
+ *
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_stream                the stream object to read from.
+ * @param       output_marker           FIXME DOC
+ * @param       p_manager               the user event manager.
+ *
+ * @return      true                    if the marker could be deduced.
+*/
+static OPJ_BOOL opj_j2k_read_unk(opj_j2k_t *p_j2k,
+                                 opj_stream_private_t *p_stream,
+                                 OPJ_UINT32 *output_marker,
+                                 opj_event_mgr_t * p_manager);
+
+/**
+ * Writes the MCT marker (Multiple Component Transform)
+ *
+ * @param       p_j2k           J2K codec.
+ * @param       p_mct_record    FIXME DOC
+ * @param       p_stream        the stream to write data to.
+ * @param       p_manager       the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_mct_record(opj_j2k_t *p_j2k,
+        opj_mct_data_t * p_mct_record,
+        opj_stream_private_t *p_stream,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a MCT marker (Multiple Component Transform)
+ *
+ * @param       p_header_data   the data contained in the MCT box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the MCT marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_mct(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+
+/**
+ * Writes the MCC marker (Multiple Component Collection)
+ *
+ * @param       p_j2k                   J2K codec.
+ * @param       p_mcc_record            FIXME DOC
+ * @param       p_stream                the stream to write data to.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_mcc_record(opj_j2k_t *p_j2k,
+        opj_simple_mcc_decorrelation_data_t * p_mcc_record,
+        opj_stream_private_t *p_stream,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a MCC marker (Multiple Component Collection)
+ *
+ * @param       p_header_data   the data contained in the MCC box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the MCC marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_mcc(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+
+/**
+ * Writes the MCO marker (Multiple component transformation ordering)
+ *
+ * @param       p_stream                                the stream to write data to.
+ * @param       p_j2k                           J2K codec.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_mco(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a MCO marker (Multiple Component Transform Ordering)
+ *
+ * @param       p_header_data   the data contained in the MCO box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the MCO marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_mco(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+
+static OPJ_BOOL opj_j2k_add_mct(opj_tcp_t * p_tcp, opj_image_t * p_image,
+                                OPJ_UINT32 p_index);
+
+static void  opj_j2k_read_int16_to_float(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem);
+static void  opj_j2k_read_int32_to_float(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem);
+static void  opj_j2k_read_float32_to_float(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem);
+static void  opj_j2k_read_float64_to_float(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem);
+
+static void  opj_j2k_read_int16_to_int32(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem);
+static void  opj_j2k_read_int32_to_int32(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem);
+static void  opj_j2k_read_float32_to_int32(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem);
+static void  opj_j2k_read_float64_to_int32(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem);
+
+static void  opj_j2k_write_float_to_int16(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem);
+static void  opj_j2k_write_float_to_int32(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem);
+static void  opj_j2k_write_float_to_float(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem);
+static void  opj_j2k_write_float_to_float64(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem);
+
+/**
+ * Ends the encoding, i.e. frees memory.
+ *
+ * @param       p_stream                the stream to write data to.
+ * @param       p_j2k                   J2K codec.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_end_encoding(opj_j2k_t *p_j2k,
+                                     opj_stream_private_t *p_stream,
+                                     opj_event_mgr_t * p_manager);
+
+/**
+ * Writes the CBD marker (Component bit depth definition)
+ *
+ * @param       p_stream                                the stream to write data to.
+ * @param       p_j2k                           J2K codec.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_cbd(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a CBD marker (Component bit depth definition)
+ * @param       p_header_data   the data contained in the CBD box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the CBD marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_cbd(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a CAP marker (extended capabilities definition). Empty implementation.
+ * Found in HTJ2K files
+ *
+ * @param       p_header_data   the data contained in the CAP box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the CAP marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_cap(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a CPF marker (corresponding profile). Empty implementation. Found in HTJ2K files
+ * @param       p_header_data   the data contained in the CPF box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the CPF marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_cpf(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager);
+
+
+/**
+ * Writes COC marker for each component.
+ *
+ * @param       p_stream                the stream to write data to.
+ * @param       p_j2k                   J2K codec.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_all_coc(opj_j2k_t *p_j2k,
+                                      opj_stream_private_t *p_stream,
+                                      opj_event_mgr_t * p_manager);
+
+/**
+ * Writes QCC marker for each component.
+ *
+ * @param       p_stream                the stream to write data to.
+ * @param       p_j2k                   J2K codec.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_all_qcc(opj_j2k_t *p_j2k,
+                                      opj_stream_private_t *p_stream,
+                                      opj_event_mgr_t * p_manager);
+
+/**
+ * Writes regions of interests.
+ *
+ * @param       p_stream                the stream to write data to.
+ * @param       p_j2k                   J2K codec.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_regions(opj_j2k_t *p_j2k,
+                                      opj_stream_private_t *p_stream,
+                                      opj_event_mgr_t * p_manager);
+
+/**
+ * Writes EPC ????
+ *
+ * @param       p_stream                the stream to write data to.
+ * @param       p_j2k                   J2K codec.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_write_epc(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Checks the progression order changes values. Tells of the poc given as input are valid.
+ * A nice message is outputted at errors.
+ *
+ * @param       p_pocs                  the progression order changes.
+ * @param       tileno                  the tile number of interest
+ * @param       p_nb_pocs               the number of progression order changes.
+ * @param       p_nb_resolutions        the number of resolutions.
+ * @param       numcomps                the number of components
+ * @param       numlayers               the number of layers.
+ * @param       p_manager               the user event manager.
+ *
+ * @return      true if the pocs are valid.
+ */
+static OPJ_BOOL opj_j2k_check_poc_val(const opj_poc_t *p_pocs,
+                                      OPJ_UINT32 tileno,
+                                      OPJ_UINT32 p_nb_pocs,
+                                      OPJ_UINT32 p_nb_resolutions,
+                                      OPJ_UINT32 numcomps,
+                                      OPJ_UINT32 numlayers,
+                                      opj_event_mgr_t * p_manager);
+
+/**
+ * Gets the number of tile parts used for the given change of progression (if any) and the given tile.
+ *
+ * @param               cp                      the coding parameters.
+ * @param               pino            the offset of the given poc (i.e. its position in the coding parameter).
+ * @param               tileno          the given tile.
+ *
+ * @return              the number of tile parts.
+ */
+static OPJ_UINT32 opj_j2k_get_num_tp(opj_cp_t *cp, OPJ_UINT32 pino,
+                                     OPJ_UINT32 tileno);
+
+/**
+ * Calculates the total number of tile parts needed by the encoder to
+ * encode such an image. If not enough memory is available, then the function return false.
+ *
+ * @param       p_nb_tiles      pointer that will hold the number of tile parts.
+ * @param       cp                      the coding parameters for the image.
+ * @param       image           the image to encode.
+ * @param       p_j2k                   the p_j2k encoder.
+ * @param       p_manager       the user event manager.
+ *
+ * @return true if the function was successful, false else.
+ */
+static OPJ_BOOL opj_j2k_calculate_tp(opj_j2k_t *p_j2k,
+                                     opj_cp_t *cp,
+                                     OPJ_UINT32 * p_nb_tiles,
+                                     opj_image_t *image,
+                                     opj_event_mgr_t * p_manager);
+
+static void opj_j2k_dump_MH_info(opj_j2k_t* p_j2k, FILE* out_stream);
+
+static void opj_j2k_dump_MH_index(opj_j2k_t* p_j2k, FILE* out_stream);
+
+static opj_codestream_index_t* opj_j2k_create_cstr_index(void);
+
+static OPJ_FLOAT32 opj_j2k_get_tp_stride(opj_tcp_t * p_tcp);
+
+static OPJ_FLOAT32 opj_j2k_get_default_stride(opj_tcp_t * p_tcp);
+
+static int opj_j2k_initialise_4K_poc(opj_poc_t *POC, int numres);
+
+static void opj_j2k_set_cinema_parameters(opj_cparameters_t *parameters,
+        opj_image_t *image, opj_event_mgr_t *p_manager);
+
+static OPJ_BOOL opj_j2k_is_cinema_compliant(opj_image_t *image, OPJ_UINT16 rsiz,
+        opj_event_mgr_t *p_manager);
+
+static void opj_j2k_set_imf_parameters(opj_cparameters_t *parameters,
+                                       opj_image_t *image, opj_event_mgr_t *p_manager);
+
+static OPJ_BOOL opj_j2k_is_imf_compliant(opj_cparameters_t *parameters,
+        opj_image_t *image,
+        opj_event_mgr_t *p_manager);
+
+/**
+ * Checks for invalid number of tile-parts in SOT marker (TPsot==TNsot). See issue 254.
+ *
+ * @param       p_stream            the stream to read data from.
+ * @param       tile_no             tile number we're looking for.
+ * @param       p_correction_needed output value. if true, non conformant codestream needs TNsot correction.
+ * @param       p_manager       the user event manager.
+ *
+ * @return true if the function was successful, false else.
+ */
+static OPJ_BOOL opj_j2k_need_nb_tile_parts_correction(opj_stream_private_t
+        *p_stream, OPJ_UINT32 tile_no, OPJ_BOOL* p_correction_needed,
+        opj_event_mgr_t * p_manager);
+
+/*@}*/
+
+/*@}*/
+
+/* ----------------------------------------------------------------------- */
+typedef struct j2k_prog_order {
+    OPJ_PROG_ORDER enum_prog;
+    char str_prog[5];
+} j2k_prog_order_t;
+
+static const j2k_prog_order_t j2k_prog_order_list[] = {
+    {OPJ_CPRL, "CPRL"},
+    {OPJ_LRCP, "LRCP"},
+    {OPJ_PCRL, "PCRL"},
+    {OPJ_RLCP, "RLCP"},
+    {OPJ_RPCL, "RPCL"},
+    {(OPJ_PROG_ORDER) - 1, ""}
+};
+
+/**
+ * FIXME DOC
+ */
+static const OPJ_UINT32 MCT_ELEMENT_SIZE [] = {
+    2,
+    4,
+    4,
+    8
+};
+
+typedef void (* opj_j2k_mct_function)(const void * p_src_data,
+                                      void * p_dest_data, OPJ_UINT32 p_nb_elem);
+
+static const opj_j2k_mct_function j2k_mct_read_functions_to_float [] = {
+    opj_j2k_read_int16_to_float,
+    opj_j2k_read_int32_to_float,
+    opj_j2k_read_float32_to_float,
+    opj_j2k_read_float64_to_float
+};
+
+static const opj_j2k_mct_function j2k_mct_read_functions_to_int32 [] = {
+    opj_j2k_read_int16_to_int32,
+    opj_j2k_read_int32_to_int32,
+    opj_j2k_read_float32_to_int32,
+    opj_j2k_read_float64_to_int32
+};
+
+static const opj_j2k_mct_function j2k_mct_write_functions_from_float [] = {
+    opj_j2k_write_float_to_int16,
+    opj_j2k_write_float_to_int32,
+    opj_j2k_write_float_to_float,
+    opj_j2k_write_float_to_float64
+};
+
+typedef struct opj_dec_memory_marker_handler {
+    /** marker value */
+    OPJ_UINT32 id;
+    /** value of the state when the marker can appear */
+    OPJ_UINT32 states;
+    /** action linked to the marker */
+    OPJ_BOOL(*handler)(opj_j2k_t *p_j2k,
+                       OPJ_BYTE * p_header_data,
+                       OPJ_UINT32 p_header_size,
+                       opj_event_mgr_t * p_manager);
+}
+opj_dec_memory_marker_handler_t;
+
+static const opj_dec_memory_marker_handler_t j2k_memory_marker_handler_tab [] =
+{
+    {J2K_MS_SOT, J2K_STATE_MH | J2K_STATE_TPHSOT, opj_j2k_read_sot},
+    {J2K_MS_COD, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_cod},
+    {J2K_MS_COC, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_coc},
+    {J2K_MS_RGN, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_rgn},
+    {J2K_MS_QCD, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_qcd},
+    {J2K_MS_QCC, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_qcc},
+    {J2K_MS_POC, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_poc},
+    {J2K_MS_SIZ, J2K_STATE_MHSIZ, opj_j2k_read_siz},
+    {J2K_MS_TLM, J2K_STATE_MH, opj_j2k_read_tlm},
+    {J2K_MS_PLM, J2K_STATE_MH, opj_j2k_read_plm},
+    {J2K_MS_PLT, J2K_STATE_TPH, opj_j2k_read_plt},
+    {J2K_MS_PPM, J2K_STATE_MH, opj_j2k_read_ppm},
+    {J2K_MS_PPT, J2K_STATE_TPH, opj_j2k_read_ppt},
+    {J2K_MS_SOP, 0, 0},
+    {J2K_MS_CRG, J2K_STATE_MH, opj_j2k_read_crg},
+    {J2K_MS_COM, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_com},
+    {J2K_MS_MCT, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_mct},
+    {J2K_MS_CBD, J2K_STATE_MH, opj_j2k_read_cbd},
+    {J2K_MS_CAP, J2K_STATE_MH, opj_j2k_read_cap},
+    {J2K_MS_CPF, J2K_STATE_MH, opj_j2k_read_cpf},
+    {J2K_MS_MCC, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_mcc},
+    {J2K_MS_MCO, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_mco},
+#ifdef USE_JPWL
+#ifdef TODO_MS /* remove these functions which are not compatible with the v2 API */
+    {J2K_MS_EPC, J2K_STATE_MH | J2K_STATE_TPH, j2k_read_epc},
+    {J2K_MS_EPB, J2K_STATE_MH | J2K_STATE_TPH, j2k_read_epb},
+    {J2K_MS_ESD, J2K_STATE_MH | J2K_STATE_TPH, j2k_read_esd},
+    {J2K_MS_RED, J2K_STATE_MH | J2K_STATE_TPH, j2k_read_red},
+#endif
+#endif /* USE_JPWL */
+#ifdef USE_JPSEC
+    {J2K_MS_SEC, J2K_DEC_STATE_MH, j2k_read_sec},
+    {J2K_MS_INSEC, 0, j2k_read_insec}
+#endif /* USE_JPSEC */
+    {J2K_MS_UNK, J2K_STATE_MH | J2K_STATE_TPH, 0}/*opj_j2k_read_unk is directly used*/
+};
+
+static void  opj_j2k_read_int16_to_float(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem)
+{
+    OPJ_BYTE * l_src_data = (OPJ_BYTE *) p_src_data;
+    OPJ_FLOAT32 * l_dest_data = (OPJ_FLOAT32 *) p_dest_data;
+    OPJ_UINT32 i;
+    OPJ_UINT32 l_temp;
+
+    for (i = 0; i < p_nb_elem; ++i) {
+        opj_read_bytes(l_src_data, &l_temp, 2);
+
+        l_src_data += sizeof(OPJ_INT16);
+
+        *(l_dest_data++) = (OPJ_FLOAT32) l_temp;
+    }
+}
+
+static void  opj_j2k_read_int32_to_float(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem)
+{
+    OPJ_BYTE * l_src_data = (OPJ_BYTE *) p_src_data;
+    OPJ_FLOAT32 * l_dest_data = (OPJ_FLOAT32 *) p_dest_data;
+    OPJ_UINT32 i;
+    OPJ_UINT32 l_temp;
+
+    for (i = 0; i < p_nb_elem; ++i) {
+        opj_read_bytes(l_src_data, &l_temp, 4);
+
+        l_src_data += sizeof(OPJ_INT32);
+
+        *(l_dest_data++) = (OPJ_FLOAT32) l_temp;
+    }
+}
+
+static void  opj_j2k_read_float32_to_float(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem)
+{
+    OPJ_BYTE * l_src_data = (OPJ_BYTE *) p_src_data;
+    OPJ_FLOAT32 * l_dest_data = (OPJ_FLOAT32 *) p_dest_data;
+    OPJ_UINT32 i;
+    OPJ_FLOAT32 l_temp;
+
+    for (i = 0; i < p_nb_elem; ++i) {
+        opj_read_float(l_src_data, &l_temp);
+
+        l_src_data += sizeof(OPJ_FLOAT32);
+
+        *(l_dest_data++) = l_temp;
+    }
+}
+
+static void  opj_j2k_read_float64_to_float(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem)
+{
+    OPJ_BYTE * l_src_data = (OPJ_BYTE *) p_src_data;
+    OPJ_FLOAT32 * l_dest_data = (OPJ_FLOAT32 *) p_dest_data;
+    OPJ_UINT32 i;
+    OPJ_FLOAT64 l_temp;
+
+    for (i = 0; i < p_nb_elem; ++i) {
+        opj_read_double(l_src_data, &l_temp);
+
+        l_src_data += sizeof(OPJ_FLOAT64);
+
+        *(l_dest_data++) = (OPJ_FLOAT32) l_temp;
+    }
+}
+
+static void  opj_j2k_read_int16_to_int32(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem)
+{
+    OPJ_BYTE * l_src_data = (OPJ_BYTE *) p_src_data;
+    OPJ_INT32 * l_dest_data = (OPJ_INT32 *) p_dest_data;
+    OPJ_UINT32 i;
+    OPJ_UINT32 l_temp;
+
+    for (i = 0; i < p_nb_elem; ++i) {
+        opj_read_bytes(l_src_data, &l_temp, 2);
+
+        l_src_data += sizeof(OPJ_INT16);
+
+        *(l_dest_data++) = (OPJ_INT32) l_temp;
+    }
+}
+
+static void  opj_j2k_read_int32_to_int32(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem)
+{
+    OPJ_BYTE * l_src_data = (OPJ_BYTE *) p_src_data;
+    OPJ_INT32 * l_dest_data = (OPJ_INT32 *) p_dest_data;
+    OPJ_UINT32 i;
+    OPJ_UINT32 l_temp;
+
+    for (i = 0; i < p_nb_elem; ++i) {
+        opj_read_bytes(l_src_data, &l_temp, 4);
+
+        l_src_data += sizeof(OPJ_INT32);
+
+        *(l_dest_data++) = (OPJ_INT32) l_temp;
+    }
+}
+
+static void  opj_j2k_read_float32_to_int32(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem)
+{
+    OPJ_BYTE * l_src_data = (OPJ_BYTE *) p_src_data;
+    OPJ_INT32 * l_dest_data = (OPJ_INT32 *) p_dest_data;
+    OPJ_UINT32 i;
+    OPJ_FLOAT32 l_temp;
+
+    for (i = 0; i < p_nb_elem; ++i) {
+        opj_read_float(l_src_data, &l_temp);
+
+        l_src_data += sizeof(OPJ_FLOAT32);
+
+        *(l_dest_data++) = (OPJ_INT32) l_temp;
+    }
+}
+
+static void  opj_j2k_read_float64_to_int32(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem)
+{
+    OPJ_BYTE * l_src_data = (OPJ_BYTE *) p_src_data;
+    OPJ_INT32 * l_dest_data = (OPJ_INT32 *) p_dest_data;
+    OPJ_UINT32 i;
+    OPJ_FLOAT64 l_temp;
+
+    for (i = 0; i < p_nb_elem; ++i) {
+        opj_read_double(l_src_data, &l_temp);
+
+        l_src_data += sizeof(OPJ_FLOAT64);
+
+        *(l_dest_data++) = (OPJ_INT32) l_temp;
+    }
+}
+
+static void  opj_j2k_write_float_to_int16(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem)
+{
+    OPJ_BYTE * l_dest_data = (OPJ_BYTE *) p_dest_data;
+    OPJ_FLOAT32 * l_src_data = (OPJ_FLOAT32 *) p_src_data;
+    OPJ_UINT32 i;
+    OPJ_UINT32 l_temp;
+
+    for (i = 0; i < p_nb_elem; ++i) {
+        l_temp = (OPJ_UINT32) * (l_src_data++);
+
+        opj_write_bytes(l_dest_data, l_temp, sizeof(OPJ_INT16));
+
+        l_dest_data += sizeof(OPJ_INT16);
+    }
+}
+
+static void opj_j2k_write_float_to_int32(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem)
+{
+    OPJ_BYTE * l_dest_data = (OPJ_BYTE *) p_dest_data;
+    OPJ_FLOAT32 * l_src_data = (OPJ_FLOAT32 *) p_src_data;
+    OPJ_UINT32 i;
+    OPJ_UINT32 l_temp;
+
+    for (i = 0; i < p_nb_elem; ++i) {
+        l_temp = (OPJ_UINT32) * (l_src_data++);
+
+        opj_write_bytes(l_dest_data, l_temp, sizeof(OPJ_INT32));
+
+        l_dest_data += sizeof(OPJ_INT32);
+    }
+}
+
+static void  opj_j2k_write_float_to_float(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem)
+{
+    OPJ_BYTE * l_dest_data = (OPJ_BYTE *) p_dest_data;
+    OPJ_FLOAT32 * l_src_data = (OPJ_FLOAT32 *) p_src_data;
+    OPJ_UINT32 i;
+    OPJ_FLOAT32 l_temp;
+
+    for (i = 0; i < p_nb_elem; ++i) {
+        l_temp = (OPJ_FLOAT32) * (l_src_data++);
+
+        opj_write_float(l_dest_data, l_temp);
+
+        l_dest_data += sizeof(OPJ_FLOAT32);
+    }
+}
+
+static void  opj_j2k_write_float_to_float64(const void * p_src_data,
+        void * p_dest_data, OPJ_UINT32 p_nb_elem)
+{
+    OPJ_BYTE * l_dest_data = (OPJ_BYTE *) p_dest_data;
+    OPJ_FLOAT32 * l_src_data = (OPJ_FLOAT32 *) p_src_data;
+    OPJ_UINT32 i;
+    OPJ_FLOAT64 l_temp;
+
+    for (i = 0; i < p_nb_elem; ++i) {
+        l_temp = (OPJ_FLOAT64) * (l_src_data++);
+
+        opj_write_double(l_dest_data, l_temp);
+
+        l_dest_data += sizeof(OPJ_FLOAT64);
+    }
+}
+
+const char *opj_j2k_convert_progression_order(OPJ_PROG_ORDER prg_order)
+{
+    const j2k_prog_order_t *po;
+    for (po = j2k_prog_order_list; po->enum_prog != -1; po++) {
+        if (po->enum_prog == prg_order) {
+            return po->str_prog;
+        }
+    }
+    return po->str_prog;
+}
+
+static OPJ_BOOL opj_j2k_check_poc_val(const opj_poc_t *p_pocs,
+                                      OPJ_UINT32 tileno,
+                                      OPJ_UINT32 p_nb_pocs,
+                                      OPJ_UINT32 p_nb_resolutions,
+                                      OPJ_UINT32 p_num_comps,
+                                      OPJ_UINT32 p_num_layers,
+                                      opj_event_mgr_t * p_manager)
+{
+    OPJ_UINT32* packet_array;
+    OPJ_UINT32 index, resno, compno, layno;
+    OPJ_UINT32 i;
+    OPJ_UINT32 step_c = 1;
+    OPJ_UINT32 step_r = p_num_comps * step_c;
+    OPJ_UINT32 step_l = p_nb_resolutions * step_r;
+    OPJ_BOOL loss = OPJ_FALSE;
+
+    assert(p_nb_pocs > 0);
+
+    packet_array = (OPJ_UINT32*) opj_calloc((size_t)step_l * p_num_layers,
+                                            sizeof(OPJ_UINT32));
+    if (packet_array == 00) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Not enough memory for checking the poc values.\n");
+        return OPJ_FALSE;
+    }
+
+    /* iterate through all the pocs that match our tile of interest. */
+    for (i = 0; i < p_nb_pocs; ++i) {
+        const opj_poc_t *poc = &p_pocs[i];
+        if (tileno + 1 == poc->tile) {
+            index = step_r * poc->resno0;
+
+            /* take each resolution for each poc */
+            for (resno = poc->resno0 ;
+                    resno < opj_uint_min(poc->resno1, p_nb_resolutions); ++resno) {
+                OPJ_UINT32 res_index = index + poc->compno0 * step_c;
+
+                /* take each comp of each resolution for each poc */
+                for (compno = poc->compno0 ;
+                        compno < opj_uint_min(poc->compno1, p_num_comps); ++compno) {
+                    /* The layer index always starts at zero for every progression. */
+                    const OPJ_UINT32 layno0 = 0;
+                    OPJ_UINT32 comp_index = res_index + layno0 * step_l;
+
+                    /* and finally take each layer of each res of ... */
+                    for (layno = layno0; layno < opj_uint_min(poc->layno1, p_num_layers);
+                            ++layno) {
+                        packet_array[comp_index] = 1;
+                        comp_index += step_l;
+                    }
+
+                    res_index += step_c;
+                }
+
+                index += step_r;
+            }
+        }
+    }
+
+    index = 0;
+    for (layno = 0; layno < p_num_layers ; ++layno) {
+        for (resno = 0; resno < p_nb_resolutions; ++resno) {
+            for (compno = 0; compno < p_num_comps; ++compno) {
+                loss |= (packet_array[index] != 1);
+#ifdef DEBUG_VERBOSE
+                if (packet_array[index] != 1) {
+                    fprintf(stderr,
+                            "Missing packet in POC: layno=%d resno=%d compno=%d\n",
+                            layno, resno, compno);
+                }
+#endif
+                index += step_c;
+            }
+        }
+    }
+
+    if (loss) {
+        opj_event_msg(p_manager, EVT_ERROR, "Missing packets possible loss of data\n");
+    }
+
+    opj_free(packet_array);
+
+    return !loss;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static OPJ_UINT32 opj_j2k_get_num_tp(opj_cp_t *cp, OPJ_UINT32 pino,
+                                     OPJ_UINT32 tileno)
+{
+    const OPJ_CHAR *prog = 00;
+    OPJ_INT32 i;
+    OPJ_UINT32 tpnum = 1;
+    opj_tcp_t *tcp = 00;
+    opj_poc_t * l_current_poc = 00;
+
+    /*  preconditions */
+    assert(tileno < (cp->tw * cp->th));
+    assert(pino < (cp->tcps[tileno].numpocs + 1));
+
+    /* get the given tile coding parameter */
+    tcp = &cp->tcps[tileno];
+    assert(tcp != 00);
+
+    l_current_poc = &(tcp->pocs[pino]);
+    assert(l_current_poc != 0);
+
+    /* get the progression order as a character string */
+    prog = opj_j2k_convert_progression_order(tcp->prg);
+    assert(strlen(prog) > 0);
+
+    if (cp->m_specific_param.m_enc.m_tp_on == 1) {
+        for (i = 0; i < 4; ++i) {
+            switch (prog[i]) {
+            /* component wise */
+            case 'C':
+                tpnum *= l_current_poc->compE;
+                break;
+            /* resolution wise */
+            case 'R':
+                tpnum *= l_current_poc->resE;
+                break;
+            /* precinct wise */
+            case 'P':
+                tpnum *= l_current_poc->prcE;
+                break;
+            /* layer wise */
+            case 'L':
+                tpnum *= l_current_poc->layE;
+                break;
+            }
+            /* would we split here ? */
+            if (cp->m_specific_param.m_enc.m_tp_flag == prog[i]) {
+                cp->m_specific_param.m_enc.m_tp_pos = i;
+                break;
+            }
+        }
+    } else {
+        tpnum = 1;
+    }
+
+    return tpnum;
+}
+
+static OPJ_BOOL opj_j2k_calculate_tp(opj_j2k_t *p_j2k,
+                                     opj_cp_t *cp,
+                                     OPJ_UINT32 * p_nb_tiles,
+                                     opj_image_t *image,
+                                     opj_event_mgr_t * p_manager
+                                    )
+{
+    OPJ_UINT32 pino, tileno;
+    OPJ_UINT32 l_nb_tiles;
+    opj_tcp_t *tcp;
+
+    /* preconditions */
+    assert(p_nb_tiles != 00);
+    assert(cp != 00);
+    assert(image != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    OPJ_UNUSED(p_j2k);
+    OPJ_UNUSED(p_manager);
+
+    l_nb_tiles = cp->tw * cp->th;
+    * p_nb_tiles = 0;
+    tcp = cp->tcps;
+
+    /* INDEX >> */
+    /* TODO mergeV2: check this part which use cstr_info */
+    /*if (p_j2k->cstr_info) {
+            opj_tile_info_t * l_info_tile_ptr = p_j2k->cstr_info->tile;
+
+            for (tileno = 0; tileno < l_nb_tiles; ++tileno) {
+                    OPJ_UINT32 cur_totnum_tp = 0;
+
+                    opj_pi_update_encoding_parameters(image,cp,tileno);
+
+                    for (pino = 0; pino <= tcp->numpocs; ++pino)
+                    {
+                            OPJ_UINT32 tp_num = opj_j2k_get_num_tp(cp,pino,tileno);
+
+                            *p_nb_tiles = *p_nb_tiles + tp_num;
+
+                            cur_totnum_tp += tp_num;
+                    }
+
+                    tcp->m_nb_tile_parts = cur_totnum_tp;
+
+                    l_info_tile_ptr->tp = (opj_tp_info_t *) opj_malloc(cur_totnum_tp * sizeof(opj_tp_info_t));
+                    if (l_info_tile_ptr->tp == 00) {
+                            return OPJ_FALSE;
+                    }
+
+                    memset(l_info_tile_ptr->tp,0,cur_totnum_tp * sizeof(opj_tp_info_t));
+
+                    l_info_tile_ptr->num_tps = cur_totnum_tp;
+
+                    ++l_info_tile_ptr;
+                    ++tcp;
+            }
+    }
+    else */{
+        for (tileno = 0; tileno < l_nb_tiles; ++tileno) {
+            OPJ_UINT32 cur_totnum_tp = 0;
+
+            opj_pi_update_encoding_parameters(image, cp, tileno);
+
+            for (pino = 0; pino <= tcp->numpocs; ++pino) {
+                OPJ_UINT32 tp_num = opj_j2k_get_num_tp(cp, pino, tileno);
+
+                *p_nb_tiles = *p_nb_tiles + tp_num;
+
+                cur_totnum_tp += tp_num;
+            }
+            tcp->m_nb_tile_parts = cur_totnum_tp;
+
+            ++tcp;
+        }
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_soc(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager)
+{
+    /* 2 bytes will be written */
+    OPJ_BYTE * l_start_stream = 00;
+
+    /* preconditions */
+    assert(p_stream != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    l_start_stream = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
+
+    /* write SOC identifier */
+    opj_write_bytes(l_start_stream, J2K_MS_SOC, 2);
+
+    if (opj_stream_write_data(p_stream, l_start_stream, 2, p_manager) != 2) {
+        return OPJ_FALSE;
+    }
+
+    /* UniPG>> */
+#ifdef USE_JPWL
+    /* update markers struct */
+    /*
+            OPJ_BOOL res = j2k_add_marker(p_j2k->cstr_info, J2K_MS_SOC, p_stream_tell(p_stream) - 2, 2);
+    */
+    assert(0 && "TODO");
+#endif /* USE_JPWL */
+    /* <<UniPG */
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a SOC marker (Start of Codestream)
+ * @param       p_j2k           the jpeg2000 file codec.
+ * @param       p_stream        FIXME DOC
+ * @param       p_manager       the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_soc(opj_j2k_t *p_j2k,
+                                 opj_stream_private_t *p_stream,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    OPJ_BYTE l_data [2];
+    OPJ_UINT32 l_marker;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    if (opj_stream_read_data(p_stream, l_data, 2, p_manager) != 2) {
+        return OPJ_FALSE;
+    }
+
+    opj_read_bytes(l_data, &l_marker, 2);
+    if (l_marker != J2K_MS_SOC) {
+        return OPJ_FALSE;
+    }
+
+    /* Next marker should be a SIZ marker in the main header */
+    p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_MHSIZ;
+
+    /* FIXME move it in a index structure included in p_j2k*/
+    p_j2k->cstr_index->main_head_start = opj_stream_tell(p_stream) - 2;
+
+    opj_event_msg(p_manager, EVT_INFO,
+                  "Start to read j2k main header (%" PRId64 ").\n",
+                  p_j2k->cstr_index->main_head_start);
+
+    /* Add the marker to the codestream index*/
+    if (OPJ_FALSE == opj_j2k_add_mhmarker(p_j2k->cstr_index, J2K_MS_SOC,
+                                          p_j2k->cstr_index->main_head_start, 2)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to add mh marker\n");
+        return OPJ_FALSE;
+    }
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_siz(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager)
+{
+    OPJ_UINT32 i;
+    OPJ_UINT32 l_size_len;
+    OPJ_BYTE * l_current_ptr;
+    opj_image_t * l_image = 00;
+    opj_cp_t *cp = 00;
+    opj_image_comp_t * l_img_comp = 00;
+
+    /* preconditions */
+    assert(p_stream != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    l_image = p_j2k->m_private_image;
+    cp = &(p_j2k->m_cp);
+    l_size_len = 40 + 3 * l_image->numcomps;
+    l_img_comp = l_image->comps;
+
+    if (l_size_len > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
+
+        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
+                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_size_len);
+        if (! new_header_tile_data) {
+            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory for the SIZ marker\n");
+            return OPJ_FALSE;
+        }
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_size_len;
+    }
+
+    l_current_ptr = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
+
+    /* write SOC identifier */
+    opj_write_bytes(l_current_ptr, J2K_MS_SIZ, 2);  /* SIZ */
+    l_current_ptr += 2;
+
+    opj_write_bytes(l_current_ptr, l_size_len - 2, 2); /* L_SIZ */
+    l_current_ptr += 2;
+
+    opj_write_bytes(l_current_ptr, cp->rsiz, 2);    /* Rsiz (capabilities) */
+    l_current_ptr += 2;
+
+    opj_write_bytes(l_current_ptr, l_image->x1, 4); /* Xsiz */
+    l_current_ptr += 4;
+
+    opj_write_bytes(l_current_ptr, l_image->y1, 4); /* Ysiz */
+    l_current_ptr += 4;
+
+    opj_write_bytes(l_current_ptr, l_image->x0, 4); /* X0siz */
+    l_current_ptr += 4;
+
+    opj_write_bytes(l_current_ptr, l_image->y0, 4); /* Y0siz */
+    l_current_ptr += 4;
+
+    opj_write_bytes(l_current_ptr, cp->tdx, 4);             /* XTsiz */
+    l_current_ptr += 4;
+
+    opj_write_bytes(l_current_ptr, cp->tdy, 4);             /* YTsiz */
+    l_current_ptr += 4;
+
+    opj_write_bytes(l_current_ptr, cp->tx0, 4);             /* XT0siz */
+    l_current_ptr += 4;
+
+    opj_write_bytes(l_current_ptr, cp->ty0, 4);             /* YT0siz */
+    l_current_ptr += 4;
+
+    opj_write_bytes(l_current_ptr, l_image->numcomps, 2);   /* Csiz */
+    l_current_ptr += 2;
+
+    for (i = 0; i < l_image->numcomps; ++i) {
+        /* TODO here with MCT ? */
+        opj_write_bytes(l_current_ptr, l_img_comp->prec - 1 + (l_img_comp->sgnd << 7),
+                        1);      /* Ssiz_i */
+        ++l_current_ptr;
+
+        opj_write_bytes(l_current_ptr, l_img_comp->dx, 1);      /* XRsiz_i */
+        ++l_current_ptr;
+
+        opj_write_bytes(l_current_ptr, l_img_comp->dy, 1);      /* YRsiz_i */
+        ++l_current_ptr;
+
+        ++l_img_comp;
+    }
+
+    if (opj_stream_write_data(p_stream,
+                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_size_len,
+                              p_manager) != l_size_len) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a SIZ marker (image and tile size)
+ * @param       p_j2k           the jpeg2000 file codec.
+ * @param       p_header_data   the data contained in the SIZ box.
+ * @param       p_header_size   the size of the data contained in the SIZ marker.
+ * @param       p_manager       the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_siz(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    OPJ_UINT32 i;
+    OPJ_UINT32 l_nb_comp;
+    OPJ_UINT32 l_nb_comp_remain;
+    OPJ_UINT32 l_remaining_size;
+    OPJ_UINT32 l_nb_tiles;
+    OPJ_UINT32 l_tmp, l_tx1, l_ty1;
+    OPJ_UINT32 l_prec0, l_sgnd0;
+    opj_image_t *l_image = 00;
+    opj_cp_t *l_cp = 00;
+    opj_image_comp_t * l_img_comp = 00;
+    opj_tcp_t * l_current_tile_param = 00;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_header_data != 00);
+
+    l_image = p_j2k->m_private_image;
+    l_cp = &(p_j2k->m_cp);
+
+    /* minimum size == 39 - 3 (= minimum component parameter) */
+    if (p_header_size < 36) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error with SIZ marker size\n");
+        return OPJ_FALSE;
+    }
+
+    l_remaining_size = p_header_size - 36;
+    l_nb_comp = l_remaining_size / 3;
+    l_nb_comp_remain = l_remaining_size % 3;
+    if (l_nb_comp_remain != 0) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error with SIZ marker size\n");
+        return OPJ_FALSE;
+    }
+
+    opj_read_bytes(p_header_data, &l_tmp,
+                   2);                                                /* Rsiz (capabilities) */
+    p_header_data += 2;
+    l_cp->rsiz = (OPJ_UINT16) l_tmp;
+    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_image->x1, 4);   /* Xsiz */
+    p_header_data += 4;
+    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_image->y1, 4);   /* Ysiz */
+    p_header_data += 4;
+    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_image->x0, 4);   /* X0siz */
+    p_header_data += 4;
+    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_image->y0, 4);   /* Y0siz */
+    p_header_data += 4;
+    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_cp->tdx,
+                   4);             /* XTsiz */
+    p_header_data += 4;
+    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_cp->tdy,
+                   4);             /* YTsiz */
+    p_header_data += 4;
+    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_cp->tx0,
+                   4);             /* XT0siz */
+    p_header_data += 4;
+    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_cp->ty0,
+                   4);             /* YT0siz */
+    p_header_data += 4;
+    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_tmp,
+                   2);                 /* Csiz */
+    p_header_data += 2;
+    if (l_tmp < 16385) {
+        l_image->numcomps = (OPJ_UINT16) l_tmp;
+    } else {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Error with SIZ marker: number of component is illegal -> %d\n", l_tmp);
+        return OPJ_FALSE;
+    }
+
+    if (l_image->numcomps != l_nb_comp) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Error with SIZ marker: number of component is not compatible with the remaining number of parameters ( %d vs %d)\n",
+                      l_image->numcomps, l_nb_comp);
+        return OPJ_FALSE;
+    }
+
+    /* testcase 4035.pdf.SIGSEGV.d8b.3375 */
+    /* testcase issue427-null-image-size.jp2 */
+    if ((l_image->x0 >= l_image->x1) || (l_image->y0 >= l_image->y1)) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Error with SIZ marker: negative or zero image size (%" PRId64 " x %" PRId64
+                      ")\n", (OPJ_INT64)l_image->x1 - l_image->x0,
+                      (OPJ_INT64)l_image->y1 - l_image->y0);
+        return OPJ_FALSE;
+    }
+    /* testcase 2539.pdf.SIGFPE.706.1712 (also 3622.pdf.SIGFPE.706.2916 and 4008.pdf.SIGFPE.706.3345 and maybe more) */
+    if ((l_cp->tdx == 0U) || (l_cp->tdy == 0U)) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Error with SIZ marker: invalid tile size (tdx: %d, tdy: %d)\n", l_cp->tdx,
+                      l_cp->tdy);
+        return OPJ_FALSE;
+    }
+
+    /* testcase issue427-illegal-tile-offset.jp2 */
+    l_tx1 = opj_uint_adds(l_cp->tx0, l_cp->tdx); /* manage overflow */
+    l_ty1 = opj_uint_adds(l_cp->ty0, l_cp->tdy); /* manage overflow */
+    if ((l_cp->tx0 > l_image->x0) || (l_cp->ty0 > l_image->y0) ||
+            (l_tx1 <= l_image->x0) || (l_ty1 <= l_image->y0)) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Error with SIZ marker: illegal tile offset\n");
+        return OPJ_FALSE;
+    }
+    if (!p_j2k->dump_state) {
+        OPJ_UINT32 siz_w, siz_h;
+
+        siz_w = l_image->x1 - l_image->x0;
+        siz_h = l_image->y1 - l_image->y0;
+
+        if (p_j2k->ihdr_w > 0 && p_j2k->ihdr_h > 0
+                && (p_j2k->ihdr_w != siz_w || p_j2k->ihdr_h != siz_h)) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Error with SIZ marker: IHDR w(%u) h(%u) vs. SIZ w(%u) h(%u)\n", p_j2k->ihdr_w,
+                          p_j2k->ihdr_h, siz_w, siz_h);
+            return OPJ_FALSE;
+        }
+    }
+#ifdef USE_JPWL
+    if (l_cp->correct) {
+        /* if JPWL is on, we check whether TX errors have damaged
+          too much the SIZ parameters */
+        if (!(l_image->x1 * l_image->y1)) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "JPWL: bad image size (%d x %d)\n",
+                          l_image->x1, l_image->y1);
+            if (!JPWL_ASSUME) {
+                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
+                return OPJ_FALSE;
+            }
+        }
+
+        /* FIXME check previously in the function so why keep this piece of code ? Need by the norm ?
+                if (l_image->numcomps != ((len - 38) / 3)) {
+                        opj_event_msg(p_manager, JPWL_ASSUME ? EVT_WARNING : EVT_ERROR,
+                                "JPWL: Csiz is %d => space in SIZ only for %d comps.!!!\n",
+                                l_image->numcomps, ((len - 38) / 3));
+                        if (!JPWL_ASSUME) {
+                                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
+                                return OPJ_FALSE;
+                        }
+        */              /* we try to correct */
+        /*              opj_event_msg(p_manager, EVT_WARNING, "- trying to adjust this\n");
+                        if (l_image->numcomps < ((len - 38) / 3)) {
+                                len = 38 + 3 * l_image->numcomps;
+                                opj_event_msg(p_manager, EVT_WARNING, "- setting Lsiz to %d => HYPOTHESIS!!!\n",
+                                        len);
+                        } else {
+                                l_image->numcomps = ((len - 38) / 3);
+                                opj_event_msg(p_manager, EVT_WARNING, "- setting Csiz to %d => HYPOTHESIS!!!\n",
+                                        l_image->numcomps);
+                        }
+                }
+        */
+
+        /* update components number in the jpwl_exp_comps filed */
+        l_cp->exp_comps = l_image->numcomps;
+    }
+#endif /* USE_JPWL */
+
+    /* Allocate the resulting image components */
+    l_image->comps = (opj_image_comp_t*) opj_calloc(l_image->numcomps,
+                     sizeof(opj_image_comp_t));
+    if (l_image->comps == 00) {
+        l_image->numcomps = 0;
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Not enough memory to take in charge SIZ marker\n");
+        return OPJ_FALSE;
+    }
+
+    l_img_comp = l_image->comps;
+
+    l_prec0 = 0;
+    l_sgnd0 = 0;
+    /* Read the component information */
+    for (i = 0; i < l_image->numcomps; ++i) {
+        OPJ_UINT32 tmp;
+        opj_read_bytes(p_header_data, &tmp, 1); /* Ssiz_i */
+        ++p_header_data;
+        l_img_comp->prec = (tmp & 0x7f) + 1;
+        l_img_comp->sgnd = tmp >> 7;
+
+        if (p_j2k->dump_state == 0) {
+            if (i == 0) {
+                l_prec0 = l_img_comp->prec;
+                l_sgnd0 = l_img_comp->sgnd;
+            } else if (!l_cp->allow_different_bit_depth_sign
+                       && (l_img_comp->prec != l_prec0 || l_img_comp->sgnd != l_sgnd0)) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "Despite JP2 BPC!=255, precision and/or sgnd values for comp[%d] is different than comp[0]:\n"
+                              "        [0] prec(%d) sgnd(%d) [%d] prec(%d) sgnd(%d)\n", i, l_prec0, l_sgnd0,
+                              i, l_img_comp->prec, l_img_comp->sgnd);
+            }
+            /* TODO: we should perhaps also check against JP2 BPCC values */
+        }
+        opj_read_bytes(p_header_data, &tmp, 1); /* XRsiz_i */
+        ++p_header_data;
+        l_img_comp->dx = (OPJ_UINT32)tmp; /* should be between 1 and 255 */
+        opj_read_bytes(p_header_data, &tmp, 1); /* YRsiz_i */
+        ++p_header_data;
+        l_img_comp->dy = (OPJ_UINT32)tmp; /* should be between 1 and 255 */
+        if (l_img_comp->dx < 1 || l_img_comp->dx > 255 ||
+                l_img_comp->dy < 1 || l_img_comp->dy > 255) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Invalid values for comp = %d : dx=%u dy=%u (should be between 1 and 255 according to the JPEG2000 norm)\n",
+                          i, l_img_comp->dx, l_img_comp->dy);
+            return OPJ_FALSE;
+        }
+        /* Avoids later undefined shift in computation of */
+        /* p_j2k->m_specific_param.m_decoder.m_default_tcp->tccps[i].m_dc_level_shift = 1
+                    << (l_image->comps[i].prec - 1); */
+        if (l_img_comp->prec > 31) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Invalid values for comp = %d : prec=%u (should be between 1 and 38 according to the JPEG2000 norm. OpenJpeg only supports up to 31)\n",
+                          i, l_img_comp->prec);
+            return OPJ_FALSE;
+        }
+#ifdef USE_JPWL
+        if (l_cp->correct) {
+            /* if JPWL is on, we check whether TX errors have damaged
+                    too much the SIZ parameters, again */
+            if (!(l_image->comps[i].dx * l_image->comps[i].dy)) {
+                opj_event_msg(p_manager, JPWL_ASSUME ? EVT_WARNING : EVT_ERROR,
+                              "JPWL: bad XRsiz_%d/YRsiz_%d (%d x %d)\n",
+                              i, i, l_image->comps[i].dx, l_image->comps[i].dy);
+                if (!JPWL_ASSUME) {
+                    opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
+                    return OPJ_FALSE;
+                }
+                /* we try to correct */
+                opj_event_msg(p_manager, EVT_WARNING, "- trying to adjust them\n");
+                if (!l_image->comps[i].dx) {
+                    l_image->comps[i].dx = 1;
+                    opj_event_msg(p_manager, EVT_WARNING,
+                                  "- setting XRsiz_%d to %d => HYPOTHESIS!!!\n",
+                                  i, l_image->comps[i].dx);
+                }
+                if (!l_image->comps[i].dy) {
+                    l_image->comps[i].dy = 1;
+                    opj_event_msg(p_manager, EVT_WARNING,
+                                  "- setting YRsiz_%d to %d => HYPOTHESIS!!!\n",
+                                  i, l_image->comps[i].dy);
+                }
+            }
+        }
+#endif /* USE_JPWL */
+        l_img_comp->resno_decoded =
+            0;                                                          /* number of resolution decoded */
+        l_img_comp->factor =
+            l_cp->m_specific_param.m_dec.m_reduce; /* reducing factor per component */
+        ++l_img_comp;
+    }
+
+    if (l_cp->tdx == 0 || l_cp->tdy == 0) {
+        return OPJ_FALSE;
+    }
+
+    /* Compute the number of tiles */
+    l_cp->tw = opj_uint_ceildiv(l_image->x1 - l_cp->tx0, l_cp->tdx);
+    l_cp->th = opj_uint_ceildiv(l_image->y1 - l_cp->ty0, l_cp->tdy);
+
+    /* Check that the number of tiles is valid */
+    if (l_cp->tw == 0 || l_cp->th == 0 || l_cp->tw > 65535 / l_cp->th) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Invalid number of tiles : %u x %u (maximum fixed by jpeg2000 norm is 65535 tiles)\n",
+                      l_cp->tw, l_cp->th);
+        return OPJ_FALSE;
+    }
+    l_nb_tiles = l_cp->tw * l_cp->th;
+
+    /* Define the tiles which will be decoded */
+    if (p_j2k->m_specific_param.m_decoder.m_discard_tiles) {
+        p_j2k->m_specific_param.m_decoder.m_start_tile_x =
+            (p_j2k->m_specific_param.m_decoder.m_start_tile_x - l_cp->tx0) / l_cp->tdx;
+        p_j2k->m_specific_param.m_decoder.m_start_tile_y =
+            (p_j2k->m_specific_param.m_decoder.m_start_tile_y - l_cp->ty0) / l_cp->tdy;
+        p_j2k->m_specific_param.m_decoder.m_end_tile_x = opj_uint_ceildiv(
+            p_j2k->m_specific_param.m_decoder.m_end_tile_x - l_cp->tx0, l_cp->tdx);
+        p_j2k->m_specific_param.m_decoder.m_end_tile_y = opj_uint_ceildiv(
+            p_j2k->m_specific_param.m_decoder.m_end_tile_y - l_cp->ty0, l_cp->tdy);
+    } else {
+        p_j2k->m_specific_param.m_decoder.m_start_tile_x = 0;
+        p_j2k->m_specific_param.m_decoder.m_start_tile_y = 0;
+        p_j2k->m_specific_param.m_decoder.m_end_tile_x = l_cp->tw;
+        p_j2k->m_specific_param.m_decoder.m_end_tile_y = l_cp->th;
+    }
+
+#ifdef USE_JPWL
+    if (l_cp->correct) {
+        /* if JPWL is on, we check whether TX errors have damaged
+          too much the SIZ parameters */
+        if ((l_cp->tw < 1) || (l_cp->th < 1) || (l_cp->tw > l_cp->max_tiles) ||
+                (l_cp->th > l_cp->max_tiles)) {
+            opj_event_msg(p_manager, JPWL_ASSUME ? EVT_WARNING : EVT_ERROR,
+                          "JPWL: bad number of tiles (%d x %d)\n",
+                          l_cp->tw, l_cp->th);
+            if (!JPWL_ASSUME) {
+                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
+                return OPJ_FALSE;
+            }
+            /* we try to correct */
+            opj_event_msg(p_manager, EVT_WARNING, "- trying to adjust them\n");
+            if (l_cp->tw < 1) {
+                l_cp->tw = 1;
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "- setting %d tiles in x => HYPOTHESIS!!!\n",
+                              l_cp->tw);
+            }
+            if (l_cp->tw > l_cp->max_tiles) {
+                l_cp->tw = 1;
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "- too large x, increase expectance of %d\n"
+                              "- setting %d tiles in x => HYPOTHESIS!!!\n",
+                              l_cp->max_tiles, l_cp->tw);
+            }
+            if (l_cp->th < 1) {
+                l_cp->th = 1;
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "- setting %d tiles in y => HYPOTHESIS!!!\n",
+                              l_cp->th);
+            }
+            if (l_cp->th > l_cp->max_tiles) {
+                l_cp->th = 1;
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "- too large y, increase expectance of %d to continue\n",
+                              "- setting %d tiles in y => HYPOTHESIS!!!\n",
+                              l_cp->max_tiles, l_cp->th);
+            }
+        }
+    }
+#endif /* USE_JPWL */
+
+    /* memory allocations */
+    l_cp->tcps = (opj_tcp_t*) opj_calloc(l_nb_tiles, sizeof(opj_tcp_t));
+    if (l_cp->tcps == 00) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Not enough memory to take in charge SIZ marker\n");
+        return OPJ_FALSE;
+    }
+
+#ifdef USE_JPWL
+    if (l_cp->correct) {
+        if (!l_cp->tcps) {
+            opj_event_msg(p_manager, JPWL_ASSUME ? EVT_WARNING : EVT_ERROR,
+                          "JPWL: could not alloc tcps field of cp\n");
+            if (!JPWL_ASSUME) {
+                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
+                return OPJ_FALSE;
+            }
+        }
+    }
+#endif /* USE_JPWL */
+
+    p_j2k->m_specific_param.m_decoder.m_default_tcp->tccps =
+        (opj_tccp_t*) opj_calloc(l_image->numcomps, sizeof(opj_tccp_t));
+    if (p_j2k->m_specific_param.m_decoder.m_default_tcp->tccps  == 00) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Not enough memory to take in charge SIZ marker\n");
+        return OPJ_FALSE;
+    }
+
+    p_j2k->m_specific_param.m_decoder.m_default_tcp->m_mct_records =
+        (opj_mct_data_t*)opj_calloc(OPJ_J2K_MCT_DEFAULT_NB_RECORDS,
+                                    sizeof(opj_mct_data_t));
+
+    if (! p_j2k->m_specific_param.m_decoder.m_default_tcp->m_mct_records) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Not enough memory to take in charge SIZ marker\n");
+        return OPJ_FALSE;
+    }
+    p_j2k->m_specific_param.m_decoder.m_default_tcp->m_nb_max_mct_records =
+        OPJ_J2K_MCT_DEFAULT_NB_RECORDS;
+
+    p_j2k->m_specific_param.m_decoder.m_default_tcp->m_mcc_records =
+        (opj_simple_mcc_decorrelation_data_t*)
+        opj_calloc(OPJ_J2K_MCC_DEFAULT_NB_RECORDS,
+                   sizeof(opj_simple_mcc_decorrelation_data_t));
+
+    if (! p_j2k->m_specific_param.m_decoder.m_default_tcp->m_mcc_records) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Not enough memory to take in charge SIZ marker\n");
+        return OPJ_FALSE;
+    }
+    p_j2k->m_specific_param.m_decoder.m_default_tcp->m_nb_max_mcc_records =
+        OPJ_J2K_MCC_DEFAULT_NB_RECORDS;
+
+    /* set up default dc level shift */
+    for (i = 0; i < l_image->numcomps; ++i) {
+        if (! l_image->comps[i].sgnd) {
+            p_j2k->m_specific_param.m_decoder.m_default_tcp->tccps[i].m_dc_level_shift = 1
+                    << (l_image->comps[i].prec - 1);
+        }
+    }
+
+    l_current_tile_param = l_cp->tcps;
+    for (i = 0; i < l_nb_tiles; ++i) {
+        l_current_tile_param->tccps = (opj_tccp_t*) opj_calloc(l_image->numcomps,
+                                      sizeof(opj_tccp_t));
+        if (l_current_tile_param->tccps == 00) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Not enough memory to take in charge SIZ marker\n");
+            return OPJ_FALSE;
+        }
+
+        ++l_current_tile_param;
+    }
+
+    p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_MH;
+    opj_image_comp_header_update(l_image, l_cp);
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_com(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager
+                                 )
+{
+    OPJ_UINT32 l_comment_size;
+    OPJ_UINT32 l_total_com_size;
+    const OPJ_CHAR *l_comment;
+    OPJ_BYTE * l_current_ptr = 00;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_stream != 00);
+    assert(p_manager != 00);
+
+    l_comment = p_j2k->m_cp.comment;
+    l_comment_size = (OPJ_UINT32)strlen(l_comment);
+    l_total_com_size = l_comment_size + 6;
+
+    if (l_total_com_size >
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
+        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
+                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_total_com_size);
+        if (! new_header_tile_data) {
+            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Not enough memory to write the COM marker\n");
+            return OPJ_FALSE;
+        }
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_total_com_size;
+    }
+
+    l_current_ptr = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
+
+    opj_write_bytes(l_current_ptr, J2K_MS_COM, 2);  /* COM */
+    l_current_ptr += 2;
+
+    opj_write_bytes(l_current_ptr, l_total_com_size - 2, 2);        /* L_COM */
+    l_current_ptr += 2;
+
+    opj_write_bytes(l_current_ptr, 1,
+                    2);   /* General use (IS 8859-15:1999 (Latin) values) */
+    l_current_ptr += 2;
+
+    memcpy(l_current_ptr, l_comment, l_comment_size);
+
+    if (opj_stream_write_data(p_stream,
+                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_total_com_size,
+                              p_manager) != l_total_com_size) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a COM marker (comments)
+ * @param       p_j2k           the jpeg2000 file codec.
+ * @param       p_header_data   the data contained in the COM box.
+ * @param       p_header_size   the size of the data contained in the COM marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_com(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_header_data != 00);
+
+    OPJ_UNUSED(p_j2k);
+    OPJ_UNUSED(p_header_data);
+    OPJ_UNUSED(p_header_size);
+    OPJ_UNUSED(p_manager);
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_cod(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager)
+{
+    opj_cp_t *l_cp = 00;
+    opj_tcp_t *l_tcp = 00;
+    OPJ_UINT32 l_code_size, l_remaining_size;
+    OPJ_BYTE * l_current_data = 00;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    l_cp = &(p_j2k->m_cp);
+    l_tcp = &l_cp->tcps[p_j2k->m_current_tile_number];
+    l_code_size = 9 + opj_j2k_get_SPCod_SPCoc_size(p_j2k,
+                  p_j2k->m_current_tile_number, 0);
+    l_remaining_size = l_code_size;
+
+    if (l_code_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
+        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
+                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_code_size);
+        if (! new_header_tile_data) {
+            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write COD marker\n");
+            return OPJ_FALSE;
+        }
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_code_size;
+    }
+
+    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
+
+    opj_write_bytes(l_current_data, J2K_MS_COD, 2);           /* COD */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, l_code_size - 2, 2);      /* L_COD */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, l_tcp->csty, 1);          /* Scod */
+    ++l_current_data;
+
+    opj_write_bytes(l_current_data, (OPJ_UINT32)l_tcp->prg, 1); /* SGcod (A) */
+    ++l_current_data;
+
+    opj_write_bytes(l_current_data, l_tcp->numlayers, 2);     /* SGcod (B) */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, l_tcp->mct, 1);           /* SGcod (C) */
+    ++l_current_data;
+
+    l_remaining_size -= 9;
+
+    if (! opj_j2k_write_SPCod_SPCoc(p_j2k, p_j2k->m_current_tile_number, 0,
+                                    l_current_data, &l_remaining_size, p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error writing COD marker\n");
+        return OPJ_FALSE;
+    }
+
+    if (l_remaining_size != 0) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error writing COD marker\n");
+        return OPJ_FALSE;
+    }
+
+    if (opj_stream_write_data(p_stream,
+                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_code_size,
+                              p_manager) != l_code_size) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a COD marker (Coding style defaults)
+ * @param       p_header_data   the data contained in the COD box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the COD marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_cod(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    /* loop */
+    OPJ_UINT32 i;
+    OPJ_UINT32 l_tmp;
+    opj_cp_t *l_cp = 00;
+    opj_tcp_t *l_tcp = 00;
+    opj_image_t *l_image = 00;
+
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    l_image = p_j2k->m_private_image;
+    l_cp = &(p_j2k->m_cp);
+
+    /* If we are in the first tile-part header of the current tile */
+    l_tcp = (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH) ?
+            &l_cp->tcps[p_j2k->m_current_tile_number] :
+            p_j2k->m_specific_param.m_decoder.m_default_tcp;
+
+#if 0
+    /* This check was added per https://github.com/uclouvain/openjpeg/commit/daed8cc9195555e101ab708a501af2dfe6d5e001 */
+    /* but this is no longer necessary to handle issue476.jp2 */
+    /* and this actually cause issues on legit files. See https://github.com/uclouvain/openjpeg/issues/1043 */
+    /* Only one COD per tile */
+    if (l_tcp->cod) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "COD marker already read. No more than one COD marker per tile.\n");
+        return OPJ_FALSE;
+    }
+#endif
+    l_tcp->cod = 1;
+
+    /* Make sure room is sufficient */
+    if (p_header_size < 5) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading COD marker\n");
+        return OPJ_FALSE;
+    }
+
+    opj_read_bytes(p_header_data, &l_tcp->csty, 1);         /* Scod */
+    ++p_header_data;
+    /* Make sure we know how to decode this */
+    if ((l_tcp->csty & ~(OPJ_UINT32)(J2K_CP_CSTY_PRT | J2K_CP_CSTY_SOP |
+                                     J2K_CP_CSTY_EPH)) != 0U) {
+        opj_event_msg(p_manager, EVT_ERROR, "Unknown Scod value in COD marker\n");
+        return OPJ_FALSE;
+    }
+    opj_read_bytes(p_header_data, &l_tmp, 1);                       /* SGcod (A) */
+    ++p_header_data;
+    l_tcp->prg = (OPJ_PROG_ORDER) l_tmp;
+    /* Make sure progression order is valid */
+    if (l_tcp->prg > OPJ_CPRL) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Unknown progression order in COD marker\n");
+        l_tcp->prg = OPJ_PROG_UNKNOWN;
+    }
+    opj_read_bytes(p_header_data, &l_tcp->numlayers, 2);    /* SGcod (B) */
+    p_header_data += 2;
+
+    if ((l_tcp->numlayers < 1U) || (l_tcp->numlayers > 65535U)) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Invalid number of layers in COD marker : %d not in range [1-65535]\n",
+                      l_tcp->numlayers);
+        return OPJ_FALSE;
+    }
+
+    /* If user didn't set a number layer to decode take the max specify in the codestream. */
+    if (l_cp->m_specific_param.m_dec.m_layer) {
+        l_tcp->num_layers_to_decode = l_cp->m_specific_param.m_dec.m_layer;
+    } else {
+        l_tcp->num_layers_to_decode = l_tcp->numlayers;
+    }
+
+    opj_read_bytes(p_header_data, &l_tcp->mct, 1);          /* SGcod (C) */
+    ++p_header_data;
+
+    if (l_tcp->mct > 1) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Invalid multiple component transformation\n");
+        return OPJ_FALSE;
+    }
+
+    p_header_size -= 5;
+    for (i = 0; i < l_image->numcomps; ++i) {
+        l_tcp->tccps[i].csty = l_tcp->csty & J2K_CCP_CSTY_PRT;
+    }
+
+    if (! opj_j2k_read_SPCod_SPCoc(p_j2k, 0, p_header_data, &p_header_size,
+                                   p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading COD marker\n");
+        return OPJ_FALSE;
+    }
+
+    if (p_header_size != 0) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading COD marker\n");
+        return OPJ_FALSE;
+    }
+
+    /* Apply the coding style to other components of the current tile or the m_default_tcp*/
+    opj_j2k_copy_tile_component_parameters(p_j2k);
+
+    /* Index */
+#ifdef WIP_REMOVE_MSD
+    if (p_j2k->cstr_info) {
+        /*opj_codestream_info_t *l_cstr_info = p_j2k->cstr_info;*/
+        p_j2k->cstr_info->prog = l_tcp->prg;
+        p_j2k->cstr_info->numlayers = l_tcp->numlayers;
+        p_j2k->cstr_info->numdecompos = (OPJ_INT32*) opj_malloc(
+                                            l_image->numcomps * sizeof(OPJ_UINT32));
+        if (!p_j2k->cstr_info->numdecompos) {
+            return OPJ_FALSE;
+        }
+        for (i = 0; i < l_image->numcomps; ++i) {
+            p_j2k->cstr_info->numdecompos[i] = l_tcp->tccps[i].numresolutions - 1;
+        }
+    }
+#endif
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_coc(opj_j2k_t *p_j2k,
+                                  OPJ_UINT32 p_comp_no,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager)
+{
+    OPJ_UINT32 l_coc_size, l_remaining_size;
+    OPJ_UINT32 l_comp_room;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    l_comp_room = (p_j2k->m_private_image->numcomps <= 256) ? 1 : 2;
+
+    l_coc_size = 5 + l_comp_room + opj_j2k_get_SPCod_SPCoc_size(p_j2k,
+                 p_j2k->m_current_tile_number, p_comp_no);
+
+    if (l_coc_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
+        OPJ_BYTE *new_header_tile_data;
+        /*p_j2k->m_specific_param.m_encoder.m_header_tile_data
+                = (OPJ_BYTE*)opj_realloc(
+                        p_j2k->m_specific_param.m_encoder.m_header_tile_data,
+                        l_coc_size);*/
+
+        new_header_tile_data = (OPJ_BYTE *) opj_realloc(
+                                   p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_coc_size);
+        if (! new_header_tile_data) {
+            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write COC marker\n");
+            return OPJ_FALSE;
+        }
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_coc_size;
+    }
+
+    opj_j2k_write_coc_in_memory(p_j2k, p_comp_no,
+                                p_j2k->m_specific_param.m_encoder.m_header_tile_data, &l_remaining_size,
+                                p_manager);
+
+    if (opj_stream_write_data(p_stream,
+                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_coc_size,
+                              p_manager) != l_coc_size) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_compare_coc(opj_j2k_t *p_j2k,
+                                    OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no)
+{
+    opj_cp_t *l_cp = NULL;
+    opj_tcp_t *l_tcp = NULL;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+
+    l_cp = &(p_j2k->m_cp);
+    l_tcp = &l_cp->tcps[p_j2k->m_current_tile_number];
+
+    if (l_tcp->tccps[p_first_comp_no].csty != l_tcp->tccps[p_second_comp_no].csty) {
+        return OPJ_FALSE;
+    }
+
+
+    return opj_j2k_compare_SPCod_SPCoc(p_j2k, p_j2k->m_current_tile_number,
+                                       p_first_comp_no, p_second_comp_no);
+}
+
+static void opj_j2k_write_coc_in_memory(opj_j2k_t *p_j2k,
+                                        OPJ_UINT32 p_comp_no,
+                                        OPJ_BYTE * p_data,
+                                        OPJ_UINT32 * p_data_written,
+                                        opj_event_mgr_t * p_manager
+                                       )
+{
+    opj_cp_t *l_cp = 00;
+    opj_tcp_t *l_tcp = 00;
+    OPJ_UINT32 l_coc_size, l_remaining_size;
+    OPJ_BYTE * l_current_data = 00;
+    opj_image_t *l_image = 00;
+    OPJ_UINT32 l_comp_room;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    l_cp = &(p_j2k->m_cp);
+    l_tcp = &l_cp->tcps[p_j2k->m_current_tile_number];
+    l_image = p_j2k->m_private_image;
+    l_comp_room = (l_image->numcomps <= 256) ? 1 : 2;
+
+    l_coc_size = 5 + l_comp_room + opj_j2k_get_SPCod_SPCoc_size(p_j2k,
+                 p_j2k->m_current_tile_number, p_comp_no);
+    l_remaining_size = l_coc_size;
+
+    l_current_data = p_data;
+
+    opj_write_bytes(l_current_data, J2K_MS_COC,
+                    2);                         /* COC */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, l_coc_size - 2,
+                    2);                     /* L_COC */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, p_comp_no, l_comp_room);        /* Ccoc */
+    l_current_data += l_comp_room;
+
+    opj_write_bytes(l_current_data, l_tcp->tccps[p_comp_no].csty,
+                    1);               /* Scoc */
+    ++l_current_data;
+
+    l_remaining_size -= (5 + l_comp_room);
+    opj_j2k_write_SPCod_SPCoc(p_j2k, p_j2k->m_current_tile_number, 0,
+                              l_current_data, &l_remaining_size, p_manager);
+    * p_data_written = l_coc_size;
+}
+
+static OPJ_UINT32 opj_j2k_get_max_coc_size(opj_j2k_t *p_j2k)
+{
+    OPJ_UINT32 i, j;
+    OPJ_UINT32 l_nb_comp;
+    OPJ_UINT32 l_nb_tiles;
+    OPJ_UINT32 l_max = 0;
+
+    /* preconditions */
+
+    l_nb_tiles = p_j2k->m_cp.tw * p_j2k->m_cp.th ;
+    l_nb_comp = p_j2k->m_private_image->numcomps;
+
+    for (i = 0; i < l_nb_tiles; ++i) {
+        for (j = 0; j < l_nb_comp; ++j) {
+            l_max = opj_uint_max(l_max, opj_j2k_get_SPCod_SPCoc_size(p_j2k, i, j));
+        }
+    }
+
+    return 6 + l_max;
+}
+
+/**
+ * Reads a COC marker (Coding Style Component)
+ * @param       p_header_data   the data contained in the COC box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the COC marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_coc(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    opj_cp_t *l_cp = NULL;
+    opj_tcp_t *l_tcp = NULL;
+    opj_image_t *l_image = NULL;
+    OPJ_UINT32 l_comp_room;
+    OPJ_UINT32 l_comp_no;
+
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    l_cp = &(p_j2k->m_cp);
+    l_tcp = (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH)
+            ?
+            &l_cp->tcps[p_j2k->m_current_tile_number] :
+            p_j2k->m_specific_param.m_decoder.m_default_tcp;
+    l_image = p_j2k->m_private_image;
+
+    l_comp_room = l_image->numcomps <= 256 ? 1 : 2;
+
+    /* make sure room is sufficient*/
+    if (p_header_size < l_comp_room + 1) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading COC marker\n");
+        return OPJ_FALSE;
+    }
+    p_header_size -= l_comp_room + 1;
+
+    opj_read_bytes(p_header_data, &l_comp_no,
+                   l_comp_room);                 /* Ccoc */
+    p_header_data += l_comp_room;
+    if (l_comp_no >= l_image->numcomps) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Error reading COC marker (bad number of components)\n");
+        return OPJ_FALSE;
+    }
+
+    opj_read_bytes(p_header_data, &l_tcp->tccps[l_comp_no].csty,
+                   1);                  /* Scoc */
+    ++p_header_data ;
+
+    if (! opj_j2k_read_SPCod_SPCoc(p_j2k, l_comp_no, p_header_data, &p_header_size,
+                                   p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading COC marker\n");
+        return OPJ_FALSE;
+    }
+
+    if (p_header_size != 0) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading COC marker\n");
+        return OPJ_FALSE;
+    }
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_qcd(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager
+                                 )
+{
+    OPJ_UINT32 l_qcd_size, l_remaining_size;
+    OPJ_BYTE * l_current_data = 00;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    l_qcd_size = 4 + opj_j2k_get_SQcd_SQcc_size(p_j2k, p_j2k->m_current_tile_number,
+                 0);
+    l_remaining_size = l_qcd_size;
+
+    if (l_qcd_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
+        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
+                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_qcd_size);
+        if (! new_header_tile_data) {
+            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write QCD marker\n");
+            return OPJ_FALSE;
+        }
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_qcd_size;
+    }
+
+    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
+
+    opj_write_bytes(l_current_data, J2K_MS_QCD, 2);         /* QCD */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, l_qcd_size - 2, 2);     /* L_QCD */
+    l_current_data += 2;
+
+    l_remaining_size -= 4;
+
+    if (! opj_j2k_write_SQcd_SQcc(p_j2k, p_j2k->m_current_tile_number, 0,
+                                  l_current_data, &l_remaining_size, p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error writing QCD marker\n");
+        return OPJ_FALSE;
+    }
+
+    if (l_remaining_size != 0) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error writing QCD marker\n");
+        return OPJ_FALSE;
+    }
+
+    if (opj_stream_write_data(p_stream,
+                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_qcd_size,
+                              p_manager) != l_qcd_size) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a QCD marker (Quantization defaults)
+ * @param       p_header_data   the data contained in the QCD box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the QCD marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_qcd(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    if (! opj_j2k_read_SQcd_SQcc(p_j2k, 0, p_header_data, &p_header_size,
+                                 p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading QCD marker\n");
+        return OPJ_FALSE;
+    }
+
+    if (p_header_size != 0) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading QCD marker\n");
+        return OPJ_FALSE;
+    }
+
+    /* Apply the quantization parameters to other components of the current tile or the m_default_tcp */
+    opj_j2k_copy_tile_quantization_parameters(p_j2k);
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_qcc(opj_j2k_t *p_j2k,
+                                  OPJ_UINT32 p_comp_no,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager
+                                 )
+{
+    OPJ_UINT32 l_qcc_size, l_remaining_size;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    l_qcc_size = 5 + opj_j2k_get_SQcd_SQcc_size(p_j2k, p_j2k->m_current_tile_number,
+                 p_comp_no);
+    l_qcc_size += p_j2k->m_private_image->numcomps <= 256 ? 0 : 1;
+    l_remaining_size = l_qcc_size;
+
+    if (l_qcc_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
+        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
+                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_qcc_size);
+        if (! new_header_tile_data) {
+            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write QCC marker\n");
+            return OPJ_FALSE;
+        }
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_qcc_size;
+    }
+
+    opj_j2k_write_qcc_in_memory(p_j2k, p_comp_no,
+                                p_j2k->m_specific_param.m_encoder.m_header_tile_data, &l_remaining_size,
+                                p_manager);
+
+    if (opj_stream_write_data(p_stream,
+                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_qcc_size,
+                              p_manager) != l_qcc_size) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_compare_qcc(opj_j2k_t *p_j2k,
+                                    OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no)
+{
+    return opj_j2k_compare_SQcd_SQcc(p_j2k, p_j2k->m_current_tile_number,
+                                     p_first_comp_no, p_second_comp_no);
+}
+
+static void opj_j2k_write_qcc_in_memory(opj_j2k_t *p_j2k,
+                                        OPJ_UINT32 p_comp_no,
+                                        OPJ_BYTE * p_data,
+                                        OPJ_UINT32 * p_data_written,
+                                        opj_event_mgr_t * p_manager
+                                       )
+{
+    OPJ_UINT32 l_qcc_size, l_remaining_size;
+    OPJ_BYTE * l_current_data = 00;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    l_qcc_size = 6 + opj_j2k_get_SQcd_SQcc_size(p_j2k, p_j2k->m_current_tile_number,
+                 p_comp_no);
+    l_remaining_size = l_qcc_size;
+
+    l_current_data = p_data;
+
+    opj_write_bytes(l_current_data, J2K_MS_QCC, 2);         /* QCC */
+    l_current_data += 2;
+
+    if (p_j2k->m_private_image->numcomps <= 256) {
+        --l_qcc_size;
+
+        opj_write_bytes(l_current_data, l_qcc_size - 2, 2);     /* L_QCC */
+        l_current_data += 2;
+
+        opj_write_bytes(l_current_data, p_comp_no, 1);  /* Cqcc */
+        ++l_current_data;
+
+        /* in the case only one byte is sufficient the last byte allocated is useless -> still do -6 for available */
+        l_remaining_size -= 6;
+    } else {
+        opj_write_bytes(l_current_data, l_qcc_size - 2, 2);     /* L_QCC */
+        l_current_data += 2;
+
+        opj_write_bytes(l_current_data, p_comp_no, 2);  /* Cqcc */
+        l_current_data += 2;
+
+        l_remaining_size -= 6;
+    }
+
+    opj_j2k_write_SQcd_SQcc(p_j2k, p_j2k->m_current_tile_number, p_comp_no,
+                            l_current_data, &l_remaining_size, p_manager);
+
+    *p_data_written = l_qcc_size;
+}
+
+static OPJ_UINT32 opj_j2k_get_max_qcc_size(opj_j2k_t *p_j2k)
+{
+    return opj_j2k_get_max_coc_size(p_j2k);
+}
+
+/**
+ * Reads a QCC marker (Quantization component)
+ * @param       p_header_data   the data contained in the QCC box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the QCC marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_qcc(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    OPJ_UINT32 l_num_comp, l_comp_no;
+
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    l_num_comp = p_j2k->m_private_image->numcomps;
+
+    if (l_num_comp <= 256) {
+        if (p_header_size < 1) {
+            opj_event_msg(p_manager, EVT_ERROR, "Error reading QCC marker\n");
+            return OPJ_FALSE;
+        }
+        opj_read_bytes(p_header_data, &l_comp_no, 1);
+        ++p_header_data;
+        --p_header_size;
+    } else {
+        if (p_header_size < 2) {
+            opj_event_msg(p_manager, EVT_ERROR, "Error reading QCC marker\n");
+            return OPJ_FALSE;
+        }
+        opj_read_bytes(p_header_data, &l_comp_no, 2);
+        p_header_data += 2;
+        p_header_size -= 2;
+    }
+
+#ifdef USE_JPWL
+    if (p_j2k->m_cp.correct) {
+
+        static OPJ_UINT32 backup_compno = 0;
+
+        /* compno is negative or larger than the number of components!!! */
+        if (/*(l_comp_no < 0) ||*/ (l_comp_no >= l_num_comp)) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "JPWL: bad component number in QCC (%d out of a maximum of %d)\n",
+                          l_comp_no, l_num_comp);
+            if (!JPWL_ASSUME) {
+                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
+                return OPJ_FALSE;
+            }
+            /* we try to correct */
+            l_comp_no = backup_compno % l_num_comp;
+            opj_event_msg(p_manager, EVT_WARNING, "- trying to adjust this\n"
+                          "- setting component number to %d\n",
+                          l_comp_no);
+        }
+
+        /* keep your private count of tiles */
+        backup_compno++;
+    };
+#endif /* USE_JPWL */
+
+    if (l_comp_no >= p_j2k->m_private_image->numcomps) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Invalid component number: %d, regarding the number of components %d\n",
+                      l_comp_no, p_j2k->m_private_image->numcomps);
+        return OPJ_FALSE;
+    }
+
+    if (! opj_j2k_read_SQcd_SQcc(p_j2k, l_comp_no, p_header_data, &p_header_size,
+                                 p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading QCC marker\n");
+        return OPJ_FALSE;
+    }
+
+    if (p_header_size != 0) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading QCC marker\n");
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_poc(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager
+                                 )
+{
+    OPJ_UINT32 l_nb_comp;
+    OPJ_UINT32 l_nb_poc;
+    OPJ_UINT32 l_poc_size;
+    OPJ_UINT32 l_written_size = 0;
+    opj_tcp_t *l_tcp = 00;
+    OPJ_UINT32 l_poc_room;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    l_tcp = &p_j2k->m_cp.tcps[p_j2k->m_current_tile_number];
+    l_nb_comp = p_j2k->m_private_image->numcomps;
+    l_nb_poc = 1 + l_tcp->numpocs;
+
+    if (l_nb_comp <= 256) {
+        l_poc_room = 1;
+    } else {
+        l_poc_room = 2;
+    }
+    l_poc_size = 4 + (5 + 2 * l_poc_room) * l_nb_poc;
+
+    if (l_poc_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
+        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
+                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_poc_size);
+        if (! new_header_tile_data) {
+            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write POC marker\n");
+            return OPJ_FALSE;
+        }
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_poc_size;
+    }
+
+    opj_j2k_write_poc_in_memory(p_j2k,
+                                p_j2k->m_specific_param.m_encoder.m_header_tile_data, &l_written_size,
+                                p_manager);
+
+    if (opj_stream_write_data(p_stream,
+                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_poc_size,
+                              p_manager) != l_poc_size) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static void opj_j2k_write_poc_in_memory(opj_j2k_t *p_j2k,
+                                        OPJ_BYTE * p_data,
+                                        OPJ_UINT32 * p_data_written,
+                                        opj_event_mgr_t * p_manager
+                                       )
+{
+    OPJ_UINT32 i;
+    OPJ_BYTE * l_current_data = 00;
+    OPJ_UINT32 l_nb_comp;
+    OPJ_UINT32 l_nb_poc;
+    OPJ_UINT32 l_poc_size;
+    opj_image_t *l_image = 00;
+    opj_tcp_t *l_tcp = 00;
+    opj_tccp_t *l_tccp = 00;
+    opj_poc_t *l_current_poc = 00;
+    OPJ_UINT32 l_poc_room;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    OPJ_UNUSED(p_manager);
+
+    l_tcp = &p_j2k->m_cp.tcps[p_j2k->m_current_tile_number];
+    l_tccp = &l_tcp->tccps[0];
+    l_image = p_j2k->m_private_image;
+    l_nb_comp = l_image->numcomps;
+    l_nb_poc = 1 + l_tcp->numpocs;
+
+    if (l_nb_comp <= 256) {
+        l_poc_room = 1;
+    } else {
+        l_poc_room = 2;
+    }
+
+    l_poc_size = 4 + (5 + 2 * l_poc_room) * l_nb_poc;
+
+    l_current_data = p_data;
+
+    opj_write_bytes(l_current_data, J2K_MS_POC,
+                    2);                                   /* POC  */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, l_poc_size - 2,
+                    2);                                 /* Lpoc */
+    l_current_data += 2;
+
+    l_current_poc =  l_tcp->pocs;
+    for (i = 0; i < l_nb_poc; ++i) {
+        opj_write_bytes(l_current_data, l_current_poc->resno0,
+                        1);                                /* RSpoc_i */
+        ++l_current_data;
+
+        opj_write_bytes(l_current_data, l_current_poc->compno0,
+                        l_poc_room);              /* CSpoc_i */
+        l_current_data += l_poc_room;
+
+        opj_write_bytes(l_current_data, l_current_poc->layno1,
+                        2);                                /* LYEpoc_i */
+        l_current_data += 2;
+
+        opj_write_bytes(l_current_data, l_current_poc->resno1,
+                        1);                                /* REpoc_i */
+        ++l_current_data;
+
+        opj_write_bytes(l_current_data, l_current_poc->compno1,
+                        l_poc_room);              /* CEpoc_i */
+        l_current_data += l_poc_room;
+
+        opj_write_bytes(l_current_data, (OPJ_UINT32)l_current_poc->prg,
+                        1);   /* Ppoc_i */
+        ++l_current_data;
+
+        /* change the value of the max layer according to the actual number of layers in the file, components and resolutions*/
+        l_current_poc->layno1 = (OPJ_UINT32)opj_int_min((OPJ_INT32)
+                                l_current_poc->layno1, (OPJ_INT32)l_tcp->numlayers);
+        l_current_poc->resno1 = (OPJ_UINT32)opj_int_min((OPJ_INT32)
+                                l_current_poc->resno1, (OPJ_INT32)l_tccp->numresolutions);
+        l_current_poc->compno1 = (OPJ_UINT32)opj_int_min((OPJ_INT32)
+                                 l_current_poc->compno1, (OPJ_INT32)l_nb_comp);
+
+        ++l_current_poc;
+    }
+
+    *p_data_written = l_poc_size;
+}
+
+static OPJ_UINT32 opj_j2k_get_max_poc_size(opj_j2k_t *p_j2k)
+{
+    opj_tcp_t * l_tcp = 00;
+    OPJ_UINT32 l_nb_tiles = 0;
+    OPJ_UINT32 l_max_poc = 0;
+    OPJ_UINT32 i;
+
+    l_tcp = p_j2k->m_cp.tcps;
+    l_nb_tiles = p_j2k->m_cp.th * p_j2k->m_cp.tw;
+
+    for (i = 0; i < l_nb_tiles; ++i) {
+        l_max_poc = opj_uint_max(l_max_poc, l_tcp->numpocs);
+        ++l_tcp;
+    }
+
+    ++l_max_poc;
+
+    return 4 + 9 * l_max_poc;
+}
+
+static OPJ_UINT32 opj_j2k_get_max_toc_size(opj_j2k_t *p_j2k)
+{
+    OPJ_UINT32 i;
+    OPJ_UINT32 l_nb_tiles;
+    OPJ_UINT32 l_max = 0;
+    opj_tcp_t * l_tcp = 00;
+
+    l_tcp = p_j2k->m_cp.tcps;
+    l_nb_tiles = p_j2k->m_cp.tw * p_j2k->m_cp.th ;
+
+    for (i = 0; i < l_nb_tiles; ++i) {
+        l_max = opj_uint_max(l_max, l_tcp->m_nb_tile_parts);
+
+        ++l_tcp;
+    }
+
+    return 12 * l_max;
+}
+
+static OPJ_UINT32 opj_j2k_get_specific_header_sizes(opj_j2k_t *p_j2k)
+{
+    OPJ_UINT32 l_nb_bytes = 0;
+    OPJ_UINT32 l_nb_comps;
+    OPJ_UINT32 l_coc_bytes, l_qcc_bytes;
+
+    l_nb_comps = p_j2k->m_private_image->numcomps - 1;
+    l_nb_bytes += opj_j2k_get_max_toc_size(p_j2k);
+
+    if (!(OPJ_IS_CINEMA(p_j2k->m_cp.rsiz))) {
+        l_coc_bytes = opj_j2k_get_max_coc_size(p_j2k);
+        l_nb_bytes += l_nb_comps * l_coc_bytes;
+
+        l_qcc_bytes = opj_j2k_get_max_qcc_size(p_j2k);
+        l_nb_bytes += l_nb_comps * l_qcc_bytes;
+    }
+
+    l_nb_bytes += opj_j2k_get_max_poc_size(p_j2k);
+
+    if (p_j2k->m_specific_param.m_encoder.m_PLT) {
+        /* Reserve space for PLT markers */
+
+        OPJ_UINT32 i;
+        const opj_cp_t * l_cp = &(p_j2k->m_cp);
+        OPJ_UINT32 l_max_packet_count = 0;
+        for (i = 0; i < l_cp->th * l_cp->tw; ++i) {
+            l_max_packet_count = opj_uint_max(l_max_packet_count,
+                                              opj_get_encoding_packet_count(p_j2k->m_private_image, l_cp, i));
+        }
+        /* Minimum 6 bytes per PLT marker, and at a minimum (taking a pessimistic */
+        /* estimate of 4 bytes for a packet size), one can write */
+        /* (65536-6) / 4 = 16382 paquet sizes per PLT marker */
+        p_j2k->m_specific_param.m_encoder.m_reserved_bytes_for_PLT =
+            6 * opj_uint_ceildiv(l_max_packet_count, 16382);
+        /* Maximum 5 bytes per packet to encode a full UINT32 */
+        p_j2k->m_specific_param.m_encoder.m_reserved_bytes_for_PLT +=
+            l_nb_bytes += 5 * l_max_packet_count;
+        p_j2k->m_specific_param.m_encoder.m_reserved_bytes_for_PLT += 1;
+        l_nb_bytes += p_j2k->m_specific_param.m_encoder.m_reserved_bytes_for_PLT;
+    }
+
+    /*** DEVELOPER CORNER, Add room for your headers ***/
+
+    return l_nb_bytes;
+}
+
+/**
+ * Reads a POC marker (Progression Order Change)
+ *
+ * @param       p_header_data   the data contained in the POC box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the POC marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_poc(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    OPJ_UINT32 i, l_nb_comp, l_tmp;
+    opj_image_t * l_image = 00;
+    OPJ_UINT32 l_old_poc_nb, l_current_poc_nb, l_current_poc_remaining;
+    OPJ_UINT32 l_chunk_size, l_comp_room;
+
+    opj_cp_t *l_cp = 00;
+    opj_tcp_t *l_tcp = 00;
+    opj_poc_t *l_current_poc = 00;
+
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    l_image = p_j2k->m_private_image;
+    l_nb_comp = l_image->numcomps;
+    if (l_nb_comp <= 256) {
+        l_comp_room = 1;
+    } else {
+        l_comp_room = 2;
+    }
+    l_chunk_size = 5 + 2 * l_comp_room;
+    l_current_poc_nb = p_header_size / l_chunk_size;
+    l_current_poc_remaining = p_header_size % l_chunk_size;
+
+    if ((l_current_poc_nb <= 0) || (l_current_poc_remaining != 0)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading POC marker\n");
+        return OPJ_FALSE;
+    }
+
+    l_cp = &(p_j2k->m_cp);
+    l_tcp = (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH) ?
+            &l_cp->tcps[p_j2k->m_current_tile_number] :
+            p_j2k->m_specific_param.m_decoder.m_default_tcp;
+    l_old_poc_nb = l_tcp->POC ? l_tcp->numpocs + 1 : 0;
+    l_current_poc_nb += l_old_poc_nb;
+
+    if (l_current_poc_nb >= J2K_MAX_POCS) {
+        opj_event_msg(p_manager, EVT_ERROR, "Too many POCs %d\n", l_current_poc_nb);
+        return OPJ_FALSE;
+    }
+
+    /* now poc is in use.*/
+    l_tcp->POC = 1;
+
+    l_current_poc = &l_tcp->pocs[l_old_poc_nb];
+    for (i = l_old_poc_nb; i < l_current_poc_nb; ++i) {
+        opj_read_bytes(p_header_data, &(l_current_poc->resno0),
+                       1);                               /* RSpoc_i */
+        ++p_header_data;
+        opj_read_bytes(p_header_data, &(l_current_poc->compno0),
+                       l_comp_room);  /* CSpoc_i */
+        p_header_data += l_comp_room;
+        opj_read_bytes(p_header_data, &(l_current_poc->layno1),
+                       2);                               /* LYEpoc_i */
+        /* make sure layer end is in acceptable bounds */
+        l_current_poc->layno1 = opj_uint_min(l_current_poc->layno1, l_tcp->numlayers);
+        p_header_data += 2;
+        opj_read_bytes(p_header_data, &(l_current_poc->resno1),
+                       1);                               /* REpoc_i */
+        ++p_header_data;
+        opj_read_bytes(p_header_data, &(l_current_poc->compno1),
+                       l_comp_room);  /* CEpoc_i */
+        p_header_data += l_comp_room;
+        opj_read_bytes(p_header_data, &l_tmp,
+                       1);                                                                 /* Ppoc_i */
+        ++p_header_data;
+        l_current_poc->prg = (OPJ_PROG_ORDER) l_tmp;
+        /* make sure comp is in acceptable bounds */
+        l_current_poc->compno1 = opj_uint_min(l_current_poc->compno1, l_nb_comp);
+        ++l_current_poc;
+    }
+
+    l_tcp->numpocs = l_current_poc_nb - 1;
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a CRG marker (Component registration)
+ *
+ * @param       p_header_data   the data contained in the TLM box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the TLM marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_crg(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    OPJ_UINT32 l_nb_comp;
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    OPJ_UNUSED(p_header_data);
+
+    l_nb_comp = p_j2k->m_private_image->numcomps;
+
+    if (p_header_size != l_nb_comp * 4) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading CRG marker\n");
+        return OPJ_FALSE;
+    }
+    /* Do not care of this at the moment since only local variables are set here */
+    /*
+    for
+            (i = 0; i < l_nb_comp; ++i)
+    {
+            opj_read_bytes(p_header_data,&l_Xcrg_i,2);                              // Xcrg_i
+            p_header_data+=2;
+            opj_read_bytes(p_header_data,&l_Ycrg_i,2);                              // Xcrg_i
+            p_header_data+=2;
+    }
+    */
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a TLM marker (Tile Length Marker)
+ *
+ * @param       p_header_data   the data contained in the TLM box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the TLM marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_tlm(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    OPJ_UINT32 l_Ztlm, l_Stlm, l_ST, l_SP, l_tot_num_tp_remaining, l_quotient,
+               l_Ptlm_size;
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    OPJ_UNUSED(p_j2k);
+
+    if (p_header_size < 2) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading TLM marker\n");
+        return OPJ_FALSE;
+    }
+    p_header_size -= 2;
+
+    opj_read_bytes(p_header_data, &l_Ztlm,
+                   1);                              /* Ztlm */
+    ++p_header_data;
+    opj_read_bytes(p_header_data, &l_Stlm,
+                   1);                              /* Stlm */
+    ++p_header_data;
+
+    l_ST = ((l_Stlm >> 4) & 0x3);
+    l_SP = (l_Stlm >> 6) & 0x1;
+
+    l_Ptlm_size = (l_SP + 1) * 2;
+    l_quotient = l_Ptlm_size + l_ST;
+
+    l_tot_num_tp_remaining = p_header_size % l_quotient;
+
+    if (l_tot_num_tp_remaining != 0) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading TLM marker\n");
+        return OPJ_FALSE;
+    }
+    /* FIXME Do not care of this at the moment since only local variables are set here */
+    /*
+    for
+            (i = 0; i < l_tot_num_tp; ++i)
+    {
+            opj_read_bytes(p_header_data,&l_Ttlm_i,l_ST);                           // Ttlm_i
+            p_header_data += l_ST;
+            opj_read_bytes(p_header_data,&l_Ptlm_i,l_Ptlm_size);            // Ptlm_i
+            p_header_data += l_Ptlm_size;
+    }*/
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a PLM marker (Packet length, main header marker)
+ *
+ * @param       p_header_data   the data contained in the TLM box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the TLM marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_plm(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    OPJ_UNUSED(p_j2k);
+    OPJ_UNUSED(p_header_data);
+
+    if (p_header_size < 1) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading PLM marker\n");
+        return OPJ_FALSE;
+    }
+    /* Do not care of this at the moment since only local variables are set here */
+    /*
+    opj_read_bytes(p_header_data,&l_Zplm,1);                                        // Zplm
+    ++p_header_data;
+    --p_header_size;
+
+    while
+            (p_header_size > 0)
+    {
+            opj_read_bytes(p_header_data,&l_Nplm,1);                                // Nplm
+            ++p_header_data;
+            p_header_size -= (1+l_Nplm);
+            if
+                    (p_header_size < 0)
+            {
+                    opj_event_msg(p_manager, EVT_ERROR, "Error reading PLM marker\n");
+                    return false;
+            }
+            for
+                    (i = 0; i < l_Nplm; ++i)
+            {
+                    opj_read_bytes(p_header_data,&l_tmp,1);                         // Iplm_ij
+                    ++p_header_data;
+                    // take only the last seven bytes
+                    l_packet_len |= (l_tmp & 0x7f);
+                    if
+                            (l_tmp & 0x80)
+                    {
+                            l_packet_len <<= 7;
+                    }
+                    else
+                    {
+            // store packet length and proceed to next packet
+                            l_packet_len = 0;
+                    }
+            }
+            if
+                    (l_packet_len != 0)
+            {
+                    opj_event_msg(p_manager, EVT_ERROR, "Error reading PLM marker\n");
+                    return false;
+            }
+    }
+    */
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a PLT marker (Packet length, tile-part header)
+ *
+ * @param       p_header_data   the data contained in the PLT box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the PLT marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_plt(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    OPJ_UINT32 l_Zplt, l_tmp, l_packet_len = 0, i;
+
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    OPJ_UNUSED(p_j2k);
+
+    if (p_header_size < 1) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading PLT marker\n");
+        return OPJ_FALSE;
+    }
+
+    opj_read_bytes(p_header_data, &l_Zplt, 1);              /* Zplt */
+    ++p_header_data;
+    --p_header_size;
+
+    for (i = 0; i < p_header_size; ++i) {
+        opj_read_bytes(p_header_data, &l_tmp, 1);       /* Iplt_ij */
+        ++p_header_data;
+        /* take only the last seven bytes */
+        l_packet_len |= (l_tmp & 0x7f);
+        if (l_tmp & 0x80) {
+            l_packet_len <<= 7;
+        } else {
+            /* store packet length and proceed to next packet */
+            l_packet_len = 0;
+        }
+    }
+
+    if (l_packet_len != 0) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading PLT marker\n");
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a PPM marker (Packed packet headers, main header)
+ *
+ * @param       p_header_data   the data contained in the POC box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the POC marker.
+ * @param       p_manager               the user event manager.
+ */
+
+static OPJ_BOOL opj_j2k_read_ppm(
+    opj_j2k_t *p_j2k,
+    OPJ_BYTE * p_header_data,
+    OPJ_UINT32 p_header_size,
+    opj_event_mgr_t * p_manager)
+{
+    opj_cp_t *l_cp = 00;
+    OPJ_UINT32 l_Z_ppm;
+
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    /* We need to have the Z_ppm element + 1 byte of Nppm/Ippm at minimum */
+    if (p_header_size < 2) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading PPM marker\n");
+        return OPJ_FALSE;
+    }
+
+    l_cp = &(p_j2k->m_cp);
+    l_cp->ppm = 1;
+
+    opj_read_bytes(p_header_data, &l_Z_ppm, 1);             /* Z_ppm */
+    ++p_header_data;
+    --p_header_size;
+
+    /* check allocation needed */
+    if (l_cp->ppm_markers == NULL) { /* first PPM marker */
+        OPJ_UINT32 l_newCount = l_Z_ppm + 1U; /* can't overflow, l_Z_ppm is UINT8 */
+        assert(l_cp->ppm_markers_count == 0U);
+
+        l_cp->ppm_markers = (opj_ppx *) opj_calloc(l_newCount, sizeof(opj_ppx));
+        if (l_cp->ppm_markers == NULL) {
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPM marker\n");
+            return OPJ_FALSE;
+        }
+        l_cp->ppm_markers_count = l_newCount;
+    } else if (l_cp->ppm_markers_count <= l_Z_ppm) {
+        OPJ_UINT32 l_newCount = l_Z_ppm + 1U; /* can't overflow, l_Z_ppm is UINT8 */
+        opj_ppx *new_ppm_markers;
+        new_ppm_markers = (opj_ppx *) opj_realloc(l_cp->ppm_markers,
+                          l_newCount * sizeof(opj_ppx));
+        if (new_ppm_markers == NULL) {
+            /* clean up to be done on l_cp destruction */
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPM marker\n");
+            return OPJ_FALSE;
+        }
+        l_cp->ppm_markers = new_ppm_markers;
+        memset(l_cp->ppm_markers + l_cp->ppm_markers_count, 0,
+               (l_newCount - l_cp->ppm_markers_count) * sizeof(opj_ppx));
+        l_cp->ppm_markers_count = l_newCount;
+    }
+
+    if (l_cp->ppm_markers[l_Z_ppm].m_data != NULL) {
+        /* clean up to be done on l_cp destruction */
+        opj_event_msg(p_manager, EVT_ERROR, "Zppm %u already read\n", l_Z_ppm);
+        return OPJ_FALSE;
+    }
+
+    l_cp->ppm_markers[l_Z_ppm].m_data = (OPJ_BYTE *) opj_malloc(p_header_size);
+    if (l_cp->ppm_markers[l_Z_ppm].m_data == NULL) {
+        /* clean up to be done on l_cp destruction */
+        opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPM marker\n");
+        return OPJ_FALSE;
+    }
+    l_cp->ppm_markers[l_Z_ppm].m_data_size = p_header_size;
+    memcpy(l_cp->ppm_markers[l_Z_ppm].m_data, p_header_data, p_header_size);
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Merges all PPM markers read (Packed headers, main header)
+ *
+ * @param       p_cp      main coding parameters.
+ * @param       p_manager the user event manager.
+ */
+static OPJ_BOOL opj_j2k_merge_ppm(opj_cp_t *p_cp, opj_event_mgr_t * p_manager)
+{
+    OPJ_UINT32 i, l_ppm_data_size, l_N_ppm_remaining;
+
+    /* preconditions */
+    assert(p_cp != 00);
+    assert(p_manager != 00);
+    assert(p_cp->ppm_buffer == NULL);
+
+    if (p_cp->ppm == 0U) {
+        return OPJ_TRUE;
+    }
+
+    l_ppm_data_size = 0U;
+    l_N_ppm_remaining = 0U;
+    for (i = 0U; i < p_cp->ppm_markers_count; ++i) {
+        if (p_cp->ppm_markers[i].m_data !=
+                NULL) { /* standard doesn't seem to require contiguous Zppm */
+            OPJ_UINT32 l_N_ppm;
+            OPJ_UINT32 l_data_size = p_cp->ppm_markers[i].m_data_size;
+            const OPJ_BYTE* l_data = p_cp->ppm_markers[i].m_data;
+
+            if (l_N_ppm_remaining >= l_data_size) {
+                l_N_ppm_remaining -= l_data_size;
+                l_data_size = 0U;
+            } else {
+                l_data += l_N_ppm_remaining;
+                l_data_size -= l_N_ppm_remaining;
+                l_N_ppm_remaining = 0U;
+            }
+
+            if (l_data_size > 0U) {
+                do {
+                    /* read Nppm */
+                    if (l_data_size < 4U) {
+                        /* clean up to be done on l_cp destruction */
+                        opj_event_msg(p_manager, EVT_ERROR, "Not enough bytes to read Nppm\n");
+                        return OPJ_FALSE;
+                    }
+                    opj_read_bytes(l_data, &l_N_ppm, 4);
+                    l_data += 4;
+                    l_data_size -= 4;
+                    l_ppm_data_size +=
+                        l_N_ppm; /* can't overflow, max 256 markers of max 65536 bytes, that is when PPM markers are not corrupted which is checked elsewhere */
+
+                    if (l_data_size >= l_N_ppm) {
+                        l_data_size -= l_N_ppm;
+                        l_data += l_N_ppm;
+                    } else {
+                        l_N_ppm_remaining = l_N_ppm - l_data_size;
+                        l_data_size = 0U;
+                    }
+                } while (l_data_size > 0U);
+            }
+        }
+    }
+
+    if (l_N_ppm_remaining != 0U) {
+        /* clean up to be done on l_cp destruction */
+        opj_event_msg(p_manager, EVT_ERROR, "Corrupted PPM markers\n");
+        return OPJ_FALSE;
+    }
+
+    p_cp->ppm_buffer = (OPJ_BYTE *) opj_malloc(l_ppm_data_size);
+    if (p_cp->ppm_buffer == 00) {
+        opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPM marker\n");
+        return OPJ_FALSE;
+    }
+    p_cp->ppm_len = l_ppm_data_size;
+    l_ppm_data_size = 0U;
+    l_N_ppm_remaining = 0U;
+    for (i = 0U; i < p_cp->ppm_markers_count; ++i) {
+        if (p_cp->ppm_markers[i].m_data !=
+                NULL) { /* standard doesn't seem to require contiguous Zppm */
+            OPJ_UINT32 l_N_ppm;
+            OPJ_UINT32 l_data_size = p_cp->ppm_markers[i].m_data_size;
+            const OPJ_BYTE* l_data = p_cp->ppm_markers[i].m_data;
+
+            if (l_N_ppm_remaining >= l_data_size) {
+                memcpy(p_cp->ppm_buffer + l_ppm_data_size, l_data, l_data_size);
+                l_ppm_data_size += l_data_size;
+                l_N_ppm_remaining -= l_data_size;
+                l_data_size = 0U;
+            } else {
+                memcpy(p_cp->ppm_buffer + l_ppm_data_size, l_data, l_N_ppm_remaining);
+                l_ppm_data_size += l_N_ppm_remaining;
+                l_data += l_N_ppm_remaining;
+                l_data_size -= l_N_ppm_remaining;
+                l_N_ppm_remaining = 0U;
+            }
+
+            if (l_data_size > 0U) {
+                do {
+                    /* read Nppm */
+                    if (l_data_size < 4U) {
+                        /* clean up to be done on l_cp destruction */
+                        opj_event_msg(p_manager, EVT_ERROR, "Not enough bytes to read Nppm\n");
+                        return OPJ_FALSE;
+                    }
+                    opj_read_bytes(l_data, &l_N_ppm, 4);
+                    l_data += 4;
+                    l_data_size -= 4;
+
+                    if (l_data_size >= l_N_ppm) {
+                        memcpy(p_cp->ppm_buffer + l_ppm_data_size, l_data, l_N_ppm);
+                        l_ppm_data_size += l_N_ppm;
+                        l_data_size -= l_N_ppm;
+                        l_data += l_N_ppm;
+                    } else {
+                        memcpy(p_cp->ppm_buffer + l_ppm_data_size, l_data, l_data_size);
+                        l_ppm_data_size += l_data_size;
+                        l_N_ppm_remaining = l_N_ppm - l_data_size;
+                        l_data_size = 0U;
+                    }
+                } while (l_data_size > 0U);
+            }
+            opj_free(p_cp->ppm_markers[i].m_data);
+            p_cp->ppm_markers[i].m_data = NULL;
+            p_cp->ppm_markers[i].m_data_size = 0U;
+        }
+    }
+
+    p_cp->ppm_data = p_cp->ppm_buffer;
+    p_cp->ppm_data_size = p_cp->ppm_len;
+
+    p_cp->ppm_markers_count = 0U;
+    opj_free(p_cp->ppm_markers);
+    p_cp->ppm_markers = NULL;
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a PPT marker (Packed packet headers, tile-part header)
+ *
+ * @param       p_header_data   the data contained in the PPT box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the PPT marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_ppt(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    opj_cp_t *l_cp = 00;
+    opj_tcp_t *l_tcp = 00;
+    OPJ_UINT32 l_Z_ppt;
+
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    /* We need to have the Z_ppt element + 1 byte of Ippt at minimum */
+    if (p_header_size < 2) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading PPT marker\n");
+        return OPJ_FALSE;
+    }
+
+    l_cp = &(p_j2k->m_cp);
+    if (l_cp->ppm) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Error reading PPT marker: packet header have been previously found in the main header (PPM marker).\n");
+        return OPJ_FALSE;
+    }
+
+    l_tcp = &(l_cp->tcps[p_j2k->m_current_tile_number]);
+    l_tcp->ppt = 1;
+
+    opj_read_bytes(p_header_data, &l_Z_ppt, 1);             /* Z_ppt */
+    ++p_header_data;
+    --p_header_size;
+
+    /* check allocation needed */
+    if (l_tcp->ppt_markers == NULL) { /* first PPT marker */
+        OPJ_UINT32 l_newCount = l_Z_ppt + 1U; /* can't overflow, l_Z_ppt is UINT8 */
+        assert(l_tcp->ppt_markers_count == 0U);
+
+        l_tcp->ppt_markers = (opj_ppx *) opj_calloc(l_newCount, sizeof(opj_ppx));
+        if (l_tcp->ppt_markers == NULL) {
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPT marker\n");
+            return OPJ_FALSE;
+        }
+        l_tcp->ppt_markers_count = l_newCount;
+    } else if (l_tcp->ppt_markers_count <= l_Z_ppt) {
+        OPJ_UINT32 l_newCount = l_Z_ppt + 1U; /* can't overflow, l_Z_ppt is UINT8 */
+        opj_ppx *new_ppt_markers;
+        new_ppt_markers = (opj_ppx *) opj_realloc(l_tcp->ppt_markers,
+                          l_newCount * sizeof(opj_ppx));
+        if (new_ppt_markers == NULL) {
+            /* clean up to be done on l_tcp destruction */
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPT marker\n");
+            return OPJ_FALSE;
+        }
+        l_tcp->ppt_markers = new_ppt_markers;
+        memset(l_tcp->ppt_markers + l_tcp->ppt_markers_count, 0,
+               (l_newCount - l_tcp->ppt_markers_count) * sizeof(opj_ppx));
+        l_tcp->ppt_markers_count = l_newCount;
+    }
+
+    if (l_tcp->ppt_markers[l_Z_ppt].m_data != NULL) {
+        /* clean up to be done on l_tcp destruction */
+        opj_event_msg(p_manager, EVT_ERROR, "Zppt %u already read\n", l_Z_ppt);
+        return OPJ_FALSE;
+    }
+
+    l_tcp->ppt_markers[l_Z_ppt].m_data = (OPJ_BYTE *) opj_malloc(p_header_size);
+    if (l_tcp->ppt_markers[l_Z_ppt].m_data == NULL) {
+        /* clean up to be done on l_tcp destruction */
+        opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPT marker\n");
+        return OPJ_FALSE;
+    }
+    l_tcp->ppt_markers[l_Z_ppt].m_data_size = p_header_size;
+    memcpy(l_tcp->ppt_markers[l_Z_ppt].m_data, p_header_data, p_header_size);
+    return OPJ_TRUE;
+}
+
+/**
+ * Merges all PPT markers read (Packed packet headers, tile-part header)
+ *
+ * @param       p_tcp   the tile.
+ * @param       p_manager               the user event manager.
+ */
+static OPJ_BOOL opj_j2k_merge_ppt(opj_tcp_t *p_tcp, opj_event_mgr_t * p_manager)
+{
+    OPJ_UINT32 i, l_ppt_data_size;
+    /* preconditions */
+    assert(p_tcp != 00);
+    assert(p_manager != 00);
+
+    if (p_tcp->ppt_buffer != NULL) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "opj_j2k_merge_ppt() has already been called\n");
+        return OPJ_FALSE;
+    }
+
+    if (p_tcp->ppt == 0U) {
+        return OPJ_TRUE;
+    }
+
+    l_ppt_data_size = 0U;
+    for (i = 0U; i < p_tcp->ppt_markers_count; ++i) {
+        l_ppt_data_size +=
+            p_tcp->ppt_markers[i].m_data_size; /* can't overflow, max 256 markers of max 65536 bytes */
+    }
+
+    p_tcp->ppt_buffer = (OPJ_BYTE *) opj_malloc(l_ppt_data_size);
+    if (p_tcp->ppt_buffer == 00) {
+        opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPT marker\n");
+        return OPJ_FALSE;
+    }
+    p_tcp->ppt_len = l_ppt_data_size;
+    l_ppt_data_size = 0U;
+    for (i = 0U; i < p_tcp->ppt_markers_count; ++i) {
+        if (p_tcp->ppt_markers[i].m_data !=
+                NULL) { /* standard doesn't seem to require contiguous Zppt */
+            memcpy(p_tcp->ppt_buffer + l_ppt_data_size, p_tcp->ppt_markers[i].m_data,
+                   p_tcp->ppt_markers[i].m_data_size);
+            l_ppt_data_size +=
+                p_tcp->ppt_markers[i].m_data_size; /* can't overflow, max 256 markers of max 65536 bytes */
+
+            opj_free(p_tcp->ppt_markers[i].m_data);
+            p_tcp->ppt_markers[i].m_data = NULL;
+            p_tcp->ppt_markers[i].m_data_size = 0U;
+        }
+    }
+
+    p_tcp->ppt_markers_count = 0U;
+    opj_free(p_tcp->ppt_markers);
+    p_tcp->ppt_markers = NULL;
+
+    p_tcp->ppt_data = p_tcp->ppt_buffer;
+    p_tcp->ppt_data_size = p_tcp->ppt_len;
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_tlm(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager
+                                 )
+{
+    OPJ_BYTE * l_current_data = 00;
+    OPJ_UINT32 l_tlm_size;
+    OPJ_UINT32 size_per_tile_part;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    /* 10921 = (65535 - header_size) / size_per_tile_part where */
+    /* header_size = 4 and size_per_tile_part = 6 */
+    if (p_j2k->m_specific_param.m_encoder.m_total_tile_parts > 10921) {
+        /* We could do more but it would require writing several TLM markers */
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "A maximum of 10921 tile-parts are supported currently "
+                      "when writing TLM marker\n");
+        return OPJ_FALSE;
+    }
+
+    if (p_j2k->m_specific_param.m_encoder.m_total_tile_parts <= 255) {
+        size_per_tile_part = 5;
+        p_j2k->m_specific_param.m_encoder.m_Ttlmi_is_byte = OPJ_TRUE;
+    } else {
+        size_per_tile_part = 6;
+        p_j2k->m_specific_param.m_encoder.m_Ttlmi_is_byte = OPJ_FALSE;
+    }
+
+    l_tlm_size = 2 + 4 + (size_per_tile_part *
+                          p_j2k->m_specific_param.m_encoder.m_total_tile_parts);
+
+    if (l_tlm_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
+        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
+                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_tlm_size);
+        if (! new_header_tile_data) {
+            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write TLM marker\n");
+            return OPJ_FALSE;
+        }
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_tlm_size;
+    }
+    memset(p_j2k->m_specific_param.m_encoder.m_header_tile_data, 0, l_tlm_size);
+
+    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
+
+    /* change the way data is written to avoid seeking if possible */
+    /* TODO */
+    p_j2k->m_specific_param.m_encoder.m_tlm_start = opj_stream_tell(p_stream);
+
+    opj_write_bytes(l_current_data, J2K_MS_TLM,
+                    2);                                   /* TLM */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, l_tlm_size - 2,
+                    2);                                 /* Lpoc */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, 0,
+                    1);                                                    /* Ztlm=0*/
+    ++l_current_data;
+
+    /* Stlm 0x50= ST=1(8bits-255 tiles max),SP=1(Ptlm=32bits) */
+    /* Stlm 0x60= ST=2(16bits-65535 tiles max),SP=1(Ptlm=32bits) */
+    opj_write_bytes(l_current_data,
+                    size_per_tile_part == 5 ? 0x50 : 0x60,
+                    1);
+    ++l_current_data;
+
+    /* do nothing on the size_per_tile_part * l_j2k->m_specific_param.m_encoder.m_total_tile_parts remaining data */
+    if (opj_stream_write_data(p_stream,
+                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_tlm_size,
+                              p_manager) != l_tlm_size) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_sot(opj_j2k_t *p_j2k,
+                                  OPJ_BYTE * p_data,
+                                  OPJ_UINT32 total_data_size,
+                                  OPJ_UINT32 * p_data_written,
+                                  const opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager
+                                 )
+{
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    OPJ_UNUSED(p_stream);
+
+    if (total_data_size < 12) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Not enough bytes in output buffer to write SOT marker\n");
+        return OPJ_FALSE;
+    }
+
+    opj_write_bytes(p_data, J2K_MS_SOT,
+                    2);                                 /* SOT */
+    p_data += 2;
+
+    opj_write_bytes(p_data, 10,
+                    2);                                                   /* Lsot */
+    p_data += 2;
+
+    opj_write_bytes(p_data, p_j2k->m_current_tile_number,
+                    2);                        /* Isot */
+    p_data += 2;
+
+    /* Psot  */
+    p_data += 4;
+
+    opj_write_bytes(p_data,
+                    p_j2k->m_specific_param.m_encoder.m_current_tile_part_number,
+                    1);                        /* TPsot */
+    ++p_data;
+
+    opj_write_bytes(p_data,
+                    p_j2k->m_cp.tcps[p_j2k->m_current_tile_number].m_nb_tile_parts,
+                    1);                      /* TNsot */
+    ++p_data;
+
+    /* UniPG>> */
+#ifdef USE_JPWL
+    /* update markers struct */
+    /*
+            OPJ_BOOL res = j2k_add_marker(p_j2k->cstr_info, J2K_MS_SOT, p_j2k->sot_start, len + 2);
+    */
+    assert(0 && "TODO");
+#endif /* USE_JPWL */
+
+    * p_data_written = 12;
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_get_sot_values(OPJ_BYTE *  p_header_data,
+                                       OPJ_UINT32  p_header_size,
+                                       OPJ_UINT32* p_tile_no,
+                                       OPJ_UINT32* p_tot_len,
+                                       OPJ_UINT32* p_current_part,
+                                       OPJ_UINT32* p_num_parts,
+                                       opj_event_mgr_t * p_manager)
+{
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_manager != 00);
+
+    /* Size of this marker is fixed = 12 (we have already read marker and its size)*/
+    if (p_header_size != 8) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading SOT marker\n");
+        return OPJ_FALSE;
+    }
+
+    opj_read_bytes(p_header_data, p_tile_no, 2);    /* Isot */
+    p_header_data += 2;
+    opj_read_bytes(p_header_data, p_tot_len, 4);    /* Psot */
+    p_header_data += 4;
+    opj_read_bytes(p_header_data, p_current_part, 1); /* TPsot */
+    ++p_header_data;
+    opj_read_bytes(p_header_data, p_num_parts, 1);  /* TNsot */
+    ++p_header_data;
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_read_sot(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager)
+{
+    opj_cp_t *l_cp = 00;
+    opj_tcp_t *l_tcp = 00;
+    OPJ_UINT32 l_tot_len, l_num_parts = 0;
+    OPJ_UINT32 l_current_part;
+    OPJ_UINT32 l_tile_x, l_tile_y;
+
+    /* preconditions */
+
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    if (! opj_j2k_get_sot_values(p_header_data, p_header_size,
+                                 &(p_j2k->m_current_tile_number), &l_tot_len, &l_current_part, &l_num_parts,
+                                 p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading SOT marker\n");
+        return OPJ_FALSE;
+    }
+#ifdef DEBUG_VERBOSE
+    fprintf(stderr, "SOT %d %d %d %d\n",
+            p_j2k->m_current_tile_number, l_tot_len, l_current_part, l_num_parts);
+#endif
+
+    l_cp = &(p_j2k->m_cp);
+
+    /* testcase 2.pdf.SIGFPE.706.1112 */
+    if (p_j2k->m_current_tile_number >= l_cp->tw * l_cp->th) {
+        opj_event_msg(p_manager, EVT_ERROR, "Invalid tile number %d\n",
+                      p_j2k->m_current_tile_number);
+        return OPJ_FALSE;
+    }
+
+    l_tcp = &l_cp->tcps[p_j2k->m_current_tile_number];
+    l_tile_x = p_j2k->m_current_tile_number % l_cp->tw;
+    l_tile_y = p_j2k->m_current_tile_number / l_cp->tw;
+
+    if (p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec < 0 ||
+            p_j2k->m_current_tile_number == (OPJ_UINT32)
+            p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec) {
+        /* Do only this check if we decode all tile part headers, or if */
+        /* we decode one precise tile. Otherwise the m_current_tile_part_number */
+        /* might not be valid */
+        /* Fixes issue with id_000020,sig_06,src_001958,op_flip4,pos_149 */
+        /* of https://github.com/uclouvain/openjpeg/issues/939 */
+        /* We must avoid reading twice the same tile part number for a given tile */
+        /* so as to avoid various issues, like opj_j2k_merge_ppt being called */
+        /* several times. */
+        /* ISO 15444-1 A.4.2 Start of tile-part (SOT) mandates that tile parts */
+        /* should appear in increasing order. */
+        if (l_tcp->m_current_tile_part_number + 1 != (OPJ_INT32)l_current_part) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Invalid tile part index for tile number %d. "
+                          "Got %d, expected %d\n",
+                          p_j2k->m_current_tile_number,
+                          l_current_part,
+                          l_tcp->m_current_tile_part_number + 1);
+            return OPJ_FALSE;
+        }
+    }
+
+    l_tcp->m_current_tile_part_number = (OPJ_INT32) l_current_part;
+
+#ifdef USE_JPWL
+    if (l_cp->correct) {
+
+        OPJ_UINT32 tileno = p_j2k->m_current_tile_number;
+        static OPJ_UINT32 backup_tileno = 0;
+
+        /* tileno is negative or larger than the number of tiles!!! */
+        if (tileno > (l_cp->tw * l_cp->th)) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "JPWL: bad tile number (%d out of a maximum of %d)\n",
+                          tileno, (l_cp->tw * l_cp->th));
+            if (!JPWL_ASSUME) {
+                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
+                return OPJ_FALSE;
+            }
+            /* we try to correct */
+            tileno = backup_tileno;
+            opj_event_msg(p_manager, EVT_WARNING, "- trying to adjust this\n"
+                          "- setting tile number to %d\n",
+                          tileno);
+        }
+
+        /* keep your private count of tiles */
+        backup_tileno++;
+    };
+#endif /* USE_JPWL */
+
+    /* look for the tile in the list of already processed tile (in parts). */
+    /* Optimization possible here with a more complex data structure and with the removing of tiles */
+    /* since the time taken by this function can only grow at the time */
+
+    /* PSot should be equal to zero or >=14 or <= 2^32-1 */
+    if ((l_tot_len != 0) && (l_tot_len < 14)) {
+        if (l_tot_len ==
+                12) { /* MSD: Special case for the PHR data which are read by kakadu*/
+            opj_event_msg(p_manager, EVT_WARNING, "Empty SOT marker detected: Psot=%d.\n",
+                          l_tot_len);
+        } else {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Psot value is not correct regards to the JPEG2000 norm: %d.\n", l_tot_len);
+            return OPJ_FALSE;
+        }
+    }
+
+#ifdef USE_JPWL
+    if (l_cp->correct) {
+
+        /* totlen is negative or larger than the bytes left!!! */
+        if (/*(l_tot_len < 0) ||*/ (l_tot_len >
+                                    p_header_size)) {   /* FIXME it seems correct; for info in V1 -> (p_stream_numbytesleft(p_stream) + 8))) { */
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "JPWL: bad tile byte size (%d bytes against %d bytes left)\n",
+                          l_tot_len,
+                          p_header_size);  /* FIXME it seems correct; for info in V1 -> p_stream_numbytesleft(p_stream) + 8); */
+            if (!JPWL_ASSUME) {
+                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
+                return OPJ_FALSE;
+            }
+            /* we try to correct */
+            l_tot_len = 0;
+            opj_event_msg(p_manager, EVT_WARNING, "- trying to adjust this\n"
+                          "- setting Psot to %d => assuming it is the last tile\n",
+                          l_tot_len);
+        }
+    };
+#endif /* USE_JPWL */
+
+    /* Ref A.4.2: Psot could be equal zero if it is the last tile-part of the codestream.*/
+    if (!l_tot_len) {
+        opj_event_msg(p_manager, EVT_INFO,
+                      "Psot value of the current tile-part is equal to zero, "
+                      "we assuming it is the last tile-part of the codestream.\n");
+        p_j2k->m_specific_param.m_decoder.m_last_tile_part = 1;
+    }
+
+    if (l_tcp->m_nb_tile_parts != 0 && l_current_part >= l_tcp->m_nb_tile_parts) {
+        /* Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=2851 */
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "In SOT marker, TPSot (%d) is not valid regards to the previous "
+                      "number of tile-part (%d), giving up\n", l_current_part,
+                      l_tcp->m_nb_tile_parts);
+        p_j2k->m_specific_param.m_decoder.m_last_tile_part = 1;
+        return OPJ_FALSE;
+    }
+
+    if (l_num_parts !=
+            0) { /* Number of tile-part header is provided by this tile-part header */
+        l_num_parts += p_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction;
+        /* Useful to manage the case of textGBR.jp2 file because two values of TNSot are allowed: the correct numbers of
+         * tile-parts for that tile and zero (A.4.2 of 15444-1 : 2002). */
+        if (l_tcp->m_nb_tile_parts) {
+            if (l_current_part >= l_tcp->m_nb_tile_parts) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "In SOT marker, TPSot (%d) is not valid regards to the current "
+                              "number of tile-part (%d), giving up\n", l_current_part,
+                              l_tcp->m_nb_tile_parts);
+                p_j2k->m_specific_param.m_decoder.m_last_tile_part = 1;
+                return OPJ_FALSE;
+            }
+        }
+        if (l_current_part >= l_num_parts) {
+            /* testcase 451.pdf.SIGSEGV.ce9.3723 */
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "In SOT marker, TPSot (%d) is not valid regards to the current "
+                          "number of tile-part (header) (%d), giving up\n", l_current_part, l_num_parts);
+            p_j2k->m_specific_param.m_decoder.m_last_tile_part = 1;
+            return OPJ_FALSE;
+        }
+        l_tcp->m_nb_tile_parts = l_num_parts;
+    }
+
+    /* If know the number of tile part header we will check if we didn't read the last*/
+    if (l_tcp->m_nb_tile_parts) {
+        if (l_tcp->m_nb_tile_parts == (l_current_part + 1)) {
+            p_j2k->m_specific_param.m_decoder.m_can_decode =
+                1; /* Process the last tile-part header*/
+        }
+    }
+
+    if (!p_j2k->m_specific_param.m_decoder.m_last_tile_part) {
+        /* Keep the size of data to skip after this marker */
+        p_j2k->m_specific_param.m_decoder.m_sot_length = l_tot_len -
+                12; /* SOT_marker_size = 12 */
+    } else {
+        /* FIXME: need to be computed from the number of bytes remaining in the codestream */
+        p_j2k->m_specific_param.m_decoder.m_sot_length = 0;
+    }
+
+    p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_TPH;
+
+    /* Check if the current tile is outside the area we want decode or not corresponding to the tile index*/
+    if (p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec == -1) {
+        p_j2k->m_specific_param.m_decoder.m_skip_data =
+            (l_tile_x < p_j2k->m_specific_param.m_decoder.m_start_tile_x)
+            || (l_tile_x >= p_j2k->m_specific_param.m_decoder.m_end_tile_x)
+            || (l_tile_y < p_j2k->m_specific_param.m_decoder.m_start_tile_y)
+            || (l_tile_y >= p_j2k->m_specific_param.m_decoder.m_end_tile_y);
+    } else {
+        assert(p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec >= 0);
+        p_j2k->m_specific_param.m_decoder.m_skip_data =
+            (p_j2k->m_current_tile_number != (OPJ_UINT32)
+             p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec);
+    }
+
+    /* Index */
+    if (p_j2k->cstr_index) {
+        assert(p_j2k->cstr_index->tile_index != 00);
+        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tileno =
+            p_j2k->m_current_tile_number;
+        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_tpsno =
+            l_current_part;
+
+        if (l_num_parts != 0) {
+            p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].nb_tps =
+                l_num_parts;
+            p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps =
+                l_num_parts;
+
+            if (!p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index) {
+                p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index =
+                    (opj_tp_index_t*)opj_calloc(l_num_parts, sizeof(opj_tp_index_t));
+                if (!p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index) {
+                    opj_event_msg(p_manager, EVT_ERROR,
+                                  "Not enough memory to read SOT marker. Tile index allocation failed\n");
+                    return OPJ_FALSE;
+                }
+            } else {
+                opj_tp_index_t *new_tp_index = (opj_tp_index_t *) opj_realloc(
+                                                   p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index,
+                                                   l_num_parts * sizeof(opj_tp_index_t));
+                if (! new_tp_index) {
+                    opj_free(p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index);
+                    p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index = NULL;
+                    opj_event_msg(p_manager, EVT_ERROR,
+                                  "Not enough memory to read SOT marker. Tile index allocation failed\n");
+                    return OPJ_FALSE;
+                }
+                p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index =
+                    new_tp_index;
+            }
+        } else {
+            /*if (!p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index)*/ {
+
+                if (!p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index) {
+                    p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps = 10;
+                    p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index =
+                        (opj_tp_index_t*)opj_calloc(
+                            p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps,
+                            sizeof(opj_tp_index_t));
+                    if (!p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index) {
+                        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps = 0;
+                        opj_event_msg(p_manager, EVT_ERROR,
+                                      "Not enough memory to read SOT marker. Tile index allocation failed\n");
+                        return OPJ_FALSE;
+                    }
+                }
+
+                if (l_current_part >=
+                        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps) {
+                    opj_tp_index_t *new_tp_index;
+                    p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps =
+                        l_current_part + 1;
+                    new_tp_index = (opj_tp_index_t *) opj_realloc(
+                                       p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index,
+                                       p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps *
+                                       sizeof(opj_tp_index_t));
+                    if (! new_tp_index) {
+                        opj_free(p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index);
+                        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index = NULL;
+                        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps = 0;
+                        opj_event_msg(p_manager, EVT_ERROR,
+                                      "Not enough memory to read SOT marker. Tile index allocation failed\n");
+                        return OPJ_FALSE;
+                    }
+                    p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index =
+                        new_tp_index;
+                }
+            }
+
+        }
+
+    }
+
+    /* FIXME move this onto a separate method to call before reading any SOT, remove part about main_end header, use a index struct inside p_j2k */
+    /* if (p_j2k->cstr_info) {
+       if (l_tcp->first) {
+       if (tileno == 0) {
+       p_j2k->cstr_info->main_head_end = p_stream_tell(p_stream) - 13;
+       }
+
+       p_j2k->cstr_info->tile[tileno].tileno = tileno;
+       p_j2k->cstr_info->tile[tileno].start_pos = p_stream_tell(p_stream) - 12;
+       p_j2k->cstr_info->tile[tileno].end_pos = p_j2k->cstr_info->tile[tileno].start_pos + totlen - 1;
+       p_j2k->cstr_info->tile[tileno].num_tps = numparts;
+
+       if (numparts) {
+       p_j2k->cstr_info->tile[tileno].tp = (opj_tp_info_t *) opj_malloc(numparts * sizeof(opj_tp_info_t));
+       }
+       else {
+       p_j2k->cstr_info->tile[tileno].tp = (opj_tp_info_t *) opj_malloc(10 * sizeof(opj_tp_info_t)); // Fixme (10)
+       }
+       }
+       else {
+       p_j2k->cstr_info->tile[tileno].end_pos += totlen;
+       }
+
+       p_j2k->cstr_info->tile[tileno].tp[partno].tp_start_pos = p_stream_tell(p_stream) - 12;
+       p_j2k->cstr_info->tile[tileno].tp[partno].tp_end_pos =
+       p_j2k->cstr_info->tile[tileno].tp[partno].tp_start_pos + totlen - 1;
+       }*/
+    return OPJ_TRUE;
+}
+
+/**
+ * Write one or more PLT markers in the provided buffer
+ */
+static OPJ_BOOL opj_j2k_write_plt_in_memory(opj_j2k_t *p_j2k,
+        opj_tcd_marker_info_t* marker_info,
+        OPJ_BYTE * p_data,
+        OPJ_UINT32 * p_data_written,
+        opj_event_mgr_t * p_manager)
+{
+    OPJ_BYTE Zplt = 0;
+    OPJ_UINT16 Lplt;
+    OPJ_BYTE* p_data_start = p_data;
+    OPJ_BYTE* p_data_Lplt = p_data + 2;
+    OPJ_UINT32 i;
+
+    OPJ_UNUSED(p_j2k);
+
+    opj_write_bytes(p_data, J2K_MS_PLT, 2);
+    p_data += 2;
+
+    /* Reserve space for Lplt */
+    p_data += 2;
+
+    opj_write_bytes(p_data, Zplt, 1);
+    p_data += 1;
+
+    Lplt = 3;
+
+    for (i = 0; i < marker_info->packet_count; i++) {
+        OPJ_BYTE var_bytes[5];
+        OPJ_UINT8 var_bytes_size = 0;
+        OPJ_UINT32 packet_size = marker_info->p_packet_size[i];
+
+        /* Packet size written in variable-length way, starting with LSB */
+        var_bytes[var_bytes_size] = (OPJ_BYTE)(packet_size & 0x7f);
+        var_bytes_size ++;
+        packet_size >>= 7;
+        while (packet_size > 0) {
+            var_bytes[var_bytes_size] = (OPJ_BYTE)((packet_size & 0x7f) | 0x80);
+            var_bytes_size ++;
+            packet_size >>= 7;
+        }
+
+        /* Check if that can fit in the current PLT marker. If not, finish */
+        /* current one, and start a new one */
+        if (Lplt + var_bytes_size > 65535) {
+            if (Zplt == 255) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "More than 255 PLT markers would be needed for current tile-part !\n");
+                return OPJ_FALSE;
+            }
+
+            /* Patch Lplt */
+            opj_write_bytes(p_data_Lplt, Lplt, 2);
+
+            /* Start new segment */
+            opj_write_bytes(p_data, J2K_MS_PLT, 2);
+            p_data += 2;
+
+            /* Reserve space for Lplt */
+            p_data_Lplt = p_data;
+            p_data += 2;
+
+            Zplt ++;
+            opj_write_bytes(p_data, Zplt, 1);
+            p_data += 1;
+
+            Lplt = 3;
+        }
+
+        Lplt = (OPJ_UINT16)(Lplt + var_bytes_size);
+
+        /* Serialize variable-length packet size, starting with MSB */
+        for (; var_bytes_size > 0; --var_bytes_size) {
+            opj_write_bytes(p_data, var_bytes[var_bytes_size - 1], 1);
+            p_data += 1;
+        }
+    }
+
+    *p_data_written = (OPJ_UINT32)(p_data - p_data_start);
+
+    /* Patch Lplt */
+    opj_write_bytes(p_data_Lplt, Lplt, 2);
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_sod(opj_j2k_t *p_j2k,
+                                  opj_tcd_t * p_tile_coder,
+                                  OPJ_BYTE * p_data,
+                                  OPJ_UINT32 * p_data_written,
+                                  OPJ_UINT32 total_data_size,
+                                  const opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager
+                                 )
+{
+    opj_codestream_info_t *l_cstr_info = 00;
+    OPJ_UINT32 l_remaining_data;
+    opj_tcd_marker_info_t* marker_info = NULL;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    OPJ_UNUSED(p_stream);
+
+    if (total_data_size < 4) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Not enough bytes in output buffer to write SOD marker\n");
+        return OPJ_FALSE;
+    }
+
+    opj_write_bytes(p_data, J2K_MS_SOD,
+                    2);                                 /* SOD */
+
+    /* make room for the EOF marker */
+    l_remaining_data =  total_data_size - 4;
+
+    /* update tile coder */
+    p_tile_coder->tp_num =
+        p_j2k->m_specific_param.m_encoder.m_current_poc_tile_part_number ;
+    p_tile_coder->cur_tp_num =
+        p_j2k->m_specific_param.m_encoder.m_current_tile_part_number;
+
+    /* INDEX >> */
+    /* TODO mergeV2: check this part which use cstr_info */
+    /*l_cstr_info = p_j2k->cstr_info;
+    if (l_cstr_info) {
+            if (!p_j2k->m_specific_param.m_encoder.m_current_tile_part_number ) {
+                    //TODO cstr_info->tile[p_j2k->m_current_tile_number].end_header = p_stream_tell(p_stream) + p_j2k->pos_correction - 1;
+                    l_cstr_info->tile[p_j2k->m_current_tile_number].tileno = p_j2k->m_current_tile_number;
+            }
+            else {*/
+    /*
+    TODO
+    if
+            (cstr_info->tile[p_j2k->m_current_tile_number].packet[cstr_info->packno - 1].end_pos < p_stream_tell(p_stream))
+    {
+            cstr_info->tile[p_j2k->m_current_tile_number].packet[cstr_info->packno].start_pos = p_stream_tell(p_stream);
+    }*/
+    /*}*/
+    /* UniPG>> */
+#ifdef USE_JPWL
+    /* update markers struct */
+    /*OPJ_BOOL res = j2k_add_marker(p_j2k->cstr_info, J2K_MS_SOD, p_j2k->sod_start, 2);
+    */
+    assert(0 && "TODO");
+#endif /* USE_JPWL */
+    /* <<UniPG */
+    /*}*/
+    /* << INDEX */
+
+    if (p_j2k->m_specific_param.m_encoder.m_current_tile_part_number == 0) {
+        p_tile_coder->tcd_image->tiles->packno = 0;
+#ifdef deadcode
+        if (l_cstr_info) {
+            l_cstr_info->packno = 0;
+        }
+#endif
+    }
+
+    *p_data_written = 0;
+
+    if (p_j2k->m_specific_param.m_encoder.m_PLT) {
+        marker_info = opj_tcd_marker_info_create(
+                          p_j2k->m_specific_param.m_encoder.m_PLT);
+        if (marker_info == NULL) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Cannot encode tile: opj_tcd_marker_info_create() failed\n");
+            return OPJ_FALSE;
+        }
+    }
+
+    if (l_remaining_data <
+            p_j2k->m_specific_param.m_encoder.m_reserved_bytes_for_PLT) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Not enough bytes in output buffer to write SOD marker\n");
+        opj_tcd_marker_info_destroy(marker_info);
+        return OPJ_FALSE;
+    }
+    l_remaining_data -= p_j2k->m_specific_param.m_encoder.m_reserved_bytes_for_PLT;
+
+    if (! opj_tcd_encode_tile(p_tile_coder, p_j2k->m_current_tile_number,
+                              p_data + 2,
+                              p_data_written, l_remaining_data, l_cstr_info,
+                              marker_info,
+                              p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Cannot encode tile\n");
+        opj_tcd_marker_info_destroy(marker_info);
+        return OPJ_FALSE;
+    }
+
+    /* For SOD */
+    *p_data_written += 2;
+
+    if (p_j2k->m_specific_param.m_encoder.m_PLT) {
+        OPJ_UINT32 l_data_written_PLT = 0;
+        OPJ_BYTE* p_PLT_buffer = (OPJ_BYTE*)opj_malloc(
+                                     p_j2k->m_specific_param.m_encoder.m_reserved_bytes_for_PLT);
+        if (!p_PLT_buffer) {
+            opj_event_msg(p_manager, EVT_ERROR, "Cannot allocate memory\n");
+            opj_tcd_marker_info_destroy(marker_info);
+            return OPJ_FALSE;
+        }
+        if (!opj_j2k_write_plt_in_memory(p_j2k,
+                                         marker_info,
+                                         p_PLT_buffer,
+                                         &l_data_written_PLT,
+                                         p_manager)) {
+            opj_tcd_marker_info_destroy(marker_info);
+            opj_free(p_PLT_buffer);
+            return OPJ_FALSE;
+        }
+
+        assert(l_data_written_PLT <=
+               p_j2k->m_specific_param.m_encoder.m_reserved_bytes_for_PLT);
+
+        /* Move PLT marker(s) before SOD */
+        memmove(p_data + l_data_written_PLT, p_data, *p_data_written);
+        memcpy(p_data, p_PLT_buffer, l_data_written_PLT);
+        opj_free(p_PLT_buffer);
+        *p_data_written += l_data_written_PLT;
+    }
+
+    opj_tcd_marker_info_destroy(marker_info);
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_read_sod(opj_j2k_t *p_j2k,
+                                 opj_stream_private_t *p_stream,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    OPJ_SIZE_T l_current_read_size;
+    opj_codestream_index_t * l_cstr_index = 00;
+    OPJ_BYTE ** l_current_data = 00;
+    opj_tcp_t * l_tcp = 00;
+    OPJ_UINT32 * l_tile_len = 00;
+    OPJ_BOOL l_sot_length_pb_detected = OPJ_FALSE;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    l_tcp = &(p_j2k->m_cp.tcps[p_j2k->m_current_tile_number]);
+
+    if (p_j2k->m_specific_param.m_decoder.m_last_tile_part) {
+        /* opj_stream_get_number_byte_left returns OPJ_OFF_T
+        // but we are in the last tile part,
+        // so its result will fit on OPJ_UINT32 unless we find
+        // a file with a single tile part of more than 4 GB...*/
+        p_j2k->m_specific_param.m_decoder.m_sot_length = (OPJ_UINT32)(
+                    opj_stream_get_number_byte_left(p_stream) - 2);
+    } else {
+        /* Check to avoid pass the limit of OPJ_UINT32 */
+        if (p_j2k->m_specific_param.m_decoder.m_sot_length >= 2) {
+            p_j2k->m_specific_param.m_decoder.m_sot_length -= 2;
+        } else {
+            /* MSD: case commented to support empty SOT marker (PHR data) */
+        }
+    }
+
+    l_current_data = &(l_tcp->m_data);
+    l_tile_len = &l_tcp->m_data_size;
+
+    /* Patch to support new PHR data */
+    if (p_j2k->m_specific_param.m_decoder.m_sot_length) {
+        /* If we are here, we'll try to read the data after allocation */
+        /* Check enough bytes left in stream before allocation */
+        if ((OPJ_OFF_T)p_j2k->m_specific_param.m_decoder.m_sot_length >
+                opj_stream_get_number_byte_left(p_stream)) {
+            if (p_j2k->m_cp.strict) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Tile part length size inconsistent with stream length\n");
+                return OPJ_FALSE;
+            } else {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "Tile part length size inconsistent with stream length\n");
+            }
+        }
+        if (p_j2k->m_specific_param.m_decoder.m_sot_length >
+                UINT_MAX - OPJ_COMMON_CBLK_DATA_EXTRA) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "p_j2k->m_specific_param.m_decoder.m_sot_length > "
+                          "UINT_MAX - OPJ_COMMON_CBLK_DATA_EXTRA");
+            return OPJ_FALSE;
+        }
+        /* Add a margin of OPJ_COMMON_CBLK_DATA_EXTRA to the allocation we */
+        /* do so that opj_mqc_init_dec_common() can safely add a synthetic */
+        /* 0xFFFF marker. */
+        if (! *l_current_data) {
+            /* LH: oddly enough, in this path, l_tile_len!=0.
+             * TODO: If this was consistent, we could simplify the code to only use realloc(), as realloc(0,...) default to malloc(0,...).
+             */
+            *l_current_data = (OPJ_BYTE*) opj_malloc(
+                                  p_j2k->m_specific_param.m_decoder.m_sot_length + OPJ_COMMON_CBLK_DATA_EXTRA);
+        } else {
+            OPJ_BYTE *l_new_current_data;
+            if (*l_tile_len > UINT_MAX - OPJ_COMMON_CBLK_DATA_EXTRA -
+                    p_j2k->m_specific_param.m_decoder.m_sot_length) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "*l_tile_len > UINT_MAX - OPJ_COMMON_CBLK_DATA_EXTRA - "
+                              "p_j2k->m_specific_param.m_decoder.m_sot_length");
+                return OPJ_FALSE;
+            }
+
+            l_new_current_data = (OPJ_BYTE *) opj_realloc(*l_current_data,
+                                 *l_tile_len + p_j2k->m_specific_param.m_decoder.m_sot_length +
+                                 OPJ_COMMON_CBLK_DATA_EXTRA);
+            if (! l_new_current_data) {
+                opj_free(*l_current_data);
+                /*nothing more is done as l_current_data will be set to null, and just
+                  afterward we enter in the error path
+                  and the actual tile_len is updated (committed) at the end of the
+                  function. */
+            }
+            *l_current_data = l_new_current_data;
+        }
+
+        if (*l_current_data == 00) {
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to decode tile\n");
+            return OPJ_FALSE;
+        }
+    } else {
+        l_sot_length_pb_detected = OPJ_TRUE;
+    }
+
+    /* Index */
+    l_cstr_index = p_j2k->cstr_index;
+    if (l_cstr_index) {
+        OPJ_OFF_T l_current_pos = opj_stream_tell(p_stream) - 2;
+
+        OPJ_UINT32 l_current_tile_part =
+            l_cstr_index->tile_index[p_j2k->m_current_tile_number].current_tpsno;
+        l_cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index[l_current_tile_part].end_header
+            =
+                l_current_pos;
+        l_cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index[l_current_tile_part].end_pos
+            =
+                l_current_pos + p_j2k->m_specific_param.m_decoder.m_sot_length + 2;
+
+        if (OPJ_FALSE == opj_j2k_add_tlmarker(p_j2k->m_current_tile_number,
+                                              l_cstr_index,
+                                              J2K_MS_SOD,
+                                              l_current_pos,
+                                              p_j2k->m_specific_param.m_decoder.m_sot_length + 2)) {
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to add tl marker\n");
+            return OPJ_FALSE;
+        }
+
+        /*l_cstr_index->packno = 0;*/
+    }
+
+    /* Patch to support new PHR data */
+    if (!l_sot_length_pb_detected) {
+        l_current_read_size = opj_stream_read_data(
+                                  p_stream,
+                                  *l_current_data + *l_tile_len,
+                                  p_j2k->m_specific_param.m_decoder.m_sot_length,
+                                  p_manager);
+    } else {
+        l_current_read_size = 0;
+    }
+
+    if (l_current_read_size != p_j2k->m_specific_param.m_decoder.m_sot_length) {
+        p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_NEOC;
+    } else {
+        p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_TPHSOT;
+    }
+
+    *l_tile_len += (OPJ_UINT32)l_current_read_size;
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_rgn(opj_j2k_t *p_j2k,
+                                  OPJ_UINT32 p_tile_no,
+                                  OPJ_UINT32 p_comp_no,
+                                  OPJ_UINT32 nb_comps,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager
+                                 )
+{
+    OPJ_BYTE * l_current_data = 00;
+    OPJ_UINT32 l_rgn_size;
+    opj_cp_t *l_cp = 00;
+    opj_tcp_t *l_tcp = 00;
+    opj_tccp_t *l_tccp = 00;
+    OPJ_UINT32 l_comp_room;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    l_cp = &(p_j2k->m_cp);
+    l_tcp = &l_cp->tcps[p_tile_no];
+    l_tccp = &l_tcp->tccps[p_comp_no];
+
+    if (nb_comps <= 256) {
+        l_comp_room = 1;
+    } else {
+        l_comp_room = 2;
+    }
+
+    l_rgn_size = 6 + l_comp_room;
+
+    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
+
+    opj_write_bytes(l_current_data, J2K_MS_RGN,
+                    2);                                   /* RGN  */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, l_rgn_size - 2,
+                    2);                                 /* Lrgn */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, p_comp_no,
+                    l_comp_room);                          /* Crgn */
+    l_current_data += l_comp_room;
+
+    opj_write_bytes(l_current_data, 0,
+                    1);                                           /* Srgn */
+    ++l_current_data;
+
+    opj_write_bytes(l_current_data, (OPJ_UINT32)l_tccp->roishift,
+                    1);                            /* SPrgn */
+    ++l_current_data;
+
+    if (opj_stream_write_data(p_stream,
+                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_rgn_size,
+                              p_manager) != l_rgn_size) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_eoc(opj_j2k_t *p_j2k,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager
+                                 )
+{
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    opj_write_bytes(p_j2k->m_specific_param.m_encoder.m_header_tile_data,
+                    J2K_MS_EOC, 2);                                    /* EOC */
+
+    /* UniPG>> */
+#ifdef USE_JPWL
+    /* update markers struct */
+    /*
+    OPJ_BOOL res = j2k_add_marker(p_j2k->cstr_info, J2K_MS_EOC, p_stream_tell(p_stream) - 2, 2);
+    */
+#endif /* USE_JPWL */
+
+    if (opj_stream_write_data(p_stream,
+                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, 2, p_manager) != 2) {
+        return OPJ_FALSE;
+    }
+
+    if (! opj_stream_flush(p_stream, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a RGN marker (Region Of Interest)
+ *
+ * @param       p_header_data   the data contained in the POC box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the POC marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_rgn(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    OPJ_UINT32 l_nb_comp;
+    opj_image_t * l_image = 00;
+
+    opj_cp_t *l_cp = 00;
+    opj_tcp_t *l_tcp = 00;
+    OPJ_UINT32 l_comp_room, l_comp_no, l_roi_sty;
+
+    /* preconditions*/
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    l_image = p_j2k->m_private_image;
+    l_nb_comp = l_image->numcomps;
+
+    if (l_nb_comp <= 256) {
+        l_comp_room = 1;
+    } else {
+        l_comp_room = 2;
+    }
+
+    if (p_header_size != 2 + l_comp_room) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading RGN marker\n");
+        return OPJ_FALSE;
+    }
+
+    l_cp = &(p_j2k->m_cp);
+    l_tcp = (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH) ?
+            &l_cp->tcps[p_j2k->m_current_tile_number] :
+            p_j2k->m_specific_param.m_decoder.m_default_tcp;
+
+    opj_read_bytes(p_header_data, &l_comp_no, l_comp_room);         /* Crgn */
+    p_header_data += l_comp_room;
+    opj_read_bytes(p_header_data, &l_roi_sty,
+                   1);                                     /* Srgn */
+    ++p_header_data;
+
+#ifdef USE_JPWL
+    if (l_cp->correct) {
+        /* totlen is negative or larger than the bytes left!!! */
+        if (l_comp_room >= l_nb_comp) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "JPWL: bad component number in RGN (%d when there are only %d)\n",
+                          l_comp_room, l_nb_comp);
+            if (!JPWL_ASSUME) {
+                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
+                return OPJ_FALSE;
+            }
+        }
+    };
+#endif /* USE_JPWL */
+
+    /* testcase 3635.pdf.asan.77.2930 */
+    if (l_comp_no >= l_nb_comp) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "bad component number in RGN (%d when there are only %d)\n",
+                      l_comp_no, l_nb_comp);
+        return OPJ_FALSE;
+    }
+
+    opj_read_bytes(p_header_data,
+                   (OPJ_UINT32 *)(&(l_tcp->tccps[l_comp_no].roishift)), 1);  /* SPrgn */
+    ++p_header_data;
+
+    return OPJ_TRUE;
+
+}
+
+static OPJ_FLOAT32 opj_j2k_get_tp_stride(opj_tcp_t * p_tcp)
+{
+    return (OPJ_FLOAT32)((p_tcp->m_nb_tile_parts - 1) * 14);
+}
+
+static OPJ_FLOAT32 opj_j2k_get_default_stride(opj_tcp_t * p_tcp)
+{
+    (void)p_tcp;
+    return 0;
+}
+
+static OPJ_BOOL opj_j2k_update_rates(opj_j2k_t *p_j2k,
+                                     opj_stream_private_t *p_stream,
+                                     opj_event_mgr_t * p_manager)
+{
+    opj_cp_t * l_cp = 00;
+    opj_image_t * l_image = 00;
+    opj_tcp_t * l_tcp = 00;
+    opj_image_comp_t * l_img_comp = 00;
+
+    OPJ_UINT32 i, j, k;
+    OPJ_INT32 l_x0, l_y0, l_x1, l_y1;
+    OPJ_FLOAT32 * l_rates = 0;
+    OPJ_FLOAT32 l_sot_remove;
+    OPJ_UINT32 l_bits_empty, l_size_pixel;
+    OPJ_UINT64 l_tile_size = 0;
+    OPJ_UINT32 l_last_res;
+    OPJ_FLOAT32(* l_tp_stride_func)(opj_tcp_t *) = 00;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    OPJ_UNUSED(p_manager);
+
+    l_cp = &(p_j2k->m_cp);
+    l_image = p_j2k->m_private_image;
+    l_tcp = l_cp->tcps;
+
+    l_bits_empty = 8 * l_image->comps->dx * l_image->comps->dy;
+    l_size_pixel = l_image->numcomps * l_image->comps->prec;
+    l_sot_remove = (OPJ_FLOAT32) opj_stream_tell(p_stream) / (OPJ_FLOAT32)(
+                       l_cp->th * l_cp->tw);
+
+    if (l_cp->m_specific_param.m_enc.m_tp_on) {
+        l_tp_stride_func = opj_j2k_get_tp_stride;
+    } else {
+        l_tp_stride_func = opj_j2k_get_default_stride;
+    }
+
+    for (i = 0; i < l_cp->th; ++i) {
+        for (j = 0; j < l_cp->tw; ++j) {
+            OPJ_FLOAT32 l_offset = (OPJ_FLOAT32)(*l_tp_stride_func)(l_tcp) /
+                                   (OPJ_FLOAT32)l_tcp->numlayers;
+
+            /* 4 borders of the tile rescale on the image if necessary */
+            l_x0 = opj_int_max((OPJ_INT32)(l_cp->tx0 + j * l_cp->tdx),
+                               (OPJ_INT32)l_image->x0);
+            l_y0 = opj_int_max((OPJ_INT32)(l_cp->ty0 + i * l_cp->tdy),
+                               (OPJ_INT32)l_image->y0);
+            l_x1 = opj_int_min((OPJ_INT32)(l_cp->tx0 + (j + 1) * l_cp->tdx),
+                               (OPJ_INT32)l_image->x1);
+            l_y1 = opj_int_min((OPJ_INT32)(l_cp->ty0 + (i + 1) * l_cp->tdy),
+                               (OPJ_INT32)l_image->y1);
+
+            l_rates = l_tcp->rates;
+
+            /* Modification of the RATE >> */
+            for (k = 0; k < l_tcp->numlayers; ++k) {
+                if (*l_rates > 0.0f) {
+                    *l_rates = (OPJ_FLOAT32)(((OPJ_FLOAT64)l_size_pixel * (OPJ_UINT32)(
+                                                  l_x1 - l_x0) *
+                                              (OPJ_UINT32)(l_y1 - l_y0))
+                                             / ((*l_rates) * (OPJ_FLOAT32)l_bits_empty))
+                               -
+                               l_offset;
+                }
+
+                ++l_rates;
+            }
+
+            ++l_tcp;
+
+        }
+    }
+
+    l_tcp = l_cp->tcps;
+
+    for (i = 0; i < l_cp->th; ++i) {
+        for (j = 0; j < l_cp->tw; ++j) {
+            l_rates = l_tcp->rates;
+
+            if (*l_rates > 0.0f) {
+                *l_rates -= l_sot_remove;
+
+                if (*l_rates < 30.0f) {
+                    *l_rates = 30.0f;
+                }
+            }
+
+            ++l_rates;
+
+            l_last_res = l_tcp->numlayers - 1;
+
+            for (k = 1; k < l_last_res; ++k) {
+
+                if (*l_rates > 0.0f) {
+                    *l_rates -= l_sot_remove;
+
+                    if (*l_rates < * (l_rates - 1) + 10.0f) {
+                        *l_rates  = (*(l_rates - 1)) + 20.0f;
+                    }
+                }
+
+                ++l_rates;
+            }
+
+            if (*l_rates > 0.0f) {
+                *l_rates -= (l_sot_remove + 2.f);
+
+                if (*l_rates < * (l_rates - 1) + 10.0f) {
+                    *l_rates  = (*(l_rates - 1)) + 20.0f;
+                }
+            }
+
+            ++l_tcp;
+        }
+    }
+
+    l_img_comp = l_image->comps;
+    l_tile_size = 0;
+
+    for (i = 0; i < l_image->numcomps; ++i) {
+        l_tile_size += (OPJ_UINT64)opj_uint_ceildiv(l_cp->tdx, l_img_comp->dx)
+                       *
+                       opj_uint_ceildiv(l_cp->tdy, l_img_comp->dy)
+                       *
+                       l_img_comp->prec;
+
+        ++l_img_comp;
+    }
+
+    /* TODO: where does this magic value come from ? */
+    /* This used to be 1.3 / 8, but with random data and very small code */
+    /* block sizes, this is not enough. For example with */
+    /* bin/test_tile_encoder 1 256 256 32 32 8 0 reversible_with_precinct.j2k 4 4 3 0 0 1 16 16 */
+    /* TODO revise this to take into account the overhead linked to the */
+    /* number of packets and number of code blocks in packets */
+    l_tile_size = (OPJ_UINT64)((double)l_tile_size * 1.4 / 8);
+
+    /* Arbitrary amount to make the following work: */
+    /* bin/test_tile_encoder 1 256 256 17 16 8 0 reversible_no_precinct.j2k 4 4 3 0 0 1 */
+    l_tile_size += 500;
+
+    l_tile_size += opj_j2k_get_specific_header_sizes(p_j2k);
+
+    if (l_tile_size > UINT_MAX) {
+        l_tile_size = UINT_MAX;
+    }
+
+    p_j2k->m_specific_param.m_encoder.m_encoded_tile_size = (OPJ_UINT32)l_tile_size;
+    p_j2k->m_specific_param.m_encoder.m_encoded_tile_data =
+        (OPJ_BYTE *) opj_malloc(p_j2k->m_specific_param.m_encoder.m_encoded_tile_size);
+    if (p_j2k->m_specific_param.m_encoder.m_encoded_tile_data == 00) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Not enough memory to allocate m_encoded_tile_data. %u MB required\n",
+                      (OPJ_UINT32)(l_tile_size / 1024 / 1024));
+        return OPJ_FALSE;
+    }
+
+    if (p_j2k->m_specific_param.m_encoder.m_TLM) {
+        p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer =
+            (OPJ_BYTE *) opj_malloc(6 *
+                                    p_j2k->m_specific_param.m_encoder.m_total_tile_parts);
+        if (! p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer) {
+            return OPJ_FALSE;
+        }
+
+        p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current =
+            p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer;
+    }
+
+    return OPJ_TRUE;
+}
+
+#if 0
+static OPJ_BOOL opj_j2k_read_eoc(opj_j2k_t *p_j2k,
+                                 opj_stream_private_t *p_stream,
+                                 opj_event_mgr_t * p_manager)
+{
+    OPJ_UINT32 i;
+    opj_tcd_t * l_tcd = 00;
+    OPJ_UINT32 l_nb_tiles;
+    opj_tcp_t * l_tcp = 00;
+    OPJ_BOOL l_success;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    l_nb_tiles = p_j2k->m_cp.th * p_j2k->m_cp.tw;
+    l_tcp = p_j2k->m_cp.tcps;
+
+    l_tcd = opj_tcd_create(OPJ_TRUE);
+    if (l_tcd == 00) {
+        opj_event_msg(p_manager, EVT_ERROR, "Cannot decode tile, memory error\n");
+        return OPJ_FALSE;
+    }
+
+    for (i = 0; i < l_nb_tiles; ++i) {
+        if (l_tcp->m_data) {
+            if (! opj_tcd_init_decode_tile(l_tcd, i)) {
+                opj_tcd_destroy(l_tcd);
+                opj_event_msg(p_manager, EVT_ERROR, "Cannot decode tile, memory error\n");
+                return OPJ_FALSE;
+            }
+
+            l_success = opj_tcd_decode_tile(l_tcd, l_tcp->m_data, l_tcp->m_data_size, i,
+                                            p_j2k->cstr_index);
+            /* cleanup */
+
+            if (! l_success) {
+                p_j2k->m_specific_param.m_decoder.m_state |= J2K_STATE_ERR;
+                break;
+            }
+        }
+
+        opj_j2k_tcp_destroy(l_tcp);
+        ++l_tcp;
+    }
+
+    opj_tcd_destroy(l_tcd);
+    return OPJ_TRUE;
+}
+#endif
+
+static OPJ_BOOL opj_j2k_get_end_header(opj_j2k_t *p_j2k,
+                                       struct opj_stream_private *p_stream,
+                                       struct opj_event_mgr * p_manager)
+{
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    OPJ_UNUSED(p_manager);
+
+    p_j2k->cstr_index->main_head_end = opj_stream_tell(p_stream);
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_mct_data_group(opj_j2k_t *p_j2k,
+        struct opj_stream_private *p_stream,
+        struct opj_event_mgr * p_manager)
+{
+    OPJ_UINT32 i;
+    opj_simple_mcc_decorrelation_data_t * l_mcc_record;
+    opj_mct_data_t * l_mct_record;
+    opj_tcp_t * l_tcp;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_stream != 00);
+    assert(p_manager != 00);
+
+    if (! opj_j2k_write_cbd(p_j2k, p_stream, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    l_tcp = &(p_j2k->m_cp.tcps[p_j2k->m_current_tile_number]);
+    l_mct_record = l_tcp->m_mct_records;
+
+    for (i = 0; i < l_tcp->m_nb_mct_records; ++i) {
+
+        if (! opj_j2k_write_mct_record(p_j2k, l_mct_record, p_stream, p_manager)) {
+            return OPJ_FALSE;
+        }
+
+        ++l_mct_record;
+    }
+
+    l_mcc_record = l_tcp->m_mcc_records;
+
+    for (i = 0; i < l_tcp->m_nb_mcc_records; ++i) {
+
+        if (! opj_j2k_write_mcc_record(p_j2k, l_mcc_record, p_stream, p_manager)) {
+            return OPJ_FALSE;
+        }
+
+        ++l_mcc_record;
+    }
+
+    if (! opj_j2k_write_mco(p_j2k, p_stream, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_all_coc(
+    opj_j2k_t *p_j2k,
+    struct opj_stream_private *p_stream,
+    struct opj_event_mgr * p_manager)
+{
+    OPJ_UINT32 compno;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    for (compno = 1; compno < p_j2k->m_private_image->numcomps; ++compno) {
+        /* cod is first component of first tile */
+        if (! opj_j2k_compare_coc(p_j2k, 0, compno)) {
+            if (! opj_j2k_write_coc(p_j2k, compno, p_stream, p_manager)) {
+                return OPJ_FALSE;
+            }
+        }
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_all_qcc(
+    opj_j2k_t *p_j2k,
+    struct opj_stream_private *p_stream,
+    struct opj_event_mgr * p_manager)
+{
+    OPJ_UINT32 compno;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    for (compno = 1; compno < p_j2k->m_private_image->numcomps; ++compno) {
+        /* qcd is first component of first tile */
+        if (! opj_j2k_compare_qcc(p_j2k, 0, compno)) {
+            if (! opj_j2k_write_qcc(p_j2k, compno, p_stream, p_manager)) {
+                return OPJ_FALSE;
+            }
+        }
+    }
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_regions(opj_j2k_t *p_j2k,
+                                      struct opj_stream_private *p_stream,
+                                      struct opj_event_mgr * p_manager)
+{
+    OPJ_UINT32 compno;
+    const opj_tccp_t *l_tccp = 00;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    l_tccp = p_j2k->m_cp.tcps->tccps;
+
+    for (compno = 0; compno < p_j2k->m_private_image->numcomps; ++compno)  {
+        if (l_tccp->roishift) {
+
+            if (! opj_j2k_write_rgn(p_j2k, 0, compno, p_j2k->m_private_image->numcomps,
+                                    p_stream, p_manager)) {
+                return OPJ_FALSE;
+            }
+        }
+
+        ++l_tccp;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_epc(opj_j2k_t *p_j2k,
+                                  struct opj_stream_private *p_stream,
+                                  struct opj_event_mgr * p_manager)
+{
+    opj_codestream_index_t * l_cstr_index = 00;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    OPJ_UNUSED(p_manager);
+
+    l_cstr_index = p_j2k->cstr_index;
+    if (l_cstr_index) {
+        l_cstr_index->codestream_size = (OPJ_UINT64)opj_stream_tell(p_stream);
+        /* UniPG>> */
+        /* The following adjustment is done to adjust the codestream size */
+        /* if SOD is not at 0 in the buffer. Useful in case of JP2, where */
+        /* the first bunch of bytes is not in the codestream              */
+        l_cstr_index->codestream_size -= (OPJ_UINT64)l_cstr_index->main_head_start;
+        /* <<UniPG */
+    }
+
+#ifdef USE_JPWL
+    /* preparation of JPWL marker segments */
+#if 0
+    if (cp->epc_on) {
+
+        /* encode according to JPWL */
+        jpwl_encode(p_j2k, p_stream, image);
+
+    }
+#endif
+    assert(0 && "TODO");
+#endif /* USE_JPWL */
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_read_unk(opj_j2k_t *p_j2k,
+                                 opj_stream_private_t *p_stream,
+                                 OPJ_UINT32 *output_marker,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    OPJ_UINT32 l_unknown_marker;
+    const opj_dec_memory_marker_handler_t * l_marker_handler;
+    OPJ_UINT32 l_size_unk = 2;
+
+    /* preconditions*/
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    opj_event_msg(p_manager, EVT_WARNING, "Unknown marker\n");
+
+    for (;;) {
+        /* Try to read 2 bytes (the next marker ID) from stream and copy them into the buffer*/
+        if (opj_stream_read_data(p_stream,
+                                 p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
+            opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
+            return OPJ_FALSE;
+        }
+
+        /* read 2 bytes as the new marker ID*/
+        opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data,
+                       &l_unknown_marker, 2);
+
+        if (!(l_unknown_marker < 0xff00)) {
+
+            /* Get the marker handler from the marker ID*/
+            l_marker_handler = opj_j2k_get_marker_handler(l_unknown_marker);
+
+            if (!(p_j2k->m_specific_param.m_decoder.m_state & l_marker_handler->states)) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Marker is not compliant with its position\n");
+                return OPJ_FALSE;
+            } else {
+                if (l_marker_handler->id != J2K_MS_UNK) {
+                    /* Add the marker to the codestream index*/
+                    if (l_marker_handler->id != J2K_MS_SOT) {
+                        OPJ_BOOL res = opj_j2k_add_mhmarker(p_j2k->cstr_index, J2K_MS_UNK,
+                                                            (OPJ_UINT32) opj_stream_tell(p_stream) - l_size_unk,
+                                                            l_size_unk);
+                        if (res == OPJ_FALSE) {
+                            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to add mh marker\n");
+                            return OPJ_FALSE;
+                        }
+                    }
+                    break; /* next marker is known and well located */
+                } else {
+                    l_size_unk += 2;
+                }
+            }
+        }
+    }
+
+    *output_marker = l_marker_handler->id ;
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_mct_record(opj_j2k_t *p_j2k,
+        opj_mct_data_t * p_mct_record,
+        struct opj_stream_private *p_stream,
+        struct opj_event_mgr * p_manager)
+{
+    OPJ_UINT32 l_mct_size;
+    OPJ_BYTE * l_current_data = 00;
+    OPJ_UINT32 l_tmp;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    l_mct_size = 10 + p_mct_record->m_data_size;
+
+    if (l_mct_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
+        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
+                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_mct_size);
+        if (! new_header_tile_data) {
+            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write MCT marker\n");
+            return OPJ_FALSE;
+        }
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_mct_size;
+    }
+
+    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
+
+    opj_write_bytes(l_current_data, J2K_MS_MCT,
+                    2);                                   /* MCT */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, l_mct_size - 2,
+                    2);                                 /* Lmct */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, 0,
+                    2);                                                    /* Zmct */
+    l_current_data += 2;
+
+    /* only one marker atm */
+    l_tmp = (p_mct_record->m_index & 0xff) | (p_mct_record->m_array_type << 8) |
+            (p_mct_record->m_element_type << 10);
+
+    opj_write_bytes(l_current_data, l_tmp, 2);
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, 0,
+                    2);                                                    /* Ymct */
+    l_current_data += 2;
+
+    memcpy(l_current_data, p_mct_record->m_data, p_mct_record->m_data_size);
+
+    if (opj_stream_write_data(p_stream,
+                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_mct_size,
+                              p_manager) != l_mct_size) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a MCT marker (Multiple Component Transform)
+ *
+ * @param       p_header_data   the data contained in the MCT box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the MCT marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_mct(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    OPJ_UINT32 i;
+    opj_tcp_t *l_tcp = 00;
+    OPJ_UINT32 l_tmp;
+    OPJ_UINT32 l_indix;
+    opj_mct_data_t * l_mct_data;
+    OPJ_BOOL new_mct = OPJ_FALSE;
+
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+
+    l_tcp = p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH ?
+            &p_j2k->m_cp.tcps[p_j2k->m_current_tile_number] :
+            p_j2k->m_specific_param.m_decoder.m_default_tcp;
+
+    if (p_header_size < 2) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading MCT marker\n");
+        return OPJ_FALSE;
+    }
+
+    /* first marker */
+    opj_read_bytes(p_header_data, &l_tmp, 2);                       /* Zmct */
+    p_header_data += 2;
+    if (l_tmp != 0) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "Cannot take in charge mct data within multiple MCT records\n");
+        return OPJ_TRUE;
+    }
+
+    if (p_header_size <= 6) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading MCT marker\n");
+        return OPJ_FALSE;
+    }
+
+    /* Imct -> no need for other values, take the first, type is double with decorrelation x0000 1101 0000 0000*/
+    opj_read_bytes(p_header_data, &l_tmp, 2);                       /* Imct */
+    p_header_data += 2;
+
+    l_indix = l_tmp & 0xff;
+    l_mct_data = l_tcp->m_mct_records;
+
+    for (i = 0; i < l_tcp->m_nb_mct_records; ++i) {
+        if (l_mct_data->m_index == l_indix) {
+            break;
+        }
+        ++l_mct_data;
+    }
+
+    /* NOT FOUND */
+    if (i == l_tcp->m_nb_mct_records) {
+        if (l_tcp->m_nb_mct_records == l_tcp->m_nb_max_mct_records) {
+            opj_mct_data_t *new_mct_records;
+            l_tcp->m_nb_max_mct_records += OPJ_J2K_MCT_DEFAULT_NB_RECORDS;
+
+            new_mct_records = (opj_mct_data_t *) opj_realloc(l_tcp->m_mct_records,
+                              l_tcp->m_nb_max_mct_records * sizeof(opj_mct_data_t));
+            if (! new_mct_records) {
+                opj_free(l_tcp->m_mct_records);
+                l_tcp->m_mct_records = NULL;
+                l_tcp->m_nb_max_mct_records = 0;
+                l_tcp->m_nb_mct_records = 0;
+                opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read MCT marker\n");
+                return OPJ_FALSE;
+            }
+
+            /* Update m_mcc_records[].m_offset_array and m_decorrelation_array
+             * to point to the new addresses */
+            if (new_mct_records != l_tcp->m_mct_records) {
+                for (i = 0; i < l_tcp->m_nb_mcc_records; ++i) {
+                    opj_simple_mcc_decorrelation_data_t* l_mcc_record =
+                        &(l_tcp->m_mcc_records[i]);
+                    if (l_mcc_record->m_decorrelation_array) {
+                        l_mcc_record->m_decorrelation_array =
+                            new_mct_records +
+                            (l_mcc_record->m_decorrelation_array -
+                             l_tcp->m_mct_records);
+                    }
+                    if (l_mcc_record->m_offset_array) {
+                        l_mcc_record->m_offset_array =
+                            new_mct_records +
+                            (l_mcc_record->m_offset_array -
+                             l_tcp->m_mct_records);
+                    }
+                }
+            }
+
+            l_tcp->m_mct_records = new_mct_records;
+            l_mct_data = l_tcp->m_mct_records + l_tcp->m_nb_mct_records;
+            memset(l_mct_data, 0, (l_tcp->m_nb_max_mct_records - l_tcp->m_nb_mct_records) *
+                   sizeof(opj_mct_data_t));
+        }
+
+        l_mct_data = l_tcp->m_mct_records + l_tcp->m_nb_mct_records;
+        new_mct = OPJ_TRUE;
+    }
+
+    if (l_mct_data->m_data) {
+        opj_free(l_mct_data->m_data);
+        l_mct_data->m_data = 00;
+        l_mct_data->m_data_size = 0;
+    }
+
+    l_mct_data->m_index = l_indix;
+    l_mct_data->m_array_type = (J2K_MCT_ARRAY_TYPE)((l_tmp  >> 8) & 3);
+    l_mct_data->m_element_type = (J2K_MCT_ELEMENT_TYPE)((l_tmp  >> 10) & 3);
+
+    opj_read_bytes(p_header_data, &l_tmp, 2);                       /* Ymct */
+    p_header_data += 2;
+    if (l_tmp != 0) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "Cannot take in charge multiple MCT markers\n");
+        return OPJ_TRUE;
+    }
+
+    p_header_size -= 6;
+
+    l_mct_data->m_data = (OPJ_BYTE*)opj_malloc(p_header_size);
+    if (! l_mct_data->m_data) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading MCT marker\n");
+        return OPJ_FALSE;
+    }
+    memcpy(l_mct_data->m_data, p_header_data, p_header_size);
+
+    l_mct_data->m_data_size = p_header_size;
+
+    if (new_mct) {
+            ++l_tcp->m_nb_mct_records;
+    }
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_mcc_record(opj_j2k_t *p_j2k,
+        struct opj_simple_mcc_decorrelation_data * p_mcc_record,
+        struct opj_stream_private *p_stream,
+        struct opj_event_mgr * p_manager)
+{
+    OPJ_UINT32 i;
+    OPJ_UINT32 l_mcc_size;
+    OPJ_BYTE * l_current_data = 00;
+    OPJ_UINT32 l_nb_bytes_for_comp;
+    OPJ_UINT32 l_mask;
+    OPJ_UINT32 l_tmcc;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    if (p_mcc_record->m_nb_comps > 255) {
+        l_nb_bytes_for_comp = 2;
+        l_mask = 0x8000;
+    } else {
+        l_nb_bytes_for_comp = 1;
+        l_mask = 0;
+    }
+
+    l_mcc_size = p_mcc_record->m_nb_comps * 2 * l_nb_bytes_for_comp + 19;
+    if (l_mcc_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
+        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
+                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_mcc_size);
+        if (! new_header_tile_data) {
+            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write MCC marker\n");
+            return OPJ_FALSE;
+        }
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_mcc_size;
+    }
+
+    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
+
+    opj_write_bytes(l_current_data, J2K_MS_MCC,
+                    2);                                   /* MCC */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, l_mcc_size - 2,
+                    2);                                 /* Lmcc */
+    l_current_data += 2;
+
+    /* first marker */
+    opj_write_bytes(l_current_data, 0,
+                    2);                                  /* Zmcc */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, p_mcc_record->m_index,
+                    1);                                        /* Imcc -> no need for other values, take the first */
+    ++l_current_data;
+
+    /* only one marker atm */
+    opj_write_bytes(l_current_data, 0,
+                    2);                                  /* Ymcc */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, 1,
+                    2);                                  /* Qmcc -> number of collections -> 1 */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, 0x1,
+                    1);                                /* Xmcci type of component transformation -> array based decorrelation */
+    ++l_current_data;
+
+    opj_write_bytes(l_current_data, p_mcc_record->m_nb_comps | l_mask,
+                    2);  /* Nmcci number of input components involved and size for each component offset = 8 bits */
+    l_current_data += 2;
+
+    for (i = 0; i < p_mcc_record->m_nb_comps; ++i) {
+        opj_write_bytes(l_current_data, i,
+                        l_nb_bytes_for_comp);                          /* Cmccij Component offset*/
+        l_current_data += l_nb_bytes_for_comp;
+    }
+
+    opj_write_bytes(l_current_data, p_mcc_record->m_nb_comps | l_mask,
+                    2);  /* Mmcci number of output components involved and size for each component offset = 8 bits */
+    l_current_data += 2;
+
+    for (i = 0; i < p_mcc_record->m_nb_comps; ++i) {
+        opj_write_bytes(l_current_data, i,
+                        l_nb_bytes_for_comp);                          /* Wmccij Component offset*/
+        l_current_data += l_nb_bytes_for_comp;
+    }
+
+    l_tmcc = ((!p_mcc_record->m_is_irreversible) & 1U) << 16;
+
+    if (p_mcc_record->m_decorrelation_array) {
+        l_tmcc |= p_mcc_record->m_decorrelation_array->m_index;
+    }
+
+    if (p_mcc_record->m_offset_array) {
+        l_tmcc |= ((p_mcc_record->m_offset_array->m_index) << 8);
+    }
+
+    opj_write_bytes(l_current_data, l_tmcc,
+                    3);     /* Tmcci : use MCT defined as number 1 and irreversible array based. */
+    l_current_data += 3;
+
+    if (opj_stream_write_data(p_stream,
+                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_mcc_size,
+                              p_manager) != l_mcc_size) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_read_mcc(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager)
+{
+    OPJ_UINT32 i, j;
+    OPJ_UINT32 l_tmp;
+    OPJ_UINT32 l_indix;
+    opj_tcp_t * l_tcp;
+    opj_simple_mcc_decorrelation_data_t * l_mcc_record;
+    opj_mct_data_t * l_mct_data;
+    OPJ_UINT32 l_nb_collections;
+    OPJ_UINT32 l_nb_comps;
+    OPJ_UINT32 l_nb_bytes_by_comp;
+    OPJ_BOOL l_new_mcc = OPJ_FALSE;
+
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    l_tcp = p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH ?
+            &p_j2k->m_cp.tcps[p_j2k->m_current_tile_number] :
+            p_j2k->m_specific_param.m_decoder.m_default_tcp;
+
+    if (p_header_size < 2) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading MCC marker\n");
+        return OPJ_FALSE;
+    }
+
+    /* first marker */
+    opj_read_bytes(p_header_data, &l_tmp, 2);                       /* Zmcc */
+    p_header_data += 2;
+    if (l_tmp != 0) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "Cannot take in charge multiple data spanning\n");
+        return OPJ_TRUE;
+    }
+
+    if (p_header_size < 7) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading MCC marker\n");
+        return OPJ_FALSE;
+    }
+
+    opj_read_bytes(p_header_data, &l_indix,
+                   1); /* Imcc -> no need for other values, take the first */
+    ++p_header_data;
+
+    l_mcc_record = l_tcp->m_mcc_records;
+
+    for (i = 0; i < l_tcp->m_nb_mcc_records; ++i) {
+        if (l_mcc_record->m_index == l_indix) {
+            break;
+        }
+        ++l_mcc_record;
+    }
+
+    /** NOT FOUND */
+    if (i == l_tcp->m_nb_mcc_records) {
+        if (l_tcp->m_nb_mcc_records == l_tcp->m_nb_max_mcc_records) {
+            opj_simple_mcc_decorrelation_data_t *new_mcc_records;
+            l_tcp->m_nb_max_mcc_records += OPJ_J2K_MCC_DEFAULT_NB_RECORDS;
+
+            new_mcc_records = (opj_simple_mcc_decorrelation_data_t *) opj_realloc(
+                                  l_tcp->m_mcc_records, l_tcp->m_nb_max_mcc_records * sizeof(
+                                      opj_simple_mcc_decorrelation_data_t));
+            if (! new_mcc_records) {
+                opj_free(l_tcp->m_mcc_records);
+                l_tcp->m_mcc_records = NULL;
+                l_tcp->m_nb_max_mcc_records = 0;
+                l_tcp->m_nb_mcc_records = 0;
+                opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read MCC marker\n");
+                return OPJ_FALSE;
+            }
+            l_tcp->m_mcc_records = new_mcc_records;
+            l_mcc_record = l_tcp->m_mcc_records + l_tcp->m_nb_mcc_records;
+            memset(l_mcc_record, 0, (l_tcp->m_nb_max_mcc_records - l_tcp->m_nb_mcc_records)
+                   * sizeof(opj_simple_mcc_decorrelation_data_t));
+        }
+        l_mcc_record = l_tcp->m_mcc_records + l_tcp->m_nb_mcc_records;
+        l_new_mcc = OPJ_TRUE;
+    }
+    l_mcc_record->m_index = l_indix;
+
+    /* only one marker atm */
+    opj_read_bytes(p_header_data, &l_tmp, 2);                       /* Ymcc */
+    p_header_data += 2;
+    if (l_tmp != 0) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "Cannot take in charge multiple data spanning\n");
+        return OPJ_TRUE;
+    }
+
+    opj_read_bytes(p_header_data, &l_nb_collections,
+                   2);                              /* Qmcc -> number of collections -> 1 */
+    p_header_data += 2;
+
+    if (l_nb_collections > 1) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "Cannot take in charge multiple collections\n");
+        return OPJ_TRUE;
+    }
+
+    p_header_size -= 7;
+
+    for (i = 0; i < l_nb_collections; ++i) {
+        if (p_header_size < 3) {
+            opj_event_msg(p_manager, EVT_ERROR, "Error reading MCC marker\n");
+            return OPJ_FALSE;
+        }
+
+        opj_read_bytes(p_header_data, &l_tmp,
+                       1); /* Xmcci type of component transformation -> array based decorrelation */
+        ++p_header_data;
+
+        if (l_tmp != 1) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "Cannot take in charge collections other than array decorrelation\n");
+            return OPJ_TRUE;
+        }
+
+        opj_read_bytes(p_header_data, &l_nb_comps, 2);
+
+        p_header_data += 2;
+        p_header_size -= 3;
+
+        l_nb_bytes_by_comp = 1 + (l_nb_comps >> 15);
+        l_mcc_record->m_nb_comps = l_nb_comps & 0x7fff;
+
+        if (p_header_size < (l_nb_bytes_by_comp * l_mcc_record->m_nb_comps + 2)) {
+            opj_event_msg(p_manager, EVT_ERROR, "Error reading MCC marker\n");
+            return OPJ_FALSE;
+        }
+
+        p_header_size -= (l_nb_bytes_by_comp * l_mcc_record->m_nb_comps + 2);
+
+        for (j = 0; j < l_mcc_record->m_nb_comps; ++j) {
+            opj_read_bytes(p_header_data, &l_tmp,
+                           l_nb_bytes_by_comp);      /* Cmccij Component offset*/
+            p_header_data += l_nb_bytes_by_comp;
+
+            if (l_tmp != j) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "Cannot take in charge collections with indix shuffle\n");
+                return OPJ_TRUE;
+            }
+        }
+
+        opj_read_bytes(p_header_data, &l_nb_comps, 2);
+        p_header_data += 2;
+
+        l_nb_bytes_by_comp = 1 + (l_nb_comps >> 15);
+        l_nb_comps &= 0x7fff;
+
+        if (l_nb_comps != l_mcc_record->m_nb_comps) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "Cannot take in charge collections without same number of indixes\n");
+            return OPJ_TRUE;
+        }
+
+        if (p_header_size < (l_nb_bytes_by_comp * l_mcc_record->m_nb_comps + 3)) {
+            opj_event_msg(p_manager, EVT_ERROR, "Error reading MCC marker\n");
+            return OPJ_FALSE;
+        }
+
+        p_header_size -= (l_nb_bytes_by_comp * l_mcc_record->m_nb_comps + 3);
+
+        for (j = 0; j < l_mcc_record->m_nb_comps; ++j) {
+            opj_read_bytes(p_header_data, &l_tmp,
+                           l_nb_bytes_by_comp);      /* Wmccij Component offset*/
+            p_header_data += l_nb_bytes_by_comp;
+
+            if (l_tmp != j) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "Cannot take in charge collections with indix shuffle\n");
+                return OPJ_TRUE;
+            }
+        }
+
+        opj_read_bytes(p_header_data, &l_tmp, 3); /* Wmccij Component offset*/
+        p_header_data += 3;
+
+        l_mcc_record->m_is_irreversible = !((l_tmp >> 16) & 1);
+        l_mcc_record->m_decorrelation_array = 00;
+        l_mcc_record->m_offset_array = 00;
+
+        l_indix = l_tmp & 0xff;
+        if (l_indix != 0) {
+            l_mct_data = l_tcp->m_mct_records;
+            for (j = 0; j < l_tcp->m_nb_mct_records; ++j) {
+                if (l_mct_data->m_index == l_indix) {
+                    l_mcc_record->m_decorrelation_array = l_mct_data;
+                    break;
+                }
+                ++l_mct_data;
+            }
+
+            if (l_mcc_record->m_decorrelation_array == 00) {
+                opj_event_msg(p_manager, EVT_ERROR, "Error reading MCC marker\n");
+                return OPJ_FALSE;
+            }
+        }
+
+        l_indix = (l_tmp >> 8) & 0xff;
+        if (l_indix != 0) {
+            l_mct_data = l_tcp->m_mct_records;
+            for (j = 0; j < l_tcp->m_nb_mct_records; ++j) {
+                if (l_mct_data->m_index == l_indix) {
+                    l_mcc_record->m_offset_array = l_mct_data;
+                    break;
+                }
+                ++l_mct_data;
+            }
+
+            if (l_mcc_record->m_offset_array == 00) {
+                opj_event_msg(p_manager, EVT_ERROR, "Error reading MCC marker\n");
+                return OPJ_FALSE;
+            }
+        }
+    }
+
+    if (p_header_size != 0) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading MCC marker\n");
+        return OPJ_FALSE;
+    }
+
+    if (l_new_mcc) {
+        ++l_tcp->m_nb_mcc_records;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_mco(opj_j2k_t *p_j2k,
+                                  struct opj_stream_private *p_stream,
+                                  struct opj_event_mgr * p_manager
+                                 )
+{
+    OPJ_BYTE * l_current_data = 00;
+    OPJ_UINT32 l_mco_size;
+    opj_tcp_t * l_tcp = 00;
+    opj_simple_mcc_decorrelation_data_t * l_mcc_record;
+    OPJ_UINT32 i;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    l_tcp = &(p_j2k->m_cp.tcps[p_j2k->m_current_tile_number]);
+
+    l_mco_size = 5 + l_tcp->m_nb_mcc_records;
+    if (l_mco_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
+
+        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
+                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_mco_size);
+        if (! new_header_tile_data) {
+            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write MCO marker\n");
+            return OPJ_FALSE;
+        }
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_mco_size;
+    }
+    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
+
+
+    opj_write_bytes(l_current_data, J2K_MS_MCO, 2);                 /* MCO */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, l_mco_size - 2, 2);             /* Lmco */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, l_tcp->m_nb_mcc_records,
+                    1);    /* Nmco : only one transform stage*/
+    ++l_current_data;
+
+    l_mcc_record = l_tcp->m_mcc_records;
+    for (i = 0; i < l_tcp->m_nb_mcc_records; ++i) {
+        opj_write_bytes(l_current_data, l_mcc_record->m_index,
+                        1); /* Imco -> use the mcc indicated by 1*/
+        ++l_current_data;
+        ++l_mcc_record;
+    }
+
+    if (opj_stream_write_data(p_stream,
+                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_mco_size,
+                              p_manager) != l_mco_size) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a MCO marker (Multiple Component Transform Ordering)
+ *
+ * @param       p_header_data   the data contained in the MCO box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the MCO marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_mco(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    OPJ_UINT32 l_tmp, i;
+    OPJ_UINT32 l_nb_stages;
+    opj_tcp_t * l_tcp;
+    opj_tccp_t * l_tccp;
+    opj_image_t * l_image;
+
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    l_image = p_j2k->m_private_image;
+    l_tcp = p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH ?
+            &p_j2k->m_cp.tcps[p_j2k->m_current_tile_number] :
+            p_j2k->m_specific_param.m_decoder.m_default_tcp;
+
+    if (p_header_size < 1) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading MCO marker\n");
+        return OPJ_FALSE;
+    }
+
+    opj_read_bytes(p_header_data, &l_nb_stages,
+                   1);                         /* Nmco : only one transform stage*/
+    ++p_header_data;
+
+    if (l_nb_stages > 1) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "Cannot take in charge multiple transformation stages.\n");
+        return OPJ_TRUE;
+    }
+
+    if (p_header_size != l_nb_stages + 1) {
+        opj_event_msg(p_manager, EVT_WARNING, "Error reading MCO marker\n");
+        return OPJ_FALSE;
+    }
+
+    l_tccp = l_tcp->tccps;
+
+    for (i = 0; i < l_image->numcomps; ++i) {
+        l_tccp->m_dc_level_shift = 0;
+        ++l_tccp;
+    }
+
+    if (l_tcp->m_mct_decoding_matrix) {
+        opj_free(l_tcp->m_mct_decoding_matrix);
+        l_tcp->m_mct_decoding_matrix = 00;
+    }
+
+    for (i = 0; i < l_nb_stages; ++i) {
+        opj_read_bytes(p_header_data, &l_tmp, 1);
+        ++p_header_data;
+
+        if (! opj_j2k_add_mct(l_tcp, p_j2k->m_private_image, l_tmp)) {
+            return OPJ_FALSE;
+        }
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_add_mct(opj_tcp_t * p_tcp, opj_image_t * p_image,
+                                OPJ_UINT32 p_index)
+{
+    OPJ_UINT32 i;
+    opj_simple_mcc_decorrelation_data_t * l_mcc_record;
+    opj_mct_data_t * l_deco_array, * l_offset_array;
+    OPJ_UINT32 l_data_size, l_mct_size, l_offset_size;
+    OPJ_UINT32 l_nb_elem;
+    OPJ_UINT32 * l_offset_data, * l_current_offset_data;
+    opj_tccp_t * l_tccp;
+
+    /* preconditions */
+    assert(p_tcp != 00);
+
+    l_mcc_record = p_tcp->m_mcc_records;
+
+    for (i = 0; i < p_tcp->m_nb_mcc_records; ++i) {
+        if (l_mcc_record->m_index == p_index) {
+            break;
+        }
+    }
+
+    if (i == p_tcp->m_nb_mcc_records) {
+        /** element discarded **/
+        return OPJ_TRUE;
+    }
+
+    if (l_mcc_record->m_nb_comps != p_image->numcomps) {
+        /** do not support number of comps != image */
+        return OPJ_TRUE;
+    }
+
+    l_deco_array = l_mcc_record->m_decorrelation_array;
+
+    if (l_deco_array) {
+        l_data_size = MCT_ELEMENT_SIZE[l_deco_array->m_element_type] * p_image->numcomps
+                      * p_image->numcomps;
+        if (l_deco_array->m_data_size != l_data_size || ! l_deco_array->m_data) {
+            return OPJ_FALSE;
+        }
+
+        l_nb_elem = p_image->numcomps * p_image->numcomps;
+        l_mct_size = l_nb_elem * (OPJ_UINT32)sizeof(OPJ_FLOAT32);
+        p_tcp->m_mct_decoding_matrix = (OPJ_FLOAT32*)opj_malloc(l_mct_size);
+
+        if (! p_tcp->m_mct_decoding_matrix) {
+            return OPJ_FALSE;
+        }
+
+        j2k_mct_read_functions_to_float[l_deco_array->m_element_type](
+            l_deco_array->m_data, p_tcp->m_mct_decoding_matrix, l_nb_elem);
+    }
+
+    l_offset_array = l_mcc_record->m_offset_array;
+
+    if (l_offset_array) {
+        l_data_size = MCT_ELEMENT_SIZE[l_offset_array->m_element_type] *
+                      p_image->numcomps;
+        if (l_offset_array->m_data_size != l_data_size || ! l_offset_array->m_data) {
+            return OPJ_FALSE;
+        }
+
+        l_nb_elem = p_image->numcomps;
+        l_offset_size = l_nb_elem * (OPJ_UINT32)sizeof(OPJ_UINT32);
+        l_offset_data = (OPJ_UINT32*)opj_malloc(l_offset_size);
+
+        if (! l_offset_data) {
+            return OPJ_FALSE;
+        }
+
+        j2k_mct_read_functions_to_int32[l_offset_array->m_element_type](
+            l_offset_array->m_data, l_offset_data, l_nb_elem);
+
+        l_tccp = p_tcp->tccps;
+        l_current_offset_data = l_offset_data;
+
+        for (i = 0; i < p_image->numcomps; ++i) {
+            l_tccp->m_dc_level_shift = (OPJ_INT32) * (l_current_offset_data++);
+            ++l_tccp;
+        }
+
+        opj_free(l_offset_data);
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_cbd(opj_j2k_t *p_j2k,
+                                  struct opj_stream_private *p_stream,
+                                  struct opj_event_mgr * p_manager)
+{
+    OPJ_UINT32 i;
+    OPJ_UINT32 l_cbd_size;
+    OPJ_BYTE * l_current_data = 00;
+    opj_image_t *l_image = 00;
+    opj_image_comp_t * l_comp = 00;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    l_image = p_j2k->m_private_image;
+    l_cbd_size = 6 + p_j2k->m_private_image->numcomps;
+
+    if (l_cbd_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
+        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
+                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_cbd_size);
+        if (! new_header_tile_data) {
+            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write CBD marker\n");
+            return OPJ_FALSE;
+        }
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_cbd_size;
+    }
+
+    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
+
+    opj_write_bytes(l_current_data, J2K_MS_CBD, 2);                 /* CBD */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, l_cbd_size - 2, 2);             /* L_CBD */
+    l_current_data += 2;
+
+    opj_write_bytes(l_current_data, l_image->numcomps, 2);          /* Ncbd */
+    l_current_data += 2;
+
+    l_comp = l_image->comps;
+
+    for (i = 0; i < l_image->numcomps; ++i) {
+        opj_write_bytes(l_current_data, (l_comp->sgnd << 7) | (l_comp->prec - 1),
+                        1);           /* Component bit depth */
+        ++l_current_data;
+
+        ++l_comp;
+    }
+
+    if (opj_stream_write_data(p_stream,
+                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_cbd_size,
+                              p_manager) != l_cbd_size) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a CBD marker (Component bit depth definition)
+ * @param       p_header_data   the data contained in the CBD box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the CBD marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_cbd(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    OPJ_UINT32 l_nb_comp, l_num_comp;
+    OPJ_UINT32 l_comp_def;
+    OPJ_UINT32 i;
+    opj_image_comp_t * l_comp = 00;
+
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    l_num_comp = p_j2k->m_private_image->numcomps;
+
+    if (p_header_size != (p_j2k->m_private_image->numcomps + 2)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Crror reading CBD marker\n");
+        return OPJ_FALSE;
+    }
+
+    opj_read_bytes(p_header_data, &l_nb_comp,
+                   2);                           /* Ncbd */
+    p_header_data += 2;
+
+    if (l_nb_comp != l_num_comp) {
+        opj_event_msg(p_manager, EVT_ERROR, "Crror reading CBD marker\n");
+        return OPJ_FALSE;
+    }
+
+    l_comp = p_j2k->m_private_image->comps;
+    for (i = 0; i < l_num_comp; ++i) {
+        opj_read_bytes(p_header_data, &l_comp_def,
+                       1);                  /* Component bit depth */
+        ++p_header_data;
+        l_comp->sgnd = (l_comp_def >> 7) & 1;
+        l_comp->prec = (l_comp_def & 0x7f) + 1;
+
+        if (l_comp->prec > 31) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Invalid values for comp = %d : prec=%u (should be between 1 and 38 according to the JPEG2000 norm. OpenJpeg only supports up to 31)\n",
+                          i, l_comp->prec);
+            return OPJ_FALSE;
+        }
+        ++l_comp;
+    }
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a CAP marker (extended capabilities definition). Empty implementation.
+ * Found in HTJ2K files.
+ *
+ * @param       p_header_data   the data contained in the CAP box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the CAP marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_cap(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    (void)p_j2k;
+    (void)p_header_data;
+    (void)p_header_size;
+    (void)p_manager;
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a CPF marker (corresponding profile). Empty implementation. Found in HTJ2K files
+ * @param       p_header_data   the data contained in the CPF box.
+ * @param       p_j2k                   the jpeg2000 codec.
+ * @param       p_header_size   the size of the data contained in the CPF marker.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_read_cpf(opj_j2k_t *p_j2k,
+                                 OPJ_BYTE * p_header_data,
+                                 OPJ_UINT32 p_header_size,
+                                 opj_event_mgr_t * p_manager
+                                )
+{
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    (void)p_j2k;
+    (void)p_header_data;
+    (void)p_header_size;
+    (void)p_manager;
+
+    return OPJ_TRUE;
+}
+
+/* ----------------------------------------------------------------------- */
+/* J2K / JPT decoder interface                                             */
+/* ----------------------------------------------------------------------- */
+
+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;
+
+        j2k->dump_state = (parameters->flags & OPJ_DPARAMETERS_DUMP_FLAG);
+#ifdef USE_JPWL
+        j2k->m_cp.correct = parameters->jpwl_correct;
+        j2k->m_cp.exp_comps = parameters->jpwl_exp_comps;
+        j2k->m_cp.max_tiles = parameters->jpwl_max_tiles;
+#endif /* USE_JPWL */
+    }
+}
+
+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(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) {
+        opj_thread_pool_destroy(j2k->m_tp);
+        j2k->m_tp = NULL;
+        if (num_threads <= (OPJ_UINT32)INT_MAX) {
+            j2k->m_tp = opj_thread_pool_create((int)num_threads);
+        }
+        if (j2k->m_tp == NULL) {
+            j2k->m_tp = opj_thread_pool_create(0);
+            return OPJ_FALSE;
+        }
+        return OPJ_TRUE;
+    }
+    return OPJ_FALSE;
+}
+
+static int opj_j2k_get_default_thread_count()
+{
+    const char* num_threads_str = getenv("OPJ_NUM_THREADS");
+    int num_cpus;
+    int num_threads;
+
+    if (num_threads_str == NULL || !opj_has_thread_support()) {
+        return 0;
+    }
+    num_cpus = opj_get_num_cpus();
+    if (strcmp(num_threads_str, "ALL_CPUS") == 0) {
+        return num_cpus;
+    }
+    if (num_cpus == 0) {
+        num_cpus = 32;
+    }
+    num_threads = atoi(num_threads_str);
+    if (num_threads < 0) {
+        num_threads = 0;
+    } else if (num_threads > 2 * num_cpus) {
+        num_threads = 2 * num_cpus;
+    }
+    return num_threads;
+}
+
+/* ----------------------------------------------------------------------- */
+/* J2K encoder interface                                                       */
+/* ----------------------------------------------------------------------- */
+
+opj_j2k_t* opj_j2k_create_compress(void)
+{
+    opj_j2k_t *l_j2k = (opj_j2k_t*) opj_calloc(1, sizeof(opj_j2k_t));
+    if (!l_j2k) {
+        return NULL;
+    }
+
+
+    l_j2k->m_is_decoder = 0;
+    l_j2k->m_cp.m_is_decoder = 0;
+
+    l_j2k->m_specific_param.m_encoder.m_header_tile_data = (OPJ_BYTE *) opj_malloc(
+                OPJ_J2K_DEFAULT_HEADER_SIZE);
+    if (! l_j2k->m_specific_param.m_encoder.m_header_tile_data) {
+        opj_j2k_destroy(l_j2k);
+        return NULL;
+    }
+
+    l_j2k->m_specific_param.m_encoder.m_header_tile_data_size =
+        OPJ_J2K_DEFAULT_HEADER_SIZE;
+
+    /* validation list creation*/
+    l_j2k->m_validation_list = opj_procedure_list_create();
+    if (! l_j2k->m_validation_list) {
+        opj_j2k_destroy(l_j2k);
+        return NULL;
+    }
+
+    /* execution list creation*/
+    l_j2k->m_procedure_list = opj_procedure_list_create();
+    if (! l_j2k->m_procedure_list) {
+        opj_j2k_destroy(l_j2k);
+        return NULL;
+    }
+
+    l_j2k->m_tp = opj_thread_pool_create(opj_j2k_get_default_thread_count());
+    if (!l_j2k->m_tp) {
+        l_j2k->m_tp = opj_thread_pool_create(0);
+    }
+    if (!l_j2k->m_tp) {
+        opj_j2k_destroy(l_j2k);
+        return NULL;
+    }
+
+    return l_j2k;
+}
+
+static int opj_j2k_initialise_4K_poc(opj_poc_t *POC, int numres)
+{
+    POC[0].tile  = 1;
+    POC[0].resno0  = 0;
+    POC[0].compno0 = 0;
+    POC[0].layno1  = 1;
+    POC[0].resno1  = (OPJ_UINT32)(numres - 1);
+    POC[0].compno1 = 3;
+    POC[0].prg1 = OPJ_CPRL;
+    POC[1].tile  = 1;
+    POC[1].resno0  = (OPJ_UINT32)(numres - 1);
+    POC[1].compno0 = 0;
+    POC[1].layno1  = 1;
+    POC[1].resno1  = (OPJ_UINT32)numres;
+    POC[1].compno1 = 3;
+    POC[1].prg1 = OPJ_CPRL;
+    return 2;
+}
+
+static void opj_j2k_set_cinema_parameters(opj_cparameters_t *parameters,
+        opj_image_t *image, opj_event_mgr_t *p_manager)
+{
+    /* Configure cinema parameters */
+    int i;
+
+    /* No tiling */
+    parameters->tile_size_on = OPJ_FALSE;
+    parameters->cp_tdx = 1;
+    parameters->cp_tdy = 1;
+
+    /* One tile part for each component */
+    parameters->tp_flag = 'C';
+    parameters->tp_on = 1;
+
+    /* Tile and Image shall be at (0,0) */
+    parameters->cp_tx0 = 0;
+    parameters->cp_ty0 = 0;
+    parameters->image_offset_x0 = 0;
+    parameters->image_offset_y0 = 0;
+
+    /* Codeblock size= 32*32 */
+    parameters->cblockw_init = 32;
+    parameters->cblockh_init = 32;
+
+    /* Codeblock style: no mode switch enabled */
+    parameters->mode = 0;
+
+    /* No ROI */
+    parameters->roi_compno = -1;
+
+    /* No subsampling */
+    parameters->subsampling_dx = 1;
+    parameters->subsampling_dy = 1;
+
+    /* 9-7 transform */
+    parameters->irreversible = 1;
+
+    /* Number of layers */
+    if (parameters->tcp_numlayers > 1) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
+                      "1 single quality layer"
+                      "-> Number of layers forced to 1 (rather than %d)\n"
+                      "-> Rate of the last layer (%3.1f) will be used",
+                      parameters->tcp_numlayers,
+                      parameters->tcp_rates[parameters->tcp_numlayers - 1]);
+        parameters->tcp_rates[0] = parameters->tcp_rates[parameters->tcp_numlayers - 1];
+        parameters->tcp_numlayers = 1;
+    }
+
+    /* Resolution levels */
+    switch (parameters->rsiz) {
+    case OPJ_PROFILE_CINEMA_2K:
+        if (parameters->numresolution > 6) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "JPEG 2000 Profile-3 (2k dc profile) requires:\n"
+                          "Number of decomposition levels <= 5\n"
+                          "-> Number of decomposition levels forced to 5 (rather than %d)\n",
+                          parameters->numresolution + 1);
+            parameters->numresolution = 6;
+        }
+        break;
+    case OPJ_PROFILE_CINEMA_4K:
+        if (parameters->numresolution < 2) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "JPEG 2000 Profile-4 (4k dc profile) requires:\n"
+                          "Number of decomposition levels >= 1 && <= 6\n"
+                          "-> Number of decomposition levels forced to 1 (rather than %d)\n",
+                          parameters->numresolution + 1);
+            parameters->numresolution = 1;
+        } else if (parameters->numresolution > 7) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "JPEG 2000 Profile-4 (4k dc profile) requires:\n"
+                          "Number of decomposition levels >= 1 && <= 6\n"
+                          "-> Number of decomposition levels forced to 6 (rather than %d)\n",
+                          parameters->numresolution + 1);
+            parameters->numresolution = 7;
+        }
+        break;
+    default :
+        break;
+    }
+
+    /* Precincts */
+    parameters->csty |= J2K_CP_CSTY_PRT;
+    if (parameters->numresolution == 1) {
+        parameters->res_spec = 1;
+        parameters->prcw_init[0] = 128;
+        parameters->prch_init[0] = 128;
+    } else {
+        parameters->res_spec = parameters->numresolution - 1;
+        for (i = 0; i < parameters->res_spec; i++) {
+            parameters->prcw_init[i] = 256;
+            parameters->prch_init[i] = 256;
+        }
+    }
+
+    /* The progression order shall be CPRL */
+    parameters->prog_order = OPJ_CPRL;
+
+    /* Progression order changes for 4K, disallowed for 2K */
+    if (parameters->rsiz == OPJ_PROFILE_CINEMA_4K) {
+        parameters->numpocs = (OPJ_UINT32)opj_j2k_initialise_4K_poc(parameters->POC,
+                              parameters->numresolution);
+    } else {
+        parameters->numpocs = 0;
+    }
+
+    /* Limited bit-rate */
+    parameters->cp_disto_alloc = 1;
+    if (parameters->max_cs_size <= 0) {
+        /* No rate has been introduced, 24 fps is assumed */
+        parameters->max_cs_size = OPJ_CINEMA_24_CS;
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
+                      "Maximum 1302083 compressed bytes @ 24fps\n"
+                      "As no rate has been given, this limit will be used.\n");
+    } else if (parameters->max_cs_size > OPJ_CINEMA_24_CS) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
+                      "Maximum 1302083 compressed bytes @ 24fps\n"
+                      "-> Specified rate exceeds this limit. Rate will be forced to 1302083 bytes.\n");
+        parameters->max_cs_size = OPJ_CINEMA_24_CS;
+    }
+
+    if (parameters->max_comp_size <= 0) {
+        /* No rate has been introduced, 24 fps is assumed */
+        parameters->max_comp_size = OPJ_CINEMA_24_COMP;
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
+                      "Maximum 1041666 compressed bytes @ 24fps\n"
+                      "As no rate has been given, this limit will be used.\n");
+    } else if (parameters->max_comp_size > OPJ_CINEMA_24_COMP) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
+                      "Maximum 1041666 compressed bytes @ 24fps\n"
+                      "-> Specified rate exceeds this limit. Rate will be forced to 1041666 bytes.\n");
+        parameters->max_comp_size = OPJ_CINEMA_24_COMP;
+    }
+
+    parameters->tcp_rates[0] = (OPJ_FLOAT32)(image->numcomps * image->comps[0].w *
+                               image->comps[0].h * image->comps[0].prec) /
+                               (OPJ_FLOAT32)(((OPJ_UINT32)parameters->max_cs_size) * 8 * image->comps[0].dx *
+                                       image->comps[0].dy);
+
+}
+
+static OPJ_BOOL opj_j2k_is_cinema_compliant(opj_image_t *image, OPJ_UINT16 rsiz,
+        opj_event_mgr_t *p_manager)
+{
+    OPJ_UINT32 i;
+
+    /* Number of components */
+    if (image->numcomps != 3) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "JPEG 2000 Profile-3 (2k dc profile) requires:\n"
+                      "3 components"
+                      "-> Number of components of input image (%d) is not compliant\n"
+                      "-> Non-profile-3 codestream will be generated\n",
+                      image->numcomps);
+        return OPJ_FALSE;
+    }
+
+    /* Bitdepth */
+    for (i = 0; i < image->numcomps; i++) {
+        if ((image->comps[i].prec != 12) | (image->comps[i].sgnd)) {
+            char signed_str[] = "signed";
+            char unsigned_str[] = "unsigned";
+            char *tmp_str = image->comps[i].sgnd ? signed_str : unsigned_str;
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "JPEG 2000 Profile-3 (2k dc profile) requires:\n"
+                          "Precision of each component shall be 12 bits unsigned"
+                          "-> At least component %d of input image (%d bits, %s) is not compliant\n"
+                          "-> Non-profile-3 codestream will be generated\n",
+                          i, image->comps[i].prec, tmp_str);
+            return OPJ_FALSE;
+        }
+    }
+
+    /* Image size */
+    switch (rsiz) {
+    case OPJ_PROFILE_CINEMA_2K:
+        if (((image->comps[0].w > 2048) | (image->comps[0].h > 1080))) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "JPEG 2000 Profile-3 (2k dc profile) requires:\n"
+                          "width <= 2048 and height <= 1080\n"
+                          "-> Input image size %d x %d is not compliant\n"
+                          "-> Non-profile-3 codestream will be generated\n",
+                          image->comps[0].w, image->comps[0].h);
+            return OPJ_FALSE;
+        }
+        break;
+    case OPJ_PROFILE_CINEMA_4K:
+        if (((image->comps[0].w > 4096) | (image->comps[0].h > 2160))) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "JPEG 2000 Profile-4 (4k dc profile) requires:\n"
+                          "width <= 4096 and height <= 2160\n"
+                          "-> Image size %d x %d is not compliant\n"
+                          "-> Non-profile-4 codestream will be generated\n",
+                          image->comps[0].w, image->comps[0].h);
+            return OPJ_FALSE;
+        }
+        break;
+    default :
+        break;
+    }
+
+    return OPJ_TRUE;
+}
+
+static int opj_j2k_get_imf_max_NL(opj_cparameters_t *parameters,
+                                  opj_image_t *image)
+{
+    /* Decomposition levels */
+    const OPJ_UINT16 rsiz = parameters->rsiz;
+    const OPJ_UINT16 profile = OPJ_GET_IMF_PROFILE(rsiz);
+    const OPJ_UINT32 XTsiz = parameters->tile_size_on ? (OPJ_UINT32)
+                             parameters->cp_tdx : image->x1;
+    switch (profile) {
+    case OPJ_PROFILE_IMF_2K:
+        return 5;
+    case OPJ_PROFILE_IMF_4K:
+        return 6;
+    case OPJ_PROFILE_IMF_8K:
+        return 7;
+    case OPJ_PROFILE_IMF_2K_R: {
+        if (XTsiz >= 2048) {
+            return 5;
+        } else if (XTsiz >= 1024) {
+            return 4;
+        }
+        break;
+    }
+    case OPJ_PROFILE_IMF_4K_R: {
+        if (XTsiz >= 4096) {
+            return 6;
+        } else if (XTsiz >= 2048) {
+            return 5;
+        } else if (XTsiz >= 1024) {
+            return 4;
+        }
+        break;
+    }
+    case OPJ_PROFILE_IMF_8K_R: {
+        if (XTsiz >= 8192) {
+            return 7;
+        } else if (XTsiz >= 4096) {
+            return 6;
+        } else if (XTsiz >= 2048) {
+            return 5;
+        } else if (XTsiz >= 1024) {
+            return 4;
+        }
+        break;
+    }
+    default:
+        break;
+    }
+    return -1;
+}
+
+static void opj_j2k_set_imf_parameters(opj_cparameters_t *parameters,
+                                       opj_image_t *image, opj_event_mgr_t *p_manager)
+{
+    const OPJ_UINT16 rsiz = parameters->rsiz;
+    const OPJ_UINT16 profile = OPJ_GET_IMF_PROFILE(rsiz);
+
+    OPJ_UNUSED(p_manager);
+
+    /* Override defaults set by opj_set_default_encoder_parameters */
+    if (parameters->cblockw_init == OPJ_COMP_PARAM_DEFAULT_CBLOCKW &&
+            parameters->cblockh_init == OPJ_COMP_PARAM_DEFAULT_CBLOCKH) {
+        parameters->cblockw_init = 32;
+        parameters->cblockh_init = 32;
+    }
+
+    /* One tile part for each component */
+    parameters->tp_flag = 'C';
+    parameters->tp_on = 1;
+
+    if (parameters->prog_order == OPJ_COMP_PARAM_DEFAULT_PROG_ORDER) {
+        parameters->prog_order = OPJ_CPRL;
+    }
+
+    if (profile == OPJ_PROFILE_IMF_2K ||
+            profile == OPJ_PROFILE_IMF_4K ||
+            profile == OPJ_PROFILE_IMF_8K) {
+        /* 9-7 transform */
+        parameters->irreversible = 1;
+    }
+
+    /* Adjust the number of resolutions if set to its defaults */
+    if (parameters->numresolution == OPJ_COMP_PARAM_DEFAULT_NUMRESOLUTION &&
+            image->x0 == 0 &&
+            image->y0 == 0) {
+        const int max_NL = opj_j2k_get_imf_max_NL(parameters, image);
+        if (max_NL >= 0 && parameters->numresolution > max_NL) {
+            parameters->numresolution = max_NL + 1;
+        }
+
+        /* Note: below is generic logic */
+        if (!parameters->tile_size_on) {
+            while (parameters->numresolution > 0) {
+                if (image->x1 < (1U << ((OPJ_UINT32)parameters->numresolution - 1U))) {
+                    parameters->numresolution --;
+                    continue;
+                }
+                if (image->y1 < (1U << ((OPJ_UINT32)parameters->numresolution - 1U))) {
+                    parameters->numresolution --;
+                    continue;
+                }
+                break;
+            }
+        }
+    }
+
+    /* Set defaults precincts */
+    if (parameters->csty == 0) {
+        parameters->csty |= J2K_CP_CSTY_PRT;
+        if (parameters->numresolution == 1) {
+            parameters->res_spec = 1;
+            parameters->prcw_init[0] = 128;
+            parameters->prch_init[0] = 128;
+        } else {
+            int i;
+            parameters->res_spec = parameters->numresolution - 1;
+            for (i = 0; i < parameters->res_spec; i++) {
+                parameters->prcw_init[i] = 256;
+                parameters->prch_init[i] = 256;
+            }
+        }
+    }
+}
+
+/* Table A.53 from JPEG2000 standard */
+static const OPJ_UINT16 tabMaxSubLevelFromMainLevel[] = {
+    15, /* unspecified */
+    1,
+    1,
+    1,
+    2,
+    3,
+    4,
+    5,
+    6,
+    7,
+    8,
+    9
+};
+
+static OPJ_BOOL opj_j2k_is_imf_compliant(opj_cparameters_t *parameters,
+        opj_image_t *image,
+        opj_event_mgr_t *p_manager)
+{
+    OPJ_UINT32 i;
+    const OPJ_UINT16 rsiz = parameters->rsiz;
+    const OPJ_UINT16 profile = OPJ_GET_IMF_PROFILE(rsiz);
+    const OPJ_UINT16 mainlevel = OPJ_GET_IMF_MAINLEVEL(rsiz);
+    const OPJ_UINT16 sublevel = OPJ_GET_IMF_SUBLEVEL(rsiz);
+    const int NL = parameters->numresolution - 1;
+    const OPJ_UINT32 XTsiz = parameters->tile_size_on ? (OPJ_UINT32)
+                             parameters->cp_tdx : image->x1;
+    OPJ_BOOL ret = OPJ_TRUE;
+
+    /* Validate mainlevel */
+    if (mainlevel > OPJ_IMF_MAINLEVEL_MAX) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "IMF profile require mainlevel <= 11.\n"
+                      "-> %d is thus not compliant\n"
+                      "-> Non-IMF codestream will be generated\n",
+                      mainlevel);
+        ret = OPJ_FALSE;
+    } else {
+        /* Validate sublevel */
+        assert(sizeof(tabMaxSubLevelFromMainLevel) ==
+               (OPJ_IMF_MAINLEVEL_MAX + 1) * sizeof(tabMaxSubLevelFromMainLevel[0]));
+        if (sublevel > tabMaxSubLevelFromMainLevel[mainlevel]) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "IMF profile require sublevel <= %d for mainlevel = %d.\n"
+                          "-> %d is thus not compliant\n"
+                          "-> Non-IMF codestream will be generated\n",
+                          tabMaxSubLevelFromMainLevel[mainlevel],
+                          mainlevel,
+                          sublevel);
+            ret = OPJ_FALSE;
+        }
+    }
+
+    /* Number of components */
+    if (image->numcomps > 3) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "IMF profiles require at most 3 components.\n"
+                      "-> Number of components of input image (%d) is not compliant\n"
+                      "-> Non-IMF codestream will be generated\n",
+                      image->numcomps);
+        ret = OPJ_FALSE;
+    }
+
+    if (image->x0 != 0 || image->y0 != 0) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "IMF profiles require image origin to be at 0,0.\n"
+                      "-> %d,%d is not compliant\n"
+                      "-> Non-IMF codestream will be generated\n",
+                      image->x0, image->y0 != 0);
+        ret = OPJ_FALSE;
+    }
+
+    if (parameters->cp_tx0 != 0 || parameters->cp_ty0 != 0) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "IMF profiles require tile origin to be at 0,0.\n"
+                      "-> %d,%d is not compliant\n"
+                      "-> Non-IMF codestream will be generated\n",
+                      parameters->cp_tx0, parameters->cp_ty0);
+        ret = OPJ_FALSE;
+    }
+
+    if (parameters->tile_size_on) {
+        if (profile == OPJ_PROFILE_IMF_2K ||
+                profile == OPJ_PROFILE_IMF_4K ||
+                profile == OPJ_PROFILE_IMF_8K) {
+            if ((OPJ_UINT32)parameters->cp_tdx < image->x1 ||
+                    (OPJ_UINT32)parameters->cp_tdy < image->y1) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "IMF 2K/4K/8K single tile profiles require tile to be greater or equal to image size.\n"
+                              "-> %d,%d is lesser than %d,%d\n"
+                              "-> Non-IMF codestream will be generated\n",
+                              parameters->cp_tdx,
+                              parameters->cp_tdy,
+                              image->x1,
+                              image->y1);
+                ret = OPJ_FALSE;
+            }
+        } else {
+            if ((OPJ_UINT32)parameters->cp_tdx >= image->x1 &&
+                    (OPJ_UINT32)parameters->cp_tdy >= image->y1) {
+                /* ok */
+            } else if (parameters->cp_tdx == 1024 &&
+                       parameters->cp_tdy == 1024) {
+                /* ok */
+            } else if (parameters->cp_tdx == 2048 &&
+                       parameters->cp_tdy == 2048 &&
+                       (profile == OPJ_PROFILE_IMF_4K ||
+                        profile == OPJ_PROFILE_IMF_8K)) {
+                /* ok */
+            } else if (parameters->cp_tdx == 4096 &&
+                       parameters->cp_tdy == 4096 &&
+                       profile == OPJ_PROFILE_IMF_8K) {
+                /* ok */
+            } else {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "IMF 2K_R/4K_R/8K_R single/multiple tile profiles "
+                              "require tile to be greater or equal to image size,\n"
+                              "or to be (1024,1024), or (2048,2048) for 4K_R/8K_R "
+                              "or (4096,4096) for 8K_R.\n"
+                              "-> %d,%d is non conformant\n"
+                              "-> Non-IMF codestream will be generated\n",
+                              parameters->cp_tdx,
+                              parameters->cp_tdy);
+                ret = OPJ_FALSE;
+            }
+        }
+    }
+
+    /* Bitdepth */
+    for (i = 0; i < image->numcomps; i++) {
+        if (!(image->comps[i].prec >= 8 && image->comps[i].prec <= 16) ||
+                (image->comps[i].sgnd)) {
+            char signed_str[] = "signed";
+            char unsigned_str[] = "unsigned";
+            char *tmp_str = image->comps[i].sgnd ? signed_str : unsigned_str;
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "IMF profiles require precision of each component to b in [8-16] bits unsigned"
+                          "-> At least component %d of input image (%d bits, %s) is not compliant\n"
+                          "-> Non-IMF codestream will be generated\n",
+                          i, image->comps[i].prec, tmp_str);
+            ret = OPJ_FALSE;
+        }
+    }
+
+    /* Sub-sampling */
+    for (i = 0; i < image->numcomps; i++) {
+        if (i == 0 && image->comps[i].dx != 1) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "IMF profiles require XRSiz1 == 1. Here it is set to %d.\n"
+                          "-> Non-IMF codestream will be generated\n",
+                          image->comps[i].dx);
+            ret = OPJ_FALSE;
+        }
+        if (i == 1 && image->comps[i].dx != 1 && image->comps[i].dx != 2) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "IMF profiles require XRSiz2 == 1 or 2. Here it is set to %d.\n"
+                          "-> Non-IMF codestream will be generated\n",
+                          image->comps[i].dx);
+            ret = OPJ_FALSE;
+        }
+        if (i > 1 && image->comps[i].dx != image->comps[i - 1].dx) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "IMF profiles require XRSiz%d to be the same as XRSiz2. "
+                          "Here it is set to %d instead of %d.\n"
+                          "-> Non-IMF codestream will be generated\n",
+                          i + 1, image->comps[i].dx, image->comps[i - 1].dx);
+            ret = OPJ_FALSE;
+        }
+        if (image->comps[i].dy != 1) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "IMF profiles require YRsiz == 1. "
+                          "Here it is set to %d for component %d.\n"
+                          "-> Non-IMF codestream will be generated\n",
+                          image->comps[i].dy, i);
+            ret = OPJ_FALSE;
+        }
+    }
+
+    /* Image size */
+    switch (profile) {
+    case OPJ_PROFILE_IMF_2K:
+    case OPJ_PROFILE_IMF_2K_R:
+        if (((image->comps[0].w > 2048) | (image->comps[0].h > 1556))) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "IMF 2K/2K_R profile require:\n"
+                          "width <= 2048 and height <= 1556\n"
+                          "-> Input image size %d x %d is not compliant\n"
+                          "-> Non-IMF codestream will be generated\n",
+                          image->comps[0].w, image->comps[0].h);
+            ret = OPJ_FALSE;
+        }
+        break;
+    case OPJ_PROFILE_IMF_4K:
+    case OPJ_PROFILE_IMF_4K_R:
+        if (((image->comps[0].w > 4096) | (image->comps[0].h > 3112))) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "IMF 4K/4K_R profile require:\n"
+                          "width <= 4096 and height <= 3112\n"
+                          "-> Input image size %d x %d is not compliant\n"
+                          "-> Non-IMF codestream will be generated\n",
+                          image->comps[0].w, image->comps[0].h);
+            ret = OPJ_FALSE;
+        }
+        break;
+    case OPJ_PROFILE_IMF_8K:
+    case OPJ_PROFILE_IMF_8K_R:
+        if (((image->comps[0].w > 8192) | (image->comps[0].h > 6224))) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "IMF 8K/8K_R profile require:\n"
+                          "width <= 8192 and height <= 6224\n"
+                          "-> Input image size %d x %d is not compliant\n"
+                          "-> Non-IMF codestream will be generated\n",
+                          image->comps[0].w, image->comps[0].h);
+            ret = OPJ_FALSE;
+        }
+        break;
+    default :
+        assert(0);
+        return OPJ_FALSE;
+    }
+
+    if (parameters->roi_compno != -1) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "IMF profile forbid RGN / region of interest marker.\n"
+                      "-> Compression parameters specify a ROI\n"
+                      "-> Non-IMF codestream will be generated\n");
+        ret = OPJ_FALSE;
+    }
+
+    if (parameters->cblockw_init != 32 || parameters->cblockh_init != 32) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "IMF profile require code block size to be 32x32.\n"
+                      "-> Compression parameters set it to %dx%d.\n"
+                      "-> Non-IMF codestream will be generated\n",
+                      parameters->cblockw_init,
+                      parameters->cblockh_init);
+        ret = OPJ_FALSE;
+    }
+
+    if (parameters->prog_order != OPJ_CPRL) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "IMF profile require progression order to be CPRL.\n"
+                      "-> Compression parameters set it to %d.\n"
+                      "-> Non-IMF codestream will be generated\n",
+                      parameters->prog_order);
+        ret = OPJ_FALSE;
+    }
+
+    if (parameters->numpocs != 0) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "IMF profile forbid POC markers.\n"
+                      "-> Compression parameters set %d POC.\n"
+                      "-> Non-IMF codestream will be generated\n",
+                      parameters->numpocs);
+        ret = OPJ_FALSE;
+    }
+
+    /* Codeblock style: no mode switch enabled */
+    if (parameters->mode != 0) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "IMF profile forbid mode switch in code block style.\n"
+                      "-> Compression parameters set code block style to %d.\n"
+                      "-> Non-IMF codestream will be generated\n",
+                      parameters->mode);
+        ret = OPJ_FALSE;
+    }
+
+    if (profile == OPJ_PROFILE_IMF_2K ||
+            profile == OPJ_PROFILE_IMF_4K ||
+            profile == OPJ_PROFILE_IMF_8K) {
+        /* Expect 9-7 transform */
+        if (parameters->irreversible != 1) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "IMF 2K/4K/8K profiles require 9-7 Irreversible Transform.\n"
+                          "-> Compression parameters set it to reversible.\n"
+                          "-> Non-IMF codestream will be generated\n");
+            ret = OPJ_FALSE;
+        }
+    } else {
+        /* Expect 5-3 transform */
+        if (parameters->irreversible != 0) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "IMF 2K/4K/8K profiles require 5-3 reversible Transform.\n"
+                          "-> Compression parameters set it to irreversible.\n"
+                          "-> Non-IMF codestream will be generated\n");
+            ret = OPJ_FALSE;
+        }
+    }
+
+    /* Number of layers */
+    if (parameters->tcp_numlayers != 1) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "IMF 2K/4K/8K profiles require 1 single quality layer.\n"
+                      "-> Number of layers is %d.\n"
+                      "-> Non-IMF codestream will be generated\n",
+                      parameters->tcp_numlayers);
+        ret = OPJ_FALSE;
+    }
+
+    /* Decomposition levels */
+    switch (profile) {
+    case OPJ_PROFILE_IMF_2K:
+        if (!(NL >= 1 && NL <= 5)) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "IMF 2K profile requires 1 <= NL <= 5:\n"
+                          "-> Number of decomposition levels is %d.\n"
+                          "-> Non-IMF codestream will be generated\n",
+                          NL);
+            ret = OPJ_FALSE;
+        }
+        break;
+    case OPJ_PROFILE_IMF_4K:
+        if (!(NL >= 1 && NL <= 6)) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "IMF 4K profile requires 1 <= NL <= 6:\n"
+                          "-> Number of decomposition levels is %d.\n"
+                          "-> Non-IMF codestream will be generated\n",
+                          NL);
+            ret = OPJ_FALSE;
+        }
+        break;
+    case OPJ_PROFILE_IMF_8K:
+        if (!(NL >= 1 && NL <= 7)) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "IMF 8K profile requires 1 <= NL <= 7:\n"
+                          "-> Number of decomposition levels is %d.\n"
+                          "-> Non-IMF codestream will be generated\n",
+                          NL);
+            ret = OPJ_FALSE;
+        }
+        break;
+    case OPJ_PROFILE_IMF_2K_R: {
+        if (XTsiz >= 2048) {
+            if (!(NL >= 1 && NL <= 5)) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "IMF 2K_R profile requires 1 <= NL <= 5 for XTsiz >= 2048:\n"
+                              "-> Number of decomposition levels is %d.\n"
+                              "-> Non-IMF codestream will be generated\n",
+                              NL);
+                ret = OPJ_FALSE;
+            }
+        } else if (XTsiz >= 1024) {
+            if (!(NL >= 1 && NL <= 4)) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "IMF 2K_R profile requires 1 <= NL <= 4 for XTsiz in [1024,2048[:\n"
+                              "-> Number of decomposition levels is %d.\n"
+                              "-> Non-IMF codestream will be generated\n",
+                              NL);
+                ret = OPJ_FALSE;
+            }
+        }
+        break;
+    }
+    case OPJ_PROFILE_IMF_4K_R: {
+        if (XTsiz >= 4096) {
+            if (!(NL >= 1 && NL <= 6)) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "IMF 4K_R profile requires 1 <= NL <= 6 for XTsiz >= 4096:\n"
+                              "-> Number of decomposition levels is %d.\n"
+                              "-> Non-IMF codestream will be generated\n",
+                              NL);
+                ret = OPJ_FALSE;
+            }
+        } else if (XTsiz >= 2048) {
+            if (!(NL >= 1 && NL <= 5)) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "IMF 4K_R profile requires 1 <= NL <= 5 for XTsiz in [2048,4096[:\n"
+                              "-> Number of decomposition levels is %d.\n"
+                              "-> Non-IMF codestream will be generated\n",
+                              NL);
+                ret = OPJ_FALSE;
+            }
+        } else if (XTsiz >= 1024) {
+            if (!(NL >= 1 && NL <= 4)) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "IMF 4K_R profile requires 1 <= NL <= 4 for XTsiz in [1024,2048[:\n"
+                              "-> Number of decomposition levels is %d.\n"
+                              "-> Non-IMF codestream will be generated\n",
+                              NL);
+                ret = OPJ_FALSE;
+            }
+        }
+        break;
+    }
+    case OPJ_PROFILE_IMF_8K_R: {
+        if (XTsiz >= 8192) {
+            if (!(NL >= 1 && NL <= 7)) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "IMF 4K_R profile requires 1 <= NL <= 7 for XTsiz >= 8192:\n"
+                              "-> Number of decomposition levels is %d.\n"
+                              "-> Non-IMF codestream will be generated\n",
+                              NL);
+                ret = OPJ_FALSE;
+            }
+        } else if (XTsiz >= 4096) {
+            if (!(NL >= 1 && NL <= 6)) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "IMF 4K_R profile requires 1 <= NL <= 6 for XTsiz in [4096,8192[:\n"
+                              "-> Number of decomposition levels is %d.\n"
+                              "-> Non-IMF codestream will be generated\n",
+                              NL);
+                ret = OPJ_FALSE;
+            }
+        } else if (XTsiz >= 2048) {
+            if (!(NL >= 1 && NL <= 5)) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "IMF 4K_R profile requires 1 <= NL <= 5 for XTsiz in [2048,4096[:\n"
+                              "-> Number of decomposition levels is %d.\n"
+                              "-> Non-IMF codestream will be generated\n",
+                              NL);
+                ret = OPJ_FALSE;
+            }
+        } else if (XTsiz >= 1024) {
+            if (!(NL >= 1 && NL <= 4)) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "IMF 4K_R profile requires 1 <= NL <= 4 for XTsiz in [1024,2048[:\n"
+                              "-> Number of decomposition levels is %d.\n"
+                              "-> Non-IMF codestream will be generated\n",
+                              NL);
+                ret = OPJ_FALSE;
+            }
+        }
+        break;
+    }
+    default:
+        break;
+    }
+
+    if (parameters->numresolution == 1) {
+        if (parameters->res_spec != 1 ||
+                parameters->prcw_init[0] != 128 ||
+                parameters->prch_init[0] != 128) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "IMF profiles require PPx = PPy = 7 for NLLL band, else 8.\n"
+                          "-> Supplied values are different from that.\n"
+                          "-> Non-IMF codestream will be generated\n");
+            ret = OPJ_FALSE;
+        }
+    } else {
+        int i;
+        for (i = 0; i < parameters->res_spec; i++) {
+            if (parameters->prcw_init[i] != 256 ||
+                    parameters->prch_init[i] != 256) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "IMF profiles require PPx = PPy = 7 for NLLL band, else 8.\n"
+                              "-> Supplied values are different from that.\n"
+                              "-> Non-IMF codestream will be generated\n");
+                ret = OPJ_FALSE;
+            }
+        }
+    }
+
+    return ret;
+}
+
+
+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;
+
+    if (!p_j2k || !parameters || ! image) {
+        return OPJ_FALSE;
+    }
+
+    if ((parameters->numresolution <= 0) ||
+            (parameters->numresolution > OPJ_J2K_MAXRLVLS)) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Invalid number of resolutions : %d not in range [1,%d]\n",
+                      parameters->numresolution, OPJ_J2K_MAXRLVLS);
+        return OPJ_FALSE;
+    }
+
+    if (parameters->cblockw_init < 4 || parameters->cblockw_init > 1024) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Invalid value for cblockw_init: %d not a power of 2 in range [4,1024]\n",
+                      parameters->cblockw_init);
+        return OPJ_FALSE;
+    }
+    if (parameters->cblockh_init < 4 || parameters->cblockh_init > 1024) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Invalid value for cblockh_init: %d not a power of 2 not in range [4,1024]\n",
+                      parameters->cblockh_init);
+        return OPJ_FALSE;
+    }
+    if (parameters->cblockw_init * parameters->cblockh_init > 4096) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Invalid value for cblockw_init * cblockh_init: should be <= 4096\n");
+        return OPJ_FALSE;
+    }
+    cblkw = (OPJ_UINT32)opj_int_floorlog2(parameters->cblockw_init);
+    cblkh = (OPJ_UINT32)opj_int_floorlog2(parameters->cblockh_init);
+    if (parameters->cblockw_init != (1 << cblkw)) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Invalid value for cblockw_init: %d not a power of 2 in range [4,1024]\n",
+                      parameters->cblockw_init);
+        return OPJ_FALSE;
+    }
+    if (parameters->cblockh_init != (1 << cblkh)) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Invalid value for cblockw_init: %d not a power of 2 in range [4,1024]\n",
+                      parameters->cblockh_init);
+        return OPJ_FALSE;
+    }
+
+    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 = &(j2k->m_cp);
+
+    /* set default values for cp */
+    cp->tw = 1;
+    cp->th = 1;
+
+    /* FIXME ADE: to be removed once deprecated cp_cinema and cp_rsiz have been removed */
+    if (parameters->rsiz ==
+            OPJ_PROFILE_NONE) { /* consider deprecated fields only if RSIZ has not been set */
+        OPJ_BOOL deprecated_used = OPJ_FALSE;
+        switch (parameters->cp_cinema) {
+        case OPJ_CINEMA2K_24:
+            parameters->rsiz = OPJ_PROFILE_CINEMA_2K;
+            parameters->max_cs_size = OPJ_CINEMA_24_CS;
+            parameters->max_comp_size = OPJ_CINEMA_24_COMP;
+            deprecated_used = OPJ_TRUE;
+            break;
+        case OPJ_CINEMA2K_48:
+            parameters->rsiz = OPJ_PROFILE_CINEMA_2K;
+            parameters->max_cs_size = OPJ_CINEMA_48_CS;
+            parameters->max_comp_size = OPJ_CINEMA_48_COMP;
+            deprecated_used = OPJ_TRUE;
+            break;
+        case OPJ_CINEMA4K_24:
+            parameters->rsiz = OPJ_PROFILE_CINEMA_4K;
+            parameters->max_cs_size = OPJ_CINEMA_24_CS;
+            parameters->max_comp_size = OPJ_CINEMA_24_COMP;
+            deprecated_used = OPJ_TRUE;
+            break;
+        case OPJ_OFF:
+        default:
+            break;
+        }
+        switch (parameters->cp_rsiz) {
+        case OPJ_CINEMA2K:
+            parameters->rsiz = OPJ_PROFILE_CINEMA_2K;
+            deprecated_used = OPJ_TRUE;
+            break;
+        case OPJ_CINEMA4K:
+            parameters->rsiz = OPJ_PROFILE_CINEMA_4K;
+            deprecated_used = OPJ_TRUE;
+            break;
+        case OPJ_MCT:
+            parameters->rsiz = OPJ_PROFILE_PART2 | OPJ_EXTENSION_MCT;
+            deprecated_used = OPJ_TRUE;
+        case OPJ_STD_RSIZ:
+        default:
+            break;
+        }
+        if (deprecated_used) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "Deprecated fields cp_cinema or cp_rsiz are used\n"
+                          "Please consider using only the rsiz field\n"
+                          "See openjpeg.h documentation for more details\n");
+        }
+    }
+
+    /* If no explicit layers are provided, use lossless settings */
+    if (parameters->tcp_numlayers == 0) {
+        parameters->tcp_numlayers = 1;
+        parameters->cp_disto_alloc = 1;
+        parameters->tcp_rates[0] = 0;
+    }
+
+    if (parameters->cp_disto_alloc) {
+        /* Emit warnings if tcp_rates are not decreasing */
+        for (i = 1; i < (OPJ_UINT32) parameters->tcp_numlayers; i++) {
+            OPJ_FLOAT32 rate_i_corr = parameters->tcp_rates[i];
+            OPJ_FLOAT32 rate_i_m_1_corr = parameters->tcp_rates[i - 1];
+            if (rate_i_corr <= 1.0) {
+                rate_i_corr = 1.0;
+            }
+            if (rate_i_m_1_corr <= 1.0) {
+                rate_i_m_1_corr = 1.0;
+            }
+            if (rate_i_corr >= rate_i_m_1_corr) {
+                if (rate_i_corr != parameters->tcp_rates[i] &&
+                        rate_i_m_1_corr != parameters->tcp_rates[i - 1]) {
+                    opj_event_msg(p_manager, EVT_WARNING,
+                                  "tcp_rates[%d]=%f (corrected as %f) should be strictly lesser "
+                                  "than tcp_rates[%d]=%f (corrected as %f)\n",
+                                  i, parameters->tcp_rates[i], rate_i_corr,
+                                  i - 1, parameters->tcp_rates[i - 1], rate_i_m_1_corr);
+                } else if (rate_i_corr != parameters->tcp_rates[i]) {
+                    opj_event_msg(p_manager, EVT_WARNING,
+                                  "tcp_rates[%d]=%f (corrected as %f) should be strictly lesser "
+                                  "than tcp_rates[%d]=%f\n",
+                                  i, parameters->tcp_rates[i], rate_i_corr,
+                                  i - 1, parameters->tcp_rates[i - 1]);
+                } else if (rate_i_m_1_corr != parameters->tcp_rates[i - 1]) {
+                    opj_event_msg(p_manager, EVT_WARNING,
+                                  "tcp_rates[%d]=%f should be strictly lesser "
+                                  "than tcp_rates[%d]=%f (corrected as %f)\n",
+                                  i, parameters->tcp_rates[i],
+                                  i - 1, parameters->tcp_rates[i - 1], rate_i_m_1_corr);
+                } else {
+                    opj_event_msg(p_manager, EVT_WARNING,
+                                  "tcp_rates[%d]=%f should be strictly lesser "
+                                  "than tcp_rates[%d]=%f\n",
+                                  i, parameters->tcp_rates[i],
+                                  i - 1, parameters->tcp_rates[i - 1]);
+                }
+            }
+        }
+    } else if (parameters->cp_fixed_quality) {
+        /* Emit warnings if tcp_distoratio are not increasing */
+        for (i = 1; i < (OPJ_UINT32) parameters->tcp_numlayers; i++) {
+            if (parameters->tcp_distoratio[i] < parameters->tcp_distoratio[i - 1] &&
+                    !(i == (OPJ_UINT32)parameters->tcp_numlayers - 1 &&
+                      parameters->tcp_distoratio[i] == 0)) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "tcp_distoratio[%d]=%f should be strictly greater "
+                              "than tcp_distoratio[%d]=%f\n",
+                              i, parameters->tcp_distoratio[i], i - 1,
+                              parameters->tcp_distoratio[i - 1]);
+            }
+        }
+    }
+
+    /* see if max_codestream_size does limit input rate */
+    if (parameters->max_cs_size <= 0) {
+        if (parameters->tcp_rates[parameters->tcp_numlayers - 1] > 0) {
+            OPJ_FLOAT32 temp_size;
+            temp_size = (OPJ_FLOAT32)(((double)image->numcomps * image->comps[0].w *
+                                       image->comps[0].h * image->comps[0].prec) /
+                                      ((double)parameters->tcp_rates[parameters->tcp_numlayers - 1] * 8 *
+                                       image->comps[0].dx * image->comps[0].dy));
+            if (temp_size > INT_MAX) {
+                parameters->max_cs_size = INT_MAX;
+            } else {
+                parameters->max_cs_size = (int) floor(temp_size);
+            }
+        } else {
+            parameters->max_cs_size = 0;
+        }
+    } else {
+        OPJ_FLOAT32 temp_rate;
+        OPJ_BOOL cap = OPJ_FALSE;
+
+        if (OPJ_IS_IMF(parameters->rsiz) && parameters->max_cs_size > 0 &&
+                parameters->tcp_numlayers == 1 && parameters->tcp_rates[0] == 0) {
+            parameters->tcp_rates[0] = (OPJ_FLOAT32)(image->numcomps * image->comps[0].w *
+                                       image->comps[0].h * image->comps[0].prec) /
+                                       (OPJ_FLOAT32)(((OPJ_UINT32)parameters->max_cs_size) * 8 * image->comps[0].dx *
+                                               image->comps[0].dy);
+        }
+
+        temp_rate = (OPJ_FLOAT32)(((double)image->numcomps * image->comps[0].w *
+                                   image->comps[0].h * image->comps[0].prec) /
+                                  (((double)parameters->max_cs_size) * 8 * image->comps[0].dx *
+                                   image->comps[0].dy));
+        for (i = 0; i < (OPJ_UINT32) parameters->tcp_numlayers; i++) {
+            if (parameters->tcp_rates[i] < temp_rate) {
+                parameters->tcp_rates[i] = temp_rate;
+                cap = OPJ_TRUE;
+            }
+        }
+        if (cap) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "The desired maximum codestream size has limited\n"
+                          "at least one of the desired quality layers\n");
+        }
+    }
+
+    if (OPJ_IS_CINEMA(parameters->rsiz) || OPJ_IS_IMF(parameters->rsiz)) {
+        j2k->m_specific_param.m_encoder.m_TLM = OPJ_TRUE;
+    }
+
+    /* Manage profiles and applications and set RSIZ */
+    /* set cinema parameters if required */
+    if (OPJ_IS_CINEMA(parameters->rsiz)) {
+        if ((parameters->rsiz == OPJ_PROFILE_CINEMA_S2K)
+                || (parameters->rsiz == OPJ_PROFILE_CINEMA_S4K)) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "JPEG 2000 Scalable Digital Cinema profiles not yet supported\n");
+            parameters->rsiz = OPJ_PROFILE_NONE;
+        } else {
+            opj_j2k_set_cinema_parameters(parameters, image, p_manager);
+            if (!opj_j2k_is_cinema_compliant(image, parameters->rsiz, p_manager)) {
+                parameters->rsiz = OPJ_PROFILE_NONE;
+            }
+        }
+    } else if (OPJ_IS_STORAGE(parameters->rsiz)) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "JPEG 2000 Long Term Storage profile not yet supported\n");
+        parameters->rsiz = OPJ_PROFILE_NONE;
+    } else if (OPJ_IS_BROADCAST(parameters->rsiz)) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "JPEG 2000 Broadcast profiles not yet supported\n");
+        parameters->rsiz = OPJ_PROFILE_NONE;
+    } else if (OPJ_IS_IMF(parameters->rsiz)) {
+        opj_j2k_set_imf_parameters(parameters, image, p_manager);
+        if (!opj_j2k_is_imf_compliant(parameters, image, p_manager)) {
+            parameters->rsiz = OPJ_PROFILE_NONE;
+        }
+    } else if (OPJ_IS_PART2(parameters->rsiz)) {
+        if (parameters->rsiz == ((OPJ_PROFILE_PART2) | (OPJ_EXTENSION_NONE))) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "JPEG 2000 Part-2 profile defined\n"
+                          "but no Part-2 extension enabled.\n"
+                          "Profile set to NONE.\n");
+            parameters->rsiz = OPJ_PROFILE_NONE;
+        } else if (parameters->rsiz != ((OPJ_PROFILE_PART2) | (OPJ_EXTENSION_MCT))) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "Unsupported Part-2 extension enabled\n"
+                          "Profile set to NONE.\n");
+            parameters->rsiz = OPJ_PROFILE_NONE;
+        }
+    }
+
+    /*
+    copy user encoding parameters
+    */
+    cp->m_specific_param.m_enc.m_max_comp_size = (OPJ_UINT32)
+            parameters->max_comp_size;
+    cp->rsiz = parameters->rsiz;
+    cp->m_specific_param.m_enc.m_disto_alloc = (OPJ_UINT32)
+            parameters->cp_disto_alloc & 1u;
+    cp->m_specific_param.m_enc.m_fixed_alloc = (OPJ_UINT32)
+            parameters->cp_fixed_alloc & 1u;
+    cp->m_specific_param.m_enc.m_fixed_quality = (OPJ_UINT32)
+            parameters->cp_fixed_quality & 1u;
+
+    /* mod fixed_quality */
+    if (parameters->cp_fixed_alloc && parameters->cp_matrice) {
+        size_t array_size = (size_t)parameters->tcp_numlayers *
+                            (size_t)parameters->numresolution * 3 * sizeof(OPJ_INT32);
+        cp->m_specific_param.m_enc.m_matrice = (OPJ_INT32 *) opj_malloc(array_size);
+        if (!cp->m_specific_param.m_enc.m_matrice) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Not enough memory to allocate copy of user encoding parameters matrix \n");
+            return OPJ_FALSE;
+        }
+        memcpy(cp->m_specific_param.m_enc.m_matrice, parameters->cp_matrice,
+               array_size);
+    }
+
+    /* tiles */
+    cp->tdx = (OPJ_UINT32)parameters->cp_tdx;
+    cp->tdy = (OPJ_UINT32)parameters->cp_tdy;
+
+    /* tile offset */
+    cp->tx0 = (OPJ_UINT32)parameters->cp_tx0;
+    cp->ty0 = (OPJ_UINT32)parameters->cp_ty0;
+
+    /* comment string */
+    if (parameters->cp_comment) {
+        cp->comment = (char*)opj_malloc(strlen(parameters->cp_comment) + 1U);
+        if (!cp->comment) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Not enough memory to allocate copy of comment string\n");
+            return OPJ_FALSE;
+        }
+        strcpy(cp->comment, parameters->cp_comment);
+    } else {
+        /* Create default comment for codestream */
+        const char comment[] = "Created by OpenJPEG version ";
+        const size_t clen = strlen(comment);
+        const char *version = opj_version();
+
+        /* UniPG>> */
+#ifdef USE_JPWL
+        const size_t cp_comment_buf_size = clen + strlen(version) + 11;
+        cp->comment = (char*)opj_malloc(cp_comment_buf_size);
+        if (!cp->comment) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Not enough memory to allocate comment string\n");
+            return OPJ_FALSE;
+        }
+        snprintf(cp->comment, cp_comment_buf_size, "%s%s with JPWL",
+                 comment, version);
+#else
+        const size_t cp_comment_buf_size = clen + strlen(version) + 1;
+        cp->comment = (char*)opj_malloc(cp_comment_buf_size);
+        if (!cp->comment) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Not enough memory to allocate comment string\n");
+            return OPJ_FALSE;
+        }
+        snprintf(cp->comment, cp_comment_buf_size, "%s%s", comment, version);
+#endif
+        /* <<UniPG */
+    }
+
+    /*
+    calculate other encoding parameters
+    */
+
+    if (parameters->tile_size_on) {
+        if (cp->tdx == 0) {
+            opj_event_msg(p_manager, EVT_ERROR, "Invalid tile width\n");
+            return OPJ_FALSE;
+        }
+        if (cp->tdy == 0) {
+            opj_event_msg(p_manager, EVT_ERROR, "Invalid tile height\n");
+            return OPJ_FALSE;
+        }
+        cp->tw = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)(image->x1 - cp->tx0),
+                                             (OPJ_INT32)cp->tdx);
+        cp->th = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)(image->y1 - cp->ty0),
+                                             (OPJ_INT32)cp->tdy);
+        /* Check that the number of tiles is valid */
+        if (cp->tw > 65535 / cp->th) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Invalid number of tiles : %u x %u (maximum fixed by jpeg2000 norm is 65535 tiles)\n",
+                          cp->tw, cp->th);
+            return OPJ_FALSE;
+        }
+    } else {
+        cp->tdx = image->x1 - cp->tx0;
+        cp->tdy = image->y1 - cp->ty0;
+    }
+
+    if (parameters->tp_on) {
+        cp->m_specific_param.m_enc.m_tp_flag = (OPJ_BYTE)parameters->tp_flag;
+        cp->m_specific_param.m_enc.m_tp_on = 1;
+    }
+
+#ifdef USE_JPWL
+    /*
+    calculate JPWL encoding parameters
+    */
+
+    if (parameters->jpwl_epc_on) {
+        OPJ_INT32 i;
+
+        /* set JPWL on */
+        cp->epc_on = OPJ_TRUE;
+        cp->info_on = OPJ_FALSE; /* no informative technique */
+
+        /* set EPB on */
+        if ((parameters->jpwl_hprot_MH > 0) || (parameters->jpwl_hprot_TPH[0] > 0)) {
+            cp->epb_on = OPJ_TRUE;
+
+            cp->hprot_MH = parameters->jpwl_hprot_MH;
+            for (i = 0; i < JPWL_MAX_NO_TILESPECS; i++) {
+                cp->hprot_TPH_tileno[i] = parameters->jpwl_hprot_TPH_tileno[i];
+                cp->hprot_TPH[i] = parameters->jpwl_hprot_TPH[i];
+            }
+            /* if tile specs are not specified, copy MH specs */
+            if (cp->hprot_TPH[0] == -1) {
+                cp->hprot_TPH_tileno[0] = 0;
+                cp->hprot_TPH[0] = parameters->jpwl_hprot_MH;
+            }
+            for (i = 0; i < JPWL_MAX_NO_PACKSPECS; i++) {
+                cp->pprot_tileno[i] = parameters->jpwl_pprot_tileno[i];
+                cp->pprot_packno[i] = parameters->jpwl_pprot_packno[i];
+                cp->pprot[i] = parameters->jpwl_pprot[i];
+            }
+        }
+
+        /* set ESD writing */
+        if ((parameters->jpwl_sens_size == 1) || (parameters->jpwl_sens_size == 2)) {
+            cp->esd_on = OPJ_TRUE;
+
+            cp->sens_size = parameters->jpwl_sens_size;
+            cp->sens_addr = parameters->jpwl_sens_addr;
+            cp->sens_range = parameters->jpwl_sens_range;
+
+            cp->sens_MH = parameters->jpwl_sens_MH;
+            for (i = 0; i < JPWL_MAX_NO_TILESPECS; i++) {
+                cp->sens_TPH_tileno[i] = parameters->jpwl_sens_TPH_tileno[i];
+                cp->sens_TPH[i] = parameters->jpwl_sens_TPH[i];
+            }
+        }
+
+        /* always set RED writing to false: we are at the encoder */
+        cp->red_on = OPJ_FALSE;
+
+    } else {
+        cp->epc_on = OPJ_FALSE;
+    }
+#endif /* USE_JPWL */
+
+    /* initialize the multiple tiles */
+    /* ---------------------------- */
+    cp->tcps = (opj_tcp_t*) opj_calloc(cp->tw * cp->th, sizeof(opj_tcp_t));
+    if (!cp->tcps) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Not enough memory to allocate tile coding parameters\n");
+        return OPJ_FALSE;
+    }
+
+    for (tileno = 0; tileno < cp->tw * cp->th; tileno++) {
+        opj_tcp_t *tcp = &cp->tcps[tileno];
+        tcp->numlayers = (OPJ_UINT32)parameters->tcp_numlayers;
+
+        for (j = 0; j < tcp->numlayers; j++) {
+            if (OPJ_IS_CINEMA(cp->rsiz) || OPJ_IS_IMF(cp->rsiz)) {
+                if (cp->m_specific_param.m_enc.m_fixed_quality) {
+                    tcp->distoratio[j] = parameters->tcp_distoratio[j];
+                }
+                tcp->rates[j] = parameters->tcp_rates[j];
+            } else {
+                if (cp->m_specific_param.m_enc.m_fixed_quality) {       /* add fixed_quality */
+                    tcp->distoratio[j] = parameters->tcp_distoratio[j];
+                } else {
+                    tcp->rates[j] = parameters->tcp_rates[j];
+                }
+            }
+            if (!cp->m_specific_param.m_enc.m_fixed_quality &&
+                    tcp->rates[j] <= 1.0) {
+                tcp->rates[j] = 0.0;    /* force lossless */
+            }
+        }
+
+        tcp->csty = (OPJ_UINT32)parameters->csty;
+        tcp->prg = parameters->prog_order;
+        tcp->mct = (OPJ_UINT32)parameters->tcp_mct;
+
+        numpocs_tile = 0;
+        tcp->POC = 0;
+
+        if (parameters->numpocs) {
+            /* initialisation of POC */
+            for (i = 0; i < parameters->numpocs; i++) {
+                if (tileno + 1 == parameters->POC[i].tile)  {
+                    opj_poc_t *tcp_poc = &tcp->pocs[numpocs_tile];
+
+                    if (parameters->POC[numpocs_tile].compno0 >= image->numcomps) {
+                        opj_event_msg(p_manager, EVT_ERROR,
+                                      "Invalid compno0 for POC %d\n", i);
+                        return OPJ_FALSE;
+                    }
+
+                    tcp_poc->resno0         = parameters->POC[numpocs_tile].resno0;
+                    tcp_poc->compno0        = parameters->POC[numpocs_tile].compno0;
+                    tcp_poc->layno1         = parameters->POC[numpocs_tile].layno1;
+                    tcp_poc->resno1         = parameters->POC[numpocs_tile].resno1;
+                    tcp_poc->compno1        = opj_uint_min(parameters->POC[numpocs_tile].compno1,
+                                                           image->numcomps);
+                    tcp_poc->prg1           = parameters->POC[numpocs_tile].prg1;
+                    tcp_poc->tile           = parameters->POC[numpocs_tile].tile;
+
+                    numpocs_tile++;
+                }
+            }
+
+            if (numpocs_tile) {
+
+                /* TODO MSD use the return value*/
+                opj_j2k_check_poc_val(parameters->POC, tileno, parameters->numpocs,
+                                      (OPJ_UINT32)parameters->numresolution, image->numcomps,
+                                      (OPJ_UINT32)parameters->tcp_numlayers, p_manager);
+
+                tcp->POC = 1;
+                tcp->numpocs = numpocs_tile - 1 ;
+            }
+        } else {
+            tcp->numpocs = 0;
+        }
+
+        tcp->tccps = (opj_tccp_t*) opj_calloc(image->numcomps, sizeof(opj_tccp_t));
+        if (!tcp->tccps) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Not enough memory to allocate tile component coding parameters\n");
+            return OPJ_FALSE;
+        }
+        if (parameters->mct_data) {
+
+            OPJ_UINT32 lMctSize = image->numcomps * image->numcomps * (OPJ_UINT32)sizeof(
+                                      OPJ_FLOAT32);
+            OPJ_FLOAT32 * lTmpBuf = (OPJ_FLOAT32*)opj_malloc(lMctSize);
+            OPJ_INT32 * l_dc_shift = (OPJ_INT32 *)((OPJ_BYTE *) parameters->mct_data +
+                                                   lMctSize);
+
+            if (!lTmpBuf) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Not enough memory to allocate temp buffer\n");
+                return OPJ_FALSE;
+            }
+
+            tcp->mct = 2;
+            tcp->m_mct_coding_matrix = (OPJ_FLOAT32*)opj_malloc(lMctSize);
+            if (! tcp->m_mct_coding_matrix) {
+                opj_free(lTmpBuf);
+                lTmpBuf = NULL;
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Not enough memory to allocate encoder MCT coding matrix \n");
+                return OPJ_FALSE;
+            }
+            memcpy(tcp->m_mct_coding_matrix, parameters->mct_data, lMctSize);
+            memcpy(lTmpBuf, parameters->mct_data, lMctSize);
+
+            tcp->m_mct_decoding_matrix = (OPJ_FLOAT32*)opj_malloc(lMctSize);
+            if (! tcp->m_mct_decoding_matrix) {
+                opj_free(lTmpBuf);
+                lTmpBuf = NULL;
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Not enough memory to allocate encoder MCT decoding matrix \n");
+                return OPJ_FALSE;
+            }
+            if (opj_matrix_inversion_f(lTmpBuf, (tcp->m_mct_decoding_matrix),
+                                       image->numcomps) == OPJ_FALSE) {
+                opj_free(lTmpBuf);
+                lTmpBuf = NULL;
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Failed to inverse encoder MCT decoding matrix \n");
+                return OPJ_FALSE;
+            }
+
+            tcp->mct_norms = (OPJ_FLOAT64*)
+                             opj_malloc(image->numcomps * sizeof(OPJ_FLOAT64));
+            if (! tcp->mct_norms) {
+                opj_free(lTmpBuf);
+                lTmpBuf = NULL;
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Not enough memory to allocate encoder MCT norms \n");
+                return OPJ_FALSE;
+            }
+            opj_calculate_norms(tcp->mct_norms, image->numcomps,
+                                tcp->m_mct_decoding_matrix);
+            opj_free(lTmpBuf);
+
+            for (i = 0; i < image->numcomps; i++) {
+                opj_tccp_t *tccp = &tcp->tccps[i];
+                tccp->m_dc_level_shift = l_dc_shift[i];
+            }
+
+            if (opj_j2k_setup_mct_encoding(tcp, image) == OPJ_FALSE) {
+                /* free will be handled by opj_j2k_destroy */
+                opj_event_msg(p_manager, EVT_ERROR, "Failed to setup j2k mct encoding\n");
+                return OPJ_FALSE;
+            }
+        } else {
+            if (tcp->mct == 1 && image->numcomps >= 3) { /* RGB->YCC MCT is enabled */
+                if ((image->comps[0].dx != image->comps[1].dx) ||
+                        (image->comps[0].dx != image->comps[2].dx) ||
+                        (image->comps[0].dy != image->comps[1].dy) ||
+                        (image->comps[0].dy != image->comps[2].dy)) {
+                    opj_event_msg(p_manager, EVT_WARNING,
+                                  "Cannot perform MCT on components with different sizes. Disabling MCT.\n");
+                    tcp->mct = 0;
+                }
+            }
+            for (i = 0; i < image->numcomps; i++) {
+                opj_tccp_t *tccp = &tcp->tccps[i];
+                opj_image_comp_t * l_comp = &(image->comps[i]);
+
+                if (! l_comp->sgnd) {
+                    tccp->m_dc_level_shift = 1 << (l_comp->prec - 1);
+                }
+            }
+        }
+
+        for (i = 0; i < image->numcomps; i++) {
+            opj_tccp_t *tccp = &tcp->tccps[i];
+
+            tccp->csty = parameters->csty &
+                         0x01;   /* 0 => one precinct || 1 => custom precinct  */
+            tccp->numresolutions = (OPJ_UINT32)parameters->numresolution;
+            tccp->cblkw = (OPJ_UINT32)opj_int_floorlog2(parameters->cblockw_init);
+            tccp->cblkh = (OPJ_UINT32)opj_int_floorlog2(parameters->cblockh_init);
+            tccp->cblksty = (OPJ_UINT32)parameters->mode;
+            tccp->qmfbid = parameters->irreversible ? 0 : 1;
+            tccp->qntsty = parameters->irreversible ? J2K_CCP_QNTSTY_SEQNT :
+                           J2K_CCP_QNTSTY_NOQNT;
+            tccp->numgbits = 2;
+
+            if ((OPJ_INT32)i == parameters->roi_compno) {
+                tccp->roishift = parameters->roi_shift;
+            } else {
+                tccp->roishift = 0;
+            }
+
+            if (parameters->csty & J2K_CCP_CSTY_PRT) {
+                OPJ_INT32 p = 0, it_res;
+                assert(tccp->numresolutions > 0);
+                for (it_res = (OPJ_INT32)tccp->numresolutions - 1; it_res >= 0; it_res--) {
+                    if (p < parameters->res_spec) {
+
+                        if (parameters->prcw_init[p] < 1) {
+                            tccp->prcw[it_res] = 1;
+                        } else {
+                            tccp->prcw[it_res] = (OPJ_UINT32)opj_int_floorlog2(parameters->prcw_init[p]);
+                        }
+
+                        if (parameters->prch_init[p] < 1) {
+                            tccp->prch[it_res] = 1;
+                        } else {
+                            tccp->prch[it_res] = (OPJ_UINT32)opj_int_floorlog2(parameters->prch_init[p]);
+                        }
+
+                    } else {
+                        OPJ_INT32 res_spec = parameters->res_spec;
+                        OPJ_INT32 size_prcw = 0;
+                        OPJ_INT32 size_prch = 0;
+
+                        assert(res_spec > 0); /* issue 189 */
+                        size_prcw = parameters->prcw_init[res_spec - 1] >> (p - (res_spec - 1));
+                        size_prch = parameters->prch_init[res_spec - 1] >> (p - (res_spec - 1));
+
+
+                        if (size_prcw < 1) {
+                            tccp->prcw[it_res] = 1;
+                        } else {
+                            tccp->prcw[it_res] = (OPJ_UINT32)opj_int_floorlog2(size_prcw);
+                        }
+
+                        if (size_prch < 1) {
+                            tccp->prch[it_res] = 1;
+                        } else {
+                            tccp->prch[it_res] = (OPJ_UINT32)opj_int_floorlog2(size_prch);
+                        }
+                    }
+                    p++;
+                    /*printf("\nsize precinct for level %d : %d,%d\n", it_res,tccp->prcw[it_res], tccp->prch[it_res]); */
+                }       /*end for*/
+            } else {
+                for (j = 0; j < tccp->numresolutions; j++) {
+                    tccp->prcw[j] = 15;
+                    tccp->prch[j] = 15;
+                }
+            }
+
+            opj_dwt_calc_explicit_stepsizes(tccp, image->comps[i].prec);
+        }
+    }
+
+    if (parameters->mct_data) {
+        opj_free(parameters->mct_data);
+        parameters->mct_data = 00;
+    }
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_add_mhmarker(opj_codestream_index_t *cstr_index,
+                                     OPJ_UINT32 type, OPJ_OFF_T pos, OPJ_UINT32 len)
+{
+    assert(cstr_index != 00);
+
+    /* expand the list? */
+    if ((cstr_index->marknum + 1) > cstr_index->maxmarknum) {
+        opj_marker_info_t *new_marker;
+        cstr_index->maxmarknum = (OPJ_UINT32)(100 + (OPJ_FLOAT32)
+                                              cstr_index->maxmarknum);
+        new_marker = (opj_marker_info_t *) opj_realloc(cstr_index->marker,
+                     cstr_index->maxmarknum * sizeof(opj_marker_info_t));
+        if (! new_marker) {
+            opj_free(cstr_index->marker);
+            cstr_index->marker = NULL;
+            cstr_index->maxmarknum = 0;
+            cstr_index->marknum = 0;
+            /* opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to add mh marker\n"); */
+            return OPJ_FALSE;
+        }
+        cstr_index->marker = new_marker;
+    }
+
+    /* add the marker */
+    cstr_index->marker[cstr_index->marknum].type = (OPJ_UINT16)type;
+    cstr_index->marker[cstr_index->marknum].pos = (OPJ_INT32)pos;
+    cstr_index->marker[cstr_index->marknum].len = (OPJ_INT32)len;
+    cstr_index->marknum++;
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_add_tlmarker(OPJ_UINT32 tileno,
+                                     opj_codestream_index_t *cstr_index, OPJ_UINT32 type, OPJ_OFF_T pos,
+                                     OPJ_UINT32 len)
+{
+    assert(cstr_index != 00);
+    assert(cstr_index->tile_index != 00);
+
+    /* expand the list? */
+    if ((cstr_index->tile_index[tileno].marknum + 1) >
+            cstr_index->tile_index[tileno].maxmarknum) {
+        opj_marker_info_t *new_marker;
+        cstr_index->tile_index[tileno].maxmarknum = (OPJ_UINT32)(100 +
+                (OPJ_FLOAT32) cstr_index->tile_index[tileno].maxmarknum);
+        new_marker = (opj_marker_info_t *) opj_realloc(
+                         cstr_index->tile_index[tileno].marker,
+                         cstr_index->tile_index[tileno].maxmarknum * sizeof(opj_marker_info_t));
+        if (! new_marker) {
+            opj_free(cstr_index->tile_index[tileno].marker);
+            cstr_index->tile_index[tileno].marker = NULL;
+            cstr_index->tile_index[tileno].maxmarknum = 0;
+            cstr_index->tile_index[tileno].marknum = 0;
+            /* opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to add tl marker\n"); */
+            return OPJ_FALSE;
+        }
+        cstr_index->tile_index[tileno].marker = new_marker;
+    }
+
+    /* add the marker */
+    cstr_index->tile_index[tileno].marker[cstr_index->tile_index[tileno].marknum].type
+        = (OPJ_UINT16)type;
+    cstr_index->tile_index[tileno].marker[cstr_index->tile_index[tileno].marknum].pos
+        = (OPJ_INT32)pos;
+    cstr_index->tile_index[tileno].marker[cstr_index->tile_index[tileno].marknum].len
+        = (OPJ_INT32)len;
+    cstr_index->tile_index[tileno].marknum++;
+
+    if (type == J2K_MS_SOT) {
+        OPJ_UINT32 l_current_tile_part = cstr_index->tile_index[tileno].current_tpsno;
+
+        if (cstr_index->tile_index[tileno].tp_index) {
+            cstr_index->tile_index[tileno].tp_index[l_current_tile_part].start_pos = pos;
+        }
+
+    }
+    return OPJ_TRUE;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ * -----------------------------------------------------------------------
+ * -----------------------------------------------------------------------
+ */
+
+OPJ_BOOL opj_j2k_end_decompress(void *p_j2k,
+                                opj_stream_private_t *p_stream,
+                                opj_event_mgr_t * p_manager
+                               )
+{
+    (void)p_j2k;
+    (void)p_stream;
+    (void)p_manager;
+    return OPJ_TRUE;
+}
+
+OPJ_BOOL opj_j2k_read_header(opj_stream_private_t *p_stream,
+                             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);
+    assert(p_manager != 00);
+
+    /* create an empty image header */
+    p_j2k->m_private_image = opj_image_create0();
+    if (! p_j2k->m_private_image) {
+        return OPJ_FALSE;
+    }
+
+    /* customization of the validation */
+    if (! opj_j2k_setup_decoding_validation(p_j2k, p_manager)) {
+        opj_image_destroy(p_j2k->m_private_image);
+        p_j2k->m_private_image = NULL;
+        return OPJ_FALSE;
+    }
+
+    /* validation of the parameters codec */
+    if (! opj_j2k_exec(p_j2k, p_j2k->m_validation_list, p_stream, p_manager)) {
+        opj_image_destroy(p_j2k->m_private_image);
+        p_j2k->m_private_image = NULL;
+        return OPJ_FALSE;
+    }
+
+    /* customization of the encoding */
+    if (! opj_j2k_setup_header_reading(p_j2k, p_manager)) {
+        opj_image_destroy(p_j2k->m_private_image);
+        p_j2k->m_private_image = NULL;
+        return OPJ_FALSE;
+    }
+
+    /* read header */
+    if (! opj_j2k_exec(p_j2k, p_j2k->m_procedure_list, p_stream, p_manager)) {
+        opj_image_destroy(p_j2k->m_private_image);
+        p_j2k->m_private_image = NULL;
+        return OPJ_FALSE;
+    }
+
+    *p_image = opj_image_create0();
+    if (!(*p_image)) {
+        return OPJ_FALSE;
+    }
+
+    /* Copy codestream image information to the output image */
+    opj_copy_image_header(p_j2k->m_private_image, *p_image);
+
+    /*Allocate and initialize some elements of codestrem index*/
+    if (!opj_j2k_allocate_tile_element_cstr_index(p_j2k)) {
+        opj_image_destroy(*p_image);
+        *p_image = NULL;
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_setup_header_reading(opj_j2k_t *p_j2k,
+        opj_event_mgr_t * p_manager)
+{
+    /* preconditions*/
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_read_header_procedure, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* DEVELOPER CORNER, add your custom procedures */
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_copy_default_tcp_and_create_tcd, p_manager))  {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_setup_decoding_validation(opj_j2k_t *p_j2k,
+        opj_event_mgr_t * p_manager)
+{
+    /* preconditions*/
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    if (! opj_procedure_list_add_procedure(p_j2k->m_validation_list,
+                                           (opj_procedure)opj_j2k_build_decoder, p_manager)) {
+        return OPJ_FALSE;
+    }
+    if (! opj_procedure_list_add_procedure(p_j2k->m_validation_list,
+                                           (opj_procedure)opj_j2k_decoding_validation, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* DEVELOPER CORNER, add your custom validation procedure */
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_mct_validation(opj_j2k_t * p_j2k,
+                                       opj_stream_private_t *p_stream,
+                                       opj_event_mgr_t * p_manager)
+{
+    OPJ_BOOL l_is_valid = OPJ_TRUE;
+    OPJ_UINT32 i, j;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_stream != 00);
+    assert(p_manager != 00);
+
+    OPJ_UNUSED(p_stream);
+    OPJ_UNUSED(p_manager);
+
+    if ((p_j2k->m_cp.rsiz & 0x8200) == 0x8200) {
+        OPJ_UINT32 l_nb_tiles = p_j2k->m_cp.th * p_j2k->m_cp.tw;
+        opj_tcp_t * l_tcp = p_j2k->m_cp.tcps;
+
+        for (i = 0; i < l_nb_tiles; ++i) {
+            if (l_tcp->mct == 2) {
+                opj_tccp_t * l_tccp = l_tcp->tccps;
+                l_is_valid &= (l_tcp->m_mct_coding_matrix != 00);
+
+                for (j = 0; j < p_j2k->m_private_image->numcomps; ++j) {
+                    l_is_valid &= !(l_tccp->qmfbid & 1);
+                    ++l_tccp;
+                }
+            }
+            ++l_tcp;
+        }
+    }
+
+    return l_is_valid;
+}
+
+OPJ_BOOL opj_j2k_setup_mct_encoding(opj_tcp_t * p_tcp, opj_image_t * p_image)
+{
+    OPJ_UINT32 i;
+    OPJ_UINT32 l_indix = 1;
+    opj_mct_data_t * l_mct_deco_data = 00, * l_mct_offset_data = 00;
+    opj_simple_mcc_decorrelation_data_t * l_mcc_data;
+    OPJ_UINT32 l_mct_size, l_nb_elem;
+    OPJ_FLOAT32 * l_data, * l_current_data;
+    opj_tccp_t * l_tccp;
+
+    /* preconditions */
+    assert(p_tcp != 00);
+
+    if (p_tcp->mct != 2) {
+        return OPJ_TRUE;
+    }
+
+    if (p_tcp->m_mct_decoding_matrix) {
+        if (p_tcp->m_nb_mct_records == p_tcp->m_nb_max_mct_records) {
+            opj_mct_data_t *new_mct_records;
+            p_tcp->m_nb_max_mct_records += OPJ_J2K_MCT_DEFAULT_NB_RECORDS;
+
+            new_mct_records = (opj_mct_data_t *) opj_realloc(p_tcp->m_mct_records,
+                              p_tcp->m_nb_max_mct_records * sizeof(opj_mct_data_t));
+            if (! new_mct_records) {
+                opj_free(p_tcp->m_mct_records);
+                p_tcp->m_mct_records = NULL;
+                p_tcp->m_nb_max_mct_records = 0;
+                p_tcp->m_nb_mct_records = 0;
+                /* opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to setup mct encoding\n"); */
+                return OPJ_FALSE;
+            }
+            p_tcp->m_mct_records = new_mct_records;
+            l_mct_deco_data = p_tcp->m_mct_records + p_tcp->m_nb_mct_records;
+
+            memset(l_mct_deco_data, 0,
+                   (p_tcp->m_nb_max_mct_records - p_tcp->m_nb_mct_records) * sizeof(
+                       opj_mct_data_t));
+        }
+        l_mct_deco_data = p_tcp->m_mct_records + p_tcp->m_nb_mct_records;
+
+        if (l_mct_deco_data->m_data) {
+            opj_free(l_mct_deco_data->m_data);
+            l_mct_deco_data->m_data = 00;
+        }
+
+        l_mct_deco_data->m_index = l_indix++;
+        l_mct_deco_data->m_array_type = MCT_TYPE_DECORRELATION;
+        l_mct_deco_data->m_element_type = MCT_TYPE_FLOAT;
+        l_nb_elem = p_image->numcomps * p_image->numcomps;
+        l_mct_size = l_nb_elem * MCT_ELEMENT_SIZE[l_mct_deco_data->m_element_type];
+        l_mct_deco_data->m_data = (OPJ_BYTE*)opj_malloc(l_mct_size);
+
+        if (! l_mct_deco_data->m_data) {
+            return OPJ_FALSE;
+        }
+
+        j2k_mct_write_functions_from_float[l_mct_deco_data->m_element_type](
+            p_tcp->m_mct_decoding_matrix, l_mct_deco_data->m_data, l_nb_elem);
+
+        l_mct_deco_data->m_data_size = l_mct_size;
+        ++p_tcp->m_nb_mct_records;
+    }
+
+    if (p_tcp->m_nb_mct_records == p_tcp->m_nb_max_mct_records) {
+        opj_mct_data_t *new_mct_records;
+        p_tcp->m_nb_max_mct_records += OPJ_J2K_MCT_DEFAULT_NB_RECORDS;
+        new_mct_records = (opj_mct_data_t *) opj_realloc(p_tcp->m_mct_records,
+                          p_tcp->m_nb_max_mct_records * sizeof(opj_mct_data_t));
+        if (! new_mct_records) {
+            opj_free(p_tcp->m_mct_records);
+            p_tcp->m_mct_records = NULL;
+            p_tcp->m_nb_max_mct_records = 0;
+            p_tcp->m_nb_mct_records = 0;
+            /* opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to setup mct encoding\n"); */
+            return OPJ_FALSE;
+        }
+        p_tcp->m_mct_records = new_mct_records;
+        l_mct_offset_data = p_tcp->m_mct_records + p_tcp->m_nb_mct_records;
+
+        memset(l_mct_offset_data, 0,
+               (p_tcp->m_nb_max_mct_records - p_tcp->m_nb_mct_records) * sizeof(
+                   opj_mct_data_t));
+
+        if (l_mct_deco_data) {
+            l_mct_deco_data = l_mct_offset_data - 1;
+        }
+    }
+
+    l_mct_offset_data = p_tcp->m_mct_records + p_tcp->m_nb_mct_records;
+
+    if (l_mct_offset_data->m_data) {
+        opj_free(l_mct_offset_data->m_data);
+        l_mct_offset_data->m_data = 00;
+    }
+
+    l_mct_offset_data->m_index = l_indix++;
+    l_mct_offset_data->m_array_type = MCT_TYPE_OFFSET;
+    l_mct_offset_data->m_element_type = MCT_TYPE_FLOAT;
+    l_nb_elem = p_image->numcomps;
+    l_mct_size = l_nb_elem * MCT_ELEMENT_SIZE[l_mct_offset_data->m_element_type];
+    l_mct_offset_data->m_data = (OPJ_BYTE*)opj_malloc(l_mct_size);
+
+    if (! l_mct_offset_data->m_data) {
+        return OPJ_FALSE;
+    }
+
+    l_data = (OPJ_FLOAT32*)opj_malloc(l_nb_elem * sizeof(OPJ_FLOAT32));
+    if (! l_data) {
+        opj_free(l_mct_offset_data->m_data);
+        l_mct_offset_data->m_data = 00;
+        return OPJ_FALSE;
+    }
+
+    l_tccp = p_tcp->tccps;
+    l_current_data = l_data;
+
+    for (i = 0; i < l_nb_elem; ++i) {
+        *(l_current_data++) = (OPJ_FLOAT32)(l_tccp->m_dc_level_shift);
+        ++l_tccp;
+    }
+
+    j2k_mct_write_functions_from_float[l_mct_offset_data->m_element_type](l_data,
+            l_mct_offset_data->m_data, l_nb_elem);
+
+    opj_free(l_data);
+
+    l_mct_offset_data->m_data_size = l_mct_size;
+
+    ++p_tcp->m_nb_mct_records;
+
+    if (p_tcp->m_nb_mcc_records == p_tcp->m_nb_max_mcc_records) {
+        opj_simple_mcc_decorrelation_data_t *new_mcc_records;
+        p_tcp->m_nb_max_mcc_records += OPJ_J2K_MCT_DEFAULT_NB_RECORDS;
+        new_mcc_records = (opj_simple_mcc_decorrelation_data_t *) opj_realloc(
+                              p_tcp->m_mcc_records, p_tcp->m_nb_max_mcc_records * sizeof(
+                                  opj_simple_mcc_decorrelation_data_t));
+        if (! new_mcc_records) {
+            opj_free(p_tcp->m_mcc_records);
+            p_tcp->m_mcc_records = NULL;
+            p_tcp->m_nb_max_mcc_records = 0;
+            p_tcp->m_nb_mcc_records = 0;
+            /* opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to setup mct encoding\n"); */
+            return OPJ_FALSE;
+        }
+        p_tcp->m_mcc_records = new_mcc_records;
+        l_mcc_data = p_tcp->m_mcc_records + p_tcp->m_nb_mcc_records;
+        memset(l_mcc_data, 0, (p_tcp->m_nb_max_mcc_records - p_tcp->m_nb_mcc_records) *
+               sizeof(opj_simple_mcc_decorrelation_data_t));
+
+    }
+
+    l_mcc_data = p_tcp->m_mcc_records + p_tcp->m_nb_mcc_records;
+    l_mcc_data->m_decorrelation_array = l_mct_deco_data;
+    l_mcc_data->m_is_irreversible = 1;
+    l_mcc_data->m_nb_comps = p_image->numcomps;
+    l_mcc_data->m_index = l_indix++;
+    l_mcc_data->m_offset_array = l_mct_offset_data;
+    ++p_tcp->m_nb_mcc_records;
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_build_decoder(opj_j2k_t * p_j2k,
+                                      opj_stream_private_t *p_stream,
+                                      opj_event_mgr_t * p_manager)
+{
+    /* add here initialization of cp
+       copy paste of setup_decoder */
+    (void)p_j2k;
+    (void)p_stream;
+    (void)p_manager;
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_build_encoder(opj_j2k_t * p_j2k,
+                                      opj_stream_private_t *p_stream,
+                                      opj_event_mgr_t * p_manager)
+{
+    /* add here initialization of cp
+       copy paste of setup_encoder */
+    (void)p_j2k;
+    (void)p_stream;
+    (void)p_manager;
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_encoding_validation(opj_j2k_t * p_j2k,
+        opj_stream_private_t *p_stream,
+        opj_event_mgr_t * p_manager)
+{
+    OPJ_BOOL l_is_valid = OPJ_TRUE;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_stream != 00);
+    assert(p_manager != 00);
+
+    OPJ_UNUSED(p_stream);
+
+    /* STATE checking */
+    /* make sure the state is at 0 */
+    l_is_valid &= (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_NONE);
+
+    /* POINTER validation */
+    /* make sure a p_j2k codec is present */
+    l_is_valid &= (p_j2k->m_procedure_list != 00);
+    /* make sure a validation list is present */
+    l_is_valid &= (p_j2k->m_validation_list != 00);
+
+    /* ISO 15444-1:2004 states between 1 & 33 (0 -> 32) */
+    /* 33 (32) would always fail the check below (if a cast to 64bits was done) */
+    /* FIXME Shall we change OPJ_J2K_MAXRLVLS to 32 ? */
+    if ((p_j2k->m_cp.tcps->tccps->numresolutions <= 0) ||
+            (p_j2k->m_cp.tcps->tccps->numresolutions > 32)) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Number of resolutions is too high in comparison to the size of tiles\n");
+        return OPJ_FALSE;
+    }
+
+    if ((p_j2k->m_cp.tdx) < (OPJ_UINT32)(1 <<
+                                         (p_j2k->m_cp.tcps->tccps->numresolutions - 1U))) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Number of resolutions is too high in comparison to the size of tiles\n");
+        return OPJ_FALSE;
+    }
+
+    if ((p_j2k->m_cp.tdy) < (OPJ_UINT32)(1 <<
+                                         (p_j2k->m_cp.tcps->tccps->numresolutions - 1U))) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Number of resolutions is too high in comparison to the size of tiles\n");
+        return OPJ_FALSE;
+    }
+
+    /* PARAMETER VALIDATION */
+    return l_is_valid;
+}
+
+static OPJ_BOOL opj_j2k_decoding_validation(opj_j2k_t *p_j2k,
+        opj_stream_private_t *p_stream,
+        opj_event_mgr_t * p_manager
+                                           )
+{
+    OPJ_BOOL l_is_valid = OPJ_TRUE;
+
+    /* preconditions*/
+    assert(p_j2k != 00);
+    assert(p_stream != 00);
+    assert(p_manager != 00);
+
+    OPJ_UNUSED(p_stream);
+    OPJ_UNUSED(p_manager);
+
+    /* STATE checking */
+    /* make sure the state is at 0 */
+#ifdef TODO_MSD
+    l_is_valid &= (p_j2k->m_specific_param.m_decoder.m_state == J2K_DEC_STATE_NONE);
+#endif
+    l_is_valid &= (p_j2k->m_specific_param.m_decoder.m_state == 0x0000);
+
+    /* POINTER validation */
+    /* make sure a p_j2k codec is present */
+    /* make sure a procedure list is present */
+    l_is_valid &= (p_j2k->m_procedure_list != 00);
+    /* make sure a validation list is present */
+    l_is_valid &= (p_j2k->m_validation_list != 00);
+
+    /* PARAMETER VALIDATION */
+    return l_is_valid;
+}
+
+static OPJ_BOOL opj_j2k_read_header_procedure(opj_j2k_t *p_j2k,
+        opj_stream_private_t *p_stream,
+        opj_event_mgr_t * p_manager)
+{
+    OPJ_UINT32 l_current_marker;
+    OPJ_UINT32 l_marker_size;
+    const opj_dec_memory_marker_handler_t * l_marker_handler = 00;
+    OPJ_BOOL l_has_siz = 0;
+    OPJ_BOOL l_has_cod = 0;
+    OPJ_BOOL l_has_qcd = 0;
+
+    /* preconditions */
+    assert(p_stream != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    /*  We enter in the main header */
+    p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_MHSOC;
+
+    /* Try to read the SOC marker, the codestream must begin with SOC marker */
+    if (! opj_j2k_read_soc(p_j2k, p_stream, p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Expected a SOC marker \n");
+        return OPJ_FALSE;
+    }
+
+    /* Try to read 2 bytes (the next marker ID) from stream and copy them into the buffer */
+    if (opj_stream_read_data(p_stream,
+                             p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
+        opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
+        return OPJ_FALSE;
+    }
+
+    /* Read 2 bytes as the new marker ID */
+    opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data,
+                   &l_current_marker, 2);
+
+    /* Try to read until the SOT is detected */
+    while (l_current_marker != J2K_MS_SOT) {
+
+        /* Check if the current marker ID is valid */
+        if (l_current_marker < 0xff00) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "A marker ID was expected (0xff--) instead of %.8x\n", l_current_marker);
+            return OPJ_FALSE;
+        }
+
+        /* Get the marker handler from the marker ID */
+        l_marker_handler = opj_j2k_get_marker_handler(l_current_marker);
+
+        /* Manage case where marker is unknown */
+        if (l_marker_handler->id == J2K_MS_UNK) {
+            if (! opj_j2k_read_unk(p_j2k, p_stream, &l_current_marker, p_manager)) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Unknown marker has been detected and generated error.\n");
+                return OPJ_FALSE;
+            }
+
+            if (l_current_marker == J2K_MS_SOT) {
+                break;    /* SOT marker is detected main header is completely read */
+            } else { /* Get the marker handler from the marker ID */
+                l_marker_handler = opj_j2k_get_marker_handler(l_current_marker);
+            }
+        }
+
+        if (l_marker_handler->id == J2K_MS_SIZ) {
+            /* Mark required SIZ marker as found */
+            l_has_siz = 1;
+        }
+        if (l_marker_handler->id == J2K_MS_COD) {
+            /* Mark required COD marker as found */
+            l_has_cod = 1;
+        }
+        if (l_marker_handler->id == J2K_MS_QCD) {
+            /* Mark required QCD marker as found */
+            l_has_qcd = 1;
+        }
+
+        /* Check if the marker is known and if it is the right place to find it */
+        if (!(p_j2k->m_specific_param.m_decoder.m_state & l_marker_handler->states)) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Marker is not compliant with its position\n");
+            return OPJ_FALSE;
+        }
+
+        /* Try to read 2 bytes (the marker size) from stream and copy them into the buffer */
+        if (opj_stream_read_data(p_stream,
+                                 p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
+            opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
+            return OPJ_FALSE;
+        }
+
+        /* read 2 bytes as the marker size */
+        opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data, &l_marker_size,
+                       2);
+        if (l_marker_size < 2) {
+            opj_event_msg(p_manager, EVT_ERROR, "Invalid marker size\n");
+            return OPJ_FALSE;
+        }
+        l_marker_size -= 2; /* Subtract the size of the marker ID already read */
+
+        /* Check if the marker size is compatible with the header data size */
+        if (l_marker_size > p_j2k->m_specific_param.m_decoder.m_header_data_size) {
+            OPJ_BYTE *new_header_data = (OPJ_BYTE *) opj_realloc(
+                                            p_j2k->m_specific_param.m_decoder.m_header_data, l_marker_size);
+            if (! new_header_data) {
+                opj_free(p_j2k->m_specific_param.m_decoder.m_header_data);
+                p_j2k->m_specific_param.m_decoder.m_header_data = NULL;
+                p_j2k->m_specific_param.m_decoder.m_header_data_size = 0;
+                opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read header\n");
+                return OPJ_FALSE;
+            }
+            p_j2k->m_specific_param.m_decoder.m_header_data = new_header_data;
+            p_j2k->m_specific_param.m_decoder.m_header_data_size = l_marker_size;
+        }
+
+        /* Try to read the rest of the marker segment from stream and copy them into the buffer */
+        if (opj_stream_read_data(p_stream,
+                                 p_j2k->m_specific_param.m_decoder.m_header_data, l_marker_size,
+                                 p_manager) != l_marker_size) {
+            opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
+            return OPJ_FALSE;
+        }
+
+        /* Read the marker segment with the correct marker handler */
+        if (!(*(l_marker_handler->handler))(p_j2k,
+                                            p_j2k->m_specific_param.m_decoder.m_header_data, l_marker_size, p_manager)) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Marker handler function failed to read the marker segment\n");
+            return OPJ_FALSE;
+        }
+
+        /* Add the marker to the codestream index*/
+        if (OPJ_FALSE == opj_j2k_add_mhmarker(
+                    p_j2k->cstr_index,
+                    l_marker_handler->id,
+                    (OPJ_UINT32) opj_stream_tell(p_stream) - l_marker_size - 4,
+                    l_marker_size + 4)) {
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to add mh marker\n");
+            return OPJ_FALSE;
+        }
+
+        /* Try to read 2 bytes (the next marker ID) from stream and copy them into the buffer */
+        if (opj_stream_read_data(p_stream,
+                                 p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
+            opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
+            return OPJ_FALSE;
+        }
+
+        /* read 2 bytes as the new marker ID */
+        opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data,
+                       &l_current_marker, 2);
+    }
+
+    if (l_has_siz == 0) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "required SIZ marker not found in main header\n");
+        return OPJ_FALSE;
+    }
+    if (l_has_cod == 0) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "required COD marker not found in main header\n");
+        return OPJ_FALSE;
+    }
+    if (l_has_qcd == 0) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "required QCD marker not found in main header\n");
+        return OPJ_FALSE;
+    }
+
+    if (! opj_j2k_merge_ppm(&(p_j2k->m_cp), p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Failed to merge PPM data\n");
+        return OPJ_FALSE;
+    }
+
+    opj_event_msg(p_manager, EVT_INFO, "Main header has been correctly decoded.\n");
+
+    /* Position of the last element if the main header */
+    p_j2k->cstr_index->main_head_end = (OPJ_UINT32) opj_stream_tell(p_stream) - 2;
+
+    /* Next step: read a tile-part header */
+    p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_TPHSOT;
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_exec(opj_j2k_t * p_j2k,
+                             opj_procedure_list_t * p_procedure_list,
+                             opj_stream_private_t *p_stream,
+                             opj_event_mgr_t * p_manager)
+{
+    OPJ_BOOL(** l_procedure)(opj_j2k_t *, opj_stream_private_t *,
+                             opj_event_mgr_t *) = 00;
+    OPJ_BOOL l_result = OPJ_TRUE;
+    OPJ_UINT32 l_nb_proc, i;
+
+    /* preconditions*/
+    assert(p_procedure_list != 00);
+    assert(p_j2k != 00);
+    assert(p_stream != 00);
+    assert(p_manager != 00);
+
+    l_nb_proc = opj_procedure_list_get_nb_procedures(p_procedure_list);
+    l_procedure = (OPJ_BOOL(**)(opj_j2k_t *, opj_stream_private_t *,
+                                opj_event_mgr_t *)) opj_procedure_list_get_first_procedure(p_procedure_list);
+
+    for (i = 0; i < l_nb_proc; ++i) {
+        l_result = l_result && ((*l_procedure)(p_j2k, p_stream, p_manager));
+        ++l_procedure;
+    }
+
+    /* and clear the procedure list at the end.*/
+    opj_procedure_list_clear(p_procedure_list);
+    return l_result;
+}
+
+/* FIXME DOC*/
+static OPJ_BOOL opj_j2k_copy_default_tcp_and_create_tcd(opj_j2k_t * p_j2k,
+        opj_stream_private_t *p_stream,
+        opj_event_mgr_t * p_manager
+                                                       )
+{
+    opj_tcp_t * l_tcp = 00;
+    opj_tcp_t * l_default_tcp = 00;
+    OPJ_UINT32 l_nb_tiles;
+    OPJ_UINT32 i, j;
+    opj_tccp_t *l_current_tccp = 00;
+    OPJ_UINT32 l_tccp_size;
+    OPJ_UINT32 l_mct_size;
+    opj_image_t * l_image;
+    OPJ_UINT32 l_mcc_records_size, l_mct_records_size;
+    opj_mct_data_t * l_src_mct_rec, *l_dest_mct_rec;
+    opj_simple_mcc_decorrelation_data_t * l_src_mcc_rec, *l_dest_mcc_rec;
+    OPJ_UINT32 l_offset;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_stream != 00);
+    assert(p_manager != 00);
+
+    OPJ_UNUSED(p_stream);
+
+    l_image = p_j2k->m_private_image;
+    l_nb_tiles = p_j2k->m_cp.th * p_j2k->m_cp.tw;
+    l_tcp = p_j2k->m_cp.tcps;
+    l_tccp_size = l_image->numcomps * (OPJ_UINT32)sizeof(opj_tccp_t);
+    l_default_tcp = p_j2k->m_specific_param.m_decoder.m_default_tcp;
+    l_mct_size = l_image->numcomps * l_image->numcomps * (OPJ_UINT32)sizeof(
+                     OPJ_FLOAT32);
+
+    /* For each tile */
+    for (i = 0; i < l_nb_tiles; ++i) {
+        /* keep the tile-compo coding parameters pointer of the current tile coding parameters*/
+        l_current_tccp = l_tcp->tccps;
+        /*Copy default coding parameters into the current tile coding parameters*/
+        memcpy(l_tcp, l_default_tcp, sizeof(opj_tcp_t));
+        /* Initialize some values of the current tile coding parameters*/
+        l_tcp->cod = 0;
+        l_tcp->ppt = 0;
+        l_tcp->ppt_data = 00;
+        l_tcp->m_current_tile_part_number = -1;
+        /* Remove memory not owned by this tile in case of early error return. */
+        l_tcp->m_mct_decoding_matrix = 00;
+        l_tcp->m_nb_max_mct_records = 0;
+        l_tcp->m_mct_records = 00;
+        l_tcp->m_nb_max_mcc_records = 0;
+        l_tcp->m_mcc_records = 00;
+        /* Reconnect the tile-compo coding parameters pointer to the current tile coding parameters*/
+        l_tcp->tccps = l_current_tccp;
+
+        /* Get the mct_decoding_matrix of the dflt_tile_cp and copy them into the current tile cp*/
+        if (l_default_tcp->m_mct_decoding_matrix) {
+            l_tcp->m_mct_decoding_matrix = (OPJ_FLOAT32*)opj_malloc(l_mct_size);
+            if (! l_tcp->m_mct_decoding_matrix) {
+                return OPJ_FALSE;
+            }
+            memcpy(l_tcp->m_mct_decoding_matrix, l_default_tcp->m_mct_decoding_matrix,
+                   l_mct_size);
+        }
+
+        /* Get the mct_record of the dflt_tile_cp and copy them into the current tile cp*/
+        l_mct_records_size = l_default_tcp->m_nb_max_mct_records * (OPJ_UINT32)sizeof(
+                                 opj_mct_data_t);
+        l_tcp->m_mct_records = (opj_mct_data_t*)opj_malloc(l_mct_records_size);
+        if (! l_tcp->m_mct_records) {
+            return OPJ_FALSE;
+        }
+        memcpy(l_tcp->m_mct_records, l_default_tcp->m_mct_records, l_mct_records_size);
+
+        /* Copy the mct record data from dflt_tile_cp to the current tile*/
+        l_src_mct_rec = l_default_tcp->m_mct_records;
+        l_dest_mct_rec = l_tcp->m_mct_records;
+
+        for (j = 0; j < l_default_tcp->m_nb_mct_records; ++j) {
+
+            if (l_src_mct_rec->m_data) {
+
+                l_dest_mct_rec->m_data = (OPJ_BYTE*) opj_malloc(l_src_mct_rec->m_data_size);
+                if (! l_dest_mct_rec->m_data) {
+                    return OPJ_FALSE;
+                }
+                memcpy(l_dest_mct_rec->m_data, l_src_mct_rec->m_data,
+                       l_src_mct_rec->m_data_size);
+            }
+
+            ++l_src_mct_rec;
+            ++l_dest_mct_rec;
+            /* Update with each pass to free exactly what has been allocated on early return. */
+            l_tcp->m_nb_max_mct_records += 1;
+        }
+
+        /* Get the mcc_record of the dflt_tile_cp and copy them into the current tile cp*/
+        l_mcc_records_size = l_default_tcp->m_nb_max_mcc_records * (OPJ_UINT32)sizeof(
+                                 opj_simple_mcc_decorrelation_data_t);
+        l_tcp->m_mcc_records = (opj_simple_mcc_decorrelation_data_t*) opj_malloc(
+                                   l_mcc_records_size);
+        if (! l_tcp->m_mcc_records) {
+            return OPJ_FALSE;
+        }
+        memcpy(l_tcp->m_mcc_records, l_default_tcp->m_mcc_records, l_mcc_records_size);
+        l_tcp->m_nb_max_mcc_records = l_default_tcp->m_nb_max_mcc_records;
+
+        /* Copy the mcc record data from dflt_tile_cp to the current tile*/
+        l_src_mcc_rec = l_default_tcp->m_mcc_records;
+        l_dest_mcc_rec = l_tcp->m_mcc_records;
+
+        for (j = 0; j < l_default_tcp->m_nb_max_mcc_records; ++j) {
+
+            if (l_src_mcc_rec->m_decorrelation_array) {
+                l_offset = (OPJ_UINT32)(l_src_mcc_rec->m_decorrelation_array -
+                                        l_default_tcp->m_mct_records);
+                l_dest_mcc_rec->m_decorrelation_array = l_tcp->m_mct_records + l_offset;
+            }
+
+            if (l_src_mcc_rec->m_offset_array) {
+                l_offset = (OPJ_UINT32)(l_src_mcc_rec->m_offset_array -
+                                        l_default_tcp->m_mct_records);
+                l_dest_mcc_rec->m_offset_array = l_tcp->m_mct_records + l_offset;
+            }
+
+            ++l_src_mcc_rec;
+            ++l_dest_mcc_rec;
+        }
+
+        /* Copy all the dflt_tile_compo_cp to the current tile cp */
+        memcpy(l_current_tccp, l_default_tcp->tccps, l_tccp_size);
+
+        /* Move to next tile cp*/
+        ++l_tcp;
+    }
+
+    /* Create the current tile decoder*/
+    p_j2k->m_tcd = opj_tcd_create(OPJ_TRUE);
+    if (! p_j2k->m_tcd) {
+        return OPJ_FALSE;
+    }
+
+    if (!opj_tcd_init(p_j2k->m_tcd, l_image, &(p_j2k->m_cp), p_j2k->m_tp)) {
+        opj_tcd_destroy(p_j2k->m_tcd);
+        p_j2k->m_tcd = 00;
+        opj_event_msg(p_manager, EVT_ERROR, "Cannot decode tile, memory error\n");
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static const opj_dec_memory_marker_handler_t * opj_j2k_get_marker_handler(
+    OPJ_UINT32 p_id)
+{
+    const opj_dec_memory_marker_handler_t *e;
+    for (e = j2k_memory_marker_handler_tab; e->id != 0; ++e) {
+        if (e->id == p_id) {
+            break; /* we find a handler corresponding to the marker ID*/
+        }
+    }
+    return e;
+}
+
+void opj_j2k_destroy(void *j2k)
+{
+    opj_j2k_t *p_j2k = (opj_j2k_t*)j2k;
+    if (p_j2k == 00) {
+        return;
+    }
+
+    if (p_j2k->m_is_decoder) {
+
+        if (p_j2k->m_specific_param.m_decoder.m_default_tcp != 00) {
+            opj_j2k_tcp_destroy(p_j2k->m_specific_param.m_decoder.m_default_tcp);
+            opj_free(p_j2k->m_specific_param.m_decoder.m_default_tcp);
+            p_j2k->m_specific_param.m_decoder.m_default_tcp = 00;
+        }
+
+        if (p_j2k->m_specific_param.m_decoder.m_header_data != 00) {
+            opj_free(p_j2k->m_specific_param.m_decoder.m_header_data);
+            p_j2k->m_specific_param.m_decoder.m_header_data = 00;
+            p_j2k->m_specific_param.m_decoder.m_header_data_size = 0;
+        }
+
+        opj_free(p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode);
+        p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode = 00;
+        p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode = 0;
+
+    } else {
+
+        if (p_j2k->m_specific_param.m_encoder.m_encoded_tile_data) {
+            opj_free(p_j2k->m_specific_param.m_encoder.m_encoded_tile_data);
+            p_j2k->m_specific_param.m_encoder.m_encoded_tile_data = 00;
+        }
+
+        if (p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer) {
+            opj_free(p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer);
+            p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer = 00;
+            p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current = 00;
+        }
+
+        if (p_j2k->m_specific_param.m_encoder.m_header_tile_data) {
+            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data = 00;
+            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
+        }
+    }
+
+    opj_tcd_destroy(p_j2k->m_tcd);
+
+    opj_j2k_cp_destroy(&(p_j2k->m_cp));
+    memset(&(p_j2k->m_cp), 0, sizeof(opj_cp_t));
+
+    opj_procedure_list_destroy(p_j2k->m_procedure_list);
+    p_j2k->m_procedure_list = 00;
+
+    opj_procedure_list_destroy(p_j2k->m_validation_list);
+    p_j2k->m_procedure_list = 00;
+
+    j2k_destroy_cstr_index(p_j2k->cstr_index);
+    p_j2k->cstr_index = NULL;
+
+    opj_image_destroy(p_j2k->m_private_image);
+    p_j2k->m_private_image = NULL;
+
+    opj_image_destroy(p_j2k->m_output_image);
+    p_j2k->m_output_image = NULL;
+
+    opj_thread_pool_destroy(p_j2k->m_tp);
+    p_j2k->m_tp = NULL;
+
+    opj_free(p_j2k);
+}
+
+void j2k_destroy_cstr_index(opj_codestream_index_t *p_cstr_ind)
+{
+    if (p_cstr_ind) {
+
+        if (p_cstr_ind->marker) {
+            opj_free(p_cstr_ind->marker);
+            p_cstr_ind->marker = NULL;
+        }
+
+        if (p_cstr_ind->tile_index) {
+            OPJ_UINT32 it_tile = 0;
+
+            for (it_tile = 0; it_tile < p_cstr_ind->nb_of_tiles; it_tile++) {
+
+                if (p_cstr_ind->tile_index[it_tile].packet_index) {
+                    opj_free(p_cstr_ind->tile_index[it_tile].packet_index);
+                    p_cstr_ind->tile_index[it_tile].packet_index = NULL;
+                }
+
+                if (p_cstr_ind->tile_index[it_tile].tp_index) {
+                    opj_free(p_cstr_ind->tile_index[it_tile].tp_index);
+                    p_cstr_ind->tile_index[it_tile].tp_index = NULL;
+                }
+
+                if (p_cstr_ind->tile_index[it_tile].marker) {
+                    opj_free(p_cstr_ind->tile_index[it_tile].marker);
+                    p_cstr_ind->tile_index[it_tile].marker = NULL;
+
+                }
+            }
+
+            opj_free(p_cstr_ind->tile_index);
+            p_cstr_ind->tile_index = NULL;
+        }
+
+        opj_free(p_cstr_ind);
+    }
+}
+
+static void opj_j2k_tcp_destroy(opj_tcp_t *p_tcp)
+{
+    if (p_tcp == 00) {
+        return;
+    }
+
+    if (p_tcp->ppt_markers != 00) {
+        OPJ_UINT32 i;
+        for (i = 0U; i < p_tcp->ppt_markers_count; ++i) {
+            if (p_tcp->ppt_markers[i].m_data != NULL) {
+                opj_free(p_tcp->ppt_markers[i].m_data);
+            }
+        }
+        p_tcp->ppt_markers_count = 0U;
+        opj_free(p_tcp->ppt_markers);
+        p_tcp->ppt_markers = NULL;
+    }
+
+    if (p_tcp->ppt_buffer != 00) {
+        opj_free(p_tcp->ppt_buffer);
+        p_tcp->ppt_buffer = 00;
+    }
+
+    if (p_tcp->tccps != 00) {
+        opj_free(p_tcp->tccps);
+        p_tcp->tccps = 00;
+    }
+
+    if (p_tcp->m_mct_coding_matrix != 00) {
+        opj_free(p_tcp->m_mct_coding_matrix);
+        p_tcp->m_mct_coding_matrix = 00;
+    }
+
+    if (p_tcp->m_mct_decoding_matrix != 00) {
+        opj_free(p_tcp->m_mct_decoding_matrix);
+        p_tcp->m_mct_decoding_matrix = 00;
+    }
+
+    if (p_tcp->m_mcc_records) {
+        opj_free(p_tcp->m_mcc_records);
+        p_tcp->m_mcc_records = 00;
+        p_tcp->m_nb_max_mcc_records = 0;
+        p_tcp->m_nb_mcc_records = 0;
+    }
+
+    if (p_tcp->m_mct_records) {
+        opj_mct_data_t * l_mct_data = p_tcp->m_mct_records;
+        OPJ_UINT32 i;
+
+        for (i = 0; i < p_tcp->m_nb_mct_records; ++i) {
+            if (l_mct_data->m_data) {
+                opj_free(l_mct_data->m_data);
+                l_mct_data->m_data = 00;
+            }
+
+            ++l_mct_data;
+        }
+
+        opj_free(p_tcp->m_mct_records);
+        p_tcp->m_mct_records = 00;
+    }
+
+    if (p_tcp->mct_norms != 00) {
+        opj_free(p_tcp->mct_norms);
+        p_tcp->mct_norms = 00;
+    }
+
+    opj_j2k_tcp_data_destroy(p_tcp);
+
+}
+
+static void opj_j2k_tcp_data_destroy(opj_tcp_t *p_tcp)
+{
+    if (p_tcp->m_data) {
+        opj_free(p_tcp->m_data);
+        p_tcp->m_data = NULL;
+        p_tcp->m_data_size = 0;
+    }
+}
+
+static void opj_j2k_cp_destroy(opj_cp_t *p_cp)
+{
+    OPJ_UINT32 l_nb_tiles;
+    opj_tcp_t * l_current_tile = 00;
+
+    if (p_cp == 00) {
+        return;
+    }
+    if (p_cp->tcps != 00) {
+        OPJ_UINT32 i;
+        l_current_tile = p_cp->tcps;
+        l_nb_tiles = p_cp->th * p_cp->tw;
+
+        for (i = 0U; i < l_nb_tiles; ++i) {
+            opj_j2k_tcp_destroy(l_current_tile);
+            ++l_current_tile;
+        }
+        opj_free(p_cp->tcps);
+        p_cp->tcps = 00;
+    }
+    if (p_cp->ppm_markers != 00) {
+        OPJ_UINT32 i;
+        for (i = 0U; i < p_cp->ppm_markers_count; ++i) {
+            if (p_cp->ppm_markers[i].m_data != NULL) {
+                opj_free(p_cp->ppm_markers[i].m_data);
+            }
+        }
+        p_cp->ppm_markers_count = 0U;
+        opj_free(p_cp->ppm_markers);
+        p_cp->ppm_markers = NULL;
+    }
+    opj_free(p_cp->ppm_buffer);
+    p_cp->ppm_buffer = 00;
+    p_cp->ppm_data =
+        NULL; /* ppm_data belongs to the allocated buffer pointed by ppm_buffer */
+    opj_free(p_cp->comment);
+    p_cp->comment = 00;
+    if (! p_cp->m_is_decoder) {
+        opj_free(p_cp->m_specific_param.m_enc.m_matrice);
+        p_cp->m_specific_param.m_enc.m_matrice = 00;
+    }
+}
+
+static OPJ_BOOL opj_j2k_need_nb_tile_parts_correction(opj_stream_private_t
+        *p_stream, OPJ_UINT32 tile_no, OPJ_BOOL* p_correction_needed,
+        opj_event_mgr_t * p_manager)
+{
+    OPJ_BYTE   l_header_data[10];
+    OPJ_OFF_T  l_stream_pos_backup;
+    OPJ_UINT32 l_current_marker;
+    OPJ_UINT32 l_marker_size;
+    OPJ_UINT32 l_tile_no, l_tot_len, l_current_part, l_num_parts;
+
+    /* initialize to no correction needed */
+    *p_correction_needed = OPJ_FALSE;
+
+    if (!opj_stream_has_seek(p_stream)) {
+        /* We can't do much in this case, seek is needed */
+        return OPJ_TRUE;
+    }
+
+    l_stream_pos_backup = opj_stream_tell(p_stream);
+    if (l_stream_pos_backup == -1) {
+        /* let's do nothing */
+        return OPJ_TRUE;
+    }
+
+    for (;;) {
+        /* Try to read 2 bytes (the next marker ID) from stream and copy them into the buffer */
+        if (opj_stream_read_data(p_stream, l_header_data, 2, p_manager) != 2) {
+            /* assume all is OK */
+            if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
+                return OPJ_FALSE;
+            }
+            return OPJ_TRUE;
+        }
+
+        /* Read 2 bytes from buffer as the new marker ID */
+        opj_read_bytes(l_header_data, &l_current_marker, 2);
+
+        if (l_current_marker != J2K_MS_SOT) {
+            /* assume all is OK */
+            if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
+                return OPJ_FALSE;
+            }
+            return OPJ_TRUE;
+        }
+
+        /* Try to read 2 bytes (the marker size) from stream and copy them into the buffer */
+        if (opj_stream_read_data(p_stream, l_header_data, 2, p_manager) != 2) {
+            opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
+            return OPJ_FALSE;
+        }
+
+        /* Read 2 bytes from the buffer as the marker size */
+        opj_read_bytes(l_header_data, &l_marker_size, 2);
+
+        /* Check marker size for SOT Marker */
+        if (l_marker_size != 10) {
+            opj_event_msg(p_manager, EVT_ERROR, "Inconsistent marker size\n");
+            return OPJ_FALSE;
+        }
+        l_marker_size -= 2;
+
+        if (opj_stream_read_data(p_stream, l_header_data, l_marker_size,
+                                 p_manager) != l_marker_size) {
+            opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
+            return OPJ_FALSE;
+        }
+
+        if (! opj_j2k_get_sot_values(l_header_data, l_marker_size, &l_tile_no,
+                                     &l_tot_len, &l_current_part, &l_num_parts, p_manager)) {
+            return OPJ_FALSE;
+        }
+
+        if (l_tile_no == tile_no) {
+            /* we found what we were looking for */
+            break;
+        }
+
+        if (l_tot_len < 14U) {
+            /* last SOT until EOC or invalid Psot value */
+            /* assume all is OK */
+            if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
+                return OPJ_FALSE;
+            }
+            return OPJ_TRUE;
+        }
+        l_tot_len -= 12U;
+        /* look for next SOT marker */
+        if (opj_stream_skip(p_stream, (OPJ_OFF_T)(l_tot_len),
+                            p_manager) != (OPJ_OFF_T)(l_tot_len)) {
+            /* assume all is OK */
+            if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
+                return OPJ_FALSE;
+            }
+            return OPJ_TRUE;
+        }
+    }
+
+    /* check for correction */
+    if (l_current_part == l_num_parts) {
+        *p_correction_needed = OPJ_TRUE;
+    }
+
+    if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
+        return OPJ_FALSE;
+    }
+    return OPJ_TRUE;
+}
+
+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,
+                                  OPJ_INT32 * p_tile_x1, OPJ_INT32 * p_tile_y1,
+                                  OPJ_UINT32 * p_nb_comps,
+                                  OPJ_BOOL * p_go_on,
+                                  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;
+    opj_tcp_t * l_tcp = NULL;
+    const OPJ_UINT32 l_nb_tiles = p_j2k->m_cp.tw * p_j2k->m_cp.th;
+
+    /* preconditions */
+    assert(p_stream != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    /* Reach the End Of Codestream ?*/
+    if (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_EOC) {
+        l_current_marker = J2K_MS_EOC;
+    }
+    /* We need to encounter a SOT marker (a new tile-part header) */
+    else if (p_j2k->m_specific_param.m_decoder.m_state != J2K_STATE_TPHSOT) {
+        return OPJ_FALSE;
+    }
+
+    /* Read into the codestream until reach the EOC or ! can_decode ??? FIXME */
+    while ((!p_j2k->m_specific_param.m_decoder.m_can_decode) &&
+            (l_current_marker != J2K_MS_EOC)) {
+
+        /* Try to read until the Start Of Data is detected */
+        while (l_current_marker != J2K_MS_SOD) {
+
+            if (opj_stream_get_number_byte_left(p_stream) == 0) {
+                p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_NEOC;
+                break;
+            }
+
+            /* Try to read 2 bytes (the marker size) from stream and copy them into the buffer */
+            if (opj_stream_read_data(p_stream,
+                                     p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
+                opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
+                return OPJ_FALSE;
+            }
+
+            /* Read 2 bytes from the buffer as the marker size */
+            opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data, &l_marker_size,
+                           2);
+
+            /* Check marker size (does not include marker ID but includes marker size) */
+            if (l_marker_size < 2) {
+                opj_event_msg(p_manager, EVT_ERROR, "Inconsistent marker size\n");
+                return OPJ_FALSE;
+            }
+
+            /* cf. https://code.google.com/p/openjpeg/issues/detail?id=226 */
+            if (l_current_marker == 0x8080 &&
+                    opj_stream_get_number_byte_left(p_stream) == 0) {
+                p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_NEOC;
+                break;
+            }
+
+            /* Why this condition? FIXME */
+            if (p_j2k->m_specific_param.m_decoder.m_state & J2K_STATE_TPH) {
+                p_j2k->m_specific_param.m_decoder.m_sot_length -= (l_marker_size + 2);
+            }
+            l_marker_size -= 2; /* Subtract the size of the marker ID already read */
+
+            /* Get the marker handler from the marker ID */
+            l_marker_handler = opj_j2k_get_marker_handler(l_current_marker);
+
+            /* Check if the marker is known and if it is the right place to find it */
+            if (!(p_j2k->m_specific_param.m_decoder.m_state & l_marker_handler->states)) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Marker is not compliant with its position\n");
+                return OPJ_FALSE;
+            }
+            /* FIXME manage case of unknown marker as in the main header ? */
+
+            /* Check if the marker size is compatible with the header data size */
+            if (l_marker_size > p_j2k->m_specific_param.m_decoder.m_header_data_size) {
+                OPJ_BYTE *new_header_data = NULL;
+                /* If we are here, this means we consider this marker as known & we will read it */
+                /* Check enough bytes left in stream before allocation */
+                if ((OPJ_OFF_T)l_marker_size >  opj_stream_get_number_byte_left(p_stream)) {
+                    opj_event_msg(p_manager, EVT_ERROR,
+                                  "Marker size inconsistent with stream length\n");
+                    return OPJ_FALSE;
+                }
+                new_header_data = (OPJ_BYTE *) opj_realloc(
+                                      p_j2k->m_specific_param.m_decoder.m_header_data, l_marker_size);
+                if (! new_header_data) {
+                    opj_free(p_j2k->m_specific_param.m_decoder.m_header_data);
+                    p_j2k->m_specific_param.m_decoder.m_header_data = NULL;
+                    p_j2k->m_specific_param.m_decoder.m_header_data_size = 0;
+                    opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read header\n");
+                    return OPJ_FALSE;
+                }
+                p_j2k->m_specific_param.m_decoder.m_header_data = new_header_data;
+                p_j2k->m_specific_param.m_decoder.m_header_data_size = l_marker_size;
+            }
+
+            /* Try to read the rest of the marker segment from stream and copy them into the buffer */
+            if (opj_stream_read_data(p_stream,
+                                     p_j2k->m_specific_param.m_decoder.m_header_data, l_marker_size,
+                                     p_manager) != l_marker_size) {
+                opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
+                return OPJ_FALSE;
+            }
+
+            if (!l_marker_handler->handler) {
+                /* See issue #175 */
+                opj_event_msg(p_manager, EVT_ERROR, "Not sure how that happened.\n");
+                return OPJ_FALSE;
+            }
+            /* Read the marker segment with the correct marker handler */
+            if (!(*(l_marker_handler->handler))(p_j2k,
+                                                p_j2k->m_specific_param.m_decoder.m_header_data, l_marker_size, p_manager)) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Fail to read the current marker segment (%#x)\n", l_current_marker);
+                return OPJ_FALSE;
+            }
+
+            /* Add the marker to the codestream index*/
+            if (OPJ_FALSE == opj_j2k_add_tlmarker(p_j2k->m_current_tile_number,
+                                                  p_j2k->cstr_index,
+                                                  l_marker_handler->id,
+                                                  (OPJ_UINT32) opj_stream_tell(p_stream) - l_marker_size - 4,
+                                                  l_marker_size + 4)) {
+                opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to add tl marker\n");
+                return OPJ_FALSE;
+            }
+
+            /* Keep the position of the last SOT marker read */
+            if (l_marker_handler->id == J2K_MS_SOT) {
+                OPJ_UINT32 sot_pos = (OPJ_UINT32) opj_stream_tell(p_stream) - l_marker_size - 4
+                                     ;
+                if (sot_pos > p_j2k->m_specific_param.m_decoder.m_last_sot_read_pos) {
+                    p_j2k->m_specific_param.m_decoder.m_last_sot_read_pos = sot_pos;
+                }
+            }
+
+            if (p_j2k->m_specific_param.m_decoder.m_skip_data) {
+                /* Skip the rest of the tile part header*/
+                if (opj_stream_skip(p_stream, p_j2k->m_specific_param.m_decoder.m_sot_length,
+                                    p_manager) != p_j2k->m_specific_param.m_decoder.m_sot_length) {
+                    opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
+                    return OPJ_FALSE;
+                }
+                l_current_marker = J2K_MS_SOD; /* Normally we reached a SOD */
+            } else {
+                /* Try to read 2 bytes (the next marker ID) from stream and copy them into the buffer*/
+                if (opj_stream_read_data(p_stream,
+                                         p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
+                    opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
+                    return OPJ_FALSE;
+                }
+                /* Read 2 bytes from the buffer as the new marker ID */
+                opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data,
+                               &l_current_marker, 2);
+            }
+        }
+        if (opj_stream_get_number_byte_left(p_stream) == 0
+                && p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_NEOC) {
+            break;
+        }
+
+        /* If we didn't skip data before, we need to read the SOD marker*/
+        if (! p_j2k->m_specific_param.m_decoder.m_skip_data) {
+            /* Try to read the SOD marker and skip data ? FIXME */
+            if (! opj_j2k_read_sod(p_j2k, p_stream, p_manager)) {
+                return OPJ_FALSE;
+            }
+            if (p_j2k->m_specific_param.m_decoder.m_can_decode &&
+                    !p_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction_checked) {
+                /* Issue 254 */
+                OPJ_BOOL l_correction_needed;
+
+                p_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction_checked = 1;
+                if (!opj_j2k_need_nb_tile_parts_correction(p_stream,
+                        p_j2k->m_current_tile_number, &l_correction_needed, p_manager)) {
+                    opj_event_msg(p_manager, EVT_ERROR,
+                                  "opj_j2k_apply_nb_tile_parts_correction error\n");
+                    return OPJ_FALSE;
+                }
+                if (l_correction_needed) {
+                    OPJ_UINT32 l_tile_no;
+
+                    p_j2k->m_specific_param.m_decoder.m_can_decode = 0;
+                    p_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction = 1;
+                    /* correct tiles */
+                    for (l_tile_no = 0U; l_tile_no < l_nb_tiles; ++l_tile_no) {
+                        if (p_j2k->m_cp.tcps[l_tile_no].m_nb_tile_parts != 0U) {
+                            p_j2k->m_cp.tcps[l_tile_no].m_nb_tile_parts += 1;
+                        }
+                    }
+                    opj_event_msg(p_manager, EVT_WARNING,
+                                  "Non conformant codestream TPsot==TNsot.\n");
+                }
+            }
+        } else {
+            /* Indicate we will try to read a new tile-part header*/
+            p_j2k->m_specific_param.m_decoder.m_skip_data = 0;
+            p_j2k->m_specific_param.m_decoder.m_can_decode = 0;
+            p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_TPHSOT;
+        }
+
+        if (! p_j2k->m_specific_param.m_decoder.m_can_decode) {
+            /* Try to read 2 bytes (the next marker ID) from stream and copy them into the buffer */
+            if (opj_stream_read_data(p_stream,
+                                     p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
+
+                /* Deal with likely non conformant SPOT6 files, where the last */
+                /* row of tiles have TPsot == 0 and TNsot == 0, and missing EOC, */
+                /* but no other tile-parts were found. */
+                if (p_j2k->m_current_tile_number + 1 == l_nb_tiles) {
+                    OPJ_UINT32 l_tile_no;
+                    for (l_tile_no = 0U; l_tile_no < l_nb_tiles; ++l_tile_no) {
+                        if (p_j2k->m_cp.tcps[l_tile_no].m_current_tile_part_number == 0 &&
+                                p_j2k->m_cp.tcps[l_tile_no].m_nb_tile_parts == 0) {
+                            break;
+                        }
+                    }
+                    if (l_tile_no < l_nb_tiles) {
+                        opj_event_msg(p_manager, EVT_INFO,
+                                      "Tile %u has TPsot == 0 and TNsot == 0, "
+                                      "but no other tile-parts were found. "
+                                      "EOC is also missing.\n",
+                                      l_tile_no);
+                        p_j2k->m_current_tile_number = l_tile_no;
+                        l_current_marker = J2K_MS_EOC;
+                        p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_EOC;
+                        break;
+                    }
+                }
+
+                opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
+                return OPJ_FALSE;
+            }
+
+            /* Read 2 bytes from buffer as the new marker ID */
+            opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data,
+                           &l_current_marker, 2);
+        }
+    }
+
+    /* Current marker is the EOC marker ?*/
+    if (l_current_marker == J2K_MS_EOC) {
+        if (p_j2k->m_specific_param.m_decoder.m_state != J2K_STATE_EOC) {
+            p_j2k->m_current_tile_number = 0;
+            p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_EOC;
+        }
+    }
+
+    /* Deal with tiles that have a single tile-part with TPsot == 0 and TNsot == 0 */
+    if (! p_j2k->m_specific_param.m_decoder.m_can_decode) {
+        l_tcp = p_j2k->m_cp.tcps + p_j2k->m_current_tile_number;
+
+        while ((p_j2k->m_current_tile_number < l_nb_tiles) && (l_tcp->m_data == 00)) {
+            ++p_j2k->m_current_tile_number;
+            ++l_tcp;
+        }
+
+        if (p_j2k->m_current_tile_number == l_nb_tiles) {
+            *p_go_on = OPJ_FALSE;
+            return OPJ_TRUE;
+        }
+    }
+
+    if (! opj_j2k_merge_ppt(p_j2k->m_cp.tcps + p_j2k->m_current_tile_number,
+                            p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Failed to merge PPT data\n");
+        return OPJ_FALSE;
+    }
+    /*FIXME ???*/
+    if (! opj_tcd_init_decode_tile(p_j2k->m_tcd, p_j2k->m_current_tile_number,
+                                   p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Cannot decode tile, memory error\n");
+        return OPJ_FALSE;
+    }
+
+    opj_event_msg(p_manager, EVT_INFO, "Header of tile %d / %d has been read.\n",
+                  p_j2k->m_current_tile_number + 1, (p_j2k->m_cp.th * p_j2k->m_cp.tw));
+
+    *p_tile_index = p_j2k->m_current_tile_number;
+    *p_go_on = OPJ_TRUE;
+    if (p_data_size) {
+        /* For internal use in j2k.c, we don't need this */
+        /* This is just needed for folks using the opj_read_tile_header() / opj_decode_tile_data() combo */
+        *p_data_size = opj_tcd_get_decoded_tile_size(p_j2k->m_tcd, OPJ_FALSE);
+        if (*p_data_size == UINT_MAX) {
+            return OPJ_FALSE;
+        }
+    }
+    *p_tile_x0 = p_j2k->m_tcd->tcd_image->tiles->x0;
+    *p_tile_y0 = p_j2k->m_tcd->tcd_image->tiles->y0;
+    *p_tile_x1 = p_j2k->m_tcd->tcd_image->tiles->x1;
+    *p_tile_y1 = p_j2k->m_tcd->tcd_image->tiles->y1;
+    *p_nb_comps = p_j2k->m_tcd->tcd_image->tiles->numcomps;
+
+    p_j2k->m_specific_param.m_decoder.m_state |= J2K_STATE_DATA;
+
+    return OPJ_TRUE;
+}
+
+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;
+    opj_image_t* l_image_for_bounds;
+
+    /* preconditions */
+    assert(p_stream != 00);
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    if (!(p_j2k->m_specific_param.m_decoder.m_state & J2K_STATE_DATA)
+            || (p_tile_index != p_j2k->m_current_tile_number)) {
+        return OPJ_FALSE;
+    }
+
+    l_tcp = &(p_j2k->m_cp.tcps[p_tile_index]);
+    if (! l_tcp->m_data) {
+        opj_j2k_tcp_destroy(l_tcp);
+        return OPJ_FALSE;
+    }
+
+    /* When using the opj_read_tile_header / opj_decode_tile_data API */
+    /* such as in test_tile_decoder, m_output_image is NULL, so fall back */
+    /* to the full image dimension. This is a bit surprising that */
+    /* opj_set_decode_area() is only used to determine intersecting tiles, */
+    /* but full tile decoding is done */
+    l_image_for_bounds = p_j2k->m_output_image ? p_j2k->m_output_image :
+                         p_j2k->m_private_image;
+    if (! opj_tcd_decode_tile(p_j2k->m_tcd,
+                              l_image_for_bounds->x0,
+                              l_image_for_bounds->y0,
+                              l_image_for_bounds->x1,
+                              l_image_for_bounds->y1,
+                              p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode,
+                              p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode,
+                              l_tcp->m_data,
+                              l_tcp->m_data_size,
+                              p_tile_index,
+                              p_j2k->cstr_index, p_manager)) {
+        opj_j2k_tcp_destroy(l_tcp);
+        p_j2k->m_specific_param.m_decoder.m_state |= J2K_STATE_ERR;
+        opj_event_msg(p_manager, EVT_ERROR, "Failed to decode.\n");
+        return OPJ_FALSE;
+    }
+
+    /* p_data can be set to NULL when the call will take care of using */
+    /* itself the TCD data. This is typically the case for whole single */
+    /* tile decoding optimization. */
+    if (p_data != NULL) {
+        if (! opj_tcd_update_tile_data(p_j2k->m_tcd, p_data, p_data_size)) {
+            return OPJ_FALSE;
+        }
+
+        /* To avoid to destroy the tcp which can be useful when we try to decode a tile decoded before (cf j2k_random_tile_access)
+        * we destroy just the data which will be re-read in read_tile_header*/
+        /*opj_j2k_tcp_destroy(l_tcp);
+        p_j2k->m_tcd->tcp = 0;*/
+        opj_j2k_tcp_data_destroy(l_tcp);
+    }
+
+    p_j2k->m_specific_param.m_decoder.m_can_decode = 0;
+    p_j2k->m_specific_param.m_decoder.m_state &= (~(OPJ_UINT32)J2K_STATE_DATA);
+
+    if (opj_stream_get_number_byte_left(p_stream) == 0
+            && p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_NEOC) {
+        return OPJ_TRUE;
+    }
+
+    if (p_j2k->m_specific_param.m_decoder.m_state != J2K_STATE_EOC) {
+        if (opj_stream_read_data(p_stream, l_data, 2, p_manager) != 2) {
+            opj_event_msg(p_manager, p_j2k->m_cp.strict ? EVT_ERROR : EVT_WARNING,
+                          "Stream too short\n");
+            return p_j2k->m_cp.strict ? OPJ_FALSE : OPJ_TRUE;
+        }
+        opj_read_bytes(l_data, &l_current_marker, 2);
+
+        if (l_current_marker == J2K_MS_EOC) {
+            p_j2k->m_current_tile_number = 0;
+            p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_EOC;
+        } else if (l_current_marker != J2K_MS_SOT) {
+            if (opj_stream_get_number_byte_left(p_stream) == 0) {
+                p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_NEOC;
+                opj_event_msg(p_manager, EVT_WARNING, "Stream does not end with EOC\n");
+                return OPJ_TRUE;
+            }
+            opj_event_msg(p_manager, EVT_ERROR, "Stream too short, expected SOT\n");
+            return OPJ_FALSE;
+        }
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_update_image_data(opj_tcd_t * p_tcd,
+        opj_image_t* p_output_image)
+{
+    OPJ_UINT32 i, j;
+    OPJ_UINT32 l_width_src, l_height_src;
+    OPJ_UINT32 l_width_dest, l_height_dest;
+    OPJ_INT32 l_offset_x0_src, l_offset_y0_src, l_offset_x1_src, l_offset_y1_src;
+    OPJ_SIZE_T l_start_offset_src;
+    OPJ_UINT32 l_start_x_dest, l_start_y_dest;
+    OPJ_UINT32 l_x0_dest, l_y0_dest, l_x1_dest, l_y1_dest;
+    OPJ_SIZE_T l_start_offset_dest;
+
+    opj_image_comp_t * l_img_comp_src = 00;
+    opj_image_comp_t * l_img_comp_dest = 00;
+
+    opj_tcd_tilecomp_t * l_tilec = 00;
+    opj_image_t * l_image_src = 00;
+    OPJ_INT32 * l_dest_ptr;
+
+    l_tilec = p_tcd->tcd_image->tiles->comps;
+    l_image_src = p_tcd->image;
+    l_img_comp_src = l_image_src->comps;
+
+    l_img_comp_dest = p_output_image->comps;
+
+    for (i = 0; i < l_image_src->numcomps;
+            i++, ++l_img_comp_dest, ++l_img_comp_src,  ++l_tilec) {
+        OPJ_INT32 res_x0, res_x1, res_y0, res_y1;
+        OPJ_UINT32 src_data_stride;
+        const OPJ_INT32* p_src_data;
+
+        /* Copy info from decoded comp image to output image */
+        l_img_comp_dest->resno_decoded = l_img_comp_src->resno_decoded;
+
+        if (p_tcd->whole_tile_decoding) {
+            opj_tcd_resolution_t* l_res = l_tilec->resolutions +
+                                          l_img_comp_src->resno_decoded;
+            res_x0 = l_res->x0;
+            res_y0 = l_res->y0;
+            res_x1 = l_res->x1;
+            res_y1 = l_res->y1;
+            src_data_stride = (OPJ_UINT32)(
+                                  l_tilec->resolutions[l_tilec->minimum_num_resolutions - 1].x1 -
+                                  l_tilec->resolutions[l_tilec->minimum_num_resolutions - 1].x0);
+            p_src_data = l_tilec->data;
+        } else {
+            opj_tcd_resolution_t* l_res = l_tilec->resolutions +
+                                          l_img_comp_src->resno_decoded;
+            res_x0 = (OPJ_INT32)l_res->win_x0;
+            res_y0 = (OPJ_INT32)l_res->win_y0;
+            res_x1 = (OPJ_INT32)l_res->win_x1;
+            res_y1 = (OPJ_INT32)l_res->win_y1;
+            src_data_stride = l_res->win_x1 - l_res->win_x0;
+            p_src_data = l_tilec->data_win;
+        }
+
+        if (p_src_data == NULL) {
+            /* Happens for partial component decoding */
+            continue;
+        }
+
+        l_width_src = (OPJ_UINT32)(res_x1 - res_x0);
+        l_height_src = (OPJ_UINT32)(res_y1 - res_y0);
+
+
+        /* Current tile component size*/
+        /*if (i == 0) {
+        fprintf(stdout, "SRC: l_res_x0=%d, l_res_x1=%d, l_res_y0=%d, l_res_y1=%d\n",
+                        res_x0, res_x1, res_y0, res_y1);
+        }*/
+
+
+        /* Border of the current output component*/
+        l_x0_dest = opj_uint_ceildivpow2(l_img_comp_dest->x0, l_img_comp_dest->factor);
+        l_y0_dest = opj_uint_ceildivpow2(l_img_comp_dest->y0, l_img_comp_dest->factor);
+        l_x1_dest = l_x0_dest +
+                    l_img_comp_dest->w; /* can't overflow given that image->x1 is uint32 */
+        l_y1_dest = l_y0_dest + l_img_comp_dest->h;
+
+        /*if (i == 0) {
+        fprintf(stdout, "DEST: l_x0_dest=%d, l_x1_dest=%d, l_y0_dest=%d, l_y1_dest=%d (%d)\n",
+                        l_x0_dest, l_x1_dest, l_y0_dest, l_y1_dest, l_img_comp_dest->factor );
+        }*/
+
+        /*-----*/
+        /* Compute the area (l_offset_x0_src, l_offset_y0_src, l_offset_x1_src, l_offset_y1_src)
+         * of the input buffer (decoded tile component) which will be move
+         * in the output buffer. Compute the area of the output buffer (l_start_x_dest,
+         * l_start_y_dest, l_width_dest, l_height_dest)  which will be modified
+         * by this input area.
+         * */
+        assert(res_x0 >= 0);
+        assert(res_x1 >= 0);
+
+        /* Prevent bad casting to unsigned values in the subsequent lines. */
+        if ( res_x0 < 0 || res_x1 < 0 || res_y0 < 0 || res_y1 < 0 ) {
+            return OPJ_FALSE;
+        }
+
+        if (l_x0_dest < (OPJ_UINT32)res_x0) {
+            l_start_x_dest = (OPJ_UINT32)res_x0 - l_x0_dest;
+            l_offset_x0_src = 0;
+
+            if (l_x1_dest >= (OPJ_UINT32)res_x1) {
+                l_width_dest = l_width_src;
+                l_offset_x1_src = 0;
+            } else {
+                l_width_dest = l_x1_dest - (OPJ_UINT32)res_x0 ;
+                l_offset_x1_src = (OPJ_INT32)(l_width_src - l_width_dest);
+            }
+        } else {
+            l_start_x_dest = 0U;
+            l_offset_x0_src = (OPJ_INT32)l_x0_dest - res_x0;
+
+            if (l_x1_dest >= (OPJ_UINT32)res_x1) {
+                l_width_dest = l_width_src - (OPJ_UINT32)l_offset_x0_src;
+                l_offset_x1_src = 0;
+            } else {
+                l_width_dest = l_img_comp_dest->w ;
+                l_offset_x1_src = res_x1 - (OPJ_INT32)l_x1_dest;
+            }
+        }
+
+        if (l_y0_dest < (OPJ_UINT32)res_y0) {
+            l_start_y_dest = (OPJ_UINT32)res_y0 - l_y0_dest;
+            l_offset_y0_src = 0;
+
+            if (l_y1_dest >= (OPJ_UINT32)res_y1) {
+                l_height_dest = l_height_src;
+                l_offset_y1_src = 0;
+            } else {
+                l_height_dest = l_y1_dest - (OPJ_UINT32)res_y0 ;
+                l_offset_y1_src = (OPJ_INT32)(l_height_src - l_height_dest);
+            }
+        } else {
+            l_start_y_dest = 0U;
+            l_offset_y0_src = (OPJ_INT32)l_y0_dest - res_y0;
+
+            if (l_y1_dest >= (OPJ_UINT32)res_y1) {
+                l_height_dest = l_height_src - (OPJ_UINT32)l_offset_y0_src;
+                l_offset_y1_src = 0;
+            } else {
+                l_height_dest = l_img_comp_dest->h ;
+                l_offset_y1_src = res_y1 - (OPJ_INT32)l_y1_dest;
+            }
+        }
+
+        if ((l_offset_x0_src < 0) || (l_offset_y0_src < 0) || (l_offset_x1_src < 0) ||
+                (l_offset_y1_src < 0)) {
+            return OPJ_FALSE;
+        }
+        /* testcase 2977.pdf.asan.67.2198 */
+        if ((OPJ_INT32)l_width_dest < 0 || (OPJ_INT32)l_height_dest < 0) {
+            return OPJ_FALSE;
+        }
+        /*-----*/
+
+        /* Compute the input buffer offset */
+        l_start_offset_src = (OPJ_SIZE_T)l_offset_x0_src + (OPJ_SIZE_T)l_offset_y0_src
+                             * (OPJ_SIZE_T)src_data_stride;
+
+        /* Compute the output buffer offset */
+        l_start_offset_dest = (OPJ_SIZE_T)l_start_x_dest + (OPJ_SIZE_T)l_start_y_dest
+                              * (OPJ_SIZE_T)l_img_comp_dest->w;
+
+        /* Allocate output component buffer if necessary */
+        if (l_img_comp_dest->data == NULL &&
+                l_start_offset_src == 0 && l_start_offset_dest == 0 &&
+                src_data_stride == l_img_comp_dest->w &&
+                l_width_dest == l_img_comp_dest->w &&
+                l_height_dest == l_img_comp_dest->h) {
+            /* If the final image matches the tile buffer, then borrow it */
+            /* directly to save a copy */
+            if (p_tcd->whole_tile_decoding) {
+                l_img_comp_dest->data = l_tilec->data;
+                l_tilec->data = NULL;
+            } else {
+                l_img_comp_dest->data = l_tilec->data_win;
+                l_tilec->data_win = NULL;
+            }
+            continue;
+        } else if (l_img_comp_dest->data == NULL) {
+            OPJ_SIZE_T l_width = l_img_comp_dest->w;
+            OPJ_SIZE_T l_height = l_img_comp_dest->h;
+
+            if ((l_height == 0U) || (l_width > (SIZE_MAX / l_height)) ||
+                    l_width * l_height > SIZE_MAX / sizeof(OPJ_INT32)) {
+                /* would overflow */
+                return OPJ_FALSE;
+            }
+            l_img_comp_dest->data = (OPJ_INT32*) opj_image_data_alloc(l_width * l_height *
+                                    sizeof(OPJ_INT32));
+            if (! l_img_comp_dest->data) {
+                return OPJ_FALSE;
+            }
+
+            if (l_img_comp_dest->w != l_width_dest ||
+                    l_img_comp_dest->h != l_height_dest) {
+                memset(l_img_comp_dest->data, 0,
+                       (OPJ_SIZE_T)l_img_comp_dest->w * l_img_comp_dest->h * sizeof(OPJ_INT32));
+            }
+        }
+
+        /* Move the output buffer to the first place where we will write*/
+        l_dest_ptr = l_img_comp_dest->data + l_start_offset_dest;
+
+        {
+            const OPJ_INT32 * l_src_ptr = p_src_data;
+            l_src_ptr += l_start_offset_src;
+
+            for (j = 0; j < l_height_dest; ++j) {
+                memcpy(l_dest_ptr, l_src_ptr, l_width_dest * sizeof(OPJ_INT32));
+                l_dest_ptr += l_img_comp_dest->w;
+                l_src_ptr += src_data_stride;
+            }
+        }
+
+
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_update_image_dimensions(opj_image_t* p_image,
+        opj_event_mgr_t * p_manager)
+{
+    OPJ_UINT32 it_comp;
+    OPJ_INT32 l_comp_x1, l_comp_y1;
+    opj_image_comp_t* l_img_comp = NULL;
+
+    l_img_comp = p_image->comps;
+    for (it_comp = 0; it_comp < p_image->numcomps; ++it_comp) {
+        OPJ_INT32 l_h, l_w;
+        if (p_image->x0 > (OPJ_UINT32)INT_MAX ||
+                p_image->y0 > (OPJ_UINT32)INT_MAX ||
+                p_image->x1 > (OPJ_UINT32)INT_MAX ||
+                p_image->y1 > (OPJ_UINT32)INT_MAX) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Image coordinates above INT_MAX are not supported\n");
+            return OPJ_FALSE;
+        }
+
+        l_img_comp->x0 = opj_uint_ceildiv(p_image->x0, l_img_comp->dx);
+        l_img_comp->y0 = opj_uint_ceildiv(p_image->y0, l_img_comp->dy);
+        l_comp_x1 = opj_int_ceildiv((OPJ_INT32)p_image->x1, (OPJ_INT32)l_img_comp->dx);
+        l_comp_y1 = opj_int_ceildiv((OPJ_INT32)p_image->y1, (OPJ_INT32)l_img_comp->dy);
+
+        l_w = opj_int_ceildivpow2(l_comp_x1, (OPJ_INT32)l_img_comp->factor)
+              - opj_int_ceildivpow2((OPJ_INT32)l_img_comp->x0, (OPJ_INT32)l_img_comp->factor);
+        if (l_w < 0) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Size x of the decoded component image is incorrect (comp[%d].w=%d).\n",
+                          it_comp, l_w);
+            return OPJ_FALSE;
+        }
+        l_img_comp->w = (OPJ_UINT32)l_w;
+
+        l_h = opj_int_ceildivpow2(l_comp_y1, (OPJ_INT32)l_img_comp->factor)
+              - opj_int_ceildivpow2((OPJ_INT32)l_img_comp->y0, (OPJ_INT32)l_img_comp->factor);
+        if (l_h < 0) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Size y of the decoded component image is incorrect (comp[%d].h=%d).\n",
+                          it_comp, l_h);
+            return OPJ_FALSE;
+        }
+        l_img_comp->h = (OPJ_UINT32)l_h;
+
+        l_img_comp++;
+    }
+
+    return OPJ_TRUE;
+}
+
+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;
+
+    if (p_j2k->m_private_image == NULL) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "opj_read_header() should be called before "
+                      "opj_set_decoded_components().\n");
+        return OPJ_FALSE;
+    }
+
+    already_mapped = (OPJ_BOOL*) opj_calloc(sizeof(OPJ_BOOL),
+                                            p_j2k->m_private_image->numcomps);
+    if (already_mapped == NULL) {
+        return OPJ_FALSE;
+    }
+
+    for (i = 0; i < numcomps; i++) {
+        if (comps_indices[i] >= p_j2k->m_private_image->numcomps) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Invalid component index: %u\n",
+                          comps_indices[i]);
+            opj_free(already_mapped);
+            return OPJ_FALSE;
+        }
+        if (already_mapped[comps_indices[i]]) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Component index %u used several times\n",
+                          comps_indices[i]);
+            opj_free(already_mapped);
+            return OPJ_FALSE;
+        }
+        already_mapped[comps_indices[i]] = OPJ_TRUE;
+    }
+    opj_free(already_mapped);
+
+    opj_free(p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode);
+    if (numcomps) {
+        p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode =
+            (OPJ_UINT32*) opj_malloc(numcomps * sizeof(OPJ_UINT32));
+        if (p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode == NULL) {
+            p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode = 0;
+            return OPJ_FALSE;
+        }
+        memcpy(p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode,
+               comps_indices,
+               numcomps * sizeof(OPJ_UINT32));
+    } else {
+        p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode = NULL;
+    }
+    p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode = numcomps;
+
+    return OPJ_TRUE;
+}
+
+
+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;
+    OPJ_UINT32 it_comp;
+
+    if (p_j2k->m_cp.tw == 1 && p_j2k->m_cp.th == 1 &&
+            p_j2k->m_cp.tcps[0].m_data != NULL) {
+        /* In the case of a single-tiled image whose codestream we have already */
+        /* ingested, go on */
+    }
+    /* Check if we are read the main header */
+    else if (p_j2k->m_specific_param.m_decoder.m_state != J2K_STATE_TPHSOT) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Need to decode the main header before begin to decode the remaining codestream.\n");
+        return OPJ_FALSE;
+    }
+
+    /* Update the comps[].factor member of the output image with the one */
+    /* of m_reduce */
+    for (it_comp = 0; it_comp < p_image->numcomps; ++it_comp) {
+        p_image->comps[it_comp].factor = p_j2k->m_cp.m_specific_param.m_dec.m_reduce;
+    }
+
+    if (!p_start_x && !p_start_y && !p_end_x && !p_end_y) {
+        opj_event_msg(p_manager, EVT_INFO,
+                      "No decoded area parameters, set the decoded area to the whole image\n");
+
+        p_j2k->m_specific_param.m_decoder.m_start_tile_x = 0;
+        p_j2k->m_specific_param.m_decoder.m_start_tile_y = 0;
+        p_j2k->m_specific_param.m_decoder.m_end_tile_x = l_cp->tw;
+        p_j2k->m_specific_param.m_decoder.m_end_tile_y = l_cp->th;
+
+        p_image->x0 = l_image->x0;
+        p_image->y0 = l_image->y0;
+        p_image->x1 = l_image->x1;
+        p_image->y1 = l_image->y1;
+
+        return opj_j2k_update_image_dimensions(p_image, p_manager);
+    }
+
+    /* ----- */
+    /* Check if the positions provided by the user are correct */
+
+    /* Left */
+    if (p_start_x < 0) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Left position of the decoded area (region_x0=%d) should be >= 0.\n",
+                      p_start_x);
+        return OPJ_FALSE;
+    } else if ((OPJ_UINT32)p_start_x > l_image->x1) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Left position of the decoded area (region_x0=%d) is outside the image area (Xsiz=%d).\n",
+                      p_start_x, l_image->x1);
+        return OPJ_FALSE;
+    } else if ((OPJ_UINT32)p_start_x < l_image->x0) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "Left position of the decoded area (region_x0=%d) is outside the image area (XOsiz=%d).\n",
+                      p_start_x, l_image->x0);
+        p_j2k->m_specific_param.m_decoder.m_start_tile_x = 0;
+        p_image->x0 = l_image->x0;
+    } else {
+        p_j2k->m_specific_param.m_decoder.m_start_tile_x = ((OPJ_UINT32)p_start_x -
+                l_cp->tx0) / l_cp->tdx;
+        p_image->x0 = (OPJ_UINT32)p_start_x;
+    }
+
+    /* Up */
+    if (p_start_y < 0) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Up position of the decoded area (region_y0=%d) should be >= 0.\n",
+                      p_start_y);
+        return OPJ_FALSE;
+    } else if ((OPJ_UINT32)p_start_y > l_image->y1) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Up position of the decoded area (region_y0=%d) is outside the image area (Ysiz=%d).\n",
+                      p_start_y, l_image->y1);
+        return OPJ_FALSE;
+    } else if ((OPJ_UINT32)p_start_y < l_image->y0) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "Up position of the decoded area (region_y0=%d) is outside the image area (YOsiz=%d).\n",
+                      p_start_y, l_image->y0);
+        p_j2k->m_specific_param.m_decoder.m_start_tile_y = 0;
+        p_image->y0 = l_image->y0;
+    } else {
+        p_j2k->m_specific_param.m_decoder.m_start_tile_y = ((OPJ_UINT32)p_start_y -
+                l_cp->ty0) / l_cp->tdy;
+        p_image->y0 = (OPJ_UINT32)p_start_y;
+    }
+
+    /* Right */
+    if (p_end_x <= 0) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Right position of the decoded area (region_x1=%d) should be > 0.\n",
+                      p_end_x);
+        return OPJ_FALSE;
+    } else if ((OPJ_UINT32)p_end_x < l_image->x0) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Right position of the decoded area (region_x1=%d) is outside the image area (XOsiz=%d).\n",
+                      p_end_x, l_image->x0);
+        return OPJ_FALSE;
+    } else if ((OPJ_UINT32)p_end_x > l_image->x1) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "Right position of the decoded area (region_x1=%d) is outside the image area (Xsiz=%d).\n",
+                      p_end_x, l_image->x1);
+        p_j2k->m_specific_param.m_decoder.m_end_tile_x = l_cp->tw;
+        p_image->x1 = l_image->x1;
+    } else {
+        p_j2k->m_specific_param.m_decoder.m_end_tile_x = (OPJ_UINT32)opj_int_ceildiv(
+                    p_end_x - (OPJ_INT32)l_cp->tx0, (OPJ_INT32)l_cp->tdx);
+        p_image->x1 = (OPJ_UINT32)p_end_x;
+    }
+
+    /* Bottom */
+    if (p_end_y <= 0) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Bottom position of the decoded area (region_y1=%d) should be > 0.\n",
+                      p_end_y);
+        return OPJ_FALSE;
+    } else if ((OPJ_UINT32)p_end_y < l_image->y0) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Bottom position of the decoded area (region_y1=%d) is outside the image area (YOsiz=%d).\n",
+                      p_end_y, l_image->y0);
+        return OPJ_FALSE;
+    }
+    if ((OPJ_UINT32)p_end_y > l_image->y1) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "Bottom position of the decoded area (region_y1=%d) is outside the image area (Ysiz=%d).\n",
+                      p_end_y, l_image->y1);
+        p_j2k->m_specific_param.m_decoder.m_end_tile_y = l_cp->th;
+        p_image->y1 = l_image->y1;
+    } else {
+        p_j2k->m_specific_param.m_decoder.m_end_tile_y = (OPJ_UINT32)opj_int_ceildiv(
+                    p_end_y - (OPJ_INT32)l_cp->ty0, (OPJ_INT32)l_cp->tdy);
+        p_image->y1 = (OPJ_UINT32)p_end_y;
+    }
+    /* ----- */
+
+    p_j2k->m_specific_param.m_decoder.m_discard_tiles = 1;
+
+    ret = opj_j2k_update_image_dimensions(p_image, p_manager);
+
+    if (ret) {
+        opj_event_msg(p_manager, EVT_INFO, "Setting decoding area to %d,%d,%d,%d\n",
+                      p_image->x0, p_image->y0, p_image->x1, p_image->y1);
+    }
+
+    return ret;
+}
+
+opj_j2k_t* opj_j2k_create_decompress(void)
+{
+    opj_j2k_t *l_j2k = (opj_j2k_t*) opj_calloc(1, sizeof(opj_j2k_t));
+    if (!l_j2k) {
+        return 00;
+    }
+
+    l_j2k->m_is_decoder = 1;
+    l_j2k->m_cp.m_is_decoder = 1;
+    /* in the absence of JP2 boxes, consider different bit depth / sign */
+    /* per component is allowed */
+    l_j2k->m_cp.allow_different_bit_depth_sign = 1;
+
+    /* Default to using strict mode. */
+    l_j2k->m_cp.strict = OPJ_TRUE;
+
+#ifdef OPJ_DISABLE_TPSOT_FIX
+    l_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction_checked = 1;
+#endif
+
+    l_j2k->m_specific_param.m_decoder.m_default_tcp = (opj_tcp_t*) opj_calloc(1,
+            sizeof(opj_tcp_t));
+    if (!l_j2k->m_specific_param.m_decoder.m_default_tcp) {
+        opj_j2k_destroy(l_j2k);
+        return 00;
+    }
+
+    l_j2k->m_specific_param.m_decoder.m_header_data = (OPJ_BYTE *) opj_calloc(1,
+            OPJ_J2K_DEFAULT_HEADER_SIZE);
+    if (! l_j2k->m_specific_param.m_decoder.m_header_data) {
+        opj_j2k_destroy(l_j2k);
+        return 00;
+    }
+
+    l_j2k->m_specific_param.m_decoder.m_header_data_size =
+        OPJ_J2K_DEFAULT_HEADER_SIZE;
+
+    l_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec = -1 ;
+
+    l_j2k->m_specific_param.m_decoder.m_last_sot_read_pos = 0 ;
+
+    /* codestream index creation */
+    l_j2k->cstr_index = opj_j2k_create_cstr_index();
+    if (!l_j2k->cstr_index) {
+        opj_j2k_destroy(l_j2k);
+        return 00;
+    }
+
+    /* validation list creation */
+    l_j2k->m_validation_list = opj_procedure_list_create();
+    if (! l_j2k->m_validation_list) {
+        opj_j2k_destroy(l_j2k);
+        return 00;
+    }
+
+    /* execution list creation */
+    l_j2k->m_procedure_list = opj_procedure_list_create();
+    if (! l_j2k->m_procedure_list) {
+        opj_j2k_destroy(l_j2k);
+        return 00;
+    }
+
+    l_j2k->m_tp = opj_thread_pool_create(opj_j2k_get_default_thread_count());
+    if (!l_j2k->m_tp) {
+        l_j2k->m_tp = opj_thread_pool_create(0);
+    }
+    if (!l_j2k->m_tp) {
+        opj_j2k_destroy(l_j2k);
+        return NULL;
+    }
+
+    return l_j2k;
+}
+
+static opj_codestream_index_t* opj_j2k_create_cstr_index(void)
+{
+    opj_codestream_index_t* cstr_index = (opj_codestream_index_t*)
+                                         opj_calloc(1, sizeof(opj_codestream_index_t));
+    if (!cstr_index) {
+        return NULL;
+    }
+
+    cstr_index->maxmarknum = 100;
+    cstr_index->marknum = 0;
+    cstr_index->marker = (opj_marker_info_t*)
+                         opj_calloc(cstr_index->maxmarknum, sizeof(opj_marker_info_t));
+    if (!cstr_index-> marker) {
+        opj_free(cstr_index);
+        return NULL;
+    }
+
+    cstr_index->tile_index = NULL;
+
+    return cstr_index;
+}
+
+static OPJ_UINT32 opj_j2k_get_SPCod_SPCoc_size(opj_j2k_t *p_j2k,
+        OPJ_UINT32 p_tile_no,
+        OPJ_UINT32 p_comp_no)
+{
+    opj_cp_t *l_cp = 00;
+    opj_tcp_t *l_tcp = 00;
+    opj_tccp_t *l_tccp = 00;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+
+    l_cp = &(p_j2k->m_cp);
+    l_tcp = &l_cp->tcps[p_tile_no];
+    l_tccp = &l_tcp->tccps[p_comp_no];
+
+    /* preconditions again */
+    assert(p_tile_no < (l_cp->tw * l_cp->th));
+    assert(p_comp_no < p_j2k->m_private_image->numcomps);
+
+    if (l_tccp->csty & J2K_CCP_CSTY_PRT) {
+        return 5 + l_tccp->numresolutions;
+    } else {
+        return 5;
+    }
+}
+
+static OPJ_BOOL opj_j2k_compare_SPCod_SPCoc(opj_j2k_t *p_j2k,
+        OPJ_UINT32 p_tile_no, OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no)
+{
+    OPJ_UINT32 i;
+    opj_cp_t *l_cp = NULL;
+    opj_tcp_t *l_tcp = NULL;
+    opj_tccp_t *l_tccp0 = NULL;
+    opj_tccp_t *l_tccp1 = NULL;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+
+    l_cp = &(p_j2k->m_cp);
+    l_tcp = &l_cp->tcps[p_tile_no];
+    l_tccp0 = &l_tcp->tccps[p_first_comp_no];
+    l_tccp1 = &l_tcp->tccps[p_second_comp_no];
+
+    if (l_tccp0->numresolutions != l_tccp1->numresolutions) {
+        return OPJ_FALSE;
+    }
+    if (l_tccp0->cblkw != l_tccp1->cblkw) {
+        return OPJ_FALSE;
+    }
+    if (l_tccp0->cblkh != l_tccp1->cblkh) {
+        return OPJ_FALSE;
+    }
+    if (l_tccp0->cblksty != l_tccp1->cblksty) {
+        return OPJ_FALSE;
+    }
+    if (l_tccp0->qmfbid != l_tccp1->qmfbid) {
+        return OPJ_FALSE;
+    }
+    if ((l_tccp0->csty & J2K_CCP_CSTY_PRT) != (l_tccp1->csty & J2K_CCP_CSTY_PRT)) {
+        return OPJ_FALSE;
+    }
+
+    for (i = 0U; i < l_tccp0->numresolutions; ++i) {
+        if (l_tccp0->prcw[i] != l_tccp1->prcw[i]) {
+            return OPJ_FALSE;
+        }
+        if (l_tccp0->prch[i] != l_tccp1->prch[i]) {
+            return OPJ_FALSE;
+        }
+    }
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_SPCod_SPCoc(opj_j2k_t *p_j2k,
+        OPJ_UINT32 p_tile_no,
+        OPJ_UINT32 p_comp_no,
+        OPJ_BYTE * p_data,
+        OPJ_UINT32 * p_header_size,
+        struct opj_event_mgr * p_manager)
+{
+    OPJ_UINT32 i;
+    opj_cp_t *l_cp = 00;
+    opj_tcp_t *l_tcp = 00;
+    opj_tccp_t *l_tccp = 00;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_header_size != 00);
+    assert(p_manager != 00);
+    assert(p_data != 00);
+
+    l_cp = &(p_j2k->m_cp);
+    l_tcp = &l_cp->tcps[p_tile_no];
+    l_tccp = &l_tcp->tccps[p_comp_no];
+
+    /* preconditions again */
+    assert(p_tile_no < (l_cp->tw * l_cp->th));
+    assert(p_comp_no < (p_j2k->m_private_image->numcomps));
+
+    if (*p_header_size < 5) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error writing SPCod SPCoc element\n");
+        return OPJ_FALSE;
+    }
+
+    opj_write_bytes(p_data, l_tccp->numresolutions - 1, 1); /* SPcoc (D) */
+    ++p_data;
+
+    opj_write_bytes(p_data, l_tccp->cblkw - 2, 1);                  /* SPcoc (E) */
+    ++p_data;
+
+    opj_write_bytes(p_data, l_tccp->cblkh - 2, 1);                  /* SPcoc (F) */
+    ++p_data;
+
+    opj_write_bytes(p_data, l_tccp->cblksty,
+                    1);                            /* SPcoc (G) */
+    ++p_data;
+
+    opj_write_bytes(p_data, l_tccp->qmfbid,
+                    1);                             /* SPcoc (H) */
+    ++p_data;
+
+    *p_header_size = *p_header_size - 5;
+
+    if (l_tccp->csty & J2K_CCP_CSTY_PRT) {
+
+        if (*p_header_size < l_tccp->numresolutions) {
+            opj_event_msg(p_manager, EVT_ERROR, "Error writing SPCod SPCoc element\n");
+            return OPJ_FALSE;
+        }
+
+        for (i = 0; i < l_tccp->numresolutions; ++i) {
+            opj_write_bytes(p_data, l_tccp->prcw[i] + (l_tccp->prch[i] << 4),
+                            1);   /* SPcoc (I_i) */
+            ++p_data;
+        }
+
+        *p_header_size = *p_header_size - l_tccp->numresolutions;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_read_SPCod_SPCoc(opj_j2k_t *p_j2k,
+        OPJ_UINT32 compno,
+        OPJ_BYTE * p_header_data,
+        OPJ_UINT32 * p_header_size,
+        opj_event_mgr_t * p_manager)
+{
+    OPJ_UINT32 i, l_tmp;
+    opj_cp_t *l_cp = NULL;
+    opj_tcp_t *l_tcp = NULL;
+    opj_tccp_t *l_tccp = NULL;
+    OPJ_BYTE * l_current_ptr = NULL;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_header_data != 00);
+
+    l_cp = &(p_j2k->m_cp);
+    l_tcp = (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH) ?
+            &l_cp->tcps[p_j2k->m_current_tile_number] :
+            p_j2k->m_specific_param.m_decoder.m_default_tcp;
+
+    /* precondition again */
+    if (compno >= p_j2k->m_private_image->numcomps) {
+        return OPJ_FALSE;
+    }
+
+    assert(compno < p_j2k->m_private_image->numcomps);
+
+    l_tccp = &l_tcp->tccps[compno];
+    l_current_ptr = p_header_data;
+
+    /* make sure room is sufficient */
+    if (*p_header_size < 5) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading SPCod SPCoc element\n");
+        return OPJ_FALSE;
+    }
+
+    /* SPcod (D) / SPcoc (A) */
+    opj_read_bytes(l_current_ptr, &l_tccp->numresolutions, 1);
+    ++l_tccp->numresolutions;  /* tccp->numresolutions = read() + 1 */
+    if (l_tccp->numresolutions > OPJ_J2K_MAXRLVLS) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Invalid value for numresolutions : %d, max value is set in openjpeg.h at %d\n",
+                      l_tccp->numresolutions, OPJ_J2K_MAXRLVLS);
+        return OPJ_FALSE;
+    }
+    ++l_current_ptr;
+
+    /* If user wants to remove more resolutions than the codestream contains, return error */
+    if (l_cp->m_specific_param.m_dec.m_reduce >= l_tccp->numresolutions) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Error decoding component %d.\nThe number of resolutions "
+                      "to remove (%d) is greater or equal than the number "
+                      "of resolutions of this component (%d)\nModify the cp_reduce parameter.\n\n",
+                      compno, l_cp->m_specific_param.m_dec.m_reduce, l_tccp->numresolutions);
+        p_j2k->m_specific_param.m_decoder.m_state |=
+            0x8000;/* FIXME J2K_DEC_STATE_ERR;*/
+        return OPJ_FALSE;
+    }
+
+    /* SPcod (E) / SPcoc (B) */
+    opj_read_bytes(l_current_ptr, &l_tccp->cblkw, 1);
+    ++l_current_ptr;
+    l_tccp->cblkw += 2;
+
+    /* SPcod (F) / SPcoc (C) */
+    opj_read_bytes(l_current_ptr, &l_tccp->cblkh, 1);
+    ++l_current_ptr;
+    l_tccp->cblkh += 2;
+
+    if ((l_tccp->cblkw > 10) || (l_tccp->cblkh > 10) ||
+            ((l_tccp->cblkw + l_tccp->cblkh) > 12)) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Error reading SPCod SPCoc element, Invalid cblkw/cblkh combination\n");
+        return OPJ_FALSE;
+    }
+
+    /* SPcod (G) / SPcoc (D) */
+    opj_read_bytes(l_current_ptr, &l_tccp->cblksty, 1);
+    ++l_current_ptr;
+    if ((l_tccp->cblksty & J2K_CCP_CBLKSTY_HTMIXED) != 0) {
+        /* We do not support HT mixed mode yet.  For conformance, it should be supported.*/
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Error reading SPCod SPCoc element. Unsupported Mixed HT code-block style found\n");
+        return OPJ_FALSE;
+    }
+
+    /* SPcod (H) / SPcoc (E) */
+    opj_read_bytes(l_current_ptr, &l_tccp->qmfbid, 1);
+    ++l_current_ptr;
+
+    if (l_tccp->qmfbid > 1) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Error reading SPCod SPCoc element, Invalid transformation found\n");
+        return OPJ_FALSE;
+    }
+
+    *p_header_size = *p_header_size - 5;
+
+    /* use custom precinct size ? */
+    if (l_tccp->csty & J2K_CCP_CSTY_PRT) {
+        if (*p_header_size < l_tccp->numresolutions) {
+            opj_event_msg(p_manager, EVT_ERROR, "Error reading SPCod SPCoc element\n");
+            return OPJ_FALSE;
+        }
+
+        /* SPcod (I_i) / SPcoc (F_i) */
+        for (i = 0; i < l_tccp->numresolutions; ++i) {
+            opj_read_bytes(l_current_ptr, &l_tmp, 1);
+            ++l_current_ptr;
+            /* Precinct exponent 0 is only allowed for lowest resolution level (Table A.21) */
+            if ((i != 0) && (((l_tmp & 0xf) == 0) || ((l_tmp >> 4) == 0))) {
+                opj_event_msg(p_manager, EVT_ERROR, "Invalid precinct size\n");
+                return OPJ_FALSE;
+            }
+            l_tccp->prcw[i] = l_tmp & 0xf;
+            l_tccp->prch[i] = l_tmp >> 4;
+        }
+
+        *p_header_size = *p_header_size - l_tccp->numresolutions;
+    } else {
+        /* set default size for the precinct width and height */
+        for (i = 0; i < l_tccp->numresolutions; ++i) {
+            l_tccp->prcw[i] = 15;
+            l_tccp->prch[i] = 15;
+        }
+    }
+
+#ifdef WIP_REMOVE_MSD
+    /* INDEX >> */
+    if (p_j2k->cstr_info && compno == 0) {
+        OPJ_UINT32 l_data_size = l_tccp->numresolutions * sizeof(OPJ_UINT32);
+
+        p_j2k->cstr_info->tile[p_j2k->m_current_tile_number].tccp_info[compno].cblkh =
+            l_tccp->cblkh;
+        p_j2k->cstr_info->tile[p_j2k->m_current_tile_number].tccp_info[compno].cblkw =
+            l_tccp->cblkw;
+        p_j2k->cstr_info->tile[p_j2k->m_current_tile_number].tccp_info[compno].numresolutions
+            = l_tccp->numresolutions;
+        p_j2k->cstr_info->tile[p_j2k->m_current_tile_number].tccp_info[compno].cblksty =
+            l_tccp->cblksty;
+        p_j2k->cstr_info->tile[p_j2k->m_current_tile_number].tccp_info[compno].qmfbid =
+            l_tccp->qmfbid;
+
+        memcpy(p_j2k->cstr_info->tile[p_j2k->m_current_tile_number].pdx, l_tccp->prcw,
+               l_data_size);
+        memcpy(p_j2k->cstr_info->tile[p_j2k->m_current_tile_number].pdy, l_tccp->prch,
+               l_data_size);
+    }
+    /* << INDEX */
+#endif
+
+    return OPJ_TRUE;
+}
+
+static void opj_j2k_copy_tile_component_parameters(opj_j2k_t *p_j2k)
+{
+    /* loop */
+    OPJ_UINT32 i;
+    opj_cp_t *l_cp = NULL;
+    opj_tcp_t *l_tcp = NULL;
+    opj_tccp_t *l_ref_tccp = NULL, *l_copied_tccp = NULL;
+    OPJ_UINT32 l_prc_size;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+
+    l_cp = &(p_j2k->m_cp);
+    l_tcp = (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH)
+            ?
+            &l_cp->tcps[p_j2k->m_current_tile_number] :
+            p_j2k->m_specific_param.m_decoder.m_default_tcp;
+
+    l_ref_tccp = &l_tcp->tccps[0];
+    l_copied_tccp = l_ref_tccp + 1;
+    l_prc_size = l_ref_tccp->numresolutions * (OPJ_UINT32)sizeof(OPJ_UINT32);
+
+    for (i = 1; i < p_j2k->m_private_image->numcomps; ++i) {
+        l_copied_tccp->numresolutions = l_ref_tccp->numresolutions;
+        l_copied_tccp->cblkw = l_ref_tccp->cblkw;
+        l_copied_tccp->cblkh = l_ref_tccp->cblkh;
+        l_copied_tccp->cblksty = l_ref_tccp->cblksty;
+        l_copied_tccp->qmfbid = l_ref_tccp->qmfbid;
+        memcpy(l_copied_tccp->prcw, l_ref_tccp->prcw, l_prc_size);
+        memcpy(l_copied_tccp->prch, l_ref_tccp->prch, l_prc_size);
+        ++l_copied_tccp;
+    }
+}
+
+static OPJ_UINT32 opj_j2k_get_SQcd_SQcc_size(opj_j2k_t *p_j2k,
+        OPJ_UINT32 p_tile_no,
+        OPJ_UINT32 p_comp_no)
+{
+    OPJ_UINT32 l_num_bands;
+
+    opj_cp_t *l_cp = 00;
+    opj_tcp_t *l_tcp = 00;
+    opj_tccp_t *l_tccp = 00;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+
+    l_cp = &(p_j2k->m_cp);
+    l_tcp = &l_cp->tcps[p_tile_no];
+    l_tccp = &l_tcp->tccps[p_comp_no];
+
+    /* preconditions again */
+    assert(p_tile_no < l_cp->tw * l_cp->th);
+    assert(p_comp_no < p_j2k->m_private_image->numcomps);
+
+    l_num_bands = (l_tccp->qntsty == J2K_CCP_QNTSTY_SIQNT) ? 1 :
+                  (l_tccp->numresolutions * 3 - 2);
+
+    if (l_tccp->qntsty == J2K_CCP_QNTSTY_NOQNT)  {
+        return 1 + l_num_bands;
+    } else {
+        return 1 + 2 * l_num_bands;
+    }
+}
+
+static OPJ_BOOL opj_j2k_compare_SQcd_SQcc(opj_j2k_t *p_j2k,
+        OPJ_UINT32 p_tile_no, OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no)
+{
+    opj_cp_t *l_cp = NULL;
+    opj_tcp_t *l_tcp = NULL;
+    opj_tccp_t *l_tccp0 = NULL;
+    opj_tccp_t *l_tccp1 = NULL;
+    OPJ_UINT32 l_band_no, l_num_bands;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+
+    l_cp = &(p_j2k->m_cp);
+    l_tcp = &l_cp->tcps[p_tile_no];
+    l_tccp0 = &l_tcp->tccps[p_first_comp_no];
+    l_tccp1 = &l_tcp->tccps[p_second_comp_no];
+
+    if (l_tccp0->qntsty != l_tccp1->qntsty) {
+        return OPJ_FALSE;
+    }
+    if (l_tccp0->numgbits != l_tccp1->numgbits) {
+        return OPJ_FALSE;
+    }
+    if (l_tccp0->qntsty == J2K_CCP_QNTSTY_SIQNT) {
+        l_num_bands = 1U;
+    } else {
+        l_num_bands = l_tccp0->numresolutions * 3U - 2U;
+        if (l_num_bands != (l_tccp1->numresolutions * 3U - 2U)) {
+            return OPJ_FALSE;
+        }
+    }
+
+    for (l_band_no = 0; l_band_no < l_num_bands; ++l_band_no) {
+        if (l_tccp0->stepsizes[l_band_no].expn != l_tccp1->stepsizes[l_band_no].expn) {
+            return OPJ_FALSE;
+        }
+    }
+    if (l_tccp0->qntsty != J2K_CCP_QNTSTY_NOQNT) {
+        for (l_band_no = 0; l_band_no < l_num_bands; ++l_band_no) {
+            if (l_tccp0->stepsizes[l_band_no].mant != l_tccp1->stepsizes[l_band_no].mant) {
+                return OPJ_FALSE;
+            }
+        }
+    }
+    return OPJ_TRUE;
+}
+
+
+static OPJ_BOOL opj_j2k_write_SQcd_SQcc(opj_j2k_t *p_j2k,
+                                        OPJ_UINT32 p_tile_no,
+                                        OPJ_UINT32 p_comp_no,
+                                        OPJ_BYTE * p_data,
+                                        OPJ_UINT32 * p_header_size,
+                                        struct opj_event_mgr * p_manager)
+{
+    OPJ_UINT32 l_header_size;
+    OPJ_UINT32 l_band_no, l_num_bands;
+    OPJ_UINT32 l_expn, l_mant;
+
+    opj_cp_t *l_cp = 00;
+    opj_tcp_t *l_tcp = 00;
+    opj_tccp_t *l_tccp = 00;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_header_size != 00);
+    assert(p_manager != 00);
+    assert(p_data != 00);
+
+    l_cp = &(p_j2k->m_cp);
+    l_tcp = &l_cp->tcps[p_tile_no];
+    l_tccp = &l_tcp->tccps[p_comp_no];
+
+    /* preconditions again */
+    assert(p_tile_no < l_cp->tw * l_cp->th);
+    assert(p_comp_no < p_j2k->m_private_image->numcomps);
+
+    l_num_bands = (l_tccp->qntsty == J2K_CCP_QNTSTY_SIQNT) ? 1 :
+                  (l_tccp->numresolutions * 3 - 2);
+
+    if (l_tccp->qntsty == J2K_CCP_QNTSTY_NOQNT)  {
+        l_header_size = 1 + l_num_bands;
+
+        if (*p_header_size < l_header_size) {
+            opj_event_msg(p_manager, EVT_ERROR, "Error writing SQcd SQcc element\n");
+            return OPJ_FALSE;
+        }
+
+        opj_write_bytes(p_data, l_tccp->qntsty + (l_tccp->numgbits << 5),
+                        1);   /* Sqcx */
+        ++p_data;
+
+        for (l_band_no = 0; l_band_no < l_num_bands; ++l_band_no) {
+            l_expn = (OPJ_UINT32)l_tccp->stepsizes[l_band_no].expn;
+            opj_write_bytes(p_data, l_expn << 3, 1);        /* SPqcx_i */
+            ++p_data;
+        }
+    } else {
+        l_header_size = 1 + 2 * l_num_bands;
+
+        if (*p_header_size < l_header_size) {
+            opj_event_msg(p_manager, EVT_ERROR, "Error writing SQcd SQcc element\n");
+            return OPJ_FALSE;
+        }
+
+        opj_write_bytes(p_data, l_tccp->qntsty + (l_tccp->numgbits << 5),
+                        1);   /* Sqcx */
+        ++p_data;
+
+        for (l_band_no = 0; l_band_no < l_num_bands; ++l_band_no) {
+            l_expn = (OPJ_UINT32)l_tccp->stepsizes[l_band_no].expn;
+            l_mant = (OPJ_UINT32)l_tccp->stepsizes[l_band_no].mant;
+
+            opj_write_bytes(p_data, (l_expn << 11) + l_mant, 2);    /* SPqcx_i */
+            p_data += 2;
+        }
+    }
+
+    *p_header_size = *p_header_size - l_header_size;
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_read_SQcd_SQcc(opj_j2k_t *p_j2k,
+                                       OPJ_UINT32 p_comp_no,
+                                       OPJ_BYTE* p_header_data,
+                                       OPJ_UINT32 * p_header_size,
+                                       opj_event_mgr_t * p_manager
+                                      )
+{
+    /* loop*/
+    OPJ_UINT32 l_band_no;
+    opj_cp_t *l_cp = 00;
+    opj_tcp_t *l_tcp = 00;
+    opj_tccp_t *l_tccp = 00;
+    OPJ_BYTE * l_current_ptr = 00;
+    OPJ_UINT32 l_tmp, l_num_band;
+
+    /* preconditions*/
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_header_data != 00);
+
+    l_cp = &(p_j2k->m_cp);
+    /* come from tile part header or main header ?*/
+    l_tcp = (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH)
+            ?
+            &l_cp->tcps[p_j2k->m_current_tile_number] :
+            p_j2k->m_specific_param.m_decoder.m_default_tcp;
+
+    /* precondition again*/
+    if (p_comp_no >=  p_j2k->m_private_image->numcomps) {
+        return OPJ_FALSE;
+    }
+
+    l_tccp = &l_tcp->tccps[p_comp_no];
+    l_current_ptr = p_header_data;
+
+    if (*p_header_size < 1) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error reading SQcd or SQcc element\n");
+        return OPJ_FALSE;
+    }
+    *p_header_size -= 1;
+
+    opj_read_bytes(l_current_ptr, &l_tmp, 1);                       /* Sqcx */
+    ++l_current_ptr;
+
+    l_tccp->qntsty = l_tmp & 0x1f;
+    l_tccp->numgbits = l_tmp >> 5;
+    if (l_tccp->qntsty == J2K_CCP_QNTSTY_SIQNT) {
+        l_num_band = 1;
+    } else {
+        l_num_band = (l_tccp->qntsty == J2K_CCP_QNTSTY_NOQNT) ?
+                     (*p_header_size) :
+                     (*p_header_size) / 2;
+
+        if (l_num_band > OPJ_J2K_MAXBANDS) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "While reading CCP_QNTSTY element inside QCD or QCC marker segment, "
+                          "number of subbands (%d) is greater to OPJ_J2K_MAXBANDS (%d). So we limit the number of elements stored to "
+                          "OPJ_J2K_MAXBANDS (%d) and skip the rest. \n", l_num_band, OPJ_J2K_MAXBANDS,
+                          OPJ_J2K_MAXBANDS);
+            /*return OPJ_FALSE;*/
+        }
+    }
+
+#ifdef USE_JPWL
+    if (l_cp->correct) {
+
+        /* if JPWL is on, we check whether there are too many subbands */
+        if (/*(l_num_band < 0) ||*/ (l_num_band >= OPJ_J2K_MAXBANDS)) {
+            opj_event_msg(p_manager, JPWL_ASSUME ? EVT_WARNING : EVT_ERROR,
+                          "JPWL: bad number of subbands in Sqcx (%d)\n",
+                          l_num_band);
+            if (!JPWL_ASSUME) {
+                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
+                return OPJ_FALSE;
+            }
+            /* we try to correct */
+            l_num_band = 1;
+            opj_event_msg(p_manager, EVT_WARNING, "- trying to adjust them\n"
+                          "- setting number of bands to %d => HYPOTHESIS!!!\n",
+                          l_num_band);
+        };
+
+    };
+#endif /* USE_JPWL */
+
+    if (l_tccp->qntsty == J2K_CCP_QNTSTY_NOQNT) {
+        for (l_band_no = 0; l_band_no < l_num_band; l_band_no++) {
+            opj_read_bytes(l_current_ptr, &l_tmp, 1);                       /* SPqcx_i */
+            ++l_current_ptr;
+            if (l_band_no < OPJ_J2K_MAXBANDS) {
+                l_tccp->stepsizes[l_band_no].expn = (OPJ_INT32)(l_tmp >> 3);
+                l_tccp->stepsizes[l_band_no].mant = 0;
+            }
+        }
+        *p_header_size = *p_header_size - l_num_band;
+    } else {
+        for (l_band_no = 0; l_band_no < l_num_band; l_band_no++) {
+            opj_read_bytes(l_current_ptr, &l_tmp, 2);                       /* SPqcx_i */
+            l_current_ptr += 2;
+            if (l_band_no < OPJ_J2K_MAXBANDS) {
+                l_tccp->stepsizes[l_band_no].expn = (OPJ_INT32)(l_tmp >> 11);
+                l_tccp->stepsizes[l_band_no].mant = l_tmp & 0x7ff;
+            }
+        }
+        *p_header_size = *p_header_size - 2 * l_num_band;
+    }
+
+    /* Add Antonin : if scalar_derived -> compute other stepsizes */
+    if (l_tccp->qntsty == J2K_CCP_QNTSTY_SIQNT) {
+        for (l_band_no = 1; l_band_no < OPJ_J2K_MAXBANDS; l_band_no++) {
+            l_tccp->stepsizes[l_band_no].expn =
+                ((OPJ_INT32)(l_tccp->stepsizes[0].expn) - (OPJ_INT32)((l_band_no - 1) / 3) > 0)
+                ?
+                (OPJ_INT32)(l_tccp->stepsizes[0].expn) - (OPJ_INT32)((l_band_no - 1) / 3) : 0;
+            l_tccp->stepsizes[l_band_no].mant = l_tccp->stepsizes[0].mant;
+        }
+    }
+
+    return OPJ_TRUE;
+}
+
+static void opj_j2k_copy_tile_quantization_parameters(opj_j2k_t *p_j2k)
+{
+    OPJ_UINT32 i;
+    opj_cp_t *l_cp = NULL;
+    opj_tcp_t *l_tcp = NULL;
+    opj_tccp_t *l_ref_tccp = NULL;
+    opj_tccp_t *l_copied_tccp = NULL;
+    OPJ_UINT32 l_size;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+
+    l_cp = &(p_j2k->m_cp);
+    l_tcp = p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH ?
+            &l_cp->tcps[p_j2k->m_current_tile_number] :
+            p_j2k->m_specific_param.m_decoder.m_default_tcp;
+
+    l_ref_tccp = &l_tcp->tccps[0];
+    l_copied_tccp = l_ref_tccp + 1;
+    l_size = OPJ_J2K_MAXBANDS * sizeof(opj_stepsize_t);
+
+    for (i = 1; i < p_j2k->m_private_image->numcomps; ++i) {
+        l_copied_tccp->qntsty = l_ref_tccp->qntsty;
+        l_copied_tccp->numgbits = l_ref_tccp->numgbits;
+        memcpy(l_copied_tccp->stepsizes, l_ref_tccp->stepsizes, l_size);
+        ++l_copied_tccp;
+    }
+}
+
+static void opj_j2k_dump_tile_info(opj_tcp_t * l_default_tile,
+                                   OPJ_INT32 numcomps, FILE* out_stream)
+{
+    if (l_default_tile) {
+        OPJ_INT32 compno;
+
+        fprintf(out_stream, "\t default tile {\n");
+        fprintf(out_stream, "\t\t csty=%#x\n", l_default_tile->csty);
+        fprintf(out_stream, "\t\t prg=%#x\n", l_default_tile->prg);
+        fprintf(out_stream, "\t\t numlayers=%d\n", l_default_tile->numlayers);
+        fprintf(out_stream, "\t\t mct=%x\n", l_default_tile->mct);
+
+        for (compno = 0; compno < numcomps; compno++) {
+            opj_tccp_t *l_tccp = &(l_default_tile->tccps[compno]);
+            OPJ_UINT32 resno;
+            OPJ_INT32 bandno, numbands;
+
+            /* coding style*/
+            fprintf(out_stream, "\t\t comp %d {\n", compno);
+            fprintf(out_stream, "\t\t\t csty=%#x\n", l_tccp->csty);
+            fprintf(out_stream, "\t\t\t numresolutions=%d\n", l_tccp->numresolutions);
+            fprintf(out_stream, "\t\t\t cblkw=2^%d\n", l_tccp->cblkw);
+            fprintf(out_stream, "\t\t\t cblkh=2^%d\n", l_tccp->cblkh);
+            fprintf(out_stream, "\t\t\t cblksty=%#x\n", l_tccp->cblksty);
+            fprintf(out_stream, "\t\t\t qmfbid=%d\n", l_tccp->qmfbid);
+
+            fprintf(out_stream, "\t\t\t preccintsize (w,h)=");
+            for (resno = 0; resno < l_tccp->numresolutions; resno++) {
+                fprintf(out_stream, "(%d,%d) ", l_tccp->prcw[resno], l_tccp->prch[resno]);
+            }
+            fprintf(out_stream, "\n");
+
+            /* quantization style*/
+            fprintf(out_stream, "\t\t\t qntsty=%d\n", l_tccp->qntsty);
+            fprintf(out_stream, "\t\t\t numgbits=%d\n", l_tccp->numgbits);
+            fprintf(out_stream, "\t\t\t stepsizes (m,e)=");
+            numbands = (l_tccp->qntsty == J2K_CCP_QNTSTY_SIQNT) ? 1 :
+                       (OPJ_INT32)l_tccp->numresolutions * 3 - 2;
+            for (bandno = 0; bandno < numbands; bandno++) {
+                fprintf(out_stream, "(%d,%d) ", l_tccp->stepsizes[bandno].mant,
+                        l_tccp->stepsizes[bandno].expn);
+            }
+            fprintf(out_stream, "\n");
+
+            /* RGN value*/
+            fprintf(out_stream, "\t\t\t roishift=%d\n", l_tccp->roishift);
+
+            fprintf(out_stream, "\t\t }\n");
+        } /*end of component of default tile*/
+        fprintf(out_stream, "\t }\n"); /*end of default tile*/
+    }
+}
+
+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");
+        return;
+    }
+
+    /* Dump the image_header */
+    if (flag & OPJ_IMG_INFO) {
+        if (p_j2k->m_private_image) {
+            j2k_dump_image_header(p_j2k->m_private_image, 0, out_stream);
+        }
+    }
+
+    /* Dump the codestream info from main header */
+    if (flag & OPJ_J2K_MH_INFO) {
+        if (p_j2k->m_private_image) {
+            opj_j2k_dump_MH_info(p_j2k, out_stream);
+        }
+    }
+    /* Dump all tile/codestream info */
+    if (flag & OPJ_J2K_TCH_INFO) {
+        OPJ_UINT32 l_nb_tiles = p_j2k->m_cp.th * p_j2k->m_cp.tw;
+        OPJ_UINT32 i;
+        opj_tcp_t * l_tcp = p_j2k->m_cp.tcps;
+        if (p_j2k->m_private_image) {
+            for (i = 0; i < l_nb_tiles; ++i) {
+                opj_j2k_dump_tile_info(l_tcp, (OPJ_INT32)p_j2k->m_private_image->numcomps,
+                                       out_stream);
+                ++l_tcp;
+            }
+        }
+    }
+
+    /* Dump the codestream info of the current tile */
+    if (flag & OPJ_J2K_TH_INFO) {
+
+    }
+
+    /* Dump the codestream index from main header */
+    if (flag & OPJ_J2K_MH_IND) {
+        opj_j2k_dump_MH_index(p_j2k, out_stream);
+    }
+
+    /* Dump the codestream index of the current tile */
+    if (flag & OPJ_J2K_TH_IND) {
+
+    }
+
+}
+
+static void opj_j2k_dump_MH_index(opj_j2k_t* p_j2k, FILE* out_stream)
+{
+    opj_codestream_index_t* cstr_index = p_j2k->cstr_index;
+    OPJ_UINT32 it_marker, it_tile, it_tile_part;
+
+    fprintf(out_stream, "Codestream index from main header: {\n");
+
+    fprintf(out_stream, "\t Main header start position=%" PRIi64 "\n"
+            "\t Main header end position=%" PRIi64 "\n",
+            cstr_index->main_head_start, cstr_index->main_head_end);
+
+    fprintf(out_stream, "\t Marker list: {\n");
+
+    if (cstr_index->marker) {
+        for (it_marker = 0; it_marker < cstr_index->marknum ; it_marker++) {
+            fprintf(out_stream, "\t\t type=%#x, pos=%" PRIi64 ", len=%d\n",
+                    cstr_index->marker[it_marker].type,
+                    cstr_index->marker[it_marker].pos,
+                    cstr_index->marker[it_marker].len);
+        }
+    }
+
+    fprintf(out_stream, "\t }\n");
+
+    if (cstr_index->tile_index) {
+
+        /* Simple test to avoid to write empty information*/
+        OPJ_UINT32 l_acc_nb_of_tile_part = 0;
+        for (it_tile = 0; it_tile < cstr_index->nb_of_tiles ; it_tile++) {
+            l_acc_nb_of_tile_part += cstr_index->tile_index[it_tile].nb_tps;
+        }
+
+        if (l_acc_nb_of_tile_part) {
+            fprintf(out_stream, "\t Tile index: {\n");
+
+            for (it_tile = 0; it_tile < cstr_index->nb_of_tiles ; it_tile++) {
+                OPJ_UINT32 nb_of_tile_part = cstr_index->tile_index[it_tile].nb_tps;
+
+                fprintf(out_stream, "\t\t nb of tile-part in tile [%d]=%d\n", it_tile,
+                        nb_of_tile_part);
+
+                if (cstr_index->tile_index[it_tile].tp_index) {
+                    for (it_tile_part = 0; it_tile_part < nb_of_tile_part; it_tile_part++) {
+                        fprintf(out_stream, "\t\t\t tile-part[%d]: star_pos=%" PRIi64 ", end_header=%"
+                                PRIi64 ", end_pos=%" PRIi64 ".\n",
+                                it_tile_part,
+                                cstr_index->tile_index[it_tile].tp_index[it_tile_part].start_pos,
+                                cstr_index->tile_index[it_tile].tp_index[it_tile_part].end_header,
+                                cstr_index->tile_index[it_tile].tp_index[it_tile_part].end_pos);
+                    }
+                }
+
+                if (cstr_index->tile_index[it_tile].marker) {
+                    for (it_marker = 0; it_marker < cstr_index->tile_index[it_tile].marknum ;
+                            it_marker++) {
+                        fprintf(out_stream, "\t\t type=%#x, pos=%" PRIi64 ", len=%d\n",
+                                cstr_index->tile_index[it_tile].marker[it_marker].type,
+                                cstr_index->tile_index[it_tile].marker[it_marker].pos,
+                                cstr_index->tile_index[it_tile].marker[it_marker].len);
+                    }
+                }
+            }
+            fprintf(out_stream, "\t }\n");
+        }
+    }
+
+    fprintf(out_stream, "}\n");
+
+}
+
+
+static void opj_j2k_dump_MH_info(opj_j2k_t* p_j2k, FILE* out_stream)
+{
+
+    fprintf(out_stream, "Codestream info from main header: {\n");
+
+    fprintf(out_stream, "\t tx0=%d, ty0=%d\n", p_j2k->m_cp.tx0, p_j2k->m_cp.ty0);
+    fprintf(out_stream, "\t tdx=%d, tdy=%d\n", p_j2k->m_cp.tdx, p_j2k->m_cp.tdy);
+    fprintf(out_stream, "\t tw=%d, th=%d\n", p_j2k->m_cp.tw, p_j2k->m_cp.th);
+    opj_j2k_dump_tile_info(p_j2k->m_specific_param.m_decoder.m_default_tcp,
+                           (OPJ_INT32)p_j2k->m_private_image->numcomps, out_stream);
+    fprintf(out_stream, "}\n");
+}
+
+void j2k_dump_image_header(opj_image_t* img_header, OPJ_BOOL dev_dump_flag,
+                           FILE* out_stream)
+{
+    char tab[2];
+
+    if (dev_dump_flag) {
+        fprintf(stdout, "[DEV] Dump an image_header struct {\n");
+        tab[0] = '\0';
+    } else {
+        fprintf(out_stream, "Image info {\n");
+        tab[0] = '\t';
+        tab[1] = '\0';
+    }
+
+    fprintf(out_stream, "%s x0=%d, y0=%d\n", tab, img_header->x0, img_header->y0);
+    fprintf(out_stream,     "%s x1=%d, y1=%d\n", tab, img_header->x1,
+            img_header->y1);
+    fprintf(out_stream, "%s numcomps=%d\n", tab, img_header->numcomps);
+
+    if (img_header->comps) {
+        OPJ_UINT32 compno;
+        for (compno = 0; compno < img_header->numcomps; compno++) {
+            fprintf(out_stream, "%s\t component %d {\n", tab, compno);
+            j2k_dump_image_comp_header(&(img_header->comps[compno]), dev_dump_flag,
+                                       out_stream);
+            fprintf(out_stream, "%s}\n", tab);
+        }
+    }
+
+    fprintf(out_stream, "}\n");
+}
+
+void j2k_dump_image_comp_header(opj_image_comp_t* comp_header,
+                                OPJ_BOOL dev_dump_flag, FILE* out_stream)
+{
+    char tab[3];
+
+    if (dev_dump_flag) {
+        fprintf(stdout, "[DEV] Dump an image_comp_header struct {\n");
+        tab[0] = '\0';
+    }       else {
+        tab[0] = '\t';
+        tab[1] = '\t';
+        tab[2] = '\0';
+    }
+
+    fprintf(out_stream, "%s dx=%d, dy=%d\n", tab, comp_header->dx, comp_header->dy);
+    fprintf(out_stream, "%s prec=%d\n", tab, comp_header->prec);
+    fprintf(out_stream, "%s sgnd=%d\n", tab, comp_header->sgnd);
+
+    if (dev_dump_flag) {
+        fprintf(out_stream, "}\n");
+    }
+}
+
+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;
+    opj_codestream_info_v2_t* cstr_info = (opj_codestream_info_v2_t*) opj_calloc(1,
+                                          sizeof(opj_codestream_info_v2_t));
+    if (!cstr_info) {
+        return NULL;
+    }
+
+    cstr_info->nbcomps = p_j2k->m_private_image->numcomps;
+
+    cstr_info->tx0 = p_j2k->m_cp.tx0;
+    cstr_info->ty0 = p_j2k->m_cp.ty0;
+    cstr_info->tdx = p_j2k->m_cp.tdx;
+    cstr_info->tdy = p_j2k->m_cp.tdy;
+    cstr_info->tw = p_j2k->m_cp.tw;
+    cstr_info->th = p_j2k->m_cp.th;
+
+    cstr_info->tile_info = NULL; /* Not fill from the main header*/
+
+    l_default_tile = p_j2k->m_specific_param.m_decoder.m_default_tcp;
+
+    cstr_info->m_default_tile_info.csty = l_default_tile->csty;
+    cstr_info->m_default_tile_info.prg = l_default_tile->prg;
+    cstr_info->m_default_tile_info.numlayers = l_default_tile->numlayers;
+    cstr_info->m_default_tile_info.mct = l_default_tile->mct;
+
+    cstr_info->m_default_tile_info.tccp_info = (opj_tccp_info_t*) opj_calloc(
+                cstr_info->nbcomps, sizeof(opj_tccp_info_t));
+    if (!cstr_info->m_default_tile_info.tccp_info) {
+        opj_destroy_cstr_info(&cstr_info);
+        return NULL;
+    }
+
+    for (compno = 0; compno < numcomps; compno++) {
+        opj_tccp_t *l_tccp = &(l_default_tile->tccps[compno]);
+        opj_tccp_info_t *l_tccp_info = &
+                                       (cstr_info->m_default_tile_info.tccp_info[compno]);
+        OPJ_INT32 bandno, numbands;
+
+        /* coding style*/
+        l_tccp_info->csty = l_tccp->csty;
+        l_tccp_info->numresolutions = l_tccp->numresolutions;
+        l_tccp_info->cblkw = l_tccp->cblkw;
+        l_tccp_info->cblkh = l_tccp->cblkh;
+        l_tccp_info->cblksty = l_tccp->cblksty;
+        l_tccp_info->qmfbid = l_tccp->qmfbid;
+        if (l_tccp->numresolutions < OPJ_J2K_MAXRLVLS) {
+            memcpy(l_tccp_info->prch, l_tccp->prch, l_tccp->numresolutions);
+            memcpy(l_tccp_info->prcw, l_tccp->prcw, l_tccp->numresolutions);
+        }
+
+        /* quantization style*/
+        l_tccp_info->qntsty = l_tccp->qntsty;
+        l_tccp_info->numgbits = l_tccp->numgbits;
+
+        numbands = (l_tccp->qntsty == J2K_CCP_QNTSTY_SIQNT) ? 1 :
+                   (OPJ_INT32)l_tccp->numresolutions * 3 - 2;
+        if (numbands < OPJ_J2K_MAXBANDS) {
+            for (bandno = 0; bandno < numbands; bandno++) {
+                l_tccp_info->stepsizes_mant[bandno] = (OPJ_UINT32)
+                                                      l_tccp->stepsizes[bandno].mant;
+                l_tccp_info->stepsizes_expn[bandno] = (OPJ_UINT32)
+                                                      l_tccp->stepsizes[bandno].expn;
+            }
+        }
+
+        /* RGN value*/
+        l_tccp_info->roishift = l_tccp->roishift;
+    }
+
+    return cstr_info;
+}
+
+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) {
+        return NULL;
+    }
+
+    l_cstr_index->main_head_start = p_j2k->cstr_index->main_head_start;
+    l_cstr_index->main_head_end = p_j2k->cstr_index->main_head_end;
+    l_cstr_index->codestream_size = p_j2k->cstr_index->codestream_size;
+
+    l_cstr_index->marknum = p_j2k->cstr_index->marknum;
+    l_cstr_index->marker = (opj_marker_info_t*)opj_malloc(l_cstr_index->marknum *
+                           sizeof(opj_marker_info_t));
+    if (!l_cstr_index->marker) {
+        opj_free(l_cstr_index);
+        return NULL;
+    }
+
+    if (p_j2k->cstr_index->marker) {
+        memcpy(l_cstr_index->marker, p_j2k->cstr_index->marker,
+               l_cstr_index->marknum * sizeof(opj_marker_info_t));
+    } else {
+        opj_free(l_cstr_index->marker);
+        l_cstr_index->marker = NULL;
+    }
+
+    l_cstr_index->nb_of_tiles = p_j2k->cstr_index->nb_of_tiles;
+    l_cstr_index->tile_index = (opj_tile_index_t*)opj_calloc(
+                                   l_cstr_index->nb_of_tiles, sizeof(opj_tile_index_t));
+    if (!l_cstr_index->tile_index) {
+        opj_free(l_cstr_index->marker);
+        opj_free(l_cstr_index);
+        return NULL;
+    }
+
+    if (!p_j2k->cstr_index->tile_index) {
+        opj_free(l_cstr_index->tile_index);
+        l_cstr_index->tile_index = NULL;
+    } else {
+        OPJ_UINT32 it_tile = 0;
+        for (it_tile = 0; it_tile < l_cstr_index->nb_of_tiles; it_tile++) {
+
+            /* Tile Marker*/
+            l_cstr_index->tile_index[it_tile].marknum =
+                p_j2k->cstr_index->tile_index[it_tile].marknum;
+
+            l_cstr_index->tile_index[it_tile].marker =
+                (opj_marker_info_t*)opj_malloc(l_cstr_index->tile_index[it_tile].marknum *
+                                               sizeof(opj_marker_info_t));
+
+            if (!l_cstr_index->tile_index[it_tile].marker) {
+                OPJ_UINT32 it_tile_free;
+
+                for (it_tile_free = 0; it_tile_free < it_tile; it_tile_free++) {
+                    opj_free(l_cstr_index->tile_index[it_tile_free].marker);
+                }
+
+                opj_free(l_cstr_index->tile_index);
+                opj_free(l_cstr_index->marker);
+                opj_free(l_cstr_index);
+                return NULL;
+            }
+
+            if (p_j2k->cstr_index->tile_index[it_tile].marker)
+                memcpy(l_cstr_index->tile_index[it_tile].marker,
+                       p_j2k->cstr_index->tile_index[it_tile].marker,
+                       l_cstr_index->tile_index[it_tile].marknum * sizeof(opj_marker_info_t));
+            else {
+                opj_free(l_cstr_index->tile_index[it_tile].marker);
+                l_cstr_index->tile_index[it_tile].marker = NULL;
+            }
+
+            /* Tile part index*/
+            l_cstr_index->tile_index[it_tile].nb_tps =
+                p_j2k->cstr_index->tile_index[it_tile].nb_tps;
+
+            l_cstr_index->tile_index[it_tile].tp_index =
+                (opj_tp_index_t*)opj_malloc(l_cstr_index->tile_index[it_tile].nb_tps * sizeof(
+                                                opj_tp_index_t));
+
+            if (!l_cstr_index->tile_index[it_tile].tp_index) {
+                OPJ_UINT32 it_tile_free;
+
+                for (it_tile_free = 0; it_tile_free < it_tile; it_tile_free++) {
+                    opj_free(l_cstr_index->tile_index[it_tile_free].marker);
+                    opj_free(l_cstr_index->tile_index[it_tile_free].tp_index);
+                }
+
+                opj_free(l_cstr_index->tile_index);
+                opj_free(l_cstr_index->marker);
+                opj_free(l_cstr_index);
+                return NULL;
+            }
+
+            if (p_j2k->cstr_index->tile_index[it_tile].tp_index) {
+                memcpy(l_cstr_index->tile_index[it_tile].tp_index,
+                       p_j2k->cstr_index->tile_index[it_tile].tp_index,
+                       l_cstr_index->tile_index[it_tile].nb_tps * sizeof(opj_tp_index_t));
+            } else {
+                opj_free(l_cstr_index->tile_index[it_tile].tp_index);
+                l_cstr_index->tile_index[it_tile].tp_index = NULL;
+            }
+
+            /* Packet index (NOT USED)*/
+            l_cstr_index->tile_index[it_tile].nb_packet = 0;
+            l_cstr_index->tile_index[it_tile].packet_index = NULL;
+
+        }
+    }
+
+    return l_cstr_index;
+}
+
+static OPJ_BOOL opj_j2k_allocate_tile_element_cstr_index(opj_j2k_t *p_j2k)
+{
+    OPJ_UINT32 it_tile = 0;
+
+    p_j2k->cstr_index->nb_of_tiles = p_j2k->m_cp.tw * p_j2k->m_cp.th;
+    p_j2k->cstr_index->tile_index = (opj_tile_index_t*)opj_calloc(
+                                        p_j2k->cstr_index->nb_of_tiles, sizeof(opj_tile_index_t));
+    if (!p_j2k->cstr_index->tile_index) {
+        return OPJ_FALSE;
+    }
+
+    for (it_tile = 0; it_tile < p_j2k->cstr_index->nb_of_tiles; it_tile++) {
+        p_j2k->cstr_index->tile_index[it_tile].maxmarknum = 100;
+        p_j2k->cstr_index->tile_index[it_tile].marknum = 0;
+        p_j2k->cstr_index->tile_index[it_tile].marker = (opj_marker_info_t*)
+                opj_calloc(p_j2k->cstr_index->tile_index[it_tile].maxmarknum,
+                           sizeof(opj_marker_info_t));
+        if (!p_j2k->cstr_index->tile_index[it_tile].marker) {
+            return OPJ_FALSE;
+        }
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_are_all_used_components_decoded(opj_j2k_t *p_j2k,
+        opj_event_mgr_t * p_manager)
+{
+    OPJ_UINT32 compno;
+    OPJ_BOOL decoded_all_used_components = OPJ_TRUE;
+
+    if (p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode) {
+        for (compno = 0;
+                compno < p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode; compno++) {
+            OPJ_UINT32 dec_compno =
+                p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode[compno];
+            if (p_j2k->m_output_image->comps[dec_compno].data == NULL) {
+                opj_event_msg(p_manager, EVT_WARNING, "Failed to decode component %d\n",
+                              dec_compno);
+                decoded_all_used_components = OPJ_FALSE;
+            }
+        }
+    } else {
+        for (compno = 0; compno < p_j2k->m_output_image->numcomps; compno++) {
+            if (p_j2k->m_output_image->comps[compno].data == NULL) {
+                opj_event_msg(p_manager, EVT_WARNING, "Failed to decode component %d\n",
+                              compno);
+                decoded_all_used_components = OPJ_FALSE;
+            }
+        }
+    }
+
+    if (decoded_all_used_components == OPJ_FALSE) {
+        opj_event_msg(p_manager, EVT_ERROR, "Failed to decode all used components\n");
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+
+static OPJ_BOOL opj_j2k_decode_tiles(opj_j2k_t *p_j2k,
+                                     opj_stream_private_t *p_stream,
+                                     opj_event_mgr_t * p_manager)
+{
+    OPJ_BOOL l_go_on = OPJ_TRUE;
+    OPJ_UINT32 l_current_tile_no;
+    OPJ_INT32 l_tile_x0, l_tile_y0, l_tile_x1, l_tile_y1;
+    OPJ_UINT32 l_nb_comps;
+    OPJ_UINT32 nr_tiles = 0;
+
+    /* Particular case for whole single tile decoding */
+    /* We can avoid allocating intermediate tile buffers */
+    if (p_j2k->m_cp.tw == 1 && p_j2k->m_cp.th == 1 &&
+            p_j2k->m_cp.tx0 == 0 && p_j2k->m_cp.ty0 == 0 &&
+            p_j2k->m_output_image->x0 == 0 &&
+            p_j2k->m_output_image->y0 == 0 &&
+            p_j2k->m_output_image->x1 == p_j2k->m_cp.tdx &&
+            p_j2k->m_output_image->y1 == p_j2k->m_cp.tdy) {
+        OPJ_UINT32 i;
+        if (! opj_j2k_read_tile_header(p_j2k,
+                                       &l_current_tile_no,
+                                       NULL,
+                                       &l_tile_x0, &l_tile_y0,
+                                       &l_tile_x1, &l_tile_y1,
+                                       &l_nb_comps,
+                                       &l_go_on,
+                                       p_stream,
+                                       p_manager)) {
+            return OPJ_FALSE;
+        }
+
+        if (! opj_j2k_decode_tile(p_j2k, l_current_tile_no, NULL, 0,
+                                  p_stream, p_manager)) {
+            opj_event_msg(p_manager, EVT_ERROR, "Failed to decode tile 1/1\n");
+            return OPJ_FALSE;
+        }
+
+        /* Transfer TCD data to output image data */
+        for (i = 0; i < p_j2k->m_output_image->numcomps; i++) {
+            opj_image_data_free(p_j2k->m_output_image->comps[i].data);
+            p_j2k->m_output_image->comps[i].data =
+                p_j2k->m_tcd->tcd_image->tiles->comps[i].data;
+            p_j2k->m_output_image->comps[i].resno_decoded =
+                p_j2k->m_tcd->image->comps[i].resno_decoded;
+            p_j2k->m_tcd->tcd_image->tiles->comps[i].data = NULL;
+        }
+
+        return OPJ_TRUE;
+    }
+
+    for (;;) {
+        if (p_j2k->m_cp.tw == 1 && p_j2k->m_cp.th == 1 &&
+                p_j2k->m_cp.tcps[0].m_data != NULL) {
+            l_current_tile_no = 0;
+            p_j2k->m_current_tile_number = 0;
+            p_j2k->m_specific_param.m_decoder.m_state |= J2K_STATE_DATA;
+        } else {
+            if (! opj_j2k_read_tile_header(p_j2k,
+                                           &l_current_tile_no,
+                                           NULL,
+                                           &l_tile_x0, &l_tile_y0,
+                                           &l_tile_x1, &l_tile_y1,
+                                           &l_nb_comps,
+                                           &l_go_on,
+                                           p_stream,
+                                           p_manager)) {
+                return OPJ_FALSE;
+            }
+
+            if (! l_go_on) {
+                break;
+            }
+        }
+
+        if (! opj_j2k_decode_tile(p_j2k, l_current_tile_no, NULL, 0,
+                                  p_stream, p_manager)) {
+            opj_event_msg(p_manager, EVT_ERROR, "Failed to decode tile %d/%d\n",
+                          l_current_tile_no + 1, p_j2k->m_cp.th * p_j2k->m_cp.tw);
+            return OPJ_FALSE;
+        }
+
+        opj_event_msg(p_manager, EVT_INFO, "Tile %d/%d has been decoded.\n",
+                      l_current_tile_no + 1, p_j2k->m_cp.th * p_j2k->m_cp.tw);
+
+        if (! opj_j2k_update_image_data(p_j2k->m_tcd,
+                                        p_j2k->m_output_image)) {
+            return OPJ_FALSE;
+        }
+
+        if (p_j2k->m_cp.tw == 1 && p_j2k->m_cp.th == 1 &&
+                !(p_j2k->m_output_image->x0 == p_j2k->m_private_image->x0 &&
+                  p_j2k->m_output_image->y0 == p_j2k->m_private_image->y0 &&
+                  p_j2k->m_output_image->x1 == p_j2k->m_private_image->x1 &&
+                  p_j2k->m_output_image->y1 == p_j2k->m_private_image->y1)) {
+            /* Keep current tcp data */
+        } else {
+            opj_j2k_tcp_data_destroy(&p_j2k->m_cp.tcps[l_current_tile_no]);
+        }
+
+        opj_event_msg(p_manager, EVT_INFO,
+                      "Image data has been updated with tile %d.\n\n", l_current_tile_no + 1);
+
+        if (opj_stream_get_number_byte_left(p_stream) == 0
+                && p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_NEOC) {
+            break;
+        }
+        if (++nr_tiles ==  p_j2k->m_cp.th * p_j2k->m_cp.tw) {
+            break;
+        }
+    }
+
+    if (! opj_j2k_are_all_used_components_decoded(p_j2k, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Sets up the procedures to do on decoding data. Developers wanting to extend the library can add their own reading procedures.
+ */
+static OPJ_BOOL opj_j2k_setup_decoding(opj_j2k_t *p_j2k,
+                                       opj_event_mgr_t * p_manager)
+{
+    /* preconditions*/
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_decode_tiles, p_manager)) {
+        return OPJ_FALSE;
+    }
+    /* DEVELOPER CORNER, add your custom procedures */
+
+    return OPJ_TRUE;
+}
+
+/*
+ * Read and decode one tile.
+ */
+static OPJ_BOOL opj_j2k_decode_one_tile(opj_j2k_t *p_j2k,
+                                        opj_stream_private_t *p_stream,
+                                        opj_event_mgr_t * p_manager)
+{
+    OPJ_BOOL l_go_on = OPJ_TRUE;
+    OPJ_UINT32 l_current_tile_no;
+    OPJ_UINT32 l_tile_no_to_dec;
+    OPJ_INT32 l_tile_x0, l_tile_y0, l_tile_x1, l_tile_y1;
+    OPJ_UINT32 l_nb_comps;
+    OPJ_UINT32 l_nb_tiles;
+    OPJ_UINT32 i;
+
+    /*Allocate and initialize some elements of codestrem index if not already done*/
+    if (!p_j2k->cstr_index->tile_index) {
+        if (!opj_j2k_allocate_tile_element_cstr_index(p_j2k)) {
+            return OPJ_FALSE;
+        }
+    }
+    /* Move into the codestream to the first SOT used to decode the desired tile */
+    l_tile_no_to_dec = (OPJ_UINT32)
+                       p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec;
+    if (p_j2k->cstr_index->tile_index)
+        if (p_j2k->cstr_index->tile_index->tp_index) {
+            if (! p_j2k->cstr_index->tile_index[l_tile_no_to_dec].nb_tps) {
+                /* the index for this tile has not been built,
+                 *  so move to the last SOT read */
+                if (!(opj_stream_read_seek(p_stream,
+                                           p_j2k->m_specific_param.m_decoder.m_last_sot_read_pos + 2, p_manager))) {
+                    opj_event_msg(p_manager, EVT_ERROR, "Problem with seek function\n");
+                    return OPJ_FALSE;
+                }
+            } else {
+                if (!(opj_stream_read_seek(p_stream,
+                                           p_j2k->cstr_index->tile_index[l_tile_no_to_dec].tp_index[0].start_pos + 2,
+                                           p_manager))) {
+                    opj_event_msg(p_manager, EVT_ERROR, "Problem with seek function\n");
+                    return OPJ_FALSE;
+                }
+            }
+            /* Special case if we have previously read the EOC marker (if the previous tile getted is the last ) */
+            if (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_EOC) {
+                p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_TPHSOT;
+            }
+        }
+
+    /* Reset current tile part number for all tiles, and not only the one */
+    /* of interest. */
+    /* Not completely sure this is always correct but required for */
+    /* ./build/bin/j2k_random_tile_access ./build/tests/tte1.j2k */
+    l_nb_tiles = p_j2k->m_cp.tw * p_j2k->m_cp.th;
+    for (i = 0; i < l_nb_tiles; ++i) {
+        p_j2k->m_cp.tcps[i].m_current_tile_part_number = -1;
+    }
+
+    for (;;) {
+        if (! opj_j2k_read_tile_header(p_j2k,
+                                       &l_current_tile_no,
+                                       NULL,
+                                       &l_tile_x0, &l_tile_y0,
+                                       &l_tile_x1, &l_tile_y1,
+                                       &l_nb_comps,
+                                       &l_go_on,
+                                       p_stream,
+                                       p_manager)) {
+            return OPJ_FALSE;
+        }
+
+        if (! l_go_on) {
+            break;
+        }
+
+        if (! opj_j2k_decode_tile(p_j2k, l_current_tile_no, NULL, 0,
+                                  p_stream, p_manager)) {
+            return OPJ_FALSE;
+        }
+        opj_event_msg(p_manager, EVT_INFO, "Tile %d/%d has been decoded.\n",
+                      l_current_tile_no + 1, p_j2k->m_cp.th * p_j2k->m_cp.tw);
+
+        if (! opj_j2k_update_image_data(p_j2k->m_tcd,
+                                        p_j2k->m_output_image)) {
+            return OPJ_FALSE;
+        }
+        opj_j2k_tcp_data_destroy(&p_j2k->m_cp.tcps[l_current_tile_no]);
+
+        opj_event_msg(p_manager, EVT_INFO,
+                      "Image data has been updated with tile %d.\n\n", l_current_tile_no + 1);
+
+        if (l_current_tile_no == l_tile_no_to_dec) {
+            /* move into the codestream to the first SOT (FIXME or not move?)*/
+            if (!(opj_stream_read_seek(p_stream, p_j2k->cstr_index->main_head_end + 2,
+                                       p_manager))) {
+                opj_event_msg(p_manager, EVT_ERROR, "Problem with seek function\n");
+                return OPJ_FALSE;
+            }
+            break;
+        } else {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "Tile read, decoded and updated is not the desired one (%d vs %d).\n",
+                          l_current_tile_no + 1, l_tile_no_to_dec + 1);
+        }
+
+    }
+
+    if (! opj_j2k_are_all_used_components_decoded(p_j2k, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Sets up the procedures to do on decoding one tile. Developers wanting to extend the library can add their own reading procedures.
+ */
+static OPJ_BOOL opj_j2k_setup_decoding_tile(opj_j2k_t *p_j2k,
+        opj_event_mgr_t * p_manager)
+{
+    /* preconditions*/
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_decode_one_tile, p_manager)) {
+        return OPJ_FALSE;
+    }
+    /* DEVELOPER CORNER, add your custom procedures */
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_move_data_from_codec_to_output_image(opj_j2k_t * p_j2k,
+        opj_image_t * p_image)
+{
+    OPJ_UINT32 compno;
+
+    /* Move data and copy one information from codec to output image*/
+    if (p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode > 0) {
+        opj_image_comp_t* newcomps =
+            (opj_image_comp_t*) opj_malloc(
+                p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode *
+                sizeof(opj_image_comp_t));
+        if (newcomps == NULL) {
+            opj_image_destroy(p_j2k->m_private_image);
+            p_j2k->m_private_image = NULL;
+            return OPJ_FALSE;
+        }
+        for (compno = 0; compno < p_image->numcomps; compno++) {
+            opj_image_data_free(p_image->comps[compno].data);
+            p_image->comps[compno].data = NULL;
+        }
+        for (compno = 0;
+                compno < p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode; compno++) {
+            OPJ_UINT32 src_compno =
+                p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode[compno];
+            memcpy(&(newcomps[compno]),
+                   &(p_j2k->m_output_image->comps[src_compno]),
+                   sizeof(opj_image_comp_t));
+            newcomps[compno].resno_decoded =
+                p_j2k->m_output_image->comps[src_compno].resno_decoded;
+            newcomps[compno].data = p_j2k->m_output_image->comps[src_compno].data;
+            p_j2k->m_output_image->comps[src_compno].data = NULL;
+        }
+        for (compno = 0; compno < p_image->numcomps; compno++) {
+            assert(p_j2k->m_output_image->comps[compno].data == NULL);
+            opj_image_data_free(p_j2k->m_output_image->comps[compno].data);
+            p_j2k->m_output_image->comps[compno].data = NULL;
+        }
+        p_image->numcomps = p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode;
+        opj_free(p_image->comps);
+        p_image->comps = newcomps;
+    } else {
+        for (compno = 0; compno < p_image->numcomps; compno++) {
+            p_image->comps[compno].resno_decoded =
+                p_j2k->m_output_image->comps[compno].resno_decoded;
+            opj_image_data_free(p_image->comps[compno].data);
+            p_image->comps[compno].data = p_j2k->m_output_image->comps[compno].data;
+#if 0
+            char fn[256];
+            snprintf(fn, sizeof fn, "/tmp/%d.raw", compno);
+            FILE *debug = fopen(fn, "wb");
+            fwrite(p_image->comps[compno].data, sizeof(OPJ_INT32),
+                   p_image->comps[compno].w * p_image->comps[compno].h, debug);
+            fclose(debug);
+#endif
+            p_j2k->m_output_image->comps[compno].data = NULL;
+        }
+    }
+    return OPJ_TRUE;
+}
+
+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;
+    }
+
+    /* Heuristics to detect sequence opj_read_header(), opj_set_decoded_resolution_factor() */
+    /* and finally opj_decode_image() without manual setting of comps[].factor */
+    /* We could potentially always execute it, if we don't allow people to do */
+    /* opj_read_header(), modify x0,y0,x1,y1 of returned image an call opj_decode_image() */
+    if (p_j2k->m_cp.m_specific_param.m_dec.m_reduce > 0 &&
+            p_j2k->m_private_image != NULL &&
+            p_j2k->m_private_image->numcomps > 0 &&
+            p_j2k->m_private_image->comps[0].factor ==
+            p_j2k->m_cp.m_specific_param.m_dec.m_reduce &&
+            p_image->numcomps > 0 &&
+            p_image->comps[0].factor == 0 &&
+            /* Don't mess with image dimension if the user has allocated it */
+            p_image->comps[0].data == NULL) {
+        OPJ_UINT32 it_comp;
+
+        /* Update the comps[].factor member of the output image with the one */
+        /* of m_reduce */
+        for (it_comp = 0; it_comp < p_image->numcomps; ++it_comp) {
+            p_image->comps[it_comp].factor = p_j2k->m_cp.m_specific_param.m_dec.m_reduce;
+        }
+        if (!opj_j2k_update_image_dimensions(p_image, p_manager)) {
+            return OPJ_FALSE;
+        }
+    }
+
+    if (p_j2k->m_output_image == NULL) {
+        p_j2k->m_output_image = opj_image_create0();
+        if (!(p_j2k->m_output_image)) {
+            return OPJ_FALSE;
+        }
+    }
+    opj_copy_image_header(p_image, p_j2k->m_output_image);
+
+    /* customization of the decoding */
+    if (!opj_j2k_setup_decoding(p_j2k, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* Decode the codestream */
+    if (! opj_j2k_exec(p_j2k, p_j2k->m_procedure_list, p_stream, p_manager)) {
+        opj_image_destroy(p_j2k->m_private_image);
+        p_j2k->m_private_image = NULL;
+        return OPJ_FALSE;
+    }
+
+    /* Move data and copy one information from codec to output image*/
+    return opj_j2k_move_data_from_codec_to_output_image(p_j2k, p_image);
+}
+
+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;
+
+    if (!p_image) {
+        opj_event_msg(p_manager, EVT_ERROR, "We need an image previously created.\n");
+        return OPJ_FALSE;
+    }
+
+    if (p_image->numcomps < p_j2k->m_private_image->numcomps) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Image has less components than codestream.\n");
+        return OPJ_FALSE;
+    }
+
+    if (/*(tile_index < 0) &&*/ (tile_index >= p_j2k->m_cp.tw * p_j2k->m_cp.th)) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Tile index provided by the user is incorrect %d (max = %d) \n", tile_index,
+                      (p_j2k->m_cp.tw * p_j2k->m_cp.th) - 1);
+        return OPJ_FALSE;
+    }
+
+    /* Compute the dimension of the desired tile*/
+    l_tile_x = tile_index % p_j2k->m_cp.tw;
+    l_tile_y = tile_index / p_j2k->m_cp.tw;
+
+    p_image->x0 = l_tile_x * p_j2k->m_cp.tdx + p_j2k->m_cp.tx0;
+    if (p_image->x0 < p_j2k->m_private_image->x0) {
+        p_image->x0 = p_j2k->m_private_image->x0;
+    }
+    p_image->x1 = (l_tile_x + 1) * p_j2k->m_cp.tdx + p_j2k->m_cp.tx0;
+    if (p_image->x1 > p_j2k->m_private_image->x1) {
+        p_image->x1 = p_j2k->m_private_image->x1;
+    }
+
+    p_image->y0 = l_tile_y * p_j2k->m_cp.tdy + p_j2k->m_cp.ty0;
+    if (p_image->y0 < p_j2k->m_private_image->y0) {
+        p_image->y0 = p_j2k->m_private_image->y0;
+    }
+    p_image->y1 = (l_tile_y + 1) * p_j2k->m_cp.tdy + p_j2k->m_cp.ty0;
+    if (p_image->y1 > p_j2k->m_private_image->y1) {
+        p_image->y1 = p_j2k->m_private_image->y1;
+    }
+
+    l_img_comp = p_image->comps;
+    for (compno = 0; compno < p_j2k->m_private_image->numcomps; ++compno) {
+        OPJ_INT32 l_comp_x1, l_comp_y1;
+
+        l_img_comp->factor = p_j2k->m_private_image->comps[compno].factor;
+
+        l_img_comp->x0 = opj_uint_ceildiv(p_image->x0, l_img_comp->dx);
+        l_img_comp->y0 = opj_uint_ceildiv(p_image->y0, l_img_comp->dy);
+        l_comp_x1 = opj_int_ceildiv((OPJ_INT32)p_image->x1, (OPJ_INT32)l_img_comp->dx);
+        l_comp_y1 = opj_int_ceildiv((OPJ_INT32)p_image->y1, (OPJ_INT32)l_img_comp->dy);
+
+        l_img_comp->w = (OPJ_UINT32)(opj_int_ceildivpow2(l_comp_x1,
+                                     (OPJ_INT32)l_img_comp->factor) - opj_int_ceildivpow2((OPJ_INT32)l_img_comp->x0,
+                                             (OPJ_INT32)l_img_comp->factor));
+        l_img_comp->h = (OPJ_UINT32)(opj_int_ceildivpow2(l_comp_y1,
+                                     (OPJ_INT32)l_img_comp->factor) - opj_int_ceildivpow2((OPJ_INT32)l_img_comp->y0,
+                                             (OPJ_INT32)l_img_comp->factor));
+
+        l_img_comp++;
+    }
+
+    if (p_image->numcomps > p_j2k->m_private_image->numcomps) {
+        /* Can happen when calling repeatdly opj_get_decoded_tile() on an
+         * image with a color palette, where color palette expansion is done
+         * later in jp2.c */
+        for (compno = p_j2k->m_private_image->numcomps; compno < p_image->numcomps;
+                ++compno) {
+            opj_image_data_free(p_image->comps[compno].data);
+            p_image->comps[compno].data = NULL;
+        }
+        p_image->numcomps = p_j2k->m_private_image->numcomps;
+    }
+
+    /* Destroy the previous output image*/
+    if (p_j2k->m_output_image) {
+        opj_image_destroy(p_j2k->m_output_image);
+    }
+
+    /* Create the output image from the information previously computed*/
+    p_j2k->m_output_image = opj_image_create0();
+    if (!(p_j2k->m_output_image)) {
+        return OPJ_FALSE;
+    }
+    opj_copy_image_header(p_image, p_j2k->m_output_image);
+
+    p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec = (OPJ_INT32)tile_index;
+
+    /* customization of the decoding */
+    if (!opj_j2k_setup_decoding_tile(p_j2k, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* Decode the codestream */
+    if (! opj_j2k_exec(p_j2k, p_j2k->m_procedure_list, p_stream, p_manager)) {
+        opj_image_destroy(p_j2k->m_private_image);
+        p_j2k->m_private_image = NULL;
+        return OPJ_FALSE;
+    }
+
+    /* Move data and copy one information from codec to output image*/
+    return opj_j2k_move_data_from_codec_to_output_image(p_j2k, p_image);
+}
+
+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;
+
+    if (p_j2k->m_private_image) {
+        if (p_j2k->m_private_image->comps) {
+            if (p_j2k->m_specific_param.m_decoder.m_default_tcp) {
+                if (p_j2k->m_specific_param.m_decoder.m_default_tcp->tccps) {
+                    for (it_comp = 0 ; it_comp < p_j2k->m_private_image->numcomps; it_comp++) {
+                        OPJ_UINT32 max_res =
+                            p_j2k->m_specific_param.m_decoder.m_default_tcp->tccps[it_comp].numresolutions;
+                        if (res_factor >= max_res) {
+                            opj_event_msg(p_manager, EVT_ERROR,
+                                          "Resolution factor is greater than the maximum resolution in the component.\n");
+                            return OPJ_FALSE;
+                        }
+                        p_j2k->m_private_image->comps[it_comp].factor = res_factor;
+                    }
+                    return OPJ_TRUE;
+                }
+            }
+        }
+    }
+
+    return OPJ_FALSE;
+}
+
+/* ----------------------------------------------------------------------- */
+
+OPJ_BOOL opj_j2k_encoder_set_extra_options(
+    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) {
+        return OPJ_TRUE;
+    }
+
+    for (p_option_iter = p_options; *p_option_iter != NULL; ++p_option_iter) {
+        if (strncmp(*p_option_iter, "PLT=", 4) == 0) {
+            if (strcmp(*p_option_iter, "PLT=YES") == 0) {
+                p_j2k->m_specific_param.m_encoder.m_PLT = OPJ_TRUE;
+            } else if (strcmp(*p_option_iter, "PLT=NO") == 0) {
+                p_j2k->m_specific_param.m_encoder.m_PLT = OPJ_FALSE;
+            } else {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Invalid value for option: %s.\n", *p_option_iter);
+                return OPJ_FALSE;
+            }
+        } else if (strncmp(*p_option_iter, "TLM=", 4) == 0) {
+            if (strcmp(*p_option_iter, "TLM=YES") == 0) {
+                p_j2k->m_specific_param.m_encoder.m_TLM = OPJ_TRUE;
+            } else if (strcmp(*p_option_iter, "TLM=NO") == 0) {
+                p_j2k->m_specific_param.m_encoder.m_TLM = OPJ_FALSE;
+            } else {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Invalid value for option: %s.\n", *p_option_iter);
+                return OPJ_FALSE;
+            }
+        } else if (strncmp(*p_option_iter, "GUARD_BITS=", strlen("GUARD_BITS=")) == 0) {
+            OPJ_UINT32 tileno;
+            opj_cp_t *cp = cp = &(p_j2k->m_cp);
+
+            int numgbits = atoi(*p_option_iter + strlen("GUARD_BITS="));
+            if (numgbits < 0 || numgbits > 7) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Invalid value for option: %s. Should be in [0,7]\n", *p_option_iter);
+                return OPJ_FALSE;
+            }
+
+            for (tileno = 0; tileno < cp->tw * cp->th; tileno++) {
+                OPJ_UINT32 i;
+                opj_tcp_t *tcp = &cp->tcps[tileno];
+                for (i = 0; i < p_j2k->m_specific_param.m_encoder.m_nb_comps; i++) {
+                    opj_tccp_t *tccp = &tcp->tccps[i];
+                    tccp->numgbits = (OPJ_UINT32)numgbits;
+                }
+            }
+        } else {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Invalid option: %s.\n", *p_option_iter);
+            return OPJ_FALSE;
+        }
+    }
+
+    return OPJ_TRUE;
+}
+
+/* ----------------------------------------------------------------------- */
+
+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;
+    OPJ_BYTE * l_current_data = 00;
+    OPJ_BOOL l_reuse_data = OPJ_FALSE;
+    opj_tcd_t* p_tcd = 00;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_stream != 00);
+    assert(p_manager != 00);
+
+    p_tcd = p_j2k->m_tcd;
+
+    l_nb_tiles = p_j2k->m_cp.th * p_j2k->m_cp.tw;
+    if (l_nb_tiles == 1) {
+        l_reuse_data = OPJ_TRUE;
+#ifdef __SSE__
+        for (j = 0; j < p_j2k->m_tcd->image->numcomps; ++j) {
+            opj_image_comp_t * l_img_comp = p_tcd->image->comps + j;
+            if (((size_t)l_img_comp->data & 0xFU) !=
+                    0U) { /* tile data shall be aligned on 16 bytes */
+                l_reuse_data = OPJ_FALSE;
+            }
+        }
+#endif
+    }
+    for (i = 0; i < l_nb_tiles; ++i) {
+        if (! opj_j2k_pre_write_tile(p_j2k, i, p_stream, p_manager)) {
+            if (l_current_data) {
+                opj_free(l_current_data);
+            }
+            return OPJ_FALSE;
+        }
+
+        /* if we only have one tile, then simply set tile component data equal to image component data */
+        /* otherwise, allocate the data */
+        for (j = 0; j < p_j2k->m_tcd->image->numcomps; ++j) {
+            opj_tcd_tilecomp_t* l_tilec = p_tcd->tcd_image->tiles->comps + j;
+            if (l_reuse_data) {
+                opj_image_comp_t * l_img_comp = p_tcd->image->comps + j;
+                l_tilec->data  =  l_img_comp->data;
+                l_tilec->ownsData = OPJ_FALSE;
+            } else {
+                if (! opj_alloc_tile_component_data(l_tilec)) {
+                    opj_event_msg(p_manager, EVT_ERROR, "Error allocating tile component data.");
+                    if (l_current_data) {
+                        opj_free(l_current_data);
+                    }
+                    return OPJ_FALSE;
+                }
+            }
+        }
+        l_current_tile_size = opj_tcd_get_encoder_input_buffer_size(p_j2k->m_tcd);
+        if (!l_reuse_data) {
+            if (l_current_tile_size > l_max_tile_size) {
+                OPJ_BYTE *l_new_current_data = (OPJ_BYTE *) opj_realloc(l_current_data,
+                                               l_current_tile_size);
+                if (! l_new_current_data) {
+                    if (l_current_data) {
+                        opj_free(l_current_data);
+                    }
+                    opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to encode all tiles\n");
+                    return OPJ_FALSE;
+                }
+                l_current_data = l_new_current_data;
+                l_max_tile_size = l_current_tile_size;
+            }
+            if (l_current_data == NULL) {
+                /* Should not happen in practice, but will avoid Coverity to */
+                /* complain about a null pointer dereference */
+                assert(0);
+                return OPJ_FALSE;
+            }
+
+            /* copy image data (32 bit) to l_current_data as contiguous, all-component, zero offset buffer */
+            /* 32 bit components @ 8 bit precision get converted to 8 bit */
+            /* 32 bit components @ 16 bit precision get converted to 16 bit */
+            opj_j2k_get_tile_data(p_j2k->m_tcd, l_current_data);
+
+            /* now copy this data into the tile component */
+            if (! opj_tcd_copy_tile_data(p_j2k->m_tcd, l_current_data,
+                                         l_current_tile_size)) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Size mismatch between tile data and sent data.");
+                opj_free(l_current_data);
+                return OPJ_FALSE;
+            }
+        }
+
+        if (! opj_j2k_post_write_tile(p_j2k, p_stream, p_manager)) {
+            if (l_current_data) {
+                opj_free(l_current_data);
+            }
+            return OPJ_FALSE;
+        }
+    }
+
+    if (l_current_data) {
+        opj_free(l_current_data);
+    }
+    return OPJ_TRUE;
+}
+
+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;
+    }
+
+    if (! opj_j2k_exec(p_j2k, p_j2k->m_procedure_list, p_stream, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+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);
+    assert(p_manager != 00);
+
+    p_j2k->m_private_image = opj_image_create0();
+    if (! p_j2k->m_private_image) {
+        opj_event_msg(p_manager, EVT_ERROR, "Failed to allocate image header.");
+        return OPJ_FALSE;
+    }
+    opj_copy_image_header(p_image, p_j2k->m_private_image);
+
+    /* TODO_MSD: Find a better way */
+    if (p_image->comps) {
+        OPJ_UINT32 it_comp;
+        for (it_comp = 0 ; it_comp < p_image->numcomps; it_comp++) {
+            if (p_image->comps[it_comp].data) {
+                p_j2k->m_private_image->comps[it_comp].data = p_image->comps[it_comp].data;
+                p_image->comps[it_comp].data = NULL;
+
+            }
+        }
+    }
+
+    /* customization of the validation */
+    if (! opj_j2k_setup_encoding_validation(p_j2k, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* validation of the parameters codec */
+    if (! opj_j2k_exec(p_j2k, p_j2k->m_validation_list, p_stream, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* customization of the encoding */
+    if (! opj_j2k_setup_header_writing(p_j2k, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* write header */
+    if (! opj_j2k_exec(p_j2k, p_j2k->m_procedure_list, p_stream, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_pre_write_tile(opj_j2k_t * p_j2k,
+                                       OPJ_UINT32 p_tile_index,
+                                       opj_stream_private_t *p_stream,
+                                       opj_event_mgr_t * p_manager)
+{
+    (void)p_stream;
+    if (p_tile_index != p_j2k->m_current_tile_number) {
+        opj_event_msg(p_manager, EVT_ERROR, "The given tile index does not match.");
+        return OPJ_FALSE;
+    }
+
+    opj_event_msg(p_manager, EVT_INFO, "tile number %d / %d\n",
+                  p_j2k->m_current_tile_number + 1, p_j2k->m_cp.tw * p_j2k->m_cp.th);
+
+    p_j2k->m_specific_param.m_encoder.m_current_tile_part_number = 0;
+    p_j2k->m_tcd->cur_totnum_tp = p_j2k->m_cp.tcps[p_tile_index].m_nb_tile_parts;
+    p_j2k->m_specific_param.m_encoder.m_current_poc_tile_part_number = 0;
+
+    /* initialisation before tile encoding  */
+    if (! opj_tcd_init_encode_tile(p_j2k->m_tcd, p_j2k->m_current_tile_number,
+                                   p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static void opj_get_tile_dimensions(opj_image_t * l_image,
+                                    opj_tcd_tilecomp_t * l_tilec,
+                                    opj_image_comp_t * l_img_comp,
+                                    OPJ_UINT32* l_size_comp,
+                                    OPJ_UINT32* l_width,
+                                    OPJ_UINT32* l_height,
+                                    OPJ_UINT32* l_offset_x,
+                                    OPJ_UINT32* l_offset_y,
+                                    OPJ_UINT32* l_image_width,
+                                    OPJ_UINT32* l_stride,
+                                    OPJ_UINT32* l_tile_offset)
+{
+    OPJ_UINT32 l_remaining;
+    *l_size_comp = l_img_comp->prec >> 3; /* (/8) */
+    l_remaining = l_img_comp->prec & 7;  /* (%8) */
+    if (l_remaining) {
+        *l_size_comp += 1;
+    }
+
+    if (*l_size_comp == 3) {
+        *l_size_comp = 4;
+    }
+
+    *l_width  = (OPJ_UINT32)(l_tilec->x1 - l_tilec->x0);
+    *l_height = (OPJ_UINT32)(l_tilec->y1 - l_tilec->y0);
+    *l_offset_x = opj_uint_ceildiv(l_image->x0, l_img_comp->dx);
+    *l_offset_y = opj_uint_ceildiv(l_image->y0, l_img_comp->dy);
+    *l_image_width = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)l_image->x1 -
+                     (OPJ_INT32)l_image->x0, (OPJ_INT32)l_img_comp->dx);
+    *l_stride = *l_image_width - *l_width;
+    *l_tile_offset = ((OPJ_UINT32)l_tilec->x0 - *l_offset_x) + ((
+                         OPJ_UINT32)l_tilec->y0 - *l_offset_y) * *l_image_width;
+}
+
+static void opj_j2k_get_tile_data(opj_tcd_t * p_tcd, OPJ_BYTE * p_data)
+{
+    OPJ_UINT32 i, j, k = 0;
+
+    for (i = 0; i < p_tcd->image->numcomps; ++i) {
+        opj_image_t * l_image =  p_tcd->image;
+        OPJ_INT32 * l_src_ptr;
+        opj_tcd_tilecomp_t * l_tilec = p_tcd->tcd_image->tiles->comps + i;
+        opj_image_comp_t * l_img_comp = l_image->comps + i;
+        OPJ_UINT32 l_size_comp, l_width, l_height, l_offset_x, l_offset_y,
+                   l_image_width, l_stride, l_tile_offset;
+
+        opj_get_tile_dimensions(l_image,
+                                l_tilec,
+                                l_img_comp,
+                                &l_size_comp,
+                                &l_width,
+                                &l_height,
+                                &l_offset_x,
+                                &l_offset_y,
+                                &l_image_width,
+                                &l_stride,
+                                &l_tile_offset);
+
+        l_src_ptr = l_img_comp->data + l_tile_offset;
+
+        switch (l_size_comp) {
+        case 1: {
+            OPJ_CHAR * l_dest_ptr = (OPJ_CHAR*) p_data;
+            if (l_img_comp->sgnd) {
+                for (j = 0; j < l_height; ++j) {
+                    for (k = 0; k < l_width; ++k) {
+                        *(l_dest_ptr) = (OPJ_CHAR)(*l_src_ptr);
+                        ++l_dest_ptr;
+                        ++l_src_ptr;
+                    }
+                    l_src_ptr += l_stride;
+                }
+            } else {
+                for (j = 0; j < l_height; ++j) {
+                    for (k = 0; k < l_width; ++k) {
+                        *(l_dest_ptr) = (OPJ_CHAR)((*l_src_ptr) & 0xff);
+                        ++l_dest_ptr;
+                        ++l_src_ptr;
+                    }
+                    l_src_ptr += l_stride;
+                }
+            }
+
+            p_data = (OPJ_BYTE*) l_dest_ptr;
+        }
+        break;
+        case 2: {
+            OPJ_INT16 * l_dest_ptr = (OPJ_INT16 *) p_data;
+            if (l_img_comp->sgnd) {
+                for (j = 0; j < l_height; ++j) {
+                    for (k = 0; k < l_width; ++k) {
+                        *(l_dest_ptr++) = (OPJ_INT16)(*(l_src_ptr++));
+                    }
+                    l_src_ptr += l_stride;
+                }
+            } else {
+                for (j = 0; j < l_height; ++j) {
+                    for (k = 0; k < l_width; ++k) {
+                        *(l_dest_ptr++) = (OPJ_INT16)((*(l_src_ptr++)) & 0xffff);
+                    }
+                    l_src_ptr += l_stride;
+                }
+            }
+
+            p_data = (OPJ_BYTE*) l_dest_ptr;
+        }
+        break;
+        case 4: {
+            OPJ_INT32 * l_dest_ptr = (OPJ_INT32 *) p_data;
+            for (j = 0; j < l_height; ++j) {
+                for (k = 0; k < l_width; ++k) {
+                    *(l_dest_ptr++) = *(l_src_ptr++);
+                }
+                l_src_ptr += l_stride;
+            }
+
+            p_data = (OPJ_BYTE*) l_dest_ptr;
+        }
+        break;
+        }
+    }
+}
+
+static OPJ_BOOL opj_j2k_post_write_tile(opj_j2k_t * p_j2k,
+                                        opj_stream_private_t *p_stream,
+                                        opj_event_mgr_t * p_manager)
+{
+    OPJ_UINT32 l_nb_bytes_written;
+    OPJ_BYTE * l_current_data = 00;
+    OPJ_UINT32 l_tile_size = 0;
+    OPJ_UINT32 l_available_data;
+
+    /* preconditions */
+    assert(p_j2k->m_specific_param.m_encoder.m_encoded_tile_data);
+
+    l_tile_size = p_j2k->m_specific_param.m_encoder.m_encoded_tile_size;
+    l_available_data = l_tile_size;
+    l_current_data = p_j2k->m_specific_param.m_encoder.m_encoded_tile_data;
+
+    l_nb_bytes_written = 0;
+    if (! opj_j2k_write_first_tile_part(p_j2k, l_current_data, &l_nb_bytes_written,
+                                        l_available_data, p_stream, p_manager)) {
+        return OPJ_FALSE;
+    }
+    l_current_data += l_nb_bytes_written;
+    l_available_data -= l_nb_bytes_written;
+
+    l_nb_bytes_written = 0;
+    if (! opj_j2k_write_all_tile_parts(p_j2k, l_current_data, &l_nb_bytes_written,
+                                       l_available_data, p_stream, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    l_available_data -= l_nb_bytes_written;
+    l_nb_bytes_written = l_tile_size - l_available_data;
+
+    if (opj_stream_write_data(p_stream,
+                              p_j2k->m_specific_param.m_encoder.m_encoded_tile_data,
+                              l_nb_bytes_written, p_manager) != l_nb_bytes_written) {
+        return OPJ_FALSE;
+    }
+
+    ++p_j2k->m_current_tile_number;
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_setup_end_compress(opj_j2k_t *p_j2k,
+        opj_event_mgr_t * p_manager)
+{
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    /* DEVELOPER CORNER, insert your custom procedures */
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_write_eoc, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    if (p_j2k->m_specific_param.m_encoder.m_TLM) {
+        if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                               (opj_procedure)opj_j2k_write_updated_tlm, p_manager)) {
+            return OPJ_FALSE;
+        }
+    }
+
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_write_epc, p_manager)) {
+        return OPJ_FALSE;
+    }
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_end_encoding, p_manager)) {
+        return OPJ_FALSE;
+    }
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_destroy_header_memory, p_manager)) {
+        return OPJ_FALSE;
+    }
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_setup_encoding_validation(opj_j2k_t *p_j2k,
+        opj_event_mgr_t * p_manager)
+{
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    if (! opj_procedure_list_add_procedure(p_j2k->m_validation_list,
+                                           (opj_procedure)opj_j2k_build_encoder, p_manager)) {
+        return OPJ_FALSE;
+    }
+    if (! opj_procedure_list_add_procedure(p_j2k->m_validation_list,
+                                           (opj_procedure)opj_j2k_encoding_validation, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* DEVELOPER CORNER, add your custom validation procedure */
+    if (! opj_procedure_list_add_procedure(p_j2k->m_validation_list,
+                                           (opj_procedure)opj_j2k_mct_validation, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_setup_header_writing(opj_j2k_t *p_j2k,
+        opj_event_mgr_t * p_manager)
+{
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_init_info, p_manager)) {
+        return OPJ_FALSE;
+    }
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_write_soc, p_manager)) {
+        return OPJ_FALSE;
+    }
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_write_siz, p_manager)) {
+        return OPJ_FALSE;
+    }
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_write_cod, p_manager)) {
+        return OPJ_FALSE;
+    }
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_write_qcd, p_manager)) {
+        return OPJ_FALSE;
+    }
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_write_all_coc, p_manager)) {
+        return OPJ_FALSE;
+    }
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_write_all_qcc, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    if (p_j2k->m_specific_param.m_encoder.m_TLM) {
+        if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                               (opj_procedure)opj_j2k_write_tlm, p_manager)) {
+            return OPJ_FALSE;
+        }
+
+        if (p_j2k->m_cp.rsiz == OPJ_PROFILE_CINEMA_4K) {
+            if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                                   (opj_procedure)opj_j2k_write_poc, p_manager)) {
+                return OPJ_FALSE;
+            }
+        }
+    }
+
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_write_regions, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    if (p_j2k->m_cp.comment != 00)  {
+        if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                               (opj_procedure)opj_j2k_write_com, p_manager)) {
+            return OPJ_FALSE;
+        }
+    }
+
+    /* DEVELOPER CORNER, insert your custom procedures */
+    if ((p_j2k->m_cp.rsiz & (OPJ_PROFILE_PART2 | OPJ_EXTENSION_MCT)) ==
+            (OPJ_PROFILE_PART2 | OPJ_EXTENSION_MCT)) {
+        if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                               (opj_procedure)opj_j2k_write_mct_data_group, p_manager)) {
+            return OPJ_FALSE;
+        }
+    }
+    /* End of Developer Corner */
+
+    if (p_j2k->cstr_index) {
+        if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                               (opj_procedure)opj_j2k_get_end_header, p_manager)) {
+            return OPJ_FALSE;
+        }
+    }
+
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_create_tcd, p_manager)) {
+        return OPJ_FALSE;
+    }
+    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
+                                           (opj_procedure)opj_j2k_update_rates, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_first_tile_part(opj_j2k_t *p_j2k,
+        OPJ_BYTE * p_data,
+        OPJ_UINT32 * p_data_written,
+        OPJ_UINT32 total_data_size,
+        opj_stream_private_t *p_stream,
+        struct opj_event_mgr * p_manager)
+{
+    OPJ_UINT32 l_nb_bytes_written = 0;
+    OPJ_UINT32 l_current_nb_bytes_written;
+    OPJ_BYTE * l_begin_data = 00;
+
+    opj_tcd_t * l_tcd = 00;
+    opj_cp_t * l_cp = 00;
+
+    l_tcd = p_j2k->m_tcd;
+    l_cp = &(p_j2k->m_cp);
+
+    l_tcd->cur_pino = 0;
+
+    /*Get number of tile parts*/
+    p_j2k->m_specific_param.m_encoder.m_current_poc_tile_part_number = 0;
+
+    /* INDEX >> */
+    /* << INDEX */
+
+    l_current_nb_bytes_written = 0;
+    l_begin_data = p_data;
+    if (! opj_j2k_write_sot(p_j2k, p_data, total_data_size,
+                            &l_current_nb_bytes_written, p_stream,
+                            p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    l_nb_bytes_written += l_current_nb_bytes_written;
+    p_data += l_current_nb_bytes_written;
+    total_data_size -= l_current_nb_bytes_written;
+
+    if (!OPJ_IS_CINEMA(l_cp->rsiz)) {
+#if 0
+        for (compno = 1; compno < p_j2k->m_private_image->numcomps; compno++) {
+            l_current_nb_bytes_written = 0;
+            opj_j2k_write_coc_in_memory(p_j2k, compno, p_data, &l_current_nb_bytes_written,
+                                        p_manager);
+            l_nb_bytes_written += l_current_nb_bytes_written;
+            p_data += l_current_nb_bytes_written;
+            total_data_size -= l_current_nb_bytes_written;
+
+            l_current_nb_bytes_written = 0;
+            opj_j2k_write_qcc_in_memory(p_j2k, compno, p_data, &l_current_nb_bytes_written,
+                                        p_manager);
+            l_nb_bytes_written += l_current_nb_bytes_written;
+            p_data += l_current_nb_bytes_written;
+            total_data_size -= l_current_nb_bytes_written;
+        }
+#endif
+        if (l_cp->tcps[p_j2k->m_current_tile_number].POC) {
+            l_current_nb_bytes_written = 0;
+            opj_j2k_write_poc_in_memory(p_j2k, p_data, &l_current_nb_bytes_written,
+                                        p_manager);
+            l_nb_bytes_written += l_current_nb_bytes_written;
+            p_data += l_current_nb_bytes_written;
+            total_data_size -= l_current_nb_bytes_written;
+        }
+    }
+
+    l_current_nb_bytes_written = 0;
+    if (! opj_j2k_write_sod(p_j2k, l_tcd, p_data, &l_current_nb_bytes_written,
+                            total_data_size, p_stream, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    l_nb_bytes_written += l_current_nb_bytes_written;
+    * p_data_written = l_nb_bytes_written;
+
+    /* Writing Psot in SOT marker */
+    opj_write_bytes(l_begin_data + 6, l_nb_bytes_written,
+                    4);                                 /* PSOT */
+
+    if (p_j2k->m_specific_param.m_encoder.m_TLM) {
+        opj_j2k_update_tlm(p_j2k, l_nb_bytes_written);
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_all_tile_parts(opj_j2k_t *p_j2k,
+        OPJ_BYTE * p_data,
+        OPJ_UINT32 * p_data_written,
+        OPJ_UINT32 total_data_size,
+        opj_stream_private_t *p_stream,
+        struct opj_event_mgr * p_manager
+                                            )
+{
+    OPJ_UINT32 tilepartno = 0;
+    OPJ_UINT32 l_nb_bytes_written = 0;
+    OPJ_UINT32 l_current_nb_bytes_written;
+    OPJ_UINT32 l_part_tile_size;
+    OPJ_UINT32 tot_num_tp;
+    OPJ_UINT32 pino;
+
+    OPJ_BYTE * l_begin_data;
+    opj_tcp_t *l_tcp = 00;
+    opj_tcd_t * l_tcd = 00;
+    opj_cp_t * l_cp = 00;
+
+    l_tcd = p_j2k->m_tcd;
+    l_cp = &(p_j2k->m_cp);
+    l_tcp = l_cp->tcps + p_j2k->m_current_tile_number;
+
+    /*Get number of tile parts*/
+    tot_num_tp = opj_j2k_get_num_tp(l_cp, 0, p_j2k->m_current_tile_number);
+
+    /* start writing remaining tile parts */
+    ++p_j2k->m_specific_param.m_encoder.m_current_tile_part_number;
+    for (tilepartno = 1; tilepartno < tot_num_tp ; ++tilepartno) {
+        p_j2k->m_specific_param.m_encoder.m_current_poc_tile_part_number = tilepartno;
+        l_current_nb_bytes_written = 0;
+        l_part_tile_size = 0;
+        l_begin_data = p_data;
+
+        if (! opj_j2k_write_sot(p_j2k, p_data,
+                                total_data_size,
+                                &l_current_nb_bytes_written,
+                                p_stream,
+                                p_manager)) {
+            return OPJ_FALSE;
+        }
+
+        l_nb_bytes_written += l_current_nb_bytes_written;
+        p_data += l_current_nb_bytes_written;
+        total_data_size -= l_current_nb_bytes_written;
+        l_part_tile_size += l_current_nb_bytes_written;
+
+        l_current_nb_bytes_written = 0;
+        if (! opj_j2k_write_sod(p_j2k, l_tcd, p_data, &l_current_nb_bytes_written,
+                                total_data_size, p_stream, p_manager)) {
+            return OPJ_FALSE;
+        }
+
+        p_data += l_current_nb_bytes_written;
+        l_nb_bytes_written += l_current_nb_bytes_written;
+        total_data_size -= l_current_nb_bytes_written;
+        l_part_tile_size += l_current_nb_bytes_written;
+
+        /* Writing Psot in SOT marker */
+        opj_write_bytes(l_begin_data + 6, l_part_tile_size,
+                        4);                                   /* PSOT */
+
+        if (p_j2k->m_specific_param.m_encoder.m_TLM) {
+            opj_j2k_update_tlm(p_j2k, l_part_tile_size);
+        }
+
+        ++p_j2k->m_specific_param.m_encoder.m_current_tile_part_number;
+    }
+
+    for (pino = 1; pino <= l_tcp->numpocs; ++pino) {
+        l_tcd->cur_pino = pino;
+
+        /*Get number of tile parts*/
+        tot_num_tp = opj_j2k_get_num_tp(l_cp, pino, p_j2k->m_current_tile_number);
+        for (tilepartno = 0; tilepartno < tot_num_tp ; ++tilepartno) {
+            p_j2k->m_specific_param.m_encoder.m_current_poc_tile_part_number = tilepartno;
+            l_current_nb_bytes_written = 0;
+            l_part_tile_size = 0;
+            l_begin_data = p_data;
+
+            if (! opj_j2k_write_sot(p_j2k, p_data,
+                                    total_data_size,
+                                    &l_current_nb_bytes_written, p_stream,
+                                    p_manager)) {
+                return OPJ_FALSE;
+            }
+
+            l_nb_bytes_written += l_current_nb_bytes_written;
+            p_data += l_current_nb_bytes_written;
+            total_data_size -= l_current_nb_bytes_written;
+            l_part_tile_size += l_current_nb_bytes_written;
+
+            l_current_nb_bytes_written = 0;
+
+            if (! opj_j2k_write_sod(p_j2k, l_tcd, p_data, &l_current_nb_bytes_written,
+                                    total_data_size, p_stream, p_manager)) {
+                return OPJ_FALSE;
+            }
+
+            l_nb_bytes_written += l_current_nb_bytes_written;
+            p_data += l_current_nb_bytes_written;
+            total_data_size -= l_current_nb_bytes_written;
+            l_part_tile_size += l_current_nb_bytes_written;
+
+            /* Writing Psot in SOT marker */
+            opj_write_bytes(l_begin_data + 6, l_part_tile_size,
+                            4);                                   /* PSOT */
+
+            if (p_j2k->m_specific_param.m_encoder.m_TLM) {
+                opj_j2k_update_tlm(p_j2k, l_part_tile_size);
+            }
+
+            ++p_j2k->m_specific_param.m_encoder.m_current_tile_part_number;
+        }
+    }
+
+    *p_data_written = l_nb_bytes_written;
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_write_updated_tlm(opj_j2k_t *p_j2k,
+        struct opj_stream_private *p_stream,
+        struct opj_event_mgr * p_manager)
+{
+    OPJ_UINT32 l_tlm_size;
+    OPJ_OFF_T l_tlm_position, l_current_position;
+    OPJ_UINT32 size_per_tile_part;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    size_per_tile_part = p_j2k->m_specific_param.m_encoder.m_Ttlmi_is_byte ? 5 : 6;
+    l_tlm_size = size_per_tile_part *
+                 p_j2k->m_specific_param.m_encoder.m_total_tile_parts;
+    l_tlm_position = 6 + p_j2k->m_specific_param.m_encoder.m_tlm_start;
+    l_current_position = opj_stream_tell(p_stream);
+
+    if (! opj_stream_seek(p_stream, l_tlm_position, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    if (opj_stream_write_data(p_stream,
+                              p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer, l_tlm_size,
+                              p_manager) != l_tlm_size) {
+        return OPJ_FALSE;
+    }
+
+    if (! opj_stream_seek(p_stream, l_current_position, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_end_encoding(opj_j2k_t *p_j2k,
+                                     struct opj_stream_private *p_stream,
+                                     struct opj_event_mgr * p_manager)
+{
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    OPJ_UNUSED(p_stream);
+    OPJ_UNUSED(p_manager);
+
+    opj_tcd_destroy(p_j2k->m_tcd);
+    p_j2k->m_tcd = 00;
+
+    if (p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer) {
+        opj_free(p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer);
+        p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer = 0;
+        p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current = 0;
+    }
+
+    if (p_j2k->m_specific_param.m_encoder.m_encoded_tile_data) {
+        opj_free(p_j2k->m_specific_param.m_encoder.m_encoded_tile_data);
+        p_j2k->m_specific_param.m_encoder.m_encoded_tile_data = 0;
+    }
+
+    p_j2k->m_specific_param.m_encoder.m_encoded_tile_size = 0;
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Destroys the memory associated with the decoding of headers.
+ */
+static OPJ_BOOL opj_j2k_destroy_header_memory(opj_j2k_t * p_j2k,
+        opj_stream_private_t *p_stream,
+        opj_event_mgr_t * p_manager
+                                             )
+{
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_stream != 00);
+    assert(p_manager != 00);
+
+    OPJ_UNUSED(p_stream);
+    OPJ_UNUSED(p_manager);
+
+    if (p_j2k->m_specific_param.m_encoder.m_header_tile_data) {
+        opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
+        p_j2k->m_specific_param.m_encoder.m_header_tile_data = 0;
+    }
+
+    p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_j2k_init_info(opj_j2k_t *p_j2k,
+                                  struct opj_stream_private *p_stream,
+                                  struct opj_event_mgr * p_manager)
+{
+    opj_codestream_info_t * l_cstr_info = 00;
+
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+    (void)l_cstr_info;
+
+    OPJ_UNUSED(p_stream);
+
+    /* TODO mergeV2: check this part which use cstr_info */
+    /*l_cstr_info = p_j2k->cstr_info;
+
+    if (l_cstr_info)  {
+            OPJ_UINT32 compno;
+            l_cstr_info->tile = (opj_tile_info_t *) opj_malloc(p_j2k->m_cp.tw * p_j2k->m_cp.th * sizeof(opj_tile_info_t));
+
+            l_cstr_info->image_w = p_j2k->m_image->x1 - p_j2k->m_image->x0;
+            l_cstr_info->image_h = p_j2k->m_image->y1 - p_j2k->m_image->y0;
+
+            l_cstr_info->prog = (&p_j2k->m_cp.tcps[0])->prg;
+
+            l_cstr_info->tw = p_j2k->m_cp.tw;
+            l_cstr_info->th = p_j2k->m_cp.th;
+
+            l_cstr_info->tile_x = p_j2k->m_cp.tdx;*/        /* new version parser */
+    /*l_cstr_info->tile_y = p_j2k->m_cp.tdy;*/      /* new version parser */
+    /*l_cstr_info->tile_Ox = p_j2k->m_cp.tx0;*/     /* new version parser */
+    /*l_cstr_info->tile_Oy = p_j2k->m_cp.ty0;*/     /* new version parser */
+
+    /*l_cstr_info->numcomps = p_j2k->m_image->numcomps;
+
+    l_cstr_info->numlayers = (&p_j2k->m_cp.tcps[0])->numlayers;
+
+    l_cstr_info->numdecompos = (OPJ_INT32*) opj_malloc(p_j2k->m_image->numcomps * sizeof(OPJ_INT32));
+
+    for (compno=0; compno < p_j2k->m_image->numcomps; compno++) {
+            l_cstr_info->numdecompos[compno] = (&p_j2k->m_cp.tcps[0])->tccps->numresolutions - 1;
+    }
+
+    l_cstr_info->D_max = 0.0;       */      /* ADD Marcela */
+
+    /*l_cstr_info->main_head_start = opj_stream_tell(p_stream);*/ /* position of SOC */
+
+    /*l_cstr_info->maxmarknum = 100;
+    l_cstr_info->marker = (opj_marker_info_t *) opj_malloc(l_cstr_info->maxmarknum * sizeof(opj_marker_info_t));
+    l_cstr_info->marknum = 0;
+    }*/
+
+    return opj_j2k_calculate_tp(p_j2k, &(p_j2k->m_cp),
+                                &p_j2k->m_specific_param.m_encoder.m_total_tile_parts, p_j2k->m_private_image,
+                                p_manager);
+}
+
+/**
+ * Creates a tile-coder encoder.
+ *
+ * @param       p_stream                the stream to write data to.
+ * @param       p_j2k                   J2K codec.
+ * @param       p_manager               the user event manager.
+*/
+static OPJ_BOOL opj_j2k_create_tcd(opj_j2k_t *p_j2k,
+                                   opj_stream_private_t *p_stream,
+                                   opj_event_mgr_t * p_manager
+                                  )
+{
+    /* preconditions */
+    assert(p_j2k != 00);
+    assert(p_manager != 00);
+    assert(p_stream != 00);
+
+    OPJ_UNUSED(p_stream);
+
+    p_j2k->m_tcd = opj_tcd_create(OPJ_FALSE);
+
+    if (! p_j2k->m_tcd) {
+        opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to create Tile Coder\n");
+        return OPJ_FALSE;
+    }
+
+    if (!opj_tcd_init(p_j2k->m_tcd, p_j2k->m_private_image, &p_j2k->m_cp,
+                      p_j2k->m_tp)) {
+        opj_tcd_destroy(p_j2k->m_tcd);
+        p_j2k->m_tcd = 00;
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+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);
+        return OPJ_FALSE;
+    } else {
+        OPJ_UINT32 j;
+        /* Allocate data */
+        for (j = 0; j < p_j2k->m_tcd->image->numcomps; ++j) {
+            opj_tcd_tilecomp_t* l_tilec = p_j2k->m_tcd->tcd_image->tiles->comps + j;
+
+            if (! opj_alloc_tile_component_data(l_tilec)) {
+                opj_event_msg(p_manager, EVT_ERROR, "Error allocating tile component data.");
+                return OPJ_FALSE;
+            }
+        }
+
+        /* now copy data into the tile component */
+        if (! opj_tcd_copy_tile_data(p_j2k->m_tcd, p_data, p_data_size)) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Size mismatch between tile data and sent data.");
+            return OPJ_FALSE;
+        }
+        if (! opj_j2k_post_write_tile(p_j2k, p_stream, p_manager)) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Error while opj_j2k_post_write_tile with tile index = %d\n", p_tile_index);
+            return OPJ_FALSE;
+        }
+    }
+
+    return OPJ_TRUE;
+}
diff --git a/third_party/libopenjpeg/j2k.h b/third_party/libopenjpeg/j2k.h
new file mode 100644
index 0000000..1d824c0
--- /dev/null
+++ b/third_party/libopenjpeg/j2k.h
@@ -0,0 +1,917 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2006-2007, Parvatha Elangovan
+ * Copyright (c) 2008, Jerome Fimes, Communications & Systemes <jerome.fimes@c-s.fr>
+ * Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France
+ * Copyright (c) 2012, CS Systemes d'Information, France
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef OPJ_J2K_H
+#define OPJ_J2K_H
+/**
+@file j2k.h
+@brief The JPEG-2000 Codestream Reader/Writer (J2K)
+
+The functions in J2K.C have for goal to read/write the several parts of the codestream: markers and data.
+*/
+
+/** @defgroup J2K J2K - JPEG-2000 codestream reader/writer */
+/*@{*/
+
+#define J2K_CP_CSTY_PRT 0x01
+#define J2K_CP_CSTY_SOP 0x02
+#define J2K_CP_CSTY_EPH 0x04
+#define J2K_CCP_CSTY_PRT 0x01
+#define J2K_CCP_CBLKSTY_LAZY 0x01     /**< Selective arithmetic coding bypass */
+#define J2K_CCP_CBLKSTY_RESET 0x02    /**< Reset context probabilities on coding pass boundaries */
+#define J2K_CCP_CBLKSTY_TERMALL 0x04  /**< Termination on each coding pass */
+#define J2K_CCP_CBLKSTY_VSC 0x08      /**< Vertically stripe causal context */
+#define J2K_CCP_CBLKSTY_PTERM 0x10    /**< Predictable termination */
+#define J2K_CCP_CBLKSTY_SEGSYM 0x20   /**< Segmentation symbols are used */
+#define J2K_CCP_CBLKSTY_HT 0x40       /**< (high throughput) HT codeblocks */
+#define J2K_CCP_CBLKSTY_HTMIXED 0x80  /**< MIXED mode HT codeblocks */
+#define J2K_CCP_QNTSTY_NOQNT 0
+#define J2K_CCP_QNTSTY_SIQNT 1
+#define J2K_CCP_QNTSTY_SEQNT 2
+
+/* ----------------------------------------------------------------------- */
+
+#define J2K_MS_SOC 0xff4f   /**< SOC marker value */
+#define J2K_MS_SOT 0xff90   /**< SOT marker value */
+#define J2K_MS_SOD 0xff93   /**< SOD marker value */
+#define J2K_MS_EOC 0xffd9   /**< EOC marker value */
+#define J2K_MS_CAP 0xff50   /**< CAP marker value */
+#define J2K_MS_SIZ 0xff51   /**< SIZ marker value */
+#define J2K_MS_COD 0xff52   /**< COD marker value */
+#define J2K_MS_COC 0xff53   /**< COC marker value */
+#define J2K_MS_CPF 0xff59   /**< CPF marker value */
+#define J2K_MS_RGN 0xff5e   /**< RGN marker value */
+#define J2K_MS_QCD 0xff5c   /**< QCD marker value */
+#define J2K_MS_QCC 0xff5d   /**< QCC marker value */
+#define J2K_MS_POC 0xff5f   /**< POC marker value */
+#define J2K_MS_TLM 0xff55   /**< TLM marker value */
+#define J2K_MS_PLM 0xff57   /**< PLM marker value */
+#define J2K_MS_PLT 0xff58   /**< PLT marker value */
+#define J2K_MS_PPM 0xff60   /**< PPM marker value */
+#define J2K_MS_PPT 0xff61   /**< PPT marker value */
+#define J2K_MS_SOP 0xff91   /**< SOP marker value */
+#define J2K_MS_EPH 0xff92   /**< EPH marker value */
+#define J2K_MS_CRG 0xff63   /**< CRG marker value */
+#define J2K_MS_COM 0xff64   /**< COM marker value */
+#define J2K_MS_CBD 0xff78   /**< CBD marker value */
+#define J2K_MS_MCC 0xff75   /**< MCC marker value */
+#define J2K_MS_MCT 0xff74   /**< MCT marker value */
+#define J2K_MS_MCO 0xff77   /**< MCO marker value */
+
+#define J2K_MS_UNK 0        /**< UNKNOWN marker value */
+
+/* UniPG>> */
+#ifdef USE_JPWL
+#define J2K_MS_EPC 0xff68   /**< EPC marker value (Part 11: JPEG 2000 for Wireless) */
+#define J2K_MS_EPB 0xff66   /**< EPB marker value (Part 11: JPEG 2000 for Wireless) */
+#define J2K_MS_ESD 0xff67   /**< ESD marker value (Part 11: JPEG 2000 for Wireless) */
+#define J2K_MS_RED 0xff69   /**< RED marker value (Part 11: JPEG 2000 for Wireless) */
+#endif /* USE_JPWL */
+#ifdef USE_JPSEC
+#define J2K_MS_SEC 0xff65    /**< SEC marker value (Part 8: Secure JPEG 2000) */
+#define J2K_MS_INSEC 0xff94  /**< INSEC marker value (Part 8: Secure JPEG 2000) */
+#endif /* USE_JPSEC */
+/* <<UniPG */
+
+#define J2K_MAX_POCS    32      /**< Maximum number of POCs */
+
+/* ----------------------------------------------------------------------- */
+
+/**
+ * Values that specify the status of the decoding process when decoding the main header.
+ * These values may be combined with a | operator.
+ * */
+typedef enum J2K_STATUS {
+    J2K_STATE_NONE  =  0x0000, /**< a SOC marker is expected */
+    J2K_STATE_MHSOC  = 0x0001, /**< a SOC marker is expected */
+    J2K_STATE_MHSIZ  = 0x0002, /**< a SIZ marker is expected */
+    J2K_STATE_MH     = 0x0004, /**< the decoding process is in the main header */
+    J2K_STATE_TPHSOT = 0x0008, /**< the decoding process is in a tile part header and expects a SOT marker */
+    J2K_STATE_TPH    = 0x0010, /**< the decoding process is in a tile part header */
+    J2K_STATE_MT     = 0x0020, /**< the EOC marker has just been read */
+    J2K_STATE_NEOC   = 0x0040, /**< the decoding process must not expect a EOC marker because the codestream is truncated */
+    J2K_STATE_DATA   = 0x0080, /**< a tile header has been successfully read and codestream is expected */
+
+    J2K_STATE_EOC    = 0x0100, /**< the decoding process has encountered the EOC marker */
+    J2K_STATE_ERR    = 0x8000  /**< the decoding process has encountered an error (FIXME warning V1 = 0x0080)*/
+} J2K_STATUS;
+
+/**
+ * Type of elements storing in the MCT data
+ */
+typedef enum MCT_ELEMENT_TYPE {
+    MCT_TYPE_INT16 = 0,     /** MCT data is stored as signed shorts*/
+    MCT_TYPE_INT32 = 1,     /** MCT data is stored as signed integers*/
+    MCT_TYPE_FLOAT = 2,     /** MCT data is stored as floats*/
+    MCT_TYPE_DOUBLE = 3     /** MCT data is stored as doubles*/
+} J2K_MCT_ELEMENT_TYPE;
+
+/**
+ * Type of MCT array
+ */
+typedef enum MCT_ARRAY_TYPE {
+    MCT_TYPE_DEPENDENCY = 0,
+    MCT_TYPE_DECORRELATION = 1,
+    MCT_TYPE_OFFSET = 2
+} J2K_MCT_ARRAY_TYPE;
+
+/* ----------------------------------------------------------------------- */
+
+/**
+T2 encoding mode
+*/
+typedef enum T2_MODE {
+    THRESH_CALC = 0,    /** Function called in Rate allocation process*/
+    FINAL_PASS = 1      /** Function called in Tier 2 process*/
+} J2K_T2_MODE;
+
+/**
+ * Quantization stepsize
+ */
+typedef struct opj_stepsize {
+    /** exponent */
+    OPJ_INT32 expn;
+    /** mantissa */
+    OPJ_INT32 mant;
+} opj_stepsize_t;
+
+/**
+Tile-component coding parameters
+*/
+typedef struct opj_tccp {
+    /** coding style */
+    OPJ_UINT32 csty;
+    /** number of resolutions */
+    OPJ_UINT32 numresolutions;
+    /** code-blocks width */
+    OPJ_UINT32 cblkw;
+    /** code-blocks height */
+    OPJ_UINT32 cblkh;
+    /** code-block coding style */
+    OPJ_UINT32 cblksty;
+    /** discrete wavelet transform identifier */
+    OPJ_UINT32 qmfbid;
+    /** quantisation style */
+    OPJ_UINT32 qntsty;
+    /** stepsizes used for quantization */
+    opj_stepsize_t stepsizes[OPJ_J2K_MAXBANDS];
+    /** number of guard bits */
+    OPJ_UINT32 numgbits;
+    /** Region Of Interest shift */
+    OPJ_INT32 roishift;
+    /** precinct width */
+    OPJ_UINT32 prcw[OPJ_J2K_MAXRLVLS];
+    /** precinct height */
+    OPJ_UINT32 prch[OPJ_J2K_MAXRLVLS];
+    /** the dc_level_shift **/
+    OPJ_INT32 m_dc_level_shift;
+}
+opj_tccp_t;
+
+
+
+/**
+ * FIXME DOC
+ */
+typedef struct opj_mct_data {
+    J2K_MCT_ELEMENT_TYPE m_element_type;
+    J2K_MCT_ARRAY_TYPE   m_array_type;
+    OPJ_UINT32           m_index;
+    OPJ_BYTE *           m_data;
+    OPJ_UINT32           m_data_size;
+}
+opj_mct_data_t;
+
+/**
+ * FIXME DOC
+ */
+typedef struct opj_simple_mcc_decorrelation_data {
+    OPJ_UINT32           m_index;
+    OPJ_UINT32           m_nb_comps;
+    opj_mct_data_t *     m_decorrelation_array;
+    opj_mct_data_t *     m_offset_array;
+    OPJ_BITFIELD         m_is_irreversible : 1;
+}
+opj_simple_mcc_decorrelation_data_t;
+
+typedef struct opj_ppx_struct {
+    OPJ_BYTE*   m_data; /* m_data == NULL => Zppx not read yet */
+    OPJ_UINT32  m_data_size;
+} opj_ppx;
+
+/**
+Tile coding parameters :
+this structure is used to store coding/decoding parameters common to all
+tiles (information like COD, COC in main header)
+*/
+typedef struct opj_tcp {
+    /** coding style */
+    OPJ_UINT32 csty;
+    /** progression order */
+    OPJ_PROG_ORDER prg;
+    /** number of layers */
+    OPJ_UINT32 numlayers;
+    OPJ_UINT32 num_layers_to_decode;
+    /** multi-component transform identifier */
+    OPJ_UINT32 mct;
+    /** rates of layers */
+    OPJ_FLOAT32 rates[100];
+    /** number of progression order changes */
+    OPJ_UINT32 numpocs;
+    /** progression order changes */
+    opj_poc_t pocs[J2K_MAX_POCS];
+
+    /** number of ppt markers (reserved size) */
+    OPJ_UINT32 ppt_markers_count;
+    /** ppt markers data (table indexed by Zppt) */
+    opj_ppx* ppt_markers;
+
+    /** packet header store there for future use in t2_decode_packet */
+    OPJ_BYTE *ppt_data;
+    /** used to keep a track of the allocated memory */
+    OPJ_BYTE *ppt_buffer;
+    /** Number of bytes stored inside ppt_data*/
+    OPJ_UINT32 ppt_data_size;
+    /** size of ppt_data*/
+    OPJ_UINT32 ppt_len;
+    /** add fixed_quality */
+    OPJ_FLOAT32 distoratio[100];
+    /** tile-component coding parameters */
+    opj_tccp_t *tccps;
+    /** current tile part number or -1 if first time into this tile */
+    OPJ_INT32  m_current_tile_part_number;
+    /** number of tile parts for the tile. */
+    OPJ_UINT32 m_nb_tile_parts;
+    /** data for the tile */
+    OPJ_BYTE *      m_data;
+    /** size of data */
+    OPJ_UINT32      m_data_size;
+    /** encoding norms */
+    OPJ_FLOAT64 *   mct_norms;
+    /** the mct decoding matrix */
+    OPJ_FLOAT32 *   m_mct_decoding_matrix;
+    /** the mct coding matrix */
+    OPJ_FLOAT32 *   m_mct_coding_matrix;
+    /** mct records */
+    opj_mct_data_t * m_mct_records;
+    /** the number of mct records. */
+    OPJ_UINT32 m_nb_mct_records;
+    /** the max number of mct records. */
+    OPJ_UINT32 m_nb_max_mct_records;
+    /** mcc records */
+    opj_simple_mcc_decorrelation_data_t * m_mcc_records;
+    /** the number of mct records. */
+    OPJ_UINT32 m_nb_mcc_records;
+    /** the max number of mct records. */
+    OPJ_UINT32 m_nb_max_mcc_records;
+
+
+    /***** FLAGS *******/
+    /** If cod == 1 --> there was a COD marker for the present tile */
+    OPJ_BITFIELD cod : 1;
+    /** If ppt == 1 --> there was a PPT marker for the present tile */
+    OPJ_BITFIELD ppt : 1;
+    /** indicates if a POC marker has been used O:NO, 1:YES */
+    OPJ_BITFIELD POC : 1;
+} opj_tcp_t;
+
+
+
+
+typedef struct opj_encoding_param {
+    /** Maximum rate for each component. If == 0, component size limitation is not considered */
+    OPJ_UINT32 m_max_comp_size;
+    /** Position of tile part flag in progression order*/
+    OPJ_INT32 m_tp_pos;
+    /** fixed layer */
+    OPJ_INT32 *m_matrice;
+    /** Flag determining tile part generation*/
+    OPJ_BYTE m_tp_flag;
+    /** allocation by rate/distortion */
+    OPJ_BITFIELD m_disto_alloc : 1;
+    /** allocation by fixed layer */
+    OPJ_BITFIELD m_fixed_alloc : 1;
+    /** add fixed_quality */
+    OPJ_BITFIELD m_fixed_quality : 1;
+    /** Enabling Tile part generation*/
+    OPJ_BITFIELD m_tp_on : 1;
+}
+opj_encoding_param_t;
+
+typedef struct opj_decoding_param {
+    /** if != 0, then original dimension divided by 2^(reduce); if == 0 or not used, image is decoded to the full resolution */
+    OPJ_UINT32 m_reduce;
+    /** if != 0, then only the first "layer" layers are decoded; if == 0 or not used, all the quality layers are decoded */
+    OPJ_UINT32 m_layer;
+}
+opj_decoding_param_t;
+
+
+/**
+ * Coding parameters
+ */
+typedef struct opj_cp {
+    /** Size of the image in bits*/
+    /*int img_size;*/
+    /** Rsiz*/
+    OPJ_UINT16 rsiz;
+    /** XTOsiz */
+    OPJ_UINT32 tx0; /* MSD see norm */
+    /** YTOsiz */
+    OPJ_UINT32 ty0; /* MSD see norm */
+    /** XTsiz */
+    OPJ_UINT32 tdx;
+    /** YTsiz */
+    OPJ_UINT32 tdy;
+    /** comment */
+    OPJ_CHAR *comment;
+    /** number of tiles in width */
+    OPJ_UINT32 tw;
+    /** number of tiles in height */
+    OPJ_UINT32 th;
+
+    /** number of ppm markers (reserved size) */
+    OPJ_UINT32 ppm_markers_count;
+    /** ppm markers data (table indexed by Zppm) */
+    opj_ppx* ppm_markers;
+
+    /** packet header store there for future use in t2_decode_packet */
+    OPJ_BYTE *ppm_data;
+    /** size of the ppm_data*/
+    OPJ_UINT32 ppm_len;
+    /** size of the ppm_data*/
+    OPJ_UINT32 ppm_data_read;
+
+    OPJ_BYTE *ppm_data_current;
+
+    /** packet header storage original buffer */
+    OPJ_BYTE *ppm_buffer;
+    /** pointer remaining on the first byte of the first header if ppm is used */
+    OPJ_BYTE *ppm_data_first;
+    /** Number of bytes actually stored inside the ppm_data */
+    OPJ_UINT32 ppm_data_size;
+    /** use in case of multiple marker PPM (number of info already store) */
+    OPJ_INT32 ppm_store;
+    /** use in case of multiple marker PPM (case on non-finished previous info) */
+    OPJ_INT32 ppm_previous;
+
+    /** tile coding parameters */
+    opj_tcp_t *tcps;
+
+    union {
+        opj_decoding_param_t m_dec;
+        opj_encoding_param_t m_enc;
+    }
+    m_specific_param;
+
+    /** OPJ_TRUE if entire bit stream must be decoded, OPJ_FALSE if partial bitstream decoding allowed */
+    OPJ_BOOL strict;
+
+    /* UniPG>> */
+#ifdef USE_JPWL
+    /** enables writing of EPC in MH, thus activating JPWL */
+    OPJ_BOOL epc_on;
+    /** enables writing of EPB, in case of activated JPWL */
+    OPJ_BOOL epb_on;
+    /** enables writing of ESD, in case of activated JPWL */
+    OPJ_BOOL esd_on;
+    /** enables writing of informative techniques of ESD, in case of activated JPWL */
+    OPJ_BOOL info_on;
+    /** enables writing of RED, in case of activated JPWL */
+    OPJ_BOOL red_on;
+    /** error protection method for MH (0,1,16,32,37-128) */
+    int hprot_MH;
+    /** tile number of header protection specification (>=0) */
+    int hprot_TPH_tileno[JPWL_MAX_NO_TILESPECS];
+    /** error protection methods for TPHs (0,1,16,32,37-128) */
+    int hprot_TPH[JPWL_MAX_NO_TILESPECS];
+    /** tile number of packet protection specification (>=0) */
+    int pprot_tileno[JPWL_MAX_NO_PACKSPECS];
+    /** packet number of packet protection specification (>=0) */
+    int pprot_packno[JPWL_MAX_NO_PACKSPECS];
+    /** error protection methods for packets (0,1,16,32,37-128) */
+    int pprot[JPWL_MAX_NO_PACKSPECS];
+    /** enables writing of ESD, (0/2/4 bytes) */
+    int sens_size;
+    /** sensitivity addressing size (0=auto/2/4 bytes) */
+    int sens_addr;
+    /** sensitivity range (0-3) */
+    int sens_range;
+    /** sensitivity method for MH (-1,0-7) */
+    int sens_MH;
+    /** tile number of sensitivity specification (>=0) */
+    int sens_TPH_tileno[JPWL_MAX_NO_TILESPECS];
+    /** sensitivity methods for TPHs (-1,0-7) */
+    int sens_TPH[JPWL_MAX_NO_TILESPECS];
+    /** enables JPWL correction at the decoder */
+    OPJ_BOOL correct;
+    /** expected number of components at the decoder */
+    int exp_comps;
+    /** maximum number of tiles at the decoder */
+    OPJ_UINT32 max_tiles;
+#endif /* USE_JPWL */
+
+    /******** FLAGS *********/
+    /** if ppm == 1 --> there was a PPM marker*/
+    OPJ_BITFIELD ppm : 1;
+    /** tells if the parameter is a coding or decoding one */
+    OPJ_BITFIELD m_is_decoder : 1;
+    /** whether different bit depth or sign per component is allowed. Decoder only for ow */
+    OPJ_BITFIELD allow_different_bit_depth_sign : 1;
+    /* <<UniPG */
+} opj_cp_t;
+
+
+typedef struct opj_j2k_dec {
+    /** locate in which part of the codestream the decoder is (main header, tile header, end) */
+    OPJ_UINT32 m_state;
+    /**
+     * store decoding parameters common to all tiles (information like COD, COC in main header)
+     */
+    opj_tcp_t *m_default_tcp;
+    OPJ_BYTE  *m_header_data;
+    OPJ_UINT32 m_header_data_size;
+    /** to tell the tile part length */
+    OPJ_UINT32 m_sot_length;
+    /** Only tiles index in the correct range will be decoded.*/
+    OPJ_UINT32 m_start_tile_x;
+    OPJ_UINT32 m_start_tile_y;
+    OPJ_UINT32 m_end_tile_x;
+    OPJ_UINT32 m_end_tile_y;
+
+    /** Index of the tile to decode (used in get_tile) */
+    OPJ_INT32 m_tile_ind_to_dec;
+    /** Position of the last SOT marker read */
+    OPJ_OFF_T m_last_sot_read_pos;
+
+    /**
+     * Indicate that the current tile-part is assume as the last tile part of the codestream.
+     * It is useful in the case of PSot is equal to zero. The sot length will be compute in the
+     * SOD reader function. FIXME NOT USED for the moment
+     */
+    OPJ_BOOL   m_last_tile_part;
+
+    OPJ_UINT32   m_numcomps_to_decode;
+    OPJ_UINT32  *m_comps_indices_to_decode;
+
+    /** to tell that a tile can be decoded. */
+    OPJ_BITFIELD m_can_decode : 1;
+    OPJ_BITFIELD m_discard_tiles : 1;
+    OPJ_BITFIELD m_skip_data : 1;
+    /** TNsot correction : see issue 254 **/
+    OPJ_BITFIELD m_nb_tile_parts_correction_checked : 1;
+    OPJ_BITFIELD m_nb_tile_parts_correction : 1;
+
+} opj_j2k_dec_t;
+
+typedef struct opj_j2k_enc {
+    /** Tile part number, regardless of poc, for each new poc, tp is reset to 1*/
+    OPJ_UINT32 m_current_poc_tile_part_number; /* tp_num */
+
+    /** Tile part number currently coding, taking into account POC. m_current_tile_part_number holds the total number of tile parts while encoding the last tile part.*/
+    OPJ_UINT32 m_current_tile_part_number; /*cur_tp_num */
+
+    /* whether to generate TLM markers */
+    OPJ_BOOL   m_TLM;
+
+    /* whether the Ttlmi field in a TLM marker is a byte (otherwise a uint16) */
+    OPJ_BOOL   m_Ttlmi_is_byte;
+
+    /**
+    locate the start position of the TLM marker
+    after encoding the tilepart, a jump (in j2k_write_sod) is done to the TLM marker to store the value of its length.
+    */
+    OPJ_OFF_T m_tlm_start;
+    /**
+     * Stores the sizes of the tlm.
+     */
+    OPJ_BYTE * m_tlm_sot_offsets_buffer;
+    /**
+     * The current offset of the tlm buffer.
+     */
+    OPJ_BYTE * m_tlm_sot_offsets_current;
+
+    /** Total num of tile parts in whole image = num tiles* num tileparts in each tile*/
+    /** used in TLMmarker*/
+    OPJ_UINT32 m_total_tile_parts;   /* totnum_tp */
+
+    /* encoded data for a tile */
+    OPJ_BYTE * m_encoded_tile_data;
+
+    /* size of the encoded_data */
+    OPJ_UINT32 m_encoded_tile_size;
+
+    /* encoded data for a tile */
+    OPJ_BYTE * m_header_tile_data;
+
+    /* size of the encoded_data */
+
+    OPJ_UINT32 m_header_tile_data_size;
+
+    /* whether to generate PLT markers */
+    OPJ_BOOL   m_PLT;
+
+    /* reserved bytes in m_encoded_tile_size for PLT markers */
+    OPJ_UINT32 m_reserved_bytes_for_PLT;
+
+    /** Number of components */
+    OPJ_UINT32 m_nb_comps;
+
+} opj_j2k_enc_t;
+
+
+
+struct opj_tcd;
+/**
+JPEG-2000 codestream reader/writer
+*/
+typedef struct opj_j2k {
+    /* J2K codestream is decoded*/
+    OPJ_BOOL m_is_decoder;
+
+    /* FIXME DOC*/
+    union {
+        opj_j2k_dec_t m_decoder;
+        opj_j2k_enc_t m_encoder;
+    }
+    m_specific_param;
+
+    /** pointer to the internal/private encoded / decoded image */
+    opj_image_t* m_private_image;
+
+    /* pointer to the output image (decoded)*/
+    opj_image_t* m_output_image;
+
+    /** Coding parameters */
+    opj_cp_t m_cp;
+
+    /** the list of procedures to exec **/
+    opj_procedure_list_t *  m_procedure_list;
+
+    /** the list of validation procedures to follow to make sure the code is valid **/
+    opj_procedure_list_t *  m_validation_list;
+
+    /** helper used to write the index file */
+    opj_codestream_index_t *cstr_index;
+
+    /** number of the tile currently concern by coding/decoding */
+    OPJ_UINT32 m_current_tile_number;
+
+    /** the current tile coder/decoder **/
+    struct opj_tcd *    m_tcd;
+
+    /** Thread pool */
+    opj_thread_pool_t* m_tp;
+
+    /** Image width coming from JP2 IHDR box. 0 from a pure codestream */
+    OPJ_UINT32 ihdr_w;
+
+    /** Image height coming from JP2 IHDR box. 0 from a pure codestream */
+    OPJ_UINT32 ihdr_h;
+
+    /** Set to 1 by the decoder initialization if OPJ_DPARAMETERS_DUMP_FLAG is set */
+    unsigned int dump_state;
+}
+opj_j2k_t;
+
+
+
+
+/** @name Exported functions */
+/*@{*/
+/* ----------------------------------------------------------------------- */
+
+/**
+Setup the decoder decoding parameters using user parameters.
+Decoding parameters are returned in p_j2k->cp.
+@param p_j2k J2K decompressor handle
+@param parameters decompression parameters
+*/
+void opj_j2k_setup_decoder(void *p_j2k, opj_dparameters_t *parameters);
+
+void opj_j2k_decoder_set_strict_mode(void *j2k, OPJ_BOOL strict);
+
+OPJ_BOOL opj_j2k_set_threads(void *j2k, OPJ_UINT32 num_threads);
+
+/**
+ * Creates a J2K compression structure
+ *
+ * @return Returns a handle to a J2K compressor if successful, returns NULL otherwise
+*/
+opj_j2k_t* opj_j2k_create_compress(void);
+
+
+OPJ_BOOL opj_j2k_setup_encoder(void *p_j2k,
+                               opj_cparameters_t *parameters,
+                               opj_image_t *image,
+                               opj_event_mgr_t * p_manager);
+
+/**
+Converts an enum type progression order to string type
+*/
+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(void *j2k,
+                                opj_stream_private_t *p_stream,
+                                opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a jpeg2000 codestream header structure.
+ *
+ * @param p_stream the stream to read data from.
+ * @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,
+                             void* j2k,
+                             opj_image_t** p_image,
+                             opj_event_mgr_t* p_manager);
+
+
+/**
+ * Destroys a jpeg2000 codec.
+ *
+ * @param   j2k   the jpeg20000 structure to destroy.
+ */
+void opj_j2k_destroy(void *j2k);
+
+/**
+ * Destroys a codestream index structure.
+ *
+ * @param   p_cstr_ind  the codestream index parameter to destroy.
+ */
+void j2k_destroy_cstr_index(opj_codestream_index_t *p_cstr_ind);
+
+/**
+ * Decode tile data.
+ * @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(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);
+
+/**
+ * Reads a tile header.
+ * @param   j2k       the jpeg2000 codec.
+ * @param   p_tile_index FIXME DOC
+ * @param   p_data_size FIXME DOC
+ * @param   p_tile_x0 FIXME DOC
+ * @param   p_tile_y0 FIXME DOC
+ * @param   p_tile_x1 FIXME DOC
+ * @param   p_tile_y1 FIXME DOC
+ * @param   p_nb_comps FIXME DOC
+ * @param   p_go_on FIXME DOC
+ * @param   p_stream            the stream to write data to.
+ * @param   p_manager   the user event manager.
+ */
+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,
+                                  OPJ_INT32 * p_tile_x1,
+                                  OPJ_INT32 * p_tile_y1,
+                                  OPJ_UINT32 * p_nb_comps,
+                                  OPJ_BOOL * p_go_on,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+
+/** Sets the indices of the components to decode.
+ *
+ * @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.
+ * @param p_manager     Event manager
+ *
+ * @return OPJ_TRUE in case of success.
+ */
+OPJ_BOOL opj_j2k_set_decoded_components(void *j2k,
+                                        OPJ_UINT32 numcomps,
+                                        const OPJ_UINT32* comps_indices,
+                                        opj_event_mgr_t * p_manager);
+
+/**
+ * Sets the given area to be decoded. This function should be called right after opj_read_header and before any tile header reading.
+ *
+ * @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).
+ * @param   p_end_x         the right position of the rectangle to decode (in image coordinates).
+ * @param   p_end_y         the bottom position of the rectangle to decode (in image coordinates).
+ * @param   p_manager       the user event manager
+ *
+ * @return  true            if the area could be set.
+ */
+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);
+
+/**
+ * Creates a J2K decompression structure.
+ *
+ * @return a handle to a J2K decompressor if successful, NULL otherwise.
+ */
+opj_j2k_t* opj_j2k_create_decompress(void);
+
+
+/**
+ * Dump some elements from the J2K decompression structure .
+ *
+ *@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(void* j2k, OPJ_INT32 flag, FILE* out_stream);
+
+
+
+/**
+ * Dump an image header structure.
+ *
+ *@param image          the image header to dump.
+ *@param dev_dump_flag      flag to describe if we are in the case of this function is use outside j2k_dump function
+ *@param out_stream         output stream where dump the elements.
+ */
+void j2k_dump_image_header(opj_image_t* image, OPJ_BOOL dev_dump_flag,
+                           FILE* out_stream);
+
+/**
+ * Dump a component image header structure.
+ *
+ *@param comp       the component image header to dump.
+ *@param dev_dump_flag      flag to describe if we are in the case of this function is use outside j2k_dump function
+ *@param out_stream         output stream where dump the elements.
+ */
+void j2k_dump_image_comp_header(opj_image_comp_t* comp, OPJ_BOOL dev_dump_flag,
+                                FILE* out_stream);
+
+/**
+ * Get the codestream info from a JPEG2000 codec.
+ *
+ *@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(void* j2k);
+
+/**
+ * Get the codestream index from a JPEG2000 codec.
+ *
+ *@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(void* j2k);
+
+/**
+ * Decode an image from a JPEG-2000 codestream
+ * @param j2k J2K decompressor handle
+ * @param p_stream  FIXME DOC
+ * @param p_image   FIXME DOC
+ * @param p_manager FIXME DOC
+ * @return FIXME DOC
+*/
+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(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(void *j2k,
+        OPJ_UINT32 res_factor,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Specify extra options for the encoder.
+ *
+ * @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(
+    void *j2k,
+    const char* const* p_options,
+    opj_event_mgr_t * p_manager);
+
+/**
+ * Writes a tile.
+ * @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(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);
+
+/**
+ * Encodes an image into a JPEG-2000 codestream
+ */
+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   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(void *j2k,
+                                opj_stream_private_t *p_stream,
+                                opj_image_t * p_image,
+                                opj_event_mgr_t * p_manager);
+
+/**
+ * Ends the compression procedures and possibiliy add data to be read after the
+ * codestream.
+ */
+OPJ_BOOL opj_j2k_end_compress(void *j2k,
+                              opj_stream_private_t *cio,
+                              opj_event_mgr_t * p_manager);
+
+OPJ_BOOL opj_j2k_setup_mct_encoding(opj_tcp_t * p_tcp, opj_image_t * p_image);
+
+
+#endif /* OPJ_J2K_H */
diff --git a/third_party/libopenjpeg/jp2.c b/third_party/libopenjpeg/jp2.c
new file mode 100644
index 0000000..6db728d
--- /dev/null
+++ b/third_party/libopenjpeg/jp2.c
@@ -0,0 +1,3486 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2010-2011, Kaori Hagihara
+ * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
+ * Copyright (c) 2012, CS Systemes d'Information, France
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "opj_includes.h"
+
+/** @defgroup JP2 JP2 - JPEG-2000 file format reader/writer */
+/*@{*/
+
+#define OPJ_BOX_SIZE    1024
+
+#define OPJ_UNUSED(x) (void)x
+
+/** @name Local static functions */
+/*@{*/
+
+/*static void jp2_write_url(opj_cio_t *cio, char *Idx_file);*/
+
+/**
+ * Reads a IHDR box - Image Header box
+ *
+ * @param   p_image_header_data         pointer to actual data (already read from file)
+ * @param   jp2                         the jpeg2000 file codec.
+ * @param   p_image_header_size         the size of the image header
+ * @param   p_manager                   the user event manager.
+ *
+ * @return  true if the image header is valid, false else.
+ */
+static OPJ_BOOL opj_jp2_read_ihdr(opj_jp2_t *jp2,
+                                  OPJ_BYTE *p_image_header_data,
+                                  OPJ_UINT32 p_image_header_size,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Writes the Image Header box - Image Header box.
+ *
+ * @param jp2                   jpeg2000 file codec.
+ * @param p_nb_bytes_written    pointer to store the nb of bytes written by the function.
+ *
+ * @return  the data being copied.
+*/
+static OPJ_BYTE * opj_jp2_write_ihdr(opj_jp2_t *jp2,
+                                     OPJ_UINT32 * p_nb_bytes_written);
+
+/**
+ * Writes the Bit per Component box.
+ *
+ * @param   jp2                     jpeg2000 file codec.
+ * @param   p_nb_bytes_written      pointer to store the nb of bytes written by the function.
+ *
+ * @return  the data being copied.
+*/
+static OPJ_BYTE * opj_jp2_write_bpcc(opj_jp2_t *jp2,
+                                     OPJ_UINT32 * p_nb_bytes_written);
+
+/**
+ * Reads a Bit per Component box.
+ *
+ * @param   p_bpc_header_data           pointer to actual data (already read from file)
+ * @param   jp2                         the jpeg2000 file codec.
+ * @param   p_bpc_header_size           the size of the bpc header
+ * @param   p_manager                   the user event manager.
+ *
+ * @return  true if the bpc header is valid, false else.
+ */
+static OPJ_BOOL opj_jp2_read_bpcc(opj_jp2_t *jp2,
+                                  OPJ_BYTE * p_bpc_header_data,
+                                  OPJ_UINT32 p_bpc_header_size,
+                                  opj_event_mgr_t * p_manager);
+
+static OPJ_BOOL opj_jp2_read_cdef(opj_jp2_t * jp2,
+                                  OPJ_BYTE * p_cdef_header_data,
+                                  OPJ_UINT32 p_cdef_header_size,
+                                  opj_event_mgr_t * p_manager);
+
+static void opj_jp2_apply_cdef(opj_image_t *image, opj_jp2_color_t *color,
+                               opj_event_mgr_t *);
+
+/**
+ * Writes the Channel Definition box.
+ *
+ * @param jp2                   jpeg2000 file codec.
+ * @param p_nb_bytes_written    pointer to store the nb of bytes written by the function.
+ *
+ * @return  the data being copied.
+ */
+static OPJ_BYTE * opj_jp2_write_cdef(opj_jp2_t *jp2,
+                                     OPJ_UINT32 * p_nb_bytes_written);
+
+/**
+ * Writes the Colour Specification box.
+ *
+ * @param jp2                   jpeg2000 file codec.
+ * @param p_nb_bytes_written    pointer to store the nb of bytes written by the function.
+ *
+ * @return  the data being copied.
+*/
+static OPJ_BYTE * opj_jp2_write_colr(opj_jp2_t *jp2,
+                                     OPJ_UINT32 * p_nb_bytes_written);
+
+/**
+ * Writes a FTYP box - File type box
+ *
+ * @param   cio         the stream to write data to.
+ * @param   jp2         the jpeg2000 file codec.
+ * @param   p_manager   the user event manager.
+ *
+ * @return  true if writing was successful.
+ */
+static OPJ_BOOL opj_jp2_write_ftyp(opj_jp2_t *jp2,
+                                   opj_stream_private_t *cio,
+                                   opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a a FTYP box - File type box
+ *
+ * @param   p_header_data   the data contained in the FTYP box.
+ * @param   jp2             the jpeg2000 file codec.
+ * @param   p_header_size   the size of the data contained in the FTYP box.
+ * @param   p_manager       the user event manager.
+ *
+ * @return true if the FTYP box is valid.
+ */
+static OPJ_BOOL opj_jp2_read_ftyp(opj_jp2_t *jp2,
+                                  OPJ_BYTE * p_header_data,
+                                  OPJ_UINT32 p_header_size,
+                                  opj_event_mgr_t * p_manager);
+
+static OPJ_BOOL opj_jp2_skip_jp2c(opj_jp2_t *jp2,
+                                  opj_stream_private_t *stream,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Reads the Jpeg2000 file Header box - JP2 Header box (warning, this is a super box).
+ *
+ * @param   p_header_data   the data contained in the file header box.
+ * @param   jp2             the jpeg2000 file codec.
+ * @param   p_header_size   the size of the data contained in the file header box.
+ * @param   p_manager       the user event manager.
+ *
+ * @return true if the JP2 Header box was successfully recognized.
+*/
+static OPJ_BOOL opj_jp2_read_jp2h(opj_jp2_t *jp2,
+                                  OPJ_BYTE *p_header_data,
+                                  OPJ_UINT32 p_header_size,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Writes the Jpeg2000 file Header box - JP2 Header box (warning, this is a super box).
+ *
+ * @param  jp2      the jpeg2000 file codec.
+ * @param  stream      the stream to write data to.
+ * @param  p_manager  user event manager.
+ *
+ * @return true if writing was successful.
+ */
+static OPJ_BOOL opj_jp2_write_jp2h(opj_jp2_t *jp2,
+                                   opj_stream_private_t *stream,
+                                   opj_event_mgr_t * p_manager);
+
+/**
+ * Writes the Jpeg2000 codestream Header box - JP2C Header box. This function must be called AFTER the coding has been done.
+ *
+ * @param   cio         the stream to write data to.
+ * @param   jp2         the jpeg2000 file codec.
+ * @param   p_manager   user event manager.
+ *
+ * @return true if writing was successful.
+*/
+static OPJ_BOOL opj_jp2_write_jp2c(opj_jp2_t *jp2,
+                                   opj_stream_private_t *cio,
+                                   opj_event_mgr_t * p_manager);
+
+#ifdef USE_JPIP
+/**
+ * Write index Finder box
+ * @param cio     the stream to write to.
+ * @param   jp2         the jpeg2000 file codec.
+ * @param   p_manager   user event manager.
+*/
+static OPJ_BOOL opj_jpip_write_iptr(opj_jp2_t *jp2,
+                                    opj_stream_private_t *cio,
+                                    opj_event_mgr_t * p_manager);
+
+/**
+ * Write index Finder box
+ * @param cio     the stream to write to.
+ * @param   jp2         the jpeg2000 file codec.
+ * @param   p_manager   user event manager.
+ */
+static OPJ_BOOL opj_jpip_write_cidx(opj_jp2_t *jp2,
+                                    opj_stream_private_t *cio,
+                                    opj_event_mgr_t * p_manager);
+
+/**
+ * Write file Index (superbox)
+ * @param cio     the stream to write to.
+ * @param   jp2         the jpeg2000 file codec.
+ * @param   p_manager   user event manager.
+ */
+static OPJ_BOOL opj_jpip_write_fidx(opj_jp2_t *jp2,
+                                    opj_stream_private_t *cio,
+                                    opj_event_mgr_t * p_manager);
+#endif /* USE_JPIP */
+
+/**
+ * Reads a jpeg2000 file signature box.
+ *
+ * @param   p_header_data   the data contained in the signature box.
+ * @param   jp2             the jpeg2000 file codec.
+ * @param   p_header_size   the size of the data contained in the signature box.
+ * @param   p_manager       the user event manager.
+ *
+ * @return true if the file signature box is valid.
+ */
+static OPJ_BOOL opj_jp2_read_jp(opj_jp2_t *jp2,
+                                OPJ_BYTE * p_header_data,
+                                OPJ_UINT32 p_header_size,
+                                opj_event_mgr_t * p_manager);
+
+/**
+ * Writes a jpeg2000 file signature box.
+ *
+ * @param cio the stream to write data to.
+ * @param   jp2         the jpeg2000 file codec.
+ * @param p_manager the user event manager.
+ *
+ * @return true if writing was successful.
+ */
+static OPJ_BOOL opj_jp2_write_jp(opj_jp2_t *jp2,
+                                 opj_stream_private_t *cio,
+                                 opj_event_mgr_t * p_manager);
+
+/**
+Apply collected palette data
+@param image Image.
+@param color Collector for profile, cdef and pclr data.
+@param p_manager the user event manager.
+@return true in case of success
+*/
+static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
+                                   opj_jp2_color_t *color,
+                                   opj_event_mgr_t * p_manager);
+
+static void opj_jp2_free_pclr(opj_jp2_color_t *color);
+
+/**
+ * Collect palette data
+ *
+ * @param jp2 JP2 handle
+ * @param p_pclr_header_data    FIXME DOC
+ * @param p_pclr_header_size    FIXME DOC
+ * @param p_manager
+ *
+ * @return Returns true if successful, returns false otherwise
+*/
+static OPJ_BOOL opj_jp2_read_pclr(opj_jp2_t *jp2,
+                                  OPJ_BYTE * p_pclr_header_data,
+                                  OPJ_UINT32 p_pclr_header_size,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Collect component mapping data
+ *
+ * @param jp2                 JP2 handle
+ * @param p_cmap_header_data  FIXME DOC
+ * @param p_cmap_header_size  FIXME DOC
+ * @param p_manager           FIXME DOC
+ *
+ * @return Returns true if successful, returns false otherwise
+*/
+
+static OPJ_BOOL opj_jp2_read_cmap(opj_jp2_t * jp2,
+                                  OPJ_BYTE * p_cmap_header_data,
+                                  OPJ_UINT32 p_cmap_header_size,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Reads the Color Specification box.
+ *
+ * @param   p_colr_header_data          pointer to actual data (already read from file)
+ * @param   jp2                         the jpeg2000 file codec.
+ * @param   p_colr_header_size          the size of the color header
+ * @param   p_manager                   the user event manager.
+ *
+ * @return  true if the bpc header is valid, false else.
+*/
+static OPJ_BOOL opj_jp2_read_colr(opj_jp2_t *jp2,
+                                  OPJ_BYTE * p_colr_header_data,
+                                  OPJ_UINT32 p_colr_header_size,
+                                  opj_event_mgr_t * p_manager);
+
+/*@}*/
+
+/*@}*/
+
+/**
+ * Sets up the procedures to do on writing header after the codestream.
+ * Developers wanting to extend the library can add their own writing procedures.
+ */
+static OPJ_BOOL opj_jp2_setup_end_header_writing(opj_jp2_t *jp2,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Sets up the procedures to do on reading header after the codestream.
+ * Developers wanting to extend the library can add their own writing procedures.
+ */
+static OPJ_BOOL opj_jp2_setup_end_header_reading(opj_jp2_t *jp2,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a jpeg2000 file header structure.
+ *
+ * @param jp2 the jpeg2000 file header structure.
+ * @param stream the stream to read data from.
+ * @param p_manager the user event manager.
+ *
+ * @return true if the box is valid.
+ */
+static OPJ_BOOL opj_jp2_read_header_procedure(opj_jp2_t *jp2,
+        opj_stream_private_t *stream,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Executes the given procedures on the given codec.
+ *
+ * @param   p_procedure_list    the list of procedures to execute
+ * @param   jp2                 the jpeg2000 file codec to execute the procedures on.
+ * @param   stream                  the stream to execute the procedures on.
+ * @param   p_manager           the user manager.
+ *
+ * @return  true                if all the procedures were successfully executed.
+ */
+static OPJ_BOOL opj_jp2_exec(opj_jp2_t * jp2,
+                             opj_procedure_list_t * p_procedure_list,
+                             opj_stream_private_t *stream,
+                             opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a box header. The box is the way data is packed inside a jpeg2000 file structure.
+ *
+ * @param   cio                     the input stream to read data from.
+ * @param   box                     the box structure to fill.
+ * @param   p_number_bytes_read     pointer to an int that will store the number of bytes read from the stream (shoul usually be 2).
+ * @param   p_manager               user event manager.
+ *
+ * @return  true if the box is recognized, false otherwise
+*/
+static OPJ_BOOL opj_jp2_read_boxhdr(opj_jp2_box_t *box,
+                                    OPJ_UINT32 * p_number_bytes_read,
+                                    opj_stream_private_t *cio,
+                                    opj_event_mgr_t * p_manager);
+
+/**
+ * Sets up the validation ,i.e. adds the procedures to launch to make sure the codec parameters
+ * are valid. Developers wanting to extend the library can add their own validation procedures.
+ */
+static OPJ_BOOL opj_jp2_setup_encoding_validation(opj_jp2_t *jp2,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Sets up the procedures to do on writing header. Developers wanting to extend the library can add their own writing procedures.
+ */
+static OPJ_BOOL opj_jp2_setup_header_writing(opj_jp2_t *jp2,
+        opj_event_mgr_t * p_manager);
+
+static OPJ_BOOL opj_jp2_default_validation(opj_jp2_t * jp2,
+        opj_stream_private_t *cio,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Finds the image execution function related to the given box id.
+ *
+ * @param   p_id    the id of the handler to fetch.
+ *
+ * @return  the given handler or NULL if it could not be found.
+ */
+static const opj_jp2_header_handler_t * opj_jp2_img_find_handler(
+    OPJ_UINT32 p_id);
+
+/**
+ * Finds the execution function related to the given box id.
+ *
+ * @param   p_id    the id of the handler to fetch.
+ *
+ * @return  the given handler or NULL if it could not be found.
+ */
+static const opj_jp2_header_handler_t * opj_jp2_find_handler(OPJ_UINT32 p_id);
+
+static const opj_jp2_header_handler_t jp2_header [] = {
+    {JP2_JP, opj_jp2_read_jp},
+    {JP2_FTYP, opj_jp2_read_ftyp},
+    {JP2_JP2H, opj_jp2_read_jp2h}
+};
+
+static const opj_jp2_header_handler_t jp2_img_header [] = {
+    {JP2_IHDR, opj_jp2_read_ihdr},
+    {JP2_COLR, opj_jp2_read_colr},
+    {JP2_BPCC, opj_jp2_read_bpcc},
+    {JP2_PCLR, opj_jp2_read_pclr},
+    {JP2_CMAP, opj_jp2_read_cmap},
+    {JP2_CDEF, opj_jp2_read_cdef}
+
+};
+
+/**
+ * Reads a box header. The box is the way data is packed inside a jpeg2000 file structure. Data is read from a character string
+ *
+ * @param   box                     the box structure to fill.
+ * @param   p_data                  the character string to read data from.
+ * @param   p_number_bytes_read     pointer to an int that will store the number of bytes read from the stream (shoul usually be 2).
+ * @param   p_box_max_size          the maximum number of bytes in the box.
+ * @param   p_manager         FIXME DOC
+ *
+ * @return  true if the box is recognized, false otherwise
+*/
+static OPJ_BOOL opj_jp2_read_boxhdr_char(opj_jp2_box_t *box,
+        OPJ_BYTE * p_data,
+        OPJ_UINT32 * p_number_bytes_read,
+        OPJ_UINT32 p_box_max_size,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Sets up the validation ,i.e. adds the procedures to launch to make sure the codec parameters
+ * are valid. Developers wanting to extend the library can add their own validation procedures.
+ */
+static OPJ_BOOL opj_jp2_setup_decoding_validation(opj_jp2_t *jp2,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Sets up the procedures to do on reading header.
+ * Developers wanting to extend the library can add their own writing procedures.
+ */
+static OPJ_BOOL opj_jp2_setup_header_reading(opj_jp2_t *jp2,
+        opj_event_mgr_t * p_manager);
+
+/* ----------------------------------------------------------------------- */
+static OPJ_BOOL opj_jp2_read_boxhdr(opj_jp2_box_t *box,
+                                    OPJ_UINT32 * p_number_bytes_read,
+                                    opj_stream_private_t *cio,
+                                    opj_event_mgr_t * p_manager)
+{
+    /* read header from file */
+    OPJ_BYTE l_data_header [8];
+
+    /* preconditions */
+    assert(cio != 00);
+    assert(box != 00);
+    assert(p_number_bytes_read != 00);
+    assert(p_manager != 00);
+
+    *p_number_bytes_read = (OPJ_UINT32)opj_stream_read_data(cio, l_data_header, 8,
+                           p_manager);
+    if (*p_number_bytes_read != 8) {
+        return OPJ_FALSE;
+    }
+
+    /* process read data */
+    opj_read_bytes(l_data_header, &(box->length), 4);
+    opj_read_bytes(l_data_header + 4, &(box->type), 4);
+
+    if (box->length == 0) { /* last box */
+        const OPJ_OFF_T bleft = opj_stream_get_number_byte_left(cio);
+        if (bleft > (OPJ_OFF_T)(0xFFFFFFFFU - 8U)) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Cannot handle box sizes higher than 2^32\n");
+            return OPJ_FALSE;
+        }
+        box->length = (OPJ_UINT32)bleft + 8U;
+        assert((OPJ_OFF_T)box->length == bleft + 8);
+        return OPJ_TRUE;
+    }
+
+    /* do we have a "special very large box ?" */
+    /* read then the XLBox */
+    if (box->length == 1) {
+        OPJ_UINT32 l_xl_part_size;
+
+        OPJ_UINT32 l_nb_bytes_read = (OPJ_UINT32)opj_stream_read_data(cio,
+                                     l_data_header, 8, p_manager);
+        if (l_nb_bytes_read != 8) {
+            if (l_nb_bytes_read > 0) {
+                *p_number_bytes_read += l_nb_bytes_read;
+            }
+
+            return OPJ_FALSE;
+        }
+
+        *p_number_bytes_read = 16;
+        opj_read_bytes(l_data_header, &l_xl_part_size, 4);
+        if (l_xl_part_size != 0) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Cannot handle box sizes higher than 2^32\n");
+            return OPJ_FALSE;
+        }
+        opj_read_bytes(l_data_header + 4, &(box->length), 4);
+    }
+    return OPJ_TRUE;
+}
+
+#if 0
+static void jp2_write_url(opj_cio_t *cio, char *Idx_file)
+{
+    OPJ_UINT32 i;
+    opj_jp2_box_t box;
+
+    box.init_pos = cio_tell(cio);
+    cio_skip(cio, 4);
+    cio_write(cio, JP2_URL, 4); /* DBTL */
+    cio_write(cio, 0, 1);       /* VERS */
+    cio_write(cio, 0, 3);       /* FLAG */
+
+    if (Idx_file) {
+        for (i = 0; i < strlen(Idx_file); i++) {
+            cio_write(cio, Idx_file[i], 1);
+        }
+    }
+
+    box.length = cio_tell(cio) - box.init_pos;
+    cio_seek(cio, box.init_pos);
+    cio_write(cio, box.length, 4);  /* L */
+    cio_seek(cio, box.init_pos + box.length);
+}
+#endif
+
+static OPJ_BOOL opj_jp2_read_ihdr(opj_jp2_t *jp2,
+                                  OPJ_BYTE *p_image_header_data,
+                                  OPJ_UINT32 p_image_header_size,
+                                  opj_event_mgr_t * p_manager)
+{
+    /* preconditions */
+    assert(p_image_header_data != 00);
+    assert(jp2 != 00);
+    assert(p_manager != 00);
+
+    if (jp2->comps != NULL) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "Ignoring ihdr box. First ihdr box already read\n");
+        return OPJ_TRUE;
+    }
+
+    if (p_image_header_size != 14) {
+        opj_event_msg(p_manager, EVT_ERROR, "Bad image header box (bad size)\n");
+        return OPJ_FALSE;
+    }
+
+    opj_read_bytes(p_image_header_data, &(jp2->h), 4);          /* HEIGHT */
+    p_image_header_data += 4;
+    opj_read_bytes(p_image_header_data, &(jp2->w), 4);          /* WIDTH */
+    p_image_header_data += 4;
+    opj_read_bytes(p_image_header_data, &(jp2->numcomps), 2);   /* NC */
+    p_image_header_data += 2;
+
+    if (jp2->h < 1 || jp2->w < 1 || jp2->numcomps < 1) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Wrong values for: w(%d) h(%d) numcomps(%d) (ihdr)\n",
+                      jp2->w, jp2->h, jp2->numcomps);
+        return OPJ_FALSE;
+    }
+    if ((jp2->numcomps - 1U) >=
+            16384U) { /* unsigned underflow is well defined: 1U <= jp2->numcomps <= 16384U */
+        opj_event_msg(p_manager, EVT_ERROR, "Invalid number of components (ihdr)\n");
+        return OPJ_FALSE;
+    }
+
+    /* allocate memory for components */
+    opj_free(jp2->comps);
+    jp2->comps = (opj_jp2_comps_t*) opj_calloc(jp2->numcomps,
+                 sizeof(opj_jp2_comps_t));
+    if (jp2->comps == 0) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Not enough memory to handle image header (ihdr)\n");
+        return OPJ_FALSE;
+    }
+
+    opj_read_bytes(p_image_header_data, &(jp2->bpc), 1);        /* BPC */
+    ++ p_image_header_data;
+
+    opj_read_bytes(p_image_header_data, &(jp2->C), 1);          /* C */
+    ++ p_image_header_data;
+
+    /* Should be equal to 7 cf. chapter about image header box of the norm */
+    if (jp2->C != 7) {
+        opj_event_msg(p_manager, EVT_INFO,
+                      "JP2 IHDR box: compression type indicate that the file is not a conforming JP2 file (%d) \n",
+                      jp2->C);
+    }
+
+    opj_read_bytes(p_image_header_data, &(jp2->UnkC), 1);       /* UnkC */
+    ++ p_image_header_data;
+    opj_read_bytes(p_image_header_data, &(jp2->IPR), 1);        /* IPR */
+    ++ p_image_header_data;
+
+    jp2->j2k->m_cp.allow_different_bit_depth_sign = (jp2->bpc == 255);
+    jp2->j2k->ihdr_w = jp2->w;
+    jp2->j2k->ihdr_h = jp2->h;
+    jp2->has_ihdr = 1;
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BYTE * opj_jp2_write_ihdr(opj_jp2_t *jp2,
+                                     OPJ_UINT32 * p_nb_bytes_written
+                                    )
+{
+    OPJ_BYTE * l_ihdr_data, * l_current_ihdr_ptr;
+
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(p_nb_bytes_written != 00);
+
+    /* default image header is 22 bytes wide */
+    l_ihdr_data = (OPJ_BYTE *) opj_calloc(1, 22);
+    if (l_ihdr_data == 00) {
+        return 00;
+    }
+
+    l_current_ihdr_ptr = l_ihdr_data;
+
+    opj_write_bytes(l_current_ihdr_ptr, 22, 4);             /* write box size */
+    l_current_ihdr_ptr += 4;
+
+    opj_write_bytes(l_current_ihdr_ptr, JP2_IHDR, 4);       /* IHDR */
+    l_current_ihdr_ptr += 4;
+
+    opj_write_bytes(l_current_ihdr_ptr, jp2->h, 4);     /* HEIGHT */
+    l_current_ihdr_ptr += 4;
+
+    opj_write_bytes(l_current_ihdr_ptr, jp2->w, 4);     /* WIDTH */
+    l_current_ihdr_ptr += 4;
+
+    opj_write_bytes(l_current_ihdr_ptr, jp2->numcomps, 2);      /* NC */
+    l_current_ihdr_ptr += 2;
+
+    opj_write_bytes(l_current_ihdr_ptr, jp2->bpc, 1);       /* BPC */
+    ++l_current_ihdr_ptr;
+
+    opj_write_bytes(l_current_ihdr_ptr, jp2->C, 1);     /* C : Always 7 */
+    ++l_current_ihdr_ptr;
+
+    opj_write_bytes(l_current_ihdr_ptr, jp2->UnkC,
+                    1);      /* UnkC, colorspace unknown */
+    ++l_current_ihdr_ptr;
+
+    opj_write_bytes(l_current_ihdr_ptr, jp2->IPR,
+                    1);       /* IPR, no intellectual property */
+    ++l_current_ihdr_ptr;
+
+    *p_nb_bytes_written = 22;
+
+    return l_ihdr_data;
+}
+
+static OPJ_BYTE * opj_jp2_write_bpcc(opj_jp2_t *jp2,
+                                     OPJ_UINT32 * p_nb_bytes_written
+                                    )
+{
+    OPJ_UINT32 i;
+    /* room for 8 bytes for box and 1 byte for each component */
+    OPJ_UINT32 l_bpcc_size;
+    OPJ_BYTE * l_bpcc_data, * l_current_bpcc_ptr;
+
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(p_nb_bytes_written != 00);
+    l_bpcc_size = 8 + jp2->numcomps;
+
+    l_bpcc_data = (OPJ_BYTE *) opj_calloc(1, l_bpcc_size);
+    if (l_bpcc_data == 00) {
+        return 00;
+    }
+
+    l_current_bpcc_ptr = l_bpcc_data;
+
+    opj_write_bytes(l_current_bpcc_ptr, l_bpcc_size,
+                    4);            /* write box size */
+    l_current_bpcc_ptr += 4;
+
+    opj_write_bytes(l_current_bpcc_ptr, JP2_BPCC, 4);               /* BPCC */
+    l_current_bpcc_ptr += 4;
+
+    for (i = 0; i < jp2->numcomps; ++i)  {
+        opj_write_bytes(l_current_bpcc_ptr, jp2->comps[i].bpcc,
+                        1); /* write each component information */
+        ++l_current_bpcc_ptr;
+    }
+
+    *p_nb_bytes_written = l_bpcc_size;
+
+    return l_bpcc_data;
+}
+
+static OPJ_BOOL opj_jp2_read_bpcc(opj_jp2_t *jp2,
+                                  OPJ_BYTE * p_bpc_header_data,
+                                  OPJ_UINT32 p_bpc_header_size,
+                                  opj_event_mgr_t * p_manager
+                                 )
+{
+    OPJ_UINT32 i;
+
+    /* preconditions */
+    assert(p_bpc_header_data != 00);
+    assert(jp2 != 00);
+    assert(p_manager != 00);
+
+
+    if (jp2->bpc != 255) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "A BPCC header box is available although BPC given by the IHDR box (%d) indicate components bit depth is constant\n",
+                      jp2->bpc);
+    }
+
+    /* and length is relevant */
+    if (p_bpc_header_size != jp2->numcomps) {
+        opj_event_msg(p_manager, EVT_ERROR, "Bad BPCC header box (bad size)\n");
+        return OPJ_FALSE;
+    }
+
+    /* read info for each component */
+    for (i = 0; i < jp2->numcomps; ++i) {
+        opj_read_bytes(p_bpc_header_data, &jp2->comps[i].bpcc,
+                       1);  /* read each BPCC component */
+        ++p_bpc_header_data;
+    }
+
+    return OPJ_TRUE;
+}
+static OPJ_BYTE * opj_jp2_write_cdef(opj_jp2_t *jp2,
+                                     OPJ_UINT32 * p_nb_bytes_written)
+{
+    /* room for 8 bytes for box, 2 for n */
+    OPJ_UINT32 l_cdef_size = 10;
+    OPJ_BYTE * l_cdef_data, * l_current_cdef_ptr;
+    OPJ_UINT32 l_value;
+    OPJ_UINT16 i;
+
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(p_nb_bytes_written != 00);
+    assert(jp2->color.jp2_cdef != 00);
+    assert(jp2->color.jp2_cdef->info != 00);
+    assert(jp2->color.jp2_cdef->n > 0U);
+
+    l_cdef_size += 6U * jp2->color.jp2_cdef->n;
+
+    l_cdef_data = (OPJ_BYTE *) opj_malloc(l_cdef_size);
+    if (l_cdef_data == 00) {
+        return 00;
+    }
+
+    l_current_cdef_ptr = l_cdef_data;
+
+    opj_write_bytes(l_current_cdef_ptr, l_cdef_size, 4);        /* write box size */
+    l_current_cdef_ptr += 4;
+
+    opj_write_bytes(l_current_cdef_ptr, JP2_CDEF, 4);               /* BPCC */
+    l_current_cdef_ptr += 4;
+
+    l_value = jp2->color.jp2_cdef->n;
+    opj_write_bytes(l_current_cdef_ptr, l_value, 2);                /* N */
+    l_current_cdef_ptr += 2;
+
+    for (i = 0U; i < jp2->color.jp2_cdef->n; ++i) {
+        l_value = jp2->color.jp2_cdef->info[i].cn;
+        opj_write_bytes(l_current_cdef_ptr, l_value, 2);                /* Cni */
+        l_current_cdef_ptr += 2;
+        l_value = jp2->color.jp2_cdef->info[i].typ;
+        opj_write_bytes(l_current_cdef_ptr, l_value, 2);                /* Typi */
+        l_current_cdef_ptr += 2;
+        l_value = jp2->color.jp2_cdef->info[i].asoc;
+        opj_write_bytes(l_current_cdef_ptr, l_value, 2);                /* Asoci */
+        l_current_cdef_ptr += 2;
+    }
+    *p_nb_bytes_written = l_cdef_size;
+
+    return l_cdef_data;
+}
+
+static OPJ_BYTE * opj_jp2_write_colr(opj_jp2_t *jp2,
+                                     OPJ_UINT32 * p_nb_bytes_written
+                                    )
+{
+    /* room for 8 bytes for box 3 for common data and variable upon profile*/
+    OPJ_UINT32 l_colr_size = 11;
+    OPJ_BYTE * l_colr_data, * l_current_colr_ptr;
+
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(p_nb_bytes_written != 00);
+    assert(jp2->meth == 1 || jp2->meth == 2);
+
+    switch (jp2->meth) {
+    case 1 :
+        l_colr_size += 4; /* EnumCS */
+        break;
+    case 2 :
+        assert(jp2->color.icc_profile_len); /* ICC profile */
+        l_colr_size += jp2->color.icc_profile_len;
+        break;
+    default :
+        return 00;
+    }
+
+    l_colr_data = (OPJ_BYTE *) opj_calloc(1, l_colr_size);
+    if (l_colr_data == 00) {
+        return 00;
+    }
+
+    l_current_colr_ptr = l_colr_data;
+
+    opj_write_bytes(l_current_colr_ptr, l_colr_size,
+                    4);            /* write box size */
+    l_current_colr_ptr += 4;
+
+    opj_write_bytes(l_current_colr_ptr, JP2_COLR, 4);               /* BPCC */
+    l_current_colr_ptr += 4;
+
+    opj_write_bytes(l_current_colr_ptr, jp2->meth, 1);              /* METH */
+    ++l_current_colr_ptr;
+
+    opj_write_bytes(l_current_colr_ptr, jp2->precedence, 1);        /* PRECEDENCE */
+    ++l_current_colr_ptr;
+
+    opj_write_bytes(l_current_colr_ptr, jp2->approx, 1);            /* APPROX */
+    ++l_current_colr_ptr;
+
+    if (jp2->meth ==
+            1) { /* Meth value is restricted to 1 or 2 (Table I.9 of part 1) */
+        opj_write_bytes(l_current_colr_ptr, jp2->enumcs, 4);
+    }       /* EnumCS */
+    else {
+        if (jp2->meth == 2) {                                      /* ICC profile */
+            OPJ_UINT32 i;
+            for (i = 0; i < jp2->color.icc_profile_len; ++i) {
+                opj_write_bytes(l_current_colr_ptr, jp2->color.icc_profile_buf[i], 1);
+                ++l_current_colr_ptr;
+            }
+        }
+    }
+
+    *p_nb_bytes_written = l_colr_size;
+
+    return l_colr_data;
+}
+
+static void opj_jp2_free_pclr(opj_jp2_color_t *color)
+{
+    opj_free(color->jp2_pclr->channel_sign);
+    opj_free(color->jp2_pclr->channel_size);
+    opj_free(color->jp2_pclr->entries);
+
+    if (color->jp2_pclr->cmap) {
+        opj_free(color->jp2_pclr->cmap);
+    }
+
+    opj_free(color->jp2_pclr);
+    color->jp2_pclr = NULL;
+}
+
+static OPJ_BOOL opj_jp2_check_color(opj_image_t *image, opj_jp2_color_t *color,
+                                    opj_event_mgr_t *p_manager)
+{
+    OPJ_UINT16 i;
+
+    /* testcase 4149.pdf.SIGSEGV.cf7.3501 */
+    if (color->jp2_cdef) {
+        opj_jp2_cdef_info_t *info = color->jp2_cdef->info;
+        OPJ_UINT16 n = color->jp2_cdef->n;
+        OPJ_UINT32 nr_channels =
+            image->numcomps; /* FIXME image->numcomps == jp2->numcomps before color is applied ??? */
+
+        /* cdef applies to cmap channels if any */
+        if (color->jp2_pclr && color->jp2_pclr->cmap) {
+            nr_channels = (OPJ_UINT32)color->jp2_pclr->nr_channels;
+        }
+
+        for (i = 0; i < n; i++) {
+            if (info[i].cn >= nr_channels) {
+                opj_event_msg(p_manager, EVT_ERROR, "Invalid component index %d (>= %d).\n",
+                              info[i].cn, nr_channels);
+                return OPJ_FALSE;
+            }
+            if (info[i].asoc == 65535U) {
+                continue;
+            }
+
+            if (info[i].asoc > 0 && (OPJ_UINT32)(info[i].asoc - 1) >= nr_channels) {
+                opj_event_msg(p_manager, EVT_ERROR, "Invalid component index %d (>= %d).\n",
+                              info[i].asoc - 1, nr_channels);
+                return OPJ_FALSE;
+            }
+        }
+
+        /* issue 397 */
+        /* ISO 15444-1 states that if cdef is present, it shall contain a complete list of channel definitions. */
+        while (nr_channels > 0) {
+            for (i = 0; i < n; ++i) {
+                if ((OPJ_UINT32)info[i].cn == (nr_channels - 1U)) {
+                    break;
+                }
+            }
+            if (i == n) {
+                opj_event_msg(p_manager, EVT_ERROR, "Incomplete channel definitions.\n");
+                return OPJ_FALSE;
+            }
+            --nr_channels;
+        }
+    }
+
+    /* testcases 451.pdf.SIGSEGV.f4c.3723, 451.pdf.SIGSEGV.5b5.3723 and
+       66ea31acbb0f23a2bbc91f64d69a03f5_signal_sigsegv_13937c0_7030_5725.pdf */
+    if (color->jp2_pclr && color->jp2_pclr->cmap) {
+        OPJ_UINT16 nr_channels = color->jp2_pclr->nr_channels;
+        opj_jp2_cmap_comp_t *cmap = color->jp2_pclr->cmap;
+        OPJ_BOOL *pcol_usage, is_sane = OPJ_TRUE;
+
+        /* verify that all original components match an existing one */
+        for (i = 0; i < nr_channels; i++) {
+            if (cmap[i].cmp >= image->numcomps) {
+                opj_event_msg(p_manager, EVT_ERROR, "Invalid component index %d (>= %d).\n",
+                              cmap[i].cmp, image->numcomps);
+                is_sane = OPJ_FALSE;
+            }
+        }
+
+        pcol_usage = (OPJ_BOOL *) opj_calloc(nr_channels, sizeof(OPJ_BOOL));
+        if (!pcol_usage) {
+            opj_event_msg(p_manager, EVT_ERROR, "Unexpected OOM.\n");
+            return OPJ_FALSE;
+        }
+        /* verify that no component is targeted more than once */
+        for (i = 0; i < nr_channels; i++) {
+            OPJ_BYTE mtyp = cmap[i].mtyp;
+            OPJ_BYTE pcol = cmap[i].pcol;
+            /* See ISO 15444-1 Table I.14 – MTYPi field values */
+            if (mtyp != 0 && mtyp != 1) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Invalid value for cmap[%d].mtyp = %d.\n", i,
+                              mtyp);
+                is_sane = OPJ_FALSE;
+            } else if (pcol >= nr_channels) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Invalid component/palette index for direct mapping %d.\n", pcol);
+                is_sane = OPJ_FALSE;
+            } else if (pcol_usage[pcol] && mtyp == 1) {
+                opj_event_msg(p_manager, EVT_ERROR, "Component %d is mapped twice.\n", pcol);
+                is_sane = OPJ_FALSE;
+            } else if (mtyp == 0 && pcol != 0) {
+                /* I.5.3.5 PCOL: If the value of the MTYP field for this channel is 0, then
+                 * the value of this field shall be 0. */
+                opj_event_msg(p_manager, EVT_ERROR, "Direct use at #%d however pcol=%d.\n", i,
+                              pcol);
+                is_sane = OPJ_FALSE;
+            } else if (mtyp == 1 && pcol != i) {
+                /* OpenJPEG implementation limitation. See assert(i == pcol); */
+                /* in opj_jp2_apply_pclr() */
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Implementation limitation: for palette mapping, "
+                              "pcol[%d] should be equal to %d, but is equal "
+                              "to %d.\n", i, i, pcol);
+                is_sane = OPJ_FALSE;
+            } else {
+                pcol_usage[pcol] = OPJ_TRUE;
+            }
+        }
+        /* verify that all components are targeted at least once */
+        for (i = 0; i < nr_channels; i++) {
+            if (!pcol_usage[i] && cmap[i].mtyp != 0) {
+                opj_event_msg(p_manager, EVT_ERROR, "Component %d doesn't have a mapping.\n",
+                              i);
+                is_sane = OPJ_FALSE;
+            }
+        }
+        /* Issue 235/447 weird cmap */
+        if (1 && is_sane && (image->numcomps == 1U)) {
+            for (i = 0; i < nr_channels; i++) {
+                if (!pcol_usage[i]) {
+                    is_sane = 0U;
+                    opj_event_msg(p_manager, EVT_WARNING,
+                                  "Component mapping seems wrong. Trying to correct.\n");
+                    break;
+                }
+            }
+            if (!is_sane) {
+                is_sane = OPJ_TRUE;
+                for (i = 0; i < nr_channels; i++) {
+                    cmap[i].mtyp = 1U;
+                    cmap[i].pcol = (OPJ_BYTE) i;
+                }
+            }
+        }
+        opj_free(pcol_usage);
+        if (!is_sane) {
+            return OPJ_FALSE;
+        }
+    }
+
+    return OPJ_TRUE;
+}
+
+/* file9.jp2 */
+static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
+                                   opj_jp2_color_t *color,
+                                   opj_event_mgr_t * p_manager)
+{
+    opj_image_comp_t *old_comps, *new_comps;
+    OPJ_BYTE *channel_size, *channel_sign;
+    OPJ_UINT32 *entries;
+    opj_jp2_cmap_comp_t *cmap;
+    OPJ_INT32 *src, *dst;
+    OPJ_UINT32 j, max;
+    OPJ_UINT16 i, nr_channels, cmp, pcol;
+    OPJ_INT32 k, top_k;
+
+    channel_size = color->jp2_pclr->channel_size;
+    channel_sign = color->jp2_pclr->channel_sign;
+    entries = color->jp2_pclr->entries;
+    cmap = color->jp2_pclr->cmap;
+    nr_channels = color->jp2_pclr->nr_channels;
+
+    for (i = 0; i < nr_channels; ++i) {
+        /* Palette mapping: */
+        cmp = cmap[i].cmp;
+        if (image->comps[cmp].data == NULL) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "image->comps[%d].data == NULL in opj_jp2_apply_pclr().\n", i);
+            return OPJ_FALSE;
+        }
+    }
+
+    old_comps = image->comps;
+    /* Overflow check: prevent integer overflow */
+    for (i = 0; i < nr_channels; ++i) {
+      cmp = cmap[i].cmp;
+      if (old_comps[cmp].h == 0 || old_comps[cmp].w > ((OPJ_UINT32)-1) / sizeof(OPJ_INT32) / old_comps[cmp].h) {
+        return OPJ_FALSE;
+      }
+    }
+
+    new_comps = (opj_image_comp_t*)
+                opj_malloc(nr_channels * sizeof(opj_image_comp_t));
+    if (!new_comps) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Memory allocation failure in opj_jp2_apply_pclr().\n");
+        return OPJ_FALSE;
+    }
+    for (i = 0; i < nr_channels; ++i) {
+        pcol = cmap[i].pcol;
+        cmp = cmap[i].cmp;
+
+        /* Direct use */
+        if (cmap[i].mtyp == 0) {
+            assert(pcol == 0);
+            new_comps[i] = old_comps[cmp];
+        } else {
+            assert( i == pcol ); // probably wrong?
+            new_comps[i] = old_comps[cmp];
+        }
+
+        /* Palette mapping: */
+        new_comps[i].data = (OPJ_INT32*)
+                            opj_image_data_alloc(sizeof(OPJ_INT32) * old_comps[cmp].w * old_comps[cmp].h);
+        if (!new_comps[i].data) {
+            while (i > 0) {
+                -- i;
+                opj_image_data_free(new_comps[i].data);
+            }
+            opj_free(new_comps);
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Memory allocation failure in opj_jp2_apply_pclr().\n");
+            return OPJ_FALSE;
+        }
+        new_comps[i].prec = channel_size[i];
+        new_comps[i].sgnd = channel_sign[i];
+    }
+
+    top_k = color->jp2_pclr->nr_entries - 1;
+
+    for (i = 0; i < nr_channels; ++i) {
+        /* Palette mapping: */
+        cmp = cmap[i].cmp;
+        pcol = cmap[i].pcol;
+        src = old_comps[cmp].data;
+        dst = new_comps[i].data;
+        max = new_comps[i].w * new_comps[i].h;
+
+        /* Prevent null pointer access */
+        if (!src || !dst) {
+          for (j = 0; j < nr_channels; ++j) {
+            opj_image_data_free(new_comps[j].data);
+          }
+          opj_free(new_comps);
+          new_comps = NULL;
+          return OPJ_FALSE;
+        }
+
+        /* Direct use: */
+        if (cmap[i].mtyp == 0) {
+            for (j = 0; j < max; ++j) {
+                dst[j] = src[j];
+            }
+        } else {
+            assert( i == pcol ); // probably wrong?
+            for (j = 0; j < max; ++j) {
+                /* The index */
+                if ((k = src[j]) < 0) {
+                    k = 0;
+                } else if (k > top_k) {
+                    k = top_k;
+                }
+
+                /* The colour */
+                dst[j] = (OPJ_INT32)entries[k * nr_channels + pcol];
+            }
+        }
+    }
+
+    max = image->numcomps;
+    for (j = 0; j < max; ++j) {
+        if (old_comps[j].data) {
+            opj_image_data_free(old_comps[j].data);
+        }
+    }
+
+    opj_free(old_comps);
+    image->comps = new_comps;
+    image->numcomps = nr_channels;
+
+    return OPJ_TRUE;
+}/* apply_pclr() */
+
+static OPJ_BOOL opj_jp2_read_pclr(opj_jp2_t *jp2,
+                                  OPJ_BYTE * p_pclr_header_data,
+                                  OPJ_UINT32 p_pclr_header_size,
+                                  opj_event_mgr_t * p_manager
+                                 )
+{
+    opj_jp2_pclr_t *jp2_pclr;
+    OPJ_BYTE *channel_size, *channel_sign;
+    OPJ_UINT32 *entries;
+    OPJ_UINT16 nr_entries, nr_channels;
+    OPJ_UINT16 i, j;
+    OPJ_UINT32 l_value;
+    OPJ_BYTE *orig_header_data = p_pclr_header_data;
+
+    /* preconditions */
+    assert(p_pclr_header_data != 00);
+    assert(jp2 != 00);
+    assert(p_manager != 00);
+    (void)p_pclr_header_size;
+
+    if (jp2->color.jp2_pclr) {
+        return OPJ_FALSE;
+    }
+
+    if (p_pclr_header_size < 3) {
+        return OPJ_FALSE;
+    }
+
+    opj_read_bytes(p_pclr_header_data, &l_value, 2);    /* NE */
+    p_pclr_header_data += 2;
+    nr_entries = (OPJ_UINT16) l_value;
+    if ((nr_entries == 0U) || (nr_entries > 1024U)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Invalid PCLR box. Reports %d entries\n",
+                      (int)nr_entries);
+        return OPJ_FALSE;
+    }
+
+    opj_read_bytes(p_pclr_header_data, &l_value, 1);    /* NPC */
+    ++p_pclr_header_data;
+    nr_channels = (OPJ_UINT16) l_value;
+    if (nr_channels == 0U) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Invalid PCLR box. Reports 0 palette columns\n");
+        return OPJ_FALSE;
+    }
+
+    if (p_pclr_header_size < 3 + (OPJ_UINT32)nr_channels) {
+        return OPJ_FALSE;
+    }
+
+    entries = (OPJ_UINT32*) opj_malloc(sizeof(OPJ_UINT32) * nr_channels *
+                                       nr_entries);
+    if (!entries) {
+        return OPJ_FALSE;
+    }
+    channel_size = (OPJ_BYTE*) opj_malloc(nr_channels);
+    if (!channel_size) {
+        opj_free(entries);
+        return OPJ_FALSE;
+    }
+    channel_sign = (OPJ_BYTE*) opj_malloc(nr_channels);
+    if (!channel_sign) {
+        opj_free(entries);
+        opj_free(channel_size);
+        return OPJ_FALSE;
+    }
+
+    jp2_pclr = (opj_jp2_pclr_t*)opj_malloc(sizeof(opj_jp2_pclr_t));
+    if (!jp2_pclr) {
+        opj_free(entries);
+        opj_free(channel_size);
+        opj_free(channel_sign);
+        return OPJ_FALSE;
+    }
+
+    jp2_pclr->channel_sign = channel_sign;
+    jp2_pclr->channel_size = channel_size;
+    jp2_pclr->entries = entries;
+    jp2_pclr->nr_entries = nr_entries;
+    jp2_pclr->nr_channels = (OPJ_BYTE) l_value;
+    jp2_pclr->cmap = NULL;
+
+    jp2->color.jp2_pclr = jp2_pclr;
+
+    for (i = 0; i < nr_channels; ++i) {
+        opj_read_bytes(p_pclr_header_data, &l_value, 1);    /* Bi */
+        ++p_pclr_header_data;
+
+        channel_size[i] = (OPJ_BYTE)((l_value & 0x7f) + 1);
+        channel_sign[i] = (l_value & 0x80) ? 1 : 0;
+    }
+
+    for (j = 0; j < nr_entries; ++j) {
+        for (i = 0; i < nr_channels; ++i) {
+            OPJ_UINT32 bytes_to_read = (OPJ_UINT32)((channel_size[i] + 7) >> 3);
+
+            if (bytes_to_read > sizeof(OPJ_UINT32)) {
+                bytes_to_read = sizeof(OPJ_UINT32);
+            }
+            if ((ptrdiff_t)p_pclr_header_size < (ptrdiff_t)(p_pclr_header_data -
+                    orig_header_data) + (ptrdiff_t)bytes_to_read) {
+                return OPJ_FALSE;
+            }
+
+            opj_read_bytes(p_pclr_header_data, &l_value, bytes_to_read);    /* Cji */
+            p_pclr_header_data += bytes_to_read;
+            *entries = (OPJ_UINT32) l_value;
+            entries++;
+        }
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_jp2_read_cmap(opj_jp2_t * jp2,
+                                  OPJ_BYTE * p_cmap_header_data,
+                                  OPJ_UINT32 p_cmap_header_size,
+                                  opj_event_mgr_t * p_manager
+                                 )
+{
+    opj_jp2_cmap_comp_t *cmap;
+    OPJ_BYTE i, nr_channels;
+    OPJ_UINT32 l_value;
+
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(p_cmap_header_data != 00);
+    assert(p_manager != 00);
+    (void)p_cmap_header_size;
+
+    /* Need nr_channels: */
+    if (jp2->color.jp2_pclr == NULL) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Need to read a PCLR box before the CMAP box.\n");
+        return OPJ_FALSE;
+    }
+
+    /* Part 1, I.5.3.5: 'There shall be at most one Component Mapping box
+     * inside a JP2 Header box' :
+    */
+    if (jp2->color.jp2_pclr->cmap) {
+        opj_event_msg(p_manager, EVT_ERROR, "Only one CMAP box is allowed.\n");
+        return OPJ_FALSE;
+    }
+
+    nr_channels = jp2->color.jp2_pclr->nr_channels;
+    if (p_cmap_header_size < (OPJ_UINT32)nr_channels * 4) {
+        opj_event_msg(p_manager, EVT_ERROR, "Insufficient data for CMAP box.\n");
+        return OPJ_FALSE;
+    }
+
+    cmap = (opj_jp2_cmap_comp_t*) opj_malloc(nr_channels * sizeof(
+                opj_jp2_cmap_comp_t));
+    if (!cmap) {
+        return OPJ_FALSE;
+    }
+
+
+    for (i = 0; i < nr_channels; ++i) {
+        opj_read_bytes_BE(p_cmap_header_data, &l_value, 2);         /* CMP^i */
+        p_cmap_header_data += 2;
+        cmap[i].cmp = (OPJ_UINT16) l_value;
+
+        opj_read_bytes(p_cmap_header_data, &l_value, 1);            /* MTYP^i */
+        ++p_cmap_header_data;
+        cmap[i].mtyp = (OPJ_BYTE) l_value;
+
+        opj_read_bytes(p_cmap_header_data, &l_value, 1);            /* PCOL^i */
+        ++p_cmap_header_data;
+        cmap[i].pcol = (OPJ_BYTE) l_value;
+    }
+
+    jp2->color.jp2_pclr->cmap = cmap;
+
+    return OPJ_TRUE;
+}
+
+static void opj_jp2_apply_cdef(opj_image_t *image, opj_jp2_color_t *color,
+                               opj_event_mgr_t *manager)
+{
+    opj_jp2_cdef_info_t *info;
+    OPJ_UINT16 i, n, cn, asoc, acn;
+
+    info = color->jp2_cdef->info;
+    n = color->jp2_cdef->n;
+
+    for (i = 0; i < n; ++i) {
+        /* WATCH: acn = asoc - 1 ! */
+        asoc = info[i].asoc;
+        cn = info[i].cn;
+
+        if (cn >= image->numcomps) {
+            opj_event_msg(manager, EVT_WARNING, "opj_jp2_apply_cdef: cn=%d, numcomps=%d\n",
+                          cn, image->numcomps);
+            continue;
+        }
+        if (asoc == 0 || asoc == 65535) {
+            image->comps[cn].alpha = info[i].typ;
+            continue;
+        }
+
+        acn = (OPJ_UINT16)(asoc - 1);
+        if (acn >= image->numcomps) {
+            opj_event_msg(manager, EVT_WARNING, "opj_jp2_apply_cdef: acn=%d, numcomps=%d\n",
+                          acn, image->numcomps);
+            continue;
+        }
+
+        /* Swap only if color channel */
+        if ((cn != acn) && (info[i].typ == 0)) {
+            opj_image_comp_t saved;
+            OPJ_UINT16 j;
+
+            memcpy(&saved, &image->comps[cn], sizeof(opj_image_comp_t));
+            memcpy(&image->comps[cn], &image->comps[acn], sizeof(opj_image_comp_t));
+            memcpy(&image->comps[acn], &saved, sizeof(opj_image_comp_t));
+
+            /* Swap channels in following channel definitions, don't bother with j <= i that are already processed */
+            for (j = (OPJ_UINT16)(i + 1U); j < n ; ++j) {
+                if (info[j].cn == cn) {
+                    info[j].cn = acn;
+                } else if (info[j].cn == acn) {
+                    info[j].cn = cn;
+                }
+                /* asoc is related to color index. Do not update. */
+            }
+        }
+
+        image->comps[cn].alpha = info[i].typ;
+    }
+
+    if (color->jp2_cdef->info) {
+        opj_free(color->jp2_cdef->info);
+    }
+
+    opj_free(color->jp2_cdef);
+    color->jp2_cdef = NULL;
+
+}/* jp2_apply_cdef() */
+
+static OPJ_BOOL opj_jp2_read_cdef(opj_jp2_t * jp2,
+                                  OPJ_BYTE * p_cdef_header_data,
+                                  OPJ_UINT32 p_cdef_header_size,
+                                  opj_event_mgr_t * p_manager
+                                 )
+{
+    opj_jp2_cdef_info_t *cdef_info;
+    OPJ_UINT16 i;
+    OPJ_UINT32 l_value;
+
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(p_cdef_header_data != 00);
+    assert(p_manager != 00);
+    (void)p_cdef_header_size;
+
+    /* Part 1, I.5.3.6: 'The shall be at most one Channel Definition box
+     * inside a JP2 Header box.'*/
+    if (jp2->color.jp2_cdef) {
+        return OPJ_FALSE;
+    }
+
+    if (p_cdef_header_size < 2) {
+        opj_event_msg(p_manager, EVT_ERROR, "Insufficient data for CDEF box.\n");
+        return OPJ_FALSE;
+    }
+
+    opj_read_bytes(p_cdef_header_data, &l_value, 2);        /* N */
+    p_cdef_header_data += 2;
+
+    if ((OPJ_UINT16)l_value == 0) { /* szukw000: FIXME */
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Number of channel description is equal to zero in CDEF box.\n");
+        return OPJ_FALSE;
+    }
+
+    if (p_cdef_header_size < 2 + (OPJ_UINT32)(OPJ_UINT16)l_value * 6) {
+        opj_event_msg(p_manager, EVT_ERROR, "Insufficient data for CDEF box.\n");
+        return OPJ_FALSE;
+    }
+
+    cdef_info = (opj_jp2_cdef_info_t*) opj_malloc(l_value * sizeof(
+                    opj_jp2_cdef_info_t));
+    if (!cdef_info) {
+        return OPJ_FALSE;
+    }
+
+    jp2->color.jp2_cdef = (opj_jp2_cdef_t*)opj_malloc(sizeof(opj_jp2_cdef_t));
+    if (!jp2->color.jp2_cdef) {
+        opj_free(cdef_info);
+        return OPJ_FALSE;
+    }
+    jp2->color.jp2_cdef->info = cdef_info;
+    jp2->color.jp2_cdef->n = (OPJ_UINT16) l_value;
+
+    for (i = 0; i < jp2->color.jp2_cdef->n; ++i) {
+        opj_read_bytes(p_cdef_header_data, &l_value, 2);            /* Cn^i */
+        p_cdef_header_data += 2;
+        cdef_info[i].cn = (OPJ_UINT16) l_value;
+
+        opj_read_bytes(p_cdef_header_data, &l_value, 2);            /* Typ^i */
+        p_cdef_header_data += 2;
+        cdef_info[i].typ = (OPJ_UINT16) l_value;
+
+        opj_read_bytes(p_cdef_header_data, &l_value, 2);            /* Asoc^i */
+        p_cdef_header_data += 2;
+        cdef_info[i].asoc = (OPJ_UINT16) l_value;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_jp2_read_colr(opj_jp2_t *jp2,
+                                  OPJ_BYTE * p_colr_header_data,
+                                  OPJ_UINT32 p_colr_header_size,
+                                  opj_event_mgr_t * p_manager
+                                 )
+{
+    OPJ_UINT32 l_value;
+
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(p_colr_header_data != 00);
+    assert(p_manager != 00);
+
+    if (p_colr_header_size < 3) {
+        opj_event_msg(p_manager, EVT_ERROR, "Bad COLR header box (bad size)\n");
+        return OPJ_FALSE;
+    }
+
+    /* Part 1, I.5.3.3 : 'A conforming JP2 reader shall ignore all Colour
+     * Specification boxes after the first.'
+    */
+    if (jp2->color.jp2_has_colr) {
+        opj_event_msg(p_manager, EVT_INFO,
+                      "A conforming JP2 reader shall ignore all Colour Specification boxes after the first, so we ignore this one.\n");
+        p_colr_header_data += p_colr_header_size;
+        return OPJ_TRUE;
+    }
+
+    opj_read_bytes(p_colr_header_data, &jp2->meth, 1);          /* METH */
+    ++p_colr_header_data;
+
+    opj_read_bytes(p_colr_header_data, &jp2->precedence, 1);    /* PRECEDENCE */
+    ++p_colr_header_data;
+
+    opj_read_bytes(p_colr_header_data, &jp2->approx, 1);        /* APPROX */
+    ++p_colr_header_data;
+
+    if (jp2->meth == 1) {
+        if (p_colr_header_size < 7) {
+            opj_event_msg(p_manager, EVT_ERROR, "Bad COLR header box (bad size: %d)\n",
+                          p_colr_header_size);
+            return OPJ_FALSE;
+        }
+        if ((p_colr_header_size > 7) &&
+                (jp2->enumcs != 14)) { /* handled below for CIELab) */
+            /* testcase Altona_Technical_v20_x4.pdf */
+            opj_event_msg(p_manager, EVT_WARNING, "Bad COLR header box (bad size: %d)\n",
+                          p_colr_header_size);
+        }
+
+        opj_read_bytes(p_colr_header_data, &jp2->enumcs, 4);        /* EnumCS */
+
+        p_colr_header_data += 4;
+
+        if (jp2->enumcs == 14) { /* CIELab */
+            OPJ_UINT32 *cielab;
+            OPJ_UINT32 rl, ol, ra, oa, rb, ob, il;
+
+            cielab = (OPJ_UINT32*)opj_malloc(9 * sizeof(OPJ_UINT32));
+            if (cielab == NULL) {
+                opj_event_msg(p_manager, EVT_ERROR, "Not enough memory for cielab\n");
+                return OPJ_FALSE;
+            }
+            cielab[0] = 14; /* enumcs */
+
+            /* default values */
+            rl = ra = rb = ol = oa = ob = 0;
+            il = 0x00443530; /* D50 */
+            cielab[1] = 0x44454600;/* DEF */
+
+            if (p_colr_header_size == 35) {
+                opj_read_bytes(p_colr_header_data, &rl, 4);
+                p_colr_header_data += 4;
+                opj_read_bytes(p_colr_header_data, &ol, 4);
+                p_colr_header_data += 4;
+                opj_read_bytes(p_colr_header_data, &ra, 4);
+                p_colr_header_data += 4;
+                opj_read_bytes(p_colr_header_data, &oa, 4);
+                p_colr_header_data += 4;
+                opj_read_bytes(p_colr_header_data, &rb, 4);
+                p_colr_header_data += 4;
+                opj_read_bytes(p_colr_header_data, &ob, 4);
+                p_colr_header_data += 4;
+                opj_read_bytes(p_colr_header_data, &il, 4);
+                p_colr_header_data += 4;
+
+                cielab[1] = 0;
+            } else if (p_colr_header_size != 7) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "Bad COLR header box (CIELab, bad size: %d)\n", p_colr_header_size);
+            }
+            cielab[2] = rl;
+            cielab[4] = ra;
+            cielab[6] = rb;
+            cielab[3] = ol;
+            cielab[5] = oa;
+            cielab[7] = ob;
+            cielab[8] = il;
+
+            jp2->color.icc_profile_buf = (OPJ_BYTE*)cielab;
+            jp2->color.icc_profile_len = 0;
+        }
+        jp2->color.jp2_has_colr = 1;
+    } else if (jp2->meth == 2) {
+        /* ICC profile */
+        OPJ_INT32 it_icc_value = 0;
+        OPJ_INT32 icc_len = (OPJ_INT32)p_colr_header_size - 3;
+
+        jp2->color.icc_profile_len = (OPJ_UINT32)icc_len;
+        jp2->color.icc_profile_buf = (OPJ_BYTE*) opj_calloc(1, (size_t)icc_len);
+        if (!jp2->color.icc_profile_buf) {
+            jp2->color.icc_profile_len = 0;
+            return OPJ_FALSE;
+        }
+
+        for (it_icc_value = 0; it_icc_value < icc_len; ++it_icc_value) {
+            opj_read_bytes(p_colr_header_data, &l_value, 1);    /* icc values */
+            ++p_colr_header_data;
+            jp2->color.icc_profile_buf[it_icc_value] = (OPJ_BYTE) l_value;
+        }
+
+        jp2->color.jp2_has_colr = 1;
+    } else if (jp2->meth > 2) {
+        /*  ISO/IEC 15444-1:2004 (E), Table I.9 Legal METH values:
+        conforming JP2 reader shall ignore the entire Colour Specification box.*/
+        opj_event_msg(p_manager, EVT_INFO,
+                      "COLR BOX meth value is not a regular value (%d), "
+                      "so we will ignore the entire Colour Specification box. \n", jp2->meth);
+    }
+
+    return OPJ_TRUE;
+}
+
+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;
+    }
+
+    /* J2K decoding */
+    if (! opj_j2k_decode(jp2->j2k, p_stream, p_image, p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Failed to decode the codestream in the JP2 file\n");
+        return OPJ_FALSE;
+    }
+
+    if (jp2->j2k->m_specific_param.m_decoder.m_numcomps_to_decode) {
+        /* Bypass all JP2 component transforms */
+        return OPJ_TRUE;
+    }
+
+    if (!jp2->ignore_pclr_cmap_cdef) {
+        if (!opj_jp2_check_color(p_image, &(jp2->color), p_manager)) {
+            return OPJ_FALSE;
+        }
+
+        /* Set Image Color Space */
+        if (jp2->enumcs == 16) {
+            p_image->color_space = OPJ_CLRSPC_SRGB;
+        } else if (jp2->enumcs == 17) {
+            p_image->color_space = OPJ_CLRSPC_GRAY;
+        } else if (jp2->enumcs == 18) {
+            p_image->color_space = OPJ_CLRSPC_SYCC;
+        } else if (jp2->enumcs == 24) {
+            p_image->color_space = OPJ_CLRSPC_EYCC;
+        } else if (jp2->enumcs == 12) {
+            p_image->color_space = OPJ_CLRSPC_CMYK;
+        } else {
+            p_image->color_space = OPJ_CLRSPC_UNKNOWN;
+        }
+
+        if (jp2->color.jp2_pclr) {
+            /* Part 1, I.5.3.4: Either both or none : */
+            if (!jp2->color.jp2_pclr->cmap) {
+                opj_jp2_free_pclr(&(jp2->color));
+            } else {
+                if (!opj_jp2_apply_pclr(p_image, &(jp2->color), p_manager)) {
+                    return OPJ_FALSE;
+                }
+            }
+        }
+
+        /* Apply the color space if needed */
+        if (jp2->color.jp2_cdef) {
+            opj_jp2_apply_cdef(p_image, &(jp2->color), p_manager);
+        }
+
+        if (jp2->color.icc_profile_buf) {
+            p_image->icc_profile_buf = jp2->color.icc_profile_buf;
+            p_image->icc_profile_len = jp2->color.icc_profile_len;
+            jp2->color.icc_profile_buf = NULL;
+        }
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_jp2_write_jp2h(opj_jp2_t *jp2,
+                                   opj_stream_private_t *stream,
+                                   opj_event_mgr_t * p_manager
+                                  )
+{
+    opj_jp2_img_header_writer_handler_t l_writers [4];
+    opj_jp2_img_header_writer_handler_t * l_current_writer;
+
+    OPJ_INT32 i, l_nb_pass;
+    /* size of data for super box*/
+    OPJ_UINT32 l_jp2h_size = 8;
+    OPJ_BOOL l_result = OPJ_TRUE;
+
+    /* to store the data of the super box */
+    OPJ_BYTE l_jp2h_data [8];
+
+    /* preconditions */
+    assert(stream != 00);
+    assert(jp2 != 00);
+    assert(p_manager != 00);
+
+    memset(l_writers, 0, sizeof(l_writers));
+
+    if (jp2->bpc == 255) {
+        l_nb_pass = 3;
+        l_writers[0].handler = opj_jp2_write_ihdr;
+        l_writers[1].handler = opj_jp2_write_bpcc;
+        l_writers[2].handler = opj_jp2_write_colr;
+    } else {
+        l_nb_pass = 2;
+        l_writers[0].handler = opj_jp2_write_ihdr;
+        l_writers[1].handler = opj_jp2_write_colr;
+    }
+
+    if (jp2->color.jp2_cdef != NULL) {
+        l_writers[l_nb_pass].handler = opj_jp2_write_cdef;
+        l_nb_pass++;
+    }
+
+    /* write box header */
+    /* write JP2H type */
+    opj_write_bytes(l_jp2h_data + 4, JP2_JP2H, 4);
+
+    l_current_writer = l_writers;
+    for (i = 0; i < l_nb_pass; ++i) {
+        l_current_writer->m_data = l_current_writer->handler(jp2,
+                                   &(l_current_writer->m_size));
+        if (l_current_writer->m_data == 00) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Not enough memory to hold JP2 Header data\n");
+            l_result = OPJ_FALSE;
+            break;
+        }
+
+        l_jp2h_size += l_current_writer->m_size;
+        ++l_current_writer;
+    }
+
+    if (! l_result) {
+        l_current_writer = l_writers;
+        for (i = 0; i < l_nb_pass; ++i) {
+            if (l_current_writer->m_data != 00) {
+                opj_free(l_current_writer->m_data);
+            }
+            ++l_current_writer;
+        }
+
+        return OPJ_FALSE;
+    }
+
+    /* write super box size */
+    opj_write_bytes(l_jp2h_data, l_jp2h_size, 4);
+
+    /* write super box data on stream */
+    if (opj_stream_write_data(stream, l_jp2h_data, 8, p_manager) != 8) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Stream error while writing JP2 Header box\n");
+        l_result = OPJ_FALSE;
+    }
+
+    if (l_result) {
+        l_current_writer = l_writers;
+        for (i = 0; i < l_nb_pass; ++i) {
+            if (opj_stream_write_data(stream, l_current_writer->m_data,
+                                      l_current_writer->m_size, p_manager) != l_current_writer->m_size) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Stream error while writing JP2 Header box\n");
+                l_result = OPJ_FALSE;
+                break;
+            }
+            ++l_current_writer;
+        }
+    }
+
+    l_current_writer = l_writers;
+
+    /* cleanup */
+    for (i = 0; i < l_nb_pass; ++i) {
+        if (l_current_writer->m_data != 00) {
+            opj_free(l_current_writer->m_data);
+        }
+        ++l_current_writer;
+    }
+
+    return l_result;
+}
+
+static OPJ_BOOL opj_jp2_write_ftyp(opj_jp2_t *jp2,
+                                   opj_stream_private_t *cio,
+                                   opj_event_mgr_t * p_manager)
+{
+    OPJ_UINT32 i;
+    OPJ_UINT32 l_ftyp_size;
+    OPJ_BYTE * l_ftyp_data, * l_current_data_ptr;
+    OPJ_BOOL l_result;
+
+    /* preconditions */
+    assert(cio != 00);
+    assert(jp2 != 00);
+    assert(p_manager != 00);
+    l_ftyp_size = 16 + 4 * jp2->numcl;
+
+    l_ftyp_data = (OPJ_BYTE *) opj_calloc(1, l_ftyp_size);
+
+    if (l_ftyp_data == 00) {
+        opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to handle ftyp data\n");
+        return OPJ_FALSE;
+    }
+
+    l_current_data_ptr = l_ftyp_data;
+
+    opj_write_bytes(l_current_data_ptr, l_ftyp_size, 4); /* box size */
+    l_current_data_ptr += 4;
+
+    opj_write_bytes(l_current_data_ptr, JP2_FTYP, 4); /* FTYP */
+    l_current_data_ptr += 4;
+
+    opj_write_bytes(l_current_data_ptr, jp2->brand, 4); /* BR */
+    l_current_data_ptr += 4;
+
+    opj_write_bytes(l_current_data_ptr, jp2->minversion, 4); /* MinV */
+    l_current_data_ptr += 4;
+
+    for (i = 0; i < jp2->numcl; i++)  {
+        opj_write_bytes(l_current_data_ptr, jp2->cl[i], 4); /* CL */
+    }
+
+    l_result = (opj_stream_write_data(cio, l_ftyp_data, l_ftyp_size,
+                                      p_manager) == l_ftyp_size);
+    if (! l_result) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Error while writing ftyp data to stream\n");
+    }
+
+    opj_free(l_ftyp_data);
+
+    return l_result;
+}
+
+static OPJ_BOOL opj_jp2_write_jp2c(opj_jp2_t *jp2,
+                                   opj_stream_private_t *cio,
+                                   opj_event_mgr_t * p_manager)
+{
+    OPJ_OFF_T j2k_codestream_exit;
+    OPJ_BYTE l_data_header [8];
+
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(cio != 00);
+    assert(p_manager != 00);
+    assert(opj_stream_has_seek(cio));
+
+    j2k_codestream_exit = opj_stream_tell(cio);
+    opj_write_bytes(l_data_header,
+                    (OPJ_UINT32)(j2k_codestream_exit - jp2->j2k_codestream_offset),
+                    4); /* size of codestream */
+    opj_write_bytes(l_data_header + 4, JP2_JP2C,
+                    4);                                     /* JP2C */
+
+    if (! opj_stream_seek(cio, jp2->j2k_codestream_offset, p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
+        return OPJ_FALSE;
+    }
+
+    if (opj_stream_write_data(cio, l_data_header, 8, p_manager) != 8) {
+        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
+        return OPJ_FALSE;
+    }
+
+    if (! opj_stream_seek(cio, j2k_codestream_exit, p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_jp2_write_jp(opj_jp2_t *jp2,
+                                 opj_stream_private_t *cio,
+                                 opj_event_mgr_t * p_manager)
+{
+    /* 12 bytes will be read */
+    OPJ_BYTE l_signature_data [12];
+
+    /* preconditions */
+    assert(cio != 00);
+    assert(jp2 != 00);
+    assert(p_manager != 00);
+
+    OPJ_UNUSED(jp2);
+
+    /* write box length */
+    opj_write_bytes(l_signature_data, 12, 4);
+    /* writes box type */
+    opj_write_bytes(l_signature_data + 4, JP2_JP, 4);
+    /* writes magic number*/
+    opj_write_bytes(l_signature_data + 8, 0x0d0a870a, 4);
+
+    if (opj_stream_write_data(cio, l_signature_data, 12, p_manager) != 12) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+/* ----------------------------------------------------------------------- */
+/* JP2 decoder interface                                             */
+/* ----------------------------------------------------------------------- */
+
+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);
+
+    /* further JP2 initializations go here */
+    jp2->color.jp2_has_colr = 0;
+    jp2->comps = NULL;
+    jp2->ignore_pclr_cmap_cdef = parameters->flags &
+                                 OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG;
+}
+
+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(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);
+}
+
+/* ----------------------------------------------------------------------- */
+/* JP2 encoder interface                                             */
+/* ----------------------------------------------------------------------- */
+
+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;
+    OPJ_UINT32 alpha_count;
+    OPJ_UINT32 color_channels = 0U;
+    OPJ_UINT32 alpha_channel = 0U;
+
+
+    if (!jp2 || !parameters || !image) {
+        return OPJ_FALSE;
+    }
+
+    /* setup the J2K codec */
+    /* ------------------- */
+
+    /* Check if number of components respects standard */
+    if (image->numcomps < 1 || image->numcomps > 16384) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Invalid number of components specified while setting up JP2 encoder\n");
+        return OPJ_FALSE;
+    }
+
+    if (opj_j2k_setup_encoder(jp2->j2k, parameters, image,
+                              p_manager) == OPJ_FALSE) {
+        return OPJ_FALSE;
+    }
+
+    /* setup the JP2 codec */
+    /* ------------------- */
+
+    /* Profile box */
+
+    jp2->brand = JP2_JP2;   /* BR */
+    jp2->minversion = 0;    /* MinV */
+    jp2->numcl = 1;
+    jp2->cl = (OPJ_UINT32*) opj_malloc(jp2->numcl * sizeof(OPJ_UINT32));
+    if (!jp2->cl) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Not enough memory when setup the JP2 encoder\n");
+        return OPJ_FALSE;
+    }
+    jp2->cl[0] = JP2_JP2;   /* CL0 : JP2 */
+
+    /* Image Header box */
+
+    jp2->numcomps = image->numcomps;    /* NC */
+    jp2->comps = (opj_jp2_comps_t*) opj_malloc(jp2->numcomps * sizeof(
+                     opj_jp2_comps_t));
+    if (!jp2->comps) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Not enough memory when setup the JP2 encoder\n");
+        /* Memory of jp2->cl will be freed by opj_jp2_destroy */
+        return OPJ_FALSE;
+    }
+
+    jp2->h = image->y1 - image->y0;     /* HEIGHT */
+    jp2->w = image->x1 - image->x0;     /* WIDTH */
+    /* BPC */
+    depth_0 = image->comps[0].prec - 1;
+    sign = image->comps[0].sgnd;
+    jp2->bpc = depth_0 + (sign << 7);
+    for (i = 1; i < image->numcomps; i++) {
+        OPJ_UINT32 depth = image->comps[i].prec - 1;
+        sign = image->comps[i].sgnd;
+        if (depth_0 != depth) {
+            jp2->bpc = 255;
+        }
+    }
+    jp2->C = 7;         /* C : Always 7 */
+    jp2->UnkC = 0;      /* UnkC, colorspace specified in colr box */
+    jp2->IPR = 0;       /* IPR, no intellectual property */
+
+    /* BitsPerComponent box */
+    for (i = 0; i < image->numcomps; i++) {
+        jp2->comps[i].bpcc = image->comps[i].prec - 1 + (image->comps[i].sgnd << 7);
+    }
+
+    /* Colour Specification box */
+    if (image->icc_profile_len) {
+        jp2->meth = 2;
+        jp2->enumcs = 0;
+    } else {
+        jp2->meth = 1;
+        if (image->color_space == 1) {
+            jp2->enumcs = 16;    /* sRGB as defined by IEC 61966-2-1 */
+        } else if (image->color_space == 2) {
+            jp2->enumcs = 17;    /* greyscale */
+        } else if (image->color_space == 3) {
+            jp2->enumcs = 18;    /* YUV */
+        }
+    }
+
+    /* Channel Definition box */
+    /* FIXME not provided by parameters */
+    /* We try to do what we can... */
+    alpha_count = 0U;
+    for (i = 0; i < image->numcomps; i++) {
+        if (image->comps[i].alpha != 0) {
+            alpha_count++;
+            alpha_channel = i;
+        }
+    }
+    if (alpha_count == 1U) { /* no way to deal with more than 1 alpha channel */
+        switch (jp2->enumcs) {
+        case 16:
+        case 18:
+            color_channels = 3;
+            break;
+        case 17:
+            color_channels = 1;
+            break;
+        default:
+            alpha_count = 0U;
+            break;
+        }
+        if (alpha_count == 0U) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "Alpha channel specified but unknown enumcs. No cdef box will be created.\n");
+        } else if (image->numcomps < (color_channels + 1)) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "Alpha channel specified but not enough image components for an automatic cdef box creation.\n");
+            alpha_count = 0U;
+        } else if ((OPJ_UINT32)alpha_channel < color_channels) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "Alpha channel position conflicts with color channel. No cdef box will be created.\n");
+            alpha_count = 0U;
+        }
+    } else if (alpha_count > 1) {
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "Multiple alpha channels specified. No cdef box will be created.\n");
+    }
+    if (alpha_count == 1U) { /* if here, we know what we can do */
+        jp2->color.jp2_cdef = (opj_jp2_cdef_t*)opj_malloc(sizeof(opj_jp2_cdef_t));
+        if (!jp2->color.jp2_cdef) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Not enough memory to setup the JP2 encoder\n");
+            return OPJ_FALSE;
+        }
+        /* no memset needed, all values will be overwritten except if jp2->color.jp2_cdef->info allocation fails, */
+        /* in which case jp2->color.jp2_cdef->info will be NULL => valid for destruction */
+        jp2->color.jp2_cdef->info = (opj_jp2_cdef_info_t*) opj_malloc(
+                                        image->numcomps * sizeof(opj_jp2_cdef_info_t));
+        if (!jp2->color.jp2_cdef->info) {
+            /* memory will be freed by opj_jp2_destroy */
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Not enough memory to setup the JP2 encoder\n");
+            return OPJ_FALSE;
+        }
+        jp2->color.jp2_cdef->n = (OPJ_UINT16)
+                                 image->numcomps; /* cast is valid : image->numcomps [1,16384] */
+        for (i = 0U; i < color_channels; i++) {
+            jp2->color.jp2_cdef->info[i].cn = (OPJ_UINT16)
+                                              i; /* cast is valid : image->numcomps [1,16384] */
+            jp2->color.jp2_cdef->info[i].typ = 0U;
+            jp2->color.jp2_cdef->info[i].asoc = (OPJ_UINT16)(i +
+                                                1U); /* No overflow + cast is valid : image->numcomps [1,16384] */
+        }
+        for (; i < image->numcomps; i++) {
+            if (image->comps[i].alpha != 0) { /* we'll be here exactly once */
+                jp2->color.jp2_cdef->info[i].cn = (OPJ_UINT16)
+                                                  i; /* cast is valid : image->numcomps [1,16384] */
+                jp2->color.jp2_cdef->info[i].typ = 1U; /* Opacity channel */
+                jp2->color.jp2_cdef->info[i].asoc =
+                    0U; /* Apply alpha channel to the whole image */
+            } else {
+                /* Unknown channel */
+                jp2->color.jp2_cdef->info[i].cn = (OPJ_UINT16)
+                                                  i; /* cast is valid : image->numcomps [1,16384] */
+                jp2->color.jp2_cdef->info[i].typ = 65535U;
+                jp2->color.jp2_cdef->info[i].asoc = 65535U;
+            }
+        }
+    }
+
+    jp2->precedence = 0;    /* PRECEDENCE */
+    jp2->approx = 0;        /* APPROX */
+
+    jp2->jpip_on = parameters->jpip_on;
+
+    return OPJ_TRUE;
+}
+
+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(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);
+    assert(p_manager != 00);
+
+    /* customization of the end encoding */
+    if (! opj_jp2_setup_end_header_reading(jp2, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* write header */
+    if (! opj_jp2_exec(jp2, jp2->m_procedure_list, cio, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    return opj_j2k_end_decompress(jp2->j2k, cio, p_manager);
+}
+
+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);
+    assert(p_manager != 00);
+
+    /* customization of the end encoding */
+    if (! opj_jp2_setup_end_header_writing(jp2, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    if (! opj_j2k_end_compress(jp2->j2k, cio, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* write header */
+    return opj_jp2_exec(jp2, jp2->m_procedure_list, cio, p_manager);
+}
+
+static OPJ_BOOL opj_jp2_setup_end_header_writing(opj_jp2_t *jp2,
+        opj_event_mgr_t * p_manager)
+{
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(p_manager != 00);
+
+#ifdef USE_JPIP
+    if (jp2->jpip_on) {
+        if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
+                                               (opj_procedure)opj_jpip_write_iptr, p_manager)) {
+            return OPJ_FALSE;
+        }
+    }
+#endif
+    if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
+                                           (opj_procedure)opj_jp2_write_jp2c, p_manager)) {
+        return OPJ_FALSE;
+    }
+    /* DEVELOPER CORNER, add your custom procedures */
+#ifdef USE_JPIP
+    if (jp2->jpip_on) {
+        if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
+                                               (opj_procedure)opj_jpip_write_cidx, p_manager)) {
+            return OPJ_FALSE;
+        }
+        if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
+                                               (opj_procedure)opj_jpip_write_fidx, p_manager)) {
+            return OPJ_FALSE;
+        }
+    }
+#endif
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_jp2_setup_end_header_reading(opj_jp2_t *jp2,
+        opj_event_mgr_t * p_manager)
+{
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(p_manager != 00);
+
+    if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
+                                           (opj_procedure)opj_jp2_read_header_procedure, p_manager)) {
+        return OPJ_FALSE;
+    }
+    /* DEVELOPER CORNER, add your custom procedures */
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_jp2_default_validation(opj_jp2_t * jp2,
+        opj_stream_private_t *cio,
+        opj_event_mgr_t * p_manager
+                                          )
+{
+    OPJ_BOOL l_is_valid = OPJ_TRUE;
+    OPJ_UINT32 i;
+
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(cio != 00);
+    assert(p_manager != 00);
+
+    OPJ_UNUSED(p_manager);
+
+    /* JPEG2000 codec validation */
+
+    /* STATE checking */
+    /* make sure the state is at 0 */
+    l_is_valid &= (jp2->jp2_state == JP2_STATE_NONE);
+
+    /* make sure not reading a jp2h ???? WEIRD */
+    l_is_valid &= (jp2->jp2_img_state == JP2_IMG_STATE_NONE);
+
+    /* POINTER validation */
+    /* make sure a j2k codec is present */
+    l_is_valid &= (jp2->j2k != 00);
+
+    /* make sure a procedure list is present */
+    l_is_valid &= (jp2->m_procedure_list != 00);
+
+    /* make sure a validation list is present */
+    l_is_valid &= (jp2->m_validation_list != 00);
+
+    /* PARAMETER VALIDATION */
+    /* number of components */
+    l_is_valid &= (jp2->numcl > 0);
+    /* width */
+    l_is_valid &= (jp2->h > 0);
+    /* height */
+    l_is_valid &= (jp2->w > 0);
+    /* precision */
+    for (i = 0; i < jp2->numcomps; ++i) {
+        l_is_valid &= ((jp2->comps[i].bpcc & 0x7FU) <
+                       38U); /* 0 is valid, ignore sign for check */
+    }
+
+    /* METH */
+    l_is_valid &= ((jp2->meth > 0) && (jp2->meth < 3));
+
+    /* stream validation */
+    /* back and forth is needed */
+    l_is_valid &= opj_stream_has_seek(cio);
+
+    return l_is_valid;
+}
+
+static OPJ_BOOL opj_jp2_read_header_procedure(opj_jp2_t *jp2,
+        opj_stream_private_t *stream,
+        opj_event_mgr_t * p_manager
+                                             )
+{
+    opj_jp2_box_t box;
+    OPJ_UINT32 l_nb_bytes_read;
+    const opj_jp2_header_handler_t * l_current_handler;
+    const opj_jp2_header_handler_t * l_current_handler_misplaced;
+    OPJ_UINT32 l_last_data_size = OPJ_BOX_SIZE;
+    OPJ_UINT32 l_current_data_size;
+    OPJ_BYTE * l_current_data = 00;
+
+    /* preconditions */
+    assert(stream != 00);
+    assert(jp2 != 00);
+    assert(p_manager != 00);
+
+    l_current_data = (OPJ_BYTE*)opj_calloc(1, l_last_data_size);
+
+    if (l_current_data == 00) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Not enough memory to handle jpeg2000 file header\n");
+        return OPJ_FALSE;
+    }
+
+    while (opj_jp2_read_boxhdr(&box, &l_nb_bytes_read, stream, p_manager)) {
+        /* is it the codestream box ? */
+        if (box.type == JP2_JP2C) {
+            if (jp2->jp2_state & JP2_STATE_HEADER) {
+                jp2->jp2_state |= JP2_STATE_CODESTREAM;
+                opj_free(l_current_data);
+                return OPJ_TRUE;
+            } else {
+                opj_event_msg(p_manager, EVT_ERROR, "bad placed jpeg codestream\n");
+                opj_free(l_current_data);
+                return OPJ_FALSE;
+            }
+        } else if (box.length == 0) {
+            opj_event_msg(p_manager, EVT_ERROR, "Cannot handle box of undefined sizes\n");
+            opj_free(l_current_data);
+            return OPJ_FALSE;
+        }
+        /* testcase 1851.pdf.SIGSEGV.ce9.948 */
+        else if (box.length < l_nb_bytes_read) {
+            opj_event_msg(p_manager, EVT_ERROR, "invalid box size %d (%x)\n", box.length,
+                          box.type);
+            opj_free(l_current_data);
+            return OPJ_FALSE;
+        }
+
+        l_current_handler = opj_jp2_find_handler(box.type);
+        l_current_handler_misplaced = opj_jp2_img_find_handler(box.type);
+        l_current_data_size = box.length - l_nb_bytes_read;
+
+        if ((l_current_handler != 00) || (l_current_handler_misplaced != 00)) {
+            if (l_current_handler == 00) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "Found a misplaced '%c%c%c%c' box outside jp2h box\n",
+                              (OPJ_BYTE)(box.type >> 24), (OPJ_BYTE)(box.type >> 16),
+                              (OPJ_BYTE)(box.type >> 8), (OPJ_BYTE)(box.type >> 0));
+                if (jp2->jp2_state & JP2_STATE_HEADER) {
+                    /* read anyway, we already have jp2h */
+                    l_current_handler = l_current_handler_misplaced;
+                } else {
+                    opj_event_msg(p_manager, EVT_WARNING,
+                                  "JPEG2000 Header box not read yet, '%c%c%c%c' box will be ignored\n",
+                                  (OPJ_BYTE)(box.type >> 24), (OPJ_BYTE)(box.type >> 16),
+                                  (OPJ_BYTE)(box.type >> 8), (OPJ_BYTE)(box.type >> 0));
+                    jp2->jp2_state |= JP2_STATE_UNKNOWN;
+                    if (opj_stream_skip(stream, l_current_data_size,
+                                        p_manager) != l_current_data_size) {
+                        opj_event_msg(p_manager, EVT_ERROR,
+                                      "Problem with skipping JPEG2000 box, stream error\n");
+                        opj_free(l_current_data);
+                        return OPJ_FALSE;
+                    }
+                    continue;
+                }
+            }
+            if ((OPJ_OFF_T)l_current_data_size > opj_stream_get_number_byte_left(stream)) {
+                /* do not even try to malloc if we can't read */
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Invalid box size %d for box '%c%c%c%c'. Need %d bytes, %d bytes remaining \n",
+                              box.length, (OPJ_BYTE)(box.type >> 24), (OPJ_BYTE)(box.type >> 16),
+                              (OPJ_BYTE)(box.type >> 8), (OPJ_BYTE)(box.type >> 0), l_current_data_size,
+                              (OPJ_UINT32)opj_stream_get_number_byte_left(stream));
+                opj_free(l_current_data);
+                return OPJ_FALSE;
+            }
+            if (l_current_data_size > l_last_data_size) {
+                OPJ_BYTE* new_current_data = (OPJ_BYTE*)opj_realloc(l_current_data,
+                                             l_current_data_size);
+                if (!new_current_data) {
+                    opj_free(l_current_data);
+                    opj_event_msg(p_manager, EVT_ERROR,
+                                  "Not enough memory to handle jpeg2000 box\n");
+                    return OPJ_FALSE;
+                }
+                l_current_data = new_current_data;
+                l_last_data_size = l_current_data_size;
+            }
+
+            l_nb_bytes_read = (OPJ_UINT32)opj_stream_read_data(stream, l_current_data,
+                              l_current_data_size, p_manager);
+            if (l_nb_bytes_read != l_current_data_size) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Problem with reading JPEG2000 box, stream error\n");
+                opj_free(l_current_data);
+                return OPJ_FALSE;
+            }
+
+            if (! l_current_handler->handler(jp2, l_current_data, l_current_data_size,
+                                             p_manager)) {
+                opj_free(l_current_data);
+                return OPJ_FALSE;
+            }
+        } else {
+            if (!(jp2->jp2_state & JP2_STATE_SIGNATURE)) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Malformed JP2 file format: first box must be JPEG 2000 signature box\n");
+                opj_free(l_current_data);
+                return OPJ_FALSE;
+            }
+            if (!(jp2->jp2_state & JP2_STATE_FILE_TYPE)) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Malformed JP2 file format: second box must be file type box\n");
+                opj_free(l_current_data);
+                return OPJ_FALSE;
+            }
+            jp2->jp2_state |= JP2_STATE_UNKNOWN;
+            if (opj_stream_skip(stream, l_current_data_size,
+                                p_manager) != l_current_data_size) {
+                if (jp2->jp2_state & JP2_STATE_CODESTREAM) {
+                    /* If we already read the codestream, do not error out */
+                    /* Needed for data/input/nonregression/issue254.jp2 */
+                    opj_event_msg(p_manager, EVT_WARNING,
+                                  "Problem with skipping JPEG2000 box, stream error\n");
+                    opj_free(l_current_data);
+                    return OPJ_TRUE;
+                } else {
+                    opj_event_msg(p_manager, EVT_ERROR,
+                                  "Problem with skipping JPEG2000 box, stream error\n");
+                    opj_free(l_current_data);
+                    return OPJ_FALSE;
+                }
+            }
+        }
+    }
+
+    opj_free(l_current_data);
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Executes the given procedures on the given codec.
+ *
+ * @param   p_procedure_list    the list of procedures to execute
+ * @param   jp2                 the jpeg2000 file codec to execute the procedures on.
+ * @param   stream                  the stream to execute the procedures on.
+ * @param   p_manager           the user manager.
+ *
+ * @return  true                if all the procedures were successfully executed.
+ */
+static OPJ_BOOL opj_jp2_exec(opj_jp2_t * jp2,
+                             opj_procedure_list_t * p_procedure_list,
+                             opj_stream_private_t *stream,
+                             opj_event_mgr_t * p_manager
+                            )
+
+{
+    OPJ_BOOL(** l_procedure)(opj_jp2_t * jp2, opj_stream_private_t *,
+                             opj_event_mgr_t *) = 00;
+    OPJ_BOOL l_result = OPJ_TRUE;
+    OPJ_UINT32 l_nb_proc, i;
+
+    /* preconditions */
+    assert(p_procedure_list != 00);
+    assert(jp2 != 00);
+    assert(stream != 00);
+    assert(p_manager != 00);
+
+    l_nb_proc = opj_procedure_list_get_nb_procedures(p_procedure_list);
+    l_procedure = (OPJ_BOOL(**)(opj_jp2_t * jp2, opj_stream_private_t *,
+                                opj_event_mgr_t *)) opj_procedure_list_get_first_procedure(p_procedure_list);
+
+    for (i = 0; i < l_nb_proc; ++i) {
+        l_result = l_result && (*l_procedure)(jp2, stream, p_manager);
+        ++l_procedure;
+    }
+
+    /* and clear the procedure list at the end. */
+    opj_procedure_list_clear(p_procedure_list);
+    return l_result;
+}
+
+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);
+    assert(p_manager != 00);
+
+    /* customization of the validation */
+    if (! opj_jp2_setup_encoding_validation(jp2, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* validation of the parameters codec */
+    if (! opj_jp2_exec(jp2, jp2->m_validation_list, stream, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* customization of the encoding */
+    if (! opj_jp2_setup_header_writing(jp2, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* write header */
+    if (! opj_jp2_exec(jp2, jp2->m_procedure_list, stream, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    return opj_j2k_start_compress(jp2->j2k, stream, p_image, p_manager);
+}
+
+static const opj_jp2_header_handler_t * opj_jp2_find_handler(OPJ_UINT32 p_id)
+{
+    OPJ_UINT32 i, l_handler_size = sizeof(jp2_header) / sizeof(
+                                       opj_jp2_header_handler_t);
+
+    for (i = 0; i < l_handler_size; ++i) {
+        if (jp2_header[i].id == p_id) {
+            return &jp2_header[i];
+        }
+    }
+    return NULL;
+}
+
+/**
+ * Finds the image execution function related to the given box id.
+ *
+ * @param   p_id    the id of the handler to fetch.
+ *
+ * @return  the given handler or 00 if it could not be found.
+ */
+static const opj_jp2_header_handler_t * opj_jp2_img_find_handler(
+    OPJ_UINT32 p_id)
+{
+    OPJ_UINT32 i, l_handler_size = sizeof(jp2_img_header) / sizeof(
+                                       opj_jp2_header_handler_t);
+    for (i = 0; i < l_handler_size; ++i) {
+        if (jp2_img_header[i].id == p_id) {
+            return &jp2_img_header[i];
+        }
+    }
+
+    return NULL;
+}
+
+/**
+ * Reads a jpeg2000 file signature box.
+ *
+ * @param   p_header_data   the data contained in the signature box.
+ * @param   jp2             the jpeg2000 file codec.
+ * @param   p_header_size   the size of the data contained in the signature box.
+ * @param   p_manager       the user event manager.
+ *
+ * @return true if the file signature box is valid.
+ */
+static OPJ_BOOL opj_jp2_read_jp(opj_jp2_t *jp2,
+                                OPJ_BYTE * p_header_data,
+                                OPJ_UINT32 p_header_size,
+                                opj_event_mgr_t * p_manager
+                               )
+
+{
+    OPJ_UINT32 l_magic_number;
+
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(jp2 != 00);
+    assert(p_manager != 00);
+
+    if (jp2->jp2_state != JP2_STATE_NONE) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "The signature box must be the first box in the file.\n");
+        return OPJ_FALSE;
+    }
+
+    /* assure length of data is correct (4 -> magic number) */
+    if (p_header_size != 4) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error with JP signature Box size\n");
+        return OPJ_FALSE;
+    }
+
+    /* rearrange data */
+    opj_read_bytes(p_header_data, &l_magic_number, 4);
+    if (l_magic_number != 0x0d0a870a) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Error with JP Signature : bad magic number\n");
+        return OPJ_FALSE;
+    }
+
+    jp2->jp2_state |= JP2_STATE_SIGNATURE;
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads a a FTYP box - File type box
+ *
+ * @param   p_header_data   the data contained in the FTYP box.
+ * @param   jp2             the jpeg2000 file codec.
+ * @param   p_header_size   the size of the data contained in the FTYP box.
+ * @param   p_manager       the user event manager.
+ *
+ * @return true if the FTYP box is valid.
+ */
+static OPJ_BOOL opj_jp2_read_ftyp(opj_jp2_t *jp2,
+                                  OPJ_BYTE * p_header_data,
+                                  OPJ_UINT32 p_header_size,
+                                  opj_event_mgr_t * p_manager
+                                 )
+{
+    OPJ_UINT32 i, l_remaining_bytes;
+
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(jp2 != 00);
+    assert(p_manager != 00);
+
+    if (jp2->jp2_state != JP2_STATE_SIGNATURE) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "The ftyp box must be the second box in the file.\n");
+        return OPJ_FALSE;
+    }
+
+    /* assure length of data is correct */
+    if (p_header_size < 8) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error with FTYP signature Box size\n");
+        return OPJ_FALSE;
+    }
+
+    opj_read_bytes(p_header_data, &jp2->brand, 4);      /* BR */
+    p_header_data += 4;
+
+    opj_read_bytes(p_header_data, &jp2->minversion, 4);     /* MinV */
+    p_header_data += 4;
+
+    l_remaining_bytes = p_header_size - 8;
+
+    /* the number of remaining bytes should be a multiple of 4 */
+    if ((l_remaining_bytes & 0x3) != 0) {
+        opj_event_msg(p_manager, EVT_ERROR, "Error with FTYP signature Box size\n");
+        return OPJ_FALSE;
+    }
+
+    /* div by 4 */
+    jp2->numcl = l_remaining_bytes >> 2;
+    if (jp2->numcl) {
+        jp2->cl = (OPJ_UINT32 *) opj_calloc(jp2->numcl, sizeof(OPJ_UINT32));
+        if (jp2->cl == 00) {
+            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory with FTYP Box\n");
+            return OPJ_FALSE;
+        }
+    }
+
+    for (i = 0; i < jp2->numcl; ++i) {
+        opj_read_bytes(p_header_data, &jp2->cl[i], 4);      /* CLi */
+        p_header_data += 4;
+    }
+
+    jp2->jp2_state |= JP2_STATE_FILE_TYPE;
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_jp2_skip_jp2c(opj_jp2_t *jp2,
+                                  opj_stream_private_t *stream,
+                                  opj_event_mgr_t * p_manager)
+{
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(stream != 00);
+    assert(p_manager != 00);
+
+    jp2->j2k_codestream_offset = opj_stream_tell(stream);
+
+    if (opj_stream_skip(stream, 8, p_manager) != 8) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_jpip_skip_iptr(opj_jp2_t *jp2,
+                                   opj_stream_private_t *stream,
+                                   opj_event_mgr_t * p_manager)
+{
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(stream != 00);
+    assert(p_manager != 00);
+
+    jp2->jpip_iptr_offset = opj_stream_tell(stream);
+
+    if (opj_stream_skip(stream, 24, p_manager) != 24) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+/**
+ * Reads the Jpeg2000 file Header box - JP2 Header box (warning, this is a super box).
+ *
+ * @param   p_header_data   the data contained in the file header box.
+ * @param   jp2             the jpeg2000 file codec.
+ * @param   p_header_size   the size of the data contained in the file header box.
+ * @param   p_manager       the user event manager.
+ *
+ * @return true if the JP2 Header box was successfully recognized.
+*/
+static OPJ_BOOL opj_jp2_read_jp2h(opj_jp2_t *jp2,
+                                  OPJ_BYTE *p_header_data,
+                                  OPJ_UINT32 p_header_size,
+                                  opj_event_mgr_t * p_manager
+                                 )
+{
+    OPJ_UINT32 l_box_size = 0, l_current_data_size = 0;
+    opj_jp2_box_t box;
+    const opj_jp2_header_handler_t * l_current_handler;
+    OPJ_BOOL l_has_ihdr = 0;
+
+    /* preconditions */
+    assert(p_header_data != 00);
+    assert(jp2 != 00);
+    assert(p_manager != 00);
+
+    /* make sure the box is well placed */
+    if ((jp2->jp2_state & JP2_STATE_FILE_TYPE) != JP2_STATE_FILE_TYPE) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "The  box must be the first box in the file.\n");
+        return OPJ_FALSE;
+    }
+
+    jp2->jp2_img_state = JP2_IMG_STATE_NONE;
+
+    /* iterate while remaining data */
+    while (p_header_size > 0) {
+
+        if (! opj_jp2_read_boxhdr_char(&box, p_header_data, &l_box_size, p_header_size,
+                                       p_manager)) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Stream error while reading JP2 Header box\n");
+            return OPJ_FALSE;
+        }
+
+        if (box.length > p_header_size) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Stream error while reading JP2 Header box: box length is inconsistent.\n");
+            return OPJ_FALSE;
+        }
+
+        l_current_handler = opj_jp2_img_find_handler(box.type);
+        l_current_data_size = box.length - l_box_size;
+        p_header_data += l_box_size;
+
+        if (l_current_handler != 00) {
+            if (! l_current_handler->handler(jp2, p_header_data, l_current_data_size,
+                                             p_manager)) {
+                return OPJ_FALSE;
+            }
+        } else {
+            jp2->jp2_img_state |= JP2_IMG_STATE_UNKNOWN;
+        }
+
+        if (box.type == JP2_IHDR) {
+            l_has_ihdr = 1;
+        }
+
+        p_header_data += l_current_data_size;
+        p_header_size -= box.length;
+    }
+
+    if (l_has_ihdr == 0) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Stream error while reading JP2 Header box: no 'ihdr' box.\n");
+        return OPJ_FALSE;
+    }
+
+    jp2->jp2_state |= JP2_STATE_HEADER;
+    jp2->has_jp2h = 1;
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_jp2_read_boxhdr_char(opj_jp2_box_t *box,
+        OPJ_BYTE * p_data,
+        OPJ_UINT32 * p_number_bytes_read,
+        OPJ_UINT32 p_box_max_size,
+        opj_event_mgr_t * p_manager
+                                        )
+{
+    OPJ_UINT32 l_value;
+
+    /* preconditions */
+    assert(p_data != 00);
+    assert(box != 00);
+    assert(p_number_bytes_read != 00);
+    assert(p_manager != 00);
+
+    if (p_box_max_size < 8) {
+        opj_event_msg(p_manager, EVT_ERROR, "Cannot handle box of less than 8 bytes\n");
+        return OPJ_FALSE;
+    }
+
+    /* process read data */
+    opj_read_bytes(p_data, &l_value, 4);
+    p_data += 4;
+    box->length = (OPJ_UINT32)(l_value);
+
+    opj_read_bytes(p_data, &l_value, 4);
+    p_data += 4;
+    box->type = (OPJ_UINT32)(l_value);
+
+    *p_number_bytes_read = 8;
+
+    /* do we have a "special very large box ?" */
+    /* read then the XLBox */
+    if (box->length == 1) {
+        OPJ_UINT32 l_xl_part_size;
+
+        if (p_box_max_size < 16) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Cannot handle XL box of less than 16 bytes\n");
+            return OPJ_FALSE;
+        }
+
+        opj_read_bytes(p_data, &l_xl_part_size, 4);
+        p_data += 4;
+        *p_number_bytes_read += 4;
+
+        if (l_xl_part_size != 0) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Cannot handle box sizes higher than 2^32\n");
+            return OPJ_FALSE;
+        }
+
+        opj_read_bytes(p_data, &l_value, 4);
+        *p_number_bytes_read += 4;
+        box->length = (OPJ_UINT32)(l_value);
+
+        if (box->length == 0) {
+            opj_event_msg(p_manager, EVT_ERROR, "Cannot handle box of undefined sizes\n");
+            return OPJ_FALSE;
+        }
+    } else if (box->length == 0) {
+        opj_event_msg(p_manager, EVT_ERROR, "Cannot handle box of undefined sizes\n");
+        return OPJ_FALSE;
+    }
+    if (box->length < *p_number_bytes_read) {
+        opj_event_msg(p_manager, EVT_ERROR, "Box length is inconsistent.\n");
+        return OPJ_FALSE;
+    }
+    return OPJ_TRUE;
+}
+
+OPJ_BOOL opj_jp2_read_header(opj_stream_private_t *p_stream,
+                             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);
+    assert(p_manager != 00);
+
+    /* customization of the validation */
+    if (! opj_jp2_setup_decoding_validation(jp2, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* customization of the encoding */
+    if (! opj_jp2_setup_header_reading(jp2, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* validation of the parameters codec */
+    if (! opj_jp2_exec(jp2, jp2->m_validation_list, p_stream, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* read header */
+    if (! opj_jp2_exec(jp2, jp2->m_procedure_list, p_stream, p_manager)) {
+        return OPJ_FALSE;
+    }
+    if (jp2->has_jp2h == 0) {
+        opj_event_msg(p_manager, EVT_ERROR, "JP2H box missing. Required.\n");
+        return OPJ_FALSE;
+    }
+    if (jp2->has_ihdr == 0) {
+        opj_event_msg(p_manager, EVT_ERROR, "IHDR box_missing. Required.\n");
+        return OPJ_FALSE;
+    }
+
+    return opj_j2k_read_header(p_stream,
+                               jp2->j2k,
+                               p_image,
+                               p_manager);
+}
+
+static OPJ_BOOL opj_jp2_setup_encoding_validation(opj_jp2_t *jp2,
+        opj_event_mgr_t * p_manager)
+{
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(p_manager != 00);
+
+    if (! opj_procedure_list_add_procedure(jp2->m_validation_list,
+                                           (opj_procedure)opj_jp2_default_validation, p_manager)) {
+        return OPJ_FALSE;
+    }
+    /* DEVELOPER CORNER, add your custom validation procedure */
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_jp2_setup_decoding_validation(opj_jp2_t *jp2,
+        opj_event_mgr_t * p_manager)
+{
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(p_manager != 00);
+
+    OPJ_UNUSED(jp2);
+    OPJ_UNUSED(p_manager);
+
+    /* DEVELOPER CORNER, add your custom validation procedure */
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_jp2_setup_header_writing(opj_jp2_t *jp2,
+        opj_event_mgr_t * p_manager)
+{
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(p_manager != 00);
+
+    if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
+                                           (opj_procedure)opj_jp2_write_jp, p_manager)) {
+        return OPJ_FALSE;
+    }
+    if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
+                                           (opj_procedure)opj_jp2_write_ftyp, p_manager)) {
+        return OPJ_FALSE;
+    }
+    if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
+                                           (opj_procedure)opj_jp2_write_jp2h, p_manager)) {
+        return OPJ_FALSE;
+    }
+    if (jp2->jpip_on) {
+        if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
+                                               (opj_procedure)opj_jpip_skip_iptr, p_manager)) {
+            return OPJ_FALSE;
+        }
+    }
+    if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
+                                           (opj_procedure)opj_jp2_skip_jp2c, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* DEVELOPER CORNER, insert your custom procedures */
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_jp2_setup_header_reading(opj_jp2_t *jp2,
+        opj_event_mgr_t * p_manager)
+{
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(p_manager != 00);
+
+    if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
+                                           (opj_procedure)opj_jp2_read_header_procedure, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* DEVELOPER CORNER, add your custom procedures */
+
+    return OPJ_TRUE;
+}
+
+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,
+                                  OPJ_INT32 * p_tile_y0,
+                                  OPJ_INT32 * p_tile_x1,
+                                  OPJ_INT32 * p_tile_y1,
+                                  OPJ_UINT32 * p_nb_comps,
+                                  OPJ_BOOL * p_go_on,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager
+                                 )
+{
+    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,
+                                    p_tile_x1, p_tile_y1,
+                                    p_nb_comps,
+                                    p_go_on,
+                                    p_stream,
+                                    p_manager);
+}
+
+OPJ_BOOL opj_jp2_write_tile(void *p_jp2,
+                            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_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(void *p_jp2,
+                             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_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(void *p_jp2)
+{
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+    if (jp2) {
+        /* destroy the J2K codec */
+        opj_j2k_destroy(jp2->j2k);
+        jp2->j2k = 00;
+
+        if (jp2->comps) {
+            opj_free(jp2->comps);
+            jp2->comps = 00;
+        }
+
+        if (jp2->cl) {
+            opj_free(jp2->cl);
+            jp2->cl = 00;
+        }
+
+        if (jp2->color.icc_profile_buf) {
+            opj_free(jp2->color.icc_profile_buf);
+            jp2->color.icc_profile_buf = 00;
+        }
+
+        if (jp2->color.jp2_cdef) {
+            if (jp2->color.jp2_cdef->info) {
+                opj_free(jp2->color.jp2_cdef->info);
+                jp2->color.jp2_cdef->info = NULL;
+            }
+
+            opj_free(jp2->color.jp2_cdef);
+            jp2->color.jp2_cdef = 00;
+        }
+
+        if (jp2->color.jp2_pclr) {
+            if (jp2->color.jp2_pclr->cmap) {
+                opj_free(jp2->color.jp2_pclr->cmap);
+                jp2->color.jp2_pclr->cmap = NULL;
+            }
+            if (jp2->color.jp2_pclr->channel_sign) {
+                opj_free(jp2->color.jp2_pclr->channel_sign);
+                jp2->color.jp2_pclr->channel_sign = NULL;
+            }
+            if (jp2->color.jp2_pclr->channel_size) {
+                opj_free(jp2->color.jp2_pclr->channel_size);
+                jp2->color.jp2_pclr->channel_size = NULL;
+            }
+            if (jp2->color.jp2_pclr->entries) {
+                opj_free(jp2->color.jp2_pclr->entries);
+                jp2->color.jp2_pclr->entries = NULL;
+            }
+
+            opj_free(jp2->color.jp2_pclr);
+            jp2->color.jp2_pclr = 00;
+        }
+
+        if (jp2->m_validation_list) {
+            opj_procedure_list_destroy(jp2->m_validation_list);
+            jp2->m_validation_list = 00;
+        }
+
+        if (jp2->m_procedure_list) {
+            opj_procedure_list_destroy(jp2->m_procedure_list);
+            jp2->m_procedure_list = 00;
+        }
+
+        opj_free(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)
+{
+    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(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
+                                )
+{
+    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(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;
+    }
+
+    opj_event_msg(p_manager, EVT_WARNING,
+                  "JP2 box which are after the codestream will not be read by this function.\n");
+
+    if (! opj_j2k_get_tile(p_jp2->j2k, p_stream, p_image, p_manager, tile_index)) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Failed to decode the codestream in the JP2 file\n");
+        return OPJ_FALSE;
+    }
+
+    if (p_jp2->j2k->m_specific_param.m_decoder.m_numcomps_to_decode) {
+        /* Bypass all JP2 component transforms */
+        return OPJ_TRUE;
+    }
+
+    if (!opj_jp2_check_color(p_image, &(p_jp2->color), p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    /* Set Image Color Space */
+    if (p_jp2->enumcs == 16) {
+        p_image->color_space = OPJ_CLRSPC_SRGB;
+    } else if (p_jp2->enumcs == 17) {
+        p_image->color_space = OPJ_CLRSPC_GRAY;
+    } else if (p_jp2->enumcs == 18) {
+        p_image->color_space = OPJ_CLRSPC_SYCC;
+    } else if (p_jp2->enumcs == 24) {
+        p_image->color_space = OPJ_CLRSPC_EYCC;
+    } else if (p_jp2->enumcs == 12) {
+        p_image->color_space = OPJ_CLRSPC_CMYK;
+    } else {
+        p_image->color_space = OPJ_CLRSPC_UNKNOWN;
+    }
+
+    if (p_jp2->color.jp2_pclr) {
+        /* Part 1, I.5.3.4: Either both or none : */
+        if (!p_jp2->color.jp2_pclr->cmap) {
+            opj_jp2_free_pclr(&(p_jp2->color));
+        } else {
+            if (!opj_jp2_apply_pclr(p_image, &(p_jp2->color), p_manager)) {
+                return OPJ_FALSE;
+            }
+        }
+    }
+
+    /* Apply the color space if needed */
+    if (p_jp2->color.jp2_cdef) {
+        opj_jp2_apply_cdef(p_image, &(p_jp2->color), p_manager);
+    }
+
+    if (p_jp2->color.icc_profile_buf) {
+        p_image->icc_profile_buf = p_jp2->color.icc_profile_buf;
+        p_image->icc_profile_len = p_jp2->color.icc_profile_len;
+        p_jp2->color.icc_profile_buf = NULL;
+    }
+
+    return OPJ_TRUE;
+}
+
+/* ----------------------------------------------------------------------- */
+/* JP2 encoder interface                                             */
+/* ----------------------------------------------------------------------- */
+
+opj_jp2_t* opj_jp2_create(OPJ_BOOL p_is_decoder)
+{
+    opj_jp2_t *jp2 = (opj_jp2_t*)opj_calloc(1, sizeof(opj_jp2_t));
+    if (jp2) {
+
+        /* create the J2K codec */
+        if (! p_is_decoder) {
+            jp2->j2k = opj_j2k_create_compress();
+        } else {
+            jp2->j2k = opj_j2k_create_decompress();
+        }
+
+        if (jp2->j2k == 00) {
+            opj_jp2_destroy(jp2);
+            return 00;
+        }
+
+        /* Color structure */
+        jp2->color.icc_profile_buf = NULL;
+        jp2->color.icc_profile_len = 0;
+        jp2->color.jp2_cdef = NULL;
+        jp2->color.jp2_pclr = NULL;
+        jp2->color.jp2_has_colr = 0;
+
+        /* validation list creation */
+        jp2->m_validation_list = opj_procedure_list_create();
+        if (! jp2->m_validation_list) {
+            opj_jp2_destroy(jp2);
+            return 00;
+        }
+
+        /* execution list creation */
+        jp2->m_procedure_list = opj_procedure_list_create();
+        if (! jp2->m_procedure_list) {
+            opj_jp2_destroy(jp2);
+            return 00;
+        }
+    }
+
+    return jp2;
+}
+
+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(jp2->j2k,
+             flag,
+             out_stream);
+}
+
+opj_codestream_index_t* jp2_get_cstr_index(void* p_jp2)
+{
+    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(void* p_jp2)
+{
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+    return j2k_get_cstr_info(jp2->j2k);
+}
+
+OPJ_BOOL opj_jp2_set_decoded_resolution_factor(void *p_jp2,
+        OPJ_UINT32 res_factor,
+        opj_event_mgr_t * 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(
+    void *p_jp2,
+    const char* const* p_options,
+    opj_event_mgr_t * p_manager)
+{
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+    return opj_j2k_encoder_set_extra_options(jp2->j2k, p_options, p_manager);
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* JPIP specific */
+
+#ifdef USE_JPIP
+static OPJ_BOOL opj_jpip_write_iptr(opj_jp2_t *jp2,
+                                    opj_stream_private_t *cio,
+                                    opj_event_mgr_t * p_manager)
+{
+    OPJ_OFF_T j2k_codestream_exit;
+    OPJ_BYTE l_data_header [24];
+
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(cio != 00);
+    assert(p_manager != 00);
+    assert(opj_stream_has_seek(cio));
+
+    j2k_codestream_exit = opj_stream_tell(cio);
+    opj_write_bytes(l_data_header, 24, 4); /* size of iptr */
+    opj_write_bytes(l_data_header + 4, JPIP_IPTR,
+                    4);                                      /* IPTR */
+#if 0
+    opj_write_bytes(l_data_header + 4 + 4, 0, 8); /* offset */
+    opj_write_bytes(l_data_header + 8 + 8, 0, 8); /* length */
+#else
+    opj_write_double(l_data_header + 4 + 4, 0); /* offset */
+    opj_write_double(l_data_header + 8 + 8, 0); /* length */
+#endif
+
+    if (! opj_stream_seek(cio, jp2->jpip_iptr_offset, p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
+        return OPJ_FALSE;
+    }
+
+    if (opj_stream_write_data(cio, l_data_header, 24, p_manager) != 24) {
+        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
+        return OPJ_FALSE;
+    }
+
+    if (! opj_stream_seek(cio, j2k_codestream_exit, p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_jpip_write_fidx(opj_jp2_t *jp2,
+                                    opj_stream_private_t *cio,
+                                    opj_event_mgr_t * p_manager)
+{
+    OPJ_OFF_T j2k_codestream_exit;
+    OPJ_BYTE l_data_header [24];
+
+    OPJ_UNUSED(jp2);
+
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(cio != 00);
+    assert(p_manager != 00);
+    assert(opj_stream_has_seek(cio));
+
+    opj_write_bytes(l_data_header, 24, 4); /* size of iptr */
+    opj_write_bytes(l_data_header + 4, JPIP_FIDX,
+                    4);                                      /* IPTR */
+    opj_write_double(l_data_header + 4 + 4, 0); /* offset */
+    opj_write_double(l_data_header + 8 + 8, 0); /* length */
+
+    if (opj_stream_write_data(cio, l_data_header, 24, p_manager) != 24) {
+        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
+        return OPJ_FALSE;
+    }
+
+    j2k_codestream_exit = opj_stream_tell(cio);
+    if (! opj_stream_seek(cio, j2k_codestream_exit, p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_jpip_write_cidx(opj_jp2_t *jp2,
+                                    opj_stream_private_t *cio,
+                                    opj_event_mgr_t * p_manager)
+{
+    OPJ_OFF_T j2k_codestream_exit;
+    OPJ_BYTE l_data_header [24];
+
+    OPJ_UNUSED(jp2);
+
+    /* preconditions */
+    assert(jp2 != 00);
+    assert(cio != 00);
+    assert(p_manager != 00);
+    assert(opj_stream_has_seek(cio));
+
+    j2k_codestream_exit = opj_stream_tell(cio);
+    opj_write_bytes(l_data_header, 24, 4); /* size of iptr */
+    opj_write_bytes(l_data_header + 4, JPIP_CIDX,
+                    4);                                      /* IPTR */
+#if 0
+    opj_write_bytes(l_data_header + 4 + 4, 0, 8); /* offset */
+    opj_write_bytes(l_data_header + 8 + 8, 0, 8); /* length */
+#else
+    opj_write_double(l_data_header + 4 + 4, 0); /* offset */
+    opj_write_double(l_data_header + 8 + 8, 0); /* length */
+#endif
+
+    if (! opj_stream_seek(cio, j2k_codestream_exit, p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
+        return OPJ_FALSE;
+    }
+
+    if (opj_stream_write_data(cio, l_data_header, 24, p_manager) != 24) {
+        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
+        return OPJ_FALSE;
+    }
+
+    j2k_codestream_exit = opj_stream_tell(cio);
+    if (! opj_stream_seek(cio, j2k_codestream_exit, p_manager)) {
+        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+#if 0
+static void write_prxy(int offset_jp2c, int length_jp2c, int offset_idx,
+                       int length_idx, opj_stream_private_t *cio,
+                       opj_event_mgr_t * p_manager)
+{
+    OPJ_BYTE l_data_header [8];
+    OPJ_OFF_T len, lenp;
+
+    lenp = opj_stream_tell(cio);
+    opj_stream_skip(cio, 4, p_manager);         /* L [at the end] */
+    opj_write_bytes(l_data_header, JPIP_PRXY, 4); /* IPTR           */
+    opj_stream_write_data(cio, l_data_header, 4, p_manager);
+
+    opj_write_bytes(l_data_header, offset_jp2c, 8);  /* OOFF           */
+    opj_stream_write_data(cio, l_data_header, 8, p_manager);
+    opj_write_bytes(l_data_header, length_jp2c, 4);  /* OBH part 1     */
+    opj_write_bytes(l_data_header + 4, JP2_JP2C, 4); /* OBH part 2     */
+    opj_stream_write_data(cio, l_data_header, 8, p_manager);
+
+    opj_write_bytes(l_data_header, 1, 1); /* NI             */
+    opj_stream_write_data(cio, l_data_header, 1, p_manager);
+
+    opj_write_bytes(l_data_header, offset_idx, 8);   /* IOFF           */
+    opj_stream_write_data(cio, l_data_header, 8, p_manager);
+    opj_write_bytes(l_data_header, length_idx, 4);   /* IBH part 1     */
+    opj_write_bytes(l_data_header + 4, JPIP_CIDX, 4);  /* IBH part 2     */
+    opj_stream_write_data(cio, l_data_header, 8, p_manager);
+
+    len = opj_stream_tell(cio) - lenp;
+    opj_stream_skip(cio, lenp, p_manager);
+    opj_write_bytes(l_data_header, len, 4); /* L              */
+    opj_stream_write_data(cio, l_data_header, 4, p_manager);
+    opj_stream_seek(cio, lenp + len, p_manager);
+}
+#endif
+
+
+#if 0
+static int write_fidx(int offset_jp2c, int length_jp2c, int offset_idx,
+                      int length_idx, opj_stream_private_t *cio,
+                      opj_event_mgr_t * p_manager)
+{
+    OPJ_BYTE l_data_header [4];
+    OPJ_OFF_T len, lenp;
+
+    lenp = opj_stream_tell(cio);
+    opj_stream_skip(cio, 4, p_manager);
+    opj_write_bytes(l_data_header, JPIP_FIDX, 4); /* FIDX */
+    opj_stream_write_data(cio, l_data_header, 4, p_manager);
+
+    write_prxy(offset_jp2c, length_jp2c, offset_idx, length_idx, cio, p_manager);
+
+    len = opj_stream_tell(cio) - lenp;
+    opj_stream_skip(cio, lenp, p_manager);
+    opj_write_bytes(l_data_header, len, 4); /* L              */
+    opj_stream_write_data(cio, l_data_header, 4, p_manager);
+    opj_stream_seek(cio, lenp + len, p_manager);
+
+    return len;
+}
+#endif
+#endif /* USE_JPIP */
diff --git a/third_party/libopenjpeg/jp2.h b/third_party/libopenjpeg/jp2.h
new file mode 100644
index 0000000..fd9175a
--- /dev/null
+++ b/third_party/libopenjpeg/jp2.h
@@ -0,0 +1,521 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
+ * Copyright (c) 2012, CS Systemes d'Information, France
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef OPJ_JP2_H
+#define OPJ_JP2_H
+/**
+@file jp2.h
+@brief The JPEG-2000 file format Reader/Writer (JP2)
+
+*/
+
+/** @defgroup JP2 JP2 - JPEG-2000 file format reader/writer */
+/*@{*/
+
+/*#define JPIP_JPIP 0x6a706970*/
+
+#define     JP2_JP   0x6a502020    /**< JPEG 2000 signature box */
+#define     JP2_FTYP 0x66747970    /**< File type box */
+#define     JP2_JP2H 0x6a703268    /**< JP2 header box (super-box) */
+#define     JP2_IHDR 0x69686472    /**< Image header box */
+#define     JP2_COLR 0x636f6c72    /**< Colour specification box */
+#define     JP2_JP2C 0x6a703263    /**< Contiguous codestream box */
+#define     JP2_URL  0x75726c20    /**< Data entry URL box */
+#define     JP2_PCLR 0x70636c72    /**< Palette box */
+#define     JP2_CMAP 0x636d6170    /**< Component Mapping box */
+#define     JP2_CDEF 0x63646566    /**< Channel Definition box */
+#define     JP2_DTBL 0x6474626c    /**< Data Reference box */
+#define     JP2_BPCC 0x62706363    /**< Bits per component box */
+#define     JP2_JP2  0x6a703220    /**< File type fields */
+
+/* For the future */
+/* #define JP2_RES 0x72657320 */  /**< Resolution box (super-box) */
+/* #define JP2_JP2I 0x6a703269 */  /**< Intellectual property box */
+/* #define JP2_XML  0x786d6c20 */  /**< XML box */
+/* #define JP2_UUID 0x75756994 */  /**< UUID box */
+/* #define JP2_UINF 0x75696e66 */  /**< UUID info box (super-box) */
+/* #define JP2_ULST 0x756c7374 */  /**< UUID list box */
+
+/* ----------------------------------------------------------------------- */
+
+typedef enum {
+    JP2_STATE_NONE            = 0x0,
+    JP2_STATE_SIGNATURE       = 0x1,
+    JP2_STATE_FILE_TYPE       = 0x2,
+    JP2_STATE_HEADER          = 0x4,
+    JP2_STATE_CODESTREAM      = 0x8,
+    JP2_STATE_END_CODESTREAM  = 0x10,
+    JP2_STATE_UNKNOWN         = 0x7fffffff /* ISO C restricts enumerator values to range of 'int' */
+}
+JP2_STATE;
+
+typedef enum {
+    JP2_IMG_STATE_NONE        = 0x0,
+    JP2_IMG_STATE_UNKNOWN     = 0x7fffffff
+}
+JP2_IMG_STATE;
+
+/**
+Channel description: channel index, type, association
+*/
+typedef struct opj_jp2_cdef_info {
+    OPJ_UINT16 cn, typ, asoc;
+} opj_jp2_cdef_info_t;
+
+/**
+Channel descriptions and number of descriptions
+*/
+typedef struct opj_jp2_cdef {
+    opj_jp2_cdef_info_t *info;
+    OPJ_UINT16 n;
+} opj_jp2_cdef_t;
+
+/**
+Component mappings: channel index, mapping type, palette index
+*/
+typedef struct opj_jp2_cmap_comp {
+    OPJ_UINT16 cmp;
+    OPJ_BYTE mtyp, pcol;
+} opj_jp2_cmap_comp_t;
+
+/**
+Palette data: table entries, palette columns
+*/
+typedef struct opj_jp2_pclr {
+    OPJ_UINT32 *entries;
+    OPJ_BYTE *channel_sign;
+    OPJ_BYTE *channel_size;
+    opj_jp2_cmap_comp_t *cmap;
+    OPJ_UINT16 nr_entries;
+    OPJ_BYTE nr_channels;
+} opj_jp2_pclr_t;
+
+/**
+Collector for ICC profile, palette, component mapping, channel description
+*/
+typedef struct opj_jp2_color {
+    OPJ_BYTE *icc_profile_buf;
+    OPJ_UINT32 icc_profile_len;
+
+    opj_jp2_cdef_t *jp2_cdef;
+    opj_jp2_pclr_t *jp2_pclr;
+    OPJ_BYTE jp2_has_colr;
+} opj_jp2_color_t;
+
+/**
+JP2 component
+*/
+typedef struct opj_jp2_comps {
+    OPJ_UINT32 depth;
+    OPJ_UINT32 sgnd;
+    OPJ_UINT32 bpcc;
+} opj_jp2_comps_t;
+
+/**
+JPEG-2000 file format reader/writer
+*/
+typedef struct opj_jp2 {
+    /** handle to the J2K codec  */
+    opj_j2k_t *j2k;
+    /** list of validation procedures */
+    struct opj_procedure_list * m_validation_list;
+    /** list of execution procedures */
+    struct opj_procedure_list * m_procedure_list;
+
+    /* width of image */
+    OPJ_UINT32 w;
+    /* height of image */
+    OPJ_UINT32 h;
+    /* number of components in the image */
+    OPJ_UINT32 numcomps;
+    OPJ_UINT32 bpc;
+    OPJ_UINT32 C;
+    OPJ_UINT32 UnkC;
+    OPJ_UINT32 IPR;
+    OPJ_UINT32 meth;
+    OPJ_UINT32 approx;
+    OPJ_UINT32 enumcs;
+    OPJ_UINT32 precedence;
+    OPJ_UINT32 brand;
+    OPJ_UINT32 minversion;
+    OPJ_UINT32 numcl;
+    OPJ_UINT32 *cl;
+    opj_jp2_comps_t *comps;
+    /* FIXME: The following two variables are used to save offset
+      as we write out a JP2 file to disk. This mechanism is not flexible
+      as codec writers will need to extand those fields as new part
+      of the standard are implemented.
+    */
+    OPJ_OFF_T j2k_codestream_offset;
+    OPJ_OFF_T jpip_iptr_offset;
+    OPJ_BOOL jpip_on;
+    OPJ_UINT32 jp2_state;
+    OPJ_UINT32 jp2_img_state;
+
+    opj_jp2_color_t color;
+
+    OPJ_BOOL ignore_pclr_cmap_cdef;
+    OPJ_BYTE has_jp2h;
+    OPJ_BYTE has_ihdr;
+}
+opj_jp2_t;
+
+/**
+JP2 Box
+*/
+typedef struct opj_jp2_box {
+    OPJ_UINT32 length;
+    OPJ_UINT32 type;
+    OPJ_INT32 init_pos;
+} opj_jp2_box_t;
+
+typedef struct opj_jp2_header_handler {
+    /* marker value */
+    OPJ_UINT32 id;
+    /* action linked to the marker */
+    OPJ_BOOL(*handler)(opj_jp2_t *jp2,
+                       OPJ_BYTE *p_header_data,
+                       OPJ_UINT32 p_header_size,
+                       opj_event_mgr_t * p_manager);
+}
+opj_jp2_header_handler_t;
+
+
+typedef struct opj_jp2_img_header_writer_handler {
+    /* action to perform */
+    OPJ_BYTE*   (*handler)(opj_jp2_t *jp2, OPJ_UINT32 * p_data_size);
+    /* result of the action : data */
+    OPJ_BYTE*   m_data;
+    /* size of data */
+    OPJ_UINT32  m_size;
+}
+opj_jp2_img_header_writer_handler_t;
+
+/** @name Exported functions */
+/*@{*/
+/* ----------------------------------------------------------------------- */
+
+/**
+Setup the decoder decoding parameters using user parameters.
+Decoding parameters are returned in jp2->j2k->cp.
+@param p_jp2 JP2 decompressor handle
+@param parameters decompression 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 p_jp2 JP2 decompressor handle
+@param strict OPJ_TRUE for strict mode
+*/
+void opj_jp2_decoder_set_strict_mode(void *p_jp2, OPJ_BOOL strict);
+
+/** Allocates worker threads for the compressor/decompressor.
+ *
+ * @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(void *p_jp2, OPJ_UINT32 num_threads);
+
+/**
+ * Decode an image from a JPEG-2000 file stream
+ * @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(void *p_jp2,
+                        opj_stream_private_t *p_stream,
+                        opj_image_t* p_image,
+                        opj_event_mgr_t * p_manager);
+
+/**
+ * Setup the encoder parameters using the current image and using user parameters.
+ * Coding parameters are returned in jp2->j2k->cp.
+ *
+ * @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(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 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(void *p_jp2,
+                        opj_stream_private_t *stream,
+                        opj_event_mgr_t * p_manager);
+
+
+/**
+ * Starts a compression scheme, i.e. validates the codec parameters, writes the header.
+ *
+ * @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(void *p_jp2,
+                                opj_stream_private_t *stream,
+                                opj_image_t * p_image,
+                                opj_event_mgr_t * p_manager);
+
+
+/**
+ * Ends the compression procedures and possibiliy add data to be read after the
+ * codestream.
+ */
+OPJ_BOOL opj_jp2_end_compress(void *p_jp2,
+                              opj_stream_private_t *cio,
+                              opj_event_mgr_t * p_manager);
+
+/* ----------------------------------------------------------------------- */
+
+/**
+ * Ends the decompression procedures and possibiliy add data to be read after the
+ * codestream.
+ */
+OPJ_BOOL opj_jp2_end_decompress(void *p_jp2,
+                                opj_stream_private_t *cio,
+                                opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a jpeg2000 file header structure.
+ *
+ * @param p_stream the stream to read data from.
+ * @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,
+                             void *p_jp2,
+                             opj_image_t ** p_image,
+                             opj_event_mgr_t * p_manager);
+
+/** Sets the indices of the components to decode.
+ *
+ * @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.
+ * @param p_manager Event manager;
+ *
+ * @return OPJ_TRUE in case of success.
+ */
+OPJ_BOOL opj_jp2_set_decoded_components(void *p_jp2,
+                                        OPJ_UINT32 numcomps,
+                                        const OPJ_UINT32* comps_indices,
+                                        opj_event_mgr_t * p_manager);
+
+/**
+ * Reads a tile header.
+ * @param  p_jp2         the jpeg2000 codec.
+ * @param  p_tile_index  FIXME DOC
+ * @param  p_data_size   FIXME DOC
+ * @param  p_tile_x0     FIXME DOC
+ * @param  p_tile_y0     FIXME DOC
+ * @param  p_tile_x1     FIXME DOC
+ * @param  p_tile_y1     FIXME DOC
+ * @param  p_nb_comps    FIXME DOC
+ * @param  p_go_on       FIXME DOC
+ * @param  p_stream      the stream to write data to.
+ * @param  p_manager     the user event manager.
+ */
+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,
+                                  OPJ_INT32 * p_tile_y0,
+                                  OPJ_INT32 * p_tile_x1,
+                                  OPJ_INT32 * p_tile_y1,
+                                  OPJ_UINT32 * p_nb_comps,
+                                  OPJ_BOOL * p_go_on,
+                                  opj_stream_private_t *p_stream,
+                                  opj_event_mgr_t * p_manager);
+
+/**
+ * Writes a tile.
+ *
+ * @param  p_jp2    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_jp2_write_tile(void *p_jp2,
+                            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);
+
+/**
+ * Decode tile data.
+ * @param  p_jp2    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.
+ *
+ * @return FIXME DOC
+ */
+OPJ_BOOL opj_jp2_decode_tile(void * p_jp2,
+                             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);
+
+/**
+ * Creates a jpeg2000 file decompressor.
+ *
+ * @return  an empty jpeg2000 file codec.
+ */
+opj_jp2_t* opj_jp2_create(OPJ_BOOL p_is_decoder);
+
+/**
+Destroy a JP2 decompressor handle
+@param p_jp2 JP2 decompressor handle to destroy
+*/
+void opj_jp2_destroy(void *p_jp2);
+
+
+/**
+ * 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_jp2      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).
+ * @param  p_end_x      the right position of the rectangle to decode (in image coordinates).
+ * @param  p_end_y      the bottom position of the rectangle to decode (in image coordinates).
+ * @param  p_manager    the user event manager
+ *
+ * @return  true      if the area could be set.
+ */
+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);
+
+/**
+*
+*/
+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_BOOL opj_jp2_set_decoded_resolution_factor(void *p_jp2,
+        OPJ_UINT32 res_factor,
+        opj_event_mgr_t * p_manager);
+
+/**
+ * Specify extra options for the encoder.
+ *
+ * @param  p_jp2        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_jp2_encoder_set_extra_options(
+    void *p_jp2,
+    const char* const* p_options,
+    opj_event_mgr_t * p_manager);
+
+
+/* TODO MSD: clean these 3 functions */
+/**
+ * Dump some elements from the JP2 decompression structure .
+ *
+ *@param p_jp2        the jp2 codec.
+ *@param flag        flag to describe what elements are dump.
+ *@param out_stream      output stream where dump the elements.
+ *
+*/
+void jp2_dump(void* p_jp2, OPJ_INT32 flag, FILE* out_stream);
+
+/**
+ * Get the codestream info from a JPEG2000 codec.
+ *
+ *@param  p_jp2        jp2 codec.
+ *
+ *@return  the codestream information extract from the jpg2000 codec
+ */
+opj_codestream_info_v2_t* jp2_get_cstr_info(void* p_jp2);
+
+/**
+ * Get the codestream index from a JPEG2000 codec.
+ *
+ *@param  p_jp2        jp2 codec.
+ *
+ *@return  the codestream index extract from the jpg2000 codec
+ */
+opj_codestream_index_t* jp2_get_cstr_index(void* p_jp2);
+
+
+/*@}*/
+
+/*@}*/
+
+#endif /* OPJ_JP2_H */
+
diff --git a/third_party/libopenjpeg/mct.c b/third_party/libopenjpeg/mct.c
new file mode 100644
index 0000000..2446efb
--- /dev/null
+++ b/third_party/libopenjpeg/mct.c
@@ -0,0 +1,466 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
+ * Copyright (c) 2012, CS Systemes d'Information, France
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(__SSE__) && !defined(_M_IX86) && !defined(__i386)
+#define USE_SSE
+#include <xmmintrin.h>
+#endif
+#if defined(__SSE2__) && !defined(_M_IX86) && !defined(__i386)
+#define USE_SSE2
+#include <emmintrin.h>
+#endif
+#if defined(__SSE4_1__) && !defined(_M_IX86) && !defined(__i386)
+#include <smmintrin.h>
+#endif
+
+#include "opj_includes.h"
+
+/* <summary> */
+/* This table contains the norms of the basis function of the reversible MCT. */
+/* </summary> */
+static const OPJ_FLOAT64 opj_mct_norms[3] = { 1.732, .8292, .8292 };
+
+/* <summary> */
+/* This table contains the norms of the basis function of the irreversible MCT. */
+/* </summary> */
+static const OPJ_FLOAT64 opj_mct_norms_real[3] = { 1.732, 1.805, 1.573 };
+
+const OPJ_FLOAT64 * opj_mct_get_mct_norms()
+{
+    return opj_mct_norms;
+}
+
+const OPJ_FLOAT64 * opj_mct_get_mct_norms_real()
+{
+    return opj_mct_norms_real;
+}
+
+/* <summary> */
+/* Forward reversible MCT. */
+/* </summary> */
+#ifdef USE_SSE2
+void opj_mct_encode(
+    OPJ_INT32* OPJ_RESTRICT c0,
+    OPJ_INT32* OPJ_RESTRICT c1,
+    OPJ_INT32* OPJ_RESTRICT c2,
+    OPJ_SIZE_T n)
+{
+    OPJ_SIZE_T i;
+    const OPJ_SIZE_T len = n;
+    /* buffer are aligned on 16 bytes */
+    assert(((size_t)c0 & 0xf) == 0);
+    assert(((size_t)c1 & 0xf) == 0);
+    assert(((size_t)c2 & 0xf) == 0);
+
+    for (i = 0; i < (len & ~3U); i += 4) {
+        __m128i y, u, v;
+        __m128i r = _mm_load_si128((const __m128i *) & (c0[i]));
+        __m128i g = _mm_load_si128((const __m128i *) & (c1[i]));
+        __m128i b = _mm_load_si128((const __m128i *) & (c2[i]));
+        y = _mm_add_epi32(g, g);
+        y = _mm_add_epi32(y, b);
+        y = _mm_add_epi32(y, r);
+        y = _mm_srai_epi32(y, 2);
+        u = _mm_sub_epi32(b, g);
+        v = _mm_sub_epi32(r, g);
+        _mm_store_si128((__m128i *) & (c0[i]), y);
+        _mm_store_si128((__m128i *) & (c1[i]), u);
+        _mm_store_si128((__m128i *) & (c2[i]), v);
+    }
+
+    for (; i < len; ++i) {
+        OPJ_INT32 r = c0[i];
+        OPJ_INT32 g = c1[i];
+        OPJ_INT32 b = c2[i];
+        OPJ_INT32 y = (r + (g * 2) + b) >> 2;
+        OPJ_INT32 u = b - g;
+        OPJ_INT32 v = r - g;
+        c0[i] = y;
+        c1[i] = u;
+        c2[i] = v;
+    }
+}
+#else
+void opj_mct_encode(
+    OPJ_INT32* OPJ_RESTRICT c0,
+    OPJ_INT32* OPJ_RESTRICT c1,
+    OPJ_INT32* OPJ_RESTRICT c2,
+    OPJ_SIZE_T n)
+{
+    OPJ_SIZE_T i;
+    const OPJ_SIZE_T len = n;
+
+    for (i = 0; i < len; ++i) {
+        OPJ_INT32 r = c0[i];
+        OPJ_INT32 g = c1[i];
+        OPJ_INT32 b = c2[i];
+        OPJ_INT32 y = (r + (g * 2) + b) >> 2;
+        OPJ_INT32 u = b - g;
+        OPJ_INT32 v = r - g;
+        c0[i] = y;
+        c1[i] = u;
+        c2[i] = v;
+    }
+}
+#endif
+
+/* <summary> */
+/* Inverse reversible MCT. */
+/* </summary> */
+#ifdef USE_SSE2
+void opj_mct_decode(
+    OPJ_INT32* OPJ_RESTRICT c0,
+    OPJ_INT32* OPJ_RESTRICT c1,
+    OPJ_INT32* OPJ_RESTRICT c2,
+    OPJ_SIZE_T n)
+{
+    OPJ_SIZE_T i;
+    const OPJ_SIZE_T len = n;
+
+    for (i = 0; i < (len & ~3U); i += 4) {
+        __m128i r, g, b;
+        __m128i y = _mm_load_si128((const __m128i *) & (c0[i]));
+        __m128i u = _mm_load_si128((const __m128i *) & (c1[i]));
+        __m128i v = _mm_load_si128((const __m128i *) & (c2[i]));
+        g = y;
+        g = _mm_sub_epi32(g, _mm_srai_epi32(_mm_add_epi32(u, v), 2));
+        r = _mm_add_epi32(v, g);
+        b = _mm_add_epi32(u, g);
+        _mm_store_si128((__m128i *) & (c0[i]), r);
+        _mm_store_si128((__m128i *) & (c1[i]), g);
+        _mm_store_si128((__m128i *) & (c2[i]), b);
+    }
+    for (; i < len; ++i) {
+        OPJ_INT32 y = c0[i];
+        OPJ_INT32 u = c1[i];
+        OPJ_INT32 v = c2[i];
+        OPJ_INT32 g = y - ((u + v) >> 2);
+        OPJ_INT32 r = v + g;
+        OPJ_INT32 b = u + g;
+        c0[i] = r;
+        c1[i] = g;
+        c2[i] = b;
+    }
+}
+#else
+void opj_mct_decode(
+    OPJ_INT32* OPJ_RESTRICT c0,
+    OPJ_INT32* OPJ_RESTRICT c1,
+    OPJ_INT32* OPJ_RESTRICT c2,
+    OPJ_SIZE_T n)
+{
+    OPJ_SIZE_T i;
+    for (i = 0; i < n; ++i) {
+        OPJ_INT32 y = c0[i];
+        OPJ_INT32 u = c1[i];
+        OPJ_INT32 v = c2[i];
+        OPJ_INT32 g = y - ((u + v) >> 2);
+        OPJ_INT32 r = v + g;
+        OPJ_INT32 b = u + g;
+        c0[i] = r;
+        c1[i] = g;
+        c2[i] = b;
+    }
+}
+#endif
+
+/* <summary> */
+/* Get norm of basis function of reversible MCT. */
+/* </summary> */
+OPJ_FLOAT64 opj_mct_getnorm(OPJ_UINT32 compno)
+{
+    return opj_mct_norms[compno];
+}
+
+/* <summary> */
+/* Forward irreversible MCT. */
+/* </summary> */
+void opj_mct_encode_real(
+    OPJ_FLOAT32* OPJ_RESTRICT c0,
+    OPJ_FLOAT32* OPJ_RESTRICT c1,
+    OPJ_FLOAT32* OPJ_RESTRICT c2,
+    OPJ_SIZE_T n)
+{
+    OPJ_SIZE_T i;
+#ifdef USE_SSE
+    const __m128 YR = _mm_set1_ps(0.299f);
+    const __m128 YG = _mm_set1_ps(0.587f);
+    const __m128 YB = _mm_set1_ps(0.114f);
+    const __m128 UR = _mm_set1_ps(-0.16875f);
+    const __m128 UG = _mm_set1_ps(-0.331260f);
+    const __m128 UB = _mm_set1_ps(0.5f);
+    const __m128 VR = _mm_set1_ps(0.5f);
+    const __m128 VG = _mm_set1_ps(-0.41869f);
+    const __m128 VB = _mm_set1_ps(-0.08131f);
+    for (i = 0; i < (n >> 3); i ++) {
+        __m128 r, g, b, y, u, v;
+
+        r = _mm_load_ps(c0);
+        g = _mm_load_ps(c1);
+        b = _mm_load_ps(c2);
+        y = _mm_add_ps(_mm_add_ps(_mm_mul_ps(r, YR), _mm_mul_ps(g, YG)),
+                       _mm_mul_ps(b, YB));
+        u = _mm_add_ps(_mm_add_ps(_mm_mul_ps(r, UR), _mm_mul_ps(g, UG)),
+                       _mm_mul_ps(b, UB));
+        v = _mm_add_ps(_mm_add_ps(_mm_mul_ps(r, VR), _mm_mul_ps(g, VG)),
+                       _mm_mul_ps(b, VB));
+        _mm_store_ps(c0, y);
+        _mm_store_ps(c1, u);
+        _mm_store_ps(c2, v);
+        c0 += 4;
+        c1 += 4;
+        c2 += 4;
+
+        r = _mm_load_ps(c0);
+        g = _mm_load_ps(c1);
+        b = _mm_load_ps(c2);
+        y = _mm_add_ps(_mm_add_ps(_mm_mul_ps(r, YR), _mm_mul_ps(g, YG)),
+                       _mm_mul_ps(b, YB));
+        u = _mm_add_ps(_mm_add_ps(_mm_mul_ps(r, UR), _mm_mul_ps(g, UG)),
+                       _mm_mul_ps(b, UB));
+        v = _mm_add_ps(_mm_add_ps(_mm_mul_ps(r, VR), _mm_mul_ps(g, VG)),
+                       _mm_mul_ps(b, VB));
+        _mm_store_ps(c0, y);
+        _mm_store_ps(c1, u);
+        _mm_store_ps(c2, v);
+        c0 += 4;
+        c1 += 4;
+        c2 += 4;
+    }
+    n &= 7;
+#endif
+    for (i = 0; i < n; ++i) {
+        OPJ_FLOAT32 r = c0[i];
+        OPJ_FLOAT32 g = c1[i];
+        OPJ_FLOAT32 b = c2[i];
+        OPJ_FLOAT32 y = 0.299f * r + 0.587f * g + 0.114f * b;
+        OPJ_FLOAT32 u = -0.16875f * r - 0.331260f * g + 0.5f * b;
+        OPJ_FLOAT32 v = 0.5f * r - 0.41869f * g - 0.08131f * b;
+        c0[i] = y;
+        c1[i] = u;
+        c2[i] = v;
+    }
+}
+
+/* <summary> */
+/* Inverse irreversible MCT. */
+/* </summary> */
+void opj_mct_decode_real(
+    OPJ_FLOAT32* OPJ_RESTRICT c0,
+    OPJ_FLOAT32* OPJ_RESTRICT c1,
+    OPJ_FLOAT32* OPJ_RESTRICT c2,
+    OPJ_SIZE_T n)
+{
+    OPJ_SIZE_T i;
+#ifdef USE_SSE
+    __m128 vrv, vgu, vgv, vbu;
+    vrv = _mm_set1_ps(1.402f);
+    vgu = _mm_set1_ps(0.34413f);
+    vgv = _mm_set1_ps(0.71414f);
+    vbu = _mm_set1_ps(1.772f);
+    for (i = 0; i < (n >> 3); ++i) {
+        __m128 vy, vu, vv;
+        __m128 vr, vg, vb;
+
+        vy = _mm_load_ps(c0);
+        vu = _mm_load_ps(c1);
+        vv = _mm_load_ps(c2);
+        vr = _mm_add_ps(vy, _mm_mul_ps(vv, vrv));
+        vg = _mm_sub_ps(_mm_sub_ps(vy, _mm_mul_ps(vu, vgu)), _mm_mul_ps(vv, vgv));
+        vb = _mm_add_ps(vy, _mm_mul_ps(vu, vbu));
+        _mm_store_ps(c0, vr);
+        _mm_store_ps(c1, vg);
+        _mm_store_ps(c2, vb);
+        c0 += 4;
+        c1 += 4;
+        c2 += 4;
+
+        vy = _mm_load_ps(c0);
+        vu = _mm_load_ps(c1);
+        vv = _mm_load_ps(c2);
+        vr = _mm_add_ps(vy, _mm_mul_ps(vv, vrv));
+        vg = _mm_sub_ps(_mm_sub_ps(vy, _mm_mul_ps(vu, vgu)), _mm_mul_ps(vv, vgv));
+        vb = _mm_add_ps(vy, _mm_mul_ps(vu, vbu));
+        _mm_store_ps(c0, vr);
+        _mm_store_ps(c1, vg);
+        _mm_store_ps(c2, vb);
+        c0 += 4;
+        c1 += 4;
+        c2 += 4;
+    }
+    n &= 7;
+#endif
+    for (i = 0; i < n; ++i) {
+        OPJ_FLOAT32 y = c0[i];
+        OPJ_FLOAT32 u = c1[i];
+        OPJ_FLOAT32 v = c2[i];
+        OPJ_FLOAT32 r = y + (v * 1.402f);
+        OPJ_FLOAT32 g = y - (u * 0.34413f) - (v * (0.71414f));
+        OPJ_FLOAT32 b = y + (u * 1.772f);
+        c0[i] = r;
+        c1[i] = g;
+        c2[i] = b;
+    }
+}
+
+/* <summary> */
+/* Get norm of basis function of irreversible MCT. */
+/* </summary> */
+OPJ_FLOAT64 opj_mct_getnorm_real(OPJ_UINT32 compno)
+{
+    return opj_mct_norms_real[compno];
+}
+
+
+OPJ_BOOL opj_mct_encode_custom(
+    OPJ_BYTE * pCodingdata,
+    OPJ_SIZE_T n,
+    OPJ_BYTE ** pData,
+    OPJ_UINT32 pNbComp,
+    OPJ_UINT32 isSigned)
+{
+    OPJ_FLOAT32 * lMct = (OPJ_FLOAT32 *) pCodingdata;
+    OPJ_SIZE_T i;
+    OPJ_UINT32 j;
+    OPJ_UINT32 k;
+    OPJ_UINT32 lNbMatCoeff = pNbComp * pNbComp;
+    OPJ_INT32 * lCurrentData = 00;
+    OPJ_INT32 * lCurrentMatrix = 00;
+    OPJ_INT32 ** lData = (OPJ_INT32 **) pData;
+    OPJ_UINT32 lMultiplicator = 1 << 13;
+    OPJ_INT32 * lMctPtr;
+
+    OPJ_ARG_NOT_USED(isSigned);
+
+    lCurrentData = (OPJ_INT32 *) opj_malloc((pNbComp + lNbMatCoeff) * sizeof(
+            OPJ_INT32));
+    if (! lCurrentData) {
+        return OPJ_FALSE;
+    }
+
+    lCurrentMatrix = lCurrentData + pNbComp;
+
+    for (i = 0; i < lNbMatCoeff; ++i) {
+        lCurrentMatrix[i] = (OPJ_INT32)(*(lMct++) * (OPJ_FLOAT32)lMultiplicator);
+    }
+
+    for (i = 0; i < n; ++i)  {
+        lMctPtr = lCurrentMatrix;
+        for (j = 0; j < pNbComp; ++j) {
+            lCurrentData[j] = (*(lData[j]));
+        }
+
+        for (j = 0; j < pNbComp; ++j) {
+            *(lData[j]) = 0;
+            for (k = 0; k < pNbComp; ++k) {
+                *(lData[j]) += opj_int_fix_mul(*lMctPtr, lCurrentData[k]);
+                ++lMctPtr;
+            }
+
+            ++lData[j];
+        }
+    }
+
+    opj_free(lCurrentData);
+
+    return OPJ_TRUE;
+}
+
+OPJ_BOOL opj_mct_decode_custom(
+    OPJ_BYTE * pDecodingData,
+    OPJ_SIZE_T n,
+    OPJ_BYTE ** pData,
+    OPJ_UINT32 pNbComp,
+    OPJ_UINT32 isSigned)
+{
+    OPJ_FLOAT32 * lMct;
+    OPJ_SIZE_T i;
+    OPJ_UINT32 j;
+    OPJ_UINT32 k;
+
+    OPJ_FLOAT32 * lCurrentData = 00;
+    OPJ_FLOAT32 * lCurrentResult = 00;
+    OPJ_FLOAT32 ** lData = (OPJ_FLOAT32 **) pData;
+
+    OPJ_ARG_NOT_USED(isSigned);
+
+    lCurrentData = (OPJ_FLOAT32 *) opj_malloc(2 * pNbComp * sizeof(OPJ_FLOAT32));
+    if (! lCurrentData) {
+        return OPJ_FALSE;
+    }
+    lCurrentResult = lCurrentData + pNbComp;
+
+    for (i = 0; i < n; ++i) {
+        lMct = (OPJ_FLOAT32 *) pDecodingData;
+        for (j = 0; j < pNbComp; ++j) {
+            lCurrentData[j] = (OPJ_FLOAT32)(*(lData[j]));
+        }
+        for (j = 0; j < pNbComp; ++j) {
+            lCurrentResult[j] = 0;
+            for (k = 0; k < pNbComp; ++k) {
+                lCurrentResult[j] += *(lMct++) * lCurrentData[k];
+            }
+            *(lData[j]++) = (OPJ_FLOAT32)(lCurrentResult[j]);
+        }
+    }
+    opj_free(lCurrentData);
+    return OPJ_TRUE;
+}
+
+void opj_calculate_norms(OPJ_FLOAT64 * pNorms,
+                         OPJ_UINT32 pNbComps,
+                         OPJ_FLOAT32 * pMatrix)
+{
+    OPJ_UINT32 i, j, lIndex;
+    OPJ_FLOAT32 lCurrentValue;
+    OPJ_FLOAT64 * lNorms = (OPJ_FLOAT64 *) pNorms;
+    OPJ_FLOAT32 * lMatrix = (OPJ_FLOAT32 *) pMatrix;
+
+    for (i = 0; i < pNbComps; ++i) {
+        lNorms[i] = 0;
+        lIndex = i;
+
+        for (j = 0; j < pNbComps; ++j) {
+            lCurrentValue = lMatrix[lIndex];
+            lIndex += pNbComps;
+            lNorms[i] += (OPJ_FLOAT64) lCurrentValue * lCurrentValue;
+        }
+        lNorms[i] = sqrt(lNorms[i]);
+    }
+}
diff --git a/third_party/libopenjpeg/mct.h b/third_party/libopenjpeg/mct.h
new file mode 100644
index 0000000..3e1f5e4
--- /dev/null
+++ b/third_party/libopenjpeg/mct.h
@@ -0,0 +1,160 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
+ * Copyright (c) 2012, CS Systemes d'Information, France
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef OPJ_MCT_H
+#define OPJ_MCT_H
+/**
+@file mct.h
+@brief Implementation of a multi-component transforms (MCT)
+
+The functions in MCT.C have for goal to realize reversible and irreversible multicomponent
+transform. The functions in MCT.C are used by some function in TCD.C.
+*/
+
+/** @defgroup MCT MCT - Implementation of a multi-component transform */
+/*@{*/
+
+/** @name Exported functions */
+/*@{*/
+/* ----------------------------------------------------------------------- */
+/**
+Apply a reversible multi-component transform to an image
+@param c0 Samples for red component
+@param c1 Samples for green component
+@param c2 Samples blue component
+@param n Number of samples for each component
+*/
+void opj_mct_encode(OPJ_INT32* OPJ_RESTRICT c0, OPJ_INT32* OPJ_RESTRICT c1,
+                    OPJ_INT32* OPJ_RESTRICT c2, OPJ_SIZE_T n);
+/**
+Apply a reversible multi-component inverse transform to an image
+@param c0 Samples for luminance component
+@param c1 Samples for red chrominance component
+@param c2 Samples for blue chrominance component
+@param n Number of samples for each component
+*/
+void opj_mct_decode(OPJ_INT32* OPJ_RESTRICT c0, OPJ_INT32* OPJ_RESTRICT c1,
+                    OPJ_INT32* OPJ_RESTRICT c2, OPJ_SIZE_T n);
+/**
+Get norm of the basis function used for the reversible multi-component transform
+@param compno Number of the component (0->Y, 1->U, 2->V)
+@return
+*/
+OPJ_FLOAT64 opj_mct_getnorm(OPJ_UINT32 compno);
+
+/**
+Apply an irreversible multi-component transform to an image
+@param c0 Samples for red component
+@param c1 Samples for green component
+@param c2 Samples blue component
+@param n Number of samples for each component
+*/
+void opj_mct_encode_real(OPJ_FLOAT32* OPJ_RESTRICT c0,
+                         OPJ_FLOAT32* OPJ_RESTRICT c1,
+                         OPJ_FLOAT32* OPJ_RESTRICT c2, OPJ_SIZE_T n);
+/**
+Apply an irreversible multi-component inverse transform to an image
+@param c0 Samples for luminance component
+@param c1 Samples for red chrominance component
+@param c2 Samples for blue chrominance component
+@param n Number of samples for each component
+*/
+void opj_mct_decode_real(OPJ_FLOAT32* OPJ_RESTRICT c0,
+                         OPJ_FLOAT32* OPJ_RESTRICT c1, OPJ_FLOAT32* OPJ_RESTRICT c2, OPJ_SIZE_T n);
+/**
+Get norm of the basis function used for the irreversible multi-component transform
+@param compno Number of the component (0->Y, 1->U, 2->V)
+@return
+*/
+OPJ_FLOAT64 opj_mct_getnorm_real(OPJ_UINT32 compno);
+
+/**
+FIXME DOC
+@param p_coding_data    MCT data
+@param n                size of components
+@param p_data           components
+@param p_nb_comp        nb of components (i.e. size of p_data)
+@param is_signed        tells if the data is signed
+@return OPJ_FALSE if function encounter a problem, OPJ_TRUE otherwise
+*/
+OPJ_BOOL opj_mct_encode_custom(
+    OPJ_BYTE * p_coding_data,
+    OPJ_SIZE_T n,
+    OPJ_BYTE ** p_data,
+    OPJ_UINT32 p_nb_comp,
+    OPJ_UINT32 is_signed);
+/**
+FIXME DOC
+@param pDecodingData    MCT data
+@param n                size of components
+@param pData            components
+@param pNbComp          nb of components (i.e. size of p_data)
+@param isSigned         tells if the data is signed
+@return OPJ_FALSE if function encounter a problem, OPJ_TRUE otherwise
+*/
+OPJ_BOOL opj_mct_decode_custom(
+    OPJ_BYTE * pDecodingData,
+    OPJ_SIZE_T n,
+    OPJ_BYTE ** pData,
+    OPJ_UINT32 pNbComp,
+    OPJ_UINT32 isSigned);
+/**
+FIXME DOC
+@param pNorms           MCT data
+@param p_nb_comps       size of components
+@param pMatrix          components
+@return
+*/
+void opj_calculate_norms(OPJ_FLOAT64 * pNorms,
+                         OPJ_UINT32 p_nb_comps,
+                         OPJ_FLOAT32 * pMatrix);
+/**
+FIXME DOC
+*/
+const OPJ_FLOAT64 * opj_mct_get_mct_norms(void);
+/**
+FIXME DOC
+*/
+const OPJ_FLOAT64 * opj_mct_get_mct_norms_real(void);
+/* ----------------------------------------------------------------------- */
+/*@}*/
+
+/*@}*/
+
+#endif /* OPJ_MCT_H */
diff --git a/third_party/libopenjpeg/mqc.c b/third_party/libopenjpeg/mqc.c
new file mode 100644
index 0000000..3caab9e
--- /dev/null
+++ b/third_party/libopenjpeg/mqc.c
@@ -0,0 +1,519 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2008, Jerome Fimes, Communications & Systemes <jerome.fimes@c-s.fr>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "opj_includes.h"
+
+#include <assert.h>
+
+/** @defgroup MQC MQC - Implementation of an MQ-Coder */
+/*@{*/
+
+/** @name Local static functions */
+/*@{*/
+
+/**
+Fill mqc->c with 1's for flushing
+@param mqc MQC handle
+*/
+static void opj_mqc_setbits(opj_mqc_t *mqc);
+/*@}*/
+
+/*@}*/
+
+/* <summary> */
+/* This array defines all the possible states for a context. */
+/* </summary> */
+static const opj_mqc_state_t mqc_states[47 * 2] = {
+    {0x5601, 0, &mqc_states[2], &mqc_states[3]},
+    {0x5601, 1, &mqc_states[3], &mqc_states[2]},
+    {0x3401, 0, &mqc_states[4], &mqc_states[12]},
+    {0x3401, 1, &mqc_states[5], &mqc_states[13]},
+    {0x1801, 0, &mqc_states[6], &mqc_states[18]},
+    {0x1801, 1, &mqc_states[7], &mqc_states[19]},
+    {0x0ac1, 0, &mqc_states[8], &mqc_states[24]},
+    {0x0ac1, 1, &mqc_states[9], &mqc_states[25]},
+    {0x0521, 0, &mqc_states[10], &mqc_states[58]},
+    {0x0521, 1, &mqc_states[11], &mqc_states[59]},
+    {0x0221, 0, &mqc_states[76], &mqc_states[66]},
+    {0x0221, 1, &mqc_states[77], &mqc_states[67]},
+    {0x5601, 0, &mqc_states[14], &mqc_states[13]},
+    {0x5601, 1, &mqc_states[15], &mqc_states[12]},
+    {0x5401, 0, &mqc_states[16], &mqc_states[28]},
+    {0x5401, 1, &mqc_states[17], &mqc_states[29]},
+    {0x4801, 0, &mqc_states[18], &mqc_states[28]},
+    {0x4801, 1, &mqc_states[19], &mqc_states[29]},
+    {0x3801, 0, &mqc_states[20], &mqc_states[28]},
+    {0x3801, 1, &mqc_states[21], &mqc_states[29]},
+    {0x3001, 0, &mqc_states[22], &mqc_states[34]},
+    {0x3001, 1, &mqc_states[23], &mqc_states[35]},
+    {0x2401, 0, &mqc_states[24], &mqc_states[36]},
+    {0x2401, 1, &mqc_states[25], &mqc_states[37]},
+    {0x1c01, 0, &mqc_states[26], &mqc_states[40]},
+    {0x1c01, 1, &mqc_states[27], &mqc_states[41]},
+    {0x1601, 0, &mqc_states[58], &mqc_states[42]},
+    {0x1601, 1, &mqc_states[59], &mqc_states[43]},
+    {0x5601, 0, &mqc_states[30], &mqc_states[29]},
+    {0x5601, 1, &mqc_states[31], &mqc_states[28]},
+    {0x5401, 0, &mqc_states[32], &mqc_states[28]},
+    {0x5401, 1, &mqc_states[33], &mqc_states[29]},
+    {0x5101, 0, &mqc_states[34], &mqc_states[30]},
+    {0x5101, 1, &mqc_states[35], &mqc_states[31]},
+    {0x4801, 0, &mqc_states[36], &mqc_states[32]},
+    {0x4801, 1, &mqc_states[37], &mqc_states[33]},
+    {0x3801, 0, &mqc_states[38], &mqc_states[34]},
+    {0x3801, 1, &mqc_states[39], &mqc_states[35]},
+    {0x3401, 0, &mqc_states[40], &mqc_states[36]},
+    {0x3401, 1, &mqc_states[41], &mqc_states[37]},
+    {0x3001, 0, &mqc_states[42], &mqc_states[38]},
+    {0x3001, 1, &mqc_states[43], &mqc_states[39]},
+    {0x2801, 0, &mqc_states[44], &mqc_states[38]},
+    {0x2801, 1, &mqc_states[45], &mqc_states[39]},
+    {0x2401, 0, &mqc_states[46], &mqc_states[40]},
+    {0x2401, 1, &mqc_states[47], &mqc_states[41]},
+    {0x2201, 0, &mqc_states[48], &mqc_states[42]},
+    {0x2201, 1, &mqc_states[49], &mqc_states[43]},
+    {0x1c01, 0, &mqc_states[50], &mqc_states[44]},
+    {0x1c01, 1, &mqc_states[51], &mqc_states[45]},
+    {0x1801, 0, &mqc_states[52], &mqc_states[46]},
+    {0x1801, 1, &mqc_states[53], &mqc_states[47]},
+    {0x1601, 0, &mqc_states[54], &mqc_states[48]},
+    {0x1601, 1, &mqc_states[55], &mqc_states[49]},
+    {0x1401, 0, &mqc_states[56], &mqc_states[50]},
+    {0x1401, 1, &mqc_states[57], &mqc_states[51]},
+    {0x1201, 0, &mqc_states[58], &mqc_states[52]},
+    {0x1201, 1, &mqc_states[59], &mqc_states[53]},
+    {0x1101, 0, &mqc_states[60], &mqc_states[54]},
+    {0x1101, 1, &mqc_states[61], &mqc_states[55]},
+    {0x0ac1, 0, &mqc_states[62], &mqc_states[56]},
+    {0x0ac1, 1, &mqc_states[63], &mqc_states[57]},
+    {0x09c1, 0, &mqc_states[64], &mqc_states[58]},
+    {0x09c1, 1, &mqc_states[65], &mqc_states[59]},
+    {0x08a1, 0, &mqc_states[66], &mqc_states[60]},
+    {0x08a1, 1, &mqc_states[67], &mqc_states[61]},
+    {0x0521, 0, &mqc_states[68], &mqc_states[62]},
+    {0x0521, 1, &mqc_states[69], &mqc_states[63]},
+    {0x0441, 0, &mqc_states[70], &mqc_states[64]},
+    {0x0441, 1, &mqc_states[71], &mqc_states[65]},
+    {0x02a1, 0, &mqc_states[72], &mqc_states[66]},
+    {0x02a1, 1, &mqc_states[73], &mqc_states[67]},
+    {0x0221, 0, &mqc_states[74], &mqc_states[68]},
+    {0x0221, 1, &mqc_states[75], &mqc_states[69]},
+    {0x0141, 0, &mqc_states[76], &mqc_states[70]},
+    {0x0141, 1, &mqc_states[77], &mqc_states[71]},
+    {0x0111, 0, &mqc_states[78], &mqc_states[72]},
+    {0x0111, 1, &mqc_states[79], &mqc_states[73]},
+    {0x0085, 0, &mqc_states[80], &mqc_states[74]},
+    {0x0085, 1, &mqc_states[81], &mqc_states[75]},
+    {0x0049, 0, &mqc_states[82], &mqc_states[76]},
+    {0x0049, 1, &mqc_states[83], &mqc_states[77]},
+    {0x0025, 0, &mqc_states[84], &mqc_states[78]},
+    {0x0025, 1, &mqc_states[85], &mqc_states[79]},
+    {0x0015, 0, &mqc_states[86], &mqc_states[80]},
+    {0x0015, 1, &mqc_states[87], &mqc_states[81]},
+    {0x0009, 0, &mqc_states[88], &mqc_states[82]},
+    {0x0009, 1, &mqc_states[89], &mqc_states[83]},
+    {0x0005, 0, &mqc_states[90], &mqc_states[84]},
+    {0x0005, 1, &mqc_states[91], &mqc_states[85]},
+    {0x0001, 0, &mqc_states[90], &mqc_states[86]},
+    {0x0001, 1, &mqc_states[91], &mqc_states[87]},
+    {0x5601, 0, &mqc_states[92], &mqc_states[92]},
+    {0x5601, 1, &mqc_states[93], &mqc_states[93]},
+};
+
+/*
+==========================================================
+   local functions
+==========================================================
+*/
+
+static void opj_mqc_setbits(opj_mqc_t *mqc)
+{
+    OPJ_UINT32 tempc = mqc->c + mqc->a;
+    mqc->c |= 0xffff;
+    if (mqc->c >= tempc) {
+        mqc->c -= 0x8000;
+    }
+}
+
+/*
+==========================================================
+   MQ-Coder interface
+==========================================================
+*/
+
+OPJ_UINT32 opj_mqc_numbytes(opj_mqc_t *mqc)
+{
+    const ptrdiff_t diff = mqc->bp - mqc->start;
+#if 0
+    assert(diff <= 0xffffffff && diff >= 0);   /* UINT32_MAX */
+#endif
+    return (OPJ_UINT32)diff;
+}
+
+void opj_mqc_init_enc(opj_mqc_t *mqc, OPJ_BYTE *bp)
+{
+    /* To avoid the curctx pointer to be dangling, but not strictly */
+    /* required as the current context is always set before encoding */
+    opj_mqc_setcurctx(mqc, 0);
+
+    /* As specified in Figure C.10 - Initialization of the encoder */
+    /* (C.2.8 Initialization of the encoder (INITENC)) */
+    mqc->a = 0x8000;
+    mqc->c = 0;
+    /* Yes, we point before the start of the buffer, but this is safe */
+    /* given opj_tcd_code_block_enc_allocate_data() */
+    mqc->bp = bp - 1;
+    mqc->ct = 12;
+    /* At this point we should test *(mqc->bp) against 0xFF, but this is not */
+    /* necessary, as this is only used at the beginning of the code block */
+    /* and our initial fake byte is set at 0 */
+    assert(*(mqc->bp) != 0xff);
+
+    mqc->start = bp;
+    mqc->end_of_byte_stream_counter = 0;
+}
+
+
+void opj_mqc_flush(opj_mqc_t *mqc)
+{
+    /* C.2.9 Termination of coding (FLUSH) */
+    /* Figure C.11 – FLUSH procedure */
+    opj_mqc_setbits(mqc);
+    mqc->c <<= mqc->ct;
+    opj_mqc_byteout(mqc);
+    mqc->c <<= mqc->ct;
+    opj_mqc_byteout(mqc);
+
+    /* It is forbidden that a coding pass ends with 0xff */
+    if (*mqc->bp != 0xff) {
+        /* Advance pointer so that opj_mqc_numbytes() returns a valid value */
+        mqc->bp++;
+    }
+}
+
+void opj_mqc_bypass_init_enc(opj_mqc_t *mqc)
+{
+    /* This function is normally called after at least one opj_mqc_flush() */
+    /* which will have advance mqc->bp by at least 2 bytes beyond its */
+    /* initial position */
+    assert(mqc->bp >= mqc->start);
+    mqc->c = 0;
+    /* in theory we should initialize to 8, but use this special value */
+    /* as a hint that opj_mqc_bypass_enc() has never been called, so */
+    /* as to avoid the 0xff 0x7f elimination trick in opj_mqc_bypass_flush_enc() */
+    /* to trigger when we don't have output any bit during this bypass sequence */
+    /* Any value > 8 will do */
+    mqc->ct = BYPASS_CT_INIT;
+    /* Given that we are called after opj_mqc_flush(), the previous byte */
+    /* cannot be 0xff. */
+    assert(mqc->bp[-1] != 0xff);
+}
+
+void opj_mqc_bypass_enc(opj_mqc_t *mqc, OPJ_UINT32 d)
+{
+    if (mqc->ct == BYPASS_CT_INIT) {
+        mqc->ct = 8;
+    }
+    mqc->ct--;
+    mqc->c = mqc->c + (d << mqc->ct);
+    if (mqc->ct == 0) {
+        *mqc->bp = (OPJ_BYTE)mqc->c;
+        mqc->ct = 8;
+        /* If the previous byte was 0xff, make sure that the next msb is 0 */
+        if (*mqc->bp == 0xff) {
+            mqc->ct = 7;
+        }
+        mqc->bp++;
+        mqc->c = 0;
+    }
+}
+
+OPJ_UINT32 opj_mqc_bypass_get_extra_bytes(opj_mqc_t *mqc, OPJ_BOOL erterm)
+{
+    return (mqc->ct < 7 ||
+            (mqc->ct == 7 && (erterm || mqc->bp[-1] != 0xff))) ? 1 : 0;
+}
+
+void opj_mqc_bypass_flush_enc(opj_mqc_t *mqc, OPJ_BOOL erterm)
+{
+    /* Is there any bit remaining to be flushed ? */
+    /* If the last output byte is 0xff, we can discard it, unless */
+    /* erterm is required (I'm not completely sure why in erterm */
+    /* we must output 0xff 0x2a if the last byte was 0xff instead of */
+    /* discarding it, but Kakadu requires it when decoding */
+    /* in -fussy mode) */
+    if (mqc->ct < 7 || (mqc->ct == 7 && (erterm || mqc->bp[-1] != 0xff))) {
+        OPJ_BYTE bit_value = 0;
+        /* If so, fill the remaining lsbs with an alternating sequence of */
+        /* 0,1,... */
+        /* Note: it seems the standard only requires that for a ERTERM flush */
+        /* and doesn't specify what to do for a regular BYPASS flush */
+        while (mqc->ct > 0) {
+            mqc->ct--;
+            mqc->c += (OPJ_UINT32)(bit_value << mqc->ct);
+            bit_value = (OPJ_BYTE)(1U - bit_value);
+        }
+        *mqc->bp = (OPJ_BYTE)mqc->c;
+        /* Advance pointer so that opj_mqc_numbytes() returns a valid value */
+        mqc->bp++;
+    } else if (mqc->ct == 7 && mqc->bp[-1] == 0xff) {
+        /* Discard last 0xff */
+        assert(!erterm);
+        mqc->bp --;
+    } else if (mqc->ct == 8 && !erterm &&
+               mqc->bp[-1] == 0x7f && mqc->bp[-2] == 0xff) {
+        /* Tiny optimization: discard terminating 0xff 0x7f since it is */
+        /* interpreted as 0xff 0x7f [0xff 0xff] by the decoder, and given */
+        /* the bit stuffing, in fact as 0xff 0xff [0xff ..] */
+        /* Happens once on opj_compress -i ../MAPA.tif -o MAPA.j2k  -M 1 */
+        mqc->bp -= 2;
+    }
+
+    assert(mqc->bp[-1] != 0xff);
+}
+
+void opj_mqc_reset_enc(opj_mqc_t *mqc)
+{
+    opj_mqc_resetstates(mqc);
+    opj_mqc_setstate(mqc, T1_CTXNO_UNI, 0, 46);
+    opj_mqc_setstate(mqc, T1_CTXNO_AGG, 0, 3);
+    opj_mqc_setstate(mqc, T1_CTXNO_ZC, 0, 4);
+}
+
+#ifdef notdef
+OPJ_UINT32 opj_mqc_restart_enc(opj_mqc_t *mqc)
+{
+    OPJ_UINT32 correction = 1;
+
+    /* <flush part> */
+    OPJ_INT32 n = (OPJ_INT32)(27 - 15 - mqc->ct);
+    mqc->c <<= mqc->ct;
+    while (n > 0) {
+        opj_mqc_byteout(mqc);
+        n -= (OPJ_INT32)mqc->ct;
+        mqc->c <<= mqc->ct;
+    }
+    opj_mqc_byteout(mqc);
+
+    return correction;
+}
+#endif
+
+void opj_mqc_restart_init_enc(opj_mqc_t *mqc)
+{
+    /* <Re-init part> */
+
+    /* As specified in Figure C.10 - Initialization of the encoder */
+    /* (C.2.8 Initialization of the encoder (INITENC)) */
+    mqc->a = 0x8000;
+    mqc->c = 0;
+    mqc->ct = 12;
+    /* This function is normally called after at least one opj_mqc_flush() */
+    /* which will have advance mqc->bp by at least 2 bytes beyond its */
+    /* initial position */
+    mqc->bp --;
+    assert(mqc->bp >= mqc->start - 1);
+    assert(*mqc->bp != 0xff);
+    if (*mqc->bp == 0xff) {
+        mqc->ct = 13;
+    }
+}
+
+void opj_mqc_erterm_enc(opj_mqc_t *mqc)
+{
+    OPJ_INT32 k = (OPJ_INT32)(11 - mqc->ct + 1);
+
+    while (k > 0) {
+        mqc->c <<= mqc->ct;
+        mqc->ct = 0;
+        opj_mqc_byteout(mqc);
+        k -= (OPJ_INT32)mqc->ct;
+    }
+
+    if (*mqc->bp != 0xff) {
+        opj_mqc_byteout(mqc);
+    }
+}
+
+/**
+Encode the most probable symbol
+@param mqc MQC handle
+*/
+static INLINE void opj_mqc_codemps(opj_mqc_t *mqc)
+{
+    opj_mqc_codemps_macro(mqc, mqc->curctx, mqc->a, mqc->c, mqc->ct);
+}
+
+/**
+Encode the most least symbol
+@param mqc MQC handle
+*/
+static INLINE void opj_mqc_codelps(opj_mqc_t *mqc)
+{
+    opj_mqc_codelps_macro(mqc, mqc->curctx, mqc->a, mqc->c, mqc->ct);
+}
+
+/**
+Encode a symbol using the MQ-coder
+@param mqc MQC handle
+@param d The symbol to be encoded (0 or 1)
+*/
+static INLINE void opj_mqc_encode(opj_mqc_t *mqc, OPJ_UINT32 d)
+{
+    if ((*mqc->curctx)->mps == d) {
+        opj_mqc_codemps(mqc);
+    } else {
+        opj_mqc_codelps(mqc);
+    }
+}
+
+void opj_mqc_segmark_enc(opj_mqc_t *mqc)
+{
+    OPJ_UINT32 i;
+    opj_mqc_setcurctx(mqc, 18);
+
+    for (i = 1; i < 5; i++) {
+        opj_mqc_encode(mqc, i % 2);
+    }
+}
+
+static void opj_mqc_init_dec_common(opj_mqc_t *mqc,
+                                    OPJ_BYTE *bp,
+                                    OPJ_UINT32 len,
+                                    OPJ_UINT32 extra_writable_bytes)
+{
+    (void)extra_writable_bytes;
+
+    assert(extra_writable_bytes >= OPJ_COMMON_CBLK_DATA_EXTRA);
+    mqc->start = bp;
+    mqc->end = bp + len;
+    /* Insert an artificial 0xFF 0xFF marker at end of the code block */
+    /* data so that the bytein routines stop on it. This saves us comparing */
+    /* the bp and end pointers */
+    /* But before inserting it, backup th bytes we will overwrite */
+    memcpy(mqc->backup, mqc->end, OPJ_COMMON_CBLK_DATA_EXTRA);
+    mqc->end[0] = 0xFF;
+    mqc->end[1] = 0xFF;
+    mqc->bp = bp;
+}
+void opj_mqc_init_dec(opj_mqc_t *mqc, OPJ_BYTE *bp, OPJ_UINT32 len,
+                      OPJ_UINT32 extra_writable_bytes)
+{
+    /* Implements ISO 15444-1 C.3.5 Initialization of the decoder (INITDEC) */
+    /* Note: alternate "J.1 - Initialization of the software-conventions */
+    /* decoder" has been tried, but does */
+    /* not bring any improvement. */
+    /* See https://github.com/uclouvain/openjpeg/issues/921 */
+    opj_mqc_init_dec_common(mqc, bp, len, extra_writable_bytes);
+    opj_mqc_setcurctx(mqc, 0);
+    mqc->end_of_byte_stream_counter = 0;
+    if (len == 0) {
+        mqc->c = 0xff << 16;
+    } else {
+        mqc->c = (OPJ_UINT32)(*mqc->bp << 16);
+    }
+
+    opj_mqc_bytein(mqc);
+    mqc->c <<= 7;
+    mqc->ct -= 7;
+    mqc->a = 0x8000;
+}
+
+
+void opj_mqc_raw_init_dec(opj_mqc_t *mqc, OPJ_BYTE *bp, OPJ_UINT32 len,
+                          OPJ_UINT32 extra_writable_bytes)
+{
+    opj_mqc_init_dec_common(mqc, bp, len, extra_writable_bytes);
+    mqc->c = 0;
+    mqc->ct = 0;
+}
+
+
+void opq_mqc_finish_dec(opj_mqc_t *mqc)
+{
+    /* Restore the bytes overwritten by opj_mqc_init_dec_common() */
+    memcpy(mqc->end, mqc->backup, OPJ_COMMON_CBLK_DATA_EXTRA);
+}
+
+void opj_mqc_resetstates(opj_mqc_t *mqc)
+{
+    OPJ_UINT32 i;
+    for (i = 0; i < MQC_NUMCTXS; i++) {
+        mqc->ctxs[i] = mqc_states;
+    }
+}
+
+void opj_mqc_setstate(opj_mqc_t *mqc, OPJ_UINT32 ctxno, OPJ_UINT32 msb,
+                      OPJ_INT32 prob)
+{
+    mqc->ctxs[ctxno] = &mqc_states[msb + (OPJ_UINT32)(prob << 1)];
+}
+
+void opj_mqc_byteout(opj_mqc_t *mqc)
+{
+    /* bp is initialized to start - 1 in opj_mqc_init_enc() */
+    /* but this is safe, see opj_tcd_code_block_enc_allocate_data() */
+    assert(mqc->bp >= mqc->start - 1);
+    if (*mqc->bp == 0xff) {
+        mqc->bp++;
+        *mqc->bp = (OPJ_BYTE)(mqc->c >> 20);
+        mqc->c &= 0xfffff;
+        mqc->ct = 7;
+    } else {
+        if ((mqc->c & 0x8000000) == 0) {
+            mqc->bp++;
+            *mqc->bp = (OPJ_BYTE)(mqc->c >> 19);
+            mqc->c &= 0x7ffff;
+            mqc->ct = 8;
+        } else {
+            (*mqc->bp)++;
+            if (*mqc->bp == 0xff) {
+                mqc->c &= 0x7ffffff;
+                mqc->bp++;
+                *mqc->bp = (OPJ_BYTE)(mqc->c >> 20);
+                mqc->c &= 0xfffff;
+                mqc->ct = 7;
+            } else {
+                mqc->bp++;
+                *mqc->bp = (OPJ_BYTE)(mqc->c >> 19);
+                mqc->c &= 0x7ffff;
+                mqc->ct = 8;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/third_party/libopenjpeg/mqc.h b/third_party/libopenjpeg/mqc.h
new file mode 100644
index 0000000..9850fed
--- /dev/null
+++ b/third_party/libopenjpeg/mqc.h
@@ -0,0 +1,268 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2008, Jerome Fimes, Communications & Systemes <jerome.fimes@c-s.fr>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef OPJ_MQC_H
+#define OPJ_MQC_H
+
+#include "opj_common.h"
+
+/**
+@file mqc.h
+@brief Implementation of an MQ-Coder (MQC)
+
+The functions in MQC.C have for goal to realize the MQ-coder operations. The functions
+in MQC.C are used by some function in T1.C.
+*/
+
+/** @defgroup MQC MQC - Implementation of an MQ-Coder */
+/*@{*/
+
+/**
+This struct defines the state of a context.
+*/
+typedef struct opj_mqc_state {
+    /** the probability of the Least Probable Symbol (0.75->0x8000, 1.5->0xffff) */
+    OPJ_UINT32 qeval;
+    /** the Most Probable Symbol (0 or 1) */
+    OPJ_UINT32 mps;
+    /** next state if the next encoded symbol is the MPS */
+    const struct opj_mqc_state *nmps;
+    /** next state if the next encoded symbol is the LPS */
+    const struct opj_mqc_state *nlps;
+} opj_mqc_state_t;
+
+#define MQC_NUMCTXS 19
+
+/**
+MQ coder
+*/
+typedef struct opj_mqc {
+    /** temporary buffer where bits are coded or decoded */
+    OPJ_UINT32 c;
+    /** only used by MQ decoder */
+    OPJ_UINT32 a;
+    /** number of bits already read or free to write */
+    OPJ_UINT32 ct;
+    /* only used by decoder, to count the number of times a terminating 0xFF >0x8F marker is read */
+    OPJ_UINT32 end_of_byte_stream_counter;
+    /** pointer to the current position in the buffer */
+    OPJ_BYTE *bp;
+    /** pointer to the start of the buffer */
+    OPJ_BYTE *start;
+    /** pointer to the end of the buffer */
+    OPJ_BYTE *end;
+    /** Array of contexts */
+    const opj_mqc_state_t *ctxs[MQC_NUMCTXS];
+    /** Active context */
+    const opj_mqc_state_t **curctx;
+    /* lut_ctxno_zc shifted by (1 << 9) * bandno */
+    const OPJ_BYTE* lut_ctxno_zc_orient;
+    /** Original value of the 2 bytes at end[0] and end[1] */
+    OPJ_BYTE backup[OPJ_COMMON_CBLK_DATA_EXTRA];
+} opj_mqc_t;
+
+#define BYPASS_CT_INIT  0xDEADBEEF
+
+#include "mqc_inl.h"
+
+/** @name Exported functions */
+/*@{*/
+/* ----------------------------------------------------------------------- */
+
+/**
+Return the number of bytes written/read since initialisation
+@param mqc MQC handle
+@return Returns the number of bytes already encoded
+*/
+OPJ_UINT32 opj_mqc_numbytes(opj_mqc_t *mqc);
+/**
+Reset the states of all the context of the coder/decoder
+(each context is set to a state where 0 and 1 are more or less equiprobable)
+@param mqc MQC handle
+*/
+void opj_mqc_resetstates(opj_mqc_t *mqc);
+/**
+Set the state of a particular context
+@param mqc MQC handle
+@param ctxno Number that identifies the context
+@param msb The MSB of the new state of the context
+@param prob Number that identifies the probability of the symbols for the new state of the context
+*/
+void opj_mqc_setstate(opj_mqc_t *mqc, OPJ_UINT32 ctxno, OPJ_UINT32 msb,
+                      OPJ_INT32 prob);
+/**
+Initialize the encoder
+@param mqc MQC handle
+@param bp Pointer to the start of the buffer where the bytes will be written
+*/
+void opj_mqc_init_enc(opj_mqc_t *mqc, OPJ_BYTE *bp);
+/**
+Set the current context used for coding/decoding
+@param mqc MQC handle
+@param ctxno Number that identifies the context
+*/
+#define opj_mqc_setcurctx(mqc, ctxno)   (mqc)->curctx = &(mqc)->ctxs[(OPJ_UINT32)(ctxno)]
+
+/**
+Flush the encoder, so that all remaining data is written
+@param mqc MQC handle
+*/
+void opj_mqc_flush(opj_mqc_t *mqc);
+/**
+BYPASS mode switch, initialization operation.
+JPEG 2000 p 505.
+@param mqc MQC handle
+*/
+void opj_mqc_bypass_init_enc(opj_mqc_t *mqc);
+
+/** Return number of extra bytes to add to opj_mqc_numbytes() for the²
+    size of a non-terminating BYPASS pass
+@param mqc MQC handle
+@param erterm 1 if ERTERM is enabled, 0 otherwise
+*/
+OPJ_UINT32 opj_mqc_bypass_get_extra_bytes(opj_mqc_t *mqc, OPJ_BOOL erterm);
+
+/**
+BYPASS mode switch, coding operation.
+JPEG 2000 p 505.
+@param mqc MQC handle
+@param d The symbol to be encoded (0 or 1)
+*/
+void opj_mqc_bypass_enc(opj_mqc_t *mqc, OPJ_UINT32 d);
+/**
+BYPASS mode switch, flush operation
+@param mqc MQC handle
+@param erterm 1 if ERTERM is enabled, 0 otherwise
+*/
+void opj_mqc_bypass_flush_enc(opj_mqc_t *mqc, OPJ_BOOL erterm);
+/**
+RESET mode switch
+@param mqc MQC handle
+*/
+void opj_mqc_reset_enc(opj_mqc_t *mqc);
+
+#ifdef notdef
+/**
+RESTART mode switch (TERMALL)
+@param mqc MQC handle
+@return Returns 1 (always)
+*/
+OPJ_UINT32 opj_mqc_restart_enc(opj_mqc_t *mqc);
+#endif
+
+/**
+RESTART mode switch (TERMALL) reinitialisation
+@param mqc MQC handle
+*/
+void opj_mqc_restart_init_enc(opj_mqc_t *mqc);
+/**
+ERTERM mode switch (PTERM)
+@param mqc MQC handle
+*/
+void opj_mqc_erterm_enc(opj_mqc_t *mqc);
+/**
+SEGMARK mode switch (SEGSYM)
+@param mqc MQC handle
+*/
+void opj_mqc_segmark_enc(opj_mqc_t *mqc);
+
+/**
+Initialize the decoder for MQ decoding.
+
+opj_mqc_finish_dec() must be absolutely called after finishing the decoding
+passes, so as to restore the bytes temporarily overwritten.
+
+@param mqc MQC handle
+@param bp Pointer to the start of the buffer from which the bytes will be read
+          Note that OPJ_COMMON_CBLK_DATA_EXTRA bytes at the end of the buffer
+          will be temporarily overwritten with an artificial 0xFF 0xFF marker.
+          (they will be backuped in the mqc structure to be restored later)
+          So bp must be at least len + OPJ_COMMON_CBLK_DATA_EXTRA large, and
+          writable.
+@param len Length of the input buffer
+@param extra_writable_bytes Indicate how many bytes after len are writable.
+                            This is to indicate your consent that bp must be
+                            large enough.
+*/
+void opj_mqc_init_dec(opj_mqc_t *mqc, OPJ_BYTE *bp, OPJ_UINT32 len,
+                      OPJ_UINT32 extra_writable_bytes);
+
+/**
+Initialize the decoder for RAW decoding.
+
+opj_mqc_finish_dec() must be absolutely called after finishing the decoding
+passes, so as to restore the bytes temporarily overwritten.
+
+@param mqc MQC handle
+@param bp Pointer to the start of the buffer from which the bytes will be read
+          Note that OPJ_COMMON_CBLK_DATA_EXTRA bytes at the end of the buffer
+          will be temporarily overwritten with an artificial 0xFF 0xFF marker.
+          (they will be backuped in the mqc structure to be restored later)
+          So bp must be at least len + OPJ_COMMON_CBLK_DATA_EXTRA large, and
+          writable.
+@param len Length of the input buffer
+@param extra_writable_bytes Indicate how many bytes after len are writable.
+                            This is to indicate your consent that bp must be
+                            large enough.
+*/
+void opj_mqc_raw_init_dec(opj_mqc_t *mqc, OPJ_BYTE *bp, OPJ_UINT32 len,
+                          OPJ_UINT32 extra_writable_bytes);
+
+
+/**
+Terminate RAW/MQC decoding
+
+This restores the bytes temporarily overwritten by opj_mqc_init_dec()/
+opj_mqc_raw_init_dec()
+
+@param mqc MQC handle
+*/
+void opq_mqc_finish_dec(opj_mqc_t *mqc);
+
+/**
+Decode a symbol
+@param mqc MQC handle
+@return Returns the decoded symbol (0 or 1)
+*/
+/*static INLINE OPJ_UINT32 opj_mqc_decode(opj_mqc_t * const mqc);*/
+/* ----------------------------------------------------------------------- */
+/*@}*/
+
+/*@}*/
+
+#endif /* OPJ_MQC_H */
diff --git a/third_party/libopenjpeg/mqc_inl.h b/third_party/libopenjpeg/mqc_inl.h
new file mode 100644
index 0000000..0031b94
--- /dev/null
+++ b/third_party/libopenjpeg/mqc_inl.h
@@ -0,0 +1,282 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2008, Jerome Fimes, Communications & Systemes <jerome.fimes@c-s.fr>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef OPJ_MQC_INL_H
+#define OPJ_MQC_INL_H
+
+/* For internal use of opj_mqc_decode_macro() */
+#define opj_mqc_mpsexchange_macro(d, curctx, a) \
+{ \
+    if (a < (*curctx)->qeval) { \
+        d = !((*curctx)->mps); \
+        *curctx = (*curctx)->nlps; \
+    } else { \
+        d = (*curctx)->mps; \
+        *curctx = (*curctx)->nmps; \
+    } \
+}
+
+/* For internal use of opj_mqc_decode_macro() */
+#define opj_mqc_lpsexchange_macro(d, curctx, a) \
+{ \
+    if (a < (*curctx)->qeval) { \
+        a = (*curctx)->qeval; \
+        d = (*curctx)->mps; \
+        *curctx = (*curctx)->nmps; \
+    } else { \
+        a = (*curctx)->qeval; \
+        d = !((*curctx)->mps); \
+        *curctx = (*curctx)->nlps; \
+    } \
+}
+
+
+/**
+Decode a symbol using raw-decoder. Cfr p.506 TAUBMAN
+@param mqc MQC handle
+@return Returns the decoded symbol (0 or 1)
+*/
+static INLINE OPJ_UINT32 opj_mqc_raw_decode(opj_mqc_t *mqc)
+{
+    OPJ_UINT32 d;
+    if (mqc->ct == 0) {
+        /* Given opj_mqc_raw_init_dec() we know that at some point we will */
+        /* have a 0xFF 0xFF artificial marker */
+        if (mqc->c == 0xff) {
+            if (*mqc->bp  > 0x8f) {
+                mqc->c = 0xff;
+                mqc->ct = 8;
+            } else {
+                mqc->c = *mqc->bp;
+                mqc->bp ++;
+                mqc->ct = 7;
+            }
+        } else {
+            mqc->c = *mqc->bp;
+            mqc->bp ++;
+            mqc->ct = 8;
+        }
+    }
+    mqc->ct--;
+    d = ((OPJ_UINT32)mqc->c >> mqc->ct) & 0x01U;
+
+    return d;
+}
+
+
+#define opj_mqc_bytein_macro(mqc, c, ct) \
+{ \
+        OPJ_UINT32 l_c;  \
+        /* Given opj_mqc_init_dec() we know that at some point we will */ \
+        /* have a 0xFF 0xFF artificial marker */ \
+        l_c = *(mqc->bp + 1); \
+        if (*mqc->bp == 0xff) { \
+            if (l_c > 0x8f) { \
+                c += 0xff00; \
+                ct = 8; \
+                mqc->end_of_byte_stream_counter ++; \
+            } else { \
+                mqc->bp++; \
+                c += l_c << 9; \
+                ct = 7; \
+            } \
+        } else { \
+            mqc->bp++; \
+            c += l_c << 8; \
+            ct = 8; \
+        } \
+}
+
+/* For internal use of opj_mqc_decode_macro() */
+#define opj_mqc_renormd_macro(mqc, a, c, ct) \
+{ \
+    do { \
+        if (ct == 0) { \
+            opj_mqc_bytein_macro(mqc, c, ct); \
+        } \
+        a <<= 1; \
+        c <<= 1; \
+        ct--; \
+    } while (a < 0x8000); \
+}
+
+#define opj_mqc_decode_macro(d, mqc, curctx, a, c, ct) \
+{ \
+    /* Implements ISO 15444-1 C.3.2 Decoding a decision (DECODE) */ \
+    /* Note: alternate "J.2 - Decoding an MPS or an LPS in the */ \
+    /* software-conventions decoder" has been tried, but does not bring any */ \
+    /* improvement. See https://github.com/uclouvain/openjpeg/issues/921 */ \
+    a -= (*curctx)->qeval;  \
+    if ((c >> 16) < (*curctx)->qeval) {  \
+        opj_mqc_lpsexchange_macro(d, curctx, a);  \
+        opj_mqc_renormd_macro(mqc, a, c, ct);  \
+    } else {  \
+        c -= (*curctx)->qeval << 16;  \
+        if ((a & 0x8000) == 0) { \
+            opj_mqc_mpsexchange_macro(d, curctx, a); \
+            opj_mqc_renormd_macro(mqc, a, c, ct); \
+        } else { \
+            d = (*curctx)->mps; \
+        } \
+    } \
+}
+
+#define DOWNLOAD_MQC_VARIABLES(mqc, curctx, a, c, ct) \
+        register const opj_mqc_state_t **curctx = mqc->curctx; \
+        register OPJ_UINT32 c = mqc->c; \
+        register OPJ_UINT32 a = mqc->a; \
+        register OPJ_UINT32 ct = mqc->ct
+
+#define UPLOAD_MQC_VARIABLES(mqc, curctx, a, c, ct) \
+        mqc->curctx = curctx; \
+        mqc->c = c; \
+        mqc->a = a; \
+        mqc->ct = ct;
+
+/**
+Input a byte
+@param mqc MQC handle
+*/
+static INLINE void opj_mqc_bytein(opj_mqc_t *const mqc)
+{
+    opj_mqc_bytein_macro(mqc, mqc->c, mqc->ct);
+}
+
+/**
+Renormalize mqc->a and mqc->c while decoding
+@param mqc MQC handle
+*/
+#define opj_mqc_renormd(mqc) \
+    opj_mqc_renormd_macro(mqc, mqc->a, mqc->c, mqc->ct)
+
+/**
+Decode a symbol
+@param d OPJ_UINT32 value where to store the decoded symbol
+@param mqc MQC handle
+@return Returns the decoded symbol (0 or 1) in d
+*/
+#define opj_mqc_decode(d, mqc) \
+    opj_mqc_decode_macro(d, mqc, mqc->curctx, mqc->a, mqc->c, mqc->ct)
+
+/**
+Output a byte, doing bit-stuffing if necessary.
+After a 0xff byte, the next byte must be smaller than 0x90.
+@param mqc MQC handle
+*/
+void opj_mqc_byteout(opj_mqc_t *mqc);
+
+/**
+Renormalize mqc->a and mqc->c while encoding, so that mqc->a stays between 0x8000 and 0x10000
+@param mqc MQC handle
+@param a_ value of mqc->a
+@param c_ value of mqc->c_
+@param ct_ value of mqc->ct_
+*/
+#define opj_mqc_renorme_macro(mqc, a_, c_, ct_) \
+{ \
+    do { \
+        a_ <<= 1; \
+        c_ <<= 1; \
+        ct_--; \
+        if (ct_ == 0) { \
+            mqc->c = c_; \
+            opj_mqc_byteout(mqc); \
+            c_ = mqc->c; \
+            ct_ = mqc->ct; \
+        } \
+    } while( (a_ & 0x8000) == 0); \
+}
+
+#define opj_mqc_codemps_macro(mqc, curctx, a, c, ct) \
+{ \
+    a -= (*curctx)->qeval; \
+    if ((a & 0x8000) == 0) { \
+        if (a < (*curctx)->qeval) { \
+            a = (*curctx)->qeval; \
+        } else { \
+            c += (*curctx)->qeval; \
+        } \
+        *curctx = (*curctx)->nmps; \
+        opj_mqc_renorme_macro(mqc, a, c, ct); \
+    } else { \
+        c += (*curctx)->qeval; \
+    } \
+}
+
+#define opj_mqc_codelps_macro(mqc, curctx, a, c, ct) \
+{ \
+    a -= (*curctx)->qeval; \
+    if (a < (*curctx)->qeval) { \
+        c += (*curctx)->qeval; \
+    } else { \
+        a = (*curctx)->qeval; \
+    } \
+    *curctx = (*curctx)->nlps; \
+    opj_mqc_renorme_macro(mqc, a, c, ct); \
+}
+
+#define opj_mqc_encode_macro(mqc, curctx, a, c, ct, d) \
+{ \
+    if ((*curctx)->mps == (d)) { \
+        opj_mqc_codemps_macro(mqc, curctx, a, c, ct); \
+    } else { \
+        opj_mqc_codelps_macro(mqc, curctx, a, c, ct); \
+    } \
+}
+
+
+#define opj_mqc_bypass_enc_macro(mqc, c, ct, d) \
+{\
+    if (ct == BYPASS_CT_INIT) {\
+        ct = 8;\
+    }\
+    ct--;\
+    c = c + ((d) << ct);\
+    if (ct == 0) {\
+        *mqc->bp = (OPJ_BYTE)c;\
+        ct = 8;\
+        /* If the previous byte was 0xff, make sure that the next msb is 0 */ \
+        if (*mqc->bp == 0xff) {\
+            ct = 7;\
+        }\
+        mqc->bp++;\
+        c = 0;\
+    }\
+}
+
+#endif /* OPJ_MQC_INL_H */
diff --git a/third_party/libopenjpeg/openjpeg.c b/third_party/libopenjpeg/openjpeg.c
new file mode 100644
index 0000000..9dd4256
--- /dev/null
+++ b/third_party/libopenjpeg/openjpeg.c
@@ -0,0 +1,1032 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
+ * Copyright (c) 2012, CS Systemes d'Information, France
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef _WIN32
+#include <windows.h>
+#endif /* _WIN32 */
+
+#include "opj_includes.h"
+
+
+/* ---------------------------------------------------------------------- */
+/* Functions to set the message handlers */
+
+OPJ_BOOL OPJ_CALLCONV opj_set_info_handler(opj_codec_t * p_codec,
+        opj_msg_callback p_callback,
+        void * p_user_data)
+{
+    opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+    if (! l_codec) {
+        return OPJ_FALSE;
+    }
+
+    l_codec->m_event_mgr.info_handler = p_callback;
+    l_codec->m_event_mgr.m_info_data = p_user_data;
+
+    return OPJ_TRUE;
+}
+
+OPJ_BOOL OPJ_CALLCONV opj_set_warning_handler(opj_codec_t * p_codec,
+        opj_msg_callback p_callback,
+        void * p_user_data)
+{
+    opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+    if (! l_codec) {
+        return OPJ_FALSE;
+    }
+
+    l_codec->m_event_mgr.warning_handler = p_callback;
+    l_codec->m_event_mgr.m_warning_data = p_user_data;
+
+    return OPJ_TRUE;
+}
+
+OPJ_BOOL OPJ_CALLCONV opj_set_error_handler(opj_codec_t * p_codec,
+        opj_msg_callback p_callback,
+        void * p_user_data)
+{
+    opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+    if (! l_codec) {
+        return OPJ_FALSE;
+    }
+
+    l_codec->m_event_mgr.error_handler = p_callback;
+    l_codec->m_event_mgr.m_error_data = p_user_data;
+
+    return OPJ_TRUE;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static OPJ_SIZE_T opj_read_from_file(void * p_buffer, OPJ_SIZE_T p_nb_bytes,
+                                     void * p_user_data)
+{
+    FILE* p_file = (FILE*)p_user_data;
+    OPJ_SIZE_T l_nb_read = fread(p_buffer, 1, p_nb_bytes, (FILE*)p_file);
+    return l_nb_read ? l_nb_read : (OPJ_SIZE_T) - 1;
+}
+
+static OPJ_UINT64 opj_get_data_length_from_file(void * p_user_data)
+{
+    FILE* p_file = (FILE*)p_user_data;
+    OPJ_OFF_T file_length = 0;
+
+    OPJ_FSEEK(p_file, 0, SEEK_END);
+    file_length = (OPJ_OFF_T)OPJ_FTELL(p_file);
+    OPJ_FSEEK(p_file, 0, SEEK_SET);
+
+    return (OPJ_UINT64)file_length;
+}
+
+static OPJ_SIZE_T opj_write_from_file(void * p_buffer, OPJ_SIZE_T p_nb_bytes,
+                                      void * p_user_data)
+{
+    FILE* p_file = (FILE*)p_user_data;
+    return fwrite(p_buffer, 1, p_nb_bytes, p_file);
+}
+
+static OPJ_OFF_T opj_skip_from_file(OPJ_OFF_T p_nb_bytes, void * p_user_data)
+{
+    FILE* p_file = (FILE*)p_user_data;
+    if (OPJ_FSEEK(p_file, p_nb_bytes, SEEK_CUR)) {
+        return -1;
+    }
+
+    return p_nb_bytes;
+}
+
+static OPJ_BOOL opj_seek_from_file(OPJ_OFF_T p_nb_bytes, void * p_user_data)
+{
+    FILE* p_file = (FILE*)p_user_data;
+    if (OPJ_FSEEK(p_file, p_nb_bytes, SEEK_SET)) {
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+static void opj_close_from_file(void* p_user_data)
+{
+    FILE* p_file = (FILE*)p_user_data;
+    fclose(p_file);
+}
+
+/* ---------------------------------------------------------------------- */
+#ifdef _WIN32
+#ifndef OPJ_STATIC
+BOOL APIENTRY
+DllMain(HINSTANCE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
+{
+
+    OPJ_ARG_NOT_USED(lpReserved);
+    OPJ_ARG_NOT_USED(hModule);
+
+    switch (ul_reason_for_call) {
+    case DLL_PROCESS_ATTACH :
+        break;
+    case DLL_PROCESS_DETACH :
+        break;
+    case DLL_THREAD_ATTACH :
+    case DLL_THREAD_DETACH :
+        break;
+    }
+
+    return TRUE;
+}
+#endif /* OPJ_STATIC */
+#endif /* _WIN32 */
+
+/* ---------------------------------------------------------------------- */
+
+const char* OPJ_CALLCONV opj_version(void)
+{
+    return OPJ_PACKAGE_VERSION;
+}
+
+/* ---------------------------------------------------------------------- */
+/* DECOMPRESSION FUNCTIONS*/
+
+opj_codec_t* OPJ_CALLCONV opj_create_decompress(OPJ_CODEC_FORMAT p_format)
+{
+    opj_codec_private_t *l_codec = 00;
+
+    l_codec = (opj_codec_private_t*) opj_calloc(1, sizeof(opj_codec_private_t));
+    if (!l_codec) {
+        return 00;
+    }
+
+    l_codec->is_decompressor = 1;
+
+    switch (p_format) {
+    case OPJ_CODEC_J2K:
+        l_codec->opj_dump_codec = j2k_dump;
+
+        l_codec->opj_get_codec_info = j2k_get_cstr_info;
+
+        l_codec->opj_get_codec_index = j2k_get_cstr_index;
+
+        l_codec->m_codec_data.m_decompression.opj_decode = opj_j2k_decode;
+
+        l_codec->m_codec_data.m_decompression.opj_end_decompress =
+            opj_j2k_end_decompress;
+
+        l_codec->m_codec_data.m_decompression.opj_read_header =
+            opj_j2k_read_header;
+
+        l_codec->m_codec_data.m_decompression.opj_destroy = opj_j2k_destroy;
+
+        l_codec->m_codec_data.m_decompression.opj_setup_decoder =
+            opj_j2k_setup_decoder;
+
+        l_codec->m_codec_data.m_decompression.opj_decoder_set_strict_mode =
+            opj_j2k_decoder_set_strict_mode;
+
+
+        l_codec->m_codec_data.m_decompression.opj_read_tile_header =
+            opj_j2k_read_tile_header;
+
+        l_codec->m_codec_data.m_decompression.opj_decode_tile_data =
+            opj_j2k_decode_tile;
+
+        l_codec->m_codec_data.m_decompression.opj_set_decode_area =
+            opj_j2k_set_decode_area;
+
+        l_codec->m_codec_data.m_decompression.opj_get_decoded_tile =
+            opj_j2k_get_tile;
+
+        l_codec->m_codec_data.m_decompression.opj_set_decoded_resolution_factor =
+            opj_j2k_set_decoded_resolution_factor;
+
+        l_codec->m_codec_data.m_decompression.opj_set_decoded_components =
+            opj_j2k_set_decoded_components;
+
+        l_codec->opj_set_threads = opj_j2k_set_threads;
+
+        l_codec->m_codec = opj_j2k_create_decompress();
+
+        if (! l_codec->m_codec) {
+            opj_free(l_codec);
+            return NULL;
+        }
+
+        break;
+
+    case OPJ_CODEC_JP2:
+        /* get a JP2 decoder handle */
+        l_codec->opj_dump_codec = jp2_dump;
+
+        l_codec->opj_get_codec_info = jp2_get_cstr_info;
+
+        l_codec->opj_get_codec_index = jp2_get_cstr_index;
+
+        l_codec->m_codec_data.m_decompression.opj_decode = opj_jp2_decode;
+
+        l_codec->m_codec_data.m_decompression.opj_end_decompress =
+            opj_jp2_end_decompress;
+
+        l_codec->m_codec_data.m_decompression.opj_read_header =
+            opj_jp2_read_header;
+
+        l_codec->m_codec_data.m_decompression.opj_read_tile_header =
+            opj_jp2_read_tile_header;
+
+        l_codec->m_codec_data.m_decompression.opj_decode_tile_data =
+            opj_jp2_decode_tile;
+
+        l_codec->m_codec_data.m_decompression.opj_destroy = opj_jp2_destroy;
+
+        l_codec->m_codec_data.m_decompression.opj_setup_decoder =
+             opj_jp2_setup_decoder;
+
+        l_codec->m_codec_data.m_decompression.opj_decoder_set_strict_mode =
+            opj_jp2_decoder_set_strict_mode;
+
+        l_codec->m_codec_data.m_decompression.opj_set_decode_area =
+            opj_jp2_set_decode_area;
+
+        l_codec->m_codec_data.m_decompression.opj_get_decoded_tile =
+            opj_jp2_get_tile;
+
+        l_codec->m_codec_data.m_decompression.opj_set_decoded_resolution_factor =
+            opj_jp2_set_decoded_resolution_factor;
+
+        l_codec->m_codec_data.m_decompression.opj_set_decoded_components =
+            opj_jp2_set_decoded_components;
+
+        l_codec->opj_set_threads = opj_jp2_set_threads;
+
+        l_codec->m_codec = opj_jp2_create(OPJ_TRUE);
+
+        if (! l_codec->m_codec) {
+            opj_free(l_codec);
+            return 00;
+        }
+
+        break;
+    case OPJ_CODEC_UNKNOWN:
+    case OPJ_CODEC_JPT:
+    default:
+        opj_free(l_codec);
+        return 00;
+    }
+
+    opj_set_default_event_handler(&(l_codec->m_event_mgr));
+    return (opj_codec_t*) l_codec;
+}
+
+void OPJ_CALLCONV opj_set_default_decoder_parameters(opj_dparameters_t
+        *parameters)
+{
+    if (parameters) {
+        memset(parameters, 0, sizeof(opj_dparameters_t));
+        /* default decoding parameters */
+        parameters->cp_layer = 0;
+        parameters->cp_reduce = 0;
+
+        parameters->decod_format = -1;
+        parameters->cod_format = -1;
+        parameters->flags = 0;
+        /* UniPG>> */
+#ifdef USE_JPWL
+        parameters->jpwl_correct = OPJ_FALSE;
+        parameters->jpwl_exp_comps = JPWL_EXPECTED_COMPONENTS;
+        parameters->jpwl_max_tiles = JPWL_MAXIMUM_TILES;
+#endif /* USE_JPWL */
+        /* <<UniPG */
+    }
+}
+
+
+OPJ_BOOL OPJ_CALLCONV opj_codec_set_threads(opj_codec_t *p_codec,
+        int num_threads)
+{
+    if (p_codec && (num_threads >= 0)) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+
+        return l_codec->opj_set_threads(l_codec->m_codec, (OPJ_UINT32)num_threads);
+    }
+    return OPJ_FALSE;
+}
+
+OPJ_BOOL OPJ_CALLCONV opj_setup_decoder(opj_codec_t *p_codec,
+                                        opj_dparameters_t *parameters
+                                       )
+{
+    if (p_codec && parameters) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+
+        if (! l_codec->is_decompressor) {
+            opj_event_msg(&(l_codec->m_event_mgr), EVT_ERROR,
+                          "Codec provided to the opj_setup_decoder function is not a decompressor handler.\n");
+            return OPJ_FALSE;
+        }
+
+        l_codec->m_codec_data.m_decompression.opj_setup_decoder(l_codec->m_codec,
+                parameters);
+        return OPJ_TRUE;
+    }
+    return OPJ_FALSE;
+}
+
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_decoder_set_strict_mode(opj_codec_t *p_codec,
+        OPJ_BOOL strict)
+{
+    if (p_codec) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+
+        if (! l_codec->is_decompressor) {
+            opj_event_msg(&(l_codec->m_event_mgr), EVT_ERROR,
+                          "Codec provided to the opj_decoder_set_strict_mode function is not a decompressor handler.\n");
+            return OPJ_FALSE;
+        }
+
+        l_codec->m_codec_data.m_decompression.opj_decoder_set_strict_mode(
+            l_codec->m_codec,
+            strict);
+        return OPJ_TRUE;
+    }
+    return OPJ_FALSE;
+}
+
+OPJ_BOOL OPJ_CALLCONV opj_read_header(opj_stream_t *p_stream,
+                                      opj_codec_t *p_codec,
+                                      opj_image_t **p_image)
+{
+    if (p_codec && p_stream) {
+        opj_codec_private_t* l_codec = (opj_codec_private_t*) p_codec;
+        opj_stream_private_t* l_stream = (opj_stream_private_t*) p_stream;
+
+        if (! l_codec->is_decompressor) {
+            opj_event_msg(&(l_codec->m_event_mgr), EVT_ERROR,
+                          "Codec provided to the opj_read_header function is not a decompressor handler.\n");
+            return OPJ_FALSE;
+        }
+
+        return l_codec->m_codec_data.m_decompression.opj_read_header(l_stream,
+                l_codec->m_codec,
+                p_image,
+                &(l_codec->m_event_mgr));
+    }
+
+    return OPJ_FALSE;
+}
+
+
+OPJ_BOOL OPJ_CALLCONV opj_set_decoded_components(opj_codec_t *p_codec,
+        OPJ_UINT32 numcomps,
+        const OPJ_UINT32* comps_indices,
+        OPJ_BOOL apply_color_transforms)
+{
+    if (p_codec) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+
+        if (! l_codec->is_decompressor) {
+            opj_event_msg(&(l_codec->m_event_mgr), EVT_ERROR,
+                          "Codec provided to the opj_set_decoded_components function is not a decompressor handler.\n");
+            return OPJ_FALSE;
+        }
+
+        if (apply_color_transforms) {
+            opj_event_msg(&(l_codec->m_event_mgr), EVT_ERROR,
+                          "apply_color_transforms = OPJ_TRUE is not supported.\n");
+            return OPJ_FALSE;
+        }
+
+        return  l_codec->m_codec_data.m_decompression.opj_set_decoded_components(
+                    l_codec->m_codec,
+                    numcomps,
+                    comps_indices,
+                    &(l_codec->m_event_mgr));
+    }
+    return OPJ_FALSE;
+}
+
+OPJ_BOOL OPJ_CALLCONV opj_decode(opj_codec_t *p_codec,
+                                 opj_stream_t *p_stream,
+                                 opj_image_t* p_image)
+{
+    if (p_codec && p_stream) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
+
+        if (! l_codec->is_decompressor) {
+            return OPJ_FALSE;
+        }
+
+        return l_codec->m_codec_data.m_decompression.opj_decode(l_codec->m_codec,
+                l_stream,
+                p_image,
+                &(l_codec->m_event_mgr));
+    }
+
+    return OPJ_FALSE;
+}
+
+OPJ_BOOL OPJ_CALLCONV opj_set_decode_area(opj_codec_t *p_codec,
+        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
+                                         )
+{
+    if (p_codec) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+
+        if (! l_codec->is_decompressor) {
+            return OPJ_FALSE;
+        }
+
+        return  l_codec->m_codec_data.m_decompression.opj_set_decode_area(
+                    l_codec->m_codec,
+                    p_image,
+                    p_start_x, p_start_y,
+                    p_end_x, p_end_y,
+                    &(l_codec->m_event_mgr));
+    }
+    return OPJ_FALSE;
+}
+
+OPJ_BOOL OPJ_CALLCONV opj_read_tile_header(opj_codec_t *p_codec,
+        opj_stream_t * p_stream,
+        OPJ_UINT32 * p_tile_index,
+        OPJ_UINT32 * p_data_size,
+        OPJ_INT32 * p_tile_x0, OPJ_INT32 * p_tile_y0,
+        OPJ_INT32 * p_tile_x1, OPJ_INT32 * p_tile_y1,
+        OPJ_UINT32 * p_nb_comps,
+        OPJ_BOOL * p_should_go_on)
+{
+    if (p_codec && p_stream && p_data_size && p_tile_index) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
+
+        if (! l_codec->is_decompressor) {
+            return OPJ_FALSE;
+        }
+
+        return l_codec->m_codec_data.m_decompression.opj_read_tile_header(
+                   l_codec->m_codec,
+                   p_tile_index,
+                   p_data_size,
+                   p_tile_x0, p_tile_y0,
+                   p_tile_x1, p_tile_y1,
+                   p_nb_comps,
+                   p_should_go_on,
+                   l_stream,
+                   &(l_codec->m_event_mgr));
+    }
+    return OPJ_FALSE;
+}
+
+OPJ_BOOL OPJ_CALLCONV opj_decode_tile_data(opj_codec_t *p_codec,
+        OPJ_UINT32 p_tile_index,
+        OPJ_BYTE * p_data,
+        OPJ_UINT32 p_data_size,
+        opj_stream_t *p_stream
+                                          )
+{
+    if (p_codec && p_data && p_stream) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
+
+        if (! l_codec->is_decompressor) {
+            return OPJ_FALSE;
+        }
+
+        return l_codec->m_codec_data.m_decompression.opj_decode_tile_data(
+                   l_codec->m_codec,
+                   p_tile_index,
+                   p_data,
+                   p_data_size,
+                   l_stream,
+                   &(l_codec->m_event_mgr));
+    }
+    return OPJ_FALSE;
+}
+
+OPJ_BOOL OPJ_CALLCONV opj_get_decoded_tile(opj_codec_t *p_codec,
+        opj_stream_t *p_stream,
+        opj_image_t *p_image,
+        OPJ_UINT32 tile_index)
+{
+    if (p_codec && p_stream) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
+
+        if (! l_codec->is_decompressor) {
+            return OPJ_FALSE;
+        }
+
+        return l_codec->m_codec_data.m_decompression.opj_get_decoded_tile(
+                   l_codec->m_codec,
+                   l_stream,
+                   p_image,
+                   &(l_codec->m_event_mgr),
+                   tile_index);
+    }
+
+    return OPJ_FALSE;
+}
+
+OPJ_BOOL OPJ_CALLCONV opj_set_decoded_resolution_factor(opj_codec_t *p_codec,
+        OPJ_UINT32 res_factor)
+{
+    opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+
+    if (!l_codec) {
+        return OPJ_FALSE;
+    }
+
+    return l_codec->m_codec_data.m_decompression.opj_set_decoded_resolution_factor(
+               l_codec->m_codec,
+               res_factor,
+               &(l_codec->m_event_mgr));
+}
+
+/* ---------------------------------------------------------------------- */
+/* COMPRESSION FUNCTIONS*/
+
+opj_codec_t* OPJ_CALLCONV opj_create_compress(OPJ_CODEC_FORMAT p_format)
+{
+    opj_codec_private_t *l_codec = 00;
+
+    l_codec = (opj_codec_private_t*)opj_calloc(1, sizeof(opj_codec_private_t));
+    if (!l_codec) {
+        return 00;
+    }
+
+    l_codec->is_decompressor = 0;
+
+    switch (p_format) {
+    case OPJ_CODEC_J2K:
+        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) {
+            opj_free(l_codec);
+            return 00;
+        }
+
+        break;
+
+    case OPJ_CODEC_JP2:
+        /* get a JP2 decoder handle */
+        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) {
+            opj_free(l_codec);
+            return 00;
+        }
+
+        break;
+
+    case OPJ_CODEC_UNKNOWN:
+    case OPJ_CODEC_JPT:
+    default:
+        opj_free(l_codec);
+        return 00;
+    }
+
+    opj_set_default_event_handler(&(l_codec->m_event_mgr));
+    return (opj_codec_t*) l_codec;
+}
+
+void OPJ_CALLCONV opj_set_default_encoder_parameters(opj_cparameters_t
+        *parameters)
+{
+    if (parameters) {
+        memset(parameters, 0, sizeof(opj_cparameters_t));
+        /* default coding parameters */
+        parameters->cp_cinema = OPJ_OFF; /* DEPRECATED */
+        parameters->rsiz = OPJ_PROFILE_NONE;
+        parameters->max_comp_size = 0;
+        parameters->numresolution = OPJ_COMP_PARAM_DEFAULT_NUMRESOLUTION;
+        parameters->cp_rsiz = OPJ_STD_RSIZ; /* DEPRECATED */
+        parameters->cblockw_init = OPJ_COMP_PARAM_DEFAULT_CBLOCKW;
+        parameters->cblockh_init = OPJ_COMP_PARAM_DEFAULT_CBLOCKH;
+        parameters->prog_order = OPJ_COMP_PARAM_DEFAULT_PROG_ORDER;
+        parameters->roi_compno = -1;        /* no ROI */
+        parameters->subsampling_dx = 1;
+        parameters->subsampling_dy = 1;
+        parameters->tp_on = 0;
+        parameters->decod_format = -1;
+        parameters->cod_format = -1;
+        parameters->tcp_rates[0] = 0;
+        parameters->tcp_numlayers = 0;
+        parameters->cp_disto_alloc = 0;
+        parameters->cp_fixed_alloc = 0;
+        parameters->cp_fixed_quality = 0;
+        parameters->jpip_on = OPJ_FALSE;
+        /* UniPG>> */
+#ifdef USE_JPWL
+        parameters->jpwl_epc_on = OPJ_FALSE;
+        parameters->jpwl_hprot_MH = -1; /* -1 means unassigned */
+        {
+            int i;
+            for (i = 0; i < JPWL_MAX_NO_TILESPECS; i++) {
+                parameters->jpwl_hprot_TPH_tileno[i] = -1; /* unassigned */
+                parameters->jpwl_hprot_TPH[i] = 0; /* absent */
+            }
+        };
+        {
+            int i;
+            for (i = 0; i < JPWL_MAX_NO_PACKSPECS; i++) {
+                parameters->jpwl_pprot_tileno[i] = -1; /* unassigned */
+                parameters->jpwl_pprot_packno[i] = -1; /* unassigned */
+                parameters->jpwl_pprot[i] = 0; /* absent */
+            }
+        };
+        parameters->jpwl_sens_size = 0; /* 0 means no ESD */
+        parameters->jpwl_sens_addr = 0; /* 0 means auto */
+        parameters->jpwl_sens_range = 0; /* 0 means packet */
+        parameters->jpwl_sens_MH = -1; /* -1 means unassigned */
+        {
+            int i;
+            for (i = 0; i < JPWL_MAX_NO_TILESPECS; i++) {
+                parameters->jpwl_sens_TPH_tileno[i] = -1; /* unassigned */
+                parameters->jpwl_sens_TPH[i] = -1; /* absent */
+            }
+        };
+#endif /* USE_JPWL */
+        /* <<UniPG */
+    }
+}
+
+OPJ_BOOL OPJ_CALLCONV opj_setup_encoder(opj_codec_t *p_codec,
+                                        opj_cparameters_t *parameters,
+                                        opj_image_t *p_image)
+{
+    if (p_codec && parameters && p_image) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+
+        if (! l_codec->is_decompressor) {
+            return l_codec->m_codec_data.m_compression.opj_setup_encoder(l_codec->m_codec,
+                    parameters,
+                    p_image,
+                    &(l_codec->m_event_mgr));
+        }
+    }
+
+    return OPJ_FALSE;
+}
+
+/* ----------------------------------------------------------------------- */
+
+OPJ_BOOL OPJ_CALLCONV opj_encoder_set_extra_options(opj_codec_t *p_codec,
+        const char* const* options)
+{
+    if (p_codec) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+
+        if (! l_codec->is_decompressor) {
+            return l_codec->m_codec_data.m_compression.opj_encoder_set_extra_options(
+                       l_codec->m_codec,
+                       options,
+                       &(l_codec->m_event_mgr));
+        }
+    }
+
+    return OPJ_FALSE;
+}
+
+/* ----------------------------------------------------------------------- */
+
+OPJ_BOOL OPJ_CALLCONV opj_start_compress(opj_codec_t *p_codec,
+        opj_image_t * p_image,
+        opj_stream_t *p_stream)
+{
+    if (p_codec && p_stream) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
+
+        if (! l_codec->is_decompressor) {
+            return l_codec->m_codec_data.m_compression.opj_start_compress(l_codec->m_codec,
+                    l_stream,
+                    p_image,
+                    &(l_codec->m_event_mgr));
+        }
+    }
+
+    return OPJ_FALSE;
+}
+
+OPJ_BOOL OPJ_CALLCONV opj_encode(opj_codec_t *p_info, opj_stream_t *p_stream)
+{
+    if (p_info && p_stream) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_info;
+        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
+
+        if (! l_codec->is_decompressor) {
+            return l_codec->m_codec_data.m_compression.opj_encode(l_codec->m_codec,
+                    l_stream,
+                    &(l_codec->m_event_mgr));
+        }
+    }
+
+    return OPJ_FALSE;
+
+}
+
+OPJ_BOOL OPJ_CALLCONV opj_end_compress(opj_codec_t *p_codec,
+                                       opj_stream_t *p_stream)
+{
+    if (p_codec && p_stream) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
+
+        if (! l_codec->is_decompressor) {
+            return l_codec->m_codec_data.m_compression.opj_end_compress(l_codec->m_codec,
+                    l_stream,
+                    &(l_codec->m_event_mgr));
+        }
+    }
+    return OPJ_FALSE;
+
+}
+
+OPJ_BOOL OPJ_CALLCONV opj_end_decompress(opj_codec_t *p_codec,
+        opj_stream_t *p_stream)
+{
+    if (p_codec && p_stream) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
+
+        if (! l_codec->is_decompressor) {
+            return OPJ_FALSE;
+        }
+
+        return l_codec->m_codec_data.m_decompression.opj_end_decompress(
+                   l_codec->m_codec,
+                   l_stream,
+                   &(l_codec->m_event_mgr));
+    }
+
+    return OPJ_FALSE;
+}
+
+OPJ_BOOL OPJ_CALLCONV opj_set_MCT(opj_cparameters_t *parameters,
+                                  OPJ_FLOAT32 * pEncodingMatrix,
+                                  OPJ_INT32 * p_dc_shift, OPJ_UINT32 pNbComp)
+{
+    OPJ_UINT32 l_matrix_size = pNbComp * pNbComp * (OPJ_UINT32)sizeof(OPJ_FLOAT32);
+    OPJ_UINT32 l_dc_shift_size = pNbComp * (OPJ_UINT32)sizeof(OPJ_INT32);
+    OPJ_UINT32 l_mct_total_size = l_matrix_size + l_dc_shift_size;
+
+    /* add MCT capability */
+    if (OPJ_IS_PART2(parameters->rsiz)) {
+        parameters->rsiz |= OPJ_EXTENSION_MCT;
+    } else {
+        parameters->rsiz = ((OPJ_PROFILE_PART2) | (OPJ_EXTENSION_MCT));
+    }
+    parameters->irreversible = 1;
+
+    /* use array based MCT */
+    parameters->tcp_mct = 2;
+    parameters->mct_data = opj_malloc(l_mct_total_size);
+    if (! parameters->mct_data) {
+        return OPJ_FALSE;
+    }
+
+    memcpy(parameters->mct_data, pEncodingMatrix, l_matrix_size);
+    memcpy(((OPJ_BYTE *) parameters->mct_data) +  l_matrix_size, p_dc_shift,
+           l_dc_shift_size);
+
+    return OPJ_TRUE;
+}
+
+OPJ_BOOL OPJ_CALLCONV opj_write_tile(opj_codec_t *p_codec,
+                                     OPJ_UINT32 p_tile_index,
+                                     OPJ_BYTE * p_data,
+                                     OPJ_UINT32 p_data_size,
+                                     opj_stream_t *p_stream)
+{
+    if (p_codec && p_stream && p_data) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
+
+        if (l_codec->is_decompressor) {
+            return OPJ_FALSE;
+        }
+
+        return l_codec->m_codec_data.m_compression.opj_write_tile(l_codec->m_codec,
+                p_tile_index,
+                p_data,
+                p_data_size,
+                l_stream,
+                &(l_codec->m_event_mgr));
+    }
+
+    return OPJ_FALSE;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void OPJ_CALLCONV opj_destroy_codec(opj_codec_t *p_codec)
+{
+    if (p_codec) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+
+        if (l_codec->is_decompressor) {
+            l_codec->m_codec_data.m_decompression.opj_destroy(l_codec->m_codec);
+        } else {
+            l_codec->m_codec_data.m_compression.opj_destroy(l_codec->m_codec);
+        }
+
+        l_codec->m_codec = 00;
+        opj_free(l_codec);
+    }
+}
+
+/* ---------------------------------------------------------------------- */
+
+void OPJ_CALLCONV opj_dump_codec(opj_codec_t *p_codec,
+                                 OPJ_INT32 info_flag,
+                                 FILE* output_stream)
+{
+    if (p_codec) {
+        opj_codec_private_t* l_codec = (opj_codec_private_t*) p_codec;
+
+        l_codec->opj_dump_codec(l_codec->m_codec, info_flag, output_stream);
+        return;
+    }
+
+    /* TODO return error */
+    /* fprintf(stderr, "[ERROR] Input parameter of the dump_codec function are incorrect.\n"); */
+    return;
+}
+
+opj_codestream_info_v2_t* OPJ_CALLCONV opj_get_cstr_info(opj_codec_t *p_codec)
+{
+    if (p_codec) {
+        opj_codec_private_t* l_codec = (opj_codec_private_t*) p_codec;
+
+        return l_codec->opj_get_codec_info(l_codec->m_codec);
+    }
+
+    return NULL;
+}
+
+void OPJ_CALLCONV opj_destroy_cstr_info(opj_codestream_info_v2_t **cstr_info)
+{
+    if (cstr_info) {
+
+        if ((*cstr_info)->m_default_tile_info.tccp_info) {
+            opj_free((*cstr_info)->m_default_tile_info.tccp_info);
+        }
+
+        if ((*cstr_info)->tile_info) {
+            /* FIXME not used for the moment*/
+        }
+
+        opj_free((*cstr_info));
+        (*cstr_info) = NULL;
+    }
+}
+
+opj_codestream_index_t * OPJ_CALLCONV opj_get_cstr_index(opj_codec_t *p_codec)
+{
+    if (p_codec) {
+        opj_codec_private_t* l_codec = (opj_codec_private_t*) p_codec;
+
+        return l_codec->opj_get_codec_index(l_codec->m_codec);
+    }
+
+    return NULL;
+}
+
+void OPJ_CALLCONV opj_destroy_cstr_index(opj_codestream_index_t **p_cstr_index)
+{
+    if (*p_cstr_index) {
+        j2k_destroy_cstr_index(*p_cstr_index);
+        (*p_cstr_index) = NULL;
+    }
+}
+
+opj_stream_t* OPJ_CALLCONV opj_stream_create_default_file_stream(
+    const char *fname, OPJ_BOOL p_is_read_stream)
+{
+    return opj_stream_create_file_stream(fname, OPJ_J2K_STREAM_CHUNK_SIZE,
+                                         p_is_read_stream);
+}
+
+opj_stream_t* OPJ_CALLCONV opj_stream_create_file_stream(
+    const char *fname,
+    OPJ_SIZE_T p_size,
+    OPJ_BOOL p_is_read_stream)
+{
+    opj_stream_t* l_stream = 00;
+    FILE *p_file;
+    const char *mode;
+
+    if (! fname) {
+        return NULL;
+    }
+
+    if (p_is_read_stream) {
+        mode = "rb";
+    } else {
+        mode = "wb";
+    }
+
+    p_file = fopen(fname, mode);
+
+    if (! p_file) {
+        return NULL;
+    }
+
+    l_stream = opj_stream_create(p_size, p_is_read_stream);
+    if (! l_stream) {
+        fclose(p_file);
+        return NULL;
+    }
+
+    opj_stream_set_user_data(l_stream, p_file, opj_close_from_file);
+    opj_stream_set_user_data_length(l_stream,
+                                    opj_get_data_length_from_file(p_file));
+    opj_stream_set_read_function(l_stream, opj_read_from_file);
+    opj_stream_set_write_function(l_stream,
+                                  (opj_stream_write_fn) opj_write_from_file);
+    opj_stream_set_skip_function(l_stream, opj_skip_from_file);
+    opj_stream_set_seek_function(l_stream, opj_seek_from_file);
+
+    return l_stream;
+}
+
+
+void* OPJ_CALLCONV opj_image_data_alloc(OPJ_SIZE_T size)
+{
+    void* ret = opj_aligned_malloc(size);
+    /* printf("opj_image_data_alloc %p\n", ret); */
+    return ret;
+}
+
+void OPJ_CALLCONV opj_image_data_free(void* ptr)
+{
+    /* printf("opj_image_data_free %p\n", ptr); */
+    opj_aligned_free(ptr);
+}
diff --git a/third_party/libopenjpeg/openjpeg.h b/third_party/libopenjpeg/openjpeg.h
new file mode 100644
index 0000000..ebce53d
--- /dev/null
+++ b/third_party/libopenjpeg/openjpeg.h
@@ -0,0 +1,1777 @@
+/*
+* The copyright in this software is being made available under the 2-clauses
+* BSD License, included below. This software may be subject to other third
+* party and contributor rights, including patent rights, and no such rights
+* are granted under this license.
+*
+* Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+* Copyright (c) 2002-2014, Professor Benoit Macq
+* Copyright (c) 2001-2003, David Janssens
+* Copyright (c) 2002-2003, Yannick Verschueren
+* Copyright (c) 2003-2007, Francois-Olivier Devaux
+* Copyright (c) 2003-2014, Antonin Descampe
+* Copyright (c) 2005, Herve Drolon, FreeImage Team
+* Copyright (c) 2006-2007, Parvatha Elangovan
+* Copyright (c) 2008, Jerome Fimes, Communications & Systemes <jerome.fimes@c-s.fr>
+* Copyright (c) 2010-2011, Kaori Hagihara
+* Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France
+* Copyright (c) 2012, CS Systemes d'Information, France
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+* POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef OPENJPEG_H
+#define OPENJPEG_H
+
+
+/*
+==========================================================
+   Compiler directives
+==========================================================
+*/
+
+/*
+The inline keyword is supported by C99 but not by C90.
+Most compilers implement their own version of this keyword ...
+*/
+#ifndef INLINE
+#if defined(_MSC_VER)
+#define INLINE __forceinline
+#elif defined(__GNUC__)
+#define INLINE __inline__
+#elif defined(__MWERKS__)
+#define INLINE inline
+#else
+/* add other compilers here ... */
+#define INLINE
+#endif /* defined(<Compiler>) */
+#endif /* INLINE */
+
+/* deprecated attribute */
+#ifdef __GNUC__
+#define OPJ_DEPRECATED(func) func __attribute__ ((deprecated))
+#elif defined(_MSC_VER)
+#define OPJ_DEPRECATED(func) __declspec(deprecated) func
+#else
+#pragma message("WARNING: You need to implement DEPRECATED for this compiler")
+#define OPJ_DEPRECATED(func) func
+#endif
+
+#if defined(__GNUC__) && __GNUC__ >= 6
+#define OPJ_DEPRECATED_STRUCT_MEMBER(memb, msg) __attribute__ ((deprecated(msg))) memb
+#else
+#define OPJ_DEPRECATED_STRUCT_MEMBER(memb, msg) memb
+#endif
+
+#if defined(OPJ_STATIC) || !defined(_WIN32)
+/* http://gcc.gnu.org/wiki/Visibility */
+#   if !defined(_WIN32) && __GNUC__ >= 4
+#       if defined(OPJ_STATIC) /* static library uses "hidden" */
+#           define OPJ_API    __attribute__ ((visibility ("hidden")))
+#       else
+#           define OPJ_API    __attribute__ ((visibility ("default")))
+#       endif
+#       define OPJ_LOCAL  __attribute__ ((visibility ("hidden")))
+#   else
+#       define OPJ_API
+#       define OPJ_LOCAL
+#   endif
+#   define OPJ_CALLCONV
+#else
+#   define OPJ_CALLCONV __stdcall
+/*
+The following ifdef block is the standard way of creating macros which make exporting
+from a DLL simpler. All files within this DLL are compiled with the OPJ_EXPORTS
+symbol defined on the command line. this symbol should not be defined on any project
+that uses this DLL. This way any other project whose source files include this file see
+OPJ_API functions as being imported from a DLL, whereas this DLL sees symbols
+defined with this macro as being exported.
+*/
+#   if defined(OPJ_EXPORTS) || defined(DLL_EXPORT)
+#       define OPJ_API __declspec(dllexport)
+#   else
+#       define OPJ_API __declspec(dllimport)
+#   endif /* OPJ_EXPORTS */
+#endif /* !OPJ_STATIC || !_WIN32 */
+
+typedef int OPJ_BOOL;
+#define OPJ_TRUE 1
+#define OPJ_FALSE 0
+
+typedef char          OPJ_CHAR;
+typedef float         OPJ_FLOAT32;
+typedef double        OPJ_FLOAT64;
+typedef unsigned char OPJ_BYTE;
+
+#include "opj_stdint.h"
+
+typedef int8_t   OPJ_INT8;
+typedef uint8_t  OPJ_UINT8;
+typedef int16_t  OPJ_INT16;
+typedef uint16_t OPJ_UINT16;
+typedef int32_t  OPJ_INT32;
+typedef uint32_t OPJ_UINT32;
+typedef int64_t  OPJ_INT64;
+typedef uint64_t OPJ_UINT64;
+
+typedef int64_t  OPJ_OFF_T; /* 64-bit file offset type */
+
+#include <stdio.h>
+typedef size_t   OPJ_SIZE_T;
+
+/* Avoid compile-time warning because parameter is not used */
+#define OPJ_ARG_NOT_USED(x) (void)(x)
+
+/*
+==========================================================
+   Useful constant definitions
+==========================================================
+*/
+
+#define OPJ_PATH_LEN 4096 /**< Maximum allowed size for filenames */
+
+#define OPJ_J2K_MAXRLVLS 33                 /**< Number of maximum resolution level authorized */
+#define OPJ_J2K_MAXBANDS (3*OPJ_J2K_MAXRLVLS-2) /**< Number of maximum sub-band linked to number of resolution level */
+
+#define OPJ_J2K_DEFAULT_NB_SEGS             10
+#define OPJ_J2K_STREAM_CHUNK_SIZE           0x100000 /** 1 mega by default */
+#define OPJ_J2K_DEFAULT_HEADER_SIZE         1000
+#define OPJ_J2K_MCC_DEFAULT_NB_RECORDS      10
+#define OPJ_J2K_MCT_DEFAULT_NB_RECORDS      10
+
+/* UniPG>> */ /* NOT YET USED IN THE V2 VERSION OF OPENJPEG */
+#define JPWL_MAX_NO_TILESPECS   16 /**< Maximum number of tile parts expected by JPWL: increase at your will */
+#define JPWL_MAX_NO_PACKSPECS   16 /**< Maximum number of packet parts expected by JPWL: increase at your will */
+#define JPWL_MAX_NO_MARKERS 512 /**< Maximum number of JPWL markers: increase at your will */
+#define JPWL_PRIVATEINDEX_NAME "jpwl_index_privatefilename" /**< index file name used when JPWL is on */
+#define JPWL_EXPECTED_COMPONENTS 3 /**< Expect this number of components, so you'll find better the first EPB */
+#define JPWL_MAXIMUM_TILES 8192 /**< Expect this maximum number of tiles, to avoid some crashes */
+#define JPWL_MAXIMUM_HAMMING 2 /**< Expect this maximum number of bit errors in marker id's */
+#define JPWL_MAXIMUM_EPB_ROOM 65450 /**< Expect this maximum number of bytes for composition of EPBs */
+/* <<UniPG */
+
+/**
+ * EXPERIMENTAL FOR THE MOMENT
+ * Supported options about file information used only in j2k_dump
+*/
+#define OPJ_IMG_INFO        1   /**< Basic image information provided to the user */
+#define OPJ_J2K_MH_INFO     2   /**< Codestream information based only on the main header */
+#define OPJ_J2K_TH_INFO     4   /**< Tile information based on the current tile header */
+#define OPJ_J2K_TCH_INFO    8   /**< Tile/Component information of all tiles */
+#define OPJ_J2K_MH_IND      16  /**< Codestream index based only on the main header */
+#define OPJ_J2K_TH_IND      32  /**< Tile index based on the current tile */
+/*FIXME #define OPJ_J2K_CSTR_IND    48*/    /**<  */
+#define OPJ_JP2_INFO        128 /**< JP2 file information */
+#define OPJ_JP2_IND         256 /**< JP2 file index */
+
+/**
+ * JPEG 2000 Profiles, see Table A.10 from 15444-1 (updated in various AMD)
+ * These values help choosing the RSIZ value for the J2K codestream.
+ * The RSIZ value triggers various encoding options, as detailed in Table A.10.
+ * If OPJ_PROFILE_PART2 is chosen, it has to be combined with one or more extensions
+ * described hereunder.
+ *   Example: rsiz = OPJ_PROFILE_PART2 | OPJ_EXTENSION_MCT;
+ * For broadcast profiles, the OPJ_PROFILE value has to be combined with the targeted
+ * mainlevel (3-0 LSB, value between 0 and 11):
+ *   Example: rsiz = OPJ_PROFILE_BC_MULTI | 0x0005; (here mainlevel 5)
+ * For IMF profiles, the OPJ_PROFILE value has to be combined with the targeted mainlevel
+ * (3-0 LSB, value between 0 and 11) and sublevel (7-4 LSB, value between 0 and 9):
+ *   Example: rsiz = OPJ_PROFILE_IMF_2K | 0x0040 | 0x0005; (here main 5 and sublevel 4)
+ * */
+#define OPJ_PROFILE_NONE        0x0000 /** no profile, conform to 15444-1 */
+#define OPJ_PROFILE_0           0x0001 /** Profile 0 as described in 15444-1,Table A.45 */
+#define OPJ_PROFILE_1           0x0002 /** Profile 1 as described in 15444-1,Table A.45 */
+#define OPJ_PROFILE_PART2       0x8000 /** At least 1 extension defined in 15444-2 (Part-2) */
+#define OPJ_PROFILE_CINEMA_2K   0x0003 /** 2K cinema profile defined in 15444-1 AMD1 */
+#define OPJ_PROFILE_CINEMA_4K   0x0004 /** 4K cinema profile defined in 15444-1 AMD1 */
+#define OPJ_PROFILE_CINEMA_S2K  0x0005 /** Scalable 2K cinema profile defined in 15444-1 AMD2 */
+#define OPJ_PROFILE_CINEMA_S4K  0x0006 /** Scalable 4K cinema profile defined in 15444-1 AMD2 */
+#define OPJ_PROFILE_CINEMA_LTS  0x0007 /** Long term storage cinema profile defined in 15444-1 AMD2 */
+#define OPJ_PROFILE_BC_SINGLE   0x0100 /** Single Tile Broadcast profile defined in 15444-1 AMD3 */
+#define OPJ_PROFILE_BC_MULTI    0x0200 /** Multi Tile Broadcast profile defined in 15444-1 AMD3 */
+#define OPJ_PROFILE_BC_MULTI_R  0x0300 /** Multi Tile Reversible Broadcast profile defined in 15444-1 AMD3 */
+#define OPJ_PROFILE_IMF_2K      0x0400 /** 2K Single Tile Lossy IMF profile defined in 15444-1 AMD 8 */
+#define OPJ_PROFILE_IMF_4K      0x0500 /** 4K Single Tile Lossy IMF profile defined in 15444-1 AMD 8 */
+#define OPJ_PROFILE_IMF_8K      0x0600 /** 8K Single Tile Lossy IMF profile defined in 15444-1 AMD 8 */
+#define OPJ_PROFILE_IMF_2K_R    0x0700 /** 2K Single/Multi Tile Reversible IMF profile defined in 15444-1 AMD 8 */
+#define OPJ_PROFILE_IMF_4K_R    0x0800 /** 4K Single/Multi Tile Reversible IMF profile defined in 15444-1 AMD 8 */
+#define OPJ_PROFILE_IMF_8K_R    0x0900 /** 8K Single/Multi Tile Reversible IMF profile defined in 15444-1 AMD 8 */
+
+/**
+ * JPEG 2000 Part-2 extensions
+ * */
+#define OPJ_EXTENSION_NONE      0x0000 /** No Part-2 extension */
+#define OPJ_EXTENSION_MCT       0x0100  /** Custom MCT support */
+
+/**
+ * JPEG 2000 profile macros
+ * */
+#define OPJ_IS_CINEMA(v)     (((v) >= OPJ_PROFILE_CINEMA_2K)&&((v) <= OPJ_PROFILE_CINEMA_S4K))
+#define OPJ_IS_STORAGE(v)    ((v) == OPJ_PROFILE_CINEMA_LTS)
+#define OPJ_IS_BROADCAST(v)  (((v) >= OPJ_PROFILE_BC_SINGLE)&&((v) <= ((OPJ_PROFILE_BC_MULTI_R) | (0x000b))))
+#define OPJ_IS_IMF(v)        (((v) >= OPJ_PROFILE_IMF_2K)&&((v) <= ((OPJ_PROFILE_IMF_8K_R) | (0x009b))))
+#define OPJ_IS_PART2(v)      ((v) & OPJ_PROFILE_PART2)
+
+#define OPJ_GET_IMF_PROFILE(v)   ((v) & 0xff00)      /** Extract IMF profile without mainlevel/sublevel */
+#define OPJ_GET_IMF_MAINLEVEL(v) ((v) & 0xf)         /** Extract IMF main level */
+#define OPJ_GET_IMF_SUBLEVEL(v)  (((v) >> 4) & 0xf)  /** Extract IMF sub level */
+
+#define OPJ_IMF_MAINLEVEL_MAX    11   /** Maximum main level */
+
+/** Max. Components Sampling Rate (MSamples/sec) per IMF main level */
+#define OPJ_IMF_MAINLEVEL_1_MSAMPLESEC   65      /** MSamples/sec for IMF main level 1 */
+#define OPJ_IMF_MAINLEVEL_2_MSAMPLESEC   130     /** MSamples/sec for IMF main level 2 */
+#define OPJ_IMF_MAINLEVEL_3_MSAMPLESEC   195     /** MSamples/sec for IMF main level 3 */
+#define OPJ_IMF_MAINLEVEL_4_MSAMPLESEC   260     /** MSamples/sec for IMF main level 4 */
+#define OPJ_IMF_MAINLEVEL_5_MSAMPLESEC   520     /** MSamples/sec for IMF main level 5 */
+#define OPJ_IMF_MAINLEVEL_6_MSAMPLESEC   1200    /** MSamples/sec for IMF main level 6 */
+#define OPJ_IMF_MAINLEVEL_7_MSAMPLESEC   2400    /** MSamples/sec for IMF main level 7 */
+#define OPJ_IMF_MAINLEVEL_8_MSAMPLESEC   4800    /** MSamples/sec for IMF main level 8 */
+#define OPJ_IMF_MAINLEVEL_9_MSAMPLESEC   9600    /** MSamples/sec for IMF main level 9 */
+#define OPJ_IMF_MAINLEVEL_10_MSAMPLESEC  19200   /** MSamples/sec for IMF main level 10 */
+#define OPJ_IMF_MAINLEVEL_11_MSAMPLESEC  38400   /** MSamples/sec for IMF main level 11 */
+
+/** Max. compressed Bit Rate (Mbits/s) per IMF sub level */
+#define OPJ_IMF_SUBLEVEL_1_MBITSSEC      200     /** Mbits/s for IMF sub level 1 */
+#define OPJ_IMF_SUBLEVEL_2_MBITSSEC      400     /** Mbits/s for IMF sub level 2 */
+#define OPJ_IMF_SUBLEVEL_3_MBITSSEC      800     /** Mbits/s for IMF sub level 3 */
+#define OPJ_IMF_SUBLEVEL_4_MBITSSEC     1600     /** Mbits/s for IMF sub level 4 */
+#define OPJ_IMF_SUBLEVEL_5_MBITSSEC     3200     /** Mbits/s for IMF sub level 5 */
+#define OPJ_IMF_SUBLEVEL_6_MBITSSEC     6400     /** Mbits/s for IMF sub level 6 */
+#define OPJ_IMF_SUBLEVEL_7_MBITSSEC    12800     /** Mbits/s for IMF sub level 7 */
+#define OPJ_IMF_SUBLEVEL_8_MBITSSEC    25600     /** Mbits/s for IMF sub level 8 */
+#define OPJ_IMF_SUBLEVEL_9_MBITSSEC    51200     /** Mbits/s for IMF sub level 9 */
+
+/**
+ * JPEG 2000 codestream and component size limits in cinema profiles
+ * */
+#define OPJ_CINEMA_24_CS     1302083    /** Maximum codestream length for 24fps */
+#define OPJ_CINEMA_48_CS     651041     /** Maximum codestream length for 48fps */
+#define OPJ_CINEMA_24_COMP   1041666    /** Maximum size per color component for 2K & 4K @ 24fps */
+#define OPJ_CINEMA_48_COMP   520833     /** Maximum size per color component for 2K @ 48fps */
+
+/*
+==========================================================
+   enum definitions
+==========================================================
+*/
+
+/**
+ * DEPRECATED: use RSIZ, OPJ_PROFILE_* and OPJ_EXTENSION_* instead
+ * Rsiz Capabilities
+ * */
+typedef enum RSIZ_CAPABILITIES {
+    OPJ_STD_RSIZ = 0,       /** Standard JPEG2000 profile*/
+    OPJ_CINEMA2K = 3,       /** Profile name for a 2K image*/
+    OPJ_CINEMA4K = 4,       /** Profile name for a 4K image*/
+    OPJ_MCT = 0x8100
+} OPJ_RSIZ_CAPABILITIES;
+
+/**
+ * DEPRECATED: use RSIZ, OPJ_PROFILE_* and OPJ_EXTENSION_* instead
+ * Digital cinema operation mode
+ * */
+typedef enum CINEMA_MODE {
+    OPJ_OFF = 0,            /** Not Digital Cinema*/
+    OPJ_CINEMA2K_24 = 1,    /** 2K Digital Cinema at 24 fps*/
+    OPJ_CINEMA2K_48 = 2,    /** 2K Digital Cinema at 48 fps*/
+    OPJ_CINEMA4K_24 = 3     /** 4K Digital Cinema at 24 fps*/
+} OPJ_CINEMA_MODE;
+
+/**
+ * Progression order
+ * */
+typedef enum PROG_ORDER {
+    OPJ_PROG_UNKNOWN = -1,  /**< place-holder */
+    OPJ_LRCP = 0,           /**< layer-resolution-component-precinct order */
+    OPJ_RLCP = 1,           /**< resolution-layer-component-precinct order */
+    OPJ_RPCL = 2,           /**< resolution-precinct-component-layer order */
+    OPJ_PCRL = 3,           /**< precinct-component-resolution-layer order */
+    OPJ_CPRL = 4            /**< component-precinct-resolution-layer order */
+} OPJ_PROG_ORDER;
+
+/**
+ * Supported image color spaces
+*/
+typedef enum COLOR_SPACE {
+    OPJ_CLRSPC_UNKNOWN = -1,    /**< not supported by the library */
+    OPJ_CLRSPC_UNSPECIFIED = 0, /**< not specified in the codestream */
+    OPJ_CLRSPC_SRGB = 1,        /**< sRGB */
+    OPJ_CLRSPC_GRAY = 2,        /**< grayscale */
+    OPJ_CLRSPC_SYCC = 3,        /**< YUV */
+    OPJ_CLRSPC_EYCC = 4,        /**< e-YCC */
+    OPJ_CLRSPC_CMYK = 5         /**< CMYK */
+} OPJ_COLOR_SPACE;
+
+/**
+ * Supported codec
+*/
+typedef enum CODEC_FORMAT {
+    OPJ_CODEC_UNKNOWN = -1, /**< place-holder */
+    OPJ_CODEC_J2K  = 0,     /**< JPEG-2000 codestream : read/write */
+    OPJ_CODEC_JPT  = 1,     /**< JPT-stream (JPEG 2000, JPIP) : read only */
+    OPJ_CODEC_JP2  = 2,     /**< JP2 file format : read/write */
+    OPJ_CODEC_JPP  = 3,     /**< JPP-stream (JPEG 2000, JPIP) : to be coded */
+    OPJ_CODEC_JPX  = 4      /**< JPX file format (JPEG 2000 Part-2) : to be coded */
+} OPJ_CODEC_FORMAT;
+
+
+/*
+==========================================================
+   event manager typedef definitions
+==========================================================
+*/
+
+/**
+ * Callback function prototype for events
+ * @param msg               Event message
+ * @param client_data       Client object where will be return the event message
+ * */
+typedef void (*opj_msg_callback)(const char *msg, void *client_data);
+
+/*
+==========================================================
+   codec typedef definitions
+==========================================================
+*/
+
+#ifndef OPJ_UINT32_SEMANTICALLY_BUT_INT32
+#define OPJ_UINT32_SEMANTICALLY_BUT_INT32 OPJ_INT32
+#endif
+
+/**
+ * Progression order changes
+ *
+ */
+typedef struct opj_poc {
+    /** Resolution num start, Component num start, given by POC */
+    OPJ_UINT32 resno0, compno0;
+    /** Layer num end,Resolution num end, Component num end, given by POC */
+    OPJ_UINT32 layno1, resno1, compno1;
+    /** Layer num start,Precinct num start, Precinct num end */
+    OPJ_UINT32 layno0, precno0, precno1;
+    /** Progression order enum*/
+    OPJ_PROG_ORDER prg1, prg;
+    /** Progression order string*/
+    OPJ_CHAR progorder[5];
+    /** Tile number (starting at 1) */
+    OPJ_UINT32 tile;
+    /** Start and end values for Tile width and height*/
+    OPJ_UINT32_SEMANTICALLY_BUT_INT32 tx0, tx1, ty0, ty1;
+    /** Start value, initialised in pi_initialise_encode*/
+    OPJ_UINT32 layS, resS, compS, prcS;
+    /** End value, initialised in pi_initialise_encode */
+    OPJ_UINT32 layE, resE, compE, prcE;
+    /** Start and end values of Tile width and height, initialised in pi_initialise_encode*/
+    OPJ_UINT32 txS, txE, tyS, tyE, dx, dy;
+    /** Temporary values for Tile parts, initialised in pi_create_encode */
+    OPJ_UINT32 lay_t, res_t, comp_t, prc_t, tx0_t, ty0_t;
+} opj_poc_t;
+
+/**
+ * Compression parameters
+ * */
+typedef struct opj_cparameters {
+    /** size of tile: tile_size_on = false (not in argument) or = true (in argument) */
+    OPJ_BOOL tile_size_on;
+    /** XTOsiz */
+    int cp_tx0;
+    /** YTOsiz */
+    int cp_ty0;
+    /** XTsiz */
+    int cp_tdx;
+    /** YTsiz */
+    int cp_tdy;
+    /** allocation by rate/distortion */
+    int cp_disto_alloc;
+    /** allocation by fixed layer */
+    int cp_fixed_alloc;
+    /** add fixed_quality */
+    int cp_fixed_quality;
+    /** fixed layer */
+    int *cp_matrice;
+    /** comment for coding */
+    char *cp_comment;
+    /** csty : coding style */
+    int csty;
+    /** progression order (default OPJ_LRCP) */
+    OPJ_PROG_ORDER prog_order;
+    /** progression order changes */
+    opj_poc_t POC[32];
+    /** number of progression order changes (POC), default to 0 */
+    OPJ_UINT32 numpocs;
+    /** number of layers */
+    int tcp_numlayers;
+    /** rates of layers - might be subsequently limited by the max_cs_size field.
+     * Should be decreasing. 1 can be
+     * used as last value to indicate the last layer is lossless. */
+    float tcp_rates[100];
+    /** different psnr for successive layers. Should be increasing. 0 can be
+     * used as last value to indicate the last layer is lossless. */
+    float tcp_distoratio[100];
+    /** number of resolutions */
+    int numresolution;
+    /** initial code block width, default to 64 */
+    int cblockw_init;
+    /** initial code block height, default to 64 */
+    int cblockh_init;
+    /** mode switch (cblk_style) */
+    int mode;
+    /** 1 : use the irreversible DWT 9-7, 0 : use lossless compression (default) */
+    int irreversible;
+    /** region of interest: affected component in [0..3], -1 means no ROI */
+    int roi_compno;
+    /** region of interest: upshift value */
+    int roi_shift;
+    /* number of precinct size specifications */
+    int res_spec;
+    /** initial precinct width */
+    int prcw_init[OPJ_J2K_MAXRLVLS];
+    /** initial precinct height */
+    int prch_init[OPJ_J2K_MAXRLVLS];
+
+    /**@name command line encoder parameters (not used inside the library) */
+    /*@{*/
+    /** input file name */
+    char infile[OPJ_PATH_LEN];
+    /** output file name */
+    char outfile[OPJ_PATH_LEN];
+    /** DEPRECATED. Index generation is now handled with the opj_encode_with_info() function. Set to NULL */
+    int index_on;
+    /** DEPRECATED. Index generation is now handled with the opj_encode_with_info() function. Set to NULL */
+    char index[OPJ_PATH_LEN];
+    /** subimage encoding: origin image offset in x direction */
+    int image_offset_x0;
+    /** subimage encoding: origin image offset in y direction */
+    int image_offset_y0;
+    /** subsampling value for dx */
+    int subsampling_dx;
+    /** subsampling value for dy */
+    int subsampling_dy;
+    /** input file format 0: PGX, 1: PxM, 2: BMP 3:TIF*/
+    int decod_format;
+    /** output file format 0: J2K, 1: JP2, 2: JPT */
+    int cod_format;
+    /*@}*/
+
+    /* UniPG>> */ /* NOT YET USED IN THE V2 VERSION OF OPENJPEG */
+    /**@name JPWL encoding parameters */
+    /*@{*/
+    /** enables writing of EPC in MH, thus activating JPWL */
+    OPJ_BOOL jpwl_epc_on;
+    /** error protection method for MH (0,1,16,32,37-128) */
+    int jpwl_hprot_MH;
+    /** tile number of header protection specification (>=0) */
+    int jpwl_hprot_TPH_tileno[JPWL_MAX_NO_TILESPECS];
+    /** error protection methods for TPHs (0,1,16,32,37-128) */
+    int jpwl_hprot_TPH[JPWL_MAX_NO_TILESPECS];
+    /** tile number of packet protection specification (>=0) */
+    int jpwl_pprot_tileno[JPWL_MAX_NO_PACKSPECS];
+    /** packet number of packet protection specification (>=0) */
+    int jpwl_pprot_packno[JPWL_MAX_NO_PACKSPECS];
+    /** error protection methods for packets (0,1,16,32,37-128) */
+    int jpwl_pprot[JPWL_MAX_NO_PACKSPECS];
+    /** enables writing of ESD, (0=no/1/2 bytes) */
+    int jpwl_sens_size;
+    /** sensitivity addressing size (0=auto/2/4 bytes) */
+    int jpwl_sens_addr;
+    /** sensitivity range (0-3) */
+    int jpwl_sens_range;
+    /** sensitivity method for MH (-1=no,0-7) */
+    int jpwl_sens_MH;
+    /** tile number of sensitivity specification (>=0) */
+    int jpwl_sens_TPH_tileno[JPWL_MAX_NO_TILESPECS];
+    /** sensitivity methods for TPHs (-1=no,0-7) */
+    int jpwl_sens_TPH[JPWL_MAX_NO_TILESPECS];
+    /*@}*/
+    /* <<UniPG */
+
+    /**
+     * DEPRECATED: use RSIZ, OPJ_PROFILE_* and MAX_COMP_SIZE instead
+     * Digital Cinema compliance 0-not compliant, 1-compliant
+     * */
+    OPJ_CINEMA_MODE cp_cinema;
+    /**
+     * Maximum size (in bytes) for each component.
+     * If == 0, component size limitation is not considered
+     * */
+    int max_comp_size;
+    /**
+     * DEPRECATED: use RSIZ, OPJ_PROFILE_* and OPJ_EXTENSION_* instead
+     * Profile name
+     * */
+    OPJ_RSIZ_CAPABILITIES cp_rsiz;
+    /** Tile part generation*/
+    char tp_on;
+    /** Flag for Tile part generation*/
+    char tp_flag;
+    /** MCT (multiple component transform) */
+    char tcp_mct;
+    /** Enable JPIP indexing*/
+    OPJ_BOOL jpip_on;
+    /** Naive implementation of MCT restricted to a single reversible array based
+        encoding without offset concerning all the components. */
+    void * mct_data;
+    /**
+     * Maximum size (in bytes) for the whole codestream.
+     * If == 0, codestream size limitation is not considered
+     * If it does not comply with tcp_rates, max_cs_size prevails
+     * and a warning is issued.
+     * */
+    int max_cs_size;
+    /** RSIZ value
+        To be used to combine OPJ_PROFILE_*, OPJ_EXTENSION_* and (sub)levels values. */
+    OPJ_UINT16 rsiz;
+} opj_cparameters_t;
+
+#define OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG  0x0001
+#define OPJ_DPARAMETERS_DUMP_FLAG 0x0002
+
+/**
+ * Decompression parameters
+ * */
+typedef struct opj_dparameters {
+    /**
+    Set the number of highest resolution levels to be discarded.
+    The image resolution is effectively divided by 2 to the power of the number of discarded levels.
+    The reduce factor is limited by the smallest total number of decomposition levels among tiles.
+    if != 0, then original dimension divided by 2^(reduce);
+    if == 0 or not used, image is decoded to the full resolution
+    */
+    OPJ_UINT32 cp_reduce;
+    /**
+    Set the maximum number of quality layers to decode.
+    If there are less quality layers than the specified number, all the quality layers are decoded.
+    if != 0, then only the first "layer" layers are decoded;
+    if == 0 or not used, all the quality layers are decoded
+    */
+    OPJ_UINT32 cp_layer;
+
+    /**@name command line decoder parameters (not used inside the library) */
+    /*@{*/
+    /** input file name */
+    char infile[OPJ_PATH_LEN];
+    /** output file name */
+    char outfile[OPJ_PATH_LEN];
+    /** input file format 0: J2K, 1: JP2, 2: JPT */
+    int decod_format;
+    /** output file format 0: PGX, 1: PxM, 2: BMP */
+    int cod_format;
+
+    /** Decoding area left boundary */
+    OPJ_UINT32 DA_x0;
+    /** Decoding area right boundary */
+    OPJ_UINT32 DA_x1;
+    /** Decoding area up boundary */
+    OPJ_UINT32 DA_y0;
+    /** Decoding area bottom boundary */
+    OPJ_UINT32 DA_y1;
+    /** Verbose mode */
+    OPJ_BOOL m_verbose;
+
+    /** tile number of the decoded tile */
+    OPJ_UINT32 tile_index;
+    /** Nb of tile to decode */
+    OPJ_UINT32 nb_tile_to_decode;
+
+    /*@}*/
+
+    /* UniPG>> */ /* NOT YET USED IN THE V2 VERSION OF OPENJPEG */
+    /**@name JPWL decoding parameters */
+    /*@{*/
+    /** activates the JPWL correction capabilities */
+    OPJ_BOOL jpwl_correct;
+    /** expected number of components */
+    int jpwl_exp_comps;
+    /** maximum number of tiles */
+    int jpwl_max_tiles;
+    /*@}*/
+    /* <<UniPG */
+
+    unsigned int flags;
+
+} opj_dparameters_t;
+
+
+/**
+ * JPEG2000 codec V2.
+ * */
+typedef void * opj_codec_t;
+
+/*
+==========================================================
+   I/O stream typedef definitions
+==========================================================
+*/
+
+/**
+ * Stream open flags.
+ * */
+/** The stream was opened for reading. */
+#define OPJ_STREAM_READ OPJ_TRUE
+/** The stream was opened for writing. */
+#define OPJ_STREAM_WRITE OPJ_FALSE
+
+/*
+ * Callback function prototype for read function
+ */
+typedef OPJ_SIZE_T(* opj_stream_read_fn)(void * p_buffer, OPJ_SIZE_T p_nb_bytes,
+        void * p_user_data) ;
+
+/*
+ * Callback function prototype for write function
+ */
+typedef OPJ_SIZE_T(* opj_stream_write_fn)(void * p_buffer,
+        OPJ_SIZE_T p_nb_bytes, void * p_user_data) ;
+
+/*
+ * Callback function prototype for skip function
+ */
+typedef OPJ_OFF_T(* opj_stream_skip_fn)(OPJ_OFF_T p_nb_bytes,
+                                        void * p_user_data) ;
+
+/*
+ * Callback function prototype for seek function
+ */
+typedef OPJ_BOOL(* opj_stream_seek_fn)(OPJ_OFF_T p_nb_bytes,
+                                       void * p_user_data) ;
+
+/*
+ * Callback function prototype for free user data function
+ */
+typedef void (* opj_stream_free_user_data_fn)(void * p_user_data) ;
+
+/*
+ * JPEG2000 Stream.
+ */
+typedef void * opj_stream_t;
+
+/*
+==========================================================
+   image typedef definitions
+==========================================================
+*/
+
+/**
+ * Defines a single image component
+ * */
+typedef struct opj_image_comp {
+    /** XRsiz: horizontal separation of a sample of ith component with respect to the reference grid */
+    OPJ_UINT32 dx;
+    /** YRsiz: vertical separation of a sample of ith component with respect to the reference grid */
+    OPJ_UINT32 dy;
+    /** data width */
+    OPJ_UINT32 w;
+    /** data height */
+    OPJ_UINT32 h;
+    /** x component offset compared to the whole image */
+    OPJ_UINT32 x0;
+    /** y component offset compared to the whole image */
+    OPJ_UINT32 y0;
+    /** precision: number of bits per component per pixel */
+    OPJ_UINT32 prec;
+    /** obsolete: use prec instead */
+    OPJ_DEPRECATED_STRUCT_MEMBER(OPJ_UINT32 bpp, "Use prec instead");
+    /** signed (1) / unsigned (0) */
+    OPJ_UINT32 sgnd;
+    /** number of decoded resolution */
+    OPJ_UINT32 resno_decoded;
+    /** number of division by 2 of the out image compared to the original size of image */
+    OPJ_UINT32 factor;
+    /** image component data */
+    OPJ_INT32 *data;
+    /** alpha channel */
+    OPJ_UINT16 alpha;
+} opj_image_comp_t;
+
+/**
+ * Defines image data and characteristics
+ * */
+typedef struct opj_image {
+    /** XOsiz: horizontal offset from the origin of the reference grid to the left side of the image area */
+    OPJ_UINT32 x0;
+    /** YOsiz: vertical offset from the origin of the reference grid to the top side of the image area */
+    OPJ_UINT32 y0;
+    /** Xsiz: width of the reference grid */
+    OPJ_UINT32 x1;
+    /** Ysiz: height of the reference grid */
+    OPJ_UINT32 y1;
+    /** number of components in the image */
+    OPJ_UINT32 numcomps;
+    /** color space: sRGB, Greyscale or YUV */
+    OPJ_COLOR_SPACE color_space;
+    /** image components */
+    opj_image_comp_t *comps;
+    /** 'restricted' ICC profile */
+    OPJ_BYTE *icc_profile_buf;
+    /** size of ICC profile */
+    OPJ_UINT32 icc_profile_len;
+} opj_image_t;
+
+
+/**
+ * Component parameters structure used by the opj_image_create function
+ * */
+typedef struct opj_image_comptparm {
+    /** XRsiz: horizontal separation of a sample of ith component with respect to the reference grid */
+    OPJ_UINT32 dx;
+    /** YRsiz: vertical separation of a sample of ith component with respect to the reference grid */
+    OPJ_UINT32 dy;
+    /** data width */
+    OPJ_UINT32 w;
+    /** data height */
+    OPJ_UINT32 h;
+    /** x component offset compared to the whole image */
+    OPJ_UINT32 x0;
+    /** y component offset compared to the whole image */
+    OPJ_UINT32 y0;
+    /** precision: number of bits per component per pixel */
+    OPJ_UINT32 prec;
+    /** obsolete: use prec instead */
+    OPJ_DEPRECATED_STRUCT_MEMBER(OPJ_UINT32 bpp, "Use prec instead");
+    /** signed (1) / unsigned (0) */
+    OPJ_UINT32 sgnd;
+} opj_image_cmptparm_t;
+
+
+/*
+==========================================================
+   Information on the JPEG 2000 codestream
+==========================================================
+*/
+/* QUITE EXPERIMENTAL FOR THE MOMENT */
+
+/**
+ * Index structure : Information concerning a packet inside tile
+ * */
+typedef struct opj_packet_info {
+    /** packet start position (including SOP marker if it exists) */
+    OPJ_OFF_T start_pos;
+    /** end of packet header position (including EPH marker if it exists)*/
+    OPJ_OFF_T end_ph_pos;
+    /** packet end position */
+    OPJ_OFF_T end_pos;
+    /** packet distorsion */
+    double disto;
+} opj_packet_info_t;
+
+
+/* UniPG>> */
+/**
+ * Marker structure
+ * */
+typedef struct opj_marker_info {
+    /** marker type */
+    unsigned short int type;
+    /** position in codestream */
+    OPJ_OFF_T pos;
+    /** length, marker val included */
+    int len;
+} opj_marker_info_t;
+/* <<UniPG */
+
+/**
+ * Index structure : Information concerning tile-parts
+*/
+typedef struct opj_tp_info {
+    /** start position of tile part */
+    int tp_start_pos;
+    /** end position of tile part header */
+    int tp_end_header;
+    /** end position of tile part */
+    int tp_end_pos;
+    /** start packet of tile part */
+    int tp_start_pack;
+    /** number of packets of tile part */
+    int tp_numpacks;
+} opj_tp_info_t;
+
+/**
+ * Index structure : information regarding tiles
+*/
+typedef struct opj_tile_info {
+    /** value of thresh for each layer by tile cfr. Marcela   */
+    double *thresh;
+    /** number of tile */
+    int tileno;
+    /** start position */
+    int start_pos;
+    /** end position of the header */
+    int end_header;
+    /** end position */
+    int end_pos;
+    /** precinct number for each resolution level (width) */
+    int pw[33];
+    /** precinct number for each resolution level (height) */
+    int ph[33];
+    /** precinct size (in power of 2), in X for each resolution level */
+    int pdx[33];
+    /** precinct size (in power of 2), in Y for each resolution level */
+    int pdy[33];
+    /** information concerning packets inside tile */
+    opj_packet_info_t *packet;
+    /** add fixed_quality */
+    int numpix;
+    /** add fixed_quality */
+    double distotile;
+    /** number of markers */
+    int marknum;
+    /** list of markers */
+    opj_marker_info_t *marker;
+    /** actual size of markers array */
+    int maxmarknum;
+    /** number of tile parts */
+    int num_tps;
+    /** information concerning tile parts */
+    opj_tp_info_t *tp;
+} opj_tile_info_t;
+
+/**
+ * Index structure of the codestream
+*/
+typedef struct opj_codestream_info {
+    /** maximum distortion reduction on the whole image (add for Marcela) */
+    double D_max;
+    /** packet number */
+    int packno;
+    /** writing the packet in the index with t2_encode_packets */
+    int index_write;
+    /** image width */
+    int image_w;
+    /** image height */
+    int image_h;
+    /** progression order */
+    OPJ_PROG_ORDER prog;
+    /** tile size in x */
+    int tile_x;
+    /** tile size in y */
+    int tile_y;
+    /** */
+    int tile_Ox;
+    /** */
+    int tile_Oy;
+    /** number of tiles in X */
+    int tw;
+    /** number of tiles in Y */
+    int th;
+    /** component numbers */
+    int numcomps;
+    /** number of layer */
+    int numlayers;
+    /** number of decomposition for each component */
+    int *numdecompos;
+    /* UniPG>> */
+    /** number of markers */
+    int marknum;
+    /** list of markers */
+    opj_marker_info_t *marker;
+    /** actual size of markers array */
+    int maxmarknum;
+    /* <<UniPG */
+    /** main header position */
+    int main_head_start;
+    /** main header position */
+    int main_head_end;
+    /** codestream's size */
+    int codestream_size;
+    /** information regarding tiles inside image */
+    opj_tile_info_t *tile;
+} opj_codestream_info_t;
+
+/* <----------------------------------------------------------- */
+/* new output management of the codestream information and index */
+
+/**
+ * Tile-component coding parameters information
+ */
+typedef struct opj_tccp_info {
+    /** component index */
+    OPJ_UINT32 compno;
+    /** coding style */
+    OPJ_UINT32 csty;
+    /** number of resolutions */
+    OPJ_UINT32 numresolutions;
+    /** log2 of code-blocks width */
+    OPJ_UINT32 cblkw;
+    /** log2 of code-blocks height */
+    OPJ_UINT32 cblkh;
+    /** code-block coding style */
+    OPJ_UINT32 cblksty;
+    /** discrete wavelet transform identifier: 0 = 9-7 irreversible, 1 = 5-3 reversible */
+    OPJ_UINT32 qmfbid;
+    /** quantisation style */
+    OPJ_UINT32 qntsty;
+    /** stepsizes used for quantization */
+    OPJ_UINT32 stepsizes_mant[OPJ_J2K_MAXBANDS];
+    /** stepsizes used for quantization */
+    OPJ_UINT32 stepsizes_expn[OPJ_J2K_MAXBANDS];
+    /** number of guard bits */
+    OPJ_UINT32 numgbits;
+    /** Region Of Interest shift */
+    OPJ_INT32 roishift;
+    /** precinct width */
+    OPJ_UINT32 prcw[OPJ_J2K_MAXRLVLS];
+    /** precinct height */
+    OPJ_UINT32 prch[OPJ_J2K_MAXRLVLS];
+}
+opj_tccp_info_t;
+
+/**
+ * Tile coding parameters information
+ */
+typedef struct opj_tile_v2_info {
+
+    /** number (index) of tile */
+    int tileno;
+    /** coding style */
+    OPJ_UINT32 csty;
+    /** progression order */
+    OPJ_PROG_ORDER prg;
+    /** number of layers */
+    OPJ_UINT32 numlayers;
+    /** multi-component transform identifier */
+    OPJ_UINT32 mct;
+
+    /** information concerning tile component parameters*/
+    opj_tccp_info_t *tccp_info;
+
+} opj_tile_info_v2_t;
+
+/**
+ * Information structure about the codestream (FIXME should be expand and enhance)
+ */
+typedef struct opj_codestream_info_v2 {
+    /* Tile info */
+    /** tile origin in x = XTOsiz */
+    OPJ_UINT32 tx0;
+    /** tile origin in y = YTOsiz */
+    OPJ_UINT32 ty0;
+    /** tile size in x = XTsiz */
+    OPJ_UINT32 tdx;
+    /** tile size in y = YTsiz */
+    OPJ_UINT32 tdy;
+    /** number of tiles in X */
+    OPJ_UINT32 tw;
+    /** number of tiles in Y */
+    OPJ_UINT32 th;
+
+    /** number of components*/
+    OPJ_UINT32 nbcomps;
+
+    /** Default information regarding tiles inside image */
+    opj_tile_info_v2_t m_default_tile_info;
+
+    /** information regarding tiles inside image */
+    opj_tile_info_v2_t *tile_info; /* FIXME not used for the moment */
+
+} opj_codestream_info_v2_t;
+
+
+/**
+ * Index structure about a tile part
+ */
+typedef struct opj_tp_index {
+    /** start position */
+    OPJ_OFF_T start_pos;
+    /** end position of the header */
+    OPJ_OFF_T end_header;
+    /** end position */
+    OPJ_OFF_T end_pos;
+
+} opj_tp_index_t;
+
+/**
+ * Index structure about a tile
+ */
+typedef struct opj_tile_index {
+    /** tile index */
+    OPJ_UINT32 tileno;
+
+    /** number of tile parts */
+    OPJ_UINT32 nb_tps;
+    /** current nb of tile part (allocated)*/
+    OPJ_UINT32 current_nb_tps;
+    /** current tile-part index */
+    OPJ_UINT32 current_tpsno;
+    /** information concerning tile parts */
+    opj_tp_index_t *tp_index;
+
+    /* UniPG>> */ /* NOT USED FOR THE MOMENT IN THE V2 VERSION */
+    /** number of markers */
+    OPJ_UINT32 marknum;
+    /** list of markers */
+    opj_marker_info_t *marker;
+    /** actual size of markers array */
+    OPJ_UINT32 maxmarknum;
+    /* <<UniPG */
+
+    /** packet number */
+    OPJ_UINT32 nb_packet;
+    /** information concerning packets inside tile */
+    opj_packet_info_t *packet_index;
+
+} opj_tile_index_t;
+
+/**
+ * Index structure of the codestream (FIXME should be expand and enhance)
+ */
+typedef struct opj_codestream_index {
+    /** main header start position (SOC position) */
+    OPJ_OFF_T main_head_start;
+    /** main header end position (first SOT position) */
+    OPJ_OFF_T main_head_end;
+
+    /** codestream's size */
+    OPJ_UINT64 codestream_size;
+
+    /* UniPG>> */ /* NOT USED FOR THE MOMENT IN THE V2 VERSION */
+    /** number of markers */
+    OPJ_UINT32 marknum;
+    /** list of markers */
+    opj_marker_info_t *marker;
+    /** actual size of markers array */
+    OPJ_UINT32 maxmarknum;
+    /* <<UniPG */
+
+    /** */
+    OPJ_UINT32 nb_of_tiles;
+    /** */
+    opj_tile_index_t *tile_index; /* FIXME not used for the moment */
+
+} opj_codestream_index_t;
+/* -----------------------------------------------------------> */
+
+/*
+==========================================================
+   Metadata from the JP2file
+==========================================================
+*/
+
+/**
+ * Info structure of the JP2 file
+ * EXPERIMENTAL FOR THE MOMENT
+ */
+typedef struct opj_jp2_metadata {
+    /** */
+    OPJ_INT32   not_used;
+
+} opj_jp2_metadata_t;
+
+/**
+ * Index structure of the JP2 file
+ * EXPERIMENTAL FOR THE MOMENT
+ */
+typedef struct opj_jp2_index {
+    /** */
+    OPJ_INT32   not_used;
+
+} opj_jp2_index_t;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+==========================================================
+   openjpeg version
+==========================================================
+*/
+
+/* Get the version of the openjpeg library*/
+OPJ_API const char * OPJ_CALLCONV opj_version(void);
+
+/*
+==========================================================
+   image functions definitions
+==========================================================
+*/
+
+/**
+ * Create an image
+ *
+ * @param numcmpts      number of components
+ * @param cmptparms     components parameters
+ * @param clrspc        image color space
+ * @return returns      a new image structure if successful, returns NULL otherwise
+ * */
+OPJ_API opj_image_t* OPJ_CALLCONV opj_image_create(OPJ_UINT32 numcmpts,
+        opj_image_cmptparm_t *cmptparms, OPJ_COLOR_SPACE clrspc);
+
+/**
+ * Deallocate any resources associated with an image
+ *
+ * @param image         image to be destroyed
+ */
+OPJ_API void OPJ_CALLCONV opj_image_destroy(opj_image_t *image);
+
+/**
+ * Creates an image without allocating memory for the image (used in the new version of the library).
+ *
+ * @param   numcmpts    the number of components
+ * @param   cmptparms   the components parameters
+ * @param   clrspc      the image color space
+ *
+ * @return  a new image structure if successful, NULL otherwise.
+*/
+OPJ_API opj_image_t* OPJ_CALLCONV opj_image_tile_create(OPJ_UINT32 numcmpts,
+        opj_image_cmptparm_t *cmptparms, OPJ_COLOR_SPACE clrspc);
+
+/**
+ * Allocator for opj_image_t->comps[].data
+ * To be paired with opj_image_data_free.
+ *
+ * @param   size    number of bytes to allocate
+ *
+ * @return  a new pointer if successful, NULL otherwise.
+ * @since 2.2.0
+*/
+OPJ_API void* OPJ_CALLCONV opj_image_data_alloc(OPJ_SIZE_T size);
+
+/**
+ * Destructor for opj_image_t->comps[].data
+ * To be paired with opj_image_data_alloc.
+ *
+ * @param   ptr    Pointer to free
+ *
+ * @since 2.2.0
+*/
+OPJ_API void OPJ_CALLCONV opj_image_data_free(void* ptr);
+
+/*
+==========================================================
+   stream functions definitions
+==========================================================
+*/
+
+/**
+ * Creates an abstract stream. This function does nothing except allocating memory and initializing the abstract stream.
+ *
+ * @param   p_is_input      if set to true then the stream will be an input stream, an output stream else.
+ *
+ * @return  a stream object.
+*/
+OPJ_API opj_stream_t* OPJ_CALLCONV opj_stream_default_create(
+    OPJ_BOOL p_is_input);
+
+/**
+ * Creates an abstract stream. This function does nothing except allocating memory and initializing the abstract stream.
+ *
+ * @param   p_buffer_size  FIXME DOC
+ * @param   p_is_input      if set to true then the stream will be an input stream, an output stream else.
+ *
+ * @return  a stream object.
+*/
+OPJ_API opj_stream_t* OPJ_CALLCONV opj_stream_create(OPJ_SIZE_T p_buffer_size,
+        OPJ_BOOL p_is_input);
+
+/**
+ * Destroys a stream created by opj_create_stream. This function does NOT close the abstract stream. If needed the user must
+ * close its own implementation of the stream.
+ *
+ * @param   p_stream    the stream to destroy.
+ */
+OPJ_API void OPJ_CALLCONV opj_stream_destroy(opj_stream_t* p_stream);
+
+/**
+ * Sets the given function to be used as a read function.
+ * @param       p_stream    the stream to modify
+ * @param       p_function  the function to use a read function.
+*/
+OPJ_API void OPJ_CALLCONV opj_stream_set_read_function(opj_stream_t* p_stream,
+        opj_stream_read_fn p_function);
+
+/**
+ * Sets the given function to be used as a write function.
+ * @param       p_stream    the stream to modify
+ * @param       p_function  the function to use a write function.
+*/
+OPJ_API void OPJ_CALLCONV opj_stream_set_write_function(opj_stream_t* p_stream,
+        opj_stream_write_fn p_function);
+
+/**
+ * Sets the given function to be used as a skip function.
+ * @param       p_stream    the stream to modify
+ * @param       p_function  the function to use a skip function.
+*/
+OPJ_API void OPJ_CALLCONV opj_stream_set_skip_function(opj_stream_t* p_stream,
+        opj_stream_skip_fn p_function);
+
+/**
+ * Sets the given function to be used as a seek function, the stream is then seekable,
+ * using SEEK_SET behavior.
+ * @param       p_stream    the stream to modify
+ * @param       p_function  the function to use a skip function.
+*/
+OPJ_API void OPJ_CALLCONV opj_stream_set_seek_function(opj_stream_t* p_stream,
+        opj_stream_seek_fn p_function);
+
+/**
+ * Sets the given data to be used as a user data for the stream.
+ * @param       p_stream    the stream to modify
+ * @param       p_data      the data to set.
+ * @param       p_function  the function to free p_data when opj_stream_destroy() is called.
+*/
+OPJ_API void OPJ_CALLCONV opj_stream_set_user_data(opj_stream_t* p_stream,
+        void * p_data, opj_stream_free_user_data_fn p_function);
+
+/**
+ * Sets the length of the user data for the stream.
+ *
+ * @param p_stream    the stream to modify
+ * @param data_length length of the user_data.
+*/
+OPJ_API void OPJ_CALLCONV opj_stream_set_user_data_length(
+    opj_stream_t* p_stream, OPJ_UINT64 data_length);
+
+/**
+ * Create a stream from a file identified with its filename with default parameters (helper function)
+ * @param fname             the filename of the file to stream
+ * @param p_is_read_stream  whether the stream is a read stream (true) or not (false)
+*/
+OPJ_API opj_stream_t* OPJ_CALLCONV opj_stream_create_default_file_stream(
+    const char *fname, OPJ_BOOL p_is_read_stream);
+
+/** Create a stream from a file identified with its filename with a specific buffer size
+ * @param fname             the filename of the file to stream
+ * @param p_buffer_size     size of the chunk used to stream
+ * @param p_is_read_stream  whether the stream is a read stream (true) or not (false)
+*/
+OPJ_API opj_stream_t* OPJ_CALLCONV opj_stream_create_file_stream(
+    const char *fname,
+    OPJ_SIZE_T p_buffer_size,
+    OPJ_BOOL p_is_read_stream);
+
+/*
+==========================================================
+   event manager functions definitions
+==========================================================
+*/
+/**
+ * Set the info handler use by openjpeg.
+ * @param p_codec       the codec previously initialise
+ * @param p_callback    the callback function which will be used
+ * @param p_user_data   client object where will be returned the message
+*/
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_set_info_handler(opj_codec_t * p_codec,
+        opj_msg_callback p_callback,
+        void * p_user_data);
+/**
+ * Set the warning handler use by openjpeg.
+ * @param p_codec       the codec previously initialise
+ * @param p_callback    the callback function which will be used
+ * @param p_user_data   client object where will be returned the message
+*/
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_set_warning_handler(opj_codec_t * p_codec,
+        opj_msg_callback p_callback,
+        void * p_user_data);
+/**
+ * Set the error handler use by openjpeg.
+ * @param p_codec       the codec previously initialise
+ * @param p_callback    the callback function which will be used
+ * @param p_user_data   client object where will be returned the message
+*/
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_set_error_handler(opj_codec_t * p_codec,
+        opj_msg_callback p_callback,
+        void * p_user_data);
+
+/*
+==========================================================
+   codec functions definitions
+==========================================================
+*/
+
+/**
+ * Creates a J2K/JP2 decompression structure
+ * @param format        Decoder to select
+ *
+ * @return Returns a handle to a decompressor if successful, returns NULL otherwise
+ * */
+OPJ_API opj_codec_t* OPJ_CALLCONV opj_create_decompress(
+    OPJ_CODEC_FORMAT format);
+
+/**
+ * Destroy a decompressor handle
+ *
+ * @param   p_codec         decompressor handle to destroy
+ */
+OPJ_API void OPJ_CALLCONV opj_destroy_codec(opj_codec_t * p_codec);
+
+/**
+ * Read after the codestream if necessary
+ * @param   p_codec         the JPEG2000 codec to read.
+ * @param   p_stream        the JPEG2000 stream.
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_end_decompress(opj_codec_t *p_codec,
+        opj_stream_t *p_stream);
+
+
+/**
+ * Set decoding parameters to default values
+ * @param parameters Decompression parameters
+ */
+OPJ_API void OPJ_CALLCONV opj_set_default_decoder_parameters(
+    opj_dparameters_t *parameters);
+
+/**
+ * Setup the decoder with decompression parameters provided by the user and with the message handler
+ * provided by the user.
+ *
+ * @param p_codec       decompressor handler
+ * @param parameters    decompression parameters
+ *
+ * @return true         if the decoder is correctly set
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_setup_decoder(opj_codec_t *p_codec,
+        opj_dparameters_t *parameters);
+
+/**
+ * Set strict decoding parameter for this decoder.  If strict decoding is enabled, partial bit
+ * streams will fail to decode.  If strict decoding is disabled, the decoder will decode partial
+ * bitstreams as much as possible without erroring
+ *
+ * @param p_codec       decompressor handler
+ * @param strict        OPJ_TRUE to enable strict decoding, OPJ_FALSE to disable
+ *
+ * @return true         if the decoder is correctly set
+ */
+
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_decoder_set_strict_mode(opj_codec_t *p_codec,
+        OPJ_BOOL strict);
+
+/**
+ * Allocates worker threads for the compressor/decompressor.
+ *
+ * By default, only the main thread is used. If this function is not used,
+ * but the OPJ_NUM_THREADS environment variable is set, its value will be
+ * used to initialize the number of threads. The value can be either an integer
+ * number, or "ALL_CPUS". If OPJ_NUM_THREADS is set and this function is called,
+ * this function will override the behaviour of the environment variable.
+ *
+ * This function must be called after opj_setup_decoder() and
+ * before opj_read_header() for the decoding side, or after opj_setup_encoder()
+ * and before opj_start_compress() for the encoding side.
+ *
+ * @param p_codec       decompressor or compressor handler
+ * @param num_threads   number of threads.
+ *
+ * @return OPJ_TRUE     if the function is successful.
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_codec_set_threads(opj_codec_t *p_codec,
+        int num_threads);
+
+/**
+ * Decodes an image header.
+ *
+ * @param   p_stream        the jpeg2000 stream.
+ * @param   p_codec         the jpeg2000 codec to read.
+ * @param   p_image         the image structure initialized with the characteristics of encoded image.
+ *
+ * @return true             if the main header of the codestream and the JP2 header is correctly read.
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_read_header(opj_stream_t *p_stream,
+        opj_codec_t *p_codec,
+        opj_image_t **p_image);
+
+
+/** Restrict the number of components to decode.
+ *
+ * This function should be called after opj_read_header().
+ *
+ * This function enables to restrict the set of decoded components to the
+ * specified indices.
+ * Note that the current implementation (apply_color_transforms == OPJ_FALSE)
+ * is such that neither the multi-component transform at codestream level,
+ * nor JP2 channel transformations will be applied.
+ * Consequently the indices are relative to the codestream.
+ *
+ * Note: opj_decode_tile_data() should not be used together with opj_set_decoded_components().
+ *
+ * @param   p_codec         the jpeg2000 codec to read.
+ * @param   numcomps        Size of the comps_indices array.
+ * @param   comps_indices   Array of numcomps values representing the indices
+ *                          of the components to decode (relative to the
+ *                          codestream, starting at 0)
+ * @param   apply_color_transforms Whether multi-component transform at codestream level
+ *                                 or JP2 channel transformations should be applied.
+ *                                 Currently this parameter should be set to OPJ_FALSE.
+ *                                 Setting it to OPJ_TRUE will result in an error.
+ *
+ * @return OPJ_TRUE         in case of success.
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_set_decoded_components(opj_codec_t *p_codec,
+        OPJ_UINT32 numcomps,
+        const OPJ_UINT32* comps_indices,
+        OPJ_BOOL apply_color_transforms);
+
+/**
+ * Sets the given area to be decoded. This function should be called right after opj_read_header and before any tile header reading.
+ *
+ * The coordinates passed to this function should be expressed in the reference grid,
+ * that is to say at the highest resolution level, even if requesting the image at lower
+ * resolution levels.
+ *
+ * Generally opj_set_decode_area() should be followed by opj_decode(), and the
+ * codec cannot be re-used.
+ * In the particular case of an image made of a single tile, several sequences of
+ * calls to opoj_set_decode_area() and opj_decode() are allowed, and will bring
+ * performance improvements when reading an image by chunks.
+ *
+ * @param   p_codec         the jpeg2000 codec.
+ * @param   p_image         the decoded image previously set by opj_read_header
+ * @param   p_start_x       the left position of the rectangle to decode (in image coordinates).
+ * @param   p_end_x         the right position of the rectangle to decode (in image coordinates).
+ * @param   p_start_y       the up position of the rectangle to decode (in image coordinates).
+ * @param   p_end_y         the bottom position of the rectangle to decode (in image coordinates).
+ *
+ * @return  true            if the area could be set.
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_set_decode_area(opj_codec_t *p_codec,
+        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);
+
+/**
+ * Decode an image from a JPEG-2000 codestream
+ *
+ * @param p_decompressor    decompressor handle
+ * @param p_stream          Input buffer stream
+ * @param p_image           the decoded image
+ * @return                  true if success, otherwise false
+ * */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_decode(opj_codec_t *p_decompressor,
+        opj_stream_t *p_stream,
+        opj_image_t *p_image);
+
+/**
+ * Get the decoded tile from the codec
+ *
+ * @param   p_codec         the jpeg2000 codec.
+ * @param   p_stream        input stream
+ * @param   p_image         output image
+ * @param   tile_index      index of the tile which will be decode
+ *
+ * @return                  true if success, otherwise false
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_get_decoded_tile(opj_codec_t *p_codec,
+        opj_stream_t *p_stream,
+        opj_image_t *p_image,
+        OPJ_UINT32 tile_index);
+
+/**
+ * Set the resolution factor of the decoded image
+ * @param   p_codec         the jpeg2000 codec.
+ * @param   res_factor      resolution factor to set
+ *
+ * @return                  true if success, otherwise false
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_set_decoded_resolution_factor(
+    opj_codec_t *p_codec, OPJ_UINT32 res_factor);
+
+/**
+ * Writes a tile with the given data.
+ *
+ * @param   p_codec             the jpeg2000 codec.
+ * @param   p_tile_index        the index of the tile to write. At the moment, the tiles must be written from 0 to n-1 in sequence.
+ * @param   p_data              pointer to the data to write. Data is arranged in sequence, data_comp0, then data_comp1, then ... NO INTERLEAVING should be set.
+ * @param   p_data_size         this value os used to make sure the data being written is correct. The size must be equal to the sum for each component of
+ *                              tile_width * tile_height * component_size. component_size can be 1,2 or 4 bytes, depending on the precision of the given component.
+ * @param   p_stream            the stream to write data to.
+ *
+ * @return  true if the data could be written.
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_write_tile(opj_codec_t *p_codec,
+        OPJ_UINT32 p_tile_index,
+        OPJ_BYTE * p_data,
+        OPJ_UINT32 p_data_size,
+        opj_stream_t *p_stream);
+
+/**
+ * Reads a tile header. This function is compulsory and allows one to know the size of the tile that will be decoded.
+ * The user may need to refer to the image got by opj_read_header to understand the size being taken by the tile.
+ *
+ * @param   p_codec         the jpeg2000 codec.
+ * @param   p_tile_index    pointer to a value that will hold the index of the tile being decoded, in case of success.
+ * @param   p_data_size     pointer to a value that will hold the maximum size of the decoded data, in case of success. In case
+ *                          of truncated codestreams, the actual number of bytes decoded may be lower. The computation of the size is the same
+ *                          as depicted in opj_write_tile.
+ * @param   p_tile_x0       pointer to a value that will hold the x0 pos of the tile (in the image).
+ * @param   p_tile_y0       pointer to a value that will hold the y0 pos of the tile (in the image).
+ * @param   p_tile_x1       pointer to a value that will hold the x1 pos of the tile (in the image).
+ * @param   p_tile_y1       pointer to a value that will hold the y1 pos of the tile (in the image).
+ * @param   p_nb_comps      pointer to a value that will hold the number of components in the tile.
+ * @param   p_should_go_on  pointer to a boolean that will hold the fact that the decoding should go on. In case the
+ *                          codestream is over at the time of the call, the value will be set to false. The user should then stop
+ *                          the decoding.
+ * @param   p_stream        the stream to decode.
+ * @return  true            if the tile header could be decoded. In case the decoding should end, the returned value is still true.
+ *                          returning false may be the result of a shortage of memory or an internal error.
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_read_tile_header(opj_codec_t *p_codec,
+        opj_stream_t * p_stream,
+        OPJ_UINT32 * p_tile_index,
+        OPJ_UINT32 * p_data_size,
+        OPJ_INT32 * p_tile_x0, OPJ_INT32 * p_tile_y0,
+        OPJ_INT32 * p_tile_x1, OPJ_INT32 * p_tile_y1,
+        OPJ_UINT32 * p_nb_comps,
+        OPJ_BOOL * p_should_go_on);
+
+/**
+ * Reads a tile data. This function is compulsory and allows one to decode tile data. opj_read_tile_header should be called before.
+ * The user may need to refer to the image got by opj_read_header to understand the size being taken by the tile.
+ *
+ * Note: opj_decode_tile_data() should not be used together with opj_set_decoded_components().
+ *
+ * @param   p_codec         the jpeg2000 codec.
+ * @param   p_tile_index    the index of the tile being decoded, this should be the value set by opj_read_tile_header.
+ * @param   p_data          pointer to a memory block that will hold the decoded data.
+ * @param   p_data_size     size of p_data. p_data_size should be bigger or equal to the value set by opj_read_tile_header.
+ * @param   p_stream        the stream to decode.
+ *
+ * @return  true            if the data could be decoded.
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_decode_tile_data(opj_codec_t *p_codec,
+        OPJ_UINT32 p_tile_index,
+        OPJ_BYTE * p_data,
+        OPJ_UINT32 p_data_size,
+        opj_stream_t *p_stream);
+
+/* COMPRESSION FUNCTIONS*/
+
+/**
+ * Creates a J2K/JP2 compression structure
+ * @param   format      Coder to select
+ * @return              Returns a handle to a compressor if successful, returns NULL otherwise
+ */
+OPJ_API opj_codec_t* OPJ_CALLCONV opj_create_compress(OPJ_CODEC_FORMAT format);
+
+/**
+Set encoding parameters to default values, that means :
+<ul>
+<li>Lossless
+<li>1 tile
+<li>Size of precinct : 2^15 x 2^15 (means 1 precinct)
+<li>Size of code-block : 64 x 64
+<li>Number of resolutions: 6
+<li>No SOP marker in the codestream
+<li>No EPH marker in the codestream
+<li>No sub-sampling in x or y direction
+<li>No mode switch activated
+<li>Progression order: LRCP
+<li>No index file
+<li>No ROI upshifted
+<li>No offset of the origin of the image
+<li>No offset of the origin of the tiles
+<li>Reversible DWT 5-3
+</ul>
+@param parameters Compression parameters
+*/
+OPJ_API void OPJ_CALLCONV opj_set_default_encoder_parameters(
+    opj_cparameters_t *parameters);
+
+/**
+ * Setup the encoder parameters using the current image and using user parameters.
+ * @param p_codec       Compressor handle
+ * @param parameters    Compression parameters
+ * @param image         Input filled image
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_setup_encoder(opj_codec_t *p_codec,
+        opj_cparameters_t *parameters,
+        opj_image_t *image);
+
+
+/**
+ * Specify extra options for the encoder.
+ *
+ * This may be called after opj_setup_encoder() and before opj_start_compress()
+ *
+ * This is the way to add new options in a fully ABI compatible way, without
+ * extending the opj_cparameters_t structure.
+ *
+ * Currently supported options are:
+ * <ul>
+ * <li>PLT=YES/NO. Defaults to NO. If set to YES, PLT marker segments,
+ *     indicating the length of each packet in the tile-part header, will be
+ *     written. Since 2.4.0</li>
+ * <li>TLM=YES/NO. Defaults to NO (except for Cinema and IMF profiles).
+ *     If set to YES, TLM marker segments, indicating the length of each
+ *     tile-part part will be written. Since 2.4.0</li>
+ * <li>GUARD_BITS=value. Number of guard bits in [0,7] range. Default value is 2.
+ *     1 may be used sometimes (like in SMPTE DCP Bv2.1 Application Profile for 2K images).
+ *     Since 2.5.0</li>
+ * </ul>
+ *
+ * @param p_codec       Compressor handle
+ * @param p_options     Compression options. This should be a NULL terminated
+ *                      array of strings. Each string is of the form KEY=VALUE.
+ *
+ * @return OPJ_TRUE in case of success.
+ * @since 2.4.0
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_encoder_set_extra_options(
+    opj_codec_t *p_codec,
+    const char* const* p_options);
+
+/**
+ * Start to compress the current image.
+ * @param p_codec       Compressor handle
+ * @param p_image       Input filled image
+ * @param p_stream      Input stgream
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_start_compress(opj_codec_t *p_codec,
+        opj_image_t * p_image,
+        opj_stream_t *p_stream);
+
+/**
+ * End to compress the current image.
+ * @param p_codec       Compressor handle
+ * @param p_stream      Input stgream
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_end_compress(opj_codec_t *p_codec,
+        opj_stream_t *p_stream);
+
+/**
+ * Encode an image into a JPEG-2000 codestream
+ * @param p_codec       compressor handle
+ * @param p_stream      Output buffer stream
+ *
+ * @return              Returns true if successful, returns false otherwise
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_encode(opj_codec_t *p_codec,
+        opj_stream_t *p_stream);
+/*
+==========================================================
+   codec output functions definitions
+==========================================================
+*/
+/* EXPERIMENTAL FUNCTIONS FOR NOW, USED ONLY IN J2K_DUMP*/
+
+/**
+Destroy Codestream information after compression or decompression
+@param cstr_info Codestream information structure
+*/
+OPJ_API void OPJ_CALLCONV opj_destroy_cstr_info(opj_codestream_info_v2_t
+        **cstr_info);
+
+
+/**
+ * Dump the codec information into the output stream
+ *
+ * @param   p_codec         the jpeg2000 codec.
+ * @param   info_flag       type of information dump.
+ * @param   output_stream   output stream where dump the information gotten from the codec.
+ *
+ */
+OPJ_API void OPJ_CALLCONV opj_dump_codec(opj_codec_t *p_codec,
+        OPJ_INT32 info_flag,
+        FILE* output_stream);
+
+/**
+ * Get the codestream information from the codec
+ *
+ * @param   p_codec         the jpeg2000 codec.
+ *
+ * @return                  a pointer to a codestream information structure.
+ *
+ */
+OPJ_API opj_codestream_info_v2_t* OPJ_CALLCONV opj_get_cstr_info(
+    opj_codec_t *p_codec);
+
+/**
+ * Get the codestream index from the codec
+ *
+ * @param   p_codec         the jpeg2000 codec.
+ *
+ * @return                  a pointer to a codestream index structure.
+ *
+ */
+OPJ_API opj_codestream_index_t * OPJ_CALLCONV opj_get_cstr_index(
+    opj_codec_t *p_codec);
+
+OPJ_API void OPJ_CALLCONV opj_destroy_cstr_index(opj_codestream_index_t
+        **p_cstr_index);
+
+
+/**
+ * Get the JP2 file information from the codec FIXME
+ *
+ * @param   p_codec         the jpeg2000 codec.
+ *
+ * @return                  a pointer to a JP2 metadata structure.
+ *
+ */
+OPJ_API opj_jp2_metadata_t* OPJ_CALLCONV opj_get_jp2_metadata(
+    opj_codec_t *p_codec);
+
+/**
+ * Get the JP2 file index from the codec FIXME
+ *
+ * @param   p_codec         the jpeg2000 codec.
+ *
+ * @return                  a pointer to a JP2 index structure.
+ *
+ */
+OPJ_API opj_jp2_index_t* OPJ_CALLCONV opj_get_jp2_index(opj_codec_t *p_codec);
+
+
+/*
+==========================================================
+   MCT functions
+==========================================================
+*/
+
+/**
+ * Sets the MCT matrix to use.
+ *
+ * @param   parameters      the parameters to change.
+ * @param   pEncodingMatrix the encoding matrix.
+ * @param   p_dc_shift      the dc shift coefficients to use.
+ * @param   pNbComp         the number of components of the image.
+ *
+ * @return  true if the parameters could be set.
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_set_MCT(opj_cparameters_t *parameters,
+        OPJ_FLOAT32 * pEncodingMatrix,
+        OPJ_INT32 * p_dc_shift,
+        OPJ_UINT32 pNbComp);
+
+/*
+==========================================================
+   Thread functions
+==========================================================
+*/
+
+/** Returns if the library is built with thread support.
+ * OPJ_TRUE if mutex, condition, thread, thread pool are available.
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_has_thread_support(void);
+
+/** Return the number of virtual CPUs */
+OPJ_API int OPJ_CALLCONV opj_get_num_cpus(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OPENJPEG_H */
diff --git a/third_party/libopenjpeg20/opj_clock.c b/third_party/libopenjpeg/opj_clock.c
similarity index 100%
rename from third_party/libopenjpeg20/opj_clock.c
rename to third_party/libopenjpeg/opj_clock.c
diff --git a/third_party/libopenjpeg20/opj_clock.h b/third_party/libopenjpeg/opj_clock.h
similarity index 100%
rename from third_party/libopenjpeg20/opj_clock.h
rename to third_party/libopenjpeg/opj_clock.h
diff --git a/third_party/libopenjpeg/opj_codec.h b/third_party/libopenjpeg/opj_codec.h
new file mode 100644
index 0000000..7cff670
--- /dev/null
+++ b/third_party/libopenjpeg/opj_codec.h
@@ -0,0 +1,179 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef OPJ_CODEC_H
+#define OPJ_CODEC_H
+/**
+@file opj_codec.h
+*/
+
+
+/**
+ * Main codec handler used for compression or decompression.
+ */
+typedef struct opj_codec_private {
+    /** FIXME DOC */
+    union {
+        /**
+         * Decompression handler.
+         */
+        struct opj_decompression {
+            /** Main header reading function handler */
+            OPJ_BOOL(*opj_read_header)(struct opj_stream_private * cio,
+                                       void * p_codec,
+                                       opj_image_t **p_image,
+                                       struct opj_event_mgr * p_manager);
+
+            /** Decoding function */
+            OPJ_BOOL(*opj_decode)(void * p_codec,
+                                  struct opj_stream_private * p_cio,
+                                  opj_image_t * p_image,
+                                  struct opj_event_mgr * p_manager);
+
+            /** FIXME DOC */
+            OPJ_BOOL(*opj_read_tile_header)(void * p_codec,
+                                            OPJ_UINT32 * p_tile_index,
+                                            OPJ_UINT32 * p_data_size,
+                                            OPJ_INT32 * p_tile_x0,
+                                            OPJ_INT32 * p_tile_y0,
+                                            OPJ_INT32 * p_tile_x1,
+                                            OPJ_INT32 * p_tile_y1,
+                                            OPJ_UINT32 * p_nb_comps,
+                                            OPJ_BOOL * p_should_go_on,
+                                            struct opj_stream_private * p_cio,
+                                            struct opj_event_mgr * p_manager);
+
+            /** FIXME DOC */
+            OPJ_BOOL(*opj_decode_tile_data)(void * p_codec,
+                                            OPJ_UINT32 p_tile_index,
+                                            OPJ_BYTE * p_data,
+                                            OPJ_UINT32 p_data_size,
+                                            struct opj_stream_private * p_cio,
+                                            struct opj_event_mgr * p_manager);
+
+            /** Reading function used after codestream if necessary */
+            OPJ_BOOL(* opj_end_decompress)(void *p_codec,
+                                           struct opj_stream_private * cio,
+                                           struct opj_event_mgr * p_manager);
+
+            /** Codec destroy function handler */
+            void (*opj_destroy)(void * p_codec);
+
+            /** Setup decoder function handler */
+            void (*opj_setup_decoder)(void * p_codec, opj_dparameters_t * p_param);
+
+            /** Strict mode function handler */
+            void (*opj_decoder_set_strict_mode)(void * p_codec, OPJ_BOOL strict);
+
+            /** Set decode area function handler */
+            OPJ_BOOL(*opj_set_decode_area)(void * p_codec,
+                                           opj_image_t * p_image,
+                                           OPJ_INT32 p_start_x,
+                                           OPJ_INT32 p_end_x,
+                                           OPJ_INT32 p_start_y,
+                                           OPJ_INT32 p_end_y,
+                                           struct opj_event_mgr * p_manager);
+
+            /** Get tile function */
+            OPJ_BOOL(*opj_get_decoded_tile)(void *p_codec,
+                                            opj_stream_private_t * p_cio,
+                                            opj_image_t *p_image,
+                                            struct opj_event_mgr * p_manager,
+                                            OPJ_UINT32 tile_index);
+
+            /** Set the decoded resolution factor */
+            OPJ_BOOL(*opj_set_decoded_resolution_factor)(void * p_codec,
+                    OPJ_UINT32 res_factor,
+                    opj_event_mgr_t * p_manager);
+
+            /** Set the decoded components */
+            OPJ_BOOL(*opj_set_decoded_components)(void * p_codec,
+                                                  OPJ_UINT32 num_comps,
+                                                  const OPJ_UINT32* comps_indices,
+                                                  opj_event_mgr_t * p_manager);
+        } m_decompression;
+
+        /**
+         * Compression handler. FIXME DOC
+         */
+        struct opj_compression {
+            OPJ_BOOL(* opj_start_compress)(void *p_codec,
+                                           struct opj_stream_private * cio,
+                                           struct opj_image * p_image,
+                                           struct opj_event_mgr * p_manager);
+
+            OPJ_BOOL(* opj_encode)(void * p_codec,
+                                   struct opj_stream_private *p_cio,
+                                   struct opj_event_mgr * p_manager);
+
+            OPJ_BOOL(* opj_write_tile)(void * p_codec,
+                                       OPJ_UINT32 p_tile_index,
+                                       OPJ_BYTE * p_data,
+                                       OPJ_UINT32 p_data_size,
+                                       struct opj_stream_private * p_cio,
+                                       struct opj_event_mgr * p_manager);
+
+            OPJ_BOOL(* opj_end_compress)(void * p_codec,
+                                         struct opj_stream_private * p_cio,
+                                         struct opj_event_mgr * p_manager);
+
+            void (* opj_destroy)(void * p_codec);
+
+            OPJ_BOOL(* opj_setup_encoder)(void * p_codec,
+                                          opj_cparameters_t * p_param,
+                                          struct opj_image * p_image,
+                                          struct opj_event_mgr * p_manager);
+
+            OPJ_BOOL(* opj_encoder_set_extra_options)(void * p_codec,
+                    const char* const* p_options,
+                    struct opj_event_mgr * p_manager);
+
+        } m_compression;
+    } m_codec_data;
+    /** FIXME DOC*/
+    void * m_codec;
+    /** Event handler */
+    opj_event_mgr_t m_event_mgr;
+    /** Flag to indicate if the codec is used to decode or encode*/
+    OPJ_BOOL is_decompressor;
+    void (*opj_dump_codec)(void * p_codec, OPJ_INT32 info_flag,
+                           FILE* output_stream);
+    opj_codestream_info_v2_t* (*opj_get_codec_info)(void* p_codec);
+    opj_codestream_index_t* (*opj_get_codec_index)(void* p_codec);
+
+    /** Set number of threads */
+    OPJ_BOOL(*opj_set_threads)(void * p_codec, OPJ_UINT32 num_threads);
+}
+opj_codec_private_t;
+
+
+#endif /* OPJ_CODEC_H */
+
diff --git a/third_party/libopenjpeg/opj_common.h b/third_party/libopenjpeg/opj_common.h
new file mode 100644
index 0000000..ee8adf4
--- /dev/null
+++ b/third_party/libopenjpeg/opj_common.h
@@ -0,0 +1,47 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef OPJ_COMMMON_H
+#define OPJ_COMMMON_H
+
+/*
+ ==========================================================
+   Common constants shared among several modules
+ ==========================================================
+*/
+#define OPJ_COMMON_CBLK_DATA_EXTRA        2    /**< Margin for a fake FFFF marker */
+
+
+#define OPJ_COMP_PARAM_DEFAULT_CBLOCKW        64
+#define OPJ_COMP_PARAM_DEFAULT_CBLOCKH        64
+#define OPJ_COMP_PARAM_DEFAULT_PROG_ORDER     OPJ_LRCP
+#define OPJ_COMP_PARAM_DEFAULT_NUMRESOLUTION  6
+
+#endif /* OPJ_COMMMON_H */
diff --git a/third_party/libopenjpeg/opj_config.h b/third_party/libopenjpeg/opj_config.h
new file mode 100644
index 0000000..794c926
--- /dev/null
+++ b/third_party/libopenjpeg/opj_config.h
@@ -0,0 +1,16 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+/* create opj_config.h for CMake */
+#define OPJ_HAVE_STDINT_H 		1
+
+/*--------------------------------------------------------------------------*/
+/* OpenJPEG Versioning                                                      */
+
+/* Version number. */
+#define OPJ_VERSION_MAJOR 2
+#define OPJ_VERSION_MINOR 4
+#define OPJ_VERSION_BUILD 0
diff --git a/third_party/libopenjpeg/opj_config_private.h b/third_party/libopenjpeg/opj_config_private.h
new file mode 100644
index 0000000..1964577
--- /dev/null
+++ b/third_party/libopenjpeg/opj_config_private.h
@@ -0,0 +1,37 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+/* create opj_config_private.h for CMake */
+#define OPJ_HAVE_INTTYPES_H 	1
+
+#define OPJ_PACKAGE_VERSION "2.4.0"
+
+/* Not used by openjp2*/
+/*#define HAVE_MEMORY_H 1*/
+/*#define HAVE_STDLIB_H 1*/
+/*#define HAVE_STRINGS_H 1*/
+/*#define HAVE_STRING_H 1*/
+/*#define HAVE_SYS_STAT_H 1*/
+/*#define HAVE_SYS_TYPES_H 1 */
+/*#define HAVE_UNISTD_H 1*/
+
+/* #undef _LARGEFILE_SOURCE */
+/* #undef _LARGE_FILES */
+/* #undef _FILE_OFFSET_BITS */
+#define OPJ_HAVE_FSEEKO ON
+
+/* Byte order.  */
+/* All compilers that support Mac OS X define either __BIG_ENDIAN__ or
+__LITTLE_ENDIAN__ to match the endianness of the architecture being
+compiled for. This is not necessarily the same as the architecture of the
+machine doing the building. In order to support Universal Binaries on
+Mac OS X, we prefer those defines to decide the endianness.
+On other platforms we use the result of the TRY_RUN. */
+#if !defined(__APPLE__)
+/* #undef OPJ_BIG_ENDIAN */
+#elif defined(__BIG_ENDIAN__)
+# define OPJ_BIG_ENDIAN
+#endif
diff --git a/third_party/libopenjpeg/opj_includes.h b/third_party/libopenjpeg/opj_includes.h
new file mode 100644
index 0000000..43c00a5
--- /dev/null
+++ b/third_party/libopenjpeg/opj_includes.h
@@ -0,0 +1,264 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
+ * Copyright (c) 2012, CS Systemes d'Information, France
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef OPJ_INCLUDES_H
+#define OPJ_INCLUDES_H
+
+/*
+ * This must be included before any system headers,
+ * since they can react to macro defined there
+ */
+#include "opj_config_private.h"
+
+/*
+ ==========================================================
+   Standard includes used by the library
+ ==========================================================
+*/
+#include <memory.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <float.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <assert.h>
+#include <limits.h>
+
+/*
+  Use fseeko() and ftello() if they are available since they use
+  'off_t' rather than 'long'.  It is wrong to use fseeko() and
+  ftello() only on systems with special LFS support since some systems
+  (e.g. FreeBSD) support a 64-bit off_t by default.
+*/
+#if defined(OPJ_HAVE_FSEEKO) && !defined(fseek)
+#  define fseek  fseeko
+#  define ftell  ftello
+#endif
+
+
+#if defined(WIN32) && !defined(Windows95) && !defined(__BORLANDC__) && \
+  !(defined(_MSC_VER) && _MSC_VER < 1400) && \
+  !(defined(__MINGW32__) && __MSVCRT_VERSION__ < 0x800)
+/*
+  Windows '95 and Borland C do not support _lseeki64
+  Visual Studio does not support _fseeki64 and _ftelli64 until the 2005 release.
+  Without these interfaces, files over 2GB in size are not supported for Windows.
+*/
+#  define OPJ_FSEEK(stream,offset,whence) _fseeki64(stream,/* __int64 */ offset,whence)
+#  define OPJ_FSTAT(fildes,stat_buff) _fstati64(fildes,/* struct _stati64 */ stat_buff)
+#  define OPJ_FTELL(stream) /* __int64 */ _ftelli64(stream)
+#  define OPJ_STAT_STRUCT_T struct _stati64
+#  define OPJ_STAT(path,stat_buff) _stati64(path,/* struct _stati64 */ stat_buff)
+#else
+#  define OPJ_FSEEK(stream,offset,whence) fseek(stream,offset,whence)
+#  define OPJ_FSTAT(fildes,stat_buff) fstat(fildes,stat_buff)
+#  define OPJ_FTELL(stream) ftell(stream)
+#  define OPJ_STAT_STRUCT_T struct stat
+#  define OPJ_STAT(path,stat_buff) stat(path,stat_buff)
+#endif
+
+
+/*
+ ==========================================================
+   OpenJPEG interface
+ ==========================================================
+ */
+#include "openjpeg.h"
+
+/*
+ ==========================================================
+   OpenJPEG modules
+ ==========================================================
+*/
+
+/* Are restricted pointers available? (C99) */
+#if (__STDC_VERSION__ >= 199901L)
+#define OPJ_RESTRICT restrict
+#else
+/* Not a C99 compiler */
+#if defined(__GNUC__)
+#define OPJ_RESTRICT __restrict__
+
+/*
+  vc14 (2015) outputs wrong results.
+  Need to check OPJ_RESTRICT usage (or a bug in vc14)
+    #elif defined(_MSC_VER) && (_MSC_VER >= 1400)
+        #define OPJ_RESTRICT __restrict
+*/
+#else
+#define OPJ_RESTRICT /* restrict */
+#endif
+#endif
+
+#ifdef __has_attribute
+#if __has_attribute(no_sanitize)
+#define OPJ_NOSANITIZE(kind) __attribute__((no_sanitize(kind)))
+#endif
+#endif
+#ifndef OPJ_NOSANITIZE
+#define OPJ_NOSANITIZE(kind)
+#endif
+
+
+/* MSVC before 2013 and Borland C do not have lrintf */
+#if defined(_MSC_VER)
+#include <intrin.h>
+static INLINE long opj_lrintf(float f)
+{
+#ifdef _M_X64
+    return _mm_cvt_ss2si(_mm_load_ss(&f));
+
+    /* commented out line breaks many tests */
+    /* return (long)((f>0.0f) ? (f + 0.5f):(f -0.5f)); */
+#elif defined(_M_IX86)
+    int i;
+    _asm{
+        fld f
+        fistp i
+    };
+
+    return i;
+#else
+    return (long)((f>0.0f) ? (f + 0.5f) : (f - 0.5f));
+#endif
+}
+#elif defined(__BORLANDC__)
+static INLINE long opj_lrintf(float f)
+{
+#ifdef _M_X64
+    return (long)((f > 0.0f) ? (f + 0.5f) : (f - 0.5f));
+#else
+    int i;
+
+    _asm {
+        fld f
+        fistp i
+    };
+
+    return i;
+#endif
+}
+#else
+static INLINE long opj_lrintf(float f)
+{
+    return lrintf(f);
+}
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER < 1400)
+#define vsnprintf _vsnprintf
+#endif
+
+/* MSVC x86 is really bad at doing int64 = int32 * int32 on its own. Use intrinsic. */
+#if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(__INTEL_COMPILER) && defined(_M_IX86)
+#   include <intrin.h>
+#   pragma intrinsic(__emul)
+#endif
+
+/* Apparently Visual Studio doesn't define __SSE__ / __SSE2__ macros */
+#if defined(_M_X64)
+/* Intel 64bit support SSE and SSE2 */
+#   ifndef __SSE__
+#       define __SSE__ 1
+#   endif
+#   ifndef __SSE2__
+#       define __SSE2__ 1
+#   endif
+#endif
+
+/* For x86, test the value of the _M_IX86_FP macro. */
+/* See https://msdn.microsoft.com/en-us/library/b0084kay.aspx */
+#if defined(_M_IX86_FP)
+#   if _M_IX86_FP >= 1
+#       ifndef __SSE__
+#           define __SSE__ 1
+#       endif
+#   endif
+#   if _M_IX86_FP >= 2
+#       ifndef __SSE2__
+#           define __SSE2__ 1
+#       endif
+#   endif
+#endif
+
+/* Type to use for bit-fields in internal headers */
+typedef unsigned int OPJ_BITFIELD;
+
+#define OPJ_UNUSED(x) (void)x
+
+#include "opj_inttypes.h"
+#include "opj_malloc.h"
+#include "event.h"
+#include "function_list.h"
+#include "bio.h"
+#include "cio.h"
+
+#include "thread.h"
+#include "tls_keys.h"
+
+#include "image.h"
+#include "invert.h"
+#include "j2k.h"
+#include "jp2.h"
+
+#include "mqc.h"
+#include "bio.h"
+
+#include "pi.h"
+#include "tgt.h"
+#include "tcd.h"
+#include "t1.h"
+#include "dwt.h"
+#include "t2.h"
+#include "mct.h"
+#include "opj_intmath.h"
+#include "sparse_array.h"
+
+#ifdef USE_JPIP
+#include "cidx_manager.h"
+#include "indexbox_manager.h"
+#endif
+
+/* JPWL>> */
+#ifdef USE_JPWL
+#include "openjpwl/jpwl.h"
+#endif /* USE_JPWL */
+/* <<JPWL */
+
+/* V2 */
+#include "opj_codec.h"
+
+
+#endif /* OPJ_INCLUDES_H */
diff --git a/third_party/libopenjpeg/opj_intmath.h b/third_party/libopenjpeg/opj_intmath.h
new file mode 100644
index 0000000..1b0c9d0
--- /dev/null
+++ b/third_party/libopenjpeg/opj_intmath.h
@@ -0,0 +1,322 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef OPJ_INTMATH_H
+#define OPJ_INTMATH_H
+/**
+@file opj_intmath.h
+@brief Implementation of operations on integers (INT)
+
+The functions in OPJ_INTMATH.H have for goal to realize operations on integers.
+*/
+
+/** @defgroup OPJ_INTMATH OPJ_INTMATH - Implementation of operations on integers */
+/*@{*/
+
+/** @name Exported functions (see also openjpeg.h) */
+/*@{*/
+/* ----------------------------------------------------------------------- */
+/**
+Get the minimum of two integers
+@return Returns a if a < b else b
+*/
+static INLINE OPJ_INT32 opj_int_min(OPJ_INT32 a, OPJ_INT32 b)
+{
+    return a < b ? a : b;
+}
+
+/**
+Get the minimum of two integers
+@return Returns a if a < b else b
+*/
+static INLINE OPJ_UINT32 opj_uint_min(OPJ_UINT32 a, OPJ_UINT32 b)
+{
+    return a < b ? a : b;
+}
+
+/**
+Get the maximum of two integers
+@return Returns a if a > b else b
+*/
+static INLINE OPJ_INT32 opj_int_max(OPJ_INT32 a, OPJ_INT32 b)
+{
+    return (a > b) ? a : b;
+}
+
+/**
+Get the maximum of two integers
+@return Returns a if a > b else b
+*/
+static INLINE OPJ_UINT32 opj_uint_max(OPJ_UINT32  a, OPJ_UINT32  b)
+{
+    return (a > b) ? a : b;
+}
+
+/**
+ Get the saturated sum of two unsigned integers
+ @return Returns saturated sum of a+b
+ */
+static INLINE OPJ_UINT32 opj_uint_adds(OPJ_UINT32 a, OPJ_UINT32 b)
+{
+    OPJ_UINT64 sum = (OPJ_UINT64)a + (OPJ_UINT64)b;
+    return (OPJ_UINT32)(-(OPJ_INT32)(sum >> 32)) | (OPJ_UINT32)sum;
+}
+
+/**
+ Get the saturated difference of two unsigned integers
+ @return Returns saturated sum of a-b
+ */
+static INLINE OPJ_UINT32 opj_uint_subs(OPJ_UINT32 a, OPJ_UINT32 b)
+{
+    return (a >= b) ? a - b : 0;
+}
+
+/**
+Clamp an integer inside an interval
+@return
+<ul>
+<li>Returns a if (min < a < max)
+<li>Returns max if (a > max)
+<li>Returns min if (a < min)
+</ul>
+*/
+static INLINE OPJ_INT32 opj_int_clamp(OPJ_INT32 a, OPJ_INT32 min,
+                                      OPJ_INT32 max)
+{
+    if (a < min) {
+        return min;
+    }
+    if (a > max) {
+        return max;
+    }
+    return a;
+}
+
+/**
+Clamp an integer inside an interval
+@return
+<ul>
+<li>Returns a if (min < a < max)
+<li>Returns max if (a > max)
+<li>Returns min if (a < min)
+</ul>
+*/
+static INLINE OPJ_INT64 opj_int64_clamp(OPJ_INT64 a, OPJ_INT64 min,
+                                        OPJ_INT64 max)
+{
+    if (a < min) {
+        return min;
+    }
+    if (a > max) {
+        return max;
+    }
+    return a;
+}
+
+/**
+@return Get absolute value of integer
+*/
+static INLINE OPJ_INT32 opj_int_abs(OPJ_INT32 a)
+{
+    return a < 0 ? -a : a;
+}
+/**
+Divide an integer and round upwards
+@return Returns a divided by b
+*/
+static INLINE OPJ_INT32 opj_int_ceildiv(OPJ_INT32 a, OPJ_INT32 b)
+{
+    assert(b);
+    return (OPJ_INT32)(((OPJ_INT64)a + b - 1) / b);
+}
+
+/**
+Divide an integer and round upwards
+@return Returns a divided by b
+*/
+static INLINE OPJ_UINT32  opj_uint_ceildiv(OPJ_UINT32  a, OPJ_UINT32  b)
+{
+    assert(b);
+    return (OPJ_UINT32)(((OPJ_UINT64)a + b - 1) / b);
+}
+
+/**
+Divide an integer by a power of 2 and round upwards
+@return Returns a divided by 2^b
+*/
+static INLINE OPJ_INT32 opj_int_ceildivpow2(OPJ_INT32 a, OPJ_INT32 b)
+{
+    return (OPJ_INT32)((a + ((OPJ_INT64)1 << b) - 1) >> b);
+}
+
+/**
+ Divide a 64bits integer by a power of 2 and round upwards
+ @return Returns a divided by 2^b
+ */
+static INLINE OPJ_INT32 opj_int64_ceildivpow2(OPJ_INT64 a, OPJ_INT32 b)
+{
+    return (OPJ_INT32)((a + ((OPJ_INT64)1 << b) - 1) >> b);
+}
+
+/**
+ Divide an integer by a power of 2 and round upwards
+ @return Returns a divided by 2^b
+ */
+static INLINE OPJ_UINT32 opj_uint_ceildivpow2(OPJ_UINT32 a, OPJ_UINT32 b)
+{
+    return (OPJ_UINT32)((a + ((OPJ_UINT64)1U << b) - 1U) >> b);
+}
+
+/**
+Divide an integer by a power of 2 and round downwards
+@return Returns a divided by 2^b
+*/
+static INLINE OPJ_INT32 opj_int_floordivpow2(OPJ_INT32 a, OPJ_INT32 b)
+{
+    return a >> b;
+}
+
+/**
+Divide an integer by a power of 2 and round downwards
+@return Returns a divided by 2^b
+*/
+static INLINE OPJ_UINT32 opj_uint_floordivpow2(OPJ_UINT32 a, OPJ_UINT32 b)
+{
+    return a >> b;
+}
+
+/**
+Get logarithm of an integer and round downwards
+@return Returns log2(a)
+*/
+static INLINE OPJ_INT32 opj_int_floorlog2(OPJ_INT32 a)
+{
+    OPJ_INT32 l;
+    for (l = 0; a > 1; l++) {
+        a >>= 1;
+    }
+    return l;
+}
+/**
+Get logarithm of an integer and round downwards
+@return Returns log2(a)
+*/
+static INLINE OPJ_UINT32  opj_uint_floorlog2(OPJ_UINT32  a)
+{
+    OPJ_UINT32  l;
+    for (l = 0; a > 1; ++l) {
+        a >>= 1;
+    }
+    return l;
+}
+
+/**
+Multiply two fixed-precision rational numbers.
+@param a
+@param b
+@return Returns a * b
+*/
+static INLINE OPJ_INT32 opj_int_fix_mul(OPJ_INT32 a, OPJ_INT32 b)
+{
+#if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(__INTEL_COMPILER) && defined(_M_IX86)
+    OPJ_INT64 temp = __emul(a, b);
+#else
+    OPJ_INT64 temp = (OPJ_INT64) a * (OPJ_INT64) b ;
+#endif
+    temp += 4096;
+    assert((temp >> 13) <= (OPJ_INT64)0x7FFFFFFF);
+    assert((temp >> 13) >= (-(OPJ_INT64)0x7FFFFFFF - (OPJ_INT64)1));
+    return (OPJ_INT32)(temp >> 13);
+}
+
+static INLINE OPJ_INT32 opj_int_fix_mul_t1(OPJ_INT32 a, OPJ_INT32 b)
+{
+#if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(__INTEL_COMPILER) && defined(_M_IX86)
+    OPJ_INT64 temp = __emul(a, b);
+#else
+    OPJ_INT64 temp = (OPJ_INT64) a * (OPJ_INT64) b ;
+#endif
+    temp += 4096;
+    assert((temp >> (13 + 11 - T1_NMSEDEC_FRACBITS)) <= (OPJ_INT64)0x7FFFFFFF);
+    assert((temp >> (13 + 11 - T1_NMSEDEC_FRACBITS)) >= (-(OPJ_INT64)0x7FFFFFFF -
+            (OPJ_INT64)1));
+    return (OPJ_INT32)(temp >> (13 + 11 - T1_NMSEDEC_FRACBITS)) ;
+}
+
+/**
+Addition two signed integers with a wrap-around behaviour.
+Assumes complement-to-two signed integers.
+@param a
+@param b
+@return Returns a + b
+*/
+static INLINE OPJ_INT32 opj_int_add_no_overflow(OPJ_INT32 a, OPJ_INT32 b)
+{
+    void* pa = &a;
+    void* pb = &b;
+    OPJ_UINT32* upa = (OPJ_UINT32*)pa;
+    OPJ_UINT32* upb = (OPJ_UINT32*)pb;
+    OPJ_UINT32 ures = *upa + *upb;
+    void* pures = &ures;
+    OPJ_INT32* ipres = (OPJ_INT32*)pures;
+    return *ipres;
+}
+
+/**
+Subtract two signed integers with a wrap-around behaviour.
+Assumes complement-to-two signed integers.
+@param a
+@param b
+@return Returns a - b
+*/
+static INLINE OPJ_INT32 opj_int_sub_no_overflow(OPJ_INT32 a, OPJ_INT32 b)
+{
+    void* pa = &a;
+    void* pb = &b;
+    OPJ_UINT32* upa = (OPJ_UINT32*)pa;
+    OPJ_UINT32* upb = (OPJ_UINT32*)pb;
+    OPJ_UINT32 ures = *upa - *upb;
+    void* pures = &ures;
+    OPJ_INT32* ipres = (OPJ_INT32*)pures;
+    return *ipres;
+}
+
+/* ----------------------------------------------------------------------- */
+/*@}*/
+
+/*@}*/
+
+#endif /* OPJ_INTMATH_H */
diff --git a/third_party/libopenjpeg20/opj_inttypes.h b/third_party/libopenjpeg/opj_inttypes.h
similarity index 100%
rename from third_party/libopenjpeg20/opj_inttypes.h
rename to third_party/libopenjpeg/opj_inttypes.h
diff --git a/third_party/libopenjpeg/opj_malloc.cc b/third_party/libopenjpeg/opj_malloc.cc
new file mode 100644
index 0000000..a8d8700
--- /dev/null
+++ b/third_party/libopenjpeg/opj_malloc.cc
@@ -0,0 +1,42 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Deliberately not including opj_malloc.h, which has poisoned malloc and
+// friends.
+
+#include "core/fxcrt/fx_memory.h"
+#include "third_party/base/memory/aligned_memory.h"
+
+extern "C" {
+
+void* opj_malloc(size_t size) {
+  return FXMEM_DefaultAlloc(size);
+}
+
+void* opj_calloc(size_t numOfElements, size_t sizeOfElements) {
+  return FXMEM_DefaultCalloc(numOfElements, sizeOfElements);
+}
+
+void* opj_aligned_malloc(size_t size) {
+  return size ? pdfium::base::AlignedAlloc(size, 16) : nullptr;
+}
+
+void opj_aligned_free(void* ptr) {
+  pdfium::base::AlignedFree(ptr);
+}
+
+void* opj_aligned_32_malloc(size_t size) {
+  return size ? pdfium::base::AlignedAlloc(size, 32) : nullptr;
+}
+
+void* opj_realloc(void* m, size_t s) {
+  return FXMEM_DefaultRealloc(m, s);
+}
+
+void opj_free(void* m) {
+  if (m)
+    FXMEM_DefaultFree(m);
+}
+
+}  // extern "C"
diff --git a/third_party/libopenjpeg/opj_malloc.h b/third_party/libopenjpeg/opj_malloc.h
new file mode 100644
index 0000000..c215c24
--- /dev/null
+++ b/third_party/libopenjpeg/opj_malloc.h
@@ -0,0 +1,112 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2007, Callum Lerwick <seg@haxxed.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef OPJ_MALLOC_H
+#define OPJ_MALLOC_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+@file opj_malloc.h
+@brief Internal functions
+
+The functions in opj_malloc.h are internal utilities used for memory management.
+*/
+
+/** @defgroup MISC MISC - Miscellaneous internal functions */
+/*@{*/
+
+/** @name Exported functions */
+/*@{*/
+/* ----------------------------------------------------------------------- */
+
+/**
+Allocate an uninitialized memory block
+@param size Bytes to allocate
+@return Returns a void pointer to the allocated space, or NULL if there is insufficient memory available
+*/
+void * opj_malloc(size_t size);
+
+/**
+Allocate a memory block with elements initialized to 0
+@param numOfElements  Blocks to allocate
+@param sizeOfElements Bytes per block to allocate
+@return Returns a void pointer to the allocated space, or NULL if there is insufficient memory available
+*/
+void * opj_calloc(size_t numOfElements, size_t sizeOfElements);
+
+/**
+Allocate memory aligned to a 16 byte boundary
+@param size Bytes to allocate
+@return Returns a void pointer to the allocated space, or NULL if there is insufficient memory available
+*/
+void * opj_aligned_malloc(size_t size);
+void opj_aligned_free(void* ptr);
+
+/**
+Allocate memory aligned to a 32 byte boundary
+@param size Bytes to allocate
+@return Returns a void pointer to the allocated space, or NULL if there is insufficient memory available
+*/
+void * opj_aligned_32_malloc(size_t size);
+
+/**
+Reallocate memory blocks.
+@param m Pointer to previously allocated memory block
+@param s New size in bytes
+@return Returns a void pointer to the reallocated (and possibly moved) memory block
+*/
+void * opj_realloc(void * m, size_t s);
+
+/**
+Deallocates or frees a memory block.
+@param m Previously allocated memory block to be freed
+*/
+void opj_free(void * m);
+
+#if defined(__GNUC__) && !defined(OPJ_SKIP_POISON)
+#pragma GCC poison malloc calloc realloc free
+#endif
+
+/* ----------------------------------------------------------------------- */
+/*@}*/
+
+/*@}*/
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif /* OPJ_MALLOC_H */
diff --git a/third_party/libopenjpeg20/opj_stdint.h b/third_party/libopenjpeg/opj_stdint.h
similarity index 100%
rename from third_party/libopenjpeg20/opj_stdint.h
rename to third_party/libopenjpeg/opj_stdint.h
diff --git a/third_party/libopenjpeg/pi.c b/third_party/libopenjpeg/pi.c
new file mode 100644
index 0000000..ce86dcc
--- /dev/null
+++ b/third_party/libopenjpeg/pi.c
@@ -0,0 +1,2163 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2006-2007, Parvatha Elangovan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define OPJ_UINT32_SEMANTICALLY_BUT_INT32 OPJ_UINT32
+
+#include "opj_includes.h"
+
+/** @defgroup PI PI - Implementation of a packet iterator */
+/*@{*/
+
+/** @name Local static functions */
+/*@{*/
+
+/**
+Get next packet in layer-resolution-component-precinct order.
+@param pi packet iterator to modify
+@return returns false if pi pointed to the last packet or else returns true
+*/
+static OPJ_BOOL opj_pi_next_lrcp(opj_pi_iterator_t * pi);
+/**
+Get next packet in resolution-layer-component-precinct order.
+@param pi packet iterator to modify
+@return returns false if pi pointed to the last packet or else returns true
+*/
+static OPJ_BOOL opj_pi_next_rlcp(opj_pi_iterator_t * pi);
+/**
+Get next packet in resolution-precinct-component-layer order.
+@param pi packet iterator to modify
+@return returns false if pi pointed to the last packet or else returns true
+*/
+static OPJ_BOOL opj_pi_next_rpcl(opj_pi_iterator_t * pi);
+/**
+Get next packet in precinct-component-resolution-layer order.
+@param pi packet iterator to modify
+@return returns false if pi pointed to the last packet or else returns true
+*/
+static OPJ_BOOL opj_pi_next_pcrl(opj_pi_iterator_t * pi);
+/**
+Get next packet in component-precinct-resolution-layer order.
+@param pi packet iterator to modify
+@return returns false if pi pointed to the last packet or else returns true
+*/
+static OPJ_BOOL opj_pi_next_cprl(opj_pi_iterator_t * pi);
+
+/**
+ * Updates the coding parameters if the encoding is used with Progression order changes and final (or cinema parameters are used).
+ *
+ * @param   p_cp        the coding parameters to modify
+ * @param   p_tileno    the tile index being concerned.
+ * @param   p_tx0       X0 parameter for the tile
+ * @param   p_tx1       X1 parameter for the tile
+ * @param   p_ty0       Y0 parameter for the tile
+ * @param   p_ty1       Y1 parameter for the tile
+ * @param   p_max_prec  the maximum precision for all the bands of the tile
+ * @param   p_max_res   the maximum number of resolutions for all the poc inside the tile.
+ * @param   p_dx_min        the minimum dx of all the components of all the resolutions for the tile.
+ * @param   p_dy_min        the minimum dy of all the components of all the resolutions for the tile.
+ */
+static void opj_pi_update_encode_poc_and_final(opj_cp_t *p_cp,
+        OPJ_UINT32 p_tileno,
+        OPJ_UINT32 p_tx0,
+        OPJ_UINT32 p_tx1,
+        OPJ_UINT32 p_ty0,
+        OPJ_UINT32 p_ty1,
+        OPJ_UINT32 p_max_prec,
+        OPJ_UINT32 p_max_res,
+        OPJ_UINT32 p_dx_min,
+        OPJ_UINT32 p_dy_min);
+
+/**
+ * Updates the coding parameters if the encoding is not used with Progression order changes and final (and cinema parameters are used).
+ *
+ * @param   p_cp        the coding parameters to modify
+ * @param   p_num_comps     the number of components
+ * @param   p_tileno    the tile index being concerned.
+ * @param   p_tx0       X0 parameter for the tile
+ * @param   p_tx1       X1 parameter for the tile
+ * @param   p_ty0       Y0 parameter for the tile
+ * @param   p_ty1       Y1 parameter for the tile
+ * @param   p_max_prec  the maximum precision for all the bands of the tile
+ * @param   p_max_res   the maximum number of resolutions for all the poc inside the tile.
+ * @param   p_dx_min        the minimum dx of all the components of all the resolutions for the tile.
+ * @param   p_dy_min        the minimum dy of all the components of all the resolutions for the tile.
+ */
+static void opj_pi_update_encode_not_poc(opj_cp_t *p_cp,
+        OPJ_UINT32 p_num_comps,
+        OPJ_UINT32 p_tileno,
+        OPJ_UINT32 p_tx0,
+        OPJ_UINT32 p_tx1,
+        OPJ_UINT32 p_ty0,
+        OPJ_UINT32 p_ty1,
+        OPJ_UINT32 p_max_prec,
+        OPJ_UINT32 p_max_res,
+        OPJ_UINT32 p_dx_min,
+        OPJ_UINT32 p_dy_min);
+/**
+ * Gets the encoding parameters needed to update the coding parameters and all the pocs.
+ *
+ * @param   p_image         the image being encoded.
+ * @param   p_cp            the coding parameters.
+ * @param   tileno          the tile index of the tile being encoded.
+ * @param   p_tx0           pointer that will hold the X0 parameter for the tile
+ * @param   p_tx1           pointer that will hold the X1 parameter for the tile
+ * @param   p_ty0           pointer that will hold the Y0 parameter for the tile
+ * @param   p_ty1           pointer that will hold the Y1 parameter for the tile
+ * @param   p_max_prec      pointer that will hold the maximum precision for all the bands of the tile
+ * @param   p_max_res       pointer that will hold the maximum number of resolutions for all the poc inside the tile.
+ * @param   p_dx_min            pointer that will hold the minimum dx of all the components of all the resolutions for the tile.
+ * @param   p_dy_min            pointer that will hold the minimum dy of all the components of all the resolutions for the tile.
+ */
+static void opj_get_encoding_parameters(const opj_image_t *p_image,
+                                        const opj_cp_t *p_cp,
+                                        OPJ_UINT32  tileno,
+                                        OPJ_UINT32 * p_tx0,
+                                        OPJ_UINT32 * p_tx1,
+                                        OPJ_UINT32 * p_ty0,
+                                        OPJ_UINT32 * p_ty1,
+                                        OPJ_UINT32 * p_dx_min,
+                                        OPJ_UINT32 * p_dy_min,
+                                        OPJ_UINT32 * p_max_prec,
+                                        OPJ_UINT32 * p_max_res);
+
+/**
+ * Gets the encoding parameters needed to update the coding parameters and all the pocs.
+ * The precinct widths, heights, dx and dy for each component at each resolution will be stored as well.
+ * the last parameter of the function should be an array of pointers of size nb components, each pointer leading
+ * to an area of size 4 * max_res. The data is stored inside this area with the following pattern :
+ * dx_compi_res0 , dy_compi_res0 , w_compi_res0, h_compi_res0 , dx_compi_res1 , dy_compi_res1 , w_compi_res1, h_compi_res1 , ...
+ *
+ * @param   p_image         the image being encoded.
+ * @param   p_cp            the coding parameters.
+ * @param   tileno          the tile index of the tile being encoded.
+ * @param   p_tx0           pointer that will hold the X0 parameter for the tile
+ * @param   p_tx1           pointer that will hold the X1 parameter for the tile
+ * @param   p_ty0           pointer that will hold the Y0 parameter for the tile
+ * @param   p_ty1           pointer that will hold the Y1 parameter for the tile
+ * @param   p_max_prec      pointer that will hold the maximum precision for all the bands of the tile
+ * @param   p_max_res       pointer that will hold the maximum number of resolutions for all the poc inside the tile.
+ * @param   p_dx_min        pointer that will hold the minimum dx of all the components of all the resolutions for the tile.
+ * @param   p_dy_min        pointer that will hold the minimum dy of all the components of all the resolutions for the tile.
+ * @param   p_resolutions   pointer to an area corresponding to the one described above.
+ */
+static void opj_get_all_encoding_parameters(const opj_image_t *p_image,
+        const opj_cp_t *p_cp,
+        OPJ_UINT32 tileno,
+        OPJ_UINT32 * p_tx0,
+        OPJ_UINT32 * p_tx1,
+        OPJ_UINT32 * p_ty0,
+        OPJ_UINT32 * p_ty1,
+        OPJ_UINT32 * p_dx_min,
+        OPJ_UINT32 * p_dy_min,
+        OPJ_UINT32 * p_max_prec,
+        OPJ_UINT32 * p_max_res,
+        OPJ_UINT32 ** p_resolutions);
+/**
+ * Allocates memory for a packet iterator. Data and data sizes are set by this operation.
+ * No other data is set. The include section of the packet  iterator is not allocated.
+ *
+ * @param   p_image     the image used to initialize the packet iterator (in fact only the number of components is relevant.
+ * @param   p_cp        the coding parameters.
+ * @param   tileno  the index of the tile from which creating the packet iterator.
+ * @param   manager Event manager
+ */
+static opj_pi_iterator_t * opj_pi_create(const opj_image_t *p_image,
+        const opj_cp_t *p_cp,
+        OPJ_UINT32 tileno,
+        opj_event_mgr_t* manager);
+/**
+ * FIXME DOC
+ */
+static void opj_pi_update_decode_not_poc(opj_pi_iterator_t * p_pi,
+        opj_tcp_t * p_tcp,
+        OPJ_UINT32 p_max_precision,
+        OPJ_UINT32 p_max_res);
+/**
+ * FIXME DOC
+ */
+static void opj_pi_update_decode_poc(opj_pi_iterator_t * p_pi,
+                                     opj_tcp_t * p_tcp,
+                                     OPJ_UINT32 p_max_precision,
+                                     OPJ_UINT32 p_max_res);
+
+/**
+ * FIXME DOC
+ */
+static OPJ_BOOL opj_pi_check_next_level(OPJ_INT32 pos,
+                                        opj_cp_t *cp,
+                                        OPJ_UINT32 tileno,
+                                        OPJ_UINT32 pino,
+                                        const OPJ_CHAR *prog);
+
+/*@}*/
+
+/*@}*/
+
+/*
+==========================================================
+   local functions
+==========================================================
+*/
+
+static OPJ_BOOL opj_pi_next_lrcp(opj_pi_iterator_t * pi)
+{
+    opj_pi_comp_t *comp = NULL;
+    opj_pi_resolution_t *res = NULL;
+    OPJ_UINT32 index = 0;
+
+    if (pi->poc.compno0 >= pi->numcomps ||
+            pi->poc.compno1 >= pi->numcomps + 1) {
+        opj_event_msg(pi->manager, EVT_ERROR,
+                      "opj_pi_next_lrcp(): invalid compno0/compno1\n");
+        return OPJ_FALSE;
+    }
+
+    if (!pi->first) {
+        comp = &pi->comps[pi->compno];
+        res = &comp->resolutions[pi->resno];
+        goto LABEL_SKIP;
+    } else {
+        pi->first = 0;
+    }
+
+    for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) {
+        for (pi->resno = pi->poc.resno0; pi->resno < pi->poc.resno1;
+                pi->resno++) {
+            for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) {
+                comp = &pi->comps[pi->compno];
+                if (pi->resno >= comp->numresolutions) {
+                    continue;
+                }
+                res = &comp->resolutions[pi->resno];
+                if (!pi->tp_on) {
+                    pi->poc.precno1 = res->pw * res->ph;
+                }
+                for (pi->precno = pi->poc.precno0; pi->precno < pi->poc.precno1; pi->precno++) {
+                    index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno *
+                            pi->step_c + pi->precno * pi->step_p;
+                    /* Avoids index out of bounds access with */
+                    /* id_000098,sig_11,src_005411,op_havoc,rep_2 of */
+                    /* https://github.com/uclouvain/openjpeg/issues/938 */
+                    /* Not sure if this is the most clever fix. Perhaps */
+                    /* include should be resized when a POC arises, or */
+                    /* the POC should be rejected */
+                    if (index >= pi->include_size) {
+                        opj_event_msg(pi->manager, EVT_ERROR, "Invalid access to pi->include");
+                        return OPJ_FALSE;
+                    }
+                    if (!pi->include[index]) {
+                        pi->include[index] = 1;
+                        return OPJ_TRUE;
+                    }
+LABEL_SKIP:
+                    ;
+                }
+            }
+        }
+    }
+
+    return OPJ_FALSE;
+}
+
+static OPJ_BOOL opj_pi_next_rlcp(opj_pi_iterator_t * pi)
+{
+    opj_pi_comp_t *comp = NULL;
+    opj_pi_resolution_t *res = NULL;
+    OPJ_UINT32 index = 0;
+
+    if (pi->poc.compno0 >= pi->numcomps ||
+            pi->poc.compno1 >= pi->numcomps + 1) {
+        opj_event_msg(pi->manager, EVT_ERROR,
+                      "opj_pi_next_rlcp(): invalid compno0/compno1\n");
+        return OPJ_FALSE;
+    }
+
+    if (!pi->first) {
+        comp = &pi->comps[pi->compno];
+        res = &comp->resolutions[pi->resno];
+        goto LABEL_SKIP;
+    } else {
+        pi->first = 0;
+    }
+
+    for (pi->resno = pi->poc.resno0; pi->resno < pi->poc.resno1; pi->resno++) {
+        for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) {
+            for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) {
+                comp = &pi->comps[pi->compno];
+                if (pi->resno >= comp->numresolutions) {
+                    continue;
+                }
+                res = &comp->resolutions[pi->resno];
+                if (!pi->tp_on) {
+                    pi->poc.precno1 = res->pw * res->ph;
+                }
+                for (pi->precno = pi->poc.precno0; pi->precno < pi->poc.precno1; pi->precno++) {
+                    index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno *
+                            pi->step_c + pi->precno * pi->step_p;
+                    if (index >= pi->include_size) {
+                        opj_event_msg(pi->manager, EVT_ERROR, "Invalid access to pi->include");
+                        return OPJ_FALSE;
+                    }
+                    if (!pi->include[index]) {
+                        pi->include[index] = 1;
+                        return OPJ_TRUE;
+                    }
+LABEL_SKIP:
+                    ;
+                }
+            }
+        }
+    }
+
+    return OPJ_FALSE;
+}
+
+static OPJ_BOOL opj_pi_next_rpcl(opj_pi_iterator_t * pi)
+{
+    opj_pi_comp_t *comp = NULL;
+    opj_pi_resolution_t *res = NULL;
+    OPJ_UINT32 index = 0;
+
+    if (pi->poc.compno0 >= pi->numcomps ||
+            pi->poc.compno1 >= pi->numcomps + 1) {
+        opj_event_msg(pi->manager, EVT_ERROR,
+                      "opj_pi_next_rpcl(): invalid compno0/compno1\n");
+        return OPJ_FALSE;
+    }
+
+    if (!pi->first) {
+        goto LABEL_SKIP;
+    } else {
+        OPJ_UINT32 compno, resno;
+        pi->first = 0;
+        pi->dx = 0;
+        pi->dy = 0;
+        for (compno = 0; compno < pi->numcomps; compno++) {
+            comp = &pi->comps[compno];
+            for (resno = 0; resno < comp->numresolutions; resno++) {
+                OPJ_UINT32 dx, dy;
+                res = &comp->resolutions[resno];
+                if (res->pdx + comp->numresolutions - 1 - resno < 32 &&
+                        comp->dx <= UINT_MAX / (1u << (res->pdx + comp->numresolutions - 1 - resno))) {
+                    dx = comp->dx * (1u << (res->pdx + comp->numresolutions - 1 - resno));
+                    pi->dx = !pi->dx ? dx : opj_uint_min(pi->dx, dx);
+                }
+                if (res->pdy + comp->numresolutions - 1 - resno < 32 &&
+                        comp->dy <= UINT_MAX / (1u << (res->pdy + comp->numresolutions - 1 - resno))) {
+                    dy = comp->dy * (1u << (res->pdy + comp->numresolutions - 1 - resno));
+                    pi->dy = !pi->dy ? dy : opj_uint_min(pi->dy, dy);
+                }
+            }
+        }
+        if (pi->dx == 0 || pi->dy == 0) {
+            return OPJ_FALSE;
+        }
+    }
+    if (!pi->tp_on) {
+        pi->poc.ty0 = pi->ty0;
+        pi->poc.tx0 = pi->tx0;
+        pi->poc.ty1 = pi->ty1;
+        pi->poc.tx1 = pi->tx1;
+    }
+    for (pi->resno = pi->poc.resno0; pi->resno < pi->poc.resno1; pi->resno++) {
+        for (pi->y = (OPJ_UINT32)pi->poc.ty0; pi->y < (OPJ_UINT32)pi->poc.ty1;
+                pi->y += (pi->dy - (pi->y % pi->dy))) {
+            for (pi->x = (OPJ_UINT32)pi->poc.tx0; pi->x < (OPJ_UINT32)pi->poc.tx1;
+                    pi->x += (pi->dx - (pi->x % pi->dx))) {
+                for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) {
+                    OPJ_UINT32 levelno;
+                    OPJ_UINT32 trx0, try0;
+                    OPJ_UINT32  trx1, try1;
+                    OPJ_UINT32  rpx, rpy;
+                    OPJ_UINT32  prci, prcj;
+                    comp = &pi->comps[pi->compno];
+                    if (pi->resno >= comp->numresolutions) {
+                        continue;
+                    }
+                    res = &comp->resolutions[pi->resno];
+                    levelno = comp->numresolutions - 1 - pi->resno;
+                    /* Avoids division by zero */
+                    /* Relates to id_000004,sig_06,src_000679,op_arith8,pos_49,val_-17 */
+                    /* of  https://github.com/uclouvain/openjpeg/issues/938 */
+                    if (levelno >= 32 ||
+                            ((comp->dx << levelno) >> levelno) != comp->dx ||
+                            ((comp->dy << levelno) >> levelno) != comp->dy) {
+                        continue;
+                    }
+                    if ((comp->dx << levelno) > INT_MAX ||
+                            (comp->dy << levelno) > INT_MAX) {
+                        continue;
+                    }
+                    trx0 = opj_uint_ceildiv(pi->tx0, (comp->dx << levelno));
+                    try0 = opj_uint_ceildiv(pi->ty0, (comp->dy << levelno));
+                    trx1 = opj_uint_ceildiv(pi->tx1, (comp->dx << levelno));
+                    try1 = opj_uint_ceildiv(pi->ty1, (comp->dy << levelno));
+                    rpx = res->pdx + levelno;
+                    rpy = res->pdy + levelno;
+
+                    /* To avoid divisions by zero / undefined behaviour on shift */
+                    /* in below tests */
+                    /* Fixes reading id:000026,sig:08,src:002419,op:int32,pos:60,val:+32 */
+                    /* of https://github.com/uclouvain/openjpeg/issues/938 */
+                    if (rpx >= 31 || ((comp->dx << rpx) >> rpx) != comp->dx ||
+                            rpy >= 31 || ((comp->dy << rpy) >> rpy) != comp->dy) {
+                        continue;
+                    }
+
+                    /* See ISO-15441. B.12.1.3 Resolution level-position-component-layer progression */
+                    if (!((pi->y % (comp->dy << rpy) == 0) || ((pi->y == pi->ty0) &&
+                            ((try0 << levelno) % (1U << rpy))))) {
+                        continue;
+                    }
+                    if (!((pi->x % (comp->dx << rpx) == 0) || ((pi->x == pi->tx0) &&
+                            ((trx0 << levelno) % (1U << rpx))))) {
+                        continue;
+                    }
+
+                    if ((res->pw == 0) || (res->ph == 0)) {
+                        continue;
+                    }
+
+                    if ((trx0 == trx1) || (try0 == try1)) {
+                        continue;
+                    }
+
+                    prci = opj_uint_floordivpow2(opj_uint_ceildiv(pi->x,
+                                                 (comp->dx << levelno)), res->pdx)
+                           - opj_uint_floordivpow2(trx0, res->pdx);
+                    prcj = opj_uint_floordivpow2(opj_uint_ceildiv(pi->y,
+                                                 (comp->dy << levelno)), res->pdy)
+                           - opj_uint_floordivpow2(try0, res->pdy);
+                    pi->precno = prci + prcj * res->pw;
+                    if (pi->precno >= res->pw * res->ph) {
+                      return OPJ_FALSE;
+                    }
+                    for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) {
+                        index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno *
+                                pi->step_c + pi->precno * pi->step_p;
+                        if (index >= pi->include_size) {
+                            opj_event_msg(pi->manager, EVT_ERROR, "Invalid access to pi->include");
+                            return OPJ_FALSE;
+                        }
+                        if (!pi->include[index]) {
+                            pi->include[index] = 1;
+                            return OPJ_TRUE;
+                        }
+LABEL_SKIP:
+                        ;
+                    }
+                }
+            }
+        }
+    }
+
+    return OPJ_FALSE;
+}
+
+static OPJ_BOOL opj_pi_next_pcrl(opj_pi_iterator_t * pi)
+{
+    opj_pi_comp_t *comp = NULL;
+    opj_pi_resolution_t *res = NULL;
+    OPJ_UINT32 index = 0;
+
+    if (pi->poc.compno0 >= pi->numcomps ||
+            pi->poc.compno1 >= pi->numcomps + 1) {
+        opj_event_msg(pi->manager, EVT_ERROR,
+                      "opj_pi_next_pcrl(): invalid compno0/compno1\n");
+        return OPJ_FALSE;
+    }
+
+    if (!pi->first) {
+        comp = &pi->comps[pi->compno];
+        goto LABEL_SKIP;
+    } else {
+        OPJ_UINT32 compno, resno;
+        pi->first = 0;
+        pi->dx = 0;
+        pi->dy = 0;
+        for (compno = 0; compno < pi->numcomps; compno++) {
+            comp = &pi->comps[compno];
+            for (resno = 0; resno < comp->numresolutions; resno++) {
+                OPJ_UINT32 dx, dy;
+                res = &comp->resolutions[resno];
+                if (res->pdx + comp->numresolutions - 1 - resno < 32 &&
+                        comp->dx <= UINT_MAX / (1u << (res->pdx + comp->numresolutions - 1 - resno))) {
+                    dx = comp->dx * (1u << (res->pdx + comp->numresolutions - 1 - resno));
+                    pi->dx = !pi->dx ? dx : opj_uint_min(pi->dx, dx);
+                }
+                if (res->pdy + comp->numresolutions - 1 - resno < 32 &&
+                        comp->dy <= UINT_MAX / (1u << (res->pdy + comp->numresolutions - 1 - resno))) {
+                    dy = comp->dy * (1u << (res->pdy + comp->numresolutions - 1 - resno));
+                    pi->dy = !pi->dy ? dy : opj_uint_min(pi->dy, dy);
+                }
+            }
+        }
+        if (pi->dx == 0 || pi->dy == 0) {
+            return OPJ_FALSE;
+        }
+    }
+    if (!pi->tp_on) {
+        pi->poc.ty0 = pi->ty0;
+        pi->poc.tx0 = pi->tx0;
+        pi->poc.ty1 = pi->ty1;
+        pi->poc.tx1 = pi->tx1;
+    }
+    for (pi->y = (OPJ_UINT32)pi->poc.ty0; pi->y < (OPJ_UINT32)pi->poc.ty1;
+            pi->y += (pi->dy - (pi->y % pi->dy))) {
+        for (pi->x = (OPJ_UINT32)pi->poc.tx0; pi->x < (OPJ_UINT32)pi->poc.tx1;
+                pi->x += (pi->dx - (pi->x % pi->dx))) {
+            for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) {
+                comp = &pi->comps[pi->compno];
+                for (pi->resno = pi->poc.resno0;
+                        pi->resno < opj_uint_min(pi->poc.resno1, comp->numresolutions); pi->resno++) {
+                    OPJ_UINT32 levelno;
+                    OPJ_UINT32 trx0, try0;
+                    OPJ_UINT32 trx1, try1;
+                    OPJ_UINT32 rpx, rpy;
+                    OPJ_UINT32 prci, prcj;
+                    res = &comp->resolutions[pi->resno];
+                    levelno = comp->numresolutions - 1 - pi->resno;
+                    /* Avoids division by zero */
+                    /* Relates to id_000004,sig_06,src_000679,op_arith8,pos_49,val_-17 */
+                    /* of  https://github.com/uclouvain/openjpeg/issues/938 */
+                    if (levelno >= 32 ||
+                            ((comp->dx << levelno) >> levelno) != comp->dx ||
+                            ((comp->dy << levelno) >> levelno) != comp->dy) {
+                        continue;
+                    }
+                    if ((comp->dx << levelno) > INT_MAX ||
+                            (comp->dy << levelno) > INT_MAX) {
+                        continue;
+                    }
+                    trx0 = opj_uint_ceildiv(pi->tx0, (comp->dx << levelno));
+                    try0 = opj_uint_ceildiv(pi->ty0, (comp->dy << levelno));
+                    trx1 = opj_uint_ceildiv(pi->tx1, (comp->dx << levelno));
+                    try1 = opj_uint_ceildiv(pi->ty1, (comp->dy << levelno));
+                    rpx = res->pdx + levelno;
+                    rpy = res->pdy + levelno;
+
+                    /* To avoid divisions by zero / undefined behaviour on shift */
+                    /* in below tests */
+                    /* Relates to id:000019,sig:08,src:001098,op:flip1,pos:49 */
+                    /* of https://github.com/uclouvain/openjpeg/issues/938 */
+                    if (rpx >= 31 || ((comp->dx << rpx) >> rpx) != comp->dx ||
+                            rpy >= 31 || ((comp->dy << rpy) >> rpy) != comp->dy) {
+                        continue;
+                    }
+
+                    /* See ISO-15441. B.12.1.4 Position-component-resolution level-layer progression */
+                    if (!((pi->y % (comp->dy << rpy) == 0) || ((pi->y == pi->ty0) &&
+                            ((try0 << levelno) % (1U << rpy))))) {
+                        continue;
+                    }
+                    if (!((pi->x % (comp->dx << rpx) == 0) || ((pi->x == pi->tx0) &&
+                            ((trx0 << levelno) % (1U << rpx))))) {
+                        continue;
+                    }
+
+                    if ((res->pw == 0) || (res->ph == 0)) {
+                        continue;
+                    }
+
+                    if ((trx0 == trx1) || (try0 == try1)) {
+                        continue;
+                    }
+
+                    prci = opj_uint_floordivpow2(opj_uint_ceildiv(pi->x,
+                                                 (comp->dx << levelno)), res->pdx)
+                           - opj_uint_floordivpow2(trx0, res->pdx);
+                    prcj = opj_uint_floordivpow2(opj_uint_ceildiv(pi->y,
+                                                 (comp->dy << levelno)), res->pdy)
+                           - opj_uint_floordivpow2(try0, res->pdy);
+                    pi->precno = prci + prcj * res->pw;
+                    if (pi->precno >= res->pw * res->ph) {
+                      return OPJ_FALSE;
+                    }
+                    for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) {
+                        index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno *
+                                pi->step_c + pi->precno * pi->step_p;
+                        if (index >= pi->include_size) {
+                            opj_event_msg(pi->manager, EVT_ERROR, "Invalid access to pi->include");
+                            return OPJ_FALSE;
+                        }
+                        if (!pi->include[index]) {
+                            pi->include[index] = 1;
+                            return OPJ_TRUE;
+                        }
+LABEL_SKIP:
+                        ;
+                    }
+                }
+            }
+        }
+    }
+
+    return OPJ_FALSE;
+}
+
+static OPJ_BOOL opj_pi_next_cprl(opj_pi_iterator_t * pi)
+{
+    opj_pi_comp_t *comp = NULL;
+    opj_pi_resolution_t *res = NULL;
+    OPJ_UINT32 index = 0;
+
+    if (pi->poc.compno0 >= pi->numcomps ||
+            pi->poc.compno1 >= pi->numcomps + 1) {
+        opj_event_msg(pi->manager, EVT_ERROR,
+                      "opj_pi_next_cprl(): invalid compno0/compno1\n");
+        return OPJ_FALSE;
+    }
+
+    if (!pi->first) {
+        comp = &pi->comps[pi->compno];
+        goto LABEL_SKIP;
+    } else {
+        pi->first = 0;
+    }
+
+    for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) {
+        OPJ_UINT32 resno;
+        comp = &pi->comps[pi->compno];
+        pi->dx = 0;
+        pi->dy = 0;
+        for (resno = 0; resno < comp->numresolutions; resno++) {
+            OPJ_UINT32 dx, dy;
+            res = &comp->resolutions[resno];
+            if (res->pdx + comp->numresolutions - 1 - resno < 32 &&
+                    comp->dx <= UINT_MAX / (1u << (res->pdx + comp->numresolutions - 1 - resno))) {
+                dx = comp->dx * (1u << (res->pdx + comp->numresolutions - 1 - resno));
+                pi->dx = !pi->dx ? dx : opj_uint_min(pi->dx, dx);
+            }
+            if (res->pdy + comp->numresolutions - 1 - resno < 32 &&
+                    comp->dy <= UINT_MAX / (1u << (res->pdy + comp->numresolutions - 1 - resno))) {
+                dy = comp->dy * (1u << (res->pdy + comp->numresolutions - 1 - resno));
+                pi->dy = !pi->dy ? dy : opj_uint_min(pi->dy, dy);
+            }
+        }
+        if (pi->dx == 0 || pi->dy == 0) {
+            return OPJ_FALSE;
+        }
+        if (!pi->tp_on) {
+            pi->poc.ty0 = pi->ty0;
+            pi->poc.tx0 = pi->tx0;
+            pi->poc.ty1 = pi->ty1;
+            pi->poc.tx1 = pi->tx1;
+        }
+        for (pi->y = (OPJ_UINT32)pi->poc.ty0; pi->y < (OPJ_UINT32)pi->poc.ty1;
+                pi->y += (pi->dy - (pi->y % pi->dy))) {
+            for (pi->x = (OPJ_UINT32)pi->poc.tx0; pi->x < (OPJ_UINT32)pi->poc.tx1;
+                    pi->x += (pi->dx - (pi->x % pi->dx))) {
+                for (pi->resno = pi->poc.resno0;
+                        pi->resno < opj_uint_min(pi->poc.resno1, comp->numresolutions); pi->resno++) {
+                    OPJ_UINT32 levelno;
+                    OPJ_UINT32 trx0, try0;
+                    OPJ_UINT32 trx1, try1;
+                    OPJ_UINT32 rpx, rpy;
+                    OPJ_UINT32 prci, prcj;
+                    res = &comp->resolutions[pi->resno];
+                    levelno = comp->numresolutions - 1 - pi->resno;
+                    /* Avoids division by zero on id_000004,sig_06,src_000679,op_arith8,pos_49,val_-17 */
+                    /* of  https://github.com/uclouvain/openjpeg/issues/938 */
+                    if (levelno >= 32 ||
+                            ((comp->dx << levelno) >> levelno) != comp->dx ||
+                            ((comp->dy << levelno) >> levelno) != comp->dy) {
+                        continue;
+                    }
+                    if ((comp->dx << levelno) > INT_MAX ||
+                            (comp->dy << levelno) > INT_MAX) {
+                        continue;
+                    }
+                    trx0 = opj_uint_ceildiv(pi->tx0, (comp->dx << levelno));
+                    try0 = opj_uint_ceildiv(pi->ty0, (comp->dy << levelno));
+                    trx1 = opj_uint_ceildiv(pi->tx1, (comp->dx << levelno));
+                    try1 = opj_uint_ceildiv(pi->ty1, (comp->dy << levelno));
+                    rpx = res->pdx + levelno;
+                    rpy = res->pdy + levelno;
+
+                    /* To avoid divisions by zero / undefined behaviour on shift */
+                    /* in below tests */
+                    /* Fixes reading id:000019,sig:08,src:001098,op:flip1,pos:49 */
+                    /* of https://github.com/uclouvain/openjpeg/issues/938 */
+                    if (rpx >= 31 || ((comp->dx << rpx) >> rpx) != comp->dx ||
+                            rpy >= 31 || ((comp->dy << rpy) >> rpy) != comp->dy) {
+                        continue;
+                    }
+
+                    /* See ISO-15441. B.12.1.5 Component-position-resolution level-layer progression */
+                    if (!((pi->y % (comp->dy << rpy) == 0) || ((pi->y == pi->ty0) &&
+                            ((try0 << levelno) % (1U << rpy))))) {
+                        continue;
+                    }
+                    if (!((pi->x % (comp->dx << rpx) == 0) || ((pi->x == pi->tx0) &&
+                            ((trx0 << levelno) % (1U << rpx))))) {
+                        continue;
+                    }
+
+                    if ((res->pw == 0) || (res->ph == 0)) {
+                        continue;
+                    }
+
+                    if ((trx0 == trx1) || (try0 == try1)) {
+                        continue;
+                    }
+
+                    prci = opj_uint_floordivpow2(opj_uint_ceildiv(pi->x,
+                                                 (comp->dx << levelno)), res->pdx)
+                           - opj_uint_floordivpow2(trx0, res->pdx);
+                    prcj = opj_uint_floordivpow2(opj_uint_ceildiv(pi->y,
+                                                 (comp->dy << levelno)), res->pdy)
+                           - opj_uint_floordivpow2(try0, res->pdy);
+                    pi->precno = (OPJ_UINT32)(prci + prcj * res->pw);
+                    if (pi->precno >= res->pw * res->ph) {
+                      return OPJ_FALSE;
+                    }
+                    for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) {
+                        index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno *
+                                pi->step_c + pi->precno * pi->step_p;
+                        if (index >= pi->include_size) {
+                            opj_event_msg(pi->manager, EVT_ERROR, "Invalid access to pi->include");
+                            return OPJ_FALSE;
+                        }
+                        if (!pi->include[index]) {
+                            pi->include[index] = 1;
+                            return OPJ_TRUE;
+                        }
+LABEL_SKIP:
+                        ;
+                    }
+                }
+            }
+        }
+    }
+
+    return OPJ_FALSE;
+}
+
+static void opj_get_encoding_parameters(const opj_image_t *p_image,
+                                        const opj_cp_t *p_cp,
+                                        OPJ_UINT32 p_tileno,
+                                        OPJ_UINT32 * p_tx0,
+                                        OPJ_UINT32  * p_tx1,
+                                        OPJ_UINT32  * p_ty0,
+                                        OPJ_UINT32  * p_ty1,
+                                        OPJ_UINT32 * p_dx_min,
+                                        OPJ_UINT32 * p_dy_min,
+                                        OPJ_UINT32 * p_max_prec,
+                                        OPJ_UINT32 * p_max_res)
+{
+    /* loop */
+    OPJ_UINT32  compno, resno;
+    /* pointers */
+    const opj_tcp_t *l_tcp = 00;
+    const opj_tccp_t * l_tccp = 00;
+    const opj_image_comp_t * l_img_comp = 00;
+
+    /* position in x and y of tile */
+    OPJ_UINT32 p, q;
+
+    /* non-corrected (in regard to image offset) tile offset */
+    OPJ_UINT32 l_tx0, l_ty0;
+
+    /* preconditions */
+    assert(p_cp != 00);
+    assert(p_image != 00);
+    assert(p_tileno < p_cp->tw * p_cp->th);
+
+    /* initializations */
+    l_tcp = &p_cp->tcps [p_tileno];
+    l_img_comp = p_image->comps;
+    l_tccp = l_tcp->tccps;
+
+    /* here calculation of tx0, tx1, ty0, ty1, maxprec, dx and dy */
+    p = p_tileno % p_cp->tw;
+    q = p_tileno / p_cp->tw;
+
+    /* find extent of tile */
+    l_tx0 = p_cp->tx0 + p *
+            p_cp->tdx; /* can't be greater than p_image->x1 so won't overflow */
+    *p_tx0 = opj_uint_max(l_tx0, p_image->x0);
+    *p_tx1 = opj_uint_min(opj_uint_adds(l_tx0, p_cp->tdx), p_image->x1);
+    l_ty0 = p_cp->ty0 + q *
+            p_cp->tdy; /* can't be greater than p_image->y1 so won't overflow */
+    *p_ty0 = opj_uint_max(l_ty0, p_image->y0);
+    *p_ty1 = opj_uint_min(opj_uint_adds(l_ty0, p_cp->tdy), p_image->y1);
+
+    /* max precision is 0 (can only grow) */
+    *p_max_prec = 0;
+    *p_max_res = 0;
+
+    /* take the largest value for dx_min and dy_min */
+    *p_dx_min = 0x7fffffff;
+    *p_dy_min  = 0x7fffffff;
+
+    for (compno = 0; compno < p_image->numcomps; ++compno) {
+        /* arithmetic variables to calculate */
+        OPJ_UINT32 l_level_no;
+        OPJ_UINT32 l_rx0, l_ry0, l_rx1, l_ry1;
+        OPJ_UINT32 l_px0, l_py0, l_px1, py1;
+        OPJ_UINT32 l_pdx, l_pdy;
+        OPJ_UINT32 l_pw, l_ph;
+        OPJ_UINT32 l_product;
+        OPJ_UINT32 l_tcx0, l_tcy0, l_tcx1, l_tcy1;
+
+        l_tcx0 = opj_uint_ceildiv(*p_tx0, l_img_comp->dx);
+        l_tcy0 = opj_uint_ceildiv(*p_ty0, l_img_comp->dy);
+        l_tcx1 = opj_uint_ceildiv(*p_tx1, l_img_comp->dx);
+        l_tcy1 = opj_uint_ceildiv(*p_ty1, l_img_comp->dy);
+
+        if (l_tccp->numresolutions > *p_max_res) {
+            *p_max_res = l_tccp->numresolutions;
+        }
+
+        /* use custom size for precincts */
+        for (resno = 0; resno < l_tccp->numresolutions; ++resno) {
+            OPJ_UINT32 l_dx, l_dy;
+
+            /* precinct width and height */
+            l_pdx = l_tccp->prcw[resno];
+            l_pdy = l_tccp->prch[resno];
+
+            l_dx = l_img_comp->dx * (1u << (l_pdx + l_tccp->numresolutions - 1 - resno));
+            l_dy = l_img_comp->dy * (1u << (l_pdy + l_tccp->numresolutions - 1 - resno));
+
+            /* take the minimum size for dx for each comp and resolution */
+            *p_dx_min = opj_uint_min(*p_dx_min, l_dx);
+            *p_dy_min = opj_uint_min(*p_dy_min, l_dy);
+
+            /* various calculations of extents */
+            l_level_no = l_tccp->numresolutions - 1 - resno;
+
+            l_rx0 = opj_uint_ceildivpow2(l_tcx0, l_level_no);
+            l_ry0 = opj_uint_ceildivpow2(l_tcy0, l_level_no);
+            l_rx1 = opj_uint_ceildivpow2(l_tcx1, l_level_no);
+            l_ry1 = opj_uint_ceildivpow2(l_tcy1, l_level_no);
+
+            l_px0 = opj_uint_floordivpow2(l_rx0, l_pdx) << l_pdx;
+            l_py0 = opj_uint_floordivpow2(l_ry0, l_pdy) << l_pdy;
+            l_px1 = opj_uint_ceildivpow2(l_rx1, l_pdx) << l_pdx;
+
+            py1 = opj_uint_ceildivpow2(l_ry1, l_pdy) << l_pdy;
+
+            l_pw = (l_rx0 == l_rx1) ? 0 : ((l_px1 - l_px0) >> l_pdx);
+            l_ph = (l_ry0 == l_ry1) ? 0 : ((py1 - l_py0) >> l_pdy);
+
+            l_product = l_pw * l_ph;
+
+            /* update precision */
+            if (l_product > *p_max_prec) {
+                *p_max_prec = l_product;
+            }
+        }
+        ++l_img_comp;
+        ++l_tccp;
+    }
+}
+
+
+static void opj_get_all_encoding_parameters(const opj_image_t *p_image,
+        const opj_cp_t *p_cp,
+        OPJ_UINT32 tileno,
+        OPJ_UINT32 * p_tx0,
+        OPJ_UINT32 * p_tx1,
+        OPJ_UINT32 * p_ty0,
+        OPJ_UINT32 * p_ty1,
+        OPJ_UINT32 * p_dx_min,
+        OPJ_UINT32 * p_dy_min,
+        OPJ_UINT32 * p_max_prec,
+        OPJ_UINT32 * p_max_res,
+        OPJ_UINT32 ** p_resolutions)
+{
+    /* loop*/
+    OPJ_UINT32 compno, resno;
+
+    /* pointers*/
+    const opj_tcp_t *tcp = 00;
+    const opj_tccp_t * l_tccp = 00;
+    const opj_image_comp_t * l_img_comp = 00;
+
+    /* to store l_dx, l_dy, w and h for each resolution and component.*/
+    OPJ_UINT32 * lResolutionPtr;
+
+    /* position in x and y of tile*/
+    OPJ_UINT32 p, q;
+
+    /* non-corrected (in regard to image offset) tile offset */
+    OPJ_UINT32 l_tx0, l_ty0;
+
+    /* preconditions in debug*/
+    assert(p_cp != 00);
+    assert(p_image != 00);
+    assert(tileno < p_cp->tw * p_cp->th);
+
+    /* initializations*/
+    tcp = &p_cp->tcps [tileno];
+    l_tccp = tcp->tccps;
+    l_img_comp = p_image->comps;
+
+    /* position in x and y of tile*/
+    p = tileno % p_cp->tw;
+    q = tileno / p_cp->tw;
+
+    /* here calculation of tx0, tx1, ty0, ty1, maxprec, l_dx and l_dy */
+    l_tx0 = p_cp->tx0 + p *
+            p_cp->tdx; /* can't be greater than p_image->x1 so won't overflow */
+    *p_tx0 = opj_uint_max(l_tx0, p_image->x0);
+    *p_tx1 = opj_uint_min(opj_uint_adds(l_tx0, p_cp->tdx), p_image->x1);
+    l_ty0 = p_cp->ty0 + q *
+            p_cp->tdy; /* can't be greater than p_image->y1 so won't overflow */
+    *p_ty0 = opj_uint_max(l_ty0, p_image->y0);
+    *p_ty1 = opj_uint_min(opj_uint_adds(l_ty0, p_cp->tdy), p_image->y1);
+
+    /* max precision and resolution is 0 (can only grow)*/
+    *p_max_prec = 0;
+    *p_max_res = 0;
+
+    /* take the largest value for dx_min and dy_min*/
+    *p_dx_min = 0x7fffffff;
+    *p_dy_min = 0x7fffffff;
+
+    for (compno = 0; compno < p_image->numcomps; ++compno) {
+        /* arithmetic variables to calculate*/
+        OPJ_UINT32 l_level_no;
+        OPJ_UINT32 l_rx0, l_ry0, l_rx1, l_ry1;
+        OPJ_UINT32 l_px0, l_py0, l_px1, py1;
+        OPJ_UINT32 l_product;
+        OPJ_UINT32 l_tcx0, l_tcy0, l_tcx1, l_tcy1;
+        OPJ_UINT32 l_pdx, l_pdy, l_pw, l_ph;
+
+        lResolutionPtr = p_resolutions ? p_resolutions[compno] : NULL;
+
+        l_tcx0 = opj_uint_ceildiv(*p_tx0, l_img_comp->dx);
+        l_tcy0 = opj_uint_ceildiv(*p_ty0, l_img_comp->dy);
+        l_tcx1 = opj_uint_ceildiv(*p_tx1, l_img_comp->dx);
+        l_tcy1 = opj_uint_ceildiv(*p_ty1, l_img_comp->dy);
+
+        if (l_tccp->numresolutions > *p_max_res) {
+            *p_max_res = l_tccp->numresolutions;
+        }
+
+        /* use custom size for precincts*/
+        l_level_no = l_tccp->numresolutions;
+        for (resno = 0; resno < l_tccp->numresolutions; ++resno) {
+            OPJ_UINT32 l_dx, l_dy;
+
+            --l_level_no;
+
+            /* precinct width and height*/
+            l_pdx = l_tccp->prcw[resno];
+            l_pdy = l_tccp->prch[resno];
+            if (lResolutionPtr) {
+                *lResolutionPtr++ = l_pdx;
+                *lResolutionPtr++ = l_pdy;
+            }
+            if (l_pdx + l_level_no < 32 &&
+                    l_img_comp->dx <= UINT_MAX / (1u << (l_pdx + l_level_no))) {
+                l_dx = l_img_comp->dx * (1u << (l_pdx + l_level_no));
+                /* take the minimum size for l_dx for each comp and resolution*/
+                *p_dx_min = opj_uint_min(*p_dx_min, l_dx);
+            }
+            if (l_pdy + l_level_no < 32 &&
+                    l_img_comp->dy <= UINT_MAX / (1u << (l_pdy + l_level_no))) {
+                l_dy = l_img_comp->dy * (1u << (l_pdy + l_level_no));
+                *p_dy_min = opj_uint_min(*p_dy_min, l_dy);
+            }
+
+            /* various calculations of extents*/
+            l_rx0 = opj_uint_ceildivpow2(l_tcx0, l_level_no);
+            l_ry0 = opj_uint_ceildivpow2(l_tcy0, l_level_no);
+            l_rx1 = opj_uint_ceildivpow2(l_tcx1, l_level_no);
+            l_ry1 = opj_uint_ceildivpow2(l_tcy1, l_level_no);
+            l_px0 = opj_uint_floordivpow2(l_rx0, l_pdx) << l_pdx;
+            l_py0 = opj_uint_floordivpow2(l_ry0, l_pdy) << l_pdy;
+            l_px1 = opj_uint_ceildivpow2(l_rx1, l_pdx) << l_pdx;
+            py1 = opj_uint_ceildivpow2(l_ry1, l_pdy) << l_pdy;
+            l_pw = (l_rx0 == l_rx1) ? 0 : ((l_px1 - l_px0) >> l_pdx);
+            l_ph = (l_ry0 == l_ry1) ? 0 : ((py1 - l_py0) >> l_pdy);
+            if (lResolutionPtr) {
+                *lResolutionPtr++ = l_pw;
+                *lResolutionPtr++ = l_ph;
+            }
+            l_product = l_pw * l_ph;
+
+            /* update precision*/
+            if (l_product > *p_max_prec) {
+                *p_max_prec = l_product;
+            }
+
+        }
+        ++l_tccp;
+        ++l_img_comp;
+    }
+}
+
+static opj_pi_iterator_t * opj_pi_create(const opj_image_t *image,
+        const opj_cp_t *cp,
+        OPJ_UINT32 tileno,
+        opj_event_mgr_t* manager)
+{
+    /* loop*/
+    OPJ_UINT32 pino, compno;
+    /* number of poc in the p_pi*/
+    OPJ_UINT32 l_poc_bound;
+
+    /* pointers to tile coding parameters and components.*/
+    opj_pi_iterator_t *l_pi = 00;
+    opj_tcp_t *tcp = 00;
+    const opj_tccp_t *tccp = 00;
+
+    /* current packet iterator being allocated*/
+    opj_pi_iterator_t *l_current_pi = 00;
+
+    /* preconditions in debug*/
+    assert(cp != 00);
+    assert(image != 00);
+    assert(tileno < cp->tw * cp->th);
+
+    /* initializations*/
+    tcp = &cp->tcps[tileno];
+    l_poc_bound = tcp->numpocs + 1;
+
+    /* memory allocations*/
+    l_pi = (opj_pi_iterator_t*) opj_calloc((l_poc_bound),
+                                           sizeof(opj_pi_iterator_t));
+    if (!l_pi) {
+        return NULL;
+    }
+
+    l_current_pi = l_pi;
+    for (pino = 0; pino < l_poc_bound ; ++pino) {
+
+        l_current_pi->manager = manager;
+
+        l_current_pi->comps = (opj_pi_comp_t*) opj_calloc(image->numcomps,
+                              sizeof(opj_pi_comp_t));
+        if (! l_current_pi->comps) {
+            opj_pi_destroy(l_pi, l_poc_bound);
+            return NULL;
+        }
+
+        l_current_pi->numcomps = image->numcomps;
+
+        for (compno = 0; compno < image->numcomps; ++compno) {
+            opj_pi_comp_t *comp = &l_current_pi->comps[compno];
+
+            tccp = &tcp->tccps[compno];
+
+            comp->resolutions = (opj_pi_resolution_t*) opj_calloc(tccp->numresolutions,
+                                sizeof(opj_pi_resolution_t));
+            if (!comp->resolutions) {
+                opj_pi_destroy(l_pi, l_poc_bound);
+                return 00;
+            }
+
+            comp->numresolutions = tccp->numresolutions;
+        }
+        ++l_current_pi;
+    }
+    return l_pi;
+}
+
+static void opj_pi_update_encode_poc_and_final(opj_cp_t *p_cp,
+        OPJ_UINT32 p_tileno,
+        OPJ_UINT32 p_tx0,
+        OPJ_UINT32 p_tx1,
+        OPJ_UINT32 p_ty0,
+        OPJ_UINT32 p_ty1,
+        OPJ_UINT32 p_max_prec,
+        OPJ_UINT32 p_max_res,
+        OPJ_UINT32 p_dx_min,
+        OPJ_UINT32 p_dy_min)
+{
+    /* loop*/
+    OPJ_UINT32 pino;
+    /* tile coding parameter*/
+    opj_tcp_t *l_tcp = 00;
+    /* current poc being updated*/
+    opj_poc_t * l_current_poc = 00;
+
+    /* number of pocs*/
+    OPJ_UINT32 l_poc_bound;
+
+    OPJ_ARG_NOT_USED(p_max_res);
+
+    /* preconditions in debug*/
+    assert(p_cp != 00);
+    assert(p_tileno < p_cp->tw * p_cp->th);
+
+    /* initializations*/
+    l_tcp = &p_cp->tcps [p_tileno];
+    /* number of iterations in the loop */
+    l_poc_bound = l_tcp->numpocs + 1;
+
+    /* start at first element, and to make sure the compiler will not make a calculation each time in the loop
+       store a pointer to the current element to modify rather than l_tcp->pocs[i]*/
+    l_current_poc = l_tcp->pocs;
+
+    l_current_poc->compS = l_current_poc->compno0;
+    l_current_poc->compE = l_current_poc->compno1;
+    l_current_poc->resS = l_current_poc->resno0;
+    l_current_poc->resE = l_current_poc->resno1;
+    l_current_poc->layE = l_current_poc->layno1;
+
+    /* special treatment for the first element*/
+    l_current_poc->layS = 0;
+    l_current_poc->prg  = l_current_poc->prg1;
+    l_current_poc->prcS = 0;
+
+    l_current_poc->prcE = p_max_prec;
+    l_current_poc->txS = (OPJ_UINT32)p_tx0;
+    l_current_poc->txE = (OPJ_UINT32)p_tx1;
+    l_current_poc->tyS = (OPJ_UINT32)p_ty0;
+    l_current_poc->tyE = (OPJ_UINT32)p_ty1;
+    l_current_poc->dx = p_dx_min;
+    l_current_poc->dy = p_dy_min;
+
+    ++ l_current_poc;
+    for (pino = 1; pino < l_poc_bound ; ++pino) {
+        l_current_poc->compS = l_current_poc->compno0;
+        l_current_poc->compE = l_current_poc->compno1;
+        l_current_poc->resS = l_current_poc->resno0;
+        l_current_poc->resE = l_current_poc->resno1;
+        l_current_poc->layE = l_current_poc->layno1;
+        l_current_poc->prg  = l_current_poc->prg1;
+        l_current_poc->prcS = 0;
+        /* special treatment here different from the first element*/
+        l_current_poc->layS = (l_current_poc->layE > (l_current_poc - 1)->layE) ?
+                              l_current_poc->layE : 0;
+
+        l_current_poc->prcE = p_max_prec;
+        l_current_poc->txS = (OPJ_UINT32)p_tx0;
+        l_current_poc->txE = (OPJ_UINT32)p_tx1;
+        l_current_poc->tyS = (OPJ_UINT32)p_ty0;
+        l_current_poc->tyE = (OPJ_UINT32)p_ty1;
+        l_current_poc->dx = p_dx_min;
+        l_current_poc->dy = p_dy_min;
+        ++ l_current_poc;
+    }
+}
+
+static void opj_pi_update_encode_not_poc(opj_cp_t *p_cp,
+        OPJ_UINT32 p_num_comps,
+        OPJ_UINT32 p_tileno,
+        OPJ_UINT32 p_tx0,
+        OPJ_UINT32 p_tx1,
+        OPJ_UINT32 p_ty0,
+        OPJ_UINT32 p_ty1,
+        OPJ_UINT32 p_max_prec,
+        OPJ_UINT32 p_max_res,
+        OPJ_UINT32 p_dx_min,
+        OPJ_UINT32 p_dy_min)
+{
+    /* loop*/
+    OPJ_UINT32 pino;
+    /* tile coding parameter*/
+    opj_tcp_t *l_tcp = 00;
+    /* current poc being updated*/
+    opj_poc_t * l_current_poc = 00;
+    /* number of pocs*/
+    OPJ_UINT32 l_poc_bound;
+
+    /* preconditions in debug*/
+    assert(p_cp != 00);
+    assert(p_tileno < p_cp->tw * p_cp->th);
+
+    /* initializations*/
+    l_tcp = &p_cp->tcps [p_tileno];
+
+    /* number of iterations in the loop */
+    l_poc_bound = l_tcp->numpocs + 1;
+
+    /* start at first element, and to make sure the compiler will not make a calculation each time in the loop
+       store a pointer to the current element to modify rather than l_tcp->pocs[i]*/
+    l_current_poc = l_tcp->pocs;
+
+    for (pino = 0; pino < l_poc_bound ; ++pino) {
+        l_current_poc->compS = 0;
+        l_current_poc->compE = p_num_comps;/*p_image->numcomps;*/
+        l_current_poc->resS = 0;
+        l_current_poc->resE = p_max_res;
+        l_current_poc->layS = 0;
+        l_current_poc->layE = l_tcp->numlayers;
+        l_current_poc->prg  = l_tcp->prg;
+        l_current_poc->prcS = 0;
+        l_current_poc->prcE = p_max_prec;
+        l_current_poc->txS = p_tx0;
+        l_current_poc->txE = p_tx1;
+        l_current_poc->tyS = p_ty0;
+        l_current_poc->tyE = p_ty1;
+        l_current_poc->dx = p_dx_min;
+        l_current_poc->dy = p_dy_min;
+        ++ l_current_poc;
+    }
+}
+
+static void opj_pi_update_decode_poc(opj_pi_iterator_t * p_pi,
+                                     opj_tcp_t * p_tcp,
+                                     OPJ_UINT32 p_max_precision,
+                                     OPJ_UINT32 p_max_res)
+{
+    /* loop*/
+    OPJ_UINT32 pino;
+
+    /* encoding parameters to set*/
+    OPJ_UINT32 l_bound;
+
+    opj_pi_iterator_t * l_current_pi = 00;
+    opj_poc_t* l_current_poc = 0;
+
+    OPJ_ARG_NOT_USED(p_max_res);
+
+    /* preconditions in debug*/
+    assert(p_pi != 00);
+    assert(p_tcp != 00);
+
+    /* initializations*/
+    l_bound = p_tcp->numpocs + 1;
+    l_current_pi = p_pi;
+    l_current_poc = p_tcp->pocs;
+
+    for (pino = 0; pino < l_bound; ++pino) {
+        l_current_pi->poc.prg = l_current_poc->prg; /* Progression Order #0 */
+        l_current_pi->first = 1;
+
+        l_current_pi->poc.resno0 =
+            l_current_poc->resno0; /* Resolution Level Index #0 (Start) */
+        l_current_pi->poc.compno0 =
+            l_current_poc->compno0; /* Component Index #0 (Start) */
+        l_current_pi->poc.layno0 = 0;
+        l_current_pi->poc.precno0 = 0;
+        l_current_pi->poc.resno1 =
+            l_current_poc->resno1; /* Resolution Level Index #0 (End) */
+        l_current_pi->poc.compno1 =
+            l_current_poc->compno1; /* Component Index #0 (End) */
+        l_current_pi->poc.layno1 = opj_uint_min(l_current_poc->layno1,
+                                                p_tcp->numlayers); /* Layer Index #0 (End) */
+        l_current_pi->poc.precno1 = p_max_precision;
+        ++l_current_pi;
+        ++l_current_poc;
+    }
+}
+
+static void opj_pi_update_decode_not_poc(opj_pi_iterator_t * p_pi,
+        opj_tcp_t * p_tcp,
+        OPJ_UINT32 p_max_precision,
+        OPJ_UINT32 p_max_res)
+{
+    /* loop*/
+    OPJ_UINT32 pino;
+
+    /* encoding parameters to set*/
+    OPJ_UINT32 l_bound;
+
+    opj_pi_iterator_t * l_current_pi = 00;
+    /* preconditions in debug*/
+    assert(p_tcp != 00);
+    assert(p_pi != 00);
+
+    /* initializations*/
+    l_bound = p_tcp->numpocs + 1;
+    l_current_pi = p_pi;
+
+    for (pino = 0; pino < l_bound; ++pino) {
+        l_current_pi->poc.prg = p_tcp->prg;
+        l_current_pi->first = 1;
+        l_current_pi->poc.resno0 = 0;
+        l_current_pi->poc.compno0 = 0;
+        l_current_pi->poc.layno0 = 0;
+        l_current_pi->poc.precno0 = 0;
+        l_current_pi->poc.resno1 = p_max_res;
+        l_current_pi->poc.compno1 = l_current_pi->numcomps;
+        l_current_pi->poc.layno1 = p_tcp->numlayers;
+        l_current_pi->poc.precno1 = p_max_precision;
+        ++l_current_pi;
+    }
+}
+
+
+
+static OPJ_BOOL opj_pi_check_next_level(OPJ_INT32 pos,
+                                        opj_cp_t *cp,
+                                        OPJ_UINT32 tileno,
+                                        OPJ_UINT32 pino,
+                                        const OPJ_CHAR *prog)
+{
+    OPJ_INT32 i;
+    opj_tcp_t *tcps = &cp->tcps[tileno];
+    opj_poc_t *tcp = &tcps->pocs[pino];
+
+    if (pos >= 0) {
+        for (i = pos; i >= 0; i--) {
+            switch (prog[i]) {
+            case 'R':
+                if (tcp->res_t == tcp->resE) {
+                    if (opj_pi_check_next_level(pos - 1, cp, tileno, pino, prog)) {
+                        return OPJ_TRUE;
+                    } else {
+                        return OPJ_FALSE;
+                    }
+                } else {
+                    return OPJ_TRUE;
+                }
+                break;
+            case 'C':
+                if (tcp->comp_t == tcp->compE) {
+                    if (opj_pi_check_next_level(pos - 1, cp, tileno, pino, prog)) {
+                        return OPJ_TRUE;
+                    } else {
+                        return OPJ_FALSE;
+                    }
+                } else {
+                    return OPJ_TRUE;
+                }
+                break;
+            case 'L':
+                if (tcp->lay_t == tcp->layE) {
+                    if (opj_pi_check_next_level(pos - 1, cp, tileno, pino, prog)) {
+                        return OPJ_TRUE;
+                    } else {
+                        return OPJ_FALSE;
+                    }
+                } else {
+                    return OPJ_TRUE;
+                }
+                break;
+            case 'P':
+                switch (tcp->prg) {
+                case OPJ_LRCP: /* fall through */
+                case OPJ_RLCP:
+                    if (tcp->prc_t == tcp->prcE) {
+                        if (opj_pi_check_next_level(i - 1, cp, tileno, pino, prog)) {
+                            return OPJ_TRUE;
+                        } else {
+                            return OPJ_FALSE;
+                        }
+                    } else {
+                        return OPJ_TRUE;
+                    }
+                    break;
+                default:
+                    if (tcp->tx0_t == tcp->txE) {
+                        /*TY*/
+                        if (tcp->ty0_t == tcp->tyE) {
+                            if (opj_pi_check_next_level(i - 1, cp, tileno, pino, prog)) {
+                                return OPJ_TRUE;
+                            } else {
+                                return OPJ_FALSE;
+                            }
+                        } else {
+                            return OPJ_TRUE;
+                        }/*TY*/
+                    } else {
+                        return OPJ_TRUE;
+                    }
+                    break;
+                }/*end case P*/
+            }/*end switch*/
+        }/*end for*/
+    }/*end if*/
+    return OPJ_FALSE;
+}
+
+
+/*
+==========================================================
+   Packet iterator interface
+==========================================================
+*/
+opj_pi_iterator_t *opj_pi_create_decode(opj_image_t *p_image,
+                                        opj_cp_t *p_cp,
+                                        OPJ_UINT32 p_tile_no,
+                                        opj_event_mgr_t* manager)
+{
+    OPJ_UINT32 numcomps = p_image->numcomps;
+
+    /* loop */
+    OPJ_UINT32 pino;
+    OPJ_UINT32 compno, resno;
+
+    /* to store w, h, dx and dy for all components and resolutions */
+    OPJ_UINT32 * l_tmp_data;
+    OPJ_UINT32 ** l_tmp_ptr;
+
+    /* encoding parameters to set */
+    OPJ_UINT32 l_max_res;
+    OPJ_UINT32 l_max_prec;
+    OPJ_UINT32 l_tx0, l_tx1, l_ty0, l_ty1;
+    OPJ_UINT32 l_dx_min, l_dy_min;
+    OPJ_UINT32 l_bound;
+    OPJ_UINT32 l_step_p, l_step_c, l_step_r, l_step_l ;
+    OPJ_UINT32 l_data_stride;
+
+    /* pointers */
+    opj_pi_iterator_t *l_pi = 00;
+    opj_tcp_t *l_tcp = 00;
+    const opj_tccp_t *l_tccp = 00;
+    opj_pi_comp_t *l_current_comp = 00;
+    opj_image_comp_t * l_img_comp = 00;
+    opj_pi_iterator_t * l_current_pi = 00;
+    OPJ_UINT32 * l_encoding_value_ptr = 00;
+
+    /* preconditions in debug */
+    assert(p_cp != 00);
+    assert(p_image != 00);
+    assert(p_tile_no < p_cp->tw * p_cp->th);
+
+    /* initializations */
+    l_tcp = &p_cp->tcps[p_tile_no];
+    l_bound = l_tcp->numpocs + 1;
+
+    l_data_stride = 4 * OPJ_J2K_MAXRLVLS;
+    l_tmp_data = (OPJ_UINT32*)opj_malloc(
+                     l_data_stride * numcomps * sizeof(OPJ_UINT32));
+    if
+    (! l_tmp_data) {
+        return 00;
+    }
+    l_tmp_ptr = (OPJ_UINT32**)opj_malloc(
+                    numcomps * sizeof(OPJ_UINT32 *));
+    if
+    (! l_tmp_ptr) {
+        opj_free(l_tmp_data);
+        return 00;
+    }
+
+    /* memory allocation for pi */
+    l_pi = opj_pi_create(p_image, p_cp, p_tile_no, manager);
+    if (!l_pi) {
+        opj_free(l_tmp_data);
+        opj_free(l_tmp_ptr);
+        return 00;
+    }
+
+    l_encoding_value_ptr = l_tmp_data;
+    /* update pointer array */
+    for
+    (compno = 0; compno < numcomps; ++compno) {
+        l_tmp_ptr[compno] = l_encoding_value_ptr;
+        l_encoding_value_ptr += l_data_stride;
+    }
+    /* get encoding parameters */
+    opj_get_all_encoding_parameters(p_image, p_cp, p_tile_no, &l_tx0, &l_tx1,
+                                    &l_ty0, &l_ty1, &l_dx_min, &l_dy_min, &l_max_prec, &l_max_res, l_tmp_ptr);
+
+    /* step calculations */
+    l_step_p = 1;
+    l_step_c = l_max_prec * l_step_p;
+    l_step_r = numcomps * l_step_c;
+    l_step_l = l_max_res * l_step_r;
+
+    /* set values for first packet iterator */
+    l_current_pi = l_pi;
+
+    /* memory allocation for include */
+    /* prevent an integer overflow issue */
+    /* 0 < l_tcp->numlayers < 65536 c.f. opj_j2k_read_cod in j2k.c */
+    l_current_pi->include = 00;
+    if (l_step_l <= (UINT_MAX / (l_tcp->numlayers + 1U))) {
+        l_current_pi->include_size = (l_tcp->numlayers + 1U) * l_step_l;
+        l_current_pi->include = (OPJ_INT16*) opj_calloc(
+                                    l_current_pi->include_size, sizeof(OPJ_INT16));
+    }
+
+    if (!l_current_pi->include) {
+        opj_free(l_tmp_data);
+        opj_free(l_tmp_ptr);
+        opj_pi_destroy(l_pi, l_bound);
+        return 00;
+    }
+
+    /* special treatment for the first packet iterator */
+    l_current_comp = l_current_pi->comps;
+    l_img_comp = p_image->comps;
+    l_tccp = l_tcp->tccps;
+
+    l_current_pi->tx0 = l_tx0;
+    l_current_pi->ty0 = l_ty0;
+    l_current_pi->tx1 = l_tx1;
+    l_current_pi->ty1 = l_ty1;
+
+    /*l_current_pi->dx = l_img_comp->dx;*/
+    /*l_current_pi->dy = l_img_comp->dy;*/
+
+    l_current_pi->step_p = l_step_p;
+    l_current_pi->step_c = l_step_c;
+    l_current_pi->step_r = l_step_r;
+    l_current_pi->step_l = l_step_l;
+
+    /* allocation for components and number of components has already been calculated by opj_pi_create */
+    for
+    (compno = 0; compno < numcomps; ++compno) {
+        opj_pi_resolution_t *l_res = l_current_comp->resolutions;
+        l_encoding_value_ptr = l_tmp_ptr[compno];
+
+        l_current_comp->dx = l_img_comp->dx;
+        l_current_comp->dy = l_img_comp->dy;
+        /* resolutions have already been initialized */
+        for
+        (resno = 0; resno < l_current_comp->numresolutions; resno++) {
+            l_res->pdx = *(l_encoding_value_ptr++);
+            l_res->pdy = *(l_encoding_value_ptr++);
+            l_res->pw =  *(l_encoding_value_ptr++);
+            l_res->ph =  *(l_encoding_value_ptr++);
+            ++l_res;
+        }
+        ++l_current_comp;
+        ++l_img_comp;
+        ++l_tccp;
+    }
+    ++l_current_pi;
+
+    for (pino = 1 ; pino < l_bound ; ++pino) {
+        l_current_comp = l_current_pi->comps;
+        l_img_comp = p_image->comps;
+        l_tccp = l_tcp->tccps;
+
+        l_current_pi->tx0 = l_tx0;
+        l_current_pi->ty0 = l_ty0;
+        l_current_pi->tx1 = l_tx1;
+        l_current_pi->ty1 = l_ty1;
+        /*l_current_pi->dx = l_dx_min;*/
+        /*l_current_pi->dy = l_dy_min;*/
+        l_current_pi->step_p = l_step_p;
+        l_current_pi->step_c = l_step_c;
+        l_current_pi->step_r = l_step_r;
+        l_current_pi->step_l = l_step_l;
+
+        /* allocation for components and number of components has already been calculated by opj_pi_create */
+        for
+        (compno = 0; compno < numcomps; ++compno) {
+            opj_pi_resolution_t *l_res = l_current_comp->resolutions;
+            l_encoding_value_ptr = l_tmp_ptr[compno];
+
+            l_current_comp->dx = l_img_comp->dx;
+            l_current_comp->dy = l_img_comp->dy;
+            /* resolutions have already been initialized */
+            for
+            (resno = 0; resno < l_current_comp->numresolutions; resno++) {
+                l_res->pdx = *(l_encoding_value_ptr++);
+                l_res->pdy = *(l_encoding_value_ptr++);
+                l_res->pw =  *(l_encoding_value_ptr++);
+                l_res->ph =  *(l_encoding_value_ptr++);
+                ++l_res;
+            }
+            ++l_current_comp;
+            ++l_img_comp;
+            ++l_tccp;
+        }
+        /* special treatment*/
+        l_current_pi->include = (l_current_pi - 1)->include;
+        l_current_pi->include_size = (l_current_pi - 1)->include_size;
+        ++l_current_pi;
+    }
+    opj_free(l_tmp_data);
+    l_tmp_data = 00;
+    opj_free(l_tmp_ptr);
+    l_tmp_ptr = 00;
+    if
+    (l_tcp->POC) {
+        opj_pi_update_decode_poc(l_pi, l_tcp, l_max_prec, l_max_res);
+    } else {
+        opj_pi_update_decode_not_poc(l_pi, l_tcp, l_max_prec, l_max_res);
+    }
+    return l_pi;
+}
+
+
+OPJ_UINT32 opj_get_encoding_packet_count(const opj_image_t *p_image,
+        const opj_cp_t *p_cp,
+        OPJ_UINT32 p_tile_no)
+{
+    OPJ_UINT32 l_max_res;
+    OPJ_UINT32 l_max_prec;
+    OPJ_UINT32 l_tx0, l_tx1, l_ty0, l_ty1;
+    OPJ_UINT32 l_dx_min, l_dy_min;
+
+    /* preconditions in debug*/
+    assert(p_cp != 00);
+    assert(p_image != 00);
+    assert(p_tile_no < p_cp->tw * p_cp->th);
+
+    /* get encoding parameters*/
+    opj_get_all_encoding_parameters(p_image, p_cp, p_tile_no, &l_tx0, &l_tx1,
+                                    &l_ty0, &l_ty1, &l_dx_min, &l_dy_min, &l_max_prec, &l_max_res, NULL);
+
+    return p_cp->tcps[p_tile_no].numlayers * l_max_prec * p_image->numcomps *
+           l_max_res;
+}
+
+
+opj_pi_iterator_t *opj_pi_initialise_encode(const opj_image_t *p_image,
+        opj_cp_t *p_cp,
+        OPJ_UINT32 p_tile_no,
+        J2K_T2_MODE p_t2_mode,
+        opj_event_mgr_t* manager)
+{
+    OPJ_UINT32 numcomps = p_image->numcomps;
+
+    /* loop*/
+    OPJ_UINT32 pino;
+    OPJ_UINT32 compno, resno;
+
+    /* to store w, h, dx and dy for all components and resolutions*/
+    OPJ_UINT32 * l_tmp_data;
+    OPJ_UINT32 ** l_tmp_ptr;
+
+    /* encoding parameters to set*/
+    OPJ_UINT32 l_max_res;
+    OPJ_UINT32 l_max_prec;
+    OPJ_UINT32 l_tx0, l_tx1, l_ty0, l_ty1;
+    OPJ_UINT32 l_dx_min, l_dy_min;
+    OPJ_UINT32 l_bound;
+    OPJ_UINT32 l_step_p, l_step_c, l_step_r, l_step_l ;
+    OPJ_UINT32 l_data_stride;
+
+    /* pointers*/
+    opj_pi_iterator_t *l_pi = 00;
+    opj_tcp_t *l_tcp = 00;
+    const opj_tccp_t *l_tccp = 00;
+    opj_pi_comp_t *l_current_comp = 00;
+    opj_image_comp_t * l_img_comp = 00;
+    opj_pi_iterator_t * l_current_pi = 00;
+    OPJ_UINT32 * l_encoding_value_ptr = 00;
+
+    /* preconditions in debug*/
+    assert(p_cp != 00);
+    assert(p_image != 00);
+    assert(p_tile_no < p_cp->tw * p_cp->th);
+
+    /* initializations*/
+    l_tcp = &p_cp->tcps[p_tile_no];
+    l_bound = l_tcp->numpocs + 1;
+
+    l_data_stride = 4 * OPJ_J2K_MAXRLVLS;
+    l_tmp_data = (OPJ_UINT32*)opj_malloc(
+                     l_data_stride * numcomps * sizeof(OPJ_UINT32));
+    if (! l_tmp_data) {
+        return 00;
+    }
+
+    l_tmp_ptr = (OPJ_UINT32**)opj_malloc(
+                    numcomps * sizeof(OPJ_UINT32 *));
+    if (! l_tmp_ptr) {
+        opj_free(l_tmp_data);
+        return 00;
+    }
+
+    /* memory allocation for pi*/
+    l_pi = opj_pi_create(p_image, p_cp, p_tile_no, manager);
+    if (!l_pi) {
+        opj_free(l_tmp_data);
+        opj_free(l_tmp_ptr);
+        return 00;
+    }
+
+    l_encoding_value_ptr = l_tmp_data;
+    /* update pointer array*/
+    for (compno = 0; compno < numcomps; ++compno) {
+        l_tmp_ptr[compno] = l_encoding_value_ptr;
+        l_encoding_value_ptr += l_data_stride;
+    }
+
+    /* get encoding parameters*/
+    opj_get_all_encoding_parameters(p_image, p_cp, p_tile_no, &l_tx0, &l_tx1,
+                                    &l_ty0, &l_ty1, &l_dx_min, &l_dy_min, &l_max_prec, &l_max_res, l_tmp_ptr);
+
+    /* step calculations*/
+    l_step_p = 1;
+    l_step_c = l_max_prec * l_step_p;
+    l_step_r = numcomps * l_step_c;
+    l_step_l = l_max_res * l_step_r;
+
+    /* set values for first packet iterator*/
+    l_pi->tp_on = (OPJ_BYTE)p_cp->m_specific_param.m_enc.m_tp_on;
+    l_current_pi = l_pi;
+
+    /* memory allocation for include*/
+    l_current_pi->include_size = l_tcp->numlayers * l_step_l;
+    l_current_pi->include = (OPJ_INT16*) opj_calloc(l_current_pi->include_size,
+                            sizeof(OPJ_INT16));
+    if (!l_current_pi->include) {
+        opj_free(l_tmp_data);
+        opj_free(l_tmp_ptr);
+        opj_pi_destroy(l_pi, l_bound);
+        return 00;
+    }
+
+    /* special treatment for the first packet iterator*/
+    l_current_comp = l_current_pi->comps;
+    l_img_comp = p_image->comps;
+    l_tccp = l_tcp->tccps;
+    l_current_pi->tx0 = l_tx0;
+    l_current_pi->ty0 = l_ty0;
+    l_current_pi->tx1 = l_tx1;
+    l_current_pi->ty1 = l_ty1;
+    l_current_pi->dx = l_dx_min;
+    l_current_pi->dy = l_dy_min;
+    l_current_pi->step_p = l_step_p;
+    l_current_pi->step_c = l_step_c;
+    l_current_pi->step_r = l_step_r;
+    l_current_pi->step_l = l_step_l;
+
+    /* allocation for components and number of components has already been calculated by opj_pi_create */
+    for (compno = 0; compno < numcomps; ++compno) {
+        opj_pi_resolution_t *l_res = l_current_comp->resolutions;
+        l_encoding_value_ptr = l_tmp_ptr[compno];
+
+        l_current_comp->dx = l_img_comp->dx;
+        l_current_comp->dy = l_img_comp->dy;
+
+        /* resolutions have already been initialized */
+        for (resno = 0; resno < l_current_comp->numresolutions; resno++) {
+            l_res->pdx = *(l_encoding_value_ptr++);
+            l_res->pdy = *(l_encoding_value_ptr++);
+            l_res->pw =  *(l_encoding_value_ptr++);
+            l_res->ph =  *(l_encoding_value_ptr++);
+            ++l_res;
+        }
+
+        ++l_current_comp;
+        ++l_img_comp;
+        ++l_tccp;
+    }
+    ++l_current_pi;
+
+    for (pino = 1 ; pino < l_bound ; ++pino) {
+        l_current_comp = l_current_pi->comps;
+        l_img_comp = p_image->comps;
+        l_tccp = l_tcp->tccps;
+
+        l_current_pi->tx0 = l_tx0;
+        l_current_pi->ty0 = l_ty0;
+        l_current_pi->tx1 = l_tx1;
+        l_current_pi->ty1 = l_ty1;
+        l_current_pi->dx = l_dx_min;
+        l_current_pi->dy = l_dy_min;
+        l_current_pi->step_p = l_step_p;
+        l_current_pi->step_c = l_step_c;
+        l_current_pi->step_r = l_step_r;
+        l_current_pi->step_l = l_step_l;
+
+        /* allocation for components and number of components has already been calculated by opj_pi_create */
+        for (compno = 0; compno < numcomps; ++compno) {
+            opj_pi_resolution_t *l_res = l_current_comp->resolutions;
+            l_encoding_value_ptr = l_tmp_ptr[compno];
+
+            l_current_comp->dx = l_img_comp->dx;
+            l_current_comp->dy = l_img_comp->dy;
+            /* resolutions have already been initialized */
+            for (resno = 0; resno < l_current_comp->numresolutions; resno++) {
+                l_res->pdx = *(l_encoding_value_ptr++);
+                l_res->pdy = *(l_encoding_value_ptr++);
+                l_res->pw =  *(l_encoding_value_ptr++);
+                l_res->ph =  *(l_encoding_value_ptr++);
+                ++l_res;
+            }
+            ++l_current_comp;
+            ++l_img_comp;
+            ++l_tccp;
+        }
+
+        /* special treatment*/
+        l_current_pi->include = (l_current_pi - 1)->include;
+        l_current_pi->include_size = (l_current_pi - 1)->include_size;
+        ++l_current_pi;
+    }
+
+    opj_free(l_tmp_data);
+    l_tmp_data = 00;
+    opj_free(l_tmp_ptr);
+    l_tmp_ptr = 00;
+
+    if (l_tcp->POC && (OPJ_IS_CINEMA(p_cp->rsiz) || p_t2_mode == FINAL_PASS)) {
+        opj_pi_update_encode_poc_and_final(p_cp, p_tile_no, l_tx0, l_tx1, l_ty0, l_ty1,
+                                           l_max_prec, l_max_res, l_dx_min, l_dy_min);
+    } else {
+        opj_pi_update_encode_not_poc(p_cp, numcomps, p_tile_no, l_tx0, l_tx1,
+                                     l_ty0, l_ty1, l_max_prec, l_max_res, l_dx_min, l_dy_min);
+    }
+
+    return l_pi;
+}
+
+void opj_pi_create_encode(opj_pi_iterator_t *pi,
+                          opj_cp_t *cp,
+                          OPJ_UINT32 tileno,
+                          OPJ_UINT32 pino,
+                          OPJ_UINT32 tpnum,
+                          OPJ_INT32 tppos,
+                          J2K_T2_MODE t2_mode)
+{
+    const OPJ_CHAR *prog;
+    OPJ_INT32 i;
+    OPJ_UINT32 incr_top = 1, resetX = 0;
+    opj_tcp_t *tcps = &cp->tcps[tileno];
+    opj_poc_t *tcp = &tcps->pocs[pino];
+
+    prog = opj_j2k_convert_progression_order(tcp->prg);
+
+    pi[pino].first = 1;
+    pi[pino].poc.prg = tcp->prg;
+
+    if (!(cp->m_specific_param.m_enc.m_tp_on && ((!OPJ_IS_CINEMA(cp->rsiz) &&
+            !OPJ_IS_IMF(cp->rsiz) &&
+            (t2_mode == FINAL_PASS)) || OPJ_IS_CINEMA(cp->rsiz) || OPJ_IS_IMF(cp->rsiz)))) {
+        pi[pino].poc.resno0 = tcp->resS;
+        pi[pino].poc.resno1 = tcp->resE;
+        pi[pino].poc.compno0 = tcp->compS;
+        pi[pino].poc.compno1 = tcp->compE;
+        pi[pino].poc.layno0 = tcp->layS;
+        pi[pino].poc.layno1 = tcp->layE;
+        pi[pino].poc.precno0 = tcp->prcS;
+        pi[pino].poc.precno1 = tcp->prcE;
+        pi[pino].poc.tx0 = tcp->txS;
+        pi[pino].poc.ty0 = tcp->tyS;
+        pi[pino].poc.tx1 = tcp->txE;
+        pi[pino].poc.ty1 = tcp->tyE;
+    } else {
+        for (i = tppos + 1; i < 4; i++) {
+            switch (prog[i]) {
+            case 'R':
+                pi[pino].poc.resno0 = tcp->resS;
+                pi[pino].poc.resno1 = tcp->resE;
+                break;
+            case 'C':
+                pi[pino].poc.compno0 = tcp->compS;
+                pi[pino].poc.compno1 = tcp->compE;
+                break;
+            case 'L':
+                pi[pino].poc.layno0 = tcp->layS;
+                pi[pino].poc.layno1 = tcp->layE;
+                break;
+            case 'P':
+                switch (tcp->prg) {
+                case OPJ_LRCP:
+                case OPJ_RLCP:
+                    pi[pino].poc.precno0 = tcp->prcS;
+                    pi[pino].poc.precno1 = tcp->prcE;
+                    break;
+                default:
+                    pi[pino].poc.tx0 = tcp->txS;
+                    pi[pino].poc.ty0 = tcp->tyS;
+                    pi[pino].poc.tx1 = tcp->txE;
+                    pi[pino].poc.ty1 = tcp->tyE;
+                    break;
+                }
+                break;
+            }
+        }
+
+        if (tpnum == 0) {
+            for (i = tppos; i >= 0; i--) {
+                switch (prog[i]) {
+                case 'C':
+                    tcp->comp_t = tcp->compS;
+                    pi[pino].poc.compno0 = tcp->comp_t;
+                    pi[pino].poc.compno1 = tcp->comp_t + 1;
+                    tcp->comp_t += 1;
+                    break;
+                case 'R':
+                    tcp->res_t = tcp->resS;
+                    pi[pino].poc.resno0 = tcp->res_t;
+                    pi[pino].poc.resno1 = tcp->res_t + 1;
+                    tcp->res_t += 1;
+                    break;
+                case 'L':
+                    tcp->lay_t = tcp->layS;
+                    pi[pino].poc.layno0 = tcp->lay_t;
+                    pi[pino].poc.layno1 = tcp->lay_t + 1;
+                    tcp->lay_t += 1;
+                    break;
+                case 'P':
+                    switch (tcp->prg) {
+                    case OPJ_LRCP:
+                    case OPJ_RLCP:
+                        tcp->prc_t = tcp->prcS;
+                        pi[pino].poc.precno0 = tcp->prc_t;
+                        pi[pino].poc.precno1 = tcp->prc_t + 1;
+                        tcp->prc_t += 1;
+                        break;
+                    default:
+                        tcp->tx0_t = tcp->txS;
+                        tcp->ty0_t = tcp->tyS;
+                        pi[pino].poc.tx0 = tcp->tx0_t;
+                        pi[pino].poc.tx1 = tcp->tx0_t + tcp->dx - (tcp->tx0_t % tcp->dx);
+                        pi[pino].poc.ty0 = tcp->ty0_t;
+                        pi[pino].poc.ty1 = tcp->ty0_t + tcp->dy - (tcp->ty0_t % tcp->dy);
+                        tcp->tx0_t = (OPJ_UINT32)pi[pino].poc.tx1;
+                        tcp->ty0_t = (OPJ_UINT32)pi[pino].poc.ty1;
+                        break;
+                    }
+                    break;
+                }
+            }
+            incr_top = 1;
+        } else {
+            for (i = tppos; i >= 0; i--) {
+                switch (prog[i]) {
+                case 'C':
+                    pi[pino].poc.compno0 = tcp->comp_t - 1;
+                    pi[pino].poc.compno1 = tcp->comp_t;
+                    break;
+                case 'R':
+                    pi[pino].poc.resno0 = tcp->res_t - 1;
+                    pi[pino].poc.resno1 = tcp->res_t;
+                    break;
+                case 'L':
+                    pi[pino].poc.layno0 = tcp->lay_t - 1;
+                    pi[pino].poc.layno1 = tcp->lay_t;
+                    break;
+                case 'P':
+                    switch (tcp->prg) {
+                    case OPJ_LRCP:
+                    case OPJ_RLCP:
+                        pi[pino].poc.precno0 = tcp->prc_t - 1;
+                        pi[pino].poc.precno1 = tcp->prc_t;
+                        break;
+                    default:
+                        pi[pino].poc.tx0 = tcp->tx0_t - tcp->dx - (tcp->tx0_t % tcp->dx);
+                        pi[pino].poc.tx1 = tcp->tx0_t ;
+                        pi[pino].poc.ty0 = tcp->ty0_t - tcp->dy - (tcp->ty0_t % tcp->dy);
+                        pi[pino].poc.ty1 = tcp->ty0_t ;
+                        break;
+                    }
+                    break;
+                }
+                if (incr_top == 1) {
+                    switch (prog[i]) {
+                    case 'R':
+                        if (tcp->res_t == tcp->resE) {
+                            if (opj_pi_check_next_level(i - 1, cp, tileno, pino, prog)) {
+                                tcp->res_t = tcp->resS;
+                                pi[pino].poc.resno0 = tcp->res_t;
+                                pi[pino].poc.resno1 = tcp->res_t + 1;
+                                tcp->res_t += 1;
+                                incr_top = 1;
+                            } else {
+                                incr_top = 0;
+                            }
+                        } else {
+                            pi[pino].poc.resno0 = tcp->res_t;
+                            pi[pino].poc.resno1 = tcp->res_t + 1;
+                            tcp->res_t += 1;
+                            incr_top = 0;
+                        }
+                        break;
+                    case 'C':
+                        if (tcp->comp_t == tcp->compE) {
+                            if (opj_pi_check_next_level(i - 1, cp, tileno, pino, prog)) {
+                                tcp->comp_t = tcp->compS;
+                                pi[pino].poc.compno0 = tcp->comp_t;
+                                pi[pino].poc.compno1 = tcp->comp_t + 1;
+                                tcp->comp_t += 1;
+                                incr_top = 1;
+                            } else {
+                                incr_top = 0;
+                            }
+                        } else {
+                            pi[pino].poc.compno0 = tcp->comp_t;
+                            pi[pino].poc.compno1 = tcp->comp_t + 1;
+                            tcp->comp_t += 1;
+                            incr_top = 0;
+                        }
+                        break;
+                    case 'L':
+                        if (tcp->lay_t == tcp->layE) {
+                            if (opj_pi_check_next_level(i - 1, cp, tileno, pino, prog)) {
+                                tcp->lay_t = tcp->layS;
+                                pi[pino].poc.layno0 = tcp->lay_t;
+                                pi[pino].poc.layno1 = tcp->lay_t + 1;
+                                tcp->lay_t += 1;
+                                incr_top = 1;
+                            } else {
+                                incr_top = 0;
+                            }
+                        } else {
+                            pi[pino].poc.layno0 = tcp->lay_t;
+                            pi[pino].poc.layno1 = tcp->lay_t + 1;
+                            tcp->lay_t += 1;
+                            incr_top = 0;
+                        }
+                        break;
+                    case 'P':
+                        switch (tcp->prg) {
+                        case OPJ_LRCP:
+                        case OPJ_RLCP:
+                            if (tcp->prc_t == tcp->prcE) {
+                                if (opj_pi_check_next_level(i - 1, cp, tileno, pino, prog)) {
+                                    tcp->prc_t = tcp->prcS;
+                                    pi[pino].poc.precno0 = tcp->prc_t;
+                                    pi[pino].poc.precno1 = tcp->prc_t + 1;
+                                    tcp->prc_t += 1;
+                                    incr_top = 1;
+                                } else {
+                                    incr_top = 0;
+                                }
+                            } else {
+                                pi[pino].poc.precno0 = tcp->prc_t;
+                                pi[pino].poc.precno1 = tcp->prc_t + 1;
+                                tcp->prc_t += 1;
+                                incr_top = 0;
+                            }
+                            break;
+                        default:
+                            if (tcp->tx0_t >= tcp->txE) {
+                                if (tcp->ty0_t >= tcp->tyE) {
+                                    if (opj_pi_check_next_level(i - 1, cp, tileno, pino, prog)) {
+                                        tcp->ty0_t = tcp->tyS;
+                                        pi[pino].poc.ty0 = tcp->ty0_t;
+                                        pi[pino].poc.ty1 = tcp->ty0_t + tcp->dy - (tcp->ty0_t % tcp->dy);
+                                        tcp->ty0_t = (OPJ_UINT32)pi[pino].poc.ty1;
+                                        incr_top = 1;
+                                        resetX = 1;
+                                    } else {
+                                        incr_top = 0;
+                                        resetX = 0;
+                                    }
+                                } else {
+                                    pi[pino].poc.ty0 = tcp->ty0_t;
+                                    pi[pino].poc.ty1 = tcp->ty0_t + tcp->dy - (tcp->ty0_t % tcp->dy);
+                                    tcp->ty0_t = (OPJ_UINT32)pi[pino].poc.ty1;
+                                    incr_top = 0;
+                                    resetX = 1;
+                                }
+                                if (resetX == 1) {
+                                    tcp->tx0_t = tcp->txS;
+                                    pi[pino].poc.tx0 = tcp->tx0_t;
+                                    pi[pino].poc.tx1 = tcp->tx0_t + tcp->dx - (tcp->tx0_t % tcp->dx);
+                                    tcp->tx0_t = (OPJ_UINT32)pi[pino].poc.tx1;
+                                }
+                            } else {
+                                pi[pino].poc.tx0 = tcp->tx0_t;
+                                pi[pino].poc.tx1 = tcp->tx0_t + tcp->dx - (tcp->tx0_t % tcp->dx);
+                                tcp->tx0_t = (OPJ_UINT32)pi[pino].poc.tx1;
+                                incr_top = 0;
+                            }
+                            break;
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+    }
+}
+
+void opj_pi_destroy(opj_pi_iterator_t *p_pi,
+                    OPJ_UINT32 p_nb_elements)
+{
+    OPJ_UINT32 compno, pino;
+    opj_pi_iterator_t *l_current_pi = p_pi;
+    if (p_pi) {
+        if (p_pi->include) {
+            opj_free(p_pi->include);
+            p_pi->include = 00;
+        }
+        for (pino = 0; pino < p_nb_elements; ++pino) {
+            if (l_current_pi->comps) {
+                opj_pi_comp_t *l_current_component = l_current_pi->comps;
+                for (compno = 0; compno < l_current_pi->numcomps; compno++) {
+                    if (l_current_component->resolutions) {
+                        opj_free(l_current_component->resolutions);
+                        l_current_component->resolutions = 00;
+                    }
+
+                    ++l_current_component;
+                }
+                opj_free(l_current_pi->comps);
+                l_current_pi->comps = 0;
+            }
+            ++l_current_pi;
+        }
+        opj_free(p_pi);
+    }
+}
+
+
+
+void opj_pi_update_encoding_parameters(const opj_image_t *p_image,
+                                       opj_cp_t *p_cp,
+                                       OPJ_UINT32 p_tile_no)
+{
+    /* encoding parameters to set */
+    OPJ_UINT32 l_max_res;
+    OPJ_UINT32 l_max_prec;
+    OPJ_UINT32 l_tx0, l_tx1, l_ty0, l_ty1;
+    OPJ_UINT32 l_dx_min, l_dy_min;
+
+    /* pointers */
+    opj_tcp_t *l_tcp = 00;
+
+    /* preconditions */
+    assert(p_cp != 00);
+    assert(p_image != 00);
+    assert(p_tile_no < p_cp->tw * p_cp->th);
+
+    l_tcp = &(p_cp->tcps[p_tile_no]);
+
+    /* get encoding parameters */
+    opj_get_encoding_parameters(p_image, p_cp, p_tile_no, &l_tx0, &l_tx1, &l_ty0,
+                                &l_ty1, &l_dx_min, &l_dy_min, &l_max_prec, &l_max_res);
+
+    if (l_tcp->POC) {
+        opj_pi_update_encode_poc_and_final(p_cp, p_tile_no, l_tx0, l_tx1, l_ty0, l_ty1,
+                                           l_max_prec, l_max_res, l_dx_min, l_dy_min);
+    } else {
+        opj_pi_update_encode_not_poc(p_cp, p_image->numcomps, p_tile_no, l_tx0, l_tx1,
+                                     l_ty0, l_ty1, l_max_prec, l_max_res, l_dx_min, l_dy_min);
+    }
+}
+
+OPJ_BOOL opj_pi_next(opj_pi_iterator_t * pi)
+{
+    switch (pi->poc.prg) {
+    case OPJ_LRCP:
+        return opj_pi_next_lrcp(pi);
+    case OPJ_RLCP:
+        return opj_pi_next_rlcp(pi);
+    case OPJ_RPCL:
+        return opj_pi_next_rpcl(pi);
+    case OPJ_PCRL:
+        return opj_pi_next_pcrl(pi);
+    case OPJ_CPRL:
+        return opj_pi_next_cprl(pi);
+    case OPJ_PROG_UNKNOWN:
+        return OPJ_FALSE;
+    }
+
+    return OPJ_FALSE;
+}
diff --git a/third_party/libopenjpeg/pi.h b/third_party/libopenjpeg/pi.h
new file mode 100644
index 0000000..0320523
--- /dev/null
+++ b/third_party/libopenjpeg/pi.h
@@ -0,0 +1,207 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef OPJ_PI_H
+#define OPJ_PI_H
+/**
+@file pi.h
+@brief Implementation of a packet iterator (PI)
+
+The functions in PI.C have for goal to realize a packet iterator that permits to get the next
+packet following the progression order and change of it. The functions in PI.C are used
+by some function in T2.C.
+*/
+
+/** @defgroup PI PI - Implementation of a packet iterator */
+/*@{*/
+
+/**
+FIXME DOC
+*/
+typedef struct opj_pi_resolution {
+    OPJ_UINT32 pdx, pdy;
+    OPJ_UINT32 pw, ph;
+} opj_pi_resolution_t;
+
+/**
+FIXME DOC
+*/
+typedef struct opj_pi_comp {
+    OPJ_UINT32 dx, dy;
+    /** number of resolution levels */
+    OPJ_UINT32 numresolutions;
+    opj_pi_resolution_t *resolutions;
+} opj_pi_comp_t;
+
+/**
+Packet iterator
+*/
+typedef struct opj_pi_iterator {
+    /** Enabling Tile part generation*/
+    OPJ_BYTE tp_on;
+    /** precise if the packet has been already used (useful for progression order change) */
+    OPJ_INT16 *include;
+    /** Number of elements in include array */
+    OPJ_UINT32 include_size;
+    /** layer step used to localize the packet in the include vector */
+    OPJ_UINT32 step_l;
+    /** resolution step used to localize the packet in the include vector */
+    OPJ_UINT32 step_r;
+    /** component step used to localize the packet in the include vector */
+    OPJ_UINT32 step_c;
+    /** precinct step used to localize the packet in the include vector */
+    OPJ_UINT32 step_p;
+    /** component that identify the packet */
+    OPJ_UINT32 compno;
+    /** resolution that identify the packet */
+    OPJ_UINT32 resno;
+    /** precinct that identify the packet */
+    OPJ_UINT32 precno;
+    /** layer that identify the packet */
+    OPJ_UINT32 layno;
+    /** 0 if the first packet */
+    OPJ_BOOL first;
+    /** progression order change information */
+    opj_poc_t poc;
+    /** number of components in the image */
+    OPJ_UINT32 numcomps;
+    /** Components*/
+    opj_pi_comp_t *comps;
+    /** FIXME DOC*/
+    OPJ_UINT32 tx0, ty0, tx1, ty1;
+    /** FIXME DOC*/
+    OPJ_UINT32 x, y;
+    /** FIXME DOC*/
+    OPJ_UINT32 dx, dy;
+    /** event manager */
+    opj_event_mgr_t* manager;
+} opj_pi_iterator_t;
+
+/** @name Exported functions */
+/*@{*/
+/* ----------------------------------------------------------------------- */
+/**
+ * Creates a packet iterator for encoding.
+ *
+ * @param   image       the image being encoded.
+ * @param   cp      the coding parameters.
+ * @param   tileno  index of the tile being encoded.
+ * @param   t2_mode the type of pass for generating the packet iterator
+ * @param   manager Event manager
+ *
+ * @return  a list of packet iterator that points to the first packet of the tile (not true).
+*/
+opj_pi_iterator_t *opj_pi_initialise_encode(const opj_image_t *image,
+        opj_cp_t *cp,
+        OPJ_UINT32 tileno,
+        J2K_T2_MODE t2_mode,
+        opj_event_mgr_t* manager);
+
+/**
+ * Updates the encoding parameters of the codec.
+ *
+ * @param   p_image     the image being encoded.
+ * @param   p_cp        the coding parameters.
+ * @param   p_tile_no   index of the tile being encoded.
+*/
+void opj_pi_update_encoding_parameters(const opj_image_t *p_image,
+                                       opj_cp_t *p_cp,
+                                       OPJ_UINT32 p_tile_no);
+
+/**
+Modify the packet iterator for enabling tile part generation
+@param pi Handle to the packet iterator generated in pi_initialise_encode
+@param cp Coding parameters
+@param tileno Number that identifies the tile for which to list the packets
+@param pino   FIXME DOC
+@param tpnum Tile part number of the current tile
+@param tppos The position of the tile part flag in the progression order
+@param t2_mode FIXME DOC
+*/
+void opj_pi_create_encode(opj_pi_iterator_t *pi,
+                          opj_cp_t *cp,
+                          OPJ_UINT32 tileno,
+                          OPJ_UINT32 pino,
+                          OPJ_UINT32 tpnum,
+                          OPJ_INT32 tppos,
+                          J2K_T2_MODE t2_mode);
+
+/**
+Create a packet iterator for Decoder
+@param image Raw image for which the packets will be listed
+@param cp Coding parameters
+@param tileno Number that identifies the tile for which to list the packets
+@param manager Event manager
+@return Returns a packet iterator that points to the first packet of the tile
+@see opj_pi_destroy
+*/
+opj_pi_iterator_t *opj_pi_create_decode(opj_image_t * image,
+                                        opj_cp_t * cp,
+                                        OPJ_UINT32 tileno,
+                                        opj_event_mgr_t* manager);
+/**
+ * Destroys a packet iterator array.
+ *
+ * @param   p_pi            the packet iterator array to destroy.
+ * @param   p_nb_elements   the number of elements in the array.
+ */
+void opj_pi_destroy(opj_pi_iterator_t *p_pi,
+                    OPJ_UINT32 p_nb_elements);
+
+/**
+Modify the packet iterator to point to the next packet
+@param pi Packet iterator to modify
+@return Returns false if pi pointed to the last packet or else returns true
+*/
+OPJ_BOOL opj_pi_next(opj_pi_iterator_t * pi);
+
+/**
+ * Return the number of packets in the tile.
+ * @param   image       the image being encoded.
+ * @param cp Coding parameters
+ * @param tileno Number that identifies the tile.
+ */
+OPJ_UINT32 opj_get_encoding_packet_count(const opj_image_t *p_image,
+        const opj_cp_t *p_cp,
+        OPJ_UINT32 p_tile_no);
+
+/* ----------------------------------------------------------------------- */
+/*@}*/
+
+/*@}*/
+
+#endif /* OPJ_PI_H */
diff --git a/third_party/libopenjpeg/sparse_array.c b/third_party/libopenjpeg/sparse_array.c
new file mode 100644
index 0000000..50d1a90
--- /dev/null
+++ b/third_party/libopenjpeg/sparse_array.c
@@ -0,0 +1,346 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2017, IntoPix SA <contact@intopix.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "opj_includes.h"
+
+
+struct opj_sparse_array_int32 {
+    OPJ_UINT32 width;
+    OPJ_UINT32 height;
+    OPJ_UINT32 block_width;
+    OPJ_UINT32 block_height;
+    OPJ_UINT32 block_count_hor;
+    OPJ_UINT32 block_count_ver;
+    OPJ_INT32** data_blocks;
+};
+
+opj_sparse_array_int32_t* opj_sparse_array_int32_create(OPJ_UINT32 width,
+        OPJ_UINT32 height,
+        OPJ_UINT32 block_width,
+        OPJ_UINT32 block_height)
+{
+    opj_sparse_array_int32_t* sa;
+
+    if (width == 0 || height == 0 || block_width == 0 || block_height == 0) {
+        return NULL;
+    }
+    if (block_width > ((OPJ_UINT32)~0U) / block_height / sizeof(OPJ_INT32)) {
+        return NULL;
+    }
+
+    sa = (opj_sparse_array_int32_t*) opj_calloc(1,
+            sizeof(opj_sparse_array_int32_t));
+    sa->width = width;
+    sa->height = height;
+    sa->block_width = block_width;
+    sa->block_height = block_height;
+    sa->block_count_hor = opj_uint_ceildiv(width, block_width);
+    sa->block_count_ver = opj_uint_ceildiv(height, block_height);
+    if (sa->block_count_hor > ((OPJ_UINT32)~0U) / sa->block_count_ver) {
+        opj_free(sa);
+        return NULL;
+    }
+    sa->data_blocks = (OPJ_INT32**) opj_calloc(sizeof(OPJ_INT32*),
+                      (size_t) sa->block_count_hor * sa->block_count_ver);
+    if (sa->data_blocks == NULL) {
+        opj_free(sa);
+        return NULL;
+    }
+
+    return sa;
+}
+
+void opj_sparse_array_int32_free(opj_sparse_array_int32_t* sa)
+{
+    if (sa) {
+        OPJ_UINT32 i;
+        for (i = 0; i < sa->block_count_hor * sa->block_count_ver; i++) {
+            if (sa->data_blocks[i]) {
+                opj_free(sa->data_blocks[i]);
+            }
+        }
+        opj_free(sa->data_blocks);
+        opj_free(sa);
+    }
+}
+
+OPJ_BOOL opj_sparse_array_is_region_valid(const opj_sparse_array_int32_t* sa,
+        OPJ_UINT32 x0,
+        OPJ_UINT32 y0,
+        OPJ_UINT32 x1,
+        OPJ_UINT32 y1)
+{
+    return !(x0 >= sa->width || x1 <= x0 || x1 > sa->width ||
+             y0 >= sa->height || y1 <= y0 || y1 > sa->height);
+}
+
+static OPJ_BOOL opj_sparse_array_int32_read_or_write(
+    const opj_sparse_array_int32_t* sa,
+    OPJ_UINT32 x0,
+    OPJ_UINT32 y0,
+    OPJ_UINT32 x1,
+    OPJ_UINT32 y1,
+    OPJ_INT32* buf,
+    OPJ_UINT32 buf_col_stride,
+    OPJ_UINT32 buf_line_stride,
+    OPJ_BOOL forgiving,
+    OPJ_BOOL is_read_op)
+{
+    OPJ_UINT32 y, block_y;
+    OPJ_UINT32 y_incr = 0;
+    const OPJ_UINT32 block_width = sa->block_width;
+
+    if (!opj_sparse_array_is_region_valid(sa, x0, y0, x1, y1)) {
+        return forgiving;
+    }
+
+    block_y = y0 / sa->block_height;
+    for (y = y0; y < y1; block_y ++, y += y_incr) {
+        OPJ_UINT32 x, block_x;
+        OPJ_UINT32 x_incr = 0;
+        OPJ_UINT32 block_y_offset;
+        y_incr = (y == y0) ? sa->block_height - (y0 % sa->block_height) :
+                 sa->block_height;
+        block_y_offset = sa->block_height - y_incr;
+        y_incr = opj_uint_min(y_incr, y1 - y);
+        block_x = x0 / block_width;
+        for (x = x0; x < x1; block_x ++, x += x_incr) {
+            OPJ_UINT32 j;
+            OPJ_UINT32 block_x_offset;
+            OPJ_INT32* src_block;
+            x_incr = (x == x0) ? block_width - (x0 % block_width) : block_width;
+            block_x_offset = block_width - x_incr;
+            x_incr = opj_uint_min(x_incr, x1 - x);
+            src_block = sa->data_blocks[block_y * sa->block_count_hor + block_x];
+            if (is_read_op) {
+                if (src_block == NULL) {
+                    if (buf_col_stride == 1) {
+                        OPJ_INT32* dest_ptr = buf + (y - y0) * (OPJ_SIZE_T)buf_line_stride +
+                                              (x - x0) * buf_col_stride;
+                        for (j = 0; j < y_incr; j++) {
+                            memset(dest_ptr, 0, sizeof(OPJ_INT32) * x_incr);
+                            dest_ptr += buf_line_stride;
+                        }
+                    } else {
+                        OPJ_INT32* dest_ptr = buf + (y - y0) * (OPJ_SIZE_T)buf_line_stride +
+                                              (x - x0) * buf_col_stride;
+                        for (j = 0; j < y_incr; j++) {
+                            OPJ_UINT32 k;
+                            for (k = 0; k < x_incr; k++) {
+                                dest_ptr[k * buf_col_stride] = 0;
+                            }
+                            dest_ptr += buf_line_stride;
+                        }
+                    }
+                } else {
+                    const OPJ_INT32* OPJ_RESTRICT src_ptr = src_block + block_y_offset *
+                                                            (OPJ_SIZE_T)block_width + block_x_offset;
+                    if (buf_col_stride == 1) {
+                        OPJ_INT32* OPJ_RESTRICT dest_ptr = buf + (y - y0) * (OPJ_SIZE_T)buf_line_stride
+                                                           +
+                                                           (x - x0) * buf_col_stride;
+                        if (x_incr == 4) {
+                            /* Same code as general branch, but the compiler */
+                            /* can have an efficient memcpy() */
+                            (void)(x_incr); /* trick to silent cppcheck duplicateBranch warning */
+                            for (j = 0; j < y_incr; j++) {
+                                memcpy(dest_ptr, src_ptr, sizeof(OPJ_INT32) * x_incr);
+                                dest_ptr += buf_line_stride;
+                                src_ptr += block_width;
+                            }
+                        } else {
+                            for (j = 0; j < y_incr; j++) {
+                                memcpy(dest_ptr, src_ptr, sizeof(OPJ_INT32) * x_incr);
+                                dest_ptr += buf_line_stride;
+                                src_ptr += block_width;
+                            }
+                        }
+                    } else {
+                        OPJ_INT32* OPJ_RESTRICT dest_ptr = buf + (y - y0) * (OPJ_SIZE_T)buf_line_stride
+                                                           +
+                                                           (x - x0) * buf_col_stride;
+                        if (x_incr == 1) {
+                            for (j = 0; j < y_incr; j++) {
+                                *dest_ptr = *src_ptr;
+                                dest_ptr += buf_line_stride;
+                                src_ptr += block_width;
+                            }
+                        } else if (y_incr == 1 && buf_col_stride == 2) {
+                            OPJ_UINT32 k;
+                            for (k = 0; k < (x_incr & ~3U); k += 4) {
+                                dest_ptr[k * buf_col_stride] = src_ptr[k];
+                                dest_ptr[(k + 1) * buf_col_stride] = src_ptr[k + 1];
+                                dest_ptr[(k + 2) * buf_col_stride] = src_ptr[k + 2];
+                                dest_ptr[(k + 3) * buf_col_stride] = src_ptr[k + 3];
+                            }
+                            for (; k < x_incr; k++) {
+                                dest_ptr[k * buf_col_stride] = src_ptr[k];
+                            }
+                        } else if (x_incr >= 8 && buf_col_stride == 8) {
+                            for (j = 0; j < y_incr; j++) {
+                                OPJ_UINT32 k;
+                                for (k = 0; k < (x_incr & ~3U); k += 4) {
+                                    dest_ptr[k * buf_col_stride] = src_ptr[k];
+                                    dest_ptr[(k + 1) * buf_col_stride] = src_ptr[k + 1];
+                                    dest_ptr[(k + 2) * buf_col_stride] = src_ptr[k + 2];
+                                    dest_ptr[(k + 3) * buf_col_stride] = src_ptr[k + 3];
+                                }
+                                for (; k < x_incr; k++) {
+                                    dest_ptr[k * buf_col_stride] = src_ptr[k];
+                                }
+                                dest_ptr += buf_line_stride;
+                                src_ptr += block_width;
+                            }
+                        } else {
+                            /* General case */
+                            for (j = 0; j < y_incr; j++) {
+                                OPJ_UINT32 k;
+                                for (k = 0; k < x_incr; k++) {
+                                    dest_ptr[k * buf_col_stride] = src_ptr[k];
+                                }
+                                dest_ptr += buf_line_stride;
+                                src_ptr += block_width;
+                            }
+                        }
+                    }
+                }
+            } else {
+                if (src_block == NULL) {
+                    src_block = (OPJ_INT32*) opj_calloc(1,
+                                                        (size_t) sa->block_width * sa->block_height * sizeof(OPJ_INT32));
+                    if (src_block == NULL) {
+                        return OPJ_FALSE;
+                    }
+                    sa->data_blocks[block_y * sa->block_count_hor + block_x] = src_block;
+                }
+
+                if (buf_col_stride == 1) {
+                    OPJ_INT32* OPJ_RESTRICT dest_ptr = src_block + block_y_offset *
+                                                       (OPJ_SIZE_T)block_width + block_x_offset;
+                    const OPJ_INT32* OPJ_RESTRICT src_ptr = buf + (y - y0) *
+                                                            (OPJ_SIZE_T)buf_line_stride + (x - x0) * buf_col_stride;
+                    if (x_incr == 4) {
+                        /* Same code as general branch, but the compiler */
+                        /* can have an efficient memcpy() */
+                        (void)(x_incr); /* trick to silent cppcheck duplicateBranch warning */
+                        for (j = 0; j < y_incr; j++) {
+                            memcpy(dest_ptr, src_ptr, sizeof(OPJ_INT32) * x_incr);
+                            dest_ptr += block_width;
+                            src_ptr += buf_line_stride;
+                        }
+                    } else {
+                        for (j = 0; j < y_incr; j++) {
+                            memcpy(dest_ptr, src_ptr, sizeof(OPJ_INT32) * x_incr);
+                            dest_ptr += block_width;
+                            src_ptr += buf_line_stride;
+                        }
+                    }
+                } else {
+                    OPJ_INT32* OPJ_RESTRICT dest_ptr = src_block + block_y_offset *
+                                                       (OPJ_SIZE_T)block_width + block_x_offset;
+                    const OPJ_INT32* OPJ_RESTRICT src_ptr = buf + (y - y0) *
+                                                            (OPJ_SIZE_T)buf_line_stride + (x - x0) * buf_col_stride;
+                    if (x_incr == 1) {
+                        for (j = 0; j < y_incr; j++) {
+                            *dest_ptr = *src_ptr;
+                            src_ptr += buf_line_stride;
+                            dest_ptr += block_width;
+                        }
+                    } else if (x_incr >= 8 && buf_col_stride == 8) {
+                        for (j = 0; j < y_incr; j++) {
+                            OPJ_UINT32 k;
+                            for (k = 0; k < (x_incr & ~3U); k += 4) {
+                                dest_ptr[k] = src_ptr[k * buf_col_stride];
+                                dest_ptr[k + 1] = src_ptr[(k + 1) * buf_col_stride];
+                                dest_ptr[k + 2] = src_ptr[(k + 2) * buf_col_stride];
+                                dest_ptr[k + 3] = src_ptr[(k + 3) * buf_col_stride];
+                            }
+                            for (; k < x_incr; k++) {
+                                dest_ptr[k] = src_ptr[k * buf_col_stride];
+                            }
+                            src_ptr += buf_line_stride;
+                            dest_ptr += block_width;
+                        }
+                    } else {
+                        /* General case */
+                        for (j = 0; j < y_incr; j++) {
+                            OPJ_UINT32 k;
+                            for (k = 0; k < x_incr; k++) {
+                                dest_ptr[k] = src_ptr[k * buf_col_stride];
+                            }
+                            src_ptr += buf_line_stride;
+                            dest_ptr += block_width;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    return OPJ_TRUE;
+}
+
+OPJ_BOOL opj_sparse_array_int32_read(const opj_sparse_array_int32_t* sa,
+                                     OPJ_UINT32 x0,
+                                     OPJ_UINT32 y0,
+                                     OPJ_UINT32 x1,
+                                     OPJ_UINT32 y1,
+                                     OPJ_INT32* dest,
+                                     OPJ_UINT32 dest_col_stride,
+                                     OPJ_UINT32 dest_line_stride,
+                                     OPJ_BOOL forgiving)
+{
+    return opj_sparse_array_int32_read_or_write(
+               (opj_sparse_array_int32_t*)sa, x0, y0, x1, y1,
+               dest,
+               dest_col_stride,
+               dest_line_stride,
+               forgiving,
+               OPJ_TRUE);
+}
+
+OPJ_BOOL opj_sparse_array_int32_write(opj_sparse_array_int32_t* sa,
+                                      OPJ_UINT32 x0,
+                                      OPJ_UINT32 y0,
+                                      OPJ_UINT32 x1,
+                                      OPJ_UINT32 y1,
+                                      const OPJ_INT32* src,
+                                      OPJ_UINT32 src_col_stride,
+                                      OPJ_UINT32 src_line_stride,
+                                      OPJ_BOOL forgiving)
+{
+    return opj_sparse_array_int32_read_or_write(sa, x0, y0, x1, y1,
+            (OPJ_INT32*)src,
+            src_col_stride,
+            src_line_stride,
+            forgiving,
+            OPJ_FALSE);
+}
diff --git a/third_party/libopenjpeg20/sparse_array.h b/third_party/libopenjpeg/sparse_array.h
similarity index 100%
rename from third_party/libopenjpeg20/sparse_array.h
rename to third_party/libopenjpeg/sparse_array.h
diff --git a/third_party/libopenjpeg/t1.c b/third_party/libopenjpeg/t1.c
new file mode 100644
index 0000000..f5fd233
--- /dev/null
+++ b/third_party/libopenjpeg/t1.c
@@ -0,0 +1,2588 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2007, Callum Lerwick <seg@haxxed.com>
+ * Copyright (c) 2012, Carl Hetherington
+ * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define OPJ_SKIP_POISON
+#include "opj_includes.h"
+
+#ifdef __SSE__
+#include <xmmintrin.h>
+#endif
+#ifdef __SSE2__
+#include <emmintrin.h>
+#endif
+
+#if defined(__GNUC__)
+#pragma GCC poison malloc calloc realloc free
+#endif
+
+#include "t1_luts.h"
+
+/** @defgroup T1 T1 - Implementation of the tier-1 coding */
+/*@{*/
+
+#define T1_FLAGS(x, y) (t1->flags[x + 1 + ((y / 4) + 1) * (t1->w+2)])
+
+#define opj_t1_setcurctx(curctx, ctxno)  curctx = &(mqc)->ctxs[(OPJ_UINT32)(ctxno)]
+
+/* Macros to deal with signed integer with just MSB bit set for
+ * negative values (smr = signed magnitude representation) */
+#define opj_smr_abs(x)  (((OPJ_UINT32)(x)) & 0x7FFFFFFFU)
+#define opj_smr_sign(x) (((OPJ_UINT32)(x)) >> 31)
+#define opj_to_smr(x)   ((x) >= 0 ? (OPJ_UINT32)(x) : ((OPJ_UINT32)(-x) | 0x80000000U))
+
+
+/** @name Local static functions */
+/*@{*/
+
+static INLINE OPJ_BYTE opj_t1_getctxno_zc(opj_mqc_t *mqc, OPJ_UINT32 f);
+static INLINE OPJ_UINT32 opj_t1_getctxno_mag(OPJ_UINT32 f);
+static OPJ_INT16 opj_t1_getnmsedec_sig(OPJ_UINT32 x, OPJ_UINT32 bitpos);
+static OPJ_INT16 opj_t1_getnmsedec_ref(OPJ_UINT32 x, OPJ_UINT32 bitpos);
+static INLINE void opj_t1_update_flags(opj_flag_t *flagsp, OPJ_UINT32 ci,
+                                       OPJ_UINT32 s, OPJ_UINT32 stride,
+                                       OPJ_UINT32 vsc);
+
+
+/**
+Decode significant pass
+*/
+
+static INLINE void opj_t1_dec_sigpass_step_raw(
+    opj_t1_t *t1,
+    opj_flag_t *flagsp,
+    OPJ_INT32 *datap,
+    OPJ_INT32 oneplushalf,
+    OPJ_UINT32 vsc,
+    OPJ_UINT32 row);
+static INLINE void opj_t1_dec_sigpass_step_mqc(
+    opj_t1_t *t1,
+    opj_flag_t *flagsp,
+    OPJ_INT32 *datap,
+    OPJ_INT32 oneplushalf,
+    OPJ_UINT32 row,
+    OPJ_UINT32 flags_stride,
+    OPJ_UINT32 vsc);
+
+/**
+Encode significant pass
+*/
+static void opj_t1_enc_sigpass(opj_t1_t *t1,
+                               OPJ_INT32 bpno,
+                               OPJ_INT32 *nmsedec,
+                               OPJ_BYTE type,
+                               OPJ_UINT32 cblksty);
+
+/**
+Decode significant pass
+*/
+static void opj_t1_dec_sigpass_raw(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno,
+    OPJ_INT32 cblksty);
+
+/**
+Encode refinement pass
+*/
+static void opj_t1_enc_refpass(opj_t1_t *t1,
+                               OPJ_INT32 bpno,
+                               OPJ_INT32 *nmsedec,
+                               OPJ_BYTE type);
+
+/**
+Decode refinement pass
+*/
+static void opj_t1_dec_refpass_raw(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno);
+
+
+/**
+Decode refinement pass
+*/
+
+static INLINE void  opj_t1_dec_refpass_step_raw(
+    opj_t1_t *t1,
+    opj_flag_t *flagsp,
+    OPJ_INT32 *datap,
+    OPJ_INT32 poshalf,
+    OPJ_UINT32 row);
+static INLINE void opj_t1_dec_refpass_step_mqc(
+    opj_t1_t *t1,
+    opj_flag_t *flagsp,
+    OPJ_INT32 *datap,
+    OPJ_INT32 poshalf,
+    OPJ_UINT32 row);
+
+
+/**
+Decode clean-up pass
+*/
+
+static void opj_t1_dec_clnpass_step(
+    opj_t1_t *t1,
+    opj_flag_t *flagsp,
+    OPJ_INT32 *datap,
+    OPJ_INT32 oneplushalf,
+    OPJ_UINT32 row,
+    OPJ_UINT32 vsc);
+
+/**
+Encode clean-up pass
+*/
+static void opj_t1_enc_clnpass(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno,
+    OPJ_INT32 *nmsedec,
+    OPJ_UINT32 cblksty);
+
+static OPJ_FLOAT64 opj_t1_getwmsedec(
+    OPJ_INT32 nmsedec,
+    OPJ_UINT32 compno,
+    OPJ_UINT32 level,
+    OPJ_UINT32 orient,
+    OPJ_INT32 bpno,
+    OPJ_UINT32 qmfbid,
+    OPJ_FLOAT64 stepsize,
+    OPJ_UINT32 numcomps,
+    const OPJ_FLOAT64 * mct_norms,
+    OPJ_UINT32 mct_numcomps);
+
+/** Return "cumwmsedec" that should be used to increase tile->distotile */
+static double opj_t1_encode_cblk(opj_t1_t *t1,
+                                 opj_tcd_cblk_enc_t* cblk,
+                                 OPJ_UINT32 orient,
+                                 OPJ_UINT32 compno,
+                                 OPJ_UINT32 level,
+                                 OPJ_UINT32 qmfbid,
+                                 OPJ_FLOAT64 stepsize,
+                                 OPJ_UINT32 cblksty,
+                                 OPJ_UINT32 numcomps,
+                                 const OPJ_FLOAT64 * mct_norms,
+                                 OPJ_UINT32 mct_numcomps);
+
+/**
+Decode 1 code-block
+@param t1 T1 handle
+@param cblk Code-block coding parameters
+@param orient
+@param roishift Region of interest shifting value
+@param cblksty Code-block style
+@param p_manager the event manager
+@param p_manager_mutex mutex for the event manager
+@param check_pterm whether PTERM correct termination should be checked
+*/
+static OPJ_BOOL opj_t1_decode_cblk(opj_t1_t *t1,
+                                   opj_tcd_cblk_dec_t* cblk,
+                                   OPJ_UINT32 orient,
+                                   OPJ_UINT32 roishift,
+                                   OPJ_UINT32 cblksty,
+                                   opj_event_mgr_t *p_manager,
+                                   opj_mutex_t* p_manager_mutex,
+                                   OPJ_BOOL check_pterm);
+
+/**
+Decode 1 HT code-block
+@param t1 T1 handle
+@param cblk Code-block coding parameters
+@param orient
+@param roishift Region of interest shifting value
+@param cblksty Code-block style
+@param p_manager the event manager
+@param p_manager_mutex mutex for the event manager
+@param check_pterm whether PTERM correct termination should be checked
+*/
+OPJ_BOOL opj_t1_ht_decode_cblk(opj_t1_t *t1,
+                               opj_tcd_cblk_dec_t* cblk,
+                               OPJ_UINT32 orient,
+                               OPJ_UINT32 roishift,
+                               OPJ_UINT32 cblksty,
+                               opj_event_mgr_t *p_manager,
+                               opj_mutex_t* p_manager_mutex,
+                               OPJ_BOOL check_pterm);
+
+
+static OPJ_BOOL opj_t1_allocate_buffers(opj_t1_t *t1,
+                                        OPJ_UINT32 w,
+                                        OPJ_UINT32 h);
+
+/*@}*/
+
+/*@}*/
+
+/* ----------------------------------------------------------------------- */
+
+static INLINE OPJ_BYTE opj_t1_getctxno_zc(opj_mqc_t *mqc, OPJ_UINT32 f)
+{
+    return mqc->lut_ctxno_zc_orient[(f & T1_SIGMA_NEIGHBOURS)];
+}
+
+static INLINE OPJ_UINT32 opj_t1_getctxtno_sc_or_spb_index(OPJ_UINT32 fX,
+        OPJ_UINT32 pfX,
+        OPJ_UINT32 nfX,
+        OPJ_UINT32 ci)
+{
+    /*
+      0 pfX T1_CHI_THIS           T1_LUT_SGN_W
+      1 tfX T1_SIGMA_1            T1_LUT_SIG_N
+      2 nfX T1_CHI_THIS           T1_LUT_SGN_E
+      3 tfX T1_SIGMA_3            T1_LUT_SIG_W
+      4  fX T1_CHI_(THIS - 1)     T1_LUT_SGN_N
+      5 tfX T1_SIGMA_5            T1_LUT_SIG_E
+      6  fX T1_CHI_(THIS + 1)     T1_LUT_SGN_S
+      7 tfX T1_SIGMA_7            T1_LUT_SIG_S
+    */
+
+    OPJ_UINT32 lu = (fX >> (ci * 3U)) & (T1_SIGMA_1 | T1_SIGMA_3 | T1_SIGMA_5 |
+                                         T1_SIGMA_7);
+
+    lu |= (pfX >> (T1_CHI_THIS_I      + (ci * 3U))) & (1U << 0);
+    lu |= (nfX >> (T1_CHI_THIS_I - 2U + (ci * 3U))) & (1U << 2);
+    if (ci == 0U) {
+        lu |= (fX >> (T1_CHI_0_I - 4U)) & (1U << 4);
+    } else {
+        lu |= (fX >> (T1_CHI_1_I - 4U + ((ci - 1U) * 3U))) & (1U << 4);
+    }
+    lu |= (fX >> (T1_CHI_2_I - 6U + (ci * 3U))) & (1U << 6);
+    return lu;
+}
+
+static INLINE OPJ_BYTE opj_t1_getctxno_sc(OPJ_UINT32 lu)
+{
+    return lut_ctxno_sc[lu];
+}
+
+static INLINE OPJ_UINT32 opj_t1_getctxno_mag(OPJ_UINT32 f)
+{
+    OPJ_UINT32 tmp = (f & T1_SIGMA_NEIGHBOURS) ? T1_CTXNO_MAG + 1 : T1_CTXNO_MAG;
+    OPJ_UINT32 tmp2 = (f & T1_MU_0) ? T1_CTXNO_MAG + 2 : tmp;
+    return tmp2;
+}
+
+static INLINE OPJ_BYTE opj_t1_getspb(OPJ_UINT32 lu)
+{
+    return lut_spb[lu];
+}
+
+static OPJ_INT16 opj_t1_getnmsedec_sig(OPJ_UINT32 x, OPJ_UINT32 bitpos)
+{
+    if (bitpos > 0) {
+        return lut_nmsedec_sig[(x >> (bitpos)) & ((1 << T1_NMSEDEC_BITS) - 1)];
+    }
+
+    return lut_nmsedec_sig0[x & ((1 << T1_NMSEDEC_BITS) - 1)];
+}
+
+static OPJ_INT16 opj_t1_getnmsedec_ref(OPJ_UINT32 x, OPJ_UINT32 bitpos)
+{
+    if (bitpos > 0) {
+        return lut_nmsedec_ref[(x >> (bitpos)) & ((1 << T1_NMSEDEC_BITS) - 1)];
+    }
+
+    return lut_nmsedec_ref0[x & ((1 << T1_NMSEDEC_BITS) - 1)];
+}
+
+#define opj_t1_update_flags_macro(flags, flagsp, ci, s, stride, vsc) \
+{ \
+    /* east */ \
+    flagsp[-1] |= T1_SIGMA_5 << (3U * ci); \
+ \
+    /* mark target as significant */ \
+    flags |= ((s << T1_CHI_1_I) | T1_SIGMA_4) << (3U * ci); \
+ \
+    /* west */ \
+    flagsp[1] |= T1_SIGMA_3 << (3U * ci); \
+ \
+    /* north-west, north, north-east */ \
+    if (ci == 0U && !(vsc)) { \
+        opj_flag_t* north = flagsp - (stride); \
+        *north |= (s << T1_CHI_5_I) | T1_SIGMA_16; \
+        north[-1] |= T1_SIGMA_17; \
+        north[1] |= T1_SIGMA_15; \
+    } \
+ \
+    /* south-west, south, south-east */ \
+    if (ci == 3U) { \
+        opj_flag_t* south = flagsp + (stride); \
+        *south |= (s << T1_CHI_0_I) | T1_SIGMA_1; \
+        south[-1] |= T1_SIGMA_2; \
+        south[1] |= T1_SIGMA_0; \
+    } \
+}
+
+
+static INLINE void opj_t1_update_flags(opj_flag_t *flagsp, OPJ_UINT32 ci,
+                                       OPJ_UINT32 s, OPJ_UINT32 stride,
+                                       OPJ_UINT32 vsc)
+{
+    opj_t1_update_flags_macro(*flagsp, flagsp, ci, s, stride, vsc);
+}
+
+/**
+Encode significant pass
+*/
+#define opj_t1_enc_sigpass_step_macro(mqc, curctx, a, c, ct, flagspIn, datapIn, bpno, one, nmsedec, type, ciIn, vscIn) \
+{ \
+    OPJ_UINT32 v; \
+    const OPJ_UINT32 ci = (ciIn); \
+    const OPJ_UINT32 vsc = (vscIn); \
+    const OPJ_INT32* l_datap = (datapIn); \
+    opj_flag_t* flagsp = (flagspIn); \
+    OPJ_UINT32 const flags = *flagsp; \
+    if ((flags & ((T1_SIGMA_THIS | T1_PI_THIS) << (ci * 3U))) == 0U && \
+            (flags & (T1_SIGMA_NEIGHBOURS << (ci * 3U))) != 0U) { \
+        OPJ_UINT32 ctxt1 = opj_t1_getctxno_zc(mqc, flags >> (ci * 3U)); \
+        v = (opj_smr_abs(*l_datap) & (OPJ_UINT32)one) ? 1 : 0; \
+/* #ifdef DEBUG_ENC_SIG */ \
+/*        fprintf(stderr, "   ctxt1=%d\n", ctxt1); */ \
+/* #endif */ \
+        opj_t1_setcurctx(curctx, ctxt1); \
+        if (type == T1_TYPE_RAW) {  /* BYPASS/LAZY MODE */ \
+            opj_mqc_bypass_enc_macro(mqc, c, ct, v); \
+        } else { \
+            opj_mqc_encode_macro(mqc, curctx, a, c, ct, v); \
+        } \
+        if (v) { \
+            OPJ_UINT32 lu = opj_t1_getctxtno_sc_or_spb_index( \
+                                *flagsp, \
+                                flagsp[-1], flagsp[1], \
+                                ci); \
+            OPJ_UINT32 ctxt2 = opj_t1_getctxno_sc(lu); \
+            v = opj_smr_sign(*l_datap); \
+            *nmsedec += opj_t1_getnmsedec_sig(opj_smr_abs(*l_datap), \
+                                              (OPJ_UINT32)bpno); \
+/* #ifdef DEBUG_ENC_SIG */ \
+/*            fprintf(stderr, "   ctxt2=%d\n", ctxt2); */ \
+/* #endif */ \
+            opj_t1_setcurctx(curctx, ctxt2); \
+            if (type == T1_TYPE_RAW) {  /* BYPASS/LAZY MODE */ \
+                opj_mqc_bypass_enc_macro(mqc, c, ct, v); \
+            } else { \
+                OPJ_UINT32 spb = opj_t1_getspb(lu); \
+/* #ifdef DEBUG_ENC_SIG */ \
+/*                fprintf(stderr, "   spb=%d\n", spb); */ \
+/* #endif */ \
+                opj_mqc_encode_macro(mqc, curctx, a, c, ct, v ^ spb); \
+            } \
+            opj_t1_update_flags(flagsp, ci, v, t1->w + 2, vsc); \
+        } \
+        *flagsp |= T1_PI_THIS << (ci * 3U); \
+    } \
+}
+
+static INLINE void opj_t1_dec_sigpass_step_raw(
+    opj_t1_t *t1,
+    opj_flag_t *flagsp,
+    OPJ_INT32 *datap,
+    OPJ_INT32 oneplushalf,
+    OPJ_UINT32 vsc,
+    OPJ_UINT32 ci)
+{
+    OPJ_UINT32 v;
+    opj_mqc_t *mqc = &(t1->mqc);       /* RAW component */
+
+    OPJ_UINT32 const flags = *flagsp;
+
+    if ((flags & ((T1_SIGMA_THIS | T1_PI_THIS) << (ci * 3U))) == 0U &&
+            (flags & (T1_SIGMA_NEIGHBOURS << (ci * 3U))) != 0U) {
+        if (opj_mqc_raw_decode(mqc)) {
+            v = opj_mqc_raw_decode(mqc);
+            *datap = v ? -oneplushalf : oneplushalf;
+            opj_t1_update_flags(flagsp, ci, v, t1->w + 2, vsc);
+        }
+        *flagsp |= T1_PI_THIS << (ci * 3U);
+    }
+}
+
+#define opj_t1_dec_sigpass_step_mqc_macro(flags, flagsp, flags_stride, data, \
+                                          data_stride, ci, mqc, curctx, \
+                                          v, a, c, ct, oneplushalf, vsc) \
+{ \
+    if ((flags & ((T1_SIGMA_THIS | T1_PI_THIS) << (ci * 3U))) == 0U && \
+        (flags & (T1_SIGMA_NEIGHBOURS << (ci * 3U))) != 0U) { \
+        OPJ_UINT32 ctxt1 = opj_t1_getctxno_zc(mqc, flags >> (ci * 3U)); \
+        opj_t1_setcurctx(curctx, ctxt1); \
+        opj_mqc_decode_macro(v, mqc, curctx, a, c, ct); \
+        if (v) { \
+            OPJ_UINT32 lu = opj_t1_getctxtno_sc_or_spb_index( \
+                                flags, \
+                                flagsp[-1], flagsp[1], \
+                                ci); \
+            OPJ_UINT32 ctxt2 = opj_t1_getctxno_sc(lu); \
+            OPJ_UINT32 spb = opj_t1_getspb(lu); \
+            opj_t1_setcurctx(curctx, ctxt2); \
+            opj_mqc_decode_macro(v, mqc, curctx, a, c, ct); \
+            v = v ^ spb; \
+            data[ci*data_stride] = v ? -oneplushalf : oneplushalf; \
+            opj_t1_update_flags_macro(flags, flagsp, ci, v, flags_stride, vsc); \
+        } \
+        flags |= T1_PI_THIS << (ci * 3U); \
+    } \
+}
+
+static INLINE void opj_t1_dec_sigpass_step_mqc(
+    opj_t1_t *t1,
+    opj_flag_t *flagsp,
+    OPJ_INT32 *datap,
+    OPJ_INT32 oneplushalf,
+    OPJ_UINT32 ci,
+    OPJ_UINT32 flags_stride,
+    OPJ_UINT32 vsc)
+{
+    OPJ_UINT32 v;
+
+    opj_mqc_t *mqc = &(t1->mqc);       /* MQC component */
+    opj_t1_dec_sigpass_step_mqc_macro(*flagsp, flagsp, flags_stride, datap,
+                                      0, ci, mqc, mqc->curctx,
+                                      v, mqc->a, mqc->c, mqc->ct, oneplushalf, vsc);
+}
+
+static void opj_t1_enc_sigpass(opj_t1_t *t1,
+                               OPJ_INT32 bpno,
+                               OPJ_INT32 *nmsedec,
+                               OPJ_BYTE type,
+                               OPJ_UINT32 cblksty
+                              )
+{
+    OPJ_UINT32 i, k;
+    OPJ_INT32 const one = 1 << (bpno + T1_NMSEDEC_FRACBITS);
+    opj_flag_t* f = &T1_FLAGS(0, 0);
+    OPJ_UINT32 const extra = 2;
+    opj_mqc_t* mqc = &(t1->mqc);
+    DOWNLOAD_MQC_VARIABLES(mqc, curctx, a, c, ct);
+    const OPJ_INT32* datap = t1->data;
+
+    *nmsedec = 0;
+#ifdef DEBUG_ENC_SIG
+    fprintf(stderr, "enc_sigpass: bpno=%d\n", bpno);
+#endif
+    for (k = 0; k < (t1->h & ~3U); k += 4, f += extra) {
+        const OPJ_UINT32 w = t1->w;
+#ifdef DEBUG_ENC_SIG
+        fprintf(stderr, " k=%d\n", k);
+#endif
+        for (i = 0; i < w; ++i, ++f, datap += 4) {
+#ifdef DEBUG_ENC_SIG
+            fprintf(stderr, " i=%d\n", i);
+#endif
+            if (*f == 0U) {
+                /* Nothing to do for any of the 4 data points */
+                continue;
+            }
+            opj_t1_enc_sigpass_step_macro(
+                mqc, curctx, a, c, ct,
+                f,
+                &datap[0],
+                bpno,
+                one,
+                nmsedec,
+                type,
+                0, cblksty & J2K_CCP_CBLKSTY_VSC);
+            opj_t1_enc_sigpass_step_macro(
+                mqc, curctx, a, c, ct,
+                f,
+                &datap[1],
+                bpno,
+                one,
+                nmsedec,
+                type,
+                1, 0);
+            opj_t1_enc_sigpass_step_macro(
+                mqc, curctx, a, c, ct,
+                f,
+                &datap[2],
+                bpno,
+                one,
+                nmsedec,
+                type,
+                2, 0);
+            opj_t1_enc_sigpass_step_macro(
+                mqc, curctx, a, c, ct,
+                f,
+                &datap[3],
+                bpno,
+                one,
+                nmsedec,
+                type,
+                3, 0);
+        }
+    }
+
+    if (k < t1->h) {
+        OPJ_UINT32 j;
+#ifdef DEBUG_ENC_SIG
+        fprintf(stderr, " k=%d\n", k);
+#endif
+        for (i = 0; i < t1->w; ++i, ++f) {
+#ifdef DEBUG_ENC_SIG
+            fprintf(stderr, " i=%d\n", i);
+#endif
+            if (*f == 0U) {
+                /* Nothing to do for any of the 4 data points */
+                datap += (t1->h - k);
+                continue;
+            }
+            for (j = k; j < t1->h; ++j, ++datap) {
+                opj_t1_enc_sigpass_step_macro(
+                    mqc, curctx, a, c, ct,
+                    f,
+                    &datap[0],
+                    bpno,
+                    one,
+                    nmsedec,
+                    type,
+                    j - k,
+                    (j == k && (cblksty & J2K_CCP_CBLKSTY_VSC) != 0));
+            }
+        }
+    }
+
+    UPLOAD_MQC_VARIABLES(mqc, curctx, a, c, ct);
+}
+
+static void opj_t1_dec_sigpass_raw(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno,
+    OPJ_INT32 cblksty)
+{
+    OPJ_INT32 one, half, oneplushalf;
+    OPJ_UINT32 i, j, k;
+    OPJ_INT32 *data = t1->data;
+    opj_flag_t *flagsp = &T1_FLAGS(0, 0);
+    const OPJ_UINT32 l_w = t1->w;
+    one = 1 << bpno;
+    half = one >> 1;
+    oneplushalf = one | half;
+
+    for (k = 0; k < (t1->h & ~3U); k += 4, flagsp += 2, data += 3 * l_w) {
+        for (i = 0; i < l_w; ++i, ++flagsp, ++data) {
+            opj_flag_t flags = *flagsp;
+            if (flags != 0) {
+                opj_t1_dec_sigpass_step_raw(
+                    t1,
+                    flagsp,
+                    data,
+                    oneplushalf,
+                    cblksty & J2K_CCP_CBLKSTY_VSC, /* vsc */
+                    0U);
+                opj_t1_dec_sigpass_step_raw(
+                    t1,
+                    flagsp,
+                    data + l_w,
+                    oneplushalf,
+                    OPJ_FALSE, /* vsc */
+                    1U);
+                opj_t1_dec_sigpass_step_raw(
+                    t1,
+                    flagsp,
+                    data + 2 * l_w,
+                    oneplushalf,
+                    OPJ_FALSE, /* vsc */
+                    2U);
+                opj_t1_dec_sigpass_step_raw(
+                    t1,
+                    flagsp,
+                    data + 3 * l_w,
+                    oneplushalf,
+                    OPJ_FALSE, /* vsc */
+                    3U);
+            }
+        }
+    }
+    if (k < t1->h) {
+        for (i = 0; i < l_w; ++i, ++flagsp, ++data) {
+            for (j = 0; j < t1->h - k; ++j) {
+                opj_t1_dec_sigpass_step_raw(
+                    t1,
+                    flagsp,
+                    data + j * l_w,
+                    oneplushalf,
+                    cblksty & J2K_CCP_CBLKSTY_VSC, /* vsc */
+                    j);
+            }
+        }
+    }
+}
+
+#define opj_t1_dec_sigpass_mqc_internal(t1, bpno, vsc, w, h, flags_stride) \
+{ \
+        OPJ_INT32 one, half, oneplushalf; \
+        OPJ_UINT32 i, j, k; \
+        register OPJ_INT32 *data = t1->data; \
+        register opj_flag_t *flagsp = &t1->flags[(flags_stride) + 1]; \
+        const OPJ_UINT32 l_w = w; \
+        opj_mqc_t* mqc = &(t1->mqc); \
+        DOWNLOAD_MQC_VARIABLES(mqc, curctx, a, c, ct); \
+        register OPJ_UINT32 v; \
+        one = 1 << bpno; \
+        half = one >> 1; \
+        oneplushalf = one | half; \
+        for (k = 0; k < (h & ~3u); k += 4, data += 3*l_w, flagsp += 2) { \
+                for (i = 0; i < l_w; ++i, ++data, ++flagsp) { \
+                        opj_flag_t flags = *flagsp; \
+                        if( flags != 0 ) { \
+                            opj_t1_dec_sigpass_step_mqc_macro( \
+                                flags, flagsp, flags_stride, data, \
+                                l_w, 0, mqc, curctx, v, a, c, ct, oneplushalf, vsc); \
+                            opj_t1_dec_sigpass_step_mqc_macro( \
+                                flags, flagsp, flags_stride, data, \
+                                l_w, 1, mqc, curctx, v, a, c, ct, oneplushalf, OPJ_FALSE); \
+                            opj_t1_dec_sigpass_step_mqc_macro( \
+                                flags, flagsp, flags_stride, data, \
+                                l_w, 2, mqc, curctx, v, a, c, ct, oneplushalf, OPJ_FALSE); \
+                            opj_t1_dec_sigpass_step_mqc_macro( \
+                                flags, flagsp, flags_stride, data, \
+                                l_w, 3, mqc, curctx, v, a, c, ct, oneplushalf, OPJ_FALSE); \
+                            *flagsp = flags; \
+                        } \
+                } \
+        } \
+        UPLOAD_MQC_VARIABLES(mqc, curctx, a, c, ct); \
+        if( k < h ) { \
+            for (i = 0; i < l_w; ++i, ++data, ++flagsp) { \
+                for (j = 0; j < h - k; ++j) { \
+                        opj_t1_dec_sigpass_step_mqc(t1, flagsp, \
+                            data + j * l_w, oneplushalf, j, flags_stride, vsc); \
+                } \
+            } \
+        } \
+}
+
+static void opj_t1_dec_sigpass_mqc_64x64_novsc(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno)
+{
+    opj_t1_dec_sigpass_mqc_internal(t1, bpno, OPJ_FALSE, 64, 64, 66);
+}
+
+static void opj_t1_dec_sigpass_mqc_64x64_vsc(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno)
+{
+    opj_t1_dec_sigpass_mqc_internal(t1, bpno, OPJ_TRUE, 64, 64, 66);
+}
+
+static void opj_t1_dec_sigpass_mqc_generic_novsc(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno)
+{
+    opj_t1_dec_sigpass_mqc_internal(t1, bpno, OPJ_FALSE, t1->w, t1->h,
+                                    t1->w + 2U);
+}
+
+static void opj_t1_dec_sigpass_mqc_generic_vsc(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno)
+{
+    opj_t1_dec_sigpass_mqc_internal(t1, bpno, OPJ_TRUE, t1->w, t1->h,
+                                    t1->w + 2U);
+}
+
+static void opj_t1_dec_sigpass_mqc(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno,
+    OPJ_INT32 cblksty)
+{
+    if (t1->w == 64 && t1->h == 64) {
+        if (cblksty & J2K_CCP_CBLKSTY_VSC) {
+            opj_t1_dec_sigpass_mqc_64x64_vsc(t1, bpno);
+        } else {
+            opj_t1_dec_sigpass_mqc_64x64_novsc(t1, bpno);
+        }
+    } else {
+        if (cblksty & J2K_CCP_CBLKSTY_VSC) {
+            opj_t1_dec_sigpass_mqc_generic_vsc(t1, bpno);
+        } else {
+            opj_t1_dec_sigpass_mqc_generic_novsc(t1, bpno);
+        }
+    }
+}
+
+/**
+Encode refinement pass step
+*/
+#define opj_t1_enc_refpass_step_macro(mqc, curctx, a, c, ct, flags, flagsUpdated, datap, bpno, one, nmsedec, type, ci) \
+{\
+    OPJ_UINT32 v; \
+    if ((flags & ((T1_SIGMA_THIS | T1_PI_THIS) << ((ci) * 3U))) == (T1_SIGMA_THIS << ((ci) * 3U))) { \
+        const OPJ_UINT32 shift_flags = (flags >> ((ci) * 3U)); \
+        OPJ_UINT32 ctxt = opj_t1_getctxno_mag(shift_flags); \
+        OPJ_UINT32 abs_data = opj_smr_abs(*datap); \
+        *nmsedec += opj_t1_getnmsedec_ref(abs_data, \
+                                          (OPJ_UINT32)bpno); \
+        v = ((OPJ_INT32)abs_data & one) ? 1 : 0; \
+/* #ifdef DEBUG_ENC_REF */ \
+/*        fprintf(stderr, "  ctxt=%d\n", ctxt); */ \
+/* #endif */ \
+        opj_t1_setcurctx(curctx, ctxt); \
+        if (type == T1_TYPE_RAW) {  /* BYPASS/LAZY MODE */ \
+            opj_mqc_bypass_enc_macro(mqc, c, ct, v); \
+        } else { \
+            opj_mqc_encode_macro(mqc, curctx, a, c, ct, v); \
+        } \
+        flagsUpdated |= T1_MU_THIS << ((ci) * 3U); \
+    } \
+}
+
+
+static INLINE void opj_t1_dec_refpass_step_raw(
+    opj_t1_t *t1,
+    opj_flag_t *flagsp,
+    OPJ_INT32 *datap,
+    OPJ_INT32 poshalf,
+    OPJ_UINT32 ci)
+{
+    OPJ_UINT32 v;
+
+    opj_mqc_t *mqc = &(t1->mqc);       /* RAW component */
+
+    if ((*flagsp & ((T1_SIGMA_THIS | T1_PI_THIS) << (ci * 3U))) ==
+            (T1_SIGMA_THIS << (ci * 3U))) {
+        v = opj_mqc_raw_decode(mqc);
+        *datap += (v ^ (*datap < 0)) ? poshalf : -poshalf;
+        *flagsp |= T1_MU_THIS << (ci * 3U);
+    }
+}
+
+#define opj_t1_dec_refpass_step_mqc_macro(flags, data, data_stride, ci, \
+                                          mqc, curctx, v, a, c, ct, poshalf) \
+{ \
+    if ((flags & ((T1_SIGMA_THIS | T1_PI_THIS) << (ci * 3U))) == \
+            (T1_SIGMA_THIS << (ci * 3U))) { \
+        OPJ_UINT32 ctxt = opj_t1_getctxno_mag(flags >> (ci * 3U)); \
+        opj_t1_setcurctx(curctx, ctxt); \
+        opj_mqc_decode_macro(v, mqc, curctx, a, c, ct); \
+        data[ci*data_stride] += (v ^ (data[ci*data_stride] < 0)) ? poshalf : -poshalf; \
+        flags |= T1_MU_THIS << (ci * 3U); \
+    } \
+}
+
+static INLINE void opj_t1_dec_refpass_step_mqc(
+    opj_t1_t *t1,
+    opj_flag_t *flagsp,
+    OPJ_INT32 *datap,
+    OPJ_INT32 poshalf,
+    OPJ_UINT32 ci)
+{
+    OPJ_UINT32 v;
+
+    opj_mqc_t *mqc = &(t1->mqc);       /* MQC component */
+    opj_t1_dec_refpass_step_mqc_macro(*flagsp, datap, 0, ci,
+                                      mqc, mqc->curctx, v, mqc->a, mqc->c,
+                                      mqc->ct, poshalf);
+}
+
+static void opj_t1_enc_refpass(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno,
+    OPJ_INT32 *nmsedec,
+    OPJ_BYTE type)
+{
+    OPJ_UINT32 i, k;
+    const OPJ_INT32 one = 1 << (bpno + T1_NMSEDEC_FRACBITS);
+    opj_flag_t* f = &T1_FLAGS(0, 0);
+    const OPJ_UINT32 extra = 2U;
+    opj_mqc_t* mqc = &(t1->mqc);
+    DOWNLOAD_MQC_VARIABLES(mqc, curctx, a, c, ct);
+    const OPJ_INT32* datap = t1->data;
+
+    *nmsedec = 0;
+#ifdef DEBUG_ENC_REF
+    fprintf(stderr, "enc_refpass: bpno=%d\n", bpno);
+#endif
+    for (k = 0; k < (t1->h & ~3U); k += 4, f += extra) {
+#ifdef DEBUG_ENC_REF
+        fprintf(stderr, " k=%d\n", k);
+#endif
+        for (i = 0; i < t1->w; ++i, f++, datap += 4) {
+            const OPJ_UINT32 flags = *f;
+            OPJ_UINT32 flagsUpdated = flags;
+#ifdef DEBUG_ENC_REF
+            fprintf(stderr, " i=%d\n", i);
+#endif
+            if ((flags & (T1_SIGMA_4 | T1_SIGMA_7 | T1_SIGMA_10 | T1_SIGMA_13)) == 0) {
+                /* none significant */
+                continue;
+            }
+            if ((flags & (T1_PI_0 | T1_PI_1 | T1_PI_2 | T1_PI_3)) ==
+                    (T1_PI_0 | T1_PI_1 | T1_PI_2 | T1_PI_3)) {
+                /* all processed by sigpass */
+                continue;
+            }
+
+            opj_t1_enc_refpass_step_macro(
+                mqc, curctx, a, c, ct,
+                flags, flagsUpdated,
+                &datap[0],
+                bpno,
+                one,
+                nmsedec,
+                type,
+                0);
+            opj_t1_enc_refpass_step_macro(
+                mqc, curctx, a, c, ct,
+                flags, flagsUpdated,
+                &datap[1],
+                bpno,
+                one,
+                nmsedec,
+                type,
+                1);
+            opj_t1_enc_refpass_step_macro(
+                mqc, curctx, a, c, ct,
+                flags, flagsUpdated,
+                &datap[2],
+                bpno,
+                one,
+                nmsedec,
+                type,
+                2);
+            opj_t1_enc_refpass_step_macro(
+                mqc, curctx, a, c, ct,
+                flags, flagsUpdated,
+                &datap[3],
+                bpno,
+                one,
+                nmsedec,
+                type,
+                3);
+            *f = flagsUpdated;
+        }
+    }
+
+    if (k < t1->h) {
+        OPJ_UINT32 j;
+        const OPJ_UINT32 remaining_lines = t1->h - k;
+#ifdef DEBUG_ENC_REF
+        fprintf(stderr, " k=%d\n", k);
+#endif
+        for (i = 0; i < t1->w; ++i, ++f) {
+#ifdef DEBUG_ENC_REF
+            fprintf(stderr, " i=%d\n", i);
+#endif
+            if ((*f & (T1_SIGMA_4 | T1_SIGMA_7 | T1_SIGMA_10 | T1_SIGMA_13)) == 0) {
+                /* none significant */
+                datap += remaining_lines;
+                continue;
+            }
+            for (j = 0; j < remaining_lines; ++j, datap ++) {
+                opj_t1_enc_refpass_step_macro(
+                    mqc, curctx, a, c, ct,
+                    *f, *f,
+                    &datap[0],
+                    bpno,
+                    one,
+                    nmsedec,
+                    type,
+                    j);
+            }
+        }
+    }
+
+    UPLOAD_MQC_VARIABLES(mqc, curctx, a, c, ct);
+}
+
+
+static void opj_t1_dec_refpass_raw(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno)
+{
+    OPJ_INT32 one, poshalf;
+    OPJ_UINT32 i, j, k;
+    OPJ_INT32 *data = t1->data;
+    opj_flag_t *flagsp = &T1_FLAGS(0, 0);
+    const OPJ_UINT32 l_w = t1->w;
+    one = 1 << bpno;
+    poshalf = one >> 1;
+    for (k = 0; k < (t1->h & ~3U); k += 4, flagsp += 2, data += 3 * l_w) {
+        for (i = 0; i < l_w; ++i, ++flagsp, ++data) {
+            opj_flag_t flags = *flagsp;
+            if (flags != 0) {
+                opj_t1_dec_refpass_step_raw(
+                    t1,
+                    flagsp,
+                    data,
+                    poshalf,
+                    0U);
+                opj_t1_dec_refpass_step_raw(
+                    t1,
+                    flagsp,
+                    data + l_w,
+                    poshalf,
+                    1U);
+                opj_t1_dec_refpass_step_raw(
+                    t1,
+                    flagsp,
+                    data + 2 * l_w,
+                    poshalf,
+                    2U);
+                opj_t1_dec_refpass_step_raw(
+                    t1,
+                    flagsp,
+                    data + 3 * l_w,
+                    poshalf,
+                    3U);
+            }
+        }
+    }
+    if (k < t1->h) {
+        for (i = 0; i < l_w; ++i, ++flagsp, ++data) {
+            for (j = 0; j < t1->h - k; ++j) {
+                opj_t1_dec_refpass_step_raw(
+                    t1,
+                    flagsp,
+                    data + j * l_w,
+                    poshalf,
+                    j);
+            }
+        }
+    }
+}
+
+#define opj_t1_dec_refpass_mqc_internal(t1, bpno, w, h, flags_stride) \
+{ \
+        OPJ_INT32 one, poshalf; \
+        OPJ_UINT32 i, j, k; \
+        register OPJ_INT32 *data = t1->data; \
+        register opj_flag_t *flagsp = &t1->flags[flags_stride + 1]; \
+        const OPJ_UINT32 l_w = w; \
+        opj_mqc_t* mqc = &(t1->mqc); \
+        DOWNLOAD_MQC_VARIABLES(mqc, curctx, a, c, ct); \
+        register OPJ_UINT32 v; \
+        one = 1 << bpno; \
+        poshalf = one >> 1; \
+        for (k = 0; k < (h & ~3u); k += 4, data += 3*l_w, flagsp += 2) { \
+                for (i = 0; i < l_w; ++i, ++data, ++flagsp) { \
+                        opj_flag_t flags = *flagsp; \
+                        if( flags != 0 ) { \
+                            opj_t1_dec_refpass_step_mqc_macro( \
+                                flags, data, l_w, 0, \
+                                mqc, curctx, v, a, c, ct, poshalf); \
+                            opj_t1_dec_refpass_step_mqc_macro( \
+                                flags, data, l_w, 1, \
+                                mqc, curctx, v, a, c, ct, poshalf); \
+                            opj_t1_dec_refpass_step_mqc_macro( \
+                                flags, data, l_w, 2, \
+                                mqc, curctx, v, a, c, ct, poshalf); \
+                            opj_t1_dec_refpass_step_mqc_macro( \
+                                flags, data, l_w, 3, \
+                                mqc, curctx, v, a, c, ct, poshalf); \
+                            *flagsp = flags; \
+                        } \
+                } \
+        } \
+        UPLOAD_MQC_VARIABLES(mqc, curctx, a, c, ct); \
+        if( k < h ) { \
+            for (i = 0; i < l_w; ++i, ++data, ++flagsp) { \
+                for (j = 0; j < h - k; ++j) { \
+                        opj_t1_dec_refpass_step_mqc(t1, flagsp, data + j * l_w, poshalf, j); \
+                } \
+            } \
+        } \
+}
+
+static void opj_t1_dec_refpass_mqc_64x64(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno)
+{
+    opj_t1_dec_refpass_mqc_internal(t1, bpno, 64, 64, 66);
+}
+
+static void opj_t1_dec_refpass_mqc_generic(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno)
+{
+    opj_t1_dec_refpass_mqc_internal(t1, bpno, t1->w, t1->h, t1->w + 2U);
+}
+
+static void opj_t1_dec_refpass_mqc(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno)
+{
+    if (t1->w == 64 && t1->h == 64) {
+        opj_t1_dec_refpass_mqc_64x64(t1, bpno);
+    } else {
+        opj_t1_dec_refpass_mqc_generic(t1, bpno);
+    }
+}
+
+/**
+Encode clean-up pass step
+*/
+#define opj_t1_enc_clnpass_step_macro(mqc, curctx, a, c, ct, flagspIn, datapIn, bpno, one, nmsedec, agg, runlen, lim, cblksty) \
+{ \
+    OPJ_UINT32 v; \
+    OPJ_UINT32 ci; \
+    opj_flag_t* const flagsp = (flagspIn); \
+    const OPJ_INT32* l_datap = (datapIn); \
+    const OPJ_UINT32 check = (T1_SIGMA_4 | T1_SIGMA_7 | T1_SIGMA_10 | T1_SIGMA_13 | \
+                              T1_PI_0 | T1_PI_1 | T1_PI_2 | T1_PI_3); \
+ \
+    if ((*flagsp & check) == check) { \
+        if (runlen == 0) { \
+            *flagsp &= ~(T1_PI_0 | T1_PI_1 | T1_PI_2 | T1_PI_3); \
+        } else if (runlen == 1) { \
+            *flagsp &= ~(T1_PI_1 | T1_PI_2 | T1_PI_3); \
+        } else if (runlen == 2) { \
+            *flagsp &= ~(T1_PI_2 | T1_PI_3); \
+        } else if (runlen == 3) { \
+            *flagsp &= ~(T1_PI_3); \
+        } \
+    } \
+    else \
+    for (ci = runlen; ci < lim; ++ci) { \
+        OPJ_BOOL goto_PARTIAL = OPJ_FALSE; \
+        if ((agg != 0) && (ci == runlen)) { \
+            goto_PARTIAL = OPJ_TRUE; \
+        } \
+        else if (!(*flagsp & ((T1_SIGMA_THIS | T1_PI_THIS) << (ci * 3U)))) { \
+            OPJ_UINT32 ctxt1 = opj_t1_getctxno_zc(mqc, *flagsp >> (ci * 3U)); \
+/* #ifdef DEBUG_ENC_CLN */ \
+/*            printf("   ctxt1=%d\n", ctxt1); */ \
+/* #endif */ \
+            opj_t1_setcurctx(curctx, ctxt1); \
+            v = (opj_smr_abs(*l_datap) & (OPJ_UINT32)one) ? 1 : 0; \
+            opj_mqc_encode_macro(mqc, curctx, a, c, ct, v); \
+            if (v) { \
+                goto_PARTIAL = OPJ_TRUE; \
+            } \
+        } \
+        if( goto_PARTIAL ) { \
+            OPJ_UINT32 vsc; \
+            OPJ_UINT32 ctxt2, spb; \
+            OPJ_UINT32 lu = opj_t1_getctxtno_sc_or_spb_index( \
+                        *flagsp, \
+                        flagsp[-1], flagsp[1], \
+                        ci); \
+            *nmsedec += opj_t1_getnmsedec_sig(opj_smr_abs(*l_datap), \
+                                                (OPJ_UINT32)bpno); \
+            ctxt2 = opj_t1_getctxno_sc(lu); \
+/* #ifdef DEBUG_ENC_CLN */ \
+/*           printf("   ctxt2=%d\n", ctxt2); */ \
+/* #endif */ \
+            opj_t1_setcurctx(curctx, ctxt2); \
+ \
+            v = opj_smr_sign(*l_datap); \
+            spb = opj_t1_getspb(lu); \
+/* #ifdef DEBUG_ENC_CLN */ \
+/*           printf("   spb=%d\n", spb); */\
+/* #endif */ \
+            opj_mqc_encode_macro(mqc, curctx, a, c, ct, v ^ spb); \
+            vsc = ((cblksty & J2K_CCP_CBLKSTY_VSC) && (ci == 0)) ? 1 : 0; \
+            opj_t1_update_flags(flagsp, ci, v, t1->w + 2U, vsc); \
+        } \
+        *flagsp &= ~(T1_PI_THIS << (3U * ci)); \
+        l_datap ++; \
+    } \
+}
+
+#define opj_t1_dec_clnpass_step_macro(check_flags, partial, \
+                                      flags, flagsp, flags_stride, data, \
+                                      data_stride, ci, mqc, curctx, \
+                                      v, a, c, ct, oneplushalf, vsc) \
+{ \
+    if ( !check_flags || !(flags & ((T1_SIGMA_THIS | T1_PI_THIS) << (ci * 3U)))) {\
+        do { \
+            if( !partial ) { \
+                OPJ_UINT32 ctxt1 = opj_t1_getctxno_zc(mqc, flags >> (ci * 3U)); \
+                opj_t1_setcurctx(curctx, ctxt1); \
+                opj_mqc_decode_macro(v, mqc, curctx, a, c, ct); \
+                if( !v ) \
+                    break; \
+            } \
+            { \
+                OPJ_UINT32 lu = opj_t1_getctxtno_sc_or_spb_index( \
+                                    flags, flagsp[-1], flagsp[1], \
+                                    ci); \
+                opj_t1_setcurctx(curctx, opj_t1_getctxno_sc(lu)); \
+                opj_mqc_decode_macro(v, mqc, curctx, a, c, ct); \
+                v = v ^ opj_t1_getspb(lu); \
+                data[ci*data_stride] = v ? -oneplushalf : oneplushalf; \
+                opj_t1_update_flags_macro(flags, flagsp, ci, v, flags_stride, vsc); \
+            } \
+        } while(0); \
+    } \
+}
+
+static void opj_t1_dec_clnpass_step(
+    opj_t1_t *t1,
+    opj_flag_t *flagsp,
+    OPJ_INT32 *datap,
+    OPJ_INT32 oneplushalf,
+    OPJ_UINT32 ci,
+    OPJ_UINT32 vsc)
+{
+    OPJ_UINT32 v;
+
+    opj_mqc_t *mqc = &(t1->mqc);   /* MQC component */
+    opj_t1_dec_clnpass_step_macro(OPJ_TRUE, OPJ_FALSE,
+                                  *flagsp, flagsp, t1->w + 2U, datap,
+                                  0, ci, mqc, mqc->curctx,
+                                  v, mqc->a, mqc->c, mqc->ct, oneplushalf, vsc);
+}
+
+static void opj_t1_enc_clnpass(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno,
+    OPJ_INT32 *nmsedec,
+    OPJ_UINT32 cblksty)
+{
+    OPJ_UINT32 i, k;
+    const OPJ_INT32 one = 1 << (bpno + T1_NMSEDEC_FRACBITS);
+    opj_mqc_t* mqc = &(t1->mqc);
+    DOWNLOAD_MQC_VARIABLES(mqc, curctx, a, c, ct);
+    const OPJ_INT32* datap = t1->data;
+    opj_flag_t *f = &T1_FLAGS(0, 0);
+    const OPJ_UINT32 extra = 2U;
+
+    *nmsedec = 0;
+#ifdef DEBUG_ENC_CLN
+    printf("enc_clnpass: bpno=%d\n", bpno);
+#endif
+    for (k = 0; k < (t1->h & ~3U); k += 4, f += extra) {
+#ifdef DEBUG_ENC_CLN
+        printf(" k=%d\n", k);
+#endif
+        for (i = 0; i < t1->w; ++i, f++) {
+            OPJ_UINT32 agg, runlen;
+#ifdef DEBUG_ENC_CLN
+            printf("  i=%d\n", i);
+#endif
+            agg = !*f;
+#ifdef DEBUG_ENC_CLN
+            printf("   agg=%d\n", agg);
+#endif
+            if (agg) {
+                for (runlen = 0; runlen < 4; ++runlen, ++datap) {
+                    if (opj_smr_abs(*datap) & (OPJ_UINT32)one) {
+                        break;
+                    }
+                }
+                opj_t1_setcurctx(curctx, T1_CTXNO_AGG);
+                opj_mqc_encode_macro(mqc, curctx, a, c, ct, runlen != 4);
+                if (runlen == 4) {
+                    continue;
+                }
+                opj_t1_setcurctx(curctx, T1_CTXNO_UNI);
+                opj_mqc_encode_macro(mqc, curctx, a, c, ct, runlen >> 1);
+                opj_mqc_encode_macro(mqc, curctx, a, c, ct, runlen & 1);
+            } else {
+                runlen = 0;
+            }
+            opj_t1_enc_clnpass_step_macro(
+                mqc, curctx, a, c, ct,
+                f,
+                datap,
+                bpno,
+                one,
+                nmsedec,
+                agg,
+                runlen,
+                4U,
+                cblksty);
+            datap += 4 - runlen;
+        }
+    }
+    if (k < t1->h) {
+        const OPJ_UINT32 agg = 0;
+        const OPJ_UINT32 runlen = 0;
+#ifdef DEBUG_ENC_CLN
+        printf(" k=%d\n", k);
+#endif
+        for (i = 0; i < t1->w; ++i, f++) {
+#ifdef DEBUG_ENC_CLN
+            printf("  i=%d\n", i);
+            printf("   agg=%d\n", agg);
+#endif
+            opj_t1_enc_clnpass_step_macro(
+                mqc, curctx, a, c, ct,
+                f,
+                datap,
+                bpno,
+                one,
+                nmsedec,
+                agg,
+                runlen,
+                t1->h - k,
+                cblksty);
+            datap += t1->h - k;
+        }
+    }
+
+    UPLOAD_MQC_VARIABLES(mqc, curctx, a, c, ct);
+}
+
+#define opj_t1_dec_clnpass_internal(t1, bpno, vsc, w, h, flags_stride) \
+{ \
+    OPJ_INT32 one, half, oneplushalf; \
+    OPJ_UINT32 runlen; \
+    OPJ_UINT32 i, j, k; \
+    const OPJ_UINT32 l_w = w; \
+    opj_mqc_t* mqc = &(t1->mqc); \
+    register OPJ_INT32 *data = t1->data; \
+    register opj_flag_t *flagsp = &t1->flags[flags_stride + 1]; \
+    DOWNLOAD_MQC_VARIABLES(mqc, curctx, a, c, ct); \
+    register OPJ_UINT32 v; \
+    one = 1 << bpno; \
+    half = one >> 1; \
+    oneplushalf = one | half; \
+    for (k = 0; k < (h & ~3u); k += 4, data += 3*l_w, flagsp += 2) { \
+        for (i = 0; i < l_w; ++i, ++data, ++flagsp) { \
+            opj_flag_t flags = *flagsp; \
+            if (flags == 0) { \
+                OPJ_UINT32 partial = OPJ_TRUE; \
+                opj_t1_setcurctx(curctx, T1_CTXNO_AGG); \
+                opj_mqc_decode_macro(v, mqc, curctx, a, c, ct); \
+                if (!v) { \
+                    continue; \
+                } \
+                opj_t1_setcurctx(curctx, T1_CTXNO_UNI); \
+                opj_mqc_decode_macro(runlen, mqc, curctx, a, c, ct); \
+                opj_mqc_decode_macro(v, mqc, curctx, a, c, ct); \
+                runlen = (runlen << 1) | v; \
+                switch(runlen) { \
+                    case 0: \
+                        opj_t1_dec_clnpass_step_macro(OPJ_FALSE, OPJ_TRUE,\
+                                            flags, flagsp, flags_stride, data, \
+                                            l_w, 0, mqc, curctx, \
+                                            v, a, c, ct, oneplushalf, vsc); \
+                        partial = OPJ_FALSE; \
+                        /* FALLTHRU */ \
+                    case 1: \
+                        opj_t1_dec_clnpass_step_macro(OPJ_FALSE, partial,\
+                                            flags, flagsp, flags_stride, data, \
+                                            l_w, 1, mqc, curctx, \
+                                            v, a, c, ct, oneplushalf, OPJ_FALSE); \
+                        partial = OPJ_FALSE; \
+                        /* FALLTHRU */ \
+                    case 2: \
+                        opj_t1_dec_clnpass_step_macro(OPJ_FALSE, partial,\
+                                            flags, flagsp, flags_stride, data, \
+                                            l_w, 2, mqc, curctx, \
+                                            v, a, c, ct, oneplushalf, OPJ_FALSE); \
+                        partial = OPJ_FALSE; \
+                        /* FALLTHRU */ \
+                    case 3: \
+                        opj_t1_dec_clnpass_step_macro(OPJ_FALSE, partial,\
+                                            flags, flagsp, flags_stride, data, \
+                                            l_w, 3, mqc, curctx, \
+                                            v, a, c, ct, oneplushalf, OPJ_FALSE); \
+                        break; \
+                } \
+            } else { \
+                opj_t1_dec_clnpass_step_macro(OPJ_TRUE, OPJ_FALSE, \
+                                    flags, flagsp, flags_stride, data, \
+                                    l_w, 0, mqc, curctx, \
+                                    v, a, c, ct, oneplushalf, vsc); \
+                opj_t1_dec_clnpass_step_macro(OPJ_TRUE, OPJ_FALSE, \
+                                    flags, flagsp, flags_stride, data, \
+                                    l_w, 1, mqc, curctx, \
+                                    v, a, c, ct, oneplushalf, OPJ_FALSE); \
+                opj_t1_dec_clnpass_step_macro(OPJ_TRUE, OPJ_FALSE, \
+                                    flags, flagsp, flags_stride, data, \
+                                    l_w, 2, mqc, curctx, \
+                                    v, a, c, ct, oneplushalf, OPJ_FALSE); \
+                opj_t1_dec_clnpass_step_macro(OPJ_TRUE, OPJ_FALSE, \
+                                    flags, flagsp, flags_stride, data, \
+                                    l_w, 3, mqc, curctx, \
+                                    v, a, c, ct, oneplushalf, OPJ_FALSE); \
+            } \
+            *flagsp = flags & ~(T1_PI_0 | T1_PI_1 | T1_PI_2 | T1_PI_3); \
+        } \
+    } \
+    UPLOAD_MQC_VARIABLES(mqc, curctx, a, c, ct); \
+    if( k < h ) { \
+        for (i = 0; i < l_w; ++i, ++flagsp, ++data) { \
+            for (j = 0; j < h - k; ++j) { \
+                opj_t1_dec_clnpass_step(t1, flagsp, data + j * l_w, oneplushalf, j, vsc); \
+            } \
+            *flagsp &= ~(T1_PI_0 | T1_PI_1 | T1_PI_2 | T1_PI_3); \
+        } \
+    } \
+}
+
+static void opj_t1_dec_clnpass_check_segsym(opj_t1_t *t1, OPJ_INT32 cblksty)
+{
+    if (cblksty & J2K_CCP_CBLKSTY_SEGSYM) {
+        opj_mqc_t* mqc = &(t1->mqc);
+        OPJ_UINT32 v, v2;
+        opj_mqc_setcurctx(mqc, T1_CTXNO_UNI);
+        opj_mqc_decode(v, mqc);
+        opj_mqc_decode(v2, mqc);
+        v = (v << 1) | v2;
+        opj_mqc_decode(v2, mqc);
+        v = (v << 1) | v2;
+        opj_mqc_decode(v2, mqc);
+        v = (v << 1) | v2;
+        /*
+        if (v!=0xa) {
+            opj_event_msg(t1->cinfo, EVT_WARNING, "Bad segmentation symbol %x\n", v);
+        }
+        */
+    }
+}
+
+static void opj_t1_dec_clnpass_64x64_novsc(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno)
+{
+    opj_t1_dec_clnpass_internal(t1, bpno, OPJ_FALSE, 64, 64, 66);
+}
+
+static void opj_t1_dec_clnpass_64x64_vsc(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno)
+{
+    opj_t1_dec_clnpass_internal(t1, bpno, OPJ_TRUE, 64, 64, 66);
+}
+
+static void opj_t1_dec_clnpass_generic_novsc(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno)
+{
+    opj_t1_dec_clnpass_internal(t1, bpno, OPJ_FALSE, t1->w, t1->h,
+                                t1->w + 2U);
+}
+
+static void opj_t1_dec_clnpass_generic_vsc(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno)
+{
+    opj_t1_dec_clnpass_internal(t1, bpno, OPJ_TRUE, t1->w, t1->h,
+                                t1->w + 2U);
+}
+
+static void opj_t1_dec_clnpass(
+    opj_t1_t *t1,
+    OPJ_INT32 bpno,
+    OPJ_INT32 cblksty)
+{
+    if (t1->w == 64 && t1->h == 64) {
+        if (cblksty & J2K_CCP_CBLKSTY_VSC) {
+            opj_t1_dec_clnpass_64x64_vsc(t1, bpno);
+        } else {
+            opj_t1_dec_clnpass_64x64_novsc(t1, bpno);
+        }
+    } else {
+        if (cblksty & J2K_CCP_CBLKSTY_VSC) {
+            opj_t1_dec_clnpass_generic_vsc(t1, bpno);
+        } else {
+            opj_t1_dec_clnpass_generic_novsc(t1, bpno);
+        }
+    }
+    opj_t1_dec_clnpass_check_segsym(t1, cblksty);
+}
+
+
+/** mod fixed_quality */
+static OPJ_FLOAT64 opj_t1_getwmsedec(
+    OPJ_INT32 nmsedec,
+    OPJ_UINT32 compno,
+    OPJ_UINT32 level,
+    OPJ_UINT32 orient,
+    OPJ_INT32 bpno,
+    OPJ_UINT32 qmfbid,
+    OPJ_FLOAT64 stepsize,
+    OPJ_UINT32 numcomps,
+    const OPJ_FLOAT64 * mct_norms,
+    OPJ_UINT32 mct_numcomps)
+{
+    OPJ_FLOAT64 w1 = 1, w2, wmsedec;
+    OPJ_ARG_NOT_USED(numcomps);
+
+    if (mct_norms && (compno < mct_numcomps)) {
+        w1 = mct_norms[compno];
+    }
+
+    if (qmfbid == 1) {
+        w2 = opj_dwt_getnorm(level, orient);
+    } else {    /* if (qmfbid == 0) */
+        const OPJ_INT32 log2_gain = (orient == 0) ? 0 :
+                                    (orient == 3) ? 2 : 1;
+        w2 = opj_dwt_getnorm_real(level, orient);
+        /* Not sure this is right. But preserves past behaviour */
+        stepsize /= (1 << log2_gain);
+    }
+
+    wmsedec = w1 * w2 * stepsize * (1 << bpno);
+    wmsedec *= wmsedec * nmsedec / 8192.0;
+
+    return wmsedec;
+}
+
+static OPJ_BOOL opj_t1_allocate_buffers(
+    opj_t1_t *t1,
+    OPJ_UINT32 w,
+    OPJ_UINT32 h)
+{
+    OPJ_UINT32 flagssize;
+    OPJ_UINT32 flags_stride;
+
+    /* No risk of overflow. Prior checks ensure those assert are met */
+    /* They are per the specification */
+    assert(w <= 1024);
+    assert(h <= 1024);
+    assert(w * h <= 4096);
+
+    /* encoder uses tile buffer, so no need to allocate */
+    {
+        OPJ_UINT32 datasize = w * h;
+
+        if (datasize > t1->datasize) {
+            opj_aligned_free(t1->data);
+            t1->data = (OPJ_INT32*) opj_aligned_malloc(datasize * sizeof(OPJ_INT32));
+            if (!t1->data) {
+                /* FIXME event manager error callback */
+                return OPJ_FALSE;
+            }
+            t1->datasize = datasize;
+        }
+        /* memset first arg is declared to never be null by gcc */
+        if (t1->data != NULL) {
+            memset(t1->data, 0, datasize * sizeof(OPJ_INT32));
+        }
+    }
+
+    flags_stride = w + 2U; /* can't be 0U */
+
+    flagssize = (h + 3U) / 4U + 2U;
+
+    flagssize *= flags_stride;
+    {
+        opj_flag_t* p;
+        OPJ_UINT32 x;
+        OPJ_UINT32 flags_height = (h + 3U) / 4U;
+
+        if (flagssize > t1->flagssize) {
+
+            opj_aligned_free(t1->flags);
+            t1->flags = (opj_flag_t*) opj_aligned_malloc(flagssize * sizeof(
+                            opj_flag_t));
+            if (!t1->flags) {
+                /* FIXME event manager error callback */
+                return OPJ_FALSE;
+            }
+        }
+        t1->flagssize = flagssize;
+
+        memset(t1->flags, 0, flagssize * sizeof(opj_flag_t));
+
+        p = &t1->flags[0];
+        for (x = 0; x < flags_stride; ++x) {
+            /* magic value to hopefully stop any passes being interested in this entry */
+            *p++ = (T1_PI_0 | T1_PI_1 | T1_PI_2 | T1_PI_3);
+        }
+
+        p = &t1->flags[((flags_height + 1) * flags_stride)];
+        for (x = 0; x < flags_stride; ++x) {
+            /* magic value to hopefully stop any passes being interested in this entry */
+            *p++ = (T1_PI_0 | T1_PI_1 | T1_PI_2 | T1_PI_3);
+        }
+
+        if (h % 4) {
+            OPJ_UINT32 v = 0;
+            p = &t1->flags[((flags_height) * flags_stride)];
+            if (h % 4 == 1) {
+                v |= T1_PI_1 | T1_PI_2 | T1_PI_3;
+            } else if (h % 4 == 2) {
+                v |= T1_PI_2 | T1_PI_3;
+            } else if (h % 4 == 3) {
+                v |= T1_PI_3;
+            }
+            for (x = 0; x < flags_stride; ++x) {
+                *p++ = v;
+            }
+        }
+    }
+
+    t1->w = w;
+    t1->h = h;
+
+    return OPJ_TRUE;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------- */
+/**
+ * Creates a new Tier 1 handle
+ * and initializes the look-up tables of the Tier-1 coder/decoder
+ * @return a new T1 handle if successful, returns NULL otherwise
+*/
+opj_t1_t* opj_t1_create(OPJ_BOOL isEncoder)
+{
+    opj_t1_t *l_t1 = 00;
+
+    l_t1 = (opj_t1_t*) opj_calloc(1, sizeof(opj_t1_t));
+    if (!l_t1) {
+        return 00;
+    }
+
+    l_t1->encoder = isEncoder;
+
+    return l_t1;
+}
+
+
+/**
+ * Destroys a previously created T1 handle
+ *
+ * @param p_t1 Tier 1 handle to destroy
+*/
+void opj_t1_destroy(opj_t1_t *p_t1)
+{
+    if (! p_t1) {
+        return;
+    }
+
+    if (p_t1->data) {
+        opj_aligned_free(p_t1->data);
+        p_t1->data = 00;
+    }
+
+    if (p_t1->flags) {
+        opj_aligned_free(p_t1->flags);
+        p_t1->flags = 00;
+    }
+
+    opj_free(p_t1->cblkdatabuffer);
+
+    opj_free(p_t1);
+}
+
+typedef struct {
+    OPJ_BOOL whole_tile_decoding;
+    OPJ_UINT32 resno;
+    opj_tcd_cblk_dec_t* cblk;
+    opj_tcd_band_t* band;
+    opj_tcd_tilecomp_t* tilec;
+    opj_tccp_t* tccp;
+    OPJ_BOOL mustuse_cblkdatabuffer;
+    volatile OPJ_BOOL* pret;
+    opj_event_mgr_t *p_manager;
+    opj_mutex_t* p_manager_mutex;
+    OPJ_BOOL check_pterm;
+} opj_t1_cblk_decode_processing_job_t;
+
+static void opj_t1_destroy_wrapper(void* t1)
+{
+    opj_t1_destroy((opj_t1_t*) t1);
+}
+
+static void opj_t1_clbl_decode_processor(void* user_data, opj_tls_t* tls)
+{
+    opj_tcd_cblk_dec_t* cblk;
+    opj_tcd_band_t* band;
+    opj_tcd_tilecomp_t* tilec;
+    opj_tccp_t* tccp;
+    OPJ_INT32* OPJ_RESTRICT datap;
+    OPJ_UINT32 cblk_w, cblk_h;
+    OPJ_INT32 x, y;
+    OPJ_UINT32 i, j;
+    opj_t1_cblk_decode_processing_job_t* job;
+    opj_t1_t* t1;
+    OPJ_UINT32 resno;
+    OPJ_UINT32 tile_w;
+
+    job = (opj_t1_cblk_decode_processing_job_t*) user_data;
+
+    cblk = job->cblk;
+
+    if (!job->whole_tile_decoding) {
+        cblk_w = (OPJ_UINT32)(cblk->x1 - cblk->x0);
+        cblk_h = (OPJ_UINT32)(cblk->y1 - cblk->y0);
+
+        cblk->decoded_data = (OPJ_INT32*)opj_aligned_malloc(sizeof(OPJ_INT32) *
+                             cblk_w * cblk_h);
+        if (cblk->decoded_data == NULL) {
+            if (job->p_manager_mutex) {
+                opj_mutex_lock(job->p_manager_mutex);
+            }
+            opj_event_msg(job->p_manager, EVT_ERROR,
+                          "Cannot allocate cblk->decoded_data\n");
+            if (job->p_manager_mutex) {
+                opj_mutex_unlock(job->p_manager_mutex);
+            }
+            *(job->pret) = OPJ_FALSE;
+            opj_free(job);
+            return;
+        }
+        /* Zero-init required */
+        memset(cblk->decoded_data, 0, sizeof(OPJ_INT32) * cblk_w * cblk_h);
+    } else if (cblk->decoded_data) {
+        /* Not sure if that code path can happen, but better be */
+        /* safe than sorry */
+        opj_aligned_free(cblk->decoded_data);
+        cblk->decoded_data = NULL;
+    }
+
+    resno = job->resno;
+    band = job->band;
+    tilec = job->tilec;
+    tccp = job->tccp;
+    tile_w = (OPJ_UINT32)(tilec->resolutions[tilec->minimum_num_resolutions - 1].x1
+                          -
+                          tilec->resolutions[tilec->minimum_num_resolutions - 1].x0);
+
+    if (!*(job->pret)) {
+        opj_free(job);
+        return;
+    }
+
+    t1 = (opj_t1_t*) opj_tls_get(tls, OPJ_TLS_KEY_T1);
+    if (t1 == NULL) {
+        t1 = opj_t1_create(OPJ_FALSE);
+        if (t1 == NULL) {
+            opj_event_msg(job->p_manager, EVT_ERROR,
+                          "Cannot allocate Tier 1 handle\n");
+            *(job->pret) = OPJ_FALSE;
+            opj_free(job);
+            return;
+        }
+        if (!opj_tls_set(tls, OPJ_TLS_KEY_T1, t1, opj_t1_destroy_wrapper)) {
+            opj_event_msg(job->p_manager, EVT_ERROR,
+                          "Unable to set t1 handle as TLS\n");
+            opj_t1_destroy(t1);
+            *(job->pret) = OPJ_FALSE;
+            opj_free(job);
+            return;
+        }
+    }
+    t1->mustuse_cblkdatabuffer = job->mustuse_cblkdatabuffer;
+
+    if ((tccp->cblksty & J2K_CCP_CBLKSTY_HT) != 0) {
+        if (OPJ_FALSE == opj_t1_ht_decode_cblk(
+                    t1,
+                    cblk,
+                    band->bandno,
+                    (OPJ_UINT32)tccp->roishift,
+                    tccp->cblksty,
+                    job->p_manager,
+                    job->p_manager_mutex,
+                    job->check_pterm)) {
+            *(job->pret) = OPJ_FALSE;
+            opj_free(job);
+            return;
+        }
+    } else {
+        if (OPJ_FALSE == opj_t1_decode_cblk(
+                    t1,
+                    cblk,
+                    band->bandno,
+                    (OPJ_UINT32)tccp->roishift,
+                    tccp->cblksty,
+                    job->p_manager,
+                    job->p_manager_mutex,
+                    job->check_pterm)) {
+            *(job->pret) = OPJ_FALSE;
+            opj_free(job);
+            return;
+        }
+    }
+
+    x = cblk->x0 - band->x0;
+    y = cblk->y0 - band->y0;
+    if (band->bandno & 1) {
+        opj_tcd_resolution_t* pres = &tilec->resolutions[resno - 1];
+        x += pres->x1 - pres->x0;
+    }
+    if (band->bandno & 2) {
+        opj_tcd_resolution_t* pres = &tilec->resolutions[resno - 1];
+        y += pres->y1 - pres->y0;
+    }
+
+    datap = cblk->decoded_data ? cblk->decoded_data : t1->data;
+    cblk_w = t1->w;
+    cblk_h = t1->h;
+
+    if (tccp->roishift) {
+        if (tccp->roishift >= 31) {
+            for (j = 0; j < cblk_h; ++j) {
+                for (i = 0; i < cblk_w; ++i) {
+                    datap[(j * cblk_w) + i] = 0;
+                }
+            }
+        } else {
+            OPJ_INT32 thresh = 1 << tccp->roishift;
+            for (j = 0; j < cblk_h; ++j) {
+                for (i = 0; i < cblk_w; ++i) {
+                    OPJ_INT32 val = datap[(j * cblk_w) + i];
+                    OPJ_INT32 mag = abs(val);
+                    if (mag >= thresh) {
+                        mag >>= tccp->roishift;
+                        datap[(j * cblk_w) + i] = val < 0 ? -mag : mag;
+                    }
+                }
+            }
+        }
+    }
+
+    /* Both can be non NULL if for example decoding a full tile and then */
+    /* partially a tile. In which case partial decoding should be the */
+    /* priority */
+    assert((cblk->decoded_data != NULL) || (tilec->data != NULL));
+
+    if (cblk->decoded_data) {
+        OPJ_UINT32 cblk_size = cblk_w * cblk_h;
+        if (tccp->qmfbid == 1) {
+            for (i = 0; i < cblk_size; ++i) {
+                datap[i] /= 2;
+            }
+        } else {        /* if (tccp->qmfbid == 0) */
+            const float stepsize = 0.5f * band->stepsize;
+            i = 0;
+#ifdef __SSE2__
+            {
+                const __m128 xmm_stepsize = _mm_set1_ps(stepsize);
+                for (; i < (cblk_size & ~15U); i += 16) {
+                    __m128 xmm0_data = _mm_cvtepi32_ps(_mm_load_si128((__m128i * const)(
+                                                           datap + 0)));
+                    __m128 xmm1_data = _mm_cvtepi32_ps(_mm_load_si128((__m128i * const)(
+                                                           datap + 4)));
+                    __m128 xmm2_data = _mm_cvtepi32_ps(_mm_load_si128((__m128i * const)(
+                                                           datap + 8)));
+                    __m128 xmm3_data = _mm_cvtepi32_ps(_mm_load_si128((__m128i * const)(
+                                                           datap + 12)));
+                    _mm_store_ps((float*)(datap +  0), _mm_mul_ps(xmm0_data, xmm_stepsize));
+                    _mm_store_ps((float*)(datap +  4), _mm_mul_ps(xmm1_data, xmm_stepsize));
+                    _mm_store_ps((float*)(datap +  8), _mm_mul_ps(xmm2_data, xmm_stepsize));
+                    _mm_store_ps((float*)(datap + 12), _mm_mul_ps(xmm3_data, xmm_stepsize));
+                    datap += 16;
+                }
+            }
+#endif
+            for (; i < cblk_size; ++i) {
+                OPJ_FLOAT32 tmp = ((OPJ_FLOAT32)(*datap)) * stepsize;
+                memcpy(datap, &tmp, sizeof(tmp));
+                datap++;
+            }
+        }
+    } else if (tccp->qmfbid == 1) {
+        OPJ_INT32* OPJ_RESTRICT tiledp = &tilec->data[(OPJ_SIZE_T)y * tile_w +
+                                                       (OPJ_SIZE_T)x];
+        for (j = 0; j < cblk_h; ++j) {
+            i = 0;
+            for (; i < (cblk_w & ~(OPJ_UINT32)3U); i += 4U) {
+                OPJ_INT32 tmp0 = datap[(j * cblk_w) + i + 0U];
+                OPJ_INT32 tmp1 = datap[(j * cblk_w) + i + 1U];
+                OPJ_INT32 tmp2 = datap[(j * cblk_w) + i + 2U];
+                OPJ_INT32 tmp3 = datap[(j * cblk_w) + i + 3U];
+                ((OPJ_INT32*)tiledp)[(j * (OPJ_SIZE_T)tile_w) + i + 0U] = tmp0 / 2;
+                ((OPJ_INT32*)tiledp)[(j * (OPJ_SIZE_T)tile_w) + i + 1U] = tmp1 / 2;
+                ((OPJ_INT32*)tiledp)[(j * (OPJ_SIZE_T)tile_w) + i + 2U] = tmp2 / 2;
+                ((OPJ_INT32*)tiledp)[(j * (OPJ_SIZE_T)tile_w) + i + 3U] = tmp3 / 2;
+            }
+            for (; i < cblk_w; ++i) {
+                OPJ_INT32 tmp = datap[(j * cblk_w) + i];
+                ((OPJ_INT32*)tiledp)[(j * (OPJ_SIZE_T)tile_w) + i] = tmp / 2;
+            }
+        }
+    } else {        /* if (tccp->qmfbid == 0) */
+        const float stepsize = 0.5f * band->stepsize;
+        OPJ_FLOAT32* OPJ_RESTRICT tiledp = (OPJ_FLOAT32*) &tilec->data[(OPJ_SIZE_T)y *
+                                                         tile_w + (OPJ_SIZE_T)x];
+        for (j = 0; j < cblk_h; ++j) {
+            OPJ_FLOAT32* OPJ_RESTRICT tiledp2 = tiledp;
+            for (i = 0; i < cblk_w; ++i) {
+                OPJ_FLOAT32 tmp = (OPJ_FLOAT32) * datap * stepsize;
+                *tiledp2 = tmp;
+                datap++;
+                tiledp2++;
+            }
+            tiledp += tile_w;
+        }
+    }
+
+    opj_free(job);
+}
+
+
+void opj_t1_decode_cblks(opj_tcd_t* tcd,
+                         volatile OPJ_BOOL* pret,
+                         opj_tcd_tilecomp_t* tilec,
+                         opj_tccp_t* tccp,
+                         opj_event_mgr_t *p_manager,
+                         opj_mutex_t* p_manager_mutex,
+                         OPJ_BOOL check_pterm
+                        )
+{
+    opj_thread_pool_t* tp = tcd->thread_pool;
+    OPJ_UINT32 resno, bandno, precno, cblkno;
+
+#ifdef DEBUG_VERBOSE
+    OPJ_UINT32 codeblocks_decoded = 0;
+    printf("Enter opj_t1_decode_cblks()\n");
+#endif
+
+    for (resno = 0; resno < tilec->minimum_num_resolutions; ++resno) {
+        opj_tcd_resolution_t* res = &tilec->resolutions[resno];
+
+        for (bandno = 0; bandno < res->numbands; ++bandno) {
+            opj_tcd_band_t* OPJ_RESTRICT band = &res->bands[bandno];
+
+            for (precno = 0; precno < res->pw * res->ph; ++precno) {
+                opj_tcd_precinct_t* precinct = &band->precincts[precno];
+
+                if (!opj_tcd_is_subband_area_of_interest(tcd,
+                        tilec->compno,
+                        resno,
+                        band->bandno,
+                        (OPJ_UINT32)precinct->x0,
+                        (OPJ_UINT32)precinct->y0,
+                        (OPJ_UINT32)precinct->x1,
+                        (OPJ_UINT32)precinct->y1)) {
+                    for (cblkno = 0; cblkno < precinct->cw * precinct->ch; ++cblkno) {
+                        opj_tcd_cblk_dec_t* cblk = &precinct->cblks.dec[cblkno];
+                        if (cblk->decoded_data) {
+#ifdef DEBUG_VERBOSE
+                            printf("Discarding codeblock %d,%d at resno=%d, bandno=%d\n",
+                                   cblk->x0, cblk->y0, resno, bandno);
+#endif
+                            opj_aligned_free(cblk->decoded_data);
+                            cblk->decoded_data = NULL;
+                        }
+                    }
+                    continue;
+                }
+
+                for (cblkno = 0; cblkno < precinct->cw * precinct->ch; ++cblkno) {
+                    opj_tcd_cblk_dec_t* cblk = &precinct->cblks.dec[cblkno];
+                    opj_t1_cblk_decode_processing_job_t* job;
+
+                    if (!opj_tcd_is_subband_area_of_interest(tcd,
+                            tilec->compno,
+                            resno,
+                            band->bandno,
+                            (OPJ_UINT32)cblk->x0,
+                            (OPJ_UINT32)cblk->y0,
+                            (OPJ_UINT32)cblk->x1,
+                            (OPJ_UINT32)cblk->y1)) {
+                        if (cblk->decoded_data) {
+#ifdef DEBUG_VERBOSE
+                            printf("Discarding codeblock %d,%d at resno=%d, bandno=%d\n",
+                                   cblk->x0, cblk->y0, resno, bandno);
+#endif
+                            opj_aligned_free(cblk->decoded_data);
+                            cblk->decoded_data = NULL;
+                        }
+                        continue;
+                    }
+
+                    if (!tcd->whole_tile_decoding) {
+                        OPJ_UINT32 cblk_w = (OPJ_UINT32)(cblk->x1 - cblk->x0);
+                        OPJ_UINT32 cblk_h = (OPJ_UINT32)(cblk->y1 - cblk->y0);
+                        if (cblk->decoded_data != NULL) {
+#ifdef DEBUG_VERBOSE
+                            printf("Reusing codeblock %d,%d at resno=%d, bandno=%d\n",
+                                   cblk->x0, cblk->y0, resno, bandno);
+#endif
+                            continue;
+                        }
+                        if (cblk_w == 0 || cblk_h == 0) {
+                            continue;
+                        }
+#ifdef DEBUG_VERBOSE
+                        printf("Decoding codeblock %d,%d at resno=%d, bandno=%d\n",
+                               cblk->x0, cblk->y0, resno, bandno);
+#endif
+                    }
+
+                    job = (opj_t1_cblk_decode_processing_job_t*) opj_calloc(1,
+                            sizeof(opj_t1_cblk_decode_processing_job_t));
+                    if (!job) {
+                        *pret = OPJ_FALSE;
+                        return;
+                    }
+                    job->whole_tile_decoding = tcd->whole_tile_decoding;
+                    job->resno = resno;
+                    job->cblk = cblk;
+                    job->band = band;
+                    job->tilec = tilec;
+                    job->tccp = tccp;
+                    job->pret = pret;
+                    job->p_manager_mutex = p_manager_mutex;
+                    job->p_manager = p_manager;
+                    job->check_pterm = check_pterm;
+                    job->mustuse_cblkdatabuffer = opj_thread_pool_get_thread_count(tp) > 1;
+                    opj_thread_pool_submit_job(tp, opj_t1_clbl_decode_processor, job);
+#ifdef DEBUG_VERBOSE
+                    codeblocks_decoded ++;
+#endif
+                    if (!(*pret)) {
+                        return;
+                    }
+                } /* cblkno */
+            } /* precno */
+        } /* bandno */
+    } /* resno */
+
+#ifdef DEBUG_VERBOSE
+    printf("Leave opj_t1_decode_cblks(). Number decoded: %d\n", codeblocks_decoded);
+#endif
+    return;
+}
+
+
+static OPJ_BOOL opj_t1_decode_cblk(opj_t1_t *t1,
+                                   opj_tcd_cblk_dec_t* cblk,
+                                   OPJ_UINT32 orient,
+                                   OPJ_UINT32 roishift,
+                                   OPJ_UINT32 cblksty,
+                                   opj_event_mgr_t *p_manager,
+                                   opj_mutex_t* p_manager_mutex,
+                                   OPJ_BOOL check_pterm)
+{
+    opj_mqc_t *mqc = &(t1->mqc);   /* MQC component */
+
+    OPJ_INT32 bpno_plus_one;
+    OPJ_UINT32 passtype;
+    OPJ_UINT32 segno, passno;
+    OPJ_BYTE* cblkdata = NULL;
+    OPJ_UINT32 cblkdataindex = 0;
+    OPJ_BYTE type = T1_TYPE_MQ; /* BYPASS mode */
+    OPJ_INT32* original_t1_data = NULL;
+
+    mqc->lut_ctxno_zc_orient = lut_ctxno_zc + (orient << 9);
+
+    if (!opj_t1_allocate_buffers(
+                t1,
+                (OPJ_UINT32)(cblk->x1 - cblk->x0),
+                (OPJ_UINT32)(cblk->y1 - cblk->y0))) {
+        return OPJ_FALSE;
+    }
+
+    bpno_plus_one = (OPJ_INT32)(roishift + cblk->numbps);
+    if (bpno_plus_one >= 31) {
+        if (p_manager_mutex) {
+            opj_mutex_lock(p_manager_mutex);
+        }
+        opj_event_msg(p_manager, EVT_WARNING,
+                      "opj_t1_decode_cblk(): unsupported bpno_plus_one = %d >= 31\n",
+                      bpno_plus_one);
+        if (p_manager_mutex) {
+            opj_mutex_unlock(p_manager_mutex);
+        }
+        return OPJ_FALSE;
+    }
+    passtype = 2;
+
+    opj_mqc_resetstates(mqc);
+    opj_mqc_setstate(mqc, T1_CTXNO_UNI, 0, 46);
+    opj_mqc_setstate(mqc, T1_CTXNO_AGG, 0, 3);
+    opj_mqc_setstate(mqc, T1_CTXNO_ZC, 0, 4);
+
+    /* Even if we have a single chunk, in multi-threaded decoding */
+    /* the insertion of our synthetic marker might potentially override */
+    /* valid codestream of other codeblocks decoded in parallel. */
+    if (cblk->numchunks > 1 || t1->mustuse_cblkdatabuffer) {
+        OPJ_UINT32 i;
+        OPJ_UINT32 cblk_len;
+
+        /* Compute whole codeblock length from chunk lengths */
+        cblk_len = 0;
+        for (i = 0; i < cblk->numchunks; i++) {
+            cblk_len += cblk->chunks[i].len;
+        }
+
+        /* Allocate temporary memory if needed */
+        if (cblk_len + OPJ_COMMON_CBLK_DATA_EXTRA > t1->cblkdatabuffersize) {
+            cblkdata = (OPJ_BYTE*)opj_realloc(t1->cblkdatabuffer,
+                                              cblk_len + OPJ_COMMON_CBLK_DATA_EXTRA);
+            if (cblkdata == NULL) {
+                return OPJ_FALSE;
+            }
+            t1->cblkdatabuffer = cblkdata;
+            memset(t1->cblkdatabuffer + cblk_len, 0, OPJ_COMMON_CBLK_DATA_EXTRA);
+            t1->cblkdatabuffersize = cblk_len + OPJ_COMMON_CBLK_DATA_EXTRA;
+        }
+
+        /* Concatenate all chunks */
+        cblkdata = t1->cblkdatabuffer;
+        cblk_len = 0;
+        for (i = 0; i < cblk->numchunks; i++) {
+            memcpy(cblkdata + cblk_len, cblk->chunks[i].data, cblk->chunks[i].len);
+            cblk_len += cblk->chunks[i].len;
+        }
+    } else if (cblk->numchunks == 1) {
+        cblkdata = cblk->chunks[0].data;
+    } else {
+        /* Not sure if that can happen in practice, but avoid Coverity to */
+        /* think we will dereference a null cblkdta pointer */
+        return OPJ_TRUE;
+    }
+
+    /* For subtile decoding, directly decode in the decoded_data buffer of */
+    /* the code-block. Hack t1->data to point to it, and restore it later */
+    if (cblk->decoded_data) {
+        original_t1_data = t1->data;
+        t1->data = cblk->decoded_data;
+    }
+
+    for (segno = 0; segno < cblk->real_num_segs; ++segno) {
+        opj_tcd_seg_t *seg = &cblk->segs[segno];
+
+        /* BYPASS mode */
+        type = ((bpno_plus_one <= ((OPJ_INT32)(cblk->numbps)) - 4) && (passtype < 2) &&
+                (cblksty & J2K_CCP_CBLKSTY_LAZY)) ? T1_TYPE_RAW : T1_TYPE_MQ;
+
+        if (type == T1_TYPE_RAW) {
+            opj_mqc_raw_init_dec(mqc, cblkdata + cblkdataindex, seg->len,
+                                 OPJ_COMMON_CBLK_DATA_EXTRA);
+        } else {
+            opj_mqc_init_dec(mqc, cblkdata + cblkdataindex, seg->len,
+                             OPJ_COMMON_CBLK_DATA_EXTRA);
+        }
+        cblkdataindex += seg->len;
+
+        for (passno = 0; (passno < seg->real_num_passes) &&
+                (bpno_plus_one >= 1); ++passno) {
+            switch (passtype) {
+            case 0:
+                if (type == T1_TYPE_RAW) {
+                    opj_t1_dec_sigpass_raw(t1, bpno_plus_one, (OPJ_INT32)cblksty);
+                } else {
+                    opj_t1_dec_sigpass_mqc(t1, bpno_plus_one, (OPJ_INT32)cblksty);
+                }
+                break;
+            case 1:
+                if (type == T1_TYPE_RAW) {
+                    opj_t1_dec_refpass_raw(t1, bpno_plus_one);
+                } else {
+                    opj_t1_dec_refpass_mqc(t1, bpno_plus_one);
+                }
+                break;
+            case 2:
+                opj_t1_dec_clnpass(t1, bpno_plus_one, (OPJ_INT32)cblksty);
+                break;
+            }
+
+            if ((cblksty & J2K_CCP_CBLKSTY_RESET) && type == T1_TYPE_MQ) {
+                opj_mqc_resetstates(mqc);
+                opj_mqc_setstate(mqc, T1_CTXNO_UNI, 0, 46);
+                opj_mqc_setstate(mqc, T1_CTXNO_AGG, 0, 3);
+                opj_mqc_setstate(mqc, T1_CTXNO_ZC, 0, 4);
+            }
+            if (++passtype == 3) {
+                passtype = 0;
+                bpno_plus_one--;
+            }
+        }
+
+        opq_mqc_finish_dec(mqc);
+    }
+
+    if (check_pterm) {
+        if (mqc->bp + 2 < mqc->end) {
+            if (p_manager_mutex) {
+                opj_mutex_lock(p_manager_mutex);
+            }
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "PTERM check failure: %d remaining bytes in code block (%d used / %d)\n",
+                          (int)(mqc->end - mqc->bp) - 2,
+                          (int)(mqc->bp - mqc->start),
+                          (int)(mqc->end - mqc->start));
+            if (p_manager_mutex) {
+                opj_mutex_unlock(p_manager_mutex);
+            }
+        } else if (mqc->end_of_byte_stream_counter > 2) {
+            if (p_manager_mutex) {
+                opj_mutex_lock(p_manager_mutex);
+            }
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "PTERM check failure: %d synthetized 0xFF markers read\n",
+                          mqc->end_of_byte_stream_counter);
+            if (p_manager_mutex) {
+                opj_mutex_unlock(p_manager_mutex);
+            }
+        }
+    }
+
+    /* Restore original t1->data is needed */
+    if (cblk->decoded_data) {
+        t1->data = original_t1_data;
+    }
+
+    return OPJ_TRUE;
+}
+
+
+typedef struct {
+    OPJ_UINT32 compno;
+    OPJ_UINT32 resno;
+    opj_tcd_cblk_enc_t* cblk;
+    opj_tcd_tile_t *tile;
+    opj_tcd_band_t* band;
+    opj_tcd_tilecomp_t* tilec;
+    opj_tccp_t* tccp;
+    const OPJ_FLOAT64 * mct_norms;
+    OPJ_UINT32 mct_numcomps;
+    volatile OPJ_BOOL* pret;
+    opj_mutex_t* mutex;
+} opj_t1_cblk_encode_processing_job_t;
+
+/** Procedure to deal with a asynchronous code-block encoding job.
+ *
+ * @param user_data Pointer to a opj_t1_cblk_encode_processing_job_t* structure
+ * @param tls       TLS handle.
+ */
+static void opj_t1_cblk_encode_processor(void* user_data, opj_tls_t* tls)
+{
+    opj_t1_cblk_encode_processing_job_t* job =
+        (opj_t1_cblk_encode_processing_job_t*)user_data;
+    opj_tcd_cblk_enc_t* cblk = job->cblk;
+    const opj_tcd_band_t* band = job->band;
+    const opj_tcd_tilecomp_t* tilec = job->tilec;
+    const opj_tccp_t* tccp = job->tccp;
+    const OPJ_UINT32 resno = job->resno;
+    opj_t1_t* t1;
+    const OPJ_UINT32 tile_w = (OPJ_UINT32)(tilec->x1 - tilec->x0);
+
+    OPJ_INT32* OPJ_RESTRICT tiledp;
+    OPJ_UINT32 cblk_w;
+    OPJ_UINT32 cblk_h;
+    OPJ_UINT32 i, j;
+
+    OPJ_INT32 x = cblk->x0 - band->x0;
+    OPJ_INT32 y = cblk->y0 - band->y0;
+
+    if (!*(job->pret)) {
+        opj_free(job);
+        return;
+    }
+
+    t1 = (opj_t1_t*) opj_tls_get(tls, OPJ_TLS_KEY_T1);
+    if (t1 == NULL) {
+        t1 = opj_t1_create(OPJ_TRUE); /* OPJ_TRUE == T1 for encoding */
+        opj_tls_set(tls, OPJ_TLS_KEY_T1, t1, opj_t1_destroy_wrapper);
+    }
+
+    if (band->bandno & 1) {
+        opj_tcd_resolution_t *pres = &tilec->resolutions[resno - 1];
+        x += pres->x1 - pres->x0;
+    }
+    if (band->bandno & 2) {
+        opj_tcd_resolution_t *pres = &tilec->resolutions[resno - 1];
+        y += pres->y1 - pres->y0;
+    }
+
+    if (!opj_t1_allocate_buffers(
+                t1,
+                (OPJ_UINT32)(cblk->x1 - cblk->x0),
+                (OPJ_UINT32)(cblk->y1 - cblk->y0))) {
+        *(job->pret) = OPJ_FALSE;
+        opj_free(job);
+        return;
+    }
+
+    cblk_w = t1->w;
+    cblk_h = t1->h;
+
+    tiledp = &tilec->data[(OPJ_SIZE_T)y * tile_w + (OPJ_SIZE_T)x];
+
+    if (tccp->qmfbid == 1) {
+        /* Do multiplication on unsigned type, even if the
+            * underlying type is signed, to avoid potential
+            * int overflow on large value (the output will be
+            * incorrect in such situation, but whatever...)
+            * This assumes complement-to-2 signed integer
+            * representation
+            * Fixes https://github.com/uclouvain/openjpeg/issues/1053
+            */
+        OPJ_UINT32* OPJ_RESTRICT tiledp_u = (OPJ_UINT32*) tiledp;
+        OPJ_UINT32* OPJ_RESTRICT t1data = (OPJ_UINT32*) t1->data;
+        /* Change from "natural" order to "zigzag" order of T1 passes */
+        for (j = 0; j < (cblk_h & ~3U); j += 4) {
+            for (i = 0; i < cblk_w; ++i) {
+                t1data[0] = tiledp_u[(j + 0) * tile_w + i] << T1_NMSEDEC_FRACBITS;
+                t1data[1] = tiledp_u[(j + 1) * tile_w + i] << T1_NMSEDEC_FRACBITS;
+                t1data[2] = tiledp_u[(j + 2) * tile_w + i] << T1_NMSEDEC_FRACBITS;
+                t1data[3] = tiledp_u[(j + 3) * tile_w + i] << T1_NMSEDEC_FRACBITS;
+                t1data += 4;
+            }
+        }
+        if (j < cblk_h) {
+            for (i = 0; i < cblk_w; ++i) {
+                OPJ_UINT32 k;
+                for (k = j; k < cblk_h; k++) {
+                    t1data[0] = tiledp_u[k * tile_w + i] << T1_NMSEDEC_FRACBITS;
+                    t1data ++;
+                }
+            }
+        }
+    } else {        /* if (tccp->qmfbid == 0) */
+        OPJ_FLOAT32* OPJ_RESTRICT tiledp_f = (OPJ_FLOAT32*) tiledp;
+        OPJ_INT32* OPJ_RESTRICT t1data = t1->data;
+        /* Change from "natural" order to "zigzag" order of T1 passes */
+        for (j = 0; j < (cblk_h & ~3U); j += 4) {
+            for (i = 0; i < cblk_w; ++i) {
+                t1data[0] = (OPJ_INT32)opj_lrintf((tiledp_f[(j + 0) * tile_w + i] /
+                                                   band->stepsize) * (1 << T1_NMSEDEC_FRACBITS));
+                t1data[1] = (OPJ_INT32)opj_lrintf((tiledp_f[(j + 1) * tile_w + i] /
+                                                   band->stepsize) * (1 << T1_NMSEDEC_FRACBITS));
+                t1data[2] = (OPJ_INT32)opj_lrintf((tiledp_f[(j + 2) * tile_w + i] /
+                                                   band->stepsize) * (1 << T1_NMSEDEC_FRACBITS));
+                t1data[3] = (OPJ_INT32)opj_lrintf((tiledp_f[(j + 3) * tile_w + i] /
+                                                   band->stepsize) * (1 << T1_NMSEDEC_FRACBITS));
+                t1data += 4;
+            }
+        }
+        if (j < cblk_h) {
+            for (i = 0; i < cblk_w; ++i) {
+                OPJ_UINT32 k;
+                for (k = j; k < cblk_h; k++) {
+                    t1data[0] = (OPJ_INT32)opj_lrintf((tiledp_f[k * tile_w + i] / band->stepsize)
+                                                      * (1 << T1_NMSEDEC_FRACBITS));
+                    t1data ++;
+                }
+            }
+        }
+    }
+
+    {
+        OPJ_FLOAT64 cumwmsedec =
+            opj_t1_encode_cblk(
+                t1,
+                cblk,
+                band->bandno,
+                job->compno,
+                tilec->numresolutions - 1 - resno,
+                tccp->qmfbid,
+                band->stepsize,
+                tccp->cblksty,
+                job->tile->numcomps,
+                job->mct_norms,
+                job->mct_numcomps);
+        if (job->mutex) {
+            opj_mutex_lock(job->mutex);
+        }
+        job->tile->distotile += cumwmsedec;
+        if (job->mutex) {
+            opj_mutex_unlock(job->mutex);
+        }
+    }
+
+    opj_free(job);
+}
+
+
+OPJ_BOOL opj_t1_encode_cblks(opj_tcd_t* tcd,
+                             opj_tcd_tile_t *tile,
+                             opj_tcp_t *tcp,
+                             const OPJ_FLOAT64 * mct_norms,
+                             OPJ_UINT32 mct_numcomps
+                            )
+{
+    volatile OPJ_BOOL ret = OPJ_TRUE;
+    opj_thread_pool_t* tp = tcd->thread_pool;
+    OPJ_UINT32 compno, resno, bandno, precno, cblkno;
+    opj_mutex_t* mutex = opj_mutex_create();
+
+    tile->distotile = 0;        /* fixed_quality */
+
+    for (compno = 0; compno < tile->numcomps; ++compno) {
+        opj_tcd_tilecomp_t* tilec = &tile->comps[compno];
+        opj_tccp_t* tccp = &tcp->tccps[compno];
+
+        for (resno = 0; resno < tilec->numresolutions; ++resno) {
+            opj_tcd_resolution_t *res = &tilec->resolutions[resno];
+
+            for (bandno = 0; bandno < res->numbands; ++bandno) {
+                opj_tcd_band_t* OPJ_RESTRICT band = &res->bands[bandno];
+
+                /* Skip empty bands */
+                if (opj_tcd_is_band_empty(band)) {
+                    continue;
+                }
+                for (precno = 0; precno < res->pw * res->ph; ++precno) {
+                    opj_tcd_precinct_t *prc = &band->precincts[precno];
+
+                    for (cblkno = 0; cblkno < prc->cw * prc->ch; ++cblkno) {
+                        opj_tcd_cblk_enc_t* cblk = &prc->cblks.enc[cblkno];
+
+                        opj_t1_cblk_encode_processing_job_t* job =
+                            (opj_t1_cblk_encode_processing_job_t*) opj_calloc(1,
+                                    sizeof(opj_t1_cblk_encode_processing_job_t));
+                        if (!job) {
+                            ret = OPJ_FALSE;
+                            goto end;
+                        }
+                        job->compno = compno;
+                        job->tile = tile;
+                        job->resno = resno;
+                        job->cblk = cblk;
+                        job->band = band;
+                        job->tilec = tilec;
+                        job->tccp = tccp;
+                        job->mct_norms = mct_norms;
+                        job->mct_numcomps = mct_numcomps;
+                        job->pret = &ret;
+                        job->mutex = mutex;
+                        opj_thread_pool_submit_job(tp, opj_t1_cblk_encode_processor, job);
+
+                    } /* cblkno */
+                } /* precno */
+            } /* bandno */
+        } /* resno  */
+    } /* compno  */
+
+end:
+    opj_thread_pool_wait_completion(tcd->thread_pool, 0);
+    if (mutex) {
+        opj_mutex_destroy(mutex);
+    }
+
+    return ret;
+}
+
+/* Returns whether the pass (bpno, passtype) is terminated */
+static int opj_t1_enc_is_term_pass(opj_tcd_cblk_enc_t* cblk,
+                                   OPJ_UINT32 cblksty,
+                                   OPJ_INT32 bpno,
+                                   OPJ_UINT32 passtype)
+{
+    /* Is it the last cleanup pass ? */
+    if (passtype == 2 && bpno == 0) {
+        return OPJ_TRUE;
+    }
+
+    if (cblksty & J2K_CCP_CBLKSTY_TERMALL) {
+        return OPJ_TRUE;
+    }
+
+    if ((cblksty & J2K_CCP_CBLKSTY_LAZY)) {
+        /* For bypass arithmetic bypass, terminate the 4th cleanup pass */
+        if ((bpno == ((OPJ_INT32)cblk->numbps - 4)) && (passtype == 2)) {
+            return OPJ_TRUE;
+        }
+        /* and beyond terminate all the magnitude refinement passes (in raw) */
+        /* and cleanup passes (in MQC) */
+        if ((bpno < ((OPJ_INT32)(cblk->numbps) - 4)) && (passtype > 0)) {
+            return OPJ_TRUE;
+        }
+    }
+
+    return OPJ_FALSE;
+}
+
+
+/** mod fixed_quality */
+static OPJ_FLOAT64 opj_t1_encode_cblk(opj_t1_t *t1,
+                                      opj_tcd_cblk_enc_t* cblk,
+                                      OPJ_UINT32 orient,
+                                      OPJ_UINT32 compno,
+                                      OPJ_UINT32 level,
+                                      OPJ_UINT32 qmfbid,
+                                      OPJ_FLOAT64 stepsize,
+                                      OPJ_UINT32 cblksty,
+                                      OPJ_UINT32 numcomps,
+                                      const OPJ_FLOAT64 * mct_norms,
+                                      OPJ_UINT32 mct_numcomps)
+{
+    OPJ_FLOAT64 cumwmsedec = 0.0;
+
+    opj_mqc_t *mqc = &(t1->mqc);   /* MQC component */
+
+    OPJ_UINT32 passno;
+    OPJ_INT32 bpno;
+    OPJ_UINT32 passtype;
+    OPJ_INT32 nmsedec = 0;
+    OPJ_INT32 max;
+    OPJ_UINT32 i, j;
+    OPJ_BYTE type = T1_TYPE_MQ;
+    OPJ_FLOAT64 tempwmsedec;
+    OPJ_INT32* datap;
+
+#ifdef EXTRA_DEBUG
+    printf("encode_cblk(x=%d,y=%d,x1=%d,y1=%d,orient=%d,compno=%d,level=%d\n",
+           cblk->x0, cblk->y0, cblk->x1, cblk->y1, orient, compno, level);
+#endif
+
+    mqc->lut_ctxno_zc_orient = lut_ctxno_zc + (orient << 9);
+
+    max = 0;
+    datap = t1->data;
+    for (j = 0; j < t1->h; ++j) {
+        const OPJ_UINT32 w = t1->w;
+        for (i = 0; i < w; ++i, ++datap) {
+            OPJ_INT32 tmp = *datap;
+            if (tmp < 0) {
+                OPJ_UINT32 tmp_unsigned;
+                max = opj_int_max(max, -tmp);
+                tmp_unsigned = opj_to_smr(tmp);
+                memcpy(datap, &tmp_unsigned, sizeof(OPJ_INT32));
+            } else {
+                max = opj_int_max(max, tmp);
+            }
+        }
+    }
+
+    cblk->numbps = max ? (OPJ_UINT32)((opj_int_floorlog2(max) + 1) -
+                                      T1_NMSEDEC_FRACBITS) : 0;
+    if (cblk->numbps == 0) {
+        cblk->totalpasses = 0;
+        return cumwmsedec;
+    }
+
+    bpno = (OPJ_INT32)(cblk->numbps - 1);
+    passtype = 2;
+
+    opj_mqc_resetstates(mqc);
+    opj_mqc_setstate(mqc, T1_CTXNO_UNI, 0, 46);
+    opj_mqc_setstate(mqc, T1_CTXNO_AGG, 0, 3);
+    opj_mqc_setstate(mqc, T1_CTXNO_ZC, 0, 4);
+    opj_mqc_init_enc(mqc, cblk->data);
+
+    for (passno = 0; bpno >= 0; ++passno) {
+        opj_tcd_pass_t *pass = &cblk->passes[passno];
+        type = ((bpno < ((OPJ_INT32)(cblk->numbps) - 4)) && (passtype < 2) &&
+                (cblksty & J2K_CCP_CBLKSTY_LAZY)) ? T1_TYPE_RAW : T1_TYPE_MQ;
+
+        /* If the previous pass was terminating, we need to reset the encoder */
+        if (passno > 0 && cblk->passes[passno - 1].term) {
+            if (type == T1_TYPE_RAW) {
+                opj_mqc_bypass_init_enc(mqc);
+            } else {
+                opj_mqc_restart_init_enc(mqc);
+            }
+        }
+
+        switch (passtype) {
+        case 0:
+            opj_t1_enc_sigpass(t1, bpno, &nmsedec, type, cblksty);
+            break;
+        case 1:
+            opj_t1_enc_refpass(t1, bpno, &nmsedec, type);
+            break;
+        case 2:
+            opj_t1_enc_clnpass(t1, bpno, &nmsedec, cblksty);
+            /* code switch SEGMARK (i.e. SEGSYM) */
+            if (cblksty & J2K_CCP_CBLKSTY_SEGSYM) {
+                opj_mqc_segmark_enc(mqc);
+            }
+            break;
+        }
+
+        /* fixed_quality */
+        tempwmsedec = opj_t1_getwmsedec(nmsedec, compno, level, orient, bpno, qmfbid,
+                                        stepsize, numcomps, mct_norms, mct_numcomps) ;
+        cumwmsedec += tempwmsedec;
+        pass->distortiondec = cumwmsedec;
+
+        if (opj_t1_enc_is_term_pass(cblk, cblksty, bpno, passtype)) {
+            /* If it is a terminated pass, terminate it */
+            if (type == T1_TYPE_RAW) {
+                opj_mqc_bypass_flush_enc(mqc, cblksty & J2K_CCP_CBLKSTY_PTERM);
+            } else {
+                if (cblksty & J2K_CCP_CBLKSTY_PTERM) {
+                    opj_mqc_erterm_enc(mqc);
+                } else {
+                    opj_mqc_flush(mqc);
+                }
+            }
+            pass->term = 1;
+            pass->rate = opj_mqc_numbytes(mqc);
+        } else {
+            /* Non terminated pass */
+            OPJ_UINT32 rate_extra_bytes;
+            if (type == T1_TYPE_RAW) {
+                rate_extra_bytes = opj_mqc_bypass_get_extra_bytes(
+                                       mqc, (cblksty & J2K_CCP_CBLKSTY_PTERM));
+            } else {
+                rate_extra_bytes = 3;
+            }
+            pass->term = 0;
+            pass->rate = opj_mqc_numbytes(mqc) + rate_extra_bytes;
+        }
+
+        if (++passtype == 3) {
+            passtype = 0;
+            bpno--;
+        }
+
+        /* Code-switch "RESET" */
+        if (cblksty & J2K_CCP_CBLKSTY_RESET) {
+            opj_mqc_reset_enc(mqc);
+        }
+    }
+
+    cblk->totalpasses = passno;
+
+    if (cblk->totalpasses) {
+        /* Make sure that pass rates are increasing */
+        OPJ_UINT32 last_pass_rate = opj_mqc_numbytes(mqc);
+        for (passno = cblk->totalpasses; passno > 0;) {
+            opj_tcd_pass_t *pass = &cblk->passes[--passno];
+            if (pass->rate > last_pass_rate) {
+                pass->rate = last_pass_rate;
+            } else {
+                last_pass_rate = pass->rate;
+            }
+        }
+    }
+
+    for (passno = 0; passno < cblk->totalpasses; passno++) {
+        opj_tcd_pass_t *pass = &cblk->passes[passno];
+
+        /* Prevent generation of FF as last data byte of a pass*/
+        /* For terminating passes, the flushing procedure ensured this already */
+        assert(pass->rate > 0);
+        if (cblk->data[pass->rate - 1] == 0xFF) {
+            pass->rate--;
+        }
+        pass->len = pass->rate - (passno == 0 ? 0 : cblk->passes[passno - 1].rate);
+    }
+
+#ifdef EXTRA_DEBUG
+    printf(" len=%d\n", (cblk->totalpasses) ? opj_mqc_numbytes(mqc) : 0);
+
+    /* Check that there not 0xff >=0x90 sequences */
+    if (cblk->totalpasses) {
+        OPJ_UINT32 i;
+        OPJ_UINT32 len = opj_mqc_numbytes(mqc);
+        for (i = 1; i < len; ++i) {
+            if (cblk->data[i - 1] == 0xff && cblk->data[i] >= 0x90) {
+                printf("0xff %02x at offset %d\n", cblk->data[i], i - 1);
+                abort();
+            }
+        }
+    }
+#endif
+
+    return cumwmsedec;
+}
diff --git a/third_party/libopenjpeg/t1.h b/third_party/libopenjpeg/t1.h
new file mode 100644
index 0000000..ce43658
--- /dev/null
+++ b/third_party/libopenjpeg/t1.h
@@ -0,0 +1,268 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2012, Carl Hetherington
+ * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef OPJ_T1_H
+#define OPJ_T1_H
+/**
+@file t1.h
+@brief Implementation of the tier-1 coding (coding of code-block coefficients) (T1)
+
+The functions in T1.C have for goal to realize the tier-1 coding operation. The functions
+in T1.C are used by some function in TCD.C.
+*/
+
+/** @defgroup T1 T1 - Implementation of the tier-1 coding */
+/*@{*/
+
+/* ----------------------------------------------------------------------- */
+#define T1_NMSEDEC_BITS 7
+
+#define T1_NUMCTXS_ZC  9
+#define T1_NUMCTXS_SC  5
+#define T1_NUMCTXS_MAG 3
+#define T1_NUMCTXS_AGG 1
+#define T1_NUMCTXS_UNI 1
+
+#define T1_CTXNO_ZC  0
+#define T1_CTXNO_SC  (T1_CTXNO_ZC+T1_NUMCTXS_ZC)
+#define T1_CTXNO_MAG (T1_CTXNO_SC+T1_NUMCTXS_SC)
+#define T1_CTXNO_AGG (T1_CTXNO_MAG+T1_NUMCTXS_MAG)
+#define T1_CTXNO_UNI (T1_CTXNO_AGG+T1_NUMCTXS_AGG)
+#define T1_NUMCTXS   (T1_CTXNO_UNI+T1_NUMCTXS_UNI)
+
+#define T1_NMSEDEC_FRACBITS (T1_NMSEDEC_BITS-1)
+
+#define T1_TYPE_MQ 0    /**< Normal coding using entropy coder */
+#define T1_TYPE_RAW 1   /**< No encoding the information is store under raw format in codestream (mode switch RAW)*/
+
+/* BEGINNING of flags that apply to opj_flag_t */
+/** We hold the state of individual data points for the T1 encoder using
+ *  a single 32-bit flags word to hold the state of 4 data points.  This corresponds
+ *  to the 4-point-high columns that the data is processed in.
+ *
+ *  These \#defines declare the layout of a 32-bit flags word.
+ *
+ *  This is currently done for encoding only.
+ *  The values must NOT be changed, otherwise this is going to break a lot of
+ *  assumptions.
+ */
+
+/* SIGMA: significance state (3 cols x 6 rows)
+ * CHI:   state for negative sample value (1 col x 6 rows)
+ * MU:    state for visited in refinement pass (1 col x 4 rows)
+ * PI:    state for visited in significance pass (1 col * 4 rows)
+ */
+
+#define T1_SIGMA_0  (1U << 0)
+#define T1_SIGMA_1  (1U << 1)
+#define T1_SIGMA_2  (1U << 2)
+#define T1_SIGMA_3  (1U << 3)
+#define T1_SIGMA_4  (1U << 4)
+#define T1_SIGMA_5  (1U << 5)
+#define T1_SIGMA_6  (1U << 6)
+#define T1_SIGMA_7  (1U << 7)
+#define T1_SIGMA_8  (1U << 8)
+#define T1_SIGMA_9  (1U << 9)
+#define T1_SIGMA_10 (1U << 10)
+#define T1_SIGMA_11 (1U << 11)
+#define T1_SIGMA_12 (1U << 12)
+#define T1_SIGMA_13 (1U << 13)
+#define T1_SIGMA_14 (1U << 14)
+#define T1_SIGMA_15 (1U << 15)
+#define T1_SIGMA_16 (1U << 16)
+#define T1_SIGMA_17 (1U << 17)
+
+#define T1_CHI_0    (1U << 18)
+#define T1_CHI_0_I  18
+#define T1_CHI_1    (1U << 19)
+#define T1_CHI_1_I  19
+#define T1_MU_0     (1U << 20)
+#define T1_PI_0     (1U << 21)
+#define T1_CHI_2    (1U << 22)
+#define T1_CHI_2_I  22
+#define T1_MU_1     (1U << 23)
+#define T1_PI_1     (1U << 24)
+#define T1_CHI_3    (1U << 25)
+#define T1_MU_2     (1U << 26)
+#define T1_PI_2     (1U << 27)
+#define T1_CHI_4    (1U << 28)
+#define T1_MU_3     (1U << 29)
+#define T1_PI_3     (1U << 30)
+#define T1_CHI_5    (1U << 31)
+#define T1_CHI_5_I  31
+
+/** As an example, the bits T1_SIGMA_3, T1_SIGMA_4 and T1_SIGMA_5
+ *  indicate the significance state of the west neighbour of data point zero
+ *  of our four, the point itself, and its east neighbour respectively.
+ *  Many of the bits are arranged so that given a flags word, you can
+ *  look at the values for the data point 0, then shift the flags
+ *  word right by 3 bits and look at the same bit positions to see the
+ *  values for data point 1.
+ *
+ *  The \#defines below help a bit with this; say you have a flags word
+ *  f, you can do things like
+ *
+ *  (f & T1_SIGMA_THIS)
+ *
+ *  to see the significance bit of data point 0, then do
+ *
+ *  ((f >> 3) & T1_SIGMA_THIS)
+ *
+ *  to see the significance bit of data point 1.
+ */
+
+#define T1_SIGMA_NW   T1_SIGMA_0
+#define T1_SIGMA_N    T1_SIGMA_1
+#define T1_SIGMA_NE   T1_SIGMA_2
+#define T1_SIGMA_W    T1_SIGMA_3
+#define T1_SIGMA_THIS T1_SIGMA_4
+#define T1_SIGMA_E    T1_SIGMA_5
+#define T1_SIGMA_SW   T1_SIGMA_6
+#define T1_SIGMA_S    T1_SIGMA_7
+#define T1_SIGMA_SE   T1_SIGMA_8
+#define T1_SIGMA_NEIGHBOURS (T1_SIGMA_NW | T1_SIGMA_N | T1_SIGMA_NE | T1_SIGMA_W | T1_SIGMA_E | T1_SIGMA_SW | T1_SIGMA_S | T1_SIGMA_SE)
+
+#define T1_CHI_THIS   T1_CHI_1
+#define T1_CHI_THIS_I T1_CHI_1_I
+#define T1_MU_THIS    T1_MU_0
+#define T1_PI_THIS    T1_PI_0
+#define T1_CHI_S      T1_CHI_2
+
+#define T1_LUT_SGN_W (1U << 0)
+#define T1_LUT_SIG_N (1U << 1)
+#define T1_LUT_SGN_E (1U << 2)
+#define T1_LUT_SIG_W (1U << 3)
+#define T1_LUT_SGN_N (1U << 4)
+#define T1_LUT_SIG_E (1U << 5)
+#define T1_LUT_SGN_S (1U << 6)
+#define T1_LUT_SIG_S (1U << 7)
+/* END of flags that apply to opj_flag_t */
+
+/* ----------------------------------------------------------------------- */
+
+/** Flags for 4 consecutive rows of a column */
+typedef OPJ_UINT32 opj_flag_t;
+
+/**
+Tier-1 coding (coding of code-block coefficients)
+*/
+typedef struct opj_t1 {
+
+    /** MQC component */
+    opj_mqc_t mqc;
+
+    OPJ_INT32  *data;
+    /** Flags used by decoder and encoder.
+     * Such that flags[1+0] is for state of col=0,row=0..3,
+       flags[1+1] for col=1, row=0..3, flags[1+flags_stride] for col=0,row=4..7, ...
+       This array avoids too much cache trashing when processing by 4 vertical samples
+       as done in the various decoding steps. */
+    opj_flag_t *flags;
+
+    OPJ_UINT32 w;
+    OPJ_UINT32 h;
+    OPJ_UINT32 datasize;
+    OPJ_UINT32 flagssize;
+    OPJ_BOOL   encoder;
+
+    /* The 3 variables below are only used by the decoder */
+    /* set to TRUE in multithreaded context */
+    OPJ_BOOL     mustuse_cblkdatabuffer;
+    /* Temporary buffer to concatenate all chunks of a codebock */
+    OPJ_BYTE    *cblkdatabuffer;
+    /* Maximum size available in cblkdatabuffer */
+    OPJ_UINT32   cblkdatabuffersize;
+} opj_t1_t;
+
+/** @name Exported functions */
+/*@{*/
+/* ----------------------------------------------------------------------- */
+
+/**
+Encode the code-blocks of a tile
+@param tcd TCD handle
+@param tile The tile to encode
+@param tcp Tile coding parameters
+@param mct_norms  FIXME DOC
+@param mct_numcomps Number of components used for MCT
+*/
+OPJ_BOOL opj_t1_encode_cblks(opj_tcd_t* tcd,
+                             opj_tcd_tile_t *tile,
+                             opj_tcp_t *tcp,
+                             const OPJ_FLOAT64 * mct_norms,
+                             OPJ_UINT32 mct_numcomps);
+
+/**
+Decode the code-blocks of a tile
+@param tcd TCD handle
+@param pret Pointer to return value
+@param tilec The tile to decode
+@param tccp Tile coding parameters
+@param p_manager the event manager
+@param p_manager_mutex mutex for the event manager
+@param check_pterm whether PTERM correct termination should be checked
+*/
+void opj_t1_decode_cblks(opj_tcd_t* tcd,
+                         volatile OPJ_BOOL* pret,
+                         opj_tcd_tilecomp_t* tilec,
+                         opj_tccp_t* tccp,
+                         opj_event_mgr_t *p_manager,
+                         opj_mutex_t* p_manager_mutex,
+                         OPJ_BOOL check_pterm);
+
+
+
+/**
+ * Creates a new Tier 1 handle
+ * and initializes the look-up tables of the Tier-1 coder/decoder
+ * @return a new T1 handle if successful, returns NULL otherwise
+*/
+opj_t1_t* opj_t1_create(OPJ_BOOL isEncoder);
+
+/**
+ * Destroys a previously created T1 handle
+ *
+ * @param p_t1 Tier 1 handle to destroy
+*/
+void opj_t1_destroy(opj_t1_t *p_t1);
+/* ----------------------------------------------------------------------- */
+/*@}*/
+
+/*@}*/
+
+#endif /* OPJ_T1_H */
diff --git a/third_party/libopenjpeg/t1_generate_luts.c b/third_party/libopenjpeg/t1_generate_luts.c
new file mode 100644
index 0000000..99c8c12
--- /dev/null
+++ b/third_party/libopenjpeg/t1_generate_luts.c
@@ -0,0 +1,323 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2007, Callum Lerwick <seg@haxxed.com>
+ * Copyright (c) 2012, Carl Hetherington
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "opj_includes.h"
+
+// defined elsewhere
+extern OPJ_BOOL vlc_init_tables();
+extern OPJ_BOOL vlc_tables_initialized;
+extern int vlc_tbl0[1024];
+extern int vlc_tbl1[1024];
+
+static int t1_init_ctxno_zc(OPJ_UINT32 f, OPJ_UINT32 orient)
+{
+    int h, v, d, n, t, hv;
+    n = 0;
+    h = ((f & T1_SIGMA_3) != 0) + ((f & T1_SIGMA_5) != 0);
+    v = ((f & T1_SIGMA_1) != 0) + ((f & T1_SIGMA_7) != 0);
+    d = ((f & T1_SIGMA_0) != 0) + ((f & T1_SIGMA_2) != 0) + ((
+                f & T1_SIGMA_8) != 0) + ((f & T1_SIGMA_6) != 0);
+
+    switch (orient) {
+    case 2:
+        t = h;
+        h = v;
+        v = t;
+    case 0:
+    case 1:
+        if (!h) {
+            if (!v) {
+                if (!d) {
+                    n = 0;
+                } else if (d == 1) {
+                    n = 1;
+                } else {
+                    n = 2;
+                }
+            } else if (v == 1) {
+                n = 3;
+            } else {
+                n = 4;
+            }
+        } else if (h == 1) {
+            if (!v) {
+                if (!d) {
+                    n = 5;
+                } else {
+                    n = 6;
+                }
+            } else {
+                n = 7;
+            }
+        } else {
+            n = 8;
+        }
+        break;
+    case 3:
+        hv = h + v;
+        if (!d) {
+            if (!hv) {
+                n = 0;
+            } else if (hv == 1) {
+                n = 1;
+            } else {
+                n = 2;
+            }
+        } else if (d == 1) {
+            if (!hv) {
+                n = 3;
+            } else if (hv == 1) {
+                n = 4;
+            } else {
+                n = 5;
+            }
+        } else if (d == 2) {
+            if (!hv) {
+                n = 6;
+            } else {
+                n = 7;
+            }
+        } else {
+            n = 8;
+        }
+        break;
+    }
+
+    return (T1_CTXNO_ZC + n);
+}
+
+static int t1_init_ctxno_sc(OPJ_UINT32 f)
+{
+    int hc, vc, n;
+    n = 0;
+
+    hc = opj_int_min(((f & (T1_LUT_SIG_E | T1_LUT_SGN_E)) ==
+                      T1_LUT_SIG_E) + ((f & (T1_LUT_SIG_W | T1_LUT_SGN_W)) == T1_LUT_SIG_W),
+                     1) - opj_int_min(((f & (T1_LUT_SIG_E | T1_LUT_SGN_E)) ==
+                                       (T1_LUT_SIG_E | T1_LUT_SGN_E)) +
+                                      ((f & (T1_LUT_SIG_W | T1_LUT_SGN_W)) ==
+                                       (T1_LUT_SIG_W | T1_LUT_SGN_W)), 1);
+
+    vc = opj_int_min(((f & (T1_LUT_SIG_N | T1_LUT_SGN_N)) ==
+                      T1_LUT_SIG_N) + ((f & (T1_LUT_SIG_S | T1_LUT_SGN_S)) == T1_LUT_SIG_S),
+                     1) - opj_int_min(((f & (T1_LUT_SIG_N | T1_LUT_SGN_N)) ==
+                                       (T1_LUT_SIG_N | T1_LUT_SGN_N)) +
+                                      ((f & (T1_LUT_SIG_S | T1_LUT_SGN_S)) ==
+                                       (T1_LUT_SIG_S | T1_LUT_SGN_S)), 1);
+
+    if (hc < 0) {
+        hc = -hc;
+        vc = -vc;
+    }
+    if (!hc) {
+        if (vc == -1) {
+            n = 1;
+        } else if (!vc) {
+            n = 0;
+        } else {
+            n = 1;
+        }
+    } else if (hc == 1) {
+        if (vc == -1) {
+            n = 2;
+        } else if (!vc) {
+            n = 3;
+        } else {
+            n = 4;
+        }
+    }
+
+    return (T1_CTXNO_SC + n);
+}
+
+static int t1_init_spb(OPJ_UINT32 f)
+{
+    int hc, vc, n;
+
+    hc = opj_int_min(((f & (T1_LUT_SIG_E | T1_LUT_SGN_E)) ==
+                      T1_LUT_SIG_E) + ((f & (T1_LUT_SIG_W | T1_LUT_SGN_W)) == T1_LUT_SIG_W),
+                     1) - opj_int_min(((f & (T1_LUT_SIG_E | T1_LUT_SGN_E)) ==
+                                       (T1_LUT_SIG_E | T1_LUT_SGN_E)) +
+                                      ((f & (T1_LUT_SIG_W | T1_LUT_SGN_W)) ==
+                                       (T1_LUT_SIG_W | T1_LUT_SGN_W)), 1);
+
+    vc = opj_int_min(((f & (T1_LUT_SIG_N | T1_LUT_SGN_N)) ==
+                      T1_LUT_SIG_N) + ((f & (T1_LUT_SIG_S | T1_LUT_SGN_S)) == T1_LUT_SIG_S),
+                     1) - opj_int_min(((f & (T1_LUT_SIG_N | T1_LUT_SGN_N)) ==
+                                       (T1_LUT_SIG_N | T1_LUT_SGN_N)) +
+                                      ((f & (T1_LUT_SIG_S | T1_LUT_SGN_S)) ==
+                                       (T1_LUT_SIG_S | T1_LUT_SGN_S)), 1);
+
+    if (!hc && !vc) {
+        n = 0;
+    } else {
+        n = (!(hc > 0 || (!hc && vc > 0)));
+    }
+
+    return n;
+}
+
+static void dump_array16(int array[], int size)
+{
+    int i;
+    --size;
+    for (i = 0; i < size; ++i) {
+        printf("0x%04x,", array[i]);
+        if (!((i + 1) & 0x7)) {
+            printf("\n    ");
+        } else {
+            printf(" ");
+        }
+    }
+    printf("0x%04x\n};\n\n", array[size]);
+}
+
+int main(int argc, char **argv)
+{
+    unsigned int i, j;
+    double u, v, t;
+
+    int lut_ctxno_zc[2048];
+    int lut_nmsedec_sig[1 << T1_NMSEDEC_BITS];
+    int lut_nmsedec_sig0[1 << T1_NMSEDEC_BITS];
+    int lut_nmsedec_ref[1 << T1_NMSEDEC_BITS];
+    int lut_nmsedec_ref0[1 << T1_NMSEDEC_BITS];
+    (void)argc;
+    (void)argv;
+
+    printf("/* This file was automatically generated by t1_generate_luts.c */\n\n");
+
+    /* lut_ctxno_zc */
+    for (j = 0; j < 4; ++j) {
+        for (i = 0; i < 512; ++i) {
+            OPJ_UINT32 orient = j;
+            if (orient == 2) {
+                orient = 1;
+            } else if (orient == 1) {
+                orient = 2;
+            }
+            lut_ctxno_zc[(orient << 9) | i] = t1_init_ctxno_zc(i, j);
+        }
+    }
+
+    printf("static const OPJ_BYTE lut_ctxno_zc[2048] = {\n    ");
+    for (i = 0; i < 2047; ++i) {
+        printf("%i,", lut_ctxno_zc[i]);
+        if (!((i + 1) & 0x1f)) {
+            printf("\n    ");
+        } else {
+            printf(" ");
+        }
+    }
+    printf("%i\n};\n\n", lut_ctxno_zc[2047]);
+
+    /* lut_ctxno_sc */
+    printf("static const OPJ_BYTE lut_ctxno_sc[256] = {\n    ");
+    for (i = 0; i < 255; ++i) {
+        printf("0x%x,", t1_init_ctxno_sc(i));
+        if (!((i + 1) & 0xf)) {
+            printf("\n    ");
+        } else {
+            printf(" ");
+        }
+    }
+    printf("0x%x\n};\n\n", t1_init_ctxno_sc(255));
+
+    /* lut_spb */
+    printf("static const OPJ_BYTE lut_spb[256] = {\n    ");
+    for (i = 0; i < 255; ++i) {
+        printf("%i,", t1_init_spb(i));
+        if (!((i + 1) & 0x1f)) {
+            printf("\n    ");
+        } else {
+            printf(" ");
+        }
+    }
+    printf("%i\n};\n\n", t1_init_spb(255));
+
+    /* FIXME FIXME FIXME */
+    /* fprintf(stdout,"nmsedec luts:\n"); */
+    for (i = 0U; i < (1U << T1_NMSEDEC_BITS); ++i) {
+        t = i / pow(2, T1_NMSEDEC_FRACBITS);
+        u = t;
+        v = t - 1.5;
+        lut_nmsedec_sig[i] =
+            opj_int_max(0,
+                        (int)(floor((u * u - v * v) * pow(2, T1_NMSEDEC_FRACBITS) + 0.5) / pow(2,
+                                T1_NMSEDEC_FRACBITS) * 8192.0));
+        lut_nmsedec_sig0[i] =
+            opj_int_max(0,
+                        (int)(floor((u * u) * pow(2, T1_NMSEDEC_FRACBITS) + 0.5) / pow(2,
+                                T1_NMSEDEC_FRACBITS) * 8192.0));
+        u = t - 1.0;
+        if (i & (1 << (T1_NMSEDEC_BITS - 1))) {
+            v = t - 1.5;
+        } else {
+            v = t - 0.5;
+        }
+        lut_nmsedec_ref[i] =
+            opj_int_max(0,
+                        (int)(floor((u * u - v * v) * pow(2, T1_NMSEDEC_FRACBITS) + 0.5) / pow(2,
+                                T1_NMSEDEC_FRACBITS) * 8192.0));
+        lut_nmsedec_ref0[i] =
+            opj_int_max(0,
+                        (int)(floor((u * u) * pow(2, T1_NMSEDEC_FRACBITS) + 0.5) / pow(2,
+                                T1_NMSEDEC_FRACBITS) * 8192.0));
+    }
+
+    printf("static const OPJ_INT16 lut_nmsedec_sig[1U << T1_NMSEDEC_BITS] = {\n    ");
+    dump_array16(lut_nmsedec_sig, 1U << T1_NMSEDEC_BITS);
+
+    printf("static const OPJ_INT16 lut_nmsedec_sig0[1U << T1_NMSEDEC_BITS] = {\n    ");
+    dump_array16(lut_nmsedec_sig0, 1U << T1_NMSEDEC_BITS);
+
+    printf("static const OPJ_INT16 lut_nmsedec_ref[1U << T1_NMSEDEC_BITS] = {\n    ");
+    dump_array16(lut_nmsedec_ref, 1U << T1_NMSEDEC_BITS);
+
+    printf("static const OPJ_INT16 lut_nmsedec_ref0[1U << T1_NMSEDEC_BITS] = {\n    ");
+    dump_array16(lut_nmsedec_ref0, 1U << T1_NMSEDEC_BITS);
+
+    vlc_tables_initialized = vlc_init_tables();
+    printf("static const OPJ_UINT16 vlc_tbl0[1024] = {\n    ");
+    dump_array16(vlc_tbl0, 1024);
+    printf("static const OPJ_UINT16 vlc_tbl1[1024] = {\n    ");
+    dump_array16(vlc_tbl1, 1024);
+
+    return 0;
+}
diff --git a/third_party/libopenjpeg/t1_ht_luts.h b/third_party/libopenjpeg/t1_ht_luts.h
new file mode 100644
index 0000000..f39d5d0
--- /dev/null
+++ b/third_party/libopenjpeg/t1_ht_luts.h
@@ -0,0 +1,261 @@
+static const OPJ_UINT16 vlc_tbl0[1024] = {
+    0x0023, 0x00a5, 0x0043, 0x0066, 0x0083, 0xa8ee, 0x0014, 0xd8df,
+    0x0023, 0x10be, 0x0043, 0xf5ff, 0x0083, 0x207e, 0x0055, 0x515f,
+    0x0023, 0x0035, 0x0043, 0x444e, 0x0083, 0xc4ce, 0x0014, 0xcccf,
+    0x0023, 0xe2fe, 0x0043, 0x99ff, 0x0083, 0x0096, 0x00c5, 0x313f,
+    0x0023, 0x00a5, 0x0043, 0x445e, 0x0083, 0xc8ce, 0x0014, 0x11df,
+    0x0023, 0xf4fe, 0x0043, 0xfcff, 0x0083, 0x009e, 0x0055, 0x0077,
+    0x0023, 0x0035, 0x0043, 0xf1ff, 0x0083, 0x88ae, 0x0014, 0x00b7,
+    0x0023, 0xf8fe, 0x0043, 0xe4ef, 0x0083, 0x888e, 0x00c5, 0x111f,
+    0x0023, 0x00a5, 0x0043, 0x0066, 0x0083, 0xa8ee, 0x0014, 0x54df,
+    0x0023, 0x10be, 0x0043, 0x22ef, 0x0083, 0x207e, 0x0055, 0x227f,
+    0x0023, 0x0035, 0x0043, 0x444e, 0x0083, 0xc4ce, 0x0014, 0x11bf,
+    0x0023, 0xe2fe, 0x0043, 0x00f7, 0x0083, 0x0096, 0x00c5, 0x223f,
+    0x0023, 0x00a5, 0x0043, 0x445e, 0x0083, 0xc8ce, 0x0014, 0x00d7,
+    0x0023, 0xf4fe, 0x0043, 0xbaff, 0x0083, 0x009e, 0x0055, 0x006f,
+    0x0023, 0x0035, 0x0043, 0xe6ff, 0x0083, 0x88ae, 0x0014, 0xa2af,
+    0x0023, 0xf8fe, 0x0043, 0x00e7, 0x0083, 0x888e, 0x00c5, 0x222f,
+    0x0002, 0x00c5, 0x0084, 0x207e, 0x0002, 0xc4ce, 0x0024, 0x00f7,
+    0x0002, 0xa2fe, 0x0044, 0x0056, 0x0002, 0x009e, 0x0014, 0x00d7,
+    0x0002, 0x10be, 0x0084, 0x0066, 0x0002, 0x88ae, 0x0024, 0x11df,
+    0x0002, 0xa8ee, 0x0044, 0x0036, 0x0002, 0x888e, 0x0014, 0x111f,
+    0x0002, 0x00c5, 0x0084, 0x006e, 0x0002, 0x88ce, 0x0024, 0x88ff,
+    0x0002, 0xb8fe, 0x0044, 0x444e, 0x0002, 0x0096, 0x0014, 0x00b7,
+    0x0002, 0xe4fe, 0x0084, 0x445e, 0x0002, 0x00a6, 0x0024, 0x00e7,
+    0x0002, 0x54de, 0x0044, 0x222e, 0x0002, 0x003e, 0x0014, 0x0077,
+    0x0002, 0x00c5, 0x0084, 0x207e, 0x0002, 0xc4ce, 0x0024, 0xf1ff,
+    0x0002, 0xa2fe, 0x0044, 0x0056, 0x0002, 0x009e, 0x0014, 0x11bf,
+    0x0002, 0x10be, 0x0084, 0x0066, 0x0002, 0x88ae, 0x0024, 0x22ef,
+    0x0002, 0xa8ee, 0x0044, 0x0036, 0x0002, 0x888e, 0x0014, 0x227f,
+    0x0002, 0x00c5, 0x0084, 0x006e, 0x0002, 0x88ce, 0x0024, 0xe4ef,
+    0x0002, 0xb8fe, 0x0044, 0x444e, 0x0002, 0x0096, 0x0014, 0xa2af,
+    0x0002, 0xe4fe, 0x0084, 0x445e, 0x0002, 0x00a6, 0x0024, 0xd8df,
+    0x0002, 0x54de, 0x0044, 0x222e, 0x0002, 0x003e, 0x0014, 0x515f,
+    0x0002, 0x0055, 0x0084, 0x0066, 0x0002, 0x88de, 0x0024, 0x32ff,
+    0x0002, 0x11fe, 0x0044, 0x444e, 0x0002, 0x00ae, 0x0014, 0x00b7,
+    0x0002, 0x317e, 0x0084, 0x515e, 0x0002, 0x00c6, 0x0024, 0x00d7,
+    0x0002, 0x20ee, 0x0044, 0x111e, 0x0002, 0x009e, 0x0014, 0x0077,
+    0x0002, 0x0055, 0x0084, 0x545e, 0x0002, 0x44ce, 0x0024, 0x00e7,
+    0x0002, 0xf1fe, 0x0044, 0x0036, 0x0002, 0x00a6, 0x0014, 0x555f,
+    0x0002, 0x74fe, 0x0084, 0x113e, 0x0002, 0x20be, 0x0024, 0x747f,
+    0x0002, 0xc4de, 0x0044, 0xf8ff, 0x0002, 0x0096, 0x0014, 0x222f,
+    0x0002, 0x0055, 0x0084, 0x0066, 0x0002, 0x88de, 0x0024, 0x00f7,
+    0x0002, 0x11fe, 0x0044, 0x444e, 0x0002, 0x00ae, 0x0014, 0x888f,
+    0x0002, 0x317e, 0x0084, 0x515e, 0x0002, 0x00c6, 0x0024, 0xc8cf,
+    0x0002, 0x20ee, 0x0044, 0x111e, 0x0002, 0x009e, 0x0014, 0x006f,
+    0x0002, 0x0055, 0x0084, 0x545e, 0x0002, 0x44ce, 0x0024, 0xd1df,
+    0x0002, 0xf1fe, 0x0044, 0x0036, 0x0002, 0x00a6, 0x0014, 0x227f,
+    0x0002, 0x74fe, 0x0084, 0x113e, 0x0002, 0x20be, 0x0024, 0x22bf,
+    0x0002, 0xc4de, 0x0044, 0x22ef, 0x0002, 0x0096, 0x0014, 0x323f,
+    0x0003, 0xd4de, 0xf4fd, 0xfcff, 0x0014, 0x113e, 0x0055, 0x888f,
+    0x0003, 0x32be, 0x0085, 0x00e7, 0x0025, 0x515e, 0xaafe, 0x727f,
+    0x0003, 0x44ce, 0xf8fd, 0x44ef, 0x0014, 0x647e, 0x0045, 0xa2af,
+    0x0003, 0x00a6, 0x555d, 0x99df, 0xf1fd, 0x0036, 0xf5fe, 0x626f,
+    0x0003, 0xd1de, 0xf4fd, 0xe6ff, 0x0014, 0x717e, 0x0055, 0xb1bf,
+    0x0003, 0x88ae, 0x0085, 0xd5df, 0x0025, 0x444e, 0xf2fe, 0x667f,
+    0x0003, 0x00c6, 0xf8fd, 0xe2ef, 0x0014, 0x545e, 0x0045, 0x119f,
+    0x0003, 0x0096, 0x555d, 0xc8cf, 0xf1fd, 0x111e, 0xc8ee, 0x0067,
+    0x0003, 0xd4de, 0xf4fd, 0xf3ff, 0x0014, 0x113e, 0x0055, 0x11bf,
+    0x0003, 0x32be, 0x0085, 0xd8df, 0x0025, 0x515e, 0xaafe, 0x222f,
+    0x0003, 0x44ce, 0xf8fd, 0x00f7, 0x0014, 0x647e, 0x0045, 0x989f,
+    0x0003, 0x00a6, 0x555d, 0x00d7, 0xf1fd, 0x0036, 0xf5fe, 0x446f,
+    0x0003, 0xd1de, 0xf4fd, 0xb9ff, 0x0014, 0x717e, 0x0055, 0x00b7,
+    0x0003, 0x88ae, 0x0085, 0xdcdf, 0x0025, 0x444e, 0xf2fe, 0x0077,
+    0x0003, 0x00c6, 0xf8fd, 0xe4ef, 0x0014, 0x545e, 0x0045, 0x737f,
+    0x0003, 0x0096, 0x555d, 0xb8bf, 0xf1fd, 0x111e, 0xc8ee, 0x323f,
+    0x0002, 0x00a5, 0x0084, 0x407e, 0x0002, 0x10de, 0x0024, 0x11df,
+    0x0002, 0x72fe, 0x0044, 0x0056, 0x0002, 0xa8ae, 0x0014, 0xb2bf,
+    0x0002, 0x0096, 0x0084, 0x0066, 0x0002, 0x00c6, 0x0024, 0x00e7,
+    0x0002, 0xc8ee, 0x0044, 0x222e, 0x0002, 0x888e, 0x0014, 0x0077,
+    0x0002, 0x00a5, 0x0084, 0x006e, 0x0002, 0x88ce, 0x0024, 0x00f7,
+    0x0002, 0x91fe, 0x0044, 0x0036, 0x0002, 0xa2ae, 0x0014, 0xaaaf,
+    0x0002, 0xb8fe, 0x0084, 0x005e, 0x0002, 0x00be, 0x0024, 0xc4cf,
+    0x0002, 0x44ee, 0x0044, 0xf4ff, 0x0002, 0x223e, 0x0014, 0x111f,
+    0x0002, 0x00a5, 0x0084, 0x407e, 0x0002, 0x10de, 0x0024, 0x99ff,
+    0x0002, 0x72fe, 0x0044, 0x0056, 0x0002, 0xa8ae, 0x0014, 0x00b7,
+    0x0002, 0x0096, 0x0084, 0x0066, 0x0002, 0x00c6, 0x0024, 0x00d7,
+    0x0002, 0xc8ee, 0x0044, 0x222e, 0x0002, 0x888e, 0x0014, 0x444f,
+    0x0002, 0x00a5, 0x0084, 0x006e, 0x0002, 0x88ce, 0x0024, 0xe2ef,
+    0x0002, 0x91fe, 0x0044, 0x0036, 0x0002, 0xa2ae, 0x0014, 0x447f,
+    0x0002, 0xb8fe, 0x0084, 0x005e, 0x0002, 0x00be, 0x0024, 0x009f,
+    0x0002, 0x44ee, 0x0044, 0x76ff, 0x0002, 0x223e, 0x0014, 0x313f,
+    0x0003, 0x00c6, 0x0085, 0xd9ff, 0xf2fd, 0x647e, 0xf1fe, 0x99bf,
+    0x0003, 0xa2ae, 0x0025, 0x66ef, 0xf4fd, 0x0056, 0xe2ee, 0x737f,
+    0x0003, 0x98be, 0x0045, 0x00f7, 0xf8fd, 0x0066, 0x76fe, 0x889f,
+    0x0003, 0x888e, 0x0015, 0xd5df, 0x00a5, 0x222e, 0x98de, 0x444f,
+    0x0003, 0xb2be, 0x0085, 0xfcff, 0xf2fd, 0x226e, 0x0096, 0x00b7,
+    0x0003, 0xaaae, 0x0025, 0xd1df, 0xf4fd, 0x0036, 0xd4de, 0x646f,
+    0x0003, 0xa8ae, 0x0045, 0xeaef, 0xf8fd, 0x445e, 0xe8ee, 0x717f,
+    0x0003, 0x323e, 0x0015, 0xc4cf, 0x00a5, 0xfaff, 0x88ce, 0x313f,
+    0x0003, 0x00c6, 0x0085, 0x77ff, 0xf2fd, 0x647e, 0xf1fe, 0xb3bf,
+    0x0003, 0xa2ae, 0x0025, 0x00e7, 0xf4fd, 0x0056, 0xe2ee, 0x0077,
+    0x0003, 0x98be, 0x0045, 0xe4ef, 0xf8fd, 0x0066, 0x76fe, 0x667f,
+    0x0003, 0x888e, 0x0015, 0x00d7, 0x00a5, 0x222e, 0x98de, 0x333f,
+    0x0003, 0xb2be, 0x0085, 0x75ff, 0xf2fd, 0x226e, 0x0096, 0x919f,
+    0x0003, 0xaaae, 0x0025, 0x99df, 0xf4fd, 0x0036, 0xd4de, 0x515f,
+    0x0003, 0xa8ae, 0x0045, 0xecef, 0xf8fd, 0x445e, 0xe8ee, 0x727f,
+    0x0003, 0x323e, 0x0015, 0xb1bf, 0x00a5, 0xf3ff, 0x88ce, 0x111f,
+    0x0003, 0x54de, 0xf2fd, 0x111e, 0x0014, 0x647e, 0xf8fe, 0xcccf,
+    0x0003, 0x91be, 0x0045, 0x22ef, 0x0025, 0x222e, 0xf3fe, 0x888f,
+    0x0003, 0x00c6, 0x0085, 0x00f7, 0x0014, 0x115e, 0xfcfe, 0xa8af,
+    0x0003, 0x00a6, 0x0035, 0xc8df, 0xf1fd, 0x313e, 0x66fe, 0x646f,
+    0x0003, 0xc8ce, 0xf2fd, 0xf5ff, 0x0014, 0x0066, 0xf4fe, 0xbabf,
+    0x0003, 0x22ae, 0x0045, 0x00e7, 0x0025, 0x323e, 0xeafe, 0x737f,
+    0x0003, 0xb2be, 0x0085, 0x55df, 0x0014, 0x0056, 0x717e, 0x119f,
+    0x0003, 0x0096, 0x0035, 0xc4cf, 0xf1fd, 0x333e, 0xe8ee, 0x444f,
+    0x0003, 0x54de, 0xf2fd, 0x111e, 0x0014, 0x647e, 0xf8fe, 0x99bf,
+    0x0003, 0x91be, 0x0045, 0xe2ef, 0x0025, 0x222e, 0xf3fe, 0x667f,
+    0x0003, 0x00c6, 0x0085, 0xe4ef, 0x0014, 0x115e, 0xfcfe, 0x989f,
+    0x0003, 0x00a6, 0x0035, 0x00d7, 0xf1fd, 0x313e, 0x66fe, 0x226f,
+    0x0003, 0xc8ce, 0xf2fd, 0xb9ff, 0x0014, 0x0066, 0xf4fe, 0x00b7,
+    0x0003, 0x22ae, 0x0045, 0xd1df, 0x0025, 0x323e, 0xeafe, 0x0077,
+    0x0003, 0xb2be, 0x0085, 0xecef, 0x0014, 0x0056, 0x717e, 0x727f,
+    0x0003, 0x0096, 0x0035, 0xb8bf, 0xf1fd, 0x333e, 0xe8ee, 0x545f,
+    0xf1fc, 0xd1de, 0xfafd, 0x00d7, 0xf8fc, 0x0016, 0xfffd, 0x747f,
+    0xf4fc, 0x717e, 0xf3fd, 0xb3bf, 0xf2fc, 0xeaef, 0xe8ee, 0x444f,
+    0xf1fc, 0x22ae, 0x0005, 0xb8bf, 0xf8fc, 0x00f7, 0xfcfe, 0x0077,
+    0xf4fc, 0x115e, 0xf5fd, 0x757f, 0xf2fc, 0xd8df, 0xe2ee, 0x333f,
+    0xf1fc, 0xb2be, 0xfafd, 0x88cf, 0xf8fc, 0xfbff, 0xfffd, 0x737f,
+    0xf4fc, 0x006e, 0xf3fd, 0x00b7, 0xf2fc, 0x66ef, 0xf9fe, 0x313f,
+    0xf1fc, 0x009e, 0x0005, 0xbabf, 0xf8fc, 0xfdff, 0xf6fe, 0x0067,
+    0xf4fc, 0x0026, 0xf5fd, 0x888f, 0xf2fc, 0xdcdf, 0xd4de, 0x222f,
+    0xf1fc, 0xd1de, 0xfafd, 0xc4cf, 0xf8fc, 0x0016, 0xfffd, 0x727f,
+    0xf4fc, 0x717e, 0xf3fd, 0x99bf, 0xf2fc, 0xecef, 0xe8ee, 0x0047,
+    0xf1fc, 0x22ae, 0x0005, 0x00a7, 0xf8fc, 0xf7ff, 0xfcfe, 0x0057,
+    0xf4fc, 0x115e, 0xf5fd, 0x0097, 0xf2fc, 0xd5df, 0xe2ee, 0x0037,
+    0xf1fc, 0xb2be, 0xfafd, 0x00c7, 0xf8fc, 0xfeff, 0xfffd, 0x667f,
+    0xf4fc, 0x006e, 0xf3fd, 0xa8af, 0xf2fc, 0x00e7, 0xf9fe, 0x323f,
+    0xf1fc, 0x009e, 0x0005, 0xb1bf, 0xf8fc, 0xe4ef, 0xf6fe, 0x545f,
+    0xf4fc, 0x0026, 0xf5fd, 0x0087, 0xf2fc, 0x99df, 0xd4de, 0x111f
+};
+
+static const OPJ_UINT16 vlc_tbl1[1024] = {
+    0x0013, 0x0065, 0x0043, 0x00de, 0x0083, 0x888d, 0x0023, 0x444e,
+    0x0013, 0x00a5, 0x0043, 0x88ae, 0x0083, 0x0035, 0x0023, 0x00d7,
+    0x0013, 0x00c5, 0x0043, 0x009e, 0x0083, 0x0055, 0x0023, 0x222e,
+    0x0013, 0x0095, 0x0043, 0x007e, 0x0083, 0x10fe, 0x0023, 0x0077,
+    0x0013, 0x0065, 0x0043, 0x88ce, 0x0083, 0x888d, 0x0023, 0x111e,
+    0x0013, 0x00a5, 0x0043, 0x005e, 0x0083, 0x0035, 0x0023, 0x00e7,
+    0x0013, 0x00c5, 0x0043, 0x00be, 0x0083, 0x0055, 0x0023, 0x11ff,
+    0x0013, 0x0095, 0x0043, 0x003e, 0x0083, 0x40ee, 0x0023, 0xa2af,
+    0x0013, 0x0065, 0x0043, 0x00de, 0x0083, 0x888d, 0x0023, 0x444e,
+    0x0013, 0x00a5, 0x0043, 0x88ae, 0x0083, 0x0035, 0x0023, 0x44ef,
+    0x0013, 0x00c5, 0x0043, 0x009e, 0x0083, 0x0055, 0x0023, 0x222e,
+    0x0013, 0x0095, 0x0043, 0x007e, 0x0083, 0x10fe, 0x0023, 0x00b7,
+    0x0013, 0x0065, 0x0043, 0x88ce, 0x0083, 0x888d, 0x0023, 0x111e,
+    0x0013, 0x00a5, 0x0043, 0x005e, 0x0083, 0x0035, 0x0023, 0xc4cf,
+    0x0013, 0x00c5, 0x0043, 0x00be, 0x0083, 0x0055, 0x0023, 0x00f7,
+    0x0013, 0x0095, 0x0043, 0x003e, 0x0083, 0x40ee, 0x0023, 0x006f,
+    0x0001, 0x0084, 0x0001, 0x0056, 0x0001, 0x0014, 0x0001, 0x00d7,
+    0x0001, 0x0024, 0x0001, 0x0096, 0x0001, 0x0045, 0x0001, 0x0077,
+    0x0001, 0x0084, 0x0001, 0x00c6, 0x0001, 0x0014, 0x0001, 0x888f,
+    0x0001, 0x0024, 0x0001, 0x00f7, 0x0001, 0x0035, 0x0001, 0x222f,
+    0x0001, 0x0084, 0x0001, 0x40fe, 0x0001, 0x0014, 0x0001, 0x00b7,
+    0x0001, 0x0024, 0x0001, 0x00bf, 0x0001, 0x0045, 0x0001, 0x0067,
+    0x0001, 0x0084, 0x0001, 0x00a6, 0x0001, 0x0014, 0x0001, 0x444f,
+    0x0001, 0x0024, 0x0001, 0x00e7, 0x0001, 0x0035, 0x0001, 0x113f,
+    0x0001, 0x0084, 0x0001, 0x0056, 0x0001, 0x0014, 0x0001, 0x00cf,
+    0x0001, 0x0024, 0x0001, 0x0096, 0x0001, 0x0045, 0x0001, 0x006f,
+    0x0001, 0x0084, 0x0001, 0x00c6, 0x0001, 0x0014, 0x0001, 0x009f,
+    0x0001, 0x0024, 0x0001, 0x00ef, 0x0001, 0x0035, 0x0001, 0x323f,
+    0x0001, 0x0084, 0x0001, 0x40fe, 0x0001, 0x0014, 0x0001, 0x00af,
+    0x0001, 0x0024, 0x0001, 0x44ff, 0x0001, 0x0045, 0x0001, 0x005f,
+    0x0001, 0x0084, 0x0001, 0x00a6, 0x0001, 0x0014, 0x0001, 0x007f,
+    0x0001, 0x0024, 0x0001, 0x00df, 0x0001, 0x0035, 0x0001, 0x111f,
+    0x0001, 0x0024, 0x0001, 0x0056, 0x0001, 0x0085, 0x0001, 0x00bf,
+    0x0001, 0x0014, 0x0001, 0x00f7, 0x0001, 0x00c6, 0x0001, 0x0077,
+    0x0001, 0x0024, 0x0001, 0xf8ff, 0x0001, 0x0045, 0x0001, 0x007f,
+    0x0001, 0x0014, 0x0001, 0x00df, 0x0001, 0x00a6, 0x0001, 0x313f,
+    0x0001, 0x0024, 0x0001, 0x222e, 0x0001, 0x0085, 0x0001, 0x00b7,
+    0x0001, 0x0014, 0x0001, 0x44ef, 0x0001, 0xa2ae, 0x0001, 0x0067,
+    0x0001, 0x0024, 0x0001, 0x51ff, 0x0001, 0x0045, 0x0001, 0x0097,
+    0x0001, 0x0014, 0x0001, 0x00cf, 0x0001, 0x0036, 0x0001, 0x223f,
+    0x0001, 0x0024, 0x0001, 0x0056, 0x0001, 0x0085, 0x0001, 0xb2bf,
+    0x0001, 0x0014, 0x0001, 0x40ef, 0x0001, 0x00c6, 0x0001, 0x006f,
+    0x0001, 0x0024, 0x0001, 0x72ff, 0x0001, 0x0045, 0x0001, 0x009f,
+    0x0001, 0x0014, 0x0001, 0x00d7, 0x0001, 0x00a6, 0x0001, 0x444f,
+    0x0001, 0x0024, 0x0001, 0x222e, 0x0001, 0x0085, 0x0001, 0xa8af,
+    0x0001, 0x0014, 0x0001, 0x00e7, 0x0001, 0xa2ae, 0x0001, 0x005f,
+    0x0001, 0x0024, 0x0001, 0x44ff, 0x0001, 0x0045, 0x0001, 0x888f,
+    0x0001, 0x0014, 0x0001, 0xaaaf, 0x0001, 0x0036, 0x0001, 0x111f,
+    0x0002, 0xf8fe, 0x0024, 0x0056, 0x0002, 0x00b6, 0x0085, 0x66ff,
+    0x0002, 0x00ce, 0x0014, 0x111e, 0x0002, 0x0096, 0x0035, 0xa8af,
+    0x0002, 0x00f6, 0x0024, 0x313e, 0x0002, 0x00a6, 0x0045, 0xb3bf,
+    0x0002, 0xb2be, 0x0014, 0xf5ff, 0x0002, 0x0066, 0x517e, 0x545f,
+    0x0002, 0xf2fe, 0x0024, 0x222e, 0x0002, 0x22ae, 0x0085, 0x44ef,
+    0x0002, 0x00c6, 0x0014, 0xf4ff, 0x0002, 0x0076, 0x0035, 0x447f,
+    0x0002, 0x40de, 0x0024, 0x323e, 0x0002, 0x009e, 0x0045, 0x00d7,
+    0x0002, 0x88be, 0x0014, 0xfaff, 0x0002, 0x115e, 0xf1fe, 0x444f,
+    0x0002, 0xf8fe, 0x0024, 0x0056, 0x0002, 0x00b6, 0x0085, 0xc8ef,
+    0x0002, 0x00ce, 0x0014, 0x111e, 0x0002, 0x0096, 0x0035, 0x888f,
+    0x0002, 0x00f6, 0x0024, 0x313e, 0x0002, 0x00a6, 0x0045, 0x44df,
+    0x0002, 0xb2be, 0x0014, 0xa8ff, 0x0002, 0x0066, 0x517e, 0x006f,
+    0x0002, 0xf2fe, 0x0024, 0x222e, 0x0002, 0x22ae, 0x0085, 0x00e7,
+    0x0002, 0x00c6, 0x0014, 0xe2ef, 0x0002, 0x0076, 0x0035, 0x727f,
+    0x0002, 0x40de, 0x0024, 0x323e, 0x0002, 0x009e, 0x0045, 0xb1bf,
+    0x0002, 0x88be, 0x0014, 0x73ff, 0x0002, 0x115e, 0xf1fe, 0x333f,
+    0x0001, 0x0084, 0x0001, 0x20ee, 0x0001, 0x00c5, 0x0001, 0xc4cf,
+    0x0001, 0x0044, 0x0001, 0x32ff, 0x0001, 0x0015, 0x0001, 0x888f,
+    0x0001, 0x0084, 0x0001, 0x0066, 0x0001, 0x0025, 0x0001, 0x00af,
+    0x0001, 0x0044, 0x0001, 0x22ef, 0x0001, 0x00a6, 0x0001, 0x005f,
+    0x0001, 0x0084, 0x0001, 0x444e, 0x0001, 0x00c5, 0x0001, 0xcccf,
+    0x0001, 0x0044, 0x0001, 0x00f7, 0x0001, 0x0015, 0x0001, 0x006f,
+    0x0001, 0x0084, 0x0001, 0x0056, 0x0001, 0x0025, 0x0001, 0x009f,
+    0x0001, 0x0044, 0x0001, 0x00df, 0x0001, 0x30fe, 0x0001, 0x222f,
+    0x0001, 0x0084, 0x0001, 0x20ee, 0x0001, 0x00c5, 0x0001, 0xc8cf,
+    0x0001, 0x0044, 0x0001, 0x11ff, 0x0001, 0x0015, 0x0001, 0x0077,
+    0x0001, 0x0084, 0x0001, 0x0066, 0x0001, 0x0025, 0x0001, 0x007f,
+    0x0001, 0x0044, 0x0001, 0x00e7, 0x0001, 0x00a6, 0x0001, 0x0037,
+    0x0001, 0x0084, 0x0001, 0x444e, 0x0001, 0x00c5, 0x0001, 0x00b7,
+    0x0001, 0x0044, 0x0001, 0x00bf, 0x0001, 0x0015, 0x0001, 0x003f,
+    0x0001, 0x0084, 0x0001, 0x0056, 0x0001, 0x0025, 0x0001, 0x0097,
+    0x0001, 0x0044, 0x0001, 0x00d7, 0x0001, 0x30fe, 0x0001, 0x111f,
+    0x0002, 0xa8ee, 0x0044, 0x888e, 0x0002, 0x00d6, 0x00c5, 0xf3ff,
+    0x0002, 0xfcfe, 0x0025, 0x003e, 0x0002, 0x00b6, 0x0055, 0xd8df,
+    0x0002, 0xf8fe, 0x0044, 0x0066, 0x0002, 0x207e, 0x0085, 0x99ff,
+    0x0002, 0x00e6, 0x00f5, 0x0036, 0x0002, 0x00a6, 0x0015, 0x009f,
+    0x0002, 0xf2fe, 0x0044, 0x0076, 0x0002, 0x44ce, 0x00c5, 0x76ff,
+    0x0002, 0xf1fe, 0x0025, 0x444e, 0x0002, 0x00ae, 0x0055, 0xc8cf,
+    0x0002, 0xf4fe, 0x0044, 0x445e, 0x0002, 0x10be, 0x0085, 0xe4ef,
+    0x0002, 0x54de, 0x00f5, 0x111e, 0x0002, 0x0096, 0x0015, 0x222f,
+    0x0002, 0xa8ee, 0x0044, 0x888e, 0x0002, 0x00d6, 0x00c5, 0xfaff,
+    0x0002, 0xfcfe, 0x0025, 0x003e, 0x0002, 0x00b6, 0x0055, 0x11bf,
+    0x0002, 0xf8fe, 0x0044, 0x0066, 0x0002, 0x207e, 0x0085, 0x22ef,
+    0x0002, 0x00e6, 0x00f5, 0x0036, 0x0002, 0x00a6, 0x0015, 0x227f,
+    0x0002, 0xf2fe, 0x0044, 0x0076, 0x0002, 0x44ce, 0x00c5, 0xd5ff,
+    0x0002, 0xf1fe, 0x0025, 0x444e, 0x0002, 0x00ae, 0x0055, 0x006f,
+    0x0002, 0xf4fe, 0x0044, 0x445e, 0x0002, 0x10be, 0x0085, 0x11df,
+    0x0002, 0x54de, 0x00f5, 0x111e, 0x0002, 0x0096, 0x0015, 0x515f,
+    0x0003, 0x00f6, 0x0014, 0x111e, 0x0044, 0x888e, 0x00a5, 0xd4df,
+    0x0003, 0xa2ae, 0x0055, 0x76ff, 0x0024, 0x223e, 0x00b6, 0xaaaf,
+    0x0003, 0x00e6, 0x0014, 0xf5ff, 0x0044, 0x0066, 0x0085, 0xcccf,
+    0x0003, 0x009e, 0x00c5, 0x44ef, 0x0024, 0x0036, 0xf8fe, 0x317f,
+    0x0003, 0xe8ee, 0x0014, 0xf1ff, 0x0044, 0x0076, 0x00a5, 0xc4cf,
+    0x0003, 0x227e, 0x0055, 0xd1df, 0x0024, 0x444e, 0xf4fe, 0x515f,
+    0x0003, 0x00d6, 0x0014, 0xe2ef, 0x0044, 0x445e, 0x0085, 0x22bf,
+    0x0003, 0x0096, 0x00c5, 0xc8df, 0x0024, 0x222e, 0xf2fe, 0x226f,
+    0x0003, 0x00f6, 0x0014, 0x111e, 0x0044, 0x888e, 0x00a5, 0xb1bf,
+    0x0003, 0xa2ae, 0x0055, 0x33ff, 0x0024, 0x223e, 0x00b6, 0xa8af,
+    0x0003, 0x00e6, 0x0014, 0xb9ff, 0x0044, 0x0066, 0x0085, 0xa8bf,
+    0x0003, 0x009e, 0x00c5, 0xe4ef, 0x0024, 0x0036, 0xf8fe, 0x646f,
+    0x0003, 0xe8ee, 0x0014, 0xfcff, 0x0044, 0x0076, 0x00a5, 0xc8cf,
+    0x0003, 0x227e, 0x0055, 0xeaef, 0x0024, 0x444e, 0xf4fe, 0x747f,
+    0x0003, 0x00d6, 0x0014, 0xfaff, 0x0044, 0x445e, 0x0085, 0xb2bf,
+    0x0003, 0x0096, 0x00c5, 0x44df, 0x0024, 0x222e, 0xf2fe, 0x313f,
+    0x00f3, 0xfafe, 0xf1fd, 0x0036, 0x0004, 0x32be, 0x0075, 0x11df,
+    0x00f3, 0x54de, 0xf2fd, 0xe4ef, 0x00d5, 0x717e, 0xfcfe, 0x737f,
+    0x00f3, 0xf3fe, 0xf8fd, 0x111e, 0x0004, 0x0096, 0x0055, 0xb1bf,
+    0x00f3, 0x00ce, 0x00b5, 0xd8df, 0xf4fd, 0x0066, 0xb9fe, 0x545f,
+    0x00f3, 0x76fe, 0xf1fd, 0x0026, 0x0004, 0x00a6, 0x0075, 0x009f,
+    0x00f3, 0x00ae, 0xf2fd, 0xf7ff, 0x00d5, 0x0046, 0xf5fe, 0x747f,
+    0x00f3, 0x00e6, 0xf8fd, 0x0016, 0x0004, 0x0086, 0x0055, 0x888f,
+    0x00f3, 0x00c6, 0x00b5, 0xe2ef, 0xf4fd, 0x115e, 0xa8ee, 0x113f,
+    0x00f3, 0xfafe, 0xf1fd, 0x0036, 0x0004, 0x32be, 0x0075, 0xd1df,
+    0x00f3, 0x54de, 0xf2fd, 0xfbff, 0x00d5, 0x717e, 0xfcfe, 0x447f,
+    0x00f3, 0xf3fe, 0xf8fd, 0x111e, 0x0004, 0x0096, 0x0055, 0x727f,
+    0x00f3, 0x00ce, 0x00b5, 0x22ef, 0xf4fd, 0x0066, 0xb9fe, 0x444f,
+    0x00f3, 0x76fe, 0xf1fd, 0x0026, 0x0004, 0x00a6, 0x0075, 0x11bf,
+    0x00f3, 0x00ae, 0xf2fd, 0xffff, 0x00d5, 0x0046, 0xf5fe, 0x323f,
+    0x00f3, 0x00e6, 0xf8fd, 0x0016, 0x0004, 0x0086, 0x0055, 0x006f,
+    0x00f3, 0x00c6, 0x00b5, 0xb8bf, 0xf4fd, 0x115e, 0xa8ee, 0x222f
+};
\ No newline at end of file
diff --git a/third_party/libopenjpeg20/t1_luts.h b/third_party/libopenjpeg/t1_luts.h
similarity index 100%
rename from third_party/libopenjpeg20/t1_luts.h
rename to third_party/libopenjpeg/t1_luts.h
diff --git a/third_party/libopenjpeg/t2.c b/third_party/libopenjpeg/t2.c
new file mode 100644
index 0000000..ebda005
--- /dev/null
+++ b/third_party/libopenjpeg/t2.c
@@ -0,0 +1,1684 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
+ * Copyright (c) 2012, CS Systemes d'Information, France
+ * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "opj_includes.h"
+#include "opj_common.h"
+
+
+/** @defgroup T2 T2 - Implementation of a tier-2 coding */
+/*@{*/
+
+/** @name Local static functions */
+/*@{*/
+
+static void opj_t2_putcommacode(opj_bio_t *bio, OPJ_INT32 n);
+
+static OPJ_UINT32 opj_t2_getcommacode(opj_bio_t *bio);
+/**
+Variable length code for signalling delta Zil (truncation point)
+@param bio  Bit Input/Output component
+@param n    delta Zil
+*/
+static void opj_t2_putnumpasses(opj_bio_t *bio, OPJ_UINT32 n);
+static OPJ_UINT32 opj_t2_getnumpasses(opj_bio_t *bio);
+
+/**
+Encode a packet of a tile to a destination buffer
+@param tileno Number of the tile encoded
+@param tile Tile for which to write the packets
+@param tcp Tile coding parameters
+@param pi Packet identity
+@param dest Destination buffer
+@param p_data_written   FIXME DOC
+@param len Length of the destination buffer
+@param cstr_info Codestream information structure
+@param p_t2_mode If == THRESH_CALC In Threshold calculation ,If == FINAL_PASS Final pass
+@param p_manager the user event manager
+@return
+*/
+static OPJ_BOOL opj_t2_encode_packet(OPJ_UINT32 tileno,
+                                     opj_tcd_tile_t *tile,
+                                     opj_tcp_t *tcp,
+                                     opj_pi_iterator_t *pi,
+                                     OPJ_BYTE *dest,
+                                     OPJ_UINT32 * p_data_written,
+                                     OPJ_UINT32 len,
+                                     opj_codestream_info_t *cstr_info,
+                                     J2K_T2_MODE p_t2_mode,
+                                     opj_event_mgr_t *p_manager);
+
+/**
+Decode a packet of a tile from a source buffer
+@param t2 T2 handle
+@param tile Tile for which to write the packets
+@param tcp Tile coding parameters
+@param pi Packet identity
+@param src Source buffer
+@param data_read   FIXME DOC
+@param max_length  FIXME DOC
+@param pack_info Packet information
+@param p_manager the user event manager
+
+@return  FIXME DOC
+*/
+static OPJ_BOOL opj_t2_decode_packet(opj_t2_t* t2,
+                                     opj_tcd_tile_t *tile,
+                                     opj_tcp_t *tcp,
+                                     opj_pi_iterator_t *pi,
+                                     OPJ_BYTE *src,
+                                     OPJ_UINT32 * data_read,
+                                     OPJ_UINT32 max_length,
+                                     opj_packet_info_t *pack_info,
+                                     opj_event_mgr_t *p_manager);
+
+static OPJ_BOOL opj_t2_skip_packet(opj_t2_t* p_t2,
+                                   opj_tcd_tile_t *p_tile,
+                                   opj_tcp_t *p_tcp,
+                                   opj_pi_iterator_t *p_pi,
+                                   OPJ_BYTE *p_src,
+                                   OPJ_UINT32 * p_data_read,
+                                   OPJ_UINT32 p_max_length,
+                                   opj_packet_info_t *p_pack_info,
+                                   opj_event_mgr_t *p_manager);
+
+static OPJ_BOOL opj_t2_read_packet_header(opj_t2_t* p_t2,
+        opj_tcd_tile_t *p_tile,
+        opj_tcp_t *p_tcp,
+        opj_pi_iterator_t *p_pi,
+        OPJ_BOOL * p_is_data_present,
+        OPJ_BYTE *p_src_data,
+        OPJ_UINT32 * p_data_read,
+        OPJ_UINT32 p_max_length,
+        opj_packet_info_t *p_pack_info,
+        opj_event_mgr_t *p_manager);
+
+static OPJ_BOOL opj_t2_read_packet_data(opj_t2_t* p_t2,
+                                        opj_tcd_tile_t *p_tile,
+                                        opj_pi_iterator_t *p_pi,
+                                        OPJ_BYTE *p_src_data,
+                                        OPJ_UINT32 * p_data_read,
+                                        OPJ_UINT32 p_max_length,
+                                        opj_packet_info_t *pack_info,
+                                        opj_event_mgr_t *p_manager);
+
+static OPJ_BOOL opj_t2_skip_packet_data(opj_t2_t* p_t2,
+                                        opj_tcd_tile_t *p_tile,
+                                        opj_pi_iterator_t *p_pi,
+                                        OPJ_UINT32 * p_data_read,
+                                        OPJ_UINT32 p_max_length,
+                                        opj_packet_info_t *pack_info,
+                                        opj_event_mgr_t *p_manager);
+
+/**
+@param cblk
+@param index
+@param cblksty
+@param first
+*/
+static OPJ_BOOL opj_t2_init_seg(opj_tcd_cblk_dec_t* cblk,
+                                OPJ_UINT32 index,
+                                OPJ_UINT32 cblksty,
+                                OPJ_UINT32 first);
+
+/*@}*/
+
+/*@}*/
+
+/* ----------------------------------------------------------------------- */
+
+/* #define RESTART 0x04 */
+static void opj_t2_putcommacode(opj_bio_t *bio, OPJ_INT32 n)
+{
+    while (--n >= 0) {
+        opj_bio_write(bio, 1, 1);
+    }
+    opj_bio_write(bio, 0, 1);
+}
+
+static OPJ_UINT32 opj_t2_getcommacode(opj_bio_t *bio)
+{
+    OPJ_UINT32 n = 0;
+    while (opj_bio_read(bio, 1)) {
+        ++n;
+    }
+    return n;
+}
+
+static void opj_t2_putnumpasses(opj_bio_t *bio, OPJ_UINT32 n)
+{
+    if (n == 1) {
+        opj_bio_write(bio, 0, 1);
+    } else if (n == 2) {
+        opj_bio_write(bio, 2, 2);
+    } else if (n <= 5) {
+        opj_bio_write(bio, 0xc | (n - 3), 4);
+    } else if (n <= 36) {
+        opj_bio_write(bio, 0x1e0 | (n - 6), 9);
+    } else if (n <= 164) {
+        opj_bio_write(bio, 0xff80 | (n - 37), 16);
+    }
+}
+
+static OPJ_UINT32 opj_t2_getnumpasses(opj_bio_t *bio)
+{
+    OPJ_UINT32 n;
+    if (!opj_bio_read(bio, 1)) {
+        return 1;
+    }
+    if (!opj_bio_read(bio, 1)) {
+        return 2;
+    }
+    if ((n = opj_bio_read(bio, 2)) != 3) {
+        return (3 + n);
+    }
+    if ((n = opj_bio_read(bio, 5)) != 31) {
+        return (6 + n);
+    }
+    return (37 + opj_bio_read(bio, 7));
+}
+
+/* ----------------------------------------------------------------------- */
+
+OPJ_BOOL opj_t2_encode_packets(opj_t2_t* p_t2,
+                               OPJ_UINT32 p_tile_no,
+                               opj_tcd_tile_t *p_tile,
+                               OPJ_UINT32 p_maxlayers,
+                               OPJ_BYTE *p_dest,
+                               OPJ_UINT32 * p_data_written,
+                               OPJ_UINT32 p_max_len,
+                               opj_codestream_info_t *cstr_info,
+                               opj_tcd_marker_info_t* p_marker_info,
+                               OPJ_UINT32 p_tp_num,
+                               OPJ_INT32 p_tp_pos,
+                               OPJ_UINT32 p_pino,
+                               J2K_T2_MODE p_t2_mode,
+                               opj_event_mgr_t *p_manager)
+{
+    OPJ_BYTE *l_current_data = p_dest;
+    OPJ_UINT32 l_nb_bytes = 0;
+    OPJ_UINT32 compno;
+    OPJ_UINT32 poc;
+    opj_pi_iterator_t *l_pi = 00;
+    opj_pi_iterator_t *l_current_pi = 00;
+    opj_image_t *l_image = p_t2->image;
+    opj_cp_t *l_cp = p_t2->cp;
+    opj_tcp_t *l_tcp = &l_cp->tcps[p_tile_no];
+    OPJ_UINT32 pocno = (l_cp->rsiz == OPJ_PROFILE_CINEMA_4K) ? 2 : 1;
+    OPJ_UINT32 l_max_comp = l_cp->m_specific_param.m_enc.m_max_comp_size > 0 ?
+                            l_image->numcomps : 1;
+    OPJ_UINT32 l_nb_pocs = l_tcp->numpocs + 1;
+
+    l_pi = opj_pi_initialise_encode(l_image, l_cp, p_tile_no, p_t2_mode, p_manager);
+    if (!l_pi) {
+        return OPJ_FALSE;
+    }
+
+    * p_data_written = 0;
+
+    if (p_t2_mode == THRESH_CALC) { /* Calculating threshold */
+        l_current_pi = l_pi;
+
+        for (compno = 0; compno < l_max_comp; ++compno) {
+            OPJ_UINT32 l_comp_len = 0;
+            l_current_pi = l_pi;
+
+            for (poc = 0; poc < pocno ; ++poc) {
+                OPJ_UINT32 l_tp_num = compno;
+
+                /* TODO MSD : check why this function cannot fail (cf. v1) */
+                opj_pi_create_encode(l_pi, l_cp, p_tile_no, poc, l_tp_num, p_tp_pos, p_t2_mode);
+
+                if (l_current_pi->poc.prg == OPJ_PROG_UNKNOWN) {
+                    /* TODO ADE : add an error */
+                    opj_pi_destroy(l_pi, l_nb_pocs);
+                    return OPJ_FALSE;
+                }
+                while (opj_pi_next(l_current_pi)) {
+                    if (l_current_pi->layno < p_maxlayers) {
+                        l_nb_bytes = 0;
+
+                        if (! opj_t2_encode_packet(p_tile_no, p_tile, l_tcp, l_current_pi,
+                                                   l_current_data, &l_nb_bytes,
+                                                   p_max_len, cstr_info,
+                                                   p_t2_mode,
+                                                   p_manager)) {
+                            opj_pi_destroy(l_pi, l_nb_pocs);
+                            return OPJ_FALSE;
+                        }
+
+                        l_comp_len += l_nb_bytes;
+                        l_current_data += l_nb_bytes;
+                        p_max_len -= l_nb_bytes;
+
+                        * p_data_written += l_nb_bytes;
+                    }
+                }
+
+                if (l_cp->m_specific_param.m_enc.m_max_comp_size) {
+                    if (l_comp_len > l_cp->m_specific_param.m_enc.m_max_comp_size) {
+                        opj_pi_destroy(l_pi, l_nb_pocs);
+                        return OPJ_FALSE;
+                    }
+                }
+
+                ++l_current_pi;
+            }
+        }
+    } else { /* t2_mode == FINAL_PASS  */
+        opj_pi_create_encode(l_pi, l_cp, p_tile_no, p_pino, p_tp_num, p_tp_pos,
+                             p_t2_mode);
+
+        l_current_pi = &l_pi[p_pino];
+        if (l_current_pi->poc.prg == OPJ_PROG_UNKNOWN) {
+            /* TODO ADE : add an error */
+            opj_pi_destroy(l_pi, l_nb_pocs);
+            return OPJ_FALSE;
+        }
+
+        if (p_marker_info && p_marker_info->need_PLT) {
+            /* One time use intended */
+            assert(p_marker_info->packet_count == 0);
+            assert(p_marker_info->p_packet_size == NULL);
+
+            p_marker_info->p_packet_size = (OPJ_UINT32*) opj_malloc(
+                                               opj_get_encoding_packet_count(l_image, l_cp, p_tile_no) * sizeof(OPJ_UINT32));
+            if (p_marker_info->p_packet_size == NULL) {
+                opj_pi_destroy(l_pi, l_nb_pocs);
+                return OPJ_FALSE;
+            }
+        }
+
+        while (opj_pi_next(l_current_pi)) {
+            if (l_current_pi->layno < p_maxlayers) {
+                l_nb_bytes = 0;
+
+                if (! opj_t2_encode_packet(p_tile_no, p_tile, l_tcp, l_current_pi,
+                                           l_current_data, &l_nb_bytes, p_max_len,
+                                           cstr_info, p_t2_mode, p_manager)) {
+                    opj_pi_destroy(l_pi, l_nb_pocs);
+                    return OPJ_FALSE;
+                }
+
+                l_current_data += l_nb_bytes;
+                p_max_len -= l_nb_bytes;
+
+                * p_data_written += l_nb_bytes;
+
+                if (p_marker_info && p_marker_info->need_PLT) {
+                    p_marker_info->p_packet_size[p_marker_info->packet_count] = l_nb_bytes;
+                    p_marker_info->packet_count ++;
+                }
+
+                /* INDEX >> */
+                if (cstr_info) {
+                    if (cstr_info->index_write) {
+                        opj_tile_info_t *info_TL = &cstr_info->tile[p_tile_no];
+                        opj_packet_info_t *info_PK = &info_TL->packet[cstr_info->packno];
+                        if (!cstr_info->packno) {
+                            info_PK->start_pos = info_TL->end_header + 1;
+                        } else {
+                            info_PK->start_pos = ((l_cp->m_specific_param.m_enc.m_tp_on | l_tcp->POC) &&
+                                                  info_PK->start_pos) ? info_PK->start_pos : info_TL->packet[cstr_info->packno -
+                                                                            1].end_pos + 1;
+                        }
+                        info_PK->end_pos = info_PK->start_pos + l_nb_bytes - 1;
+                        info_PK->end_ph_pos += info_PK->start_pos -
+                                               1;  /* End of packet header which now only represents the distance
+                                                                                                                                                                                                                                                   to start of packet is incremented by value of start of packet*/
+                    }
+
+                    cstr_info->packno++;
+                }
+                /* << INDEX */
+                ++p_tile->packno;
+            }
+        }
+    }
+
+    opj_pi_destroy(l_pi, l_nb_pocs);
+
+    return OPJ_TRUE;
+}
+
+/* see issue 80 */
+#if 0
+#define JAS_FPRINTF fprintf
+#else
+/* issue 290 */
+static void opj_null_jas_fprintf(FILE* file, const char * format, ...)
+{
+    (void)file;
+    (void)format;
+}
+#define JAS_FPRINTF opj_null_jas_fprintf
+#endif
+
+OPJ_BOOL opj_t2_decode_packets(opj_tcd_t* tcd,
+                               opj_t2_t *p_t2,
+                               OPJ_UINT32 p_tile_no,
+                               opj_tcd_tile_t *p_tile,
+                               OPJ_BYTE *p_src,
+                               OPJ_UINT32 * p_data_read,
+                               OPJ_UINT32 p_max_len,
+                               opj_codestream_index_t *p_cstr_index,
+                               opj_event_mgr_t *p_manager)
+{
+    OPJ_BYTE *l_current_data = p_src;
+    opj_pi_iterator_t *l_pi = 00;
+    OPJ_UINT32 pino;
+    opj_image_t *l_image = p_t2->image;
+    opj_cp_t *l_cp = p_t2->cp;
+    opj_tcp_t *l_tcp = &(p_t2->cp->tcps[p_tile_no]);
+    OPJ_UINT32 l_nb_bytes_read;
+    OPJ_UINT32 l_nb_pocs = l_tcp->numpocs + 1;
+    opj_pi_iterator_t *l_current_pi = 00;
+#ifdef TODO_MSD
+    OPJ_UINT32 curtp = 0;
+    OPJ_UINT32 tp_start_packno;
+#endif
+    opj_packet_info_t *l_pack_info = 00;
+    opj_image_comp_t* l_img_comp = 00;
+
+    OPJ_ARG_NOT_USED(p_cstr_index);
+
+#ifdef TODO_MSD
+    if (p_cstr_index) {
+        l_pack_info = p_cstr_index->tile_index[p_tile_no].packet;
+    }
+#endif
+
+    /* create a packet iterator */
+    l_pi = opj_pi_create_decode(l_image, l_cp, p_tile_no, p_manager);
+    if (!l_pi) {
+        return OPJ_FALSE;
+    }
+
+
+    l_current_pi = l_pi;
+
+    for (pino = 0; pino <= l_tcp->numpocs; ++pino) {
+
+        /* if the resolution needed is too low, one dim of the tilec could be equal to zero
+         * and no packets are used to decode this resolution and
+         * l_current_pi->resno is always >= p_tile->comps[l_current_pi->compno].minimum_num_resolutions
+         * and no l_img_comp->resno_decoded are computed
+         */
+        OPJ_BOOL* first_pass_failed = NULL;
+
+        if (l_current_pi->poc.prg == OPJ_PROG_UNKNOWN) {
+            /* TODO ADE : add an error */
+            opj_pi_destroy(l_pi, l_nb_pocs);
+            return OPJ_FALSE;
+        }
+
+        first_pass_failed = (OPJ_BOOL*)opj_malloc(l_image->numcomps * sizeof(OPJ_BOOL));
+        if (!first_pass_failed) {
+            opj_pi_destroy(l_pi, l_nb_pocs);
+            return OPJ_FALSE;
+        }
+        memset(first_pass_failed, OPJ_TRUE, l_image->numcomps * sizeof(OPJ_BOOL));
+
+        while (opj_pi_next(l_current_pi)) {
+            OPJ_BOOL skip_packet = OPJ_FALSE;
+            JAS_FPRINTF(stderr,
+                        "packet offset=00000166 prg=%d cmptno=%02d rlvlno=%02d prcno=%03d lyrno=%02d\n\n",
+                        l_current_pi->poc.prg1, l_current_pi->compno, l_current_pi->resno,
+                        l_current_pi->precno, l_current_pi->layno);
+
+            /* If the packet layer is greater or equal than the maximum */
+            /* number of layers, skip the packet */
+            if (l_current_pi->layno >= l_tcp->num_layers_to_decode) {
+                skip_packet = OPJ_TRUE;
+            }
+            /* If the packet resolution number is greater than the minimum */
+            /* number of resolution allowed, skip the packet */
+            else if (l_current_pi->resno >=
+                     p_tile->comps[l_current_pi->compno].minimum_num_resolutions) {
+                skip_packet = OPJ_TRUE;
+            } else {
+                /* If no precincts of any band intersects the area of interest, */
+                /* skip the packet */
+                OPJ_UINT32 bandno;
+                opj_tcd_tilecomp_t *tilec = &p_tile->comps[l_current_pi->compno];
+                opj_tcd_resolution_t *res = &tilec->resolutions[l_current_pi->resno];
+
+                skip_packet = OPJ_TRUE;
+                for (bandno = 0; bandno < res->numbands; ++bandno) {
+                    opj_tcd_band_t* band = &res->bands[bandno];
+                    opj_tcd_precinct_t* prec = &band->precincts[l_current_pi->precno];
+
+                    if (opj_tcd_is_subband_area_of_interest(tcd,
+                                                            l_current_pi->compno,
+                                                            l_current_pi->resno,
+                                                            band->bandno,
+                                                            (OPJ_UINT32)prec->x0,
+                                                            (OPJ_UINT32)prec->y0,
+                                                            (OPJ_UINT32)prec->x1,
+                                                            (OPJ_UINT32)prec->y1)) {
+                        skip_packet = OPJ_FALSE;
+                        break;
+                    }
+                }
+                /*
+                                printf("packet cmptno=%02d rlvlno=%02d prcno=%03d lyrno=%02d -> %s\n",
+                                    l_current_pi->compno, l_current_pi->resno,
+                                    l_current_pi->precno, l_current_pi->layno, skip_packet ? "skipped" : "kept");
+                */
+            }
+            if (!skip_packet) {
+                l_nb_bytes_read = 0;
+
+                first_pass_failed[l_current_pi->compno] = OPJ_FALSE;
+
+                if (! opj_t2_decode_packet(p_t2, p_tile, l_tcp, l_current_pi, l_current_data,
+                                           &l_nb_bytes_read, p_max_len, l_pack_info, p_manager)) {
+                    opj_pi_destroy(l_pi, l_nb_pocs);
+                    opj_free(first_pass_failed);
+                    return OPJ_FALSE;
+                }
+
+                l_img_comp = &(l_image->comps[l_current_pi->compno]);
+                l_img_comp->resno_decoded = opj_uint_max(l_current_pi->resno,
+                                            l_img_comp->resno_decoded);
+            } else {
+                l_nb_bytes_read = 0;
+                if (! opj_t2_skip_packet(p_t2, p_tile, l_tcp, l_current_pi, l_current_data,
+                                         &l_nb_bytes_read, p_max_len, l_pack_info, p_manager)) {
+                    opj_pi_destroy(l_pi, l_nb_pocs);
+                    opj_free(first_pass_failed);
+                    return OPJ_FALSE;
+                }
+            }
+
+            if (first_pass_failed[l_current_pi->compno]) {
+                l_img_comp = &(l_image->comps[l_current_pi->compno]);
+                if (l_img_comp->resno_decoded == 0) {
+                    l_img_comp->resno_decoded =
+                        p_tile->comps[l_current_pi->compno].minimum_num_resolutions - 1;
+                }
+            }
+
+            l_current_data += l_nb_bytes_read;
+            p_max_len -= l_nb_bytes_read;
+
+            /* INDEX >> */
+#ifdef TODO_MSD
+            if (p_cstr_info) {
+                opj_tile_info_v2_t *info_TL = &p_cstr_info->tile[p_tile_no];
+                opj_packet_info_t *info_PK = &info_TL->packet[p_cstr_info->packno];
+                tp_start_packno = 0;
+                if (!p_cstr_info->packno) {
+                    info_PK->start_pos = info_TL->end_header + 1;
+                } else if (info_TL->packet[p_cstr_info->packno - 1].end_pos >=
+                           (OPJ_INT32)
+                           p_cstr_info->tile[p_tile_no].tp[curtp].tp_end_pos) { /* New tile part */
+                    info_TL->tp[curtp].tp_numpacks = p_cstr_info->packno -
+                                                     tp_start_packno; /* Number of packets in previous tile-part */
+                    tp_start_packno = p_cstr_info->packno;
+                    curtp++;
+                    info_PK->start_pos = p_cstr_info->tile[p_tile_no].tp[curtp].tp_end_header + 1;
+                } else {
+                    info_PK->start_pos = (l_cp->m_specific_param.m_enc.m_tp_on &&
+                                          info_PK->start_pos) ? info_PK->start_pos : info_TL->packet[p_cstr_info->packno -
+                                                                      1].end_pos + 1;
+                }
+                info_PK->end_pos = info_PK->start_pos + l_nb_bytes_read - 1;
+                info_PK->end_ph_pos += info_PK->start_pos -
+                                       1;  /* End of packet header which now only represents the distance */
+                ++p_cstr_info->packno;
+            }
+#endif
+            /* << INDEX */
+        }
+        ++l_current_pi;
+
+        opj_free(first_pass_failed);
+    }
+    /* INDEX >> */
+#ifdef TODO_MSD
+    if
+    (p_cstr_info) {
+        p_cstr_info->tile[p_tile_no].tp[curtp].tp_numpacks = p_cstr_info->packno -
+                tp_start_packno; /* Number of packets in last tile-part */
+    }
+#endif
+    /* << INDEX */
+
+    /* don't forget to release pi */
+    opj_pi_destroy(l_pi, l_nb_pocs);
+    *p_data_read = (OPJ_UINT32)(l_current_data - p_src);
+    return OPJ_TRUE;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/**
+ * Creates a Tier 2 handle
+ *
+ * @param       p_image         Source or destination image
+ * @param       p_cp            Image coding parameters.
+ * @return              a new T2 handle if successful, NULL otherwise.
+*/
+opj_t2_t* opj_t2_create(opj_image_t *p_image, opj_cp_t *p_cp)
+{
+    /* create the t2 structure */
+    opj_t2_t *l_t2 = (opj_t2_t*)opj_calloc(1, sizeof(opj_t2_t));
+    if (!l_t2) {
+        return NULL;
+    }
+
+    l_t2->image = p_image;
+    l_t2->cp = p_cp;
+
+    return l_t2;
+}
+
+void opj_t2_destroy(opj_t2_t *t2)
+{
+    if (t2) {
+        opj_free(t2);
+    }
+}
+
+static OPJ_BOOL opj_t2_decode_packet(opj_t2_t* p_t2,
+                                     opj_tcd_tile_t *p_tile,
+                                     opj_tcp_t *p_tcp,
+                                     opj_pi_iterator_t *p_pi,
+                                     OPJ_BYTE *p_src,
+                                     OPJ_UINT32 * p_data_read,
+                                     OPJ_UINT32 p_max_length,
+                                     opj_packet_info_t *p_pack_info,
+                                     opj_event_mgr_t *p_manager)
+{
+    OPJ_BOOL l_read_data;
+    OPJ_UINT32 l_nb_bytes_read = 0;
+    OPJ_UINT32 l_nb_total_bytes_read = 0;
+
+    *p_data_read = 0;
+
+    if (! opj_t2_read_packet_header(p_t2, p_tile, p_tcp, p_pi, &l_read_data, p_src,
+                                    &l_nb_bytes_read, p_max_length, p_pack_info, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    p_src += l_nb_bytes_read;
+    l_nb_total_bytes_read += l_nb_bytes_read;
+    p_max_length -= l_nb_bytes_read;
+
+    /* we should read data for the packet */
+    if (l_read_data) {
+        l_nb_bytes_read = 0;
+
+        if (! opj_t2_read_packet_data(p_t2, p_tile, p_pi, p_src, &l_nb_bytes_read,
+                                      p_max_length, p_pack_info, p_manager)) {
+            return OPJ_FALSE;
+        }
+
+        l_nb_total_bytes_read += l_nb_bytes_read;
+    }
+
+    *p_data_read = l_nb_total_bytes_read;
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_t2_encode_packet(OPJ_UINT32 tileno,
+                                     opj_tcd_tile_t * tile,
+                                     opj_tcp_t * tcp,
+                                     opj_pi_iterator_t *pi,
+                                     OPJ_BYTE *dest,
+                                     OPJ_UINT32 * p_data_written,
+                                     OPJ_UINT32 length,
+                                     opj_codestream_info_t *cstr_info,
+                                     J2K_T2_MODE p_t2_mode,
+                                     opj_event_mgr_t *p_manager)
+{
+    OPJ_UINT32 bandno, cblkno;
+    OPJ_BYTE* c = dest;
+    OPJ_UINT32 l_nb_bytes;
+    OPJ_UINT32 compno = pi->compno;     /* component value */
+    OPJ_UINT32 resno  = pi->resno;      /* resolution level value */
+    OPJ_UINT32 precno = pi->precno;     /* precinct value */
+    OPJ_UINT32 layno  = pi->layno;      /* quality layer value */
+    OPJ_UINT32 l_nb_blocks;
+    opj_tcd_band_t *band = 00;
+    opj_tcd_cblk_enc_t* cblk = 00;
+    opj_tcd_pass_t *pass = 00;
+
+    opj_tcd_tilecomp_t *tilec = &tile->comps[compno];
+    opj_tcd_resolution_t *res = &tilec->resolutions[resno];
+
+    opj_bio_t *bio = 00;    /* BIO component */
+#ifdef ENABLE_EMPTY_PACKET_OPTIMIZATION
+    OPJ_BOOL packet_empty = OPJ_TRUE;
+#else
+    OPJ_BOOL packet_empty = OPJ_FALSE;
+#endif
+
+#ifdef DEBUG_VERBOSE
+    if (p_t2_mode == FINAL_PASS) {
+        fprintf(stderr,
+                "encode packet compono=%d, resno=%d, precno=%d, layno=%d\n",
+                compno, resno, precno, layno);
+    }
+#endif
+
+    /* <SOP 0xff91> */
+    if (tcp->csty & J2K_CP_CSTY_SOP) {
+        if (length < 6) {
+            if (p_t2_mode == FINAL_PASS) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "opj_t2_encode_packet(): only %u bytes remaining in "
+                              "output buffer. %u needed.\n",
+                              length, 6);
+            }
+            return OPJ_FALSE;
+        }
+        c[0] = 255;
+        c[1] = 145;
+        c[2] = 0;
+        c[3] = 4;
+#if 0
+        c[4] = (tile->packno % 65536) / 256;
+        c[5] = (tile->packno % 65536) % 256;
+#else
+        c[4] = (tile->packno >> 8) & 0xff; /* packno is uint32_t */
+        c[5] = tile->packno & 0xff;
+#endif
+        c += 6;
+        length -= 6;
+    }
+    /* </SOP> */
+
+    if (!layno) {
+        band = res->bands;
+
+        for (bandno = 0; bandno < res->numbands; ++bandno, ++band) {
+            opj_tcd_precinct_t *prc;
+
+            /* Skip empty bands */
+            if (opj_tcd_is_band_empty(band)) {
+                continue;
+            }
+
+            /* Avoid out of bounds access of https://github.com/uclouvain/openjpeg/issues/1294 */
+            /* but likely not a proper fix. */
+            if (precno >= res->pw * res->ph) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "opj_t2_encode_packet(): accessing precno=%u >= %u\n",
+                              precno, res->pw * res->ph);
+                return OPJ_FALSE;
+            }
+
+            prc = &band->precincts[precno];
+            opj_tgt_reset(prc->incltree);
+            opj_tgt_reset(prc->imsbtree);
+
+            l_nb_blocks = prc->cw * prc->ch;
+            for (cblkno = 0; cblkno < l_nb_blocks; ++cblkno) {
+                cblk = &prc->cblks.enc[cblkno];
+
+                cblk->numpasses = 0;
+                opj_tgt_setvalue(prc->imsbtree, cblkno, band->numbps - (OPJ_INT32)cblk->numbps);
+            }
+        }
+    }
+
+    bio = opj_bio_create();
+    if (!bio) {
+        /* FIXME event manager error callback */
+        return OPJ_FALSE;
+    }
+    opj_bio_init_enc(bio, c, length);
+
+#ifdef ENABLE_EMPTY_PACKET_OPTIMIZATION
+    /* WARNING: this code branch is disabled, since it has been reported that */
+    /* such packets cause decoding issues with cinema J2K hardware */
+    /* decoders: https://groups.google.com/forum/#!topic/openjpeg/M7M_fLX_Bco */
+
+    /* Check if the packet is empty */
+    /* Note: we could also skip that step and always write a packet header */
+    band = res->bands;
+    for (bandno = 0; bandno < res->numbands; ++bandno, ++band) {
+        opj_tcd_precinct_t *prc;
+        /* Skip empty bands */
+        if (opj_tcd_is_band_empty(band)) {
+            continue;
+        }
+
+        prc = &band->precincts[precno];
+        l_nb_blocks = prc->cw * prc->ch;
+        cblk = prc->cblks.enc;
+        for (cblkno = 0; cblkno < l_nb_blocks; cblkno++, ++cblk) {
+            opj_tcd_layer_t *layer = &cblk->layers[layno];
+
+            /* if cblk not included, go to the next cblk  */
+            if (!layer->numpasses) {
+                continue;
+            }
+            packet_empty = OPJ_FALSE;
+            break;
+        }
+        if (!packet_empty) {
+            break;
+        }
+    }
+#endif
+    opj_bio_write(bio, packet_empty ? 0 : 1, 1);           /* Empty header bit */
+
+    /* Writing Packet header */
+    band = res->bands;
+    for (bandno = 0; !packet_empty &&
+            bandno < res->numbands; ++bandno, ++band)      {
+        opj_tcd_precinct_t *prc;
+
+        /* Skip empty bands */
+        if (opj_tcd_is_band_empty(band)) {
+            continue;
+        }
+
+        /* Avoid out of bounds access of https://github.com/uclouvain/openjpeg/issues/1297 */
+        /* but likely not a proper fix. */
+        if (precno >= res->pw * res->ph) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "opj_t2_encode_packet(): accessing precno=%u >= %u\n",
+                          precno, res->pw * res->ph);
+            return OPJ_FALSE;
+        }
+
+        prc = &band->precincts[precno];
+        l_nb_blocks = prc->cw * prc->ch;
+        cblk = prc->cblks.enc;
+
+        for (cblkno = 0; cblkno < l_nb_blocks; ++cblkno) {
+            opj_tcd_layer_t *layer = &cblk->layers[layno];
+
+            if (!cblk->numpasses && layer->numpasses) {
+                opj_tgt_setvalue(prc->incltree, cblkno, (OPJ_INT32)layno);
+            }
+
+            ++cblk;
+        }
+
+        cblk = prc->cblks.enc;
+        for (cblkno = 0; cblkno < l_nb_blocks; cblkno++) {
+            opj_tcd_layer_t *layer = &cblk->layers[layno];
+            OPJ_UINT32 increment = 0;
+            OPJ_UINT32 nump = 0;
+            OPJ_UINT32 len = 0, passno;
+            OPJ_UINT32 l_nb_passes;
+
+            /* cblk inclusion bits */
+            if (!cblk->numpasses) {
+                opj_tgt_encode(bio, prc->incltree, cblkno, (OPJ_INT32)(layno + 1));
+            } else {
+                opj_bio_write(bio, layer->numpasses != 0, 1);
+            }
+
+            /* if cblk not included, go to the next cblk  */
+            if (!layer->numpasses) {
+                ++cblk;
+                continue;
+            }
+
+            /* if first instance of cblk --> zero bit-planes information */
+            if (!cblk->numpasses) {
+                cblk->numlenbits = 3;
+                opj_tgt_encode(bio, prc->imsbtree, cblkno, 999);
+            }
+
+            /* number of coding passes included */
+            opj_t2_putnumpasses(bio, layer->numpasses);
+            l_nb_passes = cblk->numpasses + layer->numpasses;
+            pass = cblk->passes +  cblk->numpasses;
+
+            /* computation of the increase of the length indicator and insertion in the header     */
+            for (passno = cblk->numpasses; passno < l_nb_passes; ++passno) {
+                ++nump;
+                len += pass->len;
+
+                if (pass->term || passno == (cblk->numpasses + layer->numpasses) - 1) {
+                    increment = (OPJ_UINT32)opj_int_max((OPJ_INT32)increment,
+                                                        opj_int_floorlog2((OPJ_INT32)len) + 1
+                                                        - ((OPJ_INT32)cblk->numlenbits + opj_int_floorlog2((OPJ_INT32)nump)));
+                    len = 0;
+                    nump = 0;
+                }
+
+                ++pass;
+            }
+            opj_t2_putcommacode(bio, (OPJ_INT32)increment);
+
+            /* computation of the new Length indicator */
+            cblk->numlenbits += increment;
+
+            pass = cblk->passes +  cblk->numpasses;
+            /* insertion of the codeword segment length */
+            for (passno = cblk->numpasses; passno < l_nb_passes; ++passno) {
+                nump++;
+                len += pass->len;
+
+                if (pass->term || passno == (cblk->numpasses + layer->numpasses) - 1) {
+                    opj_bio_write(bio, (OPJ_UINT32)len,
+                                  cblk->numlenbits + (OPJ_UINT32)opj_int_floorlog2((OPJ_INT32)nump));
+                    len = 0;
+                    nump = 0;
+                }
+                ++pass;
+            }
+
+            ++cblk;
+        }
+    }
+
+    if (!opj_bio_flush(bio)) {
+        opj_bio_destroy(bio);
+        return OPJ_FALSE;               /* modified to eliminate longjmp !! */
+    }
+
+    l_nb_bytes = (OPJ_UINT32)opj_bio_numbytes(bio);
+    c += l_nb_bytes;
+    length -= l_nb_bytes;
+
+    opj_bio_destroy(bio);
+
+    /* <EPH 0xff92> */
+    if (tcp->csty & J2K_CP_CSTY_EPH) {
+        if (length < 2) {
+            if (p_t2_mode == FINAL_PASS) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "opj_t2_encode_packet(): only %u bytes remaining in "
+                              "output buffer. %u needed.\n",
+                              length, 2);
+            }
+            return OPJ_FALSE;
+        }
+        c[0] = 255;
+        c[1] = 146;
+        c += 2;
+        length -= 2;
+    }
+    /* </EPH> */
+
+    /* << INDEX */
+    /* End of packet header position. Currently only represents the distance to start of packet
+       Will be updated later by incrementing with packet start value*/
+    if (cstr_info && cstr_info->index_write) {
+        opj_packet_info_t *info_PK = &cstr_info->tile[tileno].packet[cstr_info->packno];
+        info_PK->end_ph_pos = (OPJ_INT32)(c - dest);
+    }
+    /* INDEX >> */
+
+    /* Writing the packet body */
+    band = res->bands;
+    for (bandno = 0; !packet_empty && bandno < res->numbands; bandno++, ++band) {
+        opj_tcd_precinct_t *prc;
+
+        /* Skip empty bands */
+        if (opj_tcd_is_band_empty(band)) {
+            continue;
+        }
+
+        prc = &band->precincts[precno];
+        l_nb_blocks = prc->cw * prc->ch;
+        cblk = prc->cblks.enc;
+
+        for (cblkno = 0; cblkno < l_nb_blocks; ++cblkno) {
+            opj_tcd_layer_t *layer = &cblk->layers[layno];
+
+            if (!layer->numpasses) {
+                ++cblk;
+                continue;
+            }
+
+            if (layer->len > length) {
+                if (p_t2_mode == FINAL_PASS) {
+                    opj_event_msg(p_manager, EVT_ERROR,
+                                  "opj_t2_encode_packet(): only %u bytes remaining in "
+                                  "output buffer. %u needed.\n",
+                                  length, layer->len);
+                }
+                return OPJ_FALSE;
+            }
+
+            memcpy(c, layer->data, layer->len);
+            cblk->numpasses += layer->numpasses;
+            c += layer->len;
+            length -= layer->len;
+
+            /* << INDEX */
+            if (cstr_info && cstr_info->index_write) {
+                opj_packet_info_t *info_PK = &cstr_info->tile[tileno].packet[cstr_info->packno];
+                info_PK->disto += layer->disto;
+                if (cstr_info->D_max < info_PK->disto) {
+                    cstr_info->D_max = info_PK->disto;
+                }
+            }
+
+            ++cblk;
+            /* INDEX >> */
+        }
+    }
+
+    assert(c >= dest);
+    * p_data_written += (OPJ_UINT32)(c - dest);
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_t2_skip_packet(opj_t2_t* p_t2,
+                                   opj_tcd_tile_t *p_tile,
+                                   opj_tcp_t *p_tcp,
+                                   opj_pi_iterator_t *p_pi,
+                                   OPJ_BYTE *p_src,
+                                   OPJ_UINT32 * p_data_read,
+                                   OPJ_UINT32 p_max_length,
+                                   opj_packet_info_t *p_pack_info,
+                                   opj_event_mgr_t *p_manager)
+{
+    OPJ_BOOL l_read_data;
+    OPJ_UINT32 l_nb_bytes_read = 0;
+    OPJ_UINT32 l_nb_total_bytes_read = 0;
+
+    *p_data_read = 0;
+
+    if (! opj_t2_read_packet_header(p_t2, p_tile, p_tcp, p_pi, &l_read_data, p_src,
+                                    &l_nb_bytes_read, p_max_length, p_pack_info, p_manager)) {
+        return OPJ_FALSE;
+    }
+
+    p_src += l_nb_bytes_read;
+    l_nb_total_bytes_read += l_nb_bytes_read;
+    p_max_length -= l_nb_bytes_read;
+
+    /* we should read data for the packet */
+    if (l_read_data) {
+        l_nb_bytes_read = 0;
+
+        if (! opj_t2_skip_packet_data(p_t2, p_tile, p_pi, &l_nb_bytes_read,
+                                      p_max_length, p_pack_info, p_manager)) {
+            return OPJ_FALSE;
+        }
+
+        l_nb_total_bytes_read += l_nb_bytes_read;
+    }
+    *p_data_read = l_nb_total_bytes_read;
+
+    return OPJ_TRUE;
+}
+
+
+static OPJ_BOOL opj_t2_read_packet_header(opj_t2_t* p_t2,
+        opj_tcd_tile_t *p_tile,
+        opj_tcp_t *p_tcp,
+        opj_pi_iterator_t *p_pi,
+        OPJ_BOOL * p_is_data_present,
+        OPJ_BYTE *p_src_data,
+        OPJ_UINT32 * p_data_read,
+        OPJ_UINT32 p_max_length,
+        opj_packet_info_t *p_pack_info,
+        opj_event_mgr_t *p_manager)
+
+{
+    /* loop */
+    OPJ_UINT32 bandno, cblkno;
+    OPJ_UINT32 l_nb_code_blocks;
+    OPJ_UINT32 l_remaining_length;
+    OPJ_UINT32 l_header_length;
+    OPJ_UINT32 * l_modified_length_ptr = 00;
+    OPJ_BYTE *l_current_data = p_src_data;
+    opj_cp_t *l_cp = p_t2->cp;
+    opj_bio_t *l_bio = 00;  /* BIO component */
+    opj_tcd_band_t *l_band = 00;
+    opj_tcd_cblk_dec_t* l_cblk = 00;
+    opj_tcd_resolution_t* l_res =
+        &p_tile->comps[p_pi->compno].resolutions[p_pi->resno];
+
+    OPJ_BYTE *l_header_data = 00;
+    OPJ_BYTE **l_header_data_start = 00;
+
+    OPJ_UINT32 l_present;
+
+    if (p_pi->layno == 0) {
+        l_band = l_res->bands;
+
+        /* reset tagtrees */
+        for (bandno = 0; bandno < l_res->numbands; ++bandno) {
+            if (!opj_tcd_is_band_empty(l_band)) {
+                opj_tcd_precinct_t *l_prc = &l_band->precincts[p_pi->precno];
+                if (!(p_pi->precno < (l_band->precincts_data_size / sizeof(
+                                          opj_tcd_precinct_t)))) {
+                    opj_event_msg(p_manager, EVT_ERROR, "Invalid precinct\n");
+                    return OPJ_FALSE;
+                }
+
+
+                opj_tgt_reset(l_prc->incltree);
+                opj_tgt_reset(l_prc->imsbtree);
+                l_cblk = l_prc->cblks.dec;
+
+                l_nb_code_blocks = l_prc->cw * l_prc->ch;
+                for (cblkno = 0; cblkno < l_nb_code_blocks; ++cblkno) {
+                    l_cblk->numsegs = 0;
+                    l_cblk->real_num_segs = 0;
+                    ++l_cblk;
+                }
+            }
+
+            ++l_band;
+        }
+    }
+
+    /* SOP markers */
+
+    if (p_tcp->csty & J2K_CP_CSTY_SOP) {
+        if (p_max_length < 6) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "Not enough space for expected SOP marker\n");
+        } else if ((*l_current_data) != 0xff || (*(l_current_data + 1) != 0x91)) {
+            opj_event_msg(p_manager, EVT_WARNING, "Expected SOP marker\n");
+        } else {
+            l_current_data += 6;
+        }
+
+        /** TODO : check the Nsop value */
+    }
+
+    /*
+    When the marker PPT/PPM is used the packet header are store in PPT/PPM marker
+    This part deal with this characteristic
+    step 1: Read packet header in the saved structure
+    step 2: Return to codestream for decoding
+    */
+
+    l_bio = opj_bio_create();
+    if (! l_bio) {
+        return OPJ_FALSE;
+    }
+
+    if (l_cp->ppm == 1) { /* PPM */
+        l_header_data_start = &l_cp->ppm_data;
+        l_header_data = *l_header_data_start;
+        l_modified_length_ptr = &(l_cp->ppm_len);
+
+    } else if (p_tcp->ppt == 1) { /* PPT */
+        l_header_data_start = &(p_tcp->ppt_data);
+        l_header_data = *l_header_data_start;
+        l_modified_length_ptr = &(p_tcp->ppt_len);
+    } else { /* Normal Case */
+        l_header_data_start = &(l_current_data);
+        l_header_data = *l_header_data_start;
+        l_remaining_length = (OPJ_UINT32)(p_src_data + p_max_length - l_header_data);
+        l_modified_length_ptr = &(l_remaining_length);
+    }
+
+    opj_bio_init_dec(l_bio, l_header_data, *l_modified_length_ptr);
+
+    l_present = opj_bio_read(l_bio, 1);
+    JAS_FPRINTF(stderr, "present=%d \n", l_present);
+    if (!l_present) {
+        /* TODO MSD: no test to control the output of this function*/
+        opj_bio_inalign(l_bio);
+        l_header_data += opj_bio_numbytes(l_bio);
+        opj_bio_destroy(l_bio);
+
+        /* EPH markers */
+        if (p_tcp->csty & J2K_CP_CSTY_EPH) {
+            if ((*l_modified_length_ptr - (OPJ_UINT32)(l_header_data -
+                    *l_header_data_start)) < 2U) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "Not enough space for expected EPH marker\n");
+            } else if ((*l_header_data) != 0xff || (*(l_header_data + 1) != 0x92)) {
+                opj_event_msg(p_manager, EVT_WARNING, "Expected EPH marker\n");
+            } else {
+                l_header_data += 2;
+            }
+        }
+
+        l_header_length = (OPJ_UINT32)(l_header_data - *l_header_data_start);
+        *l_modified_length_ptr -= l_header_length;
+        *l_header_data_start += l_header_length;
+
+        /* << INDEX */
+        /* End of packet header position. Currently only represents the distance to start of packet
+           Will be updated later by incrementing with packet start value */
+        if (p_pack_info) {
+            p_pack_info->end_ph_pos = (OPJ_INT32)(l_current_data - p_src_data);
+        }
+        /* INDEX >> */
+
+        * p_is_data_present = OPJ_FALSE;
+        *p_data_read = (OPJ_UINT32)(l_current_data - p_src_data);
+        return OPJ_TRUE;
+    }
+
+    l_band = l_res->bands;
+    for (bandno = 0; bandno < l_res->numbands; ++bandno, ++l_band) {
+        opj_tcd_precinct_t *l_prc = &(l_band->precincts[p_pi->precno]);
+
+        if (opj_tcd_is_band_empty(l_band)) {
+            continue;
+        }
+
+        l_nb_code_blocks = l_prc->cw * l_prc->ch;
+        l_cblk = l_prc->cblks.dec;
+        for (cblkno = 0; cblkno < l_nb_code_blocks; cblkno++) {
+            OPJ_UINT32 l_included, l_increment, l_segno;
+            OPJ_INT32 n;
+
+            /* if cblk not yet included before --> inclusion tagtree */
+            if (!l_cblk->numsegs) {
+                l_included = opj_tgt_decode(l_bio, l_prc->incltree, cblkno,
+                                            (OPJ_INT32)(p_pi->layno + 1));
+                /* else one bit */
+            } else {
+                l_included = opj_bio_read(l_bio, 1);
+            }
+
+            /* if cblk not included */
+            if (!l_included) {
+                l_cblk->numnewpasses = 0;
+                ++l_cblk;
+                JAS_FPRINTF(stderr, "included=%d \n", l_included);
+                continue;
+            }
+
+            /* if cblk not yet included --> zero-bitplane tagtree */
+            if (!l_cblk->numsegs) {
+                OPJ_UINT32 i = 0;
+
+                while (!opj_tgt_decode(l_bio, l_prc->imsbtree, cblkno, (OPJ_INT32)i)) {
+                    ++i;
+                }
+
+                l_cblk->Mb = (OPJ_UINT32)l_band->numbps;
+                l_cblk->numbps = (OPJ_UINT32)l_band->numbps + 1 - i;
+                l_cblk->numlenbits = 3;
+            }
+
+            /* number of coding passes */
+            l_cblk->numnewpasses = opj_t2_getnumpasses(l_bio);
+            l_increment = opj_t2_getcommacode(l_bio);
+
+            /* length indicator increment */
+            l_cblk->numlenbits += l_increment;
+            l_segno = 0;
+
+            if (!l_cblk->numsegs) {
+                if (! opj_t2_init_seg(l_cblk, l_segno, p_tcp->tccps[p_pi->compno].cblksty, 1)) {
+                    opj_bio_destroy(l_bio);
+                    return OPJ_FALSE;
+                }
+            } else {
+                l_segno = l_cblk->numsegs - 1;
+                if (l_cblk->segs[l_segno].numpasses == l_cblk->segs[l_segno].maxpasses) {
+                    ++l_segno;
+                    if (! opj_t2_init_seg(l_cblk, l_segno, p_tcp->tccps[p_pi->compno].cblksty, 0)) {
+                        opj_bio_destroy(l_bio);
+                        return OPJ_FALSE;
+                    }
+                }
+            }
+            n = (OPJ_INT32)l_cblk->numnewpasses;
+
+            if ((p_tcp->tccps[p_pi->compno].cblksty & J2K_CCP_CBLKSTY_HT) != 0)
+                do {
+                    OPJ_UINT32 bit_number;
+                    l_cblk->segs[l_segno].numnewpasses = l_segno == 0 ? 1 : (OPJ_UINT32)n;
+                    bit_number = l_cblk->numlenbits + opj_uint_floorlog2(
+                                     l_cblk->segs[l_segno].numnewpasses);
+                    if (bit_number > 32) {
+                        opj_event_msg(p_manager, EVT_ERROR,
+                                      "Invalid bit number %d in opj_t2_read_packet_header()\n",
+                                      bit_number);
+                        opj_bio_destroy(l_bio);
+                        return OPJ_FALSE;
+                    }
+                    l_cblk->segs[l_segno].newlen = opj_bio_read(l_bio, bit_number);
+                    JAS_FPRINTF(stderr, "included=%d numnewpasses=%d increment=%d len=%d \n",
+                                l_included, l_cblk->segs[l_segno].numnewpasses, l_increment,
+                                l_cblk->segs[l_segno].newlen);
+
+                    n -= (OPJ_INT32)l_cblk->segs[l_segno].numnewpasses;
+                    if (n > 0) {
+                        ++l_segno;
+
+                        if (! opj_t2_init_seg(l_cblk, l_segno, p_tcp->tccps[p_pi->compno].cblksty, 0)) {
+                            opj_bio_destroy(l_bio);
+                            return OPJ_FALSE;
+                        }
+                    }
+                } while (n > 0);
+            else
+                do {
+                    OPJ_UINT32 bit_number;
+                    l_cblk->segs[l_segno].numnewpasses = (OPJ_UINT32)opj_int_min((OPJ_INT32)(
+                            l_cblk->segs[l_segno].maxpasses - l_cblk->segs[l_segno].numpasses), n);
+                    bit_number = l_cblk->numlenbits + opj_uint_floorlog2(
+                                     l_cblk->segs[l_segno].numnewpasses);
+                    if (bit_number > 32) {
+                        opj_event_msg(p_manager, EVT_ERROR,
+                                      "Invalid bit number %d in opj_t2_read_packet_header()\n",
+                                      bit_number);
+                        opj_bio_destroy(l_bio);
+                        return OPJ_FALSE;
+                    }
+                    l_cblk->segs[l_segno].newlen = opj_bio_read(l_bio, bit_number);
+                    JAS_FPRINTF(stderr, "included=%d numnewpasses=%d increment=%d len=%d \n",
+                                l_included, l_cblk->segs[l_segno].numnewpasses, l_increment,
+                                l_cblk->segs[l_segno].newlen);
+
+                    n -= (OPJ_INT32)l_cblk->segs[l_segno].numnewpasses;
+                    if (n > 0) {
+                        ++l_segno;
+
+                        if (! opj_t2_init_seg(l_cblk, l_segno, p_tcp->tccps[p_pi->compno].cblksty, 0)) {
+                            opj_bio_destroy(l_bio);
+                            return OPJ_FALSE;
+                        }
+                    }
+                } while (n > 0);
+
+            ++l_cblk;
+        }
+    }
+
+    if (!opj_bio_inalign(l_bio)) {
+        opj_bio_destroy(l_bio);
+        return OPJ_FALSE;
+    }
+
+    l_header_data += opj_bio_numbytes(l_bio);
+    opj_bio_destroy(l_bio);
+
+    /* EPH markers */
+    if (p_tcp->csty & J2K_CP_CSTY_EPH) {
+        if ((*l_modified_length_ptr - (OPJ_UINT32)(l_header_data -
+                *l_header_data_start)) < 2U) {
+            opj_event_msg(p_manager, EVT_WARNING,
+                          "Not enough space for expected EPH marker\n");
+        } else if ((*l_header_data) != 0xff || (*(l_header_data + 1) != 0x92)) {
+            opj_event_msg(p_manager, EVT_WARNING, "Expected EPH marker\n");
+        } else {
+            l_header_data += 2;
+        }
+    }
+
+    l_header_length = (OPJ_UINT32)(l_header_data - *l_header_data_start);
+    JAS_FPRINTF(stderr, "hdrlen=%d \n", l_header_length);
+    JAS_FPRINTF(stderr, "packet body\n");
+    *l_modified_length_ptr -= l_header_length;
+    *l_header_data_start += l_header_length;
+
+    /* << INDEX */
+    /* End of packet header position. Currently only represents the distance to start of packet
+     Will be updated later by incrementing with packet start value */
+    if (p_pack_info) {
+        p_pack_info->end_ph_pos = (OPJ_INT32)(l_current_data - p_src_data);
+    }
+    /* INDEX >> */
+
+    *p_is_data_present = OPJ_TRUE;
+    *p_data_read = (OPJ_UINT32)(l_current_data - p_src_data);
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_t2_read_packet_data(opj_t2_t* p_t2,
+                                        opj_tcd_tile_t *p_tile,
+                                        opj_pi_iterator_t *p_pi,
+                                        OPJ_BYTE *p_src_data,
+                                        OPJ_UINT32 * p_data_read,
+                                        OPJ_UINT32 p_max_length,
+                                        opj_packet_info_t *pack_info,
+                                        opj_event_mgr_t* p_manager)
+{
+    OPJ_UINT32 bandno, cblkno;
+    OPJ_UINT32 l_nb_code_blocks;
+    OPJ_BYTE *l_current_data = p_src_data;
+    opj_tcd_band_t *l_band = 00;
+    opj_tcd_cblk_dec_t* l_cblk = 00;
+    opj_tcd_resolution_t* l_res =
+        &p_tile->comps[p_pi->compno].resolutions[p_pi->resno];
+    OPJ_BOOL partial_buffer = OPJ_FALSE;
+
+    OPJ_ARG_NOT_USED(p_t2);
+    OPJ_ARG_NOT_USED(pack_info);
+
+    l_band = l_res->bands;
+    for (bandno = 0; bandno < l_res->numbands; ++bandno) {
+        opj_tcd_precinct_t *l_prc = &l_band->precincts[p_pi->precno];
+
+        if ((l_band->x1 - l_band->x0 == 0) || (l_band->y1 - l_band->y0 == 0)) {
+            ++l_band;
+            continue;
+        }
+
+        l_nb_code_blocks = l_prc->cw * l_prc->ch;
+        l_cblk = l_prc->cblks.dec;
+
+        for (cblkno = 0; cblkno < l_nb_code_blocks; ++cblkno) {
+            opj_tcd_seg_t *l_seg = 00;
+
+            // if we have a partial data stream, set numchunks to zero
+            // since we have no data to actually decode.
+            if (partial_buffer) {
+                l_cblk->numchunks = 0;
+            }
+
+            if (!l_cblk->numnewpasses) {
+                /* nothing to do */
+                ++l_cblk;
+                continue;
+            }
+
+            if (!l_cblk->numsegs) {
+                l_seg = l_cblk->segs;
+                ++l_cblk->numsegs;
+            } else {
+                l_seg = &l_cblk->segs[l_cblk->numsegs - 1];
+
+                if (l_seg->numpasses == l_seg->maxpasses) {
+                    ++l_seg;
+                    ++l_cblk->numsegs;
+                }
+            }
+
+            do {
+                /* Check possible overflow (on l_current_data only, assumes input args already checked) then size */
+                if ((((OPJ_SIZE_T)l_current_data + (OPJ_SIZE_T)l_seg->newlen) <
+                        (OPJ_SIZE_T)l_current_data) ||
+                        (l_current_data + l_seg->newlen > p_src_data + p_max_length) ||
+                        (partial_buffer)) {
+                    if (p_t2->cp->strict) {
+                        opj_event_msg(p_manager, EVT_ERROR,
+                                      "read: segment too long (%d) with max (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n",
+                                      l_seg->newlen, p_max_length, cblkno, p_pi->precno, bandno, p_pi->resno,
+                                      p_pi->compno);
+                        return OPJ_FALSE;
+                    } else {
+                        opj_event_msg(p_manager, EVT_WARNING,
+                                      "read: segment too long (%d) with max (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n",
+                                      l_seg->newlen, p_max_length, cblkno, p_pi->precno, bandno, p_pi->resno,
+                                      p_pi->compno);
+                        // skip this codeblock since it is a partial read
+                        partial_buffer = OPJ_TRUE;
+                        l_cblk->numchunks = 0;
+
+                        l_seg->numpasses += l_seg->numnewpasses;
+                        l_cblk->numnewpasses -= l_seg->numnewpasses;
+                        if (l_cblk->numnewpasses > 0) {
+                            ++l_seg;
+                            ++l_cblk->numsegs;
+                            break;
+                        }
+                        continue;
+                    }
+                }
+
+#ifdef USE_JPWL
+                /* we need here a j2k handle to verify if making a check to
+                the validity of cblocks parameters is selected from user (-W) */
+
+                /* let's check that we are not exceeding */
+                if ((l_cblk->len + l_seg->newlen) > 8192) {
+                    opj_event_msg(p_manager, EVT_WARNING,
+                                  "JPWL: segment too long (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n",
+                                  l_seg->newlen, cblkno, p_pi->precno, bandno, p_pi->resno, p_pi->compno);
+                    if (!JPWL_ASSUME) {
+                        opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
+                        return OPJ_FALSE;
+                    }
+                    l_seg->newlen = 8192 - l_cblk->len;
+                    opj_event_msg(p_manager, EVT_WARNING, "      - truncating segment to %d\n",
+                                  l_seg->newlen);
+                    break;
+                };
+
+#endif /* USE_JPWL */
+
+                if (l_cblk->numchunks == l_cblk->numchunksalloc) {
+                    OPJ_UINT32 l_numchunksalloc = l_cblk->numchunksalloc * 2 + 1;
+                    opj_tcd_seg_data_chunk_t* l_chunks =
+                        (opj_tcd_seg_data_chunk_t*)opj_realloc(l_cblk->chunks,
+                                l_numchunksalloc * sizeof(opj_tcd_seg_data_chunk_t));
+                    if (l_chunks == NULL) {
+                        opj_event_msg(p_manager, EVT_ERROR,
+                                      "cannot allocate opj_tcd_seg_data_chunk_t* array");
+                        return OPJ_FALSE;
+                    }
+                    l_cblk->chunks = l_chunks;
+                    l_cblk->numchunksalloc = l_numchunksalloc;
+                }
+
+                l_cblk->chunks[l_cblk->numchunks].data = l_current_data;
+                l_cblk->chunks[l_cblk->numchunks].len = l_seg->newlen;
+                l_cblk->numchunks ++;
+
+                l_current_data += l_seg->newlen;
+                l_seg->len += l_seg->newlen;
+                l_seg->numpasses += l_seg->numnewpasses;
+                l_cblk->numnewpasses -= l_seg->numnewpasses;
+
+                l_seg->real_num_passes = l_seg->numpasses;
+
+                if (l_cblk->numnewpasses > 0) {
+                    ++l_seg;
+                    ++l_cblk->numsegs;
+                }
+            } while (l_cblk->numnewpasses > 0);
+
+            l_cblk->real_num_segs = l_cblk->numsegs;
+            ++l_cblk;
+        } /* next code_block */
+
+        ++l_band;
+    }
+
+    // return the number of bytes read
+    if (partial_buffer) {
+        *(p_data_read) = p_max_length;
+    } else {
+        *(p_data_read) = (OPJ_UINT32)(l_current_data - p_src_data);
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_t2_skip_packet_data(opj_t2_t* p_t2,
+                                        opj_tcd_tile_t *p_tile,
+                                        opj_pi_iterator_t *p_pi,
+                                        OPJ_UINT32 * p_data_read,
+                                        OPJ_UINT32 p_max_length,
+                                        opj_packet_info_t *pack_info,
+                                        opj_event_mgr_t *p_manager)
+{
+    OPJ_UINT32 bandno, cblkno;
+    OPJ_UINT32 l_nb_code_blocks;
+    opj_tcd_band_t *l_band = 00;
+    opj_tcd_cblk_dec_t* l_cblk = 00;
+    opj_tcd_resolution_t* l_res =
+        &p_tile->comps[p_pi->compno].resolutions[p_pi->resno];
+
+    OPJ_ARG_NOT_USED(p_t2);
+    OPJ_ARG_NOT_USED(pack_info);
+
+    *p_data_read = 0;
+    l_band = l_res->bands;
+
+    for (bandno = 0; bandno < l_res->numbands; ++bandno) {
+        opj_tcd_precinct_t *l_prc = &l_band->precincts[p_pi->precno];
+
+        if ((l_band->x1 - l_band->x0 == 0) || (l_band->y1 - l_band->y0 == 0)) {
+            ++l_band;
+            continue;
+        }
+
+        l_nb_code_blocks = l_prc->cw * l_prc->ch;
+        l_cblk = l_prc->cblks.dec;
+
+        for (cblkno = 0; cblkno < l_nb_code_blocks; ++cblkno) {
+            opj_tcd_seg_t *l_seg = 00;
+
+            if (!l_cblk->numnewpasses) {
+                /* nothing to do */
+                ++l_cblk;
+                continue;
+            }
+
+            if (!l_cblk->numsegs) {
+                l_seg = l_cblk->segs;
+                ++l_cblk->numsegs;
+            } else {
+                l_seg = &l_cblk->segs[l_cblk->numsegs - 1];
+
+                if (l_seg->numpasses == l_seg->maxpasses) {
+                    ++l_seg;
+                    ++l_cblk->numsegs;
+                }
+            }
+
+            do {
+                /* Check possible overflow then size */
+                if (((*p_data_read + l_seg->newlen) < (*p_data_read)) ||
+                        ((*p_data_read + l_seg->newlen) > p_max_length)) {
+                    if (p_t2->cp->strict) {
+                        opj_event_msg(p_manager, EVT_ERROR,
+                                      "skip: segment too long (%d) with max (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n",
+                                      l_seg->newlen, p_max_length, cblkno, p_pi->precno, bandno, p_pi->resno,
+                                      p_pi->compno);
+                        return OPJ_FALSE;
+                    } else {
+                        opj_event_msg(p_manager, EVT_WARNING,
+                                      "skip: segment too long (%d) with max (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n",
+                                      l_seg->newlen, p_max_length, cblkno, p_pi->precno, bandno, p_pi->resno,
+                                      p_pi->compno);
+                    }
+                }
+
+#ifdef USE_JPWL
+                /* we need here a j2k handle to verify if making a check to
+                the validity of cblocks parameters is selected from user (-W) */
+
+                /* let's check that we are not exceeding */
+                if ((l_cblk->len + l_seg->newlen) > 8192) {
+                    opj_event_msg(p_manager, EVT_WARNING,
+                                  "JPWL: segment too long (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n",
+                                  l_seg->newlen, cblkno, p_pi->precno, bandno, p_pi->resno, p_pi->compno);
+                    if (!JPWL_ASSUME) {
+                        opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
+                        return -999;
+                    }
+                    l_seg->newlen = 8192 - l_cblk->len;
+                    opj_event_msg(p_manager, EVT_WARNING, "      - truncating segment to %d\n",
+                                  l_seg->newlen);
+                    break;
+                };
+
+#endif /* USE_JPWL */
+                JAS_FPRINTF(stderr, "p_data_read (%d) newlen (%d) \n", *p_data_read,
+                            l_seg->newlen);
+                *(p_data_read) += l_seg->newlen;
+
+                l_seg->numpasses += l_seg->numnewpasses;
+                l_cblk->numnewpasses -= l_seg->numnewpasses;
+                if (l_cblk->numnewpasses > 0) {
+                    ++l_seg;
+                    ++l_cblk->numsegs;
+                }
+            } while (l_cblk->numnewpasses > 0);
+
+            ++l_cblk;
+        }
+
+        ++l_band;
+    }
+
+    return OPJ_TRUE;
+}
+
+
+static OPJ_BOOL opj_t2_init_seg(opj_tcd_cblk_dec_t* cblk,
+                                OPJ_UINT32 index,
+                                OPJ_UINT32 cblksty,
+                                OPJ_UINT32 first)
+{
+    opj_tcd_seg_t* seg = 00;
+    OPJ_UINT32 l_nb_segs = index + 1;
+
+    if (l_nb_segs > cblk->m_current_max_segs) {
+        opj_tcd_seg_t* new_segs;
+        OPJ_UINT32 l_m_current_max_segs = cblk->m_current_max_segs +
+                                          OPJ_J2K_DEFAULT_NB_SEGS;
+
+        new_segs = (opj_tcd_seg_t*) opj_realloc(cblk->segs,
+                                                l_m_current_max_segs * sizeof(opj_tcd_seg_t));
+        if (! new_segs) {
+            /* opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to initialize segment %d\n", l_nb_segs); */
+            return OPJ_FALSE;
+        }
+        cblk->segs = new_segs;
+        memset(new_segs + cblk->m_current_max_segs,
+               0, OPJ_J2K_DEFAULT_NB_SEGS * sizeof(opj_tcd_seg_t));
+        cblk->m_current_max_segs = l_m_current_max_segs;
+    }
+
+    seg = &cblk->segs[index];
+    opj_tcd_reinit_segment(seg);
+
+    if (cblksty & J2K_CCP_CBLKSTY_TERMALL) {
+        seg->maxpasses = 1;
+    } else if (cblksty & J2K_CCP_CBLKSTY_LAZY) {
+        if (first) {
+            seg->maxpasses = 10;
+        } else {
+            seg->maxpasses = (((seg - 1)->maxpasses == 1) ||
+                              ((seg - 1)->maxpasses == 10)) ? 2 : 1;
+        }
+    } else {
+        /* See paragraph "B.10.6 Number of coding passes" of the standard.
+         * Probably that 109 must be interpreted a (Mb-1)*3 + 1 with Mb=37,
+         * Mb being the maximum number of bit-planes available for the
+         * representation of coefficients in the sub-band */
+        seg->maxpasses = 109;
+    }
+
+    return OPJ_TRUE;
+}
diff --git a/third_party/libopenjpeg/t2.h b/third_party/libopenjpeg/t2.h
new file mode 100644
index 0000000..becfa91
--- /dev/null
+++ b/third_party/libopenjpeg/t2.h
@@ -0,0 +1,142 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
+ * Copyright (c) 2012, CS Systemes d'Information, France
+ * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef OPJ_T2_H
+#define OPJ_T2_H
+/**
+@file t2.h
+@brief Implementation of a tier-2 coding (packetization of code-block data) (T2)
+
+*/
+
+/** @defgroup T2 T2 - Implementation of a tier-2 coding */
+/*@{*/
+
+/**
+Tier-2 coding
+*/
+typedef struct opj_t2 {
+
+    /** Encoding: pointer to the src image. Decoding: pointer to the dst image. */
+    opj_image_t *image;
+    /** pointer to the image coding parameters */
+    opj_cp_t *cp;
+} opj_t2_t;
+
+/** @name Exported functions */
+/*@{*/
+/* ----------------------------------------------------------------------- */
+
+/**
+Encode the packets of a tile to a destination buffer
+@param t2               T2 handle
+@param tileno           number of the tile encoded
+@param tile             the tile for which to write the packets
+@param maxlayers        maximum number of layers
+@param dest             the destination buffer
+@param p_data_written   FIXME DOC
+@param len              the length of the destination buffer
+@param cstr_info        Codestream information structure
+@param p_marker_info    Marker information structure
+@param tpnum            Tile part number of the current tile
+@param tppos            The position of the tile part flag in the progression order
+@param pino             FIXME DOC
+@param t2_mode          If == THRESH_CALC In Threshold calculation ,If == FINAL_PASS Final pass
+@param p_manager        the user event manager
+*/
+OPJ_BOOL opj_t2_encode_packets(opj_t2_t* t2,
+                               OPJ_UINT32 tileno,
+                               opj_tcd_tile_t *tile,
+                               OPJ_UINT32 maxlayers,
+                               OPJ_BYTE *dest,
+                               OPJ_UINT32 * p_data_written,
+                               OPJ_UINT32 len,
+                               opj_codestream_info_t *cstr_info,
+                               opj_tcd_marker_info_t* p_marker_info,
+                               OPJ_UINT32 tpnum,
+                               OPJ_INT32 tppos,
+                               OPJ_UINT32 pino,
+                               J2K_T2_MODE t2_mode,
+                               opj_event_mgr_t *p_manager);
+
+/**
+Decode the packets of a tile from a source buffer
+@param tcd TCD handle
+@param t2 T2 handle
+@param tileno number that identifies the tile for which to decode the packets
+@param tile tile for which to decode the packets
+@param src         FIXME DOC
+@param p_data_read the source buffer
+@param len length of the source buffer
+@param cstr_info   FIXME DOC
+@param p_manager the user event manager
+
+@return FIXME DOC
+ */
+OPJ_BOOL opj_t2_decode_packets(opj_tcd_t* tcd,
+                               opj_t2_t *t2,
+                               OPJ_UINT32 tileno,
+                               opj_tcd_tile_t *tile,
+                               OPJ_BYTE *src,
+                               OPJ_UINT32 * p_data_read,
+                               OPJ_UINT32 len,
+                               opj_codestream_index_t *cstr_info,
+                               opj_event_mgr_t *p_manager);
+
+/**
+ * Creates a Tier 2 handle
+ *
+ * @param   p_image     Source or destination image
+ * @param   p_cp        Image coding parameters.
+ * @return      a new T2 handle if successful, NULL otherwise.
+*/
+opj_t2_t* opj_t2_create(opj_image_t *p_image, opj_cp_t *p_cp);
+
+/**
+Destroy a T2 handle
+@param t2 T2 handle to destroy
+*/
+void opj_t2_destroy(opj_t2_t *t2);
+
+/* ----------------------------------------------------------------------- */
+/*@}*/
+
+/*@}*/
+
+#endif /* OPJ_T2_H */
diff --git a/third_party/libopenjpeg/tcd.c b/third_party/libopenjpeg/tcd.c
new file mode 100644
index 0000000..b9f5714
--- /dev/null
+++ b/third_party/libopenjpeg/tcd.c
@@ -0,0 +1,2867 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2006-2007, Parvatha Elangovan
+ * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
+ * Copyright (c) 2012, CS Systemes d'Information, France
+ * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "opj_includes.h"
+#include "opj_common.h"
+
+/* ----------------------------------------------------------------------- */
+
+/* TODO MSD: */
+#ifdef TODO_MSD
+void tcd_dump(FILE *fd, opj_tcd_t *tcd, opj_tcd_image_t * img)
+{
+    int tileno, compno, resno, bandno, precno;/*, cblkno;*/
+
+    fprintf(fd, "image {\n");
+    fprintf(fd, "  tw=%d, th=%d x0=%d x1=%d y0=%d y1=%d\n",
+            img->tw, img->th, tcd->image->x0, tcd->image->x1, tcd->image->y0,
+            tcd->image->y1);
+
+    for (tileno = 0; tileno < img->th * img->tw; tileno++) {
+        opj_tcd_tile_t *tile = &tcd->tcd_image->tiles[tileno];
+        fprintf(fd, "  tile {\n");
+        fprintf(fd, "    x0=%d, y0=%d, x1=%d, y1=%d, numcomps=%d\n",
+                tile->x0, tile->y0, tile->x1, tile->y1, tile->numcomps);
+        for (compno = 0; compno < tile->numcomps; compno++) {
+            opj_tcd_tilecomp_t *tilec = &tile->comps[compno];
+            fprintf(fd, "    tilec {\n");
+            fprintf(fd,
+                    "      x0=%d, y0=%d, x1=%d, y1=%d, numresolutions=%d\n",
+                    tilec->x0, tilec->y0, tilec->x1, tilec->y1, tilec->numresolutions);
+            for (resno = 0; resno < tilec->numresolutions; resno++) {
+                opj_tcd_resolution_t *res = &tilec->resolutions[resno];
+                fprintf(fd, "\n   res {\n");
+                fprintf(fd,
+                        "          x0=%d, y0=%d, x1=%d, y1=%d, pw=%d, ph=%d, numbands=%d\n",
+                        res->x0, res->y0, res->x1, res->y1, res->pw, res->ph, res->numbands);
+                for (bandno = 0; bandno < res->numbands; bandno++) {
+                    opj_tcd_band_t *band = &res->bands[bandno];
+                    fprintf(fd, "        band {\n");
+                    fprintf(fd,
+                            "          x0=%d, y0=%d, x1=%d, y1=%d, stepsize=%f, numbps=%d\n",
+                            band->x0, band->y0, band->x1, band->y1, band->stepsize, band->numbps);
+                    for (precno = 0; precno < res->pw * res->ph; precno++) {
+                        opj_tcd_precinct_t *prec = &band->precincts[precno];
+                        fprintf(fd, "          prec {\n");
+                        fprintf(fd,
+                                "            x0=%d, y0=%d, x1=%d, y1=%d, cw=%d, ch=%d\n",
+                                prec->x0, prec->y0, prec->x1, prec->y1, prec->cw, prec->ch);
+                        /*
+                        for (cblkno = 0; cblkno < prec->cw * prec->ch; cblkno++) {
+                                opj_tcd_cblk_t *cblk = &prec->cblks[cblkno];
+                                fprintf(fd, "            cblk {\n");
+                                fprintf(fd,
+                                        "              x0=%d, y0=%d, x1=%d, y1=%d\n",
+                                        cblk->x0, cblk->y0, cblk->x1, cblk->y1);
+                                fprintf(fd, "            }\n");
+                        }
+                        */
+                        fprintf(fd, "          }\n");
+                    }
+                    fprintf(fd, "        }\n");
+                }
+                fprintf(fd, "      }\n");
+            }
+            fprintf(fd, "    }\n");
+        }
+        fprintf(fd, "  }\n");
+    }
+    fprintf(fd, "}\n");
+}
+#endif
+
+/**
+ * Initializes tile coding/decoding
+ */
+static INLINE OPJ_BOOL opj_tcd_init_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
+        OPJ_BOOL isEncoder, OPJ_SIZE_T sizeof_block,
+        opj_event_mgr_t* manager);
+
+/**
+* Allocates memory for a decoding code block.
+*/
+static OPJ_BOOL opj_tcd_code_block_dec_allocate(opj_tcd_cblk_dec_t *
+        p_code_block);
+
+/**
+ * Deallocates the decoding data of the given precinct.
+ */
+static void opj_tcd_code_block_dec_deallocate(opj_tcd_precinct_t * p_precinct);
+
+/**
+ * Allocates memory for an encoding code block (but not data).
+ */
+static OPJ_BOOL opj_tcd_code_block_enc_allocate(opj_tcd_cblk_enc_t *
+        p_code_block);
+
+/**
+ * Allocates data for an encoding code block
+ */
+static OPJ_BOOL opj_tcd_code_block_enc_allocate_data(opj_tcd_cblk_enc_t *
+        p_code_block);
+
+/**
+ * Deallocates the encoding data of the given precinct.
+ */
+static void opj_tcd_code_block_enc_deallocate(opj_tcd_precinct_t * p_precinct);
+
+
+/**
+Free the memory allocated for encoding
+@param tcd TCD handle
+*/
+static void opj_tcd_free_tile(opj_tcd_t *tcd);
+
+
+static OPJ_BOOL opj_tcd_t2_decode(opj_tcd_t *p_tcd,
+                                  OPJ_BYTE * p_src_data,
+                                  OPJ_UINT32 * p_data_read,
+                                  OPJ_UINT32 p_max_src_size,
+                                  opj_codestream_index_t *p_cstr_index,
+                                  opj_event_mgr_t *p_manager);
+
+static OPJ_BOOL opj_tcd_t1_decode(opj_tcd_t *p_tcd,
+                                  opj_event_mgr_t *p_manager);
+
+static OPJ_BOOL opj_tcd_dwt_decode(opj_tcd_t *p_tcd);
+
+static OPJ_BOOL opj_tcd_mct_decode(opj_tcd_t *p_tcd,
+                                   opj_event_mgr_t *p_manager);
+
+static OPJ_BOOL opj_tcd_dc_level_shift_decode(opj_tcd_t *p_tcd);
+
+
+static OPJ_BOOL opj_tcd_dc_level_shift_encode(opj_tcd_t *p_tcd);
+
+static OPJ_BOOL opj_tcd_mct_encode(opj_tcd_t *p_tcd);
+
+static OPJ_BOOL opj_tcd_dwt_encode(opj_tcd_t *p_tcd);
+
+static OPJ_BOOL opj_tcd_t1_encode(opj_tcd_t *p_tcd);
+
+static OPJ_BOOL opj_tcd_t2_encode(opj_tcd_t *p_tcd,
+                                  OPJ_BYTE * p_dest_data,
+                                  OPJ_UINT32 * p_data_written,
+                                  OPJ_UINT32 p_max_dest_size,
+                                  opj_codestream_info_t *p_cstr_info,
+                                  opj_tcd_marker_info_t* p_marker_info,
+                                  opj_event_mgr_t *p_manager);
+
+static OPJ_BOOL opj_tcd_rate_allocate_encode(opj_tcd_t *p_tcd,
+        OPJ_BYTE * p_dest_data,
+        OPJ_UINT32 p_max_dest_size,
+        opj_codestream_info_t *p_cstr_info,
+        opj_event_mgr_t *p_manager);
+
+
+static OPJ_BOOL opj_tcd_is_whole_tilecomp_decoding(opj_tcd_t *tcd,
+        OPJ_UINT32 compno);
+
+/* ----------------------------------------------------------------------- */
+
+/**
+Create a new TCD handle
+*/
+opj_tcd_t* opj_tcd_create(OPJ_BOOL p_is_decoder)
+{
+    opj_tcd_t *l_tcd = 00;
+
+    /* create the tcd structure */
+    l_tcd = (opj_tcd_t*) opj_calloc(1, sizeof(opj_tcd_t));
+    if (!l_tcd) {
+        return 00;
+    }
+
+    l_tcd->m_is_decoder = p_is_decoder ? 1 : 0;
+
+    l_tcd->tcd_image = (opj_tcd_image_t*)opj_calloc(1, sizeof(opj_tcd_image_t));
+    if (!l_tcd->tcd_image) {
+        opj_free(l_tcd);
+        return 00;
+    }
+
+    return l_tcd;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+void opj_tcd_rateallocate_fixed(opj_tcd_t *tcd)
+{
+    OPJ_UINT32 layno;
+
+    for (layno = 0; layno < tcd->tcp->numlayers; layno++) {
+        opj_tcd_makelayer_fixed(tcd, layno, 1);
+    }
+}
+
+
+void opj_tcd_makelayer(opj_tcd_t *tcd,
+                       OPJ_UINT32 layno,
+                       OPJ_FLOAT64 thresh,
+                       OPJ_UINT32 final)
+{
+    OPJ_UINT32 compno, resno, bandno, precno, cblkno;
+    OPJ_UINT32 passno;
+
+    opj_tcd_tile_t *tcd_tile = tcd->tcd_image->tiles;
+
+    tcd_tile->distolayer[layno] = 0;        /* fixed_quality */
+
+    for (compno = 0; compno < tcd_tile->numcomps; compno++) {
+        opj_tcd_tilecomp_t *tilec = &tcd_tile->comps[compno];
+
+        for (resno = 0; resno < tilec->numresolutions; resno++) {
+            opj_tcd_resolution_t *res = &tilec->resolutions[resno];
+
+            for (bandno = 0; bandno < res->numbands; bandno++) {
+                opj_tcd_band_t *band = &res->bands[bandno];
+
+                /* Skip empty bands */
+                if (opj_tcd_is_band_empty(band)) {
+                    continue;
+                }
+
+                for (precno = 0; precno < res->pw * res->ph; precno++) {
+                    opj_tcd_precinct_t *prc = &band->precincts[precno];
+
+                    for (cblkno = 0; cblkno < prc->cw * prc->ch; cblkno++) {
+                        opj_tcd_cblk_enc_t *cblk = &prc->cblks.enc[cblkno];
+                        opj_tcd_layer_t *layer = &cblk->layers[layno];
+                        OPJ_UINT32 n;
+
+                        if (layno == 0) {
+                            cblk->numpassesinlayers = 0;
+                        }
+
+                        n = cblk->numpassesinlayers;
+
+                        if (thresh < 0) {
+                            /* Special value to indicate to use all passes */
+                            n = cblk->totalpasses;
+                        } else {
+                            for (passno = cblk->numpassesinlayers; passno < cblk->totalpasses; passno++) {
+                                OPJ_UINT32 dr;
+                                OPJ_FLOAT64 dd;
+                                opj_tcd_pass_t *pass = &cblk->passes[passno];
+
+                                if (n == 0) {
+                                    dr = pass->rate;
+                                    dd = pass->distortiondec;
+                                } else {
+                                    dr = pass->rate - cblk->passes[n - 1].rate;
+                                    dd = pass->distortiondec - cblk->passes[n - 1].distortiondec;
+                                }
+
+                                if (!dr) {
+                                    if (dd != 0) {
+                                        n = passno + 1;
+                                    }
+                                    continue;
+                                }
+                                if (thresh - (dd / dr) <
+                                        DBL_EPSILON) { /* do not rely on float equality, check with DBL_EPSILON margin */
+                                    n = passno + 1;
+                                }
+                            }
+                        }
+
+                        layer->numpasses = n - cblk->numpassesinlayers;
+
+                        if (!layer->numpasses) {
+                            layer->disto = 0;
+                            continue;
+                        }
+
+                        if (cblk->numpassesinlayers == 0) {
+                            layer->len = cblk->passes[n - 1].rate;
+                            layer->data = cblk->data;
+                            layer->disto = cblk->passes[n - 1].distortiondec;
+                        } else {
+                            layer->len = cblk->passes[n - 1].rate - cblk->passes[cblk->numpassesinlayers -
+                                         1].rate;
+                            layer->data = cblk->data + cblk->passes[cblk->numpassesinlayers - 1].rate;
+                            layer->disto = cblk->passes[n - 1].distortiondec -
+                                           cblk->passes[cblk->numpassesinlayers - 1].distortiondec;
+                        }
+
+                        tcd_tile->distolayer[layno] += layer->disto;    /* fixed_quality */
+
+                        if (final) {
+                            cblk->numpassesinlayers = n;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+void opj_tcd_makelayer_fixed(opj_tcd_t *tcd, OPJ_UINT32 layno,
+                             OPJ_UINT32 final)
+{
+    OPJ_UINT32 compno, resno, bandno, precno, cblkno;
+    OPJ_INT32 value;                        /*, matrice[tcd_tcp->numlayers][tcd_tile->comps[0].numresolutions][3]; */
+    OPJ_INT32 matrice[10][10][3];
+    OPJ_UINT32 i, j, k;
+
+    opj_cp_t *cp = tcd->cp;
+    opj_tcd_tile_t *tcd_tile = tcd->tcd_image->tiles;
+    opj_tcp_t *tcd_tcp = tcd->tcp;
+
+    for (compno = 0; compno < tcd_tile->numcomps; compno++) {
+        opj_tcd_tilecomp_t *tilec = &tcd_tile->comps[compno];
+
+        for (i = 0; i < tcd_tcp->numlayers; i++) {
+            for (j = 0; j < tilec->numresolutions; j++) {
+                for (k = 0; k < 3; k++) {
+                    matrice[i][j][k] =
+                        (OPJ_INT32)((OPJ_FLOAT32)cp->m_specific_param.m_enc.m_matrice[i *
+                                      tilec->numresolutions * 3 + j * 3 + k]
+                                    * (OPJ_FLOAT32)(tcd->image->comps[compno].prec / 16.0));
+                }
+            }
+        }
+
+        for (resno = 0; resno < tilec->numresolutions; resno++) {
+            opj_tcd_resolution_t *res = &tilec->resolutions[resno];
+
+            for (bandno = 0; bandno < res->numbands; bandno++) {
+                opj_tcd_band_t *band = &res->bands[bandno];
+
+                /* Skip empty bands */
+                if (opj_tcd_is_band_empty(band)) {
+                    continue;
+                }
+
+                for (precno = 0; precno < res->pw * res->ph; precno++) {
+                    opj_tcd_precinct_t *prc = &band->precincts[precno];
+
+                    for (cblkno = 0; cblkno < prc->cw * prc->ch; cblkno++) {
+                        opj_tcd_cblk_enc_t *cblk = &prc->cblks.enc[cblkno];
+                        opj_tcd_layer_t *layer = &cblk->layers[layno];
+                        OPJ_UINT32 n;
+                        OPJ_INT32 imsb = (OPJ_INT32)(tcd->image->comps[compno].prec -
+                                                     cblk->numbps); /* number of bit-plan equal to zero */
+
+                        /* Correction of the matrix of coefficient to include the IMSB information */
+                        if (layno == 0) {
+                            value = matrice[layno][resno][bandno];
+                            if (imsb >= value) {
+                                value = 0;
+                            } else {
+                                value -= imsb;
+                            }
+                        } else {
+                            value = matrice[layno][resno][bandno] - matrice[layno - 1][resno][bandno];
+                            if (imsb >= matrice[layno - 1][resno][bandno]) {
+                                value -= (imsb - matrice[layno - 1][resno][bandno]);
+                                if (value < 0) {
+                                    value = 0;
+                                }
+                            }
+                        }
+
+                        if (layno == 0) {
+                            cblk->numpassesinlayers = 0;
+                        }
+
+                        n = cblk->numpassesinlayers;
+                        if (cblk->numpassesinlayers == 0) {
+                            if (value != 0) {
+                                n = 3 * (OPJ_UINT32)value - 2 + cblk->numpassesinlayers;
+                            } else {
+                                n = cblk->numpassesinlayers;
+                            }
+                        } else {
+                            n = 3 * (OPJ_UINT32)value + cblk->numpassesinlayers;
+                        }
+
+                        layer->numpasses = n - cblk->numpassesinlayers;
+
+                        if (!layer->numpasses) {
+                            continue;
+                        }
+
+                        if (cblk->numpassesinlayers == 0) {
+                            layer->len = cblk->passes[n - 1].rate;
+                            layer->data = cblk->data;
+                        } else {
+                            layer->len = cblk->passes[n - 1].rate - cblk->passes[cblk->numpassesinlayers -
+                                         1].rate;
+                            layer->data = cblk->data + cblk->passes[cblk->numpassesinlayers - 1].rate;
+                        }
+
+                        if (final) {
+                            cblk->numpassesinlayers = n;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+OPJ_BOOL opj_tcd_rateallocate(opj_tcd_t *tcd,
+                              OPJ_BYTE *dest,
+                              OPJ_UINT32 * p_data_written,
+                              OPJ_UINT32 len,
+                              opj_codestream_info_t *cstr_info,
+                              opj_event_mgr_t *p_manager)
+{
+    OPJ_UINT32 compno, resno, bandno, precno, cblkno, layno;
+    OPJ_UINT32 passno;
+    OPJ_FLOAT64 min, max;
+    OPJ_FLOAT64 cumdisto[100];      /* fixed_quality */
+    const OPJ_FLOAT64 K = 1;                /* 1.1; fixed_quality */
+    OPJ_FLOAT64 maxSE = 0;
+
+    opj_cp_t *cp = tcd->cp;
+    opj_tcd_tile_t *tcd_tile = tcd->tcd_image->tiles;
+    opj_tcp_t *tcd_tcp = tcd->tcp;
+
+    min = DBL_MAX;
+    max = 0;
+
+    tcd_tile->numpix = 0;           /* fixed_quality */
+
+    for (compno = 0; compno < tcd_tile->numcomps; compno++) {
+        opj_tcd_tilecomp_t *tilec = &tcd_tile->comps[compno];
+        tilec->numpix = 0;
+
+        for (resno = 0; resno < tilec->numresolutions; resno++) {
+            opj_tcd_resolution_t *res = &tilec->resolutions[resno];
+
+            for (bandno = 0; bandno < res->numbands; bandno++) {
+                opj_tcd_band_t *band = &res->bands[bandno];
+
+                /* Skip empty bands */
+                if (opj_tcd_is_band_empty(band)) {
+                    continue;
+                }
+
+                for (precno = 0; precno < res->pw * res->ph; precno++) {
+                    opj_tcd_precinct_t *prc = &band->precincts[precno];
+
+                    for (cblkno = 0; cblkno < prc->cw * prc->ch; cblkno++) {
+                        opj_tcd_cblk_enc_t *cblk = &prc->cblks.enc[cblkno];
+
+                        for (passno = 0; passno < cblk->totalpasses; passno++) {
+                            opj_tcd_pass_t *pass = &cblk->passes[passno];
+                            OPJ_INT32 dr;
+                            OPJ_FLOAT64 dd, rdslope;
+
+                            if (passno == 0) {
+                                dr = (OPJ_INT32)pass->rate;
+                                dd = pass->distortiondec;
+                            } else {
+                                dr = (OPJ_INT32)(pass->rate - cblk->passes[passno - 1].rate);
+                                dd = pass->distortiondec - cblk->passes[passno - 1].distortiondec;
+                            }
+
+                            if (dr == 0) {
+                                continue;
+                            }
+
+                            rdslope = dd / dr;
+                            if (rdslope < min) {
+                                min = rdslope;
+                            }
+
+                            if (rdslope > max) {
+                                max = rdslope;
+                            }
+                        } /* passno */
+
+                        /* fixed_quality */
+                        tcd_tile->numpix += ((cblk->x1 - cblk->x0) * (cblk->y1 - cblk->y0));
+                        tilec->numpix += ((cblk->x1 - cblk->x0) * (cblk->y1 - cblk->y0));
+                    } /* cbklno */
+                } /* precno */
+            } /* bandno */
+        } /* resno */
+
+        maxSE += (((OPJ_FLOAT64)(1 << tcd->image->comps[compno].prec) - 1.0)
+                  * ((OPJ_FLOAT64)(1 << tcd->image->comps[compno].prec) - 1.0))
+                 * ((OPJ_FLOAT64)(tilec->numpix));
+    } /* compno */
+
+    /* index file */
+    if (cstr_info) {
+        opj_tile_info_t *tile_info = &cstr_info->tile[tcd->tcd_tileno];
+        tile_info->numpix = tcd_tile->numpix;
+        tile_info->distotile = tcd_tile->distotile;
+        tile_info->thresh = (OPJ_FLOAT64 *) opj_malloc(tcd_tcp->numlayers * sizeof(
+                                OPJ_FLOAT64));
+        if (!tile_info->thresh) {
+            /* FIXME event manager error callback */
+            return OPJ_FALSE;
+        }
+    }
+
+    for (layno = 0; layno < tcd_tcp->numlayers; layno++) {
+        OPJ_FLOAT64 lo = min;
+        OPJ_FLOAT64 hi = max;
+        OPJ_UINT32 maxlen = tcd_tcp->rates[layno] > 0.0f ? opj_uint_min(((
+                                OPJ_UINT32) ceil(tcd_tcp->rates[layno])), len) : len;
+        OPJ_FLOAT64 goodthresh = 0;
+        OPJ_FLOAT64 stable_thresh = 0;
+        OPJ_UINT32 i;
+        OPJ_FLOAT64 distotarget;                /* fixed_quality */
+
+        /* fixed_quality */
+        distotarget = tcd_tile->distotile - ((K * maxSE) / pow((OPJ_FLOAT32)10,
+                                             tcd_tcp->distoratio[layno] / 10));
+
+        /* Don't try to find an optimal threshold but rather take everything not included yet, if
+          -r xx,yy,zz,0   (disto_alloc == 1 and rates == 0)
+          -q xx,yy,zz,0   (fixed_quality == 1 and distoratio == 0)
+          ==> possible to have some lossy layers and the last layer for sure lossless */
+        if (((cp->m_specific_param.m_enc.m_disto_alloc == 1) &&
+                (tcd_tcp->rates[layno] > 0.0f)) ||
+                ((cp->m_specific_param.m_enc.m_fixed_quality == 1) &&
+                 (tcd_tcp->distoratio[layno] > 0.0))) {
+            opj_t2_t*t2 = opj_t2_create(tcd->image, cp);
+            OPJ_FLOAT64 thresh = 0;
+
+            if (t2 == 00) {
+                return OPJ_FALSE;
+            }
+
+            for (i = 0; i < 128; ++i) {
+                OPJ_FLOAT64 distoachieved = 0;  /* fixed_quality */
+
+                thresh = (lo + hi) / 2;
+
+                opj_tcd_makelayer(tcd, layno, thresh, 0);
+
+                if (cp->m_specific_param.m_enc.m_fixed_quality) {       /* fixed_quality */
+                    if (OPJ_IS_CINEMA(cp->rsiz) || OPJ_IS_IMF(cp->rsiz)) {
+                        if (! opj_t2_encode_packets(t2, tcd->tcd_tileno, tcd_tile, layno + 1, dest,
+                                                    p_data_written, maxlen, cstr_info, NULL, tcd->cur_tp_num, tcd->tp_pos,
+                                                    tcd->cur_pino,
+                                                    THRESH_CALC, p_manager)) {
+
+                            lo = thresh;
+                            continue;
+                        } else {
+                            distoachieved = layno == 0 ?
+                                            tcd_tile->distolayer[0] : cumdisto[layno - 1] + tcd_tile->distolayer[layno];
+
+                            if (distoachieved < distotarget) {
+                                hi = thresh;
+                                stable_thresh = thresh;
+                                continue;
+                            } else {
+                                lo = thresh;
+                            }
+                        }
+                    } else {
+                        distoachieved = (layno == 0) ?
+                                        tcd_tile->distolayer[0] : (cumdisto[layno - 1] + tcd_tile->distolayer[layno]);
+
+                        if (distoachieved < distotarget) {
+                            hi = thresh;
+                            stable_thresh = thresh;
+                            continue;
+                        }
+                        lo = thresh;
+                    }
+                } else {
+                    if (! opj_t2_encode_packets(t2, tcd->tcd_tileno, tcd_tile, layno + 1, dest,
+                                                p_data_written, maxlen, cstr_info, NULL, tcd->cur_tp_num, tcd->tp_pos,
+                                                tcd->cur_pino,
+                                                THRESH_CALC, p_manager)) {
+                        /* TODO: what to do with l ??? seek / tell ??? */
+                        /* opj_event_msg(tcd->cinfo, EVT_INFO, "rate alloc: len=%d, max=%d\n", l, maxlen); */
+                        lo = thresh;
+                        continue;
+                    }
+
+                    hi = thresh;
+                    stable_thresh = thresh;
+                }
+            }
+
+            goodthresh = stable_thresh == 0 ? thresh : stable_thresh;
+
+            opj_t2_destroy(t2);
+        } else {
+            /* Special value to indicate to use all passes */
+            goodthresh = -1;
+        }
+
+        if (cstr_info) { /* Threshold for Marcela Index */
+            cstr_info->tile[tcd->tcd_tileno].thresh[layno] = goodthresh;
+        }
+
+        opj_tcd_makelayer(tcd, layno, goodthresh, 1);
+
+        /* fixed_quality */
+        cumdisto[layno] = (layno == 0) ? tcd_tile->distolayer[0] :
+                          (cumdisto[layno - 1] + tcd_tile->distolayer[layno]);
+    }
+
+    return OPJ_TRUE;
+}
+
+OPJ_BOOL opj_tcd_init(opj_tcd_t *p_tcd,
+                      opj_image_t * p_image,
+                      opj_cp_t * p_cp,
+                      opj_thread_pool_t* p_tp)
+{
+    p_tcd->image = p_image;
+    p_tcd->cp = p_cp;
+
+    p_tcd->tcd_image->tiles = (opj_tcd_tile_t *) opj_calloc(1,
+                              sizeof(opj_tcd_tile_t));
+    if (! p_tcd->tcd_image->tiles) {
+        return OPJ_FALSE;
+    }
+
+    p_tcd->tcd_image->tiles->comps = (opj_tcd_tilecomp_t *) opj_calloc(
+                                         p_image->numcomps, sizeof(opj_tcd_tilecomp_t));
+    if (! p_tcd->tcd_image->tiles->comps) {
+        return OPJ_FALSE;
+    }
+
+    p_tcd->tcd_image->tiles->numcomps = p_image->numcomps;
+    p_tcd->tp_pos = p_cp->m_specific_param.m_enc.m_tp_pos;
+    p_tcd->thread_pool = p_tp;
+
+    return OPJ_TRUE;
+}
+
+/**
+Destroy a previously created TCD handle
+*/
+void opj_tcd_destroy(opj_tcd_t *tcd)
+{
+    if (tcd) {
+        opj_tcd_free_tile(tcd);
+
+        if (tcd->tcd_image) {
+            opj_free(tcd->tcd_image);
+            tcd->tcd_image = 00;
+        }
+
+        opj_free(tcd->used_component);
+
+        opj_free(tcd);
+    }
+}
+
+OPJ_BOOL opj_alloc_tile_component_data(opj_tcd_tilecomp_t *l_tilec)
+{
+    if ((l_tilec->data == 00) ||
+            ((l_tilec->data_size_needed > l_tilec->data_size) &&
+             (l_tilec->ownsData == OPJ_FALSE))) {
+        l_tilec->data = (OPJ_INT32 *) opj_image_data_alloc(l_tilec->data_size_needed);
+        if (!l_tilec->data && l_tilec->data_size_needed != 0) {
+            return OPJ_FALSE;
+        }
+        /*fprintf(stderr, "tAllocate data of tilec (int): %d x OPJ_UINT32n",l_data_size);*/
+        l_tilec->data_size = l_tilec->data_size_needed;
+        l_tilec->ownsData = OPJ_TRUE;
+    } else if (l_tilec->data_size_needed > l_tilec->data_size) {
+        /* We don't need to keep old data */
+        opj_image_data_free(l_tilec->data);
+        l_tilec->data = (OPJ_INT32 *) opj_image_data_alloc(l_tilec->data_size_needed);
+        if (! l_tilec->data) {
+            l_tilec->data_size = 0;
+            l_tilec->data_size_needed = 0;
+            l_tilec->ownsData = OPJ_FALSE;
+            return OPJ_FALSE;
+        }
+        /*fprintf(stderr, "tReallocate data of tilec (int): from %d to %d x OPJ_UINT32n", l_tilec->data_size, l_data_size);*/
+        l_tilec->data_size = l_tilec->data_size_needed;
+        l_tilec->ownsData = OPJ_TRUE;
+    }
+    return OPJ_TRUE;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static INLINE OPJ_BOOL opj_tcd_init_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
+        OPJ_BOOL isEncoder, OPJ_SIZE_T sizeof_block,
+        opj_event_mgr_t* manager)
+{
+    OPJ_UINT32 compno, resno, bandno, precno, cblkno;
+    opj_tcp_t * l_tcp = 00;
+    opj_cp_t * l_cp = 00;
+    opj_tcd_tile_t * l_tile = 00;
+    opj_tccp_t *l_tccp = 00;
+    opj_tcd_tilecomp_t *l_tilec = 00;
+    opj_image_comp_t * l_image_comp = 00;
+    opj_tcd_resolution_t *l_res = 00;
+    opj_tcd_band_t *l_band = 00;
+    opj_stepsize_t * l_step_size = 00;
+    opj_tcd_precinct_t *l_current_precinct = 00;
+    opj_image_t *l_image = 00;
+    OPJ_UINT32 p, q;
+    OPJ_UINT32 l_level_no;
+    OPJ_UINT32 l_pdx, l_pdy;
+    OPJ_INT32 l_x0b, l_y0b;
+    OPJ_UINT32 l_tx0, l_ty0;
+    /* extent of precincts , top left, bottom right**/
+    OPJ_INT32 l_tl_prc_x_start, l_tl_prc_y_start, l_br_prc_x_end, l_br_prc_y_end;
+    /* number of precinct for a resolution */
+    OPJ_UINT32 l_nb_precincts;
+    /* room needed to store l_nb_precinct precinct for a resolution */
+    OPJ_UINT32 l_nb_precinct_size;
+    /* number of code blocks for a precinct*/
+    OPJ_UINT32 l_nb_code_blocks;
+    /* room needed to store l_nb_code_blocks code blocks for a precinct*/
+    OPJ_UINT32 l_nb_code_blocks_size;
+    /* size of data for a tile */
+    OPJ_UINT32 l_data_size;
+
+    l_cp = p_tcd->cp;
+    l_tcp = &(l_cp->tcps[p_tile_no]);
+    l_tile = p_tcd->tcd_image->tiles;
+    l_tccp = l_tcp->tccps;
+    l_tilec = l_tile->comps;
+    l_image = p_tcd->image;
+    l_image_comp = p_tcd->image->comps;
+
+    p = p_tile_no % l_cp->tw;       /* tile coordinates */
+    q = p_tile_no / l_cp->tw;
+    /*fprintf(stderr, "Tile coordinate = %d,%d\n", p, q);*/
+
+    /* 4 borders of the tile rescale on the image if necessary */
+    l_tx0 = l_cp->tx0 + p *
+            l_cp->tdx; /* can't be greater than l_image->x1 so won't overflow */
+    l_tile->x0 = (OPJ_INT32)opj_uint_max(l_tx0, l_image->x0);
+    l_tile->x1 = (OPJ_INT32)opj_uint_min(opj_uint_adds(l_tx0, l_cp->tdx),
+                                         l_image->x1);
+    /* all those OPJ_UINT32 are casted to OPJ_INT32, let's do some sanity check */
+    if ((l_tile->x0 < 0) || (l_tile->x1 <= l_tile->x0)) {
+        opj_event_msg(manager, EVT_ERROR, "Tile X coordinates are not supported\n");
+        return OPJ_FALSE;
+    }
+    l_ty0 = l_cp->ty0 + q *
+            l_cp->tdy; /* can't be greater than l_image->y1 so won't overflow */
+    l_tile->y0 = (OPJ_INT32)opj_uint_max(l_ty0, l_image->y0);
+    l_tile->y1 = (OPJ_INT32)opj_uint_min(opj_uint_adds(l_ty0, l_cp->tdy),
+                                         l_image->y1);
+    /* all those OPJ_UINT32 are casted to OPJ_INT32, let's do some sanity check */
+    if ((l_tile->y0 < 0) || (l_tile->y1 <= l_tile->y0)) {
+        opj_event_msg(manager, EVT_ERROR, "Tile Y coordinates are not supported\n");
+        return OPJ_FALSE;
+    }
+
+
+    /* testcase 1888.pdf.asan.35.988 */
+    if (l_tccp->numresolutions == 0) {
+        opj_event_msg(manager, EVT_ERROR, "tiles require at least one resolution\n");
+        return OPJ_FALSE;
+    }
+    /*fprintf(stderr, "Tile border = %d,%d,%d,%d\n", l_tile->x0, l_tile->y0,l_tile->x1,l_tile->y1);*/
+
+    /*tile->numcomps = image->numcomps; */
+    for (compno = 0; compno < l_tile->numcomps; ++compno) {
+        /*fprintf(stderr, "compno = %d/%d\n", compno, l_tile->numcomps);*/
+        l_image_comp->resno_decoded = 0;
+        /* border of each l_tile component (global) */
+        l_tilec->x0 = opj_int_ceildiv(l_tile->x0, (OPJ_INT32)l_image_comp->dx);
+        l_tilec->y0 = opj_int_ceildiv(l_tile->y0, (OPJ_INT32)l_image_comp->dy);
+        l_tilec->x1 = opj_int_ceildiv(l_tile->x1, (OPJ_INT32)l_image_comp->dx);
+        l_tilec->y1 = opj_int_ceildiv(l_tile->y1, (OPJ_INT32)l_image_comp->dy);
+        l_tilec->compno = compno;
+        /*fprintf(stderr, "\tTile compo border = %d,%d,%d,%d\n", l_tilec->x0, l_tilec->y0,l_tilec->x1,l_tilec->y1);*/
+
+        l_tilec->numresolutions = l_tccp->numresolutions;
+        if (l_tccp->numresolutions < l_cp->m_specific_param.m_dec.m_reduce) {
+            l_tilec->minimum_num_resolutions = 1;
+        } else {
+            l_tilec->minimum_num_resolutions = l_tccp->numresolutions -
+                                               l_cp->m_specific_param.m_dec.m_reduce;
+        }
+
+        if (isEncoder) {
+            OPJ_SIZE_T l_tile_data_size;
+
+            if (l_tilec->x0 >= l_tilec->x1 || l_tilec->y0 >= l_tilec->y1) {
+                opj_event_msg(manager, EVT_ERROR, "Invalid tile data\n");
+                return OPJ_FALSE;
+            }
+
+            /* compute l_data_size with overflow check */
+            OPJ_SIZE_T w = (OPJ_SIZE_T)(l_tilec->x1 - l_tilec->x0);
+            OPJ_SIZE_T h = (OPJ_SIZE_T)(l_tilec->y1 - l_tilec->y0);
+
+            /* issue 733, l_data_size == 0U, probably something wrong should be checked before getting here */
+            if (h > 0 && w > SIZE_MAX / h) {
+                opj_event_msg(manager, EVT_ERROR, "Size of tile data exceeds system limits\n");
+                return OPJ_FALSE;
+            }
+            l_tile_data_size = w * h;
+
+            if (SIZE_MAX / sizeof(OPJ_UINT32) < l_tile_data_size) {
+                opj_event_msg(manager, EVT_ERROR, "Size of tile data exceeds system limits\n");
+                return OPJ_FALSE;
+            }
+            l_tile_data_size = l_tile_data_size * sizeof(OPJ_UINT32);
+
+            l_tilec->data_size_needed = l_tile_data_size;
+        }
+
+        l_data_size = l_tilec->numresolutions * (OPJ_UINT32)sizeof(
+                          opj_tcd_resolution_t);
+
+        opj_image_data_free(l_tilec->data_win);
+        l_tilec->data_win = NULL;
+        l_tilec->win_x0 = 0;
+        l_tilec->win_y0 = 0;
+        l_tilec->win_x1 = 0;
+        l_tilec->win_y1 = 0;
+
+        if (l_tilec->resolutions == 00) {
+            l_tilec->resolutions = (opj_tcd_resolution_t *) opj_malloc(l_data_size);
+            if (! l_tilec->resolutions) {
+                return OPJ_FALSE;
+            }
+            /*fprintf(stderr, "\tAllocate resolutions of tilec (opj_tcd_resolution_t): %d\n",l_data_size);*/
+            l_tilec->resolutions_size = l_data_size;
+            memset(l_tilec->resolutions, 0, l_data_size);
+        } else if (l_data_size > l_tilec->resolutions_size) {
+            opj_tcd_resolution_t* new_resolutions = (opj_tcd_resolution_t *) opj_realloc(
+                    l_tilec->resolutions, l_data_size);
+            if (! new_resolutions) {
+                opj_event_msg(manager, EVT_ERROR, "Not enough memory for tile resolutions\n");
+                opj_free(l_tilec->resolutions);
+                l_tilec->resolutions = NULL;
+                l_tilec->resolutions_size = 0;
+                return OPJ_FALSE;
+            }
+            l_tilec->resolutions = new_resolutions;
+            /*fprintf(stderr, "\tReallocate data of tilec (int): from %d to %d x OPJ_UINT32\n", l_tilec->resolutions_size, l_data_size);*/
+            memset(((OPJ_BYTE*) l_tilec->resolutions) + l_tilec->resolutions_size, 0,
+                   l_data_size - l_tilec->resolutions_size);
+            l_tilec->resolutions_size = l_data_size;
+        }
+
+        l_level_no = l_tilec->numresolutions;
+        l_res = l_tilec->resolutions;
+        l_step_size = l_tccp->stepsizes;
+        /*fprintf(stderr, "\tlevel_no=%d\n",l_level_no);*/
+
+        for (resno = 0; resno < l_tilec->numresolutions; ++resno) {
+            /*fprintf(stderr, "\t\tresno = %d/%d\n", resno, l_tilec->numresolutions);*/
+            OPJ_INT32 tlcbgxstart, tlcbgystart /*, brcbgxend, brcbgyend*/;
+            OPJ_UINT32 cbgwidthexpn, cbgheightexpn;
+            OPJ_UINT32 cblkwidthexpn, cblkheightexpn;
+
+            --l_level_no;
+
+            /* border for each resolution level (global) */
+            l_res->x0 = opj_int_ceildivpow2(l_tilec->x0, (OPJ_INT32)l_level_no);
+            l_res->y0 = opj_int_ceildivpow2(l_tilec->y0, (OPJ_INT32)l_level_no);
+            l_res->x1 = opj_int_ceildivpow2(l_tilec->x1, (OPJ_INT32)l_level_no);
+            l_res->y1 = opj_int_ceildivpow2(l_tilec->y1, (OPJ_INT32)l_level_no);
+
+            /*fprintf(stderr, "\t\t\tres_x0= %d, res_y0 =%d, res_x1=%d, res_y1=%d\n", l_res->x0, l_res->y0, l_res->x1, l_res->y1);*/
+            /* p. 35, table A-23, ISO/IEC FDIS154444-1 : 2000 (18 august 2000) */
+            l_pdx = l_tccp->prcw[resno];
+            l_pdy = l_tccp->prch[resno];
+            /*fprintf(stderr, "\t\t\tpdx=%d, pdy=%d\n", l_pdx, l_pdy);*/
+            /* p. 64, B.6, ISO/IEC FDIS15444-1 : 2000 (18 august 2000)  */
+            l_tl_prc_x_start = opj_int_floordivpow2(l_res->x0, (OPJ_INT32)l_pdx) << l_pdx;
+            l_tl_prc_y_start = opj_int_floordivpow2(l_res->y0, (OPJ_INT32)l_pdy) << l_pdy;
+            {
+                OPJ_UINT32 tmp = ((OPJ_UINT32)opj_int_ceildivpow2(l_res->x1,
+                                  (OPJ_INT32)l_pdx)) << l_pdx;
+                if (tmp > (OPJ_UINT32)INT_MAX) {
+                    opj_event_msg(manager, EVT_ERROR, "Integer overflow\n");
+                    return OPJ_FALSE;
+                }
+                l_br_prc_x_end = (OPJ_INT32)tmp;
+            }
+            {
+                OPJ_UINT32 tmp = ((OPJ_UINT32)opj_int_ceildivpow2(l_res->y1,
+                                  (OPJ_INT32)l_pdy)) << l_pdy;
+                if (tmp > (OPJ_UINT32)INT_MAX) {
+                    opj_event_msg(manager, EVT_ERROR, "Integer overflow\n");
+                    return OPJ_FALSE;
+                }
+                l_br_prc_y_end = (OPJ_INT32)tmp;
+            }
+            /*fprintf(stderr, "\t\t\tprc_x_start=%d, prc_y_start=%d, br_prc_x_end=%d, br_prc_y_end=%d \n", l_tl_prc_x_start, l_tl_prc_y_start, l_br_prc_x_end ,l_br_prc_y_end );*/
+
+            l_res->pw = (l_res->x0 == l_res->x1) ? 0U : (OPJ_UINT32)((
+                            l_br_prc_x_end - l_tl_prc_x_start) >> l_pdx);
+            l_res->ph = (l_res->y0 == l_res->y1) ? 0U : (OPJ_UINT32)((
+                            l_br_prc_y_end - l_tl_prc_y_start) >> l_pdy);
+            /*fprintf(stderr, "\t\t\tres_pw=%d, res_ph=%d\n", l_res->pw, l_res->ph );*/
+
+            if ((l_res->pw != 0U) && ((((OPJ_UINT32) - 1) / l_res->pw) < l_res->ph)) {
+                opj_event_msg(manager, EVT_ERROR, "Size of tile data exceeds system limits\n");
+                return OPJ_FALSE;
+            }
+            l_nb_precincts = l_res->pw * l_res->ph;
+
+            if ((((OPJ_UINT32) - 1) / (OPJ_UINT32)sizeof(opj_tcd_precinct_t)) <
+                    l_nb_precincts) {
+                opj_event_msg(manager, EVT_ERROR, "Size of tile data exceeds system limits\n");
+                return OPJ_FALSE;
+            }
+            l_nb_precinct_size = l_nb_precincts * (OPJ_UINT32)sizeof(opj_tcd_precinct_t);
+
+            if (resno == 0) {
+                tlcbgxstart = l_tl_prc_x_start;
+                tlcbgystart = l_tl_prc_y_start;
+                /*brcbgxend = l_br_prc_x_end;*/
+                /* brcbgyend = l_br_prc_y_end;*/
+                cbgwidthexpn = l_pdx;
+                cbgheightexpn = l_pdy;
+                l_res->numbands = 1;
+            } else {
+                tlcbgxstart = opj_int_ceildivpow2(l_tl_prc_x_start, 1);
+                tlcbgystart = opj_int_ceildivpow2(l_tl_prc_y_start, 1);
+                /*brcbgxend = opj_int_ceildivpow2(l_br_prc_x_end, 1);*/
+                /*brcbgyend = opj_int_ceildivpow2(l_br_prc_y_end, 1);*/
+                cbgwidthexpn = l_pdx - 1;
+                cbgheightexpn = l_pdy - 1;
+                l_res->numbands = 3;
+            }
+
+            cblkwidthexpn = opj_uint_min(l_tccp->cblkw, cbgwidthexpn);
+            cblkheightexpn = opj_uint_min(l_tccp->cblkh, cbgheightexpn);
+            l_band = l_res->bands;
+
+            for (bandno = 0; bandno < l_res->numbands; ++bandno, ++l_band, ++l_step_size) {
+                /*fprintf(stderr, "\t\t\tband_no=%d/%d\n", bandno, l_res->numbands );*/
+
+                if (resno == 0) {
+                    l_band->bandno = 0 ;
+                    l_band->x0 = opj_int_ceildivpow2(l_tilec->x0, (OPJ_INT32)l_level_no);
+                    l_band->y0 = opj_int_ceildivpow2(l_tilec->y0, (OPJ_INT32)l_level_no);
+                    l_band->x1 = opj_int_ceildivpow2(l_tilec->x1, (OPJ_INT32)l_level_no);
+                    l_band->y1 = opj_int_ceildivpow2(l_tilec->y1, (OPJ_INT32)l_level_no);
+                } else {
+                    l_band->bandno = bandno + 1;
+                    /* x0b = 1 if bandno = 1 or 3 */
+                    l_x0b = l_band->bandno & 1;
+                    /* y0b = 1 if bandno = 2 or 3 */
+                    l_y0b = (OPJ_INT32)((l_band->bandno) >> 1);
+                    /* l_band border (global) */
+                    l_band->x0 = opj_int64_ceildivpow2(l_tilec->x0 - ((OPJ_INT64)l_x0b <<
+                                                       l_level_no), (OPJ_INT32)(l_level_no + 1));
+                    l_band->y0 = opj_int64_ceildivpow2(l_tilec->y0 - ((OPJ_INT64)l_y0b <<
+                                                       l_level_no), (OPJ_INT32)(l_level_no + 1));
+                    l_band->x1 = opj_int64_ceildivpow2(l_tilec->x1 - ((OPJ_INT64)l_x0b <<
+                                                       l_level_no), (OPJ_INT32)(l_level_no + 1));
+                    l_band->y1 = opj_int64_ceildivpow2(l_tilec->y1 - ((OPJ_INT64)l_y0b <<
+                                                       l_level_no), (OPJ_INT32)(l_level_no + 1));
+                }
+
+                if (isEncoder) {
+                    /* Skip empty bands */
+                    if (opj_tcd_is_band_empty(l_band)) {
+                        /* Do not zero l_band->precints to avoid leaks */
+                        /* but make sure we don't use it later, since */
+                        /* it will point to precincts of previous bands... */
+                        continue;
+                    }
+                }
+
+                {
+                    /* Table E-1 - Sub-band gains */
+                    /* BUG_WEIRD_TWO_INVK (look for this identifier in dwt.c): */
+                    /* the test (!isEncoder && l_tccp->qmfbid == 0) is strongly */
+                    /* linked to the use of two_invK instead of invK */
+                    const OPJ_INT32 log2_gain = (!isEncoder &&
+                                                 l_tccp->qmfbid == 0) ? 0 : (l_band->bandno == 0) ? 0 :
+                                                (l_band->bandno == 3) ? 2 : 1;
+
+                    /* Nominal dynamic range. Equation E-4 */
+                    const OPJ_INT32 Rb = (OPJ_INT32)l_image_comp->prec + log2_gain;
+
+                    /* Delta_b value of Equation E-3 in "E.1 Inverse quantization
+                    * procedure" of the standard */
+                    l_band->stepsize = (OPJ_FLOAT32)(((1.0 + l_step_size->mant / 2048.0) * pow(2.0,
+                                                      (OPJ_INT32)(Rb - l_step_size->expn))));
+                }
+
+                /* Mb value of Equation E-2 in "E.1 Inverse quantization
+                 * procedure" of the standard */
+                l_band->numbps = l_step_size->expn + (OPJ_INT32)l_tccp->numgbits -
+                                 1;
+
+                if (!l_band->precincts && (l_nb_precincts > 0U)) {
+                    l_band->precincts = (opj_tcd_precinct_t *) opj_malloc(/*3 * */
+                                            l_nb_precinct_size);
+                    if (! l_band->precincts) {
+                        opj_event_msg(manager, EVT_ERROR,
+                                      "Not enough memory to handle band precints\n");
+                        return OPJ_FALSE;
+                    }
+                    /*fprintf(stderr, "\t\t\t\tAllocate precincts of a band (opj_tcd_precinct_t): %d\n",l_nb_precinct_size);     */
+                    memset(l_band->precincts, 0, l_nb_precinct_size);
+                    l_band->precincts_data_size = l_nb_precinct_size;
+                } else if (l_band->precincts_data_size < l_nb_precinct_size) {
+
+                    opj_tcd_precinct_t * new_precincts = (opj_tcd_precinct_t *) opj_realloc(
+                            l_band->precincts,/*3 * */ l_nb_precinct_size);
+                    if (! new_precincts) {
+                        opj_event_msg(manager, EVT_ERROR,
+                                      "Not enough memory to handle band precints\n");
+                        opj_free(l_band->precincts);
+                        l_band->precincts = NULL;
+                        l_band->precincts_data_size = 0;
+                        return OPJ_FALSE;
+                    }
+                    l_band->precincts = new_precincts;
+                    /*fprintf(stderr, "\t\t\t\tReallocate precincts of a band (opj_tcd_precinct_t): from %d to %d\n",l_band->precincts_data_size, l_nb_precinct_size);*/
+                    memset(((OPJ_BYTE *) l_band->precincts) + l_band->precincts_data_size, 0,
+                           l_nb_precinct_size - l_band->precincts_data_size);
+                    l_band->precincts_data_size = l_nb_precinct_size;
+                }
+
+                l_current_precinct = l_band->precincts;
+                for (precno = 0; precno < l_nb_precincts; ++precno) {
+                    OPJ_INT32 tlcblkxstart, tlcblkystart, brcblkxend, brcblkyend;
+                    OPJ_INT32 cbgxstart = tlcbgxstart + (OPJ_INT32)(precno % l_res->pw) *
+                                          (1 << cbgwidthexpn);
+                    OPJ_INT32 cbgystart = tlcbgystart + (OPJ_INT32)(precno / l_res->pw) *
+                                          (1 << cbgheightexpn);
+                    OPJ_INT32 cbgxend = cbgxstart + (1 << cbgwidthexpn);
+                    OPJ_INT32 cbgyend = cbgystart + (1 << cbgheightexpn);
+                    /*fprintf(stderr, "\t precno=%d; bandno=%d, resno=%d; compno=%d\n", precno, bandno , resno, compno);*/
+                    /*fprintf(stderr, "\t tlcbgxstart(=%d) + (precno(=%d) percent res->pw(=%d)) * (1 << cbgwidthexpn(=%d)) \n",tlcbgxstart,precno,l_res->pw,cbgwidthexpn);*/
+
+                    /* precinct size (global) */
+                    /*fprintf(stderr, "\t cbgxstart=%d, l_band->x0 = %d \n",cbgxstart, l_band->x0);*/
+
+                    l_current_precinct->x0 = opj_int_max(cbgxstart, l_band->x0);
+                    l_current_precinct->y0 = opj_int_max(cbgystart, l_band->y0);
+                    l_current_precinct->x1 = opj_int_min(cbgxend, l_band->x1);
+                    l_current_precinct->y1 = opj_int_min(cbgyend, l_band->y1);
+                    /*fprintf(stderr, "\t prc_x0=%d; prc_y0=%d, prc_x1=%d; prc_y1=%d\n",l_current_precinct->x0, l_current_precinct->y0 ,l_current_precinct->x1, l_current_precinct->y1);*/
+
+                    tlcblkxstart = opj_int_floordivpow2(l_current_precinct->x0,
+                                                        (OPJ_INT32)cblkwidthexpn) << cblkwidthexpn;
+                    /*fprintf(stderr, "\t tlcblkxstart =%d\n",tlcblkxstart );*/
+                    tlcblkystart = opj_int_floordivpow2(l_current_precinct->y0,
+                                                        (OPJ_INT32)cblkheightexpn) << cblkheightexpn;
+                    /*fprintf(stderr, "\t tlcblkystart =%d\n",tlcblkystart );*/
+                    brcblkxend = opj_int_ceildivpow2(l_current_precinct->x1,
+                                                     (OPJ_INT32)cblkwidthexpn) << cblkwidthexpn;
+                    /*fprintf(stderr, "\t brcblkxend =%d\n",brcblkxend );*/
+                    brcblkyend = opj_int_ceildivpow2(l_current_precinct->y1,
+                                                     (OPJ_INT32)cblkheightexpn) << cblkheightexpn;
+                    /*fprintf(stderr, "\t brcblkyend =%d\n",brcblkyend );*/
+                    l_current_precinct->cw = (OPJ_UINT32)((brcblkxend - tlcblkxstart) >>
+                                                          cblkwidthexpn);
+                    l_current_precinct->ch = (OPJ_UINT32)((brcblkyend - tlcblkystart) >>
+                                                          cblkheightexpn);
+
+                    if (l_current_precinct->cw && ((OPJ_UINT32)-1) / l_current_precinct->cw < l_current_precinct->ch) {
+                        return OPJ_FALSE;
+                    }
+                    l_nb_code_blocks = l_current_precinct->cw * l_current_precinct->ch;
+                    /*fprintf(stderr, "\t\t\t\t precinct_cw = %d x recinct_ch = %d\n",l_current_precinct->cw, l_current_precinct->ch);      */
+                    if ((((OPJ_UINT32) - 1) / (OPJ_UINT32)sizeof_block) <
+                            l_nb_code_blocks) {
+                        opj_event_msg(manager, EVT_ERROR,
+                                      "Size of code block data exceeds system limits\n");
+                        return OPJ_FALSE;
+                    }
+                    l_nb_code_blocks_size = l_nb_code_blocks * (OPJ_UINT32)sizeof_block;
+
+                    if (!l_current_precinct->cblks.blocks && (l_nb_code_blocks > 0U)) {
+                        l_current_precinct->cblks.blocks = opj_malloc(l_nb_code_blocks_size);
+                        if (! l_current_precinct->cblks.blocks) {
+                            return OPJ_FALSE;
+                        }
+                        /*fprintf(stderr, "\t\t\t\tAllocate cblks of a precinct (opj_tcd_cblk_dec_t): %d\n",l_nb_code_blocks_size);*/
+
+                        memset(l_current_precinct->cblks.blocks, 0, l_nb_code_blocks_size);
+
+                        l_current_precinct->block_size = l_nb_code_blocks_size;
+                    } else if (l_nb_code_blocks_size > l_current_precinct->block_size) {
+                        void *new_blocks = opj_realloc(l_current_precinct->cblks.blocks,
+                                                       l_nb_code_blocks_size);
+                        if (! new_blocks) {
+                            opj_free(l_current_precinct->cblks.blocks);
+                            l_current_precinct->cblks.blocks = NULL;
+                            l_current_precinct->block_size = 0;
+                            opj_event_msg(manager, EVT_ERROR,
+                                          "Not enough memory for current precinct codeblock element\n");
+                            return OPJ_FALSE;
+                        }
+                        l_current_precinct->cblks.blocks = new_blocks;
+                        /*fprintf(stderr, "\t\t\t\tReallocate cblks of a precinct (opj_tcd_cblk_dec_t): from %d to %d\n",l_current_precinct->block_size, l_nb_code_blocks_size);     */
+
+                        memset(((OPJ_BYTE *) l_current_precinct->cblks.blocks) +
+                               l_current_precinct->block_size
+                               , 0
+                               , l_nb_code_blocks_size - l_current_precinct->block_size);
+
+                        l_current_precinct->block_size = l_nb_code_blocks_size;
+                    }
+
+                    if (! l_current_precinct->incltree) {
+                        l_current_precinct->incltree = opj_tgt_create(l_current_precinct->cw,
+                                                       l_current_precinct->ch, manager);
+                    } else {
+                        l_current_precinct->incltree = opj_tgt_init(l_current_precinct->incltree,
+                                                       l_current_precinct->cw, l_current_precinct->ch, manager);
+                    }
+
+                    if (! l_current_precinct->imsbtree) {
+                        l_current_precinct->imsbtree = opj_tgt_create(l_current_precinct->cw,
+                                                       l_current_precinct->ch, manager);
+                    } else {
+                        l_current_precinct->imsbtree = opj_tgt_init(l_current_precinct->imsbtree,
+                                                       l_current_precinct->cw, l_current_precinct->ch, manager);
+                    }
+
+                    for (cblkno = 0; cblkno < l_nb_code_blocks; ++cblkno) {
+                        OPJ_INT32 cblkxstart = tlcblkxstart + (OPJ_INT32)(cblkno %
+                                               l_current_precinct->cw) * (1 << cblkwidthexpn);
+                        OPJ_INT32 cblkystart = tlcblkystart + (OPJ_INT32)(cblkno /
+                                               l_current_precinct->cw) * (1 << cblkheightexpn);
+                        OPJ_INT32 cblkxend = cblkxstart + (1 << cblkwidthexpn);
+                        OPJ_INT32 cblkyend = cblkystart + (1 << cblkheightexpn);
+
+                        if (isEncoder) {
+                            opj_tcd_cblk_enc_t* l_code_block = l_current_precinct->cblks.enc + cblkno;
+
+                            if (! opj_tcd_code_block_enc_allocate(l_code_block)) {
+                                return OPJ_FALSE;
+                            }
+                            /* code-block size (global) */
+                            l_code_block->x0 = opj_int_max(cblkxstart, l_current_precinct->x0);
+                            l_code_block->y0 = opj_int_max(cblkystart, l_current_precinct->y0);
+                            l_code_block->x1 = opj_int_min(cblkxend, l_current_precinct->x1);
+                            l_code_block->y1 = opj_int_min(cblkyend, l_current_precinct->y1);
+
+                            if (! opj_tcd_code_block_enc_allocate_data(l_code_block)) {
+                                return OPJ_FALSE;
+                            }
+                        } else {
+                            opj_tcd_cblk_dec_t* l_code_block = l_current_precinct->cblks.dec + cblkno;
+
+                            if (! opj_tcd_code_block_dec_allocate(l_code_block)) {
+                                return OPJ_FALSE;
+                            }
+                            /* code-block size (global) */
+                            l_code_block->x0 = opj_int_max(cblkxstart, l_current_precinct->x0);
+                            l_code_block->y0 = opj_int_max(cblkystart, l_current_precinct->y0);
+                            l_code_block->x1 = opj_int_min(cblkxend, l_current_precinct->x1);
+                            l_code_block->y1 = opj_int_min(cblkyend, l_current_precinct->y1);
+                        }
+                    }
+                    ++l_current_precinct;
+                } /* precno */
+            } /* bandno */
+            ++l_res;
+        } /* resno */
+        ++l_tccp;
+        ++l_tilec;
+        ++l_image_comp;
+    } /* compno */
+    return OPJ_TRUE;
+}
+
+OPJ_BOOL opj_tcd_init_encode_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
+                                  opj_event_mgr_t* p_manager)
+{
+    return opj_tcd_init_tile(p_tcd, p_tile_no, OPJ_TRUE,
+                             sizeof(opj_tcd_cblk_enc_t), p_manager);
+}
+
+OPJ_BOOL opj_tcd_init_decode_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
+                                  opj_event_mgr_t* p_manager)
+{
+    return opj_tcd_init_tile(p_tcd, p_tile_no, OPJ_FALSE,
+                             sizeof(opj_tcd_cblk_dec_t), p_manager);
+}
+
+/**
+ * Allocates memory for an encoding code block (but not data memory).
+ */
+static OPJ_BOOL opj_tcd_code_block_enc_allocate(opj_tcd_cblk_enc_t *
+        p_code_block)
+{
+    if (! p_code_block->layers) {
+        /* no memset since data */
+        p_code_block->layers = (opj_tcd_layer_t*) opj_calloc(100,
+                               sizeof(opj_tcd_layer_t));
+        if (! p_code_block->layers) {
+            return OPJ_FALSE;
+        }
+    }
+    if (! p_code_block->passes) {
+        p_code_block->passes = (opj_tcd_pass_t*) opj_calloc(100,
+                               sizeof(opj_tcd_pass_t));
+        if (! p_code_block->passes) {
+            return OPJ_FALSE;
+        }
+    }
+    return OPJ_TRUE;
+}
+
+/**
+ * Allocates data memory for an encoding code block.
+ */
+static OPJ_BOOL opj_tcd_code_block_enc_allocate_data(opj_tcd_cblk_enc_t *
+        p_code_block)
+{
+    OPJ_UINT32 l_data_size;
+
+    /* +1 is needed for https://github.com/uclouvain/openjpeg/issues/835 */
+    /* and actually +2 required for https://github.com/uclouvain/openjpeg/issues/982 */
+    /* and +7 for https://github.com/uclouvain/openjpeg/issues/1283 (-M 3) */
+    /* and +26 for https://github.com/uclouvain/openjpeg/issues/1283 (-M 7) */
+    /* and +28 for https://github.com/uclouvain/openjpeg/issues/1283 (-M 44) */
+    /* and +33 for https://github.com/uclouvain/openjpeg/issues/1283 (-M 4) */
+    /* and +63 for https://github.com/uclouvain/openjpeg/issues/1283 (-M 4 -IMF 2K) */
+    /* and +74 for https://github.com/uclouvain/openjpeg/issues/1283 (-M 4 -n 8 -s 7,7 -I) */
+    /* TODO: is there a theoretical upper-bound for the compressed code */
+    /* block size ? */
+    l_data_size = 74 + (OPJ_UINT32)((p_code_block->x1 - p_code_block->x0) *
+                                    (p_code_block->y1 - p_code_block->y0) * (OPJ_INT32)sizeof(OPJ_UINT32));
+
+    if (l_data_size > p_code_block->data_size) {
+        if (p_code_block->data) {
+            /* We refer to data - 1 since below we incremented it */
+            opj_free(p_code_block->data - 1);
+        }
+        p_code_block->data = (OPJ_BYTE*) opj_malloc(l_data_size + 1);
+        if (! p_code_block->data) {
+            p_code_block->data_size = 0U;
+            return OPJ_FALSE;
+        }
+        p_code_block->data_size = l_data_size;
+
+        /* We reserve the initial byte as a fake byte to a non-FF value */
+        /* and increment the data pointer, so that opj_mqc_init_enc() */
+        /* can do bp = data - 1, and opj_mqc_byteout() can safely dereference */
+        /* it. */
+        p_code_block->data[0] = 0;
+        p_code_block->data += 1; /*why +1 ?*/
+    }
+    return OPJ_TRUE;
+}
+
+
+void opj_tcd_reinit_segment(opj_tcd_seg_t* seg)
+{
+    memset(seg, 0, sizeof(opj_tcd_seg_t));
+}
+
+/**
+ * Allocates memory for a decoding code block.
+ */
+static OPJ_BOOL opj_tcd_code_block_dec_allocate(opj_tcd_cblk_dec_t *
+        p_code_block)
+{
+    if (! p_code_block->segs) {
+
+        p_code_block->segs = (opj_tcd_seg_t *) opj_calloc(OPJ_J2K_DEFAULT_NB_SEGS,
+                             sizeof(opj_tcd_seg_t));
+        if (! p_code_block->segs) {
+            return OPJ_FALSE;
+        }
+        /*fprintf(stderr, "Allocate %d elements of code_block->data\n", OPJ_J2K_DEFAULT_NB_SEGS * sizeof(opj_tcd_seg_t));*/
+
+        p_code_block->m_current_max_segs = OPJ_J2K_DEFAULT_NB_SEGS;
+        /*fprintf(stderr, "m_current_max_segs of code_block->data = %d\n", p_code_block->m_current_max_segs);*/
+    } else {
+        /* sanitize */
+        opj_tcd_seg_t * l_segs = p_code_block->segs;
+        OPJ_UINT32 l_current_max_segs = p_code_block->m_current_max_segs;
+        opj_tcd_seg_data_chunk_t* l_chunks = p_code_block->chunks;
+        OPJ_UINT32 l_numchunksalloc = p_code_block->numchunksalloc;
+        OPJ_UINT32 i;
+
+        opj_aligned_free(p_code_block->decoded_data);
+        p_code_block->decoded_data = 00;
+
+        memset(p_code_block, 0, sizeof(opj_tcd_cblk_dec_t));
+        p_code_block->segs = l_segs;
+        p_code_block->m_current_max_segs = l_current_max_segs;
+        for (i = 0; i < l_current_max_segs; ++i) {
+            opj_tcd_reinit_segment(&l_segs[i]);
+        }
+        p_code_block->chunks = l_chunks;
+        p_code_block->numchunksalloc = l_numchunksalloc;
+    }
+
+    return OPJ_TRUE;
+}
+
+OPJ_UINT32 opj_tcd_get_decoded_tile_size(opj_tcd_t *p_tcd,
+        OPJ_BOOL take_into_account_partial_decoding)
+{
+    OPJ_UINT32 i;
+    OPJ_UINT32 l_data_size = 0;
+    opj_image_comp_t * l_img_comp = 00;
+    opj_tcd_tilecomp_t * l_tile_comp = 00;
+    opj_tcd_resolution_t * l_res = 00;
+    OPJ_UINT32 l_size_comp, l_remaining;
+    OPJ_UINT32 l_temp;
+
+    l_tile_comp = p_tcd->tcd_image->tiles->comps;
+    l_img_comp = p_tcd->image->comps;
+
+    for (i = 0; i < p_tcd->image->numcomps; ++i) {
+        OPJ_UINT32 w, h;
+        l_size_comp = l_img_comp->prec >> 3; /*(/ 8)*/
+        l_remaining = l_img_comp->prec & 7;  /* (%8) */
+
+        if (l_remaining) {
+            ++l_size_comp;
+        }
+
+        if (l_size_comp == 3) {
+            l_size_comp = 4;
+        }
+
+        l_res = l_tile_comp->resolutions + l_tile_comp->minimum_num_resolutions - 1;
+        if (take_into_account_partial_decoding && !p_tcd->whole_tile_decoding) {
+            w = l_res->win_x1 - l_res->win_x0;
+            h = l_res->win_y1 - l_res->win_y0;
+        } else {
+            w = (OPJ_UINT32)(l_res->x1 - l_res->x0);
+            h = (OPJ_UINT32)(l_res->y1 - l_res->y0);
+        }
+        if (h > 0 && UINT_MAX / w < h) {
+            return UINT_MAX;
+        }
+        l_temp = w * h;
+        if (l_size_comp && UINT_MAX / l_size_comp < l_temp) {
+            return UINT_MAX;
+        }
+        l_temp *= l_size_comp;
+
+        if (l_temp > UINT_MAX - l_data_size) {
+            return UINT_MAX;
+        }
+        l_data_size += l_temp;
+        ++l_img_comp;
+        ++l_tile_comp;
+    }
+
+    return l_data_size;
+}
+
+OPJ_BOOL opj_tcd_encode_tile(opj_tcd_t *p_tcd,
+                             OPJ_UINT32 p_tile_no,
+                             OPJ_BYTE *p_dest,
+                             OPJ_UINT32 * p_data_written,
+                             OPJ_UINT32 p_max_length,
+                             opj_codestream_info_t *p_cstr_info,
+                             opj_tcd_marker_info_t* p_marker_info,
+                             opj_event_mgr_t *p_manager)
+{
+
+    if (p_tcd->cur_tp_num == 0) {
+
+        p_tcd->tcd_tileno = p_tile_no;
+        p_tcd->tcp = &p_tcd->cp->tcps[p_tile_no];
+
+        /* INDEX >> "Precinct_nb_X et Precinct_nb_Y" */
+        if (p_cstr_info)  {
+            OPJ_UINT32 l_num_packs = 0;
+            OPJ_UINT32 i;
+            opj_tcd_tilecomp_t *l_tilec_idx =
+                &p_tcd->tcd_image->tiles->comps[0];        /* based on component 0 */
+            opj_tccp_t *l_tccp = p_tcd->tcp->tccps; /* based on component 0 */
+
+            for (i = 0; i < l_tilec_idx->numresolutions; i++) {
+                opj_tcd_resolution_t *l_res_idx = &l_tilec_idx->resolutions[i];
+
+                p_cstr_info->tile[p_tile_no].pw[i] = (int)l_res_idx->pw;
+                p_cstr_info->tile[p_tile_no].ph[i] = (int)l_res_idx->ph;
+
+                l_num_packs += l_res_idx->pw * l_res_idx->ph;
+                p_cstr_info->tile[p_tile_no].pdx[i] = (int)l_tccp->prcw[i];
+                p_cstr_info->tile[p_tile_no].pdy[i] = (int)l_tccp->prch[i];
+            }
+            p_cstr_info->tile[p_tile_no].packet = (opj_packet_info_t*) opj_calloc((
+                    OPJ_SIZE_T)p_cstr_info->numcomps * (OPJ_SIZE_T)p_cstr_info->numlayers *
+                                                  l_num_packs,
+                                                  sizeof(opj_packet_info_t));
+            if (!p_cstr_info->tile[p_tile_no].packet) {
+                /* FIXME event manager error callback */
+                return OPJ_FALSE;
+            }
+        }
+        /* << INDEX */
+
+        /* FIXME _ProfStart(PGROUP_DC_SHIFT); */
+        /*---------------TILE-------------------*/
+        if (! opj_tcd_dc_level_shift_encode(p_tcd)) {
+            return OPJ_FALSE;
+        }
+        /* FIXME _ProfStop(PGROUP_DC_SHIFT); */
+
+        /* FIXME _ProfStart(PGROUP_MCT); */
+        if (! opj_tcd_mct_encode(p_tcd)) {
+            return OPJ_FALSE;
+        }
+        /* FIXME _ProfStop(PGROUP_MCT); */
+
+        /* FIXME _ProfStart(PGROUP_DWT); */
+        if (! opj_tcd_dwt_encode(p_tcd)) {
+            return OPJ_FALSE;
+        }
+        /* FIXME  _ProfStop(PGROUP_DWT); */
+
+        /* FIXME  _ProfStart(PGROUP_T1); */
+        if (! opj_tcd_t1_encode(p_tcd)) {
+            return OPJ_FALSE;
+        }
+        /* FIXME _ProfStop(PGROUP_T1); */
+
+        /* FIXME _ProfStart(PGROUP_RATE); */
+        if (! opj_tcd_rate_allocate_encode(p_tcd, p_dest, p_max_length,
+                                           p_cstr_info, p_manager)) {
+            return OPJ_FALSE;
+        }
+        /* FIXME _ProfStop(PGROUP_RATE); */
+
+    }
+    /*--------------TIER2------------------*/
+
+    /* INDEX */
+    if (p_cstr_info) {
+        p_cstr_info->index_write = 1;
+    }
+    /* FIXME _ProfStart(PGROUP_T2); */
+
+    if (! opj_tcd_t2_encode(p_tcd, p_dest, p_data_written, p_max_length,
+                            p_cstr_info, p_marker_info, p_manager)) {
+        return OPJ_FALSE;
+    }
+    /* FIXME _ProfStop(PGROUP_T2); */
+
+    /*---------------CLEAN-------------------*/
+
+    return OPJ_TRUE;
+}
+
+OPJ_BOOL opj_tcd_decode_tile(opj_tcd_t *p_tcd,
+                             OPJ_UINT32 win_x0,
+                             OPJ_UINT32 win_y0,
+                             OPJ_UINT32 win_x1,
+                             OPJ_UINT32 win_y1,
+                             OPJ_UINT32 numcomps_to_decode,
+                             const OPJ_UINT32 *comps_indices,
+                             OPJ_BYTE *p_src,
+                             OPJ_UINT32 p_max_length,
+                             OPJ_UINT32 p_tile_no,
+                             opj_codestream_index_t *p_cstr_index,
+                             opj_event_mgr_t *p_manager
+                            )
+{
+    OPJ_UINT32 l_data_read;
+    OPJ_UINT32 compno;
+
+    p_tcd->tcd_tileno = p_tile_no;
+    p_tcd->tcp = &(p_tcd->cp->tcps[p_tile_no]);
+    p_tcd->win_x0 = win_x0;
+    p_tcd->win_y0 = win_y0;
+    p_tcd->win_x1 = win_x1;
+    p_tcd->win_y1 = win_y1;
+    p_tcd->whole_tile_decoding = OPJ_TRUE;
+
+    opj_free(p_tcd->used_component);
+    p_tcd->used_component = NULL;
+
+    if (numcomps_to_decode) {
+        OPJ_BOOL* used_component = (OPJ_BOOL*) opj_calloc(sizeof(OPJ_BOOL),
+                                   p_tcd->image->numcomps);
+        if (used_component == NULL) {
+            return OPJ_FALSE;
+        }
+        for (compno = 0; compno < numcomps_to_decode; compno++) {
+            used_component[ comps_indices[compno] ] = OPJ_TRUE;
+        }
+
+        p_tcd->used_component = used_component;
+    }
+
+    for (compno = 0; compno < p_tcd->image->numcomps; compno++) {
+        if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
+            continue;
+        }
+
+        if (!opj_tcd_is_whole_tilecomp_decoding(p_tcd, compno)) {
+            p_tcd->whole_tile_decoding = OPJ_FALSE;
+            break;
+        }
+    }
+
+    if (p_tcd->whole_tile_decoding) {
+        for (compno = 0; compno < p_tcd->image->numcomps; compno++) {
+            opj_tcd_tilecomp_t* tilec = &(p_tcd->tcd_image->tiles->comps[compno]);
+            opj_tcd_resolution_t *l_res = &
+                                          (tilec->resolutions[tilec->minimum_num_resolutions - 1]);
+            OPJ_SIZE_T l_data_size;
+
+            /* compute l_data_size with overflow check */
+            OPJ_SIZE_T res_w = (OPJ_SIZE_T)(l_res->x1 - l_res->x0);
+            OPJ_SIZE_T res_h = (OPJ_SIZE_T)(l_res->y1 - l_res->y0);
+
+            if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
+                continue;
+            }
+
+            /* issue 733, l_data_size == 0U, probably something wrong should be checked before getting here */
+            if (res_h > 0 && res_w > SIZE_MAX / res_h) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Size of tile data exceeds system limits\n");
+                return OPJ_FALSE;
+            }
+            l_data_size = res_w * res_h;
+
+            if (SIZE_MAX / sizeof(OPJ_UINT32) < l_data_size) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Size of tile data exceeds system limits\n");
+                return OPJ_FALSE;
+            }
+            l_data_size *= sizeof(OPJ_UINT32);
+
+            tilec->data_size_needed = l_data_size;
+
+            if (!opj_alloc_tile_component_data(tilec)) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Size of tile data exceeds system limits\n");
+                return OPJ_FALSE;
+            }
+        }
+    } else {
+        /* Compute restricted tile-component and tile-resolution coordinates */
+        /* of the window of interest, but defer the memory allocation until */
+        /* we know the resno_decoded */
+        for (compno = 0; compno < p_tcd->image->numcomps; compno++) {
+            OPJ_UINT32 resno;
+            opj_tcd_tilecomp_t* tilec = &(p_tcd->tcd_image->tiles->comps[compno]);
+            opj_image_comp_t* image_comp = &(p_tcd->image->comps[compno]);
+
+            if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
+                continue;
+            }
+
+            /* Compute the intersection of the area of interest, expressed in tile coordinates */
+            /* with the tile coordinates */
+            tilec->win_x0 = opj_uint_max(
+                                (OPJ_UINT32)tilec->x0,
+                                opj_uint_ceildiv(p_tcd->win_x0, image_comp->dx));
+            tilec->win_y0 = opj_uint_max(
+                                (OPJ_UINT32)tilec->y0,
+                                opj_uint_ceildiv(p_tcd->win_y0, image_comp->dy));
+            tilec->win_x1 = opj_uint_min(
+                                (OPJ_UINT32)tilec->x1,
+                                opj_uint_ceildiv(p_tcd->win_x1, image_comp->dx));
+            tilec->win_y1 = opj_uint_min(
+                                (OPJ_UINT32)tilec->y1,
+                                opj_uint_ceildiv(p_tcd->win_y1, image_comp->dy));
+            if (tilec->win_x1 < tilec->win_x0 ||
+                    tilec->win_y1 < tilec->win_y0) {
+                /* We should not normally go there. The circumstance is when */
+                /* the tile coordinates do not intersect the area of interest */
+                /* Upper level logic should not even try to decode that tile */
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Invalid tilec->win_xxx values\n");
+                return OPJ_FALSE;
+            }
+
+            for (resno = 0; resno < tilec->numresolutions; ++resno) {
+                opj_tcd_resolution_t *res = tilec->resolutions + resno;
+                res->win_x0 = opj_uint_ceildivpow2(tilec->win_x0,
+                                                   tilec->numresolutions - 1 - resno);
+                res->win_y0 = opj_uint_ceildivpow2(tilec->win_y0,
+                                                   tilec->numresolutions - 1 - resno);
+                res->win_x1 = opj_uint_ceildivpow2(tilec->win_x1,
+                                                   tilec->numresolutions - 1 - resno);
+                res->win_y1 = opj_uint_ceildivpow2(tilec->win_y1,
+                                                   tilec->numresolutions - 1 - resno);
+            }
+        }
+    }
+
+#ifdef TODO_MSD /* FIXME */
+    /* INDEX >>  */
+    if (p_cstr_info) {
+        OPJ_UINT32 resno, compno, numprec = 0;
+        for (compno = 0; compno < (OPJ_UINT32) p_cstr_info->numcomps; compno++) {
+            opj_tcp_t *tcp = &p_tcd->cp->tcps[0];
+            opj_tccp_t *tccp = &tcp->tccps[compno];
+            opj_tcd_tilecomp_t *tilec_idx = &p_tcd->tcd_image->tiles->comps[compno];
+            for (resno = 0; resno < tilec_idx->numresolutions; resno++) {
+                opj_tcd_resolution_t *res_idx = &tilec_idx->resolutions[resno];
+                p_cstr_info->tile[p_tile_no].pw[resno] = res_idx->pw;
+                p_cstr_info->tile[p_tile_no].ph[resno] = res_idx->ph;
+                numprec += res_idx->pw * res_idx->ph;
+                p_cstr_info->tile[p_tile_no].pdx[resno] = tccp->prcw[resno];
+                p_cstr_info->tile[p_tile_no].pdy[resno] = tccp->prch[resno];
+            }
+        }
+        p_cstr_info->tile[p_tile_no].packet = (opj_packet_info_t *) opj_malloc(
+                p_cstr_info->numlayers * numprec * sizeof(opj_packet_info_t));
+        p_cstr_info->packno = 0;
+    }
+    /* << INDEX */
+#endif
+
+    /*--------------TIER2------------------*/
+    /* FIXME _ProfStart(PGROUP_T2); */
+    l_data_read = 0;
+    if (! opj_tcd_t2_decode(p_tcd, p_src, &l_data_read, p_max_length, p_cstr_index,
+                            p_manager)) {
+        return OPJ_FALSE;
+    }
+    /* FIXME _ProfStop(PGROUP_T2); */
+
+    /*------------------TIER1-----------------*/
+
+    /* FIXME _ProfStart(PGROUP_T1); */
+    if (! opj_tcd_t1_decode(p_tcd, p_manager)) {
+        return OPJ_FALSE;
+    }
+    /* FIXME _ProfStop(PGROUP_T1); */
+
+
+    /* For subtile decoding, now we know the resno_decoded, we can allocate */
+    /* the tile data buffer */
+    if (!p_tcd->whole_tile_decoding) {
+        for (compno = 0; compno < p_tcd->image->numcomps; compno++) {
+            opj_tcd_tilecomp_t* tilec = &(p_tcd->tcd_image->tiles->comps[compno]);
+            opj_image_comp_t* image_comp = &(p_tcd->image->comps[compno]);
+            opj_tcd_resolution_t *res = tilec->resolutions + image_comp->resno_decoded;
+            OPJ_SIZE_T w = res->win_x1 - res->win_x0;
+            OPJ_SIZE_T h = res->win_y1 - res->win_y0;
+            OPJ_SIZE_T l_data_size;
+
+            opj_image_data_free(tilec->data_win);
+            tilec->data_win = NULL;
+
+            if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
+                continue;
+            }
+
+            if (w > 0 && h > 0) {
+                if (w > SIZE_MAX / h) {
+                    opj_event_msg(p_manager, EVT_ERROR,
+                                  "Size of tile data exceeds system limits\n");
+                    return OPJ_FALSE;
+                }
+                l_data_size = w * h;
+                if (l_data_size > SIZE_MAX / sizeof(OPJ_INT32)) {
+                    opj_event_msg(p_manager, EVT_ERROR,
+                                  "Size of tile data exceeds system limits\n");
+                    return OPJ_FALSE;
+                }
+                l_data_size *= sizeof(OPJ_INT32);
+
+                tilec->data_win = (OPJ_INT32*) opj_image_data_alloc(l_data_size);
+                if (tilec->data_win == NULL) {
+                    opj_event_msg(p_manager, EVT_ERROR,
+                                  "Size of tile data exceeds system limits\n");
+                    return OPJ_FALSE;
+                }
+            }
+        }
+    }
+
+    /*----------------DWT---------------------*/
+
+    /* FIXME _ProfStart(PGROUP_DWT); */
+    if
+    (! opj_tcd_dwt_decode(p_tcd)) {
+        return OPJ_FALSE;
+    }
+    /* FIXME _ProfStop(PGROUP_DWT); */
+
+    /*----------------MCT-------------------*/
+    /* FIXME _ProfStart(PGROUP_MCT); */
+    if
+    (! opj_tcd_mct_decode(p_tcd, p_manager)) {
+        return OPJ_FALSE;
+    }
+    /* FIXME _ProfStop(PGROUP_MCT); */
+
+    /* FIXME _ProfStart(PGROUP_DC_SHIFT); */
+    if
+    (! opj_tcd_dc_level_shift_decode(p_tcd)) {
+        return OPJ_FALSE;
+    }
+    /* FIXME _ProfStop(PGROUP_DC_SHIFT); */
+
+
+    /*---------------TILE-------------------*/
+    return OPJ_TRUE;
+}
+
+OPJ_BOOL opj_tcd_update_tile_data(opj_tcd_t *p_tcd,
+                                  OPJ_BYTE * p_dest,
+                                  OPJ_UINT32 p_dest_length
+                                 )
+{
+    OPJ_UINT32 i, j, k, l_data_size = 0;
+    opj_image_comp_t * l_img_comp = 00;
+    opj_tcd_tilecomp_t * l_tilec = 00;
+    opj_tcd_resolution_t * l_res;
+    OPJ_UINT32 l_size_comp, l_remaining;
+    OPJ_UINT32 l_stride, l_width, l_height;
+
+    l_data_size = opj_tcd_get_decoded_tile_size(p_tcd, OPJ_TRUE);
+    if (l_data_size == UINT_MAX || l_data_size > p_dest_length) {
+        return OPJ_FALSE;
+    }
+
+    l_tilec = p_tcd->tcd_image->tiles->comps;
+    l_img_comp = p_tcd->image->comps;
+
+    for (i = 0; i < p_tcd->image->numcomps; ++i) {
+        const OPJ_INT32* l_src_data;
+        l_size_comp = l_img_comp->prec >> 3; /*(/ 8)*/
+        l_remaining = l_img_comp->prec & 7;  /* (%8) */
+        l_res = l_tilec->resolutions + l_img_comp->resno_decoded;
+        if (p_tcd->whole_tile_decoding) {
+            l_width = (OPJ_UINT32)(l_res->x1 - l_res->x0);
+            l_height = (OPJ_UINT32)(l_res->y1 - l_res->y0);
+            l_stride = (OPJ_UINT32)(l_tilec->resolutions[l_tilec->minimum_num_resolutions -
+                                                                     1].x1 -
+                                    l_tilec->resolutions[l_tilec->minimum_num_resolutions - 1].x0) - l_width;
+            l_src_data = l_tilec->data;
+        } else {
+            l_width = l_res->win_x1 - l_res->win_x0;
+            l_height = l_res->win_y1 - l_res->win_y0;
+            l_stride = 0;
+            l_src_data = l_tilec->data_win;
+        }
+
+        if (l_remaining) {
+            ++l_size_comp;
+        }
+
+        if (l_size_comp == 3) {
+            l_size_comp = 4;
+        }
+
+        switch (l_size_comp) {
+        case 1: {
+            OPJ_CHAR * l_dest_ptr = (OPJ_CHAR *) p_dest;
+            const OPJ_INT32 * l_src_ptr = l_src_data;
+
+            if (l_img_comp->sgnd) {
+                for (j = 0; j < l_height; ++j) {
+                    for (k = 0; k < l_width; ++k) {
+                        *(l_dest_ptr++) = (OPJ_CHAR)(*(l_src_ptr++));
+                    }
+                    l_src_ptr += l_stride;
+                }
+            } else {
+                for (j = 0; j < l_height; ++j) {
+                    for (k = 0; k < l_width; ++k) {
+                        *(l_dest_ptr++) = (OPJ_CHAR)((*(l_src_ptr++)) & 0xff);
+                    }
+                    l_src_ptr += l_stride;
+                }
+            }
+
+            p_dest = (OPJ_BYTE *)l_dest_ptr;
+        }
+        break;
+        case 2: {
+            const OPJ_INT32 * l_src_ptr = l_src_data;
+            OPJ_INT16 * l_dest_ptr = (OPJ_INT16 *) p_dest;
+
+            if (l_img_comp->sgnd) {
+                for (j = 0; j < l_height; ++j) {
+                    for (k = 0; k < l_width; ++k) {
+                        OPJ_INT16 val = (OPJ_INT16)(*(l_src_ptr++));
+                        memcpy(l_dest_ptr, &val, sizeof(val));
+                        l_dest_ptr ++;
+                    }
+                    l_src_ptr += l_stride;
+                }
+            } else {
+                for (j = 0; j < l_height; ++j) {
+                    for (k = 0; k < l_width; ++k) {
+                        OPJ_INT16 val = (OPJ_INT16)((*(l_src_ptr++)) & 0xffff);
+                        memcpy(l_dest_ptr, &val, sizeof(val));
+                        l_dest_ptr ++;
+                    }
+                    l_src_ptr += l_stride;
+                }
+            }
+
+            p_dest = (OPJ_BYTE*) l_dest_ptr;
+        }
+        break;
+        case 4: {
+            OPJ_INT32 * l_dest_ptr = (OPJ_INT32 *) p_dest;
+            const OPJ_INT32 * l_src_ptr = l_src_data;
+
+            for (j = 0; j < l_height; ++j) {
+                memcpy(l_dest_ptr, l_src_ptr, l_width * sizeof(OPJ_INT32));
+                l_dest_ptr += l_width;
+                l_src_ptr += l_width + l_stride;
+            }
+
+            p_dest = (OPJ_BYTE*) l_dest_ptr;
+        }
+        break;
+        }
+
+        ++l_img_comp;
+        ++l_tilec;
+    }
+
+    return OPJ_TRUE;
+}
+
+
+
+
+static void opj_tcd_free_tile(opj_tcd_t *p_tcd)
+{
+    OPJ_UINT32 compno, resno, bandno, precno;
+    opj_tcd_tile_t *l_tile = 00;
+    opj_tcd_tilecomp_t *l_tile_comp = 00;
+    opj_tcd_resolution_t *l_res = 00;
+    opj_tcd_band_t *l_band = 00;
+    opj_tcd_precinct_t *l_precinct = 00;
+    OPJ_UINT32 l_nb_resolutions, l_nb_precincts;
+    void (* l_tcd_code_block_deallocate)(opj_tcd_precinct_t *) = 00;
+
+    if (! p_tcd) {
+        return;
+    }
+
+    if (! p_tcd->tcd_image) {
+        return;
+    }
+
+    if (p_tcd->m_is_decoder) {
+        l_tcd_code_block_deallocate = opj_tcd_code_block_dec_deallocate;
+    } else {
+        l_tcd_code_block_deallocate = opj_tcd_code_block_enc_deallocate;
+    }
+
+    l_tile = p_tcd->tcd_image->tiles;
+    if (! l_tile) {
+        return;
+    }
+
+    l_tile_comp = l_tile->comps;
+
+    for (compno = 0; compno < l_tile->numcomps; ++compno) {
+        l_res = l_tile_comp->resolutions;
+        if (l_res) {
+
+            l_nb_resolutions = l_tile_comp->resolutions_size / (OPJ_UINT32)sizeof(
+                                   opj_tcd_resolution_t);
+            for (resno = 0; resno < l_nb_resolutions; ++resno) {
+                l_band = l_res->bands;
+                for (bandno = 0; bandno < 3; ++bandno) {
+                    l_precinct = l_band->precincts;
+                    if (l_precinct) {
+
+                        l_nb_precincts = l_band->precincts_data_size / (OPJ_UINT32)sizeof(
+                                             opj_tcd_precinct_t);
+                        for (precno = 0; precno < l_nb_precincts; ++precno) {
+                            opj_tgt_destroy(l_precinct->incltree);
+                            l_precinct->incltree = 00;
+                            opj_tgt_destroy(l_precinct->imsbtree);
+                            l_precinct->imsbtree = 00;
+                            (*l_tcd_code_block_deallocate)(l_precinct);
+                            ++l_precinct;
+                        }
+
+                        opj_free(l_band->precincts);
+                        l_band->precincts = 00;
+                    }
+                    ++l_band;
+                } /* for (resno */
+                ++l_res;
+            }
+
+            opj_free(l_tile_comp->resolutions);
+            l_tile_comp->resolutions = 00;
+        }
+
+        if (l_tile_comp->ownsData && l_tile_comp->data) {
+            opj_image_data_free(l_tile_comp->data);
+            l_tile_comp->data = 00;
+            l_tile_comp->ownsData = 0;
+            l_tile_comp->data_size = 0;
+            l_tile_comp->data_size_needed = 0;
+        }
+
+        opj_image_data_free(l_tile_comp->data_win);
+
+        ++l_tile_comp;
+    }
+
+    opj_free(l_tile->comps);
+    l_tile->comps = 00;
+    opj_free(p_tcd->tcd_image->tiles);
+    p_tcd->tcd_image->tiles = 00;
+}
+
+
+static OPJ_BOOL opj_tcd_t2_decode(opj_tcd_t *p_tcd,
+                                  OPJ_BYTE * p_src_data,
+                                  OPJ_UINT32 * p_data_read,
+                                  OPJ_UINT32 p_max_src_size,
+                                  opj_codestream_index_t *p_cstr_index,
+                                  opj_event_mgr_t *p_manager
+                                 )
+{
+    opj_t2_t * l_t2;
+
+    l_t2 = opj_t2_create(p_tcd->image, p_tcd->cp);
+    if (l_t2 == 00) {
+        return OPJ_FALSE;
+    }
+
+    if (! opj_t2_decode_packets(
+                p_tcd,
+                l_t2,
+                p_tcd->tcd_tileno,
+                p_tcd->tcd_image->tiles,
+                p_src_data,
+                p_data_read,
+                p_max_src_size,
+                p_cstr_index,
+                p_manager)) {
+        opj_t2_destroy(l_t2);
+        return OPJ_FALSE;
+    }
+
+    opj_t2_destroy(l_t2);
+
+    /*---------------CLEAN-------------------*/
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_tcd_t1_decode(opj_tcd_t *p_tcd, opj_event_mgr_t *p_manager)
+{
+    OPJ_UINT32 compno;
+    opj_tcd_tile_t * l_tile = p_tcd->tcd_image->tiles;
+    opj_tcd_tilecomp_t* l_tile_comp = l_tile->comps;
+    opj_tccp_t * l_tccp = p_tcd->tcp->tccps;
+    volatile OPJ_BOOL ret = OPJ_TRUE;
+    OPJ_BOOL check_pterm = OPJ_FALSE;
+    opj_mutex_t* p_manager_mutex = NULL;
+
+    p_manager_mutex = opj_mutex_create();
+
+    /* Only enable PTERM check if we decode all layers */
+    if (p_tcd->tcp->num_layers_to_decode == p_tcd->tcp->numlayers &&
+            (l_tccp->cblksty & J2K_CCP_CBLKSTY_PTERM) != 0) {
+        check_pterm = OPJ_TRUE;
+    }
+
+    for (compno = 0; compno < l_tile->numcomps;
+            ++compno, ++l_tile_comp, ++l_tccp) {
+        if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
+            continue;
+        }
+
+        opj_t1_decode_cblks(p_tcd, &ret, l_tile_comp, l_tccp,
+                            p_manager, p_manager_mutex, check_pterm);
+        if (!ret) {
+            break;
+        }
+    }
+
+    opj_thread_pool_wait_completion(p_tcd->thread_pool, 0);
+    if (p_manager_mutex) {
+        opj_mutex_destroy(p_manager_mutex);
+    }
+    return ret;
+}
+
+
+static OPJ_BOOL opj_tcd_dwt_decode(opj_tcd_t *p_tcd)
+{
+    OPJ_UINT32 compno;
+    opj_tcd_tile_t * l_tile = p_tcd->tcd_image->tiles;
+    opj_tcd_tilecomp_t * l_tile_comp = l_tile->comps;
+    opj_tccp_t * l_tccp = p_tcd->tcp->tccps;
+    opj_image_comp_t * l_img_comp = p_tcd->image->comps;
+
+    for (compno = 0; compno < l_tile->numcomps;
+            compno++, ++l_tile_comp, ++l_img_comp, ++l_tccp) {
+        if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
+            continue;
+        }
+
+        if (l_tccp->qmfbid == 1) {
+            if (! opj_dwt_decode(p_tcd, l_tile_comp,
+                                 l_img_comp->resno_decoded + 1)) {
+                return OPJ_FALSE;
+            }
+        } else {
+            if (! opj_dwt_decode_real(p_tcd, l_tile_comp,
+                                      l_img_comp->resno_decoded + 1)) {
+                return OPJ_FALSE;
+            }
+        }
+
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_tcd_mct_decode(opj_tcd_t *p_tcd, opj_event_mgr_t *p_manager)
+{
+    opj_tcd_tile_t * l_tile = p_tcd->tcd_image->tiles;
+    opj_tcp_t * l_tcp = p_tcd->tcp;
+    opj_tcd_tilecomp_t * l_tile_comp = l_tile->comps;
+    OPJ_SIZE_T l_samples;
+    OPJ_UINT32 i;
+
+    if (l_tcp->mct == 0 || p_tcd->used_component != NULL) {
+        return OPJ_TRUE;
+    }
+
+    if (p_tcd->whole_tile_decoding) {
+        opj_tcd_resolution_t* res_comp0 = l_tile->comps[0].resolutions +
+                                          l_tile_comp->minimum_num_resolutions - 1;
+
+        /* A bit inefficient: we process more data than needed if */
+        /* resno_decoded < l_tile_comp->minimum_num_resolutions-1, */
+        /* but we would need to take into account a stride then */
+        l_samples = (OPJ_SIZE_T)(res_comp0->x1 - res_comp0->x0) *
+                    (OPJ_SIZE_T)(res_comp0->y1 - res_comp0->y0);
+        if (l_tile->numcomps >= 3) {
+            if (l_tile_comp->minimum_num_resolutions !=
+                    l_tile->comps[1].minimum_num_resolutions ||
+                    l_tile_comp->minimum_num_resolutions !=
+                    l_tile->comps[2].minimum_num_resolutions) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Tiles don't all have the same dimension. Skip the MCT step.\n");
+                return OPJ_FALSE;
+            }
+        }
+        if (l_tile->numcomps >= 3) {
+            opj_tcd_resolution_t* res_comp1 = l_tile->comps[1].resolutions +
+                                              l_tile_comp->minimum_num_resolutions - 1;
+            opj_tcd_resolution_t* res_comp2 = l_tile->comps[2].resolutions +
+                                              l_tile_comp->minimum_num_resolutions - 1;
+            /* testcase 1336.pdf.asan.47.376 */
+            if (p_tcd->image->comps[0].resno_decoded !=
+                    p_tcd->image->comps[1].resno_decoded ||
+                    p_tcd->image->comps[0].resno_decoded !=
+                    p_tcd->image->comps[2].resno_decoded ||
+                    (OPJ_SIZE_T)(res_comp1->x1 - res_comp1->x0) *
+                    (OPJ_SIZE_T)(res_comp1->y1 - res_comp1->y0) != l_samples ||
+                    (OPJ_SIZE_T)(res_comp2->x1 - res_comp2->x0) *
+                    (OPJ_SIZE_T)(res_comp2->y1 - res_comp2->y0) != l_samples) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Tiles don't all have the same dimension. Skip the MCT step.\n");
+                return OPJ_FALSE;
+            }
+        }
+    } else {
+        opj_tcd_resolution_t* res_comp0 = l_tile->comps[0].resolutions +
+                                          p_tcd->image->comps[0].resno_decoded;
+
+        l_samples = (OPJ_SIZE_T)(res_comp0->win_x1 - res_comp0->win_x0) *
+                    (OPJ_SIZE_T)(res_comp0->win_y1 - res_comp0->win_y0);
+        if (l_tile->numcomps >= 3) {
+            opj_tcd_resolution_t* res_comp1 = l_tile->comps[1].resolutions +
+                                              p_tcd->image->comps[1].resno_decoded;
+            opj_tcd_resolution_t* res_comp2 = l_tile->comps[2].resolutions +
+                                              p_tcd->image->comps[2].resno_decoded;
+            /* testcase 1336.pdf.asan.47.376 */
+            if (p_tcd->image->comps[0].resno_decoded !=
+                    p_tcd->image->comps[1].resno_decoded ||
+                    p_tcd->image->comps[0].resno_decoded !=
+                    p_tcd->image->comps[2].resno_decoded ||
+                    (OPJ_SIZE_T)(res_comp1->win_x1 - res_comp1->win_x0) *
+                    (OPJ_SIZE_T)(res_comp1->win_y1 - res_comp1->win_y0) != l_samples ||
+                    (OPJ_SIZE_T)(res_comp2->win_x1 - res_comp2->win_x0) *
+                    (OPJ_SIZE_T)(res_comp2->win_y1 - res_comp2->win_y0) != l_samples) {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Tiles don't all have the same dimension. Skip the MCT step.\n");
+                return OPJ_FALSE;
+            }
+        }
+    }
+
+    if (l_tile->numcomps >= 3) {
+        if (l_tcp->mct == 2) {
+            OPJ_BYTE ** l_data;
+
+            if (! l_tcp->m_mct_decoding_matrix) {
+                return OPJ_TRUE;
+            }
+
+            l_data = (OPJ_BYTE **) opj_malloc(l_tile->numcomps * sizeof(OPJ_BYTE*));
+            if (! l_data) {
+                return OPJ_FALSE;
+            }
+
+            for (i = 0; i < l_tile->numcomps; ++i) {
+                if (p_tcd->whole_tile_decoding) {
+                    l_data[i] = (OPJ_BYTE*) l_tile_comp->data;
+                } else {
+                    l_data[i] = (OPJ_BYTE*) l_tile_comp->data_win;
+                }
+                ++l_tile_comp;
+            }
+
+            if (! opj_mct_decode_custom(/* MCT data */
+                        (OPJ_BYTE*) l_tcp->m_mct_decoding_matrix,
+                        /* size of components */
+                        l_samples,
+                        /* components */
+                        l_data,
+                        /* nb of components (i.e. size of pData) */
+                        l_tile->numcomps,
+                        /* tells if the data is signed */
+                        p_tcd->image->comps->sgnd)) {
+                opj_free(l_data);
+                return OPJ_FALSE;
+            }
+
+            opj_free(l_data);
+        } else {
+            if (l_tcp->tccps->qmfbid == 1) {
+                if (p_tcd->whole_tile_decoding) {
+                    opj_mct_decode(l_tile->comps[0].data,
+                                   l_tile->comps[1].data,
+                                   l_tile->comps[2].data,
+                                   l_samples);
+                } else {
+                    opj_mct_decode(l_tile->comps[0].data_win,
+                                   l_tile->comps[1].data_win,
+                                   l_tile->comps[2].data_win,
+                                   l_samples);
+                }
+            } else {
+                if (p_tcd->whole_tile_decoding) {
+                    opj_mct_decode_real((OPJ_FLOAT32*)l_tile->comps[0].data,
+                                        (OPJ_FLOAT32*)l_tile->comps[1].data,
+                                        (OPJ_FLOAT32*)l_tile->comps[2].data,
+                                        l_samples);
+                } else {
+                    opj_mct_decode_real((OPJ_FLOAT32*)l_tile->comps[0].data_win,
+                                        (OPJ_FLOAT32*)l_tile->comps[1].data_win,
+                                        (OPJ_FLOAT32*)l_tile->comps[2].data_win,
+                                        l_samples);
+                }
+            }
+        }
+    } else {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Number of components (%d) is inconsistent with a MCT. Skip the MCT step.\n",
+                      l_tile->numcomps);
+    }
+
+    return OPJ_TRUE;
+}
+
+
+static OPJ_BOOL opj_tcd_dc_level_shift_decode(opj_tcd_t *p_tcd)
+{
+    OPJ_UINT32 compno;
+    opj_tcd_tilecomp_t * l_tile_comp = 00;
+    opj_tccp_t * l_tccp = 00;
+    opj_image_comp_t * l_img_comp = 00;
+    opj_tcd_resolution_t* l_res = 00;
+    opj_tcd_tile_t * l_tile;
+    OPJ_UINT32 l_width, l_height, i, j;
+    OPJ_INT32 * l_current_ptr;
+    OPJ_INT32 l_min, l_max;
+    OPJ_UINT32 l_stride;
+
+    l_tile = p_tcd->tcd_image->tiles;
+    l_tile_comp = l_tile->comps;
+    l_tccp = p_tcd->tcp->tccps;
+    l_img_comp = p_tcd->image->comps;
+
+    for (compno = 0; compno < l_tile->numcomps;
+            compno++, ++l_img_comp, ++l_tccp, ++l_tile_comp) {
+
+        if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
+            continue;
+        }
+
+        l_res = l_tile_comp->resolutions + l_img_comp->resno_decoded;
+
+        if (!p_tcd->whole_tile_decoding) {
+            l_width = l_res->win_x1 - l_res->win_x0;
+            l_height = l_res->win_y1 - l_res->win_y0;
+            l_stride = 0;
+            l_current_ptr = l_tile_comp->data_win;
+        } else {
+            l_width = (OPJ_UINT32)(l_res->x1 - l_res->x0);
+            l_height = (OPJ_UINT32)(l_res->y1 - l_res->y0);
+            l_stride = (OPJ_UINT32)(
+                           l_tile_comp->resolutions[l_tile_comp->minimum_num_resolutions - 1].x1 -
+                           l_tile_comp->resolutions[l_tile_comp->minimum_num_resolutions - 1].x0)
+                       - l_width;
+            l_current_ptr = l_tile_comp->data;
+
+            assert(l_height == 0 ||
+                   l_width + l_stride <= l_tile_comp->data_size / l_height); /*MUPDF*/
+        }
+
+        if (l_img_comp->sgnd) {
+            l_min = -(1 << (l_img_comp->prec - 1));
+            l_max = (1 << (l_img_comp->prec - 1)) - 1;
+        } else {
+            l_min = 0;
+            l_max = (OPJ_INT32)((1U << l_img_comp->prec) - 1);
+        }
+
+
+        if (l_tccp->qmfbid == 1) {
+            for (j = 0; j < l_height; ++j) {
+                for (i = 0; i < l_width; ++i) {
+                    /* TODO: do addition on int64 ? */
+                    *l_current_ptr = opj_int_clamp(*l_current_ptr + l_tccp->m_dc_level_shift, l_min,
+                                                   l_max);
+                    ++l_current_ptr;
+                }
+                l_current_ptr += l_stride;
+            }
+        } else {
+            for (j = 0; j < l_height; ++j) {
+                for (i = 0; i < l_width; ++i) {
+                    OPJ_FLOAT32 l_value = *((OPJ_FLOAT32 *) l_current_ptr);
+                    if (l_value > INT_MAX) {
+                        *l_current_ptr = l_max;
+                    } else if (l_value < INT_MIN) {
+                        *l_current_ptr = l_min;
+                    } else {
+                        /* Do addition on int64 to avoid overflows */
+                        OPJ_INT64 l_value_int = (OPJ_INT64)opj_lrintf(l_value);
+                        *l_current_ptr = (OPJ_INT32)opj_int64_clamp(
+                                             l_value_int + l_tccp->m_dc_level_shift, l_min, l_max);
+                    }
+                    ++l_current_ptr;
+                }
+                l_current_ptr += l_stride;
+            }
+        }
+    }
+
+    return OPJ_TRUE;
+}
+
+
+
+/**
+ * Deallocates the encoding data of the given precinct.
+ */
+static void opj_tcd_code_block_dec_deallocate(opj_tcd_precinct_t * p_precinct)
+{
+    OPJ_UINT32 cblkno, l_nb_code_blocks;
+
+    opj_tcd_cblk_dec_t * l_code_block = p_precinct->cblks.dec;
+    if (l_code_block) {
+        /*fprintf(stderr,"deallocate codeblock:{\n");*/
+        /*fprintf(stderr,"\t x0=%d, y0=%d, x1=%d, y1=%d\n",l_code_block->x0, l_code_block->y0, l_code_block->x1, l_code_block->y1);*/
+        /*fprintf(stderr,"\t numbps=%d, numlenbits=%d, len=%d, numnewpasses=%d, real_num_segs=%d, m_current_max_segs=%d\n ",
+                        l_code_block->numbps, l_code_block->numlenbits, l_code_block->len, l_code_block->numnewpasses, l_code_block->real_num_segs, l_code_block->m_current_max_segs );*/
+
+
+        l_nb_code_blocks = p_precinct->block_size / (OPJ_UINT32)sizeof(
+                               opj_tcd_cblk_dec_t);
+        /*fprintf(stderr,"nb_code_blocks =%d\t}\n", l_nb_code_blocks);*/
+
+        for (cblkno = 0; cblkno < l_nb_code_blocks; ++cblkno) {
+
+            if (l_code_block->segs) {
+                opj_free(l_code_block->segs);
+                l_code_block->segs = 00;
+            }
+
+            if (l_code_block->chunks) {
+                opj_free(l_code_block->chunks);
+                l_code_block->chunks = 00;
+            }
+
+            opj_aligned_free(l_code_block->decoded_data);
+            l_code_block->decoded_data = NULL;
+
+            ++l_code_block;
+        }
+
+        opj_free(p_precinct->cblks.dec);
+        p_precinct->cblks.dec = 00;
+    }
+}
+
+/**
+ * Deallocates the encoding data of the given precinct.
+ */
+static void opj_tcd_code_block_enc_deallocate(opj_tcd_precinct_t * p_precinct)
+{
+    OPJ_UINT32 cblkno, l_nb_code_blocks;
+
+    opj_tcd_cblk_enc_t * l_code_block = p_precinct->cblks.enc;
+    if (l_code_block) {
+        l_nb_code_blocks = p_precinct->block_size / (OPJ_UINT32)sizeof(
+                               opj_tcd_cblk_enc_t);
+
+        for (cblkno = 0; cblkno < l_nb_code_blocks; ++cblkno)  {
+            if (l_code_block->data) {
+                /* We refer to data - 1 since below we incremented it */
+                /* in opj_tcd_code_block_enc_allocate_data() */
+                opj_free(l_code_block->data - 1);
+                l_code_block->data = 00;
+            }
+
+            if (l_code_block->layers) {
+                opj_free(l_code_block->layers);
+                l_code_block->layers = 00;
+            }
+
+            if (l_code_block->passes) {
+                opj_free(l_code_block->passes);
+                l_code_block->passes = 00;
+            }
+            ++l_code_block;
+        }
+
+        opj_free(p_precinct->cblks.enc);
+
+        p_precinct->cblks.enc = 00;
+    }
+}
+
+OPJ_SIZE_T opj_tcd_get_encoder_input_buffer_size(opj_tcd_t *p_tcd)
+{
+    OPJ_UINT32 i;
+    OPJ_SIZE_T l_data_size = 0;
+    opj_image_comp_t * l_img_comp = 00;
+    opj_tcd_tilecomp_t * l_tilec = 00;
+    OPJ_UINT32 l_size_comp, l_remaining;
+
+    l_tilec = p_tcd->tcd_image->tiles->comps;
+    l_img_comp = p_tcd->image->comps;
+    for (i = 0; i < p_tcd->image->numcomps; ++i) {
+        l_size_comp = l_img_comp->prec >> 3; /*(/ 8)*/
+        l_remaining = l_img_comp->prec & 7;  /* (%8) */
+
+        if (l_remaining) {
+            ++l_size_comp;
+        }
+
+        if (l_size_comp == 3) {
+            l_size_comp = 4;
+        }
+
+        l_data_size += l_size_comp * ((OPJ_SIZE_T)(l_tilec->x1 - l_tilec->x0) *
+                                      (OPJ_SIZE_T)(l_tilec->y1 - l_tilec->y0));
+        ++l_img_comp;
+        ++l_tilec;
+    }
+
+    return l_data_size;
+}
+
+static OPJ_BOOL opj_tcd_dc_level_shift_encode(opj_tcd_t *p_tcd)
+{
+    OPJ_UINT32 compno;
+    opj_tcd_tilecomp_t * l_tile_comp = 00;
+    opj_tccp_t * l_tccp = 00;
+    opj_image_comp_t * l_img_comp = 00;
+    opj_tcd_tile_t * l_tile;
+    OPJ_SIZE_T l_nb_elem, i;
+    OPJ_INT32 * l_current_ptr;
+
+    l_tile = p_tcd->tcd_image->tiles;
+    l_tile_comp = l_tile->comps;
+    l_tccp = p_tcd->tcp->tccps;
+    l_img_comp = p_tcd->image->comps;
+
+    for (compno = 0; compno < l_tile->numcomps; compno++) {
+        l_current_ptr = l_tile_comp->data;
+        l_nb_elem = (OPJ_SIZE_T)(l_tile_comp->x1 - l_tile_comp->x0) *
+                    (OPJ_SIZE_T)(l_tile_comp->y1 - l_tile_comp->y0);
+
+        if (l_tccp->qmfbid == 1) {
+            for (i = 0; i < l_nb_elem; ++i) {
+                *l_current_ptr -= l_tccp->m_dc_level_shift ;
+                ++l_current_ptr;
+            }
+        } else {
+            for (i = 0; i < l_nb_elem; ++i) {
+                *((OPJ_FLOAT32 *) l_current_ptr) = (OPJ_FLOAT32)(*l_current_ptr -
+                                                   l_tccp->m_dc_level_shift);
+                ++l_current_ptr;
+            }
+        }
+
+        ++l_img_comp;
+        ++l_tccp;
+        ++l_tile_comp;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_tcd_mct_encode(opj_tcd_t *p_tcd)
+{
+    opj_tcd_tile_t * l_tile = p_tcd->tcd_image->tiles;
+    opj_tcd_tilecomp_t * l_tile_comp = p_tcd->tcd_image->tiles->comps;
+    OPJ_SIZE_T samples = (OPJ_SIZE_T)(l_tile_comp->x1 - l_tile_comp->x0) *
+                         (OPJ_SIZE_T)(l_tile_comp->y1 - l_tile_comp->y0);
+    OPJ_UINT32 i;
+    OPJ_BYTE ** l_data = 00;
+    opj_tcp_t * l_tcp = p_tcd->tcp;
+
+    if (!p_tcd->tcp->mct) {
+        return OPJ_TRUE;
+    }
+
+    if (p_tcd->tcp->mct == 2) {
+        if (! p_tcd->tcp->m_mct_coding_matrix) {
+            return OPJ_TRUE;
+        }
+
+        l_data = (OPJ_BYTE **) opj_malloc(l_tile->numcomps * sizeof(OPJ_BYTE*));
+        if (! l_data) {
+            return OPJ_FALSE;
+        }
+
+        for (i = 0; i < l_tile->numcomps; ++i) {
+            l_data[i] = (OPJ_BYTE*) l_tile_comp->data;
+            ++l_tile_comp;
+        }
+
+        if (! opj_mct_encode_custom(/* MCT data */
+                    (OPJ_BYTE*) p_tcd->tcp->m_mct_coding_matrix,
+                    /* size of components */
+                    samples,
+                    /* components */
+                    l_data,
+                    /* nb of components (i.e. size of pData) */
+                    l_tile->numcomps,
+                    /* tells if the data is signed */
+                    p_tcd->image->comps->sgnd)) {
+            opj_free(l_data);
+            return OPJ_FALSE;
+        }
+
+        opj_free(l_data);
+    } else if (l_tcp->tccps->qmfbid == 0) {
+        opj_mct_encode_real(
+            (OPJ_FLOAT32*)l_tile->comps[0].data,
+            (OPJ_FLOAT32*)l_tile->comps[1].data,
+            (OPJ_FLOAT32*)l_tile->comps[2].data,
+            samples);
+    } else {
+        opj_mct_encode(l_tile->comps[0].data, l_tile->comps[1].data,
+                       l_tile->comps[2].data, samples);
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_tcd_dwt_encode(opj_tcd_t *p_tcd)
+{
+    opj_tcd_tile_t * l_tile = p_tcd->tcd_image->tiles;
+    opj_tcd_tilecomp_t * l_tile_comp = p_tcd->tcd_image->tiles->comps;
+    opj_tccp_t * l_tccp = p_tcd->tcp->tccps;
+    OPJ_UINT32 compno;
+
+    for (compno = 0; compno < l_tile->numcomps; ++compno) {
+        if (l_tccp->qmfbid == 1) {
+            if (! opj_dwt_encode(p_tcd, l_tile_comp)) {
+                return OPJ_FALSE;
+            }
+        } else if (l_tccp->qmfbid == 0) {
+            if (! opj_dwt_encode_real(p_tcd, l_tile_comp)) {
+                return OPJ_FALSE;
+            }
+        }
+
+        ++l_tile_comp;
+        ++l_tccp;
+    }
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_tcd_t1_encode(opj_tcd_t *p_tcd)
+{
+    const OPJ_FLOAT64 * l_mct_norms;
+    OPJ_UINT32 l_mct_numcomps = 0U;
+    opj_tcp_t * l_tcp = p_tcd->tcp;
+
+    if (l_tcp->mct == 1) {
+        l_mct_numcomps = 3U;
+        /* irreversible encoding */
+        if (l_tcp->tccps->qmfbid == 0) {
+            l_mct_norms = opj_mct_get_mct_norms_real();
+        } else {
+            l_mct_norms = opj_mct_get_mct_norms();
+        }
+    } else {
+        l_mct_numcomps = p_tcd->image->numcomps;
+        l_mct_norms = (const OPJ_FLOAT64 *)(l_tcp->mct_norms);
+    }
+
+    return opj_t1_encode_cblks(p_tcd,
+                               p_tcd->tcd_image->tiles, l_tcp, l_mct_norms,
+                               l_mct_numcomps);
+
+    return OPJ_TRUE;
+}
+
+static OPJ_BOOL opj_tcd_t2_encode(opj_tcd_t *p_tcd,
+                                  OPJ_BYTE * p_dest_data,
+                                  OPJ_UINT32 * p_data_written,
+                                  OPJ_UINT32 p_max_dest_size,
+                                  opj_codestream_info_t *p_cstr_info,
+                                  opj_tcd_marker_info_t* p_marker_info,
+                                  opj_event_mgr_t *p_manager)
+{
+    opj_t2_t * l_t2;
+
+    l_t2 = opj_t2_create(p_tcd->image, p_tcd->cp);
+    if (l_t2 == 00) {
+        return OPJ_FALSE;
+    }
+
+    if (! opj_t2_encode_packets(
+                l_t2,
+                p_tcd->tcd_tileno,
+                p_tcd->tcd_image->tiles,
+                p_tcd->tcp->numlayers,
+                p_dest_data,
+                p_data_written,
+                p_max_dest_size,
+                p_cstr_info,
+                p_marker_info,
+                p_tcd->tp_num,
+                p_tcd->tp_pos,
+                p_tcd->cur_pino,
+                FINAL_PASS,
+                p_manager)) {
+        opj_t2_destroy(l_t2);
+        return OPJ_FALSE;
+    }
+
+    opj_t2_destroy(l_t2);
+
+    /*---------------CLEAN-------------------*/
+    return OPJ_TRUE;
+}
+
+
+static OPJ_BOOL opj_tcd_rate_allocate_encode(opj_tcd_t *p_tcd,
+        OPJ_BYTE * p_dest_data,
+        OPJ_UINT32 p_max_dest_size,
+        opj_codestream_info_t *p_cstr_info,
+        opj_event_mgr_t *p_manager)
+{
+    opj_cp_t * l_cp = p_tcd->cp;
+    OPJ_UINT32 l_nb_written = 0;
+
+    if (p_cstr_info)  {
+        p_cstr_info->index_write = 0;
+    }
+
+    if (l_cp->m_specific_param.m_enc.m_disto_alloc ||
+            l_cp->m_specific_param.m_enc.m_fixed_quality)  {
+        /* fixed_quality */
+        /* Normal Rate/distortion allocation */
+        if (! opj_tcd_rateallocate(p_tcd, p_dest_data, &l_nb_written, p_max_dest_size,
+                                   p_cstr_info, p_manager)) {
+            return OPJ_FALSE;
+        }
+    } else {
+        /* Fixed layer allocation */
+        opj_tcd_rateallocate_fixed(p_tcd);
+    }
+
+    return OPJ_TRUE;
+}
+
+
+OPJ_BOOL opj_tcd_copy_tile_data(opj_tcd_t *p_tcd,
+                                OPJ_BYTE * p_src,
+                                OPJ_SIZE_T p_src_length)
+{
+    OPJ_UINT32 i;
+    OPJ_SIZE_T j;
+    OPJ_SIZE_T l_data_size = 0;
+    opj_image_comp_t * l_img_comp = 00;
+    opj_tcd_tilecomp_t * l_tilec = 00;
+    OPJ_UINT32 l_size_comp, l_remaining;
+    OPJ_SIZE_T l_nb_elem;
+
+    l_data_size = opj_tcd_get_encoder_input_buffer_size(p_tcd);
+    if (l_data_size != p_src_length) {
+        return OPJ_FALSE;
+    }
+
+    l_tilec = p_tcd->tcd_image->tiles->comps;
+    l_img_comp = p_tcd->image->comps;
+    for (i = 0; i < p_tcd->image->numcomps; ++i) {
+        l_size_comp = l_img_comp->prec >> 3; /*(/ 8)*/
+        l_remaining = l_img_comp->prec & 7;  /* (%8) */
+        l_nb_elem = (OPJ_SIZE_T)(l_tilec->x1 - l_tilec->x0) *
+                    (OPJ_SIZE_T)(l_tilec->y1 - l_tilec->y0);
+
+        if (l_remaining) {
+            ++l_size_comp;
+        }
+
+        if (l_size_comp == 3) {
+            l_size_comp = 4;
+        }
+
+        switch (l_size_comp) {
+        case 1: {
+            OPJ_CHAR * l_src_ptr = (OPJ_CHAR *) p_src;
+            OPJ_INT32 * l_dest_ptr = l_tilec->data;
+
+            if (l_img_comp->sgnd) {
+                for (j = 0; j < l_nb_elem; ++j) {
+                    *(l_dest_ptr++) = (OPJ_INT32)(*(l_src_ptr++));
+                }
+            } else {
+                for (j = 0; j < l_nb_elem; ++j) {
+                    *(l_dest_ptr++) = (*(l_src_ptr++)) & 0xff;
+                }
+            }
+
+            p_src = (OPJ_BYTE*) l_src_ptr;
+        }
+        break;
+        case 2: {
+            OPJ_INT32 * l_dest_ptr = l_tilec->data;
+            OPJ_INT16 * l_src_ptr = (OPJ_INT16 *) p_src;
+
+            if (l_img_comp->sgnd) {
+                for (j = 0; j < l_nb_elem; ++j) {
+                    *(l_dest_ptr++) = (OPJ_INT32)(*(l_src_ptr++));
+                }
+            } else {
+                for (j = 0; j < l_nb_elem; ++j) {
+                    *(l_dest_ptr++) = (*(l_src_ptr++)) & 0xffff;
+                }
+            }
+
+            p_src = (OPJ_BYTE*) l_src_ptr;
+        }
+        break;
+        case 4: {
+            OPJ_INT32 * l_src_ptr = (OPJ_INT32 *) p_src;
+            OPJ_INT32 * l_dest_ptr = l_tilec->data;
+
+            for (j = 0; j < l_nb_elem; ++j) {
+                *(l_dest_ptr++) = (OPJ_INT32)(*(l_src_ptr++));
+            }
+
+            p_src = (OPJ_BYTE*) l_src_ptr;
+        }
+        break;
+        }
+
+        ++l_img_comp;
+        ++l_tilec;
+    }
+
+    return OPJ_TRUE;
+}
+
+OPJ_BOOL opj_tcd_is_band_empty(opj_tcd_band_t* band)
+{
+    return (band->x1 - band->x0 == 0) || (band->y1 - band->y0 == 0);
+}
+
+OPJ_BOOL opj_tcd_is_subband_area_of_interest(opj_tcd_t *tcd,
+        OPJ_UINT32 compno,
+        OPJ_UINT32 resno,
+        OPJ_UINT32 bandno,
+        OPJ_UINT32 band_x0,
+        OPJ_UINT32 band_y0,
+        OPJ_UINT32 band_x1,
+        OPJ_UINT32 band_y1)
+{
+    /* Note: those values for filter_margin are in part the result of */
+    /* experimentation. The value 2 for QMFBID=1 (5x3 filter) can be linked */
+    /* to the maximum left/right extension given in tables F.2 and F.3 of the */
+    /* standard. The value 3 for QMFBID=0 (9x7 filter) is more suspicious, */
+    /* since F.2 and F.3 would lead to 4 instead, so the current 3 might be */
+    /* needed to be bumped to 4, in case inconsistencies are found while */
+    /* decoding parts of irreversible coded images. */
+    /* See opj_dwt_decode_partial_53 and opj_dwt_decode_partial_97 as well */
+    OPJ_UINT32 filter_margin = (tcd->tcp->tccps[compno].qmfbid == 1) ? 2 : 3;
+    opj_tcd_tilecomp_t *tilec = &(tcd->tcd_image->tiles->comps[compno]);
+    opj_image_comp_t* image_comp = &(tcd->image->comps[compno]);
+    /* Compute the intersection of the area of interest, expressed in tile coordinates */
+    /* with the tile coordinates */
+    OPJ_UINT32 tcx0 = opj_uint_max(
+                          (OPJ_UINT32)tilec->x0,
+                          opj_uint_ceildiv(tcd->win_x0, image_comp->dx));
+    OPJ_UINT32 tcy0 = opj_uint_max(
+                          (OPJ_UINT32)tilec->y0,
+                          opj_uint_ceildiv(tcd->win_y0, image_comp->dy));
+    OPJ_UINT32 tcx1 = opj_uint_min(
+                          (OPJ_UINT32)tilec->x1,
+                          opj_uint_ceildiv(tcd->win_x1, image_comp->dx));
+    OPJ_UINT32 tcy1 = opj_uint_min(
+                          (OPJ_UINT32)tilec->y1,
+                          opj_uint_ceildiv(tcd->win_y1, image_comp->dy));
+    /* Compute number of decomposition for this band. See table F-1 */
+    OPJ_UINT32 nb = (resno == 0) ?
+                    tilec->numresolutions - 1 :
+                    tilec->numresolutions - resno;
+    /* Map above tile-based coordinates to sub-band-based coordinates per */
+    /* equation B-15 of the standard */
+    OPJ_UINT32 x0b = bandno & 1;
+    OPJ_UINT32 y0b = bandno >> 1;
+    OPJ_UINT32 tbx0 = (nb == 0) ? tcx0 :
+                      (tcx0 <= (1U << (nb - 1)) * x0b) ? 0 :
+                      opj_uint_ceildivpow2(tcx0 - (1U << (nb - 1)) * x0b, nb);
+    OPJ_UINT32 tby0 = (nb == 0) ? tcy0 :
+                      (tcy0 <= (1U << (nb - 1)) * y0b) ? 0 :
+                      opj_uint_ceildivpow2(tcy0 - (1U << (nb - 1)) * y0b, nb);
+    OPJ_UINT32 tbx1 = (nb == 0) ? tcx1 :
+                      (tcx1 <= (1U << (nb - 1)) * x0b) ? 0 :
+                      opj_uint_ceildivpow2(tcx1 - (1U << (nb - 1)) * x0b, nb);
+    OPJ_UINT32 tby1 = (nb == 0) ? tcy1 :
+                      (tcy1 <= (1U << (nb - 1)) * y0b) ? 0 :
+                      opj_uint_ceildivpow2(tcy1 - (1U << (nb - 1)) * y0b, nb);
+    OPJ_BOOL intersects;
+
+    if (tbx0 < filter_margin) {
+        tbx0 = 0;
+    } else {
+        tbx0 -= filter_margin;
+    }
+    if (tby0 < filter_margin) {
+        tby0 = 0;
+    } else {
+        tby0 -= filter_margin;
+    }
+    tbx1 = opj_uint_adds(tbx1, filter_margin);
+    tby1 = opj_uint_adds(tby1, filter_margin);
+
+    intersects = band_x0 < tbx1 && band_y0 < tby1 && band_x1 > tbx0 &&
+                 band_y1 > tby0;
+
+#ifdef DEBUG_VERBOSE
+    printf("compno=%u resno=%u nb=%u bandno=%u x0b=%u y0b=%u band=%u,%u,%u,%u tb=%u,%u,%u,%u -> %u\n",
+           compno, resno, nb, bandno, x0b, y0b,
+           band_x0, band_y0, band_x1, band_y1,
+           tbx0, tby0, tbx1, tby1, intersects);
+#endif
+    return intersects;
+}
+
+/** Returns whether a tile componenent is fully decoded, taking into account
+ * p_tcd->win_* members.
+ *
+ * @param p_tcd    TCD handle.
+ * @param compno Component number
+ * @return OPJ_TRUE whether the tile componenent is fully decoded
+ */
+static OPJ_BOOL opj_tcd_is_whole_tilecomp_decoding(opj_tcd_t *p_tcd,
+        OPJ_UINT32 compno)
+{
+    opj_tcd_tilecomp_t* tilec = &(p_tcd->tcd_image->tiles->comps[compno]);
+    opj_image_comp_t* image_comp = &(p_tcd->image->comps[compno]);
+    /* Compute the intersection of the area of interest, expressed in tile coordinates */
+    /* with the tile coordinates */
+    OPJ_UINT32 tcx0 = opj_uint_max(
+                          (OPJ_UINT32)tilec->x0,
+                          opj_uint_ceildiv(p_tcd->win_x0, image_comp->dx));
+    OPJ_UINT32 tcy0 = opj_uint_max(
+                          (OPJ_UINT32)tilec->y0,
+                          opj_uint_ceildiv(p_tcd->win_y0, image_comp->dy));
+    OPJ_UINT32 tcx1 = opj_uint_min(
+                          (OPJ_UINT32)tilec->x1,
+                          opj_uint_ceildiv(p_tcd->win_x1, image_comp->dx));
+    OPJ_UINT32 tcy1 = opj_uint_min(
+                          (OPJ_UINT32)tilec->y1,
+                          opj_uint_ceildiv(p_tcd->win_y1, image_comp->dy));
+
+    OPJ_UINT32 shift = tilec->numresolutions - tilec->minimum_num_resolutions;
+    /* Tolerate small margin within the reduced resolution factor to consider if */
+    /* the whole tile path must be taken */
+    return (tcx0 >= (OPJ_UINT32)tilec->x0 &&
+            tcy0 >= (OPJ_UINT32)tilec->y0 &&
+            tcx1 <= (OPJ_UINT32)tilec->x1 &&
+            tcy1 <= (OPJ_UINT32)tilec->y1 &&
+            (shift >= 32 ||
+             (((tcx0 - (OPJ_UINT32)tilec->x0) >> shift) == 0 &&
+              ((tcy0 - (OPJ_UINT32)tilec->y0) >> shift) == 0 &&
+              (((OPJ_UINT32)tilec->x1 - tcx1) >> shift) == 0 &&
+              (((OPJ_UINT32)tilec->y1 - tcy1) >> shift) == 0)));
+}
+
+/* ----------------------------------------------------------------------- */
+
+opj_tcd_marker_info_t* opj_tcd_marker_info_create(OPJ_BOOL need_PLT)
+{
+    opj_tcd_marker_info_t *l_tcd_marker_info =
+        (opj_tcd_marker_info_t*) opj_calloc(1, sizeof(opj_tcd_marker_info_t));
+    if (!l_tcd_marker_info) {
+        return NULL;
+    }
+
+    l_tcd_marker_info->need_PLT = need_PLT;
+
+    return l_tcd_marker_info;
+}
+
+/* ----------------------------------------------------------------------- */
+
+void opj_tcd_marker_info_destroy(opj_tcd_marker_info_t *p_tcd_marker_info)
+{
+    if (p_tcd_marker_info) {
+        opj_free(p_tcd_marker_info->p_packet_size);
+        opj_free(p_tcd_marker_info);
+    }
+}
+
+/* ----------------------------------------------------------------------- */
diff --git a/third_party/libopenjpeg/tcd.h b/third_party/libopenjpeg/tcd.h
new file mode 100644
index 0000000..340c2bf
--- /dev/null
+++ b/third_party/libopenjpeg/tcd.h
@@ -0,0 +1,528 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
+ * Copyright (c) 2012, CS Systemes d'Information, France
+ * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef OPJ_TCD_H
+#define OPJ_TCD_H
+/**
+@file tcd.h
+@brief Implementation of a tile coder/decoder (TCD)
+
+The functions in TCD.C encode or decode each tile independently from
+each other. The functions in TCD.C are used by other functions in J2K.C.
+*/
+
+/** @defgroup TCD TCD - Implementation of a tile coder/decoder */
+/*@{*/
+
+
+/**
+FIXME DOC
+*/
+typedef struct opj_tcd_pass {
+    OPJ_UINT32 rate;
+    OPJ_FLOAT64 distortiondec;
+    OPJ_UINT32 len;
+    OPJ_BITFIELD term : 1;
+} opj_tcd_pass_t;
+
+/**
+FIXME DOC
+*/
+typedef struct opj_tcd_layer {
+    OPJ_UINT32 numpasses;       /* Number of passes in the layer */
+    OPJ_UINT32 len;             /* len of information */
+    OPJ_FLOAT64 disto;          /* add for index (Cfr. Marcela) */
+    OPJ_BYTE *data;             /* data */
+} opj_tcd_layer_t;
+
+/**
+FIXME DOC
+*/
+typedef struct opj_tcd_cblk_enc {
+    OPJ_BYTE* data;               /* Data */
+    opj_tcd_layer_t* layers;      /* layer information */
+    opj_tcd_pass_t* passes;       /* information about the passes */
+    OPJ_INT32 x0, y0, x1,
+              y1;     /* dimension of the code-blocks : left upper corner (x0, y0) right low corner (x1,y1) */
+    OPJ_UINT32 numbps;
+    OPJ_UINT32 numlenbits;
+    OPJ_UINT32 data_size;         /* Size of allocated data buffer */
+    OPJ_UINT32
+    numpasses;         /* number of pass already done for the code-blocks */
+    OPJ_UINT32 numpassesinlayers; /* number of passes in the layer */
+    OPJ_UINT32 totalpasses;       /* total number of passes */
+} opj_tcd_cblk_enc_t;
+
+
+/** Chunk of codestream data that is part of a code block */
+typedef struct opj_tcd_seg_data_chunk {
+    /* Point to tilepart buffer. We don't make a copy !
+       So the tilepart buffer must be kept alive
+       as long as we need to decode the codeblocks */
+    OPJ_BYTE * data;
+    OPJ_UINT32 len;                 /* Usable length of data */
+} opj_tcd_seg_data_chunk_t;
+
+/** Segment of a code-block.
+ * A segment represent a number of consecutive coding passes, without termination
+ * of MQC or RAW between them. */
+typedef struct opj_tcd_seg {
+    OPJ_UINT32 len;      /* Size of data related to this segment */
+    /* Number of passes decoded. Including those that we skip */
+    OPJ_UINT32 numpasses;
+    /* Number of passes actually to be decoded. To be used for code-block decoding */
+    OPJ_UINT32 real_num_passes;
+    /* Maximum number of passes for this segment */
+    OPJ_UINT32 maxpasses;
+    /* Number of new passes for current packed. Transitory value */
+    OPJ_UINT32 numnewpasses;
+    /* Codestream length for this segment for current packed. Transitory value */
+    OPJ_UINT32 newlen;
+} opj_tcd_seg_t;
+
+/** Code-block for decoding */
+typedef struct opj_tcd_cblk_dec {
+    opj_tcd_seg_t* segs;            /* segments information */
+    opj_tcd_seg_data_chunk_t* chunks; /* Array of chunks */
+    /* position of the code-blocks : left upper corner (x0, y0) right low corner (x1,y1) */
+    OPJ_INT32 x0, y0, x1, y1;
+    /* Mb is The maximum number of bit-planes available for the representation of
+       coefficients in any sub-band, b, as defined in Equation (E-2). See
+       Section B.10.5 of the standard */
+    OPJ_UINT32 Mb;  /* currently used only to check if HT decoding is correct */
+    /* numbps is Mb - P as defined in Section B.10.5 of the standard */
+    OPJ_UINT32 numbps;
+    /* number of bits for len, for the current packet. Transitory value */
+    OPJ_UINT32 numlenbits;
+    /* number of pass added to the code-blocks, for the current packet. Transitory value */
+    OPJ_UINT32 numnewpasses;
+    /* number of segments, including those of packet we skip */
+    OPJ_UINT32 numsegs;
+    /* number of segments, to be used for code block decoding */
+    OPJ_UINT32 real_num_segs;
+    OPJ_UINT32 m_current_max_segs;  /* allocated number of segs[] items */
+    OPJ_UINT32 numchunks;           /* Number of valid chunks items */
+    OPJ_UINT32 numchunksalloc;      /* Number of chunks item allocated */
+    /* Decoded code-block. Only used for subtile decoding. Otherwise tilec->data is directly updated */
+    OPJ_INT32* decoded_data;
+} opj_tcd_cblk_dec_t;
+
+/** Precinct structure */
+typedef struct opj_tcd_precinct {
+    /* dimension of the precinct : left upper corner (x0, y0) right low corner (x1,y1) */
+    OPJ_INT32 x0, y0, x1, y1;
+    OPJ_UINT32 cw, ch;              /* number of code-blocks, in width and height */
+    union {                         /* code-blocks information */
+        opj_tcd_cblk_enc_t* enc;
+        opj_tcd_cblk_dec_t* dec;
+        void*               blocks;
+    } cblks;
+    OPJ_UINT32 block_size;          /* size taken by cblks (in bytes) */
+    opj_tgt_tree_t *incltree;       /* inclusion tree */
+    opj_tgt_tree_t *imsbtree;       /* IMSB tree */
+} opj_tcd_precinct_t;
+
+/** Sub-band structure */
+typedef struct opj_tcd_band {
+    /* dimension of the subband : left upper corner (x0, y0) right low corner (x1,y1) */
+    OPJ_INT32 x0, y0, x1, y1;
+    /* band number: for lowest resolution level (0=LL), otherwise (1=HL, 2=LH, 3=HH) */
+    OPJ_UINT32 bandno;
+    /* precinct information */
+    opj_tcd_precinct_t *precincts;
+    /* size of data taken by precincts */
+    OPJ_UINT32 precincts_data_size;
+    OPJ_INT32 numbps;
+    OPJ_FLOAT32 stepsize;
+} opj_tcd_band_t;
+
+/** Tile-component resolution structure */
+typedef struct opj_tcd_resolution {
+    /* dimension of the resolution level : left upper corner (x0, y0) right low corner (x1,y1) */
+    OPJ_INT32 x0, y0, x1, y1;
+    /* number of precincts, in width and height, for this resolution level */
+    OPJ_UINT32 pw, ph;
+    /* number of sub-bands for the resolution level (1 for lowest resolution level, 3 otherwise) */
+    OPJ_UINT32 numbands;
+    /* subband information */
+    opj_tcd_band_t bands[3];
+
+    /* dimension of the resolution limited to window of interest. Only valid if tcd->whole_tile_decoding is set */
+    OPJ_UINT32 win_x0;
+    OPJ_UINT32 win_y0;
+    OPJ_UINT32 win_x1;
+    OPJ_UINT32 win_y1;
+} opj_tcd_resolution_t;
+
+/** Tile-component structure */
+typedef struct opj_tcd_tilecomp {
+    /* dimension of component : left upper corner (x0, y0) right low corner (x1,y1) */
+    OPJ_INT32 x0, y0, x1, y1;
+    /* component number */
+    OPJ_UINT32 compno;
+    /* number of resolutions level */
+    OPJ_UINT32 numresolutions;
+    /* number of resolutions level to decode (at max)*/
+    OPJ_UINT32 minimum_num_resolutions;
+    /* resolutions information */
+    opj_tcd_resolution_t *resolutions;
+    /* size of data for resolutions (in bytes) */
+    OPJ_UINT32 resolutions_size;
+
+    /* data of the component. For decoding, only valid if tcd->whole_tile_decoding is set (so exclusive of data_win member) */
+    OPJ_INT32 *data;
+    /* if true, then need to free after usage, otherwise do not free */
+    OPJ_BOOL  ownsData;
+    /* we may either need to allocate this amount of data, or re-use image data and ignore this value */
+    size_t data_size_needed;
+    /* size of the data of the component */
+    size_t data_size;
+
+    /** data of the component limited to window of interest. Only valid for decoding and if tcd->whole_tile_decoding is NOT set (so exclusive of data member) */
+    OPJ_INT32 *data_win;
+    /* dimension of the component limited to window of interest. Only valid for decoding and  if tcd->whole_tile_decoding is NOT set */
+    OPJ_UINT32 win_x0;
+    OPJ_UINT32 win_y0;
+    OPJ_UINT32 win_x1;
+    OPJ_UINT32 win_y1;
+
+    /* add fixed_quality */
+    OPJ_INT32 numpix;
+} opj_tcd_tilecomp_t;
+
+
+/**
+FIXME DOC
+*/
+typedef struct opj_tcd_tile {
+    /* dimension of the tile : left upper corner (x0, y0) right low corner (x1,y1) */
+    OPJ_INT32 x0, y0, x1, y1;
+    OPJ_UINT32 numcomps;            /* number of components in tile */
+    opj_tcd_tilecomp_t *comps;  /* Components information */
+    OPJ_INT32 numpix;               /* add fixed_quality */
+    OPJ_FLOAT64 distotile;          /* add fixed_quality */
+    OPJ_FLOAT64 distolayer[100];    /* add fixed_quality */
+    OPJ_UINT32 packno;              /* packet number */
+} opj_tcd_tile_t;
+
+/**
+FIXME DOC
+*/
+typedef struct opj_tcd_image {
+    opj_tcd_tile_t *tiles;      /* Tiles information */
+}
+opj_tcd_image_t;
+
+
+/**
+Tile coder/decoder
+*/
+typedef struct opj_tcd {
+    /** Position of the tilepart flag in Progression order*/
+    OPJ_INT32 tp_pos;
+    /** Tile part number*/
+    OPJ_UINT32 tp_num;
+    /** Current tile part number*/
+    OPJ_UINT32 cur_tp_num;
+    /** Total number of tileparts of the current tile*/
+    OPJ_UINT32 cur_totnum_tp;
+    /** Current Packet iterator number */
+    OPJ_UINT32 cur_pino;
+    /** info on each image tile */
+    opj_tcd_image_t *tcd_image;
+    /** image header */
+    opj_image_t *image;
+    /** coding parameters */
+    opj_cp_t *cp;
+    /** coding/decoding parameters common to all tiles */
+    opj_tcp_t *tcp;
+    /** current encoded/decoded tile */
+    OPJ_UINT32 tcd_tileno;
+    /** tell if the tcd is a decoder. */
+    OPJ_BITFIELD m_is_decoder : 1;
+    /** Thread pool */
+    opj_thread_pool_t* thread_pool;
+    /** Coordinates of the window of interest, in grid reference space */
+    OPJ_UINT32 win_x0;
+    OPJ_UINT32 win_y0;
+    OPJ_UINT32 win_x1;
+    OPJ_UINT32 win_y1;
+    /** Only valid for decoding. Whether the whole tile is decoded, or just the region in win_x0/win_y0/win_x1/win_y1 */
+    OPJ_BOOL   whole_tile_decoding;
+    /* Array of size image->numcomps indicating if a component must be decoded. NULL if all components must be decoded */
+    OPJ_BOOL* used_component;
+} opj_tcd_t;
+
+/**
+ * Structure to hold information needed to generate some markers.
+ * Used by encoder.
+ */
+typedef struct opj_tcd_marker_info {
+    /** In: Whether information to generate PLT markers in needed */
+    OPJ_BOOL    need_PLT;
+
+    /** OUT: Number of elements in p_packet_size[] array */
+    OPJ_UINT32  packet_count;
+
+    /** OUT: Array of size packet_count, such that p_packet_size[i] is
+     *       the size in bytes of the ith packet */
+    OPJ_UINT32* p_packet_size;
+} opj_tcd_marker_info_t;
+
+/** @name Exported functions */
+/*@{*/
+/* ----------------------------------------------------------------------- */
+
+/**
+Dump the content of a tcd structure
+*/
+/*void tcd_dump(FILE *fd, opj_tcd_t *tcd, opj_tcd_image_t *img);*/ /* TODO MSD shoul use the new v2 structures */
+
+/**
+Create a new TCD handle
+@param p_is_decoder FIXME DOC
+@return Returns a new TCD handle if successful returns NULL otherwise
+*/
+opj_tcd_t* opj_tcd_create(OPJ_BOOL p_is_decoder);
+
+/**
+Destroy a previously created TCD handle
+@param tcd TCD handle to destroy
+*/
+void opj_tcd_destroy(opj_tcd_t *tcd);
+
+
+/**
+ * Create a new opj_tcd_marker_info_t* structure
+ * @param need_PLT Whether information is needed to generate PLT markers.
+ */
+opj_tcd_marker_info_t* opj_tcd_marker_info_create(OPJ_BOOL need_PLT);
+
+
+/**
+Destroy a previously created opj_tcd_marker_info_t* structure
+@param p_tcd_marker_info Structure to destroy
+*/
+void opj_tcd_marker_info_destroy(opj_tcd_marker_info_t *p_tcd_marker_info);
+
+
+/**
+ * Initialize the tile coder and may reuse some memory.
+ * @param   p_tcd       TCD handle.
+ * @param   p_image     raw image.
+ * @param   p_cp        coding parameters.
+ * @param   p_tp        thread pool
+ *
+ * @return true if the encoding values could be set (false otherwise).
+*/
+OPJ_BOOL opj_tcd_init(opj_tcd_t *p_tcd,
+                      opj_image_t * p_image,
+                      opj_cp_t * p_cp,
+                      opj_thread_pool_t* p_tp);
+
+/**
+ * Allocates memory for decoding a specific tile.
+ *
+ * @param   p_tcd       the tile decoder.
+ * @param   p_tile_no   the index of the tile received in sequence. This not necessarily lead to the
+ * tile at index p_tile_no.
+ * @param p_manager the event manager.
+ *
+ * @return  true if the remaining data is sufficient.
+ */
+OPJ_BOOL opj_tcd_init_decode_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
+                                  opj_event_mgr_t* p_manager);
+
+void opj_tcd_makelayer_fixed(opj_tcd_t *tcd, OPJ_UINT32 layno,
+                             OPJ_UINT32 final);
+
+void opj_tcd_rateallocate_fixed(opj_tcd_t *tcd);
+
+void opj_tcd_makelayer(opj_tcd_t *tcd,
+                       OPJ_UINT32 layno,
+                       OPJ_FLOAT64 thresh,
+                       OPJ_UINT32 final);
+
+OPJ_BOOL opj_tcd_rateallocate(opj_tcd_t *tcd,
+                              OPJ_BYTE *dest,
+                              OPJ_UINT32 * p_data_written,
+                              OPJ_UINT32 len,
+                              opj_codestream_info_t *cstr_info,
+                              opj_event_mgr_t *p_manager);
+
+/**
+ * Gets the maximum tile size that will be taken by the tile once decoded.
+ */
+OPJ_UINT32 opj_tcd_get_decoded_tile_size(opj_tcd_t *p_tcd,
+        OPJ_BOOL take_into_account_partial_decoding);
+
+/**
+ * Encodes a tile from the raw image into the given buffer.
+ * @param   p_tcd           Tile Coder handle
+ * @param   p_tile_no       Index of the tile to encode.
+ * @param   p_dest          Destination buffer
+ * @param   p_data_written  pointer to an int that is incremented by the number of bytes really written on p_dest
+ * @param   p_len           Maximum length of the destination buffer
+ * @param   p_cstr_info     Codestream information structure
+ * @param   p_marker_info   Marker information structure
+ * @param   p_manager       the user event manager
+ * @return  true if the coding is successful.
+*/
+OPJ_BOOL opj_tcd_encode_tile(opj_tcd_t *p_tcd,
+                             OPJ_UINT32 p_tile_no,
+                             OPJ_BYTE *p_dest,
+                             OPJ_UINT32 * p_data_written,
+                             OPJ_UINT32 p_len,
+                             struct opj_codestream_info *p_cstr_info,
+                             opj_tcd_marker_info_t* p_marker_info,
+                             opj_event_mgr_t *p_manager);
+
+
+/**
+Decode a tile from a buffer into a raw image
+@param tcd TCD handle
+@param win_x0 Upper left x of region to decode (in grid coordinates)
+@param win_y0 Upper left y of region to decode (in grid coordinates)
+@param win_x1 Lower right x of region to decode (in grid coordinates)
+@param win_y1 Lower right y of region to decode (in grid coordinates)
+@param numcomps_to_decode  Size of the comps_indices array, or 0 if decoding all components.
+@param comps_indices   Array of numcomps values representing the indices
+                       of the components to decode (relative to the
+                       codestream, starting at 0). Or NULL if decoding all components.
+@param src Source buffer
+@param len Length of source buffer
+@param tileno Number that identifies one of the tiles to be decoded
+@param cstr_info  FIXME DOC
+@param manager the event manager.
+*/
+OPJ_BOOL opj_tcd_decode_tile(opj_tcd_t *tcd,
+                             OPJ_UINT32 win_x0,
+                             OPJ_UINT32 win_y0,
+                             OPJ_UINT32 win_x1,
+                             OPJ_UINT32 win_y1,
+                             OPJ_UINT32 numcomps_to_decode,
+                             const OPJ_UINT32 *comps_indices,
+                             OPJ_BYTE *src,
+                             OPJ_UINT32 len,
+                             OPJ_UINT32 tileno,
+                             opj_codestream_index_t *cstr_info,
+                             opj_event_mgr_t *manager);
+
+
+/**
+ * Copies tile data from the system onto the given memory block.
+ */
+OPJ_BOOL opj_tcd_update_tile_data(opj_tcd_t *p_tcd,
+                                  OPJ_BYTE * p_dest,
+                                  OPJ_UINT32 p_dest_length);
+
+/**
+ * Get the size in bytes of the input buffer provided before encoded.
+ * This must be the size provided to the p_src_length argument of
+ * opj_tcd_copy_tile_data()
+ */
+OPJ_SIZE_T opj_tcd_get_encoder_input_buffer_size(opj_tcd_t *p_tcd);
+
+/**
+ * Initialize the tile coder and may reuse some meory.
+ *
+ * @param   p_tcd       TCD handle.
+ * @param   p_tile_no   current tile index to encode.
+ * @param p_manager the event manager.
+ *
+ * @return true if the encoding values could be set (false otherwise).
+*/
+OPJ_BOOL opj_tcd_init_encode_tile(opj_tcd_t *p_tcd,
+                                  OPJ_UINT32 p_tile_no, opj_event_mgr_t* p_manager);
+
+/**
+ * Copies tile data from the given memory block onto the system.
+ *
+ * p_src_length must be equal to opj_tcd_get_encoder_input_buffer_size()
+ */
+OPJ_BOOL opj_tcd_copy_tile_data(opj_tcd_t *p_tcd,
+                                OPJ_BYTE * p_src,
+                                OPJ_SIZE_T p_src_length);
+
+/**
+ * Allocates tile component data
+ *
+ *
+ */
+OPJ_BOOL opj_alloc_tile_component_data(opj_tcd_tilecomp_t *l_tilec);
+
+/** Returns whether a sub-band is empty (i.e. whether it has a null area)
+ * @param band Sub-band handle.
+ * @return OPJ_TRUE whether the sub-band is empty.
+ */
+OPJ_BOOL opj_tcd_is_band_empty(opj_tcd_band_t* band);
+
+/** Reinitialize a segment */
+void opj_tcd_reinit_segment(opj_tcd_seg_t* seg);
+
+
+/** Returns whether a sub-band region contributes to the area of interest
+ * tcd->win_x0,tcd->win_y0,tcd->win_x1,tcd->win_y1.
+ *
+ * @param tcd    TCD handle.
+ * @param compno Component number
+ * @param resno  Resolution number
+ * @param bandno Band number (*not* band index, ie 0, 1, 2 or 3)
+ * @param x0     Upper left x in subband coordinates
+ * @param y0     Upper left y in subband coordinates
+ * @param x1     Lower right x in subband coordinates
+ * @param y1     Lower right y in subband coordinates
+ * @return OPJ_TRUE whether the sub-band region contributs to the area of
+ *                  interest.
+ */
+OPJ_BOOL opj_tcd_is_subband_area_of_interest(opj_tcd_t *tcd,
+        OPJ_UINT32 compno,
+        OPJ_UINT32 resno,
+        OPJ_UINT32 bandno,
+        OPJ_UINT32 x0,
+        OPJ_UINT32 y0,
+        OPJ_UINT32 x1,
+        OPJ_UINT32 y1);
+
+/* ----------------------------------------------------------------------- */
+/*@}*/
+
+/*@}*/
+
+#endif /* OPJ_TCD_H */
diff --git a/third_party/libopenjpeg20/tgt.c b/third_party/libopenjpeg/tgt.c
similarity index 100%
rename from third_party/libopenjpeg20/tgt.c
rename to third_party/libopenjpeg/tgt.c
diff --git a/third_party/libopenjpeg20/tgt.h b/third_party/libopenjpeg/tgt.h
similarity index 100%
rename from third_party/libopenjpeg20/tgt.h
rename to third_party/libopenjpeg/tgt.h
diff --git a/third_party/libopenjpeg20/thread.c b/third_party/libopenjpeg/thread.c
similarity index 100%
rename from third_party/libopenjpeg20/thread.c
rename to third_party/libopenjpeg/thread.c
diff --git a/third_party/libopenjpeg20/thread.h b/third_party/libopenjpeg/thread.h
similarity index 100%
rename from third_party/libopenjpeg20/thread.h
rename to third_party/libopenjpeg/thread.h
diff --git a/third_party/libopenjpeg20/tls_keys.h b/third_party/libopenjpeg/tls_keys.h
similarity index 100%
rename from third_party/libopenjpeg20/tls_keys.h
rename to third_party/libopenjpeg/tls_keys.h
diff --git a/third_party/libopenjpeg20/0003-dwt-decode.patch b/third_party/libopenjpeg20/0003-dwt-decode.patch
deleted file mode 100644
index 94d4b41..0000000
--- a/third_party/libopenjpeg20/0003-dwt-decode.patch
+++ /dev/null
@@ -1,264 +0,0 @@
-diff --git a/third_party/libopenjpeg20/dwt.c b/third_party/libopenjpeg20/dwt.c
-index 5930d1c71..6512b1e4c 100644
---- a/third_party/libopenjpeg20/dwt.c
-+++ b/third_party/libopenjpeg20/dwt.c
-@@ -63,9 +63,6 @@
- /** @defgroup DWT DWT - Implementation of a discrete wavelet transform */
- /*@{*/
- 
--#define OPJ_WS(i) v->mem[(i)*2]
--#define OPJ_WD(i) v->mem[(1+(i)*2)]
--
- #ifdef __AVX2__
- /** Number of int32 values in a AVX2 register */
- #define VREG_INT_COUNT       8
-@@ -82,6 +79,7 @@
- 
- typedef struct dwt_local {
-     OPJ_INT32* mem;
-+    OPJ_SIZE_T mem_count;
-     OPJ_INT32 dn;   /* number of elements in high pass band */
-     OPJ_INT32 sn;   /* number of elements in low pass band */
-     OPJ_INT32 cas;  /* 0 = start on even coord, 1 = start on odd coord */
-@@ -133,13 +131,13 @@ static void opj_dwt_deinterleave_v(OPJ_INT32 *a, OPJ_INT32 *b, OPJ_INT32 dn,
- /**
- Forward 5-3 wavelet transform in 1-D
- */
--static void opj_dwt_encode_1(OPJ_INT32 *a, OPJ_INT32 dn, OPJ_INT32 sn,
--                             OPJ_INT32 cas);
-+static void opj_dwt_encode_1(OPJ_INT32 *a, OPJ_SIZE_T a_count, OPJ_INT32 dn,
-+    OPJ_INT32 sn, OPJ_INT32 cas);
- /**
- Forward 9-7 wavelet transform in 1-D
- */
--static void opj_dwt_encode_1_real(OPJ_INT32 *a, OPJ_INT32 dn, OPJ_INT32 sn,
--                                  OPJ_INT32 cas);
-+static void opj_dwt_encode_1_real(OPJ_INT32 *a, OPJ_SIZE_T a_count,
-+    OPJ_INT32 dn, OPJ_INT32 sn, OPJ_INT32 cas);
- /**
- Explicit calculation of the Quantization Stepsizes
- */
-@@ -149,14 +147,14 @@ static void opj_dwt_encode_stepsize(OPJ_INT32 stepsize, OPJ_INT32 numbps,
- Inverse wavelet transform in 2-D.
- */
- static OPJ_BOOL opj_dwt_decode_tile(opj_thread_pool_t* tp,
--                                    opj_tcd_tilecomp_t* tilec, OPJ_UINT32 i);
-+                                    const opj_tcd_tilecomp_t* tilec, OPJ_UINT32 i);
- 
- static OPJ_BOOL opj_dwt_decode_partial_tile(
-     opj_tcd_tilecomp_t* tilec,
-     OPJ_UINT32 numres);
- 
--static OPJ_BOOL opj_dwt_encode_procedure(opj_tcd_tilecomp_t * tilec,
--        void (*p_function)(OPJ_INT32 *, OPJ_INT32, OPJ_INT32, OPJ_INT32));
-+static OPJ_BOOL opj_dwt_encode_procedure(const opj_tcd_tilecomp_t * tilec,
-+        void(*p_function)(OPJ_INT32 *, OPJ_SIZE_T, OPJ_INT32, OPJ_INT32, OPJ_INT32));
- 
- static OPJ_UINT32 opj_dwt_max_resolution(opj_tcd_resolution_t* OPJ_RESTRICT r,
-         OPJ_UINT32 i);
-@@ -205,13 +203,20 @@ static void opj_v4dwt_decode_step2(opj_v4_t* l, opj_v4_t* w,
- 
- /*@}*/
- 
--#define OPJ_S(i) a[(i)*2]
--#define OPJ_D(i) a[(1+(i)*2)]
--#define OPJ_S_(i) ((i)<0?OPJ_S(0):((i)>=sn?OPJ_S(sn-1):OPJ_S(i)))
--#define OPJ_D_(i) ((i)<0?OPJ_D(0):((i)>=dn?OPJ_D(dn-1):OPJ_D(i)))
-+#define IDX_S(i) (i)*2
-+#define IDX_D(i) 1 + (i)* 2
-+#define UNDERFLOW_SN(i) ((i) >= sn&&sn>0)
-+#define UNDERFLOW_DN(i) ((i) >= dn&&dn>0)
-+#define OVERFLOW_S(i) (IDX_S(i) >= a_count)
-+#define OVERFLOW_D(i) (IDX_D(i) >= a_count)
-+
-+#define OPJ_S(i) a[IDX_S(i)]
-+#define OPJ_D(i) a[IDX_D(i)]
-+#define OPJ_S_(i) ((i)<0 ? OPJ_S(0) : (UNDERFLOW_SN(i) ? OPJ_S(sn - 1) : OVERFLOW_S(i) ? OPJ_S(i - 1) : OPJ_S(i)))
-+#define OPJ_D_(i) ((i)<0 ? OPJ_D(0) : (UNDERFLOW_DN(i) ? OPJ_D(dn - 1) : OVERFLOW_D(i) ? OPJ_D(i - 1) : OPJ_D(i)))
- /* new */
--#define OPJ_SS_(i) ((i)<0?OPJ_S(0):((i)>=dn?OPJ_S(dn-1):OPJ_S(i)))
--#define OPJ_DD_(i) ((i)<0?OPJ_D(0):((i)>=sn?OPJ_D(sn-1):OPJ_D(i)))
-+#define OPJ_SS_(i) ((i)<0 ? OPJ_S(0) : (UNDERFLOW_DN(i) ? OPJ_S(dn - 1) : OVERFLOW_S(i) ? OPJ_S(i - 1) : OPJ_S(i)))
-+#define OPJ_DD_(i) ((i)<0 ? OPJ_D(0) : (UNDERFLOW_SN(i) ? OPJ_D(sn - 1) : OVERFLOW_D(i) ? OPJ_D(i - 1) : OPJ_D(i)))
- 
- /* <summary>                                                              */
- /* This table contains the norms of the 5-3 wavelets for different bands. */
-@@ -344,8 +349,8 @@ static void opj_dwt_interleave_v(const opj_dwt_t* v, OPJ_INT32 *a, OPJ_INT32 x)
- /* <summary>                            */
- /* Forward 5-3 wavelet transform in 1-D. */
- /* </summary>                           */
--static void opj_dwt_encode_1(OPJ_INT32 *a, OPJ_INT32 dn, OPJ_INT32 sn,
--                             OPJ_INT32 cas)
-+static void opj_dwt_encode_1(OPJ_INT32 *a, OPJ_SIZE_T a_count, OPJ_INT32 dn,
-+                             OPJ_INT32 sn, OPJ_INT32 cas)
- {
-     OPJ_INT32 i;
- 
-@@ -376,8 +381,8 @@ static void opj_dwt_encode_1(OPJ_INT32 *a, OPJ_INT32 dn, OPJ_INT32 sn,
- /* <summary>                            */
- /* Inverse 5-3 wavelet transform in 1-D. */
- /* </summary>                           */
--static void opj_dwt_decode_1_(OPJ_INT32 *a, OPJ_INT32 dn, OPJ_INT32 sn,
--                              OPJ_INT32 cas)
-+static void opj_dwt_decode_1_(OPJ_INT32 *a, OPJ_SIZE_T a_count, OPJ_INT32 dn,
-+                              OPJ_INT32 sn, OPJ_INT32 cas)
- {
-     OPJ_INT32 i;
- 
-@@ -406,7 +411,7 @@ static void opj_dwt_decode_1_(OPJ_INT32 *a, OPJ_INT32 dn, OPJ_INT32 sn,
- 
- static void opj_dwt_decode_1(const opj_dwt_t *v)
- {
--    opj_dwt_decode_1_(v->mem, v->dn, v->sn, v->cas);
-+    opj_dwt_decode_1_(v->mem, v->mem_count, v->dn, v->sn, v->cas);
- }
- 
- #endif /* STANDARD_SLOW_VERSION */
-@@ -1037,8 +1042,8 @@ static void opj_idwt53_v(const opj_dwt_t *dwt,
- /* <summary>                             */
- /* Forward 9-7 wavelet transform in 1-D. */
- /* </summary>                            */
--static void opj_dwt_encode_1_real(OPJ_INT32 *a, OPJ_INT32 dn, OPJ_INT32 sn,
--                                  OPJ_INT32 cas)
-+static void opj_dwt_encode_1_real(OPJ_INT32 *a, OPJ_SIZE_T a_count,
-+                                  OPJ_INT32 dn, OPJ_INT32 sn, OPJ_INT32 cas)
- {
-     OPJ_INT32 i;
-     if (!cas) {
-@@ -1106,8 +1111,8 @@ static void opj_dwt_encode_stepsize(OPJ_INT32 stepsize, OPJ_INT32 numbps,
- /* <summary>                            */
- /* Forward 5-3 wavelet transform in 2-D. */
- /* </summary>                           */
--static INLINE OPJ_BOOL opj_dwt_encode_procedure(opj_tcd_tilecomp_t * tilec,
--        void (*p_function)(OPJ_INT32 *, OPJ_INT32, OPJ_INT32, OPJ_INT32))
-+static INLINE OPJ_BOOL opj_dwt_encode_procedure(const opj_tcd_tilecomp_t * tilec,
-+        void(*p_function)(OPJ_INT32 *, OPJ_SIZE_T, OPJ_INT32, OPJ_INT32, OPJ_INT32))
- {
-     OPJ_INT32 i, j, k;
-     OPJ_INT32 *a = 00;
-@@ -1117,6 +1122,7 @@ static INLINE OPJ_BOOL opj_dwt_encode_procedure(opj_tcd_tilecomp_t * tilec,
- 
-     OPJ_INT32 rw;           /* width of the resolution level computed   */
-     OPJ_INT32 rh;           /* height of the resolution level computed  */
-+    OPJ_SIZE_T l_data_count;
-     OPJ_SIZE_T l_data_size;
- 
-     opj_tcd_resolution_t * l_cur_res = 0;
-@@ -1129,13 +1135,13 @@ static INLINE OPJ_BOOL opj_dwt_encode_procedure(opj_tcd_tilecomp_t * tilec,
-     l_cur_res = tilec->resolutions + l;
-     l_last_res = l_cur_res - 1;
- 
--    l_data_size = opj_dwt_max_resolution(tilec->resolutions, tilec->numresolutions);
-+    l_data_count = opj_dwt_max_resolution(tilec->resolutions, tilec->numresolutions);
-     /* overflow check */
--    if (l_data_size > (SIZE_MAX / sizeof(OPJ_INT32))) {
-+    if (l_data_count > (SIZE_MAX / sizeof(OPJ_INT32))) {
-         /* FIXME event manager error callback */
-         return OPJ_FALSE;
-     }
--    l_data_size *= sizeof(OPJ_INT32);
-+    l_data_size = l_data_count * sizeof(OPJ_INT32);
-     bj = (OPJ_INT32*)opj_malloc(l_data_size);
-     /* l_data_size is equal to 0 when numresolutions == 1 but bj is not used */
-     /* in that case, so do not error out */
-@@ -1167,7 +1173,7 @@ static INLINE OPJ_BOOL opj_dwt_encode_procedure(opj_tcd_tilecomp_t * tilec,
-                 bj[k] = aj[k * w];
-             }
- 
--            (*p_function)(bj, dn, sn, cas_col);
-+            (*p_function) (bj, l_data_count, dn, sn, cas_col);
- 
-             opj_dwt_deinterleave_v(bj, aj, dn, sn, w, cas_col);
-         }
-@@ -1180,7 +1186,7 @@ static INLINE OPJ_BOOL opj_dwt_encode_procedure(opj_tcd_tilecomp_t * tilec,
-             for (k = 0; k < rw; k++) {
-                 bj[k] = aj[k];
-             }
--            (*p_function)(bj, dn, sn, cas_row);
-+            (*p_function) (bj, l_data_count, dn, sn, cas_row);
-             opj_dwt_deinterleave_h(bj, aj, dn, sn, cas_row);
-         }
- 
-@@ -1379,7 +1385,7 @@ static void opj_dwt_decode_v_func(void* user_data, opj_tls_t* tls)
- /* Inverse wavelet transform in 2-D.    */
- /* </summary>                           */
- static OPJ_BOOL opj_dwt_decode_tile(opj_thread_pool_t* tp,
--                                    opj_tcd_tilecomp_t* tilec, OPJ_UINT32 numres)
-+        const opj_tcd_tilecomp_t* tilec, OPJ_UINT32 numres)
- {
-     opj_dwt_t h;
-     opj_dwt_t v;
-@@ -1401,22 +1407,23 @@ static OPJ_BOOL opj_dwt_decode_tile(opj_thread_pool_t* tp,
-         return OPJ_TRUE;
-     }
-     num_threads = opj_thread_pool_get_thread_count(tp);
--    h_mem_size = opj_dwt_max_resolution(tr, numres);
-+    h.mem_count = opj_dwt_max_resolution(tr, numres);
-     /* overflow check */
--    if (h_mem_size > (SIZE_MAX / PARALLEL_COLS_53 / sizeof(OPJ_INT32))) {
-+    if (h.mem_count > (SIZE_MAX / PARALLEL_COLS_53 / sizeof(OPJ_INT32))) {
-         /* FIXME event manager error callback */
-         return OPJ_FALSE;
-     }
-     /* We need PARALLEL_COLS_53 times the height of the array, */
-     /* since for the vertical pass */
-     /* we process PARALLEL_COLS_53 columns at a time */
--    h_mem_size *= PARALLEL_COLS_53 * sizeof(OPJ_INT32);
-+    h_mem_size = h.mem_count * PARALLEL_COLS_53 * sizeof(OPJ_INT32);
-     h.mem = (OPJ_INT32*)opj_aligned_32_malloc(h_mem_size);
-     if (! h.mem) {
-         /* FIXME event manager error callback */
-         return OPJ_FALSE;
-     }
- 
-+    v.mem_count = h.mem_count;
-     v.mem = h.mem;
- 
-     while (--numres) {
-@@ -1594,7 +1601,8 @@ static void opj_dwt_interleave_partial_v(OPJ_INT32 *dest,
-     OPJ_UNUSED(ret);
- }
- 
--static void opj_dwt_decode_partial_1(OPJ_INT32 *a, OPJ_INT32 dn, OPJ_INT32 sn,
-+static void opj_dwt_decode_partial_1(OPJ_INT32 *a, OPJ_SIZE_T a_count,
-+                                     OPJ_INT32 dn, OPJ_INT32 sn,
-                                      OPJ_INT32 cas,
-                                      OPJ_INT32 win_l_x0,
-                                      OPJ_INT32 win_l_x1,
-@@ -1974,16 +1982,16 @@ static OPJ_BOOL opj_dwt_decode_partial_tile(
-         opj_sparse_array_int32_free(sa);
-         return OPJ_TRUE;
-     }
--    h_mem_size = opj_dwt_max_resolution(tr, numres);
-+    h.mem_count = opj_dwt_max_resolution(tr, numres);
-     /* overflow check */
-     /* in vertical pass, we process 4 columns at a time */
--    if (h_mem_size > (SIZE_MAX / (4 * sizeof(OPJ_INT32)))) {
-+    if (h.mem_count > (SIZE_MAX / (4 * sizeof(OPJ_INT32)))) {
-         /* FIXME event manager error callback */
-         opj_sparse_array_int32_free(sa);
-         return OPJ_FALSE;
-     }
- 
--    h_mem_size *= 4 * sizeof(OPJ_INT32);
-+    h_mem_size = h.mem_count * 4 * sizeof(OPJ_INT32);
-     h.mem = (OPJ_INT32*)opj_aligned_32_malloc(h_mem_size);
-     if (! h.mem) {
-         /* FIXME event manager error callback */
-@@ -1991,6 +1999,7 @@ static OPJ_BOOL opj_dwt_decode_partial_tile(
-         return OPJ_FALSE;
-     }
- 
-+    v.mem_count = h.mem_count;
-     v.mem = h.mem;
- 
-     for (resno = 1; resno < numres; resno ++) {
-@@ -2101,7 +2110,7 @@ static OPJ_BOOL opj_dwt_decode_partial_tile(
-                                              win_ll_x1,
-                                              win_hl_x0,
-                                              win_hl_x1);
--                opj_dwt_decode_partial_1(h.mem, h.dn, h.sn, h.cas,
-+                opj_dwt_decode_partial_1(h.mem, h.mem_count, h.dn, h.sn, h.cas,
-                                          (OPJ_INT32)win_ll_x0,
-                                          (OPJ_INT32)win_ll_x1,
-                                          (OPJ_INT32)win_hl_x0,
diff --git a/third_party/libopenjpeg20/0005-jp2_apply_pclr.patch b/third_party/libopenjpeg20/0005-jp2_apply_pclr.patch
deleted file mode 100644
index 2d45017..0000000
--- a/third_party/libopenjpeg20/0005-jp2_apply_pclr.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-diff --git a/third_party/libopenjpeg20/jp2.c b/third_party/libopenjpeg20/jp2.c
-index 8dc1ecbe6..61b3f5821 100644
---- a/third_party/libopenjpeg20/jp2.c
-+++ b/third_party/libopenjpeg20/jp2.c
-@@ -1073,8 +1073,8 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
-             assert(pcol == 0);
-             new_comps[i] = old_comps[cmp];
-         } else {
--            assert(i == pcol);
--            new_comps[pcol] = old_comps[cmp];
-+            assert( i == pcol ); // probably wrong?
-+            new_comps[i] = old_comps[cmp];
-         }
- 
-         /* Palette mapping: */
-@@ -1102,7 +1102,7 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
-         pcol = cmap[i].pcol;
-         src = old_comps[cmp].data;
-         assert(src); /* verified above */
--        max = new_comps[pcol].w * new_comps[pcol].h;
-+        max = new_comps[i].w * new_comps[i].h;
- 
-         /* Direct use: */
-         if (cmap[i].mtyp == 0) {
-@@ -1112,8 +1112,8 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
-                 dst[j] = src[j];
-             }
-         } else {
--            assert(i == pcol);
--            dst = new_comps[pcol].data;
-+            assert( i == pcol ); // probably wrong?
-+            dst = new_comps[i].data;
-             assert(dst);
-             for (j = 0; j < max; ++j) {
-                 /* The index */
diff --git a/third_party/libopenjpeg20/0006-tcd_init_tile.patch b/third_party/libopenjpeg20/0006-tcd_init_tile.patch
deleted file mode 100644
index 8c37fc2..0000000
--- a/third_party/libopenjpeg20/0006-tcd_init_tile.patch
+++ /dev/null
@@ -1,16 +0,0 @@
-diff --git a/third_party/libopenjpeg20/tcd.c b/third_party/libopenjpeg20/tcd.c
-index 1dd15405d..acc28dd55 100644
---- a/third_party/libopenjpeg20/tcd.c
-+++ b/third_party/libopenjpeg20/tcd.c
-@@ -818,6 +818,11 @@ static INLINE OPJ_BOOL opj_tcd_init_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
-         if (isEncoder) {
-             OPJ_SIZE_T l_tile_data_size;
- 
-+            if (l_tilec->x0 >= l_tilec->x1 || l_tilec->y0 >= l_tilec->y1) {
-+                opj_event_msg(manager, EVT_ERROR, "Invalid tile data\n");
-+                return OPJ_FALSE;
-+            }
-+
-             /* compute l_data_size with overflow check */
-             OPJ_SIZE_T w = (OPJ_SIZE_T)(l_tilec->x1 - l_tilec->x0);
-             OPJ_SIZE_T h = (OPJ_SIZE_T)(l_tilec->y1 - l_tilec->y0);
diff --git a/third_party/libopenjpeg20/0007-jp2_read_cmap.patch b/third_party/libopenjpeg20/0007-jp2_read_cmap.patch
deleted file mode 100644
index 4cc4340..0000000
--- a/third_party/libopenjpeg20/0007-jp2_read_cmap.patch
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/third_party/libopenjpeg20/jp2.c b/third_party/libopenjpeg20/jp2.c
-index 3ace09654..7ef7c9139 100644
---- a/third_party/libopenjpeg20/jp2.c
-+++ b/third_party/libopenjpeg20/jp2.c
-@@ -1296,7 +1296,7 @@ static OPJ_BOOL opj_jp2_read_cmap(opj_jp2_t * jp2,
- 
- 
-     for (i = 0; i < nr_channels; ++i) {
--        opj_read_bytes(p_cmap_header_data, &l_value, 2);            /* CMP^i */
-+        opj_read_bytes_BE(p_cmap_header_data, &l_value, 2);     /* CMP^i */
-         p_cmap_header_data += 2;
-         cmap[i].cmp = (OPJ_UINT16) l_value;
- 
diff --git a/third_party/libopenjpeg20/0009-opj_pi_next.patch b/third_party/libopenjpeg20/0009-opj_pi_next.patch
deleted file mode 100644
index 99f17d3..0000000
--- a/third_party/libopenjpeg20/0009-opj_pi_next.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-diff --git a/third_party/libopenjpeg20/pi.c b/third_party/libopenjpeg20/pi.c
-index 91642ee4e..256fe37a1 100644
---- a/third_party/libopenjpeg20/pi.c
-+++ b/third_party/libopenjpeg20/pi.c
-@@ -445,6 +445,9 @@ static OPJ_BOOL opj_pi_next_rpcl(opj_pi_iterator_t * pi)
-                                                 (OPJ_INT32)(comp->dy << levelno)), (OPJ_INT32)res->pdy)
-                            - opj_int_floordivpow2(try0, (OPJ_INT32)res->pdy);
-                     pi->precno = (OPJ_UINT32)(prci + prcj * (OPJ_INT32)res->pw);
-+                    if (pi->precno >= res->pw * res->ph) {
-+                      return OPJ_FALSE;
-+                    }
-                     for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) {
-                         index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno *
-                                 pi->step_c + pi->precno * pi->step_p;
-@@ -576,6 +579,9 @@ static OPJ_BOOL opj_pi_next_pcrl(opj_pi_iterator_t * pi)
-                                                 (OPJ_INT32)(comp->dy << levelno)), (OPJ_INT32)res->pdy)
-                            - opj_int_floordivpow2(try0, (OPJ_INT32)res->pdy);
-                     pi->precno = (OPJ_UINT32)(prci + prcj * (OPJ_INT32)res->pw);
-+                    if (pi->precno >= res->pw * res->ph) {
-+                      return OPJ_FALSE;
-+                    }
-                     for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) {
-                         index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno *
-                                 pi->step_c + pi->precno * pi->step_p;
-@@ -704,6 +710,9 @@ static OPJ_BOOL opj_pi_next_cprl(opj_pi_iterator_t * pi)
-                                                 (OPJ_INT32)(comp->dy << levelno)), (OPJ_INT32)res->pdy)
-                            - opj_int_floordivpow2(try0, (OPJ_INT32)res->pdy);
-                     pi->precno = (OPJ_UINT32)(prci + prcj * (OPJ_INT32)res->pw);
-+                    if (pi->precno >= res->pw * res->ph) {
-+                      return OPJ_FALSE;
-+                    }
-                     for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) {
-                         index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno *
-                                 pi->step_c + pi->precno * pi->step_p;
diff --git a/third_party/libopenjpeg20/0011-j2k_update_image_data.patch b/third_party/libopenjpeg20/0011-j2k_update_image_data.patch
deleted file mode 100644
index b61324a..0000000
--- a/third_party/libopenjpeg20/0011-j2k_update_image_data.patch
+++ /dev/null
@@ -1,17 +0,0 @@
-diff --git a/third_party/libopenjpeg20/j2k.c b/third_party/libopenjpeg20/j2k.c
-index ad6e1b86f..bf1cb4f36 100644
---- a/third_party/libopenjpeg20/j2k.c
-+++ b/third_party/libopenjpeg20/j2k.c
-@@ -9086,6 +9086,12 @@ static OPJ_BOOL opj_j2k_update_image_data(opj_tcd_t * p_tcd,
-          * */
-         assert(res_x0 >= 0);
-         assert(res_x1 >= 0);
-+
-+        /* Prevent bad casting to unsigned values in the subsequent lines. */
-+        if ( res_x0 < 0 || res_x1 < 0 || res_y0 < 0 || res_y1 < 0 ) {
-+            return OPJ_FALSE;
-+        }
-+
-         if (l_x0_dest < (OPJ_UINT32)res_x0) {
-             l_start_x_dest = (OPJ_UINT32)res_x0 - l_x0_dest;
-             l_offset_x0_src = 0;
diff --git a/third_party/libopenjpeg20/0012-mct_sse.patch b/third_party/libopenjpeg20/0012-mct_sse.patch
deleted file mode 100644
index 9bc2e6f..0000000
--- a/third_party/libopenjpeg20/0012-mct_sse.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-diff --git a/third_party/libopenjpeg20/mct.c b/third_party/libopenjpeg20/mct.c
-index b79d4b87c..81ec223d8 100644
---- a/third_party/libopenjpeg20/mct.c
-+++ b/third_party/libopenjpeg20/mct.c
-@@ -37,13 +37,16 @@
-  * POSSIBILITY OF SUCH DAMAGE.
-  */
- 
--#ifdef __SSE__
-+#if defined(__SSE__) && !defined(_M_IX86) && !defined(__i386)
-+#define USE_SSE
- #include <xmmintrin.h>
- #endif
--#ifdef __SSE2__
-+#if defined(__SSE2__) && !defined(_M_IX86) && !defined(__i386)
-+#define USE_SSE2
- #include <emmintrin.h>
- #endif
--#ifdef __SSE4_1__
-+#if defined(__SSE4_1__) && !defined(_M_IX86) && !defined(__i386)
-+#define USE_SSE4
- #include <smmintrin.h>
- #endif
- 
-@@ -72,7 +75,7 @@ const OPJ_FLOAT64 * opj_mct_get_mct_norms_real()
- /* <summary> */
- /* Forward reversible MCT. */
- /* </summary> */
--#ifdef __SSE2__
-+#ifdef USE_SSE2
- void opj_mct_encode(
-     OPJ_INT32* OPJ_RESTRICT c0,
-     OPJ_INT32* OPJ_RESTRICT c1,
-@@ -141,7 +144,7 @@ void opj_mct_encode(
- /* <summary> */
- /* Inverse reversible MCT. */
- /* </summary> */
--#ifdef __SSE2__
-+#ifdef USE_SSE2
- void opj_mct_decode(
-     OPJ_INT32* OPJ_RESTRICT c0,
-     OPJ_INT32* OPJ_RESTRICT c1,
-@@ -209,7 +212,7 @@ OPJ_FLOAT64 opj_mct_getnorm(OPJ_UINT32 compno)
- /* <summary> */
- /* Forward irreversible MCT. */
- /* </summary> */
--#ifdef __SSE4_1__
-+#ifdef USE_SSE4
- void opj_mct_encode_real(
-     OPJ_INT32* OPJ_RESTRICT c0,
-     OPJ_INT32* OPJ_RESTRICT c1,
-@@ -389,7 +392,7 @@ void opj_mct_decode_real(
-     OPJ_SIZE_T n)
- {
-     OPJ_UINT32 i;
--#ifdef __SSE__
-+#ifdef USE_SSE
-     __m128 vrv, vgu, vgv, vbu;
-     vrv = _mm_set1_ps(1.402f);
-     vgu = _mm_set1_ps(0.34413f);
diff --git a/third_party/libopenjpeg20/0014-opj_jp2_read_ihdr_leak.patch b/third_party/libopenjpeg20/0014-opj_jp2_read_ihdr_leak.patch
deleted file mode 100644
index 0ae0cfc..0000000
--- a/third_party/libopenjpeg20/0014-opj_jp2_read_ihdr_leak.patch
+++ /dev/null
@@ -1,20 +0,0 @@
-diff --git a/third_party/libopenjpeg20/jp2.c b/third_party/libopenjpeg20/jp2.c
-index 7ef7c9139..1fa607d66 100644
---- a/third_party/libopenjpeg20/jp2.c
-+++ b/third_party/libopenjpeg20/jp2.c
-@@ -593,6 +593,7 @@ static OPJ_BOOL opj_jp2_read_ihdr(opj_jp2_t *jp2,
-     }
- 
-     /* allocate memory for components */
-+    opj_free(jp2->comps);
-     jp2->comps = (opj_jp2_comps_t*) opj_calloc(jp2->numcomps,
-                  sizeof(opj_jp2_comps_t));
-     if (jp2->comps == 0) {
-@@ -1882,6 +1883,7 @@ void opj_jp2_setup_decoder(opj_jp2_t *jp2, opj_dparameters_t *parameters)
- 
-     /* further JP2 initializations go here */
-     jp2->color.jp2_has_colr = 0;
-+    jp2->comps = NULL;
-     jp2->ignore_pclr_cmap_cdef = parameters->flags &
-                                  OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG;
- }
diff --git a/third_party/libopenjpeg20/0015-read_SPCod_SPCoc_overflow.patch b/third_party/libopenjpeg20/0015-read_SPCod_SPCoc_overflow.patch
deleted file mode 100644
index 760ed74..0000000
--- a/third_party/libopenjpeg20/0015-read_SPCod_SPCoc_overflow.patch
+++ /dev/null
@@ -1,15 +0,0 @@
-diff --git a/third_party/libopenjpeg20/j2k.c b/third_party/libopenjpeg20/j2k.c
-index 8e35b33ee..d95963a5c 100644
---- a/third_party/libopenjpeg20/j2k.c
-+++ b/third_party/libopenjpeg20/j2k.c
-@@ -9527,6 +9527,10 @@ static OPJ_BOOL opj_j2k_read_SPCod_SPCoc(opj_j2k_t *p_j2k,
-             p_j2k->m_specific_param.m_decoder.m_default_tcp;
- 
-     /* precondition again */
-+    if (compno >= p_j2k->m_private_image->numcomps) {
-+        return OPJ_FALSE;
-+    }
-+
-     assert(compno < p_j2k->m_private_image->numcomps);
- 
-     l_tccp = &l_tcp->tccps[compno];
diff --git a/third_party/libopenjpeg20/0016-read_SQcd_SQcc_overflow.patch b/third_party/libopenjpeg20/0016-read_SQcd_SQcc_overflow.patch
deleted file mode 100644
index d7e06ea..0000000
--- a/third_party/libopenjpeg20/0016-read_SQcd_SQcc_overflow.patch
+++ /dev/null
@@ -1,15 +0,0 @@
-diff --git a/third_party/libopenjpeg20/j2k.c b/third_party/libopenjpeg20/j2k.c
-index d95963a5c..ed449684f 100644
---- a/third_party/libopenjpeg20/j2k.c
-+++ b/third_party/libopenjpeg20/j2k.c
-@@ -9864,7 +9864,9 @@ static OPJ_BOOL opj_j2k_read_SQcd_SQcc(opj_j2k_t *p_j2k,
-             p_j2k->m_specific_param.m_decoder.m_default_tcp;
- 
-     /* precondition again*/
--    assert(p_comp_no <  p_j2k->m_private_image->numcomps);
-+    if (p_comp_no >=  p_j2k->m_private_image->numcomps) {
-+        return OPJ_FALSE;
-+    }
- 
-     l_tccp = &l_tcp->tccps[p_comp_no];
-     l_current_ptr = p_header_data;
diff --git a/third_party/libopenjpeg20/0019-tcd_init_tile.patch b/third_party/libopenjpeg20/0019-tcd_init_tile.patch
deleted file mode 100644
index 8746eac..0000000
--- a/third_party/libopenjpeg20/0019-tcd_init_tile.patch
+++ /dev/null
@@ -1,14 +0,0 @@
-diff --git a/third_party/libopenjpeg20/tcd.c b/third_party/libopenjpeg20/tcd.c
-index be3b84363..5757fd401 100644
---- a/third_party/libopenjpeg20/tcd.c
-+++ b/third_party/libopenjpeg20/tcd.c
-@@ -1065,6 +1065,9 @@ static INLINE OPJ_BOOL opj_tcd_init_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
-                     l_current_precinct->ch = (OPJ_UINT32)((brcblkyend - tlcblkystart) >>
-                                                           cblkheightexpn);
- 
-+                    if (l_current_precinct->cw && ((OPJ_UINT32)-1) / l_current_precinct->cw < l_current_precinct->ch) {
-+                        return OPJ_FALSE;
-+                    }
-                     l_nb_code_blocks = l_current_precinct->cw * l_current_precinct->ch;
-                     /*fprintf(stderr, "\t\t\t\t precinct_cw = %d x recinct_ch = %d\n",l_current_precinct->cw, l_current_precinct->ch);      */
-                     if ((((OPJ_UINT32) - 1) / (OPJ_UINT32)sizeof_block) <
diff --git a/third_party/libopenjpeg20/0022-jp2_apply_pclr_overflow.patch b/third_party/libopenjpeg20/0022-jp2_apply_pclr_overflow.patch
deleted file mode 100644
index c1773d4..0000000
--- a/third_party/libopenjpeg20/0022-jp2_apply_pclr_overflow.patch
+++ /dev/null
@@ -1,51 +0,0 @@
-diff --git a/third_party/libopenjpeg20/jp2.c b/third_party/libopenjpeg20/jp2.c
-index 8dc2b977f..3e23bc363 100644
---- a/third_party/libopenjpeg20/jp2.c
-+++ b/third_party/libopenjpeg20/jp2.c
-@@ -1058,6 +1058,14 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
-     }
- 
-     old_comps = image->comps;
-+    /* Overflow check: prevent integer overflow */
-+    for (i = 0; i < nr_channels; ++i) {
-+      cmp = cmap[i].cmp;
-+      if (old_comps[cmp].h == 0 || old_comps[cmp].w > ((OPJ_UINT32)-1) / sizeof(OPJ_INT32) / old_comps[cmp].h) {
-+        return OPJ_FALSE;
-+      }
-+    }
-+
-     new_comps = (opj_image_comp_t*)
-                 opj_malloc(nr_channels * sizeof(opj_image_comp_t));
-     if (!new_comps) {
-@@ -1102,20 +1110,26 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
-         cmp = cmap[i].cmp;
-         pcol = cmap[i].pcol;
-         src = old_comps[cmp].data;
--        assert(src); /* verified above */
-+        dst = new_comps[i].data;
-         max = new_comps[i].w * new_comps[i].h;
- 
-+        /* Prevent null pointer access */
-+        if (!src || !dst) {
-+          for (j = 0; j < nr_channels; ++j) {
-+            opj_free(new_comps[j].data);
-+          }
-+          opj_free(new_comps);
-+          new_comps = NULL;
-+          return OPJ_FALSE;
-+        }
-+
-         /* Direct use: */
-         if (cmap[i].mtyp == 0) {
--            dst = new_comps[i].data;
--            assert(dst);
-             for (j = 0; j < max; ++j) {
-                 dst[j] = src[j];
-             }
-         } else {
-             assert( i == pcol ); // probably wrong?
--            dst = new_comps[i].data;
--            assert(dst);
-             for (j = 0; j < max; ++j) {
-                 /* The index */
-                 if ((k = src[j]) < 0) {
diff --git a/third_party/libopenjpeg20/0023-opj_j2k_read_mct_records.patch b/third_party/libopenjpeg20/0023-opj_j2k_read_mct_records.patch
deleted file mode 100644
index c8415ae..0000000
--- a/third_party/libopenjpeg20/0023-opj_j2k_read_mct_records.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-diff --git a/third_party/libopenjpeg20/j2k.c b/third_party/libopenjpeg20/j2k.c
-index ed449684f..c5f9dd53e 100644
---- a/third_party/libopenjpeg20/j2k.c
-+++ b/third_party/libopenjpeg20/j2k.c
-@@ -5553,6 +5553,7 @@ static OPJ_BOOL opj_j2k_read_mct(opj_j2k_t *p_j2k,
-     OPJ_UINT32 l_tmp;
-     OPJ_UINT32 l_indix;
-     opj_mct_data_t * l_mct_data;
-+    OPJ_BOOL new_mct = OPJ_FALSE;
- 
-     /* preconditions */
-     assert(p_header_data != 00);
-@@ -5640,7 +5641,7 @@ static OPJ_BOOL opj_j2k_read_mct(opj_j2k_t *p_j2k,
-         }
- 
-         l_mct_data = l_tcp->m_mct_records + l_tcp->m_nb_mct_records;
--        ++l_tcp->m_nb_mct_records;
-+        new_mct = OPJ_TRUE;
-     }
- 
-     if (l_mct_data->m_data) {
-@@ -5672,6 +5673,9 @@ static OPJ_BOOL opj_j2k_read_mct(opj_j2k_t *p_j2k,
- 
-     l_mct_data->m_data_size = p_header_size;
- 
-+    if (new_mct) {
-+            ++l_tcp->m_nb_mct_records;
-+    }
-     return OPJ_TRUE;
- }
- 
diff --git a/third_party/libopenjpeg20/0025-opj_j2k_add_mct_null_data.patch b/third_party/libopenjpeg20/0025-opj_j2k_add_mct_null_data.patch
deleted file mode 100644
index b2e7cdc..0000000
--- a/third_party/libopenjpeg20/0025-opj_j2k_add_mct_null_data.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-diff --git a/third_party/libopenjpeg20/j2k.c b/third_party/libopenjpeg20/j2k.c
-index c5f9dd53e..d31eb29a7 100644
---- a/third_party/libopenjpeg20/j2k.c
-+++ b/third_party/libopenjpeg20/j2k.c
-@@ -6201,7 +6201,7 @@ static OPJ_BOOL opj_j2k_add_mct(opj_tcp_t * p_tcp, opj_image_t * p_image,
-     if (l_deco_array) {
-         l_data_size = MCT_ELEMENT_SIZE[l_deco_array->m_element_type] * p_image->numcomps
-                       * p_image->numcomps;
--        if (l_deco_array->m_data_size != l_data_size) {
-+        if (l_deco_array->m_data_size != l_data_size || ! l_deco_array->m_data) {
-             return OPJ_FALSE;
-         }
- 
-@@ -6222,7 +6222,7 @@ static OPJ_BOOL opj_j2k_add_mct(opj_tcp_t * p_tcp, opj_image_t * p_image,
-     if (l_offset_array) {
-         l_data_size = MCT_ELEMENT_SIZE[l_offset_array->m_element_type] *
-                       p_image->numcomps;
--        if (l_offset_array->m_data_size != l_data_size) {
-+        if (l_offset_array->m_data_size != l_data_size || ! l_offset_array->m_data) {
-             return OPJ_FALSE;
-         }
- 
diff --git a/third_party/libopenjpeg20/0026-use_opj_uint_ceildiv.patch b/third_party/libopenjpeg20/0026-use_opj_uint_ceildiv.patch
deleted file mode 100644
index 038fb90..0000000
--- a/third_party/libopenjpeg20/0026-use_opj_uint_ceildiv.patch
+++ /dev/null
@@ -1,86 +0,0 @@
-diff --git a/third_party/libopenjpeg20/j2k.c b/third_party/libopenjpeg20/j2k.c
-index c5f9dd53e..1869833f7 100644
---- a/third_party/libopenjpeg20/j2k.c
-+++ b/third_party/libopenjpeg20/j2k.c
-@@ -2311,10 +2311,8 @@ static OPJ_BOOL opj_j2k_read_siz(opj_j2k_t *p_j2k,
-     }
- 
-     /* Compute the number of tiles */
--    l_cp->tw = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)(l_image->x1 - l_cp->tx0),
--                                           (OPJ_INT32)l_cp->tdx);
--    l_cp->th = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)(l_image->y1 - l_cp->ty0),
--                                           (OPJ_INT32)l_cp->tdy);
-+    l_cp->tw = opj_uint_ceildiv(l_image->x1 - l_cp->tx0, l_cp->tdx);
-+    l_cp->th = opj_uint_ceildiv(l_image->y1 - l_cp->ty0, l_cp->tdy);
- 
-     /* Check that the number of tiles is valid */
-     if (l_cp->tw == 0 || l_cp->th == 0 || l_cp->tw > 65535 / l_cp->th) {
-@@ -2331,12 +2329,10 @@ static OPJ_BOOL opj_j2k_read_siz(opj_j2k_t *p_j2k,
-             (p_j2k->m_specific_param.m_decoder.m_start_tile_x - l_cp->tx0) / l_cp->tdx;
-         p_j2k->m_specific_param.m_decoder.m_start_tile_y =
-             (p_j2k->m_specific_param.m_decoder.m_start_tile_y - l_cp->ty0) / l_cp->tdy;
--        p_j2k->m_specific_param.m_decoder.m_end_tile_x = (OPJ_UINT32)opj_int_ceildiv((
--                    OPJ_INT32)(p_j2k->m_specific_param.m_decoder.m_end_tile_x - l_cp->tx0),
--                (OPJ_INT32)l_cp->tdx);
--        p_j2k->m_specific_param.m_decoder.m_end_tile_y = (OPJ_UINT32)opj_int_ceildiv((
--                    OPJ_INT32)(p_j2k->m_specific_param.m_decoder.m_end_tile_y - l_cp->ty0),
--                (OPJ_INT32)l_cp->tdy);
-+        p_j2k->m_specific_param.m_decoder.m_end_tile_x = opj_uint_ceildiv(
-+            p_j2k->m_specific_param.m_decoder.m_end_tile_x - l_cp->tx0, l_cp->tdx);
-+        p_j2k->m_specific_param.m_decoder.m_end_tile_y = opj_uint_ceildiv(
-+            p_j2k->m_specific_param.m_decoder.m_end_tile_y - l_cp->ty0, l_cp->tdy);
-     } else {
-         p_j2k->m_specific_param.m_decoder.m_start_tile_x = 0;
-         p_j2k->m_specific_param.m_decoder.m_start_tile_y = 0;
-@@ -6922,10 +6918,8 @@ OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k,
-     */
- 
-     if (parameters->tile_size_on) {
--        cp->tw = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)(image->x1 - cp->tx0),
--                                             (OPJ_INT32)cp->tdx);
--        cp->th = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)(image->y1 - cp->ty0),
--                                             (OPJ_INT32)cp->tdy);
-+        cp->tw = opj_uint_ceildiv(image->x1 - cp->tx0, cp->tdx);
-+        cp->th = opj_uint_ceildiv(image->y1 - cp->ty0, cp->tdy);
-     } else {
-         cp->tdx = image->x1 - cp->tx0;
-         cp->tdy = image->y1 - cp->ty0;
-@@ -9237,10 +9231,8 @@ OPJ_BOOL opj_j2k_set_decode_area(opj_j2k_t *p_j2k,
-     for (it_comp = 0; it_comp < p_image->numcomps; ++it_comp) {
-         OPJ_INT32 l_h, l_w;
- 
--        l_img_comp->x0 = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)p_image->x0,
--                         (OPJ_INT32)l_img_comp->dx);
--        l_img_comp->y0 = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)p_image->y0,
--                         (OPJ_INT32)l_img_comp->dy);
-+        l_img_comp->x0 = opj_uint_ceildiv(p_image->x0, l_img_comp->dx);
-+        l_img_comp->y0 = opj_uint_ceildiv(p_image->y0, l_img_comp->dy);
-         l_comp_x1 = opj_int_ceildiv((OPJ_INT32)p_image->x1, (OPJ_INT32)l_img_comp->dx);
-         l_comp_y1 = opj_int_ceildiv((OPJ_INT32)p_image->y1, (OPJ_INT32)l_img_comp->dy);
- 
-@@ -10848,10 +10840,8 @@ OPJ_BOOL opj_j2k_get_tile(opj_j2k_t *p_j2k,
- 
-         l_img_comp->factor = p_j2k->m_private_image->comps[compno].factor;
- 
--        l_img_comp->x0 = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)p_image->x0,
--                         (OPJ_INT32)l_img_comp->dx);
--        l_img_comp->y0 = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)p_image->y0,
--                         (OPJ_INT32)l_img_comp->dy);
-+        l_img_comp->x0 = opj_uint_ceildiv(p_image->x0, l_img_comp->dx);
-+        l_img_comp->y0 = opj_uint_ceildiv(p_image->y0, l_img_comp->dy);
-         l_comp_x1 = opj_int_ceildiv((OPJ_INT32)p_image->x1, (OPJ_INT32)l_img_comp->dx);
-         l_comp_y1 = opj_int_ceildiv((OPJ_INT32)p_image->y1, (OPJ_INT32)l_img_comp->dy);
- 
-@@ -11161,10 +11151,8 @@ static void opj_get_tile_dimensions(opj_image_t * l_image,
- 
-     *l_width  = (OPJ_UINT32)(l_tilec->x1 - l_tilec->x0);
-     *l_height = (OPJ_UINT32)(l_tilec->y1 - l_tilec->y0);
--    *l_offset_x = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)l_image->x0,
--                  (OPJ_INT32)l_img_comp->dx);
--    *l_offset_y = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)l_image->y0,
--                  (OPJ_INT32)l_img_comp->dy);
-+    *l_offset_x = opj_uint_ceildiv(l_image->x0, l_img_comp->dx);
-+    *l_offset_y = opj_uint_ceildiv(l_image->y0, l_img_comp->dy);
-     *l_image_width = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)l_image->x1 -
-                      (OPJ_INT32)l_image->x0, (OPJ_INT32)l_img_comp->dx);
-     *l_stride = *l_image_width - *l_width;
diff --git a/third_party/libopenjpeg20/0034-opj_malloc.patch b/third_party/libopenjpeg20/0034-opj_malloc.patch
deleted file mode 100644
index b2bc99c..0000000
--- a/third_party/libopenjpeg20/0034-opj_malloc.patch
+++ /dev/null
@@ -1,170 +0,0 @@
-diff --git a/third_party/libopenjpeg20/opj_malloc.h b/third_party/libopenjpeg20/opj_malloc.h
-index cbc4106c7..79b3a6410 100644
---- a/third_party/libopenjpeg20/opj_malloc.h
-+++ b/third_party/libopenjpeg20/opj_malloc.h
-@@ -31,8 +31,6 @@
-  */
- #ifndef OPJ_MALLOC_H
- #define OPJ_MALLOC_H
--
--#include <stddef.h>
- /**
- @file opj_malloc.h
- @brief Internal functions
-@@ -52,7 +50,16 @@ Allocate an uninitialized memory block
- @param size Bytes to allocate
- @return Returns a void pointer to the allocated space, or NULL if there is insufficient memory available
- */
--void * opj_malloc(size_t size);
-+#ifdef ALLOC_PERF_OPT
-+void * OPJ_CALLCONV opj_malloc(size_t size);
-+#else
-+/* prevent assertion on overflow for MSVC */
-+#ifdef _MSC_VER
-+#define opj_malloc(size) ((size_t)(size) >= (size_t)-0x100 ? NULL : malloc(size))
-+#else
-+#define opj_malloc(size) malloc(size)
-+#endif
-+#endif
- 
- /**
- Allocate a memory block with elements initialized to 0
-@@ -60,24 +67,98 @@ Allocate a memory block with elements initialized to 0
- @param sizeOfElements Bytes per block to allocate
- @return Returns a void pointer to the allocated space, or NULL if there is insufficient memory available
- */
--void * opj_calloc(size_t numOfElements, size_t sizeOfElements);
-+#ifdef ALLOC_PERF_OPT
-+void * OPJ_CALLCONV opj_calloc(size_t _NumOfElements, size_t _SizeOfElements);
-+#else
-+/* prevent assertion on overflow for MSVC */
-+#ifdef _MSC_VER
-+#define opj_calloc(num, size) ((size_t)(num) != 0 && (size_t)(num) >= (size_t)-0x100 / (size_t)(size) ? NULL : calloc(num, size))
-+#else
-+#define opj_calloc(num, size) calloc(num, size)
-+#endif
-+#endif
- 
- /**
- Allocate memory aligned to a 16 byte boundary
- @param size Bytes to allocate
- @return Returns a void pointer to the allocated space, or NULL if there is insufficient memory available
- */
--void * opj_aligned_malloc(size_t size);
--void * opj_aligned_realloc(void *ptr, size_t size);
--void opj_aligned_free(void* ptr);
-+/* FIXME: These should be set with cmake tests, but we're currently not requiring use of cmake */
-+#ifdef _WIN32
-+  /* Someone should tell the mingw people that their malloc.h ought to provide _mm_malloc() */
-+  #ifdef __GNUC__
-+    #include <mm_malloc.h>
-+    #define HAVE_MM_MALLOC
-+  #else /* MSVC, Intel C++ */
-+    #include <malloc.h>
-+    #ifdef _mm_malloc
-+      #define HAVE_MM_MALLOC
-+    #endif
-+  #endif
-+#else /* Not _WIN32 */
-+  #if defined(__sun)
-+    #define HAVE_MEMALIGN
-+  #elif defined(__FreeBSD__)
-+    #define HAVE_POSIX_MEMALIGN
-+  /* Linux x86_64 and OSX always align allocations to 16 bytes */
-+  #elif !defined(__amd64__) && !defined(__APPLE__) && !defined(_AIX)
-+    #define HAVE_MEMALIGN
-+    #include <malloc.h>
-+  #endif
-+#endif
- 
--/**
--Allocate memory aligned to a 32 byte boundary
--@param size Bytes to allocate
--@return Returns a void pointer to the allocated space, or NULL if there is insufficient memory available
--*/
--void * opj_aligned_32_malloc(size_t size);
--void * opj_aligned_32_realloc(void *ptr, size_t size);
-+#define opj_aligned_malloc(size) malloc(size)
-+#define opj_aligned_32_malloc(size) malloc(size)
-+#define opj_aligned_free(m) free(m)
-+
-+#ifdef HAVE_MM_MALLOC
-+  #undef opj_aligned_malloc
-+  #define opj_aligned_malloc(size) _mm_malloc((size), 16)
-+  #undef opj_aligned_32_malloc
-+  #define opj_aligned_32_malloc(size) _mm_malloc((size), 32)
-+  #undef opj_aligned_free
-+  #define opj_aligned_free(m) _mm_free(m)
-+#endif
-+
-+#ifdef HAVE_MEMALIGN
-+  extern void* memalign(size_t, size_t);
-+  #undef opj_aligned_malloc
-+  #define opj_aligned_malloc(size) memalign(16, (size))
-+  #undef opj_aligned_32_malloc
-+  #define opj_aligned_32_malloc(size) memalign(32, (size))
-+  #undef opj_aligned_free
-+  #define opj_aligned_free(m) free(m)
-+#endif
-+
-+#ifdef HAVE_POSIX_MEMALIGN
-+  #undef opj_aligned_malloc
-+  extern int posix_memalign(void**, size_t, size_t);
-+
-+  static INLINE void* __attribute__ ((malloc)) opj_aligned_malloc(size_t size){
-+    void* mem = NULL;
-+    posix_memalign(&mem, 16, size);
-+    return mem;
-+  }
-+
-+  #undef opj_aligned_32_malloc
-+  static INLINE void* __attribute__ ((malloc)) opj_aligned_32_malloc(size_t size){
-+    void* mem = NULL;
-+    posix_memalign(&mem, 32, size);
-+    return mem;
-+  }
-+
-+  #undef opj_aligned_free
-+  #define opj_aligned_free(m) free(m)
-+#endif
-+
-+#ifdef ALLOC_PERF_OPT
-+  #undef opj_aligned_malloc
-+  #define opj_aligned_malloc(size) opj_malloc(size)
-+  #undef opj_aligned_32_malloc
-+  #define opj_aligned_32_malloc(size) opj_malloc(size)
-+  #undef opj_aligned_free
-+  #define opj_aligned_free(m) opj_free(m)
-+#endif
- 
- /**
- Reallocate memory blocks.
-@@ -85,13 +166,26 @@ Reallocate memory blocks.
- @param s New size in bytes
- @return Returns a void pointer to the reallocated (and possibly moved) memory block
- */
--void * opj_realloc(void * m, size_t s);
-+#ifdef ALLOC_PERF_OPT
-+void * OPJ_CALLCONV opj_realloc(void * m, size_t s);
-+#else
-+/* prevent assertion on overflow for MSVC */
-+#ifdef _MSC_VER
-+#define opj_realloc(m, s) ((size_t)(s) >= (size_t)-0x100 ? NULL : realloc(m, s))
-+#else
-+#define opj_realloc(m, s) realloc(m, s)
-+#endif
-+#endif
- 
- /**
- Deallocates or frees a memory block.
- @param m Previously allocated memory block to be freed
- */
--void opj_free(void * m);
-+#ifdef ALLOC_PERF_OPT
-+void OPJ_CALLCONV opj_free(void * m);
-+#else
-+#define opj_free(m) free(m)
-+#endif
- 
- #if defined(__GNUC__) && !defined(OPJ_SKIP_POISON)
- #pragma GCC poison malloc calloc realloc free
diff --git a/third_party/libopenjpeg20/0035-opj_image_data_free.patch b/third_party/libopenjpeg20/0035-opj_image_data_free.patch
deleted file mode 100644
index bc674da..0000000
--- a/third_party/libopenjpeg20/0035-opj_image_data_free.patch
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/third_party/libopenjpeg20/jp2.c b/third_party/libopenjpeg20/jp2.c
-index 298648a77..2374d459f 100644
---- a/third_party/libopenjpeg20/jp2.c
-+++ b/third_party/libopenjpeg20/jp2.c
-@@ -1116,7 +1116,7 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
-         /* Prevent null pointer access */
-         if (!src || !dst) {
-           for (j = 0; j < nr_channels; ++j) {
--            opj_free(new_comps[j].data);
-+            opj_image_data_free(new_comps[j].data);
-           }
-           opj_free(new_comps);
-           new_comps = NULL;
diff --git a/third_party/libopenjpeg20/0036-opj_j2k_update_image_dimensions.patch b/third_party/libopenjpeg20/0036-opj_j2k_update_image_dimensions.patch
deleted file mode 100644
index b918c05..0000000
--- a/third_party/libopenjpeg20/0036-opj_j2k_update_image_dimensions.patch
+++ /dev/null
@@ -1,49 +0,0 @@
-diff --git a/third_party/libopenjpeg20/j2k.c b/third_party/libopenjpeg20/j2k.c
-index 784a0620a..cea614709 100644
---- a/third_party/libopenjpeg20/j2k.c
-+++ b/third_party/libopenjpeg20/j2k.c
-@@ -9223,32 +9223,30 @@ static OPJ_BOOL opj_j2k_update_image_dimensions(opj_image_t* p_image,
- 
-     l_img_comp = p_image->comps;
-     for (it_comp = 0; it_comp < p_image->numcomps; ++it_comp) {
--        OPJ_INT32 l_h, l_w;
--
-         l_img_comp->x0 = opj_uint_ceildiv(p_image->x0, l_img_comp->dx);
-         l_img_comp->y0 = opj_uint_ceildiv(p_image->y0, l_img_comp->dy);
-         l_comp_x1 = opj_int_ceildiv((OPJ_INT32)p_image->x1, (OPJ_INT32)l_img_comp->dx);
-         l_comp_y1 = opj_int_ceildiv((OPJ_INT32)p_image->y1, (OPJ_INT32)l_img_comp->dy);
- 
--        l_w = opj_int_ceildivpow2(l_comp_x1, (OPJ_INT32)l_img_comp->factor)
--              - opj_int_ceildivpow2((OPJ_INT32)l_img_comp->x0, (OPJ_INT32)l_img_comp->factor);
--        if (l_w < 0) {
-+        OPJ_INT32 l_1 = opj_int_ceildivpow2(l_comp_x1, (OPJ_INT32)l_img_comp->factor);
-+        OPJ_INT32 l_2 = opj_int_ceildivpow2((OPJ_INT32)l_img_comp->x0, (OPJ_INT32)l_img_comp->factor);
-+        if (l_1 < l_2) {
-             opj_event_msg(p_manager, EVT_ERROR,
--                          "Size x of the decoded component image is incorrect (comp[%d].w=%d).\n",
--                          it_comp, l_w);
-+                          "Size x of the decoded component image is incorrect (comp[%d].w<0).\n",
-+                          it_comp);
-             return OPJ_FALSE;
-         }
--        l_img_comp->w = (OPJ_UINT32)l_w;
-+        l_img_comp->w = (OPJ_UINT32)(l_1-l_2);
- 
--        l_h = opj_int_ceildivpow2(l_comp_y1, (OPJ_INT32)l_img_comp->factor)
--              - opj_int_ceildivpow2((OPJ_INT32)l_img_comp->y0, (OPJ_INT32)l_img_comp->factor);
--        if (l_h < 0) {
-+        l_1 = opj_int_ceildivpow2(l_comp_y1, (OPJ_INT32)l_img_comp->factor);
-+        l_2 = opj_int_ceildivpow2((OPJ_INT32)l_img_comp->y0, (OPJ_INT32)l_img_comp->factor);
-+        if (l_1 < l_2) {
-             opj_event_msg(p_manager, EVT_ERROR,
--                          "Size y of the decoded component image is incorrect (comp[%d].h=%d).\n",
--                          it_comp, l_h);
-+                          "Size y of the decoded component image is incorrect (comp[%d].h<0).\n",
-+                          it_comp);
-             return OPJ_FALSE;
-         }
--        l_img_comp->h = (OPJ_UINT32)l_h;
-+        l_img_comp->h = (OPJ_UINT32)(l_1-l_2);
- 
-         l_img_comp++;
-     }
diff --git a/third_party/libopenjpeg20/0037-tcd_init_tile.patch b/third_party/libopenjpeg20/0037-tcd_init_tile.patch
deleted file mode 100644
index e38a7ec..0000000
--- a/third_party/libopenjpeg20/0037-tcd_init_tile.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-diff --git a/third_party/libopenjpeg20/tcd.c b/third_party/libopenjpeg20/tcd.c
-index 2ae211ef4..9e98f04ab 100644
---- a/third_party/libopenjpeg20/tcd.c
-+++ b/third_party/libopenjpeg20/tcd.c
-@@ -910,8 +910,24 @@ static INLINE OPJ_BOOL opj_tcd_init_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
-             /* p. 64, B.6, ISO/IEC FDIS15444-1 : 2000 (18 august 2000)  */
-             l_tl_prc_x_start = opj_int_floordivpow2(l_res->x0, (OPJ_INT32)l_pdx) << l_pdx;
-             l_tl_prc_y_start = opj_int_floordivpow2(l_res->y0, (OPJ_INT32)l_pdy) << l_pdy;
--            l_br_prc_x_end = opj_int_ceildivpow2(l_res->x1, (OPJ_INT32)l_pdx) << l_pdx;
--            l_br_prc_y_end = opj_int_ceildivpow2(l_res->y1, (OPJ_INT32)l_pdy) << l_pdy;
-+            {
-+                OPJ_UINT32 tmp = ((OPJ_UINT32)opj_int_ceildivpow2(l_res->x1,
-+                                  (OPJ_INT32)l_pdx)) << l_pdx;
-+                if (tmp > (OPJ_UINT32)INT_MAX) {
-+                    opj_event_msg(manager, EVT_ERROR, "Integer overflow\n");
-+                    return OPJ_FALSE;
-+                }
-+                l_br_prc_x_end = (OPJ_INT32)tmp;
-+            }
-+            {
-+                OPJ_UINT32 tmp = ((OPJ_UINT32)opj_int_ceildivpow2(l_res->y1,
-+                                  (OPJ_INT32)l_pdy)) << l_pdy;
-+                if (tmp > (OPJ_UINT32)INT_MAX) {
-+                    opj_event_msg(manager, EVT_ERROR, "Integer overflow\n");
-+                    return OPJ_FALSE;
-+                }
-+                l_br_prc_y_end = (OPJ_INT32)tmp;
-+            }
-             /*fprintf(stderr, "\t\t\tprc_x_start=%d, prc_y_start=%d, br_prc_x_end=%d, br_prc_y_end=%d \n", l_tl_prc_x_start, l_tl_prc_y_start, l_br_prc_x_end ,l_br_prc_y_end );*/
- 
-             l_res->pw = (l_res->x0 == l_res->x1) ? 0U : (OPJ_UINT32)((
diff --git a/third_party/libopenjpeg20/README.pdfium b/third_party/libopenjpeg20/README.pdfium
deleted file mode 100644
index 2a13a61..0000000
--- a/third_party/libopenjpeg20/README.pdfium
+++ /dev/null
@@ -1,31 +0,0 @@
-Name: OpenJPEG
-URL: http://www.openjpeg.org/
-Version: 2.3.1 (also update in opj_config*)
-Security Critical: yes
-License: 2-clause BSD
-
-Description:
-JPEG 2000 library.
-
-Local Modifications:
-
-0003-dwt-decode.patch: Check array bounds for opj_dwt_decode_1() and friends.
-0005-jp2_apply_pclr.patch: Fix out of bounds access.
-0006-tcd_init_tile.patch: Fix a divide by zero bug in opj_tcd_init_tile().
-0007-jp2_read_cmap.patch: Fix wrong rendering on greyscale images with index colorspace.
-0009-opj_pi_next.patch: Fix potential bad precno value in opj_pi_next* functions.
-0011-j2k_update_image_data.patch: Prevent bad signed -> unsigned casting.
-0012-mct_sse.patch: Don't use SSE intrinsics in 32-bit builds.
-0014-opj_jp2_read_ihdr_leak.patch: Memory leak in opj_jp2_read_ihdr().
-0015-read_SPCod_SPCoc_overflow.patch: Prevent a buffer overflow in opj_j2k_read_SPCod_SPCoc.
-0016-read_SQcd_SQcc_overflow.patch: Prevent a buffer overflow in opj_j2k_read_SQcd_SQcc.
-0019-tcd_init_tile.patch: Prevent integer overflows during calculation of |l_nb_code_blocks_size|.
-0022-jp2_apply_pclr_overflow.patch: Prevent integer overflow in opj_jp2_apply_pclr.
-0023-opj_j2k_read_mct_records.patch: Fix opj_j2k_read to prevent heap-use-after-free.
-0025-opj_j2k_add_mct_null_data.patch: Check m_data != null before trying to read from it.
-0026-use_opj_uint_ceildiv.patch: Remove (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)a, (OPJ_INT32) b).
-0033-undefined-shift-opj_t1_dec_clnpass.patch: fix undefined shifts originated from opj_t1_decode_cblk.
-0034-opj_malloc.patch: PDFium changes in opj_malloc.
-0035-opj_image_data_free.patch: Use the right free function in opj_jp2_apply_pclr.
-0036-opj_j2k_update_image_dimensions.patch: fix integer overflow.
-0037-tcd_init_tile.patch: Avoid integer overflow in opj_tcd_init_tile().
diff --git a/third_party/libopenjpeg20/cio.h b/third_party/libopenjpeg20/cio.h
deleted file mode 100644
index 6996a9a..0000000
--- a/third_party/libopenjpeg20/cio.h
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
- * Copyright (c) 2012, CS Systemes d'Information, France
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef OPJ_CIO_H
-#define OPJ_CIO_H
-/**
-@file cio.h
-@brief Implementation of a byte input-output process (CIO)
-
-The functions in CIO.C have for goal to realize a byte input / output process.
-*/
-
-/** @defgroup CIO CIO - byte input-output stream */
-/*@{*/
-
-#include "opj_config_private.h"
-
-/* ----------------------------------------------------------------------- */
-
-#if defined(OPJ_BIG_ENDIAN)
-#define opj_write_bytes     opj_write_bytes_BE
-#define opj_read_bytes      opj_read_bytes_BE
-#define opj_write_double    opj_write_double_BE
-#define opj_read_double     opj_read_double_BE
-#define opj_write_float     opj_write_float_BE
-#define opj_read_float      opj_read_float_BE
-#else
-#define opj_write_bytes     opj_write_bytes_LE
-#define opj_read_bytes      opj_read_bytes_LE
-#define opj_write_double    opj_write_double_LE
-#define opj_read_double     opj_read_double_LE
-#define opj_write_float     opj_write_float_LE
-#define opj_read_float      opj_read_float_LE
-#endif
-
-
-#define OPJ_STREAM_STATUS_OUTPUT  0x1U
-#define OPJ_STREAM_STATUS_INPUT   0x2U
-#define OPJ_STREAM_STATUS_END     0x4U
-#define OPJ_STREAM_STATUS_ERROR   0x8U
-
-/**
-Byte input-output stream.
-*/
-typedef struct opj_stream_private {
-    /**
-     * User data, be it files, ... The actual data depends on the type of the stream.
-     */
-    void *                  m_user_data;
-
-    /**
-     * Pointer to function to free m_user_data (NULL at initialization)
-     * when destroying the stream. If pointer is NULL the function is not
-     * called and the m_user_data is not freed (even if non-NULL).
-     */
-    opj_stream_free_user_data_fn        m_free_user_data_fn;
-
-    /**
-     * User data length
-     */
-    OPJ_UINT64              m_user_data_length;
-
-    /**
-     * Pointer to actual read function (NULL at the initialization of the cio.
-     */
-    opj_stream_read_fn      m_read_fn;
-
-    /**
-     * Pointer to actual write function (NULL at the initialization of the cio.
-     */
-    opj_stream_write_fn     m_write_fn;
-
-    /**
-     * Pointer to actual skip function (NULL at the initialization of the cio.
-     * There is no seek function to prevent from back and forth slow procedures.
-     */
-    opj_stream_skip_fn      m_skip_fn;
-
-    /**
-     * Pointer to actual seek function (if available).
-     */
-    opj_stream_seek_fn      m_seek_fn;
-
-    /**
-     * Actual data stored into the stream if readed from. Data is read by chunk of fixed size.
-     * you should never access this data directly.
-     */
-    OPJ_BYTE *                  m_stored_data;
-
-    /**
-     * Pointer to the current read data.
-     */
-    OPJ_BYTE *                  m_current_data;
-
-    /**
-    * FIXME DOC.
-    */
-    OPJ_OFF_T(* m_opj_skip)(struct opj_stream_private *, OPJ_OFF_T,
-                            struct opj_event_mgr *);
-
-    /**
-    * FIXME DOC.
-    */
-    OPJ_BOOL(* m_opj_seek)(struct opj_stream_private *, OPJ_OFF_T,
-                           struct opj_event_mgr *);
-
-    /**
-     * number of bytes containing in the buffer.
-     */
-    OPJ_SIZE_T          m_bytes_in_buffer;
-
-    /**
-     * The number of bytes read/written from the beginning of the stream
-     */
-    OPJ_OFF_T           m_byte_offset;
-
-    /**
-     * The size of the buffer.
-     */
-    OPJ_SIZE_T          m_buffer_size;
-
-    /**
-     * Flags to tell the status of the stream.
-     * Used with OPJ_STREAM_STATUS_* defines.
-     */
-    OPJ_UINT32 m_status;
-
-}
-opj_stream_private_t;
-
-/** @name Exported functions (see also openjpeg.h) */
-/*@{*/
-/* ----------------------------------------------------------------------- */
-/**
- * Write some bytes to the given data buffer, this function is used in Big Endian cpus.
- * @param p_buffer      pointer the data buffer to write data to.
- * @param p_value       the value to write
- * @param p_nb_bytes    the number of bytes to write
-*/
-void opj_write_bytes_BE(OPJ_BYTE * p_buffer, OPJ_UINT32 p_value,
-                        OPJ_UINT32 p_nb_bytes);
-
-/**
- * Reads some bytes from the given data buffer, this function is used in Big Endian cpus.
- * @param p_buffer      pointer the data buffer to read data from.
- * @param p_value       pointer to the value that will store the data.
- * @param p_nb_bytes    the nb bytes to read.
- * @return              the number of bytes read or -1 if an error occurred.
- */
-void opj_read_bytes_BE(const OPJ_BYTE * p_buffer, OPJ_UINT32 * p_value,
-                       OPJ_UINT32 p_nb_bytes);
-
-/**
- * Write some bytes to the given data buffer, this function is used in Little Endian cpus.
- * @param p_buffer      pointer the data buffer to write data to.
- * @param p_value       the value to write
- * @param p_nb_bytes    the number of bytes to write
- * @return              the number of bytes written or -1 if an error occurred
-*/
-void opj_write_bytes_LE(OPJ_BYTE * p_buffer, OPJ_UINT32 p_value,
-                        OPJ_UINT32 p_nb_bytes);
-
-/**
- * Reads some bytes from the given data buffer, this function is used in Little Endian cpus.
- * @param p_buffer      pointer the data buffer to read data from.
- * @param p_value       pointer to the value that will store the data.
- * @param p_nb_bytes    the nb bytes to read.
- * @return              the number of bytes read or -1 if an error occurred.
- */
-void opj_read_bytes_LE(const OPJ_BYTE * p_buffer, OPJ_UINT32 * p_value,
-                       OPJ_UINT32 p_nb_bytes);
-
-
-/**
- * Write some bytes to the given data buffer, this function is used in Little Endian cpus.
- * @param p_buffer      pointer the data buffer to write data to.
- * @param p_value       the value to write
- */
-void opj_write_double_LE(OPJ_BYTE * p_buffer, OPJ_FLOAT64 p_value);
-
-/***
- * Write some bytes to the given data buffer, this function is used in Big Endian cpus.
- * @param p_buffer      pointer the data buffer to write data to.
- * @param p_value       the value to write
- */
-void opj_write_double_BE(OPJ_BYTE * p_buffer, OPJ_FLOAT64 p_value);
-
-/**
- * Reads some bytes from the given data buffer, this function is used in Little Endian cpus.
- * @param p_buffer      pointer the data buffer to read data from.
- * @param p_value       pointer to the value that will store the data.
- */
-void opj_read_double_LE(const OPJ_BYTE * p_buffer, OPJ_FLOAT64 * p_value);
-
-/**
- * Reads some bytes from the given data buffer, this function is used in Big Endian cpus.
- * @param p_buffer      pointer the data buffer to read data from.
- * @param p_value       pointer to the value that will store the data.
- */
-void opj_read_double_BE(const OPJ_BYTE * p_buffer, OPJ_FLOAT64 * p_value);
-
-/**
- * Reads some bytes from the given data buffer, this function is used in Little Endian cpus.
- * @param p_buffer      pointer the data buffer to read data from.
- * @param p_value       pointer to the value that will store the data.
- */
-void opj_read_float_LE(const OPJ_BYTE * p_buffer, OPJ_FLOAT32 * p_value);
-
-/**
- * Reads some bytes from the given data buffer, this function is used in Big Endian cpus.
- * @param p_buffer      pointer the data buffer to read data from.
- * @param p_value       pointer to the value that will store the data.
- */
-void opj_read_float_BE(const OPJ_BYTE * p_buffer, OPJ_FLOAT32 * p_value);
-
-/**
- * Write some bytes to the given data buffer, this function is used in Little Endian cpus.
- * @param p_buffer      pointer the data buffer to write data to.
- * @param p_value       the value to write
- */
-void opj_write_float_LE(OPJ_BYTE * p_buffer, OPJ_FLOAT32 p_value);
-
-/***
- * Write some bytes to the given data buffer, this function is used in Big Endian cpus.
- * @param p_buffer      pointer the data buffer to write data to.
- * @param p_value       the value to write
- */
-void opj_write_float_BE(OPJ_BYTE * p_buffer, OPJ_FLOAT32 p_value);
-
-/**
- * Reads some bytes from the stream.
- * @param       p_stream    the stream to read data from.
- * @param       p_buffer    pointer to the data buffer that will receive the data.
- * @param       p_size      number of bytes to read.
- * @param       p_event_mgr the user event manager to be notified of special events.
- * @return      the number of bytes read, or -1 if an error occurred or if the stream is at the end.
- */
-OPJ_SIZE_T opj_stream_read_data(opj_stream_private_t * p_stream,
-                                OPJ_BYTE * p_buffer, OPJ_SIZE_T p_size, struct opj_event_mgr * p_event_mgr);
-
-/**
- * Writes some bytes to the stream.
- * @param       p_stream    the stream to write data to.
- * @param       p_buffer    pointer to the data buffer holds the data to be writtent.
- * @param       p_size      number of bytes to write.
- * @param       p_event_mgr the user event manager to be notified of special events.
- * @return      the number of bytes writtent, or -1 if an error occurred.
- */
-OPJ_SIZE_T opj_stream_write_data(opj_stream_private_t * p_stream,
-                                 const OPJ_BYTE * p_buffer, OPJ_SIZE_T p_size,
-                                 struct opj_event_mgr * p_event_mgr);
-
-/**
- * Writes the content of the stream buffer to the stream.
- * @param       p_stream    the stream to write data to.
- * @param       p_event_mgr the user event manager to be notified of special events.
- * @return      true if the data could be flushed, false else.
- */
-OPJ_BOOL opj_stream_flush(opj_stream_private_t * p_stream,
-                          struct opj_event_mgr * p_event_mgr);
-
-/**
- * Skips a number of bytes from the stream.
- * @param       p_stream    the stream to skip data from.
- * @param       p_size      the number of bytes to skip.
- * @param       p_event_mgr the user event manager to be notified of special events.
- * @return      the number of bytes skipped, or -1 if an error occurred.
- */
-OPJ_OFF_T opj_stream_skip(opj_stream_private_t * p_stream, OPJ_OFF_T p_size,
-                          struct opj_event_mgr * p_event_mgr);
-
-/**
- * Tells the byte offset on the stream (similar to ftell).
- *
- * @param       p_stream    the stream to get the information from.
- *
- * @return      the current position o fthe stream.
- */
-OPJ_OFF_T opj_stream_tell(const opj_stream_private_t * p_stream);
-
-
-/**
- * Get the number of bytes left before the end of the stream (similar to cio_numbytesleft).
- *
- * @param       p_stream    the stream to get the information from.
- *
- * @return      Number of bytes left before the end of the stream.
- */
-OPJ_OFF_T opj_stream_get_number_byte_left(const opj_stream_private_t *
-        p_stream);
-
-/**
- * Skips a number of bytes from the stream.
- * @param       p_stream    the stream to skip data from.
- * @param       p_size      the number of bytes to skip.
- * @param       p_event_mgr the user event manager to be notified of special events.
- * @return      the number of bytes skipped, or -1 if an error occurred.
- */
-OPJ_OFF_T opj_stream_write_skip(opj_stream_private_t * p_stream,
-                                OPJ_OFF_T p_size, struct opj_event_mgr * p_event_mgr);
-
-/**
- * Skips a number of bytes from the stream.
- * @param       p_stream    the stream to skip data from.
- * @param       p_size      the number of bytes to skip.
- * @param       p_event_mgr the user event manager to be notified of special events.
- * @return      the number of bytes skipped, or -1 if an error occurred.
- */
-OPJ_OFF_T opj_stream_read_skip(opj_stream_private_t * p_stream,
-                               OPJ_OFF_T p_size, struct opj_event_mgr * p_event_mgr);
-
-/**
- * Skips a number of bytes from the stream.
- * @param       p_stream    the stream to skip data from.
- * @param       p_size      the number of bytes to skip.
- * @param       p_event_mgr the user event manager to be notified of special events.
- * @return      OPJ_TRUE if success, or OPJ_FALSE if an error occurred.
- */
-OPJ_BOOL opj_stream_read_seek(opj_stream_private_t * p_stream, OPJ_OFF_T p_size,
-                              struct opj_event_mgr * p_event_mgr);
-
-/**
- * Skips a number of bytes from the stream.
- * @param       p_stream    the stream to skip data from.
- * @param       p_size      the number of bytes to skip.
- * @param       p_event_mgr the user event manager to be notified of special events.
- * @return      the number of bytes skipped, or -1 if an error occurred.
- */
-OPJ_BOOL opj_stream_write_seek(opj_stream_private_t * p_stream,
-                               OPJ_OFF_T p_size, struct opj_event_mgr * p_event_mgr);
-
-/**
- * Seeks a number of bytes from the stream.
- * @param       p_stream    the stream to skip data from.
- * @param       p_size      the number of bytes to skip.
- * @param       p_event_mgr the user event manager to be notified of special events.
- * @return      true if the stream is seekable.
- */
-OPJ_BOOL opj_stream_seek(opj_stream_private_t * p_stream, OPJ_OFF_T p_size,
-                         struct opj_event_mgr * p_event_mgr);
-
-/**
- * Tells if the given stream is seekable.
- */
-OPJ_BOOL opj_stream_has_seek(const opj_stream_private_t * p_stream);
-
-/**
- * FIXME DOC.
- */
-OPJ_SIZE_T opj_stream_default_read(void * p_buffer, OPJ_SIZE_T p_nb_bytes,
-                                   void * p_user_data);
-
-/**
- * FIXME DOC.
- */
-OPJ_SIZE_T opj_stream_default_write(void * p_buffer, OPJ_SIZE_T p_nb_bytes,
-                                    void * p_user_data);
-
-/**
- * FIXME DOC.
- */
-OPJ_OFF_T opj_stream_default_skip(OPJ_OFF_T p_nb_bytes, void * p_user_data);
-
-/**
- * FIXME DOC.
- */
-OPJ_BOOL opj_stream_default_seek(OPJ_OFF_T p_nb_bytes, void * p_user_data);
-
-/* ----------------------------------------------------------------------- */
-/*@}*/
-
-/*@}*/
-
-
-#endif /* OPJ_CIO_H */
-
diff --git a/third_party/libopenjpeg20/dwt.c b/third_party/libopenjpeg20/dwt.c
deleted file mode 100644
index 6512b1e..0000000
--- a/third_party/libopenjpeg20/dwt.c
+++ /dev/null
@@ -1,2901 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2007, Jonathan Ballard <dzonatas@dzonux.net>
- * Copyright (c) 2007, Callum Lerwick <seg@haxxed.com>
- * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <assert.h>
-
-#define OPJ_SKIP_POISON
-#include "opj_includes.h"
-
-#ifdef __SSE__
-#include <xmmintrin.h>
-#endif
-#ifdef __SSE2__
-#include <emmintrin.h>
-#endif
-#ifdef __SSSE3__
-#include <tmmintrin.h>
-#endif
-#ifdef __AVX2__
-#include <immintrin.h>
-#endif
-
-#if defined(__GNUC__)
-#pragma GCC poison malloc calloc realloc free
-#endif
-
-/** @defgroup DWT DWT - Implementation of a discrete wavelet transform */
-/*@{*/
-
-#ifdef __AVX2__
-/** Number of int32 values in a AVX2 register */
-#define VREG_INT_COUNT       8
-#else
-/** Number of int32 values in a SSE2 register */
-#define VREG_INT_COUNT       4
-#endif
-
-/** Number of columns that we can process in parallel in the vertical pass */
-#define PARALLEL_COLS_53     (2*VREG_INT_COUNT)
-
-/** @name Local data structures */
-/*@{*/
-
-typedef struct dwt_local {
-    OPJ_INT32* mem;
-    OPJ_SIZE_T mem_count;
-    OPJ_INT32 dn;   /* number of elements in high pass band */
-    OPJ_INT32 sn;   /* number of elements in low pass band */
-    OPJ_INT32 cas;  /* 0 = start on even coord, 1 = start on odd coord */
-} opj_dwt_t;
-
-typedef union {
-    OPJ_FLOAT32 f[4];
-} opj_v4_t;
-
-typedef struct v4dwt_local {
-    opj_v4_t*   wavelet ;
-    OPJ_INT32       dn ;  /* number of elements in high pass band */
-    OPJ_INT32       sn ;  /* number of elements in low pass band */
-    OPJ_INT32       cas ; /* 0 = start on even coord, 1 = start on odd coord */
-    OPJ_UINT32      win_l_x0; /* start coord in low pass band */
-    OPJ_UINT32      win_l_x1; /* end coord in low pass band */
-    OPJ_UINT32      win_h_x0; /* start coord in high pass band */
-    OPJ_UINT32      win_h_x1; /* end coord in high pass band */
-} opj_v4dwt_t ;
-
-static const OPJ_FLOAT32 opj_dwt_alpha =  1.586134342f; /*  12994 */
-static const OPJ_FLOAT32 opj_dwt_beta  =  0.052980118f; /*    434 */
-static const OPJ_FLOAT32 opj_dwt_gamma = -0.882911075f; /*  -7233 */
-static const OPJ_FLOAT32 opj_dwt_delta = -0.443506852f; /*  -3633 */
-
-static const OPJ_FLOAT32 opj_K      = 1.230174105f; /*  10078 */
-static const OPJ_FLOAT32 opj_c13318 = 1.625732422f;
-
-/*@}*/
-
-/**
-Virtual function type for wavelet transform in 1-D
-*/
-typedef void (*DWT1DFN)(const opj_dwt_t* v);
-
-/** @name Local static functions */
-/*@{*/
-
-/**
-Forward lazy transform (horizontal)
-*/
-static void opj_dwt_deinterleave_h(OPJ_INT32 *a, OPJ_INT32 *b, OPJ_INT32 dn,
-                                   OPJ_INT32 sn, OPJ_INT32 cas);
-/**
-Forward lazy transform (vertical)
-*/
-static void opj_dwt_deinterleave_v(OPJ_INT32 *a, OPJ_INT32 *b, OPJ_INT32 dn,
-                                   OPJ_INT32 sn, OPJ_INT32 x, OPJ_INT32 cas);
-/**
-Forward 5-3 wavelet transform in 1-D
-*/
-static void opj_dwt_encode_1(OPJ_INT32 *a, OPJ_SIZE_T a_count, OPJ_INT32 dn,
-    OPJ_INT32 sn, OPJ_INT32 cas);
-/**
-Forward 9-7 wavelet transform in 1-D
-*/
-static void opj_dwt_encode_1_real(OPJ_INT32 *a, OPJ_SIZE_T a_count,
-    OPJ_INT32 dn, OPJ_INT32 sn, OPJ_INT32 cas);
-/**
-Explicit calculation of the Quantization Stepsizes
-*/
-static void opj_dwt_encode_stepsize(OPJ_INT32 stepsize, OPJ_INT32 numbps,
-                                    opj_stepsize_t *bandno_stepsize);
-/**
-Inverse wavelet transform in 2-D.
-*/
-static OPJ_BOOL opj_dwt_decode_tile(opj_thread_pool_t* tp,
-                                    const opj_tcd_tilecomp_t* tilec, OPJ_UINT32 i);
-
-static OPJ_BOOL opj_dwt_decode_partial_tile(
-    opj_tcd_tilecomp_t* tilec,
-    OPJ_UINT32 numres);
-
-static OPJ_BOOL opj_dwt_encode_procedure(const opj_tcd_tilecomp_t * tilec,
-        void(*p_function)(OPJ_INT32 *, OPJ_SIZE_T, OPJ_INT32, OPJ_INT32, OPJ_INT32));
-
-static OPJ_UINT32 opj_dwt_max_resolution(opj_tcd_resolution_t* OPJ_RESTRICT r,
-        OPJ_UINT32 i);
-
-/* <summary>                             */
-/* Inverse 9-7 wavelet transform in 1-D. */
-/* </summary>                            */
-static void opj_v4dwt_decode(opj_v4dwt_t* OPJ_RESTRICT dwt);
-
-static void opj_v4dwt_interleave_h(opj_v4dwt_t* OPJ_RESTRICT dwt,
-                                   OPJ_FLOAT32* OPJ_RESTRICT a,
-                                   OPJ_UINT32 width,
-                                   OPJ_UINT32 remaining_height);
-
-static void opj_v4dwt_interleave_v(opj_v4dwt_t* OPJ_RESTRICT dwt,
-                                   OPJ_FLOAT32* OPJ_RESTRICT a,
-                                   OPJ_UINT32 width,
-                                   OPJ_UINT32 nb_elts_read);
-
-#ifdef __SSE__
-static void opj_v4dwt_decode_step1_sse(opj_v4_t* w,
-                                       OPJ_UINT32 start,
-                                       OPJ_UINT32 end,
-                                       const __m128 c);
-
-static void opj_v4dwt_decode_step2_sse(opj_v4_t* l, opj_v4_t* w,
-                                       OPJ_UINT32 start,
-                                       OPJ_UINT32 end,
-                                       OPJ_UINT32 m, __m128 c);
-
-#else
-static void opj_v4dwt_decode_step1(opj_v4_t* w,
-                                   OPJ_UINT32 start,
-                                   OPJ_UINT32 end,
-                                   const OPJ_FLOAT32 c);
-
-static void opj_v4dwt_decode_step2(opj_v4_t* l, opj_v4_t* w,
-                                   OPJ_UINT32 start,
-                                   OPJ_UINT32 end,
-                                   OPJ_UINT32 m,
-                                   OPJ_FLOAT32 c);
-
-#endif
-
-/*@}*/
-
-/*@}*/
-
-#define IDX_S(i) (i)*2
-#define IDX_D(i) 1 + (i)* 2
-#define UNDERFLOW_SN(i) ((i) >= sn&&sn>0)
-#define UNDERFLOW_DN(i) ((i) >= dn&&dn>0)
-#define OVERFLOW_S(i) (IDX_S(i) >= a_count)
-#define OVERFLOW_D(i) (IDX_D(i) >= a_count)
-
-#define OPJ_S(i) a[IDX_S(i)]
-#define OPJ_D(i) a[IDX_D(i)]
-#define OPJ_S_(i) ((i)<0 ? OPJ_S(0) : (UNDERFLOW_SN(i) ? OPJ_S(sn - 1) : OVERFLOW_S(i) ? OPJ_S(i - 1) : OPJ_S(i)))
-#define OPJ_D_(i) ((i)<0 ? OPJ_D(0) : (UNDERFLOW_DN(i) ? OPJ_D(dn - 1) : OVERFLOW_D(i) ? OPJ_D(i - 1) : OPJ_D(i)))
-/* new */
-#define OPJ_SS_(i) ((i)<0 ? OPJ_S(0) : (UNDERFLOW_DN(i) ? OPJ_S(dn - 1) : OVERFLOW_S(i) ? OPJ_S(i - 1) : OPJ_S(i)))
-#define OPJ_DD_(i) ((i)<0 ? OPJ_D(0) : (UNDERFLOW_SN(i) ? OPJ_D(sn - 1) : OVERFLOW_D(i) ? OPJ_D(i - 1) : OPJ_D(i)))
-
-/* <summary>                                                              */
-/* This table contains the norms of the 5-3 wavelets for different bands. */
-/* </summary>                                                             */
-/* FIXME! the array should really be extended up to 33 resolution levels */
-/* See https://github.com/uclouvain/openjpeg/issues/493 */
-static const OPJ_FLOAT64 opj_dwt_norms[4][10] = {
-    {1.000, 1.500, 2.750, 5.375, 10.68, 21.34, 42.67, 85.33, 170.7, 341.3},
-    {1.038, 1.592, 2.919, 5.703, 11.33, 22.64, 45.25, 90.48, 180.9},
-    {1.038, 1.592, 2.919, 5.703, 11.33, 22.64, 45.25, 90.48, 180.9},
-    {.7186, .9218, 1.586, 3.043, 6.019, 12.01, 24.00, 47.97, 95.93}
-};
-
-/* <summary>                                                              */
-/* This table contains the norms of the 9-7 wavelets for different bands. */
-/* </summary>                                                             */
-/* FIXME! the array should really be extended up to 33 resolution levels */
-/* See https://github.com/uclouvain/openjpeg/issues/493 */
-static const OPJ_FLOAT64 opj_dwt_norms_real[4][10] = {
-    {1.000, 1.965, 4.177, 8.403, 16.90, 33.84, 67.69, 135.3, 270.6, 540.9},
-    {2.022, 3.989, 8.355, 17.04, 34.27, 68.63, 137.3, 274.6, 549.0},
-    {2.022, 3.989, 8.355, 17.04, 34.27, 68.63, 137.3, 274.6, 549.0},
-    {2.080, 3.865, 8.307, 17.18, 34.71, 69.59, 139.3, 278.6, 557.2}
-};
-
-/*
-==========================================================
-   local functions
-==========================================================
-*/
-
-/* <summary>                             */
-/* Forward lazy transform (horizontal).  */
-/* </summary>                            */
-static void opj_dwt_deinterleave_h(OPJ_INT32 *a, OPJ_INT32 *b, OPJ_INT32 dn,
-                                   OPJ_INT32 sn, OPJ_INT32 cas)
-{
-    OPJ_INT32 i;
-    OPJ_INT32 * l_dest = b;
-    OPJ_INT32 * l_src = a + cas;
-
-    for (i = 0; i < sn; ++i) {
-        *l_dest++ = *l_src;
-        l_src += 2;
-    }
-
-    l_dest = b + sn;
-    l_src = a + 1 - cas;
-
-    for (i = 0; i < dn; ++i)  {
-        *l_dest++ = *l_src;
-        l_src += 2;
-    }
-}
-
-/* <summary>                             */
-/* Forward lazy transform (vertical).    */
-/* </summary>                            */
-static void opj_dwt_deinterleave_v(OPJ_INT32 *a, OPJ_INT32 *b, OPJ_INT32 dn,
-                                   OPJ_INT32 sn, OPJ_INT32 x, OPJ_INT32 cas)
-{
-    OPJ_INT32 i = sn;
-    OPJ_INT32 * l_dest = b;
-    OPJ_INT32 * l_src = a + cas;
-
-    while (i--) {
-        *l_dest = *l_src;
-        l_dest += x;
-        l_src += 2;
-    } /* b[i*x]=a[2*i+cas]; */
-
-    l_dest = b + (OPJ_SIZE_T)sn * (OPJ_SIZE_T)x;
-    l_src = a + 1 - cas;
-
-    i = dn;
-    while (i--) {
-        *l_dest = *l_src;
-        l_dest += x;
-        l_src += 2;
-    } /*b[(sn+i)*x]=a[(2*i+1-cas)];*/
-}
-
-#ifdef STANDARD_SLOW_VERSION
-/* <summary>                             */
-/* Inverse lazy transform (horizontal).  */
-/* </summary>                            */
-static void opj_dwt_interleave_h(const opj_dwt_t* h, OPJ_INT32 *a)
-{
-    OPJ_INT32 *ai = a;
-    OPJ_INT32 *bi = h->mem + h->cas;
-    OPJ_INT32  i    = h->sn;
-    while (i--) {
-        *bi = *(ai++);
-        bi += 2;
-    }
-    ai  = a + h->sn;
-    bi  = h->mem + 1 - h->cas;
-    i   = h->dn ;
-    while (i--) {
-        *bi = *(ai++);
-        bi += 2;
-    }
-}
-
-/* <summary>                             */
-/* Inverse lazy transform (vertical).    */
-/* </summary>                            */
-static void opj_dwt_interleave_v(const opj_dwt_t* v, OPJ_INT32 *a, OPJ_INT32 x)
-{
-    OPJ_INT32 *ai = a;
-    OPJ_INT32 *bi = v->mem + v->cas;
-    OPJ_INT32  i = v->sn;
-    while (i--) {
-        *bi = *ai;
-        bi += 2;
-        ai += x;
-    }
-    ai = a + (v->sn * (OPJ_SIZE_T)x);
-    bi = v->mem + 1 - v->cas;
-    i = v->dn ;
-    while (i--) {
-        *bi = *ai;
-        bi += 2;
-        ai += x;
-    }
-}
-
-#endif /* STANDARD_SLOW_VERSION */
-
-/* <summary>                            */
-/* Forward 5-3 wavelet transform in 1-D. */
-/* </summary>                           */
-static void opj_dwt_encode_1(OPJ_INT32 *a, OPJ_SIZE_T a_count, OPJ_INT32 dn,
-                             OPJ_INT32 sn, OPJ_INT32 cas)
-{
-    OPJ_INT32 i;
-
-    if (!cas) {
-        if ((dn > 0) || (sn > 1)) { /* NEW :  CASE ONE ELEMENT */
-            for (i = 0; i < dn; i++) {
-                OPJ_D(i) -= (OPJ_S_(i) + OPJ_S_(i + 1)) >> 1;
-            }
-            for (i = 0; i < sn; i++) {
-                OPJ_S(i) += (OPJ_D_(i - 1) + OPJ_D_(i) + 2) >> 2;
-            }
-        }
-    } else {
-        if (!sn && dn == 1) {       /* NEW :  CASE ONE ELEMENT */
-            OPJ_S(0) *= 2;
-        } else {
-            for (i = 0; i < dn; i++) {
-                OPJ_S(i) -= (OPJ_DD_(i) + OPJ_DD_(i - 1)) >> 1;
-            }
-            for (i = 0; i < sn; i++) {
-                OPJ_D(i) += (OPJ_SS_(i) + OPJ_SS_(i + 1) + 2) >> 2;
-            }
-        }
-    }
-}
-
-#ifdef STANDARD_SLOW_VERSION
-/* <summary>                            */
-/* Inverse 5-3 wavelet transform in 1-D. */
-/* </summary>                           */
-static void opj_dwt_decode_1_(OPJ_INT32 *a, OPJ_SIZE_T a_count, OPJ_INT32 dn,
-                              OPJ_INT32 sn, OPJ_INT32 cas)
-{
-    OPJ_INT32 i;
-
-    if (!cas) {
-        if ((dn > 0) || (sn > 1)) { /* NEW :  CASE ONE ELEMENT */
-            for (i = 0; i < sn; i++) {
-                OPJ_S(i) -= (OPJ_D_(i - 1) + OPJ_D_(i) + 2) >> 2;
-            }
-            for (i = 0; i < dn; i++) {
-                OPJ_D(i) += (OPJ_S_(i) + OPJ_S_(i + 1)) >> 1;
-            }
-        }
-    } else {
-        if (!sn  && dn == 1) {        /* NEW :  CASE ONE ELEMENT */
-            OPJ_S(0) /= 2;
-        } else {
-            for (i = 0; i < sn; i++) {
-                OPJ_D(i) -= (OPJ_SS_(i) + OPJ_SS_(i + 1) + 2) >> 2;
-            }
-            for (i = 0; i < dn; i++) {
-                OPJ_S(i) += (OPJ_DD_(i) + OPJ_DD_(i - 1)) >> 1;
-            }
-        }
-    }
-}
-
-static void opj_dwt_decode_1(const opj_dwt_t *v)
-{
-    opj_dwt_decode_1_(v->mem, v->mem_count, v->dn, v->sn, v->cas);
-}
-
-#endif /* STANDARD_SLOW_VERSION */
-
-#if !defined(STANDARD_SLOW_VERSION)
-static void  opj_idwt53_h_cas0(OPJ_INT32* tmp,
-                               const OPJ_INT32 sn,
-                               const OPJ_INT32 len,
-                               OPJ_INT32* tiledp)
-{
-    OPJ_INT32 i, j;
-    const OPJ_INT32* in_even = &tiledp[0];
-    const OPJ_INT32* in_odd = &tiledp[sn];
-
-#ifdef TWO_PASS_VERSION
-    /* For documentation purpose: performs lifting in two iterations, */
-    /* but without explicit interleaving */
-
-    assert(len > 1);
-
-    /* Even */
-    tmp[0] = in_even[0] - ((in_odd[0] + 1) >> 1);
-    for (i = 2, j = 0; i <= len - 2; i += 2, j++) {
-        tmp[i] = in_even[j + 1] - ((in_odd[j] + in_odd[j + 1] + 2) >> 2);
-    }
-    if (len & 1) { /* if len is odd */
-        tmp[len - 1] = in_even[(len - 1) / 2] - ((in_odd[(len - 2) / 2] + 1) >> 1);
-    }
-
-    /* Odd */
-    for (i = 1, j = 0; i < len - 1; i += 2, j++) {
-        tmp[i] = in_odd[j] + ((tmp[i - 1] + tmp[i + 1]) >> 1);
-    }
-    if (!(len & 1)) { /* if len is even */
-        tmp[len - 1] = in_odd[(len - 1) / 2] + tmp[len - 2];
-    }
-#else
-    OPJ_INT32 d1c, d1n, s1n, s0c, s0n;
-
-    assert(len > 1);
-
-    /* Improved version of the TWO_PASS_VERSION: */
-    /* Performs lifting in one single iteration. Saves memory */
-    /* accesses and explicit interleaving. */
-    s1n = in_even[0];
-    d1n = in_odd[0];
-    s0n = s1n - ((d1n + 1) >> 1);
-
-    for (i = 0, j = 1; i < (len - 3); i += 2, j++) {
-        d1c = d1n;
-        s0c = s0n;
-
-        s1n = in_even[j];
-        d1n = in_odd[j];
-
-        s0n = s1n - ((d1c + d1n + 2) >> 2);
-
-        tmp[i  ] = s0c;
-        tmp[i + 1] = d1c + ((s0c + s0n) >> 1);
-    }
-
-    tmp[i] = s0n;
-
-    if (len & 1) {
-        tmp[len - 1] = in_even[(len - 1) / 2] - ((d1n + 1) >> 1);
-        tmp[len - 2] = d1n + ((s0n + tmp[len - 1]) >> 1);
-    } else {
-        tmp[len - 1] = d1n + s0n;
-    }
-#endif
-    memcpy(tiledp, tmp, (OPJ_UINT32)len * sizeof(OPJ_INT32));
-}
-
-static void  opj_idwt53_h_cas1(OPJ_INT32* tmp,
-                               const OPJ_INT32 sn,
-                               const OPJ_INT32 len,
-                               OPJ_INT32* tiledp)
-{
-    OPJ_INT32 i, j;
-    const OPJ_INT32* in_even = &tiledp[sn];
-    const OPJ_INT32* in_odd = &tiledp[0];
-
-#ifdef TWO_PASS_VERSION
-    /* For documentation purpose: performs lifting in two iterations, */
-    /* but without explicit interleaving */
-
-    assert(len > 2);
-
-    /* Odd */
-    for (i = 1, j = 0; i < len - 1; i += 2, j++) {
-        tmp[i] = in_odd[j] - ((in_even[j] + in_even[j + 1] + 2) >> 2);
-    }
-    if (!(len & 1)) {
-        tmp[len - 1] = in_odd[len / 2 - 1] - ((in_even[len / 2 - 1] + 1) >> 1);
-    }
-
-    /* Even */
-    tmp[0] = in_even[0] + tmp[1];
-    for (i = 2, j = 1; i < len - 1; i += 2, j++) {
-        tmp[i] = in_even[j] + ((tmp[i + 1] + tmp[i - 1]) >> 1);
-    }
-    if (len & 1) {
-        tmp[len - 1] = in_even[len / 2] + tmp[len - 2];
-    }
-#else
-    OPJ_INT32 s1, s2, dc, dn;
-
-    assert(len > 2);
-
-    /* Improved version of the TWO_PASS_VERSION: */
-    /* Performs lifting in one single iteration. Saves memory */
-    /* accesses and explicit interleaving. */
-
-    s1 = in_even[1];
-    dc = in_odd[0] - ((in_even[0] + s1 + 2) >> 2);
-    tmp[0] = in_even[0] + dc;
-
-    for (i = 1, j = 1; i < (len - 2 - !(len & 1)); i += 2, j++) {
-
-        s2 = in_even[j + 1];
-
-        dn = in_odd[j] - ((s1 + s2 + 2) >> 2);
-        tmp[i  ] = dc;
-        tmp[i + 1] = s1 + ((dn + dc) >> 1);
-
-        dc = dn;
-        s1 = s2;
-    }
-
-    tmp[i] = dc;
-
-    if (!(len & 1)) {
-        dn = in_odd[len / 2 - 1] - ((s1 + 1) >> 1);
-        tmp[len - 2] = s1 + ((dn + dc) >> 1);
-        tmp[len - 1] = dn;
-    } else {
-        tmp[len - 1] = s1 + dc;
-    }
-#endif
-    memcpy(tiledp, tmp, (OPJ_UINT32)len * sizeof(OPJ_INT32));
-}
-
-
-#endif /* !defined(STANDARD_SLOW_VERSION) */
-
-/* <summary>                            */
-/* Inverse 5-3 wavelet transform in 1-D for one row. */
-/* </summary>                           */
-/* Performs interleave, inverse wavelet transform and copy back to buffer */
-static void opj_idwt53_h(const opj_dwt_t *dwt,
-                         OPJ_INT32* tiledp)
-{
-#ifdef STANDARD_SLOW_VERSION
-    /* For documentation purpose */
-    opj_dwt_interleave_h(dwt, tiledp);
-    opj_dwt_decode_1(dwt);
-    memcpy(tiledp, dwt->mem, (OPJ_UINT32)(dwt->sn + dwt->dn) * sizeof(OPJ_INT32));
-#else
-    const OPJ_INT32 sn = dwt->sn;
-    const OPJ_INT32 len = sn + dwt->dn;
-    if (dwt->cas == 0) { /* Left-most sample is on even coordinate */
-        if (len > 1) {
-            opj_idwt53_h_cas0(dwt->mem, sn, len, tiledp);
-        } else {
-            /* Unmodified value */
-        }
-    } else { /* Left-most sample is on odd coordinate */
-        if (len == 1) {
-            tiledp[0] /= 2;
-        } else if (len == 2) {
-            OPJ_INT32* out = dwt->mem;
-            const OPJ_INT32* in_even = &tiledp[sn];
-            const OPJ_INT32* in_odd = &tiledp[0];
-            out[1] = in_odd[0] - ((in_even[0] + 1) >> 1);
-            out[0] = in_even[0] + out[1];
-            memcpy(tiledp, dwt->mem, (OPJ_UINT32)len * sizeof(OPJ_INT32));
-        } else if (len > 2) {
-            opj_idwt53_h_cas1(dwt->mem, sn, len, tiledp);
-        }
-    }
-#endif
-}
-
-#if (defined(__SSE2__) || defined(__AVX2__)) && !defined(STANDARD_SLOW_VERSION)
-
-/* Conveniency macros to improve the readabilty of the formulas */
-#if __AVX2__
-#define VREG        __m256i
-#define LOAD_CST(x) _mm256_set1_epi32(x)
-#define LOAD(x)     _mm256_load_si256((const VREG*)(x))
-#define LOADU(x)    _mm256_loadu_si256((const VREG*)(x))
-#define STORE(x,y)  _mm256_store_si256((VREG*)(x),(y))
-#define STOREU(x,y) _mm256_storeu_si256((VREG*)(x),(y))
-#define ADD(x,y)    _mm256_add_epi32((x),(y))
-#define SUB(x,y)    _mm256_sub_epi32((x),(y))
-#define SAR(x,y)    _mm256_srai_epi32((x),(y))
-#else
-#define VREG        __m128i
-#define LOAD_CST(x) _mm_set1_epi32(x)
-#define LOAD(x)     _mm_load_si128((const VREG*)(x))
-#define LOADU(x)    _mm_loadu_si128((const VREG*)(x))
-#define STORE(x,y)  _mm_store_si128((VREG*)(x),(y))
-#define STOREU(x,y) _mm_storeu_si128((VREG*)(x),(y))
-#define ADD(x,y)    _mm_add_epi32((x),(y))
-#define SUB(x,y)    _mm_sub_epi32((x),(y))
-#define SAR(x,y)    _mm_srai_epi32((x),(y))
-#endif
-#define ADD3(x,y,z) ADD(ADD(x,y),z)
-
-static
-void opj_idwt53_v_final_memcpy(OPJ_INT32* tiledp_col,
-                               const OPJ_INT32* tmp,
-                               OPJ_INT32 len,
-                               OPJ_SIZE_T stride)
-{
-    OPJ_INT32 i;
-    for (i = 0; i < len; ++i) {
-        /* A memcpy(&tiledp_col[i * stride + 0],
-                    &tmp[PARALLEL_COLS_53 * i + 0],
-                    PARALLEL_COLS_53 * sizeof(OPJ_INT32))
-           would do but would be a tiny bit slower.
-           We can take here advantage of our knowledge of alignment */
-        STOREU(&tiledp_col[(OPJ_SIZE_T)i * stride + 0],
-               LOAD(&tmp[PARALLEL_COLS_53 * i + 0]));
-        STOREU(&tiledp_col[(OPJ_SIZE_T)i * stride + VREG_INT_COUNT],
-               LOAD(&tmp[PARALLEL_COLS_53 * i + VREG_INT_COUNT]));
-    }
-}
-
-/** Vertical inverse 5x3 wavelet transform for 8 columns in SSE2, or
- * 16 in AVX2, when top-most pixel is on even coordinate */
-static void opj_idwt53_v_cas0_mcols_SSE2_OR_AVX2(
-    OPJ_INT32* tmp,
-    const OPJ_INT32 sn,
-    const OPJ_INT32 len,
-    OPJ_INT32* tiledp_col,
-    const OPJ_SIZE_T stride)
-{
-    const OPJ_INT32* in_even = &tiledp_col[0];
-    const OPJ_INT32* in_odd = &tiledp_col[(OPJ_SIZE_T)sn * stride];
-
-    OPJ_INT32 i;
-    OPJ_SIZE_T j;
-    VREG d1c_0, d1n_0, s1n_0, s0c_0, s0n_0;
-    VREG d1c_1, d1n_1, s1n_1, s0c_1, s0n_1;
-    const VREG two = LOAD_CST(2);
-
-    assert(len > 1);
-#if __AVX2__
-    assert(PARALLEL_COLS_53 == 16);
-    assert(VREG_INT_COUNT == 8);
-#else
-    assert(PARALLEL_COLS_53 == 8);
-    assert(VREG_INT_COUNT == 4);
-#endif
-
-    /* Note: loads of input even/odd values must be done in a unaligned */
-    /* fashion. But stores in tmp can be done with aligned store, since */
-    /* the temporary buffer is properly aligned */
-    assert((OPJ_SIZE_T)tmp % (sizeof(OPJ_INT32) * VREG_INT_COUNT) == 0);
-
-    s1n_0 = LOADU(in_even + 0);
-    s1n_1 = LOADU(in_even + VREG_INT_COUNT);
-    d1n_0 = LOADU(in_odd);
-    d1n_1 = LOADU(in_odd + VREG_INT_COUNT);
-
-    /* s0n = s1n - ((d1n + 1) >> 1); <==> */
-    /* s0n = s1n - ((d1n + d1n + 2) >> 2); */
-    s0n_0 = SUB(s1n_0, SAR(ADD3(d1n_0, d1n_0, two), 2));
-    s0n_1 = SUB(s1n_1, SAR(ADD3(d1n_1, d1n_1, two), 2));
-
-    for (i = 0, j = 1; i < (len - 3); i += 2, j++) {
-        d1c_0 = d1n_0;
-        s0c_0 = s0n_0;
-        d1c_1 = d1n_1;
-        s0c_1 = s0n_1;
-
-        s1n_0 = LOADU(in_even + j * stride);
-        s1n_1 = LOADU(in_even + j * stride + VREG_INT_COUNT);
-        d1n_0 = LOADU(in_odd + j * stride);
-        d1n_1 = LOADU(in_odd + j * stride + VREG_INT_COUNT);
-
-        /*s0n = s1n - ((d1c + d1n + 2) >> 2);*/
-        s0n_0 = SUB(s1n_0, SAR(ADD3(d1c_0, d1n_0, two), 2));
-        s0n_1 = SUB(s1n_1, SAR(ADD3(d1c_1, d1n_1, two), 2));
-
-        STORE(tmp + PARALLEL_COLS_53 * (i + 0), s0c_0);
-        STORE(tmp + PARALLEL_COLS_53 * (i + 0) + VREG_INT_COUNT, s0c_1);
-
-        /* d1c + ((s0c + s0n) >> 1) */
-        STORE(tmp + PARALLEL_COLS_53 * (i + 1) + 0,
-              ADD(d1c_0, SAR(ADD(s0c_0, s0n_0), 1)));
-        STORE(tmp + PARALLEL_COLS_53 * (i + 1) + VREG_INT_COUNT,
-              ADD(d1c_1, SAR(ADD(s0c_1, s0n_1), 1)));
-    }
-
-    STORE(tmp + PARALLEL_COLS_53 * (i + 0) + 0, s0n_0);
-    STORE(tmp + PARALLEL_COLS_53 * (i + 0) + VREG_INT_COUNT, s0n_1);
-
-    if (len & 1) {
-        VREG tmp_len_minus_1;
-        s1n_0 = LOADU(in_even + (OPJ_SIZE_T)((len - 1) / 2) * stride);
-        /* tmp_len_minus_1 = s1n - ((d1n + 1) >> 1); */
-        tmp_len_minus_1 = SUB(s1n_0, SAR(ADD3(d1n_0, d1n_0, two), 2));
-        STORE(tmp + PARALLEL_COLS_53 * (len - 1), tmp_len_minus_1);
-        /* d1n + ((s0n + tmp_len_minus_1) >> 1) */
-        STORE(tmp + PARALLEL_COLS_53 * (len - 2),
-              ADD(d1n_0, SAR(ADD(s0n_0, tmp_len_minus_1), 1)));
-
-        s1n_1 = LOADU(in_even + (OPJ_SIZE_T)((len - 1) / 2) * stride + VREG_INT_COUNT);
-        /* tmp_len_minus_1 = s1n - ((d1n + 1) >> 1); */
-        tmp_len_minus_1 = SUB(s1n_1, SAR(ADD3(d1n_1, d1n_1, two), 2));
-        STORE(tmp + PARALLEL_COLS_53 * (len - 1) + VREG_INT_COUNT,
-              tmp_len_minus_1);
-        /* d1n + ((s0n + tmp_len_minus_1) >> 1) */
-        STORE(tmp + PARALLEL_COLS_53 * (len - 2) + VREG_INT_COUNT,
-              ADD(d1n_1, SAR(ADD(s0n_1, tmp_len_minus_1), 1)));
-
-
-    } else {
-        STORE(tmp + PARALLEL_COLS_53 * (len - 1) + 0,
-              ADD(d1n_0, s0n_0));
-        STORE(tmp + PARALLEL_COLS_53 * (len - 1) + VREG_INT_COUNT,
-              ADD(d1n_1, s0n_1));
-    }
-
-    opj_idwt53_v_final_memcpy(tiledp_col, tmp, len, stride);
-}
-
-
-/** Vertical inverse 5x3 wavelet transform for 8 columns in SSE2, or
- * 16 in AVX2, when top-most pixel is on odd coordinate */
-static void opj_idwt53_v_cas1_mcols_SSE2_OR_AVX2(
-    OPJ_INT32* tmp,
-    const OPJ_INT32 sn,
-    const OPJ_INT32 len,
-    OPJ_INT32* tiledp_col,
-    const OPJ_SIZE_T stride)
-{
-    OPJ_INT32 i;
-    OPJ_SIZE_T j;
-
-    VREG s1_0, s2_0, dc_0, dn_0;
-    VREG s1_1, s2_1, dc_1, dn_1;
-    const VREG two = LOAD_CST(2);
-
-    const OPJ_INT32* in_even = &tiledp_col[(OPJ_SIZE_T)sn * stride];
-    const OPJ_INT32* in_odd = &tiledp_col[0];
-
-    assert(len > 2);
-#if __AVX2__
-    assert(PARALLEL_COLS_53 == 16);
-    assert(VREG_INT_COUNT == 8);
-#else
-    assert(PARALLEL_COLS_53 == 8);
-    assert(VREG_INT_COUNT == 4);
-#endif
-
-    /* Note: loads of input even/odd values must be done in a unaligned */
-    /* fashion. But stores in tmp can be done with aligned store, since */
-    /* the temporary buffer is properly aligned */
-    assert((OPJ_SIZE_T)tmp % (sizeof(OPJ_INT32) * VREG_INT_COUNT) == 0);
-
-    s1_0 = LOADU(in_even + stride);
-    /* in_odd[0] - ((in_even[0] + s1 + 2) >> 2); */
-    dc_0 = SUB(LOADU(in_odd + 0),
-               SAR(ADD3(LOADU(in_even + 0), s1_0, two), 2));
-    STORE(tmp + PARALLEL_COLS_53 * 0, ADD(LOADU(in_even + 0), dc_0));
-
-    s1_1 = LOADU(in_even + stride + VREG_INT_COUNT);
-    /* in_odd[0] - ((in_even[0] + s1 + 2) >> 2); */
-    dc_1 = SUB(LOADU(in_odd + VREG_INT_COUNT),
-               SAR(ADD3(LOADU(in_even + VREG_INT_COUNT), s1_1, two), 2));
-    STORE(tmp + PARALLEL_COLS_53 * 0 + VREG_INT_COUNT,
-          ADD(LOADU(in_even + VREG_INT_COUNT), dc_1));
-
-    for (i = 1, j = 1; i < (len - 2 - !(len & 1)); i += 2, j++) {
-
-        s2_0 = LOADU(in_even + (j + 1) * stride);
-        s2_1 = LOADU(in_even + (j + 1) * stride + VREG_INT_COUNT);
-
-        /* dn = in_odd[j * stride] - ((s1 + s2 + 2) >> 2); */
-        dn_0 = SUB(LOADU(in_odd + j * stride),
-                   SAR(ADD3(s1_0, s2_0, two), 2));
-        dn_1 = SUB(LOADU(in_odd + j * stride + VREG_INT_COUNT),
-                   SAR(ADD3(s1_1, s2_1, two), 2));
-
-        STORE(tmp + PARALLEL_COLS_53 * i, dc_0);
-        STORE(tmp + PARALLEL_COLS_53 * i + VREG_INT_COUNT, dc_1);
-
-        /* tmp[i + 1] = s1 + ((dn + dc) >> 1); */
-        STORE(tmp + PARALLEL_COLS_53 * (i + 1) + 0,
-              ADD(s1_0, SAR(ADD(dn_0, dc_0), 1)));
-        STORE(tmp + PARALLEL_COLS_53 * (i + 1) + VREG_INT_COUNT,
-              ADD(s1_1, SAR(ADD(dn_1, dc_1), 1)));
-
-        dc_0 = dn_0;
-        s1_0 = s2_0;
-        dc_1 = dn_1;
-        s1_1 = s2_1;
-    }
-    STORE(tmp + PARALLEL_COLS_53 * i, dc_0);
-    STORE(tmp + PARALLEL_COLS_53 * i + VREG_INT_COUNT, dc_1);
-
-    if (!(len & 1)) {
-        /*dn = in_odd[(len / 2 - 1) * stride] - ((s1 + 1) >> 1); */
-        dn_0 = SUB(LOADU(in_odd + (OPJ_SIZE_T)(len / 2 - 1) * stride),
-                   SAR(ADD3(s1_0, s1_0, two), 2));
-        dn_1 = SUB(LOADU(in_odd + (OPJ_SIZE_T)(len / 2 - 1) * stride + VREG_INT_COUNT),
-                   SAR(ADD3(s1_1, s1_1, two), 2));
-
-        /* tmp[len - 2] = s1 + ((dn + dc) >> 1); */
-        STORE(tmp + PARALLEL_COLS_53 * (len - 2) + 0,
-              ADD(s1_0, SAR(ADD(dn_0, dc_0), 1)));
-        STORE(tmp + PARALLEL_COLS_53 * (len - 2) + VREG_INT_COUNT,
-              ADD(s1_1, SAR(ADD(dn_1, dc_1), 1)));
-
-        STORE(tmp + PARALLEL_COLS_53 * (len - 1) + 0, dn_0);
-        STORE(tmp + PARALLEL_COLS_53 * (len - 1) + VREG_INT_COUNT, dn_1);
-    } else {
-        STORE(tmp + PARALLEL_COLS_53 * (len - 1) + 0, ADD(s1_0, dc_0));
-        STORE(tmp + PARALLEL_COLS_53 * (len - 1) + VREG_INT_COUNT,
-              ADD(s1_1, dc_1));
-    }
-
-    opj_idwt53_v_final_memcpy(tiledp_col, tmp, len, stride);
-}
-
-#undef VREG
-#undef LOAD_CST
-#undef LOADU
-#undef LOAD
-#undef STORE
-#undef STOREU
-#undef ADD
-#undef ADD3
-#undef SUB
-#undef SAR
-
-#endif /* (defined(__SSE2__) || defined(__AVX2__)) && !defined(STANDARD_SLOW_VERSION) */
-
-#if !defined(STANDARD_SLOW_VERSION)
-/** Vertical inverse 5x3 wavelet transform for one column, when top-most
- * pixel is on even coordinate */
-static void opj_idwt3_v_cas0(OPJ_INT32* tmp,
-                             const OPJ_INT32 sn,
-                             const OPJ_INT32 len,
-                             OPJ_INT32* tiledp_col,
-                             const OPJ_SIZE_T stride)
-{
-    OPJ_INT32 i, j;
-    OPJ_INT32 d1c, d1n, s1n, s0c, s0n;
-
-    assert(len > 1);
-
-    /* Performs lifting in one single iteration. Saves memory */
-    /* accesses and explicit interleaving. */
-
-    s1n = tiledp_col[0];
-    d1n = tiledp_col[(OPJ_SIZE_T)sn * stride];
-    s0n = s1n - ((d1n + 1) >> 1);
-
-    for (i = 0, j = 0; i < (len - 3); i += 2, j++) {
-        d1c = d1n;
-        s0c = s0n;
-
-        s1n = tiledp_col[(OPJ_SIZE_T)(j + 1) * stride];
-        d1n = tiledp_col[(OPJ_SIZE_T)(sn + j + 1) * stride];
-
-        s0n = s1n - ((d1c + d1n + 2) >> 2);
-
-        tmp[i  ] = s0c;
-        tmp[i + 1] = d1c + ((s0c + s0n) >> 1);
-    }
-
-    tmp[i] = s0n;
-
-    if (len & 1) {
-        tmp[len - 1] =
-            tiledp_col[(OPJ_SIZE_T)((len - 1) / 2) * stride] -
-            ((d1n + 1) >> 1);
-        tmp[len - 2] = d1n + ((s0n + tmp[len - 1]) >> 1);
-    } else {
-        tmp[len - 1] = d1n + s0n;
-    }
-
-    for (i = 0; i < len; ++i) {
-        tiledp_col[(OPJ_SIZE_T)i * stride] = tmp[i];
-    }
-}
-
-
-/** Vertical inverse 5x3 wavelet transform for one column, when top-most
- * pixel is on odd coordinate */
-static void opj_idwt3_v_cas1(OPJ_INT32* tmp,
-                             const OPJ_INT32 sn,
-                             const OPJ_INT32 len,
-                             OPJ_INT32* tiledp_col,
-                             const OPJ_SIZE_T stride)
-{
-    OPJ_INT32 i, j;
-    OPJ_INT32 s1, s2, dc, dn;
-    const OPJ_INT32* in_even = &tiledp_col[(OPJ_SIZE_T)sn * stride];
-    const OPJ_INT32* in_odd = &tiledp_col[0];
-
-    assert(len > 2);
-
-    /* Performs lifting in one single iteration. Saves memory */
-    /* accesses and explicit interleaving. */
-
-    s1 = in_even[stride];
-    dc = in_odd[0] - ((in_even[0] + s1 + 2) >> 2);
-    tmp[0] = in_even[0] + dc;
-    for (i = 1, j = 1; i < (len - 2 - !(len & 1)); i += 2, j++) {
-
-        s2 = in_even[(OPJ_SIZE_T)(j + 1) * stride];
-
-        dn = in_odd[(OPJ_SIZE_T)j * stride] - ((s1 + s2 + 2) >> 2);
-        tmp[i  ] = dc;
-        tmp[i + 1] = s1 + ((dn + dc) >> 1);
-
-        dc = dn;
-        s1 = s2;
-    }
-    tmp[i] = dc;
-    if (!(len & 1)) {
-        dn = in_odd[(OPJ_SIZE_T)(len / 2 - 1) * stride] - ((s1 + 1) >> 1);
-        tmp[len - 2] = s1 + ((dn + dc) >> 1);
-        tmp[len - 1] = dn;
-    } else {
-        tmp[len - 1] = s1 + dc;
-    }
-
-    for (i = 0; i < len; ++i) {
-        tiledp_col[(OPJ_SIZE_T)i * stride] = tmp[i];
-    }
-}
-#endif /* !defined(STANDARD_SLOW_VERSION) */
-
-/* <summary>                            */
-/* Inverse vertical 5-3 wavelet transform in 1-D for several columns. */
-/* </summary>                           */
-/* Performs interleave, inverse wavelet transform and copy back to buffer */
-static void opj_idwt53_v(const opj_dwt_t *dwt,
-                         OPJ_INT32* tiledp_col,
-                         OPJ_SIZE_T stride,
-                         OPJ_INT32 nb_cols)
-{
-#ifdef STANDARD_SLOW_VERSION
-    /* For documentation purpose */
-    OPJ_INT32 k, c;
-    for (c = 0; c < nb_cols; c ++) {
-        opj_dwt_interleave_v(dwt, tiledp_col + c, stride);
-        opj_dwt_decode_1(dwt);
-        for (k = 0; k < dwt->sn + dwt->dn; ++k) {
-            tiledp_col[c + k * stride] = dwt->mem[k];
-        }
-    }
-#else
-    const OPJ_INT32 sn = dwt->sn;
-    const OPJ_INT32 len = sn + dwt->dn;
-    if (dwt->cas == 0) {
-        /* If len == 1, unmodified value */
-
-#if (defined(__SSE2__) || defined(__AVX2__))
-        if (len > 1 && nb_cols == PARALLEL_COLS_53) {
-            /* Same as below general case, except that thanks to SSE2/AVX2 */
-            /* we can efficiently process 8/16 columns in parallel */
-            opj_idwt53_v_cas0_mcols_SSE2_OR_AVX2(dwt->mem, sn, len, tiledp_col, stride);
-            return;
-        }
-#endif
-        if (len > 1) {
-            OPJ_INT32 c;
-            for (c = 0; c < nb_cols; c++, tiledp_col++) {
-                opj_idwt3_v_cas0(dwt->mem, sn, len, tiledp_col, stride);
-            }
-            return;
-        }
-    } else {
-        if (len == 1) {
-            OPJ_INT32 c;
-            for (c = 0; c < nb_cols; c++, tiledp_col++) {
-                tiledp_col[0] /= 2;
-            }
-            return;
-        }
-
-        if (len == 2) {
-            OPJ_INT32 c;
-            OPJ_INT32* out = dwt->mem;
-            for (c = 0; c < nb_cols; c++, tiledp_col++) {
-                OPJ_INT32 i;
-                const OPJ_INT32* in_even = &tiledp_col[(OPJ_SIZE_T)sn * stride];
-                const OPJ_INT32* in_odd = &tiledp_col[0];
-
-                out[1] = in_odd[0] - ((in_even[0] + 1) >> 1);
-                out[0] = in_even[0] + out[1];
-
-                for (i = 0; i < len; ++i) {
-                    tiledp_col[(OPJ_SIZE_T)i * stride] = out[i];
-                }
-            }
-
-            return;
-        }
-
-#if (defined(__SSE2__) || defined(__AVX2__))
-        if (len > 2 && nb_cols == PARALLEL_COLS_53) {
-            /* Same as below general case, except that thanks to SSE2/AVX2 */
-            /* we can efficiently process 8/16 columns in parallel */
-            opj_idwt53_v_cas1_mcols_SSE2_OR_AVX2(dwt->mem, sn, len, tiledp_col, stride);
-            return;
-        }
-#endif
-        if (len > 2) {
-            OPJ_INT32 c;
-            for (c = 0; c < nb_cols; c++, tiledp_col++) {
-                opj_idwt3_v_cas1(dwt->mem, sn, len, tiledp_col, stride);
-            }
-            return;
-        }
-    }
-#endif
-}
-
-
-/* <summary>                             */
-/* Forward 9-7 wavelet transform in 1-D. */
-/* </summary>                            */
-static void opj_dwt_encode_1_real(OPJ_INT32 *a, OPJ_SIZE_T a_count,
-                                  OPJ_INT32 dn, OPJ_INT32 sn, OPJ_INT32 cas)
-{
-    OPJ_INT32 i;
-    if (!cas) {
-        if ((dn > 0) || (sn > 1)) { /* NEW :  CASE ONE ELEMENT */
-            for (i = 0; i < dn; i++) {
-                OPJ_D(i) -= opj_int_fix_mul(OPJ_S_(i) + OPJ_S_(i + 1), 12993);
-            }
-            for (i = 0; i < sn; i++) {
-                OPJ_S(i) -= opj_int_fix_mul(OPJ_D_(i - 1) + OPJ_D_(i), 434);
-            }
-            for (i = 0; i < dn; i++) {
-                OPJ_D(i) += opj_int_fix_mul(OPJ_S_(i) + OPJ_S_(i + 1), 7233);
-            }
-            for (i = 0; i < sn; i++) {
-                OPJ_S(i) += opj_int_fix_mul(OPJ_D_(i - 1) + OPJ_D_(i), 3633);
-            }
-            for (i = 0; i < dn; i++) {
-                OPJ_D(i) = opj_int_fix_mul(OPJ_D(i), 5038);    /*5038 */
-            }
-            for (i = 0; i < sn; i++) {
-                OPJ_S(i) = opj_int_fix_mul(OPJ_S(i), 6659);    /*6660 */
-            }
-        }
-    } else {
-        if ((sn > 0) || (dn > 1)) { /* NEW :  CASE ONE ELEMENT */
-            for (i = 0; i < dn; i++) {
-                OPJ_S(i) -= opj_int_fix_mul(OPJ_DD_(i) + OPJ_DD_(i - 1), 12993);
-            }
-            for (i = 0; i < sn; i++) {
-                OPJ_D(i) -= opj_int_fix_mul(OPJ_SS_(i) + OPJ_SS_(i + 1), 434);
-            }
-            for (i = 0; i < dn; i++) {
-                OPJ_S(i) += opj_int_fix_mul(OPJ_DD_(i) + OPJ_DD_(i - 1), 7233);
-            }
-            for (i = 0; i < sn; i++) {
-                OPJ_D(i) += opj_int_fix_mul(OPJ_SS_(i) + OPJ_SS_(i + 1), 3633);
-            }
-            for (i = 0; i < dn; i++) {
-                OPJ_S(i) = opj_int_fix_mul(OPJ_S(i), 5038);    /*5038 */
-            }
-            for (i = 0; i < sn; i++) {
-                OPJ_D(i) = opj_int_fix_mul(OPJ_D(i), 6659);    /*6660 */
-            }
-        }
-    }
-}
-
-static void opj_dwt_encode_stepsize(OPJ_INT32 stepsize, OPJ_INT32 numbps,
-                                    opj_stepsize_t *bandno_stepsize)
-{
-    OPJ_INT32 p, n;
-    p = opj_int_floorlog2(stepsize) - 13;
-    n = 11 - opj_int_floorlog2(stepsize);
-    bandno_stepsize->mant = (n < 0 ? stepsize >> -n : stepsize << n) & 0x7ff;
-    bandno_stepsize->expn = numbps - p;
-}
-
-/*
-==========================================================
-   DWT interface
-==========================================================
-*/
-
-
-/* <summary>                            */
-/* Forward 5-3 wavelet transform in 2-D. */
-/* </summary>                           */
-static INLINE OPJ_BOOL opj_dwt_encode_procedure(const opj_tcd_tilecomp_t * tilec,
-        void(*p_function)(OPJ_INT32 *, OPJ_SIZE_T, OPJ_INT32, OPJ_INT32, OPJ_INT32))
-{
-    OPJ_INT32 i, j, k;
-    OPJ_INT32 *a = 00;
-    OPJ_INT32 *aj = 00;
-    OPJ_INT32 *bj = 00;
-    OPJ_INT32 w, l;
-
-    OPJ_INT32 rw;           /* width of the resolution level computed   */
-    OPJ_INT32 rh;           /* height of the resolution level computed  */
-    OPJ_SIZE_T l_data_count;
-    OPJ_SIZE_T l_data_size;
-
-    opj_tcd_resolution_t * l_cur_res = 0;
-    opj_tcd_resolution_t * l_last_res = 0;
-
-    w = tilec->x1 - tilec->x0;
-    l = (OPJ_INT32)tilec->numresolutions - 1;
-    a = tilec->data;
-
-    l_cur_res = tilec->resolutions + l;
-    l_last_res = l_cur_res - 1;
-
-    l_data_count = opj_dwt_max_resolution(tilec->resolutions, tilec->numresolutions);
-    /* overflow check */
-    if (l_data_count > (SIZE_MAX / sizeof(OPJ_INT32))) {
-        /* FIXME event manager error callback */
-        return OPJ_FALSE;
-    }
-    l_data_size = l_data_count * sizeof(OPJ_INT32);
-    bj = (OPJ_INT32*)opj_malloc(l_data_size);
-    /* l_data_size is equal to 0 when numresolutions == 1 but bj is not used */
-    /* in that case, so do not error out */
-    if (l_data_size != 0 && ! bj) {
-        return OPJ_FALSE;
-    }
-    i = l;
-
-    while (i--) {
-        OPJ_INT32 rw1;      /* width of the resolution level once lower than computed one                                       */
-        OPJ_INT32 rh1;      /* height of the resolution level once lower than computed one                                      */
-        OPJ_INT32 cas_col;  /* 0 = non inversion on horizontal filtering 1 = inversion between low-pass and high-pass filtering */
-        OPJ_INT32 cas_row;  /* 0 = non inversion on vertical filtering 1 = inversion between low-pass and high-pass filtering   */
-        OPJ_INT32 dn, sn;
-
-        rw  = l_cur_res->x1 - l_cur_res->x0;
-        rh  = l_cur_res->y1 - l_cur_res->y0;
-        rw1 = l_last_res->x1 - l_last_res->x0;
-        rh1 = l_last_res->y1 - l_last_res->y0;
-
-        cas_row = l_cur_res->x0 & 1;
-        cas_col = l_cur_res->y0 & 1;
-
-        sn = rh1;
-        dn = rh - rh1;
-        for (j = 0; j < rw; ++j) {
-            aj = a + j;
-            for (k = 0; k < rh; ++k) {
-                bj[k] = aj[k * w];
-            }
-
-            (*p_function) (bj, l_data_count, dn, sn, cas_col);
-
-            opj_dwt_deinterleave_v(bj, aj, dn, sn, w, cas_col);
-        }
-
-        sn = rw1;
-        dn = rw - rw1;
-
-        for (j = 0; j < rh; j++) {
-            aj = a + j * w;
-            for (k = 0; k < rw; k++) {
-                bj[k] = aj[k];
-            }
-            (*p_function) (bj, l_data_count, dn, sn, cas_row);
-            opj_dwt_deinterleave_h(bj, aj, dn, sn, cas_row);
-        }
-
-        l_cur_res = l_last_res;
-
-        --l_last_res;
-    }
-
-    opj_free(bj);
-    return OPJ_TRUE;
-}
-
-/* Forward 5-3 wavelet transform in 2-D. */
-/* </summary>                           */
-OPJ_BOOL opj_dwt_encode(opj_tcd_tilecomp_t * tilec)
-{
-    return opj_dwt_encode_procedure(tilec, opj_dwt_encode_1);
-}
-
-/* <summary>                            */
-/* Inverse 5-3 wavelet transform in 2-D. */
-/* </summary>                           */
-OPJ_BOOL opj_dwt_decode(opj_tcd_t *p_tcd, opj_tcd_tilecomp_t* tilec,
-                        OPJ_UINT32 numres)
-{
-    if (p_tcd->whole_tile_decoding) {
-        return opj_dwt_decode_tile(p_tcd->thread_pool, tilec, numres);
-    } else {
-        return opj_dwt_decode_partial_tile(tilec, numres);
-    }
-}
-
-
-/* <summary>                          */
-/* Get gain of 5-3 wavelet transform. */
-/* </summary>                         */
-OPJ_UINT32 opj_dwt_getgain(OPJ_UINT32 orient)
-{
-    if (orient == 0) {
-        return 0;
-    }
-    if (orient == 1 || orient == 2) {
-        return 1;
-    }
-    return 2;
-}
-
-/* <summary>                */
-/* Get norm of 5-3 wavelet. */
-/* </summary>               */
-OPJ_FLOAT64 opj_dwt_getnorm(OPJ_UINT32 level, OPJ_UINT32 orient)
-{
-    /* FIXME ! This is just a band-aid to avoid a buffer overflow */
-    /* but the array should really be extended up to 33 resolution levels */
-    /* See https://github.com/uclouvain/openjpeg/issues/493 */
-    if (orient == 0 && level >= 10) {
-        level = 9;
-    } else if (orient > 0 && level >= 9) {
-        level = 8;
-    }
-    return opj_dwt_norms[orient][level];
-}
-
-/* <summary>                             */
-/* Forward 9-7 wavelet transform in 2-D. */
-/* </summary>                            */
-OPJ_BOOL opj_dwt_encode_real(opj_tcd_tilecomp_t * tilec)
-{
-    return opj_dwt_encode_procedure(tilec, opj_dwt_encode_1_real);
-}
-
-/* <summary>                          */
-/* Get gain of 9-7 wavelet transform. */
-/* </summary>                         */
-OPJ_UINT32 opj_dwt_getgain_real(OPJ_UINT32 orient)
-{
-    (void)orient;
-    return 0;
-}
-
-/* <summary>                */
-/* Get norm of 9-7 wavelet. */
-/* </summary>               */
-OPJ_FLOAT64 opj_dwt_getnorm_real(OPJ_UINT32 level, OPJ_UINT32 orient)
-{
-    /* FIXME ! This is just a band-aid to avoid a buffer overflow */
-    /* but the array should really be extended up to 33 resolution levels */
-    /* See https://github.com/uclouvain/openjpeg/issues/493 */
-    if (orient == 0 && level >= 10) {
-        level = 9;
-    } else if (orient > 0 && level >= 9) {
-        level = 8;
-    }
-    return opj_dwt_norms_real[orient][level];
-}
-
-void opj_dwt_calc_explicit_stepsizes(opj_tccp_t * tccp, OPJ_UINT32 prec)
-{
-    OPJ_UINT32 numbands, bandno;
-    numbands = 3 * tccp->numresolutions - 2;
-    for (bandno = 0; bandno < numbands; bandno++) {
-        OPJ_FLOAT64 stepsize;
-        OPJ_UINT32 resno, level, orient, gain;
-
-        resno = (bandno == 0) ? 0 : ((bandno - 1) / 3 + 1);
-        orient = (bandno == 0) ? 0 : ((bandno - 1) % 3 + 1);
-        level = tccp->numresolutions - 1 - resno;
-        gain = (tccp->qmfbid == 0) ? 0 : ((orient == 0) ? 0 : (((orient == 1) ||
-                                          (orient == 2)) ? 1 : 2));
-        if (tccp->qntsty == J2K_CCP_QNTSTY_NOQNT) {
-            stepsize = 1.0;
-        } else {
-            OPJ_FLOAT64 norm = opj_dwt_norms_real[orient][level];
-            stepsize = (1 << (gain)) / norm;
-        }
-        opj_dwt_encode_stepsize((OPJ_INT32) floor(stepsize * 8192.0),
-                                (OPJ_INT32)(prec + gain), &tccp->stepsizes[bandno]);
-    }
-}
-
-/* <summary>                             */
-/* Determine maximum computed resolution level for inverse wavelet transform */
-/* </summary>                            */
-static OPJ_UINT32 opj_dwt_max_resolution(opj_tcd_resolution_t* OPJ_RESTRICT r,
-        OPJ_UINT32 i)
-{
-    OPJ_UINT32 mr   = 0;
-    OPJ_UINT32 w;
-    while (--i) {
-        ++r;
-        if (mr < (w = (OPJ_UINT32)(r->x1 - r->x0))) {
-            mr = w ;
-        }
-        if (mr < (w = (OPJ_UINT32)(r->y1 - r->y0))) {
-            mr = w ;
-        }
-    }
-    return mr ;
-}
-
-typedef struct {
-    opj_dwt_t h;
-    OPJ_UINT32 rw;
-    OPJ_UINT32 w;
-    OPJ_INT32 * OPJ_RESTRICT tiledp;
-    OPJ_UINT32 min_j;
-    OPJ_UINT32 max_j;
-} opj_dwd_decode_h_job_t;
-
-static void opj_dwt_decode_h_func(void* user_data, opj_tls_t* tls)
-{
-    OPJ_UINT32 j;
-    opj_dwd_decode_h_job_t* job;
-    (void)tls;
-
-    job = (opj_dwd_decode_h_job_t*)user_data;
-    for (j = job->min_j; j < job->max_j; j++) {
-        opj_idwt53_h(&job->h, &job->tiledp[j * job->w]);
-    }
-
-    opj_aligned_free(job->h.mem);
-    opj_free(job);
-}
-
-typedef struct {
-    opj_dwt_t v;
-    OPJ_UINT32 rh;
-    OPJ_UINT32 w;
-    OPJ_INT32 * OPJ_RESTRICT tiledp;
-    OPJ_UINT32 min_j;
-    OPJ_UINT32 max_j;
-} opj_dwd_decode_v_job_t;
-
-static void opj_dwt_decode_v_func(void* user_data, opj_tls_t* tls)
-{
-    OPJ_UINT32 j;
-    opj_dwd_decode_v_job_t* job;
-    (void)tls;
-
-    job = (opj_dwd_decode_v_job_t*)user_data;
-    for (j = job->min_j; j + PARALLEL_COLS_53 <= job->max_j;
-            j += PARALLEL_COLS_53) {
-        opj_idwt53_v(&job->v, &job->tiledp[j], (OPJ_SIZE_T)job->w,
-                     PARALLEL_COLS_53);
-    }
-    if (j < job->max_j)
-        opj_idwt53_v(&job->v, &job->tiledp[j], (OPJ_SIZE_T)job->w,
-                     (OPJ_INT32)(job->max_j - j));
-
-    opj_aligned_free(job->v.mem);
-    opj_free(job);
-}
-
-
-/* <summary>                            */
-/* Inverse wavelet transform in 2-D.    */
-/* </summary>                           */
-static OPJ_BOOL opj_dwt_decode_tile(opj_thread_pool_t* tp,
-        const opj_tcd_tilecomp_t* tilec, OPJ_UINT32 numres)
-{
-    opj_dwt_t h;
-    opj_dwt_t v;
-
-    opj_tcd_resolution_t* tr = tilec->resolutions;
-
-    OPJ_UINT32 rw = (OPJ_UINT32)(tr->x1 -
-                                 tr->x0);  /* width of the resolution level computed */
-    OPJ_UINT32 rh = (OPJ_UINT32)(tr->y1 -
-                                 tr->y0);  /* height of the resolution level computed */
-
-    OPJ_UINT32 w = (OPJ_UINT32)(tilec->resolutions[tilec->minimum_num_resolutions -
-                                                               1].x1 -
-                                tilec->resolutions[tilec->minimum_num_resolutions - 1].x0);
-    OPJ_SIZE_T h_mem_size;
-    int num_threads;
-
-    if (numres == 1U) {
-        return OPJ_TRUE;
-    }
-    num_threads = opj_thread_pool_get_thread_count(tp);
-    h.mem_count = opj_dwt_max_resolution(tr, numres);
-    /* overflow check */
-    if (h.mem_count > (SIZE_MAX / PARALLEL_COLS_53 / sizeof(OPJ_INT32))) {
-        /* FIXME event manager error callback */
-        return OPJ_FALSE;
-    }
-    /* We need PARALLEL_COLS_53 times the height of the array, */
-    /* since for the vertical pass */
-    /* we process PARALLEL_COLS_53 columns at a time */
-    h_mem_size = h.mem_count * PARALLEL_COLS_53 * sizeof(OPJ_INT32);
-    h.mem = (OPJ_INT32*)opj_aligned_32_malloc(h_mem_size);
-    if (! h.mem) {
-        /* FIXME event manager error callback */
-        return OPJ_FALSE;
-    }
-
-    v.mem_count = h.mem_count;
-    v.mem = h.mem;
-
-    while (--numres) {
-        OPJ_INT32 * OPJ_RESTRICT tiledp = tilec->data;
-        OPJ_UINT32 j;
-
-        ++tr;
-        h.sn = (OPJ_INT32)rw;
-        v.sn = (OPJ_INT32)rh;
-
-        rw = (OPJ_UINT32)(tr->x1 - tr->x0);
-        rh = (OPJ_UINT32)(tr->y1 - tr->y0);
-
-        h.dn = (OPJ_INT32)(rw - (OPJ_UINT32)h.sn);
-        h.cas = tr->x0 % 2;
-
-        if (num_threads <= 1 || rh <= 1) {
-            for (j = 0; j < rh; ++j) {
-                opj_idwt53_h(&h, &tiledp[(OPJ_SIZE_T)j * w]);
-            }
-        } else {
-            OPJ_UINT32 num_jobs = (OPJ_UINT32)num_threads;
-            OPJ_UINT32 step_j;
-
-            if (rh < num_jobs) {
-                num_jobs = rh;
-            }
-            step_j = (rh / num_jobs);
-
-            for (j = 0; j < num_jobs; j++) {
-                opj_dwd_decode_h_job_t* job;
-
-                job = (opj_dwd_decode_h_job_t*) opj_malloc(sizeof(opj_dwd_decode_h_job_t));
-                if (!job) {
-                    /* It would be nice to fallback to single thread case, but */
-                    /* unfortunately some jobs may be launched and have modified */
-                    /* tiledp, so it is not practical to recover from that error */
-                    /* FIXME event manager error callback */
-                    opj_thread_pool_wait_completion(tp, 0);
-                    opj_aligned_free(h.mem);
-                    return OPJ_FALSE;
-                }
-                job->h = h;
-                job->rw = rw;
-                job->w = w;
-                job->tiledp = tiledp;
-                job->min_j = j * step_j;
-                job->max_j = (j + 1U) * step_j; /* this can overflow */
-                if (j == (num_jobs - 1U)) {  /* this will take care of the overflow */
-                    job->max_j = rh;
-                }
-                job->h.mem = (OPJ_INT32*)opj_aligned_32_malloc(h_mem_size);
-                if (!job->h.mem) {
-                    /* FIXME event manager error callback */
-                    opj_thread_pool_wait_completion(tp, 0);
-                    opj_free(job);
-                    opj_aligned_free(h.mem);
-                    return OPJ_FALSE;
-                }
-                opj_thread_pool_submit_job(tp, opj_dwt_decode_h_func, job);
-            }
-            opj_thread_pool_wait_completion(tp, 0);
-        }
-
-        v.dn = (OPJ_INT32)(rh - (OPJ_UINT32)v.sn);
-        v.cas = tr->y0 % 2;
-
-        if (num_threads <= 1 || rw <= 1) {
-            for (j = 0; j + PARALLEL_COLS_53 <= rw;
-                    j += PARALLEL_COLS_53) {
-                opj_idwt53_v(&v, &tiledp[j], (OPJ_SIZE_T)w, PARALLEL_COLS_53);
-            }
-            if (j < rw) {
-                opj_idwt53_v(&v, &tiledp[j], (OPJ_SIZE_T)w, (OPJ_INT32)(rw - j));
-            }
-        } else {
-            OPJ_UINT32 num_jobs = (OPJ_UINT32)num_threads;
-            OPJ_UINT32 step_j;
-
-            if (rw < num_jobs) {
-                num_jobs = rw;
-            }
-            step_j = (rw / num_jobs);
-
-            for (j = 0; j < num_jobs; j++) {
-                opj_dwd_decode_v_job_t* job;
-
-                job = (opj_dwd_decode_v_job_t*) opj_malloc(sizeof(opj_dwd_decode_v_job_t));
-                if (!job) {
-                    /* It would be nice to fallback to single thread case, but */
-                    /* unfortunately some jobs may be launched and have modified */
-                    /* tiledp, so it is not practical to recover from that error */
-                    /* FIXME event manager error callback */
-                    opj_thread_pool_wait_completion(tp, 0);
-                    opj_aligned_free(v.mem);
-                    return OPJ_FALSE;
-                }
-                job->v = v;
-                job->rh = rh;
-                job->w = w;
-                job->tiledp = tiledp;
-                job->min_j = j * step_j;
-                job->max_j = (j + 1U) * step_j; /* this can overflow */
-                if (j == (num_jobs - 1U)) {  /* this will take care of the overflow */
-                    job->max_j = rw;
-                }
-                job->v.mem = (OPJ_INT32*)opj_aligned_32_malloc(h_mem_size);
-                if (!job->v.mem) {
-                    /* FIXME event manager error callback */
-                    opj_thread_pool_wait_completion(tp, 0);
-                    opj_free(job);
-                    opj_aligned_free(v.mem);
-                    return OPJ_FALSE;
-                }
-                opj_thread_pool_submit_job(tp, opj_dwt_decode_v_func, job);
-            }
-            opj_thread_pool_wait_completion(tp, 0);
-        }
-    }
-    opj_aligned_free(h.mem);
-    return OPJ_TRUE;
-}
-
-static void opj_dwt_interleave_partial_h(OPJ_INT32 *dest,
-        OPJ_INT32 cas,
-        opj_sparse_array_int32_t* sa,
-        OPJ_UINT32 sa_line,
-        OPJ_UINT32 sn,
-        OPJ_UINT32 win_l_x0,
-        OPJ_UINT32 win_l_x1,
-        OPJ_UINT32 win_h_x0,
-        OPJ_UINT32 win_h_x1)
-{
-    OPJ_BOOL ret;
-    ret = opj_sparse_array_int32_read(sa,
-                                      win_l_x0, sa_line,
-                                      win_l_x1, sa_line + 1,
-                                      dest + cas + 2 * win_l_x0,
-                                      2, 0, OPJ_TRUE);
-    assert(ret);
-    ret = opj_sparse_array_int32_read(sa,
-                                      sn + win_h_x0, sa_line,
-                                      sn + win_h_x1, sa_line + 1,
-                                      dest + 1 - cas + 2 * win_h_x0,
-                                      2, 0, OPJ_TRUE);
-    assert(ret);
-    OPJ_UNUSED(ret);
-}
-
-
-static void opj_dwt_interleave_partial_v(OPJ_INT32 *dest,
-        OPJ_INT32 cas,
-        opj_sparse_array_int32_t* sa,
-        OPJ_UINT32 sa_col,
-        OPJ_UINT32 nb_cols,
-        OPJ_UINT32 sn,
-        OPJ_UINT32 win_l_y0,
-        OPJ_UINT32 win_l_y1,
-        OPJ_UINT32 win_h_y0,
-        OPJ_UINT32 win_h_y1)
-{
-    OPJ_BOOL ret;
-    ret  = opj_sparse_array_int32_read(sa,
-                                       sa_col, win_l_y0,
-                                       sa_col + nb_cols, win_l_y1,
-                                       dest + cas * 4 + 2 * 4 * win_l_y0,
-                                       1, 2 * 4, OPJ_TRUE);
-    assert(ret);
-    ret = opj_sparse_array_int32_read(sa,
-                                      sa_col, sn + win_h_y0,
-                                      sa_col + nb_cols, sn + win_h_y1,
-                                      dest + (1 - cas) * 4 + 2 * 4 * win_h_y0,
-                                      1, 2 * 4, OPJ_TRUE);
-    assert(ret);
-    OPJ_UNUSED(ret);
-}
-
-static void opj_dwt_decode_partial_1(OPJ_INT32 *a, OPJ_SIZE_T a_count,
-                                     OPJ_INT32 dn, OPJ_INT32 sn,
-                                     OPJ_INT32 cas,
-                                     OPJ_INT32 win_l_x0,
-                                     OPJ_INT32 win_l_x1,
-                                     OPJ_INT32 win_h_x0,
-                                     OPJ_INT32 win_h_x1)
-{
-    OPJ_INT32 i;
-
-    if (!cas) {
-        if ((dn > 0) || (sn > 1)) { /* NEW :  CASE ONE ELEMENT */
-
-            /* Naive version is :
-            for (i = win_l_x0; i < i_max; i++) {
-                OPJ_S(i) -= (OPJ_D_(i - 1) + OPJ_D_(i) + 2) >> 2;
-            }
-            for (i = win_h_x0; i < win_h_x1; i++) {
-                OPJ_D(i) += (OPJ_S_(i) + OPJ_S_(i + 1)) >> 1;
-            }
-            but the compiler doesn't manage to unroll it to avoid bound
-            checking in OPJ_S_ and OPJ_D_ macros
-            */
-
-            i = win_l_x0;
-            if (i < win_l_x1) {
-                OPJ_INT32 i_max;
-
-                /* Left-most case */
-                OPJ_S(i) -= (OPJ_D_(i - 1) + OPJ_D_(i) + 2) >> 2;
-                i ++;
-
-                i_max = win_l_x1;
-                if (i_max > dn) {
-                    i_max = dn;
-                }
-                for (; i < i_max; i++) {
-                    /* No bound checking */
-                    OPJ_S(i) -= (OPJ_D(i - 1) + OPJ_D(i) + 2) >> 2;
-                }
-                for (; i < win_l_x1; i++) {
-                    /* Right-most case */
-                    OPJ_S(i) -= (OPJ_D_(i - 1) + OPJ_D_(i) + 2) >> 2;
-                }
-            }
-
-            i = win_h_x0;
-            if (i < win_h_x1) {
-                OPJ_INT32 i_max = win_h_x1;
-                if (i_max >= sn) {
-                    i_max = sn - 1;
-                }
-                for (; i < i_max; i++) {
-                    /* No bound checking */
-                    OPJ_D(i) += (OPJ_S(i) + OPJ_S(i + 1)) >> 1;
-                }
-                for (; i < win_h_x1; i++) {
-                    /* Right-most case */
-                    OPJ_D(i) += (OPJ_S_(i) + OPJ_S_(i + 1)) >> 1;
-                }
-            }
-        }
-    } else {
-        if (!sn  && dn == 1) {        /* NEW :  CASE ONE ELEMENT */
-            OPJ_S(0) /= 2;
-        } else {
-            for (i = win_l_x0; i < win_l_x1; i++) {
-                OPJ_D(i) -= (OPJ_SS_(i) + OPJ_SS_(i + 1) + 2) >> 2;
-            }
-            for (i = win_h_x0; i < win_h_x1; i++) {
-                OPJ_S(i) += (OPJ_DD_(i) + OPJ_DD_(i - 1)) >> 1;
-            }
-        }
-    }
-}
-
-#define OPJ_S_off(i,off) a[(OPJ_UINT32)(i)*2*4+off]
-#define OPJ_D_off(i,off) a[(1+(OPJ_UINT32)(i)*2)*4+off]
-#define OPJ_S__off(i,off) ((i)<0?OPJ_S_off(0,off):((i)>=sn?OPJ_S_off(sn-1,off):OPJ_S_off(i,off)))
-#define OPJ_D__off(i,off) ((i)<0?OPJ_D_off(0,off):((i)>=dn?OPJ_D_off(dn-1,off):OPJ_D_off(i,off)))
-#define OPJ_SS__off(i,off) ((i)<0?OPJ_S_off(0,off):((i)>=dn?OPJ_S_off(dn-1,off):OPJ_S_off(i,off)))
-#define OPJ_DD__off(i,off) ((i)<0?OPJ_D_off(0,off):((i)>=sn?OPJ_D_off(sn-1,off):OPJ_D_off(i,off)))
-
-static void opj_dwt_decode_partial_1_parallel(OPJ_INT32 *a,
-        OPJ_UINT32 nb_cols,
-        OPJ_INT32 dn, OPJ_INT32 sn,
-        OPJ_INT32 cas,
-        OPJ_INT32 win_l_x0,
-        OPJ_INT32 win_l_x1,
-        OPJ_INT32 win_h_x0,
-        OPJ_INT32 win_h_x1)
-{
-    OPJ_INT32 i;
-    OPJ_UINT32 off;
-
-    (void)nb_cols;
-
-    if (!cas) {
-        if ((dn > 0) || (sn > 1)) { /* NEW :  CASE ONE ELEMENT */
-
-            /* Naive version is :
-            for (i = win_l_x0; i < i_max; i++) {
-                OPJ_S(i) -= (OPJ_D_(i - 1) + OPJ_D_(i) + 2) >> 2;
-            }
-            for (i = win_h_x0; i < win_h_x1; i++) {
-                OPJ_D(i) += (OPJ_S_(i) + OPJ_S_(i + 1)) >> 1;
-            }
-            but the compiler doesn't manage to unroll it to avoid bound
-            checking in OPJ_S_ and OPJ_D_ macros
-            */
-
-            i = win_l_x0;
-            if (i < win_l_x1) {
-                OPJ_INT32 i_max;
-
-                /* Left-most case */
-                for (off = 0; off < 4; off++) {
-                    OPJ_S_off(i, off) -= (OPJ_D__off(i - 1, off) + OPJ_D__off(i, off) + 2) >> 2;
-                }
-                i ++;
-
-                i_max = win_l_x1;
-                if (i_max > dn) {
-                    i_max = dn;
-                }
-
-#ifdef __SSE2__
-                if (i + 1 < i_max) {
-                    const __m128i two = _mm_set1_epi32(2);
-                    __m128i Dm1 = _mm_load_si128((__m128i * const)(a + 4 + (i - 1) * 8));
-                    for (; i + 1 < i_max; i += 2) {
-                        /* No bound checking */
-                        __m128i S = _mm_load_si128((__m128i * const)(a + i * 8));
-                        __m128i D = _mm_load_si128((__m128i * const)(a + 4 + i * 8));
-                        __m128i S1 = _mm_load_si128((__m128i * const)(a + (i + 1) * 8));
-                        __m128i D1 = _mm_load_si128((__m128i * const)(a + 4 + (i + 1) * 8));
-                        S = _mm_sub_epi32(S,
-                                          _mm_srai_epi32(_mm_add_epi32(_mm_add_epi32(Dm1, D), two), 2));
-                        S1 = _mm_sub_epi32(S1,
-                                           _mm_srai_epi32(_mm_add_epi32(_mm_add_epi32(D, D1), two), 2));
-                        _mm_store_si128((__m128i*)(a + i * 8), S);
-                        _mm_store_si128((__m128i*)(a + (i + 1) * 8), S1);
-                        Dm1 = D1;
-                    }
-                }
-#endif
-
-                for (; i < i_max; i++) {
-                    /* No bound checking */
-                    for (off = 0; off < 4; off++) {
-                        OPJ_S_off(i, off) -= (OPJ_D_off(i - 1, off) + OPJ_D_off(i, off) + 2) >> 2;
-                    }
-                }
-                for (; i < win_l_x1; i++) {
-                    /* Right-most case */
-                    for (off = 0; off < 4; off++) {
-                        OPJ_S_off(i, off) -= (OPJ_D__off(i - 1, off) + OPJ_D__off(i, off) + 2) >> 2;
-                    }
-                }
-            }
-
-            i = win_h_x0;
-            if (i < win_h_x1) {
-                OPJ_INT32 i_max = win_h_x1;
-                if (i_max >= sn) {
-                    i_max = sn - 1;
-                }
-
-#ifdef __SSE2__
-                if (i + 1 < i_max) {
-                    __m128i S =  _mm_load_si128((__m128i * const)(a + i * 8));
-                    for (; i + 1 < i_max; i += 2) {
-                        /* No bound checking */
-                        __m128i D = _mm_load_si128((__m128i * const)(a + 4 + i * 8));
-                        __m128i S1 = _mm_load_si128((__m128i * const)(a + (i + 1) * 8));
-                        __m128i D1 = _mm_load_si128((__m128i * const)(a + 4 + (i + 1) * 8));
-                        __m128i S2 = _mm_load_si128((__m128i * const)(a + (i + 2) * 8));
-                        D = _mm_add_epi32(D, _mm_srai_epi32(_mm_add_epi32(S, S1), 1));
-                        D1 = _mm_add_epi32(D1, _mm_srai_epi32(_mm_add_epi32(S1, S2), 1));
-                        _mm_store_si128((__m128i*)(a + 4 + i * 8), D);
-                        _mm_store_si128((__m128i*)(a + 4 + (i + 1) * 8), D1);
-                        S = S2;
-                    }
-                }
-#endif
-
-                for (; i < i_max; i++) {
-                    /* No bound checking */
-                    for (off = 0; off < 4; off++) {
-                        OPJ_D_off(i, off) += (OPJ_S_off(i, off) + OPJ_S_off(i + 1, off)) >> 1;
-                    }
-                }
-                for (; i < win_h_x1; i++) {
-                    /* Right-most case */
-                    for (off = 0; off < 4; off++) {
-                        OPJ_D_off(i, off) += (OPJ_S__off(i, off) + OPJ_S__off(i + 1, off)) >> 1;
-                    }
-                }
-            }
-        }
-    } else {
-        if (!sn  && dn == 1) {        /* NEW :  CASE ONE ELEMENT */
-            for (off = 0; off < 4; off++) {
-                OPJ_S_off(0, off) /= 2;
-            }
-        } else {
-            for (i = win_l_x0; i < win_l_x1; i++) {
-                for (off = 0; off < 4; off++) {
-                    OPJ_D_off(i, off) -= (OPJ_SS__off(i, off) + OPJ_SS__off(i + 1, off) + 2) >> 2;
-                }
-            }
-            for (i = win_h_x0; i < win_h_x1; i++) {
-                for (off = 0; off < 4; off++) {
-                    OPJ_S_off(i, off) += (OPJ_DD__off(i, off) + OPJ_DD__off(i - 1, off)) >> 1;
-                }
-            }
-        }
-    }
-}
-
-static void opj_dwt_get_band_coordinates(opj_tcd_tilecomp_t* tilec,
-        OPJ_UINT32 resno,
-        OPJ_UINT32 bandno,
-        OPJ_UINT32 tcx0,
-        OPJ_UINT32 tcy0,
-        OPJ_UINT32 tcx1,
-        OPJ_UINT32 tcy1,
-        OPJ_UINT32* tbx0,
-        OPJ_UINT32* tby0,
-        OPJ_UINT32* tbx1,
-        OPJ_UINT32* tby1)
-{
-    /* Compute number of decomposition for this band. See table F-1 */
-    OPJ_UINT32 nb = (resno == 0) ?
-                    tilec->numresolutions - 1 :
-                    tilec->numresolutions - resno;
-    /* Map above tile-based coordinates to sub-band-based coordinates per */
-    /* equation B-15 of the standard */
-    OPJ_UINT32 x0b = bandno & 1;
-    OPJ_UINT32 y0b = bandno >> 1;
-    if (tbx0) {
-        *tbx0 = (nb == 0) ? tcx0 :
-                (tcx0 <= (1U << (nb - 1)) * x0b) ? 0 :
-                opj_uint_ceildivpow2(tcx0 - (1U << (nb - 1)) * x0b, nb);
-    }
-    if (tby0) {
-        *tby0 = (nb == 0) ? tcy0 :
-                (tcy0 <= (1U << (nb - 1)) * y0b) ? 0 :
-                opj_uint_ceildivpow2(tcy0 - (1U << (nb - 1)) * y0b, nb);
-    }
-    if (tbx1) {
-        *tbx1 = (nb == 0) ? tcx1 :
-                (tcx1 <= (1U << (nb - 1)) * x0b) ? 0 :
-                opj_uint_ceildivpow2(tcx1 - (1U << (nb - 1)) * x0b, nb);
-    }
-    if (tby1) {
-        *tby1 = (nb == 0) ? tcy1 :
-                (tcy1 <= (1U << (nb - 1)) * y0b) ? 0 :
-                opj_uint_ceildivpow2(tcy1 - (1U << (nb - 1)) * y0b, nb);
-    }
-}
-
-static void opj_dwt_segment_grow(OPJ_UINT32 filter_width,
-                                 OPJ_UINT32 max_size,
-                                 OPJ_UINT32* start,
-                                 OPJ_UINT32* end)
-{
-    *start = opj_uint_subs(*start, filter_width);
-    *end = opj_uint_adds(*end, filter_width);
-    *end = opj_uint_min(*end, max_size);
-}
-
-
-static opj_sparse_array_int32_t* opj_dwt_init_sparse_array(
-    opj_tcd_tilecomp_t* tilec,
-    OPJ_UINT32 numres)
-{
-    opj_tcd_resolution_t* tr_max = &(tilec->resolutions[numres - 1]);
-    OPJ_UINT32 w = (OPJ_UINT32)(tr_max->x1 - tr_max->x0);
-    OPJ_UINT32 h = (OPJ_UINT32)(tr_max->y1 - tr_max->y0);
-    OPJ_UINT32 resno, bandno, precno, cblkno;
-    opj_sparse_array_int32_t* sa = opj_sparse_array_int32_create(
-                                       w, h, opj_uint_min(w, 64), opj_uint_min(h, 64));
-    if (sa == NULL) {
-        return NULL;
-    }
-
-    for (resno = 0; resno < numres; ++resno) {
-        opj_tcd_resolution_t* res = &tilec->resolutions[resno];
-
-        for (bandno = 0; bandno < res->numbands; ++bandno) {
-            opj_tcd_band_t* band = &res->bands[bandno];
-
-            for (precno = 0; precno < res->pw * res->ph; ++precno) {
-                opj_tcd_precinct_t* precinct = &band->precincts[precno];
-                for (cblkno = 0; cblkno < precinct->cw * precinct->ch; ++cblkno) {
-                    opj_tcd_cblk_dec_t* cblk = &precinct->cblks.dec[cblkno];
-                    if (cblk->decoded_data != NULL) {
-                        OPJ_UINT32 x = (OPJ_UINT32)(cblk->x0 - band->x0);
-                        OPJ_UINT32 y = (OPJ_UINT32)(cblk->y0 - band->y0);
-                        OPJ_UINT32 cblk_w = (OPJ_UINT32)(cblk->x1 - cblk->x0);
-                        OPJ_UINT32 cblk_h = (OPJ_UINT32)(cblk->y1 - cblk->y0);
-
-                        if (band->bandno & 1) {
-                            opj_tcd_resolution_t* pres = &tilec->resolutions[resno - 1];
-                            x += (OPJ_UINT32)(pres->x1 - pres->x0);
-                        }
-                        if (band->bandno & 2) {
-                            opj_tcd_resolution_t* pres = &tilec->resolutions[resno - 1];
-                            y += (OPJ_UINT32)(pres->y1 - pres->y0);
-                        }
-
-                        if (!opj_sparse_array_int32_write(sa, x, y,
-                                                          x + cblk_w, y + cblk_h,
-                                                          cblk->decoded_data,
-                                                          1, cblk_w, OPJ_TRUE)) {
-                            opj_sparse_array_int32_free(sa);
-                            return NULL;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    return sa;
-}
-
-
-static OPJ_BOOL opj_dwt_decode_partial_tile(
-    opj_tcd_tilecomp_t* tilec,
-    OPJ_UINT32 numres)
-{
-    opj_sparse_array_int32_t* sa;
-    opj_dwt_t h;
-    opj_dwt_t v;
-    OPJ_UINT32 resno;
-    /* This value matches the maximum left/right extension given in tables */
-    /* F.2 and F.3 of the standard. */
-    const OPJ_UINT32 filter_width = 2U;
-
-    opj_tcd_resolution_t* tr = tilec->resolutions;
-    opj_tcd_resolution_t* tr_max = &(tilec->resolutions[numres - 1]);
-
-    OPJ_UINT32 rw = (OPJ_UINT32)(tr->x1 -
-                                 tr->x0);  /* width of the resolution level computed */
-    OPJ_UINT32 rh = (OPJ_UINT32)(tr->y1 -
-                                 tr->y0);  /* height of the resolution level computed */
-
-    OPJ_SIZE_T h_mem_size;
-
-    /* Compute the intersection of the area of interest, expressed in tile coordinates */
-    /* with the tile coordinates */
-    OPJ_UINT32 win_tcx0 = tilec->win_x0;
-    OPJ_UINT32 win_tcy0 = tilec->win_y0;
-    OPJ_UINT32 win_tcx1 = tilec->win_x1;
-    OPJ_UINT32 win_tcy1 = tilec->win_y1;
-
-    if (tr_max->x0 == tr_max->x1 || tr_max->y0 == tr_max->y1) {
-        return OPJ_TRUE;
-    }
-
-    sa = opj_dwt_init_sparse_array(tilec, numres);
-    if (sa == NULL) {
-        return OPJ_FALSE;
-    }
-
-    if (numres == 1U) {
-        OPJ_BOOL ret = opj_sparse_array_int32_read(sa,
-                       tr_max->win_x0 - (OPJ_UINT32)tr_max->x0,
-                       tr_max->win_y0 - (OPJ_UINT32)tr_max->y0,
-                       tr_max->win_x1 - (OPJ_UINT32)tr_max->x0,
-                       tr_max->win_y1 - (OPJ_UINT32)tr_max->y0,
-                       tilec->data_win,
-                       1, tr_max->win_x1 - tr_max->win_x0,
-                       OPJ_TRUE);
-        assert(ret);
-        OPJ_UNUSED(ret);
-        opj_sparse_array_int32_free(sa);
-        return OPJ_TRUE;
-    }
-    h.mem_count = opj_dwt_max_resolution(tr, numres);
-    /* overflow check */
-    /* in vertical pass, we process 4 columns at a time */
-    if (h.mem_count > (SIZE_MAX / (4 * sizeof(OPJ_INT32)))) {
-        /* FIXME event manager error callback */
-        opj_sparse_array_int32_free(sa);
-        return OPJ_FALSE;
-    }
-
-    h_mem_size = h.mem_count * 4 * sizeof(OPJ_INT32);
-    h.mem = (OPJ_INT32*)opj_aligned_32_malloc(h_mem_size);
-    if (! h.mem) {
-        /* FIXME event manager error callback */
-        opj_sparse_array_int32_free(sa);
-        return OPJ_FALSE;
-    }
-
-    v.mem_count = h.mem_count;
-    v.mem = h.mem;
-
-    for (resno = 1; resno < numres; resno ++) {
-        OPJ_UINT32 i, j;
-        /* Window of interest subband-based coordinates */
-        OPJ_UINT32 win_ll_x0, win_ll_y0, win_ll_x1, win_ll_y1;
-        OPJ_UINT32 win_hl_x0, win_hl_x1;
-        OPJ_UINT32 win_lh_y0, win_lh_y1;
-        /* Window of interest tile-resolution-based coordinates */
-        OPJ_UINT32 win_tr_x0, win_tr_x1, win_tr_y0, win_tr_y1;
-        /* Tile-resolution subband-based coordinates */
-        OPJ_UINT32 tr_ll_x0, tr_ll_y0, tr_hl_x0, tr_lh_y0;
-
-        ++tr;
-
-        h.sn = (OPJ_INT32)rw;
-        v.sn = (OPJ_INT32)rh;
-
-        rw = (OPJ_UINT32)(tr->x1 - tr->x0);
-        rh = (OPJ_UINT32)(tr->y1 - tr->y0);
-
-        h.dn = (OPJ_INT32)(rw - (OPJ_UINT32)h.sn);
-        h.cas = tr->x0 % 2;
-
-        v.dn = (OPJ_INT32)(rh - (OPJ_UINT32)v.sn);
-        v.cas = tr->y0 % 2;
-
-        /* Get the subband coordinates for the window of interest */
-        /* LL band */
-        opj_dwt_get_band_coordinates(tilec, resno, 0,
-                                     win_tcx0, win_tcy0, win_tcx1, win_tcy1,
-                                     &win_ll_x0, &win_ll_y0,
-                                     &win_ll_x1, &win_ll_y1);
-
-        /* HL band */
-        opj_dwt_get_band_coordinates(tilec, resno, 1,
-                                     win_tcx0, win_tcy0, win_tcx1, win_tcy1,
-                                     &win_hl_x0, NULL, &win_hl_x1, NULL);
-
-        /* LH band */
-        opj_dwt_get_band_coordinates(tilec, resno, 2,
-                                     win_tcx0, win_tcy0, win_tcx1, win_tcy1,
-                                     NULL, &win_lh_y0, NULL, &win_lh_y1);
-
-        /* Beware: band index for non-LL0 resolution are 0=HL, 1=LH and 2=HH */
-        tr_ll_x0 = (OPJ_UINT32)tr->bands[1].x0;
-        tr_ll_y0 = (OPJ_UINT32)tr->bands[0].y0;
-        tr_hl_x0 = (OPJ_UINT32)tr->bands[0].x0;
-        tr_lh_y0 = (OPJ_UINT32)tr->bands[1].y0;
-
-        /* Subtract the origin of the bands for this tile, to the subwindow */
-        /* of interest band coordinates, so as to get them relative to the */
-        /* tile */
-        win_ll_x0 = opj_uint_subs(win_ll_x0, tr_ll_x0);
-        win_ll_y0 = opj_uint_subs(win_ll_y0, tr_ll_y0);
-        win_ll_x1 = opj_uint_subs(win_ll_x1, tr_ll_x0);
-        win_ll_y1 = opj_uint_subs(win_ll_y1, tr_ll_y0);
-        win_hl_x0 = opj_uint_subs(win_hl_x0, tr_hl_x0);
-        win_hl_x1 = opj_uint_subs(win_hl_x1, tr_hl_x0);
-        win_lh_y0 = opj_uint_subs(win_lh_y0, tr_lh_y0);
-        win_lh_y1 = opj_uint_subs(win_lh_y1, tr_lh_y0);
-
-        opj_dwt_segment_grow(filter_width, (OPJ_UINT32)h.sn, &win_ll_x0, &win_ll_x1);
-        opj_dwt_segment_grow(filter_width, (OPJ_UINT32)h.dn, &win_hl_x0, &win_hl_x1);
-
-        opj_dwt_segment_grow(filter_width, (OPJ_UINT32)v.sn, &win_ll_y0, &win_ll_y1);
-        opj_dwt_segment_grow(filter_width, (OPJ_UINT32)v.dn, &win_lh_y0, &win_lh_y1);
-
-        /* Compute the tile-resolution-based coordinates for the window of interest */
-        if (h.cas == 0) {
-            win_tr_x0 = opj_uint_min(2 * win_ll_x0, 2 * win_hl_x0 + 1);
-            win_tr_x1 = opj_uint_min(opj_uint_max(2 * win_ll_x1, 2 * win_hl_x1 + 1), rw);
-        } else {
-            win_tr_x0 = opj_uint_min(2 * win_hl_x0, 2 * win_ll_x0 + 1);
-            win_tr_x1 = opj_uint_min(opj_uint_max(2 * win_hl_x1, 2 * win_ll_x1 + 1), rw);
-        }
-
-        if (v.cas == 0) {
-            win_tr_y0 = opj_uint_min(2 * win_ll_y0, 2 * win_lh_y0 + 1);
-            win_tr_y1 = opj_uint_min(opj_uint_max(2 * win_ll_y1, 2 * win_lh_y1 + 1), rh);
-        } else {
-            win_tr_y0 = opj_uint_min(2 * win_lh_y0, 2 * win_ll_y0 + 1);
-            win_tr_y1 = opj_uint_min(opj_uint_max(2 * win_lh_y1, 2 * win_ll_y1 + 1), rh);
-        }
-
-        for (j = 0; j < rh; ++j) {
-            if ((j >= win_ll_y0 && j < win_ll_y1) ||
-                    (j >= win_lh_y0 + (OPJ_UINT32)v.sn && j < win_lh_y1 + (OPJ_UINT32)v.sn)) {
-
-                /* Avoids dwt.c:1584:44 (in opj_dwt_decode_partial_1): runtime error: */
-                /* signed integer overflow: -1094795586 + -1094795586 cannot be represented in type 'int' */
-                /* on opj_decompress -i  ../../openjpeg/MAPA.jp2 -o out.tif -d 0,0,256,256 */
-                /* This is less extreme than memsetting the whole buffer to 0 */
-                /* although we could potentially do better with better handling of edge conditions */
-                if (win_tr_x1 >= 1 && win_tr_x1 < rw) {
-                    h.mem[win_tr_x1 - 1] = 0;
-                }
-                if (win_tr_x1 < rw) {
-                    h.mem[win_tr_x1] = 0;
-                }
-
-                opj_dwt_interleave_partial_h(h.mem,
-                                             h.cas,
-                                             sa,
-                                             j,
-                                             (OPJ_UINT32)h.sn,
-                                             win_ll_x0,
-                                             win_ll_x1,
-                                             win_hl_x0,
-                                             win_hl_x1);
-                opj_dwt_decode_partial_1(h.mem, h.mem_count, h.dn, h.sn, h.cas,
-                                         (OPJ_INT32)win_ll_x0,
-                                         (OPJ_INT32)win_ll_x1,
-                                         (OPJ_INT32)win_hl_x0,
-                                         (OPJ_INT32)win_hl_x1);
-                if (!opj_sparse_array_int32_write(sa,
-                                                  win_tr_x0, j,
-                                                  win_tr_x1, j + 1,
-                                                  h.mem + win_tr_x0,
-                                                  1, 0, OPJ_TRUE)) {
-                    /* FIXME event manager error callback */
-                    opj_sparse_array_int32_free(sa);
-                    opj_aligned_free(h.mem);
-                    return OPJ_FALSE;
-                }
-            }
-        }
-
-        for (i = win_tr_x0; i < win_tr_x1;) {
-            OPJ_UINT32 nb_cols = opj_uint_min(4U, win_tr_x1 - i);
-            opj_dwt_interleave_partial_v(v.mem,
-                                         v.cas,
-                                         sa,
-                                         i,
-                                         nb_cols,
-                                         (OPJ_UINT32)v.sn,
-                                         win_ll_y0,
-                                         win_ll_y1,
-                                         win_lh_y0,
-                                         win_lh_y1);
-            opj_dwt_decode_partial_1_parallel(v.mem, nb_cols, v.dn, v.sn, v.cas,
-                                              (OPJ_INT32)win_ll_y0,
-                                              (OPJ_INT32)win_ll_y1,
-                                              (OPJ_INT32)win_lh_y0,
-                                              (OPJ_INT32)win_lh_y1);
-            if (!opj_sparse_array_int32_write(sa,
-                                              i, win_tr_y0,
-                                              i + nb_cols, win_tr_y1,
-                                              v.mem + 4 * win_tr_y0,
-                                              1, 4, OPJ_TRUE)) {
-                /* FIXME event manager error callback */
-                opj_sparse_array_int32_free(sa);
-                opj_aligned_free(h.mem);
-                return OPJ_FALSE;
-            }
-
-            i += nb_cols;
-        }
-    }
-    opj_aligned_free(h.mem);
-
-    {
-        OPJ_BOOL ret = opj_sparse_array_int32_read(sa,
-                       tr_max->win_x0 - (OPJ_UINT32)tr_max->x0,
-                       tr_max->win_y0 - (OPJ_UINT32)tr_max->y0,
-                       tr_max->win_x1 - (OPJ_UINT32)tr_max->x0,
-                       tr_max->win_y1 - (OPJ_UINT32)tr_max->y0,
-                       tilec->data_win,
-                       1, tr_max->win_x1 - tr_max->win_x0,
-                       OPJ_TRUE);
-        assert(ret);
-        OPJ_UNUSED(ret);
-    }
-    opj_sparse_array_int32_free(sa);
-    return OPJ_TRUE;
-}
-
-static void opj_v4dwt_interleave_h(opj_v4dwt_t* OPJ_RESTRICT dwt,
-                                   OPJ_FLOAT32* OPJ_RESTRICT a,
-                                   OPJ_UINT32 width,
-                                   OPJ_UINT32 remaining_height)
-{
-    OPJ_FLOAT32* OPJ_RESTRICT bi = (OPJ_FLOAT32*)(dwt->wavelet + dwt->cas);
-    OPJ_UINT32 i, k;
-    OPJ_UINT32 x0 = dwt->win_l_x0;
-    OPJ_UINT32 x1 = dwt->win_l_x1;
-
-    for (k = 0; k < 2; ++k) {
-        if (remaining_height >= 4 && ((OPJ_SIZE_T) a & 0x0f) == 0 &&
-                ((OPJ_SIZE_T) bi & 0x0f) == 0 && (width & 0x0f) == 0) {
-            /* Fast code path */
-            for (i = x0; i < x1; ++i) {
-                OPJ_UINT32 j = i;
-                bi[i * 8    ] = a[j];
-                j += width;
-                bi[i * 8 + 1] = a[j];
-                j += width;
-                bi[i * 8 + 2] = a[j];
-                j += width;
-                bi[i * 8 + 3] = a[j];
-            }
-        } else {
-            /* Slow code path */
-            for (i = x0; i < x1; ++i) {
-                OPJ_UINT32 j = i;
-                bi[i * 8    ] = a[j];
-                j += width;
-                if (remaining_height == 1) {
-                    continue;
-                }
-                bi[i * 8 + 1] = a[j];
-                j += width;
-                if (remaining_height == 2) {
-                    continue;
-                }
-                bi[i * 8 + 2] = a[j];
-                j += width;
-                if (remaining_height == 3) {
-                    continue;
-                }
-                bi[i * 8 + 3] = a[j]; /* This one*/
-            }
-        }
-
-        bi = (OPJ_FLOAT32*)(dwt->wavelet + 1 - dwt->cas);
-        a += dwt->sn;
-        x0 = dwt->win_h_x0;
-        x1 = dwt->win_h_x1;
-    }
-}
-
-static void opj_v4dwt_interleave_partial_h(opj_v4dwt_t* dwt,
-        opj_sparse_array_int32_t* sa,
-        OPJ_UINT32 sa_line,
-        OPJ_UINT32 remaining_height)
-{
-    OPJ_UINT32 i;
-    for (i = 0; i < remaining_height; i++) {
-        OPJ_BOOL ret;
-        ret = opj_sparse_array_int32_read(sa,
-                                          dwt->win_l_x0, sa_line + i,
-                                          dwt->win_l_x1, sa_line + i + 1,
-                                          /* Nasty cast from float* to int32* */
-                                          (OPJ_INT32*)(dwt->wavelet + dwt->cas + 2 * dwt->win_l_x0) + i,
-                                          8, 0, OPJ_TRUE);
-        assert(ret);
-        ret = opj_sparse_array_int32_read(sa,
-                                          (OPJ_UINT32)dwt->sn + dwt->win_h_x0, sa_line + i,
-                                          (OPJ_UINT32)dwt->sn + dwt->win_h_x1, sa_line + i + 1,
-                                          /* Nasty cast from float* to int32* */
-                                          (OPJ_INT32*)(dwt->wavelet + 1 - dwt->cas + 2 * dwt->win_h_x0) + i,
-                                          8, 0, OPJ_TRUE);
-        assert(ret);
-        OPJ_UNUSED(ret);
-    }
-}
-
-static void opj_v4dwt_interleave_v(opj_v4dwt_t* OPJ_RESTRICT dwt,
-                                   OPJ_FLOAT32* OPJ_RESTRICT a,
-                                   OPJ_UINT32 width,
-                                   OPJ_UINT32 nb_elts_read)
-{
-    opj_v4_t* OPJ_RESTRICT bi = dwt->wavelet + dwt->cas;
-    OPJ_UINT32 i;
-
-    for (i = dwt->win_l_x0; i < dwt->win_l_x1; ++i) {
-        memcpy(&bi[i * 2], &a[i * (OPJ_SIZE_T)width],
-               (OPJ_SIZE_T)nb_elts_read * sizeof(OPJ_FLOAT32));
-    }
-
-    a += (OPJ_UINT32)dwt->sn * (OPJ_SIZE_T)width;
-    bi = dwt->wavelet + 1 - dwt->cas;
-
-    for (i = dwt->win_h_x0; i < dwt->win_h_x1; ++i) {
-        memcpy(&bi[i * 2], &a[i * (OPJ_SIZE_T)width],
-               (OPJ_SIZE_T)nb_elts_read * sizeof(OPJ_FLOAT32));
-    }
-}
-
-static void opj_v4dwt_interleave_partial_v(opj_v4dwt_t* OPJ_RESTRICT dwt,
-        opj_sparse_array_int32_t* sa,
-        OPJ_UINT32 sa_col,
-        OPJ_UINT32 nb_elts_read)
-{
-    OPJ_BOOL ret;
-    ret = opj_sparse_array_int32_read(sa,
-                                      sa_col, dwt->win_l_x0,
-                                      sa_col + nb_elts_read, dwt->win_l_x1,
-                                      (OPJ_INT32*)(dwt->wavelet + dwt->cas + 2 * dwt->win_l_x0),
-                                      1, 8, OPJ_TRUE);
-    assert(ret);
-    ret = opj_sparse_array_int32_read(sa,
-                                      sa_col, (OPJ_UINT32)dwt->sn + dwt->win_h_x0,
-                                      sa_col + nb_elts_read, (OPJ_UINT32)dwt->sn + dwt->win_h_x1,
-                                      (OPJ_INT32*)(dwt->wavelet + 1 - dwt->cas + 2 * dwt->win_h_x0),
-                                      1, 8, OPJ_TRUE);
-    assert(ret);
-    OPJ_UNUSED(ret);
-}
-
-#ifdef __SSE__
-
-static void opj_v4dwt_decode_step1_sse(opj_v4_t* w,
-                                       OPJ_UINT32 start,
-                                       OPJ_UINT32 end,
-                                       const __m128 c)
-{
-    __m128* OPJ_RESTRICT vw = (__m128*) w;
-    OPJ_UINT32 i;
-    /* 4x unrolled loop */
-    vw += 2 * start;
-    for (i = start; i + 3 < end; i += 4, vw += 8) {
-        __m128 xmm0 = _mm_mul_ps(vw[0], c);
-        __m128 xmm2 = _mm_mul_ps(vw[2], c);
-        __m128 xmm4 = _mm_mul_ps(vw[4], c);
-        __m128 xmm6 = _mm_mul_ps(vw[6], c);
-        vw[0] = xmm0;
-        vw[2] = xmm2;
-        vw[4] = xmm4;
-        vw[6] = xmm6;
-    }
-    for (; i < end; ++i, vw += 2) {
-        vw[0] = _mm_mul_ps(vw[0], c);
-    }
-}
-
-static void opj_v4dwt_decode_step2_sse(opj_v4_t* l, opj_v4_t* w,
-                                       OPJ_UINT32 start,
-                                       OPJ_UINT32 end,
-                                       OPJ_UINT32 m,
-                                       __m128 c)
-{
-    __m128* OPJ_RESTRICT vl = (__m128*) l;
-    __m128* OPJ_RESTRICT vw = (__m128*) w;
-    OPJ_UINT32 i;
-    OPJ_UINT32 imax = opj_uint_min(end, m);
-    __m128 tmp1, tmp2, tmp3;
-    if (start == 0) {
-        tmp1 = vl[0];
-    } else {
-        vw += start * 2;
-        tmp1 = vw[-3];
-    }
-
-    i = start;
-
-    /* 4x loop unrolling */
-    for (; i + 3 < imax; i += 4) {
-        __m128 tmp4, tmp5, tmp6, tmp7, tmp8, tmp9;
-        tmp2 = vw[-1];
-        tmp3 = vw[ 0];
-        tmp4 = vw[ 1];
-        tmp5 = vw[ 2];
-        tmp6 = vw[ 3];
-        tmp7 = vw[ 4];
-        tmp8 = vw[ 5];
-        tmp9 = vw[ 6];
-        vw[-1] = _mm_add_ps(tmp2, _mm_mul_ps(_mm_add_ps(tmp1, tmp3), c));
-        vw[ 1] = _mm_add_ps(tmp4, _mm_mul_ps(_mm_add_ps(tmp3, tmp5), c));
-        vw[ 3] = _mm_add_ps(tmp6, _mm_mul_ps(_mm_add_ps(tmp5, tmp7), c));
-        vw[ 5] = _mm_add_ps(tmp8, _mm_mul_ps(_mm_add_ps(tmp7, tmp9), c));
-        tmp1 = tmp9;
-        vw += 8;
-    }
-
-    for (; i < imax; ++i) {
-        tmp2 = vw[-1];
-        tmp3 = vw[ 0];
-        vw[-1] = _mm_add_ps(tmp2, _mm_mul_ps(_mm_add_ps(tmp1, tmp3), c));
-        tmp1 = tmp3;
-        vw += 2;
-    }
-    if (m < end) {
-        assert(m + 1 == end);
-        c = _mm_add_ps(c, c);
-        c = _mm_mul_ps(c, vw[-2]);
-        vw[-1] = _mm_add_ps(vw[-1], c);
-    }
-}
-
-#else
-
-static void opj_v4dwt_decode_step1(opj_v4_t* w,
-                                   OPJ_UINT32 start,
-                                   OPJ_UINT32 end,
-                                   const OPJ_FLOAT32 c)
-{
-    OPJ_FLOAT32* OPJ_RESTRICT fw = (OPJ_FLOAT32*) w;
-    OPJ_UINT32 i;
-    for (i = start; i < end; ++i) {
-        OPJ_FLOAT32 tmp1 = fw[i * 8    ];
-        OPJ_FLOAT32 tmp2 = fw[i * 8 + 1];
-        OPJ_FLOAT32 tmp3 = fw[i * 8 + 2];
-        OPJ_FLOAT32 tmp4 = fw[i * 8 + 3];
-        fw[i * 8    ] = tmp1 * c;
-        fw[i * 8 + 1] = tmp2 * c;
-        fw[i * 8 + 2] = tmp3 * c;
-        fw[i * 8 + 3] = tmp4 * c;
-    }
-}
-
-static void opj_v4dwt_decode_step2(opj_v4_t* l, opj_v4_t* w,
-                                   OPJ_UINT32 start,
-                                   OPJ_UINT32 end,
-                                   OPJ_UINT32 m,
-                                   OPJ_FLOAT32 c)
-{
-    OPJ_FLOAT32* fl = (OPJ_FLOAT32*) l;
-    OPJ_FLOAT32* fw = (OPJ_FLOAT32*) w;
-    OPJ_UINT32 i;
-    OPJ_UINT32 imax = opj_uint_min(end, m);
-    if (start > 0) {
-        fw += 8 * start;
-        fl = fw - 8;
-    }
-    for (i = start; i < imax; ++i) {
-        OPJ_FLOAT32 tmp1_1 = fl[0];
-        OPJ_FLOAT32 tmp1_2 = fl[1];
-        OPJ_FLOAT32 tmp1_3 = fl[2];
-        OPJ_FLOAT32 tmp1_4 = fl[3];
-        OPJ_FLOAT32 tmp2_1 = fw[-4];
-        OPJ_FLOAT32 tmp2_2 = fw[-3];
-        OPJ_FLOAT32 tmp2_3 = fw[-2];
-        OPJ_FLOAT32 tmp2_4 = fw[-1];
-        OPJ_FLOAT32 tmp3_1 = fw[0];
-        OPJ_FLOAT32 tmp3_2 = fw[1];
-        OPJ_FLOAT32 tmp3_3 = fw[2];
-        OPJ_FLOAT32 tmp3_4 = fw[3];
-        fw[-4] = tmp2_1 + ((tmp1_1 + tmp3_1) * c);
-        fw[-3] = tmp2_2 + ((tmp1_2 + tmp3_2) * c);
-        fw[-2] = tmp2_3 + ((tmp1_3 + tmp3_3) * c);
-        fw[-1] = tmp2_4 + ((tmp1_4 + tmp3_4) * c);
-        fl = fw;
-        fw += 8;
-    }
-    if (m < end) {
-        assert(m + 1 == end);
-        c += c;
-        fw[-4] = fw[-4] + fl[0] * c;
-        fw[-3] = fw[-3] + fl[1] * c;
-        fw[-2] = fw[-2] + fl[2] * c;
-        fw[-1] = fw[-1] + fl[3] * c;
-    }
-}
-
-#endif
-
-/* <summary>                             */
-/* Inverse 9-7 wavelet transform in 1-D. */
-/* </summary>                            */
-static void opj_v4dwt_decode(opj_v4dwt_t* OPJ_RESTRICT dwt)
-{
-    OPJ_INT32 a, b;
-    if (dwt->cas == 0) {
-        if (!((dwt->dn > 0) || (dwt->sn > 1))) {
-            return;
-        }
-        a = 0;
-        b = 1;
-    } else {
-        if (!((dwt->sn > 0) || (dwt->dn > 1))) {
-            return;
-        }
-        a = 1;
-        b = 0;
-    }
-#ifdef __SSE__
-    opj_v4dwt_decode_step1_sse(dwt->wavelet + a, dwt->win_l_x0, dwt->win_l_x1,
-                               _mm_set1_ps(opj_K));
-    opj_v4dwt_decode_step1_sse(dwt->wavelet + b, dwt->win_h_x0, dwt->win_h_x1,
-                               _mm_set1_ps(opj_c13318));
-    opj_v4dwt_decode_step2_sse(dwt->wavelet + b, dwt->wavelet + a + 1,
-                               dwt->win_l_x0, dwt->win_l_x1,
-                               (OPJ_UINT32)opj_int_min(dwt->sn, dwt->dn - a),
-                               _mm_set1_ps(opj_dwt_delta));
-    opj_v4dwt_decode_step2_sse(dwt->wavelet + a, dwt->wavelet + b + 1,
-                               dwt->win_h_x0, dwt->win_h_x1,
-                               (OPJ_UINT32)opj_int_min(dwt->dn, dwt->sn - b),
-                               _mm_set1_ps(opj_dwt_gamma));
-    opj_v4dwt_decode_step2_sse(dwt->wavelet + b, dwt->wavelet + a + 1,
-                               dwt->win_l_x0, dwt->win_l_x1,
-                               (OPJ_UINT32)opj_int_min(dwt->sn, dwt->dn - a),
-                               _mm_set1_ps(opj_dwt_beta));
-    opj_v4dwt_decode_step2_sse(dwt->wavelet + a, dwt->wavelet + b + 1,
-                               dwt->win_h_x0, dwt->win_h_x1,
-                               (OPJ_UINT32)opj_int_min(dwt->dn, dwt->sn - b),
-                               _mm_set1_ps(opj_dwt_alpha));
-#else
-    opj_v4dwt_decode_step1(dwt->wavelet + a, dwt->win_l_x0, dwt->win_l_x1,
-                           opj_K);
-    opj_v4dwt_decode_step1(dwt->wavelet + b, dwt->win_h_x0, dwt->win_h_x1,
-                           opj_c13318);
-    opj_v4dwt_decode_step2(dwt->wavelet + b, dwt->wavelet + a + 1,
-                           dwt->win_l_x0, dwt->win_l_x1,
-                           (OPJ_UINT32)opj_int_min(dwt->sn, dwt->dn - a),
-                           opj_dwt_delta);
-    opj_v4dwt_decode_step2(dwt->wavelet + a, dwt->wavelet + b + 1,
-                           dwt->win_h_x0, dwt->win_h_x1,
-                           (OPJ_UINT32)opj_int_min(dwt->dn, dwt->sn - b),
-                           opj_dwt_gamma);
-    opj_v4dwt_decode_step2(dwt->wavelet + b, dwt->wavelet + a + 1,
-                           dwt->win_l_x0, dwt->win_l_x1,
-                           (OPJ_UINT32)opj_int_min(dwt->sn, dwt->dn - a),
-                           opj_dwt_beta);
-    opj_v4dwt_decode_step2(dwt->wavelet + a, dwt->wavelet + b + 1,
-                           dwt->win_h_x0, dwt->win_h_x1,
-                           (OPJ_UINT32)opj_int_min(dwt->dn, dwt->sn - b),
-                           opj_dwt_alpha);
-#endif
-}
-
-
-/* <summary>                             */
-/* Inverse 9-7 wavelet transform in 2-D. */
-/* </summary>                            */
-static
-OPJ_BOOL opj_dwt_decode_tile_97(opj_tcd_tilecomp_t* OPJ_RESTRICT tilec,
-                                OPJ_UINT32 numres)
-{
-    opj_v4dwt_t h;
-    opj_v4dwt_t v;
-
-    opj_tcd_resolution_t* res = tilec->resolutions;
-
-    OPJ_UINT32 rw = (OPJ_UINT32)(res->x1 -
-                                 res->x0);    /* width of the resolution level computed */
-    OPJ_UINT32 rh = (OPJ_UINT32)(res->y1 -
-                                 res->y0);    /* height of the resolution level computed */
-
-    OPJ_UINT32 w = (OPJ_UINT32)(tilec->resolutions[tilec->minimum_num_resolutions -
-                                                               1].x1 -
-                                tilec->resolutions[tilec->minimum_num_resolutions - 1].x0);
-
-    OPJ_SIZE_T l_data_size;
-
-    l_data_size = opj_dwt_max_resolution(res, numres);
-    /* overflow check */
-    if (l_data_size > (SIZE_MAX - 5U)) {
-        /* FIXME event manager error callback */
-        return OPJ_FALSE;
-    }
-    l_data_size += 5U;
-    /* overflow check */
-    if (l_data_size > (SIZE_MAX / sizeof(opj_v4_t))) {
-        /* FIXME event manager error callback */
-        return OPJ_FALSE;
-    }
-    h.wavelet = (opj_v4_t*) opj_aligned_malloc(l_data_size * sizeof(opj_v4_t));
-    if (!h.wavelet) {
-        /* FIXME event manager error callback */
-        return OPJ_FALSE;
-    }
-    v.wavelet = h.wavelet;
-
-    while (--numres) {
-        OPJ_FLOAT32 * OPJ_RESTRICT aj = (OPJ_FLOAT32*) tilec->data;
-        OPJ_UINT32 j;
-
-        h.sn = (OPJ_INT32)rw;
-        v.sn = (OPJ_INT32)rh;
-
-        ++res;
-
-        rw = (OPJ_UINT32)(res->x1 -
-                          res->x0);   /* width of the resolution level computed */
-        rh = (OPJ_UINT32)(res->y1 -
-                          res->y0);   /* height of the resolution level computed */
-
-        h.dn = (OPJ_INT32)(rw - (OPJ_UINT32)h.sn);
-        h.cas = res->x0 % 2;
-
-        h.win_l_x0 = 0;
-        h.win_l_x1 = (OPJ_UINT32)h.sn;
-        h.win_h_x0 = 0;
-        h.win_h_x1 = (OPJ_UINT32)h.dn;
-        for (j = 0; j + 3 < rh; j += 4) {
-            OPJ_UINT32 k;
-            opj_v4dwt_interleave_h(&h, aj, w, rh - j);
-            opj_v4dwt_decode(&h);
-
-            for (k = 0; k < rw; k++) {
-                aj[k      ] = h.wavelet[k].f[0];
-                aj[k + (OPJ_SIZE_T)w  ] = h.wavelet[k].f[1];
-                aj[k + (OPJ_SIZE_T)w * 2] = h.wavelet[k].f[2];
-                aj[k + (OPJ_SIZE_T)w * 3] = h.wavelet[k].f[3];
-            }
-
-            aj += w * 4;
-        }
-
-        if (j < rh) {
-            OPJ_UINT32 k;
-            opj_v4dwt_interleave_h(&h, aj, w, rh - j);
-            opj_v4dwt_decode(&h);
-            for (k = 0; k < rw; k++) {
-                switch (rh - j) {
-                case 3:
-                    aj[k + (OPJ_SIZE_T)w * 2] = h.wavelet[k].f[2];
-                /* FALLTHRU */
-                case 2:
-                    aj[k + (OPJ_SIZE_T)w  ] = h.wavelet[k].f[1];
-                /* FALLTHRU */
-                case 1:
-                    aj[k] = h.wavelet[k].f[0];
-                }
-            }
-        }
-
-        v.dn = (OPJ_INT32)(rh - (OPJ_UINT32)v.sn);
-        v.cas = res->y0 % 2;
-        v.win_l_x0 = 0;
-        v.win_l_x1 = (OPJ_UINT32)v.sn;
-        v.win_h_x0 = 0;
-        v.win_h_x1 = (OPJ_UINT32)v.dn;
-
-        aj = (OPJ_FLOAT32*) tilec->data;
-        for (j = rw; j > 3; j -= 4) {
-            OPJ_UINT32 k;
-
-            opj_v4dwt_interleave_v(&v, aj, w, 4);
-            opj_v4dwt_decode(&v);
-
-            for (k = 0; k < rh; ++k) {
-                memcpy(&aj[k * (OPJ_SIZE_T)w], &v.wavelet[k], 4 * sizeof(OPJ_FLOAT32));
-            }
-            aj += 4;
-        }
-
-        if (rw & 0x03) {
-            OPJ_UINT32 k;
-
-            j = rw & 0x03;
-
-            opj_v4dwt_interleave_v(&v, aj, w, j);
-            opj_v4dwt_decode(&v);
-
-            for (k = 0; k < rh; ++k) {
-                memcpy(&aj[k * (OPJ_SIZE_T)w], &v.wavelet[k],
-                       (OPJ_SIZE_T)j * sizeof(OPJ_FLOAT32));
-            }
-        }
-    }
-
-    opj_aligned_free(h.wavelet);
-    return OPJ_TRUE;
-}
-
-static
-OPJ_BOOL opj_dwt_decode_partial_97(opj_tcd_tilecomp_t* OPJ_RESTRICT tilec,
-                                   OPJ_UINT32 numres)
-{
-    opj_sparse_array_int32_t* sa;
-    opj_v4dwt_t h;
-    opj_v4dwt_t v;
-    OPJ_UINT32 resno;
-    /* This value matches the maximum left/right extension given in tables */
-    /* F.2 and F.3 of the standard. Note: in opj_tcd_is_subband_area_of_interest() */
-    /* we currently use 3. */
-    const OPJ_UINT32 filter_width = 4U;
-
-    opj_tcd_resolution_t* tr = tilec->resolutions;
-    opj_tcd_resolution_t* tr_max = &(tilec->resolutions[numres - 1]);
-
-    OPJ_UINT32 rw = (OPJ_UINT32)(tr->x1 -
-                                 tr->x0);    /* width of the resolution level computed */
-    OPJ_UINT32 rh = (OPJ_UINT32)(tr->y1 -
-                                 tr->y0);    /* height of the resolution level computed */
-
-    OPJ_SIZE_T l_data_size;
-
-    /* Compute the intersection of the area of interest, expressed in tile coordinates */
-    /* with the tile coordinates */
-    OPJ_UINT32 win_tcx0 = tilec->win_x0;
-    OPJ_UINT32 win_tcy0 = tilec->win_y0;
-    OPJ_UINT32 win_tcx1 = tilec->win_x1;
-    OPJ_UINT32 win_tcy1 = tilec->win_y1;
-
-    if (tr_max->x0 == tr_max->x1 || tr_max->y0 == tr_max->y1) {
-        return OPJ_TRUE;
-    }
-
-    sa = opj_dwt_init_sparse_array(tilec, numres);
-    if (sa == NULL) {
-        return OPJ_FALSE;
-    }
-
-    if (numres == 1U) {
-        OPJ_BOOL ret = opj_sparse_array_int32_read(sa,
-                       tr_max->win_x0 - (OPJ_UINT32)tr_max->x0,
-                       tr_max->win_y0 - (OPJ_UINT32)tr_max->y0,
-                       tr_max->win_x1 - (OPJ_UINT32)tr_max->x0,
-                       tr_max->win_y1 - (OPJ_UINT32)tr_max->y0,
-                       tilec->data_win,
-                       1, tr_max->win_x1 - tr_max->win_x0,
-                       OPJ_TRUE);
-        assert(ret);
-        OPJ_UNUSED(ret);
-        opj_sparse_array_int32_free(sa);
-        return OPJ_TRUE;
-    }
-
-    l_data_size = opj_dwt_max_resolution(tr, numres);
-    /* overflow check */
-    if (l_data_size > (SIZE_MAX - 5U)) {
-        /* FIXME event manager error callback */
-        opj_sparse_array_int32_free(sa);
-        return OPJ_FALSE;
-    }
-    l_data_size += 5U;
-    /* overflow check */
-    if (l_data_size > (SIZE_MAX / sizeof(opj_v4_t))) {
-        /* FIXME event manager error callback */
-        opj_sparse_array_int32_free(sa);
-        return OPJ_FALSE;
-    }
-    h.wavelet = (opj_v4_t*) opj_aligned_malloc(l_data_size * sizeof(opj_v4_t));
-    if (!h.wavelet) {
-        /* FIXME event manager error callback */
-        opj_sparse_array_int32_free(sa);
-        return OPJ_FALSE;
-    }
-    v.wavelet = h.wavelet;
-
-    for (resno = 1; resno < numres; resno ++) {
-        OPJ_UINT32 j;
-        /* Window of interest subband-based coordinates */
-        OPJ_UINT32 win_ll_x0, win_ll_y0, win_ll_x1, win_ll_y1;
-        OPJ_UINT32 win_hl_x0, win_hl_x1;
-        OPJ_UINT32 win_lh_y0, win_lh_y1;
-        /* Window of interest tile-resolution-based coordinates */
-        OPJ_UINT32 win_tr_x0, win_tr_x1, win_tr_y0, win_tr_y1;
-        /* Tile-resolution subband-based coordinates */
-        OPJ_UINT32 tr_ll_x0, tr_ll_y0, tr_hl_x0, tr_lh_y0;
-
-        ++tr;
-
-        h.sn = (OPJ_INT32)rw;
-        v.sn = (OPJ_INT32)rh;
-
-        rw = (OPJ_UINT32)(tr->x1 - tr->x0);
-        rh = (OPJ_UINT32)(tr->y1 - tr->y0);
-
-        h.dn = (OPJ_INT32)(rw - (OPJ_UINT32)h.sn);
-        h.cas = tr->x0 % 2;
-
-        v.dn = (OPJ_INT32)(rh - (OPJ_UINT32)v.sn);
-        v.cas = tr->y0 % 2;
-
-        /* Get the subband coordinates for the window of interest */
-        /* LL band */
-        opj_dwt_get_band_coordinates(tilec, resno, 0,
-                                     win_tcx0, win_tcy0, win_tcx1, win_tcy1,
-                                     &win_ll_x0, &win_ll_y0,
-                                     &win_ll_x1, &win_ll_y1);
-
-        /* HL band */
-        opj_dwt_get_band_coordinates(tilec, resno, 1,
-                                     win_tcx0, win_tcy0, win_tcx1, win_tcy1,
-                                     &win_hl_x0, NULL, &win_hl_x1, NULL);
-
-        /* LH band */
-        opj_dwt_get_band_coordinates(tilec, resno, 2,
-                                     win_tcx0, win_tcy0, win_tcx1, win_tcy1,
-                                     NULL, &win_lh_y0, NULL, &win_lh_y1);
-
-        /* Beware: band index for non-LL0 resolution are 0=HL, 1=LH and 2=HH */
-        tr_ll_x0 = (OPJ_UINT32)tr->bands[1].x0;
-        tr_ll_y0 = (OPJ_UINT32)tr->bands[0].y0;
-        tr_hl_x0 = (OPJ_UINT32)tr->bands[0].x0;
-        tr_lh_y0 = (OPJ_UINT32)tr->bands[1].y0;
-
-        /* Subtract the origin of the bands for this tile, to the subwindow */
-        /* of interest band coordinates, so as to get them relative to the */
-        /* tile */
-        win_ll_x0 = opj_uint_subs(win_ll_x0, tr_ll_x0);
-        win_ll_y0 = opj_uint_subs(win_ll_y0, tr_ll_y0);
-        win_ll_x1 = opj_uint_subs(win_ll_x1, tr_ll_x0);
-        win_ll_y1 = opj_uint_subs(win_ll_y1, tr_ll_y0);
-        win_hl_x0 = opj_uint_subs(win_hl_x0, tr_hl_x0);
-        win_hl_x1 = opj_uint_subs(win_hl_x1, tr_hl_x0);
-        win_lh_y0 = opj_uint_subs(win_lh_y0, tr_lh_y0);
-        win_lh_y1 = opj_uint_subs(win_lh_y1, tr_lh_y0);
-
-        opj_dwt_segment_grow(filter_width, (OPJ_UINT32)h.sn, &win_ll_x0, &win_ll_x1);
-        opj_dwt_segment_grow(filter_width, (OPJ_UINT32)h.dn, &win_hl_x0, &win_hl_x1);
-
-        opj_dwt_segment_grow(filter_width, (OPJ_UINT32)v.sn, &win_ll_y0, &win_ll_y1);
-        opj_dwt_segment_grow(filter_width, (OPJ_UINT32)v.dn, &win_lh_y0, &win_lh_y1);
-
-        /* Compute the tile-resolution-based coordinates for the window of interest */
-        if (h.cas == 0) {
-            win_tr_x0 = opj_uint_min(2 * win_ll_x0, 2 * win_hl_x0 + 1);
-            win_tr_x1 = opj_uint_min(opj_uint_max(2 * win_ll_x1, 2 * win_hl_x1 + 1), rw);
-        } else {
-            win_tr_x0 = opj_uint_min(2 * win_hl_x0, 2 * win_ll_x0 + 1);
-            win_tr_x1 = opj_uint_min(opj_uint_max(2 * win_hl_x1, 2 * win_ll_x1 + 1), rw);
-        }
-
-        if (v.cas == 0) {
-            win_tr_y0 = opj_uint_min(2 * win_ll_y0, 2 * win_lh_y0 + 1);
-            win_tr_y1 = opj_uint_min(opj_uint_max(2 * win_ll_y1, 2 * win_lh_y1 + 1), rh);
-        } else {
-            win_tr_y0 = opj_uint_min(2 * win_lh_y0, 2 * win_ll_y0 + 1);
-            win_tr_y1 = opj_uint_min(opj_uint_max(2 * win_lh_y1, 2 * win_ll_y1 + 1), rh);
-        }
-
-        h.win_l_x0 = win_ll_x0;
-        h.win_l_x1 = win_ll_x1;
-        h.win_h_x0 = win_hl_x0;
-        h.win_h_x1 = win_hl_x1;
-        for (j = 0; j + 3 < rh; j += 4) {
-            if ((j + 3 >= win_ll_y0 && j < win_ll_y1) ||
-                    (j + 3 >= win_lh_y0 + (OPJ_UINT32)v.sn &&
-                     j < win_lh_y1 + (OPJ_UINT32)v.sn)) {
-                opj_v4dwt_interleave_partial_h(&h, sa, j, opj_uint_min(4U, rh - j));
-                opj_v4dwt_decode(&h);
-                if (!opj_sparse_array_int32_write(sa,
-                                                  win_tr_x0, j,
-                                                  win_tr_x1, j + 4,
-                                                  (OPJ_INT32*)&h.wavelet[win_tr_x0].f[0],
-                                                  4, 1, OPJ_TRUE)) {
-                    /* FIXME event manager error callback */
-                    opj_sparse_array_int32_free(sa);
-                    opj_aligned_free(h.wavelet);
-                    return OPJ_FALSE;
-                }
-            }
-        }
-
-        if (j < rh &&
-                ((j + 3 >= win_ll_y0 && j < win_ll_y1) ||
-                 (j + 3 >= win_lh_y0 + (OPJ_UINT32)v.sn &&
-                  j < win_lh_y1 + (OPJ_UINT32)v.sn))) {
-            opj_v4dwt_interleave_partial_h(&h, sa, j, rh - j);
-            opj_v4dwt_decode(&h);
-            if (!opj_sparse_array_int32_write(sa,
-                                              win_tr_x0, j,
-                                              win_tr_x1, rh,
-                                              (OPJ_INT32*)&h.wavelet[win_tr_x0].f[0],
-                                              4, 1, OPJ_TRUE)) {
-                /* FIXME event manager error callback */
-                opj_sparse_array_int32_free(sa);
-                opj_aligned_free(h.wavelet);
-                return OPJ_FALSE;
-            }
-        }
-
-        v.win_l_x0 = win_ll_y0;
-        v.win_l_x1 = win_ll_y1;
-        v.win_h_x0 = win_lh_y0;
-        v.win_h_x1 = win_lh_y1;
-        for (j = win_tr_x0; j < win_tr_x1; j += 4) {
-            OPJ_UINT32 nb_elts = opj_uint_min(4U, win_tr_x1 - j);
-
-            opj_v4dwt_interleave_partial_v(&v, sa, j, nb_elts);
-            opj_v4dwt_decode(&v);
-
-            if (!opj_sparse_array_int32_write(sa,
-                                              j, win_tr_y0,
-                                              j + nb_elts, win_tr_y1,
-                                              (OPJ_INT32*)&h.wavelet[win_tr_y0].f[0],
-                                              1, 4, OPJ_TRUE)) {
-                /* FIXME event manager error callback */
-                opj_sparse_array_int32_free(sa);
-                opj_aligned_free(h.wavelet);
-                return OPJ_FALSE;
-            }
-        }
-    }
-
-    {
-        OPJ_BOOL ret = opj_sparse_array_int32_read(sa,
-                       tr_max->win_x0 - (OPJ_UINT32)tr_max->x0,
-                       tr_max->win_y0 - (OPJ_UINT32)tr_max->y0,
-                       tr_max->win_x1 - (OPJ_UINT32)tr_max->x0,
-                       tr_max->win_y1 - (OPJ_UINT32)tr_max->y0,
-                       tilec->data_win,
-                       1, tr_max->win_x1 - tr_max->win_x0,
-                       OPJ_TRUE);
-        assert(ret);
-        OPJ_UNUSED(ret);
-    }
-    opj_sparse_array_int32_free(sa);
-
-    opj_aligned_free(h.wavelet);
-    return OPJ_TRUE;
-}
-
-
-OPJ_BOOL opj_dwt_decode_real(opj_tcd_t *p_tcd,
-                             opj_tcd_tilecomp_t* OPJ_RESTRICT tilec,
-                             OPJ_UINT32 numres)
-{
-    if (p_tcd->whole_tile_decoding) {
-        return opj_dwt_decode_tile_97(tilec, numres);
-    } else {
-        return opj_dwt_decode_partial_97(tilec, numres);
-    }
-}
diff --git a/third_party/libopenjpeg20/dwt.h b/third_party/libopenjpeg20/dwt.h
deleted file mode 100644
index 4f63e52..0000000
--- a/third_party/libopenjpeg20/dwt.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef OPJ_DWT_H
-#define OPJ_DWT_H
-/**
-@file dwt.h
-@brief Implementation of a discrete wavelet transform (DWT)
-
-The functions in DWT.C have for goal to realize forward and inverse discret wavelet
-transform with filter 5-3 (reversible) and filter 9-7 (irreversible). The functions in
-DWT.C are used by some function in TCD.C.
-*/
-
-/** @defgroup DWT DWT - Implementation of a discrete wavelet transform */
-/*@{*/
-
-
-/** @name Exported functions */
-/*@{*/
-/* ----------------------------------------------------------------------- */
-/**
-Forward 5-3 wavelet transform in 2-D.
-Apply a reversible DWT transform to a component of an image.
-@param tilec Tile component information (current tile)
-*/
-OPJ_BOOL opj_dwt_encode(opj_tcd_tilecomp_t * tilec);
-
-/**
-Inverse 5-3 wavelet transform in 2-D.
-Apply a reversible inverse DWT transform to a component of an image.
-@param p_tcd TCD handle
-@param tilec Tile component information (current tile)
-@param numres Number of resolution levels to decode
-*/
-OPJ_BOOL opj_dwt_decode(opj_tcd_t *p_tcd,
-                        opj_tcd_tilecomp_t* tilec,
-                        OPJ_UINT32 numres);
-
-/**
-Get the gain of a subband for the reversible 5-3 DWT.
-@param orient Number that identifies the subband (0->LL, 1->HL, 2->LH, 3->HH)
-@return Returns 0 if orient = 0, returns 1 if orient = 1 or 2, returns 2 otherwise
-*/
-OPJ_UINT32 opj_dwt_getgain(OPJ_UINT32 orient) ;
-/**
-Get the norm of a wavelet function of a subband at a specified level for the reversible 5-3 DWT.
-@param level Level of the wavelet function
-@param orient Band of the wavelet function
-@return Returns the norm of the wavelet function
-*/
-OPJ_FLOAT64 opj_dwt_getnorm(OPJ_UINT32 level, OPJ_UINT32 orient);
-/**
-Forward 9-7 wavelet transform in 2-D.
-Apply an irreversible DWT transform to a component of an image.
-@param tilec Tile component information (current tile)
-*/
-OPJ_BOOL opj_dwt_encode_real(opj_tcd_tilecomp_t * tilec);
-/**
-Inverse 9-7 wavelet transform in 2-D.
-Apply an irreversible inverse DWT transform to a component of an image.
-@param p_tcd TCD handle
-@param tilec Tile component information (current tile)
-@param numres Number of resolution levels to decode
-*/
-OPJ_BOOL opj_dwt_decode_real(opj_tcd_t *p_tcd,
-                             opj_tcd_tilecomp_t* OPJ_RESTRICT tilec,
-                             OPJ_UINT32 numres);
-
-/**
-Get the gain of a subband for the irreversible 9-7 DWT.
-@param orient Number that identifies the subband (0->LL, 1->HL, 2->LH, 3->HH)
-@return Returns the gain of the 9-7 wavelet transform
-*/
-OPJ_UINT32 opj_dwt_getgain_real(OPJ_UINT32 orient);
-/**
-Get the norm of a wavelet function of a subband at a specified level for the irreversible 9-7 DWT
-@param level Level of the wavelet function
-@param orient Band of the wavelet function
-@return Returns the norm of the 9-7 wavelet
-*/
-OPJ_FLOAT64 opj_dwt_getnorm_real(OPJ_UINT32 level, OPJ_UINT32 orient);
-/**
-Explicit calculation of the Quantization Stepsizes
-@param tccp Tile-component coding parameters
-@param prec Precint analyzed
-*/
-void opj_dwt_calc_explicit_stepsizes(opj_tccp_t * tccp, OPJ_UINT32 prec);
-/* ----------------------------------------------------------------------- */
-/*@}*/
-
-/*@}*/
-
-#endif /* OPJ_DWT_H */
diff --git a/third_party/libopenjpeg20/image.c b/third_party/libopenjpeg20/image.c
deleted file mode 100644
index fe37390..0000000
--- a/third_party/libopenjpeg20/image.c
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "opj_includes.h"
-
-opj_image_t* opj_image_create0(void)
-{
-    opj_image_t *image = (opj_image_t*)opj_calloc(1, sizeof(opj_image_t));
-    return image;
-}
-
-opj_image_t* OPJ_CALLCONV opj_image_create(OPJ_UINT32 numcmpts,
-        opj_image_cmptparm_t *cmptparms, OPJ_COLOR_SPACE clrspc)
-{
-    OPJ_UINT32 compno;
-    opj_image_t *image = NULL;
-
-    image = (opj_image_t*) opj_calloc(1, sizeof(opj_image_t));
-    if (image) {
-        image->color_space = clrspc;
-        image->numcomps = numcmpts;
-        /* allocate memory for the per-component information */
-        image->comps = (opj_image_comp_t*)opj_calloc(image->numcomps,
-                       sizeof(opj_image_comp_t));
-        if (!image->comps) {
-            /* TODO replace with event manager, breaks API */
-            /* fprintf(stderr,"Unable to allocate memory for image.\n"); */
-            opj_image_destroy(image);
-            return NULL;
-        }
-        /* create the individual image components */
-        for (compno = 0; compno < numcmpts; compno++) {
-            opj_image_comp_t *comp = &image->comps[compno];
-            comp->dx = cmptparms[compno].dx;
-            comp->dy = cmptparms[compno].dy;
-            comp->w = cmptparms[compno].w;
-            comp->h = cmptparms[compno].h;
-            comp->x0 = cmptparms[compno].x0;
-            comp->y0 = cmptparms[compno].y0;
-            comp->prec = cmptparms[compno].prec;
-            comp->bpp = cmptparms[compno].bpp;
-            comp->sgnd = cmptparms[compno].sgnd;
-            if (comp->h != 0 &&
-                    (OPJ_SIZE_T)comp->w > SIZE_MAX / comp->h / sizeof(OPJ_INT32)) {
-                /* TODO event manager */
-                opj_image_destroy(image);
-                return NULL;
-            }
-            comp->data = (OPJ_INT32*) opj_image_data_alloc(
-                             (size_t)comp->w * comp->h * sizeof(OPJ_INT32));
-            if (!comp->data) {
-                /* TODO replace with event manager, breaks API */
-                /* fprintf(stderr,"Unable to allocate memory for image.\n"); */
-                opj_image_destroy(image);
-                return NULL;
-            }
-            memset(comp->data, 0, (size_t)comp->w * comp->h * sizeof(OPJ_INT32));
-        }
-    }
-
-    return image;
-}
-
-void OPJ_CALLCONV opj_image_destroy(opj_image_t *image)
-{
-    if (image) {
-        if (image->comps) {
-            OPJ_UINT32 compno;
-
-            /* image components */
-            for (compno = 0; compno < image->numcomps; compno++) {
-                opj_image_comp_t *image_comp = &(image->comps[compno]);
-                if (image_comp->data) {
-                    opj_image_data_free(image_comp->data);
-                }
-            }
-            opj_free(image->comps);
-        }
-
-        if (image->icc_profile_buf) {
-            opj_free(image->icc_profile_buf);
-        }
-
-        opj_free(image);
-    }
-}
-
-/**
- * Updates the components characteristics of the image from the coding parameters.
- *
- * @param p_image_header    the image header to update.
- * @param p_cp              the coding parameters from which to update the image.
- */
-void opj_image_comp_header_update(opj_image_t * p_image_header,
-                                  const struct opj_cp * p_cp)
-{
-    OPJ_UINT32 i, l_width, l_height;
-    OPJ_UINT32 l_x0, l_y0, l_x1, l_y1;
-    OPJ_UINT32 l_comp_x0, l_comp_y0, l_comp_x1, l_comp_y1;
-    opj_image_comp_t* l_img_comp = NULL;
-
-    l_x0 = opj_uint_max(p_cp->tx0, p_image_header->x0);
-    l_y0 = opj_uint_max(p_cp->ty0, p_image_header->y0);
-    l_x1 = p_cp->tx0 + (p_cp->tw - 1U) *
-           p_cp->tdx; /* validity of p_cp members used here checked in opj_j2k_read_siz. Can't overflow. */
-    l_y1 = p_cp->ty0 + (p_cp->th - 1U) * p_cp->tdy; /* can't overflow */
-    l_x1 = opj_uint_min(opj_uint_adds(l_x1, p_cp->tdx),
-                        p_image_header->x1); /* use add saturated to prevent overflow */
-    l_y1 = opj_uint_min(opj_uint_adds(l_y1, p_cp->tdy),
-                        p_image_header->y1); /* use add saturated to prevent overflow */
-
-    l_img_comp = p_image_header->comps;
-    for (i = 0; i < p_image_header->numcomps; ++i) {
-        l_comp_x0 = opj_uint_ceildiv(l_x0, l_img_comp->dx);
-        l_comp_y0 = opj_uint_ceildiv(l_y0, l_img_comp->dy);
-        l_comp_x1 = opj_uint_ceildiv(l_x1, l_img_comp->dx);
-        l_comp_y1 = opj_uint_ceildiv(l_y1, l_img_comp->dy);
-        l_width   = opj_uint_ceildivpow2(l_comp_x1 - l_comp_x0, l_img_comp->factor);
-        l_height  = opj_uint_ceildivpow2(l_comp_y1 - l_comp_y0, l_img_comp->factor);
-        l_img_comp->w = l_width;
-        l_img_comp->h = l_height;
-        l_img_comp->x0 = l_comp_x0;
-        l_img_comp->y0 = l_comp_y0;
-        ++l_img_comp;
-    }
-}
-
-
-/**
- * Copy only header of image and its component header (no data are copied)
- * if dest image have data, they will be freed
- *
- * @param   p_image_src     the src image
- * @param   p_image_dest    the dest image
- *
- */
-void opj_copy_image_header(const opj_image_t* p_image_src,
-                           opj_image_t* p_image_dest)
-{
-    OPJ_UINT32 compno;
-
-    /* preconditions */
-    assert(p_image_src != 00);
-    assert(p_image_dest != 00);
-
-    p_image_dest->x0 = p_image_src->x0;
-    p_image_dest->y0 = p_image_src->y0;
-    p_image_dest->x1 = p_image_src->x1;
-    p_image_dest->y1 = p_image_src->y1;
-
-    if (p_image_dest->comps) {
-        for (compno = 0; compno < p_image_dest->numcomps; compno++) {
-            opj_image_comp_t *image_comp = &(p_image_dest->comps[compno]);
-            if (image_comp->data) {
-                opj_image_data_free(image_comp->data);
-            }
-        }
-        opj_free(p_image_dest->comps);
-        p_image_dest->comps = NULL;
-    }
-
-    p_image_dest->numcomps = p_image_src->numcomps;
-
-    p_image_dest->comps = (opj_image_comp_t*) opj_malloc(p_image_dest->numcomps *
-                          sizeof(opj_image_comp_t));
-    if (!p_image_dest->comps) {
-        p_image_dest->comps = NULL;
-        p_image_dest->numcomps = 0;
-        return;
-    }
-
-    for (compno = 0; compno < p_image_dest->numcomps; compno++) {
-        memcpy(&(p_image_dest->comps[compno]),
-               &(p_image_src->comps[compno]),
-               sizeof(opj_image_comp_t));
-        p_image_dest->comps[compno].data = NULL;
-    }
-
-    p_image_dest->color_space = p_image_src->color_space;
-    p_image_dest->icc_profile_len = p_image_src->icc_profile_len;
-
-    if (p_image_dest->icc_profile_len) {
-        p_image_dest->icc_profile_buf = (OPJ_BYTE*)opj_malloc(
-                                            p_image_dest->icc_profile_len);
-        if (!p_image_dest->icc_profile_buf) {
-            p_image_dest->icc_profile_buf = NULL;
-            p_image_dest->icc_profile_len = 0;
-            return;
-        }
-        memcpy(p_image_dest->icc_profile_buf,
-               p_image_src->icc_profile_buf,
-               p_image_src->icc_profile_len);
-    } else {
-        p_image_dest->icc_profile_buf = NULL;
-    }
-
-    return;
-}
-
-opj_image_t* OPJ_CALLCONV opj_image_tile_create(OPJ_UINT32 numcmpts,
-        opj_image_cmptparm_t *cmptparms, OPJ_COLOR_SPACE clrspc)
-{
-    OPJ_UINT32 compno;
-    opj_image_t *image = 00;
-
-    image = (opj_image_t*) opj_calloc(1, sizeof(opj_image_t));
-    if (image) {
-
-        image->color_space = clrspc;
-        image->numcomps = numcmpts;
-
-        /* allocate memory for the per-component information */
-        image->comps = (opj_image_comp_t*)opj_calloc(image->numcomps,
-                       sizeof(opj_image_comp_t));
-        if (!image->comps) {
-            opj_image_destroy(image);
-            return 00;
-        }
-
-        /* create the individual image components */
-        for (compno = 0; compno < numcmpts; compno++) {
-            opj_image_comp_t *comp = &image->comps[compno];
-            comp->dx = cmptparms[compno].dx;
-            comp->dy = cmptparms[compno].dy;
-            comp->w = cmptparms[compno].w;
-            comp->h = cmptparms[compno].h;
-            comp->x0 = cmptparms[compno].x0;
-            comp->y0 = cmptparms[compno].y0;
-            comp->prec = cmptparms[compno].prec;
-            comp->sgnd = cmptparms[compno].sgnd;
-            comp->data = 0;
-        }
-    }
-
-    return image;
-}
diff --git a/third_party/libopenjpeg20/j2k.c b/third_party/libopenjpeg20/j2k.c
deleted file mode 100644
index 690b533..0000000
--- a/third_party/libopenjpeg20/j2k.c
+++ /dev/null
@@ -1,12137 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2008, Jerome Fimes, Communications & Systemes <jerome.fimes@c-s.fr>
- * Copyright (c) 2006-2007, Parvatha Elangovan
- * Copyright (c) 2010-2011, Kaori Hagihara
- * Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France
- * Copyright (c) 2012, CS Systemes d'Information, France
- * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "opj_includes.h"
-
-/** @defgroup J2K J2K - JPEG-2000 codestream reader/writer */
-/*@{*/
-
-/** @name Local static functions */
-/*@{*/
-
-/**
- * Sets up the procedures to do on reading header. Developpers wanting to extend the library can add their own reading procedures.
- */
-static OPJ_BOOL opj_j2k_setup_header_reading(opj_j2k_t *p_j2k,
-        opj_event_mgr_t * p_manager);
-
-/**
- * The read header procedure.
- */
-static OPJ_BOOL opj_j2k_read_header_procedure(opj_j2k_t *p_j2k,
-        opj_stream_private_t *p_stream,
-        opj_event_mgr_t * p_manager);
-
-/**
- * The default encoding validation procedure without any extension.
- *
- * @param       p_j2k                   the jpeg2000 codec to validate.
- * @param       p_stream                the input stream to validate.
- * @param       p_manager               the user event manager.
- *
- * @return true if the parameters are correct.
- */
-static OPJ_BOOL opj_j2k_encoding_validation(opj_j2k_t * p_j2k,
-        opj_stream_private_t *p_stream,
-        opj_event_mgr_t * p_manager);
-
-/**
- * The default decoding validation procedure without any extension.
- *
- * @param       p_j2k                   the jpeg2000 codec to validate.
- * @param       p_stream                                the input stream to validate.
- * @param       p_manager               the user event manager.
- *
- * @return true if the parameters are correct.
- */
-static OPJ_BOOL opj_j2k_decoding_validation(opj_j2k_t * p_j2k,
-        opj_stream_private_t *p_stream,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Sets up the validation ,i.e. adds the procedures to lauch to make sure the codec parameters
- * are valid. Developpers wanting to extend the library can add their own validation procedures.
- */
-static OPJ_BOOL opj_j2k_setup_encoding_validation(opj_j2k_t *p_j2k,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Sets up the validation ,i.e. adds the procedures to lauch to make sure the codec parameters
- * are valid. Developpers wanting to extend the library can add their own validation procedures.
- */
-static OPJ_BOOL opj_j2k_setup_decoding_validation(opj_j2k_t *p_j2k,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Sets up the validation ,i.e. adds the procedures to lauch to make sure the codec parameters
- * are valid. Developpers wanting to extend the library can add their own validation procedures.
- */
-static OPJ_BOOL opj_j2k_setup_end_compress(opj_j2k_t *p_j2k,
-        opj_event_mgr_t * p_manager);
-
-/**
- * The mct encoding validation procedure.
- *
- * @param       p_j2k                   the jpeg2000 codec to validate.
- * @param       p_stream                                the input stream to validate.
- * @param       p_manager               the user event manager.
- *
- * @return true if the parameters are correct.
- */
-static OPJ_BOOL opj_j2k_mct_validation(opj_j2k_t * p_j2k,
-                                       opj_stream_private_t *p_stream,
-                                       opj_event_mgr_t * p_manager);
-
-/**
- * Builds the tcd decoder to use to decode tile.
- */
-static OPJ_BOOL opj_j2k_build_decoder(opj_j2k_t * p_j2k,
-                                      opj_stream_private_t *p_stream,
-                                      opj_event_mgr_t * p_manager);
-/**
- * Builds the tcd encoder to use to encode tile.
- */
-static OPJ_BOOL opj_j2k_build_encoder(opj_j2k_t * p_j2k,
-                                      opj_stream_private_t *p_stream,
-                                      opj_event_mgr_t * p_manager);
-
-/**
- * Creates a tile-coder encoder.
- *
- * @param       p_stream                        the stream to write data to.
- * @param       p_j2k                           J2K codec.
- * @param       p_manager                   the user event manager.
-*/
-static OPJ_BOOL opj_j2k_create_tcd(opj_j2k_t *p_j2k,
-                                   opj_stream_private_t *p_stream,
-                                   opj_event_mgr_t * p_manager);
-
-/**
- * Excutes the given procedures on the given codec.
- *
- * @param       p_procedure_list        the list of procedures to execute
- * @param       p_j2k                           the jpeg2000 codec to execute the procedures on.
- * @param       p_stream                        the stream to execute the procedures on.
- * @param       p_manager                       the user manager.
- *
- * @return      true                            if all the procedures were successfully executed.
- */
-static OPJ_BOOL opj_j2k_exec(opj_j2k_t * p_j2k,
-                             opj_procedure_list_t * p_procedure_list,
-                             opj_stream_private_t *p_stream,
-                             opj_event_mgr_t * p_manager);
-
-/**
- * Updates the rates of the tcp.
- *
- * @param       p_stream                                the stream to write data to.
- * @param       p_j2k                           J2K codec.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_update_rates(opj_j2k_t *p_j2k,
-                                     opj_stream_private_t *p_stream,
-                                     opj_event_mgr_t * p_manager);
-
-/**
- * Copies the decoding tile parameters onto all the tile parameters.
- * Creates also the tile decoder.
- */
-static OPJ_BOOL opj_j2k_copy_default_tcp_and_create_tcd(opj_j2k_t * p_j2k,
-        opj_stream_private_t *p_stream,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Destroys the memory associated with the decoding of headers.
- */
-static OPJ_BOOL opj_j2k_destroy_header_memory(opj_j2k_t * p_j2k,
-        opj_stream_private_t *p_stream,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Reads the lookup table containing all the marker, status and action, and returns the handler associated
- * with the marker value.
- * @param       p_id            Marker value to look up
- *
- * @return      the handler associated with the id.
-*/
-static const struct opj_dec_memory_marker_handler * opj_j2k_get_marker_handler(
-    OPJ_UINT32 p_id);
-
-/**
- * Destroys a tile coding parameter structure.
- *
- * @param       p_tcp           the tile coding parameter to destroy.
- */
-static void opj_j2k_tcp_destroy(opj_tcp_t *p_tcp);
-
-/**
- * Destroys the data inside a tile coding parameter structure.
- *
- * @param       p_tcp           the tile coding parameter which contain data to destroy.
- */
-static void opj_j2k_tcp_data_destroy(opj_tcp_t *p_tcp);
-
-/**
- * Destroys a coding parameter structure.
- *
- * @param       p_cp            the coding parameter to destroy.
- */
-static void opj_j2k_cp_destroy(opj_cp_t *p_cp);
-
-/**
- * Compare 2 a SPCod/ SPCoc elements, i.e. the coding style of a given component of a tile.
- *
- * @param       p_j2k            J2K codec.
- * @param       p_tile_no        Tile number
- * @param       p_first_comp_no  The 1st component number to compare.
- * @param       p_second_comp_no The 1st component number to compare.
- *
- * @return OPJ_TRUE if SPCdod are equals.
- */
-static OPJ_BOOL opj_j2k_compare_SPCod_SPCoc(opj_j2k_t *p_j2k,
-        OPJ_UINT32 p_tile_no, OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no);
-
-/**
- * Writes a SPCod or SPCoc element, i.e. the coding style of a given component of a tile.
- *
- * @param       p_j2k           J2K codec.
- * @param       p_tile_no       FIXME DOC
- * @param       p_comp_no       the component number to output.
- * @param       p_data          FIXME DOC
- * @param       p_header_size   FIXME DOC
- * @param       p_manager       the user event manager.
- *
- * @return FIXME DOC
-*/
-static OPJ_BOOL opj_j2k_write_SPCod_SPCoc(opj_j2k_t *p_j2k,
-        OPJ_UINT32 p_tile_no,
-        OPJ_UINT32 p_comp_no,
-        OPJ_BYTE * p_data,
-        OPJ_UINT32 * p_header_size,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Gets the size taken by writing a SPCod or SPCoc for the given tile and component.
- *
- * @param       p_j2k                   the J2K codec.
- * @param       p_tile_no               the tile index.
- * @param       p_comp_no               the component being outputted.
- *
- * @return      the number of bytes taken by the SPCod element.
- */
-static OPJ_UINT32 opj_j2k_get_SPCod_SPCoc_size(opj_j2k_t *p_j2k,
-        OPJ_UINT32 p_tile_no,
-        OPJ_UINT32 p_comp_no);
-
-/**
- * Reads a SPCod or SPCoc element, i.e. the coding style of a given component of a tile.
- * @param       p_j2k           the jpeg2000 codec.
- * @param       compno          FIXME DOC
- * @param       p_header_data   the data contained in the COM box.
- * @param       p_header_size   the size of the data contained in the COM marker.
- * @param       p_manager       the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_SPCod_SPCoc(opj_j2k_t *p_j2k,
-        OPJ_UINT32 compno,
-        OPJ_BYTE * p_header_data,
-        OPJ_UINT32 * p_header_size,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Gets the size taken by writing SQcd or SQcc element, i.e. the quantization values of a band in the QCD or QCC.
- *
- * @param       p_tile_no               the tile index.
- * @param       p_comp_no               the component being outputted.
- * @param       p_j2k                   the J2K codec.
- *
- * @return      the number of bytes taken by the SPCod element.
- */
-static OPJ_UINT32 opj_j2k_get_SQcd_SQcc_size(opj_j2k_t *p_j2k,
-        OPJ_UINT32 p_tile_no,
-        OPJ_UINT32 p_comp_no);
-
-/**
- * Compares 2 SQcd or SQcc element, i.e. the quantization values of a band in the QCD or QCC.
- *
- * @param       p_j2k                   J2K codec.
- * @param       p_tile_no               the tile to output.
- * @param       p_first_comp_no         the first component number to compare.
- * @param       p_second_comp_no        the second component number to compare.
- *
- * @return OPJ_TRUE if equals.
- */
-static OPJ_BOOL opj_j2k_compare_SQcd_SQcc(opj_j2k_t *p_j2k,
-        OPJ_UINT32 p_tile_no, OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no);
-
-
-/**
- * Writes a SQcd or SQcc element, i.e. the quantization values of a band in the QCD or QCC.
- *
- * @param       p_tile_no               the tile to output.
- * @param       p_comp_no               the component number to output.
- * @param       p_data                  the data buffer.
- * @param       p_header_size   pointer to the size of the data buffer, it is changed by the function.
- * @param       p_j2k                   J2K codec.
- * @param       p_manager               the user event manager.
- *
-*/
-static OPJ_BOOL opj_j2k_write_SQcd_SQcc(opj_j2k_t *p_j2k,
-                                        OPJ_UINT32 p_tile_no,
-                                        OPJ_UINT32 p_comp_no,
-                                        OPJ_BYTE * p_data,
-                                        OPJ_UINT32 * p_header_size,
-                                        opj_event_mgr_t * p_manager);
-
-/**
- * Updates the Tile Length Marker.
- */
-static void opj_j2k_update_tlm(opj_j2k_t * p_j2k, OPJ_UINT32 p_tile_part_size);
-
-/**
- * Reads a SQcd or SQcc element, i.e. the quantization values of a band in the QCD or QCC.
- *
- * @param       p_j2k           J2K codec.
- * @param       compno          the component number to output.
- * @param       p_header_data   the data buffer.
- * @param       p_header_size   pointer to the size of the data buffer, it is changed by the function.
- * @param       p_manager       the user event manager.
- *
-*/
-static OPJ_BOOL opj_j2k_read_SQcd_SQcc(opj_j2k_t *p_j2k,
-                                       OPJ_UINT32 compno,
-                                       OPJ_BYTE * p_header_data,
-                                       OPJ_UINT32 * p_header_size,
-                                       opj_event_mgr_t * p_manager);
-
-/**
- * Copies the tile component parameters of all the component from the first tile component.
- *
- * @param               p_j2k           the J2k codec.
- */
-static void opj_j2k_copy_tile_component_parameters(opj_j2k_t *p_j2k);
-
-/**
- * Copies the tile quantization parameters of all the component from the first tile component.
- *
- * @param               p_j2k           the J2k codec.
- */
-static void opj_j2k_copy_tile_quantization_parameters(opj_j2k_t *p_j2k);
-
-/**
- * Reads the tiles.
- */
-static OPJ_BOOL opj_j2k_decode_tiles(opj_j2k_t *p_j2k,
-                                     opj_stream_private_t *p_stream,
-                                     opj_event_mgr_t * p_manager);
-
-static OPJ_BOOL opj_j2k_pre_write_tile(opj_j2k_t * p_j2k,
-                                       OPJ_UINT32 p_tile_index,
-                                       opj_stream_private_t *p_stream,
-                                       opj_event_mgr_t * p_manager);
-
-static OPJ_BOOL opj_j2k_update_image_data(opj_tcd_t * p_tcd,
-        opj_image_t* p_output_image);
-
-static void opj_get_tile_dimensions(opj_image_t * l_image,
-                                    opj_tcd_tilecomp_t * l_tilec,
-                                    opj_image_comp_t * l_img_comp,
-                                    OPJ_UINT32* l_size_comp,
-                                    OPJ_UINT32* l_width,
-                                    OPJ_UINT32* l_height,
-                                    OPJ_UINT32* l_offset_x,
-                                    OPJ_UINT32* l_offset_y,
-                                    OPJ_UINT32* l_image_width,
-                                    OPJ_UINT32* l_stride,
-                                    OPJ_UINT32* l_tile_offset);
-
-static void opj_j2k_get_tile_data(opj_tcd_t * p_tcd, OPJ_BYTE * p_data);
-
-static OPJ_BOOL opj_j2k_post_write_tile(opj_j2k_t * p_j2k,
-                                        opj_stream_private_t *p_stream,
-                                        opj_event_mgr_t * p_manager);
-
-/**
- * Sets up the procedures to do on writing header.
- * Developers wanting to extend the library can add their own writing procedures.
- */
-static OPJ_BOOL opj_j2k_setup_header_writing(opj_j2k_t *p_j2k,
-        opj_event_mgr_t * p_manager);
-
-static OPJ_BOOL opj_j2k_write_first_tile_part(opj_j2k_t *p_j2k,
-        OPJ_BYTE * p_data,
-        OPJ_UINT32 * p_data_written,
-        OPJ_UINT32 p_total_data_size,
-        opj_stream_private_t *p_stream,
-        struct opj_event_mgr * p_manager);
-
-static OPJ_BOOL opj_j2k_write_all_tile_parts(opj_j2k_t *p_j2k,
-        OPJ_BYTE * p_data,
-        OPJ_UINT32 * p_data_written,
-        OPJ_UINT32 p_total_data_size,
-        opj_stream_private_t *p_stream,
-        struct opj_event_mgr * p_manager);
-
-/**
- * Gets the offset of the header.
- *
- * @param       p_stream                the stream to write data to.
- * @param       p_j2k                   J2K codec.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_get_end_header(opj_j2k_t *p_j2k,
-                                       opj_stream_private_t *p_stream,
-                                       opj_event_mgr_t * p_manager);
-
-static OPJ_BOOL opj_j2k_allocate_tile_element_cstr_index(opj_j2k_t *p_j2k);
-
-/*
- * -----------------------------------------------------------------------
- * -----------------------------------------------------------------------
- * -----------------------------------------------------------------------
- */
-
-/**
- * Writes the SOC marker (Start Of Codestream)
- *
- * @param       p_stream                        the stream to write data to.
- * @param       p_j2k                   J2K codec.
- * @param       p_manager       the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_soc(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Reads a SOC marker (Start of Codestream)
- * @param       p_j2k           the jpeg2000 file codec.
- * @param       p_stream        XXX needs data
- * @param       p_manager       the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_soc(opj_j2k_t *p_j2k,
-                                 opj_stream_private_t *p_stream,
-                                 opj_event_mgr_t * p_manager);
-
-/**
- * Writes the SIZ marker (image and tile size)
- *
- * @param       p_j2k           J2K codec.
- * @param       p_stream        the stream to write data to.
- * @param       p_manager       the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_siz(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Reads a SIZ marker (image and tile size)
- * @param       p_j2k           the jpeg2000 file codec.
- * @param       p_header_data   the data contained in the SIZ box.
- * @param       p_header_size   the size of the data contained in the SIZ marker.
- * @param       p_manager       the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_siz(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-
-/**
- * Writes the COM marker (comment)
- *
- * @param       p_stream                        the stream to write data to.
- * @param       p_j2k                   J2K codec.
- * @param       p_manager       the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_com(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Reads a COM marker (comments)
- * @param       p_j2k           the jpeg2000 file codec.
- * @param       p_header_data   the data contained in the COM box.
- * @param       p_header_size   the size of the data contained in the COM marker.
- * @param       p_manager       the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_com(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-/**
- * Writes the COD marker (Coding style default)
- *
- * @param       p_stream                        the stream to write data to.
- * @param       p_j2k                   J2K codec.
- * @param       p_manager       the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_cod(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Reads a COD marker (Coding style defaults)
- * @param       p_header_data   the data contained in the COD box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the COD marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_cod(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-
-/**
- * Compares 2 COC markers (Coding style component)
- *
- * @param       p_j2k            J2K codec.
- * @param       p_first_comp_no  the index of the first component to compare.
- * @param       p_second_comp_no the index of the second component to compare.
- *
- * @return      OPJ_TRUE if equals
- */
-static OPJ_BOOL opj_j2k_compare_coc(opj_j2k_t *p_j2k,
-                                    OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no);
-
-/**
- * Writes the COC marker (Coding style component)
- *
- * @param       p_j2k       J2K codec.
- * @param       p_comp_no   the index of the component to output.
- * @param       p_stream    the stream to write data to.
- * @param       p_manager   the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_coc(opj_j2k_t *p_j2k,
-                                  OPJ_UINT32 p_comp_no,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Writes the COC marker (Coding style component)
- *
- * @param       p_j2k                   J2K codec.
- * @param       p_comp_no               the index of the component to output.
- * @param       p_data          FIXME DOC
- * @param       p_data_written  FIXME DOC
- * @param       p_manager               the user event manager.
-*/
-static void opj_j2k_write_coc_in_memory(opj_j2k_t *p_j2k,
-                                        OPJ_UINT32 p_comp_no,
-                                        OPJ_BYTE * p_data,
-                                        OPJ_UINT32 * p_data_written,
-                                        opj_event_mgr_t * p_manager);
-
-/**
- * Gets the maximum size taken by a coc.
- *
- * @param       p_j2k   the jpeg2000 codec to use.
- */
-static OPJ_UINT32 opj_j2k_get_max_coc_size(opj_j2k_t *p_j2k);
-
-/**
- * Reads a COC marker (Coding Style Component)
- * @param       p_header_data   the data contained in the COC box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the COC marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_coc(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-
-/**
- * Writes the QCD marker (quantization default)
- *
- * @param       p_j2k                   J2K codec.
- * @param       p_stream                the stream to write data to.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_qcd(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Reads a QCD marker (Quantization defaults)
- * @param       p_header_data   the data contained in the QCD box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the QCD marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_qcd(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-
-/**
- * Compare QCC markers (quantization component)
- *
- * @param       p_j2k                 J2K codec.
- * @param       p_first_comp_no       the index of the first component to compare.
- * @param       p_second_comp_no      the index of the second component to compare.
- *
- * @return OPJ_TRUE if equals.
- */
-static OPJ_BOOL opj_j2k_compare_qcc(opj_j2k_t *p_j2k,
-                                    OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no);
-
-/**
- * Writes the QCC marker (quantization component)
- *
- * @param       p_comp_no       the index of the component to output.
- * @param       p_stream                the stream to write data to.
- * @param       p_j2k                   J2K codec.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_qcc(opj_j2k_t *p_j2k,
-                                  OPJ_UINT32 p_comp_no,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Writes the QCC marker (quantization component)
- *
- * @param       p_j2k           J2K codec.
- * @param       p_comp_no       the index of the component to output.
- * @param       p_data          FIXME DOC
- * @param       p_data_written  the stream to write data to.
- * @param       p_manager       the user event manager.
-*/
-static void opj_j2k_write_qcc_in_memory(opj_j2k_t *p_j2k,
-                                        OPJ_UINT32 p_comp_no,
-                                        OPJ_BYTE * p_data,
-                                        OPJ_UINT32 * p_data_written,
-                                        opj_event_mgr_t * p_manager);
-
-/**
- * Gets the maximum size taken by a qcc.
- */
-static OPJ_UINT32 opj_j2k_get_max_qcc_size(opj_j2k_t *p_j2k);
-
-/**
- * Reads a QCC marker (Quantization component)
- * @param       p_header_data   the data contained in the QCC box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the QCC marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_qcc(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-/**
- * Writes the POC marker (Progression Order Change)
- *
- * @param       p_stream                                the stream to write data to.
- * @param       p_j2k                           J2K codec.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_poc(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-/**
- * Writes the POC marker (Progression Order Change)
- *
- * @param       p_j2k          J2K codec.
- * @param       p_data         FIXME DOC
- * @param       p_data_written the stream to write data to.
- * @param       p_manager      the user event manager.
- */
-static void opj_j2k_write_poc_in_memory(opj_j2k_t *p_j2k,
-                                        OPJ_BYTE * p_data,
-                                        OPJ_UINT32 * p_data_written,
-                                        opj_event_mgr_t * p_manager);
-/**
- * Gets the maximum size taken by the writing of a POC.
- */
-static OPJ_UINT32 opj_j2k_get_max_poc_size(opj_j2k_t *p_j2k);
-
-/**
- * Reads a POC marker (Progression Order Change)
- *
- * @param       p_header_data   the data contained in the POC box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the POC marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_poc(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-
-/**
- * Gets the maximum size taken by the toc headers of all the tile parts of any given tile.
- */
-static OPJ_UINT32 opj_j2k_get_max_toc_size(opj_j2k_t *p_j2k);
-
-/**
- * Gets the maximum size taken by the headers of the SOT.
- *
- * @param       p_j2k   the jpeg2000 codec to use.
- */
-static OPJ_UINT32 opj_j2k_get_specific_header_sizes(opj_j2k_t *p_j2k);
-
-/**
- * Reads a CRG marker (Component registration)
- *
- * @param       p_header_data   the data contained in the TLM box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the TLM marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_crg(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-/**
- * Reads a TLM marker (Tile Length Marker)
- *
- * @param       p_header_data   the data contained in the TLM box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the TLM marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_tlm(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-
-/**
- * Writes the updated tlm.
- *
- * @param       p_stream                the stream to write data to.
- * @param       p_j2k                   J2K codec.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_updated_tlm(opj_j2k_t *p_j2k,
-        opj_stream_private_t *p_stream,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Reads a PLM marker (Packet length, main header marker)
- *
- * @param       p_header_data   the data contained in the TLM box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the TLM marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_plm(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-/**
- * Reads a PLT marker (Packet length, tile-part header)
- *
- * @param       p_header_data   the data contained in the PLT box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the PLT marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_plt(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-
-/**
- * Reads a PPM marker (Packed headers, main header)
- *
- * @param       p_header_data   the data contained in the POC box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the POC marker.
- * @param       p_manager               the user event manager.
- */
-
-static OPJ_BOOL opj_j2k_read_ppm(
-    opj_j2k_t *p_j2k,
-    OPJ_BYTE * p_header_data,
-    OPJ_UINT32 p_header_size,
-    opj_event_mgr_t * p_manager);
-
-/**
- * Merges all PPM markers read (Packed headers, main header)
- *
- * @param       p_cp      main coding parameters.
- * @param       p_manager the user event manager.
- */
-static OPJ_BOOL opj_j2k_merge_ppm(opj_cp_t *p_cp, opj_event_mgr_t * p_manager);
-
-/**
- * Reads a PPT marker (Packed packet headers, tile-part header)
- *
- * @param       p_header_data   the data contained in the PPT box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the PPT marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_ppt(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-
-/**
- * Merges all PPT markers read (Packed headers, tile-part header)
- *
- * @param       p_tcp   the tile.
- * @param       p_manager               the user event manager.
- */
-static OPJ_BOOL opj_j2k_merge_ppt(opj_tcp_t *p_tcp,
-                                  opj_event_mgr_t * p_manager);
-
-
-/**
- * Writes the TLM marker (Tile Length Marker)
- *
- * @param       p_stream                                the stream to write data to.
- * @param       p_j2k                           J2K codec.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_tlm(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Writes the SOT marker (Start of tile-part)
- *
- * @param       p_j2k            J2K codec.
- * @param       p_data           Output buffer
- * @param       p_total_data_size Output buffer size
- * @param       p_data_written   Number of bytes written into stream
- * @param       p_stream         the stream to write data to.
- * @param       p_manager        the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_sot(opj_j2k_t *p_j2k,
-                                  OPJ_BYTE * p_data,
-                                  OPJ_UINT32 p_total_data_size,
-                                  OPJ_UINT32 * p_data_written,
-                                  const opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Reads values from a SOT marker (Start of tile-part)
- *
- * the j2k decoder state is not affected. No side effects, no checks except for p_header_size.
- *
- * @param       p_header_data   the data contained in the SOT marker.
- * @param       p_header_size   the size of the data contained in the SOT marker.
- * @param       p_tile_no       Isot.
- * @param       p_tot_len       Psot.
- * @param       p_current_part  TPsot.
- * @param       p_num_parts     TNsot.
- * @param       p_manager       the user event manager.
- */
-static OPJ_BOOL opj_j2k_get_sot_values(OPJ_BYTE *  p_header_data,
-                                       OPJ_UINT32  p_header_size,
-                                       OPJ_UINT32* p_tile_no,
-                                       OPJ_UINT32* p_tot_len,
-                                       OPJ_UINT32* p_current_part,
-                                       OPJ_UINT32* p_num_parts,
-                                       opj_event_mgr_t * p_manager);
-/**
- * Reads a SOT marker (Start of tile-part)
- *
- * @param       p_header_data   the data contained in the SOT marker.
- * @param       p_j2k           the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the PPT marker.
- * @param       p_manager       the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_sot(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-/**
- * Writes the SOD marker (Start of data)
- *
- * @param       p_j2k               J2K codec.
- * @param       p_tile_coder        FIXME DOC
- * @param       p_data              FIXME DOC
- * @param       p_data_written      FIXME DOC
- * @param       p_total_data_size   FIXME DOC
- * @param       p_stream            the stream to write data to.
- * @param       p_manager           the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_sod(opj_j2k_t *p_j2k,
-                                  opj_tcd_t * p_tile_coder,
-                                  OPJ_BYTE * p_data,
-                                  OPJ_UINT32 * p_data_written,
-                                  OPJ_UINT32 p_total_data_size,
-                                  const opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Reads a SOD marker (Start Of Data)
- *
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_stream                FIXME DOC
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_sod(opj_j2k_t *p_j2k,
-                                 opj_stream_private_t *p_stream,
-                                 opj_event_mgr_t * p_manager);
-
-static void opj_j2k_update_tlm(opj_j2k_t * p_j2k, OPJ_UINT32 p_tile_part_size)
-{
-    opj_write_bytes(p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current,
-                    p_j2k->m_current_tile_number, 1);           /* PSOT */
-    ++p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current;
-
-    opj_write_bytes(p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current,
-                    p_tile_part_size, 4);                                       /* PSOT */
-    p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current += 4;
-}
-
-/**
- * Writes the RGN marker (Region Of Interest)
- *
- * @param       p_tile_no               the tile to output
- * @param       p_comp_no               the component to output
- * @param       nb_comps                the number of components
- * @param       p_stream                the stream to write data to.
- * @param       p_j2k                   J2K codec.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_rgn(opj_j2k_t *p_j2k,
-                                  OPJ_UINT32 p_tile_no,
-                                  OPJ_UINT32 p_comp_no,
-                                  OPJ_UINT32 nb_comps,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Reads a RGN marker (Region Of Interest)
- *
- * @param       p_header_data   the data contained in the POC box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the POC marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_rgn(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-
-/**
- * Writes the EOC marker (End of Codestream)
- *
- * @param       p_stream                the stream to write data to.
- * @param       p_j2k                   J2K codec.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_eoc(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-#if 0
-/**
- * Reads a EOC marker (End Of Codestream)
- *
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_stream                FIXME DOC
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_eoc(opj_j2k_t *p_j2k,
-                                 opj_stream_private_t *p_stream,
-                                 opj_event_mgr_t * p_manager);
-#endif
-
-/**
- * Writes the CBD-MCT-MCC-MCO markers (Multi components transform)
- *
- * @param       p_stream                        the stream to write data to.
- * @param       p_j2k                   J2K codec.
- * @param       p_manager       the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_mct_data_group(opj_j2k_t *p_j2k,
-        opj_stream_private_t *p_stream,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Inits the Info
- *
- * @param       p_stream                the stream to write data to.
- * @param       p_j2k                   J2K codec.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_init_info(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-/**
-Add main header marker information
-@param cstr_index    Codestream information structure
-@param type         marker type
-@param pos          byte offset of marker segment
-@param len          length of marker segment
- */
-static OPJ_BOOL opj_j2k_add_mhmarker(opj_codestream_index_t *cstr_index,
-                                     OPJ_UINT32 type, OPJ_OFF_T pos, OPJ_UINT32 len) ;
-/**
-Add tile header marker information
-@param tileno       tile index number
-@param cstr_index   Codestream information structure
-@param type         marker type
-@param pos          byte offset of marker segment
-@param len          length of marker segment
- */
-static OPJ_BOOL opj_j2k_add_tlmarker(OPJ_UINT32 tileno,
-                                     opj_codestream_index_t *cstr_index, OPJ_UINT32 type, OPJ_OFF_T pos,
-                                     OPJ_UINT32 len);
-
-/**
- * Reads an unknown marker
- *
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_stream                the stream object to read from.
- * @param       output_marker           FIXME DOC
- * @param       p_manager               the user event manager.
- *
- * @return      true                    if the marker could be deduced.
-*/
-static OPJ_BOOL opj_j2k_read_unk(opj_j2k_t *p_j2k,
-                                 opj_stream_private_t *p_stream,
-                                 OPJ_UINT32 *output_marker,
-                                 opj_event_mgr_t * p_manager);
-
-/**
- * Writes the MCT marker (Multiple Component Transform)
- *
- * @param       p_j2k           J2K codec.
- * @param       p_mct_record    FIXME DOC
- * @param       p_stream        the stream to write data to.
- * @param       p_manager       the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_mct_record(opj_j2k_t *p_j2k,
-        opj_mct_data_t * p_mct_record,
-        opj_stream_private_t *p_stream,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Reads a MCT marker (Multiple Component Transform)
- *
- * @param       p_header_data   the data contained in the MCT box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the MCT marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_mct(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-
-/**
- * Writes the MCC marker (Multiple Component Collection)
- *
- * @param       p_j2k                   J2K codec.
- * @param       p_mcc_record            FIXME DOC
- * @param       p_stream                the stream to write data to.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_mcc_record(opj_j2k_t *p_j2k,
-        opj_simple_mcc_decorrelation_data_t * p_mcc_record,
-        opj_stream_private_t *p_stream,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Reads a MCC marker (Multiple Component Collection)
- *
- * @param       p_header_data   the data contained in the MCC box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the MCC marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_mcc(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-
-/**
- * Writes the MCO marker (Multiple component transformation ordering)
- *
- * @param       p_stream                                the stream to write data to.
- * @param       p_j2k                           J2K codec.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_mco(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Reads a MCO marker (Multiple Component Transform Ordering)
- *
- * @param       p_header_data   the data contained in the MCO box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the MCO marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_mco(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-
-static OPJ_BOOL opj_j2k_add_mct(opj_tcp_t * p_tcp, opj_image_t * p_image,
-                                OPJ_UINT32 p_index);
-
-static void  opj_j2k_read_int16_to_float(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem);
-static void  opj_j2k_read_int32_to_float(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem);
-static void  opj_j2k_read_float32_to_float(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem);
-static void  opj_j2k_read_float64_to_float(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem);
-
-static void  opj_j2k_read_int16_to_int32(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem);
-static void  opj_j2k_read_int32_to_int32(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem);
-static void  opj_j2k_read_float32_to_int32(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem);
-static void  opj_j2k_read_float64_to_int32(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem);
-
-static void  opj_j2k_write_float_to_int16(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem);
-static void  opj_j2k_write_float_to_int32(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem);
-static void  opj_j2k_write_float_to_float(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem);
-static void  opj_j2k_write_float_to_float64(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem);
-
-/**
- * Ends the encoding, i.e. frees memory.
- *
- * @param       p_stream                the stream to write data to.
- * @param       p_j2k                   J2K codec.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_end_encoding(opj_j2k_t *p_j2k,
-                                     opj_stream_private_t *p_stream,
-                                     opj_event_mgr_t * p_manager);
-
-/**
- * Writes the CBD marker (Component bit depth definition)
- *
- * @param       p_stream                                the stream to write data to.
- * @param       p_j2k                           J2K codec.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_cbd(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Reads a CBD marker (Component bit depth definition)
- * @param       p_header_data   the data contained in the CBD box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the CBD marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_cbd(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager);
-
-
-/**
- * Writes COC marker for each component.
- *
- * @param       p_stream                the stream to write data to.
- * @param       p_j2k                   J2K codec.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_all_coc(opj_j2k_t *p_j2k,
-                                      opj_stream_private_t *p_stream,
-                                      opj_event_mgr_t * p_manager);
-
-/**
- * Writes QCC marker for each component.
- *
- * @param       p_stream                the stream to write data to.
- * @param       p_j2k                   J2K codec.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_all_qcc(opj_j2k_t *p_j2k,
-                                      opj_stream_private_t *p_stream,
-                                      opj_event_mgr_t * p_manager);
-
-/**
- * Writes regions of interests.
- *
- * @param       p_stream                the stream to write data to.
- * @param       p_j2k                   J2K codec.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_regions(opj_j2k_t *p_j2k,
-                                      opj_stream_private_t *p_stream,
-                                      opj_event_mgr_t * p_manager);
-
-/**
- * Writes EPC ????
- *
- * @param       p_stream                the stream to write data to.
- * @param       p_j2k                   J2K codec.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_write_epc(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Checks the progression order changes values. Tells of the poc given as input are valid.
- * A nice message is outputted at errors.
- *
- * @param       p_pocs                  the progression order changes.
- * @param       p_nb_pocs               the number of progression order changes.
- * @param       p_nb_resolutions        the number of resolutions.
- * @param       numcomps                the number of components
- * @param       numlayers               the number of layers.
- * @param       p_manager               the user event manager.
- *
- * @return      true if the pocs are valid.
- */
-static OPJ_BOOL opj_j2k_check_poc_val(const opj_poc_t *p_pocs,
-                                      OPJ_UINT32 p_nb_pocs,
-                                      OPJ_UINT32 p_nb_resolutions,
-                                      OPJ_UINT32 numcomps,
-                                      OPJ_UINT32 numlayers,
-                                      opj_event_mgr_t * p_manager);
-
-/**
- * Gets the number of tile parts used for the given change of progression (if any) and the given tile.
- *
- * @param               cp                      the coding parameters.
- * @param               pino            the offset of the given poc (i.e. its position in the coding parameter).
- * @param               tileno          the given tile.
- *
- * @return              the number of tile parts.
- */
-static OPJ_UINT32 opj_j2k_get_num_tp(opj_cp_t *cp, OPJ_UINT32 pino,
-                                     OPJ_UINT32 tileno);
-
-/**
- * Calculates the total number of tile parts needed by the encoder to
- * encode such an image. If not enough memory is available, then the function return false.
- *
- * @param       p_nb_tiles      pointer that will hold the number of tile parts.
- * @param       cp                      the coding parameters for the image.
- * @param       image           the image to encode.
- * @param       p_j2k                   the p_j2k encoder.
- * @param       p_manager       the user event manager.
- *
- * @return true if the function was successful, false else.
- */
-static OPJ_BOOL opj_j2k_calculate_tp(opj_j2k_t *p_j2k,
-                                     opj_cp_t *cp,
-                                     OPJ_UINT32 * p_nb_tiles,
-                                     opj_image_t *image,
-                                     opj_event_mgr_t * p_manager);
-
-static void opj_j2k_dump_MH_info(opj_j2k_t* p_j2k, FILE* out_stream);
-
-static void opj_j2k_dump_MH_index(opj_j2k_t* p_j2k, FILE* out_stream);
-
-static opj_codestream_index_t* opj_j2k_create_cstr_index(void);
-
-static OPJ_FLOAT32 opj_j2k_get_tp_stride(opj_tcp_t * p_tcp);
-
-static OPJ_FLOAT32 opj_j2k_get_default_stride(opj_tcp_t * p_tcp);
-
-static int opj_j2k_initialise_4K_poc(opj_poc_t *POC, int numres);
-
-static void opj_j2k_set_cinema_parameters(opj_cparameters_t *parameters,
-        opj_image_t *image, opj_event_mgr_t *p_manager);
-
-static OPJ_BOOL opj_j2k_is_cinema_compliant(opj_image_t *image, OPJ_UINT16 rsiz,
-        opj_event_mgr_t *p_manager);
-
-/**
- * Checks for invalid number of tile-parts in SOT marker (TPsot==TNsot). See issue 254.
- *
- * @param       p_stream            the stream to read data from.
- * @param       tile_no             tile number we're looking for.
- * @param       p_correction_needed output value. if true, non conformant codestream needs TNsot correction.
- * @param       p_manager       the user event manager.
- *
- * @return true if the function was successful, false else.
- */
-static OPJ_BOOL opj_j2k_need_nb_tile_parts_correction(opj_stream_private_t
-        *p_stream, OPJ_UINT32 tile_no, OPJ_BOOL* p_correction_needed,
-        opj_event_mgr_t * p_manager);
-
-/*@}*/
-
-/*@}*/
-
-/* ----------------------------------------------------------------------- */
-typedef struct j2k_prog_order {
-    OPJ_PROG_ORDER enum_prog;
-    char str_prog[5];
-} j2k_prog_order_t;
-
-static const j2k_prog_order_t j2k_prog_order_list[] = {
-    {OPJ_CPRL, "CPRL"},
-    {OPJ_LRCP, "LRCP"},
-    {OPJ_PCRL, "PCRL"},
-    {OPJ_RLCP, "RLCP"},
-    {OPJ_RPCL, "RPCL"},
-    {(OPJ_PROG_ORDER) - 1, ""}
-};
-
-/**
- * FIXME DOC
- */
-static const OPJ_UINT32 MCT_ELEMENT_SIZE [] = {
-    2,
-    4,
-    4,
-    8
-};
-
-typedef void (* opj_j2k_mct_function)(const void * p_src_data,
-                                      void * p_dest_data, OPJ_UINT32 p_nb_elem);
-
-static const opj_j2k_mct_function j2k_mct_read_functions_to_float [] = {
-    opj_j2k_read_int16_to_float,
-    opj_j2k_read_int32_to_float,
-    opj_j2k_read_float32_to_float,
-    opj_j2k_read_float64_to_float
-};
-
-static const opj_j2k_mct_function j2k_mct_read_functions_to_int32 [] = {
-    opj_j2k_read_int16_to_int32,
-    opj_j2k_read_int32_to_int32,
-    opj_j2k_read_float32_to_int32,
-    opj_j2k_read_float64_to_int32
-};
-
-static const opj_j2k_mct_function j2k_mct_write_functions_from_float [] = {
-    opj_j2k_write_float_to_int16,
-    opj_j2k_write_float_to_int32,
-    opj_j2k_write_float_to_float,
-    opj_j2k_write_float_to_float64
-};
-
-typedef struct opj_dec_memory_marker_handler {
-    /** marker value */
-    OPJ_UINT32 id;
-    /** value of the state when the marker can appear */
-    OPJ_UINT32 states;
-    /** action linked to the marker */
-    OPJ_BOOL(*handler)(opj_j2k_t *p_j2k,
-                       OPJ_BYTE * p_header_data,
-                       OPJ_UINT32 p_header_size,
-                       opj_event_mgr_t * p_manager);
-}
-opj_dec_memory_marker_handler_t;
-
-static const opj_dec_memory_marker_handler_t j2k_memory_marker_handler_tab [] =
-{
-    {J2K_MS_SOT, J2K_STATE_MH | J2K_STATE_TPHSOT, opj_j2k_read_sot},
-    {J2K_MS_COD, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_cod},
-    {J2K_MS_COC, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_coc},
-    {J2K_MS_RGN, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_rgn},
-    {J2K_MS_QCD, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_qcd},
-    {J2K_MS_QCC, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_qcc},
-    {J2K_MS_POC, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_poc},
-    {J2K_MS_SIZ, J2K_STATE_MHSIZ, opj_j2k_read_siz},
-    {J2K_MS_TLM, J2K_STATE_MH, opj_j2k_read_tlm},
-    {J2K_MS_PLM, J2K_STATE_MH, opj_j2k_read_plm},
-    {J2K_MS_PLT, J2K_STATE_TPH, opj_j2k_read_plt},
-    {J2K_MS_PPM, J2K_STATE_MH, opj_j2k_read_ppm},
-    {J2K_MS_PPT, J2K_STATE_TPH, opj_j2k_read_ppt},
-    {J2K_MS_SOP, 0, 0},
-    {J2K_MS_CRG, J2K_STATE_MH, opj_j2k_read_crg},
-    {J2K_MS_COM, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_com},
-    {J2K_MS_MCT, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_mct},
-    {J2K_MS_CBD, J2K_STATE_MH, opj_j2k_read_cbd},
-    {J2K_MS_MCC, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_mcc},
-    {J2K_MS_MCO, J2K_STATE_MH | J2K_STATE_TPH, opj_j2k_read_mco},
-#ifdef USE_JPWL
-#ifdef TODO_MS /* remove these functions which are not commpatible with the v2 API */
-    {J2K_MS_EPC, J2K_STATE_MH | J2K_STATE_TPH, j2k_read_epc},
-    {J2K_MS_EPB, J2K_STATE_MH | J2K_STATE_TPH, j2k_read_epb},
-    {J2K_MS_ESD, J2K_STATE_MH | J2K_STATE_TPH, j2k_read_esd},
-    {J2K_MS_RED, J2K_STATE_MH | J2K_STATE_TPH, j2k_read_red},
-#endif
-#endif /* USE_JPWL */
-#ifdef USE_JPSEC
-    {J2K_MS_SEC, J2K_DEC_STATE_MH, j2k_read_sec},
-    {J2K_MS_INSEC, 0, j2k_read_insec}
-#endif /* USE_JPSEC */
-    {J2K_MS_UNK, J2K_STATE_MH | J2K_STATE_TPH, 0}/*opj_j2k_read_unk is directly used*/
-};
-
-static void  opj_j2k_read_int16_to_float(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem)
-{
-    OPJ_BYTE * l_src_data = (OPJ_BYTE *) p_src_data;
-    OPJ_FLOAT32 * l_dest_data = (OPJ_FLOAT32 *) p_dest_data;
-    OPJ_UINT32 i;
-    OPJ_UINT32 l_temp;
-
-    for (i = 0; i < p_nb_elem; ++i) {
-        opj_read_bytes(l_src_data, &l_temp, 2);
-
-        l_src_data += sizeof(OPJ_INT16);
-
-        *(l_dest_data++) = (OPJ_FLOAT32) l_temp;
-    }
-}
-
-static void  opj_j2k_read_int32_to_float(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem)
-{
-    OPJ_BYTE * l_src_data = (OPJ_BYTE *) p_src_data;
-    OPJ_FLOAT32 * l_dest_data = (OPJ_FLOAT32 *) p_dest_data;
-    OPJ_UINT32 i;
-    OPJ_UINT32 l_temp;
-
-    for (i = 0; i < p_nb_elem; ++i) {
-        opj_read_bytes(l_src_data, &l_temp, 4);
-
-        l_src_data += sizeof(OPJ_INT32);
-
-        *(l_dest_data++) = (OPJ_FLOAT32) l_temp;
-    }
-}
-
-static void  opj_j2k_read_float32_to_float(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem)
-{
-    OPJ_BYTE * l_src_data = (OPJ_BYTE *) p_src_data;
-    OPJ_FLOAT32 * l_dest_data = (OPJ_FLOAT32 *) p_dest_data;
-    OPJ_UINT32 i;
-    OPJ_FLOAT32 l_temp;
-
-    for (i = 0; i < p_nb_elem; ++i) {
-        opj_read_float(l_src_data, &l_temp);
-
-        l_src_data += sizeof(OPJ_FLOAT32);
-
-        *(l_dest_data++) = l_temp;
-    }
-}
-
-static void  opj_j2k_read_float64_to_float(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem)
-{
-    OPJ_BYTE * l_src_data = (OPJ_BYTE *) p_src_data;
-    OPJ_FLOAT32 * l_dest_data = (OPJ_FLOAT32 *) p_dest_data;
-    OPJ_UINT32 i;
-    OPJ_FLOAT64 l_temp;
-
-    for (i = 0; i < p_nb_elem; ++i) {
-        opj_read_double(l_src_data, &l_temp);
-
-        l_src_data += sizeof(OPJ_FLOAT64);
-
-        *(l_dest_data++) = (OPJ_FLOAT32) l_temp;
-    }
-}
-
-static void  opj_j2k_read_int16_to_int32(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem)
-{
-    OPJ_BYTE * l_src_data = (OPJ_BYTE *) p_src_data;
-    OPJ_INT32 * l_dest_data = (OPJ_INT32 *) p_dest_data;
-    OPJ_UINT32 i;
-    OPJ_UINT32 l_temp;
-
-    for (i = 0; i < p_nb_elem; ++i) {
-        opj_read_bytes(l_src_data, &l_temp, 2);
-
-        l_src_data += sizeof(OPJ_INT16);
-
-        *(l_dest_data++) = (OPJ_INT32) l_temp;
-    }
-}
-
-static void  opj_j2k_read_int32_to_int32(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem)
-{
-    OPJ_BYTE * l_src_data = (OPJ_BYTE *) p_src_data;
-    OPJ_INT32 * l_dest_data = (OPJ_INT32 *) p_dest_data;
-    OPJ_UINT32 i;
-    OPJ_UINT32 l_temp;
-
-    for (i = 0; i < p_nb_elem; ++i) {
-        opj_read_bytes(l_src_data, &l_temp, 4);
-
-        l_src_data += sizeof(OPJ_INT32);
-
-        *(l_dest_data++) = (OPJ_INT32) l_temp;
-    }
-}
-
-static void  opj_j2k_read_float32_to_int32(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem)
-{
-    OPJ_BYTE * l_src_data = (OPJ_BYTE *) p_src_data;
-    OPJ_INT32 * l_dest_data = (OPJ_INT32 *) p_dest_data;
-    OPJ_UINT32 i;
-    OPJ_FLOAT32 l_temp;
-
-    for (i = 0; i < p_nb_elem; ++i) {
-        opj_read_float(l_src_data, &l_temp);
-
-        l_src_data += sizeof(OPJ_FLOAT32);
-
-        *(l_dest_data++) = (OPJ_INT32) l_temp;
-    }
-}
-
-static void  opj_j2k_read_float64_to_int32(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem)
-{
-    OPJ_BYTE * l_src_data = (OPJ_BYTE *) p_src_data;
-    OPJ_INT32 * l_dest_data = (OPJ_INT32 *) p_dest_data;
-    OPJ_UINT32 i;
-    OPJ_FLOAT64 l_temp;
-
-    for (i = 0; i < p_nb_elem; ++i) {
-        opj_read_double(l_src_data, &l_temp);
-
-        l_src_data += sizeof(OPJ_FLOAT64);
-
-        *(l_dest_data++) = (OPJ_INT32) l_temp;
-    }
-}
-
-static void  opj_j2k_write_float_to_int16(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem)
-{
-    OPJ_BYTE * l_dest_data = (OPJ_BYTE *) p_dest_data;
-    OPJ_FLOAT32 * l_src_data = (OPJ_FLOAT32 *) p_src_data;
-    OPJ_UINT32 i;
-    OPJ_UINT32 l_temp;
-
-    for (i = 0; i < p_nb_elem; ++i) {
-        l_temp = (OPJ_UINT32) * (l_src_data++);
-
-        opj_write_bytes(l_dest_data, l_temp, sizeof(OPJ_INT16));
-
-        l_dest_data += sizeof(OPJ_INT16);
-    }
-}
-
-static void opj_j2k_write_float_to_int32(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem)
-{
-    OPJ_BYTE * l_dest_data = (OPJ_BYTE *) p_dest_data;
-    OPJ_FLOAT32 * l_src_data = (OPJ_FLOAT32 *) p_src_data;
-    OPJ_UINT32 i;
-    OPJ_UINT32 l_temp;
-
-    for (i = 0; i < p_nb_elem; ++i) {
-        l_temp = (OPJ_UINT32) * (l_src_data++);
-
-        opj_write_bytes(l_dest_data, l_temp, sizeof(OPJ_INT32));
-
-        l_dest_data += sizeof(OPJ_INT32);
-    }
-}
-
-static void  opj_j2k_write_float_to_float(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem)
-{
-    OPJ_BYTE * l_dest_data = (OPJ_BYTE *) p_dest_data;
-    OPJ_FLOAT32 * l_src_data = (OPJ_FLOAT32 *) p_src_data;
-    OPJ_UINT32 i;
-    OPJ_FLOAT32 l_temp;
-
-    for (i = 0; i < p_nb_elem; ++i) {
-        l_temp = (OPJ_FLOAT32) * (l_src_data++);
-
-        opj_write_float(l_dest_data, l_temp);
-
-        l_dest_data += sizeof(OPJ_FLOAT32);
-    }
-}
-
-static void  opj_j2k_write_float_to_float64(const void * p_src_data,
-        void * p_dest_data, OPJ_UINT32 p_nb_elem)
-{
-    OPJ_BYTE * l_dest_data = (OPJ_BYTE *) p_dest_data;
-    OPJ_FLOAT32 * l_src_data = (OPJ_FLOAT32 *) p_src_data;
-    OPJ_UINT32 i;
-    OPJ_FLOAT64 l_temp;
-
-    for (i = 0; i < p_nb_elem; ++i) {
-        l_temp = (OPJ_FLOAT64) * (l_src_data++);
-
-        opj_write_double(l_dest_data, l_temp);
-
-        l_dest_data += sizeof(OPJ_FLOAT64);
-    }
-}
-
-const char *opj_j2k_convert_progression_order(OPJ_PROG_ORDER prg_order)
-{
-    const j2k_prog_order_t *po;
-    for (po = j2k_prog_order_list; po->enum_prog != -1; po++) {
-        if (po->enum_prog == prg_order) {
-            return po->str_prog;
-        }
-    }
-    return po->str_prog;
-}
-
-static OPJ_BOOL opj_j2k_check_poc_val(const opj_poc_t *p_pocs,
-                                      OPJ_UINT32 p_nb_pocs,
-                                      OPJ_UINT32 p_nb_resolutions,
-                                      OPJ_UINT32 p_num_comps,
-                                      OPJ_UINT32 p_num_layers,
-                                      opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32* packet_array;
-    OPJ_UINT32 index, resno, compno, layno;
-    OPJ_UINT32 i;
-    OPJ_UINT32 step_c = 1;
-    OPJ_UINT32 step_r = p_num_comps * step_c;
-    OPJ_UINT32 step_l = p_nb_resolutions * step_r;
-    OPJ_BOOL loss = OPJ_FALSE;
-    OPJ_UINT32 layno0 = 0;
-
-    packet_array = (OPJ_UINT32*) opj_calloc(step_l * p_num_layers,
-                                            sizeof(OPJ_UINT32));
-    if (packet_array == 00) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Not enough memory for checking the poc values.\n");
-        return OPJ_FALSE;
-    }
-
-    if (p_nb_pocs == 0) {
-        opj_free(packet_array);
-        return OPJ_TRUE;
-    }
-
-    index = step_r * p_pocs->resno0;
-    /* take each resolution for each poc */
-    for (resno = p_pocs->resno0 ; resno < p_pocs->resno1 ; ++resno) {
-        OPJ_UINT32 res_index = index + p_pocs->compno0 * step_c;
-
-        /* take each comp of each resolution for each poc */
-        for (compno = p_pocs->compno0 ; compno < p_pocs->compno1 ; ++compno) {
-            OPJ_UINT32 comp_index = res_index + layno0 * step_l;
-
-            /* and finally take each layer of each res of ... */
-            for (layno = layno0; layno < p_pocs->layno1 ; ++layno) {
-                /*index = step_r * resno + step_c * compno + step_l * layno;*/
-                packet_array[comp_index] = 1;
-                comp_index += step_l;
-            }
-
-            res_index += step_c;
-        }
-
-        index += step_r;
-    }
-    ++p_pocs;
-
-    /* iterate through all the pocs */
-    for (i = 1; i < p_nb_pocs ; ++i) {
-        OPJ_UINT32 l_last_layno1 = (p_pocs - 1)->layno1 ;
-
-        layno0 = (p_pocs->layno1 > l_last_layno1) ? l_last_layno1 : 0;
-        index = step_r * p_pocs->resno0;
-
-        /* take each resolution for each poc */
-        for (resno = p_pocs->resno0 ; resno < p_pocs->resno1 ; ++resno) {
-            OPJ_UINT32 res_index = index + p_pocs->compno0 * step_c;
-
-            /* take each comp of each resolution for each poc */
-            for (compno = p_pocs->compno0 ; compno < p_pocs->compno1 ; ++compno) {
-                OPJ_UINT32 comp_index = res_index + layno0 * step_l;
-
-                /* and finally take each layer of each res of ... */
-                for (layno = layno0; layno < p_pocs->layno1 ; ++layno) {
-                    /*index = step_r * resno + step_c * compno + step_l * layno;*/
-                    packet_array[comp_index] = 1;
-                    comp_index += step_l;
-                }
-
-                res_index += step_c;
-            }
-
-            index += step_r;
-        }
-
-        ++p_pocs;
-    }
-
-    index = 0;
-    for (layno = 0; layno < p_num_layers ; ++layno) {
-        for (resno = 0; resno < p_nb_resolutions; ++resno) {
-            for (compno = 0; compno < p_num_comps; ++compno) {
-                loss |= (packet_array[index] != 1);
-                /*index = step_r * resno + step_c * compno + step_l * layno;*/
-                index += step_c;
-            }
-        }
-    }
-
-    if (loss) {
-        opj_event_msg(p_manager, EVT_ERROR, "Missing packets possible loss of data\n");
-    }
-
-    opj_free(packet_array);
-
-    return !loss;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static OPJ_UINT32 opj_j2k_get_num_tp(opj_cp_t *cp, OPJ_UINT32 pino,
-                                     OPJ_UINT32 tileno)
-{
-    const OPJ_CHAR *prog = 00;
-    OPJ_INT32 i;
-    OPJ_UINT32 tpnum = 1;
-    opj_tcp_t *tcp = 00;
-    opj_poc_t * l_current_poc = 00;
-
-    /*  preconditions */
-    assert(tileno < (cp->tw * cp->th));
-    assert(pino < (cp->tcps[tileno].numpocs + 1));
-
-    /* get the given tile coding parameter */
-    tcp = &cp->tcps[tileno];
-    assert(tcp != 00);
-
-    l_current_poc = &(tcp->pocs[pino]);
-    assert(l_current_poc != 0);
-
-    /* get the progression order as a character string */
-    prog = opj_j2k_convert_progression_order(tcp->prg);
-    assert(strlen(prog) > 0);
-
-    if (cp->m_specific_param.m_enc.m_tp_on == 1) {
-        for (i = 0; i < 4; ++i) {
-            switch (prog[i]) {
-            /* component wise */
-            case 'C':
-                tpnum *= l_current_poc->compE;
-                break;
-            /* resolution wise */
-            case 'R':
-                tpnum *= l_current_poc->resE;
-                break;
-            /* precinct wise */
-            case 'P':
-                tpnum *= l_current_poc->prcE;
-                break;
-            /* layer wise */
-            case 'L':
-                tpnum *= l_current_poc->layE;
-                break;
-            }
-            /* whould we split here ? */
-            if (cp->m_specific_param.m_enc.m_tp_flag == prog[i]) {
-                cp->m_specific_param.m_enc.m_tp_pos = i;
-                break;
-            }
-        }
-    } else {
-        tpnum = 1;
-    }
-
-    return tpnum;
-}
-
-static OPJ_BOOL opj_j2k_calculate_tp(opj_j2k_t *p_j2k,
-                                     opj_cp_t *cp,
-                                     OPJ_UINT32 * p_nb_tiles,
-                                     opj_image_t *image,
-                                     opj_event_mgr_t * p_manager
-                                    )
-{
-    OPJ_UINT32 pino, tileno;
-    OPJ_UINT32 l_nb_tiles;
-    opj_tcp_t *tcp;
-
-    /* preconditions */
-    assert(p_nb_tiles != 00);
-    assert(cp != 00);
-    assert(image != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    OPJ_UNUSED(p_j2k);
-    OPJ_UNUSED(p_manager);
-
-    l_nb_tiles = cp->tw * cp->th;
-    * p_nb_tiles = 0;
-    tcp = cp->tcps;
-
-    /* INDEX >> */
-    /* TODO mergeV2: check this part which use cstr_info */
-    /*if (p_j2k->cstr_info) {
-            opj_tile_info_t * l_info_tile_ptr = p_j2k->cstr_info->tile;
-
-            for (tileno = 0; tileno < l_nb_tiles; ++tileno) {
-                    OPJ_UINT32 cur_totnum_tp = 0;
-
-                    opj_pi_update_encoding_parameters(image,cp,tileno);
-
-                    for (pino = 0; pino <= tcp->numpocs; ++pino)
-                    {
-                            OPJ_UINT32 tp_num = opj_j2k_get_num_tp(cp,pino,tileno);
-
-                            *p_nb_tiles = *p_nb_tiles + tp_num;
-
-                            cur_totnum_tp += tp_num;
-                    }
-
-                    tcp->m_nb_tile_parts = cur_totnum_tp;
-
-                    l_info_tile_ptr->tp = (opj_tp_info_t *) opj_malloc(cur_totnum_tp * sizeof(opj_tp_info_t));
-                    if (l_info_tile_ptr->tp == 00) {
-                            return OPJ_FALSE;
-                    }
-
-                    memset(l_info_tile_ptr->tp,0,cur_totnum_tp * sizeof(opj_tp_info_t));
-
-                    l_info_tile_ptr->num_tps = cur_totnum_tp;
-
-                    ++l_info_tile_ptr;
-                    ++tcp;
-            }
-    }
-    else */{
-        for (tileno = 0; tileno < l_nb_tiles; ++tileno) {
-            OPJ_UINT32 cur_totnum_tp = 0;
-
-            opj_pi_update_encoding_parameters(image, cp, tileno);
-
-            for (pino = 0; pino <= tcp->numpocs; ++pino) {
-                OPJ_UINT32 tp_num = opj_j2k_get_num_tp(cp, pino, tileno);
-
-                *p_nb_tiles = *p_nb_tiles + tp_num;
-
-                cur_totnum_tp += tp_num;
-            }
-            tcp->m_nb_tile_parts = cur_totnum_tp;
-
-            ++tcp;
-        }
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_soc(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager)
-{
-    /* 2 bytes will be written */
-    OPJ_BYTE * l_start_stream = 00;
-
-    /* preconditions */
-    assert(p_stream != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    l_start_stream = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
-
-    /* write SOC identifier */
-    opj_write_bytes(l_start_stream, J2K_MS_SOC, 2);
-
-    if (opj_stream_write_data(p_stream, l_start_stream, 2, p_manager) != 2) {
-        return OPJ_FALSE;
-    }
-
-    /* UniPG>> */
-#ifdef USE_JPWL
-    /* update markers struct */
-    /*
-            OPJ_BOOL res = j2k_add_marker(p_j2k->cstr_info, J2K_MS_SOC, p_stream_tell(p_stream) - 2, 2);
-    */
-    assert(0 && "TODO");
-#endif /* USE_JPWL */
-    /* <<UniPG */
-
-    return OPJ_TRUE;
-}
-
-/**
- * Reads a SOC marker (Start of Codestream)
- * @param       p_j2k           the jpeg2000 file codec.
- * @param       p_stream        FIXME DOC
- * @param       p_manager       the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_soc(opj_j2k_t *p_j2k,
-                                 opj_stream_private_t *p_stream,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    OPJ_BYTE l_data [2];
-    OPJ_UINT32 l_marker;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    if (opj_stream_read_data(p_stream, l_data, 2, p_manager) != 2) {
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(l_data, &l_marker, 2);
-    if (l_marker != J2K_MS_SOC) {
-        return OPJ_FALSE;
-    }
-
-    /* Next marker should be a SIZ marker in the main header */
-    p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_MHSIZ;
-
-    /* FIXME move it in a index structure included in p_j2k*/
-    p_j2k->cstr_index->main_head_start = opj_stream_tell(p_stream) - 2;
-
-    opj_event_msg(p_manager, EVT_INFO,
-                  "Start to read j2k main header (%" PRId64 ").\n",
-                  p_j2k->cstr_index->main_head_start);
-
-    /* Add the marker to the codestream index*/
-    if (OPJ_FALSE == opj_j2k_add_mhmarker(p_j2k->cstr_index, J2K_MS_SOC,
-                                          p_j2k->cstr_index->main_head_start, 2)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to add mh marker\n");
-        return OPJ_FALSE;
-    }
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_siz(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32 i;
-    OPJ_UINT32 l_size_len;
-    OPJ_BYTE * l_current_ptr;
-    opj_image_t * l_image = 00;
-    opj_cp_t *cp = 00;
-    opj_image_comp_t * l_img_comp = 00;
-
-    /* preconditions */
-    assert(p_stream != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    l_image = p_j2k->m_private_image;
-    cp = &(p_j2k->m_cp);
-    l_size_len = 40 + 3 * l_image->numcomps;
-    l_img_comp = l_image->comps;
-
-    if (l_size_len > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
-
-        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
-                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_size_len);
-        if (! new_header_tile_data) {
-            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory for the SIZ marker\n");
-            return OPJ_FALSE;
-        }
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_size_len;
-    }
-
-    l_current_ptr = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
-
-    /* write SOC identifier */
-    opj_write_bytes(l_current_ptr, J2K_MS_SIZ, 2);  /* SIZ */
-    l_current_ptr += 2;
-
-    opj_write_bytes(l_current_ptr, l_size_len - 2, 2); /* L_SIZ */
-    l_current_ptr += 2;
-
-    opj_write_bytes(l_current_ptr, cp->rsiz, 2);    /* Rsiz (capabilities) */
-    l_current_ptr += 2;
-
-    opj_write_bytes(l_current_ptr, l_image->x1, 4); /* Xsiz */
-    l_current_ptr += 4;
-
-    opj_write_bytes(l_current_ptr, l_image->y1, 4); /* Ysiz */
-    l_current_ptr += 4;
-
-    opj_write_bytes(l_current_ptr, l_image->x0, 4); /* X0siz */
-    l_current_ptr += 4;
-
-    opj_write_bytes(l_current_ptr, l_image->y0, 4); /* Y0siz */
-    l_current_ptr += 4;
-
-    opj_write_bytes(l_current_ptr, cp->tdx, 4);             /* XTsiz */
-    l_current_ptr += 4;
-
-    opj_write_bytes(l_current_ptr, cp->tdy, 4);             /* YTsiz */
-    l_current_ptr += 4;
-
-    opj_write_bytes(l_current_ptr, cp->tx0, 4);             /* XT0siz */
-    l_current_ptr += 4;
-
-    opj_write_bytes(l_current_ptr, cp->ty0, 4);             /* YT0siz */
-    l_current_ptr += 4;
-
-    opj_write_bytes(l_current_ptr, l_image->numcomps, 2);   /* Csiz */
-    l_current_ptr += 2;
-
-    for (i = 0; i < l_image->numcomps; ++i) {
-        /* TODO here with MCT ? */
-        opj_write_bytes(l_current_ptr, l_img_comp->prec - 1 + (l_img_comp->sgnd << 7),
-                        1);      /* Ssiz_i */
-        ++l_current_ptr;
-
-        opj_write_bytes(l_current_ptr, l_img_comp->dx, 1);      /* XRsiz_i */
-        ++l_current_ptr;
-
-        opj_write_bytes(l_current_ptr, l_img_comp->dy, 1);      /* YRsiz_i */
-        ++l_current_ptr;
-
-        ++l_img_comp;
-    }
-
-    if (opj_stream_write_data(p_stream,
-                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_size_len,
-                              p_manager) != l_size_len) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-/**
- * Reads a SIZ marker (image and tile size)
- * @param       p_j2k           the jpeg2000 file codec.
- * @param       p_header_data   the data contained in the SIZ box.
- * @param       p_header_size   the size of the data contained in the SIZ marker.
- * @param       p_manager       the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_siz(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    OPJ_UINT32 i;
-    OPJ_UINT32 l_nb_comp;
-    OPJ_UINT32 l_nb_comp_remain;
-    OPJ_UINT32 l_remaining_size;
-    OPJ_UINT32 l_nb_tiles;
-    OPJ_UINT32 l_tmp, l_tx1, l_ty1;
-    OPJ_UINT32 l_prec0, l_sgnd0;
-    opj_image_t *l_image = 00;
-    opj_cp_t *l_cp = 00;
-    opj_image_comp_t * l_img_comp = 00;
-    opj_tcp_t * l_current_tile_param = 00;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_header_data != 00);
-
-    l_image = p_j2k->m_private_image;
-    l_cp = &(p_j2k->m_cp);
-
-    /* minimum size == 39 - 3 (= minimum component parameter) */
-    if (p_header_size < 36) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error with SIZ marker size\n");
-        return OPJ_FALSE;
-    }
-
-    l_remaining_size = p_header_size - 36;
-    l_nb_comp = l_remaining_size / 3;
-    l_nb_comp_remain = l_remaining_size % 3;
-    if (l_nb_comp_remain != 0) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error with SIZ marker size\n");
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(p_header_data, &l_tmp,
-                   2);                                                /* Rsiz (capabilities) */
-    p_header_data += 2;
-    l_cp->rsiz = (OPJ_UINT16) l_tmp;
-    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_image->x1, 4);   /* Xsiz */
-    p_header_data += 4;
-    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_image->y1, 4);   /* Ysiz */
-    p_header_data += 4;
-    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_image->x0, 4);   /* X0siz */
-    p_header_data += 4;
-    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_image->y0, 4);   /* Y0siz */
-    p_header_data += 4;
-    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_cp->tdx,
-                   4);             /* XTsiz */
-    p_header_data += 4;
-    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_cp->tdy,
-                   4);             /* YTsiz */
-    p_header_data += 4;
-    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_cp->tx0,
-                   4);             /* XT0siz */
-    p_header_data += 4;
-    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_cp->ty0,
-                   4);             /* YT0siz */
-    p_header_data += 4;
-    opj_read_bytes(p_header_data, (OPJ_UINT32*) &l_tmp,
-                   2);                 /* Csiz */
-    p_header_data += 2;
-    if (l_tmp < 16385) {
-        l_image->numcomps = (OPJ_UINT16) l_tmp;
-    } else {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Error with SIZ marker: number of component is illegal -> %d\n", l_tmp);
-        return OPJ_FALSE;
-    }
-
-    if (l_image->numcomps != l_nb_comp) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Error with SIZ marker: number of component is not compatible with the remaining number of parameters ( %d vs %d)\n",
-                      l_image->numcomps, l_nb_comp);
-        return OPJ_FALSE;
-    }
-
-    /* testcase 4035.pdf.SIGSEGV.d8b.3375 */
-    /* testcase issue427-null-image-size.jp2 */
-    if ((l_image->x0 >= l_image->x1) || (l_image->y0 >= l_image->y1)) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Error with SIZ marker: negative or zero image size (%" PRId64 " x %" PRId64
-                      ")\n", (OPJ_INT64)l_image->x1 - l_image->x0,
-                      (OPJ_INT64)l_image->y1 - l_image->y0);
-        return OPJ_FALSE;
-    }
-    /* testcase 2539.pdf.SIGFPE.706.1712 (also 3622.pdf.SIGFPE.706.2916 and 4008.pdf.SIGFPE.706.3345 and maybe more) */
-    if ((l_cp->tdx == 0U) || (l_cp->tdy == 0U)) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Error with SIZ marker: invalid tile size (tdx: %d, tdy: %d)\n", l_cp->tdx,
-                      l_cp->tdy);
-        return OPJ_FALSE;
-    }
-
-    /* testcase issue427-illegal-tile-offset.jp2 */
-    l_tx1 = opj_uint_adds(l_cp->tx0, l_cp->tdx); /* manage overflow */
-    l_ty1 = opj_uint_adds(l_cp->ty0, l_cp->tdy); /* manage overflow */
-    if ((l_cp->tx0 > l_image->x0) || (l_cp->ty0 > l_image->y0) ||
-            (l_tx1 <= l_image->x0) || (l_ty1 <= l_image->y0)) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Error with SIZ marker: illegal tile offset\n");
-        return OPJ_FALSE;
-    }
-    if (!p_j2k->dump_state) {
-        OPJ_UINT32 siz_w, siz_h;
-
-        siz_w = l_image->x1 - l_image->x0;
-        siz_h = l_image->y1 - l_image->y0;
-
-        if (p_j2k->ihdr_w > 0 && p_j2k->ihdr_h > 0
-                && (p_j2k->ihdr_w != siz_w || p_j2k->ihdr_h != siz_h)) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Error with SIZ marker: IHDR w(%u) h(%u) vs. SIZ w(%u) h(%u)\n", p_j2k->ihdr_w,
-                          p_j2k->ihdr_h, siz_w, siz_h);
-            return OPJ_FALSE;
-        }
-    }
-#ifdef USE_JPWL
-    if (l_cp->correct) {
-        /* if JPWL is on, we check whether TX errors have damaged
-          too much the SIZ parameters */
-        if (!(l_image->x1 * l_image->y1)) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "JPWL: bad image size (%d x %d)\n",
-                          l_image->x1, l_image->y1);
-            if (!JPWL_ASSUME) {
-                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
-                return OPJ_FALSE;
-            }
-        }
-
-        /* FIXME check previously in the function so why keep this piece of code ? Need by the norm ?
-                if (l_image->numcomps != ((len - 38) / 3)) {
-                        opj_event_msg(p_manager, JPWL_ASSUME ? EVT_WARNING : EVT_ERROR,
-                                "JPWL: Csiz is %d => space in SIZ only for %d comps.!!!\n",
-                                l_image->numcomps, ((len - 38) / 3));
-                        if (!JPWL_ASSUME) {
-                                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
-                                return OPJ_FALSE;
-                        }
-        */              /* we try to correct */
-        /*              opj_event_msg(p_manager, EVT_WARNING, "- trying to adjust this\n");
-                        if (l_image->numcomps < ((len - 38) / 3)) {
-                                len = 38 + 3 * l_image->numcomps;
-                                opj_event_msg(p_manager, EVT_WARNING, "- setting Lsiz to %d => HYPOTHESIS!!!\n",
-                                        len);
-                        } else {
-                                l_image->numcomps = ((len - 38) / 3);
-                                opj_event_msg(p_manager, EVT_WARNING, "- setting Csiz to %d => HYPOTHESIS!!!\n",
-                                        l_image->numcomps);
-                        }
-                }
-        */
-
-        /* update components number in the jpwl_exp_comps filed */
-        l_cp->exp_comps = l_image->numcomps;
-    }
-#endif /* USE_JPWL */
-
-    /* Allocate the resulting image components */
-    l_image->comps = (opj_image_comp_t*) opj_calloc(l_image->numcomps,
-                     sizeof(opj_image_comp_t));
-    if (l_image->comps == 00) {
-        l_image->numcomps = 0;
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Not enough memory to take in charge SIZ marker\n");
-        return OPJ_FALSE;
-    }
-
-    l_img_comp = l_image->comps;
-
-    l_prec0 = 0;
-    l_sgnd0 = 0;
-    /* Read the component information */
-    for (i = 0; i < l_image->numcomps; ++i) {
-        OPJ_UINT32 tmp;
-        opj_read_bytes(p_header_data, &tmp, 1); /* Ssiz_i */
-        ++p_header_data;
-        l_img_comp->prec = (tmp & 0x7f) + 1;
-        l_img_comp->sgnd = tmp >> 7;
-
-        if (p_j2k->dump_state == 0) {
-            if (i == 0) {
-                l_prec0 = l_img_comp->prec;
-                l_sgnd0 = l_img_comp->sgnd;
-            } else if (!l_cp->allow_different_bit_depth_sign
-                       && (l_img_comp->prec != l_prec0 || l_img_comp->sgnd != l_sgnd0)) {
-                opj_event_msg(p_manager, EVT_WARNING,
-                              "Despite JP2 BPC!=255, precision and/or sgnd values for comp[%d] is different than comp[0]:\n"
-                              "        [0] prec(%d) sgnd(%d) [%d] prec(%d) sgnd(%d)\n", i, l_prec0, l_sgnd0,
-                              i, l_img_comp->prec, l_img_comp->sgnd);
-            }
-            /* TODO: we should perhaps also check against JP2 BPCC values */
-        }
-        opj_read_bytes(p_header_data, &tmp, 1); /* XRsiz_i */
-        ++p_header_data;
-        l_img_comp->dx = (OPJ_UINT32)tmp; /* should be between 1 and 255 */
-        opj_read_bytes(p_header_data, &tmp, 1); /* YRsiz_i */
-        ++p_header_data;
-        l_img_comp->dy = (OPJ_UINT32)tmp; /* should be between 1 and 255 */
-        if (l_img_comp->dx < 1 || l_img_comp->dx > 255 ||
-                l_img_comp->dy < 1 || l_img_comp->dy > 255) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Invalid values for comp = %d : dx=%u dy=%u (should be between 1 and 255 according to the JPEG2000 norm)\n",
-                          i, l_img_comp->dx, l_img_comp->dy);
-            return OPJ_FALSE;
-        }
-        /* Avoids later undefined shift in computation of */
-        /* p_j2k->m_specific_param.m_decoder.m_default_tcp->tccps[i].m_dc_level_shift = 1
-                    << (l_image->comps[i].prec - 1); */
-        if (l_img_comp->prec > 31) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Invalid values for comp = %d : prec=%u (should be between 1 and 38 according to the JPEG2000 norm. OpenJpeg only supports up to 31)\n",
-                          i, l_img_comp->prec);
-            return OPJ_FALSE;
-        }
-#ifdef USE_JPWL
-        if (l_cp->correct) {
-            /* if JPWL is on, we check whether TX errors have damaged
-                    too much the SIZ parameters, again */
-            if (!(l_image->comps[i].dx * l_image->comps[i].dy)) {
-                opj_event_msg(p_manager, JPWL_ASSUME ? EVT_WARNING : EVT_ERROR,
-                              "JPWL: bad XRsiz_%d/YRsiz_%d (%d x %d)\n",
-                              i, i, l_image->comps[i].dx, l_image->comps[i].dy);
-                if (!JPWL_ASSUME) {
-                    opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
-                    return OPJ_FALSE;
-                }
-                /* we try to correct */
-                opj_event_msg(p_manager, EVT_WARNING, "- trying to adjust them\n");
-                if (!l_image->comps[i].dx) {
-                    l_image->comps[i].dx = 1;
-                    opj_event_msg(p_manager, EVT_WARNING,
-                                  "- setting XRsiz_%d to %d => HYPOTHESIS!!!\n",
-                                  i, l_image->comps[i].dx);
-                }
-                if (!l_image->comps[i].dy) {
-                    l_image->comps[i].dy = 1;
-                    opj_event_msg(p_manager, EVT_WARNING,
-                                  "- setting YRsiz_%d to %d => HYPOTHESIS!!!\n",
-                                  i, l_image->comps[i].dy);
-                }
-            }
-        }
-#endif /* USE_JPWL */
-        l_img_comp->resno_decoded =
-            0;                                                          /* number of resolution decoded */
-        l_img_comp->factor =
-            l_cp->m_specific_param.m_dec.m_reduce; /* reducing factor per component */
-        ++l_img_comp;
-    }
-
-    if (l_cp->tdx == 0 || l_cp->tdy == 0) {
-        return OPJ_FALSE;
-    }
-
-    /* Compute the number of tiles */
-    l_cp->tw = opj_uint_ceildiv(l_image->x1 - l_cp->tx0, l_cp->tdx);
-    l_cp->th = opj_uint_ceildiv(l_image->y1 - l_cp->ty0, l_cp->tdy);
-
-    /* Check that the number of tiles is valid */
-    if (l_cp->tw == 0 || l_cp->th == 0 || l_cp->tw > 65535 / l_cp->th) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Invalid number of tiles : %u x %u (maximum fixed by jpeg2000 norm is 65535 tiles)\n",
-                      l_cp->tw, l_cp->th);
-        return OPJ_FALSE;
-    }
-    l_nb_tiles = l_cp->tw * l_cp->th;
-
-    /* Define the tiles which will be decoded */
-    if (p_j2k->m_specific_param.m_decoder.m_discard_tiles) {
-        p_j2k->m_specific_param.m_decoder.m_start_tile_x =
-            (p_j2k->m_specific_param.m_decoder.m_start_tile_x - l_cp->tx0) / l_cp->tdx;
-        p_j2k->m_specific_param.m_decoder.m_start_tile_y =
-            (p_j2k->m_specific_param.m_decoder.m_start_tile_y - l_cp->ty0) / l_cp->tdy;
-        p_j2k->m_specific_param.m_decoder.m_end_tile_x = opj_uint_ceildiv(
-            p_j2k->m_specific_param.m_decoder.m_end_tile_x - l_cp->tx0, l_cp->tdx);
-        p_j2k->m_specific_param.m_decoder.m_end_tile_y = opj_uint_ceildiv(
-            p_j2k->m_specific_param.m_decoder.m_end_tile_y - l_cp->ty0, l_cp->tdy);
-    } else {
-        p_j2k->m_specific_param.m_decoder.m_start_tile_x = 0;
-        p_j2k->m_specific_param.m_decoder.m_start_tile_y = 0;
-        p_j2k->m_specific_param.m_decoder.m_end_tile_x = l_cp->tw;
-        p_j2k->m_specific_param.m_decoder.m_end_tile_y = l_cp->th;
-    }
-
-#ifdef USE_JPWL
-    if (l_cp->correct) {
-        /* if JPWL is on, we check whether TX errors have damaged
-          too much the SIZ parameters */
-        if ((l_cp->tw < 1) || (l_cp->th < 1) || (l_cp->tw > l_cp->max_tiles) ||
-                (l_cp->th > l_cp->max_tiles)) {
-            opj_event_msg(p_manager, JPWL_ASSUME ? EVT_WARNING : EVT_ERROR,
-                          "JPWL: bad number of tiles (%d x %d)\n",
-                          l_cp->tw, l_cp->th);
-            if (!JPWL_ASSUME) {
-                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
-                return OPJ_FALSE;
-            }
-            /* we try to correct */
-            opj_event_msg(p_manager, EVT_WARNING, "- trying to adjust them\n");
-            if (l_cp->tw < 1) {
-                l_cp->tw = 1;
-                opj_event_msg(p_manager, EVT_WARNING,
-                              "- setting %d tiles in x => HYPOTHESIS!!!\n",
-                              l_cp->tw);
-            }
-            if (l_cp->tw > l_cp->max_tiles) {
-                l_cp->tw = 1;
-                opj_event_msg(p_manager, EVT_WARNING,
-                              "- too large x, increase expectance of %d\n"
-                              "- setting %d tiles in x => HYPOTHESIS!!!\n",
-                              l_cp->max_tiles, l_cp->tw);
-            }
-            if (l_cp->th < 1) {
-                l_cp->th = 1;
-                opj_event_msg(p_manager, EVT_WARNING,
-                              "- setting %d tiles in y => HYPOTHESIS!!!\n",
-                              l_cp->th);
-            }
-            if (l_cp->th > l_cp->max_tiles) {
-                l_cp->th = 1;
-                opj_event_msg(p_manager, EVT_WARNING,
-                              "- too large y, increase expectance of %d to continue\n",
-                              "- setting %d tiles in y => HYPOTHESIS!!!\n",
-                              l_cp->max_tiles, l_cp->th);
-            }
-        }
-    }
-#endif /* USE_JPWL */
-
-    /* memory allocations */
-    l_cp->tcps = (opj_tcp_t*) opj_calloc(l_nb_tiles, sizeof(opj_tcp_t));
-    if (l_cp->tcps == 00) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Not enough memory to take in charge SIZ marker\n");
-        return OPJ_FALSE;
-    }
-
-#ifdef USE_JPWL
-    if (l_cp->correct) {
-        if (!l_cp->tcps) {
-            opj_event_msg(p_manager, JPWL_ASSUME ? EVT_WARNING : EVT_ERROR,
-                          "JPWL: could not alloc tcps field of cp\n");
-            if (!JPWL_ASSUME) {
-                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
-                return OPJ_FALSE;
-            }
-        }
-    }
-#endif /* USE_JPWL */
-
-    p_j2k->m_specific_param.m_decoder.m_default_tcp->tccps =
-        (opj_tccp_t*) opj_calloc(l_image->numcomps, sizeof(opj_tccp_t));
-    if (p_j2k->m_specific_param.m_decoder.m_default_tcp->tccps  == 00) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Not enough memory to take in charge SIZ marker\n");
-        return OPJ_FALSE;
-    }
-
-    p_j2k->m_specific_param.m_decoder.m_default_tcp->m_mct_records =
-        (opj_mct_data_t*)opj_calloc(OPJ_J2K_MCT_DEFAULT_NB_RECORDS,
-                                    sizeof(opj_mct_data_t));
-
-    if (! p_j2k->m_specific_param.m_decoder.m_default_tcp->m_mct_records) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Not enough memory to take in charge SIZ marker\n");
-        return OPJ_FALSE;
-    }
-    p_j2k->m_specific_param.m_decoder.m_default_tcp->m_nb_max_mct_records =
-        OPJ_J2K_MCT_DEFAULT_NB_RECORDS;
-
-    p_j2k->m_specific_param.m_decoder.m_default_tcp->m_mcc_records =
-        (opj_simple_mcc_decorrelation_data_t*)
-        opj_calloc(OPJ_J2K_MCC_DEFAULT_NB_RECORDS,
-                   sizeof(opj_simple_mcc_decorrelation_data_t));
-
-    if (! p_j2k->m_specific_param.m_decoder.m_default_tcp->m_mcc_records) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Not enough memory to take in charge SIZ marker\n");
-        return OPJ_FALSE;
-    }
-    p_j2k->m_specific_param.m_decoder.m_default_tcp->m_nb_max_mcc_records =
-        OPJ_J2K_MCC_DEFAULT_NB_RECORDS;
-
-    /* set up default dc level shift */
-    for (i = 0; i < l_image->numcomps; ++i) {
-        if (! l_image->comps[i].sgnd) {
-            p_j2k->m_specific_param.m_decoder.m_default_tcp->tccps[i].m_dc_level_shift = 1
-                    << (l_image->comps[i].prec - 1);
-        }
-    }
-
-    l_current_tile_param = l_cp->tcps;
-    for (i = 0; i < l_nb_tiles; ++i) {
-        l_current_tile_param->tccps = (opj_tccp_t*) opj_calloc(l_image->numcomps,
-                                      sizeof(opj_tccp_t));
-        if (l_current_tile_param->tccps == 00) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Not enough memory to take in charge SIZ marker\n");
-            return OPJ_FALSE;
-        }
-
-        ++l_current_tile_param;
-    }
-
-    p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_MH;
-    opj_image_comp_header_update(l_image, l_cp);
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_com(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager
-                                 )
-{
-    OPJ_UINT32 l_comment_size;
-    OPJ_UINT32 l_total_com_size;
-    const OPJ_CHAR *l_comment;
-    OPJ_BYTE * l_current_ptr = 00;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_stream != 00);
-    assert(p_manager != 00);
-
-    l_comment = p_j2k->m_cp.comment;
-    l_comment_size = (OPJ_UINT32)strlen(l_comment);
-    l_total_com_size = l_comment_size + 6;
-
-    if (l_total_com_size >
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
-        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
-                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_total_com_size);
-        if (! new_header_tile_data) {
-            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Not enough memory to write the COM marker\n");
-            return OPJ_FALSE;
-        }
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_total_com_size;
-    }
-
-    l_current_ptr = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
-
-    opj_write_bytes(l_current_ptr, J2K_MS_COM, 2);  /* COM */
-    l_current_ptr += 2;
-
-    opj_write_bytes(l_current_ptr, l_total_com_size - 2, 2);        /* L_COM */
-    l_current_ptr += 2;
-
-    opj_write_bytes(l_current_ptr, 1,
-                    2);   /* General use (IS 8859-15:1999 (Latin) values) */
-    l_current_ptr += 2;
-
-    memcpy(l_current_ptr, l_comment, l_comment_size);
-
-    if (opj_stream_write_data(p_stream,
-                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_total_com_size,
-                              p_manager) != l_total_com_size) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-/**
- * Reads a COM marker (comments)
- * @param       p_j2k           the jpeg2000 file codec.
- * @param       p_header_data   the data contained in the COM box.
- * @param       p_header_size   the size of the data contained in the COM marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_com(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_header_data != 00);
-
-    OPJ_UNUSED(p_j2k);
-    OPJ_UNUSED(p_header_data);
-    OPJ_UNUSED(p_header_size);
-    OPJ_UNUSED(p_manager);
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_cod(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager)
-{
-    opj_cp_t *l_cp = 00;
-    opj_tcp_t *l_tcp = 00;
-    OPJ_UINT32 l_code_size, l_remaining_size;
-    OPJ_BYTE * l_current_data = 00;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    l_cp = &(p_j2k->m_cp);
-    l_tcp = &l_cp->tcps[p_j2k->m_current_tile_number];
-    l_code_size = 9 + opj_j2k_get_SPCod_SPCoc_size(p_j2k,
-                  p_j2k->m_current_tile_number, 0);
-    l_remaining_size = l_code_size;
-
-    if (l_code_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
-        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
-                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_code_size);
-        if (! new_header_tile_data) {
-            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write COD marker\n");
-            return OPJ_FALSE;
-        }
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_code_size;
-    }
-
-    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
-
-    opj_write_bytes(l_current_data, J2K_MS_COD, 2);           /* COD */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, l_code_size - 2, 2);      /* L_COD */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, l_tcp->csty, 1);          /* Scod */
-    ++l_current_data;
-
-    opj_write_bytes(l_current_data, (OPJ_UINT32)l_tcp->prg, 1); /* SGcod (A) */
-    ++l_current_data;
-
-    opj_write_bytes(l_current_data, l_tcp->numlayers, 2);     /* SGcod (B) */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, l_tcp->mct, 1);           /* SGcod (C) */
-    ++l_current_data;
-
-    l_remaining_size -= 9;
-
-    if (! opj_j2k_write_SPCod_SPCoc(p_j2k, p_j2k->m_current_tile_number, 0,
-                                    l_current_data, &l_remaining_size, p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error writing COD marker\n");
-        return OPJ_FALSE;
-    }
-
-    if (l_remaining_size != 0) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error writing COD marker\n");
-        return OPJ_FALSE;
-    }
-
-    if (opj_stream_write_data(p_stream,
-                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_code_size,
-                              p_manager) != l_code_size) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-/**
- * Reads a COD marker (Coding style defaults)
- * @param       p_header_data   the data contained in the COD box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the COD marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_cod(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    /* loop */
-    OPJ_UINT32 i;
-    OPJ_UINT32 l_tmp;
-    opj_cp_t *l_cp = 00;
-    opj_tcp_t *l_tcp = 00;
-    opj_image_t *l_image = 00;
-
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    l_image = p_j2k->m_private_image;
-    l_cp = &(p_j2k->m_cp);
-
-    /* If we are in the first tile-part header of the current tile */
-    l_tcp = (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH) ?
-            &l_cp->tcps[p_j2k->m_current_tile_number] :
-            p_j2k->m_specific_param.m_decoder.m_default_tcp;
-
-#if 0
-    /* This check was added per https://github.com/uclouvain/openjpeg/commit/daed8cc9195555e101ab708a501af2dfe6d5e001 */
-    /* but this is no longer necessary to handle issue476.jp2 */
-    /* and this actually cause issues on legit files. See https://github.com/uclouvain/openjpeg/issues/1043 */
-    /* Only one COD per tile */
-    if (l_tcp->cod) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "COD marker already read. No more than one COD marker per tile.\n");
-        return OPJ_FALSE;
-    }
-#endif
-    l_tcp->cod = 1;
-
-    /* Make sure room is sufficient */
-    if (p_header_size < 5) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading COD marker\n");
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(p_header_data, &l_tcp->csty, 1);         /* Scod */
-    ++p_header_data;
-    /* Make sure we know how to decode this */
-    if ((l_tcp->csty & ~(OPJ_UINT32)(J2K_CP_CSTY_PRT | J2K_CP_CSTY_SOP |
-                                     J2K_CP_CSTY_EPH)) != 0U) {
-        opj_event_msg(p_manager, EVT_ERROR, "Unknown Scod value in COD marker\n");
-        return OPJ_FALSE;
-    }
-    opj_read_bytes(p_header_data, &l_tmp, 1);                       /* SGcod (A) */
-    ++p_header_data;
-    l_tcp->prg = (OPJ_PROG_ORDER) l_tmp;
-    /* Make sure progression order is valid */
-    if (l_tcp->prg > OPJ_CPRL) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Unknown progression order in COD marker\n");
-        l_tcp->prg = OPJ_PROG_UNKNOWN;
-    }
-    opj_read_bytes(p_header_data, &l_tcp->numlayers, 2);    /* SGcod (B) */
-    p_header_data += 2;
-
-    if ((l_tcp->numlayers < 1U) || (l_tcp->numlayers > 65535U)) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Invalid number of layers in COD marker : %d not in range [1-65535]\n",
-                      l_tcp->numlayers);
-        return OPJ_FALSE;
-    }
-
-    /* If user didn't set a number layer to decode take the max specify in the codestream. */
-    if (l_cp->m_specific_param.m_dec.m_layer) {
-        l_tcp->num_layers_to_decode = l_cp->m_specific_param.m_dec.m_layer;
-    } else {
-        l_tcp->num_layers_to_decode = l_tcp->numlayers;
-    }
-
-    opj_read_bytes(p_header_data, &l_tcp->mct, 1);          /* SGcod (C) */
-    ++p_header_data;
-
-    p_header_size -= 5;
-    for (i = 0; i < l_image->numcomps; ++i) {
-        l_tcp->tccps[i].csty = l_tcp->csty & J2K_CCP_CSTY_PRT;
-    }
-
-    if (! opj_j2k_read_SPCod_SPCoc(p_j2k, 0, p_header_data, &p_header_size,
-                                   p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading COD marker\n");
-        return OPJ_FALSE;
-    }
-
-    if (p_header_size != 0) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading COD marker\n");
-        return OPJ_FALSE;
-    }
-
-    /* Apply the coding style to other components of the current tile or the m_default_tcp*/
-    opj_j2k_copy_tile_component_parameters(p_j2k);
-
-    /* Index */
-#ifdef WIP_REMOVE_MSD
-    if (p_j2k->cstr_info) {
-        /*opj_codestream_info_t *l_cstr_info = p_j2k->cstr_info;*/
-        p_j2k->cstr_info->prog = l_tcp->prg;
-        p_j2k->cstr_info->numlayers = l_tcp->numlayers;
-        p_j2k->cstr_info->numdecompos = (OPJ_INT32*) opj_malloc(
-                                            l_image->numcomps * sizeof(OPJ_UINT32));
-        if (!p_j2k->cstr_info->numdecompos) {
-            return OPJ_FALSE;
-        }
-        for (i = 0; i < l_image->numcomps; ++i) {
-            p_j2k->cstr_info->numdecompos[i] = l_tcp->tccps[i].numresolutions - 1;
-        }
-    }
-#endif
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_coc(opj_j2k_t *p_j2k,
-                                  OPJ_UINT32 p_comp_no,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32 l_coc_size, l_remaining_size;
-    OPJ_UINT32 l_comp_room;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    l_comp_room = (p_j2k->m_private_image->numcomps <= 256) ? 1 : 2;
-
-    l_coc_size = 5 + l_comp_room + opj_j2k_get_SPCod_SPCoc_size(p_j2k,
-                 p_j2k->m_current_tile_number, p_comp_no);
-
-    if (l_coc_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
-        OPJ_BYTE *new_header_tile_data;
-        /*p_j2k->m_specific_param.m_encoder.m_header_tile_data
-                = (OPJ_BYTE*)opj_realloc(
-                        p_j2k->m_specific_param.m_encoder.m_header_tile_data,
-                        l_coc_size);*/
-
-        new_header_tile_data = (OPJ_BYTE *) opj_realloc(
-                                   p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_coc_size);
-        if (! new_header_tile_data) {
-            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write COC marker\n");
-            return OPJ_FALSE;
-        }
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_coc_size;
-    }
-
-    opj_j2k_write_coc_in_memory(p_j2k, p_comp_no,
-                                p_j2k->m_specific_param.m_encoder.m_header_tile_data, &l_remaining_size,
-                                p_manager);
-
-    if (opj_stream_write_data(p_stream,
-                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_coc_size,
-                              p_manager) != l_coc_size) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_compare_coc(opj_j2k_t *p_j2k,
-                                    OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no)
-{
-    opj_cp_t *l_cp = NULL;
-    opj_tcp_t *l_tcp = NULL;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-
-    l_cp = &(p_j2k->m_cp);
-    l_tcp = &l_cp->tcps[p_j2k->m_current_tile_number];
-
-    if (l_tcp->tccps[p_first_comp_no].csty != l_tcp->tccps[p_second_comp_no].csty) {
-        return OPJ_FALSE;
-    }
-
-
-    return opj_j2k_compare_SPCod_SPCoc(p_j2k, p_j2k->m_current_tile_number,
-                                       p_first_comp_no, p_second_comp_no);
-}
-
-static void opj_j2k_write_coc_in_memory(opj_j2k_t *p_j2k,
-                                        OPJ_UINT32 p_comp_no,
-                                        OPJ_BYTE * p_data,
-                                        OPJ_UINT32 * p_data_written,
-                                        opj_event_mgr_t * p_manager
-                                       )
-{
-    opj_cp_t *l_cp = 00;
-    opj_tcp_t *l_tcp = 00;
-    OPJ_UINT32 l_coc_size, l_remaining_size;
-    OPJ_BYTE * l_current_data = 00;
-    opj_image_t *l_image = 00;
-    OPJ_UINT32 l_comp_room;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    l_cp = &(p_j2k->m_cp);
-    l_tcp = &l_cp->tcps[p_j2k->m_current_tile_number];
-    l_image = p_j2k->m_private_image;
-    l_comp_room = (l_image->numcomps <= 256) ? 1 : 2;
-
-    l_coc_size = 5 + l_comp_room + opj_j2k_get_SPCod_SPCoc_size(p_j2k,
-                 p_j2k->m_current_tile_number, p_comp_no);
-    l_remaining_size = l_coc_size;
-
-    l_current_data = p_data;
-
-    opj_write_bytes(l_current_data, J2K_MS_COC,
-                    2);                         /* COC */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, l_coc_size - 2,
-                    2);                     /* L_COC */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, p_comp_no, l_comp_room);        /* Ccoc */
-    l_current_data += l_comp_room;
-
-    opj_write_bytes(l_current_data, l_tcp->tccps[p_comp_no].csty,
-                    1);               /* Scoc */
-    ++l_current_data;
-
-    l_remaining_size -= (5 + l_comp_room);
-    opj_j2k_write_SPCod_SPCoc(p_j2k, p_j2k->m_current_tile_number, 0,
-                              l_current_data, &l_remaining_size, p_manager);
-    * p_data_written = l_coc_size;
-}
-
-static OPJ_UINT32 opj_j2k_get_max_coc_size(opj_j2k_t *p_j2k)
-{
-    OPJ_UINT32 i, j;
-    OPJ_UINT32 l_nb_comp;
-    OPJ_UINT32 l_nb_tiles;
-    OPJ_UINT32 l_max = 0;
-
-    /* preconditions */
-
-    l_nb_tiles = p_j2k->m_cp.tw * p_j2k->m_cp.th ;
-    l_nb_comp = p_j2k->m_private_image->numcomps;
-
-    for (i = 0; i < l_nb_tiles; ++i) {
-        for (j = 0; j < l_nb_comp; ++j) {
-            l_max = opj_uint_max(l_max, opj_j2k_get_SPCod_SPCoc_size(p_j2k, i, j));
-        }
-    }
-
-    return 6 + l_max;
-}
-
-/**
- * Reads a COC marker (Coding Style Component)
- * @param       p_header_data   the data contained in the COC box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the COC marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_coc(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    opj_cp_t *l_cp = NULL;
-    opj_tcp_t *l_tcp = NULL;
-    opj_image_t *l_image = NULL;
-    OPJ_UINT32 l_comp_room;
-    OPJ_UINT32 l_comp_no;
-
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    l_cp = &(p_j2k->m_cp);
-    l_tcp = (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH)
-            ?
-            &l_cp->tcps[p_j2k->m_current_tile_number] :
-            p_j2k->m_specific_param.m_decoder.m_default_tcp;
-    l_image = p_j2k->m_private_image;
-
-    l_comp_room = l_image->numcomps <= 256 ? 1 : 2;
-
-    /* make sure room is sufficient*/
-    if (p_header_size < l_comp_room + 1) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading COC marker\n");
-        return OPJ_FALSE;
-    }
-    p_header_size -= l_comp_room + 1;
-
-    opj_read_bytes(p_header_data, &l_comp_no,
-                   l_comp_room);                 /* Ccoc */
-    p_header_data += l_comp_room;
-    if (l_comp_no >= l_image->numcomps) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Error reading COC marker (bad number of components)\n");
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(p_header_data, &l_tcp->tccps[l_comp_no].csty,
-                   1);                  /* Scoc */
-    ++p_header_data ;
-
-    if (! opj_j2k_read_SPCod_SPCoc(p_j2k, l_comp_no, p_header_data, &p_header_size,
-                                   p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading COC marker\n");
-        return OPJ_FALSE;
-    }
-
-    if (p_header_size != 0) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading COC marker\n");
-        return OPJ_FALSE;
-    }
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_qcd(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager
-                                 )
-{
-    OPJ_UINT32 l_qcd_size, l_remaining_size;
-    OPJ_BYTE * l_current_data = 00;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    l_qcd_size = 4 + opj_j2k_get_SQcd_SQcc_size(p_j2k, p_j2k->m_current_tile_number,
-                 0);
-    l_remaining_size = l_qcd_size;
-
-    if (l_qcd_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
-        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
-                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_qcd_size);
-        if (! new_header_tile_data) {
-            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write QCD marker\n");
-            return OPJ_FALSE;
-        }
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_qcd_size;
-    }
-
-    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
-
-    opj_write_bytes(l_current_data, J2K_MS_QCD, 2);         /* QCD */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, l_qcd_size - 2, 2);     /* L_QCD */
-    l_current_data += 2;
-
-    l_remaining_size -= 4;
-
-    if (! opj_j2k_write_SQcd_SQcc(p_j2k, p_j2k->m_current_tile_number, 0,
-                                  l_current_data, &l_remaining_size, p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error writing QCD marker\n");
-        return OPJ_FALSE;
-    }
-
-    if (l_remaining_size != 0) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error writing QCD marker\n");
-        return OPJ_FALSE;
-    }
-
-    if (opj_stream_write_data(p_stream,
-                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_qcd_size,
-                              p_manager) != l_qcd_size) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-/**
- * Reads a QCD marker (Quantization defaults)
- * @param       p_header_data   the data contained in the QCD box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the QCD marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_qcd(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    if (! opj_j2k_read_SQcd_SQcc(p_j2k, 0, p_header_data, &p_header_size,
-                                 p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading QCD marker\n");
-        return OPJ_FALSE;
-    }
-
-    if (p_header_size != 0) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading QCD marker\n");
-        return OPJ_FALSE;
-    }
-
-    /* Apply the quantization parameters to other components of the current tile or the m_default_tcp */
-    opj_j2k_copy_tile_quantization_parameters(p_j2k);
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_qcc(opj_j2k_t *p_j2k,
-                                  OPJ_UINT32 p_comp_no,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager
-                                 )
-{
-    OPJ_UINT32 l_qcc_size, l_remaining_size;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    l_qcc_size = 5 + opj_j2k_get_SQcd_SQcc_size(p_j2k, p_j2k->m_current_tile_number,
-                 p_comp_no);
-    l_qcc_size += p_j2k->m_private_image->numcomps <= 256 ? 0 : 1;
-    l_remaining_size = l_qcc_size;
-
-    if (l_qcc_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
-        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
-                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_qcc_size);
-        if (! new_header_tile_data) {
-            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write QCC marker\n");
-            return OPJ_FALSE;
-        }
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_qcc_size;
-    }
-
-    opj_j2k_write_qcc_in_memory(p_j2k, p_comp_no,
-                                p_j2k->m_specific_param.m_encoder.m_header_tile_data, &l_remaining_size,
-                                p_manager);
-
-    if (opj_stream_write_data(p_stream,
-                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_qcc_size,
-                              p_manager) != l_qcc_size) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_compare_qcc(opj_j2k_t *p_j2k,
-                                    OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no)
-{
-    return opj_j2k_compare_SQcd_SQcc(p_j2k, p_j2k->m_current_tile_number,
-                                     p_first_comp_no, p_second_comp_no);
-}
-
-static void opj_j2k_write_qcc_in_memory(opj_j2k_t *p_j2k,
-                                        OPJ_UINT32 p_comp_no,
-                                        OPJ_BYTE * p_data,
-                                        OPJ_UINT32 * p_data_written,
-                                        opj_event_mgr_t * p_manager
-                                       )
-{
-    OPJ_UINT32 l_qcc_size, l_remaining_size;
-    OPJ_BYTE * l_current_data = 00;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    l_qcc_size = 6 + opj_j2k_get_SQcd_SQcc_size(p_j2k, p_j2k->m_current_tile_number,
-                 p_comp_no);
-    l_remaining_size = l_qcc_size;
-
-    l_current_data = p_data;
-
-    opj_write_bytes(l_current_data, J2K_MS_QCC, 2);         /* QCC */
-    l_current_data += 2;
-
-    if (p_j2k->m_private_image->numcomps <= 256) {
-        --l_qcc_size;
-
-        opj_write_bytes(l_current_data, l_qcc_size - 2, 2);     /* L_QCC */
-        l_current_data += 2;
-
-        opj_write_bytes(l_current_data, p_comp_no, 1);  /* Cqcc */
-        ++l_current_data;
-
-        /* in the case only one byte is sufficient the last byte allocated is useless -> still do -6 for available */
-        l_remaining_size -= 6;
-    } else {
-        opj_write_bytes(l_current_data, l_qcc_size - 2, 2);     /* L_QCC */
-        l_current_data += 2;
-
-        opj_write_bytes(l_current_data, p_comp_no, 2);  /* Cqcc */
-        l_current_data += 2;
-
-        l_remaining_size -= 6;
-    }
-
-    opj_j2k_write_SQcd_SQcc(p_j2k, p_j2k->m_current_tile_number, p_comp_no,
-                            l_current_data, &l_remaining_size, p_manager);
-
-    *p_data_written = l_qcc_size;
-}
-
-static OPJ_UINT32 opj_j2k_get_max_qcc_size(opj_j2k_t *p_j2k)
-{
-    return opj_j2k_get_max_coc_size(p_j2k);
-}
-
-/**
- * Reads a QCC marker (Quantization component)
- * @param       p_header_data   the data contained in the QCC box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the QCC marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_qcc(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    OPJ_UINT32 l_num_comp, l_comp_no;
-
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    l_num_comp = p_j2k->m_private_image->numcomps;
-
-    if (l_num_comp <= 256) {
-        if (p_header_size < 1) {
-            opj_event_msg(p_manager, EVT_ERROR, "Error reading QCC marker\n");
-            return OPJ_FALSE;
-        }
-        opj_read_bytes(p_header_data, &l_comp_no, 1);
-        ++p_header_data;
-        --p_header_size;
-    } else {
-        if (p_header_size < 2) {
-            opj_event_msg(p_manager, EVT_ERROR, "Error reading QCC marker\n");
-            return OPJ_FALSE;
-        }
-        opj_read_bytes(p_header_data, &l_comp_no, 2);
-        p_header_data += 2;
-        p_header_size -= 2;
-    }
-
-#ifdef USE_JPWL
-    if (p_j2k->m_cp.correct) {
-
-        static OPJ_UINT32 backup_compno = 0;
-
-        /* compno is negative or larger than the number of components!!! */
-        if (/*(l_comp_no < 0) ||*/ (l_comp_no >= l_num_comp)) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "JPWL: bad component number in QCC (%d out of a maximum of %d)\n",
-                          l_comp_no, l_num_comp);
-            if (!JPWL_ASSUME) {
-                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
-                return OPJ_FALSE;
-            }
-            /* we try to correct */
-            l_comp_no = backup_compno % l_num_comp;
-            opj_event_msg(p_manager, EVT_WARNING, "- trying to adjust this\n"
-                          "- setting component number to %d\n",
-                          l_comp_no);
-        }
-
-        /* keep your private count of tiles */
-        backup_compno++;
-    };
-#endif /* USE_JPWL */
-
-    if (l_comp_no >= p_j2k->m_private_image->numcomps) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Invalid component number: %d, regarding the number of components %d\n",
-                      l_comp_no, p_j2k->m_private_image->numcomps);
-        return OPJ_FALSE;
-    }
-
-    if (! opj_j2k_read_SQcd_SQcc(p_j2k, l_comp_no, p_header_data, &p_header_size,
-                                 p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading QCC marker\n");
-        return OPJ_FALSE;
-    }
-
-    if (p_header_size != 0) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading QCC marker\n");
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_poc(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager
-                                 )
-{
-    OPJ_UINT32 l_nb_comp;
-    OPJ_UINT32 l_nb_poc;
-    OPJ_UINT32 l_poc_size;
-    OPJ_UINT32 l_written_size = 0;
-    opj_tcp_t *l_tcp = 00;
-    OPJ_UINT32 l_poc_room;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    l_tcp = &p_j2k->m_cp.tcps[p_j2k->m_current_tile_number];
-    l_nb_comp = p_j2k->m_private_image->numcomps;
-    l_nb_poc = 1 + l_tcp->numpocs;
-
-    if (l_nb_comp <= 256) {
-        l_poc_room = 1;
-    } else {
-        l_poc_room = 2;
-    }
-    l_poc_size = 4 + (5 + 2 * l_poc_room) * l_nb_poc;
-
-    if (l_poc_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
-        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
-                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_poc_size);
-        if (! new_header_tile_data) {
-            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write POC marker\n");
-            return OPJ_FALSE;
-        }
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_poc_size;
-    }
-
-    opj_j2k_write_poc_in_memory(p_j2k,
-                                p_j2k->m_specific_param.m_encoder.m_header_tile_data, &l_written_size,
-                                p_manager);
-
-    if (opj_stream_write_data(p_stream,
-                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_poc_size,
-                              p_manager) != l_poc_size) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static void opj_j2k_write_poc_in_memory(opj_j2k_t *p_j2k,
-                                        OPJ_BYTE * p_data,
-                                        OPJ_UINT32 * p_data_written,
-                                        opj_event_mgr_t * p_manager
-                                       )
-{
-    OPJ_UINT32 i;
-    OPJ_BYTE * l_current_data = 00;
-    OPJ_UINT32 l_nb_comp;
-    OPJ_UINT32 l_nb_poc;
-    OPJ_UINT32 l_poc_size;
-    opj_image_t *l_image = 00;
-    opj_tcp_t *l_tcp = 00;
-    opj_tccp_t *l_tccp = 00;
-    opj_poc_t *l_current_poc = 00;
-    OPJ_UINT32 l_poc_room;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    OPJ_UNUSED(p_manager);
-
-    l_tcp = &p_j2k->m_cp.tcps[p_j2k->m_current_tile_number];
-    l_tccp = &l_tcp->tccps[0];
-    l_image = p_j2k->m_private_image;
-    l_nb_comp = l_image->numcomps;
-    l_nb_poc = 1 + l_tcp->numpocs;
-
-    if (l_nb_comp <= 256) {
-        l_poc_room = 1;
-    } else {
-        l_poc_room = 2;
-    }
-
-    l_poc_size = 4 + (5 + 2 * l_poc_room) * l_nb_poc;
-
-    l_current_data = p_data;
-
-    opj_write_bytes(l_current_data, J2K_MS_POC,
-                    2);                                   /* POC  */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, l_poc_size - 2,
-                    2);                                 /* Lpoc */
-    l_current_data += 2;
-
-    l_current_poc =  l_tcp->pocs;
-    for (i = 0; i < l_nb_poc; ++i) {
-        opj_write_bytes(l_current_data, l_current_poc->resno0,
-                        1);                                /* RSpoc_i */
-        ++l_current_data;
-
-        opj_write_bytes(l_current_data, l_current_poc->compno0,
-                        l_poc_room);              /* CSpoc_i */
-        l_current_data += l_poc_room;
-
-        opj_write_bytes(l_current_data, l_current_poc->layno1,
-                        2);                                /* LYEpoc_i */
-        l_current_data += 2;
-
-        opj_write_bytes(l_current_data, l_current_poc->resno1,
-                        1);                                /* REpoc_i */
-        ++l_current_data;
-
-        opj_write_bytes(l_current_data, l_current_poc->compno1,
-                        l_poc_room);              /* CEpoc_i */
-        l_current_data += l_poc_room;
-
-        opj_write_bytes(l_current_data, (OPJ_UINT32)l_current_poc->prg,
-                        1);   /* Ppoc_i */
-        ++l_current_data;
-
-        /* change the value of the max layer according to the actual number of layers in the file, components and resolutions*/
-        l_current_poc->layno1 = (OPJ_UINT32)opj_int_min((OPJ_INT32)
-                                l_current_poc->layno1, (OPJ_INT32)l_tcp->numlayers);
-        l_current_poc->resno1 = (OPJ_UINT32)opj_int_min((OPJ_INT32)
-                                l_current_poc->resno1, (OPJ_INT32)l_tccp->numresolutions);
-        l_current_poc->compno1 = (OPJ_UINT32)opj_int_min((OPJ_INT32)
-                                 l_current_poc->compno1, (OPJ_INT32)l_nb_comp);
-
-        ++l_current_poc;
-    }
-
-    *p_data_written = l_poc_size;
-}
-
-static OPJ_UINT32 opj_j2k_get_max_poc_size(opj_j2k_t *p_j2k)
-{
-    opj_tcp_t * l_tcp = 00;
-    OPJ_UINT32 l_nb_tiles = 0;
-    OPJ_UINT32 l_max_poc = 0;
-    OPJ_UINT32 i;
-
-    l_tcp = p_j2k->m_cp.tcps;
-    l_nb_tiles = p_j2k->m_cp.th * p_j2k->m_cp.tw;
-
-    for (i = 0; i < l_nb_tiles; ++i) {
-        l_max_poc = opj_uint_max(l_max_poc, l_tcp->numpocs);
-        ++l_tcp;
-    }
-
-    ++l_max_poc;
-
-    return 4 + 9 * l_max_poc;
-}
-
-static OPJ_UINT32 opj_j2k_get_max_toc_size(opj_j2k_t *p_j2k)
-{
-    OPJ_UINT32 i;
-    OPJ_UINT32 l_nb_tiles;
-    OPJ_UINT32 l_max = 0;
-    opj_tcp_t * l_tcp = 00;
-
-    l_tcp = p_j2k->m_cp.tcps;
-    l_nb_tiles = p_j2k->m_cp.tw * p_j2k->m_cp.th ;
-
-    for (i = 0; i < l_nb_tiles; ++i) {
-        l_max = opj_uint_max(l_max, l_tcp->m_nb_tile_parts);
-
-        ++l_tcp;
-    }
-
-    return 12 * l_max;
-}
-
-static OPJ_UINT32 opj_j2k_get_specific_header_sizes(opj_j2k_t *p_j2k)
-{
-    OPJ_UINT32 l_nb_bytes = 0;
-    OPJ_UINT32 l_nb_comps;
-    OPJ_UINT32 l_coc_bytes, l_qcc_bytes;
-
-    l_nb_comps = p_j2k->m_private_image->numcomps - 1;
-    l_nb_bytes += opj_j2k_get_max_toc_size(p_j2k);
-
-    if (!(OPJ_IS_CINEMA(p_j2k->m_cp.rsiz))) {
-        l_coc_bytes = opj_j2k_get_max_coc_size(p_j2k);
-        l_nb_bytes += l_nb_comps * l_coc_bytes;
-
-        l_qcc_bytes = opj_j2k_get_max_qcc_size(p_j2k);
-        l_nb_bytes += l_nb_comps * l_qcc_bytes;
-    }
-
-    l_nb_bytes += opj_j2k_get_max_poc_size(p_j2k);
-
-    /*** DEVELOPER CORNER, Add room for your headers ***/
-
-    return l_nb_bytes;
-}
-
-/**
- * Reads a POC marker (Progression Order Change)
- *
- * @param       p_header_data   the data contained in the POC box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the POC marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_poc(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    OPJ_UINT32 i, l_nb_comp, l_tmp;
-    opj_image_t * l_image = 00;
-    OPJ_UINT32 l_old_poc_nb, l_current_poc_nb, l_current_poc_remaining;
-    OPJ_UINT32 l_chunk_size, l_comp_room;
-
-    opj_cp_t *l_cp = 00;
-    opj_tcp_t *l_tcp = 00;
-    opj_poc_t *l_current_poc = 00;
-
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    l_image = p_j2k->m_private_image;
-    l_nb_comp = l_image->numcomps;
-    if (l_nb_comp <= 256) {
-        l_comp_room = 1;
-    } else {
-        l_comp_room = 2;
-    }
-    l_chunk_size = 5 + 2 * l_comp_room;
-    l_current_poc_nb = p_header_size / l_chunk_size;
-    l_current_poc_remaining = p_header_size % l_chunk_size;
-
-    if ((l_current_poc_nb <= 0) || (l_current_poc_remaining != 0)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading POC marker\n");
-        return OPJ_FALSE;
-    }
-
-    l_cp = &(p_j2k->m_cp);
-    l_tcp = (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH) ?
-            &l_cp->tcps[p_j2k->m_current_tile_number] :
-            p_j2k->m_specific_param.m_decoder.m_default_tcp;
-    l_old_poc_nb = l_tcp->POC ? l_tcp->numpocs + 1 : 0;
-    l_current_poc_nb += l_old_poc_nb;
-
-    if (l_current_poc_nb >= J2K_MAX_POCS) {
-        opj_event_msg(p_manager, EVT_ERROR, "Too many POCs %d\n", l_current_poc_nb);
-        return OPJ_FALSE;
-    }
-
-    /* now poc is in use.*/
-    l_tcp->POC = 1;
-
-    l_current_poc = &l_tcp->pocs[l_old_poc_nb];
-    for (i = l_old_poc_nb; i < l_current_poc_nb; ++i) {
-        opj_read_bytes(p_header_data, &(l_current_poc->resno0),
-                       1);                               /* RSpoc_i */
-        ++p_header_data;
-        opj_read_bytes(p_header_data, &(l_current_poc->compno0),
-                       l_comp_room);  /* CSpoc_i */
-        p_header_data += l_comp_room;
-        opj_read_bytes(p_header_data, &(l_current_poc->layno1),
-                       2);                               /* LYEpoc_i */
-        /* make sure layer end is in acceptable bounds */
-        l_current_poc->layno1 = opj_uint_min(l_current_poc->layno1, l_tcp->numlayers);
-        p_header_data += 2;
-        opj_read_bytes(p_header_data, &(l_current_poc->resno1),
-                       1);                               /* REpoc_i */
-        ++p_header_data;
-        opj_read_bytes(p_header_data, &(l_current_poc->compno1),
-                       l_comp_room);  /* CEpoc_i */
-        p_header_data += l_comp_room;
-        opj_read_bytes(p_header_data, &l_tmp,
-                       1);                                                                 /* Ppoc_i */
-        ++p_header_data;
-        l_current_poc->prg = (OPJ_PROG_ORDER) l_tmp;
-        /* make sure comp is in acceptable bounds */
-        l_current_poc->compno1 = opj_uint_min(l_current_poc->compno1, l_nb_comp);
-        ++l_current_poc;
-    }
-
-    l_tcp->numpocs = l_current_poc_nb - 1;
-    return OPJ_TRUE;
-}
-
-/**
- * Reads a CRG marker (Component registration)
- *
- * @param       p_header_data   the data contained in the TLM box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the TLM marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_crg(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    OPJ_UINT32 l_nb_comp;
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    OPJ_UNUSED(p_header_data);
-
-    l_nb_comp = p_j2k->m_private_image->numcomps;
-
-    if (p_header_size != l_nb_comp * 4) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading CRG marker\n");
-        return OPJ_FALSE;
-    }
-    /* Do not care of this at the moment since only local variables are set here */
-    /*
-    for
-            (i = 0; i < l_nb_comp; ++i)
-    {
-            opj_read_bytes(p_header_data,&l_Xcrg_i,2);                              // Xcrg_i
-            p_header_data+=2;
-            opj_read_bytes(p_header_data,&l_Ycrg_i,2);                              // Xcrg_i
-            p_header_data+=2;
-    }
-    */
-    return OPJ_TRUE;
-}
-
-/**
- * Reads a TLM marker (Tile Length Marker)
- *
- * @param       p_header_data   the data contained in the TLM box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the TLM marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_tlm(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    OPJ_UINT32 l_Ztlm, l_Stlm, l_ST, l_SP, l_tot_num_tp_remaining, l_quotient,
-               l_Ptlm_size;
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    OPJ_UNUSED(p_j2k);
-
-    if (p_header_size < 2) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading TLM marker\n");
-        return OPJ_FALSE;
-    }
-    p_header_size -= 2;
-
-    opj_read_bytes(p_header_data, &l_Ztlm,
-                   1);                              /* Ztlm */
-    ++p_header_data;
-    opj_read_bytes(p_header_data, &l_Stlm,
-                   1);                              /* Stlm */
-    ++p_header_data;
-
-    l_ST = ((l_Stlm >> 4) & 0x3);
-    l_SP = (l_Stlm >> 6) & 0x1;
-
-    l_Ptlm_size = (l_SP + 1) * 2;
-    l_quotient = l_Ptlm_size + l_ST;
-
-    l_tot_num_tp_remaining = p_header_size % l_quotient;
-
-    if (l_tot_num_tp_remaining != 0) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading TLM marker\n");
-        return OPJ_FALSE;
-    }
-    /* FIXME Do not care of this at the moment since only local variables are set here */
-    /*
-    for
-            (i = 0; i < l_tot_num_tp; ++i)
-    {
-            opj_read_bytes(p_header_data,&l_Ttlm_i,l_ST);                           // Ttlm_i
-            p_header_data += l_ST;
-            opj_read_bytes(p_header_data,&l_Ptlm_i,l_Ptlm_size);            // Ptlm_i
-            p_header_data += l_Ptlm_size;
-    }*/
-    return OPJ_TRUE;
-}
-
-/**
- * Reads a PLM marker (Packet length, main header marker)
- *
- * @param       p_header_data   the data contained in the TLM box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the TLM marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_plm(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    OPJ_UNUSED(p_j2k);
-    OPJ_UNUSED(p_header_data);
-
-    if (p_header_size < 1) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading PLM marker\n");
-        return OPJ_FALSE;
-    }
-    /* Do not care of this at the moment since only local variables are set here */
-    /*
-    opj_read_bytes(p_header_data,&l_Zplm,1);                                        // Zplm
-    ++p_header_data;
-    --p_header_size;
-
-    while
-            (p_header_size > 0)
-    {
-            opj_read_bytes(p_header_data,&l_Nplm,1);                                // Nplm
-            ++p_header_data;
-            p_header_size -= (1+l_Nplm);
-            if
-                    (p_header_size < 0)
-            {
-                    opj_event_msg(p_manager, EVT_ERROR, "Error reading PLM marker\n");
-                    return false;
-            }
-            for
-                    (i = 0; i < l_Nplm; ++i)
-            {
-                    opj_read_bytes(p_header_data,&l_tmp,1);                         // Iplm_ij
-                    ++p_header_data;
-                    // take only the last seven bytes
-                    l_packet_len |= (l_tmp & 0x7f);
-                    if
-                            (l_tmp & 0x80)
-                    {
-                            l_packet_len <<= 7;
-                    }
-                    else
-                    {
-            // store packet length and proceed to next packet
-                            l_packet_len = 0;
-                    }
-            }
-            if
-                    (l_packet_len != 0)
-            {
-                    opj_event_msg(p_manager, EVT_ERROR, "Error reading PLM marker\n");
-                    return false;
-            }
-    }
-    */
-    return OPJ_TRUE;
-}
-
-/**
- * Reads a PLT marker (Packet length, tile-part header)
- *
- * @param       p_header_data   the data contained in the PLT box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the PLT marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_plt(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    OPJ_UINT32 l_Zplt, l_tmp, l_packet_len = 0, i;
-
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    OPJ_UNUSED(p_j2k);
-
-    if (p_header_size < 1) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading PLT marker\n");
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(p_header_data, &l_Zplt, 1);              /* Zplt */
-    ++p_header_data;
-    --p_header_size;
-
-    for (i = 0; i < p_header_size; ++i) {
-        opj_read_bytes(p_header_data, &l_tmp, 1);       /* Iplt_ij */
-        ++p_header_data;
-        /* take only the last seven bytes */
-        l_packet_len |= (l_tmp & 0x7f);
-        if (l_tmp & 0x80) {
-            l_packet_len <<= 7;
-        } else {
-            /* store packet length and proceed to next packet */
-            l_packet_len = 0;
-        }
-    }
-
-    if (l_packet_len != 0) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading PLT marker\n");
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-/**
- * Reads a PPM marker (Packed packet headers, main header)
- *
- * @param       p_header_data   the data contained in the POC box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the POC marker.
- * @param       p_manager               the user event manager.
- */
-
-static OPJ_BOOL opj_j2k_read_ppm(
-    opj_j2k_t *p_j2k,
-    OPJ_BYTE * p_header_data,
-    OPJ_UINT32 p_header_size,
-    opj_event_mgr_t * p_manager)
-{
-    opj_cp_t *l_cp = 00;
-    OPJ_UINT32 l_Z_ppm;
-
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    /* We need to have the Z_ppm element + 1 byte of Nppm/Ippm at minimum */
-    if (p_header_size < 2) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading PPM marker\n");
-        return OPJ_FALSE;
-    }
-
-    l_cp = &(p_j2k->m_cp);
-    l_cp->ppm = 1;
-
-    opj_read_bytes(p_header_data, &l_Z_ppm, 1);             /* Z_ppm */
-    ++p_header_data;
-    --p_header_size;
-
-    /* check allocation needed */
-    if (l_cp->ppm_markers == NULL) { /* first PPM marker */
-        OPJ_UINT32 l_newCount = l_Z_ppm + 1U; /* can't overflow, l_Z_ppm is UINT8 */
-        assert(l_cp->ppm_markers_count == 0U);
-
-        l_cp->ppm_markers = (opj_ppx *) opj_calloc(l_newCount, sizeof(opj_ppx));
-        if (l_cp->ppm_markers == NULL) {
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPM marker\n");
-            return OPJ_FALSE;
-        }
-        l_cp->ppm_markers_count = l_newCount;
-    } else if (l_cp->ppm_markers_count <= l_Z_ppm) {
-        OPJ_UINT32 l_newCount = l_Z_ppm + 1U; /* can't overflow, l_Z_ppm is UINT8 */
-        opj_ppx *new_ppm_markers;
-        new_ppm_markers = (opj_ppx *) opj_realloc(l_cp->ppm_markers,
-                          l_newCount * sizeof(opj_ppx));
-        if (new_ppm_markers == NULL) {
-            /* clean up to be done on l_cp destruction */
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPM marker\n");
-            return OPJ_FALSE;
-        }
-        l_cp->ppm_markers = new_ppm_markers;
-        memset(l_cp->ppm_markers + l_cp->ppm_markers_count, 0,
-               (l_newCount - l_cp->ppm_markers_count) * sizeof(opj_ppx));
-        l_cp->ppm_markers_count = l_newCount;
-    }
-
-    if (l_cp->ppm_markers[l_Z_ppm].m_data != NULL) {
-        /* clean up to be done on l_cp destruction */
-        opj_event_msg(p_manager, EVT_ERROR, "Zppm %u already read\n", l_Z_ppm);
-        return OPJ_FALSE;
-    }
-
-    l_cp->ppm_markers[l_Z_ppm].m_data = (OPJ_BYTE *) opj_malloc(p_header_size);
-    if (l_cp->ppm_markers[l_Z_ppm].m_data == NULL) {
-        /* clean up to be done on l_cp destruction */
-        opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPM marker\n");
-        return OPJ_FALSE;
-    }
-    l_cp->ppm_markers[l_Z_ppm].m_data_size = p_header_size;
-    memcpy(l_cp->ppm_markers[l_Z_ppm].m_data, p_header_data, p_header_size);
-
-    return OPJ_TRUE;
-}
-
-/**
- * Merges all PPM markers read (Packed headers, main header)
- *
- * @param       p_cp      main coding parameters.
- * @param       p_manager the user event manager.
- */
-static OPJ_BOOL opj_j2k_merge_ppm(opj_cp_t *p_cp, opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32 i, l_ppm_data_size, l_N_ppm_remaining;
-
-    /* preconditions */
-    assert(p_cp != 00);
-    assert(p_manager != 00);
-    assert(p_cp->ppm_buffer == NULL);
-
-    if (p_cp->ppm == 0U) {
-        return OPJ_TRUE;
-    }
-
-    l_ppm_data_size = 0U;
-    l_N_ppm_remaining = 0U;
-    for (i = 0U; i < p_cp->ppm_markers_count; ++i) {
-        if (p_cp->ppm_markers[i].m_data !=
-                NULL) { /* standard doesn't seem to require contiguous Zppm */
-            OPJ_UINT32 l_N_ppm;
-            OPJ_UINT32 l_data_size = p_cp->ppm_markers[i].m_data_size;
-            const OPJ_BYTE* l_data = p_cp->ppm_markers[i].m_data;
-
-            if (l_N_ppm_remaining >= l_data_size) {
-                l_N_ppm_remaining -= l_data_size;
-                l_data_size = 0U;
-            } else {
-                l_data += l_N_ppm_remaining;
-                l_data_size -= l_N_ppm_remaining;
-                l_N_ppm_remaining = 0U;
-            }
-
-            if (l_data_size > 0U) {
-                do {
-                    /* read Nppm */
-                    if (l_data_size < 4U) {
-                        /* clean up to be done on l_cp destruction */
-                        opj_event_msg(p_manager, EVT_ERROR, "Not enough bytes to read Nppm\n");
-                        return OPJ_FALSE;
-                    }
-                    opj_read_bytes(l_data, &l_N_ppm, 4);
-                    l_data += 4;
-                    l_data_size -= 4;
-                    l_ppm_data_size +=
-                        l_N_ppm; /* can't overflow, max 256 markers of max 65536 bytes, that is when PPM markers are not corrupted which is checked elsewhere */
-
-                    if (l_data_size >= l_N_ppm) {
-                        l_data_size -= l_N_ppm;
-                        l_data += l_N_ppm;
-                    } else {
-                        l_N_ppm_remaining = l_N_ppm - l_data_size;
-                        l_data_size = 0U;
-                    }
-                } while (l_data_size > 0U);
-            }
-        }
-    }
-
-    if (l_N_ppm_remaining != 0U) {
-        /* clean up to be done on l_cp destruction */
-        opj_event_msg(p_manager, EVT_ERROR, "Corrupted PPM markers\n");
-        return OPJ_FALSE;
-    }
-
-    p_cp->ppm_buffer = (OPJ_BYTE *) opj_malloc(l_ppm_data_size);
-    if (p_cp->ppm_buffer == 00) {
-        opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPM marker\n");
-        return OPJ_FALSE;
-    }
-    p_cp->ppm_len = l_ppm_data_size;
-    l_ppm_data_size = 0U;
-    l_N_ppm_remaining = 0U;
-    for (i = 0U; i < p_cp->ppm_markers_count; ++i) {
-        if (p_cp->ppm_markers[i].m_data !=
-                NULL) { /* standard doesn't seem to require contiguous Zppm */
-            OPJ_UINT32 l_N_ppm;
-            OPJ_UINT32 l_data_size = p_cp->ppm_markers[i].m_data_size;
-            const OPJ_BYTE* l_data = p_cp->ppm_markers[i].m_data;
-
-            if (l_N_ppm_remaining >= l_data_size) {
-                memcpy(p_cp->ppm_buffer + l_ppm_data_size, l_data, l_data_size);
-                l_ppm_data_size += l_data_size;
-                l_N_ppm_remaining -= l_data_size;
-                l_data_size = 0U;
-            } else {
-                memcpy(p_cp->ppm_buffer + l_ppm_data_size, l_data, l_N_ppm_remaining);
-                l_ppm_data_size += l_N_ppm_remaining;
-                l_data += l_N_ppm_remaining;
-                l_data_size -= l_N_ppm_remaining;
-                l_N_ppm_remaining = 0U;
-            }
-
-            if (l_data_size > 0U) {
-                do {
-                    /* read Nppm */
-                    if (l_data_size < 4U) {
-                        /* clean up to be done on l_cp destruction */
-                        opj_event_msg(p_manager, EVT_ERROR, "Not enough bytes to read Nppm\n");
-                        return OPJ_FALSE;
-                    }
-                    opj_read_bytes(l_data, &l_N_ppm, 4);
-                    l_data += 4;
-                    l_data_size -= 4;
-
-                    if (l_data_size >= l_N_ppm) {
-                        memcpy(p_cp->ppm_buffer + l_ppm_data_size, l_data, l_N_ppm);
-                        l_ppm_data_size += l_N_ppm;
-                        l_data_size -= l_N_ppm;
-                        l_data += l_N_ppm;
-                    } else {
-                        memcpy(p_cp->ppm_buffer + l_ppm_data_size, l_data, l_data_size);
-                        l_ppm_data_size += l_data_size;
-                        l_N_ppm_remaining = l_N_ppm - l_data_size;
-                        l_data_size = 0U;
-                    }
-                } while (l_data_size > 0U);
-            }
-            opj_free(p_cp->ppm_markers[i].m_data);
-            p_cp->ppm_markers[i].m_data = NULL;
-            p_cp->ppm_markers[i].m_data_size = 0U;
-        }
-    }
-
-    p_cp->ppm_data = p_cp->ppm_buffer;
-    p_cp->ppm_data_size = p_cp->ppm_len;
-
-    p_cp->ppm_markers_count = 0U;
-    opj_free(p_cp->ppm_markers);
-    p_cp->ppm_markers = NULL;
-
-    return OPJ_TRUE;
-}
-
-/**
- * Reads a PPT marker (Packed packet headers, tile-part header)
- *
- * @param       p_header_data   the data contained in the PPT box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the PPT marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_ppt(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    opj_cp_t *l_cp = 00;
-    opj_tcp_t *l_tcp = 00;
-    OPJ_UINT32 l_Z_ppt;
-
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    /* We need to have the Z_ppt element + 1 byte of Ippt at minimum */
-    if (p_header_size < 2) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading PPT marker\n");
-        return OPJ_FALSE;
-    }
-
-    l_cp = &(p_j2k->m_cp);
-    if (l_cp->ppm) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Error reading PPT marker: packet header have been previously found in the main header (PPM marker).\n");
-        return OPJ_FALSE;
-    }
-
-    l_tcp = &(l_cp->tcps[p_j2k->m_current_tile_number]);
-    l_tcp->ppt = 1;
-
-    opj_read_bytes(p_header_data, &l_Z_ppt, 1);             /* Z_ppt */
-    ++p_header_data;
-    --p_header_size;
-
-    /* check allocation needed */
-    if (l_tcp->ppt_markers == NULL) { /* first PPT marker */
-        OPJ_UINT32 l_newCount = l_Z_ppt + 1U; /* can't overflow, l_Z_ppt is UINT8 */
-        assert(l_tcp->ppt_markers_count == 0U);
-
-        l_tcp->ppt_markers = (opj_ppx *) opj_calloc(l_newCount, sizeof(opj_ppx));
-        if (l_tcp->ppt_markers == NULL) {
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPT marker\n");
-            return OPJ_FALSE;
-        }
-        l_tcp->ppt_markers_count = l_newCount;
-    } else if (l_tcp->ppt_markers_count <= l_Z_ppt) {
-        OPJ_UINT32 l_newCount = l_Z_ppt + 1U; /* can't overflow, l_Z_ppt is UINT8 */
-        opj_ppx *new_ppt_markers;
-        new_ppt_markers = (opj_ppx *) opj_realloc(l_tcp->ppt_markers,
-                          l_newCount * sizeof(opj_ppx));
-        if (new_ppt_markers == NULL) {
-            /* clean up to be done on l_tcp destruction */
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPT marker\n");
-            return OPJ_FALSE;
-        }
-        l_tcp->ppt_markers = new_ppt_markers;
-        memset(l_tcp->ppt_markers + l_tcp->ppt_markers_count, 0,
-               (l_newCount - l_tcp->ppt_markers_count) * sizeof(opj_ppx));
-        l_tcp->ppt_markers_count = l_newCount;
-    }
-
-    if (l_tcp->ppt_markers[l_Z_ppt].m_data != NULL) {
-        /* clean up to be done on l_tcp destruction */
-        opj_event_msg(p_manager, EVT_ERROR, "Zppt %u already read\n", l_Z_ppt);
-        return OPJ_FALSE;
-    }
-
-    l_tcp->ppt_markers[l_Z_ppt].m_data = (OPJ_BYTE *) opj_malloc(p_header_size);
-    if (l_tcp->ppt_markers[l_Z_ppt].m_data == NULL) {
-        /* clean up to be done on l_tcp destruction */
-        opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPT marker\n");
-        return OPJ_FALSE;
-    }
-    l_tcp->ppt_markers[l_Z_ppt].m_data_size = p_header_size;
-    memcpy(l_tcp->ppt_markers[l_Z_ppt].m_data, p_header_data, p_header_size);
-    return OPJ_TRUE;
-}
-
-/**
- * Merges all PPT markers read (Packed packet headers, tile-part header)
- *
- * @param       p_tcp   the tile.
- * @param       p_manager               the user event manager.
- */
-static OPJ_BOOL opj_j2k_merge_ppt(opj_tcp_t *p_tcp, opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32 i, l_ppt_data_size;
-    /* preconditions */
-    assert(p_tcp != 00);
-    assert(p_manager != 00);
-
-    if (p_tcp->ppt_buffer != NULL) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "opj_j2k_merge_ppt() has already been called\n");
-        return OPJ_FALSE;
-    }
-
-    if (p_tcp->ppt == 0U) {
-        return OPJ_TRUE;
-    }
-
-    l_ppt_data_size = 0U;
-    for (i = 0U; i < p_tcp->ppt_markers_count; ++i) {
-        l_ppt_data_size +=
-            p_tcp->ppt_markers[i].m_data_size; /* can't overflow, max 256 markers of max 65536 bytes */
-    }
-
-    p_tcp->ppt_buffer = (OPJ_BYTE *) opj_malloc(l_ppt_data_size);
-    if (p_tcp->ppt_buffer == 00) {
-        opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPT marker\n");
-        return OPJ_FALSE;
-    }
-    p_tcp->ppt_len = l_ppt_data_size;
-    l_ppt_data_size = 0U;
-    for (i = 0U; i < p_tcp->ppt_markers_count; ++i) {
-        if (p_tcp->ppt_markers[i].m_data !=
-                NULL) { /* standard doesn't seem to require contiguous Zppt */
-            memcpy(p_tcp->ppt_buffer + l_ppt_data_size, p_tcp->ppt_markers[i].m_data,
-                   p_tcp->ppt_markers[i].m_data_size);
-            l_ppt_data_size +=
-                p_tcp->ppt_markers[i].m_data_size; /* can't overflow, max 256 markers of max 65536 bytes */
-
-            opj_free(p_tcp->ppt_markers[i].m_data);
-            p_tcp->ppt_markers[i].m_data = NULL;
-            p_tcp->ppt_markers[i].m_data_size = 0U;
-        }
-    }
-
-    p_tcp->ppt_markers_count = 0U;
-    opj_free(p_tcp->ppt_markers);
-    p_tcp->ppt_markers = NULL;
-
-    p_tcp->ppt_data = p_tcp->ppt_buffer;
-    p_tcp->ppt_data_size = p_tcp->ppt_len;
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_tlm(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager
-                                 )
-{
-    OPJ_BYTE * l_current_data = 00;
-    OPJ_UINT32 l_tlm_size;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    l_tlm_size = 6 + (5 * p_j2k->m_specific_param.m_encoder.m_total_tile_parts);
-
-    if (l_tlm_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
-        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
-                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_tlm_size);
-        if (! new_header_tile_data) {
-            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write TLM marker\n");
-            return OPJ_FALSE;
-        }
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_tlm_size;
-    }
-
-    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
-
-    /* change the way data is written to avoid seeking if possible */
-    /* TODO */
-    p_j2k->m_specific_param.m_encoder.m_tlm_start = opj_stream_tell(p_stream);
-
-    opj_write_bytes(l_current_data, J2K_MS_TLM,
-                    2);                                   /* TLM */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, l_tlm_size - 2,
-                    2);                                 /* Lpoc */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, 0,
-                    1);                                                    /* Ztlm=0*/
-    ++l_current_data;
-
-    opj_write_bytes(l_current_data, 0x50,
-                    1);                                                 /* Stlm ST=1(8bits-255 tiles max),SP=1(Ptlm=32bits) */
-    ++l_current_data;
-
-    /* do nothing on the 5 * l_j2k->m_specific_param.m_encoder.m_total_tile_parts remaining data */
-    if (opj_stream_write_data(p_stream,
-                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_tlm_size,
-                              p_manager) != l_tlm_size) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_sot(opj_j2k_t *p_j2k,
-                                  OPJ_BYTE * p_data,
-                                  OPJ_UINT32 p_total_data_size,
-                                  OPJ_UINT32 * p_data_written,
-                                  const opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager
-                                 )
-{
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    OPJ_UNUSED(p_stream);
-
-    if (p_total_data_size < 12) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Not enough bytes in output buffer to write SOT marker\n");
-        return OPJ_FALSE;
-    }
-
-    opj_write_bytes(p_data, J2K_MS_SOT,
-                    2);                                 /* SOT */
-    p_data += 2;
-
-    opj_write_bytes(p_data, 10,
-                    2);                                                   /* Lsot */
-    p_data += 2;
-
-    opj_write_bytes(p_data, p_j2k->m_current_tile_number,
-                    2);                        /* Isot */
-    p_data += 2;
-
-    /* Psot  */
-    p_data += 4;
-
-    opj_write_bytes(p_data,
-                    p_j2k->m_specific_param.m_encoder.m_current_tile_part_number,
-                    1);                        /* TPsot */
-    ++p_data;
-
-    opj_write_bytes(p_data,
-                    p_j2k->m_cp.tcps[p_j2k->m_current_tile_number].m_nb_tile_parts,
-                    1);                      /* TNsot */
-    ++p_data;
-
-    /* UniPG>> */
-#ifdef USE_JPWL
-    /* update markers struct */
-    /*
-            OPJ_BOOL res = j2k_add_marker(p_j2k->cstr_info, J2K_MS_SOT, p_j2k->sot_start, len + 2);
-    */
-    assert(0 && "TODO");
-#endif /* USE_JPWL */
-
-    * p_data_written = 12;
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_get_sot_values(OPJ_BYTE *  p_header_data,
-                                       OPJ_UINT32  p_header_size,
-                                       OPJ_UINT32* p_tile_no,
-                                       OPJ_UINT32* p_tot_len,
-                                       OPJ_UINT32* p_current_part,
-                                       OPJ_UINT32* p_num_parts,
-                                       opj_event_mgr_t * p_manager)
-{
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(p_manager != 00);
-
-    /* Size of this marker is fixed = 12 (we have already read marker and its size)*/
-    if (p_header_size != 8) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading SOT marker\n");
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(p_header_data, p_tile_no, 2);    /* Isot */
-    p_header_data += 2;
-    opj_read_bytes(p_header_data, p_tot_len, 4);    /* Psot */
-    p_header_data += 4;
-    opj_read_bytes(p_header_data, p_current_part, 1); /* TPsot */
-    ++p_header_data;
-    opj_read_bytes(p_header_data, p_num_parts, 1);  /* TNsot */
-    ++p_header_data;
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_read_sot(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager)
-{
-    opj_cp_t *l_cp = 00;
-    opj_tcp_t *l_tcp = 00;
-    OPJ_UINT32 l_tot_len, l_num_parts = 0;
-    OPJ_UINT32 l_current_part;
-    OPJ_UINT32 l_tile_x, l_tile_y;
-
-    /* preconditions */
-
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    if (! opj_j2k_get_sot_values(p_header_data, p_header_size,
-                                 &(p_j2k->m_current_tile_number), &l_tot_len, &l_current_part, &l_num_parts,
-                                 p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading SOT marker\n");
-        return OPJ_FALSE;
-    }
-#ifdef DEBUG_VERBOSE
-    fprintf(stderr, "SOT %d %d %d %d\n",
-            p_j2k->m_current_tile_number, l_tot_len, l_current_part, l_num_parts);
-#endif
-
-    l_cp = &(p_j2k->m_cp);
-
-    /* testcase 2.pdf.SIGFPE.706.1112 */
-    if (p_j2k->m_current_tile_number >= l_cp->tw * l_cp->th) {
-        opj_event_msg(p_manager, EVT_ERROR, "Invalid tile number %d\n",
-                      p_j2k->m_current_tile_number);
-        return OPJ_FALSE;
-    }
-
-    l_tcp = &l_cp->tcps[p_j2k->m_current_tile_number];
-    l_tile_x = p_j2k->m_current_tile_number % l_cp->tw;
-    l_tile_y = p_j2k->m_current_tile_number / l_cp->tw;
-
-    if (p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec < 0 ||
-            p_j2k->m_current_tile_number == (OPJ_UINT32)
-            p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec) {
-        /* Do only this check if we decode all tile part headers, or if */
-        /* we decode one precise tile. Otherwise the m_current_tile_part_number */
-        /* might not be valid */
-        /* Fixes issue with id_000020,sig_06,src_001958,op_flip4,pos_149 */
-        /* of https://github.com/uclouvain/openjpeg/issues/939 */
-        /* We must avoid reading twice the same tile part number for a given tile */
-        /* so as to avoid various issues, like opj_j2k_merge_ppt being called */
-        /* several times. */
-        /* ISO 15444-1 A.4.2 Start of tile-part (SOT) mandates that tile parts */
-        /* should appear in increasing order. */
-        if (l_tcp->m_current_tile_part_number + 1 != (OPJ_INT32)l_current_part) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Invalid tile part index for tile number %d. "
-                          "Got %d, expected %d\n",
-                          p_j2k->m_current_tile_number,
-                          l_current_part,
-                          l_tcp->m_current_tile_part_number + 1);
-            return OPJ_FALSE;
-        }
-    }
-
-    l_tcp->m_current_tile_part_number = (OPJ_INT32) l_current_part;
-
-#ifdef USE_JPWL
-    if (l_cp->correct) {
-
-        OPJ_UINT32 tileno = p_j2k->m_current_tile_number;
-        static OPJ_UINT32 backup_tileno = 0;
-
-        /* tileno is negative or larger than the number of tiles!!! */
-        if (tileno > (l_cp->tw * l_cp->th)) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "JPWL: bad tile number (%d out of a maximum of %d)\n",
-                          tileno, (l_cp->tw * l_cp->th));
-            if (!JPWL_ASSUME) {
-                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
-                return OPJ_FALSE;
-            }
-            /* we try to correct */
-            tileno = backup_tileno;
-            opj_event_msg(p_manager, EVT_WARNING, "- trying to adjust this\n"
-                          "- setting tile number to %d\n",
-                          tileno);
-        }
-
-        /* keep your private count of tiles */
-        backup_tileno++;
-    };
-#endif /* USE_JPWL */
-
-    /* look for the tile in the list of already processed tile (in parts). */
-    /* Optimization possible here with a more complex data structure and with the removing of tiles */
-    /* since the time taken by this function can only grow at the time */
-
-    /* PSot should be equal to zero or >=14 or <= 2^32-1 */
-    if ((l_tot_len != 0) && (l_tot_len < 14)) {
-        if (l_tot_len ==
-                12) { /* MSD: Special case for the PHR data which are read by kakadu*/
-            opj_event_msg(p_manager, EVT_WARNING, "Empty SOT marker detected: Psot=%d.\n",
-                          l_tot_len);
-        } else {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Psot value is not correct regards to the JPEG2000 norm: %d.\n", l_tot_len);
-            return OPJ_FALSE;
-        }
-    }
-
-#ifdef USE_JPWL
-    if (l_cp->correct) {
-
-        /* totlen is negative or larger than the bytes left!!! */
-        if (/*(l_tot_len < 0) ||*/ (l_tot_len >
-                                    p_header_size)) {   /* FIXME it seems correct; for info in V1 -> (p_stream_numbytesleft(p_stream) + 8))) { */
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "JPWL: bad tile byte size (%d bytes against %d bytes left)\n",
-                          l_tot_len,
-                          p_header_size);  /* FIXME it seems correct; for info in V1 -> p_stream_numbytesleft(p_stream) + 8); */
-            if (!JPWL_ASSUME) {
-                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
-                return OPJ_FALSE;
-            }
-            /* we try to correct */
-            l_tot_len = 0;
-            opj_event_msg(p_manager, EVT_WARNING, "- trying to adjust this\n"
-                          "- setting Psot to %d => assuming it is the last tile\n",
-                          l_tot_len);
-        }
-    };
-#endif /* USE_JPWL */
-
-    /* Ref A.4.2: Psot could be equal zero if it is the last tile-part of the codestream.*/
-    if (!l_tot_len) {
-        opj_event_msg(p_manager, EVT_INFO,
-                      "Psot value of the current tile-part is equal to zero, "
-                      "we assuming it is the last tile-part of the codestream.\n");
-        p_j2k->m_specific_param.m_decoder.m_last_tile_part = 1;
-    }
-
-    if (l_tcp->m_nb_tile_parts != 0 && l_current_part >= l_tcp->m_nb_tile_parts) {
-        /* Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=2851 */
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "In SOT marker, TPSot (%d) is not valid regards to the previous "
-                      "number of tile-part (%d), giving up\n", l_current_part,
-                      l_tcp->m_nb_tile_parts);
-        p_j2k->m_specific_param.m_decoder.m_last_tile_part = 1;
-        return OPJ_FALSE;
-    }
-
-    if (l_num_parts !=
-            0) { /* Number of tile-part header is provided by this tile-part header */
-        l_num_parts += p_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction;
-        /* Useful to manage the case of textGBR.jp2 file because two values of TNSot are allowed: the correct numbers of
-         * tile-parts for that tile and zero (A.4.2 of 15444-1 : 2002). */
-        if (l_tcp->m_nb_tile_parts) {
-            if (l_current_part >= l_tcp->m_nb_tile_parts) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "In SOT marker, TPSot (%d) is not valid regards to the current "
-                              "number of tile-part (%d), giving up\n", l_current_part,
-                              l_tcp->m_nb_tile_parts);
-                p_j2k->m_specific_param.m_decoder.m_last_tile_part = 1;
-                return OPJ_FALSE;
-            }
-        }
-        if (l_current_part >= l_num_parts) {
-            /* testcase 451.pdf.SIGSEGV.ce9.3723 */
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "In SOT marker, TPSot (%d) is not valid regards to the current "
-                          "number of tile-part (header) (%d), giving up\n", l_current_part, l_num_parts);
-            p_j2k->m_specific_param.m_decoder.m_last_tile_part = 1;
-            return OPJ_FALSE;
-        }
-        l_tcp->m_nb_tile_parts = l_num_parts;
-    }
-
-    /* If know the number of tile part header we will check if we didn't read the last*/
-    if (l_tcp->m_nb_tile_parts) {
-        if (l_tcp->m_nb_tile_parts == (l_current_part + 1)) {
-            p_j2k->m_specific_param.m_decoder.m_can_decode =
-                1; /* Process the last tile-part header*/
-        }
-    }
-
-    if (!p_j2k->m_specific_param.m_decoder.m_last_tile_part) {
-        /* Keep the size of data to skip after this marker */
-        p_j2k->m_specific_param.m_decoder.m_sot_length = l_tot_len -
-                12; /* SOT_marker_size = 12 */
-    } else {
-        /* FIXME: need to be computed from the number of bytes remaining in the codestream */
-        p_j2k->m_specific_param.m_decoder.m_sot_length = 0;
-    }
-
-    p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_TPH;
-
-    /* Check if the current tile is outside the area we want decode or not corresponding to the tile index*/
-    if (p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec == -1) {
-        p_j2k->m_specific_param.m_decoder.m_skip_data =
-            (l_tile_x < p_j2k->m_specific_param.m_decoder.m_start_tile_x)
-            || (l_tile_x >= p_j2k->m_specific_param.m_decoder.m_end_tile_x)
-            || (l_tile_y < p_j2k->m_specific_param.m_decoder.m_start_tile_y)
-            || (l_tile_y >= p_j2k->m_specific_param.m_decoder.m_end_tile_y);
-    } else {
-        assert(p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec >= 0);
-        p_j2k->m_specific_param.m_decoder.m_skip_data =
-            (p_j2k->m_current_tile_number != (OPJ_UINT32)
-             p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec);
-    }
-
-    /* Index */
-    if (p_j2k->cstr_index) {
-        assert(p_j2k->cstr_index->tile_index != 00);
-        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tileno =
-            p_j2k->m_current_tile_number;
-        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_tpsno =
-            l_current_part;
-
-        if (l_num_parts != 0) {
-            p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].nb_tps =
-                l_num_parts;
-            p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps =
-                l_num_parts;
-
-            if (!p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index) {
-                p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index =
-                    (opj_tp_index_t*)opj_calloc(l_num_parts, sizeof(opj_tp_index_t));
-                if (!p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index) {
-                    opj_event_msg(p_manager, EVT_ERROR,
-                                  "Not enough memory to read SOT marker. Tile index allocation failed\n");
-                    return OPJ_FALSE;
-                }
-            } else {
-                opj_tp_index_t *new_tp_index = (opj_tp_index_t *) opj_realloc(
-                                                   p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index,
-                                                   l_num_parts * sizeof(opj_tp_index_t));
-                if (! new_tp_index) {
-                    opj_free(p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index);
-                    p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index = NULL;
-                    opj_event_msg(p_manager, EVT_ERROR,
-                                  "Not enough memory to read SOT marker. Tile index allocation failed\n");
-                    return OPJ_FALSE;
-                }
-                p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index =
-                    new_tp_index;
-            }
-        } else {
-            /*if (!p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index)*/ {
-
-                if (!p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index) {
-                    p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps = 10;
-                    p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index =
-                        (opj_tp_index_t*)opj_calloc(
-                            p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps,
-                            sizeof(opj_tp_index_t));
-                    if (!p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index) {
-                        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps = 0;
-                        opj_event_msg(p_manager, EVT_ERROR,
-                                      "Not enough memory to read SOT marker. Tile index allocation failed\n");
-                        return OPJ_FALSE;
-                    }
-                }
-
-                if (l_current_part >=
-                        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps) {
-                    opj_tp_index_t *new_tp_index;
-                    p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps =
-                        l_current_part + 1;
-                    new_tp_index = (opj_tp_index_t *) opj_realloc(
-                                       p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index,
-                                       p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps *
-                                       sizeof(opj_tp_index_t));
-                    if (! new_tp_index) {
-                        opj_free(p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index);
-                        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index = NULL;
-                        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps = 0;
-                        opj_event_msg(p_manager, EVT_ERROR,
-                                      "Not enough memory to read SOT marker. Tile index allocation failed\n");
-                        return OPJ_FALSE;
-                    }
-                    p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index =
-                        new_tp_index;
-                }
-            }
-
-        }
-
-    }
-
-    /* FIXME move this onto a separate method to call before reading any SOT, remove part about main_end header, use a index struct inside p_j2k */
-    /* if (p_j2k->cstr_info) {
-       if (l_tcp->first) {
-       if (tileno == 0) {
-       p_j2k->cstr_info->main_head_end = p_stream_tell(p_stream) - 13;
-       }
-
-       p_j2k->cstr_info->tile[tileno].tileno = tileno;
-       p_j2k->cstr_info->tile[tileno].start_pos = p_stream_tell(p_stream) - 12;
-       p_j2k->cstr_info->tile[tileno].end_pos = p_j2k->cstr_info->tile[tileno].start_pos + totlen - 1;
-       p_j2k->cstr_info->tile[tileno].num_tps = numparts;
-
-       if (numparts) {
-       p_j2k->cstr_info->tile[tileno].tp = (opj_tp_info_t *) opj_malloc(numparts * sizeof(opj_tp_info_t));
-       }
-       else {
-       p_j2k->cstr_info->tile[tileno].tp = (opj_tp_info_t *) opj_malloc(10 * sizeof(opj_tp_info_t)); // Fixme (10)
-       }
-       }
-       else {
-       p_j2k->cstr_info->tile[tileno].end_pos += totlen;
-       }
-
-       p_j2k->cstr_info->tile[tileno].tp[partno].tp_start_pos = p_stream_tell(p_stream) - 12;
-       p_j2k->cstr_info->tile[tileno].tp[partno].tp_end_pos =
-       p_j2k->cstr_info->tile[tileno].tp[partno].tp_start_pos + totlen - 1;
-       }*/
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_sod(opj_j2k_t *p_j2k,
-                                  opj_tcd_t * p_tile_coder,
-                                  OPJ_BYTE * p_data,
-                                  OPJ_UINT32 * p_data_written,
-                                  OPJ_UINT32 p_total_data_size,
-                                  const opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager
-                                 )
-{
-    opj_codestream_info_t *l_cstr_info = 00;
-    OPJ_UINT32 l_remaining_data;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    OPJ_UNUSED(p_stream);
-
-    if (p_total_data_size < 4) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Not enough bytes in output buffer to write SOD marker\n");
-        return OPJ_FALSE;
-    }
-
-    opj_write_bytes(p_data, J2K_MS_SOD,
-                    2);                                 /* SOD */
-    p_data += 2;
-
-    /* make room for the EOF marker */
-    l_remaining_data =  p_total_data_size - 4;
-
-    /* update tile coder */
-    p_tile_coder->tp_num =
-        p_j2k->m_specific_param.m_encoder.m_current_poc_tile_part_number ;
-    p_tile_coder->cur_tp_num =
-        p_j2k->m_specific_param.m_encoder.m_current_tile_part_number;
-
-    /* INDEX >> */
-    /* TODO mergeV2: check this part which use cstr_info */
-    /*l_cstr_info = p_j2k->cstr_info;
-    if (l_cstr_info) {
-            if (!p_j2k->m_specific_param.m_encoder.m_current_tile_part_number ) {
-                    //TODO cstr_info->tile[p_j2k->m_current_tile_number].end_header = p_stream_tell(p_stream) + p_j2k->pos_correction - 1;
-                    l_cstr_info->tile[p_j2k->m_current_tile_number].tileno = p_j2k->m_current_tile_number;
-            }
-            else {*/
-    /*
-    TODO
-    if
-            (cstr_info->tile[p_j2k->m_current_tile_number].packet[cstr_info->packno - 1].end_pos < p_stream_tell(p_stream))
-    {
-            cstr_info->tile[p_j2k->m_current_tile_number].packet[cstr_info->packno].start_pos = p_stream_tell(p_stream);
-    }*/
-    /*}*/
-    /* UniPG>> */
-#ifdef USE_JPWL
-    /* update markers struct */
-    /*OPJ_BOOL res = j2k_add_marker(p_j2k->cstr_info, J2K_MS_SOD, p_j2k->sod_start, 2);
-    */
-    assert(0 && "TODO");
-#endif /* USE_JPWL */
-    /* <<UniPG */
-    /*}*/
-    /* << INDEX */
-
-    if (p_j2k->m_specific_param.m_encoder.m_current_tile_part_number == 0) {
-        p_tile_coder->tcd_image->tiles->packno = 0;
-#ifdef deadcode
-        if (l_cstr_info) {
-            l_cstr_info->packno = 0;
-        }
-#endif
-    }
-
-    *p_data_written = 0;
-
-    if (! opj_tcd_encode_tile(p_tile_coder, p_j2k->m_current_tile_number, p_data,
-                              p_data_written, l_remaining_data, l_cstr_info,
-                              p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Cannot encode tile\n");
-        return OPJ_FALSE;
-    }
-
-    *p_data_written += 2;
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_read_sod(opj_j2k_t *p_j2k,
-                                 opj_stream_private_t *p_stream,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    OPJ_SIZE_T l_current_read_size;
-    opj_codestream_index_t * l_cstr_index = 00;
-    OPJ_BYTE ** l_current_data = 00;
-    opj_tcp_t * l_tcp = 00;
-    OPJ_UINT32 * l_tile_len = 00;
-    OPJ_BOOL l_sot_length_pb_detected = OPJ_FALSE;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    l_tcp = &(p_j2k->m_cp.tcps[p_j2k->m_current_tile_number]);
-
-    if (p_j2k->m_specific_param.m_decoder.m_last_tile_part) {
-        /* opj_stream_get_number_byte_left returns OPJ_OFF_T
-        // but we are in the last tile part,
-        // so its result will fit on OPJ_UINT32 unless we find
-        // a file with a single tile part of more than 4 GB...*/
-        p_j2k->m_specific_param.m_decoder.m_sot_length = (OPJ_UINT32)(
-                    opj_stream_get_number_byte_left(p_stream) - 2);
-    } else {
-        /* Check to avoid pass the limit of OPJ_UINT32 */
-        if (p_j2k->m_specific_param.m_decoder.m_sot_length >= 2) {
-            p_j2k->m_specific_param.m_decoder.m_sot_length -= 2;
-        } else {
-            /* MSD: case commented to support empty SOT marker (PHR data) */
-        }
-    }
-
-    l_current_data = &(l_tcp->m_data);
-    l_tile_len = &l_tcp->m_data_size;
-
-    /* Patch to support new PHR data */
-    if (p_j2k->m_specific_param.m_decoder.m_sot_length) {
-        /* If we are here, we'll try to read the data after allocation */
-        /* Check enough bytes left in stream before allocation */
-        if ((OPJ_OFF_T)p_j2k->m_specific_param.m_decoder.m_sot_length >
-                opj_stream_get_number_byte_left(p_stream)) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Tile part length size inconsistent with stream length\n");
-            return OPJ_FALSE;
-        }
-        if (p_j2k->m_specific_param.m_decoder.m_sot_length >
-                UINT_MAX - OPJ_COMMON_CBLK_DATA_EXTRA) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "p_j2k->m_specific_param.m_decoder.m_sot_length > "
-                          "UINT_MAX - OPJ_COMMON_CBLK_DATA_EXTRA");
-            return OPJ_FALSE;
-        }
-        /* Add a margin of OPJ_COMMON_CBLK_DATA_EXTRA to the allocation we */
-        /* do so that opj_mqc_init_dec_common() can safely add a synthetic */
-        /* 0xFFFF marker. */
-        if (! *l_current_data) {
-            /* LH: oddly enough, in this path, l_tile_len!=0.
-             * TODO: If this was consistent, we could simplify the code to only use realloc(), as realloc(0,...) default to malloc(0,...).
-             */
-            *l_current_data = (OPJ_BYTE*) opj_malloc(
-                                  p_j2k->m_specific_param.m_decoder.m_sot_length + OPJ_COMMON_CBLK_DATA_EXTRA);
-        } else {
-            OPJ_BYTE *l_new_current_data;
-            if (*l_tile_len > UINT_MAX - OPJ_COMMON_CBLK_DATA_EXTRA -
-                    p_j2k->m_specific_param.m_decoder.m_sot_length) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "*l_tile_len > UINT_MAX - OPJ_COMMON_CBLK_DATA_EXTRA - "
-                              "p_j2k->m_specific_param.m_decoder.m_sot_length");
-                return OPJ_FALSE;
-            }
-
-            l_new_current_data = (OPJ_BYTE *) opj_realloc(*l_current_data,
-                                 *l_tile_len + p_j2k->m_specific_param.m_decoder.m_sot_length +
-                                 OPJ_COMMON_CBLK_DATA_EXTRA);
-            if (! l_new_current_data) {
-                opj_free(*l_current_data);
-                /*nothing more is done as l_current_data will be set to null, and just
-                  afterward we enter in the error path
-                  and the actual tile_len is updated (committed) at the end of the
-                  function. */
-            }
-            *l_current_data = l_new_current_data;
-        }
-
-        if (*l_current_data == 00) {
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to decode tile\n");
-            return OPJ_FALSE;
-        }
-    } else {
-        l_sot_length_pb_detected = OPJ_TRUE;
-    }
-
-    /* Index */
-    l_cstr_index = p_j2k->cstr_index;
-    if (l_cstr_index) {
-        OPJ_OFF_T l_current_pos = opj_stream_tell(p_stream) - 2;
-
-        OPJ_UINT32 l_current_tile_part =
-            l_cstr_index->tile_index[p_j2k->m_current_tile_number].current_tpsno;
-        l_cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index[l_current_tile_part].end_header
-            =
-                l_current_pos;
-        l_cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index[l_current_tile_part].end_pos
-            =
-                l_current_pos + p_j2k->m_specific_param.m_decoder.m_sot_length + 2;
-
-        if (OPJ_FALSE == opj_j2k_add_tlmarker(p_j2k->m_current_tile_number,
-                                              l_cstr_index,
-                                              J2K_MS_SOD,
-                                              l_current_pos,
-                                              p_j2k->m_specific_param.m_decoder.m_sot_length + 2)) {
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to add tl marker\n");
-            return OPJ_FALSE;
-        }
-
-        /*l_cstr_index->packno = 0;*/
-    }
-
-    /* Patch to support new PHR data */
-    if (!l_sot_length_pb_detected) {
-        l_current_read_size = opj_stream_read_data(
-                                  p_stream,
-                                  *l_current_data + *l_tile_len,
-                                  p_j2k->m_specific_param.m_decoder.m_sot_length,
-                                  p_manager);
-    } else {
-        l_current_read_size = 0;
-    }
-
-    if (l_current_read_size != p_j2k->m_specific_param.m_decoder.m_sot_length) {
-        p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_NEOC;
-    } else {
-        p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_TPHSOT;
-    }
-
-    *l_tile_len += (OPJ_UINT32)l_current_read_size;
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_rgn(opj_j2k_t *p_j2k,
-                                  OPJ_UINT32 p_tile_no,
-                                  OPJ_UINT32 p_comp_no,
-                                  OPJ_UINT32 nb_comps,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager
-                                 )
-{
-    OPJ_BYTE * l_current_data = 00;
-    OPJ_UINT32 l_rgn_size;
-    opj_cp_t *l_cp = 00;
-    opj_tcp_t *l_tcp = 00;
-    opj_tccp_t *l_tccp = 00;
-    OPJ_UINT32 l_comp_room;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    l_cp = &(p_j2k->m_cp);
-    l_tcp = &l_cp->tcps[p_tile_no];
-    l_tccp = &l_tcp->tccps[p_comp_no];
-
-    if (nb_comps <= 256) {
-        l_comp_room = 1;
-    } else {
-        l_comp_room = 2;
-    }
-
-    l_rgn_size = 6 + l_comp_room;
-
-    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
-
-    opj_write_bytes(l_current_data, J2K_MS_RGN,
-                    2);                                   /* RGN  */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, l_rgn_size - 2,
-                    2);                                 /* Lrgn */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, p_comp_no,
-                    l_comp_room);                          /* Crgn */
-    l_current_data += l_comp_room;
-
-    opj_write_bytes(l_current_data, 0,
-                    1);                                           /* Srgn */
-    ++l_current_data;
-
-    opj_write_bytes(l_current_data, (OPJ_UINT32)l_tccp->roishift,
-                    1);                            /* SPrgn */
-    ++l_current_data;
-
-    if (opj_stream_write_data(p_stream,
-                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_rgn_size,
-                              p_manager) != l_rgn_size) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_eoc(opj_j2k_t *p_j2k,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager
-                                 )
-{
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    opj_write_bytes(p_j2k->m_specific_param.m_encoder.m_header_tile_data,
-                    J2K_MS_EOC, 2);                                    /* EOC */
-
-    /* UniPG>> */
-#ifdef USE_JPWL
-    /* update markers struct */
-    /*
-    OPJ_BOOL res = j2k_add_marker(p_j2k->cstr_info, J2K_MS_EOC, p_stream_tell(p_stream) - 2, 2);
-    */
-#endif /* USE_JPWL */
-
-    if (opj_stream_write_data(p_stream,
-                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, 2, p_manager) != 2) {
-        return OPJ_FALSE;
-    }
-
-    if (! opj_stream_flush(p_stream, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-/**
- * Reads a RGN marker (Region Of Interest)
- *
- * @param       p_header_data   the data contained in the POC box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the POC marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_rgn(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    OPJ_UINT32 l_nb_comp;
-    opj_image_t * l_image = 00;
-
-    opj_cp_t *l_cp = 00;
-    opj_tcp_t *l_tcp = 00;
-    OPJ_UINT32 l_comp_room, l_comp_no, l_roi_sty;
-
-    /* preconditions*/
-    assert(p_header_data != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    l_image = p_j2k->m_private_image;
-    l_nb_comp = l_image->numcomps;
-
-    if (l_nb_comp <= 256) {
-        l_comp_room = 1;
-    } else {
-        l_comp_room = 2;
-    }
-
-    if (p_header_size != 2 + l_comp_room) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading RGN marker\n");
-        return OPJ_FALSE;
-    }
-
-    l_cp = &(p_j2k->m_cp);
-    l_tcp = (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH) ?
-            &l_cp->tcps[p_j2k->m_current_tile_number] :
-            p_j2k->m_specific_param.m_decoder.m_default_tcp;
-
-    opj_read_bytes(p_header_data, &l_comp_no, l_comp_room);         /* Crgn */
-    p_header_data += l_comp_room;
-    opj_read_bytes(p_header_data, &l_roi_sty,
-                   1);                                     /* Srgn */
-    ++p_header_data;
-
-#ifdef USE_JPWL
-    if (l_cp->correct) {
-        /* totlen is negative or larger than the bytes left!!! */
-        if (l_comp_room >= l_nb_comp) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "JPWL: bad component number in RGN (%d when there are only %d)\n",
-                          l_comp_room, l_nb_comp);
-            if (!JPWL_ASSUME) {
-                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
-                return OPJ_FALSE;
-            }
-        }
-    };
-#endif /* USE_JPWL */
-
-    /* testcase 3635.pdf.asan.77.2930 */
-    if (l_comp_no >= l_nb_comp) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "bad component number in RGN (%d when there are only %d)\n",
-                      l_comp_no, l_nb_comp);
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(p_header_data,
-                   (OPJ_UINT32 *)(&(l_tcp->tccps[l_comp_no].roishift)), 1);  /* SPrgn */
-    ++p_header_data;
-
-    return OPJ_TRUE;
-
-}
-
-static OPJ_FLOAT32 opj_j2k_get_tp_stride(opj_tcp_t * p_tcp)
-{
-    return (OPJ_FLOAT32)((p_tcp->m_nb_tile_parts - 1) * 14);
-}
-
-static OPJ_FLOAT32 opj_j2k_get_default_stride(opj_tcp_t * p_tcp)
-{
-    (void)p_tcp;
-    return 0;
-}
-
-static OPJ_BOOL opj_j2k_update_rates(opj_j2k_t *p_j2k,
-                                     opj_stream_private_t *p_stream,
-                                     opj_event_mgr_t * p_manager)
-{
-    opj_cp_t * l_cp = 00;
-    opj_image_t * l_image = 00;
-    opj_tcp_t * l_tcp = 00;
-    opj_image_comp_t * l_img_comp = 00;
-
-    OPJ_UINT32 i, j, k;
-    OPJ_INT32 l_x0, l_y0, l_x1, l_y1;
-    OPJ_FLOAT32 * l_rates = 0;
-    OPJ_FLOAT32 l_sot_remove;
-    OPJ_UINT32 l_bits_empty, l_size_pixel;
-    OPJ_UINT32 l_tile_size = 0;
-    OPJ_UINT32 l_last_res;
-    OPJ_FLOAT32(* l_tp_stride_func)(opj_tcp_t *) = 00;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    OPJ_UNUSED(p_manager);
-
-    l_cp = &(p_j2k->m_cp);
-    l_image = p_j2k->m_private_image;
-    l_tcp = l_cp->tcps;
-
-    l_bits_empty = 8 * l_image->comps->dx * l_image->comps->dy;
-    l_size_pixel = l_image->numcomps * l_image->comps->prec;
-    l_sot_remove = (OPJ_FLOAT32) opj_stream_tell(p_stream) / (OPJ_FLOAT32)(
-                       l_cp->th * l_cp->tw);
-
-    if (l_cp->m_specific_param.m_enc.m_tp_on) {
-        l_tp_stride_func = opj_j2k_get_tp_stride;
-    } else {
-        l_tp_stride_func = opj_j2k_get_default_stride;
-    }
-
-    for (i = 0; i < l_cp->th; ++i) {
-        for (j = 0; j < l_cp->tw; ++j) {
-            OPJ_FLOAT32 l_offset = (OPJ_FLOAT32)(*l_tp_stride_func)(l_tcp) /
-                                   (OPJ_FLOAT32)l_tcp->numlayers;
-
-            /* 4 borders of the tile rescale on the image if necessary */
-            l_x0 = opj_int_max((OPJ_INT32)(l_cp->tx0 + j * l_cp->tdx),
-                               (OPJ_INT32)l_image->x0);
-            l_y0 = opj_int_max((OPJ_INT32)(l_cp->ty0 + i * l_cp->tdy),
-                               (OPJ_INT32)l_image->y0);
-            l_x1 = opj_int_min((OPJ_INT32)(l_cp->tx0 + (j + 1) * l_cp->tdx),
-                               (OPJ_INT32)l_image->x1);
-            l_y1 = opj_int_min((OPJ_INT32)(l_cp->ty0 + (i + 1) * l_cp->tdy),
-                               (OPJ_INT32)l_image->y1);
-
-            l_rates = l_tcp->rates;
-
-            /* Modification of the RATE >> */
-            if (*l_rates > 0.0f) {
-                *l_rates = (((OPJ_FLOAT32)(l_size_pixel * (OPJ_UINT32)(l_x1 - l_x0) *
-                                           (OPJ_UINT32)(l_y1 - l_y0)))
-                            /
-                            ((*l_rates) * (OPJ_FLOAT32)l_bits_empty)
-                           )
-                           -
-                           l_offset;
-            }
-
-            ++l_rates;
-
-            for (k = 1; k < l_tcp->numlayers; ++k) {
-                if (*l_rates > 0.0f) {
-                    *l_rates = (((OPJ_FLOAT32)(l_size_pixel * (OPJ_UINT32)(l_x1 - l_x0) *
-                                               (OPJ_UINT32)(l_y1 - l_y0)))
-                                /
-                                ((*l_rates) * (OPJ_FLOAT32)l_bits_empty)
-                               )
-                               -
-                               l_offset;
-                }
-
-                ++l_rates;
-            }
-
-            ++l_tcp;
-
-        }
-    }
-
-    l_tcp = l_cp->tcps;
-
-    for (i = 0; i < l_cp->th; ++i) {
-        for (j = 0; j < l_cp->tw; ++j) {
-            l_rates = l_tcp->rates;
-
-            if (*l_rates > 0.0f) {
-                *l_rates -= l_sot_remove;
-
-                if (*l_rates < 30.0f) {
-                    *l_rates = 30.0f;
-                }
-            }
-
-            ++l_rates;
-
-            l_last_res = l_tcp->numlayers - 1;
-
-            for (k = 1; k < l_last_res; ++k) {
-
-                if (*l_rates > 0.0f) {
-                    *l_rates -= l_sot_remove;
-
-                    if (*l_rates < * (l_rates - 1) + 10.0f) {
-                        *l_rates  = (*(l_rates - 1)) + 20.0f;
-                    }
-                }
-
-                ++l_rates;
-            }
-
-            if (*l_rates > 0.0f) {
-                *l_rates -= (l_sot_remove + 2.f);
-
-                if (*l_rates < * (l_rates - 1) + 10.0f) {
-                    *l_rates  = (*(l_rates - 1)) + 20.0f;
-                }
-            }
-
-            ++l_tcp;
-        }
-    }
-
-    l_img_comp = l_image->comps;
-    l_tile_size = 0;
-
-    for (i = 0; i < l_image->numcomps; ++i) {
-        l_tile_size += (opj_uint_ceildiv(l_cp->tdx, l_img_comp->dx)
-                        *
-                        opj_uint_ceildiv(l_cp->tdy, l_img_comp->dy)
-                        *
-                        l_img_comp->prec
-                       );
-
-        ++l_img_comp;
-    }
-
-    /* TODO: where does this magic value come from ? */
-    /* This used to be 1.3 / 8, but with random data and very small code */
-    /* block sizes, this is not enough. For example with */
-    /* bin/test_tile_encoder 1 256 256 32 32 8 0 reversible_with_precinct.j2k 4 4 3 0 0 1 16 16 */
-    /* TODO revise this to take into account the overhead linked to the */
-    /* number of packets and number of code blocks in packets */
-    l_tile_size = (OPJ_UINT32)(l_tile_size * 1.4 / 8);
-
-    /* Arbitrary amount to make the following work: */
-    /* bin/test_tile_encoder 1 256 256 17 16 8 0 reversible_no_precinct.j2k 4 4 3 0 0 1 */
-    l_tile_size += 500;
-
-    l_tile_size += opj_j2k_get_specific_header_sizes(p_j2k);
-
-    p_j2k->m_specific_param.m_encoder.m_encoded_tile_size = l_tile_size;
-    p_j2k->m_specific_param.m_encoder.m_encoded_tile_data =
-        (OPJ_BYTE *) opj_malloc(p_j2k->m_specific_param.m_encoder.m_encoded_tile_size);
-    if (p_j2k->m_specific_param.m_encoder.m_encoded_tile_data == 00) {
-        return OPJ_FALSE;
-    }
-
-    if (OPJ_IS_CINEMA(l_cp->rsiz)) {
-        p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer =
-            (OPJ_BYTE *) opj_malloc(5 *
-                                    p_j2k->m_specific_param.m_encoder.m_total_tile_parts);
-        if (! p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer) {
-            return OPJ_FALSE;
-        }
-
-        p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current =
-            p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer;
-    }
-
-    return OPJ_TRUE;
-}
-
-#if 0
-static OPJ_BOOL opj_j2k_read_eoc(opj_j2k_t *p_j2k,
-                                 opj_stream_private_t *p_stream,
-                                 opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32 i;
-    opj_tcd_t * l_tcd = 00;
-    OPJ_UINT32 l_nb_tiles;
-    opj_tcp_t * l_tcp = 00;
-    OPJ_BOOL l_success;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    l_nb_tiles = p_j2k->m_cp.th * p_j2k->m_cp.tw;
-    l_tcp = p_j2k->m_cp.tcps;
-
-    l_tcd = opj_tcd_create(OPJ_TRUE);
-    if (l_tcd == 00) {
-        opj_event_msg(p_manager, EVT_ERROR, "Cannot decode tile, memory error\n");
-        return OPJ_FALSE;
-    }
-
-    for (i = 0; i < l_nb_tiles; ++i) {
-        if (l_tcp->m_data) {
-            if (! opj_tcd_init_decode_tile(l_tcd, i)) {
-                opj_tcd_destroy(l_tcd);
-                opj_event_msg(p_manager, EVT_ERROR, "Cannot decode tile, memory error\n");
-                return OPJ_FALSE;
-            }
-
-            l_success = opj_tcd_decode_tile(l_tcd, l_tcp->m_data, l_tcp->m_data_size, i,
-                                            p_j2k->cstr_index);
-            /* cleanup */
-
-            if (! l_success) {
-                p_j2k->m_specific_param.m_decoder.m_state |= J2K_STATE_ERR;
-                break;
-            }
-        }
-
-        opj_j2k_tcp_destroy(l_tcp);
-        ++l_tcp;
-    }
-
-    opj_tcd_destroy(l_tcd);
-    return OPJ_TRUE;
-}
-#endif
-
-static OPJ_BOOL opj_j2k_get_end_header(opj_j2k_t *p_j2k,
-                                       struct opj_stream_private *p_stream,
-                                       struct opj_event_mgr * p_manager)
-{
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    OPJ_UNUSED(p_manager);
-
-    p_j2k->cstr_index->main_head_end = opj_stream_tell(p_stream);
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_mct_data_group(opj_j2k_t *p_j2k,
-        struct opj_stream_private *p_stream,
-        struct opj_event_mgr * p_manager)
-{
-    OPJ_UINT32 i;
-    opj_simple_mcc_decorrelation_data_t * l_mcc_record;
-    opj_mct_data_t * l_mct_record;
-    opj_tcp_t * l_tcp;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_stream != 00);
-    assert(p_manager != 00);
-
-    if (! opj_j2k_write_cbd(p_j2k, p_stream, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    l_tcp = &(p_j2k->m_cp.tcps[p_j2k->m_current_tile_number]);
-    l_mct_record = l_tcp->m_mct_records;
-
-    for (i = 0; i < l_tcp->m_nb_mct_records; ++i) {
-
-        if (! opj_j2k_write_mct_record(p_j2k, l_mct_record, p_stream, p_manager)) {
-            return OPJ_FALSE;
-        }
-
-        ++l_mct_record;
-    }
-
-    l_mcc_record = l_tcp->m_mcc_records;
-
-    for (i = 0; i < l_tcp->m_nb_mcc_records; ++i) {
-
-        if (! opj_j2k_write_mcc_record(p_j2k, l_mcc_record, p_stream, p_manager)) {
-            return OPJ_FALSE;
-        }
-
-        ++l_mcc_record;
-    }
-
-    if (! opj_j2k_write_mco(p_j2k, p_stream, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_all_coc(
-    opj_j2k_t *p_j2k,
-    struct opj_stream_private *p_stream,
-    struct opj_event_mgr * p_manager)
-{
-    OPJ_UINT32 compno;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    for (compno = 1; compno < p_j2k->m_private_image->numcomps; ++compno) {
-        /* cod is first component of first tile */
-        if (! opj_j2k_compare_coc(p_j2k, 0, compno)) {
-            if (! opj_j2k_write_coc(p_j2k, compno, p_stream, p_manager)) {
-                return OPJ_FALSE;
-            }
-        }
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_all_qcc(
-    opj_j2k_t *p_j2k,
-    struct opj_stream_private *p_stream,
-    struct opj_event_mgr * p_manager)
-{
-    OPJ_UINT32 compno;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    for (compno = 1; compno < p_j2k->m_private_image->numcomps; ++compno) {
-        /* qcd is first component of first tile */
-        if (! opj_j2k_compare_qcc(p_j2k, 0, compno)) {
-            if (! opj_j2k_write_qcc(p_j2k, compno, p_stream, p_manager)) {
-                return OPJ_FALSE;
-            }
-        }
-    }
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_regions(opj_j2k_t *p_j2k,
-                                      struct opj_stream_private *p_stream,
-                                      struct opj_event_mgr * p_manager)
-{
-    OPJ_UINT32 compno;
-    const opj_tccp_t *l_tccp = 00;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    l_tccp = p_j2k->m_cp.tcps->tccps;
-
-    for (compno = 0; compno < p_j2k->m_private_image->numcomps; ++compno)  {
-        if (l_tccp->roishift) {
-
-            if (! opj_j2k_write_rgn(p_j2k, 0, compno, p_j2k->m_private_image->numcomps,
-                                    p_stream, p_manager)) {
-                return OPJ_FALSE;
-            }
-        }
-
-        ++l_tccp;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_epc(opj_j2k_t *p_j2k,
-                                  struct opj_stream_private *p_stream,
-                                  struct opj_event_mgr * p_manager)
-{
-    opj_codestream_index_t * l_cstr_index = 00;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    OPJ_UNUSED(p_manager);
-
-    l_cstr_index = p_j2k->cstr_index;
-    if (l_cstr_index) {
-        l_cstr_index->codestream_size = (OPJ_UINT64)opj_stream_tell(p_stream);
-        /* UniPG>> */
-        /* The following adjustment is done to adjust the codestream size */
-        /* if SOD is not at 0 in the buffer. Useful in case of JP2, where */
-        /* the first bunch of bytes is not in the codestream              */
-        l_cstr_index->codestream_size -= (OPJ_UINT64)l_cstr_index->main_head_start;
-        /* <<UniPG */
-    }
-
-#ifdef USE_JPWL
-    /* preparation of JPWL marker segments */
-#if 0
-    if (cp->epc_on) {
-
-        /* encode according to JPWL */
-        jpwl_encode(p_j2k, p_stream, image);
-
-    }
-#endif
-    assert(0 && "TODO");
-#endif /* USE_JPWL */
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_read_unk(opj_j2k_t *p_j2k,
-                                 opj_stream_private_t *p_stream,
-                                 OPJ_UINT32 *output_marker,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    OPJ_UINT32 l_unknown_marker;
-    const opj_dec_memory_marker_handler_t * l_marker_handler;
-    OPJ_UINT32 l_size_unk = 2;
-
-    /* preconditions*/
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    opj_event_msg(p_manager, EVT_WARNING, "Unknown marker\n");
-
-    for (;;) {
-        /* Try to read 2 bytes (the next marker ID) from stream and copy them into the buffer*/
-        if (opj_stream_read_data(p_stream,
-                                 p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
-            opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
-            return OPJ_FALSE;
-        }
-
-        /* read 2 bytes as the new marker ID*/
-        opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data,
-                       &l_unknown_marker, 2);
-
-        if (!(l_unknown_marker < 0xff00)) {
-
-            /* Get the marker handler from the marker ID*/
-            l_marker_handler = opj_j2k_get_marker_handler(l_unknown_marker);
-
-            if (!(p_j2k->m_specific_param.m_decoder.m_state & l_marker_handler->states)) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Marker is not compliant with its position\n");
-                return OPJ_FALSE;
-            } else {
-                if (l_marker_handler->id != J2K_MS_UNK) {
-                    /* Add the marker to the codestream index*/
-                    if (l_marker_handler->id != J2K_MS_SOT) {
-                        OPJ_BOOL res = opj_j2k_add_mhmarker(p_j2k->cstr_index, J2K_MS_UNK,
-                                                            (OPJ_UINT32) opj_stream_tell(p_stream) - l_size_unk,
-                                                            l_size_unk);
-                        if (res == OPJ_FALSE) {
-                            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to add mh marker\n");
-                            return OPJ_FALSE;
-                        }
-                    }
-                    break; /* next marker is known and well located */
-                } else {
-                    l_size_unk += 2;
-                }
-            }
-        }
-    }
-
-    *output_marker = l_marker_handler->id ;
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_mct_record(opj_j2k_t *p_j2k,
-        opj_mct_data_t * p_mct_record,
-        struct opj_stream_private *p_stream,
-        struct opj_event_mgr * p_manager)
-{
-    OPJ_UINT32 l_mct_size;
-    OPJ_BYTE * l_current_data = 00;
-    OPJ_UINT32 l_tmp;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    l_mct_size = 10 + p_mct_record->m_data_size;
-
-    if (l_mct_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
-        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
-                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_mct_size);
-        if (! new_header_tile_data) {
-            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write MCT marker\n");
-            return OPJ_FALSE;
-        }
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_mct_size;
-    }
-
-    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
-
-    opj_write_bytes(l_current_data, J2K_MS_MCT,
-                    2);                                   /* MCT */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, l_mct_size - 2,
-                    2);                                 /* Lmct */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, 0,
-                    2);                                                    /* Zmct */
-    l_current_data += 2;
-
-    /* only one marker atm */
-    l_tmp = (p_mct_record->m_index & 0xff) | (p_mct_record->m_array_type << 8) |
-            (p_mct_record->m_element_type << 10);
-
-    opj_write_bytes(l_current_data, l_tmp, 2);
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, 0,
-                    2);                                                    /* Ymct */
-    l_current_data += 2;
-
-    memcpy(l_current_data, p_mct_record->m_data, p_mct_record->m_data_size);
-
-    if (opj_stream_write_data(p_stream,
-                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_mct_size,
-                              p_manager) != l_mct_size) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-/**
- * Reads a MCT marker (Multiple Component Transform)
- *
- * @param       p_header_data   the data contained in the MCT box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the MCT marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_mct(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    OPJ_UINT32 i;
-    opj_tcp_t *l_tcp = 00;
-    OPJ_UINT32 l_tmp;
-    OPJ_UINT32 l_indix;
-    opj_mct_data_t * l_mct_data;
-    OPJ_BOOL new_mct = OPJ_FALSE;
-
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(p_j2k != 00);
-
-    l_tcp = p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH ?
-            &p_j2k->m_cp.tcps[p_j2k->m_current_tile_number] :
-            p_j2k->m_specific_param.m_decoder.m_default_tcp;
-
-    if (p_header_size < 2) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading MCT marker\n");
-        return OPJ_FALSE;
-    }
-
-    /* first marker */
-    opj_read_bytes(p_header_data, &l_tmp, 2);                       /* Zmct */
-    p_header_data += 2;
-    if (l_tmp != 0) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "Cannot take in charge mct data within multiple MCT records\n");
-        return OPJ_TRUE;
-    }
-
-    if (p_header_size <= 6) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading MCT marker\n");
-        return OPJ_FALSE;
-    }
-
-    /* Imct -> no need for other values, take the first, type is double with decorrelation x0000 1101 0000 0000*/
-    opj_read_bytes(p_header_data, &l_tmp, 2);                       /* Imct */
-    p_header_data += 2;
-
-    l_indix = l_tmp & 0xff;
-    l_mct_data = l_tcp->m_mct_records;
-
-    for (i = 0; i < l_tcp->m_nb_mct_records; ++i) {
-        if (l_mct_data->m_index == l_indix) {
-            break;
-        }
-        ++l_mct_data;
-    }
-
-    /* NOT FOUND */
-    if (i == l_tcp->m_nb_mct_records) {
-        if (l_tcp->m_nb_mct_records == l_tcp->m_nb_max_mct_records) {
-            opj_mct_data_t *new_mct_records;
-            l_tcp->m_nb_max_mct_records += OPJ_J2K_MCT_DEFAULT_NB_RECORDS;
-
-            new_mct_records = (opj_mct_data_t *) opj_realloc(l_tcp->m_mct_records,
-                              l_tcp->m_nb_max_mct_records * sizeof(opj_mct_data_t));
-            if (! new_mct_records) {
-                opj_free(l_tcp->m_mct_records);
-                l_tcp->m_mct_records = NULL;
-                l_tcp->m_nb_max_mct_records = 0;
-                l_tcp->m_nb_mct_records = 0;
-                opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read MCT marker\n");
-                return OPJ_FALSE;
-            }
-
-            /* Update m_mcc_records[].m_offset_array and m_decorrelation_array
-             * to point to the new addresses */
-            if (new_mct_records != l_tcp->m_mct_records) {
-                for (i = 0; i < l_tcp->m_nb_mcc_records; ++i) {
-                    opj_simple_mcc_decorrelation_data_t* l_mcc_record =
-                        &(l_tcp->m_mcc_records[i]);
-                    if (l_mcc_record->m_decorrelation_array) {
-                        l_mcc_record->m_decorrelation_array =
-                            new_mct_records +
-                            (l_mcc_record->m_decorrelation_array -
-                             l_tcp->m_mct_records);
-                    }
-                    if (l_mcc_record->m_offset_array) {
-                        l_mcc_record->m_offset_array =
-                            new_mct_records +
-                            (l_mcc_record->m_offset_array -
-                             l_tcp->m_mct_records);
-                    }
-                }
-            }
-
-            l_tcp->m_mct_records = new_mct_records;
-            l_mct_data = l_tcp->m_mct_records + l_tcp->m_nb_mct_records;
-            memset(l_mct_data, 0, (l_tcp->m_nb_max_mct_records - l_tcp->m_nb_mct_records) *
-                   sizeof(opj_mct_data_t));
-        }
-
-        l_mct_data = l_tcp->m_mct_records + l_tcp->m_nb_mct_records;
-        new_mct = OPJ_TRUE;
-    }
-
-    if (l_mct_data->m_data) {
-        opj_free(l_mct_data->m_data);
-        l_mct_data->m_data = 00;
-        l_mct_data->m_data_size = 0;
-    }
-
-    l_mct_data->m_index = l_indix;
-    l_mct_data->m_array_type = (J2K_MCT_ARRAY_TYPE)((l_tmp  >> 8) & 3);
-    l_mct_data->m_element_type = (J2K_MCT_ELEMENT_TYPE)((l_tmp  >> 10) & 3);
-
-    opj_read_bytes(p_header_data, &l_tmp, 2);                       /* Ymct */
-    p_header_data += 2;
-    if (l_tmp != 0) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "Cannot take in charge multiple MCT markers\n");
-        return OPJ_TRUE;
-    }
-
-    p_header_size -= 6;
-
-    l_mct_data->m_data = (OPJ_BYTE*)opj_malloc(p_header_size);
-    if (! l_mct_data->m_data) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading MCT marker\n");
-        return OPJ_FALSE;
-    }
-    memcpy(l_mct_data->m_data, p_header_data, p_header_size);
-
-    l_mct_data->m_data_size = p_header_size;
-
-    if (new_mct) {
-            ++l_tcp->m_nb_mct_records;
-    }
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_mcc_record(opj_j2k_t *p_j2k,
-        struct opj_simple_mcc_decorrelation_data * p_mcc_record,
-        struct opj_stream_private *p_stream,
-        struct opj_event_mgr * p_manager)
-{
-    OPJ_UINT32 i;
-    OPJ_UINT32 l_mcc_size;
-    OPJ_BYTE * l_current_data = 00;
-    OPJ_UINT32 l_nb_bytes_for_comp;
-    OPJ_UINT32 l_mask;
-    OPJ_UINT32 l_tmcc;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    if (p_mcc_record->m_nb_comps > 255) {
-        l_nb_bytes_for_comp = 2;
-        l_mask = 0x8000;
-    } else {
-        l_nb_bytes_for_comp = 1;
-        l_mask = 0;
-    }
-
-    l_mcc_size = p_mcc_record->m_nb_comps * 2 * l_nb_bytes_for_comp + 19;
-    if (l_mcc_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
-        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
-                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_mcc_size);
-        if (! new_header_tile_data) {
-            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write MCC marker\n");
-            return OPJ_FALSE;
-        }
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_mcc_size;
-    }
-
-    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
-
-    opj_write_bytes(l_current_data, J2K_MS_MCC,
-                    2);                                   /* MCC */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, l_mcc_size - 2,
-                    2);                                 /* Lmcc */
-    l_current_data += 2;
-
-    /* first marker */
-    opj_write_bytes(l_current_data, 0,
-                    2);                                  /* Zmcc */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, p_mcc_record->m_index,
-                    1);                                        /* Imcc -> no need for other values, take the first */
-    ++l_current_data;
-
-    /* only one marker atm */
-    opj_write_bytes(l_current_data, 0,
-                    2);                                  /* Ymcc */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, 1,
-                    2);                                  /* Qmcc -> number of collections -> 1 */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, 0x1,
-                    1);                                /* Xmcci type of component transformation -> array based decorrelation */
-    ++l_current_data;
-
-    opj_write_bytes(l_current_data, p_mcc_record->m_nb_comps | l_mask,
-                    2);  /* Nmcci number of input components involved and size for each component offset = 8 bits */
-    l_current_data += 2;
-
-    for (i = 0; i < p_mcc_record->m_nb_comps; ++i) {
-        opj_write_bytes(l_current_data, i,
-                        l_nb_bytes_for_comp);                          /* Cmccij Component offset*/
-        l_current_data += l_nb_bytes_for_comp;
-    }
-
-    opj_write_bytes(l_current_data, p_mcc_record->m_nb_comps | l_mask,
-                    2);  /* Mmcci number of output components involved and size for each component offset = 8 bits */
-    l_current_data += 2;
-
-    for (i = 0; i < p_mcc_record->m_nb_comps; ++i) {
-        opj_write_bytes(l_current_data, i,
-                        l_nb_bytes_for_comp);                          /* Wmccij Component offset*/
-        l_current_data += l_nb_bytes_for_comp;
-    }
-
-    l_tmcc = ((!p_mcc_record->m_is_irreversible) & 1U) << 16;
-
-    if (p_mcc_record->m_decorrelation_array) {
-        l_tmcc |= p_mcc_record->m_decorrelation_array->m_index;
-    }
-
-    if (p_mcc_record->m_offset_array) {
-        l_tmcc |= ((p_mcc_record->m_offset_array->m_index) << 8);
-    }
-
-    opj_write_bytes(l_current_data, l_tmcc,
-                    3);     /* Tmcci : use MCT defined as number 1 and irreversible array based. */
-    l_current_data += 3;
-
-    if (opj_stream_write_data(p_stream,
-                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_mcc_size,
-                              p_manager) != l_mcc_size) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_read_mcc(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32 i, j;
-    OPJ_UINT32 l_tmp;
-    OPJ_UINT32 l_indix;
-    opj_tcp_t * l_tcp;
-    opj_simple_mcc_decorrelation_data_t * l_mcc_record;
-    opj_mct_data_t * l_mct_data;
-    OPJ_UINT32 l_nb_collections;
-    OPJ_UINT32 l_nb_comps;
-    OPJ_UINT32 l_nb_bytes_by_comp;
-    OPJ_BOOL l_new_mcc = OPJ_FALSE;
-
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    l_tcp = p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH ?
-            &p_j2k->m_cp.tcps[p_j2k->m_current_tile_number] :
-            p_j2k->m_specific_param.m_decoder.m_default_tcp;
-
-    if (p_header_size < 2) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading MCC marker\n");
-        return OPJ_FALSE;
-    }
-
-    /* first marker */
-    opj_read_bytes(p_header_data, &l_tmp, 2);                       /* Zmcc */
-    p_header_data += 2;
-    if (l_tmp != 0) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "Cannot take in charge multiple data spanning\n");
-        return OPJ_TRUE;
-    }
-
-    if (p_header_size < 7) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading MCC marker\n");
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(p_header_data, &l_indix,
-                   1); /* Imcc -> no need for other values, take the first */
-    ++p_header_data;
-
-    l_mcc_record = l_tcp->m_mcc_records;
-
-    for (i = 0; i < l_tcp->m_nb_mcc_records; ++i) {
-        if (l_mcc_record->m_index == l_indix) {
-            break;
-        }
-        ++l_mcc_record;
-    }
-
-    /** NOT FOUND */
-    if (i == l_tcp->m_nb_mcc_records) {
-        if (l_tcp->m_nb_mcc_records == l_tcp->m_nb_max_mcc_records) {
-            opj_simple_mcc_decorrelation_data_t *new_mcc_records;
-            l_tcp->m_nb_max_mcc_records += OPJ_J2K_MCC_DEFAULT_NB_RECORDS;
-
-            new_mcc_records = (opj_simple_mcc_decorrelation_data_t *) opj_realloc(
-                                  l_tcp->m_mcc_records, l_tcp->m_nb_max_mcc_records * sizeof(
-                                      opj_simple_mcc_decorrelation_data_t));
-            if (! new_mcc_records) {
-                opj_free(l_tcp->m_mcc_records);
-                l_tcp->m_mcc_records = NULL;
-                l_tcp->m_nb_max_mcc_records = 0;
-                l_tcp->m_nb_mcc_records = 0;
-                opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read MCC marker\n");
-                return OPJ_FALSE;
-            }
-            l_tcp->m_mcc_records = new_mcc_records;
-            l_mcc_record = l_tcp->m_mcc_records + l_tcp->m_nb_mcc_records;
-            memset(l_mcc_record, 0, (l_tcp->m_nb_max_mcc_records - l_tcp->m_nb_mcc_records)
-                   * sizeof(opj_simple_mcc_decorrelation_data_t));
-        }
-        l_mcc_record = l_tcp->m_mcc_records + l_tcp->m_nb_mcc_records;
-        l_new_mcc = OPJ_TRUE;
-    }
-    l_mcc_record->m_index = l_indix;
-
-    /* only one marker atm */
-    opj_read_bytes(p_header_data, &l_tmp, 2);                       /* Ymcc */
-    p_header_data += 2;
-    if (l_tmp != 0) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "Cannot take in charge multiple data spanning\n");
-        return OPJ_TRUE;
-    }
-
-    opj_read_bytes(p_header_data, &l_nb_collections,
-                   2);                              /* Qmcc -> number of collections -> 1 */
-    p_header_data += 2;
-
-    if (l_nb_collections > 1) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "Cannot take in charge multiple collections\n");
-        return OPJ_TRUE;
-    }
-
-    p_header_size -= 7;
-
-    for (i = 0; i < l_nb_collections; ++i) {
-        if (p_header_size < 3) {
-            opj_event_msg(p_manager, EVT_ERROR, "Error reading MCC marker\n");
-            return OPJ_FALSE;
-        }
-
-        opj_read_bytes(p_header_data, &l_tmp,
-                       1); /* Xmcci type of component transformation -> array based decorrelation */
-        ++p_header_data;
-
-        if (l_tmp != 1) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "Cannot take in charge collections other than array decorrelation\n");
-            return OPJ_TRUE;
-        }
-
-        opj_read_bytes(p_header_data, &l_nb_comps, 2);
-
-        p_header_data += 2;
-        p_header_size -= 3;
-
-        l_nb_bytes_by_comp = 1 + (l_nb_comps >> 15);
-        l_mcc_record->m_nb_comps = l_nb_comps & 0x7fff;
-
-        if (p_header_size < (l_nb_bytes_by_comp * l_mcc_record->m_nb_comps + 2)) {
-            opj_event_msg(p_manager, EVT_ERROR, "Error reading MCC marker\n");
-            return OPJ_FALSE;
-        }
-
-        p_header_size -= (l_nb_bytes_by_comp * l_mcc_record->m_nb_comps + 2);
-
-        for (j = 0; j < l_mcc_record->m_nb_comps; ++j) {
-            opj_read_bytes(p_header_data, &l_tmp,
-                           l_nb_bytes_by_comp);      /* Cmccij Component offset*/
-            p_header_data += l_nb_bytes_by_comp;
-
-            if (l_tmp != j) {
-                opj_event_msg(p_manager, EVT_WARNING,
-                              "Cannot take in charge collections with indix shuffle\n");
-                return OPJ_TRUE;
-            }
-        }
-
-        opj_read_bytes(p_header_data, &l_nb_comps, 2);
-        p_header_data += 2;
-
-        l_nb_bytes_by_comp = 1 + (l_nb_comps >> 15);
-        l_nb_comps &= 0x7fff;
-
-        if (l_nb_comps != l_mcc_record->m_nb_comps) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "Cannot take in charge collections without same number of indixes\n");
-            return OPJ_TRUE;
-        }
-
-        if (p_header_size < (l_nb_bytes_by_comp * l_mcc_record->m_nb_comps + 3)) {
-            opj_event_msg(p_manager, EVT_ERROR, "Error reading MCC marker\n");
-            return OPJ_FALSE;
-        }
-
-        p_header_size -= (l_nb_bytes_by_comp * l_mcc_record->m_nb_comps + 3);
-
-        for (j = 0; j < l_mcc_record->m_nb_comps; ++j) {
-            opj_read_bytes(p_header_data, &l_tmp,
-                           l_nb_bytes_by_comp);      /* Wmccij Component offset*/
-            p_header_data += l_nb_bytes_by_comp;
-
-            if (l_tmp != j) {
-                opj_event_msg(p_manager, EVT_WARNING,
-                              "Cannot take in charge collections with indix shuffle\n");
-                return OPJ_TRUE;
-            }
-        }
-
-        opj_read_bytes(p_header_data, &l_tmp, 3); /* Wmccij Component offset*/
-        p_header_data += 3;
-
-        l_mcc_record->m_is_irreversible = !((l_tmp >> 16) & 1);
-        l_mcc_record->m_decorrelation_array = 00;
-        l_mcc_record->m_offset_array = 00;
-
-        l_indix = l_tmp & 0xff;
-        if (l_indix != 0) {
-            l_mct_data = l_tcp->m_mct_records;
-            for (j = 0; j < l_tcp->m_nb_mct_records; ++j) {
-                if (l_mct_data->m_index == l_indix) {
-                    l_mcc_record->m_decorrelation_array = l_mct_data;
-                    break;
-                }
-                ++l_mct_data;
-            }
-
-            if (l_mcc_record->m_decorrelation_array == 00) {
-                opj_event_msg(p_manager, EVT_ERROR, "Error reading MCC marker\n");
-                return OPJ_FALSE;
-            }
-        }
-
-        l_indix = (l_tmp >> 8) & 0xff;
-        if (l_indix != 0) {
-            l_mct_data = l_tcp->m_mct_records;
-            for (j = 0; j < l_tcp->m_nb_mct_records; ++j) {
-                if (l_mct_data->m_index == l_indix) {
-                    l_mcc_record->m_offset_array = l_mct_data;
-                    break;
-                }
-                ++l_mct_data;
-            }
-
-            if (l_mcc_record->m_offset_array == 00) {
-                opj_event_msg(p_manager, EVT_ERROR, "Error reading MCC marker\n");
-                return OPJ_FALSE;
-            }
-        }
-    }
-
-    if (p_header_size != 0) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading MCC marker\n");
-        return OPJ_FALSE;
-    }
-
-    if (l_new_mcc) {
-        ++l_tcp->m_nb_mcc_records;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_mco(opj_j2k_t *p_j2k,
-                                  struct opj_stream_private *p_stream,
-                                  struct opj_event_mgr * p_manager
-                                 )
-{
-    OPJ_BYTE * l_current_data = 00;
-    OPJ_UINT32 l_mco_size;
-    opj_tcp_t * l_tcp = 00;
-    opj_simple_mcc_decorrelation_data_t * l_mcc_record;
-    OPJ_UINT32 i;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    l_tcp = &(p_j2k->m_cp.tcps[p_j2k->m_current_tile_number]);
-
-    l_mco_size = 5 + l_tcp->m_nb_mcc_records;
-    if (l_mco_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
-
-        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
-                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_mco_size);
-        if (! new_header_tile_data) {
-            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write MCO marker\n");
-            return OPJ_FALSE;
-        }
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_mco_size;
-    }
-    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
-
-
-    opj_write_bytes(l_current_data, J2K_MS_MCO, 2);                 /* MCO */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, l_mco_size - 2, 2);             /* Lmco */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, l_tcp->m_nb_mcc_records,
-                    1);    /* Nmco : only one transform stage*/
-    ++l_current_data;
-
-    l_mcc_record = l_tcp->m_mcc_records;
-    for (i = 0; i < l_tcp->m_nb_mcc_records; ++i) {
-        opj_write_bytes(l_current_data, l_mcc_record->m_index,
-                        1); /* Imco -> use the mcc indicated by 1*/
-        ++l_current_data;
-        ++l_mcc_record;
-    }
-
-    if (opj_stream_write_data(p_stream,
-                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_mco_size,
-                              p_manager) != l_mco_size) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-/**
- * Reads a MCO marker (Multiple Component Transform Ordering)
- *
- * @param       p_header_data   the data contained in the MCO box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the MCO marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_mco(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    OPJ_UINT32 l_tmp, i;
-    OPJ_UINT32 l_nb_stages;
-    opj_tcp_t * l_tcp;
-    opj_tccp_t * l_tccp;
-    opj_image_t * l_image;
-
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    l_image = p_j2k->m_private_image;
-    l_tcp = p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH ?
-            &p_j2k->m_cp.tcps[p_j2k->m_current_tile_number] :
-            p_j2k->m_specific_param.m_decoder.m_default_tcp;
-
-    if (p_header_size < 1) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading MCO marker\n");
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(p_header_data, &l_nb_stages,
-                   1);                         /* Nmco : only one transform stage*/
-    ++p_header_data;
-
-    if (l_nb_stages > 1) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "Cannot take in charge multiple transformation stages.\n");
-        return OPJ_TRUE;
-    }
-
-    if (p_header_size != l_nb_stages + 1) {
-        opj_event_msg(p_manager, EVT_WARNING, "Error reading MCO marker\n");
-        return OPJ_FALSE;
-    }
-
-    l_tccp = l_tcp->tccps;
-
-    for (i = 0; i < l_image->numcomps; ++i) {
-        l_tccp->m_dc_level_shift = 0;
-        ++l_tccp;
-    }
-
-    if (l_tcp->m_mct_decoding_matrix) {
-        opj_free(l_tcp->m_mct_decoding_matrix);
-        l_tcp->m_mct_decoding_matrix = 00;
-    }
-
-    for (i = 0; i < l_nb_stages; ++i) {
-        opj_read_bytes(p_header_data, &l_tmp, 1);
-        ++p_header_data;
-
-        if (! opj_j2k_add_mct(l_tcp, p_j2k->m_private_image, l_tmp)) {
-            return OPJ_FALSE;
-        }
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_add_mct(opj_tcp_t * p_tcp, opj_image_t * p_image,
-                                OPJ_UINT32 p_index)
-{
-    OPJ_UINT32 i;
-    opj_simple_mcc_decorrelation_data_t * l_mcc_record;
-    opj_mct_data_t * l_deco_array, * l_offset_array;
-    OPJ_UINT32 l_data_size, l_mct_size, l_offset_size;
-    OPJ_UINT32 l_nb_elem;
-    OPJ_UINT32 * l_offset_data, * l_current_offset_data;
-    opj_tccp_t * l_tccp;
-
-    /* preconditions */
-    assert(p_tcp != 00);
-
-    l_mcc_record = p_tcp->m_mcc_records;
-
-    for (i = 0; i < p_tcp->m_nb_mcc_records; ++i) {
-        if (l_mcc_record->m_index == p_index) {
-            break;
-        }
-    }
-
-    if (i == p_tcp->m_nb_mcc_records) {
-        /** element discarded **/
-        return OPJ_TRUE;
-    }
-
-    if (l_mcc_record->m_nb_comps != p_image->numcomps) {
-        /** do not support number of comps != image */
-        return OPJ_TRUE;
-    }
-
-    l_deco_array = l_mcc_record->m_decorrelation_array;
-
-    if (l_deco_array) {
-        l_data_size = MCT_ELEMENT_SIZE[l_deco_array->m_element_type] * p_image->numcomps
-                      * p_image->numcomps;
-        if (l_deco_array->m_data_size != l_data_size || ! l_deco_array->m_data) {
-            return OPJ_FALSE;
-        }
-
-        l_nb_elem = p_image->numcomps * p_image->numcomps;
-        l_mct_size = l_nb_elem * (OPJ_UINT32)sizeof(OPJ_FLOAT32);
-        p_tcp->m_mct_decoding_matrix = (OPJ_FLOAT32*)opj_malloc(l_mct_size);
-
-        if (! p_tcp->m_mct_decoding_matrix) {
-            return OPJ_FALSE;
-        }
-
-        j2k_mct_read_functions_to_float[l_deco_array->m_element_type](
-            l_deco_array->m_data, p_tcp->m_mct_decoding_matrix, l_nb_elem);
-    }
-
-    l_offset_array = l_mcc_record->m_offset_array;
-
-    if (l_offset_array) {
-        l_data_size = MCT_ELEMENT_SIZE[l_offset_array->m_element_type] *
-                      p_image->numcomps;
-        if (l_offset_array->m_data_size != l_data_size || ! l_offset_array->m_data) {
-            return OPJ_FALSE;
-        }
-
-        l_nb_elem = p_image->numcomps;
-        l_offset_size = l_nb_elem * (OPJ_UINT32)sizeof(OPJ_UINT32);
-        l_offset_data = (OPJ_UINT32*)opj_malloc(l_offset_size);
-
-        if (! l_offset_data) {
-            return OPJ_FALSE;
-        }
-
-        j2k_mct_read_functions_to_int32[l_offset_array->m_element_type](
-            l_offset_array->m_data, l_offset_data, l_nb_elem);
-
-        l_tccp = p_tcp->tccps;
-        l_current_offset_data = l_offset_data;
-
-        for (i = 0; i < p_image->numcomps; ++i) {
-            l_tccp->m_dc_level_shift = (OPJ_INT32) * (l_current_offset_data++);
-            ++l_tccp;
-        }
-
-        opj_free(l_offset_data);
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_cbd(opj_j2k_t *p_j2k,
-                                  struct opj_stream_private *p_stream,
-                                  struct opj_event_mgr * p_manager)
-{
-    OPJ_UINT32 i;
-    OPJ_UINT32 l_cbd_size;
-    OPJ_BYTE * l_current_data = 00;
-    opj_image_t *l_image = 00;
-    opj_image_comp_t * l_comp = 00;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    l_image = p_j2k->m_private_image;
-    l_cbd_size = 6 + p_j2k->m_private_image->numcomps;
-
-    if (l_cbd_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
-        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
-                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_cbd_size);
-        if (! new_header_tile_data) {
-            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write CBD marker\n");
-            return OPJ_FALSE;
-        }
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_cbd_size;
-    }
-
-    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
-
-    opj_write_bytes(l_current_data, J2K_MS_CBD, 2);                 /* CBD */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, l_cbd_size - 2, 2);             /* L_CBD */
-    l_current_data += 2;
-
-    opj_write_bytes(l_current_data, l_image->numcomps, 2);          /* Ncbd */
-    l_current_data += 2;
-
-    l_comp = l_image->comps;
-
-    for (i = 0; i < l_image->numcomps; ++i) {
-        opj_write_bytes(l_current_data, (l_comp->sgnd << 7) | (l_comp->prec - 1),
-                        1);           /* Component bit depth */
-        ++l_current_data;
-
-        ++l_comp;
-    }
-
-    if (opj_stream_write_data(p_stream,
-                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_cbd_size,
-                              p_manager) != l_cbd_size) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-/**
- * Reads a CBD marker (Component bit depth definition)
- * @param       p_header_data   the data contained in the CBD box.
- * @param       p_j2k                   the jpeg2000 codec.
- * @param       p_header_size   the size of the data contained in the CBD marker.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_read_cbd(opj_j2k_t *p_j2k,
-                                 OPJ_BYTE * p_header_data,
-                                 OPJ_UINT32 p_header_size,
-                                 opj_event_mgr_t * p_manager
-                                )
-{
-    OPJ_UINT32 l_nb_comp, l_num_comp;
-    OPJ_UINT32 l_comp_def;
-    OPJ_UINT32 i;
-    opj_image_comp_t * l_comp = 00;
-
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    l_num_comp = p_j2k->m_private_image->numcomps;
-
-    if (p_header_size != (p_j2k->m_private_image->numcomps + 2)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Crror reading CBD marker\n");
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(p_header_data, &l_nb_comp,
-                   2);                           /* Ncbd */
-    p_header_data += 2;
-
-    if (l_nb_comp != l_num_comp) {
-        opj_event_msg(p_manager, EVT_ERROR, "Crror reading CBD marker\n");
-        return OPJ_FALSE;
-    }
-
-    l_comp = p_j2k->m_private_image->comps;
-    for (i = 0; i < l_num_comp; ++i) {
-        opj_read_bytes(p_header_data, &l_comp_def,
-                       1);                  /* Component bit depth */
-        ++p_header_data;
-        l_comp->sgnd = (l_comp_def >> 7) & 1;
-        l_comp->prec = (l_comp_def & 0x7f) + 1;
-
-        if (l_comp->prec > 31) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Invalid values for comp = %d : prec=%u (should be between 1 and 38 according to the JPEG2000 norm. OpenJpeg only supports up to 31)\n",
-                          i, l_comp->prec);
-            return OPJ_FALSE;
-        }
-        ++l_comp;
-    }
-
-    return OPJ_TRUE;
-}
-
-/* ----------------------------------------------------------------------- */
-/* J2K / JPT decoder interface                                             */
-/* ----------------------------------------------------------------------- */
-
-void opj_j2k_setup_decoder(opj_j2k_t *j2k, opj_dparameters_t *parameters)
-{
-    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;
-
-        j2k->dump_state = (parameters->flags & OPJ_DPARAMETERS_DUMP_FLAG);
-#ifdef USE_JPWL
-        j2k->m_cp.correct = parameters->jpwl_correct;
-        j2k->m_cp.exp_comps = parameters->jpwl_exp_comps;
-        j2k->m_cp.max_tiles = parameters->jpwl_max_tiles;
-#endif /* USE_JPWL */
-    }
-}
-
-OPJ_BOOL opj_j2k_set_threads(opj_j2k_t *j2k, OPJ_UINT32 num_threads)
-{
-    /* 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) {
-        opj_thread_pool_destroy(j2k->m_tp);
-        j2k->m_tp = NULL;
-        if (num_threads <= (OPJ_UINT32)INT_MAX) {
-            j2k->m_tp = opj_thread_pool_create((int)num_threads);
-        }
-        if (j2k->m_tp == NULL) {
-            j2k->m_tp = opj_thread_pool_create(0);
-            return OPJ_FALSE;
-        }
-        return OPJ_TRUE;
-    }
-    return OPJ_FALSE;
-}
-
-static int opj_j2k_get_default_thread_count()
-{
-    const char* num_threads_str = getenv("OPJ_NUM_THREADS");
-    int num_cpus;
-    int num_threads;
-
-    if (num_threads_str == NULL || !opj_has_thread_support()) {
-        return 0;
-    }
-    num_cpus = opj_get_num_cpus();
-    if (strcmp(num_threads_str, "ALL_CPUS") == 0) {
-        return num_cpus;
-    }
-    if (num_cpus == 0) {
-        num_cpus = 32;
-    }
-    num_threads = atoi(num_threads_str);
-    if (num_threads < 0) {
-        num_threads = 0;
-    } else if (num_threads > 2 * num_cpus) {
-        num_threads = 2 * num_cpus;
-    }
-    return num_threads;
-}
-
-/* ----------------------------------------------------------------------- */
-/* J2K encoder interface                                                       */
-/* ----------------------------------------------------------------------- */
-
-opj_j2k_t* opj_j2k_create_compress(void)
-{
-    opj_j2k_t *l_j2k = (opj_j2k_t*) opj_calloc(1, sizeof(opj_j2k_t));
-    if (!l_j2k) {
-        return NULL;
-    }
-
-
-    l_j2k->m_is_decoder = 0;
-    l_j2k->m_cp.m_is_decoder = 0;
-
-    l_j2k->m_specific_param.m_encoder.m_header_tile_data = (OPJ_BYTE *) opj_malloc(
-                OPJ_J2K_DEFAULT_HEADER_SIZE);
-    if (! l_j2k->m_specific_param.m_encoder.m_header_tile_data) {
-        opj_j2k_destroy(l_j2k);
-        return NULL;
-    }
-
-    l_j2k->m_specific_param.m_encoder.m_header_tile_data_size =
-        OPJ_J2K_DEFAULT_HEADER_SIZE;
-
-    /* validation list creation*/
-    l_j2k->m_validation_list = opj_procedure_list_create();
-    if (! l_j2k->m_validation_list) {
-        opj_j2k_destroy(l_j2k);
-        return NULL;
-    }
-
-    /* execution list creation*/
-    l_j2k->m_procedure_list = opj_procedure_list_create();
-    if (! l_j2k->m_procedure_list) {
-        opj_j2k_destroy(l_j2k);
-        return NULL;
-    }
-
-    l_j2k->m_tp = opj_thread_pool_create(opj_j2k_get_default_thread_count());
-    if (!l_j2k->m_tp) {
-        l_j2k->m_tp = opj_thread_pool_create(0);
-    }
-    if (!l_j2k->m_tp) {
-        opj_j2k_destroy(l_j2k);
-        return NULL;
-    }
-
-    return l_j2k;
-}
-
-static int opj_j2k_initialise_4K_poc(opj_poc_t *POC, int numres)
-{
-    POC[0].tile  = 1;
-    POC[0].resno0  = 0;
-    POC[0].compno0 = 0;
-    POC[0].layno1  = 1;
-    POC[0].resno1  = (OPJ_UINT32)(numres - 1);
-    POC[0].compno1 = 3;
-    POC[0].prg1 = OPJ_CPRL;
-    POC[1].tile  = 1;
-    POC[1].resno0  = (OPJ_UINT32)(numres - 1);
-    POC[1].compno0 = 0;
-    POC[1].layno1  = 1;
-    POC[1].resno1  = (OPJ_UINT32)numres;
-    POC[1].compno1 = 3;
-    POC[1].prg1 = OPJ_CPRL;
-    return 2;
-}
-
-static void opj_j2k_set_cinema_parameters(opj_cparameters_t *parameters,
-        opj_image_t *image, opj_event_mgr_t *p_manager)
-{
-    /* Configure cinema parameters */
-    int i;
-
-    /* No tiling */
-    parameters->tile_size_on = OPJ_FALSE;
-    parameters->cp_tdx = 1;
-    parameters->cp_tdy = 1;
-
-    /* One tile part for each component */
-    parameters->tp_flag = 'C';
-    parameters->tp_on = 1;
-
-    /* Tile and Image shall be at (0,0) */
-    parameters->cp_tx0 = 0;
-    parameters->cp_ty0 = 0;
-    parameters->image_offset_x0 = 0;
-    parameters->image_offset_y0 = 0;
-
-    /* Codeblock size= 32*32 */
-    parameters->cblockw_init = 32;
-    parameters->cblockh_init = 32;
-
-    /* Codeblock style: no mode switch enabled */
-    parameters->mode = 0;
-
-    /* No ROI */
-    parameters->roi_compno = -1;
-
-    /* No subsampling */
-    parameters->subsampling_dx = 1;
-    parameters->subsampling_dy = 1;
-
-    /* 9-7 transform */
-    parameters->irreversible = 1;
-
-    /* Number of layers */
-    if (parameters->tcp_numlayers > 1) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
-                      "1 single quality layer"
-                      "-> Number of layers forced to 1 (rather than %d)\n"
-                      "-> Rate of the last layer (%3.1f) will be used",
-                      parameters->tcp_numlayers,
-                      parameters->tcp_rates[parameters->tcp_numlayers - 1]);
-        parameters->tcp_rates[0] = parameters->tcp_rates[parameters->tcp_numlayers - 1];
-        parameters->tcp_numlayers = 1;
-    }
-
-    /* Resolution levels */
-    switch (parameters->rsiz) {
-    case OPJ_PROFILE_CINEMA_2K:
-        if (parameters->numresolution > 6) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "JPEG 2000 Profile-3 (2k dc profile) requires:\n"
-                          "Number of decomposition levels <= 5\n"
-                          "-> Number of decomposition levels forced to 5 (rather than %d)\n",
-                          parameters->numresolution + 1);
-            parameters->numresolution = 6;
-        }
-        break;
-    case OPJ_PROFILE_CINEMA_4K:
-        if (parameters->numresolution < 2) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "JPEG 2000 Profile-4 (4k dc profile) requires:\n"
-                          "Number of decomposition levels >= 1 && <= 6\n"
-                          "-> Number of decomposition levels forced to 1 (rather than %d)\n",
-                          parameters->numresolution + 1);
-            parameters->numresolution = 1;
-        } else if (parameters->numresolution > 7) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "JPEG 2000 Profile-4 (4k dc profile) requires:\n"
-                          "Number of decomposition levels >= 1 && <= 6\n"
-                          "-> Number of decomposition levels forced to 6 (rather than %d)\n",
-                          parameters->numresolution + 1);
-            parameters->numresolution = 7;
-        }
-        break;
-    default :
-        break;
-    }
-
-    /* Precincts */
-    parameters->csty |= 0x01;
-    if (parameters->numresolution == 1) {
-        parameters->res_spec = 1;
-        parameters->prcw_init[0] = 128;
-        parameters->prch_init[0] = 128;
-    } else {
-        parameters->res_spec = parameters->numresolution - 1;
-        for (i = 0; i < parameters->res_spec; i++) {
-            parameters->prcw_init[i] = 256;
-            parameters->prch_init[i] = 256;
-        }
-    }
-
-    /* The progression order shall be CPRL */
-    parameters->prog_order = OPJ_CPRL;
-
-    /* Progression order changes for 4K, disallowed for 2K */
-    if (parameters->rsiz == OPJ_PROFILE_CINEMA_4K) {
-        parameters->numpocs = (OPJ_UINT32)opj_j2k_initialise_4K_poc(parameters->POC,
-                              parameters->numresolution);
-    } else {
-        parameters->numpocs = 0;
-    }
-
-    /* Limited bit-rate */
-    parameters->cp_disto_alloc = 1;
-    if (parameters->max_cs_size <= 0) {
-        /* No rate has been introduced, 24 fps is assumed */
-        parameters->max_cs_size = OPJ_CINEMA_24_CS;
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
-                      "Maximum 1302083 compressed bytes @ 24fps\n"
-                      "As no rate has been given, this limit will be used.\n");
-    } else if (parameters->max_cs_size > OPJ_CINEMA_24_CS) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
-                      "Maximum 1302083 compressed bytes @ 24fps\n"
-                      "-> Specified rate exceeds this limit. Rate will be forced to 1302083 bytes.\n");
-        parameters->max_cs_size = OPJ_CINEMA_24_CS;
-    }
-
-    if (parameters->max_comp_size <= 0) {
-        /* No rate has been introduced, 24 fps is assumed */
-        parameters->max_comp_size = OPJ_CINEMA_24_COMP;
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
-                      "Maximum 1041666 compressed bytes @ 24fps\n"
-                      "As no rate has been given, this limit will be used.\n");
-    } else if (parameters->max_comp_size > OPJ_CINEMA_24_COMP) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "JPEG 2000 Profile-3 and 4 (2k/4k dc profile) requires:\n"
-                      "Maximum 1041666 compressed bytes @ 24fps\n"
-                      "-> Specified rate exceeds this limit. Rate will be forced to 1041666 bytes.\n");
-        parameters->max_comp_size = OPJ_CINEMA_24_COMP;
-    }
-
-    parameters->tcp_rates[0] = (OPJ_FLOAT32)(image->numcomps * image->comps[0].w *
-                               image->comps[0].h * image->comps[0].prec) /
-                               (OPJ_FLOAT32)(((OPJ_UINT32)parameters->max_cs_size) * 8 * image->comps[0].dx *
-                                       image->comps[0].dy);
-
-}
-
-static OPJ_BOOL opj_j2k_is_cinema_compliant(opj_image_t *image, OPJ_UINT16 rsiz,
-        opj_event_mgr_t *p_manager)
-{
-    OPJ_UINT32 i;
-
-    /* Number of components */
-    if (image->numcomps != 3) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "JPEG 2000 Profile-3 (2k dc profile) requires:\n"
-                      "3 components"
-                      "-> Number of components of input image (%d) is not compliant\n"
-                      "-> Non-profile-3 codestream will be generated\n",
-                      image->numcomps);
-        return OPJ_FALSE;
-    }
-
-    /* Bitdepth */
-    for (i = 0; i < image->numcomps; i++) {
-        if ((image->comps[i].bpp != 12) | (image->comps[i].sgnd)) {
-            char signed_str[] = "signed";
-            char unsigned_str[] = "unsigned";
-            char *tmp_str = image->comps[i].sgnd ? signed_str : unsigned_str;
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "JPEG 2000 Profile-3 (2k dc profile) requires:\n"
-                          "Precision of each component shall be 12 bits unsigned"
-                          "-> At least component %d of input image (%d bits, %s) is not compliant\n"
-                          "-> Non-profile-3 codestream will be generated\n",
-                          i, image->comps[i].bpp, tmp_str);
-            return OPJ_FALSE;
-        }
-    }
-
-    /* Image size */
-    switch (rsiz) {
-    case OPJ_PROFILE_CINEMA_2K:
-        if (((image->comps[0].w > 2048) | (image->comps[0].h > 1080))) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "JPEG 2000 Profile-3 (2k dc profile) requires:\n"
-                          "width <= 2048 and height <= 1080\n"
-                          "-> Input image size %d x %d is not compliant\n"
-                          "-> Non-profile-3 codestream will be generated\n",
-                          image->comps[0].w, image->comps[0].h);
-            return OPJ_FALSE;
-        }
-        break;
-    case OPJ_PROFILE_CINEMA_4K:
-        if (((image->comps[0].w > 4096) | (image->comps[0].h > 2160))) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "JPEG 2000 Profile-4 (4k dc profile) requires:\n"
-                          "width <= 4096 and height <= 2160\n"
-                          "-> Image size %d x %d is not compliant\n"
-                          "-> Non-profile-4 codestream will be generated\n",
-                          image->comps[0].w, image->comps[0].h);
-            return OPJ_FALSE;
-        }
-        break;
-    default :
-        break;
-    }
-
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k,
-                               opj_cparameters_t *parameters,
-                               opj_image_t *image,
-                               opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32 i, j, tileno, numpocs_tile;
-    opj_cp_t *cp = 00;
-    OPJ_UINT32 cblkw, cblkh;
-
-    if (!p_j2k || !parameters || ! image) {
-        return OPJ_FALSE;
-    }
-
-    if ((parameters->numresolution <= 0) ||
-            (parameters->numresolution > OPJ_J2K_MAXRLVLS)) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Invalid number of resolutions : %d not in range [1,%d]\n",
-                      parameters->numresolution, OPJ_J2K_MAXRLVLS);
-        return OPJ_FALSE;
-    }
-
-    if (parameters->cblockw_init < 4 || parameters->cblockw_init > 1024) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Invalid value for cblockw_init: %d not a power of 2 in range [4,1024]\n",
-                      parameters->cblockw_init);
-        return OPJ_FALSE;
-    }
-    if (parameters->cblockh_init < 4 || parameters->cblockh_init > 1024) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Invalid value for cblockh_init: %d not a power of 2 not in range [4,1024]\n",
-                      parameters->cblockh_init);
-        return OPJ_FALSE;
-    }
-    if (parameters->cblockw_init * parameters->cblockh_init > 4096) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Invalid value for cblockw_init * cblockh_init: should be <= 4096\n");
-        return OPJ_FALSE;
-    }
-    cblkw = (OPJ_UINT32)opj_int_floorlog2(parameters->cblockw_init);
-    cblkh = (OPJ_UINT32)opj_int_floorlog2(parameters->cblockh_init);
-    if (parameters->cblockw_init != (1 << cblkw)) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Invalid value for cblockw_init: %d not a power of 2 in range [4,1024]\n",
-                      parameters->cblockw_init);
-        return OPJ_FALSE;
-    }
-    if (parameters->cblockh_init != (1 << cblkh)) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Invalid value for cblockw_init: %d not a power of 2 in range [4,1024]\n",
-                      parameters->cblockh_init);
-        return OPJ_FALSE;
-    }
-
-    /* keep a link to cp so that we can destroy it later in j2k_destroy_compress */
-    cp = &(p_j2k->m_cp);
-
-    /* set default values for cp */
-    cp->tw = 1;
-    cp->th = 1;
-
-    /* FIXME ADE: to be removed once deprecated cp_cinema and cp_rsiz have been removed */
-    if (parameters->rsiz ==
-            OPJ_PROFILE_NONE) { /* consider deprecated fields only if RSIZ has not been set */
-        OPJ_BOOL deprecated_used = OPJ_FALSE;
-        switch (parameters->cp_cinema) {
-        case OPJ_CINEMA2K_24:
-            parameters->rsiz = OPJ_PROFILE_CINEMA_2K;
-            parameters->max_cs_size = OPJ_CINEMA_24_CS;
-            parameters->max_comp_size = OPJ_CINEMA_24_COMP;
-            deprecated_used = OPJ_TRUE;
-            break;
-        case OPJ_CINEMA2K_48:
-            parameters->rsiz = OPJ_PROFILE_CINEMA_2K;
-            parameters->max_cs_size = OPJ_CINEMA_48_CS;
-            parameters->max_comp_size = OPJ_CINEMA_48_COMP;
-            deprecated_used = OPJ_TRUE;
-            break;
-        case OPJ_CINEMA4K_24:
-            parameters->rsiz = OPJ_PROFILE_CINEMA_4K;
-            parameters->max_cs_size = OPJ_CINEMA_24_CS;
-            parameters->max_comp_size = OPJ_CINEMA_24_COMP;
-            deprecated_used = OPJ_TRUE;
-            break;
-        case OPJ_OFF:
-        default:
-            break;
-        }
-        switch (parameters->cp_rsiz) {
-        case OPJ_CINEMA2K:
-            parameters->rsiz = OPJ_PROFILE_CINEMA_2K;
-            deprecated_used = OPJ_TRUE;
-            break;
-        case OPJ_CINEMA4K:
-            parameters->rsiz = OPJ_PROFILE_CINEMA_4K;
-            deprecated_used = OPJ_TRUE;
-            break;
-        case OPJ_MCT:
-            parameters->rsiz = OPJ_PROFILE_PART2 | OPJ_EXTENSION_MCT;
-            deprecated_used = OPJ_TRUE;
-        case OPJ_STD_RSIZ:
-        default:
-            break;
-        }
-        if (deprecated_used) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "Deprecated fields cp_cinema or cp_rsiz are used\n"
-                          "Please consider using only the rsiz field\n"
-                          "See openjpeg.h documentation for more details\n");
-        }
-    }
-
-    /* If no explicit layers are provided, use lossless settings */
-    if (parameters->tcp_numlayers == 0) {
-        parameters->tcp_numlayers = 1;
-        parameters->cp_disto_alloc = 1;
-        parameters->tcp_rates[0] = 0;
-    }
-
-    if (parameters->cp_disto_alloc) {
-        /* Emit warnings if tcp_rates are not decreasing */
-        for (i = 1; i < (OPJ_UINT32) parameters->tcp_numlayers; i++) {
-            OPJ_FLOAT32 rate_i_corr = parameters->tcp_rates[i];
-            OPJ_FLOAT32 rate_i_m_1_corr = parameters->tcp_rates[i - 1];
-            if (rate_i_corr <= 1.0) {
-                rate_i_corr = 1.0;
-            }
-            if (rate_i_m_1_corr <= 1.0) {
-                rate_i_m_1_corr = 1.0;
-            }
-            if (rate_i_corr >= rate_i_m_1_corr) {
-                if (rate_i_corr != parameters->tcp_rates[i] &&
-                        rate_i_m_1_corr != parameters->tcp_rates[i - 1]) {
-                    opj_event_msg(p_manager, EVT_WARNING,
-                                  "tcp_rates[%d]=%f (corrected as %f) should be strictly lesser "
-                                  "than tcp_rates[%d]=%f (corrected as %f)\n",
-                                  i, parameters->tcp_rates[i], rate_i_corr,
-                                  i - 1, parameters->tcp_rates[i - 1], rate_i_m_1_corr);
-                } else if (rate_i_corr != parameters->tcp_rates[i]) {
-                    opj_event_msg(p_manager, EVT_WARNING,
-                                  "tcp_rates[%d]=%f (corrected as %f) should be strictly lesser "
-                                  "than tcp_rates[%d]=%f\n",
-                                  i, parameters->tcp_rates[i], rate_i_corr,
-                                  i - 1, parameters->tcp_rates[i - 1]);
-                } else if (rate_i_m_1_corr != parameters->tcp_rates[i - 1]) {
-                    opj_event_msg(p_manager, EVT_WARNING,
-                                  "tcp_rates[%d]=%f should be strictly lesser "
-                                  "than tcp_rates[%d]=%f (corrected as %f)\n",
-                                  i, parameters->tcp_rates[i],
-                                  i - 1, parameters->tcp_rates[i - 1], rate_i_m_1_corr);
-                } else {
-                    opj_event_msg(p_manager, EVT_WARNING,
-                                  "tcp_rates[%d]=%f should be strictly lesser "
-                                  "than tcp_rates[%d]=%f\n",
-                                  i, parameters->tcp_rates[i],
-                                  i - 1, parameters->tcp_rates[i - 1]);
-                }
-            }
-        }
-    } else if (parameters->cp_fixed_quality) {
-        /* Emit warnings if tcp_distoratio are not increasing */
-        for (i = 1; i < (OPJ_UINT32) parameters->tcp_numlayers; i++) {
-            if (parameters->tcp_distoratio[i] < parameters->tcp_distoratio[i - 1] &&
-                    !(i == (OPJ_UINT32)parameters->tcp_numlayers - 1 &&
-                      parameters->tcp_distoratio[i] == 0)) {
-                opj_event_msg(p_manager, EVT_WARNING,
-                              "tcp_distoratio[%d]=%f should be strictly greater "
-                              "than tcp_distoratio[%d]=%f\n",
-                              i, parameters->tcp_distoratio[i], i - 1,
-                              parameters->tcp_distoratio[i - 1]);
-            }
-        }
-    }
-
-    /* see if max_codestream_size does limit input rate */
-    if (parameters->max_cs_size <= 0) {
-        if (parameters->tcp_rates[parameters->tcp_numlayers - 1] > 0) {
-            OPJ_FLOAT32 temp_size;
-            temp_size = (OPJ_FLOAT32)(((double)image->numcomps * image->comps[0].w *
-                                       image->comps[0].h * image->comps[0].prec) /
-                                      ((double)parameters->tcp_rates[parameters->tcp_numlayers - 1] * 8 *
-                                       image->comps[0].dx * image->comps[0].dy));
-            if (temp_size > INT_MAX) {
-                parameters->max_cs_size = INT_MAX;
-            } else {
-                parameters->max_cs_size = (int) floor(temp_size);
-            }
-        } else {
-            parameters->max_cs_size = 0;
-        }
-    } else {
-        OPJ_FLOAT32 temp_rate;
-        OPJ_BOOL cap = OPJ_FALSE;
-        temp_rate = (OPJ_FLOAT32)(((double)image->numcomps * image->comps[0].w *
-                                   image->comps[0].h * image->comps[0].prec) /
-                                  (((double)parameters->max_cs_size) * 8 * image->comps[0].dx *
-                                   image->comps[0].dy));
-        for (i = 0; i < (OPJ_UINT32) parameters->tcp_numlayers; i++) {
-            if (parameters->tcp_rates[i] < temp_rate) {
-                parameters->tcp_rates[i] = temp_rate;
-                cap = OPJ_TRUE;
-            }
-        }
-        if (cap) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "The desired maximum codestream size has limited\n"
-                          "at least one of the desired quality layers\n");
-        }
-    }
-
-    /* Manage profiles and applications and set RSIZ */
-    /* set cinema parameters if required */
-    if (OPJ_IS_CINEMA(parameters->rsiz)) {
-        if ((parameters->rsiz == OPJ_PROFILE_CINEMA_S2K)
-                || (parameters->rsiz == OPJ_PROFILE_CINEMA_S4K)) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "JPEG 2000 Scalable Digital Cinema profiles not yet supported\n");
-            parameters->rsiz = OPJ_PROFILE_NONE;
-        } else {
-            opj_j2k_set_cinema_parameters(parameters, image, p_manager);
-            if (!opj_j2k_is_cinema_compliant(image, parameters->rsiz, p_manager)) {
-                parameters->rsiz = OPJ_PROFILE_NONE;
-            }
-        }
-    } else if (OPJ_IS_STORAGE(parameters->rsiz)) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "JPEG 2000 Long Term Storage profile not yet supported\n");
-        parameters->rsiz = OPJ_PROFILE_NONE;
-    } else if (OPJ_IS_BROADCAST(parameters->rsiz)) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "JPEG 2000 Broadcast profiles not yet supported\n");
-        parameters->rsiz = OPJ_PROFILE_NONE;
-    } else if (OPJ_IS_IMF(parameters->rsiz)) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "JPEG 2000 IMF profiles not yet supported\n");
-        parameters->rsiz = OPJ_PROFILE_NONE;
-    } else if (OPJ_IS_PART2(parameters->rsiz)) {
-        if (parameters->rsiz == ((OPJ_PROFILE_PART2) | (OPJ_EXTENSION_NONE))) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "JPEG 2000 Part-2 profile defined\n"
-                          "but no Part-2 extension enabled.\n"
-                          "Profile set to NONE.\n");
-            parameters->rsiz = OPJ_PROFILE_NONE;
-        } else if (parameters->rsiz != ((OPJ_PROFILE_PART2) | (OPJ_EXTENSION_MCT))) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "Unsupported Part-2 extension enabled\n"
-                          "Profile set to NONE.\n");
-            parameters->rsiz = OPJ_PROFILE_NONE;
-        }
-    }
-
-    /*
-    copy user encoding parameters
-    */
-    cp->m_specific_param.m_enc.m_max_comp_size = (OPJ_UINT32)
-            parameters->max_comp_size;
-    cp->rsiz = parameters->rsiz;
-    cp->m_specific_param.m_enc.m_disto_alloc = (OPJ_UINT32)
-            parameters->cp_disto_alloc & 1u;
-    cp->m_specific_param.m_enc.m_fixed_alloc = (OPJ_UINT32)
-            parameters->cp_fixed_alloc & 1u;
-    cp->m_specific_param.m_enc.m_fixed_quality = (OPJ_UINT32)
-            parameters->cp_fixed_quality & 1u;
-
-    /* mod fixed_quality */
-    if (parameters->cp_fixed_alloc && parameters->cp_matrice) {
-        size_t array_size = (size_t)parameters->tcp_numlayers *
-                            (size_t)parameters->numresolution * 3 * sizeof(OPJ_INT32);
-        cp->m_specific_param.m_enc.m_matrice = (OPJ_INT32 *) opj_malloc(array_size);
-        if (!cp->m_specific_param.m_enc.m_matrice) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Not enough memory to allocate copy of user encoding parameters matrix \n");
-            return OPJ_FALSE;
-        }
-        memcpy(cp->m_specific_param.m_enc.m_matrice, parameters->cp_matrice,
-               array_size);
-    }
-
-    /* tiles */
-    cp->tdx = (OPJ_UINT32)parameters->cp_tdx;
-    cp->tdy = (OPJ_UINT32)parameters->cp_tdy;
-
-    /* tile offset */
-    cp->tx0 = (OPJ_UINT32)parameters->cp_tx0;
-    cp->ty0 = (OPJ_UINT32)parameters->cp_ty0;
-
-    /* comment string */
-    if (parameters->cp_comment) {
-        cp->comment = (char*)opj_malloc(strlen(parameters->cp_comment) + 1U);
-        if (!cp->comment) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Not enough memory to allocate copy of comment string\n");
-            return OPJ_FALSE;
-        }
-        strcpy(cp->comment, parameters->cp_comment);
-    } else {
-        /* Create default comment for codestream */
-        const char comment[] = "Created by OpenJPEG version ";
-        const size_t clen = strlen(comment);
-        const char *version = opj_version();
-
-        /* UniPG>> */
-#ifdef USE_JPWL
-        cp->comment = (char*)opj_malloc(clen + strlen(version) + 11);
-        if (!cp->comment) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Not enough memory to allocate comment string\n");
-            return OPJ_FALSE;
-        }
-        sprintf(cp->comment, "%s%s with JPWL", comment, version);
-#else
-        cp->comment = (char*)opj_malloc(clen + strlen(version) + 1);
-        if (!cp->comment) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Not enough memory to allocate comment string\n");
-            return OPJ_FALSE;
-        }
-        sprintf(cp->comment, "%s%s", comment, version);
-#endif
-        /* <<UniPG */
-    }
-
-    /*
-    calculate other encoding parameters
-    */
-
-    if (parameters->tile_size_on) {
-        cp->tw = opj_uint_ceildiv(image->x1 - cp->tx0, cp->tdx);
-        cp->th = opj_uint_ceildiv(image->y1 - cp->ty0, cp->tdy);
-    } else {
-        cp->tdx = image->x1 - cp->tx0;
-        cp->tdy = image->y1 - cp->ty0;
-    }
-
-    if (parameters->tp_on) {
-        cp->m_specific_param.m_enc.m_tp_flag = (OPJ_BYTE)parameters->tp_flag;
-        cp->m_specific_param.m_enc.m_tp_on = 1;
-    }
-
-#ifdef USE_JPWL
-    /*
-    calculate JPWL encoding parameters
-    */
-
-    if (parameters->jpwl_epc_on) {
-        OPJ_INT32 i;
-
-        /* set JPWL on */
-        cp->epc_on = OPJ_TRUE;
-        cp->info_on = OPJ_FALSE; /* no informative technique */
-
-        /* set EPB on */
-        if ((parameters->jpwl_hprot_MH > 0) || (parameters->jpwl_hprot_TPH[0] > 0)) {
-            cp->epb_on = OPJ_TRUE;
-
-            cp->hprot_MH = parameters->jpwl_hprot_MH;
-            for (i = 0; i < JPWL_MAX_NO_TILESPECS; i++) {
-                cp->hprot_TPH_tileno[i] = parameters->jpwl_hprot_TPH_tileno[i];
-                cp->hprot_TPH[i] = parameters->jpwl_hprot_TPH[i];
-            }
-            /* if tile specs are not specified, copy MH specs */
-            if (cp->hprot_TPH[0] == -1) {
-                cp->hprot_TPH_tileno[0] = 0;
-                cp->hprot_TPH[0] = parameters->jpwl_hprot_MH;
-            }
-            for (i = 0; i < JPWL_MAX_NO_PACKSPECS; i++) {
-                cp->pprot_tileno[i] = parameters->jpwl_pprot_tileno[i];
-                cp->pprot_packno[i] = parameters->jpwl_pprot_packno[i];
-                cp->pprot[i] = parameters->jpwl_pprot[i];
-            }
-        }
-
-        /* set ESD writing */
-        if ((parameters->jpwl_sens_size == 1) || (parameters->jpwl_sens_size == 2)) {
-            cp->esd_on = OPJ_TRUE;
-
-            cp->sens_size = parameters->jpwl_sens_size;
-            cp->sens_addr = parameters->jpwl_sens_addr;
-            cp->sens_range = parameters->jpwl_sens_range;
-
-            cp->sens_MH = parameters->jpwl_sens_MH;
-            for (i = 0; i < JPWL_MAX_NO_TILESPECS; i++) {
-                cp->sens_TPH_tileno[i] = parameters->jpwl_sens_TPH_tileno[i];
-                cp->sens_TPH[i] = parameters->jpwl_sens_TPH[i];
-            }
-        }
-
-        /* always set RED writing to false: we are at the encoder */
-        cp->red_on = OPJ_FALSE;
-
-    } else {
-        cp->epc_on = OPJ_FALSE;
-    }
-#endif /* USE_JPWL */
-
-    /* initialize the mutiple tiles */
-    /* ---------------------------- */
-    cp->tcps = (opj_tcp_t*) opj_calloc(cp->tw * cp->th, sizeof(opj_tcp_t));
-    if (!cp->tcps) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Not enough memory to allocate tile coding parameters\n");
-        return OPJ_FALSE;
-    }
-    if (parameters->numpocs) {
-        /* initialisation of POC */
-        opj_j2k_check_poc_val(parameters->POC, parameters->numpocs,
-                              (OPJ_UINT32)parameters->numresolution, image->numcomps,
-                              (OPJ_UINT32)parameters->tcp_numlayers, p_manager);
-        /* TODO MSD use the return value*/
-    }
-
-    for (tileno = 0; tileno < cp->tw * cp->th; tileno++) {
-        opj_tcp_t *tcp = &cp->tcps[tileno];
-        tcp->numlayers = (OPJ_UINT32)parameters->tcp_numlayers;
-
-        for (j = 0; j < tcp->numlayers; j++) {
-            if (OPJ_IS_CINEMA(cp->rsiz)) {
-                if (cp->m_specific_param.m_enc.m_fixed_quality) {
-                    tcp->distoratio[j] = parameters->tcp_distoratio[j];
-                }
-                tcp->rates[j] = parameters->tcp_rates[j];
-            } else {
-                if (cp->m_specific_param.m_enc.m_fixed_quality) {       /* add fixed_quality */
-                    tcp->distoratio[j] = parameters->tcp_distoratio[j];
-                } else {
-                    tcp->rates[j] = parameters->tcp_rates[j];
-                }
-            }
-            if (!cp->m_specific_param.m_enc.m_fixed_quality &&
-                    tcp->rates[j] <= 1.0) {
-                tcp->rates[j] = 0.0;    /* force lossless */
-            }
-        }
-
-        tcp->csty = (OPJ_UINT32)parameters->csty;
-        tcp->prg = parameters->prog_order;
-        tcp->mct = (OPJ_UINT32)parameters->tcp_mct;
-
-        numpocs_tile = 0;
-        tcp->POC = 0;
-
-        if (parameters->numpocs) {
-            /* initialisation of POC */
-            tcp->POC = 1;
-            for (i = 0; i < parameters->numpocs; i++) {
-                if (tileno + 1 == parameters->POC[i].tile)  {
-                    opj_poc_t *tcp_poc = &tcp->pocs[numpocs_tile];
-
-                    tcp_poc->resno0         = parameters->POC[numpocs_tile].resno0;
-                    tcp_poc->compno0        = parameters->POC[numpocs_tile].compno0;
-                    tcp_poc->layno1         = parameters->POC[numpocs_tile].layno1;
-                    tcp_poc->resno1         = parameters->POC[numpocs_tile].resno1;
-                    tcp_poc->compno1        = parameters->POC[numpocs_tile].compno1;
-                    tcp_poc->prg1           = parameters->POC[numpocs_tile].prg1;
-                    tcp_poc->tile           = parameters->POC[numpocs_tile].tile;
-
-                    numpocs_tile++;
-                }
-            }
-
-            tcp->numpocs = numpocs_tile - 1 ;
-        } else {
-            tcp->numpocs = 0;
-        }
-
-        tcp->tccps = (opj_tccp_t*) opj_calloc(image->numcomps, sizeof(opj_tccp_t));
-        if (!tcp->tccps) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Not enough memory to allocate tile component coding parameters\n");
-            return OPJ_FALSE;
-        }
-        if (parameters->mct_data) {
-
-            OPJ_UINT32 lMctSize = image->numcomps * image->numcomps * (OPJ_UINT32)sizeof(
-                                      OPJ_FLOAT32);
-            OPJ_FLOAT32 * lTmpBuf = (OPJ_FLOAT32*)opj_malloc(lMctSize);
-            OPJ_INT32 * l_dc_shift = (OPJ_INT32 *)((OPJ_BYTE *) parameters->mct_data +
-                                                   lMctSize);
-
-            if (!lTmpBuf) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Not enough memory to allocate temp buffer\n");
-                return OPJ_FALSE;
-            }
-
-            tcp->mct = 2;
-            tcp->m_mct_coding_matrix = (OPJ_FLOAT32*)opj_malloc(lMctSize);
-            if (! tcp->m_mct_coding_matrix) {
-                opj_free(lTmpBuf);
-                lTmpBuf = NULL;
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Not enough memory to allocate encoder MCT coding matrix \n");
-                return OPJ_FALSE;
-            }
-            memcpy(tcp->m_mct_coding_matrix, parameters->mct_data, lMctSize);
-            memcpy(lTmpBuf, parameters->mct_data, lMctSize);
-
-            tcp->m_mct_decoding_matrix = (OPJ_FLOAT32*)opj_malloc(lMctSize);
-            if (! tcp->m_mct_decoding_matrix) {
-                opj_free(lTmpBuf);
-                lTmpBuf = NULL;
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Not enough memory to allocate encoder MCT decoding matrix \n");
-                return OPJ_FALSE;
-            }
-            if (opj_matrix_inversion_f(lTmpBuf, (tcp->m_mct_decoding_matrix),
-                                       image->numcomps) == OPJ_FALSE) {
-                opj_free(lTmpBuf);
-                lTmpBuf = NULL;
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Failed to inverse encoder MCT decoding matrix \n");
-                return OPJ_FALSE;
-            }
-
-            tcp->mct_norms = (OPJ_FLOAT64*)
-                             opj_malloc(image->numcomps * sizeof(OPJ_FLOAT64));
-            if (! tcp->mct_norms) {
-                opj_free(lTmpBuf);
-                lTmpBuf = NULL;
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Not enough memory to allocate encoder MCT norms \n");
-                return OPJ_FALSE;
-            }
-            opj_calculate_norms(tcp->mct_norms, image->numcomps,
-                                tcp->m_mct_decoding_matrix);
-            opj_free(lTmpBuf);
-
-            for (i = 0; i < image->numcomps; i++) {
-                opj_tccp_t *tccp = &tcp->tccps[i];
-                tccp->m_dc_level_shift = l_dc_shift[i];
-            }
-
-            if (opj_j2k_setup_mct_encoding(tcp, image) == OPJ_FALSE) {
-                /* free will be handled by opj_j2k_destroy */
-                opj_event_msg(p_manager, EVT_ERROR, "Failed to setup j2k mct encoding\n");
-                return OPJ_FALSE;
-            }
-        } else {
-            if (tcp->mct == 1 && image->numcomps >= 3) { /* RGB->YCC MCT is enabled */
-                if ((image->comps[0].dx != image->comps[1].dx) ||
-                        (image->comps[0].dx != image->comps[2].dx) ||
-                        (image->comps[0].dy != image->comps[1].dy) ||
-                        (image->comps[0].dy != image->comps[2].dy)) {
-                    opj_event_msg(p_manager, EVT_WARNING,
-                                  "Cannot perform MCT on components with different sizes. Disabling MCT.\n");
-                    tcp->mct = 0;
-                }
-            }
-            for (i = 0; i < image->numcomps; i++) {
-                opj_tccp_t *tccp = &tcp->tccps[i];
-                opj_image_comp_t * l_comp = &(image->comps[i]);
-
-                if (! l_comp->sgnd) {
-                    tccp->m_dc_level_shift = 1 << (l_comp->prec - 1);
-                }
-            }
-        }
-
-        for (i = 0; i < image->numcomps; i++) {
-            opj_tccp_t *tccp = &tcp->tccps[i];
-
-            tccp->csty = parameters->csty &
-                         0x01;   /* 0 => one precinct || 1 => custom precinct  */
-            tccp->numresolutions = (OPJ_UINT32)parameters->numresolution;
-            tccp->cblkw = (OPJ_UINT32)opj_int_floorlog2(parameters->cblockw_init);
-            tccp->cblkh = (OPJ_UINT32)opj_int_floorlog2(parameters->cblockh_init);
-            tccp->cblksty = (OPJ_UINT32)parameters->mode;
-            tccp->qmfbid = parameters->irreversible ? 0 : 1;
-            tccp->qntsty = parameters->irreversible ? J2K_CCP_QNTSTY_SEQNT :
-                           J2K_CCP_QNTSTY_NOQNT;
-            tccp->numgbits = 2;
-
-            if ((OPJ_INT32)i == parameters->roi_compno) {
-                tccp->roishift = parameters->roi_shift;
-            } else {
-                tccp->roishift = 0;
-            }
-
-            if (parameters->csty & J2K_CCP_CSTY_PRT) {
-                OPJ_INT32 p = 0, it_res;
-                assert(tccp->numresolutions > 0);
-                for (it_res = (OPJ_INT32)tccp->numresolutions - 1; it_res >= 0; it_res--) {
-                    if (p < parameters->res_spec) {
-
-                        if (parameters->prcw_init[p] < 1) {
-                            tccp->prcw[it_res] = 1;
-                        } else {
-                            tccp->prcw[it_res] = (OPJ_UINT32)opj_int_floorlog2(parameters->prcw_init[p]);
-                        }
-
-                        if (parameters->prch_init[p] < 1) {
-                            tccp->prch[it_res] = 1;
-                        } else {
-                            tccp->prch[it_res] = (OPJ_UINT32)opj_int_floorlog2(parameters->prch_init[p]);
-                        }
-
-                    } else {
-                        OPJ_INT32 res_spec = parameters->res_spec;
-                        OPJ_INT32 size_prcw = 0;
-                        OPJ_INT32 size_prch = 0;
-
-                        assert(res_spec > 0); /* issue 189 */
-                        size_prcw = parameters->prcw_init[res_spec - 1] >> (p - (res_spec - 1));
-                        size_prch = parameters->prch_init[res_spec - 1] >> (p - (res_spec - 1));
-
-
-                        if (size_prcw < 1) {
-                            tccp->prcw[it_res] = 1;
-                        } else {
-                            tccp->prcw[it_res] = (OPJ_UINT32)opj_int_floorlog2(size_prcw);
-                        }
-
-                        if (size_prch < 1) {
-                            tccp->prch[it_res] = 1;
-                        } else {
-                            tccp->prch[it_res] = (OPJ_UINT32)opj_int_floorlog2(size_prch);
-                        }
-                    }
-                    p++;
-                    /*printf("\nsize precinct for level %d : %d,%d\n", it_res,tccp->prcw[it_res], tccp->prch[it_res]); */
-                }       /*end for*/
-            } else {
-                for (j = 0; j < tccp->numresolutions; j++) {
-                    tccp->prcw[j] = 15;
-                    tccp->prch[j] = 15;
-                }
-            }
-
-            opj_dwt_calc_explicit_stepsizes(tccp, image->comps[i].prec);
-        }
-    }
-
-    if (parameters->mct_data) {
-        opj_free(parameters->mct_data);
-        parameters->mct_data = 00;
-    }
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_add_mhmarker(opj_codestream_index_t *cstr_index,
-                                     OPJ_UINT32 type, OPJ_OFF_T pos, OPJ_UINT32 len)
-{
-    assert(cstr_index != 00);
-
-    /* expand the list? */
-    if ((cstr_index->marknum + 1) > cstr_index->maxmarknum) {
-        opj_marker_info_t *new_marker;
-        cstr_index->maxmarknum = (OPJ_UINT32)(100 + (OPJ_FLOAT32)
-                                              cstr_index->maxmarknum);
-        new_marker = (opj_marker_info_t *) opj_realloc(cstr_index->marker,
-                     cstr_index->maxmarknum * sizeof(opj_marker_info_t));
-        if (! new_marker) {
-            opj_free(cstr_index->marker);
-            cstr_index->marker = NULL;
-            cstr_index->maxmarknum = 0;
-            cstr_index->marknum = 0;
-            /* opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to add mh marker\n"); */
-            return OPJ_FALSE;
-        }
-        cstr_index->marker = new_marker;
-    }
-
-    /* add the marker */
-    cstr_index->marker[cstr_index->marknum].type = (OPJ_UINT16)type;
-    cstr_index->marker[cstr_index->marknum].pos = (OPJ_INT32)pos;
-    cstr_index->marker[cstr_index->marknum].len = (OPJ_INT32)len;
-    cstr_index->marknum++;
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_add_tlmarker(OPJ_UINT32 tileno,
-                                     opj_codestream_index_t *cstr_index, OPJ_UINT32 type, OPJ_OFF_T pos,
-                                     OPJ_UINT32 len)
-{
-    assert(cstr_index != 00);
-    assert(cstr_index->tile_index != 00);
-
-    /* expand the list? */
-    if ((cstr_index->tile_index[tileno].marknum + 1) >
-            cstr_index->tile_index[tileno].maxmarknum) {
-        opj_marker_info_t *new_marker;
-        cstr_index->tile_index[tileno].maxmarknum = (OPJ_UINT32)(100 +
-                (OPJ_FLOAT32) cstr_index->tile_index[tileno].maxmarknum);
-        new_marker = (opj_marker_info_t *) opj_realloc(
-                         cstr_index->tile_index[tileno].marker,
-                         cstr_index->tile_index[tileno].maxmarknum * sizeof(opj_marker_info_t));
-        if (! new_marker) {
-            opj_free(cstr_index->tile_index[tileno].marker);
-            cstr_index->tile_index[tileno].marker = NULL;
-            cstr_index->tile_index[tileno].maxmarknum = 0;
-            cstr_index->tile_index[tileno].marknum = 0;
-            /* opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to add tl marker\n"); */
-            return OPJ_FALSE;
-        }
-        cstr_index->tile_index[tileno].marker = new_marker;
-    }
-
-    /* add the marker */
-    cstr_index->tile_index[tileno].marker[cstr_index->tile_index[tileno].marknum].type
-        = (OPJ_UINT16)type;
-    cstr_index->tile_index[tileno].marker[cstr_index->tile_index[tileno].marknum].pos
-        = (OPJ_INT32)pos;
-    cstr_index->tile_index[tileno].marker[cstr_index->tile_index[tileno].marknum].len
-        = (OPJ_INT32)len;
-    cstr_index->tile_index[tileno].marknum++;
-
-    if (type == J2K_MS_SOT) {
-        OPJ_UINT32 l_current_tile_part = cstr_index->tile_index[tileno].current_tpsno;
-
-        if (cstr_index->tile_index[tileno].tp_index) {
-            cstr_index->tile_index[tileno].tp_index[l_current_tile_part].start_pos = pos;
-        }
-
-    }
-    return OPJ_TRUE;
-}
-
-/*
- * -----------------------------------------------------------------------
- * -----------------------------------------------------------------------
- * -----------------------------------------------------------------------
- */
-
-OPJ_BOOL opj_j2k_end_decompress(opj_j2k_t *p_j2k,
-                                opj_stream_private_t *p_stream,
-                                opj_event_mgr_t * p_manager
-                               )
-{
-    (void)p_j2k;
-    (void)p_stream;
-    (void)p_manager;
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_j2k_read_header(opj_stream_private_t *p_stream,
-                             opj_j2k_t* p_j2k,
-                             opj_image_t** p_image,
-                             opj_event_mgr_t* p_manager)
-{
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_stream != 00);
-    assert(p_manager != 00);
-
-    /* create an empty image header */
-    p_j2k->m_private_image = opj_image_create0();
-    if (! p_j2k->m_private_image) {
-        return OPJ_FALSE;
-    }
-
-    /* customization of the validation */
-    if (! opj_j2k_setup_decoding_validation(p_j2k, p_manager)) {
-        opj_image_destroy(p_j2k->m_private_image);
-        p_j2k->m_private_image = NULL;
-        return OPJ_FALSE;
-    }
-
-    /* validation of the parameters codec */
-    if (! opj_j2k_exec(p_j2k, p_j2k->m_validation_list, p_stream, p_manager)) {
-        opj_image_destroy(p_j2k->m_private_image);
-        p_j2k->m_private_image = NULL;
-        return OPJ_FALSE;
-    }
-
-    /* customization of the encoding */
-    if (! opj_j2k_setup_header_reading(p_j2k, p_manager)) {
-        opj_image_destroy(p_j2k->m_private_image);
-        p_j2k->m_private_image = NULL;
-        return OPJ_FALSE;
-    }
-
-    /* read header */
-    if (! opj_j2k_exec(p_j2k, p_j2k->m_procedure_list, p_stream, p_manager)) {
-        opj_image_destroy(p_j2k->m_private_image);
-        p_j2k->m_private_image = NULL;
-        return OPJ_FALSE;
-    }
-
-    *p_image = opj_image_create0();
-    if (!(*p_image)) {
-        return OPJ_FALSE;
-    }
-
-    /* Copy codestream image information to the output image */
-    opj_copy_image_header(p_j2k->m_private_image, *p_image);
-
-    /*Allocate and initialize some elements of codestrem index*/
-    if (!opj_j2k_allocate_tile_element_cstr_index(p_j2k)) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_setup_header_reading(opj_j2k_t *p_j2k,
-        opj_event_mgr_t * p_manager)
-{
-    /* preconditions*/
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_read_header_procedure, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* DEVELOPER CORNER, add your custom procedures */
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_copy_default_tcp_and_create_tcd, p_manager))  {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_setup_decoding_validation(opj_j2k_t *p_j2k,
-        opj_event_mgr_t * p_manager)
-{
-    /* preconditions*/
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    if (! opj_procedure_list_add_procedure(p_j2k->m_validation_list,
-                                           (opj_procedure)opj_j2k_build_decoder, p_manager)) {
-        return OPJ_FALSE;
-    }
-    if (! opj_procedure_list_add_procedure(p_j2k->m_validation_list,
-                                           (opj_procedure)opj_j2k_decoding_validation, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* DEVELOPER CORNER, add your custom validation procedure */
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_mct_validation(opj_j2k_t * p_j2k,
-                                       opj_stream_private_t *p_stream,
-                                       opj_event_mgr_t * p_manager)
-{
-    OPJ_BOOL l_is_valid = OPJ_TRUE;
-    OPJ_UINT32 i, j;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_stream != 00);
-    assert(p_manager != 00);
-
-    OPJ_UNUSED(p_stream);
-    OPJ_UNUSED(p_manager);
-
-    if ((p_j2k->m_cp.rsiz & 0x8200) == 0x8200) {
-        OPJ_UINT32 l_nb_tiles = p_j2k->m_cp.th * p_j2k->m_cp.tw;
-        opj_tcp_t * l_tcp = p_j2k->m_cp.tcps;
-
-        for (i = 0; i < l_nb_tiles; ++i) {
-            if (l_tcp->mct == 2) {
-                opj_tccp_t * l_tccp = l_tcp->tccps;
-                l_is_valid &= (l_tcp->m_mct_coding_matrix != 00);
-
-                for (j = 0; j < p_j2k->m_private_image->numcomps; ++j) {
-                    l_is_valid &= !(l_tccp->qmfbid & 1);
-                    ++l_tccp;
-                }
-            }
-            ++l_tcp;
-        }
-    }
-
-    return l_is_valid;
-}
-
-OPJ_BOOL opj_j2k_setup_mct_encoding(opj_tcp_t * p_tcp, opj_image_t * p_image)
-{
-    OPJ_UINT32 i;
-    OPJ_UINT32 l_indix = 1;
-    opj_mct_data_t * l_mct_deco_data = 00, * l_mct_offset_data = 00;
-    opj_simple_mcc_decorrelation_data_t * l_mcc_data;
-    OPJ_UINT32 l_mct_size, l_nb_elem;
-    OPJ_FLOAT32 * l_data, * l_current_data;
-    opj_tccp_t * l_tccp;
-
-    /* preconditions */
-    assert(p_tcp != 00);
-
-    if (p_tcp->mct != 2) {
-        return OPJ_TRUE;
-    }
-
-    if (p_tcp->m_mct_decoding_matrix) {
-        if (p_tcp->m_nb_mct_records == p_tcp->m_nb_max_mct_records) {
-            opj_mct_data_t *new_mct_records;
-            p_tcp->m_nb_max_mct_records += OPJ_J2K_MCT_DEFAULT_NB_RECORDS;
-
-            new_mct_records = (opj_mct_data_t *) opj_realloc(p_tcp->m_mct_records,
-                              p_tcp->m_nb_max_mct_records * sizeof(opj_mct_data_t));
-            if (! new_mct_records) {
-                opj_free(p_tcp->m_mct_records);
-                p_tcp->m_mct_records = NULL;
-                p_tcp->m_nb_max_mct_records = 0;
-                p_tcp->m_nb_mct_records = 0;
-                /* opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to setup mct encoding\n"); */
-                return OPJ_FALSE;
-            }
-            p_tcp->m_mct_records = new_mct_records;
-            l_mct_deco_data = p_tcp->m_mct_records + p_tcp->m_nb_mct_records;
-
-            memset(l_mct_deco_data, 0,
-                   (p_tcp->m_nb_max_mct_records - p_tcp->m_nb_mct_records) * sizeof(
-                       opj_mct_data_t));
-        }
-        l_mct_deco_data = p_tcp->m_mct_records + p_tcp->m_nb_mct_records;
-
-        if (l_mct_deco_data->m_data) {
-            opj_free(l_mct_deco_data->m_data);
-            l_mct_deco_data->m_data = 00;
-        }
-
-        l_mct_deco_data->m_index = l_indix++;
-        l_mct_deco_data->m_array_type = MCT_TYPE_DECORRELATION;
-        l_mct_deco_data->m_element_type = MCT_TYPE_FLOAT;
-        l_nb_elem = p_image->numcomps * p_image->numcomps;
-        l_mct_size = l_nb_elem * MCT_ELEMENT_SIZE[l_mct_deco_data->m_element_type];
-        l_mct_deco_data->m_data = (OPJ_BYTE*)opj_malloc(l_mct_size);
-
-        if (! l_mct_deco_data->m_data) {
-            return OPJ_FALSE;
-        }
-
-        j2k_mct_write_functions_from_float[l_mct_deco_data->m_element_type](
-            p_tcp->m_mct_decoding_matrix, l_mct_deco_data->m_data, l_nb_elem);
-
-        l_mct_deco_data->m_data_size = l_mct_size;
-        ++p_tcp->m_nb_mct_records;
-    }
-
-    if (p_tcp->m_nb_mct_records == p_tcp->m_nb_max_mct_records) {
-        opj_mct_data_t *new_mct_records;
-        p_tcp->m_nb_max_mct_records += OPJ_J2K_MCT_DEFAULT_NB_RECORDS;
-        new_mct_records = (opj_mct_data_t *) opj_realloc(p_tcp->m_mct_records,
-                          p_tcp->m_nb_max_mct_records * sizeof(opj_mct_data_t));
-        if (! new_mct_records) {
-            opj_free(p_tcp->m_mct_records);
-            p_tcp->m_mct_records = NULL;
-            p_tcp->m_nb_max_mct_records = 0;
-            p_tcp->m_nb_mct_records = 0;
-            /* opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to setup mct encoding\n"); */
-            return OPJ_FALSE;
-        }
-        p_tcp->m_mct_records = new_mct_records;
-        l_mct_offset_data = p_tcp->m_mct_records + p_tcp->m_nb_mct_records;
-
-        memset(l_mct_offset_data, 0,
-               (p_tcp->m_nb_max_mct_records - p_tcp->m_nb_mct_records) * sizeof(
-                   opj_mct_data_t));
-
-        if (l_mct_deco_data) {
-            l_mct_deco_data = l_mct_offset_data - 1;
-        }
-    }
-
-    l_mct_offset_data = p_tcp->m_mct_records + p_tcp->m_nb_mct_records;
-
-    if (l_mct_offset_data->m_data) {
-        opj_free(l_mct_offset_data->m_data);
-        l_mct_offset_data->m_data = 00;
-    }
-
-    l_mct_offset_data->m_index = l_indix++;
-    l_mct_offset_data->m_array_type = MCT_TYPE_OFFSET;
-    l_mct_offset_data->m_element_type = MCT_TYPE_FLOAT;
-    l_nb_elem = p_image->numcomps;
-    l_mct_size = l_nb_elem * MCT_ELEMENT_SIZE[l_mct_offset_data->m_element_type];
-    l_mct_offset_data->m_data = (OPJ_BYTE*)opj_malloc(l_mct_size);
-
-    if (! l_mct_offset_data->m_data) {
-        return OPJ_FALSE;
-    }
-
-    l_data = (OPJ_FLOAT32*)opj_malloc(l_nb_elem * sizeof(OPJ_FLOAT32));
-    if (! l_data) {
-        opj_free(l_mct_offset_data->m_data);
-        l_mct_offset_data->m_data = 00;
-        return OPJ_FALSE;
-    }
-
-    l_tccp = p_tcp->tccps;
-    l_current_data = l_data;
-
-    for (i = 0; i < l_nb_elem; ++i) {
-        *(l_current_data++) = (OPJ_FLOAT32)(l_tccp->m_dc_level_shift);
-        ++l_tccp;
-    }
-
-    j2k_mct_write_functions_from_float[l_mct_offset_data->m_element_type](l_data,
-            l_mct_offset_data->m_data, l_nb_elem);
-
-    opj_free(l_data);
-
-    l_mct_offset_data->m_data_size = l_mct_size;
-
-    ++p_tcp->m_nb_mct_records;
-
-    if (p_tcp->m_nb_mcc_records == p_tcp->m_nb_max_mcc_records) {
-        opj_simple_mcc_decorrelation_data_t *new_mcc_records;
-        p_tcp->m_nb_max_mcc_records += OPJ_J2K_MCT_DEFAULT_NB_RECORDS;
-        new_mcc_records = (opj_simple_mcc_decorrelation_data_t *) opj_realloc(
-                              p_tcp->m_mcc_records, p_tcp->m_nb_max_mcc_records * sizeof(
-                                  opj_simple_mcc_decorrelation_data_t));
-        if (! new_mcc_records) {
-            opj_free(p_tcp->m_mcc_records);
-            p_tcp->m_mcc_records = NULL;
-            p_tcp->m_nb_max_mcc_records = 0;
-            p_tcp->m_nb_mcc_records = 0;
-            /* opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to setup mct encoding\n"); */
-            return OPJ_FALSE;
-        }
-        p_tcp->m_mcc_records = new_mcc_records;
-        l_mcc_data = p_tcp->m_mcc_records + p_tcp->m_nb_mcc_records;
-        memset(l_mcc_data, 0, (p_tcp->m_nb_max_mcc_records - p_tcp->m_nb_mcc_records) *
-               sizeof(opj_simple_mcc_decorrelation_data_t));
-
-    }
-
-    l_mcc_data = p_tcp->m_mcc_records + p_tcp->m_nb_mcc_records;
-    l_mcc_data->m_decorrelation_array = l_mct_deco_data;
-    l_mcc_data->m_is_irreversible = 1;
-    l_mcc_data->m_nb_comps = p_image->numcomps;
-    l_mcc_data->m_index = l_indix++;
-    l_mcc_data->m_offset_array = l_mct_offset_data;
-    ++p_tcp->m_nb_mcc_records;
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_build_decoder(opj_j2k_t * p_j2k,
-                                      opj_stream_private_t *p_stream,
-                                      opj_event_mgr_t * p_manager)
-{
-    /* add here initialization of cp
-       copy paste of setup_decoder */
-    (void)p_j2k;
-    (void)p_stream;
-    (void)p_manager;
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_build_encoder(opj_j2k_t * p_j2k,
-                                      opj_stream_private_t *p_stream,
-                                      opj_event_mgr_t * p_manager)
-{
-    /* add here initialization of cp
-       copy paste of setup_encoder */
-    (void)p_j2k;
-    (void)p_stream;
-    (void)p_manager;
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_encoding_validation(opj_j2k_t * p_j2k,
-        opj_stream_private_t *p_stream,
-        opj_event_mgr_t * p_manager)
-{
-    OPJ_BOOL l_is_valid = OPJ_TRUE;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_stream != 00);
-    assert(p_manager != 00);
-
-    OPJ_UNUSED(p_stream);
-
-    /* STATE checking */
-    /* make sure the state is at 0 */
-    l_is_valid &= (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_NONE);
-
-    /* POINTER validation */
-    /* make sure a p_j2k codec is present */
-    l_is_valid &= (p_j2k->m_procedure_list != 00);
-    /* make sure a validation list is present */
-    l_is_valid &= (p_j2k->m_validation_list != 00);
-
-    /* ISO 15444-1:2004 states between 1 & 33 (0 -> 32) */
-    /* 33 (32) would always fail the check below (if a cast to 64bits was done) */
-    /* FIXME Shall we change OPJ_J2K_MAXRLVLS to 32 ? */
-    if ((p_j2k->m_cp.tcps->tccps->numresolutions <= 0) ||
-            (p_j2k->m_cp.tcps->tccps->numresolutions > 32)) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Number of resolutions is too high in comparison to the size of tiles\n");
-        return OPJ_FALSE;
-    }
-
-    if ((p_j2k->m_cp.tdx) < (OPJ_UINT32)(1 <<
-                                         (p_j2k->m_cp.tcps->tccps->numresolutions - 1U))) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Number of resolutions is too high in comparison to the size of tiles\n");
-        return OPJ_FALSE;
-    }
-
-    if ((p_j2k->m_cp.tdy) < (OPJ_UINT32)(1 <<
-                                         (p_j2k->m_cp.tcps->tccps->numresolutions - 1U))) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Number of resolutions is too high in comparison to the size of tiles\n");
-        return OPJ_FALSE;
-    }
-
-    /* PARAMETER VALIDATION */
-    return l_is_valid;
-}
-
-static OPJ_BOOL opj_j2k_decoding_validation(opj_j2k_t *p_j2k,
-        opj_stream_private_t *p_stream,
-        opj_event_mgr_t * p_manager
-                                           )
-{
-    OPJ_BOOL l_is_valid = OPJ_TRUE;
-
-    /* preconditions*/
-    assert(p_j2k != 00);
-    assert(p_stream != 00);
-    assert(p_manager != 00);
-
-    OPJ_UNUSED(p_stream);
-    OPJ_UNUSED(p_manager);
-
-    /* STATE checking */
-    /* make sure the state is at 0 */
-#ifdef TODO_MSD
-    l_is_valid &= (p_j2k->m_specific_param.m_decoder.m_state == J2K_DEC_STATE_NONE);
-#endif
-    l_is_valid &= (p_j2k->m_specific_param.m_decoder.m_state == 0x0000);
-
-    /* POINTER validation */
-    /* make sure a p_j2k codec is present */
-    /* make sure a procedure list is present */
-    l_is_valid &= (p_j2k->m_procedure_list != 00);
-    /* make sure a validation list is present */
-    l_is_valid &= (p_j2k->m_validation_list != 00);
-
-    /* PARAMETER VALIDATION */
-    return l_is_valid;
-}
-
-static OPJ_BOOL opj_j2k_read_header_procedure(opj_j2k_t *p_j2k,
-        opj_stream_private_t *p_stream,
-        opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32 l_current_marker;
-    OPJ_UINT32 l_marker_size;
-    const opj_dec_memory_marker_handler_t * l_marker_handler = 00;
-    OPJ_BOOL l_has_siz = 0;
-    OPJ_BOOL l_has_cod = 0;
-    OPJ_BOOL l_has_qcd = 0;
-
-    /* preconditions */
-    assert(p_stream != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    /*  We enter in the main header */
-    p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_MHSOC;
-
-    /* Try to read the SOC marker, the codestream must begin with SOC marker */
-    if (! opj_j2k_read_soc(p_j2k, p_stream, p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Expected a SOC marker \n");
-        return OPJ_FALSE;
-    }
-
-    /* Try to read 2 bytes (the next marker ID) from stream and copy them into the buffer */
-    if (opj_stream_read_data(p_stream,
-                             p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
-        opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
-        return OPJ_FALSE;
-    }
-
-    /* Read 2 bytes as the new marker ID */
-    opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data,
-                   &l_current_marker, 2);
-
-    /* Try to read until the SOT is detected */
-    while (l_current_marker != J2K_MS_SOT) {
-
-        /* Check if the current marker ID is valid */
-        if (l_current_marker < 0xff00) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "A marker ID was expected (0xff--) instead of %.8x\n", l_current_marker);
-            return OPJ_FALSE;
-        }
-
-        /* Get the marker handler from the marker ID */
-        l_marker_handler = opj_j2k_get_marker_handler(l_current_marker);
-
-        /* Manage case where marker is unknown */
-        if (l_marker_handler->id == J2K_MS_UNK) {
-            if (! opj_j2k_read_unk(p_j2k, p_stream, &l_current_marker, p_manager)) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Unknow marker have been detected and generated error.\n");
-                return OPJ_FALSE;
-            }
-
-            if (l_current_marker == J2K_MS_SOT) {
-                break;    /* SOT marker is detected main header is completely read */
-            } else { /* Get the marker handler from the marker ID */
-                l_marker_handler = opj_j2k_get_marker_handler(l_current_marker);
-            }
-        }
-
-        if (l_marker_handler->id == J2K_MS_SIZ) {
-            /* Mark required SIZ marker as found */
-            l_has_siz = 1;
-        }
-        if (l_marker_handler->id == J2K_MS_COD) {
-            /* Mark required COD marker as found */
-            l_has_cod = 1;
-        }
-        if (l_marker_handler->id == J2K_MS_QCD) {
-            /* Mark required QCD marker as found */
-            l_has_qcd = 1;
-        }
-
-        /* Check if the marker is known and if it is the right place to find it */
-        if (!(p_j2k->m_specific_param.m_decoder.m_state & l_marker_handler->states)) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Marker is not compliant with its position\n");
-            return OPJ_FALSE;
-        }
-
-        /* Try to read 2 bytes (the marker size) from stream and copy them into the buffer */
-        if (opj_stream_read_data(p_stream,
-                                 p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
-            opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
-            return OPJ_FALSE;
-        }
-
-        /* read 2 bytes as the marker size */
-        opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data, &l_marker_size,
-                       2);
-        if (l_marker_size < 2) {
-            opj_event_msg(p_manager, EVT_ERROR, "Invalid marker size\n");
-            return OPJ_FALSE;
-        }
-        l_marker_size -= 2; /* Subtract the size of the marker ID already read */
-
-        /* Check if the marker size is compatible with the header data size */
-        if (l_marker_size > p_j2k->m_specific_param.m_decoder.m_header_data_size) {
-            OPJ_BYTE *new_header_data = (OPJ_BYTE *) opj_realloc(
-                                            p_j2k->m_specific_param.m_decoder.m_header_data, l_marker_size);
-            if (! new_header_data) {
-                opj_free(p_j2k->m_specific_param.m_decoder.m_header_data);
-                p_j2k->m_specific_param.m_decoder.m_header_data = NULL;
-                p_j2k->m_specific_param.m_decoder.m_header_data_size = 0;
-                opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read header\n");
-                return OPJ_FALSE;
-            }
-            p_j2k->m_specific_param.m_decoder.m_header_data = new_header_data;
-            p_j2k->m_specific_param.m_decoder.m_header_data_size = l_marker_size;
-        }
-
-        /* Try to read the rest of the marker segment from stream and copy them into the buffer */
-        if (opj_stream_read_data(p_stream,
-                                 p_j2k->m_specific_param.m_decoder.m_header_data, l_marker_size,
-                                 p_manager) != l_marker_size) {
-            opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
-            return OPJ_FALSE;
-        }
-
-        /* Read the marker segment with the correct marker handler */
-        if (!(*(l_marker_handler->handler))(p_j2k,
-                                            p_j2k->m_specific_param.m_decoder.m_header_data, l_marker_size, p_manager)) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Marker handler function failed to read the marker segment\n");
-            return OPJ_FALSE;
-        }
-
-        /* Add the marker to the codestream index*/
-        if (OPJ_FALSE == opj_j2k_add_mhmarker(
-                    p_j2k->cstr_index,
-                    l_marker_handler->id,
-                    (OPJ_UINT32) opj_stream_tell(p_stream) - l_marker_size - 4,
-                    l_marker_size + 4)) {
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to add mh marker\n");
-            return OPJ_FALSE;
-        }
-
-        /* Try to read 2 bytes (the next marker ID) from stream and copy them into the buffer */
-        if (opj_stream_read_data(p_stream,
-                                 p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
-            opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
-            return OPJ_FALSE;
-        }
-
-        /* read 2 bytes as the new marker ID */
-        opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data,
-                       &l_current_marker, 2);
-    }
-
-    if (l_has_siz == 0) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "required SIZ marker not found in main header\n");
-        return OPJ_FALSE;
-    }
-    if (l_has_cod == 0) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "required COD marker not found in main header\n");
-        return OPJ_FALSE;
-    }
-    if (l_has_qcd == 0) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "required QCD marker not found in main header\n");
-        return OPJ_FALSE;
-    }
-
-    if (! opj_j2k_merge_ppm(&(p_j2k->m_cp), p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Failed to merge PPM data\n");
-        return OPJ_FALSE;
-    }
-
-    opj_event_msg(p_manager, EVT_INFO, "Main header has been correctly decoded.\n");
-
-    /* Position of the last element if the main header */
-    p_j2k->cstr_index->main_head_end = (OPJ_UINT32) opj_stream_tell(p_stream) - 2;
-
-    /* Next step: read a tile-part header */
-    p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_TPHSOT;
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_exec(opj_j2k_t * p_j2k,
-                             opj_procedure_list_t * p_procedure_list,
-                             opj_stream_private_t *p_stream,
-                             opj_event_mgr_t * p_manager)
-{
-    OPJ_BOOL(** l_procedure)(opj_j2k_t *, opj_stream_private_t *,
-                             opj_event_mgr_t *) = 00;
-    OPJ_BOOL l_result = OPJ_TRUE;
-    OPJ_UINT32 l_nb_proc, i;
-
-    /* preconditions*/
-    assert(p_procedure_list != 00);
-    assert(p_j2k != 00);
-    assert(p_stream != 00);
-    assert(p_manager != 00);
-
-    l_nb_proc = opj_procedure_list_get_nb_procedures(p_procedure_list);
-    l_procedure = (OPJ_BOOL(**)(opj_j2k_t *, opj_stream_private_t *,
-                                opj_event_mgr_t *)) opj_procedure_list_get_first_procedure(p_procedure_list);
-
-    for (i = 0; i < l_nb_proc; ++i) {
-        l_result = l_result && ((*l_procedure)(p_j2k, p_stream, p_manager));
-        ++l_procedure;
-    }
-
-    /* and clear the procedure list at the end.*/
-    opj_procedure_list_clear(p_procedure_list);
-    return l_result;
-}
-
-/* FIXME DOC*/
-static OPJ_BOOL opj_j2k_copy_default_tcp_and_create_tcd(opj_j2k_t * p_j2k,
-        opj_stream_private_t *p_stream,
-        opj_event_mgr_t * p_manager
-                                                       )
-{
-    opj_tcp_t * l_tcp = 00;
-    opj_tcp_t * l_default_tcp = 00;
-    OPJ_UINT32 l_nb_tiles;
-    OPJ_UINT32 i, j;
-    opj_tccp_t *l_current_tccp = 00;
-    OPJ_UINT32 l_tccp_size;
-    OPJ_UINT32 l_mct_size;
-    opj_image_t * l_image;
-    OPJ_UINT32 l_mcc_records_size, l_mct_records_size;
-    opj_mct_data_t * l_src_mct_rec, *l_dest_mct_rec;
-    opj_simple_mcc_decorrelation_data_t * l_src_mcc_rec, *l_dest_mcc_rec;
-    OPJ_UINT32 l_offset;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_stream != 00);
-    assert(p_manager != 00);
-
-    OPJ_UNUSED(p_stream);
-
-    l_image = p_j2k->m_private_image;
-    l_nb_tiles = p_j2k->m_cp.th * p_j2k->m_cp.tw;
-    l_tcp = p_j2k->m_cp.tcps;
-    l_tccp_size = l_image->numcomps * (OPJ_UINT32)sizeof(opj_tccp_t);
-    l_default_tcp = p_j2k->m_specific_param.m_decoder.m_default_tcp;
-    l_mct_size = l_image->numcomps * l_image->numcomps * (OPJ_UINT32)sizeof(
-                     OPJ_FLOAT32);
-
-    /* For each tile */
-    for (i = 0; i < l_nb_tiles; ++i) {
-        /* keep the tile-compo coding parameters pointer of the current tile coding parameters*/
-        l_current_tccp = l_tcp->tccps;
-        /*Copy default coding parameters into the current tile coding parameters*/
-        memcpy(l_tcp, l_default_tcp, sizeof(opj_tcp_t));
-        /* Initialize some values of the current tile coding parameters*/
-        l_tcp->cod = 0;
-        l_tcp->ppt = 0;
-        l_tcp->ppt_data = 00;
-        l_tcp->m_current_tile_part_number = -1;
-        /* Remove memory not owned by this tile in case of early error return. */
-        l_tcp->m_mct_decoding_matrix = 00;
-        l_tcp->m_nb_max_mct_records = 0;
-        l_tcp->m_mct_records = 00;
-        l_tcp->m_nb_max_mcc_records = 0;
-        l_tcp->m_mcc_records = 00;
-        /* Reconnect the tile-compo coding parameters pointer to the current tile coding parameters*/
-        l_tcp->tccps = l_current_tccp;
-
-        /* Get the mct_decoding_matrix of the dflt_tile_cp and copy them into the current tile cp*/
-        if (l_default_tcp->m_mct_decoding_matrix) {
-            l_tcp->m_mct_decoding_matrix = (OPJ_FLOAT32*)opj_malloc(l_mct_size);
-            if (! l_tcp->m_mct_decoding_matrix) {
-                return OPJ_FALSE;
-            }
-            memcpy(l_tcp->m_mct_decoding_matrix, l_default_tcp->m_mct_decoding_matrix,
-                   l_mct_size);
-        }
-
-        /* Get the mct_record of the dflt_tile_cp and copy them into the current tile cp*/
-        l_mct_records_size = l_default_tcp->m_nb_max_mct_records * (OPJ_UINT32)sizeof(
-                                 opj_mct_data_t);
-        l_tcp->m_mct_records = (opj_mct_data_t*)opj_malloc(l_mct_records_size);
-        if (! l_tcp->m_mct_records) {
-            return OPJ_FALSE;
-        }
-        memcpy(l_tcp->m_mct_records, l_default_tcp->m_mct_records, l_mct_records_size);
-
-        /* Copy the mct record data from dflt_tile_cp to the current tile*/
-        l_src_mct_rec = l_default_tcp->m_mct_records;
-        l_dest_mct_rec = l_tcp->m_mct_records;
-
-        for (j = 0; j < l_default_tcp->m_nb_mct_records; ++j) {
-
-            if (l_src_mct_rec->m_data) {
-
-                l_dest_mct_rec->m_data = (OPJ_BYTE*) opj_malloc(l_src_mct_rec->m_data_size);
-                if (! l_dest_mct_rec->m_data) {
-                    return OPJ_FALSE;
-                }
-                memcpy(l_dest_mct_rec->m_data, l_src_mct_rec->m_data,
-                       l_src_mct_rec->m_data_size);
-            }
-
-            ++l_src_mct_rec;
-            ++l_dest_mct_rec;
-            /* Update with each pass to free exactly what has been allocated on early return. */
-            l_tcp->m_nb_max_mct_records += 1;
-        }
-
-        /* Get the mcc_record of the dflt_tile_cp and copy them into the current tile cp*/
-        l_mcc_records_size = l_default_tcp->m_nb_max_mcc_records * (OPJ_UINT32)sizeof(
-                                 opj_simple_mcc_decorrelation_data_t);
-        l_tcp->m_mcc_records = (opj_simple_mcc_decorrelation_data_t*) opj_malloc(
-                                   l_mcc_records_size);
-        if (! l_tcp->m_mcc_records) {
-            return OPJ_FALSE;
-        }
-        memcpy(l_tcp->m_mcc_records, l_default_tcp->m_mcc_records, l_mcc_records_size);
-        l_tcp->m_nb_max_mcc_records = l_default_tcp->m_nb_max_mcc_records;
-
-        /* Copy the mcc record data from dflt_tile_cp to the current tile*/
-        l_src_mcc_rec = l_default_tcp->m_mcc_records;
-        l_dest_mcc_rec = l_tcp->m_mcc_records;
-
-        for (j = 0; j < l_default_tcp->m_nb_max_mcc_records; ++j) {
-
-            if (l_src_mcc_rec->m_decorrelation_array) {
-                l_offset = (OPJ_UINT32)(l_src_mcc_rec->m_decorrelation_array -
-                                        l_default_tcp->m_mct_records);
-                l_dest_mcc_rec->m_decorrelation_array = l_tcp->m_mct_records + l_offset;
-            }
-
-            if (l_src_mcc_rec->m_offset_array) {
-                l_offset = (OPJ_UINT32)(l_src_mcc_rec->m_offset_array -
-                                        l_default_tcp->m_mct_records);
-                l_dest_mcc_rec->m_offset_array = l_tcp->m_mct_records + l_offset;
-            }
-
-            ++l_src_mcc_rec;
-            ++l_dest_mcc_rec;
-        }
-
-        /* Copy all the dflt_tile_compo_cp to the current tile cp */
-        memcpy(l_current_tccp, l_default_tcp->tccps, l_tccp_size);
-
-        /* Move to next tile cp*/
-        ++l_tcp;
-    }
-
-    /* Create the current tile decoder*/
-    p_j2k->m_tcd = opj_tcd_create(OPJ_TRUE);
-    if (! p_j2k->m_tcd) {
-        return OPJ_FALSE;
-    }
-
-    if (!opj_tcd_init(p_j2k->m_tcd, l_image, &(p_j2k->m_cp), p_j2k->m_tp)) {
-        opj_tcd_destroy(p_j2k->m_tcd);
-        p_j2k->m_tcd = 00;
-        opj_event_msg(p_manager, EVT_ERROR, "Cannot decode tile, memory error\n");
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static const opj_dec_memory_marker_handler_t * opj_j2k_get_marker_handler(
-    OPJ_UINT32 p_id)
-{
-    const opj_dec_memory_marker_handler_t *e;
-    for (e = j2k_memory_marker_handler_tab; e->id != 0; ++e) {
-        if (e->id == p_id) {
-            break; /* we find a handler corresponding to the marker ID*/
-        }
-    }
-    return e;
-}
-
-void opj_j2k_destroy(opj_j2k_t *p_j2k)
-{
-    if (p_j2k == 00) {
-        return;
-    }
-
-    if (p_j2k->m_is_decoder) {
-
-        if (p_j2k->m_specific_param.m_decoder.m_default_tcp != 00) {
-            opj_j2k_tcp_destroy(p_j2k->m_specific_param.m_decoder.m_default_tcp);
-            opj_free(p_j2k->m_specific_param.m_decoder.m_default_tcp);
-            p_j2k->m_specific_param.m_decoder.m_default_tcp = 00;
-        }
-
-        if (p_j2k->m_specific_param.m_decoder.m_header_data != 00) {
-            opj_free(p_j2k->m_specific_param.m_decoder.m_header_data);
-            p_j2k->m_specific_param.m_decoder.m_header_data = 00;
-            p_j2k->m_specific_param.m_decoder.m_header_data_size = 0;
-        }
-
-        opj_free(p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode);
-        p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode = 00;
-        p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode = 0;
-
-    } else {
-
-        if (p_j2k->m_specific_param.m_encoder.m_encoded_tile_data) {
-            opj_free(p_j2k->m_specific_param.m_encoder.m_encoded_tile_data);
-            p_j2k->m_specific_param.m_encoder.m_encoded_tile_data = 00;
-        }
-
-        if (p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer) {
-            opj_free(p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer);
-            p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer = 00;
-            p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current = 00;
-        }
-
-        if (p_j2k->m_specific_param.m_encoder.m_header_tile_data) {
-            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data = 00;
-            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
-        }
-    }
-
-    opj_tcd_destroy(p_j2k->m_tcd);
-
-    opj_j2k_cp_destroy(&(p_j2k->m_cp));
-    memset(&(p_j2k->m_cp), 0, sizeof(opj_cp_t));
-
-    opj_procedure_list_destroy(p_j2k->m_procedure_list);
-    p_j2k->m_procedure_list = 00;
-
-    opj_procedure_list_destroy(p_j2k->m_validation_list);
-    p_j2k->m_procedure_list = 00;
-
-    j2k_destroy_cstr_index(p_j2k->cstr_index);
-    p_j2k->cstr_index = NULL;
-
-    opj_image_destroy(p_j2k->m_private_image);
-    p_j2k->m_private_image = NULL;
-
-    opj_image_destroy(p_j2k->m_output_image);
-    p_j2k->m_output_image = NULL;
-
-    opj_thread_pool_destroy(p_j2k->m_tp);
-    p_j2k->m_tp = NULL;
-
-    opj_free(p_j2k);
-}
-
-void j2k_destroy_cstr_index(opj_codestream_index_t *p_cstr_ind)
-{
-    if (p_cstr_ind) {
-
-        if (p_cstr_ind->marker) {
-            opj_free(p_cstr_ind->marker);
-            p_cstr_ind->marker = NULL;
-        }
-
-        if (p_cstr_ind->tile_index) {
-            OPJ_UINT32 it_tile = 0;
-
-            for (it_tile = 0; it_tile < p_cstr_ind->nb_of_tiles; it_tile++) {
-
-                if (p_cstr_ind->tile_index[it_tile].packet_index) {
-                    opj_free(p_cstr_ind->tile_index[it_tile].packet_index);
-                    p_cstr_ind->tile_index[it_tile].packet_index = NULL;
-                }
-
-                if (p_cstr_ind->tile_index[it_tile].tp_index) {
-                    opj_free(p_cstr_ind->tile_index[it_tile].tp_index);
-                    p_cstr_ind->tile_index[it_tile].tp_index = NULL;
-                }
-
-                if (p_cstr_ind->tile_index[it_tile].marker) {
-                    opj_free(p_cstr_ind->tile_index[it_tile].marker);
-                    p_cstr_ind->tile_index[it_tile].marker = NULL;
-
-                }
-            }
-
-            opj_free(p_cstr_ind->tile_index);
-            p_cstr_ind->tile_index = NULL;
-        }
-
-        opj_free(p_cstr_ind);
-    }
-}
-
-static void opj_j2k_tcp_destroy(opj_tcp_t *p_tcp)
-{
-    if (p_tcp == 00) {
-        return;
-    }
-
-    if (p_tcp->ppt_markers != 00) {
-        OPJ_UINT32 i;
-        for (i = 0U; i < p_tcp->ppt_markers_count; ++i) {
-            if (p_tcp->ppt_markers[i].m_data != NULL) {
-                opj_free(p_tcp->ppt_markers[i].m_data);
-            }
-        }
-        p_tcp->ppt_markers_count = 0U;
-        opj_free(p_tcp->ppt_markers);
-        p_tcp->ppt_markers = NULL;
-    }
-
-    if (p_tcp->ppt_buffer != 00) {
-        opj_free(p_tcp->ppt_buffer);
-        p_tcp->ppt_buffer = 00;
-    }
-
-    if (p_tcp->tccps != 00) {
-        opj_free(p_tcp->tccps);
-        p_tcp->tccps = 00;
-    }
-
-    if (p_tcp->m_mct_coding_matrix != 00) {
-        opj_free(p_tcp->m_mct_coding_matrix);
-        p_tcp->m_mct_coding_matrix = 00;
-    }
-
-    if (p_tcp->m_mct_decoding_matrix != 00) {
-        opj_free(p_tcp->m_mct_decoding_matrix);
-        p_tcp->m_mct_decoding_matrix = 00;
-    }
-
-    if (p_tcp->m_mcc_records) {
-        opj_free(p_tcp->m_mcc_records);
-        p_tcp->m_mcc_records = 00;
-        p_tcp->m_nb_max_mcc_records = 0;
-        p_tcp->m_nb_mcc_records = 0;
-    }
-
-    if (p_tcp->m_mct_records) {
-        opj_mct_data_t * l_mct_data = p_tcp->m_mct_records;
-        OPJ_UINT32 i;
-
-        for (i = 0; i < p_tcp->m_nb_mct_records; ++i) {
-            if (l_mct_data->m_data) {
-                opj_free(l_mct_data->m_data);
-                l_mct_data->m_data = 00;
-            }
-
-            ++l_mct_data;
-        }
-
-        opj_free(p_tcp->m_mct_records);
-        p_tcp->m_mct_records = 00;
-    }
-
-    if (p_tcp->mct_norms != 00) {
-        opj_free(p_tcp->mct_norms);
-        p_tcp->mct_norms = 00;
-    }
-
-    opj_j2k_tcp_data_destroy(p_tcp);
-
-}
-
-static void opj_j2k_tcp_data_destroy(opj_tcp_t *p_tcp)
-{
-    if (p_tcp->m_data) {
-        opj_free(p_tcp->m_data);
-        p_tcp->m_data = NULL;
-        p_tcp->m_data_size = 0;
-    }
-}
-
-static void opj_j2k_cp_destroy(opj_cp_t *p_cp)
-{
-    OPJ_UINT32 l_nb_tiles;
-    opj_tcp_t * l_current_tile = 00;
-
-    if (p_cp == 00) {
-        return;
-    }
-    if (p_cp->tcps != 00) {
-        OPJ_UINT32 i;
-        l_current_tile = p_cp->tcps;
-        l_nb_tiles = p_cp->th * p_cp->tw;
-
-        for (i = 0U; i < l_nb_tiles; ++i) {
-            opj_j2k_tcp_destroy(l_current_tile);
-            ++l_current_tile;
-        }
-        opj_free(p_cp->tcps);
-        p_cp->tcps = 00;
-    }
-    if (p_cp->ppm_markers != 00) {
-        OPJ_UINT32 i;
-        for (i = 0U; i < p_cp->ppm_markers_count; ++i) {
-            if (p_cp->ppm_markers[i].m_data != NULL) {
-                opj_free(p_cp->ppm_markers[i].m_data);
-            }
-        }
-        p_cp->ppm_markers_count = 0U;
-        opj_free(p_cp->ppm_markers);
-        p_cp->ppm_markers = NULL;
-    }
-    opj_free(p_cp->ppm_buffer);
-    p_cp->ppm_buffer = 00;
-    p_cp->ppm_data =
-        NULL; /* ppm_data belongs to the allocated buffer pointed by ppm_buffer */
-    opj_free(p_cp->comment);
-    p_cp->comment = 00;
-    if (! p_cp->m_is_decoder) {
-        opj_free(p_cp->m_specific_param.m_enc.m_matrice);
-        p_cp->m_specific_param.m_enc.m_matrice = 00;
-    }
-}
-
-static OPJ_BOOL opj_j2k_need_nb_tile_parts_correction(opj_stream_private_t
-        *p_stream, OPJ_UINT32 tile_no, OPJ_BOOL* p_correction_needed,
-        opj_event_mgr_t * p_manager)
-{
-    OPJ_BYTE   l_header_data[10];
-    OPJ_OFF_T  l_stream_pos_backup;
-    OPJ_UINT32 l_current_marker;
-    OPJ_UINT32 l_marker_size;
-    OPJ_UINT32 l_tile_no, l_tot_len, l_current_part, l_num_parts;
-
-    /* initialize to no correction needed */
-    *p_correction_needed = OPJ_FALSE;
-
-    if (!opj_stream_has_seek(p_stream)) {
-        /* We can't do much in this case, seek is needed */
-        return OPJ_TRUE;
-    }
-
-    l_stream_pos_backup = opj_stream_tell(p_stream);
-    if (l_stream_pos_backup == -1) {
-        /* let's do nothing */
-        return OPJ_TRUE;
-    }
-
-    for (;;) {
-        /* Try to read 2 bytes (the next marker ID) from stream and copy them into the buffer */
-        if (opj_stream_read_data(p_stream, l_header_data, 2, p_manager) != 2) {
-            /* assume all is OK */
-            if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
-                return OPJ_FALSE;
-            }
-            return OPJ_TRUE;
-        }
-
-        /* Read 2 bytes from buffer as the new marker ID */
-        opj_read_bytes(l_header_data, &l_current_marker, 2);
-
-        if (l_current_marker != J2K_MS_SOT) {
-            /* assume all is OK */
-            if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
-                return OPJ_FALSE;
-            }
-            return OPJ_TRUE;
-        }
-
-        /* Try to read 2 bytes (the marker size) from stream and copy them into the buffer */
-        if (opj_stream_read_data(p_stream, l_header_data, 2, p_manager) != 2) {
-            opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
-            return OPJ_FALSE;
-        }
-
-        /* Read 2 bytes from the buffer as the marker size */
-        opj_read_bytes(l_header_data, &l_marker_size, 2);
-
-        /* Check marker size for SOT Marker */
-        if (l_marker_size != 10) {
-            opj_event_msg(p_manager, EVT_ERROR, "Inconsistent marker size\n");
-            return OPJ_FALSE;
-        }
-        l_marker_size -= 2;
-
-        if (opj_stream_read_data(p_stream, l_header_data, l_marker_size,
-                                 p_manager) != l_marker_size) {
-            opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
-            return OPJ_FALSE;
-        }
-
-        if (! opj_j2k_get_sot_values(l_header_data, l_marker_size, &l_tile_no,
-                                     &l_tot_len, &l_current_part, &l_num_parts, p_manager)) {
-            return OPJ_FALSE;
-        }
-
-        if (l_tile_no == tile_no) {
-            /* we found what we were looking for */
-            break;
-        }
-
-        if (l_tot_len < 14U) {
-            /* last SOT until EOC or invalid Psot value */
-            /* assume all is OK */
-            if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
-                return OPJ_FALSE;
-            }
-            return OPJ_TRUE;
-        }
-        l_tot_len -= 12U;
-        /* look for next SOT marker */
-        if (opj_stream_skip(p_stream, (OPJ_OFF_T)(l_tot_len),
-                            p_manager) != (OPJ_OFF_T)(l_tot_len)) {
-            /* assume all is OK */
-            if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
-                return OPJ_FALSE;
-            }
-            return OPJ_TRUE;
-        }
-    }
-
-    /* check for correction */
-    if (l_current_part == l_num_parts) {
-        *p_correction_needed = OPJ_TRUE;
-    }
-
-    if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
-        return OPJ_FALSE;
-    }
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_j2k_read_tile_header(opj_j2k_t * p_j2k,
-                                  OPJ_UINT32 * p_tile_index,
-                                  OPJ_UINT32 * p_data_size,
-                                  OPJ_INT32 * p_tile_x0, OPJ_INT32 * p_tile_y0,
-                                  OPJ_INT32 * p_tile_x1, OPJ_INT32 * p_tile_y1,
-                                  OPJ_UINT32 * p_nb_comps,
-                                  OPJ_BOOL * p_go_on,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32 l_current_marker = J2K_MS_SOT;
-    OPJ_UINT32 l_marker_size;
-    const opj_dec_memory_marker_handler_t * l_marker_handler = 00;
-    opj_tcp_t * l_tcp = NULL;
-
-    /* preconditions */
-    assert(p_stream != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    /* Reach the End Of Codestream ?*/
-    if (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_EOC) {
-        l_current_marker = J2K_MS_EOC;
-    }
-    /* We need to encounter a SOT marker (a new tile-part header) */
-    else if (p_j2k->m_specific_param.m_decoder.m_state != J2K_STATE_TPHSOT) {
-        return OPJ_FALSE;
-    }
-
-    /* Read into the codestream until reach the EOC or ! can_decode ??? FIXME */
-    while ((!p_j2k->m_specific_param.m_decoder.m_can_decode) &&
-            (l_current_marker != J2K_MS_EOC)) {
-
-        /* Try to read until the Start Of Data is detected */
-        while (l_current_marker != J2K_MS_SOD) {
-
-            if (opj_stream_get_number_byte_left(p_stream) == 0) {
-                p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_NEOC;
-                break;
-            }
-
-            /* Try to read 2 bytes (the marker size) from stream and copy them into the buffer */
-            if (opj_stream_read_data(p_stream,
-                                     p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
-                opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
-                return OPJ_FALSE;
-            }
-
-            /* Read 2 bytes from the buffer as the marker size */
-            opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data, &l_marker_size,
-                           2);
-
-            /* Check marker size (does not include marker ID but includes marker size) */
-            if (l_marker_size < 2) {
-                opj_event_msg(p_manager, EVT_ERROR, "Inconsistent marker size\n");
-                return OPJ_FALSE;
-            }
-
-            /* cf. https://code.google.com/p/openjpeg/issues/detail?id=226 */
-            if (l_current_marker == 0x8080 &&
-                    opj_stream_get_number_byte_left(p_stream) == 0) {
-                p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_NEOC;
-                break;
-            }
-
-            /* Why this condition? FIXME */
-            if (p_j2k->m_specific_param.m_decoder.m_state & J2K_STATE_TPH) {
-                p_j2k->m_specific_param.m_decoder.m_sot_length -= (l_marker_size + 2);
-            }
-            l_marker_size -= 2; /* Subtract the size of the marker ID already read */
-
-            /* Get the marker handler from the marker ID */
-            l_marker_handler = opj_j2k_get_marker_handler(l_current_marker);
-
-            /* Check if the marker is known and if it is the right place to find it */
-            if (!(p_j2k->m_specific_param.m_decoder.m_state & l_marker_handler->states)) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Marker is not compliant with its position\n");
-                return OPJ_FALSE;
-            }
-            /* FIXME manage case of unknown marker as in the main header ? */
-
-            /* Check if the marker size is compatible with the header data size */
-            if (l_marker_size > p_j2k->m_specific_param.m_decoder.m_header_data_size) {
-                OPJ_BYTE *new_header_data = NULL;
-                /* If we are here, this means we consider this marker as known & we will read it */
-                /* Check enough bytes left in stream before allocation */
-                if ((OPJ_OFF_T)l_marker_size >  opj_stream_get_number_byte_left(p_stream)) {
-                    opj_event_msg(p_manager, EVT_ERROR,
-                                  "Marker size inconsistent with stream length\n");
-                    return OPJ_FALSE;
-                }
-                new_header_data = (OPJ_BYTE *) opj_realloc(
-                                      p_j2k->m_specific_param.m_decoder.m_header_data, l_marker_size);
-                if (! new_header_data) {
-                    opj_free(p_j2k->m_specific_param.m_decoder.m_header_data);
-                    p_j2k->m_specific_param.m_decoder.m_header_data = NULL;
-                    p_j2k->m_specific_param.m_decoder.m_header_data_size = 0;
-                    opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read header\n");
-                    return OPJ_FALSE;
-                }
-                p_j2k->m_specific_param.m_decoder.m_header_data = new_header_data;
-                p_j2k->m_specific_param.m_decoder.m_header_data_size = l_marker_size;
-            }
-
-            /* Try to read the rest of the marker segment from stream and copy them into the buffer */
-            if (opj_stream_read_data(p_stream,
-                                     p_j2k->m_specific_param.m_decoder.m_header_data, l_marker_size,
-                                     p_manager) != l_marker_size) {
-                opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
-                return OPJ_FALSE;
-            }
-
-            if (!l_marker_handler->handler) {
-                /* See issue #175 */
-                opj_event_msg(p_manager, EVT_ERROR, "Not sure how that happened.\n");
-                return OPJ_FALSE;
-            }
-            /* Read the marker segment with the correct marker handler */
-            if (!(*(l_marker_handler->handler))(p_j2k,
-                                                p_j2k->m_specific_param.m_decoder.m_header_data, l_marker_size, p_manager)) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Fail to read the current marker segment (%#x)\n", l_current_marker);
-                return OPJ_FALSE;
-            }
-
-            /* Add the marker to the codestream index*/
-            if (OPJ_FALSE == opj_j2k_add_tlmarker(p_j2k->m_current_tile_number,
-                                                  p_j2k->cstr_index,
-                                                  l_marker_handler->id,
-                                                  (OPJ_UINT32) opj_stream_tell(p_stream) - l_marker_size - 4,
-                                                  l_marker_size + 4)) {
-                opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to add tl marker\n");
-                return OPJ_FALSE;
-            }
-
-            /* Keep the position of the last SOT marker read */
-            if (l_marker_handler->id == J2K_MS_SOT) {
-                OPJ_UINT32 sot_pos = (OPJ_UINT32) opj_stream_tell(p_stream) - l_marker_size - 4
-                                     ;
-                if (sot_pos > p_j2k->m_specific_param.m_decoder.m_last_sot_read_pos) {
-                    p_j2k->m_specific_param.m_decoder.m_last_sot_read_pos = sot_pos;
-                }
-            }
-
-            if (p_j2k->m_specific_param.m_decoder.m_skip_data) {
-                /* Skip the rest of the tile part header*/
-                if (opj_stream_skip(p_stream, p_j2k->m_specific_param.m_decoder.m_sot_length,
-                                    p_manager) != p_j2k->m_specific_param.m_decoder.m_sot_length) {
-                    opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
-                    return OPJ_FALSE;
-                }
-                l_current_marker = J2K_MS_SOD; /* Normally we reached a SOD */
-            } else {
-                /* Try to read 2 bytes (the next marker ID) from stream and copy them into the buffer*/
-                if (opj_stream_read_data(p_stream,
-                                         p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
-                    opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
-                    return OPJ_FALSE;
-                }
-                /* Read 2 bytes from the buffer as the new marker ID */
-                opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data,
-                               &l_current_marker, 2);
-            }
-        }
-        if (opj_stream_get_number_byte_left(p_stream) == 0
-                && p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_NEOC) {
-            break;
-        }
-
-        /* If we didn't skip data before, we need to read the SOD marker*/
-        if (! p_j2k->m_specific_param.m_decoder.m_skip_data) {
-            /* Try to read the SOD marker and skip data ? FIXME */
-            if (! opj_j2k_read_sod(p_j2k, p_stream, p_manager)) {
-                return OPJ_FALSE;
-            }
-            if (p_j2k->m_specific_param.m_decoder.m_can_decode &&
-                    !p_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction_checked) {
-                /* Issue 254 */
-                OPJ_BOOL l_correction_needed;
-
-                p_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction_checked = 1;
-                if (!opj_j2k_need_nb_tile_parts_correction(p_stream,
-                        p_j2k->m_current_tile_number, &l_correction_needed, p_manager)) {
-                    opj_event_msg(p_manager, EVT_ERROR,
-                                  "opj_j2k_apply_nb_tile_parts_correction error\n");
-                    return OPJ_FALSE;
-                }
-                if (l_correction_needed) {
-                    OPJ_UINT32 l_nb_tiles = p_j2k->m_cp.tw * p_j2k->m_cp.th;
-                    OPJ_UINT32 l_tile_no;
-
-                    p_j2k->m_specific_param.m_decoder.m_can_decode = 0;
-                    p_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction = 1;
-                    /* correct tiles */
-                    for (l_tile_no = 0U; l_tile_no < l_nb_tiles; ++l_tile_no) {
-                        if (p_j2k->m_cp.tcps[l_tile_no].m_nb_tile_parts != 0U) {
-                            p_j2k->m_cp.tcps[l_tile_no].m_nb_tile_parts += 1;
-                        }
-                    }
-                    opj_event_msg(p_manager, EVT_WARNING,
-                                  "Non conformant codestream TPsot==TNsot.\n");
-                }
-            }
-            if (! p_j2k->m_specific_param.m_decoder.m_can_decode) {
-                /* Try to read 2 bytes (the next marker ID) from stream and copy them into the buffer */
-                if (opj_stream_read_data(p_stream,
-                                         p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
-                    opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
-                    return OPJ_FALSE;
-                }
-
-                /* Read 2 bytes from buffer as the new marker ID */
-                opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data,
-                               &l_current_marker, 2);
-            }
-        } else {
-            /* Indicate we will try to read a new tile-part header*/
-            p_j2k->m_specific_param.m_decoder.m_skip_data = 0;
-            p_j2k->m_specific_param.m_decoder.m_can_decode = 0;
-            p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_TPHSOT;
-
-            /* Try to read 2 bytes (the next marker ID) from stream and copy them into the buffer */
-            if (opj_stream_read_data(p_stream,
-                                     p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
-                opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
-                return OPJ_FALSE;
-            }
-
-            /* Read 2 bytes from buffer as the new marker ID */
-            opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data,
-                           &l_current_marker, 2);
-        }
-    }
-
-    /* Current marker is the EOC marker ?*/
-    if (l_current_marker == J2K_MS_EOC) {
-        if (p_j2k->m_specific_param.m_decoder.m_state != J2K_STATE_EOC) {
-            p_j2k->m_current_tile_number = 0;
-            p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_EOC;
-        }
-    }
-
-    /* FIXME DOC ???*/
-    if (! p_j2k->m_specific_param.m_decoder.m_can_decode) {
-        OPJ_UINT32 l_nb_tiles = p_j2k->m_cp.th * p_j2k->m_cp.tw;
-        l_tcp = p_j2k->m_cp.tcps + p_j2k->m_current_tile_number;
-
-        while ((p_j2k->m_current_tile_number < l_nb_tiles) && (l_tcp->m_data == 00)) {
-            ++p_j2k->m_current_tile_number;
-            ++l_tcp;
-        }
-
-        if (p_j2k->m_current_tile_number == l_nb_tiles) {
-            *p_go_on = OPJ_FALSE;
-            return OPJ_TRUE;
-        }
-    }
-
-    if (! opj_j2k_merge_ppt(p_j2k->m_cp.tcps + p_j2k->m_current_tile_number,
-                            p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Failed to merge PPT data\n");
-        return OPJ_FALSE;
-    }
-    /*FIXME ???*/
-    if (! opj_tcd_init_decode_tile(p_j2k->m_tcd, p_j2k->m_current_tile_number,
-                                   p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Cannot decode tile, memory error\n");
-        return OPJ_FALSE;
-    }
-
-    opj_event_msg(p_manager, EVT_INFO, "Header of tile %d / %d has been read.\n",
-                  p_j2k->m_current_tile_number + 1, (p_j2k->m_cp.th * p_j2k->m_cp.tw));
-
-    *p_tile_index = p_j2k->m_current_tile_number;
-    *p_go_on = OPJ_TRUE;
-    if (p_data_size) {
-        /* For internal use in j2k.c, we don't need this */
-        /* This is just needed for folks using the opj_read_tile_header() / opj_decode_tile_data() combo */
-        *p_data_size = opj_tcd_get_decoded_tile_size(p_j2k->m_tcd, OPJ_FALSE);
-        if (*p_data_size == UINT_MAX) {
-            return OPJ_FALSE;
-        }
-    }
-    *p_tile_x0 = p_j2k->m_tcd->tcd_image->tiles->x0;
-    *p_tile_y0 = p_j2k->m_tcd->tcd_image->tiles->y0;
-    *p_tile_x1 = p_j2k->m_tcd->tcd_image->tiles->x1;
-    *p_tile_y1 = p_j2k->m_tcd->tcd_image->tiles->y1;
-    *p_nb_comps = p_j2k->m_tcd->tcd_image->tiles->numcomps;
-
-    p_j2k->m_specific_param.m_decoder.m_state |= J2K_STATE_DATA;
-
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_j2k_decode_tile(opj_j2k_t * p_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_UINT32 l_current_marker;
-    OPJ_BYTE l_data [2];
-    opj_tcp_t * l_tcp;
-    opj_image_t* l_image_for_bounds;
-
-    /* preconditions */
-    assert(p_stream != 00);
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    if (!(p_j2k->m_specific_param.m_decoder.m_state & J2K_STATE_DATA)
-            || (p_tile_index != p_j2k->m_current_tile_number)) {
-        return OPJ_FALSE;
-    }
-
-    l_tcp = &(p_j2k->m_cp.tcps[p_tile_index]);
-    if (! l_tcp->m_data) {
-        opj_j2k_tcp_destroy(l_tcp);
-        return OPJ_FALSE;
-    }
-
-    /* When using the opj_read_tile_header / opj_decode_tile_data API */
-    /* such as in test_tile_decoder, m_output_image is NULL, so fall back */
-    /* to the full image dimension. This is a bit surprising that */
-    /* opj_set_decode_area() is only used to determinte intersecting tiles, */
-    /* but full tile decoding is done */
-    l_image_for_bounds = p_j2k->m_output_image ? p_j2k->m_output_image :
-                         p_j2k->m_private_image;
-    if (! opj_tcd_decode_tile(p_j2k->m_tcd,
-                              l_image_for_bounds->x0,
-                              l_image_for_bounds->y0,
-                              l_image_for_bounds->x1,
-                              l_image_for_bounds->y1,
-                              p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode,
-                              p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode,
-                              l_tcp->m_data,
-                              l_tcp->m_data_size,
-                              p_tile_index,
-                              p_j2k->cstr_index, p_manager)) {
-        opj_j2k_tcp_destroy(l_tcp);
-        p_j2k->m_specific_param.m_decoder.m_state |= J2K_STATE_ERR;
-        opj_event_msg(p_manager, EVT_ERROR, "Failed to decode.\n");
-        return OPJ_FALSE;
-    }
-
-    /* p_data can be set to NULL when the call will take care of using */
-    /* itself the TCD data. This is typically the case for whole single */
-    /* tile decoding optimization. */
-    if (p_data != NULL) {
-        if (! opj_tcd_update_tile_data(p_j2k->m_tcd, p_data, p_data_size)) {
-            return OPJ_FALSE;
-        }
-
-        /* To avoid to destroy the tcp which can be useful when we try to decode a tile decoded before (cf j2k_random_tile_access)
-        * we destroy just the data which will be re-read in read_tile_header*/
-        /*opj_j2k_tcp_destroy(l_tcp);
-        p_j2k->m_tcd->tcp = 0;*/
-        opj_j2k_tcp_data_destroy(l_tcp);
-    }
-
-    p_j2k->m_specific_param.m_decoder.m_can_decode = 0;
-    p_j2k->m_specific_param.m_decoder.m_state &= (~(OPJ_UINT32)J2K_STATE_DATA);
-
-    if (opj_stream_get_number_byte_left(p_stream) == 0
-            && p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_NEOC) {
-        return OPJ_TRUE;
-    }
-
-    if (p_j2k->m_specific_param.m_decoder.m_state != J2K_STATE_EOC) {
-        if (opj_stream_read_data(p_stream, l_data, 2, p_manager) != 2) {
-            opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
-            return OPJ_FALSE;
-        }
-
-        opj_read_bytes(l_data, &l_current_marker, 2);
-
-        if (l_current_marker == J2K_MS_EOC) {
-            p_j2k->m_current_tile_number = 0;
-            p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_EOC;
-        } else if (l_current_marker != J2K_MS_SOT) {
-            if (opj_stream_get_number_byte_left(p_stream) == 0) {
-                p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_NEOC;
-                opj_event_msg(p_manager, EVT_WARNING, "Stream does not end with EOC\n");
-                return OPJ_TRUE;
-            }
-            opj_event_msg(p_manager, EVT_ERROR, "Stream too short, expected SOT\n");
-            return OPJ_FALSE;
-        }
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_update_image_data(opj_tcd_t * p_tcd,
-        opj_image_t* p_output_image)
-{
-    OPJ_UINT32 i, j;
-    OPJ_UINT32 l_width_src, l_height_src;
-    OPJ_UINT32 l_width_dest, l_height_dest;
-    OPJ_INT32 l_offset_x0_src, l_offset_y0_src, l_offset_x1_src, l_offset_y1_src;
-    OPJ_SIZE_T l_start_offset_src;
-    OPJ_UINT32 l_start_x_dest, l_start_y_dest;
-    OPJ_UINT32 l_x0_dest, l_y0_dest, l_x1_dest, l_y1_dest;
-    OPJ_SIZE_T l_start_offset_dest;
-
-    opj_image_comp_t * l_img_comp_src = 00;
-    opj_image_comp_t * l_img_comp_dest = 00;
-
-    opj_tcd_tilecomp_t * l_tilec = 00;
-    opj_image_t * l_image_src = 00;
-    OPJ_INT32 * l_dest_ptr;
-
-    l_tilec = p_tcd->tcd_image->tiles->comps;
-    l_image_src = p_tcd->image;
-    l_img_comp_src = l_image_src->comps;
-
-    l_img_comp_dest = p_output_image->comps;
-
-    for (i = 0; i < l_image_src->numcomps;
-            i++, ++l_img_comp_dest, ++l_img_comp_src,  ++l_tilec) {
-        OPJ_INT32 res_x0, res_x1, res_y0, res_y1;
-        OPJ_UINT32 src_data_stride;
-        const OPJ_INT32* p_src_data;
-
-        /* Copy info from decoded comp image to output image */
-        l_img_comp_dest->resno_decoded = l_img_comp_src->resno_decoded;
-
-        if (p_tcd->whole_tile_decoding) {
-            opj_tcd_resolution_t* l_res = l_tilec->resolutions +
-                                          l_img_comp_src->resno_decoded;
-            res_x0 = l_res->x0;
-            res_y0 = l_res->y0;
-            res_x1 = l_res->x1;
-            res_y1 = l_res->y1;
-            src_data_stride = (OPJ_UINT32)(
-                                  l_tilec->resolutions[l_tilec->minimum_num_resolutions - 1].x1 -
-                                  l_tilec->resolutions[l_tilec->minimum_num_resolutions - 1].x0);
-            p_src_data = l_tilec->data;
-        } else {
-            opj_tcd_resolution_t* l_res = l_tilec->resolutions +
-                                          l_img_comp_src->resno_decoded;
-            res_x0 = (OPJ_INT32)l_res->win_x0;
-            res_y0 = (OPJ_INT32)l_res->win_y0;
-            res_x1 = (OPJ_INT32)l_res->win_x1;
-            res_y1 = (OPJ_INT32)l_res->win_y1;
-            src_data_stride = l_res->win_x1 - l_res->win_x0;
-            p_src_data = l_tilec->data_win;
-        }
-
-        if (p_src_data == NULL) {
-            /* Happens for partial component decoding */
-            continue;
-        }
-
-        l_width_src = (OPJ_UINT32)(res_x1 - res_x0);
-        l_height_src = (OPJ_UINT32)(res_y1 - res_y0);
-
-
-        /* Current tile component size*/
-        /*if (i == 0) {
-        fprintf(stdout, "SRC: l_res_x0=%d, l_res_x1=%d, l_res_y0=%d, l_res_y1=%d\n",
-                        res_x0, res_x1, res_y0, res_y1);
-        }*/
-
-
-        /* Border of the current output component*/
-        l_x0_dest = opj_uint_ceildivpow2(l_img_comp_dest->x0, l_img_comp_dest->factor);
-        l_y0_dest = opj_uint_ceildivpow2(l_img_comp_dest->y0, l_img_comp_dest->factor);
-        l_x1_dest = l_x0_dest +
-                    l_img_comp_dest->w; /* can't overflow given that image->x1 is uint32 */
-        l_y1_dest = l_y0_dest + l_img_comp_dest->h;
-
-        /*if (i == 0) {
-        fprintf(stdout, "DEST: l_x0_dest=%d, l_x1_dest=%d, l_y0_dest=%d, l_y1_dest=%d (%d)\n",
-                        l_x0_dest, l_x1_dest, l_y0_dest, l_y1_dest, l_img_comp_dest->factor );
-        }*/
-
-        /*-----*/
-        /* Compute the area (l_offset_x0_src, l_offset_y0_src, l_offset_x1_src, l_offset_y1_src)
-         * of the input buffer (decoded tile component) which will be move
-         * in the output buffer. Compute the area of the output buffer (l_start_x_dest,
-         * l_start_y_dest, l_width_dest, l_height_dest)  which will be modified
-         * by this input area.
-         * */
-        assert(res_x0 >= 0);
-        assert(res_x1 >= 0);
-
-        /* Prevent bad casting to unsigned values in the subsequent lines. */
-        if ( res_x0 < 0 || res_x1 < 0 || res_y0 < 0 || res_y1 < 0 ) {
-            return OPJ_FALSE;
-        }
-
-        if (l_x0_dest < (OPJ_UINT32)res_x0) {
-            l_start_x_dest = (OPJ_UINT32)res_x0 - l_x0_dest;
-            l_offset_x0_src = 0;
-
-            if (l_x1_dest >= (OPJ_UINT32)res_x1) {
-                l_width_dest = l_width_src;
-                l_offset_x1_src = 0;
-            } else {
-                l_width_dest = l_x1_dest - (OPJ_UINT32)res_x0 ;
-                l_offset_x1_src = (OPJ_INT32)(l_width_src - l_width_dest);
-            }
-        } else {
-            l_start_x_dest = 0U;
-            l_offset_x0_src = (OPJ_INT32)l_x0_dest - res_x0;
-
-            if (l_x1_dest >= (OPJ_UINT32)res_x1) {
-                l_width_dest = l_width_src - (OPJ_UINT32)l_offset_x0_src;
-                l_offset_x1_src = 0;
-            } else {
-                l_width_dest = l_img_comp_dest->w ;
-                l_offset_x1_src = res_x1 - (OPJ_INT32)l_x1_dest;
-            }
-        }
-
-        if (l_y0_dest < (OPJ_UINT32)res_y0) {
-            l_start_y_dest = (OPJ_UINT32)res_y0 - l_y0_dest;
-            l_offset_y0_src = 0;
-
-            if (l_y1_dest >= (OPJ_UINT32)res_y1) {
-                l_height_dest = l_height_src;
-                l_offset_y1_src = 0;
-            } else {
-                l_height_dest = l_y1_dest - (OPJ_UINT32)res_y0 ;
-                l_offset_y1_src = (OPJ_INT32)(l_height_src - l_height_dest);
-            }
-        } else {
-            l_start_y_dest = 0U;
-            l_offset_y0_src = (OPJ_INT32)l_y0_dest - res_y0;
-
-            if (l_y1_dest >= (OPJ_UINT32)res_y1) {
-                l_height_dest = l_height_src - (OPJ_UINT32)l_offset_y0_src;
-                l_offset_y1_src = 0;
-            } else {
-                l_height_dest = l_img_comp_dest->h ;
-                l_offset_y1_src = res_y1 - (OPJ_INT32)l_y1_dest;
-            }
-        }
-
-        if ((l_offset_x0_src < 0) || (l_offset_y0_src < 0) || (l_offset_x1_src < 0) ||
-                (l_offset_y1_src < 0)) {
-            return OPJ_FALSE;
-        }
-        /* testcase 2977.pdf.asan.67.2198 */
-        if ((OPJ_INT32)l_width_dest < 0 || (OPJ_INT32)l_height_dest < 0) {
-            return OPJ_FALSE;
-        }
-        /*-----*/
-
-        /* Compute the input buffer offset */
-        l_start_offset_src = (OPJ_SIZE_T)l_offset_x0_src + (OPJ_SIZE_T)l_offset_y0_src
-                             * (OPJ_SIZE_T)src_data_stride;
-
-        /* Compute the output buffer offset */
-        l_start_offset_dest = (OPJ_SIZE_T)l_start_x_dest + (OPJ_SIZE_T)l_start_y_dest
-                              * (OPJ_SIZE_T)l_img_comp_dest->w;
-
-        /* Allocate output component buffer if necessary */
-        if (l_img_comp_dest->data == NULL &&
-                l_start_offset_src == 0 && l_start_offset_dest == 0 &&
-                src_data_stride == l_img_comp_dest->w &&
-                l_width_dest == l_img_comp_dest->w &&
-                l_height_dest == l_img_comp_dest->h) {
-            /* If the final image matches the tile buffer, then borrow it */
-            /* directly to save a copy */
-            if (p_tcd->whole_tile_decoding) {
-                l_img_comp_dest->data = l_tilec->data;
-                l_tilec->data = NULL;
-            } else {
-                l_img_comp_dest->data = l_tilec->data_win;
-                l_tilec->data_win = NULL;
-            }
-            continue;
-        } else if (l_img_comp_dest->data == NULL) {
-            OPJ_SIZE_T l_width = l_img_comp_dest->w;
-            OPJ_SIZE_T l_height = l_img_comp_dest->h;
-
-            if ((l_height == 0U) || (l_width > (SIZE_MAX / l_height)) ||
-                    l_width * l_height > SIZE_MAX / sizeof(OPJ_INT32)) {
-                /* would overflow */
-                return OPJ_FALSE;
-            }
-            l_img_comp_dest->data = (OPJ_INT32*) opj_image_data_alloc(l_width * l_height *
-                                    sizeof(OPJ_INT32));
-            if (! l_img_comp_dest->data) {
-                return OPJ_FALSE;
-            }
-
-            if (l_img_comp_dest->w != l_width_dest ||
-                    l_img_comp_dest->h != l_height_dest) {
-                memset(l_img_comp_dest->data, 0,
-                       (OPJ_SIZE_T)l_img_comp_dest->w * l_img_comp_dest->h * sizeof(OPJ_INT32));
-            }
-        }
-
-        /* Move the output buffer to the first place where we will write*/
-        l_dest_ptr = l_img_comp_dest->data + l_start_offset_dest;
-
-        {
-            const OPJ_INT32 * l_src_ptr = p_src_data;
-            l_src_ptr += l_start_offset_src;
-
-            for (j = 0; j < l_height_dest; ++j) {
-                memcpy(l_dest_ptr, l_src_ptr, l_width_dest * sizeof(OPJ_INT32));
-                l_dest_ptr += l_img_comp_dest->w;
-                l_src_ptr += src_data_stride;
-            }
-        }
-
-
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_update_image_dimensions(opj_image_t* p_image,
-        opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32 it_comp;
-    OPJ_INT32 l_comp_x1, l_comp_y1;
-    opj_image_comp_t* l_img_comp = NULL;
-
-    l_img_comp = p_image->comps;
-    for (it_comp = 0; it_comp < p_image->numcomps; ++it_comp) {
-        l_img_comp->x0 = opj_uint_ceildiv(p_image->x0, l_img_comp->dx);
-        l_img_comp->y0 = opj_uint_ceildiv(p_image->y0, l_img_comp->dy);
-        l_comp_x1 = opj_int_ceildiv((OPJ_INT32)p_image->x1, (OPJ_INT32)l_img_comp->dx);
-        l_comp_y1 = opj_int_ceildiv((OPJ_INT32)p_image->y1, (OPJ_INT32)l_img_comp->dy);
-
-        OPJ_INT32 l_1 = opj_int_ceildivpow2(l_comp_x1, (OPJ_INT32)l_img_comp->factor);
-        OPJ_INT32 l_2 = opj_int_ceildivpow2((OPJ_INT32)l_img_comp->x0, (OPJ_INT32)l_img_comp->factor);
-        if (l_1 < l_2) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Size x of the decoded component image is incorrect (comp[%d].w<0).\n",
-                          it_comp);
-            return OPJ_FALSE;
-        }
-        l_img_comp->w = (OPJ_UINT32)(l_1-l_2);
-
-        l_1 = opj_int_ceildivpow2(l_comp_y1, (OPJ_INT32)l_img_comp->factor);
-        l_2 = opj_int_ceildivpow2((OPJ_INT32)l_img_comp->y0, (OPJ_INT32)l_img_comp->factor);
-        if (l_1 < l_2) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Size y of the decoded component image is incorrect (comp[%d].h<0).\n",
-                          it_comp);
-            return OPJ_FALSE;
-        }
-        l_img_comp->h = (OPJ_UINT32)(l_1-l_2);
-
-        l_img_comp++;
-    }
-
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_j2k_set_decoded_components(opj_j2k_t *p_j2k,
-                                        OPJ_UINT32 numcomps,
-                                        const OPJ_UINT32* comps_indices,
-                                        opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32 i;
-    OPJ_BOOL* already_mapped;
-
-    if (p_j2k->m_private_image == NULL) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "opj_read_header() should be called before "
-                      "opj_set_decoded_components().\n");
-        return OPJ_FALSE;
-    }
-
-    already_mapped = (OPJ_BOOL*) opj_calloc(sizeof(OPJ_BOOL),
-                                            p_j2k->m_private_image->numcomps);
-    if (already_mapped == NULL) {
-        return OPJ_FALSE;
-    }
-
-    for (i = 0; i < numcomps; i++) {
-        if (comps_indices[i] >= p_j2k->m_private_image->numcomps) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Invalid component index: %u\n",
-                          comps_indices[i]);
-            opj_free(already_mapped);
-            return OPJ_FALSE;
-        }
-        if (already_mapped[comps_indices[i]]) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Component index %u used several times\n",
-                          comps_indices[i]);
-            opj_free(already_mapped);
-            return OPJ_FALSE;
-        }
-        already_mapped[comps_indices[i]] = OPJ_TRUE;
-    }
-    opj_free(already_mapped);
-
-    opj_free(p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode);
-    if (numcomps) {
-        p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode =
-            (OPJ_UINT32*) opj_malloc(numcomps * sizeof(OPJ_UINT32));
-        if (p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode == NULL) {
-            p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode = 0;
-            return OPJ_FALSE;
-        }
-        memcpy(p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode,
-               comps_indices,
-               numcomps * sizeof(OPJ_UINT32));
-    } else {
-        p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode = NULL;
-    }
-    p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode = numcomps;
-
-    return OPJ_TRUE;
-}
-
-
-OPJ_BOOL opj_j2k_set_decode_area(opj_j2k_t *p_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_cp_t * l_cp = &(p_j2k->m_cp);
-    opj_image_t * l_image = p_j2k->m_private_image;
-    OPJ_BOOL ret;
-    OPJ_UINT32 it_comp;
-
-    if (p_j2k->m_cp.tw == 1 && p_j2k->m_cp.th == 1 &&
-            p_j2k->m_cp.tcps[0].m_data != NULL) {
-        /* In the case of a single-tiled image whose codestream we have already */
-        /* ingested, go on */
-    }
-    /* Check if we are read the main header */
-    else if (p_j2k->m_specific_param.m_decoder.m_state != J2K_STATE_TPHSOT) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Need to decode the main header before begin to decode the remaining codestream.\n");
-        return OPJ_FALSE;
-    }
-
-    /* Update the comps[].factor member of the output image with the one */
-    /* of m_reduce */
-    for (it_comp = 0; it_comp < p_image->numcomps; ++it_comp) {
-        p_image->comps[it_comp].factor = p_j2k->m_cp.m_specific_param.m_dec.m_reduce;
-    }
-
-    if (!p_start_x && !p_start_y && !p_end_x && !p_end_y) {
-        opj_event_msg(p_manager, EVT_INFO,
-                      "No decoded area parameters, set the decoded area to the whole image\n");
-
-        p_j2k->m_specific_param.m_decoder.m_start_tile_x = 0;
-        p_j2k->m_specific_param.m_decoder.m_start_tile_y = 0;
-        p_j2k->m_specific_param.m_decoder.m_end_tile_x = l_cp->tw;
-        p_j2k->m_specific_param.m_decoder.m_end_tile_y = l_cp->th;
-
-        p_image->x0 = l_image->x0;
-        p_image->y0 = l_image->y0;
-        p_image->x1 = l_image->x1;
-        p_image->y1 = l_image->y1;
-
-        return opj_j2k_update_image_dimensions(p_image, p_manager);
-    }
-
-    /* ----- */
-    /* Check if the positions provided by the user are correct */
-
-    /* Left */
-    if (p_start_x < 0) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Left position of the decoded area (region_x0=%d) should be >= 0.\n",
-                      p_start_x);
-        return OPJ_FALSE;
-    } else if ((OPJ_UINT32)p_start_x > l_image->x1) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Left position of the decoded area (region_x0=%d) is outside the image area (Xsiz=%d).\n",
-                      p_start_x, l_image->x1);
-        return OPJ_FALSE;
-    } else if ((OPJ_UINT32)p_start_x < l_image->x0) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "Left position of the decoded area (region_x0=%d) is outside the image area (XOsiz=%d).\n",
-                      p_start_x, l_image->x0);
-        p_j2k->m_specific_param.m_decoder.m_start_tile_x = 0;
-        p_image->x0 = l_image->x0;
-    } else {
-        p_j2k->m_specific_param.m_decoder.m_start_tile_x = ((OPJ_UINT32)p_start_x -
-                l_cp->tx0) / l_cp->tdx;
-        p_image->x0 = (OPJ_UINT32)p_start_x;
-    }
-
-    /* Up */
-    if (p_start_y < 0) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Up position of the decoded area (region_y0=%d) should be >= 0.\n",
-                      p_start_y);
-        return OPJ_FALSE;
-    } else if ((OPJ_UINT32)p_start_y > l_image->y1) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Up position of the decoded area (region_y0=%d) is outside the image area (Ysiz=%d).\n",
-                      p_start_y, l_image->y1);
-        return OPJ_FALSE;
-    } else if ((OPJ_UINT32)p_start_y < l_image->y0) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "Up position of the decoded area (region_y0=%d) is outside the image area (YOsiz=%d).\n",
-                      p_start_y, l_image->y0);
-        p_j2k->m_specific_param.m_decoder.m_start_tile_y = 0;
-        p_image->y0 = l_image->y0;
-    } else {
-        p_j2k->m_specific_param.m_decoder.m_start_tile_y = ((OPJ_UINT32)p_start_y -
-                l_cp->ty0) / l_cp->tdy;
-        p_image->y0 = (OPJ_UINT32)p_start_y;
-    }
-
-    /* Right */
-    if (p_end_x <= 0) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Right position of the decoded area (region_x1=%d) should be > 0.\n",
-                      p_end_x);
-        return OPJ_FALSE;
-    } else if ((OPJ_UINT32)p_end_x < l_image->x0) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Right position of the decoded area (region_x1=%d) is outside the image area (XOsiz=%d).\n",
-                      p_end_x, l_image->x0);
-        return OPJ_FALSE;
-    } else if ((OPJ_UINT32)p_end_x > l_image->x1) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "Right position of the decoded area (region_x1=%d) is outside the image area (Xsiz=%d).\n",
-                      p_end_x, l_image->x1);
-        p_j2k->m_specific_param.m_decoder.m_end_tile_x = l_cp->tw;
-        p_image->x1 = l_image->x1;
-    } else {
-        p_j2k->m_specific_param.m_decoder.m_end_tile_x = (OPJ_UINT32)opj_int_ceildiv(
-                    p_end_x - (OPJ_INT32)l_cp->tx0, (OPJ_INT32)l_cp->tdx);
-        p_image->x1 = (OPJ_UINT32)p_end_x;
-    }
-
-    /* Bottom */
-    if (p_end_y <= 0) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Bottom position of the decoded area (region_y1=%d) should be > 0.\n",
-                      p_end_y);
-        return OPJ_FALSE;
-    } else if ((OPJ_UINT32)p_end_y < l_image->y0) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Bottom position of the decoded area (region_y1=%d) is outside the image area (YOsiz=%d).\n",
-                      p_end_y, l_image->y0);
-        return OPJ_FALSE;
-    }
-    if ((OPJ_UINT32)p_end_y > l_image->y1) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "Bottom position of the decoded area (region_y1=%d) is outside the image area (Ysiz=%d).\n",
-                      p_end_y, l_image->y1);
-        p_j2k->m_specific_param.m_decoder.m_end_tile_y = l_cp->th;
-        p_image->y1 = l_image->y1;
-    } else {
-        p_j2k->m_specific_param.m_decoder.m_end_tile_y = (OPJ_UINT32)opj_int_ceildiv(
-                    p_end_y - (OPJ_INT32)l_cp->ty0, (OPJ_INT32)l_cp->tdy);
-        p_image->y1 = (OPJ_UINT32)p_end_y;
-    }
-    /* ----- */
-
-    p_j2k->m_specific_param.m_decoder.m_discard_tiles = 1;
-
-    ret = opj_j2k_update_image_dimensions(p_image, p_manager);
-
-    if (ret) {
-        opj_event_msg(p_manager, EVT_INFO, "Setting decoding area to %d,%d,%d,%d\n",
-                      p_image->x0, p_image->y0, p_image->x1, p_image->y1);
-    }
-
-    return ret;
-}
-
-opj_j2k_t* opj_j2k_create_decompress(void)
-{
-    opj_j2k_t *l_j2k = (opj_j2k_t*) opj_calloc(1, sizeof(opj_j2k_t));
-    if (!l_j2k) {
-        return 00;
-    }
-
-    l_j2k->m_is_decoder = 1;
-    l_j2k->m_cp.m_is_decoder = 1;
-    /* in the absence of JP2 boxes, consider different bit depth / sign */
-    /* per component is allowed */
-    l_j2k->m_cp.allow_different_bit_depth_sign = 1;
-
-#ifdef OPJ_DISABLE_TPSOT_FIX
-    l_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction_checked = 1;
-#endif
-
-    l_j2k->m_specific_param.m_decoder.m_default_tcp = (opj_tcp_t*) opj_calloc(1,
-            sizeof(opj_tcp_t));
-    if (!l_j2k->m_specific_param.m_decoder.m_default_tcp) {
-        opj_j2k_destroy(l_j2k);
-        return 00;
-    }
-
-    l_j2k->m_specific_param.m_decoder.m_header_data = (OPJ_BYTE *) opj_calloc(1,
-            OPJ_J2K_DEFAULT_HEADER_SIZE);
-    if (! l_j2k->m_specific_param.m_decoder.m_header_data) {
-        opj_j2k_destroy(l_j2k);
-        return 00;
-    }
-
-    l_j2k->m_specific_param.m_decoder.m_header_data_size =
-        OPJ_J2K_DEFAULT_HEADER_SIZE;
-
-    l_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec = -1 ;
-
-    l_j2k->m_specific_param.m_decoder.m_last_sot_read_pos = 0 ;
-
-    /* codestream index creation */
-    l_j2k->cstr_index = opj_j2k_create_cstr_index();
-    if (!l_j2k->cstr_index) {
-        opj_j2k_destroy(l_j2k);
-        return 00;
-    }
-
-    /* validation list creation */
-    l_j2k->m_validation_list = opj_procedure_list_create();
-    if (! l_j2k->m_validation_list) {
-        opj_j2k_destroy(l_j2k);
-        return 00;
-    }
-
-    /* execution list creation */
-    l_j2k->m_procedure_list = opj_procedure_list_create();
-    if (! l_j2k->m_procedure_list) {
-        opj_j2k_destroy(l_j2k);
-        return 00;
-    }
-
-    l_j2k->m_tp = opj_thread_pool_create(opj_j2k_get_default_thread_count());
-    if (!l_j2k->m_tp) {
-        l_j2k->m_tp = opj_thread_pool_create(0);
-    }
-    if (!l_j2k->m_tp) {
-        opj_j2k_destroy(l_j2k);
-        return NULL;
-    }
-
-    return l_j2k;
-}
-
-static opj_codestream_index_t* opj_j2k_create_cstr_index(void)
-{
-    opj_codestream_index_t* cstr_index = (opj_codestream_index_t*)
-                                         opj_calloc(1, sizeof(opj_codestream_index_t));
-    if (!cstr_index) {
-        return NULL;
-    }
-
-    cstr_index->maxmarknum = 100;
-    cstr_index->marknum = 0;
-    cstr_index->marker = (opj_marker_info_t*)
-                         opj_calloc(cstr_index->maxmarknum, sizeof(opj_marker_info_t));
-    if (!cstr_index-> marker) {
-        opj_free(cstr_index);
-        return NULL;
-    }
-
-    cstr_index->tile_index = NULL;
-
-    return cstr_index;
-}
-
-static OPJ_UINT32 opj_j2k_get_SPCod_SPCoc_size(opj_j2k_t *p_j2k,
-        OPJ_UINT32 p_tile_no,
-        OPJ_UINT32 p_comp_no)
-{
-    opj_cp_t *l_cp = 00;
-    opj_tcp_t *l_tcp = 00;
-    opj_tccp_t *l_tccp = 00;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-
-    l_cp = &(p_j2k->m_cp);
-    l_tcp = &l_cp->tcps[p_tile_no];
-    l_tccp = &l_tcp->tccps[p_comp_no];
-
-    /* preconditions again */
-    assert(p_tile_no < (l_cp->tw * l_cp->th));
-    assert(p_comp_no < p_j2k->m_private_image->numcomps);
-
-    if (l_tccp->csty & J2K_CCP_CSTY_PRT) {
-        return 5 + l_tccp->numresolutions;
-    } else {
-        return 5;
-    }
-}
-
-static OPJ_BOOL opj_j2k_compare_SPCod_SPCoc(opj_j2k_t *p_j2k,
-        OPJ_UINT32 p_tile_no, OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no)
-{
-    OPJ_UINT32 i;
-    opj_cp_t *l_cp = NULL;
-    opj_tcp_t *l_tcp = NULL;
-    opj_tccp_t *l_tccp0 = NULL;
-    opj_tccp_t *l_tccp1 = NULL;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-
-    l_cp = &(p_j2k->m_cp);
-    l_tcp = &l_cp->tcps[p_tile_no];
-    l_tccp0 = &l_tcp->tccps[p_first_comp_no];
-    l_tccp1 = &l_tcp->tccps[p_second_comp_no];
-
-    if (l_tccp0->numresolutions != l_tccp1->numresolutions) {
-        return OPJ_FALSE;
-    }
-    if (l_tccp0->cblkw != l_tccp1->cblkw) {
-        return OPJ_FALSE;
-    }
-    if (l_tccp0->cblkh != l_tccp1->cblkh) {
-        return OPJ_FALSE;
-    }
-    if (l_tccp0->cblksty != l_tccp1->cblksty) {
-        return OPJ_FALSE;
-    }
-    if (l_tccp0->qmfbid != l_tccp1->qmfbid) {
-        return OPJ_FALSE;
-    }
-    if ((l_tccp0->csty & J2K_CCP_CSTY_PRT) != (l_tccp1->csty & J2K_CCP_CSTY_PRT)) {
-        return OPJ_FALSE;
-    }
-
-    for (i = 0U; i < l_tccp0->numresolutions; ++i) {
-        if (l_tccp0->prcw[i] != l_tccp1->prcw[i]) {
-            return OPJ_FALSE;
-        }
-        if (l_tccp0->prch[i] != l_tccp1->prch[i]) {
-            return OPJ_FALSE;
-        }
-    }
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_SPCod_SPCoc(opj_j2k_t *p_j2k,
-        OPJ_UINT32 p_tile_no,
-        OPJ_UINT32 p_comp_no,
-        OPJ_BYTE * p_data,
-        OPJ_UINT32 * p_header_size,
-        struct opj_event_mgr * p_manager)
-{
-    OPJ_UINT32 i;
-    opj_cp_t *l_cp = 00;
-    opj_tcp_t *l_tcp = 00;
-    opj_tccp_t *l_tccp = 00;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_header_size != 00);
-    assert(p_manager != 00);
-    assert(p_data != 00);
-
-    l_cp = &(p_j2k->m_cp);
-    l_tcp = &l_cp->tcps[p_tile_no];
-    l_tccp = &l_tcp->tccps[p_comp_no];
-
-    /* preconditions again */
-    assert(p_tile_no < (l_cp->tw * l_cp->th));
-    assert(p_comp_no < (p_j2k->m_private_image->numcomps));
-
-    if (*p_header_size < 5) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error writing SPCod SPCoc element\n");
-        return OPJ_FALSE;
-    }
-
-    opj_write_bytes(p_data, l_tccp->numresolutions - 1, 1); /* SPcoc (D) */
-    ++p_data;
-
-    opj_write_bytes(p_data, l_tccp->cblkw - 2, 1);                  /* SPcoc (E) */
-    ++p_data;
-
-    opj_write_bytes(p_data, l_tccp->cblkh - 2, 1);                  /* SPcoc (F) */
-    ++p_data;
-
-    opj_write_bytes(p_data, l_tccp->cblksty,
-                    1);                            /* SPcoc (G) */
-    ++p_data;
-
-    opj_write_bytes(p_data, l_tccp->qmfbid,
-                    1);                             /* SPcoc (H) */
-    ++p_data;
-
-    *p_header_size = *p_header_size - 5;
-
-    if (l_tccp->csty & J2K_CCP_CSTY_PRT) {
-
-        if (*p_header_size < l_tccp->numresolutions) {
-            opj_event_msg(p_manager, EVT_ERROR, "Error writing SPCod SPCoc element\n");
-            return OPJ_FALSE;
-        }
-
-        for (i = 0; i < l_tccp->numresolutions; ++i) {
-            opj_write_bytes(p_data, l_tccp->prcw[i] + (l_tccp->prch[i] << 4),
-                            1);   /* SPcoc (I_i) */
-            ++p_data;
-        }
-
-        *p_header_size = *p_header_size - l_tccp->numresolutions;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_read_SPCod_SPCoc(opj_j2k_t *p_j2k,
-        OPJ_UINT32 compno,
-        OPJ_BYTE * p_header_data,
-        OPJ_UINT32 * p_header_size,
-        opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32 i, l_tmp;
-    opj_cp_t *l_cp = NULL;
-    opj_tcp_t *l_tcp = NULL;
-    opj_tccp_t *l_tccp = NULL;
-    OPJ_BYTE * l_current_ptr = NULL;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_header_data != 00);
-
-    l_cp = &(p_j2k->m_cp);
-    l_tcp = (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH) ?
-            &l_cp->tcps[p_j2k->m_current_tile_number] :
-            p_j2k->m_specific_param.m_decoder.m_default_tcp;
-
-    /* precondition again */
-    if (compno >= p_j2k->m_private_image->numcomps) {
-        return OPJ_FALSE;
-    }
-
-    assert(compno < p_j2k->m_private_image->numcomps);
-
-    l_tccp = &l_tcp->tccps[compno];
-    l_current_ptr = p_header_data;
-
-    /* make sure room is sufficient */
-    if (*p_header_size < 5) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading SPCod SPCoc element\n");
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(l_current_ptr, &l_tccp->numresolutions,
-                   1);              /* SPcox (D) */
-    ++l_tccp->numresolutions;                                                                               /* tccp->numresolutions = read() + 1 */
-    if (l_tccp->numresolutions > OPJ_J2K_MAXRLVLS) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Invalid value for numresolutions : %d, max value is set in openjpeg.h at %d\n",
-                      l_tccp->numresolutions, OPJ_J2K_MAXRLVLS);
-        return OPJ_FALSE;
-    }
-    ++l_current_ptr;
-
-    /* If user wants to remove more resolutions than the codestream contains, return error */
-    if (l_cp->m_specific_param.m_dec.m_reduce >= l_tccp->numresolutions) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Error decoding component %d.\nThe number of resolutions "
-                      "to remove (%d) is greater or equal than the number "
-                      "of resolutions of this component (%d)\nModify the cp_reduce parameter.\n\n",
-                      compno, l_cp->m_specific_param.m_dec.m_reduce, l_tccp->numresolutions);
-        p_j2k->m_specific_param.m_decoder.m_state |=
-            0x8000;/* FIXME J2K_DEC_STATE_ERR;*/
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(l_current_ptr, &l_tccp->cblkw, 1);               /* SPcoc (E) */
-    ++l_current_ptr;
-    l_tccp->cblkw += 2;
-
-    opj_read_bytes(l_current_ptr, &l_tccp->cblkh, 1);               /* SPcoc (F) */
-    ++l_current_ptr;
-    l_tccp->cblkh += 2;
-
-    if ((l_tccp->cblkw > 10) || (l_tccp->cblkh > 10) ||
-            ((l_tccp->cblkw + l_tccp->cblkh) > 12)) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Error reading SPCod SPCoc element, Invalid cblkw/cblkh combination\n");
-        return OPJ_FALSE;
-    }
-
-
-    opj_read_bytes(l_current_ptr, &l_tccp->cblksty, 1);             /* SPcoc (G) */
-    ++l_current_ptr;
-    if (l_tccp->cblksty & 0xC0U) { /* 2 msb are reserved, assume we can't read */
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Error reading SPCod SPCoc element, Invalid code-block style found\n");
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(l_current_ptr, &l_tccp->qmfbid, 1);              /* SPcoc (H) */
-    ++l_current_ptr;
-
-    *p_header_size = *p_header_size - 5;
-
-    /* use custom precinct size ? */
-    if (l_tccp->csty & J2K_CCP_CSTY_PRT) {
-        if (*p_header_size < l_tccp->numresolutions) {
-            opj_event_msg(p_manager, EVT_ERROR, "Error reading SPCod SPCoc element\n");
-            return OPJ_FALSE;
-        }
-
-        for (i = 0; i < l_tccp->numresolutions; ++i) {
-            opj_read_bytes(l_current_ptr, &l_tmp, 1);               /* SPcoc (I_i) */
-            ++l_current_ptr;
-            /* Precinct exponent 0 is only allowed for lowest resolution level (Table A.21) */
-            if ((i != 0) && (((l_tmp & 0xf) == 0) || ((l_tmp >> 4) == 0))) {
-                opj_event_msg(p_manager, EVT_ERROR, "Invalid precinct size\n");
-                return OPJ_FALSE;
-            }
-            l_tccp->prcw[i] = l_tmp & 0xf;
-            l_tccp->prch[i] = l_tmp >> 4;
-        }
-
-        *p_header_size = *p_header_size - l_tccp->numresolutions;
-    } else {
-        /* set default size for the precinct width and height */
-        for (i = 0; i < l_tccp->numresolutions; ++i) {
-            l_tccp->prcw[i] = 15;
-            l_tccp->prch[i] = 15;
-        }
-    }
-
-#ifdef WIP_REMOVE_MSD
-    /* INDEX >> */
-    if (p_j2k->cstr_info && compno == 0) {
-        OPJ_UINT32 l_data_size = l_tccp->numresolutions * sizeof(OPJ_UINT32);
-
-        p_j2k->cstr_info->tile[p_j2k->m_current_tile_number].tccp_info[compno].cblkh =
-            l_tccp->cblkh;
-        p_j2k->cstr_info->tile[p_j2k->m_current_tile_number].tccp_info[compno].cblkw =
-            l_tccp->cblkw;
-        p_j2k->cstr_info->tile[p_j2k->m_current_tile_number].tccp_info[compno].numresolutions
-            = l_tccp->numresolutions;
-        p_j2k->cstr_info->tile[p_j2k->m_current_tile_number].tccp_info[compno].cblksty =
-            l_tccp->cblksty;
-        p_j2k->cstr_info->tile[p_j2k->m_current_tile_number].tccp_info[compno].qmfbid =
-            l_tccp->qmfbid;
-
-        memcpy(p_j2k->cstr_info->tile[p_j2k->m_current_tile_number].pdx, l_tccp->prcw,
-               l_data_size);
-        memcpy(p_j2k->cstr_info->tile[p_j2k->m_current_tile_number].pdy, l_tccp->prch,
-               l_data_size);
-    }
-    /* << INDEX */
-#endif
-
-    return OPJ_TRUE;
-}
-
-static void opj_j2k_copy_tile_component_parameters(opj_j2k_t *p_j2k)
-{
-    /* loop */
-    OPJ_UINT32 i;
-    opj_cp_t *l_cp = NULL;
-    opj_tcp_t *l_tcp = NULL;
-    opj_tccp_t *l_ref_tccp = NULL, *l_copied_tccp = NULL;
-    OPJ_UINT32 l_prc_size;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-
-    l_cp = &(p_j2k->m_cp);
-    l_tcp = (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH)
-            ?
-            &l_cp->tcps[p_j2k->m_current_tile_number] :
-            p_j2k->m_specific_param.m_decoder.m_default_tcp;
-
-    l_ref_tccp = &l_tcp->tccps[0];
-    l_copied_tccp = l_ref_tccp + 1;
-    l_prc_size = l_ref_tccp->numresolutions * (OPJ_UINT32)sizeof(OPJ_UINT32);
-
-    for (i = 1; i < p_j2k->m_private_image->numcomps; ++i) {
-        l_copied_tccp->numresolutions = l_ref_tccp->numresolutions;
-        l_copied_tccp->cblkw = l_ref_tccp->cblkw;
-        l_copied_tccp->cblkh = l_ref_tccp->cblkh;
-        l_copied_tccp->cblksty = l_ref_tccp->cblksty;
-        l_copied_tccp->qmfbid = l_ref_tccp->qmfbid;
-        memcpy(l_copied_tccp->prcw, l_ref_tccp->prcw, l_prc_size);
-        memcpy(l_copied_tccp->prch, l_ref_tccp->prch, l_prc_size);
-        ++l_copied_tccp;
-    }
-}
-
-static OPJ_UINT32 opj_j2k_get_SQcd_SQcc_size(opj_j2k_t *p_j2k,
-        OPJ_UINT32 p_tile_no,
-        OPJ_UINT32 p_comp_no)
-{
-    OPJ_UINT32 l_num_bands;
-
-    opj_cp_t *l_cp = 00;
-    opj_tcp_t *l_tcp = 00;
-    opj_tccp_t *l_tccp = 00;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-
-    l_cp = &(p_j2k->m_cp);
-    l_tcp = &l_cp->tcps[p_tile_no];
-    l_tccp = &l_tcp->tccps[p_comp_no];
-
-    /* preconditions again */
-    assert(p_tile_no < l_cp->tw * l_cp->th);
-    assert(p_comp_no < p_j2k->m_private_image->numcomps);
-
-    l_num_bands = (l_tccp->qntsty == J2K_CCP_QNTSTY_SIQNT) ? 1 :
-                  (l_tccp->numresolutions * 3 - 2);
-
-    if (l_tccp->qntsty == J2K_CCP_QNTSTY_NOQNT)  {
-        return 1 + l_num_bands;
-    } else {
-        return 1 + 2 * l_num_bands;
-    }
-}
-
-static OPJ_BOOL opj_j2k_compare_SQcd_SQcc(opj_j2k_t *p_j2k,
-        OPJ_UINT32 p_tile_no, OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no)
-{
-    opj_cp_t *l_cp = NULL;
-    opj_tcp_t *l_tcp = NULL;
-    opj_tccp_t *l_tccp0 = NULL;
-    opj_tccp_t *l_tccp1 = NULL;
-    OPJ_UINT32 l_band_no, l_num_bands;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-
-    l_cp = &(p_j2k->m_cp);
-    l_tcp = &l_cp->tcps[p_tile_no];
-    l_tccp0 = &l_tcp->tccps[p_first_comp_no];
-    l_tccp1 = &l_tcp->tccps[p_second_comp_no];
-
-    if (l_tccp0->qntsty != l_tccp1->qntsty) {
-        return OPJ_FALSE;
-    }
-    if (l_tccp0->numgbits != l_tccp1->numgbits) {
-        return OPJ_FALSE;
-    }
-    if (l_tccp0->qntsty == J2K_CCP_QNTSTY_SIQNT) {
-        l_num_bands = 1U;
-    } else {
-        l_num_bands = l_tccp0->numresolutions * 3U - 2U;
-        if (l_num_bands != (l_tccp1->numresolutions * 3U - 2U)) {
-            return OPJ_FALSE;
-        }
-    }
-
-    for (l_band_no = 0; l_band_no < l_num_bands; ++l_band_no) {
-        if (l_tccp0->stepsizes[l_band_no].expn != l_tccp1->stepsizes[l_band_no].expn) {
-            return OPJ_FALSE;
-        }
-    }
-    if (l_tccp0->qntsty != J2K_CCP_QNTSTY_NOQNT) {
-        for (l_band_no = 0; l_band_no < l_num_bands; ++l_band_no) {
-            if (l_tccp0->stepsizes[l_band_no].mant != l_tccp1->stepsizes[l_band_no].mant) {
-                return OPJ_FALSE;
-            }
-        }
-    }
-    return OPJ_TRUE;
-}
-
-
-static OPJ_BOOL opj_j2k_write_SQcd_SQcc(opj_j2k_t *p_j2k,
-                                        OPJ_UINT32 p_tile_no,
-                                        OPJ_UINT32 p_comp_no,
-                                        OPJ_BYTE * p_data,
-                                        OPJ_UINT32 * p_header_size,
-                                        struct opj_event_mgr * p_manager)
-{
-    OPJ_UINT32 l_header_size;
-    OPJ_UINT32 l_band_no, l_num_bands;
-    OPJ_UINT32 l_expn, l_mant;
-
-    opj_cp_t *l_cp = 00;
-    opj_tcp_t *l_tcp = 00;
-    opj_tccp_t *l_tccp = 00;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_header_size != 00);
-    assert(p_manager != 00);
-    assert(p_data != 00);
-
-    l_cp = &(p_j2k->m_cp);
-    l_tcp = &l_cp->tcps[p_tile_no];
-    l_tccp = &l_tcp->tccps[p_comp_no];
-
-    /* preconditions again */
-    assert(p_tile_no < l_cp->tw * l_cp->th);
-    assert(p_comp_no < p_j2k->m_private_image->numcomps);
-
-    l_num_bands = (l_tccp->qntsty == J2K_CCP_QNTSTY_SIQNT) ? 1 :
-                  (l_tccp->numresolutions * 3 - 2);
-
-    if (l_tccp->qntsty == J2K_CCP_QNTSTY_NOQNT)  {
-        l_header_size = 1 + l_num_bands;
-
-        if (*p_header_size < l_header_size) {
-            opj_event_msg(p_manager, EVT_ERROR, "Error writing SQcd SQcc element\n");
-            return OPJ_FALSE;
-        }
-
-        opj_write_bytes(p_data, l_tccp->qntsty + (l_tccp->numgbits << 5),
-                        1);   /* Sqcx */
-        ++p_data;
-
-        for (l_band_no = 0; l_band_no < l_num_bands; ++l_band_no) {
-            l_expn = (OPJ_UINT32)l_tccp->stepsizes[l_band_no].expn;
-            opj_write_bytes(p_data, l_expn << 3, 1);        /* SPqcx_i */
-            ++p_data;
-        }
-    } else {
-        l_header_size = 1 + 2 * l_num_bands;
-
-        if (*p_header_size < l_header_size) {
-            opj_event_msg(p_manager, EVT_ERROR, "Error writing SQcd SQcc element\n");
-            return OPJ_FALSE;
-        }
-
-        opj_write_bytes(p_data, l_tccp->qntsty + (l_tccp->numgbits << 5),
-                        1);   /* Sqcx */
-        ++p_data;
-
-        for (l_band_no = 0; l_band_no < l_num_bands; ++l_band_no) {
-            l_expn = (OPJ_UINT32)l_tccp->stepsizes[l_band_no].expn;
-            l_mant = (OPJ_UINT32)l_tccp->stepsizes[l_band_no].mant;
-
-            opj_write_bytes(p_data, (l_expn << 11) + l_mant, 2);    /* SPqcx_i */
-            p_data += 2;
-        }
-    }
-
-    *p_header_size = *p_header_size - l_header_size;
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_read_SQcd_SQcc(opj_j2k_t *p_j2k,
-                                       OPJ_UINT32 p_comp_no,
-                                       OPJ_BYTE* p_header_data,
-                                       OPJ_UINT32 * p_header_size,
-                                       opj_event_mgr_t * p_manager
-                                      )
-{
-    /* loop*/
-    OPJ_UINT32 l_band_no;
-    opj_cp_t *l_cp = 00;
-    opj_tcp_t *l_tcp = 00;
-    opj_tccp_t *l_tccp = 00;
-    OPJ_BYTE * l_current_ptr = 00;
-    OPJ_UINT32 l_tmp, l_num_band;
-
-    /* preconditions*/
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_header_data != 00);
-
-    l_cp = &(p_j2k->m_cp);
-    /* come from tile part header or main header ?*/
-    l_tcp = (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH)
-            ?
-            &l_cp->tcps[p_j2k->m_current_tile_number] :
-            p_j2k->m_specific_param.m_decoder.m_default_tcp;
-
-    /* precondition again*/
-    if (p_comp_no >=  p_j2k->m_private_image->numcomps) {
-        return OPJ_FALSE;
-    }
-
-    l_tccp = &l_tcp->tccps[p_comp_no];
-    l_current_ptr = p_header_data;
-
-    if (*p_header_size < 1) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error reading SQcd or SQcc element\n");
-        return OPJ_FALSE;
-    }
-    *p_header_size -= 1;
-
-    opj_read_bytes(l_current_ptr, &l_tmp, 1);                       /* Sqcx */
-    ++l_current_ptr;
-
-    l_tccp->qntsty = l_tmp & 0x1f;
-    l_tccp->numgbits = l_tmp >> 5;
-    if (l_tccp->qntsty == J2K_CCP_QNTSTY_SIQNT) {
-        l_num_band = 1;
-    } else {
-        l_num_band = (l_tccp->qntsty == J2K_CCP_QNTSTY_NOQNT) ?
-                     (*p_header_size) :
-                     (*p_header_size) / 2;
-
-        if (l_num_band > OPJ_J2K_MAXBANDS) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "While reading CCP_QNTSTY element inside QCD or QCC marker segment, "
-                          "number of subbands (%d) is greater to OPJ_J2K_MAXBANDS (%d). So we limit the number of elements stored to "
-                          "OPJ_J2K_MAXBANDS (%d) and skip the rest. \n", l_num_band, OPJ_J2K_MAXBANDS,
-                          OPJ_J2K_MAXBANDS);
-            /*return OPJ_FALSE;*/
-        }
-    }
-
-#ifdef USE_JPWL
-    if (l_cp->correct) {
-
-        /* if JPWL is on, we check whether there are too many subbands */
-        if (/*(l_num_band < 0) ||*/ (l_num_band >= OPJ_J2K_MAXBANDS)) {
-            opj_event_msg(p_manager, JPWL_ASSUME ? EVT_WARNING : EVT_ERROR,
-                          "JPWL: bad number of subbands in Sqcx (%d)\n",
-                          l_num_band);
-            if (!JPWL_ASSUME) {
-                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
-                return OPJ_FALSE;
-            }
-            /* we try to correct */
-            l_num_band = 1;
-            opj_event_msg(p_manager, EVT_WARNING, "- trying to adjust them\n"
-                          "- setting number of bands to %d => HYPOTHESIS!!!\n",
-                          l_num_band);
-        };
-
-    };
-#endif /* USE_JPWL */
-
-    if (l_tccp->qntsty == J2K_CCP_QNTSTY_NOQNT) {
-        for (l_band_no = 0; l_band_no < l_num_band; l_band_no++) {
-            opj_read_bytes(l_current_ptr, &l_tmp, 1);                       /* SPqcx_i */
-            ++l_current_ptr;
-            if (l_band_no < OPJ_J2K_MAXBANDS) {
-                l_tccp->stepsizes[l_band_no].expn = (OPJ_INT32)(l_tmp >> 3);
-                l_tccp->stepsizes[l_band_no].mant = 0;
-            }
-        }
-        *p_header_size = *p_header_size - l_num_band;
-    } else {
-        for (l_band_no = 0; l_band_no < l_num_band; l_band_no++) {
-            opj_read_bytes(l_current_ptr, &l_tmp, 2);                       /* SPqcx_i */
-            l_current_ptr += 2;
-            if (l_band_no < OPJ_J2K_MAXBANDS) {
-                l_tccp->stepsizes[l_band_no].expn = (OPJ_INT32)(l_tmp >> 11);
-                l_tccp->stepsizes[l_band_no].mant = l_tmp & 0x7ff;
-            }
-        }
-        *p_header_size = *p_header_size - 2 * l_num_band;
-    }
-
-    /* Add Antonin : if scalar_derived -> compute other stepsizes */
-    if (l_tccp->qntsty == J2K_CCP_QNTSTY_SIQNT) {
-        for (l_band_no = 1; l_band_no < OPJ_J2K_MAXBANDS; l_band_no++) {
-            l_tccp->stepsizes[l_band_no].expn =
-                ((OPJ_INT32)(l_tccp->stepsizes[0].expn) - (OPJ_INT32)((l_band_no - 1) / 3) > 0)
-                ?
-                (OPJ_INT32)(l_tccp->stepsizes[0].expn) - (OPJ_INT32)((l_band_no - 1) / 3) : 0;
-            l_tccp->stepsizes[l_band_no].mant = l_tccp->stepsizes[0].mant;
-        }
-    }
-
-    return OPJ_TRUE;
-}
-
-static void opj_j2k_copy_tile_quantization_parameters(opj_j2k_t *p_j2k)
-{
-    OPJ_UINT32 i;
-    opj_cp_t *l_cp = NULL;
-    opj_tcp_t *l_tcp = NULL;
-    opj_tccp_t *l_ref_tccp = NULL;
-    opj_tccp_t *l_copied_tccp = NULL;
-    OPJ_UINT32 l_size;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-
-    l_cp = &(p_j2k->m_cp);
-    l_tcp = p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH ?
-            &l_cp->tcps[p_j2k->m_current_tile_number] :
-            p_j2k->m_specific_param.m_decoder.m_default_tcp;
-
-    l_ref_tccp = &l_tcp->tccps[0];
-    l_copied_tccp = l_ref_tccp + 1;
-    l_size = OPJ_J2K_MAXBANDS * sizeof(opj_stepsize_t);
-
-    for (i = 1; i < p_j2k->m_private_image->numcomps; ++i) {
-        l_copied_tccp->qntsty = l_ref_tccp->qntsty;
-        l_copied_tccp->numgbits = l_ref_tccp->numgbits;
-        memcpy(l_copied_tccp->stepsizes, l_ref_tccp->stepsizes, l_size);
-        ++l_copied_tccp;
-    }
-}
-
-static void opj_j2k_dump_tile_info(opj_tcp_t * l_default_tile,
-                                   OPJ_INT32 numcomps, FILE* out_stream)
-{
-    if (l_default_tile) {
-        OPJ_INT32 compno;
-
-        fprintf(out_stream, "\t default tile {\n");
-        fprintf(out_stream, "\t\t csty=%#x\n", l_default_tile->csty);
-        fprintf(out_stream, "\t\t prg=%#x\n", l_default_tile->prg);
-        fprintf(out_stream, "\t\t numlayers=%d\n", l_default_tile->numlayers);
-        fprintf(out_stream, "\t\t mct=%x\n", l_default_tile->mct);
-
-        for (compno = 0; compno < numcomps; compno++) {
-            opj_tccp_t *l_tccp = &(l_default_tile->tccps[compno]);
-            OPJ_UINT32 resno;
-            OPJ_INT32 bandno, numbands;
-
-            /* coding style*/
-            fprintf(out_stream, "\t\t comp %d {\n", compno);
-            fprintf(out_stream, "\t\t\t csty=%#x\n", l_tccp->csty);
-            fprintf(out_stream, "\t\t\t numresolutions=%d\n", l_tccp->numresolutions);
-            fprintf(out_stream, "\t\t\t cblkw=2^%d\n", l_tccp->cblkw);
-            fprintf(out_stream, "\t\t\t cblkh=2^%d\n", l_tccp->cblkh);
-            fprintf(out_stream, "\t\t\t cblksty=%#x\n", l_tccp->cblksty);
-            fprintf(out_stream, "\t\t\t qmfbid=%d\n", l_tccp->qmfbid);
-
-            fprintf(out_stream, "\t\t\t preccintsize (w,h)=");
-            for (resno = 0; resno < l_tccp->numresolutions; resno++) {
-                fprintf(out_stream, "(%d,%d) ", l_tccp->prcw[resno], l_tccp->prch[resno]);
-            }
-            fprintf(out_stream, "\n");
-
-            /* quantization style*/
-            fprintf(out_stream, "\t\t\t qntsty=%d\n", l_tccp->qntsty);
-            fprintf(out_stream, "\t\t\t numgbits=%d\n", l_tccp->numgbits);
-            fprintf(out_stream, "\t\t\t stepsizes (m,e)=");
-            numbands = (l_tccp->qntsty == J2K_CCP_QNTSTY_SIQNT) ? 1 :
-                       (OPJ_INT32)l_tccp->numresolutions * 3 - 2;
-            for (bandno = 0; bandno < numbands; bandno++) {
-                fprintf(out_stream, "(%d,%d) ", l_tccp->stepsizes[bandno].mant,
-                        l_tccp->stepsizes[bandno].expn);
-            }
-            fprintf(out_stream, "\n");
-
-            /* RGN value*/
-            fprintf(out_stream, "\t\t\t roishift=%d\n", l_tccp->roishift);
-
-            fprintf(out_stream, "\t\t }\n");
-        } /*end of component of default tile*/
-        fprintf(out_stream, "\t }\n"); /*end of default tile*/
-    }
-}
-
-void j2k_dump(opj_j2k_t* p_j2k, OPJ_INT32 flag, FILE* out_stream)
-{
-    /* Check if the flag is compatible with j2k file*/
-    if ((flag & OPJ_JP2_INFO) || (flag & OPJ_JP2_IND)) {
-        fprintf(out_stream, "Wrong flag\n");
-        return;
-    }
-
-    /* Dump the image_header */
-    if (flag & OPJ_IMG_INFO) {
-        if (p_j2k->m_private_image) {
-            j2k_dump_image_header(p_j2k->m_private_image, 0, out_stream);
-        }
-    }
-
-    /* Dump the codestream info from main header */
-    if (flag & OPJ_J2K_MH_INFO) {
-        if (p_j2k->m_private_image) {
-            opj_j2k_dump_MH_info(p_j2k, out_stream);
-        }
-    }
-    /* Dump all tile/codestream info */
-    if (flag & OPJ_J2K_TCH_INFO) {
-        OPJ_UINT32 l_nb_tiles = p_j2k->m_cp.th * p_j2k->m_cp.tw;
-        OPJ_UINT32 i;
-        opj_tcp_t * l_tcp = p_j2k->m_cp.tcps;
-        if (p_j2k->m_private_image) {
-            for (i = 0; i < l_nb_tiles; ++i) {
-                opj_j2k_dump_tile_info(l_tcp, (OPJ_INT32)p_j2k->m_private_image->numcomps,
-                                       out_stream);
-                ++l_tcp;
-            }
-        }
-    }
-
-    /* Dump the codestream info of the current tile */
-    if (flag & OPJ_J2K_TH_INFO) {
-
-    }
-
-    /* Dump the codestream index from main header */
-    if (flag & OPJ_J2K_MH_IND) {
-        opj_j2k_dump_MH_index(p_j2k, out_stream);
-    }
-
-    /* Dump the codestream index of the current tile */
-    if (flag & OPJ_J2K_TH_IND) {
-
-    }
-
-}
-
-static void opj_j2k_dump_MH_index(opj_j2k_t* p_j2k, FILE* out_stream)
-{
-    opj_codestream_index_t* cstr_index = p_j2k->cstr_index;
-    OPJ_UINT32 it_marker, it_tile, it_tile_part;
-
-    fprintf(out_stream, "Codestream index from main header: {\n");
-
-    fprintf(out_stream, "\t Main header start position=%" PRIi64 "\n"
-            "\t Main header end position=%" PRIi64 "\n",
-            cstr_index->main_head_start, cstr_index->main_head_end);
-
-    fprintf(out_stream, "\t Marker list: {\n");
-
-    if (cstr_index->marker) {
-        for (it_marker = 0; it_marker < cstr_index->marknum ; it_marker++) {
-            fprintf(out_stream, "\t\t type=%#x, pos=%" PRIi64 ", len=%d\n",
-                    cstr_index->marker[it_marker].type,
-                    cstr_index->marker[it_marker].pos,
-                    cstr_index->marker[it_marker].len);
-        }
-    }
-
-    fprintf(out_stream, "\t }\n");
-
-    if (cstr_index->tile_index) {
-
-        /* Simple test to avoid to write empty information*/
-        OPJ_UINT32 l_acc_nb_of_tile_part = 0;
-        for (it_tile = 0; it_tile < cstr_index->nb_of_tiles ; it_tile++) {
-            l_acc_nb_of_tile_part += cstr_index->tile_index[it_tile].nb_tps;
-        }
-
-        if (l_acc_nb_of_tile_part) {
-            fprintf(out_stream, "\t Tile index: {\n");
-
-            for (it_tile = 0; it_tile < cstr_index->nb_of_tiles ; it_tile++) {
-                OPJ_UINT32 nb_of_tile_part = cstr_index->tile_index[it_tile].nb_tps;
-
-                fprintf(out_stream, "\t\t nb of tile-part in tile [%d]=%d\n", it_tile,
-                        nb_of_tile_part);
-
-                if (cstr_index->tile_index[it_tile].tp_index) {
-                    for (it_tile_part = 0; it_tile_part < nb_of_tile_part; it_tile_part++) {
-                        fprintf(out_stream, "\t\t\t tile-part[%d]: star_pos=%" PRIi64 ", end_header=%"
-                                PRIi64 ", end_pos=%" PRIi64 ".\n",
-                                it_tile_part,
-                                cstr_index->tile_index[it_tile].tp_index[it_tile_part].start_pos,
-                                cstr_index->tile_index[it_tile].tp_index[it_tile_part].end_header,
-                                cstr_index->tile_index[it_tile].tp_index[it_tile_part].end_pos);
-                    }
-                }
-
-                if (cstr_index->tile_index[it_tile].marker) {
-                    for (it_marker = 0; it_marker < cstr_index->tile_index[it_tile].marknum ;
-                            it_marker++) {
-                        fprintf(out_stream, "\t\t type=%#x, pos=%" PRIi64 ", len=%d\n",
-                                cstr_index->tile_index[it_tile].marker[it_marker].type,
-                                cstr_index->tile_index[it_tile].marker[it_marker].pos,
-                                cstr_index->tile_index[it_tile].marker[it_marker].len);
-                    }
-                }
-            }
-            fprintf(out_stream, "\t }\n");
-        }
-    }
-
-    fprintf(out_stream, "}\n");
-
-}
-
-
-static void opj_j2k_dump_MH_info(opj_j2k_t* p_j2k, FILE* out_stream)
-{
-
-    fprintf(out_stream, "Codestream info from main header: {\n");
-
-    fprintf(out_stream, "\t tx0=%d, ty0=%d\n", p_j2k->m_cp.tx0, p_j2k->m_cp.ty0);
-    fprintf(out_stream, "\t tdx=%d, tdy=%d\n", p_j2k->m_cp.tdx, p_j2k->m_cp.tdy);
-    fprintf(out_stream, "\t tw=%d, th=%d\n", p_j2k->m_cp.tw, p_j2k->m_cp.th);
-    opj_j2k_dump_tile_info(p_j2k->m_specific_param.m_decoder.m_default_tcp,
-                           (OPJ_INT32)p_j2k->m_private_image->numcomps, out_stream);
-    fprintf(out_stream, "}\n");
-}
-
-void j2k_dump_image_header(opj_image_t* img_header, OPJ_BOOL dev_dump_flag,
-                           FILE* out_stream)
-{
-    char tab[2];
-
-    if (dev_dump_flag) {
-        fprintf(stdout, "[DEV] Dump an image_header struct {\n");
-        tab[0] = '\0';
-    } else {
-        fprintf(out_stream, "Image info {\n");
-        tab[0] = '\t';
-        tab[1] = '\0';
-    }
-
-    fprintf(out_stream, "%s x0=%d, y0=%d\n", tab, img_header->x0, img_header->y0);
-    fprintf(out_stream,     "%s x1=%d, y1=%d\n", tab, img_header->x1,
-            img_header->y1);
-    fprintf(out_stream, "%s numcomps=%d\n", tab, img_header->numcomps);
-
-    if (img_header->comps) {
-        OPJ_UINT32 compno;
-        for (compno = 0; compno < img_header->numcomps; compno++) {
-            fprintf(out_stream, "%s\t component %d {\n", tab, compno);
-            j2k_dump_image_comp_header(&(img_header->comps[compno]), dev_dump_flag,
-                                       out_stream);
-            fprintf(out_stream, "%s}\n", tab);
-        }
-    }
-
-    fprintf(out_stream, "}\n");
-}
-
-void j2k_dump_image_comp_header(opj_image_comp_t* comp_header,
-                                OPJ_BOOL dev_dump_flag, FILE* out_stream)
-{
-    char tab[3];
-
-    if (dev_dump_flag) {
-        fprintf(stdout, "[DEV] Dump an image_comp_header struct {\n");
-        tab[0] = '\0';
-    }       else {
-        tab[0] = '\t';
-        tab[1] = '\t';
-        tab[2] = '\0';
-    }
-
-    fprintf(out_stream, "%s dx=%d, dy=%d\n", tab, comp_header->dx, comp_header->dy);
-    fprintf(out_stream, "%s prec=%d\n", tab, comp_header->prec);
-    fprintf(out_stream, "%s sgnd=%d\n", tab, comp_header->sgnd);
-
-    if (dev_dump_flag) {
-        fprintf(out_stream, "}\n");
-    }
-}
-
-opj_codestream_info_v2_t* j2k_get_cstr_info(opj_j2k_t* p_j2k)
-{
-    OPJ_UINT32 compno;
-    OPJ_UINT32 numcomps = p_j2k->m_private_image->numcomps;
-    opj_tcp_t *l_default_tile;
-    opj_codestream_info_v2_t* cstr_info = (opj_codestream_info_v2_t*) opj_calloc(1,
-                                          sizeof(opj_codestream_info_v2_t));
-    if (!cstr_info) {
-        return NULL;
-    }
-
-    cstr_info->nbcomps = p_j2k->m_private_image->numcomps;
-
-    cstr_info->tx0 = p_j2k->m_cp.tx0;
-    cstr_info->ty0 = p_j2k->m_cp.ty0;
-    cstr_info->tdx = p_j2k->m_cp.tdx;
-    cstr_info->tdy = p_j2k->m_cp.tdy;
-    cstr_info->tw = p_j2k->m_cp.tw;
-    cstr_info->th = p_j2k->m_cp.th;
-
-    cstr_info->tile_info = NULL; /* Not fill from the main header*/
-
-    l_default_tile = p_j2k->m_specific_param.m_decoder.m_default_tcp;
-
-    cstr_info->m_default_tile_info.csty = l_default_tile->csty;
-    cstr_info->m_default_tile_info.prg = l_default_tile->prg;
-    cstr_info->m_default_tile_info.numlayers = l_default_tile->numlayers;
-    cstr_info->m_default_tile_info.mct = l_default_tile->mct;
-
-    cstr_info->m_default_tile_info.tccp_info = (opj_tccp_info_t*) opj_calloc(
-                cstr_info->nbcomps, sizeof(opj_tccp_info_t));
-    if (!cstr_info->m_default_tile_info.tccp_info) {
-        opj_destroy_cstr_info(&cstr_info);
-        return NULL;
-    }
-
-    for (compno = 0; compno < numcomps; compno++) {
-        opj_tccp_t *l_tccp = &(l_default_tile->tccps[compno]);
-        opj_tccp_info_t *l_tccp_info = &
-                                       (cstr_info->m_default_tile_info.tccp_info[compno]);
-        OPJ_INT32 bandno, numbands;
-
-        /* coding style*/
-        l_tccp_info->csty = l_tccp->csty;
-        l_tccp_info->numresolutions = l_tccp->numresolutions;
-        l_tccp_info->cblkw = l_tccp->cblkw;
-        l_tccp_info->cblkh = l_tccp->cblkh;
-        l_tccp_info->cblksty = l_tccp->cblksty;
-        l_tccp_info->qmfbid = l_tccp->qmfbid;
-        if (l_tccp->numresolutions < OPJ_J2K_MAXRLVLS) {
-            memcpy(l_tccp_info->prch, l_tccp->prch, l_tccp->numresolutions);
-            memcpy(l_tccp_info->prcw, l_tccp->prcw, l_tccp->numresolutions);
-        }
-
-        /* quantization style*/
-        l_tccp_info->qntsty = l_tccp->qntsty;
-        l_tccp_info->numgbits = l_tccp->numgbits;
-
-        numbands = (l_tccp->qntsty == J2K_CCP_QNTSTY_SIQNT) ? 1 :
-                   (OPJ_INT32)l_tccp->numresolutions * 3 - 2;
-        if (numbands < OPJ_J2K_MAXBANDS) {
-            for (bandno = 0; bandno < numbands; bandno++) {
-                l_tccp_info->stepsizes_mant[bandno] = (OPJ_UINT32)
-                                                      l_tccp->stepsizes[bandno].mant;
-                l_tccp_info->stepsizes_expn[bandno] = (OPJ_UINT32)
-                                                      l_tccp->stepsizes[bandno].expn;
-            }
-        }
-
-        /* RGN value*/
-        l_tccp_info->roishift = l_tccp->roishift;
-    }
-
-    return cstr_info;
-}
-
-opj_codestream_index_t* j2k_get_cstr_index(opj_j2k_t* p_j2k)
-{
-    opj_codestream_index_t* l_cstr_index = (opj_codestream_index_t*)
-                                           opj_calloc(1, sizeof(opj_codestream_index_t));
-    if (!l_cstr_index) {
-        return NULL;
-    }
-
-    l_cstr_index->main_head_start = p_j2k->cstr_index->main_head_start;
-    l_cstr_index->main_head_end = p_j2k->cstr_index->main_head_end;
-    l_cstr_index->codestream_size = p_j2k->cstr_index->codestream_size;
-
-    l_cstr_index->marknum = p_j2k->cstr_index->marknum;
-    l_cstr_index->marker = (opj_marker_info_t*)opj_malloc(l_cstr_index->marknum *
-                           sizeof(opj_marker_info_t));
-    if (!l_cstr_index->marker) {
-        opj_free(l_cstr_index);
-        return NULL;
-    }
-
-    if (p_j2k->cstr_index->marker) {
-        memcpy(l_cstr_index->marker, p_j2k->cstr_index->marker,
-               l_cstr_index->marknum * sizeof(opj_marker_info_t));
-    } else {
-        opj_free(l_cstr_index->marker);
-        l_cstr_index->marker = NULL;
-    }
-
-    l_cstr_index->nb_of_tiles = p_j2k->cstr_index->nb_of_tiles;
-    l_cstr_index->tile_index = (opj_tile_index_t*)opj_calloc(
-                                   l_cstr_index->nb_of_tiles, sizeof(opj_tile_index_t));
-    if (!l_cstr_index->tile_index) {
-        opj_free(l_cstr_index->marker);
-        opj_free(l_cstr_index);
-        return NULL;
-    }
-
-    if (!p_j2k->cstr_index->tile_index) {
-        opj_free(l_cstr_index->tile_index);
-        l_cstr_index->tile_index = NULL;
-    } else {
-        OPJ_UINT32 it_tile = 0;
-        for (it_tile = 0; it_tile < l_cstr_index->nb_of_tiles; it_tile++) {
-
-            /* Tile Marker*/
-            l_cstr_index->tile_index[it_tile].marknum =
-                p_j2k->cstr_index->tile_index[it_tile].marknum;
-
-            l_cstr_index->tile_index[it_tile].marker =
-                (opj_marker_info_t*)opj_malloc(l_cstr_index->tile_index[it_tile].marknum *
-                                               sizeof(opj_marker_info_t));
-
-            if (!l_cstr_index->tile_index[it_tile].marker) {
-                OPJ_UINT32 it_tile_free;
-
-                for (it_tile_free = 0; it_tile_free < it_tile; it_tile_free++) {
-                    opj_free(l_cstr_index->tile_index[it_tile_free].marker);
-                }
-
-                opj_free(l_cstr_index->tile_index);
-                opj_free(l_cstr_index->marker);
-                opj_free(l_cstr_index);
-                return NULL;
-            }
-
-            if (p_j2k->cstr_index->tile_index[it_tile].marker)
-                memcpy(l_cstr_index->tile_index[it_tile].marker,
-                       p_j2k->cstr_index->tile_index[it_tile].marker,
-                       l_cstr_index->tile_index[it_tile].marknum * sizeof(opj_marker_info_t));
-            else {
-                opj_free(l_cstr_index->tile_index[it_tile].marker);
-                l_cstr_index->tile_index[it_tile].marker = NULL;
-            }
-
-            /* Tile part index*/
-            l_cstr_index->tile_index[it_tile].nb_tps =
-                p_j2k->cstr_index->tile_index[it_tile].nb_tps;
-
-            l_cstr_index->tile_index[it_tile].tp_index =
-                (opj_tp_index_t*)opj_malloc(l_cstr_index->tile_index[it_tile].nb_tps * sizeof(
-                                                opj_tp_index_t));
-
-            if (!l_cstr_index->tile_index[it_tile].tp_index) {
-                OPJ_UINT32 it_tile_free;
-
-                for (it_tile_free = 0; it_tile_free < it_tile; it_tile_free++) {
-                    opj_free(l_cstr_index->tile_index[it_tile_free].marker);
-                    opj_free(l_cstr_index->tile_index[it_tile_free].tp_index);
-                }
-
-                opj_free(l_cstr_index->tile_index);
-                opj_free(l_cstr_index->marker);
-                opj_free(l_cstr_index);
-                return NULL;
-            }
-
-            if (p_j2k->cstr_index->tile_index[it_tile].tp_index) {
-                memcpy(l_cstr_index->tile_index[it_tile].tp_index,
-                       p_j2k->cstr_index->tile_index[it_tile].tp_index,
-                       l_cstr_index->tile_index[it_tile].nb_tps * sizeof(opj_tp_index_t));
-            } else {
-                opj_free(l_cstr_index->tile_index[it_tile].tp_index);
-                l_cstr_index->tile_index[it_tile].tp_index = NULL;
-            }
-
-            /* Packet index (NOT USED)*/
-            l_cstr_index->tile_index[it_tile].nb_packet = 0;
-            l_cstr_index->tile_index[it_tile].packet_index = NULL;
-
-        }
-    }
-
-    return l_cstr_index;
-}
-
-static OPJ_BOOL opj_j2k_allocate_tile_element_cstr_index(opj_j2k_t *p_j2k)
-{
-    OPJ_UINT32 it_tile = 0;
-
-    p_j2k->cstr_index->nb_of_tiles = p_j2k->m_cp.tw * p_j2k->m_cp.th;
-    p_j2k->cstr_index->tile_index = (opj_tile_index_t*)opj_calloc(
-                                        p_j2k->cstr_index->nb_of_tiles, sizeof(opj_tile_index_t));
-    if (!p_j2k->cstr_index->tile_index) {
-        return OPJ_FALSE;
-    }
-
-    for (it_tile = 0; it_tile < p_j2k->cstr_index->nb_of_tiles; it_tile++) {
-        p_j2k->cstr_index->tile_index[it_tile].maxmarknum = 100;
-        p_j2k->cstr_index->tile_index[it_tile].marknum = 0;
-        p_j2k->cstr_index->tile_index[it_tile].marker = (opj_marker_info_t*)
-                opj_calloc(p_j2k->cstr_index->tile_index[it_tile].maxmarknum,
-                           sizeof(opj_marker_info_t));
-        if (!p_j2k->cstr_index->tile_index[it_tile].marker) {
-            return OPJ_FALSE;
-        }
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_decode_tiles(opj_j2k_t *p_j2k,
-                                     opj_stream_private_t *p_stream,
-                                     opj_event_mgr_t * p_manager)
-{
-    OPJ_BOOL l_go_on = OPJ_TRUE;
-    OPJ_UINT32 l_current_tile_no;
-    OPJ_INT32 l_tile_x0, l_tile_y0, l_tile_x1, l_tile_y1;
-    OPJ_UINT32 l_nb_comps;
-    OPJ_UINT32 nr_tiles = 0;
-
-    /* Particular case for whole single tile decoding */
-    /* We can avoid allocating intermediate tile buffers */
-    if (p_j2k->m_cp.tw == 1 && p_j2k->m_cp.th == 1 &&
-            p_j2k->m_cp.tx0 == 0 && p_j2k->m_cp.ty0 == 0 &&
-            p_j2k->m_output_image->x0 == 0 &&
-            p_j2k->m_output_image->y0 == 0 &&
-            p_j2k->m_output_image->x1 == p_j2k->m_cp.tdx &&
-            p_j2k->m_output_image->y1 == p_j2k->m_cp.tdy) {
-        OPJ_UINT32 i;
-        if (! opj_j2k_read_tile_header(p_j2k,
-                                       &l_current_tile_no,
-                                       NULL,
-                                       &l_tile_x0, &l_tile_y0,
-                                       &l_tile_x1, &l_tile_y1,
-                                       &l_nb_comps,
-                                       &l_go_on,
-                                       p_stream,
-                                       p_manager)) {
-            return OPJ_FALSE;
-        }
-
-        if (! opj_j2k_decode_tile(p_j2k, l_current_tile_no, NULL, 0,
-                                  p_stream, p_manager)) {
-            opj_event_msg(p_manager, EVT_ERROR, "Failed to decode tile 1/1\n");
-            return OPJ_FALSE;
-        }
-
-        /* Transfer TCD data to output image data */
-        for (i = 0; i < p_j2k->m_output_image->numcomps; i++) {
-            opj_image_data_free(p_j2k->m_output_image->comps[i].data);
-            p_j2k->m_output_image->comps[i].data =
-                p_j2k->m_tcd->tcd_image->tiles->comps[i].data;
-            p_j2k->m_output_image->comps[i].resno_decoded =
-                p_j2k->m_tcd->image->comps[i].resno_decoded;
-            p_j2k->m_tcd->tcd_image->tiles->comps[i].data = NULL;
-        }
-
-        return OPJ_TRUE;
-    }
-
-    for (;;) {
-        if (p_j2k->m_cp.tw == 1 && p_j2k->m_cp.th == 1 &&
-                p_j2k->m_cp.tcps[0].m_data != NULL) {
-            l_current_tile_no = 0;
-            p_j2k->m_current_tile_number = 0;
-            p_j2k->m_specific_param.m_decoder.m_state |= J2K_STATE_DATA;
-        } else {
-            if (! opj_j2k_read_tile_header(p_j2k,
-                                           &l_current_tile_no,
-                                           NULL,
-                                           &l_tile_x0, &l_tile_y0,
-                                           &l_tile_x1, &l_tile_y1,
-                                           &l_nb_comps,
-                                           &l_go_on,
-                                           p_stream,
-                                           p_manager)) {
-                return OPJ_FALSE;
-            }
-
-            if (! l_go_on) {
-                break;
-            }
-        }
-
-        if (! opj_j2k_decode_tile(p_j2k, l_current_tile_no, NULL, 0,
-                                  p_stream, p_manager)) {
-            opj_event_msg(p_manager, EVT_ERROR, "Failed to decode tile %d/%d\n",
-                          l_current_tile_no + 1, p_j2k->m_cp.th * p_j2k->m_cp.tw);
-            return OPJ_FALSE;
-        }
-
-        opj_event_msg(p_manager, EVT_INFO, "Tile %d/%d has been decoded.\n",
-                      l_current_tile_no + 1, p_j2k->m_cp.th * p_j2k->m_cp.tw);
-
-        if (! opj_j2k_update_image_data(p_j2k->m_tcd,
-                                        p_j2k->m_output_image)) {
-            return OPJ_FALSE;
-        }
-
-        if (p_j2k->m_cp.tw == 1 && p_j2k->m_cp.th == 1 &&
-                !(p_j2k->m_output_image->x0 == p_j2k->m_private_image->x0 &&
-                  p_j2k->m_output_image->y0 == p_j2k->m_private_image->y0 &&
-                  p_j2k->m_output_image->x1 == p_j2k->m_private_image->x1 &&
-                  p_j2k->m_output_image->y1 == p_j2k->m_private_image->y1)) {
-            /* Keep current tcp data */
-        } else {
-            opj_j2k_tcp_data_destroy(&p_j2k->m_cp.tcps[l_current_tile_no]);
-        }
-
-        opj_event_msg(p_manager, EVT_INFO,
-                      "Image data has been updated with tile %d.\n\n", l_current_tile_no + 1);
-
-        if (opj_stream_get_number_byte_left(p_stream) == 0
-                && p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_NEOC) {
-            break;
-        }
-        if (++nr_tiles ==  p_j2k->m_cp.th * p_j2k->m_cp.tw) {
-            break;
-        }
-    }
-
-    return OPJ_TRUE;
-}
-
-/**
- * Sets up the procedures to do on decoding data. Developpers wanting to extend the library can add their own reading procedures.
- */
-static OPJ_BOOL opj_j2k_setup_decoding(opj_j2k_t *p_j2k,
-                                       opj_event_mgr_t * p_manager)
-{
-    /* preconditions*/
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_decode_tiles, p_manager)) {
-        return OPJ_FALSE;
-    }
-    /* DEVELOPER CORNER, add your custom procedures */
-
-    return OPJ_TRUE;
-}
-
-/*
- * Read and decode one tile.
- */
-static OPJ_BOOL opj_j2k_decode_one_tile(opj_j2k_t *p_j2k,
-                                        opj_stream_private_t *p_stream,
-                                        opj_event_mgr_t * p_manager)
-{
-    OPJ_BOOL l_go_on = OPJ_TRUE;
-    OPJ_UINT32 l_current_tile_no;
-    OPJ_UINT32 l_tile_no_to_dec;
-    OPJ_INT32 l_tile_x0, l_tile_y0, l_tile_x1, l_tile_y1;
-    OPJ_UINT32 l_nb_comps;
-    OPJ_UINT32 l_nb_tiles;
-    OPJ_UINT32 i;
-
-    /*Allocate and initialize some elements of codestrem index if not already done*/
-    if (!p_j2k->cstr_index->tile_index) {
-        if (!opj_j2k_allocate_tile_element_cstr_index(p_j2k)) {
-            return OPJ_FALSE;
-        }
-    }
-    /* Move into the codestream to the first SOT used to decode the desired tile */
-    l_tile_no_to_dec = (OPJ_UINT32)
-                       p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec;
-    if (p_j2k->cstr_index->tile_index)
-        if (p_j2k->cstr_index->tile_index->tp_index) {
-            if (! p_j2k->cstr_index->tile_index[l_tile_no_to_dec].nb_tps) {
-                /* the index for this tile has not been built,
-                 *  so move to the last SOT read */
-                if (!(opj_stream_read_seek(p_stream,
-                                           p_j2k->m_specific_param.m_decoder.m_last_sot_read_pos + 2, p_manager))) {
-                    opj_event_msg(p_manager, EVT_ERROR, "Problem with seek function\n");
-                    return OPJ_FALSE;
-                }
-            } else {
-                if (!(opj_stream_read_seek(p_stream,
-                                           p_j2k->cstr_index->tile_index[l_tile_no_to_dec].tp_index[0].start_pos + 2,
-                                           p_manager))) {
-                    opj_event_msg(p_manager, EVT_ERROR, "Problem with seek function\n");
-                    return OPJ_FALSE;
-                }
-            }
-            /* Special case if we have previously read the EOC marker (if the previous tile getted is the last ) */
-            if (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_EOC) {
-                p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_TPHSOT;
-            }
-        }
-
-    /* Reset current tile part number for all tiles, and not only the one */
-    /* of interest. */
-    /* Not completely sure this is always correct but required for */
-    /* ./build/bin/j2k_random_tile_access ./build/tests/tte1.j2k */
-    l_nb_tiles = p_j2k->m_cp.tw * p_j2k->m_cp.th;
-    for (i = 0; i < l_nb_tiles; ++i) {
-        p_j2k->m_cp.tcps[i].m_current_tile_part_number = -1;
-    }
-
-    for (;;) {
-        if (! opj_j2k_read_tile_header(p_j2k,
-                                       &l_current_tile_no,
-                                       NULL,
-                                       &l_tile_x0, &l_tile_y0,
-                                       &l_tile_x1, &l_tile_y1,
-                                       &l_nb_comps,
-                                       &l_go_on,
-                                       p_stream,
-                                       p_manager)) {
-            return OPJ_FALSE;
-        }
-
-        if (! l_go_on) {
-            break;
-        }
-
-        if (! opj_j2k_decode_tile(p_j2k, l_current_tile_no, NULL, 0,
-                                  p_stream, p_manager)) {
-            return OPJ_FALSE;
-        }
-        opj_event_msg(p_manager, EVT_INFO, "Tile %d/%d has been decoded.\n",
-                      l_current_tile_no + 1, p_j2k->m_cp.th * p_j2k->m_cp.tw);
-
-        if (! opj_j2k_update_image_data(p_j2k->m_tcd,
-                                        p_j2k->m_output_image)) {
-            return OPJ_FALSE;
-        }
-        opj_j2k_tcp_data_destroy(&p_j2k->m_cp.tcps[l_current_tile_no]);
-
-        opj_event_msg(p_manager, EVT_INFO,
-                      "Image data has been updated with tile %d.\n\n", l_current_tile_no + 1);
-
-        if (l_current_tile_no == l_tile_no_to_dec) {
-            /* move into the codestream to the first SOT (FIXME or not move?)*/
-            if (!(opj_stream_read_seek(p_stream, p_j2k->cstr_index->main_head_end + 2,
-                                       p_manager))) {
-                opj_event_msg(p_manager, EVT_ERROR, "Problem with seek function\n");
-                return OPJ_FALSE;
-            }
-            break;
-        } else {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "Tile read, decoded and updated is not the desired one (%d vs %d).\n",
-                          l_current_tile_no + 1, l_tile_no_to_dec + 1);
-        }
-
-    }
-
-    return OPJ_TRUE;
-}
-
-/**
- * Sets up the procedures to do on decoding one tile. Developpers wanting to extend the library can add their own reading procedures.
- */
-static OPJ_BOOL opj_j2k_setup_decoding_tile(opj_j2k_t *p_j2k,
-        opj_event_mgr_t * p_manager)
-{
-    /* preconditions*/
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_decode_one_tile, p_manager)) {
-        return OPJ_FALSE;
-    }
-    /* DEVELOPER CORNER, add your custom procedures */
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_move_data_from_codec_to_output_image(opj_j2k_t * p_j2k,
-        opj_image_t * p_image)
-{
-    OPJ_UINT32 compno;
-
-    /* Move data and copy one information from codec to output image*/
-    if (p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode > 0) {
-        opj_image_comp_t* newcomps =
-            (opj_image_comp_t*) opj_malloc(
-                p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode *
-                sizeof(opj_image_comp_t));
-        if (newcomps == NULL) {
-            opj_image_destroy(p_j2k->m_private_image);
-            p_j2k->m_private_image = NULL;
-            return OPJ_FALSE;
-        }
-        for (compno = 0; compno < p_image->numcomps; compno++) {
-            opj_image_data_free(p_image->comps[compno].data);
-            p_image->comps[compno].data = NULL;
-        }
-        for (compno = 0;
-                compno < p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode; compno++) {
-            OPJ_UINT32 src_compno =
-                p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode[compno];
-            memcpy(&(newcomps[compno]),
-                   &(p_j2k->m_output_image->comps[src_compno]),
-                   sizeof(opj_image_comp_t));
-            newcomps[compno].resno_decoded =
-                p_j2k->m_output_image->comps[src_compno].resno_decoded;
-            newcomps[compno].data = p_j2k->m_output_image->comps[src_compno].data;
-            p_j2k->m_output_image->comps[src_compno].data = NULL;
-        }
-        for (compno = 0; compno < p_image->numcomps; compno++) {
-            assert(p_j2k->m_output_image->comps[compno].data == NULL);
-            opj_image_data_free(p_j2k->m_output_image->comps[compno].data);
-            p_j2k->m_output_image->comps[compno].data = NULL;
-        }
-        p_image->numcomps = p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode;
-        opj_free(p_image->comps);
-        p_image->comps = newcomps;
-    } else {
-        for (compno = 0; compno < p_image->numcomps; compno++) {
-            p_image->comps[compno].resno_decoded =
-                p_j2k->m_output_image->comps[compno].resno_decoded;
-            opj_image_data_free(p_image->comps[compno].data);
-            p_image->comps[compno].data = p_j2k->m_output_image->comps[compno].data;
-#if 0
-            char fn[256];
-            sprintf(fn, "/tmp/%d.raw", compno);
-            FILE *debug = fopen(fn, "wb");
-            fwrite(p_image->comps[compno].data, sizeof(OPJ_INT32),
-                   p_image->comps[compno].w * p_image->comps[compno].h, debug);
-            fclose(debug);
-#endif
-            p_j2k->m_output_image->comps[compno].data = NULL;
-        }
-    }
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_j2k_decode(opj_j2k_t * p_j2k,
-                        opj_stream_private_t * p_stream,
-                        opj_image_t * p_image,
-                        opj_event_mgr_t * p_manager)
-{
-    if (!p_image) {
-        return OPJ_FALSE;
-    }
-
-    /* Heuristics to detect sequence opj_read_header(), opj_set_decoded_resolution_factor() */
-    /* and finally opj_decode_image() without manual setting of comps[].factor */
-    /* We could potentially always execute it, if we don't allow people to do */
-    /* opj_read_header(), modify x0,y0,x1,y1 of returned image an call opj_decode_image() */
-    if (p_j2k->m_cp.m_specific_param.m_dec.m_reduce > 0 &&
-            p_j2k->m_private_image != NULL &&
-            p_j2k->m_private_image->numcomps > 0 &&
-            p_j2k->m_private_image->comps[0].factor ==
-            p_j2k->m_cp.m_specific_param.m_dec.m_reduce &&
-            p_image->numcomps > 0 &&
-            p_image->comps[0].factor == 0 &&
-            /* Don't mess with image dimension if the user has allocated it */
-            p_image->comps[0].data == NULL) {
-        OPJ_UINT32 it_comp;
-
-        /* Update the comps[].factor member of the output image with the one */
-        /* of m_reduce */
-        for (it_comp = 0; it_comp < p_image->numcomps; ++it_comp) {
-            p_image->comps[it_comp].factor = p_j2k->m_cp.m_specific_param.m_dec.m_reduce;
-        }
-        if (!opj_j2k_update_image_dimensions(p_image, p_manager)) {
-            return OPJ_FALSE;
-        }
-    }
-
-    if (p_j2k->m_output_image == NULL) {
-        p_j2k->m_output_image = opj_image_create0();
-        if (!(p_j2k->m_output_image)) {
-            return OPJ_FALSE;
-        }
-    }
-    opj_copy_image_header(p_image, p_j2k->m_output_image);
-
-    /* customization of the decoding */
-    if (!opj_j2k_setup_decoding(p_j2k, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* Decode the codestream */
-    if (! opj_j2k_exec(p_j2k, p_j2k->m_procedure_list, p_stream, p_manager)) {
-        opj_image_destroy(p_j2k->m_private_image);
-        p_j2k->m_private_image = NULL;
-        return OPJ_FALSE;
-    }
-
-    /* Move data and copy one information from codec to output image*/
-    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_stream_private_t *p_stream,
-                          opj_image_t* p_image,
-                          opj_event_mgr_t * p_manager,
-                          OPJ_UINT32 tile_index)
-{
-    OPJ_UINT32 compno;
-    OPJ_UINT32 l_tile_x, l_tile_y;
-    opj_image_comp_t* l_img_comp;
-
-    if (!p_image) {
-        opj_event_msg(p_manager, EVT_ERROR, "We need an image previously created.\n");
-        return OPJ_FALSE;
-    }
-
-    if (p_image->numcomps < p_j2k->m_private_image->numcomps) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Image has less components than codestream.\n");
-        return OPJ_FALSE;
-    }
-
-    if (/*(tile_index < 0) &&*/ (tile_index >= p_j2k->m_cp.tw * p_j2k->m_cp.th)) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Tile index provided by the user is incorrect %d (max = %d) \n", tile_index,
-                      (p_j2k->m_cp.tw * p_j2k->m_cp.th) - 1);
-        return OPJ_FALSE;
-    }
-
-    /* Compute the dimension of the desired tile*/
-    l_tile_x = tile_index % p_j2k->m_cp.tw;
-    l_tile_y = tile_index / p_j2k->m_cp.tw;
-
-    p_image->x0 = l_tile_x * p_j2k->m_cp.tdx + p_j2k->m_cp.tx0;
-    if (p_image->x0 < p_j2k->m_private_image->x0) {
-        p_image->x0 = p_j2k->m_private_image->x0;
-    }
-    p_image->x1 = (l_tile_x + 1) * p_j2k->m_cp.tdx + p_j2k->m_cp.tx0;
-    if (p_image->x1 > p_j2k->m_private_image->x1) {
-        p_image->x1 = p_j2k->m_private_image->x1;
-    }
-
-    p_image->y0 = l_tile_y * p_j2k->m_cp.tdy + p_j2k->m_cp.ty0;
-    if (p_image->y0 < p_j2k->m_private_image->y0) {
-        p_image->y0 = p_j2k->m_private_image->y0;
-    }
-    p_image->y1 = (l_tile_y + 1) * p_j2k->m_cp.tdy + p_j2k->m_cp.ty0;
-    if (p_image->y1 > p_j2k->m_private_image->y1) {
-        p_image->y1 = p_j2k->m_private_image->y1;
-    }
-
-    l_img_comp = p_image->comps;
-    for (compno = 0; compno < p_j2k->m_private_image->numcomps; ++compno) {
-        OPJ_INT32 l_comp_x1, l_comp_y1;
-
-        l_img_comp->factor = p_j2k->m_private_image->comps[compno].factor;
-
-        l_img_comp->x0 = opj_uint_ceildiv(p_image->x0, l_img_comp->dx);
-        l_img_comp->y0 = opj_uint_ceildiv(p_image->y0, l_img_comp->dy);
-        l_comp_x1 = opj_int_ceildiv((OPJ_INT32)p_image->x1, (OPJ_INT32)l_img_comp->dx);
-        l_comp_y1 = opj_int_ceildiv((OPJ_INT32)p_image->y1, (OPJ_INT32)l_img_comp->dy);
-
-        l_img_comp->w = (OPJ_UINT32)(opj_int_ceildivpow2(l_comp_x1,
-                                     (OPJ_INT32)l_img_comp->factor) - opj_int_ceildivpow2((OPJ_INT32)l_img_comp->x0,
-                                             (OPJ_INT32)l_img_comp->factor));
-        l_img_comp->h = (OPJ_UINT32)(opj_int_ceildivpow2(l_comp_y1,
-                                     (OPJ_INT32)l_img_comp->factor) - opj_int_ceildivpow2((OPJ_INT32)l_img_comp->y0,
-                                             (OPJ_INT32)l_img_comp->factor));
-
-        l_img_comp++;
-    }
-
-    if (p_image->numcomps > p_j2k->m_private_image->numcomps) {
-        /* Can happen when calling repeatdly opj_get_decoded_tile() on an
-         * image with a color palette, where color palette expansion is done
-         * later in jp2.c */
-        for (compno = p_j2k->m_private_image->numcomps; compno < p_image->numcomps;
-                ++compno) {
-            opj_image_data_free(p_image->comps[compno].data);
-            p_image->comps[compno].data = NULL;
-        }
-        p_image->numcomps = p_j2k->m_private_image->numcomps;
-    }
-
-    /* Destroy the previous output image*/
-    if (p_j2k->m_output_image) {
-        opj_image_destroy(p_j2k->m_output_image);
-    }
-
-    /* Create the ouput image from the information previously computed*/
-    p_j2k->m_output_image = opj_image_create0();
-    if (!(p_j2k->m_output_image)) {
-        return OPJ_FALSE;
-    }
-    opj_copy_image_header(p_image, p_j2k->m_output_image);
-
-    p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec = (OPJ_INT32)tile_index;
-
-    /* customization of the decoding */
-    if (!opj_j2k_setup_decoding_tile(p_j2k, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* Decode the codestream */
-    if (! opj_j2k_exec(p_j2k, p_j2k->m_procedure_list, p_stream, p_manager)) {
-        opj_image_destroy(p_j2k->m_private_image);
-        p_j2k->m_private_image = NULL;
-        return OPJ_FALSE;
-    }
-
-    /* Move data and copy one information from codec to output image*/
-    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_UINT32 res_factor,
-        opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32 it_comp;
-
-    p_j2k->m_cp.m_specific_param.m_dec.m_reduce = res_factor;
-
-    if (p_j2k->m_private_image) {
-        if (p_j2k->m_private_image->comps) {
-            if (p_j2k->m_specific_param.m_decoder.m_default_tcp) {
-                if (p_j2k->m_specific_param.m_decoder.m_default_tcp->tccps) {
-                    for (it_comp = 0 ; it_comp < p_j2k->m_private_image->numcomps; it_comp++) {
-                        OPJ_UINT32 max_res =
-                            p_j2k->m_specific_param.m_decoder.m_default_tcp->tccps[it_comp].numresolutions;
-                        if (res_factor >= max_res) {
-                            opj_event_msg(p_manager, EVT_ERROR,
-                                          "Resolution factor is greater than the maximum resolution in the component.\n");
-                            return OPJ_FALSE;
-                        }
-                        p_j2k->m_private_image->comps[it_comp].factor = res_factor;
-                    }
-                    return OPJ_TRUE;
-                }
-            }
-        }
-    }
-
-    return OPJ_FALSE;
-}
-
-OPJ_BOOL opj_j2k_encode(opj_j2k_t * p_j2k,
-                        opj_stream_private_t *p_stream,
-                        opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32 i, j;
-    OPJ_UINT32 l_nb_tiles;
-    OPJ_SIZE_T l_max_tile_size = 0, l_current_tile_size;
-    OPJ_BYTE * l_current_data = 00;
-    OPJ_BOOL l_reuse_data = OPJ_FALSE;
-    opj_tcd_t* p_tcd = 00;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_stream != 00);
-    assert(p_manager != 00);
-
-    p_tcd = p_j2k->m_tcd;
-
-    l_nb_tiles = p_j2k->m_cp.th * p_j2k->m_cp.tw;
-    if (l_nb_tiles == 1) {
-        l_reuse_data = OPJ_TRUE;
-#ifdef __SSE__
-        for (j = 0; j < p_j2k->m_tcd->image->numcomps; ++j) {
-            opj_image_comp_t * l_img_comp = p_tcd->image->comps + j;
-            if (((size_t)l_img_comp->data & 0xFU) !=
-                    0U) { /* tile data shall be aligned on 16 bytes */
-                l_reuse_data = OPJ_FALSE;
-            }
-        }
-#endif
-    }
-    for (i = 0; i < l_nb_tiles; ++i) {
-        if (! opj_j2k_pre_write_tile(p_j2k, i, p_stream, p_manager)) {
-            if (l_current_data) {
-                opj_free(l_current_data);
-            }
-            return OPJ_FALSE;
-        }
-
-        /* if we only have one tile, then simply set tile component data equal to image component data */
-        /* otherwise, allocate the data */
-        for (j = 0; j < p_j2k->m_tcd->image->numcomps; ++j) {
-            opj_tcd_tilecomp_t* l_tilec = p_tcd->tcd_image->tiles->comps + j;
-            if (l_reuse_data) {
-                opj_image_comp_t * l_img_comp = p_tcd->image->comps + j;
-                l_tilec->data  =  l_img_comp->data;
-                l_tilec->ownsData = OPJ_FALSE;
-            } else {
-                if (! opj_alloc_tile_component_data(l_tilec)) {
-                    opj_event_msg(p_manager, EVT_ERROR, "Error allocating tile component data.");
-                    if (l_current_data) {
-                        opj_free(l_current_data);
-                    }
-                    return OPJ_FALSE;
-                }
-            }
-        }
-        l_current_tile_size = opj_tcd_get_encoded_tile_size(p_j2k->m_tcd);
-        if (!l_reuse_data) {
-            if (l_current_tile_size > l_max_tile_size) {
-                OPJ_BYTE *l_new_current_data = (OPJ_BYTE *) opj_realloc(l_current_data,
-                                               l_current_tile_size);
-                if (! l_new_current_data) {
-                    if (l_current_data) {
-                        opj_free(l_current_data);
-                    }
-                    opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to encode all tiles\n");
-                    return OPJ_FALSE;
-                }
-                l_current_data = l_new_current_data;
-                l_max_tile_size = l_current_tile_size;
-            }
-            if (l_current_data == NULL) {
-                /* Should not happen in practice, but will avoid Coverity to */
-                /* complain about a null pointer dereference */
-                assert(0);
-                return OPJ_FALSE;
-            }
-
-            /* copy image data (32 bit) to l_current_data as contiguous, all-component, zero offset buffer */
-            /* 32 bit components @ 8 bit precision get converted to 8 bit */
-            /* 32 bit components @ 16 bit precision get converted to 16 bit */
-            opj_j2k_get_tile_data(p_j2k->m_tcd, l_current_data);
-
-            /* now copy this data into the tile component */
-            if (! opj_tcd_copy_tile_data(p_j2k->m_tcd, l_current_data,
-                                         l_current_tile_size)) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Size mismatch between tile data and sent data.");
-                opj_free(l_current_data);
-                return OPJ_FALSE;
-            }
-        }
-
-        if (! opj_j2k_post_write_tile(p_j2k, p_stream, p_manager)) {
-            if (l_current_data) {
-                opj_free(l_current_data);
-            }
-            return OPJ_FALSE;
-        }
-    }
-
-    if (l_current_data) {
-        opj_free(l_current_data);
-    }
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_j2k_end_compress(opj_j2k_t *p_j2k,
-                              opj_stream_private_t *p_stream,
-                              opj_event_mgr_t * p_manager)
-{
-    /* customization of the encoding */
-    if (! opj_j2k_setup_end_compress(p_j2k, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    if (! opj_j2k_exec(p_j2k, p_j2k->m_procedure_list, p_stream, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_j2k_start_compress(opj_j2k_t *p_j2k,
-                                opj_stream_private_t *p_stream,
-                                opj_image_t * p_image,
-                                opj_event_mgr_t * p_manager)
-{
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_stream != 00);
-    assert(p_manager != 00);
-
-    p_j2k->m_private_image = opj_image_create0();
-    if (! p_j2k->m_private_image) {
-        opj_event_msg(p_manager, EVT_ERROR, "Failed to allocate image header.");
-        return OPJ_FALSE;
-    }
-    opj_copy_image_header(p_image, p_j2k->m_private_image);
-
-    /* TODO_MSD: Find a better way */
-    if (p_image->comps) {
-        OPJ_UINT32 it_comp;
-        for (it_comp = 0 ; it_comp < p_image->numcomps; it_comp++) {
-            if (p_image->comps[it_comp].data) {
-                p_j2k->m_private_image->comps[it_comp].data = p_image->comps[it_comp].data;
-                p_image->comps[it_comp].data = NULL;
-
-            }
-        }
-    }
-
-    /* customization of the validation */
-    if (! opj_j2k_setup_encoding_validation(p_j2k, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* validation of the parameters codec */
-    if (! opj_j2k_exec(p_j2k, p_j2k->m_validation_list, p_stream, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* customization of the encoding */
-    if (! opj_j2k_setup_header_writing(p_j2k, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* write header */
-    if (! opj_j2k_exec(p_j2k, p_j2k->m_procedure_list, p_stream, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_pre_write_tile(opj_j2k_t * p_j2k,
-                                       OPJ_UINT32 p_tile_index,
-                                       opj_stream_private_t *p_stream,
-                                       opj_event_mgr_t * p_manager)
-{
-    (void)p_stream;
-    if (p_tile_index != p_j2k->m_current_tile_number) {
-        opj_event_msg(p_manager, EVT_ERROR, "The given tile index does not match.");
-        return OPJ_FALSE;
-    }
-
-    opj_event_msg(p_manager, EVT_INFO, "tile number %d / %d\n",
-                  p_j2k->m_current_tile_number + 1, p_j2k->m_cp.tw * p_j2k->m_cp.th);
-
-    p_j2k->m_specific_param.m_encoder.m_current_tile_part_number = 0;
-    p_j2k->m_tcd->cur_totnum_tp = p_j2k->m_cp.tcps[p_tile_index].m_nb_tile_parts;
-    p_j2k->m_specific_param.m_encoder.m_current_poc_tile_part_number = 0;
-
-    /* initialisation before tile encoding  */
-    if (! opj_tcd_init_encode_tile(p_j2k->m_tcd, p_j2k->m_current_tile_number,
-                                   p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static void opj_get_tile_dimensions(opj_image_t * l_image,
-                                    opj_tcd_tilecomp_t * l_tilec,
-                                    opj_image_comp_t * l_img_comp,
-                                    OPJ_UINT32* l_size_comp,
-                                    OPJ_UINT32* l_width,
-                                    OPJ_UINT32* l_height,
-                                    OPJ_UINT32* l_offset_x,
-                                    OPJ_UINT32* l_offset_y,
-                                    OPJ_UINT32* l_image_width,
-                                    OPJ_UINT32* l_stride,
-                                    OPJ_UINT32* l_tile_offset)
-{
-    OPJ_UINT32 l_remaining;
-    *l_size_comp = l_img_comp->prec >> 3; /* (/8) */
-    l_remaining = l_img_comp->prec & 7;  /* (%8) */
-    if (l_remaining) {
-        *l_size_comp += 1;
-    }
-
-    if (*l_size_comp == 3) {
-        *l_size_comp = 4;
-    }
-
-    *l_width  = (OPJ_UINT32)(l_tilec->x1 - l_tilec->x0);
-    *l_height = (OPJ_UINT32)(l_tilec->y1 - l_tilec->y0);
-    *l_offset_x = opj_uint_ceildiv(l_image->x0, l_img_comp->dx);
-    *l_offset_y = opj_uint_ceildiv(l_image->y0, l_img_comp->dy);
-    *l_image_width = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)l_image->x1 -
-                     (OPJ_INT32)l_image->x0, (OPJ_INT32)l_img_comp->dx);
-    *l_stride = *l_image_width - *l_width;
-    *l_tile_offset = ((OPJ_UINT32)l_tilec->x0 - *l_offset_x) + ((
-                         OPJ_UINT32)l_tilec->y0 - *l_offset_y) * *l_image_width;
-}
-
-static void opj_j2k_get_tile_data(opj_tcd_t * p_tcd, OPJ_BYTE * p_data)
-{
-    OPJ_UINT32 i, j, k = 0;
-
-    for (i = 0; i < p_tcd->image->numcomps; ++i) {
-        opj_image_t * l_image =  p_tcd->image;
-        OPJ_INT32 * l_src_ptr;
-        opj_tcd_tilecomp_t * l_tilec = p_tcd->tcd_image->tiles->comps + i;
-        opj_image_comp_t * l_img_comp = l_image->comps + i;
-        OPJ_UINT32 l_size_comp, l_width, l_height, l_offset_x, l_offset_y,
-                   l_image_width, l_stride, l_tile_offset;
-
-        opj_get_tile_dimensions(l_image,
-                                l_tilec,
-                                l_img_comp,
-                                &l_size_comp,
-                                &l_width,
-                                &l_height,
-                                &l_offset_x,
-                                &l_offset_y,
-                                &l_image_width,
-                                &l_stride,
-                                &l_tile_offset);
-
-        l_src_ptr = l_img_comp->data + l_tile_offset;
-
-        switch (l_size_comp) {
-        case 1: {
-            OPJ_CHAR * l_dest_ptr = (OPJ_CHAR*) p_data;
-            if (l_img_comp->sgnd) {
-                for (j = 0; j < l_height; ++j) {
-                    for (k = 0; k < l_width; ++k) {
-                        *(l_dest_ptr) = (OPJ_CHAR)(*l_src_ptr);
-                        ++l_dest_ptr;
-                        ++l_src_ptr;
-                    }
-                    l_src_ptr += l_stride;
-                }
-            } else {
-                for (j = 0; j < l_height; ++j) {
-                    for (k = 0; k < l_width; ++k) {
-                        *(l_dest_ptr) = (OPJ_CHAR)((*l_src_ptr) & 0xff);
-                        ++l_dest_ptr;
-                        ++l_src_ptr;
-                    }
-                    l_src_ptr += l_stride;
-                }
-            }
-
-            p_data = (OPJ_BYTE*) l_dest_ptr;
-        }
-        break;
-        case 2: {
-            OPJ_INT16 * l_dest_ptr = (OPJ_INT16 *) p_data;
-            if (l_img_comp->sgnd) {
-                for (j = 0; j < l_height; ++j) {
-                    for (k = 0; k < l_width; ++k) {
-                        *(l_dest_ptr++) = (OPJ_INT16)(*(l_src_ptr++));
-                    }
-                    l_src_ptr += l_stride;
-                }
-            } else {
-                for (j = 0; j < l_height; ++j) {
-                    for (k = 0; k < l_width; ++k) {
-                        *(l_dest_ptr++) = (OPJ_INT16)((*(l_src_ptr++)) & 0xffff);
-                    }
-                    l_src_ptr += l_stride;
-                }
-            }
-
-            p_data = (OPJ_BYTE*) l_dest_ptr;
-        }
-        break;
-        case 4: {
-            OPJ_INT32 * l_dest_ptr = (OPJ_INT32 *) p_data;
-            for (j = 0; j < l_height; ++j) {
-                for (k = 0; k < l_width; ++k) {
-                    *(l_dest_ptr++) = *(l_src_ptr++);
-                }
-                l_src_ptr += l_stride;
-            }
-
-            p_data = (OPJ_BYTE*) l_dest_ptr;
-        }
-        break;
-        }
-    }
-}
-
-static OPJ_BOOL opj_j2k_post_write_tile(opj_j2k_t * p_j2k,
-                                        opj_stream_private_t *p_stream,
-                                        opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32 l_nb_bytes_written;
-    OPJ_BYTE * l_current_data = 00;
-    OPJ_UINT32 l_tile_size = 0;
-    OPJ_UINT32 l_available_data;
-
-    /* preconditions */
-    assert(p_j2k->m_specific_param.m_encoder.m_encoded_tile_data);
-
-    l_tile_size = p_j2k->m_specific_param.m_encoder.m_encoded_tile_size;
-    l_available_data = l_tile_size;
-    l_current_data = p_j2k->m_specific_param.m_encoder.m_encoded_tile_data;
-
-    l_nb_bytes_written = 0;
-    if (! opj_j2k_write_first_tile_part(p_j2k, l_current_data, &l_nb_bytes_written,
-                                        l_available_data, p_stream, p_manager)) {
-        return OPJ_FALSE;
-    }
-    l_current_data += l_nb_bytes_written;
-    l_available_data -= l_nb_bytes_written;
-
-    l_nb_bytes_written = 0;
-    if (! opj_j2k_write_all_tile_parts(p_j2k, l_current_data, &l_nb_bytes_written,
-                                       l_available_data, p_stream, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    l_available_data -= l_nb_bytes_written;
-    l_nb_bytes_written = l_tile_size - l_available_data;
-
-    if (opj_stream_write_data(p_stream,
-                              p_j2k->m_specific_param.m_encoder.m_encoded_tile_data,
-                              l_nb_bytes_written, p_manager) != l_nb_bytes_written) {
-        return OPJ_FALSE;
-    }
-
-    ++p_j2k->m_current_tile_number;
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_setup_end_compress(opj_j2k_t *p_j2k,
-        opj_event_mgr_t * p_manager)
-{
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    /* DEVELOPER CORNER, insert your custom procedures */
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_write_eoc, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    if (OPJ_IS_CINEMA(p_j2k->m_cp.rsiz)) {
-        if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                               (opj_procedure)opj_j2k_write_updated_tlm, p_manager)) {
-            return OPJ_FALSE;
-        }
-    }
-
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_write_epc, p_manager)) {
-        return OPJ_FALSE;
-    }
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_end_encoding, p_manager)) {
-        return OPJ_FALSE;
-    }
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_destroy_header_memory, p_manager)) {
-        return OPJ_FALSE;
-    }
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_setup_encoding_validation(opj_j2k_t *p_j2k,
-        opj_event_mgr_t * p_manager)
-{
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    if (! opj_procedure_list_add_procedure(p_j2k->m_validation_list,
-                                           (opj_procedure)opj_j2k_build_encoder, p_manager)) {
-        return OPJ_FALSE;
-    }
-    if (! opj_procedure_list_add_procedure(p_j2k->m_validation_list,
-                                           (opj_procedure)opj_j2k_encoding_validation, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* DEVELOPER CORNER, add your custom validation procedure */
-    if (! opj_procedure_list_add_procedure(p_j2k->m_validation_list,
-                                           (opj_procedure)opj_j2k_mct_validation, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_setup_header_writing(opj_j2k_t *p_j2k,
-        opj_event_mgr_t * p_manager)
-{
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_init_info, p_manager)) {
-        return OPJ_FALSE;
-    }
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_write_soc, p_manager)) {
-        return OPJ_FALSE;
-    }
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_write_siz, p_manager)) {
-        return OPJ_FALSE;
-    }
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_write_cod, p_manager)) {
-        return OPJ_FALSE;
-    }
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_write_qcd, p_manager)) {
-        return OPJ_FALSE;
-    }
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_write_all_coc, p_manager)) {
-        return OPJ_FALSE;
-    }
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_write_all_qcc, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    if (OPJ_IS_CINEMA(p_j2k->m_cp.rsiz)) {
-        if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                               (opj_procedure)opj_j2k_write_tlm, p_manager)) {
-            return OPJ_FALSE;
-        }
-
-        if (p_j2k->m_cp.rsiz == OPJ_PROFILE_CINEMA_4K) {
-            if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                                   (opj_procedure)opj_j2k_write_poc, p_manager)) {
-                return OPJ_FALSE;
-            }
-        }
-    }
-
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_write_regions, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    if (p_j2k->m_cp.comment != 00)  {
-        if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                               (opj_procedure)opj_j2k_write_com, p_manager)) {
-            return OPJ_FALSE;
-        }
-    }
-
-    /* DEVELOPER CORNER, insert your custom procedures */
-    if (p_j2k->m_cp.rsiz & OPJ_EXTENSION_MCT) {
-        if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                               (opj_procedure)opj_j2k_write_mct_data_group, p_manager)) {
-            return OPJ_FALSE;
-        }
-    }
-    /* End of Developer Corner */
-
-    if (p_j2k->cstr_index) {
-        if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                               (opj_procedure)opj_j2k_get_end_header, p_manager)) {
-            return OPJ_FALSE;
-        }
-    }
-
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_create_tcd, p_manager)) {
-        return OPJ_FALSE;
-    }
-    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
-                                           (opj_procedure)opj_j2k_update_rates, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_first_tile_part(opj_j2k_t *p_j2k,
-        OPJ_BYTE * p_data,
-        OPJ_UINT32 * p_data_written,
-        OPJ_UINT32 p_total_data_size,
-        opj_stream_private_t *p_stream,
-        struct opj_event_mgr * p_manager)
-{
-    OPJ_UINT32 l_nb_bytes_written = 0;
-    OPJ_UINT32 l_current_nb_bytes_written;
-    OPJ_BYTE * l_begin_data = 00;
-
-    opj_tcd_t * l_tcd = 00;
-    opj_cp_t * l_cp = 00;
-
-    l_tcd = p_j2k->m_tcd;
-    l_cp = &(p_j2k->m_cp);
-
-    l_tcd->cur_pino = 0;
-
-    /*Get number of tile parts*/
-    p_j2k->m_specific_param.m_encoder.m_current_poc_tile_part_number = 0;
-
-    /* INDEX >> */
-    /* << INDEX */
-
-    l_current_nb_bytes_written = 0;
-    l_begin_data = p_data;
-    if (! opj_j2k_write_sot(p_j2k, p_data, p_total_data_size,
-                            &l_current_nb_bytes_written, p_stream,
-                            p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    l_nb_bytes_written += l_current_nb_bytes_written;
-    p_data += l_current_nb_bytes_written;
-    p_total_data_size -= l_current_nb_bytes_written;
-
-    if (!OPJ_IS_CINEMA(l_cp->rsiz)) {
-#if 0
-        for (compno = 1; compno < p_j2k->m_private_image->numcomps; compno++) {
-            l_current_nb_bytes_written = 0;
-            opj_j2k_write_coc_in_memory(p_j2k, compno, p_data, &l_current_nb_bytes_written,
-                                        p_manager);
-            l_nb_bytes_written += l_current_nb_bytes_written;
-            p_data += l_current_nb_bytes_written;
-            p_total_data_size -= l_current_nb_bytes_written;
-
-            l_current_nb_bytes_written = 0;
-            opj_j2k_write_qcc_in_memory(p_j2k, compno, p_data, &l_current_nb_bytes_written,
-                                        p_manager);
-            l_nb_bytes_written += l_current_nb_bytes_written;
-            p_data += l_current_nb_bytes_written;
-            p_total_data_size -= l_current_nb_bytes_written;
-        }
-#endif
-        if (l_cp->tcps[p_j2k->m_current_tile_number].numpocs) {
-            l_current_nb_bytes_written = 0;
-            opj_j2k_write_poc_in_memory(p_j2k, p_data, &l_current_nb_bytes_written,
-                                        p_manager);
-            l_nb_bytes_written += l_current_nb_bytes_written;
-            p_data += l_current_nb_bytes_written;
-            p_total_data_size -= l_current_nb_bytes_written;
-        }
-    }
-
-    l_current_nb_bytes_written = 0;
-    if (! opj_j2k_write_sod(p_j2k, l_tcd, p_data, &l_current_nb_bytes_written,
-                            p_total_data_size, p_stream, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    l_nb_bytes_written += l_current_nb_bytes_written;
-    * p_data_written = l_nb_bytes_written;
-
-    /* Writing Psot in SOT marker */
-    opj_write_bytes(l_begin_data + 6, l_nb_bytes_written,
-                    4);                                 /* PSOT */
-
-    if (OPJ_IS_CINEMA(l_cp->rsiz)) {
-        opj_j2k_update_tlm(p_j2k, l_nb_bytes_written);
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_all_tile_parts(opj_j2k_t *p_j2k,
-        OPJ_BYTE * p_data,
-        OPJ_UINT32 * p_data_written,
-        OPJ_UINT32 p_total_data_size,
-        opj_stream_private_t *p_stream,
-        struct opj_event_mgr * p_manager
-                                            )
-{
-    OPJ_UINT32 tilepartno = 0;
-    OPJ_UINT32 l_nb_bytes_written = 0;
-    OPJ_UINT32 l_current_nb_bytes_written;
-    OPJ_UINT32 l_part_tile_size;
-    OPJ_UINT32 tot_num_tp;
-    OPJ_UINT32 pino;
-
-    OPJ_BYTE * l_begin_data;
-    opj_tcp_t *l_tcp = 00;
-    opj_tcd_t * l_tcd = 00;
-    opj_cp_t * l_cp = 00;
-
-    l_tcd = p_j2k->m_tcd;
-    l_cp = &(p_j2k->m_cp);
-    l_tcp = l_cp->tcps + p_j2k->m_current_tile_number;
-
-    /*Get number of tile parts*/
-    tot_num_tp = opj_j2k_get_num_tp(l_cp, 0, p_j2k->m_current_tile_number);
-
-    /* start writing remaining tile parts */
-    ++p_j2k->m_specific_param.m_encoder.m_current_tile_part_number;
-    for (tilepartno = 1; tilepartno < tot_num_tp ; ++tilepartno) {
-        p_j2k->m_specific_param.m_encoder.m_current_poc_tile_part_number = tilepartno;
-        l_current_nb_bytes_written = 0;
-        l_part_tile_size = 0;
-        l_begin_data = p_data;
-
-        if (! opj_j2k_write_sot(p_j2k, p_data,
-                                p_total_data_size,
-                                &l_current_nb_bytes_written,
-                                p_stream,
-                                p_manager)) {
-            return OPJ_FALSE;
-        }
-
-        l_nb_bytes_written += l_current_nb_bytes_written;
-        p_data += l_current_nb_bytes_written;
-        p_total_data_size -= l_current_nb_bytes_written;
-        l_part_tile_size += l_current_nb_bytes_written;
-
-        l_current_nb_bytes_written = 0;
-        if (! opj_j2k_write_sod(p_j2k, l_tcd, p_data, &l_current_nb_bytes_written,
-                                p_total_data_size, p_stream, p_manager)) {
-            return OPJ_FALSE;
-        }
-
-        p_data += l_current_nb_bytes_written;
-        l_nb_bytes_written += l_current_nb_bytes_written;
-        p_total_data_size -= l_current_nb_bytes_written;
-        l_part_tile_size += l_current_nb_bytes_written;
-
-        /* Writing Psot in SOT marker */
-        opj_write_bytes(l_begin_data + 6, l_part_tile_size,
-                        4);                                   /* PSOT */
-
-        if (OPJ_IS_CINEMA(l_cp->rsiz)) {
-            opj_j2k_update_tlm(p_j2k, l_part_tile_size);
-        }
-
-        ++p_j2k->m_specific_param.m_encoder.m_current_tile_part_number;
-    }
-
-    for (pino = 1; pino <= l_tcp->numpocs; ++pino) {
-        l_tcd->cur_pino = pino;
-
-        /*Get number of tile parts*/
-        tot_num_tp = opj_j2k_get_num_tp(l_cp, pino, p_j2k->m_current_tile_number);
-        for (tilepartno = 0; tilepartno < tot_num_tp ; ++tilepartno) {
-            p_j2k->m_specific_param.m_encoder.m_current_poc_tile_part_number = tilepartno;
-            l_current_nb_bytes_written = 0;
-            l_part_tile_size = 0;
-            l_begin_data = p_data;
-
-            if (! opj_j2k_write_sot(p_j2k, p_data,
-                                    p_total_data_size,
-                                    &l_current_nb_bytes_written, p_stream,
-                                    p_manager)) {
-                return OPJ_FALSE;
-            }
-
-            l_nb_bytes_written += l_current_nb_bytes_written;
-            p_data += l_current_nb_bytes_written;
-            p_total_data_size -= l_current_nb_bytes_written;
-            l_part_tile_size += l_current_nb_bytes_written;
-
-            l_current_nb_bytes_written = 0;
-
-            if (! opj_j2k_write_sod(p_j2k, l_tcd, p_data, &l_current_nb_bytes_written,
-                                    p_total_data_size, p_stream, p_manager)) {
-                return OPJ_FALSE;
-            }
-
-            l_nb_bytes_written += l_current_nb_bytes_written;
-            p_data += l_current_nb_bytes_written;
-            p_total_data_size -= l_current_nb_bytes_written;
-            l_part_tile_size += l_current_nb_bytes_written;
-
-            /* Writing Psot in SOT marker */
-            opj_write_bytes(l_begin_data + 6, l_part_tile_size,
-                            4);                                   /* PSOT */
-
-            if (OPJ_IS_CINEMA(l_cp->rsiz)) {
-                opj_j2k_update_tlm(p_j2k, l_part_tile_size);
-            }
-
-            ++p_j2k->m_specific_param.m_encoder.m_current_tile_part_number;
-        }
-    }
-
-    *p_data_written = l_nb_bytes_written;
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_write_updated_tlm(opj_j2k_t *p_j2k,
-        struct opj_stream_private *p_stream,
-        struct opj_event_mgr * p_manager)
-{
-    OPJ_UINT32 l_tlm_size;
-    OPJ_OFF_T l_tlm_position, l_current_position;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    l_tlm_size = 5 * p_j2k->m_specific_param.m_encoder.m_total_tile_parts;
-    l_tlm_position = 6 + p_j2k->m_specific_param.m_encoder.m_tlm_start;
-    l_current_position = opj_stream_tell(p_stream);
-
-    if (! opj_stream_seek(p_stream, l_tlm_position, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    if (opj_stream_write_data(p_stream,
-                              p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer, l_tlm_size,
-                              p_manager) != l_tlm_size) {
-        return OPJ_FALSE;
-    }
-
-    if (! opj_stream_seek(p_stream, l_current_position, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_end_encoding(opj_j2k_t *p_j2k,
-                                     struct opj_stream_private *p_stream,
-                                     struct opj_event_mgr * p_manager)
-{
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    OPJ_UNUSED(p_stream);
-    OPJ_UNUSED(p_manager);
-
-    opj_tcd_destroy(p_j2k->m_tcd);
-    p_j2k->m_tcd = 00;
-
-    if (p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer) {
-        opj_free(p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer);
-        p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer = 0;
-        p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current = 0;
-    }
-
-    if (p_j2k->m_specific_param.m_encoder.m_encoded_tile_data) {
-        opj_free(p_j2k->m_specific_param.m_encoder.m_encoded_tile_data);
-        p_j2k->m_specific_param.m_encoder.m_encoded_tile_data = 0;
-    }
-
-    p_j2k->m_specific_param.m_encoder.m_encoded_tile_size = 0;
-
-    return OPJ_TRUE;
-}
-
-/**
- * Destroys the memory associated with the decoding of headers.
- */
-static OPJ_BOOL opj_j2k_destroy_header_memory(opj_j2k_t * p_j2k,
-        opj_stream_private_t *p_stream,
-        opj_event_mgr_t * p_manager
-                                             )
-{
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_stream != 00);
-    assert(p_manager != 00);
-
-    OPJ_UNUSED(p_stream);
-    OPJ_UNUSED(p_manager);
-
-    if (p_j2k->m_specific_param.m_encoder.m_header_tile_data) {
-        opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
-        p_j2k->m_specific_param.m_encoder.m_header_tile_data = 0;
-    }
-
-    p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_j2k_init_info(opj_j2k_t *p_j2k,
-                                  struct opj_stream_private *p_stream,
-                                  struct opj_event_mgr * p_manager)
-{
-    opj_codestream_info_t * l_cstr_info = 00;
-
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-    (void)l_cstr_info;
-
-    OPJ_UNUSED(p_stream);
-
-    /* TODO mergeV2: check this part which use cstr_info */
-    /*l_cstr_info = p_j2k->cstr_info;
-
-    if (l_cstr_info)  {
-            OPJ_UINT32 compno;
-            l_cstr_info->tile = (opj_tile_info_t *) opj_malloc(p_j2k->m_cp.tw * p_j2k->m_cp.th * sizeof(opj_tile_info_t));
-
-            l_cstr_info->image_w = p_j2k->m_image->x1 - p_j2k->m_image->x0;
-            l_cstr_info->image_h = p_j2k->m_image->y1 - p_j2k->m_image->y0;
-
-            l_cstr_info->prog = (&p_j2k->m_cp.tcps[0])->prg;
-
-            l_cstr_info->tw = p_j2k->m_cp.tw;
-            l_cstr_info->th = p_j2k->m_cp.th;
-
-            l_cstr_info->tile_x = p_j2k->m_cp.tdx;*/        /* new version parser */
-    /*l_cstr_info->tile_y = p_j2k->m_cp.tdy;*/      /* new version parser */
-    /*l_cstr_info->tile_Ox = p_j2k->m_cp.tx0;*/     /* new version parser */
-    /*l_cstr_info->tile_Oy = p_j2k->m_cp.ty0;*/     /* new version parser */
-
-    /*l_cstr_info->numcomps = p_j2k->m_image->numcomps;
-
-    l_cstr_info->numlayers = (&p_j2k->m_cp.tcps[0])->numlayers;
-
-    l_cstr_info->numdecompos = (OPJ_INT32*) opj_malloc(p_j2k->m_image->numcomps * sizeof(OPJ_INT32));
-
-    for (compno=0; compno < p_j2k->m_image->numcomps; compno++) {
-            l_cstr_info->numdecompos[compno] = (&p_j2k->m_cp.tcps[0])->tccps->numresolutions - 1;
-    }
-
-    l_cstr_info->D_max = 0.0;       */      /* ADD Marcela */
-
-    /*l_cstr_info->main_head_start = opj_stream_tell(p_stream);*/ /* position of SOC */
-
-    /*l_cstr_info->maxmarknum = 100;
-    l_cstr_info->marker = (opj_marker_info_t *) opj_malloc(l_cstr_info->maxmarknum * sizeof(opj_marker_info_t));
-    l_cstr_info->marknum = 0;
-    }*/
-
-    return opj_j2k_calculate_tp(p_j2k, &(p_j2k->m_cp),
-                                &p_j2k->m_specific_param.m_encoder.m_total_tile_parts, p_j2k->m_private_image,
-                                p_manager);
-}
-
-/**
- * Creates a tile-coder encoder.
- *
- * @param       p_stream                the stream to write data to.
- * @param       p_j2k                   J2K codec.
- * @param       p_manager               the user event manager.
-*/
-static OPJ_BOOL opj_j2k_create_tcd(opj_j2k_t *p_j2k,
-                                   opj_stream_private_t *p_stream,
-                                   opj_event_mgr_t * p_manager
-                                  )
-{
-    /* preconditions */
-    assert(p_j2k != 00);
-    assert(p_manager != 00);
-    assert(p_stream != 00);
-
-    OPJ_UNUSED(p_stream);
-
-    p_j2k->m_tcd = opj_tcd_create(OPJ_FALSE);
-
-    if (! p_j2k->m_tcd) {
-        opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to create Tile Coder\n");
-        return OPJ_FALSE;
-    }
-
-    if (!opj_tcd_init(p_j2k->m_tcd, p_j2k->m_private_image, &p_j2k->m_cp,
-                      p_j2k->m_tp)) {
-        opj_tcd_destroy(p_j2k->m_tcd);
-        p_j2k->m_tcd = 00;
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_j2k_write_tile(opj_j2k_t * p_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)
-{
-    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);
-        return OPJ_FALSE;
-    } else {
-        OPJ_UINT32 j;
-        /* Allocate data */
-        for (j = 0; j < p_j2k->m_tcd->image->numcomps; ++j) {
-            opj_tcd_tilecomp_t* l_tilec = p_j2k->m_tcd->tcd_image->tiles->comps + j;
-
-            if (! opj_alloc_tile_component_data(l_tilec)) {
-                opj_event_msg(p_manager, EVT_ERROR, "Error allocating tile component data.");
-                return OPJ_FALSE;
-            }
-        }
-
-        /* now copy data into the tile component */
-        if (! opj_tcd_copy_tile_data(p_j2k->m_tcd, p_data, p_data_size)) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Size mismatch between tile data and sent data.");
-            return OPJ_FALSE;
-        }
-        if (! opj_j2k_post_write_tile(p_j2k, p_stream, p_manager)) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Error while opj_j2k_post_write_tile with tile index = %d\n", p_tile_index);
-            return OPJ_FALSE;
-        }
-    }
-
-    return OPJ_TRUE;
-}
diff --git a/third_party/libopenjpeg20/j2k.h b/third_party/libopenjpeg20/j2k.h
deleted file mode 100644
index 5d393c9..0000000
--- a/third_party/libopenjpeg20/j2k.h
+++ /dev/null
@@ -1,880 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2006-2007, Parvatha Elangovan
- * Copyright (c) 2008, Jerome Fimes, Communications & Systemes <jerome.fimes@c-s.fr>
- * Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France
- * Copyright (c) 2012, CS Systemes d'Information, France
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-#ifndef OPJ_J2K_H
-#define OPJ_J2K_H
-/**
-@file j2k.h
-@brief The JPEG-2000 Codestream Reader/Writer (J2K)
-
-The functions in J2K.C have for goal to read/write the several parts of the codestream: markers and data.
-*/
-
-/** @defgroup J2K J2K - JPEG-2000 codestream reader/writer */
-/*@{*/
-
-#define J2K_CP_CSTY_PRT 0x01
-#define J2K_CP_CSTY_SOP 0x02
-#define J2K_CP_CSTY_EPH 0x04
-#define J2K_CCP_CSTY_PRT 0x01
-#define J2K_CCP_CBLKSTY_LAZY 0x01     /**< Selective arithmetic coding bypass */
-#define J2K_CCP_CBLKSTY_RESET 0x02    /**< Reset context probabilities on coding pass boundaries */
-#define J2K_CCP_CBLKSTY_TERMALL 0x04  /**< Termination on each coding pass */
-#define J2K_CCP_CBLKSTY_VSC 0x08      /**< Vertically stripe causal context */
-#define J2K_CCP_CBLKSTY_PTERM 0x10    /**< Predictable termination */
-#define J2K_CCP_CBLKSTY_SEGSYM 0x20   /**< Segmentation symbols are used */
-#define J2K_CCP_QNTSTY_NOQNT 0
-#define J2K_CCP_QNTSTY_SIQNT 1
-#define J2K_CCP_QNTSTY_SEQNT 2
-
-/* ----------------------------------------------------------------------- */
-
-#define J2K_MS_SOC 0xff4f   /**< SOC marker value */
-#define J2K_MS_SOT 0xff90   /**< SOT marker value */
-#define J2K_MS_SOD 0xff93   /**< SOD marker value */
-#define J2K_MS_EOC 0xffd9   /**< EOC marker value */
-#define J2K_MS_SIZ 0xff51   /**< SIZ marker value */
-#define J2K_MS_COD 0xff52   /**< COD marker value */
-#define J2K_MS_COC 0xff53   /**< COC marker value */
-#define J2K_MS_RGN 0xff5e   /**< RGN marker value */
-#define J2K_MS_QCD 0xff5c   /**< QCD marker value */
-#define J2K_MS_QCC 0xff5d   /**< QCC marker value */
-#define J2K_MS_POC 0xff5f   /**< POC marker value */
-#define J2K_MS_TLM 0xff55   /**< TLM marker value */
-#define J2K_MS_PLM 0xff57   /**< PLM marker value */
-#define J2K_MS_PLT 0xff58   /**< PLT marker value */
-#define J2K_MS_PPM 0xff60   /**< PPM marker value */
-#define J2K_MS_PPT 0xff61   /**< PPT marker value */
-#define J2K_MS_SOP 0xff91   /**< SOP marker value */
-#define J2K_MS_EPH 0xff92   /**< EPH marker value */
-#define J2K_MS_CRG 0xff63   /**< CRG marker value */
-#define J2K_MS_COM 0xff64   /**< COM marker value */
-#define J2K_MS_CBD 0xff78   /**< CBD marker value */
-#define J2K_MS_MCC 0xff75   /**< MCC marker value */
-#define J2K_MS_MCT 0xff74   /**< MCT marker value */
-#define J2K_MS_MCO 0xff77   /**< MCO marker value */
-
-#define J2K_MS_UNK 0        /**< UNKNOWN marker value */
-
-/* UniPG>> */
-#ifdef USE_JPWL
-#define J2K_MS_EPC 0xff68   /**< EPC marker value (Part 11: JPEG 2000 for Wireless) */
-#define J2K_MS_EPB 0xff66   /**< EPB marker value (Part 11: JPEG 2000 for Wireless) */
-#define J2K_MS_ESD 0xff67   /**< ESD marker value (Part 11: JPEG 2000 for Wireless) */
-#define J2K_MS_RED 0xff69   /**< RED marker value (Part 11: JPEG 2000 for Wireless) */
-#endif /* USE_JPWL */
-#ifdef USE_JPSEC
-#define J2K_MS_SEC 0xff65    /**< SEC marker value (Part 8: Secure JPEG 2000) */
-#define J2K_MS_INSEC 0xff94  /**< INSEC marker value (Part 8: Secure JPEG 2000) */
-#endif /* USE_JPSEC */
-/* <<UniPG */
-
-#define J2K_MAX_POCS    32      /**< Maximum number of POCs */
-
-/* ----------------------------------------------------------------------- */
-
-/**
- * Values that specify the status of the decoding process when decoding the main header.
- * These values may be combined with a | operator.
- * */
-typedef enum J2K_STATUS {
-    J2K_STATE_NONE  =  0x0000, /**< a SOC marker is expected */
-    J2K_STATE_MHSOC  = 0x0001, /**< a SOC marker is expected */
-    J2K_STATE_MHSIZ  = 0x0002, /**< a SIZ marker is expected */
-    J2K_STATE_MH     = 0x0004, /**< the decoding process is in the main header */
-    J2K_STATE_TPHSOT = 0x0008, /**< the decoding process is in a tile part header and expects a SOT marker */
-    J2K_STATE_TPH    = 0x0010, /**< the decoding process is in a tile part header */
-    J2K_STATE_MT     = 0x0020, /**< the EOC marker has just been read */
-    J2K_STATE_NEOC   = 0x0040, /**< the decoding process must not expect a EOC marker because the codestream is truncated */
-    J2K_STATE_DATA   = 0x0080, /**< a tile header has been successfully read and codestream is expected */
-
-    J2K_STATE_EOC    = 0x0100, /**< the decoding process has encountered the EOC marker */
-    J2K_STATE_ERR    = 0x8000  /**< the decoding process has encountered an error (FIXME warning V1 = 0x0080)*/
-} J2K_STATUS;
-
-/**
- * Type of elements storing in the MCT data
- */
-typedef enum MCT_ELEMENT_TYPE {
-    MCT_TYPE_INT16 = 0,     /** MCT data is stored as signed shorts*/
-    MCT_TYPE_INT32 = 1,     /** MCT data is stored as signed integers*/
-    MCT_TYPE_FLOAT = 2,     /** MCT data is stored as floats*/
-    MCT_TYPE_DOUBLE = 3     /** MCT data is stored as doubles*/
-} J2K_MCT_ELEMENT_TYPE;
-
-/**
- * Type of MCT array
- */
-typedef enum MCT_ARRAY_TYPE {
-    MCT_TYPE_DEPENDENCY = 0,
-    MCT_TYPE_DECORRELATION = 1,
-    MCT_TYPE_OFFSET = 2
-} J2K_MCT_ARRAY_TYPE;
-
-/* ----------------------------------------------------------------------- */
-
-/**
-T2 encoding mode
-*/
-typedef enum T2_MODE {
-    THRESH_CALC = 0,    /** Function called in Rate allocation process*/
-    FINAL_PASS = 1      /** Function called in Tier 2 process*/
-} J2K_T2_MODE;
-
-/**
- * Quantization stepsize
- */
-typedef struct opj_stepsize {
-    /** exponent */
-    OPJ_INT32 expn;
-    /** mantissa */
-    OPJ_INT32 mant;
-} opj_stepsize_t;
-
-/**
-Tile-component coding parameters
-*/
-typedef struct opj_tccp {
-    /** coding style */
-    OPJ_UINT32 csty;
-    /** number of resolutions */
-    OPJ_UINT32 numresolutions;
-    /** code-blocks width */
-    OPJ_UINT32 cblkw;
-    /** code-blocks height */
-    OPJ_UINT32 cblkh;
-    /** code-block coding style */
-    OPJ_UINT32 cblksty;
-    /** discrete wavelet transform identifier */
-    OPJ_UINT32 qmfbid;
-    /** quantisation style */
-    OPJ_UINT32 qntsty;
-    /** stepsizes used for quantization */
-    opj_stepsize_t stepsizes[OPJ_J2K_MAXBANDS];
-    /** number of guard bits */
-    OPJ_UINT32 numgbits;
-    /** Region Of Interest shift */
-    OPJ_INT32 roishift;
-    /** precinct width */
-    OPJ_UINT32 prcw[OPJ_J2K_MAXRLVLS];
-    /** precinct height */
-    OPJ_UINT32 prch[OPJ_J2K_MAXRLVLS];
-    /** the dc_level_shift **/
-    OPJ_INT32 m_dc_level_shift;
-}
-opj_tccp_t;
-
-
-
-/**
- * FIXME DOC
- */
-typedef struct opj_mct_data {
-    J2K_MCT_ELEMENT_TYPE m_element_type;
-    J2K_MCT_ARRAY_TYPE   m_array_type;
-    OPJ_UINT32           m_index;
-    OPJ_BYTE *           m_data;
-    OPJ_UINT32           m_data_size;
-}
-opj_mct_data_t;
-
-/**
- * FIXME DOC
- */
-typedef struct opj_simple_mcc_decorrelation_data {
-    OPJ_UINT32           m_index;
-    OPJ_UINT32           m_nb_comps;
-    opj_mct_data_t *     m_decorrelation_array;
-    opj_mct_data_t *     m_offset_array;
-    OPJ_BITFIELD         m_is_irreversible : 1;
-}
-opj_simple_mcc_decorrelation_data_t;
-
-typedef struct opj_ppx_struct {
-    OPJ_BYTE*   m_data; /* m_data == NULL => Zppx not read yet */
-    OPJ_UINT32  m_data_size;
-} opj_ppx;
-
-/**
-Tile coding parameters :
-this structure is used to store coding/decoding parameters common to all
-tiles (information like COD, COC in main header)
-*/
-typedef struct opj_tcp {
-    /** coding style */
-    OPJ_UINT32 csty;
-    /** progression order */
-    OPJ_PROG_ORDER prg;
-    /** number of layers */
-    OPJ_UINT32 numlayers;
-    OPJ_UINT32 num_layers_to_decode;
-    /** multi-component transform identifier */
-    OPJ_UINT32 mct;
-    /** rates of layers */
-    OPJ_FLOAT32 rates[100];
-    /** number of progression order changes */
-    OPJ_UINT32 numpocs;
-    /** progression order changes */
-    opj_poc_t pocs[J2K_MAX_POCS];
-
-    /** number of ppt markers (reserved size) */
-    OPJ_UINT32 ppt_markers_count;
-    /** ppt markers data (table indexed by Zppt) */
-    opj_ppx* ppt_markers;
-
-    /** packet header store there for future use in t2_decode_packet */
-    OPJ_BYTE *ppt_data;
-    /** used to keep a track of the allocated memory */
-    OPJ_BYTE *ppt_buffer;
-    /** Number of bytes stored inside ppt_data*/
-    OPJ_UINT32 ppt_data_size;
-    /** size of ppt_data*/
-    OPJ_UINT32 ppt_len;
-    /** add fixed_quality */
-    OPJ_FLOAT32 distoratio[100];
-    /** tile-component coding parameters */
-    opj_tccp_t *tccps;
-    /** current tile part number or -1 if first time into this tile */
-    OPJ_INT32  m_current_tile_part_number;
-    /** number of tile parts for the tile. */
-    OPJ_UINT32 m_nb_tile_parts;
-    /** data for the tile */
-    OPJ_BYTE *      m_data;
-    /** size of data */
-    OPJ_UINT32      m_data_size;
-    /** encoding norms */
-    OPJ_FLOAT64 *   mct_norms;
-    /** the mct decoding matrix */
-    OPJ_FLOAT32 *   m_mct_decoding_matrix;
-    /** the mct coding matrix */
-    OPJ_FLOAT32 *   m_mct_coding_matrix;
-    /** mct records */
-    opj_mct_data_t * m_mct_records;
-    /** the number of mct records. */
-    OPJ_UINT32 m_nb_mct_records;
-    /** the max number of mct records. */
-    OPJ_UINT32 m_nb_max_mct_records;
-    /** mcc records */
-    opj_simple_mcc_decorrelation_data_t * m_mcc_records;
-    /** the number of mct records. */
-    OPJ_UINT32 m_nb_mcc_records;
-    /** the max number of mct records. */
-    OPJ_UINT32 m_nb_max_mcc_records;
-
-
-    /***** FLAGS *******/
-    /** If cod == 1 --> there was a COD marker for the present tile */
-    OPJ_BITFIELD cod : 1;
-    /** If ppt == 1 --> there was a PPT marker for the present tile */
-    OPJ_BITFIELD ppt : 1;
-    /** indicates if a POC marker has been used O:NO, 1:YES */
-    OPJ_BITFIELD POC : 1;
-} opj_tcp_t;
-
-
-
-
-typedef struct opj_encoding_param {
-    /** Maximum rate for each component. If == 0, component size limitation is not considered */
-    OPJ_UINT32 m_max_comp_size;
-    /** Position of tile part flag in progression order*/
-    OPJ_INT32 m_tp_pos;
-    /** fixed layer */
-    OPJ_INT32 *m_matrice;
-    /** Flag determining tile part generation*/
-    OPJ_BYTE m_tp_flag;
-    /** allocation by rate/distortion */
-    OPJ_BITFIELD m_disto_alloc : 1;
-    /** allocation by fixed layer */
-    OPJ_BITFIELD m_fixed_alloc : 1;
-    /** add fixed_quality */
-    OPJ_BITFIELD m_fixed_quality : 1;
-    /** Enabling Tile part generation*/
-    OPJ_BITFIELD m_tp_on : 1;
-}
-opj_encoding_param_t;
-
-typedef struct opj_decoding_param {
-    /** if != 0, then original dimension divided by 2^(reduce); if == 0 or not used, image is decoded to the full resolution */
-    OPJ_UINT32 m_reduce;
-    /** if != 0, then only the first "layer" layers are decoded; if == 0 or not used, all the quality layers are decoded */
-    OPJ_UINT32 m_layer;
-}
-opj_decoding_param_t;
-
-
-/**
- * Coding parameters
- */
-typedef struct opj_cp {
-    /** Size of the image in bits*/
-    /*int img_size;*/
-    /** Rsiz*/
-    OPJ_UINT16 rsiz;
-    /** XTOsiz */
-    OPJ_UINT32 tx0; /* MSD see norm */
-    /** YTOsiz */
-    OPJ_UINT32 ty0; /* MSD see norm */
-    /** XTsiz */
-    OPJ_UINT32 tdx;
-    /** YTsiz */
-    OPJ_UINT32 tdy;
-    /** comment */
-    OPJ_CHAR *comment;
-    /** number of tiles in width */
-    OPJ_UINT32 tw;
-    /** number of tiles in height */
-    OPJ_UINT32 th;
-
-    /** number of ppm markers (reserved size) */
-    OPJ_UINT32 ppm_markers_count;
-    /** ppm markers data (table indexed by Zppm) */
-    opj_ppx* ppm_markers;
-
-    /** packet header store there for future use in t2_decode_packet */
-    OPJ_BYTE *ppm_data;
-    /** size of the ppm_data*/
-    OPJ_UINT32 ppm_len;
-    /** size of the ppm_data*/
-    OPJ_UINT32 ppm_data_read;
-
-    OPJ_BYTE *ppm_data_current;
-
-    /** packet header storage original buffer */
-    OPJ_BYTE *ppm_buffer;
-    /** pointer remaining on the first byte of the first header if ppm is used */
-    OPJ_BYTE *ppm_data_first;
-    /** Number of bytes actually stored inside the ppm_data */
-    OPJ_UINT32 ppm_data_size;
-    /** use in case of multiple marker PPM (number of info already store) */
-    OPJ_INT32 ppm_store;
-    /** use in case of multiple marker PPM (case on non-finished previous info) */
-    OPJ_INT32 ppm_previous;
-
-    /** tile coding parameters */
-    opj_tcp_t *tcps;
-
-    union {
-        opj_decoding_param_t m_dec;
-        opj_encoding_param_t m_enc;
-    }
-    m_specific_param;
-
-
-    /* UniPG>> */
-#ifdef USE_JPWL
-    /** enables writing of EPC in MH, thus activating JPWL */
-    OPJ_BOOL epc_on;
-    /** enables writing of EPB, in case of activated JPWL */
-    OPJ_BOOL epb_on;
-    /** enables writing of ESD, in case of activated JPWL */
-    OPJ_BOOL esd_on;
-    /** enables writing of informative techniques of ESD, in case of activated JPWL */
-    OPJ_BOOL info_on;
-    /** enables writing of RED, in case of activated JPWL */
-    OPJ_BOOL red_on;
-    /** error protection method for MH (0,1,16,32,37-128) */
-    int hprot_MH;
-    /** tile number of header protection specification (>=0) */
-    int hprot_TPH_tileno[JPWL_MAX_NO_TILESPECS];
-    /** error protection methods for TPHs (0,1,16,32,37-128) */
-    int hprot_TPH[JPWL_MAX_NO_TILESPECS];
-    /** tile number of packet protection specification (>=0) */
-    int pprot_tileno[JPWL_MAX_NO_PACKSPECS];
-    /** packet number of packet protection specification (>=0) */
-    int pprot_packno[JPWL_MAX_NO_PACKSPECS];
-    /** error protection methods for packets (0,1,16,32,37-128) */
-    int pprot[JPWL_MAX_NO_PACKSPECS];
-    /** enables writing of ESD, (0/2/4 bytes) */
-    int sens_size;
-    /** sensitivity addressing size (0=auto/2/4 bytes) */
-    int sens_addr;
-    /** sensitivity range (0-3) */
-    int sens_range;
-    /** sensitivity method for MH (-1,0-7) */
-    int sens_MH;
-    /** tile number of sensitivity specification (>=0) */
-    int sens_TPH_tileno[JPWL_MAX_NO_TILESPECS];
-    /** sensitivity methods for TPHs (-1,0-7) */
-    int sens_TPH[JPWL_MAX_NO_TILESPECS];
-    /** enables JPWL correction at the decoder */
-    OPJ_BOOL correct;
-    /** expected number of components at the decoder */
-    int exp_comps;
-    /** maximum number of tiles at the decoder */
-    OPJ_UINT32 max_tiles;
-#endif /* USE_JPWL */
-
-    /******** FLAGS *********/
-    /** if ppm == 1 --> there was a PPM marker*/
-    OPJ_BITFIELD ppm : 1;
-    /** tells if the parameter is a coding or decoding one */
-    OPJ_BITFIELD m_is_decoder : 1;
-    /** whether different bit depth or sign per component is allowed. Decoder only for ow */
-    OPJ_BITFIELD allow_different_bit_depth_sign : 1;
-    /* <<UniPG */
-} opj_cp_t;
-
-
-typedef struct opj_j2k_dec {
-    /** locate in which part of the codestream the decoder is (main header, tile header, end) */
-    OPJ_UINT32 m_state;
-    /**
-     * store decoding parameters common to all tiles (information like COD, COC in main header)
-     */
-    opj_tcp_t *m_default_tcp;
-    OPJ_BYTE  *m_header_data;
-    OPJ_UINT32 m_header_data_size;
-    /** to tell the tile part length */
-    OPJ_UINT32 m_sot_length;
-    /** Only tiles index in the correct range will be decoded.*/
-    OPJ_UINT32 m_start_tile_x;
-    OPJ_UINT32 m_start_tile_y;
-    OPJ_UINT32 m_end_tile_x;
-    OPJ_UINT32 m_end_tile_y;
-
-    /** Index of the tile to decode (used in get_tile) */
-    OPJ_INT32 m_tile_ind_to_dec;
-    /** Position of the last SOT marker read */
-    OPJ_OFF_T m_last_sot_read_pos;
-
-    /**
-     * Indicate that the current tile-part is assume as the last tile part of the codestream.
-     * It is useful in the case of PSot is equal to zero. The sot length will be compute in the
-     * SOD reader function. FIXME NOT USED for the moment
-     */
-    OPJ_BOOL   m_last_tile_part;
-
-    OPJ_UINT32   m_numcomps_to_decode;
-    OPJ_UINT32  *m_comps_indices_to_decode;
-
-    /** to tell that a tile can be decoded. */
-    OPJ_BITFIELD m_can_decode : 1;
-    OPJ_BITFIELD m_discard_tiles : 1;
-    OPJ_BITFIELD m_skip_data : 1;
-    /** TNsot correction : see issue 254 **/
-    OPJ_BITFIELD m_nb_tile_parts_correction_checked : 1;
-    OPJ_BITFIELD m_nb_tile_parts_correction : 1;
-
-} opj_j2k_dec_t;
-
-typedef struct opj_j2k_enc {
-    /** Tile part number, regardless of poc, for each new poc, tp is reset to 1*/
-    OPJ_UINT32 m_current_poc_tile_part_number; /* tp_num */
-
-    /** Tile part number currently coding, taking into account POC. m_current_tile_part_number holds the total number of tile parts while encoding the last tile part.*/
-    OPJ_UINT32 m_current_tile_part_number; /*cur_tp_num */
-
-    /**
-    locate the start position of the TLM marker
-    after encoding the tilepart, a jump (in j2k_write_sod) is done to the TLM marker to store the value of its length.
-    */
-    OPJ_OFF_T m_tlm_start;
-    /**
-     * Stores the sizes of the tlm.
-     */
-    OPJ_BYTE * m_tlm_sot_offsets_buffer;
-    /**
-     * The current offset of the tlm buffer.
-     */
-    OPJ_BYTE * m_tlm_sot_offsets_current;
-
-    /** Total num of tile parts in whole image = num tiles* num tileparts in each tile*/
-    /** used in TLMmarker*/
-    OPJ_UINT32 m_total_tile_parts;   /* totnum_tp */
-
-    /* encoded data for a tile */
-    OPJ_BYTE * m_encoded_tile_data;
-
-    /* size of the encoded_data */
-    OPJ_UINT32 m_encoded_tile_size;
-
-    /* encoded data for a tile */
-    OPJ_BYTE * m_header_tile_data;
-
-    /* size of the encoded_data */
-    OPJ_UINT32 m_header_tile_data_size;
-
-
-} opj_j2k_enc_t;
-
-
-
-struct opj_tcd;
-/**
-JPEG-2000 codestream reader/writer
-*/
-typedef struct opj_j2k {
-    /* J2K codestream is decoded*/
-    OPJ_BOOL m_is_decoder;
-
-    /* FIXME DOC*/
-    union {
-        opj_j2k_dec_t m_decoder;
-        opj_j2k_enc_t m_encoder;
-    }
-    m_specific_param;
-
-    /** pointer to the internal/private encoded / decoded image */
-    opj_image_t* m_private_image;
-
-    /* pointer to the output image (decoded)*/
-    opj_image_t* m_output_image;
-
-    /** Coding parameters */
-    opj_cp_t m_cp;
-
-    /** the list of procedures to exec **/
-    opj_procedure_list_t *  m_procedure_list;
-
-    /** the list of validation procedures to follow to make sure the code is valid **/
-    opj_procedure_list_t *  m_validation_list;
-
-    /** helper used to write the index file */
-    opj_codestream_index_t *cstr_index;
-
-    /** number of the tile currently concern by coding/decoding */
-    OPJ_UINT32 m_current_tile_number;
-
-    /** the current tile coder/decoder **/
-    struct opj_tcd *    m_tcd;
-
-    /** Number of threads to use */
-    int m_num_threads;
-
-    /** Thread pool */
-    opj_thread_pool_t* m_tp;
-
-    OPJ_UINT32 ihdr_w;
-    OPJ_UINT32 ihdr_h;
-    OPJ_UINT32 enumcs;
-    unsigned int dump_state;
-}
-opj_j2k_t;
-
-
-
-
-/** @name Exported functions */
-/*@{*/
-/* ----------------------------------------------------------------------- */
-
-/**
-Setup the decoder decoding parameters using user parameters.
-Decoding parameters are returned in j2k->cp.
-@param j2k J2K decompressor handle
-@param parameters decompression parameters
-*/
-void opj_j2k_setup_decoder(opj_j2k_t *j2k, opj_dparameters_t *parameters);
-
-OPJ_BOOL opj_j2k_set_threads(opj_j2k_t *j2k, OPJ_UINT32 num_threads);
-
-/**
- * Creates a J2K compression structure
- *
- * @return Returns a handle to a J2K compressor if successful, returns NULL otherwise
-*/
-opj_j2k_t* opj_j2k_create_compress(void);
-
-
-OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k,
-                               opj_cparameters_t *parameters,
-                               opj_image_t *image,
-                               opj_event_mgr_t * p_manager);
-
-/**
-Converts an enum type progression order to string type
-*/
-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_stream_private_t *p_stream,
-                                opj_event_mgr_t * p_manager);
-
-/**
- * Reads a jpeg2000 codestream header structure.
- *
- * @param p_stream the stream to read data from.
- * @param p_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,
-                             opj_image_t** p_image,
-                             opj_event_mgr_t* p_manager);
-
-
-/**
- * Destroys a jpeg2000 codec.
- *
- * @param   p_j2k   the jpeg20000 structure to destroy.
- */
-void opj_j2k_destroy(opj_j2k_t *p_j2k);
-
-/**
- * Destroys a codestream index structure.
- *
- * @param   p_cstr_ind  the codestream index parameter to destroy.
- */
-void j2k_destroy_cstr_index(opj_codestream_index_t *p_cstr_ind);
-
-/**
- * Decode tile data.
- * @param   p_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_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);
-
-/**
- * Reads a tile header.
- * @param   p_j2k       the jpeg2000 codec.
- * @param   p_tile_index FIXME DOC
- * @param   p_data_size FIXME DOC
- * @param   p_tile_x0 FIXME DOC
- * @param   p_tile_y0 FIXME DOC
- * @param   p_tile_x1 FIXME DOC
- * @param   p_tile_y1 FIXME DOC
- * @param   p_nb_comps FIXME DOC
- * @param   p_go_on FIXME DOC
- * @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_UINT32 * p_tile_index,
-                                  OPJ_UINT32 * p_data_size,
-                                  OPJ_INT32 * p_tile_x0,
-                                  OPJ_INT32 * p_tile_y0,
-                                  OPJ_INT32 * p_tile_x1,
-                                  OPJ_INT32 * p_tile_y1,
-                                  OPJ_UINT32 * p_nb_comps,
-                                  OPJ_BOOL * p_go_on,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-
-/** Sets the indices of the components to decode.
- *
- * @param p_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.
- * @param p_manager     Event manager
- *
- * @return OPJ_TRUE in case of success.
- */
-OPJ_BOOL opj_j2k_set_decoded_components(opj_j2k_t *p_j2k,
-                                        OPJ_UINT32 numcomps,
-                                        const OPJ_UINT32* comps_indices,
-                                        opj_event_mgr_t * p_manager);
-
-/**
- * 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   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).
- * @param   p_end_x         the right position of the rectangle to decode (in image coordinates).
- * @param   p_end_y         the bottom position of the rectangle to decode (in image coordinates).
- * @param   p_manager       the user event manager
- *
- * @return  true            if the area could be set.
- */
-OPJ_BOOL opj_j2k_set_decode_area(opj_j2k_t *p_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);
-
-/**
- * Creates a J2K decompression structure.
- *
- * @return a handle to a J2K decompressor if successful, NULL otherwise.
- */
-opj_j2k_t* opj_j2k_create_decompress(void);
-
-
-/**
- * Dump some elements from the J2K decompression structure .
- *
- *@param p_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);
-
-
-
-/**
- * Dump an image header structure.
- *
- *@param image          the image header to dump.
- *@param dev_dump_flag      flag to describe if we are in the case of this function is use outside j2k_dump function
- *@param out_stream         output stream where dump the elements.
- */
-void j2k_dump_image_header(opj_image_t* image, OPJ_BOOL dev_dump_flag,
-                           FILE* out_stream);
-
-/**
- * Dump a component image header structure.
- *
- *@param comp       the component image header to dump.
- *@param dev_dump_flag      flag to describe if we are in the case of this function is use outside j2k_dump function
- *@param out_stream         output stream where dump the elements.
- */
-void j2k_dump_image_comp_header(opj_image_comp_t* comp, OPJ_BOOL dev_dump_flag,
-                                FILE* out_stream);
-
-/**
- * Get the codestream info from a JPEG2000 codec.
- *
- *@param    p_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);
-
-/**
- * Get the codestream index from a JPEG2000 codec.
- *
- *@param    p_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);
-
-/**
- * Decode an image from a JPEG-2000 codestream
- * @param j2k J2K decompressor handle
- * @param p_stream  FIXME DOC
- * @param p_image   FIXME DOC
- * @param p_manager FIXME DOC
- * @return FIXME DOC
-*/
-OPJ_BOOL opj_j2k_decode(opj_j2k_t *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_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_UINT32 res_factor,
-        opj_event_mgr_t * p_manager);
-
-
-/**
- * Writes a tile.
- * @param   p_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_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);
-
-/**
- * Encodes an image into a JPEG-2000 codestream
- */
-OPJ_BOOL opj_j2k_encode(opj_j2k_t * p_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   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_stream_private_t *p_stream,
-                                opj_image_t * p_image,
-                                opj_event_mgr_t * p_manager);
-
-/**
- * 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_stream_private_t *cio,
-                              opj_event_mgr_t * p_manager);
-
-OPJ_BOOL opj_j2k_setup_mct_encoding(opj_tcp_t * p_tcp, opj_image_t * p_image);
-
-
-#endif /* OPJ_J2K_H */
diff --git a/third_party/libopenjpeg20/jp2.c b/third_party/libopenjpeg20/jp2.c
deleted file mode 100644
index 1f61a23..0000000
--- a/third_party/libopenjpeg20/jp2.c
+++ /dev/null
@@ -1,3443 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2010-2011, Kaori Hagihara
- * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
- * Copyright (c) 2012, CS Systemes d'Information, France
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-#include "opj_includes.h"
-
-/** @defgroup JP2 JP2 - JPEG-2000 file format reader/writer */
-/*@{*/
-
-#define OPJ_BOX_SIZE    1024
-
-#define OPJ_UNUSED(x) (void)x
-
-/** @name Local static functions */
-/*@{*/
-
-/*static void jp2_write_url(opj_cio_t *cio, char *Idx_file);*/
-
-/**
- * Reads a IHDR box - Image Header box
- *
- * @param   p_image_header_data         pointer to actual data (already read from file)
- * @param   jp2                         the jpeg2000 file codec.
- * @param   p_image_header_size         the size of the image header
- * @param   p_manager                   the user event manager.
- *
- * @return  true if the image header is valid, false else.
- */
-static OPJ_BOOL opj_jp2_read_ihdr(opj_jp2_t *jp2,
-                                  OPJ_BYTE *p_image_header_data,
-                                  OPJ_UINT32 p_image_header_size,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Writes the Image Header box - Image Header box.
- *
- * @param jp2                   jpeg2000 file codec.
- * @param p_nb_bytes_written    pointer to store the nb of bytes written by the function.
- *
- * @return  the data being copied.
-*/
-static OPJ_BYTE * opj_jp2_write_ihdr(opj_jp2_t *jp2,
-                                     OPJ_UINT32 * p_nb_bytes_written);
-
-/**
- * Writes the Bit per Component box.
- *
- * @param   jp2                     jpeg2000 file codec.
- * @param   p_nb_bytes_written      pointer to store the nb of bytes written by the function.
- *
- * @return  the data being copied.
-*/
-static OPJ_BYTE * opj_jp2_write_bpcc(opj_jp2_t *jp2,
-                                     OPJ_UINT32 * p_nb_bytes_written);
-
-/**
- * Reads a Bit per Component box.
- *
- * @param   p_bpc_header_data           pointer to actual data (already read from file)
- * @param   jp2                         the jpeg2000 file codec.
- * @param   p_bpc_header_size           the size of the bpc header
- * @param   p_manager                   the user event manager.
- *
- * @return  true if the bpc header is valid, false else.
- */
-static OPJ_BOOL opj_jp2_read_bpcc(opj_jp2_t *jp2,
-                                  OPJ_BYTE * p_bpc_header_data,
-                                  OPJ_UINT32 p_bpc_header_size,
-                                  opj_event_mgr_t * p_manager);
-
-static OPJ_BOOL opj_jp2_read_cdef(opj_jp2_t * jp2,
-                                  OPJ_BYTE * p_cdef_header_data,
-                                  OPJ_UINT32 p_cdef_header_size,
-                                  opj_event_mgr_t * p_manager);
-
-static void opj_jp2_apply_cdef(opj_image_t *image, opj_jp2_color_t *color,
-                               opj_event_mgr_t *);
-
-/**
- * Writes the Channel Definition box.
- *
- * @param jp2                   jpeg2000 file codec.
- * @param p_nb_bytes_written    pointer to store the nb of bytes written by the function.
- *
- * @return  the data being copied.
- */
-static OPJ_BYTE * opj_jp2_write_cdef(opj_jp2_t *jp2,
-                                     OPJ_UINT32 * p_nb_bytes_written);
-
-/**
- * Writes the Colour Specification box.
- *
- * @param jp2                   jpeg2000 file codec.
- * @param p_nb_bytes_written    pointer to store the nb of bytes written by the function.
- *
- * @return  the data being copied.
-*/
-static OPJ_BYTE * opj_jp2_write_colr(opj_jp2_t *jp2,
-                                     OPJ_UINT32 * p_nb_bytes_written);
-
-/**
- * Writes a FTYP box - File type box
- *
- * @param   cio         the stream to write data to.
- * @param   jp2         the jpeg2000 file codec.
- * @param   p_manager   the user event manager.
- *
- * @return  true if writing was successful.
- */
-static OPJ_BOOL opj_jp2_write_ftyp(opj_jp2_t *jp2,
-                                   opj_stream_private_t *cio,
-                                   opj_event_mgr_t * p_manager);
-
-/**
- * Reads a a FTYP box - File type box
- *
- * @param   p_header_data   the data contained in the FTYP box.
- * @param   jp2             the jpeg2000 file codec.
- * @param   p_header_size   the size of the data contained in the FTYP box.
- * @param   p_manager       the user event manager.
- *
- * @return true if the FTYP box is valid.
- */
-static OPJ_BOOL opj_jp2_read_ftyp(opj_jp2_t *jp2,
-                                  OPJ_BYTE * p_header_data,
-                                  OPJ_UINT32 p_header_size,
-                                  opj_event_mgr_t * p_manager);
-
-static OPJ_BOOL opj_jp2_skip_jp2c(opj_jp2_t *jp2,
-                                  opj_stream_private_t *stream,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Reads the Jpeg2000 file Header box - JP2 Header box (warning, this is a super box).
- *
- * @param   p_header_data   the data contained in the file header box.
- * @param   jp2             the jpeg2000 file codec.
- * @param   p_header_size   the size of the data contained in the file header box.
- * @param   p_manager       the user event manager.
- *
- * @return true if the JP2 Header box was successfully recognized.
-*/
-static OPJ_BOOL opj_jp2_read_jp2h(opj_jp2_t *jp2,
-                                  OPJ_BYTE *p_header_data,
-                                  OPJ_UINT32 p_header_size,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Writes the Jpeg2000 file Header box - JP2 Header box (warning, this is a super box).
- *
- * @param  jp2      the jpeg2000 file codec.
- * @param  stream      the stream to write data to.
- * @param  p_manager  user event manager.
- *
- * @return true if writing was successful.
- */
-static OPJ_BOOL opj_jp2_write_jp2h(opj_jp2_t *jp2,
-                                   opj_stream_private_t *stream,
-                                   opj_event_mgr_t * p_manager);
-
-/**
- * Writes the Jpeg2000 codestream Header box - JP2C Header box. This function must be called AFTER the coding has been done.
- *
- * @param   cio         the stream to write data to.
- * @param   jp2         the jpeg2000 file codec.
- * @param   p_manager   user event manager.
- *
- * @return true if writing was successful.
-*/
-static OPJ_BOOL opj_jp2_write_jp2c(opj_jp2_t *jp2,
-                                   opj_stream_private_t *cio,
-                                   opj_event_mgr_t * p_manager);
-
-#ifdef USE_JPIP
-/**
- * Write index Finder box
- * @param cio     the stream to write to.
- * @param   jp2         the jpeg2000 file codec.
- * @param   p_manager   user event manager.
-*/
-static OPJ_BOOL opj_jpip_write_iptr(opj_jp2_t *jp2,
-                                    opj_stream_private_t *cio,
-                                    opj_event_mgr_t * p_manager);
-
-/**
- * Write index Finder box
- * @param cio     the stream to write to.
- * @param   jp2         the jpeg2000 file codec.
- * @param   p_manager   user event manager.
- */
-static OPJ_BOOL opj_jpip_write_cidx(opj_jp2_t *jp2,
-                                    opj_stream_private_t *cio,
-                                    opj_event_mgr_t * p_manager);
-
-/**
- * Write file Index (superbox)
- * @param cio     the stream to write to.
- * @param   jp2         the jpeg2000 file codec.
- * @param   p_manager   user event manager.
- */
-static OPJ_BOOL opj_jpip_write_fidx(opj_jp2_t *jp2,
-                                    opj_stream_private_t *cio,
-                                    opj_event_mgr_t * p_manager);
-#endif /* USE_JPIP */
-
-/**
- * Reads a jpeg2000 file signature box.
- *
- * @param   p_header_data   the data contained in the signature box.
- * @param   jp2             the jpeg2000 file codec.
- * @param   p_header_size   the size of the data contained in the signature box.
- * @param   p_manager       the user event manager.
- *
- * @return true if the file signature box is valid.
- */
-static OPJ_BOOL opj_jp2_read_jp(opj_jp2_t *jp2,
-                                OPJ_BYTE * p_header_data,
-                                OPJ_UINT32 p_header_size,
-                                opj_event_mgr_t * p_manager);
-
-/**
- * Writes a jpeg2000 file signature box.
- *
- * @param cio the stream to write data to.
- * @param   jp2         the jpeg2000 file codec.
- * @param p_manager the user event manager.
- *
- * @return true if writing was successful.
- */
-static OPJ_BOOL opj_jp2_write_jp(opj_jp2_t *jp2,
-                                 opj_stream_private_t *cio,
-                                 opj_event_mgr_t * p_manager);
-
-/**
-Apply collected palette data
-@param image Image.
-@param color Collector for profile, cdef and pclr data.
-@param p_manager the user event manager.
-@return true in case of success
-*/
-static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
-                                   opj_jp2_color_t *color,
-                                   opj_event_mgr_t * p_manager);
-
-static void opj_jp2_free_pclr(opj_jp2_color_t *color);
-
-/**
- * Collect palette data
- *
- * @param jp2 JP2 handle
- * @param p_pclr_header_data    FIXME DOC
- * @param p_pclr_header_size    FIXME DOC
- * @param p_manager
- *
- * @return Returns true if successful, returns false otherwise
-*/
-static OPJ_BOOL opj_jp2_read_pclr(opj_jp2_t *jp2,
-                                  OPJ_BYTE * p_pclr_header_data,
-                                  OPJ_UINT32 p_pclr_header_size,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Collect component mapping data
- *
- * @param jp2                 JP2 handle
- * @param p_cmap_header_data  FIXME DOC
- * @param p_cmap_header_size  FIXME DOC
- * @param p_manager           FIXME DOC
- *
- * @return Returns true if successful, returns false otherwise
-*/
-
-static OPJ_BOOL opj_jp2_read_cmap(opj_jp2_t * jp2,
-                                  OPJ_BYTE * p_cmap_header_data,
-                                  OPJ_UINT32 p_cmap_header_size,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Reads the Color Specification box.
- *
- * @param   p_colr_header_data          pointer to actual data (already read from file)
- * @param   jp2                         the jpeg2000 file codec.
- * @param   p_colr_header_size          the size of the color header
- * @param   p_manager                   the user event manager.
- *
- * @return  true if the bpc header is valid, false else.
-*/
-static OPJ_BOOL opj_jp2_read_colr(opj_jp2_t *jp2,
-                                  OPJ_BYTE * p_colr_header_data,
-                                  OPJ_UINT32 p_colr_header_size,
-                                  opj_event_mgr_t * p_manager);
-
-/*@}*/
-
-/*@}*/
-
-/**
- * Sets up the procedures to do on writing header after the codestream.
- * Developpers wanting to extend the library can add their own writing procedures.
- */
-static OPJ_BOOL opj_jp2_setup_end_header_writing(opj_jp2_t *jp2,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Sets up the procedures to do on reading header after the codestream.
- * Developpers wanting to extend the library can add their own writing procedures.
- */
-static OPJ_BOOL opj_jp2_setup_end_header_reading(opj_jp2_t *jp2,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Reads a jpeg2000 file header structure.
- *
- * @param jp2 the jpeg2000 file header structure.
- * @param stream the stream to read data from.
- * @param p_manager the user event manager.
- *
- * @return true if the box is valid.
- */
-static OPJ_BOOL opj_jp2_read_header_procedure(opj_jp2_t *jp2,
-        opj_stream_private_t *stream,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Executes the given procedures on the given codec.
- *
- * @param   p_procedure_list    the list of procedures to execute
- * @param   jp2                 the jpeg2000 file codec to execute the procedures on.
- * @param   stream                  the stream to execute the procedures on.
- * @param   p_manager           the user manager.
- *
- * @return  true                if all the procedures were successfully executed.
- */
-static OPJ_BOOL opj_jp2_exec(opj_jp2_t * jp2,
-                             opj_procedure_list_t * p_procedure_list,
-                             opj_stream_private_t *stream,
-                             opj_event_mgr_t * p_manager);
-
-/**
- * Reads a box header. The box is the way data is packed inside a jpeg2000 file structure.
- *
- * @param   cio                     the input stream to read data from.
- * @param   box                     the box structure to fill.
- * @param   p_number_bytes_read     pointer to an int that will store the number of bytes read from the stream (shoul usually be 2).
- * @param   p_manager               user event manager.
- *
- * @return  true if the box is recognized, false otherwise
-*/
-static OPJ_BOOL opj_jp2_read_boxhdr(opj_jp2_box_t *box,
-                                    OPJ_UINT32 * p_number_bytes_read,
-                                    opj_stream_private_t *cio,
-                                    opj_event_mgr_t * p_manager);
-
-/**
- * Sets up the validation ,i.e. adds the procedures to launch to make sure the codec parameters
- * are valid. Developpers wanting to extend the library can add their own validation procedures.
- */
-static OPJ_BOOL opj_jp2_setup_encoding_validation(opj_jp2_t *jp2,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Sets up the procedures to do on writing header. Developpers wanting to extend the library can add their own writing procedures.
- */
-static OPJ_BOOL opj_jp2_setup_header_writing(opj_jp2_t *jp2,
-        opj_event_mgr_t * p_manager);
-
-static OPJ_BOOL opj_jp2_default_validation(opj_jp2_t * jp2,
-        opj_stream_private_t *cio,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Finds the image execution function related to the given box id.
- *
- * @param   p_id    the id of the handler to fetch.
- *
- * @return  the given handler or NULL if it could not be found.
- */
-static const opj_jp2_header_handler_t * opj_jp2_img_find_handler(
-    OPJ_UINT32 p_id);
-
-/**
- * Finds the execution function related to the given box id.
- *
- * @param   p_id    the id of the handler to fetch.
- *
- * @return  the given handler or NULL if it could not be found.
- */
-static const opj_jp2_header_handler_t * opj_jp2_find_handler(OPJ_UINT32 p_id);
-
-static const opj_jp2_header_handler_t jp2_header [] = {
-    {JP2_JP, opj_jp2_read_jp},
-    {JP2_FTYP, opj_jp2_read_ftyp},
-    {JP2_JP2H, opj_jp2_read_jp2h}
-};
-
-static const opj_jp2_header_handler_t jp2_img_header [] = {
-    {JP2_IHDR, opj_jp2_read_ihdr},
-    {JP2_COLR, opj_jp2_read_colr},
-    {JP2_BPCC, opj_jp2_read_bpcc},
-    {JP2_PCLR, opj_jp2_read_pclr},
-    {JP2_CMAP, opj_jp2_read_cmap},
-    {JP2_CDEF, opj_jp2_read_cdef}
-
-};
-
-/**
- * Reads a box header. The box is the way data is packed inside a jpeg2000 file structure. Data is read from a character string
- *
- * @param   box                     the box structure to fill.
- * @param   p_data                  the character string to read data from.
- * @param   p_number_bytes_read     pointer to an int that will store the number of bytes read from the stream (shoul usually be 2).
- * @param   p_box_max_size          the maximum number of bytes in the box.
- * @param   p_manager         FIXME DOC
- *
- * @return  true if the box is recognized, false otherwise
-*/
-static OPJ_BOOL opj_jp2_read_boxhdr_char(opj_jp2_box_t *box,
-        OPJ_BYTE * p_data,
-        OPJ_UINT32 * p_number_bytes_read,
-        OPJ_UINT32 p_box_max_size,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Sets up the validation ,i.e. adds the procedures to launch to make sure the codec parameters
- * are valid. Developpers wanting to extend the library can add their own validation procedures.
- */
-static OPJ_BOOL opj_jp2_setup_decoding_validation(opj_jp2_t *jp2,
-        opj_event_mgr_t * p_manager);
-
-/**
- * Sets up the procedures to do on reading header.
- * Developpers wanting to extend the library can add their own writing procedures.
- */
-static OPJ_BOOL opj_jp2_setup_header_reading(opj_jp2_t *jp2,
-        opj_event_mgr_t * p_manager);
-
-/* ----------------------------------------------------------------------- */
-static OPJ_BOOL opj_jp2_read_boxhdr(opj_jp2_box_t *box,
-                                    OPJ_UINT32 * p_number_bytes_read,
-                                    opj_stream_private_t *cio,
-                                    opj_event_mgr_t * p_manager)
-{
-    /* read header from file */
-    OPJ_BYTE l_data_header [8];
-
-    /* preconditions */
-    assert(cio != 00);
-    assert(box != 00);
-    assert(p_number_bytes_read != 00);
-    assert(p_manager != 00);
-
-    *p_number_bytes_read = (OPJ_UINT32)opj_stream_read_data(cio, l_data_header, 8,
-                           p_manager);
-    if (*p_number_bytes_read != 8) {
-        return OPJ_FALSE;
-    }
-
-    /* process read data */
-    opj_read_bytes(l_data_header, &(box->length), 4);
-    opj_read_bytes(l_data_header + 4, &(box->type), 4);
-
-    if (box->length == 0) { /* last box */
-        const OPJ_OFF_T bleft = opj_stream_get_number_byte_left(cio);
-        if (bleft > (OPJ_OFF_T)(0xFFFFFFFFU - 8U)) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Cannot handle box sizes higher than 2^32\n");
-            return OPJ_FALSE;
-        }
-        box->length = (OPJ_UINT32)bleft + 8U;
-        assert((OPJ_OFF_T)box->length == bleft + 8);
-        return OPJ_TRUE;
-    }
-
-    /* do we have a "special very large box ?" */
-    /* read then the XLBox */
-    if (box->length == 1) {
-        OPJ_UINT32 l_xl_part_size;
-
-        OPJ_UINT32 l_nb_bytes_read = (OPJ_UINT32)opj_stream_read_data(cio,
-                                     l_data_header, 8, p_manager);
-        if (l_nb_bytes_read != 8) {
-            if (l_nb_bytes_read > 0) {
-                *p_number_bytes_read += l_nb_bytes_read;
-            }
-
-            return OPJ_FALSE;
-        }
-
-        *p_number_bytes_read = 16;
-        opj_read_bytes(l_data_header, &l_xl_part_size, 4);
-        if (l_xl_part_size != 0) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Cannot handle box sizes higher than 2^32\n");
-            return OPJ_FALSE;
-        }
-        opj_read_bytes(l_data_header + 4, &(box->length), 4);
-    }
-    return OPJ_TRUE;
-}
-
-#if 0
-static void jp2_write_url(opj_cio_t *cio, char *Idx_file)
-{
-    OPJ_UINT32 i;
-    opj_jp2_box_t box;
-
-    box.init_pos = cio_tell(cio);
-    cio_skip(cio, 4);
-    cio_write(cio, JP2_URL, 4); /* DBTL */
-    cio_write(cio, 0, 1);       /* VERS */
-    cio_write(cio, 0, 3);       /* FLAG */
-
-    if (Idx_file) {
-        for (i = 0; i < strlen(Idx_file); i++) {
-            cio_write(cio, Idx_file[i], 1);
-        }
-    }
-
-    box.length = cio_tell(cio) - box.init_pos;
-    cio_seek(cio, box.init_pos);
-    cio_write(cio, box.length, 4);  /* L */
-    cio_seek(cio, box.init_pos + box.length);
-}
-#endif
-
-static OPJ_BOOL opj_jp2_read_ihdr(opj_jp2_t *jp2,
-                                  OPJ_BYTE *p_image_header_data,
-                                  OPJ_UINT32 p_image_header_size,
-                                  opj_event_mgr_t * p_manager)
-{
-    /* preconditions */
-    assert(p_image_header_data != 00);
-    assert(jp2 != 00);
-    assert(p_manager != 00);
-
-    if (jp2->comps != NULL) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "Ignoring ihdr box. First ihdr box already read\n");
-        return OPJ_TRUE;
-    }
-
-    if (p_image_header_size != 14) {
-        opj_event_msg(p_manager, EVT_ERROR, "Bad image header box (bad size)\n");
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(p_image_header_data, &(jp2->h), 4);          /* HEIGHT */
-    p_image_header_data += 4;
-    opj_read_bytes(p_image_header_data, &(jp2->w), 4);          /* WIDTH */
-    p_image_header_data += 4;
-    opj_read_bytes(p_image_header_data, &(jp2->numcomps), 2);   /* NC */
-    p_image_header_data += 2;
-
-    if ((jp2->numcomps - 1U) >=
-            16384U) { /* unsigned underflow is well defined: 1U <= jp2->numcomps <= 16384U */
-        opj_event_msg(p_manager, EVT_ERROR, "Invalid number of components (ihdr)\n");
-        return OPJ_FALSE;
-    }
-
-    /* allocate memory for components */
-    opj_free(jp2->comps);
-    jp2->comps = (opj_jp2_comps_t*) opj_calloc(jp2->numcomps,
-                 sizeof(opj_jp2_comps_t));
-    if (jp2->comps == 0) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Not enough memory to handle image header (ihdr)\n");
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(p_image_header_data, &(jp2->bpc), 1);        /* BPC */
-    ++ p_image_header_data;
-
-    opj_read_bytes(p_image_header_data, &(jp2->C), 1);          /* C */
-    ++ p_image_header_data;
-
-    /* Should be equal to 7 cf. chapter about image header box of the norm */
-    if (jp2->C != 7) {
-        opj_event_msg(p_manager, EVT_INFO,
-                      "JP2 IHDR box: compression type indicate that the file is not a conforming JP2 file (%d) \n",
-                      jp2->C);
-    }
-
-    opj_read_bytes(p_image_header_data, &(jp2->UnkC), 1);       /* UnkC */
-    ++ p_image_header_data;
-    opj_read_bytes(p_image_header_data, &(jp2->IPR), 1);        /* IPR */
-    ++ p_image_header_data;
-
-    jp2->j2k->m_cp.allow_different_bit_depth_sign = (jp2->bpc == 255);
-    jp2->j2k->ihdr_w = jp2->w;
-    jp2->j2k->ihdr_h = jp2->h;
-    jp2->has_ihdr = 1;
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BYTE * opj_jp2_write_ihdr(opj_jp2_t *jp2,
-                                     OPJ_UINT32 * p_nb_bytes_written
-                                    )
-{
-    OPJ_BYTE * l_ihdr_data, * l_current_ihdr_ptr;
-
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(p_nb_bytes_written != 00);
-
-    /* default image header is 22 bytes wide */
-    l_ihdr_data = (OPJ_BYTE *) opj_calloc(1, 22);
-    if (l_ihdr_data == 00) {
-        return 00;
-    }
-
-    l_current_ihdr_ptr = l_ihdr_data;
-
-    opj_write_bytes(l_current_ihdr_ptr, 22, 4);             /* write box size */
-    l_current_ihdr_ptr += 4;
-
-    opj_write_bytes(l_current_ihdr_ptr, JP2_IHDR, 4);       /* IHDR */
-    l_current_ihdr_ptr += 4;
-
-    opj_write_bytes(l_current_ihdr_ptr, jp2->h, 4);     /* HEIGHT */
-    l_current_ihdr_ptr += 4;
-
-    opj_write_bytes(l_current_ihdr_ptr, jp2->w, 4);     /* WIDTH */
-    l_current_ihdr_ptr += 4;
-
-    opj_write_bytes(l_current_ihdr_ptr, jp2->numcomps, 2);      /* NC */
-    l_current_ihdr_ptr += 2;
-
-    opj_write_bytes(l_current_ihdr_ptr, jp2->bpc, 1);       /* BPC */
-    ++l_current_ihdr_ptr;
-
-    opj_write_bytes(l_current_ihdr_ptr, jp2->C, 1);     /* C : Always 7 */
-    ++l_current_ihdr_ptr;
-
-    opj_write_bytes(l_current_ihdr_ptr, jp2->UnkC,
-                    1);      /* UnkC, colorspace unknown */
-    ++l_current_ihdr_ptr;
-
-    opj_write_bytes(l_current_ihdr_ptr, jp2->IPR,
-                    1);       /* IPR, no intellectual property */
-    ++l_current_ihdr_ptr;
-
-    *p_nb_bytes_written = 22;
-
-    return l_ihdr_data;
-}
-
-static OPJ_BYTE * opj_jp2_write_bpcc(opj_jp2_t *jp2,
-                                     OPJ_UINT32 * p_nb_bytes_written
-                                    )
-{
-    OPJ_UINT32 i;
-    /* room for 8 bytes for box and 1 byte for each component */
-    OPJ_UINT32 l_bpcc_size;
-    OPJ_BYTE * l_bpcc_data, * l_current_bpcc_ptr;
-
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(p_nb_bytes_written != 00);
-    l_bpcc_size = 8 + jp2->numcomps;
-
-    l_bpcc_data = (OPJ_BYTE *) opj_calloc(1, l_bpcc_size);
-    if (l_bpcc_data == 00) {
-        return 00;
-    }
-
-    l_current_bpcc_ptr = l_bpcc_data;
-
-    opj_write_bytes(l_current_bpcc_ptr, l_bpcc_size,
-                    4);            /* write box size */
-    l_current_bpcc_ptr += 4;
-
-    opj_write_bytes(l_current_bpcc_ptr, JP2_BPCC, 4);               /* BPCC */
-    l_current_bpcc_ptr += 4;
-
-    for (i = 0; i < jp2->numcomps; ++i)  {
-        opj_write_bytes(l_current_bpcc_ptr, jp2->comps[i].bpcc,
-                        1); /* write each component information */
-        ++l_current_bpcc_ptr;
-    }
-
-    *p_nb_bytes_written = l_bpcc_size;
-
-    return l_bpcc_data;
-}
-
-static OPJ_BOOL opj_jp2_read_bpcc(opj_jp2_t *jp2,
-                                  OPJ_BYTE * p_bpc_header_data,
-                                  OPJ_UINT32 p_bpc_header_size,
-                                  opj_event_mgr_t * p_manager
-                                 )
-{
-    OPJ_UINT32 i;
-
-    /* preconditions */
-    assert(p_bpc_header_data != 00);
-    assert(jp2 != 00);
-    assert(p_manager != 00);
-
-
-    if (jp2->bpc != 255) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "A BPCC header box is available although BPC given by the IHDR box (%d) indicate components bit depth is constant\n",
-                      jp2->bpc);
-    }
-
-    /* and length is relevant */
-    if (p_bpc_header_size != jp2->numcomps) {
-        opj_event_msg(p_manager, EVT_ERROR, "Bad BPCC header box (bad size)\n");
-        return OPJ_FALSE;
-    }
-
-    /* read info for each component */
-    for (i = 0; i < jp2->numcomps; ++i) {
-        opj_read_bytes(p_bpc_header_data, &jp2->comps[i].bpcc,
-                       1);  /* read each BPCC component */
-        ++p_bpc_header_data;
-    }
-
-    return OPJ_TRUE;
-}
-static OPJ_BYTE * opj_jp2_write_cdef(opj_jp2_t *jp2,
-                                     OPJ_UINT32 * p_nb_bytes_written)
-{
-    /* room for 8 bytes for box, 2 for n */
-    OPJ_UINT32 l_cdef_size = 10;
-    OPJ_BYTE * l_cdef_data, * l_current_cdef_ptr;
-    OPJ_UINT32 l_value;
-    OPJ_UINT16 i;
-
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(p_nb_bytes_written != 00);
-    assert(jp2->color.jp2_cdef != 00);
-    assert(jp2->color.jp2_cdef->info != 00);
-    assert(jp2->color.jp2_cdef->n > 0U);
-
-    l_cdef_size += 6U * jp2->color.jp2_cdef->n;
-
-    l_cdef_data = (OPJ_BYTE *) opj_malloc(l_cdef_size);
-    if (l_cdef_data == 00) {
-        return 00;
-    }
-
-    l_current_cdef_ptr = l_cdef_data;
-
-    opj_write_bytes(l_current_cdef_ptr, l_cdef_size, 4);        /* write box size */
-    l_current_cdef_ptr += 4;
-
-    opj_write_bytes(l_current_cdef_ptr, JP2_CDEF, 4);               /* BPCC */
-    l_current_cdef_ptr += 4;
-
-    l_value = jp2->color.jp2_cdef->n;
-    opj_write_bytes(l_current_cdef_ptr, l_value, 2);                /* N */
-    l_current_cdef_ptr += 2;
-
-    for (i = 0U; i < jp2->color.jp2_cdef->n; ++i) {
-        l_value = jp2->color.jp2_cdef->info[i].cn;
-        opj_write_bytes(l_current_cdef_ptr, l_value, 2);                /* Cni */
-        l_current_cdef_ptr += 2;
-        l_value = jp2->color.jp2_cdef->info[i].typ;
-        opj_write_bytes(l_current_cdef_ptr, l_value, 2);                /* Typi */
-        l_current_cdef_ptr += 2;
-        l_value = jp2->color.jp2_cdef->info[i].asoc;
-        opj_write_bytes(l_current_cdef_ptr, l_value, 2);                /* Asoci */
-        l_current_cdef_ptr += 2;
-    }
-    *p_nb_bytes_written = l_cdef_size;
-
-    return l_cdef_data;
-}
-
-static OPJ_BYTE * opj_jp2_write_colr(opj_jp2_t *jp2,
-                                     OPJ_UINT32 * p_nb_bytes_written
-                                    )
-{
-    /* room for 8 bytes for box 3 for common data and variable upon profile*/
-    OPJ_UINT32 l_colr_size = 11;
-    OPJ_BYTE * l_colr_data, * l_current_colr_ptr;
-
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(p_nb_bytes_written != 00);
-    assert(jp2->meth == 1 || jp2->meth == 2);
-
-    switch (jp2->meth) {
-    case 1 :
-        l_colr_size += 4; /* EnumCS */
-        break;
-    case 2 :
-        assert(jp2->color.icc_profile_len); /* ICC profile */
-        l_colr_size += jp2->color.icc_profile_len;
-        break;
-    default :
-        return 00;
-    }
-
-    l_colr_data = (OPJ_BYTE *) opj_calloc(1, l_colr_size);
-    if (l_colr_data == 00) {
-        return 00;
-    }
-
-    l_current_colr_ptr = l_colr_data;
-
-    opj_write_bytes(l_current_colr_ptr, l_colr_size,
-                    4);            /* write box size */
-    l_current_colr_ptr += 4;
-
-    opj_write_bytes(l_current_colr_ptr, JP2_COLR, 4);               /* BPCC */
-    l_current_colr_ptr += 4;
-
-    opj_write_bytes(l_current_colr_ptr, jp2->meth, 1);              /* METH */
-    ++l_current_colr_ptr;
-
-    opj_write_bytes(l_current_colr_ptr, jp2->precedence, 1);        /* PRECEDENCE */
-    ++l_current_colr_ptr;
-
-    opj_write_bytes(l_current_colr_ptr, jp2->approx, 1);            /* APPROX */
-    ++l_current_colr_ptr;
-
-    if (jp2->meth ==
-            1) { /* Meth value is restricted to 1 or 2 (Table I.9 of part 1) */
-        opj_write_bytes(l_current_colr_ptr, jp2->enumcs, 4);
-    }       /* EnumCS */
-    else {
-        if (jp2->meth == 2) {                                      /* ICC profile */
-            OPJ_UINT32 i;
-            for (i = 0; i < jp2->color.icc_profile_len; ++i) {
-                opj_write_bytes(l_current_colr_ptr, jp2->color.icc_profile_buf[i], 1);
-                ++l_current_colr_ptr;
-            }
-        }
-    }
-
-    *p_nb_bytes_written = l_colr_size;
-
-    return l_colr_data;
-}
-
-static void opj_jp2_free_pclr(opj_jp2_color_t *color)
-{
-    opj_free(color->jp2_pclr->channel_sign);
-    opj_free(color->jp2_pclr->channel_size);
-    opj_free(color->jp2_pclr->entries);
-
-    if (color->jp2_pclr->cmap) {
-        opj_free(color->jp2_pclr->cmap);
-    }
-
-    opj_free(color->jp2_pclr);
-    color->jp2_pclr = NULL;
-}
-
-static OPJ_BOOL opj_jp2_check_color(opj_image_t *image, opj_jp2_color_t *color,
-                                    opj_event_mgr_t *p_manager)
-{
-    OPJ_UINT16 i;
-
-    /* testcase 4149.pdf.SIGSEGV.cf7.3501 */
-    if (color->jp2_cdef) {
-        opj_jp2_cdef_info_t *info = color->jp2_cdef->info;
-        OPJ_UINT16 n = color->jp2_cdef->n;
-        OPJ_UINT32 nr_channels =
-            image->numcomps; /* FIXME image->numcomps == jp2->numcomps before color is applied ??? */
-
-        /* cdef applies to cmap channels if any */
-        if (color->jp2_pclr && color->jp2_pclr->cmap) {
-            nr_channels = (OPJ_UINT32)color->jp2_pclr->nr_channels;
-        }
-
-        for (i = 0; i < n; i++) {
-            if (info[i].cn >= nr_channels) {
-                opj_event_msg(p_manager, EVT_ERROR, "Invalid component index %d (>= %d).\n",
-                              info[i].cn, nr_channels);
-                return OPJ_FALSE;
-            }
-            if (info[i].asoc == 65535U) {
-                continue;
-            }
-
-            if (info[i].asoc > 0 && (OPJ_UINT32)(info[i].asoc - 1) >= nr_channels) {
-                opj_event_msg(p_manager, EVT_ERROR, "Invalid component index %d (>= %d).\n",
-                              info[i].asoc - 1, nr_channels);
-                return OPJ_FALSE;
-            }
-        }
-
-        /* issue 397 */
-        /* ISO 15444-1 states that if cdef is present, it shall contain a complete list of channel definitions. */
-        while (nr_channels > 0) {
-            for (i = 0; i < n; ++i) {
-                if ((OPJ_UINT32)info[i].cn == (nr_channels - 1U)) {
-                    break;
-                }
-            }
-            if (i == n) {
-                opj_event_msg(p_manager, EVT_ERROR, "Incomplete channel definitions.\n");
-                return OPJ_FALSE;
-            }
-            --nr_channels;
-        }
-    }
-
-    /* testcases 451.pdf.SIGSEGV.f4c.3723, 451.pdf.SIGSEGV.5b5.3723 and
-       66ea31acbb0f23a2bbc91f64d69a03f5_signal_sigsegv_13937c0_7030_5725.pdf */
-    if (color->jp2_pclr && color->jp2_pclr->cmap) {
-        OPJ_UINT16 nr_channels = color->jp2_pclr->nr_channels;
-        opj_jp2_cmap_comp_t *cmap = color->jp2_pclr->cmap;
-        OPJ_BOOL *pcol_usage, is_sane = OPJ_TRUE;
-
-        /* verify that all original components match an existing one */
-        for (i = 0; i < nr_channels; i++) {
-            if (cmap[i].cmp >= image->numcomps) {
-                opj_event_msg(p_manager, EVT_ERROR, "Invalid component index %d (>= %d).\n",
-                              cmap[i].cmp, image->numcomps);
-                is_sane = OPJ_FALSE;
-            }
-        }
-
-        pcol_usage = (OPJ_BOOL *) opj_calloc(nr_channels, sizeof(OPJ_BOOL));
-        if (!pcol_usage) {
-            opj_event_msg(p_manager, EVT_ERROR, "Unexpected OOM.\n");
-            return OPJ_FALSE;
-        }
-        /* verify that no component is targeted more than once */
-        for (i = 0; i < nr_channels; i++) {
-            OPJ_BYTE mtyp = cmap[i].mtyp;
-            OPJ_BYTE pcol = cmap[i].pcol;
-            /* See ISO 15444-1 Table I.14 – MTYPi field values */
-            if (mtyp != 0 && mtyp != 1) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Invalid value for cmap[%d].mtyp = %d.\n", i,
-                              mtyp);
-                is_sane = OPJ_FALSE;
-            } else if (pcol >= nr_channels) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Invalid component/palette index for direct mapping %d.\n", pcol);
-                is_sane = OPJ_FALSE;
-            } else if (pcol_usage[pcol] && mtyp == 1) {
-                opj_event_msg(p_manager, EVT_ERROR, "Component %d is mapped twice.\n", pcol);
-                is_sane = OPJ_FALSE;
-            } else if (mtyp == 0 && pcol != 0) {
-                /* I.5.3.5 PCOL: If the value of the MTYP field for this channel is 0, then
-                 * the value of this field shall be 0. */
-                opj_event_msg(p_manager, EVT_ERROR, "Direct use at #%d however pcol=%d.\n", i,
-                              pcol);
-                is_sane = OPJ_FALSE;
-            } else if (mtyp == 1 && pcol != i) {
-                /* OpenJPEG implementation limitation. See assert(i == pcol); */
-                /* in opj_jp2_apply_pclr() */
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Implementation limitation: for palette mapping, "
-                              "pcol[%d] should be equal to %d, but is equal "
-                              "to %d.\n", i, i, pcol);
-                is_sane = OPJ_FALSE;
-            } else {
-                pcol_usage[pcol] = OPJ_TRUE;
-            }
-        }
-        /* verify that all components are targeted at least once */
-        for (i = 0; i < nr_channels; i++) {
-            if (!pcol_usage[i] && cmap[i].mtyp != 0) {
-                opj_event_msg(p_manager, EVT_ERROR, "Component %d doesn't have a mapping.\n",
-                              i);
-                is_sane = OPJ_FALSE;
-            }
-        }
-        /* Issue 235/447 weird cmap */
-        if (1 && is_sane && (image->numcomps == 1U)) {
-            for (i = 0; i < nr_channels; i++) {
-                if (!pcol_usage[i]) {
-                    is_sane = 0U;
-                    opj_event_msg(p_manager, EVT_WARNING,
-                                  "Component mapping seems wrong. Trying to correct.\n");
-                    break;
-                }
-            }
-            if (!is_sane) {
-                is_sane = OPJ_TRUE;
-                for (i = 0; i < nr_channels; i++) {
-                    cmap[i].mtyp = 1U;
-                    cmap[i].pcol = (OPJ_BYTE) i;
-                }
-            }
-        }
-        opj_free(pcol_usage);
-        if (!is_sane) {
-            return OPJ_FALSE;
-        }
-    }
-
-    return OPJ_TRUE;
-}
-
-/* file9.jp2 */
-static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
-                                   opj_jp2_color_t *color,
-                                   opj_event_mgr_t * p_manager)
-{
-    opj_image_comp_t *old_comps, *new_comps;
-    OPJ_BYTE *channel_size, *channel_sign;
-    OPJ_UINT32 *entries;
-    opj_jp2_cmap_comp_t *cmap;
-    OPJ_INT32 *src, *dst;
-    OPJ_UINT32 j, max;
-    OPJ_UINT16 i, nr_channels, cmp, pcol;
-    OPJ_INT32 k, top_k;
-
-    channel_size = color->jp2_pclr->channel_size;
-    channel_sign = color->jp2_pclr->channel_sign;
-    entries = color->jp2_pclr->entries;
-    cmap = color->jp2_pclr->cmap;
-    nr_channels = color->jp2_pclr->nr_channels;
-
-    for (i = 0; i < nr_channels; ++i) {
-        /* Palette mapping: */
-        cmp = cmap[i].cmp;
-        if (image->comps[cmp].data == NULL) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "image->comps[%d].data == NULL in opj_jp2_apply_pclr().\n", i);
-            return OPJ_FALSE;
-        }
-    }
-
-    old_comps = image->comps;
-    /* Overflow check: prevent integer overflow */
-    for (i = 0; i < nr_channels; ++i) {
-      cmp = cmap[i].cmp;
-      if (old_comps[cmp].h == 0 || old_comps[cmp].w > ((OPJ_UINT32)-1) / sizeof(OPJ_INT32) / old_comps[cmp].h) {
-        return OPJ_FALSE;
-      }
-    }
-
-    new_comps = (opj_image_comp_t*)
-                opj_malloc(nr_channels * sizeof(opj_image_comp_t));
-    if (!new_comps) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Memory allocation failure in opj_jp2_apply_pclr().\n");
-        return OPJ_FALSE;
-    }
-    for (i = 0; i < nr_channels; ++i) {
-        pcol = cmap[i].pcol;
-        cmp = cmap[i].cmp;
-
-        /* Direct use */
-        if (cmap[i].mtyp == 0) {
-            assert(pcol == 0);
-            new_comps[i] = old_comps[cmp];
-        } else {
-            assert( i == pcol ); // probably wrong?
-            new_comps[i] = old_comps[cmp];
-        }
-
-        /* Palette mapping: */
-        new_comps[i].data = (OPJ_INT32*)
-                            opj_image_data_alloc(sizeof(OPJ_INT32) * old_comps[cmp].w * old_comps[cmp].h);
-        if (!new_comps[i].data) {
-            while (i > 0) {
-                -- i;
-                opj_image_data_free(new_comps[i].data);
-            }
-            opj_free(new_comps);
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Memory allocation failure in opj_jp2_apply_pclr().\n");
-            return OPJ_FALSE;
-        }
-        new_comps[i].prec = channel_size[i];
-        new_comps[i].sgnd = channel_sign[i];
-    }
-
-    top_k = color->jp2_pclr->nr_entries - 1;
-
-    for (i = 0; i < nr_channels; ++i) {
-        /* Palette mapping: */
-        cmp = cmap[i].cmp;
-        pcol = cmap[i].pcol;
-        src = old_comps[cmp].data;
-        dst = new_comps[i].data;
-        max = new_comps[i].w * new_comps[i].h;
-
-        /* Prevent null pointer access */
-        if (!src || !dst) {
-          for (j = 0; j < nr_channels; ++j) {
-            opj_image_data_free(new_comps[j].data);
-          }
-          opj_free(new_comps);
-          new_comps = NULL;
-          return OPJ_FALSE;
-        }
-
-        /* Direct use: */
-        if (cmap[i].mtyp == 0) {
-            for (j = 0; j < max; ++j) {
-                dst[j] = src[j];
-            }
-        } else {
-            assert( i == pcol ); // probably wrong?
-            for (j = 0; j < max; ++j) {
-                /* The index */
-                if ((k = src[j]) < 0) {
-                    k = 0;
-                } else if (k > top_k) {
-                    k = top_k;
-                }
-
-                /* The colour */
-                dst[j] = (OPJ_INT32)entries[k * nr_channels + pcol];
-            }
-        }
-    }
-
-    max = image->numcomps;
-    for (i = 0; i < max; ++i) {
-        if (old_comps[i].data) {
-            opj_image_data_free(old_comps[i].data);
-        }
-    }
-
-    opj_free(old_comps);
-    image->comps = new_comps;
-    image->numcomps = nr_channels;
-
-    return OPJ_TRUE;
-}/* apply_pclr() */
-
-static OPJ_BOOL opj_jp2_read_pclr(opj_jp2_t *jp2,
-                                  OPJ_BYTE * p_pclr_header_data,
-                                  OPJ_UINT32 p_pclr_header_size,
-                                  opj_event_mgr_t * p_manager
-                                 )
-{
-    opj_jp2_pclr_t *jp2_pclr;
-    OPJ_BYTE *channel_size, *channel_sign;
-    OPJ_UINT32 *entries;
-    OPJ_UINT16 nr_entries, nr_channels;
-    OPJ_UINT16 i, j;
-    OPJ_UINT32 l_value;
-    OPJ_BYTE *orig_header_data = p_pclr_header_data;
-
-    /* preconditions */
-    assert(p_pclr_header_data != 00);
-    assert(jp2 != 00);
-    assert(p_manager != 00);
-    (void)p_pclr_header_size;
-
-    if (jp2->color.jp2_pclr) {
-        return OPJ_FALSE;
-    }
-
-    if (p_pclr_header_size < 3) {
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(p_pclr_header_data, &l_value, 2);    /* NE */
-    p_pclr_header_data += 2;
-    nr_entries = (OPJ_UINT16) l_value;
-    if ((nr_entries == 0U) || (nr_entries > 1024U)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Invalid PCLR box. Reports %d entries\n",
-                      (int)nr_entries);
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(p_pclr_header_data, &l_value, 1);    /* NPC */
-    ++p_pclr_header_data;
-    nr_channels = (OPJ_UINT16) l_value;
-    if (nr_channels == 0U) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Invalid PCLR box. Reports 0 palette columns\n");
-        return OPJ_FALSE;
-    }
-
-    if (p_pclr_header_size < 3 + (OPJ_UINT32)nr_channels) {
-        return OPJ_FALSE;
-    }
-
-    entries = (OPJ_UINT32*) opj_malloc(sizeof(OPJ_UINT32) * nr_channels *
-                                       nr_entries);
-    if (!entries) {
-        return OPJ_FALSE;
-    }
-    channel_size = (OPJ_BYTE*) opj_malloc(nr_channels);
-    if (!channel_size) {
-        opj_free(entries);
-        return OPJ_FALSE;
-    }
-    channel_sign = (OPJ_BYTE*) opj_malloc(nr_channels);
-    if (!channel_sign) {
-        opj_free(entries);
-        opj_free(channel_size);
-        return OPJ_FALSE;
-    }
-
-    jp2_pclr = (opj_jp2_pclr_t*)opj_malloc(sizeof(opj_jp2_pclr_t));
-    if (!jp2_pclr) {
-        opj_free(entries);
-        opj_free(channel_size);
-        opj_free(channel_sign);
-        return OPJ_FALSE;
-    }
-
-    jp2_pclr->channel_sign = channel_sign;
-    jp2_pclr->channel_size = channel_size;
-    jp2_pclr->entries = entries;
-    jp2_pclr->nr_entries = nr_entries;
-    jp2_pclr->nr_channels = (OPJ_BYTE) l_value;
-    jp2_pclr->cmap = NULL;
-
-    jp2->color.jp2_pclr = jp2_pclr;
-
-    for (i = 0; i < nr_channels; ++i) {
-        opj_read_bytes(p_pclr_header_data, &l_value, 1);    /* Bi */
-        ++p_pclr_header_data;
-
-        channel_size[i] = (OPJ_BYTE)((l_value & 0x7f) + 1);
-        channel_sign[i] = (l_value & 0x80) ? 1 : 0;
-    }
-
-    for (j = 0; j < nr_entries; ++j) {
-        for (i = 0; i < nr_channels; ++i) {
-            OPJ_UINT32 bytes_to_read = (OPJ_UINT32)((channel_size[i] + 7) >> 3);
-
-            if (bytes_to_read > sizeof(OPJ_UINT32)) {
-                bytes_to_read = sizeof(OPJ_UINT32);
-            }
-            if ((ptrdiff_t)p_pclr_header_size < (ptrdiff_t)(p_pclr_header_data -
-                    orig_header_data) + (ptrdiff_t)bytes_to_read) {
-                return OPJ_FALSE;
-            }
-
-            opj_read_bytes(p_pclr_header_data, &l_value, bytes_to_read);    /* Cji */
-            p_pclr_header_data += bytes_to_read;
-            *entries = (OPJ_UINT32) l_value;
-            entries++;
-        }
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_jp2_read_cmap(opj_jp2_t * jp2,
-                                  OPJ_BYTE * p_cmap_header_data,
-                                  OPJ_UINT32 p_cmap_header_size,
-                                  opj_event_mgr_t * p_manager
-                                 )
-{
-    opj_jp2_cmap_comp_t *cmap;
-    OPJ_BYTE i, nr_channels;
-    OPJ_UINT32 l_value;
-
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(p_cmap_header_data != 00);
-    assert(p_manager != 00);
-    (void)p_cmap_header_size;
-
-    /* Need nr_channels: */
-    if (jp2->color.jp2_pclr == NULL) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Need to read a PCLR box before the CMAP box.\n");
-        return OPJ_FALSE;
-    }
-
-    /* Part 1, I.5.3.5: 'There shall be at most one Component Mapping box
-     * inside a JP2 Header box' :
-    */
-    if (jp2->color.jp2_pclr->cmap) {
-        opj_event_msg(p_manager, EVT_ERROR, "Only one CMAP box is allowed.\n");
-        return OPJ_FALSE;
-    }
-
-    nr_channels = jp2->color.jp2_pclr->nr_channels;
-    if (p_cmap_header_size < (OPJ_UINT32)nr_channels * 4) {
-        opj_event_msg(p_manager, EVT_ERROR, "Insufficient data for CMAP box.\n");
-        return OPJ_FALSE;
-    }
-
-    cmap = (opj_jp2_cmap_comp_t*) opj_malloc(nr_channels * sizeof(
-                opj_jp2_cmap_comp_t));
-    if (!cmap) {
-        return OPJ_FALSE;
-    }
-
-
-    for (i = 0; i < nr_channels; ++i) {
-        opj_read_bytes_BE(p_cmap_header_data, &l_value, 2);     /* CMP^i */
-        p_cmap_header_data += 2;
-        cmap[i].cmp = (OPJ_UINT16) l_value;
-
-        opj_read_bytes(p_cmap_header_data, &l_value, 1);            /* MTYP^i */
-        ++p_cmap_header_data;
-        cmap[i].mtyp = (OPJ_BYTE) l_value;
-
-        opj_read_bytes(p_cmap_header_data, &l_value, 1);            /* PCOL^i */
-        ++p_cmap_header_data;
-        cmap[i].pcol = (OPJ_BYTE) l_value;
-    }
-
-    jp2->color.jp2_pclr->cmap = cmap;
-
-    return OPJ_TRUE;
-}
-
-static void opj_jp2_apply_cdef(opj_image_t *image, opj_jp2_color_t *color,
-                               opj_event_mgr_t *manager)
-{
-    opj_jp2_cdef_info_t *info;
-    OPJ_UINT16 i, n, cn, asoc, acn;
-
-    info = color->jp2_cdef->info;
-    n = color->jp2_cdef->n;
-
-    for (i = 0; i < n; ++i) {
-        /* WATCH: acn = asoc - 1 ! */
-        asoc = info[i].asoc;
-        cn = info[i].cn;
-
-        if (cn >= image->numcomps) {
-            opj_event_msg(manager, EVT_WARNING, "opj_jp2_apply_cdef: cn=%d, numcomps=%d\n",
-                          cn, image->numcomps);
-            continue;
-        }
-        if (asoc == 0 || asoc == 65535) {
-            image->comps[cn].alpha = info[i].typ;
-            continue;
-        }
-
-        acn = (OPJ_UINT16)(asoc - 1);
-        if (acn >= image->numcomps) {
-            opj_event_msg(manager, EVT_WARNING, "opj_jp2_apply_cdef: acn=%d, numcomps=%d\n",
-                          acn, image->numcomps);
-            continue;
-        }
-
-        /* Swap only if color channel */
-        if ((cn != acn) && (info[i].typ == 0)) {
-            opj_image_comp_t saved;
-            OPJ_UINT16 j;
-
-            memcpy(&saved, &image->comps[cn], sizeof(opj_image_comp_t));
-            memcpy(&image->comps[cn], &image->comps[acn], sizeof(opj_image_comp_t));
-            memcpy(&image->comps[acn], &saved, sizeof(opj_image_comp_t));
-
-            /* Swap channels in following channel definitions, don't bother with j <= i that are already processed */
-            for (j = (OPJ_UINT16)(i + 1U); j < n ; ++j) {
-                if (info[j].cn == cn) {
-                    info[j].cn = acn;
-                } else if (info[j].cn == acn) {
-                    info[j].cn = cn;
-                }
-                /* asoc is related to color index. Do not update. */
-            }
-        }
-
-        image->comps[cn].alpha = info[i].typ;
-    }
-
-    if (color->jp2_cdef->info) {
-        opj_free(color->jp2_cdef->info);
-    }
-
-    opj_free(color->jp2_cdef);
-    color->jp2_cdef = NULL;
-
-}/* jp2_apply_cdef() */
-
-static OPJ_BOOL opj_jp2_read_cdef(opj_jp2_t * jp2,
-                                  OPJ_BYTE * p_cdef_header_data,
-                                  OPJ_UINT32 p_cdef_header_size,
-                                  opj_event_mgr_t * p_manager
-                                 )
-{
-    opj_jp2_cdef_info_t *cdef_info;
-    OPJ_UINT16 i;
-    OPJ_UINT32 l_value;
-
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(p_cdef_header_data != 00);
-    assert(p_manager != 00);
-    (void)p_cdef_header_size;
-
-    /* Part 1, I.5.3.6: 'The shall be at most one Channel Definition box
-     * inside a JP2 Header box.'*/
-    if (jp2->color.jp2_cdef) {
-        return OPJ_FALSE;
-    }
-
-    if (p_cdef_header_size < 2) {
-        opj_event_msg(p_manager, EVT_ERROR, "Insufficient data for CDEF box.\n");
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(p_cdef_header_data, &l_value, 2);        /* N */
-    p_cdef_header_data += 2;
-
-    if ((OPJ_UINT16)l_value == 0) { /* szukw000: FIXME */
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Number of channel description is equal to zero in CDEF box.\n");
-        return OPJ_FALSE;
-    }
-
-    if (p_cdef_header_size < 2 + (OPJ_UINT32)(OPJ_UINT16)l_value * 6) {
-        opj_event_msg(p_manager, EVT_ERROR, "Insufficient data for CDEF box.\n");
-        return OPJ_FALSE;
-    }
-
-    cdef_info = (opj_jp2_cdef_info_t*) opj_malloc(l_value * sizeof(
-                    opj_jp2_cdef_info_t));
-    if (!cdef_info) {
-        return OPJ_FALSE;
-    }
-
-    jp2->color.jp2_cdef = (opj_jp2_cdef_t*)opj_malloc(sizeof(opj_jp2_cdef_t));
-    if (!jp2->color.jp2_cdef) {
-        opj_free(cdef_info);
-        return OPJ_FALSE;
-    }
-    jp2->color.jp2_cdef->info = cdef_info;
-    jp2->color.jp2_cdef->n = (OPJ_UINT16) l_value;
-
-    for (i = 0; i < jp2->color.jp2_cdef->n; ++i) {
-        opj_read_bytes(p_cdef_header_data, &l_value, 2);            /* Cn^i */
-        p_cdef_header_data += 2;
-        cdef_info[i].cn = (OPJ_UINT16) l_value;
-
-        opj_read_bytes(p_cdef_header_data, &l_value, 2);            /* Typ^i */
-        p_cdef_header_data += 2;
-        cdef_info[i].typ = (OPJ_UINT16) l_value;
-
-        opj_read_bytes(p_cdef_header_data, &l_value, 2);            /* Asoc^i */
-        p_cdef_header_data += 2;
-        cdef_info[i].asoc = (OPJ_UINT16) l_value;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_jp2_read_colr(opj_jp2_t *jp2,
-                                  OPJ_BYTE * p_colr_header_data,
-                                  OPJ_UINT32 p_colr_header_size,
-                                  opj_event_mgr_t * p_manager
-                                 )
-{
-    OPJ_UINT32 l_value;
-
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(p_colr_header_data != 00);
-    assert(p_manager != 00);
-
-    if (p_colr_header_size < 3) {
-        opj_event_msg(p_manager, EVT_ERROR, "Bad COLR header box (bad size)\n");
-        return OPJ_FALSE;
-    }
-
-    /* Part 1, I.5.3.3 : 'A conforming JP2 reader shall ignore all Colour
-     * Specification boxes after the first.'
-    */
-    if (jp2->color.jp2_has_colr) {
-        opj_event_msg(p_manager, EVT_INFO,
-                      "A conforming JP2 reader shall ignore all Colour Specification boxes after the first, so we ignore this one.\n");
-        p_colr_header_data += p_colr_header_size;
-        return OPJ_TRUE;
-    }
-
-    opj_read_bytes(p_colr_header_data, &jp2->meth, 1);          /* METH */
-    ++p_colr_header_data;
-
-    opj_read_bytes(p_colr_header_data, &jp2->precedence, 1);    /* PRECEDENCE */
-    ++p_colr_header_data;
-
-    opj_read_bytes(p_colr_header_data, &jp2->approx, 1);        /* APPROX */
-    ++p_colr_header_data;
-
-    if (jp2->meth == 1) {
-        if (p_colr_header_size < 7) {
-            opj_event_msg(p_manager, EVT_ERROR, "Bad COLR header box (bad size: %d)\n",
-                          p_colr_header_size);
-            return OPJ_FALSE;
-        }
-        if ((p_colr_header_size > 7) &&
-                (jp2->enumcs != 14)) { /* handled below for CIELab) */
-            /* testcase Altona_Technical_v20_x4.pdf */
-            opj_event_msg(p_manager, EVT_WARNING, "Bad COLR header box (bad size: %d)\n",
-                          p_colr_header_size);
-        }
-
-        opj_read_bytes(p_colr_header_data, &jp2->enumcs, 4);        /* EnumCS */
-
-        p_colr_header_data += 4;
-
-        if (jp2->enumcs == 14) { /* CIELab */
-            OPJ_UINT32 *cielab;
-            OPJ_UINT32 rl, ol, ra, oa, rb, ob, il;
-
-            cielab = (OPJ_UINT32*)opj_malloc(9 * sizeof(OPJ_UINT32));
-            if (cielab == NULL) {
-                opj_event_msg(p_manager, EVT_ERROR, "Not enough memory for cielab\n");
-                return OPJ_FALSE;
-            }
-            cielab[0] = 14; /* enumcs */
-
-            /* default values */
-            rl = ra = rb = ol = oa = ob = 0;
-            il = 0x00443530; /* D50 */
-            cielab[1] = 0x44454600;/* DEF */
-
-            if (p_colr_header_size == 35) {
-                opj_read_bytes(p_colr_header_data, &rl, 4);
-                p_colr_header_data += 4;
-                opj_read_bytes(p_colr_header_data, &ol, 4);
-                p_colr_header_data += 4;
-                opj_read_bytes(p_colr_header_data, &ra, 4);
-                p_colr_header_data += 4;
-                opj_read_bytes(p_colr_header_data, &oa, 4);
-                p_colr_header_data += 4;
-                opj_read_bytes(p_colr_header_data, &rb, 4);
-                p_colr_header_data += 4;
-                opj_read_bytes(p_colr_header_data, &ob, 4);
-                p_colr_header_data += 4;
-                opj_read_bytes(p_colr_header_data, &il, 4);
-                p_colr_header_data += 4;
-
-                cielab[1] = 0;
-            } else if (p_colr_header_size != 7) {
-                opj_event_msg(p_manager, EVT_WARNING,
-                              "Bad COLR header box (CIELab, bad size: %d)\n", p_colr_header_size);
-            }
-            cielab[2] = rl;
-            cielab[4] = ra;
-            cielab[6] = rb;
-            cielab[3] = ol;
-            cielab[5] = oa;
-            cielab[7] = ob;
-            cielab[8] = il;
-
-            jp2->color.icc_profile_buf = (OPJ_BYTE*)cielab;
-            jp2->color.icc_profile_len = 0;
-        }
-        jp2->color.jp2_has_colr = 1;
-    } else if (jp2->meth == 2) {
-        /* ICC profile */
-        OPJ_INT32 it_icc_value = 0;
-        OPJ_INT32 icc_len = (OPJ_INT32)p_colr_header_size - 3;
-
-        jp2->color.icc_profile_len = (OPJ_UINT32)icc_len;
-        jp2->color.icc_profile_buf = (OPJ_BYTE*) opj_calloc(1, (size_t)icc_len);
-        if (!jp2->color.icc_profile_buf) {
-            jp2->color.icc_profile_len = 0;
-            return OPJ_FALSE;
-        }
-
-        for (it_icc_value = 0; it_icc_value < icc_len; ++it_icc_value) {
-            opj_read_bytes(p_colr_header_data, &l_value, 1);    /* icc values */
-            ++p_colr_header_data;
-            jp2->color.icc_profile_buf[it_icc_value] = (OPJ_BYTE) l_value;
-        }
-
-        jp2->color.jp2_has_colr = 1;
-    } else if (jp2->meth > 2) {
-        /*  ISO/IEC 15444-1:2004 (E), Table I.9 Legal METH values:
-        conforming JP2 reader shall ignore the entire Colour Specification box.*/
-        opj_event_msg(p_manager, EVT_INFO,
-                      "COLR BOX meth value is not a regular value (%d), "
-                      "so we will ignore the entire Colour Specification box. \n", jp2->meth);
-    }
-    if (jp2->color.jp2_has_colr) {
-        jp2->j2k->enumcs = jp2->enumcs;
-    }
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_jp2_decode(opj_jp2_t *jp2,
-                        opj_stream_private_t *p_stream,
-                        opj_image_t* p_image,
-                        opj_event_mgr_t * p_manager)
-{
-    if (!p_image) {
-        return OPJ_FALSE;
-    }
-
-    /* J2K decoding */
-    if (! opj_j2k_decode(jp2->j2k, p_stream, p_image, p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Failed to decode the codestream in the JP2 file\n");
-        return OPJ_FALSE;
-    }
-
-    if (jp2->j2k->m_specific_param.m_decoder.m_numcomps_to_decode) {
-        /* Bypass all JP2 component transforms */
-        return OPJ_TRUE;
-    }
-
-    if (!jp2->ignore_pclr_cmap_cdef) {
-        if (!opj_jp2_check_color(p_image, &(jp2->color), p_manager)) {
-            return OPJ_FALSE;
-        }
-
-        /* Set Image Color Space */
-        if (jp2->enumcs == 16) {
-            p_image->color_space = OPJ_CLRSPC_SRGB;
-        } else if (jp2->enumcs == 17) {
-            p_image->color_space = OPJ_CLRSPC_GRAY;
-        } else if (jp2->enumcs == 18) {
-            p_image->color_space = OPJ_CLRSPC_SYCC;
-        } else if (jp2->enumcs == 24) {
-            p_image->color_space = OPJ_CLRSPC_EYCC;
-        } else if (jp2->enumcs == 12) {
-            p_image->color_space = OPJ_CLRSPC_CMYK;
-        } else {
-            p_image->color_space = OPJ_CLRSPC_UNKNOWN;
-        }
-
-        if (jp2->color.jp2_pclr) {
-            /* Part 1, I.5.3.4: Either both or none : */
-            if (!jp2->color.jp2_pclr->cmap) {
-                opj_jp2_free_pclr(&(jp2->color));
-            } else {
-                if (!opj_jp2_apply_pclr(p_image, &(jp2->color), p_manager)) {
-                    return OPJ_FALSE;
-                }
-            }
-        }
-
-        /* Apply the color space if needed */
-        if (jp2->color.jp2_cdef) {
-            opj_jp2_apply_cdef(p_image, &(jp2->color), p_manager);
-        }
-
-        if (jp2->color.icc_profile_buf) {
-            p_image->icc_profile_buf = jp2->color.icc_profile_buf;
-            p_image->icc_profile_len = jp2->color.icc_profile_len;
-            jp2->color.icc_profile_buf = NULL;
-        }
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_jp2_write_jp2h(opj_jp2_t *jp2,
-                                   opj_stream_private_t *stream,
-                                   opj_event_mgr_t * p_manager
-                                  )
-{
-    opj_jp2_img_header_writer_handler_t l_writers [4];
-    opj_jp2_img_header_writer_handler_t * l_current_writer;
-
-    OPJ_INT32 i, l_nb_pass;
-    /* size of data for super box*/
-    OPJ_UINT32 l_jp2h_size = 8;
-    OPJ_BOOL l_result = OPJ_TRUE;
-
-    /* to store the data of the super box */
-    OPJ_BYTE l_jp2h_data [8];
-
-    /* preconditions */
-    assert(stream != 00);
-    assert(jp2 != 00);
-    assert(p_manager != 00);
-
-    memset(l_writers, 0, sizeof(l_writers));
-
-    if (jp2->bpc == 255) {
-        l_nb_pass = 3;
-        l_writers[0].handler = opj_jp2_write_ihdr;
-        l_writers[1].handler = opj_jp2_write_bpcc;
-        l_writers[2].handler = opj_jp2_write_colr;
-    } else {
-        l_nb_pass = 2;
-        l_writers[0].handler = opj_jp2_write_ihdr;
-        l_writers[1].handler = opj_jp2_write_colr;
-    }
-
-    if (jp2->color.jp2_cdef != NULL) {
-        l_writers[l_nb_pass].handler = opj_jp2_write_cdef;
-        l_nb_pass++;
-    }
-
-    /* write box header */
-    /* write JP2H type */
-    opj_write_bytes(l_jp2h_data + 4, JP2_JP2H, 4);
-
-    l_current_writer = l_writers;
-    for (i = 0; i < l_nb_pass; ++i) {
-        l_current_writer->m_data = l_current_writer->handler(jp2,
-                                   &(l_current_writer->m_size));
-        if (l_current_writer->m_data == 00) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Not enough memory to hold JP2 Header data\n");
-            l_result = OPJ_FALSE;
-            break;
-        }
-
-        l_jp2h_size += l_current_writer->m_size;
-        ++l_current_writer;
-    }
-
-    if (! l_result) {
-        l_current_writer = l_writers;
-        for (i = 0; i < l_nb_pass; ++i) {
-            if (l_current_writer->m_data != 00) {
-                opj_free(l_current_writer->m_data);
-            }
-            ++l_current_writer;
-        }
-
-        return OPJ_FALSE;
-    }
-
-    /* write super box size */
-    opj_write_bytes(l_jp2h_data, l_jp2h_size, 4);
-
-    /* write super box data on stream */
-    if (opj_stream_write_data(stream, l_jp2h_data, 8, p_manager) != 8) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Stream error while writing JP2 Header box\n");
-        l_result = OPJ_FALSE;
-    }
-
-    if (l_result) {
-        l_current_writer = l_writers;
-        for (i = 0; i < l_nb_pass; ++i) {
-            if (opj_stream_write_data(stream, l_current_writer->m_data,
-                                      l_current_writer->m_size, p_manager) != l_current_writer->m_size) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Stream error while writing JP2 Header box\n");
-                l_result = OPJ_FALSE;
-                break;
-            }
-            ++l_current_writer;
-        }
-    }
-
-    l_current_writer = l_writers;
-
-    /* cleanup */
-    for (i = 0; i < l_nb_pass; ++i) {
-        if (l_current_writer->m_data != 00) {
-            opj_free(l_current_writer->m_data);
-        }
-        ++l_current_writer;
-    }
-
-    return l_result;
-}
-
-static OPJ_BOOL opj_jp2_write_ftyp(opj_jp2_t *jp2,
-                                   opj_stream_private_t *cio,
-                                   opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32 i;
-    OPJ_UINT32 l_ftyp_size;
-    OPJ_BYTE * l_ftyp_data, * l_current_data_ptr;
-    OPJ_BOOL l_result;
-
-    /* preconditions */
-    assert(cio != 00);
-    assert(jp2 != 00);
-    assert(p_manager != 00);
-    l_ftyp_size = 16 + 4 * jp2->numcl;
-
-    l_ftyp_data = (OPJ_BYTE *) opj_calloc(1, l_ftyp_size);
-
-    if (l_ftyp_data == 00) {
-        opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to handle ftyp data\n");
-        return OPJ_FALSE;
-    }
-
-    l_current_data_ptr = l_ftyp_data;
-
-    opj_write_bytes(l_current_data_ptr, l_ftyp_size, 4); /* box size */
-    l_current_data_ptr += 4;
-
-    opj_write_bytes(l_current_data_ptr, JP2_FTYP, 4); /* FTYP */
-    l_current_data_ptr += 4;
-
-    opj_write_bytes(l_current_data_ptr, jp2->brand, 4); /* BR */
-    l_current_data_ptr += 4;
-
-    opj_write_bytes(l_current_data_ptr, jp2->minversion, 4); /* MinV */
-    l_current_data_ptr += 4;
-
-    for (i = 0; i < jp2->numcl; i++)  {
-        opj_write_bytes(l_current_data_ptr, jp2->cl[i], 4); /* CL */
-    }
-
-    l_result = (opj_stream_write_data(cio, l_ftyp_data, l_ftyp_size,
-                                      p_manager) == l_ftyp_size);
-    if (! l_result) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Error while writing ftyp data to stream\n");
-    }
-
-    opj_free(l_ftyp_data);
-
-    return l_result;
-}
-
-static OPJ_BOOL opj_jp2_write_jp2c(opj_jp2_t *jp2,
-                                   opj_stream_private_t *cio,
-                                   opj_event_mgr_t * p_manager)
-{
-    OPJ_OFF_T j2k_codestream_exit;
-    OPJ_BYTE l_data_header [8];
-
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(cio != 00);
-    assert(p_manager != 00);
-    assert(opj_stream_has_seek(cio));
-
-    j2k_codestream_exit = opj_stream_tell(cio);
-    opj_write_bytes(l_data_header,
-                    (OPJ_UINT32)(j2k_codestream_exit - jp2->j2k_codestream_offset),
-                    4); /* size of codestream */
-    opj_write_bytes(l_data_header + 4, JP2_JP2C,
-                    4);                                     /* JP2C */
-
-    if (! opj_stream_seek(cio, jp2->j2k_codestream_offset, p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
-        return OPJ_FALSE;
-    }
-
-    if (opj_stream_write_data(cio, l_data_header, 8, p_manager) != 8) {
-        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
-        return OPJ_FALSE;
-    }
-
-    if (! opj_stream_seek(cio, j2k_codestream_exit, p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_jp2_write_jp(opj_jp2_t *jp2,
-                                 opj_stream_private_t *cio,
-                                 opj_event_mgr_t * p_manager)
-{
-    /* 12 bytes will be read */
-    OPJ_BYTE l_signature_data [12];
-
-    /* preconditions */
-    assert(cio != 00);
-    assert(jp2 != 00);
-    assert(p_manager != 00);
-
-    OPJ_UNUSED(jp2);
-
-    /* write box length */
-    opj_write_bytes(l_signature_data, 12, 4);
-    /* writes box type */
-    opj_write_bytes(l_signature_data + 4, JP2_JP, 4);
-    /* writes magic number*/
-    opj_write_bytes(l_signature_data + 8, 0x0d0a870a, 4);
-
-    if (opj_stream_write_data(cio, l_signature_data, 12, p_manager) != 12) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-/* ----------------------------------------------------------------------- */
-/* JP2 decoder interface                                             */
-/* ----------------------------------------------------------------------- */
-
-void opj_jp2_setup_decoder(opj_jp2_t *jp2, opj_dparameters_t *parameters)
-{
-    /* setup the J2K codec */
-    opj_j2k_setup_decoder(jp2->j2k, parameters);
-
-    /* further JP2 initializations go here */
-    jp2->color.jp2_has_colr = 0;
-    jp2->comps = NULL;
-    jp2->ignore_pclr_cmap_cdef = parameters->flags &
-                                 OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG;
-}
-
-OPJ_BOOL opj_jp2_set_threads(opj_jp2_t *jp2, OPJ_UINT32 num_threads)
-{
-    return opj_j2k_set_threads(jp2->j2k, num_threads);
-}
-
-/* ----------------------------------------------------------------------- */
-/* JP2 encoder interface                                             */
-/* ----------------------------------------------------------------------- */
-
-OPJ_BOOL opj_jp2_setup_encoder(opj_jp2_t *jp2,
-                               opj_cparameters_t *parameters,
-                               opj_image_t *image,
-                               opj_event_mgr_t * p_manager)
-{
-    OPJ_UINT32 i;
-    OPJ_UINT32 depth_0;
-    OPJ_UINT32 sign;
-    OPJ_UINT32 alpha_count;
-    OPJ_UINT32 color_channels = 0U;
-    OPJ_UINT32 alpha_channel = 0U;
-
-
-    if (!jp2 || !parameters || !image) {
-        return OPJ_FALSE;
-    }
-
-    /* setup the J2K codec */
-    /* ------------------- */
-
-    /* Check if number of components respects standard */
-    if (image->numcomps < 1 || image->numcomps > 16384) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Invalid number of components specified while setting up JP2 encoder\n");
-        return OPJ_FALSE;
-    }
-
-    if (opj_j2k_setup_encoder(jp2->j2k, parameters, image,
-                              p_manager) == OPJ_FALSE) {
-        return OPJ_FALSE;
-    }
-
-    /* setup the JP2 codec */
-    /* ------------------- */
-
-    /* Profile box */
-
-    jp2->brand = JP2_JP2;   /* BR */
-    jp2->minversion = 0;    /* MinV */
-    jp2->numcl = 1;
-    jp2->cl = (OPJ_UINT32*) opj_malloc(jp2->numcl * sizeof(OPJ_UINT32));
-    if (!jp2->cl) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Not enough memory when setup the JP2 encoder\n");
-        return OPJ_FALSE;
-    }
-    jp2->cl[0] = JP2_JP2;   /* CL0 : JP2 */
-
-    /* Image Header box */
-
-    jp2->numcomps = image->numcomps;    /* NC */
-    jp2->comps = (opj_jp2_comps_t*) opj_malloc(jp2->numcomps * sizeof(
-                     opj_jp2_comps_t));
-    if (!jp2->comps) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Not enough memory when setup the JP2 encoder\n");
-        /* Memory of jp2->cl will be freed by opj_jp2_destroy */
-        return OPJ_FALSE;
-    }
-
-    jp2->h = image->y1 - image->y0;     /* HEIGHT */
-    jp2->w = image->x1 - image->x0;     /* WIDTH */
-    /* BPC */
-    depth_0 = image->comps[0].prec - 1;
-    sign = image->comps[0].sgnd;
-    jp2->bpc = depth_0 + (sign << 7);
-    for (i = 1; i < image->numcomps; i++) {
-        OPJ_UINT32 depth = image->comps[i].prec - 1;
-        sign = image->comps[i].sgnd;
-        if (depth_0 != depth) {
-            jp2->bpc = 255;
-        }
-    }
-    jp2->C = 7;         /* C : Always 7 */
-    jp2->UnkC = 0;      /* UnkC, colorspace specified in colr box */
-    jp2->IPR = 0;       /* IPR, no intellectual property */
-
-    /* BitsPerComponent box */
-    for (i = 0; i < image->numcomps; i++) {
-        jp2->comps[i].bpcc = image->comps[i].prec - 1 + (image->comps[i].sgnd << 7);
-    }
-
-    /* Colour Specification box */
-    if (image->icc_profile_len) {
-        jp2->meth = 2;
-        jp2->enumcs = 0;
-    } else {
-        jp2->meth = 1;
-        if (image->color_space == 1) {
-            jp2->enumcs = 16;    /* sRGB as defined by IEC 61966-2-1 */
-        } else if (image->color_space == 2) {
-            jp2->enumcs = 17;    /* greyscale */
-        } else if (image->color_space == 3) {
-            jp2->enumcs = 18;    /* YUV */
-        }
-    }
-
-    /* Channel Definition box */
-    /* FIXME not provided by parameters */
-    /* We try to do what we can... */
-    alpha_count = 0U;
-    for (i = 0; i < image->numcomps; i++) {
-        if (image->comps[i].alpha != 0) {
-            alpha_count++;
-            alpha_channel = i;
-        }
-    }
-    if (alpha_count == 1U) { /* no way to deal with more than 1 alpha channel */
-        switch (jp2->enumcs) {
-        case 16:
-        case 18:
-            color_channels = 3;
-            break;
-        case 17:
-            color_channels = 1;
-            break;
-        default:
-            alpha_count = 0U;
-            break;
-        }
-        if (alpha_count == 0U) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "Alpha channel specified but unknown enumcs. No cdef box will be created.\n");
-        } else if (image->numcomps < (color_channels + 1)) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "Alpha channel specified but not enough image components for an automatic cdef box creation.\n");
-            alpha_count = 0U;
-        } else if ((OPJ_UINT32)alpha_channel < color_channels) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "Alpha channel position conflicts with color channel. No cdef box will be created.\n");
-            alpha_count = 0U;
-        }
-    } else if (alpha_count > 1) {
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "Multiple alpha channels specified. No cdef box will be created.\n");
-    }
-    if (alpha_count == 1U) { /* if here, we know what we can do */
-        jp2->color.jp2_cdef = (opj_jp2_cdef_t*)opj_malloc(sizeof(opj_jp2_cdef_t));
-        if (!jp2->color.jp2_cdef) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Not enough memory to setup the JP2 encoder\n");
-            return OPJ_FALSE;
-        }
-        /* no memset needed, all values will be overwritten except if jp2->color.jp2_cdef->info allocation fails, */
-        /* in which case jp2->color.jp2_cdef->info will be NULL => valid for destruction */
-        jp2->color.jp2_cdef->info = (opj_jp2_cdef_info_t*) opj_malloc(
-                                        image->numcomps * sizeof(opj_jp2_cdef_info_t));
-        if (!jp2->color.jp2_cdef->info) {
-            /* memory will be freed by opj_jp2_destroy */
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Not enough memory to setup the JP2 encoder\n");
-            return OPJ_FALSE;
-        }
-        jp2->color.jp2_cdef->n = (OPJ_UINT16)
-                                 image->numcomps; /* cast is valid : image->numcomps [1,16384] */
-        for (i = 0U; i < color_channels; i++) {
-            jp2->color.jp2_cdef->info[i].cn = (OPJ_UINT16)
-                                              i; /* cast is valid : image->numcomps [1,16384] */
-            jp2->color.jp2_cdef->info[i].typ = 0U;
-            jp2->color.jp2_cdef->info[i].asoc = (OPJ_UINT16)(i +
-                                                1U); /* No overflow + cast is valid : image->numcomps [1,16384] */
-        }
-        for (; i < image->numcomps; i++) {
-            if (image->comps[i].alpha != 0) { /* we'll be here exactly once */
-                jp2->color.jp2_cdef->info[i].cn = (OPJ_UINT16)
-                                                  i; /* cast is valid : image->numcomps [1,16384] */
-                jp2->color.jp2_cdef->info[i].typ = 1U; /* Opacity channel */
-                jp2->color.jp2_cdef->info[i].asoc =
-                    0U; /* Apply alpha channel to the whole image */
-            } else {
-                /* Unknown channel */
-                jp2->color.jp2_cdef->info[i].cn = (OPJ_UINT16)
-                                                  i; /* cast is valid : image->numcomps [1,16384] */
-                jp2->color.jp2_cdef->info[i].typ = 65535U;
-                jp2->color.jp2_cdef->info[i].asoc = 65535U;
-            }
-        }
-    }
-
-    jp2->precedence = 0;    /* PRECEDENCE */
-    jp2->approx = 0;        /* APPROX */
-
-    jp2->jpip_on = parameters->jpip_on;
-
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_jp2_encode(opj_jp2_t *jp2,
-                        opj_stream_private_t *stream,
-                        opj_event_mgr_t * p_manager)
-{
-    return opj_j2k_encode(jp2->j2k, stream, p_manager);
-}
-
-OPJ_BOOL opj_jp2_end_decompress(opj_jp2_t *jp2,
-                                opj_stream_private_t *cio,
-                                opj_event_mgr_t * p_manager
-                               )
-{
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(cio != 00);
-    assert(p_manager != 00);
-
-    /* customization of the end encoding */
-    if (! opj_jp2_setup_end_header_reading(jp2, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* write header */
-    if (! opj_jp2_exec(jp2, jp2->m_procedure_list, cio, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    return opj_j2k_end_decompress(jp2->j2k, cio, p_manager);
-}
-
-OPJ_BOOL opj_jp2_end_compress(opj_jp2_t *jp2,
-                              opj_stream_private_t *cio,
-                              opj_event_mgr_t * p_manager
-                             )
-{
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(cio != 00);
-    assert(p_manager != 00);
-
-    /* customization of the end encoding */
-    if (! opj_jp2_setup_end_header_writing(jp2, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    if (! opj_j2k_end_compress(jp2->j2k, cio, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* write header */
-    return opj_jp2_exec(jp2, jp2->m_procedure_list, cio, p_manager);
-}
-
-static OPJ_BOOL opj_jp2_setup_end_header_writing(opj_jp2_t *jp2,
-        opj_event_mgr_t * p_manager)
-{
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(p_manager != 00);
-
-#ifdef USE_JPIP
-    if (jp2->jpip_on) {
-        if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
-                                               (opj_procedure)opj_jpip_write_iptr, p_manager)) {
-            return OPJ_FALSE;
-        }
-    }
-#endif
-    if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
-                                           (opj_procedure)opj_jp2_write_jp2c, p_manager)) {
-        return OPJ_FALSE;
-    }
-    /* DEVELOPER CORNER, add your custom procedures */
-#ifdef USE_JPIP
-    if (jp2->jpip_on) {
-        if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
-                                               (opj_procedure)opj_jpip_write_cidx, p_manager)) {
-            return OPJ_FALSE;
-        }
-        if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
-                                               (opj_procedure)opj_jpip_write_fidx, p_manager)) {
-            return OPJ_FALSE;
-        }
-    }
-#endif
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_jp2_setup_end_header_reading(opj_jp2_t *jp2,
-        opj_event_mgr_t * p_manager)
-{
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(p_manager != 00);
-
-    if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
-                                           (opj_procedure)opj_jp2_read_header_procedure, p_manager)) {
-        return OPJ_FALSE;
-    }
-    /* DEVELOPER CORNER, add your custom procedures */
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_jp2_default_validation(opj_jp2_t * jp2,
-        opj_stream_private_t *cio,
-        opj_event_mgr_t * p_manager
-                                          )
-{
-    OPJ_BOOL l_is_valid = OPJ_TRUE;
-    OPJ_UINT32 i;
-
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(cio != 00);
-    assert(p_manager != 00);
-
-    OPJ_UNUSED(p_manager);
-
-    /* JPEG2000 codec validation */
-
-    /* STATE checking */
-    /* make sure the state is at 0 */
-    l_is_valid &= (jp2->jp2_state == JP2_STATE_NONE);
-
-    /* make sure not reading a jp2h ???? WEIRD */
-    l_is_valid &= (jp2->jp2_img_state == JP2_IMG_STATE_NONE);
-
-    /* POINTER validation */
-    /* make sure a j2k codec is present */
-    l_is_valid &= (jp2->j2k != 00);
-
-    /* make sure a procedure list is present */
-    l_is_valid &= (jp2->m_procedure_list != 00);
-
-    /* make sure a validation list is present */
-    l_is_valid &= (jp2->m_validation_list != 00);
-
-    /* PARAMETER VALIDATION */
-    /* number of components */
-    l_is_valid &= (jp2->numcl > 0);
-    /* width */
-    l_is_valid &= (jp2->h > 0);
-    /* height */
-    l_is_valid &= (jp2->w > 0);
-    /* precision */
-    for (i = 0; i < jp2->numcomps; ++i) {
-        l_is_valid &= ((jp2->comps[i].bpcc & 0x7FU) <
-                       38U); /* 0 is valid, ignore sign for check */
-    }
-
-    /* METH */
-    l_is_valid &= ((jp2->meth > 0) && (jp2->meth < 3));
-
-    /* stream validation */
-    /* back and forth is needed */
-    l_is_valid &= opj_stream_has_seek(cio);
-
-    return l_is_valid;
-}
-
-static OPJ_BOOL opj_jp2_read_header_procedure(opj_jp2_t *jp2,
-        opj_stream_private_t *stream,
-        opj_event_mgr_t * p_manager
-                                             )
-{
-    opj_jp2_box_t box;
-    OPJ_UINT32 l_nb_bytes_read;
-    const opj_jp2_header_handler_t * l_current_handler;
-    const opj_jp2_header_handler_t * l_current_handler_misplaced;
-    OPJ_UINT32 l_last_data_size = OPJ_BOX_SIZE;
-    OPJ_UINT32 l_current_data_size;
-    OPJ_BYTE * l_current_data = 00;
-
-    /* preconditions */
-    assert(stream != 00);
-    assert(jp2 != 00);
-    assert(p_manager != 00);
-
-    l_current_data = (OPJ_BYTE*)opj_calloc(1, l_last_data_size);
-
-    if (l_current_data == 00) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Not enough memory to handle jpeg2000 file header\n");
-        return OPJ_FALSE;
-    }
-
-    while (opj_jp2_read_boxhdr(&box, &l_nb_bytes_read, stream, p_manager)) {
-        /* is it the codestream box ? */
-        if (box.type == JP2_JP2C) {
-            if (jp2->jp2_state & JP2_STATE_HEADER) {
-                jp2->jp2_state |= JP2_STATE_CODESTREAM;
-                opj_free(l_current_data);
-                return OPJ_TRUE;
-            } else {
-                opj_event_msg(p_manager, EVT_ERROR, "bad placed jpeg codestream\n");
-                opj_free(l_current_data);
-                return OPJ_FALSE;
-            }
-        } else if (box.length == 0) {
-            opj_event_msg(p_manager, EVT_ERROR, "Cannot handle box of undefined sizes\n");
-            opj_free(l_current_data);
-            return OPJ_FALSE;
-        }
-        /* testcase 1851.pdf.SIGSEGV.ce9.948 */
-        else if (box.length < l_nb_bytes_read) {
-            opj_event_msg(p_manager, EVT_ERROR, "invalid box size %d (%x)\n", box.length,
-                          box.type);
-            opj_free(l_current_data);
-            return OPJ_FALSE;
-        }
-
-        l_current_handler = opj_jp2_find_handler(box.type);
-        l_current_handler_misplaced = opj_jp2_img_find_handler(box.type);
-        l_current_data_size = box.length - l_nb_bytes_read;
-
-        if ((l_current_handler != 00) || (l_current_handler_misplaced != 00)) {
-            if (l_current_handler == 00) {
-                opj_event_msg(p_manager, EVT_WARNING,
-                              "Found a misplaced '%c%c%c%c' box outside jp2h box\n",
-                              (OPJ_BYTE)(box.type >> 24), (OPJ_BYTE)(box.type >> 16),
-                              (OPJ_BYTE)(box.type >> 8), (OPJ_BYTE)(box.type >> 0));
-                if (jp2->jp2_state & JP2_STATE_HEADER) {
-                    /* read anyway, we already have jp2h */
-                    l_current_handler = l_current_handler_misplaced;
-                } else {
-                    opj_event_msg(p_manager, EVT_WARNING,
-                                  "JPEG2000 Header box not read yet, '%c%c%c%c' box will be ignored\n",
-                                  (OPJ_BYTE)(box.type >> 24), (OPJ_BYTE)(box.type >> 16),
-                                  (OPJ_BYTE)(box.type >> 8), (OPJ_BYTE)(box.type >> 0));
-                    jp2->jp2_state |= JP2_STATE_UNKNOWN;
-                    if (opj_stream_skip(stream, l_current_data_size,
-                                        p_manager) != l_current_data_size) {
-                        opj_event_msg(p_manager, EVT_ERROR,
-                                      "Problem with skipping JPEG2000 box, stream error\n");
-                        opj_free(l_current_data);
-                        return OPJ_FALSE;
-                    }
-                    continue;
-                }
-            }
-            if ((OPJ_OFF_T)l_current_data_size > opj_stream_get_number_byte_left(stream)) {
-                /* do not even try to malloc if we can't read */
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Invalid box size %d for box '%c%c%c%c'. Need %d bytes, %d bytes remaining \n",
-                              box.length, (OPJ_BYTE)(box.type >> 24), (OPJ_BYTE)(box.type >> 16),
-                              (OPJ_BYTE)(box.type >> 8), (OPJ_BYTE)(box.type >> 0), l_current_data_size,
-                              (OPJ_UINT32)opj_stream_get_number_byte_left(stream));
-                opj_free(l_current_data);
-                return OPJ_FALSE;
-            }
-            if (l_current_data_size > l_last_data_size) {
-                OPJ_BYTE* new_current_data = (OPJ_BYTE*)opj_realloc(l_current_data,
-                                             l_current_data_size);
-                if (!new_current_data) {
-                    opj_free(l_current_data);
-                    opj_event_msg(p_manager, EVT_ERROR,
-                                  "Not enough memory to handle jpeg2000 box\n");
-                    return OPJ_FALSE;
-                }
-                l_current_data = new_current_data;
-                l_last_data_size = l_current_data_size;
-            }
-
-            l_nb_bytes_read = (OPJ_UINT32)opj_stream_read_data(stream, l_current_data,
-                              l_current_data_size, p_manager);
-            if (l_nb_bytes_read != l_current_data_size) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Problem with reading JPEG2000 box, stream error\n");
-                opj_free(l_current_data);
-                return OPJ_FALSE;
-            }
-
-            if (! l_current_handler->handler(jp2, l_current_data, l_current_data_size,
-                                             p_manager)) {
-                opj_free(l_current_data);
-                return OPJ_FALSE;
-            }
-        } else {
-            if (!(jp2->jp2_state & JP2_STATE_SIGNATURE)) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Malformed JP2 file format: first box must be JPEG 2000 signature box\n");
-                opj_free(l_current_data);
-                return OPJ_FALSE;
-            }
-            if (!(jp2->jp2_state & JP2_STATE_FILE_TYPE)) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Malformed JP2 file format: second box must be file type box\n");
-                opj_free(l_current_data);
-                return OPJ_FALSE;
-            }
-            jp2->jp2_state |= JP2_STATE_UNKNOWN;
-            if (opj_stream_skip(stream, l_current_data_size,
-                                p_manager) != l_current_data_size) {
-                if (jp2->jp2_state & JP2_STATE_CODESTREAM) {
-                    /* If we already read the codestream, do not error out */
-                    /* Needed for data/input/nonregression/issue254.jp2 */
-                    opj_event_msg(p_manager, EVT_WARNING,
-                                  "Problem with skipping JPEG2000 box, stream error\n");
-                    opj_free(l_current_data);
-                    return OPJ_TRUE;
-                } else {
-                    opj_event_msg(p_manager, EVT_ERROR,
-                                  "Problem with skipping JPEG2000 box, stream error\n");
-                    opj_free(l_current_data);
-                    return OPJ_FALSE;
-                }
-            }
-        }
-    }
-
-    opj_free(l_current_data);
-
-    return OPJ_TRUE;
-}
-
-/**
- * Executes the given procedures on the given codec.
- *
- * @param   p_procedure_list    the list of procedures to execute
- * @param   jp2                 the jpeg2000 file codec to execute the procedures on.
- * @param   stream                  the stream to execute the procedures on.
- * @param   p_manager           the user manager.
- *
- * @return  true                if all the procedures were successfully executed.
- */
-static OPJ_BOOL opj_jp2_exec(opj_jp2_t * jp2,
-                             opj_procedure_list_t * p_procedure_list,
-                             opj_stream_private_t *stream,
-                             opj_event_mgr_t * p_manager
-                            )
-
-{
-    OPJ_BOOL(** l_procedure)(opj_jp2_t * jp2, opj_stream_private_t *,
-                             opj_event_mgr_t *) = 00;
-    OPJ_BOOL l_result = OPJ_TRUE;
-    OPJ_UINT32 l_nb_proc, i;
-
-    /* preconditions */
-    assert(p_procedure_list != 00);
-    assert(jp2 != 00);
-    assert(stream != 00);
-    assert(p_manager != 00);
-
-    l_nb_proc = opj_procedure_list_get_nb_procedures(p_procedure_list);
-    l_procedure = (OPJ_BOOL(**)(opj_jp2_t * jp2, opj_stream_private_t *,
-                                opj_event_mgr_t *)) opj_procedure_list_get_first_procedure(p_procedure_list);
-
-    for (i = 0; i < l_nb_proc; ++i) {
-        l_result = l_result && (*l_procedure)(jp2, stream, p_manager);
-        ++l_procedure;
-    }
-
-    /* and clear the procedure list at the end. */
-    opj_procedure_list_clear(p_procedure_list);
-    return l_result;
-}
-
-OPJ_BOOL opj_jp2_start_compress(opj_jp2_t *jp2,
-                                opj_stream_private_t *stream,
-                                opj_image_t * p_image,
-                                opj_event_mgr_t * p_manager
-                               )
-{
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(stream != 00);
-    assert(p_manager != 00);
-
-    /* customization of the validation */
-    if (! opj_jp2_setup_encoding_validation(jp2, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* validation of the parameters codec */
-    if (! opj_jp2_exec(jp2, jp2->m_validation_list, stream, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* customization of the encoding */
-    if (! opj_jp2_setup_header_writing(jp2, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* write header */
-    if (! opj_jp2_exec(jp2, jp2->m_procedure_list, stream, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    return opj_j2k_start_compress(jp2->j2k, stream, p_image, p_manager);
-}
-
-static const opj_jp2_header_handler_t * opj_jp2_find_handler(OPJ_UINT32 p_id)
-{
-    OPJ_UINT32 i, l_handler_size = sizeof(jp2_header) / sizeof(
-                                       opj_jp2_header_handler_t);
-
-    for (i = 0; i < l_handler_size; ++i) {
-        if (jp2_header[i].id == p_id) {
-            return &jp2_header[i];
-        }
-    }
-    return NULL;
-}
-
-/**
- * Finds the image execution function related to the given box id.
- *
- * @param   p_id    the id of the handler to fetch.
- *
- * @return  the given handler or 00 if it could not be found.
- */
-static const opj_jp2_header_handler_t * opj_jp2_img_find_handler(
-    OPJ_UINT32 p_id)
-{
-    OPJ_UINT32 i, l_handler_size = sizeof(jp2_img_header) / sizeof(
-                                       opj_jp2_header_handler_t);
-    for (i = 0; i < l_handler_size; ++i) {
-        if (jp2_img_header[i].id == p_id) {
-            return &jp2_img_header[i];
-        }
-    }
-
-    return NULL;
-}
-
-/**
- * Reads a jpeg2000 file signature box.
- *
- * @param   p_header_data   the data contained in the signature box.
- * @param   jp2             the jpeg2000 file codec.
- * @param   p_header_size   the size of the data contained in the signature box.
- * @param   p_manager       the user event manager.
- *
- * @return true if the file signature box is valid.
- */
-static OPJ_BOOL opj_jp2_read_jp(opj_jp2_t *jp2,
-                                OPJ_BYTE * p_header_data,
-                                OPJ_UINT32 p_header_size,
-                                opj_event_mgr_t * p_manager
-                               )
-
-{
-    OPJ_UINT32 l_magic_number;
-
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(jp2 != 00);
-    assert(p_manager != 00);
-
-    if (jp2->jp2_state != JP2_STATE_NONE) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "The signature box must be the first box in the file.\n");
-        return OPJ_FALSE;
-    }
-
-    /* assure length of data is correct (4 -> magic number) */
-    if (p_header_size != 4) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error with JP signature Box size\n");
-        return OPJ_FALSE;
-    }
-
-    /* rearrange data */
-    opj_read_bytes(p_header_data, &l_magic_number, 4);
-    if (l_magic_number != 0x0d0a870a) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Error with JP Signature : bad magic number\n");
-        return OPJ_FALSE;
-    }
-
-    jp2->jp2_state |= JP2_STATE_SIGNATURE;
-
-    return OPJ_TRUE;
-}
-
-/**
- * Reads a a FTYP box - File type box
- *
- * @param   p_header_data   the data contained in the FTYP box.
- * @param   jp2             the jpeg2000 file codec.
- * @param   p_header_size   the size of the data contained in the FTYP box.
- * @param   p_manager       the user event manager.
- *
- * @return true if the FTYP box is valid.
- */
-static OPJ_BOOL opj_jp2_read_ftyp(opj_jp2_t *jp2,
-                                  OPJ_BYTE * p_header_data,
-                                  OPJ_UINT32 p_header_size,
-                                  opj_event_mgr_t * p_manager
-                                 )
-{
-    OPJ_UINT32 i, l_remaining_bytes;
-
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(jp2 != 00);
-    assert(p_manager != 00);
-
-    if (jp2->jp2_state != JP2_STATE_SIGNATURE) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "The ftyp box must be the second box in the file.\n");
-        return OPJ_FALSE;
-    }
-
-    /* assure length of data is correct */
-    if (p_header_size < 8) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error with FTYP signature Box size\n");
-        return OPJ_FALSE;
-    }
-
-    opj_read_bytes(p_header_data, &jp2->brand, 4);      /* BR */
-    p_header_data += 4;
-
-    opj_read_bytes(p_header_data, &jp2->minversion, 4);     /* MinV */
-    p_header_data += 4;
-
-    l_remaining_bytes = p_header_size - 8;
-
-    /* the number of remaining bytes should be a multiple of 4 */
-    if ((l_remaining_bytes & 0x3) != 0) {
-        opj_event_msg(p_manager, EVT_ERROR, "Error with FTYP signature Box size\n");
-        return OPJ_FALSE;
-    }
-
-    /* div by 4 */
-    jp2->numcl = l_remaining_bytes >> 2;
-    if (jp2->numcl) {
-        jp2->cl = (OPJ_UINT32 *) opj_calloc(jp2->numcl, sizeof(OPJ_UINT32));
-        if (jp2->cl == 00) {
-            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory with FTYP Box\n");
-            return OPJ_FALSE;
-        }
-    }
-
-    for (i = 0; i < jp2->numcl; ++i) {
-        opj_read_bytes(p_header_data, &jp2->cl[i], 4);      /* CLi */
-        p_header_data += 4;
-    }
-
-    jp2->jp2_state |= JP2_STATE_FILE_TYPE;
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_jp2_skip_jp2c(opj_jp2_t *jp2,
-                                  opj_stream_private_t *stream,
-                                  opj_event_mgr_t * p_manager)
-{
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(stream != 00);
-    assert(p_manager != 00);
-
-    jp2->j2k_codestream_offset = opj_stream_tell(stream);
-
-    if (opj_stream_skip(stream, 8, p_manager) != 8) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_jpip_skip_iptr(opj_jp2_t *jp2,
-                                   opj_stream_private_t *stream,
-                                   opj_event_mgr_t * p_manager)
-{
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(stream != 00);
-    assert(p_manager != 00);
-
-    jp2->jpip_iptr_offset = opj_stream_tell(stream);
-
-    if (opj_stream_skip(stream, 24, p_manager) != 24) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-/**
- * Reads the Jpeg2000 file Header box - JP2 Header box (warning, this is a super box).
- *
- * @param   p_header_data   the data contained in the file header box.
- * @param   jp2             the jpeg2000 file codec.
- * @param   p_header_size   the size of the data contained in the file header box.
- * @param   p_manager       the user event manager.
- *
- * @return true if the JP2 Header box was successfully recognized.
-*/
-static OPJ_BOOL opj_jp2_read_jp2h(opj_jp2_t *jp2,
-                                  OPJ_BYTE *p_header_data,
-                                  OPJ_UINT32 p_header_size,
-                                  opj_event_mgr_t * p_manager
-                                 )
-{
-    OPJ_UINT32 l_box_size = 0, l_current_data_size = 0;
-    opj_jp2_box_t box;
-    const opj_jp2_header_handler_t * l_current_handler;
-    OPJ_BOOL l_has_ihdr = 0;
-
-    /* preconditions */
-    assert(p_header_data != 00);
-    assert(jp2 != 00);
-    assert(p_manager != 00);
-
-    /* make sure the box is well placed */
-    if ((jp2->jp2_state & JP2_STATE_FILE_TYPE) != JP2_STATE_FILE_TYPE) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "The  box must be the first box in the file.\n");
-        return OPJ_FALSE;
-    }
-
-    jp2->jp2_img_state = JP2_IMG_STATE_NONE;
-
-    /* iterate while remaining data */
-    while (p_header_size > 0) {
-
-        if (! opj_jp2_read_boxhdr_char(&box, p_header_data, &l_box_size, p_header_size,
-                                       p_manager)) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Stream error while reading JP2 Header box\n");
-            return OPJ_FALSE;
-        }
-
-        if (box.length > p_header_size) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Stream error while reading JP2 Header box: box length is inconsistent.\n");
-            return OPJ_FALSE;
-        }
-
-        l_current_handler = opj_jp2_img_find_handler(box.type);
-        l_current_data_size = box.length - l_box_size;
-        p_header_data += l_box_size;
-
-        if (l_current_handler != 00) {
-            if (! l_current_handler->handler(jp2, p_header_data, l_current_data_size,
-                                             p_manager)) {
-                return OPJ_FALSE;
-            }
-        } else {
-            jp2->jp2_img_state |= JP2_IMG_STATE_UNKNOWN;
-        }
-
-        if (box.type == JP2_IHDR) {
-            l_has_ihdr = 1;
-        }
-
-        p_header_data += l_current_data_size;
-        p_header_size -= box.length;
-    }
-
-    if (l_has_ihdr == 0) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Stream error while reading JP2 Header box: no 'ihdr' box.\n");
-        return OPJ_FALSE;
-    }
-
-    jp2->jp2_state |= JP2_STATE_HEADER;
-    jp2->has_jp2h = 1;
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_jp2_read_boxhdr_char(opj_jp2_box_t *box,
-        OPJ_BYTE * p_data,
-        OPJ_UINT32 * p_number_bytes_read,
-        OPJ_UINT32 p_box_max_size,
-        opj_event_mgr_t * p_manager
-                                        )
-{
-    OPJ_UINT32 l_value;
-
-    /* preconditions */
-    assert(p_data != 00);
-    assert(box != 00);
-    assert(p_number_bytes_read != 00);
-    assert(p_manager != 00);
-
-    if (p_box_max_size < 8) {
-        opj_event_msg(p_manager, EVT_ERROR, "Cannot handle box of less than 8 bytes\n");
-        return OPJ_FALSE;
-    }
-
-    /* process read data */
-    opj_read_bytes(p_data, &l_value, 4);
-    p_data += 4;
-    box->length = (OPJ_UINT32)(l_value);
-
-    opj_read_bytes(p_data, &l_value, 4);
-    p_data += 4;
-    box->type = (OPJ_UINT32)(l_value);
-
-    *p_number_bytes_read = 8;
-
-    /* do we have a "special very large box ?" */
-    /* read then the XLBox */
-    if (box->length == 1) {
-        OPJ_UINT32 l_xl_part_size;
-
-        if (p_box_max_size < 16) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Cannot handle XL box of less than 16 bytes\n");
-            return OPJ_FALSE;
-        }
-
-        opj_read_bytes(p_data, &l_xl_part_size, 4);
-        p_data += 4;
-        *p_number_bytes_read += 4;
-
-        if (l_xl_part_size != 0) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Cannot handle box sizes higher than 2^32\n");
-            return OPJ_FALSE;
-        }
-
-        opj_read_bytes(p_data, &l_value, 4);
-        *p_number_bytes_read += 4;
-        box->length = (OPJ_UINT32)(l_value);
-
-        if (box->length == 0) {
-            opj_event_msg(p_manager, EVT_ERROR, "Cannot handle box of undefined sizes\n");
-            return OPJ_FALSE;
-        }
-    } else if (box->length == 0) {
-        opj_event_msg(p_manager, EVT_ERROR, "Cannot handle box of undefined sizes\n");
-        return OPJ_FALSE;
-    }
-    if (box->length < *p_number_bytes_read) {
-        opj_event_msg(p_manager, EVT_ERROR, "Box length is inconsistent.\n");
-        return OPJ_FALSE;
-    }
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_jp2_read_header(opj_stream_private_t *p_stream,
-                             opj_jp2_t *jp2,
-                             opj_image_t ** p_image,
-                             opj_event_mgr_t * p_manager
-                            )
-{
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(p_stream != 00);
-    assert(p_manager != 00);
-
-    /* customization of the validation */
-    if (! opj_jp2_setup_decoding_validation(jp2, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* customization of the encoding */
-    if (! opj_jp2_setup_header_reading(jp2, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* validation of the parameters codec */
-    if (! opj_jp2_exec(jp2, jp2->m_validation_list, p_stream, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* read header */
-    if (! opj_jp2_exec(jp2, jp2->m_procedure_list, p_stream, p_manager)) {
-        return OPJ_FALSE;
-    }
-    if (jp2->has_jp2h == 0) {
-        opj_event_msg(p_manager, EVT_ERROR, "JP2H box missing. Required.\n");
-        return OPJ_FALSE;
-    }
-    if (jp2->has_ihdr == 0) {
-        opj_event_msg(p_manager, EVT_ERROR, "IHDR box_missing. Required.\n");
-        return OPJ_FALSE;
-    }
-
-    return opj_j2k_read_header(p_stream,
-                               jp2->j2k,
-                               p_image,
-                               p_manager);
-}
-
-static OPJ_BOOL opj_jp2_setup_encoding_validation(opj_jp2_t *jp2,
-        opj_event_mgr_t * p_manager)
-{
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(p_manager != 00);
-
-    if (! opj_procedure_list_add_procedure(jp2->m_validation_list,
-                                           (opj_procedure)opj_jp2_default_validation, p_manager)) {
-        return OPJ_FALSE;
-    }
-    /* DEVELOPER CORNER, add your custom validation procedure */
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_jp2_setup_decoding_validation(opj_jp2_t *jp2,
-        opj_event_mgr_t * p_manager)
-{
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(p_manager != 00);
-
-    OPJ_UNUSED(jp2);
-    OPJ_UNUSED(p_manager);
-
-    /* DEVELOPER CORNER, add your custom validation procedure */
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_jp2_setup_header_writing(opj_jp2_t *jp2,
-        opj_event_mgr_t * p_manager)
-{
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(p_manager != 00);
-
-    if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
-                                           (opj_procedure)opj_jp2_write_jp, p_manager)) {
-        return OPJ_FALSE;
-    }
-    if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
-                                           (opj_procedure)opj_jp2_write_ftyp, p_manager)) {
-        return OPJ_FALSE;
-    }
-    if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
-                                           (opj_procedure)opj_jp2_write_jp2h, p_manager)) {
-        return OPJ_FALSE;
-    }
-    if (jp2->jpip_on) {
-        if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
-                                               (opj_procedure)opj_jpip_skip_iptr, p_manager)) {
-            return OPJ_FALSE;
-        }
-    }
-    if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
-                                           (opj_procedure)opj_jp2_skip_jp2c, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* DEVELOPER CORNER, insert your custom procedures */
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_jp2_setup_header_reading(opj_jp2_t *jp2,
-        opj_event_mgr_t * p_manager)
-{
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(p_manager != 00);
-
-    if (! opj_procedure_list_add_procedure(jp2->m_procedure_list,
-                                           (opj_procedure)opj_jp2_read_header_procedure, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* DEVELOPER CORNER, add your custom procedures */
-
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_jp2_read_tile_header(opj_jp2_t * p_jp2,
-                                  OPJ_UINT32 * p_tile_index,
-                                  OPJ_UINT32 * p_data_size,
-                                  OPJ_INT32 * p_tile_x0,
-                                  OPJ_INT32 * p_tile_y0,
-                                  OPJ_INT32 * p_tile_x1,
-                                  OPJ_INT32 * p_tile_y1,
-                                  OPJ_UINT32 * p_nb_comps,
-                                  OPJ_BOOL * p_go_on,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager
-                                 )
-{
-    return opj_j2k_read_tile_header(p_jp2->j2k,
-                                    p_tile_index,
-                                    p_data_size,
-                                    p_tile_x0, p_tile_y0,
-                                    p_tile_x1, p_tile_y1,
-                                    p_nb_comps,
-                                    p_go_on,
-                                    p_stream,
-                                    p_manager);
-}
-
-OPJ_BOOL opj_jp2_write_tile(opj_jp2_t *p_jp2,
-                            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
-                           )
-
-{
-    return opj_j2k_write_tile(p_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_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
-                            )
-{
-    return opj_j2k_decode_tile(p_jp2->j2k, p_tile_index, p_data, p_data_size,
-                               p_stream, p_manager);
-}
-
-void opj_jp2_destroy(opj_jp2_t *jp2)
-{
-    if (jp2) {
-        /* destroy the J2K codec */
-        opj_j2k_destroy(jp2->j2k);
-        jp2->j2k = 00;
-
-        if (jp2->comps) {
-            opj_free(jp2->comps);
-            jp2->comps = 00;
-        }
-
-        if (jp2->cl) {
-            opj_free(jp2->cl);
-            jp2->cl = 00;
-        }
-
-        if (jp2->color.icc_profile_buf) {
-            opj_free(jp2->color.icc_profile_buf);
-            jp2->color.icc_profile_buf = 00;
-        }
-
-        if (jp2->color.jp2_cdef) {
-            if (jp2->color.jp2_cdef->info) {
-                opj_free(jp2->color.jp2_cdef->info);
-                jp2->color.jp2_cdef->info = NULL;
-            }
-
-            opj_free(jp2->color.jp2_cdef);
-            jp2->color.jp2_cdef = 00;
-        }
-
-        if (jp2->color.jp2_pclr) {
-            if (jp2->color.jp2_pclr->cmap) {
-                opj_free(jp2->color.jp2_pclr->cmap);
-                jp2->color.jp2_pclr->cmap = NULL;
-            }
-            if (jp2->color.jp2_pclr->channel_sign) {
-                opj_free(jp2->color.jp2_pclr->channel_sign);
-                jp2->color.jp2_pclr->channel_sign = NULL;
-            }
-            if (jp2->color.jp2_pclr->channel_size) {
-                opj_free(jp2->color.jp2_pclr->channel_size);
-                jp2->color.jp2_pclr->channel_size = NULL;
-            }
-            if (jp2->color.jp2_pclr->entries) {
-                opj_free(jp2->color.jp2_pclr->entries);
-                jp2->color.jp2_pclr->entries = NULL;
-            }
-
-            opj_free(jp2->color.jp2_pclr);
-            jp2->color.jp2_pclr = 00;
-        }
-
-        if (jp2->m_validation_list) {
-            opj_procedure_list_destroy(jp2->m_validation_list);
-            jp2->m_validation_list = 00;
-        }
-
-        if (jp2->m_procedure_list) {
-            opj_procedure_list_destroy(jp2->m_procedure_list);
-            jp2->m_procedure_list = 00;
-        }
-
-        opj_free(jp2);
-    }
-}
-
-OPJ_BOOL opj_jp2_set_decoded_components(opj_jp2_t *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,
-                                          numcomps, comps_indices,
-                                          p_manager);
-}
-
-OPJ_BOOL opj_jp2_set_decode_area(opj_jp2_t *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,
-                                   p_end_x, p_end_y, p_manager);
-}
-
-OPJ_BOOL opj_jp2_get_tile(opj_jp2_t *p_jp2,
-                          opj_stream_private_t *p_stream,
-                          opj_image_t* p_image,
-                          opj_event_mgr_t * p_manager,
-                          OPJ_UINT32 tile_index
-                         )
-{
-    if (!p_image) {
-        return OPJ_FALSE;
-    }
-
-    opj_event_msg(p_manager, EVT_WARNING,
-                  "JP2 box which are after the codestream will not be read by this function.\n");
-
-    if (! opj_j2k_get_tile(p_jp2->j2k, p_stream, p_image, p_manager, tile_index)) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Failed to decode the codestream in the JP2 file\n");
-        return OPJ_FALSE;
-    }
-
-    if (p_jp2->j2k->m_specific_param.m_decoder.m_numcomps_to_decode) {
-        /* Bypass all JP2 component transforms */
-        return OPJ_TRUE;
-    }
-
-    if (!opj_jp2_check_color(p_image, &(p_jp2->color), p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    /* Set Image Color Space */
-    if (p_jp2->enumcs == 16) {
-        p_image->color_space = OPJ_CLRSPC_SRGB;
-    } else if (p_jp2->enumcs == 17) {
-        p_image->color_space = OPJ_CLRSPC_GRAY;
-    } else if (p_jp2->enumcs == 18) {
-        p_image->color_space = OPJ_CLRSPC_SYCC;
-    } else if (p_jp2->enumcs == 24) {
-        p_image->color_space = OPJ_CLRSPC_EYCC;
-    } else if (p_jp2->enumcs == 12) {
-        p_image->color_space = OPJ_CLRSPC_CMYK;
-    } else {
-        p_image->color_space = OPJ_CLRSPC_UNKNOWN;
-    }
-
-    if (p_jp2->color.jp2_pclr) {
-        /* Part 1, I.5.3.4: Either both or none : */
-        if (!p_jp2->color.jp2_pclr->cmap) {
-            opj_jp2_free_pclr(&(p_jp2->color));
-        } else {
-            if (!opj_jp2_apply_pclr(p_image, &(p_jp2->color), p_manager)) {
-                return OPJ_FALSE;
-            }
-        }
-    }
-
-    /* Apply the color space if needed */
-    if (p_jp2->color.jp2_cdef) {
-        opj_jp2_apply_cdef(p_image, &(p_jp2->color), p_manager);
-    }
-
-    if (p_jp2->color.icc_profile_buf) {
-        p_image->icc_profile_buf = p_jp2->color.icc_profile_buf;
-        p_image->icc_profile_len = p_jp2->color.icc_profile_len;
-        p_jp2->color.icc_profile_buf = NULL;
-    }
-
-    return OPJ_TRUE;
-}
-
-/* ----------------------------------------------------------------------- */
-/* JP2 encoder interface                                             */
-/* ----------------------------------------------------------------------- */
-
-opj_jp2_t* opj_jp2_create(OPJ_BOOL p_is_decoder)
-{
-    opj_jp2_t *jp2 = (opj_jp2_t*)opj_calloc(1, sizeof(opj_jp2_t));
-    if (jp2) {
-
-        /* create the J2K codec */
-        if (! p_is_decoder) {
-            jp2->j2k = opj_j2k_create_compress();
-        } else {
-            jp2->j2k = opj_j2k_create_decompress();
-        }
-
-        if (jp2->j2k == 00) {
-            opj_jp2_destroy(jp2);
-            return 00;
-        }
-
-        /* Color structure */
-        jp2->color.icc_profile_buf = NULL;
-        jp2->color.icc_profile_len = 0;
-        jp2->color.jp2_cdef = NULL;
-        jp2->color.jp2_pclr = NULL;
-        jp2->color.jp2_has_colr = 0;
-
-        /* validation list creation */
-        jp2->m_validation_list = opj_procedure_list_create();
-        if (! jp2->m_validation_list) {
-            opj_jp2_destroy(jp2);
-            return 00;
-        }
-
-        /* execution list creation */
-        jp2->m_procedure_list = opj_procedure_list_create();
-        if (! jp2->m_procedure_list) {
-            opj_jp2_destroy(jp2);
-            return 00;
-        }
-    }
-
-    return jp2;
-}
-
-void jp2_dump(opj_jp2_t* p_jp2, OPJ_INT32 flag, FILE* out_stream)
-{
-    /* preconditions */
-    assert(p_jp2 != 00);
-
-    j2k_dump(p_jp2->j2k,
-             flag,
-             out_stream);
-}
-
-opj_codestream_index_t* jp2_get_cstr_index(opj_jp2_t* p_jp2)
-{
-    return j2k_get_cstr_index(p_jp2->j2k);
-}
-
-opj_codestream_info_v2_t* jp2_get_cstr_info(opj_jp2_t* p_jp2)
-{
-    return j2k_get_cstr_info(p_jp2->j2k);
-}
-
-OPJ_BOOL opj_jp2_set_decoded_resolution_factor(opj_jp2_t *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);
-}
-
-/* JPIP specific */
-
-#ifdef USE_JPIP
-static OPJ_BOOL opj_jpip_write_iptr(opj_jp2_t *jp2,
-                                    opj_stream_private_t *cio,
-                                    opj_event_mgr_t * p_manager)
-{
-    OPJ_OFF_T j2k_codestream_exit;
-    OPJ_BYTE l_data_header [24];
-
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(cio != 00);
-    assert(p_manager != 00);
-    assert(opj_stream_has_seek(cio));
-
-    j2k_codestream_exit = opj_stream_tell(cio);
-    opj_write_bytes(l_data_header, 24, 4); /* size of iptr */
-    opj_write_bytes(l_data_header + 4, JPIP_IPTR,
-                    4);                                      /* IPTR */
-#if 0
-    opj_write_bytes(l_data_header + 4 + 4, 0, 8); /* offset */
-    opj_write_bytes(l_data_header + 8 + 8, 0, 8); /* length */
-#else
-    opj_write_double(l_data_header + 4 + 4, 0); /* offset */
-    opj_write_double(l_data_header + 8 + 8, 0); /* length */
-#endif
-
-    if (! opj_stream_seek(cio, jp2->jpip_iptr_offset, p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
-        return OPJ_FALSE;
-    }
-
-    if (opj_stream_write_data(cio, l_data_header, 24, p_manager) != 24) {
-        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
-        return OPJ_FALSE;
-    }
-
-    if (! opj_stream_seek(cio, j2k_codestream_exit, p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_jpip_write_fidx(opj_jp2_t *jp2,
-                                    opj_stream_private_t *cio,
-                                    opj_event_mgr_t * p_manager)
-{
-    OPJ_OFF_T j2k_codestream_exit;
-    OPJ_BYTE l_data_header [24];
-
-    OPJ_UNUSED(jp2);
-
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(cio != 00);
-    assert(p_manager != 00);
-    assert(opj_stream_has_seek(cio));
-
-    opj_write_bytes(l_data_header, 24, 4); /* size of iptr */
-    opj_write_bytes(l_data_header + 4, JPIP_FIDX,
-                    4);                                      /* IPTR */
-    opj_write_double(l_data_header + 4 + 4, 0); /* offset */
-    opj_write_double(l_data_header + 8 + 8, 0); /* length */
-
-    if (opj_stream_write_data(cio, l_data_header, 24, p_manager) != 24) {
-        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
-        return OPJ_FALSE;
-    }
-
-    j2k_codestream_exit = opj_stream_tell(cio);
-    if (! opj_stream_seek(cio, j2k_codestream_exit, p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_jpip_write_cidx(opj_jp2_t *jp2,
-                                    opj_stream_private_t *cio,
-                                    opj_event_mgr_t * p_manager)
-{
-    OPJ_OFF_T j2k_codestream_exit;
-    OPJ_BYTE l_data_header [24];
-
-    OPJ_UNUSED(jp2);
-
-    /* preconditions */
-    assert(jp2 != 00);
-    assert(cio != 00);
-    assert(p_manager != 00);
-    assert(opj_stream_has_seek(cio));
-
-    j2k_codestream_exit = opj_stream_tell(cio);
-    opj_write_bytes(l_data_header, 24, 4); /* size of iptr */
-    opj_write_bytes(l_data_header + 4, JPIP_CIDX,
-                    4);                                      /* IPTR */
-#if 0
-    opj_write_bytes(l_data_header + 4 + 4, 0, 8); /* offset */
-    opj_write_bytes(l_data_header + 8 + 8, 0, 8); /* length */
-#else
-    opj_write_double(l_data_header + 4 + 4, 0); /* offset */
-    opj_write_double(l_data_header + 8 + 8, 0); /* length */
-#endif
-
-    if (! opj_stream_seek(cio, j2k_codestream_exit, p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
-        return OPJ_FALSE;
-    }
-
-    if (opj_stream_write_data(cio, l_data_header, 24, p_manager) != 24) {
-        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
-        return OPJ_FALSE;
-    }
-
-    j2k_codestream_exit = opj_stream_tell(cio);
-    if (! opj_stream_seek(cio, j2k_codestream_exit, p_manager)) {
-        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-#if 0
-static void write_prxy(int offset_jp2c, int length_jp2c, int offset_idx,
-                       int length_idx, opj_stream_private_t *cio,
-                       opj_event_mgr_t * p_manager)
-{
-    OPJ_BYTE l_data_header [8];
-    OPJ_OFF_T len, lenp;
-
-    lenp = opj_stream_tell(cio);
-    opj_stream_skip(cio, 4, p_manager);         /* L [at the end] */
-    opj_write_bytes(l_data_header, JPIP_PRXY, 4); /* IPTR           */
-    opj_stream_write_data(cio, l_data_header, 4, p_manager);
-
-    opj_write_bytes(l_data_header, offset_jp2c, 8);  /* OOFF           */
-    opj_stream_write_data(cio, l_data_header, 8, p_manager);
-    opj_write_bytes(l_data_header, length_jp2c, 4);  /* OBH part 1     */
-    opj_write_bytes(l_data_header + 4, JP2_JP2C, 4); /* OBH part 2     */
-    opj_stream_write_data(cio, l_data_header, 8, p_manager);
-
-    opj_write_bytes(l_data_header, 1, 1); /* NI             */
-    opj_stream_write_data(cio, l_data_header, 1, p_manager);
-
-    opj_write_bytes(l_data_header, offset_idx, 8);   /* IOFF           */
-    opj_stream_write_data(cio, l_data_header, 8, p_manager);
-    opj_write_bytes(l_data_header, length_idx, 4);   /* IBH part 1     */
-    opj_write_bytes(l_data_header + 4, JPIP_CIDX, 4);  /* IBH part 2     */
-    opj_stream_write_data(cio, l_data_header, 8, p_manager);
-
-    len = opj_stream_tell(cio) - lenp;
-    opj_stream_skip(cio, lenp, p_manager);
-    opj_write_bytes(l_data_header, len, 4); /* L              */
-    opj_stream_write_data(cio, l_data_header, 4, p_manager);
-    opj_stream_seek(cio, lenp + len, p_manager);
-}
-#endif
-
-
-#if 0
-static int write_fidx(int offset_jp2c, int length_jp2c, int offset_idx,
-                      int length_idx, opj_stream_private_t *cio,
-                      opj_event_mgr_t * p_manager)
-{
-    OPJ_BYTE l_data_header [4];
-    OPJ_OFF_T len, lenp;
-
-    lenp = opj_stream_tell(cio);
-    opj_stream_skip(cio, 4, p_manager);
-    opj_write_bytes(l_data_header, JPIP_FIDX, 4); /* FIDX */
-    opj_stream_write_data(cio, l_data_header, 4, p_manager);
-
-    write_prxy(offset_jp2c, length_jp2c, offset_idx, length_idx, cio, p_manager);
-
-    len = opj_stream_tell(cio) - lenp;
-    opj_stream_skip(cio, lenp, p_manager);
-    opj_write_bytes(l_data_header, len, 4); /* L              */
-    opj_stream_write_data(cio, l_data_header, 4, p_manager);
-    opj_stream_seek(cio, lenp + len, p_manager);
-
-    return len;
-}
-#endif
-#endif /* USE_JPIP */
diff --git a/third_party/libopenjpeg20/jp2.h b/third_party/libopenjpeg20/jp2.h
deleted file mode 100644
index 34abd51..0000000
--- a/third_party/libopenjpeg20/jp2.h
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
- * Copyright (c) 2012, CS Systemes d'Information, France
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-#ifndef OPJ_JP2_H
-#define OPJ_JP2_H
-/**
-@file jp2.h
-@brief The JPEG-2000 file format Reader/Writer (JP2)
-
-*/
-
-/** @defgroup JP2 JP2 - JPEG-2000 file format reader/writer */
-/*@{*/
-
-/*#define JPIP_JPIP 0x6a706970*/
-
-#define     JP2_JP   0x6a502020    /**< JPEG 2000 signature box */
-#define     JP2_FTYP 0x66747970    /**< File type box */
-#define     JP2_JP2H 0x6a703268    /**< JP2 header box (super-box) */
-#define     JP2_IHDR 0x69686472    /**< Image header box */
-#define     JP2_COLR 0x636f6c72    /**< Colour specification box */
-#define     JP2_JP2C 0x6a703263    /**< Contiguous codestream box */
-#define     JP2_URL  0x75726c20    /**< Data entry URL box */
-#define     JP2_PCLR 0x70636c72    /**< Palette box */
-#define     JP2_CMAP 0x636d6170    /**< Component Mapping box */
-#define     JP2_CDEF 0x63646566    /**< Channel Definition box */
-#define     JP2_DTBL 0x6474626c    /**< Data Reference box */
-#define     JP2_BPCC 0x62706363    /**< Bits per component box */
-#define     JP2_JP2  0x6a703220    /**< File type fields */
-
-/* For the future */
-/* #define JP2_RES 0x72657320 */  /**< Resolution box (super-box) */
-/* #define JP2_JP2I 0x6a703269 */  /**< Intellectual property box */
-/* #define JP2_XML  0x786d6c20 */  /**< XML box */
-/* #define JP2_UUID 0x75756994 */  /**< UUID box */
-/* #define JP2_UINF 0x75696e66 */  /**< UUID info box (super-box) */
-/* #define JP2_ULST 0x756c7374 */  /**< UUID list box */
-
-/* ----------------------------------------------------------------------- */
-
-typedef enum {
-    JP2_STATE_NONE            = 0x0,
-    JP2_STATE_SIGNATURE       = 0x1,
-    JP2_STATE_FILE_TYPE       = 0x2,
-    JP2_STATE_HEADER          = 0x4,
-    JP2_STATE_CODESTREAM      = 0x8,
-    JP2_STATE_END_CODESTREAM  = 0x10,
-    JP2_STATE_UNKNOWN         = 0x7fffffff /* ISO C restricts enumerator values to range of 'int' */
-}
-JP2_STATE;
-
-typedef enum {
-    JP2_IMG_STATE_NONE        = 0x0,
-    JP2_IMG_STATE_UNKNOWN     = 0x7fffffff
-}
-JP2_IMG_STATE;
-
-/**
-Channel description: channel index, type, association
-*/
-typedef struct opj_jp2_cdef_info {
-    OPJ_UINT16 cn, typ, asoc;
-} opj_jp2_cdef_info_t;
-
-/**
-Channel descriptions and number of descriptions
-*/
-typedef struct opj_jp2_cdef {
-    opj_jp2_cdef_info_t *info;
-    OPJ_UINT16 n;
-} opj_jp2_cdef_t;
-
-/**
-Component mappings: channel index, mapping type, palette index
-*/
-typedef struct opj_jp2_cmap_comp {
-    OPJ_UINT16 cmp;
-    OPJ_BYTE mtyp, pcol;
-} opj_jp2_cmap_comp_t;
-
-/**
-Palette data: table entries, palette columns
-*/
-typedef struct opj_jp2_pclr {
-    OPJ_UINT32 *entries;
-    OPJ_BYTE *channel_sign;
-    OPJ_BYTE *channel_size;
-    opj_jp2_cmap_comp_t *cmap;
-    OPJ_UINT16 nr_entries;
-    OPJ_BYTE nr_channels;
-} opj_jp2_pclr_t;
-
-/**
-Collector for ICC profile, palette, component mapping, channel description
-*/
-typedef struct opj_jp2_color {
-    OPJ_BYTE *icc_profile_buf;
-    OPJ_UINT32 icc_profile_len;
-
-    opj_jp2_cdef_t *jp2_cdef;
-    opj_jp2_pclr_t *jp2_pclr;
-    OPJ_BYTE jp2_has_colr;
-} opj_jp2_color_t;
-
-/**
-JP2 component
-*/
-typedef struct opj_jp2_comps {
-    OPJ_UINT32 depth;
-    OPJ_UINT32 sgnd;
-    OPJ_UINT32 bpcc;
-} opj_jp2_comps_t;
-
-/**
-JPEG-2000 file format reader/writer
-*/
-typedef struct opj_jp2 {
-    /** handle to the J2K codec  */
-    opj_j2k_t *j2k;
-    /** list of validation procedures */
-    struct opj_procedure_list * m_validation_list;
-    /** list of execution procedures */
-    struct opj_procedure_list * m_procedure_list;
-
-    /* width of image */
-    OPJ_UINT32 w;
-    /* height of image */
-    OPJ_UINT32 h;
-    /* number of components in the image */
-    OPJ_UINT32 numcomps;
-    OPJ_UINT32 bpc;
-    OPJ_UINT32 C;
-    OPJ_UINT32 UnkC;
-    OPJ_UINT32 IPR;
-    OPJ_UINT32 meth;
-    OPJ_UINT32 approx;
-    OPJ_UINT32 enumcs;
-    OPJ_UINT32 precedence;
-    OPJ_UINT32 brand;
-    OPJ_UINT32 minversion;
-    OPJ_UINT32 numcl;
-    OPJ_UINT32 *cl;
-    opj_jp2_comps_t *comps;
-    /* FIXME: The following two variables are used to save offset
-      as we write out a JP2 file to disk. This mechanism is not flexible
-      as codec writers will need to extand those fields as new part
-      of the standard are implemented.
-    */
-    OPJ_OFF_T j2k_codestream_offset;
-    OPJ_OFF_T jpip_iptr_offset;
-    OPJ_BOOL jpip_on;
-    OPJ_UINT32 jp2_state;
-    OPJ_UINT32 jp2_img_state;
-
-    opj_jp2_color_t color;
-
-    OPJ_BOOL ignore_pclr_cmap_cdef;
-    OPJ_BYTE has_jp2h;
-    OPJ_BYTE has_ihdr;
-}
-opj_jp2_t;
-
-/**
-JP2 Box
-*/
-typedef struct opj_jp2_box {
-    OPJ_UINT32 length;
-    OPJ_UINT32 type;
-    OPJ_INT32 init_pos;
-} opj_jp2_box_t;
-
-typedef struct opj_jp2_header_handler {
-    /* marker value */
-    OPJ_UINT32 id;
-    /* action linked to the marker */
-    OPJ_BOOL(*handler)(opj_jp2_t *jp2,
-                       OPJ_BYTE *p_header_data,
-                       OPJ_UINT32 p_header_size,
-                       opj_event_mgr_t * p_manager);
-}
-opj_jp2_header_handler_t;
-
-
-typedef struct opj_jp2_img_header_writer_handler {
-    /* action to perform */
-    OPJ_BYTE*   (*handler)(opj_jp2_t *jp2, OPJ_UINT32 * p_data_size);
-    /* result of the action : data */
-    OPJ_BYTE*   m_data;
-    /* size of data */
-    OPJ_UINT32  m_size;
-}
-opj_jp2_img_header_writer_handler_t;
-
-/** @name Exported functions */
-/*@{*/
-/* ----------------------------------------------------------------------- */
-
-/**
-Setup the decoder decoding parameters using user parameters.
-Decoding parameters are returned in jp2->j2k->cp.
-@param jp2 JP2 decompressor handle
-@param parameters decompression parameters
-*/
-void opj_jp2_setup_decoder(opj_jp2_t *jp2, opj_dparameters_t *parameters);
-
-/** Allocates worker threads for the compressor/decompressor.
- *
- * @param 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);
-
-/**
- * Decode an image from a JPEG-2000 file stream
- * @param 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_stream_private_t *p_stream,
-                        opj_image_t* p_image,
-                        opj_event_mgr_t * p_manager);
-
-/**
- * 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 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_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 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_stream_private_t *stream,
-                        opj_event_mgr_t * p_manager);
-
-
-/**
- * Starts a compression scheme, i.e. validates the codec parameters, writes the header.
- *
- * @param  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_stream_private_t *stream,
-                                opj_image_t * p_image,
-                                opj_event_mgr_t * p_manager);
-
-
-/**
- * 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_stream_private_t *cio,
-                              opj_event_mgr_t * p_manager);
-
-/* ----------------------------------------------------------------------- */
-
-/**
- * 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_stream_private_t *cio,
-                                opj_event_mgr_t * p_manager);
-
-/**
- * Reads a jpeg2000 file header structure.
- *
- * @param p_stream the stream to read data from.
- * @param 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,
-                             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 numcomps Number of components to decode.
- * @param comps_indices Array of num_compts indices (numbering starting at 0)
- *                     corresponding to the components to decode.
- * @param p_manager Event manager;
- *
- * @return OPJ_TRUE in case of success.
- */
-OPJ_BOOL opj_jp2_set_decoded_components(opj_jp2_t *jp2,
-                                        OPJ_UINT32 numcomps,
-                                        const OPJ_UINT32* comps_indices,
-                                        opj_event_mgr_t * p_manager);
-
-/**
- * Reads a tile header.
- * @param  p_jp2         the jpeg2000 codec.
- * @param  p_tile_index  FIXME DOC
- * @param  p_data_size   FIXME DOC
- * @param  p_tile_x0     FIXME DOC
- * @param  p_tile_y0     FIXME DOC
- * @param  p_tile_x1     FIXME DOC
- * @param  p_tile_y1     FIXME DOC
- * @param  p_nb_comps    FIXME DOC
- * @param  p_go_on       FIXME DOC
- * @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_UINT32 * p_tile_index,
-                                  OPJ_UINT32 * p_data_size,
-                                  OPJ_INT32 * p_tile_x0,
-                                  OPJ_INT32 * p_tile_y0,
-                                  OPJ_INT32 * p_tile_x1,
-                                  OPJ_INT32 * p_tile_y1,
-                                  OPJ_UINT32 * p_nb_comps,
-                                  OPJ_BOOL * p_go_on,
-                                  opj_stream_private_t *p_stream,
-                                  opj_event_mgr_t * p_manager);
-
-/**
- * Writes a tile.
- *
- * @param  p_jp2    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_jp2_write_tile(opj_jp2_t *p_jp2,
-                            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);
-
-/**
- * Decode tile data.
- * @param  p_jp2    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.
- *
- * @return FIXME DOC
- */
-OPJ_BOOL opj_jp2_decode_tile(opj_jp2_t * p_jp2,
-                             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);
-
-/**
- * Creates a jpeg2000 file decompressor.
- *
- * @return  an empty jpeg2000 file codec.
- */
-opj_jp2_t* opj_jp2_create(OPJ_BOOL p_is_decoder);
-
-/**
-Destroy a JP2 decompressor handle
-@param jp2 JP2 decompressor handle to destroy
-*/
-void opj_jp2_destroy(opj_jp2_t *jp2);
-
-
-/**
- * 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_jp2      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).
- * @param  p_end_x      the right position of the rectangle to decode (in image coordinates).
- * @param  p_end_y      the bottom position of the rectangle to decode (in image coordinates).
- * @param  p_manager    the user event manager
- *
- * @return  true      if the area could be set.
- */
-OPJ_BOOL opj_jp2_set_decode_area(opj_jp2_t *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);
-
-/**
-*
-*/
-OPJ_BOOL opj_jp2_get_tile(opj_jp2_t *p_jp2,
-                          opj_stream_private_t *p_stream,
-                          opj_image_t* p_image,
-                          opj_event_mgr_t * p_manager,
-                          OPJ_UINT32 tile_index);
-
-
-/**
- *
- */
-OPJ_BOOL opj_jp2_set_decoded_resolution_factor(opj_jp2_t *p_jp2,
-        OPJ_UINT32 res_factor,
-        opj_event_mgr_t * p_manager);
-
-
-/* TODO MSD: clean these 3 functions */
-/**
- * Dump some elements from the JP2 decompression structure .
- *
- *@param p_jp2        the jp2 codec.
- *@param flag        flag to describe what elements are dump.
- *@param out_stream      output stream where dump the elements.
- *
-*/
-void jp2_dump(opj_jp2_t* p_jp2, OPJ_INT32 flag, FILE* out_stream);
-
-/**
- * Get the codestream info from a JPEG2000 codec.
- *
- *@param  p_jp2        jp2 codec.
- *
- *@return  the codestream information extract from the jpg2000 codec
- */
-opj_codestream_info_v2_t* jp2_get_cstr_info(opj_jp2_t* p_jp2);
-
-/**
- * Get the codestream index from a JPEG2000 codec.
- *
- *@param  p_jp2        jp2 codec.
- *
- *@return  the codestream index extract from the jpg2000 codec
- */
-opj_codestream_index_t* jp2_get_cstr_index(opj_jp2_t* p_jp2);
-
-
-/*@}*/
-
-/*@}*/
-
-#endif /* OPJ_JP2_H */
-
diff --git a/third_party/libopenjpeg20/mct.c b/third_party/libopenjpeg20/mct.c
deleted file mode 100644
index 81ec223..0000000
--- a/third_party/libopenjpeg20/mct.c
+++ /dev/null
@@ -1,570 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
- * Copyright (c) 2012, CS Systemes d'Information, France
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#if defined(__SSE__) && !defined(_M_IX86) && !defined(__i386)
-#define USE_SSE
-#include <xmmintrin.h>
-#endif
-#if defined(__SSE2__) && !defined(_M_IX86) && !defined(__i386)
-#define USE_SSE2
-#include <emmintrin.h>
-#endif
-#if defined(__SSE4_1__) && !defined(_M_IX86) && !defined(__i386)
-#define USE_SSE4
-#include <smmintrin.h>
-#endif
-
-#include "opj_includes.h"
-
-/* <summary> */
-/* This table contains the norms of the basis function of the reversible MCT. */
-/* </summary> */
-static const OPJ_FLOAT64 opj_mct_norms[3] = { 1.732, .8292, .8292 };
-
-/* <summary> */
-/* This table contains the norms of the basis function of the irreversible MCT. */
-/* </summary> */
-static const OPJ_FLOAT64 opj_mct_norms_real[3] = { 1.732, 1.805, 1.573 };
-
-const OPJ_FLOAT64 * opj_mct_get_mct_norms()
-{
-    return opj_mct_norms;
-}
-
-const OPJ_FLOAT64 * opj_mct_get_mct_norms_real()
-{
-    return opj_mct_norms_real;
-}
-
-/* <summary> */
-/* Forward reversible MCT. */
-/* </summary> */
-#ifdef USE_SSE2
-void opj_mct_encode(
-    OPJ_INT32* OPJ_RESTRICT c0,
-    OPJ_INT32* OPJ_RESTRICT c1,
-    OPJ_INT32* OPJ_RESTRICT c2,
-    OPJ_SIZE_T n)
-{
-    OPJ_SIZE_T i;
-    const OPJ_SIZE_T len = n;
-    /* buffer are aligned on 16 bytes */
-    assert(((size_t)c0 & 0xf) == 0);
-    assert(((size_t)c1 & 0xf) == 0);
-    assert(((size_t)c2 & 0xf) == 0);
-
-    for (i = 0; i < (len & ~3U); i += 4) {
-        __m128i y, u, v;
-        __m128i r = _mm_load_si128((const __m128i *) & (c0[i]));
-        __m128i g = _mm_load_si128((const __m128i *) & (c1[i]));
-        __m128i b = _mm_load_si128((const __m128i *) & (c2[i]));
-        y = _mm_add_epi32(g, g);
-        y = _mm_add_epi32(y, b);
-        y = _mm_add_epi32(y, r);
-        y = _mm_srai_epi32(y, 2);
-        u = _mm_sub_epi32(b, g);
-        v = _mm_sub_epi32(r, g);
-        _mm_store_si128((__m128i *) & (c0[i]), y);
-        _mm_store_si128((__m128i *) & (c1[i]), u);
-        _mm_store_si128((__m128i *) & (c2[i]), v);
-    }
-
-    for (; i < len; ++i) {
-        OPJ_INT32 r = c0[i];
-        OPJ_INT32 g = c1[i];
-        OPJ_INT32 b = c2[i];
-        OPJ_INT32 y = (r + (g * 2) + b) >> 2;
-        OPJ_INT32 u = b - g;
-        OPJ_INT32 v = r - g;
-        c0[i] = y;
-        c1[i] = u;
-        c2[i] = v;
-    }
-}
-#else
-void opj_mct_encode(
-    OPJ_INT32* OPJ_RESTRICT c0,
-    OPJ_INT32* OPJ_RESTRICT c1,
-    OPJ_INT32* OPJ_RESTRICT c2,
-    OPJ_SIZE_T n)
-{
-    OPJ_SIZE_T i;
-    const OPJ_SIZE_T len = n;
-
-    for (i = 0; i < len; ++i) {
-        OPJ_INT32 r = c0[i];
-        OPJ_INT32 g = c1[i];
-        OPJ_INT32 b = c2[i];
-        OPJ_INT32 y = (r + (g * 2) + b) >> 2;
-        OPJ_INT32 u = b - g;
-        OPJ_INT32 v = r - g;
-        c0[i] = y;
-        c1[i] = u;
-        c2[i] = v;
-    }
-}
-#endif
-
-/* <summary> */
-/* Inverse reversible MCT. */
-/* </summary> */
-#ifdef USE_SSE2
-void opj_mct_decode(
-    OPJ_INT32* OPJ_RESTRICT c0,
-    OPJ_INT32* OPJ_RESTRICT c1,
-    OPJ_INT32* OPJ_RESTRICT c2,
-    OPJ_SIZE_T n)
-{
-    OPJ_SIZE_T i;
-    const OPJ_SIZE_T len = n;
-
-    for (i = 0; i < (len & ~3U); i += 4) {
-        __m128i r, g, b;
-        __m128i y = _mm_load_si128((const __m128i *) & (c0[i]));
-        __m128i u = _mm_load_si128((const __m128i *) & (c1[i]));
-        __m128i v = _mm_load_si128((const __m128i *) & (c2[i]));
-        g = y;
-        g = _mm_sub_epi32(g, _mm_srai_epi32(_mm_add_epi32(u, v), 2));
-        r = _mm_add_epi32(v, g);
-        b = _mm_add_epi32(u, g);
-        _mm_store_si128((__m128i *) & (c0[i]), r);
-        _mm_store_si128((__m128i *) & (c1[i]), g);
-        _mm_store_si128((__m128i *) & (c2[i]), b);
-    }
-    for (; i < len; ++i) {
-        OPJ_INT32 y = c0[i];
-        OPJ_INT32 u = c1[i];
-        OPJ_INT32 v = c2[i];
-        OPJ_INT32 g = y - ((u + v) >> 2);
-        OPJ_INT32 r = v + g;
-        OPJ_INT32 b = u + g;
-        c0[i] = r;
-        c1[i] = g;
-        c2[i] = b;
-    }
-}
-#else
-void opj_mct_decode(
-    OPJ_INT32* OPJ_RESTRICT c0,
-    OPJ_INT32* OPJ_RESTRICT c1,
-    OPJ_INT32* OPJ_RESTRICT c2,
-    OPJ_SIZE_T n)
-{
-    OPJ_UINT32 i;
-    for (i = 0; i < n; ++i) {
-        OPJ_INT32 y = c0[i];
-        OPJ_INT32 u = c1[i];
-        OPJ_INT32 v = c2[i];
-        OPJ_INT32 g = y - ((u + v) >> 2);
-        OPJ_INT32 r = v + g;
-        OPJ_INT32 b = u + g;
-        c0[i] = r;
-        c1[i] = g;
-        c2[i] = b;
-    }
-}
-#endif
-
-/* <summary> */
-/* Get norm of basis function of reversible MCT. */
-/* </summary> */
-OPJ_FLOAT64 opj_mct_getnorm(OPJ_UINT32 compno)
-{
-    return opj_mct_norms[compno];
-}
-
-/* <summary> */
-/* Forward irreversible MCT. */
-/* </summary> */
-#ifdef USE_SSE4
-void opj_mct_encode_real(
-    OPJ_INT32* OPJ_RESTRICT c0,
-    OPJ_INT32* OPJ_RESTRICT c1,
-    OPJ_INT32* OPJ_RESTRICT c2,
-    OPJ_SIZE_T n)
-{
-    OPJ_SIZE_T i;
-    const OPJ_SIZE_T len = n;
-
-    const __m128i ry = _mm_set1_epi32(2449);
-    const __m128i gy = _mm_set1_epi32(4809);
-    const __m128i by = _mm_set1_epi32(934);
-    const __m128i ru = _mm_set1_epi32(1382);
-    const __m128i gu = _mm_set1_epi32(2714);
-    /* const __m128i bu = _mm_set1_epi32(4096); */
-    /* const __m128i rv = _mm_set1_epi32(4096); */
-    const __m128i gv = _mm_set1_epi32(3430);
-    const __m128i bv = _mm_set1_epi32(666);
-    const __m128i mulround = _mm_shuffle_epi32(_mm_cvtsi32_si128(4096),
-                             _MM_SHUFFLE(1, 0, 1, 0));
-
-    for (i = 0; i < (len & ~3U); i += 4) {
-        __m128i lo, hi;
-        __m128i y, u, v;
-        __m128i r = _mm_load_si128((const __m128i *) & (c0[i]));
-        __m128i g = _mm_load_si128((const __m128i *) & (c1[i]));
-        __m128i b = _mm_load_si128((const __m128i *) & (c2[i]));
-
-        lo = r;
-        hi = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 3, 1, 1));
-        lo = _mm_mul_epi32(lo, ry);
-        hi = _mm_mul_epi32(hi, ry);
-        lo = _mm_add_epi64(lo, mulround);
-        hi = _mm_add_epi64(hi, mulround);
-        lo = _mm_srli_epi64(lo, 13);
-        hi = _mm_slli_epi64(hi, 32 - 13);
-        y = _mm_blend_epi16(lo, hi, 0xCC);
-
-        lo = g;
-        hi = _mm_shuffle_epi32(g, _MM_SHUFFLE(3, 3, 1, 1));
-        lo = _mm_mul_epi32(lo, gy);
-        hi = _mm_mul_epi32(hi, gy);
-        lo = _mm_add_epi64(lo, mulround);
-        hi = _mm_add_epi64(hi, mulround);
-        lo = _mm_srli_epi64(lo, 13);
-        hi = _mm_slli_epi64(hi, 32 - 13);
-        y = _mm_add_epi32(y, _mm_blend_epi16(lo, hi, 0xCC));
-
-        lo = b;
-        hi = _mm_shuffle_epi32(b, _MM_SHUFFLE(3, 3, 1, 1));
-        lo = _mm_mul_epi32(lo, by);
-        hi = _mm_mul_epi32(hi, by);
-        lo = _mm_add_epi64(lo, mulround);
-        hi = _mm_add_epi64(hi, mulround);
-        lo = _mm_srli_epi64(lo, 13);
-        hi = _mm_slli_epi64(hi, 32 - 13);
-        y = _mm_add_epi32(y, _mm_blend_epi16(lo, hi, 0xCC));
-        _mm_store_si128((__m128i *) & (c0[i]), y);
-
-        /*lo = b;
-        hi = _mm_shuffle_epi32(b, _MM_SHUFFLE(3, 3, 1, 1));
-        lo = _mm_mul_epi32(lo, mulround);
-        hi = _mm_mul_epi32(hi, mulround);*/
-        lo = _mm_cvtepi32_epi64(_mm_shuffle_epi32(b, _MM_SHUFFLE(3, 2, 2, 0)));
-        hi = _mm_cvtepi32_epi64(_mm_shuffle_epi32(b, _MM_SHUFFLE(3, 2, 3, 1)));
-        lo = _mm_slli_epi64(lo, 12);
-        hi = _mm_slli_epi64(hi, 12);
-        lo = _mm_add_epi64(lo, mulround);
-        hi = _mm_add_epi64(hi, mulround);
-        lo = _mm_srli_epi64(lo, 13);
-        hi = _mm_slli_epi64(hi, 32 - 13);
-        u = _mm_blend_epi16(lo, hi, 0xCC);
-
-        lo = r;
-        hi = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 3, 1, 1));
-        lo = _mm_mul_epi32(lo, ru);
-        hi = _mm_mul_epi32(hi, ru);
-        lo = _mm_add_epi64(lo, mulround);
-        hi = _mm_add_epi64(hi, mulround);
-        lo = _mm_srli_epi64(lo, 13);
-        hi = _mm_slli_epi64(hi, 32 - 13);
-        u = _mm_sub_epi32(u, _mm_blend_epi16(lo, hi, 0xCC));
-
-        lo = g;
-        hi = _mm_shuffle_epi32(g, _MM_SHUFFLE(3, 3, 1, 1));
-        lo = _mm_mul_epi32(lo, gu);
-        hi = _mm_mul_epi32(hi, gu);
-        lo = _mm_add_epi64(lo, mulround);
-        hi = _mm_add_epi64(hi, mulround);
-        lo = _mm_srli_epi64(lo, 13);
-        hi = _mm_slli_epi64(hi, 32 - 13);
-        u = _mm_sub_epi32(u, _mm_blend_epi16(lo, hi, 0xCC));
-        _mm_store_si128((__m128i *) & (c1[i]), u);
-
-        /*lo = r;
-        hi = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 3, 1, 1));
-        lo = _mm_mul_epi32(lo, mulround);
-        hi = _mm_mul_epi32(hi, mulround);*/
-        lo = _mm_cvtepi32_epi64(_mm_shuffle_epi32(r, _MM_SHUFFLE(3, 2, 2, 0)));
-        hi = _mm_cvtepi32_epi64(_mm_shuffle_epi32(r, _MM_SHUFFLE(3, 2, 3, 1)));
-        lo = _mm_slli_epi64(lo, 12);
-        hi = _mm_slli_epi64(hi, 12);
-        lo = _mm_add_epi64(lo, mulround);
-        hi = _mm_add_epi64(hi, mulround);
-        lo = _mm_srli_epi64(lo, 13);
-        hi = _mm_slli_epi64(hi, 32 - 13);
-        v = _mm_blend_epi16(lo, hi, 0xCC);
-
-        lo = g;
-        hi = _mm_shuffle_epi32(g, _MM_SHUFFLE(3, 3, 1, 1));
-        lo = _mm_mul_epi32(lo, gv);
-        hi = _mm_mul_epi32(hi, gv);
-        lo = _mm_add_epi64(lo, mulround);
-        hi = _mm_add_epi64(hi, mulround);
-        lo = _mm_srli_epi64(lo, 13);
-        hi = _mm_slli_epi64(hi, 32 - 13);
-        v = _mm_sub_epi32(v, _mm_blend_epi16(lo, hi, 0xCC));
-
-        lo = b;
-        hi = _mm_shuffle_epi32(b, _MM_SHUFFLE(3, 3, 1, 1));
-        lo = _mm_mul_epi32(lo, bv);
-        hi = _mm_mul_epi32(hi, bv);
-        lo = _mm_add_epi64(lo, mulround);
-        hi = _mm_add_epi64(hi, mulround);
-        lo = _mm_srli_epi64(lo, 13);
-        hi = _mm_slli_epi64(hi, 32 - 13);
-        v = _mm_sub_epi32(v, _mm_blend_epi16(lo, hi, 0xCC));
-        _mm_store_si128((__m128i *) & (c2[i]), v);
-    }
-    for (; i < len; ++i) {
-        OPJ_INT32 r = c0[i];
-        OPJ_INT32 g = c1[i];
-        OPJ_INT32 b = c2[i];
-        OPJ_INT32 y =  opj_int_fix_mul(r, 2449) + opj_int_fix_mul(g,
-                       4809) + opj_int_fix_mul(b, 934);
-        OPJ_INT32 u = -opj_int_fix_mul(r, 1382) - opj_int_fix_mul(g,
-                      2714) + opj_int_fix_mul(b, 4096);
-        OPJ_INT32 v =  opj_int_fix_mul(r, 4096) - opj_int_fix_mul(g,
-                       3430) - opj_int_fix_mul(b, 666);
-        c0[i] = y;
-        c1[i] = u;
-        c2[i] = v;
-    }
-}
-#else
-void opj_mct_encode_real(
-    OPJ_INT32* OPJ_RESTRICT c0,
-    OPJ_INT32* OPJ_RESTRICT c1,
-    OPJ_INT32* OPJ_RESTRICT c2,
-    OPJ_SIZE_T n)
-{
-    OPJ_UINT32 i;
-    for (i = 0; i < n; ++i) {
-        OPJ_INT32 r = c0[i];
-        OPJ_INT32 g = c1[i];
-        OPJ_INT32 b = c2[i];
-        OPJ_INT32 y =  opj_int_fix_mul(r, 2449) + opj_int_fix_mul(g,
-                       4809) + opj_int_fix_mul(b, 934);
-        OPJ_INT32 u = -opj_int_fix_mul(r, 1382) - opj_int_fix_mul(g,
-                      2714) + opj_int_fix_mul(b, 4096);
-        OPJ_INT32 v =  opj_int_fix_mul(r, 4096) - opj_int_fix_mul(g,
-                       3430) - opj_int_fix_mul(b, 666);
-        c0[i] = y;
-        c1[i] = u;
-        c2[i] = v;
-    }
-}
-#endif
-
-/* <summary> */
-/* Inverse irreversible MCT. */
-/* </summary> */
-void opj_mct_decode_real(
-    OPJ_FLOAT32* OPJ_RESTRICT c0,
-    OPJ_FLOAT32* OPJ_RESTRICT c1,
-    OPJ_FLOAT32* OPJ_RESTRICT c2,
-    OPJ_SIZE_T n)
-{
-    OPJ_UINT32 i;
-#ifdef USE_SSE
-    __m128 vrv, vgu, vgv, vbu;
-    vrv = _mm_set1_ps(1.402f);
-    vgu = _mm_set1_ps(0.34413f);
-    vgv = _mm_set1_ps(0.71414f);
-    vbu = _mm_set1_ps(1.772f);
-    for (i = 0; i < (n >> 3); ++i) {
-        __m128 vy, vu, vv;
-        __m128 vr, vg, vb;
-
-        vy = _mm_load_ps(c0);
-        vu = _mm_load_ps(c1);
-        vv = _mm_load_ps(c2);
-        vr = _mm_add_ps(vy, _mm_mul_ps(vv, vrv));
-        vg = _mm_sub_ps(_mm_sub_ps(vy, _mm_mul_ps(vu, vgu)), _mm_mul_ps(vv, vgv));
-        vb = _mm_add_ps(vy, _mm_mul_ps(vu, vbu));
-        _mm_store_ps(c0, vr);
-        _mm_store_ps(c1, vg);
-        _mm_store_ps(c2, vb);
-        c0 += 4;
-        c1 += 4;
-        c2 += 4;
-
-        vy = _mm_load_ps(c0);
-        vu = _mm_load_ps(c1);
-        vv = _mm_load_ps(c2);
-        vr = _mm_add_ps(vy, _mm_mul_ps(vv, vrv));
-        vg = _mm_sub_ps(_mm_sub_ps(vy, _mm_mul_ps(vu, vgu)), _mm_mul_ps(vv, vgv));
-        vb = _mm_add_ps(vy, _mm_mul_ps(vu, vbu));
-        _mm_store_ps(c0, vr);
-        _mm_store_ps(c1, vg);
-        _mm_store_ps(c2, vb);
-        c0 += 4;
-        c1 += 4;
-        c2 += 4;
-    }
-    n &= 7;
-#endif
-    for (i = 0; i < n; ++i) {
-        OPJ_FLOAT32 y = c0[i];
-        OPJ_FLOAT32 u = c1[i];
-        OPJ_FLOAT32 v = c2[i];
-        OPJ_FLOAT32 r = y + (v * 1.402f);
-        OPJ_FLOAT32 g = y - (u * 0.34413f) - (v * (0.71414f));
-        OPJ_FLOAT32 b = y + (u * 1.772f);
-        c0[i] = r;
-        c1[i] = g;
-        c2[i] = b;
-    }
-}
-
-/* <summary> */
-/* Get norm of basis function of irreversible MCT. */
-/* </summary> */
-OPJ_FLOAT64 opj_mct_getnorm_real(OPJ_UINT32 compno)
-{
-    return opj_mct_norms_real[compno];
-}
-
-
-OPJ_BOOL opj_mct_encode_custom(
-    OPJ_BYTE * pCodingdata,
-    OPJ_SIZE_T n,
-    OPJ_BYTE ** pData,
-    OPJ_UINT32 pNbComp,
-    OPJ_UINT32 isSigned)
-{
-    OPJ_FLOAT32 * lMct = (OPJ_FLOAT32 *) pCodingdata;
-    OPJ_SIZE_T i;
-    OPJ_UINT32 j;
-    OPJ_UINT32 k;
-    OPJ_UINT32 lNbMatCoeff = pNbComp * pNbComp;
-    OPJ_INT32 * lCurrentData = 00;
-    OPJ_INT32 * lCurrentMatrix = 00;
-    OPJ_INT32 ** lData = (OPJ_INT32 **) pData;
-    OPJ_UINT32 lMultiplicator = 1 << 13;
-    OPJ_INT32 * lMctPtr;
-
-    OPJ_ARG_NOT_USED(isSigned);
-
-    lCurrentData = (OPJ_INT32 *) opj_malloc((pNbComp + lNbMatCoeff) * sizeof(
-            OPJ_INT32));
-    if (! lCurrentData) {
-        return OPJ_FALSE;
-    }
-
-    lCurrentMatrix = lCurrentData + pNbComp;
-
-    for (i = 0; i < lNbMatCoeff; ++i) {
-        lCurrentMatrix[i] = (OPJ_INT32)(*(lMct++) * (OPJ_FLOAT32)lMultiplicator);
-    }
-
-    for (i = 0; i < n; ++i)  {
-        lMctPtr = lCurrentMatrix;
-        for (j = 0; j < pNbComp; ++j) {
-            lCurrentData[j] = (*(lData[j]));
-        }
-
-        for (j = 0; j < pNbComp; ++j) {
-            *(lData[j]) = 0;
-            for (k = 0; k < pNbComp; ++k) {
-                *(lData[j]) += opj_int_fix_mul(*lMctPtr, lCurrentData[k]);
-                ++lMctPtr;
-            }
-
-            ++lData[j];
-        }
-    }
-
-    opj_free(lCurrentData);
-
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_mct_decode_custom(
-    OPJ_BYTE * pDecodingData,
-    OPJ_SIZE_T n,
-    OPJ_BYTE ** pData,
-    OPJ_UINT32 pNbComp,
-    OPJ_UINT32 isSigned)
-{
-    OPJ_FLOAT32 * lMct;
-    OPJ_SIZE_T i;
-    OPJ_UINT32 j;
-    OPJ_UINT32 k;
-
-    OPJ_FLOAT32 * lCurrentData = 00;
-    OPJ_FLOAT32 * lCurrentResult = 00;
-    OPJ_FLOAT32 ** lData = (OPJ_FLOAT32 **) pData;
-
-    OPJ_ARG_NOT_USED(isSigned);
-
-    lCurrentData = (OPJ_FLOAT32 *) opj_malloc(2 * pNbComp * sizeof(OPJ_FLOAT32));
-    if (! lCurrentData) {
-        return OPJ_FALSE;
-    }
-    lCurrentResult = lCurrentData + pNbComp;
-
-    for (i = 0; i < n; ++i) {
-        lMct = (OPJ_FLOAT32 *) pDecodingData;
-        for (j = 0; j < pNbComp; ++j) {
-            lCurrentData[j] = (OPJ_FLOAT32)(*(lData[j]));
-        }
-        for (j = 0; j < pNbComp; ++j) {
-            lCurrentResult[j] = 0;
-            for (k = 0; k < pNbComp; ++k) {
-                lCurrentResult[j] += *(lMct++) * lCurrentData[k];
-            }
-            *(lData[j]++) = (OPJ_FLOAT32)(lCurrentResult[j]);
-        }
-    }
-    opj_free(lCurrentData);
-    return OPJ_TRUE;
-}
-
-void opj_calculate_norms(OPJ_FLOAT64 * pNorms,
-                         OPJ_UINT32 pNbComps,
-                         OPJ_FLOAT32 * pMatrix)
-{
-    OPJ_UINT32 i, j, lIndex;
-    OPJ_FLOAT32 lCurrentValue;
-    OPJ_FLOAT64 * lNorms = (OPJ_FLOAT64 *) pNorms;
-    OPJ_FLOAT32 * lMatrix = (OPJ_FLOAT32 *) pMatrix;
-
-    for (i = 0; i < pNbComps; ++i) {
-        lNorms[i] = 0;
-        lIndex = i;
-
-        for (j = 0; j < pNbComps; ++j) {
-            lCurrentValue = lMatrix[lIndex];
-            lIndex += pNbComps;
-            lNorms[i] += lCurrentValue * lCurrentValue;
-        }
-        lNorms[i] = sqrt(lNorms[i]);
-    }
-}
diff --git a/third_party/libopenjpeg20/mct.h b/third_party/libopenjpeg20/mct.h
deleted file mode 100644
index 2e37ce7..0000000
--- a/third_party/libopenjpeg20/mct.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
- * Copyright (c) 2012, CS Systemes d'Information, France
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef OPJ_MCT_H
-#define OPJ_MCT_H
-/**
-@file mct.h
-@brief Implementation of a multi-component transforms (MCT)
-
-The functions in MCT.C have for goal to realize reversible and irreversible multicomponent
-transform. The functions in MCT.C are used by some function in TCD.C.
-*/
-
-/** @defgroup MCT MCT - Implementation of a multi-component transform */
-/*@{*/
-
-/** @name Exported functions */
-/*@{*/
-/* ----------------------------------------------------------------------- */
-/**
-Apply a reversible multi-component transform to an image
-@param c0 Samples for red component
-@param c1 Samples for green component
-@param c2 Samples blue component
-@param n Number of samples for each component
-*/
-void opj_mct_encode(OPJ_INT32* OPJ_RESTRICT c0, OPJ_INT32* OPJ_RESTRICT c1,
-                    OPJ_INT32* OPJ_RESTRICT c2, OPJ_SIZE_T n);
-/**
-Apply a reversible multi-component inverse transform to an image
-@param c0 Samples for luminance component
-@param c1 Samples for red chrominance component
-@param c2 Samples for blue chrominance component
-@param n Number of samples for each component
-*/
-void opj_mct_decode(OPJ_INT32* OPJ_RESTRICT c0, OPJ_INT32* OPJ_RESTRICT c1,
-                    OPJ_INT32* OPJ_RESTRICT c2, OPJ_SIZE_T n);
-/**
-Get norm of the basis function used for the reversible multi-component transform
-@param compno Number of the component (0->Y, 1->U, 2->V)
-@return
-*/
-OPJ_FLOAT64 opj_mct_getnorm(OPJ_UINT32 compno);
-
-/**
-Apply an irreversible multi-component transform to an image
-@param c0 Samples for red component
-@param c1 Samples for green component
-@param c2 Samples blue component
-@param n Number of samples for each component
-*/
-void opj_mct_encode_real(OPJ_INT32* OPJ_RESTRICT c0, OPJ_INT32* OPJ_RESTRICT c1,
-                         OPJ_INT32* OPJ_RESTRICT c2, OPJ_SIZE_T n);
-/**
-Apply an irreversible multi-component inverse transform to an image
-@param c0 Samples for luminance component
-@param c1 Samples for red chrominance component
-@param c2 Samples for blue chrominance component
-@param n Number of samples for each component
-*/
-void opj_mct_decode_real(OPJ_FLOAT32* OPJ_RESTRICT c0,
-                         OPJ_FLOAT32* OPJ_RESTRICT c1, OPJ_FLOAT32* OPJ_RESTRICT c2, OPJ_SIZE_T n);
-/**
-Get norm of the basis function used for the irreversible multi-component transform
-@param compno Number of the component (0->Y, 1->U, 2->V)
-@return
-*/
-OPJ_FLOAT64 opj_mct_getnorm_real(OPJ_UINT32 compno);
-
-/**
-FIXME DOC
-@param p_coding_data    MCT data
-@param n                size of components
-@param p_data           components
-@param p_nb_comp        nb of components (i.e. size of p_data)
-@param is_signed        tells if the data is signed
-@return OPJ_FALSE if function encounter a problem, OPJ_TRUE otherwise
-*/
-OPJ_BOOL opj_mct_encode_custom(
-    OPJ_BYTE * p_coding_data,
-    OPJ_SIZE_T n,
-    OPJ_BYTE ** p_data,
-    OPJ_UINT32 p_nb_comp,
-    OPJ_UINT32 is_signed);
-/**
-FIXME DOC
-@param pDecodingData    MCT data
-@param n                size of components
-@param pData            components
-@param pNbComp          nb of components (i.e. size of p_data)
-@param isSigned         tells if the data is signed
-@return OPJ_FALSE if function encounter a problem, OPJ_TRUE otherwise
-*/
-OPJ_BOOL opj_mct_decode_custom(
-    OPJ_BYTE * pDecodingData,
-    OPJ_SIZE_T n,
-    OPJ_BYTE ** pData,
-    OPJ_UINT32 pNbComp,
-    OPJ_UINT32 isSigned);
-/**
-FIXME DOC
-@param pNorms           MCT data
-@param p_nb_comps       size of components
-@param pMatrix          components
-@return
-*/
-void opj_calculate_norms(OPJ_FLOAT64 * pNorms,
-                         OPJ_UINT32 p_nb_comps,
-                         OPJ_FLOAT32 * pMatrix);
-/**
-FIXME DOC
-*/
-const OPJ_FLOAT64 * opj_mct_get_mct_norms(void);
-/**
-FIXME DOC
-*/
-const OPJ_FLOAT64 * opj_mct_get_mct_norms_real(void);
-/* ----------------------------------------------------------------------- */
-/*@}*/
-
-/*@}*/
-
-#endif /* OPJ_MCT_H */
diff --git a/third_party/libopenjpeg20/mqc.c b/third_party/libopenjpeg20/mqc.c
deleted file mode 100644
index 6299b17..0000000
--- a/third_party/libopenjpeg20/mqc.c
+++ /dev/null
@@ -1,560 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2008, Jerome Fimes, Communications & Systemes <jerome.fimes@c-s.fr>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "opj_includes.h"
-
-#include <assert.h>
-
-/** @defgroup MQC MQC - Implementation of an MQ-Coder */
-/*@{*/
-
-/** @name Local static functions */
-/*@{*/
-
-/**
-Output a byte, doing bit-stuffing if necessary.
-After a 0xff byte, the next byte must be smaller than 0x90.
-@param mqc MQC handle
-*/
-static void opj_mqc_byteout(opj_mqc_t *mqc);
-/**
-Renormalize mqc->a and mqc->c while encoding, so that mqc->a stays between 0x8000 and 0x10000
-@param mqc MQC handle
-*/
-static void opj_mqc_renorme(opj_mqc_t *mqc);
-/**
-Encode the most probable symbol
-@param mqc MQC handle
-*/
-static void opj_mqc_codemps(opj_mqc_t *mqc);
-/**
-Encode the most least symbol
-@param mqc MQC handle
-*/
-static void opj_mqc_codelps(opj_mqc_t *mqc);
-/**
-Fill mqc->c with 1's for flushing
-@param mqc MQC handle
-*/
-static void opj_mqc_setbits(opj_mqc_t *mqc);
-/*@}*/
-
-/*@}*/
-
-/* <summary> */
-/* This array defines all the possible states for a context. */
-/* </summary> */
-static const opj_mqc_state_t mqc_states[47 * 2] = {
-    {0x5601, 0, &mqc_states[2], &mqc_states[3]},
-    {0x5601, 1, &mqc_states[3], &mqc_states[2]},
-    {0x3401, 0, &mqc_states[4], &mqc_states[12]},
-    {0x3401, 1, &mqc_states[5], &mqc_states[13]},
-    {0x1801, 0, &mqc_states[6], &mqc_states[18]},
-    {0x1801, 1, &mqc_states[7], &mqc_states[19]},
-    {0x0ac1, 0, &mqc_states[8], &mqc_states[24]},
-    {0x0ac1, 1, &mqc_states[9], &mqc_states[25]},
-    {0x0521, 0, &mqc_states[10], &mqc_states[58]},
-    {0x0521, 1, &mqc_states[11], &mqc_states[59]},
-    {0x0221, 0, &mqc_states[76], &mqc_states[66]},
-    {0x0221, 1, &mqc_states[77], &mqc_states[67]},
-    {0x5601, 0, &mqc_states[14], &mqc_states[13]},
-    {0x5601, 1, &mqc_states[15], &mqc_states[12]},
-    {0x5401, 0, &mqc_states[16], &mqc_states[28]},
-    {0x5401, 1, &mqc_states[17], &mqc_states[29]},
-    {0x4801, 0, &mqc_states[18], &mqc_states[28]},
-    {0x4801, 1, &mqc_states[19], &mqc_states[29]},
-    {0x3801, 0, &mqc_states[20], &mqc_states[28]},
-    {0x3801, 1, &mqc_states[21], &mqc_states[29]},
-    {0x3001, 0, &mqc_states[22], &mqc_states[34]},
-    {0x3001, 1, &mqc_states[23], &mqc_states[35]},
-    {0x2401, 0, &mqc_states[24], &mqc_states[36]},
-    {0x2401, 1, &mqc_states[25], &mqc_states[37]},
-    {0x1c01, 0, &mqc_states[26], &mqc_states[40]},
-    {0x1c01, 1, &mqc_states[27], &mqc_states[41]},
-    {0x1601, 0, &mqc_states[58], &mqc_states[42]},
-    {0x1601, 1, &mqc_states[59], &mqc_states[43]},
-    {0x5601, 0, &mqc_states[30], &mqc_states[29]},
-    {0x5601, 1, &mqc_states[31], &mqc_states[28]},
-    {0x5401, 0, &mqc_states[32], &mqc_states[28]},
-    {0x5401, 1, &mqc_states[33], &mqc_states[29]},
-    {0x5101, 0, &mqc_states[34], &mqc_states[30]},
-    {0x5101, 1, &mqc_states[35], &mqc_states[31]},
-    {0x4801, 0, &mqc_states[36], &mqc_states[32]},
-    {0x4801, 1, &mqc_states[37], &mqc_states[33]},
-    {0x3801, 0, &mqc_states[38], &mqc_states[34]},
-    {0x3801, 1, &mqc_states[39], &mqc_states[35]},
-    {0x3401, 0, &mqc_states[40], &mqc_states[36]},
-    {0x3401, 1, &mqc_states[41], &mqc_states[37]},
-    {0x3001, 0, &mqc_states[42], &mqc_states[38]},
-    {0x3001, 1, &mqc_states[43], &mqc_states[39]},
-    {0x2801, 0, &mqc_states[44], &mqc_states[38]},
-    {0x2801, 1, &mqc_states[45], &mqc_states[39]},
-    {0x2401, 0, &mqc_states[46], &mqc_states[40]},
-    {0x2401, 1, &mqc_states[47], &mqc_states[41]},
-    {0x2201, 0, &mqc_states[48], &mqc_states[42]},
-    {0x2201, 1, &mqc_states[49], &mqc_states[43]},
-    {0x1c01, 0, &mqc_states[50], &mqc_states[44]},
-    {0x1c01, 1, &mqc_states[51], &mqc_states[45]},
-    {0x1801, 0, &mqc_states[52], &mqc_states[46]},
-    {0x1801, 1, &mqc_states[53], &mqc_states[47]},
-    {0x1601, 0, &mqc_states[54], &mqc_states[48]},
-    {0x1601, 1, &mqc_states[55], &mqc_states[49]},
-    {0x1401, 0, &mqc_states[56], &mqc_states[50]},
-    {0x1401, 1, &mqc_states[57], &mqc_states[51]},
-    {0x1201, 0, &mqc_states[58], &mqc_states[52]},
-    {0x1201, 1, &mqc_states[59], &mqc_states[53]},
-    {0x1101, 0, &mqc_states[60], &mqc_states[54]},
-    {0x1101, 1, &mqc_states[61], &mqc_states[55]},
-    {0x0ac1, 0, &mqc_states[62], &mqc_states[56]},
-    {0x0ac1, 1, &mqc_states[63], &mqc_states[57]},
-    {0x09c1, 0, &mqc_states[64], &mqc_states[58]},
-    {0x09c1, 1, &mqc_states[65], &mqc_states[59]},
-    {0x08a1, 0, &mqc_states[66], &mqc_states[60]},
-    {0x08a1, 1, &mqc_states[67], &mqc_states[61]},
-    {0x0521, 0, &mqc_states[68], &mqc_states[62]},
-    {0x0521, 1, &mqc_states[69], &mqc_states[63]},
-    {0x0441, 0, &mqc_states[70], &mqc_states[64]},
-    {0x0441, 1, &mqc_states[71], &mqc_states[65]},
-    {0x02a1, 0, &mqc_states[72], &mqc_states[66]},
-    {0x02a1, 1, &mqc_states[73], &mqc_states[67]},
-    {0x0221, 0, &mqc_states[74], &mqc_states[68]},
-    {0x0221, 1, &mqc_states[75], &mqc_states[69]},
-    {0x0141, 0, &mqc_states[76], &mqc_states[70]},
-    {0x0141, 1, &mqc_states[77], &mqc_states[71]},
-    {0x0111, 0, &mqc_states[78], &mqc_states[72]},
-    {0x0111, 1, &mqc_states[79], &mqc_states[73]},
-    {0x0085, 0, &mqc_states[80], &mqc_states[74]},
-    {0x0085, 1, &mqc_states[81], &mqc_states[75]},
-    {0x0049, 0, &mqc_states[82], &mqc_states[76]},
-    {0x0049, 1, &mqc_states[83], &mqc_states[77]},
-    {0x0025, 0, &mqc_states[84], &mqc_states[78]},
-    {0x0025, 1, &mqc_states[85], &mqc_states[79]},
-    {0x0015, 0, &mqc_states[86], &mqc_states[80]},
-    {0x0015, 1, &mqc_states[87], &mqc_states[81]},
-    {0x0009, 0, &mqc_states[88], &mqc_states[82]},
-    {0x0009, 1, &mqc_states[89], &mqc_states[83]},
-    {0x0005, 0, &mqc_states[90], &mqc_states[84]},
-    {0x0005, 1, &mqc_states[91], &mqc_states[85]},
-    {0x0001, 0, &mqc_states[90], &mqc_states[86]},
-    {0x0001, 1, &mqc_states[91], &mqc_states[87]},
-    {0x5601, 0, &mqc_states[92], &mqc_states[92]},
-    {0x5601, 1, &mqc_states[93], &mqc_states[93]},
-};
-
-/*
-==========================================================
-   local functions
-==========================================================
-*/
-
-static void opj_mqc_byteout(opj_mqc_t *mqc)
-{
-    /* bp is initialized to start - 1 in opj_mqc_init_enc() */
-    /* but this is safe, see opj_tcd_code_block_enc_allocate_data() */
-    assert(mqc->bp >= mqc->start - 1);
-    if (*mqc->bp == 0xff) {
-        mqc->bp++;
-        *mqc->bp = (OPJ_BYTE)(mqc->c >> 20);
-        mqc->c &= 0xfffff;
-        mqc->ct = 7;
-    } else {
-        if ((mqc->c & 0x8000000) == 0) {
-            mqc->bp++;
-            *mqc->bp = (OPJ_BYTE)(mqc->c >> 19);
-            mqc->c &= 0x7ffff;
-            mqc->ct = 8;
-        } else {
-            (*mqc->bp)++;
-            if (*mqc->bp == 0xff) {
-                mqc->c &= 0x7ffffff;
-                mqc->bp++;
-                *mqc->bp = (OPJ_BYTE)(mqc->c >> 20);
-                mqc->c &= 0xfffff;
-                mqc->ct = 7;
-            } else {
-                mqc->bp++;
-                *mqc->bp = (OPJ_BYTE)(mqc->c >> 19);
-                mqc->c &= 0x7ffff;
-                mqc->ct = 8;
-            }
-        }
-    }
-}
-
-static void opj_mqc_renorme(opj_mqc_t *mqc)
-{
-    do {
-        mqc->a <<= 1;
-        mqc->c <<= 1;
-        mqc->ct--;
-        if (mqc->ct == 0) {
-            opj_mqc_byteout(mqc);
-        }
-    } while ((mqc->a & 0x8000) == 0);
-}
-
-static void opj_mqc_codemps(opj_mqc_t *mqc)
-{
-    mqc->a -= (*mqc->curctx)->qeval;
-    if ((mqc->a & 0x8000) == 0) {
-        if (mqc->a < (*mqc->curctx)->qeval) {
-            mqc->a = (*mqc->curctx)->qeval;
-        } else {
-            mqc->c += (*mqc->curctx)->qeval;
-        }
-        *mqc->curctx = (*mqc->curctx)->nmps;
-        opj_mqc_renorme(mqc);
-    } else {
-        mqc->c += (*mqc->curctx)->qeval;
-    }
-}
-
-static void opj_mqc_codelps(opj_mqc_t *mqc)
-{
-    mqc->a -= (*mqc->curctx)->qeval;
-    if (mqc->a < (*mqc->curctx)->qeval) {
-        mqc->c += (*mqc->curctx)->qeval;
-    } else {
-        mqc->a = (*mqc->curctx)->qeval;
-    }
-    *mqc->curctx = (*mqc->curctx)->nlps;
-    opj_mqc_renorme(mqc);
-}
-
-static void opj_mqc_setbits(opj_mqc_t *mqc)
-{
-    OPJ_UINT32 tempc = mqc->c + mqc->a;
-    mqc->c |= 0xffff;
-    if (mqc->c >= tempc) {
-        mqc->c -= 0x8000;
-    }
-}
-
-/*
-==========================================================
-   MQ-Coder interface
-==========================================================
-*/
-
-OPJ_UINT32 opj_mqc_numbytes(opj_mqc_t *mqc)
-{
-    const ptrdiff_t diff = mqc->bp - mqc->start;
-#if 0
-    assert(diff <= 0xffffffff && diff >= 0);   /* UINT32_MAX */
-#endif
-    return (OPJ_UINT32)diff;
-}
-
-void opj_mqc_init_enc(opj_mqc_t *mqc, OPJ_BYTE *bp)
-{
-    /* To avoid the curctx pointer to be dangling, but not strictly */
-    /* required as the current context is always set before encoding */
-    opj_mqc_setcurctx(mqc, 0);
-
-    /* As specified in Figure C.10 - Initialization of the encoder */
-    /* (C.2.8 Initialization of the encoder (INITENC)) */
-    mqc->a = 0x8000;
-    mqc->c = 0;
-    /* Yes, we point before the start of the buffer, but this is safe */
-    /* given opj_tcd_code_block_enc_allocate_data() */
-    mqc->bp = bp - 1;
-    mqc->ct = 12;
-    /* At this point we should test *(mqc->bp) against 0xFF, but this is not */
-    /* necessary, as this is only used at the beginning of the code block */
-    /* and our initial fake byte is set at 0 */
-    assert(*(mqc->bp) != 0xff);
-
-    mqc->start = bp;
-    mqc->end_of_byte_stream_counter = 0;
-}
-
-void opj_mqc_encode(opj_mqc_t *mqc, OPJ_UINT32 d)
-{
-    if ((*mqc->curctx)->mps == d) {
-        opj_mqc_codemps(mqc);
-    } else {
-        opj_mqc_codelps(mqc);
-    }
-}
-
-void opj_mqc_flush(opj_mqc_t *mqc)
-{
-    /* C.2.9 Termination of coding (FLUSH) */
-    /* Figure C.11 – FLUSH procedure */
-    opj_mqc_setbits(mqc);
-    mqc->c <<= mqc->ct;
-    opj_mqc_byteout(mqc);
-    mqc->c <<= mqc->ct;
-    opj_mqc_byteout(mqc);
-
-    /* It is forbidden that a coding pass ends with 0xff */
-    if (*mqc->bp != 0xff) {
-        /* Advance pointer so that opj_mqc_numbytes() returns a valid value */
-        mqc->bp++;
-    }
-}
-
-#define BYPASS_CT_INIT  0xDEADBEEF
-
-void opj_mqc_bypass_init_enc(opj_mqc_t *mqc)
-{
-    /* This function is normally called after at least one opj_mqc_flush() */
-    /* which will have advance mqc->bp by at least 2 bytes beyond its */
-    /* initial position */
-    assert(mqc->bp >= mqc->start);
-    mqc->c = 0;
-    /* in theory we should initialize to 8, but use this special value */
-    /* as a hint that opj_mqc_bypass_enc() has never been called, so */
-    /* as to avoid the 0xff 0x7f elimination trick in opj_mqc_bypass_flush_enc() */
-    /* to trigger when we don't have output any bit during this bypass sequence */
-    /* Any value > 8 will do */
-    mqc->ct = BYPASS_CT_INIT;
-    /* Given that we are called after opj_mqc_flush(), the previous byte */
-    /* cannot be 0xff. */
-    assert(mqc->bp[-1] != 0xff);
-}
-
-void opj_mqc_bypass_enc(opj_mqc_t *mqc, OPJ_UINT32 d)
-{
-    if (mqc->ct == BYPASS_CT_INIT) {
-        mqc->ct = 8;
-    }
-    mqc->ct--;
-    mqc->c = mqc->c + (d << mqc->ct);
-    if (mqc->ct == 0) {
-        *mqc->bp = (OPJ_BYTE)mqc->c;
-        mqc->ct = 8;
-        /* If the previous byte was 0xff, make sure that the next msb is 0 */
-        if (*mqc->bp == 0xff) {
-            mqc->ct = 7;
-        }
-        mqc->bp++;
-        mqc->c = 0;
-    }
-}
-
-OPJ_UINT32 opj_mqc_bypass_get_extra_bytes(opj_mqc_t *mqc, OPJ_BOOL erterm)
-{
-    return (mqc->ct < 7 ||
-            (mqc->ct == 7 && (erterm || mqc->bp[-1] != 0xff))) ? 1 : 0;
-}
-
-void opj_mqc_bypass_flush_enc(opj_mqc_t *mqc, OPJ_BOOL erterm)
-{
-    /* Is there any bit remaining to be flushed ? */
-    /* If the last output byte is 0xff, we can discard it, unless */
-    /* erterm is required (I'm not completely sure why in erterm */
-    /* we must output 0xff 0x2a if the last byte was 0xff instead of */
-    /* discarding it, but Kakadu requires it when decoding */
-    /* in -fussy mode) */
-    if (mqc->ct < 7 || (mqc->ct == 7 && (erterm || mqc->bp[-1] != 0xff))) {
-        OPJ_BYTE bit_value = 0;
-        /* If so, fill the remaining lsbs with an alternating sequence of */
-        /* 0,1,... */
-        /* Note: it seems the standard only requires that for a ERTERM flush */
-        /* and doesn't specify what to do for a regular BYPASS flush */
-        while (mqc->ct > 0) {
-            mqc->ct--;
-            mqc->c += (OPJ_UINT32)(bit_value << mqc->ct);
-            bit_value = (OPJ_BYTE)(1U - bit_value);
-        }
-        *mqc->bp = (OPJ_BYTE)mqc->c;
-        /* Advance pointer so that opj_mqc_numbytes() returns a valid value */
-        mqc->bp++;
-    } else if (mqc->ct == 7 && mqc->bp[-1] == 0xff) {
-        /* Discard last 0xff */
-        assert(!erterm);
-        mqc->bp --;
-    } else if (mqc->ct == 8 && !erterm &&
-               mqc->bp[-1] == 0x7f && mqc->bp[-2] == 0xff) {
-        /* Tiny optimization: discard terminating 0xff 0x7f since it is */
-        /* interpreted as 0xff 0x7f [0xff 0xff] by the decoder, and given */
-        /* the bit stuffing, in fact as 0xff 0xff [0xff ..] */
-        /* Happens once on opj_compress -i ../MAPA.tif -o MAPA.j2k  -M 1 */
-        mqc->bp -= 2;
-    }
-
-    assert(mqc->bp[-1] != 0xff);
-}
-
-void opj_mqc_reset_enc(opj_mqc_t *mqc)
-{
-    opj_mqc_resetstates(mqc);
-    opj_mqc_setstate(mqc, T1_CTXNO_UNI, 0, 46);
-    opj_mqc_setstate(mqc, T1_CTXNO_AGG, 0, 3);
-    opj_mqc_setstate(mqc, T1_CTXNO_ZC, 0, 4);
-}
-
-#ifdef notdef
-OPJ_UINT32 opj_mqc_restart_enc(opj_mqc_t *mqc)
-{
-    OPJ_UINT32 correction = 1;
-
-    /* <flush part> */
-    OPJ_INT32 n = (OPJ_INT32)(27 - 15 - mqc->ct);
-    mqc->c <<= mqc->ct;
-    while (n > 0) {
-        opj_mqc_byteout(mqc);
-        n -= (OPJ_INT32)mqc->ct;
-        mqc->c <<= mqc->ct;
-    }
-    opj_mqc_byteout(mqc);
-
-    return correction;
-}
-#endif
-
-void opj_mqc_restart_init_enc(opj_mqc_t *mqc)
-{
-    /* <Re-init part> */
-
-    /* As specified in Figure C.10 - Initialization of the encoder */
-    /* (C.2.8 Initialization of the encoder (INITENC)) */
-    mqc->a = 0x8000;
-    mqc->c = 0;
-    mqc->ct = 12;
-    /* This function is normally called after at least one opj_mqc_flush() */
-    /* which will have advance mqc->bp by at least 2 bytes beyond its */
-    /* initial position */
-    mqc->bp --;
-    assert(mqc->bp >= mqc->start - 1);
-    assert(*mqc->bp != 0xff);
-    if (*mqc->bp == 0xff) {
-        mqc->ct = 13;
-    }
-}
-
-void opj_mqc_erterm_enc(opj_mqc_t *mqc)
-{
-    OPJ_INT32 k = (OPJ_INT32)(11 - mqc->ct + 1);
-
-    while (k > 0) {
-        mqc->c <<= mqc->ct;
-        mqc->ct = 0;
-        opj_mqc_byteout(mqc);
-        k -= (OPJ_INT32)mqc->ct;
-    }
-
-    if (*mqc->bp != 0xff) {
-        opj_mqc_byteout(mqc);
-    }
-}
-
-void opj_mqc_segmark_enc(opj_mqc_t *mqc)
-{
-    OPJ_UINT32 i;
-    opj_mqc_setcurctx(mqc, 18);
-
-    for (i = 1; i < 5; i++) {
-        opj_mqc_encode(mqc, i % 2);
-    }
-}
-
-static void opj_mqc_init_dec_common(opj_mqc_t *mqc,
-                                    OPJ_BYTE *bp,
-                                    OPJ_UINT32 len,
-                                    OPJ_UINT32 extra_writable_bytes)
-{
-    (void)extra_writable_bytes;
-
-    assert(extra_writable_bytes >= OPJ_COMMON_CBLK_DATA_EXTRA);
-    mqc->start = bp;
-    mqc->end = bp + len;
-    /* Insert an artificial 0xFF 0xFF marker at end of the code block */
-    /* data so that the bytein routines stop on it. This saves us comparing */
-    /* the bp and end pointers */
-    /* But before inserting it, backup th bytes we will overwrite */
-    memcpy(mqc->backup, mqc->end, OPJ_COMMON_CBLK_DATA_EXTRA);
-    mqc->end[0] = 0xFF;
-    mqc->end[1] = 0xFF;
-    mqc->bp = bp;
-}
-void opj_mqc_init_dec(opj_mqc_t *mqc, OPJ_BYTE *bp, OPJ_UINT32 len,
-                      OPJ_UINT32 extra_writable_bytes)
-{
-    /* Implements ISO 15444-1 C.3.5 Initialization of the decoder (INITDEC) */
-    /* Note: alternate "J.1 - Initialization of the software-conventions */
-    /* decoder" has been tried, but does */
-    /* not bring any improvement. */
-    /* See https://github.com/uclouvain/openjpeg/issues/921 */
-    opj_mqc_init_dec_common(mqc, bp, len, extra_writable_bytes);
-    opj_mqc_setcurctx(mqc, 0);
-    mqc->end_of_byte_stream_counter = 0;
-    if (len == 0) {
-        mqc->c = 0xff << 16;
-    } else {
-        mqc->c = (OPJ_UINT32)(*mqc->bp << 16);
-    }
-
-    opj_mqc_bytein(mqc);
-    mqc->c <<= 7;
-    mqc->ct -= 7;
-    mqc->a = 0x8000;
-}
-
-
-void opj_mqc_raw_init_dec(opj_mqc_t *mqc, OPJ_BYTE *bp, OPJ_UINT32 len,
-                          OPJ_UINT32 extra_writable_bytes)
-{
-    opj_mqc_init_dec_common(mqc, bp, len, extra_writable_bytes);
-    mqc->c = 0;
-    mqc->ct = 0;
-}
-
-
-void opq_mqc_finish_dec(opj_mqc_t *mqc)
-{
-    /* Restore the bytes overwritten by opj_mqc_init_dec_common() */
-    memcpy(mqc->end, mqc->backup, OPJ_COMMON_CBLK_DATA_EXTRA);
-}
-
-void opj_mqc_resetstates(opj_mqc_t *mqc)
-{
-    OPJ_UINT32 i;
-    for (i = 0; i < MQC_NUMCTXS; i++) {
-        mqc->ctxs[i] = mqc_states;
-    }
-}
-
-void opj_mqc_setstate(opj_mqc_t *mqc, OPJ_UINT32 ctxno, OPJ_UINT32 msb,
-                      OPJ_INT32 prob)
-{
-    mqc->ctxs[ctxno] = &mqc_states[msb + (OPJ_UINT32)(prob << 1)];
-}
-
-
diff --git a/third_party/libopenjpeg20/mqc.h b/third_party/libopenjpeg20/mqc.h
deleted file mode 100644
index 69a2a79..0000000
--- a/third_party/libopenjpeg20/mqc.h
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2008, Jerome Fimes, Communications & Systemes <jerome.fimes@c-s.fr>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef OPJ_MQC_H
-#define OPJ_MQC_H
-
-#include "opj_common.h"
-
-/**
-@file mqc.h
-@brief Implementation of an MQ-Coder (MQC)
-
-The functions in MQC.C have for goal to realize the MQ-coder operations. The functions
-in MQC.C are used by some function in T1.C.
-*/
-
-/** @defgroup MQC MQC - Implementation of an MQ-Coder */
-/*@{*/
-
-/**
-This struct defines the state of a context.
-*/
-typedef struct opj_mqc_state {
-    /** the probability of the Least Probable Symbol (0.75->0x8000, 1.5->0xffff) */
-    OPJ_UINT32 qeval;
-    /** the Most Probable Symbol (0 or 1) */
-    OPJ_UINT32 mps;
-    /** next state if the next encoded symbol is the MPS */
-    const struct opj_mqc_state *nmps;
-    /** next state if the next encoded symbol is the LPS */
-    const struct opj_mqc_state *nlps;
-} opj_mqc_state_t;
-
-#define MQC_NUMCTXS 19
-
-/**
-MQ coder
-*/
-typedef struct opj_mqc {
-    /** temporary buffer where bits are coded or decoded */
-    OPJ_UINT32 c;
-    /** only used by MQ decoder */
-    OPJ_UINT32 a;
-    /** number of bits already read or free to write */
-    OPJ_UINT32 ct;
-    /* only used by decoder, to count the number of times a terminating 0xFF >0x8F marker is read */
-    OPJ_UINT32 end_of_byte_stream_counter;
-    /** pointer to the current position in the buffer */
-    OPJ_BYTE *bp;
-    /** pointer to the start of the buffer */
-    OPJ_BYTE *start;
-    /** pointer to the end of the buffer */
-    OPJ_BYTE *end;
-    /** Array of contexts */
-    const opj_mqc_state_t *ctxs[MQC_NUMCTXS];
-    /** Active context */
-    const opj_mqc_state_t **curctx;
-    /* lut_ctxno_zc shifted by (1 << 9) * bandno */
-    const OPJ_BYTE* lut_ctxno_zc_orient;
-    /** Original value of the 2 bytes at end[0] and end[1] */
-    OPJ_BYTE backup[OPJ_COMMON_CBLK_DATA_EXTRA];
-} opj_mqc_t;
-
-#include "mqc_inl.h"
-
-/** @name Exported functions */
-/*@{*/
-/* ----------------------------------------------------------------------- */
-
-/**
-Return the number of bytes written/read since initialisation
-@param mqc MQC handle
-@return Returns the number of bytes already encoded
-*/
-OPJ_UINT32 opj_mqc_numbytes(opj_mqc_t *mqc);
-/**
-Reset the states of all the context of the coder/decoder
-(each context is set to a state where 0 and 1 are more or less equiprobable)
-@param mqc MQC handle
-*/
-void opj_mqc_resetstates(opj_mqc_t *mqc);
-/**
-Set the state of a particular context
-@param mqc MQC handle
-@param ctxno Number that identifies the context
-@param msb The MSB of the new state of the context
-@param prob Number that identifies the probability of the symbols for the new state of the context
-*/
-void opj_mqc_setstate(opj_mqc_t *mqc, OPJ_UINT32 ctxno, OPJ_UINT32 msb,
-                      OPJ_INT32 prob);
-/**
-Initialize the encoder
-@param mqc MQC handle
-@param bp Pointer to the start of the buffer where the bytes will be written
-*/
-void opj_mqc_init_enc(opj_mqc_t *mqc, OPJ_BYTE *bp);
-/**
-Set the current context used for coding/decoding
-@param mqc MQC handle
-@param ctxno Number that identifies the context
-*/
-#define opj_mqc_setcurctx(mqc, ctxno)   (mqc)->curctx = &(mqc)->ctxs[(OPJ_UINT32)(ctxno)]
-/**
-Encode a symbol using the MQ-coder
-@param mqc MQC handle
-@param d The symbol to be encoded (0 or 1)
-*/
-void opj_mqc_encode(opj_mqc_t *mqc, OPJ_UINT32 d);
-/**
-Flush the encoder, so that all remaining data is written
-@param mqc MQC handle
-*/
-void opj_mqc_flush(opj_mqc_t *mqc);
-/**
-BYPASS mode switch, initialization operation.
-JPEG 2000 p 505.
-@param mqc MQC handle
-*/
-void opj_mqc_bypass_init_enc(opj_mqc_t *mqc);
-
-/** Return number of extra bytes to add to opj_mqc_numbytes() for the²
-    size of a non-terminating BYPASS pass
-@param mqc MQC handle
-@param erterm 1 if ERTERM is enabled, 0 otherwise
-*/
-OPJ_UINT32 opj_mqc_bypass_get_extra_bytes(opj_mqc_t *mqc, OPJ_BOOL erterm);
-
-/**
-BYPASS mode switch, coding operation.
-JPEG 2000 p 505.
-@param mqc MQC handle
-@param d The symbol to be encoded (0 or 1)
-*/
-void opj_mqc_bypass_enc(opj_mqc_t *mqc, OPJ_UINT32 d);
-/**
-BYPASS mode switch, flush operation
-@param mqc MQC handle
-@param erterm 1 if ERTERM is enabled, 0 otherwise
-*/
-void opj_mqc_bypass_flush_enc(opj_mqc_t *mqc, OPJ_BOOL erterm);
-/**
-RESET mode switch
-@param mqc MQC handle
-*/
-void opj_mqc_reset_enc(opj_mqc_t *mqc);
-
-#ifdef notdef
-/**
-RESTART mode switch (TERMALL)
-@param mqc MQC handle
-@return Returns 1 (always)
-*/
-OPJ_UINT32 opj_mqc_restart_enc(opj_mqc_t *mqc);
-#endif
-
-/**
-RESTART mode switch (TERMALL) reinitialisation
-@param mqc MQC handle
-*/
-void opj_mqc_restart_init_enc(opj_mqc_t *mqc);
-/**
-ERTERM mode switch (PTERM)
-@param mqc MQC handle
-*/
-void opj_mqc_erterm_enc(opj_mqc_t *mqc);
-/**
-SEGMARK mode switch (SEGSYM)
-@param mqc MQC handle
-*/
-void opj_mqc_segmark_enc(opj_mqc_t *mqc);
-
-/**
-Initialize the decoder for MQ decoding.
-
-opj_mqc_finish_dec() must be absolutely called after finishing the decoding
-passes, so as to restore the bytes temporarily overwritten.
-
-@param mqc MQC handle
-@param bp Pointer to the start of the buffer from which the bytes will be read
-          Note that OPJ_COMMON_CBLK_DATA_EXTRA bytes at the end of the buffer
-          will be temporarily overwritten with an artificial 0xFF 0xFF marker.
-          (they will be backuped in the mqc structure to be restored later)
-          So bp must be at least len + OPJ_COMMON_CBLK_DATA_EXTRA large, and
-          writable.
-@param len Length of the input buffer
-@param extra_writable_bytes Indicate how many bytes after len are writable.
-                            This is to indicate your consent that bp must be
-                            large enough.
-*/
-void opj_mqc_init_dec(opj_mqc_t *mqc, OPJ_BYTE *bp, OPJ_UINT32 len,
-                      OPJ_UINT32 extra_writable_bytes);
-
-/**
-Initialize the decoder for RAW decoding.
-
-opj_mqc_finish_dec() must be absolutely called after finishing the decoding
-passes, so as to restore the bytes temporarily overwritten.
-
-@param mqc MQC handle
-@param bp Pointer to the start of the buffer from which the bytes will be read
-          Note that OPJ_COMMON_CBLK_DATA_EXTRA bytes at the end of the buffer
-          will be temporarily overwritten with an artificial 0xFF 0xFF marker.
-          (they will be backuped in the mqc structure to be restored later)
-          So bp must be at least len + OPJ_COMMON_CBLK_DATA_EXTRA large, and
-          writable.
-@param len Length of the input buffer
-@param extra_writable_bytes Indicate how many bytes after len are writable.
-                            This is to indicate your consent that bp must be
-                            large enough.
-*/
-void opj_mqc_raw_init_dec(opj_mqc_t *mqc, OPJ_BYTE *bp, OPJ_UINT32 len,
-                          OPJ_UINT32 extra_writable_bytes);
-
-
-/**
-Terminate RAW/MQC decoding
-
-This restores the bytes temporarily overwritten by opj_mqc_init_dec()/
-opj_mqc_raw_init_dec()
-
-@param mqc MQC handle
-*/
-void opq_mqc_finish_dec(opj_mqc_t *mqc);
-
-/**
-Decode a symbol
-@param mqc MQC handle
-@return Returns the decoded symbol (0 or 1)
-*/
-/*static INLINE OPJ_UINT32 opj_mqc_decode(opj_mqc_t * const mqc);*/
-/* ----------------------------------------------------------------------- */
-/*@}*/
-
-/*@}*/
-
-#endif /* OPJ_MQC_H */
diff --git a/third_party/libopenjpeg20/mqc_inl.h b/third_party/libopenjpeg20/mqc_inl.h
deleted file mode 100644
index 310a328..0000000
--- a/third_party/libopenjpeg20/mqc_inl.h
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2008, Jerome Fimes, Communications & Systemes <jerome.fimes@c-s.fr>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef OPJ_MQC_INL_H
-#define OPJ_MQC_INL_H
-
-/* For internal use of opj_mqc_decode_macro() */
-#define opj_mqc_mpsexchange_macro(d, curctx, a) \
-{ \
-    if (a < (*curctx)->qeval) { \
-        d = !((*curctx)->mps); \
-        *curctx = (*curctx)->nlps; \
-    } else { \
-        d = (*curctx)->mps; \
-        *curctx = (*curctx)->nmps; \
-    } \
-}
-
-/* For internal use of opj_mqc_decode_macro() */
-#define opj_mqc_lpsexchange_macro(d, curctx, a) \
-{ \
-    if (a < (*curctx)->qeval) { \
-        a = (*curctx)->qeval; \
-        d = (*curctx)->mps; \
-        *curctx = (*curctx)->nmps; \
-    } else { \
-        a = (*curctx)->qeval; \
-        d = !((*curctx)->mps); \
-        *curctx = (*curctx)->nlps; \
-    } \
-}
-
-
-/**
-Decode a symbol using raw-decoder. Cfr p.506 TAUBMAN
-@param mqc MQC handle
-@return Returns the decoded symbol (0 or 1)
-*/
-static INLINE OPJ_UINT32 opj_mqc_raw_decode(opj_mqc_t *mqc)
-{
-    OPJ_UINT32 d;
-    if (mqc->ct == 0) {
-        /* Given opj_mqc_raw_init_dec() we know that at some point we will */
-        /* have a 0xFF 0xFF artificial marker */
-        if (mqc->c == 0xff) {
-            if (*mqc->bp  > 0x8f) {
-                mqc->c = 0xff;
-                mqc->ct = 8;
-            } else {
-                mqc->c = *mqc->bp;
-                mqc->bp ++;
-                mqc->ct = 7;
-            }
-        } else {
-            mqc->c = *mqc->bp;
-            mqc->bp ++;
-            mqc->ct = 8;
-        }
-    }
-    mqc->ct--;
-    d = ((OPJ_UINT32)mqc->c >> mqc->ct) & 0x01U;
-
-    return d;
-}
-
-
-#define opj_mqc_bytein_macro(mqc, c, ct) \
-{ \
-        OPJ_UINT32 l_c;  \
-        /* Given opj_mqc_init_dec() we know that at some point we will */ \
-        /* have a 0xFF 0xFF artificial marker */ \
-        l_c = *(mqc->bp + 1); \
-        if (*mqc->bp == 0xff) { \
-            if (l_c > 0x8f) { \
-                c += 0xff00; \
-                ct = 8; \
-                mqc->end_of_byte_stream_counter ++; \
-            } else { \
-                mqc->bp++; \
-                c += l_c << 9; \
-                ct = 7; \
-            } \
-        } else { \
-            mqc->bp++; \
-            c += l_c << 8; \
-            ct = 8; \
-        } \
-}
-
-/* For internal use of opj_mqc_decode_macro() */
-#define opj_mqc_renormd_macro(mqc, a, c, ct) \
-{ \
-    do { \
-        if (ct == 0) { \
-            opj_mqc_bytein_macro(mqc, c, ct); \
-        } \
-        a <<= 1; \
-        c <<= 1; \
-        ct--; \
-    } while (a < 0x8000); \
-}
-
-#define opj_mqc_decode_macro(d, mqc, curctx, a, c, ct) \
-{ \
-    /* Implements ISO 15444-1 C.3.2 Decoding a decision (DECODE) */ \
-    /* Note: alternate "J.2 - Decoding an MPS or an LPS in the */ \
-    /* software-conventions decoder" has been tried, but does not bring any */ \
-    /* improvement. See https://github.com/uclouvain/openjpeg/issues/921 */ \
-    a -= (*curctx)->qeval;  \
-    if ((c >> 16) < (*curctx)->qeval) {  \
-        opj_mqc_lpsexchange_macro(d, curctx, a);  \
-        opj_mqc_renormd_macro(mqc, a, c, ct);  \
-    } else {  \
-        c -= (*curctx)->qeval << 16;  \
-        if ((a & 0x8000) == 0) { \
-            opj_mqc_mpsexchange_macro(d, curctx, a); \
-            opj_mqc_renormd_macro(mqc, a, c, ct); \
-        } else { \
-            d = (*curctx)->mps; \
-        } \
-    } \
-}
-
-#define DOWNLOAD_MQC_VARIABLES(mqc, curctx, c, a, ct) \
-        register const opj_mqc_state_t **curctx = mqc->curctx; \
-        register OPJ_UINT32 c = mqc->c; \
-        register OPJ_UINT32 a = mqc->a; \
-        register OPJ_UINT32 ct = mqc->ct
-
-#define UPLOAD_MQC_VARIABLES(mqc, curctx, c, a, ct) \
-        mqc->curctx = curctx; \
-        mqc->c = c; \
-        mqc->a = a; \
-        mqc->ct = ct;
-
-/**
-Input a byte
-@param mqc MQC handle
-*/
-static INLINE void opj_mqc_bytein(opj_mqc_t *const mqc)
-{
-    opj_mqc_bytein_macro(mqc, mqc->c, mqc->ct);
-}
-
-/**
-Renormalize mqc->a and mqc->c while decoding
-@param mqc MQC handle
-*/
-#define opj_mqc_renormd(mqc) \
-    opj_mqc_renormd_macro(mqc, mqc->a, mqc->c, mqc->ct)
-
-/**
-Decode a symbol
-@param d OPJ_UINT32 value where to store the decoded symbol
-@param mqc MQC handle
-@return Returns the decoded symbol (0 or 1) in d
-*/
-#define opj_mqc_decode(d, mqc) \
-    opj_mqc_decode_macro(d, mqc, mqc->curctx, mqc->a, mqc->c, mqc->ct)
-
-#endif /* OPJ_MQC_INL_H */
diff --git a/third_party/libopenjpeg20/openjpeg.c b/third_party/libopenjpeg20/openjpeg.c
deleted file mode 100644
index 7b12303..0000000
--- a/third_party/libopenjpeg20/openjpeg.c
+++ /dev/null
@@ -1,1065 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
- * Copyright (c) 2012, CS Systemes d'Information, France
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifdef _WIN32
-#include <windows.h>
-#endif /* _WIN32 */
-
-#include "opj_includes.h"
-
-
-/* ---------------------------------------------------------------------- */
-/* Functions to set the message handlers */
-
-OPJ_BOOL OPJ_CALLCONV opj_set_info_handler(opj_codec_t * p_codec,
-        opj_msg_callback p_callback,
-        void * p_user_data)
-{
-    opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-    if (! l_codec) {
-        return OPJ_FALSE;
-    }
-
-    l_codec->m_event_mgr.info_handler = p_callback;
-    l_codec->m_event_mgr.m_info_data = p_user_data;
-
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL OPJ_CALLCONV opj_set_warning_handler(opj_codec_t * p_codec,
-        opj_msg_callback p_callback,
-        void * p_user_data)
-{
-    opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-    if (! l_codec) {
-        return OPJ_FALSE;
-    }
-
-    l_codec->m_event_mgr.warning_handler = p_callback;
-    l_codec->m_event_mgr.m_warning_data = p_user_data;
-
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL OPJ_CALLCONV opj_set_error_handler(opj_codec_t * p_codec,
-        opj_msg_callback p_callback,
-        void * p_user_data)
-{
-    opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-    if (! l_codec) {
-        return OPJ_FALSE;
-    }
-
-    l_codec->m_event_mgr.error_handler = p_callback;
-    l_codec->m_event_mgr.m_error_data = p_user_data;
-
-    return OPJ_TRUE;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static OPJ_SIZE_T opj_read_from_file(void * p_buffer, OPJ_SIZE_T p_nb_bytes,
-                                     FILE * p_file)
-{
-    OPJ_SIZE_T l_nb_read = fread(p_buffer, 1, p_nb_bytes, p_file);
-    return l_nb_read ? l_nb_read : (OPJ_SIZE_T) - 1;
-}
-
-static OPJ_UINT64 opj_get_data_length_from_file(FILE * p_file)
-{
-    OPJ_OFF_T file_length = 0;
-
-    OPJ_FSEEK(p_file, 0, SEEK_END);
-    file_length = (OPJ_OFF_T)OPJ_FTELL(p_file);
-    OPJ_FSEEK(p_file, 0, SEEK_SET);
-
-    return (OPJ_UINT64)file_length;
-}
-
-static OPJ_SIZE_T opj_write_from_file(void * p_buffer, OPJ_SIZE_T p_nb_bytes,
-                                      FILE * p_file)
-{
-    return fwrite(p_buffer, 1, p_nb_bytes, p_file);
-}
-
-static OPJ_OFF_T opj_skip_from_file(OPJ_OFF_T p_nb_bytes, FILE * p_user_data)
-{
-    if (OPJ_FSEEK(p_user_data, p_nb_bytes, SEEK_CUR)) {
-        return -1;
-    }
-
-    return p_nb_bytes;
-}
-
-static OPJ_BOOL opj_seek_from_file(OPJ_OFF_T p_nb_bytes, FILE * p_user_data)
-{
-    if (OPJ_FSEEK(p_user_data, p_nb_bytes, SEEK_SET)) {
-        return OPJ_FALSE;
-    }
-
-    return OPJ_TRUE;
-}
-
-/* ---------------------------------------------------------------------- */
-#ifdef _WIN32
-#ifndef OPJ_STATIC
-BOOL APIENTRY
-DllMain(HINSTANCE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
-{
-
-    OPJ_ARG_NOT_USED(lpReserved);
-    OPJ_ARG_NOT_USED(hModule);
-
-    switch (ul_reason_for_call) {
-    case DLL_PROCESS_ATTACH :
-        break;
-    case DLL_PROCESS_DETACH :
-        break;
-    case DLL_THREAD_ATTACH :
-    case DLL_THREAD_DETACH :
-        break;
-    }
-
-    return TRUE;
-}
-#endif /* OPJ_STATIC */
-#endif /* _WIN32 */
-
-/* ---------------------------------------------------------------------- */
-
-const char* OPJ_CALLCONV opj_version(void)
-{
-    return OPJ_PACKAGE_VERSION;
-}
-
-/* ---------------------------------------------------------------------- */
-/* DECOMPRESSION FUNCTIONS*/
-
-opj_codec_t* OPJ_CALLCONV opj_create_decompress(OPJ_CODEC_FORMAT p_format)
-{
-    opj_codec_private_t *l_codec = 00;
-
-    l_codec = (opj_codec_private_t*) opj_calloc(1, sizeof(opj_codec_private_t));
-    if (!l_codec) {
-        return 00;
-    }
-
-    l_codec->is_decompressor = 1;
-
-    switch (p_format) {
-    case OPJ_CODEC_J2K:
-        l_codec->opj_dump_codec = (void (*)(void*, OPJ_INT32, FILE*)) j2k_dump;
-
-        l_codec->opj_get_codec_info = (opj_codestream_info_v2_t* (*)(
-                                           void*)) j2k_get_cstr_info;
-
-        l_codec->opj_get_codec_index = (opj_codestream_index_t* (*)(
-                                            void*)) 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_end_decompress =
-            (OPJ_BOOL(*)(void *,
-                         struct opj_stream_private *,
-                         struct opj_event_mgr *)) 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;
-
-        l_codec->m_codec_data.m_decompression.opj_destroy =
-            (void (*)(void *))opj_j2k_destroy;
-
-        l_codec->m_codec_data.m_decompression.opj_setup_decoder =
-            (void (*)(void *, opj_dparameters_t *)) opj_j2k_setup_decoder;
-
-        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;
-
-        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;
-
-        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;
-
-        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;
-
-        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;
-
-        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;
-
-        l_codec->opj_set_threads =
-            (OPJ_BOOL(*)(void * p_codec, OPJ_UINT32 num_threads)) opj_j2k_set_threads;
-
-        l_codec->m_codec = opj_j2k_create_decompress();
-
-        if (! l_codec->m_codec) {
-            opj_free(l_codec);
-            return NULL;
-        }
-
-        break;
-
-    case OPJ_CODEC_JP2:
-        /* get a JP2 decoder handle */
-        l_codec->opj_dump_codec = (void (*)(void*, OPJ_INT32, FILE*)) jp2_dump;
-
-        l_codec->opj_get_codec_info = (opj_codestream_info_v2_t* (*)(
-                                           void*)) jp2_get_cstr_info;
-
-        l_codec->opj_get_codec_index = (opj_codestream_index_t* (*)(
-                                            void*)) 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_end_decompress =
-            (OPJ_BOOL(*)(void *,
-                         struct opj_stream_private *,
-                         struct opj_event_mgr *)) 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;
-
-        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;
-
-        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;
-
-        l_codec->m_codec_data.m_decompression.opj_destroy = (void (*)(
-                    void *))opj_jp2_destroy;
-
-        l_codec->m_codec_data.m_decompression.opj_setup_decoder =
-            (void (*)(void *, opj_dparameters_t *)) opj_jp2_setup_decoder;
-
-        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;
-
-        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;
-
-        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;
-
-        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;
-
-        l_codec->opj_set_threads =
-            (OPJ_BOOL(*)(void * p_codec, OPJ_UINT32 num_threads)) opj_jp2_set_threads;
-
-        l_codec->m_codec = opj_jp2_create(OPJ_TRUE);
-
-        if (! l_codec->m_codec) {
-            opj_free(l_codec);
-            return 00;
-        }
-
-        break;
-    case OPJ_CODEC_UNKNOWN:
-    case OPJ_CODEC_JPT:
-    default:
-        opj_free(l_codec);
-        return 00;
-    }
-
-    opj_set_default_event_handler(&(l_codec->m_event_mgr));
-    return (opj_codec_t*) l_codec;
-}
-
-void OPJ_CALLCONV opj_set_default_decoder_parameters(opj_dparameters_t
-        *parameters)
-{
-    if (parameters) {
-        memset(parameters, 0, sizeof(opj_dparameters_t));
-        /* default decoding parameters */
-        parameters->cp_layer = 0;
-        parameters->cp_reduce = 0;
-
-        parameters->decod_format = -1;
-        parameters->cod_format = -1;
-        parameters->flags = 0;
-        /* UniPG>> */
-#ifdef USE_JPWL
-        parameters->jpwl_correct = OPJ_FALSE;
-        parameters->jpwl_exp_comps = JPWL_EXPECTED_COMPONENTS;
-        parameters->jpwl_max_tiles = JPWL_MAXIMUM_TILES;
-#endif /* USE_JPWL */
-        /* <<UniPG */
-    }
-}
-
-
-OPJ_BOOL OPJ_CALLCONV opj_codec_set_threads(opj_codec_t *p_codec,
-        int num_threads)
-{
-    if (p_codec && (num_threads >= 0)) {
-        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-
-        return l_codec->opj_set_threads(l_codec->m_codec, (OPJ_UINT32)num_threads);
-    }
-    return OPJ_FALSE;
-}
-
-OPJ_BOOL OPJ_CALLCONV opj_setup_decoder(opj_codec_t *p_codec,
-                                        opj_dparameters_t *parameters
-                                       )
-{
-    if (p_codec && parameters) {
-        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-
-        if (! l_codec->is_decompressor) {
-            opj_event_msg(&(l_codec->m_event_mgr), EVT_ERROR,
-                          "Codec provided to the opj_setup_decoder function is not a decompressor handler.\n");
-            return OPJ_FALSE;
-        }
-
-        l_codec->m_codec_data.m_decompression.opj_setup_decoder(l_codec->m_codec,
-                parameters);
-        return OPJ_TRUE;
-    }
-    return OPJ_FALSE;
-}
-
-OPJ_BOOL OPJ_CALLCONV opj_read_header(opj_stream_t *p_stream,
-                                      opj_codec_t *p_codec,
-                                      opj_image_t **p_image)
-{
-    if (p_codec && p_stream) {
-        opj_codec_private_t* l_codec = (opj_codec_private_t*) p_codec;
-        opj_stream_private_t* l_stream = (opj_stream_private_t*) p_stream;
-
-        if (! l_codec->is_decompressor) {
-            opj_event_msg(&(l_codec->m_event_mgr), EVT_ERROR,
-                          "Codec provided to the opj_read_header function is not a decompressor handler.\n");
-            return OPJ_FALSE;
-        }
-
-        return l_codec->m_codec_data.m_decompression.opj_read_header(l_stream,
-                l_codec->m_codec,
-                p_image,
-                &(l_codec->m_event_mgr));
-    }
-
-    return OPJ_FALSE;
-}
-
-
-OPJ_BOOL OPJ_CALLCONV opj_set_decoded_components(opj_codec_t *p_codec,
-        OPJ_UINT32 numcomps,
-        const OPJ_UINT32* comps_indices,
-        OPJ_BOOL apply_color_transforms)
-{
-    if (p_codec) {
-        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-
-        if (! l_codec->is_decompressor) {
-            opj_event_msg(&(l_codec->m_event_mgr), EVT_ERROR,
-                          "Codec provided to the opj_set_decoded_components function is not a decompressor handler.\n");
-            return OPJ_FALSE;
-        }
-
-        if (apply_color_transforms) {
-            opj_event_msg(&(l_codec->m_event_mgr), EVT_ERROR,
-                          "apply_color_transforms = OPJ_TRUE is not supported.\n");
-            return OPJ_FALSE;
-        }
-
-        return  l_codec->m_codec_data.m_decompression.opj_set_decoded_components(
-                    l_codec->m_codec,
-                    numcomps,
-                    comps_indices,
-                    &(l_codec->m_event_mgr));
-    }
-    return OPJ_FALSE;
-}
-
-OPJ_BOOL OPJ_CALLCONV opj_decode(opj_codec_t *p_codec,
-                                 opj_stream_t *p_stream,
-                                 opj_image_t* p_image)
-{
-    if (p_codec && p_stream) {
-        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
-
-        if (! l_codec->is_decompressor) {
-            return OPJ_FALSE;
-        }
-
-        return l_codec->m_codec_data.m_decompression.opj_decode(l_codec->m_codec,
-                l_stream,
-                p_image,
-                &(l_codec->m_event_mgr));
-    }
-
-    return OPJ_FALSE;
-}
-
-OPJ_BOOL OPJ_CALLCONV opj_set_decode_area(opj_codec_t *p_codec,
-        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
-                                         )
-{
-    if (p_codec) {
-        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-
-        if (! l_codec->is_decompressor) {
-            return OPJ_FALSE;
-        }
-
-        return  l_codec->m_codec_data.m_decompression.opj_set_decode_area(
-                    l_codec->m_codec,
-                    p_image,
-                    p_start_x, p_start_y,
-                    p_end_x, p_end_y,
-                    &(l_codec->m_event_mgr));
-    }
-    return OPJ_FALSE;
-}
-
-OPJ_BOOL OPJ_CALLCONV opj_read_tile_header(opj_codec_t *p_codec,
-        opj_stream_t * p_stream,
-        OPJ_UINT32 * p_tile_index,
-        OPJ_UINT32 * p_data_size,
-        OPJ_INT32 * p_tile_x0, OPJ_INT32 * p_tile_y0,
-        OPJ_INT32 * p_tile_x1, OPJ_INT32 * p_tile_y1,
-        OPJ_UINT32 * p_nb_comps,
-        OPJ_BOOL * p_should_go_on)
-{
-    if (p_codec && p_stream && p_data_size && p_tile_index) {
-        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
-
-        if (! l_codec->is_decompressor) {
-            return OPJ_FALSE;
-        }
-
-        return l_codec->m_codec_data.m_decompression.opj_read_tile_header(
-                   l_codec->m_codec,
-                   p_tile_index,
-                   p_data_size,
-                   p_tile_x0, p_tile_y0,
-                   p_tile_x1, p_tile_y1,
-                   p_nb_comps,
-                   p_should_go_on,
-                   l_stream,
-                   &(l_codec->m_event_mgr));
-    }
-    return OPJ_FALSE;
-}
-
-OPJ_BOOL OPJ_CALLCONV opj_decode_tile_data(opj_codec_t *p_codec,
-        OPJ_UINT32 p_tile_index,
-        OPJ_BYTE * p_data,
-        OPJ_UINT32 p_data_size,
-        opj_stream_t *p_stream
-                                          )
-{
-    if (p_codec && p_data && p_stream) {
-        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
-
-        if (! l_codec->is_decompressor) {
-            return OPJ_FALSE;
-        }
-
-        return l_codec->m_codec_data.m_decompression.opj_decode_tile_data(
-                   l_codec->m_codec,
-                   p_tile_index,
-                   p_data,
-                   p_data_size,
-                   l_stream,
-                   &(l_codec->m_event_mgr));
-    }
-    return OPJ_FALSE;
-}
-
-OPJ_BOOL OPJ_CALLCONV opj_get_decoded_tile(opj_codec_t *p_codec,
-        opj_stream_t *p_stream,
-        opj_image_t *p_image,
-        OPJ_UINT32 tile_index)
-{
-    if (p_codec && p_stream) {
-        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
-
-        if (! l_codec->is_decompressor) {
-            return OPJ_FALSE;
-        }
-
-        return l_codec->m_codec_data.m_decompression.opj_get_decoded_tile(
-                   l_codec->m_codec,
-                   l_stream,
-                   p_image,
-                   &(l_codec->m_event_mgr),
-                   tile_index);
-    }
-
-    return OPJ_FALSE;
-}
-
-OPJ_BOOL OPJ_CALLCONV opj_set_decoded_resolution_factor(opj_codec_t *p_codec,
-        OPJ_UINT32 res_factor)
-{
-    opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-
-    if (!l_codec) {
-        return OPJ_FALSE;
-    }
-
-    return l_codec->m_codec_data.m_decompression.opj_set_decoded_resolution_factor(
-               l_codec->m_codec,
-               res_factor,
-               &(l_codec->m_event_mgr));
-}
-
-/* ---------------------------------------------------------------------- */
-/* COMPRESSION FUNCTIONS*/
-
-opj_codec_t* OPJ_CALLCONV opj_create_compress(OPJ_CODEC_FORMAT p_format)
-{
-    opj_codec_private_t *l_codec = 00;
-
-    l_codec = (opj_codec_private_t*)opj_calloc(1, sizeof(opj_codec_private_t));
-    if (!l_codec) {
-        return 00;
-    }
-
-    l_codec->is_decompressor = 0;
-
-    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 = opj_j2k_create_compress();
-        if (! l_codec->m_codec) {
-            opj_free(l_codec);
-            return 00;
-        }
-
-        break;
-
-    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 = opj_jp2_create(OPJ_FALSE);
-        if (! l_codec->m_codec) {
-            opj_free(l_codec);
-            return 00;
-        }
-
-        break;
-
-    case OPJ_CODEC_UNKNOWN:
-    case OPJ_CODEC_JPT:
-    default:
-        opj_free(l_codec);
-        return 00;
-    }
-
-    opj_set_default_event_handler(&(l_codec->m_event_mgr));
-    return (opj_codec_t*) l_codec;
-}
-
-void OPJ_CALLCONV opj_set_default_encoder_parameters(opj_cparameters_t
-        *parameters)
-{
-    if (parameters) {
-        memset(parameters, 0, sizeof(opj_cparameters_t));
-        /* default coding parameters */
-        parameters->cp_cinema = OPJ_OFF; /* DEPRECATED */
-        parameters->rsiz = OPJ_PROFILE_NONE;
-        parameters->max_comp_size = 0;
-        parameters->numresolution = 6;
-        parameters->cp_rsiz = OPJ_STD_RSIZ; /* DEPRECATED */
-        parameters->cblockw_init = 64;
-        parameters->cblockh_init = 64;
-        parameters->prog_order = OPJ_LRCP;
-        parameters->roi_compno = -1;        /* no ROI */
-        parameters->subsampling_dx = 1;
-        parameters->subsampling_dy = 1;
-        parameters->tp_on = 0;
-        parameters->decod_format = -1;
-        parameters->cod_format = -1;
-        parameters->tcp_rates[0] = 0;
-        parameters->tcp_numlayers = 0;
-        parameters->cp_disto_alloc = 0;
-        parameters->cp_fixed_alloc = 0;
-        parameters->cp_fixed_quality = 0;
-        parameters->jpip_on = OPJ_FALSE;
-        /* UniPG>> */
-#ifdef USE_JPWL
-        parameters->jpwl_epc_on = OPJ_FALSE;
-        parameters->jpwl_hprot_MH = -1; /* -1 means unassigned */
-        {
-            int i;
-            for (i = 0; i < JPWL_MAX_NO_TILESPECS; i++) {
-                parameters->jpwl_hprot_TPH_tileno[i] = -1; /* unassigned */
-                parameters->jpwl_hprot_TPH[i] = 0; /* absent */
-            }
-        };
-        {
-            int i;
-            for (i = 0; i < JPWL_MAX_NO_PACKSPECS; i++) {
-                parameters->jpwl_pprot_tileno[i] = -1; /* unassigned */
-                parameters->jpwl_pprot_packno[i] = -1; /* unassigned */
-                parameters->jpwl_pprot[i] = 0; /* absent */
-            }
-        };
-        parameters->jpwl_sens_size = 0; /* 0 means no ESD */
-        parameters->jpwl_sens_addr = 0; /* 0 means auto */
-        parameters->jpwl_sens_range = 0; /* 0 means packet */
-        parameters->jpwl_sens_MH = -1; /* -1 means unassigned */
-        {
-            int i;
-            for (i = 0; i < JPWL_MAX_NO_TILESPECS; i++) {
-                parameters->jpwl_sens_TPH_tileno[i] = -1; /* unassigned */
-                parameters->jpwl_sens_TPH[i] = -1; /* absent */
-            }
-        };
-#endif /* USE_JPWL */
-        /* <<UniPG */
-    }
-}
-
-OPJ_BOOL OPJ_CALLCONV opj_setup_encoder(opj_codec_t *p_codec,
-                                        opj_cparameters_t *parameters,
-                                        opj_image_t *p_image)
-{
-    if (p_codec && parameters && p_image) {
-        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-
-        if (! l_codec->is_decompressor) {
-            return l_codec->m_codec_data.m_compression.opj_setup_encoder(l_codec->m_codec,
-                    parameters,
-                    p_image,
-                    &(l_codec->m_event_mgr));
-        }
-    }
-
-    return OPJ_FALSE;
-}
-
-OPJ_BOOL OPJ_CALLCONV opj_start_compress(opj_codec_t *p_codec,
-        opj_image_t * p_image,
-        opj_stream_t *p_stream)
-{
-    if (p_codec && p_stream) {
-        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
-
-        if (! l_codec->is_decompressor) {
-            return l_codec->m_codec_data.m_compression.opj_start_compress(l_codec->m_codec,
-                    l_stream,
-                    p_image,
-                    &(l_codec->m_event_mgr));
-        }
-    }
-
-    return OPJ_FALSE;
-}
-
-OPJ_BOOL OPJ_CALLCONV opj_encode(opj_codec_t *p_info, opj_stream_t *p_stream)
-{
-    if (p_info && p_stream) {
-        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_info;
-        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
-
-        if (! l_codec->is_decompressor) {
-            return l_codec->m_codec_data.m_compression.opj_encode(l_codec->m_codec,
-                    l_stream,
-                    &(l_codec->m_event_mgr));
-        }
-    }
-
-    return OPJ_FALSE;
-
-}
-
-OPJ_BOOL OPJ_CALLCONV opj_end_compress(opj_codec_t *p_codec,
-                                       opj_stream_t *p_stream)
-{
-    if (p_codec && p_stream) {
-        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
-
-        if (! l_codec->is_decompressor) {
-            return l_codec->m_codec_data.m_compression.opj_end_compress(l_codec->m_codec,
-                    l_stream,
-                    &(l_codec->m_event_mgr));
-        }
-    }
-    return OPJ_FALSE;
-
-}
-
-OPJ_BOOL OPJ_CALLCONV opj_end_decompress(opj_codec_t *p_codec,
-        opj_stream_t *p_stream)
-{
-    if (p_codec && p_stream) {
-        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
-
-        if (! l_codec->is_decompressor) {
-            return OPJ_FALSE;
-        }
-
-        return l_codec->m_codec_data.m_decompression.opj_end_decompress(
-                   l_codec->m_codec,
-                   l_stream,
-                   &(l_codec->m_event_mgr));
-    }
-
-    return OPJ_FALSE;
-}
-
-OPJ_BOOL OPJ_CALLCONV opj_set_MCT(opj_cparameters_t *parameters,
-                                  OPJ_FLOAT32 * pEncodingMatrix,
-                                  OPJ_INT32 * p_dc_shift, OPJ_UINT32 pNbComp)
-{
-    OPJ_UINT32 l_matrix_size = pNbComp * pNbComp * (OPJ_UINT32)sizeof(OPJ_FLOAT32);
-    OPJ_UINT32 l_dc_shift_size = pNbComp * (OPJ_UINT32)sizeof(OPJ_INT32);
-    OPJ_UINT32 l_mct_total_size = l_matrix_size + l_dc_shift_size;
-
-    /* add MCT capability */
-    if (OPJ_IS_PART2(parameters->rsiz)) {
-        parameters->rsiz |= OPJ_EXTENSION_MCT;
-    } else {
-        parameters->rsiz = ((OPJ_PROFILE_PART2) | (OPJ_EXTENSION_MCT));
-    }
-    parameters->irreversible = 1;
-
-    /* use array based MCT */
-    parameters->tcp_mct = 2;
-    parameters->mct_data = opj_malloc(l_mct_total_size);
-    if (! parameters->mct_data) {
-        return OPJ_FALSE;
-    }
-
-    memcpy(parameters->mct_data, pEncodingMatrix, l_matrix_size);
-    memcpy(((OPJ_BYTE *) parameters->mct_data) +  l_matrix_size, p_dc_shift,
-           l_dc_shift_size);
-
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL OPJ_CALLCONV opj_write_tile(opj_codec_t *p_codec,
-                                     OPJ_UINT32 p_tile_index,
-                                     OPJ_BYTE * p_data,
-                                     OPJ_UINT32 p_data_size,
-                                     opj_stream_t *p_stream)
-{
-    if (p_codec && p_stream && p_data) {
-        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;
-
-        if (l_codec->is_decompressor) {
-            return OPJ_FALSE;
-        }
-
-        return l_codec->m_codec_data.m_compression.opj_write_tile(l_codec->m_codec,
-                p_tile_index,
-                p_data,
-                p_data_size,
-                l_stream,
-                &(l_codec->m_event_mgr));
-    }
-
-    return OPJ_FALSE;
-}
-
-/* ---------------------------------------------------------------------- */
-
-void OPJ_CALLCONV opj_destroy_codec(opj_codec_t *p_codec)
-{
-    if (p_codec) {
-        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
-
-        if (l_codec->is_decompressor) {
-            l_codec->m_codec_data.m_decompression.opj_destroy(l_codec->m_codec);
-        } else {
-            l_codec->m_codec_data.m_compression.opj_destroy(l_codec->m_codec);
-        }
-
-        l_codec->m_codec = 00;
-        opj_free(l_codec);
-    }
-}
-
-/* ---------------------------------------------------------------------- */
-
-void OPJ_CALLCONV opj_dump_codec(opj_codec_t *p_codec,
-                                 OPJ_INT32 info_flag,
-                                 FILE* output_stream)
-{
-    if (p_codec) {
-        opj_codec_private_t* l_codec = (opj_codec_private_t*) p_codec;
-
-        l_codec->opj_dump_codec(l_codec->m_codec, info_flag, output_stream);
-        return;
-    }
-
-    /* TODO return error */
-    /* fprintf(stderr, "[ERROR] Input parameter of the dump_codec function are incorrect.\n"); */
-    return;
-}
-
-opj_codestream_info_v2_t* OPJ_CALLCONV opj_get_cstr_info(opj_codec_t *p_codec)
-{
-    if (p_codec) {
-        opj_codec_private_t* l_codec = (opj_codec_private_t*) p_codec;
-
-        return l_codec->opj_get_codec_info(l_codec->m_codec);
-    }
-
-    return NULL;
-}
-
-void OPJ_CALLCONV opj_destroy_cstr_info(opj_codestream_info_v2_t **cstr_info)
-{
-    if (cstr_info) {
-
-        if ((*cstr_info)->m_default_tile_info.tccp_info) {
-            opj_free((*cstr_info)->m_default_tile_info.tccp_info);
-        }
-
-        if ((*cstr_info)->tile_info) {
-            /* FIXME not used for the moment*/
-        }
-
-        opj_free((*cstr_info));
-        (*cstr_info) = NULL;
-    }
-}
-
-opj_codestream_index_t * OPJ_CALLCONV opj_get_cstr_index(opj_codec_t *p_codec)
-{
-    if (p_codec) {
-        opj_codec_private_t* l_codec = (opj_codec_private_t*) p_codec;
-
-        return l_codec->opj_get_codec_index(l_codec->m_codec);
-    }
-
-    return NULL;
-}
-
-void OPJ_CALLCONV opj_destroy_cstr_index(opj_codestream_index_t **p_cstr_index)
-{
-    if (*p_cstr_index) {
-        j2k_destroy_cstr_index(*p_cstr_index);
-        (*p_cstr_index) = NULL;
-    }
-}
-
-opj_stream_t* OPJ_CALLCONV opj_stream_create_default_file_stream(
-    const char *fname, OPJ_BOOL p_is_read_stream)
-{
-    return opj_stream_create_file_stream(fname, OPJ_J2K_STREAM_CHUNK_SIZE,
-                                         p_is_read_stream);
-}
-
-opj_stream_t* OPJ_CALLCONV opj_stream_create_file_stream(
-    const char *fname,
-    OPJ_SIZE_T p_size,
-    OPJ_BOOL p_is_read_stream)
-{
-    opj_stream_t* l_stream = 00;
-    FILE *p_file;
-    const char *mode;
-
-    if (! fname) {
-        return NULL;
-    }
-
-    if (p_is_read_stream) {
-        mode = "rb";
-    } else {
-        mode = "wb";
-    }
-
-    p_file = fopen(fname, mode);
-
-    if (! p_file) {
-        return NULL;
-    }
-
-    l_stream = opj_stream_create(p_size, p_is_read_stream);
-    if (! l_stream) {
-        fclose(p_file);
-        return NULL;
-    }
-
-    opj_stream_set_user_data(l_stream, p_file,
-                             (opj_stream_free_user_data_fn) fclose);
-    opj_stream_set_user_data_length(l_stream,
-                                    opj_get_data_length_from_file(p_file));
-    opj_stream_set_read_function(l_stream, (opj_stream_read_fn) opj_read_from_file);
-    opj_stream_set_write_function(l_stream,
-                                  (opj_stream_write_fn) opj_write_from_file);
-    opj_stream_set_skip_function(l_stream, (opj_stream_skip_fn) opj_skip_from_file);
-    opj_stream_set_seek_function(l_stream, (opj_stream_seek_fn) opj_seek_from_file);
-
-    return l_stream;
-}
-
-
-void* OPJ_CALLCONV opj_image_data_alloc(OPJ_SIZE_T size)
-{
-    void* ret = opj_aligned_malloc(size);
-    /* printf("opj_image_data_alloc %p\n", ret); */
-    return ret;
-}
-
-void OPJ_CALLCONV opj_image_data_free(void* ptr)
-{
-    /* printf("opj_image_data_free %p\n", ptr); */
-    opj_aligned_free(ptr);
-}
diff --git a/third_party/libopenjpeg20/openjpeg.h b/third_party/libopenjpeg20/openjpeg.h
deleted file mode 100644
index 53a0e10..0000000
--- a/third_party/libopenjpeg20/openjpeg.h
+++ /dev/null
@@ -1,1691 +0,0 @@
-/*
-* The copyright in this software is being made available under the 2-clauses
-* BSD License, included below. This software may be subject to other third
-* party and contributor rights, including patent rights, and no such rights
-* are granted under this license.
-*
-* Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
-* Copyright (c) 2002-2014, Professor Benoit Macq
-* Copyright (c) 2001-2003, David Janssens
-* Copyright (c) 2002-2003, Yannick Verschueren
-* Copyright (c) 2003-2007, Francois-Olivier Devaux
-* Copyright (c) 2003-2014, Antonin Descampe
-* Copyright (c) 2005, Herve Drolon, FreeImage Team
-* Copyright (c) 2006-2007, Parvatha Elangovan
-* Copyright (c) 2008, Jerome Fimes, Communications & Systemes <jerome.fimes@c-s.fr>
-* Copyright (c) 2010-2011, Kaori Hagihara
-* Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France
-* Copyright (c) 2012, CS Systemes d'Information, France
-* All rights reserved.
-*
-* Redistribution and use in source and binary forms, with or without
-* modification, are permitted provided that the following conditions
-* are met:
-* 1. Redistributions of source code must retain the above copyright
-*    notice, this list of conditions and the following disclaimer.
-* 2. Redistributions in binary form must reproduce the above copyright
-*    notice, this list of conditions and the following disclaimer in the
-*    documentation and/or other materials provided with the distribution.
-*
-* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
-* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-* POSSIBILITY OF SUCH DAMAGE.
-*/
-#ifndef OPENJPEG_H
-#define OPENJPEG_H
-
-
-/*
-==========================================================
-   Compiler directives
-==========================================================
-*/
-
-/*
-The inline keyword is supported by C99 but not by C90.
-Most compilers implement their own version of this keyword ...
-*/
-#ifndef INLINE
-#if defined(_MSC_VER)
-#define INLINE __forceinline
-#elif defined(__GNUC__)
-#define INLINE __inline__
-#elif defined(__MWERKS__)
-#define INLINE inline
-#else
-/* add other compilers here ... */
-#define INLINE
-#endif /* defined(<Compiler>) */
-#endif /* INLINE */
-
-/* deprecated attribute */
-#ifdef __GNUC__
-#define OPJ_DEPRECATED(func) func __attribute__ ((deprecated))
-#elif defined(_MSC_VER)
-#define OPJ_DEPRECATED(func) __declspec(deprecated) func
-#else
-#pragma message("WARNING: You need to implement DEPRECATED for this compiler")
-#define OPJ_DEPRECATED(func) func
-#endif
-
-#if defined(OPJ_STATIC) || !defined(_WIN32)
-/* http://gcc.gnu.org/wiki/Visibility */
-#   if __GNUC__ >= 4
-#       if defined(OPJ_STATIC) /* static library uses "hidden" */
-#           define OPJ_API    __attribute__ ((visibility ("hidden")))
-#       else
-#           define OPJ_API    __attribute__ ((visibility ("default")))
-#       endif
-#       define OPJ_LOCAL  __attribute__ ((visibility ("hidden")))
-#   else
-#       define OPJ_API
-#       define OPJ_LOCAL
-#   endif
-#   define OPJ_CALLCONV
-#else
-#   define OPJ_CALLCONV __stdcall
-/*
-The following ifdef block is the standard way of creating macros which make exporting
-from a DLL simpler. All files within this DLL are compiled with the OPJ_EXPORTS
-symbol defined on the command line. this symbol should not be defined on any project
-that uses this DLL. This way any other project whose source files include this file see
-OPJ_API functions as being imported from a DLL, whereas this DLL sees symbols
-defined with this macro as being exported.
-*/
-#   if defined(OPJ_EXPORTS) || defined(DLL_EXPORT)
-#       define OPJ_API __declspec(dllexport)
-#   else
-#       define OPJ_API __declspec(dllimport)
-#   endif /* OPJ_EXPORTS */
-#endif /* !OPJ_STATIC || !_WIN32 */
-
-typedef int OPJ_BOOL;
-#define OPJ_TRUE 1
-#define OPJ_FALSE 0
-
-typedef char          OPJ_CHAR;
-typedef float         OPJ_FLOAT32;
-typedef double        OPJ_FLOAT64;
-typedef unsigned char OPJ_BYTE;
-
-#include "opj_stdint.h"
-
-typedef int8_t   OPJ_INT8;
-typedef uint8_t  OPJ_UINT8;
-typedef int16_t  OPJ_INT16;
-typedef uint16_t OPJ_UINT16;
-typedef int32_t  OPJ_INT32;
-typedef uint32_t OPJ_UINT32;
-typedef int64_t  OPJ_INT64;
-typedef uint64_t OPJ_UINT64;
-
-typedef int64_t  OPJ_OFF_T; /* 64-bit file offset type */
-
-#include <stdio.h>
-typedef size_t   OPJ_SIZE_T;
-
-/* Avoid compile-time warning because parameter is not used */
-#define OPJ_ARG_NOT_USED(x) (void)(x)
-
-/*
-==========================================================
-   Useful constant definitions
-==========================================================
-*/
-
-#define OPJ_PATH_LEN 4096 /**< Maximum allowed size for filenames */
-
-#define OPJ_J2K_MAXRLVLS 33                 /**< Number of maximum resolution level authorized */
-#define OPJ_J2K_MAXBANDS (3*OPJ_J2K_MAXRLVLS-2) /**< Number of maximum sub-band linked to number of resolution level */
-
-#define OPJ_J2K_DEFAULT_NB_SEGS             10
-#define OPJ_J2K_STREAM_CHUNK_SIZE           0x100000 /** 1 mega by default */
-#define OPJ_J2K_DEFAULT_HEADER_SIZE         1000
-#define OPJ_J2K_MCC_DEFAULT_NB_RECORDS      10
-#define OPJ_J2K_MCT_DEFAULT_NB_RECORDS      10
-
-/* UniPG>> */ /* NOT YET USED IN THE V2 VERSION OF OPENJPEG */
-#define JPWL_MAX_NO_TILESPECS   16 /**< Maximum number of tile parts expected by JPWL: increase at your will */
-#define JPWL_MAX_NO_PACKSPECS   16 /**< Maximum number of packet parts expected by JPWL: increase at your will */
-#define JPWL_MAX_NO_MARKERS 512 /**< Maximum number of JPWL markers: increase at your will */
-#define JPWL_PRIVATEINDEX_NAME "jpwl_index_privatefilename" /**< index file name used when JPWL is on */
-#define JPWL_EXPECTED_COMPONENTS 3 /**< Expect this number of components, so you'll find better the first EPB */
-#define JPWL_MAXIMUM_TILES 8192 /**< Expect this maximum number of tiles, to avoid some crashes */
-#define JPWL_MAXIMUM_HAMMING 2 /**< Expect this maximum number of bit errors in marker id's */
-#define JPWL_MAXIMUM_EPB_ROOM 65450 /**< Expect this maximum number of bytes for composition of EPBs */
-/* <<UniPG */
-
-/**
- * EXPERIMENTAL FOR THE MOMENT
- * Supported options about file information used only in j2k_dump
-*/
-#define OPJ_IMG_INFO        1   /**< Basic image information provided to the user */
-#define OPJ_J2K_MH_INFO     2   /**< Codestream information based only on the main header */
-#define OPJ_J2K_TH_INFO     4   /**< Tile information based on the current tile header */
-#define OPJ_J2K_TCH_INFO    8   /**< Tile/Component information of all tiles */
-#define OPJ_J2K_MH_IND      16  /**< Codestream index based only on the main header */
-#define OPJ_J2K_TH_IND      32  /**< Tile index based on the current tile */
-/*FIXME #define OPJ_J2K_CSTR_IND    48*/    /**<  */
-#define OPJ_JP2_INFO        128 /**< JP2 file information */
-#define OPJ_JP2_IND         256 /**< JP2 file index */
-
-/**
- * JPEG 2000 Profiles, see Table A.10 from 15444-1 (updated in various AMD)
- * These values help choosing the RSIZ value for the J2K codestream.
- * The RSIZ value triggers various encoding options, as detailed in Table A.10.
- * If OPJ_PROFILE_PART2 is chosen, it has to be combined with one or more extensions
- * described hereunder.
- *   Example: rsiz = OPJ_PROFILE_PART2 | OPJ_EXTENSION_MCT;
- * For broadcast profiles, the OPJ_PROFILE value has to be combined with the targeted
- * mainlevel (3-0 LSB, value between 0 and 11):
- *   Example: rsiz = OPJ_PROFILE_BC_MULTI | 0x0005; (here mainlevel 5)
- * For IMF profiles, the OPJ_PROFILE value has to be combined with the targeted mainlevel
- * (3-0 LSB, value between 0 and 11) and sublevel (7-4 LSB, value between 0 and 9):
- *   Example: rsiz = OPJ_PROFILE_IMF_2K | 0x0040 | 0x0005; (here main 5 and sublevel 4)
- * */
-#define OPJ_PROFILE_NONE        0x0000 /** no profile, conform to 15444-1 */
-#define OPJ_PROFILE_0           0x0001 /** Profile 0 as described in 15444-1,Table A.45 */
-#define OPJ_PROFILE_1           0x0002 /** Profile 1 as described in 15444-1,Table A.45 */
-#define OPJ_PROFILE_PART2       0x8000 /** At least 1 extension defined in 15444-2 (Part-2) */
-#define OPJ_PROFILE_CINEMA_2K   0x0003 /** 2K cinema profile defined in 15444-1 AMD1 */
-#define OPJ_PROFILE_CINEMA_4K   0x0004 /** 4K cinema profile defined in 15444-1 AMD1 */
-#define OPJ_PROFILE_CINEMA_S2K  0x0005 /** Scalable 2K cinema profile defined in 15444-1 AMD2 */
-#define OPJ_PROFILE_CINEMA_S4K  0x0006 /** Scalable 4K cinema profile defined in 15444-1 AMD2 */
-#define OPJ_PROFILE_CINEMA_LTS  0x0007 /** Long term storage cinema profile defined in 15444-1 AMD2 */
-#define OPJ_PROFILE_BC_SINGLE   0x0100 /** Single Tile Broadcast profile defined in 15444-1 AMD3 */
-#define OPJ_PROFILE_BC_MULTI    0x0200 /** Multi Tile Broadcast profile defined in 15444-1 AMD3 */
-#define OPJ_PROFILE_BC_MULTI_R  0x0300 /** Multi Tile Reversible Broadcast profile defined in 15444-1 AMD3 */
-#define OPJ_PROFILE_IMF_2K      0x0400 /** 2K Single Tile Lossy IMF profile defined in 15444-1 AMD 8 */
-#define OPJ_PROFILE_IMF_4K      0x0401 /** 4K Single Tile Lossy IMF profile defined in 15444-1 AMD 8 */
-#define OPJ_PROFILE_IMF_8K      0x0402 /** 8K Single Tile Lossy IMF profile defined in 15444-1 AMD 8 */
-#define OPJ_PROFILE_IMF_2K_R    0x0403 /** 2K Single/Multi Tile Reversible IMF profile defined in 15444-1 AMD 8 */
-#define OPJ_PROFILE_IMF_4K_R    0x0800 /** 4K Single/Multi Tile Reversible IMF profile defined in 15444-1 AMD 8 */
-#define OPJ_PROFILE_IMF_8K_R    0x0801  /** 8K Single/Multi Tile Reversible IMF profile defined in 15444-1 AMD 8 */
-
-/**
- * JPEG 2000 Part-2 extensions
- * */
-#define OPJ_EXTENSION_NONE      0x0000 /** No Part-2 extension */
-#define OPJ_EXTENSION_MCT       0x0100  /** Custom MCT support */
-
-/**
- * JPEG 2000 profile macros
- * */
-#define OPJ_IS_CINEMA(v)     (((v) >= OPJ_PROFILE_CINEMA_2K)&&((v) <= OPJ_PROFILE_CINEMA_S4K))
-#define OPJ_IS_STORAGE(v)    ((v) == OPJ_PROFILE_CINEMA_LTS)
-#define OPJ_IS_BROADCAST(v)  (((v) >= OPJ_PROFILE_BC_SINGLE)&&((v) <= ((OPJ_PROFILE_BC_MULTI_R) | (0x000b))))
-#define OPJ_IS_IMF(v)        (((v) >= OPJ_PROFILE_IMF_2K)&&((v) <= ((OPJ_PROFILE_IMF_8K_R) | (0x009b))))
-#define OPJ_IS_PART2(v)      ((v) & OPJ_PROFILE_PART2)
-
-/**
- * JPEG 2000 codestream and component size limits in cinema profiles
- * */
-#define OPJ_CINEMA_24_CS     1302083    /** Maximum codestream length for 24fps */
-#define OPJ_CINEMA_48_CS     651041     /** Maximum codestream length for 48fps */
-#define OPJ_CINEMA_24_COMP   1041666    /** Maximum size per color component for 2K & 4K @ 24fps */
-#define OPJ_CINEMA_48_COMP   520833     /** Maximum size per color component for 2K @ 48fps */
-
-/*
-==========================================================
-   enum definitions
-==========================================================
-*/
-
-/**
- * DEPRECATED: use RSIZ, OPJ_PROFILE_* and OPJ_EXTENSION_* instead
- * Rsiz Capabilities
- * */
-typedef enum RSIZ_CAPABILITIES {
-    OPJ_STD_RSIZ = 0,       /** Standard JPEG2000 profile*/
-    OPJ_CINEMA2K = 3,       /** Profile name for a 2K image*/
-    OPJ_CINEMA4K = 4,       /** Profile name for a 4K image*/
-    OPJ_MCT = 0x8100
-} OPJ_RSIZ_CAPABILITIES;
-
-/**
- * DEPRECATED: use RSIZ, OPJ_PROFILE_* and OPJ_EXTENSION_* instead
- * Digital cinema operation mode
- * */
-typedef enum CINEMA_MODE {
-    OPJ_OFF = 0,            /** Not Digital Cinema*/
-    OPJ_CINEMA2K_24 = 1,    /** 2K Digital Cinema at 24 fps*/
-    OPJ_CINEMA2K_48 = 2,    /** 2K Digital Cinema at 48 fps*/
-    OPJ_CINEMA4K_24 = 3     /** 4K Digital Cinema at 24 fps*/
-} OPJ_CINEMA_MODE;
-
-/**
- * Progression order
- * */
-typedef enum PROG_ORDER {
-    OPJ_PROG_UNKNOWN = -1,  /**< place-holder */
-    OPJ_LRCP = 0,           /**< layer-resolution-component-precinct order */
-    OPJ_RLCP = 1,           /**< resolution-layer-component-precinct order */
-    OPJ_RPCL = 2,           /**< resolution-precinct-component-layer order */
-    OPJ_PCRL = 3,           /**< precinct-component-resolution-layer order */
-    OPJ_CPRL = 4            /**< component-precinct-resolution-layer order */
-} OPJ_PROG_ORDER;
-
-/**
- * Supported image color spaces
-*/
-typedef enum COLOR_SPACE {
-    OPJ_CLRSPC_UNKNOWN = -1,    /**< not supported by the library */
-    OPJ_CLRSPC_UNSPECIFIED = 0, /**< not specified in the codestream */
-    OPJ_CLRSPC_SRGB = 1,        /**< sRGB */
-    OPJ_CLRSPC_GRAY = 2,        /**< grayscale */
-    OPJ_CLRSPC_SYCC = 3,        /**< YUV */
-    OPJ_CLRSPC_EYCC = 4,        /**< e-YCC */
-    OPJ_CLRSPC_CMYK = 5         /**< CMYK */
-} OPJ_COLOR_SPACE;
-
-/**
- * Supported codec
-*/
-typedef enum CODEC_FORMAT {
-    OPJ_CODEC_UNKNOWN = -1, /**< place-holder */
-    OPJ_CODEC_J2K  = 0,     /**< JPEG-2000 codestream : read/write */
-    OPJ_CODEC_JPT  = 1,     /**< JPT-stream (JPEG 2000, JPIP) : read only */
-    OPJ_CODEC_JP2  = 2,     /**< JP2 file format : read/write */
-    OPJ_CODEC_JPP  = 3,     /**< JPP-stream (JPEG 2000, JPIP) : to be coded */
-    OPJ_CODEC_JPX  = 4      /**< JPX file format (JPEG 2000 Part-2) : to be coded */
-} OPJ_CODEC_FORMAT;
-
-
-/*
-==========================================================
-   event manager typedef definitions
-==========================================================
-*/
-
-/**
- * Callback function prototype for events
- * @param msg               Event message
- * @param client_data       Client object where will be return the event message
- * */
-typedef void (*opj_msg_callback)(const char *msg, void *client_data);
-
-/*
-==========================================================
-   codec typedef definitions
-==========================================================
-*/
-
-/**
- * Progression order changes
- *
- */
-typedef struct opj_poc {
-    /** Resolution num start, Component num start, given by POC */
-    OPJ_UINT32 resno0, compno0;
-    /** Layer num end,Resolution num end, Component num end, given by POC */
-    OPJ_UINT32 layno1, resno1, compno1;
-    /** Layer num start,Precinct num start, Precinct num end */
-    OPJ_UINT32 layno0, precno0, precno1;
-    /** Progression order enum*/
-    OPJ_PROG_ORDER prg1, prg;
-    /** Progression order string*/
-    OPJ_CHAR progorder[5];
-    /** Tile number */
-    OPJ_UINT32 tile;
-    /** Start and end values for Tile width and height*/
-    OPJ_INT32 tx0, tx1, ty0, ty1;
-    /** Start value, initialised in pi_initialise_encode*/
-    OPJ_UINT32 layS, resS, compS, prcS;
-    /** End value, initialised in pi_initialise_encode */
-    OPJ_UINT32 layE, resE, compE, prcE;
-    /** Start and end values of Tile width and height, initialised in pi_initialise_encode*/
-    OPJ_UINT32 txS, txE, tyS, tyE, dx, dy;
-    /** Temporary values for Tile parts, initialised in pi_create_encode */
-    OPJ_UINT32 lay_t, res_t, comp_t, prc_t, tx0_t, ty0_t;
-} opj_poc_t;
-
-/**
- * Compression parameters
- * */
-typedef struct opj_cparameters {
-    /** size of tile: tile_size_on = false (not in argument) or = true (in argument) */
-    OPJ_BOOL tile_size_on;
-    /** XTOsiz */
-    int cp_tx0;
-    /** YTOsiz */
-    int cp_ty0;
-    /** XTsiz */
-    int cp_tdx;
-    /** YTsiz */
-    int cp_tdy;
-    /** allocation by rate/distortion */
-    int cp_disto_alloc;
-    /** allocation by fixed layer */
-    int cp_fixed_alloc;
-    /** add fixed_quality */
-    int cp_fixed_quality;
-    /** fixed layer */
-    int *cp_matrice;
-    /** comment for coding */
-    char *cp_comment;
-    /** csty : coding style */
-    int csty;
-    /** progression order (default OPJ_LRCP) */
-    OPJ_PROG_ORDER prog_order;
-    /** progression order changes */
-    opj_poc_t POC[32];
-    /** number of progression order changes (POC), default to 0 */
-    OPJ_UINT32 numpocs;
-    /** number of layers */
-    int tcp_numlayers;
-    /** rates of layers - might be subsequently limited by the max_cs_size field.
-     * Should be decreasing. 1 can be
-     * used as last value to indicate the last layer is lossless. */
-    float tcp_rates[100];
-    /** different psnr for successive layers. Should be increasing. 0 can be
-     * used as last value to indicate the last layer is lossless. */
-    float tcp_distoratio[100];
-    /** number of resolutions */
-    int numresolution;
-    /** initial code block width, default to 64 */
-    int cblockw_init;
-    /** initial code block height, default to 64 */
-    int cblockh_init;
-    /** mode switch (cblk_style) */
-    int mode;
-    /** 1 : use the irreversible DWT 9-7, 0 : use lossless compression (default) */
-    int irreversible;
-    /** region of interest: affected component in [0..3], -1 means no ROI */
-    int roi_compno;
-    /** region of interest: upshift value */
-    int roi_shift;
-    /* number of precinct size specifications */
-    int res_spec;
-    /** initial precinct width */
-    int prcw_init[OPJ_J2K_MAXRLVLS];
-    /** initial precinct height */
-    int prch_init[OPJ_J2K_MAXRLVLS];
-
-    /**@name command line encoder parameters (not used inside the library) */
-    /*@{*/
-    /** input file name */
-    char infile[OPJ_PATH_LEN];
-    /** output file name */
-    char outfile[OPJ_PATH_LEN];
-    /** DEPRECATED. Index generation is now handeld with the opj_encode_with_info() function. Set to NULL */
-    int index_on;
-    /** DEPRECATED. Index generation is now handeld with the opj_encode_with_info() function. Set to NULL */
-    char index[OPJ_PATH_LEN];
-    /** subimage encoding: origin image offset in x direction */
-    int image_offset_x0;
-    /** subimage encoding: origin image offset in y direction */
-    int image_offset_y0;
-    /** subsampling value for dx */
-    int subsampling_dx;
-    /** subsampling value for dy */
-    int subsampling_dy;
-    /** input file format 0: PGX, 1: PxM, 2: BMP 3:TIF*/
-    int decod_format;
-    /** output file format 0: J2K, 1: JP2, 2: JPT */
-    int cod_format;
-    /*@}*/
-
-    /* UniPG>> */ /* NOT YET USED IN THE V2 VERSION OF OPENJPEG */
-    /**@name JPWL encoding parameters */
-    /*@{*/
-    /** enables writing of EPC in MH, thus activating JPWL */
-    OPJ_BOOL jpwl_epc_on;
-    /** error protection method for MH (0,1,16,32,37-128) */
-    int jpwl_hprot_MH;
-    /** tile number of header protection specification (>=0) */
-    int jpwl_hprot_TPH_tileno[JPWL_MAX_NO_TILESPECS];
-    /** error protection methods for TPHs (0,1,16,32,37-128) */
-    int jpwl_hprot_TPH[JPWL_MAX_NO_TILESPECS];
-    /** tile number of packet protection specification (>=0) */
-    int jpwl_pprot_tileno[JPWL_MAX_NO_PACKSPECS];
-    /** packet number of packet protection specification (>=0) */
-    int jpwl_pprot_packno[JPWL_MAX_NO_PACKSPECS];
-    /** error protection methods for packets (0,1,16,32,37-128) */
-    int jpwl_pprot[JPWL_MAX_NO_PACKSPECS];
-    /** enables writing of ESD, (0=no/1/2 bytes) */
-    int jpwl_sens_size;
-    /** sensitivity addressing size (0=auto/2/4 bytes) */
-    int jpwl_sens_addr;
-    /** sensitivity range (0-3) */
-    int jpwl_sens_range;
-    /** sensitivity method for MH (-1=no,0-7) */
-    int jpwl_sens_MH;
-    /** tile number of sensitivity specification (>=0) */
-    int jpwl_sens_TPH_tileno[JPWL_MAX_NO_TILESPECS];
-    /** sensitivity methods for TPHs (-1=no,0-7) */
-    int jpwl_sens_TPH[JPWL_MAX_NO_TILESPECS];
-    /*@}*/
-    /* <<UniPG */
-
-    /**
-     * DEPRECATED: use RSIZ, OPJ_PROFILE_* and MAX_COMP_SIZE instead
-     * Digital Cinema compliance 0-not compliant, 1-compliant
-     * */
-    OPJ_CINEMA_MODE cp_cinema;
-    /**
-     * Maximum size (in bytes) for each component.
-     * If == 0, component size limitation is not considered
-     * */
-    int max_comp_size;
-    /**
-     * DEPRECATED: use RSIZ, OPJ_PROFILE_* and OPJ_EXTENSION_* instead
-     * Profile name
-     * */
-    OPJ_RSIZ_CAPABILITIES cp_rsiz;
-    /** Tile part generation*/
-    char tp_on;
-    /** Flag for Tile part generation*/
-    char tp_flag;
-    /** MCT (multiple component transform) */
-    char tcp_mct;
-    /** Enable JPIP indexing*/
-    OPJ_BOOL jpip_on;
-    /** Naive implementation of MCT restricted to a single reversible array based
-        encoding without offset concerning all the components. */
-    void * mct_data;
-    /**
-     * Maximum size (in bytes) for the whole codestream.
-     * If == 0, codestream size limitation is not considered
-     * If it does not comply with tcp_rates, max_cs_size prevails
-     * and a warning is issued.
-     * */
-    int max_cs_size;
-    /** RSIZ value
-        To be used to combine OPJ_PROFILE_*, OPJ_EXTENSION_* and (sub)levels values. */
-    OPJ_UINT16 rsiz;
-} opj_cparameters_t;
-
-#define OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG  0x0001
-#define OPJ_DPARAMETERS_DUMP_FLAG 0x0002
-
-/**
- * Decompression parameters
- * */
-typedef struct opj_dparameters {
-    /**
-    Set the number of highest resolution levels to be discarded.
-    The image resolution is effectively divided by 2 to the power of the number of discarded levels.
-    The reduce factor is limited by the smallest total number of decomposition levels among tiles.
-    if != 0, then original dimension divided by 2^(reduce);
-    if == 0 or not used, image is decoded to the full resolution
-    */
-    OPJ_UINT32 cp_reduce;
-    /**
-    Set the maximum number of quality layers to decode.
-    If there are less quality layers than the specified number, all the quality layers are decoded.
-    if != 0, then only the first "layer" layers are decoded;
-    if == 0 or not used, all the quality layers are decoded
-    */
-    OPJ_UINT32 cp_layer;
-
-    /**@name command line decoder parameters (not used inside the library) */
-    /*@{*/
-    /** input file name */
-    char infile[OPJ_PATH_LEN];
-    /** output file name */
-    char outfile[OPJ_PATH_LEN];
-    /** input file format 0: J2K, 1: JP2, 2: JPT */
-    int decod_format;
-    /** output file format 0: PGX, 1: PxM, 2: BMP */
-    int cod_format;
-
-    /** Decoding area left boundary */
-    OPJ_UINT32 DA_x0;
-    /** Decoding area right boundary */
-    OPJ_UINT32 DA_x1;
-    /** Decoding area up boundary */
-    OPJ_UINT32 DA_y0;
-    /** Decoding area bottom boundary */
-    OPJ_UINT32 DA_y1;
-    /** Verbose mode */
-    OPJ_BOOL m_verbose;
-
-    /** tile number of the decoded tile */
-    OPJ_UINT32 tile_index;
-    /** Nb of tile to decode */
-    OPJ_UINT32 nb_tile_to_decode;
-
-    /*@}*/
-
-    /* UniPG>> */ /* NOT YET USED IN THE V2 VERSION OF OPENJPEG */
-    /**@name JPWL decoding parameters */
-    /*@{*/
-    /** activates the JPWL correction capabilities */
-    OPJ_BOOL jpwl_correct;
-    /** expected number of components */
-    int jpwl_exp_comps;
-    /** maximum number of tiles */
-    int jpwl_max_tiles;
-    /*@}*/
-    /* <<UniPG */
-
-    unsigned int flags;
-
-} opj_dparameters_t;
-
-
-/**
- * JPEG2000 codec V2.
- * */
-typedef void * opj_codec_t;
-
-/*
-==========================================================
-   I/O stream typedef definitions
-==========================================================
-*/
-
-/**
- * Stream open flags.
- * */
-/** The stream was opened for reading. */
-#define OPJ_STREAM_READ OPJ_TRUE
-/** The stream was opened for writing. */
-#define OPJ_STREAM_WRITE OPJ_FALSE
-
-/*
- * Callback function prototype for read function
- */
-typedef OPJ_SIZE_T(* opj_stream_read_fn)(void * p_buffer, OPJ_SIZE_T p_nb_bytes,
-        void * p_user_data) ;
-
-/*
- * Callback function prototype for write function
- */
-typedef OPJ_SIZE_T(* opj_stream_write_fn)(void * p_buffer,
-        OPJ_SIZE_T p_nb_bytes, void * p_user_data) ;
-
-/*
- * Callback function prototype for skip function
- */
-typedef OPJ_OFF_T(* opj_stream_skip_fn)(OPJ_OFF_T p_nb_bytes,
-                                        void * p_user_data) ;
-
-/*
- * Callback function prototype for seek function
- */
-typedef OPJ_BOOL(* opj_stream_seek_fn)(OPJ_OFF_T p_nb_bytes,
-                                       void * p_user_data) ;
-
-/*
- * Callback function prototype for free user data function
- */
-typedef void (* opj_stream_free_user_data_fn)(void * p_user_data) ;
-
-/*
- * JPEG2000 Stream.
- */
-typedef void * opj_stream_t;
-
-/*
-==========================================================
-   image typedef definitions
-==========================================================
-*/
-
-/**
- * Defines a single image component
- * */
-typedef struct opj_image_comp {
-    /** XRsiz: horizontal separation of a sample of ith component with respect to the reference grid */
-    OPJ_UINT32 dx;
-    /** YRsiz: vertical separation of a sample of ith component with respect to the reference grid */
-    OPJ_UINT32 dy;
-    /** data width */
-    OPJ_UINT32 w;
-    /** data height */
-    OPJ_UINT32 h;
-    /** x component offset compared to the whole image */
-    OPJ_UINT32 x0;
-    /** y component offset compared to the whole image */
-    OPJ_UINT32 y0;
-    /** precision */
-    OPJ_UINT32 prec;
-    /** image depth in bits */
-    OPJ_UINT32 bpp;
-    /** signed (1) / unsigned (0) */
-    OPJ_UINT32 sgnd;
-    /** number of decoded resolution */
-    OPJ_UINT32 resno_decoded;
-    /** number of division by 2 of the out image compared to the original size of image */
-    OPJ_UINT32 factor;
-    /** image component data */
-    OPJ_INT32 *data;
-    /** alpha channel */
-    OPJ_UINT16 alpha;
-} opj_image_comp_t;
-
-/**
- * Defines image data and characteristics
- * */
-typedef struct opj_image {
-    /** XOsiz: horizontal offset from the origin of the reference grid to the left side of the image area */
-    OPJ_UINT32 x0;
-    /** YOsiz: vertical offset from the origin of the reference grid to the top side of the image area */
-    OPJ_UINT32 y0;
-    /** Xsiz: width of the reference grid */
-    OPJ_UINT32 x1;
-    /** Ysiz: height of the reference grid */
-    OPJ_UINT32 y1;
-    /** number of components in the image */
-    OPJ_UINT32 numcomps;
-    /** color space: sRGB, Greyscale or YUV */
-    OPJ_COLOR_SPACE color_space;
-    /** image components */
-    opj_image_comp_t *comps;
-    /** 'restricted' ICC profile */
-    OPJ_BYTE *icc_profile_buf;
-    /** size of ICC profile */
-    OPJ_UINT32 icc_profile_len;
-} opj_image_t;
-
-
-/**
- * Component parameters structure used by the opj_image_create function
- * */
-typedef struct opj_image_comptparm {
-    /** XRsiz: horizontal separation of a sample of ith component with respect to the reference grid */
-    OPJ_UINT32 dx;
-    /** YRsiz: vertical separation of a sample of ith component with respect to the reference grid */
-    OPJ_UINT32 dy;
-    /** data width */
-    OPJ_UINT32 w;
-    /** data height */
-    OPJ_UINT32 h;
-    /** x component offset compared to the whole image */
-    OPJ_UINT32 x0;
-    /** y component offset compared to the whole image */
-    OPJ_UINT32 y0;
-    /** precision */
-    OPJ_UINT32 prec;
-    /** image depth in bits */
-    OPJ_UINT32 bpp;
-    /** signed (1) / unsigned (0) */
-    OPJ_UINT32 sgnd;
-} opj_image_cmptparm_t;
-
-
-/*
-==========================================================
-   Information on the JPEG 2000 codestream
-==========================================================
-*/
-/* QUITE EXPERIMENTAL FOR THE MOMENT */
-
-/**
- * Index structure : Information concerning a packet inside tile
- * */
-typedef struct opj_packet_info {
-    /** packet start position (including SOP marker if it exists) */
-    OPJ_OFF_T start_pos;
-    /** end of packet header position (including EPH marker if it exists)*/
-    OPJ_OFF_T end_ph_pos;
-    /** packet end position */
-    OPJ_OFF_T end_pos;
-    /** packet distorsion */
-    double disto;
-} opj_packet_info_t;
-
-
-/* UniPG>> */
-/**
- * Marker structure
- * */
-typedef struct opj_marker_info {
-    /** marker type */
-    unsigned short int type;
-    /** position in codestream */
-    OPJ_OFF_T pos;
-    /** length, marker val included */
-    int len;
-} opj_marker_info_t;
-/* <<UniPG */
-
-/**
- * Index structure : Information concerning tile-parts
-*/
-typedef struct opj_tp_info {
-    /** start position of tile part */
-    int tp_start_pos;
-    /** end position of tile part header */
-    int tp_end_header;
-    /** end position of tile part */
-    int tp_end_pos;
-    /** start packet of tile part */
-    int tp_start_pack;
-    /** number of packets of tile part */
-    int tp_numpacks;
-} opj_tp_info_t;
-
-/**
- * Index structure : information regarding tiles
-*/
-typedef struct opj_tile_info {
-    /** value of thresh for each layer by tile cfr. Marcela   */
-    double *thresh;
-    /** number of tile */
-    int tileno;
-    /** start position */
-    int start_pos;
-    /** end position of the header */
-    int end_header;
-    /** end position */
-    int end_pos;
-    /** precinct number for each resolution level (width) */
-    int pw[33];
-    /** precinct number for each resolution level (height) */
-    int ph[33];
-    /** precinct size (in power of 2), in X for each resolution level */
-    int pdx[33];
-    /** precinct size (in power of 2), in Y for each resolution level */
-    int pdy[33];
-    /** information concerning packets inside tile */
-    opj_packet_info_t *packet;
-    /** add fixed_quality */
-    int numpix;
-    /** add fixed_quality */
-    double distotile;
-    /** number of markers */
-    int marknum;
-    /** list of markers */
-    opj_marker_info_t *marker;
-    /** actual size of markers array */
-    int maxmarknum;
-    /** number of tile parts */
-    int num_tps;
-    /** information concerning tile parts */
-    opj_tp_info_t *tp;
-} opj_tile_info_t;
-
-/**
- * Index structure of the codestream
-*/
-typedef struct opj_codestream_info {
-    /** maximum distortion reduction on the whole image (add for Marcela) */
-    double D_max;
-    /** packet number */
-    int packno;
-    /** writing the packet in the index with t2_encode_packets */
-    int index_write;
-    /** image width */
-    int image_w;
-    /** image height */
-    int image_h;
-    /** progression order */
-    OPJ_PROG_ORDER prog;
-    /** tile size in x */
-    int tile_x;
-    /** tile size in y */
-    int tile_y;
-    /** */
-    int tile_Ox;
-    /** */
-    int tile_Oy;
-    /** number of tiles in X */
-    int tw;
-    /** number of tiles in Y */
-    int th;
-    /** component numbers */
-    int numcomps;
-    /** number of layer */
-    int numlayers;
-    /** number of decomposition for each component */
-    int *numdecompos;
-    /* UniPG>> */
-    /** number of markers */
-    int marknum;
-    /** list of markers */
-    opj_marker_info_t *marker;
-    /** actual size of markers array */
-    int maxmarknum;
-    /* <<UniPG */
-    /** main header position */
-    int main_head_start;
-    /** main header position */
-    int main_head_end;
-    /** codestream's size */
-    int codestream_size;
-    /** information regarding tiles inside image */
-    opj_tile_info_t *tile;
-} opj_codestream_info_t;
-
-/* <----------------------------------------------------------- */
-/* new output management of the codestream information and index */
-
-/**
- * Tile-component coding parameters information
- */
-typedef struct opj_tccp_info {
-    /** component index */
-    OPJ_UINT32 compno;
-    /** coding style */
-    OPJ_UINT32 csty;
-    /** number of resolutions */
-    OPJ_UINT32 numresolutions;
-    /** log2 of code-blocks width */
-    OPJ_UINT32 cblkw;
-    /** log2 of code-blocks height */
-    OPJ_UINT32 cblkh;
-    /** code-block coding style */
-    OPJ_UINT32 cblksty;
-    /** discrete wavelet transform identifier: 0 = 9-7 irreversible, 1 = 5-3 reversible */
-    OPJ_UINT32 qmfbid;
-    /** quantisation style */
-    OPJ_UINT32 qntsty;
-    /** stepsizes used for quantization */
-    OPJ_UINT32 stepsizes_mant[OPJ_J2K_MAXBANDS];
-    /** stepsizes used for quantization */
-    OPJ_UINT32 stepsizes_expn[OPJ_J2K_MAXBANDS];
-    /** number of guard bits */
-    OPJ_UINT32 numgbits;
-    /** Region Of Interest shift */
-    OPJ_INT32 roishift;
-    /** precinct width */
-    OPJ_UINT32 prcw[OPJ_J2K_MAXRLVLS];
-    /** precinct height */
-    OPJ_UINT32 prch[OPJ_J2K_MAXRLVLS];
-}
-opj_tccp_info_t;
-
-/**
- * Tile coding parameters information
- */
-typedef struct opj_tile_v2_info {
-
-    /** number (index) of tile */
-    int tileno;
-    /** coding style */
-    OPJ_UINT32 csty;
-    /** progression order */
-    OPJ_PROG_ORDER prg;
-    /** number of layers */
-    OPJ_UINT32 numlayers;
-    /** multi-component transform identifier */
-    OPJ_UINT32 mct;
-
-    /** information concerning tile component parameters*/
-    opj_tccp_info_t *tccp_info;
-
-} opj_tile_info_v2_t;
-
-/**
- * Information structure about the codestream (FIXME should be expand and enhance)
- */
-typedef struct opj_codestream_info_v2 {
-    /* Tile info */
-    /** tile origin in x = XTOsiz */
-    OPJ_UINT32 tx0;
-    /** tile origin in y = YTOsiz */
-    OPJ_UINT32 ty0;
-    /** tile size in x = XTsiz */
-    OPJ_UINT32 tdx;
-    /** tile size in y = YTsiz */
-    OPJ_UINT32 tdy;
-    /** number of tiles in X */
-    OPJ_UINT32 tw;
-    /** number of tiles in Y */
-    OPJ_UINT32 th;
-
-    /** number of components*/
-    OPJ_UINT32 nbcomps;
-
-    /** Default information regarding tiles inside image */
-    opj_tile_info_v2_t m_default_tile_info;
-
-    /** information regarding tiles inside image */
-    opj_tile_info_v2_t *tile_info; /* FIXME not used for the moment */
-
-} opj_codestream_info_v2_t;
-
-
-/**
- * Index structure about a tile part
- */
-typedef struct opj_tp_index {
-    /** start position */
-    OPJ_OFF_T start_pos;
-    /** end position of the header */
-    OPJ_OFF_T end_header;
-    /** end position */
-    OPJ_OFF_T end_pos;
-
-} opj_tp_index_t;
-
-/**
- * Index structure about a tile
- */
-typedef struct opj_tile_index {
-    /** tile index */
-    OPJ_UINT32 tileno;
-
-    /** number of tile parts */
-    OPJ_UINT32 nb_tps;
-    /** current nb of tile part (allocated)*/
-    OPJ_UINT32 current_nb_tps;
-    /** current tile-part index */
-    OPJ_UINT32 current_tpsno;
-    /** information concerning tile parts */
-    opj_tp_index_t *tp_index;
-
-    /* UniPG>> */ /* NOT USED FOR THE MOMENT IN THE V2 VERSION */
-    /** number of markers */
-    OPJ_UINT32 marknum;
-    /** list of markers */
-    opj_marker_info_t *marker;
-    /** actual size of markers array */
-    OPJ_UINT32 maxmarknum;
-    /* <<UniPG */
-
-    /** packet number */
-    OPJ_UINT32 nb_packet;
-    /** information concerning packets inside tile */
-    opj_packet_info_t *packet_index;
-
-} opj_tile_index_t;
-
-/**
- * Index structure of the codestream (FIXME should be expand and enhance)
- */
-typedef struct opj_codestream_index {
-    /** main header start position (SOC position) */
-    OPJ_OFF_T main_head_start;
-    /** main header end position (first SOT position) */
-    OPJ_OFF_T main_head_end;
-
-    /** codestream's size */
-    OPJ_UINT64 codestream_size;
-
-    /* UniPG>> */ /* NOT USED FOR THE MOMENT IN THE V2 VERSION */
-    /** number of markers */
-    OPJ_UINT32 marknum;
-    /** list of markers */
-    opj_marker_info_t *marker;
-    /** actual size of markers array */
-    OPJ_UINT32 maxmarknum;
-    /* <<UniPG */
-
-    /** */
-    OPJ_UINT32 nb_of_tiles;
-    /** */
-    opj_tile_index_t *tile_index; /* FIXME not used for the moment */
-
-} opj_codestream_index_t;
-/* -----------------------------------------------------------> */
-
-/*
-==========================================================
-   Metadata from the JP2file
-==========================================================
-*/
-
-/**
- * Info structure of the JP2 file
- * EXPERIMENTAL FOR THE MOMENT
- */
-typedef struct opj_jp2_metadata {
-    /** */
-    OPJ_INT32   not_used;
-
-} opj_jp2_metadata_t;
-
-/**
- * Index structure of the JP2 file
- * EXPERIMENTAL FOR THE MOMENT
- */
-typedef struct opj_jp2_index {
-    /** */
-    OPJ_INT32   not_used;
-
-} opj_jp2_index_t;
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-/*
-==========================================================
-   openjpeg version
-==========================================================
-*/
-
-/* Get the version of the openjpeg library*/
-OPJ_API const char * OPJ_CALLCONV opj_version(void);
-
-/*
-==========================================================
-   image functions definitions
-==========================================================
-*/
-
-/**
- * Create an image
- *
- * @param numcmpts      number of components
- * @param cmptparms     components parameters
- * @param clrspc        image color space
- * @return returns      a new image structure if successful, returns NULL otherwise
- * */
-OPJ_API opj_image_t* OPJ_CALLCONV opj_image_create(OPJ_UINT32 numcmpts,
-        opj_image_cmptparm_t *cmptparms, OPJ_COLOR_SPACE clrspc);
-
-/**
- * Deallocate any resources associated with an image
- *
- * @param image         image to be destroyed
- */
-OPJ_API void OPJ_CALLCONV opj_image_destroy(opj_image_t *image);
-
-/**
- * Creates an image without allocating memory for the image (used in the new version of the library).
- *
- * @param   numcmpts    the number of components
- * @param   cmptparms   the components parameters
- * @param   clrspc      the image color space
- *
- * @return  a new image structure if successful, NULL otherwise.
-*/
-OPJ_API opj_image_t* OPJ_CALLCONV opj_image_tile_create(OPJ_UINT32 numcmpts,
-        opj_image_cmptparm_t *cmptparms, OPJ_COLOR_SPACE clrspc);
-
-/**
- * Allocator for opj_image_t->comps[].data
- * To be paired with opj_image_data_free.
- *
- * @param   size    number of bytes to allocate
- *
- * @return  a new pointer if successful, NULL otherwise.
- * @since 2.2.0
-*/
-OPJ_API void* OPJ_CALLCONV opj_image_data_alloc(OPJ_SIZE_T size);
-
-/**
- * Destructor for opj_image_t->comps[].data
- * To be paired with opj_image_data_alloc.
- *
- * @param   ptr    Pointer to free
- *
- * @since 2.2.0
-*/
-OPJ_API void OPJ_CALLCONV opj_image_data_free(void* ptr);
-
-/*
-==========================================================
-   stream functions definitions
-==========================================================
-*/
-
-/**
- * Creates an abstract stream. This function does nothing except allocating memory and initializing the abstract stream.
- *
- * @param   p_is_input      if set to true then the stream will be an input stream, an output stream else.
- *
- * @return  a stream object.
-*/
-OPJ_API opj_stream_t* OPJ_CALLCONV opj_stream_default_create(
-    OPJ_BOOL p_is_input);
-
-/**
- * Creates an abstract stream. This function does nothing except allocating memory and initializing the abstract stream.
- *
- * @param   p_buffer_size  FIXME DOC
- * @param   p_is_input      if set to true then the stream will be an input stream, an output stream else.
- *
- * @return  a stream object.
-*/
-OPJ_API opj_stream_t* OPJ_CALLCONV opj_stream_create(OPJ_SIZE_T p_buffer_size,
-        OPJ_BOOL p_is_input);
-
-/**
- * Destroys a stream created by opj_create_stream. This function does NOT close the abstract stream. If needed the user must
- * close its own implementation of the stream.
- *
- * @param   p_stream    the stream to destroy.
- */
-OPJ_API void OPJ_CALLCONV opj_stream_destroy(opj_stream_t* p_stream);
-
-/**
- * Sets the given function to be used as a read function.
- * @param       p_stream    the stream to modify
- * @param       p_function  the function to use a read function.
-*/
-OPJ_API void OPJ_CALLCONV opj_stream_set_read_function(opj_stream_t* p_stream,
-        opj_stream_read_fn p_function);
-
-/**
- * Sets the given function to be used as a write function.
- * @param       p_stream    the stream to modify
- * @param       p_function  the function to use a write function.
-*/
-OPJ_API void OPJ_CALLCONV opj_stream_set_write_function(opj_stream_t* p_stream,
-        opj_stream_write_fn p_function);
-
-/**
- * Sets the given function to be used as a skip function.
- * @param       p_stream    the stream to modify
- * @param       p_function  the function to use a skip function.
-*/
-OPJ_API void OPJ_CALLCONV opj_stream_set_skip_function(opj_stream_t* p_stream,
-        opj_stream_skip_fn p_function);
-
-/**
- * Sets the given function to be used as a seek function, the stream is then seekable,
- * using SEEK_SET behavior.
- * @param       p_stream    the stream to modify
- * @param       p_function  the function to use a skip function.
-*/
-OPJ_API void OPJ_CALLCONV opj_stream_set_seek_function(opj_stream_t* p_stream,
-        opj_stream_seek_fn p_function);
-
-/**
- * Sets the given data to be used as a user data for the stream.
- * @param       p_stream    the stream to modify
- * @param       p_data      the data to set.
- * @param       p_function  the function to free p_data when opj_stream_destroy() is called.
-*/
-OPJ_API void OPJ_CALLCONV opj_stream_set_user_data(opj_stream_t* p_stream,
-        void * p_data, opj_stream_free_user_data_fn p_function);
-
-/**
- * Sets the length of the user data for the stream.
- *
- * @param p_stream    the stream to modify
- * @param data_length length of the user_data.
-*/
-OPJ_API void OPJ_CALLCONV opj_stream_set_user_data_length(
-    opj_stream_t* p_stream, OPJ_UINT64 data_length);
-
-/**
- * Create a stream from a file identified with its filename with default parameters (helper function)
- * @param fname             the filename of the file to stream
- * @param p_is_read_stream  whether the stream is a read stream (true) or not (false)
-*/
-OPJ_API opj_stream_t* OPJ_CALLCONV opj_stream_create_default_file_stream(
-    const char *fname, OPJ_BOOL p_is_read_stream);
-
-/** Create a stream from a file identified with its filename with a specific buffer size
- * @param fname             the filename of the file to stream
- * @param p_buffer_size     size of the chunk used to stream
- * @param p_is_read_stream  whether the stream is a read stream (true) or not (false)
-*/
-OPJ_API opj_stream_t* OPJ_CALLCONV opj_stream_create_file_stream(
-    const char *fname,
-    OPJ_SIZE_T p_buffer_size,
-    OPJ_BOOL p_is_read_stream);
-
-/*
-==========================================================
-   event manager functions definitions
-==========================================================
-*/
-/**
- * Set the info handler use by openjpeg.
- * @param p_codec       the codec previously initialise
- * @param p_callback    the callback function which will be used
- * @param p_user_data   client object where will be returned the message
-*/
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_set_info_handler(opj_codec_t * p_codec,
-        opj_msg_callback p_callback,
-        void * p_user_data);
-/**
- * Set the warning handler use by openjpeg.
- * @param p_codec       the codec previously initialise
- * @param p_callback    the callback function which will be used
- * @param p_user_data   client object where will be returned the message
-*/
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_set_warning_handler(opj_codec_t * p_codec,
-        opj_msg_callback p_callback,
-        void * p_user_data);
-/**
- * Set the error handler use by openjpeg.
- * @param p_codec       the codec previously initialise
- * @param p_callback    the callback function which will be used
- * @param p_user_data   client object where will be returned the message
-*/
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_set_error_handler(opj_codec_t * p_codec,
-        opj_msg_callback p_callback,
-        void * p_user_data);
-
-/*
-==========================================================
-   codec functions definitions
-==========================================================
-*/
-
-/**
- * Creates a J2K/JP2 decompression structure
- * @param format        Decoder to select
- *
- * @return Returns a handle to a decompressor if successful, returns NULL otherwise
- * */
-OPJ_API opj_codec_t* OPJ_CALLCONV opj_create_decompress(
-    OPJ_CODEC_FORMAT format);
-
-/**
- * Destroy a decompressor handle
- *
- * @param   p_codec         decompressor handle to destroy
- */
-OPJ_API void OPJ_CALLCONV opj_destroy_codec(opj_codec_t * p_codec);
-
-/**
- * Read after the codestream if necessary
- * @param   p_codec         the JPEG2000 codec to read.
- * @param   p_stream        the JPEG2000 stream.
- */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_end_decompress(opj_codec_t *p_codec,
-        opj_stream_t *p_stream);
-
-
-/**
- * Set decoding parameters to default values
- * @param parameters Decompression parameters
- */
-OPJ_API void OPJ_CALLCONV opj_set_default_decoder_parameters(
-    opj_dparameters_t *parameters);
-
-/**
- * Setup the decoder with decompression parameters provided by the user and with the message handler
- * provided by the user.
- *
- * @param p_codec       decompressor handler
- * @param parameters    decompression parameters
- *
- * @return true         if the decoder is correctly set
- */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_setup_decoder(opj_codec_t *p_codec,
-        opj_dparameters_t *parameters);
-
-/**
- * Allocates worker threads for the compressor/decompressor.
- *
- * By default, only the main thread is used. If this function is not used,
- * but the OPJ_NUM_THREADS environment variable is set, its value will be
- * used to initialize the number of threads. The value can be either an integer
- * number, or "ALL_CPUS". If OPJ_NUM_THREADS is set and this function is called,
- * this function will override the behaviour of the environment variable.
- *
- * Currently this function must be called after opj_setup_decoder() and
- * before opj_read_header().
- *
- * Note: currently only has effect on the decompressor.
- *
- * @param p_codec       decompressor handler
- * @param num_threads   number of threads.
- *
- * @return OPJ_TRUE     if the decoder is correctly set
- */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_codec_set_threads(opj_codec_t *p_codec,
-        int num_threads);
-
-/**
- * Decodes an image header.
- *
- * @param   p_stream        the jpeg2000 stream.
- * @param   p_codec         the jpeg2000 codec to read.
- * @param   p_image         the image structure initialized with the characteristics of encoded image.
- *
- * @return true             if the main header of the codestream and the JP2 header is correctly read.
- */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_read_header(opj_stream_t *p_stream,
-        opj_codec_t *p_codec,
-        opj_image_t **p_image);
-
-
-/** Restrict the number of components to decode.
- *
- * This function should be called after opj_read_header().
- *
- * This function enables to restrict the set of decoded components to the
- * specified indices.
- * Note that the current implementation (apply_color_transforms == OPJ_FALSE)
- * is such that neither the multi-component transform at codestream level,
- * nor JP2 channel transformations will be applied.
- * Consequently the indices are relative to the codestream.
- *
- * Note: opj_decode_tile_data() should not be used together with opj_set_decoded_components().
- *
- * @param   p_codec         the jpeg2000 codec to read.
- * @param   numcomps        Size of the comps_indices array.
- * @param   comps_indices   Array of numcomps values representing the indices
- *                          of the components to decode (relative to the
- *                          codestream, starting at 0)
- * @param   apply_color_transforms Whether multi-component transform at codestream level
- *                                 or JP2 channel transformations should be applied.
- *                                 Currently this parameter should be set to OPJ_FALSE.
- *                                 Setting it to OPJ_TRUE will result in an error.
- *
- * @return OPJ_TRUE         in case of success.
- */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_set_decoded_components(opj_codec_t *p_codec,
-        OPJ_UINT32 numcomps,
-        const OPJ_UINT32* comps_indices,
-        OPJ_BOOL apply_color_transforms);
-
-/**
- * Sets the given area to be decoded. This function should be called right after opj_read_header and before any tile header reading.
- *
- * The coordinates passed to this function should be expressed in the reference grid,
- * that is to say at the highest resolution level, even if requesting the image at lower
- * resolution levels.
- *
- * Generally opj_set_decode_area() should be followed by opj_decode(), and the
- * codec cannot be re-used.
- * In the particular case of an image made of a single tile, several sequences of
- * calls to opoj_set_decode_area() and opj_decode() are allowed, and will bring
- * performance improvements when reading an image by chunks.
- *
- * @param   p_codec         the jpeg2000 codec.
- * @param   p_image         the decoded image previously set by opj_read_header
- * @param   p_start_x       the left position of the rectangle to decode (in image coordinates).
- * @param   p_end_x         the right position of the rectangle to decode (in image coordinates).
- * @param   p_start_y       the up position of the rectangle to decode (in image coordinates).
- * @param   p_end_y         the bottom position of the rectangle to decode (in image coordinates).
- *
- * @return  true            if the area could be set.
- */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_set_decode_area(opj_codec_t *p_codec,
-        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);
-
-/**
- * Decode an image from a JPEG-2000 codestream
- *
- * @param p_decompressor    decompressor handle
- * @param p_stream          Input buffer stream
- * @param p_image           the decoded image
- * @return                  true if success, otherwise false
- * */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_decode(opj_codec_t *p_decompressor,
-        opj_stream_t *p_stream,
-        opj_image_t *p_image);
-
-/**
- * Get the decoded tile from the codec
- *
- * @param   p_codec         the jpeg2000 codec.
- * @param   p_stream        input streamm
- * @param   p_image         output image
- * @param   tile_index      index of the tile which will be decode
- *
- * @return                  true if success, otherwise false
- */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_get_decoded_tile(opj_codec_t *p_codec,
-        opj_stream_t *p_stream,
-        opj_image_t *p_image,
-        OPJ_UINT32 tile_index);
-
-/**
- * Set the resolution factor of the decoded image
- * @param   p_codec         the jpeg2000 codec.
- * @param   res_factor      resolution factor to set
- *
- * @return                  true if success, otherwise false
- */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_set_decoded_resolution_factor(
-    opj_codec_t *p_codec, OPJ_UINT32 res_factor);
-
-/**
- * Writes a tile with the given data.
- *
- * @param   p_codec             the jpeg2000 codec.
- * @param   p_tile_index        the index of the tile to write. At the moment, the tiles must be written from 0 to n-1 in sequence.
- * @param   p_data              pointer to the data to write. Data is arranged in sequence, data_comp0, then data_comp1, then ... NO INTERLEAVING should be set.
- * @param   p_data_size         this value os used to make sure the data being written is correct. The size must be equal to the sum for each component of
- *                              tile_width * tile_height * component_size. component_size can be 1,2 or 4 bytes, depending on the precision of the given component.
- * @param   p_stream            the stream to write data to.
- *
- * @return  true if the data could be written.
- */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_write_tile(opj_codec_t *p_codec,
-        OPJ_UINT32 p_tile_index,
-        OPJ_BYTE * p_data,
-        OPJ_UINT32 p_data_size,
-        opj_stream_t *p_stream);
-
-/**
- * Reads a tile header. This function is compulsory and allows one to know the size of the tile that will be decoded.
- * The user may need to refer to the image got by opj_read_header to understand the size being taken by the tile.
- *
- * @param   p_codec         the jpeg2000 codec.
- * @param   p_tile_index    pointer to a value that will hold the index of the tile being decoded, in case of success.
- * @param   p_data_size     pointer to a value that will hold the maximum size of the decoded data, in case of success. In case
- *                          of truncated codestreams, the actual number of bytes decoded may be lower. The computation of the size is the same
- *                          as depicted in opj_write_tile.
- * @param   p_tile_x0       pointer to a value that will hold the x0 pos of the tile (in the image).
- * @param   p_tile_y0       pointer to a value that will hold the y0 pos of the tile (in the image).
- * @param   p_tile_x1       pointer to a value that will hold the x1 pos of the tile (in the image).
- * @param   p_tile_y1       pointer to a value that will hold the y1 pos of the tile (in the image).
- * @param   p_nb_comps      pointer to a value that will hold the number of components in the tile.
- * @param   p_should_go_on  pointer to a boolean that will hold the fact that the decoding should go on. In case the
- *                          codestream is over at the time of the call, the value will be set to false. The user should then stop
- *                          the decoding.
- * @param   p_stream        the stream to decode.
- * @return  true            if the tile header could be decoded. In case the decoding should end, the returned value is still true.
- *                          returning false may be the result of a shortage of memory or an internal error.
- */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_read_tile_header(opj_codec_t *p_codec,
-        opj_stream_t * p_stream,
-        OPJ_UINT32 * p_tile_index,
-        OPJ_UINT32 * p_data_size,
-        OPJ_INT32 * p_tile_x0, OPJ_INT32 * p_tile_y0,
-        OPJ_INT32 * p_tile_x1, OPJ_INT32 * p_tile_y1,
-        OPJ_UINT32 * p_nb_comps,
-        OPJ_BOOL * p_should_go_on);
-
-/**
- * Reads a tile data. This function is compulsory and allows one to decode tile data. opj_read_tile_header should be called before.
- * The user may need to refer to the image got by opj_read_header to understand the size being taken by the tile.
- *
- * Note: opj_decode_tile_data() should not be used together with opj_set_decoded_components().
- *
- * @param   p_codec         the jpeg2000 codec.
- * @param   p_tile_index    the index of the tile being decoded, this should be the value set by opj_read_tile_header.
- * @param   p_data          pointer to a memory block that will hold the decoded data.
- * @param   p_data_size     size of p_data. p_data_size should be bigger or equal to the value set by opj_read_tile_header.
- * @param   p_stream        the stream to decode.
- *
- * @return  true            if the data could be decoded.
- */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_decode_tile_data(opj_codec_t *p_codec,
-        OPJ_UINT32 p_tile_index,
-        OPJ_BYTE * p_data,
-        OPJ_UINT32 p_data_size,
-        opj_stream_t *p_stream);
-
-/* COMPRESSION FUNCTIONS*/
-
-/**
- * Creates a J2K/JP2 compression structure
- * @param   format      Coder to select
- * @return              Returns a handle to a compressor if successful, returns NULL otherwise
- */
-OPJ_API opj_codec_t* OPJ_CALLCONV opj_create_compress(OPJ_CODEC_FORMAT format);
-
-/**
-Set encoding parameters to default values, that means :
-<ul>
-<li>Lossless
-<li>1 tile
-<li>Size of precinct : 2^15 x 2^15 (means 1 precinct)
-<li>Size of code-block : 64 x 64
-<li>Number of resolutions: 6
-<li>No SOP marker in the codestream
-<li>No EPH marker in the codestream
-<li>No sub-sampling in x or y direction
-<li>No mode switch activated
-<li>Progression order: LRCP
-<li>No index file
-<li>No ROI upshifted
-<li>No offset of the origin of the image
-<li>No offset of the origin of the tiles
-<li>Reversible DWT 5-3
-</ul>
-@param parameters Compression parameters
-*/
-OPJ_API void OPJ_CALLCONV opj_set_default_encoder_parameters(
-    opj_cparameters_t *parameters);
-
-/**
- * Setup the encoder parameters using the current image and using user parameters.
- * @param p_codec       Compressor handle
- * @param parameters    Compression parameters
- * @param image         Input filled image
- */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_setup_encoder(opj_codec_t *p_codec,
-        opj_cparameters_t *parameters,
-        opj_image_t *image);
-
-/**
- * Start to compress the current image.
- * @param p_codec       Compressor handle
- * @param p_image       Input filled image
- * @param p_stream      Input stgream
- */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_start_compress(opj_codec_t *p_codec,
-        opj_image_t * p_image,
-        opj_stream_t *p_stream);
-
-/**
- * End to compress the current image.
- * @param p_codec       Compressor handle
- * @param p_stream      Input stgream
- */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_end_compress(opj_codec_t *p_codec,
-        opj_stream_t *p_stream);
-
-/**
- * Encode an image into a JPEG-2000 codestream
- * @param p_codec       compressor handle
- * @param p_stream      Output buffer stream
- *
- * @return              Returns true if successful, returns false otherwise
- */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_encode(opj_codec_t *p_codec,
-        opj_stream_t *p_stream);
-/*
-==========================================================
-   codec output functions definitions
-==========================================================
-*/
-/* EXPERIMENTAL FUNCTIONS FOR NOW, USED ONLY IN J2K_DUMP*/
-
-/**
-Destroy Codestream information after compression or decompression
-@param cstr_info Codestream information structure
-*/
-OPJ_API void OPJ_CALLCONV opj_destroy_cstr_info(opj_codestream_info_v2_t
-        **cstr_info);
-
-
-/**
- * Dump the codec information into the output stream
- *
- * @param   p_codec         the jpeg2000 codec.
- * @param   info_flag       type of information dump.
- * @param   output_stream   output stream where dump the information gotten from the codec.
- *
- */
-OPJ_API void OPJ_CALLCONV opj_dump_codec(opj_codec_t *p_codec,
-        OPJ_INT32 info_flag,
-        FILE* output_stream);
-
-/**
- * Get the codestream information from the codec
- *
- * @param   p_codec         the jpeg2000 codec.
- *
- * @return                  a pointer to a codestream information structure.
- *
- */
-OPJ_API opj_codestream_info_v2_t* OPJ_CALLCONV opj_get_cstr_info(
-    opj_codec_t *p_codec);
-
-/**
- * Get the codestream index from the codec
- *
- * @param   p_codec         the jpeg2000 codec.
- *
- * @return                  a pointer to a codestream index structure.
- *
- */
-OPJ_API opj_codestream_index_t * OPJ_CALLCONV opj_get_cstr_index(
-    opj_codec_t *p_codec);
-
-OPJ_API void OPJ_CALLCONV opj_destroy_cstr_index(opj_codestream_index_t
-        **p_cstr_index);
-
-
-/**
- * Get the JP2 file information from the codec FIXME
- *
- * @param   p_codec         the jpeg2000 codec.
- *
- * @return                  a pointer to a JP2 metadata structure.
- *
- */
-OPJ_API opj_jp2_metadata_t* OPJ_CALLCONV opj_get_jp2_metadata(
-    opj_codec_t *p_codec);
-
-/**
- * Get the JP2 file index from the codec FIXME
- *
- * @param   p_codec         the jpeg2000 codec.
- *
- * @return                  a pointer to a JP2 index structure.
- *
- */
-OPJ_API opj_jp2_index_t* OPJ_CALLCONV opj_get_jp2_index(opj_codec_t *p_codec);
-
-
-/*
-==========================================================
-   MCT functions
-==========================================================
-*/
-
-/**
- * Sets the MCT matrix to use.
- *
- * @param   parameters      the parameters to change.
- * @param   pEncodingMatrix the encoding matrix.
- * @param   p_dc_shift      the dc shift coefficients to use.
- * @param   pNbComp         the number of components of the image.
- *
- * @return  true if the parameters could be set.
- */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_set_MCT(opj_cparameters_t *parameters,
-        OPJ_FLOAT32 * pEncodingMatrix,
-        OPJ_INT32 * p_dc_shift,
-        OPJ_UINT32 pNbComp);
-
-/*
-==========================================================
-   Thread functions
-==========================================================
-*/
-
-/** Returns if the library is built with thread support.
- * OPJ_TRUE if mutex, condition, thread, thread pool are available.
- */
-OPJ_API OPJ_BOOL OPJ_CALLCONV opj_has_thread_support(void);
-
-/** Return the number of virtual CPUs */
-OPJ_API int OPJ_CALLCONV opj_get_num_cpus(void);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* OPENJPEG_H */
diff --git a/third_party/libopenjpeg20/opj_codec.h b/third_party/libopenjpeg20/opj_codec.h
deleted file mode 100644
index b962b12..0000000
--- a/third_party/libopenjpeg20/opj_codec.h
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-#ifndef OPJ_CODEC_H
-#define OPJ_CODEC_H
-/**
-@file opj_codec.h
-*/
-
-
-/**
- * Main codec handler used for compression or decompression.
- */
-typedef struct opj_codec_private {
-    /** FIXME DOC */
-    union {
-        /**
-         * Decompression handler.
-         */
-        struct opj_decompression {
-            /** Main header reading function handler */
-            OPJ_BOOL(*opj_read_header)(struct opj_stream_private * cio,
-                                       void * p_codec,
-                                       opj_image_t **p_image,
-                                       struct opj_event_mgr * p_manager);
-
-            /** Decoding function */
-            OPJ_BOOL(*opj_decode)(void * p_codec,
-                                  struct opj_stream_private * p_cio,
-                                  opj_image_t * p_image,
-                                  struct opj_event_mgr * p_manager);
-
-            /** FIXME DOC */
-            OPJ_BOOL(*opj_read_tile_header)(void * p_codec,
-                                            OPJ_UINT32 * p_tile_index,
-                                            OPJ_UINT32 * p_data_size,
-                                            OPJ_INT32 * p_tile_x0,
-                                            OPJ_INT32 * p_tile_y0,
-                                            OPJ_INT32 * p_tile_x1,
-                                            OPJ_INT32 * p_tile_y1,
-                                            OPJ_UINT32 * p_nb_comps,
-                                            OPJ_BOOL * p_should_go_on,
-                                            struct opj_stream_private * p_cio,
-                                            struct opj_event_mgr * p_manager);
-
-            /** FIXME DOC */
-            OPJ_BOOL(*opj_decode_tile_data)(void * p_codec,
-                                            OPJ_UINT32 p_tile_index,
-                                            OPJ_BYTE * p_data,
-                                            OPJ_UINT32 p_data_size,
-                                            struct opj_stream_private * p_cio,
-                                            struct opj_event_mgr * p_manager);
-
-            /** Reading function used after codestream if necessary */
-            OPJ_BOOL(* opj_end_decompress)(void *p_codec,
-                                           struct opj_stream_private * cio,
-                                           struct opj_event_mgr * p_manager);
-
-            /** Codec destroy function handler */
-            void (*opj_destroy)(void * p_codec);
-
-            /** Setup decoder function handler */
-            void (*opj_setup_decoder)(void * p_codec, opj_dparameters_t * p_param);
-
-            /** Set decode area function handler */
-            OPJ_BOOL(*opj_set_decode_area)(void * p_codec,
-                                           opj_image_t * p_image,
-                                           OPJ_INT32 p_start_x,
-                                           OPJ_INT32 p_end_x,
-                                           OPJ_INT32 p_start_y,
-                                           OPJ_INT32 p_end_y,
-                                           struct opj_event_mgr * p_manager);
-
-            /** Get tile function */
-            OPJ_BOOL(*opj_get_decoded_tile)(void *p_codec,
-                                            opj_stream_private_t * p_cio,
-                                            opj_image_t *p_image,
-                                            struct opj_event_mgr * p_manager,
-                                            OPJ_UINT32 tile_index);
-
-            /** Set the decoded resolution factor */
-            OPJ_BOOL(*opj_set_decoded_resolution_factor)(void * p_codec,
-                    OPJ_UINT32 res_factor,
-                    opj_event_mgr_t * p_manager);
-
-            /** Set the decoded components */
-            OPJ_BOOL(*opj_set_decoded_components)(void * p_codec,
-                                                  OPJ_UINT32 num_comps,
-                                                  const OPJ_UINT32* comps_indices,
-                                                  opj_event_mgr_t * p_manager);
-        } m_decompression;
-
-        /**
-         * Compression handler. FIXME DOC
-         */
-        struct opj_compression {
-            OPJ_BOOL(* opj_start_compress)(void *p_codec,
-                                           struct opj_stream_private * cio,
-                                           struct opj_image * p_image,
-                                           struct opj_event_mgr * p_manager);
-
-            OPJ_BOOL(* opj_encode)(void * p_codec,
-                                   struct opj_stream_private *p_cio,
-                                   struct opj_event_mgr * p_manager);
-
-            OPJ_BOOL(* opj_write_tile)(void * p_codec,
-                                       OPJ_UINT32 p_tile_index,
-                                       OPJ_BYTE * p_data,
-                                       OPJ_UINT32 p_data_size,
-                                       struct opj_stream_private * p_cio,
-                                       struct opj_event_mgr * p_manager);
-
-            OPJ_BOOL(* opj_end_compress)(void * p_codec,
-                                         struct opj_stream_private * p_cio,
-                                         struct opj_event_mgr * p_manager);
-
-            void (* opj_destroy)(void * p_codec);
-
-            OPJ_BOOL(* opj_setup_encoder)(void * p_codec,
-                                          opj_cparameters_t * p_param,
-                                          struct opj_image * p_image,
-                                          struct opj_event_mgr * p_manager);
-        } m_compression;
-    } m_codec_data;
-    /** FIXME DOC*/
-    void * m_codec;
-    /** Event handler */
-    opj_event_mgr_t m_event_mgr;
-    /** Flag to indicate if the codec is used to decode or encode*/
-    OPJ_BOOL is_decompressor;
-    void (*opj_dump_codec)(void * p_codec, OPJ_INT32 info_flag,
-                           FILE* output_stream);
-    opj_codestream_info_v2_t* (*opj_get_codec_info)(void* p_codec);
-    opj_codestream_index_t* (*opj_get_codec_index)(void* p_codec);
-
-    /** Set number of threads */
-    OPJ_BOOL(*opj_set_threads)(void * p_codec, OPJ_UINT32 num_threads);
-}
-opj_codec_private_t;
-
-
-#endif /* OPJ_CODEC_H */
-
diff --git a/third_party/libopenjpeg20/opj_common.h b/third_party/libopenjpeg20/opj_common.h
deleted file mode 100644
index a051339..0000000
--- a/third_party/libopenjpeg20/opj_common.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-#ifndef OPJ_COMMMON_H
-#define OPJ_COMMMON_H
-
-/*
- ==========================================================
-   Common constants shared among several modules
- ==========================================================
-*/
-#define OPJ_COMMON_CBLK_DATA_EXTRA        2    /**< Margin for a fake FFFF marker */
-
-#endif /* OPJ_COMMMON_H */
diff --git a/third_party/libopenjpeg20/opj_config.h b/third_party/libopenjpeg20/opj_config.h
deleted file mode 100644
index fda1f64..0000000
--- a/third_party/libopenjpeg20/opj_config.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-/* create opj_config.h for CMake */
-#define OPJ_HAVE_STDINT_H 		1
-
-/*--------------------------------------------------------------------------*/
-/* OpenJPEG Versioning                                                      */
-
-/* Version number. */
-#define OPJ_VERSION_MAJOR 2
-#define OPJ_VERSION_MINOR 3
-#define OPJ_VERSION_BUILD 1
diff --git a/third_party/libopenjpeg20/opj_config_private.h b/third_party/libopenjpeg20/opj_config_private.h
deleted file mode 100644
index b6986f9..0000000
--- a/third_party/libopenjpeg20/opj_config_private.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-/* create opj_config_private.h for CMake */
-#define OPJ_HAVE_INTTYPES_H 	1
-
-#define OPJ_PACKAGE_VERSION "2.3.1"
-
-/* Not used by openjp2*/
-/*#define HAVE_MEMORY_H 1*/
-/*#define HAVE_STDLIB_H 1*/
-/*#define HAVE_STRINGS_H 1*/
-/*#define HAVE_STRING_H 1*/
-/*#define HAVE_SYS_STAT_H 1*/
-/*#define HAVE_SYS_TYPES_H 1 */
-/*#define HAVE_UNISTD_H 1*/
-
-/* #undef _LARGEFILE_SOURCE */
-/* #undef _LARGE_FILES */
-/* #undef _FILE_OFFSET_BITS */
-#define OPJ_HAVE_FSEEKO ON
-
-/* Byte order.  */
-/* All compilers that support Mac OS X define either __BIG_ENDIAN__ or
-__LITTLE_ENDIAN__ to match the endianness of the architecture being
-compiled for. This is not necessarily the same as the architecture of the
-machine doing the building. In order to support Universal Binaries on
-Mac OS X, we prefer those defines to decide the endianness.
-On other platforms we use the result of the TRY_RUN. */
-#if !defined(__APPLE__)
-/* #undef OPJ_BIG_ENDIAN */
-#elif defined(__BIG_ENDIAN__)
-# define OPJ_BIG_ENDIAN
-#endif
diff --git a/third_party/libopenjpeg20/opj_includes.h b/third_party/libopenjpeg20/opj_includes.h
deleted file mode 100644
index 0a8628c..0000000
--- a/third_party/libopenjpeg20/opj_includes.h
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
- * Copyright (c) 2012, CS Systemes d'Information, France
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-#ifndef OPJ_INCLUDES_H
-#define OPJ_INCLUDES_H
-
-/*
- * This must be included before any system headers,
- * since they can react to macro defined there
- */
-#include "opj_config_private.h"
-
-/*
- ==========================================================
-   Standard includes used by the library
- ==========================================================
-*/
-#include <memory.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <float.h>
-#include <time.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <ctype.h>
-#include <assert.h>
-#include <limits.h>
-
-/*
-  Use fseeko() and ftello() if they are available since they use
-  'off_t' rather than 'long'.  It is wrong to use fseeko() and
-  ftello() only on systems with special LFS support since some systems
-  (e.g. FreeBSD) support a 64-bit off_t by default.
-*/
-#if defined(OPJ_HAVE_FSEEKO) && !defined(fseek)
-#  define fseek  fseeko
-#  define ftell  ftello
-#endif
-
-
-#if defined(WIN32) && !defined(Windows95) && !defined(__BORLANDC__) && \
-  !(defined(_MSC_VER) && _MSC_VER < 1400) && \
-  !(defined(__MINGW32__) && __MSVCRT_VERSION__ < 0x800)
-/*
-  Windows '95 and Borland C do not support _lseeki64
-  Visual Studio does not support _fseeki64 and _ftelli64 until the 2005 release.
-  Without these interfaces, files over 2GB in size are not supported for Windows.
-*/
-#  define OPJ_FSEEK(stream,offset,whence) _fseeki64(stream,/* __int64 */ offset,whence)
-#  define OPJ_FSTAT(fildes,stat_buff) _fstati64(fildes,/* struct _stati64 */ stat_buff)
-#  define OPJ_FTELL(stream) /* __int64 */ _ftelli64(stream)
-#  define OPJ_STAT_STRUCT_T struct _stati64
-#  define OPJ_STAT(path,stat_buff) _stati64(path,/* struct _stati64 */ stat_buff)
-#else
-#  define OPJ_FSEEK(stream,offset,whence) fseek(stream,offset,whence)
-#  define OPJ_FSTAT(fildes,stat_buff) fstat(fildes,stat_buff)
-#  define OPJ_FTELL(stream) ftell(stream)
-#  define OPJ_STAT_STRUCT_T struct stat
-#  define OPJ_STAT(path,stat_buff) stat(path,stat_buff)
-#endif
-
-
-/*
- ==========================================================
-   OpenJPEG interface
- ==========================================================
- */
-#include "openjpeg.h"
-
-/*
- ==========================================================
-   OpenJPEG modules
- ==========================================================
-*/
-
-/* Are restricted pointers available? (C99) */
-#if (__STDC_VERSION__ >= 199901L)
-#define OPJ_RESTRICT restrict
-#else
-/* Not a C99 compiler */
-#if defined(__GNUC__)
-#define OPJ_RESTRICT __restrict__
-
-/*
-  vc14 (2015) outputs wrong results.
-  Need to check OPJ_RESTRICT usage (or a bug in vc14)
-    #elif defined(_MSC_VER) && (_MSC_VER >= 1400)
-        #define OPJ_RESTRICT __restrict
-*/
-#else
-#define OPJ_RESTRICT /* restrict */
-#endif
-#endif
-
-#ifdef __has_attribute
-#if __has_attribute(no_sanitize)
-#define OPJ_NOSANITIZE(kind) __attribute__((no_sanitize(kind)))
-#endif
-#endif
-#ifndef OPJ_NOSANITIZE
-#define OPJ_NOSANITIZE(kind)
-#endif
-
-
-/* MSVC before 2013 and Borland C do not have lrintf */
-#if defined(_MSC_VER)
-#include <intrin.h>
-static INLINE long opj_lrintf(float f)
-{
-#ifdef _M_X64
-    return _mm_cvt_ss2si(_mm_load_ss(&f));
-
-    /* commented out line breaks many tests */
-    /* return (long)((f>0.0f) ? (f + 0.5f):(f -0.5f)); */
-#elif defined(_M_IX86)
-    int i;
-    _asm{
-        fld f
-        fistp i
-    };
-
-    return i;
-#else
-    return (long)((f>0.0f) ? (f + 0.5f) : (f - 0.5f));
-#endif
-}
-#elif defined(__BORLANDC__)
-static INLINE long opj_lrintf(float f)
-{
-#ifdef _M_X64
-    return (long)((f > 0.0f) ? (f + 0.5f) : (f - 0.5f));
-#else
-    int i;
-
-    _asm {
-        fld f
-        fistp i
-    };
-
-    return i;
-#endif
-}
-#else
-static INLINE long opj_lrintf(float f)
-{
-    return lrintf(f);
-}
-#endif
-
-#if defined(_MSC_VER) && (_MSC_VER < 1400)
-#define vsnprintf _vsnprintf
-#endif
-
-/* MSVC x86 is really bad at doing int64 = int32 * int32 on its own. Use intrinsic. */
-#if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(__INTEL_COMPILER) && defined(_M_IX86)
-#   include <intrin.h>
-#   pragma intrinsic(__emul)
-#endif
-
-/* Apparently Visual Studio doesn't define __SSE__ / __SSE2__ macros */
-#if defined(_M_X64)
-/* Intel 64bit support SSE and SSE2 */
-#   ifndef __SSE__
-#       define __SSE__ 1
-#   endif
-#   ifndef __SSE2__
-#       define __SSE2__ 1
-#   endif
-#endif
-
-/* For x86, test the value of the _M_IX86_FP macro. */
-/* See https://msdn.microsoft.com/en-us/library/b0084kay.aspx */
-#if defined(_M_IX86_FP)
-#   if _M_IX86_FP >= 1
-#       ifndef __SSE__
-#           define __SSE__ 1
-#       endif
-#   endif
-#   if _M_IX86_FP >= 2
-#       ifndef __SSE2__
-#           define __SSE2__ 1
-#       endif
-#   endif
-#endif
-
-/* Type to use for bit-fields in internal headers */
-typedef unsigned int OPJ_BITFIELD;
-
-#define OPJ_UNUSED(x) (void)x
-
-#include "opj_inttypes.h"
-#include "opj_clock.h"
-#include "opj_malloc.h"
-#include "event.h"
-#include "function_list.h"
-#include "bio.h"
-#include "cio.h"
-
-#include "thread.h"
-#include "tls_keys.h"
-
-#include "image.h"
-#include "invert.h"
-#include "j2k.h"
-#include "jp2.h"
-
-#include "mqc.h"
-#include "bio.h"
-
-#include "pi.h"
-#include "tgt.h"
-#include "tcd.h"
-#include "t1.h"
-#include "dwt.h"
-#include "t2.h"
-#include "mct.h"
-#include "opj_intmath.h"
-#include "sparse_array.h"
-
-#ifdef USE_JPIP
-#include "cidx_manager.h"
-#include "indexbox_manager.h"
-#endif
-
-/* JPWL>> */
-#ifdef USE_JPWL
-#include "openjpwl/jpwl.h"
-#endif /* USE_JPWL */
-/* <<JPWL */
-
-/* V2 */
-#include "opj_codec.h"
-
-
-#endif /* OPJ_INCLUDES_H */
diff --git a/third_party/libopenjpeg20/opj_intmath.h b/third_party/libopenjpeg20/opj_intmath.h
deleted file mode 100644
index 754b551..0000000
--- a/third_party/libopenjpeg20/opj_intmath.h
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-#ifndef OPJ_INTMATH_H
-#define OPJ_INTMATH_H
-/**
-@file opj_intmath.h
-@brief Implementation of operations on integers (INT)
-
-The functions in OPJ_INTMATH.H have for goal to realize operations on integers.
-*/
-
-/** @defgroup OPJ_INTMATH OPJ_INTMATH - Implementation of operations on integers */
-/*@{*/
-
-/** @name Exported functions (see also openjpeg.h) */
-/*@{*/
-/* ----------------------------------------------------------------------- */
-/**
-Get the minimum of two integers
-@return Returns a if a < b else b
-*/
-static INLINE OPJ_INT32 opj_int_min(OPJ_INT32 a, OPJ_INT32 b)
-{
-    return a < b ? a : b;
-}
-
-/**
-Get the minimum of two integers
-@return Returns a if a < b else b
-*/
-static INLINE OPJ_UINT32 opj_uint_min(OPJ_UINT32 a, OPJ_UINT32 b)
-{
-    return a < b ? a : b;
-}
-
-/**
-Get the maximum of two integers
-@return Returns a if a > b else b
-*/
-static INLINE OPJ_INT32 opj_int_max(OPJ_INT32 a, OPJ_INT32 b)
-{
-    return (a > b) ? a : b;
-}
-
-/**
-Get the maximum of two integers
-@return Returns a if a > b else b
-*/
-static INLINE OPJ_UINT32 opj_uint_max(OPJ_UINT32  a, OPJ_UINT32  b)
-{
-    return (a > b) ? a : b;
-}
-
-/**
- Get the saturated sum of two unsigned integers
- @return Returns saturated sum of a+b
- */
-static INLINE OPJ_UINT32 opj_uint_adds(OPJ_UINT32 a, OPJ_UINT32 b)
-{
-    OPJ_UINT64 sum = (OPJ_UINT64)a + (OPJ_UINT64)b;
-    return (OPJ_UINT32)(-(OPJ_INT32)(sum >> 32)) | (OPJ_UINT32)sum;
-}
-
-/**
- Get the saturated difference of two unsigned integers
- @return Returns saturated sum of a-b
- */
-static INLINE OPJ_UINT32 opj_uint_subs(OPJ_UINT32 a, OPJ_UINT32 b)
-{
-    return (a >= b) ? a - b : 0;
-}
-
-/**
-Clamp an integer inside an interval
-@return
-<ul>
-<li>Returns a if (min < a < max)
-<li>Returns max if (a > max)
-<li>Returns min if (a < min)
-</ul>
-*/
-static INLINE OPJ_INT32 opj_int_clamp(OPJ_INT32 a, OPJ_INT32 min,
-                                      OPJ_INT32 max)
-{
-    if (a < min) {
-        return min;
-    }
-    if (a > max) {
-        return max;
-    }
-    return a;
-}
-
-/**
-Clamp an integer inside an interval
-@return
-<ul>
-<li>Returns a if (min < a < max)
-<li>Returns max if (a > max)
-<li>Returns min if (a < min)
-</ul>
-*/
-static INLINE OPJ_INT64 opj_int64_clamp(OPJ_INT64 a, OPJ_INT64 min,
-                                        OPJ_INT64 max)
-{
-    if (a < min) {
-        return min;
-    }
-    if (a > max) {
-        return max;
-    }
-    return a;
-}
-
-/**
-@return Get absolute value of integer
-*/
-static INLINE OPJ_INT32 opj_int_abs(OPJ_INT32 a)
-{
-    return a < 0 ? -a : a;
-}
-/**
-Divide an integer and round upwards
-@return Returns a divided by b
-*/
-static INLINE OPJ_INT32 opj_int_ceildiv(OPJ_INT32 a, OPJ_INT32 b)
-{
-    assert(b);
-    return (OPJ_INT32)(((OPJ_INT64)a + b - 1) / b);
-}
-
-/**
-Divide an integer and round upwards
-@return Returns a divided by b
-*/
-static INLINE OPJ_UINT32  opj_uint_ceildiv(OPJ_UINT32  a, OPJ_UINT32  b)
-{
-    assert(b);
-    return (OPJ_UINT32)(((OPJ_UINT64)a + b - 1) / b);
-}
-
-/**
-Divide an integer by a power of 2 and round upwards
-@return Returns a divided by 2^b
-*/
-static INLINE OPJ_INT32 opj_int_ceildivpow2(OPJ_INT32 a, OPJ_INT32 b)
-{
-    return (OPJ_INT32)((a + ((OPJ_INT64)1 << b) - 1) >> b);
-}
-
-/**
- Divide a 64bits integer by a power of 2 and round upwards
- @return Returns a divided by 2^b
- */
-static INLINE OPJ_INT32 opj_int64_ceildivpow2(OPJ_INT64 a, OPJ_INT32 b)
-{
-    return (OPJ_INT32)((a + ((OPJ_INT64)1 << b) - 1) >> b);
-}
-
-/**
- Divide an integer by a power of 2 and round upwards
- @return Returns a divided by 2^b
- */
-static INLINE OPJ_UINT32 opj_uint_ceildivpow2(OPJ_UINT32 a, OPJ_UINT32 b)
-{
-    return (OPJ_UINT32)((a + ((OPJ_UINT64)1U << b) - 1U) >> b);
-}
-
-/**
-Divide an integer by a power of 2 and round downwards
-@return Returns a divided by 2^b
-*/
-static INLINE OPJ_INT32 opj_int_floordivpow2(OPJ_INT32 a, OPJ_INT32 b)
-{
-    return a >> b;
-}
-/**
-Get logarithm of an integer and round downwards
-@return Returns log2(a)
-*/
-static INLINE OPJ_INT32 opj_int_floorlog2(OPJ_INT32 a)
-{
-    OPJ_INT32 l;
-    for (l = 0; a > 1; l++) {
-        a >>= 1;
-    }
-    return l;
-}
-/**
-Get logarithm of an integer and round downwards
-@return Returns log2(a)
-*/
-static INLINE OPJ_UINT32  opj_uint_floorlog2(OPJ_UINT32  a)
-{
-    OPJ_UINT32  l;
-    for (l = 0; a > 1; ++l) {
-        a >>= 1;
-    }
-    return l;
-}
-
-/**
-Multiply two fixed-precision rational numbers.
-@param a
-@param b
-@return Returns a * b
-*/
-static INLINE OPJ_INT32 opj_int_fix_mul(OPJ_INT32 a, OPJ_INT32 b)
-{
-#if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(__INTEL_COMPILER) && defined(_M_IX86)
-    OPJ_INT64 temp = __emul(a, b);
-#else
-    OPJ_INT64 temp = (OPJ_INT64) a * (OPJ_INT64) b ;
-#endif
-    temp += 4096;
-    assert((temp >> 13) <= (OPJ_INT64)0x7FFFFFFF);
-    assert((temp >> 13) >= (-(OPJ_INT64)0x7FFFFFFF - (OPJ_INT64)1));
-    return (OPJ_INT32)(temp >> 13);
-}
-
-static INLINE OPJ_INT32 opj_int_fix_mul_t1(OPJ_INT32 a, OPJ_INT32 b)
-{
-#if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(__INTEL_COMPILER) && defined(_M_IX86)
-    OPJ_INT64 temp = __emul(a, b);
-#else
-    OPJ_INT64 temp = (OPJ_INT64) a * (OPJ_INT64) b ;
-#endif
-    temp += 4096;
-    assert((temp >> (13 + 11 - T1_NMSEDEC_FRACBITS)) <= (OPJ_INT64)0x7FFFFFFF);
-    assert((temp >> (13 + 11 - T1_NMSEDEC_FRACBITS)) >= (-(OPJ_INT64)0x7FFFFFFF -
-            (OPJ_INT64)1));
-    return (OPJ_INT32)(temp >> (13 + 11 - T1_NMSEDEC_FRACBITS)) ;
-}
-
-/* ----------------------------------------------------------------------- */
-/*@}*/
-
-/*@}*/
-
-#endif /* OPJ_INTMATH_H */
diff --git a/third_party/libopenjpeg20/opj_malloc.h b/third_party/libopenjpeg20/opj_malloc.h
deleted file mode 100644
index 79b3a64..0000000
--- a/third_party/libopenjpeg20/opj_malloc.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2007, Callum Lerwick <seg@haxxed.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-#ifndef OPJ_MALLOC_H
-#define OPJ_MALLOC_H
-/**
-@file opj_malloc.h
-@brief Internal functions
-
-The functions in opj_malloc.h are internal utilities used for memory management.
-*/
-
-/** @defgroup MISC MISC - Miscellaneous internal functions */
-/*@{*/
-
-/** @name Exported functions */
-/*@{*/
-/* ----------------------------------------------------------------------- */
-
-/**
-Allocate an uninitialized memory block
-@param size Bytes to allocate
-@return Returns a void pointer to the allocated space, or NULL if there is insufficient memory available
-*/
-#ifdef ALLOC_PERF_OPT
-void * OPJ_CALLCONV opj_malloc(size_t size);
-#else
-/* prevent assertion on overflow for MSVC */
-#ifdef _MSC_VER
-#define opj_malloc(size) ((size_t)(size) >= (size_t)-0x100 ? NULL : malloc(size))
-#else
-#define opj_malloc(size) malloc(size)
-#endif
-#endif
-
-/**
-Allocate a memory block with elements initialized to 0
-@param numOfElements  Blocks to allocate
-@param sizeOfElements Bytes per block to allocate
-@return Returns a void pointer to the allocated space, or NULL if there is insufficient memory available
-*/
-#ifdef ALLOC_PERF_OPT
-void * OPJ_CALLCONV opj_calloc(size_t _NumOfElements, size_t _SizeOfElements);
-#else
-/* prevent assertion on overflow for MSVC */
-#ifdef _MSC_VER
-#define opj_calloc(num, size) ((size_t)(num) != 0 && (size_t)(num) >= (size_t)-0x100 / (size_t)(size) ? NULL : calloc(num, size))
-#else
-#define opj_calloc(num, size) calloc(num, size)
-#endif
-#endif
-
-/**
-Allocate memory aligned to a 16 byte boundary
-@param size Bytes to allocate
-@return Returns a void pointer to the allocated space, or NULL if there is insufficient memory available
-*/
-/* FIXME: These should be set with cmake tests, but we're currently not requiring use of cmake */
-#ifdef _WIN32
-  /* Someone should tell the mingw people that their malloc.h ought to provide _mm_malloc() */
-  #ifdef __GNUC__
-    #include <mm_malloc.h>
-    #define HAVE_MM_MALLOC
-  #else /* MSVC, Intel C++ */
-    #include <malloc.h>
-    #ifdef _mm_malloc
-      #define HAVE_MM_MALLOC
-    #endif
-  #endif
-#else /* Not _WIN32 */
-  #if defined(__sun)
-    #define HAVE_MEMALIGN
-  #elif defined(__FreeBSD__)
-    #define HAVE_POSIX_MEMALIGN
-  /* Linux x86_64 and OSX always align allocations to 16 bytes */
-  #elif !defined(__amd64__) && !defined(__APPLE__) && !defined(_AIX)
-    #define HAVE_MEMALIGN
-    #include <malloc.h>
-  #endif
-#endif
-
-#define opj_aligned_malloc(size) malloc(size)
-#define opj_aligned_32_malloc(size) malloc(size)
-#define opj_aligned_free(m) free(m)
-
-#ifdef HAVE_MM_MALLOC
-  #undef opj_aligned_malloc
-  #define opj_aligned_malloc(size) _mm_malloc((size), 16)
-  #undef opj_aligned_32_malloc
-  #define opj_aligned_32_malloc(size) _mm_malloc((size), 32)
-  #undef opj_aligned_free
-  #define opj_aligned_free(m) _mm_free(m)
-#endif
-
-#ifdef HAVE_MEMALIGN
-  extern void* memalign(size_t, size_t);
-  #undef opj_aligned_malloc
-  #define opj_aligned_malloc(size) memalign(16, (size))
-  #undef opj_aligned_32_malloc
-  #define opj_aligned_32_malloc(size) memalign(32, (size))
-  #undef opj_aligned_free
-  #define opj_aligned_free(m) free(m)
-#endif
-
-#ifdef HAVE_POSIX_MEMALIGN
-  #undef opj_aligned_malloc
-  extern int posix_memalign(void**, size_t, size_t);
-
-  static INLINE void* __attribute__ ((malloc)) opj_aligned_malloc(size_t size){
-    void* mem = NULL;
-    posix_memalign(&mem, 16, size);
-    return mem;
-  }
-
-  #undef opj_aligned_32_malloc
-  static INLINE void* __attribute__ ((malloc)) opj_aligned_32_malloc(size_t size){
-    void* mem = NULL;
-    posix_memalign(&mem, 32, size);
-    return mem;
-  }
-
-  #undef opj_aligned_free
-  #define opj_aligned_free(m) free(m)
-#endif
-
-#ifdef ALLOC_PERF_OPT
-  #undef opj_aligned_malloc
-  #define opj_aligned_malloc(size) opj_malloc(size)
-  #undef opj_aligned_32_malloc
-  #define opj_aligned_32_malloc(size) opj_malloc(size)
-  #undef opj_aligned_free
-  #define opj_aligned_free(m) opj_free(m)
-#endif
-
-/**
-Reallocate memory blocks.
-@param m Pointer to previously allocated memory block
-@param s New size in bytes
-@return Returns a void pointer to the reallocated (and possibly moved) memory block
-*/
-#ifdef ALLOC_PERF_OPT
-void * OPJ_CALLCONV opj_realloc(void * m, size_t s);
-#else
-/* prevent assertion on overflow for MSVC */
-#ifdef _MSC_VER
-#define opj_realloc(m, s) ((size_t)(s) >= (size_t)-0x100 ? NULL : realloc(m, s))
-#else
-#define opj_realloc(m, s) realloc(m, s)
-#endif
-#endif
-
-/**
-Deallocates or frees a memory block.
-@param m Previously allocated memory block to be freed
-*/
-#ifdef ALLOC_PERF_OPT
-void OPJ_CALLCONV opj_free(void * m);
-#else
-#define opj_free(m) free(m)
-#endif
-
-#if defined(__GNUC__) && !defined(OPJ_SKIP_POISON)
-#pragma GCC poison malloc calloc realloc free
-#endif
-
-/* ----------------------------------------------------------------------- */
-/*@}*/
-
-/*@}*/
-
-#endif /* OPJ_MALLOC_H */
-
diff --git a/third_party/libopenjpeg20/pi.c b/third_party/libopenjpeg20/pi.c
deleted file mode 100644
index 5f3d9ec..0000000
--- a/third_party/libopenjpeg20/pi.c
+++ /dev/null
@@ -1,2098 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2006-2007, Parvatha Elangovan
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "opj_includes.h"
-
-/** @defgroup PI PI - Implementation of a packet iterator */
-/*@{*/
-
-/** @name Local static functions */
-/*@{*/
-
-/**
-Get next packet in layer-resolution-component-precinct order.
-@param pi packet iterator to modify
-@return returns false if pi pointed to the last packet or else returns true
-*/
-static OPJ_BOOL opj_pi_next_lrcp(opj_pi_iterator_t * pi);
-/**
-Get next packet in resolution-layer-component-precinct order.
-@param pi packet iterator to modify
-@return returns false if pi pointed to the last packet or else returns true
-*/
-static OPJ_BOOL opj_pi_next_rlcp(opj_pi_iterator_t * pi);
-/**
-Get next packet in resolution-precinct-component-layer order.
-@param pi packet iterator to modify
-@return returns false if pi pointed to the last packet or else returns true
-*/
-static OPJ_BOOL opj_pi_next_rpcl(opj_pi_iterator_t * pi);
-/**
-Get next packet in precinct-component-resolution-layer order.
-@param pi packet iterator to modify
-@return returns false if pi pointed to the last packet or else returns true
-*/
-static OPJ_BOOL opj_pi_next_pcrl(opj_pi_iterator_t * pi);
-/**
-Get next packet in component-precinct-resolution-layer order.
-@param pi packet iterator to modify
-@return returns false if pi pointed to the last packet or else returns true
-*/
-static OPJ_BOOL opj_pi_next_cprl(opj_pi_iterator_t * pi);
-
-/**
- * Updates the coding parameters if the encoding is used with Progression order changes and final (or cinema parameters are used).
- *
- * @param   p_cp        the coding parameters to modify
- * @param   p_tileno    the tile index being concerned.
- * @param   p_tx0       X0 parameter for the tile
- * @param   p_tx1       X1 parameter for the tile
- * @param   p_ty0       Y0 parameter for the tile
- * @param   p_ty1       Y1 parameter for the tile
- * @param   p_max_prec  the maximum precision for all the bands of the tile
- * @param   p_max_res   the maximum number of resolutions for all the poc inside the tile.
- * @param   p_dx_min        the minimum dx of all the components of all the resolutions for the tile.
- * @param   p_dy_min        the minimum dy of all the components of all the resolutions for the tile.
- */
-static void opj_pi_update_encode_poc_and_final(opj_cp_t *p_cp,
-        OPJ_UINT32 p_tileno,
-        OPJ_INT32 p_tx0,
-        OPJ_INT32 p_tx1,
-        OPJ_INT32 p_ty0,
-        OPJ_INT32 p_ty1,
-        OPJ_UINT32 p_max_prec,
-        OPJ_UINT32 p_max_res,
-        OPJ_UINT32 p_dx_min,
-        OPJ_UINT32 p_dy_min);
-
-/**
- * Updates the coding parameters if the encoding is not used with Progression order changes and final (and cinema parameters are used).
- *
- * @param   p_cp        the coding parameters to modify
- * @param   p_num_comps     the number of components
- * @param   p_tileno    the tile index being concerned.
- * @param   p_tx0       X0 parameter for the tile
- * @param   p_tx1       X1 parameter for the tile
- * @param   p_ty0       Y0 parameter for the tile
- * @param   p_ty1       Y1 parameter for the tile
- * @param   p_max_prec  the maximum precision for all the bands of the tile
- * @param   p_max_res   the maximum number of resolutions for all the poc inside the tile.
- * @param   p_dx_min        the minimum dx of all the components of all the resolutions for the tile.
- * @param   p_dy_min        the minimum dy of all the components of all the resolutions for the tile.
- */
-static void opj_pi_update_encode_not_poc(opj_cp_t *p_cp,
-        OPJ_UINT32 p_num_comps,
-        OPJ_UINT32 p_tileno,
-        OPJ_INT32 p_tx0,
-        OPJ_INT32 p_tx1,
-        OPJ_INT32 p_ty0,
-        OPJ_INT32 p_ty1,
-        OPJ_UINT32 p_max_prec,
-        OPJ_UINT32 p_max_res,
-        OPJ_UINT32 p_dx_min,
-        OPJ_UINT32 p_dy_min);
-/**
- * Gets the encoding parameters needed to update the coding parameters and all the pocs.
- *
- * @param   p_image         the image being encoded.
- * @param   p_cp            the coding parameters.
- * @param   tileno          the tile index of the tile being encoded.
- * @param   p_tx0           pointer that will hold the X0 parameter for the tile
- * @param   p_tx1           pointer that will hold the X1 parameter for the tile
- * @param   p_ty0           pointer that will hold the Y0 parameter for the tile
- * @param   p_ty1           pointer that will hold the Y1 parameter for the tile
- * @param   p_max_prec      pointer that will hold the maximum precision for all the bands of the tile
- * @param   p_max_res       pointer that will hold the maximum number of resolutions for all the poc inside the tile.
- * @param   p_dx_min            pointer that will hold the minimum dx of all the components of all the resolutions for the tile.
- * @param   p_dy_min            pointer that will hold the minimum dy of all the components of all the resolutions for the tile.
- */
-static void opj_get_encoding_parameters(const opj_image_t *p_image,
-                                        const opj_cp_t *p_cp,
-                                        OPJ_UINT32  tileno,
-                                        OPJ_INT32  * p_tx0,
-                                        OPJ_INT32 * p_tx1,
-                                        OPJ_INT32 * p_ty0,
-                                        OPJ_INT32 * p_ty1,
-                                        OPJ_UINT32 * p_dx_min,
-                                        OPJ_UINT32 * p_dy_min,
-                                        OPJ_UINT32 * p_max_prec,
-                                        OPJ_UINT32 * p_max_res);
-
-/**
- * Gets the encoding parameters needed to update the coding parameters and all the pocs.
- * The precinct widths, heights, dx and dy for each component at each resolution will be stored as well.
- * the last parameter of the function should be an array of pointers of size nb components, each pointer leading
- * to an area of size 4 * max_res. The data is stored inside this area with the following pattern :
- * dx_compi_res0 , dy_compi_res0 , w_compi_res0, h_compi_res0 , dx_compi_res1 , dy_compi_res1 , w_compi_res1, h_compi_res1 , ...
- *
- * @param   p_image         the image being encoded.
- * @param   p_cp            the coding parameters.
- * @param   tileno          the tile index of the tile being encoded.
- * @param   p_tx0           pointer that will hold the X0 parameter for the tile
- * @param   p_tx1           pointer that will hold the X1 parameter for the tile
- * @param   p_ty0           pointer that will hold the Y0 parameter for the tile
- * @param   p_ty1           pointer that will hold the Y1 parameter for the tile
- * @param   p_max_prec      pointer that will hold the maximum precision for all the bands of the tile
- * @param   p_max_res       pointer that will hold the maximum number of resolutions for all the poc inside the tile.
- * @param   p_dx_min        pointer that will hold the minimum dx of all the components of all the resolutions for the tile.
- * @param   p_dy_min        pointer that will hold the minimum dy of all the components of all the resolutions for the tile.
- * @param   p_resolutions   pointer to an area corresponding to the one described above.
- */
-static void opj_get_all_encoding_parameters(const opj_image_t *p_image,
-        const opj_cp_t *p_cp,
-        OPJ_UINT32 tileno,
-        OPJ_INT32 * p_tx0,
-        OPJ_INT32 * p_tx1,
-        OPJ_INT32 * p_ty0,
-        OPJ_INT32 * p_ty1,
-        OPJ_UINT32 * p_dx_min,
-        OPJ_UINT32 * p_dy_min,
-        OPJ_UINT32 * p_max_prec,
-        OPJ_UINT32 * p_max_res,
-        OPJ_UINT32 ** p_resolutions);
-/**
- * Allocates memory for a packet iterator. Data and data sizes are set by this operation.
- * No other data is set. The include section of the packet  iterator is not allocated.
- *
- * @param   p_image     the image used to initialize the packet iterator (in fact only the number of components is relevant.
- * @param   p_cp        the coding parameters.
- * @param   tileno  the index of the tile from which creating the packet iterator.
- */
-static opj_pi_iterator_t * opj_pi_create(const opj_image_t *p_image,
-        const opj_cp_t *p_cp,
-        OPJ_UINT32 tileno);
-/**
- * FIXME DOC
- */
-static void opj_pi_update_decode_not_poc(opj_pi_iterator_t * p_pi,
-        opj_tcp_t * p_tcp,
-        OPJ_UINT32 p_max_precision,
-        OPJ_UINT32 p_max_res);
-/**
- * FIXME DOC
- */
-static void opj_pi_update_decode_poc(opj_pi_iterator_t * p_pi,
-                                     opj_tcp_t * p_tcp,
-                                     OPJ_UINT32 p_max_precision,
-                                     OPJ_UINT32 p_max_res);
-
-/**
- * FIXME DOC
- */
-static OPJ_BOOL opj_pi_check_next_level(OPJ_INT32 pos,
-                                        opj_cp_t *cp,
-                                        OPJ_UINT32 tileno,
-                                        OPJ_UINT32 pino,
-                                        const OPJ_CHAR *prog);
-
-/*@}*/
-
-/*@}*/
-
-/*
-==========================================================
-   local functions
-==========================================================
-*/
-
-static void opj_pi_emit_error(opj_pi_iterator_t * pi, const char* msg)
-{
-    (void)pi;
-    (void)msg;
-}
-
-static OPJ_BOOL opj_pi_next_lrcp(opj_pi_iterator_t * pi)
-{
-    opj_pi_comp_t *comp = NULL;
-    opj_pi_resolution_t *res = NULL;
-    OPJ_UINT32 index = 0;
-
-    if (!pi->first) {
-        comp = &pi->comps[pi->compno];
-        res = &comp->resolutions[pi->resno];
-        goto LABEL_SKIP;
-    } else {
-        pi->first = 0;
-    }
-
-    for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) {
-        for (pi->resno = pi->poc.resno0; pi->resno < pi->poc.resno1;
-                pi->resno++) {
-            for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) {
-                comp = &pi->comps[pi->compno];
-                if (pi->resno >= comp->numresolutions) {
-                    continue;
-                }
-                res = &comp->resolutions[pi->resno];
-                if (!pi->tp_on) {
-                    pi->poc.precno1 = res->pw * res->ph;
-                }
-                for (pi->precno = pi->poc.precno0; pi->precno < pi->poc.precno1; pi->precno++) {
-                    index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno *
-                            pi->step_c + pi->precno * pi->step_p;
-                    /* Avoids index out of bounds access with */
-                    /* id_000098,sig_11,src_005411,op_havoc,rep_2 of */
-                    /* https://github.com/uclouvain/openjpeg/issues/938 */
-                    /* Not sure if this is the most clever fix. Perhaps */
-                    /* include should be resized when a POC arises, or */
-                    /* the POC should be rejected */
-                    if (index >= pi->include_size) {
-                        opj_pi_emit_error(pi, "Invalid access to pi->include");
-                        return OPJ_FALSE;
-                    }
-                    if (!pi->include[index]) {
-                        pi->include[index] = 1;
-                        return OPJ_TRUE;
-                    }
-LABEL_SKIP:
-                    ;
-                }
-            }
-        }
-    }
-
-    return OPJ_FALSE;
-}
-
-static OPJ_BOOL opj_pi_next_rlcp(opj_pi_iterator_t * pi)
-{
-    opj_pi_comp_t *comp = NULL;
-    opj_pi_resolution_t *res = NULL;
-    OPJ_UINT32 index = 0;
-
-    if (!pi->first) {
-        comp = &pi->comps[pi->compno];
-        res = &comp->resolutions[pi->resno];
-        goto LABEL_SKIP;
-    } else {
-        pi->first = 0;
-    }
-
-    for (pi->resno = pi->poc.resno0; pi->resno < pi->poc.resno1; pi->resno++) {
-        for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) {
-            for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) {
-                comp = &pi->comps[pi->compno];
-                if (pi->resno >= comp->numresolutions) {
-                    continue;
-                }
-                res = &comp->resolutions[pi->resno];
-                if (!pi->tp_on) {
-                    pi->poc.precno1 = res->pw * res->ph;
-                }
-                for (pi->precno = pi->poc.precno0; pi->precno < pi->poc.precno1; pi->precno++) {
-                    index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno *
-                            pi->step_c + pi->precno * pi->step_p;
-                    if (index >= pi->include_size) {
-                        opj_pi_emit_error(pi, "Invalid access to pi->include");
-                        return OPJ_FALSE;
-                    }
-                    if (!pi->include[index]) {
-                        pi->include[index] = 1;
-                        return OPJ_TRUE;
-                    }
-LABEL_SKIP:
-                    ;
-                }
-            }
-        }
-    }
-
-    return OPJ_FALSE;
-}
-
-static OPJ_BOOL opj_pi_next_rpcl(opj_pi_iterator_t * pi)
-{
-    opj_pi_comp_t *comp = NULL;
-    opj_pi_resolution_t *res = NULL;
-    OPJ_UINT32 index = 0;
-
-    if (!pi->first) {
-        goto LABEL_SKIP;
-    } else {
-        OPJ_UINT32 compno, resno;
-        pi->first = 0;
-        pi->dx = 0;
-        pi->dy = 0;
-        for (compno = 0; compno < pi->numcomps; compno++) {
-            comp = &pi->comps[compno];
-            for (resno = 0; resno < comp->numresolutions; resno++) {
-                OPJ_UINT32 dx, dy;
-                res = &comp->resolutions[resno];
-                if (res->pdx + comp->numresolutions - 1 - resno < 32 &&
-                        comp->dx <= UINT_MAX / (1u << (res->pdx + comp->numresolutions - 1 - resno))) {
-                    dx = comp->dx * (1u << (res->pdx + comp->numresolutions - 1 - resno));
-                    pi->dx = !pi->dx ? dx : opj_uint_min(pi->dx, dx);
-                }
-                if (res->pdy + comp->numresolutions - 1 - resno < 32 &&
-                        comp->dy <= UINT_MAX / (1u << (res->pdy + comp->numresolutions - 1 - resno))) {
-                    dy = comp->dy * (1u << (res->pdy + comp->numresolutions - 1 - resno));
-                    pi->dy = !pi->dy ? dy : opj_uint_min(pi->dy, dy);
-                }
-            }
-        }
-        if (pi->dx == 0 || pi->dy == 0) {
-            return OPJ_FALSE;
-        }
-    }
-    if (!pi->tp_on) {
-        pi->poc.ty0 = pi->ty0;
-        pi->poc.tx0 = pi->tx0;
-        pi->poc.ty1 = pi->ty1;
-        pi->poc.tx1 = pi->tx1;
-    }
-    for (pi->resno = pi->poc.resno0; pi->resno < pi->poc.resno1; pi->resno++) {
-        for (pi->y = pi->poc.ty0; pi->y < pi->poc.ty1;
-                pi->y += (OPJ_INT32)(pi->dy - (OPJ_UINT32)(pi->y % (OPJ_INT32)pi->dy))) {
-            for (pi->x = pi->poc.tx0; pi->x < pi->poc.tx1;
-                    pi->x += (OPJ_INT32)(pi->dx - (OPJ_UINT32)(pi->x % (OPJ_INT32)pi->dx))) {
-                for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) {
-                    OPJ_UINT32 levelno;
-                    OPJ_INT32 trx0, try0;
-                    OPJ_INT32  trx1, try1;
-                    OPJ_UINT32  rpx, rpy;
-                    OPJ_INT32  prci, prcj;
-                    comp = &pi->comps[pi->compno];
-                    if (pi->resno >= comp->numresolutions) {
-                        continue;
-                    }
-                    res = &comp->resolutions[pi->resno];
-                    levelno = comp->numresolutions - 1 - pi->resno;
-                    /* Avoids division by zero */
-                    /* Relates to id_000004,sig_06,src_000679,op_arith8,pos_49,val_-17 */
-                    /* of  https://github.com/uclouvain/openjpeg/issues/938 */
-                    if (levelno >= 32 ||
-                            ((comp->dx << levelno) >> levelno) != comp->dx ||
-                            ((comp->dy << levelno) >> levelno) != comp->dy) {
-                        continue;
-                    }
-                    if ((comp->dx << levelno) > INT_MAX ||
-                            (comp->dy << levelno) > INT_MAX) {
-                        continue;
-                    }
-                    trx0 = opj_int_ceildiv(pi->tx0, (OPJ_INT32)(comp->dx << levelno));
-                    try0 = opj_int_ceildiv(pi->ty0, (OPJ_INT32)(comp->dy << levelno));
-                    trx1 = opj_int_ceildiv(pi->tx1, (OPJ_INT32)(comp->dx << levelno));
-                    try1 = opj_int_ceildiv(pi->ty1, (OPJ_INT32)(comp->dy << levelno));
-                    rpx = res->pdx + levelno;
-                    rpy = res->pdy + levelno;
-
-                    /* To avoid divisions by zero / undefined behaviour on shift */
-                    /* in below tests */
-                    /* Fixes reading id:000026,sig:08,src:002419,op:int32,pos:60,val:+32 */
-                    /* of https://github.com/uclouvain/openjpeg/issues/938 */
-                    if (rpx >= 31 || ((comp->dx << rpx) >> rpx) != comp->dx ||
-                            rpy >= 31 || ((comp->dy << rpy) >> rpy) != comp->dy) {
-                        continue;
-                    }
-
-                    /* See ISO-15441. B.12.1.3 Resolution level-position-component-layer progression */
-                    if (!((pi->y % (OPJ_INT32)(comp->dy << rpy) == 0) || ((pi->y == pi->ty0) &&
-                            ((try0 << levelno) % (1 << rpy))))) {
-                        continue;
-                    }
-                    if (!((pi->x % (OPJ_INT32)(comp->dx << rpx) == 0) || ((pi->x == pi->tx0) &&
-                            ((trx0 << levelno) % (1 << rpx))))) {
-                        continue;
-                    }
-
-                    if ((res->pw == 0) || (res->ph == 0)) {
-                        continue;
-                    }
-
-                    if ((trx0 == trx1) || (try0 == try1)) {
-                        continue;
-                    }
-
-                    prci = opj_int_floordivpow2(opj_int_ceildiv(pi->x,
-                                                (OPJ_INT32)(comp->dx << levelno)), (OPJ_INT32)res->pdx)
-                           - opj_int_floordivpow2(trx0, (OPJ_INT32)res->pdx);
-                    prcj = opj_int_floordivpow2(opj_int_ceildiv(pi->y,
-                                                (OPJ_INT32)(comp->dy << levelno)), (OPJ_INT32)res->pdy)
-                           - opj_int_floordivpow2(try0, (OPJ_INT32)res->pdy);
-                    pi->precno = (OPJ_UINT32)(prci + prcj * (OPJ_INT32)res->pw);
-                    if (pi->precno >= res->pw * res->ph) {
-                      return OPJ_FALSE;
-                    }
-                    for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) {
-                        index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno *
-                                pi->step_c + pi->precno * pi->step_p;
-                        if (index >= pi->include_size) {
-                            opj_pi_emit_error(pi, "Invalid access to pi->include");
-                            return OPJ_FALSE;
-                        }
-                        if (!pi->include[index]) {
-                            pi->include[index] = 1;
-                            return OPJ_TRUE;
-                        }
-LABEL_SKIP:
-                        ;
-                    }
-                }
-            }
-        }
-    }
-
-    return OPJ_FALSE;
-}
-
-static OPJ_BOOL opj_pi_next_pcrl(opj_pi_iterator_t * pi)
-{
-    opj_pi_comp_t *comp = NULL;
-    opj_pi_resolution_t *res = NULL;
-    OPJ_UINT32 index = 0;
-
-    if (!pi->first) {
-        comp = &pi->comps[pi->compno];
-        goto LABEL_SKIP;
-    } else {
-        OPJ_UINT32 compno, resno;
-        pi->first = 0;
-        pi->dx = 0;
-        pi->dy = 0;
-        for (compno = 0; compno < pi->numcomps; compno++) {
-            comp = &pi->comps[compno];
-            for (resno = 0; resno < comp->numresolutions; resno++) {
-                OPJ_UINT32 dx, dy;
-                res = &comp->resolutions[resno];
-                if (res->pdx + comp->numresolutions - 1 - resno < 32 &&
-                        comp->dx <= UINT_MAX / (1u << (res->pdx + comp->numresolutions - 1 - resno))) {
-                    dx = comp->dx * (1u << (res->pdx + comp->numresolutions - 1 - resno));
-                    pi->dx = !pi->dx ? dx : opj_uint_min(pi->dx, dx);
-                }
-                if (res->pdy + comp->numresolutions - 1 - resno < 32 &&
-                        comp->dy <= UINT_MAX / (1u << (res->pdy + comp->numresolutions - 1 - resno))) {
-                    dy = comp->dy * (1u << (res->pdy + comp->numresolutions - 1 - resno));
-                    pi->dy = !pi->dy ? dy : opj_uint_min(pi->dy, dy);
-                }
-            }
-        }
-        if (pi->dx == 0 || pi->dy == 0) {
-            return OPJ_FALSE;
-        }
-    }
-    if (!pi->tp_on) {
-        pi->poc.ty0 = pi->ty0;
-        pi->poc.tx0 = pi->tx0;
-        pi->poc.ty1 = pi->ty1;
-        pi->poc.tx1 = pi->tx1;
-    }
-    for (pi->y = pi->poc.ty0; pi->y < pi->poc.ty1;
-            pi->y += (OPJ_INT32)(pi->dy - (OPJ_UINT32)(pi->y % (OPJ_INT32)pi->dy))) {
-        for (pi->x = pi->poc.tx0; pi->x < pi->poc.tx1;
-                pi->x += (OPJ_INT32)(pi->dx - (OPJ_UINT32)(pi->x % (OPJ_INT32)pi->dx))) {
-            for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) {
-                comp = &pi->comps[pi->compno];
-                for (pi->resno = pi->poc.resno0;
-                        pi->resno < opj_uint_min(pi->poc.resno1, comp->numresolutions); pi->resno++) {
-                    OPJ_UINT32 levelno;
-                    OPJ_INT32 trx0, try0;
-                    OPJ_INT32 trx1, try1;
-                    OPJ_UINT32 rpx, rpy;
-                    OPJ_INT32 prci, prcj;
-                    res = &comp->resolutions[pi->resno];
-                    levelno = comp->numresolutions - 1 - pi->resno;
-                    /* Avoids division by zero */
-                    /* Relates to id_000004,sig_06,src_000679,op_arith8,pos_49,val_-17 */
-                    /* of  https://github.com/uclouvain/openjpeg/issues/938 */
-                    if (levelno >= 32 ||
-                            ((comp->dx << levelno) >> levelno) != comp->dx ||
-                            ((comp->dy << levelno) >> levelno) != comp->dy) {
-                        continue;
-                    }
-                    if ((comp->dx << levelno) > INT_MAX ||
-                            (comp->dy << levelno) > INT_MAX) {
-                        continue;
-                    }
-                    trx0 = opj_int_ceildiv(pi->tx0, (OPJ_INT32)(comp->dx << levelno));
-                    try0 = opj_int_ceildiv(pi->ty0, (OPJ_INT32)(comp->dy << levelno));
-                    trx1 = opj_int_ceildiv(pi->tx1, (OPJ_INT32)(comp->dx << levelno));
-                    try1 = opj_int_ceildiv(pi->ty1, (OPJ_INT32)(comp->dy << levelno));
-                    rpx = res->pdx + levelno;
-                    rpy = res->pdy + levelno;
-
-                    /* To avoid divisions by zero / undefined behaviour on shift */
-                    /* in below tests */
-                    /* Relates to id:000019,sig:08,src:001098,op:flip1,pos:49 */
-                    /* of https://github.com/uclouvain/openjpeg/issues/938 */
-                    if (rpx >= 31 || ((comp->dx << rpx) >> rpx) != comp->dx ||
-                            rpy >= 31 || ((comp->dy << rpy) >> rpy) != comp->dy) {
-                        continue;
-                    }
-
-                    /* See ISO-15441. B.12.1.4 Position-component-resolution level-layer progression */
-                    if (!((pi->y % (OPJ_INT32)(comp->dy << rpy) == 0) || ((pi->y == pi->ty0) &&
-                            ((try0 << levelno) % (1 << rpy))))) {
-                        continue;
-                    }
-                    if (!((pi->x % (OPJ_INT32)(comp->dx << rpx) == 0) || ((pi->x == pi->tx0) &&
-                            ((trx0 << levelno) % (1 << rpx))))) {
-                        continue;
-                    }
-
-                    if ((res->pw == 0) || (res->ph == 0)) {
-                        continue;
-                    }
-
-                    if ((trx0 == trx1) || (try0 == try1)) {
-                        continue;
-                    }
-
-                    prci = opj_int_floordivpow2(opj_int_ceildiv(pi->x,
-                                                (OPJ_INT32)(comp->dx << levelno)), (OPJ_INT32)res->pdx)
-                           - opj_int_floordivpow2(trx0, (OPJ_INT32)res->pdx);
-                    prcj = opj_int_floordivpow2(opj_int_ceildiv(pi->y,
-                                                (OPJ_INT32)(comp->dy << levelno)), (OPJ_INT32)res->pdy)
-                           - opj_int_floordivpow2(try0, (OPJ_INT32)res->pdy);
-                    pi->precno = (OPJ_UINT32)(prci + prcj * (OPJ_INT32)res->pw);
-                    if (pi->precno >= res->pw * res->ph) {
-                      return OPJ_FALSE;
-                    }
-                    for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) {
-                        index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno *
-                                pi->step_c + pi->precno * pi->step_p;
-                        if (index >= pi->include_size) {
-                            opj_pi_emit_error(pi, "Invalid access to pi->include");
-                            return OPJ_FALSE;
-                        }
-                        if (!pi->include[index]) {
-                            pi->include[index] = 1;
-                            return OPJ_TRUE;
-                        }
-LABEL_SKIP:
-                        ;
-                    }
-                }
-            }
-        }
-    }
-
-    return OPJ_FALSE;
-}
-
-static OPJ_BOOL opj_pi_next_cprl(opj_pi_iterator_t * pi)
-{
-    opj_pi_comp_t *comp = NULL;
-    opj_pi_resolution_t *res = NULL;
-    OPJ_UINT32 index = 0;
-
-    if (!pi->first) {
-        comp = &pi->comps[pi->compno];
-        goto LABEL_SKIP;
-    } else {
-        pi->first = 0;
-    }
-
-    for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) {
-        OPJ_UINT32 resno;
-        comp = &pi->comps[pi->compno];
-        pi->dx = 0;
-        pi->dy = 0;
-        for (resno = 0; resno < comp->numresolutions; resno++) {
-            OPJ_UINT32 dx, dy;
-            res = &comp->resolutions[resno];
-            if (res->pdx + comp->numresolutions - 1 - resno < 32 &&
-                    comp->dx <= UINT_MAX / (1u << (res->pdx + comp->numresolutions - 1 - resno))) {
-                dx = comp->dx * (1u << (res->pdx + comp->numresolutions - 1 - resno));
-                pi->dx = !pi->dx ? dx : opj_uint_min(pi->dx, dx);
-            }
-            if (res->pdy + comp->numresolutions - 1 - resno < 32 &&
-                    comp->dy <= UINT_MAX / (1u << (res->pdy + comp->numresolutions - 1 - resno))) {
-                dy = comp->dy * (1u << (res->pdy + comp->numresolutions - 1 - resno));
-                pi->dy = !pi->dy ? dy : opj_uint_min(pi->dy, dy);
-            }
-        }
-        if (pi->dx == 0 || pi->dy == 0) {
-            return OPJ_FALSE;
-        }
-        if (!pi->tp_on) {
-            pi->poc.ty0 = pi->ty0;
-            pi->poc.tx0 = pi->tx0;
-            pi->poc.ty1 = pi->ty1;
-            pi->poc.tx1 = pi->tx1;
-        }
-        for (pi->y = pi->poc.ty0; pi->y < pi->poc.ty1;
-                pi->y += (OPJ_INT32)(pi->dy - (OPJ_UINT32)(pi->y % (OPJ_INT32)pi->dy))) {
-            for (pi->x = pi->poc.tx0; pi->x < pi->poc.tx1;
-                    pi->x += (OPJ_INT32)(pi->dx - (OPJ_UINT32)(pi->x % (OPJ_INT32)pi->dx))) {
-                for (pi->resno = pi->poc.resno0;
-                        pi->resno < opj_uint_min(pi->poc.resno1, comp->numresolutions); pi->resno++) {
-                    OPJ_UINT32 levelno;
-                    OPJ_INT32 trx0, try0;
-                    OPJ_INT32 trx1, try1;
-                    OPJ_UINT32 rpx, rpy;
-                    OPJ_INT32 prci, prcj;
-                    res = &comp->resolutions[pi->resno];
-                    levelno = comp->numresolutions - 1 - pi->resno;
-                    /* Avoids division by zero on id_000004,sig_06,src_000679,op_arith8,pos_49,val_-17 */
-                    /* of  https://github.com/uclouvain/openjpeg/issues/938 */
-                    if (levelno >= 32 ||
-                            ((comp->dx << levelno) >> levelno) != comp->dx ||
-                            ((comp->dy << levelno) >> levelno) != comp->dy) {
-                        continue;
-                    }
-                    if ((comp->dx << levelno) > INT_MAX ||
-                            (comp->dy << levelno) > INT_MAX) {
-                        continue;
-                    }
-                    trx0 = opj_int_ceildiv(pi->tx0, (OPJ_INT32)(comp->dx << levelno));
-                    try0 = opj_int_ceildiv(pi->ty0, (OPJ_INT32)(comp->dy << levelno));
-                    trx1 = opj_int_ceildiv(pi->tx1, (OPJ_INT32)(comp->dx << levelno));
-                    try1 = opj_int_ceildiv(pi->ty1, (OPJ_INT32)(comp->dy << levelno));
-                    rpx = res->pdx + levelno;
-                    rpy = res->pdy + levelno;
-
-                    /* To avoid divisions by zero / undefined behaviour on shift */
-                    /* in below tests */
-                    /* Fixes reading id:000019,sig:08,src:001098,op:flip1,pos:49 */
-                    /* of https://github.com/uclouvain/openjpeg/issues/938 */
-                    if (rpx >= 31 || ((comp->dx << rpx) >> rpx) != comp->dx ||
-                            rpy >= 31 || ((comp->dy << rpy) >> rpy) != comp->dy) {
-                        continue;
-                    }
-
-                    /* See ISO-15441. B.12.1.5 Component-position-resolution level-layer progression */
-                    if (!((pi->y % (OPJ_INT32)(comp->dy << rpy) == 0) || ((pi->y == pi->ty0) &&
-                            ((try0 << levelno) % (1 << rpy))))) {
-                        continue;
-                    }
-                    if (!((pi->x % (OPJ_INT32)(comp->dx << rpx) == 0) || ((pi->x == pi->tx0) &&
-                            ((trx0 << levelno) % (1 << rpx))))) {
-                        continue;
-                    }
-
-                    if ((res->pw == 0) || (res->ph == 0)) {
-                        continue;
-                    }
-
-                    if ((trx0 == trx1) || (try0 == try1)) {
-                        continue;
-                    }
-
-                    prci = opj_int_floordivpow2(opj_int_ceildiv(pi->x,
-                                                (OPJ_INT32)(comp->dx << levelno)), (OPJ_INT32)res->pdx)
-                           - opj_int_floordivpow2(trx0, (OPJ_INT32)res->pdx);
-                    prcj = opj_int_floordivpow2(opj_int_ceildiv(pi->y,
-                                                (OPJ_INT32)(comp->dy << levelno)), (OPJ_INT32)res->pdy)
-                           - opj_int_floordivpow2(try0, (OPJ_INT32)res->pdy);
-                    pi->precno = (OPJ_UINT32)(prci + prcj * (OPJ_INT32)res->pw);
-                    if (pi->precno >= res->pw * res->ph) {
-                      return OPJ_FALSE;
-                    }
-                    for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) {
-                        index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno *
-                                pi->step_c + pi->precno * pi->step_p;
-                        if (index >= pi->include_size) {
-                            opj_pi_emit_error(pi, "Invalid access to pi->include");
-                            return OPJ_FALSE;
-                        }
-                        if (!pi->include[index]) {
-                            pi->include[index] = 1;
-                            return OPJ_TRUE;
-                        }
-LABEL_SKIP:
-                        ;
-                    }
-                }
-            }
-        }
-    }
-
-    return OPJ_FALSE;
-}
-
-static void opj_get_encoding_parameters(const opj_image_t *p_image,
-                                        const opj_cp_t *p_cp,
-                                        OPJ_UINT32 p_tileno,
-                                        OPJ_INT32 * p_tx0,
-                                        OPJ_INT32  * p_tx1,
-                                        OPJ_INT32  * p_ty0,
-                                        OPJ_INT32  * p_ty1,
-                                        OPJ_UINT32 * p_dx_min,
-                                        OPJ_UINT32 * p_dy_min,
-                                        OPJ_UINT32 * p_max_prec,
-                                        OPJ_UINT32 * p_max_res)
-{
-    /* loop */
-    OPJ_UINT32  compno, resno;
-    /* pointers */
-    const opj_tcp_t *l_tcp = 00;
-    const opj_tccp_t * l_tccp = 00;
-    const opj_image_comp_t * l_img_comp = 00;
-
-    /* position in x and y of tile */
-    OPJ_UINT32 p, q;
-
-    /* non-corrected (in regard to image offset) tile offset */
-    OPJ_UINT32 l_tx0, l_ty0;
-
-    /* preconditions */
-    assert(p_cp != 00);
-    assert(p_image != 00);
-    assert(p_tileno < p_cp->tw * p_cp->th);
-
-    /* initializations */
-    l_tcp = &p_cp->tcps [p_tileno];
-    l_img_comp = p_image->comps;
-    l_tccp = l_tcp->tccps;
-
-    /* here calculation of tx0, tx1, ty0, ty1, maxprec, dx and dy */
-    p = p_tileno % p_cp->tw;
-    q = p_tileno / p_cp->tw;
-
-    /* find extent of tile */
-    l_tx0 = p_cp->tx0 + p *
-            p_cp->tdx; /* can't be greater than p_image->x1 so won't overflow */
-    *p_tx0 = (OPJ_INT32)opj_uint_max(l_tx0, p_image->x0);
-    *p_tx1 = (OPJ_INT32)opj_uint_min(opj_uint_adds(l_tx0, p_cp->tdx), p_image->x1);
-    l_ty0 = p_cp->ty0 + q *
-            p_cp->tdy; /* can't be greater than p_image->y1 so won't overflow */
-    *p_ty0 = (OPJ_INT32)opj_uint_max(l_ty0, p_image->y0);
-    *p_ty1 = (OPJ_INT32)opj_uint_min(opj_uint_adds(l_ty0, p_cp->tdy), p_image->y1);
-
-    /* max precision is 0 (can only grow) */
-    *p_max_prec = 0;
-    *p_max_res = 0;
-
-    /* take the largest value for dx_min and dy_min */
-    *p_dx_min = 0x7fffffff;
-    *p_dy_min  = 0x7fffffff;
-
-    for (compno = 0; compno < p_image->numcomps; ++compno) {
-        /* arithmetic variables to calculate */
-        OPJ_UINT32 l_level_no;
-        OPJ_INT32 l_rx0, l_ry0, l_rx1, l_ry1;
-        OPJ_INT32 l_px0, l_py0, l_px1, py1;
-        OPJ_UINT32 l_pdx, l_pdy;
-        OPJ_UINT32 l_pw, l_ph;
-        OPJ_UINT32 l_product;
-        OPJ_INT32 l_tcx0, l_tcy0, l_tcx1, l_tcy1;
-
-        l_tcx0 = opj_int_ceildiv(*p_tx0, (OPJ_INT32)l_img_comp->dx);
-        l_tcy0 = opj_int_ceildiv(*p_ty0, (OPJ_INT32)l_img_comp->dy);
-        l_tcx1 = opj_int_ceildiv(*p_tx1, (OPJ_INT32)l_img_comp->dx);
-        l_tcy1 = opj_int_ceildiv(*p_ty1, (OPJ_INT32)l_img_comp->dy);
-
-        if (l_tccp->numresolutions > *p_max_res) {
-            *p_max_res = l_tccp->numresolutions;
-        }
-
-        /* use custom size for precincts */
-        for (resno = 0; resno < l_tccp->numresolutions; ++resno) {
-            OPJ_UINT32 l_dx, l_dy;
-
-            /* precinct width and height */
-            l_pdx = l_tccp->prcw[resno];
-            l_pdy = l_tccp->prch[resno];
-
-            l_dx = l_img_comp->dx * (1u << (l_pdx + l_tccp->numresolutions - 1 - resno));
-            l_dy = l_img_comp->dy * (1u << (l_pdy + l_tccp->numresolutions - 1 - resno));
-
-            /* take the minimum size for dx for each comp and resolution */
-            *p_dx_min = opj_uint_min(*p_dx_min, l_dx);
-            *p_dy_min = opj_uint_min(*p_dy_min, l_dy);
-
-            /* various calculations of extents */
-            l_level_no = l_tccp->numresolutions - 1 - resno;
-
-            l_rx0 = opj_int_ceildivpow2(l_tcx0, (OPJ_INT32)l_level_no);
-            l_ry0 = opj_int_ceildivpow2(l_tcy0, (OPJ_INT32)l_level_no);
-            l_rx1 = opj_int_ceildivpow2(l_tcx1, (OPJ_INT32)l_level_no);
-            l_ry1 = opj_int_ceildivpow2(l_tcy1, (OPJ_INT32)l_level_no);
-
-            l_px0 = opj_int_floordivpow2(l_rx0, (OPJ_INT32)l_pdx) << l_pdx;
-            l_py0 = opj_int_floordivpow2(l_ry0, (OPJ_INT32)l_pdy) << l_pdy;
-            l_px1 = opj_int_ceildivpow2(l_rx1, (OPJ_INT32)l_pdx) << l_pdx;
-
-            py1 = opj_int_ceildivpow2(l_ry1, (OPJ_INT32)l_pdy) << l_pdy;
-
-            l_pw = (l_rx0 == l_rx1) ? 0 : (OPJ_UINT32)((l_px1 - l_px0) >> l_pdx);
-            l_ph = (l_ry0 == l_ry1) ? 0 : (OPJ_UINT32)((py1 - l_py0) >> l_pdy);
-
-            l_product = l_pw * l_ph;
-
-            /* update precision */
-            if (l_product > *p_max_prec) {
-                *p_max_prec = l_product;
-            }
-        }
-        ++l_img_comp;
-        ++l_tccp;
-    }
-}
-
-
-static void opj_get_all_encoding_parameters(const opj_image_t *p_image,
-        const opj_cp_t *p_cp,
-        OPJ_UINT32 tileno,
-        OPJ_INT32 * p_tx0,
-        OPJ_INT32 * p_tx1,
-        OPJ_INT32 * p_ty0,
-        OPJ_INT32 * p_ty1,
-        OPJ_UINT32 * p_dx_min,
-        OPJ_UINT32 * p_dy_min,
-        OPJ_UINT32 * p_max_prec,
-        OPJ_UINT32 * p_max_res,
-        OPJ_UINT32 ** p_resolutions)
-{
-    /* loop*/
-    OPJ_UINT32 compno, resno;
-
-    /* pointers*/
-    const opj_tcp_t *tcp = 00;
-    const opj_tccp_t * l_tccp = 00;
-    const opj_image_comp_t * l_img_comp = 00;
-
-    /* to store l_dx, l_dy, w and h for each resolution and component.*/
-    OPJ_UINT32 * lResolutionPtr;
-
-    /* position in x and y of tile*/
-    OPJ_UINT32 p, q;
-
-    /* non-corrected (in regard to image offset) tile offset */
-    OPJ_UINT32 l_tx0, l_ty0;
-
-    /* preconditions in debug*/
-    assert(p_cp != 00);
-    assert(p_image != 00);
-    assert(tileno < p_cp->tw * p_cp->th);
-
-    /* initializations*/
-    tcp = &p_cp->tcps [tileno];
-    l_tccp = tcp->tccps;
-    l_img_comp = p_image->comps;
-
-    /* position in x and y of tile*/
-    p = tileno % p_cp->tw;
-    q = tileno / p_cp->tw;
-
-    /* here calculation of tx0, tx1, ty0, ty1, maxprec, l_dx and l_dy */
-    l_tx0 = p_cp->tx0 + p *
-            p_cp->tdx; /* can't be greater than p_image->x1 so won't overflow */
-    *p_tx0 = (OPJ_INT32)opj_uint_max(l_tx0, p_image->x0);
-    *p_tx1 = (OPJ_INT32)opj_uint_min(opj_uint_adds(l_tx0, p_cp->tdx), p_image->x1);
-    l_ty0 = p_cp->ty0 + q *
-            p_cp->tdy; /* can't be greater than p_image->y1 so won't overflow */
-    *p_ty0 = (OPJ_INT32)opj_uint_max(l_ty0, p_image->y0);
-    *p_ty1 = (OPJ_INT32)opj_uint_min(opj_uint_adds(l_ty0, p_cp->tdy), p_image->y1);
-
-    /* max precision and resolution is 0 (can only grow)*/
-    *p_max_prec = 0;
-    *p_max_res = 0;
-
-    /* take the largest value for dx_min and dy_min*/
-    *p_dx_min = 0x7fffffff;
-    *p_dy_min = 0x7fffffff;
-
-    for (compno = 0; compno < p_image->numcomps; ++compno) {
-        /* aritmetic variables to calculate*/
-        OPJ_UINT32 l_level_no;
-        OPJ_INT32 l_rx0, l_ry0, l_rx1, l_ry1;
-        OPJ_INT32 l_px0, l_py0, l_px1, py1;
-        OPJ_UINT32 l_product;
-        OPJ_INT32 l_tcx0, l_tcy0, l_tcx1, l_tcy1;
-        OPJ_UINT32 l_pdx, l_pdy, l_pw, l_ph;
-
-        lResolutionPtr = p_resolutions[compno];
-
-        l_tcx0 = opj_int_ceildiv(*p_tx0, (OPJ_INT32)l_img_comp->dx);
-        l_tcy0 = opj_int_ceildiv(*p_ty0, (OPJ_INT32)l_img_comp->dy);
-        l_tcx1 = opj_int_ceildiv(*p_tx1, (OPJ_INT32)l_img_comp->dx);
-        l_tcy1 = opj_int_ceildiv(*p_ty1, (OPJ_INT32)l_img_comp->dy);
-
-        if (l_tccp->numresolutions > *p_max_res) {
-            *p_max_res = l_tccp->numresolutions;
-        }
-
-        /* use custom size for precincts*/
-        l_level_no = l_tccp->numresolutions;
-        for (resno = 0; resno < l_tccp->numresolutions; ++resno) {
-            OPJ_UINT32 l_dx, l_dy;
-
-            --l_level_no;
-
-            /* precinct width and height*/
-            l_pdx = l_tccp->prcw[resno];
-            l_pdy = l_tccp->prch[resno];
-            *lResolutionPtr++ = l_pdx;
-            *lResolutionPtr++ = l_pdy;
-            if (l_pdx + l_level_no < 32 &&
-                    l_img_comp->dx <= UINT_MAX / (1u << (l_pdx + l_level_no))) {
-                l_dx = l_img_comp->dx * (1u << (l_pdx + l_level_no));
-                /* take the minimum size for l_dx for each comp and resolution*/
-                *p_dx_min = (OPJ_UINT32)opj_int_min((OPJ_INT32) * p_dx_min, (OPJ_INT32)l_dx);
-            }
-            if (l_pdy + l_level_no < 32 &&
-                    l_img_comp->dy <= UINT_MAX / (1u << (l_pdy + l_level_no))) {
-                l_dy = l_img_comp->dy * (1u << (l_pdy + l_level_no));
-                *p_dy_min = (OPJ_UINT32)opj_int_min((OPJ_INT32) * p_dy_min, (OPJ_INT32)l_dy);
-            }
-
-            /* various calculations of extents*/
-            l_rx0 = opj_int_ceildivpow2(l_tcx0, (OPJ_INT32)l_level_no);
-            l_ry0 = opj_int_ceildivpow2(l_tcy0, (OPJ_INT32)l_level_no);
-            l_rx1 = opj_int_ceildivpow2(l_tcx1, (OPJ_INT32)l_level_no);
-            l_ry1 = opj_int_ceildivpow2(l_tcy1, (OPJ_INT32)l_level_no);
-            l_px0 = opj_int_floordivpow2(l_rx0, (OPJ_INT32)l_pdx) << l_pdx;
-            l_py0 = opj_int_floordivpow2(l_ry0, (OPJ_INT32)l_pdy) << l_pdy;
-            l_px1 = opj_int_ceildivpow2(l_rx1, (OPJ_INT32)l_pdx) << l_pdx;
-            py1 = opj_int_ceildivpow2(l_ry1, (OPJ_INT32)l_pdy) << l_pdy;
-            l_pw = (l_rx0 == l_rx1) ? 0 : (OPJ_UINT32)((l_px1 - l_px0) >> l_pdx);
-            l_ph = (l_ry0 == l_ry1) ? 0 : (OPJ_UINT32)((py1 - l_py0) >> l_pdy);
-            *lResolutionPtr++ = l_pw;
-            *lResolutionPtr++ = l_ph;
-            l_product = l_pw * l_ph;
-
-            /* update precision*/
-            if (l_product > *p_max_prec) {
-                *p_max_prec = l_product;
-            }
-
-        }
-        ++l_tccp;
-        ++l_img_comp;
-    }
-}
-
-static opj_pi_iterator_t * opj_pi_create(const opj_image_t *image,
-        const opj_cp_t *cp,
-        OPJ_UINT32 tileno)
-{
-    /* loop*/
-    OPJ_UINT32 pino, compno;
-    /* number of poc in the p_pi*/
-    OPJ_UINT32 l_poc_bound;
-
-    /* pointers to tile coding parameters and components.*/
-    opj_pi_iterator_t *l_pi = 00;
-    opj_tcp_t *tcp = 00;
-    const opj_tccp_t *tccp = 00;
-
-    /* current packet iterator being allocated*/
-    opj_pi_iterator_t *l_current_pi = 00;
-
-    /* preconditions in debug*/
-    assert(cp != 00);
-    assert(image != 00);
-    assert(tileno < cp->tw * cp->th);
-
-    /* initializations*/
-    tcp = &cp->tcps[tileno];
-    l_poc_bound = tcp->numpocs + 1;
-
-    /* memory allocations*/
-    l_pi = (opj_pi_iterator_t*) opj_calloc((l_poc_bound),
-                                           sizeof(opj_pi_iterator_t));
-    if (!l_pi) {
-        return NULL;
-    }
-
-    l_current_pi = l_pi;
-    for (pino = 0; pino < l_poc_bound ; ++pino) {
-
-        l_current_pi->comps = (opj_pi_comp_t*) opj_calloc(image->numcomps,
-                              sizeof(opj_pi_comp_t));
-        if (! l_current_pi->comps) {
-            opj_pi_destroy(l_pi, l_poc_bound);
-            return NULL;
-        }
-
-        l_current_pi->numcomps = image->numcomps;
-
-        for (compno = 0; compno < image->numcomps; ++compno) {
-            opj_pi_comp_t *comp = &l_current_pi->comps[compno];
-
-            tccp = &tcp->tccps[compno];
-
-            comp->resolutions = (opj_pi_resolution_t*) opj_calloc(tccp->numresolutions,
-                                sizeof(opj_pi_resolution_t));
-            if (!comp->resolutions) {
-                opj_pi_destroy(l_pi, l_poc_bound);
-                return 00;
-            }
-
-            comp->numresolutions = tccp->numresolutions;
-        }
-        ++l_current_pi;
-    }
-    return l_pi;
-}
-
-static void opj_pi_update_encode_poc_and_final(opj_cp_t *p_cp,
-        OPJ_UINT32 p_tileno,
-        OPJ_INT32 p_tx0,
-        OPJ_INT32 p_tx1,
-        OPJ_INT32 p_ty0,
-        OPJ_INT32 p_ty1,
-        OPJ_UINT32 p_max_prec,
-        OPJ_UINT32 p_max_res,
-        OPJ_UINT32 p_dx_min,
-        OPJ_UINT32 p_dy_min)
-{
-    /* loop*/
-    OPJ_UINT32 pino;
-    /* tile coding parameter*/
-    opj_tcp_t *l_tcp = 00;
-    /* current poc being updated*/
-    opj_poc_t * l_current_poc = 00;
-
-    /* number of pocs*/
-    OPJ_UINT32 l_poc_bound;
-
-    OPJ_ARG_NOT_USED(p_max_res);
-
-    /* preconditions in debug*/
-    assert(p_cp != 00);
-    assert(p_tileno < p_cp->tw * p_cp->th);
-
-    /* initializations*/
-    l_tcp = &p_cp->tcps [p_tileno];
-    /* number of iterations in the loop */
-    l_poc_bound = l_tcp->numpocs + 1;
-
-    /* start at first element, and to make sure the compiler will not make a calculation each time in the loop
-       store a pointer to the current element to modify rather than l_tcp->pocs[i]*/
-    l_current_poc = l_tcp->pocs;
-
-    l_current_poc->compS = l_current_poc->compno0;
-    l_current_poc->compE = l_current_poc->compno1;
-    l_current_poc->resS = l_current_poc->resno0;
-    l_current_poc->resE = l_current_poc->resno1;
-    l_current_poc->layE = l_current_poc->layno1;
-
-    /* special treatment for the first element*/
-    l_current_poc->layS = 0;
-    l_current_poc->prg  = l_current_poc->prg1;
-    l_current_poc->prcS = 0;
-
-    l_current_poc->prcE = p_max_prec;
-    l_current_poc->txS = (OPJ_UINT32)p_tx0;
-    l_current_poc->txE = (OPJ_UINT32)p_tx1;
-    l_current_poc->tyS = (OPJ_UINT32)p_ty0;
-    l_current_poc->tyE = (OPJ_UINT32)p_ty1;
-    l_current_poc->dx = p_dx_min;
-    l_current_poc->dy = p_dy_min;
-
-    ++ l_current_poc;
-    for (pino = 1; pino < l_poc_bound ; ++pino) {
-        l_current_poc->compS = l_current_poc->compno0;
-        l_current_poc->compE = l_current_poc->compno1;
-        l_current_poc->resS = l_current_poc->resno0;
-        l_current_poc->resE = l_current_poc->resno1;
-        l_current_poc->layE = l_current_poc->layno1;
-        l_current_poc->prg  = l_current_poc->prg1;
-        l_current_poc->prcS = 0;
-        /* special treatment here different from the first element*/
-        l_current_poc->layS = (l_current_poc->layE > (l_current_poc - 1)->layE) ?
-                              l_current_poc->layE : 0;
-
-        l_current_poc->prcE = p_max_prec;
-        l_current_poc->txS = (OPJ_UINT32)p_tx0;
-        l_current_poc->txE = (OPJ_UINT32)p_tx1;
-        l_current_poc->tyS = (OPJ_UINT32)p_ty0;
-        l_current_poc->tyE = (OPJ_UINT32)p_ty1;
-        l_current_poc->dx = p_dx_min;
-        l_current_poc->dy = p_dy_min;
-        ++ l_current_poc;
-    }
-}
-
-static void opj_pi_update_encode_not_poc(opj_cp_t *p_cp,
-        OPJ_UINT32 p_num_comps,
-        OPJ_UINT32 p_tileno,
-        OPJ_INT32 p_tx0,
-        OPJ_INT32 p_tx1,
-        OPJ_INT32 p_ty0,
-        OPJ_INT32 p_ty1,
-        OPJ_UINT32 p_max_prec,
-        OPJ_UINT32 p_max_res,
-        OPJ_UINT32 p_dx_min,
-        OPJ_UINT32 p_dy_min)
-{
-    /* loop*/
-    OPJ_UINT32 pino;
-    /* tile coding parameter*/
-    opj_tcp_t *l_tcp = 00;
-    /* current poc being updated*/
-    opj_poc_t * l_current_poc = 00;
-    /* number of pocs*/
-    OPJ_UINT32 l_poc_bound;
-
-    /* preconditions in debug*/
-    assert(p_cp != 00);
-    assert(p_tileno < p_cp->tw * p_cp->th);
-
-    /* initializations*/
-    l_tcp = &p_cp->tcps [p_tileno];
-
-    /* number of iterations in the loop */
-    l_poc_bound = l_tcp->numpocs + 1;
-
-    /* start at first element, and to make sure the compiler will not make a calculation each time in the loop
-       store a pointer to the current element to modify rather than l_tcp->pocs[i]*/
-    l_current_poc = l_tcp->pocs;
-
-    for (pino = 0; pino < l_poc_bound ; ++pino) {
-        l_current_poc->compS = 0;
-        l_current_poc->compE = p_num_comps;/*p_image->numcomps;*/
-        l_current_poc->resS = 0;
-        l_current_poc->resE = p_max_res;
-        l_current_poc->layS = 0;
-        l_current_poc->layE = l_tcp->numlayers;
-        l_current_poc->prg  = l_tcp->prg;
-        l_current_poc->prcS = 0;
-        l_current_poc->prcE = p_max_prec;
-        l_current_poc->txS = (OPJ_UINT32)p_tx0;
-        l_current_poc->txE = (OPJ_UINT32)p_tx1;
-        l_current_poc->tyS = (OPJ_UINT32)p_ty0;
-        l_current_poc->tyE = (OPJ_UINT32)p_ty1;
-        l_current_poc->dx = p_dx_min;
-        l_current_poc->dy = p_dy_min;
-        ++ l_current_poc;
-    }
-}
-
-static void opj_pi_update_decode_poc(opj_pi_iterator_t * p_pi,
-                                     opj_tcp_t * p_tcp,
-                                     OPJ_UINT32 p_max_precision,
-                                     OPJ_UINT32 p_max_res)
-{
-    /* loop*/
-    OPJ_UINT32 pino;
-
-    /* encoding prameters to set*/
-    OPJ_UINT32 l_bound;
-
-    opj_pi_iterator_t * l_current_pi = 00;
-    opj_poc_t* l_current_poc = 0;
-
-    OPJ_ARG_NOT_USED(p_max_res);
-
-    /* preconditions in debug*/
-    assert(p_pi != 00);
-    assert(p_tcp != 00);
-
-    /* initializations*/
-    l_bound = p_tcp->numpocs + 1;
-    l_current_pi = p_pi;
-    l_current_poc = p_tcp->pocs;
-
-    for (pino = 0; pino < l_bound; ++pino) {
-        l_current_pi->poc.prg = l_current_poc->prg; /* Progression Order #0 */
-        l_current_pi->first = 1;
-
-        l_current_pi->poc.resno0 =
-            l_current_poc->resno0; /* Resolution Level Index #0 (Start) */
-        l_current_pi->poc.compno0 =
-            l_current_poc->compno0; /* Component Index #0 (Start) */
-        l_current_pi->poc.layno0 = 0;
-        l_current_pi->poc.precno0 = 0;
-        l_current_pi->poc.resno1 =
-            l_current_poc->resno1; /* Resolution Level Index #0 (End) */
-        l_current_pi->poc.compno1 =
-            l_current_poc->compno1; /* Component Index #0 (End) */
-        l_current_pi->poc.layno1 = opj_uint_min(l_current_poc->layno1,
-                                                p_tcp->numlayers); /* Layer Index #0 (End) */
-        l_current_pi->poc.precno1 = p_max_precision;
-        ++l_current_pi;
-        ++l_current_poc;
-    }
-}
-
-static void opj_pi_update_decode_not_poc(opj_pi_iterator_t * p_pi,
-        opj_tcp_t * p_tcp,
-        OPJ_UINT32 p_max_precision,
-        OPJ_UINT32 p_max_res)
-{
-    /* loop*/
-    OPJ_UINT32 pino;
-
-    /* encoding prameters to set*/
-    OPJ_UINT32 l_bound;
-
-    opj_pi_iterator_t * l_current_pi = 00;
-    /* preconditions in debug*/
-    assert(p_tcp != 00);
-    assert(p_pi != 00);
-
-    /* initializations*/
-    l_bound = p_tcp->numpocs + 1;
-    l_current_pi = p_pi;
-
-    for (pino = 0; pino < l_bound; ++pino) {
-        l_current_pi->poc.prg = p_tcp->prg;
-        l_current_pi->first = 1;
-        l_current_pi->poc.resno0 = 0;
-        l_current_pi->poc.compno0 = 0;
-        l_current_pi->poc.layno0 = 0;
-        l_current_pi->poc.precno0 = 0;
-        l_current_pi->poc.resno1 = p_max_res;
-        l_current_pi->poc.compno1 = l_current_pi->numcomps;
-        l_current_pi->poc.layno1 = p_tcp->numlayers;
-        l_current_pi->poc.precno1 = p_max_precision;
-        ++l_current_pi;
-    }
-}
-
-
-
-static OPJ_BOOL opj_pi_check_next_level(OPJ_INT32 pos,
-                                        opj_cp_t *cp,
-                                        OPJ_UINT32 tileno,
-                                        OPJ_UINT32 pino,
-                                        const OPJ_CHAR *prog)
-{
-    OPJ_INT32 i;
-    opj_tcp_t *tcps = &cp->tcps[tileno];
-    opj_poc_t *tcp = &tcps->pocs[pino];
-
-    if (pos >= 0) {
-        for (i = pos; pos >= 0; i--) {
-            switch (prog[i]) {
-            case 'R':
-                if (tcp->res_t == tcp->resE) {
-                    if (opj_pi_check_next_level(pos - 1, cp, tileno, pino, prog)) {
-                        return OPJ_TRUE;
-                    } else {
-                        return OPJ_FALSE;
-                    }
-                } else {
-                    return OPJ_TRUE;
-                }
-                break;
-            case 'C':
-                if (tcp->comp_t == tcp->compE) {
-                    if (opj_pi_check_next_level(pos - 1, cp, tileno, pino, prog)) {
-                        return OPJ_TRUE;
-                    } else {
-                        return OPJ_FALSE;
-                    }
-                } else {
-                    return OPJ_TRUE;
-                }
-                break;
-            case 'L':
-                if (tcp->lay_t == tcp->layE) {
-                    if (opj_pi_check_next_level(pos - 1, cp, tileno, pino, prog)) {
-                        return OPJ_TRUE;
-                    } else {
-                        return OPJ_FALSE;
-                    }
-                } else {
-                    return OPJ_TRUE;
-                }
-                break;
-            case 'P':
-                switch (tcp->prg) {
-                case OPJ_LRCP: /* fall through */
-                case OPJ_RLCP:
-                    if (tcp->prc_t == tcp->prcE) {
-                        if (opj_pi_check_next_level(i - 1, cp, tileno, pino, prog)) {
-                            return OPJ_TRUE;
-                        } else {
-                            return OPJ_FALSE;
-                        }
-                    } else {
-                        return OPJ_TRUE;
-                    }
-                    break;
-                default:
-                    if (tcp->tx0_t == tcp->txE) {
-                        /*TY*/
-                        if (tcp->ty0_t == tcp->tyE) {
-                            if (opj_pi_check_next_level(i - 1, cp, tileno, pino, prog)) {
-                                return OPJ_TRUE;
-                            } else {
-                                return OPJ_FALSE;
-                            }
-                        } else {
-                            return OPJ_TRUE;
-                        }/*TY*/
-                    } else {
-                        return OPJ_TRUE;
-                    }
-                    break;
-                }/*end case P*/
-            }/*end switch*/
-        }/*end for*/
-    }/*end if*/
-    return OPJ_FALSE;
-}
-
-
-/*
-==========================================================
-   Packet iterator interface
-==========================================================
-*/
-opj_pi_iterator_t *opj_pi_create_decode(opj_image_t *p_image,
-                                        opj_cp_t *p_cp,
-                                        OPJ_UINT32 p_tile_no)
-{
-    OPJ_UINT32 numcomps = p_image->numcomps;
-
-    /* loop */
-    OPJ_UINT32 pino;
-    OPJ_UINT32 compno, resno;
-
-    /* to store w, h, dx and dy fro all components and resolutions */
-    OPJ_UINT32 * l_tmp_data;
-    OPJ_UINT32 ** l_tmp_ptr;
-
-    /* encoding prameters to set */
-    OPJ_UINT32 l_max_res;
-    OPJ_UINT32 l_max_prec;
-    OPJ_INT32 l_tx0, l_tx1, l_ty0, l_ty1;
-    OPJ_UINT32 l_dx_min, l_dy_min;
-    OPJ_UINT32 l_bound;
-    OPJ_UINT32 l_step_p, l_step_c, l_step_r, l_step_l ;
-    OPJ_UINT32 l_data_stride;
-
-    /* pointers */
-    opj_pi_iterator_t *l_pi = 00;
-    opj_tcp_t *l_tcp = 00;
-    const opj_tccp_t *l_tccp = 00;
-    opj_pi_comp_t *l_current_comp = 00;
-    opj_image_comp_t * l_img_comp = 00;
-    opj_pi_iterator_t * l_current_pi = 00;
-    OPJ_UINT32 * l_encoding_value_ptr = 00;
-
-    /* preconditions in debug */
-    assert(p_cp != 00);
-    assert(p_image != 00);
-    assert(p_tile_no < p_cp->tw * p_cp->th);
-
-    /* initializations */
-    l_tcp = &p_cp->tcps[p_tile_no];
-    l_bound = l_tcp->numpocs + 1;
-
-    l_data_stride = 4 * OPJ_J2K_MAXRLVLS;
-    l_tmp_data = (OPJ_UINT32*)opj_malloc(
-                     l_data_stride * numcomps * sizeof(OPJ_UINT32));
-    if
-    (! l_tmp_data) {
-        return 00;
-    }
-    l_tmp_ptr = (OPJ_UINT32**)opj_malloc(
-                    numcomps * sizeof(OPJ_UINT32 *));
-    if
-    (! l_tmp_ptr) {
-        opj_free(l_tmp_data);
-        return 00;
-    }
-
-    /* memory allocation for pi */
-    l_pi = opj_pi_create(p_image, p_cp, p_tile_no);
-    if (!l_pi) {
-        opj_free(l_tmp_data);
-        opj_free(l_tmp_ptr);
-        return 00;
-    }
-
-    l_encoding_value_ptr = l_tmp_data;
-    /* update pointer array */
-    for
-    (compno = 0; compno < numcomps; ++compno) {
-        l_tmp_ptr[compno] = l_encoding_value_ptr;
-        l_encoding_value_ptr += l_data_stride;
-    }
-    /* get encoding parameters */
-    opj_get_all_encoding_parameters(p_image, p_cp, p_tile_no, &l_tx0, &l_tx1,
-                                    &l_ty0, &l_ty1, &l_dx_min, &l_dy_min, &l_max_prec, &l_max_res, l_tmp_ptr);
-
-    /* step calculations */
-    l_step_p = 1;
-    l_step_c = l_max_prec * l_step_p;
-    l_step_r = numcomps * l_step_c;
-    l_step_l = l_max_res * l_step_r;
-
-    /* set values for first packet iterator */
-    l_current_pi = l_pi;
-
-    /* memory allocation for include */
-    /* prevent an integer overflow issue */
-    /* 0 < l_tcp->numlayers < 65536 c.f. opj_j2k_read_cod in j2k.c */
-    l_current_pi->include = 00;
-    if (l_step_l <= (UINT_MAX / (l_tcp->numlayers + 1U))) {
-        l_current_pi->include_size = (l_tcp->numlayers + 1U) * l_step_l;
-        l_current_pi->include = (OPJ_INT16*) opj_calloc(
-                                    l_current_pi->include_size, sizeof(OPJ_INT16));
-    }
-
-    if (!l_current_pi->include) {
-        opj_free(l_tmp_data);
-        opj_free(l_tmp_ptr);
-        opj_pi_destroy(l_pi, l_bound);
-        return 00;
-    }
-
-    /* special treatment for the first packet iterator */
-    l_current_comp = l_current_pi->comps;
-    l_img_comp = p_image->comps;
-    l_tccp = l_tcp->tccps;
-
-    l_current_pi->tx0 = l_tx0;
-    l_current_pi->ty0 = l_ty0;
-    l_current_pi->tx1 = l_tx1;
-    l_current_pi->ty1 = l_ty1;
-
-    /*l_current_pi->dx = l_img_comp->dx;*/
-    /*l_current_pi->dy = l_img_comp->dy;*/
-
-    l_current_pi->step_p = l_step_p;
-    l_current_pi->step_c = l_step_c;
-    l_current_pi->step_r = l_step_r;
-    l_current_pi->step_l = l_step_l;
-
-    /* allocation for components and number of components has already been calculated by opj_pi_create */
-    for
-    (compno = 0; compno < numcomps; ++compno) {
-        opj_pi_resolution_t *l_res = l_current_comp->resolutions;
-        l_encoding_value_ptr = l_tmp_ptr[compno];
-
-        l_current_comp->dx = l_img_comp->dx;
-        l_current_comp->dy = l_img_comp->dy;
-        /* resolutions have already been initialized */
-        for
-        (resno = 0; resno < l_current_comp->numresolutions; resno++) {
-            l_res->pdx = *(l_encoding_value_ptr++);
-            l_res->pdy = *(l_encoding_value_ptr++);
-            l_res->pw =  *(l_encoding_value_ptr++);
-            l_res->ph =  *(l_encoding_value_ptr++);
-            ++l_res;
-        }
-        ++l_current_comp;
-        ++l_img_comp;
-        ++l_tccp;
-    }
-    ++l_current_pi;
-
-    for (pino = 1 ; pino < l_bound ; ++pino) {
-        l_current_comp = l_current_pi->comps;
-        l_img_comp = p_image->comps;
-        l_tccp = l_tcp->tccps;
-
-        l_current_pi->tx0 = l_tx0;
-        l_current_pi->ty0 = l_ty0;
-        l_current_pi->tx1 = l_tx1;
-        l_current_pi->ty1 = l_ty1;
-        /*l_current_pi->dx = l_dx_min;*/
-        /*l_current_pi->dy = l_dy_min;*/
-        l_current_pi->step_p = l_step_p;
-        l_current_pi->step_c = l_step_c;
-        l_current_pi->step_r = l_step_r;
-        l_current_pi->step_l = l_step_l;
-
-        /* allocation for components and number of components has already been calculated by opj_pi_create */
-        for
-        (compno = 0; compno < numcomps; ++compno) {
-            opj_pi_resolution_t *l_res = l_current_comp->resolutions;
-            l_encoding_value_ptr = l_tmp_ptr[compno];
-
-            l_current_comp->dx = l_img_comp->dx;
-            l_current_comp->dy = l_img_comp->dy;
-            /* resolutions have already been initialized */
-            for
-            (resno = 0; resno < l_current_comp->numresolutions; resno++) {
-                l_res->pdx = *(l_encoding_value_ptr++);
-                l_res->pdy = *(l_encoding_value_ptr++);
-                l_res->pw =  *(l_encoding_value_ptr++);
-                l_res->ph =  *(l_encoding_value_ptr++);
-                ++l_res;
-            }
-            ++l_current_comp;
-            ++l_img_comp;
-            ++l_tccp;
-        }
-        /* special treatment*/
-        l_current_pi->include = (l_current_pi - 1)->include;
-        l_current_pi->include_size = (l_current_pi - 1)->include_size;
-        ++l_current_pi;
-    }
-    opj_free(l_tmp_data);
-    l_tmp_data = 00;
-    opj_free(l_tmp_ptr);
-    l_tmp_ptr = 00;
-    if
-    (l_tcp->POC) {
-        opj_pi_update_decode_poc(l_pi, l_tcp, l_max_prec, l_max_res);
-    } else {
-        opj_pi_update_decode_not_poc(l_pi, l_tcp, l_max_prec, l_max_res);
-    }
-    return l_pi;
-}
-
-
-
-opj_pi_iterator_t *opj_pi_initialise_encode(const opj_image_t *p_image,
-        opj_cp_t *p_cp,
-        OPJ_UINT32 p_tile_no,
-        J2K_T2_MODE p_t2_mode)
-{
-    OPJ_UINT32 numcomps = p_image->numcomps;
-
-    /* loop*/
-    OPJ_UINT32 pino;
-    OPJ_UINT32 compno, resno;
-
-    /* to store w, h, dx and dy fro all components and resolutions*/
-    OPJ_UINT32 * l_tmp_data;
-    OPJ_UINT32 ** l_tmp_ptr;
-
-    /* encoding prameters to set*/
-    OPJ_UINT32 l_max_res;
-    OPJ_UINT32 l_max_prec;
-    OPJ_INT32 l_tx0, l_tx1, l_ty0, l_ty1;
-    OPJ_UINT32 l_dx_min, l_dy_min;
-    OPJ_UINT32 l_bound;
-    OPJ_UINT32 l_step_p, l_step_c, l_step_r, l_step_l ;
-    OPJ_UINT32 l_data_stride;
-
-    /* pointers*/
-    opj_pi_iterator_t *l_pi = 00;
-    opj_tcp_t *l_tcp = 00;
-    const opj_tccp_t *l_tccp = 00;
-    opj_pi_comp_t *l_current_comp = 00;
-    opj_image_comp_t * l_img_comp = 00;
-    opj_pi_iterator_t * l_current_pi = 00;
-    OPJ_UINT32 * l_encoding_value_ptr = 00;
-
-    /* preconditions in debug*/
-    assert(p_cp != 00);
-    assert(p_image != 00);
-    assert(p_tile_no < p_cp->tw * p_cp->th);
-
-    /* initializations*/
-    l_tcp = &p_cp->tcps[p_tile_no];
-    l_bound = l_tcp->numpocs + 1;
-
-    l_data_stride = 4 * OPJ_J2K_MAXRLVLS;
-    l_tmp_data = (OPJ_UINT32*)opj_malloc(
-                     l_data_stride * numcomps * sizeof(OPJ_UINT32));
-    if (! l_tmp_data) {
-        return 00;
-    }
-
-    l_tmp_ptr = (OPJ_UINT32**)opj_malloc(
-                    numcomps * sizeof(OPJ_UINT32 *));
-    if (! l_tmp_ptr) {
-        opj_free(l_tmp_data);
-        return 00;
-    }
-
-    /* memory allocation for pi*/
-    l_pi = opj_pi_create(p_image, p_cp, p_tile_no);
-    if (!l_pi) {
-        opj_free(l_tmp_data);
-        opj_free(l_tmp_ptr);
-        return 00;
-    }
-
-    l_encoding_value_ptr = l_tmp_data;
-    /* update pointer array*/
-    for (compno = 0; compno < numcomps; ++compno) {
-        l_tmp_ptr[compno] = l_encoding_value_ptr;
-        l_encoding_value_ptr += l_data_stride;
-    }
-
-    /* get encoding parameters*/
-    opj_get_all_encoding_parameters(p_image, p_cp, p_tile_no, &l_tx0, &l_tx1,
-                                    &l_ty0, &l_ty1, &l_dx_min, &l_dy_min, &l_max_prec, &l_max_res, l_tmp_ptr);
-
-    /* step calculations*/
-    l_step_p = 1;
-    l_step_c = l_max_prec * l_step_p;
-    l_step_r = numcomps * l_step_c;
-    l_step_l = l_max_res * l_step_r;
-
-    /* set values for first packet iterator*/
-    l_pi->tp_on = (OPJ_BYTE)p_cp->m_specific_param.m_enc.m_tp_on;
-    l_current_pi = l_pi;
-
-    /* memory allocation for include*/
-    l_current_pi->include_size = l_tcp->numlayers * l_step_l;
-    l_current_pi->include = (OPJ_INT16*) opj_calloc(l_current_pi->include_size,
-                            sizeof(OPJ_INT16));
-    if (!l_current_pi->include) {
-        opj_free(l_tmp_data);
-        opj_free(l_tmp_ptr);
-        opj_pi_destroy(l_pi, l_bound);
-        return 00;
-    }
-
-    /* special treatment for the first packet iterator*/
-    l_current_comp = l_current_pi->comps;
-    l_img_comp = p_image->comps;
-    l_tccp = l_tcp->tccps;
-    l_current_pi->tx0 = l_tx0;
-    l_current_pi->ty0 = l_ty0;
-    l_current_pi->tx1 = l_tx1;
-    l_current_pi->ty1 = l_ty1;
-    l_current_pi->dx = l_dx_min;
-    l_current_pi->dy = l_dy_min;
-    l_current_pi->step_p = l_step_p;
-    l_current_pi->step_c = l_step_c;
-    l_current_pi->step_r = l_step_r;
-    l_current_pi->step_l = l_step_l;
-
-    /* allocation for components and number of components has already been calculated by opj_pi_create */
-    for (compno = 0; compno < numcomps; ++compno) {
-        opj_pi_resolution_t *l_res = l_current_comp->resolutions;
-        l_encoding_value_ptr = l_tmp_ptr[compno];
-
-        l_current_comp->dx = l_img_comp->dx;
-        l_current_comp->dy = l_img_comp->dy;
-
-        /* resolutions have already been initialized */
-        for (resno = 0; resno < l_current_comp->numresolutions; resno++) {
-            l_res->pdx = *(l_encoding_value_ptr++);
-            l_res->pdy = *(l_encoding_value_ptr++);
-            l_res->pw =  *(l_encoding_value_ptr++);
-            l_res->ph =  *(l_encoding_value_ptr++);
-            ++l_res;
-        }
-
-        ++l_current_comp;
-        ++l_img_comp;
-        ++l_tccp;
-    }
-    ++l_current_pi;
-
-    for (pino = 1 ; pino < l_bound ; ++pino) {
-        l_current_comp = l_current_pi->comps;
-        l_img_comp = p_image->comps;
-        l_tccp = l_tcp->tccps;
-
-        l_current_pi->tx0 = l_tx0;
-        l_current_pi->ty0 = l_ty0;
-        l_current_pi->tx1 = l_tx1;
-        l_current_pi->ty1 = l_ty1;
-        l_current_pi->dx = l_dx_min;
-        l_current_pi->dy = l_dy_min;
-        l_current_pi->step_p = l_step_p;
-        l_current_pi->step_c = l_step_c;
-        l_current_pi->step_r = l_step_r;
-        l_current_pi->step_l = l_step_l;
-
-        /* allocation for components and number of components has already been calculated by opj_pi_create */
-        for (compno = 0; compno < numcomps; ++compno) {
-            opj_pi_resolution_t *l_res = l_current_comp->resolutions;
-            l_encoding_value_ptr = l_tmp_ptr[compno];
-
-            l_current_comp->dx = l_img_comp->dx;
-            l_current_comp->dy = l_img_comp->dy;
-            /* resolutions have already been initialized */
-            for (resno = 0; resno < l_current_comp->numresolutions; resno++) {
-                l_res->pdx = *(l_encoding_value_ptr++);
-                l_res->pdy = *(l_encoding_value_ptr++);
-                l_res->pw =  *(l_encoding_value_ptr++);
-                l_res->ph =  *(l_encoding_value_ptr++);
-                ++l_res;
-            }
-            ++l_current_comp;
-            ++l_img_comp;
-            ++l_tccp;
-        }
-
-        /* special treatment*/
-        l_current_pi->include = (l_current_pi - 1)->include;
-        l_current_pi->include_size = (l_current_pi - 1)->include_size;
-        ++l_current_pi;
-    }
-
-    opj_free(l_tmp_data);
-    l_tmp_data = 00;
-    opj_free(l_tmp_ptr);
-    l_tmp_ptr = 00;
-
-    if (l_tcp->POC && (OPJ_IS_CINEMA(p_cp->rsiz) || p_t2_mode == FINAL_PASS)) {
-        opj_pi_update_encode_poc_and_final(p_cp, p_tile_no, l_tx0, l_tx1, l_ty0, l_ty1,
-                                           l_max_prec, l_max_res, l_dx_min, l_dy_min);
-    } else {
-        opj_pi_update_encode_not_poc(p_cp, numcomps, p_tile_no, l_tx0, l_tx1,
-                                     l_ty0, l_ty1, l_max_prec, l_max_res, l_dx_min, l_dy_min);
-    }
-
-    return l_pi;
-}
-
-void opj_pi_create_encode(opj_pi_iterator_t *pi,
-                          opj_cp_t *cp,
-                          OPJ_UINT32 tileno,
-                          OPJ_UINT32 pino,
-                          OPJ_UINT32 tpnum,
-                          OPJ_INT32 tppos,
-                          J2K_T2_MODE t2_mode)
-{
-    const OPJ_CHAR *prog;
-    OPJ_INT32 i;
-    OPJ_UINT32 incr_top = 1, resetX = 0;
-    opj_tcp_t *tcps = &cp->tcps[tileno];
-    opj_poc_t *tcp = &tcps->pocs[pino];
-
-    prog = opj_j2k_convert_progression_order(tcp->prg);
-
-    pi[pino].first = 1;
-    pi[pino].poc.prg = tcp->prg;
-
-    if (!(cp->m_specific_param.m_enc.m_tp_on && ((!OPJ_IS_CINEMA(cp->rsiz) &&
-            (t2_mode == FINAL_PASS)) || OPJ_IS_CINEMA(cp->rsiz)))) {
-        pi[pino].poc.resno0 = tcp->resS;
-        pi[pino].poc.resno1 = tcp->resE;
-        pi[pino].poc.compno0 = tcp->compS;
-        pi[pino].poc.compno1 = tcp->compE;
-        pi[pino].poc.layno0 = tcp->layS;
-        pi[pino].poc.layno1 = tcp->layE;
-        pi[pino].poc.precno0 = tcp->prcS;
-        pi[pino].poc.precno1 = tcp->prcE;
-        pi[pino].poc.tx0 = (OPJ_INT32)tcp->txS;
-        pi[pino].poc.ty0 = (OPJ_INT32)tcp->tyS;
-        pi[pino].poc.tx1 = (OPJ_INT32)tcp->txE;
-        pi[pino].poc.ty1 = (OPJ_INT32)tcp->tyE;
-    } else {
-        for (i = tppos + 1; i < 4; i++) {
-            switch (prog[i]) {
-            case 'R':
-                pi[pino].poc.resno0 = tcp->resS;
-                pi[pino].poc.resno1 = tcp->resE;
-                break;
-            case 'C':
-                pi[pino].poc.compno0 = tcp->compS;
-                pi[pino].poc.compno1 = tcp->compE;
-                break;
-            case 'L':
-                pi[pino].poc.layno0 = tcp->layS;
-                pi[pino].poc.layno1 = tcp->layE;
-                break;
-            case 'P':
-                switch (tcp->prg) {
-                case OPJ_LRCP:
-                case OPJ_RLCP:
-                    pi[pino].poc.precno0 = tcp->prcS;
-                    pi[pino].poc.precno1 = tcp->prcE;
-                    break;
-                default:
-                    pi[pino].poc.tx0 = (OPJ_INT32)tcp->txS;
-                    pi[pino].poc.ty0 = (OPJ_INT32)tcp->tyS;
-                    pi[pino].poc.tx1 = (OPJ_INT32)tcp->txE;
-                    pi[pino].poc.ty1 = (OPJ_INT32)tcp->tyE;
-                    break;
-                }
-                break;
-            }
-        }
-
-        if (tpnum == 0) {
-            for (i = tppos; i >= 0; i--) {
-                switch (prog[i]) {
-                case 'C':
-                    tcp->comp_t = tcp->compS;
-                    pi[pino].poc.compno0 = tcp->comp_t;
-                    pi[pino].poc.compno1 = tcp->comp_t + 1;
-                    tcp->comp_t += 1;
-                    break;
-                case 'R':
-                    tcp->res_t = tcp->resS;
-                    pi[pino].poc.resno0 = tcp->res_t;
-                    pi[pino].poc.resno1 = tcp->res_t + 1;
-                    tcp->res_t += 1;
-                    break;
-                case 'L':
-                    tcp->lay_t = tcp->layS;
-                    pi[pino].poc.layno0 = tcp->lay_t;
-                    pi[pino].poc.layno1 = tcp->lay_t + 1;
-                    tcp->lay_t += 1;
-                    break;
-                case 'P':
-                    switch (tcp->prg) {
-                    case OPJ_LRCP:
-                    case OPJ_RLCP:
-                        tcp->prc_t = tcp->prcS;
-                        pi[pino].poc.precno0 = tcp->prc_t;
-                        pi[pino].poc.precno1 = tcp->prc_t + 1;
-                        tcp->prc_t += 1;
-                        break;
-                    default:
-                        tcp->tx0_t = tcp->txS;
-                        tcp->ty0_t = tcp->tyS;
-                        pi[pino].poc.tx0 = (OPJ_INT32)tcp->tx0_t;
-                        pi[pino].poc.tx1 = (OPJ_INT32)(tcp->tx0_t + tcp->dx - (tcp->tx0_t % tcp->dx));
-                        pi[pino].poc.ty0 = (OPJ_INT32)tcp->ty0_t;
-                        pi[pino].poc.ty1 = (OPJ_INT32)(tcp->ty0_t + tcp->dy - (tcp->ty0_t % tcp->dy));
-                        tcp->tx0_t = (OPJ_UINT32)pi[pino].poc.tx1;
-                        tcp->ty0_t = (OPJ_UINT32)pi[pino].poc.ty1;
-                        break;
-                    }
-                    break;
-                }
-            }
-            incr_top = 1;
-        } else {
-            for (i = tppos; i >= 0; i--) {
-                switch (prog[i]) {
-                case 'C':
-                    pi[pino].poc.compno0 = tcp->comp_t - 1;
-                    pi[pino].poc.compno1 = tcp->comp_t;
-                    break;
-                case 'R':
-                    pi[pino].poc.resno0 = tcp->res_t - 1;
-                    pi[pino].poc.resno1 = tcp->res_t;
-                    break;
-                case 'L':
-                    pi[pino].poc.layno0 = tcp->lay_t - 1;
-                    pi[pino].poc.layno1 = tcp->lay_t;
-                    break;
-                case 'P':
-                    switch (tcp->prg) {
-                    case OPJ_LRCP:
-                    case OPJ_RLCP:
-                        pi[pino].poc.precno0 = tcp->prc_t - 1;
-                        pi[pino].poc.precno1 = tcp->prc_t;
-                        break;
-                    default:
-                        pi[pino].poc.tx0 = (OPJ_INT32)(tcp->tx0_t - tcp->dx - (tcp->tx0_t % tcp->dx));
-                        pi[pino].poc.tx1 = (OPJ_INT32)tcp->tx0_t ;
-                        pi[pino].poc.ty0 = (OPJ_INT32)(tcp->ty0_t - tcp->dy - (tcp->ty0_t % tcp->dy));
-                        pi[pino].poc.ty1 = (OPJ_INT32)tcp->ty0_t ;
-                        break;
-                    }
-                    break;
-                }
-                if (incr_top == 1) {
-                    switch (prog[i]) {
-                    case 'R':
-                        if (tcp->res_t == tcp->resE) {
-                            if (opj_pi_check_next_level(i - 1, cp, tileno, pino, prog)) {
-                                tcp->res_t = tcp->resS;
-                                pi[pino].poc.resno0 = tcp->res_t;
-                                pi[pino].poc.resno1 = tcp->res_t + 1;
-                                tcp->res_t += 1;
-                                incr_top = 1;
-                            } else {
-                                incr_top = 0;
-                            }
-                        } else {
-                            pi[pino].poc.resno0 = tcp->res_t;
-                            pi[pino].poc.resno1 = tcp->res_t + 1;
-                            tcp->res_t += 1;
-                            incr_top = 0;
-                        }
-                        break;
-                    case 'C':
-                        if (tcp->comp_t == tcp->compE) {
-                            if (opj_pi_check_next_level(i - 1, cp, tileno, pino, prog)) {
-                                tcp->comp_t = tcp->compS;
-                                pi[pino].poc.compno0 = tcp->comp_t;
-                                pi[pino].poc.compno1 = tcp->comp_t + 1;
-                                tcp->comp_t += 1;
-                                incr_top = 1;
-                            } else {
-                                incr_top = 0;
-                            }
-                        } else {
-                            pi[pino].poc.compno0 = tcp->comp_t;
-                            pi[pino].poc.compno1 = tcp->comp_t + 1;
-                            tcp->comp_t += 1;
-                            incr_top = 0;
-                        }
-                        break;
-                    case 'L':
-                        if (tcp->lay_t == tcp->layE) {
-                            if (opj_pi_check_next_level(i - 1, cp, tileno, pino, prog)) {
-                                tcp->lay_t = tcp->layS;
-                                pi[pino].poc.layno0 = tcp->lay_t;
-                                pi[pino].poc.layno1 = tcp->lay_t + 1;
-                                tcp->lay_t += 1;
-                                incr_top = 1;
-                            } else {
-                                incr_top = 0;
-                            }
-                        } else {
-                            pi[pino].poc.layno0 = tcp->lay_t;
-                            pi[pino].poc.layno1 = tcp->lay_t + 1;
-                            tcp->lay_t += 1;
-                            incr_top = 0;
-                        }
-                        break;
-                    case 'P':
-                        switch (tcp->prg) {
-                        case OPJ_LRCP:
-                        case OPJ_RLCP:
-                            if (tcp->prc_t == tcp->prcE) {
-                                if (opj_pi_check_next_level(i - 1, cp, tileno, pino, prog)) {
-                                    tcp->prc_t = tcp->prcS;
-                                    pi[pino].poc.precno0 = tcp->prc_t;
-                                    pi[pino].poc.precno1 = tcp->prc_t + 1;
-                                    tcp->prc_t += 1;
-                                    incr_top = 1;
-                                } else {
-                                    incr_top = 0;
-                                }
-                            } else {
-                                pi[pino].poc.precno0 = tcp->prc_t;
-                                pi[pino].poc.precno1 = tcp->prc_t + 1;
-                                tcp->prc_t += 1;
-                                incr_top = 0;
-                            }
-                            break;
-                        default:
-                            if (tcp->tx0_t >= tcp->txE) {
-                                if (tcp->ty0_t >= tcp->tyE) {
-                                    if (opj_pi_check_next_level(i - 1, cp, tileno, pino, prog)) {
-                                        tcp->ty0_t = tcp->tyS;
-                                        pi[pino].poc.ty0 = (OPJ_INT32)tcp->ty0_t;
-                                        pi[pino].poc.ty1 = (OPJ_INT32)(tcp->ty0_t + tcp->dy - (tcp->ty0_t % tcp->dy));
-                                        tcp->ty0_t = (OPJ_UINT32)pi[pino].poc.ty1;
-                                        incr_top = 1;
-                                        resetX = 1;
-                                    } else {
-                                        incr_top = 0;
-                                        resetX = 0;
-                                    }
-                                } else {
-                                    pi[pino].poc.ty0 = (OPJ_INT32)tcp->ty0_t;
-                                    pi[pino].poc.ty1 = (OPJ_INT32)(tcp->ty0_t + tcp->dy - (tcp->ty0_t % tcp->dy));
-                                    tcp->ty0_t = (OPJ_UINT32)pi[pino].poc.ty1;
-                                    incr_top = 0;
-                                    resetX = 1;
-                                }
-                                if (resetX == 1) {
-                                    tcp->tx0_t = tcp->txS;
-                                    pi[pino].poc.tx0 = (OPJ_INT32)tcp->tx0_t;
-                                    pi[pino].poc.tx1 = (OPJ_INT32)(tcp->tx0_t + tcp->dx - (tcp->tx0_t % tcp->dx));
-                                    tcp->tx0_t = (OPJ_UINT32)pi[pino].poc.tx1;
-                                }
-                            } else {
-                                pi[pino].poc.tx0 = (OPJ_INT32)tcp->tx0_t;
-                                pi[pino].poc.tx1 = (OPJ_INT32)(tcp->tx0_t + tcp->dx - (tcp->tx0_t % tcp->dx));
-                                tcp->tx0_t = (OPJ_UINT32)pi[pino].poc.tx1;
-                                incr_top = 0;
-                            }
-                            break;
-                        }
-                        break;
-                    }
-                }
-            }
-        }
-    }
-}
-
-void opj_pi_destroy(opj_pi_iterator_t *p_pi,
-                    OPJ_UINT32 p_nb_elements)
-{
-    OPJ_UINT32 compno, pino;
-    opj_pi_iterator_t *l_current_pi = p_pi;
-    if (p_pi) {
-        if (p_pi->include) {
-            opj_free(p_pi->include);
-            p_pi->include = 00;
-        }
-        for (pino = 0; pino < p_nb_elements; ++pino) {
-            if (l_current_pi->comps) {
-                opj_pi_comp_t *l_current_component = l_current_pi->comps;
-                for (compno = 0; compno < l_current_pi->numcomps; compno++) {
-                    if (l_current_component->resolutions) {
-                        opj_free(l_current_component->resolutions);
-                        l_current_component->resolutions = 00;
-                    }
-
-                    ++l_current_component;
-                }
-                opj_free(l_current_pi->comps);
-                l_current_pi->comps = 0;
-            }
-            ++l_current_pi;
-        }
-        opj_free(p_pi);
-    }
-}
-
-
-
-void opj_pi_update_encoding_parameters(const opj_image_t *p_image,
-                                       opj_cp_t *p_cp,
-                                       OPJ_UINT32 p_tile_no)
-{
-    /* encoding parameters to set */
-    OPJ_UINT32 l_max_res;
-    OPJ_UINT32 l_max_prec;
-    OPJ_INT32 l_tx0, l_tx1, l_ty0, l_ty1;
-    OPJ_UINT32 l_dx_min, l_dy_min;
-
-    /* pointers */
-    opj_tcp_t *l_tcp = 00;
-
-    /* preconditions */
-    assert(p_cp != 00);
-    assert(p_image != 00);
-    assert(p_tile_no < p_cp->tw * p_cp->th);
-
-    l_tcp = &(p_cp->tcps[p_tile_no]);
-
-    /* get encoding parameters */
-    opj_get_encoding_parameters(p_image, p_cp, p_tile_no, &l_tx0, &l_tx1, &l_ty0,
-                                &l_ty1, &l_dx_min, &l_dy_min, &l_max_prec, &l_max_res);
-
-    if (l_tcp->POC) {
-        opj_pi_update_encode_poc_and_final(p_cp, p_tile_no, l_tx0, l_tx1, l_ty0, l_ty1,
-                                           l_max_prec, l_max_res, l_dx_min, l_dy_min);
-    } else {
-        opj_pi_update_encode_not_poc(p_cp, p_image->numcomps, p_tile_no, l_tx0, l_tx1,
-                                     l_ty0, l_ty1, l_max_prec, l_max_res, l_dx_min, l_dy_min);
-    }
-}
-
-OPJ_BOOL opj_pi_next(opj_pi_iterator_t * pi)
-{
-    switch (pi->poc.prg) {
-    case OPJ_LRCP:
-        return opj_pi_next_lrcp(pi);
-    case OPJ_RLCP:
-        return opj_pi_next_rlcp(pi);
-    case OPJ_RPCL:
-        return opj_pi_next_rpcl(pi);
-    case OPJ_PCRL:
-        return opj_pi_next_pcrl(pi);
-    case OPJ_CPRL:
-        return opj_pi_next_cprl(pi);
-    case OPJ_PROG_UNKNOWN:
-        return OPJ_FALSE;
-    }
-
-    return OPJ_FALSE;
-}
diff --git a/third_party/libopenjpeg20/pi.h b/third_party/libopenjpeg20/pi.h
deleted file mode 100644
index 8c0dc25..0000000
--- a/third_party/libopenjpeg20/pi.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef OPJ_PI_H
-#define OPJ_PI_H
-/**
-@file pi.h
-@brief Implementation of a packet iterator (PI)
-
-The functions in PI.C have for goal to realize a packet iterator that permits to get the next
-packet following the progression order and change of it. The functions in PI.C are used
-by some function in T2.C.
-*/
-
-/** @defgroup PI PI - Implementation of a packet iterator */
-/*@{*/
-
-/**
-FIXME DOC
-*/
-typedef struct opj_pi_resolution {
-    OPJ_UINT32 pdx, pdy;
-    OPJ_UINT32 pw, ph;
-} opj_pi_resolution_t;
-
-/**
-FIXME DOC
-*/
-typedef struct opj_pi_comp {
-    OPJ_UINT32 dx, dy;
-    /** number of resolution levels */
-    OPJ_UINT32 numresolutions;
-    opj_pi_resolution_t *resolutions;
-} opj_pi_comp_t;
-
-/**
-Packet iterator
-*/
-typedef struct opj_pi_iterator {
-    /** Enabling Tile part generation*/
-    OPJ_BYTE tp_on;
-    /** precise if the packet has been already used (useful for progression order change) */
-    OPJ_INT16 *include;
-    /** Number of elements in include array */
-    OPJ_UINT32 include_size;
-    /** layer step used to localize the packet in the include vector */
-    OPJ_UINT32 step_l;
-    /** resolution step used to localize the packet in the include vector */
-    OPJ_UINT32 step_r;
-    /** component step used to localize the packet in the include vector */
-    OPJ_UINT32 step_c;
-    /** precinct step used to localize the packet in the include vector */
-    OPJ_UINT32 step_p;
-    /** component that identify the packet */
-    OPJ_UINT32 compno;
-    /** resolution that identify the packet */
-    OPJ_UINT32 resno;
-    /** precinct that identify the packet */
-    OPJ_UINT32 precno;
-    /** layer that identify the packet */
-    OPJ_UINT32 layno;
-    /** 0 if the first packet */
-    OPJ_BOOL first;
-    /** progression order change information */
-    opj_poc_t poc;
-    /** number of components in the image */
-    OPJ_UINT32 numcomps;
-    /** Components*/
-    opj_pi_comp_t *comps;
-    /** FIXME DOC*/
-    OPJ_INT32 tx0, ty0, tx1, ty1;
-    /** FIXME DOC*/
-    OPJ_INT32 x, y;
-    /** FIXME DOC*/
-    OPJ_UINT32 dx, dy;
-} opj_pi_iterator_t;
-
-/** @name Exported functions */
-/*@{*/
-/* ----------------------------------------------------------------------- */
-/**
- * Creates a packet iterator for encoding.
- *
- * @param   image       the image being encoded.
- * @param   cp      the coding parameters.
- * @param   tileno  index of the tile being encoded.
- * @param   t2_mode the type of pass for generating the packet iterator
- *
- * @return  a list of packet iterator that points to the first packet of the tile (not true).
-*/
-opj_pi_iterator_t *opj_pi_initialise_encode(const opj_image_t *image,
-        opj_cp_t *cp,
-        OPJ_UINT32 tileno,
-        J2K_T2_MODE t2_mode);
-
-/**
- * Updates the encoding parameters of the codec.
- *
- * @param   p_image     the image being encoded.
- * @param   p_cp        the coding parameters.
- * @param   p_tile_no   index of the tile being encoded.
-*/
-void opj_pi_update_encoding_parameters(const opj_image_t *p_image,
-                                       opj_cp_t *p_cp,
-                                       OPJ_UINT32 p_tile_no);
-
-/**
-Modify the packet iterator for enabling tile part generation
-@param pi Handle to the packet iterator generated in pi_initialise_encode
-@param cp Coding parameters
-@param tileno Number that identifies the tile for which to list the packets
-@param pino   FIXME DOC
-@param tpnum Tile part number of the current tile
-@param tppos The position of the tile part flag in the progression order
-@param t2_mode FIXME DOC
-*/
-void opj_pi_create_encode(opj_pi_iterator_t *pi,
-                          opj_cp_t *cp,
-                          OPJ_UINT32 tileno,
-                          OPJ_UINT32 pino,
-                          OPJ_UINT32 tpnum,
-                          OPJ_INT32 tppos,
-                          J2K_T2_MODE t2_mode);
-
-/**
-Create a packet iterator for Decoder
-@param image Raw image for which the packets will be listed
-@param cp Coding parameters
-@param tileno Number that identifies the tile for which to list the packets
-@return Returns a packet iterator that points to the first packet of the tile
-@see opj_pi_destroy
-*/
-opj_pi_iterator_t *opj_pi_create_decode(opj_image_t * image,
-                                        opj_cp_t * cp,
-                                        OPJ_UINT32 tileno);
-/**
- * Destroys a packet iterator array.
- *
- * @param   p_pi            the packet iterator array to destroy.
- * @param   p_nb_elements   the number of elements in the array.
- */
-void opj_pi_destroy(opj_pi_iterator_t *p_pi,
-                    OPJ_UINT32 p_nb_elements);
-
-/**
-Modify the packet iterator to point to the next packet
-@param pi Packet iterator to modify
-@return Returns false if pi pointed to the last packet or else returns true
-*/
-OPJ_BOOL opj_pi_next(opj_pi_iterator_t * pi);
-/* ----------------------------------------------------------------------- */
-/*@}*/
-
-/*@}*/
-
-#endif /* OPJ_PI_H */
diff --git a/third_party/libopenjpeg20/sparse_array.c b/third_party/libopenjpeg20/sparse_array.c
deleted file mode 100644
index 7319292..0000000
--- a/third_party/libopenjpeg20/sparse_array.c
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2017, IntoPix SA <contact@intopix.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "opj_includes.h"
-
-
-struct opj_sparse_array_int32 {
-    OPJ_UINT32 width;
-    OPJ_UINT32 height;
-    OPJ_UINT32 block_width;
-    OPJ_UINT32 block_height;
-    OPJ_UINT32 block_count_hor;
-    OPJ_UINT32 block_count_ver;
-    OPJ_INT32** data_blocks;
-};
-
-opj_sparse_array_int32_t* opj_sparse_array_int32_create(OPJ_UINT32 width,
-        OPJ_UINT32 height,
-        OPJ_UINT32 block_width,
-        OPJ_UINT32 block_height)
-{
-    opj_sparse_array_int32_t* sa;
-
-    if (width == 0 || height == 0 || block_width == 0 || block_height == 0) {
-        return NULL;
-    }
-    if (block_width > ((OPJ_UINT32)~0U) / block_height / sizeof(OPJ_INT32)) {
-        return NULL;
-    }
-
-    sa = (opj_sparse_array_int32_t*) opj_calloc(1,
-            sizeof(opj_sparse_array_int32_t));
-    sa->width = width;
-    sa->height = height;
-    sa->block_width = block_width;
-    sa->block_height = block_height;
-    sa->block_count_hor = opj_uint_ceildiv(width, block_width);
-    sa->block_count_ver = opj_uint_ceildiv(height, block_height);
-    if (sa->block_count_hor > ((OPJ_UINT32)~0U) / sa->block_count_ver) {
-        opj_free(sa);
-        return NULL;
-    }
-    sa->data_blocks = (OPJ_INT32**) opj_calloc(sizeof(OPJ_INT32*),
-                      sa->block_count_hor * sa->block_count_ver);
-    if (sa->data_blocks == NULL) {
-        opj_free(sa);
-        return NULL;
-    }
-
-    return sa;
-}
-
-void opj_sparse_array_int32_free(opj_sparse_array_int32_t* sa)
-{
-    if (sa) {
-        OPJ_UINT32 i;
-        for (i = 0; i < sa->block_count_hor * sa->block_count_ver; i++) {
-            if (sa->data_blocks[i]) {
-                opj_free(sa->data_blocks[i]);
-            }
-        }
-        opj_free(sa->data_blocks);
-        opj_free(sa);
-    }
-}
-
-OPJ_BOOL opj_sparse_array_is_region_valid(const opj_sparse_array_int32_t* sa,
-        OPJ_UINT32 x0,
-        OPJ_UINT32 y0,
-        OPJ_UINT32 x1,
-        OPJ_UINT32 y1)
-{
-    return !(x0 >= sa->width || x1 <= x0 || x1 > sa->width ||
-             y0 >= sa->height || y1 <= y0 || y1 > sa->height);
-}
-
-static OPJ_BOOL opj_sparse_array_int32_read_or_write(
-    const opj_sparse_array_int32_t* sa,
-    OPJ_UINT32 x0,
-    OPJ_UINT32 y0,
-    OPJ_UINT32 x1,
-    OPJ_UINT32 y1,
-    OPJ_INT32* buf,
-    OPJ_UINT32 buf_col_stride,
-    OPJ_UINT32 buf_line_stride,
-    OPJ_BOOL forgiving,
-    OPJ_BOOL is_read_op)
-{
-    OPJ_UINT32 y, block_y;
-    OPJ_UINT32 y_incr = 0;
-    const OPJ_UINT32 block_width = sa->block_width;
-
-    if (!opj_sparse_array_is_region_valid(sa, x0, y0, x1, y1)) {
-        return forgiving;
-    }
-
-    block_y = y0 / sa->block_height;
-    for (y = y0; y < y1; block_y ++, y += y_incr) {
-        OPJ_UINT32 x, block_x;
-        OPJ_UINT32 x_incr = 0;
-        OPJ_UINT32 block_y_offset;
-        y_incr = (y == y0) ? sa->block_height - (y0 % sa->block_height) :
-                 sa->block_height;
-        block_y_offset = sa->block_height - y_incr;
-        y_incr = opj_uint_min(y_incr, y1 - y);
-        block_x = x0 / block_width;
-        for (x = x0; x < x1; block_x ++, x += x_incr) {
-            OPJ_UINT32 j;
-            OPJ_UINT32 block_x_offset;
-            OPJ_INT32* src_block;
-            x_incr = (x == x0) ? block_width - (x0 % block_width) : block_width;
-            block_x_offset = block_width - x_incr;
-            x_incr = opj_uint_min(x_incr, x1 - x);
-            src_block = sa->data_blocks[block_y * sa->block_count_hor + block_x];
-            if (is_read_op) {
-                if (src_block == NULL) {
-                    if (buf_col_stride == 1) {
-                        OPJ_INT32* dest_ptr = buf + (y - y0) * (OPJ_SIZE_T)buf_line_stride +
-                                              (x - x0) * buf_col_stride;
-                        for (j = 0; j < y_incr; j++) {
-                            memset(dest_ptr, 0, sizeof(OPJ_INT32) * x_incr);
-                            dest_ptr += buf_line_stride;
-                        }
-                    } else {
-                        OPJ_INT32* dest_ptr = buf + (y - y0) * (OPJ_SIZE_T)buf_line_stride +
-                                              (x - x0) * buf_col_stride;
-                        for (j = 0; j < y_incr; j++) {
-                            OPJ_UINT32 k;
-                            for (k = 0; k < x_incr; k++) {
-                                dest_ptr[k * buf_col_stride] = 0;
-                            }
-                            dest_ptr += buf_line_stride;
-                        }
-                    }
-                } else {
-                    const OPJ_INT32* OPJ_RESTRICT src_ptr = src_block + block_y_offset *
-                                                            (OPJ_SIZE_T)block_width + block_x_offset;
-                    if (buf_col_stride == 1) {
-                        OPJ_INT32* OPJ_RESTRICT dest_ptr = buf + (y - y0) * (OPJ_SIZE_T)buf_line_stride
-                                                           +
-                                                           (x - x0) * buf_col_stride;
-                        if (x_incr == 4) {
-                            /* Same code as general branch, but the compiler */
-                            /* can have an efficient memcpy() */
-                            (void)(x_incr); /* trick to silent cppcheck duplicateBranch warning */
-                            for (j = 0; j < y_incr; j++) {
-                                memcpy(dest_ptr, src_ptr, sizeof(OPJ_INT32) * x_incr);
-                                dest_ptr += buf_line_stride;
-                                src_ptr += block_width;
-                            }
-                        } else {
-                            for (j = 0; j < y_incr; j++) {
-                                memcpy(dest_ptr, src_ptr, sizeof(OPJ_INT32) * x_incr);
-                                dest_ptr += buf_line_stride;
-                                src_ptr += block_width;
-                            }
-                        }
-                    } else {
-                        OPJ_INT32* OPJ_RESTRICT dest_ptr = buf + (y - y0) * (OPJ_SIZE_T)buf_line_stride
-                                                           +
-                                                           (x - x0) * buf_col_stride;
-                        if (x_incr == 1) {
-                            for (j = 0; j < y_incr; j++) {
-                                *dest_ptr = *src_ptr;
-                                dest_ptr += buf_line_stride;
-                                src_ptr += block_width;
-                            }
-                        } else if (y_incr == 1 && buf_col_stride == 2) {
-                            OPJ_UINT32 k;
-                            for (k = 0; k < (x_incr & ~3U); k += 4) {
-                                dest_ptr[k * buf_col_stride] = src_ptr[k];
-                                dest_ptr[(k + 1) * buf_col_stride] = src_ptr[k + 1];
-                                dest_ptr[(k + 2) * buf_col_stride] = src_ptr[k + 2];
-                                dest_ptr[(k + 3) * buf_col_stride] = src_ptr[k + 3];
-                            }
-                            for (; k < x_incr; k++) {
-                                dest_ptr[k * buf_col_stride] = src_ptr[k];
-                            }
-                        } else if (x_incr >= 8 && buf_col_stride == 8) {
-                            for (j = 0; j < y_incr; j++) {
-                                OPJ_UINT32 k;
-                                for (k = 0; k < (x_incr & ~3U); k += 4) {
-                                    dest_ptr[k * buf_col_stride] = src_ptr[k];
-                                    dest_ptr[(k + 1) * buf_col_stride] = src_ptr[k + 1];
-                                    dest_ptr[(k + 2) * buf_col_stride] = src_ptr[k + 2];
-                                    dest_ptr[(k + 3) * buf_col_stride] = src_ptr[k + 3];
-                                }
-                                for (; k < x_incr; k++) {
-                                    dest_ptr[k * buf_col_stride] = src_ptr[k];
-                                }
-                                dest_ptr += buf_line_stride;
-                                src_ptr += block_width;
-                            }
-                        } else {
-                            /* General case */
-                            for (j = 0; j < y_incr; j++) {
-                                OPJ_UINT32 k;
-                                for (k = 0; k < x_incr; k++) {
-                                    dest_ptr[k * buf_col_stride] = src_ptr[k];
-                                }
-                                dest_ptr += buf_line_stride;
-                                src_ptr += block_width;
-                            }
-                        }
-                    }
-                }
-            } else {
-                if (src_block == NULL) {
-                    src_block = (OPJ_INT32*) opj_calloc(1,
-                                                        sa->block_width * sa->block_height * sizeof(OPJ_INT32));
-                    if (src_block == NULL) {
-                        return OPJ_FALSE;
-                    }
-                    sa->data_blocks[block_y * sa->block_count_hor + block_x] = src_block;
-                }
-
-                if (buf_col_stride == 1) {
-                    OPJ_INT32* OPJ_RESTRICT dest_ptr = src_block + block_y_offset *
-                                                       (OPJ_SIZE_T)block_width + block_x_offset;
-                    const OPJ_INT32* OPJ_RESTRICT src_ptr = buf + (y - y0) *
-                                                            (OPJ_SIZE_T)buf_line_stride + (x - x0) * buf_col_stride;
-                    if (x_incr == 4) {
-                        /* Same code as general branch, but the compiler */
-                        /* can have an efficient memcpy() */
-                        (void)(x_incr); /* trick to silent cppcheck duplicateBranch warning */
-                        for (j = 0; j < y_incr; j++) {
-                            memcpy(dest_ptr, src_ptr, sizeof(OPJ_INT32) * x_incr);
-                            dest_ptr += block_width;
-                            src_ptr += buf_line_stride;
-                        }
-                    } else {
-                        for (j = 0; j < y_incr; j++) {
-                            memcpy(dest_ptr, src_ptr, sizeof(OPJ_INT32) * x_incr);
-                            dest_ptr += block_width;
-                            src_ptr += buf_line_stride;
-                        }
-                    }
-                } else {
-                    OPJ_INT32* OPJ_RESTRICT dest_ptr = src_block + block_y_offset *
-                                                       (OPJ_SIZE_T)block_width + block_x_offset;
-                    const OPJ_INT32* OPJ_RESTRICT src_ptr = buf + (y - y0) *
-                                                            (OPJ_SIZE_T)buf_line_stride + (x - x0) * buf_col_stride;
-                    if (x_incr == 1) {
-                        for (j = 0; j < y_incr; j++) {
-                            *dest_ptr = *src_ptr;
-                            src_ptr += buf_line_stride;
-                            dest_ptr += block_width;
-                        }
-                    } else if (x_incr >= 8 && buf_col_stride == 8) {
-                        for (j = 0; j < y_incr; j++) {
-                            OPJ_UINT32 k;
-                            for (k = 0; k < (x_incr & ~3U); k += 4) {
-                                dest_ptr[k] = src_ptr[k * buf_col_stride];
-                                dest_ptr[k + 1] = src_ptr[(k + 1) * buf_col_stride];
-                                dest_ptr[k + 2] = src_ptr[(k + 2) * buf_col_stride];
-                                dest_ptr[k + 3] = src_ptr[(k + 3) * buf_col_stride];
-                            }
-                            for (; k < x_incr; k++) {
-                                dest_ptr[k] = src_ptr[k * buf_col_stride];
-                            }
-                            src_ptr += buf_line_stride;
-                            dest_ptr += block_width;
-                        }
-                    } else {
-                        /* General case */
-                        for (j = 0; j < y_incr; j++) {
-                            OPJ_UINT32 k;
-                            for (k = 0; k < x_incr; k++) {
-                                dest_ptr[k] = src_ptr[k * buf_col_stride];
-                            }
-                            src_ptr += buf_line_stride;
-                            dest_ptr += block_width;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_sparse_array_int32_read(const opj_sparse_array_int32_t* sa,
-                                     OPJ_UINT32 x0,
-                                     OPJ_UINT32 y0,
-                                     OPJ_UINT32 x1,
-                                     OPJ_UINT32 y1,
-                                     OPJ_INT32* dest,
-                                     OPJ_UINT32 dest_col_stride,
-                                     OPJ_UINT32 dest_line_stride,
-                                     OPJ_BOOL forgiving)
-{
-    return opj_sparse_array_int32_read_or_write(
-               (opj_sparse_array_int32_t*)sa, x0, y0, x1, y1,
-               dest,
-               dest_col_stride,
-               dest_line_stride,
-               forgiving,
-               OPJ_TRUE);
-}
-
-OPJ_BOOL opj_sparse_array_int32_write(opj_sparse_array_int32_t* sa,
-                                      OPJ_UINT32 x0,
-                                      OPJ_UINT32 y0,
-                                      OPJ_UINT32 x1,
-                                      OPJ_UINT32 y1,
-                                      const OPJ_INT32* src,
-                                      OPJ_UINT32 src_col_stride,
-                                      OPJ_UINT32 src_line_stride,
-                                      OPJ_BOOL forgiving)
-{
-    return opj_sparse_array_int32_read_or_write(sa, x0, y0, x1, y1,
-            (OPJ_INT32*)src,
-            src_col_stride,
-            src_line_stride,
-            forgiving,
-            OPJ_FALSE);
-}
diff --git a/third_party/libopenjpeg20/t1.c b/third_party/libopenjpeg20/t1.c
deleted file mode 100644
index f6f7671..0000000
--- a/third_party/libopenjpeg20/t1.c
+++ /dev/null
@@ -1,2428 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2007, Callum Lerwick <seg@haxxed.com>
- * Copyright (c) 2012, Carl Hetherington
- * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#define OPJ_SKIP_POISON
-#include "opj_includes.h"
-
-#ifdef __SSE__
-#include <xmmintrin.h>
-#endif
-#ifdef __SSE2__
-#include <emmintrin.h>
-#endif
-
-#if defined(__GNUC__)
-#pragma GCC poison malloc calloc realloc free
-#endif
-
-#include "t1_luts.h"
-
-/** @defgroup T1 T1 - Implementation of the tier-1 coding */
-/*@{*/
-
-#define T1_FLAGS(x, y) (t1->flags[x + 1 + ((y / 4) + 1) * (t1->w+2)])
-
-#define opj_t1_setcurctx(curctx, ctxno)  curctx = &(mqc)->ctxs[(OPJ_UINT32)(ctxno)]
-
-/** @name Local static functions */
-/*@{*/
-
-static INLINE OPJ_BYTE opj_t1_getctxno_zc(opj_mqc_t *mqc, OPJ_UINT32 f);
-static INLINE OPJ_UINT32 opj_t1_getctxno_mag(OPJ_UINT32 f);
-static OPJ_INT16 opj_t1_getnmsedec_sig(OPJ_UINT32 x, OPJ_UINT32 bitpos);
-static OPJ_INT16 opj_t1_getnmsedec_ref(OPJ_UINT32 x, OPJ_UINT32 bitpos);
-static INLINE void opj_t1_update_flags(opj_flag_t *flagsp, OPJ_UINT32 ci,
-                                       OPJ_UINT32 s, OPJ_UINT32 stride,
-                                       OPJ_UINT32 vsc);
-
-
-/**
-Decode significant pass
-*/
-
-static INLINE void opj_t1_dec_sigpass_step_raw(
-    opj_t1_t *t1,
-    opj_flag_t *flagsp,
-    OPJ_INT32 *datap,
-    OPJ_INT32 oneplushalf,
-    OPJ_UINT32 vsc,
-    OPJ_UINT32 row);
-static INLINE void opj_t1_dec_sigpass_step_mqc(
-    opj_t1_t *t1,
-    opj_flag_t *flagsp,
-    OPJ_INT32 *datap,
-    OPJ_INT32 oneplushalf,
-    OPJ_UINT32 row,
-    OPJ_UINT32 flags_stride,
-    OPJ_UINT32 vsc);
-
-/**
-Encode significant pass
-*/
-static void opj_t1_enc_sigpass(opj_t1_t *t1,
-                               OPJ_INT32 bpno,
-                               OPJ_INT32 *nmsedec,
-                               OPJ_BYTE type,
-                               OPJ_UINT32 cblksty);
-
-/**
-Decode significant pass
-*/
-static void opj_t1_dec_sigpass_raw(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno,
-    OPJ_INT32 cblksty);
-
-/**
-Encode refinement pass
-*/
-static void opj_t1_enc_refpass(opj_t1_t *t1,
-                               OPJ_INT32 bpno,
-                               OPJ_INT32 *nmsedec,
-                               OPJ_BYTE type);
-
-/**
-Decode refinement pass
-*/
-static void opj_t1_dec_refpass_raw(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno);
-
-
-/**
-Decode refinement pass
-*/
-
-static INLINE void  opj_t1_dec_refpass_step_raw(
-    opj_t1_t *t1,
-    opj_flag_t *flagsp,
-    OPJ_INT32 *datap,
-    OPJ_INT32 poshalf,
-    OPJ_UINT32 row);
-static INLINE void opj_t1_dec_refpass_step_mqc(
-    opj_t1_t *t1,
-    opj_flag_t *flagsp,
-    OPJ_INT32 *datap,
-    OPJ_INT32 poshalf,
-    OPJ_UINT32 row);
-
-
-/**
-Decode clean-up pass
-*/
-
-static void opj_t1_dec_clnpass_step(
-    opj_t1_t *t1,
-    opj_flag_t *flagsp,
-    OPJ_INT32 *datap,
-    OPJ_INT32 oneplushalf,
-    OPJ_UINT32 row,
-    OPJ_UINT32 vsc);
-
-/**
-Encode clean-up pass
-*/
-static void opj_t1_enc_clnpass(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno,
-    OPJ_INT32 *nmsedec,
-    OPJ_UINT32 cblksty);
-
-static OPJ_FLOAT64 opj_t1_getwmsedec(
-    OPJ_INT32 nmsedec,
-    OPJ_UINT32 compno,
-    OPJ_UINT32 level,
-    OPJ_UINT32 orient,
-    OPJ_INT32 bpno,
-    OPJ_UINT32 qmfbid,
-    OPJ_FLOAT64 stepsize,
-    OPJ_UINT32 numcomps,
-    const OPJ_FLOAT64 * mct_norms,
-    OPJ_UINT32 mct_numcomps);
-
-static void opj_t1_encode_cblk(opj_t1_t *t1,
-                               opj_tcd_cblk_enc_t* cblk,
-                               OPJ_UINT32 orient,
-                               OPJ_UINT32 compno,
-                               OPJ_UINT32 level,
-                               OPJ_UINT32 qmfbid,
-                               OPJ_FLOAT64 stepsize,
-                               OPJ_UINT32 cblksty,
-                               OPJ_UINT32 numcomps,
-                               opj_tcd_tile_t * tile,
-                               const OPJ_FLOAT64 * mct_norms,
-                               OPJ_UINT32 mct_numcomps);
-
-/**
-Decode 1 code-block
-@param t1 T1 handle
-@param cblk Code-block coding parameters
-@param orient
-@param roishift Region of interest shifting value
-@param cblksty Code-block style
-@param p_manager the event manager
-@param p_manager_mutex mutex for the event manager
-@param check_pterm whether PTERM correct termination should be checked
-*/
-static OPJ_BOOL opj_t1_decode_cblk(opj_t1_t *t1,
-                                   opj_tcd_cblk_dec_t* cblk,
-                                   OPJ_UINT32 orient,
-                                   OPJ_UINT32 roishift,
-                                   OPJ_UINT32 cblksty,
-                                   opj_event_mgr_t *p_manager,
-                                   opj_mutex_t* p_manager_mutex,
-                                   OPJ_BOOL check_pterm);
-
-static OPJ_BOOL opj_t1_allocate_buffers(opj_t1_t *t1,
-                                        OPJ_UINT32 w,
-                                        OPJ_UINT32 h);
-
-/*@}*/
-
-/*@}*/
-
-/* ----------------------------------------------------------------------- */
-
-static INLINE OPJ_BYTE opj_t1_getctxno_zc(opj_mqc_t *mqc, OPJ_UINT32 f)
-{
-    return mqc->lut_ctxno_zc_orient[(f & T1_SIGMA_NEIGHBOURS)];
-}
-
-static INLINE OPJ_UINT32 opj_t1_getctxtno_sc_or_spb_index(OPJ_UINT32 fX,
-        OPJ_UINT32 pfX,
-        OPJ_UINT32 nfX,
-        OPJ_UINT32 ci)
-{
-    /*
-      0 pfX T1_CHI_THIS           T1_LUT_SGN_W
-      1 tfX T1_SIGMA_1            T1_LUT_SIG_N
-      2 nfX T1_CHI_THIS           T1_LUT_SGN_E
-      3 tfX T1_SIGMA_3            T1_LUT_SIG_W
-      4  fX T1_CHI_(THIS - 1)     T1_LUT_SGN_N
-      5 tfX T1_SIGMA_5            T1_LUT_SIG_E
-      6  fX T1_CHI_(THIS + 1)     T1_LUT_SGN_S
-      7 tfX T1_SIGMA_7            T1_LUT_SIG_S
-    */
-
-    OPJ_UINT32 lu = (fX >> (ci * 3U)) & (T1_SIGMA_1 | T1_SIGMA_3 | T1_SIGMA_5 |
-                                         T1_SIGMA_7);
-
-    lu |= (pfX >> (T1_CHI_THIS_I      + (ci * 3U))) & (1U << 0);
-    lu |= (nfX >> (T1_CHI_THIS_I - 2U + (ci * 3U))) & (1U << 2);
-    if (ci == 0U) {
-        lu |= (fX >> (T1_CHI_0_I - 4U)) & (1U << 4);
-    } else {
-        lu |= (fX >> (T1_CHI_1_I - 4U + ((ci - 1U) * 3U))) & (1U << 4);
-    }
-    lu |= (fX >> (T1_CHI_2_I - 6U + (ci * 3U))) & (1U << 6);
-    return lu;
-}
-
-static INLINE OPJ_BYTE opj_t1_getctxno_sc(OPJ_UINT32 lu)
-{
-    return lut_ctxno_sc[lu];
-}
-
-static INLINE OPJ_UINT32 opj_t1_getctxno_mag(OPJ_UINT32 f)
-{
-    OPJ_UINT32 tmp = (f & T1_SIGMA_NEIGHBOURS) ? T1_CTXNO_MAG + 1 : T1_CTXNO_MAG;
-    OPJ_UINT32 tmp2 = (f & T1_MU_0) ? T1_CTXNO_MAG + 2 : tmp;
-    return tmp2;
-}
-
-static INLINE OPJ_BYTE opj_t1_getspb(OPJ_UINT32 lu)
-{
-    return lut_spb[lu];
-}
-
-static OPJ_INT16 opj_t1_getnmsedec_sig(OPJ_UINT32 x, OPJ_UINT32 bitpos)
-{
-    if (bitpos > 0) {
-        return lut_nmsedec_sig[(x >> (bitpos)) & ((1 << T1_NMSEDEC_BITS) - 1)];
-    }
-
-    return lut_nmsedec_sig0[x & ((1 << T1_NMSEDEC_BITS) - 1)];
-}
-
-static OPJ_INT16 opj_t1_getnmsedec_ref(OPJ_UINT32 x, OPJ_UINT32 bitpos)
-{
-    if (bitpos > 0) {
-        return lut_nmsedec_ref[(x >> (bitpos)) & ((1 << T1_NMSEDEC_BITS) - 1)];
-    }
-
-    return lut_nmsedec_ref0[x & ((1 << T1_NMSEDEC_BITS) - 1)];
-}
-
-#define opj_t1_update_flags_macro(flags, flagsp, ci, s, stride, vsc) \
-{ \
-    /* east */ \
-    flagsp[-1] |= T1_SIGMA_5 << (3U * ci); \
- \
-    /* mark target as significant */ \
-    flags |= ((s << T1_CHI_1_I) | T1_SIGMA_4) << (3U * ci); \
- \
-    /* west */ \
-    flagsp[1] |= T1_SIGMA_3 << (3U * ci); \
- \
-    /* north-west, north, north-east */ \
-    if (ci == 0U && !(vsc)) { \
-        opj_flag_t* north = flagsp - (stride); \
-        *north |= (s << T1_CHI_5_I) | T1_SIGMA_16; \
-        north[-1] |= T1_SIGMA_17; \
-        north[1] |= T1_SIGMA_15; \
-    } \
- \
-    /* south-west, south, south-east */ \
-    if (ci == 3U) { \
-        opj_flag_t* south = flagsp + (stride); \
-        *south |= (s << T1_CHI_0_I) | T1_SIGMA_1; \
-        south[-1] |= T1_SIGMA_2; \
-        south[1] |= T1_SIGMA_0; \
-    } \
-}
-
-
-static INLINE void opj_t1_update_flags(opj_flag_t *flagsp, OPJ_UINT32 ci,
-                                       OPJ_UINT32 s, OPJ_UINT32 stride,
-                                       OPJ_UINT32 vsc)
-{
-    opj_t1_update_flags_macro(*flagsp, flagsp, ci, s, stride, vsc);
-}
-
-/**
-Encode significant pass
-*/
-static INLINE void opj_t1_enc_sigpass_step(opj_t1_t *t1,
-        opj_flag_t *flagsp,
-        OPJ_INT32 *datap,
-        OPJ_INT32 bpno,
-        OPJ_INT32 one,
-        OPJ_INT32 *nmsedec,
-        OPJ_BYTE type,
-        OPJ_UINT32 ci,
-        OPJ_UINT32 vsc)
-{
-    OPJ_UINT32 v;
-
-    opj_mqc_t *mqc = &(t1->mqc);   /* MQC component */
-
-    OPJ_UINT32 const flags = *flagsp;
-
-    if ((flags & ((T1_SIGMA_THIS | T1_PI_THIS) << (ci * 3U))) == 0U &&
-            (flags & (T1_SIGMA_NEIGHBOURS << (ci * 3U))) != 0U) {
-        OPJ_UINT32 ctxt1 = opj_t1_getctxno_zc(mqc, flags >> (ci * 3U));
-        v = (opj_int_abs(*datap) & one) ? 1 : 0;
-#ifdef DEBUG_ENC_SIG
-        fprintf(stderr, "   ctxt1=%d\n", ctxt1);
-#endif
-        opj_mqc_setcurctx(mqc, ctxt1);
-        if (type == T1_TYPE_RAW) {  /* BYPASS/LAZY MODE */
-            opj_mqc_bypass_enc(mqc, v);
-        } else {
-            opj_mqc_encode(mqc, v);
-        }
-        if (v) {
-            OPJ_UINT32 lu = opj_t1_getctxtno_sc_or_spb_index(
-                                *flagsp,
-                                flagsp[-1], flagsp[1],
-                                ci);
-            OPJ_UINT32 ctxt2 = opj_t1_getctxno_sc(lu);
-            v = *datap < 0 ? 1U : 0U;
-            *nmsedec += opj_t1_getnmsedec_sig((OPJ_UINT32)opj_int_abs(*datap),
-                                              (OPJ_UINT32)bpno);
-#ifdef DEBUG_ENC_SIG
-            fprintf(stderr, "   ctxt2=%d\n", ctxt2);
-#endif
-            opj_mqc_setcurctx(mqc, ctxt2);
-            if (type == T1_TYPE_RAW) {  /* BYPASS/LAZY MODE */
-                opj_mqc_bypass_enc(mqc, v);
-            } else {
-                OPJ_UINT32 spb = opj_t1_getspb(lu);
-#ifdef DEBUG_ENC_SIG
-                fprintf(stderr, "   spb=%d\n", spb);
-#endif
-                opj_mqc_encode(mqc, v ^ spb);
-            }
-            opj_t1_update_flags(flagsp, ci, v, t1->w + 2, vsc);
-        }
-        *flagsp |= T1_PI_THIS << (ci * 3U);
-    }
-}
-
-static INLINE void opj_t1_dec_sigpass_step_raw(
-    opj_t1_t *t1,
-    opj_flag_t *flagsp,
-    OPJ_INT32 *datap,
-    OPJ_INT32 oneplushalf,
-    OPJ_UINT32 vsc,
-    OPJ_UINT32 ci)
-{
-    OPJ_UINT32 v;
-    opj_mqc_t *mqc = &(t1->mqc);       /* RAW component */
-
-    OPJ_UINT32 const flags = *flagsp;
-
-    if ((flags & ((T1_SIGMA_THIS | T1_PI_THIS) << (ci * 3U))) == 0U &&
-            (flags & (T1_SIGMA_NEIGHBOURS << (ci * 3U))) != 0U) {
-        if (opj_mqc_raw_decode(mqc)) {
-            v = opj_mqc_raw_decode(mqc);
-            *datap = v ? -oneplushalf : oneplushalf;
-            opj_t1_update_flags(flagsp, ci, v, t1->w + 2, vsc);
-        }
-        *flagsp |= T1_PI_THIS << (ci * 3U);
-    }
-}
-
-#define opj_t1_dec_sigpass_step_mqc_macro(flags, flagsp, flags_stride, data, \
-                                          data_stride, ci, mqc, curctx, \
-                                          v, a, c, ct, oneplushalf, vsc) \
-{ \
-    if ((flags & ((T1_SIGMA_THIS | T1_PI_THIS) << (ci * 3U))) == 0U && \
-        (flags & (T1_SIGMA_NEIGHBOURS << (ci * 3U))) != 0U) { \
-        OPJ_UINT32 ctxt1 = opj_t1_getctxno_zc(mqc, flags >> (ci * 3U)); \
-        opj_t1_setcurctx(curctx, ctxt1); \
-        opj_mqc_decode_macro(v, mqc, curctx, a, c, ct); \
-        if (v) { \
-            OPJ_UINT32 lu = opj_t1_getctxtno_sc_or_spb_index( \
-                                flags, \
-                                flagsp[-1], flagsp[1], \
-                                ci); \
-            OPJ_UINT32 ctxt2 = opj_t1_getctxno_sc(lu); \
-            OPJ_UINT32 spb = opj_t1_getspb(lu); \
-            opj_t1_setcurctx(curctx, ctxt2); \
-            opj_mqc_decode_macro(v, mqc, curctx, a, c, ct); \
-            v = v ^ spb; \
-            data[ci*data_stride] = v ? -oneplushalf : oneplushalf; \
-            opj_t1_update_flags_macro(flags, flagsp, ci, v, flags_stride, vsc); \
-        } \
-        flags |= T1_PI_THIS << (ci * 3U); \
-    } \
-}
-
-static INLINE void opj_t1_dec_sigpass_step_mqc(
-    opj_t1_t *t1,
-    opj_flag_t *flagsp,
-    OPJ_INT32 *datap,
-    OPJ_INT32 oneplushalf,
-    OPJ_UINT32 ci,
-    OPJ_UINT32 flags_stride,
-    OPJ_UINT32 vsc)
-{
-    OPJ_UINT32 v;
-
-    opj_mqc_t *mqc = &(t1->mqc);       /* MQC component */
-    opj_t1_dec_sigpass_step_mqc_macro(*flagsp, flagsp, flags_stride, datap,
-                                      0, ci, mqc, mqc->curctx,
-                                      v, mqc->a, mqc->c, mqc->ct, oneplushalf, vsc);
-}
-
-static void opj_t1_enc_sigpass(opj_t1_t *t1,
-                               OPJ_INT32 bpno,
-                               OPJ_INT32 *nmsedec,
-                               OPJ_BYTE type,
-                               OPJ_UINT32 cblksty
-                              )
-{
-    OPJ_UINT32 i, k;
-    OPJ_INT32 const one = 1 << (bpno + T1_NMSEDEC_FRACBITS);
-    opj_flag_t* f = &T1_FLAGS(0, 0);
-    OPJ_UINT32 const extra = 2;
-
-    *nmsedec = 0;
-#ifdef DEBUG_ENC_SIG
-    fprintf(stderr, "enc_sigpass: bpno=%d\n", bpno);
-#endif
-    for (k = 0; k < (t1->h & ~3U); k += 4) {
-#ifdef DEBUG_ENC_SIG
-        fprintf(stderr, " k=%d\n", k);
-#endif
-        for (i = 0; i < t1->w; ++i) {
-#ifdef DEBUG_ENC_SIG
-            fprintf(stderr, " i=%d\n", i);
-#endif
-            if (*f == 0U) {
-                /* Nothing to do for any of the 4 data points */
-                f++;
-                continue;
-            }
-            opj_t1_enc_sigpass_step(
-                t1,
-                f,
-                &t1->data[((k + 0) * t1->data_stride) + i],
-                bpno,
-                one,
-                nmsedec,
-                type,
-                0, cblksty & J2K_CCP_CBLKSTY_VSC);
-            opj_t1_enc_sigpass_step(
-                t1,
-                f,
-                &t1->data[((k + 1) * t1->data_stride) + i],
-                bpno,
-                one,
-                nmsedec,
-                type,
-                1, 0);
-            opj_t1_enc_sigpass_step(
-                t1,
-                f,
-                &t1->data[((k + 2) * t1->data_stride) + i],
-                bpno,
-                one,
-                nmsedec,
-                type,
-                2, 0);
-            opj_t1_enc_sigpass_step(
-                t1,
-                f,
-                &t1->data[((k + 3) * t1->data_stride) + i],
-                bpno,
-                one,
-                nmsedec,
-                type,
-                3, 0);
-            ++f;
-        }
-        f += extra;
-    }
-
-    if (k < t1->h) {
-        OPJ_UINT32 j;
-#ifdef DEBUG_ENC_SIG
-        fprintf(stderr, " k=%d\n", k);
-#endif
-        for (i = 0; i < t1->w; ++i) {
-#ifdef DEBUG_ENC_SIG
-            fprintf(stderr, " i=%d\n", i);
-#endif
-            if (*f == 0U) {
-                /* Nothing to do for any of the 4 data points */
-                f++;
-                continue;
-            }
-            for (j = k; j < t1->h; ++j) {
-                opj_t1_enc_sigpass_step(
-                    t1,
-                    f,
-                    &t1->data[(j * t1->data_stride) + i],
-                    bpno,
-                    one,
-                    nmsedec,
-                    type,
-                    j - k,
-                    (j == k && (cblksty & J2K_CCP_CBLKSTY_VSC) != 0));
-            }
-            ++f;
-        }
-    }
-}
-
-static void opj_t1_dec_sigpass_raw(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno,
-    OPJ_INT32 cblksty)
-{
-    OPJ_INT32 one, half, oneplushalf;
-    OPJ_UINT32 i, j, k;
-    OPJ_INT32 *data = t1->data;
-    opj_flag_t *flagsp = &T1_FLAGS(0, 0);
-    const OPJ_UINT32 l_w = t1->w;
-    one = 1 << bpno;
-    half = one >> 1;
-    oneplushalf = one | half;
-
-    for (k = 0; k < (t1->h & ~3U); k += 4, flagsp += 2, data += 3 * l_w) {
-        for (i = 0; i < l_w; ++i, ++flagsp, ++data) {
-            opj_flag_t flags = *flagsp;
-            if (flags != 0) {
-                opj_t1_dec_sigpass_step_raw(
-                    t1,
-                    flagsp,
-                    data,
-                    oneplushalf,
-                    cblksty & J2K_CCP_CBLKSTY_VSC, /* vsc */
-                    0U);
-                opj_t1_dec_sigpass_step_raw(
-                    t1,
-                    flagsp,
-                    data + l_w,
-                    oneplushalf,
-                    OPJ_FALSE, /* vsc */
-                    1U);
-                opj_t1_dec_sigpass_step_raw(
-                    t1,
-                    flagsp,
-                    data + 2 * l_w,
-                    oneplushalf,
-                    OPJ_FALSE, /* vsc */
-                    2U);
-                opj_t1_dec_sigpass_step_raw(
-                    t1,
-                    flagsp,
-                    data + 3 * l_w,
-                    oneplushalf,
-                    OPJ_FALSE, /* vsc */
-                    3U);
-            }
-        }
-    }
-    if (k < t1->h) {
-        for (i = 0; i < l_w; ++i, ++flagsp, ++data) {
-            for (j = 0; j < t1->h - k; ++j) {
-                opj_t1_dec_sigpass_step_raw(
-                    t1,
-                    flagsp,
-                    data + j * l_w,
-                    oneplushalf,
-                    cblksty & J2K_CCP_CBLKSTY_VSC, /* vsc */
-                    j);
-            }
-        }
-    }
-}
-
-#define opj_t1_dec_sigpass_mqc_internal(t1, bpno, vsc, w, h, flags_stride) \
-{ \
-        OPJ_INT32 one, half, oneplushalf; \
-        OPJ_UINT32 i, j, k; \
-        register OPJ_INT32 *data = t1->data; \
-        register opj_flag_t *flagsp = &t1->flags[(flags_stride) + 1]; \
-        const OPJ_UINT32 l_w = w; \
-        opj_mqc_t* mqc = &(t1->mqc); \
-        DOWNLOAD_MQC_VARIABLES(mqc, curctx, c, a, ct); \
-        register OPJ_UINT32 v; \
-        one = 1 << bpno; \
-        half = one >> 1; \
-        oneplushalf = one | half; \
-        for (k = 0; k < (h & ~3u); k += 4, data += 3*l_w, flagsp += 2) { \
-                for (i = 0; i < l_w; ++i, ++data, ++flagsp) { \
-                        opj_flag_t flags = *flagsp; \
-                        if( flags != 0 ) { \
-                            opj_t1_dec_sigpass_step_mqc_macro( \
-                                flags, flagsp, flags_stride, data, \
-                                l_w, 0, mqc, curctx, v, a, c, ct, oneplushalf, vsc); \
-                            opj_t1_dec_sigpass_step_mqc_macro( \
-                                flags, flagsp, flags_stride, data, \
-                                l_w, 1, mqc, curctx, v, a, c, ct, oneplushalf, OPJ_FALSE); \
-                            opj_t1_dec_sigpass_step_mqc_macro( \
-                                flags, flagsp, flags_stride, data, \
-                                l_w, 2, mqc, curctx, v, a, c, ct, oneplushalf, OPJ_FALSE); \
-                            opj_t1_dec_sigpass_step_mqc_macro( \
-                                flags, flagsp, flags_stride, data, \
-                                l_w, 3, mqc, curctx, v, a, c, ct, oneplushalf, OPJ_FALSE); \
-                            *flagsp = flags; \
-                        } \
-                } \
-        } \
-        UPLOAD_MQC_VARIABLES(mqc, curctx, c, a, ct); \
-        if( k < h ) { \
-            for (i = 0; i < l_w; ++i, ++data, ++flagsp) { \
-                for (j = 0; j < h - k; ++j) { \
-                        opj_t1_dec_sigpass_step_mqc(t1, flagsp, \
-                            data + j * l_w, oneplushalf, j, flags_stride, vsc); \
-                } \
-            } \
-        } \
-}
-
-static void opj_t1_dec_sigpass_mqc_64x64_novsc(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno)
-{
-    opj_t1_dec_sigpass_mqc_internal(t1, bpno, OPJ_FALSE, 64, 64, 66);
-}
-
-static void opj_t1_dec_sigpass_mqc_64x64_vsc(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno)
-{
-    opj_t1_dec_sigpass_mqc_internal(t1, bpno, OPJ_TRUE, 64, 64, 66);
-}
-
-static void opj_t1_dec_sigpass_mqc_generic_novsc(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno)
-{
-    opj_t1_dec_sigpass_mqc_internal(t1, bpno, OPJ_FALSE, t1->w, t1->h,
-                                    t1->w + 2U);
-}
-
-static void opj_t1_dec_sigpass_mqc_generic_vsc(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno)
-{
-    opj_t1_dec_sigpass_mqc_internal(t1, bpno, OPJ_TRUE, t1->w, t1->h,
-                                    t1->w + 2U);
-}
-
-static void opj_t1_dec_sigpass_mqc(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno,
-    OPJ_INT32 cblksty)
-{
-    if (t1->w == 64 && t1->h == 64) {
-        if (cblksty & J2K_CCP_CBLKSTY_VSC) {
-            opj_t1_dec_sigpass_mqc_64x64_vsc(t1, bpno);
-        } else {
-            opj_t1_dec_sigpass_mqc_64x64_novsc(t1, bpno);
-        }
-    } else {
-        if (cblksty & J2K_CCP_CBLKSTY_VSC) {
-            opj_t1_dec_sigpass_mqc_generic_vsc(t1, bpno);
-        } else {
-            opj_t1_dec_sigpass_mqc_generic_novsc(t1, bpno);
-        }
-    }
-}
-
-/**
-Encode refinement pass step
-*/
-static INLINE void opj_t1_enc_refpass_step(opj_t1_t *t1,
-        opj_flag_t *flagsp,
-        OPJ_INT32 *datap,
-        OPJ_INT32 bpno,
-        OPJ_INT32 one,
-        OPJ_INT32 *nmsedec,
-        OPJ_BYTE type,
-        OPJ_UINT32 ci)
-{
-    OPJ_UINT32 v;
-
-    opj_mqc_t *mqc = &(t1->mqc);   /* MQC component */
-
-    OPJ_UINT32 const shift_flags =
-        (*flagsp >> (ci * 3U));
-
-    if ((shift_flags & (T1_SIGMA_THIS | T1_PI_THIS)) == T1_SIGMA_THIS) {
-        OPJ_UINT32 ctxt = opj_t1_getctxno_mag(shift_flags);
-        *nmsedec += opj_t1_getnmsedec_ref((OPJ_UINT32)opj_int_abs(*datap),
-                                          (OPJ_UINT32)bpno);
-        v = (opj_int_abs(*datap) & one) ? 1 : 0;
-#ifdef DEBUG_ENC_REF
-        fprintf(stderr, "  ctxt=%d\n", ctxt);
-#endif
-        opj_mqc_setcurctx(mqc, ctxt);
-        if (type == T1_TYPE_RAW) {  /* BYPASS/LAZY MODE */
-            opj_mqc_bypass_enc(mqc, v);
-        } else {
-            opj_mqc_encode(mqc, v);
-        }
-        *flagsp |= T1_MU_THIS << (ci * 3U);
-    }
-}
-
-
-static INLINE void opj_t1_dec_refpass_step_raw(
-    opj_t1_t *t1,
-    opj_flag_t *flagsp,
-    OPJ_INT32 *datap,
-    OPJ_INT32 poshalf,
-    OPJ_UINT32 ci)
-{
-    OPJ_UINT32 v;
-
-    opj_mqc_t *mqc = &(t1->mqc);       /* RAW component */
-
-    if ((*flagsp & ((T1_SIGMA_THIS | T1_PI_THIS) << (ci * 3U))) ==
-            (T1_SIGMA_THIS << (ci * 3U))) {
-        v = opj_mqc_raw_decode(mqc);
-        *datap += (v ^ (*datap < 0)) ? poshalf : -poshalf;
-        *flagsp |= T1_MU_THIS << (ci * 3U);
-    }
-}
-
-#define opj_t1_dec_refpass_step_mqc_macro(flags, data, data_stride, ci, \
-                                          mqc, curctx, v, a, c, ct, poshalf) \
-{ \
-    if ((flags & ((T1_SIGMA_THIS | T1_PI_THIS) << (ci * 3U))) == \
-            (T1_SIGMA_THIS << (ci * 3U))) { \
-        OPJ_UINT32 ctxt = opj_t1_getctxno_mag(flags >> (ci * 3U)); \
-        opj_t1_setcurctx(curctx, ctxt); \
-        opj_mqc_decode_macro(v, mqc, curctx, a, c, ct); \
-        data[ci*data_stride] += (v ^ (data[ci*data_stride] < 0)) ? poshalf : -poshalf; \
-        flags |= T1_MU_THIS << (ci * 3U); \
-    } \
-}
-
-static INLINE void opj_t1_dec_refpass_step_mqc(
-    opj_t1_t *t1,
-    opj_flag_t *flagsp,
-    OPJ_INT32 *datap,
-    OPJ_INT32 poshalf,
-    OPJ_UINT32 ci)
-{
-    OPJ_UINT32 v;
-
-    opj_mqc_t *mqc = &(t1->mqc);       /* MQC component */
-    opj_t1_dec_refpass_step_mqc_macro(*flagsp, datap, 0, ci,
-                                      mqc, mqc->curctx, v, mqc->a, mqc->c,
-                                      mqc->ct, poshalf);
-}
-
-static void opj_t1_enc_refpass(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno,
-    OPJ_INT32 *nmsedec,
-    OPJ_BYTE type)
-{
-    OPJ_UINT32 i, k;
-    const OPJ_INT32 one = 1 << (bpno + T1_NMSEDEC_FRACBITS);
-    opj_flag_t* f = &T1_FLAGS(0, 0);
-    const OPJ_UINT32 extra = 2U;
-
-    *nmsedec = 0;
-#ifdef DEBUG_ENC_REF
-    fprintf(stderr, "enc_refpass: bpno=%d\n", bpno);
-#endif
-    for (k = 0; k < (t1->h & ~3U); k += 4) {
-#ifdef DEBUG_ENC_REF
-        fprintf(stderr, " k=%d\n", k);
-#endif
-        for (i = 0; i < t1->w; ++i) {
-#ifdef DEBUG_ENC_REF
-            fprintf(stderr, " i=%d\n", i);
-#endif
-            if ((*f & (T1_SIGMA_4 | T1_SIGMA_7 | T1_SIGMA_10 | T1_SIGMA_13)) == 0) {
-                /* none significant */
-                f++;
-                continue;
-            }
-            if ((*f & (T1_PI_0 | T1_PI_1 | T1_PI_2 | T1_PI_3)) ==
-                    (T1_PI_0 | T1_PI_1 | T1_PI_2 | T1_PI_3)) {
-                /* all processed by sigpass */
-                f++;
-                continue;
-            }
-
-            opj_t1_enc_refpass_step(
-                t1,
-                f,
-                &t1->data[((k + 0) * t1->data_stride) + i],
-                bpno,
-                one,
-                nmsedec,
-                type,
-                0);
-            opj_t1_enc_refpass_step(
-                t1,
-                f,
-                &t1->data[((k + 1) * t1->data_stride) + i],
-                bpno,
-                one,
-                nmsedec,
-                type,
-                1);
-            opj_t1_enc_refpass_step(
-                t1,
-                f,
-                &t1->data[((k + 2) * t1->data_stride) + i],
-                bpno,
-                one,
-                nmsedec,
-                type,
-                2);
-            opj_t1_enc_refpass_step(
-                t1,
-                f,
-                &t1->data[((k + 3) * t1->data_stride) + i],
-                bpno,
-                one,
-                nmsedec,
-                type,
-                3);
-            ++f;
-        }
-        f += extra;
-    }
-
-    if (k < t1->h) {
-        OPJ_UINT32 j;
-#ifdef DEBUG_ENC_REF
-        fprintf(stderr, " k=%d\n", k);
-#endif
-        for (i = 0; i < t1->w; ++i) {
-#ifdef DEBUG_ENC_REF
-            fprintf(stderr, " i=%d\n", i);
-#endif
-            if ((*f & (T1_SIGMA_4 | T1_SIGMA_7 | T1_SIGMA_10 | T1_SIGMA_13)) == 0) {
-                /* none significant */
-                f++;
-                continue;
-            }
-            for (j = k; j < t1->h; ++j) {
-                opj_t1_enc_refpass_step(
-                    t1,
-                    f,
-                    &t1->data[(j * t1->data_stride) + i],
-                    bpno,
-                    one,
-                    nmsedec,
-                    type,
-                    j - k);
-            }
-            ++f;
-        }
-    }
-}
-
-
-static void opj_t1_dec_refpass_raw(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno)
-{
-    OPJ_INT32 one, poshalf;
-    OPJ_UINT32 i, j, k;
-    OPJ_INT32 *data = t1->data;
-    opj_flag_t *flagsp = &T1_FLAGS(0, 0);
-    const OPJ_UINT32 l_w = t1->w;
-    one = 1 << bpno;
-    poshalf = one >> 1;
-    for (k = 0; k < (t1->h & ~3U); k += 4, flagsp += 2, data += 3 * l_w) {
-        for (i = 0; i < l_w; ++i, ++flagsp, ++data) {
-            opj_flag_t flags = *flagsp;
-            if (flags != 0) {
-                opj_t1_dec_refpass_step_raw(
-                    t1,
-                    flagsp,
-                    data,
-                    poshalf,
-                    0U);
-                opj_t1_dec_refpass_step_raw(
-                    t1,
-                    flagsp,
-                    data + l_w,
-                    poshalf,
-                    1U);
-                opj_t1_dec_refpass_step_raw(
-                    t1,
-                    flagsp,
-                    data + 2 * l_w,
-                    poshalf,
-                    2U);
-                opj_t1_dec_refpass_step_raw(
-                    t1,
-                    flagsp,
-                    data + 3 * l_w,
-                    poshalf,
-                    3U);
-            }
-        }
-    }
-    if (k < t1->h) {
-        for (i = 0; i < l_w; ++i, ++flagsp, ++data) {
-            for (j = 0; j < t1->h - k; ++j) {
-                opj_t1_dec_refpass_step_raw(
-                    t1,
-                    flagsp,
-                    data + j * l_w,
-                    poshalf,
-                    j);
-            }
-        }
-    }
-}
-
-#define opj_t1_dec_refpass_mqc_internal(t1, bpno, w, h, flags_stride) \
-{ \
-        OPJ_INT32 one, poshalf; \
-        OPJ_UINT32 i, j, k; \
-        register OPJ_INT32 *data = t1->data; \
-        register opj_flag_t *flagsp = &t1->flags[flags_stride + 1]; \
-        const OPJ_UINT32 l_w = w; \
-        opj_mqc_t* mqc = &(t1->mqc); \
-        DOWNLOAD_MQC_VARIABLES(mqc, curctx, c, a, ct); \
-        register OPJ_UINT32 v; \
-        one = 1 << bpno; \
-        poshalf = one >> 1; \
-        for (k = 0; k < (h & ~3u); k += 4, data += 3*l_w, flagsp += 2) { \
-                for (i = 0; i < l_w; ++i, ++data, ++flagsp) { \
-                        opj_flag_t flags = *flagsp; \
-                        if( flags != 0 ) { \
-                            opj_t1_dec_refpass_step_mqc_macro( \
-                                flags, data, l_w, 0, \
-                                mqc, curctx, v, a, c, ct, poshalf); \
-                            opj_t1_dec_refpass_step_mqc_macro( \
-                                flags, data, l_w, 1, \
-                                mqc, curctx, v, a, c, ct, poshalf); \
-                            opj_t1_dec_refpass_step_mqc_macro( \
-                                flags, data, l_w, 2, \
-                                mqc, curctx, v, a, c, ct, poshalf); \
-                            opj_t1_dec_refpass_step_mqc_macro( \
-                                flags, data, l_w, 3, \
-                                mqc, curctx, v, a, c, ct, poshalf); \
-                            *flagsp = flags; \
-                        } \
-                } \
-        } \
-        UPLOAD_MQC_VARIABLES(mqc, curctx, c, a, ct); \
-        if( k < h ) { \
-            for (i = 0; i < l_w; ++i, ++data, ++flagsp) { \
-                for (j = 0; j < h - k; ++j) { \
-                        opj_t1_dec_refpass_step_mqc(t1, flagsp, data + j * l_w, poshalf, j); \
-                } \
-            } \
-        } \
-}
-
-static void opj_t1_dec_refpass_mqc_64x64(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno)
-{
-    opj_t1_dec_refpass_mqc_internal(t1, bpno, 64, 64, 66);
-}
-
-static void opj_t1_dec_refpass_mqc_generic(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno)
-{
-    opj_t1_dec_refpass_mqc_internal(t1, bpno, t1->w, t1->h, t1->w + 2U);
-}
-
-static void opj_t1_dec_refpass_mqc(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno)
-{
-    if (t1->w == 64 && t1->h == 64) {
-        opj_t1_dec_refpass_mqc_64x64(t1, bpno);
-    } else {
-        opj_t1_dec_refpass_mqc_generic(t1, bpno);
-    }
-}
-
-/**
-Encode clean-up pass step
-*/
-static void opj_t1_enc_clnpass_step(
-    opj_t1_t *t1,
-    opj_flag_t *flagsp,
-    OPJ_INT32 *datap,
-    OPJ_INT32 bpno,
-    OPJ_INT32 one,
-    OPJ_INT32 *nmsedec,
-    OPJ_UINT32 agg,
-    OPJ_UINT32 runlen,
-    OPJ_UINT32 lim,
-    OPJ_UINT32 cblksty)
-{
-    OPJ_UINT32 v;
-    OPJ_UINT32 ci;
-    opj_mqc_t *mqc = &(t1->mqc);   /* MQC component */
-
-    const OPJ_UINT32 check = (T1_SIGMA_4 | T1_SIGMA_7 | T1_SIGMA_10 | T1_SIGMA_13 |
-                              T1_PI_0 | T1_PI_1 | T1_PI_2 | T1_PI_3);
-
-    if ((*flagsp & check) == check) {
-        if (runlen == 0) {
-            *flagsp &= ~(T1_PI_0 | T1_PI_1 | T1_PI_2 | T1_PI_3);
-        } else if (runlen == 1) {
-            *flagsp &= ~(T1_PI_1 | T1_PI_2 | T1_PI_3);
-        } else if (runlen == 2) {
-            *flagsp &= ~(T1_PI_2 | T1_PI_3);
-        } else if (runlen == 3) {
-            *flagsp &= ~(T1_PI_3);
-        }
-        return;
-    }
-
-    for (ci = runlen; ci < lim; ++ci) {
-        OPJ_UINT32 vsc;
-        opj_flag_t flags;
-        OPJ_UINT32 ctxt1;
-
-        flags = *flagsp;
-
-        if ((agg != 0) && (ci == runlen)) {
-            goto LABEL_PARTIAL;
-        }
-
-        if (!(flags & ((T1_SIGMA_THIS | T1_PI_THIS) << (ci * 3U)))) {
-            ctxt1 = opj_t1_getctxno_zc(mqc, flags >> (ci * 3U));
-#ifdef DEBUG_ENC_CLN
-            printf("   ctxt1=%d\n", ctxt1);
-#endif
-            opj_mqc_setcurctx(mqc, ctxt1);
-            v = (opj_int_abs(*datap) & one) ? 1 : 0;
-            opj_mqc_encode(mqc, v);
-            if (v) {
-                OPJ_UINT32 ctxt2, spb;
-                OPJ_UINT32 lu;
-LABEL_PARTIAL:
-                lu = opj_t1_getctxtno_sc_or_spb_index(
-                         *flagsp,
-                         flagsp[-1], flagsp[1],
-                         ci);
-                *nmsedec += opj_t1_getnmsedec_sig((OPJ_UINT32)opj_int_abs(*datap),
-                                                  (OPJ_UINT32)bpno);
-                ctxt2 = opj_t1_getctxno_sc(lu);
-#ifdef DEBUG_ENC_CLN
-                printf("   ctxt2=%d\n", ctxt2);
-#endif
-                opj_mqc_setcurctx(mqc, ctxt2);
-
-                v = *datap < 0 ? 1U : 0U;
-                spb = opj_t1_getspb(lu);
-#ifdef DEBUG_ENC_CLN
-                printf("   spb=%d\n", spb);
-#endif
-                opj_mqc_encode(mqc, v ^ spb);
-                vsc = ((cblksty & J2K_CCP_CBLKSTY_VSC) && (ci == 0)) ? 1 : 0;
-                opj_t1_update_flags(flagsp, ci, v, t1->w + 2U, vsc);
-            }
-        }
-        *flagsp &= ~(T1_PI_THIS << (3U * ci));
-        datap += t1->data_stride;
-    }
-}
-
-#define opj_t1_dec_clnpass_step_macro(check_flags, partial, \
-                                      flags, flagsp, flags_stride, data, \
-                                      data_stride, ci, mqc, curctx, \
-                                      v, a, c, ct, oneplushalf, vsc) \
-{ \
-    if ( !check_flags || !(flags & ((T1_SIGMA_THIS | T1_PI_THIS) << (ci * 3U)))) {\
-        do { \
-            if( !partial ) { \
-                OPJ_UINT32 ctxt1 = opj_t1_getctxno_zc(mqc, flags >> (ci * 3U)); \
-                opj_t1_setcurctx(curctx, ctxt1); \
-                opj_mqc_decode_macro(v, mqc, curctx, a, c, ct); \
-                if( !v ) \
-                    break; \
-            } \
-            { \
-                OPJ_UINT32 lu = opj_t1_getctxtno_sc_or_spb_index( \
-                                    flags, flagsp[-1], flagsp[1], \
-                                    ci); \
-                opj_t1_setcurctx(curctx, opj_t1_getctxno_sc(lu)); \
-                opj_mqc_decode_macro(v, mqc, curctx, a, c, ct); \
-                v = v ^ opj_t1_getspb(lu); \
-                data[ci*data_stride] = v ? -oneplushalf : oneplushalf; \
-                opj_t1_update_flags_macro(flags, flagsp, ci, v, flags_stride, vsc); \
-            } \
-        } while(0); \
-    } \
-}
-
-static void opj_t1_dec_clnpass_step(
-    opj_t1_t *t1,
-    opj_flag_t *flagsp,
-    OPJ_INT32 *datap,
-    OPJ_INT32 oneplushalf,
-    OPJ_UINT32 ci,
-    OPJ_UINT32 vsc)
-{
-    OPJ_UINT32 v;
-
-    opj_mqc_t *mqc = &(t1->mqc);   /* MQC component */
-    opj_t1_dec_clnpass_step_macro(OPJ_TRUE, OPJ_FALSE,
-                                  *flagsp, flagsp, t1->w + 2U, datap,
-                                  0, ci, mqc, mqc->curctx,
-                                  v, mqc->a, mqc->c, mqc->ct, oneplushalf, vsc);
-}
-
-static void opj_t1_enc_clnpass(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno,
-    OPJ_INT32 *nmsedec,
-    OPJ_UINT32 cblksty)
-{
-    OPJ_UINT32 i, k;
-    const OPJ_INT32 one = 1 << (bpno + T1_NMSEDEC_FRACBITS);
-    OPJ_UINT32 agg, runlen;
-
-    opj_mqc_t *mqc = &(t1->mqc);   /* MQC component */
-
-    *nmsedec = 0;
-#ifdef DEBUG_ENC_CLN
-    printf("enc_clnpass: bpno=%d\n", bpno);
-#endif
-    for (k = 0; k < (t1->h & ~3U); k += 4) {
-#ifdef DEBUG_ENC_CLN
-        printf(" k=%d\n", k);
-#endif
-        for (i = 0; i < t1->w; ++i) {
-#ifdef DEBUG_ENC_CLN
-            printf("  i=%d\n", i);
-#endif
-            agg = !(T1_FLAGS(i, k));
-#ifdef DEBUG_ENC_CLN
-            printf("   agg=%d\n", agg);
-#endif
-            if (agg) {
-                for (runlen = 0; runlen < 4; ++runlen) {
-                    if (opj_int_abs(t1->data[((k + runlen)*t1->data_stride) + i]) & one) {
-                        break;
-                    }
-                }
-                opj_mqc_setcurctx(mqc, T1_CTXNO_AGG);
-                opj_mqc_encode(mqc, runlen != 4);
-                if (runlen == 4) {
-                    continue;
-                }
-                opj_mqc_setcurctx(mqc, T1_CTXNO_UNI);
-                opj_mqc_encode(mqc, runlen >> 1);
-                opj_mqc_encode(mqc, runlen & 1);
-            } else {
-                runlen = 0;
-            }
-            opj_t1_enc_clnpass_step(
-                t1,
-                &T1_FLAGS(i, k),
-                &t1->data[((k + runlen) * t1->data_stride) + i],
-                bpno,
-                one,
-                nmsedec,
-                agg,
-                runlen,
-                4U,
-                cblksty);
-        }
-    }
-    if (k < t1->h) {
-        agg = 0;
-        runlen = 0;
-#ifdef DEBUG_ENC_CLN
-        printf(" k=%d\n", k);
-#endif
-        for (i = 0; i < t1->w; ++i) {
-#ifdef DEBUG_ENC_CLN
-            printf("  i=%d\n", i);
-            printf("   agg=%d\n", agg);
-#endif
-            opj_t1_enc_clnpass_step(
-                t1,
-                &T1_FLAGS(i, k),
-                &t1->data[((k + runlen) * t1->data_stride) + i],
-                bpno,
-                one,
-                nmsedec,
-                agg,
-                runlen,
-                t1->h - k,
-                cblksty);
-        }
-    }
-}
-
-#define opj_t1_dec_clnpass_internal(t1, bpno, vsc, w, h, flags_stride) \
-{ \
-    OPJ_INT32 one, half, oneplushalf; \
-    OPJ_UINT32 runlen; \
-    OPJ_UINT32 i, j, k; \
-    const OPJ_UINT32 l_w = w; \
-    opj_mqc_t* mqc = &(t1->mqc); \
-    register OPJ_INT32 *data = t1->data; \
-    register opj_flag_t *flagsp = &t1->flags[flags_stride + 1]; \
-    DOWNLOAD_MQC_VARIABLES(mqc, curctx, c, a, ct); \
-    register OPJ_UINT32 v; \
-    one = 1 << bpno; \
-    half = one >> 1; \
-    oneplushalf = one | half; \
-    for (k = 0; k < (h & ~3u); k += 4, data += 3*l_w, flagsp += 2) { \
-        for (i = 0; i < l_w; ++i, ++data, ++flagsp) { \
-            opj_flag_t flags = *flagsp; \
-            if (flags == 0) { \
-                OPJ_UINT32 partial = OPJ_TRUE; \
-                opj_t1_setcurctx(curctx, T1_CTXNO_AGG); \
-                opj_mqc_decode_macro(v, mqc, curctx, a, c, ct); \
-                if (!v) { \
-                    continue; \
-                } \
-                opj_t1_setcurctx(curctx, T1_CTXNO_UNI); \
-                opj_mqc_decode_macro(runlen, mqc, curctx, a, c, ct); \
-                opj_mqc_decode_macro(v, mqc, curctx, a, c, ct); \
-                runlen = (runlen << 1) | v; \
-                switch(runlen) { \
-                    case 0: \
-                        opj_t1_dec_clnpass_step_macro(OPJ_FALSE, OPJ_TRUE,\
-                                            flags, flagsp, flags_stride, data, \
-                                            l_w, 0, mqc, curctx, \
-                                            v, a, c, ct, oneplushalf, vsc); \
-                        partial = OPJ_FALSE; \
-                        /* FALLTHRU */ \
-                    case 1: \
-                        opj_t1_dec_clnpass_step_macro(OPJ_FALSE, partial,\
-                                            flags, flagsp, flags_stride, data, \
-                                            l_w, 1, mqc, curctx, \
-                                            v, a, c, ct, oneplushalf, OPJ_FALSE); \
-                        partial = OPJ_FALSE; \
-                        /* FALLTHRU */ \
-                    case 2: \
-                        opj_t1_dec_clnpass_step_macro(OPJ_FALSE, partial,\
-                                            flags, flagsp, flags_stride, data, \
-                                            l_w, 2, mqc, curctx, \
-                                            v, a, c, ct, oneplushalf, OPJ_FALSE); \
-                        partial = OPJ_FALSE; \
-                        /* FALLTHRU */ \
-                    case 3: \
-                        opj_t1_dec_clnpass_step_macro(OPJ_FALSE, partial,\
-                                            flags, flagsp, flags_stride, data, \
-                                            l_w, 3, mqc, curctx, \
-                                            v, a, c, ct, oneplushalf, OPJ_FALSE); \
-                        break; \
-                } \
-            } else { \
-                opj_t1_dec_clnpass_step_macro(OPJ_TRUE, OPJ_FALSE, \
-                                    flags, flagsp, flags_stride, data, \
-                                    l_w, 0, mqc, curctx, \
-                                    v, a, c, ct, oneplushalf, vsc); \
-                opj_t1_dec_clnpass_step_macro(OPJ_TRUE, OPJ_FALSE, \
-                                    flags, flagsp, flags_stride, data, \
-                                    l_w, 1, mqc, curctx, \
-                                    v, a, c, ct, oneplushalf, OPJ_FALSE); \
-                opj_t1_dec_clnpass_step_macro(OPJ_TRUE, OPJ_FALSE, \
-                                    flags, flagsp, flags_stride, data, \
-                                    l_w, 2, mqc, curctx, \
-                                    v, a, c, ct, oneplushalf, OPJ_FALSE); \
-                opj_t1_dec_clnpass_step_macro(OPJ_TRUE, OPJ_FALSE, \
-                                    flags, flagsp, flags_stride, data, \
-                                    l_w, 3, mqc, curctx, \
-                                    v, a, c, ct, oneplushalf, OPJ_FALSE); \
-            } \
-            *flagsp = flags & ~(T1_PI_0 | T1_PI_1 | T1_PI_2 | T1_PI_3); \
-        } \
-    } \
-    UPLOAD_MQC_VARIABLES(mqc, curctx, c, a, ct); \
-    if( k < h ) { \
-        for (i = 0; i < l_w; ++i, ++flagsp, ++data) { \
-            for (j = 0; j < h - k; ++j) { \
-                opj_t1_dec_clnpass_step(t1, flagsp, data + j * l_w, oneplushalf, j, vsc); \
-            } \
-            *flagsp &= ~(T1_PI_0 | T1_PI_1 | T1_PI_2 | T1_PI_3); \
-        } \
-    } \
-}
-
-static void opj_t1_dec_clnpass_check_segsym(opj_t1_t *t1, OPJ_INT32 cblksty)
-{
-    if (cblksty & J2K_CCP_CBLKSTY_SEGSYM) {
-        opj_mqc_t* mqc = &(t1->mqc);
-        OPJ_UINT32 v, v2;
-        opj_mqc_setcurctx(mqc, T1_CTXNO_UNI);
-        opj_mqc_decode(v, mqc);
-        opj_mqc_decode(v2, mqc);
-        v = (v << 1) | v2;
-        opj_mqc_decode(v2, mqc);
-        v = (v << 1) | v2;
-        opj_mqc_decode(v2, mqc);
-        v = (v << 1) | v2;
-        /*
-        if (v!=0xa) {
-            opj_event_msg(t1->cinfo, EVT_WARNING, "Bad segmentation symbol %x\n", v);
-        }
-        */
-    }
-}
-
-static void opj_t1_dec_clnpass_64x64_novsc(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno)
-{
-    opj_t1_dec_clnpass_internal(t1, bpno, OPJ_FALSE, 64, 64, 66);
-}
-
-static void opj_t1_dec_clnpass_64x64_vsc(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno)
-{
-    opj_t1_dec_clnpass_internal(t1, bpno, OPJ_TRUE, 64, 64, 66);
-}
-
-static void opj_t1_dec_clnpass_generic_novsc(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno)
-{
-    opj_t1_dec_clnpass_internal(t1, bpno, OPJ_FALSE, t1->w, t1->h,
-                                t1->w + 2U);
-}
-
-static void opj_t1_dec_clnpass_generic_vsc(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno)
-{
-    opj_t1_dec_clnpass_internal(t1, bpno, OPJ_TRUE, t1->w, t1->h,
-                                t1->w + 2U);
-}
-
-static void opj_t1_dec_clnpass(
-    opj_t1_t *t1,
-    OPJ_INT32 bpno,
-    OPJ_INT32 cblksty)
-{
-    if (t1->w == 64 && t1->h == 64) {
-        if (cblksty & J2K_CCP_CBLKSTY_VSC) {
-            opj_t1_dec_clnpass_64x64_vsc(t1, bpno);
-        } else {
-            opj_t1_dec_clnpass_64x64_novsc(t1, bpno);
-        }
-    } else {
-        if (cblksty & J2K_CCP_CBLKSTY_VSC) {
-            opj_t1_dec_clnpass_generic_vsc(t1, bpno);
-        } else {
-            opj_t1_dec_clnpass_generic_novsc(t1, bpno);
-        }
-    }
-    opj_t1_dec_clnpass_check_segsym(t1, cblksty);
-}
-
-
-/** mod fixed_quality */
-static OPJ_FLOAT64 opj_t1_getwmsedec(
-    OPJ_INT32 nmsedec,
-    OPJ_UINT32 compno,
-    OPJ_UINT32 level,
-    OPJ_UINT32 orient,
-    OPJ_INT32 bpno,
-    OPJ_UINT32 qmfbid,
-    OPJ_FLOAT64 stepsize,
-    OPJ_UINT32 numcomps,
-    const OPJ_FLOAT64 * mct_norms,
-    OPJ_UINT32 mct_numcomps)
-{
-    OPJ_FLOAT64 w1 = 1, w2, wmsedec;
-    OPJ_ARG_NOT_USED(numcomps);
-
-    if (mct_norms && (compno < mct_numcomps)) {
-        w1 = mct_norms[compno];
-    }
-
-    if (qmfbid == 1) {
-        w2 = opj_dwt_getnorm(level, orient);
-    } else {    /* if (qmfbid == 0) */
-        w2 = opj_dwt_getnorm_real(level, orient);
-    }
-
-    wmsedec = w1 * w2 * stepsize * (1 << bpno);
-    wmsedec *= wmsedec * nmsedec / 8192.0;
-
-    return wmsedec;
-}
-
-static OPJ_BOOL opj_t1_allocate_buffers(
-    opj_t1_t *t1,
-    OPJ_UINT32 w,
-    OPJ_UINT32 h)
-{
-    OPJ_UINT32 flagssize;
-    OPJ_UINT32 flags_stride;
-
-    /* No risk of overflow. Prior checks ensure those assert are met */
-    /* They are per the specification */
-    assert(w <= 1024);
-    assert(h <= 1024);
-    assert(w * h <= 4096);
-
-    /* encoder uses tile buffer, so no need to allocate */
-    if (!t1->encoder) {
-        OPJ_UINT32 datasize = w * h;
-
-        if (datasize > t1->datasize) {
-            opj_aligned_free(t1->data);
-            t1->data = (OPJ_INT32*) opj_aligned_malloc(datasize * sizeof(OPJ_INT32));
-            if (!t1->data) {
-                /* FIXME event manager error callback */
-                return OPJ_FALSE;
-            }
-            t1->datasize = datasize;
-        }
-        /* memset first arg is declared to never be null by gcc */
-        if (t1->data != NULL) {
-            memset(t1->data, 0, datasize * sizeof(OPJ_INT32));
-        }
-    }
-
-    flags_stride = w + 2U; /* can't be 0U */
-
-    flagssize = (h + 3U) / 4U + 2U;
-
-    flagssize *= flags_stride;
-    {
-        opj_flag_t* p;
-        OPJ_UINT32 x;
-        OPJ_UINT32 flags_height = (h + 3U) / 4U;
-
-        if (flagssize > t1->flagssize) {
-
-            opj_aligned_free(t1->flags);
-            t1->flags = (opj_flag_t*) opj_aligned_malloc(flagssize * sizeof(
-                            opj_flag_t));
-            if (!t1->flags) {
-                /* FIXME event manager error callback */
-                return OPJ_FALSE;
-            }
-        }
-        t1->flagssize = flagssize;
-
-        memset(t1->flags, 0, flagssize * sizeof(opj_flag_t));
-
-        p = &t1->flags[0];
-        for (x = 0; x < flags_stride; ++x) {
-            /* magic value to hopefully stop any passes being interested in this entry */
-            *p++ = (T1_PI_0 | T1_PI_1 | T1_PI_2 | T1_PI_3);
-        }
-
-        p = &t1->flags[((flags_height + 1) * flags_stride)];
-        for (x = 0; x < flags_stride; ++x) {
-            /* magic value to hopefully stop any passes being interested in this entry */
-            *p++ = (T1_PI_0 | T1_PI_1 | T1_PI_2 | T1_PI_3);
-        }
-
-        if (h % 4) {
-            OPJ_UINT32 v = 0;
-            p = &t1->flags[((flags_height) * flags_stride)];
-            if (h % 4 == 1) {
-                v |= T1_PI_1 | T1_PI_2 | T1_PI_3;
-            } else if (h % 4 == 2) {
-                v |= T1_PI_2 | T1_PI_3;
-            } else if (h % 4 == 3) {
-                v |= T1_PI_3;
-            }
-            for (x = 0; x < flags_stride; ++x) {
-                *p++ = v;
-            }
-        }
-    }
-
-    t1->w = w;
-    t1->h = h;
-
-    return OPJ_TRUE;
-}
-
-/* ----------------------------------------------------------------------- */
-
-/* ----------------------------------------------------------------------- */
-/**
- * Creates a new Tier 1 handle
- * and initializes the look-up tables of the Tier-1 coder/decoder
- * @return a new T1 handle if successful, returns NULL otherwise
-*/
-opj_t1_t* opj_t1_create(OPJ_BOOL isEncoder)
-{
-    opj_t1_t *l_t1 = 00;
-
-    l_t1 = (opj_t1_t*) opj_calloc(1, sizeof(opj_t1_t));
-    if (!l_t1) {
-        return 00;
-    }
-
-    l_t1->encoder = isEncoder;
-
-    return l_t1;
-}
-
-
-/**
- * Destroys a previously created T1 handle
- *
- * @param p_t1 Tier 1 handle to destroy
-*/
-void opj_t1_destroy(opj_t1_t *p_t1)
-{
-    if (! p_t1) {
-        return;
-    }
-
-    /* encoder uses tile buffer, so no need to free */
-    if (!p_t1->encoder && p_t1->data) {
-        opj_aligned_free(p_t1->data);
-        p_t1->data = 00;
-    }
-
-    if (p_t1->flags) {
-        opj_aligned_free(p_t1->flags);
-        p_t1->flags = 00;
-    }
-
-    opj_free(p_t1->cblkdatabuffer);
-
-    opj_free(p_t1);
-}
-
-typedef struct {
-    OPJ_BOOL whole_tile_decoding;
-    OPJ_UINT32 resno;
-    opj_tcd_cblk_dec_t* cblk;
-    opj_tcd_band_t* band;
-    opj_tcd_tilecomp_t* tilec;
-    opj_tccp_t* tccp;
-    OPJ_BOOL mustuse_cblkdatabuffer;
-    volatile OPJ_BOOL* pret;
-    opj_event_mgr_t *p_manager;
-    opj_mutex_t* p_manager_mutex;
-    OPJ_BOOL check_pterm;
-} opj_t1_cblk_decode_processing_job_t;
-
-static void opj_t1_destroy_wrapper(void* t1)
-{
-    opj_t1_destroy((opj_t1_t*) t1);
-}
-
-static void opj_t1_clbl_decode_processor(void* user_data, opj_tls_t* tls)
-{
-    opj_tcd_cblk_dec_t* cblk;
-    opj_tcd_band_t* band;
-    opj_tcd_tilecomp_t* tilec;
-    opj_tccp_t* tccp;
-    OPJ_INT32* OPJ_RESTRICT datap;
-    OPJ_UINT32 cblk_w, cblk_h;
-    OPJ_INT32 x, y;
-    OPJ_UINT32 i, j;
-    opj_t1_cblk_decode_processing_job_t* job;
-    opj_t1_t* t1;
-    OPJ_UINT32 resno;
-    OPJ_UINT32 tile_w;
-
-    job = (opj_t1_cblk_decode_processing_job_t*) user_data;
-
-    cblk = job->cblk;
-
-    if (!job->whole_tile_decoding) {
-        cblk_w = (OPJ_UINT32)(cblk->x1 - cblk->x0);
-        cblk_h = (OPJ_UINT32)(cblk->y1 - cblk->y0);
-
-        cblk->decoded_data = (OPJ_INT32*)opj_aligned_malloc(sizeof(OPJ_INT32) *
-                             cblk_w * cblk_h);
-        if (cblk->decoded_data == NULL) {
-            if (job->p_manager_mutex) {
-                opj_mutex_lock(job->p_manager_mutex);
-            }
-            opj_event_msg(job->p_manager, EVT_ERROR,
-                          "Cannot allocate cblk->decoded_data\n");
-            if (job->p_manager_mutex) {
-                opj_mutex_unlock(job->p_manager_mutex);
-            }
-            *(job->pret) = OPJ_FALSE;
-            opj_free(job);
-            return;
-        }
-        /* Zero-init required */
-        memset(cblk->decoded_data, 0, sizeof(OPJ_INT32) * cblk_w * cblk_h);
-    } else if (cblk->decoded_data) {
-        /* Not sure if that code path can happen, but better be */
-        /* safe than sorry */
-        opj_aligned_free(cblk->decoded_data);
-        cblk->decoded_data = NULL;
-    }
-
-    resno = job->resno;
-    band = job->band;
-    tilec = job->tilec;
-    tccp = job->tccp;
-    tile_w = (OPJ_UINT32)(tilec->resolutions[tilec->minimum_num_resolutions - 1].x1
-                          -
-                          tilec->resolutions[tilec->minimum_num_resolutions - 1].x0);
-
-    if (!*(job->pret)) {
-        opj_free(job);
-        return;
-    }
-
-    t1 = (opj_t1_t*) opj_tls_get(tls, OPJ_TLS_KEY_T1);
-    if (t1 == NULL) {
-        t1 = opj_t1_create(OPJ_FALSE);
-        opj_tls_set(tls, OPJ_TLS_KEY_T1, t1, opj_t1_destroy_wrapper);
-    }
-    t1->mustuse_cblkdatabuffer = job->mustuse_cblkdatabuffer;
-
-    if (OPJ_FALSE == opj_t1_decode_cblk(
-                t1,
-                cblk,
-                band->bandno,
-                (OPJ_UINT32)tccp->roishift,
-                tccp->cblksty,
-                job->p_manager,
-                job->p_manager_mutex,
-                job->check_pterm)) {
-        *(job->pret) = OPJ_FALSE;
-        opj_free(job);
-        return;
-    }
-
-    x = cblk->x0 - band->x0;
-    y = cblk->y0 - band->y0;
-    if (band->bandno & 1) {
-        opj_tcd_resolution_t* pres = &tilec->resolutions[resno - 1];
-        x += pres->x1 - pres->x0;
-    }
-    if (band->bandno & 2) {
-        opj_tcd_resolution_t* pres = &tilec->resolutions[resno - 1];
-        y += pres->y1 - pres->y0;
-    }
-
-    datap = cblk->decoded_data ? cblk->decoded_data : t1->data;
-    cblk_w = t1->w;
-    cblk_h = t1->h;
-
-    if (tccp->roishift) {
-        if (tccp->roishift >= 31) {
-            for (j = 0; j < cblk_h; ++j) {
-                for (i = 0; i < cblk_w; ++i) {
-                    datap[(j * cblk_w) + i] = 0;
-                }
-            }
-        } else {
-            OPJ_INT32 thresh = 1 << tccp->roishift;
-            for (j = 0; j < cblk_h; ++j) {
-                for (i = 0; i < cblk_w; ++i) {
-                    OPJ_INT32 val = datap[(j * cblk_w) + i];
-                    OPJ_INT32 mag = abs(val);
-                    if (mag >= thresh) {
-                        mag >>= tccp->roishift;
-                        datap[(j * cblk_w) + i] = val < 0 ? -mag : mag;
-                    }
-                }
-            }
-        }
-    }
-
-    /* Both can be non NULL if for example decoding a full tile and then */
-    /* partially a tile. In which case partial decoding should be the */
-    /* priority */
-    assert((cblk->decoded_data != NULL) || (tilec->data != NULL));
-
-    if (cblk->decoded_data) {
-        OPJ_UINT32 cblk_size = cblk_w * cblk_h;
-        if (tccp->qmfbid == 1) {
-            for (i = 0; i < cblk_size; ++i) {
-                datap[i] /= 2;
-            }
-        } else {        /* if (tccp->qmfbid == 0) */
-            i = 0;
-#ifdef __SSE2__
-            {
-                const __m128 xmm_stepsize = _mm_set1_ps(band->stepsize);
-                for (; i < (cblk_size & ~15U); i += 16) {
-                    __m128 xmm0_data = _mm_cvtepi32_ps(_mm_load_si128((__m128i * const)(
-                                                           datap + 0)));
-                    __m128 xmm1_data = _mm_cvtepi32_ps(_mm_load_si128((__m128i * const)(
-                                                           datap + 4)));
-                    __m128 xmm2_data = _mm_cvtepi32_ps(_mm_load_si128((__m128i * const)(
-                                                           datap + 8)));
-                    __m128 xmm3_data = _mm_cvtepi32_ps(_mm_load_si128((__m128i * const)(
-                                                           datap + 12)));
-                    _mm_store_ps((float*)(datap +  0), _mm_mul_ps(xmm0_data, xmm_stepsize));
-                    _mm_store_ps((float*)(datap +  4), _mm_mul_ps(xmm1_data, xmm_stepsize));
-                    _mm_store_ps((float*)(datap +  8), _mm_mul_ps(xmm2_data, xmm_stepsize));
-                    _mm_store_ps((float*)(datap + 12), _mm_mul_ps(xmm3_data, xmm_stepsize));
-                    datap += 16;
-                }
-            }
-#endif
-            for (; i < cblk_size; ++i) {
-                OPJ_FLOAT32 tmp = ((OPJ_FLOAT32)(*datap)) * band->stepsize;
-                memcpy(datap, &tmp, sizeof(tmp));
-                datap++;
-            }
-        }
-    } else if (tccp->qmfbid == 1) {
-        OPJ_INT32* OPJ_RESTRICT tiledp = &tilec->data[(OPJ_SIZE_T)y * tile_w +
-                                                       (OPJ_SIZE_T)x];
-        for (j = 0; j < cblk_h; ++j) {
-            i = 0;
-            for (; i < (cblk_w & ~(OPJ_UINT32)3U); i += 4U) {
-                OPJ_INT32 tmp0 = datap[(j * cblk_w) + i + 0U];
-                OPJ_INT32 tmp1 = datap[(j * cblk_w) + i + 1U];
-                OPJ_INT32 tmp2 = datap[(j * cblk_w) + i + 2U];
-                OPJ_INT32 tmp3 = datap[(j * cblk_w) + i + 3U];
-                ((OPJ_INT32*)tiledp)[(j * (OPJ_SIZE_T)tile_w) + i + 0U] = tmp0 / 2;
-                ((OPJ_INT32*)tiledp)[(j * (OPJ_SIZE_T)tile_w) + i + 1U] = tmp1 / 2;
-                ((OPJ_INT32*)tiledp)[(j * (OPJ_SIZE_T)tile_w) + i + 2U] = tmp2 / 2;
-                ((OPJ_INT32*)tiledp)[(j * (OPJ_SIZE_T)tile_w) + i + 3U] = tmp3 / 2;
-            }
-            for (; i < cblk_w; ++i) {
-                OPJ_INT32 tmp = datap[(j * cblk_w) + i];
-                ((OPJ_INT32*)tiledp)[(j * (OPJ_SIZE_T)tile_w) + i] = tmp / 2;
-            }
-        }
-    } else {        /* if (tccp->qmfbid == 0) */
-        OPJ_FLOAT32* OPJ_RESTRICT tiledp = (OPJ_FLOAT32*) &tilec->data[(OPJ_SIZE_T)y *
-                                                         tile_w + (OPJ_SIZE_T)x];
-        for (j = 0; j < cblk_h; ++j) {
-            OPJ_FLOAT32* OPJ_RESTRICT tiledp2 = tiledp;
-            for (i = 0; i < cblk_w; ++i) {
-                OPJ_FLOAT32 tmp = (OPJ_FLOAT32) * datap * band->stepsize;
-                *tiledp2 = tmp;
-                datap++;
-                tiledp2++;
-            }
-            tiledp += tile_w;
-        }
-    }
-
-    opj_free(job);
-}
-
-
-void opj_t1_decode_cblks(opj_tcd_t* tcd,
-                         volatile OPJ_BOOL* pret,
-                         opj_tcd_tilecomp_t* tilec,
-                         opj_tccp_t* tccp,
-                         opj_event_mgr_t *p_manager,
-                         opj_mutex_t* p_manager_mutex,
-                         OPJ_BOOL check_pterm
-                        )
-{
-    opj_thread_pool_t* tp = tcd->thread_pool;
-    OPJ_UINT32 resno, bandno, precno, cblkno;
-
-#ifdef DEBUG_VERBOSE
-    OPJ_UINT32 codeblocks_decoded = 0;
-    printf("Enter opj_t1_decode_cblks()\n");
-#endif
-
-    for (resno = 0; resno < tilec->minimum_num_resolutions; ++resno) {
-        opj_tcd_resolution_t* res = &tilec->resolutions[resno];
-
-        for (bandno = 0; bandno < res->numbands; ++bandno) {
-            opj_tcd_band_t* OPJ_RESTRICT band = &res->bands[bandno];
-
-            for (precno = 0; precno < res->pw * res->ph; ++precno) {
-                opj_tcd_precinct_t* precinct = &band->precincts[precno];
-
-                if (!opj_tcd_is_subband_area_of_interest(tcd,
-                        tilec->compno,
-                        resno,
-                        band->bandno,
-                        (OPJ_UINT32)precinct->x0,
-                        (OPJ_UINT32)precinct->y0,
-                        (OPJ_UINT32)precinct->x1,
-                        (OPJ_UINT32)precinct->y1)) {
-                    for (cblkno = 0; cblkno < precinct->cw * precinct->ch; ++cblkno) {
-                        opj_tcd_cblk_dec_t* cblk = &precinct->cblks.dec[cblkno];
-                        if (cblk->decoded_data) {
-#ifdef DEBUG_VERBOSE
-                            printf("Discarding codeblock %d,%d at resno=%d, bandno=%d\n",
-                                   cblk->x0, cblk->y0, resno, bandno);
-#endif
-                            opj_aligned_free(cblk->decoded_data);
-                            cblk->decoded_data = NULL;
-                        }
-                    }
-                    continue;
-                }
-
-                for (cblkno = 0; cblkno < precinct->cw * precinct->ch; ++cblkno) {
-                    opj_tcd_cblk_dec_t* cblk = &precinct->cblks.dec[cblkno];
-                    opj_t1_cblk_decode_processing_job_t* job;
-
-                    if (!opj_tcd_is_subband_area_of_interest(tcd,
-                            tilec->compno,
-                            resno,
-                            band->bandno,
-                            (OPJ_UINT32)cblk->x0,
-                            (OPJ_UINT32)cblk->y0,
-                            (OPJ_UINT32)cblk->x1,
-                            (OPJ_UINT32)cblk->y1)) {
-                        if (cblk->decoded_data) {
-#ifdef DEBUG_VERBOSE
-                            printf("Discarding codeblock %d,%d at resno=%d, bandno=%d\n",
-                                   cblk->x0, cblk->y0, resno, bandno);
-#endif
-                            opj_aligned_free(cblk->decoded_data);
-                            cblk->decoded_data = NULL;
-                        }
-                        continue;
-                    }
-
-                    if (!tcd->whole_tile_decoding) {
-                        OPJ_UINT32 cblk_w = (OPJ_UINT32)(cblk->x1 - cblk->x0);
-                        OPJ_UINT32 cblk_h = (OPJ_UINT32)(cblk->y1 - cblk->y0);
-                        if (cblk->decoded_data != NULL) {
-#ifdef DEBUG_VERBOSE
-                            printf("Reusing codeblock %d,%d at resno=%d, bandno=%d\n",
-                                   cblk->x0, cblk->y0, resno, bandno);
-#endif
-                            continue;
-                        }
-                        if (cblk_w == 0 || cblk_h == 0) {
-                            continue;
-                        }
-#ifdef DEBUG_VERBOSE
-                        printf("Decoding codeblock %d,%d at resno=%d, bandno=%d\n",
-                               cblk->x0, cblk->y0, resno, bandno);
-#endif
-                    }
-
-                    job = (opj_t1_cblk_decode_processing_job_t*) opj_calloc(1,
-                            sizeof(opj_t1_cblk_decode_processing_job_t));
-                    if (!job) {
-                        *pret = OPJ_FALSE;
-                        return;
-                    }
-                    job->whole_tile_decoding = tcd->whole_tile_decoding;
-                    job->resno = resno;
-                    job->cblk = cblk;
-                    job->band = band;
-                    job->tilec = tilec;
-                    job->tccp = tccp;
-                    job->pret = pret;
-                    job->p_manager_mutex = p_manager_mutex;
-                    job->p_manager = p_manager;
-                    job->check_pterm = check_pterm;
-                    job->mustuse_cblkdatabuffer = opj_thread_pool_get_thread_count(tp) > 1;
-                    opj_thread_pool_submit_job(tp, opj_t1_clbl_decode_processor, job);
-#ifdef DEBUG_VERBOSE
-                    codeblocks_decoded ++;
-#endif
-                    if (!(*pret)) {
-                        return;
-                    }
-                } /* cblkno */
-            } /* precno */
-        } /* bandno */
-    } /* resno */
-
-#ifdef DEBUG_VERBOSE
-    printf("Leave opj_t1_decode_cblks(). Number decoded: %d\n", codeblocks_decoded);
-#endif
-    return;
-}
-
-
-static OPJ_BOOL opj_t1_decode_cblk(opj_t1_t *t1,
-                                   opj_tcd_cblk_dec_t* cblk,
-                                   OPJ_UINT32 orient,
-                                   OPJ_UINT32 roishift,
-                                   OPJ_UINT32 cblksty,
-                                   opj_event_mgr_t *p_manager,
-                                   opj_mutex_t* p_manager_mutex,
-                                   OPJ_BOOL check_pterm)
-{
-    opj_mqc_t *mqc = &(t1->mqc);   /* MQC component */
-
-    OPJ_INT32 bpno_plus_one;
-    OPJ_UINT32 passtype;
-    OPJ_UINT32 segno, passno;
-    OPJ_BYTE* cblkdata = NULL;
-    OPJ_UINT32 cblkdataindex = 0;
-    OPJ_BYTE type = T1_TYPE_MQ; /* BYPASS mode */
-    OPJ_INT32* original_t1_data = NULL;
-
-    mqc->lut_ctxno_zc_orient = lut_ctxno_zc + (orient << 9);
-
-    if (!opj_t1_allocate_buffers(
-                t1,
-                (OPJ_UINT32)(cblk->x1 - cblk->x0),
-                (OPJ_UINT32)(cblk->y1 - cblk->y0))) {
-        return OPJ_FALSE;
-    }
-
-    bpno_plus_one = (OPJ_INT32)(roishift + cblk->numbps);
-    if (bpno_plus_one >= 31) {
-        if (p_manager_mutex) {
-            opj_mutex_lock(p_manager_mutex);
-        }
-        opj_event_msg(p_manager, EVT_WARNING,
-                      "opj_t1_decode_cblk(): unsupported bpno_plus_one = %d >= 31\n",
-                      bpno_plus_one);
-        if (p_manager_mutex) {
-            opj_mutex_unlock(p_manager_mutex);
-        }
-        return OPJ_FALSE;
-    }
-    passtype = 2;
-
-    opj_mqc_resetstates(mqc);
-    opj_mqc_setstate(mqc, T1_CTXNO_UNI, 0, 46);
-    opj_mqc_setstate(mqc, T1_CTXNO_AGG, 0, 3);
-    opj_mqc_setstate(mqc, T1_CTXNO_ZC, 0, 4);
-
-    /* Even if we have a single chunk, in multi-threaded decoding */
-    /* the insertion of our synthetic marker might potentially override */
-    /* valid codestream of other codeblocks decoded in parallel. */
-    if (cblk->numchunks > 1 || t1->mustuse_cblkdatabuffer) {
-        OPJ_UINT32 i;
-        OPJ_UINT32 cblk_len;
-
-        /* Compute whole codeblock length from chunk lengths */
-        cblk_len = 0;
-        for (i = 0; i < cblk->numchunks; i++) {
-            cblk_len += cblk->chunks[i].len;
-        }
-
-        /* Allocate temporary memory if needed */
-        if (cblk_len + OPJ_COMMON_CBLK_DATA_EXTRA > t1->cblkdatabuffersize) {
-            cblkdata = (OPJ_BYTE*)opj_realloc(t1->cblkdatabuffer,
-                                              cblk_len + OPJ_COMMON_CBLK_DATA_EXTRA);
-            if (cblkdata == NULL) {
-                return OPJ_FALSE;
-            }
-            t1->cblkdatabuffer = cblkdata;
-            memset(t1->cblkdatabuffer + cblk_len, 0, OPJ_COMMON_CBLK_DATA_EXTRA);
-            t1->cblkdatabuffersize = cblk_len + OPJ_COMMON_CBLK_DATA_EXTRA;
-        }
-
-        /* Concatenate all chunks */
-        cblkdata = t1->cblkdatabuffer;
-        cblk_len = 0;
-        for (i = 0; i < cblk->numchunks; i++) {
-            memcpy(cblkdata + cblk_len, cblk->chunks[i].data, cblk->chunks[i].len);
-            cblk_len += cblk->chunks[i].len;
-        }
-    } else if (cblk->numchunks == 1) {
-        cblkdata = cblk->chunks[0].data;
-    } else {
-        /* Not sure if that can happen in practice, but avoid Coverity to */
-        /* think we will dereference a null cblkdta pointer */
-        return OPJ_TRUE;
-    }
-
-    /* For subtile decoding, directly decode in the decoded_data buffer of */
-    /* the code-block. Hack t1->data to point to it, and restore it later */
-    if (cblk->decoded_data) {
-        original_t1_data = t1->data;
-        t1->data = cblk->decoded_data;
-    }
-
-    for (segno = 0; segno < cblk->real_num_segs; ++segno) {
-        opj_tcd_seg_t *seg = &cblk->segs[segno];
-
-        /* BYPASS mode */
-        type = ((bpno_plus_one <= ((OPJ_INT32)(cblk->numbps)) - 4) && (passtype < 2) &&
-                (cblksty & J2K_CCP_CBLKSTY_LAZY)) ? T1_TYPE_RAW : T1_TYPE_MQ;
-
-        if (type == T1_TYPE_RAW) {
-            opj_mqc_raw_init_dec(mqc, cblkdata + cblkdataindex, seg->len,
-                                 OPJ_COMMON_CBLK_DATA_EXTRA);
-        } else {
-            opj_mqc_init_dec(mqc, cblkdata + cblkdataindex, seg->len,
-                             OPJ_COMMON_CBLK_DATA_EXTRA);
-        }
-        cblkdataindex += seg->len;
-
-        for (passno = 0; (passno < seg->real_num_passes) &&
-                (bpno_plus_one >= 1); ++passno) {
-            switch (passtype) {
-            case 0:
-                if (type == T1_TYPE_RAW) {
-                    opj_t1_dec_sigpass_raw(t1, bpno_plus_one, (OPJ_INT32)cblksty);
-                } else {
-                    opj_t1_dec_sigpass_mqc(t1, bpno_plus_one, (OPJ_INT32)cblksty);
-                }
-                break;
-            case 1:
-                if (type == T1_TYPE_RAW) {
-                    opj_t1_dec_refpass_raw(t1, bpno_plus_one);
-                } else {
-                    opj_t1_dec_refpass_mqc(t1, bpno_plus_one);
-                }
-                break;
-            case 2:
-                opj_t1_dec_clnpass(t1, bpno_plus_one, (OPJ_INT32)cblksty);
-                break;
-            }
-
-            if ((cblksty & J2K_CCP_CBLKSTY_RESET) && type == T1_TYPE_MQ) {
-                opj_mqc_resetstates(mqc);
-                opj_mqc_setstate(mqc, T1_CTXNO_UNI, 0, 46);
-                opj_mqc_setstate(mqc, T1_CTXNO_AGG, 0, 3);
-                opj_mqc_setstate(mqc, T1_CTXNO_ZC, 0, 4);
-            }
-            if (++passtype == 3) {
-                passtype = 0;
-                bpno_plus_one--;
-            }
-        }
-
-        opq_mqc_finish_dec(mqc);
-    }
-
-    if (check_pterm) {
-        if (mqc->bp + 2 < mqc->end) {
-            if (p_manager_mutex) {
-                opj_mutex_lock(p_manager_mutex);
-            }
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "PTERM check failure: %d remaining bytes in code block (%d used / %d)\n",
-                          (int)(mqc->end - mqc->bp) - 2,
-                          (int)(mqc->bp - mqc->start),
-                          (int)(mqc->end - mqc->start));
-            if (p_manager_mutex) {
-                opj_mutex_unlock(p_manager_mutex);
-            }
-        } else if (mqc->end_of_byte_stream_counter > 2) {
-            if (p_manager_mutex) {
-                opj_mutex_lock(p_manager_mutex);
-            }
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "PTERM check failure: %d synthetized 0xFF markers read\n",
-                          mqc->end_of_byte_stream_counter);
-            if (p_manager_mutex) {
-                opj_mutex_unlock(p_manager_mutex);
-            }
-        }
-    }
-
-    /* Restore original t1->data is needed */
-    if (cblk->decoded_data) {
-        t1->data = original_t1_data;
-    }
-
-    return OPJ_TRUE;
-}
-
-
-
-
-OPJ_BOOL opj_t1_encode_cblks(opj_t1_t *t1,
-                             opj_tcd_tile_t *tile,
-                             opj_tcp_t *tcp,
-                             const OPJ_FLOAT64 * mct_norms,
-                             OPJ_UINT32 mct_numcomps
-                            )
-{
-    OPJ_UINT32 compno, resno, bandno, precno, cblkno;
-
-    tile->distotile = 0;        /* fixed_quality */
-
-    for (compno = 0; compno < tile->numcomps; ++compno) {
-        opj_tcd_tilecomp_t* tilec = &tile->comps[compno];
-        opj_tccp_t* tccp = &tcp->tccps[compno];
-        OPJ_UINT32 tile_w = (OPJ_UINT32)(tilec->x1 - tilec->x0);
-
-        for (resno = 0; resno < tilec->numresolutions; ++resno) {
-            opj_tcd_resolution_t *res = &tilec->resolutions[resno];
-
-            for (bandno = 0; bandno < res->numbands; ++bandno) {
-                opj_tcd_band_t* OPJ_RESTRICT band = &res->bands[bandno];
-                OPJ_INT32 bandconst;
-
-                /* Skip empty bands */
-                if (opj_tcd_is_band_empty(band)) {
-                    continue;
-                }
-
-                bandconst = 8192 * 8192 / ((OPJ_INT32) floor(band->stepsize * 8192));
-                for (precno = 0; precno < res->pw * res->ph; ++precno) {
-                    opj_tcd_precinct_t *prc = &band->precincts[precno];
-
-                    for (cblkno = 0; cblkno < prc->cw * prc->ch; ++cblkno) {
-                        opj_tcd_cblk_enc_t* cblk = &prc->cblks.enc[cblkno];
-                        OPJ_INT32* OPJ_RESTRICT tiledp;
-                        OPJ_UINT32 cblk_w;
-                        OPJ_UINT32 cblk_h;
-                        OPJ_UINT32 i, j, tileLineAdvance;
-                        OPJ_SIZE_T tileIndex = 0;
-
-                        OPJ_INT32 x = cblk->x0 - band->x0;
-                        OPJ_INT32 y = cblk->y0 - band->y0;
-                        if (band->bandno & 1) {
-                            opj_tcd_resolution_t *pres = &tilec->resolutions[resno - 1];
-                            x += pres->x1 - pres->x0;
-                        }
-                        if (band->bandno & 2) {
-                            opj_tcd_resolution_t *pres = &tilec->resolutions[resno - 1];
-                            y += pres->y1 - pres->y0;
-                        }
-
-                        if (!opj_t1_allocate_buffers(
-                                    t1,
-                                    (OPJ_UINT32)(cblk->x1 - cblk->x0),
-                                    (OPJ_UINT32)(cblk->y1 - cblk->y0))) {
-                            return OPJ_FALSE;
-                        }
-
-                        cblk_w = t1->w;
-                        cblk_h = t1->h;
-                        tileLineAdvance = tile_w - cblk_w;
-
-                        tiledp = &tilec->data[(OPJ_SIZE_T)y * tile_w + (OPJ_SIZE_T)x];
-                        t1->data = tiledp;
-                        t1->data_stride = tile_w;
-                        if (tccp->qmfbid == 1) {
-                            /* Do multiplication on unsigned type, even if the
-                             * underlying type is signed, to avoid potential
-                             * int overflow on large value (the output will be
-                             * incorrect in such situation, but whatever...)
-                             * This assumes complement-to-2 signed integer
-                             * representation
-                             * Fixes https://github.com/uclouvain/openjpeg/issues/1053
-                             */
-                            OPJ_UINT32* OPJ_RESTRICT tiledp_u = (OPJ_UINT32*) tiledp;
-                            for (j = 0; j < cblk_h; ++j) {
-                                for (i = 0; i < cblk_w; ++i) {
-                                    tiledp_u[tileIndex] <<= T1_NMSEDEC_FRACBITS;
-                                    tileIndex++;
-                                }
-                                tileIndex += tileLineAdvance;
-                            }
-                        } else {        /* if (tccp->qmfbid == 0) */
-                            for (j = 0; j < cblk_h; ++j) {
-                                for (i = 0; i < cblk_w; ++i) {
-                                    OPJ_INT32 tmp = tiledp[tileIndex];
-                                    tiledp[tileIndex] =
-                                        opj_int_fix_mul_t1(
-                                            tmp,
-                                            bandconst);
-                                    tileIndex++;
-                                }
-                                tileIndex += tileLineAdvance;
-                            }
-                        }
-
-                        opj_t1_encode_cblk(
-                            t1,
-                            cblk,
-                            band->bandno,
-                            compno,
-                            tilec->numresolutions - 1 - resno,
-                            tccp->qmfbid,
-                            band->stepsize,
-                            tccp->cblksty,
-                            tile->numcomps,
-                            tile,
-                            mct_norms,
-                            mct_numcomps);
-
-                    } /* cblkno */
-                } /* precno */
-            } /* bandno */
-        } /* resno  */
-    } /* compno  */
-    return OPJ_TRUE;
-}
-
-/* Returns whether the pass (bpno, passtype) is terminated */
-static int opj_t1_enc_is_term_pass(opj_tcd_cblk_enc_t* cblk,
-                                   OPJ_UINT32 cblksty,
-                                   OPJ_INT32 bpno,
-                                   OPJ_UINT32 passtype)
-{
-    /* Is it the last cleanup pass ? */
-    if (passtype == 2 && bpno == 0) {
-        return OPJ_TRUE;
-    }
-
-    if (cblksty & J2K_CCP_CBLKSTY_TERMALL) {
-        return OPJ_TRUE;
-    }
-
-    if ((cblksty & J2K_CCP_CBLKSTY_LAZY)) {
-        /* For bypass arithmetic bypass, terminate the 4th cleanup pass */
-        if ((bpno == ((OPJ_INT32)cblk->numbps - 4)) && (passtype == 2)) {
-            return OPJ_TRUE;
-        }
-        /* and beyond terminate all the magnitude refinement passes (in raw) */
-        /* and cleanup passes (in MQC) */
-        if ((bpno < ((OPJ_INT32)(cblk->numbps) - 4)) && (passtype > 0)) {
-            return OPJ_TRUE;
-        }
-    }
-
-    return OPJ_FALSE;
-}
-
-
-/** mod fixed_quality */
-static void opj_t1_encode_cblk(opj_t1_t *t1,
-                               opj_tcd_cblk_enc_t* cblk,
-                               OPJ_UINT32 orient,
-                               OPJ_UINT32 compno,
-                               OPJ_UINT32 level,
-                               OPJ_UINT32 qmfbid,
-                               OPJ_FLOAT64 stepsize,
-                               OPJ_UINT32 cblksty,
-                               OPJ_UINT32 numcomps,
-                               opj_tcd_tile_t * tile,
-                               const OPJ_FLOAT64 * mct_norms,
-                               OPJ_UINT32 mct_numcomps)
-{
-    OPJ_FLOAT64 cumwmsedec = 0.0;
-
-    opj_mqc_t *mqc = &(t1->mqc);   /* MQC component */
-
-    OPJ_UINT32 passno;
-    OPJ_INT32 bpno;
-    OPJ_UINT32 passtype;
-    OPJ_INT32 nmsedec = 0;
-    OPJ_INT32 max;
-    OPJ_UINT32 i, j;
-    OPJ_BYTE type = T1_TYPE_MQ;
-    OPJ_FLOAT64 tempwmsedec;
-
-#ifdef EXTRA_DEBUG
-    printf("encode_cblk(x=%d,y=%d,x1=%d,y1=%d,orient=%d,compno=%d,level=%d\n",
-           cblk->x0, cblk->y0, cblk->x1, cblk->y1, orient, compno, level);
-#endif
-
-    mqc->lut_ctxno_zc_orient = lut_ctxno_zc + (orient << 9);
-
-    max = 0;
-    for (i = 0; i < t1->w; ++i) {
-        for (j = 0; j < t1->h; ++j) {
-            OPJ_INT32 tmp = abs(t1->data[i + j * t1->data_stride]);
-            max = opj_int_max(max, tmp);
-        }
-    }
-
-    cblk->numbps = max ? (OPJ_UINT32)((opj_int_floorlog2(max) + 1) -
-                                      T1_NMSEDEC_FRACBITS) : 0;
-    if (cblk->numbps == 0) {
-        cblk->totalpasses = 0;
-        return;
-    }
-
-    bpno = (OPJ_INT32)(cblk->numbps - 1);
-    passtype = 2;
-
-    opj_mqc_resetstates(mqc);
-    opj_mqc_setstate(mqc, T1_CTXNO_UNI, 0, 46);
-    opj_mqc_setstate(mqc, T1_CTXNO_AGG, 0, 3);
-    opj_mqc_setstate(mqc, T1_CTXNO_ZC, 0, 4);
-    opj_mqc_init_enc(mqc, cblk->data);
-
-    for (passno = 0; bpno >= 0; ++passno) {
-        opj_tcd_pass_t *pass = &cblk->passes[passno];
-        type = ((bpno < ((OPJ_INT32)(cblk->numbps) - 4)) && (passtype < 2) &&
-                (cblksty & J2K_CCP_CBLKSTY_LAZY)) ? T1_TYPE_RAW : T1_TYPE_MQ;
-
-        /* If the previous pass was terminating, we need to reset the encoder */
-        if (passno > 0 && cblk->passes[passno - 1].term) {
-            if (type == T1_TYPE_RAW) {
-                opj_mqc_bypass_init_enc(mqc);
-            } else {
-                opj_mqc_restart_init_enc(mqc);
-            }
-        }
-
-        switch (passtype) {
-        case 0:
-            opj_t1_enc_sigpass(t1, bpno, &nmsedec, type, cblksty);
-            break;
-        case 1:
-            opj_t1_enc_refpass(t1, bpno, &nmsedec, type);
-            break;
-        case 2:
-            opj_t1_enc_clnpass(t1, bpno, &nmsedec, cblksty);
-            /* code switch SEGMARK (i.e. SEGSYM) */
-            if (cblksty & J2K_CCP_CBLKSTY_SEGSYM) {
-                opj_mqc_segmark_enc(mqc);
-            }
-            break;
-        }
-
-        /* fixed_quality */
-        tempwmsedec = opj_t1_getwmsedec(nmsedec, compno, level, orient, bpno, qmfbid,
-                                        stepsize, numcomps, mct_norms, mct_numcomps) ;
-        cumwmsedec += tempwmsedec;
-        tile->distotile += tempwmsedec;
-        pass->distortiondec = cumwmsedec;
-
-        if (opj_t1_enc_is_term_pass(cblk, cblksty, bpno, passtype)) {
-            /* If it is a terminated pass, terminate it */
-            if (type == T1_TYPE_RAW) {
-                opj_mqc_bypass_flush_enc(mqc, cblksty & J2K_CCP_CBLKSTY_PTERM);
-            } else {
-                if (cblksty & J2K_CCP_CBLKSTY_PTERM) {
-                    opj_mqc_erterm_enc(mqc);
-                } else {
-                    opj_mqc_flush(mqc);
-                }
-            }
-            pass->term = 1;
-            pass->rate = opj_mqc_numbytes(mqc);
-        } else {
-            /* Non terminated pass */
-            OPJ_UINT32 rate_extra_bytes;
-            if (type == T1_TYPE_RAW) {
-                rate_extra_bytes = opj_mqc_bypass_get_extra_bytes(
-                                       mqc, (cblksty & J2K_CCP_CBLKSTY_PTERM));
-            } else {
-                rate_extra_bytes = 3;
-            }
-            pass->term = 0;
-            pass->rate = opj_mqc_numbytes(mqc) + rate_extra_bytes;
-        }
-
-        if (++passtype == 3) {
-            passtype = 0;
-            bpno--;
-        }
-
-        /* Code-switch "RESET" */
-        if (cblksty & J2K_CCP_CBLKSTY_RESET) {
-            opj_mqc_reset_enc(mqc);
-        }
-    }
-
-    cblk->totalpasses = passno;
-
-    if (cblk->totalpasses) {
-        /* Make sure that pass rates are increasing */
-        OPJ_UINT32 last_pass_rate = opj_mqc_numbytes(mqc);
-        for (passno = cblk->totalpasses; passno > 0;) {
-            opj_tcd_pass_t *pass = &cblk->passes[--passno];
-            if (pass->rate > last_pass_rate) {
-                pass->rate = last_pass_rate;
-            } else {
-                last_pass_rate = pass->rate;
-            }
-        }
-    }
-
-    for (passno = 0; passno < cblk->totalpasses; passno++) {
-        opj_tcd_pass_t *pass = &cblk->passes[passno];
-
-        /* Prevent generation of FF as last data byte of a pass*/
-        /* For terminating passes, the flushing procedure ensured this already */
-        assert(pass->rate > 0);
-        if (cblk->data[pass->rate - 1] == 0xFF) {
-            pass->rate--;
-        }
-        pass->len = pass->rate - (passno == 0 ? 0 : cblk->passes[passno - 1].rate);
-    }
-
-#ifdef EXTRA_DEBUG
-    printf(" len=%d\n", (cblk->totalpasses) ? opj_mqc_numbytes(mqc) : 0);
-
-    /* Check that there not 0xff >=0x90 sequences */
-    if (cblk->totalpasses) {
-        OPJ_UINT32 i;
-        OPJ_UINT32 len = opj_mqc_numbytes(mqc);
-        for (i = 1; i < len; ++i) {
-            if (cblk->data[i - 1] == 0xff && cblk->data[i] >= 0x90) {
-                printf("0xff %02x at offset %d\n", cblk->data[i], i - 1);
-                abort();
-            }
-        }
-    }
-#endif
-}
diff --git a/third_party/libopenjpeg20/t1.h b/third_party/libopenjpeg20/t1.h
deleted file mode 100644
index 171dfb0..0000000
--- a/third_party/libopenjpeg20/t1.h
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2012, Carl Hetherington
- * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-#ifndef OPJ_T1_H
-#define OPJ_T1_H
-/**
-@file t1.h
-@brief Implementation of the tier-1 coding (coding of code-block coefficients) (T1)
-
-The functions in T1.C have for goal to realize the tier-1 coding operation. The functions
-in T1.C are used by some function in TCD.C.
-*/
-
-/** @defgroup T1 T1 - Implementation of the tier-1 coding */
-/*@{*/
-
-/* ----------------------------------------------------------------------- */
-#define T1_NMSEDEC_BITS 7
-
-#define T1_NUMCTXS_ZC  9
-#define T1_NUMCTXS_SC  5
-#define T1_NUMCTXS_MAG 3
-#define T1_NUMCTXS_AGG 1
-#define T1_NUMCTXS_UNI 1
-
-#define T1_CTXNO_ZC  0
-#define T1_CTXNO_SC  (T1_CTXNO_ZC+T1_NUMCTXS_ZC)
-#define T1_CTXNO_MAG (T1_CTXNO_SC+T1_NUMCTXS_SC)
-#define T1_CTXNO_AGG (T1_CTXNO_MAG+T1_NUMCTXS_MAG)
-#define T1_CTXNO_UNI (T1_CTXNO_AGG+T1_NUMCTXS_AGG)
-#define T1_NUMCTXS   (T1_CTXNO_UNI+T1_NUMCTXS_UNI)
-
-#define T1_NMSEDEC_FRACBITS (T1_NMSEDEC_BITS-1)
-
-#define T1_TYPE_MQ 0    /**< Normal coding using entropy coder */
-#define T1_TYPE_RAW 1   /**< No encoding the information is store under raw format in codestream (mode switch RAW)*/
-
-/* BEGINNING of flags that apply to opj_flag_t */
-/** We hold the state of individual data points for the T1 encoder using
- *  a single 32-bit flags word to hold the state of 4 data points.  This corresponds
- *  to the 4-point-high columns that the data is processed in.
- *
- *  These \#defines declare the layout of a 32-bit flags word.
- *
- *  This is currently done for encoding only.
- *  The values must NOT be changed, otherwise this is going to break a lot of
- *  assumptions.
- */
-
-/* SIGMA: significance state (3 cols x 6 rows)
- * CHI:   state for negative sample value (1 col x 6 rows)
- * MU:    state for visited in refinement pass (1 col x 4 rows)
- * PI:    state for visited in significance pass (1 col * 4 rows)
- */
-
-#define T1_SIGMA_0  (1U << 0)
-#define T1_SIGMA_1  (1U << 1)
-#define T1_SIGMA_2  (1U << 2)
-#define T1_SIGMA_3  (1U << 3)
-#define T1_SIGMA_4  (1U << 4)
-#define T1_SIGMA_5  (1U << 5)
-#define T1_SIGMA_6  (1U << 6)
-#define T1_SIGMA_7  (1U << 7)
-#define T1_SIGMA_8  (1U << 8)
-#define T1_SIGMA_9  (1U << 9)
-#define T1_SIGMA_10 (1U << 10)
-#define T1_SIGMA_11 (1U << 11)
-#define T1_SIGMA_12 (1U << 12)
-#define T1_SIGMA_13 (1U << 13)
-#define T1_SIGMA_14 (1U << 14)
-#define T1_SIGMA_15 (1U << 15)
-#define T1_SIGMA_16 (1U << 16)
-#define T1_SIGMA_17 (1U << 17)
-
-#define T1_CHI_0    (1U << 18)
-#define T1_CHI_0_I  18
-#define T1_CHI_1    (1U << 19)
-#define T1_CHI_1_I  19
-#define T1_MU_0     (1U << 20)
-#define T1_PI_0     (1U << 21)
-#define T1_CHI_2    (1U << 22)
-#define T1_CHI_2_I  22
-#define T1_MU_1     (1U << 23)
-#define T1_PI_1     (1U << 24)
-#define T1_CHI_3    (1U << 25)
-#define T1_MU_2     (1U << 26)
-#define T1_PI_2     (1U << 27)
-#define T1_CHI_4    (1U << 28)
-#define T1_MU_3     (1U << 29)
-#define T1_PI_3     (1U << 30)
-#define T1_CHI_5    (1U << 31)
-#define T1_CHI_5_I  31
-
-/** As an example, the bits T1_SIGMA_3, T1_SIGMA_4 and T1_SIGMA_5
- *  indicate the significance state of the west neighbour of data point zero
- *  of our four, the point itself, and its east neighbour respectively.
- *  Many of the bits are arranged so that given a flags word, you can
- *  look at the values for the data point 0, then shift the flags
- *  word right by 3 bits and look at the same bit positions to see the
- *  values for data point 1.
- *
- *  The \#defines below help a bit with this; say you have a flags word
- *  f, you can do things like
- *
- *  (f & T1_SIGMA_THIS)
- *
- *  to see the significance bit of data point 0, then do
- *
- *  ((f >> 3) & T1_SIGMA_THIS)
- *
- *  to see the significance bit of data point 1.
- */
-
-#define T1_SIGMA_NW   T1_SIGMA_0
-#define T1_SIGMA_N    T1_SIGMA_1
-#define T1_SIGMA_NE   T1_SIGMA_2
-#define T1_SIGMA_W    T1_SIGMA_3
-#define T1_SIGMA_THIS T1_SIGMA_4
-#define T1_SIGMA_E    T1_SIGMA_5
-#define T1_SIGMA_SW   T1_SIGMA_6
-#define T1_SIGMA_S    T1_SIGMA_7
-#define T1_SIGMA_SE   T1_SIGMA_8
-#define T1_SIGMA_NEIGHBOURS (T1_SIGMA_NW | T1_SIGMA_N | T1_SIGMA_NE | T1_SIGMA_W | T1_SIGMA_E | T1_SIGMA_SW | T1_SIGMA_S | T1_SIGMA_SE)
-
-#define T1_CHI_THIS   T1_CHI_1
-#define T1_CHI_THIS_I T1_CHI_1_I
-#define T1_MU_THIS    T1_MU_0
-#define T1_PI_THIS    T1_PI_0
-#define T1_CHI_S      T1_CHI_2
-
-#define T1_LUT_SGN_W (1U << 0)
-#define T1_LUT_SIG_N (1U << 1)
-#define T1_LUT_SGN_E (1U << 2)
-#define T1_LUT_SIG_W (1U << 3)
-#define T1_LUT_SGN_N (1U << 4)
-#define T1_LUT_SIG_E (1U << 5)
-#define T1_LUT_SGN_S (1U << 6)
-#define T1_LUT_SIG_S (1U << 7)
-/* END of flags that apply to opj_flag_t */
-
-/* ----------------------------------------------------------------------- */
-
-/** Flags for 4 consecutive rows of a column */
-typedef OPJ_UINT32 opj_flag_t;
-
-/**
-Tier-1 coding (coding of code-block coefficients)
-*/
-typedef struct opj_t1 {
-
-    /** MQC component */
-    opj_mqc_t mqc;
-
-    OPJ_INT32  *data;
-    /** Flags used by decoder and encoder.
-     * Such that flags[1+0] is for state of col=0,row=0..3,
-       flags[1+1] for col=1, row=0..3, flags[1+flags_stride] for col=0,row=4..7, ...
-       This array avoids too much cache trashing when processing by 4 vertical samples
-       as done in the various decoding steps. */
-    opj_flag_t *flags;
-
-    OPJ_UINT32 w;
-    OPJ_UINT32 h;
-    OPJ_UINT32 datasize;
-    OPJ_UINT32 flagssize;
-    OPJ_UINT32 data_stride;
-    OPJ_BOOL   encoder;
-
-    /* Thre 3 variables below are only used by the decoder */
-    /* set to TRUE in multithreaded context */
-    OPJ_BOOL     mustuse_cblkdatabuffer;
-    /* Temporary buffer to concatenate all chunks of a codebock */
-    OPJ_BYTE    *cblkdatabuffer;
-    /* Maximum size available in cblkdatabuffer */
-    OPJ_UINT32   cblkdatabuffersize;
-} opj_t1_t;
-
-/** @name Exported functions */
-/*@{*/
-/* ----------------------------------------------------------------------- */
-
-/**
-Encode the code-blocks of a tile
-@param t1 T1 handle
-@param tile The tile to encode
-@param tcp Tile coding parameters
-@param mct_norms  FIXME DOC
-@param mct_numcomps Number of components used for MCT
-*/
-OPJ_BOOL opj_t1_encode_cblks(opj_t1_t *t1,
-                             opj_tcd_tile_t *tile,
-                             opj_tcp_t *tcp,
-                             const OPJ_FLOAT64 * mct_norms,
-                             OPJ_UINT32 mct_numcomps);
-
-/**
-Decode the code-blocks of a tile
-@param tcd TCD handle
-@param pret Pointer to return value
-@param tilec The tile to decode
-@param tccp Tile coding parameters
-@param p_manager the event manager
-@param p_manager_mutex mutex for the event manager
-@param check_pterm whether PTERM correct termination should be checked
-*/
-void opj_t1_decode_cblks(opj_tcd_t* tcd,
-                         volatile OPJ_BOOL* pret,
-                         opj_tcd_tilecomp_t* tilec,
-                         opj_tccp_t* tccp,
-                         opj_event_mgr_t *p_manager,
-                         opj_mutex_t* p_manager_mutex,
-                         OPJ_BOOL check_pterm);
-
-
-
-/**
- * Creates a new Tier 1 handle
- * and initializes the look-up tables of the Tier-1 coder/decoder
- * @return a new T1 handle if successful, returns NULL otherwise
-*/
-opj_t1_t* opj_t1_create(OPJ_BOOL isEncoder);
-
-/**
- * Destroys a previously created T1 handle
- *
- * @param p_t1 Tier 1 handle to destroy
-*/
-void opj_t1_destroy(opj_t1_t *p_t1);
-/* ----------------------------------------------------------------------- */
-/*@}*/
-
-/*@}*/
-
-#endif /* OPJ_T1_H */
diff --git a/third_party/libopenjpeg20/t1_generate_luts.c b/third_party/libopenjpeg20/t1_generate_luts.c
deleted file mode 100644
index 9ad6f20..0000000
--- a/third_party/libopenjpeg20/t1_generate_luts.c
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2007, Callum Lerwick <seg@haxxed.com>
- * Copyright (c) 2012, Carl Hetherington
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "opj_includes.h"
-
-static int t1_init_ctxno_zc(OPJ_UINT32 f, OPJ_UINT32 orient)
-{
-    int h, v, d, n, t, hv;
-    n = 0;
-    h = ((f & T1_SIGMA_3) != 0) + ((f & T1_SIGMA_5) != 0);
-    v = ((f & T1_SIGMA_1) != 0) + ((f & T1_SIGMA_7) != 0);
-    d = ((f & T1_SIGMA_0) != 0) + ((f & T1_SIGMA_2) != 0) + ((
-                f & T1_SIGMA_8) != 0) + ((f & T1_SIGMA_6) != 0);
-
-    switch (orient) {
-    case 2:
-        t = h;
-        h = v;
-        v = t;
-    case 0:
-    case 1:
-        if (!h) {
-            if (!v) {
-                if (!d) {
-                    n = 0;
-                } else if (d == 1) {
-                    n = 1;
-                } else {
-                    n = 2;
-                }
-            } else if (v == 1) {
-                n = 3;
-            } else {
-                n = 4;
-            }
-        } else if (h == 1) {
-            if (!v) {
-                if (!d) {
-                    n = 5;
-                } else {
-                    n = 6;
-                }
-            } else {
-                n = 7;
-            }
-        } else {
-            n = 8;
-        }
-        break;
-    case 3:
-        hv = h + v;
-        if (!d) {
-            if (!hv) {
-                n = 0;
-            } else if (hv == 1) {
-                n = 1;
-            } else {
-                n = 2;
-            }
-        } else if (d == 1) {
-            if (!hv) {
-                n = 3;
-            } else if (hv == 1) {
-                n = 4;
-            } else {
-                n = 5;
-            }
-        } else if (d == 2) {
-            if (!hv) {
-                n = 6;
-            } else {
-                n = 7;
-            }
-        } else {
-            n = 8;
-        }
-        break;
-    }
-
-    return (T1_CTXNO_ZC + n);
-}
-
-static int t1_init_ctxno_sc(OPJ_UINT32 f)
-{
-    int hc, vc, n;
-    n = 0;
-
-    hc = opj_int_min(((f & (T1_LUT_SIG_E | T1_LUT_SGN_E)) ==
-                      T1_LUT_SIG_E) + ((f & (T1_LUT_SIG_W | T1_LUT_SGN_W)) == T1_LUT_SIG_W),
-                     1) - opj_int_min(((f & (T1_LUT_SIG_E | T1_LUT_SGN_E)) ==
-                                       (T1_LUT_SIG_E | T1_LUT_SGN_E)) +
-                                      ((f & (T1_LUT_SIG_W | T1_LUT_SGN_W)) ==
-                                       (T1_LUT_SIG_W | T1_LUT_SGN_W)), 1);
-
-    vc = opj_int_min(((f & (T1_LUT_SIG_N | T1_LUT_SGN_N)) ==
-                      T1_LUT_SIG_N) + ((f & (T1_LUT_SIG_S | T1_LUT_SGN_S)) == T1_LUT_SIG_S),
-                     1) - opj_int_min(((f & (T1_LUT_SIG_N | T1_LUT_SGN_N)) ==
-                                       (T1_LUT_SIG_N | T1_LUT_SGN_N)) +
-                                      ((f & (T1_LUT_SIG_S | T1_LUT_SGN_S)) ==
-                                       (T1_LUT_SIG_S | T1_LUT_SGN_S)), 1);
-
-    if (hc < 0) {
-        hc = -hc;
-        vc = -vc;
-    }
-    if (!hc) {
-        if (vc == -1) {
-            n = 1;
-        } else if (!vc) {
-            n = 0;
-        } else {
-            n = 1;
-        }
-    } else if (hc == 1) {
-        if (vc == -1) {
-            n = 2;
-        } else if (!vc) {
-            n = 3;
-        } else {
-            n = 4;
-        }
-    }
-
-    return (T1_CTXNO_SC + n);
-}
-
-static int t1_init_spb(OPJ_UINT32 f)
-{
-    int hc, vc, n;
-
-    hc = opj_int_min(((f & (T1_LUT_SIG_E | T1_LUT_SGN_E)) ==
-                      T1_LUT_SIG_E) + ((f & (T1_LUT_SIG_W | T1_LUT_SGN_W)) == T1_LUT_SIG_W),
-                     1) - opj_int_min(((f & (T1_LUT_SIG_E | T1_LUT_SGN_E)) ==
-                                       (T1_LUT_SIG_E | T1_LUT_SGN_E)) +
-                                      ((f & (T1_LUT_SIG_W | T1_LUT_SGN_W)) ==
-                                       (T1_LUT_SIG_W | T1_LUT_SGN_W)), 1);
-
-    vc = opj_int_min(((f & (T1_LUT_SIG_N | T1_LUT_SGN_N)) ==
-                      T1_LUT_SIG_N) + ((f & (T1_LUT_SIG_S | T1_LUT_SGN_S)) == T1_LUT_SIG_S),
-                     1) - opj_int_min(((f & (T1_LUT_SIG_N | T1_LUT_SGN_N)) ==
-                                       (T1_LUT_SIG_N | T1_LUT_SGN_N)) +
-                                      ((f & (T1_LUT_SIG_S | T1_LUT_SGN_S)) ==
-                                       (T1_LUT_SIG_S | T1_LUT_SGN_S)), 1);
-
-    if (!hc && !vc) {
-        n = 0;
-    } else {
-        n = (!(hc > 0 || (!hc && vc > 0)));
-    }
-
-    return n;
-}
-
-static void dump_array16(int array[], int size)
-{
-    int i;
-    --size;
-    for (i = 0; i < size; ++i) {
-        printf("0x%04x,", array[i]);
-        if (!((i + 1) & 0x7)) {
-            printf("\n    ");
-        } else {
-            printf(" ");
-        }
-    }
-    printf("0x%04x\n};\n\n", array[size]);
-}
-
-int main(int argc, char **argv)
-{
-    unsigned int i, j;
-    double u, v, t;
-
-    int lut_ctxno_zc[2048];
-    int lut_nmsedec_sig[1 << T1_NMSEDEC_BITS];
-    int lut_nmsedec_sig0[1 << T1_NMSEDEC_BITS];
-    int lut_nmsedec_ref[1 << T1_NMSEDEC_BITS];
-    int lut_nmsedec_ref0[1 << T1_NMSEDEC_BITS];
-    (void)argc;
-    (void)argv;
-
-    printf("/* This file was automatically generated by t1_generate_luts.c */\n\n");
-
-    /* lut_ctxno_zc */
-    for (j = 0; j < 4; ++j) {
-        for (i = 0; i < 512; ++i) {
-            OPJ_UINT32 orient = j;
-            if (orient == 2) {
-                orient = 1;
-            } else if (orient == 1) {
-                orient = 2;
-            }
-            lut_ctxno_zc[(orient << 9) | i] = t1_init_ctxno_zc(i, j);
-        }
-    }
-
-    printf("static const OPJ_BYTE lut_ctxno_zc[2048] = {\n    ");
-    for (i = 0; i < 2047; ++i) {
-        printf("%i,", lut_ctxno_zc[i]);
-        if (!((i + 1) & 0x1f)) {
-            printf("\n    ");
-        } else {
-            printf(" ");
-        }
-    }
-    printf("%i\n};\n\n", lut_ctxno_zc[2047]);
-
-    /* lut_ctxno_sc */
-    printf("static const OPJ_BYTE lut_ctxno_sc[256] = {\n    ");
-    for (i = 0; i < 255; ++i) {
-        printf("0x%x,", t1_init_ctxno_sc(i));
-        if (!((i + 1) & 0xf)) {
-            printf("\n    ");
-        } else {
-            printf(" ");
-        }
-    }
-    printf("0x%x\n};\n\n", t1_init_ctxno_sc(255));
-
-    /* lut_spb */
-    printf("static const OPJ_BYTE lut_spb[256] = {\n    ");
-    for (i = 0; i < 255; ++i) {
-        printf("%i,", t1_init_spb(i));
-        if (!((i + 1) & 0x1f)) {
-            printf("\n    ");
-        } else {
-            printf(" ");
-        }
-    }
-    printf("%i\n};\n\n", t1_init_spb(255));
-
-    /* FIXME FIXME FIXME */
-    /* fprintf(stdout,"nmsedec luts:\n"); */
-    for (i = 0U; i < (1U << T1_NMSEDEC_BITS); ++i) {
-        t = i / pow(2, T1_NMSEDEC_FRACBITS);
-        u = t;
-        v = t - 1.5;
-        lut_nmsedec_sig[i] =
-            opj_int_max(0,
-                        (int)(floor((u * u - v * v) * pow(2, T1_NMSEDEC_FRACBITS) + 0.5) / pow(2,
-                                T1_NMSEDEC_FRACBITS) * 8192.0));
-        lut_nmsedec_sig0[i] =
-            opj_int_max(0,
-                        (int)(floor((u * u) * pow(2, T1_NMSEDEC_FRACBITS) + 0.5) / pow(2,
-                                T1_NMSEDEC_FRACBITS) * 8192.0));
-        u = t - 1.0;
-        if (i & (1 << (T1_NMSEDEC_BITS - 1))) {
-            v = t - 1.5;
-        } else {
-            v = t - 0.5;
-        }
-        lut_nmsedec_ref[i] =
-            opj_int_max(0,
-                        (int)(floor((u * u - v * v) * pow(2, T1_NMSEDEC_FRACBITS) + 0.5) / pow(2,
-                                T1_NMSEDEC_FRACBITS) * 8192.0));
-        lut_nmsedec_ref0[i] =
-            opj_int_max(0,
-                        (int)(floor((u * u) * pow(2, T1_NMSEDEC_FRACBITS) + 0.5) / pow(2,
-                                T1_NMSEDEC_FRACBITS) * 8192.0));
-    }
-
-    printf("static const OPJ_INT16 lut_nmsedec_sig[1U << T1_NMSEDEC_BITS] = {\n    ");
-    dump_array16(lut_nmsedec_sig, 1U << T1_NMSEDEC_BITS);
-
-    printf("static const OPJ_INT16 lut_nmsedec_sig0[1U << T1_NMSEDEC_BITS] = {\n    ");
-    dump_array16(lut_nmsedec_sig0, 1U << T1_NMSEDEC_BITS);
-
-    printf("static const OPJ_INT16 lut_nmsedec_ref[1U << T1_NMSEDEC_BITS] = {\n    ");
-    dump_array16(lut_nmsedec_ref, 1U << T1_NMSEDEC_BITS);
-
-    printf("static const OPJ_INT16 lut_nmsedec_ref0[1U << T1_NMSEDEC_BITS] = {\n    ");
-    dump_array16(lut_nmsedec_ref0, 1U << T1_NMSEDEC_BITS);
-
-    return 0;
-}
diff --git a/third_party/libopenjpeg20/t2.c b/third_party/libopenjpeg20/t2.c
deleted file mode 100644
index 9825118..0000000
--- a/third_party/libopenjpeg20/t2.c
+++ /dev/null
@@ -1,1571 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
- * Copyright (c) 2012, CS Systemes d'Information, France
- * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "opj_includes.h"
-#include "opj_common.h"
-
-
-/** @defgroup T2 T2 - Implementation of a tier-2 coding */
-/*@{*/
-
-/** @name Local static functions */
-/*@{*/
-
-static void opj_t2_putcommacode(opj_bio_t *bio, OPJ_INT32 n);
-
-static OPJ_UINT32 opj_t2_getcommacode(opj_bio_t *bio);
-/**
-Variable length code for signalling delta Zil (truncation point)
-@param bio  Bit Input/Output component
-@param n    delta Zil
-*/
-static void opj_t2_putnumpasses(opj_bio_t *bio, OPJ_UINT32 n);
-static OPJ_UINT32 opj_t2_getnumpasses(opj_bio_t *bio);
-
-/**
-Encode a packet of a tile to a destination buffer
-@param tileno Number of the tile encoded
-@param tile Tile for which to write the packets
-@param tcp Tile coding parameters
-@param pi Packet identity
-@param dest Destination buffer
-@param p_data_written   FIXME DOC
-@param len Length of the destination buffer
-@param cstr_info Codestream information structure
-@param p_t2_mode If == THRESH_CALC In Threshold calculation ,If == FINAL_PASS Final pass
-@param p_manager the user event manager
-@return
-*/
-static OPJ_BOOL opj_t2_encode_packet(OPJ_UINT32 tileno,
-                                     opj_tcd_tile_t *tile,
-                                     opj_tcp_t *tcp,
-                                     opj_pi_iterator_t *pi,
-                                     OPJ_BYTE *dest,
-                                     OPJ_UINT32 * p_data_written,
-                                     OPJ_UINT32 len,
-                                     opj_codestream_info_t *cstr_info,
-                                     J2K_T2_MODE p_t2_mode,
-                                     opj_event_mgr_t *p_manager);
-
-/**
-Decode a packet of a tile from a source buffer
-@param t2 T2 handle
-@param tile Tile for which to write the packets
-@param tcp Tile coding parameters
-@param pi Packet identity
-@param src Source buffer
-@param data_read   FIXME DOC
-@param max_length  FIXME DOC
-@param pack_info Packet information
-@param p_manager the user event manager
-
-@return  FIXME DOC
-*/
-static OPJ_BOOL opj_t2_decode_packet(opj_t2_t* t2,
-                                     opj_tcd_tile_t *tile,
-                                     opj_tcp_t *tcp,
-                                     opj_pi_iterator_t *pi,
-                                     OPJ_BYTE *src,
-                                     OPJ_UINT32 * data_read,
-                                     OPJ_UINT32 max_length,
-                                     opj_packet_info_t *pack_info,
-                                     opj_event_mgr_t *p_manager);
-
-static OPJ_BOOL opj_t2_skip_packet(opj_t2_t* p_t2,
-                                   opj_tcd_tile_t *p_tile,
-                                   opj_tcp_t *p_tcp,
-                                   opj_pi_iterator_t *p_pi,
-                                   OPJ_BYTE *p_src,
-                                   OPJ_UINT32 * p_data_read,
-                                   OPJ_UINT32 p_max_length,
-                                   opj_packet_info_t *p_pack_info,
-                                   opj_event_mgr_t *p_manager);
-
-static OPJ_BOOL opj_t2_read_packet_header(opj_t2_t* p_t2,
-        opj_tcd_tile_t *p_tile,
-        opj_tcp_t *p_tcp,
-        opj_pi_iterator_t *p_pi,
-        OPJ_BOOL * p_is_data_present,
-        OPJ_BYTE *p_src_data,
-        OPJ_UINT32 * p_data_read,
-        OPJ_UINT32 p_max_length,
-        opj_packet_info_t *p_pack_info,
-        opj_event_mgr_t *p_manager);
-
-static OPJ_BOOL opj_t2_read_packet_data(opj_t2_t* p_t2,
-                                        opj_tcd_tile_t *p_tile,
-                                        opj_pi_iterator_t *p_pi,
-                                        OPJ_BYTE *p_src_data,
-                                        OPJ_UINT32 * p_data_read,
-                                        OPJ_UINT32 p_max_length,
-                                        opj_packet_info_t *pack_info,
-                                        opj_event_mgr_t *p_manager);
-
-static OPJ_BOOL opj_t2_skip_packet_data(opj_t2_t* p_t2,
-                                        opj_tcd_tile_t *p_tile,
-                                        opj_pi_iterator_t *p_pi,
-                                        OPJ_UINT32 * p_data_read,
-                                        OPJ_UINT32 p_max_length,
-                                        opj_packet_info_t *pack_info,
-                                        opj_event_mgr_t *p_manager);
-
-/**
-@param cblk
-@param index
-@param cblksty
-@param first
-*/
-static OPJ_BOOL opj_t2_init_seg(opj_tcd_cblk_dec_t* cblk,
-                                OPJ_UINT32 index,
-                                OPJ_UINT32 cblksty,
-                                OPJ_UINT32 first);
-
-/*@}*/
-
-/*@}*/
-
-/* ----------------------------------------------------------------------- */
-
-/* #define RESTART 0x04 */
-static void opj_t2_putcommacode(opj_bio_t *bio, OPJ_INT32 n)
-{
-    while (--n >= 0) {
-        opj_bio_write(bio, 1, 1);
-    }
-    opj_bio_write(bio, 0, 1);
-}
-
-static OPJ_UINT32 opj_t2_getcommacode(opj_bio_t *bio)
-{
-    OPJ_UINT32 n = 0;
-    while (opj_bio_read(bio, 1)) {
-        ++n;
-    }
-    return n;
-}
-
-static void opj_t2_putnumpasses(opj_bio_t *bio, OPJ_UINT32 n)
-{
-    if (n == 1) {
-        opj_bio_write(bio, 0, 1);
-    } else if (n == 2) {
-        opj_bio_write(bio, 2, 2);
-    } else if (n <= 5) {
-        opj_bio_write(bio, 0xc | (n - 3), 4);
-    } else if (n <= 36) {
-        opj_bio_write(bio, 0x1e0 | (n - 6), 9);
-    } else if (n <= 164) {
-        opj_bio_write(bio, 0xff80 | (n - 37), 16);
-    }
-}
-
-static OPJ_UINT32 opj_t2_getnumpasses(opj_bio_t *bio)
-{
-    OPJ_UINT32 n;
-    if (!opj_bio_read(bio, 1)) {
-        return 1;
-    }
-    if (!opj_bio_read(bio, 1)) {
-        return 2;
-    }
-    if ((n = opj_bio_read(bio, 2)) != 3) {
-        return (3 + n);
-    }
-    if ((n = opj_bio_read(bio, 5)) != 31) {
-        return (6 + n);
-    }
-    return (37 + opj_bio_read(bio, 7));
-}
-
-/* ----------------------------------------------------------------------- */
-
-OPJ_BOOL opj_t2_encode_packets(opj_t2_t* p_t2,
-                               OPJ_UINT32 p_tile_no,
-                               opj_tcd_tile_t *p_tile,
-                               OPJ_UINT32 p_maxlayers,
-                               OPJ_BYTE *p_dest,
-                               OPJ_UINT32 * p_data_written,
-                               OPJ_UINT32 p_max_len,
-                               opj_codestream_info_t *cstr_info,
-                               OPJ_UINT32 p_tp_num,
-                               OPJ_INT32 p_tp_pos,
-                               OPJ_UINT32 p_pino,
-                               J2K_T2_MODE p_t2_mode,
-                               opj_event_mgr_t *p_manager)
-{
-    OPJ_BYTE *l_current_data = p_dest;
-    OPJ_UINT32 l_nb_bytes = 0;
-    OPJ_UINT32 compno;
-    OPJ_UINT32 poc;
-    opj_pi_iterator_t *l_pi = 00;
-    opj_pi_iterator_t *l_current_pi = 00;
-    opj_image_t *l_image = p_t2->image;
-    opj_cp_t *l_cp = p_t2->cp;
-    opj_tcp_t *l_tcp = &l_cp->tcps[p_tile_no];
-    OPJ_UINT32 pocno = (l_cp->rsiz == OPJ_PROFILE_CINEMA_4K) ? 2 : 1;
-    OPJ_UINT32 l_max_comp = l_cp->m_specific_param.m_enc.m_max_comp_size > 0 ?
-                            l_image->numcomps : 1;
-    OPJ_UINT32 l_nb_pocs = l_tcp->numpocs + 1;
-
-    l_pi = opj_pi_initialise_encode(l_image, l_cp, p_tile_no, p_t2_mode);
-    if (!l_pi) {
-        return OPJ_FALSE;
-    }
-
-    * p_data_written = 0;
-
-    if (p_t2_mode == THRESH_CALC) { /* Calculating threshold */
-        l_current_pi = l_pi;
-
-        for (compno = 0; compno < l_max_comp; ++compno) {
-            OPJ_UINT32 l_comp_len = 0;
-            l_current_pi = l_pi;
-
-            for (poc = 0; poc < pocno ; ++poc) {
-                OPJ_UINT32 l_tp_num = compno;
-
-                /* TODO MSD : check why this function cannot fail (cf. v1) */
-                opj_pi_create_encode(l_pi, l_cp, p_tile_no, poc, l_tp_num, p_tp_pos, p_t2_mode);
-
-                if (l_current_pi->poc.prg == OPJ_PROG_UNKNOWN) {
-                    /* TODO ADE : add an error */
-                    opj_pi_destroy(l_pi, l_nb_pocs);
-                    return OPJ_FALSE;
-                }
-                while (opj_pi_next(l_current_pi)) {
-                    if (l_current_pi->layno < p_maxlayers) {
-                        l_nb_bytes = 0;
-
-                        if (! opj_t2_encode_packet(p_tile_no, p_tile, l_tcp, l_current_pi,
-                                                   l_current_data, &l_nb_bytes,
-                                                   p_max_len, cstr_info,
-                                                   p_t2_mode,
-                                                   p_manager)) {
-                            opj_pi_destroy(l_pi, l_nb_pocs);
-                            return OPJ_FALSE;
-                        }
-
-                        l_comp_len += l_nb_bytes;
-                        l_current_data += l_nb_bytes;
-                        p_max_len -= l_nb_bytes;
-
-                        * p_data_written += l_nb_bytes;
-                    }
-                }
-
-                if (l_cp->m_specific_param.m_enc.m_max_comp_size) {
-                    if (l_comp_len > l_cp->m_specific_param.m_enc.m_max_comp_size) {
-                        opj_pi_destroy(l_pi, l_nb_pocs);
-                        return OPJ_FALSE;
-                    }
-                }
-
-                ++l_current_pi;
-            }
-        }
-    } else { /* t2_mode == FINAL_PASS  */
-        opj_pi_create_encode(l_pi, l_cp, p_tile_no, p_pino, p_tp_num, p_tp_pos,
-                             p_t2_mode);
-
-        l_current_pi = &l_pi[p_pino];
-        if (l_current_pi->poc.prg == OPJ_PROG_UNKNOWN) {
-            /* TODO ADE : add an error */
-            opj_pi_destroy(l_pi, l_nb_pocs);
-            return OPJ_FALSE;
-        }
-        while (opj_pi_next(l_current_pi)) {
-            if (l_current_pi->layno < p_maxlayers) {
-                l_nb_bytes = 0;
-
-                if (! opj_t2_encode_packet(p_tile_no, p_tile, l_tcp, l_current_pi,
-                                           l_current_data, &l_nb_bytes, p_max_len,
-                                           cstr_info, p_t2_mode, p_manager)) {
-                    opj_pi_destroy(l_pi, l_nb_pocs);
-                    return OPJ_FALSE;
-                }
-
-                l_current_data += l_nb_bytes;
-                p_max_len -= l_nb_bytes;
-
-                * p_data_written += l_nb_bytes;
-
-                /* INDEX >> */
-                if (cstr_info) {
-                    if (cstr_info->index_write) {
-                        opj_tile_info_t *info_TL = &cstr_info->tile[p_tile_no];
-                        opj_packet_info_t *info_PK = &info_TL->packet[cstr_info->packno];
-                        if (!cstr_info->packno) {
-                            info_PK->start_pos = info_TL->end_header + 1;
-                        } else {
-                            info_PK->start_pos = ((l_cp->m_specific_param.m_enc.m_tp_on | l_tcp->POC) &&
-                                                  info_PK->start_pos) ? info_PK->start_pos : info_TL->packet[cstr_info->packno -
-                                                                            1].end_pos + 1;
-                        }
-                        info_PK->end_pos = info_PK->start_pos + l_nb_bytes - 1;
-                        info_PK->end_ph_pos += info_PK->start_pos -
-                                               1;  /* End of packet header which now only represents the distance
-                                                                                                                                                                                                                                                   to start of packet is incremented by value of start of packet*/
-                    }
-
-                    cstr_info->packno++;
-                }
-                /* << INDEX */
-                ++p_tile->packno;
-            }
-        }
-    }
-
-    opj_pi_destroy(l_pi, l_nb_pocs);
-
-    return OPJ_TRUE;
-}
-
-/* see issue 80 */
-#if 0
-#define JAS_FPRINTF fprintf
-#else
-/* issue 290 */
-static void opj_null_jas_fprintf(FILE* file, const char * format, ...)
-{
-    (void)file;
-    (void)format;
-}
-#define JAS_FPRINTF opj_null_jas_fprintf
-#endif
-
-OPJ_BOOL opj_t2_decode_packets(opj_tcd_t* tcd,
-                               opj_t2_t *p_t2,
-                               OPJ_UINT32 p_tile_no,
-                               opj_tcd_tile_t *p_tile,
-                               OPJ_BYTE *p_src,
-                               OPJ_UINT32 * p_data_read,
-                               OPJ_UINT32 p_max_len,
-                               opj_codestream_index_t *p_cstr_index,
-                               opj_event_mgr_t *p_manager)
-{
-    OPJ_BYTE *l_current_data = p_src;
-    opj_pi_iterator_t *l_pi = 00;
-    OPJ_UINT32 pino;
-    opj_image_t *l_image = p_t2->image;
-    opj_cp_t *l_cp = p_t2->cp;
-    opj_tcp_t *l_tcp = &(p_t2->cp->tcps[p_tile_no]);
-    OPJ_UINT32 l_nb_bytes_read;
-    OPJ_UINT32 l_nb_pocs = l_tcp->numpocs + 1;
-    opj_pi_iterator_t *l_current_pi = 00;
-#ifdef TODO_MSD
-    OPJ_UINT32 curtp = 0;
-    OPJ_UINT32 tp_start_packno;
-#endif
-    opj_packet_info_t *l_pack_info = 00;
-    opj_image_comp_t* l_img_comp = 00;
-
-    OPJ_ARG_NOT_USED(p_cstr_index);
-
-#ifdef TODO_MSD
-    if (p_cstr_index) {
-        l_pack_info = p_cstr_index->tile_index[p_tile_no].packet;
-    }
-#endif
-
-    /* create a packet iterator */
-    l_pi = opj_pi_create_decode(l_image, l_cp, p_tile_no);
-    if (!l_pi) {
-        return OPJ_FALSE;
-    }
-
-
-    l_current_pi = l_pi;
-
-    for (pino = 0; pino <= l_tcp->numpocs; ++pino) {
-
-        /* if the resolution needed is too low, one dim of the tilec could be equal to zero
-         * and no packets are used to decode this resolution and
-         * l_current_pi->resno is always >= p_tile->comps[l_current_pi->compno].minimum_num_resolutions
-         * and no l_img_comp->resno_decoded are computed
-         */
-        OPJ_BOOL* first_pass_failed = NULL;
-
-        if (l_current_pi->poc.prg == OPJ_PROG_UNKNOWN) {
-            /* TODO ADE : add an error */
-            opj_pi_destroy(l_pi, l_nb_pocs);
-            return OPJ_FALSE;
-        }
-
-        first_pass_failed = (OPJ_BOOL*)opj_malloc(l_image->numcomps * sizeof(OPJ_BOOL));
-        if (!first_pass_failed) {
-            opj_pi_destroy(l_pi, l_nb_pocs);
-            return OPJ_FALSE;
-        }
-        memset(first_pass_failed, OPJ_TRUE, l_image->numcomps * sizeof(OPJ_BOOL));
-
-        while (opj_pi_next(l_current_pi)) {
-            OPJ_BOOL skip_packet = OPJ_FALSE;
-            JAS_FPRINTF(stderr,
-                        "packet offset=00000166 prg=%d cmptno=%02d rlvlno=%02d prcno=%03d lyrno=%02d\n\n",
-                        l_current_pi->poc.prg1, l_current_pi->compno, l_current_pi->resno,
-                        l_current_pi->precno, l_current_pi->layno);
-
-            /* If the packet layer is greater or equal than the maximum */
-            /* number of layers, skip the packet */
-            if (l_current_pi->layno >= l_tcp->num_layers_to_decode) {
-                skip_packet = OPJ_TRUE;
-            }
-            /* If the packet resolution number is greater than the minimum */
-            /* number of resolution allowed, skip the packet */
-            else if (l_current_pi->resno >=
-                     p_tile->comps[l_current_pi->compno].minimum_num_resolutions) {
-                skip_packet = OPJ_TRUE;
-            } else {
-                /* If no precincts of any band intersects the area of interest, */
-                /* skip the packet */
-                OPJ_UINT32 bandno;
-                opj_tcd_tilecomp_t *tilec = &p_tile->comps[l_current_pi->compno];
-                opj_tcd_resolution_t *res = &tilec->resolutions[l_current_pi->resno];
-
-                skip_packet = OPJ_TRUE;
-                for (bandno = 0; bandno < res->numbands; ++bandno) {
-                    opj_tcd_band_t* band = &res->bands[bandno];
-                    opj_tcd_precinct_t* prec = &band->precincts[l_current_pi->precno];
-
-                    if (opj_tcd_is_subband_area_of_interest(tcd,
-                                                            l_current_pi->compno,
-                                                            l_current_pi->resno,
-                                                            band->bandno,
-                                                            (OPJ_UINT32)prec->x0,
-                                                            (OPJ_UINT32)prec->y0,
-                                                            (OPJ_UINT32)prec->x1,
-                                                            (OPJ_UINT32)prec->y1)) {
-                        skip_packet = OPJ_FALSE;
-                        break;
-                    }
-                }
-                /*
-                                printf("packet cmptno=%02d rlvlno=%02d prcno=%03d lyrno=%02d -> %s\n",
-                                    l_current_pi->compno, l_current_pi->resno,
-                                    l_current_pi->precno, l_current_pi->layno, skip_packet ? "skipped" : "kept");
-                */
-            }
-
-            if (!skip_packet) {
-                l_nb_bytes_read = 0;
-
-                first_pass_failed[l_current_pi->compno] = OPJ_FALSE;
-
-                if (! opj_t2_decode_packet(p_t2, p_tile, l_tcp, l_current_pi, l_current_data,
-                                           &l_nb_bytes_read, p_max_len, l_pack_info, p_manager)) {
-                    opj_pi_destroy(l_pi, l_nb_pocs);
-                    opj_free(first_pass_failed);
-                    return OPJ_FALSE;
-                }
-
-                l_img_comp = &(l_image->comps[l_current_pi->compno]);
-                l_img_comp->resno_decoded = opj_uint_max(l_current_pi->resno,
-                                            l_img_comp->resno_decoded);
-            } else {
-                l_nb_bytes_read = 0;
-                if (! opj_t2_skip_packet(p_t2, p_tile, l_tcp, l_current_pi, l_current_data,
-                                         &l_nb_bytes_read, p_max_len, l_pack_info, p_manager)) {
-                    opj_pi_destroy(l_pi, l_nb_pocs);
-                    opj_free(first_pass_failed);
-                    return OPJ_FALSE;
-                }
-            }
-
-            if (first_pass_failed[l_current_pi->compno]) {
-                l_img_comp = &(l_image->comps[l_current_pi->compno]);
-                if (l_img_comp->resno_decoded == 0) {
-                    l_img_comp->resno_decoded =
-                        p_tile->comps[l_current_pi->compno].minimum_num_resolutions - 1;
-                }
-            }
-
-            l_current_data += l_nb_bytes_read;
-            p_max_len -= l_nb_bytes_read;
-
-            /* INDEX >> */
-#ifdef TODO_MSD
-            if (p_cstr_info) {
-                opj_tile_info_v2_t *info_TL = &p_cstr_info->tile[p_tile_no];
-                opj_packet_info_t *info_PK = &info_TL->packet[p_cstr_info->packno];
-                tp_start_packno = 0;
-                if (!p_cstr_info->packno) {
-                    info_PK->start_pos = info_TL->end_header + 1;
-                } else if (info_TL->packet[p_cstr_info->packno - 1].end_pos >=
-                           (OPJ_INT32)
-                           p_cstr_info->tile[p_tile_no].tp[curtp].tp_end_pos) { /* New tile part */
-                    info_TL->tp[curtp].tp_numpacks = p_cstr_info->packno -
-                                                     tp_start_packno; /* Number of packets in previous tile-part */
-                    tp_start_packno = p_cstr_info->packno;
-                    curtp++;
-                    info_PK->start_pos = p_cstr_info->tile[p_tile_no].tp[curtp].tp_end_header + 1;
-                } else {
-                    info_PK->start_pos = (l_cp->m_specific_param.m_enc.m_tp_on &&
-                                          info_PK->start_pos) ? info_PK->start_pos : info_TL->packet[p_cstr_info->packno -
-                                                                      1].end_pos + 1;
-                }
-                info_PK->end_pos = info_PK->start_pos + l_nb_bytes_read - 1;
-                info_PK->end_ph_pos += info_PK->start_pos -
-                                       1;  /* End of packet header which now only represents the distance */
-                ++p_cstr_info->packno;
-            }
-#endif
-            /* << INDEX */
-        }
-        ++l_current_pi;
-
-        opj_free(first_pass_failed);
-    }
-    /* INDEX >> */
-#ifdef TODO_MSD
-    if
-    (p_cstr_info) {
-        p_cstr_info->tile[p_tile_no].tp[curtp].tp_numpacks = p_cstr_info->packno -
-                tp_start_packno; /* Number of packets in last tile-part */
-    }
-#endif
-    /* << INDEX */
-
-    /* don't forget to release pi */
-    opj_pi_destroy(l_pi, l_nb_pocs);
-    *p_data_read = (OPJ_UINT32)(l_current_data - p_src);
-    return OPJ_TRUE;
-}
-
-/* ----------------------------------------------------------------------- */
-
-/**
- * Creates a Tier 2 handle
- *
- * @param       p_image         Source or destination image
- * @param       p_cp            Image coding parameters.
- * @return              a new T2 handle if successful, NULL otherwise.
-*/
-opj_t2_t* opj_t2_create(opj_image_t *p_image, opj_cp_t *p_cp)
-{
-    /* create the t2 structure */
-    opj_t2_t *l_t2 = (opj_t2_t*)opj_calloc(1, sizeof(opj_t2_t));
-    if (!l_t2) {
-        return NULL;
-    }
-
-    l_t2->image = p_image;
-    l_t2->cp = p_cp;
-
-    return l_t2;
-}
-
-void opj_t2_destroy(opj_t2_t *t2)
-{
-    if (t2) {
-        opj_free(t2);
-    }
-}
-
-static OPJ_BOOL opj_t2_decode_packet(opj_t2_t* p_t2,
-                                     opj_tcd_tile_t *p_tile,
-                                     opj_tcp_t *p_tcp,
-                                     opj_pi_iterator_t *p_pi,
-                                     OPJ_BYTE *p_src,
-                                     OPJ_UINT32 * p_data_read,
-                                     OPJ_UINT32 p_max_length,
-                                     opj_packet_info_t *p_pack_info,
-                                     opj_event_mgr_t *p_manager)
-{
-    OPJ_BOOL l_read_data;
-    OPJ_UINT32 l_nb_bytes_read = 0;
-    OPJ_UINT32 l_nb_total_bytes_read = 0;
-
-    *p_data_read = 0;
-
-    if (! opj_t2_read_packet_header(p_t2, p_tile, p_tcp, p_pi, &l_read_data, p_src,
-                                    &l_nb_bytes_read, p_max_length, p_pack_info, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    p_src += l_nb_bytes_read;
-    l_nb_total_bytes_read += l_nb_bytes_read;
-    p_max_length -= l_nb_bytes_read;
-
-    /* we should read data for the packet */
-    if (l_read_data) {
-        l_nb_bytes_read = 0;
-
-        if (! opj_t2_read_packet_data(p_t2, p_tile, p_pi, p_src, &l_nb_bytes_read,
-                                      p_max_length, p_pack_info, p_manager)) {
-            return OPJ_FALSE;
-        }
-
-        l_nb_total_bytes_read += l_nb_bytes_read;
-    }
-
-    *p_data_read = l_nb_total_bytes_read;
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_t2_encode_packet(OPJ_UINT32 tileno,
-                                     opj_tcd_tile_t * tile,
-                                     opj_tcp_t * tcp,
-                                     opj_pi_iterator_t *pi,
-                                     OPJ_BYTE *dest,
-                                     OPJ_UINT32 * p_data_written,
-                                     OPJ_UINT32 length,
-                                     opj_codestream_info_t *cstr_info,
-                                     J2K_T2_MODE p_t2_mode,
-                                     opj_event_mgr_t *p_manager)
-{
-    OPJ_UINT32 bandno, cblkno;
-    OPJ_BYTE* c = dest;
-    OPJ_UINT32 l_nb_bytes;
-    OPJ_UINT32 compno = pi->compno;     /* component value */
-    OPJ_UINT32 resno  = pi->resno;      /* resolution level value */
-    OPJ_UINT32 precno = pi->precno;     /* precinct value */
-    OPJ_UINT32 layno  = pi->layno;      /* quality layer value */
-    OPJ_UINT32 l_nb_blocks;
-    opj_tcd_band_t *band = 00;
-    opj_tcd_cblk_enc_t* cblk = 00;
-    opj_tcd_pass_t *pass = 00;
-
-    opj_tcd_tilecomp_t *tilec = &tile->comps[compno];
-    opj_tcd_resolution_t *res = &tilec->resolutions[resno];
-
-    opj_bio_t *bio = 00;    /* BIO component */
-#ifdef ENABLE_EMPTY_PACKET_OPTIMIZATION
-    OPJ_BOOL packet_empty = OPJ_TRUE;
-#else
-    OPJ_BOOL packet_empty = OPJ_FALSE;
-#endif
-
-    /* <SOP 0xff91> */
-    if (tcp->csty & J2K_CP_CSTY_SOP) {
-        if (length < 6) {
-            if (p_t2_mode == FINAL_PASS) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "opj_t2_encode_packet(): only %u bytes remaining in "
-                              "output buffer. %u needed.\n",
-                              length, 6);
-            }
-            return OPJ_FALSE;
-        }
-        c[0] = 255;
-        c[1] = 145;
-        c[2] = 0;
-        c[3] = 4;
-#if 0
-        c[4] = (tile->packno % 65536) / 256;
-        c[5] = (tile->packno % 65536) % 256;
-#else
-        c[4] = (tile->packno >> 8) & 0xff; /* packno is uint32_t */
-        c[5] = tile->packno & 0xff;
-#endif
-        c += 6;
-        length -= 6;
-    }
-    /* </SOP> */
-
-    if (!layno) {
-        band = res->bands;
-
-        for (bandno = 0; bandno < res->numbands; ++bandno, ++band) {
-            opj_tcd_precinct_t *prc;
-
-            /* Skip empty bands */
-            if (opj_tcd_is_band_empty(band)) {
-                continue;
-            }
-
-            prc = &band->precincts[precno];
-            opj_tgt_reset(prc->incltree);
-            opj_tgt_reset(prc->imsbtree);
-
-            l_nb_blocks = prc->cw * prc->ch;
-            for (cblkno = 0; cblkno < l_nb_blocks; ++cblkno) {
-                cblk = &prc->cblks.enc[cblkno];
-
-                cblk->numpasses = 0;
-                opj_tgt_setvalue(prc->imsbtree, cblkno, band->numbps - (OPJ_INT32)cblk->numbps);
-            }
-        }
-    }
-
-    bio = opj_bio_create();
-    if (!bio) {
-        /* FIXME event manager error callback */
-        return OPJ_FALSE;
-    }
-    opj_bio_init_enc(bio, c, length);
-
-#ifdef ENABLE_EMPTY_PACKET_OPTIMIZATION
-    /* WARNING: this code branch is disabled, since it has been reported that */
-    /* such packets cause decoding issues with cinema J2K hardware */
-    /* decoders: https://groups.google.com/forum/#!topic/openjpeg/M7M_fLX_Bco */
-
-    /* Check if the packet is empty */
-    /* Note: we could also skip that step and always write a packet header */
-    band = res->bands;
-    for (bandno = 0; bandno < res->numbands; ++bandno, ++band) {
-        opj_tcd_precinct_t *prc;
-        /* Skip empty bands */
-        if (opj_tcd_is_band_empty(band)) {
-            continue;
-        }
-
-        prc = &band->precincts[precno];
-        l_nb_blocks = prc->cw * prc->ch;
-        cblk = prc->cblks.enc;
-        for (cblkno = 0; cblkno < l_nb_blocks; cblkno++, ++cblk) {
-            opj_tcd_layer_t *layer = &cblk->layers[layno];
-
-            /* if cblk not included, go to the next cblk  */
-            if (!layer->numpasses) {
-                continue;
-            }
-            packet_empty = OPJ_FALSE;
-            break;
-        }
-        if (!packet_empty) {
-            break;
-        }
-    }
-#endif
-    opj_bio_write(bio, packet_empty ? 0 : 1, 1);           /* Empty header bit */
-
-    /* Writing Packet header */
-    band = res->bands;
-    for (bandno = 0; !packet_empty &&
-            bandno < res->numbands; ++bandno, ++band)      {
-        opj_tcd_precinct_t *prc;
-
-        /* Skip empty bands */
-        if (opj_tcd_is_band_empty(band)) {
-            continue;
-        }
-
-        prc = &band->precincts[precno];
-        l_nb_blocks = prc->cw * prc->ch;
-        cblk = prc->cblks.enc;
-
-        for (cblkno = 0; cblkno < l_nb_blocks; ++cblkno) {
-            opj_tcd_layer_t *layer = &cblk->layers[layno];
-
-            if (!cblk->numpasses && layer->numpasses) {
-                opj_tgt_setvalue(prc->incltree, cblkno, (OPJ_INT32)layno);
-            }
-
-            ++cblk;
-        }
-
-        cblk = prc->cblks.enc;
-        for (cblkno = 0; cblkno < l_nb_blocks; cblkno++) {
-            opj_tcd_layer_t *layer = &cblk->layers[layno];
-            OPJ_UINT32 increment = 0;
-            OPJ_UINT32 nump = 0;
-            OPJ_UINT32 len = 0, passno;
-            OPJ_UINT32 l_nb_passes;
-
-            /* cblk inclusion bits */
-            if (!cblk->numpasses) {
-                opj_tgt_encode(bio, prc->incltree, cblkno, (OPJ_INT32)(layno + 1));
-            } else {
-                opj_bio_write(bio, layer->numpasses != 0, 1);
-            }
-
-            /* if cblk not included, go to the next cblk  */
-            if (!layer->numpasses) {
-                ++cblk;
-                continue;
-            }
-
-            /* if first instance of cblk --> zero bit-planes information */
-            if (!cblk->numpasses) {
-                cblk->numlenbits = 3;
-                opj_tgt_encode(bio, prc->imsbtree, cblkno, 999);
-            }
-
-            /* number of coding passes included */
-            opj_t2_putnumpasses(bio, layer->numpasses);
-            l_nb_passes = cblk->numpasses + layer->numpasses;
-            pass = cblk->passes +  cblk->numpasses;
-
-            /* computation of the increase of the length indicator and insertion in the header     */
-            for (passno = cblk->numpasses; passno < l_nb_passes; ++passno) {
-                ++nump;
-                len += pass->len;
-
-                if (pass->term || passno == (cblk->numpasses + layer->numpasses) - 1) {
-                    increment = (OPJ_UINT32)opj_int_max((OPJ_INT32)increment,
-                                                        opj_int_floorlog2((OPJ_INT32)len) + 1
-                                                        - ((OPJ_INT32)cblk->numlenbits + opj_int_floorlog2((OPJ_INT32)nump)));
-                    len = 0;
-                    nump = 0;
-                }
-
-                ++pass;
-            }
-            opj_t2_putcommacode(bio, (OPJ_INT32)increment);
-
-            /* computation of the new Length indicator */
-            cblk->numlenbits += increment;
-
-            pass = cblk->passes +  cblk->numpasses;
-            /* insertion of the codeword segment length */
-            for (passno = cblk->numpasses; passno < l_nb_passes; ++passno) {
-                nump++;
-                len += pass->len;
-
-                if (pass->term || passno == (cblk->numpasses + layer->numpasses) - 1) {
-                    opj_bio_write(bio, (OPJ_UINT32)len,
-                                  cblk->numlenbits + (OPJ_UINT32)opj_int_floorlog2((OPJ_INT32)nump));
-                    len = 0;
-                    nump = 0;
-                }
-                ++pass;
-            }
-
-            ++cblk;
-        }
-    }
-
-    if (!opj_bio_flush(bio)) {
-        opj_bio_destroy(bio);
-        return OPJ_FALSE;               /* modified to eliminate longjmp !! */
-    }
-
-    l_nb_bytes = (OPJ_UINT32)opj_bio_numbytes(bio);
-    c += l_nb_bytes;
-    length -= l_nb_bytes;
-
-    opj_bio_destroy(bio);
-
-    /* <EPH 0xff92> */
-    if (tcp->csty & J2K_CP_CSTY_EPH) {
-        if (length < 2) {
-            if (p_t2_mode == FINAL_PASS) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "opj_t2_encode_packet(): only %u bytes remaining in "
-                              "output buffer. %u needed.\n",
-                              length, 2);
-            }
-            return OPJ_FALSE;
-        }
-        c[0] = 255;
-        c[1] = 146;
-        c += 2;
-        length -= 2;
-    }
-    /* </EPH> */
-
-    /* << INDEX */
-    /* End of packet header position. Currently only represents the distance to start of packet
-       Will be updated later by incrementing with packet start value*/
-    if (cstr_info && cstr_info->index_write) {
-        opj_packet_info_t *info_PK = &cstr_info->tile[tileno].packet[cstr_info->packno];
-        info_PK->end_ph_pos = (OPJ_INT32)(c - dest);
-    }
-    /* INDEX >> */
-
-    /* Writing the packet body */
-    band = res->bands;
-    for (bandno = 0; !packet_empty && bandno < res->numbands; bandno++, ++band) {
-        opj_tcd_precinct_t *prc;
-
-        /* Skip empty bands */
-        if (opj_tcd_is_band_empty(band)) {
-            continue;
-        }
-
-        prc = &band->precincts[precno];
-        l_nb_blocks = prc->cw * prc->ch;
-        cblk = prc->cblks.enc;
-
-        for (cblkno = 0; cblkno < l_nb_blocks; ++cblkno) {
-            opj_tcd_layer_t *layer = &cblk->layers[layno];
-
-            if (!layer->numpasses) {
-                ++cblk;
-                continue;
-            }
-
-            if (layer->len > length) {
-                if (p_t2_mode == FINAL_PASS) {
-                    opj_event_msg(p_manager, EVT_ERROR,
-                                  "opj_t2_encode_packet(): only %u bytes remaining in "
-                                  "output buffer. %u needed.\n",
-                                  length, layer->len);
-                }
-                return OPJ_FALSE;
-            }
-
-            memcpy(c, layer->data, layer->len);
-            cblk->numpasses += layer->numpasses;
-            c += layer->len;
-            length -= layer->len;
-
-            /* << INDEX */
-            if (cstr_info && cstr_info->index_write) {
-                opj_packet_info_t *info_PK = &cstr_info->tile[tileno].packet[cstr_info->packno];
-                info_PK->disto += layer->disto;
-                if (cstr_info->D_max < info_PK->disto) {
-                    cstr_info->D_max = info_PK->disto;
-                }
-            }
-
-            ++cblk;
-            /* INDEX >> */
-        }
-    }
-
-    assert(c >= dest);
-    * p_data_written += (OPJ_UINT32)(c - dest);
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_t2_skip_packet(opj_t2_t* p_t2,
-                                   opj_tcd_tile_t *p_tile,
-                                   opj_tcp_t *p_tcp,
-                                   opj_pi_iterator_t *p_pi,
-                                   OPJ_BYTE *p_src,
-                                   OPJ_UINT32 * p_data_read,
-                                   OPJ_UINT32 p_max_length,
-                                   opj_packet_info_t *p_pack_info,
-                                   opj_event_mgr_t *p_manager)
-{
-    OPJ_BOOL l_read_data;
-    OPJ_UINT32 l_nb_bytes_read = 0;
-    OPJ_UINT32 l_nb_total_bytes_read = 0;
-
-    *p_data_read = 0;
-
-    if (! opj_t2_read_packet_header(p_t2, p_tile, p_tcp, p_pi, &l_read_data, p_src,
-                                    &l_nb_bytes_read, p_max_length, p_pack_info, p_manager)) {
-        return OPJ_FALSE;
-    }
-
-    p_src += l_nb_bytes_read;
-    l_nb_total_bytes_read += l_nb_bytes_read;
-    p_max_length -= l_nb_bytes_read;
-
-    /* we should read data for the packet */
-    if (l_read_data) {
-        l_nb_bytes_read = 0;
-
-        if (! opj_t2_skip_packet_data(p_t2, p_tile, p_pi, &l_nb_bytes_read,
-                                      p_max_length, p_pack_info, p_manager)) {
-            return OPJ_FALSE;
-        }
-
-        l_nb_total_bytes_read += l_nb_bytes_read;
-    }
-    *p_data_read = l_nb_total_bytes_read;
-
-    return OPJ_TRUE;
-}
-
-
-static OPJ_BOOL opj_t2_read_packet_header(opj_t2_t* p_t2,
-        opj_tcd_tile_t *p_tile,
-        opj_tcp_t *p_tcp,
-        opj_pi_iterator_t *p_pi,
-        OPJ_BOOL * p_is_data_present,
-        OPJ_BYTE *p_src_data,
-        OPJ_UINT32 * p_data_read,
-        OPJ_UINT32 p_max_length,
-        opj_packet_info_t *p_pack_info,
-        opj_event_mgr_t *p_manager)
-
-{
-    /* loop */
-    OPJ_UINT32 bandno, cblkno;
-    OPJ_UINT32 l_nb_code_blocks;
-    OPJ_UINT32 l_remaining_length;
-    OPJ_UINT32 l_header_length;
-    OPJ_UINT32 * l_modified_length_ptr = 00;
-    OPJ_BYTE *l_current_data = p_src_data;
-    opj_cp_t *l_cp = p_t2->cp;
-    opj_bio_t *l_bio = 00;  /* BIO component */
-    opj_tcd_band_t *l_band = 00;
-    opj_tcd_cblk_dec_t* l_cblk = 00;
-    opj_tcd_resolution_t* l_res =
-        &p_tile->comps[p_pi->compno].resolutions[p_pi->resno];
-
-    OPJ_BYTE *l_header_data = 00;
-    OPJ_BYTE **l_header_data_start = 00;
-
-    OPJ_UINT32 l_present;
-
-    if (p_pi->layno == 0) {
-        l_band = l_res->bands;
-
-        /* reset tagtrees */
-        for (bandno = 0; bandno < l_res->numbands; ++bandno) {
-            if (!opj_tcd_is_band_empty(l_band)) {
-                opj_tcd_precinct_t *l_prc = &l_band->precincts[p_pi->precno];
-                if (!(p_pi->precno < (l_band->precincts_data_size / sizeof(
-                                          opj_tcd_precinct_t)))) {
-                    opj_event_msg(p_manager, EVT_ERROR, "Invalid precinct\n");
-                    return OPJ_FALSE;
-                }
-
-
-                opj_tgt_reset(l_prc->incltree);
-                opj_tgt_reset(l_prc->imsbtree);
-                l_cblk = l_prc->cblks.dec;
-
-                l_nb_code_blocks = l_prc->cw * l_prc->ch;
-                for (cblkno = 0; cblkno < l_nb_code_blocks; ++cblkno) {
-                    l_cblk->numsegs = 0;
-                    l_cblk->real_num_segs = 0;
-                    ++l_cblk;
-                }
-            }
-
-            ++l_band;
-        }
-    }
-
-    /* SOP markers */
-
-    if (p_tcp->csty & J2K_CP_CSTY_SOP) {
-        if (p_max_length < 6) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "Not enough space for expected SOP marker\n");
-        } else if ((*l_current_data) != 0xff || (*(l_current_data + 1) != 0x91)) {
-            opj_event_msg(p_manager, EVT_WARNING, "Expected SOP marker\n");
-        } else {
-            l_current_data += 6;
-        }
-
-        /** TODO : check the Nsop value */
-    }
-
-    /*
-    When the marker PPT/PPM is used the packet header are store in PPT/PPM marker
-    This part deal with this caracteristic
-    step 1: Read packet header in the saved structure
-    step 2: Return to codestream for decoding
-    */
-
-    l_bio = opj_bio_create();
-    if (! l_bio) {
-        return OPJ_FALSE;
-    }
-
-    if (l_cp->ppm == 1) { /* PPM */
-        l_header_data_start = &l_cp->ppm_data;
-        l_header_data = *l_header_data_start;
-        l_modified_length_ptr = &(l_cp->ppm_len);
-
-    } else if (p_tcp->ppt == 1) { /* PPT */
-        l_header_data_start = &(p_tcp->ppt_data);
-        l_header_data = *l_header_data_start;
-        l_modified_length_ptr = &(p_tcp->ppt_len);
-    } else { /* Normal Case */
-        l_header_data_start = &(l_current_data);
-        l_header_data = *l_header_data_start;
-        l_remaining_length = (OPJ_UINT32)(p_src_data + p_max_length - l_header_data);
-        l_modified_length_ptr = &(l_remaining_length);
-    }
-
-    opj_bio_init_dec(l_bio, l_header_data, *l_modified_length_ptr);
-
-    l_present = opj_bio_read(l_bio, 1);
-    JAS_FPRINTF(stderr, "present=%d \n", l_present);
-    if (!l_present) {
-        /* TODO MSD: no test to control the output of this function*/
-        opj_bio_inalign(l_bio);
-        l_header_data += opj_bio_numbytes(l_bio);
-        opj_bio_destroy(l_bio);
-
-        /* EPH markers */
-        if (p_tcp->csty & J2K_CP_CSTY_EPH) {
-            if ((*l_modified_length_ptr - (OPJ_UINT32)(l_header_data -
-                    *l_header_data_start)) < 2U) {
-                opj_event_msg(p_manager, EVT_WARNING,
-                              "Not enough space for expected EPH marker\n");
-            } else if ((*l_header_data) != 0xff || (*(l_header_data + 1) != 0x92)) {
-                opj_event_msg(p_manager, EVT_WARNING, "Expected EPH marker\n");
-            } else {
-                l_header_data += 2;
-            }
-        }
-
-        l_header_length = (OPJ_UINT32)(l_header_data - *l_header_data_start);
-        *l_modified_length_ptr -= l_header_length;
-        *l_header_data_start += l_header_length;
-
-        /* << INDEX */
-        /* End of packet header position. Currently only represents the distance to start of packet
-           Will be updated later by incrementing with packet start value */
-        if (p_pack_info) {
-            p_pack_info->end_ph_pos = (OPJ_INT32)(l_current_data - p_src_data);
-        }
-        /* INDEX >> */
-
-        * p_is_data_present = OPJ_FALSE;
-        *p_data_read = (OPJ_UINT32)(l_current_data - p_src_data);
-        return OPJ_TRUE;
-    }
-
-    l_band = l_res->bands;
-    for (bandno = 0; bandno < l_res->numbands; ++bandno, ++l_band) {
-        opj_tcd_precinct_t *l_prc = &(l_band->precincts[p_pi->precno]);
-
-        if (opj_tcd_is_band_empty(l_band)) {
-            continue;
-        }
-
-        l_nb_code_blocks = l_prc->cw * l_prc->ch;
-        l_cblk = l_prc->cblks.dec;
-        for (cblkno = 0; cblkno < l_nb_code_blocks; cblkno++) {
-            OPJ_UINT32 l_included, l_increment, l_segno;
-            OPJ_INT32 n;
-
-            /* if cblk not yet included before --> inclusion tagtree */
-            if (!l_cblk->numsegs) {
-                l_included = opj_tgt_decode(l_bio, l_prc->incltree, cblkno,
-                                            (OPJ_INT32)(p_pi->layno + 1));
-                /* else one bit */
-            } else {
-                l_included = opj_bio_read(l_bio, 1);
-            }
-
-            /* if cblk not included */
-            if (!l_included) {
-                l_cblk->numnewpasses = 0;
-                ++l_cblk;
-                JAS_FPRINTF(stderr, "included=%d \n", l_included);
-                continue;
-            }
-
-            /* if cblk not yet included --> zero-bitplane tagtree */
-            if (!l_cblk->numsegs) {
-                OPJ_UINT32 i = 0;
-
-                while (!opj_tgt_decode(l_bio, l_prc->imsbtree, cblkno, (OPJ_INT32)i)) {
-                    ++i;
-                }
-
-                l_cblk->numbps = (OPJ_UINT32)l_band->numbps + 1 - i;
-                l_cblk->numlenbits = 3;
-            }
-
-            /* number of coding passes */
-            l_cblk->numnewpasses = opj_t2_getnumpasses(l_bio);
-            l_increment = opj_t2_getcommacode(l_bio);
-
-            /* length indicator increment */
-            l_cblk->numlenbits += l_increment;
-            l_segno = 0;
-
-            if (!l_cblk->numsegs) {
-                if (! opj_t2_init_seg(l_cblk, l_segno, p_tcp->tccps[p_pi->compno].cblksty, 1)) {
-                    opj_bio_destroy(l_bio);
-                    return OPJ_FALSE;
-                }
-            } else {
-                l_segno = l_cblk->numsegs - 1;
-                if (l_cblk->segs[l_segno].numpasses == l_cblk->segs[l_segno].maxpasses) {
-                    ++l_segno;
-                    if (! opj_t2_init_seg(l_cblk, l_segno, p_tcp->tccps[p_pi->compno].cblksty, 0)) {
-                        opj_bio_destroy(l_bio);
-                        return OPJ_FALSE;
-                    }
-                }
-            }
-            n = (OPJ_INT32)l_cblk->numnewpasses;
-
-            do {
-                OPJ_UINT32 bit_number;
-                l_cblk->segs[l_segno].numnewpasses = (OPJ_UINT32)opj_int_min((OPJ_INT32)(
-                        l_cblk->segs[l_segno].maxpasses - l_cblk->segs[l_segno].numpasses), n);
-                bit_number = l_cblk->numlenbits + opj_uint_floorlog2(
-                                 l_cblk->segs[l_segno].numnewpasses);
-                if (bit_number > 32) {
-                    opj_event_msg(p_manager, EVT_ERROR,
-                                  "Invalid bit number %d in opj_t2_read_packet_header()\n",
-                                  bit_number);
-                    opj_bio_destroy(l_bio);
-                    return OPJ_FALSE;
-                }
-                l_cblk->segs[l_segno].newlen = opj_bio_read(l_bio, bit_number);
-                JAS_FPRINTF(stderr, "included=%d numnewpasses=%d increment=%d len=%d \n",
-                            l_included, l_cblk->segs[l_segno].numnewpasses, l_increment,
-                            l_cblk->segs[l_segno].newlen);
-
-                n -= (OPJ_INT32)l_cblk->segs[l_segno].numnewpasses;
-                if (n > 0) {
-                    ++l_segno;
-
-                    if (! opj_t2_init_seg(l_cblk, l_segno, p_tcp->tccps[p_pi->compno].cblksty, 0)) {
-                        opj_bio_destroy(l_bio);
-                        return OPJ_FALSE;
-                    }
-                }
-            } while (n > 0);
-
-            ++l_cblk;
-        }
-    }
-
-    if (!opj_bio_inalign(l_bio)) {
-        opj_bio_destroy(l_bio);
-        return OPJ_FALSE;
-    }
-
-    l_header_data += opj_bio_numbytes(l_bio);
-    opj_bio_destroy(l_bio);
-
-    /* EPH markers */
-    if (p_tcp->csty & J2K_CP_CSTY_EPH) {
-        if ((*l_modified_length_ptr - (OPJ_UINT32)(l_header_data -
-                *l_header_data_start)) < 2U) {
-            opj_event_msg(p_manager, EVT_WARNING,
-                          "Not enough space for expected EPH marker\n");
-        } else if ((*l_header_data) != 0xff || (*(l_header_data + 1) != 0x92)) {
-            opj_event_msg(p_manager, EVT_WARNING, "Expected EPH marker\n");
-        } else {
-            l_header_data += 2;
-        }
-    }
-
-    l_header_length = (OPJ_UINT32)(l_header_data - *l_header_data_start);
-    JAS_FPRINTF(stderr, "hdrlen=%d \n", l_header_length);
-    JAS_FPRINTF(stderr, "packet body\n");
-    *l_modified_length_ptr -= l_header_length;
-    *l_header_data_start += l_header_length;
-
-    /* << INDEX */
-    /* End of packet header position. Currently only represents the distance to start of packet
-     Will be updated later by incrementing with packet start value */
-    if (p_pack_info) {
-        p_pack_info->end_ph_pos = (OPJ_INT32)(l_current_data - p_src_data);
-    }
-    /* INDEX >> */
-
-    *p_is_data_present = OPJ_TRUE;
-    *p_data_read = (OPJ_UINT32)(l_current_data - p_src_data);
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_t2_read_packet_data(opj_t2_t* p_t2,
-                                        opj_tcd_tile_t *p_tile,
-                                        opj_pi_iterator_t *p_pi,
-                                        OPJ_BYTE *p_src_data,
-                                        OPJ_UINT32 * p_data_read,
-                                        OPJ_UINT32 p_max_length,
-                                        opj_packet_info_t *pack_info,
-                                        opj_event_mgr_t* p_manager)
-{
-    OPJ_UINT32 bandno, cblkno;
-    OPJ_UINT32 l_nb_code_blocks;
-    OPJ_BYTE *l_current_data = p_src_data;
-    opj_tcd_band_t *l_band = 00;
-    opj_tcd_cblk_dec_t* l_cblk = 00;
-    opj_tcd_resolution_t* l_res =
-        &p_tile->comps[p_pi->compno].resolutions[p_pi->resno];
-
-    OPJ_ARG_NOT_USED(p_t2);
-    OPJ_ARG_NOT_USED(pack_info);
-
-    l_band = l_res->bands;
-    for (bandno = 0; bandno < l_res->numbands; ++bandno) {
-        opj_tcd_precinct_t *l_prc = &l_band->precincts[p_pi->precno];
-
-        if ((l_band->x1 - l_band->x0 == 0) || (l_band->y1 - l_band->y0 == 0)) {
-            ++l_band;
-            continue;
-        }
-
-        l_nb_code_blocks = l_prc->cw * l_prc->ch;
-        l_cblk = l_prc->cblks.dec;
-
-        for (cblkno = 0; cblkno < l_nb_code_blocks; ++cblkno) {
-            opj_tcd_seg_t *l_seg = 00;
-
-            if (!l_cblk->numnewpasses) {
-                /* nothing to do */
-                ++l_cblk;
-                continue;
-            }
-
-            if (!l_cblk->numsegs) {
-                l_seg = l_cblk->segs;
-                ++l_cblk->numsegs;
-            } else {
-                l_seg = &l_cblk->segs[l_cblk->numsegs - 1];
-
-                if (l_seg->numpasses == l_seg->maxpasses) {
-                    ++l_seg;
-                    ++l_cblk->numsegs;
-                }
-            }
-
-            do {
-                /* Check possible overflow (on l_current_data only, assumes input args already checked) then size */
-                if ((((OPJ_SIZE_T)l_current_data + (OPJ_SIZE_T)l_seg->newlen) <
-                        (OPJ_SIZE_T)l_current_data) ||
-                        (l_current_data + l_seg->newlen > p_src_data + p_max_length)) {
-                    opj_event_msg(p_manager, EVT_ERROR,
-                                  "read: segment too long (%d) with max (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n",
-                                  l_seg->newlen, p_max_length, cblkno, p_pi->precno, bandno, p_pi->resno,
-                                  p_pi->compno);
-                    return OPJ_FALSE;
-                }
-
-#ifdef USE_JPWL
-                /* we need here a j2k handle to verify if making a check to
-                the validity of cblocks parameters is selected from user (-W) */
-
-                /* let's check that we are not exceeding */
-                if ((l_cblk->len + l_seg->newlen) > 8192) {
-                    opj_event_msg(p_manager, EVT_WARNING,
-                                  "JPWL: segment too long (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n",
-                                  l_seg->newlen, cblkno, p_pi->precno, bandno, p_pi->resno, p_pi->compno);
-                    if (!JPWL_ASSUME) {
-                        opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
-                        return OPJ_FALSE;
-                    }
-                    l_seg->newlen = 8192 - l_cblk->len;
-                    opj_event_msg(p_manager, EVT_WARNING, "      - truncating segment to %d\n",
-                                  l_seg->newlen);
-                    break;
-                };
-
-#endif /* USE_JPWL */
-
-                if (l_cblk->numchunks == l_cblk->numchunksalloc) {
-                    OPJ_UINT32 l_numchunksalloc = l_cblk->numchunksalloc * 2 + 1;
-                    opj_tcd_seg_data_chunk_t* l_chunks =
-                        (opj_tcd_seg_data_chunk_t*)opj_realloc(l_cblk->chunks,
-                                l_numchunksalloc * sizeof(opj_tcd_seg_data_chunk_t));
-                    if (l_chunks == NULL) {
-                        opj_event_msg(p_manager, EVT_ERROR,
-                                      "cannot allocate opj_tcd_seg_data_chunk_t* array");
-                        return OPJ_FALSE;
-                    }
-                    l_cblk->chunks = l_chunks;
-                    l_cblk->numchunksalloc = l_numchunksalloc;
-                }
-
-                l_cblk->chunks[l_cblk->numchunks].data = l_current_data;
-                l_cblk->chunks[l_cblk->numchunks].len = l_seg->newlen;
-                l_cblk->numchunks ++;
-
-                l_current_data += l_seg->newlen;
-                l_seg->len += l_seg->newlen;
-                l_seg->numpasses += l_seg->numnewpasses;
-                l_cblk->numnewpasses -= l_seg->numnewpasses;
-
-                l_seg->real_num_passes = l_seg->numpasses;
-
-                if (l_cblk->numnewpasses > 0) {
-                    ++l_seg;
-                    ++l_cblk->numsegs;
-                }
-            } while (l_cblk->numnewpasses > 0);
-
-            l_cblk->real_num_segs = l_cblk->numsegs;
-            ++l_cblk;
-        } /* next code_block */
-
-        ++l_band;
-    }
-
-    *(p_data_read) = (OPJ_UINT32)(l_current_data - p_src_data);
-
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_t2_skip_packet_data(opj_t2_t* p_t2,
-                                        opj_tcd_tile_t *p_tile,
-                                        opj_pi_iterator_t *p_pi,
-                                        OPJ_UINT32 * p_data_read,
-                                        OPJ_UINT32 p_max_length,
-                                        opj_packet_info_t *pack_info,
-                                        opj_event_mgr_t *p_manager)
-{
-    OPJ_UINT32 bandno, cblkno;
-    OPJ_UINT32 l_nb_code_blocks;
-    opj_tcd_band_t *l_band = 00;
-    opj_tcd_cblk_dec_t* l_cblk = 00;
-    opj_tcd_resolution_t* l_res =
-        &p_tile->comps[p_pi->compno].resolutions[p_pi->resno];
-
-    OPJ_ARG_NOT_USED(p_t2);
-    OPJ_ARG_NOT_USED(pack_info);
-
-    *p_data_read = 0;
-    l_band = l_res->bands;
-
-    for (bandno = 0; bandno < l_res->numbands; ++bandno) {
-        opj_tcd_precinct_t *l_prc = &l_band->precincts[p_pi->precno];
-
-        if ((l_band->x1 - l_band->x0 == 0) || (l_band->y1 - l_band->y0 == 0)) {
-            ++l_band;
-            continue;
-        }
-
-        l_nb_code_blocks = l_prc->cw * l_prc->ch;
-        l_cblk = l_prc->cblks.dec;
-
-        for (cblkno = 0; cblkno < l_nb_code_blocks; ++cblkno) {
-            opj_tcd_seg_t *l_seg = 00;
-
-            if (!l_cblk->numnewpasses) {
-                /* nothing to do */
-                ++l_cblk;
-                continue;
-            }
-
-            if (!l_cblk->numsegs) {
-                l_seg = l_cblk->segs;
-                ++l_cblk->numsegs;
-            } else {
-                l_seg = &l_cblk->segs[l_cblk->numsegs - 1];
-
-                if (l_seg->numpasses == l_seg->maxpasses) {
-                    ++l_seg;
-                    ++l_cblk->numsegs;
-                }
-            }
-
-            do {
-                /* Check possible overflow then size */
-                if (((*p_data_read + l_seg->newlen) < (*p_data_read)) ||
-                        ((*p_data_read + l_seg->newlen) > p_max_length)) {
-                    opj_event_msg(p_manager, EVT_ERROR,
-                                  "skip: segment too long (%d) with max (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n",
-                                  l_seg->newlen, p_max_length, cblkno, p_pi->precno, bandno, p_pi->resno,
-                                  p_pi->compno);
-                    return OPJ_FALSE;
-                }
-
-#ifdef USE_JPWL
-                /* we need here a j2k handle to verify if making a check to
-                the validity of cblocks parameters is selected from user (-W) */
-
-                /* let's check that we are not exceeding */
-                if ((l_cblk->len + l_seg->newlen) > 8192) {
-                    opj_event_msg(p_manager, EVT_WARNING,
-                                  "JPWL: segment too long (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n",
-                                  l_seg->newlen, cblkno, p_pi->precno, bandno, p_pi->resno, p_pi->compno);
-                    if (!JPWL_ASSUME) {
-                        opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
-                        return -999;
-                    }
-                    l_seg->newlen = 8192 - l_cblk->len;
-                    opj_event_msg(p_manager, EVT_WARNING, "      - truncating segment to %d\n",
-                                  l_seg->newlen);
-                    break;
-                };
-
-#endif /* USE_JPWL */
-                JAS_FPRINTF(stderr, "p_data_read (%d) newlen (%d) \n", *p_data_read,
-                            l_seg->newlen);
-                *(p_data_read) += l_seg->newlen;
-
-                l_seg->numpasses += l_seg->numnewpasses;
-                l_cblk->numnewpasses -= l_seg->numnewpasses;
-                if (l_cblk->numnewpasses > 0) {
-                    ++l_seg;
-                    ++l_cblk->numsegs;
-                }
-            } while (l_cblk->numnewpasses > 0);
-
-            ++l_cblk;
-        }
-
-        ++l_band;
-    }
-
-    return OPJ_TRUE;
-}
-
-
-static OPJ_BOOL opj_t2_init_seg(opj_tcd_cblk_dec_t* cblk,
-                                OPJ_UINT32 index,
-                                OPJ_UINT32 cblksty,
-                                OPJ_UINT32 first)
-{
-    opj_tcd_seg_t* seg = 00;
-    OPJ_UINT32 l_nb_segs = index + 1;
-
-    if (l_nb_segs > cblk->m_current_max_segs) {
-        opj_tcd_seg_t* new_segs;
-        OPJ_UINT32 l_m_current_max_segs = cblk->m_current_max_segs +
-                                          OPJ_J2K_DEFAULT_NB_SEGS;
-
-        new_segs = (opj_tcd_seg_t*) opj_realloc(cblk->segs,
-                                                l_m_current_max_segs * sizeof(opj_tcd_seg_t));
-        if (! new_segs) {
-            /* opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to initialize segment %d\n", l_nb_segs); */
-            return OPJ_FALSE;
-        }
-        cblk->segs = new_segs;
-        memset(new_segs + cblk->m_current_max_segs,
-               0, OPJ_J2K_DEFAULT_NB_SEGS * sizeof(opj_tcd_seg_t));
-        cblk->m_current_max_segs = l_m_current_max_segs;
-    }
-
-    seg = &cblk->segs[index];
-    opj_tcd_reinit_segment(seg);
-
-    if (cblksty & J2K_CCP_CBLKSTY_TERMALL) {
-        seg->maxpasses = 1;
-    } else if (cblksty & J2K_CCP_CBLKSTY_LAZY) {
-        if (first) {
-            seg->maxpasses = 10;
-        } else {
-            seg->maxpasses = (((seg - 1)->maxpasses == 1) ||
-                              ((seg - 1)->maxpasses == 10)) ? 2 : 1;
-        }
-    } else {
-        /* See paragraph "B.10.6 Number of coding passes" of the standard.
-         * Probably that 109 must be interpreted a (Mb-1)*3 + 1 with Mb=37,
-         * Mb being the maximum number of bit-planes available for the
-         * representation of coefficients in the sub-band */
-        seg->maxpasses = 109;
-    }
-
-    return OPJ_TRUE;
-}
diff --git a/third_party/libopenjpeg20/t2.h b/third_party/libopenjpeg20/t2.h
deleted file mode 100644
index 66500b1..0000000
--- a/third_party/libopenjpeg20/t2.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
- * Copyright (c) 2012, CS Systemes d'Information, France
- * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-#ifndef OPJ_T2_H
-#define OPJ_T2_H
-/**
-@file t2.h
-@brief Implementation of a tier-2 coding (packetization of code-block data) (T2)
-
-*/
-
-/** @defgroup T2 T2 - Implementation of a tier-2 coding */
-/*@{*/
-
-/**
-Tier-2 coding
-*/
-typedef struct opj_t2 {
-
-    /** Encoding: pointer to the src image. Decoding: pointer to the dst image. */
-    opj_image_t *image;
-    /** pointer to the image coding parameters */
-    opj_cp_t *cp;
-} opj_t2_t;
-
-/** @name Exported functions */
-/*@{*/
-/* ----------------------------------------------------------------------- */
-
-/**
-Encode the packets of a tile to a destination buffer
-@param t2               T2 handle
-@param tileno           number of the tile encoded
-@param tile             the tile for which to write the packets
-@param maxlayers        maximum number of layers
-@param dest             the destination buffer
-@param p_data_written   FIXME DOC
-@param len              the length of the destination buffer
-@param cstr_info        Codestream information structure
-@param tpnum            Tile part number of the current tile
-@param tppos            The position of the tile part flag in the progression order
-@param pino             FIXME DOC
-@param t2_mode          If == THRESH_CALC In Threshold calculation ,If == FINAL_PASS Final pass
-@param p_manager        the user event manager
-*/
-OPJ_BOOL opj_t2_encode_packets(opj_t2_t* t2,
-                               OPJ_UINT32 tileno,
-                               opj_tcd_tile_t *tile,
-                               OPJ_UINT32 maxlayers,
-                               OPJ_BYTE *dest,
-                               OPJ_UINT32 * p_data_written,
-                               OPJ_UINT32 len,
-                               opj_codestream_info_t *cstr_info,
-                               OPJ_UINT32 tpnum,
-                               OPJ_INT32 tppos,
-                               OPJ_UINT32 pino,
-                               J2K_T2_MODE t2_mode,
-                               opj_event_mgr_t *p_manager);
-
-/**
-Decode the packets of a tile from a source buffer
-@param tcd TCD handle
-@param t2 T2 handle
-@param tileno number that identifies the tile for which to decode the packets
-@param tile tile for which to decode the packets
-@param src         FIXME DOC
-@param p_data_read the source buffer
-@param len length of the source buffer
-@param cstr_info   FIXME DOC
-@param p_manager the user event manager
-
-@return FIXME DOC
- */
-OPJ_BOOL opj_t2_decode_packets(opj_tcd_t* tcd,
-                               opj_t2_t *t2,
-                               OPJ_UINT32 tileno,
-                               opj_tcd_tile_t *tile,
-                               OPJ_BYTE *src,
-                               OPJ_UINT32 * p_data_read,
-                               OPJ_UINT32 len,
-                               opj_codestream_index_t *cstr_info,
-                               opj_event_mgr_t *p_manager);
-
-/**
- * Creates a Tier 2 handle
- *
- * @param   p_image     Source or destination image
- * @param   p_cp        Image coding parameters.
- * @return      a new T2 handle if successful, NULL otherwise.
-*/
-opj_t2_t* opj_t2_create(opj_image_t *p_image, opj_cp_t *p_cp);
-
-/**
-Destroy a T2 handle
-@param t2 T2 handle to destroy
-*/
-void opj_t2_destroy(opj_t2_t *t2);
-
-/* ----------------------------------------------------------------------- */
-/*@}*/
-
-/*@}*/
-
-#endif /* OPJ_T2_H */
diff --git a/third_party/libopenjpeg20/tcd.c b/third_party/libopenjpeg20/tcd.c
deleted file mode 100644
index 9e98f04..0000000
--- a/third_party/libopenjpeg20/tcd.c
+++ /dev/null
@@ -1,2828 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2006-2007, Parvatha Elangovan
- * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
- * Copyright (c) 2012, CS Systemes d'Information, France
- * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "opj_includes.h"
-#include "opj_common.h"
-
-/* ----------------------------------------------------------------------- */
-
-/* TODO MSD: */
-#ifdef TODO_MSD
-void tcd_dump(FILE *fd, opj_tcd_t *tcd, opj_tcd_image_t * img)
-{
-    int tileno, compno, resno, bandno, precno;/*, cblkno;*/
-
-    fprintf(fd, "image {\n");
-    fprintf(fd, "  tw=%d, th=%d x0=%d x1=%d y0=%d y1=%d\n",
-            img->tw, img->th, tcd->image->x0, tcd->image->x1, tcd->image->y0,
-            tcd->image->y1);
-
-    for (tileno = 0; tileno < img->th * img->tw; tileno++) {
-        opj_tcd_tile_t *tile = &tcd->tcd_image->tiles[tileno];
-        fprintf(fd, "  tile {\n");
-        fprintf(fd, "    x0=%d, y0=%d, x1=%d, y1=%d, numcomps=%d\n",
-                tile->x0, tile->y0, tile->x1, tile->y1, tile->numcomps);
-        for (compno = 0; compno < tile->numcomps; compno++) {
-            opj_tcd_tilecomp_t *tilec = &tile->comps[compno];
-            fprintf(fd, "    tilec {\n");
-            fprintf(fd,
-                    "      x0=%d, y0=%d, x1=%d, y1=%d, numresolutions=%d\n",
-                    tilec->x0, tilec->y0, tilec->x1, tilec->y1, tilec->numresolutions);
-            for (resno = 0; resno < tilec->numresolutions; resno++) {
-                opj_tcd_resolution_t *res = &tilec->resolutions[resno];
-                fprintf(fd, "\n   res {\n");
-                fprintf(fd,
-                        "          x0=%d, y0=%d, x1=%d, y1=%d, pw=%d, ph=%d, numbands=%d\n",
-                        res->x0, res->y0, res->x1, res->y1, res->pw, res->ph, res->numbands);
-                for (bandno = 0; bandno < res->numbands; bandno++) {
-                    opj_tcd_band_t *band = &res->bands[bandno];
-                    fprintf(fd, "        band {\n");
-                    fprintf(fd,
-                            "          x0=%d, y0=%d, x1=%d, y1=%d, stepsize=%f, numbps=%d\n",
-                            band->x0, band->y0, band->x1, band->y1, band->stepsize, band->numbps);
-                    for (precno = 0; precno < res->pw * res->ph; precno++) {
-                        opj_tcd_precinct_t *prec = &band->precincts[precno];
-                        fprintf(fd, "          prec {\n");
-                        fprintf(fd,
-                                "            x0=%d, y0=%d, x1=%d, y1=%d, cw=%d, ch=%d\n",
-                                prec->x0, prec->y0, prec->x1, prec->y1, prec->cw, prec->ch);
-                        /*
-                        for (cblkno = 0; cblkno < prec->cw * prec->ch; cblkno++) {
-                                opj_tcd_cblk_t *cblk = &prec->cblks[cblkno];
-                                fprintf(fd, "            cblk {\n");
-                                fprintf(fd,
-                                        "              x0=%d, y0=%d, x1=%d, y1=%d\n",
-                                        cblk->x0, cblk->y0, cblk->x1, cblk->y1);
-                                fprintf(fd, "            }\n");
-                        }
-                        */
-                        fprintf(fd, "          }\n");
-                    }
-                    fprintf(fd, "        }\n");
-                }
-                fprintf(fd, "      }\n");
-            }
-            fprintf(fd, "    }\n");
-        }
-        fprintf(fd, "  }\n");
-    }
-    fprintf(fd, "}\n");
-}
-#endif
-
-/**
- * Initializes tile coding/decoding
- */
-static INLINE OPJ_BOOL opj_tcd_init_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
-        OPJ_BOOL isEncoder, OPJ_FLOAT32 fraction, OPJ_SIZE_T sizeof_block,
-        opj_event_mgr_t* manager);
-
-/**
-* Allocates memory for a decoding code block.
-*/
-static OPJ_BOOL opj_tcd_code_block_dec_allocate(opj_tcd_cblk_dec_t *
-        p_code_block);
-
-/**
- * Deallocates the decoding data of the given precinct.
- */
-static void opj_tcd_code_block_dec_deallocate(opj_tcd_precinct_t * p_precinct);
-
-/**
- * Allocates memory for an encoding code block (but not data).
- */
-static OPJ_BOOL opj_tcd_code_block_enc_allocate(opj_tcd_cblk_enc_t *
-        p_code_block);
-
-/**
- * Allocates data for an encoding code block
- */
-static OPJ_BOOL opj_tcd_code_block_enc_allocate_data(opj_tcd_cblk_enc_t *
-        p_code_block);
-
-/**
- * Deallocates the encoding data of the given precinct.
- */
-static void opj_tcd_code_block_enc_deallocate(opj_tcd_precinct_t * p_precinct);
-
-
-/**
-Free the memory allocated for encoding
-@param tcd TCD handle
-*/
-static void opj_tcd_free_tile(opj_tcd_t *tcd);
-
-
-static OPJ_BOOL opj_tcd_t2_decode(opj_tcd_t *p_tcd,
-                                  OPJ_BYTE * p_src_data,
-                                  OPJ_UINT32 * p_data_read,
-                                  OPJ_UINT32 p_max_src_size,
-                                  opj_codestream_index_t *p_cstr_index,
-                                  opj_event_mgr_t *p_manager);
-
-static OPJ_BOOL opj_tcd_t1_decode(opj_tcd_t *p_tcd,
-                                  opj_event_mgr_t *p_manager);
-
-static OPJ_BOOL opj_tcd_dwt_decode(opj_tcd_t *p_tcd);
-
-static OPJ_BOOL opj_tcd_mct_decode(opj_tcd_t *p_tcd,
-                                   opj_event_mgr_t *p_manager);
-
-static OPJ_BOOL opj_tcd_dc_level_shift_decode(opj_tcd_t *p_tcd);
-
-
-static OPJ_BOOL opj_tcd_dc_level_shift_encode(opj_tcd_t *p_tcd);
-
-static OPJ_BOOL opj_tcd_mct_encode(opj_tcd_t *p_tcd);
-
-static OPJ_BOOL opj_tcd_dwt_encode(opj_tcd_t *p_tcd);
-
-static OPJ_BOOL opj_tcd_t1_encode(opj_tcd_t *p_tcd);
-
-static OPJ_BOOL opj_tcd_t2_encode(opj_tcd_t *p_tcd,
-                                  OPJ_BYTE * p_dest_data,
-                                  OPJ_UINT32 * p_data_written,
-                                  OPJ_UINT32 p_max_dest_size,
-                                  opj_codestream_info_t *p_cstr_info,
-                                  opj_event_mgr_t *p_manager);
-
-static OPJ_BOOL opj_tcd_rate_allocate_encode(opj_tcd_t *p_tcd,
-        OPJ_BYTE * p_dest_data,
-        OPJ_UINT32 p_max_dest_size,
-        opj_codestream_info_t *p_cstr_info,
-        opj_event_mgr_t *p_manager);
-
-
-static OPJ_BOOL opj_tcd_is_whole_tilecomp_decoding(opj_tcd_t *tcd,
-        OPJ_UINT32 compno);
-
-/* ----------------------------------------------------------------------- */
-
-/**
-Create a new TCD handle
-*/
-opj_tcd_t* opj_tcd_create(OPJ_BOOL p_is_decoder)
-{
-    opj_tcd_t *l_tcd = 00;
-
-    /* create the tcd structure */
-    l_tcd = (opj_tcd_t*) opj_calloc(1, sizeof(opj_tcd_t));
-    if (!l_tcd) {
-        return 00;
-    }
-
-    l_tcd->m_is_decoder = p_is_decoder ? 1 : 0;
-
-    l_tcd->tcd_image = (opj_tcd_image_t*)opj_calloc(1, sizeof(opj_tcd_image_t));
-    if (!l_tcd->tcd_image) {
-        opj_free(l_tcd);
-        return 00;
-    }
-
-    return l_tcd;
-}
-
-
-/* ----------------------------------------------------------------------- */
-
-void opj_tcd_rateallocate_fixed(opj_tcd_t *tcd)
-{
-    OPJ_UINT32 layno;
-
-    for (layno = 0; layno < tcd->tcp->numlayers; layno++) {
-        opj_tcd_makelayer_fixed(tcd, layno, 1);
-    }
-}
-
-
-void opj_tcd_makelayer(opj_tcd_t *tcd,
-                       OPJ_UINT32 layno,
-                       OPJ_FLOAT64 thresh,
-                       OPJ_UINT32 final)
-{
-    OPJ_UINT32 compno, resno, bandno, precno, cblkno;
-    OPJ_UINT32 passno;
-
-    opj_tcd_tile_t *tcd_tile = tcd->tcd_image->tiles;
-
-    tcd_tile->distolayer[layno] = 0;        /* fixed_quality */
-
-    for (compno = 0; compno < tcd_tile->numcomps; compno++) {
-        opj_tcd_tilecomp_t *tilec = &tcd_tile->comps[compno];
-
-        for (resno = 0; resno < tilec->numresolutions; resno++) {
-            opj_tcd_resolution_t *res = &tilec->resolutions[resno];
-
-            for (bandno = 0; bandno < res->numbands; bandno++) {
-                opj_tcd_band_t *band = &res->bands[bandno];
-
-                /* Skip empty bands */
-                if (opj_tcd_is_band_empty(band)) {
-                    continue;
-                }
-
-                for (precno = 0; precno < res->pw * res->ph; precno++) {
-                    opj_tcd_precinct_t *prc = &band->precincts[precno];
-
-                    for (cblkno = 0; cblkno < prc->cw * prc->ch; cblkno++) {
-                        opj_tcd_cblk_enc_t *cblk = &prc->cblks.enc[cblkno];
-                        opj_tcd_layer_t *layer = &cblk->layers[layno];
-                        OPJ_UINT32 n;
-
-                        if (layno == 0) {
-                            cblk->numpassesinlayers = 0;
-                        }
-
-                        n = cblk->numpassesinlayers;
-
-                        if (thresh < 0) {
-                            /* Special value to indicate to use all passes */
-                            n = cblk->totalpasses;
-                        } else {
-                            for (passno = cblk->numpassesinlayers; passno < cblk->totalpasses; passno++) {
-                                OPJ_UINT32 dr;
-                                OPJ_FLOAT64 dd;
-                                opj_tcd_pass_t *pass = &cblk->passes[passno];
-
-                                if (n == 0) {
-                                    dr = pass->rate;
-                                    dd = pass->distortiondec;
-                                } else {
-                                    dr = pass->rate - cblk->passes[n - 1].rate;
-                                    dd = pass->distortiondec - cblk->passes[n - 1].distortiondec;
-                                }
-
-                                if (!dr) {
-                                    if (dd != 0) {
-                                        n = passno + 1;
-                                    }
-                                    continue;
-                                }
-                                if (thresh - (dd / dr) <
-                                        DBL_EPSILON) { /* do not rely on float equality, check with DBL_EPSILON margin */
-                                    n = passno + 1;
-                                }
-                            }
-                        }
-
-                        layer->numpasses = n - cblk->numpassesinlayers;
-
-                        if (!layer->numpasses) {
-                            layer->disto = 0;
-                            continue;
-                        }
-
-                        if (cblk->numpassesinlayers == 0) {
-                            layer->len = cblk->passes[n - 1].rate;
-                            layer->data = cblk->data;
-                            layer->disto = cblk->passes[n - 1].distortiondec;
-                        } else {
-                            layer->len = cblk->passes[n - 1].rate - cblk->passes[cblk->numpassesinlayers -
-                                         1].rate;
-                            layer->data = cblk->data + cblk->passes[cblk->numpassesinlayers - 1].rate;
-                            layer->disto = cblk->passes[n - 1].distortiondec -
-                                           cblk->passes[cblk->numpassesinlayers - 1].distortiondec;
-                        }
-
-                        tcd_tile->distolayer[layno] += layer->disto;    /* fixed_quality */
-
-                        if (final) {
-                            cblk->numpassesinlayers = n;
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-void opj_tcd_makelayer_fixed(opj_tcd_t *tcd, OPJ_UINT32 layno,
-                             OPJ_UINT32 final)
-{
-    OPJ_UINT32 compno, resno, bandno, precno, cblkno;
-    OPJ_INT32 value;                        /*, matrice[tcd_tcp->numlayers][tcd_tile->comps[0].numresolutions][3]; */
-    OPJ_INT32 matrice[10][10][3];
-    OPJ_UINT32 i, j, k;
-
-    opj_cp_t *cp = tcd->cp;
-    opj_tcd_tile_t *tcd_tile = tcd->tcd_image->tiles;
-    opj_tcp_t *tcd_tcp = tcd->tcp;
-
-    for (compno = 0; compno < tcd_tile->numcomps; compno++) {
-        opj_tcd_tilecomp_t *tilec = &tcd_tile->comps[compno];
-
-        for (i = 0; i < tcd_tcp->numlayers; i++) {
-            for (j = 0; j < tilec->numresolutions; j++) {
-                for (k = 0; k < 3; k++) {
-                    matrice[i][j][k] =
-                        (OPJ_INT32)((OPJ_FLOAT32)cp->m_specific_param.m_enc.m_matrice[i *
-                                      tilec->numresolutions * 3 + j * 3 + k]
-                                    * (OPJ_FLOAT32)(tcd->image->comps[compno].prec / 16.0));
-                }
-            }
-        }
-
-        for (resno = 0; resno < tilec->numresolutions; resno++) {
-            opj_tcd_resolution_t *res = &tilec->resolutions[resno];
-
-            for (bandno = 0; bandno < res->numbands; bandno++) {
-                opj_tcd_band_t *band = &res->bands[bandno];
-
-                /* Skip empty bands */
-                if (opj_tcd_is_band_empty(band)) {
-                    continue;
-                }
-
-                for (precno = 0; precno < res->pw * res->ph; precno++) {
-                    opj_tcd_precinct_t *prc = &band->precincts[precno];
-
-                    for (cblkno = 0; cblkno < prc->cw * prc->ch; cblkno++) {
-                        opj_tcd_cblk_enc_t *cblk = &prc->cblks.enc[cblkno];
-                        opj_tcd_layer_t *layer = &cblk->layers[layno];
-                        OPJ_UINT32 n;
-                        OPJ_INT32 imsb = (OPJ_INT32)(tcd->image->comps[compno].prec -
-                                                     cblk->numbps); /* number of bit-plan equal to zero */
-
-                        /* Correction of the matrix of coefficient to include the IMSB information */
-                        if (layno == 0) {
-                            value = matrice[layno][resno][bandno];
-                            if (imsb >= value) {
-                                value = 0;
-                            } else {
-                                value -= imsb;
-                            }
-                        } else {
-                            value = matrice[layno][resno][bandno] - matrice[layno - 1][resno][bandno];
-                            if (imsb >= matrice[layno - 1][resno][bandno]) {
-                                value -= (imsb - matrice[layno - 1][resno][bandno]);
-                                if (value < 0) {
-                                    value = 0;
-                                }
-                            }
-                        }
-
-                        if (layno == 0) {
-                            cblk->numpassesinlayers = 0;
-                        }
-
-                        n = cblk->numpassesinlayers;
-                        if (cblk->numpassesinlayers == 0) {
-                            if (value != 0) {
-                                n = 3 * (OPJ_UINT32)value - 2 + cblk->numpassesinlayers;
-                            } else {
-                                n = cblk->numpassesinlayers;
-                            }
-                        } else {
-                            n = 3 * (OPJ_UINT32)value + cblk->numpassesinlayers;
-                        }
-
-                        layer->numpasses = n - cblk->numpassesinlayers;
-
-                        if (!layer->numpasses) {
-                            continue;
-                        }
-
-                        if (cblk->numpassesinlayers == 0) {
-                            layer->len = cblk->passes[n - 1].rate;
-                            layer->data = cblk->data;
-                        } else {
-                            layer->len = cblk->passes[n - 1].rate - cblk->passes[cblk->numpassesinlayers -
-                                         1].rate;
-                            layer->data = cblk->data + cblk->passes[cblk->numpassesinlayers - 1].rate;
-                        }
-
-                        if (final) {
-                            cblk->numpassesinlayers = n;
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-OPJ_BOOL opj_tcd_rateallocate(opj_tcd_t *tcd,
-                              OPJ_BYTE *dest,
-                              OPJ_UINT32 * p_data_written,
-                              OPJ_UINT32 len,
-                              opj_codestream_info_t *cstr_info,
-                              opj_event_mgr_t *p_manager)
-{
-    OPJ_UINT32 compno, resno, bandno, precno, cblkno, layno;
-    OPJ_UINT32 passno;
-    OPJ_FLOAT64 min, max;
-    OPJ_FLOAT64 cumdisto[100];      /* fixed_quality */
-    const OPJ_FLOAT64 K = 1;                /* 1.1; fixed_quality */
-    OPJ_FLOAT64 maxSE = 0;
-
-    opj_cp_t *cp = tcd->cp;
-    opj_tcd_tile_t *tcd_tile = tcd->tcd_image->tiles;
-    opj_tcp_t *tcd_tcp = tcd->tcp;
-
-    min = DBL_MAX;
-    max = 0;
-
-    tcd_tile->numpix = 0;           /* fixed_quality */
-
-    for (compno = 0; compno < tcd_tile->numcomps; compno++) {
-        opj_tcd_tilecomp_t *tilec = &tcd_tile->comps[compno];
-        tilec->numpix = 0;
-
-        for (resno = 0; resno < tilec->numresolutions; resno++) {
-            opj_tcd_resolution_t *res = &tilec->resolutions[resno];
-
-            for (bandno = 0; bandno < res->numbands; bandno++) {
-                opj_tcd_band_t *band = &res->bands[bandno];
-
-                /* Skip empty bands */
-                if (opj_tcd_is_band_empty(band)) {
-                    continue;
-                }
-
-                for (precno = 0; precno < res->pw * res->ph; precno++) {
-                    opj_tcd_precinct_t *prc = &band->precincts[precno];
-
-                    for (cblkno = 0; cblkno < prc->cw * prc->ch; cblkno++) {
-                        opj_tcd_cblk_enc_t *cblk = &prc->cblks.enc[cblkno];
-
-                        for (passno = 0; passno < cblk->totalpasses; passno++) {
-                            opj_tcd_pass_t *pass = &cblk->passes[passno];
-                            OPJ_INT32 dr;
-                            OPJ_FLOAT64 dd, rdslope;
-
-                            if (passno == 0) {
-                                dr = (OPJ_INT32)pass->rate;
-                                dd = pass->distortiondec;
-                            } else {
-                                dr = (OPJ_INT32)(pass->rate - cblk->passes[passno - 1].rate);
-                                dd = pass->distortiondec - cblk->passes[passno - 1].distortiondec;
-                            }
-
-                            if (dr == 0) {
-                                continue;
-                            }
-
-                            rdslope = dd / dr;
-                            if (rdslope < min) {
-                                min = rdslope;
-                            }
-
-                            if (rdslope > max) {
-                                max = rdslope;
-                            }
-                        } /* passno */
-
-                        /* fixed_quality */
-                        tcd_tile->numpix += ((cblk->x1 - cblk->x0) * (cblk->y1 - cblk->y0));
-                        tilec->numpix += ((cblk->x1 - cblk->x0) * (cblk->y1 - cblk->y0));
-                    } /* cbklno */
-                } /* precno */
-            } /* bandno */
-        } /* resno */
-
-        maxSE += (((OPJ_FLOAT64)(1 << tcd->image->comps[compno].prec) - 1.0)
-                  * ((OPJ_FLOAT64)(1 << tcd->image->comps[compno].prec) - 1.0))
-                 * ((OPJ_FLOAT64)(tilec->numpix));
-    } /* compno */
-
-    /* index file */
-    if (cstr_info) {
-        opj_tile_info_t *tile_info = &cstr_info->tile[tcd->tcd_tileno];
-        tile_info->numpix = tcd_tile->numpix;
-        tile_info->distotile = tcd_tile->distotile;
-        tile_info->thresh = (OPJ_FLOAT64 *) opj_malloc(tcd_tcp->numlayers * sizeof(
-                                OPJ_FLOAT64));
-        if (!tile_info->thresh) {
-            /* FIXME event manager error callback */
-            return OPJ_FALSE;
-        }
-    }
-
-    for (layno = 0; layno < tcd_tcp->numlayers; layno++) {
-        OPJ_FLOAT64 lo = min;
-        OPJ_FLOAT64 hi = max;
-        OPJ_UINT32 maxlen = tcd_tcp->rates[layno] > 0.0f ? opj_uint_min(((
-                                OPJ_UINT32) ceil(tcd_tcp->rates[layno])), len) : len;
-        OPJ_FLOAT64 goodthresh = 0;
-        OPJ_FLOAT64 stable_thresh = 0;
-        OPJ_UINT32 i;
-        OPJ_FLOAT64 distotarget;                /* fixed_quality */
-
-        /* fixed_quality */
-        distotarget = tcd_tile->distotile - ((K * maxSE) / pow((OPJ_FLOAT32)10,
-                                             tcd_tcp->distoratio[layno] / 10));
-
-        /* Don't try to find an optimal threshold but rather take everything not included yet, if
-          -r xx,yy,zz,0   (disto_alloc == 1 and rates == 0)
-          -q xx,yy,zz,0   (fixed_quality == 1 and distoratio == 0)
-          ==> possible to have some lossy layers and the last layer for sure lossless */
-        if (((cp->m_specific_param.m_enc.m_disto_alloc == 1) &&
-                (tcd_tcp->rates[layno] > 0.0f)) ||
-                ((cp->m_specific_param.m_enc.m_fixed_quality == 1) &&
-                 (tcd_tcp->distoratio[layno] > 0.0))) {
-            opj_t2_t*t2 = opj_t2_create(tcd->image, cp);
-            OPJ_FLOAT64 thresh = 0;
-
-            if (t2 == 00) {
-                return OPJ_FALSE;
-            }
-
-            for (i = 0; i < 128; ++i) {
-                OPJ_FLOAT64 distoachieved = 0;  /* fixed_quality */
-
-                thresh = (lo + hi) / 2;
-
-                opj_tcd_makelayer(tcd, layno, thresh, 0);
-
-                if (cp->m_specific_param.m_enc.m_fixed_quality) {       /* fixed_quality */
-                    if (OPJ_IS_CINEMA(cp->rsiz)) {
-                        if (! opj_t2_encode_packets(t2, tcd->tcd_tileno, tcd_tile, layno + 1, dest,
-                                                    p_data_written, maxlen, cstr_info, tcd->cur_tp_num, tcd->tp_pos, tcd->cur_pino,
-                                                    THRESH_CALC, p_manager)) {
-
-                            lo = thresh;
-                            continue;
-                        } else {
-                            distoachieved = layno == 0 ?
-                                            tcd_tile->distolayer[0] : cumdisto[layno - 1] + tcd_tile->distolayer[layno];
-
-                            if (distoachieved < distotarget) {
-                                hi = thresh;
-                                stable_thresh = thresh;
-                                continue;
-                            } else {
-                                lo = thresh;
-                            }
-                        }
-                    } else {
-                        distoachieved = (layno == 0) ?
-                                        tcd_tile->distolayer[0] : (cumdisto[layno - 1] + tcd_tile->distolayer[layno]);
-
-                        if (distoachieved < distotarget) {
-                            hi = thresh;
-                            stable_thresh = thresh;
-                            continue;
-                        }
-                        lo = thresh;
-                    }
-                } else {
-                    if (! opj_t2_encode_packets(t2, tcd->tcd_tileno, tcd_tile, layno + 1, dest,
-                                                p_data_written, maxlen, cstr_info, tcd->cur_tp_num, tcd->tp_pos, tcd->cur_pino,
-                                                THRESH_CALC, p_manager)) {
-                        /* TODO: what to do with l ??? seek / tell ??? */
-                        /* opj_event_msg(tcd->cinfo, EVT_INFO, "rate alloc: len=%d, max=%d\n", l, maxlen); */
-                        lo = thresh;
-                        continue;
-                    }
-
-                    hi = thresh;
-                    stable_thresh = thresh;
-                }
-            }
-
-            goodthresh = stable_thresh == 0 ? thresh : stable_thresh;
-
-            opj_t2_destroy(t2);
-        } else {
-            /* Special value to indicate to use all passes */
-            goodthresh = -1;
-        }
-
-        if (cstr_info) { /* Threshold for Marcela Index */
-            cstr_info->tile[tcd->tcd_tileno].thresh[layno] = goodthresh;
-        }
-
-        opj_tcd_makelayer(tcd, layno, goodthresh, 1);
-
-        /* fixed_quality */
-        cumdisto[layno] = (layno == 0) ? tcd_tile->distolayer[0] :
-                          (cumdisto[layno - 1] + tcd_tile->distolayer[layno]);
-    }
-
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_tcd_init(opj_tcd_t *p_tcd,
-                      opj_image_t * p_image,
-                      opj_cp_t * p_cp,
-                      opj_thread_pool_t* p_tp)
-{
-    p_tcd->image = p_image;
-    p_tcd->cp = p_cp;
-
-    p_tcd->tcd_image->tiles = (opj_tcd_tile_t *) opj_calloc(1,
-                              sizeof(opj_tcd_tile_t));
-    if (! p_tcd->tcd_image->tiles) {
-        return OPJ_FALSE;
-    }
-
-    p_tcd->tcd_image->tiles->comps = (opj_tcd_tilecomp_t *) opj_calloc(
-                                         p_image->numcomps, sizeof(opj_tcd_tilecomp_t));
-    if (! p_tcd->tcd_image->tiles->comps) {
-        return OPJ_FALSE;
-    }
-
-    p_tcd->tcd_image->tiles->numcomps = p_image->numcomps;
-    p_tcd->tp_pos = p_cp->m_specific_param.m_enc.m_tp_pos;
-    p_tcd->thread_pool = p_tp;
-
-    return OPJ_TRUE;
-}
-
-/**
-Destroy a previously created TCD handle
-*/
-void opj_tcd_destroy(opj_tcd_t *tcd)
-{
-    if (tcd) {
-        opj_tcd_free_tile(tcd);
-
-        if (tcd->tcd_image) {
-            opj_free(tcd->tcd_image);
-            tcd->tcd_image = 00;
-        }
-
-        opj_free(tcd->used_component);
-
-        opj_free(tcd);
-    }
-}
-
-OPJ_BOOL opj_alloc_tile_component_data(opj_tcd_tilecomp_t *l_tilec)
-{
-    if ((l_tilec->data == 00) ||
-            ((l_tilec->data_size_needed > l_tilec->data_size) &&
-             (l_tilec->ownsData == OPJ_FALSE))) {
-        l_tilec->data = (OPJ_INT32 *) opj_image_data_alloc(l_tilec->data_size_needed);
-        if (!l_tilec->data && l_tilec->data_size_needed != 0) {
-            return OPJ_FALSE;
-        }
-        /*fprintf(stderr, "tAllocate data of tilec (int): %d x OPJ_UINT32n",l_data_size);*/
-        l_tilec->data_size = l_tilec->data_size_needed;
-        l_tilec->ownsData = OPJ_TRUE;
-    } else if (l_tilec->data_size_needed > l_tilec->data_size) {
-        /* We don't need to keep old data */
-        opj_image_data_free(l_tilec->data);
-        l_tilec->data = (OPJ_INT32 *) opj_image_data_alloc(l_tilec->data_size_needed);
-        if (! l_tilec->data) {
-            l_tilec->data_size = 0;
-            l_tilec->data_size_needed = 0;
-            l_tilec->ownsData = OPJ_FALSE;
-            return OPJ_FALSE;
-        }
-        /*fprintf(stderr, "tReallocate data of tilec (int): from %d to %d x OPJ_UINT32n", l_tilec->data_size, l_data_size);*/
-        l_tilec->data_size = l_tilec->data_size_needed;
-        l_tilec->ownsData = OPJ_TRUE;
-    }
-    return OPJ_TRUE;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static INLINE OPJ_BOOL opj_tcd_init_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
-        OPJ_BOOL isEncoder, OPJ_FLOAT32 fraction, OPJ_SIZE_T sizeof_block,
-        opj_event_mgr_t* manager)
-{
-    OPJ_UINT32(*l_gain_ptr)(OPJ_UINT32) = 00;
-    OPJ_UINT32 compno, resno, bandno, precno, cblkno;
-    opj_tcp_t * l_tcp = 00;
-    opj_cp_t * l_cp = 00;
-    opj_tcd_tile_t * l_tile = 00;
-    opj_tccp_t *l_tccp = 00;
-    opj_tcd_tilecomp_t *l_tilec = 00;
-    opj_image_comp_t * l_image_comp = 00;
-    opj_tcd_resolution_t *l_res = 00;
-    opj_tcd_band_t *l_band = 00;
-    opj_stepsize_t * l_step_size = 00;
-    opj_tcd_precinct_t *l_current_precinct = 00;
-    opj_image_t *l_image = 00;
-    OPJ_UINT32 p, q;
-    OPJ_UINT32 l_level_no;
-    OPJ_UINT32 l_pdx, l_pdy;
-    OPJ_UINT32 l_gain;
-    OPJ_INT32 l_x0b, l_y0b;
-    OPJ_UINT32 l_tx0, l_ty0;
-    /* extent of precincts , top left, bottom right**/
-    OPJ_INT32 l_tl_prc_x_start, l_tl_prc_y_start, l_br_prc_x_end, l_br_prc_y_end;
-    /* number of precinct for a resolution */
-    OPJ_UINT32 l_nb_precincts;
-    /* room needed to store l_nb_precinct precinct for a resolution */
-    OPJ_UINT32 l_nb_precinct_size;
-    /* number of code blocks for a precinct*/
-    OPJ_UINT32 l_nb_code_blocks;
-    /* room needed to store l_nb_code_blocks code blocks for a precinct*/
-    OPJ_UINT32 l_nb_code_blocks_size;
-    /* size of data for a tile */
-    OPJ_UINT32 l_data_size;
-
-    l_cp = p_tcd->cp;
-    l_tcp = &(l_cp->tcps[p_tile_no]);
-    l_tile = p_tcd->tcd_image->tiles;
-    l_tccp = l_tcp->tccps;
-    l_tilec = l_tile->comps;
-    l_image = p_tcd->image;
-    l_image_comp = p_tcd->image->comps;
-
-    p = p_tile_no % l_cp->tw;       /* tile coordinates */
-    q = p_tile_no / l_cp->tw;
-    /*fprintf(stderr, "Tile coordinate = %d,%d\n", p, q);*/
-
-    /* 4 borders of the tile rescale on the image if necessary */
-    l_tx0 = l_cp->tx0 + p *
-            l_cp->tdx; /* can't be greater than l_image->x1 so won't overflow */
-    l_tile->x0 = (OPJ_INT32)opj_uint_max(l_tx0, l_image->x0);
-    l_tile->x1 = (OPJ_INT32)opj_uint_min(opj_uint_adds(l_tx0, l_cp->tdx),
-                                         l_image->x1);
-    /* all those OPJ_UINT32 are casted to OPJ_INT32, let's do some sanity check */
-    if ((l_tile->x0 < 0) || (l_tile->x1 <= l_tile->x0)) {
-        opj_event_msg(manager, EVT_ERROR, "Tile X coordinates are not supported\n");
-        return OPJ_FALSE;
-    }
-    l_ty0 = l_cp->ty0 + q *
-            l_cp->tdy; /* can't be greater than l_image->y1 so won't overflow */
-    l_tile->y0 = (OPJ_INT32)opj_uint_max(l_ty0, l_image->y0);
-    l_tile->y1 = (OPJ_INT32)opj_uint_min(opj_uint_adds(l_ty0, l_cp->tdy),
-                                         l_image->y1);
-    /* all those OPJ_UINT32 are casted to OPJ_INT32, let's do some sanity check */
-    if ((l_tile->y0 < 0) || (l_tile->y1 <= l_tile->y0)) {
-        opj_event_msg(manager, EVT_ERROR, "Tile Y coordinates are not supported\n");
-        return OPJ_FALSE;
-    }
-
-
-    /* testcase 1888.pdf.asan.35.988 */
-    if (l_tccp->numresolutions == 0) {
-        opj_event_msg(manager, EVT_ERROR, "tiles require at least one resolution\n");
-        return OPJ_FALSE;
-    }
-    /*fprintf(stderr, "Tile border = %d,%d,%d,%d\n", l_tile->x0, l_tile->y0,l_tile->x1,l_tile->y1);*/
-
-    /*tile->numcomps = image->numcomps; */
-    for (compno = 0; compno < l_tile->numcomps; ++compno) {
-        /*fprintf(stderr, "compno = %d/%d\n", compno, l_tile->numcomps);*/
-        l_image_comp->resno_decoded = 0;
-        /* border of each l_tile component (global) */
-        l_tilec->x0 = opj_int_ceildiv(l_tile->x0, (OPJ_INT32)l_image_comp->dx);
-        l_tilec->y0 = opj_int_ceildiv(l_tile->y0, (OPJ_INT32)l_image_comp->dy);
-        l_tilec->x1 = opj_int_ceildiv(l_tile->x1, (OPJ_INT32)l_image_comp->dx);
-        l_tilec->y1 = opj_int_ceildiv(l_tile->y1, (OPJ_INT32)l_image_comp->dy);
-        l_tilec->compno = compno;
-        /*fprintf(stderr, "\tTile compo border = %d,%d,%d,%d\n", l_tilec->x0, l_tilec->y0,l_tilec->x1,l_tilec->y1);*/
-
-        l_tilec->numresolutions = l_tccp->numresolutions;
-        if (l_tccp->numresolutions < l_cp->m_specific_param.m_dec.m_reduce) {
-            l_tilec->minimum_num_resolutions = 1;
-        } else {
-            l_tilec->minimum_num_resolutions = l_tccp->numresolutions -
-                                               l_cp->m_specific_param.m_dec.m_reduce;
-        }
-
-        if (isEncoder) {
-            OPJ_SIZE_T l_tile_data_size;
-
-            if (l_tilec->x0 >= l_tilec->x1 || l_tilec->y0 >= l_tilec->y1) {
-                opj_event_msg(manager, EVT_ERROR, "Invalid tile data\n");
-                return OPJ_FALSE;
-            }
-
-            /* compute l_data_size with overflow check */
-            OPJ_SIZE_T w = (OPJ_SIZE_T)(l_tilec->x1 - l_tilec->x0);
-            OPJ_SIZE_T h = (OPJ_SIZE_T)(l_tilec->y1 - l_tilec->y0);
-
-            /* issue 733, l_data_size == 0U, probably something wrong should be checked before getting here */
-            if (h > 0 && w > SIZE_MAX / h) {
-                opj_event_msg(manager, EVT_ERROR, "Size of tile data exceeds system limits\n");
-                return OPJ_FALSE;
-            }
-            l_tile_data_size = w * h;
-
-            if (SIZE_MAX / sizeof(OPJ_UINT32) < l_tile_data_size) {
-                opj_event_msg(manager, EVT_ERROR, "Size of tile data exceeds system limits\n");
-                return OPJ_FALSE;
-            }
-            l_tile_data_size = l_tile_data_size * sizeof(OPJ_UINT32);
-
-            l_tilec->data_size_needed = l_tile_data_size;
-        }
-
-        l_data_size = l_tilec->numresolutions * (OPJ_UINT32)sizeof(
-                          opj_tcd_resolution_t);
-
-        opj_image_data_free(l_tilec->data_win);
-        l_tilec->data_win = NULL;
-        l_tilec->win_x0 = 0;
-        l_tilec->win_y0 = 0;
-        l_tilec->win_x1 = 0;
-        l_tilec->win_y1 = 0;
-
-        if (l_tilec->resolutions == 00) {
-            l_tilec->resolutions = (opj_tcd_resolution_t *) opj_malloc(l_data_size);
-            if (! l_tilec->resolutions) {
-                return OPJ_FALSE;
-            }
-            /*fprintf(stderr, "\tAllocate resolutions of tilec (opj_tcd_resolution_t): %d\n",l_data_size);*/
-            l_tilec->resolutions_size = l_data_size;
-            memset(l_tilec->resolutions, 0, l_data_size);
-        } else if (l_data_size > l_tilec->resolutions_size) {
-            opj_tcd_resolution_t* new_resolutions = (opj_tcd_resolution_t *) opj_realloc(
-                    l_tilec->resolutions, l_data_size);
-            if (! new_resolutions) {
-                opj_event_msg(manager, EVT_ERROR, "Not enough memory for tile resolutions\n");
-                opj_free(l_tilec->resolutions);
-                l_tilec->resolutions = NULL;
-                l_tilec->resolutions_size = 0;
-                return OPJ_FALSE;
-            }
-            l_tilec->resolutions = new_resolutions;
-            /*fprintf(stderr, "\tReallocate data of tilec (int): from %d to %d x OPJ_UINT32\n", l_tilec->resolutions_size, l_data_size);*/
-            memset(((OPJ_BYTE*) l_tilec->resolutions) + l_tilec->resolutions_size, 0,
-                   l_data_size - l_tilec->resolutions_size);
-            l_tilec->resolutions_size = l_data_size;
-        }
-
-        l_level_no = l_tilec->numresolutions;
-        l_res = l_tilec->resolutions;
-        l_step_size = l_tccp->stepsizes;
-        if (l_tccp->qmfbid == 0) {
-            l_gain_ptr = &opj_dwt_getgain_real;
-        } else {
-            l_gain_ptr  = &opj_dwt_getgain;
-        }
-        /*fprintf(stderr, "\tlevel_no=%d\n",l_level_no);*/
-
-        for (resno = 0; resno < l_tilec->numresolutions; ++resno) {
-            /*fprintf(stderr, "\t\tresno = %d/%d\n", resno, l_tilec->numresolutions);*/
-            OPJ_INT32 tlcbgxstart, tlcbgystart /*, brcbgxend, brcbgyend*/;
-            OPJ_UINT32 cbgwidthexpn, cbgheightexpn;
-            OPJ_UINT32 cblkwidthexpn, cblkheightexpn;
-
-            --l_level_no;
-
-            /* border for each resolution level (global) */
-            l_res->x0 = opj_int_ceildivpow2(l_tilec->x0, (OPJ_INT32)l_level_no);
-            l_res->y0 = opj_int_ceildivpow2(l_tilec->y0, (OPJ_INT32)l_level_no);
-            l_res->x1 = opj_int_ceildivpow2(l_tilec->x1, (OPJ_INT32)l_level_no);
-            l_res->y1 = opj_int_ceildivpow2(l_tilec->y1, (OPJ_INT32)l_level_no);
-
-            /*fprintf(stderr, "\t\t\tres_x0= %d, res_y0 =%d, res_x1=%d, res_y1=%d\n", l_res->x0, l_res->y0, l_res->x1, l_res->y1);*/
-            /* p. 35, table A-23, ISO/IEC FDIS154444-1 : 2000 (18 august 2000) */
-            l_pdx = l_tccp->prcw[resno];
-            l_pdy = l_tccp->prch[resno];
-            /*fprintf(stderr, "\t\t\tpdx=%d, pdy=%d\n", l_pdx, l_pdy);*/
-            /* p. 64, B.6, ISO/IEC FDIS15444-1 : 2000 (18 august 2000)  */
-            l_tl_prc_x_start = opj_int_floordivpow2(l_res->x0, (OPJ_INT32)l_pdx) << l_pdx;
-            l_tl_prc_y_start = opj_int_floordivpow2(l_res->y0, (OPJ_INT32)l_pdy) << l_pdy;
-            {
-                OPJ_UINT32 tmp = ((OPJ_UINT32)opj_int_ceildivpow2(l_res->x1,
-                                  (OPJ_INT32)l_pdx)) << l_pdx;
-                if (tmp > (OPJ_UINT32)INT_MAX) {
-                    opj_event_msg(manager, EVT_ERROR, "Integer overflow\n");
-                    return OPJ_FALSE;
-                }
-                l_br_prc_x_end = (OPJ_INT32)tmp;
-            }
-            {
-                OPJ_UINT32 tmp = ((OPJ_UINT32)opj_int_ceildivpow2(l_res->y1,
-                                  (OPJ_INT32)l_pdy)) << l_pdy;
-                if (tmp > (OPJ_UINT32)INT_MAX) {
-                    opj_event_msg(manager, EVT_ERROR, "Integer overflow\n");
-                    return OPJ_FALSE;
-                }
-                l_br_prc_y_end = (OPJ_INT32)tmp;
-            }
-            /*fprintf(stderr, "\t\t\tprc_x_start=%d, prc_y_start=%d, br_prc_x_end=%d, br_prc_y_end=%d \n", l_tl_prc_x_start, l_tl_prc_y_start, l_br_prc_x_end ,l_br_prc_y_end );*/
-
-            l_res->pw = (l_res->x0 == l_res->x1) ? 0U : (OPJ_UINT32)((
-                            l_br_prc_x_end - l_tl_prc_x_start) >> l_pdx);
-            l_res->ph = (l_res->y0 == l_res->y1) ? 0U : (OPJ_UINT32)((
-                            l_br_prc_y_end - l_tl_prc_y_start) >> l_pdy);
-            /*fprintf(stderr, "\t\t\tres_pw=%d, res_ph=%d\n", l_res->pw, l_res->ph );*/
-
-            if ((l_res->pw != 0U) && ((((OPJ_UINT32) - 1) / l_res->pw) < l_res->ph)) {
-                opj_event_msg(manager, EVT_ERROR, "Size of tile data exceeds system limits\n");
-                return OPJ_FALSE;
-            }
-            l_nb_precincts = l_res->pw * l_res->ph;
-
-            if ((((OPJ_UINT32) - 1) / (OPJ_UINT32)sizeof(opj_tcd_precinct_t)) <
-                    l_nb_precincts) {
-                opj_event_msg(manager, EVT_ERROR, "Size of tile data exceeds system limits\n");
-                return OPJ_FALSE;
-            }
-            l_nb_precinct_size = l_nb_precincts * (OPJ_UINT32)sizeof(opj_tcd_precinct_t);
-
-            if (resno == 0) {
-                tlcbgxstart = l_tl_prc_x_start;
-                tlcbgystart = l_tl_prc_y_start;
-                /*brcbgxend = l_br_prc_x_end;*/
-                /* brcbgyend = l_br_prc_y_end;*/
-                cbgwidthexpn = l_pdx;
-                cbgheightexpn = l_pdy;
-                l_res->numbands = 1;
-            } else {
-                tlcbgxstart = opj_int_ceildivpow2(l_tl_prc_x_start, 1);
-                tlcbgystart = opj_int_ceildivpow2(l_tl_prc_y_start, 1);
-                /*brcbgxend = opj_int_ceildivpow2(l_br_prc_x_end, 1);*/
-                /*brcbgyend = opj_int_ceildivpow2(l_br_prc_y_end, 1);*/
-                cbgwidthexpn = l_pdx - 1;
-                cbgheightexpn = l_pdy - 1;
-                l_res->numbands = 3;
-            }
-
-            cblkwidthexpn = opj_uint_min(l_tccp->cblkw, cbgwidthexpn);
-            cblkheightexpn = opj_uint_min(l_tccp->cblkh, cbgheightexpn);
-            l_band = l_res->bands;
-
-            for (bandno = 0; bandno < l_res->numbands; ++bandno, ++l_band, ++l_step_size) {
-                OPJ_INT32 numbps;
-                /*fprintf(stderr, "\t\t\tband_no=%d/%d\n", bandno, l_res->numbands );*/
-
-                if (resno == 0) {
-                    l_band->bandno = 0 ;
-                    l_band->x0 = opj_int_ceildivpow2(l_tilec->x0, (OPJ_INT32)l_level_no);
-                    l_band->y0 = opj_int_ceildivpow2(l_tilec->y0, (OPJ_INT32)l_level_no);
-                    l_band->x1 = opj_int_ceildivpow2(l_tilec->x1, (OPJ_INT32)l_level_no);
-                    l_band->y1 = opj_int_ceildivpow2(l_tilec->y1, (OPJ_INT32)l_level_no);
-                } else {
-                    l_band->bandno = bandno + 1;
-                    /* x0b = 1 if bandno = 1 or 3 */
-                    l_x0b = l_band->bandno & 1;
-                    /* y0b = 1 if bandno = 2 or 3 */
-                    l_y0b = (OPJ_INT32)((l_band->bandno) >> 1);
-                    /* l_band border (global) */
-                    l_band->x0 = opj_int64_ceildivpow2(l_tilec->x0 - ((OPJ_INT64)l_x0b <<
-                                                       l_level_no), (OPJ_INT32)(l_level_no + 1));
-                    l_band->y0 = opj_int64_ceildivpow2(l_tilec->y0 - ((OPJ_INT64)l_y0b <<
-                                                       l_level_no), (OPJ_INT32)(l_level_no + 1));
-                    l_band->x1 = opj_int64_ceildivpow2(l_tilec->x1 - ((OPJ_INT64)l_x0b <<
-                                                       l_level_no), (OPJ_INT32)(l_level_no + 1));
-                    l_band->y1 = opj_int64_ceildivpow2(l_tilec->y1 - ((OPJ_INT64)l_y0b <<
-                                                       l_level_no), (OPJ_INT32)(l_level_no + 1));
-                }
-
-                if (isEncoder) {
-                    /* Skip empty bands */
-                    if (opj_tcd_is_band_empty(l_band)) {
-                        /* Do not zero l_band->precints to avoid leaks */
-                        /* but make sure we don't use it later, since */
-                        /* it will point to precincts of previous bands... */
-                        continue;
-                    }
-                }
-
-                /** avoid an if with storing function pointer */
-                l_gain = (*l_gain_ptr)(l_band->bandno);
-                numbps = (OPJ_INT32)(l_image_comp->prec + l_gain);
-                l_band->stepsize = (OPJ_FLOAT32)(((1.0 + l_step_size->mant / 2048.0) * pow(2.0,
-                                                  (OPJ_INT32)(numbps - l_step_size->expn)))) * fraction;
-                /* Mb value of Equation E-2 in "E.1 Inverse quantization
-                 * procedure" of the standard */
-                l_band->numbps = l_step_size->expn + (OPJ_INT32)l_tccp->numgbits -
-                                 1;
-
-                if (!l_band->precincts && (l_nb_precincts > 0U)) {
-                    l_band->precincts = (opj_tcd_precinct_t *) opj_malloc(/*3 * */
-                                            l_nb_precinct_size);
-                    if (! l_band->precincts) {
-                        opj_event_msg(manager, EVT_ERROR,
-                                      "Not enough memory to handle band precints\n");
-                        return OPJ_FALSE;
-                    }
-                    /*fprintf(stderr, "\t\t\t\tAllocate precincts of a band (opj_tcd_precinct_t): %d\n",l_nb_precinct_size);     */
-                    memset(l_band->precincts, 0, l_nb_precinct_size);
-                    l_band->precincts_data_size = l_nb_precinct_size;
-                } else if (l_band->precincts_data_size < l_nb_precinct_size) {
-
-                    opj_tcd_precinct_t * new_precincts = (opj_tcd_precinct_t *) opj_realloc(
-                            l_band->precincts,/*3 * */ l_nb_precinct_size);
-                    if (! new_precincts) {
-                        opj_event_msg(manager, EVT_ERROR,
-                                      "Not enough memory to handle band precints\n");
-                        opj_free(l_band->precincts);
-                        l_band->precincts = NULL;
-                        l_band->precincts_data_size = 0;
-                        return OPJ_FALSE;
-                    }
-                    l_band->precincts = new_precincts;
-                    /*fprintf(stderr, "\t\t\t\tReallocate precincts of a band (opj_tcd_precinct_t): from %d to %d\n",l_band->precincts_data_size, l_nb_precinct_size);*/
-                    memset(((OPJ_BYTE *) l_band->precincts) + l_band->precincts_data_size, 0,
-                           l_nb_precinct_size - l_band->precincts_data_size);
-                    l_band->precincts_data_size = l_nb_precinct_size;
-                }
-
-                l_current_precinct = l_band->precincts;
-                for (precno = 0; precno < l_nb_precincts; ++precno) {
-                    OPJ_INT32 tlcblkxstart, tlcblkystart, brcblkxend, brcblkyend;
-                    OPJ_INT32 cbgxstart = tlcbgxstart + (OPJ_INT32)(precno % l_res->pw) *
-                                          (1 << cbgwidthexpn);
-                    OPJ_INT32 cbgystart = tlcbgystart + (OPJ_INT32)(precno / l_res->pw) *
-                                          (1 << cbgheightexpn);
-                    OPJ_INT32 cbgxend = cbgxstart + (1 << cbgwidthexpn);
-                    OPJ_INT32 cbgyend = cbgystart + (1 << cbgheightexpn);
-                    /*fprintf(stderr, "\t precno=%d; bandno=%d, resno=%d; compno=%d\n", precno, bandno , resno, compno);*/
-                    /*fprintf(stderr, "\t tlcbgxstart(=%d) + (precno(=%d) percent res->pw(=%d)) * (1 << cbgwidthexpn(=%d)) \n",tlcbgxstart,precno,l_res->pw,cbgwidthexpn);*/
-
-                    /* precinct size (global) */
-                    /*fprintf(stderr, "\t cbgxstart=%d, l_band->x0 = %d \n",cbgxstart, l_band->x0);*/
-
-                    l_current_precinct->x0 = opj_int_max(cbgxstart, l_band->x0);
-                    l_current_precinct->y0 = opj_int_max(cbgystart, l_band->y0);
-                    l_current_precinct->x1 = opj_int_min(cbgxend, l_band->x1);
-                    l_current_precinct->y1 = opj_int_min(cbgyend, l_band->y1);
-                    /*fprintf(stderr, "\t prc_x0=%d; prc_y0=%d, prc_x1=%d; prc_y1=%d\n",l_current_precinct->x0, l_current_precinct->y0 ,l_current_precinct->x1, l_current_precinct->y1);*/
-
-                    tlcblkxstart = opj_int_floordivpow2(l_current_precinct->x0,
-                                                        (OPJ_INT32)cblkwidthexpn) << cblkwidthexpn;
-                    /*fprintf(stderr, "\t tlcblkxstart =%d\n",tlcblkxstart );*/
-                    tlcblkystart = opj_int_floordivpow2(l_current_precinct->y0,
-                                                        (OPJ_INT32)cblkheightexpn) << cblkheightexpn;
-                    /*fprintf(stderr, "\t tlcblkystart =%d\n",tlcblkystart );*/
-                    brcblkxend = opj_int_ceildivpow2(l_current_precinct->x1,
-                                                     (OPJ_INT32)cblkwidthexpn) << cblkwidthexpn;
-                    /*fprintf(stderr, "\t brcblkxend =%d\n",brcblkxend );*/
-                    brcblkyend = opj_int_ceildivpow2(l_current_precinct->y1,
-                                                     (OPJ_INT32)cblkheightexpn) << cblkheightexpn;
-                    /*fprintf(stderr, "\t brcblkyend =%d\n",brcblkyend );*/
-                    l_current_precinct->cw = (OPJ_UINT32)((brcblkxend - tlcblkxstart) >>
-                                                          cblkwidthexpn);
-                    l_current_precinct->ch = (OPJ_UINT32)((brcblkyend - tlcblkystart) >>
-                                                          cblkheightexpn);
-
-                    if (l_current_precinct->cw && ((OPJ_UINT32)-1) / l_current_precinct->cw < l_current_precinct->ch) {
-                        return OPJ_FALSE;
-                    }
-                    l_nb_code_blocks = l_current_precinct->cw * l_current_precinct->ch;
-                    /*fprintf(stderr, "\t\t\t\t precinct_cw = %d x recinct_ch = %d\n",l_current_precinct->cw, l_current_precinct->ch);      */
-                    if ((((OPJ_UINT32) - 1) / (OPJ_UINT32)sizeof_block) <
-                            l_nb_code_blocks) {
-                        opj_event_msg(manager, EVT_ERROR,
-                                      "Size of code block data exceeds system limits\n");
-                        return OPJ_FALSE;
-                    }
-                    l_nb_code_blocks_size = l_nb_code_blocks * (OPJ_UINT32)sizeof_block;
-
-                    if (!l_current_precinct->cblks.blocks && (l_nb_code_blocks > 0U)) {
-                        l_current_precinct->cblks.blocks = opj_malloc(l_nb_code_blocks_size);
-                        if (! l_current_precinct->cblks.blocks) {
-                            return OPJ_FALSE;
-                        }
-                        /*fprintf(stderr, "\t\t\t\tAllocate cblks of a precinct (opj_tcd_cblk_dec_t): %d\n",l_nb_code_blocks_size);*/
-
-                        memset(l_current_precinct->cblks.blocks, 0, l_nb_code_blocks_size);
-
-                        l_current_precinct->block_size = l_nb_code_blocks_size;
-                    } else if (l_nb_code_blocks_size > l_current_precinct->block_size) {
-                        void *new_blocks = opj_realloc(l_current_precinct->cblks.blocks,
-                                                       l_nb_code_blocks_size);
-                        if (! new_blocks) {
-                            opj_free(l_current_precinct->cblks.blocks);
-                            l_current_precinct->cblks.blocks = NULL;
-                            l_current_precinct->block_size = 0;
-                            opj_event_msg(manager, EVT_ERROR,
-                                          "Not enough memory for current precinct codeblock element\n");
-                            return OPJ_FALSE;
-                        }
-                        l_current_precinct->cblks.blocks = new_blocks;
-                        /*fprintf(stderr, "\t\t\t\tReallocate cblks of a precinct (opj_tcd_cblk_dec_t): from %d to %d\n",l_current_precinct->block_size, l_nb_code_blocks_size);     */
-
-                        memset(((OPJ_BYTE *) l_current_precinct->cblks.blocks) +
-                               l_current_precinct->block_size
-                               , 0
-                               , l_nb_code_blocks_size - l_current_precinct->block_size);
-
-                        l_current_precinct->block_size = l_nb_code_blocks_size;
-                    }
-
-                    if (! l_current_precinct->incltree) {
-                        l_current_precinct->incltree = opj_tgt_create(l_current_precinct->cw,
-                                                       l_current_precinct->ch, manager);
-                    } else {
-                        l_current_precinct->incltree = opj_tgt_init(l_current_precinct->incltree,
-                                                       l_current_precinct->cw, l_current_precinct->ch, manager);
-                    }
-
-                    if (! l_current_precinct->imsbtree) {
-                        l_current_precinct->imsbtree = opj_tgt_create(l_current_precinct->cw,
-                                                       l_current_precinct->ch, manager);
-                    } else {
-                        l_current_precinct->imsbtree = opj_tgt_init(l_current_precinct->imsbtree,
-                                                       l_current_precinct->cw, l_current_precinct->ch, manager);
-                    }
-
-                    for (cblkno = 0; cblkno < l_nb_code_blocks; ++cblkno) {
-                        OPJ_INT32 cblkxstart = tlcblkxstart + (OPJ_INT32)(cblkno %
-                                               l_current_precinct->cw) * (1 << cblkwidthexpn);
-                        OPJ_INT32 cblkystart = tlcblkystart + (OPJ_INT32)(cblkno /
-                                               l_current_precinct->cw) * (1 << cblkheightexpn);
-                        OPJ_INT32 cblkxend = cblkxstart + (1 << cblkwidthexpn);
-                        OPJ_INT32 cblkyend = cblkystart + (1 << cblkheightexpn);
-
-                        if (isEncoder) {
-                            opj_tcd_cblk_enc_t* l_code_block = l_current_precinct->cblks.enc + cblkno;
-
-                            if (! opj_tcd_code_block_enc_allocate(l_code_block)) {
-                                return OPJ_FALSE;
-                            }
-                            /* code-block size (global) */
-                            l_code_block->x0 = opj_int_max(cblkxstart, l_current_precinct->x0);
-                            l_code_block->y0 = opj_int_max(cblkystart, l_current_precinct->y0);
-                            l_code_block->x1 = opj_int_min(cblkxend, l_current_precinct->x1);
-                            l_code_block->y1 = opj_int_min(cblkyend, l_current_precinct->y1);
-
-                            if (! opj_tcd_code_block_enc_allocate_data(l_code_block)) {
-                                return OPJ_FALSE;
-                            }
-                        } else {
-                            opj_tcd_cblk_dec_t* l_code_block = l_current_precinct->cblks.dec + cblkno;
-
-                            if (! opj_tcd_code_block_dec_allocate(l_code_block)) {
-                                return OPJ_FALSE;
-                            }
-                            /* code-block size (global) */
-                            l_code_block->x0 = opj_int_max(cblkxstart, l_current_precinct->x0);
-                            l_code_block->y0 = opj_int_max(cblkystart, l_current_precinct->y0);
-                            l_code_block->x1 = opj_int_min(cblkxend, l_current_precinct->x1);
-                            l_code_block->y1 = opj_int_min(cblkyend, l_current_precinct->y1);
-                        }
-                    }
-                    ++l_current_precinct;
-                } /* precno */
-            } /* bandno */
-            ++l_res;
-        } /* resno */
-        ++l_tccp;
-        ++l_tilec;
-        ++l_image_comp;
-    } /* compno */
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_tcd_init_encode_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
-                                  opj_event_mgr_t* p_manager)
-{
-    return opj_tcd_init_tile(p_tcd, p_tile_no, OPJ_TRUE, 1.0F,
-                             sizeof(opj_tcd_cblk_enc_t), p_manager);
-}
-
-OPJ_BOOL opj_tcd_init_decode_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
-                                  opj_event_mgr_t* p_manager)
-{
-    return opj_tcd_init_tile(p_tcd, p_tile_no, OPJ_FALSE, 0.5F,
-                             sizeof(opj_tcd_cblk_dec_t), p_manager);
-}
-
-/**
- * Allocates memory for an encoding code block (but not data memory).
- */
-static OPJ_BOOL opj_tcd_code_block_enc_allocate(opj_tcd_cblk_enc_t *
-        p_code_block)
-{
-    if (! p_code_block->layers) {
-        /* no memset since data */
-        p_code_block->layers = (opj_tcd_layer_t*) opj_calloc(100,
-                               sizeof(opj_tcd_layer_t));
-        if (! p_code_block->layers) {
-            return OPJ_FALSE;
-        }
-    }
-    if (! p_code_block->passes) {
-        p_code_block->passes = (opj_tcd_pass_t*) opj_calloc(100,
-                               sizeof(opj_tcd_pass_t));
-        if (! p_code_block->passes) {
-            return OPJ_FALSE;
-        }
-    }
-    return OPJ_TRUE;
-}
-
-/**
- * Allocates data memory for an encoding code block.
- */
-static OPJ_BOOL opj_tcd_code_block_enc_allocate_data(opj_tcd_cblk_enc_t *
-        p_code_block)
-{
-    OPJ_UINT32 l_data_size;
-
-    /* +1 is needed for https://github.com/uclouvain/openjpeg/issues/835 */
-    /* and actually +2 required for https://github.com/uclouvain/openjpeg/issues/982 */
-    /* TODO: is there a theoretical upper-bound for the compressed code */
-    /* block size ? */
-    l_data_size = 2 + (OPJ_UINT32)((p_code_block->x1 - p_code_block->x0) *
-                                   (p_code_block->y1 - p_code_block->y0) * (OPJ_INT32)sizeof(OPJ_UINT32));
-
-    if (l_data_size > p_code_block->data_size) {
-        if (p_code_block->data) {
-            /* We refer to data - 1 since below we incremented it */
-            opj_free(p_code_block->data - 1);
-        }
-        p_code_block->data = (OPJ_BYTE*) opj_malloc(l_data_size + 1);
-        if (! p_code_block->data) {
-            p_code_block->data_size = 0U;
-            return OPJ_FALSE;
-        }
-        p_code_block->data_size = l_data_size;
-
-        /* We reserve the initial byte as a fake byte to a non-FF value */
-        /* and increment the data pointer, so that opj_mqc_init_enc() */
-        /* can do bp = data - 1, and opj_mqc_byteout() can safely dereference */
-        /* it. */
-        p_code_block->data[0] = 0;
-        p_code_block->data += 1; /*why +1 ?*/
-    }
-    return OPJ_TRUE;
-}
-
-
-void opj_tcd_reinit_segment(opj_tcd_seg_t* seg)
-{
-    memset(seg, 0, sizeof(opj_tcd_seg_t));
-}
-
-/**
- * Allocates memory for a decoding code block.
- */
-static OPJ_BOOL opj_tcd_code_block_dec_allocate(opj_tcd_cblk_dec_t *
-        p_code_block)
-{
-    if (! p_code_block->segs) {
-
-        p_code_block->segs = (opj_tcd_seg_t *) opj_calloc(OPJ_J2K_DEFAULT_NB_SEGS,
-                             sizeof(opj_tcd_seg_t));
-        if (! p_code_block->segs) {
-            return OPJ_FALSE;
-        }
-        /*fprintf(stderr, "Allocate %d elements of code_block->data\n", OPJ_J2K_DEFAULT_NB_SEGS * sizeof(opj_tcd_seg_t));*/
-
-        p_code_block->m_current_max_segs = OPJ_J2K_DEFAULT_NB_SEGS;
-        /*fprintf(stderr, "m_current_max_segs of code_block->data = %d\n", p_code_block->m_current_max_segs);*/
-    } else {
-        /* sanitize */
-        opj_tcd_seg_t * l_segs = p_code_block->segs;
-        OPJ_UINT32 l_current_max_segs = p_code_block->m_current_max_segs;
-        opj_tcd_seg_data_chunk_t* l_chunks = p_code_block->chunks;
-        OPJ_UINT32 l_numchunksalloc = p_code_block->numchunksalloc;
-        OPJ_UINT32 i;
-
-        opj_aligned_free(p_code_block->decoded_data);
-        p_code_block->decoded_data = 00;
-
-        memset(p_code_block, 0, sizeof(opj_tcd_cblk_dec_t));
-        p_code_block->segs = l_segs;
-        p_code_block->m_current_max_segs = l_current_max_segs;
-        for (i = 0; i < l_current_max_segs; ++i) {
-            opj_tcd_reinit_segment(&l_segs[i]);
-        }
-        p_code_block->chunks = l_chunks;
-        p_code_block->numchunksalloc = l_numchunksalloc;
-    }
-
-    return OPJ_TRUE;
-}
-
-OPJ_UINT32 opj_tcd_get_decoded_tile_size(opj_tcd_t *p_tcd,
-        OPJ_BOOL take_into_account_partial_decoding)
-{
-    OPJ_UINT32 i;
-    OPJ_UINT32 l_data_size = 0;
-    opj_image_comp_t * l_img_comp = 00;
-    opj_tcd_tilecomp_t * l_tile_comp = 00;
-    opj_tcd_resolution_t * l_res = 00;
-    OPJ_UINT32 l_size_comp, l_remaining;
-    OPJ_UINT32 l_temp;
-
-    l_tile_comp = p_tcd->tcd_image->tiles->comps;
-    l_img_comp = p_tcd->image->comps;
-
-    for (i = 0; i < p_tcd->image->numcomps; ++i) {
-        OPJ_UINT32 w, h;
-        l_size_comp = l_img_comp->prec >> 3; /*(/ 8)*/
-        l_remaining = l_img_comp->prec & 7;  /* (%8) */
-
-        if (l_remaining) {
-            ++l_size_comp;
-        }
-
-        if (l_size_comp == 3) {
-            l_size_comp = 4;
-        }
-
-        l_res = l_tile_comp->resolutions + l_tile_comp->minimum_num_resolutions - 1;
-        if (take_into_account_partial_decoding && !p_tcd->whole_tile_decoding) {
-            w = l_res->win_x1 - l_res->win_x0;
-            h = l_res->win_y1 - l_res->win_y0;
-        } else {
-            w = (OPJ_UINT32)(l_res->x1 - l_res->x0);
-            h = (OPJ_UINT32)(l_res->y1 - l_res->y0);
-        }
-        if (h > 0 && UINT_MAX / w < h) {
-            return UINT_MAX;
-        }
-        l_temp = w * h;
-        if (l_size_comp && UINT_MAX / l_size_comp < l_temp) {
-            return UINT_MAX;
-        }
-        l_temp *= l_size_comp;
-
-        if (l_temp > UINT_MAX - l_data_size) {
-            return UINT_MAX;
-        }
-        l_data_size += l_temp;
-        ++l_img_comp;
-        ++l_tile_comp;
-    }
-
-    return l_data_size;
-}
-
-OPJ_BOOL opj_tcd_encode_tile(opj_tcd_t *p_tcd,
-                             OPJ_UINT32 p_tile_no,
-                             OPJ_BYTE *p_dest,
-                             OPJ_UINT32 * p_data_written,
-                             OPJ_UINT32 p_max_length,
-                             opj_codestream_info_t *p_cstr_info,
-                             opj_event_mgr_t *p_manager)
-{
-
-    if (p_tcd->cur_tp_num == 0) {
-
-        p_tcd->tcd_tileno = p_tile_no;
-        p_tcd->tcp = &p_tcd->cp->tcps[p_tile_no];
-
-        /* INDEX >> "Precinct_nb_X et Precinct_nb_Y" */
-        if (p_cstr_info)  {
-            OPJ_UINT32 l_num_packs = 0;
-            OPJ_UINT32 i;
-            opj_tcd_tilecomp_t *l_tilec_idx =
-                &p_tcd->tcd_image->tiles->comps[0];        /* based on component 0 */
-            opj_tccp_t *l_tccp = p_tcd->tcp->tccps; /* based on component 0 */
-
-            for (i = 0; i < l_tilec_idx->numresolutions; i++) {
-                opj_tcd_resolution_t *l_res_idx = &l_tilec_idx->resolutions[i];
-
-                p_cstr_info->tile[p_tile_no].pw[i] = (int)l_res_idx->pw;
-                p_cstr_info->tile[p_tile_no].ph[i] = (int)l_res_idx->ph;
-
-                l_num_packs += l_res_idx->pw * l_res_idx->ph;
-                p_cstr_info->tile[p_tile_no].pdx[i] = (int)l_tccp->prcw[i];
-                p_cstr_info->tile[p_tile_no].pdy[i] = (int)l_tccp->prch[i];
-            }
-            p_cstr_info->tile[p_tile_no].packet = (opj_packet_info_t*) opj_calloc((
-                    OPJ_SIZE_T)p_cstr_info->numcomps * (OPJ_SIZE_T)p_cstr_info->numlayers *
-                                                  l_num_packs,
-                                                  sizeof(opj_packet_info_t));
-            if (!p_cstr_info->tile[p_tile_no].packet) {
-                /* FIXME event manager error callback */
-                return OPJ_FALSE;
-            }
-        }
-        /* << INDEX */
-
-        /* FIXME _ProfStart(PGROUP_DC_SHIFT); */
-        /*---------------TILE-------------------*/
-        if (! opj_tcd_dc_level_shift_encode(p_tcd)) {
-            return OPJ_FALSE;
-        }
-        /* FIXME _ProfStop(PGROUP_DC_SHIFT); */
-
-        /* FIXME _ProfStart(PGROUP_MCT); */
-        if (! opj_tcd_mct_encode(p_tcd)) {
-            return OPJ_FALSE;
-        }
-        /* FIXME _ProfStop(PGROUP_MCT); */
-
-        /* FIXME _ProfStart(PGROUP_DWT); */
-        if (! opj_tcd_dwt_encode(p_tcd)) {
-            return OPJ_FALSE;
-        }
-        /* FIXME  _ProfStop(PGROUP_DWT); */
-
-        /* FIXME  _ProfStart(PGROUP_T1); */
-        if (! opj_tcd_t1_encode(p_tcd)) {
-            return OPJ_FALSE;
-        }
-        /* FIXME _ProfStop(PGROUP_T1); */
-
-        /* FIXME _ProfStart(PGROUP_RATE); */
-        if (! opj_tcd_rate_allocate_encode(p_tcd, p_dest, p_max_length,
-                                           p_cstr_info, p_manager)) {
-            return OPJ_FALSE;
-        }
-        /* FIXME _ProfStop(PGROUP_RATE); */
-
-    }
-    /*--------------TIER2------------------*/
-
-    /* INDEX */
-    if (p_cstr_info) {
-        p_cstr_info->index_write = 1;
-    }
-    /* FIXME _ProfStart(PGROUP_T2); */
-
-    if (! opj_tcd_t2_encode(p_tcd, p_dest, p_data_written, p_max_length,
-                            p_cstr_info, p_manager)) {
-        return OPJ_FALSE;
-    }
-    /* FIXME _ProfStop(PGROUP_T2); */
-
-    /*---------------CLEAN-------------------*/
-
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_tcd_decode_tile(opj_tcd_t *p_tcd,
-                             OPJ_UINT32 win_x0,
-                             OPJ_UINT32 win_y0,
-                             OPJ_UINT32 win_x1,
-                             OPJ_UINT32 win_y1,
-                             OPJ_UINT32 numcomps_to_decode,
-                             const OPJ_UINT32 *comps_indices,
-                             OPJ_BYTE *p_src,
-                             OPJ_UINT32 p_max_length,
-                             OPJ_UINT32 p_tile_no,
-                             opj_codestream_index_t *p_cstr_index,
-                             opj_event_mgr_t *p_manager
-                            )
-{
-    OPJ_UINT32 l_data_read;
-    OPJ_UINT32 compno;
-
-    p_tcd->tcd_tileno = p_tile_no;
-    p_tcd->tcp = &(p_tcd->cp->tcps[p_tile_no]);
-    p_tcd->win_x0 = win_x0;
-    p_tcd->win_y0 = win_y0;
-    p_tcd->win_x1 = win_x1;
-    p_tcd->win_y1 = win_y1;
-    p_tcd->whole_tile_decoding = OPJ_TRUE;
-
-    opj_free(p_tcd->used_component);
-    p_tcd->used_component = NULL;
-
-    if (numcomps_to_decode) {
-        OPJ_BOOL* used_component = (OPJ_BOOL*) opj_calloc(sizeof(OPJ_BOOL),
-                                   p_tcd->image->numcomps);
-        if (used_component == NULL) {
-            return OPJ_FALSE;
-        }
-        for (compno = 0; compno < numcomps_to_decode; compno++) {
-            used_component[ comps_indices[compno] ] = OPJ_TRUE;
-        }
-
-        p_tcd->used_component = used_component;
-    }
-
-    for (compno = 0; compno < p_tcd->image->numcomps; compno++) {
-        if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
-            continue;
-        }
-
-        if (!opj_tcd_is_whole_tilecomp_decoding(p_tcd, compno)) {
-            p_tcd->whole_tile_decoding = OPJ_FALSE;
-            break;
-        }
-    }
-
-    if (p_tcd->whole_tile_decoding) {
-        for (compno = 0; compno < p_tcd->image->numcomps; compno++) {
-            opj_tcd_tilecomp_t* tilec = &(p_tcd->tcd_image->tiles->comps[compno]);
-            opj_tcd_resolution_t *l_res = &
-                                          (tilec->resolutions[tilec->minimum_num_resolutions - 1]);
-            OPJ_SIZE_T l_data_size;
-
-            /* compute l_data_size with overflow check */
-            OPJ_SIZE_T res_w = (OPJ_SIZE_T)(l_res->x1 - l_res->x0);
-            OPJ_SIZE_T res_h = (OPJ_SIZE_T)(l_res->y1 - l_res->y0);
-
-            if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
-                continue;
-            }
-
-            /* issue 733, l_data_size == 0U, probably something wrong should be checked before getting here */
-            if (res_h > 0 && res_w > SIZE_MAX / res_h) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Size of tile data exceeds system limits\n");
-                return OPJ_FALSE;
-            }
-            l_data_size = res_w * res_h;
-
-            if (SIZE_MAX / sizeof(OPJ_UINT32) < l_data_size) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Size of tile data exceeds system limits\n");
-                return OPJ_FALSE;
-            }
-            l_data_size *= sizeof(OPJ_UINT32);
-
-            tilec->data_size_needed = l_data_size;
-
-            if (!opj_alloc_tile_component_data(tilec)) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Size of tile data exceeds system limits\n");
-                return OPJ_FALSE;
-            }
-        }
-    } else {
-        /* Compute restricted tile-component and tile-resolution coordinates */
-        /* of the window of interest, but defer the memory allocation until */
-        /* we know the resno_decoded */
-        for (compno = 0; compno < p_tcd->image->numcomps; compno++) {
-            OPJ_UINT32 resno;
-            opj_tcd_tilecomp_t* tilec = &(p_tcd->tcd_image->tiles->comps[compno]);
-            opj_image_comp_t* image_comp = &(p_tcd->image->comps[compno]);
-
-            if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
-                continue;
-            }
-
-            /* Compute the intersection of the area of interest, expressed in tile coordinates */
-            /* with the tile coordinates */
-            tilec->win_x0 = opj_uint_max(
-                                (OPJ_UINT32)tilec->x0,
-                                opj_uint_ceildiv(p_tcd->win_x0, image_comp->dx));
-            tilec->win_y0 = opj_uint_max(
-                                (OPJ_UINT32)tilec->y0,
-                                opj_uint_ceildiv(p_tcd->win_y0, image_comp->dy));
-            tilec->win_x1 = opj_uint_min(
-                                (OPJ_UINT32)tilec->x1,
-                                opj_uint_ceildiv(p_tcd->win_x1, image_comp->dx));
-            tilec->win_y1 = opj_uint_min(
-                                (OPJ_UINT32)tilec->y1,
-                                opj_uint_ceildiv(p_tcd->win_y1, image_comp->dy));
-            if (tilec->win_x1 < tilec->win_x0 ||
-                    tilec->win_y1 < tilec->win_y0) {
-                /* We should not normally go there. The circumstance is when */
-                /* the tile coordinates do not intersect the area of interest */
-                /* Upper level logic should not even try to decode that tile */
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Invalid tilec->win_xxx values\n");
-                return OPJ_FALSE;
-            }
-
-            for (resno = 0; resno < tilec->numresolutions; ++resno) {
-                opj_tcd_resolution_t *res = tilec->resolutions + resno;
-                res->win_x0 = opj_uint_ceildivpow2(tilec->win_x0,
-                                                   tilec->numresolutions - 1 - resno);
-                res->win_y0 = opj_uint_ceildivpow2(tilec->win_y0,
-                                                   tilec->numresolutions - 1 - resno);
-                res->win_x1 = opj_uint_ceildivpow2(tilec->win_x1,
-                                                   tilec->numresolutions - 1 - resno);
-                res->win_y1 = opj_uint_ceildivpow2(tilec->win_y1,
-                                                   tilec->numresolutions - 1 - resno);
-            }
-        }
-    }
-
-#ifdef TODO_MSD /* FIXME */
-    /* INDEX >>  */
-    if (p_cstr_info) {
-        OPJ_UINT32 resno, compno, numprec = 0;
-        for (compno = 0; compno < (OPJ_UINT32) p_cstr_info->numcomps; compno++) {
-            opj_tcp_t *tcp = &p_tcd->cp->tcps[0];
-            opj_tccp_t *tccp = &tcp->tccps[compno];
-            opj_tcd_tilecomp_t *tilec_idx = &p_tcd->tcd_image->tiles->comps[compno];
-            for (resno = 0; resno < tilec_idx->numresolutions; resno++) {
-                opj_tcd_resolution_t *res_idx = &tilec_idx->resolutions[resno];
-                p_cstr_info->tile[p_tile_no].pw[resno] = res_idx->pw;
-                p_cstr_info->tile[p_tile_no].ph[resno] = res_idx->ph;
-                numprec += res_idx->pw * res_idx->ph;
-                p_cstr_info->tile[p_tile_no].pdx[resno] = tccp->prcw[resno];
-                p_cstr_info->tile[p_tile_no].pdy[resno] = tccp->prch[resno];
-            }
-        }
-        p_cstr_info->tile[p_tile_no].packet = (opj_packet_info_t *) opj_malloc(
-                p_cstr_info->numlayers * numprec * sizeof(opj_packet_info_t));
-        p_cstr_info->packno = 0;
-    }
-    /* << INDEX */
-#endif
-
-    /*--------------TIER2------------------*/
-    /* FIXME _ProfStart(PGROUP_T2); */
-    l_data_read = 0;
-    if (! opj_tcd_t2_decode(p_tcd, p_src, &l_data_read, p_max_length, p_cstr_index,
-                            p_manager)) {
-        return OPJ_FALSE;
-    }
-    /* FIXME _ProfStop(PGROUP_T2); */
-
-    /*------------------TIER1-----------------*/
-
-    /* FIXME _ProfStart(PGROUP_T1); */
-    if (! opj_tcd_t1_decode(p_tcd, p_manager)) {
-        return OPJ_FALSE;
-    }
-    /* FIXME _ProfStop(PGROUP_T1); */
-
-
-    /* For subtile decoding, now we know the resno_decoded, we can allocate */
-    /* the tile data buffer */
-    if (!p_tcd->whole_tile_decoding) {
-        for (compno = 0; compno < p_tcd->image->numcomps; compno++) {
-            opj_tcd_tilecomp_t* tilec = &(p_tcd->tcd_image->tiles->comps[compno]);
-            opj_image_comp_t* image_comp = &(p_tcd->image->comps[compno]);
-            opj_tcd_resolution_t *res = tilec->resolutions + image_comp->resno_decoded;
-            OPJ_SIZE_T w = res->win_x1 - res->win_x0;
-            OPJ_SIZE_T h = res->win_y1 - res->win_y0;
-            OPJ_SIZE_T l_data_size;
-
-            opj_image_data_free(tilec->data_win);
-            tilec->data_win = NULL;
-
-            if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
-                continue;
-            }
-
-            if (w > 0 && h > 0) {
-                if (w > SIZE_MAX / h) {
-                    opj_event_msg(p_manager, EVT_ERROR,
-                                  "Size of tile data exceeds system limits\n");
-                    return OPJ_FALSE;
-                }
-                l_data_size = w * h;
-                if (l_data_size > SIZE_MAX / sizeof(OPJ_INT32)) {
-                    opj_event_msg(p_manager, EVT_ERROR,
-                                  "Size of tile data exceeds system limits\n");
-                    return OPJ_FALSE;
-                }
-                l_data_size *= sizeof(OPJ_INT32);
-
-                tilec->data_win = (OPJ_INT32*) opj_image_data_alloc(l_data_size);
-                if (tilec->data_win == NULL) {
-                    opj_event_msg(p_manager, EVT_ERROR,
-                                  "Size of tile data exceeds system limits\n");
-                    return OPJ_FALSE;
-                }
-            }
-        }
-    }
-
-    /*----------------DWT---------------------*/
-
-    /* FIXME _ProfStart(PGROUP_DWT); */
-    if
-    (! opj_tcd_dwt_decode(p_tcd)) {
-        return OPJ_FALSE;
-    }
-    /* FIXME _ProfStop(PGROUP_DWT); */
-
-    /*----------------MCT-------------------*/
-    /* FIXME _ProfStart(PGROUP_MCT); */
-    if
-    (! opj_tcd_mct_decode(p_tcd, p_manager)) {
-        return OPJ_FALSE;
-    }
-    /* FIXME _ProfStop(PGROUP_MCT); */
-
-    /* FIXME _ProfStart(PGROUP_DC_SHIFT); */
-    if
-    (! opj_tcd_dc_level_shift_decode(p_tcd)) {
-        return OPJ_FALSE;
-    }
-    /* FIXME _ProfStop(PGROUP_DC_SHIFT); */
-
-
-    /*---------------TILE-------------------*/
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_tcd_update_tile_data(opj_tcd_t *p_tcd,
-                                  OPJ_BYTE * p_dest,
-                                  OPJ_UINT32 p_dest_length
-                                 )
-{
-    OPJ_UINT32 i, j, k, l_data_size = 0;
-    opj_image_comp_t * l_img_comp = 00;
-    opj_tcd_tilecomp_t * l_tilec = 00;
-    opj_tcd_resolution_t * l_res;
-    OPJ_UINT32 l_size_comp, l_remaining;
-    OPJ_UINT32 l_stride, l_width, l_height;
-
-    l_data_size = opj_tcd_get_decoded_tile_size(p_tcd, OPJ_TRUE);
-    if (l_data_size == UINT_MAX || l_data_size > p_dest_length) {
-        return OPJ_FALSE;
-    }
-
-    l_tilec = p_tcd->tcd_image->tiles->comps;
-    l_img_comp = p_tcd->image->comps;
-
-    for (i = 0; i < p_tcd->image->numcomps; ++i) {
-        const OPJ_INT32* l_src_data;
-        l_size_comp = l_img_comp->prec >> 3; /*(/ 8)*/
-        l_remaining = l_img_comp->prec & 7;  /* (%8) */
-        l_res = l_tilec->resolutions + l_img_comp->resno_decoded;
-        if (p_tcd->whole_tile_decoding) {
-            l_width = (OPJ_UINT32)(l_res->x1 - l_res->x0);
-            l_height = (OPJ_UINT32)(l_res->y1 - l_res->y0);
-            l_stride = (OPJ_UINT32)(l_tilec->resolutions[l_tilec->minimum_num_resolutions -
-                                                                     1].x1 -
-                                    l_tilec->resolutions[l_tilec->minimum_num_resolutions - 1].x0) - l_width;
-            l_src_data = l_tilec->data;
-        } else {
-            l_width = l_res->win_x1 - l_res->win_x0;
-            l_height = l_res->win_y1 - l_res->win_y0;
-            l_stride = 0;
-            l_src_data = l_tilec->data_win;
-        }
-
-        if (l_remaining) {
-            ++l_size_comp;
-        }
-
-        if (l_size_comp == 3) {
-            l_size_comp = 4;
-        }
-
-        switch (l_size_comp) {
-        case 1: {
-            OPJ_CHAR * l_dest_ptr = (OPJ_CHAR *) p_dest;
-            const OPJ_INT32 * l_src_ptr = l_src_data;
-
-            if (l_img_comp->sgnd) {
-                for (j = 0; j < l_height; ++j) {
-                    for (k = 0; k < l_width; ++k) {
-                        *(l_dest_ptr++) = (OPJ_CHAR)(*(l_src_ptr++));
-                    }
-                    l_src_ptr += l_stride;
-                }
-            } else {
-                for (j = 0; j < l_height; ++j) {
-                    for (k = 0; k < l_width; ++k) {
-                        *(l_dest_ptr++) = (OPJ_CHAR)((*(l_src_ptr++)) & 0xff);
-                    }
-                    l_src_ptr += l_stride;
-                }
-            }
-
-            p_dest = (OPJ_BYTE *)l_dest_ptr;
-        }
-        break;
-        case 2: {
-            const OPJ_INT32 * l_src_ptr = l_src_data;
-            OPJ_INT16 * l_dest_ptr = (OPJ_INT16 *) p_dest;
-
-            if (l_img_comp->sgnd) {
-                for (j = 0; j < l_height; ++j) {
-                    for (k = 0; k < l_width; ++k) {
-                        OPJ_INT16 val = (OPJ_INT16)(*(l_src_ptr++));
-                        memcpy(l_dest_ptr, &val, sizeof(val));
-                        l_dest_ptr ++;
-                    }
-                    l_src_ptr += l_stride;
-                }
-            } else {
-                for (j = 0; j < l_height; ++j) {
-                    for (k = 0; k < l_width; ++k) {
-                        OPJ_INT16 val = (OPJ_INT16)((*(l_src_ptr++)) & 0xffff);
-                        memcpy(l_dest_ptr, &val, sizeof(val));
-                        l_dest_ptr ++;
-                    }
-                    l_src_ptr += l_stride;
-                }
-            }
-
-            p_dest = (OPJ_BYTE*) l_dest_ptr;
-        }
-        break;
-        case 4: {
-            OPJ_INT32 * l_dest_ptr = (OPJ_INT32 *) p_dest;
-            const OPJ_INT32 * l_src_ptr = l_src_data;
-
-            for (j = 0; j < l_height; ++j) {
-                memcpy(l_dest_ptr, l_src_ptr, l_width * sizeof(OPJ_INT32));
-                l_dest_ptr += l_width;
-                l_src_ptr += l_width + l_stride;
-            }
-
-            p_dest = (OPJ_BYTE*) l_dest_ptr;
-        }
-        break;
-        }
-
-        ++l_img_comp;
-        ++l_tilec;
-    }
-
-    return OPJ_TRUE;
-}
-
-
-
-
-static void opj_tcd_free_tile(opj_tcd_t *p_tcd)
-{
-    OPJ_UINT32 compno, resno, bandno, precno;
-    opj_tcd_tile_t *l_tile = 00;
-    opj_tcd_tilecomp_t *l_tile_comp = 00;
-    opj_tcd_resolution_t *l_res = 00;
-    opj_tcd_band_t *l_band = 00;
-    opj_tcd_precinct_t *l_precinct = 00;
-    OPJ_UINT32 l_nb_resolutions, l_nb_precincts;
-    void (* l_tcd_code_block_deallocate)(opj_tcd_precinct_t *) = 00;
-
-    if (! p_tcd) {
-        return;
-    }
-
-    if (! p_tcd->tcd_image) {
-        return;
-    }
-
-    if (p_tcd->m_is_decoder) {
-        l_tcd_code_block_deallocate = opj_tcd_code_block_dec_deallocate;
-    } else {
-        l_tcd_code_block_deallocate = opj_tcd_code_block_enc_deallocate;
-    }
-
-    l_tile = p_tcd->tcd_image->tiles;
-    if (! l_tile) {
-        return;
-    }
-
-    l_tile_comp = l_tile->comps;
-
-    for (compno = 0; compno < l_tile->numcomps; ++compno) {
-        l_res = l_tile_comp->resolutions;
-        if (l_res) {
-
-            l_nb_resolutions = l_tile_comp->resolutions_size / (OPJ_UINT32)sizeof(
-                                   opj_tcd_resolution_t);
-            for (resno = 0; resno < l_nb_resolutions; ++resno) {
-                l_band = l_res->bands;
-                for (bandno = 0; bandno < 3; ++bandno) {
-                    l_precinct = l_band->precincts;
-                    if (l_precinct) {
-
-                        l_nb_precincts = l_band->precincts_data_size / (OPJ_UINT32)sizeof(
-                                             opj_tcd_precinct_t);
-                        for (precno = 0; precno < l_nb_precincts; ++precno) {
-                            opj_tgt_destroy(l_precinct->incltree);
-                            l_precinct->incltree = 00;
-                            opj_tgt_destroy(l_precinct->imsbtree);
-                            l_precinct->imsbtree = 00;
-                            (*l_tcd_code_block_deallocate)(l_precinct);
-                            ++l_precinct;
-                        }
-
-                        opj_free(l_band->precincts);
-                        l_band->precincts = 00;
-                    }
-                    ++l_band;
-                } /* for (resno */
-                ++l_res;
-            }
-
-            opj_free(l_tile_comp->resolutions);
-            l_tile_comp->resolutions = 00;
-        }
-
-        if (l_tile_comp->ownsData && l_tile_comp->data) {
-            opj_image_data_free(l_tile_comp->data);
-            l_tile_comp->data = 00;
-            l_tile_comp->ownsData = 0;
-            l_tile_comp->data_size = 0;
-            l_tile_comp->data_size_needed = 0;
-        }
-
-        opj_image_data_free(l_tile_comp->data_win);
-
-        ++l_tile_comp;
-    }
-
-    opj_free(l_tile->comps);
-    l_tile->comps = 00;
-    opj_free(p_tcd->tcd_image->tiles);
-    p_tcd->tcd_image->tiles = 00;
-}
-
-
-static OPJ_BOOL opj_tcd_t2_decode(opj_tcd_t *p_tcd,
-                                  OPJ_BYTE * p_src_data,
-                                  OPJ_UINT32 * p_data_read,
-                                  OPJ_UINT32 p_max_src_size,
-                                  opj_codestream_index_t *p_cstr_index,
-                                  opj_event_mgr_t *p_manager
-                                 )
-{
-    opj_t2_t * l_t2;
-
-    l_t2 = opj_t2_create(p_tcd->image, p_tcd->cp);
-    if (l_t2 == 00) {
-        return OPJ_FALSE;
-    }
-
-    if (! opj_t2_decode_packets(
-                p_tcd,
-                l_t2,
-                p_tcd->tcd_tileno,
-                p_tcd->tcd_image->tiles,
-                p_src_data,
-                p_data_read,
-                p_max_src_size,
-                p_cstr_index,
-                p_manager)) {
-        opj_t2_destroy(l_t2);
-        return OPJ_FALSE;
-    }
-
-    opj_t2_destroy(l_t2);
-
-    /*---------------CLEAN-------------------*/
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_tcd_t1_decode(opj_tcd_t *p_tcd, opj_event_mgr_t *p_manager)
-{
-    OPJ_UINT32 compno;
-    opj_tcd_tile_t * l_tile = p_tcd->tcd_image->tiles;
-    opj_tcd_tilecomp_t* l_tile_comp = l_tile->comps;
-    opj_tccp_t * l_tccp = p_tcd->tcp->tccps;
-    volatile OPJ_BOOL ret = OPJ_TRUE;
-    OPJ_BOOL check_pterm = OPJ_FALSE;
-    opj_mutex_t* p_manager_mutex = NULL;
-
-    p_manager_mutex = opj_mutex_create();
-
-    /* Only enable PTERM check if we decode all layers */
-    if (p_tcd->tcp->num_layers_to_decode == p_tcd->tcp->numlayers &&
-            (l_tccp->cblksty & J2K_CCP_CBLKSTY_PTERM) != 0) {
-        check_pterm = OPJ_TRUE;
-    }
-
-    for (compno = 0; compno < l_tile->numcomps;
-            ++compno, ++l_tile_comp, ++l_tccp) {
-        if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
-            continue;
-        }
-
-        opj_t1_decode_cblks(p_tcd, &ret, l_tile_comp, l_tccp,
-                            p_manager, p_manager_mutex, check_pterm);
-        if (!ret) {
-            break;
-        }
-    }
-
-    opj_thread_pool_wait_completion(p_tcd->thread_pool, 0);
-    if (p_manager_mutex) {
-        opj_mutex_destroy(p_manager_mutex);
-    }
-    return ret;
-}
-
-
-static OPJ_BOOL opj_tcd_dwt_decode(opj_tcd_t *p_tcd)
-{
-    OPJ_UINT32 compno;
-    opj_tcd_tile_t * l_tile = p_tcd->tcd_image->tiles;
-    opj_tcd_tilecomp_t * l_tile_comp = l_tile->comps;
-    opj_tccp_t * l_tccp = p_tcd->tcp->tccps;
-    opj_image_comp_t * l_img_comp = p_tcd->image->comps;
-
-    for (compno = 0; compno < l_tile->numcomps;
-            compno++, ++l_tile_comp, ++l_img_comp, ++l_tccp) {
-        if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
-            continue;
-        }
-
-        if (l_tccp->qmfbid == 1) {
-            if (! opj_dwt_decode(p_tcd, l_tile_comp,
-                                 l_img_comp->resno_decoded + 1)) {
-                return OPJ_FALSE;
-            }
-        } else {
-            if (! opj_dwt_decode_real(p_tcd, l_tile_comp,
-                                      l_img_comp->resno_decoded + 1)) {
-                return OPJ_FALSE;
-            }
-        }
-
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_tcd_mct_decode(opj_tcd_t *p_tcd, opj_event_mgr_t *p_manager)
-{
-    opj_tcd_tile_t * l_tile = p_tcd->tcd_image->tiles;
-    opj_tcp_t * l_tcp = p_tcd->tcp;
-    opj_tcd_tilecomp_t * l_tile_comp = l_tile->comps;
-    OPJ_UINT32 l_samples, i;
-
-    if (l_tcp->mct == 0 || p_tcd->used_component != NULL) {
-        return OPJ_TRUE;
-    }
-
-    if (p_tcd->whole_tile_decoding) {
-        opj_tcd_resolution_t* res_comp0 = l_tile->comps[0].resolutions +
-                                          l_tile_comp->minimum_num_resolutions - 1;
-
-        /* A bit inefficient: we process more data than needed if */
-        /* resno_decoded < l_tile_comp->minimum_num_resolutions-1, */
-        /* but we would need to take into account a stride then */
-        l_samples = (OPJ_UINT32)((res_comp0->x1 - res_comp0->x0) *
-                                 (res_comp0->y1 - res_comp0->y0));
-        if (l_tile->numcomps >= 3) {
-            if (l_tile_comp->minimum_num_resolutions !=
-                    l_tile->comps[1].minimum_num_resolutions ||
-                    l_tile_comp->minimum_num_resolutions !=
-                    l_tile->comps[2].minimum_num_resolutions) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Tiles don't all have the same dimension. Skip the MCT step.\n");
-                return OPJ_FALSE;
-            }
-        }
-        if (l_tile->numcomps >= 3) {
-            opj_tcd_resolution_t* res_comp1 = l_tile->comps[1].resolutions +
-                                              l_tile_comp->minimum_num_resolutions - 1;
-            opj_tcd_resolution_t* res_comp2 = l_tile->comps[2].resolutions +
-                                              l_tile_comp->minimum_num_resolutions - 1;
-            /* testcase 1336.pdf.asan.47.376 */
-            if (p_tcd->image->comps[0].resno_decoded !=
-                    p_tcd->image->comps[1].resno_decoded ||
-                    p_tcd->image->comps[0].resno_decoded !=
-                    p_tcd->image->comps[2].resno_decoded ||
-                    (OPJ_SIZE_T)(res_comp1->x1 - res_comp1->x0) *
-                    (OPJ_SIZE_T)(res_comp1->y1 - res_comp1->y0) != l_samples ||
-                    (OPJ_SIZE_T)(res_comp2->x1 - res_comp2->x0) *
-                    (OPJ_SIZE_T)(res_comp2->y1 - res_comp2->y0) != l_samples) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Tiles don't all have the same dimension. Skip the MCT step.\n");
-                return OPJ_FALSE;
-            }
-        }
-    } else {
-        opj_tcd_resolution_t* res_comp0 = l_tile->comps[0].resolutions +
-                                          p_tcd->image->comps[0].resno_decoded;
-
-        l_samples = (res_comp0->win_x1 - res_comp0->win_x0) *
-                    (res_comp0->win_y1 - res_comp0->win_y0);
-        if (l_tile->numcomps >= 3) {
-            opj_tcd_resolution_t* res_comp1 = l_tile->comps[1].resolutions +
-                                              p_tcd->image->comps[1].resno_decoded;
-            opj_tcd_resolution_t* res_comp2 = l_tile->comps[2].resolutions +
-                                              p_tcd->image->comps[2].resno_decoded;
-            /* testcase 1336.pdf.asan.47.376 */
-            if (p_tcd->image->comps[0].resno_decoded !=
-                    p_tcd->image->comps[1].resno_decoded ||
-                    p_tcd->image->comps[0].resno_decoded !=
-                    p_tcd->image->comps[2].resno_decoded ||
-                    (OPJ_SIZE_T)(res_comp1->win_x1 - res_comp1->win_x0) *
-                    (OPJ_SIZE_T)(res_comp1->win_y1 - res_comp1->win_y0) != l_samples ||
-                    (OPJ_SIZE_T)(res_comp2->win_x1 - res_comp2->win_x0) *
-                    (OPJ_SIZE_T)(res_comp2->win_y1 - res_comp2->win_y0) != l_samples) {
-                opj_event_msg(p_manager, EVT_ERROR,
-                              "Tiles don't all have the same dimension. Skip the MCT step.\n");
-                return OPJ_FALSE;
-            }
-        }
-    }
-
-    if (l_tile->numcomps >= 3) {
-        if (l_tcp->mct == 2) {
-            OPJ_BYTE ** l_data;
-
-            if (! l_tcp->m_mct_decoding_matrix) {
-                return OPJ_TRUE;
-            }
-
-            l_data = (OPJ_BYTE **) opj_malloc(l_tile->numcomps * sizeof(OPJ_BYTE*));
-            if (! l_data) {
-                return OPJ_FALSE;
-            }
-
-            for (i = 0; i < l_tile->numcomps; ++i) {
-                if (p_tcd->whole_tile_decoding) {
-                    l_data[i] = (OPJ_BYTE*) l_tile_comp->data;
-                } else {
-                    l_data[i] = (OPJ_BYTE*) l_tile_comp->data_win;
-                }
-                ++l_tile_comp;
-            }
-
-            if (! opj_mct_decode_custom(/* MCT data */
-                        (OPJ_BYTE*) l_tcp->m_mct_decoding_matrix,
-                        /* size of components */
-                        l_samples,
-                        /* components */
-                        l_data,
-                        /* nb of components (i.e. size of pData) */
-                        l_tile->numcomps,
-                        /* tells if the data is signed */
-                        p_tcd->image->comps->sgnd)) {
-                opj_free(l_data);
-                return OPJ_FALSE;
-            }
-
-            opj_free(l_data);
-        } else {
-            if (l_tcp->tccps->qmfbid == 1) {
-                if (p_tcd->whole_tile_decoding) {
-                    opj_mct_decode(l_tile->comps[0].data,
-                                   l_tile->comps[1].data,
-                                   l_tile->comps[2].data,
-                                   l_samples);
-                } else {
-                    opj_mct_decode(l_tile->comps[0].data_win,
-                                   l_tile->comps[1].data_win,
-                                   l_tile->comps[2].data_win,
-                                   l_samples);
-                }
-            } else {
-                if (p_tcd->whole_tile_decoding) {
-                    opj_mct_decode_real((OPJ_FLOAT32*)l_tile->comps[0].data,
-                                        (OPJ_FLOAT32*)l_tile->comps[1].data,
-                                        (OPJ_FLOAT32*)l_tile->comps[2].data,
-                                        l_samples);
-                } else {
-                    opj_mct_decode_real((OPJ_FLOAT32*)l_tile->comps[0].data_win,
-                                        (OPJ_FLOAT32*)l_tile->comps[1].data_win,
-                                        (OPJ_FLOAT32*)l_tile->comps[2].data_win,
-                                        l_samples);
-                }
-            }
-        }
-    } else {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Number of components (%d) is inconsistent with a MCT. Skip the MCT step.\n",
-                      l_tile->numcomps);
-    }
-
-    return OPJ_TRUE;
-}
-
-
-static OPJ_BOOL opj_tcd_dc_level_shift_decode(opj_tcd_t *p_tcd)
-{
-    OPJ_UINT32 compno;
-    opj_tcd_tilecomp_t * l_tile_comp = 00;
-    opj_tccp_t * l_tccp = 00;
-    opj_image_comp_t * l_img_comp = 00;
-    opj_tcd_resolution_t* l_res = 00;
-    opj_tcd_tile_t * l_tile;
-    OPJ_UINT32 l_width, l_height, i, j;
-    OPJ_INT32 * l_current_ptr;
-    OPJ_INT32 l_min, l_max;
-    OPJ_UINT32 l_stride;
-
-    l_tile = p_tcd->tcd_image->tiles;
-    l_tile_comp = l_tile->comps;
-    l_tccp = p_tcd->tcp->tccps;
-    l_img_comp = p_tcd->image->comps;
-
-    for (compno = 0; compno < l_tile->numcomps;
-            compno++, ++l_img_comp, ++l_tccp, ++l_tile_comp) {
-
-        if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
-            continue;
-        }
-
-        l_res = l_tile_comp->resolutions + l_img_comp->resno_decoded;
-
-        if (!p_tcd->whole_tile_decoding) {
-            l_width = l_res->win_x1 - l_res->win_x0;
-            l_height = l_res->win_y1 - l_res->win_y0;
-            l_stride = 0;
-            l_current_ptr = l_tile_comp->data_win;
-        } else {
-            l_width = (OPJ_UINT32)(l_res->x1 - l_res->x0);
-            l_height = (OPJ_UINT32)(l_res->y1 - l_res->y0);
-            l_stride = (OPJ_UINT32)(
-                           l_tile_comp->resolutions[l_tile_comp->minimum_num_resolutions - 1].x1 -
-                           l_tile_comp->resolutions[l_tile_comp->minimum_num_resolutions - 1].x0)
-                       - l_width;
-            l_current_ptr = l_tile_comp->data;
-
-            assert(l_height == 0 ||
-                   l_width + l_stride <= l_tile_comp->data_size / l_height); /*MUPDF*/
-        }
-
-        if (l_img_comp->sgnd) {
-            l_min = -(1 << (l_img_comp->prec - 1));
-            l_max = (1 << (l_img_comp->prec - 1)) - 1;
-        } else {
-            l_min = 0;
-            l_max = (OPJ_INT32)((1U << l_img_comp->prec) - 1);
-        }
-
-
-        if (l_tccp->qmfbid == 1) {
-            for (j = 0; j < l_height; ++j) {
-                for (i = 0; i < l_width; ++i) {
-                    /* TODO: do addition on int64 ? */
-                    *l_current_ptr = opj_int_clamp(*l_current_ptr + l_tccp->m_dc_level_shift, l_min,
-                                                   l_max);
-                    ++l_current_ptr;
-                }
-                l_current_ptr += l_stride;
-            }
-        } else {
-            for (j = 0; j < l_height; ++j) {
-                for (i = 0; i < l_width; ++i) {
-                    OPJ_FLOAT32 l_value = *((OPJ_FLOAT32 *) l_current_ptr);
-                    if (l_value > INT_MAX) {
-                        *l_current_ptr = l_max;
-                    } else if (l_value < INT_MIN) {
-                        *l_current_ptr = l_min;
-                    } else {
-                        /* Do addition on int64 to avoid overflows */
-                        OPJ_INT64 l_value_int = (OPJ_INT64)opj_lrintf(l_value);
-                        *l_current_ptr = (OPJ_INT32)opj_int64_clamp(
-                                             l_value_int + l_tccp->m_dc_level_shift, l_min, l_max);
-                    }
-                    ++l_current_ptr;
-                }
-                l_current_ptr += l_stride;
-            }
-        }
-    }
-
-    return OPJ_TRUE;
-}
-
-
-
-/**
- * Deallocates the encoding data of the given precinct.
- */
-static void opj_tcd_code_block_dec_deallocate(opj_tcd_precinct_t * p_precinct)
-{
-    OPJ_UINT32 cblkno, l_nb_code_blocks;
-
-    opj_tcd_cblk_dec_t * l_code_block = p_precinct->cblks.dec;
-    if (l_code_block) {
-        /*fprintf(stderr,"deallocate codeblock:{\n");*/
-        /*fprintf(stderr,"\t x0=%d, y0=%d, x1=%d, y1=%d\n",l_code_block->x0, l_code_block->y0, l_code_block->x1, l_code_block->y1);*/
-        /*fprintf(stderr,"\t numbps=%d, numlenbits=%d, len=%d, numnewpasses=%d, real_num_segs=%d, m_current_max_segs=%d\n ",
-                        l_code_block->numbps, l_code_block->numlenbits, l_code_block->len, l_code_block->numnewpasses, l_code_block->real_num_segs, l_code_block->m_current_max_segs );*/
-
-
-        l_nb_code_blocks = p_precinct->block_size / (OPJ_UINT32)sizeof(
-                               opj_tcd_cblk_dec_t);
-        /*fprintf(stderr,"nb_code_blocks =%d\t}\n", l_nb_code_blocks);*/
-
-        for (cblkno = 0; cblkno < l_nb_code_blocks; ++cblkno) {
-
-            if (l_code_block->segs) {
-                opj_free(l_code_block->segs);
-                l_code_block->segs = 00;
-            }
-
-            if (l_code_block->chunks) {
-                opj_free(l_code_block->chunks);
-                l_code_block->chunks = 00;
-            }
-
-            opj_aligned_free(l_code_block->decoded_data);
-            l_code_block->decoded_data = NULL;
-
-            ++l_code_block;
-        }
-
-        opj_free(p_precinct->cblks.dec);
-        p_precinct->cblks.dec = 00;
-    }
-}
-
-/**
- * Deallocates the encoding data of the given precinct.
- */
-static void opj_tcd_code_block_enc_deallocate(opj_tcd_precinct_t * p_precinct)
-{
-    OPJ_UINT32 cblkno, l_nb_code_blocks;
-
-    opj_tcd_cblk_enc_t * l_code_block = p_precinct->cblks.enc;
-    if (l_code_block) {
-        l_nb_code_blocks = p_precinct->block_size / (OPJ_UINT32)sizeof(
-                               opj_tcd_cblk_enc_t);
-
-        for (cblkno = 0; cblkno < l_nb_code_blocks; ++cblkno)  {
-            if (l_code_block->data) {
-                /* We refer to data - 1 since below we incremented it */
-                /* in opj_tcd_code_block_enc_allocate_data() */
-                opj_free(l_code_block->data - 1);
-                l_code_block->data = 00;
-            }
-
-            if (l_code_block->layers) {
-                opj_free(l_code_block->layers);
-                l_code_block->layers = 00;
-            }
-
-            if (l_code_block->passes) {
-                opj_free(l_code_block->passes);
-                l_code_block->passes = 00;
-            }
-            ++l_code_block;
-        }
-
-        opj_free(p_precinct->cblks.enc);
-
-        p_precinct->cblks.enc = 00;
-    }
-}
-
-OPJ_SIZE_T opj_tcd_get_encoded_tile_size(opj_tcd_t *p_tcd)
-{
-    OPJ_UINT32 i;
-    OPJ_SIZE_T l_data_size = 0;
-    opj_image_comp_t * l_img_comp = 00;
-    opj_tcd_tilecomp_t * l_tilec = 00;
-    OPJ_UINT32 l_size_comp, l_remaining;
-
-    l_tilec = p_tcd->tcd_image->tiles->comps;
-    l_img_comp = p_tcd->image->comps;
-    for (i = 0; i < p_tcd->image->numcomps; ++i) {
-        l_size_comp = l_img_comp->prec >> 3; /*(/ 8)*/
-        l_remaining = l_img_comp->prec & 7;  /* (%8) */
-
-        if (l_remaining) {
-            ++l_size_comp;
-        }
-
-        if (l_size_comp == 3) {
-            l_size_comp = 4;
-        }
-
-        l_data_size += l_size_comp * ((OPJ_SIZE_T)(l_tilec->x1 - l_tilec->x0) *
-                                      (OPJ_SIZE_T)(l_tilec->y1 - l_tilec->y0));
-        ++l_img_comp;
-        ++l_tilec;
-    }
-
-    return l_data_size;
-}
-
-static OPJ_BOOL opj_tcd_dc_level_shift_encode(opj_tcd_t *p_tcd)
-{
-    OPJ_UINT32 compno;
-    opj_tcd_tilecomp_t * l_tile_comp = 00;
-    opj_tccp_t * l_tccp = 00;
-    opj_image_comp_t * l_img_comp = 00;
-    opj_tcd_tile_t * l_tile;
-    OPJ_SIZE_T l_nb_elem, i;
-    OPJ_INT32 * l_current_ptr;
-
-    l_tile = p_tcd->tcd_image->tiles;
-    l_tile_comp = l_tile->comps;
-    l_tccp = p_tcd->tcp->tccps;
-    l_img_comp = p_tcd->image->comps;
-
-    for (compno = 0; compno < l_tile->numcomps; compno++) {
-        l_current_ptr = l_tile_comp->data;
-        l_nb_elem = (OPJ_SIZE_T)(l_tile_comp->x1 - l_tile_comp->x0) *
-                    (OPJ_SIZE_T)(l_tile_comp->y1 - l_tile_comp->y0);
-
-        if (l_tccp->qmfbid == 1) {
-            for (i = 0; i < l_nb_elem; ++i) {
-                *l_current_ptr -= l_tccp->m_dc_level_shift ;
-                ++l_current_ptr;
-            }
-        } else {
-            for (i = 0; i < l_nb_elem; ++i) {
-                *l_current_ptr = (*l_current_ptr - l_tccp->m_dc_level_shift) * (1 << 11);
-                ++l_current_ptr;
-            }
-        }
-
-        ++l_img_comp;
-        ++l_tccp;
-        ++l_tile_comp;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_tcd_mct_encode(opj_tcd_t *p_tcd)
-{
-    opj_tcd_tile_t * l_tile = p_tcd->tcd_image->tiles;
-    opj_tcd_tilecomp_t * l_tile_comp = p_tcd->tcd_image->tiles->comps;
-    OPJ_SIZE_T samples = (OPJ_SIZE_T)(l_tile_comp->x1 - l_tile_comp->x0) *
-                         (OPJ_SIZE_T)(l_tile_comp->y1 - l_tile_comp->y0);
-    OPJ_UINT32 i;
-    OPJ_BYTE ** l_data = 00;
-    opj_tcp_t * l_tcp = p_tcd->tcp;
-
-    if (!p_tcd->tcp->mct) {
-        return OPJ_TRUE;
-    }
-
-    if (p_tcd->tcp->mct == 2) {
-        if (! p_tcd->tcp->m_mct_coding_matrix) {
-            return OPJ_TRUE;
-        }
-
-        l_data = (OPJ_BYTE **) opj_malloc(l_tile->numcomps * sizeof(OPJ_BYTE*));
-        if (! l_data) {
-            return OPJ_FALSE;
-        }
-
-        for (i = 0; i < l_tile->numcomps; ++i) {
-            l_data[i] = (OPJ_BYTE*) l_tile_comp->data;
-            ++l_tile_comp;
-        }
-
-        if (! opj_mct_encode_custom(/* MCT data */
-                    (OPJ_BYTE*) p_tcd->tcp->m_mct_coding_matrix,
-                    /* size of components */
-                    samples,
-                    /* components */
-                    l_data,
-                    /* nb of components (i.e. size of pData) */
-                    l_tile->numcomps,
-                    /* tells if the data is signed */
-                    p_tcd->image->comps->sgnd)) {
-            opj_free(l_data);
-            return OPJ_FALSE;
-        }
-
-        opj_free(l_data);
-    } else if (l_tcp->tccps->qmfbid == 0) {
-        opj_mct_encode_real(l_tile->comps[0].data, l_tile->comps[1].data,
-                            l_tile->comps[2].data, samples);
-    } else {
-        opj_mct_encode(l_tile->comps[0].data, l_tile->comps[1].data,
-                       l_tile->comps[2].data, samples);
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_tcd_dwt_encode(opj_tcd_t *p_tcd)
-{
-    opj_tcd_tile_t * l_tile = p_tcd->tcd_image->tiles;
-    opj_tcd_tilecomp_t * l_tile_comp = p_tcd->tcd_image->tiles->comps;
-    opj_tccp_t * l_tccp = p_tcd->tcp->tccps;
-    OPJ_UINT32 compno;
-
-    for (compno = 0; compno < l_tile->numcomps; ++compno) {
-        if (l_tccp->qmfbid == 1) {
-            if (! opj_dwt_encode(l_tile_comp)) {
-                return OPJ_FALSE;
-            }
-        } else if (l_tccp->qmfbid == 0) {
-            if (! opj_dwt_encode_real(l_tile_comp)) {
-                return OPJ_FALSE;
-            }
-        }
-
-        ++l_tile_comp;
-        ++l_tccp;
-    }
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_tcd_t1_encode(opj_tcd_t *p_tcd)
-{
-    opj_t1_t * l_t1;
-    const OPJ_FLOAT64 * l_mct_norms;
-    OPJ_UINT32 l_mct_numcomps = 0U;
-    opj_tcp_t * l_tcp = p_tcd->tcp;
-
-    l_t1 = opj_t1_create(OPJ_TRUE);
-    if (l_t1 == 00) {
-        return OPJ_FALSE;
-    }
-
-    if (l_tcp->mct == 1) {
-        l_mct_numcomps = 3U;
-        /* irreversible encoding */
-        if (l_tcp->tccps->qmfbid == 0) {
-            l_mct_norms = opj_mct_get_mct_norms_real();
-        } else {
-            l_mct_norms = opj_mct_get_mct_norms();
-        }
-    } else {
-        l_mct_numcomps = p_tcd->image->numcomps;
-        l_mct_norms = (const OPJ_FLOAT64 *)(l_tcp->mct_norms);
-    }
-
-    if (! opj_t1_encode_cblks(l_t1, p_tcd->tcd_image->tiles, l_tcp, l_mct_norms,
-                              l_mct_numcomps)) {
-        opj_t1_destroy(l_t1);
-        return OPJ_FALSE;
-    }
-
-    opj_t1_destroy(l_t1);
-
-    return OPJ_TRUE;
-}
-
-static OPJ_BOOL opj_tcd_t2_encode(opj_tcd_t *p_tcd,
-                                  OPJ_BYTE * p_dest_data,
-                                  OPJ_UINT32 * p_data_written,
-                                  OPJ_UINT32 p_max_dest_size,
-                                  opj_codestream_info_t *p_cstr_info,
-                                  opj_event_mgr_t *p_manager)
-{
-    opj_t2_t * l_t2;
-
-    l_t2 = opj_t2_create(p_tcd->image, p_tcd->cp);
-    if (l_t2 == 00) {
-        return OPJ_FALSE;
-    }
-
-    if (! opj_t2_encode_packets(
-                l_t2,
-                p_tcd->tcd_tileno,
-                p_tcd->tcd_image->tiles,
-                p_tcd->tcp->numlayers,
-                p_dest_data,
-                p_data_written,
-                p_max_dest_size,
-                p_cstr_info,
-                p_tcd->tp_num,
-                p_tcd->tp_pos,
-                p_tcd->cur_pino,
-                FINAL_PASS,
-                p_manager)) {
-        opj_t2_destroy(l_t2);
-        return OPJ_FALSE;
-    }
-
-    opj_t2_destroy(l_t2);
-
-    /*---------------CLEAN-------------------*/
-    return OPJ_TRUE;
-}
-
-
-static OPJ_BOOL opj_tcd_rate_allocate_encode(opj_tcd_t *p_tcd,
-        OPJ_BYTE * p_dest_data,
-        OPJ_UINT32 p_max_dest_size,
-        opj_codestream_info_t *p_cstr_info,
-        opj_event_mgr_t *p_manager)
-{
-    opj_cp_t * l_cp = p_tcd->cp;
-    OPJ_UINT32 l_nb_written = 0;
-
-    if (p_cstr_info)  {
-        p_cstr_info->index_write = 0;
-    }
-
-    if (l_cp->m_specific_param.m_enc.m_disto_alloc ||
-            l_cp->m_specific_param.m_enc.m_fixed_quality)  {
-        /* fixed_quality */
-        /* Normal Rate/distortion allocation */
-        if (! opj_tcd_rateallocate(p_tcd, p_dest_data, &l_nb_written, p_max_dest_size,
-                                   p_cstr_info, p_manager)) {
-            return OPJ_FALSE;
-        }
-    } else {
-        /* Fixed layer allocation */
-        opj_tcd_rateallocate_fixed(p_tcd);
-    }
-
-    return OPJ_TRUE;
-}
-
-
-OPJ_BOOL opj_tcd_copy_tile_data(opj_tcd_t *p_tcd,
-                                OPJ_BYTE * p_src,
-                                OPJ_SIZE_T p_src_length)
-{
-    OPJ_UINT32 i;
-    OPJ_SIZE_T j;
-    OPJ_SIZE_T l_data_size = 0;
-    opj_image_comp_t * l_img_comp = 00;
-    opj_tcd_tilecomp_t * l_tilec = 00;
-    OPJ_UINT32 l_size_comp, l_remaining;
-    OPJ_SIZE_T l_nb_elem;
-
-    l_data_size = opj_tcd_get_encoded_tile_size(p_tcd);
-    if (l_data_size != p_src_length) {
-        return OPJ_FALSE;
-    }
-
-    l_tilec = p_tcd->tcd_image->tiles->comps;
-    l_img_comp = p_tcd->image->comps;
-    for (i = 0; i < p_tcd->image->numcomps; ++i) {
-        l_size_comp = l_img_comp->prec >> 3; /*(/ 8)*/
-        l_remaining = l_img_comp->prec & 7;  /* (%8) */
-        l_nb_elem = (OPJ_SIZE_T)(l_tilec->x1 - l_tilec->x0) *
-                    (OPJ_SIZE_T)(l_tilec->y1 - l_tilec->y0);
-
-        if (l_remaining) {
-            ++l_size_comp;
-        }
-
-        if (l_size_comp == 3) {
-            l_size_comp = 4;
-        }
-
-        switch (l_size_comp) {
-        case 1: {
-            OPJ_CHAR * l_src_ptr = (OPJ_CHAR *) p_src;
-            OPJ_INT32 * l_dest_ptr = l_tilec->data;
-
-            if (l_img_comp->sgnd) {
-                for (j = 0; j < l_nb_elem; ++j) {
-                    *(l_dest_ptr++) = (OPJ_INT32)(*(l_src_ptr++));
-                }
-            } else {
-                for (j = 0; j < l_nb_elem; ++j) {
-                    *(l_dest_ptr++) = (*(l_src_ptr++)) & 0xff;
-                }
-            }
-
-            p_src = (OPJ_BYTE*) l_src_ptr;
-        }
-        break;
-        case 2: {
-            OPJ_INT32 * l_dest_ptr = l_tilec->data;
-            OPJ_INT16 * l_src_ptr = (OPJ_INT16 *) p_src;
-
-            if (l_img_comp->sgnd) {
-                for (j = 0; j < l_nb_elem; ++j) {
-                    *(l_dest_ptr++) = (OPJ_INT32)(*(l_src_ptr++));
-                }
-            } else {
-                for (j = 0; j < l_nb_elem; ++j) {
-                    *(l_dest_ptr++) = (*(l_src_ptr++)) & 0xffff;
-                }
-            }
-
-            p_src = (OPJ_BYTE*) l_src_ptr;
-        }
-        break;
-        case 4: {
-            OPJ_INT32 * l_src_ptr = (OPJ_INT32 *) p_src;
-            OPJ_INT32 * l_dest_ptr = l_tilec->data;
-
-            for (j = 0; j < l_nb_elem; ++j) {
-                *(l_dest_ptr++) = (OPJ_INT32)(*(l_src_ptr++));
-            }
-
-            p_src = (OPJ_BYTE*) l_src_ptr;
-        }
-        break;
-        }
-
-        ++l_img_comp;
-        ++l_tilec;
-    }
-
-    return OPJ_TRUE;
-}
-
-OPJ_BOOL opj_tcd_is_band_empty(opj_tcd_band_t* band)
-{
-    return (band->x1 - band->x0 == 0) || (band->y1 - band->y0 == 0);
-}
-
-OPJ_BOOL opj_tcd_is_subband_area_of_interest(opj_tcd_t *tcd,
-        OPJ_UINT32 compno,
-        OPJ_UINT32 resno,
-        OPJ_UINT32 bandno,
-        OPJ_UINT32 band_x0,
-        OPJ_UINT32 band_y0,
-        OPJ_UINT32 band_x1,
-        OPJ_UINT32 band_y1)
-{
-    /* Note: those values for filter_margin are in part the result of */
-    /* experimentation. The value 2 for QMFBID=1 (5x3 filter) can be linked */
-    /* to the maximum left/right extension given in tables F.2 and F.3 of the */
-    /* standard. The value 3 for QMFBID=0 (9x7 filter) is more suspicious, */
-    /* since F.2 and F.3 would lead to 4 instead, so the current 3 might be */
-    /* needed to be bumped to 4, in case inconsistencies are found while */
-    /* decoding parts of irreversible coded images. */
-    /* See opj_dwt_decode_partial_53 and opj_dwt_decode_partial_97 as well */
-    OPJ_UINT32 filter_margin = (tcd->tcp->tccps[compno].qmfbid == 1) ? 2 : 3;
-    opj_tcd_tilecomp_t *tilec = &(tcd->tcd_image->tiles->comps[compno]);
-    opj_image_comp_t* image_comp = &(tcd->image->comps[compno]);
-    /* Compute the intersection of the area of interest, expressed in tile coordinates */
-    /* with the tile coordinates */
-    OPJ_UINT32 tcx0 = opj_uint_max(
-                          (OPJ_UINT32)tilec->x0,
-                          opj_uint_ceildiv(tcd->win_x0, image_comp->dx));
-    OPJ_UINT32 tcy0 = opj_uint_max(
-                          (OPJ_UINT32)tilec->y0,
-                          opj_uint_ceildiv(tcd->win_y0, image_comp->dy));
-    OPJ_UINT32 tcx1 = opj_uint_min(
-                          (OPJ_UINT32)tilec->x1,
-                          opj_uint_ceildiv(tcd->win_x1, image_comp->dx));
-    OPJ_UINT32 tcy1 = opj_uint_min(
-                          (OPJ_UINT32)tilec->y1,
-                          opj_uint_ceildiv(tcd->win_y1, image_comp->dy));
-    /* Compute number of decomposition for this band. See table F-1 */
-    OPJ_UINT32 nb = (resno == 0) ?
-                    tilec->numresolutions - 1 :
-                    tilec->numresolutions - resno;
-    /* Map above tile-based coordinates to sub-band-based coordinates per */
-    /* equation B-15 of the standard */
-    OPJ_UINT32 x0b = bandno & 1;
-    OPJ_UINT32 y0b = bandno >> 1;
-    OPJ_UINT32 tbx0 = (nb == 0) ? tcx0 :
-                      (tcx0 <= (1U << (nb - 1)) * x0b) ? 0 :
-                      opj_uint_ceildivpow2(tcx0 - (1U << (nb - 1)) * x0b, nb);
-    OPJ_UINT32 tby0 = (nb == 0) ? tcy0 :
-                      (tcy0 <= (1U << (nb - 1)) * y0b) ? 0 :
-                      opj_uint_ceildivpow2(tcy0 - (1U << (nb - 1)) * y0b, nb);
-    OPJ_UINT32 tbx1 = (nb == 0) ? tcx1 :
-                      (tcx1 <= (1U << (nb - 1)) * x0b) ? 0 :
-                      opj_uint_ceildivpow2(tcx1 - (1U << (nb - 1)) * x0b, nb);
-    OPJ_UINT32 tby1 = (nb == 0) ? tcy1 :
-                      (tcy1 <= (1U << (nb - 1)) * y0b) ? 0 :
-                      opj_uint_ceildivpow2(tcy1 - (1U << (nb - 1)) * y0b, nb);
-    OPJ_BOOL intersects;
-
-    if (tbx0 < filter_margin) {
-        tbx0 = 0;
-    } else {
-        tbx0 -= filter_margin;
-    }
-    if (tby0 < filter_margin) {
-        tby0 = 0;
-    } else {
-        tby0 -= filter_margin;
-    }
-    tbx1 = opj_uint_adds(tbx1, filter_margin);
-    tby1 = opj_uint_adds(tby1, filter_margin);
-
-    intersects = band_x0 < tbx1 && band_y0 < tby1 && band_x1 > tbx0 &&
-                 band_y1 > tby0;
-
-#ifdef DEBUG_VERBOSE
-    printf("compno=%u resno=%u nb=%u bandno=%u x0b=%u y0b=%u band=%u,%u,%u,%u tb=%u,%u,%u,%u -> %u\n",
-           compno, resno, nb, bandno, x0b, y0b,
-           band_x0, band_y0, band_x1, band_y1,
-           tbx0, tby0, tbx1, tby1, intersects);
-#endif
-    return intersects;
-}
-
-/** Returns whether a tile componenent is fully decoded, taking into account
- * p_tcd->win_* members.
- *
- * @param p_tcd    TCD handle.
- * @param compno Component number
- * @return OPJ_TRUE whether the tile componenent is fully decoded
- */
-static OPJ_BOOL opj_tcd_is_whole_tilecomp_decoding(opj_tcd_t *p_tcd,
-        OPJ_UINT32 compno)
-{
-    opj_tcd_tilecomp_t* tilec = &(p_tcd->tcd_image->tiles->comps[compno]);
-    opj_image_comp_t* image_comp = &(p_tcd->image->comps[compno]);
-    /* Compute the intersection of the area of interest, expressed in tile coordinates */
-    /* with the tile coordinates */
-    OPJ_UINT32 tcx0 = opj_uint_max(
-                          (OPJ_UINT32)tilec->x0,
-                          opj_uint_ceildiv(p_tcd->win_x0, image_comp->dx));
-    OPJ_UINT32 tcy0 = opj_uint_max(
-                          (OPJ_UINT32)tilec->y0,
-                          opj_uint_ceildiv(p_tcd->win_y0, image_comp->dy));
-    OPJ_UINT32 tcx1 = opj_uint_min(
-                          (OPJ_UINT32)tilec->x1,
-                          opj_uint_ceildiv(p_tcd->win_x1, image_comp->dx));
-    OPJ_UINT32 tcy1 = opj_uint_min(
-                          (OPJ_UINT32)tilec->y1,
-                          opj_uint_ceildiv(p_tcd->win_y1, image_comp->dy));
-
-    OPJ_UINT32 shift = tilec->numresolutions - tilec->minimum_num_resolutions;
-    /* Tolerate small margin within the reduced resolution factor to consider if */
-    /* the whole tile path must be taken */
-    return (tcx0 >= (OPJ_UINT32)tilec->x0 &&
-            tcy0 >= (OPJ_UINT32)tilec->y0 &&
-            tcx1 <= (OPJ_UINT32)tilec->x1 &&
-            tcy1 <= (OPJ_UINT32)tilec->y1 &&
-            (shift >= 32 ||
-             (((tcx0 - (OPJ_UINT32)tilec->x0) >> shift) == 0 &&
-              ((tcy0 - (OPJ_UINT32)tilec->y0) >> shift) == 0 &&
-              (((OPJ_UINT32)tilec->x1 - tcx1) >> shift) == 0 &&
-              (((OPJ_UINT32)tilec->y1 - tcy1) >> shift) == 0)));
-}
diff --git a/third_party/libopenjpeg20/tcd.h b/third_party/libopenjpeg20/tcd.h
deleted file mode 100644
index e3214c1..0000000
--- a/third_party/libopenjpeg20/tcd.h
+++ /dev/null
@@ -1,486 +0,0 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
- * Copyright (c) 2012, CS Systemes d'Information, France
- * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-#ifndef OPJ_TCD_H
-#define OPJ_TCD_H
-/**
-@file tcd.h
-@brief Implementation of a tile coder/decoder (TCD)
-
-The functions in TCD.C encode or decode each tile independently from
-each other. The functions in TCD.C are used by other functions in J2K.C.
-*/
-
-/** @defgroup TCD TCD - Implementation of a tile coder/decoder */
-/*@{*/
-
-
-/**
-FIXME DOC
-*/
-typedef struct opj_tcd_pass {
-    OPJ_UINT32 rate;
-    OPJ_FLOAT64 distortiondec;
-    OPJ_UINT32 len;
-    OPJ_BITFIELD term : 1;
-} opj_tcd_pass_t;
-
-/**
-FIXME DOC
-*/
-typedef struct opj_tcd_layer {
-    OPJ_UINT32 numpasses;       /* Number of passes in the layer */
-    OPJ_UINT32 len;             /* len of information */
-    OPJ_FLOAT64 disto;          /* add for index (Cfr. Marcela) */
-    OPJ_BYTE *data;             /* data */
-} opj_tcd_layer_t;
-
-/**
-FIXME DOC
-*/
-typedef struct opj_tcd_cblk_enc {
-    OPJ_BYTE* data;               /* Data */
-    opj_tcd_layer_t* layers;      /* layer information */
-    opj_tcd_pass_t* passes;       /* information about the passes */
-    OPJ_INT32 x0, y0, x1,
-              y1;     /* dimension of the code-blocks : left upper corner (x0, y0) right low corner (x1,y1) */
-    OPJ_UINT32 numbps;
-    OPJ_UINT32 numlenbits;
-    OPJ_UINT32 data_size;         /* Size of allocated data buffer */
-    OPJ_UINT32
-    numpasses;         /* number of pass already done for the code-blocks */
-    OPJ_UINT32 numpassesinlayers; /* number of passes in the layer */
-    OPJ_UINT32 totalpasses;       /* total number of passes */
-} opj_tcd_cblk_enc_t;
-
-
-/** Chunk of codestream data that is part of a code block */
-typedef struct opj_tcd_seg_data_chunk {
-    /* Point to tilepart buffer. We don't make a copy !
-       So the tilepart buffer must be kept alive
-       as long as we need to decode the codeblocks */
-    OPJ_BYTE * data;
-    OPJ_UINT32 len;                 /* Usable length of data */
-} opj_tcd_seg_data_chunk_t;
-
-/** Segment of a code-block.
- * A segment represent a number of consecutive coding passes, without termination
- * of MQC or RAW between them. */
-typedef struct opj_tcd_seg {
-    OPJ_UINT32 len;      /* Size of data related to this segment */
-    /* Number of passes decoded. Including those that we skip */
-    OPJ_UINT32 numpasses;
-    /* Number of passes actually to be decoded. To be used for code-block decoding */
-    OPJ_UINT32 real_num_passes;
-    /* Maximum number of passes for this segment */
-    OPJ_UINT32 maxpasses;
-    /* Number of new passes for current packed. Transitory value */
-    OPJ_UINT32 numnewpasses;
-    /* Codestream length for this segment for current packed. Transitory value */
-    OPJ_UINT32 newlen;
-} opj_tcd_seg_t;
-
-/** Code-block for decoding */
-typedef struct opj_tcd_cblk_dec {
-    opj_tcd_seg_t* segs;            /* segments information */
-    opj_tcd_seg_data_chunk_t* chunks; /* Array of chunks */
-    /* position of the code-blocks : left upper corner (x0, y0) right low corner (x1,y1) */
-    OPJ_INT32 x0, y0, x1, y1;
-    OPJ_UINT32 numbps;
-    /* number of bits for len, for the current packet. Transitory value */
-    OPJ_UINT32 numlenbits;
-    /* number of pass added to the code-blocks, for the current packet. Transitory value */
-    OPJ_UINT32 numnewpasses;
-    /* number of segments, including those of packet we skip */
-    OPJ_UINT32 numsegs;
-    /* number of segments, to be used for code block decoding */
-    OPJ_UINT32 real_num_segs;
-    OPJ_UINT32 m_current_max_segs;  /* allocated number of segs[] items */
-    OPJ_UINT32 numchunks;           /* Number of valid chunks items */
-    OPJ_UINT32 numchunksalloc;      /* Number of chunks item allocated */
-    /* Decoded code-block. Only used for subtile decoding. Otherwise tilec->data is directly updated */
-    OPJ_INT32* decoded_data;
-} opj_tcd_cblk_dec_t;
-
-/** Precinct structure */
-typedef struct opj_tcd_precinct {
-    /* dimension of the precinct : left upper corner (x0, y0) right low corner (x1,y1) */
-    OPJ_INT32 x0, y0, x1, y1;
-    OPJ_UINT32 cw, ch;              /* number of code-blocks, in width and height */
-    union {                         /* code-blocks information */
-        opj_tcd_cblk_enc_t* enc;
-        opj_tcd_cblk_dec_t* dec;
-        void*               blocks;
-    } cblks;
-    OPJ_UINT32 block_size;          /* size taken by cblks (in bytes) */
-    opj_tgt_tree_t *incltree;       /* inclusion tree */
-    opj_tgt_tree_t *imsbtree;       /* IMSB tree */
-} opj_tcd_precinct_t;
-
-/** Sub-band structure */
-typedef struct opj_tcd_band {
-    /* dimension of the subband : left upper corner (x0, y0) right low corner (x1,y1) */
-    OPJ_INT32 x0, y0, x1, y1;
-    /* band number: for lowest resolution level (0=LL), otherwise (1=HL, 2=LH, 3=HH) */
-    OPJ_UINT32 bandno;
-    /* precinct information */
-    opj_tcd_precinct_t *precincts;
-    /* size of data taken by precincts */
-    OPJ_UINT32 precincts_data_size;
-    OPJ_INT32 numbps;
-    OPJ_FLOAT32 stepsize;
-} opj_tcd_band_t;
-
-/** Tile-component resolution structure */
-typedef struct opj_tcd_resolution {
-    /* dimension of the resolution level : left upper corner (x0, y0) right low corner (x1,y1) */
-    OPJ_INT32 x0, y0, x1, y1;
-    /* number of precincts, in width and height, for this resolution level */
-    OPJ_UINT32 pw, ph;
-    /* number of sub-bands for the resolution level (1 for lowest resolution level, 3 otherwise) */
-    OPJ_UINT32 numbands;
-    /* subband information */
-    opj_tcd_band_t bands[3];
-
-    /* dimension of the resolution limited to window of interest. Only valid if tcd->whole_tile_decoding is set */
-    OPJ_UINT32 win_x0;
-    OPJ_UINT32 win_y0;
-    OPJ_UINT32 win_x1;
-    OPJ_UINT32 win_y1;
-} opj_tcd_resolution_t;
-
-/** Tile-component structure */
-typedef struct opj_tcd_tilecomp {
-    /* dimension of component : left upper corner (x0, y0) right low corner (x1,y1) */
-    OPJ_INT32 x0, y0, x1, y1;
-    /* component number */
-    OPJ_UINT32 compno;
-    /* number of resolutions level */
-    OPJ_UINT32 numresolutions;
-    /* number of resolutions level to decode (at max)*/
-    OPJ_UINT32 minimum_num_resolutions;
-    /* resolutions information */
-    opj_tcd_resolution_t *resolutions;
-    /* size of data for resolutions (in bytes) */
-    OPJ_UINT32 resolutions_size;
-
-    /* data of the component. For decoding, only valid if tcd->whole_tile_decoding is set (so exclusive of data_win member) */
-    OPJ_INT32 *data;
-    /* if true, then need to free after usage, otherwise do not free */
-    OPJ_BOOL  ownsData;
-    /* we may either need to allocate this amount of data, or re-use image data and ignore this value */
-    size_t data_size_needed;
-    /* size of the data of the component */
-    size_t data_size;
-
-    /** data of the component limited to window of interest. Only valid for decoding and if tcd->whole_tile_decoding is NOT set (so exclusive of data member) */
-    OPJ_INT32 *data_win;
-    /* dimension of the component limited to window of interest. Only valid for decoding and  if tcd->whole_tile_decoding is NOT set */
-    OPJ_UINT32 win_x0;
-    OPJ_UINT32 win_y0;
-    OPJ_UINT32 win_x1;
-    OPJ_UINT32 win_y1;
-
-    /* add fixed_quality */
-    OPJ_INT32 numpix;
-} opj_tcd_tilecomp_t;
-
-
-/**
-FIXME DOC
-*/
-typedef struct opj_tcd_tile {
-    /* dimension of the tile : left upper corner (x0, y0) right low corner (x1,y1) */
-    OPJ_INT32 x0, y0, x1, y1;
-    OPJ_UINT32 numcomps;            /* number of components in tile */
-    opj_tcd_tilecomp_t *comps;  /* Components information */
-    OPJ_INT32 numpix;               /* add fixed_quality */
-    OPJ_FLOAT64 distotile;          /* add fixed_quality */
-    OPJ_FLOAT64 distolayer[100];    /* add fixed_quality */
-    OPJ_UINT32 packno;              /* packet number */
-} opj_tcd_tile_t;
-
-/**
-FIXME DOC
-*/
-typedef struct opj_tcd_image {
-    opj_tcd_tile_t *tiles;      /* Tiles information */
-}
-opj_tcd_image_t;
-
-
-/**
-Tile coder/decoder
-*/
-typedef struct opj_tcd {
-    /** Position of the tilepart flag in Progression order*/
-    OPJ_INT32 tp_pos;
-    /** Tile part number*/
-    OPJ_UINT32 tp_num;
-    /** Current tile part number*/
-    OPJ_UINT32 cur_tp_num;
-    /** Total number of tileparts of the current tile*/
-    OPJ_UINT32 cur_totnum_tp;
-    /** Current Packet iterator number */
-    OPJ_UINT32 cur_pino;
-    /** info on each image tile */
-    opj_tcd_image_t *tcd_image;
-    /** image header */
-    opj_image_t *image;
-    /** coding parameters */
-    opj_cp_t *cp;
-    /** coding/decoding parameters common to all tiles */
-    opj_tcp_t *tcp;
-    /** current encoded/decoded tile */
-    OPJ_UINT32 tcd_tileno;
-    /** tell if the tcd is a decoder. */
-    OPJ_BITFIELD m_is_decoder : 1;
-    /** Thread pool */
-    opj_thread_pool_t* thread_pool;
-    /** Coordinates of the window of interest, in grid reference space */
-    OPJ_UINT32 win_x0;
-    OPJ_UINT32 win_y0;
-    OPJ_UINT32 win_x1;
-    OPJ_UINT32 win_y1;
-    /** Only valid for decoding. Whether the whole tile is decoded, or just the region in win_x0/win_y0/win_x1/win_y1 */
-    OPJ_BOOL   whole_tile_decoding;
-    /* Array of size image->numcomps indicating if a component must be decoded. NULL if all components must be decoded */
-    OPJ_BOOL* used_component;
-} opj_tcd_t;
-
-/** @name Exported functions */
-/*@{*/
-/* ----------------------------------------------------------------------- */
-
-/**
-Dump the content of a tcd structure
-*/
-/*void tcd_dump(FILE *fd, opj_tcd_t *tcd, opj_tcd_image_t *img);*/ /* TODO MSD shoul use the new v2 structures */
-
-/**
-Create a new TCD handle
-@param p_is_decoder FIXME DOC
-@return Returns a new TCD handle if successful returns NULL otherwise
-*/
-opj_tcd_t* opj_tcd_create(OPJ_BOOL p_is_decoder);
-
-/**
-Destroy a previously created TCD handle
-@param tcd TCD handle to destroy
-*/
-void opj_tcd_destroy(opj_tcd_t *tcd);
-
-/**
- * Initialize the tile coder and may reuse some memory.
- * @param   p_tcd       TCD handle.
- * @param   p_image     raw image.
- * @param   p_cp        coding parameters.
- * @param   p_tp        thread pool
- *
- * @return true if the encoding values could be set (false otherwise).
-*/
-OPJ_BOOL opj_tcd_init(opj_tcd_t *p_tcd,
-                      opj_image_t * p_image,
-                      opj_cp_t * p_cp,
-                      opj_thread_pool_t* p_tp);
-
-/**
- * Allocates memory for decoding a specific tile.
- *
- * @param   p_tcd       the tile decoder.
- * @param   p_tile_no   the index of the tile received in sequence. This not necessarily lead to the
- * tile at index p_tile_no.
- * @param p_manager the event manager.
- *
- * @return  true if the remaining data is sufficient.
- */
-OPJ_BOOL opj_tcd_init_decode_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
-                                  opj_event_mgr_t* p_manager);
-
-void opj_tcd_makelayer_fixed(opj_tcd_t *tcd, OPJ_UINT32 layno,
-                             OPJ_UINT32 final);
-
-void opj_tcd_rateallocate_fixed(opj_tcd_t *tcd);
-
-void opj_tcd_makelayer(opj_tcd_t *tcd,
-                       OPJ_UINT32 layno,
-                       OPJ_FLOAT64 thresh,
-                       OPJ_UINT32 final);
-
-OPJ_BOOL opj_tcd_rateallocate(opj_tcd_t *tcd,
-                              OPJ_BYTE *dest,
-                              OPJ_UINT32 * p_data_written,
-                              OPJ_UINT32 len,
-                              opj_codestream_info_t *cstr_info,
-                              opj_event_mgr_t *p_manager);
-
-/**
- * Gets the maximum tile size that will be taken by the tile once decoded.
- */
-OPJ_UINT32 opj_tcd_get_decoded_tile_size(opj_tcd_t *p_tcd,
-        OPJ_BOOL take_into_account_partial_decoding);
-
-/**
- * Encodes a tile from the raw image into the given buffer.
- * @param   p_tcd           Tile Coder handle
- * @param   p_tile_no       Index of the tile to encode.
- * @param   p_dest          Destination buffer
- * @param   p_data_written  pointer to an int that is incremented by the number of bytes really written on p_dest
- * @param   p_len           Maximum length of the destination buffer
- * @param   p_cstr_info     Codestream information structure
- * @param   p_manager       the user event manager
- * @return  true if the coding is successful.
-*/
-OPJ_BOOL opj_tcd_encode_tile(opj_tcd_t *p_tcd,
-                             OPJ_UINT32 p_tile_no,
-                             OPJ_BYTE *p_dest,
-                             OPJ_UINT32 * p_data_written,
-                             OPJ_UINT32 p_len,
-                             struct opj_codestream_info *p_cstr_info,
-                             opj_event_mgr_t *p_manager);
-
-
-/**
-Decode a tile from a buffer into a raw image
-@param tcd TCD handle
-@param win_x0 Upper left x of region to decode (in grid coordinates)
-@param win_y0 Upper left y of region to decode (in grid coordinates)
-@param win_x1 Lower right x of region to decode (in grid coordinates)
-@param win_y1 Lower right y of region to decode (in grid coordinates)
-@param numcomps_to_decode  Size of the comps_indices array, or 0 if decoding all components.
-@param comps_indices   Array of numcomps values representing the indices
-                       of the components to decode (relative to the
-                       codestream, starting at 0). Or NULL if decoding all components.
-@param src Source buffer
-@param len Length of source buffer
-@param tileno Number that identifies one of the tiles to be decoded
-@param cstr_info  FIXME DOC
-@param manager the event manager.
-*/
-OPJ_BOOL opj_tcd_decode_tile(opj_tcd_t *tcd,
-                             OPJ_UINT32 win_x0,
-                             OPJ_UINT32 win_y0,
-                             OPJ_UINT32 win_x1,
-                             OPJ_UINT32 win_y1,
-                             OPJ_UINT32 numcomps_to_decode,
-                             const OPJ_UINT32 *comps_indices,
-                             OPJ_BYTE *src,
-                             OPJ_UINT32 len,
-                             OPJ_UINT32 tileno,
-                             opj_codestream_index_t *cstr_info,
-                             opj_event_mgr_t *manager);
-
-
-/**
- * Copies tile data from the system onto the given memory block.
- */
-OPJ_BOOL opj_tcd_update_tile_data(opj_tcd_t *p_tcd,
-                                  OPJ_BYTE * p_dest,
-                                  OPJ_UINT32 p_dest_length);
-
-/**
- *
- */
-OPJ_SIZE_T opj_tcd_get_encoded_tile_size(opj_tcd_t *p_tcd);
-
-/**
- * Initialize the tile coder and may reuse some meory.
- *
- * @param   p_tcd       TCD handle.
- * @param   p_tile_no   current tile index to encode.
- * @param p_manager the event manager.
- *
- * @return true if the encoding values could be set (false otherwise).
-*/
-OPJ_BOOL opj_tcd_init_encode_tile(opj_tcd_t *p_tcd,
-                                  OPJ_UINT32 p_tile_no, opj_event_mgr_t* p_manager);
-
-/**
- * Copies tile data from the given memory block onto the system.
- */
-OPJ_BOOL opj_tcd_copy_tile_data(opj_tcd_t *p_tcd,
-                                OPJ_BYTE * p_src,
-                                OPJ_SIZE_T p_src_length);
-
-/**
- * Allocates tile component data
- *
- *
- */
-OPJ_BOOL opj_alloc_tile_component_data(opj_tcd_tilecomp_t *l_tilec);
-
-/** Returns whether a sub-band is empty (i.e. whether it has a null area)
- * @param band Sub-band handle.
- * @return OPJ_TRUE whether the sub-band is empty.
- */
-OPJ_BOOL opj_tcd_is_band_empty(opj_tcd_band_t* band);
-
-/** Reinitialize a segment */
-void opj_tcd_reinit_segment(opj_tcd_seg_t* seg);
-
-
-/** Returns whether a sub-band region contributes to the area of interest
- * tcd->win_x0,tcd->win_y0,tcd->win_x1,tcd->win_y1.
- *
- * @param tcd    TCD handle.
- * @param compno Component number
- * @param resno  Resolution number
- * @param bandno Band number (*not* band index, ie 0, 1, 2 or 3)
- * @param x0     Upper left x in subband coordinates
- * @param y0     Upper left y in subband coordinates
- * @param x1     Lower right x in subband coordinates
- * @param y1     Lower right y in subband coordinates
- * @return OPJ_TRUE whether the sub-band region contributs to the area of
- *                  interest.
- */
-OPJ_BOOL opj_tcd_is_subband_area_of_interest(opj_tcd_t *tcd,
-        OPJ_UINT32 compno,
-        OPJ_UINT32 resno,
-        OPJ_UINT32 bandno,
-        OPJ_UINT32 x0,
-        OPJ_UINT32 y0,
-        OPJ_UINT32 x1,
-        OPJ_UINT32 y1);
-
-/* ----------------------------------------------------------------------- */
-/*@}*/
-
-/*@}*/
-
-#endif /* OPJ_TCD_H */
diff --git a/third_party/libpng16/0000-build-config.patch b/third_party/libpng16/0000-build-config.patch
deleted file mode 100644
index 325b58f..0000000
--- a/third_party/libpng16/0000-build-config.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-diff a/third_party/libpng16/pngmem.c b/third_party/libpng16/pngmem.c
---- a/third_party/libpng16/pngmem.c
-+++ b/third_party/libpng16/pngmem.c
-@@ -19,6 +19,9 @@
- 
- #include "pngpriv.h"
- 
-+void*	FXMEM_DefaultAlloc(size_t byte_size);
-+void	FXMEM_DefaultFree(void* pointer);
-+
- #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
- /* Free a png_struct */
- void /* PRIVATE */
-@@ -92,7 +95,7 @@
- 
-       else
- #endif
--         return malloc((size_t)size); /* checked for truncation above */
-+         return FXMEM_DefaultAlloc(size);
-    }
- 
-    else
-@@ -249,7 +252,7 @@
-       return;
- #endif /* USER_MEM */
- 
--   free(ptr);
-+   FXMEM_DefaultFree(ptr);
- }
- 
- #ifdef PNG_USER_MEM_SUPPORTED
diff --git a/third_party/libpng16/0002-static-png-gt.patch b/third_party/libpng16/0002-static-png-gt.patch
deleted file mode 100644
index b2b1d94..0000000
--- a/third_party/libpng16/0002-static-png-gt.patch
+++ /dev/null
@@ -1,14 +0,0 @@
-diff --git a/third_party/libpng16/png.c b/third_party/libpng16/png.c
-index c183e3f..1cf5271 100644
---- a/third_party/libpng16/png.c
-+++ b/third_party/libpng16/png.c
-@@ -2464,7 +2464,8 @@ png_colorspace_set_rgb_coefficients(png_structrp png_ptr)
- 
- #endif /* COLORSPACE */
- 
--#ifdef __GNUC__
-+/* #ifdef __GNUC__ */
-+#if 1
- /* This exists solely to work round a warning from GNU C. */
- static int /* PRIVATE */
- png_gt(size_t a, size_t b)
diff --git a/third_party/libpng16/LICENSE b/third_party/libpng16/LICENSE
deleted file mode 100644
index e0c5b53..0000000
--- a/third_party/libpng16/LICENSE
+++ /dev/null
@@ -1,134 +0,0 @@
-COPYRIGHT NOTICE, DISCLAIMER, and LICENSE
-=========================================
-
-PNG Reference Library License version 2
----------------------------------------
-
- * Copyright (c) 1995-2019 The PNG Reference Library Authors.
- * Copyright (c) 2018-2019 Cosmin Truta.
- * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
- * Copyright (c) 1996-1997 Andreas Dilger.
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
-
-The software is supplied "as is", without warranty of any kind,
-express or implied, including, without limitation, the warranties
-of merchantability, fitness for a particular purpose, title, and
-non-infringement.  In no event shall the Copyright owners, or
-anyone distributing the software, be liable for any damages or
-other liability, whether in contract, tort or otherwise, arising
-from, out of, or in connection with the software, or the use or
-other dealings in the software, even if advised of the possibility
-of such damage.
-
-Permission is hereby granted to use, copy, modify, and distribute
-this software, or portions hereof, for any purpose, without fee,
-subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you
-    must not claim that you wrote the original software.  If you
-    use this software in a product, an acknowledgment in the product
-    documentation would be appreciated, but is not required.
-
- 2. Altered source versions must be plainly marked as such, and must
-    not be misrepresented as being the original software.
-
- 3. This Copyright notice may not be removed or altered from any
-    source or altered source distribution.
-
-
-PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35)
------------------------------------------------------------------------
-
-libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are
-Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are
-derived from libpng-1.0.6, and are distributed according to the same
-disclaimer and license as libpng-1.0.6 with the following individuals
-added to the list of Contributing Authors:
-
-    Simon-Pierre Cadieux
-    Eric S. Raymond
-    Mans Rullgard
-    Cosmin Truta
-    Gilles Vollant
-    James Yu
-    Mandar Sahastrabuddhe
-    Google Inc.
-    Vadim Barkov
-
-and with the following additions to the disclaimer:
-
-    There is no warranty against interference with your enjoyment of
-    the library or against infringement.  There is no warranty that our
-    efforts or the library will fulfill any of your particular purposes
-    or needs.  This library is provided with all faults, and the entire
-    risk of satisfactory quality, performance, accuracy, and effort is
-    with the user.
-
-Some files in the "contrib" directory and some configure-generated
-files that are distributed with libpng have other copyright owners, and
-are released under other open source licenses.
-
-libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
-Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from
-libpng-0.96, and are distributed according to the same disclaimer and
-license as libpng-0.96, with the following individuals added to the
-list of Contributing Authors:
-
-    Tom Lane
-    Glenn Randers-Pehrson
-    Willem van Schaik
-
-libpng versions 0.89, June 1996, through 0.96, May 1997, are
-Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88,
-and are distributed according to the same disclaimer and license as
-libpng-0.88, with the following individuals added to the list of
-Contributing Authors:
-
-    John Bowler
-    Kevin Bracey
-    Sam Bushell
-    Magnus Holmgren
-    Greg Roelofs
-    Tom Tanner
-
-Some files in the "scripts" directory have other copyright owners,
-but are released under this license.
-
-libpng versions 0.5, May 1995, through 0.88, January 1996, are
-Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
-
-For the purposes of this copyright and license, "Contributing Authors"
-is defined as the following set of individuals:
-
-    Andreas Dilger
-    Dave Martindale
-    Guy Eric Schalnat
-    Paul Schmidt
-    Tim Wegner
-
-The PNG Reference Library is supplied "AS IS".  The Contributing
-Authors and Group 42, Inc. disclaim all warranties, expressed or
-implied, including, without limitation, the warranties of
-merchantability and of fitness for any purpose.  The Contributing
-Authors and Group 42, Inc. assume no liability for direct, indirect,
-incidental, special, exemplary, or consequential damages, which may
-result from the use of the PNG Reference Library, even if advised of
-the possibility of such damage.
-
-Permission is hereby granted to use, copy, modify, and distribute this
-source code, or portions hereof, for any purpose, without fee, subject
-to the following restrictions:
-
- 1. The origin of this source code must not be misrepresented.
-
- 2. Altered versions must be plainly marked as such and must not
-    be misrepresented as being the original source.
-
- 3. This Copyright notice may not be removed or altered from any
-    source or altered source distribution.
-
-The Contributing Authors and Group 42, Inc. specifically permit,
-without fee, and encourage the use of this source code as a component
-to supporting the PNG file format in commercial products.  If you use
-this source code in a product, acknowledgment is not required but would
-be appreciated.
diff --git a/third_party/libpng16/README.pdfium b/third_party/libpng16/README.pdfium
deleted file mode 100644
index 0d9c30c..0000000
--- a/third_party/libpng16/README.pdfium
+++ /dev/null
@@ -1,19 +0,0 @@
-Name: libpng
-URL: http://libpng.org/
-Version: 1.6.37
-Security Critical: yes
-License: libpng license
-License Android Compatible: yes
-
-Description:
-PNG library.
-
-Local Modifications:
-
-Local changes in Chromium's copy of libpng as of
-e87a02987101e2dbe319a4aba6b52470f7624b4a - see README.chromium.
-
-pnglibconf.h: a copy of libpng's scripts/pnglibconf.h.prebuilt.
-pngprefix.h: manually-created redefinitions to avoid conflicts with Chromium.
-0000-build-config.patch: Local build configuration changes.
-0002-static-png-gt.patch: Unconditionally use static png_gt() in png.c to avoid compilation warning.
diff --git a/third_party/libpng16/arm/arm_init.c b/third_party/libpng16/arm/arm_init.c
deleted file mode 100644
index a34ecdb..0000000
--- a/third_party/libpng16/arm/arm_init.c
+++ /dev/null
@@ -1,136 +0,0 @@
-
-/* arm_init.c - NEON optimised filter functions
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 2014,2016 Glenn Randers-Pehrson
- * Written by Mans Rullgard, 2011.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
-/* Below, after checking __linux__, various non-C90 POSIX 1003.1 functions are
- * called.
- */
-#define _POSIX_SOURCE 1
-
-#include "../pngpriv.h"
-
-#ifdef PNG_READ_SUPPORTED
-
-#if PNG_ARM_NEON_OPT > 0
-#ifdef PNG_ARM_NEON_CHECK_SUPPORTED /* Do run-time checks */
-/* WARNING: it is strongly recommended that you do not build libpng with
- * run-time checks for CPU features if at all possible.  In the case of the ARM
- * NEON instructions there is no processor-specific way of detecting the
- * presence of the required support, therefore run-time detection is extremely
- * OS specific.
- *
- * You may set the macro PNG_ARM_NEON_FILE to the file name of file containing
- * a fragment of C source code which defines the png_have_neon function.  There
- * are a number of implementations in contrib/arm-neon, but the only one that
- * has partial support is contrib/arm-neon/linux.c - a generic Linux
- * implementation which reads /proc/cpufino.
- */
-#ifndef PNG_ARM_NEON_FILE
-#  ifdef __linux__
-#     define PNG_ARM_NEON_FILE "contrib/arm-neon/linux.c"
-#  endif
-#endif
-
-#ifdef PNG_ARM_NEON_FILE
-
-#include <signal.h> /* for sig_atomic_t */
-static int png_have_neon(png_structp png_ptr);
-#include PNG_ARM_NEON_FILE
-
-#else  /* PNG_ARM_NEON_FILE */
-#  error "PNG_ARM_NEON_FILE undefined: no support for run-time ARM NEON checks"
-#endif /* PNG_ARM_NEON_FILE */
-#endif /* PNG_ARM_NEON_CHECK_SUPPORTED */
-
-#ifndef PNG_ALIGNED_MEMORY_SUPPORTED
-#  error "ALIGNED_MEMORY is required; set: -DPNG_ALIGNED_MEMORY_SUPPORTED"
-#endif
-
-void
-png_init_filter_functions_neon(png_structp pp, unsigned int bpp)
-{
-   /* The switch statement is compiled in for ARM_NEON_API, the call to
-    * png_have_neon is compiled in for ARM_NEON_CHECK.  If both are defined
-    * the check is only performed if the API has not set the NEON option on
-    * or off explicitly.  In this case the check controls what happens.
-    *
-    * If the CHECK is not compiled in and the option is UNSET the behavior prior
-    * to 1.6.7 was to use the NEON code - this was a bug caused by having the
-    * wrong order of the 'ON' and 'default' cases.  UNSET now defaults to OFF,
-    * as documented in png.h
-    */
-   png_debug(1, "in png_init_filter_functions_neon");
-#ifdef PNG_ARM_NEON_API_SUPPORTED
-   switch ((pp->options >> PNG_ARM_NEON) & 3)
-   {
-      case PNG_OPTION_UNSET:
-         /* Allow the run-time check to execute if it has been enabled -
-          * thus both API and CHECK can be turned on.  If it isn't supported
-          * this case will fall through to the 'default' below, which just
-          * returns.
-          */
-#endif /* PNG_ARM_NEON_API_SUPPORTED */
-#ifdef PNG_ARM_NEON_CHECK_SUPPORTED
-         {
-            static volatile sig_atomic_t no_neon = -1; /* not checked */
-
-            if (no_neon < 0)
-               no_neon = !png_have_neon(pp);
-
-            if (no_neon)
-               return;
-         }
-#ifdef PNG_ARM_NEON_API_SUPPORTED
-         break;
-#endif
-#endif /* PNG_ARM_NEON_CHECK_SUPPORTED */
-
-#ifdef PNG_ARM_NEON_API_SUPPORTED
-      default: /* OFF or INVALID */
-         return;
-
-      case PNG_OPTION_ON:
-         /* Option turned on */
-         break;
-   }
-#endif
-
-   /* IMPORTANT: any new external functions used here must be declared using
-    * PNG_INTERNAL_FUNCTION in ../pngpriv.h.  This is required so that the
-    * 'prefix' option to configure works:
-    *
-    *    ./configure --with-libpng-prefix=foobar_
-    *
-    * Verify you have got this right by running the above command, doing a build
-    * and examining pngprefix.h; it must contain a #define for every external
-    * function you add.  (Notice that this happens automatically for the
-    * initialization function.)
-    */
-   pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up_neon;
-
-   if (bpp == 3)
-   {
-      pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_neon;
-      pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_neon;
-      pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
-         png_read_filter_row_paeth3_neon;
-   }
-
-   else if (bpp == 4)
-   {
-      pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_neon;
-      pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_neon;
-      pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
-          png_read_filter_row_paeth4_neon;
-   }
-}
-#endif /* PNG_ARM_NEON_OPT > 0 */
-#endif /* READ */
diff --git a/third_party/libpng16/arm/filter_neon.S b/third_party/libpng16/arm/filter_neon.S
deleted file mode 100644
index 2308aad..0000000
--- a/third_party/libpng16/arm/filter_neon.S
+++ /dev/null
@@ -1,253 +0,0 @@
-
-/* filter_neon.S - NEON optimised filter functions
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 2014,2017 Glenn Randers-Pehrson
- * Written by Mans Rullgard, 2011.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
-/* This is required to get the symbol renames, which are #defines, and the
- * definitions (or not) of PNG_ARM_NEON_OPT and PNG_ARM_NEON_IMPLEMENTATION.
- */
-#define PNG_VERSION_INFO_ONLY
-#include "../pngpriv.h"
-
-#if (defined(__linux__) || defined(__FreeBSD__)) && defined(__ELF__)
-.section .note.GNU-stack,"",%progbits /* mark stack as non-executable */
-#endif
-
-#ifdef PNG_READ_SUPPORTED
-
-/* Assembler NEON support - only works for 32-bit ARM (i.e. it does not work for
- * ARM64).  The code in arm/filter_neon_intrinsics.c supports ARM64, however it
- * only works if -mfpu=neon is specified on the GCC command line.  See pngpriv.h
- * for the logic which sets PNG_USE_ARM_NEON_ASM:
- */
-#if PNG_ARM_NEON_IMPLEMENTATION == 2 /* hand-coded assembler */
-
-#if PNG_ARM_NEON_OPT > 0
-
-#ifdef __ELF__
-#   define ELF
-#else
-#   define ELF @
-#endif
-
-        .arch armv7-a
-        .fpu  neon
-
-.macro  func    name, export=0
-    .macro endfunc
-ELF     .size   \name, . - \name
-        .endfunc
-        .purgem endfunc
-    .endm
-        .text
-
-        /* Explicitly specifying alignment here because some versions of
-         * GAS don't align code correctly.  This is harmless in correctly
-         * written versions of GAS.
-         */
-        .align 2
-
-    .if \export
-        .global \name
-    .endif
-ELF     .type   \name, STT_FUNC
-        .func   \name
-\name:
-.endm
-
-func    png_read_filter_row_sub4_neon, export=1
-        ldr             r3,  [r0, #4]           @ rowbytes
-        vmov.i8         d3,  #0
-1:
-        vld4.32         {d4[],d5[],d6[],d7[]},    [r1,:128]
-        vadd.u8         d0,  d3,  d4
-        vadd.u8         d1,  d0,  d5
-        vadd.u8         d2,  d1,  d6
-        vadd.u8         d3,  d2,  d7
-        vst4.32         {d0[0],d1[0],d2[0],d3[0]},[r1,:128]!
-        subs            r3,  r3,  #16
-        bgt             1b
-
-        bx              lr
-endfunc
-
-func    png_read_filter_row_sub3_neon, export=1
-        ldr             r3,  [r0, #4]           @ rowbytes
-        vmov.i8         d3,  #0
-        mov             r0,  r1
-        mov             r2,  #3
-        mov             r12, #12
-        vld1.8          {q11},    [r0], r12
-1:
-        vext.8          d5,  d22, d23, #3
-        vadd.u8         d0,  d3,  d22
-        vext.8          d6,  d22, d23, #6
-        vadd.u8         d1,  d0,  d5
-        vext.8          d7,  d23, d23, #1
-        vld1.8          {q11},    [r0], r12
-        vst1.32         {d0[0]},  [r1,:32], r2
-        vadd.u8         d2,  d1,  d6
-        vst1.32         {d1[0]},  [r1], r2
-        vadd.u8         d3,  d2,  d7
-        vst1.32         {d2[0]},  [r1], r2
-        vst1.32         {d3[0]},  [r1], r2
-        subs            r3,  r3,  #12
-        bgt             1b
-
-        bx              lr
-endfunc
-
-func    png_read_filter_row_up_neon, export=1
-        ldr             r3,  [r0, #4]           @ rowbytes
-1:
-        vld1.8          {q0}, [r1,:128]
-        vld1.8          {q1}, [r2,:128]!
-        vadd.u8         q0,  q0,  q1
-        vst1.8          {q0}, [r1,:128]!
-        subs            r3,  r3,  #16
-        bgt             1b
-
-        bx              lr
-endfunc
-
-func    png_read_filter_row_avg4_neon, export=1
-        ldr             r12, [r0, #4]           @ rowbytes
-        vmov.i8         d3,  #0
-1:
-        vld4.32         {d4[],d5[],d6[],d7[]},    [r1,:128]
-        vld4.32         {d16[],d17[],d18[],d19[]},[r2,:128]!
-        vhadd.u8        d0,  d3,  d16
-        vadd.u8         d0,  d0,  d4
-        vhadd.u8        d1,  d0,  d17
-        vadd.u8         d1,  d1,  d5
-        vhadd.u8        d2,  d1,  d18
-        vadd.u8         d2,  d2,  d6
-        vhadd.u8        d3,  d2,  d19
-        vadd.u8         d3,  d3,  d7
-        vst4.32         {d0[0],d1[0],d2[0],d3[0]},[r1,:128]!
-        subs            r12, r12, #16
-        bgt             1b
-
-        bx              lr
-endfunc
-
-func    png_read_filter_row_avg3_neon, export=1
-        push            {r4,lr}
-        ldr             r12, [r0, #4]           @ rowbytes
-        vmov.i8         d3,  #0
-        mov             r0,  r1
-        mov             r4,  #3
-        mov             lr,  #12
-        vld1.8          {q11},    [r0], lr
-1:
-        vld1.8          {q10},    [r2], lr
-        vext.8          d5,  d22, d23, #3
-        vhadd.u8        d0,  d3,  d20
-        vext.8          d17, d20, d21, #3
-        vadd.u8         d0,  d0,  d22
-        vext.8          d6,  d22, d23, #6
-        vhadd.u8        d1,  d0,  d17
-        vext.8          d18, d20, d21, #6
-        vadd.u8         d1,  d1,  d5
-        vext.8          d7,  d23, d23, #1
-        vld1.8          {q11},    [r0], lr
-        vst1.32         {d0[0]},  [r1,:32], r4
-        vhadd.u8        d2,  d1,  d18
-        vst1.32         {d1[0]},  [r1], r4
-        vext.8          d19, d21, d21, #1
-        vadd.u8         d2,  d2,  d6
-        vhadd.u8        d3,  d2,  d19
-        vst1.32         {d2[0]},  [r1], r4
-        vadd.u8         d3,  d3,  d7
-        vst1.32         {d3[0]},  [r1], r4
-        subs            r12, r12, #12
-        bgt             1b
-
-        pop             {r4,pc}
-endfunc
-
-.macro  paeth           rx,  ra,  rb,  rc
-        vaddl.u8        q12, \ra, \rb           @ a + b
-        vaddl.u8        q15, \rc, \rc           @ 2*c
-        vabdl.u8        q13, \rb, \rc           @ pa
-        vabdl.u8        q14, \ra, \rc           @ pb
-        vabd.u16        q15, q12, q15           @ pc
-        vcle.u16        q12, q13, q14           @ pa <= pb
-        vcle.u16        q13, q13, q15           @ pa <= pc
-        vcle.u16        q14, q14, q15           @ pb <= pc
-        vand            q12, q12, q13           @ pa <= pb && pa <= pc
-        vmovn.u16       d28, q14
-        vmovn.u16       \rx, q12
-        vbsl            d28, \rb, \rc
-        vbsl            \rx, \ra, d28
-.endm
-
-func    png_read_filter_row_paeth4_neon, export=1
-        ldr             r12, [r0, #4]           @ rowbytes
-        vmov.i8         d3,  #0
-        vmov.i8         d20, #0
-1:
-        vld4.32         {d4[],d5[],d6[],d7[]},    [r1,:128]
-        vld4.32         {d16[],d17[],d18[],d19[]},[r2,:128]!
-        paeth           d0,  d3,  d16, d20
-        vadd.u8         d0,  d0,  d4
-        paeth           d1,  d0,  d17, d16
-        vadd.u8         d1,  d1,  d5
-        paeth           d2,  d1,  d18, d17
-        vadd.u8         d2,  d2,  d6
-        paeth           d3,  d2,  d19, d18
-        vmov            d20, d19
-        vadd.u8         d3,  d3,  d7
-        vst4.32         {d0[0],d1[0],d2[0],d3[0]},[r1,:128]!
-        subs            r12, r12, #16
-        bgt             1b
-
-        bx              lr
-endfunc
-
-func    png_read_filter_row_paeth3_neon, export=1
-        push            {r4,lr}
-        ldr             r12, [r0, #4]           @ rowbytes
-        vmov.i8         d3,  #0
-        vmov.i8         d4,  #0
-        mov             r0,  r1
-        mov             r4,  #3
-        mov             lr,  #12
-        vld1.8          {q11},    [r0], lr
-1:
-        vld1.8          {q10},    [r2], lr
-        paeth           d0,  d3,  d20, d4
-        vext.8          d5,  d22, d23, #3
-        vadd.u8         d0,  d0,  d22
-        vext.8          d17, d20, d21, #3
-        paeth           d1,  d0,  d17, d20
-        vst1.32         {d0[0]},  [r1,:32], r4
-        vext.8          d6,  d22, d23, #6
-        vadd.u8         d1,  d1,  d5
-        vext.8          d18, d20, d21, #6
-        paeth           d2,  d1,  d18, d17
-        vext.8          d7,  d23, d23, #1
-        vld1.8          {q11},    [r0], lr
-        vst1.32         {d1[0]},  [r1], r4
-        vadd.u8         d2,  d2,  d6
-        vext.8          d19, d21, d21, #1
-        paeth           d3,  d2,  d19, d18
-        vst1.32         {d2[0]},  [r1], r4
-        vmov            d4,  d19
-        vadd.u8         d3,  d3,  d7
-        vst1.32         {d3[0]},  [r1], r4
-        subs            r12, r12, #12
-        bgt             1b
-
-        pop             {r4,pc}
-endfunc
-#endif /* PNG_ARM_NEON_OPT > 0 */
-#endif /* PNG_ARM_NEON_IMPLEMENTATION == 2 (assembler) */
-#endif /* READ */
diff --git a/third_party/libpng16/arm/filter_neon_intrinsics.c b/third_party/libpng16/arm/filter_neon_intrinsics.c
deleted file mode 100644
index 0647bfa..0000000
--- a/third_party/libpng16/arm/filter_neon_intrinsics.c
+++ /dev/null
@@ -1,402 +0,0 @@
-
-/* filter_neon_intrinsics.c - NEON optimised filter functions
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 2014,2016 Glenn Randers-Pehrson
- * Written by James Yu <james.yu at linaro.org>, October 2013.
- * Based on filter_neon.S, written by Mans Rullgard, 2011.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
-#include "../pngpriv.h"
-
-#ifdef PNG_READ_SUPPORTED
-
-/* This code requires -mfpu=neon on the command line: */
-#if PNG_ARM_NEON_IMPLEMENTATION == 1 /* intrinsics code from pngpriv.h */
-
-#if defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__)
-#  include <arm64_neon.h>
-#else
-#  include <arm_neon.h>
-#endif
-
-/* libpng row pointers are not necessarily aligned to any particular boundary,
- * however this code will only work with appropriate alignment.  arm/arm_init.c
- * checks for this (and will not compile unless it is done). This code uses
- * variants of png_aligncast to avoid compiler warnings.
- */
-#define png_ptr(type,pointer) png_aligncast(type *,pointer)
-#define png_ptrc(type,pointer) png_aligncastconst(const type *,pointer)
-
-/* The following relies on a variable 'temp_pointer' being declared with type
- * 'type'.  This is written this way just to hide the GCC strict aliasing
- * warning; note that the code is safe because there never is an alias between
- * the input and output pointers.
- *
- * When compiling with MSVC ARM64, the png_ldr macro can't be passed directly
- * to vst4_lane_u32, because of an internal compiler error inside MSVC.
- * To avoid this compiler bug, we use a temporary variable (vdest_val) to store
- * the result of png_ldr.
- */
-#define png_ldr(type,pointer)\
-   (temp_pointer = png_ptr(type,pointer), *temp_pointer)
-
-#if PNG_ARM_NEON_OPT > 0
-
-void
-png_read_filter_row_up_neon(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev_row)
-{
-   png_bytep rp = row;
-   png_bytep rp_stop = row + row_info->rowbytes;
-   png_const_bytep pp = prev_row;
-
-   png_debug(1, "in png_read_filter_row_up_neon");
-
-   for (; rp < rp_stop; rp += 16, pp += 16)
-   {
-      uint8x16_t qrp, qpp;
-
-      qrp = vld1q_u8(rp);
-      qpp = vld1q_u8(pp);
-      qrp = vaddq_u8(qrp, qpp);
-      vst1q_u8(rp, qrp);
-   }
-}
-
-void
-png_read_filter_row_sub3_neon(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev_row)
-{
-   png_bytep rp = row;
-   png_bytep rp_stop = row + row_info->rowbytes;
-
-   uint8x16_t vtmp = vld1q_u8(rp);
-   uint8x8x2_t *vrpt = png_ptr(uint8x8x2_t, &vtmp);
-   uint8x8x2_t vrp = *vrpt;
-
-   uint8x8x4_t vdest;
-   vdest.val[3] = vdup_n_u8(0);
-
-   png_debug(1, "in png_read_filter_row_sub3_neon");
-
-   for (; rp < rp_stop;)
-   {
-      uint8x8_t vtmp1, vtmp2;
-      uint32x2_t *temp_pointer;
-
-      vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3);
-      vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]);
-      vtmp2 = vext_u8(vrp.val[0], vrp.val[1], 6);
-      vdest.val[1] = vadd_u8(vdest.val[0], vtmp1);
-
-      vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1);
-      vdest.val[2] = vadd_u8(vdest.val[1], vtmp2);
-      vdest.val[3] = vadd_u8(vdest.val[2], vtmp1);
-
-      vtmp = vld1q_u8(rp + 12);
-      vrpt = png_ptr(uint8x8x2_t, &vtmp);
-      vrp = *vrpt;
-
-      vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0);
-      rp += 3;
-      vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0);
-      rp += 3;
-      vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0);
-      rp += 3;
-      vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0);
-      rp += 3;
-   }
-
-   PNG_UNUSED(prev_row)
-}
-
-void
-png_read_filter_row_sub4_neon(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev_row)
-{
-   png_bytep rp = row;
-   png_bytep rp_stop = row + row_info->rowbytes;
-
-   uint8x8x4_t vdest;
-   vdest.val[3] = vdup_n_u8(0);
-
-   png_debug(1, "in png_read_filter_row_sub4_neon");
-
-   for (; rp < rp_stop; rp += 16)
-   {
-      uint32x2x4_t vtmp = vld4_u32(png_ptr(uint32_t,rp));
-      uint8x8x4_t *vrpt = png_ptr(uint8x8x4_t,&vtmp);
-      uint8x8x4_t vrp = *vrpt;
-      uint32x2x4_t *temp_pointer;
-      uint32x2x4_t vdest_val;
-
-      vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]);
-      vdest.val[1] = vadd_u8(vdest.val[0], vrp.val[1]);
-      vdest.val[2] = vadd_u8(vdest.val[1], vrp.val[2]);
-      vdest.val[3] = vadd_u8(vdest.val[2], vrp.val[3]);
-
-      vdest_val = png_ldr(uint32x2x4_t, &vdest);
-      vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);
-   }
-
-   PNG_UNUSED(prev_row)
-}
-
-void
-png_read_filter_row_avg3_neon(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev_row)
-{
-   png_bytep rp = row;
-   png_const_bytep pp = prev_row;
-   png_bytep rp_stop = row + row_info->rowbytes;
-
-   uint8x16_t vtmp;
-   uint8x8x2_t *vrpt;
-   uint8x8x2_t vrp;
-   uint8x8x4_t vdest;
-   vdest.val[3] = vdup_n_u8(0);
-
-   vtmp = vld1q_u8(rp);
-   vrpt = png_ptr(uint8x8x2_t,&vtmp);
-   vrp = *vrpt;
-
-   png_debug(1, "in png_read_filter_row_avg3_neon");
-
-   for (; rp < rp_stop; pp += 12)
-   {
-      uint8x8_t vtmp1, vtmp2, vtmp3;
-
-      uint8x8x2_t *vppt;
-      uint8x8x2_t vpp;
-
-      uint32x2_t *temp_pointer;
-
-      vtmp = vld1q_u8(pp);
-      vppt = png_ptr(uint8x8x2_t,&vtmp);
-      vpp = *vppt;
-
-      vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3);
-      vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]);
-      vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);
-
-      vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 3);
-      vtmp3 = vext_u8(vrp.val[0], vrp.val[1], 6);
-      vdest.val[1] = vhadd_u8(vdest.val[0], vtmp2);
-      vdest.val[1] = vadd_u8(vdest.val[1], vtmp1);
-
-      vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 6);
-      vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1);
-
-      vtmp = vld1q_u8(rp + 12);
-      vrpt = png_ptr(uint8x8x2_t,&vtmp);
-      vrp = *vrpt;
-
-      vdest.val[2] = vhadd_u8(vdest.val[1], vtmp2);
-      vdest.val[2] = vadd_u8(vdest.val[2], vtmp3);
-
-      vtmp2 = vext_u8(vpp.val[1], vpp.val[1], 1);
-
-      vdest.val[3] = vhadd_u8(vdest.val[2], vtmp2);
-      vdest.val[3] = vadd_u8(vdest.val[3], vtmp1);
-
-      vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0);
-      rp += 3;
-      vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0);
-      rp += 3;
-      vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0);
-      rp += 3;
-      vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0);
-      rp += 3;
-   }
-}
-
-void
-png_read_filter_row_avg4_neon(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev_row)
-{
-   png_bytep rp = row;
-   png_bytep rp_stop = row + row_info->rowbytes;
-   png_const_bytep pp = prev_row;
-
-   uint8x8x4_t vdest;
-   vdest.val[3] = vdup_n_u8(0);
-
-   png_debug(1, "in png_read_filter_row_avg4_neon");
-
-   for (; rp < rp_stop; rp += 16, pp += 16)
-   {
-      uint32x2x4_t vtmp;
-      uint8x8x4_t *vrpt, *vppt;
-      uint8x8x4_t vrp, vpp;
-      uint32x2x4_t *temp_pointer;
-      uint32x2x4_t vdest_val;
-
-      vtmp = vld4_u32(png_ptr(uint32_t,rp));
-      vrpt = png_ptr(uint8x8x4_t,&vtmp);
-      vrp = *vrpt;
-      vtmp = vld4_u32(png_ptrc(uint32_t,pp));
-      vppt = png_ptr(uint8x8x4_t,&vtmp);
-      vpp = *vppt;
-
-      vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]);
-      vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);
-      vdest.val[1] = vhadd_u8(vdest.val[0], vpp.val[1]);
-      vdest.val[1] = vadd_u8(vdest.val[1], vrp.val[1]);
-      vdest.val[2] = vhadd_u8(vdest.val[1], vpp.val[2]);
-      vdest.val[2] = vadd_u8(vdest.val[2], vrp.val[2]);
-      vdest.val[3] = vhadd_u8(vdest.val[2], vpp.val[3]);
-      vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]);
-
-      vdest_val = png_ldr(uint32x2x4_t, &vdest);
-      vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);
-   }
-}
-
-static uint8x8_t
-paeth(uint8x8_t a, uint8x8_t b, uint8x8_t c)
-{
-   uint8x8_t d, e;
-   uint16x8_t p1, pa, pb, pc;
-
-   p1 = vaddl_u8(a, b); /* a + b */
-   pc = vaddl_u8(c, c); /* c * 2 */
-   pa = vabdl_u8(b, c); /* pa */
-   pb = vabdl_u8(a, c); /* pb */
-   pc = vabdq_u16(p1, pc); /* pc */
-
-   p1 = vcleq_u16(pa, pb); /* pa <= pb */
-   pa = vcleq_u16(pa, pc); /* pa <= pc */
-   pb = vcleq_u16(pb, pc); /* pb <= pc */
-
-   p1 = vandq_u16(p1, pa); /* pa <= pb && pa <= pc */
-
-   d = vmovn_u16(pb);
-   e = vmovn_u16(p1);
-
-   d = vbsl_u8(d, b, c);
-   e = vbsl_u8(e, a, d);
-
-   return e;
-}
-
-void
-png_read_filter_row_paeth3_neon(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev_row)
-{
-   png_bytep rp = row;
-   png_const_bytep pp = prev_row;
-   png_bytep rp_stop = row + row_info->rowbytes;
-
-   uint8x16_t vtmp;
-   uint8x8x2_t *vrpt;
-   uint8x8x2_t vrp;
-   uint8x8_t vlast = vdup_n_u8(0);
-   uint8x8x4_t vdest;
-   vdest.val[3] = vdup_n_u8(0);
-
-   vtmp = vld1q_u8(rp);
-   vrpt = png_ptr(uint8x8x2_t,&vtmp);
-   vrp = *vrpt;
-
-   png_debug(1, "in png_read_filter_row_paeth3_neon");
-
-   for (; rp < rp_stop; pp += 12)
-   {
-      uint8x8x2_t *vppt;
-      uint8x8x2_t vpp;
-      uint8x8_t vtmp1, vtmp2, vtmp3;
-      uint32x2_t *temp_pointer;
-
-      vtmp = vld1q_u8(pp);
-      vppt = png_ptr(uint8x8x2_t,&vtmp);
-      vpp = *vppt;
-
-      vdest.val[0] = paeth(vdest.val[3], vpp.val[0], vlast);
-      vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);
-
-      vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3);
-      vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 3);
-      vdest.val[1] = paeth(vdest.val[0], vtmp2, vpp.val[0]);
-      vdest.val[1] = vadd_u8(vdest.val[1], vtmp1);
-
-      vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 6);
-      vtmp3 = vext_u8(vpp.val[0], vpp.val[1], 6);
-      vdest.val[2] = paeth(vdest.val[1], vtmp3, vtmp2);
-      vdest.val[2] = vadd_u8(vdest.val[2], vtmp1);
-
-      vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1);
-      vtmp2 = vext_u8(vpp.val[1], vpp.val[1], 1);
-
-      vtmp = vld1q_u8(rp + 12);
-      vrpt = png_ptr(uint8x8x2_t,&vtmp);
-      vrp = *vrpt;
-
-      vdest.val[3] = paeth(vdest.val[2], vtmp2, vtmp3);
-      vdest.val[3] = vadd_u8(vdest.val[3], vtmp1);
-
-      vlast = vtmp2;
-
-      vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0);
-      rp += 3;
-      vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0);
-      rp += 3;
-      vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0);
-      rp += 3;
-      vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0);
-      rp += 3;
-   }
-}
-
-void
-png_read_filter_row_paeth4_neon(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev_row)
-{
-   png_bytep rp = row;
-   png_bytep rp_stop = row + row_info->rowbytes;
-   png_const_bytep pp = prev_row;
-
-   uint8x8_t vlast = vdup_n_u8(0);
-   uint8x8x4_t vdest;
-   vdest.val[3] = vdup_n_u8(0);
-
-   png_debug(1, "in png_read_filter_row_paeth4_neon");
-
-   for (; rp < rp_stop; rp += 16, pp += 16)
-   {
-      uint32x2x4_t vtmp;
-      uint8x8x4_t *vrpt, *vppt;
-      uint8x8x4_t vrp, vpp;
-      uint32x2x4_t *temp_pointer;
-      uint32x2x4_t vdest_val;
-
-      vtmp = vld4_u32(png_ptr(uint32_t,rp));
-      vrpt = png_ptr(uint8x8x4_t,&vtmp);
-      vrp = *vrpt;
-      vtmp = vld4_u32(png_ptrc(uint32_t,pp));
-      vppt = png_ptr(uint8x8x4_t,&vtmp);
-      vpp = *vppt;
-
-      vdest.val[0] = paeth(vdest.val[3], vpp.val[0], vlast);
-      vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);
-      vdest.val[1] = paeth(vdest.val[0], vpp.val[1], vpp.val[0]);
-      vdest.val[1] = vadd_u8(vdest.val[1], vrp.val[1]);
-      vdest.val[2] = paeth(vdest.val[1], vpp.val[2], vpp.val[1]);
-      vdest.val[2] = vadd_u8(vdest.val[2], vrp.val[2]);
-      vdest.val[3] = paeth(vdest.val[2], vpp.val[3], vpp.val[2]);
-      vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]);
-
-      vlast = vpp.val[3];
-
-      vdest_val = png_ldr(uint32x2x4_t, &vdest);
-      vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);
-   }
-}
-
-#endif /* PNG_ARM_NEON_OPT > 0 */
-#endif /* PNG_ARM_NEON_IMPLEMENTATION == 1 (intrinsics) */
-#endif /* READ */
diff --git a/third_party/libpng16/arm/palette_neon_intrinsics.c b/third_party/libpng16/arm/palette_neon_intrinsics.c
deleted file mode 100644
index 8217d65..0000000
--- a/third_party/libpng16/arm/palette_neon_intrinsics.c
+++ /dev/null
@@ -1,149 +0,0 @@
-
-/* palette_neon_intrinsics.c - NEON optimised palette expansion functions
- *
- * Copyright (c) 2018-2019 Cosmin Truta
- * Copyright (c) 2017-2018 Arm Holdings. All rights reserved.
- * Written by Richard Townsend <Richard.Townsend@arm.com>, February 2017.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
-#include "../pngpriv.h"
-
-#if PNG_ARM_NEON_IMPLEMENTATION == 1
-
-#if defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__)
-#  include <arm64_neon.h>
-#else
-#  include <arm_neon.h>
-#endif
-
-/* Build an RGBA8 palette from the separate RGB and alpha palettes. */
-void
-png_riffle_palette_neon(png_structrp png_ptr)
-{
-   png_const_colorp palette = png_ptr->palette;
-   png_bytep riffled_palette = png_ptr->riffled_palette;
-   png_const_bytep trans_alpha = png_ptr->trans_alpha;
-   int num_trans = png_ptr->num_trans;
-   int i;
-
-   png_debug(1, "in png_riffle_palette_neon");
-
-   /* Initially black, opaque. */
-   uint8x16x4_t w = {{
-      vdupq_n_u8(0x00),
-      vdupq_n_u8(0x00),
-      vdupq_n_u8(0x00),
-      vdupq_n_u8(0xff),
-   }};
-
-   /* First, riffle the RGB colours into an RGBA8 palette.
-    * The alpha component is set to opaque for now.
-    */
-   for (i = 0; i < 256; i += 16)
-   {
-      uint8x16x3_t v = vld3q_u8((png_const_bytep)(palette + i));
-      w.val[0] = v.val[0];
-      w.val[1] = v.val[1];
-      w.val[2] = v.val[2];
-      vst4q_u8(riffled_palette + (i << 2), w);
-   }
-
-   /* Fix up the missing transparency values. */
-   for (i = 0; i < num_trans; i++)
-      riffled_palette[(i << 2) + 3] = trans_alpha[i];
-}
-
-/* Expands a palettized row into RGBA8. */
-int
-png_do_expand_palette_rgba8_neon(png_structrp png_ptr, png_row_infop row_info,
-    png_const_bytep row, png_bytepp ssp, png_bytepp ddp)
-{
-   png_uint_32 row_width = row_info->width;
-   const png_uint_32 *riffled_palette =
-      (const png_uint_32 *)png_ptr->riffled_palette;
-   const png_int_32 pixels_per_chunk = 4;
-   int i;
-
-   png_debug(1, "in png_do_expand_palette_rgba8_neon");
-
-   if (row_width < pixels_per_chunk)
-      return 0;
-
-   /* This function originally gets the last byte of the output row.
-    * The NEON part writes forward from a given position, so we have
-    * to seek this back by 4 pixels x 4 bytes.
-    */
-   *ddp = *ddp - ((pixels_per_chunk * sizeof(png_uint_32)) - 1);
-
-   for (i = 0; i < row_width; i += pixels_per_chunk)
-   {
-      uint32x4_t cur;
-      png_bytep sp = *ssp - i, dp = *ddp - (i << 2);
-      cur = vld1q_dup_u32 (riffled_palette + *(sp - 3));
-      cur = vld1q_lane_u32(riffled_palette + *(sp - 2), cur, 1);
-      cur = vld1q_lane_u32(riffled_palette + *(sp - 1), cur, 2);
-      cur = vld1q_lane_u32(riffled_palette + *(sp - 0), cur, 3);
-      vst1q_u32((void *)dp, cur);
-   }
-   if (i != row_width)
-   {
-      /* Remove the amount that wasn't processed. */
-      i -= pixels_per_chunk;
-   }
-
-   /* Decrement output pointers. */
-   *ssp = *ssp - i;
-   *ddp = *ddp - (i << 2);
-   return i;
-}
-
-/* Expands a palettized row into RGB8. */
-int
-png_do_expand_palette_rgb8_neon(png_structrp png_ptr, png_row_infop row_info,
-    png_const_bytep row, png_bytepp ssp, png_bytepp ddp)
-{
-   png_uint_32 row_width = row_info->width;
-   png_const_bytep palette = (png_const_bytep)png_ptr->palette;
-   const png_uint_32 pixels_per_chunk = 8;
-   int i;
-
-   png_debug(1, "in png_do_expand_palette_rgb8_neon");
-
-   if (row_width <= pixels_per_chunk)
-      return 0;
-
-   /* Seeking this back by 8 pixels x 3 bytes. */
-   *ddp = *ddp - ((pixels_per_chunk * sizeof(png_color)) - 1);
-
-   for (i = 0; i < row_width; i += pixels_per_chunk)
-   {
-      uint8x8x3_t cur;
-      png_bytep sp = *ssp - i, dp = *ddp - ((i << 1) + i);
-      cur = vld3_dup_u8(palette + sizeof(png_color) * (*(sp - 7)));
-      cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 6)), cur, 1);
-      cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 5)), cur, 2);
-      cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 4)), cur, 3);
-      cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 3)), cur, 4);
-      cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 2)), cur, 5);
-      cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 1)), cur, 6);
-      cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 0)), cur, 7);
-      vst3_u8((void *)dp, cur);
-   }
-
-   if (i != row_width)
-   {
-      /* Remove the amount that wasn't processed. */
-      i -= pixels_per_chunk;
-   }
-
-   /* Decrement output pointers. */
-   *ssp = *ssp - i;
-   *ddp = *ddp - ((i << 1) + i);
-   return i;
-}
-
-#endif /* PNG_ARM_NEON_IMPLEMENTATION */
diff --git a/third_party/libpng16/intel/filter_sse2_intrinsics.c b/third_party/libpng16/intel/filter_sse2_intrinsics.c
deleted file mode 100644
index f52aaa8..0000000
--- a/third_party/libpng16/intel/filter_sse2_intrinsics.c
+++ /dev/null
@@ -1,391 +0,0 @@
-
-/* filter_sse2_intrinsics.c - SSE2 optimized filter functions
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 2016-2017 Glenn Randers-Pehrson
- * Written by Mike Klein and Matt Sarett
- * Derived from arm/filter_neon_intrinsics.c
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
-#include "../pngpriv.h"
-
-#ifdef PNG_READ_SUPPORTED
-
-#if PNG_INTEL_SSE_IMPLEMENTATION > 0
-
-#include <immintrin.h>
-
-/* Functions in this file look at most 3 pixels (a,b,c) to predict the 4th (d).
- * They're positioned like this:
- *    prev:  c b
- *    row:   a d
- * The Sub filter predicts d=a, Avg d=(a+b)/2, and Paeth predicts d to be
- * whichever of a, b, or c is closest to p=a+b-c.
- */
-
-static __m128i load4(const void* p) {
-   int tmp;
-   memcpy(&tmp, p, sizeof(tmp));
-   return _mm_cvtsi32_si128(tmp);
-}
-
-static void store4(void* p, __m128i v) {
-   int tmp = _mm_cvtsi128_si32(v);
-   memcpy(p, &tmp, sizeof(int));
-}
-
-static __m128i load3(const void* p) {
-   png_uint_32 tmp = 0;
-   memcpy(&tmp, p, 3);
-   return _mm_cvtsi32_si128(tmp);
-}
-
-static void store3(void* p, __m128i v) {
-   int tmp = _mm_cvtsi128_si32(v);
-   memcpy(p, &tmp, 3);
-}
-
-void png_read_filter_row_sub3_sse2(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev)
-{
-   /* The Sub filter predicts each pixel as the previous pixel, a.
-    * There is no pixel to the left of the first pixel.  It's encoded directly.
-    * That works with our main loop if we just say that left pixel was zero.
-    */
-   size_t rb;
-
-   __m128i a, d = _mm_setzero_si128();
-
-   png_debug(1, "in png_read_filter_row_sub3_sse2");
-
-   rb = row_info->rowbytes;
-   while (rb >= 4) {
-      a = d; d = load4(row);
-      d = _mm_add_epi8(d, a);
-      store3(row, d);
-
-      row += 3;
-      rb  -= 3;
-   }
-   if (rb > 0) {
-      a = d; d = load3(row);
-      d = _mm_add_epi8(d, a);
-      store3(row, d);
-
-      row += 3;
-      rb  -= 3;
-   }
-   PNG_UNUSED(prev)
-}
-
-void png_read_filter_row_sub4_sse2(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev)
-{
-   /* The Sub filter predicts each pixel as the previous pixel, a.
-    * There is no pixel to the left of the first pixel.  It's encoded directly.
-    * That works with our main loop if we just say that left pixel was zero.
-    */
-   size_t rb;
-
-   __m128i a, d = _mm_setzero_si128();
-
-   png_debug(1, "in png_read_filter_row_sub4_sse2");
-
-   rb = row_info->rowbytes+4;
-   while (rb > 4) {
-      a = d; d = load4(row);
-      d = _mm_add_epi8(d, a);
-      store4(row, d);
-
-      row += 4;
-      rb  -= 4;
-   }
-   PNG_UNUSED(prev)
-}
-
-void png_read_filter_row_avg3_sse2(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev)
-{
-   /* The Avg filter predicts each pixel as the (truncated) average of a and b.
-    * There's no pixel to the left of the first pixel.  Luckily, it's
-    * predicted to be half of the pixel above it.  So again, this works
-    * perfectly with our loop if we make sure a starts at zero.
-    */
-
-   size_t rb;
-
-   const __m128i zero = _mm_setzero_si128();
-
-   __m128i    b;
-   __m128i a, d = zero;
-
-   png_debug(1, "in png_read_filter_row_avg3_sse2");
-   rb = row_info->rowbytes;
-   while (rb >= 4) {
-      __m128i avg;
-             b = load4(prev);
-      a = d; d = load4(row );
-
-      /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
-      avg = _mm_avg_epu8(a,b);
-      /* ...but we can fix it up by subtracting off 1 if it rounded up. */
-      avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
-                                            _mm_set1_epi8(1)));
-      d = _mm_add_epi8(d, avg);
-      store3(row, d);
-
-      prev += 3;
-      row  += 3;
-      rb   -= 3;
-   }
-   if (rb > 0) {
-      __m128i avg;
-             b = load3(prev);
-      a = d; d = load3(row );
-
-      /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
-      avg = _mm_avg_epu8(a,b);
-      /* ...but we can fix it up by subtracting off 1 if it rounded up. */
-      avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
-                                            _mm_set1_epi8(1)));
-
-      d = _mm_add_epi8(d, avg);
-      store3(row, d);
-
-      prev += 3;
-      row  += 3;
-      rb   -= 3;
-   }
-}
-
-void png_read_filter_row_avg4_sse2(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev)
-{
-   /* The Avg filter predicts each pixel as the (truncated) average of a and b.
-    * There's no pixel to the left of the first pixel.  Luckily, it's
-    * predicted to be half of the pixel above it.  So again, this works
-    * perfectly with our loop if we make sure a starts at zero.
-    */
-   size_t rb;
-   const __m128i zero = _mm_setzero_si128();
-   __m128i    b;
-   __m128i a, d = zero;
-
-   png_debug(1, "in png_read_filter_row_avg4_sse2");
-
-   rb = row_info->rowbytes+4;
-   while (rb > 4) {
-      __m128i avg;
-             b = load4(prev);
-      a = d; d = load4(row );
-
-      /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
-      avg = _mm_avg_epu8(a,b);
-      /* ...but we can fix it up by subtracting off 1 if it rounded up. */
-      avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
-                                            _mm_set1_epi8(1)));
-
-      d = _mm_add_epi8(d, avg);
-      store4(row, d);
-
-      prev += 4;
-      row  += 4;
-      rb   -= 4;
-   }
-}
-
-/* Returns |x| for 16-bit lanes. */
-static __m128i abs_i16(__m128i x) {
-#if PNG_INTEL_SSE_IMPLEMENTATION >= 2
-   return _mm_abs_epi16(x);
-#else
-   /* Read this all as, return x<0 ? -x : x.
-   * To negate two's complement, you flip all the bits then add 1.
-    */
-   __m128i is_negative = _mm_cmplt_epi16(x, _mm_setzero_si128());
-
-   /* Flip negative lanes. */
-   x = _mm_xor_si128(x, is_negative);
-
-   /* +1 to negative lanes, else +0. */
-   x = _mm_sub_epi16(x, is_negative);
-   return x;
-#endif
-}
-
-/* Bytewise c ? t : e. */
-static __m128i if_then_else(__m128i c, __m128i t, __m128i e) {
-#if PNG_INTEL_SSE_IMPLEMENTATION >= 3
-   return _mm_blendv_epi8(e,t,c);
-#else
-   return _mm_or_si128(_mm_and_si128(c, t), _mm_andnot_si128(c, e));
-#endif
-}
-
-void png_read_filter_row_paeth3_sse2(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev)
-{
-   /* Paeth tries to predict pixel d using the pixel to the left of it, a,
-    * and two pixels from the previous row, b and c:
-    *   prev: c b
-    *   row:  a d
-    * The Paeth function predicts d to be whichever of a, b, or c is nearest to
-    * p=a+b-c.
-    *
-    * The first pixel has no left context, and so uses an Up filter, p = b.
-    * This works naturally with our main loop's p = a+b-c if we force a and c
-    * to zero.
-    * Here we zero b and d, which become c and a respectively at the start of
-    * the loop.
-    */
-   size_t rb;
-   const __m128i zero = _mm_setzero_si128();
-   __m128i c, b = zero,
-           a, d = zero;
-
-   png_debug(1, "in png_read_filter_row_paeth3_sse2");
-
-   rb = row_info->rowbytes;
-   while (rb >= 4) {
-      /* It's easiest to do this math (particularly, deal with pc) with 16-bit
-       * intermediates.
-       */
-      __m128i pa,pb,pc,smallest,nearest;
-      c = b; b = _mm_unpacklo_epi8(load4(prev), zero);
-      a = d; d = _mm_unpacklo_epi8(load4(row ), zero);
-
-      /* (p-a) == (a+b-c - a) == (b-c) */
-   
-      pa = _mm_sub_epi16(b,c);
-
-      /* (p-b) == (a+b-c - b) == (a-c) */
-      pb = _mm_sub_epi16(a,c);
-
-      /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
-      pc = _mm_add_epi16(pa,pb);
-
-      pa = abs_i16(pa);  /* |p-a| */
-      pb = abs_i16(pb);  /* |p-b| */
-      pc = abs_i16(pc);  /* |p-c| */
-
-      smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
-
-      /* Paeth breaks ties favoring a over b over c. */
-      nearest  = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
-                 if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
-                                                             c));
-
-      /* Note `_epi8`: we need addition to wrap modulo 255. */
-      d = _mm_add_epi8(d, nearest);
-      store3(row, _mm_packus_epi16(d,d));
-
-      prev += 3;
-      row  += 3;
-      rb   -= 3;
-   }
-   if (rb > 0) {
-      /* It's easiest to do this math (particularly, deal with pc) with 16-bit
-       * intermediates.
-       */
-      __m128i pa,pb,pc,smallest,nearest;
-      c = b; b = _mm_unpacklo_epi8(load3(prev), zero);
-      a = d; d = _mm_unpacklo_epi8(load3(row ), zero);
-
-      /* (p-a) == (a+b-c - a) == (b-c) */
-      pa = _mm_sub_epi16(b,c);
-
-      /* (p-b) == (a+b-c - b) == (a-c) */
-      pb = _mm_sub_epi16(a,c);
-
-      /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
-      pc = _mm_add_epi16(pa,pb);
-
-      pa = abs_i16(pa);  /* |p-a| */
-      pb = abs_i16(pb);  /* |p-b| */
-      pc = abs_i16(pc);  /* |p-c| */
-
-      smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
-
-      /* Paeth breaks ties favoring a over b over c. */
-      nearest  = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
-                         if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
-                                                                     c));
-
-      /* Note `_epi8`: we need addition to wrap modulo 255. */
-      d = _mm_add_epi8(d, nearest);
-      store3(row, _mm_packus_epi16(d,d));
-
-      prev += 3;
-      row  += 3;
-      rb   -= 3;
-   }
-}
-
-void png_read_filter_row_paeth4_sse2(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev)
-{
-   /* Paeth tries to predict pixel d using the pixel to the left of it, a,
-    * and two pixels from the previous row, b and c:
-    *   prev: c b
-    *   row:  a d
-    * The Paeth function predicts d to be whichever of a, b, or c is nearest to
-    * p=a+b-c.
-    *
-    * The first pixel has no left context, and so uses an Up filter, p = b.
-    * This works naturally with our main loop's p = a+b-c if we force a and c
-    * to zero.
-    * Here we zero b and d, which become c and a respectively at the start of
-    * the loop.
-    */
-   size_t rb;
-   const __m128i zero = _mm_setzero_si128();
-   __m128i pa,pb,pc,smallest,nearest;
-   __m128i c, b = zero,
-           a, d = zero;
-
-   png_debug(1, "in png_read_filter_row_paeth4_sse2");
-
-   rb = row_info->rowbytes+4;
-   while (rb > 4) {
-      /* It's easiest to do this math (particularly, deal with pc) with 16-bit
-       * intermediates.
-       */
-      c = b; b = _mm_unpacklo_epi8(load4(prev), zero);
-      a = d; d = _mm_unpacklo_epi8(load4(row ), zero);
-
-      /* (p-a) == (a+b-c - a) == (b-c) */
-      pa = _mm_sub_epi16(b,c);
-
-      /* (p-b) == (a+b-c - b) == (a-c) */
-      pb = _mm_sub_epi16(a,c);
-
-      /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
-      pc = _mm_add_epi16(pa,pb);
-
-      pa = abs_i16(pa);  /* |p-a| */
-      pb = abs_i16(pb);  /* |p-b| */
-      pc = abs_i16(pc);  /* |p-c| */
-
-      smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
-
-      /* Paeth breaks ties favoring a over b over c. */
-      nearest  = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
-                         if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
-                                                                     c));
-
-      /* Note `_epi8`: we need addition to wrap modulo 255. */
-      d = _mm_add_epi8(d, nearest);
-      store4(row, _mm_packus_epi16(d,d));
-
-      prev += 4;
-      row  += 4;
-      rb   -= 4;
-   }
-}
-
-#endif /* PNG_INTEL_SSE_IMPLEMENTATION > 0 */
-#endif /* READ */
diff --git a/third_party/libpng16/intel/intel_init.c b/third_party/libpng16/intel/intel_init.c
deleted file mode 100644
index 2f8168b..0000000
--- a/third_party/libpng16/intel/intel_init.c
+++ /dev/null
@@ -1,52 +0,0 @@
-
-/* intel_init.c - SSE2 optimized filter functions
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 2016-2017 Glenn Randers-Pehrson
- * Written by Mike Klein and Matt Sarett, Google, Inc.
- * Derived from arm/arm_init.c
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
-#include "../pngpriv.h"
-
-#ifdef PNG_READ_SUPPORTED
-#if PNG_INTEL_SSE_IMPLEMENTATION > 0
-
-void
-png_init_filter_functions_sse2(png_structp pp, unsigned int bpp)
-{
-   /* The techniques used to implement each of these filters in SSE operate on
-    * one pixel at a time.
-    * So they generally speed up 3bpp images about 3x, 4bpp images about 4x.
-    * They can scale up to 6 and 8 bpp images and down to 2 bpp images,
-    * but they'd not likely have any benefit for 1bpp images.
-    * Most of these can be implemented using only MMX and 64-bit registers,
-    * but they end up a bit slower than using the equally-ubiquitous SSE2.
-   */
-   png_debug(1, "in png_init_filter_functions_sse2");
-   if (bpp == 3)
-   {
-      pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_sse2;
-      pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_sse2;
-      pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
-         png_read_filter_row_paeth3_sse2;
-   }
-   else if (bpp == 4)
-   {
-      pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_sse2;
-      pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_sse2;
-      pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
-          png_read_filter_row_paeth4_sse2;
-   }
-
-   /* No need optimize PNG_FILTER_VALUE_UP.  The compiler should
-    * autovectorize.
-    */
-}
-
-#endif /* PNG_INTEL_SSE_IMPLEMENTATION > 0 */
-#endif /* PNG_READ_SUPPORTED */
diff --git a/third_party/libpng16/png.c b/third_party/libpng16/png.c
deleted file mode 100644
index ddf7a2f..0000000
--- a/third_party/libpng16/png.c
+++ /dev/null
@@ -1,4608 +0,0 @@
-
-/* png.c - location for general purpose libpng functions
- *
- * Copyright (c) 2018-2019 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
-#include "pngpriv.h"
-
-/* Generate a compiler error if there is an old png.h in the search path. */
-typedef png_libpng_version_1_6_37 Your_png_h_is_not_version_1_6_37;
-
-#ifdef __GNUC__
-/* The version tests may need to be added to, but the problem warning has
- * consistently been fixed in GCC versions which obtain wide-spread release.
- * The problem is that many versions of GCC rearrange comparison expressions in
- * the optimizer in such a way that the results of the comparison will change
- * if signed integer overflow occurs.  Such comparisons are not permitted in
- * ANSI C90, however GCC isn't clever enough to work out that that do not occur
- * below in png_ascii_from_fp and png_muldiv, so it produces a warning with
- * -Wextra.  Unfortunately this is highly dependent on the optimizer and the
- * machine architecture so the warning comes and goes unpredictably and is
- * impossible to "fix", even were that a good idea.
- */
-#if __GNUC__ == 7 && __GNUC_MINOR__ == 1
-#define GCC_STRICT_OVERFLOW 1
-#endif /* GNU 7.1.x */
-#endif /* GNU */
-#ifndef GCC_STRICT_OVERFLOW
-#define GCC_STRICT_OVERFLOW 0
-#endif
-
-/* Tells libpng that we have already handled the first "num_bytes" bytes
- * of the PNG file signature.  If the PNG data is embedded into another
- * stream we can set num_bytes = 8 so that libpng will not attempt to read
- * or write any of the magic bytes before it starts on the IHDR.
- */
-
-#ifdef PNG_READ_SUPPORTED
-void PNGAPI
-png_set_sig_bytes(png_structrp png_ptr, int num_bytes)
-{
-   unsigned int nb = (unsigned int)num_bytes;
-
-   png_debug(1, "in png_set_sig_bytes");
-
-   if (png_ptr == NULL)
-      return;
-
-   if (num_bytes < 0)
-      nb = 0;
-
-   if (nb > 8)
-      png_error(png_ptr, "Too many bytes for PNG signature");
-
-   png_ptr->sig_bytes = (png_byte)nb;
-}
-
-/* Checks whether the supplied bytes match the PNG signature.  We allow
- * checking less than the full 8-byte signature so that those apps that
- * already read the first few bytes of a file to determine the file type
- * can simply check the remaining bytes for extra assurance.  Returns
- * an integer less than, equal to, or greater than zero if sig is found,
- * respectively, to be less than, to match, or be greater than the correct
- * PNG signature (this is the same behavior as strcmp, memcmp, etc).
- */
-int PNGAPI
-png_sig_cmp(png_const_bytep sig, size_t start, size_t num_to_check)
-{
-   png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
-
-   if (num_to_check > 8)
-      num_to_check = 8;
-
-   else if (num_to_check < 1)
-      return (-1);
-
-   if (start > 7)
-      return (-1);
-
-   if (start + num_to_check > 8)
-      num_to_check = 8 - start;
-
-   return ((int)(memcmp(&sig[start], &png_signature[start], num_to_check)));
-}
-
-#endif /* READ */
-
-#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
-/* Function to allocate memory for zlib */
-PNG_FUNCTION(voidpf /* PRIVATE */,
-png_zalloc,(voidpf png_ptr, uInt items, uInt size),PNG_ALLOCATED)
-{
-   png_alloc_size_t num_bytes = size;
-
-   if (png_ptr == NULL)
-      return NULL;
-
-   if (items >= (~(png_alloc_size_t)0)/size)
-   {
-      png_warning (png_voidcast(png_structrp, png_ptr),
-          "Potential overflow in png_zalloc()");
-      return NULL;
-   }
-
-   num_bytes *= items;
-   return png_malloc_warn(png_voidcast(png_structrp, png_ptr), num_bytes);
-}
-
-/* Function to free memory for zlib */
-void /* PRIVATE */
-png_zfree(voidpf png_ptr, voidpf ptr)
-{
-   png_free(png_voidcast(png_const_structrp,png_ptr), ptr);
-}
-
-/* Reset the CRC variable to 32 bits of 1's.  Care must be taken
- * in case CRC is > 32 bits to leave the top bits 0.
- */
-void /* PRIVATE */
-png_reset_crc(png_structrp png_ptr)
-{
-   /* The cast is safe because the crc is a 32-bit value. */
-   png_ptr->crc = (png_uint_32)crc32(0, Z_NULL, 0);
-}
-
-/* Calculate the CRC over a section of data.  We can only pass as
- * much data to this routine as the largest single buffer size.  We
- * also check that this data will actually be used before going to the
- * trouble of calculating it.
- */
-void /* PRIVATE */
-png_calculate_crc(png_structrp png_ptr, png_const_bytep ptr, size_t length)
-{
-   int need_crc = 1;
-
-   if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0)
-   {
-      if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
-          (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
-         need_crc = 0;
-   }
-
-   else /* critical */
-   {
-      if ((png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) != 0)
-         need_crc = 0;
-   }
-
-   /* 'uLong' is defined in zlib.h as unsigned long; this means that on some
-    * systems it is a 64-bit value.  crc32, however, returns 32 bits so the
-    * following cast is safe.  'uInt' may be no more than 16 bits, so it is
-    * necessary to perform a loop here.
-    */
-   if (need_crc != 0 && length > 0)
-   {
-      uLong crc = png_ptr->crc; /* Should never issue a warning */
-
-      do
-      {
-         uInt safe_length = (uInt)length;
-#ifndef __COVERITY__
-         if (safe_length == 0)
-            safe_length = (uInt)-1; /* evil, but safe */
-#endif
-
-         crc = crc32(crc, ptr, safe_length);
-
-         /* The following should never issue compiler warnings; if they do the
-          * target system has characteristics that will probably violate other
-          * assumptions within the libpng code.
-          */
-         ptr += safe_length;
-         length -= safe_length;
-      }
-      while (length > 0);
-
-      /* And the following is always safe because the crc is only 32 bits. */
-      png_ptr->crc = (png_uint_32)crc;
-   }
-}
-
-/* Check a user supplied version number, called from both read and write
- * functions that create a png_struct.
- */
-int
-png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver)
-{
-   /* Libpng versions 1.0.0 and later are binary compatible if the version
-    * string matches through the second '.'; we must recompile any
-    * applications that use any older library version.
-    */
-
-   if (user_png_ver != NULL)
-   {
-      int i = -1;
-      int found_dots = 0;
-
-      do
-      {
-         i++;
-         if (user_png_ver[i] != PNG_LIBPNG_VER_STRING[i])
-            png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
-         if (user_png_ver[i] == '.')
-            found_dots++;
-      } while (found_dots < 2 && user_png_ver[i] != 0 &&
-            PNG_LIBPNG_VER_STRING[i] != 0);
-   }
-
-   else
-      png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
-
-   if ((png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) != 0)
-   {
-#ifdef PNG_WARNINGS_SUPPORTED
-      size_t pos = 0;
-      char m[128];
-
-      pos = png_safecat(m, (sizeof m), pos,
-          "Application built with libpng-");
-      pos = png_safecat(m, (sizeof m), pos, user_png_ver);
-      pos = png_safecat(m, (sizeof m), pos, " but running with ");
-      pos = png_safecat(m, (sizeof m), pos, PNG_LIBPNG_VER_STRING);
-      PNG_UNUSED(pos)
-
-      png_warning(png_ptr, m);
-#endif
-
-#ifdef PNG_ERROR_NUMBERS_SUPPORTED
-      png_ptr->flags = 0;
-#endif
-
-      return 0;
-   }
-
-   /* Success return. */
-   return 1;
-}
-
-/* Generic function to create a png_struct for either read or write - this
- * contains the common initialization.
- */
-PNG_FUNCTION(png_structp /* PRIVATE */,
-png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
-    png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
-    png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
-{
-   png_struct create_struct;
-#  ifdef PNG_SETJMP_SUPPORTED
-      jmp_buf create_jmp_buf;
-#  endif
-
-   /* This temporary stack-allocated structure is used to provide a place to
-    * build enough context to allow the user provided memory allocator (if any)
-    * to be called.
-    */
-   memset(&create_struct, 0, (sizeof create_struct));
-
-   /* Added at libpng-1.2.6 */
-#  ifdef PNG_USER_LIMITS_SUPPORTED
-      create_struct.user_width_max = PNG_USER_WIDTH_MAX;
-      create_struct.user_height_max = PNG_USER_HEIGHT_MAX;
-
-#     ifdef PNG_USER_CHUNK_CACHE_MAX
-      /* Added at libpng-1.2.43 and 1.4.0 */
-      create_struct.user_chunk_cache_max = PNG_USER_CHUNK_CACHE_MAX;
-#     endif
-
-#     ifdef PNG_USER_CHUNK_MALLOC_MAX
-      /* Added at libpng-1.2.43 and 1.4.1, required only for read but exists
-       * in png_struct regardless.
-       */
-      create_struct.user_chunk_malloc_max = PNG_USER_CHUNK_MALLOC_MAX;
-#     endif
-#  endif
-
-   /* The following two API calls simply set fields in png_struct, so it is safe
-    * to do them now even though error handling is not yet set up.
-    */
-#  ifdef PNG_USER_MEM_SUPPORTED
-      png_set_mem_fn(&create_struct, mem_ptr, malloc_fn, free_fn);
-#  else
-      PNG_UNUSED(mem_ptr)
-      PNG_UNUSED(malloc_fn)
-      PNG_UNUSED(free_fn)
-#  endif
-
-   /* (*error_fn) can return control to the caller after the error_ptr is set,
-    * this will result in a memory leak unless the error_fn does something
-    * extremely sophisticated.  The design lacks merit but is implicit in the
-    * API.
-    */
-   png_set_error_fn(&create_struct, error_ptr, error_fn, warn_fn);
-
-#  ifdef PNG_SETJMP_SUPPORTED
-      if (!setjmp(create_jmp_buf))
-#  endif
-      {
-#  ifdef PNG_SETJMP_SUPPORTED
-         /* Temporarily fake out the longjmp information until we have
-          * successfully completed this function.  This only works if we have
-          * setjmp() support compiled in, but it is safe - this stuff should
-          * never happen.
-          */
-         create_struct.jmp_buf_ptr = &create_jmp_buf;
-         create_struct.jmp_buf_size = 0; /*stack allocation*/
-         create_struct.longjmp_fn = longjmp;
-#  endif
-         /* Call the general version checker (shared with read and write code):
-          */
-         if (png_user_version_check(&create_struct, user_png_ver) != 0)
-         {
-            png_structrp png_ptr = png_voidcast(png_structrp,
-                png_malloc_warn(&create_struct, (sizeof *png_ptr)));
-
-            if (png_ptr != NULL)
-            {
-               /* png_ptr->zstream holds a back-pointer to the png_struct, so
-                * this can only be done now:
-                */
-               create_struct.zstream.zalloc = png_zalloc;
-               create_struct.zstream.zfree = png_zfree;
-               create_struct.zstream.opaque = png_ptr;
-
-#              ifdef PNG_SETJMP_SUPPORTED
-               /* Eliminate the local error handling: */
-               create_struct.jmp_buf_ptr = NULL;
-               create_struct.jmp_buf_size = 0;
-               create_struct.longjmp_fn = 0;
-#              endif
-
-               *png_ptr = create_struct;
-
-               /* This is the successful return point */
-               return png_ptr;
-            }
-         }
-      }
-
-   /* A longjmp because of a bug in the application storage allocator or a
-    * simple failure to allocate the png_struct.
-    */
-   return NULL;
-}
-
-/* Allocate the memory for an info_struct for the application. */
-PNG_FUNCTION(png_infop,PNGAPI
-png_create_info_struct,(png_const_structrp png_ptr),PNG_ALLOCATED)
-{
-   png_inforp info_ptr;
-
-   png_debug(1, "in png_create_info_struct");
-
-   if (png_ptr == NULL)
-      return NULL;
-
-   /* Use the internal API that does not (or at least should not) error out, so
-    * that this call always returns ok.  The application typically sets up the
-    * error handling *after* creating the info_struct because this is the way it
-    * has always been done in 'example.c'.
-    */
-   info_ptr = png_voidcast(png_inforp, png_malloc_base(png_ptr,
-       (sizeof *info_ptr)));
-
-   if (info_ptr != NULL)
-      memset(info_ptr, 0, (sizeof *info_ptr));
-
-   return info_ptr;
-}
-
-/* This function frees the memory associated with a single info struct.
- * Normally, one would use either png_destroy_read_struct() or
- * png_destroy_write_struct() to free an info struct, but this may be
- * useful for some applications.  From libpng 1.6.0 this function is also used
- * internally to implement the png_info release part of the 'struct' destroy
- * APIs.  This ensures that all possible approaches free the same data (all of
- * it).
- */
-void PNGAPI
-png_destroy_info_struct(png_const_structrp png_ptr, png_infopp info_ptr_ptr)
-{
-   png_inforp info_ptr = NULL;
-
-   png_debug(1, "in png_destroy_info_struct");
-
-   if (png_ptr == NULL)
-      return;
-
-   if (info_ptr_ptr != NULL)
-      info_ptr = *info_ptr_ptr;
-
-   if (info_ptr != NULL)
-   {
-      /* Do this first in case of an error below; if the app implements its own
-       * memory management this can lead to png_free calling png_error, which
-       * will abort this routine and return control to the app error handler.
-       * An infinite loop may result if it then tries to free the same info
-       * ptr.
-       */
-      *info_ptr_ptr = NULL;
-
-      png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
-      memset(info_ptr, 0, (sizeof *info_ptr));
-      png_free(png_ptr, info_ptr);
-   }
-}
-
-/* Initialize the info structure.  This is now an internal function (0.89)
- * and applications using it are urged to use png_create_info_struct()
- * instead.  Use deprecated in 1.6.0, internal use removed (used internally it
- * is just a memset).
- *
- * NOTE: it is almost inconceivable that this API is used because it bypasses
- * the user-memory mechanism and the user error handling/warning mechanisms in
- * those cases where it does anything other than a memset.
- */
-PNG_FUNCTION(void,PNGAPI
-png_info_init_3,(png_infopp ptr_ptr, size_t png_info_struct_size),
-    PNG_DEPRECATED)
-{
-   png_inforp info_ptr = *ptr_ptr;
-
-   png_debug(1, "in png_info_init_3");
-
-   if (info_ptr == NULL)
-      return;
-
-   if ((sizeof (png_info)) > png_info_struct_size)
-   {
-      *ptr_ptr = NULL;
-      /* The following line is why this API should not be used: */
-      free(info_ptr);
-      info_ptr = png_voidcast(png_inforp, png_malloc_base(NULL,
-          (sizeof *info_ptr)));
-      if (info_ptr == NULL)
-         return;
-      *ptr_ptr = info_ptr;
-   }
-
-   /* Set everything to 0 */
-   memset(info_ptr, 0, (sizeof *info_ptr));
-}
-
-/* The following API is not called internally */
-void PNGAPI
-png_data_freer(png_const_structrp png_ptr, png_inforp info_ptr,
-    int freer, png_uint_32 mask)
-{
-   png_debug(1, "in png_data_freer");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   if (freer == PNG_DESTROY_WILL_FREE_DATA)
-      info_ptr->free_me |= mask;
-
-   else if (freer == PNG_USER_WILL_FREE_DATA)
-      info_ptr->free_me &= ~mask;
-
-   else
-      png_error(png_ptr, "Unknown freer parameter in png_data_freer");
-}
-
-void PNGAPI
-png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask,
-    int num)
-{
-   png_debug(1, "in png_free_data");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-#ifdef PNG_TEXT_SUPPORTED
-   /* Free text item num or (if num == -1) all text items */
-   if (info_ptr->text != NULL &&
-       ((mask & PNG_FREE_TEXT) & info_ptr->free_me) != 0)
-   {
-      if (num != -1)
-      {
-         png_free(png_ptr, info_ptr->text[num].key);
-         info_ptr->text[num].key = NULL;
-      }
-
-      else
-      {
-         int i;
-
-         for (i = 0; i < info_ptr->num_text; i++)
-            png_free(png_ptr, info_ptr->text[i].key);
-
-         png_free(png_ptr, info_ptr->text);
-         info_ptr->text = NULL;
-         info_ptr->num_text = 0;
-         info_ptr->max_text = 0;
-      }
-   }
-#endif
-
-#ifdef PNG_tRNS_SUPPORTED
-   /* Free any tRNS entry */
-   if (((mask & PNG_FREE_TRNS) & info_ptr->free_me) != 0)
-   {
-      info_ptr->valid &= ~PNG_INFO_tRNS;
-      png_free(png_ptr, info_ptr->trans_alpha);
-      info_ptr->trans_alpha = NULL;
-      info_ptr->num_trans = 0;
-   }
-#endif
-
-#ifdef PNG_sCAL_SUPPORTED
-   /* Free any sCAL entry */
-   if (((mask & PNG_FREE_SCAL) & info_ptr->free_me) != 0)
-   {
-      png_free(png_ptr, info_ptr->scal_s_width);
-      png_free(png_ptr, info_ptr->scal_s_height);
-      info_ptr->scal_s_width = NULL;
-      info_ptr->scal_s_height = NULL;
-      info_ptr->valid &= ~PNG_INFO_sCAL;
-   }
-#endif
-
-#ifdef PNG_pCAL_SUPPORTED
-   /* Free any pCAL entry */
-   if (((mask & PNG_FREE_PCAL) & info_ptr->free_me) != 0)
-   {
-      png_free(png_ptr, info_ptr->pcal_purpose);
-      png_free(png_ptr, info_ptr->pcal_units);
-      info_ptr->pcal_purpose = NULL;
-      info_ptr->pcal_units = NULL;
-
-      if (info_ptr->pcal_params != NULL)
-         {
-            int i;
-
-            for (i = 0; i < info_ptr->pcal_nparams; i++)
-               png_free(png_ptr, info_ptr->pcal_params[i]);
-
-            png_free(png_ptr, info_ptr->pcal_params);
-            info_ptr->pcal_params = NULL;
-         }
-      info_ptr->valid &= ~PNG_INFO_pCAL;
-   }
-#endif
-
-#ifdef PNG_iCCP_SUPPORTED
-   /* Free any profile entry */
-   if (((mask & PNG_FREE_ICCP) & info_ptr->free_me) != 0)
-   {
-      png_free(png_ptr, info_ptr->iccp_name);
-      png_free(png_ptr, info_ptr->iccp_profile);
-      info_ptr->iccp_name = NULL;
-      info_ptr->iccp_profile = NULL;
-      info_ptr->valid &= ~PNG_INFO_iCCP;
-   }
-#endif
-
-#ifdef PNG_sPLT_SUPPORTED
-   /* Free a given sPLT entry, or (if num == -1) all sPLT entries */
-   if (info_ptr->splt_palettes != NULL &&
-       ((mask & PNG_FREE_SPLT) & info_ptr->free_me) != 0)
-   {
-      if (num != -1)
-      {
-         png_free(png_ptr, info_ptr->splt_palettes[num].name);
-         png_free(png_ptr, info_ptr->splt_palettes[num].entries);
-         info_ptr->splt_palettes[num].name = NULL;
-         info_ptr->splt_palettes[num].entries = NULL;
-      }
-
-      else
-      {
-         int i;
-
-         for (i = 0; i < info_ptr->splt_palettes_num; i++)
-         {
-            png_free(png_ptr, info_ptr->splt_palettes[i].name);
-            png_free(png_ptr, info_ptr->splt_palettes[i].entries);
-         }
-
-         png_free(png_ptr, info_ptr->splt_palettes);
-         info_ptr->splt_palettes = NULL;
-         info_ptr->splt_palettes_num = 0;
-         info_ptr->valid &= ~PNG_INFO_sPLT;
-      }
-   }
-#endif
-
-#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
-   if (info_ptr->unknown_chunks != NULL &&
-       ((mask & PNG_FREE_UNKN) & info_ptr->free_me) != 0)
-   {
-      if (num != -1)
-      {
-          png_free(png_ptr, info_ptr->unknown_chunks[num].data);
-          info_ptr->unknown_chunks[num].data = NULL;
-      }
-
-      else
-      {
-         int i;
-
-         for (i = 0; i < info_ptr->unknown_chunks_num; i++)
-            png_free(png_ptr, info_ptr->unknown_chunks[i].data);
-
-         png_free(png_ptr, info_ptr->unknown_chunks);
-         info_ptr->unknown_chunks = NULL;
-         info_ptr->unknown_chunks_num = 0;
-      }
-   }
-#endif
-
-#ifdef PNG_eXIf_SUPPORTED
-   /* Free any eXIf entry */
-   if (((mask & PNG_FREE_EXIF) & info_ptr->free_me) != 0)
-   {
-# ifdef PNG_READ_eXIf_SUPPORTED
-      if (info_ptr->eXIf_buf)
-      {
-         png_free(png_ptr, info_ptr->eXIf_buf);
-         info_ptr->eXIf_buf = NULL;
-      }
-# endif
-      if (info_ptr->exif)
-      {
-         png_free(png_ptr, info_ptr->exif);
-         info_ptr->exif = NULL;
-      }
-      info_ptr->valid &= ~PNG_INFO_eXIf;
-   }
-#endif
-
-#ifdef PNG_hIST_SUPPORTED
-   /* Free any hIST entry */
-   if (((mask & PNG_FREE_HIST) & info_ptr->free_me) != 0)
-   {
-      png_free(png_ptr, info_ptr->hist);
-      info_ptr->hist = NULL;
-      info_ptr->valid &= ~PNG_INFO_hIST;
-   }
-#endif
-
-   /* Free any PLTE entry that was internally allocated */
-   if (((mask & PNG_FREE_PLTE) & info_ptr->free_me) != 0)
-   {
-      png_free(png_ptr, info_ptr->palette);
-      info_ptr->palette = NULL;
-      info_ptr->valid &= ~PNG_INFO_PLTE;
-      info_ptr->num_palette = 0;
-   }
-
-#ifdef PNG_INFO_IMAGE_SUPPORTED
-   /* Free any image bits attached to the info structure */
-   if (((mask & PNG_FREE_ROWS) & info_ptr->free_me) != 0)
-   {
-      if (info_ptr->row_pointers != NULL)
-      {
-         png_uint_32 row;
-         for (row = 0; row < info_ptr->height; row++)
-            png_free(png_ptr, info_ptr->row_pointers[row]);
-
-         png_free(png_ptr, info_ptr->row_pointers);
-         info_ptr->row_pointers = NULL;
-      }
-      info_ptr->valid &= ~PNG_INFO_IDAT;
-   }
-#endif
-
-   if (num != -1)
-      mask &= ~PNG_FREE_MUL;
-
-   info_ptr->free_me &= ~mask;
-}
-#endif /* READ || WRITE */
-
-/* This function returns a pointer to the io_ptr associated with the user
- * functions.  The application should free any memory associated with this
- * pointer before png_write_destroy() or png_read_destroy() are called.
- */
-png_voidp PNGAPI
-png_get_io_ptr(png_const_structrp png_ptr)
-{
-   if (png_ptr == NULL)
-      return (NULL);
-
-   return (png_ptr->io_ptr);
-}
-
-#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
-#  ifdef PNG_STDIO_SUPPORTED
-/* Initialize the default input/output functions for the PNG file.  If you
- * use your own read or write routines, you can call either png_set_read_fn()
- * or png_set_write_fn() instead of png_init_io().  If you have defined
- * PNG_NO_STDIO or otherwise disabled PNG_STDIO_SUPPORTED, you must use a
- * function of your own because "FILE *" isn't necessarily available.
- */
-void PNGAPI
-png_init_io(png_structrp png_ptr, png_FILE_p fp)
-{
-   png_debug(1, "in png_init_io");
-
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->io_ptr = (png_voidp)fp;
-}
-#  endif
-
-#  ifdef PNG_SAVE_INT_32_SUPPORTED
-/* PNG signed integers are saved in 32-bit 2's complement format.  ANSI C-90
- * defines a cast of a signed integer to an unsigned integer either to preserve
- * the value, if it is positive, or to calculate:
- *
- *     (UNSIGNED_MAX+1) + integer
- *
- * Where UNSIGNED_MAX is the appropriate maximum unsigned value, so when the
- * negative integral value is added the result will be an unsigned value
- * correspnding to the 2's complement representation.
- */
-void PNGAPI
-png_save_int_32(png_bytep buf, png_int_32 i)
-{
-   png_save_uint_32(buf, (png_uint_32)i);
-}
-#  endif
-
-#  ifdef PNG_TIME_RFC1123_SUPPORTED
-/* Convert the supplied time into an RFC 1123 string suitable for use in
- * a "Creation Time" or other text-based time string.
- */
-int PNGAPI
-png_convert_to_rfc1123_buffer(char out[29], png_const_timep ptime)
-{
-   static const char short_months[12][4] =
-        {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
-         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
-
-   if (out == NULL)
-      return 0;
-
-   if (ptime->year > 9999 /* RFC1123 limitation */ ||
-       ptime->month == 0    ||  ptime->month > 12  ||
-       ptime->day   == 0    ||  ptime->day   > 31  ||
-       ptime->hour  > 23    ||  ptime->minute > 59 ||
-       ptime->second > 60)
-      return 0;
-
-   {
-      size_t pos = 0;
-      char number_buf[5]; /* enough for a four-digit year */
-
-#     define APPEND_STRING(string) pos = png_safecat(out, 29, pos, (string))
-#     define APPEND_NUMBER(format, value)\
-         APPEND_STRING(PNG_FORMAT_NUMBER(number_buf, format, (value)))
-#     define APPEND(ch) if (pos < 28) out[pos++] = (ch)
-
-      APPEND_NUMBER(PNG_NUMBER_FORMAT_u, (unsigned)ptime->day);
-      APPEND(' ');
-      APPEND_STRING(short_months[(ptime->month - 1)]);
-      APPEND(' ');
-      APPEND_NUMBER(PNG_NUMBER_FORMAT_u, ptime->year);
-      APPEND(' ');
-      APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->hour);
-      APPEND(':');
-      APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->minute);
-      APPEND(':');
-      APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->second);
-      APPEND_STRING(" +0000"); /* This reliably terminates the buffer */
-      PNG_UNUSED (pos)
-
-#     undef APPEND
-#     undef APPEND_NUMBER
-#     undef APPEND_STRING
-   }
-
-   return 1;
-}
-
-#    if PNG_LIBPNG_VER < 10700
-/* To do: remove the following from libpng-1.7 */
-/* Original API that uses a private buffer in png_struct.
- * Deprecated because it causes png_struct to carry a spurious temporary
- * buffer (png_struct::time_buffer), better to have the caller pass this in.
- */
-png_const_charp PNGAPI
-png_convert_to_rfc1123(png_structrp png_ptr, png_const_timep ptime)
-{
-   if (png_ptr != NULL)
-   {
-      /* The only failure above if png_ptr != NULL is from an invalid ptime */
-      if (png_convert_to_rfc1123_buffer(png_ptr->time_buffer, ptime) == 0)
-         png_warning(png_ptr, "Ignoring invalid time value");
-
-      else
-         return png_ptr->time_buffer;
-   }
-
-   return NULL;
-}
-#    endif /* LIBPNG_VER < 10700 */
-#  endif /* TIME_RFC1123 */
-
-#endif /* READ || WRITE */
-
-png_const_charp PNGAPI
-png_get_copyright(png_const_structrp png_ptr)
-{
-   PNG_UNUSED(png_ptr)  /* Silence compiler warning about unused png_ptr */
-#ifdef PNG_STRING_COPYRIGHT
-   return PNG_STRING_COPYRIGHT
-#else
-   return PNG_STRING_NEWLINE \
-      "libpng version 1.6.37" PNG_STRING_NEWLINE \
-      "Copyright (c) 2018-2019 Cosmin Truta" PNG_STRING_NEWLINE \
-      "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \
-      PNG_STRING_NEWLINE \
-      "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \
-      "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \
-      PNG_STRING_NEWLINE;
-#endif
-}
-
-/* The following return the library version as a short string in the
- * format 1.0.0 through 99.99.99zz.  To get the version of *.h files
- * used with your application, print out PNG_LIBPNG_VER_STRING, which
- * is defined in png.h.
- * Note: now there is no difference between png_get_libpng_ver() and
- * png_get_header_ver().  Due to the version_nn_nn_nn typedef guard,
- * it is guaranteed that png.c uses the correct version of png.h.
- */
-png_const_charp PNGAPI
-png_get_libpng_ver(png_const_structrp png_ptr)
-{
-   /* Version of *.c files used when building libpng */
-   return png_get_header_ver(png_ptr);
-}
-
-png_const_charp PNGAPI
-png_get_header_ver(png_const_structrp png_ptr)
-{
-   /* Version of *.h files used when building libpng */
-   PNG_UNUSED(png_ptr)  /* Silence compiler warning about unused png_ptr */
-   return PNG_LIBPNG_VER_STRING;
-}
-
-png_const_charp PNGAPI
-png_get_header_version(png_const_structrp png_ptr)
-{
-   /* Returns longer string containing both version and date */
-   PNG_UNUSED(png_ptr)  /* Silence compiler warning about unused png_ptr */
-#ifdef __STDC__
-   return PNG_HEADER_VERSION_STRING
-#  ifndef PNG_READ_SUPPORTED
-      " (NO READ SUPPORT)"
-#  endif
-      PNG_STRING_NEWLINE;
-#else
-   return PNG_HEADER_VERSION_STRING;
-#endif
-}
-
-#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED
-/* NOTE: this routine is not used internally! */
-/* Build a grayscale palette.  Palette is assumed to be 1 << bit_depth
- * large of png_color.  This lets grayscale images be treated as
- * paletted.  Most useful for gamma correction and simplification
- * of code.  This API is not used internally.
- */
-void PNGAPI
-png_build_grayscale_palette(int bit_depth, png_colorp palette)
-{
-   int num_palette;
-   int color_inc;
-   int i;
-   int v;
-
-   png_debug(1, "in png_do_build_grayscale_palette");
-
-   if (palette == NULL)
-      return;
-
-   switch (bit_depth)
-   {
-      case 1:
-         num_palette = 2;
-         color_inc = 0xff;
-         break;
-
-      case 2:
-         num_palette = 4;
-         color_inc = 0x55;
-         break;
-
-      case 4:
-         num_palette = 16;
-         color_inc = 0x11;
-         break;
-
-      case 8:
-         num_palette = 256;
-         color_inc = 1;
-         break;
-
-      default:
-         num_palette = 0;
-         color_inc = 0;
-         break;
-   }
-
-   for (i = 0, v = 0; i < num_palette; i++, v += color_inc)
-   {
-      palette[i].red = (png_byte)(v & 0xff);
-      palette[i].green = (png_byte)(v & 0xff);
-      palette[i].blue = (png_byte)(v & 0xff);
-   }
-}
-#endif
-
-#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
-int PNGAPI
-png_handle_as_unknown(png_const_structrp png_ptr, png_const_bytep chunk_name)
-{
-   /* Check chunk_name and return "keep" value if it's on the list, else 0 */
-   png_const_bytep p, p_end;
-
-   if (png_ptr == NULL || chunk_name == NULL || png_ptr->num_chunk_list == 0)
-      return PNG_HANDLE_CHUNK_AS_DEFAULT;
-
-   p_end = png_ptr->chunk_list;
-   p = p_end + png_ptr->num_chunk_list*5; /* beyond end */
-
-   /* The code is the fifth byte after each four byte string.  Historically this
-    * code was always searched from the end of the list, this is no longer
-    * necessary because the 'set' routine handles duplicate entries correctly.
-    */
-   do /* num_chunk_list > 0, so at least one */
-   {
-      p -= 5;
-
-      if (memcmp(chunk_name, p, 4) == 0)
-         return p[4];
-   }
-   while (p > p_end);
-
-   /* This means that known chunks should be processed and unknown chunks should
-    * be handled according to the value of png_ptr->unknown_default; this can be
-    * confusing because, as a result, there are two levels of defaulting for
-    * unknown chunks.
-    */
-   return PNG_HANDLE_CHUNK_AS_DEFAULT;
-}
-
-#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) ||\
-   defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED)
-int /* PRIVATE */
-png_chunk_unknown_handling(png_const_structrp png_ptr, png_uint_32 chunk_name)
-{
-   png_byte chunk_string[5];
-
-   PNG_CSTRING_FROM_CHUNK(chunk_string, chunk_name);
-   return png_handle_as_unknown(png_ptr, chunk_string);
-}
-#endif /* READ_UNKNOWN_CHUNKS || HANDLE_AS_UNKNOWN */
-#endif /* SET_UNKNOWN_CHUNKS */
-
-#ifdef PNG_READ_SUPPORTED
-/* This function, added to libpng-1.0.6g, is untested. */
-int PNGAPI
-png_reset_zstream(png_structrp png_ptr)
-{
-   if (png_ptr == NULL)
-      return Z_STREAM_ERROR;
-
-   /* WARNING: this resets the window bits to the maximum! */
-   return (inflateReset(&png_ptr->zstream));
-}
-#endif /* READ */
-
-/* This function was added to libpng-1.0.7 */
-png_uint_32 PNGAPI
-png_access_version_number(void)
-{
-   /* Version of *.c files used when building libpng */
-   return((png_uint_32)PNG_LIBPNG_VER);
-}
-
-#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
-/* Ensure that png_ptr->zstream.msg holds some appropriate error message string.
- * If it doesn't 'ret' is used to set it to something appropriate, even in cases
- * like Z_OK or Z_STREAM_END where the error code is apparently a success code.
- */
-void /* PRIVATE */
-png_zstream_error(png_structrp png_ptr, int ret)
-{
-   /* Translate 'ret' into an appropriate error string, priority is given to the
-    * one in zstream if set.  This always returns a string, even in cases like
-    * Z_OK or Z_STREAM_END where the error code is a success code.
-    */
-   if (png_ptr->zstream.msg == NULL) switch (ret)
-   {
-      default:
-      case Z_OK:
-         png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return code");
-         break;
-
-      case Z_STREAM_END:
-         /* Normal exit */
-         png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected end of LZ stream");
-         break;
-
-      case Z_NEED_DICT:
-         /* This means the deflate stream did not have a dictionary; this
-          * indicates a bogus PNG.
-          */
-         png_ptr->zstream.msg = PNGZ_MSG_CAST("missing LZ dictionary");
-         break;
-
-      case Z_ERRNO:
-         /* gz APIs only: should not happen */
-         png_ptr->zstream.msg = PNGZ_MSG_CAST("zlib IO error");
-         break;
-
-      case Z_STREAM_ERROR:
-         /* internal libpng error */
-         png_ptr->zstream.msg = PNGZ_MSG_CAST("bad parameters to zlib");
-         break;
-
-      case Z_DATA_ERROR:
-         png_ptr->zstream.msg = PNGZ_MSG_CAST("damaged LZ stream");
-         break;
-
-      case Z_MEM_ERROR:
-         png_ptr->zstream.msg = PNGZ_MSG_CAST("insufficient memory");
-         break;
-
-      case Z_BUF_ERROR:
-         /* End of input or output; not a problem if the caller is doing
-          * incremental read or write.
-          */
-         png_ptr->zstream.msg = PNGZ_MSG_CAST("truncated");
-         break;
-
-      case Z_VERSION_ERROR:
-         png_ptr->zstream.msg = PNGZ_MSG_CAST("unsupported zlib version");
-         break;
-
-      case PNG_UNEXPECTED_ZLIB_RETURN:
-         /* Compile errors here mean that zlib now uses the value co-opted in
-          * pngpriv.h for PNG_UNEXPECTED_ZLIB_RETURN; update the switch above
-          * and change pngpriv.h.  Note that this message is "... return",
-          * whereas the default/Z_OK one is "... return code".
-          */
-         png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return");
-         break;
-   }
-}
-
-/* png_convert_size: a PNGAPI but no longer in png.h, so deleted
- * at libpng 1.5.5!
- */
-
-/* Added at libpng version 1.2.34 and 1.4.0 (moved from pngset.c) */
-#ifdef PNG_GAMMA_SUPPORTED /* always set if COLORSPACE */
-static int
-png_colorspace_check_gamma(png_const_structrp png_ptr,
-    png_colorspacerp colorspace, png_fixed_point gAMA, int from)
-   /* This is called to check a new gamma value against an existing one.  The
-    * routine returns false if the new gamma value should not be written.
-    *
-    * 'from' says where the new gamma value comes from:
-    *
-    *    0: the new gamma value is the libpng estimate for an ICC profile
-    *    1: the new gamma value comes from a gAMA chunk
-    *    2: the new gamma value comes from an sRGB chunk
-    */
-{
-   png_fixed_point gtest;
-
-   if ((colorspace->flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 &&
-       (png_muldiv(&gtest, colorspace->gamma, PNG_FP_1, gAMA) == 0  ||
-      png_gamma_significant(gtest) != 0))
-   {
-      /* Either this is an sRGB image, in which case the calculated gamma
-       * approximation should match, or this is an image with a profile and the
-       * value libpng calculates for the gamma of the profile does not match the
-       * value recorded in the file.  The former, sRGB, case is an error, the
-       * latter is just a warning.
-       */
-      if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0 || from == 2)
-      {
-         png_chunk_report(png_ptr, "gamma value does not match sRGB",
-             PNG_CHUNK_ERROR);
-         /* Do not overwrite an sRGB value */
-         return from == 2;
-      }
-
-      else /* sRGB tag not involved */
-      {
-         png_chunk_report(png_ptr, "gamma value does not match libpng estimate",
-             PNG_CHUNK_WARNING);
-         return from == 1;
-      }
-   }
-
-   return 1;
-}
-
-void /* PRIVATE */
-png_colorspace_set_gamma(png_const_structrp png_ptr,
-    png_colorspacerp colorspace, png_fixed_point gAMA)
-{
-   /* Changed in libpng-1.5.4 to limit the values to ensure overflow can't
-    * occur.  Since the fixed point representation is asymmetrical it is
-    * possible for 1/gamma to overflow the limit of 21474 and this means the
-    * gamma value must be at least 5/100000 and hence at most 20000.0.  For
-    * safety the limits here are a little narrower.  The values are 0.00016 to
-    * 6250.0, which are truly ridiculous gamma values (and will produce
-    * displays that are all black or all white.)
-    *
-    * In 1.6.0 this test replaces the ones in pngrutil.c, in the gAMA chunk
-    * handling code, which only required the value to be >0.
-    */
-   png_const_charp errmsg;
-
-   if (gAMA < 16 || gAMA > 625000000)
-      errmsg = "gamma value out of range";
-
-#  ifdef PNG_READ_gAMA_SUPPORTED
-   /* Allow the application to set the gamma value more than once */
-   else if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
-      (colorspace->flags & PNG_COLORSPACE_FROM_gAMA) != 0)
-      errmsg = "duplicate";
-#  endif
-
-   /* Do nothing if the colorspace is already invalid */
-   else if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
-      return;
-
-   else
-   {
-      if (png_colorspace_check_gamma(png_ptr, colorspace, gAMA,
-          1/*from gAMA*/) != 0)
-      {
-         /* Store this gamma value. */
-         colorspace->gamma = gAMA;
-         colorspace->flags |=
-            (PNG_COLORSPACE_HAVE_GAMMA | PNG_COLORSPACE_FROM_gAMA);
-      }
-
-      /* At present if the check_gamma test fails the gamma of the colorspace is
-       * not updated however the colorspace is not invalidated.  This
-       * corresponds to the case where the existing gamma comes from an sRGB
-       * chunk or profile.  An error message has already been output.
-       */
-      return;
-   }
-
-   /* Error exit - errmsg has been set. */
-   colorspace->flags |= PNG_COLORSPACE_INVALID;
-   png_chunk_report(png_ptr, errmsg, PNG_CHUNK_WRITE_ERROR);
-}
-
-void /* PRIVATE */
-png_colorspace_sync_info(png_const_structrp png_ptr, png_inforp info_ptr)
-{
-   if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0)
-   {
-      /* Everything is invalid */
-      info_ptr->valid &= ~(PNG_INFO_gAMA|PNG_INFO_cHRM|PNG_INFO_sRGB|
-         PNG_INFO_iCCP);
-
-#     ifdef PNG_COLORSPACE_SUPPORTED
-      /* Clean up the iCCP profile now if it won't be used. */
-      png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, -1/*not used*/);
-#     else
-      PNG_UNUSED(png_ptr)
-#     endif
-   }
-
-   else
-   {
-#     ifdef PNG_COLORSPACE_SUPPORTED
-      /* Leave the INFO_iCCP flag set if the pngset.c code has already set
-       * it; this allows a PNG to contain a profile which matches sRGB and
-       * yet still have that profile retrievable by the application.
-       */
-      if ((info_ptr->colorspace.flags & PNG_COLORSPACE_MATCHES_sRGB) != 0)
-         info_ptr->valid |= PNG_INFO_sRGB;
-
-      else
-         info_ptr->valid &= ~PNG_INFO_sRGB;
-
-      if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
-         info_ptr->valid |= PNG_INFO_cHRM;
-
-      else
-         info_ptr->valid &= ~PNG_INFO_cHRM;
-#     endif
-
-      if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0)
-         info_ptr->valid |= PNG_INFO_gAMA;
-
-      else
-         info_ptr->valid &= ~PNG_INFO_gAMA;
-   }
-}
-
-#ifdef PNG_READ_SUPPORTED
-void /* PRIVATE */
-png_colorspace_sync(png_const_structrp png_ptr, png_inforp info_ptr)
-{
-   if (info_ptr == NULL) /* reduce code size; check here not in the caller */
-      return;
-
-   info_ptr->colorspace = png_ptr->colorspace;
-   png_colorspace_sync_info(png_ptr, info_ptr);
-}
-#endif
-#endif /* GAMMA */
-
-#ifdef PNG_COLORSPACE_SUPPORTED
-/* Added at libpng-1.5.5 to support read and write of true CIEXYZ values for
- * cHRM, as opposed to using chromaticities.  These internal APIs return
- * non-zero on a parameter error.  The X, Y and Z values are required to be
- * positive and less than 1.0.
- */
-static int
-png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ)
-{
-   png_int_32 d, dwhite, whiteX, whiteY;
-
-   d = XYZ->red_X + XYZ->red_Y + XYZ->red_Z;
-   if (png_muldiv(&xy->redx, XYZ->red_X, PNG_FP_1, d) == 0)
-      return 1;
-   if (png_muldiv(&xy->redy, XYZ->red_Y, PNG_FP_1, d) == 0)
-      return 1;
-   dwhite = d;
-   whiteX = XYZ->red_X;
-   whiteY = XYZ->red_Y;
-
-   d = XYZ->green_X + XYZ->green_Y + XYZ->green_Z;
-   if (png_muldiv(&xy->greenx, XYZ->green_X, PNG_FP_1, d) == 0)
-      return 1;
-   if (png_muldiv(&xy->greeny, XYZ->green_Y, PNG_FP_1, d) == 0)
-      return 1;
-   dwhite += d;
-   whiteX += XYZ->green_X;
-   whiteY += XYZ->green_Y;
-
-   d = XYZ->blue_X + XYZ->blue_Y + XYZ->blue_Z;
-   if (png_muldiv(&xy->bluex, XYZ->blue_X, PNG_FP_1, d) == 0)
-      return 1;
-   if (png_muldiv(&xy->bluey, XYZ->blue_Y, PNG_FP_1, d) == 0)
-      return 1;
-   dwhite += d;
-   whiteX += XYZ->blue_X;
-   whiteY += XYZ->blue_Y;
-
-   /* The reference white is simply the sum of the end-point (X,Y,Z) vectors,
-    * thus:
-    */
-   if (png_muldiv(&xy->whitex, whiteX, PNG_FP_1, dwhite) == 0)
-      return 1;
-   if (png_muldiv(&xy->whitey, whiteY, PNG_FP_1, dwhite) == 0)
-      return 1;
-
-   return 0;
-}
-
-static int
-png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy)
-{
-   png_fixed_point red_inverse, green_inverse, blue_scale;
-   png_fixed_point left, right, denominator;
-
-   /* Check xy and, implicitly, z.  Note that wide gamut color spaces typically
-    * have end points with 0 tristimulus values (these are impossible end
-    * points, but they are used to cover the possible colors).  We check
-    * xy->whitey against 5, not 0, to avoid a possible integer overflow.
-    */
-   if (xy->redx   < 0 || xy->redx > PNG_FP_1) return 1;
-   if (xy->redy   < 0 || xy->redy > PNG_FP_1-xy->redx) return 1;
-   if (xy->greenx < 0 || xy->greenx > PNG_FP_1) return 1;
-   if (xy->greeny < 0 || xy->greeny > PNG_FP_1-xy->greenx) return 1;
-   if (xy->bluex  < 0 || xy->bluex > PNG_FP_1) return 1;
-   if (xy->bluey  < 0 || xy->bluey > PNG_FP_1-xy->bluex) return 1;
-   if (xy->whitex < 0 || xy->whitex > PNG_FP_1) return 1;
-   if (xy->whitey < 5 || xy->whitey > PNG_FP_1-xy->whitex) return 1;
-
-   /* The reverse calculation is more difficult because the original tristimulus
-    * value had 9 independent values (red,green,blue)x(X,Y,Z) however only 8
-    * derived values were recorded in the cHRM chunk;
-    * (red,green,blue,white)x(x,y).  This loses one degree of freedom and
-    * therefore an arbitrary ninth value has to be introduced to undo the
-    * original transformations.
-    *
-    * Think of the original end-points as points in (X,Y,Z) space.  The
-    * chromaticity values (c) have the property:
-    *
-    *           C
-    *   c = ---------
-    *       X + Y + Z
-    *
-    * For each c (x,y,z) from the corresponding original C (X,Y,Z).  Thus the
-    * three chromaticity values (x,y,z) for each end-point obey the
-    * relationship:
-    *
-    *   x + y + z = 1
-    *
-    * This describes the plane in (X,Y,Z) space that intersects each axis at the
-    * value 1.0; call this the chromaticity plane.  Thus the chromaticity
-    * calculation has scaled each end-point so that it is on the x+y+z=1 plane
-    * and chromaticity is the intersection of the vector from the origin to the
-    * (X,Y,Z) value with the chromaticity plane.
-    *
-    * To fully invert the chromaticity calculation we would need the three
-    * end-point scale factors, (red-scale, green-scale, blue-scale), but these
-    * were not recorded.  Instead we calculated the reference white (X,Y,Z) and
-    * recorded the chromaticity of this.  The reference white (X,Y,Z) would have
-    * given all three of the scale factors since:
-    *
-    *    color-C = color-c * color-scale
-    *    white-C = red-C + green-C + blue-C
-    *            = red-c*red-scale + green-c*green-scale + blue-c*blue-scale
-    *
-    * But cHRM records only white-x and white-y, so we have lost the white scale
-    * factor:
-    *
-    *    white-C = white-c*white-scale
-    *
-    * To handle this the inverse transformation makes an arbitrary assumption
-    * about white-scale:
-    *
-    *    Assume: white-Y = 1.0
-    *    Hence:  white-scale = 1/white-y
-    *    Or:     red-Y + green-Y + blue-Y = 1.0
-    *
-    * Notice the last statement of the assumption gives an equation in three of
-    * the nine values we want to calculate.  8 more equations come from the
-    * above routine as summarised at the top above (the chromaticity
-    * calculation):
-    *
-    *    Given: color-x = color-X / (color-X + color-Y + color-Z)
-    *    Hence: (color-x - 1)*color-X + color.x*color-Y + color.x*color-Z = 0
-    *
-    * This is 9 simultaneous equations in the 9 variables "color-C" and can be
-    * solved by Cramer's rule.  Cramer's rule requires calculating 10 9x9 matrix
-    * determinants, however this is not as bad as it seems because only 28 of
-    * the total of 90 terms in the various matrices are non-zero.  Nevertheless
-    * Cramer's rule is notoriously numerically unstable because the determinant
-    * calculation involves the difference of large, but similar, numbers.  It is
-    * difficult to be sure that the calculation is stable for real world values
-    * and it is certain that it becomes unstable where the end points are close
-    * together.
-    *
-    * So this code uses the perhaps slightly less optimal but more
-    * understandable and totally obvious approach of calculating color-scale.
-    *
-    * This algorithm depends on the precision in white-scale and that is
-    * (1/white-y), so we can immediately see that as white-y approaches 0 the
-    * accuracy inherent in the cHRM chunk drops off substantially.
-    *
-    * libpng arithmetic: a simple inversion of the above equations
-    * ------------------------------------------------------------
-    *
-    *    white_scale = 1/white-y
-    *    white-X = white-x * white-scale
-    *    white-Y = 1.0
-    *    white-Z = (1 - white-x - white-y) * white_scale
-    *
-    *    white-C = red-C + green-C + blue-C
-    *            = red-c*red-scale + green-c*green-scale + blue-c*blue-scale
-    *
-    * This gives us three equations in (red-scale,green-scale,blue-scale) where
-    * all the coefficients are now known:
-    *
-    *    red-x*red-scale + green-x*green-scale + blue-x*blue-scale
-    *       = white-x/white-y
-    *    red-y*red-scale + green-y*green-scale + blue-y*blue-scale = 1
-    *    red-z*red-scale + green-z*green-scale + blue-z*blue-scale
-    *       = (1 - white-x - white-y)/white-y
-    *
-    * In the last equation color-z is (1 - color-x - color-y) so we can add all
-    * three equations together to get an alternative third:
-    *
-    *    red-scale + green-scale + blue-scale = 1/white-y = white-scale
-    *
-    * So now we have a Cramer's rule solution where the determinants are just
-    * 3x3 - far more tractible.  Unfortunately 3x3 determinants still involve
-    * multiplication of three coefficients so we can't guarantee to avoid
-    * overflow in the libpng fixed point representation.  Using Cramer's rule in
-    * floating point is probably a good choice here, but it's not an option for
-    * fixed point.  Instead proceed to simplify the first two equations by
-    * eliminating what is likely to be the largest value, blue-scale:
-    *
-    *    blue-scale = white-scale - red-scale - green-scale
-    *
-    * Hence:
-    *
-    *    (red-x - blue-x)*red-scale + (green-x - blue-x)*green-scale =
-    *                (white-x - blue-x)*white-scale
-    *
-    *    (red-y - blue-y)*red-scale + (green-y - blue-y)*green-scale =
-    *                1 - blue-y*white-scale
-    *
-    * And now we can trivially solve for (red-scale,green-scale):
-    *
-    *    green-scale =
-    *                (white-x - blue-x)*white-scale - (red-x - blue-x)*red-scale
-    *                -----------------------------------------------------------
-    *                                  green-x - blue-x
-    *
-    *    red-scale =
-    *                1 - blue-y*white-scale - (green-y - blue-y) * green-scale
-    *                ---------------------------------------------------------
-    *                                  red-y - blue-y
-    *
-    * Hence:
-    *
-    *    red-scale =
-    *          ( (green-x - blue-x) * (white-y - blue-y) -
-    *            (green-y - blue-y) * (white-x - blue-x) ) / white-y
-    * -------------------------------------------------------------------------
-    *  (green-x - blue-x)*(red-y - blue-y)-(green-y - blue-y)*(red-x - blue-x)
-    *
-    *    green-scale =
-    *          ( (red-y - blue-y) * (white-x - blue-x) -
-    *            (red-x - blue-x) * (white-y - blue-y) ) / white-y
-    * -------------------------------------------------------------------------
-    *  (green-x - blue-x)*(red-y - blue-y)-(green-y - blue-y)*(red-x - blue-x)
-    *
-    * Accuracy:
-    * The input values have 5 decimal digits of accuracy.  The values are all in
-    * the range 0 < value < 1, so simple products are in the same range but may
-    * need up to 10 decimal digits to preserve the original precision and avoid
-    * underflow.  Because we are using a 32-bit signed representation we cannot
-    * match this; the best is a little over 9 decimal digits, less than 10.
-    *
-    * The approach used here is to preserve the maximum precision within the
-    * signed representation.  Because the red-scale calculation above uses the
-    * difference between two products of values that must be in the range -1..+1
-    * it is sufficient to divide the product by 7; ceil(100,000/32767*2).  The
-    * factor is irrelevant in the calculation because it is applied to both
-    * numerator and denominator.
-    *
-    * Note that the values of the differences of the products of the
-    * chromaticities in the above equations tend to be small, for example for
-    * the sRGB chromaticities they are:
-    *
-    * red numerator:    -0.04751
-    * green numerator:  -0.08788
-    * denominator:      -0.2241 (without white-y multiplication)
-    *
-    *  The resultant Y coefficients from the chromaticities of some widely used
-    *  color space definitions are (to 15 decimal places):
-    *
-    *  sRGB
-    *    0.212639005871510 0.715168678767756 0.072192315360734
-    *  Kodak ProPhoto
-    *    0.288071128229293 0.711843217810102 0.000085653960605
-    *  Adobe RGB
-    *    0.297344975250536 0.627363566255466 0.075291458493998
-    *  Adobe Wide Gamut RGB
-    *    0.258728243040113 0.724682314948566 0.016589442011321
-    */
-   /* By the argument, above overflow should be impossible here. The return
-    * value of 2 indicates an internal error to the caller.
-    */
-   if (png_muldiv(&left, xy->greenx-xy->bluex, xy->redy - xy->bluey, 7) == 0)
-      return 2;
-   if (png_muldiv(&right, xy->greeny-xy->bluey, xy->redx - xy->bluex, 7) == 0)
-      return 2;
-   denominator = left - right;
-
-   /* Now find the red numerator. */
-   if (png_muldiv(&left, xy->greenx-xy->bluex, xy->whitey-xy->bluey, 7) == 0)
-      return 2;
-   if (png_muldiv(&right, xy->greeny-xy->bluey, xy->whitex-xy->bluex, 7) == 0)
-      return 2;
-
-   /* Overflow is possible here and it indicates an extreme set of PNG cHRM
-    * chunk values.  This calculation actually returns the reciprocal of the
-    * scale value because this allows us to delay the multiplication of white-y
-    * into the denominator, which tends to produce a small number.
-    */
-   if (png_muldiv(&red_inverse, xy->whitey, denominator, left-right) == 0 ||
-       red_inverse <= xy->whitey /* r+g+b scales = white scale */)
-      return 1;
-
-   /* Similarly for green_inverse: */
-   if (png_muldiv(&left, xy->redy-xy->bluey, xy->whitex-xy->bluex, 7) == 0)
-      return 2;
-   if (png_muldiv(&right, xy->redx-xy->bluex, xy->whitey-xy->bluey, 7) == 0)
-      return 2;
-   if (png_muldiv(&green_inverse, xy->whitey, denominator, left-right) == 0 ||
-       green_inverse <= xy->whitey)
-      return 1;
-
-   /* And the blue scale, the checks above guarantee this can't overflow but it
-    * can still produce 0 for extreme cHRM values.
-    */
-   blue_scale = png_reciprocal(xy->whitey) - png_reciprocal(red_inverse) -
-       png_reciprocal(green_inverse);
-   if (blue_scale <= 0)
-      return 1;
-
-
-   /* And fill in the png_XYZ: */
-   if (png_muldiv(&XYZ->red_X, xy->redx, PNG_FP_1, red_inverse) == 0)
-      return 1;
-   if (png_muldiv(&XYZ->red_Y, xy->redy, PNG_FP_1, red_inverse) == 0)
-      return 1;
-   if (png_muldiv(&XYZ->red_Z, PNG_FP_1 - xy->redx - xy->redy, PNG_FP_1,
-       red_inverse) == 0)
-      return 1;
-
-   if (png_muldiv(&XYZ->green_X, xy->greenx, PNG_FP_1, green_inverse) == 0)
-      return 1;
-   if (png_muldiv(&XYZ->green_Y, xy->greeny, PNG_FP_1, green_inverse) == 0)
-      return 1;
-   if (png_muldiv(&XYZ->green_Z, PNG_FP_1 - xy->greenx - xy->greeny, PNG_FP_1,
-       green_inverse) == 0)
-      return 1;
-
-   if (png_muldiv(&XYZ->blue_X, xy->bluex, blue_scale, PNG_FP_1) == 0)
-      return 1;
-   if (png_muldiv(&XYZ->blue_Y, xy->bluey, blue_scale, PNG_FP_1) == 0)
-      return 1;
-   if (png_muldiv(&XYZ->blue_Z, PNG_FP_1 - xy->bluex - xy->bluey, blue_scale,
-       PNG_FP_1) == 0)
-      return 1;
-
-   return 0; /*success*/
-}
-
-static int
-png_XYZ_normalize(png_XYZ *XYZ)
-{
-   png_int_32 Y;
-
-   if (XYZ->red_Y < 0 || XYZ->green_Y < 0 || XYZ->blue_Y < 0 ||
-      XYZ->red_X < 0 || XYZ->green_X < 0 || XYZ->blue_X < 0 ||
-      XYZ->red_Z < 0 || XYZ->green_Z < 0 || XYZ->blue_Z < 0)
-      return 1;
-
-   /* Normalize by scaling so the sum of the end-point Y values is PNG_FP_1.
-    * IMPLEMENTATION NOTE: ANSI requires signed overflow not to occur, therefore
-    * relying on addition of two positive values producing a negative one is not
-    * safe.
-    */
-   Y = XYZ->red_Y;
-   if (0x7fffffff - Y < XYZ->green_X)
-      return 1;
-   Y += XYZ->green_Y;
-   if (0x7fffffff - Y < XYZ->blue_X)
-      return 1;
-   Y += XYZ->blue_Y;
-
-   if (Y != PNG_FP_1)
-   {
-      if (png_muldiv(&XYZ->red_X, XYZ->red_X, PNG_FP_1, Y) == 0)
-         return 1;
-      if (png_muldiv(&XYZ->red_Y, XYZ->red_Y, PNG_FP_1, Y) == 0)
-         return 1;
-      if (png_muldiv(&XYZ->red_Z, XYZ->red_Z, PNG_FP_1, Y) == 0)
-         return 1;
-
-      if (png_muldiv(&XYZ->green_X, XYZ->green_X, PNG_FP_1, Y) == 0)
-         return 1;
-      if (png_muldiv(&XYZ->green_Y, XYZ->green_Y, PNG_FP_1, Y) == 0)
-         return 1;
-      if (png_muldiv(&XYZ->green_Z, XYZ->green_Z, PNG_FP_1, Y) == 0)
-         return 1;
-
-      if (png_muldiv(&XYZ->blue_X, XYZ->blue_X, PNG_FP_1, Y) == 0)
-         return 1;
-      if (png_muldiv(&XYZ->blue_Y, XYZ->blue_Y, PNG_FP_1, Y) == 0)
-         return 1;
-      if (png_muldiv(&XYZ->blue_Z, XYZ->blue_Z, PNG_FP_1, Y) == 0)
-         return 1;
-   }
-
-   return 0;
-}
-
-static int
-png_colorspace_endpoints_match(const png_xy *xy1, const png_xy *xy2, int delta)
-{
-   /* Allow an error of +/-0.01 (absolute value) on each chromaticity */
-   if (PNG_OUT_OF_RANGE(xy1->whitex, xy2->whitex,delta) ||
-       PNG_OUT_OF_RANGE(xy1->whitey, xy2->whitey,delta) ||
-       PNG_OUT_OF_RANGE(xy1->redx,   xy2->redx,  delta) ||
-       PNG_OUT_OF_RANGE(xy1->redy,   xy2->redy,  delta) ||
-       PNG_OUT_OF_RANGE(xy1->greenx, xy2->greenx,delta) ||
-       PNG_OUT_OF_RANGE(xy1->greeny, xy2->greeny,delta) ||
-       PNG_OUT_OF_RANGE(xy1->bluex,  xy2->bluex, delta) ||
-       PNG_OUT_OF_RANGE(xy1->bluey,  xy2->bluey, delta))
-      return 0;
-   return 1;
-}
-
-/* Added in libpng-1.6.0, a different check for the validity of a set of cHRM
- * chunk chromaticities.  Earlier checks used to simply look for the overflow
- * condition (where the determinant of the matrix to solve for XYZ ends up zero
- * because the chromaticity values are not all distinct.)  Despite this it is
- * theoretically possible to produce chromaticities that are apparently valid
- * but that rapidly degrade to invalid, potentially crashing, sets because of
- * arithmetic inaccuracies when calculations are performed on them.  The new
- * check is to round-trip xy -> XYZ -> xy and then check that the result is
- * within a small percentage of the original.
- */
-static int
-png_colorspace_check_xy(png_XYZ *XYZ, const png_xy *xy)
-{
-   int result;
-   png_xy xy_test;
-
-   /* As a side-effect this routine also returns the XYZ endpoints. */
-   result = png_XYZ_from_xy(XYZ, xy);
-   if (result != 0)
-      return result;
-
-   result = png_xy_from_XYZ(&xy_test, XYZ);
-   if (result != 0)
-      return result;
-
-   if (png_colorspace_endpoints_match(xy, &xy_test,
-       5/*actually, the math is pretty accurate*/) != 0)
-      return 0;
-
-   /* Too much slip */
-   return 1;
-}
-
-/* This is the check going the other way.  The XYZ is modified to normalize it
- * (another side-effect) and the xy chromaticities are returned.
- */
-static int
-png_colorspace_check_XYZ(png_xy *xy, png_XYZ *XYZ)
-{
-   int result;
-   png_XYZ XYZtemp;
-
-   result = png_XYZ_normalize(XYZ);
-   if (result != 0)
-      return result;
-
-   result = png_xy_from_XYZ(xy, XYZ);
-   if (result != 0)
-      return result;
-
-   XYZtemp = *XYZ;
-   return png_colorspace_check_xy(&XYZtemp, xy);
-}
-
-/* Used to check for an endpoint match against sRGB */
-static const png_xy sRGB_xy = /* From ITU-R BT.709-3 */
-{
-   /* color      x       y */
-   /* red   */ 64000, 33000,
-   /* green */ 30000, 60000,
-   /* blue  */ 15000,  6000,
-   /* white */ 31270, 32900
-};
-
-static int
-png_colorspace_set_xy_and_XYZ(png_const_structrp png_ptr,
-    png_colorspacerp colorspace, const png_xy *xy, const png_XYZ *XYZ,
-    int preferred)
-{
-   if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
-      return 0;
-
-   /* The consistency check is performed on the chromaticities; this factors out
-    * variations because of the normalization (or not) of the end point Y
-    * values.
-    */
-   if (preferred < 2 &&
-       (colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
-   {
-      /* The end points must be reasonably close to any we already have.  The
-       * following allows an error of up to +/-.001
-       */
-      if (png_colorspace_endpoints_match(xy, &colorspace->end_points_xy,
-          100) == 0)
-      {
-         colorspace->flags |= PNG_COLORSPACE_INVALID;
-         png_benign_error(png_ptr, "inconsistent chromaticities");
-         return 0; /* failed */
-      }
-
-      /* Only overwrite with preferred values */
-      if (preferred == 0)
-         return 1; /* ok, but no change */
-   }
-
-   colorspace->end_points_xy = *xy;
-   colorspace->end_points_XYZ = *XYZ;
-   colorspace->flags |= PNG_COLORSPACE_HAVE_ENDPOINTS;
-
-   /* The end points are normally quoted to two decimal digits, so allow +/-0.01
-    * on this test.
-    */
-   if (png_colorspace_endpoints_match(xy, &sRGB_xy, 1000) != 0)
-      colorspace->flags |= PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB;
-
-   else
-      colorspace->flags &= PNG_COLORSPACE_CANCEL(
-         PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB);
-
-   return 2; /* ok and changed */
-}
-
-int /* PRIVATE */
-png_colorspace_set_chromaticities(png_const_structrp png_ptr,
-    png_colorspacerp colorspace, const png_xy *xy, int preferred)
-{
-   /* We must check the end points to ensure they are reasonable - in the past
-    * color management systems have crashed as a result of getting bogus
-    * colorant values, while this isn't the fault of libpng it is the
-    * responsibility of libpng because PNG carries the bomb and libpng is in a
-    * position to protect against it.
-    */
-   png_XYZ XYZ;
-
-   switch (png_colorspace_check_xy(&XYZ, xy))
-   {
-      case 0: /* success */
-         return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, xy, &XYZ,
-             preferred);
-
-      case 1:
-         /* We can't invert the chromaticities so we can't produce value XYZ
-          * values.  Likely as not a color management system will fail too.
-          */
-         colorspace->flags |= PNG_COLORSPACE_INVALID;
-         png_benign_error(png_ptr, "invalid chromaticities");
-         break;
-
-      default:
-         /* libpng is broken; this should be a warning but if it happens we
-          * want error reports so for the moment it is an error.
-          */
-         colorspace->flags |= PNG_COLORSPACE_INVALID;
-         png_error(png_ptr, "internal error checking chromaticities");
-   }
-
-   return 0; /* failed */
-}
-
-int /* PRIVATE */
-png_colorspace_set_endpoints(png_const_structrp png_ptr,
-    png_colorspacerp colorspace, const png_XYZ *XYZ_in, int preferred)
-{
-   png_XYZ XYZ = *XYZ_in;
-   png_xy xy;
-
-   switch (png_colorspace_check_XYZ(&xy, &XYZ))
-   {
-      case 0:
-         return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, &xy, &XYZ,
-             preferred);
-
-      case 1:
-         /* End points are invalid. */
-         colorspace->flags |= PNG_COLORSPACE_INVALID;
-         png_benign_error(png_ptr, "invalid end points");
-         break;
-
-      default:
-         colorspace->flags |= PNG_COLORSPACE_INVALID;
-         png_error(png_ptr, "internal error checking chromaticities");
-   }
-
-   return 0; /* failed */
-}
-
-#if defined(PNG_sRGB_SUPPORTED) || defined(PNG_iCCP_SUPPORTED)
-/* Error message generation */
-static char
-png_icc_tag_char(png_uint_32 byte)
-{
-   byte &= 0xff;
-   if (byte >= 32 && byte <= 126)
-      return (char)byte;
-   else
-      return '?';
-}
-
-static void
-png_icc_tag_name(char *name, png_uint_32 tag)
-{
-   name[0] = '\'';
-   name[1] = png_icc_tag_char(tag >> 24);
-   name[2] = png_icc_tag_char(tag >> 16);
-   name[3] = png_icc_tag_char(tag >>  8);
-   name[4] = png_icc_tag_char(tag      );
-   name[5] = '\'';
-}
-
-static int
-is_ICC_signature_char(png_alloc_size_t it)
-{
-   return it == 32 || (it >= 48 && it <= 57) || (it >= 65 && it <= 90) ||
-      (it >= 97 && it <= 122);
-}
-
-static int
-is_ICC_signature(png_alloc_size_t it)
-{
-   return is_ICC_signature_char(it >> 24) /* checks all the top bits */ &&
-      is_ICC_signature_char((it >> 16) & 0xff) &&
-      is_ICC_signature_char((it >> 8) & 0xff) &&
-      is_ICC_signature_char(it & 0xff);
-}
-
-static int
-png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    png_const_charp name, png_alloc_size_t value, png_const_charp reason)
-{
-   size_t pos;
-   char message[196]; /* see below for calculation */
-
-   if (colorspace != NULL)
-      colorspace->flags |= PNG_COLORSPACE_INVALID;
-
-   pos = png_safecat(message, (sizeof message), 0, "profile '"); /* 9 chars */
-   pos = png_safecat(message, pos+79, pos, name); /* Truncate to 79 chars */
-   pos = png_safecat(message, (sizeof message), pos, "': "); /* +2 = 90 */
-   if (is_ICC_signature(value) != 0)
-   {
-      /* So 'value' is at most 4 bytes and the following cast is safe */
-      png_icc_tag_name(message+pos, (png_uint_32)value);
-      pos += 6; /* total +8; less than the else clause */
-      message[pos++] = ':';
-      message[pos++] = ' ';
-   }
-#  ifdef PNG_WARNINGS_SUPPORTED
-   else
-      {
-         char number[PNG_NUMBER_BUFFER_SIZE]; /* +24 = 114*/
-
-         pos = png_safecat(message, (sizeof message), pos,
-             png_format_number(number, number+(sizeof number),
-             PNG_NUMBER_FORMAT_x, value));
-         pos = png_safecat(message, (sizeof message), pos, "h: "); /*+2 = 116*/
-      }
-#  endif
-   /* The 'reason' is an arbitrary message, allow +79 maximum 195 */
-   pos = png_safecat(message, (sizeof message), pos, reason);
-   PNG_UNUSED(pos)
-
-   /* This is recoverable, but make it unconditionally an app_error on write to
-    * avoid writing invalid ICC profiles into PNG files (i.e., we handle them
-    * on read, with a warning, but on write unless the app turns off
-    * application errors the PNG won't be written.)
-    */
-   png_chunk_report(png_ptr, message,
-       (colorspace != NULL) ? PNG_CHUNK_ERROR : PNG_CHUNK_WRITE_ERROR);
-
-   return 0;
-}
-#endif /* sRGB || iCCP */
-
-#ifdef PNG_sRGB_SUPPORTED
-int /* PRIVATE */
-png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    int intent)
-{
-   /* sRGB sets known gamma, end points and (from the chunk) intent. */
-   /* IMPORTANT: these are not necessarily the values found in an ICC profile
-    * because ICC profiles store values adapted to a D50 environment; it is
-    * expected that the ICC profile mediaWhitePointTag will be D50; see the
-    * checks and code elsewhere to understand this better.
-    *
-    * These XYZ values, which are accurate to 5dp, produce rgb to gray
-    * coefficients of (6968,23435,2366), which are reduced (because they add up
-    * to 32769 not 32768) to (6968,23434,2366).  These are the values that
-    * libpng has traditionally used (and are the best values given the 15bit
-    * algorithm used by the rgb to gray code.)
-    */
-   static const png_XYZ sRGB_XYZ = /* D65 XYZ (*not* the D50 adapted values!) */
-   {
-      /* color      X      Y      Z */
-      /* red   */ 41239, 21264,  1933,
-      /* green */ 35758, 71517, 11919,
-      /* blue  */ 18048,  7219, 95053
-   };
-
-   /* Do nothing if the colorspace is already invalidated. */
-   if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
-      return 0;
-
-   /* Check the intent, then check for existing settings.  It is valid for the
-    * PNG file to have cHRM or gAMA chunks along with sRGB, but the values must
-    * be consistent with the correct values.  If, however, this function is
-    * called below because an iCCP chunk matches sRGB then it is quite
-    * conceivable that an older app recorded incorrect gAMA and cHRM because of
-    * an incorrect calculation based on the values in the profile - this does
-    * *not* invalidate the profile (though it still produces an error, which can
-    * be ignored.)
-    */
-   if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST)
-      return png_icc_profile_error(png_ptr, colorspace, "sRGB",
-          (png_alloc_size_t)intent, "invalid sRGB rendering intent");
-
-   if ((colorspace->flags & PNG_COLORSPACE_HAVE_INTENT) != 0 &&
-       colorspace->rendering_intent != intent)
-      return png_icc_profile_error(png_ptr, colorspace, "sRGB",
-         (png_alloc_size_t)intent, "inconsistent rendering intents");
-
-   if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0)
-   {
-      png_benign_error(png_ptr, "duplicate sRGB information ignored");
-      return 0;
-   }
-
-   /* If the standard sRGB cHRM chunk does not match the one from the PNG file
-    * warn but overwrite the value with the correct one.
-    */
-   if ((colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0 &&
-       !png_colorspace_endpoints_match(&sRGB_xy, &colorspace->end_points_xy,
-       100))
-      png_chunk_report(png_ptr, "cHRM chunk does not match sRGB",
-         PNG_CHUNK_ERROR);
-
-   /* This check is just done for the error reporting - the routine always
-    * returns true when the 'from' argument corresponds to sRGB (2).
-    */
-   (void)png_colorspace_check_gamma(png_ptr, colorspace, PNG_GAMMA_sRGB_INVERSE,
-       2/*from sRGB*/);
-
-   /* intent: bugs in GCC force 'int' to be used as the parameter type. */
-   colorspace->rendering_intent = (png_uint_16)intent;
-   colorspace->flags |= PNG_COLORSPACE_HAVE_INTENT;
-
-   /* endpoints */
-   colorspace->end_points_xy = sRGB_xy;
-   colorspace->end_points_XYZ = sRGB_XYZ;
-   colorspace->flags |=
-      (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB);
-
-   /* gamma */
-   colorspace->gamma = PNG_GAMMA_sRGB_INVERSE;
-   colorspace->flags |= PNG_COLORSPACE_HAVE_GAMMA;
-
-   /* Finally record that we have an sRGB profile */
-   colorspace->flags |=
-      (PNG_COLORSPACE_MATCHES_sRGB|PNG_COLORSPACE_FROM_sRGB);
-
-   return 1; /* set */
-}
-#endif /* sRGB */
-
-#ifdef PNG_iCCP_SUPPORTED
-/* Encoded value of D50 as an ICC XYZNumber.  From the ICC 2010 spec the value
- * is XYZ(0.9642,1.0,0.8249), which scales to:
- *
- *    (63189.8112, 65536, 54060.6464)
- */
-static const png_byte D50_nCIEXYZ[12] =
-   { 0x00, 0x00, 0xf6, 0xd6, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d };
-
-static int /* bool */
-icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    png_const_charp name, png_uint_32 profile_length)
-{
-   if (profile_length < 132)
-      return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
-          "too short");
-   return 1;
-}
-
-#ifdef PNG_READ_iCCP_SUPPORTED
-int /* PRIVATE */
-png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    png_const_charp name, png_uint_32 profile_length)
-{
-   if (!icc_check_length(png_ptr, colorspace, name, profile_length))
-      return 0;
-
-   /* This needs to be here because the 'normal' check is in
-    * png_decompress_chunk, yet this happens after the attempt to
-    * png_malloc_base the required data.  We only need this on read; on write
-    * the caller supplies the profile buffer so libpng doesn't allocate it.  See
-    * the call to icc_check_length below (the write case).
-    */
-#  ifdef PNG_SET_USER_LIMITS_SUPPORTED
-      else if (png_ptr->user_chunk_malloc_max > 0 &&
-               png_ptr->user_chunk_malloc_max < profile_length)
-         return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
-             "exceeds application limits");
-#  elif PNG_USER_CHUNK_MALLOC_MAX > 0
-      else if (PNG_USER_CHUNK_MALLOC_MAX < profile_length)
-         return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
-             "exceeds libpng limits");
-#  else /* !SET_USER_LIMITS */
-      /* This will get compiled out on all 32-bit and better systems. */
-      else if (PNG_SIZE_MAX < profile_length)
-         return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
-             "exceeds system limits");
-#  endif /* !SET_USER_LIMITS */
-
-   return 1;
-}
-#endif /* READ_iCCP */
-
-int /* PRIVATE */
-png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    png_const_charp name, png_uint_32 profile_length,
-    png_const_bytep profile/* first 132 bytes only */, int color_type)
-{
-   png_uint_32 temp;
-
-   /* Length check; this cannot be ignored in this code because profile_length
-    * is used later to check the tag table, so even if the profile seems over
-    * long profile_length from the caller must be correct.  The caller can fix
-    * this up on read or write by just passing in the profile header length.
-    */
-   temp = png_get_uint_32(profile);
-   if (temp != profile_length)
-      return png_icc_profile_error(png_ptr, colorspace, name, temp,
-          "length does not match profile");
-
-   temp = (png_uint_32) (*(profile+8));
-   if (temp > 3 && (profile_length & 3))
-      return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
-          "invalid length");
-
-   temp = png_get_uint_32(profile+128); /* tag count: 12 bytes/tag */
-   if (temp > 357913930 || /* (2^32-4-132)/12: maximum possible tag count */
-      profile_length < 132+12*temp) /* truncated tag table */
-      return png_icc_profile_error(png_ptr, colorspace, name, temp,
-          "tag count too large");
-
-   /* The 'intent' must be valid or we can't store it, ICC limits the intent to
-    * 16 bits.
-    */
-   temp = png_get_uint_32(profile+64);
-   if (temp >= 0xffff) /* The ICC limit */
-      return png_icc_profile_error(png_ptr, colorspace, name, temp,
-          "invalid rendering intent");
-
-   /* This is just a warning because the profile may be valid in future
-    * versions.
-    */
-   if (temp >= PNG_sRGB_INTENT_LAST)
-      (void)png_icc_profile_error(png_ptr, NULL, name, temp,
-          "intent outside defined range");
-
-   /* At this point the tag table can't be checked because it hasn't necessarily
-    * been loaded; however, various header fields can be checked.  These checks
-    * are for values permitted by the PNG spec in an ICC profile; the PNG spec
-    * restricts the profiles that can be passed in an iCCP chunk (they must be
-    * appropriate to processing PNG data!)
-    */
-
-   /* Data checks (could be skipped).  These checks must be independent of the
-    * version number; however, the version number doesn't accommodate changes in
-    * the header fields (just the known tags and the interpretation of the
-    * data.)
-    */
-   temp = png_get_uint_32(profile+36); /* signature 'ascp' */
-   if (temp != 0x61637370)
-      return png_icc_profile_error(png_ptr, colorspace, name, temp,
-          "invalid signature");
-
-   /* Currently the PCS illuminant/adopted white point (the computational
-    * white point) are required to be D50,
-    * however the profile contains a record of the illuminant so perhaps ICC
-    * expects to be able to change this in the future (despite the rationale in
-    * the introduction for using a fixed PCS adopted white.)  Consequently the
-    * following is just a warning.
-    */
-   if (memcmp(profile+68, D50_nCIEXYZ, 12) != 0)
-      (void)png_icc_profile_error(png_ptr, NULL, name, 0/*no tag value*/,
-          "PCS illuminant is not D50");
-
-   /* The PNG spec requires this:
-    * "If the iCCP chunk is present, the image samples conform to the colour
-    * space represented by the embedded ICC profile as defined by the
-    * International Color Consortium [ICC]. The colour space of the ICC profile
-    * shall be an RGB colour space for colour images (PNG colour types 2, 3, and
-    * 6), or a greyscale colour space for greyscale images (PNG colour types 0
-    * and 4)."
-    *
-    * This checking code ensures the embedded profile (on either read or write)
-    * conforms to the specification requirements.  Notice that an ICC 'gray'
-    * color-space profile contains the information to transform the monochrome
-    * data to XYZ or L*a*b (according to which PCS the profile uses) and this
-    * should be used in preference to the standard libpng K channel replication
-    * into R, G and B channels.
-    *
-    * Previously it was suggested that an RGB profile on grayscale data could be
-    * handled.  However it it is clear that using an RGB profile in this context
-    * must be an error - there is no specification of what it means.  Thus it is
-    * almost certainly more correct to ignore the profile.
-    */
-   temp = png_get_uint_32(profile+16); /* data colour space field */
-   switch (temp)
-   {
-      case 0x52474220: /* 'RGB ' */
-         if ((color_type & PNG_COLOR_MASK_COLOR) == 0)
-            return png_icc_profile_error(png_ptr, colorspace, name, temp,
-                "RGB color space not permitted on grayscale PNG");
-         break;
-
-      case 0x47524159: /* 'GRAY' */
-         if ((color_type & PNG_COLOR_MASK_COLOR) != 0)
-            return png_icc_profile_error(png_ptr, colorspace, name, temp,
-                "Gray color space not permitted on RGB PNG");
-         break;
-
-      default:
-         return png_icc_profile_error(png_ptr, colorspace, name, temp,
-             "invalid ICC profile color space");
-   }
-
-   /* It is up to the application to check that the profile class matches the
-    * application requirements; the spec provides no guidance, but it's pretty
-    * weird if the profile is not scanner ('scnr'), monitor ('mntr'), printer
-    * ('prtr') or 'spac' (for generic color spaces).  Issue a warning in these
-    * cases.  Issue an error for device link or abstract profiles - these don't
-    * contain the records necessary to transform the color-space to anything
-    * other than the target device (and not even that for an abstract profile).
-    * Profiles of these classes may not be embedded in images.
-    */
-   temp = png_get_uint_32(profile+12); /* profile/device class */
-   switch (temp)
-   {
-      case 0x73636e72: /* 'scnr' */
-      case 0x6d6e7472: /* 'mntr' */
-      case 0x70727472: /* 'prtr' */
-      case 0x73706163: /* 'spac' */
-         /* All supported */
-         break;
-
-      case 0x61627374: /* 'abst' */
-         /* May not be embedded in an image */
-         return png_icc_profile_error(png_ptr, colorspace, name, temp,
-             "invalid embedded Abstract ICC profile");
-
-      case 0x6c696e6b: /* 'link' */
-         /* DeviceLink profiles cannot be interpreted in a non-device specific
-          * fashion, if an app uses the AToB0Tag in the profile the results are
-          * undefined unless the result is sent to the intended device,
-          * therefore a DeviceLink profile should not be found embedded in a
-          * PNG.
-          */
-         return png_icc_profile_error(png_ptr, colorspace, name, temp,
-             "unexpected DeviceLink ICC profile class");
-
-      case 0x6e6d636c: /* 'nmcl' */
-         /* A NamedColor profile is also device specific, however it doesn't
-          * contain an AToB0 tag that is open to misinterpretation.  Almost
-          * certainly it will fail the tests below.
-          */
-         (void)png_icc_profile_error(png_ptr, NULL, name, temp,
-             "unexpected NamedColor ICC profile class");
-         break;
-
-      default:
-         /* To allow for future enhancements to the profile accept unrecognized
-          * profile classes with a warning, these then hit the test below on the
-          * tag content to ensure they are backward compatible with one of the
-          * understood profiles.
-          */
-         (void)png_icc_profile_error(png_ptr, NULL, name, temp,
-             "unrecognized ICC profile class");
-         break;
-   }
-
-   /* For any profile other than a device link one the PCS must be encoded
-    * either in XYZ or Lab.
-    */
-   temp = png_get_uint_32(profile+20);
-   switch (temp)
-   {
-      case 0x58595a20: /* 'XYZ ' */
-      case 0x4c616220: /* 'Lab ' */
-         break;
-
-      default:
-         return png_icc_profile_error(png_ptr, colorspace, name, temp,
-             "unexpected ICC PCS encoding");
-   }
-
-   return 1;
-}
-
-int /* PRIVATE */
-png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    png_const_charp name, png_uint_32 profile_length,
-    png_const_bytep profile /* header plus whole tag table */)
-{
-   png_uint_32 tag_count = png_get_uint_32(profile+128);
-   png_uint_32 itag;
-   png_const_bytep tag = profile+132; /* The first tag */
-
-   /* First scan all the tags in the table and add bits to the icc_info value
-    * (temporarily in 'tags').
-    */
-   for (itag=0; itag < tag_count; ++itag, tag += 12)
-   {
-      png_uint_32 tag_id = png_get_uint_32(tag+0);
-      png_uint_32 tag_start = png_get_uint_32(tag+4); /* must be aligned */
-      png_uint_32 tag_length = png_get_uint_32(tag+8);/* not padded */
-
-      /* The ICC specification does not exclude zero length tags, therefore the
-       * start might actually be anywhere if there is no data, but this would be
-       * a clear abuse of the intent of the standard so the start is checked for
-       * being in range.  All defined tag types have an 8 byte header - a 4 byte
-       * type signature then 0.
-       */
-
-      /* This is a hard error; potentially it can cause read outside the
-       * profile.
-       */
-      if (tag_start > profile_length || tag_length > profile_length - tag_start)
-         return png_icc_profile_error(png_ptr, colorspace, name, tag_id,
-             "ICC profile tag outside profile");
-
-      if ((tag_start & 3) != 0)
-      {
-         /* CNHP730S.icc shipped with Microsoft Windows 64 violates this; it is
-          * only a warning here because libpng does not care about the
-          * alignment.
-          */
-         (void)png_icc_profile_error(png_ptr, NULL, name, tag_id,
-             "ICC profile tag start not a multiple of 4");
-      }
-   }
-
-   return 1; /* success, maybe with warnings */
-}
-
-#ifdef PNG_sRGB_SUPPORTED
-#if PNG_sRGB_PROFILE_CHECKS >= 0
-/* Information about the known ICC sRGB profiles */
-static const struct
-{
-   png_uint_32 adler, crc, length;
-   png_uint_32 md5[4];
-   png_byte    have_md5;
-   png_byte    is_broken;
-   png_uint_16 intent;
-
-#  define PNG_MD5(a,b,c,d) { a, b, c, d }, (a!=0)||(b!=0)||(c!=0)||(d!=0)
-#  define PNG_ICC_CHECKSUM(adler, crc, md5, intent, broke, date, length, fname)\
-      { adler, crc, length, md5, broke, intent },
-
-} png_sRGB_checks[] =
-{
-   /* This data comes from contrib/tools/checksum-icc run on downloads of
-    * all four ICC sRGB profiles from www.color.org.
-    */
-   /* adler32, crc32, MD5[4], intent, date, length, file-name */
-   PNG_ICC_CHECKSUM(0x0a3fd9f6, 0x3b8772b9,
-       PNG_MD5(0x29f83dde, 0xaff255ae, 0x7842fae4, 0xca83390d), 0, 0,
-       "2009/03/27 21:36:31", 3048, "sRGB_IEC61966-2-1_black_scaled.icc")
-
-   /* ICC sRGB v2 perceptual no black-compensation: */
-   PNG_ICC_CHECKSUM(0x4909e5e1, 0x427ebb21,
-       PNG_MD5(0xc95bd637, 0xe95d8a3b, 0x0df38f99, 0xc1320389), 1, 0,
-       "2009/03/27 21:37:45", 3052, "sRGB_IEC61966-2-1_no_black_scaling.icc")
-
-   PNG_ICC_CHECKSUM(0xfd2144a1, 0x306fd8ae,
-       PNG_MD5(0xfc663378, 0x37e2886b, 0xfd72e983, 0x8228f1b8), 0, 0,
-       "2009/08/10 17:28:01", 60988, "sRGB_v4_ICC_preference_displayclass.icc")
-
-   /* ICC sRGB v4 perceptual */
-   PNG_ICC_CHECKSUM(0x209c35d2, 0xbbef7812,
-       PNG_MD5(0x34562abf, 0x994ccd06, 0x6d2c5721, 0xd0d68c5d), 0, 0,
-       "2007/07/25 00:05:37", 60960, "sRGB_v4_ICC_preference.icc")
-
-   /* The following profiles have no known MD5 checksum. If there is a match
-    * on the (empty) MD5 the other fields are used to attempt a match and
-    * a warning is produced.  The first two of these profiles have a 'cprt' tag
-    * which suggests that they were also made by Hewlett Packard.
-    */
-   PNG_ICC_CHECKSUM(0xa054d762, 0x5d5129ce,
-       PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 0,
-       "2004/07/21 18:57:42", 3024, "sRGB_IEC61966-2-1_noBPC.icc")
-
-   /* This is a 'mntr' (display) profile with a mediaWhitePointTag that does not
-    * match the D50 PCS illuminant in the header (it is in fact the D65 values,
-    * so the white point is recorded as the un-adapted value.)  The profiles
-    * below only differ in one byte - the intent - and are basically the same as
-    * the previous profile except for the mediaWhitePointTag error and a missing
-    * chromaticAdaptationTag.
-    */
-   PNG_ICC_CHECKSUM(0xf784f3fb, 0x182ea552,
-       PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 0, 1/*broken*/,
-       "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 perceptual")
-
-   PNG_ICC_CHECKSUM(0x0398f3fc, 0xf29e526d,
-       PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 1/*broken*/,
-       "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 media-relative")
-};
-
-static int
-png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr,
-    png_const_bytep profile, uLong adler)
-{
-   /* The quick check is to verify just the MD5 signature and trust the
-    * rest of the data.  Because the profile has already been verified for
-    * correctness this is safe.  png_colorspace_set_sRGB will check the 'intent'
-    * field too, so if the profile has been edited with an intent not defined
-    * by sRGB (but maybe defined by a later ICC specification) the read of
-    * the profile will fail at that point.
-    */
-
-   png_uint_32 length = 0;
-   png_uint_32 intent = 0x10000; /* invalid */
-#if PNG_sRGB_PROFILE_CHECKS > 1
-   uLong crc = 0; /* the value for 0 length data */
-#endif
-   unsigned int i;
-
-#ifdef PNG_SET_OPTION_SUPPORTED
-   /* First see if PNG_SKIP_sRGB_CHECK_PROFILE has been set to "on" */
-   if (((png_ptr->options >> PNG_SKIP_sRGB_CHECK_PROFILE) & 3) ==
-               PNG_OPTION_ON)
-      return 0;
-#endif
-
-   for (i=0; i < (sizeof png_sRGB_checks) / (sizeof png_sRGB_checks[0]); ++i)
-   {
-      if (png_get_uint_32(profile+84) == png_sRGB_checks[i].md5[0] &&
-         png_get_uint_32(profile+88) == png_sRGB_checks[i].md5[1] &&
-         png_get_uint_32(profile+92) == png_sRGB_checks[i].md5[2] &&
-         png_get_uint_32(profile+96) == png_sRGB_checks[i].md5[3])
-      {
-         /* This may be one of the old HP profiles without an MD5, in that
-          * case we can only use the length and Adler32 (note that these
-          * are not used by default if there is an MD5!)
-          */
-#        if PNG_sRGB_PROFILE_CHECKS == 0
-            if (png_sRGB_checks[i].have_md5 != 0)
-               return 1+png_sRGB_checks[i].is_broken;
-#        endif
-
-         /* Profile is unsigned or more checks have been configured in. */
-         if (length == 0)
-         {
-            length = png_get_uint_32(profile);
-            intent = png_get_uint_32(profile+64);
-         }
-
-         /* Length *and* intent must match */
-         if (length == (png_uint_32) png_sRGB_checks[i].length &&
-            intent == (png_uint_32) png_sRGB_checks[i].intent)
-         {
-            /* Now calculate the adler32 if not done already. */
-            if (adler == 0)
-            {
-               adler = adler32(0, NULL, 0);
-               adler = adler32(adler, profile, length);
-            }
-
-            if (adler == png_sRGB_checks[i].adler)
-            {
-               /* These basic checks suggest that the data has not been
-                * modified, but if the check level is more than 1 perform
-                * our own crc32 checksum on the data.
-                */
-#              if PNG_sRGB_PROFILE_CHECKS > 1
-                  if (crc == 0)
-                  {
-                     crc = crc32(0, NULL, 0);
-                     crc = crc32(crc, profile, length);
-                  }
-
-                  /* So this check must pass for the 'return' below to happen.
-                   */
-                  if (crc == png_sRGB_checks[i].crc)
-#              endif
-               {
-                  if (png_sRGB_checks[i].is_broken != 0)
-                  {
-                     /* These profiles are known to have bad data that may cause
-                      * problems if they are used, therefore attempt to
-                      * discourage their use, skip the 'have_md5' warning below,
-                      * which is made irrelevant by this error.
-                      */
-                     png_chunk_report(png_ptr, "known incorrect sRGB profile",
-                         PNG_CHUNK_ERROR);
-                  }
-
-                  /* Warn that this being done; this isn't even an error since
-                   * the profile is perfectly valid, but it would be nice if
-                   * people used the up-to-date ones.
-                   */
-                  else if (png_sRGB_checks[i].have_md5 == 0)
-                  {
-                     png_chunk_report(png_ptr,
-                         "out-of-date sRGB profile with no signature",
-                         PNG_CHUNK_WARNING);
-                  }
-
-                  return 1+png_sRGB_checks[i].is_broken;
-               }
-            }
-
-# if PNG_sRGB_PROFILE_CHECKS > 0
-         /* The signature matched, but the profile had been changed in some
-          * way.  This probably indicates a data error or uninformed hacking.
-          * Fall through to "no match".
-          */
-         png_chunk_report(png_ptr,
-             "Not recognizing known sRGB profile that has been edited",
-             PNG_CHUNK_WARNING);
-         break;
-# endif
-         }
-      }
-   }
-
-   return 0; /* no match */
-}
-
-void /* PRIVATE */
-png_icc_set_sRGB(png_const_structrp png_ptr,
-    png_colorspacerp colorspace, png_const_bytep profile, uLong adler)
-{
-   /* Is this profile one of the known ICC sRGB profiles?  If it is, just set
-    * the sRGB information.
-    */
-   if (png_compare_ICC_profile_with_sRGB(png_ptr, profile, adler) != 0)
-      (void)png_colorspace_set_sRGB(png_ptr, colorspace,
-         (int)/*already checked*/png_get_uint_32(profile+64));
-}
-#endif /* PNG_sRGB_PROFILE_CHECKS >= 0 */
-#endif /* sRGB */
-
-int /* PRIVATE */
-png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    png_const_charp name, png_uint_32 profile_length, png_const_bytep profile,
-    int color_type)
-{
-   if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
-      return 0;
-
-   if (icc_check_length(png_ptr, colorspace, name, profile_length) != 0 &&
-       png_icc_check_header(png_ptr, colorspace, name, profile_length, profile,
-           color_type) != 0 &&
-       png_icc_check_tag_table(png_ptr, colorspace, name, profile_length,
-           profile) != 0)
-   {
-#     if defined(PNG_sRGB_SUPPORTED) && PNG_sRGB_PROFILE_CHECKS >= 0
-         /* If no sRGB support, don't try storing sRGB information */
-         png_icc_set_sRGB(png_ptr, colorspace, profile, 0);
-#     endif
-      return 1;
-   }
-
-   /* Failure case */
-   return 0;
-}
-#endif /* iCCP */
-
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-void /* PRIVATE */
-png_colorspace_set_rgb_coefficients(png_structrp png_ptr)
-{
-   /* Set the rgb_to_gray coefficients from the colorspace. */
-   if (png_ptr->rgb_to_gray_coefficients_set == 0 &&
-      (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
-   {
-      /* png_set_background has not been called, get the coefficients from the Y
-       * values of the colorspace colorants.
-       */
-      png_fixed_point r = png_ptr->colorspace.end_points_XYZ.red_Y;
-      png_fixed_point g = png_ptr->colorspace.end_points_XYZ.green_Y;
-      png_fixed_point b = png_ptr->colorspace.end_points_XYZ.blue_Y;
-      png_fixed_point total = r+g+b;
-
-      if (total > 0 &&
-         r >= 0 && png_muldiv(&r, r, 32768, total) && r >= 0 && r <= 32768 &&
-         g >= 0 && png_muldiv(&g, g, 32768, total) && g >= 0 && g <= 32768 &&
-         b >= 0 && png_muldiv(&b, b, 32768, total) && b >= 0 && b <= 32768 &&
-         r+g+b <= 32769)
-      {
-         /* We allow 0 coefficients here.  r+g+b may be 32769 if two or
-          * all of the coefficients were rounded up.  Handle this by
-          * reducing the *largest* coefficient by 1; this matches the
-          * approach used for the default coefficients in pngrtran.c
-          */
-         int add = 0;
-
-         if (r+g+b > 32768)
-            add = -1;
-         else if (r+g+b < 32768)
-            add = 1;
-
-         if (add != 0)
-         {
-            if (g >= r && g >= b)
-               g += add;
-            else if (r >= g && r >= b)
-               r += add;
-            else
-               b += add;
-         }
-
-         /* Check for an internal error. */
-         if (r+g+b != 32768)
-            png_error(png_ptr,
-                "internal error handling cHRM coefficients");
-
-         else
-         {
-            png_ptr->rgb_to_gray_red_coeff   = (png_uint_16)r;
-            png_ptr->rgb_to_gray_green_coeff = (png_uint_16)g;
-         }
-      }
-
-      /* This is a png_error at present even though it could be ignored -
-       * it should never happen, but it is important that if it does, the
-       * bug is fixed.
-       */
-      else
-         png_error(png_ptr, "internal error handling cHRM->XYZ");
-   }
-}
-#endif /* READ_RGB_TO_GRAY */
-
-#endif /* COLORSPACE */
-
-/* #ifdef __GNUC__ */
-#if 1
-/* This exists solely to work round a warning from GNU C. */
-static int /* PRIVATE */
-png_gt(size_t a, size_t b)
-{
-   return a > b;
-}
-#else
-#   define png_gt(a,b) ((a) > (b))
-#endif
-
-void /* PRIVATE */
-png_check_IHDR(png_const_structrp png_ptr,
-    png_uint_32 width, png_uint_32 height, int bit_depth,
-    int color_type, int interlace_type, int compression_type,
-    int filter_type)
-{
-   int error = 0;
-
-   /* Check for width and height valid values */
-   if (width == 0)
-   {
-      png_warning(png_ptr, "Image width is zero in IHDR");
-      error = 1;
-   }
-
-   if (width > PNG_UINT_31_MAX)
-   {
-      png_warning(png_ptr, "Invalid image width in IHDR");
-      error = 1;
-   }
-
-   if (png_gt(((width + 7) & (~7U)),
-       ((PNG_SIZE_MAX
-           - 48        /* big_row_buf hack */
-           - 1)        /* filter byte */
-           / 8)        /* 8-byte RGBA pixels */
-           - 1))       /* extra max_pixel_depth pad */
-   {
-      /* The size of the row must be within the limits of this architecture.
-       * Because the read code can perform arbitrary transformations the
-       * maximum size is checked here.  Because the code in png_read_start_row
-       * adds extra space "for safety's sake" in several places a conservative
-       * limit is used here.
-       *
-       * NOTE: it would be far better to check the size that is actually used,
-       * but the effect in the real world is minor and the changes are more
-       * extensive, therefore much more dangerous and much more difficult to
-       * write in a way that avoids compiler warnings.
-       */
-      png_warning(png_ptr, "Image width is too large for this architecture");
-      error = 1;
-   }
-
-#ifdef PNG_SET_USER_LIMITS_SUPPORTED
-   if (width > png_ptr->user_width_max)
-#else
-   if (width > PNG_USER_WIDTH_MAX)
-#endif
-   {
-      png_warning(png_ptr, "Image width exceeds user limit in IHDR");
-      error = 1;
-   }
-
-   if (height == 0)
-   {
-      png_warning(png_ptr, "Image height is zero in IHDR");
-      error = 1;
-   }
-
-   if (height > PNG_UINT_31_MAX)
-   {
-      png_warning(png_ptr, "Invalid image height in IHDR");
-      error = 1;
-   }
-
-#ifdef PNG_SET_USER_LIMITS_SUPPORTED
-   if (height > png_ptr->user_height_max)
-#else
-   if (height > PNG_USER_HEIGHT_MAX)
-#endif
-   {
-      png_warning(png_ptr, "Image height exceeds user limit in IHDR");
-      error = 1;
-   }
-
-   /* Check other values */
-   if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 &&
-       bit_depth != 8 && bit_depth != 16)
-   {
-      png_warning(png_ptr, "Invalid bit depth in IHDR");
-      error = 1;
-   }
-
-   if (color_type < 0 || color_type == 1 ||
-       color_type == 5 || color_type > 6)
-   {
-      png_warning(png_ptr, "Invalid color type in IHDR");
-      error = 1;
-   }
-
-   if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) ||
-       ((color_type == PNG_COLOR_TYPE_RGB ||
-         color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
-         color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8))
-   {
-      png_warning(png_ptr, "Invalid color type/bit depth combination in IHDR");
-      error = 1;
-   }
-
-   if (interlace_type >= PNG_INTERLACE_LAST)
-   {
-      png_warning(png_ptr, "Unknown interlace method in IHDR");
-      error = 1;
-   }
-
-   if (compression_type != PNG_COMPRESSION_TYPE_BASE)
-   {
-      png_warning(png_ptr, "Unknown compression method in IHDR");
-      error = 1;
-   }
-
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-   /* Accept filter_method 64 (intrapixel differencing) only if
-    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
-    * 2. Libpng did not read a PNG signature (this filter_method is only
-    *    used in PNG datastreams that are embedded in MNG datastreams) and
-    * 3. The application called png_permit_mng_features with a mask that
-    *    included PNG_FLAG_MNG_FILTER_64 and
-    * 4. The filter_method is 64 and
-    * 5. The color_type is RGB or RGBA
-    */
-   if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0 &&
-       png_ptr->mng_features_permitted != 0)
-      png_warning(png_ptr, "MNG features are not allowed in a PNG datastream");
-
-   if (filter_type != PNG_FILTER_TYPE_BASE)
-   {
-      if (!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
-          (filter_type == PNG_INTRAPIXEL_DIFFERENCING) &&
-          ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) &&
-          (color_type == PNG_COLOR_TYPE_RGB ||
-          color_type == PNG_COLOR_TYPE_RGB_ALPHA)))
-      {
-         png_warning(png_ptr, "Unknown filter method in IHDR");
-         error = 1;
-      }
-
-      if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0)
-      {
-         png_warning(png_ptr, "Invalid filter method in IHDR");
-         error = 1;
-      }
-   }
-
-#else
-   if (filter_type != PNG_FILTER_TYPE_BASE)
-   {
-      png_warning(png_ptr, "Unknown filter method in IHDR");
-      error = 1;
-   }
-#endif
-
-   if (error == 1)
-      png_error(png_ptr, "Invalid IHDR data");
-}
-
-#if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED)
-/* ASCII to fp functions */
-/* Check an ASCII formatted floating point value, see the more detailed
- * comments in pngpriv.h
- */
-/* The following is used internally to preserve the sticky flags */
-#define png_fp_add(state, flags) ((state) |= (flags))
-#define png_fp_set(state, value) ((state) = (value) | ((state) & PNG_FP_STICKY))
-
-int /* PRIVATE */
-png_check_fp_number(png_const_charp string, size_t size, int *statep,
-    png_size_tp whereami)
-{
-   int state = *statep;
-   size_t i = *whereami;
-
-   while (i < size)
-   {
-      int type;
-      /* First find the type of the next character */
-      switch (string[i])
-      {
-      case 43:  type = PNG_FP_SAW_SIGN;                   break;
-      case 45:  type = PNG_FP_SAW_SIGN + PNG_FP_NEGATIVE; break;
-      case 46:  type = PNG_FP_SAW_DOT;                    break;
-      case 48:  type = PNG_FP_SAW_DIGIT;                  break;
-      case 49: case 50: case 51: case 52:
-      case 53: case 54: case 55: case 56:
-      case 57:  type = PNG_FP_SAW_DIGIT + PNG_FP_NONZERO; break;
-      case 69:
-      case 101: type = PNG_FP_SAW_E;                      break;
-      default:  goto PNG_FP_End;
-      }
-
-      /* Now deal with this type according to the current
-       * state, the type is arranged to not overlap the
-       * bits of the PNG_FP_STATE.
-       */
-      switch ((state & PNG_FP_STATE) + (type & PNG_FP_SAW_ANY))
-      {
-      case PNG_FP_INTEGER + PNG_FP_SAW_SIGN:
-         if ((state & PNG_FP_SAW_ANY) != 0)
-            goto PNG_FP_End; /* not a part of the number */
-
-         png_fp_add(state, type);
-         break;
-
-      case PNG_FP_INTEGER + PNG_FP_SAW_DOT:
-         /* Ok as trailer, ok as lead of fraction. */
-         if ((state & PNG_FP_SAW_DOT) != 0) /* two dots */
-            goto PNG_FP_End;
-
-         else if ((state & PNG_FP_SAW_DIGIT) != 0) /* trailing dot? */
-            png_fp_add(state, type);
-
-         else
-            png_fp_set(state, PNG_FP_FRACTION | type);
-
-         break;
-
-      case PNG_FP_INTEGER + PNG_FP_SAW_DIGIT:
-         if ((state & PNG_FP_SAW_DOT) != 0) /* delayed fraction */
-            png_fp_set(state, PNG_FP_FRACTION | PNG_FP_SAW_DOT);
-
-         png_fp_add(state, type | PNG_FP_WAS_VALID);
-
-         break;
-
-      case PNG_FP_INTEGER + PNG_FP_SAW_E:
-         if ((state & PNG_FP_SAW_DIGIT) == 0)
-            goto PNG_FP_End;
-
-         png_fp_set(state, PNG_FP_EXPONENT);
-
-         break;
-
-   /* case PNG_FP_FRACTION + PNG_FP_SAW_SIGN:
-         goto PNG_FP_End; ** no sign in fraction */
-
-   /* case PNG_FP_FRACTION + PNG_FP_SAW_DOT:
-         goto PNG_FP_End; ** Because SAW_DOT is always set */
-
-      case PNG_FP_FRACTION + PNG_FP_SAW_DIGIT:
-         png_fp_add(state, type | PNG_FP_WAS_VALID);
-         break;
-
-      case PNG_FP_FRACTION + PNG_FP_SAW_E:
-         /* This is correct because the trailing '.' on an
-          * integer is handled above - so we can only get here
-          * with the sequence ".E" (with no preceding digits).
-          */
-         if ((state & PNG_FP_SAW_DIGIT) == 0)
-            goto PNG_FP_End;
-
-         png_fp_set(state, PNG_FP_EXPONENT);
-
-         break;
-
-      case PNG_FP_EXPONENT + PNG_FP_SAW_SIGN:
-         if ((state & PNG_FP_SAW_ANY) != 0)
-            goto PNG_FP_End; /* not a part of the number */
-
-         png_fp_add(state, PNG_FP_SAW_SIGN);
-
-         break;
-
-   /* case PNG_FP_EXPONENT + PNG_FP_SAW_DOT:
-         goto PNG_FP_End; */
-
-      case PNG_FP_EXPONENT + PNG_FP_SAW_DIGIT:
-         png_fp_add(state, PNG_FP_SAW_DIGIT | PNG_FP_WAS_VALID);
-
-         break;
-
-   /* case PNG_FP_EXPONEXT + PNG_FP_SAW_E:
-         goto PNG_FP_End; */
-
-      default: goto PNG_FP_End; /* I.e. break 2 */
-      }
-
-      /* The character seems ok, continue. */
-      ++i;
-   }
-
-PNG_FP_End:
-   /* Here at the end, update the state and return the correct
-    * return code.
-    */
-   *statep = state;
-   *whereami = i;
-
-   return (state & PNG_FP_SAW_DIGIT) != 0;
-}
-
-
-/* The same but for a complete string. */
-int
-png_check_fp_string(png_const_charp string, size_t size)
-{
-   int        state=0;
-   size_t char_index=0;
-
-   if (png_check_fp_number(string, size, &state, &char_index) != 0 &&
-      (char_index == size || string[char_index] == 0))
-      return state /* must be non-zero - see above */;
-
-   return 0; /* i.e. fail */
-}
-#endif /* pCAL || sCAL */
-
-#ifdef PNG_sCAL_SUPPORTED
-#  ifdef PNG_FLOATING_POINT_SUPPORTED
-/* Utility used below - a simple accurate power of ten from an integral
- * exponent.
- */
-static double
-png_pow10(int power)
-{
-   int recip = 0;
-   double d = 1;
-
-   /* Handle negative exponent with a reciprocal at the end because
-    * 10 is exact whereas .1 is inexact in base 2
-    */
-   if (power < 0)
-   {
-      if (power < DBL_MIN_10_EXP) return 0;
-      recip = 1; power = -power;
-   }
-
-   if (power > 0)
-   {
-      /* Decompose power bitwise. */
-      double mult = 10;
-      do
-      {
-         if (power & 1) d *= mult;
-         mult *= mult;
-         power >>= 1;
-      }
-      while (power > 0);
-
-      if (recip != 0) d = 1/d;
-   }
-   /* else power is 0 and d is 1 */
-
-   return d;
-}
-
-/* Function to format a floating point value in ASCII with a given
- * precision.
- */
-#if GCC_STRICT_OVERFLOW
-#pragma GCC diagnostic push
-/* The problem arises below with exp_b10, which can never overflow because it
- * comes, originally, from frexp and is therefore limited to a range which is
- * typically +/-710 (log2(DBL_MAX)/log2(DBL_MIN)).
- */
-#pragma GCC diagnostic warning "-Wstrict-overflow=2"
-#endif /* GCC_STRICT_OVERFLOW */
-void /* PRIVATE */
-png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, size_t size,
-    double fp, unsigned int precision)
-{
-   /* We use standard functions from math.h, but not printf because
-    * that would require stdio.  The caller must supply a buffer of
-    * sufficient size or we will png_error.  The tests on size and
-    * the space in ascii[] consumed are indicated below.
-    */
-   if (precision < 1)
-      precision = DBL_DIG;
-
-   /* Enforce the limit of the implementation precision too. */
-   if (precision > DBL_DIG+1)
-      precision = DBL_DIG+1;
-
-   /* Basic sanity checks */
-   if (size >= precision+5) /* See the requirements below. */
-   {
-      if (fp < 0)
-      {
-         fp = -fp;
-         *ascii++ = 45; /* '-'  PLUS 1 TOTAL 1 */
-         --size;
-      }
-
-      if (fp >= DBL_MIN && fp <= DBL_MAX)
-      {
-         int exp_b10;   /* A base 10 exponent */
-         double base;   /* 10^exp_b10 */
-
-         /* First extract a base 10 exponent of the number,
-          * the calculation below rounds down when converting
-          * from base 2 to base 10 (multiply by log10(2) -
-          * 0.3010, but 77/256 is 0.3008, so exp_b10 needs to
-          * be increased.  Note that the arithmetic shift
-          * performs a floor() unlike C arithmetic - using a
-          * C multiply would break the following for negative
-          * exponents.
-          */
-         (void)frexp(fp, &exp_b10); /* exponent to base 2 */
-
-         exp_b10 = (exp_b10 * 77) >> 8; /* <= exponent to base 10 */
-
-         /* Avoid underflow here. */
-         base = png_pow10(exp_b10); /* May underflow */
-
-         while (base < DBL_MIN || base < fp)
-         {
-            /* And this may overflow. */
-            double test = png_pow10(exp_b10+1);
-
-            if (test <= DBL_MAX)
-            {
-               ++exp_b10; base = test;
-            }
-
-            else
-               break;
-         }
-
-         /* Normalize fp and correct exp_b10, after this fp is in the
-          * range [.1,1) and exp_b10 is both the exponent and the digit
-          * *before* which the decimal point should be inserted
-          * (starting with 0 for the first digit).  Note that this
-          * works even if 10^exp_b10 is out of range because of the
-          * test on DBL_MAX above.
-          */
-         fp /= base;
-         while (fp >= 1)
-         {
-            fp /= 10; ++exp_b10;
-         }
-
-         /* Because of the code above fp may, at this point, be
-          * less than .1, this is ok because the code below can
-          * handle the leading zeros this generates, so no attempt
-          * is made to correct that here.
-          */
-
-         {
-            unsigned int czero, clead, cdigits;
-            char exponent[10];
-
-            /* Allow up to two leading zeros - this will not lengthen
-             * the number compared to using E-n.
-             */
-            if (exp_b10 < 0 && exp_b10 > -3) /* PLUS 3 TOTAL 4 */
-            {
-               czero = 0U-exp_b10; /* PLUS 2 digits: TOTAL 3 */
-               exp_b10 = 0;      /* Dot added below before first output. */
-            }
-            else
-               czero = 0;    /* No zeros to add */
-
-            /* Generate the digit list, stripping trailing zeros and
-             * inserting a '.' before a digit if the exponent is 0.
-             */
-            clead = czero; /* Count of leading zeros */
-            cdigits = 0;   /* Count of digits in list. */
-
-            do
-            {
-               double d;
-
-               fp *= 10;
-               /* Use modf here, not floor and subtract, so that
-                * the separation is done in one step.  At the end
-                * of the loop don't break the number into parts so
-                * that the final digit is rounded.
-                */
-               if (cdigits+czero+1 < precision+clead)
-                  fp = modf(fp, &d);
-
-               else
-               {
-                  d = floor(fp + .5);
-
-                  if (d > 9)
-                  {
-                     /* Rounding up to 10, handle that here. */
-                     if (czero > 0)
-                     {
-                        --czero; d = 1;
-                        if (cdigits == 0) --clead;
-                     }
-                     else
-                     {
-                        while (cdigits > 0 && d > 9)
-                        {
-                           int ch = *--ascii;
-
-                           if (exp_b10 != (-1))
-                              ++exp_b10;
-
-                           else if (ch == 46)
-                           {
-                              ch = *--ascii; ++size;
-                              /* Advance exp_b10 to '1', so that the
-                               * decimal point happens after the
-                               * previous digit.
-                               */
-                              exp_b10 = 1;
-                           }
-
-                           --cdigits;
-                           d = ch - 47;  /* I.e. 1+(ch-48) */
-                        }
-
-                        /* Did we reach the beginning? If so adjust the
-                         * exponent but take into account the leading
-                         * decimal point.
-                         */
-                        if (d > 9)  /* cdigits == 0 */
-                        {
-                           if (exp_b10 == (-1))
-                           {
-                              /* Leading decimal point (plus zeros?), if
-                               * we lose the decimal point here it must
-                               * be reentered below.
-                               */
-                              int ch = *--ascii;
-
-                              if (ch == 46)
-                              {
-                                 ++size; exp_b10 = 1;
-                              }
-
-                              /* Else lost a leading zero, so 'exp_b10' is
-                               * still ok at (-1)
-                               */
-                           }
-                           else
-                              ++exp_b10;
-
-                           /* In all cases we output a '1' */
-                           d = 1;
-                        }
-                     }
-                  }
-                  fp = 0; /* Guarantees termination below. */
-               }
-
-               if (d == 0)
-               {
-                  ++czero;
-                  if (cdigits == 0) ++clead;
-               }
-               else
-               {
-                  /* Included embedded zeros in the digit count. */
-                  cdigits += czero - clead;
-                  clead = 0;
-
-                  while (czero > 0)
-                  {
-                     /* exp_b10 == (-1) means we just output the decimal
-                      * place - after the DP don't adjust 'exp_b10' any
-                      * more!
-                      */
-                     if (exp_b10 != (-1))
-                     {
-                        if (exp_b10 == 0)
-                        {
-                           *ascii++ = 46; --size;
-                        }
-                        /* PLUS 1: TOTAL 4 */
-                        --exp_b10;
-                     }
-                     *ascii++ = 48; --czero;
-                  }
-
-                  if (exp_b10 != (-1))
-                  {
-                     if (exp_b10 == 0)
-                     {
-                        *ascii++ = 46; --size; /* counted above */
-                     }
-
-                     --exp_b10;
-                  }
-                  *ascii++ = (char)(48 + (int)d); ++cdigits;
-               }
-            }
-            while (cdigits+czero < precision+clead && fp > DBL_MIN);
-
-            /* The total output count (max) is now 4+precision */
-
-            /* Check for an exponent, if we don't need one we are
-             * done and just need to terminate the string.  At this
-             * point, exp_b10==(-1) is effectively a flag: it got
-             * to '-1' because of the decrement, after outputting
-             * the decimal point above. (The exponent required is
-             * *not* -1.)
-             */
-            if (exp_b10 >= (-1) && exp_b10 <= 2)
-            {
-               /* The following only happens if we didn't output the
-                * leading zeros above for negative exponent, so this
-                * doesn't add to the digit requirement.  Note that the
-                * two zeros here can only be output if the two leading
-                * zeros were *not* output, so this doesn't increase
-                * the output count.
-                */
-               while (exp_b10-- > 0) *ascii++ = 48;
-
-               *ascii = 0;
-
-               /* Total buffer requirement (including the '\0') is
-                * 5+precision - see check at the start.
-                */
-               return;
-            }
-
-            /* Here if an exponent is required, adjust size for
-             * the digits we output but did not count.  The total
-             * digit output here so far is at most 1+precision - no
-             * decimal point and no leading or trailing zeros have
-             * been output.
-             */
-            size -= cdigits;
-
-            *ascii++ = 69; --size;    /* 'E': PLUS 1 TOTAL 2+precision */
-
-            /* The following use of an unsigned temporary avoids ambiguities in
-             * the signed arithmetic on exp_b10 and permits GCC at least to do
-             * better optimization.
-             */
-            {
-               unsigned int uexp_b10;
-
-               if (exp_b10 < 0)
-               {
-                  *ascii++ = 45; --size; /* '-': PLUS 1 TOTAL 3+precision */
-                  uexp_b10 = 0U-exp_b10;
-               }
-
-               else
-                  uexp_b10 = 0U+exp_b10;
-
-               cdigits = 0;
-
-               while (uexp_b10 > 0)
-               {
-                  exponent[cdigits++] = (char)(48 + uexp_b10 % 10);
-                  uexp_b10 /= 10;
-               }
-            }
-
-            /* Need another size check here for the exponent digits, so
-             * this need not be considered above.
-             */
-            if (size > cdigits)
-            {
-               while (cdigits > 0) *ascii++ = exponent[--cdigits];
-
-               *ascii = 0;
-
-               return;
-            }
-         }
-      }
-      else if (!(fp >= DBL_MIN))
-      {
-         *ascii++ = 48; /* '0' */
-         *ascii = 0;
-         return;
-      }
-      else
-      {
-         *ascii++ = 105; /* 'i' */
-         *ascii++ = 110; /* 'n' */
-         *ascii++ = 102; /* 'f' */
-         *ascii = 0;
-         return;
-      }
-   }
-
-   /* Here on buffer too small. */
-   png_error(png_ptr, "ASCII conversion buffer too small");
-}
-#if GCC_STRICT_OVERFLOW
-#pragma GCC diagnostic pop
-#endif /* GCC_STRICT_OVERFLOW */
-
-#  endif /* FLOATING_POINT */
-
-#  ifdef PNG_FIXED_POINT_SUPPORTED
-/* Function to format a fixed point value in ASCII.
- */
-void /* PRIVATE */
-png_ascii_from_fixed(png_const_structrp png_ptr, png_charp ascii,
-    size_t size, png_fixed_point fp)
-{
-   /* Require space for 10 decimal digits, a decimal point, a minus sign and a
-    * trailing \0, 13 characters:
-    */
-   if (size > 12)
-   {
-      png_uint_32 num;
-
-      /* Avoid overflow here on the minimum integer. */
-      if (fp < 0)
-      {
-         *ascii++ = 45; num = (png_uint_32)(-fp);
-      }
-      else
-         num = (png_uint_32)fp;
-
-      if (num <= 0x80000000) /* else overflowed */
-      {
-         unsigned int ndigits = 0, first = 16 /* flag value */;
-         char digits[10];
-
-         while (num)
-         {
-            /* Split the low digit off num: */
-            unsigned int tmp = num/10;
-            num -= tmp*10;
-            digits[ndigits++] = (char)(48 + num);
-            /* Record the first non-zero digit, note that this is a number
-             * starting at 1, it's not actually the array index.
-             */
-            if (first == 16 && num > 0)
-               first = ndigits;
-            num = tmp;
-         }
-
-         if (ndigits > 0)
-         {
-            while (ndigits > 5) *ascii++ = digits[--ndigits];
-            /* The remaining digits are fractional digits, ndigits is '5' or
-             * smaller at this point.  It is certainly not zero.  Check for a
-             * non-zero fractional digit:
-             */
-            if (first <= 5)
-            {
-               unsigned int i;
-               *ascii++ = 46; /* decimal point */
-               /* ndigits may be <5 for small numbers, output leading zeros
-                * then ndigits digits to first:
-                */
-               i = 5;
-               while (ndigits < i)
-               {
-                  *ascii++ = 48; --i;
-               }
-               while (ndigits >= first) *ascii++ = digits[--ndigits];
-               /* Don't output the trailing zeros! */
-            }
-         }
-         else
-            *ascii++ = 48;
-
-         /* And null terminate the string: */
-         *ascii = 0;
-         return;
-      }
-   }
-
-   /* Here on buffer too small. */
-   png_error(png_ptr, "ASCII conversion buffer too small");
-}
-#   endif /* FIXED_POINT */
-#endif /* SCAL */
-
-#if defined(PNG_FLOATING_POINT_SUPPORTED) && \
-   !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \
-   (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \
-   defined(PNG_sCAL_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) || \
-   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)) || \
-   (defined(PNG_sCAL_SUPPORTED) && \
-   defined(PNG_FLOATING_ARITHMETIC_SUPPORTED))
-png_fixed_point
-png_fixed(png_const_structrp png_ptr, double fp, png_const_charp text)
-{
-   double r = floor(100000 * fp + .5);
-
-   if (r > 2147483647. || r < -2147483648.)
-      png_fixed_error(png_ptr, text);
-
-#  ifndef PNG_ERROR_TEXT_SUPPORTED
-   PNG_UNUSED(text)
-#  endif
-
-   return (png_fixed_point)r;
-}
-#endif
-
-#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_COLORSPACE_SUPPORTED) ||\
-    defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED)
-/* muldiv functions */
-/* This API takes signed arguments and rounds the result to the nearest
- * integer (or, for a fixed point number - the standard argument - to
- * the nearest .00001).  Overflow and divide by zero are signalled in
- * the result, a boolean - true on success, false on overflow.
- */
-#if GCC_STRICT_OVERFLOW /* from above */
-/* It is not obvious which comparison below gets optimized in such a way that
- * signed overflow would change the result; looking through the code does not
- * reveal any tests which have the form GCC complains about, so presumably the
- * optimizer is moving an add or subtract into the 'if' somewhere.
- */
-#pragma GCC diagnostic push
-#pragma GCC diagnostic warning "-Wstrict-overflow=2"
-#endif /* GCC_STRICT_OVERFLOW */
-int
-png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times,
-    png_int_32 divisor)
-{
-   /* Return a * times / divisor, rounded. */
-   if (divisor != 0)
-   {
-      if (a == 0 || times == 0)
-      {
-         *res = 0;
-         return 1;
-      }
-      else
-      {
-#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
-         double r = a;
-         r *= times;
-         r /= divisor;
-         r = floor(r+.5);
-
-         /* A png_fixed_point is a 32-bit integer. */
-         if (r <= 2147483647. && r >= -2147483648.)
-         {
-            *res = (png_fixed_point)r;
-            return 1;
-         }
-#else
-         int negative = 0;
-         png_uint_32 A, T, D;
-         png_uint_32 s16, s32, s00;
-
-         if (a < 0)
-            negative = 1, A = -a;
-         else
-            A = a;
-
-         if (times < 0)
-            negative = !negative, T = -times;
-         else
-            T = times;
-
-         if (divisor < 0)
-            negative = !negative, D = -divisor;
-         else
-            D = divisor;
-
-         /* Following can't overflow because the arguments only
-          * have 31 bits each, however the result may be 32 bits.
-          */
-         s16 = (A >> 16) * (T & 0xffff) +
-                           (A & 0xffff) * (T >> 16);
-         /* Can't overflow because the a*times bit is only 30
-          * bits at most.
-          */
-         s32 = (A >> 16) * (T >> 16) + (s16 >> 16);
-         s00 = (A & 0xffff) * (T & 0xffff);
-
-         s16 = (s16 & 0xffff) << 16;
-         s00 += s16;
-
-         if (s00 < s16)
-            ++s32; /* carry */
-
-         if (s32 < D) /* else overflow */
-         {
-            /* s32.s00 is now the 64-bit product, do a standard
-             * division, we know that s32 < D, so the maximum
-             * required shift is 31.
-             */
-            int bitshift = 32;
-            png_fixed_point result = 0; /* NOTE: signed */
-
-            while (--bitshift >= 0)
-            {
-               png_uint_32 d32, d00;
-
-               if (bitshift > 0)
-                  d32 = D >> (32-bitshift), d00 = D << bitshift;
-
-               else
-                  d32 = 0, d00 = D;
-
-               if (s32 > d32)
-               {
-                  if (s00 < d00) --s32; /* carry */
-                  s32 -= d32, s00 -= d00, result += 1<<bitshift;
-               }
-
-               else
-                  if (s32 == d32 && s00 >= d00)
-                     s32 = 0, s00 -= d00, result += 1<<bitshift;
-            }
-
-            /* Handle the rounding. */
-            if (s00 >= (D >> 1))
-               ++result;
-
-            if (negative != 0)
-               result = -result;
-
-            /* Check for overflow. */
-            if ((negative != 0 && result <= 0) ||
-                (negative == 0 && result >= 0))
-            {
-               *res = result;
-               return 1;
-            }
-         }
-#endif
-      }
-   }
-
-   return 0;
-}
-#if GCC_STRICT_OVERFLOW
-#pragma GCC diagnostic pop
-#endif /* GCC_STRICT_OVERFLOW */
-#endif /* READ_GAMMA || INCH_CONVERSIONS */
-
-#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED)
-/* The following is for when the caller doesn't much care about the
- * result.
- */
-png_fixed_point
-png_muldiv_warn(png_const_structrp png_ptr, png_fixed_point a, png_int_32 times,
-    png_int_32 divisor)
-{
-   png_fixed_point result;
-
-   if (png_muldiv(&result, a, times, divisor) != 0)
-      return result;
-
-   png_warning(png_ptr, "fixed point overflow ignored");
-   return 0;
-}
-#endif
-
-#ifdef PNG_GAMMA_SUPPORTED /* more fixed point functions for gamma */
-/* Calculate a reciprocal, return 0 on div-by-zero or overflow. */
-png_fixed_point
-png_reciprocal(png_fixed_point a)
-{
-#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
-   double r = floor(1E10/a+.5);
-
-   if (r <= 2147483647. && r >= -2147483648.)
-      return (png_fixed_point)r;
-#else
-   png_fixed_point res;
-
-   if (png_muldiv(&res, 100000, 100000, a) != 0)
-      return res;
-#endif
-
-   return 0; /* error/overflow */
-}
-
-/* This is the shared test on whether a gamma value is 'significant' - whether
- * it is worth doing gamma correction.
- */
-int /* PRIVATE */
-png_gamma_significant(png_fixed_point gamma_val)
-{
-   return gamma_val < PNG_FP_1 - PNG_GAMMA_THRESHOLD_FIXED ||
-       gamma_val > PNG_FP_1 + PNG_GAMMA_THRESHOLD_FIXED;
-}
-#endif
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
-#ifdef PNG_16BIT_SUPPORTED
-/* A local convenience routine. */
-static png_fixed_point
-png_product2(png_fixed_point a, png_fixed_point b)
-{
-   /* The required result is 1/a * 1/b; the following preserves accuracy. */
-#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
-   double r = a * 1E-5;
-   r *= b;
-   r = floor(r+.5);
-
-   if (r <= 2147483647. && r >= -2147483648.)
-      return (png_fixed_point)r;
-#else
-   png_fixed_point res;
-
-   if (png_muldiv(&res, a, b, 100000) != 0)
-      return res;
-#endif
-
-   return 0; /* overflow */
-}
-#endif /* 16BIT */
-
-/* The inverse of the above. */
-png_fixed_point
-png_reciprocal2(png_fixed_point a, png_fixed_point b)
-{
-   /* The required result is 1/a * 1/b; the following preserves accuracy. */
-#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
-   if (a != 0 && b != 0)
-   {
-      double r = 1E15/a;
-      r /= b;
-      r = floor(r+.5);
-
-      if (r <= 2147483647. && r >= -2147483648.)
-         return (png_fixed_point)r;
-   }
-#else
-   /* This may overflow because the range of png_fixed_point isn't symmetric,
-    * but this API is only used for the product of file and screen gamma so it
-    * doesn't matter that the smallest number it can produce is 1/21474, not
-    * 1/100000
-    */
-   png_fixed_point res = png_product2(a, b);
-
-   if (res != 0)
-      return png_reciprocal(res);
-#endif
-
-   return 0; /* overflow */
-}
-#endif /* READ_GAMMA */
-
-#ifdef PNG_READ_GAMMA_SUPPORTED /* gamma table code */
-#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED
-/* Fixed point gamma.
- *
- * The code to calculate the tables used below can be found in the shell script
- * contrib/tools/intgamma.sh
- *
- * To calculate gamma this code implements fast log() and exp() calls using only
- * fixed point arithmetic.  This code has sufficient precision for either 8-bit
- * or 16-bit sample values.
- *
- * The tables used here were calculated using simple 'bc' programs, but C double
- * precision floating point arithmetic would work fine.
- *
- * 8-bit log table
- *   This is a table of -log(value/255)/log(2) for 'value' in the range 128 to
- *   255, so it's the base 2 logarithm of a normalized 8-bit floating point
- *   mantissa.  The numbers are 32-bit fractions.
- */
-static const png_uint_32
-png_8bit_l2[128] =
-{
-   4270715492U, 4222494797U, 4174646467U, 4127164793U, 4080044201U, 4033279239U,
-   3986864580U, 3940795015U, 3895065449U, 3849670902U, 3804606499U, 3759867474U,
-   3715449162U, 3671346997U, 3627556511U, 3584073329U, 3540893168U, 3498011834U,
-   3455425220U, 3413129301U, 3371120137U, 3329393864U, 3287946700U, 3246774933U,
-   3205874930U, 3165243125U, 3124876025U, 3084770202U, 3044922296U, 3005329011U,
-   2965987113U, 2926893432U, 2888044853U, 2849438323U, 2811070844U, 2772939474U,
-   2735041326U, 2697373562U, 2659933400U, 2622718104U, 2585724991U, 2548951424U,
-   2512394810U, 2476052606U, 2439922311U, 2404001468U, 2368287663U, 2332778523U,
-   2297471715U, 2262364947U, 2227455964U, 2192742551U, 2158222529U, 2123893754U,
-   2089754119U, 2055801552U, 2022034013U, 1988449497U, 1955046031U, 1921821672U,
-   1888774511U, 1855902668U, 1823204291U, 1790677560U, 1758320682U, 1726131893U,
-   1694109454U, 1662251657U, 1630556815U, 1599023271U, 1567649391U, 1536433567U,
-   1505374214U, 1474469770U, 1443718700U, 1413119487U, 1382670639U, 1352370686U,
-   1322218179U, 1292211689U, 1262349810U, 1232631153U, 1203054352U, 1173618059U,
-   1144320946U, 1115161701U, 1086139034U, 1057251672U, 1028498358U, 999877854U,
-   971388940U, 943030410U, 914801076U, 886699767U, 858725327U, 830876614U,
-   803152505U, 775551890U, 748073672U, 720716771U, 693480120U, 666362667U,
-   639363374U, 612481215U, 585715177U, 559064263U, 532527486U, 506103872U,
-   479792461U, 453592303U, 427502463U, 401522014U, 375650043U, 349885648U,
-   324227938U, 298676034U, 273229066U, 247886176U, 222646516U, 197509248U,
-   172473545U, 147538590U, 122703574U, 97967701U, 73330182U, 48790236U,
-   24347096U, 0U
-
-#if 0
-   /* The following are the values for 16-bit tables - these work fine for the
-    * 8-bit conversions but produce very slightly larger errors in the 16-bit
-    * log (about 1.2 as opposed to 0.7 absolute error in the final value).  To
-    * use these all the shifts below must be adjusted appropriately.
-    */
-   65166, 64430, 63700, 62976, 62257, 61543, 60835, 60132, 59434, 58741, 58054,
-   57371, 56693, 56020, 55352, 54689, 54030, 53375, 52726, 52080, 51439, 50803,
-   50170, 49542, 48918, 48298, 47682, 47070, 46462, 45858, 45257, 44661, 44068,
-   43479, 42894, 42312, 41733, 41159, 40587, 40020, 39455, 38894, 38336, 37782,
-   37230, 36682, 36137, 35595, 35057, 34521, 33988, 33459, 32932, 32408, 31887,
-   31369, 30854, 30341, 29832, 29325, 28820, 28319, 27820, 27324, 26830, 26339,
-   25850, 25364, 24880, 24399, 23920, 23444, 22970, 22499, 22029, 21562, 21098,
-   20636, 20175, 19718, 19262, 18808, 18357, 17908, 17461, 17016, 16573, 16132,
-   15694, 15257, 14822, 14390, 13959, 13530, 13103, 12678, 12255, 11834, 11415,
-   10997, 10582, 10168, 9756, 9346, 8937, 8531, 8126, 7723, 7321, 6921, 6523,
-   6127, 5732, 5339, 4947, 4557, 4169, 3782, 3397, 3014, 2632, 2251, 1872, 1495,
-   1119, 744, 372
-#endif
-};
-
-static png_int_32
-png_log8bit(unsigned int x)
-{
-   unsigned int lg2 = 0;
-   /* Each time 'x' is multiplied by 2, 1 must be subtracted off the final log,
-    * because the log is actually negate that means adding 1.  The final
-    * returned value thus has the range 0 (for 255 input) to 7.994 (for 1
-    * input), return -1 for the overflow (log 0) case, - so the result is
-    * always at most 19 bits.
-    */
-   if ((x &= 0xff) == 0)
-      return -1;
-
-   if ((x & 0xf0) == 0)
-      lg2  = 4, x <<= 4;
-
-   if ((x & 0xc0) == 0)
-      lg2 += 2, x <<= 2;
-
-   if ((x & 0x80) == 0)
-      lg2 += 1, x <<= 1;
-
-   /* result is at most 19 bits, so this cast is safe: */
-   return (png_int_32)((lg2 << 16) + ((png_8bit_l2[x-128]+32768)>>16));
-}
-
-/* The above gives exact (to 16 binary places) log2 values for 8-bit images,
- * for 16-bit images we use the most significant 8 bits of the 16-bit value to
- * get an approximation then multiply the approximation by a correction factor
- * determined by the remaining up to 8 bits.  This requires an additional step
- * in the 16-bit case.
- *
- * We want log2(value/65535), we have log2(v'/255), where:
- *
- *    value = v' * 256 + v''
- *          = v' * f
- *
- * So f is value/v', which is equal to (256+v''/v') since v' is in the range 128
- * to 255 and v'' is in the range 0 to 255 f will be in the range 256 to less
- * than 258.  The final factor also needs to correct for the fact that our 8-bit
- * value is scaled by 255, whereas the 16-bit values must be scaled by 65535.
- *
- * This gives a final formula using a calculated value 'x' which is value/v' and
- * scaling by 65536 to match the above table:
- *
- *   log2(x/257) * 65536
- *
- * Since these numbers are so close to '1' we can use simple linear
- * interpolation between the two end values 256/257 (result -368.61) and 258/257
- * (result 367.179).  The values used below are scaled by a further 64 to give
- * 16-bit precision in the interpolation:
- *
- * Start (256): -23591
- * Zero  (257):      0
- * End   (258):  23499
- */
-#ifdef PNG_16BIT_SUPPORTED
-static png_int_32
-png_log16bit(png_uint_32 x)
-{
-   unsigned int lg2 = 0;
-
-   /* As above, but now the input has 16 bits. */
-   if ((x &= 0xffff) == 0)
-      return -1;
-
-   if ((x & 0xff00) == 0)
-      lg2  = 8, x <<= 8;
-
-   if ((x & 0xf000) == 0)
-      lg2 += 4, x <<= 4;
-
-   if ((x & 0xc000) == 0)
-      lg2 += 2, x <<= 2;
-
-   if ((x & 0x8000) == 0)
-      lg2 += 1, x <<= 1;
-
-   /* Calculate the base logarithm from the top 8 bits as a 28-bit fractional
-    * value.
-    */
-   lg2 <<= 28;
-   lg2 += (png_8bit_l2[(x>>8)-128]+8) >> 4;
-
-   /* Now we need to interpolate the factor, this requires a division by the top
-    * 8 bits.  Do this with maximum precision.
-    */
-   x = ((x << 16) + (x >> 9)) / (x >> 8);
-
-   /* Since we divided by the top 8 bits of 'x' there will be a '1' at 1<<24,
-    * the value at 1<<16 (ignoring this) will be 0 or 1; this gives us exactly
-    * 16 bits to interpolate to get the low bits of the result.  Round the
-    * answer.  Note that the end point values are scaled by 64 to retain overall
-    * precision and that 'lg2' is current scaled by an extra 12 bits, so adjust
-    * the overall scaling by 6-12.  Round at every step.
-    */
-   x -= 1U << 24;
-
-   if (x <= 65536U) /* <= '257' */
-      lg2 += ((23591U * (65536U-x)) + (1U << (16+6-12-1))) >> (16+6-12);
-
-   else
-      lg2 -= ((23499U * (x-65536U)) + (1U << (16+6-12-1))) >> (16+6-12);
-
-   /* Safe, because the result can't have more than 20 bits: */
-   return (png_int_32)((lg2 + 2048) >> 12);
-}
-#endif /* 16BIT */
-
-/* The 'exp()' case must invert the above, taking a 20-bit fixed point
- * logarithmic value and returning a 16 or 8-bit number as appropriate.  In
- * each case only the low 16 bits are relevant - the fraction - since the
- * integer bits (the top 4) simply determine a shift.
- *
- * The worst case is the 16-bit distinction between 65535 and 65534. This
- * requires perhaps spurious accuracy in the decoding of the logarithm to
- * distinguish log2(65535/65534.5) - 10^-5 or 17 bits.  There is little chance
- * of getting this accuracy in practice.
- *
- * To deal with this the following exp() function works out the exponent of the
- * fractional part of the logarithm by using an accurate 32-bit value from the
- * top four fractional bits then multiplying in the remaining bits.
- */
-static const png_uint_32
-png_32bit_exp[16] =
-{
-   /* NOTE: the first entry is deliberately set to the maximum 32-bit value. */
-   4294967295U, 4112874773U, 3938502376U, 3771522796U, 3611622603U, 3458501653U,
-   3311872529U, 3171459999U, 3037000500U, 2908241642U, 2784941738U, 2666869345U,
-   2553802834U, 2445529972U, 2341847524U, 2242560872U
-};
-
-/* Adjustment table; provided to explain the numbers in the code below. */
-#if 0
-for (i=11;i>=0;--i){ print i, " ", (1 - e(-(2^i)/65536*l(2))) * 2^(32-i), "\n"}
-   11 44937.64284865548751208448
-   10 45180.98734845585101160448
-    9 45303.31936980687359311872
-    8 45364.65110595323018870784
-    7 45395.35850361789624614912
-    6 45410.72259715102037508096
-    5 45418.40724413220722311168
-    4 45422.25021786898173001728
-    3 45424.17186732298419044352
-    2 45425.13273269940811464704
-    1 45425.61317555035558641664
-    0 45425.85339951654943850496
-#endif
-
-static png_uint_32
-png_exp(png_fixed_point x)
-{
-   if (x > 0 && x <= 0xfffff) /* Else overflow or zero (underflow) */
-   {
-      /* Obtain a 4-bit approximation */
-      png_uint_32 e = png_32bit_exp[(x >> 12) & 0x0f];
-
-      /* Incorporate the low 12 bits - these decrease the returned value by
-       * multiplying by a number less than 1 if the bit is set.  The multiplier
-       * is determined by the above table and the shift. Notice that the values
-       * converge on 45426 and this is used to allow linear interpolation of the
-       * low bits.
-       */
-      if (x & 0x800)
-         e -= (((e >> 16) * 44938U) +  16U) >> 5;
-
-      if (x & 0x400)
-         e -= (((e >> 16) * 45181U) +  32U) >> 6;
-
-      if (x & 0x200)
-         e -= (((e >> 16) * 45303U) +  64U) >> 7;
-
-      if (x & 0x100)
-         e -= (((e >> 16) * 45365U) + 128U) >> 8;
-
-      if (x & 0x080)
-         e -= (((e >> 16) * 45395U) + 256U) >> 9;
-
-      if (x & 0x040)
-         e -= (((e >> 16) * 45410U) + 512U) >> 10;
-
-      /* And handle the low 6 bits in a single block. */
-      e -= (((e >> 16) * 355U * (x & 0x3fU)) + 256U) >> 9;
-
-      /* Handle the upper bits of x. */
-      e >>= x >> 16;
-      return e;
-   }
-
-   /* Check for overflow */
-   if (x <= 0)
-      return png_32bit_exp[0];
-
-   /* Else underflow */
-   return 0;
-}
-
-static png_byte
-png_exp8bit(png_fixed_point lg2)
-{
-   /* Get a 32-bit value: */
-   png_uint_32 x = png_exp(lg2);
-
-   /* Convert the 32-bit value to 0..255 by multiplying by 256-1. Note that the
-    * second, rounding, step can't overflow because of the first, subtraction,
-    * step.
-    */
-   x -= x >> 8;
-   return (png_byte)(((x + 0x7fffffU) >> 24) & 0xff);
-}
-
-#ifdef PNG_16BIT_SUPPORTED
-static png_uint_16
-png_exp16bit(png_fixed_point lg2)
-{
-   /* Get a 32-bit value: */
-   png_uint_32 x = png_exp(lg2);
-
-   /* Convert the 32-bit value to 0..65535 by multiplying by 65536-1: */
-   x -= x >> 16;
-   return (png_uint_16)((x + 32767U) >> 16);
-}
-#endif /* 16BIT */
-#endif /* FLOATING_ARITHMETIC */
-
-png_byte
-png_gamma_8bit_correct(unsigned int value, png_fixed_point gamma_val)
-{
-   if (value > 0 && value < 255)
-   {
-#     ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
-         /* 'value' is unsigned, ANSI-C90 requires the compiler to correctly
-          * convert this to a floating point value.  This includes values that
-          * would overflow if 'value' were to be converted to 'int'.
-          *
-          * Apparently GCC, however, does an intermediate conversion to (int)
-          * on some (ARM) but not all (x86) platforms, possibly because of
-          * hardware FP limitations.  (E.g. if the hardware conversion always
-          * assumes the integer register contains a signed value.)  This results
-          * in ANSI-C undefined behavior for large values.
-          *
-          * Other implementations on the same machine might actually be ANSI-C90
-          * conformant and therefore compile spurious extra code for the large
-          * values.
-          *
-          * We can be reasonably sure that an unsigned to float conversion
-          * won't be faster than an int to float one.  Therefore this code
-          * assumes responsibility for the undefined behavior, which it knows
-          * can't happen because of the check above.
-          *
-          * Note the argument to this routine is an (unsigned int) because, on
-          * 16-bit platforms, it is assigned a value which might be out of
-          * range for an (int); that would result in undefined behavior in the
-          * caller if the *argument* ('value') were to be declared (int).
-          */
-         double r = floor(255*pow((int)/*SAFE*/value/255.,gamma_val*.00001)+.5);
-         return (png_byte)r;
-#     else
-         png_int_32 lg2 = png_log8bit(value);
-         png_fixed_point res;
-
-         if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1) != 0)
-            return png_exp8bit(res);
-
-         /* Overflow. */
-         value = 0;
-#     endif
-   }
-
-   return (png_byte)(value & 0xff);
-}
-
-#ifdef PNG_16BIT_SUPPORTED
-png_uint_16
-png_gamma_16bit_correct(unsigned int value, png_fixed_point gamma_val)
-{
-   if (value > 0 && value < 65535)
-   {
-# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
-      /* The same (unsigned int)->(double) constraints apply here as above,
-       * however in this case the (unsigned int) to (int) conversion can
-       * overflow on an ANSI-C90 compliant system so the cast needs to ensure
-       * that this is not possible.
-       */
-      double r = floor(65535*pow((png_int_32)value/65535.,
-          gamma_val*.00001)+.5);
-      return (png_uint_16)r;
-# else
-      png_int_32 lg2 = png_log16bit(value);
-      png_fixed_point res;
-
-      if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1) != 0)
-         return png_exp16bit(res);
-
-      /* Overflow. */
-      value = 0;
-# endif
-   }
-
-   return (png_uint_16)value;
-}
-#endif /* 16BIT */
-
-/* This does the right thing based on the bit_depth field of the
- * png_struct, interpreting values as 8-bit or 16-bit.  While the result
- * is nominally a 16-bit value if bit depth is 8 then the result is
- * 8-bit (as are the arguments.)
- */
-png_uint_16 /* PRIVATE */
-png_gamma_correct(png_structrp png_ptr, unsigned int value,
-    png_fixed_point gamma_val)
-{
-   if (png_ptr->bit_depth == 8)
-      return png_gamma_8bit_correct(value, gamma_val);
-
-#ifdef PNG_16BIT_SUPPORTED
-   else
-      return png_gamma_16bit_correct(value, gamma_val);
-#else
-      /* should not reach this */
-      return 0;
-#endif /* 16BIT */
-}
-
-#ifdef PNG_16BIT_SUPPORTED
-/* Internal function to build a single 16-bit table - the table consists of
- * 'num' 256 entry subtables, where 'num' is determined by 'shift' - the amount
- * to shift the input values right (or 16-number_of_signifiant_bits).
- *
- * The caller is responsible for ensuring that the table gets cleaned up on
- * png_error (i.e. if one of the mallocs below fails) - i.e. the *table argument
- * should be somewhere that will be cleaned.
- */
-static void
-png_build_16bit_table(png_structrp png_ptr, png_uint_16pp *ptable,
-    unsigned int shift, png_fixed_point gamma_val)
-{
-   /* Various values derived from 'shift': */
-   unsigned int num = 1U << (8U - shift);
-#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
-   /* CSE the division and work round wacky GCC warnings (see the comments
-    * in png_gamma_8bit_correct for where these come from.)
-    */
-   double fmax = 1.0 / (((png_int_32)1 << (16U - shift)) - 1);
-#endif
-   unsigned int max = (1U << (16U - shift)) - 1U;
-   unsigned int max_by_2 = 1U << (15U - shift);
-   unsigned int i;
-
-   png_uint_16pp table = *ptable =
-       (png_uint_16pp)png_calloc(png_ptr, num * (sizeof (png_uint_16p)));
-
-   for (i = 0; i < num; i++)
-   {
-      png_uint_16p sub_table = table[i] =
-          (png_uint_16p)png_malloc(png_ptr, 256 * (sizeof (png_uint_16)));
-
-      /* The 'threshold' test is repeated here because it can arise for one of
-       * the 16-bit tables even if the others don't hit it.
-       */
-      if (png_gamma_significant(gamma_val) != 0)
-      {
-         /* The old code would overflow at the end and this would cause the
-          * 'pow' function to return a result >1, resulting in an
-          * arithmetic error.  This code follows the spec exactly; ig is
-          * the recovered input sample, it always has 8-16 bits.
-          *
-          * We want input * 65535/max, rounded, the arithmetic fits in 32
-          * bits (unsigned) so long as max <= 32767.
-          */
-         unsigned int j;
-         for (j = 0; j < 256; j++)
-         {
-            png_uint_32 ig = (j << (8-shift)) + i;
-#           ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
-               /* Inline the 'max' scaling operation: */
-               /* See png_gamma_8bit_correct for why the cast to (int) is
-                * required here.
-                */
-               double d = floor(65535.*pow(ig*fmax, gamma_val*.00001)+.5);
-               sub_table[j] = (png_uint_16)d;
-#           else
-               if (shift != 0)
-                  ig = (ig * 65535U + max_by_2)/max;
-
-               sub_table[j] = png_gamma_16bit_correct(ig, gamma_val);
-#           endif
-         }
-      }
-      else
-      {
-         /* We must still build a table, but do it the fast way. */
-         unsigned int j;
-
-         for (j = 0; j < 256; j++)
-         {
-            png_uint_32 ig = (j << (8-shift)) + i;
-
-            if (shift != 0)
-               ig = (ig * 65535U + max_by_2)/max;
-
-            sub_table[j] = (png_uint_16)ig;
-         }
-      }
-   }
-}
-
-/* NOTE: this function expects the *inverse* of the overall gamma transformation
- * required.
- */
-static void
-png_build_16to8_table(png_structrp png_ptr, png_uint_16pp *ptable,
-    unsigned int shift, png_fixed_point gamma_val)
-{
-   unsigned int num = 1U << (8U - shift);
-   unsigned int max = (1U << (16U - shift))-1U;
-   unsigned int i;
-   png_uint_32 last;
-
-   png_uint_16pp table = *ptable =
-       (png_uint_16pp)png_calloc(png_ptr, num * (sizeof (png_uint_16p)));
-
-   /* 'num' is the number of tables and also the number of low bits of low
-    * bits of the input 16-bit value used to select a table.  Each table is
-    * itself indexed by the high 8 bits of the value.
-    */
-   for (i = 0; i < num; i++)
-      table[i] = (png_uint_16p)png_malloc(png_ptr,
-          256 * (sizeof (png_uint_16)));
-
-   /* 'gamma_val' is set to the reciprocal of the value calculated above, so
-    * pow(out,g) is an *input* value.  'last' is the last input value set.
-    *
-    * In the loop 'i' is used to find output values.  Since the output is
-    * 8-bit there are only 256 possible values.  The tables are set up to
-    * select the closest possible output value for each input by finding
-    * the input value at the boundary between each pair of output values
-    * and filling the table up to that boundary with the lower output
-    * value.
-    *
-    * The boundary values are 0.5,1.5..253.5,254.5.  Since these are 9-bit
-    * values the code below uses a 16-bit value in i; the values start at
-    * 128.5 (for 0.5) and step by 257, for a total of 254 values (the last
-    * entries are filled with 255).  Start i at 128 and fill all 'last'
-    * table entries <= 'max'
-    */
-   last = 0;
-   for (i = 0; i < 255; ++i) /* 8-bit output value */
-   {
-      /* Find the corresponding maximum input value */
-      png_uint_16 out = (png_uint_16)(i * 257U); /* 16-bit output value */
-
-      /* Find the boundary value in 16 bits: */
-      png_uint_32 bound = png_gamma_16bit_correct(out+128U, gamma_val);
-
-      /* Adjust (round) to (16-shift) bits: */
-      bound = (bound * max + 32768U)/65535U + 1U;
-
-      while (last < bound)
-      {
-         table[last & (0xffU >> shift)][last >> (8U - shift)] = out;
-         last++;
-      }
-   }
-
-   /* And fill in the final entries. */
-   while (last < (num << 8))
-   {
-      table[last & (0xff >> shift)][last >> (8U - shift)] = 65535U;
-      last++;
-   }
-}
-#endif /* 16BIT */
-
-/* Build a single 8-bit table: same as the 16-bit case but much simpler (and
- * typically much faster).  Note that libpng currently does no sBIT processing
- * (apparently contrary to the spec) so a 256-entry table is always generated.
- */
-static void
-png_build_8bit_table(png_structrp png_ptr, png_bytepp ptable,
-    png_fixed_point gamma_val)
-{
-   unsigned int i;
-   png_bytep table = *ptable = (png_bytep)png_malloc(png_ptr, 256);
-
-   if (png_gamma_significant(gamma_val) != 0)
-      for (i=0; i<256; i++)
-         table[i] = png_gamma_8bit_correct(i, gamma_val);
-
-   else
-      for (i=0; i<256; ++i)
-         table[i] = (png_byte)(i & 0xff);
-}
-
-/* Used from png_read_destroy and below to release the memory used by the gamma
- * tables.
- */
-void /* PRIVATE */
-png_destroy_gamma_table(png_structrp png_ptr)
-{
-   png_free(png_ptr, png_ptr->gamma_table);
-   png_ptr->gamma_table = NULL;
-
-#ifdef PNG_16BIT_SUPPORTED
-   if (png_ptr->gamma_16_table != NULL)
-   {
-      int i;
-      int istop = (1 << (8 - png_ptr->gamma_shift));
-      for (i = 0; i < istop; i++)
-      {
-         png_free(png_ptr, png_ptr->gamma_16_table[i]);
-      }
-   png_free(png_ptr, png_ptr->gamma_16_table);
-   png_ptr->gamma_16_table = NULL;
-   }
-#endif /* 16BIT */
-
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
-   defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
-   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
-   png_free(png_ptr, png_ptr->gamma_from_1);
-   png_ptr->gamma_from_1 = NULL;
-   png_free(png_ptr, png_ptr->gamma_to_1);
-   png_ptr->gamma_to_1 = NULL;
-
-#ifdef PNG_16BIT_SUPPORTED
-   if (png_ptr->gamma_16_from_1 != NULL)
-   {
-      int i;
-      int istop = (1 << (8 - png_ptr->gamma_shift));
-      for (i = 0; i < istop; i++)
-      {
-         png_free(png_ptr, png_ptr->gamma_16_from_1[i]);
-      }
-   png_free(png_ptr, png_ptr->gamma_16_from_1);
-   png_ptr->gamma_16_from_1 = NULL;
-   }
-   if (png_ptr->gamma_16_to_1 != NULL)
-   {
-      int i;
-      int istop = (1 << (8 - png_ptr->gamma_shift));
-      for (i = 0; i < istop; i++)
-      {
-         png_free(png_ptr, png_ptr->gamma_16_to_1[i]);
-      }
-   png_free(png_ptr, png_ptr->gamma_16_to_1);
-   png_ptr->gamma_16_to_1 = NULL;
-   }
-#endif /* 16BIT */
-#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
-}
-
-/* We build the 8- or 16-bit gamma tables here.  Note that for 16-bit
- * tables, we don't make a full table if we are reducing to 8-bit in
- * the future.  Note also how the gamma_16 tables are segmented so that
- * we don't need to allocate > 64K chunks for a full 16-bit table.
- */
-void /* PRIVATE */
-png_build_gamma_table(png_structrp png_ptr, int bit_depth)
-{
-   png_debug(1, "in png_build_gamma_table");
-
-   /* Remove any existing table; this copes with multiple calls to
-    * png_read_update_info. The warning is because building the gamma tables
-    * multiple times is a performance hit - it's harmless but the ability to
-    * call png_read_update_info() multiple times is new in 1.5.6 so it seems
-    * sensible to warn if the app introduces such a hit.
-    */
-   if (png_ptr->gamma_table != NULL || png_ptr->gamma_16_table != NULL)
-   {
-      png_warning(png_ptr, "gamma table being rebuilt");
-      png_destroy_gamma_table(png_ptr);
-   }
-
-   if (bit_depth <= 8)
-   {
-      png_build_8bit_table(png_ptr, &png_ptr->gamma_table,
-          png_ptr->screen_gamma > 0 ?
-          png_reciprocal2(png_ptr->colorspace.gamma,
-          png_ptr->screen_gamma) : PNG_FP_1);
-
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
-   defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
-   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
-      if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0)
-      {
-         png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1,
-             png_reciprocal(png_ptr->colorspace.gamma));
-
-         png_build_8bit_table(png_ptr, &png_ptr->gamma_from_1,
-             png_ptr->screen_gamma > 0 ?
-             png_reciprocal(png_ptr->screen_gamma) :
-             png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */);
-      }
-#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
-   }
-#ifdef PNG_16BIT_SUPPORTED
-   else
-   {
-      png_byte shift, sig_bit;
-
-      if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
-      {
-         sig_bit = png_ptr->sig_bit.red;
-
-         if (png_ptr->sig_bit.green > sig_bit)
-            sig_bit = png_ptr->sig_bit.green;
-
-         if (png_ptr->sig_bit.blue > sig_bit)
-            sig_bit = png_ptr->sig_bit.blue;
-      }
-      else
-         sig_bit = png_ptr->sig_bit.gray;
-
-      /* 16-bit gamma code uses this equation:
-       *
-       *   ov = table[(iv & 0xff) >> gamma_shift][iv >> 8]
-       *
-       * Where 'iv' is the input color value and 'ov' is the output value -
-       * pow(iv, gamma).
-       *
-       * Thus the gamma table consists of up to 256 256-entry tables.  The table
-       * is selected by the (8-gamma_shift) most significant of the low 8 bits
-       * of the color value then indexed by the upper 8 bits:
-       *
-       *   table[low bits][high 8 bits]
-       *
-       * So the table 'n' corresponds to all those 'iv' of:
-       *
-       *   <all high 8-bit values><n << gamma_shift>..<(n+1 << gamma_shift)-1>
-       *
-       */
-      if (sig_bit > 0 && sig_bit < 16U)
-         /* shift == insignificant bits */
-         shift = (png_byte)((16U - sig_bit) & 0xff);
-
-      else
-         shift = 0; /* keep all 16 bits */
-
-      if ((png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) != 0)
-      {
-         /* PNG_MAX_GAMMA_8 is the number of bits to keep - effectively
-          * the significant bits in the *input* when the output will
-          * eventually be 8 bits.  By default it is 11.
-          */
-         if (shift < (16U - PNG_MAX_GAMMA_8))
-            shift = (16U - PNG_MAX_GAMMA_8);
-      }
-
-      if (shift > 8U)
-         shift = 8U; /* Guarantees at least one table! */
-
-      png_ptr->gamma_shift = shift;
-
-      /* NOTE: prior to 1.5.4 this test used to include PNG_BACKGROUND (now
-       * PNG_COMPOSE).  This effectively smashed the background calculation for
-       * 16-bit output because the 8-bit table assumes the result will be
-       * reduced to 8 bits.
-       */
-      if ((png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) != 0)
-          png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift,
-          png_ptr->screen_gamma > 0 ? png_product2(png_ptr->colorspace.gamma,
-          png_ptr->screen_gamma) : PNG_FP_1);
-
-      else
-          png_build_16bit_table(png_ptr, &png_ptr->gamma_16_table, shift,
-          png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma,
-          png_ptr->screen_gamma) : PNG_FP_1);
-
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
-   defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
-   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
-      if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0)
-      {
-         png_build_16bit_table(png_ptr, &png_ptr->gamma_16_to_1, shift,
-             png_reciprocal(png_ptr->colorspace.gamma));
-
-         /* Notice that the '16 from 1' table should be full precision, however
-          * the lookup on this table still uses gamma_shift, so it can't be.
-          * TODO: fix this.
-          */
-         png_build_16bit_table(png_ptr, &png_ptr->gamma_16_from_1, shift,
-             png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) :
-             png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */);
-      }
-#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
-   }
-#endif /* 16BIT */
-}
-#endif /* READ_GAMMA */
-
-/* HARDWARE OR SOFTWARE OPTION SUPPORT */
-#ifdef PNG_SET_OPTION_SUPPORTED
-int PNGAPI
-png_set_option(png_structrp png_ptr, int option, int onoff)
-{
-   if (png_ptr != NULL && option >= 0 && option < PNG_OPTION_NEXT &&
-      (option & 1) == 0)
-   {
-      png_uint_32 mask = 3U << option;
-      png_uint_32 setting = (2U + (onoff != 0)) << option;
-      png_uint_32 current = png_ptr->options;
-
-      png_ptr->options = (png_uint_32)((current & ~mask) | setting);
-
-      return (int)(current & mask) >> option;
-   }
-
-   return PNG_OPTION_INVALID;
-}
-#endif
-
-/* sRGB support */
-#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
-   defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
-/* sRGB conversion tables; these are machine generated with the code in
- * contrib/tools/makesRGB.c.  The actual sRGB transfer curve defined in the
- * specification (see the article at https://en.wikipedia.org/wiki/SRGB)
- * is used, not the gamma=1/2.2 approximation use elsewhere in libpng.
- * The sRGB to linear table is exact (to the nearest 16-bit linear fraction).
- * The inverse (linear to sRGB) table has accuracies as follows:
- *
- * For all possible (255*65535+1) input values:
- *
- *    error: -0.515566 - 0.625971, 79441 (0.475369%) of readings inexact
- *
- * For the input values corresponding to the 65536 16-bit values:
- *
- *    error: -0.513727 - 0.607759, 308 (0.469978%) of readings inexact
- *
- * In all cases the inexact readings are only off by one.
- */
-
-#ifdef PNG_SIMPLIFIED_READ_SUPPORTED
-/* The convert-to-sRGB table is only currently required for read. */
-const png_uint_16 png_sRGB_table[256] =
-{
-   0,20,40,60,80,99,119,139,
-   159,179,199,219,241,264,288,313,
-   340,367,396,427,458,491,526,562,
-   599,637,677,718,761,805,851,898,
-   947,997,1048,1101,1156,1212,1270,1330,
-   1391,1453,1517,1583,1651,1720,1790,1863,
-   1937,2013,2090,2170,2250,2333,2418,2504,
-   2592,2681,2773,2866,2961,3058,3157,3258,
-   3360,3464,3570,3678,3788,3900,4014,4129,
-   4247,4366,4488,4611,4736,4864,4993,5124,
-   5257,5392,5530,5669,5810,5953,6099,6246,
-   6395,6547,6700,6856,7014,7174,7335,7500,
-   7666,7834,8004,8177,8352,8528,8708,8889,
-   9072,9258,9445,9635,9828,10022,10219,10417,
-   10619,10822,11028,11235,11446,11658,11873,12090,
-   12309,12530,12754,12980,13209,13440,13673,13909,
-   14146,14387,14629,14874,15122,15371,15623,15878,
-   16135,16394,16656,16920,17187,17456,17727,18001,
-   18277,18556,18837,19121,19407,19696,19987,20281,
-   20577,20876,21177,21481,21787,22096,22407,22721,
-   23038,23357,23678,24002,24329,24658,24990,25325,
-   25662,26001,26344,26688,27036,27386,27739,28094,
-   28452,28813,29176,29542,29911,30282,30656,31033,
-   31412,31794,32179,32567,32957,33350,33745,34143,
-   34544,34948,35355,35764,36176,36591,37008,37429,
-   37852,38278,38706,39138,39572,40009,40449,40891,
-   41337,41785,42236,42690,43147,43606,44069,44534,
-   45002,45473,45947,46423,46903,47385,47871,48359,
-   48850,49344,49841,50341,50844,51349,51858,52369,
-   52884,53401,53921,54445,54971,55500,56032,56567,
-   57105,57646,58190,58737,59287,59840,60396,60955,
-   61517,62082,62650,63221,63795,64372,64952,65535
-};
-#endif /* SIMPLIFIED_READ */
-
-/* The base/delta tables are required for both read and write (but currently
- * only the simplified versions.)
- */
-const png_uint_16 png_sRGB_base[512] =
-{
-   128,1782,3383,4644,5675,6564,7357,8074,
-   8732,9346,9921,10463,10977,11466,11935,12384,
-   12816,13233,13634,14024,14402,14769,15125,15473,
-   15812,16142,16466,16781,17090,17393,17690,17981,
-   18266,18546,18822,19093,19359,19621,19879,20133,
-   20383,20630,20873,21113,21349,21583,21813,22041,
-   22265,22487,22707,22923,23138,23350,23559,23767,
-   23972,24175,24376,24575,24772,24967,25160,25352,
-   25542,25730,25916,26101,26284,26465,26645,26823,
-   27000,27176,27350,27523,27695,27865,28034,28201,
-   28368,28533,28697,28860,29021,29182,29341,29500,
-   29657,29813,29969,30123,30276,30429,30580,30730,
-   30880,31028,31176,31323,31469,31614,31758,31902,
-   32045,32186,32327,32468,32607,32746,32884,33021,
-   33158,33294,33429,33564,33697,33831,33963,34095,
-   34226,34357,34486,34616,34744,34873,35000,35127,
-   35253,35379,35504,35629,35753,35876,35999,36122,
-   36244,36365,36486,36606,36726,36845,36964,37083,
-   37201,37318,37435,37551,37668,37783,37898,38013,
-   38127,38241,38354,38467,38580,38692,38803,38915,
-   39026,39136,39246,39356,39465,39574,39682,39790,
-   39898,40005,40112,40219,40325,40431,40537,40642,
-   40747,40851,40955,41059,41163,41266,41369,41471,
-   41573,41675,41777,41878,41979,42079,42179,42279,
-   42379,42478,42577,42676,42775,42873,42971,43068,
-   43165,43262,43359,43456,43552,43648,43743,43839,
-   43934,44028,44123,44217,44311,44405,44499,44592,
-   44685,44778,44870,44962,45054,45146,45238,45329,
-   45420,45511,45601,45692,45782,45872,45961,46051,
-   46140,46229,46318,46406,46494,46583,46670,46758,
-   46846,46933,47020,47107,47193,47280,47366,47452,
-   47538,47623,47709,47794,47879,47964,48048,48133,
-   48217,48301,48385,48468,48552,48635,48718,48801,
-   48884,48966,49048,49131,49213,49294,49376,49458,
-   49539,49620,49701,49782,49862,49943,50023,50103,
-   50183,50263,50342,50422,50501,50580,50659,50738,
-   50816,50895,50973,51051,51129,51207,51285,51362,
-   51439,51517,51594,51671,51747,51824,51900,51977,
-   52053,52129,52205,52280,52356,52432,52507,52582,
-   52657,52732,52807,52881,52956,53030,53104,53178,
-   53252,53326,53400,53473,53546,53620,53693,53766,
-   53839,53911,53984,54056,54129,54201,54273,54345,
-   54417,54489,54560,54632,54703,54774,54845,54916,
-   54987,55058,55129,55199,55269,55340,55410,55480,
-   55550,55620,55689,55759,55828,55898,55967,56036,
-   56105,56174,56243,56311,56380,56448,56517,56585,
-   56653,56721,56789,56857,56924,56992,57059,57127,
-   57194,57261,57328,57395,57462,57529,57595,57662,
-   57728,57795,57861,57927,57993,58059,58125,58191,
-   58256,58322,58387,58453,58518,58583,58648,58713,
-   58778,58843,58908,58972,59037,59101,59165,59230,
-   59294,59358,59422,59486,59549,59613,59677,59740,
-   59804,59867,59930,59993,60056,60119,60182,60245,
-   60308,60370,60433,60495,60558,60620,60682,60744,
-   60806,60868,60930,60992,61054,61115,61177,61238,
-   61300,61361,61422,61483,61544,61605,61666,61727,
-   61788,61848,61909,61969,62030,62090,62150,62211,
-   62271,62331,62391,62450,62510,62570,62630,62689,
-   62749,62808,62867,62927,62986,63045,63104,63163,
-   63222,63281,63340,63398,63457,63515,63574,63632,
-   63691,63749,63807,63865,63923,63981,64039,64097,
-   64155,64212,64270,64328,64385,64443,64500,64557,
-   64614,64672,64729,64786,64843,64900,64956,65013,
-   65070,65126,65183,65239,65296,65352,65409,65465
-};
-
-const png_byte png_sRGB_delta[512] =
-{
-   207,201,158,129,113,100,90,82,77,72,68,64,61,59,56,54,
-   52,50,49,47,46,45,43,42,41,40,39,39,38,37,36,36,
-   35,34,34,33,33,32,32,31,31,30,30,30,29,29,28,28,
-   28,27,27,27,27,26,26,26,25,25,25,25,24,24,24,24,
-   23,23,23,23,23,22,22,22,22,22,22,21,21,21,21,21,
-   21,20,20,20,20,20,20,20,20,19,19,19,19,19,19,19,
-   19,18,18,18,18,18,18,18,18,18,18,17,17,17,17,17,
-   17,17,17,17,17,17,16,16,16,16,16,16,16,16,16,16,
-   16,16,16,16,15,15,15,15,15,15,15,15,15,15,15,15,
-   15,15,15,15,14,14,14,14,14,14,14,14,14,14,14,14,
-   14,14,14,14,14,14,14,13,13,13,13,13,13,13,13,13,
-   13,13,13,13,13,13,13,13,13,13,13,13,13,13,12,12,
-   12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,
-   12,12,12,12,12,12,12,12,12,12,12,12,11,11,11,11,
-   11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
-   11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
-   11,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
-   10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
-   10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
-   10,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
-   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
-   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
-   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
-   9,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
-   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
-   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
-   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
-   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
-   8,8,8,8,8,8,8,8,8,7,7,7,7,7,7,7,
-   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
-};
-#endif /* SIMPLIFIED READ/WRITE sRGB support */
-
-/* SIMPLIFIED READ/WRITE SUPPORT */
-#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
-   defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
-static int
-png_image_free_function(png_voidp argument)
-{
-   png_imagep image = png_voidcast(png_imagep, argument);
-   png_controlp cp = image->opaque;
-   png_control c;
-
-   /* Double check that we have a png_ptr - it should be impossible to get here
-    * without one.
-    */
-   if (cp->png_ptr == NULL)
-      return 0;
-
-   /* First free any data held in the control structure. */
-#  ifdef PNG_STDIO_SUPPORTED
-      if (cp->owned_file != 0)
-      {
-         FILE *fp = png_voidcast(FILE*, cp->png_ptr->io_ptr);
-         cp->owned_file = 0;
-
-         /* Ignore errors here. */
-         if (fp != NULL)
-         {
-            cp->png_ptr->io_ptr = NULL;
-            (void)fclose(fp);
-         }
-      }
-#  endif
-
-   /* Copy the control structure so that the original, allocated, version can be
-    * safely freed.  Notice that a png_error here stops the remainder of the
-    * cleanup, but this is probably fine because that would indicate bad memory
-    * problems anyway.
-    */
-   c = *cp;
-   image->opaque = &c;
-   png_free(c.png_ptr, cp);
-
-   /* Then the structures, calling the correct API. */
-   if (c.for_write != 0)
-   {
-#     ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED
-         png_destroy_write_struct(&c.png_ptr, &c.info_ptr);
-#     else
-         png_error(c.png_ptr, "simplified write not supported");
-#     endif
-   }
-   else
-   {
-#     ifdef PNG_SIMPLIFIED_READ_SUPPORTED
-         png_destroy_read_struct(&c.png_ptr, &c.info_ptr, NULL);
-#     else
-         png_error(c.png_ptr, "simplified read not supported");
-#     endif
-   }
-
-   /* Success. */
-   return 1;
-}
-
-void PNGAPI
-png_image_free(png_imagep image)
-{
-   /* Safely call the real function, but only if doing so is safe at this point
-    * (if not inside an error handling context).  Otherwise assume
-    * png_safe_execute will call this API after the return.
-    */
-   if (image != NULL && image->opaque != NULL &&
-      image->opaque->error_buf == NULL)
-   {
-      png_image_free_function(image);
-      image->opaque = NULL;
-   }
-}
-
-int /* PRIVATE */
-png_image_error(png_imagep image, png_const_charp error_message)
-{
-   /* Utility to log an error. */
-   png_safecat(image->message, (sizeof image->message), 0, error_message);
-   image->warning_or_error |= PNG_IMAGE_ERROR;
-   png_image_free(image);
-   return 0;
-}
-
-#endif /* SIMPLIFIED READ/WRITE */
-#endif /* READ || WRITE */
diff --git a/third_party/libpng16/png.h b/third_party/libpng16/png.h
deleted file mode 100644
index 139eb0d..0000000
--- a/third_party/libpng16/png.h
+++ /dev/null
@@ -1,3247 +0,0 @@
-
-/* png.h - header file for PNG reference library
- *
- * libpng version 1.6.37 - April 14, 2019
- *
- * Copyright (c) 2018-2019 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license. (See LICENSE, below.)
- *
- * Authors and maintainers:
- *   libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat
- *   libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger
- *   libpng versions 0.97, January 1998, through 1.6.35, July 2018:
- *     Glenn Randers-Pehrson
- *   libpng versions 1.6.36, December 2018, through 1.6.37, April 2019:
- *     Cosmin Truta
- *   See also "Contributing Authors", below.
- */
-
-/*
- * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE
- * =========================================
- *
- * PNG Reference Library License version 2
- * ---------------------------------------
- *
- *  * Copyright (c) 1995-2019 The PNG Reference Library Authors.
- *  * Copyright (c) 2018-2019 Cosmin Truta.
- *  * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
- *  * Copyright (c) 1996-1997 Andreas Dilger.
- *  * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * The software is supplied "as is", without warranty of any kind,
- * express or implied, including, without limitation, the warranties
- * of merchantability, fitness for a particular purpose, title, and
- * non-infringement.  In no event shall the Copyright owners, or
- * anyone distributing the software, be liable for any damages or
- * other liability, whether in contract, tort or otherwise, arising
- * from, out of, or in connection with the software, or the use or
- * other dealings in the software, even if advised of the possibility
- * of such damage.
- *
- * Permission is hereby granted to use, copy, modify, and distribute
- * this software, or portions hereof, for any purpose, without fee,
- * subject to the following restrictions:
- *
- *  1. The origin of this software must not be misrepresented; you
- *     must not claim that you wrote the original software.  If you
- *     use this software in a product, an acknowledgment in the product
- *     documentation would be appreciated, but is not required.
- *
- *  2. Altered source versions must be plainly marked as such, and must
- *     not be misrepresented as being the original software.
- *
- *  3. This Copyright notice may not be removed or altered from any
- *     source or altered source distribution.
- *
- *
- * PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35)
- * -----------------------------------------------------------------------
- *
- * libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are
- * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are
- * derived from libpng-1.0.6, and are distributed according to the same
- * disclaimer and license as libpng-1.0.6 with the following individuals
- * added to the list of Contributing Authors:
- *
- *     Simon-Pierre Cadieux
- *     Eric S. Raymond
- *     Mans Rullgard
- *     Cosmin Truta
- *     Gilles Vollant
- *     James Yu
- *     Mandar Sahastrabuddhe
- *     Google Inc.
- *     Vadim Barkov
- *
- * and with the following additions to the disclaimer:
- *
- *     There is no warranty against interference with your enjoyment of
- *     the library or against infringement.  There is no warranty that our
- *     efforts or the library will fulfill any of your particular purposes
- *     or needs.  This library is provided with all faults, and the entire
- *     risk of satisfactory quality, performance, accuracy, and effort is
- *     with the user.
- *
- * Some files in the "contrib" directory and some configure-generated
- * files that are distributed with libpng have other copyright owners, and
- * are released under other open source licenses.
- *
- * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
- * Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from
- * libpng-0.96, and are distributed according to the same disclaimer and
- * license as libpng-0.96, with the following individuals added to the
- * list of Contributing Authors:
- *
- *     Tom Lane
- *     Glenn Randers-Pehrson
- *     Willem van Schaik
- *
- * libpng versions 0.89, June 1996, through 0.96, May 1997, are
- * Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88,
- * and are distributed according to the same disclaimer and license as
- * libpng-0.88, with the following individuals added to the list of
- * Contributing Authors:
- *
- *     John Bowler
- *     Kevin Bracey
- *     Sam Bushell
- *     Magnus Holmgren
- *     Greg Roelofs
- *     Tom Tanner
- *
- * Some files in the "scripts" directory have other copyright owners,
- * but are released under this license.
- *
- * libpng versions 0.5, May 1995, through 0.88, January 1996, are
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * For the purposes of this copyright and license, "Contributing Authors"
- * is defined as the following set of individuals:
- *
- *     Andreas Dilger
- *     Dave Martindale
- *     Guy Eric Schalnat
- *     Paul Schmidt
- *     Tim Wegner
- *
- * The PNG Reference Library is supplied "AS IS".  The Contributing
- * Authors and Group 42, Inc. disclaim all warranties, expressed or
- * implied, including, without limitation, the warranties of
- * merchantability and of fitness for any purpose.  The Contributing
- * Authors and Group 42, Inc. assume no liability for direct, indirect,
- * incidental, special, exemplary, or consequential damages, which may
- * result from the use of the PNG Reference Library, even if advised of
- * the possibility of such damage.
- *
- * Permission is hereby granted to use, copy, modify, and distribute this
- * source code, or portions hereof, for any purpose, without fee, subject
- * to the following restrictions:
- *
- *  1. The origin of this source code must not be misrepresented.
- *
- *  2. Altered versions must be plainly marked as such and must not
- *     be misrepresented as being the original source.
- *
- *  3. This Copyright notice may not be removed or altered from any
- *     source or altered source distribution.
- *
- * The Contributing Authors and Group 42, Inc. specifically permit,
- * without fee, and encourage the use of this source code as a component
- * to supporting the PNG file format in commercial products.  If you use
- * this source code in a product, acknowledgment is not required but would
- * be appreciated.
- *
- * END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE.
- *
- * TRADEMARK
- * =========
- *
- * The name "libpng" has not been registered by the Copyright owners
- * as a trademark in any jurisdiction.  However, because libpng has
- * been distributed and maintained world-wide, continually since 1995,
- * the Copyright owners claim "common-law trademark protection" in any
- * jurisdiction where common-law trademark is recognized.
- */
-
-/*
- * A "png_get_copyright" function is available, for convenient use in "about"
- * boxes and the like:
- *
- *    printf("%s", png_get_copyright(NULL));
- *
- * Also, the PNG logo (in PNG format, of course) is supplied in the
- * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31).
- */
-
-/*
- * The contributing authors would like to thank all those who helped
- * with testing, bug fixes, and patience.  This wouldn't have been
- * possible without all of you.
- *
- * Thanks to Frank J. T. Wojcik for helping with the documentation.
- */
-
-/* Note about libpng version numbers:
- *
- *    Due to various miscommunications, unforeseen code incompatibilities
- *    and occasional factors outside the authors' control, version numbering
- *    on the library has not always been consistent and straightforward.
- *    The following table summarizes matters since version 0.89c, which was
- *    the first widely used release:
- *
- *    source                 png.h  png.h  shared-lib
- *    version                string   int  version
- *    -------                ------ -----  ----------
- *    0.89c "1.0 beta 3"     0.89      89  1.0.89
- *    0.90  "1.0 beta 4"     0.90      90  0.90  [should have been 2.0.90]
- *    0.95  "1.0 beta 5"     0.95      95  0.95  [should have been 2.0.95]
- *    0.96  "1.0 beta 6"     0.96      96  0.96  [should have been 2.0.96]
- *    0.97b "1.00.97 beta 7" 1.00.97   97  1.0.1 [should have been 2.0.97]
- *    0.97c                  0.97      97  2.0.97
- *    0.98                   0.98      98  2.0.98
- *    0.99                   0.99      98  2.0.99
- *    0.99a-m                0.99      99  2.0.99
- *    1.00                   1.00     100  2.1.0 [100 should be 10000]
- *    1.0.0      (from here on, the   100  2.1.0 [100 should be 10000]
- *    1.0.1       png.h string is   10001  2.1.0
- *    1.0.1a-e    identical to the  10002  from here on, the shared library
- *    1.0.2       source version)   10002  is 2.V where V is the source code
- *    1.0.2a-b                      10003  version, except as noted.
- *    1.0.3                         10003
- *    1.0.3a-d                      10004
- *    1.0.4                         10004
- *    1.0.4a-f                      10005
- *    1.0.5 (+ 2 patches)           10005
- *    1.0.5a-d                      10006
- *    1.0.5e-r                      10100 (not source compatible)
- *    1.0.5s-v                      10006 (not binary compatible)
- *    1.0.6 (+ 3 patches)           10006 (still binary incompatible)
- *    1.0.6d-f                      10007 (still binary incompatible)
- *    1.0.6g                        10007
- *    1.0.6h                        10007  10.6h (testing xy.z so-numbering)
- *    1.0.6i                        10007  10.6i
- *    1.0.6j                        10007  2.1.0.6j (incompatible with 1.0.0)
- *    1.0.7beta11-14        DLLNUM  10007  2.1.0.7beta11-14 (binary compatible)
- *    1.0.7beta15-18           1    10007  2.1.0.7beta15-18 (binary compatible)
- *    1.0.7rc1-2               1    10007  2.1.0.7rc1-2 (binary compatible)
- *    1.0.7                    1    10007  (still compatible)
- *    ...
- *    1.0.69                  10    10069  10.so.0.69[.0]
- *    ...
- *    1.2.59                  13    10259  12.so.0.59[.0]
- *    ...
- *    1.4.20                  14    10420  14.so.0.20[.0]
- *    ...
- *    1.5.30                  15    10530  15.so.15.30[.0]
- *    ...
- *    1.6.37                  16    10637  16.so.16.37[.0]
- *
- *    Henceforth the source version will match the shared-library major and
- *    minor numbers; the shared-library major version number will be used for
- *    changes in backward compatibility, as it is intended.
- *    The PNG_LIBPNG_VER macro, which is not used within libpng but is
- *    available for applications, is an unsigned integer of the form XYYZZ
- *    corresponding to the source version X.Y.Z (leading zeros in Y and Z).
- *    Beta versions were given the previous public release number plus a
- *    letter, until version 1.0.6j; from then on they were given the upcoming
- *    public release number plus "betaNN" or "rcNN".
- *
- *    Binary incompatibility exists only when applications make direct access
- *    to the info_ptr or png_ptr members through png.h, and the compiled
- *    application is loaded with a different version of the library.
- *
- *    DLLNUM will change each time there are forward or backward changes
- *    in binary compatibility (e.g., when a new feature is added).
- *
- * See libpng.txt or libpng.3 for more information.  The PNG specification
- * is available as a W3C Recommendation and as an ISO/IEC Standard; see
- * <https://www.w3.org/TR/2003/REC-PNG-20031110/>
- */
-
-#ifndef PNG_H
-#define PNG_H
-
-/* This is not the place to learn how to use libpng. The file libpng-manual.txt
- * describes how to use libpng, and the file example.c summarizes it
- * with some code on which to build.  This file is useful for looking
- * at the actual function definitions and structure components.  If that
- * file has been stripped from your copy of libpng, you can find it at
- * <http://www.libpng.org/pub/png/libpng-manual.txt>
- *
- * If you just need to read a PNG file and don't want to read the documentation
- * skip to the end of this file and read the section entitled 'simplified API'.
- */
-
-/* Version information for png.h - this should match the version in png.c */
-#define PNG_LIBPNG_VER_STRING "1.6.37"
-#define PNG_HEADER_VERSION_STRING " libpng version 1.6.37 - April 14, 2019\n"
-
-#define PNG_LIBPNG_VER_SONUM   16
-#define PNG_LIBPNG_VER_DLLNUM  16
-
-/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
-#define PNG_LIBPNG_VER_MAJOR   1
-#define PNG_LIBPNG_VER_MINOR   6
-#define PNG_LIBPNG_VER_RELEASE 37
-
-/* This should be zero for a public release, or non-zero for a
- * development version.  [Deprecated]
- */
-#define PNG_LIBPNG_VER_BUILD  0
-
-/* Release Status */
-#define PNG_LIBPNG_BUILD_ALPHA    1
-#define PNG_LIBPNG_BUILD_BETA     2
-#define PNG_LIBPNG_BUILD_RC       3
-#define PNG_LIBPNG_BUILD_STABLE   4
-#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7
-
-/* Release-Specific Flags */
-#define PNG_LIBPNG_BUILD_PATCH    8 /* Can be OR'ed with
-                                       PNG_LIBPNG_BUILD_STABLE only */
-#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with
-                                       PNG_LIBPNG_BUILD_SPECIAL */
-#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with
-                                       PNG_LIBPNG_BUILD_PRIVATE */
-
-#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE
-
-/* Careful here.  At one time, Guy wanted to use 082, but that
- * would be octal.  We must not include leading zeros.
- * Versions 0.7 through 1.0.0 were in the range 0 to 100 here
- * (only version 1.0.0 was mis-numbered 100 instead of 10000).
- * From version 1.0.1 it is:
- * XXYYZZ, where XX=major, YY=minor, ZZ=release
- */
-#define PNG_LIBPNG_VER 10637 /* 1.6.37 */
-
-/* Library configuration: these options cannot be changed after
- * the library has been built.
- */
-#ifndef PNGLCONF_H
-/* If pnglibconf.h is missing, you can
- * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h
- */
-#   include "pnglibconf.h"
-#endif
-
-#ifndef PNG_VERSION_INFO_ONLY
-/* Machine specific configuration. */
-#  include "pngconf.h"
-#endif
-
-/*
- * Added at libpng-1.2.8
- *
- * Ref MSDN: Private as priority over Special
- * VS_FF_PRIVATEBUILD File *was not* built using standard release
- * procedures. If this value is given, the StringFileInfo block must
- * contain a PrivateBuild string.
- *
- * VS_FF_SPECIALBUILD File *was* built by the original company using
- * standard release procedures but is a variation of the standard
- * file of the same version number. If this value is given, the
- * StringFileInfo block must contain a SpecialBuild string.
- */
-
-#ifdef PNG_USER_PRIVATEBUILD /* From pnglibconf.h */
-#  define PNG_LIBPNG_BUILD_TYPE \
-       (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE)
-#else
-#  ifdef PNG_LIBPNG_SPECIALBUILD
-#    define PNG_LIBPNG_BUILD_TYPE \
-         (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL)
-#  else
-#    define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE)
-#  endif
-#endif
-
-#ifndef PNG_VERSION_INFO_ONLY
-
-/* Inhibit C++ name-mangling for libpng functions but not for system calls. */
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-/* Version information for C files, stored in png.c.  This had better match
- * the version above.
- */
-#define png_libpng_ver png_get_header_ver(NULL)
-
-/* This file is arranged in several sections:
- *
- * 1. [omitted]
- * 2. Any configuration options that can be specified by for the application
- *    code when it is built.  (Build time configuration is in pnglibconf.h)
- * 3. Type definitions (base types are defined in pngconf.h), structure
- *    definitions.
- * 4. Exported library functions.
- * 5. Simplified API.
- * 6. Implementation options.
- *
- * The library source code has additional files (principally pngpriv.h) that
- * allow configuration of the library.
- */
-
-/* Section 1: [omitted] */
-
-/* Section 2: run time configuration
- * See pnglibconf.h for build time configuration
- *
- * Run time configuration allows the application to choose between
- * implementations of certain arithmetic APIs.  The default is set
- * at build time and recorded in pnglibconf.h, but it is safe to
- * override these (and only these) settings.  Note that this won't
- * change what the library does, only application code, and the
- * settings can (and probably should) be made on a per-file basis
- * by setting the #defines before including png.h
- *
- * Use macros to read integers from PNG data or use the exported
- * functions?
- *   PNG_USE_READ_MACROS: use the macros (see below)  Note that
- *     the macros evaluate their argument multiple times.
- *   PNG_NO_USE_READ_MACROS: call the relevant library function.
- *
- * Use the alternative algorithm for compositing alpha samples that
- * does not use division?
- *   PNG_READ_COMPOSITE_NODIV_SUPPORTED: use the 'no division'
- *      algorithm.
- *   PNG_NO_READ_COMPOSITE_NODIV: use the 'division' algorithm.
- *
- * How to handle benign errors if PNG_ALLOW_BENIGN_ERRORS is
- * false?
- *   PNG_ALLOW_BENIGN_ERRORS: map calls to the benign error
- *      APIs to png_warning.
- * Otherwise the calls are mapped to png_error.
- */
-
-/* Section 3: type definitions, including structures and compile time
- * constants.
- * See pngconf.h for base types that vary by machine/system
- */
-
-/* This triggers a compiler error in png.c, if png.c and png.h
- * do not agree upon the version number.
- */
-typedef char* png_libpng_version_1_6_37;
-
-/* Basic control structions.  Read libpng-manual.txt or libpng.3 for more info.
- *
- * png_struct is the cache of information used while reading or writing a single
- * PNG file.  One of these is always required, although the simplified API
- * (below) hides the creation and destruction of it.
- */
-typedef struct png_struct_def png_struct;
-typedef const png_struct * png_const_structp;
-typedef png_struct * png_structp;
-typedef png_struct * * png_structpp;
-
-/* png_info contains information read from or to be written to a PNG file.  One
- * or more of these must exist while reading or creating a PNG file.  The
- * information is not used by libpng during read but is used to control what
- * gets written when a PNG file is created.  "png_get_" function calls read
- * information during read and "png_set_" functions calls write information
- * when creating a PNG.
- * been moved into a separate header file that is not accessible to
- * applications.  Read libpng-manual.txt or libpng.3 for more info.
- */
-typedef struct png_info_def png_info;
-typedef png_info * png_infop;
-typedef const png_info * png_const_infop;
-typedef png_info * * png_infopp;
-
-/* Types with names ending 'p' are pointer types.  The corresponding types with
- * names ending 'rp' are identical pointer types except that the pointer is
- * marked 'restrict', which means that it is the only pointer to the object
- * passed to the function.  Applications should not use the 'restrict' types;
- * it is always valid to pass 'p' to a pointer with a function argument of the
- * corresponding 'rp' type.  Different compilers have different rules with
- * regard to type matching in the presence of 'restrict'.  For backward
- * compatibility libpng callbacks never have 'restrict' in their parameters and,
- * consequentially, writing portable application code is extremely difficult if
- * an attempt is made to use 'restrict'.
- */
-typedef png_struct * PNG_RESTRICT png_structrp;
-typedef const png_struct * PNG_RESTRICT png_const_structrp;
-typedef png_info * PNG_RESTRICT png_inforp;
-typedef const png_info * PNG_RESTRICT png_const_inforp;
-
-/* Three color definitions.  The order of the red, green, and blue, (and the
- * exact size) is not important, although the size of the fields need to
- * be png_byte or png_uint_16 (as defined below).
- */
-typedef struct png_color_struct
-{
-   png_byte red;
-   png_byte green;
-   png_byte blue;
-} png_color;
-typedef png_color * png_colorp;
-typedef const png_color * png_const_colorp;
-typedef png_color * * png_colorpp;
-
-typedef struct png_color_16_struct
-{
-   png_byte index;    /* used for palette files */
-   png_uint_16 red;   /* for use in red green blue files */
-   png_uint_16 green;
-   png_uint_16 blue;
-   png_uint_16 gray;  /* for use in grayscale files */
-} png_color_16;
-typedef png_color_16 * png_color_16p;
-typedef const png_color_16 * png_const_color_16p;
-typedef png_color_16 * * png_color_16pp;
-
-typedef struct png_color_8_struct
-{
-   png_byte red;   /* for use in red green blue files */
-   png_byte green;
-   png_byte blue;
-   png_byte gray;  /* for use in grayscale files */
-   png_byte alpha; /* for alpha channel files */
-} png_color_8;
-typedef png_color_8 * png_color_8p;
-typedef const png_color_8 * png_const_color_8p;
-typedef png_color_8 * * png_color_8pp;
-
-/*
- * The following two structures are used for the in-core representation
- * of sPLT chunks.
- */
-typedef struct png_sPLT_entry_struct
-{
-   png_uint_16 red;
-   png_uint_16 green;
-   png_uint_16 blue;
-   png_uint_16 alpha;
-   png_uint_16 frequency;
-} png_sPLT_entry;
-typedef png_sPLT_entry * png_sPLT_entryp;
-typedef const png_sPLT_entry * png_const_sPLT_entryp;
-typedef png_sPLT_entry * * png_sPLT_entrypp;
-
-/*  When the depth of the sPLT palette is 8 bits, the color and alpha samples
- *  occupy the LSB of their respective members, and the MSB of each member
- *  is zero-filled.  The frequency member always occupies the full 16 bits.
- */
-
-typedef struct png_sPLT_struct
-{
-   png_charp name;           /* palette name */
-   png_byte depth;           /* depth of palette samples */
-   png_sPLT_entryp entries;  /* palette entries */
-   png_int_32 nentries;      /* number of palette entries */
-} png_sPLT_t;
-typedef png_sPLT_t * png_sPLT_tp;
-typedef const png_sPLT_t * png_const_sPLT_tp;
-typedef png_sPLT_t * * png_sPLT_tpp;
-
-#ifdef PNG_TEXT_SUPPORTED
-/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file,
- * and whether that contents is compressed or not.  The "key" field
- * points to a regular zero-terminated C string.  The "text" fields can be a
- * regular C string, an empty string, or a NULL pointer.
- * However, the structure returned by png_get_text() will always contain
- * the "text" field as a regular zero-terminated C string (possibly
- * empty), never a NULL pointer, so it can be safely used in printf() and
- * other string-handling functions.  Note that the "itxt_length", "lang", and
- * "lang_key" members of the structure only exist when the library is built
- * with iTXt chunk support.  Prior to libpng-1.4.0 the library was built by
- * default without iTXt support. Also note that when iTXt *is* supported,
- * the "lang" and "lang_key" fields contain NULL pointers when the
- * "compression" field contains * PNG_TEXT_COMPRESSION_NONE or
- * PNG_TEXT_COMPRESSION_zTXt. Note that the "compression value" is not the
- * same as what appears in the PNG tEXt/zTXt/iTXt chunk's "compression flag"
- * which is always 0 or 1, or its "compression method" which is always 0.
- */
-typedef struct png_text_struct
-{
-   int  compression;       /* compression value:
-                             -1: tEXt, none
-                              0: zTXt, deflate
-                              1: iTXt, none
-                              2: iTXt, deflate  */
-   png_charp key;          /* keyword, 1-79 character description of "text" */
-   png_charp text;         /* comment, may be an empty string (ie "")
-                              or a NULL pointer */
-   size_t text_length;     /* length of the text string */
-   size_t itxt_length;     /* length of the itxt string */
-   png_charp lang;         /* language code, 0-79 characters
-                              or a NULL pointer */
-   png_charp lang_key;     /* keyword translated UTF-8 string, 0 or more
-                              chars or a NULL pointer */
-} png_text;
-typedef png_text * png_textp;
-typedef const png_text * png_const_textp;
-typedef png_text * * png_textpp;
-#endif
-
-/* Supported compression types for text in PNG files (tEXt, and zTXt).
- * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */
-#define PNG_TEXT_COMPRESSION_NONE_WR -3
-#define PNG_TEXT_COMPRESSION_zTXt_WR -2
-#define PNG_TEXT_COMPRESSION_NONE    -1
-#define PNG_TEXT_COMPRESSION_zTXt     0
-#define PNG_ITXT_COMPRESSION_NONE     1
-#define PNG_ITXT_COMPRESSION_zTXt     2
-#define PNG_TEXT_COMPRESSION_LAST     3  /* Not a valid value */
-
-/* png_time is a way to hold the time in an machine independent way.
- * Two conversions are provided, both from time_t and struct tm.  There
- * is no portable way to convert to either of these structures, as far
- * as I know.  If you know of a portable way, send it to me.  As a side
- * note - PNG has always been Year 2000 compliant!
- */
-typedef struct png_time_struct
-{
-   png_uint_16 year; /* full year, as in, 1995 */
-   png_byte month;   /* month of year, 1 - 12 */
-   png_byte day;     /* day of month, 1 - 31 */
-   png_byte hour;    /* hour of day, 0 - 23 */
-   png_byte minute;  /* minute of hour, 0 - 59 */
-   png_byte second;  /* second of minute, 0 - 60 (for leap seconds) */
-} png_time;
-typedef png_time * png_timep;
-typedef const png_time * png_const_timep;
-typedef png_time * * png_timepp;
-
-#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) ||\
-   defined(PNG_USER_CHUNKS_SUPPORTED)
-/* png_unknown_chunk is a structure to hold queued chunks for which there is
- * no specific support.  The idea is that we can use this to queue
- * up private chunks for output even though the library doesn't actually
- * know about their semantics.
- *
- * The data in the structure is set by libpng on read and used on write.
- */
-typedef struct png_unknown_chunk_t
-{
-   png_byte name[5]; /* Textual chunk name with '\0' terminator */
-   png_byte *data;   /* Data, should not be modified on read! */
-   size_t size;
-
-   /* On write 'location' must be set using the flag values listed below.
-    * Notice that on read it is set by libpng however the values stored have
-    * more bits set than are listed below.  Always treat the value as a
-    * bitmask.  On write set only one bit - setting multiple bits may cause the
-    * chunk to be written in multiple places.
-    */
-   png_byte location; /* mode of operation at read time */
-}
-png_unknown_chunk;
-
-typedef png_unknown_chunk * png_unknown_chunkp;
-typedef const png_unknown_chunk * png_const_unknown_chunkp;
-typedef png_unknown_chunk * * png_unknown_chunkpp;
-#endif
-
-/* Flag values for the unknown chunk location byte. */
-#define PNG_HAVE_IHDR  0x01
-#define PNG_HAVE_PLTE  0x02
-#define PNG_AFTER_IDAT 0x08
-
-/* Maximum positive integer used in PNG is (2^31)-1 */
-#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL)
-#define PNG_UINT_32_MAX ((png_uint_32)(-1))
-#define PNG_SIZE_MAX ((size_t)(-1))
-
-/* These are constants for fixed point values encoded in the
- * PNG specification manner (x100000)
- */
-#define PNG_FP_1    100000
-#define PNG_FP_HALF  50000
-#define PNG_FP_MAX  ((png_fixed_point)0x7fffffffL)
-#define PNG_FP_MIN  (-PNG_FP_MAX)
-
-/* These describe the color_type field in png_info. */
-/* color type masks */
-#define PNG_COLOR_MASK_PALETTE    1
-#define PNG_COLOR_MASK_COLOR      2
-#define PNG_COLOR_MASK_ALPHA      4
-
-/* color types.  Note that not all combinations are legal */
-#define PNG_COLOR_TYPE_GRAY 0
-#define PNG_COLOR_TYPE_PALETTE  (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE)
-#define PNG_COLOR_TYPE_RGB        (PNG_COLOR_MASK_COLOR)
-#define PNG_COLOR_TYPE_RGB_ALPHA  (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA)
-#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA)
-/* aliases */
-#define PNG_COLOR_TYPE_RGBA  PNG_COLOR_TYPE_RGB_ALPHA
-#define PNG_COLOR_TYPE_GA  PNG_COLOR_TYPE_GRAY_ALPHA
-
-/* This is for compression type. PNG 1.0-1.2 only define the single type. */
-#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */
-#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE
-
-/* This is for filter type. PNG 1.0-1.2 only define the single type. */
-#define PNG_FILTER_TYPE_BASE      0 /* Single row per-byte filtering */
-#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */
-#define PNG_FILTER_TYPE_DEFAULT   PNG_FILTER_TYPE_BASE
-
-/* These are for the interlacing type.  These values should NOT be changed. */
-#define PNG_INTERLACE_NONE        0 /* Non-interlaced image */
-#define PNG_INTERLACE_ADAM7       1 /* Adam7 interlacing */
-#define PNG_INTERLACE_LAST        2 /* Not a valid value */
-
-/* These are for the oFFs chunk.  These values should NOT be changed. */
-#define PNG_OFFSET_PIXEL          0 /* Offset in pixels */
-#define PNG_OFFSET_MICROMETER     1 /* Offset in micrometers (1/10^6 meter) */
-#define PNG_OFFSET_LAST           2 /* Not a valid value */
-
-/* These are for the pCAL chunk.  These values should NOT be changed. */
-#define PNG_EQUATION_LINEAR       0 /* Linear transformation */
-#define PNG_EQUATION_BASE_E       1 /* Exponential base e transform */
-#define PNG_EQUATION_ARBITRARY    2 /* Arbitrary base exponential transform */
-#define PNG_EQUATION_HYPERBOLIC   3 /* Hyperbolic sine transformation */
-#define PNG_EQUATION_LAST         4 /* Not a valid value */
-
-/* These are for the sCAL chunk.  These values should NOT be changed. */
-#define PNG_SCALE_UNKNOWN         0 /* unknown unit (image scale) */
-#define PNG_SCALE_METER           1 /* meters per pixel */
-#define PNG_SCALE_RADIAN          2 /* radians per pixel */
-#define PNG_SCALE_LAST            3 /* Not a valid value */
-
-/* These are for the pHYs chunk.  These values should NOT be changed. */
-#define PNG_RESOLUTION_UNKNOWN    0 /* pixels/unknown unit (aspect ratio) */
-#define PNG_RESOLUTION_METER      1 /* pixels/meter */
-#define PNG_RESOLUTION_LAST       2 /* Not a valid value */
-
-/* These are for the sRGB chunk.  These values should NOT be changed. */
-#define PNG_sRGB_INTENT_PERCEPTUAL 0
-#define PNG_sRGB_INTENT_RELATIVE   1
-#define PNG_sRGB_INTENT_SATURATION 2
-#define PNG_sRGB_INTENT_ABSOLUTE   3
-#define PNG_sRGB_INTENT_LAST       4 /* Not a valid value */
-
-/* This is for text chunks */
-#define PNG_KEYWORD_MAX_LENGTH     79
-
-/* Maximum number of entries in PLTE/sPLT/tRNS arrays */
-#define PNG_MAX_PALETTE_LENGTH    256
-
-/* These determine if an ancillary chunk's data has been successfully read
- * from the PNG header, or if the application has filled in the corresponding
- * data in the info_struct to be written into the output file.  The values
- * of the PNG_INFO_<chunk> defines should NOT be changed.
- */
-#define PNG_INFO_gAMA 0x0001U
-#define PNG_INFO_sBIT 0x0002U
-#define PNG_INFO_cHRM 0x0004U
-#define PNG_INFO_PLTE 0x0008U
-#define PNG_INFO_tRNS 0x0010U
-#define PNG_INFO_bKGD 0x0020U
-#define PNG_INFO_hIST 0x0040U
-#define PNG_INFO_pHYs 0x0080U
-#define PNG_INFO_oFFs 0x0100U
-#define PNG_INFO_tIME 0x0200U
-#define PNG_INFO_pCAL 0x0400U
-#define PNG_INFO_sRGB 0x0800U  /* GR-P, 0.96a */
-#define PNG_INFO_iCCP 0x1000U  /* ESR, 1.0.6 */
-#define PNG_INFO_sPLT 0x2000U  /* ESR, 1.0.6 */
-#define PNG_INFO_sCAL 0x4000U  /* ESR, 1.0.6 */
-#define PNG_INFO_IDAT 0x8000U  /* ESR, 1.0.6 */
-#define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */
-
-/* This is used for the transformation routines, as some of them
- * change these values for the row.  It also should enable using
- * the routines for other purposes.
- */
-typedef struct png_row_info_struct
-{
-   png_uint_32 width;    /* width of row */
-   size_t rowbytes;      /* number of bytes in row */
-   png_byte color_type;  /* color type of row */
-   png_byte bit_depth;   /* bit depth of row */
-   png_byte channels;    /* number of channels (1, 2, 3, or 4) */
-   png_byte pixel_depth; /* bits per pixel (depth * channels) */
-} png_row_info;
-
-typedef png_row_info * png_row_infop;
-typedef png_row_info * * png_row_infopp;
-
-/* These are the function types for the I/O functions and for the functions
- * that allow the user to override the default I/O functions with his or her
- * own.  The png_error_ptr type should match that of user-supplied warning
- * and error functions, while the png_rw_ptr type should match that of the
- * user read/write data functions.  Note that the 'write' function must not
- * modify the buffer it is passed. The 'read' function, on the other hand, is
- * expected to return the read data in the buffer.
- */
-typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp));
-typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, size_t));
-typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp));
-typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32,
-    int));
-typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32,
-    int));
-
-#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
-typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop));
-typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop));
-
-/* The following callback receives png_uint_32 row_number, int pass for the
- * png_bytep data of the row.  When transforming an interlaced image the
- * row number is the row number within the sub-image of the interlace pass, so
- * the value will increase to the height of the sub-image (not the full image)
- * then reset to 0 for the next pass.
- *
- * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to
- * find the output pixel (x,y) given an interlaced sub-image pixel
- * (row,col,pass).  (See below for these macros.)
- */
-typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep,
-    png_uint_32, int));
-#endif
-
-#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
-    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
-typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop,
-    png_bytep));
-#endif
-
-#ifdef PNG_USER_CHUNKS_SUPPORTED
-typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp,
-    png_unknown_chunkp));
-#endif
-#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
-/* not used anywhere */
-/* typedef PNG_CALLBACK(void, *png_unknown_chunk_ptr, (png_structp)); */
-#endif
-
-#ifdef PNG_SETJMP_SUPPORTED
-/* This must match the function definition in <setjmp.h>, and the application
- * must include this before png.h to obtain the definition of jmp_buf.  The
- * function is required to be PNG_NORETURN, but this is not checked.  If the
- * function does return the application will crash via an abort() or similar
- * system level call.
- *
- * If you get a warning here while building the library you may need to make
- * changes to ensure that pnglibconf.h records the calling convention used by
- * your compiler.  This may be very difficult - try using a different compiler
- * to build the library!
- */
-PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef);
-#endif
-
-/* Transform masks for the high-level interface */
-#define PNG_TRANSFORM_IDENTITY       0x0000    /* read and write */
-#define PNG_TRANSFORM_STRIP_16       0x0001    /* read only */
-#define PNG_TRANSFORM_STRIP_ALPHA    0x0002    /* read only */
-#define PNG_TRANSFORM_PACKING        0x0004    /* read and write */
-#define PNG_TRANSFORM_PACKSWAP       0x0008    /* read and write */
-#define PNG_TRANSFORM_EXPAND         0x0010    /* read only */
-#define PNG_TRANSFORM_INVERT_MONO    0x0020    /* read and write */
-#define PNG_TRANSFORM_SHIFT          0x0040    /* read and write */
-#define PNG_TRANSFORM_BGR            0x0080    /* read and write */
-#define PNG_TRANSFORM_SWAP_ALPHA     0x0100    /* read and write */
-#define PNG_TRANSFORM_SWAP_ENDIAN    0x0200    /* read and write */
-#define PNG_TRANSFORM_INVERT_ALPHA   0x0400    /* read and write */
-#define PNG_TRANSFORM_STRIP_FILLER   0x0800    /* write only */
-/* Added to libpng-1.2.34 */
-#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER
-#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */
-/* Added to libpng-1.4.0 */
-#define PNG_TRANSFORM_GRAY_TO_RGB   0x2000      /* read only */
-/* Added to libpng-1.5.4 */
-#define PNG_TRANSFORM_EXPAND_16     0x4000      /* read only */
-#if INT_MAX >= 0x8000 /* else this might break */
-#define PNG_TRANSFORM_SCALE_16      0x8000      /* read only */
-#endif
-
-/* Flags for MNG supported features */
-#define PNG_FLAG_MNG_EMPTY_PLTE     0x01
-#define PNG_FLAG_MNG_FILTER_64      0x04
-#define PNG_ALL_MNG_FEATURES        0x05
-
-/* NOTE: prior to 1.5 these functions had no 'API' style declaration,
- * this allowed the zlib default functions to be used on Windows
- * platforms.  In 1.5 the zlib default malloc (which just calls malloc and
- * ignores the first argument) should be completely compatible with the
- * following.
- */
-typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp,
-    png_alloc_size_t));
-typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp));
-
-/* Section 4: exported functions
- * Here are the function definitions most commonly used.  This is not
- * the place to find out how to use libpng.  See libpng-manual.txt for the
- * full explanation, see example.c for the summary.  This just provides
- * a simple one line description of the use of each function.
- *
- * The PNG_EXPORT() and PNG_EXPORTA() macros used below are defined in
- * pngconf.h and in the *.dfn files in the scripts directory.
- *
- *   PNG_EXPORT(ordinal, type, name, (args));
- *
- *       ordinal:    ordinal that is used while building
- *                   *.def files. The ordinal value is only
- *                   relevant when preprocessing png.h with
- *                   the *.dfn files for building symbol table
- *                   entries, and are removed by pngconf.h.
- *       type:       return type of the function
- *       name:       function name
- *       args:       function arguments, with types
- *
- * When we wish to append attributes to a function prototype we use
- * the PNG_EXPORTA() macro instead.
- *
- *   PNG_EXPORTA(ordinal, type, name, (args), attributes);
- *
- *       ordinal, type, name, and args: same as in PNG_EXPORT().
- *       attributes: function attributes
- */
-
-/* Returns the version number of the library */
-PNG_EXPORT(1, png_uint_32, png_access_version_number, (void));
-
-/* Tell lib we have already handled the first <num_bytes> magic bytes.
- * Handling more than 8 bytes from the beginning of the file is an error.
- */
-PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes));
-
-/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a
- * PNG file.  Returns zero if the supplied bytes match the 8-byte PNG
- * signature, and non-zero otherwise.  Having num_to_check == 0 or
- * start > 7 will always fail (ie return non-zero).
- */
-PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start,
-    size_t num_to_check));
-
-/* Simple signature checking function.  This is the same as calling
- * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n).
- */
-#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n))
-
-/* Allocate and initialize png_ptr struct for reading, and any other memory. */
-PNG_EXPORTA(4, png_structp, png_create_read_struct,
-    (png_const_charp user_png_ver, png_voidp error_ptr,
-    png_error_ptr error_fn, png_error_ptr warn_fn),
-    PNG_ALLOCATED);
-
-/* Allocate and initialize png_ptr struct for writing, and any other memory */
-PNG_EXPORTA(5, png_structp, png_create_write_struct,
-    (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
-    png_error_ptr warn_fn),
-    PNG_ALLOCATED);
-
-PNG_EXPORT(6, size_t, png_get_compression_buffer_size,
-    (png_const_structrp png_ptr));
-
-PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr,
-    size_t size));
-
-/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp
- * match up.
- */
-#ifdef PNG_SETJMP_SUPPORTED
-/* This function returns the jmp_buf built in to *png_ptr.  It must be
- * supplied with an appropriate 'longjmp' function to use on that jmp_buf
- * unless the default error function is overridden in which case NULL is
- * acceptable.  The size of the jmp_buf is checked against the actual size
- * allocated by the library - the call will return NULL on a mismatch
- * indicating an ABI mismatch.
- */
-PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr,
-    png_longjmp_ptr longjmp_fn, size_t jmp_buf_size));
-#  define png_jmpbuf(png_ptr) \
-      (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf))))
-#else
-#  define png_jmpbuf(png_ptr) \
-      (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP)
-#endif
-/* This function should be used by libpng applications in place of
- * longjmp(png_ptr->jmpbuf, val).  If longjmp_fn() has been set, it
- * will use it; otherwise it will call PNG_ABORT().  This function was
- * added in libpng-1.5.0.
- */
-PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val),
-    PNG_NORETURN);
-
-#ifdef PNG_READ_SUPPORTED
-/* Reset the compression stream */
-PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED);
-#endif
-
-/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */
-#ifdef PNG_USER_MEM_SUPPORTED
-PNG_EXPORTA(11, png_structp, png_create_read_struct_2,
-    (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
-    png_error_ptr warn_fn,
-    png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn),
-    PNG_ALLOCATED);
-PNG_EXPORTA(12, png_structp, png_create_write_struct_2,
-    (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
-    png_error_ptr warn_fn,
-    png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn),
-    PNG_ALLOCATED);
-#endif
-
-/* Write the PNG file signature. */
-PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr));
-
-/* Write a PNG chunk - size, type, (optional) data, CRC. */
-PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep
-    chunk_name, png_const_bytep data, size_t length));
-
-/* Write the start of a PNG chunk - length and chunk name. */
-PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr,
-    png_const_bytep chunk_name, png_uint_32 length));
-
-/* Write the data of a PNG chunk started with png_write_chunk_start(). */
-PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr,
-    png_const_bytep data, size_t length));
-
-/* Finish a chunk started with png_write_chunk_start() (includes CRC). */
-PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr));
-
-/* Allocate and initialize the info structure */
-PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr),
-    PNG_ALLOCATED);
-
-/* DEPRECATED: this function allowed init structures to be created using the
- * default allocation method (typically malloc).  Use is deprecated in 1.6.0 and
- * the API will be removed in the future.
- */
-PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr,
-    size_t png_info_struct_size), PNG_DEPRECATED);
-
-/* Writes all the PNG information before the image. */
-PNG_EXPORT(20, void, png_write_info_before_PLTE,
-    (png_structrp png_ptr, png_const_inforp info_ptr));
-PNG_EXPORT(21, void, png_write_info,
-    (png_structrp png_ptr, png_const_inforp info_ptr));
-
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-/* Read the information before the actual image data. */
-PNG_EXPORT(22, void, png_read_info,
-    (png_structrp png_ptr, png_inforp info_ptr));
-#endif
-
-#ifdef PNG_TIME_RFC1123_SUPPORTED
-   /* Convert to a US string format: there is no localization support in this
-    * routine.  The original implementation used a 29 character buffer in
-    * png_struct, this will be removed in future versions.
-    */
-#if PNG_LIBPNG_VER < 10700
-/* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */
-PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr,
-    png_const_timep ptime),PNG_DEPRECATED);
-#endif
-PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29],
-    png_const_timep ptime));
-#endif
-
-#ifdef PNG_CONVERT_tIME_SUPPORTED
-/* Convert from a struct tm to png_time */
-PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime,
-    const struct tm * ttime));
-
-/* Convert from time_t to png_time.  Uses gmtime() */
-PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime));
-#endif /* CONVERT_tIME */
-
-#ifdef PNG_READ_EXPAND_SUPPORTED
-/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */
-PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr));
-PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr));
-PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr));
-PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr));
-#endif
-
-#ifdef PNG_READ_EXPAND_16_SUPPORTED
-/* Expand to 16-bit channels, forces conversion of palette to RGB and expansion
- * of a tRNS chunk if present.
- */
-PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr));
-#endif
-
-#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
-/* Use blue, green, red order for pixels. */
-PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr));
-#endif
-
-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
-/* Expand the grayscale to 24-bit RGB if necessary. */
-PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr));
-#endif
-
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-/* Reduce RGB to grayscale. */
-#define PNG_ERROR_ACTION_NONE  1
-#define PNG_ERROR_ACTION_WARN  2
-#define PNG_ERROR_ACTION_ERROR 3
-#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/
-
-PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr,
-    int error_action, double red, double green))
-PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr,
-    int error_action, png_fixed_point red, png_fixed_point green))
-
-PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp
-    png_ptr));
-#endif
-
-#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED
-PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth,
-    png_colorp palette));
-#endif
-
-#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
-/* How the alpha channel is interpreted - this affects how the color channels
- * of a PNG file are returned to the calling application when an alpha channel,
- * or a tRNS chunk in a palette file, is present.
- *
- * This has no effect on the way pixels are written into a PNG output
- * datastream. The color samples in a PNG datastream are never premultiplied
- * with the alpha samples.
- *
- * The default is to return data according to the PNG specification: the alpha
- * channel is a linear measure of the contribution of the pixel to the
- * corresponding composited pixel, and the color channels are unassociated
- * (not premultiplied).  The gamma encoded color channels must be scaled
- * according to the contribution and to do this it is necessary to undo
- * the encoding, scale the color values, perform the composition and re-encode
- * the values.  This is the 'PNG' mode.
- *
- * The alternative is to 'associate' the alpha with the color information by
- * storing color channel values that have been scaled by the alpha.
- * image.  These are the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' modes
- * (the latter being the two common names for associated alpha color channels).
- *
- * For the 'OPTIMIZED' mode, a pixel is treated as opaque only if the alpha
- * value is equal to the maximum value.
- *
- * The final choice is to gamma encode the alpha channel as well.  This is
- * broken because, in practice, no implementation that uses this choice
- * correctly undoes the encoding before handling alpha composition.  Use this
- * choice only if other serious errors in the software or hardware you use
- * mandate it; the typical serious error is for dark halos to appear around
- * opaque areas of the composited PNG image because of arithmetic overflow.
- *
- * The API function png_set_alpha_mode specifies which of these choices to use
- * with an enumerated 'mode' value and the gamma of the required output:
- */
-#define PNG_ALPHA_PNG           0 /* according to the PNG standard */
-#define PNG_ALPHA_STANDARD      1 /* according to Porter/Duff */
-#define PNG_ALPHA_ASSOCIATED    1 /* as above; this is the normal practice */
-#define PNG_ALPHA_PREMULTIPLIED 1 /* as above */
-#define PNG_ALPHA_OPTIMIZED     2 /* 'PNG' for opaque pixels, else 'STANDARD' */
-#define PNG_ALPHA_BROKEN        3 /* the alpha channel is gamma encoded */
-
-PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode,
-    double output_gamma))
-PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr,
-    int mode, png_fixed_point output_gamma))
-#endif
-
-#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED)
-/* The output_gamma value is a screen gamma in libpng terminology: it expresses
- * how to decode the output values, not how they are encoded.
- */
-#define PNG_DEFAULT_sRGB -1       /* sRGB gamma and color space */
-#define PNG_GAMMA_MAC_18 -2       /* Old Mac '1.8' gamma and color space */
-#define PNG_GAMMA_sRGB   220000   /* Television standards--matches sRGB gamma */
-#define PNG_GAMMA_LINEAR PNG_FP_1 /* Linear */
-#endif
-
-/* The following are examples of calls to png_set_alpha_mode to achieve the
- * required overall gamma correction and, where necessary, alpha
- * premultiplication.
- *
- * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB);
- *    This is the default libpng handling of the alpha channel - it is not
- *    pre-multiplied into the color components.  In addition the call states
- *    that the output is for a sRGB system and causes all PNG files without gAMA
- *    chunks to be assumed to be encoded using sRGB.
- *
- * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC);
- *    In this case the output is assumed to be something like an sRGB conformant
- *    display preceded by a power-law lookup table of power 1.45.  This is how
- *    early Mac systems behaved.
- *
- * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR);
- *    This is the classic Jim Blinn approach and will work in academic
- *    environments where everything is done by the book.  It has the shortcoming
- *    of assuming that input PNG data with no gamma information is linear - this
- *    is unlikely to be correct unless the PNG files where generated locally.
- *    Most of the time the output precision will be so low as to show
- *    significant banding in dark areas of the image.
- *
- * png_set_expand_16(pp);
- * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB);
- *    This is a somewhat more realistic Jim Blinn inspired approach.  PNG files
- *    are assumed to have the sRGB encoding if not marked with a gamma value and
- *    the output is always 16 bits per component.  This permits accurate scaling
- *    and processing of the data.  If you know that your input PNG files were
- *    generated locally you might need to replace PNG_DEFAULT_sRGB with the
- *    correct value for your system.
- *
- * png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB);
- *    If you just need to composite the PNG image onto an existing background
- *    and if you control the code that does this you can use the optimization
- *    setting.  In this case you just copy completely opaque pixels to the
- *    output.  For pixels that are not completely transparent (you just skip
- *    those) you do the composition math using png_composite or png_composite_16
- *    below then encode the resultant 8-bit or 16-bit values to match the output
- *    encoding.
- *
- * Other cases
- *    If neither the PNG nor the standard linear encoding work for you because
- *    of the software or hardware you use then you have a big problem.  The PNG
- *    case will probably result in halos around the image.  The linear encoding
- *    will probably result in a washed out, too bright, image (it's actually too
- *    contrasty.)  Try the ALPHA_OPTIMIZED mode above - this will probably
- *    substantially reduce the halos.  Alternatively try:
- *
- * png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB);
- *    This option will also reduce the halos, but there will be slight dark
- *    halos round the opaque parts of the image where the background is light.
- *    In the OPTIMIZED mode the halos will be light halos where the background
- *    is dark.  Take your pick - the halos are unavoidable unless you can get
- *    your hardware/software fixed!  (The OPTIMIZED approach is slightly
- *    faster.)
- *
- * When the default gamma of PNG files doesn't match the output gamma.
- *    If you have PNG files with no gamma information png_set_alpha_mode allows
- *    you to provide a default gamma, but it also sets the output gamma to the
- *    matching value.  If you know your PNG files have a gamma that doesn't
- *    match the output you can take advantage of the fact that
- *    png_set_alpha_mode always sets the output gamma but only sets the PNG
- *    default if it is not already set:
- *
- * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB);
- * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC);
- *    The first call sets both the default and the output gamma values, the
- *    second call overrides the output gamma without changing the default.  This
- *    is easier than achieving the same effect with png_set_gamma.  You must use
- *    PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will
- *    fire if more than one call to png_set_alpha_mode and png_set_background is
- *    made in the same read operation, however multiple calls with PNG_ALPHA_PNG
- *    are ignored.
- */
-
-#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
-PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr));
-#endif
-
-#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
-    defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
-PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr));
-#endif
-
-#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \
-    defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
-PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp png_ptr));
-#endif
-
-#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
-/* Add a filler byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */
-PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler,
-    int flags));
-/* The values of the PNG_FILLER_ defines should NOT be changed */
-#  define PNG_FILLER_BEFORE 0
-#  define PNG_FILLER_AFTER 1
-/* Add an alpha byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */
-PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr,
-    png_uint_32 filler, int flags));
-#endif /* READ_FILLER || WRITE_FILLER */
-
-#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
-/* Swap bytes in 16-bit depth files. */
-PNG_EXPORT(41, void, png_set_swap, (png_structrp png_ptr));
-#endif
-
-#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
-/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */
-PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr));
-#endif
-
-#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \
-    defined(PNG_WRITE_PACKSWAP_SUPPORTED)
-/* Swap packing order of pixels in bytes. */
-PNG_EXPORT(43, void, png_set_packswap, (png_structrp png_ptr));
-#endif
-
-#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
-/* Converts files to legal bit depths. */
-PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p
-    true_bits));
-#endif
-
-#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
-    defined(PNG_WRITE_INTERLACING_SUPPORTED)
-/* Have the code handle the interlacing.  Returns the number of passes.
- * MUST be called before png_read_update_info or png_start_read_image,
- * otherwise it will not have the desired effect.  Note that it is still
- * necessary to call png_read_row or png_read_rows png_get_image_height
- * times for each pass.
-*/
-PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp png_ptr));
-#endif
-
-#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
-/* Invert monochrome files */
-PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr));
-#endif
-
-#ifdef PNG_READ_BACKGROUND_SUPPORTED
-/* Handle alpha and tRNS by replacing with a background color.  Prior to
- * libpng-1.5.4 this API must not be called before the PNG file header has been
- * read.  Doing so will result in unexpected behavior and possible warnings or
- * errors if the PNG file contains a bKGD chunk.
- */
-PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr,
-    png_const_color_16p background_color, int background_gamma_code,
-    int need_expand, double background_gamma))
-PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr,
-    png_const_color_16p background_color, int background_gamma_code,
-    int need_expand, png_fixed_point background_gamma))
-#endif
-#ifdef PNG_READ_BACKGROUND_SUPPORTED
-#  define PNG_BACKGROUND_GAMMA_UNKNOWN 0
-#  define PNG_BACKGROUND_GAMMA_SCREEN  1
-#  define PNG_BACKGROUND_GAMMA_FILE    2
-#  define PNG_BACKGROUND_GAMMA_UNIQUE  3
-#endif
-
-#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
-/* Scale a 16-bit depth file down to 8-bit, accurately. */
-PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr));
-#endif
-
-#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
-#define PNG_READ_16_TO_8_SUPPORTED /* Name prior to 1.5.4 */
-/* Strip the second byte of information from a 16-bit depth file. */
-PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr));
-#endif
-
-#ifdef PNG_READ_QUANTIZE_SUPPORTED
-/* Turn on quantizing, and reduce the palette to the number of colors
- * available.
- */
-PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr,
-    png_colorp palette, int num_palette, int maximum_colors,
-    png_const_uint_16p histogram, int full_quantize));
-#endif
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
-/* The threshold on gamma processing is configurable but hard-wired into the
- * library.  The following is the floating point variant.
- */
-#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001)
-
-/* Handle gamma correction. Screen_gamma=(display_exponent).
- * NOTE: this API simply sets the screen and file gamma values. It will
- * therefore override the value for gamma in a PNG file if it is called after
- * the file header has been read - use with care  - call before reading the PNG
- * file for best results!
- *
- * These routines accept the same gamma values as png_set_alpha_mode (described
- * above).  The PNG_GAMMA_ defines and PNG_DEFAULT_sRGB can be passed to either
- * API (floating point or fixed.)  Notice, however, that the 'file_gamma' value
- * is the inverse of a 'screen gamma' value.
- */
-PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr,
-    double screen_gamma, double override_file_gamma))
-PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr,
-    png_fixed_point screen_gamma, png_fixed_point override_file_gamma))
-#endif
-
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
-/* Set how many lines between output flushes - 0 for no flushing */
-PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows));
-/* Flush the current PNG output buffer */
-PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr));
-#endif
-
-/* Optional update palette with requested transformations */
-PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr));
-
-/* Optional call to update the users info structure */
-PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr,
-    png_inforp info_ptr));
-
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-/* Read one or more rows of image data. */
-PNG_EXPORT(55, void, png_read_rows, (png_structrp png_ptr, png_bytepp row,
-    png_bytepp display_row, png_uint_32 num_rows));
-#endif
-
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-/* Read a row of data. */
-PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row,
-    png_bytep display_row));
-#endif
-
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-/* Read the whole image into memory at once. */
-PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image));
-#endif
-
-/* Write a row of image data */
-PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr,
-    png_const_bytep row));
-
-/* Write a few rows of image data: (*row) is not written; however, the type
- * is declared as writeable to maintain compatibility with previous versions
- * of libpng and to allow the 'display_row' array from read_rows to be passed
- * unchanged to write_rows.
- */
-PNG_EXPORT(59, void, png_write_rows, (png_structrp png_ptr, png_bytepp row,
-    png_uint_32 num_rows));
-
-/* Write the image data */
-PNG_EXPORT(60, void, png_write_image, (png_structrp png_ptr, png_bytepp image));
-
-/* Write the end of the PNG file. */
-PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr,
-    png_inforp info_ptr));
-
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-/* Read the end of the PNG file. */
-PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp info_ptr));
-#endif
-
-/* Free any memory associated with the png_info_struct */
-PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp png_ptr,
-    png_infopp info_ptr_ptr));
-
-/* Free any memory associated with the png_struct and the png_info_structs */
-PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr,
-    png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr));
-
-/* Free any memory associated with the png_struct and the png_info_structs */
-PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr,
-    png_infopp info_ptr_ptr));
-
-/* Set the libpng method of handling chunk CRC errors */
-PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action,
-    int ancil_action));
-
-/* Values for png_set_crc_action() say how to handle CRC errors in
- * ancillary and critical chunks, and whether to use the data contained
- * therein.  Note that it is impossible to "discard" data in a critical
- * chunk.  For versions prior to 0.90, the action was always error/quit,
- * whereas in version 0.90 and later, the action for CRC errors in ancillary
- * chunks is warn/discard.  These values should NOT be changed.
- *
- *      value                       action:critical     action:ancillary
- */
-#define PNG_CRC_DEFAULT       0  /* error/quit          warn/discard data */
-#define PNG_CRC_ERROR_QUIT    1  /* error/quit          error/quit        */
-#define PNG_CRC_WARN_DISCARD  2  /* (INVALID)           warn/discard data */
-#define PNG_CRC_WARN_USE      3  /* warn/use data       warn/use data     */
-#define PNG_CRC_QUIET_USE     4  /* quiet/use data      quiet/use data    */
-#define PNG_CRC_NO_CHANGE     5  /* use current value   use current value */
-
-#ifdef PNG_WRITE_SUPPORTED
-/* These functions give the user control over the scan-line filtering in
- * libpng and the compression methods used by zlib.  These functions are
- * mainly useful for testing, as the defaults should work with most users.
- * Those users who are tight on memory or want faster performance at the
- * expense of compression can modify them.  See the compression library
- * header file (zlib.h) for an explination of the compression functions.
- */
-
-/* Set the filtering method(s) used by libpng.  Currently, the only valid
- * value for "method" is 0.
- */
-PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method,
-    int filters));
-#endif /* WRITE */
-
-/* Flags for png_set_filter() to say which filters to use.  The flags
- * are chosen so that they don't conflict with real filter types
- * below, in case they are supplied instead of the #defined constants.
- * These values should NOT be changed.
- */
-#define PNG_NO_FILTERS     0x00
-#define PNG_FILTER_NONE    0x08
-#define PNG_FILTER_SUB     0x10
-#define PNG_FILTER_UP      0x20
-#define PNG_FILTER_AVG     0x40
-#define PNG_FILTER_PAETH   0x80
-#define PNG_FAST_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP)
-#define PNG_ALL_FILTERS (PNG_FAST_FILTERS | PNG_FILTER_AVG | PNG_FILTER_PAETH)
-
-/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now.
- * These defines should NOT be changed.
- */
-#define PNG_FILTER_VALUE_NONE  0
-#define PNG_FILTER_VALUE_SUB   1
-#define PNG_FILTER_VALUE_UP    2
-#define PNG_FILTER_VALUE_AVG   3
-#define PNG_FILTER_VALUE_PAETH 4
-#define PNG_FILTER_VALUE_LAST  5
-
-#ifdef PNG_WRITE_SUPPORTED
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */
-PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr,
-    int heuristic_method, int num_weights, png_const_doublep filter_weights,
-    png_const_doublep filter_costs))
-PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed,
-    (png_structrp png_ptr, int heuristic_method, int num_weights,
-    png_const_fixed_point_p filter_weights,
-    png_const_fixed_point_p filter_costs))
-#endif /* WRITE_WEIGHTED_FILTER */
-
-/* The following are no longer used and will be removed from libpng-1.7: */
-#define PNG_FILTER_HEURISTIC_DEFAULT    0  /* Currently "UNWEIGHTED" */
-#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1  /* Used by libpng < 0.95 */
-#define PNG_FILTER_HEURISTIC_WEIGHTED   2  /* Experimental feature */
-#define PNG_FILTER_HEURISTIC_LAST       3  /* Not a valid value */
-
-/* Set the library compression level.  Currently, valid values range from
- * 0 - 9, corresponding directly to the zlib compression levels 0 - 9
- * (0 - no compression, 9 - "maximal" compression).  Note that tests have
- * shown that zlib compression levels 3-6 usually perform as well as level 9
- * for PNG images, and do considerably fewer caclulations.  In the future,
- * these values may not correspond directly to the zlib compression levels.
- */
-#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
-PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr,
-    int level));
-
-PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr,
-    int mem_level));
-
-PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr,
-    int strategy));
-
-/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
- * smaller value of window_bits if it can do so safely.
- */
-PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr,
-    int window_bits));
-
-PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr,
-    int method));
-#endif /* WRITE_CUSTOMIZE_COMPRESSION */
-
-#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
-/* Also set zlib parameters for compressing non-IDAT chunks */
-PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr,
-    int level));
-
-PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr,
-    int mem_level));
-
-PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr,
-    int strategy));
-
-/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
- * smaller value of window_bits if it can do so safely.
- */
-PNG_EXPORT(225, void, png_set_text_compression_window_bits,
-    (png_structrp png_ptr, int window_bits));
-
-PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr,
-    int method));
-#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */
-#endif /* WRITE */
-
-/* These next functions are called for input/output, memory, and error
- * handling.  They are in the file pngrio.c, pngwio.c, and pngerror.c,
- * and call standard C I/O routines such as fread(), fwrite(), and
- * fprintf().  These functions can be made to use other I/O routines
- * at run time for those applications that need to handle I/O in a
- * different manner by calling png_set_???_fn().  See libpng-manual.txt for
- * more information.
- */
-
-#ifdef PNG_STDIO_SUPPORTED
-/* Initialize the input/output for the PNG file to the default functions. */
-PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp));
-#endif
-
-/* Replace the (error and abort), and warning functions with user
- * supplied functions.  If no messages are to be printed you must still
- * write and use replacement functions. The replacement error_fn should
- * still do a longjmp to the last setjmp location if you are using this
- * method of error handling.  If error_fn or warning_fn is NULL, the
- * default function will be used.
- */
-
-PNG_EXPORT(75, void, png_set_error_fn, (png_structrp png_ptr,
-    png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn));
-
-/* Return the user pointer associated with the error functions */
-PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr));
-
-/* Replace the default data output functions with a user supplied one(s).
- * If buffered output is not used, then output_flush_fn can be set to NULL.
- * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time
- * output_flush_fn will be ignored (and thus can be NULL).
- * It is probably a mistake to use NULL for output_flush_fn if
- * write_data_fn is not also NULL unless you have built libpng with
- * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's
- * default flush function, which uses the standard *FILE structure, will
- * be used.
- */
-PNG_EXPORT(77, void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr,
-    png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn));
-
-/* Replace the default data input function with a user supplied one. */
-PNG_EXPORT(78, void, png_set_read_fn, (png_structrp png_ptr, png_voidp io_ptr,
-    png_rw_ptr read_data_fn));
-
-/* Return the user pointer associated with the I/O functions */
-PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr));
-
-PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr,
-    png_read_status_ptr read_row_fn));
-
-PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp png_ptr,
-    png_write_status_ptr write_row_fn));
-
-#ifdef PNG_USER_MEM_SUPPORTED
-/* Replace the default memory allocation functions with user supplied one(s). */
-PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp png_ptr, png_voidp mem_ptr,
-    png_malloc_ptr malloc_fn, png_free_ptr free_fn));
-/* Return the user pointer associated with the memory functions */
-PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr));
-#endif
-
-#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
-PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr,
-    png_user_transform_ptr read_user_transform_fn));
-#endif
-
-#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
-PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr,
-    png_user_transform_ptr write_user_transform_fn));
-#endif
-
-#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
-PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp png_ptr,
-    png_voidp user_transform_ptr, int user_transform_depth,
-    int user_transform_channels));
-/* Return the user pointer associated with the user transform functions */
-PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr,
-    (png_const_structrp png_ptr));
-#endif
-
-#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
-/* Return information about the row currently being processed.  Note that these
- * APIs do not fail but will return unexpected results if called outside a user
- * transform callback.  Also note that when transforming an interlaced image the
- * row number is the row number within the sub-image of the interlace pass, so
- * the value will increase to the height of the sub-image (not the full image)
- * then reset to 0 for the next pass.
- *
- * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to
- * find the output pixel (x,y) given an interlaced sub-image pixel
- * (row,col,pass).  (See below for these macros.)
- */
-PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp));
-PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp));
-#endif
-
-#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
-/* This callback is called only for *unknown* chunks.  If
- * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known
- * chunks to be treated as unknown, however in this case the callback must do
- * any processing required by the chunk (e.g. by calling the appropriate
- * png_set_ APIs.)
- *
- * There is no write support - on write, by default, all the chunks in the
- * 'unknown' list are written in the specified position.
- *
- * The integer return from the callback function is interpreted thus:
- *
- * negative: An error occurred; png_chunk_error will be called.
- *     zero: The chunk was not handled, the chunk will be saved. A critical
- *           chunk will cause an error at this point unless it is to be saved.
- * positive: The chunk was handled, libpng will ignore/discard it.
- *
- * See "INTERACTION WITH USER CHUNK CALLBACKS" below for important notes about
- * how this behavior will change in libpng 1.7
- */
-PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr,
-    png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn));
-#endif
-
-#ifdef PNG_USER_CHUNKS_SUPPORTED
-PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr));
-#endif
-
-#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
-/* Sets the function callbacks for the push reader, and a pointer to a
- * user-defined structure available to the callback functions.
- */
-PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp png_ptr,
-    png_voidp progressive_ptr, png_progressive_info_ptr info_fn,
-    png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn));
-
-/* Returns the user pointer associated with the push read functions */
-PNG_EXPORT(91, png_voidp, png_get_progressive_ptr,
-    (png_const_structrp png_ptr));
-
-/* Function to be called when data becomes available */
-PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr,
-    png_inforp info_ptr, png_bytep buffer, size_t buffer_size));
-
-/* A function which may be called *only* within png_process_data to stop the
- * processing of any more data.  The function returns the number of bytes
- * remaining, excluding any that libpng has cached internally.  A subsequent
- * call to png_process_data must supply these bytes again.  If the argument
- * 'save' is set to true the routine will first save all the pending data and
- * will always return 0.
- */
-PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save));
-
-/* A function which may be called *only* outside (after) a call to
- * png_process_data.  It returns the number of bytes of data to skip in the
- * input.  Normally it will return 0, but if it returns a non-zero value the
- * application must skip than number of bytes of input data and pass the
- * following data to the next call to png_process_data.
- */
-PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp));
-
-/* Function that combines rows.  'new_row' is a flag that should come from
- * the callback and be non-NULL if anything needs to be done; the library
- * stores its own version of the new data internally and ignores the passed
- * in value.
- */
-PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr,
-    png_bytep old_row, png_const_bytep new_row));
-#endif /* PROGRESSIVE_READ */
-
-PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr,
-    png_alloc_size_t size), PNG_ALLOCATED);
-/* Added at libpng version 1.4.0 */
-PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr,
-    png_alloc_size_t size), PNG_ALLOCATED);
-
-/* Added at libpng version 1.2.4 */
-PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr,
-    png_alloc_size_t size), PNG_ALLOCATED);
-
-/* Frees a pointer allocated by png_malloc() */
-PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr));
-
-/* Free data that was allocated internally */
-PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 free_me, int num));
-
-/* Reassign responsibility for freeing existing data, whether allocated
- * by libpng or by the application; this works on the png_info structure passed
- * in, it does not change the state for other png_info structures.
- *
- * It is unlikely that this function works correctly as of 1.6.0 and using it
- * may result either in memory leaks or double free of allocated data.
- */
-PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr,
-    png_inforp info_ptr, int freer, png_uint_32 mask));
-
-/* Assignments for png_data_freer */
-#define PNG_DESTROY_WILL_FREE_DATA 1
-#define PNG_SET_WILL_FREE_DATA 1
-#define PNG_USER_WILL_FREE_DATA 2
-/* Flags for png_ptr->free_me and info_ptr->free_me */
-#define PNG_FREE_HIST 0x0008U
-#define PNG_FREE_ICCP 0x0010U
-#define PNG_FREE_SPLT 0x0020U
-#define PNG_FREE_ROWS 0x0040U
-#define PNG_FREE_PCAL 0x0080U
-#define PNG_FREE_SCAL 0x0100U
-#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
-#  define PNG_FREE_UNKN 0x0200U
-#endif
-/*      PNG_FREE_LIST 0x0400U   removed in 1.6.0 because it is ignored */
-#define PNG_FREE_PLTE 0x1000U
-#define PNG_FREE_TRNS 0x2000U
-#define PNG_FREE_TEXT 0x4000U
-#define PNG_FREE_EXIF 0x8000U /* Added at libpng-1.6.31 */
-#define PNG_FREE_ALL  0xffffU
-#define PNG_FREE_MUL  0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */
-
-#ifdef PNG_USER_MEM_SUPPORTED
-PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr,
-    png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED);
-PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr,
-    png_voidp ptr), PNG_DEPRECATED);
-#endif
-
-#ifdef PNG_ERROR_TEXT_SUPPORTED
-/* Fatal error in PNG image of libpng - can't continue */
-PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr,
-    png_const_charp error_message), PNG_NORETURN);
-
-/* The same, but the chunk name is prepended to the error string. */
-PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr,
-    png_const_charp error_message), PNG_NORETURN);
-
-#else
-/* Fatal error in PNG image of libpng - can't continue */
-PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN);
-#  define png_error(s1,s2) png_err(s1)
-#  define png_chunk_error(s1,s2) png_err(s1)
-#endif
-
-#ifdef PNG_WARNINGS_SUPPORTED
-/* Non-fatal error in libpng.  Can continue, but may have a problem. */
-PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr,
-    png_const_charp warning_message));
-
-/* Non-fatal error in libpng, chunk name is prepended to message. */
-PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr,
-    png_const_charp warning_message));
-#else
-#  define png_warning(s1,s2) ((void)(s1))
-#  define png_chunk_warning(s1,s2) ((void)(s1))
-#endif
-
-#ifdef PNG_BENIGN_ERRORS_SUPPORTED
-/* Benign error in libpng.  Can continue, but may have a problem.
- * User can choose whether to handle as a fatal error or as a warning. */
-PNG_EXPORT(107, void, png_benign_error, (png_const_structrp png_ptr,
-    png_const_charp warning_message));
-
-#ifdef PNG_READ_SUPPORTED
-/* Same, chunk name is prepended to message (only during read) */
-PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr,
-    png_const_charp warning_message));
-#endif
-
-PNG_EXPORT(109, void, png_set_benign_errors,
-    (png_structrp png_ptr, int allowed));
-#else
-#  ifdef PNG_ALLOW_BENIGN_ERRORS
-#    define png_benign_error png_warning
-#    define png_chunk_benign_error png_chunk_warning
-#  else
-#    define png_benign_error png_error
-#    define png_chunk_benign_error png_chunk_error
-#  endif
-#endif
-
-/* The png_set_<chunk> functions are for storing values in the png_info_struct.
- * Similarly, the png_get_<chunk> calls are used to read values from the
- * png_info_struct, either storing the parameters in the passed variables, or
- * setting pointers into the png_info_struct where the data is stored.  The
- * png_get_<chunk> functions return a non-zero value if the data was available
- * in info_ptr, or return zero and do not change any of the parameters if the
- * data was not available.
- *
- * These functions should be used instead of directly accessing png_info
- * to avoid problems with future changes in the size and internal layout of
- * png_info_struct.
- */
-/* Returns "flag" if chunk data is valid in info_ptr. */
-PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, png_uint_32 flag));
-
-/* Returns number of bytes needed to hold a transformed row. */
-PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
-
-#ifdef PNG_INFO_IMAGE_SUPPORTED
-/* Returns row_pointers, which is an array of pointers to scanlines that was
- * returned from png_read_png().
- */
-PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
-
-/* Set row_pointers, which is an array of pointers to scanlines for use
- * by png_write_png().
- */
-PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_bytepp row_pointers));
-#endif
-
-/* Returns number of color channels in image. */
-PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
-
-#ifdef PNG_EASY_ACCESS_SUPPORTED
-/* Returns image width in pixels. */
-PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
-
-/* Returns image height in pixels. */
-PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
-
-/* Returns image bit_depth. */
-PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
-
-/* Returns image color_type. */
-PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
-
-/* Returns image filter_type. */
-PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
-
-/* Returns image interlace_type. */
-PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
-
-/* Returns image compression_type. */
-PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
-
-/* Returns image resolution in pixels per meter, from pHYs chunk data. */
-PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
-PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
-PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
-
-/* Returns pixel aspect ratio, computed from pHYs chunk data.  */
-PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr))
-PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr))
-
-/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */
-PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
-PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
-PNG_EXPORT(128, png_int_32, png_get_x_offset_microns,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
-PNG_EXPORT(129, png_int_32, png_get_y_offset_microns,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
-
-#endif /* EASY_ACCESS */
-
-#ifdef PNG_READ_SUPPORTED
-/* Returns pointer to signature string read from PNG header */
-PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
-#endif
-
-#ifdef PNG_bKGD_SUPPORTED
-PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_color_16p *background));
-#endif
-
-#ifdef PNG_bKGD_SUPPORTED
-PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_color_16p background));
-#endif
-
-#ifdef PNG_cHRM_SUPPORTED
-PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x,
-    double *red_y, double *green_x, double *green_y, double *blue_x,
-    double *blue_y))
-PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z,
-    double *green_X, double *green_Y, double *green_Z, double *blue_X,
-    double *blue_Y, double *blue_Z))
-PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr,
-    png_fixed_point *int_white_x, png_fixed_point *int_white_y,
-    png_fixed_point *int_red_x, png_fixed_point *int_red_y,
-    png_fixed_point *int_green_x, png_fixed_point *int_green_y,
-    png_fixed_point *int_blue_x, png_fixed_point *int_blue_y))
-PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr,
-    png_fixed_point *int_red_X, png_fixed_point *int_red_Y,
-    png_fixed_point *int_red_Z, png_fixed_point *int_green_X,
-    png_fixed_point *int_green_Y, png_fixed_point *int_green_Z,
-    png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y,
-    png_fixed_point *int_blue_Z))
-#endif
-
-#ifdef PNG_cHRM_SUPPORTED
-PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr,
-    png_inforp info_ptr,
-    double white_x, double white_y, double red_x, double red_y, double green_x,
-    double green_y, double blue_x, double blue_y))
-PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr,
-    png_inforp info_ptr, double red_X, double red_Y, double red_Z,
-    double green_X, double green_Y, double green_Z, double blue_X,
-    double blue_Y, double blue_Z))
-PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_fixed_point int_white_x,
-    png_fixed_point int_white_y, png_fixed_point int_red_x,
-    png_fixed_point int_red_y, png_fixed_point int_green_x,
-    png_fixed_point int_green_y, png_fixed_point int_blue_x,
-    png_fixed_point int_blue_y))
-PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y,
-    png_fixed_point int_red_Z, png_fixed_point int_green_X,
-    png_fixed_point int_green_Y, png_fixed_point int_green_Z,
-    png_fixed_point int_blue_X, png_fixed_point int_blue_Y,
-    png_fixed_point int_blue_Z))
-#endif
-
-#ifdef PNG_eXIf_SUPPORTED
-PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_bytep *exif));
-PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_bytep exif));
-
-PNG_EXPORT(248, png_uint_32, png_get_eXIf_1, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, png_uint_32 *num_exif, png_bytep *exif));
-PNG_EXPORT(249, void, png_set_eXIf_1, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 num_exif, png_bytep exif));
-#endif
-
-#ifdef PNG_gAMA_SUPPORTED
-PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, double *file_gamma))
-PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr,
-    png_fixed_point *int_file_gamma))
-#endif
-
-#ifdef PNG_gAMA_SUPPORTED
-PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr,
-    png_inforp info_ptr, double file_gamma))
-PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_fixed_point int_file_gamma))
-#endif
-
-#ifdef PNG_hIST_SUPPORTED
-PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_uint_16p *hist));
-PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_uint_16p hist));
-#endif
-
-PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height,
-    int *bit_depth, int *color_type, int *interlace_method,
-    int *compression_method, int *filter_method));
-
-PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth,
-    int color_type, int interlace_method, int compression_method,
-    int filter_method));
-
-#ifdef PNG_oFFs_SUPPORTED
-PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr,
-   png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y,
-   int *unit_type));
-#endif
-
-#ifdef PNG_oFFs_SUPPORTED
-PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y,
-    int unit_type));
-#endif
-
-#ifdef PNG_pCAL_SUPPORTED
-PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_charp *purpose, png_int_32 *X0,
-    png_int_32 *X1, int *type, int *nparams, png_charp *units,
-    png_charpp *params));
-#endif
-
-#ifdef PNG_pCAL_SUPPORTED
-PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1,
-    int type, int nparams, png_const_charp units, png_charpp params));
-#endif
-
-#ifdef PNG_pHYs_SUPPORTED
-PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y,
-    int *unit_type));
-#endif
-
-#ifdef PNG_pHYs_SUPPORTED
-PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type));
-#endif
-
-PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr,
-   png_inforp info_ptr, png_colorp *palette, int *num_palette));
-
-PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr,
-    png_inforp info_ptr, png_const_colorp palette, int num_palette));
-
-#ifdef PNG_sBIT_SUPPORTED
-PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_color_8p *sig_bit));
-#endif
-
-#ifdef PNG_sBIT_SUPPORTED
-PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_color_8p sig_bit));
-#endif
-
-#ifdef PNG_sRGB_SUPPORTED
-PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, int *file_srgb_intent));
-#endif
-
-#ifdef PNG_sRGB_SUPPORTED
-PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr,
-    png_inforp info_ptr, int srgb_intent));
-PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr,
-    png_inforp info_ptr, int srgb_intent));
-#endif
-
-#ifdef PNG_iCCP_SUPPORTED
-PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_charpp name, int *compression_type,
-    png_bytepp profile, png_uint_32 *proflen));
-#endif
-
-#ifdef PNG_iCCP_SUPPORTED
-PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_charp name, int compression_type,
-    png_const_bytep profile, png_uint_32 proflen));
-#endif
-
-#ifdef PNG_sPLT_SUPPORTED
-PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_sPLT_tpp entries));
-#endif
-
-#ifdef PNG_sPLT_SUPPORTED
-PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_sPLT_tp entries, int nentries));
-#endif
-
-#ifdef PNG_TEXT_SUPPORTED
-/* png_get_text also returns the number of text chunks in *num_text */
-PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_textp *text_ptr, int *num_text));
-#endif
-
-/* Note while png_set_text() will accept a structure whose text,
- * language, and  translated keywords are NULL pointers, the structure
- * returned by png_get_text will always contain regular
- * zero-terminated C strings.  They might be empty strings but
- * they will never be NULL pointers.
- */
-
-#ifdef PNG_TEXT_SUPPORTED
-PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_textp text_ptr, int num_text));
-#endif
-
-#ifdef PNG_tIME_SUPPORTED
-PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_timep *mod_time));
-#endif
-
-#ifdef PNG_tIME_SUPPORTED
-PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_timep mod_time));
-#endif
-
-#ifdef PNG_tRNS_SUPPORTED
-PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans,
-    png_color_16p *trans_color));
-#endif
-
-#ifdef PNG_tRNS_SUPPORTED
-PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr,
-    png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans,
-    png_const_color_16p trans_color));
-#endif
-
-#ifdef PNG_sCAL_SUPPORTED
-PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, int *unit, double *width, double *height))
-#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \
-   defined(PNG_FLOATING_POINT_SUPPORTED)
-/* NOTE: this API is currently implemented using floating point arithmetic,
- * consequently it can only be used on systems with floating point support.
- * In any case the range of values supported by png_fixed_point is small and it
- * is highly recommended that png_get_sCAL_s be used instead.
- */
-PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit,
-    png_fixed_point *width, png_fixed_point *height))
-#endif
-PNG_EXPORT(169, png_uint_32, png_get_sCAL_s,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit,
-    png_charpp swidth, png_charpp sheight));
-
-PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr,
-    png_inforp info_ptr, int unit, double width, double height))
-PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr,
-   png_inforp info_ptr, int unit, png_fixed_point width,
-   png_fixed_point height))
-PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr,
-    png_inforp info_ptr, int unit,
-    png_const_charp swidth, png_const_charp sheight));
-#endif /* sCAL */
-
-#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
-/* Provide the default handling for all unknown chunks or, optionally, for
- * specific unknown chunks.
- *
- * NOTE: prior to 1.6.0 the handling specified for particular chunks on read was
- * ignored and the default was used, the per-chunk setting only had an effect on
- * write.  If you wish to have chunk-specific handling on read in code that must
- * work on earlier versions you must use a user chunk callback to specify the
- * desired handling (keep or discard.)
- *
- * The 'keep' parameter is a PNG_HANDLE_CHUNK_ value as listed below.  The
- * parameter is interpreted as follows:
- *
- * READ:
- *    PNG_HANDLE_CHUNK_AS_DEFAULT:
- *       Known chunks: do normal libpng processing, do not keep the chunk (but
- *          see the comments below about PNG_HANDLE_AS_UNKNOWN_SUPPORTED)
- *       Unknown chunks: for a specific chunk use the global default, when used
- *          as the default discard the chunk data.
- *    PNG_HANDLE_CHUNK_NEVER:
- *       Discard the chunk data.
- *    PNG_HANDLE_CHUNK_IF_SAFE:
- *       Keep the chunk data if the chunk is not critical else raise a chunk
- *       error.
- *    PNG_HANDLE_CHUNK_ALWAYS:
- *       Keep the chunk data.
- *
- * If the chunk data is saved it can be retrieved using png_get_unknown_chunks,
- * below.  Notice that specifying "AS_DEFAULT" as a global default is equivalent
- * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks
- * it simply resets the behavior to the libpng default.
- *
- * INTERACTION WITH USER CHUNK CALLBACKS:
- * The per-chunk handling is always used when there is a png_user_chunk_ptr
- * callback and the callback returns 0; the chunk is then always stored *unless*
- * it is critical and the per-chunk setting is other than ALWAYS.  Notice that
- * the global default is *not* used in this case.  (In effect the per-chunk
- * value is incremented to at least IF_SAFE.)
- *
- * IMPORTANT NOTE: this behavior will change in libpng 1.7 - the global and
- * per-chunk defaults will be honored.  If you want to preserve the current
- * behavior when your callback returns 0 you must set PNG_HANDLE_CHUNK_IF_SAFE
- * as the default - if you don't do this libpng 1.6 will issue a warning.
- *
- * If you want unhandled unknown chunks to be discarded in libpng 1.6 and
- * earlier simply return '1' (handled).
- *
- * PNG_HANDLE_AS_UNKNOWN_SUPPORTED:
- *    If this is *not* set known chunks will always be handled by libpng and
- *    will never be stored in the unknown chunk list.  Known chunks listed to
- *    png_set_keep_unknown_chunks will have no effect.  If it is set then known
- *    chunks listed with a keep other than AS_DEFAULT will *never* be processed
- *    by libpng, in addition critical chunks must either be processed by the
- *    callback or saved.
- *
- *    The IHDR and IEND chunks must not be listed.  Because this turns off the
- *    default handling for chunks that would otherwise be recognized the
- *    behavior of libpng transformations may well become incorrect!
- *
- * WRITE:
- *    When writing chunks the options only apply to the chunks specified by
- *    png_set_unknown_chunks (below), libpng will *always* write known chunks
- *    required by png_set_ calls and will always write the core critical chunks
- *    (as required for PLTE).
- *
- *    Each chunk in the png_set_unknown_chunks list is looked up in the
- *    png_set_keep_unknown_chunks list to find the keep setting, this is then
- *    interpreted as follows:
- *
- *    PNG_HANDLE_CHUNK_AS_DEFAULT:
- *       Write safe-to-copy chunks and write other chunks if the global
- *       default is set to _ALWAYS, otherwise don't write this chunk.
- *    PNG_HANDLE_CHUNK_NEVER:
- *       Do not write the chunk.
- *    PNG_HANDLE_CHUNK_IF_SAFE:
- *       Write the chunk if it is safe-to-copy, otherwise do not write it.
- *    PNG_HANDLE_CHUNK_ALWAYS:
- *       Write the chunk.
- *
- * Note that the default behavior is effectively the opposite of the read case -
- * in read unknown chunks are not stored by default, in write they are written
- * by default.  Also the behavior of PNG_HANDLE_CHUNK_IF_SAFE is very different
- * - on write the safe-to-copy bit is checked, on read the critical bit is
- * checked and on read if the chunk is critical an error will be raised.
- *
- * num_chunks:
- * ===========
- *    If num_chunks is positive, then the "keep" parameter specifies the manner
- *    for handling only those chunks appearing in the chunk_list array,
- *    otherwise the chunk list array is ignored.
- *
- *    If num_chunks is 0 the "keep" parameter specifies the default behavior for
- *    unknown chunks, as described above.
- *
- *    If num_chunks is negative, then the "keep" parameter specifies the manner
- *    for handling all unknown chunks plus all chunks recognized by libpng
- *    except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to
- *    be processed by libpng.
- */
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr,
-    int keep, png_const_bytep chunk_list, int num_chunks));
-#endif /* HANDLE_AS_UNKNOWN */
-
-/* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned;
- * the result is therefore true (non-zero) if special handling is required,
- * false for the default handling.
- */
-PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr,
-    png_const_bytep chunk_name));
-#endif /* SET_UNKNOWN_CHUNKS */
-
-#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
-PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_unknown_chunkp unknowns,
-    int num_unknowns));
-   /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added
-    * unknowns to the location currently stored in the png_struct.  This is
-    * invariably the wrong value on write.  To fix this call the following API
-    * for each chunk in the list with the correct location.  If you know your
-    * code won't be compiled on earlier versions you can rely on
-    * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing
-    * the correct thing.
-    */
-
-PNG_EXPORT(175, void, png_set_unknown_chunk_location,
-    (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location));
-
-PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_unknown_chunkpp entries));
-#endif
-
-/* Png_free_data() will turn off the "valid" flag for anything it frees.
- * If you need to turn it off for a chunk that your application has freed,
- * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK);
- */
-PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr,
-    png_inforp info_ptr, int mask));
-
-#ifdef PNG_INFO_IMAGE_SUPPORTED
-/* The "params" pointer is currently not used and is for future expansion. */
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr,
-    int transforms, png_voidp params));
-#endif
-#ifdef PNG_WRITE_SUPPORTED
-PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr,
-    int transforms, png_voidp params));
-#endif
-#endif
-
-PNG_EXPORT(180, png_const_charp, png_get_copyright,
-    (png_const_structrp png_ptr));
-PNG_EXPORT(181, png_const_charp, png_get_header_ver,
-    (png_const_structrp png_ptr));
-PNG_EXPORT(182, png_const_charp, png_get_header_version,
-    (png_const_structrp png_ptr));
-PNG_EXPORT(183, png_const_charp, png_get_libpng_ver,
-    (png_const_structrp png_ptr));
-
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr,
-    png_uint_32 mng_features_permitted));
-#endif
-
-/* For use in png_set_keep_unknown, added to version 1.2.6 */
-#define PNG_HANDLE_CHUNK_AS_DEFAULT   0
-#define PNG_HANDLE_CHUNK_NEVER        1
-#define PNG_HANDLE_CHUNK_IF_SAFE      2
-#define PNG_HANDLE_CHUNK_ALWAYS       3
-#define PNG_HANDLE_CHUNK_LAST         4
-
-/* Strip the prepended error numbers ("#nnn ") from error and warning
- * messages before passing them to the error or warning handler.
- */
-#ifdef PNG_ERROR_NUMBERS_SUPPORTED
-PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr,
-    png_uint_32 strip_mode));
-#endif
-
-/* Added in libpng-1.2.6 */
-#ifdef PNG_SET_USER_LIMITS_SUPPORTED
-PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr,
-    png_uint_32 user_width_max, png_uint_32 user_height_max));
-PNG_EXPORT(187, png_uint_32, png_get_user_width_max,
-    (png_const_structrp png_ptr));
-PNG_EXPORT(188, png_uint_32, png_get_user_height_max,
-    (png_const_structrp png_ptr));
-/* Added in libpng-1.4.0 */
-PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr,
-    png_uint_32 user_chunk_cache_max));
-PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max,
-    (png_const_structrp png_ptr));
-/* Added in libpng-1.4.1 */
-PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr,
-    png_alloc_size_t user_chunk_cache_max));
-PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max,
-    (png_const_structrp png_ptr));
-#endif
-
-#if defined(PNG_INCH_CONVERSIONS_SUPPORTED)
-PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
-
-PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
-
-PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
-
-PNG_FP_EXPORT(196, float, png_get_x_offset_inches,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr))
-#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */
-PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr))
-#endif
-
-PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr))
-#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */
-PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr))
-#endif
-
-#  ifdef PNG_pHYs_SUPPORTED
-PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y,
-    int *unit_type));
-#  endif /* pHYs */
-#endif  /* INCH_CONVERSIONS */
-
-/* Added in libpng-1.4.0 */
-#ifdef PNG_IO_STATE_SUPPORTED
-PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr));
-
-/* Removed from libpng 1.6; use png_get_io_chunk_type. */
-PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr),
-    PNG_DEPRECATED)
-
-PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type,
-    (png_const_structrp png_ptr));
-
-/* The flags returned by png_get_io_state() are the following: */
-#  define PNG_IO_NONE        0x0000   /* no I/O at this moment */
-#  define PNG_IO_READING     0x0001   /* currently reading */
-#  define PNG_IO_WRITING     0x0002   /* currently writing */
-#  define PNG_IO_SIGNATURE   0x0010   /* currently at the file signature */
-#  define PNG_IO_CHUNK_HDR   0x0020   /* currently at the chunk header */
-#  define PNG_IO_CHUNK_DATA  0x0040   /* currently at the chunk data */
-#  define PNG_IO_CHUNK_CRC   0x0080   /* currently at the chunk crc */
-#  define PNG_IO_MASK_OP     0x000f   /* current operation: reading/writing */
-#  define PNG_IO_MASK_LOC    0x00f0   /* current location: sig/hdr/data/crc */
-#endif /* IO_STATE */
-
-/* Interlace support.  The following macros are always defined so that if
- * libpng interlace handling is turned off the macros may be used to handle
- * interlaced images within the application.
- */
-#define PNG_INTERLACE_ADAM7_PASSES 7
-
-/* Two macros to return the first row and first column of the original,
- * full, image which appears in a given pass.  'pass' is in the range 0
- * to 6 and the result is in the range 0 to 7.
- */
-#define PNG_PASS_START_ROW(pass) (((1&~(pass))<<(3-((pass)>>1)))&7)
-#define PNG_PASS_START_COL(pass) (((1& (pass))<<(3-(((pass)+1)>>1)))&7)
-
-/* A macro to return the offset between pixels in the output row for a pair of
- * pixels in the input - effectively the inverse of the 'COL_SHIFT' macro that
- * follows.  Note that ROW_OFFSET is the offset from one row to the next whereas
- * COL_OFFSET is from one column to the next, within a row.
- */
-#define PNG_PASS_ROW_OFFSET(pass) ((pass)>2?(8>>(((pass)-1)>>1)):8)
-#define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1))
-
-/* Two macros to help evaluate the number of rows or columns in each
- * pass.  This is expressed as a shift - effectively log2 of the number or
- * rows or columns in each 8x8 tile of the original image.
- */
-#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3)
-#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3)
-
-/* Hence two macros to determine the number of rows or columns in a given
- * pass of an image given its height or width.  In fact these macros may
- * return non-zero even though the sub-image is empty, because the other
- * dimension may be empty for a small image.
- */
-#define PNG_PASS_ROWS(height, pass) (((height)+(((1<<PNG_PASS_ROW_SHIFT(pass))\
-   -1)-PNG_PASS_START_ROW(pass)))>>PNG_PASS_ROW_SHIFT(pass))
-#define PNG_PASS_COLS(width, pass) (((width)+(((1<<PNG_PASS_COL_SHIFT(pass))\
-   -1)-PNG_PASS_START_COL(pass)))>>PNG_PASS_COL_SHIFT(pass))
-
-/* For the reader row callbacks (both progressive and sequential) it is
- * necessary to find the row in the output image given a row in an interlaced
- * image, so two more macros:
- */
-#define PNG_ROW_FROM_PASS_ROW(y_in, pass) \
-   (((y_in)<<PNG_PASS_ROW_SHIFT(pass))+PNG_PASS_START_ROW(pass))
-#define PNG_COL_FROM_PASS_COL(x_in, pass) \
-   (((x_in)<<PNG_PASS_COL_SHIFT(pass))+PNG_PASS_START_COL(pass))
-
-/* Two macros which return a boolean (0 or 1) saying whether the given row
- * or column is in a particular pass.  These use a common utility macro that
- * returns a mask for a given pass - the offset 'off' selects the row or
- * column version.  The mask has the appropriate bit set for each column in
- * the tile.
- */
-#define PNG_PASS_MASK(pass,off) ( \
-   ((0x110145AF>>(((7-(off))-(pass))<<2)) & 0xF) | \
-   ((0x01145AF0>>(((7-(off))-(pass))<<2)) & 0xF0))
-
-#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \
-   ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1)
-#define PNG_COL_IN_INTERLACE_PASS(x, pass) \
-   ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1)
-
-#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED
-/* With these routines we avoid an integer divide, which will be slower on
- * most machines.  However, it does take more operations than the corresponding
- * divide method, so it may be slower on a few RISC systems.  There are two
- * shifts (by 8 or 16 bits) and an addition, versus a single integer divide.
- *
- * Note that the rounding factors are NOT supposed to be the same!  128 and
- * 32768 are correct for the NODIV code; 127 and 32767 are correct for the
- * standard method.
- *
- * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ]
- */
-
- /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */
-
-#  define png_composite(composite, fg, alpha, bg)        \
-   {                                                     \
-      png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \
-          * (png_uint_16)(alpha)                         \
-          + (png_uint_16)(bg)*(png_uint_16)(255          \
-          - (png_uint_16)(alpha)) + 128);                \
-      (composite) = (png_byte)(((temp + (temp >> 8)) >> 8) & 0xff); \
-   }
-
-#  define png_composite_16(composite, fg, alpha, bg)     \
-   {                                                     \
-      png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \
-          * (png_uint_32)(alpha)                         \
-          + (png_uint_32)(bg)*(65535                     \
-          - (png_uint_32)(alpha)) + 32768);              \
-      (composite) = (png_uint_16)(0xffff & ((temp + (temp >> 16)) >> 16)); \
-   }
-
-#else  /* Standard method using integer division */
-
-#  define png_composite(composite, fg, alpha, bg)                      \
-   (composite) =                                                       \
-       (png_byte)(0xff & (((png_uint_16)(fg) * (png_uint_16)(alpha) +  \
-       (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \
-       127) / 255))
-
-#  define png_composite_16(composite, fg, alpha, bg)                       \
-   (composite) =                                                           \
-       (png_uint_16)(0xffff & (((png_uint_32)(fg) * (png_uint_32)(alpha) + \
-       (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) +     \
-       32767) / 65535))
-#endif /* READ_COMPOSITE_NODIV */
-
-#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED
-PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf));
-PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf));
-PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf));
-#endif
-
-PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr,
-    png_const_bytep buf));
-/* No png_get_int_16 -- may be added if there's a real need for it. */
-
-/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */
-#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED
-PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i));
-#endif
-#ifdef PNG_SAVE_INT_32_SUPPORTED
-PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i));
-#endif
-
-/* Place a 16-bit number into a buffer in PNG byte order.
- * The parameter is declared unsigned int, not png_uint_16,
- * just to avoid potential problems on pre-ANSI C compilers.
- */
-#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED
-PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i));
-/* No png_save_int_16 -- may be added if there's a real need for it. */
-#endif
-
-#ifdef PNG_USE_READ_MACROS
-/* Inline macros to do direct reads of bytes from the input buffer.
- * The png_get_int_32() routine assumes we are using two's complement
- * format for negative values, which is almost certainly true.
- */
-#  define PNG_get_uint_32(buf) \
-   (((png_uint_32)(*(buf)) << 24) + \
-    ((png_uint_32)(*((buf) + 1)) << 16) + \
-    ((png_uint_32)(*((buf) + 2)) << 8) + \
-    ((png_uint_32)(*((buf) + 3))))
-
-   /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the
-    * function) incorrectly returned a value of type png_uint_32.
-    */
-#  define PNG_get_uint_16(buf) \
-   ((png_uint_16) \
-    (((unsigned int)(*(buf)) << 8) + \
-    ((unsigned int)(*((buf) + 1)))))
-
-#  define PNG_get_int_32(buf) \
-   ((png_int_32)((*(buf) & 0x80) \
-    ? -((png_int_32)(((png_get_uint_32(buf)^0xffffffffU)+1U)&0x7fffffffU)) \
-    : (png_int_32)png_get_uint_32(buf)))
-
-/* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h,
- * but defining a macro name prefixed with PNG_PREFIX.
- */
-#  ifndef PNG_PREFIX
-#    define png_get_uint_32(buf) PNG_get_uint_32(buf)
-#    define png_get_uint_16(buf) PNG_get_uint_16(buf)
-#    define png_get_int_32(buf)  PNG_get_int_32(buf)
-#  endif
-#else
-#  ifdef PNG_PREFIX
-   /* No macros; revert to the (redefined) function */
-#    define PNG_get_uint_32 (png_get_uint_32)
-#    define PNG_get_uint_16 (png_get_uint_16)
-#    define PNG_get_int_32  (png_get_int_32)
-#  endif
-#endif
-
-#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
-PNG_EXPORT(242, void, png_set_check_for_invalid_index,
-    (png_structrp png_ptr, int allowed));
-#  ifdef PNG_GET_PALETTE_MAX_SUPPORTED
-PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr,
-    png_const_infop info_ptr));
-#  endif
-#endif /* CHECK_FOR_INVALID_INDEX */
-
-/*******************************************************************************
- * Section 5: SIMPLIFIED API
- *******************************************************************************
- *
- * Please read the documentation in libpng-manual.txt (TODO: write said
- * documentation) if you don't understand what follows.
- *
- * The simplified API hides the details of both libpng and the PNG file format
- * itself.  It allows PNG files to be read into a very limited number of
- * in-memory bitmap formats or to be written from the same formats.  If these
- * formats do not accommodate your needs then you can, and should, use the more
- * sophisticated APIs above - these support a wide variety of in-memory formats
- * and a wide variety of sophisticated transformations to those formats as well
- * as a wide variety of APIs to manipulate ancillary information.
- *
- * To read a PNG file using the simplified API:
- *
- * 1) Declare a 'png_image' structure (see below) on the stack, set the
- *    version field to PNG_IMAGE_VERSION and the 'opaque' pointer to NULL
- *    (this is REQUIRED, your program may crash if you don't do it.)
- * 2) Call the appropriate png_image_begin_read... function.
- * 3) Set the png_image 'format' member to the required sample format.
- * 4) Allocate a buffer for the image and, if required, the color-map.
- * 5) Call png_image_finish_read to read the image and, if required, the
- *    color-map into your buffers.
- *
- * There are no restrictions on the format of the PNG input itself; all valid
- * color types, bit depths, and interlace methods are acceptable, and the
- * input image is transformed as necessary to the requested in-memory format
- * during the png_image_finish_read() step.  The only caveat is that if you
- * request a color-mapped image from a PNG that is full-color or makes
- * complex use of an alpha channel the transformation is extremely lossy and the
- * result may look terrible.
- *
- * To write a PNG file using the simplified API:
- *
- * 1) Declare a 'png_image' structure on the stack and memset() it to all zero.
- * 2) Initialize the members of the structure that describe the image, setting
- *    the 'format' member to the format of the image samples.
- * 3) Call the appropriate png_image_write... function with a pointer to the
- *    image and, if necessary, the color-map to write the PNG data.
- *
- * png_image is a structure that describes the in-memory format of an image
- * when it is being read or defines the in-memory format of an image that you
- * need to write:
- */
-#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) || \
-    defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
-
-#define PNG_IMAGE_VERSION 1
-
-typedef struct png_control *png_controlp;
-typedef struct
-{
-   png_controlp opaque;    /* Initialize to NULL, free with png_image_free */
-   png_uint_32  version;   /* Set to PNG_IMAGE_VERSION */
-   png_uint_32  width;     /* Image width in pixels (columns) */
-   png_uint_32  height;    /* Image height in pixels (rows) */
-   png_uint_32  format;    /* Image format as defined below */
-   png_uint_32  flags;     /* A bit mask containing informational flags */
-   png_uint_32  colormap_entries;
-                           /* Number of entries in the color-map */
-
-   /* In the event of an error or warning the following field will be set to a
-    * non-zero value and the 'message' field will contain a '\0' terminated
-    * string with the libpng error or warning message.  If both warnings and
-    * an error were encountered, only the error is recorded.  If there
-    * are multiple warnings, only the first one is recorded.
-    *
-    * The upper 30 bits of this value are reserved, the low two bits contain
-    * a value as follows:
-    */
-#  define PNG_IMAGE_WARNING 1
-#  define PNG_IMAGE_ERROR 2
-   /*
-    * The result is a two-bit code such that a value more than 1 indicates
-    * a failure in the API just called:
-    *
-    *    0 - no warning or error
-    *    1 - warning
-    *    2 - error
-    *    3 - error preceded by warning
-    */
-#  define PNG_IMAGE_FAILED(png_cntrl) ((((png_cntrl).warning_or_error)&0x03)>1)
-
-   png_uint_32  warning_or_error;
-
-   char         message[64];
-} png_image, *png_imagep;
-
-/* The samples of the image have one to four channels whose components have
- * original values in the range 0 to 1.0:
- *
- * 1: A single gray or luminance channel (G).
- * 2: A gray/luminance channel and an alpha channel (GA).
- * 3: Three red, green, blue color channels (RGB).
- * 4: Three color channels and an alpha channel (RGBA).
- *
- * The components are encoded in one of two ways:
- *
- * a) As a small integer, value 0..255, contained in a single byte.  For the
- * alpha channel the original value is simply value/255.  For the color or
- * luminance channels the value is encoded according to the sRGB specification
- * and matches the 8-bit format expected by typical display devices.
- *
- * The color/gray channels are not scaled (pre-multiplied) by the alpha
- * channel and are suitable for passing to color management software.
- *
- * b) As a value in the range 0..65535, contained in a 2-byte integer.  All
- * channels can be converted to the original value by dividing by 65535; all
- * channels are linear.  Color channels use the RGB encoding (RGB end-points) of
- * the sRGB specification.  This encoding is identified by the
- * PNG_FORMAT_FLAG_LINEAR flag below.
- *
- * When the simplified API needs to convert between sRGB and linear colorspaces,
- * the actual sRGB transfer curve defined in the sRGB specification (see the
- * article at <https://en.wikipedia.org/wiki/SRGB>) is used, not the gamma=1/2.2
- * approximation used elsewhere in libpng.
- *
- * When an alpha channel is present it is expected to denote pixel coverage
- * of the color or luminance channels and is returned as an associated alpha
- * channel: the color/gray channels are scaled (pre-multiplied) by the alpha
- * value.
- *
- * The samples are either contained directly in the image data, between 1 and 8
- * bytes per pixel according to the encoding, or are held in a color-map indexed
- * by bytes in the image data.  In the case of a color-map the color-map entries
- * are individual samples, encoded as above, and the image data has one byte per
- * pixel to select the relevant sample from the color-map.
- */
-
-/* PNG_FORMAT_*
- *
- * #defines to be used in png_image::format.  Each #define identifies a
- * particular layout of sample data and, if present, alpha values.  There are
- * separate defines for each of the two component encodings.
- *
- * A format is built up using single bit flag values.  All combinations are
- * valid.  Formats can be built up from the flag values or you can use one of
- * the predefined values below.  When testing formats always use the FORMAT_FLAG
- * macros to test for individual features - future versions of the library may
- * add new flags.
- *
- * When reading or writing color-mapped images the format should be set to the
- * format of the entries in the color-map then png_image_{read,write}_colormap
- * called to read or write the color-map and set the format correctly for the
- * image data.  Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly!
- *
- * NOTE: libpng can be built with particular features disabled. If you see
- * compiler errors because the definition of one of the following flags has been
- * compiled out it is because libpng does not have the required support.  It is
- * possible, however, for the libpng configuration to enable the format on just
- * read or just write; in that case you may see an error at run time.  You can
- * guard against this by checking for the definition of the appropriate
- * "_SUPPORTED" macro, one of:
- *
- *    PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED
- */
-#define PNG_FORMAT_FLAG_ALPHA    0x01U /* format with an alpha channel */
-#define PNG_FORMAT_FLAG_COLOR    0x02U /* color format: otherwise grayscale */
-#define PNG_FORMAT_FLAG_LINEAR   0x04U /* 2-byte channels else 1-byte */
-#define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */
-
-#ifdef PNG_FORMAT_BGR_SUPPORTED
-#  define PNG_FORMAT_FLAG_BGR    0x10U /* BGR colors, else order is RGB */
-#endif
-
-#ifdef PNG_FORMAT_AFIRST_SUPPORTED
-#  define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */
-#endif
-
-#define PNG_FORMAT_FLAG_ASSOCIATED_ALPHA 0x40U /* alpha channel is associated */
-
-/* Commonly used formats have predefined macros.
- *
- * First the single byte (sRGB) formats:
- */
-#define PNG_FORMAT_GRAY 0
-#define PNG_FORMAT_GA   PNG_FORMAT_FLAG_ALPHA
-#define PNG_FORMAT_AG   (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST)
-#define PNG_FORMAT_RGB  PNG_FORMAT_FLAG_COLOR
-#define PNG_FORMAT_BGR  (PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_BGR)
-#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_ALPHA)
-#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_AFIRST)
-#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_ALPHA)
-#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_AFIRST)
-
-/* Then the linear 2-byte formats.  When naming these "Y" is used to
- * indicate a luminance (gray) channel.
- */
-#define PNG_FORMAT_LINEAR_Y PNG_FORMAT_FLAG_LINEAR
-#define PNG_FORMAT_LINEAR_Y_ALPHA (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_ALPHA)
-#define PNG_FORMAT_LINEAR_RGB (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR)
-#define PNG_FORMAT_LINEAR_RGB_ALPHA \
-   (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA)
-
-/* With color-mapped formats the image data is one byte for each pixel, the byte
- * is an index into the color-map which is formatted as above.  To obtain a
- * color-mapped format it is sufficient just to add the PNG_FOMAT_FLAG_COLORMAP
- * to one of the above definitions, or you can use one of the definitions below.
- */
-#define PNG_FORMAT_RGB_COLORMAP  (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_COLORMAP)
-#define PNG_FORMAT_BGR_COLORMAP  (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_COLORMAP)
-#define PNG_FORMAT_RGBA_COLORMAP (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_COLORMAP)
-#define PNG_FORMAT_ARGB_COLORMAP (PNG_FORMAT_ARGB|PNG_FORMAT_FLAG_COLORMAP)
-#define PNG_FORMAT_BGRA_COLORMAP (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_COLORMAP)
-#define PNG_FORMAT_ABGR_COLORMAP (PNG_FORMAT_ABGR|PNG_FORMAT_FLAG_COLORMAP)
-
-/* PNG_IMAGE macros
- *
- * These are convenience macros to derive information from a png_image
- * structure.  The PNG_IMAGE_SAMPLE_ macros return values appropriate to the
- * actual image sample values - either the entries in the color-map or the
- * pixels in the image.  The PNG_IMAGE_PIXEL_ macros return corresponding values
- * for the pixels and will always return 1 for color-mapped formats.  The
- * remaining macros return information about the rows in the image and the
- * complete image.
- *
- * NOTE: All the macros that take a png_image::format parameter are compile time
- * constants if the format parameter is, itself, a constant.  Therefore these
- * macros can be used in array declarations and case labels where required.
- * Similarly the macros are also pre-processor constants (sizeof is not used) so
- * they can be used in #if tests.
- *
- * First the information about the samples.
- */
-#define PNG_IMAGE_SAMPLE_CHANNELS(fmt)\
-   (((fmt)&(PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA))+1)
-   /* Return the total number of channels in a given format: 1..4 */
-
-#define PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)\
-   ((((fmt) & PNG_FORMAT_FLAG_LINEAR) >> 2)+1)
-   /* Return the size in bytes of a single component of a pixel or color-map
-    * entry (as appropriate) in the image: 1 or 2.
-    */
-
-#define PNG_IMAGE_SAMPLE_SIZE(fmt)\
-   (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt))
-   /* This is the size of the sample data for one sample.  If the image is
-    * color-mapped it is the size of one color-map entry (and image pixels are
-    * one byte in size), otherwise it is the size of one image pixel.
-    */
-
-#define PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(fmt)\
-   (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * 256)
-   /* The maximum size of the color-map required by the format expressed in a
-    * count of components.  This can be used to compile-time allocate a
-    * color-map:
-    *
-    * png_uint_16 colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(linear_fmt)];
-    *
-    * png_byte colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(sRGB_fmt)];
-    *
-    * Alternatively use the PNG_IMAGE_COLORMAP_SIZE macro below to use the
-    * information from one of the png_image_begin_read_ APIs and dynamically
-    * allocate the required memory.
-    */
-
-/* Corresponding information about the pixels */
-#define PNG_IMAGE_PIXEL_(test,fmt)\
-   (((fmt)&PNG_FORMAT_FLAG_COLORMAP)?1:test(fmt))
-
-#define PNG_IMAGE_PIXEL_CHANNELS(fmt)\
-   PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_CHANNELS,fmt)
-   /* The number of separate channels (components) in a pixel; 1 for a
-    * color-mapped image.
-    */
-
-#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\
-   PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt)
-   /* The size, in bytes, of each component in a pixel; 1 for a color-mapped
-    * image.
-    */
-
-#define PNG_IMAGE_PIXEL_SIZE(fmt) PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_SIZE,fmt)
-   /* The size, in bytes, of a complete pixel; 1 for a color-mapped image. */
-
-/* Information about the whole row, or whole image */
-#define PNG_IMAGE_ROW_STRIDE(image)\
-   (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width)
-   /* Return the total number of components in a single row of the image; this
-    * is the minimum 'row stride', the minimum count of components between each
-    * row.  For a color-mapped image this is the minimum number of bytes in a
-    * row.
-    *
-    * WARNING: this macro overflows for some images with more than one component
-    * and very large image widths.  libpng will refuse to process an image where
-    * this macro would overflow.
-    */
-
-#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\
-   (PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride))
-   /* Return the size, in bytes, of an image buffer given a png_image and a row
-    * stride - the number of components to leave space for in each row.
-    *
-    * WARNING: this macro overflows a 32-bit integer for some large PNG images,
-    * libpng will refuse to process an image where such an overflow would occur.
-    */
-
-#define PNG_IMAGE_SIZE(image)\
-   PNG_IMAGE_BUFFER_SIZE(image, PNG_IMAGE_ROW_STRIDE(image))
-   /* Return the size, in bytes, of the image in memory given just a png_image;
-    * the row stride is the minimum stride required for the image.
-    */
-
-#define PNG_IMAGE_COLORMAP_SIZE(image)\
-   (PNG_IMAGE_SAMPLE_SIZE((image).format) * (image).colormap_entries)
-   /* Return the size, in bytes, of the color-map of this image.  If the image
-    * format is not a color-map format this will return a size sufficient for
-    * 256 entries in the given format; check PNG_FORMAT_FLAG_COLORMAP if
-    * you don't want to allocate a color-map in this case.
-    */
-
-/* PNG_IMAGE_FLAG_*
- *
- * Flags containing additional information about the image are held in the
- * 'flags' field of png_image.
- */
-#define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 0x01
-   /* This indicates that the RGB values of the in-memory bitmap do not
-    * correspond to the red, green and blue end-points defined by sRGB.
-    */
-
-#define PNG_IMAGE_FLAG_FAST 0x02
-   /* On write emphasise speed over compression; the resultant PNG file will be
-    * larger but will be produced significantly faster, particular for large
-    * images.  Do not use this option for images which will be distributed, only
-    * used it when producing intermediate files that will be read back in
-    * repeatedly.  For a typical 24-bit image the option will double the read
-    * speed at the cost of increasing the image size by 25%, however for many
-    * more compressible images the PNG file can be 10 times larger with only a
-    * slight speed gain.
-    */
-
-#define PNG_IMAGE_FLAG_16BIT_sRGB 0x04
-   /* On read if the image is a 16-bit per component image and there is no gAMA
-    * or sRGB chunk assume that the components are sRGB encoded.  Notice that
-    * images output by the simplified API always have gamma information; setting
-    * this flag only affects the interpretation of 16-bit images from an
-    * external source.  It is recommended that the application expose this flag
-    * to the user; the user can normally easily recognize the difference between
-    * linear and sRGB encoding.  This flag has no effect on write - the data
-    * passed to the write APIs must have the correct encoding (as defined
-    * above.)
-    *
-    * If the flag is not set (the default) input 16-bit per component data is
-    * assumed to be linear.
-    *
-    * NOTE: the flag can only be set after the png_image_begin_read_ call,
-    * because that call initializes the 'flags' field.
-    */
-
-#ifdef PNG_SIMPLIFIED_READ_SUPPORTED
-/* READ APIs
- * ---------
- *
- * The png_image passed to the read APIs must have been initialized by setting
- * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.)
- */
-#ifdef PNG_STDIO_SUPPORTED
-PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image,
-   const char *file_name));
-   /* The named file is opened for read and the image header is filled in
-    * from the PNG header in the file.
-    */
-
-PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image,
-   FILE* file));
-   /* The PNG header is read from the stdio FILE object. */
-#endif /* STDIO */
-
-PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image,
-   png_const_voidp memory, size_t size));
-   /* The PNG header is read from the given memory buffer. */
-
-PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image,
-   png_const_colorp background, void *buffer, png_int_32 row_stride,
-   void *colormap));
-   /* Finish reading the image into the supplied buffer and clean up the
-    * png_image structure.
-    *
-    * row_stride is the step, in byte or 2-byte units as appropriate,
-    * between adjacent rows.  A positive stride indicates that the top-most row
-    * is first in the buffer - the normal top-down arrangement.  A negative
-    * stride indicates that the bottom-most row is first in the buffer.
-    *
-    * background need only be supplied if an alpha channel must be removed from
-    * a png_byte format and the removal is to be done by compositing on a solid
-    * color; otherwise it may be NULL and any composition will be done directly
-    * onto the buffer.  The value is an sRGB color to use for the background,
-    * for grayscale output the green channel is used.
-    *
-    * background must be supplied when an alpha channel must be removed from a
-    * single byte color-mapped output format, in other words if:
-    *
-    * 1) The original format from png_image_begin_read_from_* had
-    *    PNG_FORMAT_FLAG_ALPHA set.
-    * 2) The format set by the application does not.
-    * 3) The format set by the application has PNG_FORMAT_FLAG_COLORMAP set and
-    *    PNG_FORMAT_FLAG_LINEAR *not* set.
-    *
-    * For linear output removing the alpha channel is always done by compositing
-    * on black and background is ignored.
-    *
-    * colormap must be supplied when PNG_FORMAT_FLAG_COLORMAP is set.  It must
-    * be at least the size (in bytes) returned by PNG_IMAGE_COLORMAP_SIZE.
-    * image->colormap_entries will be updated to the actual number of entries
-    * written to the colormap; this may be less than the original value.
-    */
-
-PNG_EXPORT(238, void, png_image_free, (png_imagep image));
-   /* Free any data allocated by libpng in image->opaque, setting the pointer to
-    * NULL.  May be called at any time after the structure is initialized.
-    */
-#endif /* SIMPLIFIED_READ */
-
-#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED
-/* WRITE APIS
- * ----------
- * For write you must initialize a png_image structure to describe the image to
- * be written.  To do this use memset to set the whole structure to 0 then
- * initialize fields describing your image.
- *
- * version: must be set to PNG_IMAGE_VERSION
- * opaque: must be initialized to NULL
- * width: image width in pixels
- * height: image height in rows
- * format: the format of the data (image and color-map) you wish to write
- * flags: set to 0 unless one of the defined flags applies; set
- *    PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images where the RGB
- *    values do not correspond to the colors in sRGB.
- * colormap_entries: set to the number of entries in the color-map (0 to 256)
- */
-#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
-PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image,
-   const char *file, int convert_to_8bit, const void *buffer,
-   png_int_32 row_stride, const void *colormap));
-   /* Write the image to the named file. */
-
-PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file,
-   int convert_to_8_bit, const void *buffer, png_int_32 row_stride,
-   const void *colormap));
-   /* Write the image to the given (FILE*). */
-#endif /* SIMPLIFIED_WRITE_STDIO */
-
-/* With all write APIs if image is in one of the linear formats with 16-bit
- * data then setting convert_to_8_bit will cause the output to be an 8-bit PNG
- * gamma encoded according to the sRGB specification, otherwise a 16-bit linear
- * encoded PNG file is written.
- *
- * With color-mapped data formats the colormap parameter point to a color-map
- * with at least image->colormap_entries encoded in the specified format.  If
- * the format is linear the written PNG color-map will be converted to sRGB
- * regardless of the convert_to_8_bit flag.
- *
- * With all APIs row_stride is handled as in the read APIs - it is the spacing
- * from one row to the next in component sized units (1 or 2 bytes) and if
- * negative indicates a bottom-up row layout in the buffer.  If row_stride is
- * zero, libpng will calculate it for you from the image width and number of
- * channels.
- *
- * Note that the write API does not support interlacing, sub-8-bit pixels or
- * most ancillary chunks.  If you need to write text chunks (e.g. for copyright
- * notices) you need to use one of the other APIs.
- */
-
-PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory,
-   png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit,
-   const void *buffer, png_int_32 row_stride, const void *colormap));
-   /* Write the image to the given memory buffer.  The function both writes the
-    * whole PNG data stream to *memory and updates *memory_bytes with the count
-    * of bytes written.
-    *
-    * 'memory' may be NULL.  In this case *memory_bytes is not read however on
-    * success the number of bytes which would have been written will still be
-    * stored in *memory_bytes.  On failure *memory_bytes will contain 0.
-    *
-    * If 'memory' is not NULL it must point to memory[*memory_bytes] of
-    * writeable memory.
-    *
-    * If the function returns success memory[*memory_bytes] (if 'memory' is not
-    * NULL) contains the written PNG data.  *memory_bytes will always be less
-    * than or equal to the original value.
-    *
-    * If the function returns false and *memory_bytes was not changed an error
-    * occurred during write.  If *memory_bytes was changed, or is not 0 if
-    * 'memory' was NULL, the write would have succeeded but for the memory
-    * buffer being too small.  *memory_bytes contains the required number of
-    * bytes and will be bigger that the original value.
-    */
-
-#define png_image_write_get_memory_size(image, size, convert_to_8_bit, buffer,\
-   row_stride, colormap)\
-   png_image_write_to_memory(&(image), 0, &(size), convert_to_8_bit, buffer,\
-         row_stride, colormap)
-   /* Return the amount of memory in 'size' required to compress this image.
-    * The png_image structure 'image' must be filled in as in the above
-    * function and must not be changed before the actual write call, the buffer
-    * and all other parameters must also be identical to that in the final
-    * write call.  The 'size' variable need not be initialized.
-    *
-    * NOTE: the macro returns true/false, if false is returned 'size' will be
-    * set to zero and the write failed and probably will fail if tried again.
-    */
-
-/* You can pre-allocate the buffer by making sure it is of sufficient size
- * regardless of the amount of compression achieved.  The buffer size will
- * always be bigger than the original image and it will never be filled.  The
- * following macros are provided to assist in allocating the buffer.
- */
-#define PNG_IMAGE_DATA_SIZE(image) (PNG_IMAGE_SIZE(image)+(image).height)
-   /* The number of uncompressed bytes in the PNG byte encoding of the image;
-    * uncompressing the PNG IDAT data will give this number of bytes.
-    *
-    * NOTE: while PNG_IMAGE_SIZE cannot overflow for an image in memory this
-    * macro can because of the extra bytes used in the PNG byte encoding.  You
-    * need to avoid this macro if your image size approaches 2^30 in width or
-    * height.  The same goes for the remainder of these macros; they all produce
-    * bigger numbers than the actual in-memory image size.
-    */
-#ifndef PNG_ZLIB_MAX_SIZE
-#  define PNG_ZLIB_MAX_SIZE(b) ((b)+(((b)+7U)>>3)+(((b)+63U)>>6)+11U)
-   /* An upper bound on the number of compressed bytes given 'b' uncompressed
-    * bytes.  This is based on deflateBounds() in zlib; different
-    * implementations of zlib compression may conceivably produce more data so
-    * if your zlib implementation is not zlib itself redefine this macro
-    * appropriately.
-    */
-#endif
-
-#define PNG_IMAGE_COMPRESSED_SIZE_MAX(image)\
-   PNG_ZLIB_MAX_SIZE((png_alloc_size_t)PNG_IMAGE_DATA_SIZE(image))
-   /* An upper bound on the size of the data in the PNG IDAT chunks. */
-
-#define PNG_IMAGE_PNG_SIZE_MAX_(image, image_size)\
-   ((8U/*sig*/+25U/*IHDR*/+16U/*gAMA*/+44U/*cHRM*/+12U/*IEND*/+\
-    (((image).format&PNG_FORMAT_FLAG_COLORMAP)?/*colormap: PLTE, tRNS*/\
-    12U+3U*(image).colormap_entries/*PLTE data*/+\
-    (((image).format&PNG_FORMAT_FLAG_ALPHA)?\
-    12U/*tRNS*/+(image).colormap_entries:0U):0U)+\
-    12U)+(12U*((image_size)/PNG_ZBUF_SIZE))/*IDAT*/+(image_size))
-   /* A helper for the following macro; if your compiler cannot handle the
-    * following macro use this one with the result of
-    * PNG_IMAGE_COMPRESSED_SIZE_MAX(image) as the second argument (most
-    * compilers should handle this just fine.)
-    */
-
-#define PNG_IMAGE_PNG_SIZE_MAX(image)\
-   PNG_IMAGE_PNG_SIZE_MAX_(image, PNG_IMAGE_COMPRESSED_SIZE_MAX(image))
-   /* An upper bound on the total length of the PNG data stream for 'image'.
-    * The result is of type png_alloc_size_t, on 32-bit systems this may
-    * overflow even though PNG_IMAGE_DATA_SIZE does not overflow; the write will
-    * run out of buffer space but return a corrected size which should work.
-    */
-#endif /* SIMPLIFIED_WRITE */
-/*******************************************************************************
- *  END OF SIMPLIFIED API
- ******************************************************************************/
-#endif /* SIMPLIFIED_{READ|WRITE} */
-
-/*******************************************************************************
- * Section 6: IMPLEMENTATION OPTIONS
- *******************************************************************************
- *
- * Support for arbitrary implementation-specific optimizations.  The API allows
- * particular options to be turned on or off.  'Option' is the number of the
- * option and 'onoff' is 0 (off) or non-0 (on).  The value returned is given
- * by the PNG_OPTION_ defines below.
- *
- * HARDWARE: normally hardware capabilities, such as the Intel SSE instructions,
- *           are detected at run time, however sometimes it may be impossible
- *           to do this in user mode, in which case it is necessary to discover
- *           the capabilities in an OS specific way.  Such capabilities are
- *           listed here when libpng has support for them and must be turned
- *           ON by the application if present.
- *
- * SOFTWARE: sometimes software optimizations actually result in performance
- *           decrease on some architectures or systems, or with some sets of
- *           PNG images.  'Software' options allow such optimizations to be
- *           selected at run time.
- */
-#ifdef PNG_SET_OPTION_SUPPORTED
-#ifdef PNG_ARM_NEON_API_SUPPORTED
-#  define PNG_ARM_NEON   0 /* HARDWARE: ARM Neon SIMD instructions supported */
-#endif
-#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */
-#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */
-#ifdef PNG_MIPS_MSA_API_SUPPORTED
-#  define PNG_MIPS_MSA   6 /* HARDWARE: MIPS Msa SIMD instructions supported */
-#endif
-#define PNG_IGNORE_ADLER32 8
-#ifdef PNG_POWERPC_VSX_API_SUPPORTED
-#  define PNG_POWERPC_VSX   10 /* HARDWARE: PowerPC VSX SIMD instructions supported */
-#endif
-#define PNG_OPTION_NEXT  12 /* Next option - numbers must be even */
-
-/* Return values: NOTE: there are four values and 'off' is *not* zero */
-#define PNG_OPTION_UNSET   0 /* Unset - defaults to off */
-#define PNG_OPTION_INVALID 1 /* Option number out of range */
-#define PNG_OPTION_OFF     2
-#define PNG_OPTION_ON      3
-
-PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option,
-   int onoff));
-#endif /* SET_OPTION */
-
-/*******************************************************************************
- *  END OF HARDWARE AND SOFTWARE OPTIONS
- ******************************************************************************/
-
-/* Maintainer: Put new public prototypes here ^, in libpng.3, in project
- * defs, and in scripts/symbols.def.
- */
-
-/* The last ordinal number (this is the *last* one already used; the next
- * one to use is one more than this.)
- */
-#ifdef PNG_EXPORT_LAST_ORDINAL
-  PNG_EXPORT_LAST_ORDINAL(249);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* PNG_VERSION_INFO_ONLY */
-/* Do not put anything past this line */
-#endif /* PNG_H */
diff --git a/third_party/libpng16/pngconf.h b/third_party/libpng16/pngconf.h
deleted file mode 100644
index 927a769..0000000
--- a/third_party/libpng16/pngconf.h
+++ /dev/null
@@ -1,623 +0,0 @@
-
-/* pngconf.h - machine-configurable file for libpng
- *
- * libpng version 1.6.37
- *
- * Copyright (c) 2018-2019 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- *
- * Any machine specific code is near the front of this file, so if you
- * are configuring libpng for a machine, you may want to read the section
- * starting here down to where it starts to typedef png_color, png_text,
- * and png_info.
- */
-
-#ifndef PNGCONF_H
-#define PNGCONF_H
-
-#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */
-
-/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C
- * compiler for correct compilation.  The following header files are required by
- * the standard.  If your compiler doesn't provide these header files, or they
- * do not match the standard, you will need to provide/improve them.
- */
-#include <limits.h>
-#include <stddef.h>
-
-/* Library header files.  These header files are all defined by ISOC90; libpng
- * expects conformant implementations, however, an ISOC90 conformant system need
- * not provide these header files if the functionality cannot be implemented.
- * In this case it will be necessary to disable the relevant parts of libpng in
- * the build of pnglibconf.h.
- *
- * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not
- * include this unnecessary header file.
- */
-
-#ifdef PNG_STDIO_SUPPORTED
-   /* Required for the definition of FILE: */
-#  include <stdio.h>
-#endif
-
-#ifdef PNG_SETJMP_SUPPORTED
-   /* Required for the definition of jmp_buf and the declaration of longjmp: */
-#  include <setjmp.h>
-#endif
-
-#ifdef PNG_CONVERT_tIME_SUPPORTED
-   /* Required for struct tm: */
-#  include <time.h>
-#endif
-
-#endif /* PNG_BUILDING_SYMBOL_TABLE */
-
-/* Prior to 1.6.0, it was possible to turn off 'const' in declarations,
- * using PNG_NO_CONST.  This is no longer supported.
- */
-#define PNG_CONST const /* backward compatibility only */
-
-/* This controls optimization of the reading of 16-bit and 32-bit
- * values from PNG files.  It can be set on a per-app-file basis: it
- * just changes whether a macro is used when the function is called.
- * The library builder sets the default; if read functions are not
- * built into the library the macro implementation is forced on.
- */
-#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED
-#  define PNG_USE_READ_MACROS
-#endif
-#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS)
-#  if PNG_DEFAULT_READ_MACROS
-#    define PNG_USE_READ_MACROS
-#  endif
-#endif
-
-/* COMPILER SPECIFIC OPTIONS.
- *
- * These options are provided so that a variety of difficult compilers
- * can be used.  Some are fixed at build time (e.g. PNG_API_RULE
- * below) but still have compiler specific implementations, others
- * may be changed on a per-file basis when compiling against libpng.
- */
-
-/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect
- * against legacy (pre ISOC90) compilers that did not understand function
- * prototypes.  It is not required for modern C compilers.
- */
-#ifndef PNGARG
-#  define PNGARG(arglist) arglist
-#endif
-
-/* Function calling conventions.
- * =============================
- * Normally it is not necessary to specify to the compiler how to call
- * a function - it just does it - however on x86 systems derived from
- * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems
- * and some others) there are multiple ways to call a function and the
- * default can be changed on the compiler command line.  For this reason
- * libpng specifies the calling convention of every exported function and
- * every function called via a user supplied function pointer.  This is
- * done in this file by defining the following macros:
- *
- * PNGAPI    Calling convention for exported functions.
- * PNGCBAPI  Calling convention for user provided (callback) functions.
- * PNGCAPI   Calling convention used by the ANSI-C library (required
- *           for longjmp callbacks and sometimes used internally to
- *           specify the calling convention for zlib).
- *
- * These macros should never be overridden.  If it is necessary to
- * change calling convention in a private build this can be done
- * by setting PNG_API_RULE (which defaults to 0) to one of the values
- * below to select the correct 'API' variants.
- *
- * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout.
- *                This is correct in every known environment.
- * PNG_API_RULE=1 Use the operating system convention for PNGAPI and
- *                the 'C' calling convention (from PNGCAPI) for
- *                callbacks (PNGCBAPI).  This is no longer required
- *                in any known environment - if it has to be used
- *                please post an explanation of the problem to the
- *                libpng mailing list.
- *
- * These cases only differ if the operating system does not use the C
- * calling convention, at present this just means the above cases
- * (x86 DOS/Windows systems) and, even then, this does not apply to
- * Cygwin running on those systems.
- *
- * Note that the value must be defined in pnglibconf.h so that what
- * the application uses to call the library matches the conventions
- * set when building the library.
- */
-
-/* Symbol export
- * =============
- * When building a shared library it is almost always necessary to tell
- * the compiler which symbols to export.  The png.h macro 'PNG_EXPORT'
- * is used to mark the symbols.  On some systems these symbols can be
- * extracted at link time and need no special processing by the compiler,
- * on other systems the symbols are flagged by the compiler and just
- * the declaration requires a special tag applied (unfortunately) in a
- * compiler dependent way.  Some systems can do either.
- *
- * A small number of older systems also require a symbol from a DLL to
- * be flagged to the program that calls it.  This is a problem because
- * we do not know in the header file included by application code that
- * the symbol will come from a shared library, as opposed to a statically
- * linked one.  For this reason the application must tell us by setting
- * the magic flag PNG_USE_DLL to turn on the special processing before
- * it includes png.h.
- *
- * Four additional macros are used to make this happen:
- *
- * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from
- *            the build or imported if PNG_USE_DLL is set - compiler
- *            and system specific.
- *
- * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to
- *                       'type', compiler specific.
- *
- * PNG_DLL_EXPORT Set to the magic to use during a libpng build to
- *                make a symbol exported from the DLL.  Not used in the
- *                public header files; see pngpriv.h for how it is used
- *                in the libpng build.
- *
- * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come
- *                from a DLL - used to define PNG_IMPEXP when
- *                PNG_USE_DLL is set.
- */
-
-/* System specific discovery.
- * ==========================
- * This code is used at build time to find PNG_IMPEXP, the API settings
- * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL
- * import processing is possible.  On Windows systems it also sets
- * compiler-specific macros to the values required to change the calling
- * conventions of the various functions.
- */
-#if defined(_Windows) || defined(_WINDOWS) || defined(WIN32) ||\
-    defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
-  /* Windows system (DOS doesn't support DLLs).  Includes builds under Cygwin or
-   * MinGW on any architecture currently supported by Windows.  Also includes
-   * Watcom builds but these need special treatment because they are not
-   * compatible with GCC or Visual C because of different calling conventions.
-   */
-#  if PNG_API_RULE == 2
-   /* If this line results in an error, either because __watcall is not
-    * understood or because of a redefine just below you cannot use *this*
-    * build of the library with the compiler you are using.  *This* build was
-    * build using Watcom and applications must also be built using Watcom!
-    */
-#    define PNGCAPI __watcall
-#  endif
-
-#  if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800))
-#    define PNGCAPI __cdecl
-#    if PNG_API_RULE == 1
-   /* If this line results in an error __stdcall is not understood and
-    * PNG_API_RULE should not have been set to '1'.
-    */
-#      define PNGAPI __stdcall
-#    endif
-#  else
-   /* An older compiler, or one not detected (erroneously) above,
-    * if necessary override on the command line to get the correct
-    * variants for the compiler.
-    */
-#    ifndef PNGCAPI
-#      define PNGCAPI _cdecl
-#    endif
-#    if PNG_API_RULE == 1 && !defined(PNGAPI)
-#      define PNGAPI _stdcall
-#    endif
-#  endif /* compiler/api */
-
-  /* NOTE: PNGCBAPI always defaults to PNGCAPI. */
-
-#  if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD)
-#     error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed"
-#  endif
-
-#  if (defined(_MSC_VER) && _MSC_VER < 800) ||\
-      (defined(__BORLANDC__) && __BORLANDC__ < 0x500)
-   /* older Borland and MSC
-    * compilers used '__export' and required this to be after
-    * the type.
-    */
-#    ifndef PNG_EXPORT_TYPE
-#      define PNG_EXPORT_TYPE(type) type PNG_IMPEXP
-#    endif
-#    define PNG_DLL_EXPORT __export
-#  else /* newer compiler */
-#    define PNG_DLL_EXPORT __declspec(dllexport)
-#    ifndef PNG_DLL_IMPORT
-#      define PNG_DLL_IMPORT __declspec(dllimport)
-#    endif
-#  endif /* compiler */
-
-#else /* !Windows */
-#  if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__)
-#    define PNGAPI _System
-#  else /* !Windows/x86 && !OS/2 */
-   /* Use the defaults, or define PNG*API on the command line (but
-    * this will have to be done for every compile!)
-    */
-#  endif /* other system, !OS/2 */
-#endif /* !Windows/x86 */
-
-/* Now do all the defaulting . */
-#ifndef PNGCAPI
-#  define PNGCAPI
-#endif
-#ifndef PNGCBAPI
-#  define PNGCBAPI PNGCAPI
-#endif
-#ifndef PNGAPI
-#  define PNGAPI PNGCAPI
-#endif
-
-/* PNG_IMPEXP may be set on the compilation system command line or (if not set)
- * then in an internal header file when building the library, otherwise (when
- * using the library) it is set here.
- */
-#ifndef PNG_IMPEXP
-#  if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT)
-   /* This forces use of a DLL, disallowing static linking */
-#    define PNG_IMPEXP PNG_DLL_IMPORT
-#  endif
-
-#  ifndef PNG_IMPEXP
-#    define PNG_IMPEXP
-#  endif
-#endif
-
-/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat
- * 'attributes' as a storage class - the attributes go at the start of the
- * function definition, and attributes are always appended regardless of the
- * compiler.  This considerably simplifies these macros but may cause problems
- * if any compilers both need function attributes and fail to handle them as
- * a storage class (this is unlikely.)
- */
-#ifndef PNG_FUNCTION
-#  define PNG_FUNCTION(type, name, args, attributes) attributes type name args
-#endif
-
-#ifndef PNG_EXPORT_TYPE
-#  define PNG_EXPORT_TYPE(type) PNG_IMPEXP type
-#endif
-
-   /* The ordinal value is only relevant when preprocessing png.h for symbol
-    * table entries, so we discard it here.  See the .dfn files in the
-    * scripts directory.
-    */
-
-#ifndef PNG_EXPORTA
-#  define PNG_EXPORTA(ordinal, type, name, args, attributes) \
-      PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), PNGARG(args), \
-      PNG_LINKAGE_API attributes)
-#endif
-
-/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument,
- * so make something non-empty to satisfy the requirement:
- */
-#define PNG_EMPTY /*empty list*/
-
-#define PNG_EXPORT(ordinal, type, name, args) \
-   PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY)
-
-/* Use PNG_REMOVED to comment out a removed interface. */
-#ifndef PNG_REMOVED
-#  define PNG_REMOVED(ordinal, type, name, args, attributes)
-#endif
-
-#ifndef PNG_CALLBACK
-#  define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args)
-#endif
-
-/* Support for compiler specific function attributes.  These are used
- * so that where compiler support is available incorrect use of API
- * functions in png.h will generate compiler warnings.
- *
- * Added at libpng-1.2.41.
- */
-
-#ifndef PNG_NO_PEDANTIC_WARNINGS
-#  ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED
-#    define PNG_PEDANTIC_WARNINGS_SUPPORTED
-#  endif
-#endif
-
-#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED
-  /* Support for compiler specific function attributes.  These are used
-   * so that where compiler support is available, incorrect use of API
-   * functions in png.h will generate compiler warnings.  Added at libpng
-   * version 1.2.41.  Disabling these removes the warnings but may also produce
-   * less efficient code.
-   */
-#  if defined(__clang__) && defined(__has_attribute)
-   /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */
-#    if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__)
-#      define PNG_USE_RESULT __attribute__((__warn_unused_result__))
-#    endif
-#    if !defined(PNG_NORETURN) && __has_attribute(__noreturn__)
-#      define PNG_NORETURN __attribute__((__noreturn__))
-#    endif
-#    if !defined(PNG_ALLOCATED) && __has_attribute(__malloc__)
-#      define PNG_ALLOCATED __attribute__((__malloc__))
-#    endif
-#    if !defined(PNG_DEPRECATED) && __has_attribute(__deprecated__)
-#      define PNG_DEPRECATED __attribute__((__deprecated__))
-#    endif
-#    if !defined(PNG_PRIVATE)
-#      ifdef __has_extension
-#        if __has_extension(attribute_unavailable_with_message)
-#          define PNG_PRIVATE __attribute__((__unavailable__(\
-             "This function is not exported by libpng.")))
-#        endif
-#      endif
-#    endif
-#    ifndef PNG_RESTRICT
-#      define PNG_RESTRICT __restrict
-#    endif
-
-#  elif defined(__GNUC__)
-#    ifndef PNG_USE_RESULT
-#      define PNG_USE_RESULT __attribute__((__warn_unused_result__))
-#    endif
-#    ifndef PNG_NORETURN
-#      define PNG_NORETURN   __attribute__((__noreturn__))
-#    endif
-#    if __GNUC__ >= 3
-#      ifndef PNG_ALLOCATED
-#        define PNG_ALLOCATED  __attribute__((__malloc__))
-#      endif
-#      ifndef PNG_DEPRECATED
-#        define PNG_DEPRECATED __attribute__((__deprecated__))
-#      endif
-#      ifndef PNG_PRIVATE
-#        if 0 /* Doesn't work so we use deprecated instead*/
-#          define PNG_PRIVATE \
-            __attribute__((warning("This function is not exported by libpng.")))
-#        else
-#          define PNG_PRIVATE \
-            __attribute__((__deprecated__))
-#        endif
-#      endif
-#      if ((__GNUC__ > 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1))
-#        ifndef PNG_RESTRICT
-#          define PNG_RESTRICT __restrict
-#        endif
-#      endif /* __GNUC__.__GNUC_MINOR__ > 3.0 */
-#    endif /* __GNUC__ >= 3 */
-
-#  elif defined(_MSC_VER)  && (_MSC_VER >= 1300)
-#    ifndef PNG_USE_RESULT
-#      define PNG_USE_RESULT /* not supported */
-#    endif
-#    ifndef PNG_NORETURN
-#      define PNG_NORETURN   __declspec(noreturn)
-#    endif
-#    ifndef PNG_ALLOCATED
-#      if (_MSC_VER >= 1400)
-#        define PNG_ALLOCATED __declspec(restrict)
-#      endif
-#    endif
-#    ifndef PNG_DEPRECATED
-#      define PNG_DEPRECATED __declspec(deprecated)
-#    endif
-#    ifndef PNG_PRIVATE
-#      define PNG_PRIVATE __declspec(deprecated)
-#    endif
-#    ifndef PNG_RESTRICT
-#      if (_MSC_VER >= 1400)
-#        define PNG_RESTRICT __restrict
-#      endif
-#    endif
-
-#  elif defined(__WATCOMC__)
-#    ifndef PNG_RESTRICT
-#      define PNG_RESTRICT __restrict
-#    endif
-#  endif
-#endif /* PNG_PEDANTIC_WARNINGS */
-
-#ifndef PNG_DEPRECATED
-#  define PNG_DEPRECATED  /* Use of this function is deprecated */
-#endif
-#ifndef PNG_USE_RESULT
-#  define PNG_USE_RESULT  /* The result of this function must be checked */
-#endif
-#ifndef PNG_NORETURN
-#  define PNG_NORETURN    /* This function does not return */
-#endif
-#ifndef PNG_ALLOCATED
-#  define PNG_ALLOCATED   /* The result of the function is new memory */
-#endif
-#ifndef PNG_PRIVATE
-#  define PNG_PRIVATE     /* This is a private libpng function */
-#endif
-#ifndef PNG_RESTRICT
-#  define PNG_RESTRICT    /* The C99 "restrict" feature */
-#endif
-
-#ifndef PNG_FP_EXPORT     /* A floating point API. */
-#  ifdef PNG_FLOATING_POINT_SUPPORTED
-#     define PNG_FP_EXPORT(ordinal, type, name, args)\
-         PNG_EXPORT(ordinal, type, name, args);
-#  else                   /* No floating point APIs */
-#     define PNG_FP_EXPORT(ordinal, type, name, args)
-#  endif
-#endif
-#ifndef PNG_FIXED_EXPORT  /* A fixed point API. */
-#  ifdef PNG_FIXED_POINT_SUPPORTED
-#     define PNG_FIXED_EXPORT(ordinal, type, name, args)\
-         PNG_EXPORT(ordinal, type, name, args);
-#  else                   /* No fixed point APIs */
-#     define PNG_FIXED_EXPORT(ordinal, type, name, args)
-#  endif
-#endif
-
-#ifndef PNG_BUILDING_SYMBOL_TABLE
-/* Some typedefs to get us started.  These should be safe on most of the common
- * platforms.
- *
- * png_uint_32 and png_int_32 may, currently, be larger than required to hold a
- * 32-bit value however this is not normally advisable.
- *
- * png_uint_16 and png_int_16 should always be two bytes in size - this is
- * verified at library build time.
- *
- * png_byte must always be one byte in size.
- *
- * The checks below use constants from limits.h, as defined by the ISOC90
- * standard.
- */
-#if CHAR_BIT == 8 && UCHAR_MAX == 255
-   typedef unsigned char png_byte;
-#else
-#  error "libpng requires 8-bit bytes"
-#endif
-
-#if INT_MIN == -32768 && INT_MAX == 32767
-   typedef int png_int_16;
-#elif SHRT_MIN == -32768 && SHRT_MAX == 32767
-   typedef short png_int_16;
-#else
-#  error "libpng requires a signed 16-bit type"
-#endif
-
-#if UINT_MAX == 65535
-   typedef unsigned int png_uint_16;
-#elif USHRT_MAX == 65535
-   typedef unsigned short png_uint_16;
-#else
-#  error "libpng requires an unsigned 16-bit type"
-#endif
-
-#if INT_MIN < -2147483646 && INT_MAX > 2147483646
-   typedef int png_int_32;
-#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646
-   typedef long int png_int_32;
-#else
-#  error "libpng requires a signed 32-bit (or more) type"
-#endif
-
-#if UINT_MAX > 4294967294U
-   typedef unsigned int png_uint_32;
-#elif ULONG_MAX > 4294967294U
-   typedef unsigned long int png_uint_32;
-#else
-#  error "libpng requires an unsigned 32-bit (or more) type"
-#endif
-
-/* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t.
- * From 1.6.0 onwards, an ISO C90 compiler, as well as a standard-compliant
- * behavior of sizeof and ptrdiff_t are required.
- * The legacy typedefs are provided here for backwards compatibility.
- */
-typedef size_t png_size_t;
-typedef ptrdiff_t png_ptrdiff_t;
-
-/* libpng needs to know the maximum value of 'size_t' and this controls the
- * definition of png_alloc_size_t, below.  This maximum value of size_t limits
- * but does not control the maximum allocations the library makes - there is
- * direct application control of this through png_set_user_limits().
- */
-#ifndef PNG_SMALL_SIZE_T
-   /* Compiler specific tests for systems where size_t is known to be less than
-    * 32 bits (some of these systems may no longer work because of the lack of
-    * 'far' support; see above.)
-    */
-#  if (defined(__TURBOC__) && !defined(__FLAT__)) ||\
-   (defined(_MSC_VER) && defined(MAXSEG_64K))
-#     define PNG_SMALL_SIZE_T
-#  endif
-#endif
-
-/* png_alloc_size_t is guaranteed to be no smaller than size_t, and no smaller
- * than png_uint_32.  Casts from size_t or png_uint_32 to png_alloc_size_t are
- * not necessary; in fact, it is recommended not to use them at all, so that
- * the compiler can complain when something turns out to be problematic.
- *
- * Casts in the other direction (from png_alloc_size_t to size_t or
- * png_uint_32) should be explicitly applied; however, we do not expect to
- * encounter practical situations that require such conversions.
- *
- * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than
- * 4294967295 - i.e. less than the maximum value of png_uint_32.
- */
-#ifdef PNG_SMALL_SIZE_T
-   typedef png_uint_32 png_alloc_size_t;
-#else
-   typedef size_t png_alloc_size_t;
-#endif
-
-/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler
- * implementations of Intel CPU specific support of user-mode segmented address
- * spaces, where 16-bit pointers address more than 65536 bytes of memory using
- * separate 'segment' registers.  The implementation requires two different
- * types of pointer (only one of which includes the segment value.)
- *
- * If required this support is available in version 1.2 of libpng and may be
- * available in versions through 1.5, although the correctness of the code has
- * not been verified recently.
- */
-
-/* Typedef for floating-point numbers that are converted to fixed-point with a
- * multiple of 100,000, e.g., gamma
- */
-typedef png_int_32 png_fixed_point;
-
-/* Add typedefs for pointers */
-typedef void                  * png_voidp;
-typedef const void            * png_const_voidp;
-typedef png_byte              * png_bytep;
-typedef const png_byte        * png_const_bytep;
-typedef png_uint_32           * png_uint_32p;
-typedef const png_uint_32     * png_const_uint_32p;
-typedef png_int_32            * png_int_32p;
-typedef const png_int_32      * png_const_int_32p;
-typedef png_uint_16           * png_uint_16p;
-typedef const png_uint_16     * png_const_uint_16p;
-typedef png_int_16            * png_int_16p;
-typedef const png_int_16      * png_const_int_16p;
-typedef char                  * png_charp;
-typedef const char            * png_const_charp;
-typedef png_fixed_point       * png_fixed_point_p;
-typedef const png_fixed_point * png_const_fixed_point_p;
-typedef size_t                * png_size_tp;
-typedef const size_t          * png_const_size_tp;
-
-#ifdef PNG_STDIO_SUPPORTED
-typedef FILE            * png_FILE_p;
-#endif
-
-#ifdef PNG_FLOATING_POINT_SUPPORTED
-typedef double       * png_doublep;
-typedef const double * png_const_doublep;
-#endif
-
-/* Pointers to pointers; i.e. arrays */
-typedef png_byte        * * png_bytepp;
-typedef png_uint_32     * * png_uint_32pp;
-typedef png_int_32      * * png_int_32pp;
-typedef png_uint_16     * * png_uint_16pp;
-typedef png_int_16      * * png_int_16pp;
-typedef const char      * * png_const_charpp;
-typedef char            * * png_charpp;
-typedef png_fixed_point * * png_fixed_point_pp;
-#ifdef PNG_FLOATING_POINT_SUPPORTED
-typedef double          * * png_doublepp;
-#endif
-
-/* Pointers to pointers to pointers; i.e., pointer to array */
-typedef char            * * * png_charppp;
-
-#endif /* PNG_BUILDING_SYMBOL_TABLE */
-
-#endif /* PNGCONF_H */
diff --git a/third_party/libpng16/pngdebug.h b/third_party/libpng16/pngdebug.h
deleted file mode 100644
index 00d5a45..0000000
--- a/third_party/libpng16/pngdebug.h
+++ /dev/null
@@ -1,153 +0,0 @@
-
-/* pngdebug.h - Debugging macros for libpng, also used in pngtest.c
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2013 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
-/* Define PNG_DEBUG at compile time for debugging information.  Higher
- * numbers for PNG_DEBUG mean more debugging information.  This has
- * only been added since version 0.95 so it is not implemented throughout
- * libpng yet, but more support will be added as needed.
- *
- * png_debug[1-2]?(level, message ,arg{0-2})
- *   Expands to a statement (either a simple expression or a compound
- *   do..while(0) statement) that outputs a message with parameter
- *   substitution if PNG_DEBUG is defined to 2 or more.  If PNG_DEBUG
- *   is undefined, 0 or 1 every png_debug expands to a simple expression
- *   (actually ((void)0)).
- *
- *   level: level of detail of message, starting at 0.  A level 'n'
- *          message is preceded by 'n' 3-space indentations (not implemented
- *          on Microsoft compilers unless PNG_DEBUG_FILE is also
- *          defined, to allow debug DLL compilation with no standard IO).
- *   message: a printf(3) style text string.  A trailing '\n' is added
- *            to the message.
- *   arg: 0 to 2 arguments for printf(3) style substitution in message.
- */
-#ifndef PNGDEBUG_H
-#define PNGDEBUG_H
-/* These settings control the formatting of messages in png.c and pngerror.c */
-/* Moved to pngdebug.h at 1.5.0 */
-#  ifndef PNG_LITERAL_SHARP
-#    define PNG_LITERAL_SHARP 0x23
-#  endif
-#  ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET
-#    define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b
-#  endif
-#  ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET
-#    define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d
-#  endif
-#  ifndef PNG_STRING_NEWLINE
-#    define PNG_STRING_NEWLINE "\n"
-#  endif
-
-#ifdef PNG_DEBUG
-#  if (PNG_DEBUG > 0)
-#    if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER)
-#      include <crtdbg.h>
-#      if (PNG_DEBUG > 1)
-#        ifndef _DEBUG
-#          define _DEBUG
-#        endif
-#        ifndef png_debug
-#          define png_debug(l,m)  _RPT0(_CRT_WARN,m PNG_STRING_NEWLINE)
-#        endif
-#        ifndef png_debug1
-#          define png_debug1(l,m,p1)  _RPT1(_CRT_WARN,m PNG_STRING_NEWLINE,p1)
-#        endif
-#        ifndef png_debug2
-#          define png_debug2(l,m,p1,p2) \
-             _RPT2(_CRT_WARN,m PNG_STRING_NEWLINE,p1,p2)
-#        endif
-#      endif
-#    else /* PNG_DEBUG_FILE || !_MSC_VER */
-#      ifndef PNG_STDIO_SUPPORTED
-#        include <stdio.h> /* not included yet */
-#      endif
-#      ifndef PNG_DEBUG_FILE
-#        define PNG_DEBUG_FILE stderr
-#      endif /* PNG_DEBUG_FILE */
-
-#      if (PNG_DEBUG > 1)
-#        ifdef __STDC__
-#          ifndef png_debug
-#            define png_debug(l,m) \
-       do { \
-       int num_tabs=l; \
-       fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? "   " : \
-         (num_tabs==2 ? "      " : (num_tabs>2 ? "         " : "")))); \
-       } while (0)
-#          endif
-#          ifndef png_debug1
-#            define png_debug1(l,m,p1) \
-       do { \
-       int num_tabs=l; \
-       fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? "   " : \
-         (num_tabs==2 ? "      " : (num_tabs>2 ? "         " : ""))),p1); \
-       } while (0)
-#          endif
-#          ifndef png_debug2
-#            define png_debug2(l,m,p1,p2) \
-       do { \
-       int num_tabs=l; \
-       fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? "   " : \
-         (num_tabs==2 ? "      " : (num_tabs>2 ? "         " : ""))),p1,p2);\
-       } while (0)
-#          endif
-#        else /* __STDC __ */
-#          ifndef png_debug
-#            define png_debug(l,m) \
-       do { \
-       int num_tabs=l; \
-       char format[256]; \
-       snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
-         (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
-         m,PNG_STRING_NEWLINE); \
-       fprintf(PNG_DEBUG_FILE,format); \
-       } while (0)
-#          endif
-#          ifndef png_debug1
-#            define png_debug1(l,m,p1) \
-       do { \
-       int num_tabs=l; \
-       char format[256]; \
-       snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
-         (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
-         m,PNG_STRING_NEWLINE); \
-       fprintf(PNG_DEBUG_FILE,format,p1); \
-       } while (0)
-#          endif
-#          ifndef png_debug2
-#            define png_debug2(l,m,p1,p2) \
-       do { \
-       int num_tabs=l; \
-       char format[256]; \
-       snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
-         (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
-         m,PNG_STRING_NEWLINE); \
-       fprintf(PNG_DEBUG_FILE,format,p1,p2); \
-       } while (0)
-#          endif
-#        endif /* __STDC __ */
-#      endif /* (PNG_DEBUG > 1) */
-
-#    endif /* _MSC_VER */
-#  endif /* (PNG_DEBUG > 0) */
-#endif /* PNG_DEBUG */
-#ifndef png_debug
-#  define png_debug(l, m) ((void)0)
-#endif
-#ifndef png_debug1
-#  define png_debug1(l, m, p1) ((void)0)
-#endif
-#ifndef png_debug2
-#  define png_debug2(l, m, p1, p2) ((void)0)
-#endif
-#endif /* PNGDEBUG_H */
diff --git a/third_party/libpng16/pngerror.c b/third_party/libpng16/pngerror.c
deleted file mode 100644
index ec3a709..0000000
--- a/third_party/libpng16/pngerror.c
+++ /dev/null
@@ -1,963 +0,0 @@
-
-/* pngerror.c - stub functions for i/o and memory allocation
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- *
- * This file provides a location for all error handling.  Users who
- * need special error handling are expected to write replacement functions
- * and use png_set_error_fn() to use those functions.  See the instructions
- * at each function.
- */
-
-#include "pngpriv.h"
-
-#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
-
-static PNG_FUNCTION(void, png_default_error,PNGARG((png_const_structrp png_ptr,
-    png_const_charp error_message)),PNG_NORETURN);
-
-#ifdef PNG_WARNINGS_SUPPORTED
-static void /* PRIVATE */
-png_default_warning PNGARG((png_const_structrp png_ptr,
-    png_const_charp warning_message));
-#endif /* WARNINGS */
-
-/* This function is called whenever there is a fatal error.  This function
- * should not be changed.  If there is a need to handle errors differently,
- * you should supply a replacement error function and use png_set_error_fn()
- * to replace the error function at run-time.
- */
-#ifdef PNG_ERROR_TEXT_SUPPORTED
-PNG_FUNCTION(void,PNGAPI
-png_error,(png_const_structrp png_ptr, png_const_charp error_message),
-    PNG_NORETURN)
-{
-#ifdef PNG_ERROR_NUMBERS_SUPPORTED
-   char msg[16];
-   if (png_ptr != NULL)
-   {
-      if ((png_ptr->flags &
-         (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0)
-      {
-         if (*error_message == PNG_LITERAL_SHARP)
-         {
-            /* Strip "#nnnn " from beginning of error message. */
-            int offset;
-            for (offset = 1; offset<15; offset++)
-               if (error_message[offset] == ' ')
-                  break;
-
-            if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0)
-            {
-               int i;
-               for (i = 0; i < offset - 1; i++)
-                  msg[i] = error_message[i + 1];
-               msg[i - 1] = '\0';
-               error_message = msg;
-            }
-
-            else
-               error_message += offset;
-         }
-
-         else
-         {
-            if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0)
-            {
-               msg[0] = '0';
-               msg[1] = '\0';
-               error_message = msg;
-            }
-         }
-      }
-   }
-#endif
-   if (png_ptr != NULL && png_ptr->error_fn != NULL)
-      (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr),
-          error_message);
-
-   /* If the custom handler doesn't exist, or if it returns,
-      use the default handler, which will not return. */
-   png_default_error(png_ptr, error_message);
-}
-#else
-PNG_FUNCTION(void,PNGAPI
-png_err,(png_const_structrp png_ptr),PNG_NORETURN)
-{
-   /* Prior to 1.5.2 the error_fn received a NULL pointer, expressed
-    * erroneously as '\0', instead of the empty string "".  This was
-    * apparently an error, introduced in libpng-1.2.20, and png_default_error
-    * will crash in this case.
-    */
-   if (png_ptr != NULL && png_ptr->error_fn != NULL)
-      (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr), "");
-
-   /* If the custom handler doesn't exist, or if it returns,
-      use the default handler, which will not return. */
-   png_default_error(png_ptr, "");
-}
-#endif /* ERROR_TEXT */
-
-/* Utility to safely appends strings to a buffer.  This never errors out so
- * error checking is not required in the caller.
- */
-size_t
-png_safecat(png_charp buffer, size_t bufsize, size_t pos,
-    png_const_charp string)
-{
-   if (buffer != NULL && pos < bufsize)
-   {
-      if (string != NULL)
-         while (*string != '\0' && pos < bufsize-1)
-           buffer[pos++] = *string++;
-
-      buffer[pos] = '\0';
-   }
-
-   return pos;
-}
-
-#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED)
-/* Utility to dump an unsigned value into a buffer, given a start pointer and
- * and end pointer (which should point just *beyond* the end of the buffer!)
- * Returns the pointer to the start of the formatted string.
- */
-png_charp
-png_format_number(png_const_charp start, png_charp end, int format,
-    png_alloc_size_t number)
-{
-   int count = 0;    /* number of digits output */
-   int mincount = 1; /* minimum number required */
-   int output = 0;   /* digit output (for the fixed point format) */
-
-   *--end = '\0';
-
-   /* This is written so that the loop always runs at least once, even with
-    * number zero.
-    */
-   while (end > start && (number != 0 || count < mincount))
-   {
-
-      static const char digits[] = "0123456789ABCDEF";
-
-      switch (format)
-      {
-         case PNG_NUMBER_FORMAT_fixed:
-            /* Needs five digits (the fraction) */
-            mincount = 5;
-            if (output != 0 || number % 10 != 0)
-            {
-               *--end = digits[number % 10];
-               output = 1;
-            }
-            number /= 10;
-            break;
-
-         case PNG_NUMBER_FORMAT_02u:
-            /* Expects at least 2 digits. */
-            mincount = 2;
-            /* FALLTHROUGH */
-
-         case PNG_NUMBER_FORMAT_u:
-            *--end = digits[number % 10];
-            number /= 10;
-            break;
-
-         case PNG_NUMBER_FORMAT_02x:
-            /* This format expects at least two digits */
-            mincount = 2;
-            /* FALLTHROUGH */
-
-         case PNG_NUMBER_FORMAT_x:
-            *--end = digits[number & 0xf];
-            number >>= 4;
-            break;
-
-         default: /* an error */
-            number = 0;
-            break;
-      }
-
-      /* Keep track of the number of digits added */
-      ++count;
-
-      /* Float a fixed number here: */
-      if ((format == PNG_NUMBER_FORMAT_fixed) && (count == 5) && (end > start))
-      {
-         /* End of the fraction, but maybe nothing was output?  In that case
-          * drop the decimal point.  If the number is a true zero handle that
-          * here.
-          */
-         if (output != 0)
-            *--end = '.';
-         else if (number == 0) /* and !output */
-            *--end = '0';
-      }
-   }
-
-   return end;
-}
-#endif
-
-#ifdef PNG_WARNINGS_SUPPORTED
-/* This function is called whenever there is a non-fatal error.  This function
- * should not be changed.  If there is a need to handle warnings differently,
- * you should supply a replacement warning function and use
- * png_set_error_fn() to replace the warning function at run-time.
- */
-void PNGAPI
-png_warning(png_const_structrp png_ptr, png_const_charp warning_message)
-{
-   int offset = 0;
-   if (png_ptr != NULL)
-   {
-#ifdef PNG_ERROR_NUMBERS_SUPPORTED
-   if ((png_ptr->flags &
-       (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0)
-#endif
-      {
-         if (*warning_message == PNG_LITERAL_SHARP)
-         {
-            for (offset = 1; offset < 15; offset++)
-               if (warning_message[offset] == ' ')
-                  break;
-         }
-      }
-   }
-   if (png_ptr != NULL && png_ptr->warning_fn != NULL)
-      (*(png_ptr->warning_fn))(png_constcast(png_structrp,png_ptr),
-          warning_message + offset);
-   else
-      png_default_warning(png_ptr, warning_message + offset);
-}
-
-/* These functions support 'formatted' warning messages with up to
- * PNG_WARNING_PARAMETER_COUNT parameters.  In the format string the parameter
- * is introduced by @<number>, where 'number' starts at 1.  This follows the
- * standard established by X/Open for internationalizable error messages.
- */
-void
-png_warning_parameter(png_warning_parameters p, int number,
-    png_const_charp string)
-{
-   if (number > 0 && number <= PNG_WARNING_PARAMETER_COUNT)
-      (void)png_safecat(p[number-1], (sizeof p[number-1]), 0, string);
-}
-
-void
-png_warning_parameter_unsigned(png_warning_parameters p, int number, int format,
-    png_alloc_size_t value)
-{
-   char buffer[PNG_NUMBER_BUFFER_SIZE];
-   png_warning_parameter(p, number, PNG_FORMAT_NUMBER(buffer, format, value));
-}
-
-void
-png_warning_parameter_signed(png_warning_parameters p, int number, int format,
-    png_int_32 value)
-{
-   png_alloc_size_t u;
-   png_charp str;
-   char buffer[PNG_NUMBER_BUFFER_SIZE];
-
-   /* Avoid overflow by doing the negate in a png_alloc_size_t: */
-   u = (png_alloc_size_t)value;
-   if (value < 0)
-      u = ~u + 1;
-
-   str = PNG_FORMAT_NUMBER(buffer, format, u);
-
-   if (value < 0 && str > buffer)
-      *--str = '-';
-
-   png_warning_parameter(p, number, str);
-}
-
-void
-png_formatted_warning(png_const_structrp png_ptr, png_warning_parameters p,
-    png_const_charp message)
-{
-   /* The internal buffer is just 192 bytes - enough for all our messages,
-    * overflow doesn't happen because this code checks!  If someone figures
-    * out how to send us a message longer than 192 bytes, all that will
-    * happen is that the message will be truncated appropriately.
-    */
-   size_t i = 0; /* Index in the msg[] buffer: */
-   char msg[192];
-
-   /* Each iteration through the following loop writes at most one character
-    * to msg[i++] then returns here to validate that there is still space for
-    * the trailing '\0'.  It may (in the case of a parameter) read more than
-    * one character from message[]; it must check for '\0' and continue to the
-    * test if it finds the end of string.
-    */
-   while (i<(sizeof msg)-1 && *message != '\0')
-   {
-      /* '@' at end of string is now just printed (previously it was skipped);
-       * it is an error in the calling code to terminate the string with @.
-       */
-      if (p != NULL && *message == '@' && message[1] != '\0')
-      {
-         int parameter_char = *++message; /* Consume the '@' */
-         static const char valid_parameters[] = "123456789";
-         int parameter = 0;
-
-         /* Search for the parameter digit, the index in the string is the
-          * parameter to use.
-          */
-         while (valid_parameters[parameter] != parameter_char &&
-            valid_parameters[parameter] != '\0')
-            ++parameter;
-
-         /* If the parameter digit is out of range it will just get printed. */
-         if (parameter < PNG_WARNING_PARAMETER_COUNT)
-         {
-            /* Append this parameter */
-            png_const_charp parm = p[parameter];
-            png_const_charp pend = p[parameter] + (sizeof p[parameter]);
-
-            /* No need to copy the trailing '\0' here, but there is no guarantee
-             * that parm[] has been initialized, so there is no guarantee of a
-             * trailing '\0':
-             */
-            while (i<(sizeof msg)-1 && *parm != '\0' && parm < pend)
-               msg[i++] = *parm++;
-
-            /* Consume the parameter digit too: */
-            ++message;
-            continue;
-         }
-
-         /* else not a parameter and there is a character after the @ sign; just
-          * copy that.  This is known not to be '\0' because of the test above.
-          */
-      }
-
-      /* At this point *message can't be '\0', even in the bad parameter case
-       * above where there is a lone '@' at the end of the message string.
-       */
-      msg[i++] = *message++;
-   }
-
-   /* i is always less than (sizeof msg), so: */
-   msg[i] = '\0';
-
-   /* And this is the formatted message. It may be larger than
-    * PNG_MAX_ERROR_TEXT, but that is only used for 'chunk' errors and these
-    * are not (currently) formatted.
-    */
-   png_warning(png_ptr, msg);
-}
-#endif /* WARNINGS */
-
-#ifdef PNG_BENIGN_ERRORS_SUPPORTED
-void PNGAPI
-png_benign_error(png_const_structrp png_ptr, png_const_charp error_message)
-{
-   if ((png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) != 0)
-   {
-#     ifdef PNG_READ_SUPPORTED
-         if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
-            png_ptr->chunk_name != 0)
-            png_chunk_warning(png_ptr, error_message);
-         else
-#     endif
-      png_warning(png_ptr, error_message);
-   }
-
-   else
-   {
-#     ifdef PNG_READ_SUPPORTED
-         if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
-            png_ptr->chunk_name != 0)
-            png_chunk_error(png_ptr, error_message);
-         else
-#     endif
-      png_error(png_ptr, error_message);
-   }
-
-#  ifndef PNG_ERROR_TEXT_SUPPORTED
-      PNG_UNUSED(error_message)
-#  endif
-}
-
-void /* PRIVATE */
-png_app_warning(png_const_structrp png_ptr, png_const_charp error_message)
-{
-   if ((png_ptr->flags & PNG_FLAG_APP_WARNINGS_WARN) != 0)
-      png_warning(png_ptr, error_message);
-   else
-      png_error(png_ptr, error_message);
-
-#  ifndef PNG_ERROR_TEXT_SUPPORTED
-      PNG_UNUSED(error_message)
-#  endif
-}
-
-void /* PRIVATE */
-png_app_error(png_const_structrp png_ptr, png_const_charp error_message)
-{
-   if ((png_ptr->flags & PNG_FLAG_APP_ERRORS_WARN) != 0)
-      png_warning(png_ptr, error_message);
-   else
-      png_error(png_ptr, error_message);
-
-#  ifndef PNG_ERROR_TEXT_SUPPORTED
-      PNG_UNUSED(error_message)
-#  endif
-}
-#endif /* BENIGN_ERRORS */
-
-#define PNG_MAX_ERROR_TEXT 196 /* Currently limited by profile_error in png.c */
-#if defined(PNG_WARNINGS_SUPPORTED) || \
-   (defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED))
-/* These utilities are used internally to build an error message that relates
- * to the current chunk.  The chunk name comes from png_ptr->chunk_name,
- * which is used to prefix the message.  The message is limited in length
- * to 63 bytes. The name characters are output as hex digits wrapped in []
- * if the character is invalid.
- */
-#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
-static const char png_digit[16] = {
-   '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
-   'A', 'B', 'C', 'D', 'E', 'F'
-};
-
-static void /* PRIVATE */
-png_format_buffer(png_const_structrp png_ptr, png_charp buffer, png_const_charp
-    error_message)
-{
-   png_uint_32 chunk_name = png_ptr->chunk_name;
-   int iout = 0, ishift = 24;
-
-   while (ishift >= 0)
-   {
-      int c = (int)(chunk_name >> ishift) & 0xff;
-
-      ishift -= 8;
-      if (isnonalpha(c) != 0)
-      {
-         buffer[iout++] = PNG_LITERAL_LEFT_SQUARE_BRACKET;
-         buffer[iout++] = png_digit[(c & 0xf0) >> 4];
-         buffer[iout++] = png_digit[c & 0x0f];
-         buffer[iout++] = PNG_LITERAL_RIGHT_SQUARE_BRACKET;
-      }
-
-      else
-      {
-         buffer[iout++] = (char)c;
-      }
-   }
-
-   if (error_message == NULL)
-      buffer[iout] = '\0';
-
-   else
-   {
-      int iin = 0;
-
-      buffer[iout++] = ':';
-      buffer[iout++] = ' ';
-
-      while (iin < PNG_MAX_ERROR_TEXT-1 && error_message[iin] != '\0')
-         buffer[iout++] = error_message[iin++];
-
-      /* iin < PNG_MAX_ERROR_TEXT, so the following is safe: */
-      buffer[iout] = '\0';
-   }
-}
-#endif /* WARNINGS || ERROR_TEXT */
-
-#if defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)
-PNG_FUNCTION(void,PNGAPI
-png_chunk_error,(png_const_structrp png_ptr, png_const_charp error_message),
-    PNG_NORETURN)
-{
-   char msg[18+PNG_MAX_ERROR_TEXT];
-   if (png_ptr == NULL)
-      png_error(png_ptr, error_message);
-
-   else
-   {
-      png_format_buffer(png_ptr, msg, error_message);
-      png_error(png_ptr, msg);
-   }
-}
-#endif /* READ && ERROR_TEXT */
-
-#ifdef PNG_WARNINGS_SUPPORTED
-void PNGAPI
-png_chunk_warning(png_const_structrp png_ptr, png_const_charp warning_message)
-{
-   char msg[18+PNG_MAX_ERROR_TEXT];
-   if (png_ptr == NULL)
-      png_warning(png_ptr, warning_message);
-
-   else
-   {
-      png_format_buffer(png_ptr, msg, warning_message);
-      png_warning(png_ptr, msg);
-   }
-}
-#endif /* WARNINGS */
-
-#ifdef PNG_READ_SUPPORTED
-#ifdef PNG_BENIGN_ERRORS_SUPPORTED
-void PNGAPI
-png_chunk_benign_error(png_const_structrp png_ptr, png_const_charp
-    error_message)
-{
-   if ((png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) != 0)
-      png_chunk_warning(png_ptr, error_message);
-
-   else
-      png_chunk_error(png_ptr, error_message);
-
-#  ifndef PNG_ERROR_TEXT_SUPPORTED
-      PNG_UNUSED(error_message)
-#  endif
-}
-#endif
-#endif /* READ */
-
-void /* PRIVATE */
-png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error)
-{
-#  ifndef PNG_WARNINGS_SUPPORTED
-      PNG_UNUSED(message)
-#  endif
-
-   /* This is always supported, but for just read or just write it
-    * unconditionally does the right thing.
-    */
-#  if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED)
-      if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0)
-#  endif
-
-#  ifdef PNG_READ_SUPPORTED
-      {
-         if (error < PNG_CHUNK_ERROR)
-            png_chunk_warning(png_ptr, message);
-
-         else
-            png_chunk_benign_error(png_ptr, message);
-      }
-#  endif
-
-#  if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED)
-      else if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0)
-#  endif
-
-#  ifdef PNG_WRITE_SUPPORTED
-      {
-         if (error < PNG_CHUNK_WRITE_ERROR)
-            png_app_warning(png_ptr, message);
-
-         else
-            png_app_error(png_ptr, message);
-      }
-#  endif
-}
-
-#ifdef PNG_ERROR_TEXT_SUPPORTED
-#ifdef PNG_FLOATING_POINT_SUPPORTED
-PNG_FUNCTION(void,
-png_fixed_error,(png_const_structrp png_ptr, png_const_charp name),PNG_NORETURN)
-{
-#  define fixed_message "fixed point overflow in "
-#  define fixed_message_ln ((sizeof fixed_message)-1)
-   unsigned int  iin;
-   char msg[fixed_message_ln+PNG_MAX_ERROR_TEXT];
-   memcpy(msg, fixed_message, fixed_message_ln);
-   iin = 0;
-   if (name != NULL)
-      while (iin < (PNG_MAX_ERROR_TEXT-1) && name[iin] != 0)
-      {
-         msg[fixed_message_ln + iin] = name[iin];
-         ++iin;
-      }
-   msg[fixed_message_ln + iin] = 0;
-   png_error(png_ptr, msg);
-}
-#endif
-#endif
-
-#ifdef PNG_SETJMP_SUPPORTED
-/* This API only exists if ANSI-C style error handling is used,
- * otherwise it is necessary for png_default_error to be overridden.
- */
-jmp_buf* PNGAPI
-png_set_longjmp_fn(png_structrp png_ptr, png_longjmp_ptr longjmp_fn,
-    size_t jmp_buf_size)
-{
-   /* From libpng 1.6.0 the app gets one chance to set a 'jmpbuf_size' value
-    * and it must not change after that.  Libpng doesn't care how big the
-    * buffer is, just that it doesn't change.
-    *
-    * If the buffer size is no *larger* than the size of jmp_buf when libpng is
-    * compiled a built in jmp_buf is returned; this preserves the pre-1.6.0
-    * semantics that this call will not fail.  If the size is larger, however,
-    * the buffer is allocated and this may fail, causing the function to return
-    * NULL.
-    */
-   if (png_ptr == NULL)
-      return NULL;
-
-   if (png_ptr->jmp_buf_ptr == NULL)
-   {
-      png_ptr->jmp_buf_size = 0; /* not allocated */
-
-      if (jmp_buf_size <= (sizeof png_ptr->jmp_buf_local))
-         png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local;
-
-      else
-      {
-         png_ptr->jmp_buf_ptr = png_voidcast(jmp_buf *,
-             png_malloc_warn(png_ptr, jmp_buf_size));
-
-         if (png_ptr->jmp_buf_ptr == NULL)
-            return NULL; /* new NULL return on OOM */
-
-         png_ptr->jmp_buf_size = jmp_buf_size;
-      }
-   }
-
-   else /* Already allocated: check the size */
-   {
-      size_t size = png_ptr->jmp_buf_size;
-
-      if (size == 0)
-      {
-         size = (sizeof png_ptr->jmp_buf_local);
-         if (png_ptr->jmp_buf_ptr != &png_ptr->jmp_buf_local)
-         {
-            /* This is an internal error in libpng: somehow we have been left
-             * with a stack allocated jmp_buf when the application regained
-             * control.  It's always possible to fix this up, but for the moment
-             * this is a png_error because that makes it easy to detect.
-             */
-            png_error(png_ptr, "Libpng jmp_buf still allocated");
-            /* png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local; */
-         }
-      }
-
-      if (size != jmp_buf_size)
-      {
-         png_warning(png_ptr, "Application jmp_buf size changed");
-         return NULL; /* caller will probably crash: no choice here */
-      }
-   }
-
-   /* Finally fill in the function, now we have a satisfactory buffer. It is
-    * valid to change the function on every call.
-    */
-   png_ptr->longjmp_fn = longjmp_fn;
-   return png_ptr->jmp_buf_ptr;
-}
-
-void /* PRIVATE */
-png_free_jmpbuf(png_structrp png_ptr)
-{
-   if (png_ptr != NULL)
-   {
-      jmp_buf *jb = png_ptr->jmp_buf_ptr;
-
-      /* A size of 0 is used to indicate a local, stack, allocation of the
-       * pointer; used here and in png.c
-       */
-      if (jb != NULL && png_ptr->jmp_buf_size > 0)
-      {
-
-         /* This stuff is so that a failure to free the error control structure
-          * does not leave libpng in a state with no valid error handling: the
-          * free always succeeds, if there is an error it gets ignored.
-          */
-         if (jb != &png_ptr->jmp_buf_local)
-         {
-            /* Make an internal, libpng, jmp_buf to return here */
-            jmp_buf free_jmp_buf;
-
-            if (!setjmp(free_jmp_buf))
-            {
-               png_ptr->jmp_buf_ptr = &free_jmp_buf; /* come back here */
-               png_ptr->jmp_buf_size = 0; /* stack allocation */
-               png_ptr->longjmp_fn = longjmp;
-               png_free(png_ptr, jb); /* Return to setjmp on error */
-            }
-         }
-      }
-
-      /* *Always* cancel everything out: */
-      png_ptr->jmp_buf_size = 0;
-      png_ptr->jmp_buf_ptr = NULL;
-      png_ptr->longjmp_fn = 0;
-   }
-}
-#endif
-
-/* This is the default error handling function.  Note that replacements for
- * this function MUST NOT RETURN, or the program will likely crash.  This
- * function is used by default, or if the program supplies NULL for the
- * error function pointer in png_set_error_fn().
- */
-static PNG_FUNCTION(void /* PRIVATE */,
-png_default_error,(png_const_structrp png_ptr, png_const_charp error_message),
-    PNG_NORETURN)
-{
-#ifdef PNG_CONSOLE_IO_SUPPORTED
-#ifdef PNG_ERROR_NUMBERS_SUPPORTED
-   /* Check on NULL only added in 1.5.4 */
-   if (error_message != NULL && *error_message == PNG_LITERAL_SHARP)
-   {
-      /* Strip "#nnnn " from beginning of error message. */
-      int offset;
-      char error_number[16];
-      for (offset = 0; offset<15; offset++)
-      {
-         error_number[offset] = error_message[offset + 1];
-         if (error_message[offset] == ' ')
-            break;
-      }
-
-      if ((offset > 1) && (offset < 15))
-      {
-         error_number[offset - 1] = '\0';
-         fprintf(stderr, "libpng error no. %s: %s",
-             error_number, error_message + offset + 1);
-         fprintf(stderr, PNG_STRING_NEWLINE);
-      }
-
-      else
-      {
-         fprintf(stderr, "libpng error: %s, offset=%d",
-             error_message, offset);
-         fprintf(stderr, PNG_STRING_NEWLINE);
-      }
-   }
-   else
-#endif
-   {
-      fprintf(stderr, "libpng error: %s", error_message ? error_message :
-         "undefined");
-      fprintf(stderr, PNG_STRING_NEWLINE);
-   }
-#else
-   PNG_UNUSED(error_message) /* Make compiler happy */
-#endif
-   png_longjmp(png_ptr, 1);
-}
-
-PNG_FUNCTION(void,PNGAPI
-png_longjmp,(png_const_structrp png_ptr, int val),PNG_NORETURN)
-{
-#ifdef PNG_SETJMP_SUPPORTED
-   if (png_ptr != NULL && png_ptr->longjmp_fn != NULL &&
-       png_ptr->jmp_buf_ptr != NULL)
-      png_ptr->longjmp_fn(*png_ptr->jmp_buf_ptr, val);
-#else
-   PNG_UNUSED(png_ptr)
-   PNG_UNUSED(val)
-#endif
-
-   /* If control reaches this point, png_longjmp() must not return. The only
-    * choice is to terminate the whole process (or maybe the thread); to do
-    * this the ANSI-C abort() function is used unless a different method is
-    * implemented by overriding the default configuration setting for
-    * PNG_ABORT().
-    */
-   PNG_ABORT();
-}
-
-#ifdef PNG_WARNINGS_SUPPORTED
-/* This function is called when there is a warning, but the library thinks
- * it can continue anyway.  Replacement functions don't have to do anything
- * here if you don't want them to.  In the default configuration, png_ptr is
- * not used, but it is passed in case it may be useful.
- */
-static void /* PRIVATE */
-png_default_warning(png_const_structrp png_ptr, png_const_charp warning_message)
-{
-#ifdef PNG_CONSOLE_IO_SUPPORTED
-#  ifdef PNG_ERROR_NUMBERS_SUPPORTED
-   if (*warning_message == PNG_LITERAL_SHARP)
-   {
-      int offset;
-      char warning_number[16];
-      for (offset = 0; offset < 15; offset++)
-      {
-         warning_number[offset] = warning_message[offset + 1];
-         if (warning_message[offset] == ' ')
-            break;
-      }
-
-      if ((offset > 1) && (offset < 15))
-      {
-         warning_number[offset + 1] = '\0';
-         fprintf(stderr, "libpng warning no. %s: %s",
-             warning_number, warning_message + offset);
-         fprintf(stderr, PNG_STRING_NEWLINE);
-      }
-
-      else
-      {
-         fprintf(stderr, "libpng warning: %s",
-             warning_message);
-         fprintf(stderr, PNG_STRING_NEWLINE);
-      }
-   }
-   else
-#  endif
-
-   {
-      fprintf(stderr, "libpng warning: %s", warning_message);
-      fprintf(stderr, PNG_STRING_NEWLINE);
-   }
-#else
-   PNG_UNUSED(warning_message) /* Make compiler happy */
-#endif
-   PNG_UNUSED(png_ptr) /* Make compiler happy */
-}
-#endif /* WARNINGS */
-
-/* This function is called when the application wants to use another method
- * of handling errors and warnings.  Note that the error function MUST NOT
- * return to the calling routine or serious problems will occur.  The return
- * method used in the default routine calls longjmp(png_ptr->jmp_buf_ptr, 1)
- */
-void PNGAPI
-png_set_error_fn(png_structrp png_ptr, png_voidp error_ptr,
-    png_error_ptr error_fn, png_error_ptr warning_fn)
-{
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->error_ptr = error_ptr;
-   png_ptr->error_fn = error_fn;
-#ifdef PNG_WARNINGS_SUPPORTED
-   png_ptr->warning_fn = warning_fn;
-#else
-   PNG_UNUSED(warning_fn)
-#endif
-}
-
-
-/* This function returns a pointer to the error_ptr associated with the user
- * functions.  The application should free any memory associated with this
- * pointer before png_write_destroy and png_read_destroy are called.
- */
-png_voidp PNGAPI
-png_get_error_ptr(png_const_structrp png_ptr)
-{
-   if (png_ptr == NULL)
-      return NULL;
-
-   return ((png_voidp)png_ptr->error_ptr);
-}
-
-
-#ifdef PNG_ERROR_NUMBERS_SUPPORTED
-void PNGAPI
-png_set_strip_error_numbers(png_structrp png_ptr, png_uint_32 strip_mode)
-{
-   if (png_ptr != NULL)
-   {
-      png_ptr->flags &=
-         ((~(PNG_FLAG_STRIP_ERROR_NUMBERS |
-         PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode);
-   }
-}
-#endif
-
-#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
-   defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
-   /* Currently the above both depend on SETJMP_SUPPORTED, however it would be
-    * possible to implement without setjmp support just so long as there is some
-    * way to handle the error return here:
-    */
-PNG_FUNCTION(void /* PRIVATE */, (PNGCBAPI
-png_safe_error),(png_structp png_nonconst_ptr, png_const_charp error_message),
-    PNG_NORETURN)
-{
-   png_const_structrp png_ptr = png_nonconst_ptr;
-   png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr);
-
-   /* An error is always logged here, overwriting anything (typically a warning)
-    * that is already there:
-    */
-   if (image != NULL)
-   {
-      png_safecat(image->message, (sizeof image->message), 0, error_message);
-      image->warning_or_error |= PNG_IMAGE_ERROR;
-
-      /* Retrieve the jmp_buf from within the png_control, making this work for
-       * C++ compilation too is pretty tricky: C++ wants a pointer to the first
-       * element of a jmp_buf, but C doesn't tell us the type of that.
-       */
-      if (image->opaque != NULL && image->opaque->error_buf != NULL)
-         longjmp(png_control_jmp_buf(image->opaque), 1);
-
-      /* Missing longjmp buffer, the following is to help debugging: */
-      {
-         size_t pos = png_safecat(image->message, (sizeof image->message), 0,
-             "bad longjmp: ");
-         png_safecat(image->message, (sizeof image->message), pos,
-             error_message);
-      }
-   }
-
-   /* Here on an internal programming error. */
-   abort();
-}
-
-#ifdef PNG_WARNINGS_SUPPORTED
-void /* PRIVATE */ PNGCBAPI
-png_safe_warning(png_structp png_nonconst_ptr, png_const_charp warning_message)
-{
-   png_const_structrp png_ptr = png_nonconst_ptr;
-   png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr);
-
-   /* A warning is only logged if there is no prior warning or error. */
-   if (image->warning_or_error == 0)
-   {
-      png_safecat(image->message, (sizeof image->message), 0, warning_message);
-      image->warning_or_error |= PNG_IMAGE_WARNING;
-   }
-}
-#endif
-
-int /* PRIVATE */
-png_safe_execute(png_imagep image_in, int (*function)(png_voidp), png_voidp arg)
-{
-   volatile png_imagep image = image_in;
-   volatile int result;
-   volatile png_voidp saved_error_buf;
-   jmp_buf safe_jmpbuf;
-
-   /* Safely execute function(arg) with png_error returning to this function. */
-   saved_error_buf = image->opaque->error_buf;
-   result = setjmp(safe_jmpbuf) == 0;
-
-   if (result != 0)
-   {
-
-      image->opaque->error_buf = safe_jmpbuf;
-      result = function(arg);
-   }
-
-   image->opaque->error_buf = saved_error_buf;
-
-   /* And do the cleanup prior to any failure return. */
-   if (result == 0)
-      png_image_free(image);
-
-   return result;
-}
-#endif /* SIMPLIFIED READ || SIMPLIFIED_WRITE */
-#endif /* READ || WRITE */
diff --git a/third_party/libpng16/pngget.c b/third_party/libpng16/pngget.c
deleted file mode 100644
index 5abf1ef..0000000
--- a/third_party/libpng16/pngget.c
+++ /dev/null
@@ -1,1249 +0,0 @@
-
-/* pngget.c - retrieval of values from info struct
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- *
- */
-
-#include "pngpriv.h"
-
-#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
-
-png_uint_32 PNGAPI
-png_get_valid(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    png_uint_32 flag)
-{
-   if (png_ptr != NULL && info_ptr != NULL)
-      return(info_ptr->valid & flag);
-
-   return(0);
-}
-
-size_t PNGAPI
-png_get_rowbytes(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-   if (png_ptr != NULL && info_ptr != NULL)
-      return(info_ptr->rowbytes);
-
-   return(0);
-}
-
-#ifdef PNG_INFO_IMAGE_SUPPORTED
-png_bytepp PNGAPI
-png_get_rows(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-   if (png_ptr != NULL && info_ptr != NULL)
-      return(info_ptr->row_pointers);
-
-   return(0);
-}
-#endif
-
-#ifdef PNG_EASY_ACCESS_SUPPORTED
-/* Easy access to info, added in libpng-0.99 */
-png_uint_32 PNGAPI
-png_get_image_width(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-   if (png_ptr != NULL && info_ptr != NULL)
-      return info_ptr->width;
-
-   return (0);
-}
-
-png_uint_32 PNGAPI
-png_get_image_height(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-   if (png_ptr != NULL && info_ptr != NULL)
-      return info_ptr->height;
-
-   return (0);
-}
-
-png_byte PNGAPI
-png_get_bit_depth(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-   if (png_ptr != NULL && info_ptr != NULL)
-      return info_ptr->bit_depth;
-
-   return (0);
-}
-
-png_byte PNGAPI
-png_get_color_type(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-   if (png_ptr != NULL && info_ptr != NULL)
-      return info_ptr->color_type;
-
-   return (0);
-}
-
-png_byte PNGAPI
-png_get_filter_type(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-   if (png_ptr != NULL && info_ptr != NULL)
-      return info_ptr->filter_type;
-
-   return (0);
-}
-
-png_byte PNGAPI
-png_get_interlace_type(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-   if (png_ptr != NULL && info_ptr != NULL)
-      return info_ptr->interlace_type;
-
-   return (0);
-}
-
-png_byte PNGAPI
-png_get_compression_type(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-   if (png_ptr != NULL && info_ptr != NULL)
-      return info_ptr->compression_type;
-
-   return (0);
-}
-
-png_uint_32 PNGAPI
-png_get_x_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp
-   info_ptr)
-{
-#ifdef PNG_pHYs_SUPPORTED
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_pHYs) != 0)
-      {
-         png_debug1(1, "in %s retrieval function",
-             "png_get_x_pixels_per_meter");
-
-         if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER)
-            return (info_ptr->x_pixels_per_unit);
-      }
-#else
-   PNG_UNUSED(png_ptr)
-   PNG_UNUSED(info_ptr)
-#endif
-
-   return (0);
-}
-
-png_uint_32 PNGAPI
-png_get_y_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp
-    info_ptr)
-{
-#ifdef PNG_pHYs_SUPPORTED
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_pHYs) != 0)
-   {
-      png_debug1(1, "in %s retrieval function",
-          "png_get_y_pixels_per_meter");
-
-      if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER)
-         return (info_ptr->y_pixels_per_unit);
-   }
-#else
-   PNG_UNUSED(png_ptr)
-   PNG_UNUSED(info_ptr)
-#endif
-
-   return (0);
-}
-
-png_uint_32 PNGAPI
-png_get_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-#ifdef PNG_pHYs_SUPPORTED
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_pHYs) != 0)
-   {
-      png_debug1(1, "in %s retrieval function", "png_get_pixels_per_meter");
-
-      if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER &&
-          info_ptr->x_pixels_per_unit == info_ptr->y_pixels_per_unit)
-         return (info_ptr->x_pixels_per_unit);
-   }
-#else
-   PNG_UNUSED(png_ptr)
-   PNG_UNUSED(info_ptr)
-#endif
-
-   return (0);
-}
-
-#ifdef PNG_FLOATING_POINT_SUPPORTED
-float PNGAPI
-png_get_pixel_aspect_ratio(png_const_structrp png_ptr, png_const_inforp
-   info_ptr)
-{
-#ifdef PNG_READ_pHYs_SUPPORTED
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_pHYs) != 0)
-   {
-      png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio");
-
-      if (info_ptr->x_pixels_per_unit != 0)
-         return ((float)((float)info_ptr->y_pixels_per_unit
-             /(float)info_ptr->x_pixels_per_unit));
-   }
-#else
-   PNG_UNUSED(png_ptr)
-   PNG_UNUSED(info_ptr)
-#endif
-
-   return ((float)0.0);
-}
-#endif
-
-#ifdef PNG_FIXED_POINT_SUPPORTED
-png_fixed_point PNGAPI
-png_get_pixel_aspect_ratio_fixed(png_const_structrp png_ptr,
-    png_const_inforp info_ptr)
-{
-#ifdef PNG_READ_pHYs_SUPPORTED
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_pHYs) != 0 &&
-       info_ptr->x_pixels_per_unit > 0 && info_ptr->y_pixels_per_unit > 0 &&
-       info_ptr->x_pixels_per_unit <= PNG_UINT_31_MAX &&
-       info_ptr->y_pixels_per_unit <= PNG_UINT_31_MAX)
-   {
-      png_fixed_point res;
-
-      png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio_fixed");
-
-      /* The following casts work because a PNG 4 byte integer only has a valid
-       * range of 0..2^31-1; otherwise the cast might overflow.
-       */
-      if (png_muldiv(&res, (png_int_32)info_ptr->y_pixels_per_unit, PNG_FP_1,
-          (png_int_32)info_ptr->x_pixels_per_unit) != 0)
-         return res;
-   }
-#else
-   PNG_UNUSED(png_ptr)
-   PNG_UNUSED(info_ptr)
-#endif
-
-   return 0;
-}
-#endif
-
-png_int_32 PNGAPI
-png_get_x_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-#ifdef PNG_oFFs_SUPPORTED
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_oFFs) != 0)
-   {
-      png_debug1(1, "in %s retrieval function", "png_get_x_offset_microns");
-
-      if (info_ptr->offset_unit_type == PNG_OFFSET_MICROMETER)
-         return (info_ptr->x_offset);
-   }
-#else
-   PNG_UNUSED(png_ptr)
-   PNG_UNUSED(info_ptr)
-#endif
-
-   return (0);
-}
-
-png_int_32 PNGAPI
-png_get_y_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-#ifdef PNG_oFFs_SUPPORTED
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_oFFs) != 0)
-   {
-      png_debug1(1, "in %s retrieval function", "png_get_y_offset_microns");
-
-      if (info_ptr->offset_unit_type == PNG_OFFSET_MICROMETER)
-         return (info_ptr->y_offset);
-   }
-#else
-   PNG_UNUSED(png_ptr)
-   PNG_UNUSED(info_ptr)
-#endif
-
-   return (0);
-}
-
-png_int_32 PNGAPI
-png_get_x_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-#ifdef PNG_oFFs_SUPPORTED
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_oFFs) != 0)
-   {
-      png_debug1(1, "in %s retrieval function", "png_get_x_offset_pixels");
-
-      if (info_ptr->offset_unit_type == PNG_OFFSET_PIXEL)
-         return (info_ptr->x_offset);
-   }
-#else
-   PNG_UNUSED(png_ptr)
-   PNG_UNUSED(info_ptr)
-#endif
-
-   return (0);
-}
-
-png_int_32 PNGAPI
-png_get_y_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-#ifdef PNG_oFFs_SUPPORTED
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_oFFs) != 0)
-   {
-      png_debug1(1, "in %s retrieval function", "png_get_y_offset_pixels");
-
-      if (info_ptr->offset_unit_type == PNG_OFFSET_PIXEL)
-         return (info_ptr->y_offset);
-   }
-#else
-   PNG_UNUSED(png_ptr)
-   PNG_UNUSED(info_ptr)
-#endif
-
-   return (0);
-}
-
-#ifdef PNG_INCH_CONVERSIONS_SUPPORTED
-static png_uint_32
-ppi_from_ppm(png_uint_32 ppm)
-{
-#if 0
-   /* The conversion is *(2.54/100), in binary (32 digits):
-    * .00000110100000001001110101001001
-    */
-   png_uint_32 t1001, t1101;
-   ppm >>= 1;                  /* .1 */
-   t1001 = ppm + (ppm >> 3);   /* .1001 */
-   t1101 = t1001 + (ppm >> 1); /* .1101 */
-   ppm >>= 20;                 /* .000000000000000000001 */
-   t1101 += t1101 >> 15;       /* .1101000000000001101 */
-   t1001 >>= 11;               /* .000000000001001 */
-   t1001 += t1001 >> 12;       /* .000000000001001000000001001 */
-   ppm += t1001;               /* .000000000001001000001001001 */
-   ppm += t1101;               /* .110100000001001110101001001 */
-   return (ppm + 16) >> 5;/* .00000110100000001001110101001001 */
-#else
-   /* The argument is a PNG unsigned integer, so it is not permitted
-    * to be bigger than 2^31.
-    */
-   png_fixed_point result;
-   if (ppm <= PNG_UINT_31_MAX && png_muldiv(&result, (png_int_32)ppm, 127,
-       5000) != 0)
-      return (png_uint_32)result;
-
-   /* Overflow. */
-   return 0;
-#endif
-}
-
-png_uint_32 PNGAPI
-png_get_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-   return ppi_from_ppm(png_get_pixels_per_meter(png_ptr, info_ptr));
-}
-
-png_uint_32 PNGAPI
-png_get_x_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-   return ppi_from_ppm(png_get_x_pixels_per_meter(png_ptr, info_ptr));
-}
-
-png_uint_32 PNGAPI
-png_get_y_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-   return ppi_from_ppm(png_get_y_pixels_per_meter(png_ptr, info_ptr));
-}
-
-#ifdef PNG_FIXED_POINT_SUPPORTED
-static png_fixed_point
-png_fixed_inches_from_microns(png_const_structrp png_ptr, png_int_32 microns)
-{
-   /* Convert from meters * 1,000,000 to inches * 100,000, meters to
-    * inches is simply *(100/2.54), so we want *(10/2.54) == 500/127.
-    * Notice that this can overflow - a warning is output and 0 is
-    * returned.
-    */
-   return png_muldiv_warn(png_ptr, microns, 500, 127);
-}
-
-png_fixed_point PNGAPI
-png_get_x_offset_inches_fixed(png_const_structrp png_ptr,
-    png_const_inforp info_ptr)
-{
-   return png_fixed_inches_from_microns(png_ptr,
-       png_get_x_offset_microns(png_ptr, info_ptr));
-}
-#endif
-
-#ifdef PNG_FIXED_POINT_SUPPORTED
-png_fixed_point PNGAPI
-png_get_y_offset_inches_fixed(png_const_structrp png_ptr,
-    png_const_inforp info_ptr)
-{
-   return png_fixed_inches_from_microns(png_ptr,
-       png_get_y_offset_microns(png_ptr, info_ptr));
-}
-#endif
-
-#ifdef PNG_FLOATING_POINT_SUPPORTED
-float PNGAPI
-png_get_x_offset_inches(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-   /* To avoid the overflow do the conversion directly in floating
-    * point.
-    */
-   return (float)(png_get_x_offset_microns(png_ptr, info_ptr) * .00003937);
-}
-#endif
-
-#ifdef PNG_FLOATING_POINT_SUPPORTED
-float PNGAPI
-png_get_y_offset_inches(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-   /* To avoid the overflow do the conversion directly in floating
-    * point.
-    */
-   return (float)(png_get_y_offset_microns(png_ptr, info_ptr) * .00003937);
-}
-#endif
-
-#ifdef PNG_pHYs_SUPPORTED
-png_uint_32 PNGAPI
-png_get_pHYs_dpi(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)
-{
-   png_uint_32 retval = 0;
-
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_pHYs) != 0)
-   {
-      png_debug1(1, "in %s retrieval function", "pHYs");
-
-      if (res_x != NULL)
-      {
-         *res_x = info_ptr->x_pixels_per_unit;
-         retval |= PNG_INFO_pHYs;
-      }
-
-      if (res_y != NULL)
-      {
-         *res_y = info_ptr->y_pixels_per_unit;
-         retval |= PNG_INFO_pHYs;
-      }
-
-      if (unit_type != NULL)
-      {
-         *unit_type = (int)info_ptr->phys_unit_type;
-         retval |= PNG_INFO_pHYs;
-
-         if (*unit_type == 1)
-         {
-            if (res_x != NULL) *res_x = (png_uint_32)(*res_x * .0254 + .50);
-            if (res_y != NULL) *res_y = (png_uint_32)(*res_y * .0254 + .50);
-         }
-      }
-   }
-
-   return (retval);
-}
-#endif /* pHYs */
-#endif /* INCH_CONVERSIONS */
-
-/* png_get_channels really belongs in here, too, but it's been around longer */
-
-#endif /* EASY_ACCESS */
-
-
-png_byte PNGAPI
-png_get_channels(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-   if (png_ptr != NULL && info_ptr != NULL)
-      return(info_ptr->channels);
-
-   return (0);
-}
-
-#ifdef PNG_READ_SUPPORTED
-png_const_bytep PNGAPI
-png_get_signature(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
-   if (png_ptr != NULL && info_ptr != NULL)
-      return(info_ptr->signature);
-
-   return (NULL);
-}
-#endif
-
-#ifdef PNG_bKGD_SUPPORTED
-png_uint_32 PNGAPI
-png_get_bKGD(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_color_16p *background)
-{
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_bKGD) != 0 &&
-       background != NULL)
-   {
-      png_debug1(1, "in %s retrieval function", "bKGD");
-
-      *background = &(info_ptr->background);
-      return (PNG_INFO_bKGD);
-   }
-
-   return (0);
-}
-#endif
-
-#ifdef PNG_cHRM_SUPPORTED
-/* The XYZ APIs were added in 1.5.5 to take advantage of the code added at the
- * same time to correct the rgb grayscale coefficient defaults obtained from the
- * cHRM chunk in 1.5.4
- */
-#  ifdef PNG_FLOATING_POINT_SUPPORTED
-png_uint_32 PNGAPI
-png_get_cHRM(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    double *white_x, double *white_y, double *red_x, double *red_y,
-    double *green_x, double *green_y, double *blue_x, double *blue_y)
-{
-   /* Quiet API change: this code used to only return the end points if a cHRM
-    * chunk was present, but the end points can also come from iCCP or sRGB
-    * chunks, so in 1.6.0 the png_get_ APIs return the end points regardless and
-    * the png_set_ APIs merely check that set end points are mutually
-    * consistent.
-    */
-   if (png_ptr != NULL && info_ptr != NULL &&
-      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
-   {
-      png_debug1(1, "in %s retrieval function", "cHRM");
-
-      if (white_x != NULL)
-         *white_x = png_float(png_ptr,
-             info_ptr->colorspace.end_points_xy.whitex, "cHRM white X");
-      if (white_y != NULL)
-         *white_y = png_float(png_ptr,
-             info_ptr->colorspace.end_points_xy.whitey, "cHRM white Y");
-      if (red_x != NULL)
-         *red_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redx,
-             "cHRM red X");
-      if (red_y != NULL)
-         *red_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redy,
-             "cHRM red Y");
-      if (green_x != NULL)
-         *green_x = png_float(png_ptr,
-             info_ptr->colorspace.end_points_xy.greenx, "cHRM green X");
-      if (green_y != NULL)
-         *green_y = png_float(png_ptr,
-             info_ptr->colorspace.end_points_xy.greeny, "cHRM green Y");
-      if (blue_x != NULL)
-         *blue_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluex,
-             "cHRM blue X");
-      if (blue_y != NULL)
-         *blue_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluey,
-             "cHRM blue Y");
-      return (PNG_INFO_cHRM);
-   }
-
-   return (0);
-}
-
-png_uint_32 PNGAPI
-png_get_cHRM_XYZ(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    double *red_X, double *red_Y, double *red_Z, double *green_X,
-    double *green_Y, double *green_Z, double *blue_X, double *blue_Y,
-    double *blue_Z)
-{
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
-   {
-      png_debug1(1, "in %s retrieval function", "cHRM_XYZ(float)");
-
-      if (red_X != NULL)
-         *red_X = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_X,
-             "cHRM red X");
-      if (red_Y != NULL)
-         *red_Y = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Y,
-             "cHRM red Y");
-      if (red_Z != NULL)
-         *red_Z = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Z,
-             "cHRM red Z");
-      if (green_X != NULL)
-         *green_X = png_float(png_ptr,
-             info_ptr->colorspace.end_points_XYZ.green_X, "cHRM green X");
-      if (green_Y != NULL)
-         *green_Y = png_float(png_ptr,
-             info_ptr->colorspace.end_points_XYZ.green_Y, "cHRM green Y");
-      if (green_Z != NULL)
-         *green_Z = png_float(png_ptr,
-             info_ptr->colorspace.end_points_XYZ.green_Z, "cHRM green Z");
-      if (blue_X != NULL)
-         *blue_X = png_float(png_ptr,
-             info_ptr->colorspace.end_points_XYZ.blue_X, "cHRM blue X");
-      if (blue_Y != NULL)
-         *blue_Y = png_float(png_ptr,
-             info_ptr->colorspace.end_points_XYZ.blue_Y, "cHRM blue Y");
-      if (blue_Z != NULL)
-         *blue_Z = png_float(png_ptr,
-             info_ptr->colorspace.end_points_XYZ.blue_Z, "cHRM blue Z");
-      return (PNG_INFO_cHRM);
-   }
-
-   return (0);
-}
-#  endif
-
-#  ifdef PNG_FIXED_POINT_SUPPORTED
-png_uint_32 PNGAPI
-png_get_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    png_fixed_point *int_red_X, png_fixed_point *int_red_Y,
-    png_fixed_point *int_red_Z, png_fixed_point *int_green_X,
-    png_fixed_point *int_green_Y, png_fixed_point *int_green_Z,
-    png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y,
-    png_fixed_point *int_blue_Z)
-{
-   if (png_ptr != NULL && info_ptr != NULL &&
-      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
-   {
-      png_debug1(1, "in %s retrieval function", "cHRM_XYZ");
-
-      if (int_red_X != NULL)
-         *int_red_X = info_ptr->colorspace.end_points_XYZ.red_X;
-      if (int_red_Y != NULL)
-         *int_red_Y = info_ptr->colorspace.end_points_XYZ.red_Y;
-      if (int_red_Z != NULL)
-         *int_red_Z = info_ptr->colorspace.end_points_XYZ.red_Z;
-      if (int_green_X != NULL)
-         *int_green_X = info_ptr->colorspace.end_points_XYZ.green_X;
-      if (int_green_Y != NULL)
-         *int_green_Y = info_ptr->colorspace.end_points_XYZ.green_Y;
-      if (int_green_Z != NULL)
-         *int_green_Z = info_ptr->colorspace.end_points_XYZ.green_Z;
-      if (int_blue_X != NULL)
-         *int_blue_X = info_ptr->colorspace.end_points_XYZ.blue_X;
-      if (int_blue_Y != NULL)
-         *int_blue_Y = info_ptr->colorspace.end_points_XYZ.blue_Y;
-      if (int_blue_Z != NULL)
-         *int_blue_Z = info_ptr->colorspace.end_points_XYZ.blue_Z;
-      return (PNG_INFO_cHRM);
-   }
-
-   return (0);
-}
-
-png_uint_32 PNGAPI
-png_get_cHRM_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x,
-    png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y,
-    png_fixed_point *blue_x, png_fixed_point *blue_y)
-{
-   png_debug1(1, "in %s retrieval function", "cHRM");
-
-   if (png_ptr != NULL && info_ptr != NULL &&
-      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
-   {
-      if (white_x != NULL)
-         *white_x = info_ptr->colorspace.end_points_xy.whitex;
-      if (white_y != NULL)
-         *white_y = info_ptr->colorspace.end_points_xy.whitey;
-      if (red_x != NULL)
-         *red_x = info_ptr->colorspace.end_points_xy.redx;
-      if (red_y != NULL)
-         *red_y = info_ptr->colorspace.end_points_xy.redy;
-      if (green_x != NULL)
-         *green_x = info_ptr->colorspace.end_points_xy.greenx;
-      if (green_y != NULL)
-         *green_y = info_ptr->colorspace.end_points_xy.greeny;
-      if (blue_x != NULL)
-         *blue_x = info_ptr->colorspace.end_points_xy.bluex;
-      if (blue_y != NULL)
-         *blue_y = info_ptr->colorspace.end_points_xy.bluey;
-      return (PNG_INFO_cHRM);
-   }
-
-   return (0);
-}
-#  endif
-#endif
-
-#ifdef PNG_gAMA_SUPPORTED
-#  ifdef PNG_FIXED_POINT_SUPPORTED
-png_uint_32 PNGAPI
-png_get_gAMA_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    png_fixed_point *file_gamma)
-{
-   png_debug1(1, "in %s retrieval function", "gAMA");
-
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 &&
-       file_gamma != NULL)
-   {
-      *file_gamma = info_ptr->colorspace.gamma;
-      return (PNG_INFO_gAMA);
-   }
-
-   return (0);
-}
-#  endif
-
-#  ifdef PNG_FLOATING_POINT_SUPPORTED
-png_uint_32 PNGAPI
-png_get_gAMA(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    double *file_gamma)
-{
-   png_debug1(1, "in %s retrieval function", "gAMA(float)");
-
-   if (png_ptr != NULL && info_ptr != NULL &&
-      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 &&
-      file_gamma != NULL)
-   {
-      *file_gamma = png_float(png_ptr, info_ptr->colorspace.gamma,
-          "png_get_gAMA");
-      return (PNG_INFO_gAMA);
-   }
-
-   return (0);
-}
-#  endif
-#endif
-
-#ifdef PNG_sRGB_SUPPORTED
-png_uint_32 PNGAPI
-png_get_sRGB(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    int *file_srgb_intent)
-{
-   png_debug1(1, "in %s retrieval function", "sRGB");
-
-   if (png_ptr != NULL && info_ptr != NULL &&
-      (info_ptr->valid & PNG_INFO_sRGB) != 0 && file_srgb_intent != NULL)
-   {
-      *file_srgb_intent = info_ptr->colorspace.rendering_intent;
-      return (PNG_INFO_sRGB);
-   }
-
-   return (0);
-}
-#endif
-
-#ifdef PNG_iCCP_SUPPORTED
-png_uint_32 PNGAPI
-png_get_iCCP(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_charpp name, int *compression_type,
-    png_bytepp profile, png_uint_32 *proflen)
-{
-   png_debug1(1, "in %s retrieval function", "iCCP");
-
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_iCCP) != 0 &&
-       name != NULL && profile != NULL && proflen != NULL)
-   {
-      *name = info_ptr->iccp_name;
-      *profile = info_ptr->iccp_profile;
-      *proflen = png_get_uint_32(info_ptr->iccp_profile);
-      /* This is somewhat irrelevant since the profile data returned has
-       * actually been uncompressed.
-       */
-      if (compression_type != NULL)
-         *compression_type = PNG_COMPRESSION_TYPE_BASE;
-      return (PNG_INFO_iCCP);
-   }
-
-   return (0);
-
-}
-#endif
-
-#ifdef PNG_sPLT_SUPPORTED
-int PNGAPI
-png_get_sPLT(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_sPLT_tpp spalettes)
-{
-   if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL)
-   {
-      *spalettes = info_ptr->splt_palettes;
-      return info_ptr->splt_palettes_num;
-   }
-
-   return (0);
-}
-#endif
-
-#ifdef PNG_eXIf_SUPPORTED
-png_uint_32 PNGAPI
-png_get_eXIf(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_bytep *exif)
-{
-  png_warning(png_ptr, "png_get_eXIf does not work; use png_get_eXIf_1");
-  PNG_UNUSED(info_ptr)
-  PNG_UNUSED(exif)
-  return 0;
-}
-
-png_uint_32 PNGAPI
-png_get_eXIf_1(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    png_uint_32 *num_exif, png_bytep *exif)
-{
-   png_debug1(1, "in %s retrieval function", "eXIf");
-
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_eXIf) != 0 && exif != NULL)
-   {
-      *num_exif = info_ptr->num_exif;
-      *exif = info_ptr->exif;
-      return (PNG_INFO_eXIf);
-   }
-
-   return (0);
-}
-#endif
-
-#ifdef PNG_hIST_SUPPORTED
-png_uint_32 PNGAPI
-png_get_hIST(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_uint_16p *hist)
-{
-   png_debug1(1, "in %s retrieval function", "hIST");
-
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_hIST) != 0 && hist != NULL)
-   {
-      *hist = info_ptr->hist;
-      return (PNG_INFO_hIST);
-   }
-
-   return (0);
-}
-#endif
-
-png_uint_32 PNGAPI
-png_get_IHDR(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    png_uint_32 *width, png_uint_32 *height, int *bit_depth,
-    int *color_type, int *interlace_type, int *compression_type,
-    int *filter_type)
-{
-   png_debug1(1, "in %s retrieval function", "IHDR");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return (0);
-
-   if (width != NULL)
-       *width = info_ptr->width;
-
-   if (height != NULL)
-       *height = info_ptr->height;
-
-   if (bit_depth != NULL)
-       *bit_depth = info_ptr->bit_depth;
-
-   if (color_type != NULL)
-       *color_type = info_ptr->color_type;
-
-   if (compression_type != NULL)
-      *compression_type = info_ptr->compression_type;
-
-   if (filter_type != NULL)
-      *filter_type = info_ptr->filter_type;
-
-   if (interlace_type != NULL)
-      *interlace_type = info_ptr->interlace_type;
-
-   /* This is redundant if we can be sure that the info_ptr values were all
-    * assigned in png_set_IHDR().  We do the check anyhow in case an
-    * application has ignored our advice not to mess with the members
-    * of info_ptr directly.
-    */
-   png_check_IHDR(png_ptr, info_ptr->width, info_ptr->height,
-       info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type,
-       info_ptr->compression_type, info_ptr->filter_type);
-
-   return (1);
-}
-
-#ifdef PNG_oFFs_SUPPORTED
-png_uint_32 PNGAPI
-png_get_oFFs(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type)
-{
-   png_debug1(1, "in %s retrieval function", "oFFs");
-
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_oFFs) != 0 &&
-       offset_x != NULL && offset_y != NULL && unit_type != NULL)
-   {
-      *offset_x = info_ptr->x_offset;
-      *offset_y = info_ptr->y_offset;
-      *unit_type = (int)info_ptr->offset_unit_type;
-      return (PNG_INFO_oFFs);
-   }
-
-   return (0);
-}
-#endif
-
-#ifdef PNG_pCAL_SUPPORTED
-png_uint_32 PNGAPI
-png_get_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams,
-    png_charp *units, png_charpp *params)
-{
-   png_debug1(1, "in %s retrieval function", "pCAL");
-
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_pCAL) != 0 &&
-       purpose != NULL && X0 != NULL && X1 != NULL && type != NULL &&
-       nparams != NULL && units != NULL && params != NULL)
-   {
-      *purpose = info_ptr->pcal_purpose;
-      *X0 = info_ptr->pcal_X0;
-      *X1 = info_ptr->pcal_X1;
-      *type = (int)info_ptr->pcal_type;
-      *nparams = (int)info_ptr->pcal_nparams;
-      *units = info_ptr->pcal_units;
-      *params = info_ptr->pcal_params;
-      return (PNG_INFO_pCAL);
-   }
-
-   return (0);
-}
-#endif
-
-#ifdef PNG_sCAL_SUPPORTED
-#  ifdef PNG_FIXED_POINT_SUPPORTED
-#    if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \
-         defined(PNG_FLOATING_POINT_SUPPORTED)
-png_uint_32 PNGAPI
-png_get_sCAL_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    int *unit, png_fixed_point *width, png_fixed_point *height)
-{
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_sCAL) != 0)
-   {
-      *unit = info_ptr->scal_unit;
-      /*TODO: make this work without FP support; the API is currently eliminated
-       * if neither floating point APIs nor internal floating point arithmetic
-       * are enabled.
-       */
-      *width = png_fixed(png_ptr, atof(info_ptr->scal_s_width), "sCAL width");
-      *height = png_fixed(png_ptr, atof(info_ptr->scal_s_height),
-          "sCAL height");
-      return (PNG_INFO_sCAL);
-   }
-
-   return(0);
-}
-#    endif /* FLOATING_ARITHMETIC */
-#  endif /* FIXED_POINT */
-#  ifdef PNG_FLOATING_POINT_SUPPORTED
-png_uint_32 PNGAPI
-png_get_sCAL(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    int *unit, double *width, double *height)
-{
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_sCAL) != 0)
-   {
-      *unit = info_ptr->scal_unit;
-      *width = atof(info_ptr->scal_s_width);
-      *height = atof(info_ptr->scal_s_height);
-      return (PNG_INFO_sCAL);
-   }
-
-   return(0);
-}
-#  endif /* FLOATING POINT */
-png_uint_32 PNGAPI
-png_get_sCAL_s(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    int *unit, png_charpp width, png_charpp height)
-{
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_sCAL) != 0)
-   {
-      *unit = info_ptr->scal_unit;
-      *width = info_ptr->scal_s_width;
-      *height = info_ptr->scal_s_height;
-      return (PNG_INFO_sCAL);
-   }
-
-   return(0);
-}
-#endif /* sCAL */
-
-#ifdef PNG_pHYs_SUPPORTED
-png_uint_32 PNGAPI
-png_get_pHYs(png_const_structrp png_ptr, png_const_inforp info_ptr,
-    png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)
-{
-   png_uint_32 retval = 0;
-
-   png_debug1(1, "in %s retrieval function", "pHYs");
-
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_pHYs) != 0)
-   {
-      if (res_x != NULL)
-      {
-         *res_x = info_ptr->x_pixels_per_unit;
-         retval |= PNG_INFO_pHYs;
-      }
-
-      if (res_y != NULL)
-      {
-         *res_y = info_ptr->y_pixels_per_unit;
-         retval |= PNG_INFO_pHYs;
-      }
-
-      if (unit_type != NULL)
-      {
-         *unit_type = (int)info_ptr->phys_unit_type;
-         retval |= PNG_INFO_pHYs;
-      }
-   }
-
-   return (retval);
-}
-#endif /* pHYs */
-
-png_uint_32 PNGAPI
-png_get_PLTE(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_colorp *palette, int *num_palette)
-{
-   png_debug1(1, "in %s retrieval function", "PLTE");
-
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_PLTE) != 0 && palette != NULL)
-   {
-      *palette = info_ptr->palette;
-      *num_palette = info_ptr->num_palette;
-      png_debug1(3, "num_palette = %d", *num_palette);
-      return (PNG_INFO_PLTE);
-   }
-
-   return (0);
-}
-
-#ifdef PNG_sBIT_SUPPORTED
-png_uint_32 PNGAPI
-png_get_sBIT(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_color_8p *sig_bit)
-{
-   png_debug1(1, "in %s retrieval function", "sBIT");
-
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_sBIT) != 0 && sig_bit != NULL)
-   {
-      *sig_bit = &(info_ptr->sig_bit);
-      return (PNG_INFO_sBIT);
-   }
-
-   return (0);
-}
-#endif
-
-#ifdef PNG_TEXT_SUPPORTED
-int PNGAPI
-png_get_text(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_textp *text_ptr, int *num_text)
-{
-   if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0)
-   {
-      png_debug1(1, "in 0x%lx retrieval function",
-         (unsigned long)png_ptr->chunk_name);
-
-      if (text_ptr != NULL)
-         *text_ptr = info_ptr->text;
-
-      if (num_text != NULL)
-         *num_text = info_ptr->num_text;
-
-      return info_ptr->num_text;
-   }
-
-   if (num_text != NULL)
-      *num_text = 0;
-
-   return(0);
-}
-#endif
-
-#ifdef PNG_tIME_SUPPORTED
-png_uint_32 PNGAPI
-png_get_tIME(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_timep *mod_time)
-{
-   png_debug1(1, "in %s retrieval function", "tIME");
-
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_tIME) != 0 && mod_time != NULL)
-   {
-      *mod_time = &(info_ptr->mod_time);
-      return (PNG_INFO_tIME);
-   }
-
-   return (0);
-}
-#endif
-
-#ifdef PNG_tRNS_SUPPORTED
-png_uint_32 PNGAPI
-png_get_tRNS(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_bytep *trans_alpha, int *num_trans, png_color_16p *trans_color)
-{
-   png_uint_32 retval = 0;
-   if (png_ptr != NULL && info_ptr != NULL &&
-       (info_ptr->valid & PNG_INFO_tRNS) != 0)
-   {
-      png_debug1(1, "in %s retrieval function", "tRNS");
-
-      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-      {
-         if (trans_alpha != NULL)
-         {
-            *trans_alpha = info_ptr->trans_alpha;
-            retval |= PNG_INFO_tRNS;
-         }
-
-         if (trans_color != NULL)
-            *trans_color = &(info_ptr->trans_color);
-      }
-
-      else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */
-      {
-         if (trans_color != NULL)
-         {
-            *trans_color = &(info_ptr->trans_color);
-            retval |= PNG_INFO_tRNS;
-         }
-
-         if (trans_alpha != NULL)
-            *trans_alpha = NULL;
-      }
-
-      if (num_trans != NULL)
-      {
-         *num_trans = info_ptr->num_trans;
-         retval |= PNG_INFO_tRNS;
-      }
-   }
-
-   return (retval);
-}
-#endif
-
-#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
-int PNGAPI
-png_get_unknown_chunks(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_unknown_chunkpp unknowns)
-{
-   if (png_ptr != NULL && info_ptr != NULL && unknowns != NULL)
-   {
-      *unknowns = info_ptr->unknown_chunks;
-      return info_ptr->unknown_chunks_num;
-   }
-
-   return (0);
-}
-#endif
-
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-png_byte PNGAPI
-png_get_rgb_to_gray_status (png_const_structrp png_ptr)
-{
-   return (png_byte)(png_ptr ? png_ptr->rgb_to_gray_status : 0);
-}
-#endif
-
-#ifdef PNG_USER_CHUNKS_SUPPORTED
-png_voidp PNGAPI
-png_get_user_chunk_ptr(png_const_structrp png_ptr)
-{
-   return (png_ptr ? png_ptr->user_chunk_ptr : NULL);
-}
-#endif
-
-size_t PNGAPI
-png_get_compression_buffer_size(png_const_structrp png_ptr)
-{
-   if (png_ptr == NULL)
-      return 0;
-
-#ifdef PNG_WRITE_SUPPORTED
-   if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0)
-#endif
-   {
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-      return png_ptr->IDAT_read_size;
-#else
-      return PNG_IDAT_READ_SIZE;
-#endif
-   }
-
-#ifdef PNG_WRITE_SUPPORTED
-   else
-      return png_ptr->zbuffer_size;
-#endif
-}
-
-#ifdef PNG_SET_USER_LIMITS_SUPPORTED
-/* These functions were added to libpng 1.2.6 and were enabled
- * by default in libpng-1.4.0 */
-png_uint_32 PNGAPI
-png_get_user_width_max (png_const_structrp png_ptr)
-{
-   return (png_ptr ? png_ptr->user_width_max : 0);
-}
-
-png_uint_32 PNGAPI
-png_get_user_height_max (png_const_structrp png_ptr)
-{
-   return (png_ptr ? png_ptr->user_height_max : 0);
-}
-
-/* This function was added to libpng 1.4.0 */
-png_uint_32 PNGAPI
-png_get_chunk_cache_max (png_const_structrp png_ptr)
-{
-   return (png_ptr ? png_ptr->user_chunk_cache_max : 0);
-}
-
-/* This function was added to libpng 1.4.1 */
-png_alloc_size_t PNGAPI
-png_get_chunk_malloc_max (png_const_structrp png_ptr)
-{
-   return (png_ptr ? png_ptr->user_chunk_malloc_max : 0);
-}
-#endif /* SET_USER_LIMITS */
-
-/* These functions were added to libpng 1.4.0 */
-#ifdef PNG_IO_STATE_SUPPORTED
-png_uint_32 PNGAPI
-png_get_io_state (png_const_structrp png_ptr)
-{
-   return png_ptr->io_state;
-}
-
-png_uint_32 PNGAPI
-png_get_io_chunk_type (png_const_structrp png_ptr)
-{
-   return png_ptr->chunk_name;
-}
-#endif /* IO_STATE */
-
-#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
-#  ifdef PNG_GET_PALETTE_MAX_SUPPORTED
-int PNGAPI
-png_get_palette_max(png_const_structp png_ptr, png_const_infop info_ptr)
-{
-   if (png_ptr != NULL && info_ptr != NULL)
-      return png_ptr->num_palette_max;
-
-   return (-1);
-}
-#  endif
-#endif
-
-#endif /* READ || WRITE */
diff --git a/third_party/libpng16/pnginfo.h b/third_party/libpng16/pnginfo.h
deleted file mode 100644
index 1f98ded..0000000
--- a/third_party/libpng16/pnginfo.h
+++ /dev/null
@@ -1,267 +0,0 @@
-
-/* pnginfo.h - header file for PNG reference library
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2013,2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
- /* png_info is a structure that holds the information in a PNG file so
- * that the application can find out the characteristics of the image.
- * If you are reading the file, this structure will tell you what is
- * in the PNG file.  If you are writing the file, fill in the information
- * you want to put into the PNG file, using png_set_*() functions, then
- * call png_write_info().
- *
- * The names chosen should be very close to the PNG specification, so
- * consult that document for information about the meaning of each field.
- *
- * With libpng < 0.95, it was only possible to directly set and read the
- * the values in the png_info_struct, which meant that the contents and
- * order of the values had to remain fixed.  With libpng 0.95 and later,
- * however, there are now functions that abstract the contents of
- * png_info_struct from the application, so this makes it easier to use
- * libpng with dynamic libraries, and even makes it possible to use
- * libraries that don't have all of the libpng ancillary chunk-handing
- * functionality.  In libpng-1.5.0 this was moved into a separate private
- * file that is not visible to applications.
- *
- * The following members may have allocated storage attached that should be
- * cleaned up before the structure is discarded: palette, trans, text,
- * pcal_purpose, pcal_units, pcal_params, hist, iccp_name, iccp_profile,
- * splt_palettes, scal_unit, row_pointers, and unknowns.   By default, these
- * are automatically freed when the info structure is deallocated, if they were
- * allocated internally by libpng.  This behavior can be changed by means
- * of the png_data_freer() function.
- *
- * More allocation details: all the chunk-reading functions that
- * change these members go through the corresponding png_set_*
- * functions.  A function to clear these members is available: see
- * png_free_data().  The png_set_* functions do not depend on being
- * able to point info structure members to any of the storage they are
- * passed (they make their own copies), EXCEPT that the png_set_text
- * functions use the same storage passed to them in the text_ptr or
- * itxt_ptr structure argument, and the png_set_rows and png_set_unknowns
- * functions do not make their own copies.
- */
-#ifndef PNGINFO_H
-#define PNGINFO_H
-
-struct png_info_def
-{
-   /* The following are necessary for every PNG file */
-   png_uint_32 width;       /* width of image in pixels (from IHDR) */
-   png_uint_32 height;      /* height of image in pixels (from IHDR) */
-   png_uint_32 valid;       /* valid chunk data (see PNG_INFO_ below) */
-   size_t rowbytes;         /* bytes needed to hold an untransformed row */
-   png_colorp palette;      /* array of color values (valid & PNG_INFO_PLTE) */
-   png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */
-   png_uint_16 num_trans;   /* number of transparent palette color (tRNS) */
-   png_byte bit_depth;      /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */
-   png_byte color_type;     /* see PNG_COLOR_TYPE_ below (from IHDR) */
-   /* The following three should have been named *_method not *_type */
-   png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */
-   png_byte filter_type;    /* must be PNG_FILTER_TYPE_BASE (from IHDR) */
-   png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
-
-   /* The following are set by png_set_IHDR, called from the application on
-    * write, but the are never actually used by the write code.
-    */
-   png_byte channels;       /* number of data channels per pixel (1, 2, 3, 4) */
-   png_byte pixel_depth;    /* number of bits per pixel */
-   png_byte spare_byte;     /* to align the data, and for future use */
-
-#ifdef PNG_READ_SUPPORTED
-   /* This is never set during write */
-   png_byte signature[8];   /* magic bytes read by libpng from start of file */
-#endif
-
-   /* The rest of the data is optional.  If you are reading, check the
-    * valid field to see if the information in these are valid.  If you
-    * are writing, set the valid field to those chunks you want written,
-    * and initialize the appropriate fields below.
-    */
-
-#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
-   /* png_colorspace only contains 'flags' if neither GAMMA or COLORSPACE are
-    * defined.  When COLORSPACE is switched on all the colorspace-defining
-    * chunks should be enabled, when GAMMA is switched on all the gamma-defining
-    * chunks should be enabled.  If this is not done it becomes possible to read
-    * inconsistent PNG files and assign a probably incorrect interpretation to
-    * the information.  (In other words, by carefully choosing which chunks to
-    * recognize the system configuration can select an interpretation for PNG
-    * files containing ambiguous data and this will result in inconsistent
-    * behavior between different libpng builds!)
-    */
-   png_colorspace colorspace;
-#endif
-
-#ifdef PNG_iCCP_SUPPORTED
-   /* iCCP chunk data. */
-   png_charp iccp_name;     /* profile name */
-   png_bytep iccp_profile;  /* International Color Consortium profile data */
-   png_uint_32 iccp_proflen;  /* ICC profile data length */
-#endif
-
-#ifdef PNG_TEXT_SUPPORTED
-   /* The tEXt, and zTXt chunks contain human-readable textual data in
-    * uncompressed, compressed, and optionally compressed forms, respectively.
-    * The data in "text" is an array of pointers to uncompressed,
-    * null-terminated C strings. Each chunk has a keyword that describes the
-    * textual data contained in that chunk.  Keywords are not required to be
-    * unique, and the text string may be empty.  Any number of text chunks may
-    * be in an image.
-    */
-   int num_text; /* number of comments read or comments to write */
-   int max_text; /* current size of text array */
-   png_textp text; /* array of comments read or comments to write */
-#endif /* TEXT */
-
-#ifdef PNG_tIME_SUPPORTED
-   /* The tIME chunk holds the last time the displayed image data was
-    * modified.  See the png_time struct for the contents of this struct.
-    */
-   png_time mod_time;
-#endif
-
-#ifdef PNG_sBIT_SUPPORTED
-   /* The sBIT chunk specifies the number of significant high-order bits
-    * in the pixel data.  Values are in the range [1, bit_depth], and are
-    * only specified for the channels in the pixel data.  The contents of
-    * the low-order bits is not specified.  Data is valid if
-    * (valid & PNG_INFO_sBIT) is non-zero.
-    */
-   png_color_8 sig_bit; /* significant bits in color channels */
-#endif
-
-#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_EXPAND_SUPPORTED) || \
-defined(PNG_READ_BACKGROUND_SUPPORTED)
-   /* The tRNS chunk supplies transparency data for paletted images and
-    * other image types that don't need a full alpha channel.  There are
-    * "num_trans" transparency values for a paletted image, stored in the
-    * same order as the palette colors, starting from index 0.  Values
-    * for the data are in the range [0, 255], ranging from fully transparent
-    * to fully opaque, respectively.  For non-paletted images, there is a
-    * single color specified that should be treated as fully transparent.
-    * Data is valid if (valid & PNG_INFO_tRNS) is non-zero.
-    */
-   png_bytep trans_alpha;    /* alpha values for paletted image */
-   png_color_16 trans_color; /* transparent color for non-palette image */
-#endif
-
-#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
-   /* The bKGD chunk gives the suggested image background color if the
-    * display program does not have its own background color and the image
-    * is needs to composited onto a background before display.  The colors
-    * in "background" are normally in the same color space/depth as the
-    * pixel data.  Data is valid if (valid & PNG_INFO_bKGD) is non-zero.
-    */
-   png_color_16 background;
-#endif
-
-#ifdef PNG_oFFs_SUPPORTED
-   /* The oFFs chunk gives the offset in "offset_unit_type" units rightwards
-    * and downwards from the top-left corner of the display, page, or other
-    * application-specific co-ordinate space.  See the PNG_OFFSET_ defines
-    * below for the unit types.  Valid if (valid & PNG_INFO_oFFs) non-zero.
-    */
-   png_int_32 x_offset; /* x offset on page */
-   png_int_32 y_offset; /* y offset on page */
-   png_byte offset_unit_type; /* offset units type */
-#endif
-
-#ifdef PNG_pHYs_SUPPORTED
-   /* The pHYs chunk gives the physical pixel density of the image for
-    * display or printing in "phys_unit_type" units (see PNG_RESOLUTION_
-    * defines below).  Data is valid if (valid & PNG_INFO_pHYs) is non-zero.
-    */
-   png_uint_32 x_pixels_per_unit; /* horizontal pixel density */
-   png_uint_32 y_pixels_per_unit; /* vertical pixel density */
-   png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */
-#endif
-
-#ifdef PNG_eXIf_SUPPORTED
-   int num_exif;  /* Added at libpng-1.6.31 */
-   png_bytep exif;
-# ifdef PNG_READ_eXIf_SUPPORTED
-   png_bytep eXIf_buf;  /* Added at libpng-1.6.32 */
-# endif
-#endif
-
-#ifdef PNG_hIST_SUPPORTED
-   /* The hIST chunk contains the relative frequency or importance of the
-    * various palette entries, so that a viewer can intelligently select a
-    * reduced-color palette, if required.  Data is an array of "num_palette"
-    * values in the range [0,65535]. Data valid if (valid & PNG_INFO_hIST)
-    * is non-zero.
-    */
-   png_uint_16p hist;
-#endif
-
-#ifdef PNG_pCAL_SUPPORTED
-   /* The pCAL chunk describes a transformation between the stored pixel
-    * values and original physical data values used to create the image.
-    * The integer range [0, 2^bit_depth - 1] maps to the floating-point
-    * range given by [pcal_X0, pcal_X1], and are further transformed by a
-    * (possibly non-linear) transformation function given by "pcal_type"
-    * and "pcal_params" into "pcal_units".  Please see the PNG_EQUATION_
-    * defines below, and the PNG-Group's PNG extensions document for a
-    * complete description of the transformations and how they should be
-    * implemented, and for a description of the ASCII parameter strings.
-    * Data values are valid if (valid & PNG_INFO_pCAL) non-zero.
-    */
-   png_charp pcal_purpose;  /* pCAL chunk description string */
-   png_int_32 pcal_X0;      /* minimum value */
-   png_int_32 pcal_X1;      /* maximum value */
-   png_charp pcal_units;    /* Latin-1 string giving physical units */
-   png_charpp pcal_params;  /* ASCII strings containing parameter values */
-   png_byte pcal_type;      /* equation type (see PNG_EQUATION_ below) */
-   png_byte pcal_nparams;   /* number of parameters given in pcal_params */
-#endif
-
-/* New members added in libpng-1.0.6 */
-   png_uint_32 free_me;     /* flags items libpng is responsible for freeing */
-
-#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
-   /* Storage for unknown chunks that the library doesn't recognize. */
-   png_unknown_chunkp unknown_chunks;
-
-   /* The type of this field is limited by the type of
-    * png_struct::user_chunk_cache_max, else overflow can occur.
-    */
-   int                unknown_chunks_num;
-#endif
-
-#ifdef PNG_sPLT_SUPPORTED
-   /* Data on sPLT chunks (there may be more than one). */
-   png_sPLT_tp splt_palettes;
-   int         splt_palettes_num; /* Match type returned by png_get API */
-#endif
-
-#ifdef PNG_sCAL_SUPPORTED
-   /* The sCAL chunk describes the actual physical dimensions of the
-    * subject matter of the graphic.  The chunk contains a unit specification
-    * a byte value, and two ASCII strings representing floating-point
-    * values.  The values are width and height corresponding to one pixel
-    * in the image.  Data values are valid if (valid & PNG_INFO_sCAL) is
-    * non-zero.
-    */
-   png_byte scal_unit;         /* unit of physical scale */
-   png_charp scal_s_width;     /* string containing height */
-   png_charp scal_s_height;    /* string containing width */
-#endif
-
-#ifdef PNG_INFO_IMAGE_SUPPORTED
-   /* Memory has been allocated if (valid & PNG_ALLOCATED_INFO_ROWS)
-      non-zero */
-   /* Data valid if (valid & PNG_INFO_IDAT) non-zero */
-   png_bytepp row_pointers;        /* the image bits */
-#endif
-
-};
-#endif /* PNGINFO_H */
diff --git a/third_party/libpng16/pnglibconf.h b/third_party/libpng16/pnglibconf.h
deleted file mode 100644
index 308c606..0000000
--- a/third_party/libpng16/pnglibconf.h
+++ /dev/null
@@ -1,236 +0,0 @@
-/* libpng 1.6.22 CUSTOM API DEFINITION */
-
-/* pnglibconf.h - library build configuration */
-
-/* Libpng version 1.6.22 - May 29, 2016 */
-
-/* Copyright (c) 1998-2015 Glenn Randers-Pehrson */
-
-/* This code is released under the libpng license. */
-/* For conditions of distribution and use, see the disclaimer */
-/* and license in png.h */
-
-/* pnglibconf.h */
-/* Derived from: scripts/pnglibconf.dfa */
-#ifndef PNGLCONF_H
-#define PNGLCONF_H
-
-/* default options */
-/* These are PNG options that match the default in scripts/pnglibconf.dfa */
-#define PNG_16BIT_SUPPORTED
-#define PNG_ALIGNED_MEMORY_SUPPORTED
-/*#undef PNG_ARM_NEON_API_SUPPORTED*/
-/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/
-#define PNG_BENIGN_ERRORS_SUPPORTED
-#define PNG_BENIGN_READ_ERRORS_SUPPORTED
-/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/
-#define PNG_COLORSPACE_SUPPORTED
-#define PNG_EASY_ACCESS_SUPPORTED
-/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/
-#define PNG_ERROR_TEXT_SUPPORTED
-#define PNG_FIXED_POINT_SUPPORTED
-#define PNG_FLOATING_ARITHMETIC_SUPPORTED
-#define PNG_FLOATING_POINT_SUPPORTED
-#define PNG_FORMAT_AFIRST_SUPPORTED
-#define PNG_FORMAT_BGR_SUPPORTED
-#define PNG_GAMMA_SUPPORTED
-#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-#define PNG_INFO_IMAGE_SUPPORTED
-#define PNG_POINTER_INDEXING_SUPPORTED
-#define PNG_PROGRESSIVE_READ_SUPPORTED
-#define PNG_READ_16BIT_SUPPORTED
-#define PNG_READ_ALPHA_MODE_SUPPORTED
-#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED
-#define PNG_READ_BACKGROUND_SUPPORTED
-#define PNG_READ_BGR_SUPPORTED
-#define PNG_READ_COMPOSITE_NODIV_SUPPORTED
-#define PNG_READ_COMPRESSED_TEXT_SUPPORTED
-#define PNG_READ_EXPAND_16_SUPPORTED
-#define PNG_READ_EXPAND_SUPPORTED
-#define PNG_READ_FILLER_SUPPORTED
-#define PNG_READ_GAMMA_SUPPORTED
-#define PNG_READ_GRAY_TO_RGB_SUPPORTED
-#define PNG_READ_INTERLACING_SUPPORTED
-#define PNG_READ_INT_FUNCTIONS_SUPPORTED
-#define PNG_READ_PACKSWAP_SUPPORTED
-#define PNG_READ_PACK_SUPPORTED
-#define PNG_READ_RGB_TO_GRAY_SUPPORTED
-#define PNG_READ_SCALE_16_TO_8_SUPPORTED
-#define PNG_READ_SHIFT_SUPPORTED
-#define PNG_READ_STRIP_16_TO_8_SUPPORTED
-#define PNG_READ_STRIP_ALPHA_SUPPORTED
-#define PNG_READ_SUPPORTED
-#define PNG_READ_SWAP_ALPHA_SUPPORTED
-#define PNG_READ_SWAP_SUPPORTED
-#define PNG_READ_TEXT_SUPPORTED
-#define PNG_READ_TRANSFORMS_SUPPORTED
-#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
-#define PNG_READ_USER_CHUNKS_SUPPORTED
-#define PNG_READ_USER_TRANSFORM_SUPPORTED
-#define PNG_READ_cHRM_SUPPORTED
-#define PNG_READ_gAMA_SUPPORTED
-#define PNG_READ_iCCP_SUPPORTED
-#define PNG_READ_sRGB_SUPPORTED
-#define PNG_READ_tEXt_SUPPORTED
-#define PNG_READ_tRNS_SUPPORTED
-#define PNG_READ_zTXt_SUPPORTED
-#define PNG_SAVE_INT_32_SUPPORTED
-#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
-#define PNG_SEQUENTIAL_READ_SUPPORTED
-#define PNG_SETJMP_SUPPORTED
-#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
-#define PNG_SET_USER_LIMITS_SUPPORTED
-#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED
-#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED
-#define PNG_SIMPLIFIED_READ_SUPPORTED
-#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
-#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED
-#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
-#define PNG_SIMPLIFIED_WRITE_SUPPORTED
-#define PNG_STDIO_SUPPORTED
-#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
-#define PNG_TEXT_SUPPORTED
-#define PNG_UNKNOWN_CHUNKS_SUPPORTED
-#define PNG_USER_CHUNKS_SUPPORTED
-#define PNG_USER_LIMITS_SUPPORTED
-#define PNG_USER_MEM_SUPPORTED
-#define PNG_USER_TRANSFORM_INFO_SUPPORTED
-#define PNG_USER_TRANSFORM_PTR_SUPPORTED
-#define PNG_WARNINGS_SUPPORTED
-#define PNG_WRITE_16BIT_SUPPORTED
-#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED
-#define PNG_WRITE_BGR_SUPPORTED
-#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
-#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
-#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
-#define PNG_WRITE_FILLER_SUPPORTED
-#define PNG_WRITE_FILTER_SUPPORTED
-#define PNG_WRITE_FLUSH_SUPPORTED
-#define PNG_WRITE_INTERLACING_SUPPORTED
-#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED
-#define PNG_WRITE_PACKSWAP_SUPPORTED
-#define PNG_WRITE_PACK_SUPPORTED
-#define PNG_WRITE_SHIFT_SUPPORTED
-#define PNG_WRITE_SUPPORTED
-#define PNG_WRITE_SWAP_ALPHA_SUPPORTED
-#define PNG_WRITE_SWAP_SUPPORTED
-#define PNG_WRITE_TEXT_SUPPORTED
-#define PNG_WRITE_TRANSFORMS_SUPPORTED
-#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
-#define PNG_WRITE_USER_TRANSFORM_SUPPORTED
-#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
-#define PNG_WRITE_cHRM_SUPPORTED
-#define PNG_WRITE_gAMA_SUPPORTED
-#define PNG_WRITE_iCCP_SUPPORTED
-#define PNG_WRITE_sRGB_SUPPORTED
-#define PNG_WRITE_tEXt_SUPPORTED
-#define PNG_WRITE_tRNS_SUPPORTED
-#define PNG_WRITE_zTXt_SUPPORTED
-#define PNG_cHRM_SUPPORTED
-#define PNG_gAMA_SUPPORTED
-#define PNG_iCCP_SUPPORTED
-#define PNG_sBIT_SUPPORTED
-#define PNG_sRGB_SUPPORTED
-#define PNG_tEXt_SUPPORTED
-#define PNG_tRNS_SUPPORTED
-#define PNG_zTXt_SUPPORTED
-/* end of options */
-
-/* chromium options */
-/* These are PNG options that chromium chooses to explicitly disable */
-/*#undef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED*/
-/*#undef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED*/
-/*#undef PNG_CONSOLE_IO_SUPPORTED*/
-/*#undef PNG_CONVERT_tIME_SUPPORTED*/
-/*#undef PNG_GET_PALETTE_MAX_SUPPORTED*/
-/*#undef PNG_INCH_CONVERSIONS_SUPPORTED*/
-/*#undef PNG_IO_STATE_SUPPORTED*/
-/*#undef PNG_MNG_FEATURES_SUPPORTED*/
-/*#undef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED*/
-/*#undef PNG_READ_GET_PALETTE_MAX_SUPPORTED*/
-/*#undef PNG_READ_INVERT_ALPHA_SUPPORTED*/
-/*#undef PNG_READ_INVERT_SUPPORTED*/
-/*#undef PNG_READ_OPT_PLTE_SUPPORTED*/
-/*#undef PNG_READ_QUANTIZE_SUPPORTED*/
-/*#undef PNG_READ_bKGD_SUPPORTED*/
-/*#undef PNG_READ_hIST_SUPPORTED*/
-/*#undef PNG_READ_iTXt_SUPPORTED*/
-/*#undef PNG_READ_oFFs_SUPPORTED*/
-/*#undef PNG_READ_pCAL_SUPPORTED*/
-/*#undef PNG_READ_pHYs_SUPPORTED*/
-/*#undef PNG_READ_sBIT_SUPPORTED*/
-/*#undef PNG_READ_sCAL_SUPPORTED*/
-/*#undef PNG_READ_sPLT_SUPPORTED*/
-/*#undef PNG_READ_tIME_SUPPORTED*/
-/*#undef PNG_SET_OPTION_SUPPORTED*/
-/*#undef PNG_TIME_RFC1123_SUPPORTED*/
-/*#undef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED*/
-/*#undef PNG_WRITE_GET_PALETTE_MAX_SUPPORTED*/
-/*#undef PNG_WRITE_INVERT_ALPHA_SUPPORTED*/
-/*#undef PNG_WRITE_INVERT_SUPPORTED*/
-/*#undef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED*/
-/*#undef PNG_WRITE_bKGD_SUPPORTED*/
-/*#undef PNG_WRITE_hIST_SUPPORTED*/
-/*#undef PNG_WRITE_iTXt_SUPPORTED*/
-/*#undef PNG_WRITE_oFFs_SUPPORTED*/
-/*#undef PNG_WRITE_pCAL_SUPPORTED*/
-/*#undef PNG_WRITE_pHYs_SUPPORTED*/
-/*#undef PNG_WRITE_sBIT_SUPPORTED*/
-/*#undef PNG_WRITE_sCAL_SUPPORTED*/
-/*#undef PNG_WRITE_sPLT_SUPPORTED*/
-/*#undef PNG_WRITE_tIME_SUPPORTED*/
-/*#undef PNG_bKGD_SUPPORTED*/
-/*#undef PNG_hIST_SUPPORTED*/
-/*#undef PNG_iTXt_SUPPORTED*/
-/*#undef PNG_oFFs_SUPPORTED*/
-/*#undef PNG_pCAL_SUPPORTED*/
-/*#undef PNG_pHYs_SUPPORTED*/
-/*#undef PNG_sCAL_SUPPORTED*/
-/*#undef PNG_sPLT_SUPPORTED*/
-/*#undef PNG_tIME_SUPPORTED*/
-/* end of chromium options */
-
-/* default settings */
-/* These are PNG settings that match the default in scripts/pnglibconf.dfa */
-#define PNG_API_RULE 0
-#define PNG_DEFAULT_READ_MACROS 1
-#define PNG_GAMMA_THRESHOLD_FIXED 5000
-#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE
-#define PNG_INFLATE_BUF_SIZE 1024
-#define PNG_LINKAGE_API extern
-#define PNG_LINKAGE_CALLBACK extern
-#define PNG_LINKAGE_DATA extern
-#define PNG_LINKAGE_FUNCTION extern
-#define PNG_MAX_GAMMA_8 11
-#define PNG_QUANTIZE_BLUE_BITS 5
-#define PNG_QUANTIZE_GREEN_BITS 5
-#define PNG_QUANTIZE_RED_BITS 5
-#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1)
-#define PNG_TEXT_Z_DEFAULT_STRATEGY 0
-#define PNG_USER_HEIGHT_MAX 1000000
-#define PNG_USER_WIDTH_MAX 1000000
-#define PNG_ZBUF_SIZE 8192
-#define PNG_ZLIB_VERNUM 0 /* unknown */
-#define PNG_Z_DEFAULT_COMPRESSION (-1)
-#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0
-#define PNG_Z_DEFAULT_STRATEGY 1
-#define PNG_sCAL_PRECISION 5
-#define PNG_sRGB_PROFILE_CHECKS 2
-/* end of default settings */
-
-/* chromium settings */
-/* These are PNG setting that chromium has modified */
-/* crbug.com/117369 */
-#define PNG_USER_CHUNK_CACHE_MAX 128
-#define PNG_USER_CHUNK_MALLOC_MAX 4000000L
-/* end of chromium settings */
-
-/* pdfium prefixing */
-/*
- * This is necessary to build multiple copies of libpng.  We need this while pdfium builds
- * its own copy of libpng.
- */
-#include "pngprefix.h"
-/* end of pdfium prefixing */
-
-#endif /* PNGLCONF_H */
diff --git a/third_party/libpng16/pngmem.c b/third_party/libpng16/pngmem.c
deleted file mode 100644
index 726b7e0..0000000
--- a/third_party/libpng16/pngmem.c
+++ /dev/null
@@ -1,287 +0,0 @@
-
-/* pngmem.c - stub functions for memory allocation
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2014,2016 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- *
- * This file provides a location for all memory allocation.  Users who
- * need special memory handling are expected to supply replacement
- * functions for png_malloc() and png_free(), and to use
- * png_create_read_struct_2() and png_create_write_struct_2() to
- * identify the replacement functions.
- */
-
-#include "pngpriv.h"
-
-void*	FXMEM_DefaultAlloc(size_t byte_size);
-void	FXMEM_DefaultFree(void* pointer);
-
-#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
-/* Free a png_struct */
-void /* PRIVATE */
-png_destroy_png_struct(png_structrp png_ptr)
-{
-   if (png_ptr != NULL)
-   {
-      /* png_free might call png_error and may certainly call
-       * png_get_mem_ptr, so fake a temporary png_struct to support this.
-       */
-      png_struct dummy_struct = *png_ptr;
-      memset(png_ptr, 0, (sizeof *png_ptr));
-      png_free(&dummy_struct, png_ptr);
-
-#     ifdef PNG_SETJMP_SUPPORTED
-         /* We may have a jmp_buf left to deallocate. */
-         png_free_jmpbuf(&dummy_struct);
-#     endif
-   }
-}
-
-/* Allocate memory.  For reasonable files, size should never exceed
- * 64K.  However, zlib may allocate more than 64K if you don't tell
- * it not to.  See zconf.h and png.h for more information.  zlib does
- * need to allocate exactly 64K, so whatever you call here must
- * have the ability to do that.
- */
-PNG_FUNCTION(png_voidp,PNGAPI
-png_calloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
-{
-   png_voidp ret;
-
-   ret = png_malloc(png_ptr, size);
-
-   if (ret != NULL)
-      memset(ret, 0, size);
-
-   return ret;
-}
-
-/* png_malloc_base, an internal function added at libpng 1.6.0, does the work of
- * allocating memory, taking into account limits and PNG_USER_MEM_SUPPORTED.
- * Checking and error handling must happen outside this routine; it returns NULL
- * if the allocation cannot be done (for any reason.)
- */
-PNG_FUNCTION(png_voidp /* PRIVATE */,
-png_malloc_base,(png_const_structrp png_ptr, png_alloc_size_t size),
-    PNG_ALLOCATED)
-{
-   /* Moved to png_malloc_base from png_malloc_default in 1.6.0; the DOS
-    * allocators have also been removed in 1.6.0, so any 16-bit system now has
-    * to implement a user memory handler.  This checks to be sure it isn't
-    * called with big numbers.
-    */
-#ifndef PNG_USER_MEM_SUPPORTED
-   PNG_UNUSED(png_ptr)
-#endif
-
-   /* Some compilers complain that this is always true.  However, it
-    * can be false when integer overflow happens.
-    */
-   if (size > 0 && size <= PNG_SIZE_MAX
-#     ifdef PNG_MAX_MALLOC_64K
-         && size <= 65536U
-#     endif
-      )
-   {
-#ifdef PNG_USER_MEM_SUPPORTED
-      if (png_ptr != NULL && png_ptr->malloc_fn != NULL)
-         return png_ptr->malloc_fn(png_constcast(png_structrp,png_ptr), size);
-
-      else
-#endif
-         return FXMEM_DefaultAlloc(size);
-   }
-
-   else
-      return NULL;
-}
-
-#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) ||\
-   defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED)
-/* This is really here only to work round a spurious warning in GCC 4.6 and 4.7
- * that arises because of the checks in png_realloc_array that are repeated in
- * png_malloc_array.
- */
-static png_voidp
-png_malloc_array_checked(png_const_structrp png_ptr, int nelements,
-    size_t element_size)
-{
-   png_alloc_size_t req = (png_alloc_size_t)nelements; /* known to be > 0 */
-
-   if (req <= PNG_SIZE_MAX/element_size)
-      return png_malloc_base(png_ptr, req * element_size);
-
-   /* The failure case when the request is too large */
-   return NULL;
-}
-
-PNG_FUNCTION(png_voidp /* PRIVATE */,
-png_malloc_array,(png_const_structrp png_ptr, int nelements,
-    size_t element_size),PNG_ALLOCATED)
-{
-   if (nelements <= 0 || element_size == 0)
-      png_error(png_ptr, "internal error: array alloc");
-
-   return png_malloc_array_checked(png_ptr, nelements, element_size);
-}
-
-PNG_FUNCTION(png_voidp /* PRIVATE */,
-png_realloc_array,(png_const_structrp png_ptr, png_const_voidp old_array,
-    int old_elements, int add_elements, size_t element_size),PNG_ALLOCATED)
-{
-   /* These are internal errors: */
-   if (add_elements <= 0 || element_size == 0 || old_elements < 0 ||
-      (old_array == NULL && old_elements > 0))
-      png_error(png_ptr, "internal error: array realloc");
-
-   /* Check for overflow on the elements count (so the caller does not have to
-    * check.)
-    */
-   if (add_elements <= INT_MAX - old_elements)
-   {
-      png_voidp new_array = png_malloc_array_checked(png_ptr,
-          old_elements+add_elements, element_size);
-
-      if (new_array != NULL)
-      {
-         /* Because png_malloc_array worked the size calculations below cannot
-          * overflow.
-          */
-         if (old_elements > 0)
-            memcpy(new_array, old_array, element_size*(unsigned)old_elements);
-
-         memset((char*)new_array + element_size*(unsigned)old_elements, 0,
-             element_size*(unsigned)add_elements);
-
-         return new_array;
-      }
-   }
-
-   return NULL; /* error */
-}
-#endif /* TEXT || sPLT || STORE_UNKNOWN_CHUNKS */
-
-/* Various functions that have different error handling are derived from this.
- * png_malloc always exists, but if PNG_USER_MEM_SUPPORTED is defined a separate
- * function png_malloc_default is also provided.
- */
-PNG_FUNCTION(png_voidp,PNGAPI
-png_malloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
-{
-   png_voidp ret;
-
-   if (png_ptr == NULL)
-      return NULL;
-
-   ret = png_malloc_base(png_ptr, size);
-
-   if (ret == NULL)
-       png_error(png_ptr, "Out of memory"); /* 'm' means png_malloc */
-
-   return ret;
-}
-
-#ifdef PNG_USER_MEM_SUPPORTED
-PNG_FUNCTION(png_voidp,PNGAPI
-png_malloc_default,(png_const_structrp png_ptr, png_alloc_size_t size),
-    PNG_ALLOCATED PNG_DEPRECATED)
-{
-   png_voidp ret;
-
-   if (png_ptr == NULL)
-      return NULL;
-
-   /* Passing 'NULL' here bypasses the application provided memory handler. */
-   ret = png_malloc_base(NULL/*use malloc*/, size);
-
-   if (ret == NULL)
-      png_error(png_ptr, "Out of Memory"); /* 'M' means png_malloc_default */
-
-   return ret;
-}
-#endif /* USER_MEM */
-
-/* This function was added at libpng version 1.2.3.  The png_malloc_warn()
- * function will issue a png_warning and return NULL instead of issuing a
- * png_error, if it fails to allocate the requested memory.
- */
-PNG_FUNCTION(png_voidp,PNGAPI
-png_malloc_warn,(png_const_structrp png_ptr, png_alloc_size_t size),
-    PNG_ALLOCATED)
-{
-   if (png_ptr != NULL)
-   {
-      png_voidp ret = png_malloc_base(png_ptr, size);
-
-      if (ret != NULL)
-         return ret;
-
-      png_warning(png_ptr, "Out of memory");
-   }
-
-   return NULL;
-}
-
-/* Free a pointer allocated by png_malloc().  If ptr is NULL, return
- * without taking any action.
- */
-void PNGAPI
-png_free(png_const_structrp png_ptr, png_voidp ptr)
-{
-   if (png_ptr == NULL || ptr == NULL)
-      return;
-
-#ifdef PNG_USER_MEM_SUPPORTED
-   if (png_ptr->free_fn != NULL)
-      png_ptr->free_fn(png_constcast(png_structrp,png_ptr), ptr);
-
-   else
-      png_free_default(png_ptr, ptr);
-}
-
-PNG_FUNCTION(void,PNGAPI
-png_free_default,(png_const_structrp png_ptr, png_voidp ptr),PNG_DEPRECATED)
-{
-   if (png_ptr == NULL || ptr == NULL)
-      return;
-#endif /* USER_MEM */
-
-   FXMEM_DefaultFree(ptr);
-}
-
-#ifdef PNG_USER_MEM_SUPPORTED
-/* This function is called when the application wants to use another method
- * of allocating and freeing memory.
- */
-void PNGAPI
-png_set_mem_fn(png_structrp png_ptr, png_voidp mem_ptr, png_malloc_ptr
-  malloc_fn, png_free_ptr free_fn)
-{
-   if (png_ptr != NULL)
-   {
-      png_ptr->mem_ptr = mem_ptr;
-      png_ptr->malloc_fn = malloc_fn;
-      png_ptr->free_fn = free_fn;
-   }
-}
-
-/* This function returns a pointer to the mem_ptr associated with the user
- * functions.  The application should free any memory associated with this
- * pointer before png_write_destroy and png_read_destroy are called.
- */
-png_voidp PNGAPI
-png_get_mem_ptr(png_const_structrp png_ptr)
-{
-   if (png_ptr == NULL)
-      return NULL;
-
-   return png_ptr->mem_ptr;
-}
-#endif /* USER_MEM */
-#endif /* READ || WRITE */
diff --git a/third_party/libpng16/pngpread.c b/third_party/libpng16/pngpread.c
deleted file mode 100644
index e283627..0000000
--- a/third_party/libpng16/pngpread.c
+++ /dev/null
@@ -1,1096 +0,0 @@
-
-/* pngpread.c - read a png file in push mode
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
-#include "pngpriv.h"
-
-#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
-
-/* Push model modes */
-#define PNG_READ_SIG_MODE   0
-#define PNG_READ_CHUNK_MODE 1
-#define PNG_READ_IDAT_MODE  2
-#define PNG_READ_tEXt_MODE  4
-#define PNG_READ_zTXt_MODE  5
-#define PNG_READ_DONE_MODE  6
-#define PNG_READ_iTXt_MODE  7
-#define PNG_ERROR_MODE      8
-
-#define PNG_PUSH_SAVE_BUFFER_IF_FULL \
-if (png_ptr->push_length + 4 > png_ptr->buffer_size) \
-   { png_push_save_buffer(png_ptr); return; }
-#define PNG_PUSH_SAVE_BUFFER_IF_LT(N) \
-if (png_ptr->buffer_size < N) \
-   { png_push_save_buffer(png_ptr); return; }
-
-void PNGAPI
-png_process_data(png_structrp png_ptr, png_inforp info_ptr,
-    png_bytep buffer, size_t buffer_size)
-{
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   png_push_restore_buffer(png_ptr, buffer, buffer_size);
-
-   while (png_ptr->buffer_size)
-   {
-      png_process_some_data(png_ptr, info_ptr);
-   }
-}
-
-size_t PNGAPI
-png_process_data_pause(png_structrp png_ptr, int save)
-{
-   if (png_ptr != NULL)
-   {
-      /* It's easiest for the caller if we do the save; then the caller doesn't
-       * have to supply the same data again:
-       */
-      if (save != 0)
-         png_push_save_buffer(png_ptr);
-      else
-      {
-         /* This includes any pending saved bytes: */
-         size_t remaining = png_ptr->buffer_size;
-         png_ptr->buffer_size = 0;
-
-         /* So subtract the saved buffer size, unless all the data
-          * is actually 'saved', in which case we just return 0
-          */
-         if (png_ptr->save_buffer_size < remaining)
-            return remaining - png_ptr->save_buffer_size;
-      }
-   }
-
-   return 0;
-}
-
-png_uint_32 PNGAPI
-png_process_data_skip(png_structrp png_ptr)
-{
-/* TODO: Deprecate and remove this API.
- * Somewhere the implementation of this seems to have been lost,
- * or abandoned.  It was only to support some internal back-door access
- * to png_struct) in libpng-1.4.x.
- */
-   png_app_warning(png_ptr,
-"png_process_data_skip is not implemented in any current version of libpng");
-   return 0;
-}
-
-/* What we do with the incoming data depends on what we were previously
- * doing before we ran out of data...
- */
-void /* PRIVATE */
-png_process_some_data(png_structrp png_ptr, png_inforp info_ptr)
-{
-   if (png_ptr == NULL)
-      return;
-
-   switch (png_ptr->process_mode)
-   {
-      case PNG_READ_SIG_MODE:
-      {
-         png_push_read_sig(png_ptr, info_ptr);
-         break;
-      }
-
-      case PNG_READ_CHUNK_MODE:
-      {
-         png_push_read_chunk(png_ptr, info_ptr);
-         break;
-      }
-
-      case PNG_READ_IDAT_MODE:
-      {
-         png_push_read_IDAT(png_ptr);
-         break;
-      }
-
-      default:
-      {
-         png_ptr->buffer_size = 0;
-         break;
-      }
-   }
-}
-
-/* Read any remaining signature bytes from the stream and compare them with
- * the correct PNG signature.  It is possible that this routine is called
- * with bytes already read from the signature, either because they have been
- * checked by the calling application, or because of multiple calls to this
- * routine.
- */
-void /* PRIVATE */
-png_push_read_sig(png_structrp png_ptr, png_inforp info_ptr)
-{
-   size_t num_checked = png_ptr->sig_bytes; /* SAFE, does not exceed 8 */
-   size_t num_to_check = 8 - num_checked;
-
-   if (png_ptr->buffer_size < num_to_check)
-   {
-      num_to_check = png_ptr->buffer_size;
-   }
-
-   png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]),
-       num_to_check);
-   png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes + num_to_check);
-
-   if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check))
-   {
-      if (num_checked < 4 &&
-          png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4))
-         png_error(png_ptr, "Not a PNG file");
-
-      else
-         png_error(png_ptr, "PNG file corrupted by ASCII conversion");
-   }
-   else
-   {
-      if (png_ptr->sig_bytes >= 8)
-      {
-         png_ptr->process_mode = PNG_READ_CHUNK_MODE;
-      }
-   }
-}
-
-void /* PRIVATE */
-png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr)
-{
-   png_uint_32 chunk_name;
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-   int keep; /* unknown handling method */
-#endif
-
-   /* First we make sure we have enough data for the 4-byte chunk name
-    * and the 4-byte chunk length before proceeding with decoding the
-    * chunk data.  To fully decode each of these chunks, we also make
-    * sure we have enough data in the buffer for the 4-byte CRC at the
-    * end of every chunk (except IDAT, which is handled separately).
-    */
-   if ((png_ptr->mode & PNG_HAVE_CHUNK_HEADER) == 0)
-   {
-      png_byte chunk_length[4];
-      png_byte chunk_tag[4];
-
-      PNG_PUSH_SAVE_BUFFER_IF_LT(8)
-      png_push_fill_buffer(png_ptr, chunk_length, 4);
-      png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length);
-      png_reset_crc(png_ptr);
-      png_crc_read(png_ptr, chunk_tag, 4);
-      png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag);
-      png_check_chunk_name(png_ptr, png_ptr->chunk_name);
-      png_check_chunk_length(png_ptr, png_ptr->push_length);
-      png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
-   }
-
-   chunk_name = png_ptr->chunk_name;
-
-   if (chunk_name == png_IDAT)
-   {
-      if ((png_ptr->mode & PNG_AFTER_IDAT) != 0)
-         png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT;
-
-      /* If we reach an IDAT chunk, this means we have read all of the
-       * header chunks, and we can start reading the image (or if this
-       * is called after the image has been read - we have an error).
-       */
-      if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-         png_error(png_ptr, "Missing IHDR before IDAT");
-
-      else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
-          (png_ptr->mode & PNG_HAVE_PLTE) == 0)
-         png_error(png_ptr, "Missing PLTE before IDAT");
-
-      png_ptr->process_mode = PNG_READ_IDAT_MODE;
-
-      if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-         if ((png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT) == 0)
-            if (png_ptr->push_length == 0)
-               return;
-
-      png_ptr->mode |= PNG_HAVE_IDAT;
-
-      if ((png_ptr->mode & PNG_AFTER_IDAT) != 0)
-         png_benign_error(png_ptr, "Too many IDATs found");
-   }
-
-   if (chunk_name == png_IHDR)
-   {
-      if (png_ptr->push_length != 13)
-         png_error(png_ptr, "Invalid IHDR length");
-
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-   else if (chunk_name == png_IEND)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length);
-
-      png_ptr->process_mode = PNG_READ_DONE_MODE;
-      png_push_have_end(png_ptr, info_ptr);
-   }
-
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-   else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length, keep);
-
-      if (chunk_name == png_PLTE)
-         png_ptr->mode |= PNG_HAVE_PLTE;
-   }
-#endif
-
-   else if (chunk_name == png_PLTE)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-   else if (chunk_name == png_IDAT)
-   {
-      png_ptr->idat_size = png_ptr->push_length;
-      png_ptr->process_mode = PNG_READ_IDAT_MODE;
-      png_push_have_info(png_ptr, info_ptr);
-      png_ptr->zstream.avail_out =
-          (uInt) PNG_ROWBYTES(png_ptr->pixel_depth,
-          png_ptr->iwidth) + 1;
-      png_ptr->zstream.next_out = png_ptr->row_buf;
-      return;
-   }
-
-#ifdef PNG_READ_gAMA_SUPPORTED
-   else if (png_ptr->chunk_name == png_gAMA)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_sBIT_SUPPORTED
-   else if (png_ptr->chunk_name == png_sBIT)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_cHRM_SUPPORTED
-   else if (png_ptr->chunk_name == png_cHRM)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_sRGB_SUPPORTED
-   else if (chunk_name == png_sRGB)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_iCCP_SUPPORTED
-   else if (png_ptr->chunk_name == png_iCCP)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_sPLT_SUPPORTED
-   else if (chunk_name == png_sPLT)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_tRNS_SUPPORTED
-   else if (chunk_name == png_tRNS)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_bKGD_SUPPORTED
-   else if (chunk_name == png_bKGD)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_hIST_SUPPORTED
-   else if (chunk_name == png_hIST)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_pHYs_SUPPORTED
-   else if (chunk_name == png_pHYs)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_oFFs_SUPPORTED
-   else if (chunk_name == png_oFFs)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length);
-   }
-#endif
-
-#ifdef PNG_READ_pCAL_SUPPORTED
-   else if (chunk_name == png_pCAL)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_sCAL_SUPPORTED
-   else if (chunk_name == png_sCAL)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_tIME_SUPPORTED
-   else if (chunk_name == png_tIME)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_tEXt_SUPPORTED
-   else if (chunk_name == png_tEXt)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_zTXt_SUPPORTED
-   else if (chunk_name == png_zTXt)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length);
-   }
-
-#endif
-#ifdef PNG_READ_iTXt_SUPPORTED
-   else if (chunk_name == png_iTXt)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length);
-   }
-#endif
-
-   else
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_FULL
-      png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length,
-          PNG_HANDLE_CHUNK_AS_DEFAULT);
-   }
-
-   png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
-}
-
-void PNGCBAPI
-png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, size_t length)
-{
-   png_bytep ptr;
-
-   if (png_ptr == NULL)
-      return;
-
-   ptr = buffer;
-   if (png_ptr->save_buffer_size != 0)
-   {
-      size_t save_size;
-
-      if (length < png_ptr->save_buffer_size)
-         save_size = length;
-
-      else
-         save_size = png_ptr->save_buffer_size;
-
-      memcpy(ptr, png_ptr->save_buffer_ptr, save_size);
-      length -= save_size;
-      ptr += save_size;
-      png_ptr->buffer_size -= save_size;
-      png_ptr->save_buffer_size -= save_size;
-      png_ptr->save_buffer_ptr += save_size;
-   }
-   if (length != 0 && png_ptr->current_buffer_size != 0)
-   {
-      size_t save_size;
-
-      if (length < png_ptr->current_buffer_size)
-         save_size = length;
-
-      else
-         save_size = png_ptr->current_buffer_size;
-
-      memcpy(ptr, png_ptr->current_buffer_ptr, save_size);
-      png_ptr->buffer_size -= save_size;
-      png_ptr->current_buffer_size -= save_size;
-      png_ptr->current_buffer_ptr += save_size;
-   }
-}
-
-void /* PRIVATE */
-png_push_save_buffer(png_structrp png_ptr)
-{
-   if (png_ptr->save_buffer_size != 0)
-   {
-      if (png_ptr->save_buffer_ptr != png_ptr->save_buffer)
-      {
-         size_t i, istop;
-         png_bytep sp;
-         png_bytep dp;
-
-         istop = png_ptr->save_buffer_size;
-         for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer;
-             i < istop; i++, sp++, dp++)
-         {
-            *dp = *sp;
-         }
-      }
-   }
-   if (png_ptr->save_buffer_size + png_ptr->current_buffer_size >
-       png_ptr->save_buffer_max)
-   {
-      size_t new_max;
-      png_bytep old_buffer;
-
-      if (png_ptr->save_buffer_size > PNG_SIZE_MAX -
-          (png_ptr->current_buffer_size + 256))
-      {
-         png_error(png_ptr, "Potential overflow of save_buffer");
-      }
-
-      new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256;
-      old_buffer = png_ptr->save_buffer;
-      png_ptr->save_buffer = (png_bytep)png_malloc_warn(png_ptr,
-          (size_t)new_max);
-
-      if (png_ptr->save_buffer == NULL)
-      {
-         png_free(png_ptr, old_buffer);
-         png_error(png_ptr, "Insufficient memory for save_buffer");
-      }
-
-      if (old_buffer)
-         memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size);
-      else if (png_ptr->save_buffer_size)
-         png_error(png_ptr, "save_buffer error");
-      png_free(png_ptr, old_buffer);
-      png_ptr->save_buffer_max = new_max;
-   }
-   if (png_ptr->current_buffer_size)
-   {
-      memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size,
-         png_ptr->current_buffer_ptr, png_ptr->current_buffer_size);
-      png_ptr->save_buffer_size += png_ptr->current_buffer_size;
-      png_ptr->current_buffer_size = 0;
-   }
-   png_ptr->save_buffer_ptr = png_ptr->save_buffer;
-   png_ptr->buffer_size = 0;
-}
-
-void /* PRIVATE */
-png_push_restore_buffer(png_structrp png_ptr, png_bytep buffer,
-    size_t buffer_length)
-{
-   png_ptr->current_buffer = buffer;
-   png_ptr->current_buffer_size = buffer_length;
-   png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size;
-   png_ptr->current_buffer_ptr = png_ptr->current_buffer;
-}
-
-void /* PRIVATE */
-png_push_read_IDAT(png_structrp png_ptr)
-{
-   if ((png_ptr->mode & PNG_HAVE_CHUNK_HEADER) == 0)
-   {
-      png_byte chunk_length[4];
-      png_byte chunk_tag[4];
-
-      /* TODO: this code can be commoned up with the same code in push_read */
-      PNG_PUSH_SAVE_BUFFER_IF_LT(8)
-      png_push_fill_buffer(png_ptr, chunk_length, 4);
-      png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length);
-      png_reset_crc(png_ptr);
-      png_crc_read(png_ptr, chunk_tag, 4);
-      png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag);
-      png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
-
-      if (png_ptr->chunk_name != png_IDAT)
-      {
-         png_ptr->process_mode = PNG_READ_CHUNK_MODE;
-
-         if ((png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) == 0)
-            png_error(png_ptr, "Not enough compressed data");
-
-         return;
-      }
-
-      png_ptr->idat_size = png_ptr->push_length;
-   }
-
-   if (png_ptr->idat_size != 0 && png_ptr->save_buffer_size != 0)
-   {
-      size_t save_size = png_ptr->save_buffer_size;
-      png_uint_32 idat_size = png_ptr->idat_size;
-
-      /* We want the smaller of 'idat_size' and 'current_buffer_size', but they
-       * are of different types and we don't know which variable has the fewest
-       * bits.  Carefully select the smaller and cast it to the type of the
-       * larger - this cannot overflow.  Do not cast in the following test - it
-       * will break on either 16-bit or 64-bit platforms.
-       */
-      if (idat_size < save_size)
-         save_size = (size_t)idat_size;
-
-      else
-         idat_size = (png_uint_32)save_size;
-
-      png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size);
-
-      png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size);
-
-      png_ptr->idat_size -= idat_size;
-      png_ptr->buffer_size -= save_size;
-      png_ptr->save_buffer_size -= save_size;
-      png_ptr->save_buffer_ptr += save_size;
-   }
-
-   if (png_ptr->idat_size != 0 && png_ptr->current_buffer_size != 0)
-   {
-      size_t save_size = png_ptr->current_buffer_size;
-      png_uint_32 idat_size = png_ptr->idat_size;
-
-      /* We want the smaller of 'idat_size' and 'current_buffer_size', but they
-       * are of different types and we don't know which variable has the fewest
-       * bits.  Carefully select the smaller and cast it to the type of the
-       * larger - this cannot overflow.
-       */
-      if (idat_size < save_size)
-         save_size = (size_t)idat_size;
-
-      else
-         idat_size = (png_uint_32)save_size;
-
-      png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size);
-
-      png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size);
-
-      png_ptr->idat_size -= idat_size;
-      png_ptr->buffer_size -= save_size;
-      png_ptr->current_buffer_size -= save_size;
-      png_ptr->current_buffer_ptr += save_size;
-   }
-
-   if (png_ptr->idat_size == 0)
-   {
-      PNG_PUSH_SAVE_BUFFER_IF_LT(4)
-      png_crc_finish(png_ptr, 0);
-      png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
-      png_ptr->mode |= PNG_AFTER_IDAT;
-      png_ptr->zowner = 0;
-   }
-}
-
-void /* PRIVATE */
-png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer,
-    size_t buffer_length)
-{
-   /* The caller checks for a non-zero buffer length. */
-   if (!(buffer_length > 0) || buffer == NULL)
-      png_error(png_ptr, "No IDAT data (internal error)");
-
-   /* This routine must process all the data it has been given
-    * before returning, calling the row callback as required to
-    * handle the uncompressed results.
-    */
-   png_ptr->zstream.next_in = buffer;
-   /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */
-   png_ptr->zstream.avail_in = (uInt)buffer_length;
-
-   /* Keep going until the decompressed data is all processed
-    * or the stream marked as finished.
-    */
-   while (png_ptr->zstream.avail_in > 0 &&
-      (png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) == 0)
-   {
-      int ret;
-
-      /* We have data for zlib, but we must check that zlib
-       * has someplace to put the results.  It doesn't matter
-       * if we don't expect any results -- it may be the input
-       * data is just the LZ end code.
-       */
-      if (!(png_ptr->zstream.avail_out > 0))
-      {
-         /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */
-         png_ptr->zstream.avail_out = (uInt)(PNG_ROWBYTES(png_ptr->pixel_depth,
-             png_ptr->iwidth) + 1);
-
-         png_ptr->zstream.next_out = png_ptr->row_buf;
-      }
-
-      /* Using Z_SYNC_FLUSH here means that an unterminated
-       * LZ stream (a stream with a missing end code) can still
-       * be handled, otherwise (Z_NO_FLUSH) a future zlib
-       * implementation might defer output and therefore
-       * change the current behavior (see comments in inflate.c
-       * for why this doesn't happen at present with zlib 1.2.5).
-       */
-      ret = PNG_INFLATE(png_ptr, Z_SYNC_FLUSH);
-
-      /* Check for any failure before proceeding. */
-      if (ret != Z_OK && ret != Z_STREAM_END)
-      {
-         /* Terminate the decompression. */
-         png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
-         png_ptr->zowner = 0;
-
-         /* This may be a truncated stream (missing or
-          * damaged end code).  Treat that as a warning.
-          */
-         if (png_ptr->row_number >= png_ptr->num_rows ||
-             png_ptr->pass > 6)
-            png_warning(png_ptr, "Truncated compressed data in IDAT");
-
-         else
-         {
-            if (ret == Z_DATA_ERROR)
-               png_benign_error(png_ptr, "IDAT: ADLER32 checksum mismatch");
-            else
-               png_error(png_ptr, "Decompression error in IDAT");
-         }
-
-         /* Skip the check on unprocessed input */
-         return;
-      }
-
-      /* Did inflate output any data? */
-      if (png_ptr->zstream.next_out != png_ptr->row_buf)
-      {
-         /* Is this unexpected data after the last row?
-          * If it is, artificially terminate the LZ output
-          * here.
-          */
-         if (png_ptr->row_number >= png_ptr->num_rows ||
-             png_ptr->pass > 6)
-         {
-            /* Extra data. */
-            png_warning(png_ptr, "Extra compressed data in IDAT");
-            png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
-            png_ptr->zowner = 0;
-
-            /* Do no more processing; skip the unprocessed
-             * input check below.
-             */
-            return;
-         }
-
-         /* Do we have a complete row? */
-         if (png_ptr->zstream.avail_out == 0)
-            png_push_process_row(png_ptr);
-      }
-
-      /* And check for the end of the stream. */
-      if (ret == Z_STREAM_END)
-         png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
-   }
-
-   /* All the data should have been processed, if anything
-    * is left at this point we have bytes of IDAT data
-    * after the zlib end code.
-    */
-   if (png_ptr->zstream.avail_in > 0)
-      png_warning(png_ptr, "Extra compression data in IDAT");
-}
-
-void /* PRIVATE */
-png_push_process_row(png_structrp png_ptr)
-{
-   /* 1.5.6: row_info moved out of png_struct to a local here. */
-   png_row_info row_info;
-
-   row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */
-   row_info.color_type = png_ptr->color_type;
-   row_info.bit_depth = png_ptr->bit_depth;
-   row_info.channels = png_ptr->channels;
-   row_info.pixel_depth = png_ptr->pixel_depth;
-   row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);
-
-   if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE)
-   {
-      if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST)
-         png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1,
-            png_ptr->prev_row + 1, png_ptr->row_buf[0]);
-      else
-         png_error(png_ptr, "bad adaptive filter value");
-   }
-
-   /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before
-    * 1.5.6, while the buffer really is this big in current versions of libpng
-    * it may not be in the future, so this was changed just to copy the
-    * interlaced row count:
-    */
-   memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1);
-
-#ifdef PNG_READ_TRANSFORMS_SUPPORTED
-   if (png_ptr->transformations != 0)
-      png_do_read_transformations(png_ptr, &row_info);
-#endif
-
-   /* The transformed pixel depth should match the depth now in row_info. */
-   if (png_ptr->transformed_pixel_depth == 0)
-   {
-      png_ptr->transformed_pixel_depth = row_info.pixel_depth;
-      if (row_info.pixel_depth > png_ptr->maximum_pixel_depth)
-         png_error(png_ptr, "progressive row overflow");
-   }
-
-   else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth)
-      png_error(png_ptr, "internal progressive row size calculation error");
-
-
-#ifdef PNG_READ_INTERLACING_SUPPORTED
-   /* Expand interlaced rows to full size */
-   if (png_ptr->interlaced != 0 &&
-       (png_ptr->transformations & PNG_INTERLACE) != 0)
-   {
-      if (png_ptr->pass < 6)
-         png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass,
-             png_ptr->transformations);
-
-      switch (png_ptr->pass)
-      {
-         case 0:
-         {
-            int i;
-            for (i = 0; i < 8 && png_ptr->pass == 0; i++)
-            {
-               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
-               png_read_push_finish_row(png_ptr); /* Updates png_ptr->pass */
-            }
-
-            if (png_ptr->pass == 2) /* Pass 1 might be empty */
-            {
-               for (i = 0; i < 4 && png_ptr->pass == 2; i++)
-               {
-                  png_push_have_row(png_ptr, NULL);
-                  png_read_push_finish_row(png_ptr);
-               }
-            }
-
-            if (png_ptr->pass == 4 && png_ptr->height <= 4)
-            {
-               for (i = 0; i < 2 && png_ptr->pass == 4; i++)
-               {
-                  png_push_have_row(png_ptr, NULL);
-                  png_read_push_finish_row(png_ptr);
-               }
-            }
-
-            if (png_ptr->pass == 6 && png_ptr->height <= 4)
-            {
-                png_push_have_row(png_ptr, NULL);
-                png_read_push_finish_row(png_ptr);
-            }
-
-            break;
-         }
-
-         case 1:
-         {
-            int i;
-            for (i = 0; i < 8 && png_ptr->pass == 1; i++)
-            {
-               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
-               png_read_push_finish_row(png_ptr);
-            }
-
-            if (png_ptr->pass == 2) /* Skip top 4 generated rows */
-            {
-               for (i = 0; i < 4 && png_ptr->pass == 2; i++)
-               {
-                  png_push_have_row(png_ptr, NULL);
-                  png_read_push_finish_row(png_ptr);
-               }
-            }
-
-            break;
-         }
-
-         case 2:
-         {
-            int i;
-
-            for (i = 0; i < 4 && png_ptr->pass == 2; i++)
-            {
-               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
-               png_read_push_finish_row(png_ptr);
-            }
-
-            for (i = 0; i < 4 && png_ptr->pass == 2; i++)
-            {
-               png_push_have_row(png_ptr, NULL);
-               png_read_push_finish_row(png_ptr);
-            }
-
-            if (png_ptr->pass == 4) /* Pass 3 might be empty */
-            {
-               for (i = 0; i < 2 && png_ptr->pass == 4; i++)
-               {
-                  png_push_have_row(png_ptr, NULL);
-                  png_read_push_finish_row(png_ptr);
-               }
-            }
-
-            break;
-         }
-
-         case 3:
-         {
-            int i;
-
-            for (i = 0; i < 4 && png_ptr->pass == 3; i++)
-            {
-               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
-               png_read_push_finish_row(png_ptr);
-            }
-
-            if (png_ptr->pass == 4) /* Skip top two generated rows */
-            {
-               for (i = 0; i < 2 && png_ptr->pass == 4; i++)
-               {
-                  png_push_have_row(png_ptr, NULL);
-                  png_read_push_finish_row(png_ptr);
-               }
-            }
-
-            break;
-         }
-
-         case 4:
-         {
-            int i;
-
-            for (i = 0; i < 2 && png_ptr->pass == 4; i++)
-            {
-               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
-               png_read_push_finish_row(png_ptr);
-            }
-
-            for (i = 0; i < 2 && png_ptr->pass == 4; i++)
-            {
-               png_push_have_row(png_ptr, NULL);
-               png_read_push_finish_row(png_ptr);
-            }
-
-            if (png_ptr->pass == 6) /* Pass 5 might be empty */
-            {
-               png_push_have_row(png_ptr, NULL);
-               png_read_push_finish_row(png_ptr);
-            }
-
-            break;
-         }
-
-         case 5:
-         {
-            int i;
-
-            for (i = 0; i < 2 && png_ptr->pass == 5; i++)
-            {
-               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
-               png_read_push_finish_row(png_ptr);
-            }
-
-            if (png_ptr->pass == 6) /* Skip top generated row */
-            {
-               png_push_have_row(png_ptr, NULL);
-               png_read_push_finish_row(png_ptr);
-            }
-
-            break;
-         }
-
-         default:
-         case 6:
-         {
-            png_push_have_row(png_ptr, png_ptr->row_buf + 1);
-            png_read_push_finish_row(png_ptr);
-
-            if (png_ptr->pass != 6)
-               break;
-
-            png_push_have_row(png_ptr, NULL);
-            png_read_push_finish_row(png_ptr);
-         }
-      }
-   }
-   else
-#endif
-   {
-      png_push_have_row(png_ptr, png_ptr->row_buf + 1);
-      png_read_push_finish_row(png_ptr);
-   }
-}
-
-void /* PRIVATE */
-png_read_push_finish_row(png_structrp png_ptr)
-{
-#ifdef PNG_READ_INTERLACING_SUPPORTED
-   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
-
-   /* Start of interlace block */
-   static const png_byte png_pass_start[] = {0, 4, 0, 2, 0, 1, 0};
-
-   /* Offset to next interlace block */
-   static const png_byte png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};
-
-   /* Start of interlace block in the y direction */
-   static const png_byte png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1};
-
-   /* Offset to next interlace block in the y direction */
-   static const png_byte png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2};
-
-   /* Height of interlace block.  This is not currently used - if you need
-    * it, uncomment it here and in png.h
-   static const png_byte png_pass_height[] = {8, 8, 4, 4, 2, 2, 1};
-   */
-#endif
-
-   png_ptr->row_number++;
-   if (png_ptr->row_number < png_ptr->num_rows)
-      return;
-
-#ifdef PNG_READ_INTERLACING_SUPPORTED
-   if (png_ptr->interlaced != 0)
-   {
-      png_ptr->row_number = 0;
-      memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
-
-      do
-      {
-         png_ptr->pass++;
-         if ((png_ptr->pass == 1 && png_ptr->width < 5) ||
-             (png_ptr->pass == 3 && png_ptr->width < 3) ||
-             (png_ptr->pass == 5 && png_ptr->width < 2))
-            png_ptr->pass++;
-
-         if (png_ptr->pass > 7)
-            png_ptr->pass--;
-
-         if (png_ptr->pass >= 7)
-            break;
-
-         png_ptr->iwidth = (png_ptr->width +
-             png_pass_inc[png_ptr->pass] - 1 -
-             png_pass_start[png_ptr->pass]) /
-             png_pass_inc[png_ptr->pass];
-
-         if ((png_ptr->transformations & PNG_INTERLACE) != 0)
-            break;
-
-         png_ptr->num_rows = (png_ptr->height +
-             png_pass_yinc[png_ptr->pass] - 1 -
-             png_pass_ystart[png_ptr->pass]) /
-             png_pass_yinc[png_ptr->pass];
-
-      } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0);
-   }
-#endif /* READ_INTERLACING */
-}
-
-void /* PRIVATE */
-png_push_have_info(png_structrp png_ptr, png_inforp info_ptr)
-{
-   if (png_ptr->info_fn != NULL)
-      (*(png_ptr->info_fn))(png_ptr, info_ptr);
-}
-
-void /* PRIVATE */
-png_push_have_end(png_structrp png_ptr, png_inforp info_ptr)
-{
-   if (png_ptr->end_fn != NULL)
-      (*(png_ptr->end_fn))(png_ptr, info_ptr);
-}
-
-void /* PRIVATE */
-png_push_have_row(png_structrp png_ptr, png_bytep row)
-{
-   if (png_ptr->row_fn != NULL)
-      (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number,
-          (int)png_ptr->pass);
-}
-
-#ifdef PNG_READ_INTERLACING_SUPPORTED
-void PNGAPI
-png_progressive_combine_row(png_const_structrp png_ptr, png_bytep old_row,
-    png_const_bytep new_row)
-{
-   if (png_ptr == NULL)
-      return;
-
-   /* new_row is a flag here - if it is NULL then the app callback was called
-    * from an empty row (see the calls to png_struct::row_fn below), otherwise
-    * it must be png_ptr->row_buf+1
-    */
-   if (new_row != NULL)
-      png_combine_row(png_ptr, old_row, 1/*blocky display*/);
-}
-#endif /* READ_INTERLACING */
-
-void PNGAPI
-png_set_progressive_read_fn(png_structrp png_ptr, png_voidp progressive_ptr,
-    png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn,
-    png_progressive_end_ptr end_fn)
-{
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->info_fn = info_fn;
-   png_ptr->row_fn = row_fn;
-   png_ptr->end_fn = end_fn;
-
-   png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer);
-}
-
-png_voidp PNGAPI
-png_get_progressive_ptr(png_const_structrp png_ptr)
-{
-   if (png_ptr == NULL)
-      return (NULL);
-
-   return png_ptr->io_ptr;
-}
-#endif /* PROGRESSIVE_READ */
diff --git a/third_party/libpng16/pngprefix.h b/third_party/libpng16/pngprefix.h
deleted file mode 100644
index 228f42f..0000000
--- a/third_party/libpng16/pngprefix.h
+++ /dev/null
@@ -1,461 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef PNGPREFIX_H
-#define PNGPREFIX_H
-
-// The purpose of this file is to rename conflicting functions
-// when this version of libpng and chromium's version of it are
-// both simultaneously present.
-
-#define png_access_version_number PDFIUM_png_access_version_number
-#define png_app_error PDFIUM_png_app_error
-#define png_app_warning PDFIUM_png_app_warning
-#define png_ascii_from_fixed PDFIUM_png_ascii_from_fixed
-#define png_ascii_from_fp PDFIUM_png_ascii_from_fp
-#define png_benign_error PDFIUM_png_benign_error
-#define png_build_gamma_table PDFIUM_png_build_gamma_table
-#define png_build_grayscale_palette PDFIUM_png_build_grayscale_palette
-#define png_calculate_crc PDFIUM_png_calculate_crc
-#define png_calloc PDFIUM_png_calloc
-#define png_check_IHDR PDFIUM_png_check_IHDR
-#define png_check_chunk_length PDFIUM_png_check_chunk_length
-#define png_check_chunk_name PDFIUM_png_check_chunk_name
-#define png_check_fp_number PDFIUM_png_check_fp_number
-#define png_check_fp_string PDFIUM_png_check_fp_string
-#define png_check_keyword PDFIUM_png_check_keyword
-#define png_chunk_benign_error PDFIUM_png_chunk_benign_error
-#define png_chunk_error PDFIUM_png_chunk_error
-#define png_chunk_report PDFIUM_png_chunk_report
-#define png_chunk_unknown_handling PDFIUM_png_chunk_unknown_handling
-#define png_chunk_warning PDFIUM_png_chunk_warning
-#define png_colorspace_set_ICC PDFIUM_png_colorspace_set_ICC
-#define png_colorspace_set_chromaticities PDFIUM_png_colorspace_set_chromaticities
-#define png_colorspace_set_endpoints PDFIUM_png_colorspace_set_endpoints
-#define png_colorspace_set_gamma PDFIUM_png_colorspace_set_gamma
-#define png_colorspace_set_rgb_coefficients PDFIUM_png_colorspace_set_rgb_coefficients
-#define png_colorspace_set_sRGB PDFIUM_png_colorspace_set_sRGB
-#define png_colorspace_sync PDFIUM_png_colorspace_sync
-#define png_colorspace_sync_info PDFIUM_png_colorspace_sync_info
-#define png_combine_row PDFIUM_png_combine_row
-#define png_compress_IDAT PDFIUM_png_compress_IDAT
-#define png_convert_from_struct_tm PDFIUM_png_convert_from_struct_tm
-#define png_convert_from_time_t PDFIUM_png_convert_from_time_t
-#define png_convert_to_rfc1123 PDFIUM_png_convert_to_rfc1123
-#define png_convert_to_rfc1123_buffer PDFIUM_png_convert_to_rfc1123_buffer
-#define png_crc_error PDFIUM_png_crc_error
-#define png_crc_finish PDFIUM_png_crc_finish
-#define png_crc_read PDFIUM_png_crc_read
-#define png_create_info_struct PDFIUM_png_create_info_struct
-#define png_create_png_struct PDFIUM_png_create_png_struct
-#define png_create_read_struct PDFIUM_png_create_read_struct
-#define png_create_read_struct_2 PDFIUM_png_create_read_struct_2
-#define png_create_write_struct PDFIUM_png_create_write_struct
-#define png_create_write_struct_2 PDFIUM_png_create_write_struct_2
-#define png_data_freer PDFIUM_png_data_freer
-#define png_default_flush PDFIUM_png_default_flush
-#define png_default_read_data PDFIUM_png_default_read_data
-#define png_default_write_data PDFIUM_png_default_write_data
-#define png_destroy_gamma_table PDFIUM_png_destroy_gamma_table
-#define png_destroy_info_struct PDFIUM_png_destroy_info_struct
-#define png_destroy_png_struct PDFIUM_png_destroy_png_struct
-#define png_destroy_read_struct PDFIUM_png_destroy_read_struct
-#define png_destroy_write_struct PDFIUM_png_destroy_write_struct
-#define png_do_bgr PDFIUM_png_do_bgr
-#define png_do_check_palette_indexes PDFIUM_png_do_check_palette_indexes
-#define png_do_chop PDFIUM_png_do_chop
-#define png_do_compose PDFIUM_png_do_compose
-#define png_do_encode_alpha PDFIUM_png_do_encode_alpha
-#define png_do_expand PDFIUM_png_do_expand
-#define png_do_expand_16 PDFIUM_png_do_expand_16
-#define png_do_expand_palette PDFIUM_png_do_expand_palette
-#define png_do_expand_palette_neon_rgb PDFIUM_png_do_expand_palette_neon_rgb
-#define png_do_expand_palette_neon_rgba PDFIUM_png_do_expand_palette_neon_rgba
-#define png_do_gamma PDFIUM_png_do_gamma
-#define png_do_gray_to_rgb PDFIUM_png_do_gray_to_rgb
-#define png_do_invert PDFIUM_png_do_invert
-#define png_do_pack PDFIUM_png_do_pack
-#define png_do_packswap PDFIUM_png_do_packswap
-#define png_do_quantize PDFIUM_png_do_quantize
-#define png_do_read_filler PDFIUM_png_do_read_filler
-#define png_do_read_interlace PDFIUM_png_do_read_interlace
-#define png_do_read_intrapixel PDFIUM_png_do_read_intrapixel
-#define png_do_read_invert_alpha PDFIUM_png_do_read_invert_alpha
-#define png_do_read_swap_alpha PDFIUM_png_do_read_swap_alpha
-#define png_do_read_transformations PDFIUM_png_do_read_transformations
-#define png_do_rgb_to_gray PDFIUM_png_do_rgb_to_gray
-#define png_do_scale_16_to_8 PDFIUM_png_do_scale_16_to_8
-#define png_do_shift PDFIUM_png_do_shift
-#define png_do_strip_channel PDFIUM_png_do_strip_channel
-#define png_do_swap PDFIUM_png_do_swap
-#define png_do_unpack PDFIUM_png_do_unpack
-#define png_do_unshift PDFIUM_png_do_unshift
-#define png_do_write_interlace PDFIUM_png_do_write_interlace
-#define png_do_write_intrapixel PDFIUM_png_do_write_intrapixel
-#define png_do_write_invert_alpha PDFIUM_png_do_write_invert_alpha
-#define png_do_write_swap_alpha PDFIUM_png_do_write_swap_alpha
-#define png_do_write_transformations PDFIUM_png_do_write_transformations
-#define png_error PDFIUM_png_error
-#define png_fixed PDFIUM_png_fixed
-#define png_fixed_error PDFIUM_png_fixed_error
-#define png_flush PDFIUM_png_flush
-#define png_format_number PDFIUM_png_format_number
-#define png_formatted_warning PDFIUM_png_formatted_warning
-#define png_free PDFIUM_png_free
-#define png_free_buffer_list PDFIUM_png_free_buffer_list
-#define png_free_data PDFIUM_png_free_data
-#define png_free_default PDFIUM_png_free_default
-#define png_free_jmpbuf PDFIUM_png_free_jmpbuf
-#define png_gamma_16bit_correct PDFIUM_png_gamma_16bit_correct
-#define png_gamma_8bit_correct PDFIUM_png_gamma_8bit_correct
-#define png_gamma_correct PDFIUM_png_gamma_correct
-#define png_gamma_significant PDFIUM_png_gamma_significant
-#define png_get_IHDR PDFIUM_png_get_IHDR
-#define png_get_PLTE PDFIUM_png_get_PLTE
-#define png_get_bKGD PDFIUM_png_get_bKGD
-#define png_get_bit_depth PDFIUM_png_get_bit_depth
-#define png_get_cHRM PDFIUM_png_get_cHRM
-#define png_get_cHRM_XYZ PDFIUM_png_get_cHRM_XYZ
-#define png_get_cHRM_XYZ_fixed PDFIUM_png_get_cHRM_XYZ_fixed
-#define png_get_cHRM_fixed PDFIUM_png_get_cHRM_fixed
-#define png_get_channels PDFIUM_png_get_channels
-#define png_get_chunk_cache_max PDFIUM_png_get_chunk_cache_max
-#define png_get_chunk_malloc_max PDFIUM_png_get_chunk_malloc_max
-#define png_get_color_type PDFIUM_png_get_color_type
-#define png_get_compression_buffer_size PDFIUM_png_get_compression_buffer_size
-#define png_get_compression_type PDFIUM_png_get_compression_type
-#define png_get_copyright PDFIUM_png_get_copyright
-#define png_get_current_pass_number PDFIUM_png_get_current_pass_number
-#define png_get_current_row_number PDFIUM_png_get_current_row_number
-#define png_get_eXIf PDFIUM_png_get_eXIf
-#define png_get_eXIf_1 PDFIUM_png_get_eXIf_1
-#define png_get_error_ptr PDFIUM_png_get_error_ptr
-#define png_get_filter_type PDFIUM_png_get_filter_type
-#define png_get_gAMA PDFIUM_png_get_gAMA
-#define png_get_gAMA_fixed PDFIUM_png_get_gAMA_fixed
-#define png_get_hIST PDFIUM_png_get_hIST
-#define png_get_header_ver PDFIUM_png_get_header_ver
-#define png_get_header_version PDFIUM_png_get_header_version
-#define png_get_iCCP PDFIUM_png_get_iCCP
-#define png_get_image_height PDFIUM_png_get_image_height
-#define png_get_image_width PDFIUM_png_get_image_width
-#define png_get_int_32 PDFIUM_png_get_int_32
-#define png_get_interlace_type PDFIUM_png_get_interlace_type
-#define png_get_io_chunk_type PDFIUM_png_get_io_chunk_type
-#define png_get_io_ptr PDFIUM_png_get_io_ptr
-#define png_get_io_state PDFIUM_png_get_io_state
-#define png_get_libpng_ver PDFIUM_png_get_libpng_ver
-#define png_get_mem_ptr PDFIUM_png_get_mem_ptr
-#define png_get_oFFs PDFIUM_png_get_oFFs
-#define png_get_pCAL PDFIUM_png_get_pCAL
-#define png_get_pHYs PDFIUM_png_get_pHYs
-#define png_get_pHYs_dpi PDFIUM_png_get_pHYs_dpi
-#define png_get_palette_max PDFIUM_png_get_palette_max
-#define png_get_pixel_aspect_ratio PDFIUM_png_get_pixel_aspect_ratio
-#define png_get_pixel_aspect_ratio_fixed PDFIUM_png_get_pixel_aspect_ratio_fixed
-#define png_get_pixels_per_inch PDFIUM_png_get_pixels_per_inch
-#define png_get_pixels_per_meter PDFIUM_png_get_pixels_per_meter
-#define png_get_progressive_ptr PDFIUM_png_get_progressive_ptr
-#define png_get_rgb_to_gray_status PDFIUM_png_get_rgb_to_gray_status
-#define png_get_rowbytes PDFIUM_png_get_rowbytes
-#define png_get_rows PDFIUM_png_get_rows
-#define png_get_sBIT PDFIUM_png_get_sBIT
-#define png_get_sCAL PDFIUM_png_get_sCAL
-#define png_get_sCAL_fixed PDFIUM_png_get_sCAL_fixed
-#define png_get_sCAL_s PDFIUM_png_get_sCAL_s
-#define png_get_sPLT PDFIUM_png_get_sPLT
-#define png_get_sRGB PDFIUM_png_get_sRGB
-#define png_get_signature PDFIUM_png_get_signature
-#define png_get_tIME PDFIUM_png_get_tIME
-#define png_get_tRNS PDFIUM_png_get_tRNS
-#define png_get_text PDFIUM_png_get_text
-#define png_get_uint_16 PDFIUM_png_get_uint_16
-#define png_get_uint_31 PDFIUM_png_get_uint_31
-#define png_get_uint_32 PDFIUM_png_get_uint_32
-#define png_get_unknown_chunks PDFIUM_png_get_unknown_chunks
-#define png_get_user_chunk_ptr PDFIUM_png_get_user_chunk_ptr
-#define png_get_user_height_max PDFIUM_png_get_user_height_max
-#define png_get_user_transform_ptr PDFIUM_png_get_user_transform_ptr
-#define png_get_user_width_max PDFIUM_png_get_user_width_max
-#define png_get_valid PDFIUM_png_get_valid
-#define png_get_x_offset_inches PDFIUM_png_get_x_offset_inches
-#define png_get_x_offset_inches_fixed PDFIUM_png_get_x_offset_inches_fixed
-#define png_get_x_offset_microns PDFIUM_png_get_x_offset_microns
-#define png_get_x_offset_pixels PDFIUM_png_get_x_offset_pixels
-#define png_get_x_pixels_per_inch PDFIUM_png_get_x_pixels_per_inch
-#define png_get_x_pixels_per_meter PDFIUM_png_get_x_pixels_per_meter
-#define png_get_y_offset_inches PDFIUM_png_get_y_offset_inches
-#define png_get_y_offset_inches_fixed PDFIUM_png_get_y_offset_inches_fixed
-#define png_get_y_offset_microns PDFIUM_png_get_y_offset_microns
-#define png_get_y_offset_pixels PDFIUM_png_get_y_offset_pixels
-#define png_get_y_pixels_per_inch PDFIUM_png_get_y_pixels_per_inch
-#define png_get_y_pixels_per_meter PDFIUM_png_get_y_pixels_per_meter
-#define png_handle_IEND PDFIUM_png_handle_IEND
-#define png_handle_IHDR PDFIUM_png_handle_IHDR
-#define png_handle_PLTE PDFIUM_png_handle_PLTE
-#define png_handle_as_unknown PDFIUM_png_handle_as_unknown
-#define png_handle_bKGD PDFIUM_png_handle_bKGD
-#define png_handle_cHRM PDFIUM_png_handle_cHRM
-#define png_handle_gAMA PDFIUM_png_handle_gAMA
-#define png_handle_hIST PDFIUM_png_handle_hIST
-#define png_handle_iCCP PDFIUM_png_handle_iCCP
-#define png_handle_iTXt PDFIUM_png_handle_iTXt
-#define png_handle_oFFs PDFIUM_png_handle_oFFs
-#define png_handle_pCAL PDFIUM_png_handle_pCAL
-#define png_handle_pHYs PDFIUM_png_handle_pHYs
-#define png_handle_sBIT PDFIUM_png_handle_sBIT
-#define png_handle_sCAL PDFIUM_png_handle_sCAL
-#define png_handle_sPLT PDFIUM_png_handle_sPLT
-#define png_handle_sRGB PDFIUM_png_handle_sRGB
-#define png_handle_tEXt PDFIUM_png_handle_tEXt
-#define png_handle_tIME PDFIUM_png_handle_tIME
-#define png_handle_tRNS PDFIUM_png_handle_tRNS
-#define png_handle_unknown PDFIUM_png_handle_unknown
-#define png_handle_zTXt PDFIUM_png_handle_zTXt
-#define png_icc_check_header PDFIUM_png_icc_check_header
-#define png_icc_check_length PDFIUM_png_icc_check_length
-#define png_icc_check_tag_table PDFIUM_png_icc_check_tag_table
-#define png_icc_set_sRGB PDFIUM_png_icc_set_sRGB
-#define png_image_begin_read_from_file PDFIUM_png_image_begin_read_from_file
-#define png_image_begin_read_from_memory PDFIUM_png_image_begin_read_from_memory
-#define png_image_begin_read_from_stdio PDFIUM_png_image_begin_read_from_stdio
-#define png_image_error PDFIUM_png_image_error
-#define png_image_finish_read PDFIUM_png_image_finish_read
-#define png_image_free PDFIUM_png_image_free
-#define png_image_write_to_file PDFIUM_png_image_write_to_file
-#define png_image_write_to_memory PDFIUM_png_image_write_to_memory
-#define png_image_write_to_stdio PDFIUM_png_image_write_to_stdio
-#define png_info_init_3 PDFIUM_png_info_init_3
-#define png_init_filter_functions_neon PDFIUM_png_init_filter_functions_neon
-#define png_init_filter_functions_sse2 PDFIUM_png_init_filter_functions_sse2
-#define png_init_io PDFIUM_png_init_io
-#define png_init_read_transformations PDFIUM_png_init_read_transformations
-#define png_longjmp PDFIUM_png_longjmp
-#define png_malloc PDFIUM_png_malloc
-#define png_malloc_array PDFIUM_png_malloc_array
-#define png_malloc_base PDFIUM_png_malloc_base
-#define png_malloc_default PDFIUM_png_malloc_default
-#define png_malloc_warn PDFIUM_png_malloc_warn
-#define png_muldiv PDFIUM_png_muldiv
-#define png_muldiv_warn PDFIUM_png_muldiv_warn
-#define png_permit_mng_features PDFIUM_png_permit_mng_features
-#define png_process_IDAT_data PDFIUM_png_process_IDAT_data
-#define png_process_data PDFIUM_png_process_data
-#define png_process_data_pause PDFIUM_png_process_data_pause
-#define png_process_data_skip PDFIUM_png_process_data_skip
-#define png_process_some_data PDFIUM_png_process_some_data
-#define png_progressive_combine_row PDFIUM_png_progressive_combine_row
-#define png_push_check_crc PDFIUM_png_push_check_crc
-#define png_push_crc_finish PDFIUM_png_push_crc_finish
-#define png_push_crc_skip PDFIUM_png_push_crc_skip
-#define png_push_fill_buffer PDFIUM_png_push_fill_buffer
-#define png_push_handle_iTXt PDFIUM_png_push_handle_iTXt
-#define png_push_handle_tEXt PDFIUM_png_push_handle_tEXt
-#define png_push_handle_unknown PDFIUM_png_push_handle_unknown
-#define png_push_handle_zTXt PDFIUM_png_push_handle_zTXt
-#define png_push_have_end PDFIUM_png_push_have_end
-#define png_push_have_info PDFIUM_png_push_have_info
-#define png_push_have_row PDFIUM_png_push_have_row
-#define png_push_process_row PDFIUM_png_push_process_row
-#define png_push_read_IDAT PDFIUM_png_push_read_IDAT
-#define png_push_read_chunk PDFIUM_png_push_read_chunk
-#define png_push_read_end PDFIUM_png_push_read_end
-#define png_push_read_iTXt PDFIUM_png_push_read_iTXt
-#define png_push_read_sig PDFIUM_png_push_read_sig
-#define png_push_read_tEXt PDFIUM_png_push_read_tEXt
-#define png_push_read_zTXt PDFIUM_png_push_read_zTXt
-#define png_push_restore_buffer PDFIUM_png_push_restore_buffer
-#define png_push_save_buffer PDFIUM_png_push_save_buffer
-#define png_read_IDAT_data PDFIUM_png_read_IDAT_data
-#define png_read_chunk_header PDFIUM_png_read_chunk_header
-#define png_read_data PDFIUM_png_read_data
-#define png_read_end PDFIUM_png_read_end
-#define png_read_filter_row PDFIUM_png_read_filter_row
-#define png_read_filter_row_avg3_neon PDFIUM_png_read_filter_row_avg3_neon
-#define png_read_filter_row_avg3_sse2 PDFIUM_png_read_filter_row_avg3_sse2
-#define png_read_filter_row_avg4_neon PDFIUM_png_read_filter_row_avg4_neon
-#define png_read_filter_row_avg4_sse2 PDFIUM_png_read_filter_row_avg4_sse2
-#define png_read_filter_row_paeth3_neon PDFIUM_png_read_filter_row_paeth3_neon
-#define png_read_filter_row_paeth3_sse2 PDFIUM_png_read_filter_row_paeth3_sse2
-#define png_read_filter_row_paeth4_neon PDFIUM_png_read_filter_row_paeth4_neon
-#define png_read_filter_row_paeth4_sse2 PDFIUM_png_read_filter_row_paeth4_sse2
-#define png_read_filter_row_sub3_neon PDFIUM_png_read_filter_row_sub3_neon
-#define png_read_filter_row_sub3_sse2 PDFIUM_png_read_filter_row_sub3_sse2
-#define png_read_filter_row_sub4_neon PDFIUM_png_read_filter_row_sub4_neon
-#define png_read_filter_row_sub4_sse2 PDFIUM_png_read_filter_row_sub4_sse2
-#define png_read_filter_row_up_neon PDFIUM_png_read_filter_row_up_neon
-#define png_read_finish_IDAT PDFIUM_png_read_finish_IDAT
-#define png_read_finish_row PDFIUM_png_read_finish_row
-#define png_read_image PDFIUM_png_read_image
-#define png_read_info PDFIUM_png_read_info
-#define png_read_png PDFIUM_png_read_png
-#define png_read_push_finish_row PDFIUM_png_read_push_finish_row
-#define png_read_row PDFIUM_png_read_row
-#define png_read_rows PDFIUM_png_read_rows
-#define png_read_sig PDFIUM_png_read_sig
-#define png_read_start_row PDFIUM_png_read_start_row
-#define png_read_transform_info PDFIUM_png_read_transform_info
-#define png_read_update_info PDFIUM_png_read_update_info
-#define png_realloc_array PDFIUM_png_realloc_array
-#define png_reciprocal PDFIUM_png_reciprocal
-#define png_reciprocal2 PDFIUM_png_reciprocal2
-#define png_reset_crc PDFIUM_png_reset_crc
-#define png_reset_zstream PDFIUM_png_reset_zstream
-#define png_riffle_palette_rgba PDFIUM_png_riffle_palette_rgba
-#define png_sRGB_base PDFIUM_png_sRGB_base
-#define png_sRGB_delta PDFIUM_png_sRGB_delta
-#define png_sRGB_table PDFIUM_png_sRGB_table
-#define png_safe_error PDFIUM_png_safe_error
-#define png_safe_execute PDFIUM_png_safe_execute
-#define png_safe_warning PDFIUM_png_safe_warning
-#define png_safecat PDFIUM_png_safecat
-#define png_save_int_32 PDFIUM_png_save_int_32
-#define png_save_uint_16 PDFIUM_png_save_uint_16
-#define png_save_uint_32 PDFIUM_png_save_uint_32
-#define png_set_IHDR PDFIUM_png_set_IHDR
-#define png_set_PLTE PDFIUM_png_set_PLTE
-#define png_set_add_alpha PDFIUM_png_set_add_alpha
-#define png_set_alpha_mode PDFIUM_png_set_alpha_mode
-#define png_set_alpha_mode_fixed PDFIUM_png_set_alpha_mode_fixed
-#define png_set_bKGD PDFIUM_png_set_bKGD
-#define png_set_background PDFIUM_png_set_background
-#define png_set_background_fixed PDFIUM_png_set_background_fixed
-#define png_set_benign_errors PDFIUM_png_set_benign_errors
-#define png_set_bgr PDFIUM_png_set_bgr
-#define png_set_cHRM PDFIUM_png_set_cHRM
-#define png_set_cHRM_XYZ PDFIUM_png_set_cHRM_XYZ
-#define png_set_cHRM_XYZ_fixed PDFIUM_png_set_cHRM_XYZ_fixed
-#define png_set_cHRM_fixed PDFIUM_png_set_cHRM_fixed
-#define png_set_check_for_invalid_index PDFIUM_png_set_check_for_invalid_index
-#define png_set_chunk_cache_max PDFIUM_png_set_chunk_cache_max
-#define png_set_chunk_malloc_max PDFIUM_png_set_chunk_malloc_max
-#define png_set_compression_buffer_size PDFIUM_png_set_compression_buffer_size
-#define png_set_compression_level PDFIUM_png_set_compression_level
-#define png_set_compression_mem_level PDFIUM_png_set_compression_mem_level
-#define png_set_compression_method PDFIUM_png_set_compression_method
-#define png_set_compression_strategy PDFIUM_png_set_compression_strategy
-#define png_set_compression_window_bits PDFIUM_png_set_compression_window_bits
-#define png_set_crc_action PDFIUM_png_set_crc_action
-#define png_set_eXIf PDFIUM_png_set_eXIf
-#define png_set_eXIf_1 PDFIUM_png_set_eXIf_1
-#define png_set_error_fn PDFIUM_png_set_error_fn
-#define png_set_expand PDFIUM_png_set_expand
-#define png_set_expand_16 PDFIUM_png_set_expand_16
-#define png_set_expand_gray_1_2_4_to_8 PDFIUM_png_set_expand_gray_1_2_4_to_8
-#define png_set_filler PDFIUM_png_set_filler
-#define png_set_filter PDFIUM_png_set_filter
-#define png_set_filter_heuristics PDFIUM_png_set_filter_heuristics
-#define png_set_filter_heuristics_fixed PDFIUM_png_set_filter_heuristics_fixed
-#define png_set_flush PDFIUM_png_set_flush
-#define png_set_gAMA PDFIUM_png_set_gAMA
-#define png_set_gAMA_fixed PDFIUM_png_set_gAMA_fixed
-#define png_set_gamma PDFIUM_png_set_gamma
-#define png_set_gamma_fixed PDFIUM_png_set_gamma_fixed
-#define png_set_gray_to_rgb PDFIUM_png_set_gray_to_rgb
-#define png_set_hIST PDFIUM_png_set_hIST
-#define png_set_iCCP PDFIUM_png_set_iCCP
-#define png_set_interlace_handling PDFIUM_png_set_interlace_handling
-#define png_set_invalid PDFIUM_png_set_invalid
-#define png_set_invert_alpha PDFIUM_png_set_invert_alpha
-#define png_set_invert_mono PDFIUM_png_set_invert_mono
-#define png_set_keep_unknown_chunks PDFIUM_png_set_keep_unknown_chunks
-#define png_set_longjmp_fn PDFIUM_png_set_longjmp_fn
-#define png_set_mem_fn PDFIUM_png_set_mem_fn
-#define png_set_oFFs PDFIUM_png_set_oFFs
-#define png_set_option PDFIUM_png_set_option
-#define png_set_pCAL PDFIUM_png_set_pCAL
-#define png_set_pHYs PDFIUM_png_set_pHYs
-#define png_set_packing PDFIUM_png_set_packing
-#define png_set_packswap PDFIUM_png_set_packswap
-#define png_set_palette_to_rgb PDFIUM_png_set_palette_to_rgb
-#define png_set_progressive_read_fn PDFIUM_png_set_progressive_read_fn
-#define png_set_quantize PDFIUM_png_set_quantize
-#define png_set_read_fn PDFIUM_png_set_read_fn
-#define png_set_read_status_fn PDFIUM_png_set_read_status_fn
-#define png_set_read_user_chunk_fn PDFIUM_png_set_read_user_chunk_fn
-#define png_set_read_user_transform_fn PDFIUM_png_set_read_user_transform_fn
-#define png_set_rgb_to_gray PDFIUM_png_set_rgb_to_gray
-#define png_set_rgb_to_gray_fixed PDFIUM_png_set_rgb_to_gray_fixed
-#define png_set_rows PDFIUM_png_set_rows
-#define png_set_sBIT PDFIUM_png_set_sBIT
-#define png_set_sCAL PDFIUM_png_set_sCAL
-#define png_set_sCAL_fixed PDFIUM_png_set_sCAL_fixed
-#define png_set_sCAL_s PDFIUM_png_set_sCAL_s
-#define png_set_sPLT PDFIUM_png_set_sPLT
-#define png_set_sRGB PDFIUM_png_set_sRGB
-#define png_set_sRGB_gAMA_and_cHRM PDFIUM_png_set_sRGB_gAMA_and_cHRM
-#define png_set_scale_16 PDFIUM_png_set_scale_16
-#define png_set_shift PDFIUM_png_set_shift
-#define png_set_sig_bytes PDFIUM_png_set_sig_bytes
-#define png_set_strip_16 PDFIUM_png_set_strip_16
-#define png_set_strip_alpha PDFIUM_png_set_strip_alpha
-#define png_set_swap PDFIUM_png_set_swap
-#define png_set_swap_alpha PDFIUM_png_set_swap_alpha
-#define png_set_tIME PDFIUM_png_set_tIME
-#define png_set_tRNS PDFIUM_png_set_tRNS
-#define png_set_tRNS_to_alpha PDFIUM_png_set_tRNS_to_alpha
-#define png_set_text PDFIUM_png_set_text
-#define png_set_text_2 PDFIUM_png_set_text_2
-#define png_set_text_compression_level PDFIUM_png_set_text_compression_level
-#define png_set_text_compression_mem_level PDFIUM_png_set_text_compression_mem_level
-#define png_set_text_compression_method PDFIUM_png_set_text_compression_method
-#define png_set_text_compression_strategy PDFIUM_png_set_text_compression_strategy
-#define png_set_text_compression_window_bits PDFIUM_png_set_text_compression_window_bits
-#define png_set_unknown_chunk_location PDFIUM_png_set_unknown_chunk_location
-#define png_set_unknown_chunks PDFIUM_png_set_unknown_chunks
-#define png_set_user_limits PDFIUM_png_set_user_limits
-#define png_set_user_transform_info PDFIUM_png_set_user_transform_info
-#define png_set_write_fn PDFIUM_png_set_write_fn
-#define png_set_write_status_fn PDFIUM_png_set_write_status_fn
-#define png_set_write_user_transform_fn PDFIUM_png_set_write_user_transform_fn
-#define png_sig_cmp PDFIUM_png_sig_cmp
-#define png_start_read_image PDFIUM_png_start_read_image
-#define png_user_version_check PDFIUM_png_user_version_check
-#define png_warning PDFIUM_png_warning
-#define png_warning_parameter PDFIUM_png_warning_parameter
-#define png_warning_parameter_signed PDFIUM_png_warning_parameter_signed
-#define png_warning_parameter_unsigned PDFIUM_png_warning_parameter_unsigned
-#define png_write_IEND PDFIUM_png_write_IEND
-#define png_write_IHDR PDFIUM_png_write_IHDR
-#define png_write_PLTE PDFIUM_png_write_PLTE
-#define png_write_bKGD PDFIUM_png_write_bKGD
-#define png_write_cHRM_fixed PDFIUM_png_write_cHRM_fixed
-#define png_write_chunk PDFIUM_png_write_chunk
-#define png_write_chunk_data PDFIUM_png_write_chunk_data
-#define png_write_chunk_end PDFIUM_png_write_chunk_end
-#define png_write_chunk_start PDFIUM_png_write_chunk_start
-#define png_write_data PDFIUM_png_write_data
-#define png_write_end PDFIUM_png_write_end
-#define png_write_find_filter PDFIUM_png_write_find_filter
-#define png_write_finish_row PDFIUM_png_write_finish_row
-#define png_write_flush PDFIUM_png_write_flush
-#define png_write_gAMA_fixed PDFIUM_png_write_gAMA_fixed
-#define png_write_hIST PDFIUM_png_write_hIST
-#define png_write_iCCP PDFIUM_png_write_iCCP
-#define png_write_iTXt PDFIUM_png_write_iTXt
-#define png_write_image PDFIUM_png_write_image
-#define png_write_info PDFIUM_png_write_info
-#define png_write_info_before_PLTE PDFIUM_png_write_info_before_PLTE
-#define png_write_oFFs PDFIUM_png_write_oFFs
-#define png_write_pCAL PDFIUM_png_write_pCAL
-#define png_write_pHYs PDFIUM_png_write_pHYs
-#define png_write_png PDFIUM_png_write_png
-#define png_write_row PDFIUM_png_write_row
-#define png_write_rows PDFIUM_png_write_rows
-#define png_write_sBIT PDFIUM_png_write_sBIT
-#define png_write_sCAL_s PDFIUM_png_write_sCAL_s
-#define png_write_sPLT PDFIUM_png_write_sPLT
-#define png_write_sRGB PDFIUM_png_write_sRGB
-#define png_write_sig PDFIUM_png_write_sig
-#define png_write_start_row PDFIUM_png_write_start_row
-#define png_write_tEXt PDFIUM_png_write_tEXt
-#define png_write_tIME PDFIUM_png_write_tIME
-#define png_write_tRNS PDFIUM_png_write_tRNS
-#define png_write_zTXt PDFIUM_png_write_zTXt
-#define png_zalloc PDFIUM_png_zalloc
-#define png_zfree PDFIUM_png_zfree
-#define png_zlib_inflate PDFIUM_png_zlib_inflate
-#define png_zstream_error PDFIUM_png_zstream_error
-
-#endif  // PNGPREFIX_H
diff --git a/third_party/libpng16/pngpriv.h b/third_party/libpng16/pngpriv.h
deleted file mode 100644
index 583c26f..0000000
--- a/third_party/libpng16/pngpriv.h
+++ /dev/null
@@ -1,2152 +0,0 @@
-
-/* pngpriv.h - private declarations for use inside libpng
- *
- * Copyright (c) 2018-2019 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
-/* The symbols declared in this file (including the functions declared
- * as extern) are PRIVATE.  They are not part of the libpng public
- * interface, and are not recommended for use by regular applications.
- * Some of them may become public in the future; others may stay private,
- * change in an incompatible way, or even disappear.
- * Although the libpng users are not forbidden to include this header,
- * they should be well aware of the issues that may arise from doing so.
- */
-
-#ifndef PNGPRIV_H
-#define PNGPRIV_H
-
-/* Feature Test Macros.  The following are defined here to ensure that correctly
- * implemented libraries reveal the APIs libpng needs to build and hide those
- * that are not needed and potentially damaging to the compilation.
- *
- * Feature Test Macros must be defined before any system header is included (see
- * POSIX 1003.1 2.8.2 "POSIX Symbols."
- *
- * These macros only have an effect if the operating system supports either
- * POSIX 1003.1 or C99, or both.  On other operating systems (particularly
- * Windows/Visual Studio) there is no effect; the OS specific tests below are
- * still required (as of 2011-05-02.)
- */
-#ifndef _POSIX_SOURCE
-# define _POSIX_SOURCE 1 /* Just the POSIX 1003.1 and C89 APIs */
-#endif
-
-#ifndef PNG_VERSION_INFO_ONLY
-/* Standard library headers not required by png.h: */
-#  include <stdlib.h>
-#  include <string.h>
-#endif
-
-#define PNGLIB_BUILD /*libpng is being built, not used*/
-
-/* If HAVE_CONFIG_H is defined during the build then the build system must
- * provide an appropriate "config.h" file on the include path.  The header file
- * must provide definitions as required below (search for "HAVE_CONFIG_H");
- * see configure.ac for more details of the requirements.  The macro
- * "PNG_NO_CONFIG_H" is provided for maintainers to test for dependencies on
- * 'configure'; define this macro to prevent the configure build including the
- * configure generated config.h.  Libpng is expected to compile without *any*
- * special build system support on a reasonably ANSI-C compliant system.
- */
-#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
-#  include <config.h>
-
-   /* Pick up the definition of 'restrict' from config.h if it was read: */
-#  define PNG_RESTRICT restrict
-#endif
-
-/* To support symbol prefixing it is necessary to know *before* including png.h
- * whether the fixed point (and maybe other) APIs are exported, because if they
- * are not internal definitions may be required.  This is handled below just
- * before png.h is included, but load the configuration now if it is available.
- */
-#ifndef PNGLCONF_H
-#  include "pnglibconf.h"
-#endif
-
-/* Local renames may change non-exported API functions from png.h */
-#if defined(PNG_PREFIX) && !defined(PNGPREFIX_H)
-#  include "pngprefix.h"
-#endif
-
-#ifdef PNG_USER_CONFIG
-#  include "pngusr.h"
-   /* These should have been defined in pngusr.h */
-#  ifndef PNG_USER_PRIVATEBUILD
-#    define PNG_USER_PRIVATEBUILD "Custom libpng build"
-#  endif
-#  ifndef PNG_USER_DLLFNAME_POSTFIX
-#    define PNG_USER_DLLFNAME_POSTFIX "Cb"
-#  endif
-#endif
-
-/* Compile time options.
- * =====================
- * In a multi-arch build the compiler may compile the code several times for the
- * same object module, producing different binaries for different architectures.
- * When this happens configure-time setting of the target host options cannot be
- * done and this interferes with the handling of the ARM NEON optimizations, and
- * possibly other similar optimizations.  Put additional tests here; in general
- * this is needed when the same option can be changed at both compile time and
- * run time depending on the target OS (i.e. iOS vs Android.)
- *
- * NOTE: symbol prefixing does not pass $(CFLAGS) to the preprocessor, because
- * this is not possible with certain compilers (Oracle SUN OS CC), as a result
- * it is necessary to ensure that all extern functions that *might* be used
- * regardless of $(CFLAGS) get declared in this file.  The test on __ARM_NEON__
- * below is one example of this behavior because it is controlled by the
- * presence or not of -mfpu=neon on the GCC command line, it is possible to do
- * this in $(CC), e.g. "CC=gcc -mfpu=neon", but people who build libpng rarely
- * do this.
- */
-#ifndef PNG_ARM_NEON_OPT
-   /* ARM NEON optimizations are being controlled by the compiler settings,
-    * typically the target FPU.  If the FPU has been set to NEON (-mfpu=neon
-    * with GCC) then the compiler will define __ARM_NEON__ and we can rely
-    * unconditionally on NEON instructions not crashing, otherwise we must
-    * disable use of NEON instructions.
-    *
-    * NOTE: at present these optimizations depend on 'ALIGNED_MEMORY', so they
-    * can only be turned on automatically if that is supported too.  If
-    * PNG_ARM_NEON_OPT is set in CPPFLAGS (to >0) then arm/arm_init.c will fail
-    * to compile with an appropriate #error if ALIGNED_MEMORY has been turned
-    * off.
-    *
-    * Note that gcc-4.9 defines __ARM_NEON instead of the deprecated
-    * __ARM_NEON__, so we check both variants.
-    *
-    * To disable ARM_NEON optimizations entirely, and skip compiling the
-    * associated assembler code, pass --enable-arm-neon=no to configure
-    * or put -DPNG_ARM_NEON_OPT=0 in CPPFLAGS.
-    */
-#  if (defined(__ARM_NEON__) || defined(__ARM_NEON)) && \
-   defined(PNG_ALIGNED_MEMORY_SUPPORTED)
-#     define PNG_ARM_NEON_OPT 2
-#  else
-#     define PNG_ARM_NEON_OPT 0
-#  endif
-#endif
-
-#if PNG_ARM_NEON_OPT > 0
-   /* NEON optimizations are to be at least considered by libpng, so enable the
-    * callbacks to do this.
-    */
-#  define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_neon
-
-   /* By default the 'intrinsics' code in arm/filter_neon_intrinsics.c is used
-    * if possible - if __ARM_NEON__ is set and the compiler version is not known
-    * to be broken.  This is controlled by PNG_ARM_NEON_IMPLEMENTATION which can
-    * be:
-    *
-    *    1  The intrinsics code (the default with __ARM_NEON__)
-    *    2  The hand coded assembler (the default without __ARM_NEON__)
-    *
-    * It is possible to set PNG_ARM_NEON_IMPLEMENTATION in CPPFLAGS, however
-    * this is *NOT* supported and may cease to work even after a minor revision
-    * to libpng.  It *is* valid to do this for testing purposes, e.g. speed
-    * testing or a new compiler, but the results should be communicated to the
-    * libpng implementation list for incorporation in the next minor release.
-    */
-#  ifndef PNG_ARM_NEON_IMPLEMENTATION
-#     if defined(__ARM_NEON__) || defined(__ARM_NEON)
-#        if defined(__clang__)
-            /* At present it is unknown by the libpng developers which versions
-             * of clang support the intrinsics, however some or perhaps all
-             * versions do not work with the assembler so this may be
-             * irrelevant, so just use the default (do nothing here.)
-             */
-#        elif defined(__GNUC__)
-            /* GCC 4.5.4 NEON support is known to be broken.  4.6.3 is known to
-             * work, so if this *is* GCC, or G++, look for a version >4.5
-             */
-#           if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)
-#              define PNG_ARM_NEON_IMPLEMENTATION 2
-#           endif /* no GNUC support */
-#        endif /* __GNUC__ */
-#     else /* !defined __ARM_NEON__ */
-         /* The 'intrinsics' code simply won't compile without this -mfpu=neon:
-          */
-#        if !defined(__aarch64__)
-            /* The assembler code currently does not work on ARM64 */
-#          define PNG_ARM_NEON_IMPLEMENTATION 2
-#        endif /* __aarch64__ */
-#     endif /* __ARM_NEON__ */
-#  endif /* !PNG_ARM_NEON_IMPLEMENTATION */
-
-#  ifndef PNG_ARM_NEON_IMPLEMENTATION
-      /* Use the intrinsics code by default. */
-#     define PNG_ARM_NEON_IMPLEMENTATION 1
-#  endif
-#endif /* PNG_ARM_NEON_OPT > 0 */
-
-#ifndef PNG_MIPS_MSA_OPT
-#  if defined(__mips_msa) && (__mips_isa_rev >= 5) && defined(PNG_ALIGNED_MEMORY_SUPPORTED)
-#     define PNG_MIPS_MSA_OPT 2
-#  else
-#     define PNG_MIPS_MSA_OPT 0
-#  endif
-#endif
-
-#ifndef PNG_POWERPC_VSX_OPT
-#  if defined(__PPC64__) && defined(__ALTIVEC__) && defined(__VSX__)
-#     define PNG_POWERPC_VSX_OPT 2
-#  else
-#     define PNG_POWERPC_VSX_OPT 0
-#  endif
-#endif
-
-#ifndef PNG_INTEL_SSE_OPT
-#   ifdef PNG_INTEL_SSE
-      /* Only check for SSE if the build configuration has been modified to
-       * enable SSE optimizations.  This means that these optimizations will
-       * be off by default.  See contrib/intel for more details.
-       */
-#     if defined(__SSE4_1__) || defined(__AVX__) || defined(__SSSE3__) || \
-       defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) || \
-       (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
-#         define PNG_INTEL_SSE_OPT 1
-#      else
-#         define PNG_INTEL_SSE_OPT 0
-#      endif
-#   else
-#      define PNG_INTEL_SSE_OPT 0
-#   endif
-#endif
-
-#if PNG_INTEL_SSE_OPT > 0
-#   ifndef PNG_INTEL_SSE_IMPLEMENTATION
-#      if defined(__SSE4_1__) || defined(__AVX__)
-          /* We are not actually using AVX, but checking for AVX is the best
-             way we can detect SSE4.1 and SSSE3 on MSVC.
-          */
-#         define PNG_INTEL_SSE_IMPLEMENTATION 3
-#      elif defined(__SSSE3__)
-#         define PNG_INTEL_SSE_IMPLEMENTATION 2
-#      elif defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) || \
-       (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
-#         define PNG_INTEL_SSE_IMPLEMENTATION 1
-#      else
-#         define PNG_INTEL_SSE_IMPLEMENTATION 0
-#      endif
-#   endif
-
-#   if PNG_INTEL_SSE_IMPLEMENTATION > 0
-#      define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_sse2
-#   endif
-#else
-#   define PNG_INTEL_SSE_IMPLEMENTATION 0
-#endif
-
-#if PNG_MIPS_MSA_OPT > 0
-#  define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_msa
-#  ifndef PNG_MIPS_MSA_IMPLEMENTATION
-#     if defined(__mips_msa)
-#        if defined(__clang__)
-#        elif defined(__GNUC__)
-#           if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7)
-#              define PNG_MIPS_MSA_IMPLEMENTATION 2
-#           endif /* no GNUC support */
-#        endif /* __GNUC__ */
-#     else /* !defined __mips_msa */
-#        define PNG_MIPS_MSA_IMPLEMENTATION 2
-#     endif /* __mips_msa */
-#  endif /* !PNG_MIPS_MSA_IMPLEMENTATION */
-
-#  ifndef PNG_MIPS_MSA_IMPLEMENTATION
-#     define PNG_MIPS_MSA_IMPLEMENTATION 1
-#  endif
-#endif /* PNG_MIPS_MSA_OPT > 0 */
-
-#if PNG_POWERPC_VSX_OPT > 0
-#  define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_vsx
-#  define PNG_POWERPC_VSX_IMPLEMENTATION 1
-#endif
-
-
-/* Is this a build of a DLL where compilation of the object modules requires
- * different preprocessor settings to those required for a simple library?  If
- * so PNG_BUILD_DLL must be set.
- *
- * If libpng is used inside a DLL but that DLL does not export the libpng APIs
- * PNG_BUILD_DLL must not be set.  To avoid the code below kicking in build a
- * static library of libpng then link the DLL against that.
- */
-#ifndef PNG_BUILD_DLL
-#  ifdef DLL_EXPORT
-      /* This is set by libtool when files are compiled for a DLL; libtool
-       * always compiles twice, even on systems where it isn't necessary.  Set
-       * PNG_BUILD_DLL in case it is necessary:
-       */
-#     define PNG_BUILD_DLL
-#  else
-#     ifdef _WINDLL
-         /* This is set by the Microsoft Visual Studio IDE in projects that
-          * build a DLL.  It can't easily be removed from those projects (it
-          * isn't visible in the Visual Studio UI) so it is a fairly reliable
-          * indication that PNG_IMPEXP needs to be set to the DLL export
-          * attributes.
-          */
-#        define PNG_BUILD_DLL
-#     else
-#        ifdef __DLL__
-            /* This is set by the Borland C system when compiling for a DLL
-             * (as above.)
-             */
-#           define PNG_BUILD_DLL
-#        else
-            /* Add additional compiler cases here. */
-#        endif
-#     endif
-#  endif
-#endif /* Setting PNG_BUILD_DLL if required */
-
-/* See pngconf.h for more details: the builder of the library may set this on
- * the command line to the right thing for the specific compilation system or it
- * may be automagically set above (at present we know of no system where it does
- * need to be set on the command line.)
- *
- * PNG_IMPEXP must be set here when building the library to prevent pngconf.h
- * setting it to the "import" setting for a DLL build.
- */
-#ifndef PNG_IMPEXP
-#  ifdef PNG_BUILD_DLL
-#     define PNG_IMPEXP PNG_DLL_EXPORT
-#  else
-      /* Not building a DLL, or the DLL doesn't require specific export
-       * definitions.
-       */
-#     define PNG_IMPEXP
-#  endif
-#endif
-
-/* No warnings for private or deprecated functions in the build: */
-#ifndef PNG_DEPRECATED
-#  define PNG_DEPRECATED
-#endif
-#ifndef PNG_PRIVATE
-#  define PNG_PRIVATE
-#endif
-
-/* Symbol preprocessing support.
- *
- * To enable listing global, but internal, symbols the following macros should
- * always be used to declare an extern data or function object in this file.
- */
-#ifndef PNG_INTERNAL_DATA
-#  define PNG_INTERNAL_DATA(type, name, array) PNG_LINKAGE_DATA type name array
-#endif
-
-#ifndef PNG_INTERNAL_FUNCTION
-#  define PNG_INTERNAL_FUNCTION(type, name, args, attributes)\
-      PNG_LINKAGE_FUNCTION PNG_FUNCTION(type, name, args, PNG_EMPTY attributes)
-#endif
-
-#ifndef PNG_INTERNAL_CALLBACK
-#  define PNG_INTERNAL_CALLBACK(type, name, args, attributes)\
-      PNG_LINKAGE_CALLBACK PNG_FUNCTION(type, (PNGCBAPI name), args,\
-         PNG_EMPTY attributes)
-#endif
-
-/* If floating or fixed point APIs are disabled they may still be compiled
- * internally.  To handle this make sure they are declared as the appropriate
- * internal extern function (otherwise the symbol prefixing stuff won't work and
- * the functions will be used without definitions.)
- *
- * NOTE: although all the API functions are declared here they are not all
- * actually built!  Because the declarations are still made it is necessary to
- * fake out types that they depend on.
- */
-#ifndef PNG_FP_EXPORT
-#  ifndef PNG_FLOATING_POINT_SUPPORTED
-#     define PNG_FP_EXPORT(ordinal, type, name, args)\
-         PNG_INTERNAL_FUNCTION(type, name, args, PNG_EMPTY);
-#     ifndef PNG_VERSION_INFO_ONLY
-         typedef struct png_incomplete png_double;
-         typedef png_double*           png_doublep;
-         typedef const png_double*     png_const_doublep;
-         typedef png_double**          png_doublepp;
-#     endif
-#  endif
-#endif
-#ifndef PNG_FIXED_EXPORT
-#  ifndef PNG_FIXED_POINT_SUPPORTED
-#     define PNG_FIXED_EXPORT(ordinal, type, name, args)\
-         PNG_INTERNAL_FUNCTION(type, name, args, PNG_EMPTY);
-#  endif
-#endif
-
-#include "png.h"
-
-/* pngconf.h does not set PNG_DLL_EXPORT unless it is required, so: */
-#ifndef PNG_DLL_EXPORT
-#  define PNG_DLL_EXPORT
-#endif
-
-/* This is a global switch to set the compilation for an installed system
- * (a release build).  It can be set for testing debug builds to ensure that
- * they will compile when the build type is switched to RC or STABLE, the
- * default is just to use PNG_LIBPNG_BUILD_BASE_TYPE.  Set this in CPPFLAGS
- * with either:
- *
- *   -DPNG_RELEASE_BUILD Turns on the release compile path
- *   -DPNG_RELEASE_BUILD=0 Turns it off
- * or in your pngusr.h with
- *   #define PNG_RELEASE_BUILD=1 Turns on the release compile path
- *   #define PNG_RELEASE_BUILD=0 Turns it off
- */
-#ifndef PNG_RELEASE_BUILD
-#  define PNG_RELEASE_BUILD (PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC)
-#endif
-
-/* SECURITY and SAFETY:
- *
- * libpng is built with support for internal limits on image dimensions and
- * memory usage.  These are documented in scripts/pnglibconf.dfa of the
- * source and recorded in the machine generated header file pnglibconf.h.
- */
-
-/* If you are running on a machine where you cannot allocate more
- * than 64K of memory at once, uncomment this.  While libpng will not
- * normally need that much memory in a chunk (unless you load up a very
- * large file), zlib needs to know how big of a chunk it can use, and
- * libpng thus makes sure to check any memory allocation to verify it
- * will fit into memory.
- *
- * zlib provides 'MAXSEG_64K' which, if defined, indicates the
- * same limit and pngconf.h (already included) sets the limit
- * if certain operating systems are detected.
- */
-#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K)
-#  define PNG_MAX_MALLOC_64K
-#endif
-
-#ifndef PNG_UNUSED
-/* Unused formal parameter warnings are silenced using the following macro
- * which is expected to have no bad effects on performance (optimizing
- * compilers will probably remove it entirely).  Note that if you replace
- * it with something other than whitespace, you must include the terminating
- * semicolon.
- */
-#  define PNG_UNUSED(param) (void)param;
-#endif
-
-/* Just a little check that someone hasn't tried to define something
- * contradictory.
- */
-#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K)
-#  undef PNG_ZBUF_SIZE
-#  define PNG_ZBUF_SIZE 65536L
-#endif
-
-/* If warnings or errors are turned off the code is disabled or redirected here.
- * From 1.5.4 functions have been added to allow very limited formatting of
- * error and warning messages - this code will also be disabled here.
- */
-#ifdef PNG_WARNINGS_SUPPORTED
-#  define PNG_WARNING_PARAMETERS(p) png_warning_parameters p;
-#else
-#  define png_warning_parameter(p,number,string) ((void)0)
-#  define png_warning_parameter_unsigned(p,number,format,value) ((void)0)
-#  define png_warning_parameter_signed(p,number,format,value) ((void)0)
-#  define png_formatted_warning(pp,p,message) ((void)(pp))
-#  define PNG_WARNING_PARAMETERS(p)
-#endif
-#ifndef PNG_ERROR_TEXT_SUPPORTED
-#  define png_fixed_error(s1,s2) png_err(s1)
-#endif
-
-/* Some fixed point APIs are still required even if not exported because
- * they get used by the corresponding floating point APIs.  This magic
- * deals with this:
- */
-#ifdef PNG_FIXED_POINT_SUPPORTED
-#  define PNGFAPI PNGAPI
-#else
-#  define PNGFAPI /* PRIVATE */
-#endif
-
-#ifndef PNG_VERSION_INFO_ONLY
-/* Other defines specific to compilers can go here.  Try to keep
- * them inside an appropriate ifdef/endif pair for portability.
- */
-
-/* C allows up-casts from (void*) to any pointer and (const void*) to any
- * pointer to a const object.  C++ regards this as a type error and requires an
- * explicit, static, cast and provides the static_cast<> rune to ensure that
- * const is not cast away.
- */
-#ifdef __cplusplus
-#  define png_voidcast(type, value) static_cast<type>(value)
-#  define png_constcast(type, value) const_cast<type>(value)
-#  define png_aligncast(type, value) \
-   static_cast<type>(static_cast<void*>(value))
-#  define png_aligncastconst(type, value) \
-   static_cast<type>(static_cast<const void*>(value))
-#else
-#  define png_voidcast(type, value) (value)
-#  ifdef _WIN64
-#     ifdef __GNUC__
-         typedef unsigned long long png_ptruint;
-#     else
-         typedef unsigned __int64 png_ptruint;
-#     endif
-#  else
-      typedef unsigned long png_ptruint;
-#  endif
-#  define png_constcast(type, value) ((type)(png_ptruint)(const void*)(value))
-#  define png_aligncast(type, value) ((void*)(value))
-#  define png_aligncastconst(type, value) ((const void*)(value))
-#endif /* __cplusplus */
-
-#if defined(PNG_FLOATING_POINT_SUPPORTED) ||\
-    defined(PNG_FLOATING_ARITHMETIC_SUPPORTED)
-   /* png.c requires the following ANSI-C constants if the conversion of
-    * floating point to ASCII is implemented therein:
-    *
-    *  DBL_DIG  Maximum number of decimal digits (can be set to any constant)
-    *  DBL_MIN  Smallest normalized fp number (can be set to an arbitrary value)
-    *  DBL_MAX  Maximum floating point number (can be set to an arbitrary value)
-    */
-#  include <float.h>
-
-#  if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \
-    defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC)
-   /* We need to check that <math.h> hasn't already been included earlier
-    * as it seems it doesn't agree with <fp.h>, yet we should really use
-    * <fp.h> if possible.
-    */
-#    if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__)
-#      include <fp.h>
-#    endif
-#  else
-#    include <math.h>
-#  endif
-#  if defined(_AMIGA) && defined(__SASC) && defined(_M68881)
-   /* Amiga SAS/C: We must include builtin FPU functions when compiling using
-    * MATH=68881
-    */
-#    include <m68881.h>
-#  endif
-#endif
-
-/* This provides the non-ANSI (far) memory allocation routines. */
-#if defined(__TURBOC__) && defined(__MSDOS__)
-#  include <mem.h>
-#  include <alloc.h>
-#endif
-
-#if defined(WIN32) || defined(_Windows) || defined(_WINDOWS) || \
-    defined(_WIN32) || defined(__WIN32__)
-#  include <windows.h>  /* defines _WINDOWS_ macro */
-#endif
-#endif /* PNG_VERSION_INFO_ONLY */
-
-/* Moved here around 1.5.0beta36 from pngconf.h */
-/* Users may want to use these so they are not private.  Any library
- * functions that are passed far data must be model-independent.
- */
-
-/* Memory model/platform independent fns */
-#ifndef PNG_ABORT
-#  ifdef _WINDOWS_
-#    define PNG_ABORT() ExitProcess(0)
-#  else
-#    define PNG_ABORT() abort()
-#  endif
-#endif
-
-/* These macros may need to be architecture dependent. */
-#define PNG_ALIGN_NONE   0 /* do not use data alignment */
-#define PNG_ALIGN_ALWAYS 1 /* assume unaligned accesses are OK */
-#ifdef offsetof
-#  define PNG_ALIGN_OFFSET 2 /* use offsetof to determine alignment */
-#else
-#  define PNG_ALIGN_OFFSET -1 /* prevent the use of this */
-#endif
-#define PNG_ALIGN_SIZE   3 /* use sizeof to determine alignment */
-
-#ifndef PNG_ALIGN_TYPE
-   /* Default to using aligned access optimizations and requiring alignment to a
-    * multiple of the data type size.  Override in a compiler specific fashion
-    * if necessary by inserting tests here:
-    */
-#  define PNG_ALIGN_TYPE PNG_ALIGN_SIZE
-#endif
-
-#if PNG_ALIGN_TYPE == PNG_ALIGN_SIZE
-   /* This is used because in some compiler implementations non-aligned
-    * structure members are supported, so the offsetof approach below fails.
-    * Set PNG_ALIGN_SIZE=0 for compiler combinations where unaligned access
-    * is good for performance.  Do not do this unless you have tested the result
-    * and understand it.
-    */
-#  define png_alignof(type) (sizeof (type))
-#else
-#  if PNG_ALIGN_TYPE == PNG_ALIGN_OFFSET
-#     define png_alignof(type) offsetof(struct{char c; type t;}, t)
-#  else
-#     if PNG_ALIGN_TYPE == PNG_ALIGN_ALWAYS
-#        define png_alignof(type) (1)
-#     endif
-      /* Else leave png_alignof undefined to prevent use thereof */
-#  endif
-#endif
-
-/* This implicitly assumes alignment is always to a power of 2. */
-#ifdef png_alignof
-#  define png_isaligned(ptr, type)\
-   (((type)((const char*)ptr-(const char*)0) & \
-   (type)(png_alignof(type)-1)) == 0)
-#else
-#  define png_isaligned(ptr, type) 0
-#endif
-
-/* End of memory model/platform independent support */
-/* End of 1.5.0beta36 move from pngconf.h */
-
-/* CONSTANTS and UTILITY MACROS
- * These are used internally by libpng and not exposed in the API
- */
-
-/* Various modes of operation.  Note that after an init, mode is set to
- * zero automatically when the structure is created.  Three of these
- * are defined in png.h because they need to be visible to applications
- * that call png_set_unknown_chunk().
- */
-/* #define PNG_HAVE_IHDR            0x01U (defined in png.h) */
-/* #define PNG_HAVE_PLTE            0x02U (defined in png.h) */
-#define PNG_HAVE_IDAT               0x04U
-/* #define PNG_AFTER_IDAT           0x08U (defined in png.h) */
-#define PNG_HAVE_IEND               0x10U
-                   /*               0x20U (unused) */
-                   /*               0x40U (unused) */
-                   /*               0x80U (unused) */
-#define PNG_HAVE_CHUNK_HEADER      0x100U
-#define PNG_WROTE_tIME             0x200U
-#define PNG_WROTE_INFO_BEFORE_PLTE 0x400U
-#define PNG_BACKGROUND_IS_GRAY     0x800U
-#define PNG_HAVE_PNG_SIGNATURE    0x1000U
-#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000U /* Have another chunk after IDAT */
-                   /*             0x4000U (unused) */
-#define PNG_IS_READ_STRUCT        0x8000U /* Else is a write struct */
-
-/* Flags for the transformations the PNG library does on the image data */
-#define PNG_BGR                 0x0001U
-#define PNG_INTERLACE           0x0002U
-#define PNG_PACK                0x0004U
-#define PNG_SHIFT               0x0008U
-#define PNG_SWAP_BYTES          0x0010U
-#define PNG_INVERT_MONO         0x0020U
-#define PNG_QUANTIZE            0x0040U
-#define PNG_COMPOSE             0x0080U    /* Was PNG_BACKGROUND */
-#define PNG_BACKGROUND_EXPAND   0x0100U
-#define PNG_EXPAND_16           0x0200U    /* Added to libpng 1.5.2 */
-#define PNG_16_TO_8             0x0400U    /* Becomes 'chop' in 1.5.4 */
-#define PNG_RGBA                0x0800U
-#define PNG_EXPAND              0x1000U
-#define PNG_GAMMA               0x2000U
-#define PNG_GRAY_TO_RGB         0x4000U
-#define PNG_FILLER              0x8000U
-#define PNG_PACKSWAP           0x10000U
-#define PNG_SWAP_ALPHA         0x20000U
-#define PNG_STRIP_ALPHA        0x40000U
-#define PNG_INVERT_ALPHA       0x80000U
-#define PNG_USER_TRANSFORM    0x100000U
-#define PNG_RGB_TO_GRAY_ERR   0x200000U
-#define PNG_RGB_TO_GRAY_WARN  0x400000U
-#define PNG_RGB_TO_GRAY       0x600000U /* two bits, RGB_TO_GRAY_ERR|WARN */
-#define PNG_ENCODE_ALPHA      0x800000U /* Added to libpng-1.5.4 */
-#define PNG_ADD_ALPHA        0x1000000U /* Added to libpng-1.2.7 */
-#define PNG_EXPAND_tRNS      0x2000000U /* Added to libpng-1.2.9 */
-#define PNG_SCALE_16_TO_8    0x4000000U /* Added to libpng-1.5.4 */
-                       /*    0x8000000U unused */
-                       /*   0x10000000U unused */
-                       /*   0x20000000U unused */
-                       /*   0x40000000U unused */
-/* Flags for png_create_struct */
-#define PNG_STRUCT_PNG   0x0001U
-#define PNG_STRUCT_INFO  0x0002U
-
-/* Flags for the png_ptr->flags rather than declaring a byte for each one */
-#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY     0x0001U
-#define PNG_FLAG_ZSTREAM_INITIALIZED      0x0002U /* Added to libpng-1.6.0 */
-                                  /*      0x0004U    unused */
-#define PNG_FLAG_ZSTREAM_ENDED            0x0008U /* Added to libpng-1.6.0 */
-                                  /*      0x0010U    unused */
-                                  /*      0x0020U    unused */
-#define PNG_FLAG_ROW_INIT                 0x0040U
-#define PNG_FLAG_FILLER_AFTER             0x0080U
-#define PNG_FLAG_CRC_ANCILLARY_USE        0x0100U
-#define PNG_FLAG_CRC_ANCILLARY_NOWARN     0x0200U
-#define PNG_FLAG_CRC_CRITICAL_USE         0x0400U
-#define PNG_FLAG_CRC_CRITICAL_IGNORE      0x0800U
-#define PNG_FLAG_ASSUME_sRGB              0x1000U /* Added to libpng-1.5.4 */
-#define PNG_FLAG_OPTIMIZE_ALPHA           0x2000U /* Added to libpng-1.5.4 */
-#define PNG_FLAG_DETECT_UNINITIALIZED     0x4000U /* Added to libpng-1.5.4 */
-/* #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS      0x8000U */
-/* #define PNG_FLAG_KEEP_UNSAFE_CHUNKS      0x10000U */
-#define PNG_FLAG_LIBRARY_MISMATCH        0x20000U
-#define PNG_FLAG_STRIP_ERROR_NUMBERS     0x40000U
-#define PNG_FLAG_STRIP_ERROR_TEXT        0x80000U
-#define PNG_FLAG_BENIGN_ERRORS_WARN     0x100000U /* Added to libpng-1.4.0 */
-#define PNG_FLAG_APP_WARNINGS_WARN      0x200000U /* Added to libpng-1.6.0 */
-#define PNG_FLAG_APP_ERRORS_WARN        0x400000U /* Added to libpng-1.6.0 */
-                                  /*    0x800000U    unused */
-                                  /*   0x1000000U    unused */
-                                  /*   0x2000000U    unused */
-                                  /*   0x4000000U    unused */
-                                  /*   0x8000000U    unused */
-                                  /*  0x10000000U    unused */
-                                  /*  0x20000000U    unused */
-                                  /*  0x40000000U    unused */
-
-#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \
-                                     PNG_FLAG_CRC_ANCILLARY_NOWARN)
-
-#define PNG_FLAG_CRC_CRITICAL_MASK  (PNG_FLAG_CRC_CRITICAL_USE | \
-                                     PNG_FLAG_CRC_CRITICAL_IGNORE)
-
-#define PNG_FLAG_CRC_MASK           (PNG_FLAG_CRC_ANCILLARY_MASK | \
-                                     PNG_FLAG_CRC_CRITICAL_MASK)
-
-/* Save typing and make code easier to understand */
-
-#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \
-   abs((int)((c1).green) - (int)((c2).green)) + \
-   abs((int)((c1).blue) - (int)((c2).blue)))
-
-/* Added to libpng-1.6.0: scale a 16-bit value in the range 0..65535 to 0..255
- * by dividing by 257 *with rounding*.  This macro is exact for the given range.
- * See the discourse in pngrtran.c png_do_scale_16_to_8.  The values in the
- * macro were established by experiment (modifying the added value).  The macro
- * has a second variant that takes a value already scaled by 255 and divides by
- * 65535 - this has a maximum error of .502.  Over the range 0..65535*65535 it
- * only gives off-by-one errors and only for 0.5% (1 in 200) of the values.
- */
-#define PNG_DIV65535(v24) (((v24) + 32895) >> 16)
-#define PNG_DIV257(v16) PNG_DIV65535((png_uint_32)(v16) * 255)
-
-/* Added to libpng-1.2.6 JB */
-#define PNG_ROWBYTES(pixel_bits, width) \
-    ((pixel_bits) >= 8 ? \
-    ((size_t)(width) * (((size_t)(pixel_bits)) >> 3)) : \
-    (( ((size_t)(width) * ((size_t)(pixel_bits))) + 7) >> 3) )
-
-/* This returns the number of trailing bits in the last byte of a row, 0 if the
- * last byte is completely full of pixels.  It is, in principle, (pixel_bits x
- * width) % 8, but that would overflow for large 'width'.  The second macro is
- * the same except that it returns the number of unused bits in the last byte;
- * (8-TRAILBITS), but 0 when TRAILBITS is 0.
- *
- * NOTE: these macros are intended to be self-evidently correct and never
- * overflow on the assumption that pixel_bits is in the range 0..255.  The
- * arguments are evaluated only once and they can be signed (e.g. as a result of
- * the integral promotions).  The result of the expression always has type
- * (png_uint_32), however the compiler always knows it is in the range 0..7.
- */
-#define PNG_TRAILBITS(pixel_bits, width) \
-    (((pixel_bits) * ((width) % (png_uint_32)8)) % 8)
-
-#define PNG_PADBITS(pixel_bits, width) \
-    ((8 - PNG_TRAILBITS(pixel_bits, width)) % 8)
-
-/* PNG_OUT_OF_RANGE returns true if value is outside the range
- * ideal-delta..ideal+delta.  Each argument is evaluated twice.
- * "ideal" and "delta" should be constants, normally simple
- * integers, "value" a variable. Added to libpng-1.2.6 JB
- */
-#define PNG_OUT_OF_RANGE(value, ideal, delta) \
-   ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) )
-
-/* Conversions between fixed and floating point, only defined if
- * required (to make sure the code doesn't accidentally use float
- * when it is supposedly disabled.)
- */
-#ifdef PNG_FLOATING_POINT_SUPPORTED
-/* The floating point conversion can't overflow, though it can and
- * does lose accuracy relative to the original fixed point value.
- * In practice this doesn't matter because png_fixed_point only
- * stores numbers with very low precision.  The png_ptr and s
- * arguments are unused by default but are there in case error
- * checking becomes a requirement.
- */
-#define png_float(png_ptr, fixed, s) (.00001 * (fixed))
-
-/* The fixed point conversion performs range checking and evaluates
- * its argument multiple times, so must be used with care.  The
- * range checking uses the PNG specification values for a signed
- * 32-bit fixed point value except that the values are deliberately
- * rounded-to-zero to an integral value - 21474 (21474.83 is roughly
- * (2^31-1) * 100000). 's' is a string that describes the value being
- * converted.
- *
- * NOTE: this macro will raise a png_error if the range check fails,
- * therefore it is normally only appropriate to use this on values
- * that come from API calls or other sources where an out of range
- * error indicates a programming error, not a data error!
- *
- * NOTE: by default this is off - the macro is not used - because the
- * function call saves a lot of code.
- */
-#ifdef PNG_FIXED_POINT_MACRO_SUPPORTED
-#define png_fixed(png_ptr, fp, s) ((fp) <= 21474 && (fp) >= -21474 ?\
-    ((png_fixed_point)(100000 * (fp))) : (png_fixed_error(png_ptr, s),0))
-#endif
-/* else the corresponding function is defined below, inside the scope of the
- * cplusplus test.
- */
-#endif
-
-/* Constants for known chunk types.  If you need to add a chunk, define the name
- * here.  For historical reasons these constants have the form png_<name>; i.e.
- * the prefix is lower case.  Please use decimal values as the parameters to
- * match the ISO PNG specification and to avoid relying on the C locale
- * interpretation of character values.
- *
- * Prior to 1.5.6 these constants were strings, as of 1.5.6 png_uint_32 values
- * are computed and a new macro (PNG_STRING_FROM_CHUNK) added to allow a string
- * to be generated if required.
- *
- * PNG_32b correctly produces a value shifted by up to 24 bits, even on
- * architectures where (int) is only 16 bits.
- */
-#define PNG_32b(b,s) ((png_uint_32)(b) << (s))
-#define PNG_U32(b1,b2,b3,b4) \
-   (PNG_32b(b1,24) | PNG_32b(b2,16) | PNG_32b(b3,8) | PNG_32b(b4,0))
-
-/* Constants for known chunk types.
- *
- * MAINTAINERS: If you need to add a chunk, define the name here.
- * For historical reasons these constants have the form png_<name>; i.e.
- * the prefix is lower case.  Please use decimal values as the parameters to
- * match the ISO PNG specification and to avoid relying on the C locale
- * interpretation of character values.  Please keep the list sorted.
- *
- * Notice that PNG_U32 is used to define a 32-bit value for the 4 byte chunk
- * type.  In fact the specification does not express chunk types this way,
- * however using a 32-bit value means that the chunk type can be read from the
- * stream using exactly the same code as used for a 32-bit unsigned value and
- * can be examined far more efficiently (using one arithmetic compare).
- *
- * Prior to 1.5.6 the chunk type constants were expressed as C strings.  The
- * libpng API still uses strings for 'unknown' chunks and a macro,
- * PNG_STRING_FROM_CHUNK, allows a string to be generated if required.  Notice
- * that for portable code numeric values must still be used; the string "IHDR"
- * is not portable and neither is PNG_U32('I', 'H', 'D', 'R').
- *
- * In 1.7.0 the definitions will be made public in png.h to avoid having to
- * duplicate the same definitions in application code.
- */
-#define png_IDAT PNG_U32( 73,  68,  65,  84)
-#define png_IEND PNG_U32( 73,  69,  78,  68)
-#define png_IHDR PNG_U32( 73,  72,  68,  82)
-#define png_PLTE PNG_U32( 80,  76,  84,  69)
-#define png_bKGD PNG_U32( 98,  75,  71,  68)
-#define png_cHRM PNG_U32( 99,  72,  82,  77)
-#define png_eXIf PNG_U32(101,  88,  73, 102) /* registered July 2017 */
-#define png_fRAc PNG_U32(102,  82,  65,  99) /* registered, not defined */
-#define png_gAMA PNG_U32(103,  65,  77,  65)
-#define png_gIFg PNG_U32(103,  73,  70, 103)
-#define png_gIFt PNG_U32(103,  73,  70, 116) /* deprecated */
-#define png_gIFx PNG_U32(103,  73,  70, 120)
-#define png_hIST PNG_U32(104,  73,  83,  84)
-#define png_iCCP PNG_U32(105,  67,  67,  80)
-#define png_iTXt PNG_U32(105,  84,  88, 116)
-#define png_oFFs PNG_U32(111,  70,  70, 115)
-#define png_pCAL PNG_U32(112,  67,  65,  76)
-#define png_pHYs PNG_U32(112,  72,  89, 115)
-#define png_sBIT PNG_U32(115,  66,  73,  84)
-#define png_sCAL PNG_U32(115,  67,  65,  76)
-#define png_sPLT PNG_U32(115,  80,  76,  84)
-#define png_sRGB PNG_U32(115,  82,  71,  66)
-#define png_sTER PNG_U32(115,  84,  69,  82)
-#define png_tEXt PNG_U32(116,  69,  88, 116)
-#define png_tIME PNG_U32(116,  73,  77,  69)
-#define png_tRNS PNG_U32(116,  82,  78,  83)
-#define png_zTXt PNG_U32(122,  84,  88, 116)
-
-/* The following will work on (signed char*) strings, whereas the get_uint_32
- * macro will fail on top-bit-set values because of the sign extension.
- */
-#define PNG_CHUNK_FROM_STRING(s)\
-   PNG_U32(0xff & (s)[0], 0xff & (s)[1], 0xff & (s)[2], 0xff & (s)[3])
-
-/* This uses (char), not (png_byte) to avoid warnings on systems where (char) is
- * signed and the argument is a (char[])  This macro will fail miserably on
- * systems where (char) is more than 8 bits.
- */
-#define PNG_STRING_FROM_CHUNK(s,c)\
-   (void)(((char*)(s))[0]=(char)(((c)>>24) & 0xff), \
-   ((char*)(s))[1]=(char)(((c)>>16) & 0xff),\
-   ((char*)(s))[2]=(char)(((c)>>8) & 0xff), \
-   ((char*)(s))[3]=(char)((c & 0xff)))
-
-/* Do the same but terminate with a null character. */
-#define PNG_CSTRING_FROM_CHUNK(s,c)\
-   (void)(PNG_STRING_FROM_CHUNK(s,c), ((char*)(s))[4] = 0)
-
-/* Test on flag values as defined in the spec (section 5.4): */
-#define PNG_CHUNK_ANCILLARY(c)   (1 & ((c) >> 29))
-#define PNG_CHUNK_CRITICAL(c)     (!PNG_CHUNK_ANCILLARY(c))
-#define PNG_CHUNK_PRIVATE(c)      (1 & ((c) >> 21))
-#define PNG_CHUNK_RESERVED(c)     (1 & ((c) >> 13))
-#define PNG_CHUNK_SAFE_TO_COPY(c) (1 & ((c) >>  5))
-
-/* Gamma values (new at libpng-1.5.4): */
-#define PNG_GAMMA_MAC_OLD 151724  /* Assume '1.8' is really 2.2/1.45! */
-#define PNG_GAMMA_MAC_INVERSE 65909
-#define PNG_GAMMA_sRGB_INVERSE 45455
-
-/* Almost everything below is C specific; the #defines above can be used in
- * non-C code (so long as it is C-preprocessed) the rest of this stuff cannot.
- */
-#ifndef PNG_VERSION_INFO_ONLY
-
-#include "pngstruct.h"
-#include "pnginfo.h"
-
-/* Validate the include paths - the include path used to generate pnglibconf.h
- * must match that used in the build, or we must be using pnglibconf.h.prebuilt:
- */
-#if PNG_ZLIB_VERNUM != 0 && PNG_ZLIB_VERNUM != ZLIB_VERNUM
-#  error ZLIB_VERNUM != PNG_ZLIB_VERNUM \
-      "-I (include path) error: see the notes in pngpriv.h"
-   /* This means that when pnglibconf.h was built the copy of zlib.h that it
-    * used is not the same as the one being used here.  Because the build of
-    * libpng makes decisions to use inflateInit2 and inflateReset2 based on the
-    * zlib version number and because this affects handling of certain broken
-    * PNG files the -I directives must match.
-    *
-    * The most likely explanation is that you passed a -I in CFLAGS. This will
-    * not work; all the preprocessor directives and in particular all the -I
-    * directives must be in CPPFLAGS.
-    */
-#endif
-
-/* This is used for 16-bit gamma tables -- only the top level pointers are
- * const; this could be changed:
- */
-typedef const png_uint_16p * png_const_uint_16pp;
-
-/* Added to libpng-1.5.7: sRGB conversion tables */
-#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
-   defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
-#ifdef PNG_SIMPLIFIED_READ_SUPPORTED
-PNG_INTERNAL_DATA(const png_uint_16, png_sRGB_table, [256]);
-   /* Convert from an sRGB encoded value 0..255 to a 16-bit linear value,
-    * 0..65535.  This table gives the closest 16-bit answers (no errors).
-    */
-#endif
-
-PNG_INTERNAL_DATA(const png_uint_16, png_sRGB_base, [512]);
-PNG_INTERNAL_DATA(const png_byte, png_sRGB_delta, [512]);
-
-#define PNG_sRGB_FROM_LINEAR(linear) \
-  ((png_byte)(0xff & ((png_sRGB_base[(linear)>>15] \
-   + ((((linear) & 0x7fff)*png_sRGB_delta[(linear)>>15])>>12)) >> 8)))
-   /* Given a value 'linear' in the range 0..255*65535 calculate the 8-bit sRGB
-    * encoded value with maximum error 0.646365.  Note that the input is not a
-    * 16-bit value; it has been multiplied by 255! */
-#endif /* SIMPLIFIED_READ/WRITE */
-
-
-/* Inhibit C++ name-mangling for libpng functions but not for system calls. */
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-/* Internal functions; these are not exported from a DLL however because they
- * are used within several of the C source files they have to be C extern.
- *
- * All of these functions must be declared with PNG_INTERNAL_FUNCTION.
- */
-
-/* Zlib support */
-#define PNG_UNEXPECTED_ZLIB_RETURN (-7)
-PNG_INTERNAL_FUNCTION(void, png_zstream_error,(png_structrp png_ptr, int ret),
-   PNG_EMPTY);
-   /* Used by the zlib handling functions to ensure that z_stream::msg is always
-    * set before they return.
-    */
-
-#ifdef PNG_WRITE_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr,
-   png_compression_bufferp *list),PNG_EMPTY);
-   /* Free the buffer list used by the compressed write code. */
-#endif
-
-#if defined(PNG_FLOATING_POINT_SUPPORTED) && \
-   !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \
-   (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \
-   defined(PNG_sCAL_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) || \
-   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)) || \
-   (defined(PNG_sCAL_SUPPORTED) && \
-   defined(PNG_FLOATING_ARITHMETIC_SUPPORTED))
-PNG_INTERNAL_FUNCTION(png_fixed_point,png_fixed,(png_const_structrp png_ptr,
-   double fp, png_const_charp text),PNG_EMPTY);
-#endif
-
-/* Check the user version string for compatibility, returns false if the version
- * numbers aren't compatible.
- */
-PNG_INTERNAL_FUNCTION(int,png_user_version_check,(png_structrp png_ptr,
-   png_const_charp user_png_ver),PNG_EMPTY);
-
-/* Internal base allocator - no messages, NULL on failure to allocate.  This
- * does, however, call the application provided allocator and that could call
- * png_error (although that would be a bug in the application implementation.)
- */
-PNG_INTERNAL_FUNCTION(png_voidp,png_malloc_base,(png_const_structrp png_ptr,
-   png_alloc_size_t size),PNG_ALLOCATED);
-
-#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) ||\
-   defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED)
-/* Internal array allocator, outputs no error or warning messages on failure,
- * just returns NULL.
- */
-PNG_INTERNAL_FUNCTION(png_voidp,png_malloc_array,(png_const_structrp png_ptr,
-   int nelements, size_t element_size),PNG_ALLOCATED);
-
-/* The same but an existing array is extended by add_elements.  This function
- * also memsets the new elements to 0 and copies the old elements.  The old
- * array is not freed or altered.
- */
-PNG_INTERNAL_FUNCTION(png_voidp,png_realloc_array,(png_const_structrp png_ptr,
-   png_const_voidp array, int old_elements, int add_elements,
-   size_t element_size),PNG_ALLOCATED);
-#endif /* text, sPLT or unknown chunks */
-
-/* Magic to create a struct when there is no struct to call the user supplied
- * memory allocators.  Because error handling has not been set up the memory
- * handlers can't safely call png_error, but this is an obscure and undocumented
- * restriction so libpng has to assume that the 'free' handler, at least, might
- * call png_error.
- */
-PNG_INTERNAL_FUNCTION(png_structp,png_create_png_struct,
-   (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
-    png_error_ptr warn_fn, png_voidp mem_ptr, png_malloc_ptr malloc_fn,
-    png_free_ptr free_fn),PNG_ALLOCATED);
-
-/* Free memory from internal libpng struct */
-PNG_INTERNAL_FUNCTION(void,png_destroy_png_struct,(png_structrp png_ptr),
-   PNG_EMPTY);
-
-/* Free an allocated jmp_buf (always succeeds) */
-PNG_INTERNAL_FUNCTION(void,png_free_jmpbuf,(png_structrp png_ptr),PNG_EMPTY);
-
-/* Function to allocate memory for zlib.  PNGAPI is disallowed. */
-PNG_INTERNAL_FUNCTION(voidpf,png_zalloc,(voidpf png_ptr, uInt items, uInt size),
-   PNG_ALLOCATED);
-
-/* Function to free memory for zlib.  PNGAPI is disallowed. */
-PNG_INTERNAL_FUNCTION(void,png_zfree,(voidpf png_ptr, voidpf ptr),PNG_EMPTY);
-
-/* Next four functions are used internally as callbacks.  PNGCBAPI is required
- * but not PNG_EXPORT.  PNGAPI added at libpng version 1.2.3, changed to
- * PNGCBAPI at 1.5.0
- */
-
-PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_read_data,(png_structp png_ptr,
-    png_bytep data, size_t length),PNG_EMPTY);
-
-#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
-PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_push_fill_buffer,(png_structp png_ptr,
-    png_bytep buffer, size_t length),PNG_EMPTY);
-#endif
-
-PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_write_data,(png_structp png_ptr,
-    png_bytep data, size_t length),PNG_EMPTY);
-
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
-#  ifdef PNG_STDIO_SUPPORTED
-PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_flush,(png_structp png_ptr),
-   PNG_EMPTY);
-#  endif
-#endif
-
-/* Reset the CRC variable */
-PNG_INTERNAL_FUNCTION(void,png_reset_crc,(png_structrp png_ptr),PNG_EMPTY);
-
-/* Write the "data" buffer to whatever output you are using */
-PNG_INTERNAL_FUNCTION(void,png_write_data,(png_structrp png_ptr,
-    png_const_bytep data, size_t length),PNG_EMPTY);
-
-/* Read and check the PNG file signature */
-PNG_INTERNAL_FUNCTION(void,png_read_sig,(png_structrp png_ptr,
-   png_inforp info_ptr),PNG_EMPTY);
-
-/* Read the chunk header (length + type name) */
-PNG_INTERNAL_FUNCTION(png_uint_32,png_read_chunk_header,(png_structrp png_ptr),
-   PNG_EMPTY);
-
-/* Read data from whatever input you are using into the "data" buffer */
-PNG_INTERNAL_FUNCTION(void,png_read_data,(png_structrp png_ptr, png_bytep data,
-    size_t length),PNG_EMPTY);
-
-/* Read bytes into buf, and update png_ptr->crc */
-PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_bytep buf,
-    png_uint_32 length),PNG_EMPTY);
-
-/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */
-PNG_INTERNAL_FUNCTION(int,png_crc_finish,(png_structrp png_ptr,
-   png_uint_32 skip),PNG_EMPTY);
-
-/* Read the CRC from the file and compare it to the libpng calculated CRC */
-PNG_INTERNAL_FUNCTION(int,png_crc_error,(png_structrp png_ptr),PNG_EMPTY);
-
-/* Calculate the CRC over a section of data.  Note that we are only
- * passing a maximum of 64K on systems that have this as a memory limit,
- * since this is the maximum buffer size we can specify.
- */
-PNG_INTERNAL_FUNCTION(void,png_calculate_crc,(png_structrp png_ptr,
-   png_const_bytep ptr, size_t length),PNG_EMPTY);
-
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_flush,(png_structrp png_ptr),PNG_EMPTY);
-#endif
-
-/* Write various chunks */
-
-/* Write the IHDR chunk, and update the png_struct with the necessary
- * information.
- */
-PNG_INTERNAL_FUNCTION(void,png_write_IHDR,(png_structrp png_ptr,
-   png_uint_32 width, png_uint_32 height, int bit_depth, int color_type,
-   int compression_method, int filter_method, int interlace_method),PNG_EMPTY);
-
-PNG_INTERNAL_FUNCTION(void,png_write_PLTE,(png_structrp png_ptr,
-   png_const_colorp palette, png_uint_32 num_pal),PNG_EMPTY);
-
-PNG_INTERNAL_FUNCTION(void,png_compress_IDAT,(png_structrp png_ptr,
-   png_const_bytep row_data, png_alloc_size_t row_data_length, int flush),
-   PNG_EMPTY);
-
-PNG_INTERNAL_FUNCTION(void,png_write_IEND,(png_structrp png_ptr),PNG_EMPTY);
-
-#ifdef PNG_WRITE_gAMA_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_gAMA_fixed,(png_structrp png_ptr,
-    png_fixed_point file_gamma),PNG_EMPTY);
-#endif
-
-#ifdef PNG_WRITE_sBIT_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_sBIT,(png_structrp png_ptr,
-    png_const_color_8p sbit, int color_type),PNG_EMPTY);
-#endif
-
-#ifdef PNG_WRITE_cHRM_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_cHRM_fixed,(png_structrp png_ptr,
-    const png_xy *xy), PNG_EMPTY);
-   /* The xy value must have been previously validated */
-#endif
-
-#ifdef PNG_WRITE_sRGB_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_sRGB,(png_structrp png_ptr,
-    int intent),PNG_EMPTY);
-#endif
-
-#ifdef PNG_WRITE_eXIf_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_eXIf,(png_structrp png_ptr,
-    png_bytep exif, int num_exif),PNG_EMPTY);
-#endif
-
-#ifdef PNG_WRITE_iCCP_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_iCCP,(png_structrp png_ptr,
-   png_const_charp name, png_const_bytep profile), PNG_EMPTY);
-   /* The profile must have been previously validated for correctness, the
-    * length comes from the first four bytes.  Only the base, deflate,
-    * compression is supported.
-    */
-#endif
-
-#ifdef PNG_WRITE_sPLT_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_sPLT,(png_structrp png_ptr,
-    png_const_sPLT_tp palette),PNG_EMPTY);
-#endif
-
-#ifdef PNG_WRITE_tRNS_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_tRNS,(png_structrp png_ptr,
-    png_const_bytep trans, png_const_color_16p values, int number,
-    int color_type),PNG_EMPTY);
-#endif
-
-#ifdef PNG_WRITE_bKGD_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_bKGD,(png_structrp png_ptr,
-    png_const_color_16p values, int color_type),PNG_EMPTY);
-#endif
-
-#ifdef PNG_WRITE_hIST_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_hIST,(png_structrp png_ptr,
-    png_const_uint_16p hist, int num_hist),PNG_EMPTY);
-#endif
-
-/* Chunks that have keywords */
-#ifdef PNG_WRITE_tEXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_tEXt,(png_structrp png_ptr,
-   png_const_charp key, png_const_charp text, size_t text_len),PNG_EMPTY);
-#endif
-
-#ifdef PNG_WRITE_zTXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_zTXt,(png_structrp png_ptr, png_const_charp
-    key, png_const_charp text, int compression),PNG_EMPTY);
-#endif
-
-#ifdef PNG_WRITE_iTXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_iTXt,(png_structrp png_ptr,
-    int compression, png_const_charp key, png_const_charp lang,
-    png_const_charp lang_key, png_const_charp text),PNG_EMPTY);
-#endif
-
-#ifdef PNG_TEXT_SUPPORTED  /* Added at version 1.0.14 and 1.2.4 */
-PNG_INTERNAL_FUNCTION(int,png_set_text_2,(png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_textp text_ptr, int num_text),PNG_EMPTY);
-#endif
-
-#ifdef PNG_WRITE_oFFs_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_oFFs,(png_structrp png_ptr,
-    png_int_32 x_offset, png_int_32 y_offset, int unit_type),PNG_EMPTY);
-#endif
-
-#ifdef PNG_WRITE_pCAL_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_pCAL,(png_structrp png_ptr,
-    png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams,
-    png_const_charp units, png_charpp params),PNG_EMPTY);
-#endif
-
-#ifdef PNG_WRITE_pHYs_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_pHYs,(png_structrp png_ptr,
-    png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit,
-    int unit_type),PNG_EMPTY);
-#endif
-
-#ifdef PNG_WRITE_tIME_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_tIME,(png_structrp png_ptr,
-    png_const_timep mod_time),PNG_EMPTY);
-#endif
-
-#ifdef PNG_WRITE_sCAL_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_sCAL_s,(png_structrp png_ptr,
-    int unit, png_const_charp width, png_const_charp height),PNG_EMPTY);
-#endif
-
-/* Called when finished processing a row of data */
-PNG_INTERNAL_FUNCTION(void,png_write_finish_row,(png_structrp png_ptr),
-    PNG_EMPTY);
-
-/* Internal use only.   Called before first row of data */
-PNG_INTERNAL_FUNCTION(void,png_write_start_row,(png_structrp png_ptr),
-    PNG_EMPTY);
-
-/* Combine a row of data, dealing with alpha, etc. if requested.  'row' is an
- * array of png_ptr->width pixels.  If the image is not interlaced or this
- * is the final pass this just does a memcpy, otherwise the "display" flag
- * is used to determine whether to copy pixels that are not in the current pass.
- *
- * Because 'png_do_read_interlace' (below) replicates pixels this allows this
- * function to achieve the documented 'blocky' appearance during interlaced read
- * if display is 1 and the 'sparkle' appearance, where existing pixels in 'row'
- * are not changed if they are not in the current pass, when display is 0.
- *
- * 'display' must be 0 or 1, otherwise the memcpy will be done regardless.
- *
- * The API always reads from the png_struct row buffer and always assumes that
- * it is full width (png_do_read_interlace has already been called.)
- *
- * This function is only ever used to write to row buffers provided by the
- * caller of the relevant libpng API and the row must have already been
- * transformed by the read transformations.
- *
- * The PNG_USE_COMPILE_TIME_MASKS option causes generation of pre-computed
- * bitmasks for use within the code, otherwise runtime generated masks are used.
- * The default is compile time masks.
- */
-#ifndef PNG_USE_COMPILE_TIME_MASKS
-#  define PNG_USE_COMPILE_TIME_MASKS 1
-#endif
-PNG_INTERNAL_FUNCTION(void,png_combine_row,(png_const_structrp png_ptr,
-    png_bytep row, int display),PNG_EMPTY);
-
-#ifdef PNG_READ_INTERLACING_SUPPORTED
-/* Expand an interlaced row: the 'row_info' describes the pass data that has
- * been read in and must correspond to the pixels in 'row', the pixels are
- * expanded (moved apart) in 'row' to match the final layout, when doing this
- * the pixels are *replicated* to the intervening space.  This is essential for
- * the correct operation of png_combine_row, above.
- */
-PNG_INTERNAL_FUNCTION(void,png_do_read_interlace,(png_row_infop row_info,
-    png_bytep row, int pass, png_uint_32 transformations),PNG_EMPTY);
-#endif
-
-/* GRR TO DO (2.0 or whenever):  simplify other internal calling interfaces */
-
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
-/* Grab pixels out of a row for an interlaced pass */
-PNG_INTERNAL_FUNCTION(void,png_do_write_interlace,(png_row_infop row_info,
-    png_bytep row, int pass),PNG_EMPTY);
-#endif
-
-/* Unfilter a row: check the filter value before calling this, there is no point
- * calling it for PNG_FILTER_VALUE_NONE.
- */
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row,(png_structrp pp, png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row, int filter),PNG_EMPTY);
-
-#if PNG_ARM_NEON_OPT > 0
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_neon,(png_row_infop row_info,
-    png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_neon,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_neon,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_neon,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_neon,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_neon,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_neon,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-#endif
-
-#if PNG_MIPS_MSA_OPT > 0
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_msa,(png_row_infop row_info,
-    png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_msa,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_msa,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_msa,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_msa,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_msa,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_msa,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-#endif
-
-#if PNG_POWERPC_VSX_OPT > 0
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_vsx,(png_row_infop row_info,
-    png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_vsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_vsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_vsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_vsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_vsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_vsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-#endif
-
-#if PNG_INTEL_SSE_IMPLEMENTATION > 0
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-#endif
-
-/* Choose the best filter to use and filter the row data */
-PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr,
-    png_row_infop row_info),PNG_EMPTY);
-
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_read_IDAT_data,(png_structrp png_ptr,
-   png_bytep output, png_alloc_size_t avail_out),PNG_EMPTY);
-   /* Read 'avail_out' bytes of data from the IDAT stream.  If the output buffer
-    * is NULL the function checks, instead, for the end of the stream.  In this
-    * case a benign error will be issued if the stream end is not found or if
-    * extra data has to be consumed.
-    */
-PNG_INTERNAL_FUNCTION(void,png_read_finish_IDAT,(png_structrp png_ptr),
-   PNG_EMPTY);
-   /* This cleans up when the IDAT LZ stream does not end when the last image
-    * byte is read; there is still some pending input.
-    */
-
-PNG_INTERNAL_FUNCTION(void,png_read_finish_row,(png_structrp png_ptr),
-   PNG_EMPTY);
-   /* Finish a row while reading, dealing with interlacing passes, etc. */
-#endif /* SEQUENTIAL_READ */
-
-/* Initialize the row buffers, etc. */
-PNG_INTERNAL_FUNCTION(void,png_read_start_row,(png_structrp png_ptr),PNG_EMPTY);
-
-#if ZLIB_VERNUM >= 0x1240
-PNG_INTERNAL_FUNCTION(int,png_zlib_inflate,(png_structrp png_ptr, int flush),
-      PNG_EMPTY);
-#  define PNG_INFLATE(pp, flush) png_zlib_inflate(pp, flush)
-#else /* Zlib < 1.2.4 */
-#  define PNG_INFLATE(pp, flush) inflate(&(pp)->zstream, flush)
-#endif /* Zlib < 1.2.4 */
-
-#ifdef PNG_READ_TRANSFORMS_SUPPORTED
-/* Optional call to update the users info structure */
-PNG_INTERNAL_FUNCTION(void,png_read_transform_info,(png_structrp png_ptr,
-    png_inforp info_ptr),PNG_EMPTY);
-#endif
-
-/* Shared transform functions, defined in pngtran.c */
-#if defined(PNG_WRITE_FILLER_SUPPORTED) || \
-    defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_strip_channel,(png_row_infop row_info,
-    png_bytep row, int at_start),PNG_EMPTY);
-#endif
-
-#ifdef PNG_16BIT_SUPPORTED
-#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_swap,(png_row_infop row_info,
-    png_bytep row),PNG_EMPTY);
-#endif
-#endif
-
-#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \
-    defined(PNG_WRITE_PACKSWAP_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_packswap,(png_row_infop row_info,
-    png_bytep row),PNG_EMPTY);
-#endif
-
-#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_invert,(png_row_infop row_info,
-    png_bytep row),PNG_EMPTY);
-#endif
-
-#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_bgr,(png_row_infop row_info,
-    png_bytep row),PNG_EMPTY);
-#endif
-
-/* The following decodes the appropriate chunks, and does error correction,
- * then calls the appropriate callback for the chunk if it is valid.
- */
-
-/* Decode the IHDR chunk */
-PNG_INTERNAL_FUNCTION(void,png_handle_IHDR,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_handle_PLTE,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_handle_IEND,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-
-#ifdef PNG_READ_bKGD_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_bKGD,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_cHRM_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_cHRM,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_eXIf_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_eXIf,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_gAMA_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_gAMA,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_hIST_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_hIST,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_iCCP_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_iCCP,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif /* READ_iCCP */
-
-#ifdef PNG_READ_iTXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_iTXt,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_oFFs_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_oFFs,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_pCAL_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_pCAL,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_pHYs_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_pHYs,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_sBIT_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_sBIT,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_sCAL_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_sCAL,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_sPLT_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_sPLT,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif /* READ_sPLT */
-
-#ifdef PNG_READ_sRGB_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_sRGB,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_tEXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_tEXt,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_tIME_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_tIME,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_tRNS_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_tRNS,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_zTXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_zTXt,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-PNG_INTERNAL_FUNCTION(void,png_check_chunk_name,(png_const_structrp png_ptr,
-    png_uint_32 chunk_name),PNG_EMPTY);
-
-PNG_INTERNAL_FUNCTION(void,png_check_chunk_length,(png_const_structrp png_ptr,
-    png_uint_32 chunk_length),PNG_EMPTY);
-
-PNG_INTERNAL_FUNCTION(void,png_handle_unknown,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length, int keep),PNG_EMPTY);
-   /* This is the function that gets called for unknown chunks.  The 'keep'
-    * argument is either non-zero for a known chunk that has been set to be
-    * handled as unknown or zero for an unknown chunk.  By default the function
-    * just skips the chunk or errors out if it is critical.
-    */
-
-#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) ||\
-    defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED)
-PNG_INTERNAL_FUNCTION(int,png_chunk_unknown_handling,
-    (png_const_structrp png_ptr, png_uint_32 chunk_name),PNG_EMPTY);
-   /* Exactly as the API png_handle_as_unknown() except that the argument is a
-    * 32-bit chunk name, not a string.
-    */
-#endif /* READ_UNKNOWN_CHUNKS || HANDLE_AS_UNKNOWN */
-
-/* Handle the transformations for reading and writing */
-#ifdef PNG_READ_TRANSFORMS_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_do_read_transformations,(png_structrp png_ptr,
-   png_row_infop row_info),PNG_EMPTY);
-#endif
-#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_do_write_transformations,(png_structrp png_ptr,
-   png_row_infop row_info),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_TRANSFORMS_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_init_read_transformations,(png_structrp png_ptr),
-    PNG_EMPTY);
-#endif
-
-#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_push_read_chunk,(png_structrp png_ptr,
-    png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_sig,(png_structrp png_ptr,
-    png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_check_crc,(png_structrp png_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_save_buffer,(png_structrp png_ptr),
-    PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_restore_buffer,(png_structrp png_ptr,
-    png_bytep buffer, size_t buffer_length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_IDAT,(png_structrp png_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_process_IDAT_data,(png_structrp png_ptr,
-    png_bytep buffer, size_t buffer_length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_process_row,(png_structrp png_ptr),
-    PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_handle_unknown,(png_structrp png_ptr,
-   png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_have_info,(png_structrp png_ptr,
-   png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_have_end,(png_structrp png_ptr,
-   png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_have_row,(png_structrp png_ptr,
-    png_bytep row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_end,(png_structrp png_ptr,
-    png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_process_some_data,(png_structrp png_ptr,
-    png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_push_finish_row,(png_structrp png_ptr),
-    PNG_EMPTY);
-#  ifdef PNG_READ_tEXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_push_handle_tEXt,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_tEXt,(png_structrp png_ptr,
-    png_inforp info_ptr),PNG_EMPTY);
-#  endif
-#  ifdef PNG_READ_zTXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_push_handle_zTXt,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_zTXt,(png_structrp png_ptr,
-    png_inforp info_ptr),PNG_EMPTY);
-#  endif
-#  ifdef PNG_READ_iTXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_push_handle_iTXt,(png_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_iTXt,(png_structrp png_ptr,
-    png_inforp info_ptr),PNG_EMPTY);
-#  endif
-
-#endif /* PROGRESSIVE_READ */
-
-/* Added at libpng version 1.6.0 */
-#ifdef PNG_GAMMA_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_colorspace_set_gamma,(png_const_structrp png_ptr,
-    png_colorspacerp colorspace, png_fixed_point gAMA), PNG_EMPTY);
-   /* Set the colorspace gamma with a value provided by the application or by
-    * the gAMA chunk on read.  The value will override anything set by an ICC
-    * profile.
-    */
-
-PNG_INTERNAL_FUNCTION(void,png_colorspace_sync_info,(png_const_structrp png_ptr,
-    png_inforp info_ptr), PNG_EMPTY);
-   /* Synchronize the info 'valid' flags with the colorspace */
-
-PNG_INTERNAL_FUNCTION(void,png_colorspace_sync,(png_const_structrp png_ptr,
-    png_inforp info_ptr), PNG_EMPTY);
-   /* Copy the png_struct colorspace to the info_struct and call the above to
-    * synchronize the flags.  Checks for NULL info_ptr and does nothing.
-    */
-#endif
-
-/* Added at libpng version 1.4.0 */
-#ifdef PNG_COLORSPACE_SUPPORTED
-/* These internal functions are for maintaining the colorspace structure within
- * a png_info or png_struct (or, indeed, both).
- */
-PNG_INTERNAL_FUNCTION(int,png_colorspace_set_chromaticities,
-   (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_xy *xy,
-    int preferred), PNG_EMPTY);
-
-PNG_INTERNAL_FUNCTION(int,png_colorspace_set_endpoints,
-   (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_XYZ *XYZ,
-    int preferred), PNG_EMPTY);
-
-#ifdef PNG_sRGB_SUPPORTED
-PNG_INTERNAL_FUNCTION(int,png_colorspace_set_sRGB,(png_const_structrp png_ptr,
-   png_colorspacerp colorspace, int intent), PNG_EMPTY);
-   /* This does set the colorspace gAMA and cHRM values too, but doesn't set the
-    * flags to write them, if it returns false there was a problem and an error
-    * message has already been output (but the colorspace may still need to be
-    * synced to record the invalid flag).
-    */
-#endif /* sRGB */
-
-#ifdef PNG_iCCP_SUPPORTED
-PNG_INTERNAL_FUNCTION(int,png_colorspace_set_ICC,(png_const_structrp png_ptr,
-   png_colorspacerp colorspace, png_const_charp name,
-   png_uint_32 profile_length, png_const_bytep profile, int color_type),
-   PNG_EMPTY);
-   /* The 'name' is used for information only */
-
-/* Routines for checking parts of an ICC profile. */
-#ifdef PNG_READ_iCCP_SUPPORTED
-PNG_INTERNAL_FUNCTION(int,png_icc_check_length,(png_const_structrp png_ptr,
-   png_colorspacerp colorspace, png_const_charp name,
-   png_uint_32 profile_length), PNG_EMPTY);
-#endif /* READ_iCCP */
-PNG_INTERNAL_FUNCTION(int,png_icc_check_header,(png_const_structrp png_ptr,
-   png_colorspacerp colorspace, png_const_charp name,
-   png_uint_32 profile_length,
-   png_const_bytep profile /* first 132 bytes only */, int color_type),
-   PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(int,png_icc_check_tag_table,(png_const_structrp png_ptr,
-   png_colorspacerp colorspace, png_const_charp name,
-   png_uint_32 profile_length,
-   png_const_bytep profile /* header plus whole tag table */), PNG_EMPTY);
-#ifdef PNG_sRGB_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_icc_set_sRGB,(
-   png_const_structrp png_ptr, png_colorspacerp colorspace,
-   png_const_bytep profile, uLong adler), PNG_EMPTY);
-   /* 'adler' is the Adler32 checksum of the uncompressed profile data. It may
-    * be zero to indicate that it is not available.  It is used, if provided,
-    * as a fast check on the profile when checking to see if it is sRGB.
-    */
-#endif
-#endif /* iCCP */
-
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_colorspace_set_rgb_coefficients,
-   (png_structrp png_ptr), PNG_EMPTY);
-   /* Set the rgb_to_gray coefficients from the colorspace Y values */
-#endif /* READ_RGB_TO_GRAY */
-#endif /* COLORSPACE */
-
-/* Added at libpng version 1.4.0 */
-PNG_INTERNAL_FUNCTION(void,png_check_IHDR,(png_const_structrp png_ptr,
-    png_uint_32 width, png_uint_32 height, int bit_depth,
-    int color_type, int interlace_type, int compression_type,
-    int filter_type),PNG_EMPTY);
-
-/* Added at libpng version 1.5.10 */
-#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \
-    defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_check_palette_indexes,
-   (png_structrp png_ptr, png_row_infop row_info),PNG_EMPTY);
-#endif
-
-#if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_fixed_error,(png_const_structrp png_ptr,
-   png_const_charp name),PNG_NORETURN);
-#endif
-
-/* Puts 'string' into 'buffer' at buffer[pos], taking care never to overwrite
- * the end.  Always leaves the buffer nul terminated.  Never errors out (and
- * there is no error code.)
- */
-PNG_INTERNAL_FUNCTION(size_t,png_safecat,(png_charp buffer, size_t bufsize,
-   size_t pos, png_const_charp string),PNG_EMPTY);
-
-/* Various internal functions to handle formatted warning messages, currently
- * only implemented for warnings.
- */
-#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED)
-/* Utility to dump an unsigned value into a buffer, given a start pointer and
- * and end pointer (which should point just *beyond* the end of the buffer!)
- * Returns the pointer to the start of the formatted string.  This utility only
- * does unsigned values.
- */
-PNG_INTERNAL_FUNCTION(png_charp,png_format_number,(png_const_charp start,
-   png_charp end, int format, png_alloc_size_t number),PNG_EMPTY);
-
-/* Convenience macro that takes an array: */
-#define PNG_FORMAT_NUMBER(buffer,format,number) \
-   png_format_number(buffer, buffer + (sizeof buffer), format, number)
-
-/* Suggested size for a number buffer (enough for 64 bits and a sign!) */
-#define PNG_NUMBER_BUFFER_SIZE 24
-
-/* These are the integer formats currently supported, the name is formed from
- * the standard printf(3) format string.
- */
-#define PNG_NUMBER_FORMAT_u     1 /* chose unsigned API! */
-#define PNG_NUMBER_FORMAT_02u   2
-#define PNG_NUMBER_FORMAT_d     1 /* chose signed API! */
-#define PNG_NUMBER_FORMAT_02d   2
-#define PNG_NUMBER_FORMAT_x     3
-#define PNG_NUMBER_FORMAT_02x   4
-#define PNG_NUMBER_FORMAT_fixed 5 /* choose the signed API */
-#endif
-
-#ifdef PNG_WARNINGS_SUPPORTED
-/* New defines and members adding in libpng-1.5.4 */
-#  define PNG_WARNING_PARAMETER_SIZE 32
-#  define PNG_WARNING_PARAMETER_COUNT 8 /* Maximum 9; see pngerror.c */
-
-/* An l-value of this type has to be passed to the APIs below to cache the
- * values of the parameters to a formatted warning message.
- */
-typedef char png_warning_parameters[PNG_WARNING_PARAMETER_COUNT][
-   PNG_WARNING_PARAMETER_SIZE];
-
-PNG_INTERNAL_FUNCTION(void,png_warning_parameter,(png_warning_parameters p,
-   int number, png_const_charp string),PNG_EMPTY);
-   /* Parameters are limited in size to PNG_WARNING_PARAMETER_SIZE characters,
-    * including the trailing '\0'.
-    */
-PNG_INTERNAL_FUNCTION(void,png_warning_parameter_unsigned,
-   (png_warning_parameters p, int number, int format, png_alloc_size_t value),
-   PNG_EMPTY);
-   /* Use png_alloc_size_t because it is an unsigned type as big as any we
-    * need to output.  Use the following for a signed value.
-    */
-PNG_INTERNAL_FUNCTION(void,png_warning_parameter_signed,
-   (png_warning_parameters p, int number, int format, png_int_32 value),
-   PNG_EMPTY);
-
-PNG_INTERNAL_FUNCTION(void,png_formatted_warning,(png_const_structrp png_ptr,
-   png_warning_parameters p, png_const_charp message),PNG_EMPTY);
-   /* 'message' follows the X/Open approach of using @1, @2 to insert
-    * parameters previously supplied using the above functions.  Errors in
-    * specifying the parameters will simply result in garbage substitutions.
-    */
-#endif
-
-#ifdef PNG_BENIGN_ERRORS_SUPPORTED
-/* Application errors (new in 1.6); use these functions (declared below) for
- * errors in the parameters or order of API function calls on read.  The
- * 'warning' should be used for an error that can be handled completely; the
- * 'error' for one which can be handled safely but which may lose application
- * information or settings.
- *
- * By default these both result in a png_error call prior to release, while in a
- * released version the 'warning' is just a warning.  However if the application
- * explicitly disables benign errors (explicitly permitting the code to lose
- * information) they both turn into warnings.
- *
- * If benign errors aren't supported they end up as the corresponding base call
- * (png_warning or png_error.)
- */
-PNG_INTERNAL_FUNCTION(void,png_app_warning,(png_const_structrp png_ptr,
-   png_const_charp message),PNG_EMPTY);
-   /* The application provided invalid parameters to an API function or called
-    * an API function at the wrong time, libpng can completely recover.
-    */
-
-PNG_INTERNAL_FUNCTION(void,png_app_error,(png_const_structrp png_ptr,
-   png_const_charp message),PNG_EMPTY);
-   /* As above but libpng will ignore the call, or attempt some other partial
-    * recovery from the error.
-    */
-#else
-#  define png_app_warning(pp,s) png_warning(pp,s)
-#  define png_app_error(pp,s) png_error(pp,s)
-#endif
-
-PNG_INTERNAL_FUNCTION(void,png_chunk_report,(png_const_structrp png_ptr,
-   png_const_charp message, int error),PNG_EMPTY);
-   /* Report a recoverable issue in chunk data.  On read this is used to report
-    * a problem found while reading a particular chunk and the
-    * png_chunk_benign_error or png_chunk_warning function is used as
-    * appropriate.  On write this is used to report an error that comes from
-    * data set via an application call to a png_set_ API and png_app_error or
-    * png_app_warning is used as appropriate.
-    *
-    * The 'error' parameter must have one of the following values:
-    */
-#define PNG_CHUNK_WARNING     0 /* never an error */
-#define PNG_CHUNK_WRITE_ERROR 1 /* an error only on write */
-#define PNG_CHUNK_ERROR       2 /* always an error */
-
-/* ASCII to FP interfaces, currently only implemented if sCAL
- * support is required.
- */
-#if defined(PNG_sCAL_SUPPORTED)
-/* MAX_DIGITS is actually the maximum number of characters in an sCAL
- * width or height, derived from the precision (number of significant
- * digits - a build time settable option) and assumptions about the
- * maximum ridiculous exponent.
- */
-#define PNG_sCAL_MAX_DIGITS (PNG_sCAL_PRECISION+1/*.*/+1/*E*/+10/*exponent*/)
-
-#ifdef PNG_FLOATING_POINT_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_ascii_from_fp,(png_const_structrp png_ptr,
-   png_charp ascii, size_t size, double fp, unsigned int precision),
-   PNG_EMPTY);
-#endif /* FLOATING_POINT */
-
-#ifdef PNG_FIXED_POINT_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_ascii_from_fixed,(png_const_structrp png_ptr,
-   png_charp ascii, size_t size, png_fixed_point fp),PNG_EMPTY);
-#endif /* FIXED_POINT */
-#endif /* sCAL */
-
-#if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED)
-/* An internal API to validate the format of a floating point number.
- * The result is the index of the next character.  If the number is
- * not valid it will be the index of a character in the supposed number.
- *
- * The format of a number is defined in the PNG extensions specification
- * and this API is strictly conformant to that spec, not anyone elses!
- *
- * The format as a regular expression is:
- *
- * [+-]?[0-9]+.?([Ee][+-]?[0-9]+)?
- *
- * or:
- *
- * [+-]?.[0-9]+(.[0-9]+)?([Ee][+-]?[0-9]+)?
- *
- * The complexity is that either integer or fraction must be present and the
- * fraction is permitted to have no digits only if the integer is present.
- *
- * NOTE: The dangling E problem.
- *   There is a PNG valid floating point number in the following:
- *
- *       PNG floating point numbers are not greedy.
- *
- *   Working this out requires *TWO* character lookahead (because of the
- *   sign), the parser does not do this - it will fail at the 'r' - this
- *   doesn't matter for PNG sCAL chunk values, but it requires more care
- *   if the value were ever to be embedded in something more complex.  Use
- *   ANSI-C strtod if you need the lookahead.
- */
-/* State table for the parser. */
-#define PNG_FP_INTEGER    0  /* before or in integer */
-#define PNG_FP_FRACTION   1  /* before or in fraction */
-#define PNG_FP_EXPONENT   2  /* before or in exponent */
-#define PNG_FP_STATE      3  /* mask for the above */
-#define PNG_FP_SAW_SIGN   4  /* Saw +/- in current state */
-#define PNG_FP_SAW_DIGIT  8  /* Saw a digit in current state */
-#define PNG_FP_SAW_DOT   16  /* Saw a dot in current state */
-#define PNG_FP_SAW_E     32  /* Saw an E (or e) in current state */
-#define PNG_FP_SAW_ANY   60  /* Saw any of the above 4 */
-
-/* These three values don't affect the parser.  They are set but not used.
- */
-#define PNG_FP_WAS_VALID 64  /* Preceding substring is a valid fp number */
-#define PNG_FP_NEGATIVE 128  /* A negative number, including "-0" */
-#define PNG_FP_NONZERO  256  /* A non-zero value */
-#define PNG_FP_STICKY   448  /* The above three flags */
-
-/* This is available for the caller to store in 'state' if required.  Do not
- * call the parser after setting it (the parser sometimes clears it.)
- */
-#define PNG_FP_INVALID  512  /* Available for callers as a distinct value */
-
-/* Result codes for the parser (boolean - true meants ok, false means
- * not ok yet.)
- */
-#define PNG_FP_MAYBE      0  /* The number may be valid in the future */
-#define PNG_FP_OK         1  /* The number is valid */
-
-/* Tests on the sticky non-zero and negative flags.  To pass these checks
- * the state must also indicate that the whole number is valid - this is
- * achieved by testing PNG_FP_SAW_DIGIT (see the implementation for why this
- * is equivalent to PNG_FP_OK above.)
- */
-#define PNG_FP_NZ_MASK (PNG_FP_SAW_DIGIT | PNG_FP_NEGATIVE | PNG_FP_NONZERO)
-   /* NZ_MASK: the string is valid and a non-zero negative value */
-#define PNG_FP_Z_MASK (PNG_FP_SAW_DIGIT | PNG_FP_NONZERO)
-   /* Z MASK: the string is valid and a non-zero value. */
-   /* PNG_FP_SAW_DIGIT: the string is valid. */
-#define PNG_FP_IS_ZERO(state) (((state) & PNG_FP_Z_MASK) == PNG_FP_SAW_DIGIT)
-#define PNG_FP_IS_POSITIVE(state) (((state) & PNG_FP_NZ_MASK) == PNG_FP_Z_MASK)
-#define PNG_FP_IS_NEGATIVE(state) (((state) & PNG_FP_NZ_MASK) == PNG_FP_NZ_MASK)
-
-/* The actual parser.  This can be called repeatedly. It updates
- * the index into the string and the state variable (which must
- * be initialized to 0).  It returns a result code, as above.  There
- * is no point calling the parser any more if it fails to advance to
- * the end of the string - it is stuck on an invalid character (or
- * terminated by '\0').
- *
- * Note that the pointer will consume an E or even an E+ and then leave
- * a 'maybe' state even though a preceding integer.fraction is valid.
- * The PNG_FP_WAS_VALID flag indicates that a preceding substring was
- * a valid number.  It's possible to recover from this by calling
- * the parser again (from the start, with state 0) but with a string
- * that omits the last character (i.e. set the size to the index of
- * the problem character.)  This has not been tested within libpng.
- */
-PNG_INTERNAL_FUNCTION(int,png_check_fp_number,(png_const_charp string,
-   size_t size, int *statep, png_size_tp whereami),PNG_EMPTY);
-
-/* This is the same but it checks a complete string and returns true
- * only if it just contains a floating point number.  As of 1.5.4 this
- * function also returns the state at the end of parsing the number if
- * it was valid (otherwise it returns 0.)  This can be used for testing
- * for negative or zero values using the sticky flag.
- */
-PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string,
-   size_t size),PNG_EMPTY);
-#endif /* pCAL || sCAL */
-
-#if defined(PNG_GAMMA_SUPPORTED) ||\
-    defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED)
-/* Added at libpng version 1.5.0 */
-/* This is a utility to provide a*times/div (rounded) and indicate
- * if there is an overflow.  The result is a boolean - false (0)
- * for overflow, true (1) if no overflow, in which case *res
- * holds the result.
- */
-PNG_INTERNAL_FUNCTION(int,png_muldiv,(png_fixed_point_p res, png_fixed_point a,
-   png_int_32 multiplied_by, png_int_32 divided_by),PNG_EMPTY);
-#endif
-
-#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED)
-/* Same deal, but issue a warning on overflow and return 0. */
-PNG_INTERNAL_FUNCTION(png_fixed_point,png_muldiv_warn,
-   (png_const_structrp png_ptr, png_fixed_point a, png_int_32 multiplied_by,
-   png_int_32 divided_by),PNG_EMPTY);
-#endif
-
-#ifdef PNG_GAMMA_SUPPORTED
-/* Calculate a reciprocal - used for gamma values.  This returns
- * 0 if the argument is 0 in order to maintain an undefined value;
- * there are no warnings.
- */
-PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a),
-   PNG_EMPTY);
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
-/* The same but gives a reciprocal of the product of two fixed point
- * values.  Accuracy is suitable for gamma calculations but this is
- * not exact - use png_muldiv for that.  Only required at present on read.
- */
-PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal2,(png_fixed_point a,
-   png_fixed_point b),PNG_EMPTY);
-#endif
-
-/* Return true if the gamma value is significantly different from 1.0 */
-PNG_INTERNAL_FUNCTION(int,png_gamma_significant,(png_fixed_point gamma_value),
-   PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
-/* Internal fixed point gamma correction.  These APIs are called as
- * required to convert single values - they don't need to be fast,
- * they are not used when processing image pixel values.
- *
- * While the input is an 'unsigned' value it must actually be the
- * correct bit value - 0..255 or 0..65535 as required.
- */
-PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_correct,(png_structrp png_ptr,
-   unsigned int value, png_fixed_point gamma_value),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_16bit_correct,(unsigned int value,
-   png_fixed_point gamma_value),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(png_byte,png_gamma_8bit_correct,(unsigned int value,
-   png_fixed_point gamma_value),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_destroy_gamma_table,(png_structrp png_ptr),
-   PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_build_gamma_table,(png_structrp png_ptr,
-   int bit_depth),PNG_EMPTY);
-#endif
-
-/* SIMPLIFIED READ/WRITE SUPPORT */
-#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
-   defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
-/* The internal structure that png_image::opaque points to. */
-typedef struct png_control
-{
-   png_structp png_ptr;
-   png_infop   info_ptr;
-   png_voidp   error_buf;           /* Always a jmp_buf at present. */
-
-   png_const_bytep memory;          /* Memory buffer. */
-   size_t          size;            /* Size of the memory buffer. */
-
-   unsigned int for_write       :1; /* Otherwise it is a read structure */
-   unsigned int owned_file      :1; /* We own the file in io_ptr */
-} png_control;
-
-/* Return the pointer to the jmp_buf from a png_control: necessary because C
- * does not reveal the type of the elements of jmp_buf.
- */
-#ifdef __cplusplus
-#  define png_control_jmp_buf(pc) (((jmp_buf*)((pc)->error_buf))[0])
-#else
-#  define png_control_jmp_buf(pc) ((pc)->error_buf)
-#endif
-
-/* Utility to safely execute a piece of libpng code catching and logging any
- * errors that might occur.  Returns true on success, false on failure (either
- * of the function or as a result of a png_error.)
- */
-PNG_INTERNAL_CALLBACK(void,png_safe_error,(png_structp png_ptr,
-   png_const_charp error_message),PNG_NORETURN);
-
-#ifdef PNG_WARNINGS_SUPPORTED
-PNG_INTERNAL_CALLBACK(void,png_safe_warning,(png_structp png_ptr,
-   png_const_charp warning_message),PNG_EMPTY);
-#else
-#  define png_safe_warning 0/*dummy argument*/
-#endif
-
-PNG_INTERNAL_FUNCTION(int,png_safe_execute,(png_imagep image,
-   int (*function)(png_voidp), png_voidp arg),PNG_EMPTY);
-
-/* Utility to log an error; this also cleans up the png_image; the function
- * always returns 0 (false).
- */
-PNG_INTERNAL_FUNCTION(int,png_image_error,(png_imagep image,
-   png_const_charp error_message),PNG_EMPTY);
-
-#ifndef PNG_SIMPLIFIED_READ_SUPPORTED
-/* png_image_free is used by the write code but not exported */
-PNG_INTERNAL_FUNCTION(void, png_image_free, (png_imagep image), PNG_EMPTY);
-#endif /* !SIMPLIFIED_READ */
-
-#endif /* SIMPLIFIED READ/WRITE */
-
-/* These are initialization functions for hardware specific PNG filter
- * optimizations; list these here then select the appropriate one at compile
- * time using the macro PNG_FILTER_OPTIMIZATIONS.  If the macro is not defined
- * the generic code is used.
- */
-#ifdef PNG_FILTER_OPTIMIZATIONS
-PNG_INTERNAL_FUNCTION(void, PNG_FILTER_OPTIMIZATIONS, (png_structp png_ptr,
-   unsigned int bpp), PNG_EMPTY);
-   /* Just declare the optimization that will be used */
-#else
-   /* List *all* the possible optimizations here - this branch is required if
-    * the builder of libpng passes the definition of PNG_FILTER_OPTIMIZATIONS in
-    * CFLAGS in place of CPPFLAGS *and* uses symbol prefixing.
-    */
-#  if PNG_ARM_NEON_OPT > 0
-PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_neon,
-   (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
-#endif
-
-#if PNG_MIPS_MSA_OPT > 0
-PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_msa,
-   (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
-#endif
-
-#  if PNG_INTEL_SSE_IMPLEMENTATION > 0
-PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_sse2,
-   (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
-#  endif
-#endif
-
-PNG_INTERNAL_FUNCTION(png_uint_32, png_check_keyword, (png_structrp png_ptr,
-   png_const_charp key, png_bytep new_key), PNG_EMPTY);
-
-#if PNG_ARM_NEON_IMPLEMENTATION == 1
-PNG_INTERNAL_FUNCTION(void,
-                      png_riffle_palette_neon,
-                      (png_structrp),
-                      PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(int,
-                      png_do_expand_palette_rgba8_neon,
-                      (png_structrp,
-                       png_row_infop,
-                       png_const_bytep,
-                       const png_bytepp,
-                       const png_bytepp),
-                      PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(int,
-                      png_do_expand_palette_rgb8_neon,
-                      (png_structrp,
-                       png_row_infop,
-                       png_const_bytep,
-                       const png_bytepp,
-                       const png_bytepp),
-                      PNG_EMPTY);
-#endif
-
-/* Maintainer: Put new private prototypes here ^ */
-
-#include "pngdebug.h"
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* PNG_VERSION_INFO_ONLY */
-#endif /* PNGPRIV_H */
diff --git a/third_party/libpng16/pngread.c b/third_party/libpng16/pngread.c
deleted file mode 100644
index 8fa7d9f..0000000
--- a/third_party/libpng16/pngread.c
+++ /dev/null
@@ -1,4225 +0,0 @@
-
-/* pngread.c - read a PNG file
- *
- * Copyright (c) 2018-2019 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- *
- * This file contains routines that an application calls directly to
- * read a PNG file or stream.
- */
-
-#include "pngpriv.h"
-#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)
-#  include <errno.h>
-#endif
-
-#ifdef PNG_READ_SUPPORTED
-
-/* Create a PNG structure for reading, and allocate any memory needed. */
-PNG_FUNCTION(png_structp,PNGAPI
-png_create_read_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
-    png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED)
-{
-#ifndef PNG_USER_MEM_SUPPORTED
-   png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
-        error_fn, warn_fn, NULL, NULL, NULL);
-#else
-   return png_create_read_struct_2(user_png_ver, error_ptr, error_fn,
-        warn_fn, NULL, NULL, NULL);
-}
-
-/* Alternate create PNG structure for reading, and allocate any memory
- * needed.
- */
-PNG_FUNCTION(png_structp,PNGAPI
-png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
-    png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
-    png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
-{
-   png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
-       error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);
-#endif /* USER_MEM */
-
-   if (png_ptr != NULL)
-   {
-      png_ptr->mode = PNG_IS_READ_STRUCT;
-
-      /* Added in libpng-1.6.0; this can be used to detect a read structure if
-       * required (it will be zero in a write structure.)
-       */
-#     ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-         png_ptr->IDAT_read_size = PNG_IDAT_READ_SIZE;
-#     endif
-
-#     ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED
-         png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN;
-
-         /* In stable builds only warn if an application error can be completely
-          * handled.
-          */
-#        if PNG_RELEASE_BUILD
-            png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN;
-#        endif
-#     endif
-
-      /* TODO: delay this, it can be done in png_init_io (if the app doesn't
-       * do it itself) avoiding setting the default function if it is not
-       * required.
-       */
-      png_set_read_fn(png_ptr, NULL, NULL);
-   }
-
-   return png_ptr;
-}
-
-
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-/* Read the information before the actual image data.  This has been
- * changed in v0.90 to allow reading a file that already has the magic
- * bytes read from the stream.  You can tell libpng how many bytes have
- * been read from the beginning of the stream (up to the maximum of 8)
- * via png_set_sig_bytes(), and we will only check the remaining bytes
- * here.  The application can then have access to the signature bytes we
- * read if it is determined that this isn't a valid PNG file.
- */
-void PNGAPI
-png_read_info(png_structrp png_ptr, png_inforp info_ptr)
-{
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-   int keep;
-#endif
-
-   png_debug(1, "in png_read_info");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   /* Read and check the PNG file signature. */
-   png_read_sig(png_ptr, info_ptr);
-
-   for (;;)
-   {
-      png_uint_32 length = png_read_chunk_header(png_ptr);
-      png_uint_32 chunk_name = png_ptr->chunk_name;
-
-      /* IDAT logic needs to happen here to simplify getting the two flags
-       * right.
-       */
-      if (chunk_name == png_IDAT)
-      {
-         if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-            png_chunk_error(png_ptr, "Missing IHDR before IDAT");
-
-         else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
-             (png_ptr->mode & PNG_HAVE_PLTE) == 0)
-            png_chunk_error(png_ptr, "Missing PLTE before IDAT");
-
-         else if ((png_ptr->mode & PNG_AFTER_IDAT) != 0)
-            png_chunk_benign_error(png_ptr, "Too many IDATs found");
-
-         png_ptr->mode |= PNG_HAVE_IDAT;
-      }
-
-      else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-      {
-         png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT;
-         png_ptr->mode |= PNG_AFTER_IDAT;
-      }
-
-      /* This should be a binary subdivision search or a hash for
-       * matching the chunk name rather than a linear search.
-       */
-      if (chunk_name == png_IHDR)
-         png_handle_IHDR(png_ptr, info_ptr, length);
-
-      else if (chunk_name == png_IEND)
-         png_handle_IEND(png_ptr, info_ptr, length);
-
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-      else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0)
-      {
-         png_handle_unknown(png_ptr, info_ptr, length, keep);
-
-         if (chunk_name == png_PLTE)
-            png_ptr->mode |= PNG_HAVE_PLTE;
-
-         else if (chunk_name == png_IDAT)
-         {
-            png_ptr->idat_size = 0; /* It has been consumed */
-            break;
-         }
-      }
-#endif
-      else if (chunk_name == png_PLTE)
-         png_handle_PLTE(png_ptr, info_ptr, length);
-
-      else if (chunk_name == png_IDAT)
-      {
-         png_ptr->idat_size = length;
-         break;
-      }
-
-#ifdef PNG_READ_bKGD_SUPPORTED
-      else if (chunk_name == png_bKGD)
-         png_handle_bKGD(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_cHRM_SUPPORTED
-      else if (chunk_name == png_cHRM)
-         png_handle_cHRM(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_eXIf_SUPPORTED
-      else if (chunk_name == png_eXIf)
-         png_handle_eXIf(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_gAMA_SUPPORTED
-      else if (chunk_name == png_gAMA)
-         png_handle_gAMA(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_hIST_SUPPORTED
-      else if (chunk_name == png_hIST)
-         png_handle_hIST(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_oFFs_SUPPORTED
-      else if (chunk_name == png_oFFs)
-         png_handle_oFFs(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_pCAL_SUPPORTED
-      else if (chunk_name == png_pCAL)
-         png_handle_pCAL(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sCAL_SUPPORTED
-      else if (chunk_name == png_sCAL)
-         png_handle_sCAL(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_pHYs_SUPPORTED
-      else if (chunk_name == png_pHYs)
-         png_handle_pHYs(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sBIT_SUPPORTED
-      else if (chunk_name == png_sBIT)
-         png_handle_sBIT(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sRGB_SUPPORTED
-      else if (chunk_name == png_sRGB)
-         png_handle_sRGB(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_iCCP_SUPPORTED
-      else if (chunk_name == png_iCCP)
-         png_handle_iCCP(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sPLT_SUPPORTED
-      else if (chunk_name == png_sPLT)
-         png_handle_sPLT(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tEXt_SUPPORTED
-      else if (chunk_name == png_tEXt)
-         png_handle_tEXt(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tIME_SUPPORTED
-      else if (chunk_name == png_tIME)
-         png_handle_tIME(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tRNS_SUPPORTED
-      else if (chunk_name == png_tRNS)
-         png_handle_tRNS(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_zTXt_SUPPORTED
-      else if (chunk_name == png_zTXt)
-         png_handle_zTXt(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_iTXt_SUPPORTED
-      else if (chunk_name == png_iTXt)
-         png_handle_iTXt(png_ptr, info_ptr, length);
-#endif
-
-      else
-         png_handle_unknown(png_ptr, info_ptr, length,
-             PNG_HANDLE_CHUNK_AS_DEFAULT);
-   }
-}
-#endif /* SEQUENTIAL_READ */
-
-/* Optional call to update the users info_ptr structure */
-void PNGAPI
-png_read_update_info(png_structrp png_ptr, png_inforp info_ptr)
-{
-   png_debug(1, "in png_read_update_info");
-
-   if (png_ptr != NULL)
-   {
-      if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0)
-      {
-         png_read_start_row(png_ptr);
-
-#        ifdef PNG_READ_TRANSFORMS_SUPPORTED
-            png_read_transform_info(png_ptr, info_ptr);
-#        else
-            PNG_UNUSED(info_ptr)
-#        endif
-      }
-
-      /* New in 1.6.0 this avoids the bug of doing the initializations twice */
-      else
-         png_app_error(png_ptr,
-             "png_read_update_info/png_start_read_image: duplicate call");
-   }
-}
-
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-/* Initialize palette, background, etc, after transformations
- * are set, but before any reading takes place.  This allows
- * the user to obtain a gamma-corrected palette, for example.
- * If the user doesn't call this, we will do it ourselves.
- */
-void PNGAPI
-png_start_read_image(png_structrp png_ptr)
-{
-   png_debug(1, "in png_start_read_image");
-
-   if (png_ptr != NULL)
-   {
-      if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0)
-         png_read_start_row(png_ptr);
-
-      /* New in 1.6.0 this avoids the bug of doing the initializations twice */
-      else
-         png_app_error(png_ptr,
-             "png_start_read_image/png_read_update_info: duplicate call");
-   }
-}
-#endif /* SEQUENTIAL_READ */
-
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-/* Undoes intrapixel differencing,
- * NOTE: this is apparently only supported in the 'sequential' reader.
- */
-static void
-png_do_read_intrapixel(png_row_infop row_info, png_bytep row)
-{
-   png_debug(1, "in png_do_read_intrapixel");
-
-   if (
-       (row_info->color_type & PNG_COLOR_MASK_COLOR) != 0)
-   {
-      int bytes_per_pixel;
-      png_uint_32 row_width = row_info->width;
-
-      if (row_info->bit_depth == 8)
-      {
-         png_bytep rp;
-         png_uint_32 i;
-
-         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
-            bytes_per_pixel = 3;
-
-         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
-            bytes_per_pixel = 4;
-
-         else
-            return;
-
-         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
-         {
-            *(rp) = (png_byte)((256 + *rp + *(rp + 1)) & 0xff);
-            *(rp+2) = (png_byte)((256 + *(rp + 2) + *(rp + 1)) & 0xff);
-         }
-      }
-      else if (row_info->bit_depth == 16)
-      {
-         png_bytep rp;
-         png_uint_32 i;
-
-         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
-            bytes_per_pixel = 6;
-
-         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
-            bytes_per_pixel = 8;
-
-         else
-            return;
-
-         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
-         {
-            png_uint_32 s0   = (png_uint_32)(*(rp    ) << 8) | *(rp + 1);
-            png_uint_32 s1   = (png_uint_32)(*(rp + 2) << 8) | *(rp + 3);
-            png_uint_32 s2   = (png_uint_32)(*(rp + 4) << 8) | *(rp + 5);
-            png_uint_32 red  = (s0 + s1 + 65536) & 0xffff;
-            png_uint_32 blue = (s2 + s1 + 65536) & 0xffff;
-            *(rp    ) = (png_byte)((red >> 8) & 0xff);
-            *(rp + 1) = (png_byte)(red & 0xff);
-            *(rp + 4) = (png_byte)((blue >> 8) & 0xff);
-            *(rp + 5) = (png_byte)(blue & 0xff);
-         }
-      }
-   }
-}
-#endif /* MNG_FEATURES */
-
-void PNGAPI
-png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row)
-{
-   png_row_info row_info;
-
-   if (png_ptr == NULL)
-      return;
-
-   png_debug2(1, "in png_read_row (row %lu, pass %d)",
-       (unsigned long)png_ptr->row_number, png_ptr->pass);
-
-   /* png_read_start_row sets the information (in particular iwidth) for this
-    * interlace pass.
-    */
-   if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0)
-      png_read_start_row(png_ptr);
-
-   /* 1.5.6: row_info moved out of png_struct to a local here. */
-   row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */
-   row_info.color_type = png_ptr->color_type;
-   row_info.bit_depth = png_ptr->bit_depth;
-   row_info.channels = png_ptr->channels;
-   row_info.pixel_depth = png_ptr->pixel_depth;
-   row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);
-
-#ifdef PNG_WARNINGS_SUPPORTED
-   if (png_ptr->row_number == 0 && png_ptr->pass == 0)
-   {
-   /* Check for transforms that have been set but were defined out */
-#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED)
-   if ((png_ptr->transformations & PNG_INVERT_MONO) != 0)
-      png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined");
-#endif
-
-#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED)
-   if ((png_ptr->transformations & PNG_FILLER) != 0)
-      png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined");
-#endif
-
-#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \
-    !defined(PNG_READ_PACKSWAP_SUPPORTED)
-   if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
-      png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined");
-#endif
-
-#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED)
-   if ((png_ptr->transformations & PNG_PACK) != 0)
-      png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined");
-#endif
-
-#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED)
-   if ((png_ptr->transformations & PNG_SHIFT) != 0)
-      png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined");
-#endif
-
-#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED)
-   if ((png_ptr->transformations & PNG_BGR) != 0)
-      png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined");
-#endif
-
-#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED)
-   if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0)
-      png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined");
-#endif
-   }
-#endif /* WARNINGS */
-
-#ifdef PNG_READ_INTERLACING_SUPPORTED
-   /* If interlaced and we do not need a new row, combine row and return.
-    * Notice that the pixels we have from previous rows have been transformed
-    * already; we can only combine like with like (transformed or
-    * untransformed) and, because of the libpng API for interlaced images, this
-    * means we must transform before de-interlacing.
-    */
-   if (png_ptr->interlaced != 0 &&
-       (png_ptr->transformations & PNG_INTERLACE) != 0)
-   {
-      switch (png_ptr->pass)
-      {
-         case 0:
-            if (png_ptr->row_number & 0x07)
-            {
-               if (dsp_row != NULL)
-                  png_combine_row(png_ptr, dsp_row, 1/*display*/);
-               png_read_finish_row(png_ptr);
-               return;
-            }
-            break;
-
-         case 1:
-            if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)
-            {
-               if (dsp_row != NULL)
-                  png_combine_row(png_ptr, dsp_row, 1/*display*/);
-
-               png_read_finish_row(png_ptr);
-               return;
-            }
-            break;
-
-         case 2:
-            if ((png_ptr->row_number & 0x07) != 4)
-            {
-               if (dsp_row != NULL && (png_ptr->row_number & 4))
-                  png_combine_row(png_ptr, dsp_row, 1/*display*/);
-
-               png_read_finish_row(png_ptr);
-               return;
-            }
-            break;
-
-         case 3:
-            if ((png_ptr->row_number & 3) || png_ptr->width < 3)
-            {
-               if (dsp_row != NULL)
-                  png_combine_row(png_ptr, dsp_row, 1/*display*/);
-
-               png_read_finish_row(png_ptr);
-               return;
-            }
-            break;
-
-         case 4:
-            if ((png_ptr->row_number & 3) != 2)
-            {
-               if (dsp_row != NULL && (png_ptr->row_number & 2))
-                  png_combine_row(png_ptr, dsp_row, 1/*display*/);
-
-               png_read_finish_row(png_ptr);
-               return;
-            }
-            break;
-
-         case 5:
-            if ((png_ptr->row_number & 1) || png_ptr->width < 2)
-            {
-               if (dsp_row != NULL)
-                  png_combine_row(png_ptr, dsp_row, 1/*display*/);
-
-               png_read_finish_row(png_ptr);
-               return;
-            }
-            break;
-
-         default:
-         case 6:
-            if ((png_ptr->row_number & 1) == 0)
-            {
-               png_read_finish_row(png_ptr);
-               return;
-            }
-            break;
-      }
-   }
-#endif
-
-   if ((png_ptr->mode & PNG_HAVE_IDAT) == 0)
-      png_error(png_ptr, "Invalid attempt to read row data");
-
-   /* Fill the row with IDAT data: */
-   png_ptr->row_buf[0]=255; /* to force error if no data was found */
-   png_read_IDAT_data(png_ptr, png_ptr->row_buf, row_info.rowbytes + 1);
-
-   if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE)
-   {
-      if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST)
-         png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1,
-             png_ptr->prev_row + 1, png_ptr->row_buf[0]);
-      else
-         png_error(png_ptr, "bad adaptive filter value");
-   }
-
-   /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before
-    * 1.5.6, while the buffer really is this big in current versions of libpng
-    * it may not be in the future, so this was changed just to copy the
-    * interlaced count:
-    */
-   memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1);
-
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
-       (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
-   {
-      /* Intrapixel differencing */
-      png_do_read_intrapixel(&row_info, png_ptr->row_buf + 1);
-   }
-#endif
-
-#ifdef PNG_READ_TRANSFORMS_SUPPORTED
-   if (png_ptr->transformations)
-      png_do_read_transformations(png_ptr, &row_info);
-#endif
-
-   /* The transformed pixel depth should match the depth now in row_info. */
-   if (png_ptr->transformed_pixel_depth == 0)
-   {
-      png_ptr->transformed_pixel_depth = row_info.pixel_depth;
-      if (row_info.pixel_depth > png_ptr->maximum_pixel_depth)
-         png_error(png_ptr, "sequential row overflow");
-   }
-
-   else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth)
-      png_error(png_ptr, "internal sequential row size calculation error");
-
-#ifdef PNG_READ_INTERLACING_SUPPORTED
-   /* Expand interlaced rows to full size */
-   if (png_ptr->interlaced != 0 &&
-      (png_ptr->transformations & PNG_INTERLACE) != 0)
-   {
-      if (png_ptr->pass < 6)
-         png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass,
-             png_ptr->transformations);
-
-      if (dsp_row != NULL)
-         png_combine_row(png_ptr, dsp_row, 1/*display*/);
-
-      if (row != NULL)
-         png_combine_row(png_ptr, row, 0/*row*/);
-   }
-
-   else
-#endif
-   {
-      if (row != NULL)
-         png_combine_row(png_ptr, row, -1/*ignored*/);
-
-      if (dsp_row != NULL)
-         png_combine_row(png_ptr, dsp_row, -1/*ignored*/);
-   }
-   png_read_finish_row(png_ptr);
-
-   if (png_ptr->read_row_fn != NULL)
-      (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
-
-}
-#endif /* SEQUENTIAL_READ */
-
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-/* Read one or more rows of image data.  If the image is interlaced,
- * and png_set_interlace_handling() has been called, the rows need to
- * contain the contents of the rows from the previous pass.  If the
- * image has alpha or transparency, and png_handle_alpha()[*] has been
- * called, the rows contents must be initialized to the contents of the
- * screen.
- *
- * "row" holds the actual image, and pixels are placed in it
- * as they arrive.  If the image is displayed after each pass, it will
- * appear to "sparkle" in.  "display_row" can be used to display a
- * "chunky" progressive image, with finer detail added as it becomes
- * available.  If you do not want this "chunky" display, you may pass
- * NULL for display_row.  If you do not want the sparkle display, and
- * you have not called png_handle_alpha(), you may pass NULL for rows.
- * If you have called png_handle_alpha(), and the image has either an
- * alpha channel or a transparency chunk, you must provide a buffer for
- * rows.  In this case, you do not have to provide a display_row buffer
- * also, but you may.  If the image is not interlaced, or if you have
- * not called png_set_interlace_handling(), the display_row buffer will
- * be ignored, so pass NULL to it.
- *
- * [*] png_handle_alpha() does not exist yet, as of this version of libpng
- */
-
-void PNGAPI
-png_read_rows(png_structrp png_ptr, png_bytepp row,
-    png_bytepp display_row, png_uint_32 num_rows)
-{
-   png_uint_32 i;
-   png_bytepp rp;
-   png_bytepp dp;
-
-   png_debug(1, "in png_read_rows");
-
-   if (png_ptr == NULL)
-      return;
-
-   rp = row;
-   dp = display_row;
-   if (rp != NULL && dp != NULL)
-      for (i = 0; i < num_rows; i++)
-      {
-         png_bytep rptr = *rp++;
-         png_bytep dptr = *dp++;
-
-         png_read_row(png_ptr, rptr, dptr);
-      }
-
-   else if (rp != NULL)
-      for (i = 0; i < num_rows; i++)
-      {
-         png_bytep rptr = *rp;
-         png_read_row(png_ptr, rptr, NULL);
-         rp++;
-      }
-
-   else if (dp != NULL)
-      for (i = 0; i < num_rows; i++)
-      {
-         png_bytep dptr = *dp;
-         png_read_row(png_ptr, NULL, dptr);
-         dp++;
-      }
-}
-#endif /* SEQUENTIAL_READ */
-
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-/* Read the entire image.  If the image has an alpha channel or a tRNS
- * chunk, and you have called png_handle_alpha()[*], you will need to
- * initialize the image to the current image that PNG will be overlaying.
- * We set the num_rows again here, in case it was incorrectly set in
- * png_read_start_row() by a call to png_read_update_info() or
- * png_start_read_image() if png_set_interlace_handling() wasn't called
- * prior to either of these functions like it should have been.  You can
- * only call this function once.  If you desire to have an image for
- * each pass of a interlaced image, use png_read_rows() instead.
- *
- * [*] png_handle_alpha() does not exist yet, as of this version of libpng
- */
-void PNGAPI
-png_read_image(png_structrp png_ptr, png_bytepp image)
-{
-   png_uint_32 i, image_height;
-   int pass, j;
-   png_bytepp rp;
-
-   png_debug(1, "in png_read_image");
-
-   if (png_ptr == NULL)
-      return;
-
-#ifdef PNG_READ_INTERLACING_SUPPORTED
-   if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0)
-   {
-      pass = png_set_interlace_handling(png_ptr);
-      /* And make sure transforms are initialized. */
-      png_start_read_image(png_ptr);
-   }
-   else
-   {
-      if (png_ptr->interlaced != 0 &&
-          (png_ptr->transformations & PNG_INTERLACE) == 0)
-      {
-         /* Caller called png_start_read_image or png_read_update_info without
-          * first turning on the PNG_INTERLACE transform.  We can fix this here,
-          * but the caller should do it!
-          */
-         png_warning(png_ptr, "Interlace handling should be turned on when "
-             "using png_read_image");
-         /* Make sure this is set correctly */
-         png_ptr->num_rows = png_ptr->height;
-      }
-
-      /* Obtain the pass number, which also turns on the PNG_INTERLACE flag in
-       * the above error case.
-       */
-      pass = png_set_interlace_handling(png_ptr);
-   }
-#else
-   if (png_ptr->interlaced)
-      png_error(png_ptr,
-          "Cannot read interlaced image -- interlace handler disabled");
-
-   pass = 1;
-#endif
-
-   image_height=png_ptr->height;
-
-   for (j = 0; j < pass; j++)
-   {
-      rp = image;
-      for (i = 0; i < image_height; i++)
-      {
-         png_read_row(png_ptr, *rp, NULL);
-         rp++;
-      }
-   }
-}
-#endif /* SEQUENTIAL_READ */
-
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-/* Read the end of the PNG file.  Will not read past the end of the
- * file, will verify the end is accurate, and will read any comments
- * or time information at the end of the file, if info is not NULL.
- */
-void PNGAPI
-png_read_end(png_structrp png_ptr, png_inforp info_ptr)
-{
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-   int keep;
-#endif
-
-   png_debug(1, "in png_read_end");
-
-   if (png_ptr == NULL)
-      return;
-
-   /* If png_read_end is called in the middle of reading the rows there may
-    * still be pending IDAT data and an owned zstream.  Deal with this here.
-    */
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-   if (png_chunk_unknown_handling(png_ptr, png_IDAT) == 0)
-#endif
-      png_read_finish_IDAT(png_ptr);
-
-#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
-   /* Report invalid palette index; added at libng-1.5.10 */
-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
-       png_ptr->num_palette_max > png_ptr->num_palette)
-      png_benign_error(png_ptr, "Read palette index exceeding num_palette");
-#endif
-
-   do
-   {
-      png_uint_32 length = png_read_chunk_header(png_ptr);
-      png_uint_32 chunk_name = png_ptr->chunk_name;
-
-      if (chunk_name != png_IDAT)
-         png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT;
-
-      if (chunk_name == png_IEND)
-         png_handle_IEND(png_ptr, info_ptr, length);
-
-      else if (chunk_name == png_IHDR)
-         png_handle_IHDR(png_ptr, info_ptr, length);
-
-      else if (info_ptr == NULL)
-         png_crc_finish(png_ptr, length);
-
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-      else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0)
-      {
-         if (chunk_name == png_IDAT)
-         {
-            if ((length > 0 && !(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED))
-                || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT) != 0)
-               png_benign_error(png_ptr, ".Too many IDATs found");
-         }
-         png_handle_unknown(png_ptr, info_ptr, length, keep);
-         if (chunk_name == png_PLTE)
-            png_ptr->mode |= PNG_HAVE_PLTE;
-      }
-#endif
-
-      else if (chunk_name == png_IDAT)
-      {
-         /* Zero length IDATs are legal after the last IDAT has been
-          * read, but not after other chunks have been read.  1.6 does not
-          * always read all the deflate data; specifically it cannot be relied
-          * upon to read the Adler32 at the end.  If it doesn't ignore IDAT
-          * chunks which are longer than zero as well:
-          */
-         if ((length > 0 && !(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED))
-             || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT) != 0)
-            png_benign_error(png_ptr, "..Too many IDATs found");
-
-         png_crc_finish(png_ptr, length);
-      }
-      else if (chunk_name == png_PLTE)
-         png_handle_PLTE(png_ptr, info_ptr, length);
-
-#ifdef PNG_READ_bKGD_SUPPORTED
-      else if (chunk_name == png_bKGD)
-         png_handle_bKGD(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_cHRM_SUPPORTED
-      else if (chunk_name == png_cHRM)
-         png_handle_cHRM(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_eXIf_SUPPORTED
-      else if (chunk_name == png_eXIf)
-         png_handle_eXIf(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_gAMA_SUPPORTED
-      else if (chunk_name == png_gAMA)
-         png_handle_gAMA(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_hIST_SUPPORTED
-      else if (chunk_name == png_hIST)
-         png_handle_hIST(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_oFFs_SUPPORTED
-      else if (chunk_name == png_oFFs)
-         png_handle_oFFs(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_pCAL_SUPPORTED
-      else if (chunk_name == png_pCAL)
-         png_handle_pCAL(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sCAL_SUPPORTED
-      else if (chunk_name == png_sCAL)
-         png_handle_sCAL(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_pHYs_SUPPORTED
-      else if (chunk_name == png_pHYs)
-         png_handle_pHYs(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sBIT_SUPPORTED
-      else if (chunk_name == png_sBIT)
-         png_handle_sBIT(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sRGB_SUPPORTED
-      else if (chunk_name == png_sRGB)
-         png_handle_sRGB(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_iCCP_SUPPORTED
-      else if (chunk_name == png_iCCP)
-         png_handle_iCCP(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sPLT_SUPPORTED
-      else if (chunk_name == png_sPLT)
-         png_handle_sPLT(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tEXt_SUPPORTED
-      else if (chunk_name == png_tEXt)
-         png_handle_tEXt(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tIME_SUPPORTED
-      else if (chunk_name == png_tIME)
-         png_handle_tIME(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tRNS_SUPPORTED
-      else if (chunk_name == png_tRNS)
-         png_handle_tRNS(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_zTXt_SUPPORTED
-      else if (chunk_name == png_zTXt)
-         png_handle_zTXt(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_iTXt_SUPPORTED
-      else if (chunk_name == png_iTXt)
-         png_handle_iTXt(png_ptr, info_ptr, length);
-#endif
-
-      else
-         png_handle_unknown(png_ptr, info_ptr, length,
-             PNG_HANDLE_CHUNK_AS_DEFAULT);
-   } while ((png_ptr->mode & PNG_HAVE_IEND) == 0);
-}
-#endif /* SEQUENTIAL_READ */
-
-/* Free all memory used in the read struct */
-static void
-png_read_destroy(png_structrp png_ptr)
-{
-   png_debug(1, "in png_read_destroy");
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
-   png_destroy_gamma_table(png_ptr);
-#endif
-
-   png_free(png_ptr, png_ptr->big_row_buf);
-   png_ptr->big_row_buf = NULL;
-   png_free(png_ptr, png_ptr->big_prev_row);
-   png_ptr->big_prev_row = NULL;
-   png_free(png_ptr, png_ptr->read_buffer);
-   png_ptr->read_buffer = NULL;
-
-#ifdef PNG_READ_QUANTIZE_SUPPORTED
-   png_free(png_ptr, png_ptr->palette_lookup);
-   png_ptr->palette_lookup = NULL;
-   png_free(png_ptr, png_ptr->quantize_index);
-   png_ptr->quantize_index = NULL;
-#endif
-
-   if ((png_ptr->free_me & PNG_FREE_PLTE) != 0)
-   {
-      png_zfree(png_ptr, png_ptr->palette);
-      png_ptr->palette = NULL;
-   }
-   png_ptr->free_me &= ~PNG_FREE_PLTE;
-
-#if defined(PNG_tRNS_SUPPORTED) || \
-    defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
-   if ((png_ptr->free_me & PNG_FREE_TRNS) != 0)
-   {
-      png_free(png_ptr, png_ptr->trans_alpha);
-      png_ptr->trans_alpha = NULL;
-   }
-   png_ptr->free_me &= ~PNG_FREE_TRNS;
-#endif
-
-   inflateEnd(&png_ptr->zstream);
-
-#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
-   png_free(png_ptr, png_ptr->save_buffer);
-   png_ptr->save_buffer = NULL;
-#endif
-
-#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) && \
-   defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
-   png_free(png_ptr, png_ptr->unknown_chunk.data);
-   png_ptr->unknown_chunk.data = NULL;
-#endif
-
-#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
-   png_free(png_ptr, png_ptr->chunk_list);
-   png_ptr->chunk_list = NULL;
-#endif
-
-#if defined(PNG_READ_EXPAND_SUPPORTED) && \
-    defined(PNG_ARM_NEON_IMPLEMENTATION)
-   png_free(png_ptr, png_ptr->riffled_palette);
-   png_ptr->riffled_palette = NULL;
-#endif
-
-   /* NOTE: the 'setjmp' buffer may still be allocated and the memory and error
-    * callbacks are still set at this point.  They are required to complete the
-    * destruction of the png_struct itself.
-    */
-}
-
-/* Free all memory used by the read */
-void PNGAPI
-png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr,
-    png_infopp end_info_ptr_ptr)
-{
-   png_structrp png_ptr = NULL;
-
-   png_debug(1, "in png_destroy_read_struct");
-
-   if (png_ptr_ptr != NULL)
-      png_ptr = *png_ptr_ptr;
-
-   if (png_ptr == NULL)
-      return;
-
-   /* libpng 1.6.0: use the API to destroy info structs to ensure consistent
-    * behavior.  Prior to 1.6.0 libpng did extra 'info' destruction in this API.
-    * The extra was, apparently, unnecessary yet this hides memory leak bugs.
-    */
-   png_destroy_info_struct(png_ptr, end_info_ptr_ptr);
-   png_destroy_info_struct(png_ptr, info_ptr_ptr);
-
-   *png_ptr_ptr = NULL;
-   png_read_destroy(png_ptr);
-   png_destroy_png_struct(png_ptr);
-}
-
-void PNGAPI
-png_set_read_status_fn(png_structrp png_ptr, png_read_status_ptr read_row_fn)
-{
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->read_row_fn = read_row_fn;
-}
-
-
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-#ifdef PNG_INFO_IMAGE_SUPPORTED
-void PNGAPI
-png_read_png(png_structrp png_ptr, png_inforp info_ptr,
-    int transforms, voidp params)
-{
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   /* png_read_info() gives us all of the information from the
-    * PNG file before the first IDAT (image data chunk).
-    */
-   png_read_info(png_ptr, info_ptr);
-   if (info_ptr->height > PNG_UINT_32_MAX/(sizeof (png_bytep)))
-      png_error(png_ptr, "Image is too high to process with png_read_png()");
-
-   /* -------------- image transformations start here ------------------- */
-   /* libpng 1.6.10: add code to cause a png_app_error if a selected TRANSFORM
-    * is not implemented.  This will only happen in de-configured (non-default)
-    * libpng builds.  The results can be unexpected - png_read_png may return
-    * short or mal-formed rows because the transform is skipped.
-    */
-
-   /* Tell libpng to strip 16-bit/color files down to 8 bits per color.
-    */
-   if ((transforms & PNG_TRANSFORM_SCALE_16) != 0)
-      /* Added at libpng-1.5.4. "strip_16" produces the same result that it
-       * did in earlier versions, while "scale_16" is now more accurate.
-       */
-#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
-      png_set_scale_16(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_SCALE_16 not supported");
-#endif
-
-   /* If both SCALE and STRIP are required pngrtran will effectively cancel the
-    * latter by doing SCALE first.  This is ok and allows apps not to check for
-    * which is supported to get the right answer.
-    */
-   if ((transforms & PNG_TRANSFORM_STRIP_16) != 0)
-#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
-      png_set_strip_16(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_STRIP_16 not supported");
-#endif
-
-   /* Strip alpha bytes from the input data without combining with
-    * the background (not recommended).
-    */
-   if ((transforms & PNG_TRANSFORM_STRIP_ALPHA) != 0)
-#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
-      png_set_strip_alpha(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_STRIP_ALPHA not supported");
-#endif
-
-   /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single
-    * byte into separate bytes (useful for paletted and grayscale images).
-    */
-   if ((transforms & PNG_TRANSFORM_PACKING) != 0)
-#ifdef PNG_READ_PACK_SUPPORTED
-      png_set_packing(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_PACKING not supported");
-#endif
-
-   /* Change the order of packed pixels to least significant bit first
-    * (not useful if you are using png_set_packing).
-    */
-   if ((transforms & PNG_TRANSFORM_PACKSWAP) != 0)
-#ifdef PNG_READ_PACKSWAP_SUPPORTED
-      png_set_packswap(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_PACKSWAP not supported");
-#endif
-
-   /* Expand paletted colors into true RGB triplets
-    * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel
-    * Expand paletted or RGB images with transparency to full alpha
-    * channels so the data will be available as RGBA quartets.
-    */
-   if ((transforms & PNG_TRANSFORM_EXPAND) != 0)
-#ifdef PNG_READ_EXPAND_SUPPORTED
-      png_set_expand(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_EXPAND not supported");
-#endif
-
-   /* We don't handle background color or gamma transformation or quantizing.
-    */
-
-   /* Invert monochrome files to have 0 as white and 1 as black
-    */
-   if ((transforms & PNG_TRANSFORM_INVERT_MONO) != 0)
-#ifdef PNG_READ_INVERT_SUPPORTED
-      png_set_invert_mono(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_INVERT_MONO not supported");
-#endif
-
-   /* If you want to shift the pixel values from the range [0,255] or
-    * [0,65535] to the original [0,7] or [0,31], or whatever range the
-    * colors were originally in:
-    */
-   if ((transforms & PNG_TRANSFORM_SHIFT) != 0)
-#ifdef PNG_READ_SHIFT_SUPPORTED
-      if ((info_ptr->valid & PNG_INFO_sBIT) != 0)
-         png_set_shift(png_ptr, &info_ptr->sig_bit);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_SHIFT not supported");
-#endif
-
-   /* Flip the RGB pixels to BGR (or RGBA to BGRA) */
-   if ((transforms & PNG_TRANSFORM_BGR) != 0)
-#ifdef PNG_READ_BGR_SUPPORTED
-      png_set_bgr(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_BGR not supported");
-#endif
-
-   /* Swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */
-   if ((transforms & PNG_TRANSFORM_SWAP_ALPHA) != 0)
-#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
-      png_set_swap_alpha(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_SWAP_ALPHA not supported");
-#endif
-
-   /* Swap bytes of 16-bit files to least significant byte first */
-   if ((transforms & PNG_TRANSFORM_SWAP_ENDIAN) != 0)
-#ifdef PNG_READ_SWAP_SUPPORTED
-      png_set_swap(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_SWAP_ENDIAN not supported");
-#endif
-
-/* Added at libpng-1.2.41 */
-   /* Invert the alpha channel from opacity to transparency */
-   if ((transforms & PNG_TRANSFORM_INVERT_ALPHA) != 0)
-#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
-      png_set_invert_alpha(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_INVERT_ALPHA not supported");
-#endif
-
-/* Added at libpng-1.2.41 */
-   /* Expand grayscale image to RGB */
-   if ((transforms & PNG_TRANSFORM_GRAY_TO_RGB) != 0)
-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
-      png_set_gray_to_rgb(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_GRAY_TO_RGB not supported");
-#endif
-
-/* Added at libpng-1.5.4 */
-   if ((transforms & PNG_TRANSFORM_EXPAND_16) != 0)
-#ifdef PNG_READ_EXPAND_16_SUPPORTED
-      png_set_expand_16(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_EXPAND_16 not supported");
-#endif
-
-   /* We don't handle adding filler bytes */
-
-   /* We use png_read_image and rely on that for interlace handling, but we also
-    * call png_read_update_info therefore must turn on interlace handling now:
-    */
-   (void)png_set_interlace_handling(png_ptr);
-
-   /* Optional call to gamma correct and add the background to the palette
-    * and update info structure.  REQUIRED if you are expecting libpng to
-    * update the palette for you (i.e., you selected such a transform above).
-    */
-   png_read_update_info(png_ptr, info_ptr);
-
-   /* -------------- image transformations end here ------------------- */
-
-   png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0);
-   if (info_ptr->row_pointers == NULL)
-   {
-      png_uint_32 iptr;
-
-      info_ptr->row_pointers = png_voidcast(png_bytepp, png_malloc(png_ptr,
-          info_ptr->height * (sizeof (png_bytep))));
-
-      for (iptr=0; iptr<info_ptr->height; iptr++)
-         info_ptr->row_pointers[iptr] = NULL;
-
-      info_ptr->free_me |= PNG_FREE_ROWS;
-
-      for (iptr = 0; iptr < info_ptr->height; iptr++)
-         info_ptr->row_pointers[iptr] = png_voidcast(png_bytep,
-             png_malloc(png_ptr, info_ptr->rowbytes));
-   }
-
-   png_read_image(png_ptr, info_ptr->row_pointers);
-   info_ptr->valid |= PNG_INFO_IDAT;
-
-   /* Read rest of file, and get additional chunks in info_ptr - REQUIRED */
-   png_read_end(png_ptr, info_ptr);
-
-   PNG_UNUSED(params)
-}
-#endif /* INFO_IMAGE */
-#endif /* SEQUENTIAL_READ */
-
-#ifdef PNG_SIMPLIFIED_READ_SUPPORTED
-/* SIMPLIFIED READ
- *
- * This code currently relies on the sequential reader, though it could easily
- * be made to work with the progressive one.
- */
-/* Arguments to png_image_finish_read: */
-
-/* Encoding of PNG data (used by the color-map code) */
-#  define P_NOTSET  0 /* File encoding not yet known */
-#  define P_sRGB    1 /* 8-bit encoded to sRGB gamma */
-#  define P_LINEAR  2 /* 16-bit linear: not encoded, NOT pre-multiplied! */
-#  define P_FILE    3 /* 8-bit encoded to file gamma, not sRGB or linear */
-#  define P_LINEAR8 4 /* 8-bit linear: only from a file value */
-
-/* Color-map processing: after libpng has run on the PNG image further
- * processing may be needed to convert the data to color-map indices.
- */
-#define PNG_CMAP_NONE      0
-#define PNG_CMAP_GA        1 /* Process GA data to a color-map with alpha */
-#define PNG_CMAP_TRANS     2 /* Process GA data to a background index */
-#define PNG_CMAP_RGB       3 /* Process RGB data */
-#define PNG_CMAP_RGB_ALPHA 4 /* Process RGBA data */
-
-/* The following document where the background is for each processing case. */
-#define PNG_CMAP_NONE_BACKGROUND      256
-#define PNG_CMAP_GA_BACKGROUND        231
-#define PNG_CMAP_TRANS_BACKGROUND     254
-#define PNG_CMAP_RGB_BACKGROUND       256
-#define PNG_CMAP_RGB_ALPHA_BACKGROUND 216
-
-typedef struct
-{
-   /* Arguments: */
-   png_imagep image;
-   png_voidp  buffer;
-   png_int_32 row_stride;
-   png_voidp  colormap;
-   png_const_colorp background;
-   /* Local variables: */
-   png_voidp       local_row;
-   png_voidp       first_row;
-   ptrdiff_t       row_bytes;           /* step between rows */
-   int             file_encoding;       /* E_ values above */
-   png_fixed_point gamma_to_linear;     /* For P_FILE, reciprocal of gamma */
-   int             colormap_processing; /* PNG_CMAP_ values above */
-} png_image_read_control;
-
-/* Do all the *safe* initialization - 'safe' means that png_error won't be
- * called, so setting up the jmp_buf is not required.  This means that anything
- * called from here must *not* call png_malloc - it has to call png_malloc_warn
- * instead so that control is returned safely back to this routine.
- */
-static int
-png_image_read_init(png_imagep image)
-{
-   if (image->opaque == NULL)
-   {
-      png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, image,
-          png_safe_error, png_safe_warning);
-
-      /* And set the rest of the structure to NULL to ensure that the various
-       * fields are consistent.
-       */
-      memset(image, 0, (sizeof *image));
-      image->version = PNG_IMAGE_VERSION;
-
-      if (png_ptr != NULL)
-      {
-         png_infop info_ptr = png_create_info_struct(png_ptr);
-
-         if (info_ptr != NULL)
-         {
-            png_controlp control = png_voidcast(png_controlp,
-                png_malloc_warn(png_ptr, (sizeof *control)));
-
-            if (control != NULL)
-            {
-               memset(control, 0, (sizeof *control));
-
-               control->png_ptr = png_ptr;
-               control->info_ptr = info_ptr;
-               control->for_write = 0;
-
-               image->opaque = control;
-               return 1;
-            }
-
-            /* Error clean up */
-            png_destroy_info_struct(png_ptr, &info_ptr);
-         }
-
-         png_destroy_read_struct(&png_ptr, NULL, NULL);
-      }
-
-      return png_image_error(image, "png_image_read: out of memory");
-   }
-
-   return png_image_error(image, "png_image_read: opaque pointer not NULL");
-}
-
-/* Utility to find the base format of a PNG file from a png_struct. */
-static png_uint_32
-png_image_format(png_structrp png_ptr)
-{
-   png_uint_32 format = 0;
-
-   if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
-      format |= PNG_FORMAT_FLAG_COLOR;
-
-   if ((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0)
-      format |= PNG_FORMAT_FLAG_ALPHA;
-
-   /* Use png_ptr here, not info_ptr, because by examination png_handle_tRNS
-    * sets the png_struct fields; that's all we are interested in here.  The
-    * precise interaction with an app call to png_set_tRNS and PNG file reading
-    * is unclear.
-    */
-   else if (png_ptr->num_trans > 0)
-      format |= PNG_FORMAT_FLAG_ALPHA;
-
-   if (png_ptr->bit_depth == 16)
-      format |= PNG_FORMAT_FLAG_LINEAR;
-
-   if ((png_ptr->color_type & PNG_COLOR_MASK_PALETTE) != 0)
-      format |= PNG_FORMAT_FLAG_COLORMAP;
-
-   return format;
-}
-
-/* Is the given gamma significantly different from sRGB?  The test is the same
- * one used in pngrtran.c when deciding whether to do gamma correction.  The
- * arithmetic optimizes the division by using the fact that the inverse of the
- * file sRGB gamma is 2.2
- */
-static int
-png_gamma_not_sRGB(png_fixed_point g)
-{
-   if (g < PNG_FP_1)
-   {
-      /* An uninitialized gamma is assumed to be sRGB for the simplified API. */
-      if (g == 0)
-         return 0;
-
-      return png_gamma_significant((g * 11 + 2)/5 /* i.e. *2.2, rounded */);
-   }
-
-   return 1;
-}
-
-/* Do the main body of a 'png_image_begin_read' function; read the PNG file
- * header and fill in all the information.  This is executed in a safe context,
- * unlike the init routine above.
- */
-static int
-png_image_read_header(png_voidp argument)
-{
-   png_imagep image = png_voidcast(png_imagep, argument);
-   png_structrp png_ptr = image->opaque->png_ptr;
-   png_inforp info_ptr = image->opaque->info_ptr;
-
-#ifdef PNG_BENIGN_ERRORS_SUPPORTED
-   png_set_benign_errors(png_ptr, 1/*warn*/);
-#endif
-   png_read_info(png_ptr, info_ptr);
-
-   /* Do this the fast way; just read directly out of png_struct. */
-   image->width = png_ptr->width;
-   image->height = png_ptr->height;
-
-   {
-      png_uint_32 format = png_image_format(png_ptr);
-
-      image->format = format;
-
-#ifdef PNG_COLORSPACE_SUPPORTED
-      /* Does the colorspace match sRGB?  If there is no color endpoint
-       * (colorant) information assume yes, otherwise require the
-       * 'ENDPOINTS_MATCHP_sRGB' colorspace flag to have been set.  If the
-       * colorspace has been determined to be invalid ignore it.
-       */
-      if ((format & PNG_FORMAT_FLAG_COLOR) != 0 && ((png_ptr->colorspace.flags
-         & (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB|
-            PNG_COLORSPACE_INVALID)) == PNG_COLORSPACE_HAVE_ENDPOINTS))
-         image->flags |= PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB;
-#endif
-   }
-
-   /* We need the maximum number of entries regardless of the format the
-    * application sets here.
-    */
-   {
-      png_uint_32 cmap_entries;
-
-      switch (png_ptr->color_type)
-      {
-         case PNG_COLOR_TYPE_GRAY:
-            cmap_entries = 1U << png_ptr->bit_depth;
-            break;
-
-         case PNG_COLOR_TYPE_PALETTE:
-            cmap_entries = (png_uint_32)png_ptr->num_palette;
-            break;
-
-         default:
-            cmap_entries = 256;
-            break;
-      }
-
-      if (cmap_entries > 256)
-         cmap_entries = 256;
-
-      image->colormap_entries = cmap_entries;
-   }
-
-   return 1;
-}
-
-#ifdef PNG_STDIO_SUPPORTED
-int PNGAPI
-png_image_begin_read_from_stdio(png_imagep image, FILE* file)
-{
-   if (image != NULL && image->version == PNG_IMAGE_VERSION)
-   {
-      if (file != NULL)
-      {
-         if (png_image_read_init(image) != 0)
-         {
-            /* This is slightly evil, but png_init_io doesn't do anything other
-             * than this and we haven't changed the standard IO functions so
-             * this saves a 'safe' function.
-             */
-            image->opaque->png_ptr->io_ptr = file;
-            return png_safe_execute(image, png_image_read_header, image);
-         }
-      }
-
-      else
-         return png_image_error(image,
-             "png_image_begin_read_from_stdio: invalid argument");
-   }
-
-   else if (image != NULL)
-      return png_image_error(image,
-          "png_image_begin_read_from_stdio: incorrect PNG_IMAGE_VERSION");
-
-   return 0;
-}
-
-int PNGAPI
-png_image_begin_read_from_file(png_imagep image, const char *file_name)
-{
-   if (image != NULL && image->version == PNG_IMAGE_VERSION)
-   {
-      if (file_name != NULL)
-      {
-         FILE *fp = fopen(file_name, "rb");
-
-         if (fp != NULL)
-         {
-            if (png_image_read_init(image) != 0)
-            {
-               image->opaque->png_ptr->io_ptr = fp;
-               image->opaque->owned_file = 1;
-               return png_safe_execute(image, png_image_read_header, image);
-            }
-
-            /* Clean up: just the opened file. */
-            (void)fclose(fp);
-         }
-
-         else
-            return png_image_error(image, strerror(errno));
-      }
-
-      else
-         return png_image_error(image,
-             "png_image_begin_read_from_file: invalid argument");
-   }
-
-   else if (image != NULL)
-      return png_image_error(image,
-          "png_image_begin_read_from_file: incorrect PNG_IMAGE_VERSION");
-
-   return 0;
-}
-#endif /* STDIO */
-
-static void PNGCBAPI
-png_image_memory_read(png_structp png_ptr, png_bytep out, size_t need)
-{
-   if (png_ptr != NULL)
-   {
-      png_imagep image = png_voidcast(png_imagep, png_ptr->io_ptr);
-      if (image != NULL)
-      {
-         png_controlp cp = image->opaque;
-         if (cp != NULL)
-         {
-            png_const_bytep memory = cp->memory;
-            size_t size = cp->size;
-
-            if (memory != NULL && size >= need)
-            {
-               memcpy(out, memory, need);
-               cp->memory = memory + need;
-               cp->size = size - need;
-               return;
-            }
-
-            png_error(png_ptr, "read beyond end of data");
-         }
-      }
-
-      png_error(png_ptr, "invalid memory read");
-   }
-}
-
-int PNGAPI png_image_begin_read_from_memory(png_imagep image,
-    png_const_voidp memory, size_t size)
-{
-   if (image != NULL && image->version == PNG_IMAGE_VERSION)
-   {
-      if (memory != NULL && size > 0)
-      {
-         if (png_image_read_init(image) != 0)
-         {
-            /* Now set the IO functions to read from the memory buffer and
-             * store it into io_ptr.  Again do this in-place to avoid calling a
-             * libpng function that requires error handling.
-             */
-            image->opaque->memory = png_voidcast(png_const_bytep, memory);
-            image->opaque->size = size;
-            image->opaque->png_ptr->io_ptr = image;
-            image->opaque->png_ptr->read_data_fn = png_image_memory_read;
-
-            return png_safe_execute(image, png_image_read_header, image);
-         }
-      }
-
-      else
-         return png_image_error(image,
-             "png_image_begin_read_from_memory: invalid argument");
-   }
-
-   else if (image != NULL)
-      return png_image_error(image,
-          "png_image_begin_read_from_memory: incorrect PNG_IMAGE_VERSION");
-
-   return 0;
-}
-
-/* Utility function to skip chunks that are not used by the simplified image
- * read functions and an appropriate macro to call it.
- */
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-static void
-png_image_skip_unused_chunks(png_structrp png_ptr)
-{
-   /* Prepare the reader to ignore all recognized chunks whose data will not
-    * be used, i.e., all chunks recognized by libpng except for those
-    * involved in basic image reading:
-    *
-    *    IHDR, PLTE, IDAT, IEND
-    *
-    * Or image data handling:
-    *
-    *    tRNS, bKGD, gAMA, cHRM, sRGB, [iCCP] and sBIT.
-    *
-    * This provides a small performance improvement and eliminates any
-    * potential vulnerability to security problems in the unused chunks.
-    *
-    * At present the iCCP chunk data isn't used, so iCCP chunk can be ignored
-    * too.  This allows the simplified API to be compiled without iCCP support,
-    * however if the support is there the chunk is still checked to detect
-    * errors (which are unfortunately quite common.)
-    */
-   {
-         static const png_byte chunks_to_process[] = {
-            98,  75,  71,  68, '\0',  /* bKGD */
-            99,  72,  82,  77, '\0',  /* cHRM */
-           103,  65,  77,  65, '\0',  /* gAMA */
-#        ifdef PNG_READ_iCCP_SUPPORTED
-           105,  67,  67,  80, '\0',  /* iCCP */
-#        endif
-           115,  66,  73,  84, '\0',  /* sBIT */
-           115,  82,  71,  66, '\0',  /* sRGB */
-           };
-
-       /* Ignore unknown chunks and all other chunks except for the
-        * IHDR, PLTE, tRNS, IDAT, and IEND chunks.
-        */
-       png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_NEVER,
-           NULL, -1);
-
-       /* But do not ignore image data handling chunks */
-       png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_AS_DEFAULT,
-           chunks_to_process, (int)/*SAFE*/(sizeof chunks_to_process)/5);
-   }
-}
-
-#  define PNG_SKIP_CHUNKS(p) png_image_skip_unused_chunks(p)
-#else
-#  define PNG_SKIP_CHUNKS(p) ((void)0)
-#endif /* HANDLE_AS_UNKNOWN */
-
-/* The following macro gives the exact rounded answer for all values in the
- * range 0..255 (it actually divides by 51.2, but the rounding still generates
- * the correct numbers 0..5
- */
-#define PNG_DIV51(v8) (((v8) * 5 + 130) >> 8)
-
-/* Utility functions to make particular color-maps */
-static void
-set_file_encoding(png_image_read_control *display)
-{
-   png_fixed_point g = display->image->opaque->png_ptr->colorspace.gamma;
-   if (png_gamma_significant(g) != 0)
-   {
-      if (png_gamma_not_sRGB(g) != 0)
-      {
-         display->file_encoding = P_FILE;
-         display->gamma_to_linear = png_reciprocal(g);
-      }
-
-      else
-         display->file_encoding = P_sRGB;
-   }
-
-   else
-      display->file_encoding = P_LINEAR8;
-}
-
-static unsigned int
-decode_gamma(png_image_read_control *display, png_uint_32 value, int encoding)
-{
-   if (encoding == P_FILE) /* double check */
-      encoding = display->file_encoding;
-
-   if (encoding == P_NOTSET) /* must be the file encoding */
-   {
-      set_file_encoding(display);
-      encoding = display->file_encoding;
-   }
-
-   switch (encoding)
-   {
-      case P_FILE:
-         value = png_gamma_16bit_correct(value*257, display->gamma_to_linear);
-         break;
-
-      case P_sRGB:
-         value = png_sRGB_table[value];
-         break;
-
-      case P_LINEAR:
-         break;
-
-      case P_LINEAR8:
-         value *= 257;
-         break;
-
-#ifdef __GNUC__
-      default:
-         png_error(display->image->opaque->png_ptr,
-             "unexpected encoding (internal error)");
-#endif
-   }
-
-   return value;
-}
-
-static png_uint_32
-png_colormap_compose(png_image_read_control *display,
-    png_uint_32 foreground, int foreground_encoding, png_uint_32 alpha,
-    png_uint_32 background, int encoding)
-{
-   /* The file value is composed on the background, the background has the given
-    * encoding and so does the result, the file is encoded with P_FILE and the
-    * file and alpha are 8-bit values.  The (output) encoding will always be
-    * P_LINEAR or P_sRGB.
-    */
-   png_uint_32 f = decode_gamma(display, foreground, foreground_encoding);
-   png_uint_32 b = decode_gamma(display, background, encoding);
-
-   /* The alpha is always an 8-bit value (it comes from the palette), the value
-    * scaled by 255 is what PNG_sRGB_FROM_LINEAR requires.
-    */
-   f = f * alpha + b * (255-alpha);
-
-   if (encoding == P_LINEAR)
-   {
-      /* Scale to 65535; divide by 255, approximately (in fact this is extremely
-       * accurate, it divides by 255.00000005937181414556, with no overflow.)
-       */
-      f *= 257; /* Now scaled by 65535 */
-      f += f >> 16;
-      f = (f+32768) >> 16;
-   }
-
-   else /* P_sRGB */
-      f = PNG_sRGB_FROM_LINEAR(f);
-
-   return f;
-}
-
-/* NOTE: P_LINEAR values to this routine must be 16-bit, but P_FILE values must
- * be 8-bit.
- */
-static void
-png_create_colormap_entry(png_image_read_control *display,
-    png_uint_32 ip, png_uint_32 red, png_uint_32 green, png_uint_32 blue,
-    png_uint_32 alpha, int encoding)
-{
-   png_imagep image = display->image;
-   int output_encoding = (image->format & PNG_FORMAT_FLAG_LINEAR) != 0 ?
-       P_LINEAR : P_sRGB;
-   int convert_to_Y = (image->format & PNG_FORMAT_FLAG_COLOR) == 0 &&
-       (red != green || green != blue);
-
-   if (ip > 255)
-      png_error(image->opaque->png_ptr, "color-map index out of range");
-
-   /* Update the cache with whether the file gamma is significantly different
-    * from sRGB.
-    */
-   if (encoding == P_FILE)
-   {
-      if (display->file_encoding == P_NOTSET)
-         set_file_encoding(display);
-
-      /* Note that the cached value may be P_FILE too, but if it is then the
-       * gamma_to_linear member has been set.
-       */
-      encoding = display->file_encoding;
-   }
-
-   if (encoding == P_FILE)
-   {
-      png_fixed_point g = display->gamma_to_linear;
-
-      red = png_gamma_16bit_correct(red*257, g);
-      green = png_gamma_16bit_correct(green*257, g);
-      blue = png_gamma_16bit_correct(blue*257, g);
-
-      if (convert_to_Y != 0 || output_encoding == P_LINEAR)
-      {
-         alpha *= 257;
-         encoding = P_LINEAR;
-      }
-
-      else
-      {
-         red = PNG_sRGB_FROM_LINEAR(red * 255);
-         green = PNG_sRGB_FROM_LINEAR(green * 255);
-         blue = PNG_sRGB_FROM_LINEAR(blue * 255);
-         encoding = P_sRGB;
-      }
-   }
-
-   else if (encoding == P_LINEAR8)
-   {
-      /* This encoding occurs quite frequently in test cases because PngSuite
-       * includes a gAMA 1.0 chunk with most images.
-       */
-      red *= 257;
-      green *= 257;
-      blue *= 257;
-      alpha *= 257;
-      encoding = P_LINEAR;
-   }
-
-   else if (encoding == P_sRGB &&
-       (convert_to_Y  != 0 || output_encoding == P_LINEAR))
-   {
-      /* The values are 8-bit sRGB values, but must be converted to 16-bit
-       * linear.
-       */
-      red = png_sRGB_table[red];
-      green = png_sRGB_table[green];
-      blue = png_sRGB_table[blue];
-      alpha *= 257;
-      encoding = P_LINEAR;
-   }
-
-   /* This is set if the color isn't gray but the output is. */
-   if (encoding == P_LINEAR)
-   {
-      if (convert_to_Y != 0)
-      {
-         /* NOTE: these values are copied from png_do_rgb_to_gray */
-         png_uint_32 y = (png_uint_32)6968 * red  + (png_uint_32)23434 * green +
-            (png_uint_32)2366 * blue;
-
-         if (output_encoding == P_LINEAR)
-            y = (y + 16384) >> 15;
-
-         else
-         {
-            /* y is scaled by 32768, we need it scaled by 255: */
-            y = (y + 128) >> 8;
-            y *= 255;
-            y = PNG_sRGB_FROM_LINEAR((y + 64) >> 7);
-            alpha = PNG_DIV257(alpha);
-            encoding = P_sRGB;
-         }
-
-         blue = red = green = y;
-      }
-
-      else if (output_encoding == P_sRGB)
-      {
-         red = PNG_sRGB_FROM_LINEAR(red * 255);
-         green = PNG_sRGB_FROM_LINEAR(green * 255);
-         blue = PNG_sRGB_FROM_LINEAR(blue * 255);
-         alpha = PNG_DIV257(alpha);
-         encoding = P_sRGB;
-      }
-   }
-
-   if (encoding != output_encoding)
-      png_error(image->opaque->png_ptr, "bad encoding (internal error)");
-
-   /* Store the value. */
-   {
-#     ifdef PNG_FORMAT_AFIRST_SUPPORTED
-         int afirst = (image->format & PNG_FORMAT_FLAG_AFIRST) != 0 &&
-            (image->format & PNG_FORMAT_FLAG_ALPHA) != 0;
-#     else
-#        define afirst 0
-#     endif
-#     ifdef PNG_FORMAT_BGR_SUPPORTED
-         int bgr = (image->format & PNG_FORMAT_FLAG_BGR) != 0 ? 2 : 0;
-#     else
-#        define bgr 0
-#     endif
-
-      if (output_encoding == P_LINEAR)
-      {
-         png_uint_16p entry = png_voidcast(png_uint_16p, display->colormap);
-
-         entry += ip * PNG_IMAGE_SAMPLE_CHANNELS(image->format);
-
-         /* The linear 16-bit values must be pre-multiplied by the alpha channel
-          * value, if less than 65535 (this is, effectively, composite on black
-          * if the alpha channel is removed.)
-          */
-         switch (PNG_IMAGE_SAMPLE_CHANNELS(image->format))
-         {
-            case 4:
-               entry[afirst ? 0 : 3] = (png_uint_16)alpha;
-               /* FALLTHROUGH */
-
-            case 3:
-               if (alpha < 65535)
-               {
-                  if (alpha > 0)
-                  {
-                     blue = (blue * alpha + 32767U)/65535U;
-                     green = (green * alpha + 32767U)/65535U;
-                     red = (red * alpha + 32767U)/65535U;
-                  }
-
-                  else
-                     red = green = blue = 0;
-               }
-               entry[afirst + (2 ^ bgr)] = (png_uint_16)blue;
-               entry[afirst + 1] = (png_uint_16)green;
-               entry[afirst + bgr] = (png_uint_16)red;
-               break;
-
-            case 2:
-               entry[1 ^ afirst] = (png_uint_16)alpha;
-               /* FALLTHROUGH */
-
-            case 1:
-               if (alpha < 65535)
-               {
-                  if (alpha > 0)
-                     green = (green * alpha + 32767U)/65535U;
-
-                  else
-                     green = 0;
-               }
-               entry[afirst] = (png_uint_16)green;
-               break;
-
-            default:
-               break;
-         }
-      }
-
-      else /* output encoding is P_sRGB */
-      {
-         png_bytep entry = png_voidcast(png_bytep, display->colormap);
-
-         entry += ip * PNG_IMAGE_SAMPLE_CHANNELS(image->format);
-
-         switch (PNG_IMAGE_SAMPLE_CHANNELS(image->format))
-         {
-            case 4:
-               entry[afirst ? 0 : 3] = (png_byte)alpha;
-               /* FALLTHROUGH */
-            case 3:
-               entry[afirst + (2 ^ bgr)] = (png_byte)blue;
-               entry[afirst + 1] = (png_byte)green;
-               entry[afirst + bgr] = (png_byte)red;
-               break;
-
-            case 2:
-               entry[1 ^ afirst] = (png_byte)alpha;
-               /* FALLTHROUGH */
-            case 1:
-               entry[afirst] = (png_byte)green;
-               break;
-
-            default:
-               break;
-         }
-      }
-
-#     ifdef afirst
-#        undef afirst
-#     endif
-#     ifdef bgr
-#        undef bgr
-#     endif
-   }
-}
-
-static int
-make_gray_file_colormap(png_image_read_control *display)
-{
-   unsigned int i;
-
-   for (i=0; i<256; ++i)
-      png_create_colormap_entry(display, i, i, i, i, 255, P_FILE);
-
-   return (int)i;
-}
-
-static int
-make_gray_colormap(png_image_read_control *display)
-{
-   unsigned int i;
-
-   for (i=0; i<256; ++i)
-      png_create_colormap_entry(display, i, i, i, i, 255, P_sRGB);
-
-   return (int)i;
-}
-#define PNG_GRAY_COLORMAP_ENTRIES 256
-
-static int
-make_ga_colormap(png_image_read_control *display)
-{
-   unsigned int i, a;
-
-   /* Alpha is retained, the output will be a color-map with entries
-    * selected by six levels of alpha.  One transparent entry, 6 gray
-    * levels for all the intermediate alpha values, leaving 230 entries
-    * for the opaque grays.  The color-map entries are the six values
-    * [0..5]*51, the GA processing uses PNG_DIV51(value) to find the
-    * relevant entry.
-    *
-    * if (alpha > 229) // opaque
-    * {
-    *    // The 231 entries are selected to make the math below work:
-    *    base = 0;
-    *    entry = (231 * gray + 128) >> 8;
-    * }
-    * else if (alpha < 26) // transparent
-    * {
-    *    base = 231;
-    *    entry = 0;
-    * }
-    * else // partially opaque
-    * {
-    *    base = 226 + 6 * PNG_DIV51(alpha);
-    *    entry = PNG_DIV51(gray);
-    * }
-    */
-   i = 0;
-   while (i < 231)
-   {
-      unsigned int gray = (i * 256 + 115) / 231;
-      png_create_colormap_entry(display, i++, gray, gray, gray, 255, P_sRGB);
-   }
-
-   /* 255 is used here for the component values for consistency with the code
-    * that undoes premultiplication in pngwrite.c.
-    */
-   png_create_colormap_entry(display, i++, 255, 255, 255, 0, P_sRGB);
-
-   for (a=1; a<5; ++a)
-   {
-      unsigned int g;
-
-      for (g=0; g<6; ++g)
-         png_create_colormap_entry(display, i++, g*51, g*51, g*51, a*51,
-             P_sRGB);
-   }
-
-   return (int)i;
-}
-
-#define PNG_GA_COLORMAP_ENTRIES 256
-
-static int
-make_rgb_colormap(png_image_read_control *display)
-{
-   unsigned int i, r;
-
-   /* Build a 6x6x6 opaque RGB cube */
-   for (i=r=0; r<6; ++r)
-   {
-      unsigned int g;
-
-      for (g=0; g<6; ++g)
-      {
-         unsigned int b;
-
-         for (b=0; b<6; ++b)
-            png_create_colormap_entry(display, i++, r*51, g*51, b*51, 255,
-                P_sRGB);
-      }
-   }
-
-   return (int)i;
-}
-
-#define PNG_RGB_COLORMAP_ENTRIES 216
-
-/* Return a palette index to the above palette given three 8-bit sRGB values. */
-#define PNG_RGB_INDEX(r,g,b) \
-   ((png_byte)(6 * (6 * PNG_DIV51(r) + PNG_DIV51(g)) + PNG_DIV51(b)))
-
-static int
-png_image_read_colormap(png_voidp argument)
-{
-   png_image_read_control *display =
-      png_voidcast(png_image_read_control*, argument);
-   png_imagep image = display->image;
-
-   png_structrp png_ptr = image->opaque->png_ptr;
-   png_uint_32 output_format = image->format;
-   int output_encoding = (output_format & PNG_FORMAT_FLAG_LINEAR) != 0 ?
-      P_LINEAR : P_sRGB;
-
-   unsigned int cmap_entries;
-   unsigned int output_processing;        /* Output processing option */
-   unsigned int data_encoding = P_NOTSET; /* Encoding libpng must produce */
-
-   /* Background information; the background color and the index of this color
-    * in the color-map if it exists (else 256).
-    */
-   unsigned int background_index = 256;
-   png_uint_32 back_r, back_g, back_b;
-
-   /* Flags to accumulate things that need to be done to the input. */
-   int expand_tRNS = 0;
-
-   /* Exclude the NYI feature of compositing onto a color-mapped buffer; it is
-    * very difficult to do, the results look awful, and it is difficult to see
-    * what possible use it is because the application can't control the
-    * color-map.
-    */
-   if (((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0 ||
-         png_ptr->num_trans > 0) /* alpha in input */ &&
-      ((output_format & PNG_FORMAT_FLAG_ALPHA) == 0) /* no alpha in output */)
-   {
-      if (output_encoding == P_LINEAR) /* compose on black */
-         back_b = back_g = back_r = 0;
-
-      else if (display->background == NULL /* no way to remove it */)
-         png_error(png_ptr,
-             "background color must be supplied to remove alpha/transparency");
-
-      /* Get a copy of the background color (this avoids repeating the checks
-       * below.)  The encoding is 8-bit sRGB or 16-bit linear, depending on the
-       * output format.
-       */
-      else
-      {
-         back_g = display->background->green;
-         if ((output_format & PNG_FORMAT_FLAG_COLOR) != 0)
-         {
-            back_r = display->background->red;
-            back_b = display->background->blue;
-         }
-         else
-            back_b = back_r = back_g;
-      }
-   }
-
-   else if (output_encoding == P_LINEAR)
-      back_b = back_r = back_g = 65535;
-
-   else
-      back_b = back_r = back_g = 255;
-
-   /* Default the input file gamma if required - this is necessary because
-    * libpng assumes that if no gamma information is present the data is in the
-    * output format, but the simplified API deduces the gamma from the input
-    * format.
-    */
-   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) == 0)
-   {
-      /* Do this directly, not using the png_colorspace functions, to ensure
-       * that it happens even if the colorspace is invalid (though probably if
-       * it is the setting will be ignored)  Note that the same thing can be
-       * achieved at the application interface with png_set_gAMA.
-       */
-      if (png_ptr->bit_depth == 16 &&
-         (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0)
-         png_ptr->colorspace.gamma = PNG_GAMMA_LINEAR;
-
-      else
-         png_ptr->colorspace.gamma = PNG_GAMMA_sRGB_INVERSE;
-
-      png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
-   }
-
-   /* Decide what to do based on the PNG color type of the input data.  The
-    * utility function png_create_colormap_entry deals with most aspects of the
-    * output transformations; this code works out how to produce bytes of
-    * color-map entries from the original format.
-    */
-   switch (png_ptr->color_type)
-   {
-      case PNG_COLOR_TYPE_GRAY:
-         if (png_ptr->bit_depth <= 8)
-         {
-            /* There at most 256 colors in the output, regardless of
-             * transparency.
-             */
-            unsigned int step, i, val, trans = 256/*ignore*/, back_alpha = 0;
-
-            cmap_entries = 1U << png_ptr->bit_depth;
-            if (cmap_entries > image->colormap_entries)
-               png_error(png_ptr, "gray[8] color-map: too few entries");
-
-            step = 255 / (cmap_entries - 1);
-            output_processing = PNG_CMAP_NONE;
-
-            /* If there is a tRNS chunk then this either selects a transparent
-             * value or, if the output has no alpha, the background color.
-             */
-            if (png_ptr->num_trans > 0)
-            {
-               trans = png_ptr->trans_color.gray;
-
-               if ((output_format & PNG_FORMAT_FLAG_ALPHA) == 0)
-                  back_alpha = output_encoding == P_LINEAR ? 65535 : 255;
-            }
-
-            /* png_create_colormap_entry just takes an RGBA and writes the
-             * corresponding color-map entry using the format from 'image',
-             * including the required conversion to sRGB or linear as
-             * appropriate.  The input values are always either sRGB (if the
-             * gamma correction flag is 0) or 0..255 scaled file encoded values
-             * (if the function must gamma correct them).
-             */
-            for (i=val=0; i<cmap_entries; ++i, val += step)
-            {
-               /* 'i' is a file value.  While this will result in duplicated
-                * entries for 8-bit non-sRGB encoded files it is necessary to
-                * have non-gamma corrected values to do tRNS handling.
-                */
-               if (i != trans)
-                  png_create_colormap_entry(display, i, val, val, val, 255,
-                      P_FILE/*8-bit with file gamma*/);
-
-               /* Else this entry is transparent.  The colors don't matter if
-                * there is an alpha channel (back_alpha == 0), but it does no
-                * harm to pass them in; the values are not set above so this
-                * passes in white.
-                *
-                * NOTE: this preserves the full precision of the application
-                * supplied background color when it is used.
-                */
-               else
-                  png_create_colormap_entry(display, i, back_r, back_g, back_b,
-                      back_alpha, output_encoding);
-            }
-
-            /* We need libpng to preserve the original encoding. */
-            data_encoding = P_FILE;
-
-            /* The rows from libpng, while technically gray values, are now also
-             * color-map indices; however, they may need to be expanded to 1
-             * byte per pixel.  This is what png_set_packing does (i.e., it
-             * unpacks the bit values into bytes.)
-             */
-            if (png_ptr->bit_depth < 8)
-               png_set_packing(png_ptr);
-         }
-
-         else /* bit depth is 16 */
-         {
-            /* The 16-bit input values can be converted directly to 8-bit gamma
-             * encoded values; however, if a tRNS chunk is present 257 color-map
-             * entries are required.  This means that the extra entry requires
-             * special processing; add an alpha channel, sacrifice gray level
-             * 254 and convert transparent (alpha==0) entries to that.
-             *
-             * Use libpng to chop the data to 8 bits.  Convert it to sRGB at the
-             * same time to minimize quality loss.  If a tRNS chunk is present
-             * this means libpng must handle it too; otherwise it is impossible
-             * to do the exact match on the 16-bit value.
-             *
-             * If the output has no alpha channel *and* the background color is
-             * gray then it is possible to let libpng handle the substitution by
-             * ensuring that the corresponding gray level matches the background
-             * color exactly.
-             */
-            data_encoding = P_sRGB;
-
-            if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries)
-               png_error(png_ptr, "gray[16] color-map: too few entries");
-
-            cmap_entries = (unsigned int)make_gray_colormap(display);
-
-            if (png_ptr->num_trans > 0)
-            {
-               unsigned int back_alpha;
-
-               if ((output_format & PNG_FORMAT_FLAG_ALPHA) != 0)
-                  back_alpha = 0;
-
-               else
-               {
-                  if (back_r == back_g && back_g == back_b)
-                  {
-                     /* Background is gray; no special processing will be
-                      * required.
-                      */
-                     png_color_16 c;
-                     png_uint_32 gray = back_g;
-
-                     if (output_encoding == P_LINEAR)
-                     {
-                        gray = PNG_sRGB_FROM_LINEAR(gray * 255);
-
-                        /* And make sure the corresponding palette entry
-                         * matches.
-                         */
-                        png_create_colormap_entry(display, gray, back_g, back_g,
-                            back_g, 65535, P_LINEAR);
-                     }
-
-                     /* The background passed to libpng, however, must be the
-                      * sRGB value.
-                      */
-                     c.index = 0; /*unused*/
-                     c.gray = c.red = c.green = c.blue = (png_uint_16)gray;
-
-                     /* NOTE: does this work without expanding tRNS to alpha?
-                      * It should be the color->gray case below apparently
-                      * doesn't.
-                      */
-                     png_set_background_fixed(png_ptr, &c,
-                         PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
-                         0/*gamma: not used*/);
-
-                     output_processing = PNG_CMAP_NONE;
-                     break;
-                  }
-#ifdef __COVERITY__
-                 /* Coverity claims that output_encoding cannot be 2 (P_LINEAR)
-                  * here.
-                  */
-                  back_alpha = 255;
-#else
-                  back_alpha = output_encoding == P_LINEAR ? 65535 : 255;
-#endif
-               }
-
-               /* output_processing means that the libpng-processed row will be
-                * 8-bit GA and it has to be processing to single byte color-map
-                * values.  Entry 254 is replaced by either a completely
-                * transparent entry or by the background color at full
-                * precision (and the background color is not a simple gray
-                * level in this case.)
-                */
-               expand_tRNS = 1;
-               output_processing = PNG_CMAP_TRANS;
-               background_index = 254;
-
-               /* And set (overwrite) color-map entry 254 to the actual
-                * background color at full precision.
-                */
-               png_create_colormap_entry(display, 254, back_r, back_g, back_b,
-                   back_alpha, output_encoding);
-            }
-
-            else
-               output_processing = PNG_CMAP_NONE;
-         }
-         break;
-
-      case PNG_COLOR_TYPE_GRAY_ALPHA:
-         /* 8-bit or 16-bit PNG with two channels - gray and alpha.  A minimum
-          * of 65536 combinations.  If, however, the alpha channel is to be
-          * removed there are only 256 possibilities if the background is gray.
-          * (Otherwise there is a subset of the 65536 possibilities defined by
-          * the triangle between black, white and the background color.)
-          *
-          * Reduce 16-bit files to 8-bit and sRGB encode the result.  No need to
-          * worry about tRNS matching - tRNS is ignored if there is an alpha
-          * channel.
-          */
-         data_encoding = P_sRGB;
-
-         if ((output_format & PNG_FORMAT_FLAG_ALPHA) != 0)
-         {
-            if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries)
-               png_error(png_ptr, "gray+alpha color-map: too few entries");
-
-            cmap_entries = (unsigned int)make_ga_colormap(display);
-
-            background_index = PNG_CMAP_GA_BACKGROUND;
-            output_processing = PNG_CMAP_GA;
-         }
-
-         else /* alpha is removed */
-         {
-            /* Alpha must be removed as the PNG data is processed when the
-             * background is a color because the G and A channels are
-             * independent and the vector addition (non-parallel vectors) is a
-             * 2-D problem.
-             *
-             * This can be reduced to the same algorithm as above by making a
-             * colormap containing gray levels (for the opaque grays), a
-             * background entry (for a transparent pixel) and a set of four six
-             * level color values, one set for each intermediate alpha value.
-             * See the comments in make_ga_colormap for how this works in the
-             * per-pixel processing.
-             *
-             * If the background is gray, however, we only need a 256 entry gray
-             * level color map.  It is sufficient to make the entry generated
-             * for the background color be exactly the color specified.
-             */
-            if ((output_format & PNG_FORMAT_FLAG_COLOR) == 0 ||
-               (back_r == back_g && back_g == back_b))
-            {
-               /* Background is gray; no special processing will be required. */
-               png_color_16 c;
-               png_uint_32 gray = back_g;
-
-               if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries)
-                  png_error(png_ptr, "gray-alpha color-map: too few entries");
-
-               cmap_entries = (unsigned int)make_gray_colormap(display);
-
-               if (output_encoding == P_LINEAR)
-               {
-                  gray = PNG_sRGB_FROM_LINEAR(gray * 255);
-
-                  /* And make sure the corresponding palette entry matches. */
-                  png_create_colormap_entry(display, gray, back_g, back_g,
-                      back_g, 65535, P_LINEAR);
-               }
-
-               /* The background passed to libpng, however, must be the sRGB
-                * value.
-                */
-               c.index = 0; /*unused*/
-               c.gray = c.red = c.green = c.blue = (png_uint_16)gray;
-
-               png_set_background_fixed(png_ptr, &c,
-                   PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
-                   0/*gamma: not used*/);
-
-               output_processing = PNG_CMAP_NONE;
-            }
-
-            else
-            {
-               png_uint_32 i, a;
-
-               /* This is the same as png_make_ga_colormap, above, except that
-                * the entries are all opaque.
-                */
-               if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries)
-                  png_error(png_ptr, "ga-alpha color-map: too few entries");
-
-               i = 0;
-               while (i < 231)
-               {
-                  png_uint_32 gray = (i * 256 + 115) / 231;
-                  png_create_colormap_entry(display, i++, gray, gray, gray,
-                      255, P_sRGB);
-               }
-
-               /* NOTE: this preserves the full precision of the application
-                * background color.
-                */
-               background_index = i;
-               png_create_colormap_entry(display, i++, back_r, back_g, back_b,
-#ifdef __COVERITY__
-                   /* Coverity claims that output_encoding
-                    * cannot be 2 (P_LINEAR) here.
-                    */ 255U,
-#else
-                    output_encoding == P_LINEAR ? 65535U : 255U,
-#endif
-                    output_encoding);
-
-               /* For non-opaque input composite on the sRGB background - this
-                * requires inverting the encoding for each component.  The input
-                * is still converted to the sRGB encoding because this is a
-                * reasonable approximate to the logarithmic curve of human
-                * visual sensitivity, at least over the narrow range which PNG
-                * represents.  Consequently 'G' is always sRGB encoded, while
-                * 'A' is linear.  We need the linear background colors.
-                */
-               if (output_encoding == P_sRGB) /* else already linear */
-               {
-                  /* This may produce a value not exactly matching the
-                   * background, but that's ok because these numbers are only
-                   * used when alpha != 0
-                   */
-                  back_r = png_sRGB_table[back_r];
-                  back_g = png_sRGB_table[back_g];
-                  back_b = png_sRGB_table[back_b];
-               }
-
-               for (a=1; a<5; ++a)
-               {
-                  unsigned int g;
-
-                  /* PNG_sRGB_FROM_LINEAR expects a 16-bit linear value scaled
-                   * by an 8-bit alpha value (0..255).
-                   */
-                  png_uint_32 alpha = 51 * a;
-                  png_uint_32 back_rx = (255-alpha) * back_r;
-                  png_uint_32 back_gx = (255-alpha) * back_g;
-                  png_uint_32 back_bx = (255-alpha) * back_b;
-
-                  for (g=0; g<6; ++g)
-                  {
-                     png_uint_32 gray = png_sRGB_table[g*51] * alpha;
-
-                     png_create_colormap_entry(display, i++,
-                         PNG_sRGB_FROM_LINEAR(gray + back_rx),
-                         PNG_sRGB_FROM_LINEAR(gray + back_gx),
-                         PNG_sRGB_FROM_LINEAR(gray + back_bx), 255, P_sRGB);
-                  }
-               }
-
-               cmap_entries = i;
-               output_processing = PNG_CMAP_GA;
-            }
-         }
-         break;
-
-      case PNG_COLOR_TYPE_RGB:
-      case PNG_COLOR_TYPE_RGB_ALPHA:
-         /* Exclude the case where the output is gray; we can always handle this
-          * with the cases above.
-          */
-         if ((output_format & PNG_FORMAT_FLAG_COLOR) == 0)
-         {
-            /* The color-map will be grayscale, so we may as well convert the
-             * input RGB values to a simple grayscale and use the grayscale
-             * code above.
-             *
-             * NOTE: calling this apparently damages the recognition of the
-             * transparent color in background color handling; call
-             * png_set_tRNS_to_alpha before png_set_background_fixed.
-             */
-            png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE, -1,
-                -1);
-            data_encoding = P_sRGB;
-
-            /* The output will now be one or two 8-bit gray or gray+alpha
-             * channels.  The more complex case arises when the input has alpha.
-             */
-            if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
-               png_ptr->num_trans > 0) &&
-               (output_format & PNG_FORMAT_FLAG_ALPHA) != 0)
-            {
-               /* Both input and output have an alpha channel, so no background
-                * processing is required; just map the GA bytes to the right
-                * color-map entry.
-                */
-               expand_tRNS = 1;
-
-               if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries)
-                  png_error(png_ptr, "rgb[ga] color-map: too few entries");
-
-               cmap_entries = (unsigned int)make_ga_colormap(display);
-               background_index = PNG_CMAP_GA_BACKGROUND;
-               output_processing = PNG_CMAP_GA;
-            }
-
-            else
-            {
-               /* Either the input or the output has no alpha channel, so there
-                * will be no non-opaque pixels in the color-map; it will just be
-                * grayscale.
-                */
-               if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries)
-                  png_error(png_ptr, "rgb[gray] color-map: too few entries");
-
-               /* Ideally this code would use libpng to do the gamma correction,
-                * but if an input alpha channel is to be removed we will hit the
-                * libpng bug in gamma+compose+rgb-to-gray (the double gamma
-                * correction bug).  Fix this by dropping the gamma correction in
-                * this case and doing it in the palette; this will result in
-                * duplicate palette entries, but that's better than the
-                * alternative of double gamma correction.
-                */
-               if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
-                  png_ptr->num_trans > 0) &&
-                  png_gamma_not_sRGB(png_ptr->colorspace.gamma) != 0)
-               {
-                  cmap_entries = (unsigned int)make_gray_file_colormap(display);
-                  data_encoding = P_FILE;
-               }
-
-               else
-                  cmap_entries = (unsigned int)make_gray_colormap(display);
-
-               /* But if the input has alpha or transparency it must be removed
-                */
-               if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
-                  png_ptr->num_trans > 0)
-               {
-                  png_color_16 c;
-                  png_uint_32 gray = back_g;
-
-                  /* We need to ensure that the application background exists in
-                   * the colormap and that completely transparent pixels map to
-                   * it.  Achieve this simply by ensuring that the entry
-                   * selected for the background really is the background color.
-                   */
-                  if (data_encoding == P_FILE) /* from the fixup above */
-                  {
-                     /* The app supplied a gray which is in output_encoding, we
-                      * need to convert it to a value of the input (P_FILE)
-                      * encoding then set this palette entry to the required
-                      * output encoding.
-                      */
-                     if (output_encoding == P_sRGB)
-                        gray = png_sRGB_table[gray]; /* now P_LINEAR */
-
-                     gray = PNG_DIV257(png_gamma_16bit_correct(gray,
-                         png_ptr->colorspace.gamma)); /* now P_FILE */
-
-                     /* And make sure the corresponding palette entry contains
-                      * exactly the required sRGB value.
-                      */
-                     png_create_colormap_entry(display, gray, back_g, back_g,
-                         back_g, 0/*unused*/, output_encoding);
-                  }
-
-                  else if (output_encoding == P_LINEAR)
-                  {
-                     gray = PNG_sRGB_FROM_LINEAR(gray * 255);
-
-                     /* And make sure the corresponding palette entry matches.
-                      */
-                     png_create_colormap_entry(display, gray, back_g, back_g,
-                        back_g, 0/*unused*/, P_LINEAR);
-                  }
-
-                  /* The background passed to libpng, however, must be the
-                   * output (normally sRGB) value.
-                   */
-                  c.index = 0; /*unused*/
-                  c.gray = c.red = c.green = c.blue = (png_uint_16)gray;
-
-                  /* NOTE: the following is apparently a bug in libpng. Without
-                   * it the transparent color recognition in
-                   * png_set_background_fixed seems to go wrong.
-                   */
-                  expand_tRNS = 1;
-                  png_set_background_fixed(png_ptr, &c,
-                      PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
-                      0/*gamma: not used*/);
-               }
-
-               output_processing = PNG_CMAP_NONE;
-            }
-         }
-
-         else /* output is color */
-         {
-            /* We could use png_quantize here so long as there is no transparent
-             * color or alpha; png_quantize ignores alpha.  Easier overall just
-             * to do it once and using PNG_DIV51 on the 6x6x6 reduced RGB cube.
-             * Consequently we always want libpng to produce sRGB data.
-             */
-            data_encoding = P_sRGB;
-
-            /* Is there any transparency or alpha? */
-            if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
-               png_ptr->num_trans > 0)
-            {
-               /* Is there alpha in the output too?  If so all four channels are
-                * processed into a special RGB cube with alpha support.
-                */
-               if ((output_format & PNG_FORMAT_FLAG_ALPHA) != 0)
-               {
-                  png_uint_32 r;
-
-                  if (PNG_RGB_COLORMAP_ENTRIES+1+27 > image->colormap_entries)
-                     png_error(png_ptr, "rgb+alpha color-map: too few entries");
-
-                  cmap_entries = (unsigned int)make_rgb_colormap(display);
-
-                  /* Add a transparent entry. */
-                  png_create_colormap_entry(display, cmap_entries, 255, 255,
-                      255, 0, P_sRGB);
-
-                  /* This is stored as the background index for the processing
-                   * algorithm.
-                   */
-                  background_index = cmap_entries++;
-
-                  /* Add 27 r,g,b entries each with alpha 0.5. */
-                  for (r=0; r<256; r = (r << 1) | 0x7f)
-                  {
-                     png_uint_32 g;
-
-                     for (g=0; g<256; g = (g << 1) | 0x7f)
-                     {
-                        png_uint_32 b;
-
-                        /* This generates components with the values 0, 127 and
-                         * 255
-                         */
-                        for (b=0; b<256; b = (b << 1) | 0x7f)
-                           png_create_colormap_entry(display, cmap_entries++,
-                               r, g, b, 128, P_sRGB);
-                     }
-                  }
-
-                  expand_tRNS = 1;
-                  output_processing = PNG_CMAP_RGB_ALPHA;
-               }
-
-               else
-               {
-                  /* Alpha/transparency must be removed.  The background must
-                   * exist in the color map (achieved by setting adding it after
-                   * the 666 color-map).  If the standard processing code will
-                   * pick up this entry automatically that's all that is
-                   * required; libpng can be called to do the background
-                   * processing.
-                   */
-                  unsigned int sample_size =
-                     PNG_IMAGE_SAMPLE_SIZE(output_format);
-                  png_uint_32 r, g, b; /* sRGB background */
-
-                  if (PNG_RGB_COLORMAP_ENTRIES+1+27 > image->colormap_entries)
-                     png_error(png_ptr, "rgb-alpha color-map: too few entries");
-
-                  cmap_entries = (unsigned int)make_rgb_colormap(display);
-
-                  png_create_colormap_entry(display, cmap_entries, back_r,
-                      back_g, back_b, 0/*unused*/, output_encoding);
-
-                  if (output_encoding == P_LINEAR)
-                  {
-                     r = PNG_sRGB_FROM_LINEAR(back_r * 255);
-                     g = PNG_sRGB_FROM_LINEAR(back_g * 255);
-                     b = PNG_sRGB_FROM_LINEAR(back_b * 255);
-                  }
-
-                  else
-                  {
-                     r = back_r;
-                     g = back_g;
-                     b = back_g;
-                  }
-
-                  /* Compare the newly-created color-map entry with the one the
-                   * PNG_CMAP_RGB algorithm will use.  If the two entries don't
-                   * match, add the new one and set this as the background
-                   * index.
-                   */
-                  if (memcmp((png_const_bytep)display->colormap +
-                      sample_size * cmap_entries,
-                      (png_const_bytep)display->colormap +
-                          sample_size * PNG_RGB_INDEX(r,g,b),
-                     sample_size) != 0)
-                  {
-                     /* The background color must be added. */
-                     background_index = cmap_entries++;
-
-                     /* Add 27 r,g,b entries each with created by composing with
-                      * the background at alpha 0.5.
-                      */
-                     for (r=0; r<256; r = (r << 1) | 0x7f)
-                     {
-                        for (g=0; g<256; g = (g << 1) | 0x7f)
-                        {
-                           /* This generates components with the values 0, 127
-                            * and 255
-                            */
-                           for (b=0; b<256; b = (b << 1) | 0x7f)
-                              png_create_colormap_entry(display, cmap_entries++,
-                                  png_colormap_compose(display, r, P_sRGB, 128,
-                                      back_r, output_encoding),
-                                  png_colormap_compose(display, g, P_sRGB, 128,
-                                      back_g, output_encoding),
-                                  png_colormap_compose(display, b, P_sRGB, 128,
-                                      back_b, output_encoding),
-                                  0/*unused*/, output_encoding);
-                        }
-                     }
-
-                     expand_tRNS = 1;
-                     output_processing = PNG_CMAP_RGB_ALPHA;
-                  }
-
-                  else /* background color is in the standard color-map */
-                  {
-                     png_color_16 c;
-
-                     c.index = 0; /*unused*/
-                     c.red = (png_uint_16)back_r;
-                     c.gray = c.green = (png_uint_16)back_g;
-                     c.blue = (png_uint_16)back_b;
-
-                     png_set_background_fixed(png_ptr, &c,
-                         PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
-                         0/*gamma: not used*/);
-
-                     output_processing = PNG_CMAP_RGB;
-                  }
-               }
-            }
-
-            else /* no alpha or transparency in the input */
-            {
-               /* Alpha in the output is irrelevant, simply map the opaque input
-                * pixels to the 6x6x6 color-map.
-                */
-               if (PNG_RGB_COLORMAP_ENTRIES > image->colormap_entries)
-                  png_error(png_ptr, "rgb color-map: too few entries");
-
-               cmap_entries = (unsigned int)make_rgb_colormap(display);
-               output_processing = PNG_CMAP_RGB;
-            }
-         }
-         break;
-
-      case PNG_COLOR_TYPE_PALETTE:
-         /* It's already got a color-map.  It may be necessary to eliminate the
-          * tRNS entries though.
-          */
-         {
-            unsigned int num_trans = png_ptr->num_trans;
-            png_const_bytep trans = num_trans > 0 ? png_ptr->trans_alpha : NULL;
-            png_const_colorp colormap = png_ptr->palette;
-            int do_background = trans != NULL &&
-               (output_format & PNG_FORMAT_FLAG_ALPHA) == 0;
-            unsigned int i;
-
-            /* Just in case: */
-            if (trans == NULL)
-               num_trans = 0;
-
-            output_processing = PNG_CMAP_NONE;
-            data_encoding = P_FILE; /* Don't change from color-map indices */
-            cmap_entries = (unsigned int)png_ptr->num_palette;
-            if (cmap_entries > 256)
-               cmap_entries = 256;
-
-            if (cmap_entries > (unsigned int)image->colormap_entries)
-               png_error(png_ptr, "palette color-map: too few entries");
-
-            for (i=0; i < cmap_entries; ++i)
-            {
-               if (do_background != 0 && i < num_trans && trans[i] < 255)
-               {
-                  if (trans[i] == 0)
-                     png_create_colormap_entry(display, i, back_r, back_g,
-                         back_b, 0, output_encoding);
-
-                  else
-                  {
-                     /* Must compose the PNG file color in the color-map entry
-                      * on the sRGB color in 'back'.
-                      */
-                     png_create_colormap_entry(display, i,
-                         png_colormap_compose(display, colormap[i].red,
-                             P_FILE, trans[i], back_r, output_encoding),
-                         png_colormap_compose(display, colormap[i].green,
-                             P_FILE, trans[i], back_g, output_encoding),
-                         png_colormap_compose(display, colormap[i].blue,
-                             P_FILE, trans[i], back_b, output_encoding),
-                         output_encoding == P_LINEAR ? trans[i] * 257U :
-                             trans[i],
-                         output_encoding);
-                  }
-               }
-
-               else
-                  png_create_colormap_entry(display, i, colormap[i].red,
-                      colormap[i].green, colormap[i].blue,
-                      i < num_trans ? trans[i] : 255U, P_FILE/*8-bit*/);
-            }
-
-            /* The PNG data may have indices packed in fewer than 8 bits, it
-             * must be expanded if so.
-             */
-            if (png_ptr->bit_depth < 8)
-               png_set_packing(png_ptr);
-         }
-         break;
-
-      default:
-         png_error(png_ptr, "invalid PNG color type");
-         /*NOT REACHED*/
-   }
-
-   /* Now deal with the output processing */
-   if (expand_tRNS != 0 && png_ptr->num_trans > 0 &&
-       (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) == 0)
-      png_set_tRNS_to_alpha(png_ptr);
-
-   switch (data_encoding)
-   {
-      case P_sRGB:
-         /* Change to 8-bit sRGB */
-         png_set_alpha_mode_fixed(png_ptr, PNG_ALPHA_PNG, PNG_GAMMA_sRGB);
-         /* FALLTHROUGH */
-
-      case P_FILE:
-         if (png_ptr->bit_depth > 8)
-            png_set_scale_16(png_ptr);
-         break;
-
-#ifdef __GNUC__
-      default:
-         png_error(png_ptr, "bad data option (internal error)");
-#endif
-   }
-
-   if (cmap_entries > 256 || cmap_entries > image->colormap_entries)
-      png_error(png_ptr, "color map overflow (BAD internal error)");
-
-   image->colormap_entries = cmap_entries;
-
-   /* Double check using the recorded background index */
-   switch (output_processing)
-   {
-      case PNG_CMAP_NONE:
-         if (background_index != PNG_CMAP_NONE_BACKGROUND)
-            goto bad_background;
-         break;
-
-      case PNG_CMAP_GA:
-         if (background_index != PNG_CMAP_GA_BACKGROUND)
-            goto bad_background;
-         break;
-
-      case PNG_CMAP_TRANS:
-         if (background_index >= cmap_entries ||
-            background_index != PNG_CMAP_TRANS_BACKGROUND)
-            goto bad_background;
-         break;
-
-      case PNG_CMAP_RGB:
-         if (background_index != PNG_CMAP_RGB_BACKGROUND)
-            goto bad_background;
-         break;
-
-      case PNG_CMAP_RGB_ALPHA:
-         if (background_index != PNG_CMAP_RGB_ALPHA_BACKGROUND)
-            goto bad_background;
-         break;
-
-      default:
-         png_error(png_ptr, "bad processing option (internal error)");
-
-      bad_background:
-         png_error(png_ptr, "bad background index (internal error)");
-   }
-
-   display->colormap_processing = (int)output_processing;
-
-   return 1/*ok*/;
-}
-
-/* The final part of the color-map read called from png_image_finish_read. */
-static int
-png_image_read_and_map(png_voidp argument)
-{
-   png_image_read_control *display = png_voidcast(png_image_read_control*,
-       argument);
-   png_imagep image = display->image;
-   png_structrp png_ptr = image->opaque->png_ptr;
-   int passes;
-
-   /* Called when the libpng data must be transformed into the color-mapped
-    * form.  There is a local row buffer in display->local and this routine must
-    * do the interlace handling.
-    */
-   switch (png_ptr->interlaced)
-   {
-      case PNG_INTERLACE_NONE:
-         passes = 1;
-         break;
-
-      case PNG_INTERLACE_ADAM7:
-         passes = PNG_INTERLACE_ADAM7_PASSES;
-         break;
-
-      default:
-         png_error(png_ptr, "unknown interlace type");
-   }
-
-   {
-      png_uint_32  height = image->height;
-      png_uint_32  width = image->width;
-      int          proc = display->colormap_processing;
-      png_bytep    first_row = png_voidcast(png_bytep, display->first_row);
-      ptrdiff_t    step_row = display->row_bytes;
-      int pass;
-
-      for (pass = 0; pass < passes; ++pass)
-      {
-         unsigned int     startx, stepx, stepy;
-         png_uint_32      y;
-
-         if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)
-         {
-            /* The row may be empty for a short image: */
-            if (PNG_PASS_COLS(width, pass) == 0)
-               continue;
-
-            startx = PNG_PASS_START_COL(pass);
-            stepx = PNG_PASS_COL_OFFSET(pass);
-            y = PNG_PASS_START_ROW(pass);
-            stepy = PNG_PASS_ROW_OFFSET(pass);
-         }
-
-         else
-         {
-            y = 0;
-            startx = 0;
-            stepx = stepy = 1;
-         }
-
-         for (; y<height; y += stepy)
-         {
-            png_bytep inrow = png_voidcast(png_bytep, display->local_row);
-            png_bytep outrow = first_row + y * step_row;
-            png_const_bytep end_row = outrow + width;
-
-            /* Read read the libpng data into the temporary buffer. */
-            png_read_row(png_ptr, inrow, NULL);
-
-            /* Now process the row according to the processing option, note
-             * that the caller verifies that the format of the libpng output
-             * data is as required.
-             */
-            outrow += startx;
-            switch (proc)
-            {
-               case PNG_CMAP_GA:
-                  for (; outrow < end_row; outrow += stepx)
-                  {
-                     /* The data is always in the PNG order */
-                     unsigned int gray = *inrow++;
-                     unsigned int alpha = *inrow++;
-                     unsigned int entry;
-
-                     /* NOTE: this code is copied as a comment in
-                      * make_ga_colormap above.  Please update the
-                      * comment if you change this code!
-                      */
-                     if (alpha > 229) /* opaque */
-                     {
-                        entry = (231 * gray + 128) >> 8;
-                     }
-                     else if (alpha < 26) /* transparent */
-                     {
-                        entry = 231;
-                     }
-                     else /* partially opaque */
-                     {
-                        entry = 226 + 6 * PNG_DIV51(alpha) + PNG_DIV51(gray);
-                     }
-
-                     *outrow = (png_byte)entry;
-                  }
-                  break;
-
-               case PNG_CMAP_TRANS:
-                  for (; outrow < end_row; outrow += stepx)
-                  {
-                     png_byte gray = *inrow++;
-                     png_byte alpha = *inrow++;
-
-                     if (alpha == 0)
-                        *outrow = PNG_CMAP_TRANS_BACKGROUND;
-
-                     else if (gray != PNG_CMAP_TRANS_BACKGROUND)
-                        *outrow = gray;
-
-                     else
-                        *outrow = (png_byte)(PNG_CMAP_TRANS_BACKGROUND+1);
-                  }
-                  break;
-
-               case PNG_CMAP_RGB:
-                  for (; outrow < end_row; outrow += stepx)
-                  {
-                     *outrow = PNG_RGB_INDEX(inrow[0], inrow[1], inrow[2]);
-                     inrow += 3;
-                  }
-                  break;
-
-               case PNG_CMAP_RGB_ALPHA:
-                  for (; outrow < end_row; outrow += stepx)
-                  {
-                     unsigned int alpha = inrow[3];
-
-                     /* Because the alpha entries only hold alpha==0.5 values
-                      * split the processing at alpha==0.25 (64) and 0.75
-                      * (196).
-                      */
-
-                     if (alpha >= 196)
-                        *outrow = PNG_RGB_INDEX(inrow[0], inrow[1],
-                            inrow[2]);
-
-                     else if (alpha < 64)
-                        *outrow = PNG_CMAP_RGB_ALPHA_BACKGROUND;
-
-                     else
-                     {
-                        /* Likewise there are three entries for each of r, g
-                         * and b.  We could select the entry by popcount on
-                         * the top two bits on those architectures that
-                         * support it, this is what the code below does,
-                         * crudely.
-                         */
-                        unsigned int back_i = PNG_CMAP_RGB_ALPHA_BACKGROUND+1;
-
-                        /* Here are how the values map:
-                         *
-                         * 0x00 .. 0x3f -> 0
-                         * 0x40 .. 0xbf -> 1
-                         * 0xc0 .. 0xff -> 2
-                         *
-                         * So, as above with the explicit alpha checks, the
-                         * breakpoints are at 64 and 196.
-                         */
-                        if (inrow[0] & 0x80) back_i += 9; /* red */
-                        if (inrow[0] & 0x40) back_i += 9;
-                        if (inrow[0] & 0x80) back_i += 3; /* green */
-                        if (inrow[0] & 0x40) back_i += 3;
-                        if (inrow[0] & 0x80) back_i += 1; /* blue */
-                        if (inrow[0] & 0x40) back_i += 1;
-
-                        *outrow = (png_byte)back_i;
-                     }
-
-                     inrow += 4;
-                  }
-                  break;
-
-               default:
-                  break;
-            }
-         }
-      }
-   }
-
-   return 1;
-}
-
-static int
-png_image_read_colormapped(png_voidp argument)
-{
-   png_image_read_control *display = png_voidcast(png_image_read_control*,
-       argument);
-   png_imagep image = display->image;
-   png_controlp control = image->opaque;
-   png_structrp png_ptr = control->png_ptr;
-   png_inforp info_ptr = control->info_ptr;
-
-   int passes = 0; /* As a flag */
-
-   PNG_SKIP_CHUNKS(png_ptr);
-
-   /* Update the 'info' structure and make sure the result is as required; first
-    * make sure to turn on the interlace handling if it will be required
-    * (because it can't be turned on *after* the call to png_read_update_info!)
-    */
-   if (display->colormap_processing == PNG_CMAP_NONE)
-      passes = png_set_interlace_handling(png_ptr);
-
-   png_read_update_info(png_ptr, info_ptr);
-
-   /* The expected output can be deduced from the colormap_processing option. */
-   switch (display->colormap_processing)
-   {
-      case PNG_CMAP_NONE:
-         /* Output must be one channel and one byte per pixel, the output
-          * encoding can be anything.
-          */
-         if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
-            info_ptr->color_type == PNG_COLOR_TYPE_GRAY) &&
-            info_ptr->bit_depth == 8)
-            break;
-
-         goto bad_output;
-
-      case PNG_CMAP_TRANS:
-      case PNG_CMAP_GA:
-         /* Output must be two channels and the 'G' one must be sRGB, the latter
-          * can be checked with an exact number because it should have been set
-          * to this number above!
-          */
-         if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
-            info_ptr->bit_depth == 8 &&
-            png_ptr->screen_gamma == PNG_GAMMA_sRGB &&
-            image->colormap_entries == 256)
-            break;
-
-         goto bad_output;
-
-      case PNG_CMAP_RGB:
-         /* Output must be 8-bit sRGB encoded RGB */
-         if (info_ptr->color_type == PNG_COLOR_TYPE_RGB &&
-            info_ptr->bit_depth == 8 &&
-            png_ptr->screen_gamma == PNG_GAMMA_sRGB &&
-            image->colormap_entries == 216)
-            break;
-
-         goto bad_output;
-
-      case PNG_CMAP_RGB_ALPHA:
-         /* Output must be 8-bit sRGB encoded RGBA */
-         if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA &&
-            info_ptr->bit_depth == 8 &&
-            png_ptr->screen_gamma == PNG_GAMMA_sRGB &&
-            image->colormap_entries == 244 /* 216 + 1 + 27 */)
-            break;
-
-         goto bad_output;
-
-      default:
-      bad_output:
-         png_error(png_ptr, "bad color-map processing (internal error)");
-   }
-
-   /* Now read the rows.  Do this here if it is possible to read directly into
-    * the output buffer, otherwise allocate a local row buffer of the maximum
-    * size libpng requires and call the relevant processing routine safely.
-    */
-   {
-      png_voidp first_row = display->buffer;
-      ptrdiff_t row_bytes = display->row_stride;
-
-      /* The following expression is designed to work correctly whether it gives
-       * a signed or an unsigned result.
-       */
-      if (row_bytes < 0)
-      {
-         char *ptr = png_voidcast(char*, first_row);
-         ptr += (image->height-1) * (-row_bytes);
-         first_row = png_voidcast(png_voidp, ptr);
-      }
-
-      display->first_row = first_row;
-      display->row_bytes = row_bytes;
-   }
-
-   if (passes == 0)
-   {
-      int result;
-      png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));
-
-      display->local_row = row;
-      result = png_safe_execute(image, png_image_read_and_map, display);
-      display->local_row = NULL;
-      png_free(png_ptr, row);
-
-      return result;
-   }
-
-   else
-   {
-      png_alloc_size_t row_bytes = (png_alloc_size_t)display->row_bytes;
-
-      while (--passes >= 0)
-      {
-         png_uint_32      y = image->height;
-         png_bytep        row = png_voidcast(png_bytep, display->first_row);
-
-         for (; y > 0; --y)
-         {
-            png_read_row(png_ptr, row, NULL);
-            row += row_bytes;
-         }
-      }
-
-      return 1;
-   }
-}
-
-/* Just the row reading part of png_image_read. */
-static int
-png_image_read_composite(png_voidp argument)
-{
-   png_image_read_control *display = png_voidcast(png_image_read_control*,
-       argument);
-   png_imagep image = display->image;
-   png_structrp png_ptr = image->opaque->png_ptr;
-   int passes;
-
-   switch (png_ptr->interlaced)
-   {
-      case PNG_INTERLACE_NONE:
-         passes = 1;
-         break;
-
-      case PNG_INTERLACE_ADAM7:
-         passes = PNG_INTERLACE_ADAM7_PASSES;
-         break;
-
-      default:
-         png_error(png_ptr, "unknown interlace type");
-   }
-
-   {
-      png_uint_32  height = image->height;
-      png_uint_32  width = image->width;
-      ptrdiff_t    step_row = display->row_bytes;
-      unsigned int channels =
-          (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1;
-      int pass;
-
-      for (pass = 0; pass < passes; ++pass)
-      {
-         unsigned int     startx, stepx, stepy;
-         png_uint_32      y;
-
-         if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)
-         {
-            /* The row may be empty for a short image: */
-            if (PNG_PASS_COLS(width, pass) == 0)
-               continue;
-
-            startx = PNG_PASS_START_COL(pass) * channels;
-            stepx = PNG_PASS_COL_OFFSET(pass) * channels;
-            y = PNG_PASS_START_ROW(pass);
-            stepy = PNG_PASS_ROW_OFFSET(pass);
-         }
-
-         else
-         {
-            y = 0;
-            startx = 0;
-            stepx = channels;
-            stepy = 1;
-         }
-
-         for (; y<height; y += stepy)
-         {
-            png_bytep inrow = png_voidcast(png_bytep, display->local_row);
-            png_bytep outrow;
-            png_const_bytep end_row;
-
-            /* Read the row, which is packed: */
-            png_read_row(png_ptr, inrow, NULL);
-
-            outrow = png_voidcast(png_bytep, display->first_row);
-            outrow += y * step_row;
-            end_row = outrow + width * channels;
-
-            /* Now do the composition on each pixel in this row. */
-            outrow += startx;
-            for (; outrow < end_row; outrow += stepx)
-            {
-               png_byte alpha = inrow[channels];
-
-               if (alpha > 0) /* else no change to the output */
-               {
-                  unsigned int c;
-
-                  for (c=0; c<channels; ++c)
-                  {
-                     png_uint_32 component = inrow[c];
-
-                     if (alpha < 255) /* else just use component */
-                     {
-                        /* This is PNG_OPTIMIZED_ALPHA, the component value
-                         * is a linear 8-bit value.  Combine this with the
-                         * current outrow[c] value which is sRGB encoded.
-                         * Arithmetic here is 16-bits to preserve the output
-                         * values correctly.
-                         */
-                        component *= 257*255; /* =65535 */
-                        component += (255-alpha)*png_sRGB_table[outrow[c]];
-
-                        /* So 'component' is scaled by 255*65535 and is
-                         * therefore appropriate for the sRGB to linear
-                         * conversion table.
-                         */
-                        component = PNG_sRGB_FROM_LINEAR(component);
-                     }
-
-                     outrow[c] = (png_byte)component;
-                  }
-               }
-
-               inrow += channels+1; /* components and alpha channel */
-            }
-         }
-      }
-   }
-
-   return 1;
-}
-
-/* The do_local_background case; called when all the following transforms are to
- * be done:
- *
- * PNG_RGB_TO_GRAY
- * PNG_COMPOSITE
- * PNG_GAMMA
- *
- * This is a work-around for the fact that both the PNG_RGB_TO_GRAY and
- * PNG_COMPOSITE code performs gamma correction, so we get double gamma
- * correction.  The fix-up is to prevent the PNG_COMPOSITE operation from
- * happening inside libpng, so this routine sees an 8 or 16-bit gray+alpha
- * row and handles the removal or pre-multiplication of the alpha channel.
- */
-static int
-png_image_read_background(png_voidp argument)
-{
-   png_image_read_control *display = png_voidcast(png_image_read_control*,
-       argument);
-   png_imagep image = display->image;
-   png_structrp png_ptr = image->opaque->png_ptr;
-   png_inforp info_ptr = image->opaque->info_ptr;
-   png_uint_32 height = image->height;
-   png_uint_32 width = image->width;
-   int pass, passes;
-
-   /* Double check the convoluted logic below.  We expect to get here with
-    * libpng doing rgb to gray and gamma correction but background processing
-    * left to the png_image_read_background function.  The rows libpng produce
-    * might be 8 or 16-bit but should always have two channels; gray plus alpha.
-    */
-   if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == 0)
-      png_error(png_ptr, "lost rgb to gray");
-
-   if ((png_ptr->transformations & PNG_COMPOSE) != 0)
-      png_error(png_ptr, "unexpected compose");
-
-   if (png_get_channels(png_ptr, info_ptr) != 2)
-      png_error(png_ptr, "lost/gained channels");
-
-   /* Expect the 8-bit case to always remove the alpha channel */
-   if ((image->format & PNG_FORMAT_FLAG_LINEAR) == 0 &&
-      (image->format & PNG_FORMAT_FLAG_ALPHA) != 0)
-      png_error(png_ptr, "unexpected 8-bit transformation");
-
-   switch (png_ptr->interlaced)
-   {
-      case PNG_INTERLACE_NONE:
-         passes = 1;
-         break;
-
-      case PNG_INTERLACE_ADAM7:
-         passes = PNG_INTERLACE_ADAM7_PASSES;
-         break;
-
-      default:
-         png_error(png_ptr, "unknown interlace type");
-   }
-
-   /* Use direct access to info_ptr here because otherwise the simplified API
-    * would require PNG_EASY_ACCESS_SUPPORTED (just for this.)  Note this is
-    * checking the value after libpng expansions, not the original value in the
-    * PNG.
-    */
-   switch (info_ptr->bit_depth)
-   {
-      case 8:
-         /* 8-bit sRGB gray values with an alpha channel; the alpha channel is
-          * to be removed by composing on a background: either the row if
-          * display->background is NULL or display->background->green if not.
-          * Unlike the code above ALPHA_OPTIMIZED has *not* been done.
-          */
-         {
-            png_bytep first_row = png_voidcast(png_bytep, display->first_row);
-            ptrdiff_t step_row = display->row_bytes;
-
-            for (pass = 0; pass < passes; ++pass)
-            {
-               png_bytep row = png_voidcast(png_bytep, display->first_row);
-               unsigned int     startx, stepx, stepy;
-               png_uint_32      y;
-
-               if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)
-               {
-                  /* The row may be empty for a short image: */
-                  if (PNG_PASS_COLS(width, pass) == 0)
-                     continue;
-
-                  startx = PNG_PASS_START_COL(pass);
-                  stepx = PNG_PASS_COL_OFFSET(pass);
-                  y = PNG_PASS_START_ROW(pass);
-                  stepy = PNG_PASS_ROW_OFFSET(pass);
-               }
-
-               else
-               {
-                  y = 0;
-                  startx = 0;
-                  stepx = stepy = 1;
-               }
-
-               if (display->background == NULL)
-               {
-                  for (; y<height; y += stepy)
-                  {
-                     png_bytep inrow = png_voidcast(png_bytep,
-                         display->local_row);
-                     png_bytep outrow = first_row + y * step_row;
-                     png_const_bytep end_row = outrow + width;
-
-                     /* Read the row, which is packed: */
-                     png_read_row(png_ptr, inrow, NULL);
-
-                     /* Now do the composition on each pixel in this row. */
-                     outrow += startx;
-                     for (; outrow < end_row; outrow += stepx)
-                     {
-                        png_byte alpha = inrow[1];
-
-                        if (alpha > 0) /* else no change to the output */
-                        {
-                           png_uint_32 component = inrow[0];
-
-                           if (alpha < 255) /* else just use component */
-                           {
-                              /* Since PNG_OPTIMIZED_ALPHA was not set it is
-                               * necessary to invert the sRGB transfer
-                               * function and multiply the alpha out.
-                               */
-                              component = png_sRGB_table[component] * alpha;
-                              component += png_sRGB_table[outrow[0]] *
-                                 (255-alpha);
-                              component = PNG_sRGB_FROM_LINEAR(component);
-                           }
-
-                           outrow[0] = (png_byte)component;
-                        }
-
-                        inrow += 2; /* gray and alpha channel */
-                     }
-                  }
-               }
-
-               else /* constant background value */
-               {
-                  png_byte background8 = display->background->green;
-                  png_uint_16 background = png_sRGB_table[background8];
-
-                  for (; y<height; y += stepy)
-                  {
-                     png_bytep inrow = png_voidcast(png_bytep,
-                         display->local_row);
-                     png_bytep outrow = first_row + y * step_row;
-                     png_const_bytep end_row = outrow + width;
-
-                     /* Read the row, which is packed: */
-                     png_read_row(png_ptr, inrow, NULL);
-
-                     /* Now do the composition on each pixel in this row. */
-                     outrow += startx;
-                     for (; outrow < end_row; outrow += stepx)
-                     {
-                        png_byte alpha = inrow[1];
-
-                        if (alpha > 0) /* else use background */
-                        {
-                           png_uint_32 component = inrow[0];
-
-                           if (alpha < 255) /* else just use component */
-                           {
-                              component = png_sRGB_table[component] * alpha;
-                              component += background * (255-alpha);
-                              component = PNG_sRGB_FROM_LINEAR(component);
-                           }
-
-                           outrow[0] = (png_byte)component;
-                        }
-
-                        else
-                           outrow[0] = background8;
-
-                        inrow += 2; /* gray and alpha channel */
-                     }
-
-                     row += display->row_bytes;
-                  }
-               }
-            }
-         }
-         break;
-
-      case 16:
-         /* 16-bit linear with pre-multiplied alpha; the pre-multiplication must
-          * still be done and, maybe, the alpha channel removed.  This code also
-          * handles the alpha-first option.
-          */
-         {
-            png_uint_16p first_row = png_voidcast(png_uint_16p,
-                display->first_row);
-            /* The division by two is safe because the caller passed in a
-             * stride which was multiplied by 2 (below) to get row_bytes.
-             */
-            ptrdiff_t    step_row = display->row_bytes / 2;
-            unsigned int preserve_alpha = (image->format &
-                PNG_FORMAT_FLAG_ALPHA) != 0;
-            unsigned int outchannels = 1U+preserve_alpha;
-            int swap_alpha = 0;
-
-#           ifdef PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED
-               if (preserve_alpha != 0 &&
-                   (image->format & PNG_FORMAT_FLAG_AFIRST) != 0)
-                  swap_alpha = 1;
-#           endif
-
-            for (pass = 0; pass < passes; ++pass)
-            {
-               unsigned int     startx, stepx, stepy;
-               png_uint_32      y;
-
-               /* The 'x' start and step are adjusted to output components here.
-                */
-               if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)
-               {
-                  /* The row may be empty for a short image: */
-                  if (PNG_PASS_COLS(width, pass) == 0)
-                     continue;
-
-                  startx = PNG_PASS_START_COL(pass) * outchannels;
-                  stepx = PNG_PASS_COL_OFFSET(pass) * outchannels;
-                  y = PNG_PASS_START_ROW(pass);
-                  stepy = PNG_PASS_ROW_OFFSET(pass);
-               }
-
-               else
-               {
-                  y = 0;
-                  startx = 0;
-                  stepx = outchannels;
-                  stepy = 1;
-               }
-
-               for (; y<height; y += stepy)
-               {
-                  png_const_uint_16p inrow;
-                  png_uint_16p outrow = first_row + y*step_row;
-                  png_uint_16p end_row = outrow + width * outchannels;
-
-                  /* Read the row, which is packed: */
-                  png_read_row(png_ptr, png_voidcast(png_bytep,
-                      display->local_row), NULL);
-                  inrow = png_voidcast(png_const_uint_16p, display->local_row);
-
-                  /* Now do the pre-multiplication on each pixel in this row.
-                   */
-                  outrow += startx;
-                  for (; outrow < end_row; outrow += stepx)
-                  {
-                     png_uint_32 component = inrow[0];
-                     png_uint_16 alpha = inrow[1];
-
-                     if (alpha > 0) /* else 0 */
-                     {
-                        if (alpha < 65535) /* else just use component */
-                        {
-                           component *= alpha;
-                           component += 32767;
-                           component /= 65535;
-                        }
-                     }
-
-                     else
-                        component = 0;
-
-                     outrow[swap_alpha] = (png_uint_16)component;
-                     if (preserve_alpha != 0)
-                        outrow[1 ^ swap_alpha] = alpha;
-
-                     inrow += 2; /* components and alpha channel */
-                  }
-               }
-            }
-         }
-         break;
-
-#ifdef __GNUC__
-      default:
-         png_error(png_ptr, "unexpected bit depth");
-#endif
-   }
-
-   return 1;
-}
-
-/* The guts of png_image_finish_read as a png_safe_execute callback. */
-static int
-png_image_read_direct(png_voidp argument)
-{
-   png_image_read_control *display = png_voidcast(png_image_read_control*,
-       argument);
-   png_imagep image = display->image;
-   png_structrp png_ptr = image->opaque->png_ptr;
-   png_inforp info_ptr = image->opaque->info_ptr;
-
-   png_uint_32 format = image->format;
-   int linear = (format & PNG_FORMAT_FLAG_LINEAR) != 0;
-   int do_local_compose = 0;
-   int do_local_background = 0; /* to avoid double gamma correction bug */
-   int passes = 0;
-
-   /* Add transforms to ensure the correct output format is produced then check
-    * that the required implementation support is there.  Always expand; always
-    * need 8 bits minimum, no palette and expanded tRNS.
-    */
-   png_set_expand(png_ptr);
-
-   /* Now check the format to see if it was modified. */
-   {
-      png_uint_32 base_format = png_image_format(png_ptr) &
-         ~PNG_FORMAT_FLAG_COLORMAP /* removed by png_set_expand */;
-      png_uint_32 change = format ^ base_format;
-      png_fixed_point output_gamma;
-      int mode; /* alpha mode */
-
-      /* Do this first so that we have a record if rgb to gray is happening. */
-      if ((change & PNG_FORMAT_FLAG_COLOR) != 0)
-      {
-         /* gray<->color transformation required. */
-         if ((format & PNG_FORMAT_FLAG_COLOR) != 0)
-            png_set_gray_to_rgb(png_ptr);
-
-         else
-         {
-            /* libpng can't do both rgb to gray and
-             * background/pre-multiplication if there is also significant gamma
-             * correction, because both operations require linear colors and
-             * the code only supports one transform doing the gamma correction.
-             * Handle this by doing the pre-multiplication or background
-             * operation in this code, if necessary.
-             *
-             * TODO: fix this by rewriting pngrtran.c (!)
-             *
-             * For the moment (given that fixing this in pngrtran.c is an
-             * enormous change) 'do_local_background' is used to indicate that
-             * the problem exists.
-             */
-            if ((base_format & PNG_FORMAT_FLAG_ALPHA) != 0)
-               do_local_background = 1/*maybe*/;
-
-            png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE,
-                PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT);
-         }
-
-         change &= ~PNG_FORMAT_FLAG_COLOR;
-      }
-
-      /* Set the gamma appropriately, linear for 16-bit input, sRGB otherwise.
-       */
-      {
-         png_fixed_point input_gamma_default;
-
-         if ((base_format & PNG_FORMAT_FLAG_LINEAR) != 0 &&
-             (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0)
-            input_gamma_default = PNG_GAMMA_LINEAR;
-         else
-            input_gamma_default = PNG_DEFAULT_sRGB;
-
-         /* Call png_set_alpha_mode to set the default for the input gamma; the
-          * output gamma is set by a second call below.
-          */
-         png_set_alpha_mode_fixed(png_ptr, PNG_ALPHA_PNG, input_gamma_default);
-      }
-
-      if (linear != 0)
-      {
-         /* If there *is* an alpha channel in the input it must be multiplied
-          * out; use PNG_ALPHA_STANDARD, otherwise just use PNG_ALPHA_PNG.
-          */
-         if ((base_format & PNG_FORMAT_FLAG_ALPHA) != 0)
-            mode = PNG_ALPHA_STANDARD; /* associated alpha */
-
-         else
-            mode = PNG_ALPHA_PNG;
-
-         output_gamma = PNG_GAMMA_LINEAR;
-      }
-
-      else
-      {
-         mode = PNG_ALPHA_PNG;
-         output_gamma = PNG_DEFAULT_sRGB;
-      }
-      
-      if ((change & PNG_FORMAT_FLAG_ASSOCIATED_ALPHA) != 0)
-      {
-         mode = PNG_ALPHA_OPTIMIZED;
-         change &= ~PNG_FORMAT_FLAG_ASSOCIATED_ALPHA;
-      }
-      
-      /* If 'do_local_background' is set check for the presence of gamma
-       * correction; this is part of the work-round for the libpng bug
-       * described above.
-       *
-       * TODO: fix libpng and remove this.
-       */
-      if (do_local_background != 0)
-      {
-         png_fixed_point gtest;
-
-         /* This is 'png_gamma_threshold' from pngrtran.c; the test used for
-          * gamma correction, the screen gamma hasn't been set on png_struct
-          * yet; it's set below.  png_struct::gamma, however, is set to the
-          * final value.
-          */
-         if (png_muldiv(&gtest, output_gamma, png_ptr->colorspace.gamma,
-             PNG_FP_1) != 0 && png_gamma_significant(gtest) == 0)
-            do_local_background = 0;
-
-         else if (mode == PNG_ALPHA_STANDARD)
-         {
-            do_local_background = 2/*required*/;
-            mode = PNG_ALPHA_PNG; /* prevent libpng doing it */
-         }
-
-         /* else leave as 1 for the checks below */
-      }
-
-      /* If the bit-depth changes then handle that here. */
-      if ((change & PNG_FORMAT_FLAG_LINEAR) != 0)
-      {
-         if (linear != 0 /*16-bit output*/)
-            png_set_expand_16(png_ptr);
-
-         else /* 8-bit output */
-            png_set_scale_16(png_ptr);
-
-         change &= ~PNG_FORMAT_FLAG_LINEAR;
-      }
-
-      /* Now the background/alpha channel changes. */
-      if ((change & PNG_FORMAT_FLAG_ALPHA) != 0)
-      {
-         /* Removing an alpha channel requires composition for the 8-bit
-          * formats; for the 16-bit it is already done, above, by the
-          * pre-multiplication and the channel just needs to be stripped.
-          */
-         if ((base_format & PNG_FORMAT_FLAG_ALPHA) != 0)
-         {
-            /* If RGB->gray is happening the alpha channel must be left and the
-             * operation completed locally.
-             *
-             * TODO: fix libpng and remove this.
-             */
-            if (do_local_background != 0)
-               do_local_background = 2/*required*/;
-
-            /* 16-bit output: just remove the channel */
-            else if (linear != 0) /* compose on black (well, pre-multiply) */
-               png_set_strip_alpha(png_ptr);
-
-            /* 8-bit output: do an appropriate compose */
-            else if (display->background != NULL)
-            {
-               png_color_16 c;
-
-               c.index = 0; /*unused*/
-               c.red = display->background->red;
-               c.green = display->background->green;
-               c.blue = display->background->blue;
-               c.gray = display->background->green;
-
-               /* This is always an 8-bit sRGB value, using the 'green' channel
-                * for gray is much better than calculating the luminance here;
-                * we can get off-by-one errors in that calculation relative to
-                * the app expectations and that will show up in transparent
-                * pixels.
-                */
-               png_set_background_fixed(png_ptr, &c,
-                   PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
-                   0/*gamma: not used*/);
-            }
-
-            else /* compose on row: implemented below. */
-            {
-               do_local_compose = 1;
-               /* This leaves the alpha channel in the output, so it has to be
-                * removed by the code below.  Set the encoding to the 'OPTIMIZE'
-                * one so the code only has to hack on the pixels that require
-                * composition.
-                */
-               mode = PNG_ALPHA_OPTIMIZED;
-            }
-         }
-
-         else /* output needs an alpha channel */
-         {
-            /* This is tricky because it happens before the swap operation has
-             * been accomplished; however, the swap does *not* swap the added
-             * alpha channel (weird API), so it must be added in the correct
-             * place.
-             */
-            png_uint_32 filler; /* opaque filler */
-            int where;
-
-            if (linear != 0)
-               filler = 65535;
-
-            else
-               filler = 255;
-
-#ifdef PNG_FORMAT_AFIRST_SUPPORTED
-            if ((format & PNG_FORMAT_FLAG_AFIRST) != 0)
-            {
-               where = PNG_FILLER_BEFORE;
-               change &= ~PNG_FORMAT_FLAG_AFIRST;
-            }
-
-            else
-#endif
-            where = PNG_FILLER_AFTER;
-
-            png_set_add_alpha(png_ptr, filler, where);
-         }
-
-         /* This stops the (irrelevant) call to swap_alpha below. */
-         change &= ~PNG_FORMAT_FLAG_ALPHA;
-      }
-
-      /* Now set the alpha mode correctly; this is always done, even if there is
-       * no alpha channel in either the input or the output because it correctly
-       * sets the output gamma.
-       */
-      png_set_alpha_mode_fixed(png_ptr, mode, output_gamma);
-
-#     ifdef PNG_FORMAT_BGR_SUPPORTED
-         if ((change & PNG_FORMAT_FLAG_BGR) != 0)
-         {
-            /* Check only the output format; PNG is never BGR; don't do this if
-             * the output is gray, but fix up the 'format' value in that case.
-             */
-            if ((format & PNG_FORMAT_FLAG_COLOR) != 0)
-               png_set_bgr(png_ptr);
-
-            else
-               format &= ~PNG_FORMAT_FLAG_BGR;
-
-            change &= ~PNG_FORMAT_FLAG_BGR;
-         }
-#     endif
-
-#     ifdef PNG_FORMAT_AFIRST_SUPPORTED
-         if ((change & PNG_FORMAT_FLAG_AFIRST) != 0)
-         {
-            /* Only relevant if there is an alpha channel - it's particularly
-             * important to handle this correctly because do_local_compose may
-             * be set above and then libpng will keep the alpha channel for this
-             * code to remove.
-             */
-            if ((format & PNG_FORMAT_FLAG_ALPHA) != 0)
-            {
-               /* Disable this if doing a local background,
-                * TODO: remove this when local background is no longer required.
-                */
-               if (do_local_background != 2)
-                  png_set_swap_alpha(png_ptr);
-            }
-
-            else
-               format &= ~PNG_FORMAT_FLAG_AFIRST;
-
-            change &= ~PNG_FORMAT_FLAG_AFIRST;
-         }
-#     endif
-
-      /* If the *output* is 16-bit then we need to check for a byte-swap on this
-       * architecture.
-       */
-      if (linear != 0)
-      {
-         png_uint_16 le = 0x0001;
-
-         if ((*(png_const_bytep) & le) != 0)
-            png_set_swap(png_ptr);
-      }
-
-      /* If change is not now 0 some transformation is missing - error out. */
-      if (change != 0)
-         png_error(png_ptr, "png_read_image: unsupported transformation");
-   }
-
-   PNG_SKIP_CHUNKS(png_ptr);
-
-   /* Update the 'info' structure and make sure the result is as required; first
-    * make sure to turn on the interlace handling if it will be required
-    * (because it can't be turned on *after* the call to png_read_update_info!)
-    *
-    * TODO: remove the do_local_background fixup below.
-    */
-   if (do_local_compose == 0 && do_local_background != 2)
-      passes = png_set_interlace_handling(png_ptr);
-
-   png_read_update_info(png_ptr, info_ptr);
-
-   {
-      png_uint_32 info_format = 0;
-
-      if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
-         info_format |= PNG_FORMAT_FLAG_COLOR;
-
-      if ((info_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0)
-      {
-         /* do_local_compose removes this channel below. */
-         if (do_local_compose == 0)
-         {
-            /* do_local_background does the same if required. */
-            if (do_local_background != 2 ||
-               (format & PNG_FORMAT_FLAG_ALPHA) != 0)
-               info_format |= PNG_FORMAT_FLAG_ALPHA;
-         }
-      }
-
-      else if (do_local_compose != 0) /* internal error */
-         png_error(png_ptr, "png_image_read: alpha channel lost");
-
-      if ((format & PNG_FORMAT_FLAG_ASSOCIATED_ALPHA) != 0) {
-         info_format |= PNG_FORMAT_FLAG_ASSOCIATED_ALPHA;
-      }
-
-      if (info_ptr->bit_depth == 16)
-         info_format |= PNG_FORMAT_FLAG_LINEAR;
-
-#ifdef PNG_FORMAT_BGR_SUPPORTED
-      if ((png_ptr->transformations & PNG_BGR) != 0)
-         info_format |= PNG_FORMAT_FLAG_BGR;
-#endif
-
-#ifdef PNG_FORMAT_AFIRST_SUPPORTED
-         if (do_local_background == 2)
-         {
-            if ((format & PNG_FORMAT_FLAG_AFIRST) != 0)
-               info_format |= PNG_FORMAT_FLAG_AFIRST;
-         }
-
-         if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0 ||
-            ((png_ptr->transformations & PNG_ADD_ALPHA) != 0 &&
-            (png_ptr->flags & PNG_FLAG_FILLER_AFTER) == 0))
-         {
-            if (do_local_background == 2)
-               png_error(png_ptr, "unexpected alpha swap transformation");
-
-            info_format |= PNG_FORMAT_FLAG_AFIRST;
-         }
-#     endif
-
-      /* This is actually an internal error. */
-      if (info_format != format)
-         png_error(png_ptr, "png_read_image: invalid transformations");
-   }
-
-   /* Now read the rows.  If do_local_compose is set then it is necessary to use
-    * a local row buffer.  The output will be GA, RGBA or BGRA and must be
-    * converted to G, RGB or BGR as appropriate.  The 'local_row' member of the
-    * display acts as a flag.
-    */
-   {
-      png_voidp first_row = display->buffer;
-      ptrdiff_t row_bytes = display->row_stride;
-
-      if (linear != 0)
-         row_bytes *= 2;
-
-      /* The following expression is designed to work correctly whether it gives
-       * a signed or an unsigned result.
-       */
-      if (row_bytes < 0)
-      {
-         char *ptr = png_voidcast(char*, first_row);
-         ptr += (image->height-1) * (-row_bytes);
-         first_row = png_voidcast(png_voidp, ptr);
-      }
-
-      display->first_row = first_row;
-      display->row_bytes = row_bytes;
-   }
-
-   if (do_local_compose != 0)
-   {
-      int result;
-      png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));
-
-      display->local_row = row;
-      result = png_safe_execute(image, png_image_read_composite, display);
-      display->local_row = NULL;
-      png_free(png_ptr, row);
-
-      return result;
-   }
-
-   else if (do_local_background == 2)
-   {
-      int result;
-      png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));
-
-      display->local_row = row;
-      result = png_safe_execute(image, png_image_read_background, display);
-      display->local_row = NULL;
-      png_free(png_ptr, row);
-
-      return result;
-   }
-
-   else
-   {
-      png_alloc_size_t row_bytes = (png_alloc_size_t)display->row_bytes;
-
-      while (--passes >= 0)
-      {
-         png_uint_32      y = image->height;
-         png_bytep        row = png_voidcast(png_bytep, display->first_row);
-
-         for (; y > 0; --y)
-         {
-            png_read_row(png_ptr, row, NULL);
-            row += row_bytes;
-         }
-      }
-
-      return 1;
-   }
-}
-
-int PNGAPI
-png_image_finish_read(png_imagep image, png_const_colorp background,
-    void *buffer, png_int_32 row_stride, void *colormap)
-{
-   if (image != NULL && image->version == PNG_IMAGE_VERSION)
-   {
-      /* Check for row_stride overflow.  This check is not performed on the
-       * original PNG format because it may not occur in the output PNG format
-       * and libpng deals with the issues of reading the original.
-       */
-      unsigned int channels = PNG_IMAGE_PIXEL_CHANNELS(image->format);
-
-      /* The following checks just the 'row_stride' calculation to ensure it
-       * fits in a signed 32-bit value.  Because channels/components can be
-       * either 1 or 2 bytes in size the length of a row can still overflow 32
-       * bits; this is just to verify that the 'row_stride' argument can be
-       * represented.
-       */
-      if (image->width <= 0x7fffffffU/channels) /* no overflow */
-      {
-         png_uint_32 check;
-         png_uint_32 png_row_stride = image->width * channels;
-
-         if (row_stride == 0)
-            row_stride = (png_int_32)/*SAFE*/png_row_stride;
-
-         if (row_stride < 0)
-            check = (png_uint_32)(-row_stride);
-
-         else
-            check = (png_uint_32)row_stride;
-
-         /* This verifies 'check', the absolute value of the actual stride
-          * passed in and detects overflow in the application calculation (i.e.
-          * if the app did actually pass in a non-zero 'row_stride'.
-          */
-         if (image->opaque != NULL && buffer != NULL && check >= png_row_stride)
-         {
-            /* Now check for overflow of the image buffer calculation; this
-             * limits the whole image size to 32 bits for API compatibility with
-             * the current, 32-bit, PNG_IMAGE_BUFFER_SIZE macro.
-             *
-             * The PNG_IMAGE_BUFFER_SIZE macro is:
-             *
-             *    (PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)*height*(row_stride))
-             *
-             * And the component size is always 1 or 2, so make sure that the
-             * number of *bytes* that the application is saying are available
-             * does actually fit into a 32-bit number.
-             *
-             * NOTE: this will be changed in 1.7 because PNG_IMAGE_BUFFER_SIZE
-             * will be changed to use png_alloc_size_t; bigger images can be
-             * accommodated on 64-bit systems.
-             */
-            if (image->height <=
-                0xffffffffU/PNG_IMAGE_PIXEL_COMPONENT_SIZE(image->format)/check)
-            {
-               if ((image->format & PNG_FORMAT_FLAG_COLORMAP) == 0 ||
-                  (image->colormap_entries > 0 && colormap != NULL))
-               {
-                  int result;
-                  png_image_read_control display;
-
-                  memset(&display, 0, (sizeof display));
-                  display.image = image;
-                  display.buffer = buffer;
-                  display.row_stride = row_stride;
-                  display.colormap = colormap;
-                  display.background = background;
-                  display.local_row = NULL;
-
-                  /* Choose the correct 'end' routine; for the color-map case
-                   * all the setup has already been done.
-                   */
-                  if ((image->format & PNG_FORMAT_FLAG_COLORMAP) != 0)
-                     result =
-                         png_safe_execute(image,
-                             png_image_read_colormap, &display) &&
-                             png_safe_execute(image,
-                             png_image_read_colormapped, &display);
-
-                  else
-                     result =
-                        png_safe_execute(image,
-                            png_image_read_direct, &display);
-
-                  png_image_free(image);
-                  return result;
-               }
-
-               else
-                  return png_image_error(image,
-                      "png_image_finish_read[color-map]: no color-map");
-            }
-
-            else
-               return png_image_error(image,
-                   "png_image_finish_read: image too large");
-         }
-
-         else
-            return png_image_error(image,
-                "png_image_finish_read: invalid argument");
-      }
-
-      else
-         return png_image_error(image,
-             "png_image_finish_read: row_stride too large");
-   }
-
-   else if (image != NULL)
-      return png_image_error(image,
-          "png_image_finish_read: damaged PNG_IMAGE_VERSION");
-
-   return 0;
-}
-
-#endif /* SIMPLIFIED_READ */
-#endif /* READ */
diff --git a/third_party/libpng16/pngrio.c b/third_party/libpng16/pngrio.c
deleted file mode 100644
index 7946358..0000000
--- a/third_party/libpng16/pngrio.c
+++ /dev/null
@@ -1,120 +0,0 @@
-
-/* pngrio.c - functions for data input
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- *
- * This file provides a location for all input.  Users who need
- * special handling are expected to write a function that has the same
- * arguments as this and performs a similar function, but that possibly
- * has a different input method.  Note that you shouldn't change this
- * function, but rather write a replacement function and then make
- * libpng use it at run time with png_set_read_fn(...).
- */
-
-#include "pngpriv.h"
-
-#ifdef PNG_READ_SUPPORTED
-
-/* Read the data from whatever input you are using.  The default routine
- * reads from a file pointer.  Note that this routine sometimes gets called
- * with very small lengths, so you should implement some kind of simple
- * buffering if you are using unbuffered reads.  This should never be asked
- * to read more than 64K on a 16-bit machine.
- */
-void /* PRIVATE */
-png_read_data(png_structrp png_ptr, png_bytep data, size_t length)
-{
-   png_debug1(4, "reading %d bytes", (int)length);
-
-   if (png_ptr->read_data_fn != NULL)
-      (*(png_ptr->read_data_fn))(png_ptr, data, length);
-
-   else
-      png_error(png_ptr, "Call to NULL read function");
-}
-
-#ifdef PNG_STDIO_SUPPORTED
-/* This is the function that does the actual reading of data.  If you are
- * not reading from a standard C stream, you should create a replacement
- * read_data function and use it at run time with png_set_read_fn(), rather
- * than changing the library.
- */
-void PNGCBAPI
-png_default_read_data(png_structp png_ptr, png_bytep data, size_t length)
-{
-   size_t check;
-
-   if (png_ptr == NULL)
-      return;
-
-   /* fread() returns 0 on error, so it is OK to store this in a size_t
-    * instead of an int, which is what fread() actually returns.
-    */
-   check = fread(data, 1, length, png_voidcast(png_FILE_p, png_ptr->io_ptr));
-
-   if (check != length)
-      png_error(png_ptr, "Read Error");
-}
-#endif
-
-/* This function allows the application to supply a new input function
- * for libpng if standard C streams aren't being used.
- *
- * This function takes as its arguments:
- *
- * png_ptr      - pointer to a png input data structure
- *
- * io_ptr       - pointer to user supplied structure containing info about
- *                the input functions.  May be NULL.
- *
- * read_data_fn - pointer to a new input function that takes as its
- *                arguments a pointer to a png_struct, a pointer to
- *                a location where input data can be stored, and a 32-bit
- *                unsigned int that is the number of bytes to be read.
- *                To exit and output any fatal error messages the new write
- *                function should call png_error(png_ptr, "Error msg").
- *                May be NULL, in which case libpng's default function will
- *                be used.
- */
-void PNGAPI
-png_set_read_fn(png_structrp png_ptr, png_voidp io_ptr,
-    png_rw_ptr read_data_fn)
-{
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->io_ptr = io_ptr;
-
-#ifdef PNG_STDIO_SUPPORTED
-   if (read_data_fn != NULL)
-      png_ptr->read_data_fn = read_data_fn;
-
-   else
-      png_ptr->read_data_fn = png_default_read_data;
-#else
-   png_ptr->read_data_fn = read_data_fn;
-#endif
-
-#ifdef PNG_WRITE_SUPPORTED
-   /* It is an error to write to a read device */
-   if (png_ptr->write_data_fn != NULL)
-   {
-      png_ptr->write_data_fn = NULL;
-      png_warning(png_ptr,
-          "Can't set both read_data_fn and write_data_fn in the"
-          " same structure");
-   }
-#endif
-
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
-   png_ptr->output_flush_fn = NULL;
-#endif
-}
-#endif /* READ */
diff --git a/third_party/libpng16/pngrtran.c b/third_party/libpng16/pngrtran.c
deleted file mode 100644
index 786e0de..0000000
--- a/third_party/libpng16/pngrtran.c
+++ /dev/null
@@ -1,5044 +0,0 @@
-
-/* pngrtran.c - transforms the data in a row for PNG readers
- *
- * Copyright (c) 2018-2019 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- *
- * This file contains functions optionally called by an application
- * in order to tell libpng how to handle data when reading a PNG.
- * Transformations that are used in both reading and writing are
- * in pngtrans.c.
- */
-
-#include "pngpriv.h"
-
-#ifdef PNG_ARM_NEON_IMPLEMENTATION
-#  if PNG_ARM_NEON_IMPLEMENTATION == 1
-#    define PNG_ARM_NEON_INTRINSICS_AVAILABLE
-#    if defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__)
-#      include <arm64_neon.h>
-#    else
-#      include <arm_neon.h>
-#    endif
-#  endif
-#endif
-
-#ifdef PNG_READ_SUPPORTED
-
-/* Set the action on getting a CRC error for an ancillary or critical chunk. */
-void PNGAPI
-png_set_crc_action(png_structrp png_ptr, int crit_action, int ancil_action)
-{
-   png_debug(1, "in png_set_crc_action");
-
-   if (png_ptr == NULL)
-      return;
-
-   /* Tell libpng how we react to CRC errors in critical chunks */
-   switch (crit_action)
-   {
-      case PNG_CRC_NO_CHANGE:                        /* Leave setting as is */
-         break;
-
-      case PNG_CRC_WARN_USE:                               /* Warn/use data */
-         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
-         png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE;
-         break;
-
-      case PNG_CRC_QUIET_USE:                             /* Quiet/use data */
-         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
-         png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE |
-                           PNG_FLAG_CRC_CRITICAL_IGNORE;
-         break;
-
-      case PNG_CRC_WARN_DISCARD:    /* Not a valid action for critical data */
-         png_warning(png_ptr,
-             "Can't discard critical data on CRC error");
-         /* FALLTHROUGH */
-      case PNG_CRC_ERROR_QUIT:                                /* Error/quit */
-
-      case PNG_CRC_DEFAULT:
-      default:
-         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
-         break;
-   }
-
-   /* Tell libpng how we react to CRC errors in ancillary chunks */
-   switch (ancil_action)
-   {
-      case PNG_CRC_NO_CHANGE:                       /* Leave setting as is */
-         break;
-
-      case PNG_CRC_WARN_USE:                              /* Warn/use data */
-         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
-         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE;
-         break;
-
-      case PNG_CRC_QUIET_USE:                            /* Quiet/use data */
-         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
-         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE |
-                           PNG_FLAG_CRC_ANCILLARY_NOWARN;
-         break;
-
-      case PNG_CRC_ERROR_QUIT:                               /* Error/quit */
-         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
-         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN;
-         break;
-
-      case PNG_CRC_WARN_DISCARD:                      /* Warn/discard data */
-
-      case PNG_CRC_DEFAULT:
-      default:
-         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
-         break;
-   }
-}
-
-#ifdef PNG_READ_TRANSFORMS_SUPPORTED
-/* Is it OK to set a transformation now?  Only if png_start_read_image or
- * png_read_update_info have not been called.  It is not necessary for the IHDR
- * to have been read in all cases; the need_IHDR parameter allows for this
- * check too.
- */
-static int
-png_rtran_ok(png_structrp png_ptr, int need_IHDR)
-{
-   if (png_ptr != NULL)
-   {
-      if ((png_ptr->flags & PNG_FLAG_ROW_INIT) != 0)
-         png_app_error(png_ptr,
-             "invalid after png_start_read_image or png_read_update_info");
-
-      else if (need_IHDR && (png_ptr->mode & PNG_HAVE_IHDR) == 0)
-         png_app_error(png_ptr, "invalid before the PNG header has been read");
-
-      else
-      {
-         /* Turn on failure to initialize correctly for all transforms. */
-         png_ptr->flags |= PNG_FLAG_DETECT_UNINITIALIZED;
-
-         return 1; /* Ok */
-      }
-   }
-
-   return 0; /* no png_error possible! */
-}
-#endif
-
-#ifdef PNG_READ_BACKGROUND_SUPPORTED
-/* Handle alpha and tRNS via a background color */
-void PNGFAPI
-png_set_background_fixed(png_structrp png_ptr,
-    png_const_color_16p background_color, int background_gamma_code,
-    int need_expand, png_fixed_point background_gamma)
-{
-   png_debug(1, "in png_set_background_fixed");
-
-   if (png_rtran_ok(png_ptr, 0) == 0 || background_color == NULL)
-      return;
-
-   if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN)
-   {
-      png_warning(png_ptr, "Application must supply a known background gamma");
-      return;
-   }
-
-   png_ptr->transformations |= PNG_COMPOSE | PNG_STRIP_ALPHA;
-   png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
-   png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
-
-   png_ptr->background = *background_color;
-   png_ptr->background_gamma = background_gamma;
-   png_ptr->background_gamma_type = (png_byte)(background_gamma_code);
-   if (need_expand != 0)
-      png_ptr->transformations |= PNG_BACKGROUND_EXPAND;
-   else
-      png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND;
-}
-
-#  ifdef PNG_FLOATING_POINT_SUPPORTED
-void PNGAPI
-png_set_background(png_structrp png_ptr,
-    png_const_color_16p background_color, int background_gamma_code,
-    int need_expand, double background_gamma)
-{
-   png_set_background_fixed(png_ptr, background_color, background_gamma_code,
-      need_expand, png_fixed(png_ptr, background_gamma, "png_set_background"));
-}
-#  endif /* FLOATING_POINT */
-#endif /* READ_BACKGROUND */
-
-/* Scale 16-bit depth files to 8-bit depth.  If both of these are set then the
- * one that pngrtran does first (scale) happens.  This is necessary to allow the
- * TRANSFORM and API behavior to be somewhat consistent, and it's simpler.
- */
-#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
-void PNGAPI
-png_set_scale_16(png_structrp png_ptr)
-{
-   png_debug(1, "in png_set_scale_16");
-
-   if (png_rtran_ok(png_ptr, 0) == 0)
-      return;
-
-   png_ptr->transformations |= PNG_SCALE_16_TO_8;
-}
-#endif
-
-#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
-/* Chop 16-bit depth files to 8-bit depth */
-void PNGAPI
-png_set_strip_16(png_structrp png_ptr)
-{
-   png_debug(1, "in png_set_strip_16");
-
-   if (png_rtran_ok(png_ptr, 0) == 0)
-      return;
-
-   png_ptr->transformations |= PNG_16_TO_8;
-}
-#endif
-
-#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
-void PNGAPI
-png_set_strip_alpha(png_structrp png_ptr)
-{
-   png_debug(1, "in png_set_strip_alpha");
-
-   if (png_rtran_ok(png_ptr, 0) == 0)
-      return;
-
-   png_ptr->transformations |= PNG_STRIP_ALPHA;
-}
-#endif
-
-#if defined(PNG_READ_ALPHA_MODE_SUPPORTED) || defined(PNG_READ_GAMMA_SUPPORTED)
-static png_fixed_point
-translate_gamma_flags(png_structrp png_ptr, png_fixed_point output_gamma,
-    int is_screen)
-{
-   /* Check for flag values.  The main reason for having the old Mac value as a
-    * flag is that it is pretty near impossible to work out what the correct
-    * value is from Apple documentation - a working Mac system is needed to
-    * discover the value!
-    */
-   if (output_gamma == PNG_DEFAULT_sRGB ||
-      output_gamma == PNG_FP_1 / PNG_DEFAULT_sRGB)
-   {
-      /* If there is no sRGB support this just sets the gamma to the standard
-       * sRGB value.  (This is a side effect of using this function!)
-       */
-#     ifdef PNG_READ_sRGB_SUPPORTED
-         png_ptr->flags |= PNG_FLAG_ASSUME_sRGB;
-#     else
-         PNG_UNUSED(png_ptr)
-#     endif
-      if (is_screen != 0)
-         output_gamma = PNG_GAMMA_sRGB;
-      else
-         output_gamma = PNG_GAMMA_sRGB_INVERSE;
-   }
-
-   else if (output_gamma == PNG_GAMMA_MAC_18 ||
-      output_gamma == PNG_FP_1 / PNG_GAMMA_MAC_18)
-   {
-      if (is_screen != 0)
-         output_gamma = PNG_GAMMA_MAC_OLD;
-      else
-         output_gamma = PNG_GAMMA_MAC_INVERSE;
-   }
-
-   return output_gamma;
-}
-
-#  ifdef PNG_FLOATING_POINT_SUPPORTED
-static png_fixed_point
-convert_gamma_value(png_structrp png_ptr, double output_gamma)
-{
-   /* The following silently ignores cases where fixed point (times 100,000)
-    * gamma values are passed to the floating point API.  This is safe and it
-    * means the fixed point constants work just fine with the floating point
-    * API.  The alternative would just lead to undetected errors and spurious
-    * bug reports.  Negative values fail inside the _fixed API unless they
-    * correspond to the flag values.
-    */
-   if (output_gamma > 0 && output_gamma < 128)
-      output_gamma *= PNG_FP_1;
-
-   /* This preserves -1 and -2 exactly: */
-   output_gamma = floor(output_gamma + .5);
-
-   if (output_gamma > PNG_FP_MAX || output_gamma < PNG_FP_MIN)
-      png_fixed_error(png_ptr, "gamma value");
-
-   return (png_fixed_point)output_gamma;
-}
-#  endif
-#endif /* READ_ALPHA_MODE || READ_GAMMA */
-
-#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
-void PNGFAPI
-png_set_alpha_mode_fixed(png_structrp png_ptr, int mode,
-    png_fixed_point output_gamma)
-{
-   int compose = 0;
-   png_fixed_point file_gamma;
-
-   png_debug(1, "in png_set_alpha_mode");
-
-   if (png_rtran_ok(png_ptr, 0) == 0)
-      return;
-
-   output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/);
-
-   /* Validate the value to ensure it is in a reasonable range. The value
-    * is expected to be 1 or greater, but this range test allows for some
-    * viewing correction values.  The intent is to weed out users of this API
-    * who use the inverse of the gamma value accidentally!  Since some of these
-    * values are reasonable this may have to be changed:
-    *
-    * 1.6.x: changed from 0.07..3 to 0.01..100 (to accommodate the optimal 16-bit
-    * gamma of 36, and its reciprocal.)
-    */
-   if (output_gamma < 1000 || output_gamma > 10000000)
-      png_error(png_ptr, "output gamma out of expected range");
-
-   /* The default file gamma is the inverse of the output gamma; the output
-    * gamma may be changed below so get the file value first:
-    */
-   file_gamma = png_reciprocal(output_gamma);
-
-   /* There are really 8 possibilities here, composed of any combination
-    * of:
-    *
-    *    premultiply the color channels
-    *    do not encode non-opaque pixels
-    *    encode the alpha as well as the color channels
-    *
-    * The differences disappear if the input/output ('screen') gamma is 1.0,
-    * because then the encoding is a no-op and there is only the choice of
-    * premultiplying the color channels or not.
-    *
-    * png_set_alpha_mode and png_set_background interact because both use
-    * png_compose to do the work.  Calling both is only useful when
-    * png_set_alpha_mode is used to set the default mode - PNG_ALPHA_PNG - along
-    * with a default gamma value.  Otherwise PNG_COMPOSE must not be set.
-    */
-   switch (mode)
-   {
-      case PNG_ALPHA_PNG:        /* default: png standard */
-         /* No compose, but it may be set by png_set_background! */
-         png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
-         png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
-         break;
-
-      case PNG_ALPHA_ASSOCIATED: /* color channels premultiplied */
-         compose = 1;
-         png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
-         png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
-         /* The output is linear: */
-         output_gamma = PNG_FP_1;
-         break;
-
-      case PNG_ALPHA_OPTIMIZED:  /* associated, non-opaque pixels linear */
-         compose = 1;
-         png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
-         png_ptr->flags |= PNG_FLAG_OPTIMIZE_ALPHA;
-         /* output_gamma records the encoding of opaque pixels! */
-         break;
-
-      case PNG_ALPHA_BROKEN:     /* associated, non-linear, alpha encoded */
-         compose = 1;
-         png_ptr->transformations |= PNG_ENCODE_ALPHA;
-         png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
-         break;
-
-      default:
-         png_error(png_ptr, "invalid alpha mode");
-   }
-
-   /* Only set the default gamma if the file gamma has not been set (this has
-    * the side effect that the gamma in a second call to png_set_alpha_mode will
-    * be ignored.)
-    */
-   if (png_ptr->colorspace.gamma == 0)
-   {
-      png_ptr->colorspace.gamma = file_gamma;
-      png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
-   }
-
-   /* But always set the output gamma: */
-   png_ptr->screen_gamma = output_gamma;
-
-   /* Finally, if pre-multiplying, set the background fields to achieve the
-    * desired result.
-    */
-   if (compose != 0)
-   {
-      /* And obtain alpha pre-multiplication by composing on black: */
-      memset(&png_ptr->background, 0, (sizeof png_ptr->background));
-      png_ptr->background_gamma = png_ptr->colorspace.gamma; /* just in case */
-      png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_FILE;
-      png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND;
-
-      if ((png_ptr->transformations & PNG_COMPOSE) != 0)
-         png_error(png_ptr,
-             "conflicting calls to set alpha mode and background");
-
-      png_ptr->transformations |= PNG_COMPOSE;
-   }
-}
-
-#  ifdef PNG_FLOATING_POINT_SUPPORTED
-void PNGAPI
-png_set_alpha_mode(png_structrp png_ptr, int mode, double output_gamma)
-{
-   png_set_alpha_mode_fixed(png_ptr, mode, convert_gamma_value(png_ptr,
-       output_gamma));
-}
-#  endif
-#endif
-
-#ifdef PNG_READ_QUANTIZE_SUPPORTED
-/* Dither file to 8-bit.  Supply a palette, the current number
- * of elements in the palette, the maximum number of elements
- * allowed, and a histogram if possible.  If the current number
- * of colors is greater than the maximum number, the palette will be
- * modified to fit in the maximum number.  "full_quantize" indicates
- * whether we need a quantizing cube set up for RGB images, or if we
- * simply are reducing the number of colors in a paletted image.
- */
-
-typedef struct png_dsort_struct
-{
-   struct png_dsort_struct * next;
-   png_byte left;
-   png_byte right;
-} png_dsort;
-typedef png_dsort *   png_dsortp;
-typedef png_dsort * * png_dsortpp;
-
-void PNGAPI
-png_set_quantize(png_structrp png_ptr, png_colorp palette,
-    int num_palette, int maximum_colors, png_const_uint_16p histogram,
-    int full_quantize)
-{
-   png_debug(1, "in png_set_quantize");
-
-   if (png_rtran_ok(png_ptr, 0) == 0)
-      return;
-
-   png_ptr->transformations |= PNG_QUANTIZE;
-
-   if (full_quantize == 0)
-   {
-      int i;
-
-      png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr,
-          (png_alloc_size_t)((png_uint_32)num_palette * (sizeof (png_byte))));
-      for (i = 0; i < num_palette; i++)
-         png_ptr->quantize_index[i] = (png_byte)i;
-   }
-
-   if (num_palette > maximum_colors)
-   {
-      if (histogram != NULL)
-      {
-         /* This is easy enough, just throw out the least used colors.
-          * Perhaps not the best solution, but good enough.
-          */
-
-         int i;
-
-         /* Initialize an array to sort colors */
-         png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr,
-             (png_alloc_size_t)((png_uint_32)num_palette * (sizeof (png_byte))));
-
-         /* Initialize the quantize_sort array */
-         for (i = 0; i < num_palette; i++)
-            png_ptr->quantize_sort[i] = (png_byte)i;
-
-         /* Find the least used palette entries by starting a
-          * bubble sort, and running it until we have sorted
-          * out enough colors.  Note that we don't care about
-          * sorting all the colors, just finding which are
-          * least used.
-          */
-
-         for (i = num_palette - 1; i >= maximum_colors; i--)
-         {
-            int done; /* To stop early if the list is pre-sorted */
-            int j;
-
-            done = 1;
-            for (j = 0; j < i; j++)
-            {
-               if (histogram[png_ptr->quantize_sort[j]]
-                   < histogram[png_ptr->quantize_sort[j + 1]])
-               {
-                  png_byte t;
-
-                  t = png_ptr->quantize_sort[j];
-                  png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1];
-                  png_ptr->quantize_sort[j + 1] = t;
-                  done = 0;
-               }
-            }
-
-            if (done != 0)
-               break;
-         }
-
-         /* Swap the palette around, and set up a table, if necessary */
-         if (full_quantize != 0)
-         {
-            int j = num_palette;
-
-            /* Put all the useful colors within the max, but don't
-             * move the others.
-             */
-            for (i = 0; i < maximum_colors; i++)
-            {
-               if ((int)png_ptr->quantize_sort[i] >= maximum_colors)
-               {
-                  do
-                     j--;
-                  while ((int)png_ptr->quantize_sort[j] >= maximum_colors);
-
-                  palette[i] = palette[j];
-               }
-            }
-         }
-         else
-         {
-            int j = num_palette;
-
-            /* Move all the used colors inside the max limit, and
-             * develop a translation table.
-             */
-            for (i = 0; i < maximum_colors; i++)
-            {
-               /* Only move the colors we need to */
-               if ((int)png_ptr->quantize_sort[i] >= maximum_colors)
-               {
-                  png_color tmp_color;
-
-                  do
-                     j--;
-                  while ((int)png_ptr->quantize_sort[j] >= maximum_colors);
-
-                  tmp_color = palette[j];
-                  palette[j] = palette[i];
-                  palette[i] = tmp_color;
-                  /* Indicate where the color went */
-                  png_ptr->quantize_index[j] = (png_byte)i;
-                  png_ptr->quantize_index[i] = (png_byte)j;
-               }
-            }
-
-            /* Find closest color for those colors we are not using */
-            for (i = 0; i < num_palette; i++)
-            {
-               if ((int)png_ptr->quantize_index[i] >= maximum_colors)
-               {
-                  int min_d, k, min_k, d_index;
-
-                  /* Find the closest color to one we threw out */
-                  d_index = png_ptr->quantize_index[i];
-                  min_d = PNG_COLOR_DIST(palette[d_index], palette[0]);
-                  for (k = 1, min_k = 0; k < maximum_colors; k++)
-                  {
-                     int d;
-
-                     d = PNG_COLOR_DIST(palette[d_index], palette[k]);
-
-                     if (d < min_d)
-                     {
-                        min_d = d;
-                        min_k = k;
-                     }
-                  }
-                  /* Point to closest color */
-                  png_ptr->quantize_index[i] = (png_byte)min_k;
-               }
-            }
-         }
-         png_free(png_ptr, png_ptr->quantize_sort);
-         png_ptr->quantize_sort = NULL;
-      }
-      else
-      {
-         /* This is much harder to do simply (and quickly).  Perhaps
-          * we need to go through a median cut routine, but those
-          * don't always behave themselves with only a few colors
-          * as input.  So we will just find the closest two colors,
-          * and throw out one of them (chosen somewhat randomly).
-          * [We don't understand this at all, so if someone wants to
-          *  work on improving it, be our guest - AED, GRP]
-          */
-         int i;
-         int max_d;
-         int num_new_palette;
-         png_dsortp t;
-         png_dsortpp hash;
-
-         t = NULL;
-
-         /* Initialize palette index arrays */
-         png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr,
-             (png_alloc_size_t)((png_uint_32)num_palette *
-             (sizeof (png_byte))));
-         png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr,
-             (png_alloc_size_t)((png_uint_32)num_palette *
-             (sizeof (png_byte))));
-
-         /* Initialize the sort array */
-         for (i = 0; i < num_palette; i++)
-         {
-            png_ptr->index_to_palette[i] = (png_byte)i;
-            png_ptr->palette_to_index[i] = (png_byte)i;
-         }
-
-         hash = (png_dsortpp)png_calloc(png_ptr, (png_alloc_size_t)(769 *
-             (sizeof (png_dsortp))));
-
-         num_new_palette = num_palette;
-
-         /* Initial wild guess at how far apart the farthest pixel
-          * pair we will be eliminating will be.  Larger
-          * numbers mean more areas will be allocated, Smaller
-          * numbers run the risk of not saving enough data, and
-          * having to do this all over again.
-          *
-          * I have not done extensive checking on this number.
-          */
-         max_d = 96;
-
-         while (num_new_palette > maximum_colors)
-         {
-            for (i = 0; i < num_new_palette - 1; i++)
-            {
-               int j;
-
-               for (j = i + 1; j < num_new_palette; j++)
-               {
-                  int d;
-
-                  d = PNG_COLOR_DIST(palette[i], palette[j]);
-
-                  if (d <= max_d)
-                  {
-
-                     t = (png_dsortp)png_malloc_warn(png_ptr,
-                         (png_alloc_size_t)(sizeof (png_dsort)));
-
-                     if (t == NULL)
-                         break;
-
-                     t->next = hash[d];
-                     t->left = (png_byte)i;
-                     t->right = (png_byte)j;
-                     hash[d] = t;
-                  }
-               }
-               if (t == NULL)
-                  break;
-            }
-
-            if (t != NULL)
-            for (i = 0; i <= max_d; i++)
-            {
-               if (hash[i] != NULL)
-               {
-                  png_dsortp p;
-
-                  for (p = hash[i]; p; p = p->next)
-                  {
-                     if ((int)png_ptr->index_to_palette[p->left]
-                         < num_new_palette &&
-                         (int)png_ptr->index_to_palette[p->right]
-                         < num_new_palette)
-                     {
-                        int j, next_j;
-
-                        if (num_new_palette & 0x01)
-                        {
-                           j = p->left;
-                           next_j = p->right;
-                        }
-                        else
-                        {
-                           j = p->right;
-                           next_j = p->left;
-                        }
-
-                        num_new_palette--;
-                        palette[png_ptr->index_to_palette[j]]
-                            = palette[num_new_palette];
-                        if (full_quantize == 0)
-                        {
-                           int k;
-
-                           for (k = 0; k < num_palette; k++)
-                           {
-                              if (png_ptr->quantize_index[k] ==
-                                  png_ptr->index_to_palette[j])
-                                 png_ptr->quantize_index[k] =
-                                     png_ptr->index_to_palette[next_j];
-
-                              if ((int)png_ptr->quantize_index[k] ==
-                                  num_new_palette)
-                                 png_ptr->quantize_index[k] =
-                                     png_ptr->index_to_palette[j];
-                           }
-                        }
-
-                        png_ptr->index_to_palette[png_ptr->palette_to_index
-                            [num_new_palette]] = png_ptr->index_to_palette[j];
-
-                        png_ptr->palette_to_index[png_ptr->index_to_palette[j]]
-                            = png_ptr->palette_to_index[num_new_palette];
-
-                        png_ptr->index_to_palette[j] =
-                            (png_byte)num_new_palette;
-
-                        png_ptr->palette_to_index[num_new_palette] =
-                            (png_byte)j;
-                     }
-                     if (num_new_palette <= maximum_colors)
-                        break;
-                  }
-                  if (num_new_palette <= maximum_colors)
-                     break;
-               }
-            }
-
-            for (i = 0; i < 769; i++)
-            {
-               if (hash[i] != NULL)
-               {
-                  png_dsortp p = hash[i];
-                  while (p)
-                  {
-                     t = p->next;
-                     png_free(png_ptr, p);
-                     p = t;
-                  }
-               }
-               hash[i] = 0;
-            }
-            max_d += 96;
-         }
-         png_free(png_ptr, hash);
-         png_free(png_ptr, png_ptr->palette_to_index);
-         png_free(png_ptr, png_ptr->index_to_palette);
-         png_ptr->palette_to_index = NULL;
-         png_ptr->index_to_palette = NULL;
-      }
-      num_palette = maximum_colors;
-   }
-   if (png_ptr->palette == NULL)
-   {
-      png_ptr->palette = palette;
-   }
-   png_ptr->num_palette = (png_uint_16)num_palette;
-
-   if (full_quantize != 0)
-   {
-      int i;
-      png_bytep distance;
-      int total_bits = PNG_QUANTIZE_RED_BITS + PNG_QUANTIZE_GREEN_BITS +
-          PNG_QUANTIZE_BLUE_BITS;
-      int num_red = (1 << PNG_QUANTIZE_RED_BITS);
-      int num_green = (1 << PNG_QUANTIZE_GREEN_BITS);
-      int num_blue = (1 << PNG_QUANTIZE_BLUE_BITS);
-      size_t num_entries = ((size_t)1 << total_bits);
-
-      png_ptr->palette_lookup = (png_bytep)png_calloc(png_ptr,
-          (png_alloc_size_t)(num_entries * (sizeof (png_byte))));
-
-      distance = (png_bytep)png_malloc(png_ptr, (png_alloc_size_t)(num_entries *
-          (sizeof (png_byte))));
-
-      memset(distance, 0xff, num_entries * (sizeof (png_byte)));
-
-      for (i = 0; i < num_palette; i++)
-      {
-         int ir, ig, ib;
-         int r = (palette[i].red >> (8 - PNG_QUANTIZE_RED_BITS));
-         int g = (palette[i].green >> (8 - PNG_QUANTIZE_GREEN_BITS));
-         int b = (palette[i].blue >> (8 - PNG_QUANTIZE_BLUE_BITS));
-
-         for (ir = 0; ir < num_red; ir++)
-         {
-            /* int dr = abs(ir - r); */
-            int dr = ((ir > r) ? ir - r : r - ir);
-            int index_r = (ir << (PNG_QUANTIZE_BLUE_BITS +
-                PNG_QUANTIZE_GREEN_BITS));
-
-            for (ig = 0; ig < num_green; ig++)
-            {
-               /* int dg = abs(ig - g); */
-               int dg = ((ig > g) ? ig - g : g - ig);
-               int dt = dr + dg;
-               int dm = ((dr > dg) ? dr : dg);
-               int index_g = index_r | (ig << PNG_QUANTIZE_BLUE_BITS);
-
-               for (ib = 0; ib < num_blue; ib++)
-               {
-                  int d_index = index_g | ib;
-                  /* int db = abs(ib - b); */
-                  int db = ((ib > b) ? ib - b : b - ib);
-                  int dmax = ((dm > db) ? dm : db);
-                  int d = dmax + dt + db;
-
-                  if (d < (int)distance[d_index])
-                  {
-                     distance[d_index] = (png_byte)d;
-                     png_ptr->palette_lookup[d_index] = (png_byte)i;
-                  }
-               }
-            }
-         }
-      }
-
-      png_free(png_ptr, distance);
-   }
-}
-#endif /* READ_QUANTIZE */
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
-void PNGFAPI
-png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma,
-    png_fixed_point file_gamma)
-{
-   png_debug(1, "in png_set_gamma_fixed");
-
-   if (png_rtran_ok(png_ptr, 0) == 0)
-      return;
-
-   /* New in libpng-1.5.4 - reserve particular negative values as flags. */
-   scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/);
-   file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/);
-
-   /* Checking the gamma values for being >0 was added in 1.5.4 along with the
-    * premultiplied alpha support; this actually hides an undocumented feature
-    * of the previous implementation which allowed gamma processing to be
-    * disabled in background handling.  There is no evidence (so far) that this
-    * was being used; however, png_set_background itself accepted and must still
-    * accept '0' for the gamma value it takes, because it isn't always used.
-    *
-    * Since this is an API change (albeit a very minor one that removes an
-    * undocumented API feature) the following checks were only enabled in
-    * libpng-1.6.0.
-    */
-   if (file_gamma <= 0)
-      png_error(png_ptr, "invalid file gamma in png_set_gamma");
-
-   if (scrn_gamma <= 0)
-      png_error(png_ptr, "invalid screen gamma in png_set_gamma");
-
-   /* Set the gamma values unconditionally - this overrides the value in the PNG
-    * file if a gAMA chunk was present.  png_set_alpha_mode provides a
-    * different, easier, way to default the file gamma.
-    */
-   png_ptr->colorspace.gamma = file_gamma;
-   png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
-   png_ptr->screen_gamma = scrn_gamma;
-}
-
-#  ifdef PNG_FLOATING_POINT_SUPPORTED
-void PNGAPI
-png_set_gamma(png_structrp png_ptr, double scrn_gamma, double file_gamma)
-{
-   png_set_gamma_fixed(png_ptr, convert_gamma_value(png_ptr, scrn_gamma),
-       convert_gamma_value(png_ptr, file_gamma));
-}
-#  endif /* FLOATING_POINT */
-#endif /* READ_GAMMA */
-
-#ifdef PNG_READ_EXPAND_SUPPORTED
-/* Expand paletted images to RGB, expand grayscale images of
- * less than 8-bit depth to 8-bit depth, and expand tRNS chunks
- * to alpha channels.
- */
-void PNGAPI
-png_set_expand(png_structrp png_ptr)
-{
-   png_debug(1, "in png_set_expand");
-
-   if (png_rtran_ok(png_ptr, 0) == 0)
-      return;
-
-   png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
-}
-
-/* GRR 19990627:  the following three functions currently are identical
- *  to png_set_expand().  However, it is entirely reasonable that someone
- *  might wish to expand an indexed image to RGB but *not* expand a single,
- *  fully transparent palette entry to a full alpha channel--perhaps instead
- *  convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace
- *  the transparent color with a particular RGB value, or drop tRNS entirely.
- *  IOW, a future version of the library may make the transformations flag
- *  a bit more fine-grained, with separate bits for each of these three
- *  functions.
- *
- *  More to the point, these functions make it obvious what libpng will be
- *  doing, whereas "expand" can (and does) mean any number of things.
- *
- *  GRP 20060307: In libpng-1.2.9, png_set_gray_1_2_4_to_8() was modified
- *  to expand only the sample depth but not to expand the tRNS to alpha
- *  and its name was changed to png_set_expand_gray_1_2_4_to_8().
- */
-
-/* Expand paletted images to RGB. */
-void PNGAPI
-png_set_palette_to_rgb(png_structrp png_ptr)
-{
-   png_debug(1, "in png_set_palette_to_rgb");
-
-   if (png_rtran_ok(png_ptr, 0) == 0)
-      return;
-
-   png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
-}
-
-/* Expand grayscale images of less than 8-bit depth to 8 bits. */
-void PNGAPI
-png_set_expand_gray_1_2_4_to_8(png_structrp png_ptr)
-{
-   png_debug(1, "in png_set_expand_gray_1_2_4_to_8");
-
-   if (png_rtran_ok(png_ptr, 0) == 0)
-      return;
-
-   png_ptr->transformations |= PNG_EXPAND;
-}
-
-/* Expand tRNS chunks to alpha channels. */
-void PNGAPI
-png_set_tRNS_to_alpha(png_structrp png_ptr)
-{
-   png_debug(1, "in png_set_tRNS_to_alpha");
-
-   if (png_rtran_ok(png_ptr, 0) == 0)
-      return;
-
-   png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
-}
-#endif /* READ_EXPAND */
-
-#ifdef PNG_READ_EXPAND_16_SUPPORTED
-/* Expand to 16-bit channels, expand the tRNS chunk too (because otherwise
- * it may not work correctly.)
- */
-void PNGAPI
-png_set_expand_16(png_structrp png_ptr)
-{
-   png_debug(1, "in png_set_expand_16");
-
-   if (png_rtran_ok(png_ptr, 0) == 0)
-      return;
-
-   png_ptr->transformations |= (PNG_EXPAND_16 | PNG_EXPAND | PNG_EXPAND_tRNS);
-}
-#endif
-
-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
-void PNGAPI
-png_set_gray_to_rgb(png_structrp png_ptr)
-{
-   png_debug(1, "in png_set_gray_to_rgb");
-
-   if (png_rtran_ok(png_ptr, 0) == 0)
-      return;
-
-   /* Because rgb must be 8 bits or more: */
-   png_set_expand_gray_1_2_4_to_8(png_ptr);
-   png_ptr->transformations |= PNG_GRAY_TO_RGB;
-}
-#endif
-
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-void PNGFAPI
-png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action,
-    png_fixed_point red, png_fixed_point green)
-{
-   png_debug(1, "in png_set_rgb_to_gray");
-
-   /* Need the IHDR here because of the check on color_type below. */
-   /* TODO: fix this */
-   if (png_rtran_ok(png_ptr, 1) == 0)
-      return;
-
-   switch (error_action)
-   {
-      case PNG_ERROR_ACTION_NONE:
-         png_ptr->transformations |= PNG_RGB_TO_GRAY;
-         break;
-
-      case PNG_ERROR_ACTION_WARN:
-         png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN;
-         break;
-
-      case PNG_ERROR_ACTION_ERROR:
-         png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR;
-         break;
-
-      default:
-         png_error(png_ptr, "invalid error action to rgb_to_gray");
-   }
-
-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-#ifdef PNG_READ_EXPAND_SUPPORTED
-      png_ptr->transformations |= PNG_EXPAND;
-#else
-   {
-      /* Make this an error in 1.6 because otherwise the application may assume
-       * that it just worked and get a memory overwrite.
-       */
-      png_error(png_ptr,
-          "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED");
-
-      /* png_ptr->transformations &= ~PNG_RGB_TO_GRAY; */
-   }
-#endif
-   {
-      if (red >= 0 && green >= 0 && red + green <= PNG_FP_1)
-      {
-         png_uint_16 red_int, green_int;
-
-         /* NOTE: this calculation does not round, but this behavior is retained
-          * for consistency; the inaccuracy is very small.  The code here always
-          * overwrites the coefficients, regardless of whether they have been
-          * defaulted or set already.
-          */
-         red_int = (png_uint_16)(((png_uint_32)red*32768)/100000);
-         green_int = (png_uint_16)(((png_uint_32)green*32768)/100000);
-
-         png_ptr->rgb_to_gray_red_coeff   = red_int;
-         png_ptr->rgb_to_gray_green_coeff = green_int;
-         png_ptr->rgb_to_gray_coefficients_set = 1;
-      }
-
-      else
-      {
-         if (red >= 0 && green >= 0)
-            png_app_warning(png_ptr,
-                "ignoring out of range rgb_to_gray coefficients");
-
-         /* Use the defaults, from the cHRM chunk if set, else the historical
-          * values which are close to the sRGB/HDTV/ITU-Rec 709 values.  See
-          * png_do_rgb_to_gray for more discussion of the values.  In this case
-          * the coefficients are not marked as 'set' and are not overwritten if
-          * something has already provided a default.
-          */
-         if (png_ptr->rgb_to_gray_red_coeff == 0 &&
-             png_ptr->rgb_to_gray_green_coeff == 0)
-         {
-            png_ptr->rgb_to_gray_red_coeff   = 6968;
-            png_ptr->rgb_to_gray_green_coeff = 23434;
-            /* png_ptr->rgb_to_gray_blue_coeff  = 2366; */
-         }
-      }
-   }
-}
-
-#ifdef PNG_FLOATING_POINT_SUPPORTED
-/* Convert a RGB image to a grayscale of the same width.  This allows us,
- * for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image.
- */
-
-void PNGAPI
-png_set_rgb_to_gray(png_structrp png_ptr, int error_action, double red,
-    double green)
-{
-   png_set_rgb_to_gray_fixed(png_ptr, error_action,
-       png_fixed(png_ptr, red, "rgb to gray red coefficient"),
-      png_fixed(png_ptr, green, "rgb to gray green coefficient"));
-}
-#endif /* FLOATING POINT */
-
-#endif /* RGB_TO_GRAY */
-
-#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
-    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
-void PNGAPI
-png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
-    read_user_transform_fn)
-{
-   png_debug(1, "in png_set_read_user_transform_fn");
-
-#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
-   png_ptr->transformations |= PNG_USER_TRANSFORM;
-   png_ptr->read_user_transform_fn = read_user_transform_fn;
-#endif
-}
-#endif
-
-#ifdef PNG_READ_TRANSFORMS_SUPPORTED
-#ifdef PNG_READ_GAMMA_SUPPORTED
-/* In the case of gamma transformations only do transformations on images where
- * the [file] gamma and screen_gamma are not close reciprocals, otherwise it
- * slows things down slightly, and also needlessly introduces small errors.
- */
-static int /* PRIVATE */
-png_gamma_threshold(png_fixed_point screen_gamma, png_fixed_point file_gamma)
-{
-   /* PNG_GAMMA_THRESHOLD is the threshold for performing gamma
-    * correction as a difference of the overall transform from 1.0
-    *
-    * We want to compare the threshold with s*f - 1, if we get
-    * overflow here it is because of wacky gamma values so we
-    * turn on processing anyway.
-    */
-   png_fixed_point gtest;
-   return !png_muldiv(&gtest, screen_gamma, file_gamma, PNG_FP_1) ||
-       png_gamma_significant(gtest);
-}
-#endif
-
-/* Initialize everything needed for the read.  This includes modifying
- * the palette.
- */
-
-/* For the moment 'png_init_palette_transformations' and
- * 'png_init_rgb_transformations' only do some flag canceling optimizations.
- * The intent is that these two routines should have palette or rgb operations
- * extracted from 'png_init_read_transformations'.
- */
-static void /* PRIVATE */
-png_init_palette_transformations(png_structrp png_ptr)
-{
-   /* Called to handle the (input) palette case.  In png_do_read_transformations
-    * the first step is to expand the palette if requested, so this code must
-    * take care to only make changes that are invariant with respect to the
-    * palette expansion, or only do them if there is no expansion.
-    *
-    * STRIP_ALPHA has already been handled in the caller (by setting num_trans
-    * to 0.)
-    */
-   int input_has_alpha = 0;
-   int input_has_transparency = 0;
-
-   if (png_ptr->num_trans > 0)
-   {
-      int i;
-
-      /* Ignore if all the entries are opaque (unlikely!) */
-      for (i=0; i<png_ptr->num_trans; ++i)
-      {
-         if (png_ptr->trans_alpha[i] == 255)
-            continue;
-         else if (png_ptr->trans_alpha[i] == 0)
-            input_has_transparency = 1;
-         else
-         {
-            input_has_transparency = 1;
-            input_has_alpha = 1;
-            break;
-         }
-      }
-   }
-
-   /* If no alpha we can optimize. */
-   if (input_has_alpha == 0)
-   {
-      /* Any alpha means background and associative alpha processing is
-       * required, however if the alpha is 0 or 1 throughout OPTIMIZE_ALPHA
-       * and ENCODE_ALPHA are irrelevant.
-       */
-      png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
-      png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
-
-      if (input_has_transparency == 0)
-         png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND);
-   }
-
-#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED)
-   /* png_set_background handling - deals with the complexity of whether the
-    * background color is in the file format or the screen format in the case
-    * where an 'expand' will happen.
-    */
-
-   /* The following code cannot be entered in the alpha pre-multiplication case
-    * because PNG_BACKGROUND_EXPAND is cancelled below.
-    */
-   if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) != 0 &&
-       (png_ptr->transformations & PNG_EXPAND) != 0)
-   {
-      {
-         png_ptr->background.red   =
-             png_ptr->palette[png_ptr->background.index].red;
-         png_ptr->background.green =
-             png_ptr->palette[png_ptr->background.index].green;
-         png_ptr->background.blue  =
-             png_ptr->palette[png_ptr->background.index].blue;
-
-#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
-         if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0)
-         {
-            if ((png_ptr->transformations & PNG_EXPAND_tRNS) == 0)
-            {
-               /* Invert the alpha channel (in tRNS) unless the pixels are
-                * going to be expanded, in which case leave it for later
-                */
-               int i, istop = png_ptr->num_trans;
-
-               for (i = 0; i < istop; i++)
-                  png_ptr->trans_alpha[i] =
-                      (png_byte)(255 - png_ptr->trans_alpha[i]);
-            }
-         }
-#endif /* READ_INVERT_ALPHA */
-      }
-   } /* background expand and (therefore) no alpha association. */
-#endif /* READ_EXPAND && READ_BACKGROUND */
-}
-
-static void /* PRIVATE */
-png_init_rgb_transformations(png_structrp png_ptr)
-{
-   /* Added to libpng-1.5.4: check the color type to determine whether there
-    * is any alpha or transparency in the image and simply cancel the
-    * background and alpha mode stuff if there isn't.
-    */
-   int input_has_alpha = (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0;
-   int input_has_transparency = png_ptr->num_trans > 0;
-
-   /* If no alpha we can optimize. */
-   if (input_has_alpha == 0)
-   {
-      /* Any alpha means background and associative alpha processing is
-       * required, however if the alpha is 0 or 1 throughout OPTIMIZE_ALPHA
-       * and ENCODE_ALPHA are irrelevant.
-       */
-#     ifdef PNG_READ_ALPHA_MODE_SUPPORTED
-         png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
-         png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
-#     endif
-
-      if (input_has_transparency == 0)
-         png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND);
-   }
-
-#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED)
-   /* png_set_background handling - deals with the complexity of whether the
-    * background color is in the file format or the screen format in the case
-    * where an 'expand' will happen.
-    */
-
-   /* The following code cannot be entered in the alpha pre-multiplication case
-    * because PNG_BACKGROUND_EXPAND is cancelled below.
-    */
-   if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) != 0 &&
-       (png_ptr->transformations & PNG_EXPAND) != 0 &&
-       (png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0)
-       /* i.e., GRAY or GRAY_ALPHA */
-   {
-      {
-         /* Expand background and tRNS chunks */
-         int gray = png_ptr->background.gray;
-         int trans_gray = png_ptr->trans_color.gray;
-
-         switch (png_ptr->bit_depth)
-         {
-            case 1:
-               gray *= 0xff;
-               trans_gray *= 0xff;
-               break;
-
-            case 2:
-               gray *= 0x55;
-               trans_gray *= 0x55;
-               break;
-
-            case 4:
-               gray *= 0x11;
-               trans_gray *= 0x11;
-               break;
-
-            default:
-
-            case 8:
-               /* FALLTHROUGH */ /*  (Already 8 bits) */
-
-            case 16:
-               /* Already a full 16 bits */
-               break;
-         }
-
-         png_ptr->background.red = png_ptr->background.green =
-            png_ptr->background.blue = (png_uint_16)gray;
-
-         if ((png_ptr->transformations & PNG_EXPAND_tRNS) == 0)
-         {
-            png_ptr->trans_color.red = png_ptr->trans_color.green =
-               png_ptr->trans_color.blue = (png_uint_16)trans_gray;
-         }
-      }
-   } /* background expand and (therefore) no alpha association. */
-#endif /* READ_EXPAND && READ_BACKGROUND */
-}
-
-void /* PRIVATE */
-png_init_read_transformations(png_structrp png_ptr)
-{
-   png_debug(1, "in png_init_read_transformations");
-
-   /* This internal function is called from png_read_start_row in pngrutil.c
-    * and it is called before the 'rowbytes' calculation is done, so the code
-    * in here can change or update the transformations flags.
-    *
-    * First do updates that do not depend on the details of the PNG image data
-    * being processed.
-    */
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
-   /* Prior to 1.5.4 these tests were performed from png_set_gamma, 1.5.4 adds
-    * png_set_alpha_mode and this is another source for a default file gamma so
-    * the test needs to be performed later - here.  In addition prior to 1.5.4
-    * the tests were repeated for the PALETTE color type here - this is no
-    * longer necessary (and doesn't seem to have been necessary before.)
-    */
-   {
-      /* The following temporary indicates if overall gamma correction is
-       * required.
-       */
-      int gamma_correction = 0;
-
-      if (png_ptr->colorspace.gamma != 0) /* has been set */
-      {
-         if (png_ptr->screen_gamma != 0) /* screen set too */
-            gamma_correction = png_gamma_threshold(png_ptr->colorspace.gamma,
-                png_ptr->screen_gamma);
-
-         else
-            /* Assume the output matches the input; a long time default behavior
-             * of libpng, although the standard has nothing to say about this.
-             */
-            png_ptr->screen_gamma = png_reciprocal(png_ptr->colorspace.gamma);
-      }
-
-      else if (png_ptr->screen_gamma != 0)
-         /* The converse - assume the file matches the screen, note that this
-          * perhaps undesirable default can (from 1.5.4) be changed by calling
-          * png_set_alpha_mode (even if the alpha handling mode isn't required
-          * or isn't changed from the default.)
-          */
-         png_ptr->colorspace.gamma = png_reciprocal(png_ptr->screen_gamma);
-
-      else /* neither are set */
-         /* Just in case the following prevents any processing - file and screen
-          * are both assumed to be linear and there is no way to introduce a
-          * third gamma value other than png_set_background with 'UNIQUE', and,
-          * prior to 1.5.4
-          */
-         png_ptr->screen_gamma = png_ptr->colorspace.gamma = PNG_FP_1;
-
-      /* We have a gamma value now. */
-      png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
-
-      /* Now turn the gamma transformation on or off as appropriate.  Notice
-       * that PNG_GAMMA just refers to the file->screen correction.  Alpha
-       * composition may independently cause gamma correction because it needs
-       * linear data (e.g. if the file has a gAMA chunk but the screen gamma
-       * hasn't been specified.)  In any case this flag may get turned off in
-       * the code immediately below if the transform can be handled outside the
-       * row loop.
-       */
-      if (gamma_correction != 0)
-         png_ptr->transformations |= PNG_GAMMA;
-
-      else
-         png_ptr->transformations &= ~PNG_GAMMA;
-   }
-#endif
-
-   /* Certain transformations have the effect of preventing other
-    * transformations that happen afterward in png_do_read_transformations;
-    * resolve the interdependencies here.  From the code of
-    * png_do_read_transformations the order is:
-    *
-    *  1) PNG_EXPAND (including PNG_EXPAND_tRNS)
-    *  2) PNG_STRIP_ALPHA (if no compose)
-    *  3) PNG_RGB_TO_GRAY
-    *  4) PNG_GRAY_TO_RGB iff !PNG_BACKGROUND_IS_GRAY
-    *  5) PNG_COMPOSE
-    *  6) PNG_GAMMA
-    *  7) PNG_STRIP_ALPHA (if compose)
-    *  8) PNG_ENCODE_ALPHA
-    *  9) PNG_SCALE_16_TO_8
-    * 10) PNG_16_TO_8
-    * 11) PNG_QUANTIZE (converts to palette)
-    * 12) PNG_EXPAND_16
-    * 13) PNG_GRAY_TO_RGB iff PNG_BACKGROUND_IS_GRAY
-    * 14) PNG_INVERT_MONO
-    * 15) PNG_INVERT_ALPHA
-    * 16) PNG_SHIFT
-    * 17) PNG_PACK
-    * 18) PNG_BGR
-    * 19) PNG_PACKSWAP
-    * 20) PNG_FILLER (includes PNG_ADD_ALPHA)
-    * 21) PNG_SWAP_ALPHA
-    * 22) PNG_SWAP_BYTES
-    * 23) PNG_USER_TRANSFORM [must be last]
-    */
-#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
-   if ((png_ptr->transformations & PNG_STRIP_ALPHA) != 0 &&
-       (png_ptr->transformations & PNG_COMPOSE) == 0)
-   {
-      /* Stripping the alpha channel happens immediately after the 'expand'
-       * transformations, before all other transformation, so it cancels out
-       * the alpha handling.  It has the side effect negating the effect of
-       * PNG_EXPAND_tRNS too:
-       */
-      png_ptr->transformations &= ~(PNG_BACKGROUND_EXPAND | PNG_ENCODE_ALPHA |
-         PNG_EXPAND_tRNS);
-      png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
-
-      /* Kill the tRNS chunk itself too.  Prior to 1.5.4 this did not happen
-       * so transparency information would remain just so long as it wasn't
-       * expanded.  This produces unexpected API changes if the set of things
-       * that do PNG_EXPAND_tRNS changes (perfectly possible given the
-       * documentation - which says ask for what you want, accept what you
-       * get.)  This makes the behavior consistent from 1.5.4:
-       */
-      png_ptr->num_trans = 0;
-   }
-#endif /* STRIP_ALPHA supported, no COMPOSE */
-
-#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
-   /* If the screen gamma is about 1.0 then the OPTIMIZE_ALPHA and ENCODE_ALPHA
-    * settings will have no effect.
-    */
-   if (png_gamma_significant(png_ptr->screen_gamma) == 0)
-   {
-      png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
-      png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
-   }
-#endif
-
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-   /* Make sure the coefficients for the rgb to gray conversion are set
-    * appropriately.
-    */
-   if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0)
-      png_colorspace_set_rgb_coefficients(png_ptr);
-#endif
-
-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
-#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED)
-   /* Detect gray background and attempt to enable optimization for
-    * gray --> RGB case.
-    *
-    * Note:  if PNG_BACKGROUND_EXPAND is set and color_type is either RGB or
-    * RGB_ALPHA (in which case need_expand is superfluous anyway), the
-    * background color might actually be gray yet not be flagged as such.
-    * This is not a problem for the current code, which uses
-    * PNG_BACKGROUND_IS_GRAY only to decide when to do the
-    * png_do_gray_to_rgb() transformation.
-    *
-    * TODO: this code needs to be revised to avoid the complexity and
-    * interdependencies.  The color type of the background should be recorded in
-    * png_set_background, along with the bit depth, then the code has a record
-    * of exactly what color space the background is currently in.
-    */
-   if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) != 0)
-   {
-      /* PNG_BACKGROUND_EXPAND: the background is in the file color space, so if
-       * the file was grayscale the background value is gray.
-       */
-      if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0)
-         png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;
-   }
-
-   else if ((png_ptr->transformations & PNG_COMPOSE) != 0)
-   {
-      /* PNG_COMPOSE: png_set_background was called with need_expand false,
-       * so the color is in the color space of the output or png_set_alpha_mode
-       * was called and the color is black.  Ignore RGB_TO_GRAY because that
-       * happens before GRAY_TO_RGB.
-       */
-      if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0)
-      {
-         if (png_ptr->background.red == png_ptr->background.green &&
-             png_ptr->background.red == png_ptr->background.blue)
-         {
-            png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;
-            png_ptr->background.gray = png_ptr->background.red;
-         }
-      }
-   }
-#endif /* READ_EXPAND && READ_BACKGROUND */
-#endif /* READ_GRAY_TO_RGB */
-
-   /* For indexed PNG data (PNG_COLOR_TYPE_PALETTE) many of the transformations
-    * can be performed directly on the palette, and some (such as rgb to gray)
-    * can be optimized inside the palette.  This is particularly true of the
-    * composite (background and alpha) stuff, which can be pretty much all done
-    * in the palette even if the result is expanded to RGB or gray afterward.
-    *
-    * NOTE: this is Not Yet Implemented, the code behaves as in 1.5.1 and
-    * earlier and the palette stuff is actually handled on the first row.  This
-    * leads to the reported bug that the palette returned by png_get_PLTE is not
-    * updated.
-    */
-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-      png_init_palette_transformations(png_ptr);
-
-   else
-      png_init_rgb_transformations(png_ptr);
-
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \
-   defined(PNG_READ_EXPAND_16_SUPPORTED)
-   if ((png_ptr->transformations & PNG_EXPAND_16) != 0 &&
-       (png_ptr->transformations & PNG_COMPOSE) != 0 &&
-       (png_ptr->transformations & PNG_BACKGROUND_EXPAND) == 0 &&
-       png_ptr->bit_depth != 16)
-   {
-      /* TODO: fix this.  Because the expand_16 operation is after the compose
-       * handling the background color must be 8, not 16, bits deep, but the
-       * application will supply a 16-bit value so reduce it here.
-       *
-       * The PNG_BACKGROUND_EXPAND code above does not expand to 16 bits at
-       * present, so that case is ok (until do_expand_16 is moved.)
-       *
-       * NOTE: this discards the low 16 bits of the user supplied background
-       * color, but until expand_16 works properly there is no choice!
-       */
-#     define CHOP(x) (x)=((png_uint_16)PNG_DIV257(x))
-      CHOP(png_ptr->background.red);
-      CHOP(png_ptr->background.green);
-      CHOP(png_ptr->background.blue);
-      CHOP(png_ptr->background.gray);
-#     undef CHOP
-   }
-#endif /* READ_BACKGROUND && READ_EXPAND_16 */
-
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \
-   (defined(PNG_READ_SCALE_16_TO_8_SUPPORTED) || \
-   defined(PNG_READ_STRIP_16_TO_8_SUPPORTED))
-   if ((png_ptr->transformations & (PNG_16_TO_8|PNG_SCALE_16_TO_8)) != 0 &&
-       (png_ptr->transformations & PNG_COMPOSE) != 0 &&
-       (png_ptr->transformations & PNG_BACKGROUND_EXPAND) == 0 &&
-       png_ptr->bit_depth == 16)
-   {
-      /* On the other hand, if a 16-bit file is to be reduced to 8-bits per
-       * component this will also happen after PNG_COMPOSE and so the background
-       * color must be pre-expanded here.
-       *
-       * TODO: fix this too.
-       */
-      png_ptr->background.red = (png_uint_16)(png_ptr->background.red * 257);
-      png_ptr->background.green =
-         (png_uint_16)(png_ptr->background.green * 257);
-      png_ptr->background.blue = (png_uint_16)(png_ptr->background.blue * 257);
-      png_ptr->background.gray = (png_uint_16)(png_ptr->background.gray * 257);
-   }
-#endif
-
-   /* NOTE: below 'PNG_READ_ALPHA_MODE_SUPPORTED' is presumed to also enable the
-    * background support (see the comments in scripts/pnglibconf.dfa), this
-    * allows pre-multiplication of the alpha channel to be implemented as
-    * compositing on black.  This is probably sub-optimal and has been done in
-    * 1.5.4 betas simply to enable external critique and testing (i.e. to
-    * implement the new API quickly, without lots of internal changes.)
-    */
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
-#  ifdef PNG_READ_BACKGROUND_SUPPORTED
-      /* Includes ALPHA_MODE */
-      png_ptr->background_1 = png_ptr->background;
-#  endif
-
-   /* This needs to change - in the palette image case a whole set of tables are
-    * built when it would be quicker to just calculate the correct value for
-    * each palette entry directly.  Also, the test is too tricky - why check
-    * PNG_RGB_TO_GRAY if PNG_GAMMA is not set?  The answer seems to be that
-    * PNG_GAMMA is cancelled even if the gamma is known?  The test excludes the
-    * PNG_COMPOSE case, so apparently if there is no *overall* gamma correction
-    * the gamma tables will not be built even if composition is required on a
-    * gamma encoded value.
-    *
-    * In 1.5.4 this is addressed below by an additional check on the individual
-    * file gamma - if it is not 1.0 both RGB_TO_GRAY and COMPOSE need the
-    * tables.
-    */
-   if ((png_ptr->transformations & PNG_GAMMA) != 0 ||
-       ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0 &&
-        (png_gamma_significant(png_ptr->colorspace.gamma) != 0 ||
-         png_gamma_significant(png_ptr->screen_gamma) != 0)) ||
-        ((png_ptr->transformations & PNG_COMPOSE) != 0 &&
-         (png_gamma_significant(png_ptr->colorspace.gamma) != 0 ||
-          png_gamma_significant(png_ptr->screen_gamma) != 0
-#  ifdef PNG_READ_BACKGROUND_SUPPORTED
-         || (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_UNIQUE &&
-           png_gamma_significant(png_ptr->background_gamma) != 0)
-#  endif
-        )) || ((png_ptr->transformations & PNG_ENCODE_ALPHA) != 0 &&
-       png_gamma_significant(png_ptr->screen_gamma) != 0))
-   {
-      png_build_gamma_table(png_ptr, png_ptr->bit_depth);
-
-#ifdef PNG_READ_BACKGROUND_SUPPORTED
-      if ((png_ptr->transformations & PNG_COMPOSE) != 0)
-      {
-         /* Issue a warning about this combination: because RGB_TO_GRAY is
-          * optimized to do the gamma transform if present yet do_background has
-          * to do the same thing if both options are set a
-          * double-gamma-correction happens.  This is true in all versions of
-          * libpng to date.
-          */
-         if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0)
-            png_warning(png_ptr,
-                "libpng does not support gamma+background+rgb_to_gray");
-
-         if ((png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) != 0)
-         {
-            /* We don't get to here unless there is a tRNS chunk with non-opaque
-             * entries - see the checking code at the start of this function.
-             */
-            png_color back, back_1;
-            png_colorp palette = png_ptr->palette;
-            int num_palette = png_ptr->num_palette;
-            int i;
-            if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE)
-            {
-
-               back.red = png_ptr->gamma_table[png_ptr->background.red];
-               back.green = png_ptr->gamma_table[png_ptr->background.green];
-               back.blue = png_ptr->gamma_table[png_ptr->background.blue];
-
-               back_1.red = png_ptr->gamma_to_1[png_ptr->background.red];
-               back_1.green = png_ptr->gamma_to_1[png_ptr->background.green];
-               back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue];
-            }
-            else
-            {
-               png_fixed_point g, gs;
-
-               switch (png_ptr->background_gamma_type)
-               {
-                  case PNG_BACKGROUND_GAMMA_SCREEN:
-                     g = (png_ptr->screen_gamma);
-                     gs = PNG_FP_1;
-                     break;
-
-                  case PNG_BACKGROUND_GAMMA_FILE:
-                     g = png_reciprocal(png_ptr->colorspace.gamma);
-                     gs = png_reciprocal2(png_ptr->colorspace.gamma,
-                         png_ptr->screen_gamma);
-                     break;
-
-                  case PNG_BACKGROUND_GAMMA_UNIQUE:
-                     g = png_reciprocal(png_ptr->background_gamma);
-                     gs = png_reciprocal2(png_ptr->background_gamma,
-                         png_ptr->screen_gamma);
-                     break;
-                  default:
-                     g = PNG_FP_1;    /* back_1 */
-                     gs = PNG_FP_1;   /* back */
-                     break;
-               }
-
-               if (png_gamma_significant(gs) != 0)
-               {
-                  back.red = png_gamma_8bit_correct(png_ptr->background.red,
-                      gs);
-                  back.green = png_gamma_8bit_correct(png_ptr->background.green,
-                      gs);
-                  back.blue = png_gamma_8bit_correct(png_ptr->background.blue,
-                      gs);
-               }
-
-               else
-               {
-                  back.red   = (png_byte)png_ptr->background.red;
-                  back.green = (png_byte)png_ptr->background.green;
-                  back.blue  = (png_byte)png_ptr->background.blue;
-               }
-
-               if (png_gamma_significant(g) != 0)
-               {
-                  back_1.red = png_gamma_8bit_correct(png_ptr->background.red,
-                      g);
-                  back_1.green = png_gamma_8bit_correct(
-                      png_ptr->background.green, g);
-                  back_1.blue = png_gamma_8bit_correct(png_ptr->background.blue,
-                      g);
-               }
-
-               else
-               {
-                  back_1.red   = (png_byte)png_ptr->background.red;
-                  back_1.green = (png_byte)png_ptr->background.green;
-                  back_1.blue  = (png_byte)png_ptr->background.blue;
-               }
-            }
-
-            for (i = 0; i < num_palette; i++)
-            {
-               if (i < (int)png_ptr->num_trans &&
-                   png_ptr->trans_alpha[i] != 0xff)
-               {
-                  if (png_ptr->trans_alpha[i] == 0)
-                  {
-                     palette[i] = back;
-                  }
-                  else /* if (png_ptr->trans_alpha[i] != 0xff) */
-                  {
-                     png_byte v, w;
-
-                     v = png_ptr->gamma_to_1[palette[i].red];
-                     png_composite(w, v, png_ptr->trans_alpha[i], back_1.red);
-                     palette[i].red = png_ptr->gamma_from_1[w];
-
-                     v = png_ptr->gamma_to_1[palette[i].green];
-                     png_composite(w, v, png_ptr->trans_alpha[i], back_1.green);
-                     palette[i].green = png_ptr->gamma_from_1[w];
-
-                     v = png_ptr->gamma_to_1[palette[i].blue];
-                     png_composite(w, v, png_ptr->trans_alpha[i], back_1.blue);
-                     palette[i].blue = png_ptr->gamma_from_1[w];
-                  }
-               }
-               else
-               {
-                  palette[i].red = png_ptr->gamma_table[palette[i].red];
-                  palette[i].green = png_ptr->gamma_table[palette[i].green];
-                  palette[i].blue = png_ptr->gamma_table[palette[i].blue];
-               }
-            }
-
-            /* Prevent the transformations being done again.
-             *
-             * NOTE: this is highly dubious; it removes the transformations in
-             * place.  This seems inconsistent with the general treatment of the
-             * transformations elsewhere.
-             */
-            png_ptr->transformations &= ~(PNG_COMPOSE | PNG_GAMMA);
-         } /* color_type == PNG_COLOR_TYPE_PALETTE */
-
-         /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */
-         else /* color_type != PNG_COLOR_TYPE_PALETTE */
-         {
-            int gs_sig, g_sig;
-            png_fixed_point g = PNG_FP_1;  /* Correction to linear */
-            png_fixed_point gs = PNG_FP_1; /* Correction to screen */
-
-            switch (png_ptr->background_gamma_type)
-            {
-               case PNG_BACKGROUND_GAMMA_SCREEN:
-                  g = png_ptr->screen_gamma;
-                  /* gs = PNG_FP_1; */
-                  break;
-
-               case PNG_BACKGROUND_GAMMA_FILE:
-                  g = png_reciprocal(png_ptr->colorspace.gamma);
-                  gs = png_reciprocal2(png_ptr->colorspace.gamma,
-                      png_ptr->screen_gamma);
-                  break;
-
-               case PNG_BACKGROUND_GAMMA_UNIQUE:
-                  g = png_reciprocal(png_ptr->background_gamma);
-                  gs = png_reciprocal2(png_ptr->background_gamma,
-                      png_ptr->screen_gamma);
-                  break;
-
-               default:
-                  png_error(png_ptr, "invalid background gamma type");
-            }
-
-            g_sig = png_gamma_significant(g);
-            gs_sig = png_gamma_significant(gs);
-
-            if (g_sig != 0)
-               png_ptr->background_1.gray = png_gamma_correct(png_ptr,
-                   png_ptr->background.gray, g);
-
-            if (gs_sig != 0)
-               png_ptr->background.gray = png_gamma_correct(png_ptr,
-                   png_ptr->background.gray, gs);
-
-            if ((png_ptr->background.red != png_ptr->background.green) ||
-                (png_ptr->background.red != png_ptr->background.blue) ||
-                (png_ptr->background.red != png_ptr->background.gray))
-            {
-               /* RGB or RGBA with color background */
-               if (g_sig != 0)
-               {
-                  png_ptr->background_1.red = png_gamma_correct(png_ptr,
-                      png_ptr->background.red, g);
-
-                  png_ptr->background_1.green = png_gamma_correct(png_ptr,
-                      png_ptr->background.green, g);
-
-                  png_ptr->background_1.blue = png_gamma_correct(png_ptr,
-                      png_ptr->background.blue, g);
-               }
-
-               if (gs_sig != 0)
-               {
-                  png_ptr->background.red = png_gamma_correct(png_ptr,
-                      png_ptr->background.red, gs);
-
-                  png_ptr->background.green = png_gamma_correct(png_ptr,
-                      png_ptr->background.green, gs);
-
-                  png_ptr->background.blue = png_gamma_correct(png_ptr,
-                      png_ptr->background.blue, gs);
-               }
-            }
-
-            else
-            {
-               /* GRAY, GRAY ALPHA, RGB, or RGBA with gray background */
-               png_ptr->background_1.red = png_ptr->background_1.green
-                   = png_ptr->background_1.blue = png_ptr->background_1.gray;
-
-               png_ptr->background.red = png_ptr->background.green
-                   = png_ptr->background.blue = png_ptr->background.gray;
-            }
-
-            /* The background is now in screen gamma: */
-            png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_SCREEN;
-         } /* color_type != PNG_COLOR_TYPE_PALETTE */
-      }/* png_ptr->transformations & PNG_BACKGROUND */
-
-      else
-      /* Transformation does not include PNG_BACKGROUND */
-#endif /* READ_BACKGROUND */
-      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-         /* RGB_TO_GRAY needs to have non-gamma-corrected values! */
-         && ((png_ptr->transformations & PNG_EXPAND) == 0 ||
-         (png_ptr->transformations & PNG_RGB_TO_GRAY) == 0)
-#endif
-         )
-      {
-         png_colorp palette = png_ptr->palette;
-         int num_palette = png_ptr->num_palette;
-         int i;
-
-         /* NOTE: there are other transformations that should probably be in
-          * here too.
-          */
-         for (i = 0; i < num_palette; i++)
-         {
-            palette[i].red = png_ptr->gamma_table[palette[i].red];
-            palette[i].green = png_ptr->gamma_table[palette[i].green];
-            palette[i].blue = png_ptr->gamma_table[palette[i].blue];
-         }
-
-         /* Done the gamma correction. */
-         png_ptr->transformations &= ~PNG_GAMMA;
-      } /* color_type == PALETTE && !PNG_BACKGROUND transformation */
-   }
-#ifdef PNG_READ_BACKGROUND_SUPPORTED
-   else
-#endif
-#endif /* READ_GAMMA */
-
-#ifdef PNG_READ_BACKGROUND_SUPPORTED
-   /* No GAMMA transformation (see the hanging else 4 lines above) */
-   if ((png_ptr->transformations & PNG_COMPOSE) != 0 &&
-       (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE))
-   {
-      int i;
-      int istop = (int)png_ptr->num_trans;
-      png_color back;
-      png_colorp palette = png_ptr->palette;
-
-      back.red   = (png_byte)png_ptr->background.red;
-      back.green = (png_byte)png_ptr->background.green;
-      back.blue  = (png_byte)png_ptr->background.blue;
-
-      for (i = 0; i < istop; i++)
-      {
-         if (png_ptr->trans_alpha[i] == 0)
-         {
-            palette[i] = back;
-         }
-
-         else if (png_ptr->trans_alpha[i] != 0xff)
-         {
-            /* The png_composite() macro is defined in png.h */
-            png_composite(palette[i].red, palette[i].red,
-                png_ptr->trans_alpha[i], back.red);
-
-            png_composite(palette[i].green, palette[i].green,
-                png_ptr->trans_alpha[i], back.green);
-
-            png_composite(palette[i].blue, palette[i].blue,
-                png_ptr->trans_alpha[i], back.blue);
-         }
-      }
-
-      png_ptr->transformations &= ~PNG_COMPOSE;
-   }
-#endif /* READ_BACKGROUND */
-
-#ifdef PNG_READ_SHIFT_SUPPORTED
-   if ((png_ptr->transformations & PNG_SHIFT) != 0 &&
-       (png_ptr->transformations & PNG_EXPAND) == 0 &&
-       (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE))
-   {
-      int i;
-      int istop = png_ptr->num_palette;
-      int shift = 8 - png_ptr->sig_bit.red;
-
-      png_ptr->transformations &= ~PNG_SHIFT;
-
-      /* significant bits can be in the range 1 to 7 for a meaningful result, if
-       * the number of significant bits is 0 then no shift is done (this is an
-       * error condition which is silently ignored.)
-       */
-      if (shift > 0 && shift < 8)
-         for (i=0; i<istop; ++i)
-         {
-            int component = png_ptr->palette[i].red;
-
-            component >>= shift;
-            png_ptr->palette[i].red = (png_byte)component;
-         }
-
-      shift = 8 - png_ptr->sig_bit.green;
-      if (shift > 0 && shift < 8)
-         for (i=0; i<istop; ++i)
-         {
-            int component = png_ptr->palette[i].green;
-
-            component >>= shift;
-            png_ptr->palette[i].green = (png_byte)component;
-         }
-
-      shift = 8 - png_ptr->sig_bit.blue;
-      if (shift > 0 && shift < 8)
-         for (i=0; i<istop; ++i)
-         {
-            int component = png_ptr->palette[i].blue;
-
-            component >>= shift;
-            png_ptr->palette[i].blue = (png_byte)component;
-         }
-   }
-#endif /* READ_SHIFT */
-}
-
-/* Modify the info structure to reflect the transformations.  The
- * info should be updated so a PNG file could be written with it,
- * assuming the transformations result in valid PNG data.
- */
-void /* PRIVATE */
-png_read_transform_info(png_structrp png_ptr, png_inforp info_ptr)
-{
-   png_debug(1, "in png_read_transform_info");
-
-#ifdef PNG_READ_EXPAND_SUPPORTED
-   if ((png_ptr->transformations & PNG_EXPAND) != 0)
-   {
-      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-      {
-         /* This check must match what actually happens in
-          * png_do_expand_palette; if it ever checks the tRNS chunk to see if
-          * it is all opaque we must do the same (at present it does not.)
-          */
-         if (png_ptr->num_trans > 0)
-            info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
-
-         else
-            info_ptr->color_type = PNG_COLOR_TYPE_RGB;
-
-         info_ptr->bit_depth = 8;
-         info_ptr->num_trans = 0;
-
-         if (png_ptr->palette == NULL)
-            png_error (png_ptr, "Palette is NULL in indexed image");
-      }
-      else
-      {
-         if (png_ptr->num_trans != 0)
-         {
-            if ((png_ptr->transformations & PNG_EXPAND_tRNS) != 0)
-               info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
-         }
-         if (info_ptr->bit_depth < 8)
-            info_ptr->bit_depth = 8;
-
-         info_ptr->num_trans = 0;
-      }
-   }
-#endif
-
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
-   defined(PNG_READ_ALPHA_MODE_SUPPORTED)
-   /* The following is almost certainly wrong unless the background value is in
-    * the screen space!
-    */
-   if ((png_ptr->transformations & PNG_COMPOSE) != 0)
-      info_ptr->background = png_ptr->background;
-#endif
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
-   /* The following used to be conditional on PNG_GAMMA (prior to 1.5.4),
-    * however it seems that the code in png_init_read_transformations, which has
-    * been called before this from png_read_update_info->png_read_start_row
-    * sometimes does the gamma transform and cancels the flag.
-    *
-    * TODO: this looks wrong; the info_ptr should end up with a gamma equal to
-    * the screen_gamma value.  The following probably results in weirdness if
-    * the info_ptr is used by the app after the rows have been read.
-    */
-   info_ptr->colorspace.gamma = png_ptr->colorspace.gamma;
-#endif
-
-   if (info_ptr->bit_depth == 16)
-   {
-#  ifdef PNG_READ_16BIT_SUPPORTED
-#     ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
-         if ((png_ptr->transformations & PNG_SCALE_16_TO_8) != 0)
-            info_ptr->bit_depth = 8;
-#     endif
-
-#     ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
-         if ((png_ptr->transformations & PNG_16_TO_8) != 0)
-            info_ptr->bit_depth = 8;
-#     endif
-
-#  else
-      /* No 16-bit support: force chopping 16-bit input down to 8, in this case
-       * the app program can chose if both APIs are available by setting the
-       * correct scaling to use.
-       */
-#     ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
-         /* For compatibility with previous versions use the strip method by
-          * default.  This code works because if PNG_SCALE_16_TO_8 is already
-          * set the code below will do that in preference to the chop.
-          */
-         png_ptr->transformations |= PNG_16_TO_8;
-         info_ptr->bit_depth = 8;
-#     else
-
-#        ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
-            png_ptr->transformations |= PNG_SCALE_16_TO_8;
-            info_ptr->bit_depth = 8;
-#        else
-
-            CONFIGURATION ERROR: you must enable at least one 16 to 8 method
-#        endif
-#    endif
-#endif /* !READ_16BIT */
-   }
-
-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
-   if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0)
-      info_ptr->color_type = (png_byte)(info_ptr->color_type |
-         PNG_COLOR_MASK_COLOR);
-#endif
-
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-   if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0)
-      info_ptr->color_type = (png_byte)(info_ptr->color_type &
-         ~PNG_COLOR_MASK_COLOR);
-#endif
-
-#ifdef PNG_READ_QUANTIZE_SUPPORTED
-   if ((png_ptr->transformations & PNG_QUANTIZE) != 0)
-   {
-      if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) ||
-          (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) &&
-          png_ptr->palette_lookup != 0 && info_ptr->bit_depth == 8)
-      {
-         info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
-      }
-   }
-#endif
-
-#ifdef PNG_READ_EXPAND_16_SUPPORTED
-   if ((png_ptr->transformations & PNG_EXPAND_16) != 0 &&
-       info_ptr->bit_depth == 8 &&
-       info_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
-   {
-      info_ptr->bit_depth = 16;
-   }
-#endif
-
-#ifdef PNG_READ_PACK_SUPPORTED
-   if ((png_ptr->transformations & PNG_PACK) != 0 &&
-       (info_ptr->bit_depth < 8))
-      info_ptr->bit_depth = 8;
-#endif
-
-   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-      info_ptr->channels = 1;
-
-   else if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
-      info_ptr->channels = 3;
-
-   else
-      info_ptr->channels = 1;
-
-#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
-   if ((png_ptr->transformations & PNG_STRIP_ALPHA) != 0)
-   {
-      info_ptr->color_type = (png_byte)(info_ptr->color_type &
-         ~PNG_COLOR_MASK_ALPHA);
-      info_ptr->num_trans = 0;
-   }
-#endif
-
-   if ((info_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0)
-      info_ptr->channels++;
-
-#ifdef PNG_READ_FILLER_SUPPORTED
-   /* STRIP_ALPHA and FILLER allowed:  MASK_ALPHA bit stripped above */
-   if ((png_ptr->transformations & PNG_FILLER) != 0 &&
-       (info_ptr->color_type == PNG_COLOR_TYPE_RGB ||
-       info_ptr->color_type == PNG_COLOR_TYPE_GRAY))
-   {
-      info_ptr->channels++;
-      /* If adding a true alpha channel not just filler */
-      if ((png_ptr->transformations & PNG_ADD_ALPHA) != 0)
-         info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
-   }
-#endif
-
-#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \
-defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
-   if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0)
-   {
-      if (png_ptr->user_transform_depth != 0)
-         info_ptr->bit_depth = png_ptr->user_transform_depth;
-
-      if (png_ptr->user_transform_channels != 0)
-         info_ptr->channels = png_ptr->user_transform_channels;
-   }
-#endif
-
-   info_ptr->pixel_depth = (png_byte)(info_ptr->channels *
-       info_ptr->bit_depth);
-
-   info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, info_ptr->width);
-
-   /* Adding in 1.5.4: cache the above value in png_struct so that we can later
-    * check in png_rowbytes that the user buffer won't get overwritten.  Note
-    * that the field is not always set - if png_read_update_info isn't called
-    * the application has to either not do any transforms or get the calculation
-    * right itself.
-    */
-   png_ptr->info_rowbytes = info_ptr->rowbytes;
-
-#ifndef PNG_READ_EXPAND_SUPPORTED
-   if (png_ptr != NULL)
-      return;
-#endif
-}
-
-#ifdef PNG_READ_PACK_SUPPORTED
-/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel,
- * without changing the actual values.  Thus, if you had a row with
- * a bit depth of 1, you would end up with bytes that only contained
- * the numbers 0 or 1.  If you would rather they contain 0 and 255, use
- * png_do_shift() after this.
- */
-static void
-png_do_unpack(png_row_infop row_info, png_bytep row)
-{
-   png_debug(1, "in png_do_unpack");
-
-   if (row_info->bit_depth < 8)
-   {
-      png_uint_32 i;
-      png_uint_32 row_width=row_info->width;
-
-      switch (row_info->bit_depth)
-      {
-         case 1:
-         {
-            png_bytep sp = row + (size_t)((row_width - 1) >> 3);
-            png_bytep dp = row + (size_t)row_width - 1;
-            png_uint_32 shift = 7U - ((row_width + 7U) & 0x07);
-            for (i = 0; i < row_width; i++)
-            {
-               *dp = (png_byte)((*sp >> shift) & 0x01);
-
-               if (shift == 7)
-               {
-                  shift = 0;
-                  sp--;
-               }
-
-               else
-                  shift++;
-
-               dp--;
-            }
-            break;
-         }
-
-         case 2:
-         {
-
-            png_bytep sp = row + (size_t)((row_width - 1) >> 2);
-            png_bytep dp = row + (size_t)row_width - 1;
-            png_uint_32 shift = ((3U - ((row_width + 3U) & 0x03)) << 1);
-            for (i = 0; i < row_width; i++)
-            {
-               *dp = (png_byte)((*sp >> shift) & 0x03);
-
-               if (shift == 6)
-               {
-                  shift = 0;
-                  sp--;
-               }
-
-               else
-                  shift += 2;
-
-               dp--;
-            }
-            break;
-         }
-
-         case 4:
-         {
-            png_bytep sp = row + (size_t)((row_width - 1) >> 1);
-            png_bytep dp = row + (size_t)row_width - 1;
-            png_uint_32 shift = ((1U - ((row_width + 1U) & 0x01)) << 2);
-            for (i = 0; i < row_width; i++)
-            {
-               *dp = (png_byte)((*sp >> shift) & 0x0f);
-
-               if (shift == 4)
-               {
-                  shift = 0;
-                  sp--;
-               }
-
-               else
-                  shift = 4;
-
-               dp--;
-            }
-            break;
-         }
-
-         default:
-            break;
-      }
-      row_info->bit_depth = 8;
-      row_info->pixel_depth = (png_byte)(8 * row_info->channels);
-      row_info->rowbytes = row_width * row_info->channels;
-   }
-}
-#endif
-
-#ifdef PNG_READ_SHIFT_SUPPORTED
-/* Reverse the effects of png_do_shift.  This routine merely shifts the
- * pixels back to their significant bits values.  Thus, if you have
- * a row of bit depth 8, but only 5 are significant, this will shift
- * the values back to 0 through 31.
- */
-static void
-png_do_unshift(png_row_infop row_info, png_bytep row,
-    png_const_color_8p sig_bits)
-{
-   int color_type;
-
-   png_debug(1, "in png_do_unshift");
-
-   /* The palette case has already been handled in the _init routine. */
-   color_type = row_info->color_type;
-
-   if (color_type != PNG_COLOR_TYPE_PALETTE)
-   {
-      int shift[4];
-      int channels = 0;
-      int bit_depth = row_info->bit_depth;
-
-      if ((color_type & PNG_COLOR_MASK_COLOR) != 0)
-      {
-         shift[channels++] = bit_depth - sig_bits->red;
-         shift[channels++] = bit_depth - sig_bits->green;
-         shift[channels++] = bit_depth - sig_bits->blue;
-      }
-
-      else
-      {
-         shift[channels++] = bit_depth - sig_bits->gray;
-      }
-
-      if ((color_type & PNG_COLOR_MASK_ALPHA) != 0)
-      {
-         shift[channels++] = bit_depth - sig_bits->alpha;
-      }
-
-      {
-         int c, have_shift;
-
-         for (c = have_shift = 0; c < channels; ++c)
-         {
-            /* A shift of more than the bit depth is an error condition but it
-             * gets ignored here.
-             */
-            if (shift[c] <= 0 || shift[c] >= bit_depth)
-               shift[c] = 0;
-
-            else
-               have_shift = 1;
-         }
-
-         if (have_shift == 0)
-            return;
-      }
-
-      switch (bit_depth)
-      {
-         default:
-         /* Must be 1bpp gray: should not be here! */
-            /* NOTREACHED */
-            break;
-
-         case 2:
-         /* Must be 2bpp gray */
-         /* assert(channels == 1 && shift[0] == 1) */
-         {
-            png_bytep bp = row;
-            png_bytep bp_end = bp + row_info->rowbytes;
-
-            while (bp < bp_end)
-            {
-               int b = (*bp >> 1) & 0x55;
-               *bp++ = (png_byte)b;
-            }
-            break;
-         }
-
-         case 4:
-         /* Must be 4bpp gray */
-         /* assert(channels == 1) */
-         {
-            png_bytep bp = row;
-            png_bytep bp_end = bp + row_info->rowbytes;
-            int gray_shift = shift[0];
-            int mask =  0xf >> gray_shift;
-
-            mask |= mask << 4;
-
-            while (bp < bp_end)
-            {
-               int b = (*bp >> gray_shift) & mask;
-               *bp++ = (png_byte)b;
-            }
-            break;
-         }
-
-         case 8:
-         /* Single byte components, G, GA, RGB, RGBA */
-         {
-            png_bytep bp = row;
-            png_bytep bp_end = bp + row_info->rowbytes;
-            int channel = 0;
-
-            while (bp < bp_end)
-            {
-               int b = *bp >> shift[channel];
-               if (++channel >= channels)
-                  channel = 0;
-               *bp++ = (png_byte)b;
-            }
-            break;
-         }
-
-#ifdef PNG_READ_16BIT_SUPPORTED
-         case 16:
-         /* Double byte components, G, GA, RGB, RGBA */
-         {
-            png_bytep bp = row;
-            png_bytep bp_end = bp + row_info->rowbytes;
-            int channel = 0;
-
-            while (bp < bp_end)
-            {
-               int value = (bp[0] << 8) + bp[1];
-
-               value >>= shift[channel];
-               if (++channel >= channels)
-                  channel = 0;
-               *bp++ = (png_byte)(value >> 8);
-               *bp++ = (png_byte)value;
-            }
-            break;
-         }
-#endif
-      }
-   }
-}
-#endif
-
-#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
-/* Scale rows of bit depth 16 down to 8 accurately */
-static void
-png_do_scale_16_to_8(png_row_infop row_info, png_bytep row)
-{
-   png_debug(1, "in png_do_scale_16_to_8");
-
-   if (row_info->bit_depth == 16)
-   {
-      png_bytep sp = row; /* source */
-      png_bytep dp = row; /* destination */
-      png_bytep ep = sp + row_info->rowbytes; /* end+1 */
-
-      while (sp < ep)
-      {
-         /* The input is an array of 16-bit components, these must be scaled to
-          * 8 bits each.  For a 16-bit value V the required value (from the PNG
-          * specification) is:
-          *
-          *    (V * 255) / 65535
-          *
-          * This reduces to round(V / 257), or floor((V + 128.5)/257)
-          *
-          * Represent V as the two byte value vhi.vlo.  Make a guess that the
-          * result is the top byte of V, vhi, then the correction to this value
-          * is:
-          *
-          *    error = floor(((V-vhi.vhi) + 128.5) / 257)
-          *          = floor(((vlo-vhi) + 128.5) / 257)
-          *
-          * This can be approximated using integer arithmetic (and a signed
-          * shift):
-          *
-          *    error = (vlo-vhi+128) >> 8;
-          *
-          * The approximate differs from the exact answer only when (vlo-vhi) is
-          * 128; it then gives a correction of +1 when the exact correction is
-          * 0.  This gives 128 errors.  The exact answer (correct for all 16-bit
-          * input values) is:
-          *
-          *    error = (vlo-vhi+128)*65535 >> 24;
-          *
-          * An alternative arithmetic calculation which also gives no errors is:
-          *
-          *    (V * 255 + 32895) >> 16
-          */
-
-         png_int_32 tmp = *sp++; /* must be signed! */
-         tmp += (((int)*sp++ - tmp + 128) * 65535) >> 24;
-         *dp++ = (png_byte)tmp;
-      }
-
-      row_info->bit_depth = 8;
-      row_info->pixel_depth = (png_byte)(8 * row_info->channels);
-      row_info->rowbytes = row_info->width * row_info->channels;
-   }
-}
-#endif
-
-#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
-static void
-/* Simply discard the low byte.  This was the default behavior prior
- * to libpng-1.5.4.
- */
-png_do_chop(png_row_infop row_info, png_bytep row)
-{
-   png_debug(1, "in png_do_chop");
-
-   if (row_info->bit_depth == 16)
-   {
-      png_bytep sp = row; /* source */
-      png_bytep dp = row; /* destination */
-      png_bytep ep = sp + row_info->rowbytes; /* end+1 */
-
-      while (sp < ep)
-      {
-         *dp++ = *sp;
-         sp += 2; /* skip low byte */
-      }
-
-      row_info->bit_depth = 8;
-      row_info->pixel_depth = (png_byte)(8 * row_info->channels);
-      row_info->rowbytes = row_info->width * row_info->channels;
-   }
-}
-#endif
-
-#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
-static void
-png_do_read_swap_alpha(png_row_infop row_info, png_bytep row)
-{
-   png_uint_32 row_width = row_info->width;
-
-   png_debug(1, "in png_do_read_swap_alpha");
-
-   if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
-   {
-      /* This converts from RGBA to ARGB */
-      if (row_info->bit_depth == 8)
-      {
-         png_bytep sp = row + row_info->rowbytes;
-         png_bytep dp = sp;
-         png_byte save;
-         png_uint_32 i;
-
-         for (i = 0; i < row_width; i++)
-         {
-            save = *(--sp);
-            *(--dp) = *(--sp);
-            *(--dp) = *(--sp);
-            *(--dp) = *(--sp);
-            *(--dp) = save;
-         }
-      }
-
-#ifdef PNG_READ_16BIT_SUPPORTED
-      /* This converts from RRGGBBAA to AARRGGBB */
-      else
-      {
-         png_bytep sp = row + row_info->rowbytes;
-         png_bytep dp = sp;
-         png_byte save[2];
-         png_uint_32 i;
-
-         for (i = 0; i < row_width; i++)
-         {
-            save[0] = *(--sp);
-            save[1] = *(--sp);
-            *(--dp) = *(--sp);
-            *(--dp) = *(--sp);
-            *(--dp) = *(--sp);
-            *(--dp) = *(--sp);
-            *(--dp) = *(--sp);
-            *(--dp) = *(--sp);
-            *(--dp) = save[0];
-            *(--dp) = save[1];
-         }
-      }
-#endif
-   }
-
-   else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
-   {
-      /* This converts from GA to AG */
-      if (row_info->bit_depth == 8)
-      {
-         png_bytep sp = row + row_info->rowbytes;
-         png_bytep dp = sp;
-         png_byte save;
-         png_uint_32 i;
-
-         for (i = 0; i < row_width; i++)
-         {
-            save = *(--sp);
-            *(--dp) = *(--sp);
-            *(--dp) = save;
-         }
-      }
-
-#ifdef PNG_READ_16BIT_SUPPORTED
-      /* This converts from GGAA to AAGG */
-      else
-      {
-         png_bytep sp = row + row_info->rowbytes;
-         png_bytep dp = sp;
-         png_byte save[2];
-         png_uint_32 i;
-
-         for (i = 0; i < row_width; i++)
-         {
-            save[0] = *(--sp);
-            save[1] = *(--sp);
-            *(--dp) = *(--sp);
-            *(--dp) = *(--sp);
-            *(--dp) = save[0];
-            *(--dp) = save[1];
-         }
-      }
-#endif
-   }
-}
-#endif
-
-#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
-static void
-png_do_read_invert_alpha(png_row_infop row_info, png_bytep row)
-{
-   png_uint_32 row_width;
-   png_debug(1, "in png_do_read_invert_alpha");
-
-   row_width = row_info->width;
-   if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
-   {
-      if (row_info->bit_depth == 8)
-      {
-         /* This inverts the alpha channel in RGBA */
-         png_bytep sp = row + row_info->rowbytes;
-         png_bytep dp = sp;
-         png_uint_32 i;
-
-         for (i = 0; i < row_width; i++)
-         {
-            *(--dp) = (png_byte)(255 - *(--sp));
-
-/*          This does nothing:
-            *(--dp) = *(--sp);
-            *(--dp) = *(--sp);
-            *(--dp) = *(--sp);
-            We can replace it with:
-*/
-            sp-=3;
-            dp=sp;
-         }
-      }
-
-#ifdef PNG_READ_16BIT_SUPPORTED
-      /* This inverts the alpha channel in RRGGBBAA */
-      else
-      {
-         png_bytep sp = row + row_info->rowbytes;
-         png_bytep dp = sp;
-         png_uint_32 i;
-
-         for (i = 0; i < row_width; i++)
-         {
-            *(--dp) = (png_byte)(255 - *(--sp));
-            *(--dp) = (png_byte)(255 - *(--sp));
-
-/*          This does nothing:
-            *(--dp) = *(--sp);
-            *(--dp) = *(--sp);
-            *(--dp) = *(--sp);
-            *(--dp) = *(--sp);
-            *(--dp) = *(--sp);
-            *(--dp) = *(--sp);
-            We can replace it with:
-*/
-            sp-=6;
-            dp=sp;
-         }
-      }
-#endif
-   }
-   else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
-   {
-      if (row_info->bit_depth == 8)
-      {
-         /* This inverts the alpha channel in GA */
-         png_bytep sp = row + row_info->rowbytes;
-         png_bytep dp = sp;
-         png_uint_32 i;
-
-         for (i = 0; i < row_width; i++)
-         {
-            *(--dp) = (png_byte)(255 - *(--sp));
-            *(--dp) = *(--sp);
-         }
-      }
-
-#ifdef PNG_READ_16BIT_SUPPORTED
-      else
-      {
-         /* This inverts the alpha channel in GGAA */
-         png_bytep sp  = row + row_info->rowbytes;
-         png_bytep dp = sp;
-         png_uint_32 i;
-
-         for (i = 0; i < row_width; i++)
-         {
-            *(--dp) = (png_byte)(255 - *(--sp));
-            *(--dp) = (png_byte)(255 - *(--sp));
-/*
-            *(--dp) = *(--sp);
-            *(--dp) = *(--sp);
-*/
-            sp-=2;
-            dp=sp;
-         }
-      }
-#endif
-   }
-}
-#endif
-
-#ifdef PNG_READ_FILLER_SUPPORTED
-/* Add filler channel if we have RGB color */
-static void
-png_do_read_filler(png_row_infop row_info, png_bytep row,
-    png_uint_32 filler, png_uint_32 flags)
-{
-   png_uint_32 i;
-   png_uint_32 row_width = row_info->width;
-
-#ifdef PNG_READ_16BIT_SUPPORTED
-   png_byte hi_filler = (png_byte)(filler>>8);
-#endif
-   png_byte lo_filler = (png_byte)filler;
-
-   png_debug(1, "in png_do_read_filler");
-
-   if (
-       row_info->color_type == PNG_COLOR_TYPE_GRAY)
-   {
-      if (row_info->bit_depth == 8)
-      {
-         if ((flags & PNG_FLAG_FILLER_AFTER) != 0)
-         {
-            /* This changes the data from G to GX */
-            png_bytep sp = row + (size_t)row_width;
-            png_bytep dp =  sp + (size_t)row_width;
-            for (i = 1; i < row_width; i++)
-            {
-               *(--dp) = lo_filler;
-               *(--dp) = *(--sp);
-            }
-            *(--dp) = lo_filler;
-            row_info->channels = 2;
-            row_info->pixel_depth = 16;
-            row_info->rowbytes = row_width * 2;
-         }
-
-         else
-         {
-            /* This changes the data from G to XG */
-            png_bytep sp = row + (size_t)row_width;
-            png_bytep dp = sp  + (size_t)row_width;
-            for (i = 0; i < row_width; i++)
-            {
-               *(--dp) = *(--sp);
-               *(--dp) = lo_filler;
-            }
-            row_info->channels = 2;
-            row_info->pixel_depth = 16;
-            row_info->rowbytes = row_width * 2;
-         }
-      }
-
-#ifdef PNG_READ_16BIT_SUPPORTED
-      else if (row_info->bit_depth == 16)
-      {
-         if ((flags & PNG_FLAG_FILLER_AFTER) != 0)
-         {
-            /* This changes the data from GG to GGXX */
-            png_bytep sp = row + (size_t)row_width * 2;
-            png_bytep dp = sp  + (size_t)row_width * 2;
-            for (i = 1; i < row_width; i++)
-            {
-               *(--dp) = lo_filler;
-               *(--dp) = hi_filler;
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-            }
-            *(--dp) = lo_filler;
-            *(--dp) = hi_filler;
-            row_info->channels = 2;
-            row_info->pixel_depth = 32;
-            row_info->rowbytes = row_width * 4;
-         }
-
-         else
-         {
-            /* This changes the data from GG to XXGG */
-            png_bytep sp = row + (size_t)row_width * 2;
-            png_bytep dp = sp  + (size_t)row_width * 2;
-            for (i = 0; i < row_width; i++)
-            {
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = lo_filler;
-               *(--dp) = hi_filler;
-            }
-            row_info->channels = 2;
-            row_info->pixel_depth = 32;
-            row_info->rowbytes = row_width * 4;
-         }
-      }
-#endif
-   } /* COLOR_TYPE == GRAY */
-   else if (row_info->color_type == PNG_COLOR_TYPE_RGB)
-   {
-      if (row_info->bit_depth == 8)
-      {
-         if ((flags & PNG_FLAG_FILLER_AFTER) != 0)
-         {
-            /* This changes the data from RGB to RGBX */
-            png_bytep sp = row + (size_t)row_width * 3;
-            png_bytep dp = sp  + (size_t)row_width;
-            for (i = 1; i < row_width; i++)
-            {
-               *(--dp) = lo_filler;
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-            }
-            *(--dp) = lo_filler;
-            row_info->channels = 4;
-            row_info->pixel_depth = 32;
-            row_info->rowbytes = row_width * 4;
-         }
-
-         else
-         {
-            /* This changes the data from RGB to XRGB */
-            png_bytep sp = row + (size_t)row_width * 3;
-            png_bytep dp = sp + (size_t)row_width;
-            for (i = 0; i < row_width; i++)
-            {
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = lo_filler;
-            }
-            row_info->channels = 4;
-            row_info->pixel_depth = 32;
-            row_info->rowbytes = row_width * 4;
-         }
-      }
-
-#ifdef PNG_READ_16BIT_SUPPORTED
-      else if (row_info->bit_depth == 16)
-      {
-         if ((flags & PNG_FLAG_FILLER_AFTER) != 0)
-         {
-            /* This changes the data from RRGGBB to RRGGBBXX */
-            png_bytep sp = row + (size_t)row_width * 6;
-            png_bytep dp = sp  + (size_t)row_width * 2;
-            for (i = 1; i < row_width; i++)
-            {
-               *(--dp) = lo_filler;
-               *(--dp) = hi_filler;
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-            }
-            *(--dp) = lo_filler;
-            *(--dp) = hi_filler;
-            row_info->channels = 4;
-            row_info->pixel_depth = 64;
-            row_info->rowbytes = row_width * 8;
-         }
-
-         else
-         {
-            /* This changes the data from RRGGBB to XXRRGGBB */
-            png_bytep sp = row + (size_t)row_width * 6;
-            png_bytep dp = sp  + (size_t)row_width * 2;
-            for (i = 0; i < row_width; i++)
-            {
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = lo_filler;
-               *(--dp) = hi_filler;
-            }
-
-            row_info->channels = 4;
-            row_info->pixel_depth = 64;
-            row_info->rowbytes = row_width * 8;
-         }
-      }
-#endif
-   } /* COLOR_TYPE == RGB */
-}
-#endif
-
-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
-/* Expand grayscale files to RGB, with or without alpha */
-static void
-png_do_gray_to_rgb(png_row_infop row_info, png_bytep row)
-{
-   png_uint_32 i;
-   png_uint_32 row_width = row_info->width;
-
-   png_debug(1, "in png_do_gray_to_rgb");
-
-   if (row_info->bit_depth >= 8 &&
-       (row_info->color_type & PNG_COLOR_MASK_COLOR) == 0)
-   {
-      if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
-      {
-         if (row_info->bit_depth == 8)
-         {
-            /* This changes G to RGB */
-            png_bytep sp = row + (size_t)row_width - 1;
-            png_bytep dp = sp  + (size_t)row_width * 2;
-            for (i = 0; i < row_width; i++)
-            {
-               *(dp--) = *sp;
-               *(dp--) = *sp;
-               *(dp--) = *(sp--);
-            }
-         }
-
-         else
-         {
-            /* This changes GG to RRGGBB */
-            png_bytep sp = row + (size_t)row_width * 2 - 1;
-            png_bytep dp = sp  + (size_t)row_width * 4;
-            for (i = 0; i < row_width; i++)
-            {
-               *(dp--) = *sp;
-               *(dp--) = *(sp - 1);
-               *(dp--) = *sp;
-               *(dp--) = *(sp - 1);
-               *(dp--) = *(sp--);
-               *(dp--) = *(sp--);
-            }
-         }
-      }
-
-      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
-      {
-         if (row_info->bit_depth == 8)
-         {
-            /* This changes GA to RGBA */
-            png_bytep sp = row + (size_t)row_width * 2 - 1;
-            png_bytep dp = sp  + (size_t)row_width * 2;
-            for (i = 0; i < row_width; i++)
-            {
-               *(dp--) = *(sp--);
-               *(dp--) = *sp;
-               *(dp--) = *sp;
-               *(dp--) = *(sp--);
-            }
-         }
-
-         else
-         {
-            /* This changes GGAA to RRGGBBAA */
-            png_bytep sp = row + (size_t)row_width * 4 - 1;
-            png_bytep dp = sp  + (size_t)row_width * 4;
-            for (i = 0; i < row_width; i++)
-            {
-               *(dp--) = *(sp--);
-               *(dp--) = *(sp--);
-               *(dp--) = *sp;
-               *(dp--) = *(sp - 1);
-               *(dp--) = *sp;
-               *(dp--) = *(sp - 1);
-               *(dp--) = *(sp--);
-               *(dp--) = *(sp--);
-            }
-         }
-      }
-      row_info->channels = (png_byte)(row_info->channels + 2);
-      row_info->color_type |= PNG_COLOR_MASK_COLOR;
-      row_info->pixel_depth = (png_byte)(row_info->channels *
-          row_info->bit_depth);
-      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
-   }
-}
-#endif
-
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-/* Reduce RGB files to grayscale, with or without alpha
- * using the equation given in Poynton's ColorFAQ of 1998-01-04 at
- * <http://www.inforamp.net/~poynton/>  (THIS LINK IS DEAD June 2008 but
- * versions dated 1998 through November 2002 have been archived at
- * https://web.archive.org/web/20000816232553/www.inforamp.net/
- * ~poynton/notes/colour_and_gamma/ColorFAQ.txt )
- * Charles Poynton poynton at poynton.com
- *
- *     Y = 0.212671 * R + 0.715160 * G + 0.072169 * B
- *
- *  which can be expressed with integers as
- *
- *     Y = (6969 * R + 23434 * G + 2365 * B)/32768
- *
- * Poynton's current link (as of January 2003 through July 2011):
- * <http://www.poynton.com/notes/colour_and_gamma/>
- * has changed the numbers slightly:
- *
- *     Y = 0.2126*R + 0.7152*G + 0.0722*B
- *
- *  which can be expressed with integers as
- *
- *     Y = (6966 * R + 23436 * G + 2366 * B)/32768
- *
- *  Historically, however, libpng uses numbers derived from the ITU-R Rec 709
- *  end point chromaticities and the D65 white point.  Depending on the
- *  precision used for the D65 white point this produces a variety of different
- *  numbers, however if the four decimal place value used in ITU-R Rec 709 is
- *  used (0.3127,0.3290) the Y calculation would be:
- *
- *     Y = (6968 * R + 23435 * G + 2366 * B)/32768
- *
- *  While this is correct the rounding results in an overflow for white, because
- *  the sum of the rounded coefficients is 32769, not 32768.  Consequently
- *  libpng uses, instead, the closest non-overflowing approximation:
- *
- *     Y = (6968 * R + 23434 * G + 2366 * B)/32768
- *
- *  Starting with libpng-1.5.5, if the image being converted has a cHRM chunk
- *  (including an sRGB chunk) then the chromaticities are used to calculate the
- *  coefficients.  See the chunk handling in pngrutil.c for more information.
- *
- *  In all cases the calculation is to be done in a linear colorspace.  If no
- *  gamma information is available to correct the encoding of the original RGB
- *  values this results in an implicit assumption that the original PNG RGB
- *  values were linear.
- *
- *  Other integer coefficients can be used via png_set_rgb_to_gray().  Because
- *  the API takes just red and green coefficients the blue coefficient is
- *  calculated to make the sum 32768.  This will result in different rounding
- *  to that used above.
- */
-static int
-png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row)
-{
-   int rgb_error = 0;
-
-   png_debug(1, "in png_do_rgb_to_gray");
-
-   if ((row_info->color_type & PNG_COLOR_MASK_PALETTE) == 0 &&
-       (row_info->color_type & PNG_COLOR_MASK_COLOR) != 0)
-   {
-      png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff;
-      png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff;
-      png_uint_32 bc = 32768 - rc - gc;
-      png_uint_32 row_width = row_info->width;
-      int have_alpha = (row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0;
-
-      if (row_info->bit_depth == 8)
-      {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-         /* Notice that gamma to/from 1 are not necessarily inverses (if
-          * there is an overall gamma correction).  Prior to 1.5.5 this code
-          * checked the linearized values for equality; this doesn't match
-          * the documentation, the original values must be checked.
-          */
-         if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL)
-         {
-            png_bytep sp = row;
-            png_bytep dp = row;
-            png_uint_32 i;
-
-            for (i = 0; i < row_width; i++)
-            {
-               png_byte red   = *(sp++);
-               png_byte green = *(sp++);
-               png_byte blue  = *(sp++);
-
-               if (red != green || red != blue)
-               {
-                  red = png_ptr->gamma_to_1[red];
-                  green = png_ptr->gamma_to_1[green];
-                  blue = png_ptr->gamma_to_1[blue];
-
-                  rgb_error |= 1;
-                  *(dp++) = png_ptr->gamma_from_1[
-                      (rc*red + gc*green + bc*blue + 16384)>>15];
-               }
-
-               else
-               {
-                  /* If there is no overall correction the table will not be
-                   * set.
-                   */
-                  if (png_ptr->gamma_table != NULL)
-                     red = png_ptr->gamma_table[red];
-
-                  *(dp++) = red;
-               }
-
-               if (have_alpha != 0)
-                  *(dp++) = *(sp++);
-            }
-         }
-         else
-#endif
-         {
-            png_bytep sp = row;
-            png_bytep dp = row;
-            png_uint_32 i;
-
-            for (i = 0; i < row_width; i++)
-            {
-               png_byte red   = *(sp++);
-               png_byte green = *(sp++);
-               png_byte blue  = *(sp++);
-
-               if (red != green || red != blue)
-               {
-                  rgb_error |= 1;
-                  /* NOTE: this is the historical approach which simply
-                   * truncates the results.
-                   */
-                  *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15);
-               }
-
-               else
-                  *(dp++) = red;
-
-               if (have_alpha != 0)
-                  *(dp++) = *(sp++);
-            }
-         }
-      }
-
-      else /* RGB bit_depth == 16 */
-      {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-         if (png_ptr->gamma_16_to_1 != NULL && png_ptr->gamma_16_from_1 != NULL)
-         {
-            png_bytep sp = row;
-            png_bytep dp = row;
-            png_uint_32 i;
-
-            for (i = 0; i < row_width; i++)
-            {
-               png_uint_16 red, green, blue, w;
-               png_byte hi,lo;
-
-               hi=*(sp)++; lo=*(sp)++; red   = (png_uint_16)((hi << 8) | (lo));
-               hi=*(sp)++; lo=*(sp)++; green = (png_uint_16)((hi << 8) | (lo));
-               hi=*(sp)++; lo=*(sp)++; blue  = (png_uint_16)((hi << 8) | (lo));
-
-               if (red == green && red == blue)
-               {
-                  if (png_ptr->gamma_16_table != NULL)
-                     w = png_ptr->gamma_16_table[(red & 0xff)
-                         >> png_ptr->gamma_shift][red >> 8];
-
-                  else
-                     w = red;
-               }
-
-               else
-               {
-                  png_uint_16 red_1   = png_ptr->gamma_16_to_1[(red & 0xff)
-                      >> png_ptr->gamma_shift][red>>8];
-                  png_uint_16 green_1 =
-                      png_ptr->gamma_16_to_1[(green & 0xff) >>
-                      png_ptr->gamma_shift][green>>8];
-                  png_uint_16 blue_1  = png_ptr->gamma_16_to_1[(blue & 0xff)
-                      >> png_ptr->gamma_shift][blue>>8];
-                  png_uint_16 gray16  = (png_uint_16)((rc*red_1 + gc*green_1
-                      + bc*blue_1 + 16384)>>15);
-                  w = png_ptr->gamma_16_from_1[(gray16 & 0xff) >>
-                      png_ptr->gamma_shift][gray16 >> 8];
-                  rgb_error |= 1;
-               }
-
-               *(dp++) = (png_byte)((w>>8) & 0xff);
-               *(dp++) = (png_byte)(w & 0xff);
-
-               if (have_alpha != 0)
-               {
-                  *(dp++) = *(sp++);
-                  *(dp++) = *(sp++);
-               }
-            }
-         }
-         else
-#endif
-         {
-            png_bytep sp = row;
-            png_bytep dp = row;
-            png_uint_32 i;
-
-            for (i = 0; i < row_width; i++)
-            {
-               png_uint_16 red, green, blue, gray16;
-               png_byte hi,lo;
-
-               hi=*(sp)++; lo=*(sp)++; red   = (png_uint_16)((hi << 8) | (lo));
-               hi=*(sp)++; lo=*(sp)++; green = (png_uint_16)((hi << 8) | (lo));
-               hi=*(sp)++; lo=*(sp)++; blue  = (png_uint_16)((hi << 8) | (lo));
-
-               if (red != green || red != blue)
-                  rgb_error |= 1;
-
-               /* From 1.5.5 in the 16-bit case do the accurate conversion even
-                * in the 'fast' case - this is because this is where the code
-                * ends up when handling linear 16-bit data.
-                */
-               gray16  = (png_uint_16)((rc*red + gc*green + bc*blue + 16384) >>
-                  15);
-               *(dp++) = (png_byte)((gray16 >> 8) & 0xff);
-               *(dp++) = (png_byte)(gray16 & 0xff);
-
-               if (have_alpha != 0)
-               {
-                  *(dp++) = *(sp++);
-                  *(dp++) = *(sp++);
-               }
-            }
-         }
-      }
-
-      row_info->channels = (png_byte)(row_info->channels - 2);
-      row_info->color_type = (png_byte)(row_info->color_type &
-          ~PNG_COLOR_MASK_COLOR);
-      row_info->pixel_depth = (png_byte)(row_info->channels *
-          row_info->bit_depth);
-      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
-   }
-   return rgb_error;
-}
-#endif
-
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
-   defined(PNG_READ_ALPHA_MODE_SUPPORTED)
-/* Replace any alpha or transparency with the supplied background color.
- * "background" is already in the screen gamma, while "background_1" is
- * at a gamma of 1.0.  Paletted files have already been taken care of.
- */
-static void
-png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr)
-{
-#ifdef PNG_READ_GAMMA_SUPPORTED
-   png_const_bytep gamma_table = png_ptr->gamma_table;
-   png_const_bytep gamma_from_1 = png_ptr->gamma_from_1;
-   png_const_bytep gamma_to_1 = png_ptr->gamma_to_1;
-   png_const_uint_16pp gamma_16 = png_ptr->gamma_16_table;
-   png_const_uint_16pp gamma_16_from_1 = png_ptr->gamma_16_from_1;
-   png_const_uint_16pp gamma_16_to_1 = png_ptr->gamma_16_to_1;
-   int gamma_shift = png_ptr->gamma_shift;
-   int optimize = (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0;
-#endif
-
-   png_bytep sp;
-   png_uint_32 i;
-   png_uint_32 row_width = row_info->width;
-   int shift;
-
-   png_debug(1, "in png_do_compose");
-
-   switch (row_info->color_type)
-   {
-      case PNG_COLOR_TYPE_GRAY:
-      {
-         switch (row_info->bit_depth)
-         {
-            case 1:
-            {
-               sp = row;
-               shift = 7;
-               for (i = 0; i < row_width; i++)
-               {
-                  if ((png_uint_16)((*sp >> shift) & 0x01)
-                     == png_ptr->trans_color.gray)
-                  {
-                     unsigned int tmp = *sp & (0x7f7f >> (7 - shift));
-                     tmp |=
-                         (unsigned int)(png_ptr->background.gray << shift);
-                     *sp = (png_byte)(tmp & 0xff);
-                  }
-
-                  if (shift == 0)
-                  {
-                     shift = 7;
-                     sp++;
-                  }
-
-                  else
-                     shift--;
-               }
-               break;
-            }
-
-            case 2:
-            {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-               if (gamma_table != NULL)
-               {
-                  sp = row;
-                  shift = 6;
-                  for (i = 0; i < row_width; i++)
-                  {
-                     if ((png_uint_16)((*sp >> shift) & 0x03)
-                         == png_ptr->trans_color.gray)
-                     {
-                        unsigned int tmp = *sp & (0x3f3f >> (6 - shift));
-                        tmp |=
-                           (unsigned int)png_ptr->background.gray << shift;
-                        *sp = (png_byte)(tmp & 0xff);
-                     }
-
-                     else
-                     {
-                        unsigned int p = (*sp >> shift) & 0x03;
-                        unsigned int g = (gamma_table [p | (p << 2) |
-                            (p << 4) | (p << 6)] >> 6) & 0x03;
-                        unsigned int tmp = *sp & (0x3f3f >> (6 - shift));
-                        tmp |= (unsigned int)(g << shift);
-                        *sp = (png_byte)(tmp & 0xff);
-                     }
-
-                     if (shift == 0)
-                     {
-                        shift = 6;
-                        sp++;
-                     }
-
-                     else
-                        shift -= 2;
-                  }
-               }
-
-               else
-#endif
-               {
-                  sp = row;
-                  shift = 6;
-                  for (i = 0; i < row_width; i++)
-                  {
-                     if ((png_uint_16)((*sp >> shift) & 0x03)
-                         == png_ptr->trans_color.gray)
-                     {
-                        unsigned int tmp = *sp & (0x3f3f >> (6 - shift));
-                        tmp |=
-                            (unsigned int)png_ptr->background.gray << shift;
-                        *sp = (png_byte)(tmp & 0xff);
-                     }
-
-                     if (shift == 0)
-                     {
-                        shift = 6;
-                        sp++;
-                     }
-
-                     else
-                        shift -= 2;
-                  }
-               }
-               break;
-            }
-
-            case 4:
-            {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-               if (gamma_table != NULL)
-               {
-                  sp = row;
-                  shift = 4;
-                  for (i = 0; i < row_width; i++)
-                  {
-                     if ((png_uint_16)((*sp >> shift) & 0x0f)
-                         == png_ptr->trans_color.gray)
-                     {
-                        unsigned int tmp = *sp & (0x0f0f >> (4 - shift));
-                        tmp |=
-                           (unsigned int)(png_ptr->background.gray << shift);
-                        *sp = (png_byte)(tmp & 0xff);
-                     }
-
-                     else
-                     {
-                        unsigned int p = (*sp >> shift) & 0x0f;
-                        unsigned int g = (gamma_table[p | (p << 4)] >> 4) &
-                           0x0f;
-                        unsigned int tmp = *sp & (0x0f0f >> (4 - shift));
-                        tmp |= (unsigned int)(g << shift);
-                        *sp = (png_byte)(tmp & 0xff);
-                     }
-
-                     if (shift == 0)
-                     {
-                        shift = 4;
-                        sp++;
-                     }
-
-                     else
-                        shift -= 4;
-                  }
-               }
-
-               else
-#endif
-               {
-                  sp = row;
-                  shift = 4;
-                  for (i = 0; i < row_width; i++)
-                  {
-                     if ((png_uint_16)((*sp >> shift) & 0x0f)
-                         == png_ptr->trans_color.gray)
-                     {
-                        unsigned int tmp = *sp & (0x0f0f >> (4 - shift));
-                        tmp |=
-                           (unsigned int)(png_ptr->background.gray << shift);
-                        *sp = (png_byte)(tmp & 0xff);
-                     }
-
-                     if (shift == 0)
-                     {
-                        shift = 4;
-                        sp++;
-                     }
-
-                     else
-                        shift -= 4;
-                  }
-               }
-               break;
-            }
-
-            case 8:
-            {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-               if (gamma_table != NULL)
-               {
-                  sp = row;
-                  for (i = 0; i < row_width; i++, sp++)
-                  {
-                     if (*sp == png_ptr->trans_color.gray)
-                        *sp = (png_byte)png_ptr->background.gray;
-
-                     else
-                        *sp = gamma_table[*sp];
-                  }
-               }
-               else
-#endif
-               {
-                  sp = row;
-                  for (i = 0; i < row_width; i++, sp++)
-                  {
-                     if (*sp == png_ptr->trans_color.gray)
-                        *sp = (png_byte)png_ptr->background.gray;
-                  }
-               }
-               break;
-            }
-
-            case 16:
-            {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-               if (gamma_16 != NULL)
-               {
-                  sp = row;
-                  for (i = 0; i < row_width; i++, sp += 2)
-                  {
-                     png_uint_16 v;
-
-                     v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
-
-                     if (v == png_ptr->trans_color.gray)
-                     {
-                        /* Background is already in screen gamma */
-                        *sp = (png_byte)((png_ptr->background.gray >> 8)
-                             & 0xff);
-                        *(sp + 1) = (png_byte)(png_ptr->background.gray
-                             & 0xff);
-                     }
-
-                     else
-                     {
-                        v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
-                        *sp = (png_byte)((v >> 8) & 0xff);
-                        *(sp + 1) = (png_byte)(v & 0xff);
-                     }
-                  }
-               }
-               else
-#endif
-               {
-                  sp = row;
-                  for (i = 0; i < row_width; i++, sp += 2)
-                  {
-                     png_uint_16 v;
-
-                     v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
-
-                     if (v == png_ptr->trans_color.gray)
-                     {
-                        *sp = (png_byte)((png_ptr->background.gray >> 8)
-                             & 0xff);
-                        *(sp + 1) = (png_byte)(png_ptr->background.gray
-                             & 0xff);
-                     }
-                  }
-               }
-               break;
-            }
-
-            default:
-               break;
-         }
-         break;
-      }
-
-      case PNG_COLOR_TYPE_RGB:
-      {
-         if (row_info->bit_depth == 8)
-         {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-            if (gamma_table != NULL)
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++, sp += 3)
-               {
-                  if (*sp == png_ptr->trans_color.red &&
-                      *(sp + 1) == png_ptr->trans_color.green &&
-                      *(sp + 2) == png_ptr->trans_color.blue)
-                  {
-                     *sp = (png_byte)png_ptr->background.red;
-                     *(sp + 1) = (png_byte)png_ptr->background.green;
-                     *(sp + 2) = (png_byte)png_ptr->background.blue;
-                  }
-
-                  else
-                  {
-                     *sp = gamma_table[*sp];
-                     *(sp + 1) = gamma_table[*(sp + 1)];
-                     *(sp + 2) = gamma_table[*(sp + 2)];
-                  }
-               }
-            }
-            else
-#endif
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++, sp += 3)
-               {
-                  if (*sp == png_ptr->trans_color.red &&
-                      *(sp + 1) == png_ptr->trans_color.green &&
-                      *(sp + 2) == png_ptr->trans_color.blue)
-                  {
-                     *sp = (png_byte)png_ptr->background.red;
-                     *(sp + 1) = (png_byte)png_ptr->background.green;
-                     *(sp + 2) = (png_byte)png_ptr->background.blue;
-                  }
-               }
-            }
-         }
-         else /* if (row_info->bit_depth == 16) */
-         {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-            if (gamma_16 != NULL)
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++, sp += 6)
-               {
-                  png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
-
-                  png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
-                      + *(sp + 3));
-
-                  png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
-                      + *(sp + 5));
-
-                  if (r == png_ptr->trans_color.red &&
-                      g == png_ptr->trans_color.green &&
-                      b == png_ptr->trans_color.blue)
-                  {
-                     /* Background is already in screen gamma */
-                     *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
-                     *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
-                     *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
-                             & 0xff);
-                     *(sp + 3) = (png_byte)(png_ptr->background.green
-                             & 0xff);
-                     *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
-                             & 0xff);
-                     *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
-                  }
-
-                  else
-                  {
-                     png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
-                     *sp = (png_byte)((v >> 8) & 0xff);
-                     *(sp + 1) = (png_byte)(v & 0xff);
-
-                     v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];
-                     *(sp + 2) = (png_byte)((v >> 8) & 0xff);
-                     *(sp + 3) = (png_byte)(v & 0xff);
-
-                     v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];
-                     *(sp + 4) = (png_byte)((v >> 8) & 0xff);
-                     *(sp + 5) = (png_byte)(v & 0xff);
-                  }
-               }
-            }
-
-            else
-#endif
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++, sp += 6)
-               {
-                  png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
-
-                  png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
-                      + *(sp + 3));
-
-                  png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
-                      + *(sp + 5));
-
-                  if (r == png_ptr->trans_color.red &&
-                      g == png_ptr->trans_color.green &&
-                      b == png_ptr->trans_color.blue)
-                  {
-                     *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
-                     *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
-                     *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
-                             & 0xff);
-                     *(sp + 3) = (png_byte)(png_ptr->background.green
-                             & 0xff);
-                     *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
-                             & 0xff);
-                     *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
-                  }
-               }
-            }
-         }
-         break;
-      }
-
-      case PNG_COLOR_TYPE_GRAY_ALPHA:
-      {
-         if (row_info->bit_depth == 8)
-         {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-            if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
-                gamma_table != NULL)
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++, sp += 2)
-               {
-                  png_uint_16 a = *(sp + 1);
-
-                  if (a == 0xff)
-                     *sp = gamma_table[*sp];
-
-                  else if (a == 0)
-                  {
-                     /* Background is already in screen gamma */
-                     *sp = (png_byte)png_ptr->background.gray;
-                  }
-
-                  else
-                  {
-                     png_byte v, w;
-
-                     v = gamma_to_1[*sp];
-                     png_composite(w, v, a, png_ptr->background_1.gray);
-                     if (optimize == 0)
-                        w = gamma_from_1[w];
-                     *sp = w;
-                  }
-               }
-            }
-            else
-#endif
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++, sp += 2)
-               {
-                  png_byte a = *(sp + 1);
-
-                  if (a == 0)
-                     *sp = (png_byte)png_ptr->background.gray;
-
-                  else if (a < 0xff)
-                     png_composite(*sp, *sp, a, png_ptr->background.gray);
-               }
-            }
-         }
-         else /* if (png_ptr->bit_depth == 16) */
-         {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-            if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
-                gamma_16_to_1 != NULL)
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++, sp += 4)
-               {
-                  png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8)
-                      + *(sp + 3));
-
-                  if (a == (png_uint_16)0xffff)
-                  {
-                     png_uint_16 v;
-
-                     v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
-                     *sp = (png_byte)((v >> 8) & 0xff);
-                     *(sp + 1) = (png_byte)(v & 0xff);
-                  }
-
-                  else if (a == 0)
-                  {
-                     /* Background is already in screen gamma */
-                     *sp = (png_byte)((png_ptr->background.gray >> 8)
-                             & 0xff);
-                     *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff);
-                  }
-
-                  else
-                  {
-                     png_uint_16 g, v, w;
-
-                     g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
-                     png_composite_16(v, g, a, png_ptr->background_1.gray);
-                     if (optimize != 0)
-                        w = v;
-                     else
-                        w = gamma_16_from_1[(v & 0xff) >>
-                            gamma_shift][v >> 8];
-                     *sp = (png_byte)((w >> 8) & 0xff);
-                     *(sp + 1) = (png_byte)(w & 0xff);
-                  }
-               }
-            }
-            else
-#endif
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++, sp += 4)
-               {
-                  png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8)
-                      + *(sp + 3));
-
-                  if (a == 0)
-                  {
-                     *sp = (png_byte)((png_ptr->background.gray >> 8)
-                             & 0xff);
-                     *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff);
-                  }
-
-                  else if (a < 0xffff)
-                  {
-                     png_uint_16 g, v;
-
-                     g = (png_uint_16)(((*sp) << 8) + *(sp + 1));
-                     png_composite_16(v, g, a, png_ptr->background.gray);
-                     *sp = (png_byte)((v >> 8) & 0xff);
-                     *(sp + 1) = (png_byte)(v & 0xff);
-                  }
-               }
-            }
-         }
-         break;
-      }
-
-      case PNG_COLOR_TYPE_RGB_ALPHA:
-      {
-         if (row_info->bit_depth == 8)
-         {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-            if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
-                gamma_table != NULL)
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++, sp += 4)
-               {
-                  png_byte a = *(sp + 3);
-
-                  if (a == 0xff)
-                  {
-                     *sp = gamma_table[*sp];
-                     *(sp + 1) = gamma_table[*(sp + 1)];
-                     *(sp + 2) = gamma_table[*(sp + 2)];
-                  }
-
-                  else if (a == 0)
-                  {
-                     /* Background is already in screen gamma */
-                     *sp = (png_byte)png_ptr->background.red;
-                     *(sp + 1) = (png_byte)png_ptr->background.green;
-                     *(sp + 2) = (png_byte)png_ptr->background.blue;
-                  }
-
-                  else
-                  {
-                     png_byte v, w;
-
-                     v = gamma_to_1[*sp];
-                     png_composite(w, v, a, png_ptr->background_1.red);
-                     if (optimize == 0) w = gamma_from_1[w];
-                     *sp = w;
-
-                     v = gamma_to_1[*(sp + 1)];
-                     png_composite(w, v, a, png_ptr->background_1.green);
-                     if (optimize == 0) w = gamma_from_1[w];
-                     *(sp + 1) = w;
-
-                     v = gamma_to_1[*(sp + 2)];
-                     png_composite(w, v, a, png_ptr->background_1.blue);
-                     if (optimize == 0) w = gamma_from_1[w];
-                     *(sp + 2) = w;
-                  }
-               }
-            }
-            else
-#endif
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++, sp += 4)
-               {
-                  png_byte a = *(sp + 3);
-
-                  if (a == 0)
-                  {
-                     *sp = (png_byte)png_ptr->background.red;
-                     *(sp + 1) = (png_byte)png_ptr->background.green;
-                     *(sp + 2) = (png_byte)png_ptr->background.blue;
-                  }
-
-                  else if (a < 0xff)
-                  {
-                     png_composite(*sp, *sp, a, png_ptr->background.red);
-
-                     png_composite(*(sp + 1), *(sp + 1), a,
-                         png_ptr->background.green);
-
-                     png_composite(*(sp + 2), *(sp + 2), a,
-                         png_ptr->background.blue);
-                  }
-               }
-            }
-         }
-         else /* if (row_info->bit_depth == 16) */
-         {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-            if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
-                gamma_16_to_1 != NULL)
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++, sp += 8)
-               {
-                  png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
-                      << 8) + (png_uint_16)(*(sp + 7)));
-
-                  if (a == (png_uint_16)0xffff)
-                  {
-                     png_uint_16 v;
-
-                     v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
-                     *sp = (png_byte)((v >> 8) & 0xff);
-                     *(sp + 1) = (png_byte)(v & 0xff);
-
-                     v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];
-                     *(sp + 2) = (png_byte)((v >> 8) & 0xff);
-                     *(sp + 3) = (png_byte)(v & 0xff);
-
-                     v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];
-                     *(sp + 4) = (png_byte)((v >> 8) & 0xff);
-                     *(sp + 5) = (png_byte)(v & 0xff);
-                  }
-
-                  else if (a == 0)
-                  {
-                     /* Background is already in screen gamma */
-                     *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
-                     *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
-                     *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
-                             & 0xff);
-                     *(sp + 3) = (png_byte)(png_ptr->background.green
-                             & 0xff);
-                     *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
-                             & 0xff);
-                     *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
-                  }
-
-                  else
-                  {
-                     png_uint_16 v, w;
-
-                     v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
-                     png_composite_16(w, v, a, png_ptr->background_1.red);
-                     if (optimize == 0)
-                        w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >>
-                             8];
-                     *sp = (png_byte)((w >> 8) & 0xff);
-                     *(sp + 1) = (png_byte)(w & 0xff);
-
-                     v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)];
-                     png_composite_16(w, v, a, png_ptr->background_1.green);
-                     if (optimize == 0)
-                        w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >>
-                             8];
-
-                     *(sp + 2) = (png_byte)((w >> 8) & 0xff);
-                     *(sp + 3) = (png_byte)(w & 0xff);
-
-                     v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)];
-                     png_composite_16(w, v, a, png_ptr->background_1.blue);
-                     if (optimize == 0)
-                        w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >>
-                             8];
-
-                     *(sp + 4) = (png_byte)((w >> 8) & 0xff);
-                     *(sp + 5) = (png_byte)(w & 0xff);
-                  }
-               }
-            }
-
-            else
-#endif
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++, sp += 8)
-               {
-                  png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
-                      << 8) + (png_uint_16)(*(sp + 7)));
-
-                  if (a == 0)
-                  {
-                     *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
-                     *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
-                     *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
-                             & 0xff);
-                     *(sp + 3) = (png_byte)(png_ptr->background.green
-                             & 0xff);
-                     *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
-                             & 0xff);
-                     *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
-                  }
-
-                  else if (a < 0xffff)
-                  {
-                     png_uint_16 v;
-
-                     png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
-                     png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
-                         + *(sp + 3));
-                     png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
-                         + *(sp + 5));
-
-                     png_composite_16(v, r, a, png_ptr->background.red);
-                     *sp = (png_byte)((v >> 8) & 0xff);
-                     *(sp + 1) = (png_byte)(v & 0xff);
-
-                     png_composite_16(v, g, a, png_ptr->background.green);
-                     *(sp + 2) = (png_byte)((v >> 8) & 0xff);
-                     *(sp + 3) = (png_byte)(v & 0xff);
-
-                     png_composite_16(v, b, a, png_ptr->background.blue);
-                     *(sp + 4) = (png_byte)((v >> 8) & 0xff);
-                     *(sp + 5) = (png_byte)(v & 0xff);
-                  }
-               }
-            }
-         }
-         break;
-      }
-
-      default:
-         break;
-   }
-}
-#endif /* READ_BACKGROUND || READ_ALPHA_MODE */
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
-/* Gamma correct the image, avoiding the alpha channel.  Make sure
- * you do this after you deal with the transparency issue on grayscale
- * or RGB images. If your bit depth is 8, use gamma_table, if it
- * is 16, use gamma_16_table and gamma_shift.  Build these with
- * build_gamma_table().
- */
-static void
-png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr)
-{
-   png_const_bytep gamma_table = png_ptr->gamma_table;
-   png_const_uint_16pp gamma_16_table = png_ptr->gamma_16_table;
-   int gamma_shift = png_ptr->gamma_shift;
-
-   png_bytep sp;
-   png_uint_32 i;
-   png_uint_32 row_width=row_info->width;
-
-   png_debug(1, "in png_do_gamma");
-
-   if (((row_info->bit_depth <= 8 && gamma_table != NULL) ||
-       (row_info->bit_depth == 16 && gamma_16_table != NULL)))
-   {
-      switch (row_info->color_type)
-      {
-         case PNG_COLOR_TYPE_RGB:
-         {
-            if (row_info->bit_depth == 8)
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++)
-               {
-                  *sp = gamma_table[*sp];
-                  sp++;
-                  *sp = gamma_table[*sp];
-                  sp++;
-                  *sp = gamma_table[*sp];
-                  sp++;
-               }
-            }
-
-            else /* if (row_info->bit_depth == 16) */
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++)
-               {
-                  png_uint_16 v;
-
-                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
-                  *sp = (png_byte)((v >> 8) & 0xff);
-                  *(sp + 1) = (png_byte)(v & 0xff);
-                  sp += 2;
-
-                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
-                  *sp = (png_byte)((v >> 8) & 0xff);
-                  *(sp + 1) = (png_byte)(v & 0xff);
-                  sp += 2;
-
-                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
-                  *sp = (png_byte)((v >> 8) & 0xff);
-                  *(sp + 1) = (png_byte)(v & 0xff);
-                  sp += 2;
-               }
-            }
-            break;
-         }
-
-         case PNG_COLOR_TYPE_RGB_ALPHA:
-         {
-            if (row_info->bit_depth == 8)
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++)
-               {
-                  *sp = gamma_table[*sp];
-                  sp++;
-
-                  *sp = gamma_table[*sp];
-                  sp++;
-
-                  *sp = gamma_table[*sp];
-                  sp++;
-
-                  sp++;
-               }
-            }
-
-            else /* if (row_info->bit_depth == 16) */
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++)
-               {
-                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
-                  *sp = (png_byte)((v >> 8) & 0xff);
-                  *(sp + 1) = (png_byte)(v & 0xff);
-                  sp += 2;
-
-                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
-                  *sp = (png_byte)((v >> 8) & 0xff);
-                  *(sp + 1) = (png_byte)(v & 0xff);
-                  sp += 2;
-
-                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
-                  *sp = (png_byte)((v >> 8) & 0xff);
-                  *(sp + 1) = (png_byte)(v & 0xff);
-                  sp += 4;
-               }
-            }
-            break;
-         }
-
-         case PNG_COLOR_TYPE_GRAY_ALPHA:
-         {
-            if (row_info->bit_depth == 8)
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++)
-               {
-                  *sp = gamma_table[*sp];
-                  sp += 2;
-               }
-            }
-
-            else /* if (row_info->bit_depth == 16) */
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++)
-               {
-                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
-                  *sp = (png_byte)((v >> 8) & 0xff);
-                  *(sp + 1) = (png_byte)(v & 0xff);
-                  sp += 4;
-               }
-            }
-            break;
-         }
-
-         case PNG_COLOR_TYPE_GRAY:
-         {
-            if (row_info->bit_depth == 2)
-            {
-               sp = row;
-               for (i = 0; i < row_width; i += 4)
-               {
-                  int a = *sp & 0xc0;
-                  int b = *sp & 0x30;
-                  int c = *sp & 0x0c;
-                  int d = *sp & 0x03;
-
-                  *sp = (png_byte)(
-                      ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)])   ) & 0xc0)|
-                      ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)|
-                      ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)|
-                      ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) ));
-                  sp++;
-               }
-            }
-
-            if (row_info->bit_depth == 4)
-            {
-               sp = row;
-               for (i = 0; i < row_width; i += 2)
-               {
-                  int msb = *sp & 0xf0;
-                  int lsb = *sp & 0x0f;
-
-                  *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0)
-                      | (((int)gamma_table[(lsb << 4) | lsb]) >> 4));
-                  sp++;
-               }
-            }
-
-            else if (row_info->bit_depth == 8)
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++)
-               {
-                  *sp = gamma_table[*sp];
-                  sp++;
-               }
-            }
-
-            else if (row_info->bit_depth == 16)
-            {
-               sp = row;
-               for (i = 0; i < row_width; i++)
-               {
-                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
-                  *sp = (png_byte)((v >> 8) & 0xff);
-                  *(sp + 1) = (png_byte)(v & 0xff);
-                  sp += 2;
-               }
-            }
-            break;
-         }
-
-         default:
-            break;
-      }
-   }
-}
-#endif
-
-#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
-/* Encode the alpha channel to the output gamma (the input channel is always
- * linear.)  Called only with color types that have an alpha channel.  Needs the
- * from_1 tables.
- */
-static void
-png_do_encode_alpha(png_row_infop row_info, png_bytep row, png_structrp png_ptr)
-{
-   png_uint_32 row_width = row_info->width;
-
-   png_debug(1, "in png_do_encode_alpha");
-
-   if ((row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0)
-   {
-      if (row_info->bit_depth == 8)
-      {
-         png_bytep table = png_ptr->gamma_from_1;
-
-         if (table != NULL)
-         {
-            int step = (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 4 : 2;
-
-            /* The alpha channel is the last component: */
-            row += step - 1;
-
-            for (; row_width > 0; --row_width, row += step)
-               *row = table[*row];
-
-            return;
-         }
-      }
-
-      else if (row_info->bit_depth == 16)
-      {
-         png_uint_16pp table = png_ptr->gamma_16_from_1;
-         int gamma_shift = png_ptr->gamma_shift;
-
-         if (table != NULL)
-         {
-            int step = (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 8 : 4;
-
-            /* The alpha channel is the last component: */
-            row += step - 2;
-
-            for (; row_width > 0; --row_width, row += step)
-            {
-               png_uint_16 v;
-
-               v = table[*(row + 1) >> gamma_shift][*row];
-               *row = (png_byte)((v >> 8) & 0xff);
-               *(row + 1) = (png_byte)(v & 0xff);
-            }
-
-            return;
-         }
-      }
-   }
-
-   /* Only get to here if called with a weird row_info; no harm has been done,
-    * so just issue a warning.
-    */
-   png_warning(png_ptr, "png_do_encode_alpha: unexpected call");
-}
-#endif
-
-#ifdef PNG_READ_EXPAND_SUPPORTED
-/* Expands a palette row to an RGB or RGBA row depending
- * upon whether you supply trans and num_trans.
- */
-static void
-png_do_expand_palette(png_structrp png_ptr, png_row_infop row_info,
-    png_bytep row, png_const_colorp palette, png_const_bytep trans_alpha,
-    int num_trans)
-{
-   int shift, value;
-   png_bytep sp, dp;
-   png_uint_32 i;
-   png_uint_32 row_width=row_info->width;
-
-   png_debug(1, "in png_do_expand_palette");
-
-   if (row_info->color_type == PNG_COLOR_TYPE_PALETTE)
-   {
-      if (row_info->bit_depth < 8)
-      {
-         switch (row_info->bit_depth)
-         {
-            case 1:
-            {
-               sp = row + (size_t)((row_width - 1) >> 3);
-               dp = row + (size_t)row_width - 1;
-               shift = 7 - (int)((row_width + 7) & 0x07);
-               for (i = 0; i < row_width; i++)
-               {
-                  if ((*sp >> shift) & 0x01)
-                     *dp = 1;
-
-                  else
-                     *dp = 0;
-
-                  if (shift == 7)
-                  {
-                     shift = 0;
-                     sp--;
-                  }
-
-                  else
-                     shift++;
-
-                  dp--;
-               }
-               break;
-            }
-
-            case 2:
-            {
-               sp = row + (size_t)((row_width - 1) >> 2);
-               dp = row + (size_t)row_width - 1;
-               shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
-               for (i = 0; i < row_width; i++)
-               {
-                  value = (*sp >> shift) & 0x03;
-                  *dp = (png_byte)value;
-                  if (shift == 6)
-                  {
-                     shift = 0;
-                     sp--;
-                  }
-
-                  else
-                     shift += 2;
-
-                  dp--;
-               }
-               break;
-            }
-
-            case 4:
-            {
-               sp = row + (size_t)((row_width - 1) >> 1);
-               dp = row + (size_t)row_width - 1;
-               shift = (int)((row_width & 0x01) << 2);
-               for (i = 0; i < row_width; i++)
-               {
-                  value = (*sp >> shift) & 0x0f;
-                  *dp = (png_byte)value;
-                  if (shift == 4)
-                  {
-                     shift = 0;
-                     sp--;
-                  }
-
-                  else
-                     shift += 4;
-
-                  dp--;
-               }
-               break;
-            }
-
-            default:
-               break;
-         }
-         row_info->bit_depth = 8;
-         row_info->pixel_depth = 8;
-         row_info->rowbytes = row_width;
-      }
-
-      if (row_info->bit_depth == 8)
-      {
-         {
-            if (num_trans > 0)
-            {
-               sp = row + (size_t)row_width - 1;
-               dp = row + ((size_t)row_width << 2) - 1;
-
-               i = 0;
-#ifdef PNG_ARM_NEON_INTRINSICS_AVAILABLE
-               if (png_ptr->riffled_palette != NULL)
-               {
-                  /* The RGBA optimization works with png_ptr->bit_depth == 8
-                   * but sometimes row_info->bit_depth has been changed to 8.
-                   * In these cases, the palette hasn't been riffled.
-                   */
-                  i = png_do_expand_palette_rgba8_neon(png_ptr, row_info, row,
-                      &sp, &dp);
-               }
-#else
-               PNG_UNUSED(png_ptr)
-#endif
-
-               for (; i < row_width; i++)
-               {
-                  if ((int)(*sp) >= num_trans)
-                     *dp-- = 0xff;
-                  else
-                     *dp-- = trans_alpha[*sp];
-                  *dp-- = palette[*sp].blue;
-                  *dp-- = palette[*sp].green;
-                  *dp-- = palette[*sp].red;
-                  sp--;
-               }
-               row_info->bit_depth = 8;
-               row_info->pixel_depth = 32;
-               row_info->rowbytes = row_width * 4;
-               row_info->color_type = 6;
-               row_info->channels = 4;
-            }
-
-            else
-            {
-               sp = row + (size_t)row_width - 1;
-               dp = row + (size_t)(row_width * 3) - 1;
-               i = 0;
-#ifdef PNG_ARM_NEON_INTRINSICS_AVAILABLE
-               i = png_do_expand_palette_rgb8_neon(png_ptr, row_info, row,
-                   &sp, &dp);
-#else
-               PNG_UNUSED(png_ptr)
-#endif
-
-               for (; i < row_width; i++)
-               {
-                  *dp-- = palette[*sp].blue;
-                  *dp-- = palette[*sp].green;
-                  *dp-- = palette[*sp].red;
-                  sp--;
-               }
-
-               row_info->bit_depth = 8;
-               row_info->pixel_depth = 24;
-               row_info->rowbytes = row_width * 3;
-               row_info->color_type = 2;
-               row_info->channels = 3;
-            }
-         }
-      }
-   }
-}
-
-/* If the bit depth < 8, it is expanded to 8.  Also, if the already
- * expanded transparency value is supplied, an alpha channel is built.
- */
-static void
-png_do_expand(png_row_infop row_info, png_bytep row,
-    png_const_color_16p trans_color)
-{
-   int shift, value;
-   png_bytep sp, dp;
-   png_uint_32 i;
-   png_uint_32 row_width=row_info->width;
-
-   png_debug(1, "in png_do_expand");
-
-   if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
-   {
-      unsigned int gray = trans_color != NULL ? trans_color->gray : 0;
-
-      if (row_info->bit_depth < 8)
-      {
-         switch (row_info->bit_depth)
-         {
-            case 1:
-            {
-               gray = (gray & 0x01) * 0xff;
-               sp = row + (size_t)((row_width - 1) >> 3);
-               dp = row + (size_t)row_width - 1;
-               shift = 7 - (int)((row_width + 7) & 0x07);
-               for (i = 0; i < row_width; i++)
-               {
-                  if ((*sp >> shift) & 0x01)
-                     *dp = 0xff;
-
-                  else
-                     *dp = 0;
-
-                  if (shift == 7)
-                  {
-                     shift = 0;
-                     sp--;
-                  }
-
-                  else
-                     shift++;
-
-                  dp--;
-               }
-               break;
-            }
-
-            case 2:
-            {
-               gray = (gray & 0x03) * 0x55;
-               sp = row + (size_t)((row_width - 1) >> 2);
-               dp = row + (size_t)row_width - 1;
-               shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
-               for (i = 0; i < row_width; i++)
-               {
-                  value = (*sp >> shift) & 0x03;
-                  *dp = (png_byte)(value | (value << 2) | (value << 4) |
-                     (value << 6));
-                  if (shift == 6)
-                  {
-                     shift = 0;
-                     sp--;
-                  }
-
-                  else
-                     shift += 2;
-
-                  dp--;
-               }
-               break;
-            }
-
-            case 4:
-            {
-               gray = (gray & 0x0f) * 0x11;
-               sp = row + (size_t)((row_width - 1) >> 1);
-               dp = row + (size_t)row_width - 1;
-               shift = (int)((1 - ((row_width + 1) & 0x01)) << 2);
-               for (i = 0; i < row_width; i++)
-               {
-                  value = (*sp >> shift) & 0x0f;
-                  *dp = (png_byte)(value | (value << 4));
-                  if (shift == 4)
-                  {
-                     shift = 0;
-                     sp--;
-                  }
-
-                  else
-                     shift = 4;
-
-                  dp--;
-               }
-               break;
-            }
-
-            default:
-               break;
-         }
-
-         row_info->bit_depth = 8;
-         row_info->pixel_depth = 8;
-         row_info->rowbytes = row_width;
-      }
-
-      if (trans_color != NULL)
-      {
-         if (row_info->bit_depth == 8)
-         {
-            gray = gray & 0xff;
-            sp = row + (size_t)row_width - 1;
-            dp = row + ((size_t)row_width << 1) - 1;
-
-            for (i = 0; i < row_width; i++)
-            {
-               if ((*sp & 0xffU) == gray)
-                  *dp-- = 0;
-
-               else
-                  *dp-- = 0xff;
-
-               *dp-- = *sp--;
-            }
-         }
-
-         else if (row_info->bit_depth == 16)
-         {
-            unsigned int gray_high = (gray >> 8) & 0xff;
-            unsigned int gray_low = gray & 0xff;
-            sp = row + row_info->rowbytes - 1;
-            dp = row + (row_info->rowbytes << 1) - 1;
-            for (i = 0; i < row_width; i++)
-            {
-               if ((*(sp - 1) & 0xffU) == gray_high &&
-                   (*(sp) & 0xffU) == gray_low)
-               {
-                  *dp-- = 0;
-                  *dp-- = 0;
-               }
-
-               else
-               {
-                  *dp-- = 0xff;
-                  *dp-- = 0xff;
-               }
-
-               *dp-- = *sp--;
-               *dp-- = *sp--;
-            }
-         }
-
-         row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
-         row_info->channels = 2;
-         row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1);
-         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
-             row_width);
-      }
-   }
-   else if (row_info->color_type == PNG_COLOR_TYPE_RGB &&
-       trans_color != NULL)
-   {
-      if (row_info->bit_depth == 8)
-      {
-         png_byte red = (png_byte)(trans_color->red & 0xff);
-         png_byte green = (png_byte)(trans_color->green & 0xff);
-         png_byte blue = (png_byte)(trans_color->blue & 0xff);
-         sp = row + (size_t)row_info->rowbytes - 1;
-         dp = row + ((size_t)row_width << 2) - 1;
-         for (i = 0; i < row_width; i++)
-         {
-            if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue)
-               *dp-- = 0;
-
-            else
-               *dp-- = 0xff;
-
-            *dp-- = *sp--;
-            *dp-- = *sp--;
-            *dp-- = *sp--;
-         }
-      }
-      else if (row_info->bit_depth == 16)
-      {
-         png_byte red_high = (png_byte)((trans_color->red >> 8) & 0xff);
-         png_byte green_high = (png_byte)((trans_color->green >> 8) & 0xff);
-         png_byte blue_high = (png_byte)((trans_color->blue >> 8) & 0xff);
-         png_byte red_low = (png_byte)(trans_color->red & 0xff);
-         png_byte green_low = (png_byte)(trans_color->green & 0xff);
-         png_byte blue_low = (png_byte)(trans_color->blue & 0xff);
-         sp = row + row_info->rowbytes - 1;
-         dp = row + ((size_t)row_width << 3) - 1;
-         for (i = 0; i < row_width; i++)
-         {
-            if (*(sp - 5) == red_high &&
-                *(sp - 4) == red_low &&
-                *(sp - 3) == green_high &&
-                *(sp - 2) == green_low &&
-                *(sp - 1) == blue_high &&
-                *(sp    ) == blue_low)
-            {
-               *dp-- = 0;
-               *dp-- = 0;
-            }
-
-            else
-            {
-               *dp-- = 0xff;
-               *dp-- = 0xff;
-            }
-
-            *dp-- = *sp--;
-            *dp-- = *sp--;
-            *dp-- = *sp--;
-            *dp-- = *sp--;
-            *dp-- = *sp--;
-            *dp-- = *sp--;
-         }
-      }
-      row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
-      row_info->channels = 4;
-      row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2);
-      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
-   }
-}
-#endif
-
-#ifdef PNG_READ_EXPAND_16_SUPPORTED
-/* If the bit depth is 8 and the color type is not a palette type expand the
- * whole row to 16 bits.  Has no effect otherwise.
- */
-static void
-png_do_expand_16(png_row_infop row_info, png_bytep row)
-{
-   if (row_info->bit_depth == 8 &&
-      row_info->color_type != PNG_COLOR_TYPE_PALETTE)
-   {
-      /* The row have a sequence of bytes containing [0..255] and we need
-       * to turn it into another row containing [0..65535], to do this we
-       * calculate:
-       *
-       *  (input / 255) * 65535
-       *
-       *  Which happens to be exactly input * 257 and this can be achieved
-       *  simply by byte replication in place (copying backwards).
-       */
-      png_byte *sp = row + row_info->rowbytes; /* source, last byte + 1 */
-      png_byte *dp = sp + row_info->rowbytes;  /* destination, end + 1 */
-      while (dp > sp)
-      {
-         dp[-2] = dp[-1] = *--sp; dp -= 2;
-      }
-
-      row_info->rowbytes *= 2;
-      row_info->bit_depth = 16;
-      row_info->pixel_depth = (png_byte)(row_info->channels * 16);
-   }
-}
-#endif
-
-#ifdef PNG_READ_QUANTIZE_SUPPORTED
-static void
-png_do_quantize(png_row_infop row_info, png_bytep row,
-    png_const_bytep palette_lookup, png_const_bytep quantize_lookup)
-{
-   png_bytep sp, dp;
-   png_uint_32 i;
-   png_uint_32 row_width=row_info->width;
-
-   png_debug(1, "in png_do_quantize");
-
-   if (row_info->bit_depth == 8)
-   {
-      if (row_info->color_type == PNG_COLOR_TYPE_RGB && palette_lookup)
-      {
-         int r, g, b, p;
-         sp = row;
-         dp = row;
-         for (i = 0; i < row_width; i++)
-         {
-            r = *sp++;
-            g = *sp++;
-            b = *sp++;
-
-            /* This looks real messy, but the compiler will reduce
-             * it down to a reasonable formula.  For example, with
-             * 5 bits per color, we get:
-             * p = (((r >> 3) & 0x1f) << 10) |
-             *    (((g >> 3) & 0x1f) << 5) |
-             *    ((b >> 3) & 0x1f);
-             */
-            p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) &
-                ((1 << PNG_QUANTIZE_RED_BITS) - 1)) <<
-                (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) |
-                (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) &
-                ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) <<
-                (PNG_QUANTIZE_BLUE_BITS)) |
-                ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) &
-                ((1 << PNG_QUANTIZE_BLUE_BITS) - 1));
-
-            *dp++ = palette_lookup[p];
-         }
-
-         row_info->color_type = PNG_COLOR_TYPE_PALETTE;
-         row_info->channels = 1;
-         row_info->pixel_depth = row_info->bit_depth;
-         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
-      }
-
-      else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA &&
-         palette_lookup != NULL)
-      {
-         int r, g, b, p;
-         sp = row;
-         dp = row;
-         for (i = 0; i < row_width; i++)
-         {
-            r = *sp++;
-            g = *sp++;
-            b = *sp++;
-            sp++;
-
-            p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) &
-                ((1 << PNG_QUANTIZE_RED_BITS) - 1)) <<
-                (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) |
-                (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) &
-                ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) <<
-                (PNG_QUANTIZE_BLUE_BITS)) |
-                ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) &
-                ((1 << PNG_QUANTIZE_BLUE_BITS) - 1));
-
-            *dp++ = palette_lookup[p];
-         }
-
-         row_info->color_type = PNG_COLOR_TYPE_PALETTE;
-         row_info->channels = 1;
-         row_info->pixel_depth = row_info->bit_depth;
-         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
-      }
-
-      else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE &&
-         quantize_lookup)
-      {
-         sp = row;
-
-         for (i = 0; i < row_width; i++, sp++)
-         {
-            *sp = quantize_lookup[*sp];
-         }
-      }
-   }
-}
-#endif /* READ_QUANTIZE */
-
-/* Transform the row.  The order of transformations is significant,
- * and is very touchy.  If you add a transformation, take care to
- * decide how it fits in with the other transformations here.
- */
-void /* PRIVATE */
-png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info)
-{
-   png_debug(1, "in png_do_read_transformations");
-
-   if (png_ptr->row_buf == NULL)
-   {
-      /* Prior to 1.5.4 this output row/pass where the NULL pointer is, but this
-       * error is incredibly rare and incredibly easy to debug without this
-       * information.
-       */
-      png_error(png_ptr, "NULL row buffer");
-   }
-
-   /* The following is debugging; prior to 1.5.4 the code was never compiled in;
-    * in 1.5.4 PNG_FLAG_DETECT_UNINITIALIZED was added and the macro
-    * PNG_WARN_UNINITIALIZED_ROW removed.  In 1.6 the new flag is set only for
-    * all transformations, however in practice the ROW_INIT always gets done on
-    * demand, if necessary.
-    */
-   if ((png_ptr->flags & PNG_FLAG_DETECT_UNINITIALIZED) != 0 &&
-       (png_ptr->flags & PNG_FLAG_ROW_INIT) == 0)
-   {
-      /* Application has failed to call either png_read_start_image() or
-       * png_read_update_info() after setting transforms that expand pixels.
-       * This check added to libpng-1.2.19 (but not enabled until 1.5.4).
-       */
-      png_error(png_ptr, "Uninitialized row");
-   }
-
-#ifdef PNG_READ_EXPAND_SUPPORTED
-   if ((png_ptr->transformations & PNG_EXPAND) != 0)
-   {
-      if (row_info->color_type == PNG_COLOR_TYPE_PALETTE)
-      {
-#ifdef PNG_ARM_NEON_INTRINSICS_AVAILABLE
-         if ((png_ptr->num_trans > 0) && (png_ptr->bit_depth == 8))
-         {
-            if (png_ptr->riffled_palette == NULL)
-            {
-               /* Initialize the accelerated palette expansion. */
-               png_ptr->riffled_palette =
-                   (png_bytep)png_malloc(png_ptr, 256 * 4);
-               png_riffle_palette_neon(png_ptr);
-            }
-         }
-#endif
-         png_do_expand_palette(png_ptr, row_info, png_ptr->row_buf + 1,
-             png_ptr->palette, png_ptr->trans_alpha, png_ptr->num_trans);
-      }
-
-      else
-      {
-         if (png_ptr->num_trans != 0 &&
-             (png_ptr->transformations & PNG_EXPAND_tRNS) != 0)
-            png_do_expand(row_info, png_ptr->row_buf + 1,
-                &(png_ptr->trans_color));
-
-         else
-            png_do_expand(row_info, png_ptr->row_buf + 1, NULL);
-      }
-   }
-#endif
-
-#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
-   if ((png_ptr->transformations & PNG_STRIP_ALPHA) != 0 &&
-       (png_ptr->transformations & PNG_COMPOSE) == 0 &&
-       (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
-       row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
-      png_do_strip_channel(row_info, png_ptr->row_buf + 1,
-          0 /* at_start == false, because SWAP_ALPHA happens later */);
-#endif
-
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-   if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0)
-   {
-      int rgb_error =
-          png_do_rgb_to_gray(png_ptr, row_info,
-              png_ptr->row_buf + 1);
-
-      if (rgb_error != 0)
-      {
-         png_ptr->rgb_to_gray_status=1;
-         if ((png_ptr->transformations & PNG_RGB_TO_GRAY) ==
-             PNG_RGB_TO_GRAY_WARN)
-            png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel");
-
-         if ((png_ptr->transformations & PNG_RGB_TO_GRAY) ==
-             PNG_RGB_TO_GRAY_ERR)
-            png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel");
-      }
-   }
-#endif
-
-/* From Andreas Dilger e-mail to png-implement, 26 March 1998:
- *
- *   In most cases, the "simple transparency" should be done prior to doing
- *   gray-to-RGB, or you will have to test 3x as many bytes to check if a
- *   pixel is transparent.  You would also need to make sure that the
- *   transparency information is upgraded to RGB.
- *
- *   To summarize, the current flow is:
- *   - Gray + simple transparency -> compare 1 or 2 gray bytes and composite
- *                                   with background "in place" if transparent,
- *                                   convert to RGB if necessary
- *   - Gray + alpha -> composite with gray background and remove alpha bytes,
- *                                   convert to RGB if necessary
- *
- *   To support RGB backgrounds for gray images we need:
- *   - Gray + simple transparency -> convert to RGB + simple transparency,
- *                                   compare 3 or 6 bytes and composite with
- *                                   background "in place" if transparent
- *                                   (3x compare/pixel compared to doing
- *                                   composite with gray bkgrnd)
- *   - Gray + alpha -> convert to RGB + alpha, composite with background and
- *                                   remove alpha bytes (3x float
- *                                   operations/pixel compared with composite
- *                                   on gray background)
- *
- *  Greg's change will do this.  The reason it wasn't done before is for
- *  performance, as this increases the per-pixel operations.  If we would check
- *  in advance if the background was gray or RGB, and position the gray-to-RGB
- *  transform appropriately, then it would save a lot of work/time.
- */
-
-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
-   /* If gray -> RGB, do so now only if background is non-gray; else do later
-    * for performance reasons
-    */
-   if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0 &&
-       (png_ptr->mode & PNG_BACKGROUND_IS_GRAY) == 0)
-      png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1);
-#endif
-
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
-   defined(PNG_READ_ALPHA_MODE_SUPPORTED)
-   if ((png_ptr->transformations & PNG_COMPOSE) != 0)
-      png_do_compose(row_info, png_ptr->row_buf + 1, png_ptr);
-#endif
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
-   if ((png_ptr->transformations & PNG_GAMMA) != 0 &&
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-      /* Because RGB_TO_GRAY does the gamma transform. */
-      (png_ptr->transformations & PNG_RGB_TO_GRAY) == 0 &&
-#endif
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
-   defined(PNG_READ_ALPHA_MODE_SUPPORTED)
-      /* Because PNG_COMPOSE does the gamma transform if there is something to
-       * do (if there is an alpha channel or transparency.)
-       */
-       !((png_ptr->transformations & PNG_COMPOSE) != 0 &&
-       ((png_ptr->num_trans != 0) ||
-       (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0)) &&
-#endif
-      /* Because png_init_read_transformations transforms the palette, unless
-       * RGB_TO_GRAY will do the transform.
-       */
-       (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE))
-      png_do_gamma(row_info, png_ptr->row_buf + 1, png_ptr);
-#endif
-
-#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
-   if ((png_ptr->transformations & PNG_STRIP_ALPHA) != 0 &&
-       (png_ptr->transformations & PNG_COMPOSE) != 0 &&
-       (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
-       row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
-      png_do_strip_channel(row_info, png_ptr->row_buf + 1,
-          0 /* at_start == false, because SWAP_ALPHA happens later */);
-#endif
-
-#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
-   if ((png_ptr->transformations & PNG_ENCODE_ALPHA) != 0 &&
-       (row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0)
-      png_do_encode_alpha(row_info, png_ptr->row_buf + 1, png_ptr);
-#endif
-
-#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
-   if ((png_ptr->transformations & PNG_SCALE_16_TO_8) != 0)
-      png_do_scale_16_to_8(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
-   /* There is no harm in doing both of these because only one has any effect,
-    * by putting the 'scale' option first if the app asks for scale (either by
-    * calling the API or in a TRANSFORM flag) this is what happens.
-    */
-   if ((png_ptr->transformations & PNG_16_TO_8) != 0)
-      png_do_chop(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_QUANTIZE_SUPPORTED
-   if ((png_ptr->transformations & PNG_QUANTIZE) != 0)
-   {
-      png_do_quantize(row_info, png_ptr->row_buf + 1,
-          png_ptr->palette_lookup, png_ptr->quantize_index);
-
-      if (row_info->rowbytes == 0)
-         png_error(png_ptr, "png_do_quantize returned rowbytes=0");
-   }
-#endif /* READ_QUANTIZE */
-
-#ifdef PNG_READ_EXPAND_16_SUPPORTED
-   /* Do the expansion now, after all the arithmetic has been done.  Notice
-    * that previous transformations can handle the PNG_EXPAND_16 flag if this
-    * is efficient (particularly true in the case of gamma correction, where
-    * better accuracy results faster!)
-    */
-   if ((png_ptr->transformations & PNG_EXPAND_16) != 0)
-      png_do_expand_16(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
-   /* NOTE: moved here in 1.5.4 (from much later in this list.) */
-   if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0 &&
-       (png_ptr->mode & PNG_BACKGROUND_IS_GRAY) != 0)
-      png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_INVERT_SUPPORTED
-   if ((png_ptr->transformations & PNG_INVERT_MONO) != 0)
-      png_do_invert(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
-   if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0)
-      png_do_read_invert_alpha(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_SHIFT_SUPPORTED
-   if ((png_ptr->transformations & PNG_SHIFT) != 0)
-      png_do_unshift(row_info, png_ptr->row_buf + 1,
-          &(png_ptr->shift));
-#endif
-
-#ifdef PNG_READ_PACK_SUPPORTED
-   if ((png_ptr->transformations & PNG_PACK) != 0)
-      png_do_unpack(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
-   /* Added at libpng-1.5.10 */
-   if (row_info->color_type == PNG_COLOR_TYPE_PALETTE &&
-       png_ptr->num_palette_max >= 0)
-      png_do_check_palette_indexes(png_ptr, row_info);
-#endif
-
-#ifdef PNG_READ_BGR_SUPPORTED
-   if ((png_ptr->transformations & PNG_BGR) != 0)
-      png_do_bgr(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_PACKSWAP_SUPPORTED
-   if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
-      png_do_packswap(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_FILLER_SUPPORTED
-   if ((png_ptr->transformations & PNG_FILLER) != 0)
-      png_do_read_filler(row_info, png_ptr->row_buf + 1,
-          (png_uint_32)png_ptr->filler, png_ptr->flags);
-#endif
-
-#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
-   if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0)
-      png_do_read_swap_alpha(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_16BIT_SUPPORTED
-#ifdef PNG_READ_SWAP_SUPPORTED
-   if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0)
-      png_do_swap(row_info, png_ptr->row_buf + 1);
-#endif
-#endif
-
-#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
-   if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0)
-   {
-      if (png_ptr->read_user_transform_fn != NULL)
-         (*(png_ptr->read_user_transform_fn)) /* User read transform function */
-             (png_ptr,     /* png_ptr */
-             row_info,     /* row_info: */
-                /*  png_uint_32 width;       width of row */
-                /*  size_t rowbytes;         number of bytes in row */
-                /*  png_byte color_type;     color type of pixels */
-                /*  png_byte bit_depth;      bit depth of samples */
-                /*  png_byte channels;       number of channels (1-4) */
-                /*  png_byte pixel_depth;    bits per pixel (depth*channels) */
-             png_ptr->row_buf + 1);    /* start of pixel data for row */
-#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
-      if (png_ptr->user_transform_depth != 0)
-         row_info->bit_depth = png_ptr->user_transform_depth;
-
-      if (png_ptr->user_transform_channels != 0)
-         row_info->channels = png_ptr->user_transform_channels;
-#endif
-      row_info->pixel_depth = (png_byte)(row_info->bit_depth *
-          row_info->channels);
-
-      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_info->width);
-   }
-#endif
-}
-
-#endif /* READ_TRANSFORMS */
-#endif /* READ */
diff --git a/third_party/libpng16/pngrutil.c b/third_party/libpng16/pngrutil.c
deleted file mode 100644
index 3007414..0000000
--- a/third_party/libpng16/pngrutil.c
+++ /dev/null
@@ -1,4681 +0,0 @@
-
-/* pngrutil.c - utilities to read a PNG file
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- *
- * This file contains routines that are only called from within
- * libpng itself during the course of reading an image.
- */
-
-#include "pngpriv.h"
-
-#ifdef PNG_READ_SUPPORTED
-
-png_uint_32 PNGAPI
-png_get_uint_31(png_const_structrp png_ptr, png_const_bytep buf)
-{
-   png_uint_32 uval = png_get_uint_32(buf);
-
-   if (uval > PNG_UINT_31_MAX)
-      png_error(png_ptr, "PNG unsigned integer out of range");
-
-   return (uval);
-}
-
-#if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_READ_cHRM_SUPPORTED)
-/* The following is a variation on the above for use with the fixed
- * point values used for gAMA and cHRM.  Instead of png_error it
- * issues a warning and returns (-1) - an invalid value because both
- * gAMA and cHRM use *unsigned* integers for fixed point values.
- */
-#define PNG_FIXED_ERROR (-1)
-
-static png_fixed_point /* PRIVATE */
-png_get_fixed_point(png_structrp png_ptr, png_const_bytep buf)
-{
-   png_uint_32 uval = png_get_uint_32(buf);
-
-   if (uval <= PNG_UINT_31_MAX)
-      return (png_fixed_point)uval; /* known to be in range */
-
-   /* The caller can turn off the warning by passing NULL. */
-   if (png_ptr != NULL)
-      png_warning(png_ptr, "PNG fixed point integer out of range");
-
-   return PNG_FIXED_ERROR;
-}
-#endif
-
-#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED
-/* NOTE: the read macros will obscure these definitions, so that if
- * PNG_USE_READ_MACROS is set the library will not use them internally,
- * but the APIs will still be available externally.
- *
- * The parentheses around "PNGAPI function_name" in the following three
- * functions are necessary because they allow the macros to co-exist with
- * these (unused but exported) functions.
- */
-
-/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */
-png_uint_32 (PNGAPI
-png_get_uint_32)(png_const_bytep buf)
-{
-   png_uint_32 uval =
-       ((png_uint_32)(*(buf    )) << 24) +
-       ((png_uint_32)(*(buf + 1)) << 16) +
-       ((png_uint_32)(*(buf + 2)) <<  8) +
-       ((png_uint_32)(*(buf + 3))      ) ;
-
-   return uval;
-}
-
-/* Grab a signed 32-bit integer from a buffer in big-endian format.  The
- * data is stored in the PNG file in two's complement format and there
- * is no guarantee that a 'png_int_32' is exactly 32 bits, therefore
- * the following code does a two's complement to native conversion.
- */
-png_int_32 (PNGAPI
-png_get_int_32)(png_const_bytep buf)
-{
-   png_uint_32 uval = png_get_uint_32(buf);
-   if ((uval & 0x80000000) == 0) /* non-negative */
-      return (png_int_32)uval;
-
-   uval = (uval ^ 0xffffffff) + 1;  /* 2's complement: -x = ~x+1 */
-   if ((uval & 0x80000000) == 0) /* no overflow */
-      return -(png_int_32)uval;
-   /* The following has to be safe; this function only gets called on PNG data
-    * and if we get here that data is invalid.  0 is the most safe value and
-    * if not then an attacker would surely just generate a PNG with 0 instead.
-    */
-   return 0;
-}
-
-/* Grab an unsigned 16-bit integer from a buffer in big-endian format. */
-png_uint_16 (PNGAPI
-png_get_uint_16)(png_const_bytep buf)
-{
-   /* ANSI-C requires an int value to accommodate at least 16 bits so this
-    * works and allows the compiler not to worry about possible narrowing
-    * on 32-bit systems.  (Pre-ANSI systems did not make integers smaller
-    * than 16 bits either.)
-    */
-   unsigned int val =
-       ((unsigned int)(*buf) << 8) +
-       ((unsigned int)(*(buf + 1)));
-
-   return (png_uint_16)val;
-}
-
-#endif /* READ_INT_FUNCTIONS */
-
-/* Read and check the PNG file signature */
-void /* PRIVATE */
-png_read_sig(png_structrp png_ptr, png_inforp info_ptr)
-{
-   size_t num_checked, num_to_check;
-
-   /* Exit if the user application does not expect a signature. */
-   if (png_ptr->sig_bytes >= 8)
-      return;
-
-   num_checked = png_ptr->sig_bytes;
-   num_to_check = 8 - num_checked;
-
-#ifdef PNG_IO_STATE_SUPPORTED
-   png_ptr->io_state = PNG_IO_READING | PNG_IO_SIGNATURE;
-#endif
-
-   /* The signature must be serialized in a single I/O call. */
-   png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check);
-   png_ptr->sig_bytes = 8;
-
-   if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check) != 0)
-   {
-      if (num_checked < 4 &&
-          png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4))
-         png_error(png_ptr, "Not a PNG file");
-      else
-         png_error(png_ptr, "PNG file corrupted by ASCII conversion");
-   }
-   if (num_checked < 3)
-      png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;
-}
-
-/* Read the chunk header (length + type name).
- * Put the type name into png_ptr->chunk_name, and return the length.
- */
-png_uint_32 /* PRIVATE */
-png_read_chunk_header(png_structrp png_ptr)
-{
-   png_byte buf[8];
-   png_uint_32 length;
-
-#ifdef PNG_IO_STATE_SUPPORTED
-   png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_HDR;
-#endif
-
-   /* Read the length and the chunk name.
-    * This must be performed in a single I/O call.
-    */
-   png_read_data(png_ptr, buf, 8);
-   length = png_get_uint_31(png_ptr, buf);
-
-   /* Put the chunk name into png_ptr->chunk_name. */
-   png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(buf+4);
-
-   png_debug2(0, "Reading %lx chunk, length = %lu",
-       (unsigned long)png_ptr->chunk_name, (unsigned long)length);
-
-   /* Reset the crc and run it over the chunk name. */
-   png_reset_crc(png_ptr);
-   png_calculate_crc(png_ptr, buf + 4, 4);
-
-   /* Check to see if chunk name is valid. */
-   png_check_chunk_name(png_ptr, png_ptr->chunk_name);
-
-   /* Check for too-large chunk length */
-   png_check_chunk_length(png_ptr, length);
-
-#ifdef PNG_IO_STATE_SUPPORTED
-   png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA;
-#endif
-
-   return length;
-}
-
-/* Read data, and (optionally) run it through the CRC. */
-void /* PRIVATE */
-png_crc_read(png_structrp png_ptr, png_bytep buf, png_uint_32 length)
-{
-   if (png_ptr == NULL)
-      return;
-
-   png_read_data(png_ptr, buf, length);
-   png_calculate_crc(png_ptr, buf, length);
-}
-
-/* Optionally skip data and then check the CRC.  Depending on whether we
- * are reading an ancillary or critical chunk, and how the program has set
- * things up, we may calculate the CRC on the data and print a message.
- * Returns '1' if there was a CRC error, '0' otherwise.
- */
-int /* PRIVATE */
-png_crc_finish(png_structrp png_ptr, png_uint_32 skip)
-{
-   /* The size of the local buffer for inflate is a good guess as to a
-    * reasonable size to use for buffering reads from the application.
-    */
-   while (skip > 0)
-   {
-      png_uint_32 len;
-      png_byte tmpbuf[PNG_INFLATE_BUF_SIZE];
-
-      len = (sizeof tmpbuf);
-      if (len > skip)
-         len = skip;
-      skip -= len;
-
-      png_crc_read(png_ptr, tmpbuf, len);
-   }
-
-   if (png_crc_error(png_ptr) != 0)
-   {
-      if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0 ?
-          (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) == 0 :
-          (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE) != 0)
-      {
-         png_chunk_warning(png_ptr, "CRC error");
-      }
-
-      else
-         png_chunk_error(png_ptr, "CRC error");
-
-      return (1);
-   }
-
-   return (0);
-}
-
-/* Compare the CRC stored in the PNG file with that calculated by libpng from
- * the data it has read thus far.
- */
-int /* PRIVATE */
-png_crc_error(png_structrp png_ptr)
-{
-   png_byte crc_bytes[4];
-   png_uint_32 crc;
-   int need_crc = 1;
-
-   if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0)
-   {
-      if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
-          (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
-         need_crc = 0;
-   }
-
-   else /* critical */
-   {
-      if ((png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) != 0)
-         need_crc = 0;
-   }
-
-#ifdef PNG_IO_STATE_SUPPORTED
-   png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC;
-#endif
-
-   /* The chunk CRC must be serialized in a single I/O call. */
-   png_read_data(png_ptr, crc_bytes, 4);
-
-   if (need_crc != 0)
-   {
-      crc = png_get_uint_32(crc_bytes);
-      return ((int)(crc != png_ptr->crc));
-   }
-
-   else
-      return (0);
-}
-
-#if defined(PNG_READ_iCCP_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) ||\
-    defined(PNG_READ_pCAL_SUPPORTED) || defined(PNG_READ_sCAL_SUPPORTED) ||\
-    defined(PNG_READ_sPLT_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) ||\
-    defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_SEQUENTIAL_READ_SUPPORTED)
-/* Manage the read buffer; this simply reallocates the buffer if it is not small
- * enough (or if it is not allocated).  The routine returns a pointer to the
- * buffer; if an error occurs and 'warn' is set the routine returns NULL, else
- * it will call png_error (via png_malloc) on failure.  (warn == 2 means
- * 'silent').
- */
-static png_bytep
-png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn)
-{
-   png_bytep buffer = png_ptr->read_buffer;
-
-   if (buffer != NULL && new_size > png_ptr->read_buffer_size)
-   {
-      png_ptr->read_buffer = NULL;
-      png_ptr->read_buffer = NULL;
-      png_ptr->read_buffer_size = 0;
-      png_free(png_ptr, buffer);
-      buffer = NULL;
-   }
-
-   if (buffer == NULL)
-   {
-      buffer = png_voidcast(png_bytep, png_malloc_base(png_ptr, new_size));
-
-      if (buffer != NULL)
-      {
-         memset(buffer, 0, new_size); /* just in case */
-         png_ptr->read_buffer = buffer;
-         png_ptr->read_buffer_size = new_size;
-      }
-
-      else if (warn < 2) /* else silent */
-      {
-         if (warn != 0)
-             png_chunk_warning(png_ptr, "insufficient memory to read chunk");
-
-         else
-             png_chunk_error(png_ptr, "insufficient memory to read chunk");
-      }
-   }
-
-   return buffer;
-}
-#endif /* READ_iCCP|iTXt|pCAL|sCAL|sPLT|tEXt|zTXt|SEQUENTIAL_READ */
-
-/* png_inflate_claim: claim the zstream for some nefarious purpose that involves
- * decompression.  Returns Z_OK on success, else a zlib error code.  It checks
- * the owner but, in final release builds, just issues a warning if some other
- * chunk apparently owns the stream.  Prior to release it does a png_error.
- */
-static int
-png_inflate_claim(png_structrp png_ptr, png_uint_32 owner)
-{
-   if (png_ptr->zowner != 0)
-   {
-      char msg[64];
-
-      PNG_STRING_FROM_CHUNK(msg, png_ptr->zowner);
-      /* So the message that results is "<chunk> using zstream"; this is an
-       * internal error, but is very useful for debugging.  i18n requirements
-       * are minimal.
-       */
-      (void)png_safecat(msg, (sizeof msg), 4, " using zstream");
-#if PNG_RELEASE_BUILD
-      png_chunk_warning(png_ptr, msg);
-      png_ptr->zowner = 0;
-#else
-      png_chunk_error(png_ptr, msg);
-#endif
-   }
-
-   /* Implementation note: unlike 'png_deflate_claim' this internal function
-    * does not take the size of the data as an argument.  Some efficiency could
-    * be gained by using this when it is known *if* the zlib stream itself does
-    * not record the number; however, this is an illusion: the original writer
-    * of the PNG may have selected a lower window size, and we really must
-    * follow that because, for systems with with limited capabilities, we
-    * would otherwise reject the application's attempts to use a smaller window
-    * size (zlib doesn't have an interface to say "this or lower"!).
-    *
-    * inflateReset2 was added to zlib 1.2.4; before this the window could not be
-    * reset, therefore it is necessary to always allocate the maximum window
-    * size with earlier zlibs just in case later compressed chunks need it.
-    */
-   {
-      int ret; /* zlib return code */
-#if ZLIB_VERNUM >= 0x1240
-      int window_bits = 0;
-
-# if defined(PNG_SET_OPTION_SUPPORTED) && defined(PNG_MAXIMUM_INFLATE_WINDOW)
-      if (((png_ptr->options >> PNG_MAXIMUM_INFLATE_WINDOW) & 3) ==
-          PNG_OPTION_ON)
-      {
-         window_bits = 15;
-         png_ptr->zstream_start = 0; /* fixed window size */
-      }
-
-      else
-      {
-         png_ptr->zstream_start = 1;
-      }
-# endif
-
-#endif /* ZLIB_VERNUM >= 0x1240 */
-
-      /* Set this for safety, just in case the previous owner left pointers to
-       * memory allocations.
-       */
-      png_ptr->zstream.next_in = NULL;
-      png_ptr->zstream.avail_in = 0;
-      png_ptr->zstream.next_out = NULL;
-      png_ptr->zstream.avail_out = 0;
-
-      if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0)
-      {
-#if ZLIB_VERNUM >= 0x1240
-         ret = inflateReset2(&png_ptr->zstream, window_bits);
-#else
-         ret = inflateReset(&png_ptr->zstream);
-#endif
-      }
-
-      else
-      {
-#if ZLIB_VERNUM >= 0x1240
-         ret = inflateInit2(&png_ptr->zstream, window_bits);
-#else
-         ret = inflateInit(&png_ptr->zstream);
-#endif
-
-         if (ret == Z_OK)
-            png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED;
-      }
-
-#if ZLIB_VERNUM >= 0x1290 && \
-   defined(PNG_SET_OPTION_SUPPORTED) && defined(PNG_IGNORE_ADLER32)
-      if (((png_ptr->options >> PNG_IGNORE_ADLER32) & 3) == PNG_OPTION_ON)
-         /* Turn off validation of the ADLER32 checksum in IDAT chunks */
-         ret = inflateValidate(&png_ptr->zstream, 0);
-#endif
-
-      if (ret == Z_OK)
-         png_ptr->zowner = owner;
-
-      else
-         png_zstream_error(png_ptr, ret);
-
-      return ret;
-   }
-
-#ifdef window_bits
-# undef window_bits
-#endif
-}
-
-#if ZLIB_VERNUM >= 0x1240
-/* Handle the start of the inflate stream if we called inflateInit2(strm,0);
- * in this case some zlib versions skip validation of the CINFO field and, in
- * certain circumstances, libpng may end up displaying an invalid image, in
- * contrast to implementations that call zlib in the normal way (e.g. libpng
- * 1.5).
- */
-int /* PRIVATE */
-png_zlib_inflate(png_structrp png_ptr, int flush)
-{
-   if (png_ptr->zstream_start && png_ptr->zstream.avail_in > 0)
-   {
-      if ((*png_ptr->zstream.next_in >> 4) > 7)
-      {
-         png_ptr->zstream.msg = "invalid window size (libpng)";
-         return Z_DATA_ERROR;
-      }
-
-      png_ptr->zstream_start = 0;
-   }
-
-   return inflate(&png_ptr->zstream, flush);
-}
-#endif /* Zlib >= 1.2.4 */
-
-#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED
-#if defined(PNG_READ_zTXt_SUPPORTED) || defined (PNG_READ_iTXt_SUPPORTED)
-/* png_inflate now returns zlib error codes including Z_OK and Z_STREAM_END to
- * allow the caller to do multiple calls if required.  If the 'finish' flag is
- * set Z_FINISH will be passed to the final inflate() call and Z_STREAM_END must
- * be returned or there has been a problem, otherwise Z_SYNC_FLUSH is used and
- * Z_OK or Z_STREAM_END will be returned on success.
- *
- * The input and output sizes are updated to the actual amounts of data consumed
- * or written, not the amount available (as in a z_stream).  The data pointers
- * are not changed, so the next input is (data+input_size) and the next
- * available output is (output+output_size).
- */
-static int
-png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish,
-    /* INPUT: */ png_const_bytep input, png_uint_32p input_size_ptr,
-    /* OUTPUT: */ png_bytep output, png_alloc_size_t *output_size_ptr)
-{
-   if (png_ptr->zowner == owner) /* Else not claimed */
-   {
-      int ret;
-      png_alloc_size_t avail_out = *output_size_ptr;
-      png_uint_32 avail_in = *input_size_ptr;
-
-      /* zlib can't necessarily handle more than 65535 bytes at once (i.e. it
-       * can't even necessarily handle 65536 bytes) because the type uInt is
-       * "16 bits or more".  Consequently it is necessary to chunk the input to
-       * zlib.  This code uses ZLIB_IO_MAX, from pngpriv.h, as the maximum (the
-       * maximum value that can be stored in a uInt.)  It is possible to set
-       * ZLIB_IO_MAX to a lower value in pngpriv.h and this may sometimes have
-       * a performance advantage, because it reduces the amount of data accessed
-       * at each step and that may give the OS more time to page it in.
-       */
-      png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input);
-      /* avail_in and avail_out are set below from 'size' */
-      png_ptr->zstream.avail_in = 0;
-      png_ptr->zstream.avail_out = 0;
-
-      /* Read directly into the output if it is available (this is set to
-       * a local buffer below if output is NULL).
-       */
-      if (output != NULL)
-         png_ptr->zstream.next_out = output;
-
-      do
-      {
-         uInt avail;
-         Byte local_buffer[PNG_INFLATE_BUF_SIZE];
-
-         /* zlib INPUT BUFFER */
-         /* The setting of 'avail_in' used to be outside the loop; by setting it
-          * inside it is possible to chunk the input to zlib and simply rely on
-          * zlib to advance the 'next_in' pointer.  This allows arbitrary
-          * amounts of data to be passed through zlib at the unavoidable cost of
-          * requiring a window save (memcpy of up to 32768 output bytes)
-          * every ZLIB_IO_MAX input bytes.
-          */
-         avail_in += png_ptr->zstream.avail_in; /* not consumed last time */
-
-         avail = ZLIB_IO_MAX;
-
-         if (avail_in < avail)
-            avail = (uInt)avail_in; /* safe: < than ZLIB_IO_MAX */
-
-         avail_in -= avail;
-         png_ptr->zstream.avail_in = avail;
-
-         /* zlib OUTPUT BUFFER */
-         avail_out += png_ptr->zstream.avail_out; /* not written last time */
-
-         avail = ZLIB_IO_MAX; /* maximum zlib can process */
-
-         if (output == NULL)
-         {
-            /* Reset the output buffer each time round if output is NULL and
-             * make available the full buffer, up to 'remaining_space'
-             */
-            png_ptr->zstream.next_out = local_buffer;
-            if ((sizeof local_buffer) < avail)
-               avail = (sizeof local_buffer);
-         }
-
-         if (avail_out < avail)
-            avail = (uInt)avail_out; /* safe: < ZLIB_IO_MAX */
-
-         png_ptr->zstream.avail_out = avail;
-         avail_out -= avail;
-
-         /* zlib inflate call */
-         /* In fact 'avail_out' may be 0 at this point, that happens at the end
-          * of the read when the final LZ end code was not passed at the end of
-          * the previous chunk of input data.  Tell zlib if we have reached the
-          * end of the output buffer.
-          */
-         ret = PNG_INFLATE(png_ptr, avail_out > 0 ? Z_NO_FLUSH :
-             (finish ? Z_FINISH : Z_SYNC_FLUSH));
-      } while (ret == Z_OK);
-
-      /* For safety kill the local buffer pointer now */
-      if (output == NULL)
-         png_ptr->zstream.next_out = NULL;
-
-      /* Claw back the 'size' and 'remaining_space' byte counts. */
-      avail_in += png_ptr->zstream.avail_in;
-      avail_out += png_ptr->zstream.avail_out;
-
-      /* Update the input and output sizes; the updated values are the amount
-       * consumed or written, effectively the inverse of what zlib uses.
-       */
-      if (avail_out > 0)
-         *output_size_ptr -= avail_out;
-
-      if (avail_in > 0)
-         *input_size_ptr -= avail_in;
-
-      /* Ensure png_ptr->zstream.msg is set (even in the success case!) */
-      png_zstream_error(png_ptr, ret);
-      return ret;
-   }
-
-   else
-   {
-      /* This is a bad internal error.  The recovery assigns to the zstream msg
-       * pointer, which is not owned by the caller, but this is safe; it's only
-       * used on errors!
-       */
-      png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed");
-      return Z_STREAM_ERROR;
-   }
-}
-
-/*
- * Decompress trailing data in a chunk.  The assumption is that read_buffer
- * points at an allocated area holding the contents of a chunk with a
- * trailing compressed part.  What we get back is an allocated area
- * holding the original prefix part and an uncompressed version of the
- * trailing part (the malloc area passed in is freed).
- */
-static int
-png_decompress_chunk(png_structrp png_ptr,
-    png_uint_32 chunklength, png_uint_32 prefix_size,
-    png_alloc_size_t *newlength /* must be initialized to the maximum! */,
-    int terminate /*add a '\0' to the end of the uncompressed data*/)
-{
-   /* TODO: implement different limits for different types of chunk.
-    *
-    * The caller supplies *newlength set to the maximum length of the
-    * uncompressed data, but this routine allocates space for the prefix and
-    * maybe a '\0' terminator too.  We have to assume that 'prefix_size' is
-    * limited only by the maximum chunk size.
-    */
-   png_alloc_size_t limit = PNG_SIZE_MAX;
-
-# ifdef PNG_SET_USER_LIMITS_SUPPORTED
-   if (png_ptr->user_chunk_malloc_max > 0 &&
-       png_ptr->user_chunk_malloc_max < limit)
-      limit = png_ptr->user_chunk_malloc_max;
-# elif PNG_USER_CHUNK_MALLOC_MAX > 0
-   if (PNG_USER_CHUNK_MALLOC_MAX < limit)
-      limit = PNG_USER_CHUNK_MALLOC_MAX;
-# endif
-
-   if (limit >= prefix_size + (terminate != 0))
-   {
-      int ret;
-
-      limit -= prefix_size + (terminate != 0);
-
-      if (limit < *newlength)
-         *newlength = limit;
-
-      /* Now try to claim the stream. */
-      ret = png_inflate_claim(png_ptr, png_ptr->chunk_name);
-
-      if (ret == Z_OK)
-      {
-         png_uint_32 lzsize = chunklength - prefix_size;
-
-         ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/,
-             /* input: */ png_ptr->read_buffer + prefix_size, &lzsize,
-             /* output: */ NULL, newlength);
-
-         if (ret == Z_STREAM_END)
-         {
-            /* Use 'inflateReset' here, not 'inflateReset2' because this
-             * preserves the previously decided window size (otherwise it would
-             * be necessary to store the previous window size.)  In practice
-             * this doesn't matter anyway, because png_inflate will call inflate
-             * with Z_FINISH in almost all cases, so the window will not be
-             * maintained.
-             */
-            if (inflateReset(&png_ptr->zstream) == Z_OK)
-            {
-               /* Because of the limit checks above we know that the new,
-                * expanded, size will fit in a size_t (let alone an
-                * png_alloc_size_t).  Use png_malloc_base here to avoid an
-                * extra OOM message.
-                */
-               png_alloc_size_t new_size = *newlength;
-               png_alloc_size_t buffer_size = prefix_size + new_size +
-                   (terminate != 0);
-               png_bytep text = png_voidcast(png_bytep, png_malloc_base(png_ptr,
-                   buffer_size));
-
-               if (text != NULL)
-               {
-                  memset(text, 0, buffer_size);
-
-                  ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/,
-                      png_ptr->read_buffer + prefix_size, &lzsize,
-                      text + prefix_size, newlength);
-
-                  if (ret == Z_STREAM_END)
-                  {
-                     if (new_size == *newlength)
-                     {
-                        if (terminate != 0)
-                           text[prefix_size + *newlength] = 0;
-
-                        if (prefix_size > 0)
-                           memcpy(text, png_ptr->read_buffer, prefix_size);
-
-                        {
-                           png_bytep old_ptr = png_ptr->read_buffer;
-
-                           png_ptr->read_buffer = text;
-                           png_ptr->read_buffer_size = buffer_size;
-                           text = old_ptr; /* freed below */
-                        }
-                     }
-
-                     else
-                     {
-                        /* The size changed on the second read, there can be no
-                         * guarantee that anything is correct at this point.
-                         * The 'msg' pointer has been set to "unexpected end of
-                         * LZ stream", which is fine, but return an error code
-                         * that the caller won't accept.
-                         */
-                        ret = PNG_UNEXPECTED_ZLIB_RETURN;
-                     }
-                  }
-
-                  else if (ret == Z_OK)
-                     ret = PNG_UNEXPECTED_ZLIB_RETURN; /* for safety */
-
-                  /* Free the text pointer (this is the old read_buffer on
-                   * success)
-                   */
-                  png_free(png_ptr, text);
-
-                  /* This really is very benign, but it's still an error because
-                   * the extra space may otherwise be used as a Trojan Horse.
-                   */
-                  if (ret == Z_STREAM_END &&
-                      chunklength - prefix_size != lzsize)
-                     png_chunk_benign_error(png_ptr, "extra compressed data");
-               }
-
-               else
-               {
-                  /* Out of memory allocating the buffer */
-                  ret = Z_MEM_ERROR;
-                  png_zstream_error(png_ptr, Z_MEM_ERROR);
-               }
-            }
-
-            else
-            {
-               /* inflateReset failed, store the error message */
-               png_zstream_error(png_ptr, ret);
-               ret = PNG_UNEXPECTED_ZLIB_RETURN;
-            }
-         }
-
-         else if (ret == Z_OK)
-            ret = PNG_UNEXPECTED_ZLIB_RETURN;
-
-         /* Release the claimed stream */
-         png_ptr->zowner = 0;
-      }
-
-      else /* the claim failed */ if (ret == Z_STREAM_END) /* impossible! */
-         ret = PNG_UNEXPECTED_ZLIB_RETURN;
-
-      return ret;
-   }
-
-   else
-   {
-      /* Application/configuration limits exceeded */
-      png_zstream_error(png_ptr, Z_MEM_ERROR);
-      return Z_MEM_ERROR;
-   }
-}
-#endif /* READ_zTXt || READ_iTXt */
-#endif /* READ_COMPRESSED_TEXT */
-
-#ifdef PNG_READ_iCCP_SUPPORTED
-/* Perform a partial read and decompress, producing 'avail_out' bytes and
- * reading from the current chunk as required.
- */
-static int
-png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size,
-    png_uint_32p chunk_bytes, png_bytep next_out, png_alloc_size_t *out_size,
-    int finish)
-{
-   if (png_ptr->zowner == png_ptr->chunk_name)
-   {
-      int ret;
-
-      /* next_in and avail_in must have been initialized by the caller. */
-      png_ptr->zstream.next_out = next_out;
-      png_ptr->zstream.avail_out = 0; /* set in the loop */
-
-      do
-      {
-         if (png_ptr->zstream.avail_in == 0)
-         {
-            if (read_size > *chunk_bytes)
-               read_size = (uInt)*chunk_bytes;
-            *chunk_bytes -= read_size;
-
-            if (read_size > 0)
-               png_crc_read(png_ptr, read_buffer, read_size);
-
-            png_ptr->zstream.next_in = read_buffer;
-            png_ptr->zstream.avail_in = read_size;
-         }
-
-         if (png_ptr->zstream.avail_out == 0)
-         {
-            uInt avail = ZLIB_IO_MAX;
-            if (avail > *out_size)
-               avail = (uInt)*out_size;
-            *out_size -= avail;
-
-            png_ptr->zstream.avail_out = avail;
-         }
-
-         /* Use Z_SYNC_FLUSH when there is no more chunk data to ensure that all
-          * the available output is produced; this allows reading of truncated
-          * streams.
-          */
-         ret = PNG_INFLATE(png_ptr, *chunk_bytes > 0 ?
-             Z_NO_FLUSH : (finish ? Z_FINISH : Z_SYNC_FLUSH));
-      }
-      while (ret == Z_OK && (*out_size > 0 || png_ptr->zstream.avail_out > 0));
-
-      *out_size += png_ptr->zstream.avail_out;
-      png_ptr->zstream.avail_out = 0; /* Should not be required, but is safe */
-
-      /* Ensure the error message pointer is always set: */
-      png_zstream_error(png_ptr, ret);
-      return ret;
-   }
-
-   else
-   {
-      png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed");
-      return Z_STREAM_ERROR;
-   }
-}
-#endif /* READ_iCCP */
-
-/* Read and check the IDHR chunk */
-
-void /* PRIVATE */
-png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   png_byte buf[13];
-   png_uint_32 width, height;
-   int bit_depth, color_type, compression_type, filter_type;
-   int interlace_type;
-
-   png_debug(1, "in png_handle_IHDR");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) != 0)
-      png_chunk_error(png_ptr, "out of place");
-
-   /* Check the length */
-   if (length != 13)
-      png_chunk_error(png_ptr, "invalid");
-
-   png_ptr->mode |= PNG_HAVE_IHDR;
-
-   png_crc_read(png_ptr, buf, 13);
-   png_crc_finish(png_ptr, 0);
-
-   width = png_get_uint_31(png_ptr, buf);
-   height = png_get_uint_31(png_ptr, buf + 4);
-   bit_depth = buf[8];
-   color_type = buf[9];
-   compression_type = buf[10];
-   filter_type = buf[11];
-   interlace_type = buf[12];
-
-   /* Set internal variables */
-   png_ptr->width = width;
-   png_ptr->height = height;
-   png_ptr->bit_depth = (png_byte)bit_depth;
-   png_ptr->interlaced = (png_byte)interlace_type;
-   png_ptr->color_type = (png_byte)color_type;
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-   png_ptr->filter_type = (png_byte)filter_type;
-#endif
-   png_ptr->compression_type = (png_byte)compression_type;
-
-   /* Find number of channels */
-   switch (png_ptr->color_type)
-   {
-      default: /* invalid, png_set_IHDR calls png_error */
-      case PNG_COLOR_TYPE_GRAY:
-      case PNG_COLOR_TYPE_PALETTE:
-         png_ptr->channels = 1;
-         break;
-
-      case PNG_COLOR_TYPE_RGB:
-         png_ptr->channels = 3;
-         break;
-
-      case PNG_COLOR_TYPE_GRAY_ALPHA:
-         png_ptr->channels = 2;
-         break;
-
-      case PNG_COLOR_TYPE_RGB_ALPHA:
-         png_ptr->channels = 4;
-         break;
-   }
-
-   /* Set up other useful info */
-   png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * png_ptr->channels);
-   png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->width);
-   png_debug1(3, "bit_depth = %d", png_ptr->bit_depth);
-   png_debug1(3, "channels = %d", png_ptr->channels);
-   png_debug1(3, "rowbytes = %lu", (unsigned long)png_ptr->rowbytes);
-   png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
-       color_type, interlace_type, compression_type, filter_type);
-}
-
-/* Read and check the palette */
-void /* PRIVATE */
-png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   png_color palette[PNG_MAX_PALETTE_LENGTH];
-   int max_palette_length, num, i;
-#ifdef PNG_POINTER_INDEXING_SUPPORTED
-   png_colorp pal_ptr;
-#endif
-
-   png_debug(1, "in png_handle_PLTE");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   /* Moved to before the 'after IDAT' check below because otherwise duplicate
-    * PLTE chunks are potentially ignored (the spec says there shall not be more
-    * than one PLTE, the error is not treated as benign, so this check trumps
-    * the requirement that PLTE appears before IDAT.)
-    */
-   else if ((png_ptr->mode & PNG_HAVE_PLTE) != 0)
-      png_chunk_error(png_ptr, "duplicate");
-
-   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-   {
-      /* This is benign because the non-benign error happened before, when an
-       * IDAT was encountered in a color-mapped image with no PLTE.
-       */
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   png_ptr->mode |= PNG_HAVE_PLTE;
-
-   if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "ignored in grayscale PNG");
-      return;
-   }
-
-#ifndef PNG_READ_OPT_PLTE_SUPPORTED
-   if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
-   {
-      png_crc_finish(png_ptr, length);
-      return;
-   }
-#endif
-
-   if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3)
-   {
-      png_crc_finish(png_ptr, length);
-
-      if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
-         png_chunk_benign_error(png_ptr, "invalid");
-
-      else
-         png_chunk_error(png_ptr, "invalid");
-
-      return;
-   }
-
-   /* The cast is safe because 'length' is less than 3*PNG_MAX_PALETTE_LENGTH */
-   num = (int)length / 3;
-
-   /* If the palette has 256 or fewer entries but is too large for the bit
-    * depth, we don't issue an error, to preserve the behavior of previous
-    * libpng versions. We silently truncate the unused extra palette entries
-    * here.
-    */
-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-      max_palette_length = (1 << png_ptr->bit_depth);
-   else
-      max_palette_length = PNG_MAX_PALETTE_LENGTH;
-
-   if (num > max_palette_length)
-      num = max_palette_length;
-
-#ifdef PNG_POINTER_INDEXING_SUPPORTED
-   for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++)
-   {
-      png_byte buf[3];
-
-      png_crc_read(png_ptr, buf, 3);
-      pal_ptr->red = buf[0];
-      pal_ptr->green = buf[1];
-      pal_ptr->blue = buf[2];
-   }
-#else
-   for (i = 0; i < num; i++)
-   {
-      png_byte buf[3];
-
-      png_crc_read(png_ptr, buf, 3);
-      /* Don't depend upon png_color being any order */
-      palette[i].red = buf[0];
-      palette[i].green = buf[1];
-      palette[i].blue = buf[2];
-   }
-#endif
-
-   /* If we actually need the PLTE chunk (ie for a paletted image), we do
-    * whatever the normal CRC configuration tells us.  However, if we
-    * have an RGB image, the PLTE can be considered ancillary, so
-    * we will act as though it is.
-    */
-#ifndef PNG_READ_OPT_PLTE_SUPPORTED
-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-#endif
-   {
-      png_crc_finish(png_ptr, (png_uint_32) (length - (unsigned int)num * 3));
-   }
-
-#ifndef PNG_READ_OPT_PLTE_SUPPORTED
-   else if (png_crc_error(png_ptr) != 0)  /* Only if we have a CRC error */
-   {
-      /* If we don't want to use the data from an ancillary chunk,
-       * we have two options: an error abort, or a warning and we
-       * ignore the data in this chunk (which should be OK, since
-       * it's considered ancillary for a RGB or RGBA image).
-       *
-       * IMPLEMENTATION NOTE: this is only here because png_crc_finish uses the
-       * chunk type to determine whether to check the ancillary or the critical
-       * flags.
-       */
-      if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE) == 0)
-      {
-         if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) != 0)
-            return;
-
-         else
-            png_chunk_error(png_ptr, "CRC error");
-      }
-
-      /* Otherwise, we (optionally) emit a warning and use the chunk. */
-      else if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) == 0)
-         png_chunk_warning(png_ptr, "CRC error");
-   }
-#endif
-
-   /* TODO: png_set_PLTE has the side effect of setting png_ptr->palette to its
-    * own copy of the palette.  This has the side effect that when png_start_row
-    * is called (this happens after any call to png_read_update_info) the
-    * info_ptr palette gets changed.  This is extremely unexpected and
-    * confusing.
-    *
-    * Fix this by not sharing the palette in this way.
-    */
-   png_set_PLTE(png_ptr, info_ptr, palette, num);
-
-   /* The three chunks, bKGD, hIST and tRNS *must* appear after PLTE and before
-    * IDAT.  Prior to 1.6.0 this was not checked; instead the code merely
-    * checked the apparent validity of a tRNS chunk inserted before PLTE on a
-    * palette PNG.  1.6.0 attempts to rigorously follow the standard and
-    * therefore does a benign error if the erroneous condition is detected *and*
-    * cancels the tRNS if the benign error returns.  The alternative is to
-    * amend the standard since it would be rather hypocritical of the standards
-    * maintainers to ignore it.
-    */
-#ifdef PNG_READ_tRNS_SUPPORTED
-   if (png_ptr->num_trans > 0 ||
-       (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0))
-   {
-      /* Cancel this because otherwise it would be used if the transforms
-       * require it.  Don't cancel the 'valid' flag because this would prevent
-       * detection of duplicate chunks.
-       */
-      png_ptr->num_trans = 0;
-
-      if (info_ptr != NULL)
-         info_ptr->num_trans = 0;
-
-      png_chunk_benign_error(png_ptr, "tRNS must be after");
-   }
-#endif
-
-#ifdef PNG_READ_hIST_SUPPORTED
-   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0)
-      png_chunk_benign_error(png_ptr, "hIST must be after");
-#endif
-
-#ifdef PNG_READ_bKGD_SUPPORTED
-   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0)
-      png_chunk_benign_error(png_ptr, "bKGD must be after");
-#endif
-}
-
-void /* PRIVATE */
-png_handle_IEND(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   png_debug(1, "in png_handle_IEND");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0 ||
-       (png_ptr->mode & PNG_HAVE_IDAT) == 0)
-      png_chunk_error(png_ptr, "out of place");
-
-   png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND);
-
-   png_crc_finish(png_ptr, length);
-
-   if (length != 0)
-      png_chunk_benign_error(png_ptr, "invalid");
-
-   PNG_UNUSED(info_ptr)
-}
-
-#ifdef PNG_READ_gAMA_SUPPORTED
-void /* PRIVATE */
-png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   png_fixed_point igamma;
-   png_byte buf[4];
-
-   png_debug(1, "in png_handle_gAMA");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   if (length != 4)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
-   png_crc_read(png_ptr, buf, 4);
-
-   if (png_crc_finish(png_ptr, 0) != 0)
-      return;
-
-   igamma = png_get_fixed_point(NULL, buf);
-
-   png_colorspace_set_gamma(png_ptr, &png_ptr->colorspace, igamma);
-   png_colorspace_sync(png_ptr, info_ptr);
-}
-#endif
-
-#ifdef PNG_READ_sBIT_SUPPORTED
-void /* PRIVATE */
-png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   unsigned int truelen, i;
-   png_byte sample_depth;
-   png_byte buf[4];
-
-   png_debug(1, "in png_handle_sBIT");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-   {
-      truelen = 3;
-      sample_depth = 8;
-   }
-
-   else
-   {
-      truelen = png_ptr->channels;
-      sample_depth = png_ptr->bit_depth;
-   }
-
-   if (length != truelen || length > 4)
-   {
-      png_chunk_benign_error(png_ptr, "invalid");
-      png_crc_finish(png_ptr, length);
-      return;
-   }
-
-   buf[0] = buf[1] = buf[2] = buf[3] = sample_depth;
-   png_crc_read(png_ptr, buf, truelen);
-
-   if (png_crc_finish(png_ptr, 0) != 0)
-      return;
-
-   for (i=0; i<truelen; ++i)
-   {
-      if (buf[i] == 0 || buf[i] > sample_depth)
-      {
-         png_chunk_benign_error(png_ptr, "invalid");
-         return;
-      }
-   }
-
-   if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
-   {
-      png_ptr->sig_bit.red = buf[0];
-      png_ptr->sig_bit.green = buf[1];
-      png_ptr->sig_bit.blue = buf[2];
-      png_ptr->sig_bit.alpha = buf[3];
-   }
-
-   else
-   {
-      png_ptr->sig_bit.gray = buf[0];
-      png_ptr->sig_bit.red = buf[0];
-      png_ptr->sig_bit.green = buf[0];
-      png_ptr->sig_bit.blue = buf[0];
-      png_ptr->sig_bit.alpha = buf[1];
-   }
-
-   png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit));
-}
-#endif
-
-#ifdef PNG_READ_cHRM_SUPPORTED
-void /* PRIVATE */
-png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   png_byte buf[32];
-   png_xy xy;
-
-   png_debug(1, "in png_handle_cHRM");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   if (length != 32)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
-   png_crc_read(png_ptr, buf, 32);
-
-   if (png_crc_finish(png_ptr, 0) != 0)
-      return;
-
-   xy.whitex = png_get_fixed_point(NULL, buf);
-   xy.whitey = png_get_fixed_point(NULL, buf + 4);
-   xy.redx   = png_get_fixed_point(NULL, buf + 8);
-   xy.redy   = png_get_fixed_point(NULL, buf + 12);
-   xy.greenx = png_get_fixed_point(NULL, buf + 16);
-   xy.greeny = png_get_fixed_point(NULL, buf + 20);
-   xy.bluex  = png_get_fixed_point(NULL, buf + 24);
-   xy.bluey  = png_get_fixed_point(NULL, buf + 28);
-
-   if (xy.whitex == PNG_FIXED_ERROR ||
-       xy.whitey == PNG_FIXED_ERROR ||
-       xy.redx   == PNG_FIXED_ERROR ||
-       xy.redy   == PNG_FIXED_ERROR ||
-       xy.greenx == PNG_FIXED_ERROR ||
-       xy.greeny == PNG_FIXED_ERROR ||
-       xy.bluex  == PNG_FIXED_ERROR ||
-       xy.bluey  == PNG_FIXED_ERROR)
-   {
-      png_chunk_benign_error(png_ptr, "invalid values");
-      return;
-   }
-
-   /* If a colorspace error has already been output skip this chunk */
-   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0)
-      return;
-
-   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) != 0)
-   {
-      png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID;
-      png_colorspace_sync(png_ptr, info_ptr);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   png_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM;
-   (void)png_colorspace_set_chromaticities(png_ptr, &png_ptr->colorspace, &xy,
-       1/*prefer cHRM values*/);
-   png_colorspace_sync(png_ptr, info_ptr);
-}
-#endif
-
-#ifdef PNG_READ_sRGB_SUPPORTED
-void /* PRIVATE */
-png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   png_byte intent;
-
-   png_debug(1, "in png_handle_sRGB");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   if (length != 1)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
-   png_crc_read(png_ptr, &intent, 1);
-
-   if (png_crc_finish(png_ptr, 0) != 0)
-      return;
-
-   /* If a colorspace error has already been output skip this chunk */
-   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0)
-      return;
-
-   /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect
-    * this.
-    */
-   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) != 0)
-   {
-      png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID;
-      png_colorspace_sync(png_ptr, info_ptr);
-      png_chunk_benign_error(png_ptr, "too many profiles");
-      return;
-   }
-
-   (void)png_colorspace_set_sRGB(png_ptr, &png_ptr->colorspace, intent);
-   png_colorspace_sync(png_ptr, info_ptr);
-}
-#endif /* READ_sRGB */
-
-#ifdef PNG_READ_iCCP_SUPPORTED
-void /* PRIVATE */
-png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-/* Note: this does not properly handle profiles that are > 64K under DOS */
-{
-   png_const_charp errmsg = NULL; /* error message output, or no error */
-   int finished = 0; /* crc checked */
-
-   png_debug(1, "in png_handle_iCCP");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   /* Consistent with all the above colorspace handling an obviously *invalid*
-    * chunk is just ignored, so does not invalidate the color space.  An
-    * alternative is to set the 'invalid' flags at the start of this routine
-    * and only clear them in they were not set before and all the tests pass.
-    */
-
-   /* The keyword must be at least one character and there is a
-    * terminator (0) byte and the compression method byte, and the
-    * 'zlib' datastream is at least 11 bytes.
-    */
-   if (length < 14)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "too short");
-      return;
-   }
-
-   /* If a colorspace error has already been output skip this chunk */
-   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      return;
-   }
-
-   /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect
-    * this.
-    */
-   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) == 0)
-   {
-      uInt read_length, keyword_length;
-      char keyword[81];
-
-      /* Find the keyword; the keyword plus separator and compression method
-       * bytes can be at most 81 characters long.
-       */
-      read_length = 81; /* maximum */
-      if (read_length > length)
-         read_length = (uInt)length;
-
-      png_crc_read(png_ptr, (png_bytep)keyword, read_length);
-      length -= read_length;
-
-      /* The minimum 'zlib' stream is assumed to be just the 2 byte header,
-       * 5 bytes minimum 'deflate' stream, and the 4 byte checksum.
-       */
-      if (length < 11)
-      {
-         png_crc_finish(png_ptr, length);
-         png_chunk_benign_error(png_ptr, "too short");
-         return;
-      }
-
-      keyword_length = 0;
-      while (keyword_length < 80 && keyword_length < read_length &&
-         keyword[keyword_length] != 0)
-         ++keyword_length;
-
-      /* TODO: make the keyword checking common */
-      if (keyword_length >= 1 && keyword_length <= 79)
-      {
-         /* We only understand '0' compression - deflate - so if we get a
-          * different value we can't safely decode the chunk.
-          */
-         if (keyword_length+1 < read_length &&
-            keyword[keyword_length+1] == PNG_COMPRESSION_TYPE_BASE)
-         {
-            read_length -= keyword_length+2;
-
-            if (png_inflate_claim(png_ptr, png_iCCP) == Z_OK)
-            {
-               Byte profile_header[132]={0};
-               Byte local_buffer[PNG_INFLATE_BUF_SIZE];
-               png_alloc_size_t size = (sizeof profile_header);
-
-               png_ptr->zstream.next_in = (Bytef*)keyword + (keyword_length+2);
-               png_ptr->zstream.avail_in = read_length;
-               (void)png_inflate_read(png_ptr, local_buffer,
-                   (sizeof local_buffer), &length, profile_header, &size,
-                   0/*finish: don't, because the output is too small*/);
-
-               if (size == 0)
-               {
-                  /* We have the ICC profile header; do the basic header checks.
-                   */
-                  png_uint_32 profile_length = png_get_uint_32(profile_header);
-
-                  if (png_icc_check_length(png_ptr, &png_ptr->colorspace,
-                      keyword, profile_length) != 0)
-                  {
-                     /* The length is apparently ok, so we can check the 132
-                      * byte header.
-                      */
-                     if (png_icc_check_header(png_ptr, &png_ptr->colorspace,
-                         keyword, profile_length, profile_header,
-                         png_ptr->color_type) != 0)
-                     {
-                        /* Now read the tag table; a variable size buffer is
-                         * needed at this point, allocate one for the whole
-                         * profile.  The header check has already validated
-                         * that none of this stuff will overflow.
-                         */
-                        png_uint_32 tag_count =
-                           png_get_uint_32(profile_header + 128);
-                        png_bytep profile = png_read_buffer(png_ptr,
-                            profile_length, 2/*silent*/);
-
-                        if (profile != NULL)
-                        {
-                           memcpy(profile, profile_header,
-                               (sizeof profile_header));
-
-                           size = 12 * tag_count;
-
-                           (void)png_inflate_read(png_ptr, local_buffer,
-                               (sizeof local_buffer), &length,
-                               profile + (sizeof profile_header), &size, 0);
-
-                           /* Still expect a buffer error because we expect
-                            * there to be some tag data!
-                            */
-                           if (size == 0)
-                           {
-                              if (png_icc_check_tag_table(png_ptr,
-                                  &png_ptr->colorspace, keyword, profile_length,
-                                  profile) != 0)
-                              {
-                                 /* The profile has been validated for basic
-                                  * security issues, so read the whole thing in.
-                                  */
-                                 size = profile_length - (sizeof profile_header)
-                                     - 12 * tag_count;
-
-                                 (void)png_inflate_read(png_ptr, local_buffer,
-                                     (sizeof local_buffer), &length,
-                                     profile + (sizeof profile_header) +
-                                     12 * tag_count, &size, 1/*finish*/);
-
-                                 if (length > 0 && !(png_ptr->flags &
-                                     PNG_FLAG_BENIGN_ERRORS_WARN))
-                                    errmsg = "extra compressed data";
-
-                                 /* But otherwise allow extra data: */
-                                 else if (size == 0)
-                                 {
-                                    if (length > 0)
-                                    {
-                                       /* This can be handled completely, so
-                                        * keep going.
-                                        */
-                                       png_chunk_warning(png_ptr,
-                                           "extra compressed data");
-                                    }
-
-                                    png_crc_finish(png_ptr, length);
-                                    finished = 1;
-
-# if defined(PNG_sRGB_SUPPORTED) && PNG_sRGB_PROFILE_CHECKS >= 0
-                                    /* Check for a match against sRGB */
-                                    png_icc_set_sRGB(png_ptr,
-                                        &png_ptr->colorspace, profile,
-                                        png_ptr->zstream.adler);
-# endif
-
-                                    /* Steal the profile for info_ptr. */
-                                    if (info_ptr != NULL)
-                                    {
-                                       png_free_data(png_ptr, info_ptr,
-                                           PNG_FREE_ICCP, 0);
-
-                                       info_ptr->iccp_name = png_voidcast(char*,
-                                           png_malloc_base(png_ptr,
-                                           keyword_length+1));
-                                       if (info_ptr->iccp_name != NULL)
-                                       {
-                                          memcpy(info_ptr->iccp_name, keyword,
-                                              keyword_length+1);
-                                          info_ptr->iccp_proflen =
-                                              profile_length;
-                                          info_ptr->iccp_profile = profile;
-                                          png_ptr->read_buffer = NULL; /*steal*/
-                                          info_ptr->free_me |= PNG_FREE_ICCP;
-                                          info_ptr->valid |= PNG_INFO_iCCP;
-                                       }
-
-                                       else
-                                       {
-                                          png_ptr->colorspace.flags |=
-                                             PNG_COLORSPACE_INVALID;
-                                          errmsg = "out of memory";
-                                       }
-                                    }
-
-                                    /* else the profile remains in the read
-                                     * buffer which gets reused for subsequent
-                                     * chunks.
-                                     */
-
-                                    if (info_ptr != NULL)
-                                       png_colorspace_sync(png_ptr, info_ptr);
-
-                                    if (errmsg == NULL)
-                                    {
-                                       png_ptr->zowner = 0;
-                                       return;
-                                    }
-                                 }
-                                 if (errmsg == NULL)
-                                    errmsg = png_ptr->zstream.msg;
-                              }
-                              /* else png_icc_check_tag_table output an error */
-                           }
-                           else /* profile truncated */
-                              errmsg = png_ptr->zstream.msg;
-                        }
-
-                        else
-                           errmsg = "out of memory";
-                     }
-
-                     /* else png_icc_check_header output an error */
-                  }
-
-                  /* else png_icc_check_length output an error */
-               }
-
-               else /* profile truncated */
-                  errmsg = png_ptr->zstream.msg;
-
-               /* Release the stream */
-               png_ptr->zowner = 0;
-            }
-
-            else /* png_inflate_claim failed */
-               errmsg = png_ptr->zstream.msg;
-         }
-
-         else
-            errmsg = "bad compression method"; /* or missing */
-      }
-
-      else
-         errmsg = "bad keyword";
-   }
-
-   else
-      errmsg = "too many profiles";
-
-   /* Failure: the reason is in 'errmsg' */
-   if (finished == 0)
-      png_crc_finish(png_ptr, length);
-
-   png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID;
-   png_colorspace_sync(png_ptr, info_ptr);
-   if (errmsg != NULL) /* else already output */
-      png_chunk_benign_error(png_ptr, errmsg);
-}
-#endif /* READ_iCCP */
-
-#ifdef PNG_READ_sPLT_SUPPORTED
-void /* PRIVATE */
-png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-/* Note: this does not properly handle chunks that are > 64K under DOS */
-{
-   png_bytep entry_start, buffer;
-   png_sPLT_t new_palette;
-   png_sPLT_entryp pp;
-   png_uint_32 data_length;
-   int entry_size, i;
-   png_uint_32 skip = 0;
-   png_uint_32 dl;
-   size_t max_dl;
-
-   png_debug(1, "in png_handle_sPLT");
-
-#ifdef PNG_USER_LIMITS_SUPPORTED
-   if (png_ptr->user_chunk_cache_max != 0)
-   {
-      if (png_ptr->user_chunk_cache_max == 1)
-      {
-         png_crc_finish(png_ptr, length);
-         return;
-      }
-
-      if (--png_ptr->user_chunk_cache_max == 1)
-      {
-         png_warning(png_ptr, "No space in chunk cache for sPLT");
-         png_crc_finish(png_ptr, length);
-         return;
-      }
-   }
-#endif
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-#ifdef PNG_MAX_MALLOC_64K
-   if (length > 65535U)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "too large to fit in memory");
-      return;
-   }
-#endif
-
-   buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/);
-   if (buffer == NULL)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of memory");
-      return;
-   }
-
-
-   /* WARNING: this may break if size_t is less than 32 bits; it is assumed
-    * that the PNG_MAX_MALLOC_64K test is enabled in this case, but this is a
-    * potential breakage point if the types in pngconf.h aren't exactly right.
-    */
-   png_crc_read(png_ptr, buffer, length);
-
-   if (png_crc_finish(png_ptr, skip) != 0)
-      return;
-
-   buffer[length] = 0;
-
-   for (entry_start = buffer; *entry_start; entry_start++)
-      /* Empty loop to find end of name */ ;
-
-   ++entry_start;
-
-   /* A sample depth should follow the separator, and we should be on it  */
-   if (length < 2U || entry_start > buffer + (length - 2U))
-   {
-      png_warning(png_ptr, "malformed sPLT chunk");
-      return;
-   }
-
-   new_palette.depth = *entry_start++;
-   entry_size = (new_palette.depth == 8 ? 6 : 10);
-   /* This must fit in a png_uint_32 because it is derived from the original
-    * chunk data length.
-    */
-   data_length = length - (png_uint_32)(entry_start - buffer);
-
-   /* Integrity-check the data length */
-   if ((data_length % (unsigned int)entry_size) != 0)
-   {
-      png_warning(png_ptr, "sPLT chunk has bad length");
-      return;
-   }
-
-   dl = (png_uint_32)(data_length / (unsigned int)entry_size);
-   max_dl = PNG_SIZE_MAX / (sizeof (png_sPLT_entry));
-
-   if (dl > max_dl)
-   {
-      png_warning(png_ptr, "sPLT chunk too long");
-      return;
-   }
-
-   new_palette.nentries = (png_int_32)(data_length / (unsigned int)entry_size);
-
-   new_palette.entries = (png_sPLT_entryp)png_malloc_warn(png_ptr,
-       (png_alloc_size_t) new_palette.nentries * (sizeof (png_sPLT_entry)));
-
-   if (new_palette.entries == NULL)
-   {
-      png_warning(png_ptr, "sPLT chunk requires too much memory");
-      return;
-   }
-
-#ifdef PNG_POINTER_INDEXING_SUPPORTED
-   for (i = 0; i < new_palette.nentries; i++)
-   {
-      pp = new_palette.entries + i;
-
-      if (new_palette.depth == 8)
-      {
-         pp->red = *entry_start++;
-         pp->green = *entry_start++;
-         pp->blue = *entry_start++;
-         pp->alpha = *entry_start++;
-      }
-
-      else
-      {
-         pp->red   = png_get_uint_16(entry_start); entry_start += 2;
-         pp->green = png_get_uint_16(entry_start); entry_start += 2;
-         pp->blue  = png_get_uint_16(entry_start); entry_start += 2;
-         pp->alpha = png_get_uint_16(entry_start); entry_start += 2;
-      }
-
-      pp->frequency = png_get_uint_16(entry_start); entry_start += 2;
-   }
-#else
-   pp = new_palette.entries;
-
-   for (i = 0; i < new_palette.nentries; i++)
-   {
-
-      if (new_palette.depth == 8)
-      {
-         pp[i].red   = *entry_start++;
-         pp[i].green = *entry_start++;
-         pp[i].blue  = *entry_start++;
-         pp[i].alpha = *entry_start++;
-      }
-
-      else
-      {
-         pp[i].red   = png_get_uint_16(entry_start); entry_start += 2;
-         pp[i].green = png_get_uint_16(entry_start); entry_start += 2;
-         pp[i].blue  = png_get_uint_16(entry_start); entry_start += 2;
-         pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2;
-      }
-
-      pp[i].frequency = png_get_uint_16(entry_start); entry_start += 2;
-   }
-#endif
-
-   /* Discard all chunk data except the name and stash that */
-   new_palette.name = (png_charp)buffer;
-
-   png_set_sPLT(png_ptr, info_ptr, &new_palette, 1);
-
-   png_free(png_ptr, new_palette.entries);
-}
-#endif /* READ_sPLT */
-
-#ifdef PNG_READ_tRNS_SUPPORTED
-void /* PRIVATE */
-png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   png_byte readbuf[PNG_MAX_PALETTE_LENGTH];
-
-   png_debug(1, "in png_handle_tRNS");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
-   {
-      png_byte buf[2];
-
-      if (length != 2)
-      {
-         png_crc_finish(png_ptr, length);
-         png_chunk_benign_error(png_ptr, "invalid");
-         return;
-      }
-
-      png_crc_read(png_ptr, buf, 2);
-      png_ptr->num_trans = 1;
-      png_ptr->trans_color.gray = png_get_uint_16(buf);
-   }
-
-   else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
-   {
-      png_byte buf[6];
-
-      if (length != 6)
-      {
-         png_crc_finish(png_ptr, length);
-         png_chunk_benign_error(png_ptr, "invalid");
-         return;
-      }
-
-      png_crc_read(png_ptr, buf, length);
-      png_ptr->num_trans = 1;
-      png_ptr->trans_color.red = png_get_uint_16(buf);
-      png_ptr->trans_color.green = png_get_uint_16(buf + 2);
-      png_ptr->trans_color.blue = png_get_uint_16(buf + 4);
-   }
-
-   else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-   {
-      if ((png_ptr->mode & PNG_HAVE_PLTE) == 0)
-      {
-         /* TODO: is this actually an error in the ISO spec? */
-         png_crc_finish(png_ptr, length);
-         png_chunk_benign_error(png_ptr, "out of place");
-         return;
-      }
-
-      if (length > (unsigned int) png_ptr->num_palette ||
-         length > (unsigned int) PNG_MAX_PALETTE_LENGTH ||
-         length == 0)
-      {
-         png_crc_finish(png_ptr, length);
-         png_chunk_benign_error(png_ptr, "invalid");
-         return;
-      }
-
-      png_crc_read(png_ptr, readbuf, length);
-      png_ptr->num_trans = (png_uint_16)length;
-   }
-
-   else
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid with alpha channel");
-      return;
-   }
-
-   if (png_crc_finish(png_ptr, 0) != 0)
-   {
-      png_ptr->num_trans = 0;
-      return;
-   }
-
-   /* TODO: this is a horrible side effect in the palette case because the
-    * png_struct ends up with a pointer to the tRNS buffer owned by the
-    * png_info.  Fix this.
-    */
-   png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans,
-       &(png_ptr->trans_color));
-}
-#endif
-
-#ifdef PNG_READ_bKGD_SUPPORTED
-void /* PRIVATE */
-png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   unsigned int truelen;
-   png_byte buf[6];
-   png_color_16 background;
-
-   png_debug(1, "in png_handle_bKGD");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0 ||
-       (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
-       (png_ptr->mode & PNG_HAVE_PLTE) == 0))
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-      truelen = 1;
-
-   else if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
-      truelen = 6;
-
-   else
-      truelen = 2;
-
-   if (length != truelen)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
-   png_crc_read(png_ptr, buf, truelen);
-
-   if (png_crc_finish(png_ptr, 0) != 0)
-      return;
-
-   /* We convert the index value into RGB components so that we can allow
-    * arbitrary RGB values for background when we have transparency, and
-    * so it is easy to determine the RGB values of the background color
-    * from the info_ptr struct.
-    */
-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-   {
-      background.index = buf[0];
-
-      if (info_ptr != NULL && info_ptr->num_palette != 0)
-      {
-         if (buf[0] >= info_ptr->num_palette)
-         {
-            png_chunk_benign_error(png_ptr, "invalid index");
-            return;
-         }
-
-         background.red = (png_uint_16)png_ptr->palette[buf[0]].red;
-         background.green = (png_uint_16)png_ptr->palette[buf[0]].green;
-         background.blue = (png_uint_16)png_ptr->palette[buf[0]].blue;
-      }
-
-      else
-         background.red = background.green = background.blue = 0;
-
-      background.gray = 0;
-   }
-
-   else if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0) /* GRAY */
-   {
-      if (png_ptr->bit_depth <= 8)
-      {
-         if (buf[0] != 0 || buf[1] >= (unsigned int)(1 << png_ptr->bit_depth))
-         {
-            png_chunk_benign_error(png_ptr, "invalid gray level");
-            return;
-         }
-      }
-
-      background.index = 0;
-      background.red =
-      background.green =
-      background.blue =
-      background.gray = png_get_uint_16(buf);
-   }
-
-   else
-   {
-      if (png_ptr->bit_depth <= 8)
-      {
-         if (buf[0] != 0 || buf[2] != 0 || buf[4] != 0)
-         {
-            png_chunk_benign_error(png_ptr, "invalid color");
-            return;
-         }
-      }
-
-      background.index = 0;
-      background.red = png_get_uint_16(buf);
-      background.green = png_get_uint_16(buf + 2);
-      background.blue = png_get_uint_16(buf + 4);
-      background.gray = 0;
-   }
-
-   png_set_bKGD(png_ptr, info_ptr, &background);
-}
-#endif
-
-#ifdef PNG_READ_eXIf_SUPPORTED
-void /* PRIVATE */
-png_handle_eXIf(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   unsigned int i;
-
-   png_debug(1, "in png_handle_eXIf");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   if (length < 2)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "too short");
-      return;
-   }
-
-   else if (info_ptr == NULL || (info_ptr->valid & PNG_INFO_eXIf) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   info_ptr->free_me |= PNG_FREE_EXIF;
-
-   info_ptr->eXIf_buf = png_voidcast(png_bytep,
-             png_malloc_warn(png_ptr, length));
-
-   if (info_ptr->eXIf_buf == NULL)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of memory");
-      return;
-   }
-
-   for (i = 0; i < length; i++)
-   {
-      png_byte buf[1];
-      png_crc_read(png_ptr, buf, 1);
-      info_ptr->eXIf_buf[i] = buf[0];
-      if (i == 1 && buf[0] != 'M' && buf[0] != 'I'
-                 && info_ptr->eXIf_buf[0] != buf[0])
-      {
-         png_crc_finish(png_ptr, length);
-         png_chunk_benign_error(png_ptr, "incorrect byte-order specifier");
-         png_free(png_ptr, info_ptr->eXIf_buf);
-         info_ptr->eXIf_buf = NULL;
-         return;
-      }
-   }
-
-   if (png_crc_finish(png_ptr, 0) != 0)
-      return;
-
-   png_set_eXIf_1(png_ptr, info_ptr, length, info_ptr->eXIf_buf);
-
-   png_free(png_ptr, info_ptr->eXIf_buf);
-   info_ptr->eXIf_buf = NULL;
-}
-#endif
-
-#ifdef PNG_READ_hIST_SUPPORTED
-void /* PRIVATE */
-png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   unsigned int num, i;
-   png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH];
-
-   png_debug(1, "in png_handle_hIST");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0 ||
-       (png_ptr->mode & PNG_HAVE_PLTE) == 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   num = length / 2 ;
-
-   if (num != (unsigned int) png_ptr->num_palette ||
-       num > (unsigned int) PNG_MAX_PALETTE_LENGTH)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
-   for (i = 0; i < num; i++)
-   {
-      png_byte buf[2];
-
-      png_crc_read(png_ptr, buf, 2);
-      readbuf[i] = png_get_uint_16(buf);
-   }
-
-   if (png_crc_finish(png_ptr, 0) != 0)
-      return;
-
-   png_set_hIST(png_ptr, info_ptr, readbuf);
-}
-#endif
-
-#ifdef PNG_READ_pHYs_SUPPORTED
-void /* PRIVATE */
-png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   png_byte buf[9];
-   png_uint_32 res_x, res_y;
-   int unit_type;
-
-   png_debug(1, "in png_handle_pHYs");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   if (length != 9)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
-   png_crc_read(png_ptr, buf, 9);
-
-   if (png_crc_finish(png_ptr, 0) != 0)
-      return;
-
-   res_x = png_get_uint_32(buf);
-   res_y = png_get_uint_32(buf + 4);
-   unit_type = buf[8];
-   png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type);
-}
-#endif
-
-#ifdef PNG_READ_oFFs_SUPPORTED
-void /* PRIVATE */
-png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   png_byte buf[9];
-   png_int_32 offset_x, offset_y;
-   int unit_type;
-
-   png_debug(1, "in png_handle_oFFs");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   if (length != 9)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
-   png_crc_read(png_ptr, buf, 9);
-
-   if (png_crc_finish(png_ptr, 0) != 0)
-      return;
-
-   offset_x = png_get_int_32(buf);
-   offset_y = png_get_int_32(buf + 4);
-   unit_type = buf[8];
-   png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type);
-}
-#endif
-
-#ifdef PNG_READ_pCAL_SUPPORTED
-/* Read the pCAL chunk (described in the PNG Extensions document) */
-void /* PRIVATE */
-png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   png_int_32 X0, X1;
-   png_byte type, nparams;
-   png_bytep buffer, buf, units, endptr;
-   png_charpp params;
-   int i;
-
-   png_debug(1, "in png_handle_pCAL");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   png_debug1(2, "Allocating and reading pCAL chunk data (%u bytes)",
-       length + 1);
-
-   buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/);
-
-   if (buffer == NULL)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of memory");
-      return;
-   }
-
-   png_crc_read(png_ptr, buffer, length);
-
-   if (png_crc_finish(png_ptr, 0) != 0)
-      return;
-
-   buffer[length] = 0; /* Null terminate the last string */
-
-   png_debug(3, "Finding end of pCAL purpose string");
-   for (buf = buffer; *buf; buf++)
-      /* Empty loop */ ;
-
-   endptr = buffer + length;
-
-   /* We need to have at least 12 bytes after the purpose string
-    * in order to get the parameter information.
-    */
-   if (endptr - buf <= 12)
-   {
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
-   png_debug(3, "Reading pCAL X0, X1, type, nparams, and units");
-   X0 = png_get_int_32((png_bytep)buf+1);
-   X1 = png_get_int_32((png_bytep)buf+5);
-   type = buf[9];
-   nparams = buf[10];
-   units = buf + 11;
-
-   png_debug(3, "Checking pCAL equation type and number of parameters");
-   /* Check that we have the right number of parameters for known
-    * equation types.
-    */
-   if ((type == PNG_EQUATION_LINEAR && nparams != 2) ||
-       (type == PNG_EQUATION_BASE_E && nparams != 3) ||
-       (type == PNG_EQUATION_ARBITRARY && nparams != 3) ||
-       (type == PNG_EQUATION_HYPERBOLIC && nparams != 4))
-   {
-      png_chunk_benign_error(png_ptr, "invalid parameter count");
-      return;
-   }
-
-   else if (type >= PNG_EQUATION_LAST)
-   {
-      png_chunk_benign_error(png_ptr, "unrecognized equation type");
-   }
-
-   for (buf = units; *buf; buf++)
-      /* Empty loop to move past the units string. */ ;
-
-   png_debug(3, "Allocating pCAL parameters array");
-
-   params = png_voidcast(png_charpp, png_malloc_warn(png_ptr,
-       nparams * (sizeof (png_charp))));
-
-   if (params == NULL)
-   {
-      png_chunk_benign_error(png_ptr, "out of memory");
-      return;
-   }
-
-   /* Get pointers to the start of each parameter string. */
-   for (i = 0; i < nparams; i++)
-   {
-      buf++; /* Skip the null string terminator from previous parameter. */
-
-      png_debug1(3, "Reading pCAL parameter %d", i);
-
-      for (params[i] = (png_charp)buf; buf <= endptr && *buf != 0; buf++)
-         /* Empty loop to move past each parameter string */ ;
-
-      /* Make sure we haven't run out of data yet */
-      if (buf > endptr)
-      {
-         png_free(png_ptr, params);
-         png_chunk_benign_error(png_ptr, "invalid data");
-         return;
-      }
-   }
-
-   png_set_pCAL(png_ptr, info_ptr, (png_charp)buffer, X0, X1, type, nparams,
-       (png_charp)units, params);
-
-   png_free(png_ptr, params);
-}
-#endif
-
-#ifdef PNG_READ_sCAL_SUPPORTED
-/* Read the sCAL chunk */
-void /* PRIVATE */
-png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   png_bytep buffer;
-   size_t i;
-   int state;
-
-   png_debug(1, "in png_handle_sCAL");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of place");
-      return;
-   }
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   /* Need unit type, width, \0, height: minimum 4 bytes */
-   else if (length < 4)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
-   png_debug1(2, "Allocating and reading sCAL chunk data (%u bytes)",
-       length + 1);
-
-   buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/);
-
-   if (buffer == NULL)
-   {
-      png_chunk_benign_error(png_ptr, "out of memory");
-      png_crc_finish(png_ptr, length);
-      return;
-   }
-
-   png_crc_read(png_ptr, buffer, length);
-   buffer[length] = 0; /* Null terminate the last string */
-
-   if (png_crc_finish(png_ptr, 0) != 0)
-      return;
-
-   /* Validate the unit. */
-   if (buffer[0] != 1 && buffer[0] != 2)
-   {
-      png_chunk_benign_error(png_ptr, "invalid unit");
-      return;
-   }
-
-   /* Validate the ASCII numbers, need two ASCII numbers separated by
-    * a '\0' and they need to fit exactly in the chunk data.
-    */
-   i = 1;
-   state = 0;
-
-   if (png_check_fp_number((png_const_charp)buffer, length, &state, &i) == 0 ||
-       i >= length || buffer[i++] != 0)
-      png_chunk_benign_error(png_ptr, "bad width format");
-
-   else if (PNG_FP_IS_POSITIVE(state) == 0)
-      png_chunk_benign_error(png_ptr, "non-positive width");
-
-   else
-   {
-      size_t heighti = i;
-
-      state = 0;
-      if (png_check_fp_number((png_const_charp)buffer, length,
-          &state, &i) == 0 || i != length)
-         png_chunk_benign_error(png_ptr, "bad height format");
-
-      else if (PNG_FP_IS_POSITIVE(state) == 0)
-         png_chunk_benign_error(png_ptr, "non-positive height");
-
-      else
-         /* This is the (only) success case. */
-         png_set_sCAL_s(png_ptr, info_ptr, buffer[0],
-             (png_charp)buffer+1, (png_charp)buffer+heighti);
-   }
-}
-#endif
-
-#ifdef PNG_READ_tIME_SUPPORTED
-void /* PRIVATE */
-png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   png_byte buf[7];
-   png_time mod_time;
-
-   png_debug(1, "in png_handle_tIME");
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) != 0)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "duplicate");
-      return;
-   }
-
-   if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-      png_ptr->mode |= PNG_AFTER_IDAT;
-
-   if (length != 7)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "invalid");
-      return;
-   }
-
-   png_crc_read(png_ptr, buf, 7);
-
-   if (png_crc_finish(png_ptr, 0) != 0)
-      return;
-
-   mod_time.second = buf[6];
-   mod_time.minute = buf[5];
-   mod_time.hour = buf[4];
-   mod_time.day = buf[3];
-   mod_time.month = buf[2];
-   mod_time.year = png_get_uint_16(buf);
-
-   png_set_tIME(png_ptr, info_ptr, &mod_time);
-}
-#endif
-
-#ifdef PNG_READ_tEXt_SUPPORTED
-/* Note: this does not properly handle chunks that are > 64K under DOS */
-void /* PRIVATE */
-png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   png_text  text_info;
-   png_bytep buffer;
-   png_charp key;
-   png_charp text;
-   png_uint_32 skip = 0;
-
-   png_debug(1, "in png_handle_tEXt");
-
-#ifdef PNG_USER_LIMITS_SUPPORTED
-   if (png_ptr->user_chunk_cache_max != 0)
-   {
-      if (png_ptr->user_chunk_cache_max == 1)
-      {
-         png_crc_finish(png_ptr, length);
-         return;
-      }
-
-      if (--png_ptr->user_chunk_cache_max == 1)
-      {
-         png_crc_finish(png_ptr, length);
-         png_chunk_benign_error(png_ptr, "no space in chunk cache");
-         return;
-      }
-   }
-#endif
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-      png_ptr->mode |= PNG_AFTER_IDAT;
-
-#ifdef PNG_MAX_MALLOC_64K
-   if (length > 65535U)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "too large to fit in memory");
-      return;
-   }
-#endif
-
-   buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
-
-   if (buffer == NULL)
-   {
-      png_chunk_benign_error(png_ptr, "out of memory");
-      return;
-   }
-
-   png_crc_read(png_ptr, buffer, length);
-
-   if (png_crc_finish(png_ptr, skip) != 0)
-      return;
-
-   key = (png_charp)buffer;
-   key[length] = 0;
-
-   for (text = key; *text; text++)
-      /* Empty loop to find end of key */ ;
-
-   if (text != key + length)
-      text++;
-
-   text_info.compression = PNG_TEXT_COMPRESSION_NONE;
-   text_info.key = key;
-   text_info.lang = NULL;
-   text_info.lang_key = NULL;
-   text_info.itxt_length = 0;
-   text_info.text = text;
-   text_info.text_length = strlen(text);
-
-   if (png_set_text_2(png_ptr, info_ptr, &text_info, 1) != 0)
-      png_warning(png_ptr, "Insufficient memory to process text chunk");
-}
-#endif
-
-#ifdef PNG_READ_zTXt_SUPPORTED
-/* Note: this does not correctly handle chunks that are > 64K under DOS */
-void /* PRIVATE */
-png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   png_const_charp errmsg = NULL;
-   png_bytep       buffer;
-   png_uint_32     keyword_length;
-
-   png_debug(1, "in png_handle_zTXt");
-
-#ifdef PNG_USER_LIMITS_SUPPORTED
-   if (png_ptr->user_chunk_cache_max != 0)
-   {
-      if (png_ptr->user_chunk_cache_max == 1)
-      {
-         png_crc_finish(png_ptr, length);
-         return;
-      }
-
-      if (--png_ptr->user_chunk_cache_max == 1)
-      {
-         png_crc_finish(png_ptr, length);
-         png_chunk_benign_error(png_ptr, "no space in chunk cache");
-         return;
-      }
-   }
-#endif
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-      png_ptr->mode |= PNG_AFTER_IDAT;
-
-   /* Note, "length" is sufficient here; we won't be adding
-    * a null terminator later.
-    */
-   buffer = png_read_buffer(png_ptr, length, 2/*silent*/);
-
-   if (buffer == NULL)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of memory");
-      return;
-   }
-
-   png_crc_read(png_ptr, buffer, length);
-
-   if (png_crc_finish(png_ptr, 0) != 0)
-      return;
-
-   /* TODO: also check that the keyword contents match the spec! */
-   for (keyword_length = 0;
-      keyword_length < length && buffer[keyword_length] != 0;
-      ++keyword_length)
-      /* Empty loop to find end of name */ ;
-
-   if (keyword_length > 79 || keyword_length < 1)
-      errmsg = "bad keyword";
-
-   /* zTXt must have some LZ data after the keyword, although it may expand to
-    * zero bytes; we need a '\0' at the end of the keyword, the compression type
-    * then the LZ data:
-    */
-   else if (keyword_length + 3 > length)
-      errmsg = "truncated";
-
-   else if (buffer[keyword_length+1] != PNG_COMPRESSION_TYPE_BASE)
-      errmsg = "unknown compression type";
-
-   else
-   {
-      png_alloc_size_t uncompressed_length = PNG_SIZE_MAX;
-
-      /* TODO: at present png_decompress_chunk imposes a single application
-       * level memory limit, this should be split to different values for iCCP
-       * and text chunks.
-       */
-      if (png_decompress_chunk(png_ptr, length, keyword_length+2,
-          &uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
-      {
-         png_text text;
-
-         if (png_ptr->read_buffer == NULL)
-           errmsg="Read failure in png_handle_zTXt";
-         else
-         {
-            /* It worked; png_ptr->read_buffer now looks like a tEXt chunk
-             * except for the extra compression type byte and the fact that
-             * it isn't necessarily '\0' terminated.
-             */
-            buffer = png_ptr->read_buffer;
-            buffer[uncompressed_length+(keyword_length+2)] = 0;
-
-            text.compression = PNG_TEXT_COMPRESSION_zTXt;
-            text.key = (png_charp)buffer;
-            text.text = (png_charp)(buffer + keyword_length+2);
-            text.text_length = uncompressed_length;
-            text.itxt_length = 0;
-            text.lang = NULL;
-            text.lang_key = NULL;
-
-            if (png_set_text_2(png_ptr, info_ptr, &text, 1) != 0)
-               errmsg = "insufficient memory";
-         }
-      }
-
-      else
-         errmsg = png_ptr->zstream.msg;
-   }
-
-   if (errmsg != NULL)
-      png_chunk_benign_error(png_ptr, errmsg);
-}
-#endif
-
-#ifdef PNG_READ_iTXt_SUPPORTED
-/* Note: this does not correctly handle chunks that are > 64K under DOS */
-void /* PRIVATE */
-png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
-{
-   png_const_charp errmsg = NULL;
-   png_bytep buffer;
-   png_uint_32 prefix_length;
-
-   png_debug(1, "in png_handle_iTXt");
-
-#ifdef PNG_USER_LIMITS_SUPPORTED
-   if (png_ptr->user_chunk_cache_max != 0)
-   {
-      if (png_ptr->user_chunk_cache_max == 1)
-      {
-         png_crc_finish(png_ptr, length);
-         return;
-      }
-
-      if (--png_ptr->user_chunk_cache_max == 1)
-      {
-         png_crc_finish(png_ptr, length);
-         png_chunk_benign_error(png_ptr, "no space in chunk cache");
-         return;
-      }
-   }
-#endif
-
-   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
-      png_chunk_error(png_ptr, "missing IHDR");
-
-   if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
-      png_ptr->mode |= PNG_AFTER_IDAT;
-
-   buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
-
-   if (buffer == NULL)
-   {
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "out of memory");
-      return;
-   }
-
-   png_crc_read(png_ptr, buffer, length);
-
-   if (png_crc_finish(png_ptr, 0) != 0)
-      return;
-
-   /* First the keyword. */
-   for (prefix_length=0;
-      prefix_length < length && buffer[prefix_length] != 0;
-      ++prefix_length)
-      /* Empty loop */ ;
-
-   /* Perform a basic check on the keyword length here. */
-   if (prefix_length > 79 || prefix_length < 1)
-      errmsg = "bad keyword";
-
-   /* Expect keyword, compression flag, compression type, language, translated
-    * keyword (both may be empty but are 0 terminated) then the text, which may
-    * be empty.
-    */
-   else if (prefix_length + 5 > length)
-      errmsg = "truncated";
-
-   else if (buffer[prefix_length+1] == 0 ||
-      (buffer[prefix_length+1] == 1 &&
-      buffer[prefix_length+2] == PNG_COMPRESSION_TYPE_BASE))
-   {
-      int compressed = buffer[prefix_length+1] != 0;
-      png_uint_32 language_offset, translated_keyword_offset;
-      png_alloc_size_t uncompressed_length = 0;
-
-      /* Now the language tag */
-      prefix_length += 3;
-      language_offset = prefix_length;
-
-      for (; prefix_length < length && buffer[prefix_length] != 0;
-         ++prefix_length)
-         /* Empty loop */ ;
-
-      /* WARNING: the length may be invalid here, this is checked below. */
-      translated_keyword_offset = ++prefix_length;
-
-      for (; prefix_length < length && buffer[prefix_length] != 0;
-         ++prefix_length)
-         /* Empty loop */ ;
-
-      /* prefix_length should now be at the trailing '\0' of the translated
-       * keyword, but it may already be over the end.  None of this arithmetic
-       * can overflow because chunks are at most 2^31 bytes long, but on 16-bit
-       * systems the available allocation may overflow.
-       */
-      ++prefix_length;
-
-      if (compressed == 0 && prefix_length <= length)
-         uncompressed_length = length - prefix_length;
-
-      else if (compressed != 0 && prefix_length < length)
-      {
-         uncompressed_length = PNG_SIZE_MAX;
-
-         /* TODO: at present png_decompress_chunk imposes a single application
-          * level memory limit, this should be split to different values for
-          * iCCP and text chunks.
-          */
-         if (png_decompress_chunk(png_ptr, length, prefix_length,
-             &uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
-            buffer = png_ptr->read_buffer;
-
-         else
-            errmsg = png_ptr->zstream.msg;
-      }
-
-      else
-         errmsg = "truncated";
-
-      if (errmsg == NULL)
-      {
-         png_text text;
-
-         buffer[uncompressed_length+prefix_length] = 0;
-
-         if (compressed == 0)
-            text.compression = PNG_ITXT_COMPRESSION_NONE;
-
-         else
-            text.compression = PNG_ITXT_COMPRESSION_zTXt;
-
-         text.key = (png_charp)buffer;
-         text.lang = (png_charp)buffer + language_offset;
-         text.lang_key = (png_charp)buffer + translated_keyword_offset;
-         text.text = (png_charp)buffer + prefix_length;
-         text.text_length = 0;
-         text.itxt_length = uncompressed_length;
-
-         if (png_set_text_2(png_ptr, info_ptr, &text, 1) != 0)
-            errmsg = "insufficient memory";
-      }
-   }
-
-   else
-      errmsg = "bad compression info";
-
-   if (errmsg != NULL)
-      png_chunk_benign_error(png_ptr, errmsg);
-}
-#endif
-
-#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
-/* Utility function for png_handle_unknown; set up png_ptr::unknown_chunk */
-static int
-png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length)
-{
-   png_alloc_size_t limit = PNG_SIZE_MAX;
-
-   if (png_ptr->unknown_chunk.data != NULL)
-   {
-      png_free(png_ptr, png_ptr->unknown_chunk.data);
-      png_ptr->unknown_chunk.data = NULL;
-   }
-
-#  ifdef PNG_SET_USER_LIMITS_SUPPORTED
-   if (png_ptr->user_chunk_malloc_max > 0 &&
-       png_ptr->user_chunk_malloc_max < limit)
-      limit = png_ptr->user_chunk_malloc_max;
-
-#  elif PNG_USER_CHUNK_MALLOC_MAX > 0
-   if (PNG_USER_CHUNK_MALLOC_MAX < limit)
-      limit = PNG_USER_CHUNK_MALLOC_MAX;
-#  endif
-
-   if (length <= limit)
-   {
-      PNG_CSTRING_FROM_CHUNK(png_ptr->unknown_chunk.name, png_ptr->chunk_name);
-      /* The following is safe because of the PNG_SIZE_MAX init above */
-      png_ptr->unknown_chunk.size = (size_t)length/*SAFE*/;
-      /* 'mode' is a flag array, only the bottom four bits matter here */
-      png_ptr->unknown_chunk.location = (png_byte)png_ptr->mode/*SAFE*/;
-
-      if (length == 0)
-         png_ptr->unknown_chunk.data = NULL;
-
-      else
-      {
-         /* Do a 'warn' here - it is handled below. */
-         png_ptr->unknown_chunk.data = png_voidcast(png_bytep,
-             png_malloc_warn(png_ptr, length));
-      }
-   }
-
-   if (png_ptr->unknown_chunk.data == NULL && length > 0)
-   {
-      /* This is benign because we clean up correctly */
-      png_crc_finish(png_ptr, length);
-      png_chunk_benign_error(png_ptr, "unknown chunk exceeds memory limits");
-      return 0;
-   }
-
-   else
-   {
-      if (length > 0)
-         png_crc_read(png_ptr, png_ptr->unknown_chunk.data, length);
-      png_crc_finish(png_ptr, 0);
-      return 1;
-   }
-}
-#endif /* READ_UNKNOWN_CHUNKS */
-
-/* Handle an unknown, or known but disabled, chunk */
-void /* PRIVATE */
-png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr,
-    png_uint_32 length, int keep)
-{
-   int handled = 0; /* the chunk was handled */
-
-   png_debug(1, "in png_handle_unknown");
-
-#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
-   /* NOTE: this code is based on the code in libpng-1.4.12 except for fixing
-    * the bug which meant that setting a non-default behavior for a specific
-    * chunk would be ignored (the default was always used unless a user
-    * callback was installed).
-    *
-    * 'keep' is the value from the png_chunk_unknown_handling, the setting for
-    * this specific chunk_name, if PNG_HANDLE_AS_UNKNOWN_SUPPORTED, if not it
-    * will always be PNG_HANDLE_CHUNK_AS_DEFAULT and it needs to be set here.
-    * This is just an optimization to avoid multiple calls to the lookup
-    * function.
-    */
-#  ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-#     ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
-   keep = png_chunk_unknown_handling(png_ptr, png_ptr->chunk_name);
-#     endif
-#  endif
-
-   /* One of the following methods will read the chunk or skip it (at least one
-    * of these is always defined because this is the only way to switch on
-    * PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
-    */
-#  ifdef PNG_READ_USER_CHUNKS_SUPPORTED
-   /* The user callback takes precedence over the chunk keep value, but the
-    * keep value is still required to validate a save of a critical chunk.
-    */
-   if (png_ptr->read_user_chunk_fn != NULL)
-   {
-      if (png_cache_unknown_chunk(png_ptr, length) != 0)
-      {
-         /* Callback to user unknown chunk handler */
-         int ret = (*(png_ptr->read_user_chunk_fn))(png_ptr,
-             &png_ptr->unknown_chunk);
-
-         /* ret is:
-          * negative: An error occurred; png_chunk_error will be called.
-          *     zero: The chunk was not handled, the chunk will be discarded
-          *           unless png_set_keep_unknown_chunks has been used to set
-          *           a 'keep' behavior for this particular chunk, in which
-          *           case that will be used.  A critical chunk will cause an
-          *           error at this point unless it is to be saved.
-          * positive: The chunk was handled, libpng will ignore/discard it.
-          */
-         if (ret < 0)
-            png_chunk_error(png_ptr, "error in user chunk");
-
-         else if (ret == 0)
-         {
-            /* If the keep value is 'default' or 'never' override it, but
-             * still error out on critical chunks unless the keep value is
-             * 'always'  While this is weird it is the behavior in 1.4.12.
-             * A possible improvement would be to obey the value set for the
-             * chunk, but this would be an API change that would probably
-             * damage some applications.
-             *
-             * The png_app_warning below catches the case that matters, where
-             * the application has not set specific save or ignore for this
-             * chunk or global save or ignore.
-             */
-            if (keep < PNG_HANDLE_CHUNK_IF_SAFE)
-            {
-#              ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
-               if (png_ptr->unknown_default < PNG_HANDLE_CHUNK_IF_SAFE)
-               {
-                  png_chunk_warning(png_ptr, "Saving unknown chunk:");
-                  png_app_warning(png_ptr,
-                      "forcing save of an unhandled chunk;"
-                      " please call png_set_keep_unknown_chunks");
-                      /* with keep = PNG_HANDLE_CHUNK_IF_SAFE */
-               }
-#              endif
-               keep = PNG_HANDLE_CHUNK_IF_SAFE;
-            }
-         }
-
-         else /* chunk was handled */
-         {
-            handled = 1;
-            /* Critical chunks can be safely discarded at this point. */
-            keep = PNG_HANDLE_CHUNK_NEVER;
-         }
-      }
-
-      else
-         keep = PNG_HANDLE_CHUNK_NEVER; /* insufficient memory */
-   }
-
-   else
-   /* Use the SAVE_UNKNOWN_CHUNKS code or skip the chunk */
-#  endif /* READ_USER_CHUNKS */
-
-#  ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
-   {
-      /* keep is currently just the per-chunk setting, if there was no
-       * setting change it to the global default now (not that this may
-       * still be AS_DEFAULT) then obtain the cache of the chunk if required,
-       * if not simply skip the chunk.
-       */
-      if (keep == PNG_HANDLE_CHUNK_AS_DEFAULT)
-         keep = png_ptr->unknown_default;
-
-      if (keep == PNG_HANDLE_CHUNK_ALWAYS ||
-         (keep == PNG_HANDLE_CHUNK_IF_SAFE &&
-          PNG_CHUNK_ANCILLARY(png_ptr->chunk_name)))
-      {
-         if (png_cache_unknown_chunk(png_ptr, length) == 0)
-            keep = PNG_HANDLE_CHUNK_NEVER;
-      }
-
-      else
-         png_crc_finish(png_ptr, length);
-   }
-#  else
-#     ifndef PNG_READ_USER_CHUNKS_SUPPORTED
-#        error no method to support READ_UNKNOWN_CHUNKS
-#     endif
-
-   {
-      /* If here there is no read callback pointer set and no support is
-       * compiled in to just save the unknown chunks, so simply skip this
-       * chunk.  If 'keep' is something other than AS_DEFAULT or NEVER then
-       * the app has erroneously asked for unknown chunk saving when there
-       * is no support.
-       */
-      if (keep > PNG_HANDLE_CHUNK_NEVER)
-         png_app_error(png_ptr, "no unknown chunk support available");
-
-      png_crc_finish(png_ptr, length);
-   }
-#  endif
-
-#  ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
-   /* Now store the chunk in the chunk list if appropriate, and if the limits
-    * permit it.
-    */
-   if (keep == PNG_HANDLE_CHUNK_ALWAYS ||
-      (keep == PNG_HANDLE_CHUNK_IF_SAFE &&
-       PNG_CHUNK_ANCILLARY(png_ptr->chunk_name)))
-   {
-#     ifdef PNG_USER_LIMITS_SUPPORTED
-      switch (png_ptr->user_chunk_cache_max)
-      {
-         case 2:
-            png_ptr->user_chunk_cache_max = 1;
-            png_chunk_benign_error(png_ptr, "no space in chunk cache");
-            /* FALLTHROUGH */
-         case 1:
-            /* NOTE: prior to 1.6.0 this case resulted in an unknown critical
-             * chunk being skipped, now there will be a hard error below.
-             */
-            break;
-
-         default: /* not at limit */
-            --(png_ptr->user_chunk_cache_max);
-            /* FALLTHROUGH */
-         case 0: /* no limit */
-#  endif /* USER_LIMITS */
-            /* Here when the limit isn't reached or when limits are compiled
-             * out; store the chunk.
-             */
-            png_set_unknown_chunks(png_ptr, info_ptr,
-                &png_ptr->unknown_chunk, 1);
-            handled = 1;
-#  ifdef PNG_USER_LIMITS_SUPPORTED
-            break;
-      }
-#  endif
-   }
-#  else /* no store support: the chunk must be handled by the user callback */
-   PNG_UNUSED(info_ptr)
-#  endif
-
-   /* Regardless of the error handling below the cached data (if any) can be
-    * freed now.  Notice that the data is not freed if there is a png_error, but
-    * it will be freed by destroy_read_struct.
-    */
-   if (png_ptr->unknown_chunk.data != NULL)
-      png_free(png_ptr, png_ptr->unknown_chunk.data);
-   png_ptr->unknown_chunk.data = NULL;
-
-#else /* !PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */
-   /* There is no support to read an unknown chunk, so just skip it. */
-   png_crc_finish(png_ptr, length);
-   PNG_UNUSED(info_ptr)
-   PNG_UNUSED(keep)
-#endif /* !READ_UNKNOWN_CHUNKS */
-
-   /* Check for unhandled critical chunks */
-   if (handled == 0 && PNG_CHUNK_CRITICAL(png_ptr->chunk_name))
-      png_chunk_error(png_ptr, "unhandled critical chunk");
-}
-
-/* This function is called to verify that a chunk name is valid.
- * This function can't have the "critical chunk check" incorporated
- * into it, since in the future we will need to be able to call user
- * functions to handle unknown critical chunks after we check that
- * the chunk name itself is valid.
- */
-
-/* Bit hacking: the test for an invalid byte in the 4 byte chunk name is:
- *
- * ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
- */
-
-void /* PRIVATE */
-png_check_chunk_name(png_const_structrp png_ptr, png_uint_32 chunk_name)
-{
-   int i;
-   png_uint_32 cn=chunk_name;
-
-   png_debug(1, "in png_check_chunk_name");
-
-   for (i=1; i<=4; ++i)
-   {
-      int c = cn & 0xff;
-
-      if (c < 65 || c > 122 || (c > 90 && c < 97))
-         png_chunk_error(png_ptr, "invalid chunk type");
-
-      cn >>= 8;
-   }
-}
-
-void /* PRIVATE */
-png_check_chunk_length(png_const_structrp png_ptr, png_uint_32 length)
-{
-   png_alloc_size_t limit = PNG_UINT_31_MAX;
-
-# ifdef PNG_SET_USER_LIMITS_SUPPORTED
-   if (png_ptr->user_chunk_malloc_max > 0 &&
-       png_ptr->user_chunk_malloc_max < limit)
-      limit = png_ptr->user_chunk_malloc_max;
-# elif PNG_USER_CHUNK_MALLOC_MAX > 0
-   if (PNG_USER_CHUNK_MALLOC_MAX < limit)
-      limit = PNG_USER_CHUNK_MALLOC_MAX;
-# endif
-   if (png_ptr->chunk_name == png_IDAT)
-   {
-      png_alloc_size_t idat_limit = PNG_UINT_31_MAX;
-      size_t row_factor =
-         (size_t)png_ptr->width
-         * (size_t)png_ptr->channels
-         * (png_ptr->bit_depth > 8? 2: 1)
-         + 1
-         + (png_ptr->interlaced? 6: 0);
-      if (png_ptr->height > PNG_UINT_32_MAX/row_factor)
-         idat_limit = PNG_UINT_31_MAX;
-      else
-         idat_limit = png_ptr->height * row_factor;
-      row_factor = row_factor > 32566? 32566 : row_factor;
-      idat_limit += 6 + 5*(idat_limit/row_factor+1); /* zlib+deflate overhead */
-      idat_limit=idat_limit < PNG_UINT_31_MAX? idat_limit : PNG_UINT_31_MAX;
-      limit = limit < idat_limit? idat_limit : limit;
-   }
-
-   if (length > limit)
-   {
-      png_debug2(0," length = %lu, limit = %lu",
-         (unsigned long)length,(unsigned long)limit);
-      png_benign_error(png_ptr, "chunk data is too large");
-   }
-}
-
-/* Combines the row recently read in with the existing pixels in the row.  This
- * routine takes care of alpha and transparency if requested.  This routine also
- * handles the two methods of progressive display of interlaced images,
- * depending on the 'display' value; if 'display' is true then the whole row
- * (dp) is filled from the start by replicating the available pixels.  If
- * 'display' is false only those pixels present in the pass are filled in.
- */
-void /* PRIVATE */
-png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display)
-{
-   unsigned int pixel_depth = png_ptr->transformed_pixel_depth;
-   png_const_bytep sp = png_ptr->row_buf + 1;
-   png_alloc_size_t row_width = png_ptr->width;
-   unsigned int pass = png_ptr->pass;
-   png_bytep end_ptr = 0;
-   png_byte end_byte = 0;
-   unsigned int end_mask;
-
-   png_debug(1, "in png_combine_row");
-
-   /* Added in 1.5.6: it should not be possible to enter this routine until at
-    * least one row has been read from the PNG data and transformed.
-    */
-   if (pixel_depth == 0)
-      png_error(png_ptr, "internal row logic error");
-
-   /* Added in 1.5.4: the pixel depth should match the information returned by
-    * any call to png_read_update_info at this point.  Do not continue if we got
-    * this wrong.
-    */
-   if (png_ptr->info_rowbytes != 0 && png_ptr->info_rowbytes !=
-          PNG_ROWBYTES(pixel_depth, row_width))
-      png_error(png_ptr, "internal row size calculation error");
-
-   /* Don't expect this to ever happen: */
-   if (row_width == 0)
-      png_error(png_ptr, "internal row width error");
-
-   /* Preserve the last byte in cases where only part of it will be overwritten,
-    * the multiply below may overflow, we don't care because ANSI-C guarantees
-    * we get the low bits.
-    */
-   end_mask = (pixel_depth * row_width) & 7;
-   if (end_mask != 0)
-   {
-      /* end_ptr == NULL is a flag to say do nothing */
-      end_ptr = dp + PNG_ROWBYTES(pixel_depth, row_width) - 1;
-      end_byte = *end_ptr;
-#     ifdef PNG_READ_PACKSWAP_SUPPORTED
-      if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
-         /* little-endian byte */
-         end_mask = (unsigned int)(0xff << end_mask);
-
-      else /* big-endian byte */
-#     endif
-      end_mask = 0xff >> end_mask;
-      /* end_mask is now the bits to *keep* from the destination row */
-   }
-
-   /* For non-interlaced images this reduces to a memcpy(). A memcpy()
-    * will also happen if interlacing isn't supported or if the application
-    * does not call png_set_interlace_handling().  In the latter cases the
-    * caller just gets a sequence of the unexpanded rows from each interlace
-    * pass.
-    */
-#ifdef PNG_READ_INTERLACING_SUPPORTED
-   if (png_ptr->interlaced != 0 &&
-       (png_ptr->transformations & PNG_INTERLACE) != 0 &&
-       pass < 6 && (display == 0 ||
-       /* The following copies everything for 'display' on passes 0, 2 and 4. */
-       (display == 1 && (pass & 1) != 0)))
-   {
-      /* Narrow images may have no bits in a pass; the caller should handle
-       * this, but this test is cheap:
-       */
-      if (row_width <= PNG_PASS_START_COL(pass))
-         return;
-
-      if (pixel_depth < 8)
-      {
-         /* For pixel depths up to 4 bpp the 8-pixel mask can be expanded to fit
-          * into 32 bits, then a single loop over the bytes using the four byte
-          * values in the 32-bit mask can be used.  For the 'display' option the
-          * expanded mask may also not require any masking within a byte.  To
-          * make this work the PACKSWAP option must be taken into account - it
-          * simply requires the pixels to be reversed in each byte.
-          *
-          * The 'regular' case requires a mask for each of the first 6 passes,
-          * the 'display' case does a copy for the even passes in the range
-          * 0..6.  This has already been handled in the test above.
-          *
-          * The masks are arranged as four bytes with the first byte to use in
-          * the lowest bits (little-endian) regardless of the order (PACKSWAP or
-          * not) of the pixels in each byte.
-          *
-          * NOTE: the whole of this logic depends on the caller of this function
-          * only calling it on rows appropriate to the pass.  This function only
-          * understands the 'x' logic; the 'y' logic is handled by the caller.
-          *
-          * The following defines allow generation of compile time constant bit
-          * masks for each pixel depth and each possibility of swapped or not
-          * swapped bytes.  Pass 'p' is in the range 0..6; 'x', a pixel index,
-          * is in the range 0..7; and the result is 1 if the pixel is to be
-          * copied in the pass, 0 if not.  'S' is for the sparkle method, 'B'
-          * for the block method.
-          *
-          * With some compilers a compile time expression of the general form:
-          *
-          *    (shift >= 32) ? (a >> (shift-32)) : (b >> shift)
-          *
-          * Produces warnings with values of 'shift' in the range 33 to 63
-          * because the right hand side of the ?: expression is evaluated by
-          * the compiler even though it isn't used.  Microsoft Visual C (various
-          * versions) and the Intel C compiler are known to do this.  To avoid
-          * this the following macros are used in 1.5.6.  This is a temporary
-          * solution to avoid destabilizing the code during the release process.
-          */
-#        if PNG_USE_COMPILE_TIME_MASKS
-#           define PNG_LSR(x,s) ((x)>>((s) & 0x1f))
-#           define PNG_LSL(x,s) ((x)<<((s) & 0x1f))
-#        else
-#           define PNG_LSR(x,s) ((x)>>(s))
-#           define PNG_LSL(x,s) ((x)<<(s))
-#        endif
-#        define S_COPY(p,x) (((p)<4 ? PNG_LSR(0x80088822,(3-(p))*8+(7-(x))) :\
-           PNG_LSR(0xaa55ff00,(7-(p))*8+(7-(x)))) & 1)
-#        define B_COPY(p,x) (((p)<4 ? PNG_LSR(0xff0fff33,(3-(p))*8+(7-(x))) :\
-           PNG_LSR(0xff55ff00,(7-(p))*8+(7-(x)))) & 1)
-
-         /* Return a mask for pass 'p' pixel 'x' at depth 'd'.  The mask is
-          * little endian - the first pixel is at bit 0 - however the extra
-          * parameter 's' can be set to cause the mask position to be swapped
-          * within each byte, to match the PNG format.  This is done by XOR of
-          * the shift with 7, 6 or 4 for bit depths 1, 2 and 4.
-          */
-#        define PIXEL_MASK(p,x,d,s) \
-            (PNG_LSL(((PNG_LSL(1U,(d)))-1),(((x)*(d))^((s)?8-(d):0))))
-
-         /* Hence generate the appropriate 'block' or 'sparkle' pixel copy mask.
-          */
-#        define S_MASKx(p,x,d,s) (S_COPY(p,x)?PIXEL_MASK(p,x,d,s):0)
-#        define B_MASKx(p,x,d,s) (B_COPY(p,x)?PIXEL_MASK(p,x,d,s):0)
-
-         /* Combine 8 of these to get the full mask.  For the 1-bpp and 2-bpp
-          * cases the result needs replicating, for the 4-bpp case the above
-          * generates a full 32 bits.
-          */
-#        define MASK_EXPAND(m,d) ((m)*((d)==1?0x01010101:((d)==2?0x00010001:1)))
-
-#        define S_MASK(p,d,s) MASK_EXPAND(S_MASKx(p,0,d,s) + S_MASKx(p,1,d,s) +\
-            S_MASKx(p,2,d,s) + S_MASKx(p,3,d,s) + S_MASKx(p,4,d,s) +\
-            S_MASKx(p,5,d,s) + S_MASKx(p,6,d,s) + S_MASKx(p,7,d,s), d)
-
-#        define B_MASK(p,d,s) MASK_EXPAND(B_MASKx(p,0,d,s) + B_MASKx(p,1,d,s) +\
-            B_MASKx(p,2,d,s) + B_MASKx(p,3,d,s) + B_MASKx(p,4,d,s) +\
-            B_MASKx(p,5,d,s) + B_MASKx(p,6,d,s) + B_MASKx(p,7,d,s), d)
-
-#if PNG_USE_COMPILE_TIME_MASKS
-         /* Utility macros to construct all the masks for a depth/swap
-          * combination.  The 's' parameter says whether the format is PNG
-          * (big endian bytes) or not.  Only the three odd-numbered passes are
-          * required for the display/block algorithm.
-          */
-#        define S_MASKS(d,s) { S_MASK(0,d,s), S_MASK(1,d,s), S_MASK(2,d,s),\
-            S_MASK(3,d,s), S_MASK(4,d,s), S_MASK(5,d,s) }
-
-#        define B_MASKS(d,s) { B_MASK(1,d,s), B_MASK(3,d,s), B_MASK(5,d,s) }
-
-#        define DEPTH_INDEX(d) ((d)==1?0:((d)==2?1:2))
-
-         /* Hence the pre-compiled masks indexed by PACKSWAP (or not), depth and
-          * then pass:
-          */
-         static const png_uint_32 row_mask[2/*PACKSWAP*/][3/*depth*/][6] =
-         {
-            /* Little-endian byte masks for PACKSWAP */
-            { S_MASKS(1,0), S_MASKS(2,0), S_MASKS(4,0) },
-            /* Normal (big-endian byte) masks - PNG format */
-            { S_MASKS(1,1), S_MASKS(2,1), S_MASKS(4,1) }
-         };
-
-         /* display_mask has only three entries for the odd passes, so index by
-          * pass>>1.
-          */
-         static const png_uint_32 display_mask[2][3][3] =
-         {
-            /* Little-endian byte masks for PACKSWAP */
-            { B_MASKS(1,0), B_MASKS(2,0), B_MASKS(4,0) },
-            /* Normal (big-endian byte) masks - PNG format */
-            { B_MASKS(1,1), B_MASKS(2,1), B_MASKS(4,1) }
-         };
-
-#        define MASK(pass,depth,display,png)\
-            ((display)?display_mask[png][DEPTH_INDEX(depth)][pass>>1]:\
-               row_mask[png][DEPTH_INDEX(depth)][pass])
-
-#else /* !PNG_USE_COMPILE_TIME_MASKS */
-         /* This is the runtime alternative: it seems unlikely that this will
-          * ever be either smaller or faster than the compile time approach.
-          */
-#        define MASK(pass,depth,display,png)\
-            ((display)?B_MASK(pass,depth,png):S_MASK(pass,depth,png))
-#endif /* !USE_COMPILE_TIME_MASKS */
-
-         /* Use the appropriate mask to copy the required bits.  In some cases
-          * the byte mask will be 0 or 0xff; optimize these cases.  row_width is
-          * the number of pixels, but the code copies bytes, so it is necessary
-          * to special case the end.
-          */
-         png_uint_32 pixels_per_byte = 8 / pixel_depth;
-         png_uint_32 mask;
-
-#        ifdef PNG_READ_PACKSWAP_SUPPORTED
-         if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
-            mask = MASK(pass, pixel_depth, display, 0);
-
-         else
-#        endif
-         mask = MASK(pass, pixel_depth, display, 1);
-
-         for (;;)
-         {
-            png_uint_32 m;
-
-            /* It doesn't matter in the following if png_uint_32 has more than
-             * 32 bits because the high bits always match those in m<<24; it is,
-             * however, essential to use OR here, not +, because of this.
-             */
-            m = mask;
-            mask = (m >> 8) | (m << 24); /* rotate right to good compilers */
-            m &= 0xff;
-
-            if (m != 0) /* something to copy */
-            {
-               if (m != 0xff)
-                  *dp = (png_byte)((*dp & ~m) | (*sp & m));
-               else
-                  *dp = *sp;
-            }
-
-            /* NOTE: this may overwrite the last byte with garbage if the image
-             * is not an exact number of bytes wide; libpng has always done
-             * this.
-             */
-            if (row_width <= pixels_per_byte)
-               break; /* May need to restore part of the last byte */
-
-            row_width -= pixels_per_byte;
-            ++dp;
-            ++sp;
-         }
-      }
-
-      else /* pixel_depth >= 8 */
-      {
-         unsigned int bytes_to_copy, bytes_to_jump;
-
-         /* Validate the depth - it must be a multiple of 8 */
-         if (pixel_depth & 7)
-            png_error(png_ptr, "invalid user transform pixel depth");
-
-         pixel_depth >>= 3; /* now in bytes */
-         row_width *= pixel_depth;
-
-         /* Regardless of pass number the Adam 7 interlace always results in a
-          * fixed number of pixels to copy then to skip.  There may be a
-          * different number of pixels to skip at the start though.
-          */
-         {
-            unsigned int offset = PNG_PASS_START_COL(pass) * pixel_depth;
-
-            row_width -= offset;
-            dp += offset;
-            sp += offset;
-         }
-
-         /* Work out the bytes to copy. */
-         if (display != 0)
-         {
-            /* When doing the 'block' algorithm the pixel in the pass gets
-             * replicated to adjacent pixels.  This is why the even (0,2,4,6)
-             * passes are skipped above - the entire expanded row is copied.
-             */
-            bytes_to_copy = (1<<((6-pass)>>1)) * pixel_depth;
-
-            /* But don't allow this number to exceed the actual row width. */
-            if (bytes_to_copy > row_width)
-               bytes_to_copy = (unsigned int)/*SAFE*/row_width;
-         }
-
-         else /* normal row; Adam7 only ever gives us one pixel to copy. */
-            bytes_to_copy = pixel_depth;
-
-         /* In Adam7 there is a constant offset between where the pixels go. */
-         bytes_to_jump = PNG_PASS_COL_OFFSET(pass) * pixel_depth;
-
-         /* And simply copy these bytes.  Some optimization is possible here,
-          * depending on the value of 'bytes_to_copy'.  Special case the low
-          * byte counts, which we know to be frequent.
-          *
-          * Notice that these cases all 'return' rather than 'break' - this
-          * avoids an unnecessary test on whether to restore the last byte
-          * below.
-          */
-         switch (bytes_to_copy)
-         {
-            case 1:
-               for (;;)
-               {
-                  *dp = *sp;
-
-                  if (row_width <= bytes_to_jump)
-                     return;
-
-                  dp += bytes_to_jump;
-                  sp += bytes_to_jump;
-                  row_width -= bytes_to_jump;
-               }
-
-            case 2:
-               /* There is a possibility of a partial copy at the end here; this
-                * slows the code down somewhat.
-                */
-               do
-               {
-                  dp[0] = sp[0]; dp[1] = sp[1];
-
-                  if (row_width <= bytes_to_jump)
-                     return;
-
-                  sp += bytes_to_jump;
-                  dp += bytes_to_jump;
-                  row_width -= bytes_to_jump;
-               }
-               while (row_width > 1);
-
-               /* And there can only be one byte left at this point: */
-               *dp = *sp;
-               return;
-
-            case 3:
-               /* This can only be the RGB case, so each copy is exactly one
-                * pixel and it is not necessary to check for a partial copy.
-                */
-               for (;;)
-               {
-                  dp[0] = sp[0]; dp[1] = sp[1]; dp[2] = sp[2];
-
-                  if (row_width <= bytes_to_jump)
-                     return;
-
-                  sp += bytes_to_jump;
-                  dp += bytes_to_jump;
-                  row_width -= bytes_to_jump;
-               }
-
-            default:
-#if PNG_ALIGN_TYPE != PNG_ALIGN_NONE
-               /* Check for double byte alignment and, if possible, use a
-                * 16-bit copy.  Don't attempt this for narrow images - ones that
-                * are less than an interlace panel wide.  Don't attempt it for
-                * wide bytes_to_copy either - use the memcpy there.
-                */
-               if (bytes_to_copy < 16 /*else use memcpy*/ &&
-                   png_isaligned(dp, png_uint_16) &&
-                   png_isaligned(sp, png_uint_16) &&
-                   bytes_to_copy % (sizeof (png_uint_16)) == 0 &&
-                   bytes_to_jump % (sizeof (png_uint_16)) == 0)
-               {
-                  /* Everything is aligned for png_uint_16 copies, but try for
-                   * png_uint_32 first.
-                   */
-                  if (png_isaligned(dp, png_uint_32) &&
-                      png_isaligned(sp, png_uint_32) &&
-                      bytes_to_copy % (sizeof (png_uint_32)) == 0 &&
-                      bytes_to_jump % (sizeof (png_uint_32)) == 0)
-                  {
-                     png_uint_32p dp32 = png_aligncast(png_uint_32p,dp);
-                     png_const_uint_32p sp32 = png_aligncastconst(
-                         png_const_uint_32p, sp);
-                     size_t skip = (bytes_to_jump-bytes_to_copy) /
-                         (sizeof (png_uint_32));
-
-                     do
-                     {
-                        size_t c = bytes_to_copy;
-                        do
-                        {
-                           *dp32++ = *sp32++;
-                           c -= (sizeof (png_uint_32));
-                        }
-                        while (c > 0);
-
-                        if (row_width <= bytes_to_jump)
-                           return;
-
-                        dp32 += skip;
-                        sp32 += skip;
-                        row_width -= bytes_to_jump;
-                     }
-                     while (bytes_to_copy <= row_width);
-
-                     /* Get to here when the row_width truncates the final copy.
-                      * There will be 1-3 bytes left to copy, so don't try the
-                      * 16-bit loop below.
-                      */
-                     dp = (png_bytep)dp32;
-                     sp = (png_const_bytep)sp32;
-                     do
-                        *dp++ = *sp++;
-                     while (--row_width > 0);
-                     return;
-                  }
-
-                  /* Else do it in 16-bit quantities, but only if the size is
-                   * not too large.
-                   */
-                  else
-                  {
-                     png_uint_16p dp16 = png_aligncast(png_uint_16p, dp);
-                     png_const_uint_16p sp16 = png_aligncastconst(
-                        png_const_uint_16p, sp);
-                     size_t skip = (bytes_to_jump-bytes_to_copy) /
-                        (sizeof (png_uint_16));
-
-                     do
-                     {
-                        size_t c = bytes_to_copy;
-                        do
-                        {
-                           *dp16++ = *sp16++;
-                           c -= (sizeof (png_uint_16));
-                        }
-                        while (c > 0);
-
-                        if (row_width <= bytes_to_jump)
-                           return;
-
-                        dp16 += skip;
-                        sp16 += skip;
-                        row_width -= bytes_to_jump;
-                     }
-                     while (bytes_to_copy <= row_width);
-
-                     /* End of row - 1 byte left, bytes_to_copy > row_width: */
-                     dp = (png_bytep)dp16;
-                     sp = (png_const_bytep)sp16;
-                     do
-                        *dp++ = *sp++;
-                     while (--row_width > 0);
-                     return;
-                  }
-               }
-#endif /* ALIGN_TYPE code */
-
-               /* The true default - use a memcpy: */
-               for (;;)
-               {
-                  memcpy(dp, sp, bytes_to_copy);
-
-                  if (row_width <= bytes_to_jump)
-                     return;
-
-                  sp += bytes_to_jump;
-                  dp += bytes_to_jump;
-                  row_width -= bytes_to_jump;
-                  if (bytes_to_copy > row_width)
-                     bytes_to_copy = (unsigned int)/*SAFE*/row_width;
-               }
-         }
-
-         /* NOT REACHED*/
-      } /* pixel_depth >= 8 */
-
-      /* Here if pixel_depth < 8 to check 'end_ptr' below. */
-   }
-   else
-#endif /* READ_INTERLACING */
-
-   /* If here then the switch above wasn't used so just memcpy the whole row
-    * from the temporary row buffer (notice that this overwrites the end of the
-    * destination row if it is a partial byte.)
-    */
-   memcpy(dp, sp, PNG_ROWBYTES(pixel_depth, row_width));
-
-   /* Restore the overwritten bits from the last byte if necessary. */
-   if (end_ptr != NULL)
-      *end_ptr = (png_byte)((end_byte & end_mask) | (*end_ptr & ~end_mask));
-}
-
-#ifdef PNG_READ_INTERLACING_SUPPORTED
-void /* PRIVATE */
-png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass,
-    png_uint_32 transformations /* Because these may affect the byte layout */)
-{
-   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
-   /* Offset to next interlace block */
-   static const unsigned int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
-
-   png_debug(1, "in png_do_read_interlace");
-   if (row != NULL && row_info != NULL)
-   {
-      png_uint_32 final_width;
-
-      final_width = row_info->width * png_pass_inc[pass];
-
-      switch (row_info->pixel_depth)
-      {
-         case 1:
-         {
-            png_bytep sp = row + (size_t)((row_info->width - 1) >> 3);
-            png_bytep dp = row + (size_t)((final_width - 1) >> 3);
-            unsigned int sshift, dshift;
-            unsigned int s_start, s_end;
-            int s_inc;
-            int jstop = (int)png_pass_inc[pass];
-            png_byte v;
-            png_uint_32 i;
-            int j;
-
-#ifdef PNG_READ_PACKSWAP_SUPPORTED
-            if ((transformations & PNG_PACKSWAP) != 0)
-            {
-                sshift = ((row_info->width + 7) & 0x07);
-                dshift = ((final_width + 7) & 0x07);
-                s_start = 7;
-                s_end = 0;
-                s_inc = -1;
-            }
-
-            else
-#endif
-            {
-                sshift = 7 - ((row_info->width + 7) & 0x07);
-                dshift = 7 - ((final_width + 7) & 0x07);
-                s_start = 0;
-                s_end = 7;
-                s_inc = 1;
-            }
-
-            for (i = 0; i < row_info->width; i++)
-            {
-               v = (png_byte)((*sp >> sshift) & 0x01);
-               for (j = 0; j < jstop; j++)
-               {
-                  unsigned int tmp = *dp & (0x7f7f >> (7 - dshift));
-                  tmp |= (unsigned int)(v << dshift);
-                  *dp = (png_byte)(tmp & 0xff);
-
-                  if (dshift == s_end)
-                  {
-                     dshift = s_start;
-                     dp--;
-                  }
-
-                  else
-                     dshift = (unsigned int)((int)dshift + s_inc);
-               }
-
-               if (sshift == s_end)
-               {
-                  sshift = s_start;
-                  sp--;
-               }
-
-               else
-                  sshift = (unsigned int)((int)sshift + s_inc);
-            }
-            break;
-         }
-
-         case 2:
-         {
-            png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2);
-            png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2);
-            unsigned int sshift, dshift;
-            unsigned int s_start, s_end;
-            int s_inc;
-            int jstop = (int)png_pass_inc[pass];
-            png_uint_32 i;
-
-#ifdef PNG_READ_PACKSWAP_SUPPORTED
-            if ((transformations & PNG_PACKSWAP) != 0)
-            {
-               sshift = (((row_info->width + 3) & 0x03) << 1);
-               dshift = (((final_width + 3) & 0x03) << 1);
-               s_start = 6;
-               s_end = 0;
-               s_inc = -2;
-            }
-
-            else
-#endif
-            {
-               sshift = ((3 - ((row_info->width + 3) & 0x03)) << 1);
-               dshift = ((3 - ((final_width + 3) & 0x03)) << 1);
-               s_start = 0;
-               s_end = 6;
-               s_inc = 2;
-            }
-
-            for (i = 0; i < row_info->width; i++)
-            {
-               png_byte v;
-               int j;
-
-               v = (png_byte)((*sp >> sshift) & 0x03);
-               for (j = 0; j < jstop; j++)
-               {
-                  unsigned int tmp = *dp & (0x3f3f >> (6 - dshift));
-                  tmp |= (unsigned int)(v << dshift);
-                  *dp = (png_byte)(tmp & 0xff);
-
-                  if (dshift == s_end)
-                  {
-                     dshift = s_start;
-                     dp--;
-                  }
-
-                  else
-                     dshift = (unsigned int)((int)dshift + s_inc);
-               }
-
-               if (sshift == s_end)
-               {
-                  sshift = s_start;
-                  sp--;
-               }
-
-               else
-                  sshift = (unsigned int)((int)sshift + s_inc);
-            }
-            break;
-         }
-
-         case 4:
-         {
-            png_bytep sp = row + (size_t)((row_info->width - 1) >> 1);
-            png_bytep dp = row + (size_t)((final_width - 1) >> 1);
-            unsigned int sshift, dshift;
-            unsigned int s_start, s_end;
-            int s_inc;
-            png_uint_32 i;
-            int jstop = (int)png_pass_inc[pass];
-
-#ifdef PNG_READ_PACKSWAP_SUPPORTED
-            if ((transformations & PNG_PACKSWAP) != 0)
-            {
-               sshift = (((row_info->width + 1) & 0x01) << 2);
-               dshift = (((final_width + 1) & 0x01) << 2);
-               s_start = 4;
-               s_end = 0;
-               s_inc = -4;
-            }
-
-            else
-#endif
-            {
-               sshift = ((1 - ((row_info->width + 1) & 0x01)) << 2);
-               dshift = ((1 - ((final_width + 1) & 0x01)) << 2);
-               s_start = 0;
-               s_end = 4;
-               s_inc = 4;
-            }
-
-            for (i = 0; i < row_info->width; i++)
-            {
-               png_byte v = (png_byte)((*sp >> sshift) & 0x0f);
-               int j;
-
-               for (j = 0; j < jstop; j++)
-               {
-                  unsigned int tmp = *dp & (0xf0f >> (4 - dshift));
-                  tmp |= (unsigned int)(v << dshift);
-                  *dp = (png_byte)(tmp & 0xff);
-
-                  if (dshift == s_end)
-                  {
-                     dshift = s_start;
-                     dp--;
-                  }
-
-                  else
-                     dshift = (unsigned int)((int)dshift + s_inc);
-               }
-
-               if (sshift == s_end)
-               {
-                  sshift = s_start;
-                  sp--;
-               }
-
-               else
-                  sshift = (unsigned int)((int)sshift + s_inc);
-            }
-            break;
-         }
-
-         default:
-         {
-            size_t pixel_bytes = (row_info->pixel_depth >> 3);
-
-            png_bytep sp = row + (size_t)(row_info->width - 1)
-                * pixel_bytes;
-
-            png_bytep dp = row + (size_t)(final_width - 1) * pixel_bytes;
-
-            int jstop = (int)png_pass_inc[pass];
-            png_uint_32 i;
-
-            for (i = 0; i < row_info->width; i++)
-            {
-               png_byte v[8]; /* SAFE; pixel_depth does not exceed 64 */
-               int j;
-
-               memcpy(v, sp, pixel_bytes);
-
-               for (j = 0; j < jstop; j++)
-               {
-                  memcpy(dp, v, pixel_bytes);
-                  dp -= pixel_bytes;
-               }
-
-               sp -= pixel_bytes;
-            }
-            break;
-         }
-      }
-
-      row_info->width = final_width;
-      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, final_width);
-   }
-#ifndef PNG_READ_PACKSWAP_SUPPORTED
-   PNG_UNUSED(transformations)  /* Silence compiler warning */
-#endif
-}
-#endif /* READ_INTERLACING */
-
-static void
-png_read_filter_row_sub(png_row_infop row_info, png_bytep row,
-    png_const_bytep prev_row)
-{
-   size_t i;
-   size_t istop = row_info->rowbytes;
-   unsigned int bpp = (row_info->pixel_depth + 7) >> 3;
-   png_bytep rp = row + bpp;
-
-   PNG_UNUSED(prev_row)
-
-   for (i = bpp; i < istop; i++)
-   {
-      *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff);
-      rp++;
-   }
-}
-
-static void
-png_read_filter_row_up(png_row_infop row_info, png_bytep row,
-    png_const_bytep prev_row)
-{
-   size_t i;
-   size_t istop = row_info->rowbytes;
-   png_bytep rp = row;
-   png_const_bytep pp = prev_row;
-
-   for (i = 0; i < istop; i++)
-   {
-      *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
-      rp++;
-   }
-}
-
-static void
-png_read_filter_row_avg(png_row_infop row_info, png_bytep row,
-    png_const_bytep prev_row)
-{
-   size_t i;
-   png_bytep rp = row;
-   png_const_bytep pp = prev_row;
-   unsigned int bpp = (row_info->pixel_depth + 7) >> 3;
-   size_t istop = row_info->rowbytes - bpp;
-
-   for (i = 0; i < bpp; i++)
-   {
-      *rp = (png_byte)(((int)(*rp) +
-         ((int)(*pp++) / 2 )) & 0xff);
-
-      rp++;
-   }
-
-   for (i = 0; i < istop; i++)
-   {
-      *rp = (png_byte)(((int)(*rp) +
-         (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff);
-
-      rp++;
-   }
-}
-
-static void
-png_read_filter_row_paeth_1byte_pixel(png_row_infop row_info, png_bytep row,
-    png_const_bytep prev_row)
-{
-   png_bytep rp_end = row + row_info->rowbytes;
-   int a, c;
-
-   /* First pixel/byte */
-   c = *prev_row++;
-   a = *row + c;
-   *row++ = (png_byte)a;
-
-   /* Remainder */
-   while (row < rp_end)
-   {
-      int b, pa, pb, pc, p;
-
-      a &= 0xff; /* From previous iteration or start */
-      b = *prev_row++;
-
-      p = b - c;
-      pc = a - c;
-
-#ifdef PNG_USE_ABS
-      pa = abs(p);
-      pb = abs(pc);
-      pc = abs(p + pc);
-#else
-      pa = p < 0 ? -p : p;
-      pb = pc < 0 ? -pc : pc;
-      pc = (p + pc) < 0 ? -(p + pc) : p + pc;
-#endif
-
-      /* Find the best predictor, the least of pa, pb, pc favoring the earlier
-       * ones in the case of a tie.
-       */
-      if (pb < pa)
-      {
-         pa = pb; a = b;
-      }
-      if (pc < pa) a = c;
-
-      /* Calculate the current pixel in a, and move the previous row pixel to c
-       * for the next time round the loop
-       */
-      c = b;
-      a += *row;
-      *row++ = (png_byte)a;
-   }
-}
-
-static void
-png_read_filter_row_paeth_multibyte_pixel(png_row_infop row_info, png_bytep row,
-    png_const_bytep prev_row)
-{
-   unsigned int bpp = (row_info->pixel_depth + 7) >> 3;
-   png_bytep rp_end = row + bpp;
-
-   /* Process the first pixel in the row completely (this is the same as 'up'
-    * because there is only one candidate predictor for the first row).
-    */
-   while (row < rp_end)
-   {
-      int a = *row + *prev_row++;
-      *row++ = (png_byte)a;
-   }
-
-   /* Remainder */
-   rp_end = rp_end + (row_info->rowbytes - bpp);
-
-   while (row < rp_end)
-   {
-      int a, b, c, pa, pb, pc, p;
-
-      c = *(prev_row - bpp);
-      a = *(row - bpp);
-      b = *prev_row++;
-
-      p = b - c;
-      pc = a - c;
-
-#ifdef PNG_USE_ABS
-      pa = abs(p);
-      pb = abs(pc);
-      pc = abs(p + pc);
-#else
-      pa = p < 0 ? -p : p;
-      pb = pc < 0 ? -pc : pc;
-      pc = (p + pc) < 0 ? -(p + pc) : p + pc;
-#endif
-
-      if (pb < pa)
-      {
-         pa = pb; a = b;
-      }
-      if (pc < pa) a = c;
-
-      a += *row;
-      *row++ = (png_byte)a;
-   }
-}
-
-static void
-png_init_filter_functions(png_structrp pp)
-   /* This function is called once for every PNG image (except for PNG images
-    * that only use PNG_FILTER_VALUE_NONE for all rows) to set the
-    * implementations required to reverse the filtering of PNG rows.  Reversing
-    * the filter is the first transformation performed on the row data.  It is
-    * performed in place, therefore an implementation can be selected based on
-    * the image pixel format.  If the implementation depends on image width then
-    * take care to ensure that it works correctly if the image is interlaced -
-    * interlacing causes the actual row width to vary.
-    */
-{
-   unsigned int bpp = (pp->pixel_depth + 7) >> 3;
-
-   pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub;
-   pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up;
-   pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg;
-   if (bpp == 1)
-      pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
-         png_read_filter_row_paeth_1byte_pixel;
-   else
-      pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
-         png_read_filter_row_paeth_multibyte_pixel;
-
-#ifdef PNG_FILTER_OPTIMIZATIONS
-   /* To use this define PNG_FILTER_OPTIMIZATIONS as the name of a function to
-    * call to install hardware optimizations for the above functions; simply
-    * replace whatever elements of the pp->read_filter[] array with a hardware
-    * specific (or, for that matter, generic) optimization.
-    *
-    * To see an example of this examine what configure.ac does when
-    * --enable-arm-neon is specified on the command line.
-    */
-   PNG_FILTER_OPTIMIZATIONS(pp, bpp);
-#endif
-}
-
-void /* PRIVATE */
-png_read_filter_row(png_structrp pp, png_row_infop row_info, png_bytep row,
-    png_const_bytep prev_row, int filter)
-{
-   /* OPTIMIZATION: DO NOT MODIFY THIS FUNCTION, instead #define
-    * PNG_FILTER_OPTIMIZATIONS to a function that overrides the generic
-    * implementations.  See png_init_filter_functions above.
-    */
-   if (filter > PNG_FILTER_VALUE_NONE && filter < PNG_FILTER_VALUE_LAST)
-   {
-      if (pp->read_filter[0] == NULL)
-         png_init_filter_functions(pp);
-
-      pp->read_filter[filter-1](row_info, row, prev_row);
-   }
-}
-
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-void /* PRIVATE */
-png_read_IDAT_data(png_structrp png_ptr, png_bytep output,
-    png_alloc_size_t avail_out)
-{
-   /* Loop reading IDATs and decompressing the result into output[avail_out] */
-   png_ptr->zstream.next_out = output;
-   png_ptr->zstream.avail_out = 0; /* safety: set below */
-
-   if (output == NULL)
-      avail_out = 0;
-
-   do
-   {
-      int ret;
-      png_byte tmpbuf[PNG_INFLATE_BUF_SIZE];
-
-      if (png_ptr->zstream.avail_in == 0)
-      {
-         uInt avail_in;
-         png_bytep buffer;
-
-         while (png_ptr->idat_size == 0)
-         {
-            png_crc_finish(png_ptr, 0);
-
-            png_ptr->idat_size = png_read_chunk_header(png_ptr);
-            /* This is an error even in the 'check' case because the code just
-             * consumed a non-IDAT header.
-             */
-            if (png_ptr->chunk_name != png_IDAT)
-               png_error(png_ptr, "Not enough image data");
-         }
-
-         avail_in = png_ptr->IDAT_read_size;
-
-         if (avail_in > png_ptr->idat_size)
-            avail_in = (uInt)png_ptr->idat_size;
-
-         /* A PNG with a gradually increasing IDAT size will defeat this attempt
-          * to minimize memory usage by causing lots of re-allocs, but
-          * realistically doing IDAT_read_size re-allocs is not likely to be a
-          * big problem.
-          */
-         buffer = png_read_buffer(png_ptr, avail_in, 0/*error*/);
-
-         png_crc_read(png_ptr, buffer, avail_in);
-         png_ptr->idat_size -= avail_in;
-
-         png_ptr->zstream.next_in = buffer;
-         png_ptr->zstream.avail_in = avail_in;
-      }
-
-      /* And set up the output side. */
-      if (output != NULL) /* standard read */
-      {
-         uInt out = ZLIB_IO_MAX;
-
-         if (out > avail_out)
-            out = (uInt)avail_out;
-
-         avail_out -= out;
-         png_ptr->zstream.avail_out = out;
-      }
-
-      else /* after last row, checking for end */
-      {
-         png_ptr->zstream.next_out = tmpbuf;
-         png_ptr->zstream.avail_out = (sizeof tmpbuf);
-      }
-
-      /* Use NO_FLUSH; this gives zlib the maximum opportunity to optimize the
-       * process.  If the LZ stream is truncated the sequential reader will
-       * terminally damage the stream, above, by reading the chunk header of the
-       * following chunk (it then exits with png_error).
-       *
-       * TODO: deal more elegantly with truncated IDAT lists.
-       */
-      ret = PNG_INFLATE(png_ptr, Z_NO_FLUSH);
-
-      /* Take the unconsumed output back. */
-      if (output != NULL)
-         avail_out += png_ptr->zstream.avail_out;
-
-      else /* avail_out counts the extra bytes */
-         avail_out += (sizeof tmpbuf) - png_ptr->zstream.avail_out;
-
-      png_ptr->zstream.avail_out = 0;
-
-      if (ret == Z_STREAM_END)
-      {
-         /* Do this for safety; we won't read any more into this row. */
-         png_ptr->zstream.next_out = NULL;
-
-         png_ptr->mode |= PNG_AFTER_IDAT;
-         png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
-
-         if (png_ptr->zstream.avail_in > 0 || png_ptr->idat_size > 0)
-            png_chunk_benign_error(png_ptr, "Extra compressed data");
-         break;
-      }
-
-      if (ret != Z_OK)
-      {
-         png_zstream_error(png_ptr, ret);
-
-         if (output != NULL)
-            png_chunk_error(png_ptr, png_ptr->zstream.msg);
-
-         else /* checking */
-         {
-            png_chunk_benign_error(png_ptr, png_ptr->zstream.msg);
-            return;
-         }
-      }
-   } while (avail_out > 0);
-
-   if (avail_out > 0)
-   {
-      /* The stream ended before the image; this is the same as too few IDATs so
-       * should be handled the same way.
-       */
-      if (output != NULL)
-         png_error(png_ptr, "Not enough image data");
-
-      else /* the deflate stream contained extra data */
-         png_chunk_benign_error(png_ptr, "Too much image data");
-   }
-}
-
-void /* PRIVATE */
-png_read_finish_IDAT(png_structrp png_ptr)
-{
-   /* We don't need any more data and the stream should have ended, however the
-    * LZ end code may actually not have been processed.  In this case we must
-    * read it otherwise stray unread IDAT data or, more likely, an IDAT chunk
-    * may still remain to be consumed.
-    */
-   if ((png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) == 0)
-   {
-      /* The NULL causes png_read_IDAT_data to swallow any remaining bytes in
-       * the compressed stream, but the stream may be damaged too, so even after
-       * this call we may need to terminate the zstream ownership.
-       */
-      png_read_IDAT_data(png_ptr, NULL, 0);
-      png_ptr->zstream.next_out = NULL; /* safety */
-
-      /* Now clear everything out for safety; the following may not have been
-       * done.
-       */
-      if ((png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) == 0)
-      {
-         png_ptr->mode |= PNG_AFTER_IDAT;
-         png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
-      }
-   }
-
-   /* If the zstream has not been released do it now *and* terminate the reading
-    * of the final IDAT chunk.
-    */
-   if (png_ptr->zowner == png_IDAT)
-   {
-      /* Always do this; the pointers otherwise point into the read buffer. */
-      png_ptr->zstream.next_in = NULL;
-      png_ptr->zstream.avail_in = 0;
-
-      /* Now we no longer own the zstream. */
-      png_ptr->zowner = 0;
-
-      /* The slightly weird semantics of the sequential IDAT reading is that we
-       * are always in or at the end of an IDAT chunk, so we always need to do a
-       * crc_finish here.  If idat_size is non-zero we also need to read the
-       * spurious bytes at the end of the chunk now.
-       */
-      (void)png_crc_finish(png_ptr, png_ptr->idat_size);
-   }
-}
-
-void /* PRIVATE */
-png_read_finish_row(png_structrp png_ptr)
-{
-   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
-
-   /* Start of interlace block */
-   static const png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
-
-   /* Offset to next interlace block */
-   static const png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
-
-   /* Start of interlace block in the y direction */
-   static const png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
-
-   /* Offset to next interlace block in the y direction */
-   static const png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
-
-   png_debug(1, "in png_read_finish_row");
-   png_ptr->row_number++;
-   if (png_ptr->row_number < png_ptr->num_rows)
-      return;
-
-   if (png_ptr->interlaced != 0)
-   {
-      png_ptr->row_number = 0;
-
-      /* TO DO: don't do this if prev_row isn't needed (requires
-       * read-ahead of the next row's filter byte.
-       */
-      memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
-
-      do
-      {
-         png_ptr->pass++;
-
-         if (png_ptr->pass >= 7)
-            break;
-
-         png_ptr->iwidth = (png_ptr->width +
-            png_pass_inc[png_ptr->pass] - 1 -
-            png_pass_start[png_ptr->pass]) /
-            png_pass_inc[png_ptr->pass];
-
-         if ((png_ptr->transformations & PNG_INTERLACE) == 0)
-         {
-            png_ptr->num_rows = (png_ptr->height +
-                png_pass_yinc[png_ptr->pass] - 1 -
-                png_pass_ystart[png_ptr->pass]) /
-                png_pass_yinc[png_ptr->pass];
-         }
-
-         else  /* if (png_ptr->transformations & PNG_INTERLACE) */
-            break; /* libpng deinterlacing sees every row */
-
-      } while (png_ptr->num_rows == 0 || png_ptr->iwidth == 0);
-
-      if (png_ptr->pass < 7)
-         return;
-   }
-
-   /* Here after at the end of the last row of the last pass. */
-   png_read_finish_IDAT(png_ptr);
-}
-#endif /* SEQUENTIAL_READ */
-
-void /* PRIVATE */
-png_read_start_row(png_structrp png_ptr)
-{
-   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
-
-   /* Start of interlace block */
-   static const png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
-
-   /* Offset to next interlace block */
-   static const png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
-
-   /* Start of interlace block in the y direction */
-   static const png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
-
-   /* Offset to next interlace block in the y direction */
-   static const png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
-
-   unsigned int max_pixel_depth;
-   size_t row_bytes;
-
-   png_debug(1, "in png_read_start_row");
-
-#ifdef PNG_READ_TRANSFORMS_SUPPORTED
-   png_init_read_transformations(png_ptr);
-#endif
-   if (png_ptr->interlaced != 0)
-   {
-      if ((png_ptr->transformations & PNG_INTERLACE) == 0)
-         png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
-             png_pass_ystart[0]) / png_pass_yinc[0];
-
-      else
-         png_ptr->num_rows = png_ptr->height;
-
-      png_ptr->iwidth = (png_ptr->width +
-          png_pass_inc[png_ptr->pass] - 1 -
-          png_pass_start[png_ptr->pass]) /
-          png_pass_inc[png_ptr->pass];
-   }
-
-   else
-   {
-      png_ptr->num_rows = png_ptr->height;
-      png_ptr->iwidth = png_ptr->width;
-   }
-
-   max_pixel_depth = (unsigned int)png_ptr->pixel_depth;
-
-   /* WARNING: * png_read_transform_info (pngrtran.c) performs a simpler set of
-    * calculations to calculate the final pixel depth, then
-    * png_do_read_transforms actually does the transforms.  This means that the
-    * code which effectively calculates this value is actually repeated in three
-    * separate places.  They must all match.  Innocent changes to the order of
-    * transformations can and will break libpng in a way that causes memory
-    * overwrites.
-    *
-    * TODO: fix this.
-    */
-#ifdef PNG_READ_PACK_SUPPORTED
-   if ((png_ptr->transformations & PNG_PACK) != 0 && png_ptr->bit_depth < 8)
-      max_pixel_depth = 8;
-#endif
-
-#ifdef PNG_READ_EXPAND_SUPPORTED
-   if ((png_ptr->transformations & PNG_EXPAND) != 0)
-   {
-      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-      {
-         if (png_ptr->num_trans != 0)
-            max_pixel_depth = 32;
-
-         else
-            max_pixel_depth = 24;
-      }
-
-      else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
-      {
-         if (max_pixel_depth < 8)
-            max_pixel_depth = 8;
-
-         if (png_ptr->num_trans != 0)
-            max_pixel_depth *= 2;
-      }
-
-      else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
-      {
-         if (png_ptr->num_trans != 0)
-         {
-            max_pixel_depth *= 4;
-            max_pixel_depth /= 3;
-         }
-      }
-   }
-#endif
-
-#ifdef PNG_READ_EXPAND_16_SUPPORTED
-   if ((png_ptr->transformations & PNG_EXPAND_16) != 0)
-   {
-#  ifdef PNG_READ_EXPAND_SUPPORTED
-      /* In fact it is an error if it isn't supported, but checking is
-       * the safe way.
-       */
-      if ((png_ptr->transformations & PNG_EXPAND) != 0)
-      {
-         if (png_ptr->bit_depth < 16)
-            max_pixel_depth *= 2;
-      }
-      else
-#  endif
-      png_ptr->transformations &= ~PNG_EXPAND_16;
-   }
-#endif
-
-#ifdef PNG_READ_FILLER_SUPPORTED
-   if ((png_ptr->transformations & (PNG_FILLER)) != 0)
-   {
-      if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
-      {
-         if (max_pixel_depth <= 8)
-            max_pixel_depth = 16;
-
-         else
-            max_pixel_depth = 32;
-      }
-
-      else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB ||
-         png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-      {
-         if (max_pixel_depth <= 32)
-            max_pixel_depth = 32;
-
-         else
-            max_pixel_depth = 64;
-      }
-   }
-#endif
-
-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
-   if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0)
-   {
-      if (
-#ifdef PNG_READ_EXPAND_SUPPORTED
-          (png_ptr->num_trans != 0 &&
-          (png_ptr->transformations & PNG_EXPAND) != 0) ||
-#endif
-#ifdef PNG_READ_FILLER_SUPPORTED
-          (png_ptr->transformations & (PNG_FILLER)) != 0 ||
-#endif
-          png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
-      {
-         if (max_pixel_depth <= 16)
-            max_pixel_depth = 32;
-
-         else
-            max_pixel_depth = 64;
-      }
-
-      else
-      {
-         if (max_pixel_depth <= 8)
-         {
-            if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
-               max_pixel_depth = 32;
-
-            else
-               max_pixel_depth = 24;
-         }
-
-         else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
-            max_pixel_depth = 64;
-
-         else
-            max_pixel_depth = 48;
-      }
-   }
-#endif
-
-#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \
-defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
-   if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0)
-   {
-      unsigned int user_pixel_depth = png_ptr->user_transform_depth *
-         png_ptr->user_transform_channels;
-
-      if (user_pixel_depth > max_pixel_depth)
-         max_pixel_depth = user_pixel_depth;
-   }
-#endif
-
-   /* This value is stored in png_struct and double checked in the row read
-    * code.
-    */
-   png_ptr->maximum_pixel_depth = (png_byte)max_pixel_depth;
-   png_ptr->transformed_pixel_depth = 0; /* calculated on demand */
-
-   /* Align the width on the next larger 8 pixels.  Mainly used
-    * for interlacing
-    */
-   row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7));
-   /* Calculate the maximum bytes needed, adding a byte and a pixel
-    * for safety's sake
-    */
-   row_bytes = PNG_ROWBYTES(max_pixel_depth, row_bytes) +
-       1 + ((max_pixel_depth + 7) >> 3U);
-
-#ifdef PNG_MAX_MALLOC_64K
-   if (row_bytes > (png_uint_32)65536L)
-      png_error(png_ptr, "This image requires a row greater than 64KB");
-#endif
-
-   if (row_bytes + 48 > png_ptr->old_big_row_buf_size)
-   {
-      png_free(png_ptr, png_ptr->big_row_buf);
-      png_free(png_ptr, png_ptr->big_prev_row);
-
-      if (png_ptr->interlaced != 0)
-         png_ptr->big_row_buf = (png_bytep)png_calloc(png_ptr,
-             row_bytes + 48);
-
-      else
-         png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes + 48);
-
-      png_ptr->big_prev_row = (png_bytep)png_malloc(png_ptr, row_bytes + 48);
-
-#ifdef PNG_ALIGNED_MEMORY_SUPPORTED
-      /* Use 16-byte aligned memory for row_buf with at least 16 bytes
-       * of padding before and after row_buf; treat prev_row similarly.
-       * NOTE: the alignment is to the start of the pixels, one beyond the start
-       * of the buffer, because of the filter byte.  Prior to libpng 1.5.6 this
-       * was incorrect; the filter byte was aligned, which had the exact
-       * opposite effect of that intended.
-       */
-      {
-         png_bytep temp = png_ptr->big_row_buf + 32;
-         int extra = (int)((temp - (png_bytep)0) & 0x0f);
-         png_ptr->row_buf = temp - extra - 1/*filter byte*/;
-
-         temp = png_ptr->big_prev_row + 32;
-         extra = (int)((temp - (png_bytep)0) & 0x0f);
-         png_ptr->prev_row = temp - extra - 1/*filter byte*/;
-      }
-
-#else
-      /* Use 31 bytes of padding before and 17 bytes after row_buf. */
-      png_ptr->row_buf = png_ptr->big_row_buf + 31;
-      png_ptr->prev_row = png_ptr->big_prev_row + 31;
-#endif
-      png_ptr->old_big_row_buf_size = row_bytes + 48;
-   }
-
-#ifdef PNG_MAX_MALLOC_64K
-   if (png_ptr->rowbytes > 65535)
-      png_error(png_ptr, "This image requires a row greater than 64KB");
-
-#endif
-   if (png_ptr->rowbytes > (PNG_SIZE_MAX - 1))
-      png_error(png_ptr, "Row has too many bytes to allocate in memory");
-
-   memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
-
-   png_debug1(3, "width = %u,", png_ptr->width);
-   png_debug1(3, "height = %u,", png_ptr->height);
-   png_debug1(3, "iwidth = %u,", png_ptr->iwidth);
-   png_debug1(3, "num_rows = %u,", png_ptr->num_rows);
-   png_debug1(3, "rowbytes = %lu,", (unsigned long)png_ptr->rowbytes);
-   png_debug1(3, "irowbytes = %lu",
-       (unsigned long)PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1);
-
-   /* The sequential reader needs a buffer for IDAT, but the progressive reader
-    * does not, so free the read buffer now regardless; the sequential reader
-    * reallocates it on demand.
-    */
-   if (png_ptr->read_buffer != NULL)
-   {
-      png_bytep buffer = png_ptr->read_buffer;
-
-      png_ptr->read_buffer_size = 0;
-      png_ptr->read_buffer = NULL;
-      png_free(png_ptr, buffer);
-   }
-
-   /* Finally claim the zstream for the inflate of the IDAT data, use the bits
-    * value from the stream (note that this will result in a fatal error if the
-    * IDAT stream has a bogus deflate header window_bits value, but this should
-    * not be happening any longer!)
-    */
-   if (png_inflate_claim(png_ptr, png_IDAT) != Z_OK)
-      png_error(png_ptr, png_ptr->zstream.msg);
-
-   png_ptr->flags |= PNG_FLAG_ROW_INIT;
-}
-#endif /* READ */
diff --git a/third_party/libpng16/pngset.c b/third_party/libpng16/pngset.c
deleted file mode 100644
index ec75dbe..0000000
--- a/third_party/libpng16/pngset.c
+++ /dev/null
@@ -1,1802 +0,0 @@
-
-/* pngset.c - storage of image information into info struct
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 1998-2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- *
- * The functions here are used during reads to store data from the file
- * into the info struct, and during writes to store application data
- * into the info struct for writing into the file.  This abstracts the
- * info struct and allows us to change the structure in the future.
- */
-
-#include "pngpriv.h"
-
-#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
-
-#ifdef PNG_bKGD_SUPPORTED
-void PNGAPI
-png_set_bKGD(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_const_color_16p background)
-{
-   png_debug1(1, "in %s storage function", "bKGD");
-
-   if (png_ptr == NULL || info_ptr == NULL || background == NULL)
-      return;
-
-   info_ptr->background = *background;
-   info_ptr->valid |= PNG_INFO_bKGD;
-}
-#endif
-
-#ifdef PNG_cHRM_SUPPORTED
-void PNGFAPI
-png_set_cHRM_fixed(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x,
-    png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y,
-    png_fixed_point blue_x, png_fixed_point blue_y)
-{
-   png_xy xy;
-
-   png_debug1(1, "in %s storage function", "cHRM fixed");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   xy.redx = red_x;
-   xy.redy = red_y;
-   xy.greenx = green_x;
-   xy.greeny = green_y;
-   xy.bluex = blue_x;
-   xy.bluey = blue_y;
-   xy.whitex = white_x;
-   xy.whitey = white_y;
-
-   if (png_colorspace_set_chromaticities(png_ptr, &info_ptr->colorspace, &xy,
-       2/* override with app values*/) != 0)
-      info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM;
-
-   png_colorspace_sync_info(png_ptr, info_ptr);
-}
-
-void PNGFAPI
-png_set_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_fixed_point int_red_X, png_fixed_point int_red_Y,
-    png_fixed_point int_red_Z, png_fixed_point int_green_X,
-    png_fixed_point int_green_Y, png_fixed_point int_green_Z,
-    png_fixed_point int_blue_X, png_fixed_point int_blue_Y,
-    png_fixed_point int_blue_Z)
-{
-   png_XYZ XYZ;
-
-   png_debug1(1, "in %s storage function", "cHRM XYZ fixed");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   XYZ.red_X = int_red_X;
-   XYZ.red_Y = int_red_Y;
-   XYZ.red_Z = int_red_Z;
-   XYZ.green_X = int_green_X;
-   XYZ.green_Y = int_green_Y;
-   XYZ.green_Z = int_green_Z;
-   XYZ.blue_X = int_blue_X;
-   XYZ.blue_Y = int_blue_Y;
-   XYZ.blue_Z = int_blue_Z;
-
-   if (png_colorspace_set_endpoints(png_ptr, &info_ptr->colorspace,
-       &XYZ, 2) != 0)
-      info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM;
-
-   png_colorspace_sync_info(png_ptr, info_ptr);
-}
-
-#  ifdef PNG_FLOATING_POINT_SUPPORTED
-void PNGAPI
-png_set_cHRM(png_const_structrp png_ptr, png_inforp info_ptr,
-    double white_x, double white_y, double red_x, double red_y,
-    double green_x, double green_y, double blue_x, double blue_y)
-{
-   png_set_cHRM_fixed(png_ptr, info_ptr,
-       png_fixed(png_ptr, white_x, "cHRM White X"),
-       png_fixed(png_ptr, white_y, "cHRM White Y"),
-       png_fixed(png_ptr, red_x, "cHRM Red X"),
-       png_fixed(png_ptr, red_y, "cHRM Red Y"),
-       png_fixed(png_ptr, green_x, "cHRM Green X"),
-       png_fixed(png_ptr, green_y, "cHRM Green Y"),
-       png_fixed(png_ptr, blue_x, "cHRM Blue X"),
-       png_fixed(png_ptr, blue_y, "cHRM Blue Y"));
-}
-
-void PNGAPI
-png_set_cHRM_XYZ(png_const_structrp png_ptr, png_inforp info_ptr, double red_X,
-    double red_Y, double red_Z, double green_X, double green_Y, double green_Z,
-    double blue_X, double blue_Y, double blue_Z)
-{
-   png_set_cHRM_XYZ_fixed(png_ptr, info_ptr,
-       png_fixed(png_ptr, red_X, "cHRM Red X"),
-       png_fixed(png_ptr, red_Y, "cHRM Red Y"),
-       png_fixed(png_ptr, red_Z, "cHRM Red Z"),
-       png_fixed(png_ptr, green_X, "cHRM Green X"),
-       png_fixed(png_ptr, green_Y, "cHRM Green Y"),
-       png_fixed(png_ptr, green_Z, "cHRM Green Z"),
-       png_fixed(png_ptr, blue_X, "cHRM Blue X"),
-       png_fixed(png_ptr, blue_Y, "cHRM Blue Y"),
-       png_fixed(png_ptr, blue_Z, "cHRM Blue Z"));
-}
-#  endif /* FLOATING_POINT */
-
-#endif /* cHRM */
-
-#ifdef PNG_eXIf_SUPPORTED
-void PNGAPI
-png_set_eXIf(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_bytep eXIf_buf)
-{
-  png_warning(png_ptr, "png_set_eXIf does not work; use png_set_eXIf_1");
-  PNG_UNUSED(info_ptr)
-  PNG_UNUSED(eXIf_buf)
-}
-
-void PNGAPI
-png_set_eXIf_1(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_uint_32 num_exif, png_bytep eXIf_buf)
-{
-   int i;
-
-   png_debug1(1, "in %s storage function", "eXIf");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   if (info_ptr->exif)
-   {
-      png_free(png_ptr, info_ptr->exif);
-      info_ptr->exif = NULL;
-   }
-
-   info_ptr->num_exif = num_exif;
-
-   info_ptr->exif = png_voidcast(png_bytep, png_malloc_warn(png_ptr,
-       info_ptr->num_exif));
-
-   if (info_ptr->exif == NULL)
-   {
-      png_warning(png_ptr, "Insufficient memory for eXIf chunk data");
-      return;
-   }
-
-   info_ptr->free_me |= PNG_FREE_EXIF;
-
-   for (i = 0; i < (int) info_ptr->num_exif; i++)
-      info_ptr->exif[i] = eXIf_buf[i];
-
-   info_ptr->valid |= PNG_INFO_eXIf;
-}
-#endif /* eXIf */
-
-#ifdef PNG_gAMA_SUPPORTED
-void PNGFAPI
-png_set_gAMA_fixed(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_fixed_point file_gamma)
-{
-   png_debug1(1, "in %s storage function", "gAMA");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   png_colorspace_set_gamma(png_ptr, &info_ptr->colorspace, file_gamma);
-   png_colorspace_sync_info(png_ptr, info_ptr);
-}
-
-#  ifdef PNG_FLOATING_POINT_SUPPORTED
-void PNGAPI
-png_set_gAMA(png_const_structrp png_ptr, png_inforp info_ptr, double file_gamma)
-{
-   png_set_gAMA_fixed(png_ptr, info_ptr, png_fixed(png_ptr, file_gamma,
-       "png_set_gAMA"));
-}
-#  endif
-#endif
-
-#ifdef PNG_hIST_SUPPORTED
-void PNGAPI
-png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_const_uint_16p hist)
-{
-   int i;
-
-   png_debug1(1, "in %s storage function", "hIST");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   if (info_ptr->num_palette == 0 || info_ptr->num_palette
-       > PNG_MAX_PALETTE_LENGTH)
-   {
-      png_warning(png_ptr,
-          "Invalid palette size, hIST allocation skipped");
-
-      return;
-   }
-
-   png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0);
-
-   /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in
-    * version 1.2.1
-    */
-   info_ptr->hist = png_voidcast(png_uint_16p, png_malloc_warn(png_ptr,
-       PNG_MAX_PALETTE_LENGTH * (sizeof (png_uint_16))));
-
-   if (info_ptr->hist == NULL)
-   {
-      png_warning(png_ptr, "Insufficient memory for hIST chunk data");
-
-      return;
-   }
-
-   info_ptr->free_me |= PNG_FREE_HIST;
-
-   for (i = 0; i < info_ptr->num_palette; i++)
-      info_ptr->hist[i] = hist[i];
-
-   info_ptr->valid |= PNG_INFO_hIST;
-}
-#endif
-
-void PNGAPI
-png_set_IHDR(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_uint_32 width, png_uint_32 height, int bit_depth,
-    int color_type, int interlace_type, int compression_type,
-    int filter_type)
-{
-   png_debug1(1, "in %s storage function", "IHDR");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   info_ptr->width = width;
-   info_ptr->height = height;
-   info_ptr->bit_depth = (png_byte)bit_depth;
-   info_ptr->color_type = (png_byte)color_type;
-   info_ptr->compression_type = (png_byte)compression_type;
-   info_ptr->filter_type = (png_byte)filter_type;
-   info_ptr->interlace_type = (png_byte)interlace_type;
-
-   png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height,
-       info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type,
-       info_ptr->compression_type, info_ptr->filter_type);
-
-   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-      info_ptr->channels = 1;
-
-   else if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
-      info_ptr->channels = 3;
-
-   else
-      info_ptr->channels = 1;
-
-   if ((info_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0)
-      info_ptr->channels++;
-
-   info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth);
-
-   info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width);
-}
-
-#ifdef PNG_oFFs_SUPPORTED
-void PNGAPI
-png_set_oFFs(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_int_32 offset_x, png_int_32 offset_y, int unit_type)
-{
-   png_debug1(1, "in %s storage function", "oFFs");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   info_ptr->x_offset = offset_x;
-   info_ptr->y_offset = offset_y;
-   info_ptr->offset_unit_type = (png_byte)unit_type;
-   info_ptr->valid |= PNG_INFO_oFFs;
-}
-#endif
-
-#ifdef PNG_pCAL_SUPPORTED
-void PNGAPI
-png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_const_charp purpose, png_int_32 X0, png_int_32 X1, int type,
-    int nparams, png_const_charp units, png_charpp params)
-{
-   size_t length;
-   int i;
-
-   png_debug1(1, "in %s storage function", "pCAL");
-
-   if (png_ptr == NULL || info_ptr == NULL || purpose == NULL || units == NULL
-       || (nparams > 0 && params == NULL))
-      return;
-
-   length = strlen(purpose) + 1;
-   png_debug1(3, "allocating purpose for info (%lu bytes)",
-       (unsigned long)length);
-
-   /* TODO: validate format of calibration name and unit name */
-
-   /* Check that the type matches the specification. */
-   if (type < 0 || type > 3)
-   {
-      png_chunk_report(png_ptr, "Invalid pCAL equation type",
-            PNG_CHUNK_WRITE_ERROR);
-      return;
-   }
-
-   if (nparams < 0 || nparams > 255)
-   {
-      png_chunk_report(png_ptr, "Invalid pCAL parameter count",
-            PNG_CHUNK_WRITE_ERROR);
-      return;
-   }
-
-   /* Validate params[nparams] */
-   for (i=0; i<nparams; ++i)
-   {
-      if (params[i] == NULL ||
-          !png_check_fp_string(params[i], strlen(params[i])))
-      {
-         png_chunk_report(png_ptr, "Invalid format for pCAL parameter",
-               PNG_CHUNK_WRITE_ERROR);
-         return;
-      }
-   }
-
-   info_ptr->pcal_purpose = png_voidcast(png_charp,
-       png_malloc_warn(png_ptr, length));
-
-   if (info_ptr->pcal_purpose == NULL)
-   {
-      png_chunk_report(png_ptr, "Insufficient memory for pCAL purpose",
-            PNG_CHUNK_WRITE_ERROR);
-      return;
-   }
-
-   memcpy(info_ptr->pcal_purpose, purpose, length);
-
-   png_debug(3, "storing X0, X1, type, and nparams in info");
-   info_ptr->pcal_X0 = X0;
-   info_ptr->pcal_X1 = X1;
-   info_ptr->pcal_type = (png_byte)type;
-   info_ptr->pcal_nparams = (png_byte)nparams;
-
-   length = strlen(units) + 1;
-   png_debug1(3, "allocating units for info (%lu bytes)",
-       (unsigned long)length);
-
-   info_ptr->pcal_units = png_voidcast(png_charp,
-       png_malloc_warn(png_ptr, length));
-
-   if (info_ptr->pcal_units == NULL)
-   {
-      png_warning(png_ptr, "Insufficient memory for pCAL units");
-
-      return;
-   }
-
-   memcpy(info_ptr->pcal_units, units, length);
-
-   info_ptr->pcal_params = png_voidcast(png_charpp, png_malloc_warn(png_ptr,
-       (size_t)(((unsigned int)nparams + 1) * (sizeof (png_charp)))));
-
-   if (info_ptr->pcal_params == NULL)
-   {
-      png_warning(png_ptr, "Insufficient memory for pCAL params");
-
-      return;
-   }
-
-   memset(info_ptr->pcal_params, 0, ((unsigned int)nparams + 1) *
-       (sizeof (png_charp)));
-
-   for (i = 0; i < nparams; i++)
-   {
-      length = strlen(params[i]) + 1;
-      png_debug2(3, "allocating parameter %d for info (%lu bytes)", i,
-          (unsigned long)length);
-
-      info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length);
-
-      if (info_ptr->pcal_params[i] == NULL)
-      {
-         png_warning(png_ptr, "Insufficient memory for pCAL parameter");
-
-         return;
-      }
-
-      memcpy(info_ptr->pcal_params[i], params[i], length);
-   }
-
-   info_ptr->valid |= PNG_INFO_pCAL;
-   info_ptr->free_me |= PNG_FREE_PCAL;
-}
-#endif
-
-#ifdef PNG_sCAL_SUPPORTED
-void PNGAPI
-png_set_sCAL_s(png_const_structrp png_ptr, png_inforp info_ptr,
-    int unit, png_const_charp swidth, png_const_charp sheight)
-{
-   size_t lengthw = 0, lengthh = 0;
-
-   png_debug1(1, "in %s storage function", "sCAL");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   /* Double check the unit (should never get here with an invalid
-    * unit unless this is an API call.)
-    */
-   if (unit != 1 && unit != 2)
-      png_error(png_ptr, "Invalid sCAL unit");
-
-   if (swidth == NULL || (lengthw = strlen(swidth)) == 0 ||
-       swidth[0] == 45 /* '-' */ || !png_check_fp_string(swidth, lengthw))
-      png_error(png_ptr, "Invalid sCAL width");
-
-   if (sheight == NULL || (lengthh = strlen(sheight)) == 0 ||
-       sheight[0] == 45 /* '-' */ || !png_check_fp_string(sheight, lengthh))
-      png_error(png_ptr, "Invalid sCAL height");
-
-   info_ptr->scal_unit = (png_byte)unit;
-
-   ++lengthw;
-
-   png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthw);
-
-   info_ptr->scal_s_width = png_voidcast(png_charp,
-       png_malloc_warn(png_ptr, lengthw));
-
-   if (info_ptr->scal_s_width == NULL)
-   {
-      png_warning(png_ptr, "Memory allocation failed while processing sCAL");
-
-      return;
-   }
-
-   memcpy(info_ptr->scal_s_width, swidth, lengthw);
-
-   ++lengthh;
-
-   png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthh);
-
-   info_ptr->scal_s_height = png_voidcast(png_charp,
-       png_malloc_warn(png_ptr, lengthh));
-
-   if (info_ptr->scal_s_height == NULL)
-   {
-      png_free (png_ptr, info_ptr->scal_s_width);
-      info_ptr->scal_s_width = NULL;
-
-      png_warning(png_ptr, "Memory allocation failed while processing sCAL");
-
-      return;
-   }
-
-   memcpy(info_ptr->scal_s_height, sheight, lengthh);
-
-   info_ptr->valid |= PNG_INFO_sCAL;
-   info_ptr->free_me |= PNG_FREE_SCAL;
-}
-
-#  ifdef PNG_FLOATING_POINT_SUPPORTED
-void PNGAPI
-png_set_sCAL(png_const_structrp png_ptr, png_inforp info_ptr, int unit,
-    double width, double height)
-{
-   png_debug1(1, "in %s storage function", "sCAL");
-
-   /* Check the arguments. */
-   if (width <= 0)
-      png_warning(png_ptr, "Invalid sCAL width ignored");
-
-   else if (height <= 0)
-      png_warning(png_ptr, "Invalid sCAL height ignored");
-
-   else
-   {
-      /* Convert 'width' and 'height' to ASCII. */
-      char swidth[PNG_sCAL_MAX_DIGITS+1];
-      char sheight[PNG_sCAL_MAX_DIGITS+1];
-
-      png_ascii_from_fp(png_ptr, swidth, (sizeof swidth), width,
-          PNG_sCAL_PRECISION);
-      png_ascii_from_fp(png_ptr, sheight, (sizeof sheight), height,
-          PNG_sCAL_PRECISION);
-
-      png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight);
-   }
-}
-#  endif
-
-#  ifdef PNG_FIXED_POINT_SUPPORTED
-void PNGAPI
-png_set_sCAL_fixed(png_const_structrp png_ptr, png_inforp info_ptr, int unit,
-    png_fixed_point width, png_fixed_point height)
-{
-   png_debug1(1, "in %s storage function", "sCAL");
-
-   /* Check the arguments. */
-   if (width <= 0)
-      png_warning(png_ptr, "Invalid sCAL width ignored");
-
-   else if (height <= 0)
-      png_warning(png_ptr, "Invalid sCAL height ignored");
-
-   else
-   {
-      /* Convert 'width' and 'height' to ASCII. */
-      char swidth[PNG_sCAL_MAX_DIGITS+1];
-      char sheight[PNG_sCAL_MAX_DIGITS+1];
-
-      png_ascii_from_fixed(png_ptr, swidth, (sizeof swidth), width);
-      png_ascii_from_fixed(png_ptr, sheight, (sizeof sheight), height);
-
-      png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight);
-   }
-}
-#  endif
-#endif
-
-#ifdef PNG_pHYs_SUPPORTED
-void PNGAPI
-png_set_pHYs(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_uint_32 res_x, png_uint_32 res_y, int unit_type)
-{
-   png_debug1(1, "in %s storage function", "pHYs");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   info_ptr->x_pixels_per_unit = res_x;
-   info_ptr->y_pixels_per_unit = res_y;
-   info_ptr->phys_unit_type = (png_byte)unit_type;
-   info_ptr->valid |= PNG_INFO_pHYs;
-}
-#endif
-
-void PNGAPI
-png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr,
-    png_const_colorp palette, int num_palette)
-{
-
-   png_uint_32 max_palette_length;
-
-   png_debug1(1, "in %s storage function", "PLTE");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   max_palette_length = (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ?
-      (1 << info_ptr->bit_depth) : PNG_MAX_PALETTE_LENGTH;
-
-   if (num_palette < 0 || num_palette > (int) max_palette_length)
-   {
-      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-         png_error(png_ptr, "Invalid palette length");
-
-      else
-      {
-         png_warning(png_ptr, "Invalid palette length");
-
-         return;
-      }
-   }
-
-   if ((num_palette > 0 && palette == NULL) ||
-      (num_palette == 0
-#        ifdef PNG_MNG_FEATURES_SUPPORTED
-            && (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0
-#        endif
-      ))
-   {
-      png_error(png_ptr, "Invalid palette");
-   }
-
-   /* It may not actually be necessary to set png_ptr->palette here;
-    * we do it for backward compatibility with the way the png_handle_tRNS
-    * function used to do the allocation.
-    *
-    * 1.6.0: the above statement appears to be incorrect; something has to set
-    * the palette inside png_struct on read.
-    */
-   png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0);
-
-   /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead
-    * of num_palette entries, in case of an invalid PNG file or incorrect
-    * call to png_set_PLTE() with too-large sample values.
-    */
-   png_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr,
-       PNG_MAX_PALETTE_LENGTH * (sizeof (png_color))));
-
-   if (num_palette > 0)
-      memcpy(png_ptr->palette, palette, (unsigned int)num_palette *
-          (sizeof (png_color)));
-   info_ptr->palette = png_ptr->palette;
-   info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette;
-
-   info_ptr->free_me |= PNG_FREE_PLTE;
-
-   info_ptr->valid |= PNG_INFO_PLTE;
-}
-
-#ifdef PNG_sBIT_SUPPORTED
-void PNGAPI
-png_set_sBIT(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_const_color_8p sig_bit)
-{
-   png_debug1(1, "in %s storage function", "sBIT");
-
-   if (png_ptr == NULL || info_ptr == NULL || sig_bit == NULL)
-      return;
-
-   info_ptr->sig_bit = *sig_bit;
-   info_ptr->valid |= PNG_INFO_sBIT;
-}
-#endif
-
-#ifdef PNG_sRGB_SUPPORTED
-void PNGAPI
-png_set_sRGB(png_const_structrp png_ptr, png_inforp info_ptr, int srgb_intent)
-{
-   png_debug1(1, "in %s storage function", "sRGB");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   (void)png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent);
-   png_colorspace_sync_info(png_ptr, info_ptr);
-}
-
-void PNGAPI
-png_set_sRGB_gAMA_and_cHRM(png_const_structrp png_ptr, png_inforp info_ptr,
-    int srgb_intent)
-{
-   png_debug1(1, "in %s storage function", "sRGB_gAMA_and_cHRM");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   if (png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace,
-       srgb_intent) != 0)
-   {
-      /* This causes the gAMA and cHRM to be written too */
-      info_ptr->colorspace.flags |=
-         PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM;
-   }
-
-   png_colorspace_sync_info(png_ptr, info_ptr);
-}
-#endif /* sRGB */
-
-
-#ifdef PNG_iCCP_SUPPORTED
-void PNGAPI
-png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_const_charp name, int compression_type,
-    png_const_bytep profile, png_uint_32 proflen)
-{
-   png_charp new_iccp_name;
-   png_bytep new_iccp_profile;
-   size_t length;
-
-   png_debug1(1, "in %s storage function", "iCCP");
-
-   if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL)
-      return;
-
-   if (compression_type != PNG_COMPRESSION_TYPE_BASE)
-      png_app_error(png_ptr, "Invalid iCCP compression method");
-
-   /* Set the colorspace first because this validates the profile; do not
-    * override previously set app cHRM or gAMA here (because likely as not the
-    * application knows better than libpng what the correct values are.)  Pass
-    * the info_ptr color_type field to png_colorspace_set_ICC because in the
-    * write case it has not yet been stored in png_ptr.
-    */
-   {
-      int result = png_colorspace_set_ICC(png_ptr, &info_ptr->colorspace, name,
-          proflen, profile, info_ptr->color_type);
-
-      png_colorspace_sync_info(png_ptr, info_ptr);
-
-      /* Don't do any of the copying if the profile was bad, or inconsistent. */
-      if (result == 0)
-         return;
-
-      /* But do write the gAMA and cHRM chunks from the profile. */
-      info_ptr->colorspace.flags |=
-         PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM;
-   }
-
-   length = strlen(name)+1;
-   new_iccp_name = png_voidcast(png_charp, png_malloc_warn(png_ptr, length));
-
-   if (new_iccp_name == NULL)
-   {
-      png_benign_error(png_ptr, "Insufficient memory to process iCCP chunk");
-
-      return;
-   }
-
-   memcpy(new_iccp_name, name, length);
-   new_iccp_profile = png_voidcast(png_bytep,
-       png_malloc_warn(png_ptr, proflen));
-
-   if (new_iccp_profile == NULL)
-   {
-      png_free(png_ptr, new_iccp_name);
-      png_benign_error(png_ptr,
-          "Insufficient memory to process iCCP profile");
-
-      return;
-   }
-
-   memcpy(new_iccp_profile, profile, proflen);
-
-   png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0);
-
-   info_ptr->iccp_proflen = proflen;
-   info_ptr->iccp_name = new_iccp_name;
-   info_ptr->iccp_profile = new_iccp_profile;
-   info_ptr->free_me |= PNG_FREE_ICCP;
-   info_ptr->valid |= PNG_INFO_iCCP;
-}
-#endif
-
-#ifdef PNG_TEXT_SUPPORTED
-void PNGAPI
-png_set_text(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_const_textp text_ptr, int num_text)
-{
-   int ret;
-   ret = png_set_text_2(png_ptr, info_ptr, text_ptr, num_text);
-
-   if (ret != 0)
-      png_error(png_ptr, "Insufficient memory to store text");
-}
-
-int /* PRIVATE */
-png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_const_textp text_ptr, int num_text)
-{
-   int i;
-
-   png_debug1(1, "in %lx storage function", png_ptr == NULL ? 0xabadca11U :
-      (unsigned long)png_ptr->chunk_name);
-
-   if (png_ptr == NULL || info_ptr == NULL || num_text <= 0 || text_ptr == NULL)
-      return(0);
-
-   /* Make sure we have enough space in the "text" array in info_struct
-    * to hold all of the incoming text_ptr objects.  This compare can't overflow
-    * because max_text >= num_text (anyway, subtract of two positive integers
-    * can't overflow in any case.)
-    */
-   if (num_text > info_ptr->max_text - info_ptr->num_text)
-   {
-      int old_num_text = info_ptr->num_text;
-      int max_text;
-      png_textp new_text = NULL;
-
-      /* Calculate an appropriate max_text, checking for overflow. */
-      max_text = old_num_text;
-      if (num_text <= INT_MAX - max_text)
-      {
-         max_text += num_text;
-
-         /* Round up to a multiple of 8 */
-         if (max_text < INT_MAX-8)
-            max_text = (max_text + 8) & ~0x7;
-
-         else
-            max_text = INT_MAX;
-
-         /* Now allocate a new array and copy the old members in; this does all
-          * the overflow checks.
-          */
-         new_text = png_voidcast(png_textp,png_realloc_array(png_ptr,
-             info_ptr->text, old_num_text, max_text-old_num_text,
-             sizeof *new_text));
-      }
-
-      if (new_text == NULL)
-      {
-         png_chunk_report(png_ptr, "too many text chunks",
-             PNG_CHUNK_WRITE_ERROR);
-
-         return 1;
-      }
-
-      png_free(png_ptr, info_ptr->text);
-
-      info_ptr->text = new_text;
-      info_ptr->free_me |= PNG_FREE_TEXT;
-      info_ptr->max_text = max_text;
-      /* num_text is adjusted below as the entries are copied in */
-
-      png_debug1(3, "allocated %d entries for info_ptr->text", max_text);
-   }
-
-   for (i = 0; i < num_text; i++)
-   {
-      size_t text_length, key_len;
-      size_t lang_len, lang_key_len;
-      png_textp textp = &(info_ptr->text[info_ptr->num_text]);
-
-      if (text_ptr[i].key == NULL)
-          continue;
-
-      if (text_ptr[i].compression < PNG_TEXT_COMPRESSION_NONE ||
-          text_ptr[i].compression >= PNG_TEXT_COMPRESSION_LAST)
-      {
-         png_chunk_report(png_ptr, "text compression mode is out of range",
-             PNG_CHUNK_WRITE_ERROR);
-         continue;
-      }
-
-      key_len = strlen(text_ptr[i].key);
-
-      if (text_ptr[i].compression <= 0)
-      {
-         lang_len = 0;
-         lang_key_len = 0;
-      }
-
-      else
-#  ifdef PNG_iTXt_SUPPORTED
-      {
-         /* Set iTXt data */
-
-         if (text_ptr[i].lang != NULL)
-            lang_len = strlen(text_ptr[i].lang);
-
-         else
-            lang_len = 0;
-
-         if (text_ptr[i].lang_key != NULL)
-            lang_key_len = strlen(text_ptr[i].lang_key);
-
-         else
-            lang_key_len = 0;
-      }
-#  else /* iTXt */
-      {
-         png_chunk_report(png_ptr, "iTXt chunk not supported",
-             PNG_CHUNK_WRITE_ERROR);
-         continue;
-      }
-#  endif
-
-      if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0')
-      {
-         text_length = 0;
-#  ifdef PNG_iTXt_SUPPORTED
-         if (text_ptr[i].compression > 0)
-            textp->compression = PNG_ITXT_COMPRESSION_NONE;
-
-         else
-#  endif
-            textp->compression = PNG_TEXT_COMPRESSION_NONE;
-      }
-
-      else
-      {
-         text_length = strlen(text_ptr[i].text);
-         textp->compression = text_ptr[i].compression;
-      }
-
-      textp->key = png_voidcast(png_charp,png_malloc_base(png_ptr,
-          key_len + text_length + lang_len + lang_key_len + 4));
-
-      if (textp->key == NULL)
-      {
-         png_chunk_report(png_ptr, "text chunk: out of memory",
-             PNG_CHUNK_WRITE_ERROR);
-
-         return 1;
-      }
-
-      png_debug2(2, "Allocated %lu bytes at %p in png_set_text",
-          (unsigned long)(png_uint_32)
-          (key_len + lang_len + lang_key_len + text_length + 4),
-          textp->key);
-
-      memcpy(textp->key, text_ptr[i].key, key_len);
-      *(textp->key + key_len) = '\0';
-
-      if (text_ptr[i].compression > 0)
-      {
-         textp->lang = textp->key + key_len + 1;
-         memcpy(textp->lang, text_ptr[i].lang, lang_len);
-         *(textp->lang + lang_len) = '\0';
-         textp->lang_key = textp->lang + lang_len + 1;
-         memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len);
-         *(textp->lang_key + lang_key_len) = '\0';
-         textp->text = textp->lang_key + lang_key_len + 1;
-      }
-
-      else
-      {
-         textp->lang=NULL;
-         textp->lang_key=NULL;
-         textp->text = textp->key + key_len + 1;
-      }
-
-      if (text_length != 0)
-         memcpy(textp->text, text_ptr[i].text, text_length);
-
-      *(textp->text + text_length) = '\0';
-
-#  ifdef PNG_iTXt_SUPPORTED
-      if (textp->compression > 0)
-      {
-         textp->text_length = 0;
-         textp->itxt_length = text_length;
-      }
-
-      else
-#  endif
-      {
-         textp->text_length = text_length;
-         textp->itxt_length = 0;
-      }
-
-      info_ptr->num_text++;
-      png_debug1(3, "transferred text chunk %d", info_ptr->num_text);
-   }
-
-   return(0);
-}
-#endif
-
-#ifdef PNG_tIME_SUPPORTED
-void PNGAPI
-png_set_tIME(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_const_timep mod_time)
-{
-   png_debug1(1, "in %s storage function", "tIME");
-
-   if (png_ptr == NULL || info_ptr == NULL || mod_time == NULL ||
-       (png_ptr->mode & PNG_WROTE_tIME) != 0)
-      return;
-
-   if (mod_time->month == 0   || mod_time->month > 12  ||
-       mod_time->day   == 0   || mod_time->day   > 31  ||
-       mod_time->hour  > 23   || mod_time->minute > 59 ||
-       mod_time->second > 60)
-   {
-      png_warning(png_ptr, "Ignoring invalid time value");
-
-      return;
-   }
-
-   info_ptr->mod_time = *mod_time;
-   info_ptr->valid |= PNG_INFO_tIME;
-}
-#endif
-
-#ifdef PNG_tRNS_SUPPORTED
-void PNGAPI
-png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr,
-    png_const_bytep trans_alpha, int num_trans, png_const_color_16p trans_color)
-{
-   png_debug1(1, "in %s storage function", "tRNS");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-
-      return;
-
-   if (trans_alpha != NULL)
-   {
-       /* It may not actually be necessary to set png_ptr->trans_alpha here;
-        * we do it for backward compatibility with the way the png_handle_tRNS
-        * function used to do the allocation.
-        *
-        * 1.6.0: The above statement is incorrect; png_handle_tRNS effectively
-        * relies on png_set_tRNS storing the information in png_struct
-        * (otherwise it won't be there for the code in pngrtran.c).
-        */
-
-       png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0);
-
-       if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH)
-       {
-         /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */
-          info_ptr->trans_alpha = png_voidcast(png_bytep,
-              png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH));
-          memcpy(info_ptr->trans_alpha, trans_alpha, (size_t)num_trans);
-       }
-       png_ptr->trans_alpha = info_ptr->trans_alpha;
-   }
-
-   if (trans_color != NULL)
-   {
-#ifdef PNG_WARNINGS_SUPPORTED
-      if (info_ptr->bit_depth < 16)
-      {
-         int sample_max = (1 << info_ptr->bit_depth) - 1;
-
-         if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY &&
-             trans_color->gray > sample_max) ||
-             (info_ptr->color_type == PNG_COLOR_TYPE_RGB &&
-             (trans_color->red > sample_max ||
-             trans_color->green > sample_max ||
-             trans_color->blue > sample_max)))
-            png_warning(png_ptr,
-                "tRNS chunk has out-of-range samples for bit_depth");
-      }
-#endif
-
-      info_ptr->trans_color = *trans_color;
-
-      if (num_trans == 0)
-         num_trans = 1;
-   }
-
-   info_ptr->num_trans = (png_uint_16)num_trans;
-
-   if (num_trans != 0)
-   {
-      info_ptr->valid |= PNG_INFO_tRNS;
-      info_ptr->free_me |= PNG_FREE_TRNS;
-   }
-}
-#endif
-
-#ifdef PNG_sPLT_SUPPORTED
-void PNGAPI
-png_set_sPLT(png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_sPLT_tp entries, int nentries)
-/*
- *  entries        - array of png_sPLT_t structures
- *                   to be added to the list of palettes
- *                   in the info structure.
- *
- *  nentries       - number of palette structures to be
- *                   added.
- */
-{
-   png_sPLT_tp np;
-
-   if (png_ptr == NULL || info_ptr == NULL || nentries <= 0 || entries == NULL)
-      return;
-
-   /* Use the internal realloc function, which checks for all the possible
-    * overflows.  Notice that the parameters are (int) and (size_t)
-    */
-   np = png_voidcast(png_sPLT_tp,png_realloc_array(png_ptr,
-       info_ptr->splt_palettes, info_ptr->splt_palettes_num, nentries,
-       sizeof *np));
-
-   if (np == NULL)
-   {
-      /* Out of memory or too many chunks */
-      png_chunk_report(png_ptr, "too many sPLT chunks", PNG_CHUNK_WRITE_ERROR);
-
-      return;
-   }
-
-   png_free(png_ptr, info_ptr->splt_palettes);
-   info_ptr->splt_palettes = np;
-   info_ptr->free_me |= PNG_FREE_SPLT;
-
-   np += info_ptr->splt_palettes_num;
-
-   do
-   {
-      size_t length;
-
-      /* Skip invalid input entries */
-      if (entries->name == NULL || entries->entries == NULL)
-      {
-         /* png_handle_sPLT doesn't do this, so this is an app error */
-         png_app_error(png_ptr, "png_set_sPLT: invalid sPLT");
-         /* Just skip the invalid entry */
-         continue;
-      }
-
-      np->depth = entries->depth;
-
-      /* In the event of out-of-memory just return - there's no point keeping
-       * on trying to add sPLT chunks.
-       */
-      length = strlen(entries->name) + 1;
-      np->name = png_voidcast(png_charp, png_malloc_base(png_ptr, length));
-
-      if (np->name == NULL)
-         break;
-
-      memcpy(np->name, entries->name, length);
-
-      /* IMPORTANT: we have memory now that won't get freed if something else
-       * goes wrong; this code must free it.  png_malloc_array produces no
-       * warnings; use a png_chunk_report (below) if there is an error.
-       */
-      np->entries = png_voidcast(png_sPLT_entryp, png_malloc_array(png_ptr,
-          entries->nentries, sizeof (png_sPLT_entry)));
-
-      if (np->entries == NULL)
-      {
-         png_free(png_ptr, np->name);
-         np->name = NULL;
-         break;
-      }
-
-      np->nentries = entries->nentries;
-      /* This multiply can't overflow because png_malloc_array has already
-       * checked it when doing the allocation.
-       */
-      memcpy(np->entries, entries->entries,
-          (unsigned int)entries->nentries * sizeof (png_sPLT_entry));
-
-      /* Note that 'continue' skips the advance of the out pointer and out
-       * count, so an invalid entry is not added.
-       */
-      info_ptr->valid |= PNG_INFO_sPLT;
-      ++(info_ptr->splt_palettes_num);
-      ++np;
-      ++entries;
-   }
-   while (--nentries);
-
-   if (nentries > 0)
-      png_chunk_report(png_ptr, "sPLT out of memory", PNG_CHUNK_WRITE_ERROR);
-}
-#endif /* sPLT */
-
-#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
-static png_byte
-check_location(png_const_structrp png_ptr, int location)
-{
-   location &= (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT);
-
-   /* New in 1.6.0; copy the location and check it.  This is an API
-    * change; previously the app had to use the
-    * png_set_unknown_chunk_location API below for each chunk.
-    */
-   if (location == 0 && (png_ptr->mode & PNG_IS_READ_STRUCT) == 0)
-   {
-      /* Write struct, so unknown chunks come from the app */
-      png_app_warning(png_ptr,
-          "png_set_unknown_chunks now expects a valid location");
-      /* Use the old behavior */
-      location = (png_byte)(png_ptr->mode &
-          (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT));
-   }
-
-   /* This need not be an internal error - if the app calls
-    * png_set_unknown_chunks on a read pointer it must get the location right.
-    */
-   if (location == 0)
-      png_error(png_ptr, "invalid location in png_set_unknown_chunks");
-
-   /* Now reduce the location to the top-most set bit by removing each least
-    * significant bit in turn.
-    */
-   while (location != (location & -location))
-      location &= ~(location & -location);
-
-   /* The cast is safe because 'location' is a bit mask and only the low four
-    * bits are significant.
-    */
-   return (png_byte)location;
-}
-
-void PNGAPI
-png_set_unknown_chunks(png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns)
-{
-   png_unknown_chunkp np;
-
-   if (png_ptr == NULL || info_ptr == NULL || num_unknowns <= 0 ||
-       unknowns == NULL)
-      return;
-
-   /* Check for the failure cases where support has been disabled at compile
-    * time.  This code is hardly ever compiled - it's here because
-    * STORE_UNKNOWN_CHUNKS is set by both read and write code (compiling in this
-    * code) but may be meaningless if the read or write handling of unknown
-    * chunks is not compiled in.
-    */
-#  if !defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) && \
-      defined(PNG_READ_SUPPORTED)
-      if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0)
-      {
-         png_app_error(png_ptr, "no unknown chunk support on read");
-
-         return;
-      }
-#  endif
-#  if !defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) && \
-      defined(PNG_WRITE_SUPPORTED)
-      if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0)
-      {
-         png_app_error(png_ptr, "no unknown chunk support on write");
-
-         return;
-      }
-#  endif
-
-   /* Prior to 1.6.0 this code used png_malloc_warn; however, this meant that
-    * unknown critical chunks could be lost with just a warning resulting in
-    * undefined behavior.  Now png_chunk_report is used to provide behavior
-    * appropriate to read or write.
-    */
-   np = png_voidcast(png_unknown_chunkp, png_realloc_array(png_ptr,
-       info_ptr->unknown_chunks, info_ptr->unknown_chunks_num, num_unknowns,
-       sizeof *np));
-
-   if (np == NULL)
-   {
-      png_chunk_report(png_ptr, "too many unknown chunks",
-          PNG_CHUNK_WRITE_ERROR);
-
-      return;
-   }
-
-   png_free(png_ptr, info_ptr->unknown_chunks);
-   info_ptr->unknown_chunks = np; /* safe because it is initialized */
-   info_ptr->free_me |= PNG_FREE_UNKN;
-
-   np += info_ptr->unknown_chunks_num;
-
-   /* Increment unknown_chunks_num each time round the loop to protect the
-    * just-allocated chunk data.
-    */
-   for (; num_unknowns > 0; --num_unknowns, ++unknowns)
-   {
-      memcpy(np->name, unknowns->name, (sizeof np->name));
-      np->name[(sizeof np->name)-1] = '\0';
-      np->location = check_location(png_ptr, unknowns->location);
-
-      if (unknowns->size == 0)
-      {
-         np->data = NULL;
-         np->size = 0;
-      }
-
-      else
-      {
-         np->data = png_voidcast(png_bytep,
-             png_malloc_base(png_ptr, unknowns->size));
-
-         if (np->data == NULL)
-         {
-            png_chunk_report(png_ptr, "unknown chunk: out of memory",
-                PNG_CHUNK_WRITE_ERROR);
-            /* But just skip storing the unknown chunk */
-            continue;
-         }
-
-         memcpy(np->data, unknowns->data, unknowns->size);
-         np->size = unknowns->size;
-      }
-
-      /* These increments are skipped on out-of-memory for the data - the
-       * unknown chunk entry gets overwritten if the png_chunk_report returns.
-       * This is correct in the read case (the chunk is just dropped.)
-       */
-      ++np;
-      ++(info_ptr->unknown_chunks_num);
-   }
-}
-
-void PNGAPI
-png_set_unknown_chunk_location(png_const_structrp png_ptr, png_inforp info_ptr,
-    int chunk, int location)
-{
-   /* This API is pretty pointless in 1.6.0 because the location can be set
-    * before the call to png_set_unknown_chunks.
-    *
-    * TODO: add a png_app_warning in 1.7
-    */
-   if (png_ptr != NULL && info_ptr != NULL && chunk >= 0 &&
-      chunk < info_ptr->unknown_chunks_num)
-   {
-      if ((location & (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT)) == 0)
-      {
-         png_app_error(png_ptr, "invalid unknown chunk location");
-         /* Fake out the pre 1.6.0 behavior: */
-         if (((unsigned int)location & PNG_HAVE_IDAT) != 0) /* undocumented! */
-            location = PNG_AFTER_IDAT;
-
-         else
-            location = PNG_HAVE_IHDR; /* also undocumented */
-      }
-
-      info_ptr->unknown_chunks[chunk].location =
-         check_location(png_ptr, location);
-   }
-}
-#endif /* STORE_UNKNOWN_CHUNKS */
-
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-png_uint_32 PNGAPI
-png_permit_mng_features (png_structrp png_ptr, png_uint_32 mng_features)
-{
-   png_debug(1, "in png_permit_mng_features");
-
-   if (png_ptr == NULL)
-      return 0;
-
-   png_ptr->mng_features_permitted = mng_features & PNG_ALL_MNG_FEATURES;
-
-   return png_ptr->mng_features_permitted;
-}
-#endif
-
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-static unsigned int
-add_one_chunk(png_bytep list, unsigned int count, png_const_bytep add, int keep)
-{
-   unsigned int i;
-
-   /* Utility function: update the 'keep' state of a chunk if it is already in
-    * the list, otherwise add it to the list.
-    */
-   for (i=0; i<count; ++i, list += 5)
-   {
-      if (memcmp(list, add, 4) == 0)
-      {
-         list[4] = (png_byte)keep;
-
-         return count;
-      }
-   }
-
-   if (keep != PNG_HANDLE_CHUNK_AS_DEFAULT)
-   {
-      ++count;
-      memcpy(list, add, 4);
-      list[4] = (png_byte)keep;
-   }
-
-   return count;
-}
-
-void PNGAPI
-png_set_keep_unknown_chunks(png_structrp png_ptr, int keep,
-    png_const_bytep chunk_list, int num_chunks_in)
-{
-   png_bytep new_list;
-   unsigned int num_chunks, old_num_chunks;
-
-   if (png_ptr == NULL)
-      return;
-
-   if (keep < 0 || keep >= PNG_HANDLE_CHUNK_LAST)
-   {
-      png_app_error(png_ptr, "png_set_keep_unknown_chunks: invalid keep");
-
-      return;
-   }
-
-   if (num_chunks_in <= 0)
-   {
-      png_ptr->unknown_default = keep;
-
-      /* '0' means just set the flags, so stop here */
-      if (num_chunks_in == 0)
-        return;
-   }
-
-   if (num_chunks_in < 0)
-   {
-      /* Ignore all unknown chunks and all chunks recognized by
-       * libpng except for IHDR, PLTE, tRNS, IDAT, and IEND
-       */
-      static const png_byte chunks_to_ignore[] = {
-         98,  75,  71,  68, '\0',  /* bKGD */
-         99,  72,  82,  77, '\0',  /* cHRM */
-        101,  88,  73, 102, '\0',  /* eXIf */
-        103,  65,  77,  65, '\0',  /* gAMA */
-        104,  73,  83,  84, '\0',  /* hIST */
-        105,  67,  67,  80, '\0',  /* iCCP */
-        105,  84,  88, 116, '\0',  /* iTXt */
-        111,  70,  70, 115, '\0',  /* oFFs */
-        112,  67,  65,  76, '\0',  /* pCAL */
-        112,  72,  89, 115, '\0',  /* pHYs */
-        115,  66,  73,  84, '\0',  /* sBIT */
-        115,  67,  65,  76, '\0',  /* sCAL */
-        115,  80,  76,  84, '\0',  /* sPLT */
-        115,  84,  69,  82, '\0',  /* sTER */
-        115,  82,  71,  66, '\0',  /* sRGB */
-        116,  69,  88, 116, '\0',  /* tEXt */
-        116,  73,  77,  69, '\0',  /* tIME */
-        122,  84,  88, 116, '\0'   /* zTXt */
-      };
-
-      chunk_list = chunks_to_ignore;
-      num_chunks = (unsigned int)/*SAFE*/(sizeof chunks_to_ignore)/5U;
-   }
-
-   else /* num_chunks_in > 0 */
-   {
-      if (chunk_list == NULL)
-      {
-         /* Prior to 1.6.0 this was silently ignored, now it is an app_error
-          * which can be switched off.
-          */
-         png_app_error(png_ptr, "png_set_keep_unknown_chunks: no chunk list");
-
-         return;
-      }
-
-      num_chunks = (unsigned int)num_chunks_in;
-   }
-
-   old_num_chunks = png_ptr->num_chunk_list;
-   if (png_ptr->chunk_list == NULL)
-      old_num_chunks = 0;
-
-   /* Since num_chunks is always restricted to UINT_MAX/5 this can't overflow.
-    */
-   if (num_chunks + old_num_chunks > UINT_MAX/5)
-   {
-      png_app_error(png_ptr, "png_set_keep_unknown_chunks: too many chunks");
-
-      return;
-   }
-
-   /* If these chunks are being reset to the default then no more memory is
-    * required because add_one_chunk above doesn't extend the list if the 'keep'
-    * parameter is the default.
-    */
-   if (keep != 0)
-   {
-      new_list = png_voidcast(png_bytep, png_malloc(png_ptr,
-          5 * (num_chunks + old_num_chunks)));
-
-      if (old_num_chunks > 0)
-         memcpy(new_list, png_ptr->chunk_list, 5*old_num_chunks);
-   }
-
-   else if (old_num_chunks > 0)
-      new_list = png_ptr->chunk_list;
-
-   else
-      new_list = NULL;
-
-   /* Add the new chunks together with each one's handling code.  If the chunk
-    * already exists the code is updated, otherwise the chunk is added to the
-    * end.  (In libpng 1.6.0 order no longer matters because this code enforces
-    * the earlier convention that the last setting is the one that is used.)
-    */
-   if (new_list != NULL)
-   {
-      png_const_bytep inlist;
-      png_bytep outlist;
-      unsigned int i;
-
-      for (i=0; i<num_chunks; ++i)
-      {
-         old_num_chunks = add_one_chunk(new_list, old_num_chunks,
-             chunk_list+5*i, keep);
-      }
-
-      /* Now remove any spurious 'default' entries. */
-      num_chunks = 0;
-      for (i=0, inlist=outlist=new_list; i<old_num_chunks; ++i, inlist += 5)
-      {
-         if (inlist[4])
-         {
-            if (outlist != inlist)
-               memcpy(outlist, inlist, 5);
-            outlist += 5;
-            ++num_chunks;
-         }
-      }
-
-      /* This means the application has removed all the specialized handling. */
-      if (num_chunks == 0)
-      {
-         if (png_ptr->chunk_list != new_list)
-            png_free(png_ptr, new_list);
-
-         new_list = NULL;
-      }
-   }
-
-   else
-      num_chunks = 0;
-
-   png_ptr->num_chunk_list = num_chunks;
-
-   if (png_ptr->chunk_list != new_list)
-   {
-      if (png_ptr->chunk_list != NULL)
-         png_free(png_ptr, png_ptr->chunk_list);
-
-      png_ptr->chunk_list = new_list;
-   }
-}
-#endif
-
-#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
-void PNGAPI
-png_set_read_user_chunk_fn(png_structrp png_ptr, png_voidp user_chunk_ptr,
-    png_user_chunk_ptr read_user_chunk_fn)
-{
-   png_debug(1, "in png_set_read_user_chunk_fn");
-
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->read_user_chunk_fn = read_user_chunk_fn;
-   png_ptr->user_chunk_ptr = user_chunk_ptr;
-}
-#endif
-
-#ifdef PNG_INFO_IMAGE_SUPPORTED
-void PNGAPI
-png_set_rows(png_const_structrp png_ptr, png_inforp info_ptr,
-    png_bytepp row_pointers)
-{
-   png_debug1(1, "in %s storage function", "rows");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   if (info_ptr->row_pointers != NULL &&
-       (info_ptr->row_pointers != row_pointers))
-      png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0);
-
-   info_ptr->row_pointers = row_pointers;
-
-   if (row_pointers != NULL)
-      info_ptr->valid |= PNG_INFO_IDAT;
-}
-#endif
-
-void PNGAPI
-png_set_compression_buffer_size(png_structrp png_ptr, size_t size)
-{
-   if (png_ptr == NULL)
-      return;
-
-   if (size == 0 || size > PNG_UINT_31_MAX)
-      png_error(png_ptr, "invalid compression buffer size");
-
-#  ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-   if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0)
-   {
-      png_ptr->IDAT_read_size = (png_uint_32)size; /* checked above */
-      return;
-   }
-#  endif
-
-#  ifdef PNG_WRITE_SUPPORTED
-   if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0)
-   {
-      if (png_ptr->zowner != 0)
-      {
-         png_warning(png_ptr,
-             "Compression buffer size cannot be changed because it is in use");
-
-         return;
-      }
-
-#ifndef __COVERITY__
-      /* Some compilers complain that this is always false.  However, it
-       * can be true when integer overflow happens.
-       */
-      if (size > ZLIB_IO_MAX)
-      {
-         png_warning(png_ptr,
-             "Compression buffer size limited to system maximum");
-         size = ZLIB_IO_MAX; /* must fit */
-      }
-#endif
-
-      if (size < 6)
-      {
-         /* Deflate will potentially go into an infinite loop on a SYNC_FLUSH
-          * if this is permitted.
-          */
-         png_warning(png_ptr,
-             "Compression buffer size cannot be reduced below 6");
-
-         return;
-      }
-
-      if (png_ptr->zbuffer_size != size)
-      {
-         png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);
-         png_ptr->zbuffer_size = (uInt)size;
-      }
-   }
-#  endif
-}
-
-void PNGAPI
-png_set_invalid(png_const_structrp png_ptr, png_inforp info_ptr, int mask)
-{
-   if (png_ptr != NULL && info_ptr != NULL)
-      info_ptr->valid &= (unsigned int)(~mask);
-}
-
-
-#ifdef PNG_SET_USER_LIMITS_SUPPORTED
-/* This function was added to libpng 1.2.6 */
-void PNGAPI
-png_set_user_limits (png_structrp png_ptr, png_uint_32 user_width_max,
-    png_uint_32 user_height_max)
-{
-   /* Images with dimensions larger than these limits will be
-    * rejected by png_set_IHDR().  To accept any PNG datastream
-    * regardless of dimensions, set both limits to 0x7fffffff.
-    */
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->user_width_max = user_width_max;
-   png_ptr->user_height_max = user_height_max;
-}
-
-/* This function was added to libpng 1.4.0 */
-void PNGAPI
-png_set_chunk_cache_max (png_structrp png_ptr, png_uint_32 user_chunk_cache_max)
-{
-   if (png_ptr != NULL)
-      png_ptr->user_chunk_cache_max = user_chunk_cache_max;
-}
-
-/* This function was added to libpng 1.4.1 */
-void PNGAPI
-png_set_chunk_malloc_max (png_structrp png_ptr,
-    png_alloc_size_t user_chunk_malloc_max)
-{
-   if (png_ptr != NULL)
-      png_ptr->user_chunk_malloc_max = user_chunk_malloc_max;
-}
-#endif /* ?SET_USER_LIMITS */
-
-
-#ifdef PNG_BENIGN_ERRORS_SUPPORTED
-void PNGAPI
-png_set_benign_errors(png_structrp png_ptr, int allowed)
-{
-   png_debug(1, "in png_set_benign_errors");
-
-   /* If allowed is 1, png_benign_error() is treated as a warning.
-    *
-    * If allowed is 0, png_benign_error() is treated as an error (which
-    * is the default behavior if png_set_benign_errors() is not called).
-    */
-
-   if (allowed != 0)
-      png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN |
-         PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN;
-
-   else
-      png_ptr->flags &= ~(PNG_FLAG_BENIGN_ERRORS_WARN |
-         PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN);
-}
-#endif /* BENIGN_ERRORS */
-
-#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
-   /* Whether to report invalid palette index; added at libng-1.5.10.
-    * It is possible for an indexed (color-type==3) PNG file to contain
-    * pixels with invalid (out-of-range) indexes if the PLTE chunk has
-    * fewer entries than the image's bit-depth would allow. We recover
-    * from this gracefully by filling any incomplete palette with zeros
-    * (opaque black).  By default, when this occurs libpng will issue
-    * a benign error.  This API can be used to override that behavior.
-    */
-void PNGAPI
-png_set_check_for_invalid_index(png_structrp png_ptr, int allowed)
-{
-   png_debug(1, "in png_set_check_for_invalid_index");
-
-   if (allowed > 0)
-      png_ptr->num_palette_max = 0;
-
-   else
-      png_ptr->num_palette_max = -1;
-}
-#endif
-
-#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_pCAL_SUPPORTED) || \
-    defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED)
-/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification,
- * and if invalid, correct the keyword rather than discarding the entire
- * chunk.  The PNG 1.0 specification requires keywords 1-79 characters in
- * length, forbids leading or trailing whitespace, multiple internal spaces,
- * and the non-break space (0x80) from ISO 8859-1.  Returns keyword length.
- *
- * The 'new_key' buffer must be 80 characters in size (for the keyword plus a
- * trailing '\0').  If this routine returns 0 then there was no keyword, or a
- * valid one could not be generated, and the caller must png_error.
- */
-png_uint_32 /* PRIVATE */
-png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key)
-{
-#ifdef PNG_WARNINGS_SUPPORTED
-   png_const_charp orig_key = key;
-#endif
-   png_uint_32 key_len = 0;
-   int bad_character = 0;
-   int space = 1;
-
-   png_debug(1, "in png_check_keyword");
-
-   if (key == NULL)
-   {
-      *new_key = 0;
-      return 0;
-   }
-
-   while (*key && key_len < 79)
-   {
-      png_byte ch = (png_byte)*key++;
-
-      if ((ch > 32 && ch <= 126) || (ch >= 161 /*&& ch <= 255*/))
-      {
-         *new_key++ = ch; ++key_len; space = 0;
-      }
-
-      else if (space == 0)
-      {
-         /* A space or an invalid character when one wasn't seen immediately
-          * before; output just a space.
-          */
-         *new_key++ = 32; ++key_len; space = 1;
-
-         /* If the character was not a space then it is invalid. */
-         if (ch != 32)
-            bad_character = ch;
-      }
-
-      else if (bad_character == 0)
-         bad_character = ch; /* just skip it, record the first error */
-   }
-
-   if (key_len > 0 && space != 0) /* trailing space */
-   {
-      --key_len; --new_key;
-      if (bad_character == 0)
-         bad_character = 32;
-   }
-
-   /* Terminate the keyword */
-   *new_key = 0;
-
-   if (key_len == 0)
-      return 0;
-
-#ifdef PNG_WARNINGS_SUPPORTED
-   /* Try to only output one warning per keyword: */
-   if (*key != 0) /* keyword too long */
-      png_warning(png_ptr, "keyword truncated");
-
-   else if (bad_character != 0)
-   {
-      PNG_WARNING_PARAMETERS(p)
-
-      png_warning_parameter(p, 1, orig_key);
-      png_warning_parameter_signed(p, 2, PNG_NUMBER_FORMAT_02x, bad_character);
-
-      png_formatted_warning(png_ptr, p, "keyword \"@1\": bad character '0x@2'");
-   }
-#else /* !WARNINGS */
-   PNG_UNUSED(png_ptr)
-#endif /* !WARNINGS */
-
-   return key_len;
-}
-#endif /* TEXT || pCAL || iCCP || sPLT */
-#endif /* READ || WRITE */
diff --git a/third_party/libpng16/pngstruct.h b/third_party/libpng16/pngstruct.h
deleted file mode 100644
index 8bdc7ce..0000000
--- a/third_party/libpng16/pngstruct.h
+++ /dev/null
@@ -1,489 +0,0 @@
-
-/* pngstruct.h - header file for PNG reference library
- *
- * Copyright (c) 2018-2019 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
-/* The structure that holds the information to read and write PNG files.
- * The only people who need to care about what is inside of this are the
- * people who will be modifying the library for their own special needs.
- * It should NOT be accessed directly by an application.
- */
-
-#ifndef PNGSTRUCT_H
-#define PNGSTRUCT_H
-/* zlib.h defines the structure z_stream, an instance of which is included
- * in this structure and is required for decompressing the LZ compressed
- * data in PNG files.
- */
-#ifndef ZLIB_CONST
-   /* We must ensure that zlib uses 'const' in declarations. */
-#  define ZLIB_CONST
-#endif
-#include "zlib.h"
-#ifdef const
-   /* zlib.h sometimes #defines const to nothing, undo this. */
-#  undef const
-#endif
-
-/* zlib.h has mediocre z_const use before 1.2.6, this stuff is for compatibility
- * with older builds.
- */
-#if ZLIB_VERNUM < 0x1260
-#  define PNGZ_MSG_CAST(s) png_constcast(char*,s)
-#  define PNGZ_INPUT_CAST(b) png_constcast(png_bytep,b)
-#else
-#  define PNGZ_MSG_CAST(s) (s)
-#  define PNGZ_INPUT_CAST(b) (b)
-#endif
-
-/* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib
- * can handle at once.  This type need be no larger than 16 bits (so maximum of
- * 65535), this define allows us to discover how big it is, but limited by the
- * maximum for size_t.  The value can be overridden in a library build
- * (pngusr.h, or set it in CPPFLAGS) and it works to set it to a considerably
- * lower value (e.g. 255 works).  A lower value may help memory usage (slightly)
- * and may even improve performance on some systems (and degrade it on others.)
- */
-#ifndef ZLIB_IO_MAX
-#  define ZLIB_IO_MAX ((uInt)-1)
-#endif
-
-#ifdef PNG_WRITE_SUPPORTED
-/* The type of a compression buffer list used by the write code. */
-typedef struct png_compression_buffer
-{
-   struct png_compression_buffer *next;
-   png_byte                       output[1]; /* actually zbuf_size */
-} png_compression_buffer, *png_compression_bufferp;
-
-#define PNG_COMPRESSION_BUFFER_SIZE(pp)\
-   (offsetof(png_compression_buffer, output) + (pp)->zbuffer_size)
-#endif
-
-/* Colorspace support; structures used in png_struct, png_info and in internal
- * functions to hold and communicate information about the color space.
- *
- * PNG_COLORSPACE_SUPPORTED is only required if the application will perform
- * colorspace corrections, otherwise all the colorspace information can be
- * skipped and the size of libpng can be reduced (significantly) by compiling
- * out the colorspace support.
- */
-#ifdef PNG_COLORSPACE_SUPPORTED
-/* The chromaticities of the red, green and blue colorants and the chromaticity
- * of the corresponding white point (i.e. of rgb(1.0,1.0,1.0)).
- */
-typedef struct png_xy
-{
-   png_fixed_point redx, redy;
-   png_fixed_point greenx, greeny;
-   png_fixed_point bluex, bluey;
-   png_fixed_point whitex, whitey;
-} png_xy;
-
-/* The same data as above but encoded as CIE XYZ values.  When this data comes
- * from chromaticities the sum of the Y values is assumed to be 1.0
- */
-typedef struct png_XYZ
-{
-   png_fixed_point red_X, red_Y, red_Z;
-   png_fixed_point green_X, green_Y, green_Z;
-   png_fixed_point blue_X, blue_Y, blue_Z;
-} png_XYZ;
-#endif /* COLORSPACE */
-
-#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
-/* A colorspace is all the above plus, potentially, profile information;
- * however at present libpng does not use the profile internally so it is only
- * stored in the png_info struct (if iCCP is supported.)  The rendering intent
- * is retained here and is checked.
- *
- * The file gamma encoding information is also stored here and gamma correction
- * is done by libpng, whereas color correction must currently be done by the
- * application.
- */
-typedef struct png_colorspace
-{
-#ifdef PNG_GAMMA_SUPPORTED
-   png_fixed_point gamma;        /* File gamma */
-#endif
-
-#ifdef PNG_COLORSPACE_SUPPORTED
-   png_xy      end_points_xy;    /* End points as chromaticities */
-   png_XYZ     end_points_XYZ;   /* End points as CIE XYZ colorant values */
-   png_uint_16 rendering_intent; /* Rendering intent of a profile */
-#endif
-
-   /* Flags are always defined to simplify the code. */
-   png_uint_16 flags;            /* As defined below */
-} png_colorspace, * PNG_RESTRICT png_colorspacerp;
-
-typedef const png_colorspace * PNG_RESTRICT png_const_colorspacerp;
-
-/* General flags for the 'flags' field */
-#define PNG_COLORSPACE_HAVE_GAMMA           0x0001
-#define PNG_COLORSPACE_HAVE_ENDPOINTS       0x0002
-#define PNG_COLORSPACE_HAVE_INTENT          0x0004
-#define PNG_COLORSPACE_FROM_gAMA            0x0008
-#define PNG_COLORSPACE_FROM_cHRM            0x0010
-#define PNG_COLORSPACE_FROM_sRGB            0x0020
-#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040
-#define PNG_COLORSPACE_MATCHES_sRGB         0x0080 /* exact match on profile */
-#define PNG_COLORSPACE_INVALID              0x8000
-#define PNG_COLORSPACE_CANCEL(flags)        (0xffff ^ (flags))
-#endif /* COLORSPACE || GAMMA */
-
-struct png_struct_def
-{
-#ifdef PNG_SETJMP_SUPPORTED
-   jmp_buf jmp_buf_local;     /* New name in 1.6.0 for jmp_buf in png_struct */
-   png_longjmp_ptr longjmp_fn;/* setjmp non-local goto function. */
-   jmp_buf *jmp_buf_ptr;      /* passed to longjmp_fn */
-   size_t jmp_buf_size;       /* size of the above, if allocated */
-#endif
-   png_error_ptr error_fn;    /* function for printing errors and aborting */
-#ifdef PNG_WARNINGS_SUPPORTED
-   png_error_ptr warning_fn;  /* function for printing warnings */
-#endif
-   png_voidp error_ptr;       /* user supplied struct for error functions */
-   png_rw_ptr write_data_fn;  /* function for writing output data */
-   png_rw_ptr read_data_fn;   /* function for reading input data */
-   png_voidp io_ptr;          /* ptr to application struct for I/O functions */
-
-#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
-   png_user_transform_ptr read_user_transform_fn; /* user read transform */
-#endif
-
-#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
-   png_user_transform_ptr write_user_transform_fn; /* user write transform */
-#endif
-
-/* These were added in libpng-1.0.2 */
-#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
-#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
-    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
-   png_voidp user_transform_ptr; /* user supplied struct for user transform */
-   png_byte user_transform_depth;    /* bit depth of user transformed pixels */
-   png_byte user_transform_channels; /* channels in user transformed pixels */
-#endif
-#endif
-
-   png_uint_32 mode;          /* tells us where we are in the PNG file */
-   png_uint_32 flags;         /* flags indicating various things to libpng */
-   png_uint_32 transformations; /* which transformations to perform */
-
-   png_uint_32 zowner;        /* ID (chunk type) of zstream owner, 0 if none */
-   z_stream    zstream;       /* decompression structure */
-
-#ifdef PNG_WRITE_SUPPORTED
-   png_compression_bufferp zbuffer_list; /* Created on demand during write */
-   uInt                    zbuffer_size; /* size of the actual buffer */
-
-   int zlib_level;            /* holds zlib compression level */
-   int zlib_method;           /* holds zlib compression method */
-   int zlib_window_bits;      /* holds zlib compression window bits */
-   int zlib_mem_level;        /* holds zlib compression memory level */
-   int zlib_strategy;         /* holds zlib compression strategy */
-#endif
-/* Added at libpng 1.5.4 */
-#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
-   int zlib_text_level;            /* holds zlib compression level */
-   int zlib_text_method;           /* holds zlib compression method */
-   int zlib_text_window_bits;      /* holds zlib compression window bits */
-   int zlib_text_mem_level;        /* holds zlib compression memory level */
-   int zlib_text_strategy;         /* holds zlib compression strategy */
-#endif
-/* End of material added at libpng 1.5.4 */
-/* Added at libpng 1.6.0 */
-#ifdef PNG_WRITE_SUPPORTED
-   int zlib_set_level;        /* Actual values set into the zstream on write */
-   int zlib_set_method;
-   int zlib_set_window_bits;
-   int zlib_set_mem_level;
-   int zlib_set_strategy;
-#endif
-
-   png_uint_32 width;         /* width of image in pixels */
-   png_uint_32 height;        /* height of image in pixels */
-   png_uint_32 num_rows;      /* number of rows in current pass */
-   png_uint_32 usr_width;     /* width of row at start of write */
-   size_t rowbytes;           /* size of row in bytes */
-   png_uint_32 iwidth;        /* width of current interlaced row in pixels */
-   png_uint_32 row_number;    /* current row in interlace pass */
-   png_uint_32 chunk_name;    /* PNG_CHUNK() id of current chunk */
-   png_bytep prev_row;        /* buffer to save previous (unfiltered) row.
-                               * While reading this is a pointer into
-                               * big_prev_row; while writing it is separately
-                               * allocated if needed.
-                               */
-   png_bytep row_buf;         /* buffer to save current (unfiltered) row.
-                               * While reading, this is a pointer into
-                               * big_row_buf; while writing it is separately
-                               * allocated.
-                               */
-#ifdef PNG_WRITE_FILTER_SUPPORTED
-   png_bytep try_row;    /* buffer to save trial row when filtering */
-   png_bytep tst_row;    /* buffer to save best trial row when filtering */
-#endif
-   size_t info_rowbytes;      /* Added in 1.5.4: cache of updated row bytes */
-
-   png_uint_32 idat_size;     /* current IDAT size for read */
-   png_uint_32 crc;           /* current chunk CRC value */
-   png_colorp palette;        /* palette from the input file */
-   png_uint_16 num_palette;   /* number of color entries in palette */
-
-/* Added at libpng-1.5.10 */
-#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
-   int num_palette_max;       /* maximum palette index found in IDAT */
-#endif
-
-   png_uint_16 num_trans;     /* number of transparency values */
-   png_byte compression;      /* file compression type (always 0) */
-   png_byte filter;           /* file filter type (always 0) */
-   png_byte interlaced;       /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
-   png_byte pass;             /* current interlace pass (0 - 6) */
-   png_byte do_filter;        /* row filter flags (see PNG_FILTER_ in png.h ) */
-   png_byte color_type;       /* color type of file */
-   png_byte bit_depth;        /* bit depth of file */
-   png_byte usr_bit_depth;    /* bit depth of users row: write only */
-   png_byte pixel_depth;      /* number of bits per pixel */
-   png_byte channels;         /* number of channels in file */
-#ifdef PNG_WRITE_SUPPORTED
-   png_byte usr_channels;     /* channels at start of write: write only */
-#endif
-   png_byte sig_bytes;        /* magic bytes read/written from start of file */
-   png_byte maximum_pixel_depth;
-                              /* pixel depth used for the row buffers */
-   png_byte transformed_pixel_depth;
-                              /* pixel depth after read/write transforms */
-#if ZLIB_VERNUM >= 0x1240
-   png_byte zstream_start;    /* at start of an input zlib stream */
-#endif /* Zlib >= 1.2.4 */
-#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
-   png_uint_16 filler;           /* filler bytes for pixel expansion */
-#endif
-
-#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
-   defined(PNG_READ_ALPHA_MODE_SUPPORTED)
-   png_byte background_gamma_type;
-   png_fixed_point background_gamma;
-   png_color_16 background;   /* background color in screen gamma space */
-#ifdef PNG_READ_GAMMA_SUPPORTED
-   png_color_16 background_1; /* background normalized to gamma 1.0 */
-#endif
-#endif /* bKGD */
-
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
-   png_flush_ptr output_flush_fn; /* Function for flushing output */
-   png_uint_32 flush_dist;    /* how many rows apart to flush, 0 - no flush */
-   png_uint_32 flush_rows;    /* number of rows written since last flush */
-#endif
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
-   int gamma_shift;      /* number of "insignificant" bits in 16-bit gamma */
-   png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */
-
-   png_bytep gamma_table;     /* gamma table for 8-bit depth files */
-   png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
-   defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
-   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
-   png_bytep gamma_from_1;    /* converts from 1.0 to screen */
-   png_bytep gamma_to_1;      /* converts from file to 1.0 */
-   png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */
-   png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */
-#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
-#endif
-
-#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED)
-   png_color_8 sig_bit;       /* significant bits in each available channel */
-#endif
-
-#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
-   png_color_8 shift;         /* shift for significant bit transformation */
-#endif
-
-#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \
- || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
-   png_bytep trans_alpha;           /* alpha values for paletted files */
-   png_color_16 trans_color;  /* transparent color for non-paletted files */
-#endif
-
-   png_read_status_ptr read_row_fn;   /* called after each row is decoded */
-   png_write_status_ptr write_row_fn; /* called after each row is encoded */
-#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
-   png_progressive_info_ptr info_fn; /* called after header data fully read */
-   png_progressive_row_ptr row_fn;   /* called after a prog. row is decoded */
-   png_progressive_end_ptr end_fn;   /* called after image is complete */
-   png_bytep save_buffer_ptr;        /* current location in save_buffer */
-   png_bytep save_buffer;            /* buffer for previously read data */
-   png_bytep current_buffer_ptr;     /* current location in current_buffer */
-   png_bytep current_buffer;         /* buffer for recently used data */
-   png_uint_32 push_length;          /* size of current input chunk */
-   png_uint_32 skip_length;          /* bytes to skip in input data */
-   size_t save_buffer_size;          /* amount of data now in save_buffer */
-   size_t save_buffer_max;           /* total size of save_buffer */
-   size_t buffer_size;               /* total amount of available input data */
-   size_t current_buffer_size;       /* amount of data now in current_buffer */
-   int process_mode;                 /* what push library is currently doing */
-   int cur_palette;                  /* current push library palette index */
-
-#endif /* PROGRESSIVE_READ */
-
-#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__)
-/* For the Borland special 64K segment handler */
-   png_bytepp offset_table_ptr;
-   png_bytep offset_table;
-   png_uint_16 offset_table_number;
-   png_uint_16 offset_table_count;
-   png_uint_16 offset_table_count_free;
-#endif
-
-#ifdef PNG_READ_QUANTIZE_SUPPORTED
-   png_bytep palette_lookup; /* lookup table for quantizing */
-   png_bytep quantize_index; /* index translation for palette files */
-#endif
-
-/* Options */
-#ifdef PNG_SET_OPTION_SUPPORTED
-   png_uint_32 options;           /* On/off state (up to 16 options) */
-#endif
-
-#if PNG_LIBPNG_VER < 10700
-/* To do: remove this from libpng-1.7 */
-#ifdef PNG_TIME_RFC1123_SUPPORTED
-   char time_buffer[29]; /* String to hold RFC 1123 time text */
-#endif
-#endif
-
-/* New members added in libpng-1.0.6 */
-
-   png_uint_32 free_me;    /* flags items libpng is responsible for freeing */
-
-#ifdef PNG_USER_CHUNKS_SUPPORTED
-   png_voidp user_chunk_ptr;
-#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
-   png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */
-#endif
-#endif
-
-#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
-   int          unknown_default; /* As PNG_HANDLE_* */
-   unsigned int num_chunk_list;  /* Number of entries in the list */
-   png_bytep    chunk_list;      /* List of png_byte[5]; the textual chunk name
-                                  * followed by a PNG_HANDLE_* byte */
-#endif
-
-/* New members added in libpng-1.0.3 */
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-   png_byte rgb_to_gray_status;
-   /* Added in libpng 1.5.5 to record setting of coefficients: */
-   png_byte rgb_to_gray_coefficients_set;
-   /* These were changed from png_byte in libpng-1.0.6 */
-   png_uint_16 rgb_to_gray_red_coeff;
-   png_uint_16 rgb_to_gray_green_coeff;
-   /* deleted in 1.5.5: rgb_to_gray_blue_coeff; */
-#endif
-
-/* New member added in libpng-1.6.36 */
-#if defined(PNG_READ_EXPAND_SUPPORTED) && \
-    defined(PNG_ARM_NEON_IMPLEMENTATION)
-   png_bytep riffled_palette; /* buffer for accelerated palette expansion */
-#endif
-
-/* New member added in libpng-1.0.4 (renamed in 1.0.9) */
-#if defined(PNG_MNG_FEATURES_SUPPORTED)
-/* Changed from png_byte to png_uint_32 at version 1.2.0 */
-   png_uint_32 mng_features_permitted;
-#endif
-
-/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-   png_byte filter_type;
-#endif
-
-/* New members added in libpng-1.2.0 */
-
-/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */
-#ifdef PNG_USER_MEM_SUPPORTED
-   png_voidp mem_ptr;             /* user supplied struct for mem functions */
-   png_malloc_ptr malloc_fn;      /* function for allocating memory */
-   png_free_ptr free_fn;          /* function for freeing memory */
-#endif
-
-/* New member added in libpng-1.0.13 and 1.2.0 */
-   png_bytep big_row_buf;         /* buffer to save current (unfiltered) row */
-
-#ifdef PNG_READ_QUANTIZE_SUPPORTED
-/* The following three members were added at version 1.0.14 and 1.2.4 */
-   png_bytep quantize_sort;          /* working sort array */
-   png_bytep index_to_palette;       /* where the original index currently is
-                                        in the palette */
-   png_bytep palette_to_index;       /* which original index points to this
-                                         palette color */
-#endif
-
-/* New members added in libpng-1.0.16 and 1.2.6 */
-   png_byte compression_type;
-
-#ifdef PNG_USER_LIMITS_SUPPORTED
-   png_uint_32 user_width_max;
-   png_uint_32 user_height_max;
-
-   /* Added in libpng-1.4.0: Total number of sPLT, text, and unknown
-    * chunks that can be stored (0 means unlimited).
-    */
-   png_uint_32 user_chunk_cache_max;
-
-   /* Total memory that a zTXt, sPLT, iTXt, iCCP, or unknown chunk
-    * can occupy when decompressed.  0 means unlimited.
-    */
-   png_alloc_size_t user_chunk_malloc_max;
-#endif
-
-/* New member added in libpng-1.0.25 and 1.2.17 */
-#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
-   /* Temporary storage for unknown chunk that the library doesn't recognize,
-    * used while reading the chunk.
-    */
-   png_unknown_chunk unknown_chunk;
-#endif
-
-/* New member added in libpng-1.2.26 */
-   size_t old_big_row_buf_size;
-
-#ifdef PNG_READ_SUPPORTED
-/* New member added in libpng-1.2.30 */
-  png_bytep        read_buffer;      /* buffer for reading chunk data */
-  png_alloc_size_t read_buffer_size; /* current size of the buffer */
-#endif
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-  uInt             IDAT_read_size;   /* limit on read buffer size for IDAT */
-#endif
-
-#ifdef PNG_IO_STATE_SUPPORTED
-/* New member added in libpng-1.4.0 */
-   png_uint_32 io_state;
-#endif
-
-/* New member added in libpng-1.5.6 */
-   png_bytep big_prev_row;
-
-/* New member added in libpng-1.5.7 */
-   void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_row_infop row_info,
-      png_bytep row, png_const_bytep prev_row);
-
-#ifdef PNG_READ_SUPPORTED
-#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
-   png_colorspace   colorspace;
-#endif
-#endif
-};
-#endif /* PNGSTRUCT_H */
diff --git a/third_party/libpng16/pngtrans.c b/third_party/libpng16/pngtrans.c
deleted file mode 100644
index 1100f46..0000000
--- a/third_party/libpng16/pngtrans.c
+++ /dev/null
@@ -1,864 +0,0 @@
-
-/* pngtrans.c - transforms the data in a row (used by both readers and writers)
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
-#include "pngpriv.h"
-
-#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
-
-#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
-/* Turn on BGR-to-RGB mapping */
-void PNGAPI
-png_set_bgr(png_structrp png_ptr)
-{
-   png_debug(1, "in png_set_bgr");
-
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->transformations |= PNG_BGR;
-}
-#endif
-
-#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
-/* Turn on 16-bit byte swapping */
-void PNGAPI
-png_set_swap(png_structrp png_ptr)
-{
-   png_debug(1, "in png_set_swap");
-
-   if (png_ptr == NULL)
-      return;
-
-   if (png_ptr->bit_depth == 16)
-      png_ptr->transformations |= PNG_SWAP_BYTES;
-}
-#endif
-
-#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
-/* Turn on pixel packing */
-void PNGAPI
-png_set_packing(png_structrp png_ptr)
-{
-   png_debug(1, "in png_set_packing");
-
-   if (png_ptr == NULL)
-      return;
-
-   if (png_ptr->bit_depth < 8)
-   {
-      png_ptr->transformations |= PNG_PACK;
-#     ifdef PNG_WRITE_SUPPORTED
-         png_ptr->usr_bit_depth = 8;
-#     endif
-   }
-}
-#endif
-
-#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
-/* Turn on packed pixel swapping */
-void PNGAPI
-png_set_packswap(png_structrp png_ptr)
-{
-   png_debug(1, "in png_set_packswap");
-
-   if (png_ptr == NULL)
-      return;
-
-   if (png_ptr->bit_depth < 8)
-      png_ptr->transformations |= PNG_PACKSWAP;
-}
-#endif
-
-#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
-void PNGAPI
-png_set_shift(png_structrp png_ptr, png_const_color_8p true_bits)
-{
-   png_debug(1, "in png_set_shift");
-
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->transformations |= PNG_SHIFT;
-   png_ptr->shift = *true_bits;
-}
-#endif
-
-#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
-    defined(PNG_WRITE_INTERLACING_SUPPORTED)
-int PNGAPI
-png_set_interlace_handling(png_structrp png_ptr)
-{
-   png_debug(1, "in png_set_interlace handling");
-
-   if (png_ptr != 0 && png_ptr->interlaced != 0)
-   {
-      png_ptr->transformations |= PNG_INTERLACE;
-      return (7);
-   }
-
-   return (1);
-}
-#endif
-
-#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
-/* Add a filler byte on read, or remove a filler or alpha byte on write.
- * The filler type has changed in v0.95 to allow future 2-byte fillers
- * for 48-bit input data, as well as to avoid problems with some compilers
- * that don't like bytes as parameters.
- */
-void PNGAPI
-png_set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc)
-{
-   png_debug(1, "in png_set_filler");
-
-   if (png_ptr == NULL)
-      return;
-
-   /* In libpng 1.6 it is possible to determine whether this is a read or write
-    * operation and therefore to do more checking here for a valid call.
-    */
-   if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0)
-   {
-#     ifdef PNG_READ_FILLER_SUPPORTED
-         /* On read png_set_filler is always valid, regardless of the base PNG
-          * format, because other transformations can give a format where the
-          * filler code can execute (basically an 8 or 16-bit component RGB or G
-          * format.)
-          *
-          * NOTE: usr_channels is not used by the read code!  (This has led to
-          * confusion in the past.)  The filler is only used in the read code.
-          */
-         png_ptr->filler = (png_uint_16)filler;
-#     else
-         png_app_error(png_ptr, "png_set_filler not supported on read");
-         PNG_UNUSED(filler) /* not used in the write case */
-         return;
-#     endif
-   }
-
-   else /* write */
-   {
-#     ifdef PNG_WRITE_FILLER_SUPPORTED
-         /* On write the usr_channels parameter must be set correctly at the
-          * start to record the number of channels in the app-supplied data.
-          */
-         switch (png_ptr->color_type)
-         {
-            case PNG_COLOR_TYPE_RGB:
-               png_ptr->usr_channels = 4;
-               break;
-
-            case PNG_COLOR_TYPE_GRAY:
-               if (png_ptr->bit_depth >= 8)
-               {
-                  png_ptr->usr_channels = 2;
-                  break;
-               }
-
-               else
-               {
-                  /* There simply isn't any code in libpng to strip out bits
-                   * from bytes when the components are less than a byte in
-                   * size!
-                   */
-                  png_app_error(png_ptr,
-                      "png_set_filler is invalid for"
-                      " low bit depth gray output");
-                  return;
-               }
-
-            default:
-               png_app_error(png_ptr,
-                   "png_set_filler: inappropriate color type");
-               return;
-         }
-#     else
-         png_app_error(png_ptr, "png_set_filler not supported on write");
-         return;
-#     endif
-   }
-
-   /* Here on success - libpng supports the operation, set the transformation
-    * and the flag to say where the filler channel is.
-    */
-   png_ptr->transformations |= PNG_FILLER;
-
-   if (filler_loc == PNG_FILLER_AFTER)
-      png_ptr->flags |= PNG_FLAG_FILLER_AFTER;
-
-   else
-      png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER;
-}
-
-/* Added to libpng-1.2.7 */
-void PNGAPI
-png_set_add_alpha(png_structrp png_ptr, png_uint_32 filler, int filler_loc)
-{
-   png_debug(1, "in png_set_add_alpha");
-
-   if (png_ptr == NULL)
-      return;
-
-   png_set_filler(png_ptr, filler, filler_loc);
-   /* The above may fail to do anything. */
-   if ((png_ptr->transformations & PNG_FILLER) != 0)
-      png_ptr->transformations |= PNG_ADD_ALPHA;
-}
-
-#endif
-
-#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
-    defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
-void PNGAPI
-png_set_swap_alpha(png_structrp png_ptr)
-{
-   png_debug(1, "in png_set_swap_alpha");
-
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->transformations |= PNG_SWAP_ALPHA;
-}
-#endif
-
-#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \
-    defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
-void PNGAPI
-png_set_invert_alpha(png_structrp png_ptr)
-{
-   png_debug(1, "in png_set_invert_alpha");
-
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->transformations |= PNG_INVERT_ALPHA;
-}
-#endif
-
-#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
-void PNGAPI
-png_set_invert_mono(png_structrp png_ptr)
-{
-   png_debug(1, "in png_set_invert_mono");
-
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->transformations |= PNG_INVERT_MONO;
-}
-
-/* Invert monochrome grayscale data */
-void /* PRIVATE */
-png_do_invert(png_row_infop row_info, png_bytep row)
-{
-   png_debug(1, "in png_do_invert");
-
-  /* This test removed from libpng version 1.0.13 and 1.2.0:
-   *   if (row_info->bit_depth == 1 &&
-   */
-   if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
-   {
-      png_bytep rp = row;
-      size_t i;
-      size_t istop = row_info->rowbytes;
-
-      for (i = 0; i < istop; i++)
-      {
-         *rp = (png_byte)(~(*rp));
-         rp++;
-      }
-   }
-
-   else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
-      row_info->bit_depth == 8)
-   {
-      png_bytep rp = row;
-      size_t i;
-      size_t istop = row_info->rowbytes;
-
-      for (i = 0; i < istop; i += 2)
-      {
-         *rp = (png_byte)(~(*rp));
-         rp += 2;
-      }
-   }
-
-#ifdef PNG_16BIT_SUPPORTED
-   else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
-      row_info->bit_depth == 16)
-   {
-      png_bytep rp = row;
-      size_t i;
-      size_t istop = row_info->rowbytes;
-
-      for (i = 0; i < istop; i += 4)
-      {
-         *rp = (png_byte)(~(*rp));
-         *(rp + 1) = (png_byte)(~(*(rp + 1)));
-         rp += 4;
-      }
-   }
-#endif
-}
-#endif
-
-#ifdef PNG_16BIT_SUPPORTED
-#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
-/* Swaps byte order on 16-bit depth images */
-void /* PRIVATE */
-png_do_swap(png_row_infop row_info, png_bytep row)
-{
-   png_debug(1, "in png_do_swap");
-
-   if (row_info->bit_depth == 16)
-   {
-      png_bytep rp = row;
-      png_uint_32 i;
-      png_uint_32 istop= row_info->width * row_info->channels;
-
-      for (i = 0; i < istop; i++, rp += 2)
-      {
-#ifdef PNG_BUILTIN_BSWAP16_SUPPORTED
-         /* Feature added to libpng-1.6.11 for testing purposes, not
-          * enabled by default.
-          */
-         *(png_uint_16*)rp = __builtin_bswap16(*(png_uint_16*)rp);
-#else
-         png_byte t = *rp;
-         *rp = *(rp + 1);
-         *(rp + 1) = t;
-#endif
-      }
-   }
-}
-#endif
-#endif
-
-#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
-static const png_byte onebppswaptable[256] = {
-   0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
-   0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
-   0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
-   0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
-   0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
-   0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
-   0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
-   0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
-   0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
-   0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
-   0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
-   0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
-   0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
-   0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
-   0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
-   0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
-   0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
-   0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
-   0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
-   0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
-   0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
-   0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
-   0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
-   0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
-   0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
-   0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
-   0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
-   0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
-   0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
-   0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
-   0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
-   0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
-};
-
-static const png_byte twobppswaptable[256] = {
-   0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0,
-   0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0,
-   0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4,
-   0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4,
-   0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8,
-   0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8,
-   0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC,
-   0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC,
-   0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1,
-   0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1,
-   0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5,
-   0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5,
-   0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9,
-   0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9,
-   0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD,
-   0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD,
-   0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2,
-   0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2,
-   0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6,
-   0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6,
-   0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA,
-   0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA,
-   0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE,
-   0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE,
-   0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3,
-   0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3,
-   0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7,
-   0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7,
-   0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB,
-   0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB,
-   0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF,
-   0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF
-};
-
-static const png_byte fourbppswaptable[256] = {
-   0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
-   0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0,
-   0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71,
-   0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1,
-   0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72,
-   0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2,
-   0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73,
-   0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3,
-   0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74,
-   0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4,
-   0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75,
-   0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5,
-   0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76,
-   0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6,
-   0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77,
-   0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7,
-   0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78,
-   0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8,
-   0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79,
-   0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9,
-   0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A,
-   0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA,
-   0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B,
-   0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB,
-   0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C,
-   0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC,
-   0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D,
-   0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD,
-   0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E,
-   0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE,
-   0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F,
-   0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF
-};
-
-/* Swaps pixel packing order within bytes */
-void /* PRIVATE */
-png_do_packswap(png_row_infop row_info, png_bytep row)
-{
-   png_debug(1, "in png_do_packswap");
-
-   if (row_info->bit_depth < 8)
-   {
-      png_bytep rp;
-      png_const_bytep end, table;
-
-      end = row + row_info->rowbytes;
-
-      if (row_info->bit_depth == 1)
-         table = onebppswaptable;
-
-      else if (row_info->bit_depth == 2)
-         table = twobppswaptable;
-
-      else if (row_info->bit_depth == 4)
-         table = fourbppswaptable;
-
-      else
-         return;
-
-      for (rp = row; rp < end; rp++)
-         *rp = table[*rp];
-   }
-}
-#endif /* PACKSWAP || WRITE_PACKSWAP */
-
-#if defined(PNG_WRITE_FILLER_SUPPORTED) || \
-    defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
-/* Remove a channel - this used to be 'png_do_strip_filler' but it used a
- * somewhat weird combination of flags to determine what to do.  All the calls
- * to png_do_strip_filler are changed in 1.5.2 to call this instead with the
- * correct arguments.
- *
- * The routine isn't general - the channel must be the channel at the start or
- * end (not in the middle) of each pixel.
- */
-void /* PRIVATE */
-png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start)
-{
-   png_bytep sp = row; /* source pointer */
-   png_bytep dp = row; /* destination pointer */
-   png_bytep ep = row + row_info->rowbytes; /* One beyond end of row */
-
-   /* At the start sp will point to the first byte to copy and dp to where
-    * it is copied to.  ep always points just beyond the end of the row, so
-    * the loop simply copies (channels-1) channels until sp reaches ep.
-    *
-    * at_start:        0 -- convert AG, XG, ARGB, XRGB, AAGG, XXGG, etc.
-    *            nonzero -- convert GA, GX, RGBA, RGBX, GGAA, RRGGBBXX, etc.
-    */
-
-   /* GA, GX, XG cases */
-   if (row_info->channels == 2)
-   {
-      if (row_info->bit_depth == 8)
-      {
-         if (at_start != 0) /* Skip initial filler */
-            ++sp;
-         else          /* Skip initial channel and, for sp, the filler */
-         {
-            sp += 2; ++dp;
-         }
-
-         /* For a 1 pixel wide image there is nothing to do */
-         while (sp < ep)
-         {
-            *dp++ = *sp; sp += 2;
-         }
-
-         row_info->pixel_depth = 8;
-      }
-
-      else if (row_info->bit_depth == 16)
-      {
-         if (at_start != 0) /* Skip initial filler */
-            sp += 2;
-         else          /* Skip initial channel and, for sp, the filler */
-         {
-            sp += 4; dp += 2;
-         }
-
-         while (sp < ep)
-         {
-            *dp++ = *sp++; *dp++ = *sp; sp += 3;
-         }
-
-         row_info->pixel_depth = 16;
-      }
-
-      else
-         return; /* bad bit depth */
-
-      row_info->channels = 1;
-
-      /* Finally fix the color type if it records an alpha channel */
-      if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
-         row_info->color_type = PNG_COLOR_TYPE_GRAY;
-   }
-
-   /* RGBA, RGBX, XRGB cases */
-   else if (row_info->channels == 4)
-   {
-      if (row_info->bit_depth == 8)
-      {
-         if (at_start != 0) /* Skip initial filler */
-            ++sp;
-         else          /* Skip initial channels and, for sp, the filler */
-         {
-            sp += 4; dp += 3;
-         }
-
-         /* Note that the loop adds 3 to dp and 4 to sp each time. */
-         while (sp < ep)
-         {
-            *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp; sp += 2;
-         }
-
-         row_info->pixel_depth = 24;
-      }
-
-      else if (row_info->bit_depth == 16)
-      {
-         if (at_start != 0) /* Skip initial filler */
-            sp += 2;
-         else          /* Skip initial channels and, for sp, the filler */
-         {
-            sp += 8; dp += 6;
-         }
-
-         while (sp < ep)
-         {
-            /* Copy 6 bytes, skip 2 */
-            *dp++ = *sp++; *dp++ = *sp++;
-            *dp++ = *sp++; *dp++ = *sp++;
-            *dp++ = *sp++; *dp++ = *sp; sp += 3;
-         }
-
-         row_info->pixel_depth = 48;
-      }
-
-      else
-         return; /* bad bit depth */
-
-      row_info->channels = 3;
-
-      /* Finally fix the color type if it records an alpha channel */
-      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
-         row_info->color_type = PNG_COLOR_TYPE_RGB;
-   }
-
-   else
-      return; /* The filler channel has gone already */
-
-   /* Fix the rowbytes value. */
-   row_info->rowbytes = (size_t)(dp-row);
-}
-#endif
-
-#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
-/* Swaps red and blue bytes within a pixel */
-void /* PRIVATE */
-png_do_bgr(png_row_infop row_info, png_bytep row)
-{
-   png_debug(1, "in png_do_bgr");
-
-   if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0)
-   {
-      png_uint_32 row_width = row_info->width;
-      if (row_info->bit_depth == 8)
-      {
-         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
-         {
-            png_bytep rp;
-            png_uint_32 i;
-
-            for (i = 0, rp = row; i < row_width; i++, rp += 3)
-            {
-               png_byte save = *rp;
-               *rp = *(rp + 2);
-               *(rp + 2) = save;
-            }
-         }
-
-         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
-         {
-            png_bytep rp;
-            png_uint_32 i;
-
-            for (i = 0, rp = row; i < row_width; i++, rp += 4)
-            {
-               png_byte save = *rp;
-               *rp = *(rp + 2);
-               *(rp + 2) = save;
-            }
-         }
-      }
-
-#ifdef PNG_16BIT_SUPPORTED
-      else if (row_info->bit_depth == 16)
-      {
-         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
-         {
-            png_bytep rp;
-            png_uint_32 i;
-
-            for (i = 0, rp = row; i < row_width; i++, rp += 6)
-            {
-               png_byte save = *rp;
-               *rp = *(rp + 4);
-               *(rp + 4) = save;
-               save = *(rp + 1);
-               *(rp + 1) = *(rp + 5);
-               *(rp + 5) = save;
-            }
-         }
-
-         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
-         {
-            png_bytep rp;
-            png_uint_32 i;
-
-            for (i = 0, rp = row; i < row_width; i++, rp += 8)
-            {
-               png_byte save = *rp;
-               *rp = *(rp + 4);
-               *(rp + 4) = save;
-               save = *(rp + 1);
-               *(rp + 1) = *(rp + 5);
-               *(rp + 5) = save;
-            }
-         }
-      }
-#endif
-   }
-}
-#endif /* READ_BGR || WRITE_BGR */
-
-#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \
-    defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED)
-/* Added at libpng-1.5.10 */
-void /* PRIVATE */
-png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info)
-{
-   if (png_ptr->num_palette < (1 << row_info->bit_depth) &&
-      png_ptr->num_palette > 0) /* num_palette can be 0 in MNG files */
-   {
-      /* Calculations moved outside switch in an attempt to stop different
-       * compiler warnings.  'padding' is in *bits* within the last byte, it is
-       * an 'int' because pixel_depth becomes an 'int' in the expression below,
-       * and this calculation is used because it avoids warnings that other
-       * forms produced on either GCC or MSVC.
-       */
-      int padding = PNG_PADBITS(row_info->pixel_depth, row_info->width);
-      png_bytep rp = png_ptr->row_buf + row_info->rowbytes - 1;
-
-      switch (row_info->bit_depth)
-      {
-         case 1:
-         {
-            /* in this case, all bytes must be 0 so we don't need
-             * to unpack the pixels except for the rightmost one.
-             */
-            for (; rp > png_ptr->row_buf; rp--)
-            {
-              if ((*rp >> padding) != 0)
-                 png_ptr->num_palette_max = 1;
-              padding = 0;
-            }
-
-            break;
-         }
-
-         case 2:
-         {
-            for (; rp > png_ptr->row_buf; rp--)
-            {
-              int i = ((*rp >> padding) & 0x03);
-
-              if (i > png_ptr->num_palette_max)
-                 png_ptr->num_palette_max = i;
-
-              i = (((*rp >> padding) >> 2) & 0x03);
-
-              if (i > png_ptr->num_palette_max)
-                 png_ptr->num_palette_max = i;
-
-              i = (((*rp >> padding) >> 4) & 0x03);
-
-              if (i > png_ptr->num_palette_max)
-                 png_ptr->num_palette_max = i;
-
-              i = (((*rp >> padding) >> 6) & 0x03);
-
-              if (i > png_ptr->num_palette_max)
-                 png_ptr->num_palette_max = i;
-
-              padding = 0;
-            }
-
-            break;
-         }
-
-         case 4:
-         {
-            for (; rp > png_ptr->row_buf; rp--)
-            {
-              int i = ((*rp >> padding) & 0x0f);
-
-              if (i > png_ptr->num_palette_max)
-                 png_ptr->num_palette_max = i;
-
-              i = (((*rp >> padding) >> 4) & 0x0f);
-
-              if (i > png_ptr->num_palette_max)
-                 png_ptr->num_palette_max = i;
-
-              padding = 0;
-            }
-
-            break;
-         }
-
-         case 8:
-         {
-            for (; rp > png_ptr->row_buf; rp--)
-            {
-               if (*rp > png_ptr->num_palette_max)
-                  png_ptr->num_palette_max = (int) *rp;
-            }
-
-            break;
-         }
-
-         default:
-            break;
-      }
-   }
-}
-#endif /* CHECK_FOR_INVALID_INDEX */
-
-#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
-    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
-#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
-void PNGAPI
-png_set_user_transform_info(png_structrp png_ptr, png_voidp
-   user_transform_ptr, int user_transform_depth, int user_transform_channels)
-{
-   png_debug(1, "in png_set_user_transform_info");
-
-   if (png_ptr == NULL)
-      return;
-
-#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
-   if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
-      (png_ptr->flags & PNG_FLAG_ROW_INIT) != 0)
-   {
-      png_app_error(png_ptr,
-          "info change after png_start_read_image or png_read_update_info");
-      return;
-   }
-#endif
-
-   png_ptr->user_transform_ptr = user_transform_ptr;
-   png_ptr->user_transform_depth = (png_byte)user_transform_depth;
-   png_ptr->user_transform_channels = (png_byte)user_transform_channels;
-}
-#endif
-
-/* This function returns a pointer to the user_transform_ptr associated with
- * the user transform functions.  The application should free any memory
- * associated with this pointer before png_write_destroy and png_read_destroy
- * are called.
- */
-#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
-png_voidp PNGAPI
-png_get_user_transform_ptr(png_const_structrp png_ptr)
-{
-   if (png_ptr == NULL)
-      return (NULL);
-
-   return png_ptr->user_transform_ptr;
-}
-#endif
-
-#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
-png_uint_32 PNGAPI
-png_get_current_row_number(png_const_structrp png_ptr)
-{
-   /* See the comments in png.h - this is the sub-image row when reading an
-    * interlaced image.
-    */
-   if (png_ptr != NULL)
-      return png_ptr->row_number;
-
-   return PNG_UINT_32_MAX; /* help the app not to fail silently */
-}
-
-png_byte PNGAPI
-png_get_current_pass_number(png_const_structrp png_ptr)
-{
-   if (png_ptr != NULL)
-      return png_ptr->pass;
-   return 8; /* invalid */
-}
-#endif /* USER_TRANSFORM_INFO */
-#endif /* READ_USER_TRANSFORM || WRITE_USER_TRANSFORM */
-#endif /* READ || WRITE */
diff --git a/third_party/libpng16/pngwio.c b/third_party/libpng16/pngwio.c
deleted file mode 100644
index 10e919d..0000000
--- a/third_party/libpng16/pngwio.c
+++ /dev/null
@@ -1,168 +0,0 @@
-
-/* pngwio.c - functions for data output
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2014,2016,2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- *
- * This file provides a location for all output.  Users who need
- * special handling are expected to write functions that have the same
- * arguments as these and perform similar functions, but that possibly
- * use different output methods.  Note that you shouldn't change these
- * functions, but rather write replacement functions and then change
- * them at run time with png_set_write_fn(...).
- */
-
-#include "pngpriv.h"
-
-#ifdef PNG_WRITE_SUPPORTED
-
-/* Write the data to whatever output you are using.  The default routine
- * writes to a file pointer.  Note that this routine sometimes gets called
- * with very small lengths, so you should implement some kind of simple
- * buffering if you are using unbuffered writes.  This should never be asked
- * to write more than 64K on a 16-bit machine.
- */
-
-void /* PRIVATE */
-png_write_data(png_structrp png_ptr, png_const_bytep data, size_t length)
-{
-   /* NOTE: write_data_fn must not change the buffer! */
-   if (png_ptr->write_data_fn != NULL )
-      (*(png_ptr->write_data_fn))(png_ptr, png_constcast(png_bytep,data),
-          length);
-
-   else
-      png_error(png_ptr, "Call to NULL write function");
-}
-
-#ifdef PNG_STDIO_SUPPORTED
-/* This is the function that does the actual writing of data.  If you are
- * not writing to a standard C stream, you should create a replacement
- * write_data function and use it at run time with png_set_write_fn(), rather
- * than changing the library.
- */
-void PNGCBAPI
-png_default_write_data(png_structp png_ptr, png_bytep data, size_t length)
-{
-   size_t check;
-
-   if (png_ptr == NULL)
-      return;
-
-   check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr));
-
-   if (check != length)
-      png_error(png_ptr, "Write Error");
-}
-#endif
-
-/* This function is called to output any data pending writing (normally
- * to disk).  After png_flush is called, there should be no data pending
- * writing in any buffers.
- */
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
-void /* PRIVATE */
-png_flush(png_structrp png_ptr)
-{
-   if (png_ptr->output_flush_fn != NULL)
-      (*(png_ptr->output_flush_fn))(png_ptr);
-}
-
-#  ifdef PNG_STDIO_SUPPORTED
-void PNGCBAPI
-png_default_flush(png_structp png_ptr)
-{
-   png_FILE_p io_ptr;
-
-   if (png_ptr == NULL)
-      return;
-
-   io_ptr = png_voidcast(png_FILE_p, (png_ptr->io_ptr));
-   fflush(io_ptr);
-}
-#  endif
-#endif
-
-/* This function allows the application to supply new output functions for
- * libpng if standard C streams aren't being used.
- *
- * This function takes as its arguments:
- * png_ptr       - pointer to a png output data structure
- * io_ptr        - pointer to user supplied structure containing info about
- *                 the output functions.  May be NULL.
- * write_data_fn - pointer to a new output function that takes as its
- *                 arguments a pointer to a png_struct, a pointer to
- *                 data to be written, and a 32-bit unsigned int that is
- *                 the number of bytes to be written.  The new write
- *                 function should call png_error(png_ptr, "Error msg")
- *                 to exit and output any fatal error messages.  May be
- *                 NULL, in which case libpng's default function will
- *                 be used.
- * flush_data_fn - pointer to a new flush function that takes as its
- *                 arguments a pointer to a png_struct.  After a call to
- *                 the flush function, there should be no data in any buffers
- *                 or pending transmission.  If the output method doesn't do
- *                 any buffering of output, a function prototype must still be
- *                 supplied although it doesn't have to do anything.  If
- *                 PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile
- *                 time, output_flush_fn will be ignored, although it must be
- *                 supplied for compatibility.  May be NULL, in which case
- *                 libpng's default function will be used, if
- *                 PNG_WRITE_FLUSH_SUPPORTED is defined.  This is not
- *                 a good idea if io_ptr does not point to a standard
- *                 *FILE structure.
- */
-void PNGAPI
-png_set_write_fn(png_structrp png_ptr, png_voidp io_ptr,
-    png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)
-{
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->io_ptr = io_ptr;
-
-#ifdef PNG_STDIO_SUPPORTED
-   if (write_data_fn != NULL)
-      png_ptr->write_data_fn = write_data_fn;
-
-   else
-      png_ptr->write_data_fn = png_default_write_data;
-#else
-   png_ptr->write_data_fn = write_data_fn;
-#endif
-
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
-#  ifdef PNG_STDIO_SUPPORTED
-
-   if (output_flush_fn != NULL)
-      png_ptr->output_flush_fn = output_flush_fn;
-
-   else
-      png_ptr->output_flush_fn = png_default_flush;
-
-#  else
-   png_ptr->output_flush_fn = output_flush_fn;
-#  endif
-#else
-   PNG_UNUSED(output_flush_fn)
-#endif /* WRITE_FLUSH */
-
-#ifdef PNG_READ_SUPPORTED
-   /* It is an error to read while writing a png file */
-   if (png_ptr->read_data_fn != NULL)
-   {
-      png_ptr->read_data_fn = NULL;
-
-      png_warning(png_ptr,
-          "Can't set both read_data_fn and write_data_fn in the"
-          " same structure");
-   }
-#endif
-}
-#endif /* WRITE */
diff --git a/third_party/libpng16/pngwrite.c b/third_party/libpng16/pngwrite.c
deleted file mode 100644
index 59377a4..0000000
--- a/third_party/libpng16/pngwrite.c
+++ /dev/null
@@ -1,2395 +0,0 @@
-
-/* pngwrite.c - general routines to write a PNG file
- *
- * Copyright (c) 2018-2019 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
-#include "pngpriv.h"
-#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
-#  include <errno.h>
-#endif /* SIMPLIFIED_WRITE_STDIO */
-
-#ifdef PNG_WRITE_SUPPORTED
-
-#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
-/* Write out all the unknown chunks for the current given location */
-static void
-write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr,
-    unsigned int where)
-{
-   if (info_ptr->unknown_chunks_num != 0)
-   {
-      png_const_unknown_chunkp up;
-
-      png_debug(5, "writing extra chunks");
-
-      for (up = info_ptr->unknown_chunks;
-           up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
-           ++up)
-         if ((up->location & where) != 0)
-      {
-         /* If per-chunk unknown chunk handling is enabled use it, otherwise
-          * just write the chunks the application has set.
-          */
-#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
-         int keep = png_handle_as_unknown(png_ptr, up->name);
-
-         /* NOTE: this code is radically different from the read side in the
-          * matter of handling an ancillary unknown chunk.  In the read side
-          * the default behavior is to discard it, in the code below the default
-          * behavior is to write it.  Critical chunks are, however, only
-          * written if explicitly listed or if the default is set to write all
-          * unknown chunks.
-          *
-          * The default handling is also slightly weird - it is not possible to
-          * stop the writing of all unsafe-to-copy chunks!
-          *
-          * TODO: REVIEW: this would seem to be a bug.
-          */
-         if (keep != PNG_HANDLE_CHUNK_NEVER &&
-             ((up->name[3] & 0x20) /* safe-to-copy overrides everything */ ||
-              keep == PNG_HANDLE_CHUNK_ALWAYS ||
-              (keep == PNG_HANDLE_CHUNK_AS_DEFAULT &&
-               png_ptr->unknown_default == PNG_HANDLE_CHUNK_ALWAYS)))
-#endif
-         {
-            /* TODO: review, what is wrong with a zero length unknown chunk? */
-            if (up->size == 0)
-               png_warning(png_ptr, "Writing zero-length unknown chunk");
-
-            png_write_chunk(png_ptr, up->name, up->data, up->size);
-         }
-      }
-   }
-}
-#endif /* WRITE_UNKNOWN_CHUNKS */
-
-/* Writes all the PNG information.  This is the suggested way to use the
- * library.  If you have a new chunk to add, make a function to write it,
- * and put it in the correct location here.  If you want the chunk written
- * after the image data, put it in png_write_end().  I strongly encourage
- * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing
- * the chunk, as that will keep the code from breaking if you want to just
- * write a plain PNG file.  If you have long comments, I suggest writing
- * them in png_write_end(), and compressing them.
- */
-void PNGAPI
-png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr)
-{
-   png_debug(1, "in png_write_info_before_PLTE");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   if ((png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE) == 0)
-   {
-      /* Write PNG signature */
-      png_write_sig(png_ptr);
-
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-      if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0 && \
-          png_ptr->mng_features_permitted != 0)
-      {
-         png_warning(png_ptr,
-             "MNG features are not allowed in a PNG datastream");
-         png_ptr->mng_features_permitted = 0;
-      }
-#endif
-
-      /* Write IHDR information. */
-      png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,
-          info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type,
-          info_ptr->filter_type,
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
-          info_ptr->interlace_type
-#else
-          0
-#endif
-         );
-
-      /* The rest of these check to see if the valid field has the appropriate
-       * flag set, and if it does, writes the chunk.
-       *
-       * 1.6.0: COLORSPACE support controls the writing of these chunks too, and
-       * the chunks will be written if the WRITE routine is there and
-       * information * is available in the COLORSPACE. (See
-       * png_colorspace_sync_info in png.c for where the valid flags get set.)
-       *
-       * Under certain circumstances the colorspace can be invalidated without
-       * syncing the info_struct 'valid' flags; this happens if libpng detects
-       * an error and calls png_error while the color space is being set, yet
-       * the application continues writing the PNG.  So check the 'invalid'
-       * flag here too.
-       */
-#ifdef PNG_GAMMA_SUPPORTED
-#  ifdef PNG_WRITE_gAMA_SUPPORTED
-      if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
-          (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) != 0 &&
-          (info_ptr->valid & PNG_INFO_gAMA) != 0)
-         png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma);
-#  endif
-#endif
-
-#ifdef PNG_COLORSPACE_SUPPORTED
-      /* Write only one of sRGB or an ICC profile.  If a profile was supplied
-       * and it matches one of the known sRGB ones issue a warning.
-       */
-#  ifdef PNG_WRITE_iCCP_SUPPORTED
-         if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
-             (info_ptr->valid & PNG_INFO_iCCP) != 0)
-         {
-#    ifdef PNG_WRITE_sRGB_SUPPORTED
-               if ((info_ptr->valid & PNG_INFO_sRGB) != 0)
-                  png_app_warning(png_ptr,
-                      "profile matches sRGB but writing iCCP instead");
-#     endif
-
-            png_write_iCCP(png_ptr, info_ptr->iccp_name,
-                info_ptr->iccp_profile);
-         }
-#     ifdef PNG_WRITE_sRGB_SUPPORTED
-         else
-#     endif
-#  endif
-
-#  ifdef PNG_WRITE_sRGB_SUPPORTED
-         if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
-             (info_ptr->valid & PNG_INFO_sRGB) != 0)
-            png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent);
-#  endif /* WRITE_sRGB */
-#endif /* COLORSPACE */
-
-#ifdef PNG_WRITE_sBIT_SUPPORTED
-         if ((info_ptr->valid & PNG_INFO_sBIT) != 0)
-            png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);
-#endif
-
-#ifdef PNG_COLORSPACE_SUPPORTED
-#  ifdef PNG_WRITE_cHRM_SUPPORTED
-         if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
-             (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) != 0 &&
-             (info_ptr->valid & PNG_INFO_cHRM) != 0)
-            png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy);
-#  endif
-#endif
-
-#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
-         write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_IHDR);
-#endif
-
-      png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE;
-   }
-}
-
-void PNGAPI
-png_write_info(png_structrp png_ptr, png_const_inforp info_ptr)
-{
-#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
-   int i;
-#endif
-
-   png_debug(1, "in png_write_info");
-
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   png_write_info_before_PLTE(png_ptr, info_ptr);
-
-   if ((info_ptr->valid & PNG_INFO_PLTE) != 0)
-      png_write_PLTE(png_ptr, info_ptr->palette,
-          (png_uint_32)info_ptr->num_palette);
-
-   else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-      png_error(png_ptr, "Valid palette required for paletted images");
-
-#ifdef PNG_WRITE_tRNS_SUPPORTED
-   if ((info_ptr->valid & PNG_INFO_tRNS) !=0)
-   {
-#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
-      /* Invert the alpha channel (in tRNS) */
-      if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0 &&
-          info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-      {
-         int j, jend;
-
-         jend = info_ptr->num_trans;
-         if (jend > PNG_MAX_PALETTE_LENGTH)
-            jend = PNG_MAX_PALETTE_LENGTH;
-
-         for (j = 0; j<jend; ++j)
-            info_ptr->trans_alpha[j] =
-               (png_byte)(255 - info_ptr->trans_alpha[j]);
-      }
-#endif
-      png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color),
-          info_ptr->num_trans, info_ptr->color_type);
-   }
-#endif
-#ifdef PNG_WRITE_bKGD_SUPPORTED
-   if ((info_ptr->valid & PNG_INFO_bKGD) != 0)
-      png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);
-#endif
-
-#ifdef PNG_WRITE_eXIf_SUPPORTED
-   if ((info_ptr->valid & PNG_INFO_eXIf) != 0)
-      png_write_eXIf(png_ptr, info_ptr->exif, info_ptr->num_exif);
-#endif
-
-#ifdef PNG_WRITE_hIST_SUPPORTED
-   if ((info_ptr->valid & PNG_INFO_hIST) != 0)
-      png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
-#endif
-
-#ifdef PNG_WRITE_oFFs_SUPPORTED
-   if ((info_ptr->valid & PNG_INFO_oFFs) != 0)
-      png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,
-          info_ptr->offset_unit_type);
-#endif
-
-#ifdef PNG_WRITE_pCAL_SUPPORTED
-   if ((info_ptr->valid & PNG_INFO_pCAL) != 0)
-      png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,
-          info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,
-          info_ptr->pcal_units, info_ptr->pcal_params);
-#endif
-
-#ifdef PNG_WRITE_sCAL_SUPPORTED
-   if ((info_ptr->valid & PNG_INFO_sCAL) != 0)
-      png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit,
-          info_ptr->scal_s_width, info_ptr->scal_s_height);
-#endif /* sCAL */
-
-#ifdef PNG_WRITE_pHYs_SUPPORTED
-   if ((info_ptr->valid & PNG_INFO_pHYs) != 0)
-      png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,
-          info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);
-#endif /* pHYs */
-
-#ifdef PNG_WRITE_tIME_SUPPORTED
-   if ((info_ptr->valid & PNG_INFO_tIME) != 0)
-   {
-      png_write_tIME(png_ptr, &(info_ptr->mod_time));
-      png_ptr->mode |= PNG_WROTE_tIME;
-   }
-#endif /* tIME */
-
-#ifdef PNG_WRITE_sPLT_SUPPORTED
-   if ((info_ptr->valid & PNG_INFO_sPLT) != 0)
-      for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)
-         png_write_sPLT(png_ptr, info_ptr->splt_palettes + i);
-#endif /* sPLT */
-
-#ifdef PNG_WRITE_TEXT_SUPPORTED
-   /* Check to see if we need to write text chunks */
-   for (i = 0; i < info_ptr->num_text; i++)
-   {
-      png_debug2(2, "Writing header text chunk %d, type %d", i,
-          info_ptr->text[i].compression);
-      /* An internationalized chunk? */
-      if (info_ptr->text[i].compression > 0)
-      {
-#ifdef PNG_WRITE_iTXt_SUPPORTED
-         /* Write international chunk */
-         png_write_iTXt(png_ptr,
-             info_ptr->text[i].compression,
-             info_ptr->text[i].key,
-             info_ptr->text[i].lang,
-             info_ptr->text[i].lang_key,
-             info_ptr->text[i].text);
-         /* Mark this chunk as written */
-         if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
-            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
-         else
-            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
-#else
-         png_warning(png_ptr, "Unable to write international text");
-#endif
-      }
-
-      /* If we want a compressed text chunk */
-      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt)
-      {
-#ifdef PNG_WRITE_zTXt_SUPPORTED
-         /* Write compressed chunk */
-         png_write_zTXt(png_ptr, info_ptr->text[i].key,
-             info_ptr->text[i].text, info_ptr->text[i].compression);
-         /* Mark this chunk as written */
-         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
-#else
-         png_warning(png_ptr, "Unable to write compressed text");
-#endif
-      }
-
-      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
-      {
-#ifdef PNG_WRITE_tEXt_SUPPORTED
-         /* Write uncompressed chunk */
-         png_write_tEXt(png_ptr, info_ptr->text[i].key,
-             info_ptr->text[i].text,
-             0);
-         /* Mark this chunk as written */
-         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
-#else
-         /* Can't get here */
-         png_warning(png_ptr, "Unable to write uncompressed text");
-#endif
-      }
-   }
-#endif /* tEXt */
-
-#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
-   write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_PLTE);
-#endif
-}
-
-/* Writes the end of the PNG file.  If you don't want to write comments or
- * time information, you can pass NULL for info.  If you already wrote these
- * in png_write_info(), do not write them again here.  If you have long
- * comments, I suggest writing them here, and compressing them.
- */
-void PNGAPI
-png_write_end(png_structrp png_ptr, png_inforp info_ptr)
-{
-   png_debug(1, "in png_write_end");
-
-   if (png_ptr == NULL)
-      return;
-
-   if ((png_ptr->mode & PNG_HAVE_IDAT) == 0)
-      png_error(png_ptr, "No IDATs written into file");
-
-#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
-   if (png_ptr->num_palette_max > png_ptr->num_palette)
-      png_benign_error(png_ptr, "Wrote palette index exceeding num_palette");
-#endif
-
-   /* See if user wants us to write information chunks */
-   if (info_ptr != NULL)
-   {
-#ifdef PNG_WRITE_TEXT_SUPPORTED
-      int i; /* local index variable */
-#endif
-#ifdef PNG_WRITE_tIME_SUPPORTED
-      /* Check to see if user has supplied a time chunk */
-      if ((info_ptr->valid & PNG_INFO_tIME) != 0 &&
-          (png_ptr->mode & PNG_WROTE_tIME) == 0)
-         png_write_tIME(png_ptr, &(info_ptr->mod_time));
-
-#endif
-#ifdef PNG_WRITE_TEXT_SUPPORTED
-      /* Loop through comment chunks */
-      for (i = 0; i < info_ptr->num_text; i++)
-      {
-         png_debug2(2, "Writing trailer text chunk %d, type %d", i,
-             info_ptr->text[i].compression);
-         /* An internationalized chunk? */
-         if (info_ptr->text[i].compression > 0)
-         {
-#ifdef PNG_WRITE_iTXt_SUPPORTED
-            /* Write international chunk */
-            png_write_iTXt(png_ptr,
-                info_ptr->text[i].compression,
-                info_ptr->text[i].key,
-                info_ptr->text[i].lang,
-                info_ptr->text[i].lang_key,
-                info_ptr->text[i].text);
-            /* Mark this chunk as written */
-            if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
-               info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
-            else
-               info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
-#else
-            png_warning(png_ptr, "Unable to write international text");
-#endif
-         }
-
-         else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
-         {
-#ifdef PNG_WRITE_zTXt_SUPPORTED
-            /* Write compressed chunk */
-            png_write_zTXt(png_ptr, info_ptr->text[i].key,
-                info_ptr->text[i].text, info_ptr->text[i].compression);
-            /* Mark this chunk as written */
-            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
-#else
-            png_warning(png_ptr, "Unable to write compressed text");
-#endif
-         }
-
-         else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
-         {
-#ifdef PNG_WRITE_tEXt_SUPPORTED
-            /* Write uncompressed chunk */
-            png_write_tEXt(png_ptr, info_ptr->text[i].key,
-                info_ptr->text[i].text, 0);
-            /* Mark this chunk as written */
-            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
-#else
-            png_warning(png_ptr, "Unable to write uncompressed text");
-#endif
-         }
-      }
-#endif
-
-#ifdef PNG_WRITE_eXIf_SUPPORTED
-   if ((info_ptr->valid & PNG_INFO_eXIf) != 0)
-      png_write_eXIf(png_ptr, info_ptr->exif, info_ptr->num_exif);
-#endif
-
-#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
-      write_unknown_chunks(png_ptr, info_ptr, PNG_AFTER_IDAT);
-#endif
-   }
-
-   png_ptr->mode |= PNG_AFTER_IDAT;
-
-   /* Write end of PNG file */
-   png_write_IEND(png_ptr);
-
-   /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03,
-    * and restored again in libpng-1.2.30, may cause some applications that
-    * do not set png_ptr->output_flush_fn to crash.  If your application
-    * experiences a problem, please try building libpng with
-    * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to
-    * png-mng-implement at lists.sf.net .
-    */
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
-#  ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED
-   png_flush(png_ptr);
-#  endif
-#endif
-}
-
-#ifdef PNG_CONVERT_tIME_SUPPORTED
-void PNGAPI
-png_convert_from_struct_tm(png_timep ptime, const struct tm * ttime)
-{
-   png_debug(1, "in png_convert_from_struct_tm");
-
-   ptime->year = (png_uint_16)(1900 + ttime->tm_year);
-   ptime->month = (png_byte)(ttime->tm_mon + 1);
-   ptime->day = (png_byte)ttime->tm_mday;
-   ptime->hour = (png_byte)ttime->tm_hour;
-   ptime->minute = (png_byte)ttime->tm_min;
-   ptime->second = (png_byte)ttime->tm_sec;
-}
-
-void PNGAPI
-png_convert_from_time_t(png_timep ptime, time_t ttime)
-{
-   struct tm *tbuf;
-
-   png_debug(1, "in png_convert_from_time_t");
-
-   tbuf = gmtime(&ttime);
-   png_convert_from_struct_tm(ptime, tbuf);
-}
-#endif
-
-/* Initialize png_ptr structure, and allocate any memory needed */
-PNG_FUNCTION(png_structp,PNGAPI
-png_create_write_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
-    png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED)
-{
-#ifndef PNG_USER_MEM_SUPPORTED
-   png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
-       error_fn, warn_fn, NULL, NULL, NULL);
-#else
-   return png_create_write_struct_2(user_png_ver, error_ptr, error_fn,
-       warn_fn, NULL, NULL, NULL);
-}
-
-/* Alternate initialize png_ptr structure, and allocate any memory needed */
-PNG_FUNCTION(png_structp,PNGAPI
-png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
-    png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
-    png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
-{
-   png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
-       error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);
-#endif /* USER_MEM */
-   if (png_ptr != NULL)
-   {
-      /* Set the zlib control values to defaults; they can be overridden by the
-       * application after the struct has been created.
-       */
-      png_ptr->zbuffer_size = PNG_ZBUF_SIZE;
-
-      /* The 'zlib_strategy' setting is irrelevant because png_default_claim in
-       * pngwutil.c defaults it according to whether or not filters will be
-       * used, and ignores this setting.
-       */
-      png_ptr->zlib_strategy = PNG_Z_DEFAULT_STRATEGY;
-      png_ptr->zlib_level = PNG_Z_DEFAULT_COMPRESSION;
-      png_ptr->zlib_mem_level = 8;
-      png_ptr->zlib_window_bits = 15;
-      png_ptr->zlib_method = 8;
-
-#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
-      png_ptr->zlib_text_strategy = PNG_TEXT_Z_DEFAULT_STRATEGY;
-      png_ptr->zlib_text_level = PNG_TEXT_Z_DEFAULT_COMPRESSION;
-      png_ptr->zlib_text_mem_level = 8;
-      png_ptr->zlib_text_window_bits = 15;
-      png_ptr->zlib_text_method = 8;
-#endif /* WRITE_COMPRESSED_TEXT */
-
-      /* This is a highly dubious configuration option; by default it is off,
-       * but it may be appropriate for private builds that are testing
-       * extensions not conformant to the current specification, or of
-       * applications that must not fail to write at all costs!
-       */
-#ifdef PNG_BENIGN_WRITE_ERRORS_SUPPORTED
-      /* In stable builds only warn if an application error can be completely
-       * handled.
-       */
-      png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN;
-#endif
-
-      /* App warnings are warnings in release (or release candidate) builds but
-       * are errors during development.
-       */
-#if PNG_RELEASE_BUILD
-      png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN;
-#endif
-
-      /* TODO: delay this, it can be done in png_init_io() (if the app doesn't
-       * do it itself) avoiding setting the default function if it is not
-       * required.
-       */
-      png_set_write_fn(png_ptr, NULL, NULL, NULL);
-   }
-
-   return png_ptr;
-}
-
-
-/* Write a few rows of image data.  If the image is interlaced,
- * either you will have to write the 7 sub images, or, if you
- * have called png_set_interlace_handling(), you will have to
- * "write" the image seven times.
- */
-void PNGAPI
-png_write_rows(png_structrp png_ptr, png_bytepp row,
-    png_uint_32 num_rows)
-{
-   png_uint_32 i; /* row counter */
-   png_bytepp rp; /* row pointer */
-
-   png_debug(1, "in png_write_rows");
-
-   if (png_ptr == NULL)
-      return;
-
-   /* Loop through the rows */
-   for (i = 0, rp = row; i < num_rows; i++, rp++)
-   {
-      png_write_row(png_ptr, *rp);
-   }
-}
-
-/* Write the image.  You only need to call this function once, even
- * if you are writing an interlaced image.
- */
-void PNGAPI
-png_write_image(png_structrp png_ptr, png_bytepp image)
-{
-   png_uint_32 i; /* row index */
-   int pass, num_pass; /* pass variables */
-   png_bytepp rp; /* points to current row */
-
-   if (png_ptr == NULL)
-      return;
-
-   png_debug(1, "in png_write_image");
-
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
-   /* Initialize interlace handling.  If image is not interlaced,
-    * this will set pass to 1
-    */
-   num_pass = png_set_interlace_handling(png_ptr);
-#else
-   num_pass = 1;
-#endif
-   /* Loop through passes */
-   for (pass = 0; pass < num_pass; pass++)
-   {
-      /* Loop through image */
-      for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
-      {
-         png_write_row(png_ptr, *rp);
-      }
-   }
-}
-
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-/* Performs intrapixel differencing  */
-static void
-png_do_write_intrapixel(png_row_infop row_info, png_bytep row)
-{
-   png_debug(1, "in png_do_write_intrapixel");
-
-   if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0)
-   {
-      int bytes_per_pixel;
-      png_uint_32 row_width = row_info->width;
-      if (row_info->bit_depth == 8)
-      {
-         png_bytep rp;
-         png_uint_32 i;
-
-         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
-            bytes_per_pixel = 3;
-
-         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
-            bytes_per_pixel = 4;
-
-         else
-            return;
-
-         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
-         {
-            *(rp)     = (png_byte)(*rp       - *(rp + 1));
-            *(rp + 2) = (png_byte)(*(rp + 2) - *(rp + 1));
-         }
-      }
-
-#ifdef PNG_WRITE_16BIT_SUPPORTED
-      else if (row_info->bit_depth == 16)
-      {
-         png_bytep rp;
-         png_uint_32 i;
-
-         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
-            bytes_per_pixel = 6;
-
-         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
-            bytes_per_pixel = 8;
-
-         else
-            return;
-
-         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
-         {
-            png_uint_32 s0   = (png_uint_32)(*(rp    ) << 8) | *(rp + 1);
-            png_uint_32 s1   = (png_uint_32)(*(rp + 2) << 8) | *(rp + 3);
-            png_uint_32 s2   = (png_uint_32)(*(rp + 4) << 8) | *(rp + 5);
-            png_uint_32 red  = (png_uint_32)((s0 - s1) & 0xffffL);
-            png_uint_32 blue = (png_uint_32)((s2 - s1) & 0xffffL);
-            *(rp    ) = (png_byte)(red >> 8);
-            *(rp + 1) = (png_byte)red;
-            *(rp + 4) = (png_byte)(blue >> 8);
-            *(rp + 5) = (png_byte)blue;
-         }
-      }
-#endif /* WRITE_16BIT */
-   }
-}
-#endif /* MNG_FEATURES */
-
-/* Called by user to write a row of image data */
-void PNGAPI
-png_write_row(png_structrp png_ptr, png_const_bytep row)
-{
-   /* 1.5.6: moved from png_struct to be a local structure: */
-   png_row_info row_info;
-
-   if (png_ptr == NULL)
-      return;
-
-   png_debug2(1, "in png_write_row (row %u, pass %d)",
-       png_ptr->row_number, png_ptr->pass);
-
-   /* Initialize transformations and other stuff if first time */
-   if (png_ptr->row_number == 0 && png_ptr->pass == 0)
-   {
-      /* Make sure we wrote the header info */
-      if ((png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE) == 0)
-         png_error(png_ptr,
-             "png_write_info was never called before png_write_row");
-
-      /* Check for transforms that have been set but were defined out */
-#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED)
-      if ((png_ptr->transformations & PNG_INVERT_MONO) != 0)
-         png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined");
-#endif
-
-#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED)
-      if ((png_ptr->transformations & PNG_FILLER) != 0)
-         png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined");
-#endif
-#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \
-    defined(PNG_READ_PACKSWAP_SUPPORTED)
-      if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
-         png_warning(png_ptr,
-             "PNG_WRITE_PACKSWAP_SUPPORTED is not defined");
-#endif
-
-#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)
-      if ((png_ptr->transformations & PNG_PACK) != 0)
-         png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined");
-#endif
-
-#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)
-      if ((png_ptr->transformations & PNG_SHIFT) != 0)
-         png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined");
-#endif
-
-#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)
-      if ((png_ptr->transformations & PNG_BGR) != 0)
-         png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined");
-#endif
-
-#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)
-      if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0)
-         png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined");
-#endif
-
-      png_write_start_row(png_ptr);
-   }
-
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
-   /* If interlaced and not interested in row, return */
-   if (png_ptr->interlaced != 0 &&
-       (png_ptr->transformations & PNG_INTERLACE) != 0)
-   {
-      switch (png_ptr->pass)
-      {
-         case 0:
-            if ((png_ptr->row_number & 0x07) != 0)
-            {
-               png_write_finish_row(png_ptr);
-               return;
-            }
-            break;
-
-         case 1:
-            if ((png_ptr->row_number & 0x07) != 0 || png_ptr->width < 5)
-            {
-               png_write_finish_row(png_ptr);
-               return;
-            }
-            break;
-
-         case 2:
-            if ((png_ptr->row_number & 0x07) != 4)
-            {
-               png_write_finish_row(png_ptr);
-               return;
-            }
-            break;
-
-         case 3:
-            if ((png_ptr->row_number & 0x03) != 0 || png_ptr->width < 3)
-            {
-               png_write_finish_row(png_ptr);
-               return;
-            }
-            break;
-
-         case 4:
-            if ((png_ptr->row_number & 0x03) != 2)
-            {
-               png_write_finish_row(png_ptr);
-               return;
-            }
-            break;
-
-         case 5:
-            if ((png_ptr->row_number & 0x01) != 0 || png_ptr->width < 2)
-            {
-               png_write_finish_row(png_ptr);
-               return;
-            }
-            break;
-
-         case 6:
-            if ((png_ptr->row_number & 0x01) == 0)
-            {
-               png_write_finish_row(png_ptr);
-               return;
-            }
-            break;
-
-         default: /* error: ignore it */
-            break;
-      }
-   }
-#endif
-
-   /* Set up row info for transformations */
-   row_info.color_type = png_ptr->color_type;
-   row_info.width = png_ptr->usr_width;
-   row_info.channels = png_ptr->usr_channels;
-   row_info.bit_depth = png_ptr->usr_bit_depth;
-   row_info.pixel_depth = (png_byte)(row_info.bit_depth * row_info.channels);
-   row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);
-
-   png_debug1(3, "row_info->color_type = %d", row_info.color_type);
-   png_debug1(3, "row_info->width = %u", row_info.width);
-   png_debug1(3, "row_info->channels = %d", row_info.channels);
-   png_debug1(3, "row_info->bit_depth = %d", row_info.bit_depth);
-   png_debug1(3, "row_info->pixel_depth = %d", row_info.pixel_depth);
-   png_debug1(3, "row_info->rowbytes = %lu", (unsigned long)row_info.rowbytes);
-
-   /* Copy user's row into buffer, leaving room for filter byte. */
-   memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes);
-
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
-   /* Handle interlacing */
-   if (png_ptr->interlaced && png_ptr->pass < 6 &&
-       (png_ptr->transformations & PNG_INTERLACE) != 0)
-   {
-      png_do_write_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass);
-      /* This should always get caught above, but still ... */
-      if (row_info.width == 0)
-      {
-         png_write_finish_row(png_ptr);
-         return;
-      }
-   }
-#endif
-
-#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
-   /* Handle other transformations */
-   if (png_ptr->transformations != 0)
-      png_do_write_transformations(png_ptr, &row_info);
-#endif
-
-   /* At this point the row_info pixel depth must match the 'transformed' depth,
-    * which is also the output depth.
-    */
-   if (row_info.pixel_depth != png_ptr->pixel_depth ||
-       row_info.pixel_depth != png_ptr->transformed_pixel_depth)
-      png_error(png_ptr, "internal write transform logic error");
-
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-   /* Write filter_method 64 (intrapixel differencing) only if
-    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
-    * 2. Libpng did not write a PNG signature (this filter_method is only
-    *    used in PNG datastreams that are embedded in MNG datastreams) and
-    * 3. The application called png_permit_mng_features with a mask that
-    *    included PNG_FLAG_MNG_FILTER_64 and
-    * 4. The filter_method is 64 and
-    * 5. The color_type is RGB or RGBA
-    */
-   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
-       (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
-   {
-      /* Intrapixel differencing */
-      png_do_write_intrapixel(&row_info, png_ptr->row_buf + 1);
-   }
-#endif
-
-/* Added at libpng-1.5.10 */
-#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
-   /* Check for out-of-range palette index */
-   if (row_info.color_type == PNG_COLOR_TYPE_PALETTE &&
-       png_ptr->num_palette_max >= 0)
-      png_do_check_palette_indexes(png_ptr, &row_info);
-#endif
-
-   /* Find a filter if necessary, filter the row and write it out. */
-   png_write_find_filter(png_ptr, &row_info);
-
-   if (png_ptr->write_row_fn != NULL)
-      (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
-}
-
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
-/* Set the automatic flush interval or 0 to turn flushing off */
-void PNGAPI
-png_set_flush(png_structrp png_ptr, int nrows)
-{
-   png_debug(1, "in png_set_flush");
-
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->flush_dist = (nrows < 0 ? 0 : (png_uint_32)nrows);
-}
-
-/* Flush the current output buffers now */
-void PNGAPI
-png_write_flush(png_structrp png_ptr)
-{
-   png_debug(1, "in png_write_flush");
-
-   if (png_ptr == NULL)
-      return;
-
-   /* We have already written out all of the data */
-   if (png_ptr->row_number >= png_ptr->num_rows)
-      return;
-
-   png_compress_IDAT(png_ptr, NULL, 0, Z_SYNC_FLUSH);
-   png_ptr->flush_rows = 0;
-   png_flush(png_ptr);
-}
-#endif /* WRITE_FLUSH */
-
-/* Free any memory used in png_ptr struct without freeing the struct itself. */
-static void
-png_write_destroy(png_structrp png_ptr)
-{
-   png_debug(1, "in png_write_destroy");
-
-   /* Free any memory zlib uses */
-   if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0)
-      deflateEnd(&png_ptr->zstream);
-
-   /* Free our memory.  png_free checks NULL for us. */
-   png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);
-   png_free(png_ptr, png_ptr->row_buf);
-   png_ptr->row_buf = NULL;
-#ifdef PNG_WRITE_FILTER_SUPPORTED
-   png_free(png_ptr, png_ptr->prev_row);
-   png_free(png_ptr, png_ptr->try_row);
-   png_free(png_ptr, png_ptr->tst_row);
-   png_ptr->prev_row = NULL;
-   png_ptr->try_row = NULL;
-   png_ptr->tst_row = NULL;
-#endif
-
-#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
-   png_free(png_ptr, png_ptr->chunk_list);
-   png_ptr->chunk_list = NULL;
-#endif
-
-   /* The error handling and memory handling information is left intact at this
-    * point: the jmp_buf may still have to be freed.  See png_destroy_png_struct
-    * for how this happens.
-    */
-}
-
-/* Free all memory used by the write.
- * In libpng 1.6.0 this API changed quietly to no longer accept a NULL value for
- * *png_ptr_ptr.  Prior to 1.6.0 it would accept such a value and it would free
- * the passed in info_structs but it would quietly fail to free any of the data
- * inside them.  In 1.6.0 it quietly does nothing (it has to be quiet because it
- * has no png_ptr.)
- */
-void PNGAPI
-png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
-{
-   png_debug(1, "in png_destroy_write_struct");
-
-   if (png_ptr_ptr != NULL)
-   {
-      png_structrp png_ptr = *png_ptr_ptr;
-
-      if (png_ptr != NULL) /* added in libpng 1.6.0 */
-      {
-         png_destroy_info_struct(png_ptr, info_ptr_ptr);
-
-         *png_ptr_ptr = NULL;
-         png_write_destroy(png_ptr);
-         png_destroy_png_struct(png_ptr);
-      }
-   }
-}
-
-/* Allow the application to select one or more row filters to use. */
-void PNGAPI
-png_set_filter(png_structrp png_ptr, int method, int filters)
-{
-   png_debug(1, "in png_set_filter");
-
-   if (png_ptr == NULL)
-      return;
-
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
-       (method == PNG_INTRAPIXEL_DIFFERENCING))
-      method = PNG_FILTER_TYPE_BASE;
-
-#endif
-   if (method == PNG_FILTER_TYPE_BASE)
-   {
-      switch (filters & (PNG_ALL_FILTERS | 0x07))
-      {
-#ifdef PNG_WRITE_FILTER_SUPPORTED
-         case 5:
-         case 6:
-         case 7: png_app_error(png_ptr, "Unknown row filter for method 0");
-#endif /* WRITE_FILTER */
-            /* FALLTHROUGH */
-         case PNG_FILTER_VALUE_NONE:
-            png_ptr->do_filter = PNG_FILTER_NONE; break;
-
-#ifdef PNG_WRITE_FILTER_SUPPORTED
-         case PNG_FILTER_VALUE_SUB:
-            png_ptr->do_filter = PNG_FILTER_SUB; break;
-
-         case PNG_FILTER_VALUE_UP:
-            png_ptr->do_filter = PNG_FILTER_UP; break;
-
-         case PNG_FILTER_VALUE_AVG:
-            png_ptr->do_filter = PNG_FILTER_AVG; break;
-
-         case PNG_FILTER_VALUE_PAETH:
-            png_ptr->do_filter = PNG_FILTER_PAETH; break;
-
-         default:
-            png_ptr->do_filter = (png_byte)filters; break;
-#else
-         default:
-            png_app_error(png_ptr, "Unknown row filter for method 0");
-#endif /* WRITE_FILTER */
-      }
-
-#ifdef PNG_WRITE_FILTER_SUPPORTED
-      /* If we have allocated the row_buf, this means we have already started
-       * with the image and we should have allocated all of the filter buffers
-       * that have been selected.  If prev_row isn't already allocated, then
-       * it is too late to start using the filters that need it, since we
-       * will be missing the data in the previous row.  If an application
-       * wants to start and stop using particular filters during compression,
-       * it should start out with all of the filters, and then remove them
-       * or add them back after the start of compression.
-       *
-       * NOTE: this is a nasty constraint on the code, because it means that the
-       * prev_row buffer must be maintained even if there are currently no
-       * 'prev_row' requiring filters active.
-       */
-      if (png_ptr->row_buf != NULL)
-      {
-         int num_filters;
-         png_alloc_size_t buf_size;
-
-         /* Repeat the checks in png_write_start_row; 1 pixel high or wide
-          * images cannot benefit from certain filters.  If this isn't done here
-          * the check below will fire on 1 pixel high images.
-          */
-         if (png_ptr->height == 1)
-            filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
-
-         if (png_ptr->width == 1)
-            filters &= ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH);
-
-         if ((filters & (PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH)) != 0
-            && png_ptr->prev_row == NULL)
-         {
-            /* This is the error case, however it is benign - the previous row
-             * is not available so the filter can't be used.  Just warn here.
-             */
-            png_app_warning(png_ptr,
-                "png_set_filter: UP/AVG/PAETH cannot be added after start");
-            filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
-         }
-
-         num_filters = 0;
-
-         if (filters & PNG_FILTER_SUB)
-            num_filters++;
-
-         if (filters & PNG_FILTER_UP)
-            num_filters++;
-
-         if (filters & PNG_FILTER_AVG)
-            num_filters++;
-
-         if (filters & PNG_FILTER_PAETH)
-            num_filters++;
-
-         /* Allocate needed row buffers if they have not already been
-          * allocated.
-          */
-         buf_size = PNG_ROWBYTES(png_ptr->usr_channels * png_ptr->usr_bit_depth,
-             png_ptr->width) + 1;
-
-         if (png_ptr->try_row == NULL)
-            png_ptr->try_row = png_voidcast(png_bytep,
-                png_malloc(png_ptr, buf_size));
-
-         if (num_filters > 1)
-         {
-            if (png_ptr->tst_row == NULL)
-               png_ptr->tst_row = png_voidcast(png_bytep,
-                   png_malloc(png_ptr, buf_size));
-         }
-      }
-      png_ptr->do_filter = (png_byte)filters;
-#endif
-   }
-   else
-      png_error(png_ptr, "Unknown custom filter method");
-}
-
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */
-/* Provide floating and fixed point APIs */
-#ifdef PNG_FLOATING_POINT_SUPPORTED
-void PNGAPI
-png_set_filter_heuristics(png_structrp png_ptr, int heuristic_method,
-    int num_weights, png_const_doublep filter_weights,
-    png_const_doublep filter_costs)
-{
-   PNG_UNUSED(png_ptr)
-   PNG_UNUSED(heuristic_method)
-   PNG_UNUSED(num_weights)
-   PNG_UNUSED(filter_weights)
-   PNG_UNUSED(filter_costs)
-}
-#endif /* FLOATING_POINT */
-
-#ifdef PNG_FIXED_POINT_SUPPORTED
-void PNGAPI
-png_set_filter_heuristics_fixed(png_structrp png_ptr, int heuristic_method,
-    int num_weights, png_const_fixed_point_p filter_weights,
-    png_const_fixed_point_p filter_costs)
-{
-   PNG_UNUSED(png_ptr)
-   PNG_UNUSED(heuristic_method)
-   PNG_UNUSED(num_weights)
-   PNG_UNUSED(filter_weights)
-   PNG_UNUSED(filter_costs)
-}
-#endif /* FIXED_POINT */
-#endif /* WRITE_WEIGHTED_FILTER */
-
-#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
-void PNGAPI
-png_set_compression_level(png_structrp png_ptr, int level)
-{
-   png_debug(1, "in png_set_compression_level");
-
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->zlib_level = level;
-}
-
-void PNGAPI
-png_set_compression_mem_level(png_structrp png_ptr, int mem_level)
-{
-   png_debug(1, "in png_set_compression_mem_level");
-
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->zlib_mem_level = mem_level;
-}
-
-void PNGAPI
-png_set_compression_strategy(png_structrp png_ptr, int strategy)
-{
-   png_debug(1, "in png_set_compression_strategy");
-
-   if (png_ptr == NULL)
-      return;
-
-   /* The flag setting here prevents the libpng dynamic selection of strategy.
-    */
-   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
-   png_ptr->zlib_strategy = strategy;
-}
-
-/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
- * smaller value of window_bits if it can do so safely.
- */
-void PNGAPI
-png_set_compression_window_bits(png_structrp png_ptr, int window_bits)
-{
-   if (png_ptr == NULL)
-      return;
-
-   /* Prior to 1.6.0 this would warn but then set the window_bits value. This
-    * meant that negative window bits values could be selected that would cause
-    * libpng to write a non-standard PNG file with raw deflate or gzip
-    * compressed IDAT or ancillary chunks.  Such files can be read and there is
-    * no warning on read, so this seems like a very bad idea.
-    */
-   if (window_bits > 15)
-   {
-      png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
-      window_bits = 15;
-   }
-
-   else if (window_bits < 8)
-   {
-      png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
-      window_bits = 8;
-   }
-
-   png_ptr->zlib_window_bits = window_bits;
-}
-
-void PNGAPI
-png_set_compression_method(png_structrp png_ptr, int method)
-{
-   png_debug(1, "in png_set_compression_method");
-
-   if (png_ptr == NULL)
-      return;
-
-   /* This would produce an invalid PNG file if it worked, but it doesn't and
-    * deflate will fault it, so it is harmless to just warn here.
-    */
-   if (method != 8)
-      png_warning(png_ptr, "Only compression method 8 is supported by PNG");
-
-   png_ptr->zlib_method = method;
-}
-#endif /* WRITE_CUSTOMIZE_COMPRESSION */
-
-/* The following were added to libpng-1.5.4 */
-#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
-void PNGAPI
-png_set_text_compression_level(png_structrp png_ptr, int level)
-{
-   png_debug(1, "in png_set_text_compression_level");
-
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->zlib_text_level = level;
-}
-
-void PNGAPI
-png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level)
-{
-   png_debug(1, "in png_set_text_compression_mem_level");
-
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->zlib_text_mem_level = mem_level;
-}
-
-void PNGAPI
-png_set_text_compression_strategy(png_structrp png_ptr, int strategy)
-{
-   png_debug(1, "in png_set_text_compression_strategy");
-
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->zlib_text_strategy = strategy;
-}
-
-/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
- * smaller value of window_bits if it can do so safely.
- */
-void PNGAPI
-png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits)
-{
-   if (png_ptr == NULL)
-      return;
-
-   if (window_bits > 15)
-   {
-      png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
-      window_bits = 15;
-   }
-
-   else if (window_bits < 8)
-   {
-      png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
-      window_bits = 8;
-   }
-
-   png_ptr->zlib_text_window_bits = window_bits;
-}
-
-void PNGAPI
-png_set_text_compression_method(png_structrp png_ptr, int method)
-{
-   png_debug(1, "in png_set_text_compression_method");
-
-   if (png_ptr == NULL)
-      return;
-
-   if (method != 8)
-      png_warning(png_ptr, "Only compression method 8 is supported by PNG");
-
-   png_ptr->zlib_text_method = method;
-}
-#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */
-/* end of API added to libpng-1.5.4 */
-
-void PNGAPI
-png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn)
-{
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->write_row_fn = write_row_fn;
-}
-
-#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
-void PNGAPI
-png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
-    write_user_transform_fn)
-{
-   png_debug(1, "in png_set_write_user_transform_fn");
-
-   if (png_ptr == NULL)
-      return;
-
-   png_ptr->transformations |= PNG_USER_TRANSFORM;
-   png_ptr->write_user_transform_fn = write_user_transform_fn;
-}
-#endif
-
-
-#ifdef PNG_INFO_IMAGE_SUPPORTED
-void PNGAPI
-png_write_png(png_structrp png_ptr, png_inforp info_ptr,
-    int transforms, voidp params)
-{
-   if (png_ptr == NULL || info_ptr == NULL)
-      return;
-
-   if ((info_ptr->valid & PNG_INFO_IDAT) == 0)
-   {
-      png_app_error(png_ptr, "no rows for png_write_image to write");
-      return;
-   }
-
-   /* Write the file header information. */
-   png_write_info(png_ptr, info_ptr);
-
-   /* ------ these transformations don't touch the info structure ------- */
-
-   /* Invert monochrome pixels */
-   if ((transforms & PNG_TRANSFORM_INVERT_MONO) != 0)
-#ifdef PNG_WRITE_INVERT_SUPPORTED
-      png_set_invert_mono(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_INVERT_MONO not supported");
-#endif
-
-   /* Shift the pixels up to a legal bit depth and fill in
-    * as appropriate to correctly scale the image.
-    */
-   if ((transforms & PNG_TRANSFORM_SHIFT) != 0)
-#ifdef PNG_WRITE_SHIFT_SUPPORTED
-      if ((info_ptr->valid & PNG_INFO_sBIT) != 0)
-         png_set_shift(png_ptr, &info_ptr->sig_bit);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_SHIFT not supported");
-#endif
-
-   /* Pack pixels into bytes */
-   if ((transforms & PNG_TRANSFORM_PACKING) != 0)
-#ifdef PNG_WRITE_PACK_SUPPORTED
-      png_set_packing(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_PACKING not supported");
-#endif
-
-   /* Swap location of alpha bytes from ARGB to RGBA */
-   if ((transforms & PNG_TRANSFORM_SWAP_ALPHA) != 0)
-#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
-      png_set_swap_alpha(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_SWAP_ALPHA not supported");
-#endif
-
-   /* Remove a filler (X) from XRGB/RGBX/AG/GA into to convert it into
-    * RGB, note that the code expects the input color type to be G or RGB; no
-    * alpha channel.
-    */
-   if ((transforms & (PNG_TRANSFORM_STRIP_FILLER_AFTER|
-       PNG_TRANSFORM_STRIP_FILLER_BEFORE)) != 0)
-   {
-#ifdef PNG_WRITE_FILLER_SUPPORTED
-      if ((transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER) != 0)
-      {
-         if ((transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) != 0)
-            png_app_error(png_ptr,
-                "PNG_TRANSFORM_STRIP_FILLER: BEFORE+AFTER not supported");
-
-         /* Continue if ignored - this is the pre-1.6.10 behavior */
-         png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
-      }
-
-      else if ((transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) != 0)
-         png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_STRIP_FILLER not supported");
-#endif
-   }
-
-   /* Flip BGR pixels to RGB */
-   if ((transforms & PNG_TRANSFORM_BGR) != 0)
-#ifdef PNG_WRITE_BGR_SUPPORTED
-      png_set_bgr(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_BGR not supported");
-#endif
-
-   /* Swap bytes of 16-bit files to most significant byte first */
-   if ((transforms & PNG_TRANSFORM_SWAP_ENDIAN) != 0)
-#ifdef PNG_WRITE_SWAP_SUPPORTED
-      png_set_swap(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_SWAP_ENDIAN not supported");
-#endif
-
-   /* Swap bits of 1-bit, 2-bit, 4-bit packed pixel formats */
-   if ((transforms & PNG_TRANSFORM_PACKSWAP) != 0)
-#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
-      png_set_packswap(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_PACKSWAP not supported");
-#endif
-
-   /* Invert the alpha channel from opacity to transparency */
-   if ((transforms & PNG_TRANSFORM_INVERT_ALPHA) != 0)
-#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
-      png_set_invert_alpha(png_ptr);
-#else
-      png_app_error(png_ptr, "PNG_TRANSFORM_INVERT_ALPHA not supported");
-#endif
-
-   /* ----------------------- end of transformations ------------------- */
-
-   /* Write the bits */
-   png_write_image(png_ptr, info_ptr->row_pointers);
-
-   /* It is REQUIRED to call this to finish writing the rest of the file */
-   png_write_end(png_ptr, info_ptr);
-
-   PNG_UNUSED(params)
-}
-#endif
-
-
-#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED
-/* Initialize the write structure - general purpose utility. */
-static int
-png_image_write_init(png_imagep image)
-{
-   png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, image,
-       png_safe_error, png_safe_warning);
-
-   if (png_ptr != NULL)
-   {
-      png_infop info_ptr = png_create_info_struct(png_ptr);
-
-      if (info_ptr != NULL)
-      {
-         png_controlp control = png_voidcast(png_controlp,
-             png_malloc_warn(png_ptr, (sizeof *control)));
-
-         if (control != NULL)
-         {
-            memset(control, 0, (sizeof *control));
-
-            control->png_ptr = png_ptr;
-            control->info_ptr = info_ptr;
-            control->for_write = 1;
-
-            image->opaque = control;
-            return 1;
-         }
-
-         /* Error clean up */
-         png_destroy_info_struct(png_ptr, &info_ptr);
-      }
-
-      png_destroy_write_struct(&png_ptr, NULL);
-   }
-
-   return png_image_error(image, "png_image_write_: out of memory");
-}
-
-/* Arguments to png_image_write_main: */
-typedef struct
-{
-   /* Arguments: */
-   png_imagep      image;
-   png_const_voidp buffer;
-   png_int_32      row_stride;
-   png_const_voidp colormap;
-   int             convert_to_8bit;
-   /* Local variables: */
-   png_const_voidp first_row;
-   ptrdiff_t       row_bytes;
-   png_voidp       local_row;
-   /* Byte count for memory writing */
-   png_bytep        memory;
-   png_alloc_size_t memory_bytes; /* not used for STDIO */
-   png_alloc_size_t output_bytes; /* running total */
-} png_image_write_control;
-
-/* Write png_uint_16 input to a 16-bit PNG; the png_ptr has already been set to
- * do any necessary byte swapping.  The component order is defined by the
- * png_image format value.
- */
-static int
-png_write_image_16bit(png_voidp argument)
-{
-   png_image_write_control *display = png_voidcast(png_image_write_control*,
-       argument);
-   png_imagep image = display->image;
-   png_structrp png_ptr = image->opaque->png_ptr;
-
-   png_const_uint_16p input_row = png_voidcast(png_const_uint_16p,
-       display->first_row);
-   png_uint_16p output_row = png_voidcast(png_uint_16p, display->local_row);
-   png_uint_16p row_end;
-   unsigned int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ?
-       3 : 1;
-   int aindex = 0;
-   png_uint_32 y = image->height;
-
-   if ((image->format & PNG_FORMAT_FLAG_ALPHA) != 0)
-   {
-#   ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
-      if ((image->format & PNG_FORMAT_FLAG_AFIRST) != 0)
-      {
-         aindex = -1;
-         ++input_row; /* To point to the first component */
-         ++output_row;
-      }
-         else
-            aindex = (int)channels;
-#     else
-         aindex = (int)channels;
-#     endif
-   }
-
-   else
-      png_error(png_ptr, "png_write_image: internal call error");
-
-   /* Work out the output row end and count over this, note that the increment
-    * above to 'row' means that row_end can actually be beyond the end of the
-    * row; this is correct.
-    */
-   row_end = output_row + image->width * (channels+1);
-
-   for (; y > 0; --y)
-   {
-      png_const_uint_16p in_ptr = input_row;
-      png_uint_16p out_ptr = output_row;
-
-      while (out_ptr < row_end)
-      {
-         png_uint_16 alpha = in_ptr[aindex];
-         png_uint_32 reciprocal = 0;
-         int c;
-
-         out_ptr[aindex] = alpha;
-
-         /* Calculate a reciprocal.  The correct calculation is simply
-          * component/alpha*65535 << 15. (I.e. 15 bits of precision); this
-          * allows correct rounding by adding .5 before the shift.  'reciprocal'
-          * is only initialized when required.
-          */
-         if (alpha > 0 && alpha < 65535)
-            reciprocal = ((0xffff<<15)+(alpha>>1))/alpha;
-
-         c = (int)channels;
-         do /* always at least one channel */
-         {
-            png_uint_16 component = *in_ptr++;
-
-            /* The following gives 65535 for an alpha of 0, which is fine,
-             * otherwise if 0/0 is represented as some other value there is more
-             * likely to be a discontinuity which will probably damage
-             * compression when moving from a fully transparent area to a
-             * nearly transparent one.  (The assumption here is that opaque
-             * areas tend not to be 0 intensity.)
-             */
-            if (component >= alpha)
-               component = 65535;
-
-            /* component<alpha, so component/alpha is less than one and
-             * component*reciprocal is less than 2^31.
-             */
-            else if (component > 0 && alpha < 65535)
-            {
-               png_uint_32 calc = component * reciprocal;
-               calc += 16384; /* round to nearest */
-               component = (png_uint_16)(calc >> 15);
-            }
-
-            *out_ptr++ = component;
-         }
-         while (--c > 0);
-
-         /* Skip to next component (skip the intervening alpha channel) */
-         ++in_ptr;
-         ++out_ptr;
-      }
-
-      png_write_row(png_ptr, png_voidcast(png_const_bytep, display->local_row));
-      input_row += (png_uint_16)display->row_bytes/(sizeof (png_uint_16));
-   }
-
-   return 1;
-}
-
-/* Given 16-bit input (1 to 4 channels) write 8-bit output.  If an alpha channel
- * is present it must be removed from the components, the components are then
- * written in sRGB encoding.  No components are added or removed.
- *
- * Calculate an alpha reciprocal to reverse pre-multiplication.  As above the
- * calculation can be done to 15 bits of accuracy; however, the output needs to
- * be scaled in the range 0..255*65535, so include that scaling here.
- */
-#   define UNP_RECIPROCAL(alpha) ((((0xffff*0xff)<<7)+((alpha)>>1))/(alpha))
-
-static png_byte
-png_unpremultiply(png_uint_32 component, png_uint_32 alpha,
-    png_uint_32 reciprocal/*from the above macro*/)
-{
-   /* The following gives 1.0 for an alpha of 0, which is fine, otherwise if 0/0
-    * is represented as some other value there is more likely to be a
-    * discontinuity which will probably damage compression when moving from a
-    * fully transparent area to a nearly transparent one.  (The assumption here
-    * is that opaque areas tend not to be 0 intensity.)
-    *
-    * There is a rounding problem here; if alpha is less than 128 it will end up
-    * as 0 when scaled to 8 bits.  To avoid introducing spurious colors into the
-    * output change for this too.
-    */
-   if (component >= alpha || alpha < 128)
-      return 255;
-
-   /* component<alpha, so component/alpha is less than one and
-    * component*reciprocal is less than 2^31.
-    */
-   else if (component > 0)
-   {
-      /* The test is that alpha/257 (rounded) is less than 255, the first value
-       * that becomes 255 is 65407.
-       * NOTE: this must agree with the PNG_DIV257 macro (which must, therefore,
-       * be exact!)  [Could also test reciprocal != 0]
-       */
-      if (alpha < 65407)
-      {
-         component *= reciprocal;
-         component += 64; /* round to nearest */
-         component >>= 7;
-      }
-
-      else
-         component *= 255;
-
-      /* Convert the component to sRGB. */
-      return (png_byte)PNG_sRGB_FROM_LINEAR(component);
-   }
-
-   else
-      return 0;
-}
-
-static int
-png_write_image_8bit(png_voidp argument)
-{
-   png_image_write_control *display = png_voidcast(png_image_write_control*,
-       argument);
-   png_imagep image = display->image;
-   png_structrp png_ptr = image->opaque->png_ptr;
-
-   png_const_uint_16p input_row = png_voidcast(png_const_uint_16p,
-       display->first_row);
-   png_bytep output_row = png_voidcast(png_bytep, display->local_row);
-   png_uint_32 y = image->height;
-   unsigned int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ?
-       3 : 1;
-
-   if ((image->format & PNG_FORMAT_FLAG_ALPHA) != 0)
-   {
-      png_bytep row_end;
-      int aindex;
-
-#   ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
-      if ((image->format & PNG_FORMAT_FLAG_AFIRST) != 0)
-      {
-         aindex = -1;
-         ++input_row; /* To point to the first component */
-         ++output_row;
-      }
-
-      else
-#   endif
-      aindex = (int)channels;
-
-      /* Use row_end in place of a loop counter: */
-      row_end = output_row + image->width * (channels+1);
-
-      for (; y > 0; --y)
-      {
-         png_const_uint_16p in_ptr = input_row;
-         png_bytep out_ptr = output_row;
-
-         while (out_ptr < row_end)
-         {
-            png_uint_16 alpha = in_ptr[aindex];
-            png_byte alphabyte = (png_byte)PNG_DIV257(alpha);
-            png_uint_32 reciprocal = 0;
-            int c;
-
-            /* Scale and write the alpha channel. */
-            out_ptr[aindex] = alphabyte;
-
-            if (alphabyte > 0 && alphabyte < 255)
-               reciprocal = UNP_RECIPROCAL(alpha);
-
-            c = (int)channels;
-            do /* always at least one channel */
-               *out_ptr++ = png_unpremultiply(*in_ptr++, alpha, reciprocal);
-            while (--c > 0);
-
-            /* Skip to next component (skip the intervening alpha channel) */
-            ++in_ptr;
-            ++out_ptr;
-         } /* while out_ptr < row_end */
-
-         png_write_row(png_ptr, png_voidcast(png_const_bytep,
-             display->local_row));
-         input_row += (png_uint_16)display->row_bytes/(sizeof (png_uint_16));
-      } /* while y */
-   }
-
-   else
-   {
-      /* No alpha channel, so the row_end really is the end of the row and it
-       * is sufficient to loop over the components one by one.
-       */
-      png_bytep row_end = output_row + image->width * channels;
-
-      for (; y > 0; --y)
-      {
-         png_const_uint_16p in_ptr = input_row;
-         png_bytep out_ptr = output_row;
-
-         while (out_ptr < row_end)
-         {
-            png_uint_32 component = *in_ptr++;
-
-            component *= 255;
-            *out_ptr++ = (png_byte)PNG_sRGB_FROM_LINEAR(component);
-         }
-
-         png_write_row(png_ptr, output_row);
-         input_row += (png_uint_16)display->row_bytes/(sizeof (png_uint_16));
-      }
-   }
-
-   return 1;
-}
-
-static void
-png_image_set_PLTE(png_image_write_control *display)
-{
-   png_imagep image = display->image;
-   const void *cmap = display->colormap;
-   int entries = image->colormap_entries > 256 ? 256 :
-       (int)image->colormap_entries;
-
-   /* NOTE: the caller must check for cmap != NULL and entries != 0 */
-   png_uint_32 format = image->format;
-   unsigned int channels = PNG_IMAGE_SAMPLE_CHANNELS(format);
-
-#   if defined(PNG_FORMAT_BGR_SUPPORTED) &&\
-      defined(PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED)
-      int afirst = (format & PNG_FORMAT_FLAG_AFIRST) != 0 &&
-          (format & PNG_FORMAT_FLAG_ALPHA) != 0;
-#   else
-#     define afirst 0
-#   endif
-
-#   ifdef PNG_FORMAT_BGR_SUPPORTED
-      int bgr = (format & PNG_FORMAT_FLAG_BGR) != 0 ? 2 : 0;
-#   else
-#     define bgr 0
-#   endif
-
-   int i, num_trans;
-   png_color palette[256];
-   png_byte tRNS[256];
-
-   memset(tRNS, 255, (sizeof tRNS));
-   memset(palette, 0, (sizeof palette));
-
-   for (i=num_trans=0; i<entries; ++i)
-   {
-      /* This gets automatically converted to sRGB with reversal of the
-       * pre-multiplication if the color-map has an alpha channel.
-       */
-      if ((format & PNG_FORMAT_FLAG_LINEAR) != 0)
-      {
-         png_const_uint_16p entry = png_voidcast(png_const_uint_16p, cmap);
-
-         entry += (unsigned int)i * channels;
-
-         if ((channels & 1) != 0) /* no alpha */
-         {
-            if (channels >= 3) /* RGB */
-            {
-               palette[i].blue = (png_byte)PNG_sRGB_FROM_LINEAR(255 *
-                   entry[(2 ^ bgr)]);
-               palette[i].green = (png_byte)PNG_sRGB_FROM_LINEAR(255 *
-                   entry[1]);
-               palette[i].red = (png_byte)PNG_sRGB_FROM_LINEAR(255 *
-                   entry[bgr]);
-            }
-
-            else /* Gray */
-               palette[i].blue = palette[i].red = palette[i].green =
-                  (png_byte)PNG_sRGB_FROM_LINEAR(255 * *entry);
-         }
-
-         else /* alpha */
-         {
-            png_uint_16 alpha = entry[afirst ? 0 : channels-1];
-            png_byte alphabyte = (png_byte)PNG_DIV257(alpha);
-            png_uint_32 reciprocal = 0;
-
-            /* Calculate a reciprocal, as in the png_write_image_8bit code above
-             * this is designed to produce a value scaled to 255*65535 when
-             * divided by 128 (i.e. asr 7).
-             */
-            if (alphabyte > 0 && alphabyte < 255)
-               reciprocal = (((0xffff*0xff)<<7)+(alpha>>1))/alpha;
-
-            tRNS[i] = alphabyte;
-            if (alphabyte < 255)
-               num_trans = i+1;
-
-            if (channels >= 3) /* RGB */
-            {
-               palette[i].blue = png_unpremultiply(entry[afirst + (2 ^ bgr)],
-                   alpha, reciprocal);
-               palette[i].green = png_unpremultiply(entry[afirst + 1], alpha,
-                   reciprocal);
-               palette[i].red = png_unpremultiply(entry[afirst + bgr], alpha,
-                   reciprocal);
-            }
-
-            else /* gray */
-               palette[i].blue = palette[i].red = palette[i].green =
-                   png_unpremultiply(entry[afirst], alpha, reciprocal);
-         }
-      }
-
-      else /* Color-map has sRGB values */
-      {
-         png_const_bytep entry = png_voidcast(png_const_bytep, cmap);
-
-         entry += (unsigned int)i * channels;
-
-         switch (channels)
-         {
-            case 4:
-               tRNS[i] = entry[afirst ? 0 : 3];
-               if (tRNS[i] < 255)
-                  num_trans = i+1;
-               /* FALLTHROUGH */
-            case 3:
-               palette[i].blue = entry[afirst + (2 ^ bgr)];
-               palette[i].green = entry[afirst + 1];
-               palette[i].red = entry[afirst + bgr];
-               break;
-
-            case 2:
-               tRNS[i] = entry[1 ^ afirst];
-               if (tRNS[i] < 255)
-                  num_trans = i+1;
-               /* FALLTHROUGH */
-            case 1:
-               palette[i].blue = palette[i].red = palette[i].green =
-                  entry[afirst];
-               break;
-
-            default:
-               break;
-         }
-      }
-   }
-
-#   ifdef afirst
-#     undef afirst
-#   endif
-#   ifdef bgr
-#     undef bgr
-#   endif
-
-   png_set_PLTE(image->opaque->png_ptr, image->opaque->info_ptr, palette,
-       entries);
-
-   if (num_trans > 0)
-      png_set_tRNS(image->opaque->png_ptr, image->opaque->info_ptr, tRNS,
-          num_trans, NULL);
-
-   image->colormap_entries = (png_uint_32)entries;
-}
-
-static int
-png_image_write_main(png_voidp argument)
-{
-   png_image_write_control *display = png_voidcast(png_image_write_control*,
-       argument);
-   png_imagep image = display->image;
-   png_structrp png_ptr = image->opaque->png_ptr;
-   png_inforp info_ptr = image->opaque->info_ptr;
-   png_uint_32 format = image->format;
-
-   /* The following four ints are actually booleans */
-   int colormap = (format & PNG_FORMAT_FLAG_COLORMAP);
-   int linear = !colormap && (format & PNG_FORMAT_FLAG_LINEAR); /* input */
-   int alpha = !colormap && (format & PNG_FORMAT_FLAG_ALPHA);
-   int write_16bit = linear && (display->convert_to_8bit == 0);
-
-#   ifdef PNG_BENIGN_ERRORS_SUPPORTED
-      /* Make sure we error out on any bad situation */
-      png_set_benign_errors(png_ptr, 0/*error*/);
-#   endif
-
-   /* Default the 'row_stride' parameter if required, also check the row stride
-    * and total image size to ensure that they are within the system limits.
-    */
-   {
-      unsigned int channels = PNG_IMAGE_PIXEL_CHANNELS(image->format);
-
-      if (image->width <= 0x7fffffffU/channels) /* no overflow */
-      {
-         png_uint_32 check;
-         png_uint_32 png_row_stride = image->width * channels;
-
-         if (display->row_stride == 0)
-            display->row_stride = (png_int_32)/*SAFE*/png_row_stride;
-
-         if (display->row_stride < 0)
-            check = (png_uint_32)(-display->row_stride);
-
-         else
-            check = (png_uint_32)display->row_stride;
-
-         if (check >= png_row_stride)
-         {
-            /* Now check for overflow of the image buffer calculation; this
-             * limits the whole image size to 32 bits for API compatibility with
-             * the current, 32-bit, PNG_IMAGE_BUFFER_SIZE macro.
-             */
-            if (image->height > 0xffffffffU/png_row_stride)
-               png_error(image->opaque->png_ptr, "memory image too large");
-         }
-
-         else
-            png_error(image->opaque->png_ptr, "supplied row stride too small");
-      }
-
-      else
-         png_error(image->opaque->png_ptr, "image row stride too large");
-   }
-
-   /* Set the required transforms then write the rows in the correct order. */
-   if ((format & PNG_FORMAT_FLAG_COLORMAP) != 0)
-   {
-      if (display->colormap != NULL && image->colormap_entries > 0)
-      {
-         png_uint_32 entries = image->colormap_entries;
-
-         png_set_IHDR(png_ptr, info_ptr, image->width, image->height,
-             entries > 16 ? 8 : (entries > 4 ? 4 : (entries > 2 ? 2 : 1)),
-             PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
-             PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
-
-         png_image_set_PLTE(display);
-      }
-
-      else
-         png_error(image->opaque->png_ptr,
-             "no color-map for color-mapped image");
-   }
-
-   else
-      png_set_IHDR(png_ptr, info_ptr, image->width, image->height,
-          write_16bit ? 16 : 8,
-          ((format & PNG_FORMAT_FLAG_COLOR) ? PNG_COLOR_MASK_COLOR : 0) +
-          ((format & PNG_FORMAT_FLAG_ALPHA) ? PNG_COLOR_MASK_ALPHA : 0),
-          PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
-
-   /* Counter-intuitively the data transformations must be called *after*
-    * png_write_info, not before as in the read code, but the 'set' functions
-    * must still be called before.  Just set the color space information, never
-    * write an interlaced image.
-    */
-
-   if (write_16bit != 0)
-   {
-      /* The gamma here is 1.0 (linear) and the cHRM chunk matches sRGB. */
-      png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_LINEAR);
-
-      if ((image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB) == 0)
-         png_set_cHRM_fixed(png_ptr, info_ptr,
-             /* color      x       y */
-             /* white */ 31270, 32900,
-             /* red   */ 64000, 33000,
-             /* green */ 30000, 60000,
-             /* blue  */ 15000,  6000
-         );
-   }
-
-   else if ((image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB) == 0)
-      png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
-
-   /* Else writing an 8-bit file and the *colors* aren't sRGB, but the 8-bit
-    * space must still be gamma encoded.
-    */
-   else
-      png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_sRGB_INVERSE);
-
-   /* Write the file header. */
-   png_write_info(png_ptr, info_ptr);
-
-   /* Now set up the data transformations (*after* the header is written),
-    * remove the handled transformations from the 'format' flags for checking.
-    *
-    * First check for a little endian system if writing 16-bit files.
-    */
-   if (write_16bit != 0)
-   {
-      png_uint_16 le = 0x0001;
-
-      if ((*(png_const_bytep) & le) != 0)
-         png_set_swap(png_ptr);
-   }
-
-#   ifdef PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED
-      if ((format & PNG_FORMAT_FLAG_BGR) != 0)
-      {
-         if (colormap == 0 && (format & PNG_FORMAT_FLAG_COLOR) != 0)
-            png_set_bgr(png_ptr);
-         format &= ~PNG_FORMAT_FLAG_BGR;
-      }
-#   endif
-
-#   ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
-      if ((format & PNG_FORMAT_FLAG_AFIRST) != 0)
-      {
-         if (colormap == 0 && (format & PNG_FORMAT_FLAG_ALPHA) != 0)
-            png_set_swap_alpha(png_ptr);
-         format &= ~PNG_FORMAT_FLAG_AFIRST;
-      }
-#   endif
-
-   /* If there are 16 or fewer color-map entries we wrote a lower bit depth
-    * above, but the application data is still byte packed.
-    */
-   if (colormap != 0 && image->colormap_entries <= 16)
-      png_set_packing(png_ptr);
-
-   /* That should have handled all (both) the transforms. */
-   if ((format & ~(png_uint_32)(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR |
-         PNG_FORMAT_FLAG_ALPHA | PNG_FORMAT_FLAG_COLORMAP)) != 0)
-      png_error(png_ptr, "png_write_image: unsupported transformation");
-
-   {
-      png_const_bytep row = png_voidcast(png_const_bytep, display->buffer);
-      ptrdiff_t row_bytes = display->row_stride;
-
-      if (linear != 0)
-         row_bytes *= (sizeof (png_uint_16));
-
-      if (row_bytes < 0)
-         row += (image->height-1) * (-row_bytes);
-
-      display->first_row = row;
-      display->row_bytes = row_bytes;
-   }
-
-   /* Apply 'fast' options if the flag is set. */
-   if ((image->flags & PNG_IMAGE_FLAG_FAST) != 0)
-   {
-      png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_NO_FILTERS);
-      /* NOTE: determined by experiment using pngstest, this reflects some
-       * balance between the time to write the image once and the time to read
-       * it about 50 times.  The speed-up in pngstest was about 10-20% of the
-       * total (user) time on a heavily loaded system.
-       */
-#   ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
-      png_set_compression_level(png_ptr, 3);
-#   endif
-   }
-
-   /* Check for the cases that currently require a pre-transform on the row
-    * before it is written.  This only applies when the input is 16-bit and
-    * either there is an alpha channel or it is converted to 8-bit.
-    */
-   if ((linear != 0 && alpha != 0 ) ||
-       (colormap == 0 && display->convert_to_8bit != 0))
-   {
-      png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr,
-          png_get_rowbytes(png_ptr, info_ptr)));
-      int result;
-
-      display->local_row = row;
-      if (write_16bit != 0)
-         result = png_safe_execute(image, png_write_image_16bit, display);
-      else
-         result = png_safe_execute(image, png_write_image_8bit, display);
-      display->local_row = NULL;
-
-      png_free(png_ptr, row);
-
-      /* Skip the 'write_end' on error: */
-      if (result == 0)
-         return 0;
-   }
-
-   /* Otherwise this is the case where the input is in a format currently
-    * supported by the rest of the libpng write code; call it directly.
-    */
-   else
-   {
-      png_const_bytep row = png_voidcast(png_const_bytep, display->first_row);
-      ptrdiff_t row_bytes = display->row_bytes;
-      png_uint_32 y = image->height;
-
-      for (; y > 0; --y)
-      {
-         png_write_row(png_ptr, row);
-         row += row_bytes;
-      }
-   }
-
-   png_write_end(png_ptr, info_ptr);
-   return 1;
-}
-
-
-static void (PNGCBAPI
-image_memory_write)(png_structp png_ptr, png_bytep/*const*/ data, size_t size)
-{
-   png_image_write_control *display = png_voidcast(png_image_write_control*,
-       png_ptr->io_ptr/*backdoor: png_get_io_ptr(png_ptr)*/);
-   png_alloc_size_t ob = display->output_bytes;
-
-   /* Check for overflow; this should never happen: */
-   if (size <= ((png_alloc_size_t)-1) - ob)
-   {
-      /* I don't think libpng ever does this, but just in case: */
-      if (size > 0)
-      {
-         if (display->memory_bytes >= ob+size) /* writing */
-            memcpy(display->memory+ob, data, size);
-
-         /* Always update the size: */
-         display->output_bytes = ob+size;
-      }
-   }
-
-   else
-      png_error(png_ptr, "png_image_write_to_memory: PNG too big");
-}
-
-static void (PNGCBAPI
-image_memory_flush)(png_structp png_ptr)
-{
-   PNG_UNUSED(png_ptr)
-}
-
-static int
-png_image_write_memory(png_voidp argument)
-{
-   png_image_write_control *display = png_voidcast(png_image_write_control*,
-       argument);
-
-   /* The rest of the memory-specific init and write_main in an error protected
-    * environment.  This case needs to use callbacks for the write operations
-    * since libpng has no built in support for writing to memory.
-    */
-   png_set_write_fn(display->image->opaque->png_ptr, display/*io_ptr*/,
-       image_memory_write, image_memory_flush);
-
-   return png_image_write_main(display);
-}
-
-int PNGAPI
-png_image_write_to_memory(png_imagep image, void *memory,
-    png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8bit,
-    const void *buffer, png_int_32 row_stride, const void *colormap)
-{
-   /* Write the image to the given buffer, or count the bytes if it is NULL */
-   if (image != NULL && image->version == PNG_IMAGE_VERSION)
-   {
-      if (memory_bytes != NULL && buffer != NULL)
-      {
-         /* This is to give the caller an easier error detection in the NULL
-          * case and guard against uninitialized variable problems:
-          */
-         if (memory == NULL)
-            *memory_bytes = 0;
-
-         if (png_image_write_init(image) != 0)
-         {
-            png_image_write_control display;
-            int result;
-
-            memset(&display, 0, (sizeof display));
-            display.image = image;
-            display.buffer = buffer;
-            display.row_stride = row_stride;
-            display.colormap = colormap;
-            display.convert_to_8bit = convert_to_8bit;
-            display.memory = png_voidcast(png_bytep, memory);
-            display.memory_bytes = *memory_bytes;
-            display.output_bytes = 0;
-
-            result = png_safe_execute(image, png_image_write_memory, &display);
-            png_image_free(image);
-
-            /* write_memory returns true even if we ran out of buffer. */
-            if (result)
-            {
-               /* On out-of-buffer this function returns '0' but still updates
-                * memory_bytes:
-                */
-               if (memory != NULL && display.output_bytes > *memory_bytes)
-                  result = 0;
-
-               *memory_bytes = display.output_bytes;
-            }
-
-            return result;
-         }
-
-         else
-            return 0;
-      }
-
-      else
-         return png_image_error(image,
-             "png_image_write_to_memory: invalid argument");
-   }
-
-   else if (image != NULL)
-      return png_image_error(image,
-          "png_image_write_to_memory: incorrect PNG_IMAGE_VERSION");
-
-   else
-      return 0;
-}
-
-#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
-int PNGAPI
-png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit,
-    const void *buffer, png_int_32 row_stride, const void *colormap)
-{
-   /* Write the image to the given (FILE*). */
-   if (image != NULL && image->version == PNG_IMAGE_VERSION)
-   {
-      if (file != NULL && buffer != NULL)
-      {
-         if (png_image_write_init(image) != 0)
-         {
-            png_image_write_control display;
-            int result;
-
-            /* This is slightly evil, but png_init_io doesn't do anything other
-             * than this and we haven't changed the standard IO functions so
-             * this saves a 'safe' function.
-             */
-            image->opaque->png_ptr->io_ptr = file;
-
-            memset(&display, 0, (sizeof display));
-            display.image = image;
-            display.buffer = buffer;
-            display.row_stride = row_stride;
-            display.colormap = colormap;
-            display.convert_to_8bit = convert_to_8bit;
-
-            result = png_safe_execute(image, png_image_write_main, &display);
-            png_image_free(image);
-            return result;
-         }
-
-         else
-            return 0;
-      }
-
-      else
-         return png_image_error(image,
-             "png_image_write_to_stdio: invalid argument");
-   }
-
-   else if (image != NULL)
-      return png_image_error(image,
-          "png_image_write_to_stdio: incorrect PNG_IMAGE_VERSION");
-
-   else
-      return 0;
-}
-
-int PNGAPI
-png_image_write_to_file(png_imagep image, const char *file_name,
-    int convert_to_8bit, const void *buffer, png_int_32 row_stride,
-    const void *colormap)
-{
-   /* Write the image to the named file. */
-   if (image != NULL && image->version == PNG_IMAGE_VERSION)
-   {
-      if (file_name != NULL && buffer != NULL)
-      {
-         FILE *fp = fopen(file_name, "wb");
-
-         if (fp != NULL)
-         {
-            if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer,
-                row_stride, colormap) != 0)
-            {
-               int error; /* from fflush/fclose */
-
-               /* Make sure the file is flushed correctly. */
-               if (fflush(fp) == 0 && ferror(fp) == 0)
-               {
-                  if (fclose(fp) == 0)
-                     return 1;
-
-                  error = errno; /* from fclose */
-               }
-
-               else
-               {
-                  error = errno; /* from fflush or ferror */
-                  (void)fclose(fp);
-               }
-
-               (void)remove(file_name);
-               /* The image has already been cleaned up; this is just used to
-                * set the error (because the original write succeeded).
-                */
-               return png_image_error(image, strerror(error));
-            }
-
-            else
-            {
-               /* Clean up: just the opened file. */
-               (void)fclose(fp);
-               (void)remove(file_name);
-               return 0;
-            }
-         }
-
-         else
-            return png_image_error(image, strerror(errno));
-      }
-
-      else
-         return png_image_error(image,
-             "png_image_write_to_file: invalid argument");
-   }
-
-   else if (image != NULL)
-      return png_image_error(image,
-          "png_image_write_to_file: incorrect PNG_IMAGE_VERSION");
-
-   else
-      return 0;
-}
-#endif /* SIMPLIFIED_WRITE_STDIO */
-#endif /* SIMPLIFIED_WRITE */
-#endif /* WRITE */
diff --git a/third_party/libpng16/pngwtran.c b/third_party/libpng16/pngwtran.c
deleted file mode 100644
index 49a13c1..0000000
--- a/third_party/libpng16/pngwtran.c
+++ /dev/null
@@ -1,575 +0,0 @@
-
-/* pngwtran.c - transforms the data in a row for PNG writers
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
-#include "pngpriv.h"
-
-#ifdef PNG_WRITE_SUPPORTED
-#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
-
-#ifdef PNG_WRITE_PACK_SUPPORTED
-/* Pack pixels into bytes.  Pass the true bit depth in bit_depth.  The
- * row_info bit depth should be 8 (one pixel per byte).  The channels
- * should be 1 (this only happens on grayscale and paletted images).
- */
-static void
-png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth)
-{
-   png_debug(1, "in png_do_pack");
-
-   if (row_info->bit_depth == 8 &&
-      row_info->channels == 1)
-   {
-      switch ((int)bit_depth)
-      {
-         case 1:
-         {
-            png_bytep sp, dp;
-            int mask, v;
-            png_uint_32 i;
-            png_uint_32 row_width = row_info->width;
-
-            sp = row;
-            dp = row;
-            mask = 0x80;
-            v = 0;
-
-            for (i = 0; i < row_width; i++)
-            {
-               if (*sp != 0)
-                  v |= mask;
-
-               sp++;
-
-               if (mask > 1)
-                  mask >>= 1;
-
-               else
-               {
-                  mask = 0x80;
-                  *dp = (png_byte)v;
-                  dp++;
-                  v = 0;
-               }
-            }
-
-            if (mask != 0x80)
-               *dp = (png_byte)v;
-
-            break;
-         }
-
-         case 2:
-         {
-            png_bytep sp, dp;
-            unsigned int shift;
-            int v;
-            png_uint_32 i;
-            png_uint_32 row_width = row_info->width;
-
-            sp = row;
-            dp = row;
-            shift = 6;
-            v = 0;
-
-            for (i = 0; i < row_width; i++)
-            {
-               png_byte value;
-
-               value = (png_byte)(*sp & 0x03);
-               v |= (value << shift);
-
-               if (shift == 0)
-               {
-                  shift = 6;
-                  *dp = (png_byte)v;
-                  dp++;
-                  v = 0;
-               }
-
-               else
-                  shift -= 2;
-
-               sp++;
-            }
-
-            if (shift != 6)
-               *dp = (png_byte)v;
-
-            break;
-         }
-
-         case 4:
-         {
-            png_bytep sp, dp;
-            unsigned int shift;
-            int v;
-            png_uint_32 i;
-            png_uint_32 row_width = row_info->width;
-
-            sp = row;
-            dp = row;
-            shift = 4;
-            v = 0;
-
-            for (i = 0; i < row_width; i++)
-            {
-               png_byte value;
-
-               value = (png_byte)(*sp & 0x0f);
-               v |= (value << shift);
-
-               if (shift == 0)
-               {
-                  shift = 4;
-                  *dp = (png_byte)v;
-                  dp++;
-                  v = 0;
-               }
-
-               else
-                  shift -= 4;
-
-               sp++;
-            }
-
-            if (shift != 4)
-               *dp = (png_byte)v;
-
-            break;
-         }
-
-         default:
-            break;
-      }
-
-      row_info->bit_depth = (png_byte)bit_depth;
-      row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels);
-      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
-          row_info->width);
-   }
-}
-#endif
-
-#ifdef PNG_WRITE_SHIFT_SUPPORTED
-/* Shift pixel values to take advantage of whole range.  Pass the
- * true number of bits in bit_depth.  The row should be packed
- * according to row_info->bit_depth.  Thus, if you had a row of
- * bit depth 4, but the pixels only had values from 0 to 7, you
- * would pass 3 as bit_depth, and this routine would translate the
- * data to 0 to 15.
- */
-static void
-png_do_shift(png_row_infop row_info, png_bytep row,
-    png_const_color_8p bit_depth)
-{
-   png_debug(1, "in png_do_shift");
-
-   if (row_info->color_type != PNG_COLOR_TYPE_PALETTE)
-   {
-      int shift_start[4], shift_dec[4];
-      unsigned int channels = 0;
-
-      if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0)
-      {
-         shift_start[channels] = row_info->bit_depth - bit_depth->red;
-         shift_dec[channels] = bit_depth->red;
-         channels++;
-
-         shift_start[channels] = row_info->bit_depth - bit_depth->green;
-         shift_dec[channels] = bit_depth->green;
-         channels++;
-
-         shift_start[channels] = row_info->bit_depth - bit_depth->blue;
-         shift_dec[channels] = bit_depth->blue;
-         channels++;
-      }
-
-      else
-      {
-         shift_start[channels] = row_info->bit_depth - bit_depth->gray;
-         shift_dec[channels] = bit_depth->gray;
-         channels++;
-      }
-
-      if ((row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0)
-      {
-         shift_start[channels] = row_info->bit_depth - bit_depth->alpha;
-         shift_dec[channels] = bit_depth->alpha;
-         channels++;
-      }
-
-      /* With low row depths, could only be grayscale, so one channel */
-      if (row_info->bit_depth < 8)
-      {
-         png_bytep bp = row;
-         size_t i;
-         unsigned int mask;
-         size_t row_bytes = row_info->rowbytes;
-
-         if (bit_depth->gray == 1 && row_info->bit_depth == 2)
-            mask = 0x55;
-
-         else if (row_info->bit_depth == 4 && bit_depth->gray == 3)
-            mask = 0x11;
-
-         else
-            mask = 0xff;
-
-         for (i = 0; i < row_bytes; i++, bp++)
-         {
-            int j;
-            unsigned int v, out;
-
-            v = *bp;
-            out = 0;
-
-            for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0])
-            {
-               if (j > 0)
-                  out |= v << j;
-
-               else
-                  out |= (v >> (-j)) & mask;
-            }
-
-            *bp = (png_byte)(out & 0xff);
-         }
-      }
-
-      else if (row_info->bit_depth == 8)
-      {
-         png_bytep bp = row;
-         png_uint_32 i;
-         png_uint_32 istop = channels * row_info->width;
-
-         for (i = 0; i < istop; i++, bp++)
-         {
-            unsigned int c = i%channels;
-            int j;
-            unsigned int v, out;
-
-            v = *bp;
-            out = 0;
-
-            for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
-            {
-               if (j > 0)
-                  out |= v << j;
-
-               else
-                  out |= v >> (-j);
-            }
-
-            *bp = (png_byte)(out & 0xff);
-         }
-      }
-
-      else
-      {
-         png_bytep bp;
-         png_uint_32 i;
-         png_uint_32 istop = channels * row_info->width;
-
-         for (bp = row, i = 0; i < istop; i++)
-         {
-            unsigned int c = i%channels;
-            int j;
-            unsigned int value, v;
-
-            v = png_get_uint_16(bp);
-            value = 0;
-
-            for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
-            {
-               if (j > 0)
-                  value |= v << j;
-
-               else
-                  value |= v >> (-j);
-            }
-            *bp++ = (png_byte)((value >> 8) & 0xff);
-            *bp++ = (png_byte)(value & 0xff);
-         }
-      }
-   }
-}
-#endif
-
-#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
-static void
-png_do_write_swap_alpha(png_row_infop row_info, png_bytep row)
-{
-   png_debug(1, "in png_do_write_swap_alpha");
-
-   {
-      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
-      {
-         if (row_info->bit_depth == 8)
-         {
-            /* This converts from ARGB to RGBA */
-            png_bytep sp, dp;
-            png_uint_32 i;
-            png_uint_32 row_width = row_info->width;
-
-            for (i = 0, sp = dp = row; i < row_width; i++)
-            {
-               png_byte save = *(sp++);
-               *(dp++) = *(sp++);
-               *(dp++) = *(sp++);
-               *(dp++) = *(sp++);
-               *(dp++) = save;
-            }
-         }
-
-#ifdef PNG_WRITE_16BIT_SUPPORTED
-         else
-         {
-            /* This converts from AARRGGBB to RRGGBBAA */
-            png_bytep sp, dp;
-            png_uint_32 i;
-            png_uint_32 row_width = row_info->width;
-
-            for (i = 0, sp = dp = row; i < row_width; i++)
-            {
-               png_byte save[2];
-               save[0] = *(sp++);
-               save[1] = *(sp++);
-               *(dp++) = *(sp++);
-               *(dp++) = *(sp++);
-               *(dp++) = *(sp++);
-               *(dp++) = *(sp++);
-               *(dp++) = *(sp++);
-               *(dp++) = *(sp++);
-               *(dp++) = save[0];
-               *(dp++) = save[1];
-            }
-         }
-#endif /* WRITE_16BIT */
-      }
-
-      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
-      {
-         if (row_info->bit_depth == 8)
-         {
-            /* This converts from AG to GA */
-            png_bytep sp, dp;
-            png_uint_32 i;
-            png_uint_32 row_width = row_info->width;
-
-            for (i = 0, sp = dp = row; i < row_width; i++)
-            {
-               png_byte save = *(sp++);
-               *(dp++) = *(sp++);
-               *(dp++) = save;
-            }
-         }
-
-#ifdef PNG_WRITE_16BIT_SUPPORTED
-         else
-         {
-            /* This converts from AAGG to GGAA */
-            png_bytep sp, dp;
-            png_uint_32 i;
-            png_uint_32 row_width = row_info->width;
-
-            for (i = 0, sp = dp = row; i < row_width; i++)
-            {
-               png_byte save[2];
-               save[0] = *(sp++);
-               save[1] = *(sp++);
-               *(dp++) = *(sp++);
-               *(dp++) = *(sp++);
-               *(dp++) = save[0];
-               *(dp++) = save[1];
-            }
-         }
-#endif /* WRITE_16BIT */
-      }
-   }
-}
-#endif
-
-#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
-static void
-png_do_write_invert_alpha(png_row_infop row_info, png_bytep row)
-{
-   png_debug(1, "in png_do_write_invert_alpha");
-
-   {
-      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
-      {
-         if (row_info->bit_depth == 8)
-         {
-            /* This inverts the alpha channel in RGBA */
-            png_bytep sp, dp;
-            png_uint_32 i;
-            png_uint_32 row_width = row_info->width;
-
-            for (i = 0, sp = dp = row; i < row_width; i++)
-            {
-               /* Does nothing
-               *(dp++) = *(sp++);
-               *(dp++) = *(sp++);
-               *(dp++) = *(sp++);
-               */
-               sp+=3; dp = sp;
-               *dp = (png_byte)(255 - *(sp++));
-            }
-         }
-
-#ifdef PNG_WRITE_16BIT_SUPPORTED
-         else
-         {
-            /* This inverts the alpha channel in RRGGBBAA */
-            png_bytep sp, dp;
-            png_uint_32 i;
-            png_uint_32 row_width = row_info->width;
-
-            for (i = 0, sp = dp = row; i < row_width; i++)
-            {
-               /* Does nothing
-               *(dp++) = *(sp++);
-               *(dp++) = *(sp++);
-               *(dp++) = *(sp++);
-               *(dp++) = *(sp++);
-               *(dp++) = *(sp++);
-               *(dp++) = *(sp++);
-               */
-               sp+=6; dp = sp;
-               *(dp++) = (png_byte)(255 - *(sp++));
-               *dp     = (png_byte)(255 - *(sp++));
-            }
-         }
-#endif /* WRITE_16BIT */
-      }
-
-      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
-      {
-         if (row_info->bit_depth == 8)
-         {
-            /* This inverts the alpha channel in GA */
-            png_bytep sp, dp;
-            png_uint_32 i;
-            png_uint_32 row_width = row_info->width;
-
-            for (i = 0, sp = dp = row; i < row_width; i++)
-            {
-               *(dp++) = *(sp++);
-               *(dp++) = (png_byte)(255 - *(sp++));
-            }
-         }
-
-#ifdef PNG_WRITE_16BIT_SUPPORTED
-         else
-         {
-            /* This inverts the alpha channel in GGAA */
-            png_bytep sp, dp;
-            png_uint_32 i;
-            png_uint_32 row_width = row_info->width;
-
-            for (i = 0, sp = dp = row; i < row_width; i++)
-            {
-               /* Does nothing
-               *(dp++) = *(sp++);
-               *(dp++) = *(sp++);
-               */
-               sp+=2; dp = sp;
-               *(dp++) = (png_byte)(255 - *(sp++));
-               *dp     = (png_byte)(255 - *(sp++));
-            }
-         }
-#endif /* WRITE_16BIT */
-      }
-   }
-}
-#endif
-
-/* Transform the data according to the user's wishes.  The order of
- * transformations is significant.
- */
-void /* PRIVATE */
-png_do_write_transformations(png_structrp png_ptr, png_row_infop row_info)
-{
-   png_debug(1, "in png_do_write_transformations");
-
-   if (png_ptr == NULL)
-      return;
-
-#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
-   if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0)
-      if (png_ptr->write_user_transform_fn != NULL)
-         (*(png_ptr->write_user_transform_fn)) /* User write transform
-                                                 function */
-             (png_ptr,  /* png_ptr */
-             row_info,  /* row_info: */
-                /*  png_uint_32 width;       width of row */
-                /*  size_t rowbytes;         number of bytes in row */
-                /*  png_byte color_type;     color type of pixels */
-                /*  png_byte bit_depth;      bit depth of samples */
-                /*  png_byte channels;       number of channels (1-4) */
-                /*  png_byte pixel_depth;    bits per pixel (depth*channels) */
-             png_ptr->row_buf + 1);      /* start of pixel data for row */
-#endif
-
-#ifdef PNG_WRITE_FILLER_SUPPORTED
-   if ((png_ptr->transformations & PNG_FILLER) != 0)
-      png_do_strip_channel(row_info, png_ptr->row_buf + 1,
-          !(png_ptr->flags & PNG_FLAG_FILLER_AFTER));
-#endif
-
-#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
-   if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
-      png_do_packswap(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_WRITE_PACK_SUPPORTED
-   if ((png_ptr->transformations & PNG_PACK) != 0)
-      png_do_pack(row_info, png_ptr->row_buf + 1,
-          (png_uint_32)png_ptr->bit_depth);
-#endif
-
-#ifdef PNG_WRITE_SWAP_SUPPORTED
-#  ifdef PNG_16BIT_SUPPORTED
-   if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0)
-      png_do_swap(row_info, png_ptr->row_buf + 1);
-#  endif
-#endif
-
-#ifdef PNG_WRITE_SHIFT_SUPPORTED
-   if ((png_ptr->transformations & PNG_SHIFT) != 0)
-      png_do_shift(row_info, png_ptr->row_buf + 1,
-           &(png_ptr->shift));
-#endif
-
-#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
-   if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0)
-      png_do_write_swap_alpha(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
-   if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0)
-      png_do_write_invert_alpha(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_WRITE_BGR_SUPPORTED
-   if ((png_ptr->transformations & PNG_BGR) != 0)
-      png_do_bgr(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_WRITE_INVERT_SUPPORTED
-   if ((png_ptr->transformations & PNG_INVERT_MONO) != 0)
-      png_do_invert(row_info, png_ptr->row_buf + 1);
-#endif
-}
-#endif /* WRITE_TRANSFORMS */
-#endif /* WRITE */
diff --git a/third_party/libpng16/pngwutil.c b/third_party/libpng16/pngwutil.c
deleted file mode 100644
index 16345e4..0000000
--- a/third_party/libpng16/pngwutil.c
+++ /dev/null
@@ -1,2781 +0,0 @@
-
-/* pngwutil.c - utilities to write a PNG file
- *
- * Copyright (c) 2018 Cosmin Truta
- * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
- * Copyright (c) 1996-1997 Andreas Dilger
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
-#include "pngpriv.h"
-
-#ifdef PNG_WRITE_SUPPORTED
-
-#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED
-/* Place a 32-bit number into a buffer in PNG byte order.  We work
- * with unsigned numbers for convenience, although one supported
- * ancillary chunk uses signed (two's complement) numbers.
- */
-void PNGAPI
-png_save_uint_32(png_bytep buf, png_uint_32 i)
-{
-   buf[0] = (png_byte)((i >> 24) & 0xffU);
-   buf[1] = (png_byte)((i >> 16) & 0xffU);
-   buf[2] = (png_byte)((i >>  8) & 0xffU);
-   buf[3] = (png_byte)( i        & 0xffU);
-}
-
-/* Place a 16-bit number into a buffer in PNG byte order.
- * The parameter is declared unsigned int, not png_uint_16,
- * just to avoid potential problems on pre-ANSI C compilers.
- */
-void PNGAPI
-png_save_uint_16(png_bytep buf, unsigned int i)
-{
-   buf[0] = (png_byte)((i >> 8) & 0xffU);
-   buf[1] = (png_byte)( i       & 0xffU);
-}
-#endif
-
-/* Simple function to write the signature.  If we have already written
- * the magic bytes of the signature, or more likely, the PNG stream is
- * being embedded into another stream and doesn't need its own signature,
- * we should call png_set_sig_bytes() to tell libpng how many of the
- * bytes have already been written.
- */
-void PNGAPI
-png_write_sig(png_structrp png_ptr)
-{
-   png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
-
-#ifdef PNG_IO_STATE_SUPPORTED
-   /* Inform the I/O callback that the signature is being written */
-   png_ptr->io_state = PNG_IO_WRITING | PNG_IO_SIGNATURE;
-#endif
-
-   /* Write the rest of the 8 byte signature */
-   png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes],
-       (size_t)(8 - png_ptr->sig_bytes));
-
-   if (png_ptr->sig_bytes < 3)
-      png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;
-}
-
-/* Write the start of a PNG chunk.  The type is the chunk type.
- * The total_length is the sum of the lengths of all the data you will be
- * passing in png_write_chunk_data().
- */
-static void
-png_write_chunk_header(png_structrp png_ptr, png_uint_32 chunk_name,
-    png_uint_32 length)
-{
-   png_byte buf[8];
-
-#if defined(PNG_DEBUG) && (PNG_DEBUG > 0)
-   PNG_CSTRING_FROM_CHUNK(buf, chunk_name);
-   png_debug2(0, "Writing %s chunk, length = %lu", buf, (unsigned long)length);
-#endif
-
-   if (png_ptr == NULL)
-      return;
-
-#ifdef PNG_IO_STATE_SUPPORTED
-   /* Inform the I/O callback that the chunk header is being written.
-    * PNG_IO_CHUNK_HDR requires a single I/O call.
-    */
-   png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_HDR;
-#endif
-
-   /* Write the length and the chunk name */
-   png_save_uint_32(buf, length);
-   png_save_uint_32(buf + 4, chunk_name);
-   png_write_data(png_ptr, buf, 8);
-
-   /* Put the chunk name into png_ptr->chunk_name */
-   png_ptr->chunk_name = chunk_name;
-
-   /* Reset the crc and run it over the chunk name */
-   png_reset_crc(png_ptr);
-
-   png_calculate_crc(png_ptr, buf + 4, 4);
-
-#ifdef PNG_IO_STATE_SUPPORTED
-   /* Inform the I/O callback that chunk data will (possibly) be written.
-    * PNG_IO_CHUNK_DATA does NOT require a specific number of I/O calls.
-    */
-   png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_DATA;
-#endif
-}
-
-void PNGAPI
-png_write_chunk_start(png_structrp png_ptr, png_const_bytep chunk_string,
-    png_uint_32 length)
-{
-   png_write_chunk_header(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), length);
-}
-
-/* Write the data of a PNG chunk started with png_write_chunk_header().
- * Note that multiple calls to this function are allowed, and that the
- * sum of the lengths from these calls *must* add up to the total_length
- * given to png_write_chunk_header().
- */
-void PNGAPI
-png_write_chunk_data(png_structrp png_ptr, png_const_bytep data, size_t length)
-{
-   /* Write the data, and run the CRC over it */
-   if (png_ptr == NULL)
-      return;
-
-   if (data != NULL && length > 0)
-   {
-      png_write_data(png_ptr, data, length);
-
-      /* Update the CRC after writing the data,
-       * in case the user I/O routine alters it.
-       */
-      png_calculate_crc(png_ptr, data, length);
-   }
-}
-
-/* Finish a chunk started with png_write_chunk_header(). */
-void PNGAPI
-png_write_chunk_end(png_structrp png_ptr)
-{
-   png_byte buf[4];
-
-   if (png_ptr == NULL) return;
-
-#ifdef PNG_IO_STATE_SUPPORTED
-   /* Inform the I/O callback that the chunk CRC is being written.
-    * PNG_IO_CHUNK_CRC requires a single I/O function call.
-    */
-   png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_CRC;
-#endif
-
-   /* Write the crc in a single operation */
-   png_save_uint_32(buf, png_ptr->crc);
-
-   png_write_data(png_ptr, buf, 4);
-}
-
-/* Write a PNG chunk all at once.  The type is an array of ASCII characters
- * representing the chunk name.  The array must be at least 4 bytes in
- * length, and does not need to be null terminated.  To be safe, pass the
- * pre-defined chunk names here, and if you need a new one, define it
- * where the others are defined.  The length is the length of the data.
- * All the data must be present.  If that is not possible, use the
- * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end()
- * functions instead.
- */
-static void
-png_write_complete_chunk(png_structrp png_ptr, png_uint_32 chunk_name,
-    png_const_bytep data, size_t length)
-{
-   if (png_ptr == NULL)
-      return;
-
-   /* On 64-bit architectures 'length' may not fit in a png_uint_32. */
-   if (length > PNG_UINT_31_MAX)
-      png_error(png_ptr, "length exceeds PNG maximum");
-
-   png_write_chunk_header(png_ptr, chunk_name, (png_uint_32)length);
-   png_write_chunk_data(png_ptr, data, length);
-   png_write_chunk_end(png_ptr);
-}
-
-/* This is the API that calls the internal function above. */
-void PNGAPI
-png_write_chunk(png_structrp png_ptr, png_const_bytep chunk_string,
-    png_const_bytep data, size_t length)
-{
-   png_write_complete_chunk(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), data,
-       length);
-}
-
-/* This is used below to find the size of an image to pass to png_deflate_claim,
- * so it only needs to be accurate if the size is less than 16384 bytes (the
- * point at which a lower LZ window size can be used.)
- */
-static png_alloc_size_t
-png_image_size(png_structrp png_ptr)
-{
-   /* Only return sizes up to the maximum of a png_uint_32; do this by limiting
-    * the width and height used to 15 bits.
-    */
-   png_uint_32 h = png_ptr->height;
-
-   if (png_ptr->rowbytes < 32768 && h < 32768)
-   {
-      if (png_ptr->interlaced != 0)
-      {
-         /* Interlacing makes the image larger because of the replication of
-          * both the filter byte and the padding to a byte boundary.
-          */
-         png_uint_32 w = png_ptr->width;
-         unsigned int pd = png_ptr->pixel_depth;
-         png_alloc_size_t cb_base;
-         int pass;
-
-         for (cb_base=0, pass=0; pass<=6; ++pass)
-         {
-            png_uint_32 pw = PNG_PASS_COLS(w, pass);
-
-            if (pw > 0)
-               cb_base += (PNG_ROWBYTES(pd, pw)+1) * PNG_PASS_ROWS(h, pass);
-         }
-
-         return cb_base;
-      }
-
-      else
-         return (png_ptr->rowbytes+1) * h;
-   }
-
-   else
-      return 0xffffffffU;
-}
-
-#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
-   /* This is the code to hack the first two bytes of the deflate stream (the
-    * deflate header) to correct the windowBits value to match the actual data
-    * size.  Note that the second argument is the *uncompressed* size but the
-    * first argument is the *compressed* data (and it must be deflate
-    * compressed.)
-    */
-static void
-optimize_cmf(png_bytep data, png_alloc_size_t data_size)
-{
-   /* Optimize the CMF field in the zlib stream.  The resultant zlib stream is
-    * still compliant to the stream specification.
-    */
-   if (data_size <= 16384) /* else windowBits must be 15 */
-   {
-      unsigned int z_cmf = data[0];  /* zlib compression method and flags */
-
-      if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70)
-      {
-         unsigned int z_cinfo;
-         unsigned int half_z_window_size;
-
-         z_cinfo = z_cmf >> 4;
-         half_z_window_size = 1U << (z_cinfo + 7);
-
-         if (data_size <= half_z_window_size) /* else no change */
-         {
-            unsigned int tmp;
-
-            do
-            {
-               half_z_window_size >>= 1;
-               --z_cinfo;
-            }
-            while (z_cinfo > 0 && data_size <= half_z_window_size);
-
-            z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4);
-
-            data[0] = (png_byte)z_cmf;
-            tmp = data[1] & 0xe0;
-            tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f;
-            data[1] = (png_byte)tmp;
-         }
-      }
-   }
-}
-#endif /* WRITE_OPTIMIZE_CMF */
-
-/* Initialize the compressor for the appropriate type of compression. */
-static int
-png_deflate_claim(png_structrp png_ptr, png_uint_32 owner,
-    png_alloc_size_t data_size)
-{
-   if (png_ptr->zowner != 0)
-   {
-#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_ERROR_TEXT_SUPPORTED)
-      char msg[64];
-
-      PNG_STRING_FROM_CHUNK(msg, owner);
-      msg[4] = ':';
-      msg[5] = ' ';
-      PNG_STRING_FROM_CHUNK(msg+6, png_ptr->zowner);
-      /* So the message that results is "<chunk> using zstream"; this is an
-       * internal error, but is very useful for debugging.  i18n requirements
-       * are minimal.
-       */
-      (void)png_safecat(msg, (sizeof msg), 10, " using zstream");
-#endif
-#if PNG_RELEASE_BUILD
-         png_warning(png_ptr, msg);
-
-         /* Attempt sane error recovery */
-         if (png_ptr->zowner == png_IDAT) /* don't steal from IDAT */
-         {
-            png_ptr->zstream.msg = PNGZ_MSG_CAST("in use by IDAT");
-            return Z_STREAM_ERROR;
-         }
-
-         png_ptr->zowner = 0;
-#else
-         png_error(png_ptr, msg);
-#endif
-   }
-
-   {
-      int level = png_ptr->zlib_level;
-      int method = png_ptr->zlib_method;
-      int windowBits = png_ptr->zlib_window_bits;
-      int memLevel = png_ptr->zlib_mem_level;
-      int strategy; /* set below */
-      int ret; /* zlib return code */
-
-      if (owner == png_IDAT)
-      {
-         if ((png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY) != 0)
-            strategy = png_ptr->zlib_strategy;
-
-         else if (png_ptr->do_filter != PNG_FILTER_NONE)
-            strategy = PNG_Z_DEFAULT_STRATEGY;
-
-         else
-            strategy = PNG_Z_DEFAULT_NOFILTER_STRATEGY;
-      }
-
-      else
-      {
-#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
-            level = png_ptr->zlib_text_level;
-            method = png_ptr->zlib_text_method;
-            windowBits = png_ptr->zlib_text_window_bits;
-            memLevel = png_ptr->zlib_text_mem_level;
-            strategy = png_ptr->zlib_text_strategy;
-#else
-            /* If customization is not supported the values all come from the
-             * IDAT values except for the strategy, which is fixed to the
-             * default.  (This is the pre-1.6.0 behavior too, although it was
-             * implemented in a very different way.)
-             */
-            strategy = Z_DEFAULT_STRATEGY;
-#endif
-      }
-
-      /* Adjust 'windowBits' down if larger than 'data_size'; to stop this
-       * happening just pass 32768 as the data_size parameter.  Notice that zlib
-       * requires an extra 262 bytes in the window in addition to the data to be
-       * able to see the whole of the data, so if data_size+262 takes us to the
-       * next windowBits size we need to fix up the value later.  (Because even
-       * though deflate needs the extra window, inflate does not!)
-       */
-      if (data_size <= 16384)
-      {
-         /* IMPLEMENTATION NOTE: this 'half_window_size' stuff is only here to
-          * work round a Microsoft Visual C misbehavior which, contrary to C-90,
-          * widens the result of the following shift to 64-bits if (and,
-          * apparently, only if) it is used in a test.
-          */
-         unsigned int half_window_size = 1U << (windowBits-1);
-
-         while (data_size + 262 <= half_window_size)
-         {
-            half_window_size >>= 1;
-            --windowBits;
-         }
-      }
-
-      /* Check against the previous initialized values, if any. */
-      if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0 &&
-         (png_ptr->zlib_set_level != level ||
-         png_ptr->zlib_set_method != method ||
-         png_ptr->zlib_set_window_bits != windowBits ||
-         png_ptr->zlib_set_mem_level != memLevel ||
-         png_ptr->zlib_set_strategy != strategy))
-      {
-         if (deflateEnd(&png_ptr->zstream) != Z_OK)
-            png_warning(png_ptr, "deflateEnd failed (ignored)");
-
-         png_ptr->flags &= ~PNG_FLAG_ZSTREAM_INITIALIZED;
-      }
-
-      /* For safety clear out the input and output pointers (currently zlib
-       * doesn't use them on Init, but it might in the future).
-       */
-      png_ptr->zstream.next_in = NULL;
-      png_ptr->zstream.avail_in = 0;
-      png_ptr->zstream.next_out = NULL;
-      png_ptr->zstream.avail_out = 0;
-
-      /* Now initialize if required, setting the new parameters, otherwise just
-       * do a simple reset to the previous parameters.
-       */
-      if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0)
-         ret = deflateReset(&png_ptr->zstream);
-
-      else
-      {
-         ret = deflateInit2(&png_ptr->zstream, level, method, windowBits,
-             memLevel, strategy);
-
-         if (ret == Z_OK)
-            png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED;
-      }
-
-      /* The return code is from either deflateReset or deflateInit2; they have
-       * pretty much the same set of error codes.
-       */
-      if (ret == Z_OK)
-         png_ptr->zowner = owner;
-
-      else
-         png_zstream_error(png_ptr, ret);
-
-      return ret;
-   }
-}
-
-/* Clean up (or trim) a linked list of compression buffers. */
-void /* PRIVATE */
-png_free_buffer_list(png_structrp png_ptr, png_compression_bufferp *listp)
-{
-   png_compression_bufferp list = *listp;
-
-   if (list != NULL)
-   {
-      *listp = NULL;
-
-      do
-      {
-         png_compression_bufferp next = list->next;
-
-         png_free(png_ptr, list);
-         list = next;
-      }
-      while (list != NULL);
-   }
-}
-
-#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
-/* This pair of functions encapsulates the operation of (a) compressing a
- * text string, and (b) issuing it later as a series of chunk data writes.
- * The compression_state structure is shared context for these functions
- * set up by the caller to allow access to the relevant local variables.
- *
- * compression_buffer (new in 1.6.0) is just a linked list of zbuffer_size
- * temporary buffers.  From 1.6.0 it is retained in png_struct so that it will
- * be correctly freed in the event of a write error (previous implementations
- * just leaked memory.)
- */
-typedef struct
-{
-   png_const_bytep      input;        /* The uncompressed input data */
-   png_alloc_size_t     input_len;    /* Its length */
-   png_uint_32          output_len;   /* Final compressed length */
-   png_byte             output[1024]; /* First block of output */
-} compression_state;
-
-static void
-png_text_compress_init(compression_state *comp, png_const_bytep input,
-    png_alloc_size_t input_len)
-{
-   comp->input = input;
-   comp->input_len = input_len;
-   comp->output_len = 0;
-}
-
-/* Compress the data in the compression state input */
-static int
-png_text_compress(png_structrp png_ptr, png_uint_32 chunk_name,
-    compression_state *comp, png_uint_32 prefix_len)
-{
-   int ret;
-
-   /* To find the length of the output it is necessary to first compress the
-    * input. The result is buffered rather than using the two-pass algorithm
-    * that is used on the inflate side; deflate is assumed to be slower and a
-    * PNG writer is assumed to have more memory available than a PNG reader.
-    *
-    * IMPLEMENTATION NOTE: the zlib API deflateBound() can be used to find an
-    * upper limit on the output size, but it is always bigger than the input
-    * size so it is likely to be more efficient to use this linked-list
-    * approach.
-    */
-   ret = png_deflate_claim(png_ptr, chunk_name, comp->input_len);
-
-   if (ret != Z_OK)
-      return ret;
-
-   /* Set up the compression buffers, we need a loop here to avoid overflowing a
-    * uInt.  Use ZLIB_IO_MAX to limit the input.  The output is always limited
-    * by the output buffer size, so there is no need to check that.  Since this
-    * is ANSI-C we know that an 'int', hence a uInt, is always at least 16 bits
-    * in size.
-    */
-   {
-      png_compression_bufferp *end = &png_ptr->zbuffer_list;
-      png_alloc_size_t input_len = comp->input_len; /* may be zero! */
-      png_uint_32 output_len;
-
-      /* zlib updates these for us: */
-      png_ptr->zstream.next_in = PNGZ_INPUT_CAST(comp->input);
-      png_ptr->zstream.avail_in = 0; /* Set below */
-      png_ptr->zstream.next_out = comp->output;
-      png_ptr->zstream.avail_out = (sizeof comp->output);
-
-      output_len = png_ptr->zstream.avail_out;
-
-      do
-      {
-         uInt avail_in = ZLIB_IO_MAX;
-
-         if (avail_in > input_len)
-            avail_in = (uInt)input_len;
-
-         input_len -= avail_in;
-
-         png_ptr->zstream.avail_in = avail_in;
-
-         if (png_ptr->zstream.avail_out == 0)
-         {
-            png_compression_buffer *next;
-
-            /* Chunk data is limited to 2^31 bytes in length, so the prefix
-             * length must be counted here.
-             */
-            if (output_len + prefix_len > PNG_UINT_31_MAX)
-            {
-               ret = Z_MEM_ERROR;
-               break;
-            }
-
-            /* Need a new (malloc'ed) buffer, but there may be one present
-             * already.
-             */
-            next = *end;
-            if (next == NULL)
-            {
-               next = png_voidcast(png_compression_bufferp, png_malloc_base
-                  (png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));
-
-               if (next == NULL)
-               {
-                  ret = Z_MEM_ERROR;
-                  break;
-               }
-
-               /* Link in this buffer (so that it will be freed later) */
-               next->next = NULL;
-               *end = next;
-            }
-
-            png_ptr->zstream.next_out = next->output;
-            png_ptr->zstream.avail_out = png_ptr->zbuffer_size;
-            output_len += png_ptr->zstream.avail_out;
-
-            /* Move 'end' to the next buffer pointer. */
-            end = &next->next;
-         }
-
-         /* Compress the data */
-         ret = deflate(&png_ptr->zstream,
-             input_len > 0 ? Z_NO_FLUSH : Z_FINISH);
-
-         /* Claw back input data that was not consumed (because avail_in is
-          * reset above every time round the loop).
-          */
-         input_len += png_ptr->zstream.avail_in;
-         png_ptr->zstream.avail_in = 0; /* safety */
-      }
-      while (ret == Z_OK);
-
-      /* There may be some space left in the last output buffer. This needs to
-       * be subtracted from output_len.
-       */
-      output_len -= png_ptr->zstream.avail_out;
-      png_ptr->zstream.avail_out = 0; /* safety */
-      comp->output_len = output_len;
-
-      /* Now double check the output length, put in a custom message if it is
-       * too long.  Otherwise ensure the z_stream::msg pointer is set to
-       * something.
-       */
-      if (output_len + prefix_len >= PNG_UINT_31_MAX)
-      {
-         png_ptr->zstream.msg = PNGZ_MSG_CAST("compressed data too long");
-         ret = Z_MEM_ERROR;
-      }
-
-      else
-         png_zstream_error(png_ptr, ret);
-
-      /* Reset zlib for another zTXt/iTXt or image data */
-      png_ptr->zowner = 0;
-
-      /* The only success case is Z_STREAM_END, input_len must be 0; if not this
-       * is an internal error.
-       */
-      if (ret == Z_STREAM_END && input_len == 0)
-      {
-#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
-         /* Fix up the deflate header, if required */
-         optimize_cmf(comp->output, comp->input_len);
-#endif
-         /* But Z_OK is returned, not Z_STREAM_END; this allows the claim
-          * function above to return Z_STREAM_END on an error (though it never
-          * does in the current versions of zlib.)
-          */
-         return Z_OK;
-      }
-
-      else
-         return ret;
-   }
-}
-
-/* Ship the compressed text out via chunk writes */
-static void
-png_write_compressed_data_out(png_structrp png_ptr, compression_state *comp)
-{
-   png_uint_32 output_len = comp->output_len;
-   png_const_bytep output = comp->output;
-   png_uint_32 avail = (sizeof comp->output);
-   png_compression_buffer *next = png_ptr->zbuffer_list;
-
-   for (;;)
-   {
-      if (avail > output_len)
-         avail = output_len;
-
-      png_write_chunk_data(png_ptr, output, avail);
-
-      output_len -= avail;
-
-      if (output_len == 0 || next == NULL)
-         break;
-
-      avail = png_ptr->zbuffer_size;
-      output = next->output;
-      next = next->next;
-   }
-
-   /* This is an internal error; 'next' must have been NULL! */
-   if (output_len > 0)
-      png_error(png_ptr, "error writing ancillary chunked compressed data");
-}
-#endif /* WRITE_COMPRESSED_TEXT */
-
-/* Write the IHDR chunk, and update the png_struct with the necessary
- * information.  Note that the rest of this code depends upon this
- * information being correct.
- */
-void /* PRIVATE */
-png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height,
-    int bit_depth, int color_type, int compression_type, int filter_type,
-    int interlace_type)
-{
-   png_byte buf[13]; /* Buffer to store the IHDR info */
-   int is_invalid_depth;
-
-   png_debug(1, "in png_write_IHDR");
-
-   /* Check that we have valid input data from the application info */
-   switch (color_type)
-   {
-      case PNG_COLOR_TYPE_GRAY:
-         switch (bit_depth)
-         {
-            case 1:
-            case 2:
-            case 4:
-            case 8:
-#ifdef PNG_WRITE_16BIT_SUPPORTED
-            case 16:
-#endif
-               png_ptr->channels = 1; break;
-
-            default:
-               png_error(png_ptr,
-                   "Invalid bit depth for grayscale image");
-         }
-         break;
-
-      case PNG_COLOR_TYPE_RGB:
-         is_invalid_depth = (bit_depth != 8);
-#ifdef PNG_WRITE_16BIT_SUPPORTED
-         is_invalid_depth = (is_invalid_depth && bit_depth != 16);
-#endif
-         if (is_invalid_depth)
-            png_error(png_ptr, "Invalid bit depth for RGB image");
-
-         png_ptr->channels = 3;
-         break;
-
-      case PNG_COLOR_TYPE_PALETTE:
-         switch (bit_depth)
-         {
-            case 1:
-            case 2:
-            case 4:
-            case 8:
-               png_ptr->channels = 1;
-               break;
-
-            default:
-               png_error(png_ptr, "Invalid bit depth for paletted image");
-         }
-         break;
-
-      case PNG_COLOR_TYPE_GRAY_ALPHA:
-         is_invalid_depth = (bit_depth != 8);
-#ifdef PNG_WRITE_16BIT_SUPPORTED
-         is_invalid_depth = (is_invalid_depth && bit_depth != 16);
-#endif
-         if (is_invalid_depth)
-            png_error(png_ptr, "Invalid bit depth for grayscale+alpha image");
-
-         png_ptr->channels = 2;
-         break;
-
-      case PNG_COLOR_TYPE_RGB_ALPHA:
-         is_invalid_depth = (bit_depth != 8);
-#ifdef PNG_WRITE_16BIT_SUPPORTED
-         is_invalid_depth = (is_invalid_depth && bit_depth != 16);
-#endif
-         if (is_invalid_depth)
-            png_error(png_ptr, "Invalid bit depth for RGBA image");
-
-         png_ptr->channels = 4;
-         break;
-
-      default:
-         png_error(png_ptr, "Invalid image color type specified");
-   }
-
-   if (compression_type != PNG_COMPRESSION_TYPE_BASE)
-   {
-      png_warning(png_ptr, "Invalid compression type specified");
-      compression_type = PNG_COMPRESSION_TYPE_BASE;
-   }
-
-   /* Write filter_method 64 (intrapixel differencing) only if
-    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
-    * 2. Libpng did not write a PNG signature (this filter_method is only
-    *    used in PNG datastreams that are embedded in MNG datastreams) and
-    * 3. The application called png_permit_mng_features with a mask that
-    *    included PNG_FLAG_MNG_FILTER_64 and
-    * 4. The filter_method is 64 and
-    * 5. The color_type is RGB or RGBA
-    */
-   if (
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-       !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
-       ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) &&
-       (color_type == PNG_COLOR_TYPE_RGB ||
-        color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
-       (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) &&
-#endif
-       filter_type != PNG_FILTER_TYPE_BASE)
-   {
-      png_warning(png_ptr, "Invalid filter type specified");
-      filter_type = PNG_FILTER_TYPE_BASE;
-   }
-
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
-   if (interlace_type != PNG_INTERLACE_NONE &&
-       interlace_type != PNG_INTERLACE_ADAM7)
-   {
-      png_warning(png_ptr, "Invalid interlace type specified");
-      interlace_type = PNG_INTERLACE_ADAM7;
-   }
-#else
-   interlace_type=PNG_INTERLACE_NONE;
-#endif
-
-   /* Save the relevant information */
-   png_ptr->bit_depth = (png_byte)bit_depth;
-   png_ptr->color_type = (png_byte)color_type;
-   png_ptr->interlaced = (png_byte)interlace_type;
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-   png_ptr->filter_type = (png_byte)filter_type;
-#endif
-   png_ptr->compression_type = (png_byte)compression_type;
-   png_ptr->width = width;
-   png_ptr->height = height;
-
-   png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels);
-   png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width);
-   /* Set the usr info, so any transformations can modify it */
-   png_ptr->usr_width = png_ptr->width;
-   png_ptr->usr_bit_depth = png_ptr->bit_depth;
-   png_ptr->usr_channels = png_ptr->channels;
-
-   /* Pack the header information into the buffer */
-   png_save_uint_32(buf, width);
-   png_save_uint_32(buf + 4, height);
-   buf[8] = (png_byte)bit_depth;
-   buf[9] = (png_byte)color_type;
-   buf[10] = (png_byte)compression_type;
-   buf[11] = (png_byte)filter_type;
-   buf[12] = (png_byte)interlace_type;
-
-   /* Write the chunk */
-   png_write_complete_chunk(png_ptr, png_IHDR, buf, 13);
-
-   if ((png_ptr->do_filter) == PNG_NO_FILTERS)
-   {
-      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
-          png_ptr->bit_depth < 8)
-         png_ptr->do_filter = PNG_FILTER_NONE;
-
-      else
-         png_ptr->do_filter = PNG_ALL_FILTERS;
-   }
-
-   png_ptr->mode = PNG_HAVE_IHDR; /* not READY_FOR_ZTXT */
-}
-
-/* Write the palette.  We are careful not to trust png_color to be in the
- * correct order for PNG, so people can redefine it to any convenient
- * structure.
- */
-void /* PRIVATE */
-png_write_PLTE(png_structrp png_ptr, png_const_colorp palette,
-    png_uint_32 num_pal)
-{
-   png_uint_32 max_palette_length, i;
-   png_const_colorp pal_ptr;
-   png_byte buf[3];
-
-   png_debug(1, "in png_write_PLTE");
-
-   max_palette_length = (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ?
-      (1 << png_ptr->bit_depth) : PNG_MAX_PALETTE_LENGTH;
-
-   if ((
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-       (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0 &&
-#endif
-       num_pal == 0) || num_pal > max_palette_length)
-   {
-      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-      {
-         png_error(png_ptr, "Invalid number of colors in palette");
-      }
-
-      else
-      {
-         png_warning(png_ptr, "Invalid number of colors in palette");
-         return;
-      }
-   }
-
-   if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0)
-   {
-      png_warning(png_ptr,
-          "Ignoring request to write a PLTE chunk in grayscale PNG");
-
-      return;
-   }
-
-   png_ptr->num_palette = (png_uint_16)num_pal;
-   png_debug1(3, "num_palette = %d", png_ptr->num_palette);
-
-   png_write_chunk_header(png_ptr, png_PLTE, (png_uint_32)(num_pal * 3));
-#ifdef PNG_POINTER_INDEXING_SUPPORTED
-
-   for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++)
-   {
-      buf[0] = pal_ptr->red;
-      buf[1] = pal_ptr->green;
-      buf[2] = pal_ptr->blue;
-      png_write_chunk_data(png_ptr, buf, 3);
-   }
-
-#else
-   /* This is a little slower but some buggy compilers need to do this
-    * instead
-    */
-   pal_ptr=palette;
-
-   for (i = 0; i < num_pal; i++)
-   {
-      buf[0] = pal_ptr[i].red;
-      buf[1] = pal_ptr[i].green;
-      buf[2] = pal_ptr[i].blue;
-      png_write_chunk_data(png_ptr, buf, 3);
-   }
-
-#endif
-   png_write_chunk_end(png_ptr);
-   png_ptr->mode |= PNG_HAVE_PLTE;
-}
-
-/* This is similar to png_text_compress, above, except that it does not require
- * all of the data at once and, instead of buffering the compressed result,
- * writes it as IDAT chunks.  Unlike png_text_compress it *can* png_error out
- * because it calls the write interface.  As a result it does its own error
- * reporting and does not return an error code.  In the event of error it will
- * just call png_error.  The input data length may exceed 32-bits.  The 'flush'
- * parameter is exactly the same as that to deflate, with the following
- * meanings:
- *
- * Z_NO_FLUSH: normal incremental output of compressed data
- * Z_SYNC_FLUSH: do a SYNC_FLUSH, used by png_write_flush
- * Z_FINISH: this is the end of the input, do a Z_FINISH and clean up
- *
- * The routine manages the acquire and release of the png_ptr->zstream by
- * checking and (at the end) clearing png_ptr->zowner; it does some sanity
- * checks on the 'mode' flags while doing this.
- */
-void /* PRIVATE */
-png_compress_IDAT(png_structrp png_ptr, png_const_bytep input,
-    png_alloc_size_t input_len, int flush)
-{
-   if (png_ptr->zowner != png_IDAT)
-   {
-      /* First time.   Ensure we have a temporary buffer for compression and
-       * trim the buffer list if it has more than one entry to free memory.
-       * If 'WRITE_COMPRESSED_TEXT' is not set the list will never have been
-       * created at this point, but the check here is quick and safe.
-       */
-      if (png_ptr->zbuffer_list == NULL)
-      {
-         png_ptr->zbuffer_list = png_voidcast(png_compression_bufferp,
-             png_malloc(png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));
-         png_ptr->zbuffer_list->next = NULL;
-      }
-
-      else
-         png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list->next);
-
-      /* It is a terminal error if we can't claim the zstream. */
-      if (png_deflate_claim(png_ptr, png_IDAT, png_image_size(png_ptr)) != Z_OK)
-         png_error(png_ptr, png_ptr->zstream.msg);
-
-      /* The output state is maintained in png_ptr->zstream, so it must be
-       * initialized here after the claim.
-       */
-      png_ptr->zstream.next_out = png_ptr->zbuffer_list->output;
-      png_ptr->zstream.avail_out = png_ptr->zbuffer_size;
-   }
-
-   /* Now loop reading and writing until all the input is consumed or an error
-    * terminates the operation.  The _out values are maintained across calls to
-    * this function, but the input must be reset each time.
-    */
-   png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input);
-   png_ptr->zstream.avail_in = 0; /* set below */
-   for (;;)
-   {
-      int ret;
-
-      /* INPUT: from the row data */
-      uInt avail = ZLIB_IO_MAX;
-
-      if (avail > input_len)
-         avail = (uInt)input_len; /* safe because of the check */
-
-      png_ptr->zstream.avail_in = avail;
-      input_len -= avail;
-
-      ret = deflate(&png_ptr->zstream, input_len > 0 ? Z_NO_FLUSH : flush);
-
-      /* Include as-yet unconsumed input */
-      input_len += png_ptr->zstream.avail_in;
-      png_ptr->zstream.avail_in = 0;
-
-      /* OUTPUT: write complete IDAT chunks when avail_out drops to zero. Note
-       * that these two zstream fields are preserved across the calls, therefore
-       * there is no need to set these up on entry to the loop.
-       */
-      if (png_ptr->zstream.avail_out == 0)
-      {
-         png_bytep data = png_ptr->zbuffer_list->output;
-         uInt size = png_ptr->zbuffer_size;
-
-         /* Write an IDAT containing the data then reset the buffer.  The
-          * first IDAT may need deflate header optimization.
-          */
-#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
-            if ((png_ptr->mode & PNG_HAVE_IDAT) == 0 &&
-                png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
-               optimize_cmf(data, png_image_size(png_ptr));
-#endif
-
-         if (size > 0)
-            png_write_complete_chunk(png_ptr, png_IDAT, data, size);
-         png_ptr->mode |= PNG_HAVE_IDAT;
-
-         png_ptr->zstream.next_out = data;
-         png_ptr->zstream.avail_out = size;
-
-         /* For SYNC_FLUSH or FINISH it is essential to keep calling zlib with
-          * the same flush parameter until it has finished output, for NO_FLUSH
-          * it doesn't matter.
-          */
-         if (ret == Z_OK && flush != Z_NO_FLUSH)
-            continue;
-      }
-
-      /* The order of these checks doesn't matter much; it just affects which
-       * possible error might be detected if multiple things go wrong at once.
-       */
-      if (ret == Z_OK) /* most likely return code! */
-      {
-         /* If all the input has been consumed then just return.  If Z_FINISH
-          * was used as the flush parameter something has gone wrong if we get
-          * here.
-          */
-         if (input_len == 0)
-         {
-            if (flush == Z_FINISH)
-               png_error(png_ptr, "Z_OK on Z_FINISH with output space");
-
-            return;
-         }
-      }
-
-      else if (ret == Z_STREAM_END && flush == Z_FINISH)
-      {
-         /* This is the end of the IDAT data; any pending output must be
-          * flushed.  For small PNG files we may still be at the beginning.
-          */
-         png_bytep data = png_ptr->zbuffer_list->output;
-         uInt size = png_ptr->zbuffer_size - png_ptr->zstream.avail_out;
-
-#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
-         if ((png_ptr->mode & PNG_HAVE_IDAT) == 0 &&
-             png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
-            optimize_cmf(data, png_image_size(png_ptr));
-#endif
-
-         if (size > 0)
-            png_write_complete_chunk(png_ptr, png_IDAT, data, size);
-         png_ptr->zstream.avail_out = 0;
-         png_ptr->zstream.next_out = NULL;
-         png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT;
-
-         png_ptr->zowner = 0; /* Release the stream */
-         return;
-      }
-
-      else
-      {
-         /* This is an error condition. */
-         png_zstream_error(png_ptr, ret);
-         png_error(png_ptr, png_ptr->zstream.msg);
-      }
-   }
-}
-
-/* Write an IEND chunk */
-void /* PRIVATE */
-png_write_IEND(png_structrp png_ptr)
-{
-   png_debug(1, "in png_write_IEND");
-
-   png_write_complete_chunk(png_ptr, png_IEND, NULL, 0);
-   png_ptr->mode |= PNG_HAVE_IEND;
-}
-
-#ifdef PNG_WRITE_gAMA_SUPPORTED
-/* Write a gAMA chunk */
-void /* PRIVATE */
-png_write_gAMA_fixed(png_structrp png_ptr, png_fixed_point file_gamma)
-{
-   png_byte buf[4];
-
-   png_debug(1, "in png_write_gAMA");
-
-   /* file_gamma is saved in 1/100,000ths */
-   png_save_uint_32(buf, (png_uint_32)file_gamma);
-   png_write_complete_chunk(png_ptr, png_gAMA, buf, 4);
-}
-#endif
-
-#ifdef PNG_WRITE_sRGB_SUPPORTED
-/* Write a sRGB chunk */
-void /* PRIVATE */
-png_write_sRGB(png_structrp png_ptr, int srgb_intent)
-{
-   png_byte buf[1];
-
-   png_debug(1, "in png_write_sRGB");
-
-   if (srgb_intent >= PNG_sRGB_INTENT_LAST)
-      png_warning(png_ptr,
-          "Invalid sRGB rendering intent specified");
-
-   buf[0]=(png_byte)srgb_intent;
-   png_write_complete_chunk(png_ptr, png_sRGB, buf, 1);
-}
-#endif
-
-#ifdef PNG_WRITE_iCCP_SUPPORTED
-/* Write an iCCP chunk */
-void /* PRIVATE */
-png_write_iCCP(png_structrp png_ptr, png_const_charp name,
-    png_const_bytep profile)
-{
-   png_uint_32 name_len;
-   png_uint_32 profile_len;
-   png_byte new_name[81]; /* 1 byte for the compression byte */
-   compression_state comp;
-   png_uint_32 temp;
-
-   png_debug(1, "in png_write_iCCP");
-
-   /* These are all internal problems: the profile should have been checked
-    * before when it was stored.
-    */
-   if (profile == NULL)
-      png_error(png_ptr, "No profile for iCCP chunk"); /* internal error */
-
-   profile_len = png_get_uint_32(profile);
-
-   if (profile_len < 132)
-      png_error(png_ptr, "ICC profile too short");
-
-   temp = (png_uint_32) (*(profile+8));
-   if (temp > 3 && (profile_len & 0x03))
-      png_error(png_ptr, "ICC profile length invalid (not a multiple of 4)");
-
-   {
-      png_uint_32 embedded_profile_len = png_get_uint_32(profile);
-
-      if (profile_len != embedded_profile_len)
-         png_error(png_ptr, "Profile length does not match profile");
-   }
-
-   name_len = png_check_keyword(png_ptr, name, new_name);
-
-   if (name_len == 0)
-      png_error(png_ptr, "iCCP: invalid keyword");
-
-   new_name[++name_len] = PNG_COMPRESSION_TYPE_BASE;
-
-   /* Make sure we include the NULL after the name and the compression type */
-   ++name_len;
-
-   png_text_compress_init(&comp, profile, profile_len);
-
-   /* Allow for keyword terminator and compression byte */
-   if (png_text_compress(png_ptr, png_iCCP, &comp, name_len) != Z_OK)
-      png_error(png_ptr, png_ptr->zstream.msg);
-
-   png_write_chunk_header(png_ptr, png_iCCP, name_len + comp.output_len);
-
-   png_write_chunk_data(png_ptr, new_name, name_len);
-
-   png_write_compressed_data_out(png_ptr, &comp);
-
-   png_write_chunk_end(png_ptr);
-}
-#endif
-
-#ifdef PNG_WRITE_sPLT_SUPPORTED
-/* Write a sPLT chunk */
-void /* PRIVATE */
-png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette)
-{
-   png_uint_32 name_len;
-   png_byte new_name[80];
-   png_byte entrybuf[10];
-   size_t entry_size = (spalette->depth == 8 ? 6 : 10);
-   size_t palette_size = entry_size * (size_t)spalette->nentries;
-   png_sPLT_entryp ep;
-#ifndef PNG_POINTER_INDEXING_SUPPORTED
-   int i;
-#endif
-
-   png_debug(1, "in png_write_sPLT");
-
-   name_len = png_check_keyword(png_ptr, spalette->name, new_name);
-
-   if (name_len == 0)
-      png_error(png_ptr, "sPLT: invalid keyword");
-
-   /* Make sure we include the NULL after the name */
-   png_write_chunk_header(png_ptr, png_sPLT,
-       (png_uint_32)(name_len + 2 + palette_size));
-
-   png_write_chunk_data(png_ptr, (png_bytep)new_name, (size_t)(name_len + 1));
-
-   png_write_chunk_data(png_ptr, &spalette->depth, 1);
-
-   /* Loop through each palette entry, writing appropriately */
-#ifdef PNG_POINTER_INDEXING_SUPPORTED
-   for (ep = spalette->entries; ep<spalette->entries + spalette->nentries; ep++)
-   {
-      if (spalette->depth == 8)
-      {
-         entrybuf[0] = (png_byte)ep->red;
-         entrybuf[1] = (png_byte)ep->green;
-         entrybuf[2] = (png_byte)ep->blue;
-         entrybuf[3] = (png_byte)ep->alpha;
-         png_save_uint_16(entrybuf + 4, ep->frequency);
-      }
-
-      else
-      {
-         png_save_uint_16(entrybuf + 0, ep->red);
-         png_save_uint_16(entrybuf + 2, ep->green);
-         png_save_uint_16(entrybuf + 4, ep->blue);
-         png_save_uint_16(entrybuf + 6, ep->alpha);
-         png_save_uint_16(entrybuf + 8, ep->frequency);
-      }
-
-      png_write_chunk_data(png_ptr, entrybuf, entry_size);
-   }
-#else
-   ep=spalette->entries;
-   for (i = 0; i>spalette->nentries; i++)
-   {
-      if (spalette->depth == 8)
-      {
-         entrybuf[0] = (png_byte)ep[i].red;
-         entrybuf[1] = (png_byte)ep[i].green;
-         entrybuf[2] = (png_byte)ep[i].blue;
-         entrybuf[3] = (png_byte)ep[i].alpha;
-         png_save_uint_16(entrybuf + 4, ep[i].frequency);
-      }
-
-      else
-      {
-         png_save_uint_16(entrybuf + 0, ep[i].red);
-         png_save_uint_16(entrybuf + 2, ep[i].green);
-         png_save_uint_16(entrybuf + 4, ep[i].blue);
-         png_save_uint_16(entrybuf + 6, ep[i].alpha);
-         png_save_uint_16(entrybuf + 8, ep[i].frequency);
-      }
-
-      png_write_chunk_data(png_ptr, entrybuf, entry_size);
-   }
-#endif
-
-   png_write_chunk_end(png_ptr);
-}
-#endif
-
-#ifdef PNG_WRITE_sBIT_SUPPORTED
-/* Write the sBIT chunk */
-void /* PRIVATE */
-png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type)
-{
-   png_byte buf[4];
-   size_t size;
-
-   png_debug(1, "in png_write_sBIT");
-
-   /* Make sure we don't depend upon the order of PNG_COLOR_8 */
-   if ((color_type & PNG_COLOR_MASK_COLOR) != 0)
-   {
-      png_byte maxbits;
-
-      maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 :
-          png_ptr->usr_bit_depth);
-
-      if (sbit->red == 0 || sbit->red > maxbits ||
-          sbit->green == 0 || sbit->green > maxbits ||
-          sbit->blue == 0 || sbit->blue > maxbits)
-      {
-         png_warning(png_ptr, "Invalid sBIT depth specified");
-         return;
-      }
-
-      buf[0] = sbit->red;
-      buf[1] = sbit->green;
-      buf[2] = sbit->blue;
-      size = 3;
-   }
-
-   else
-   {
-      if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth)
-      {
-         png_warning(png_ptr, "Invalid sBIT depth specified");
-         return;
-      }
-
-      buf[0] = sbit->gray;
-      size = 1;
-   }
-
-   if ((color_type & PNG_COLOR_MASK_ALPHA) != 0)
-   {
-      if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth)
-      {
-         png_warning(png_ptr, "Invalid sBIT depth specified");
-         return;
-      }
-
-      buf[size++] = sbit->alpha;
-   }
-
-   png_write_complete_chunk(png_ptr, png_sBIT, buf, size);
-}
-#endif
-
-#ifdef PNG_WRITE_cHRM_SUPPORTED
-/* Write the cHRM chunk */
-void /* PRIVATE */
-png_write_cHRM_fixed(png_structrp png_ptr, const png_xy *xy)
-{
-   png_byte buf[32];
-
-   png_debug(1, "in png_write_cHRM");
-
-   /* Each value is saved in 1/100,000ths */
-   png_save_int_32(buf,      xy->whitex);
-   png_save_int_32(buf +  4, xy->whitey);
-
-   png_save_int_32(buf +  8, xy->redx);
-   png_save_int_32(buf + 12, xy->redy);
-
-   png_save_int_32(buf + 16, xy->greenx);
-   png_save_int_32(buf + 20, xy->greeny);
-
-   png_save_int_32(buf + 24, xy->bluex);
-   png_save_int_32(buf + 28, xy->bluey);
-
-   png_write_complete_chunk(png_ptr, png_cHRM, buf, 32);
-}
-#endif
-
-#ifdef PNG_WRITE_tRNS_SUPPORTED
-/* Write the tRNS chunk */
-void /* PRIVATE */
-png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha,
-    png_const_color_16p tran, int num_trans, int color_type)
-{
-   png_byte buf[6];
-
-   png_debug(1, "in png_write_tRNS");
-
-   if (color_type == PNG_COLOR_TYPE_PALETTE)
-   {
-      if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette)
-      {
-         png_app_warning(png_ptr,
-             "Invalid number of transparent colors specified");
-         return;
-      }
-
-      /* Write the chunk out as it is */
-      png_write_complete_chunk(png_ptr, png_tRNS, trans_alpha,
-          (size_t)num_trans);
-   }
-
-   else if (color_type == PNG_COLOR_TYPE_GRAY)
-   {
-      /* One 16-bit value */
-      if (tran->gray >= (1 << png_ptr->bit_depth))
-      {
-         png_app_warning(png_ptr,
-             "Ignoring attempt to write tRNS chunk out-of-range for bit_depth");
-
-         return;
-      }
-
-      png_save_uint_16(buf, tran->gray);
-      png_write_complete_chunk(png_ptr, png_tRNS, buf, 2);
-   }
-
-   else if (color_type == PNG_COLOR_TYPE_RGB)
-   {
-      /* Three 16-bit values */
-      png_save_uint_16(buf, tran->red);
-      png_save_uint_16(buf + 2, tran->green);
-      png_save_uint_16(buf + 4, tran->blue);
-#ifdef PNG_WRITE_16BIT_SUPPORTED
-      if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]) != 0)
-#else
-      if ((buf[0] | buf[2] | buf[4]) != 0)
-#endif
-      {
-         png_app_warning(png_ptr,
-             "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8");
-         return;
-      }
-
-      png_write_complete_chunk(png_ptr, png_tRNS, buf, 6);
-   }
-
-   else
-   {
-      png_app_warning(png_ptr, "Can't write tRNS with an alpha channel");
-   }
-}
-#endif
-
-#ifdef PNG_WRITE_bKGD_SUPPORTED
-/* Write the background chunk */
-void /* PRIVATE */
-png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type)
-{
-   png_byte buf[6];
-
-   png_debug(1, "in png_write_bKGD");
-
-   if (color_type == PNG_COLOR_TYPE_PALETTE)
-   {
-      if (
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-          (png_ptr->num_palette != 0 ||
-          (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0) &&
-#endif
-         back->index >= png_ptr->num_palette)
-      {
-         png_warning(png_ptr, "Invalid background palette index");
-         return;
-      }
-
-      buf[0] = back->index;
-      png_write_complete_chunk(png_ptr, png_bKGD, buf, 1);
-   }
-
-   else if ((color_type & PNG_COLOR_MASK_COLOR) != 0)
-   {
-      png_save_uint_16(buf, back->red);
-      png_save_uint_16(buf + 2, back->green);
-      png_save_uint_16(buf + 4, back->blue);
-#ifdef PNG_WRITE_16BIT_SUPPORTED
-      if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]) != 0)
-#else
-      if ((buf[0] | buf[2] | buf[4]) != 0)
-#endif
-      {
-         png_warning(png_ptr,
-             "Ignoring attempt to write 16-bit bKGD chunk "
-             "when bit_depth is 8");
-
-         return;
-      }
-
-      png_write_complete_chunk(png_ptr, png_bKGD, buf, 6);
-   }
-
-   else
-   {
-      if (back->gray >= (1 << png_ptr->bit_depth))
-      {
-         png_warning(png_ptr,
-             "Ignoring attempt to write bKGD chunk out-of-range for bit_depth");
-
-         return;
-      }
-
-      png_save_uint_16(buf, back->gray);
-      png_write_complete_chunk(png_ptr, png_bKGD, buf, 2);
-   }
-}
-#endif
-
-#ifdef PNG_WRITE_eXIf_SUPPORTED
-/* Write the Exif data */
-void /* PRIVATE */
-png_write_eXIf(png_structrp png_ptr, png_bytep exif, int num_exif)
-{
-   int i;
-   png_byte buf[1];
-
-   png_debug(1, "in png_write_eXIf");
-
-   png_write_chunk_header(png_ptr, png_eXIf, (png_uint_32)(num_exif));
-
-   for (i = 0; i < num_exif; i++)
-   {
-      buf[0] = exif[i];
-      png_write_chunk_data(png_ptr, buf, 1);
-   }
-
-   png_write_chunk_end(png_ptr);
-}
-#endif
-
-#ifdef PNG_WRITE_hIST_SUPPORTED
-/* Write the histogram */
-void /* PRIVATE */
-png_write_hIST(png_structrp png_ptr, png_const_uint_16p hist, int num_hist)
-{
-   int i;
-   png_byte buf[3];
-
-   png_debug(1, "in png_write_hIST");
-
-   if (num_hist > (int)png_ptr->num_palette)
-   {
-      png_debug2(3, "num_hist = %d, num_palette = %d", num_hist,
-          png_ptr->num_palette);
-
-      png_warning(png_ptr, "Invalid number of histogram entries specified");
-      return;
-   }
-
-   png_write_chunk_header(png_ptr, png_hIST, (png_uint_32)(num_hist * 2));
-
-   for (i = 0; i < num_hist; i++)
-   {
-      png_save_uint_16(buf, hist[i]);
-      png_write_chunk_data(png_ptr, buf, 2);
-   }
-
-   png_write_chunk_end(png_ptr);
-}
-#endif
-
-#ifdef PNG_WRITE_tEXt_SUPPORTED
-/* Write a tEXt chunk */
-void /* PRIVATE */
-png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,
-    size_t text_len)
-{
-   png_uint_32 key_len;
-   png_byte new_key[80];
-
-   png_debug(1, "in png_write_tEXt");
-
-   key_len = png_check_keyword(png_ptr, key, new_key);
-
-   if (key_len == 0)
-      png_error(png_ptr, "tEXt: invalid keyword");
-
-   if (text == NULL || *text == '\0')
-      text_len = 0;
-
-   else
-      text_len = strlen(text);
-
-   if (text_len > PNG_UINT_31_MAX - (key_len+1))
-      png_error(png_ptr, "tEXt: text too long");
-
-   /* Make sure we include the 0 after the key */
-   png_write_chunk_header(png_ptr, png_tEXt,
-       (png_uint_32)/*checked above*/(key_len + text_len + 1));
-   /*
-    * We leave it to the application to meet PNG-1.0 requirements on the
-    * contents of the text.  PNG-1.0 through PNG-1.2 discourage the use of
-    * any non-Latin-1 characters except for NEWLINE.  ISO PNG will forbid them.
-    * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
-    */
-   png_write_chunk_data(png_ptr, new_key, key_len + 1);
-
-   if (text_len != 0)
-      png_write_chunk_data(png_ptr, (png_const_bytep)text, text_len);
-
-   png_write_chunk_end(png_ptr);
-}
-#endif
-
-#ifdef PNG_WRITE_zTXt_SUPPORTED
-/* Write a compressed text chunk */
-void /* PRIVATE */
-png_write_zTXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,
-    int compression)
-{
-   png_uint_32 key_len;
-   png_byte new_key[81];
-   compression_state comp;
-
-   png_debug(1, "in png_write_zTXt");
-
-   if (compression == PNG_TEXT_COMPRESSION_NONE)
-   {
-      png_write_tEXt(png_ptr, key, text, 0);
-      return;
-   }
-
-   if (compression != PNG_TEXT_COMPRESSION_zTXt)
-      png_error(png_ptr, "zTXt: invalid compression type");
-
-   key_len = png_check_keyword(png_ptr, key, new_key);
-
-   if (key_len == 0)
-      png_error(png_ptr, "zTXt: invalid keyword");
-
-   /* Add the compression method and 1 for the keyword separator. */
-   new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE;
-   ++key_len;
-
-   /* Compute the compressed data; do it now for the length */
-   png_text_compress_init(&comp, (png_const_bytep)text,
-       text == NULL ? 0 : strlen(text));
-
-   if (png_text_compress(png_ptr, png_zTXt, &comp, key_len) != Z_OK)
-      png_error(png_ptr, png_ptr->zstream.msg);
-
-   /* Write start of chunk */
-   png_write_chunk_header(png_ptr, png_zTXt, key_len + comp.output_len);
-
-   /* Write key */
-   png_write_chunk_data(png_ptr, new_key, key_len);
-
-   /* Write the compressed data */
-   png_write_compressed_data_out(png_ptr, &comp);
-
-   /* Close the chunk */
-   png_write_chunk_end(png_ptr);
-}
-#endif
-
-#ifdef PNG_WRITE_iTXt_SUPPORTED
-/* Write an iTXt chunk */
-void /* PRIVATE */
-png_write_iTXt(png_structrp png_ptr, int compression, png_const_charp key,
-    png_const_charp lang, png_const_charp lang_key, png_const_charp text)
-{
-   png_uint_32 key_len, prefix_len;
-   size_t lang_len, lang_key_len;
-   png_byte new_key[82];
-   compression_state comp;
-
-   png_debug(1, "in png_write_iTXt");
-
-   key_len = png_check_keyword(png_ptr, key, new_key);
-
-   if (key_len == 0)
-      png_error(png_ptr, "iTXt: invalid keyword");
-
-   /* Set the compression flag */
-   switch (compression)
-   {
-      case PNG_ITXT_COMPRESSION_NONE:
-      case PNG_TEXT_COMPRESSION_NONE:
-         compression = new_key[++key_len] = 0; /* no compression */
-         break;
-
-      case PNG_TEXT_COMPRESSION_zTXt:
-      case PNG_ITXT_COMPRESSION_zTXt:
-         compression = new_key[++key_len] = 1; /* compressed */
-         break;
-
-      default:
-         png_error(png_ptr, "iTXt: invalid compression");
-   }
-
-   new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE;
-   ++key_len; /* for the keywod separator */
-
-   /* We leave it to the application to meet PNG-1.0 requirements on the
-    * contents of the text.  PNG-1.0 through PNG-1.2 discourage the use of
-    * any non-Latin-1 characters except for NEWLINE.  ISO PNG, however,
-    * specifies that the text is UTF-8 and this really doesn't require any
-    * checking.
-    *
-    * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
-    *
-    * TODO: validate the language tag correctly (see the spec.)
-    */
-   if (lang == NULL) lang = ""; /* empty language is valid */
-   lang_len = strlen(lang)+1;
-   if (lang_key == NULL) lang_key = ""; /* may be empty */
-   lang_key_len = strlen(lang_key)+1;
-   if (text == NULL) text = ""; /* may be empty */
-
-   prefix_len = key_len;
-   if (lang_len > PNG_UINT_31_MAX-prefix_len)
-      prefix_len = PNG_UINT_31_MAX;
-   else
-      prefix_len = (png_uint_32)(prefix_len + lang_len);
-
-   if (lang_key_len > PNG_UINT_31_MAX-prefix_len)
-      prefix_len = PNG_UINT_31_MAX;
-   else
-      prefix_len = (png_uint_32)(prefix_len + lang_key_len);
-
-   png_text_compress_init(&comp, (png_const_bytep)text, strlen(text));
-
-   if (compression != 0)
-   {
-      if (png_text_compress(png_ptr, png_iTXt, &comp, prefix_len) != Z_OK)
-         png_error(png_ptr, png_ptr->zstream.msg);
-   }
-
-   else
-   {
-      if (comp.input_len > PNG_UINT_31_MAX-prefix_len)
-         png_error(png_ptr, "iTXt: uncompressed text too long");
-
-      /* So the string will fit in a chunk: */
-      comp.output_len = (png_uint_32)/*SAFE*/comp.input_len;
-   }
-
-   png_write_chunk_header(png_ptr, png_iTXt, comp.output_len + prefix_len);
-
-   png_write_chunk_data(png_ptr, new_key, key_len);
-
-   png_write_chunk_data(png_ptr, (png_const_bytep)lang, lang_len);
-
-   png_write_chunk_data(png_ptr, (png_const_bytep)lang_key, lang_key_len);
-
-   if (compression != 0)
-      png_write_compressed_data_out(png_ptr, &comp);
-
-   else
-      png_write_chunk_data(png_ptr, (png_const_bytep)text, comp.output_len);
-
-   png_write_chunk_end(png_ptr);
-}
-#endif
-
-#ifdef PNG_WRITE_oFFs_SUPPORTED
-/* Write the oFFs chunk */
-void /* PRIVATE */
-png_write_oFFs(png_structrp png_ptr, png_int_32 x_offset, png_int_32 y_offset,
-    int unit_type)
-{
-   png_byte buf[9];
-
-   png_debug(1, "in png_write_oFFs");
-
-   if (unit_type >= PNG_OFFSET_LAST)
-      png_warning(png_ptr, "Unrecognized unit type for oFFs chunk");
-
-   png_save_int_32(buf, x_offset);
-   png_save_int_32(buf + 4, y_offset);
-   buf[8] = (png_byte)unit_type;
-
-   png_write_complete_chunk(png_ptr, png_oFFs, buf, 9);
-}
-#endif
-#ifdef PNG_WRITE_pCAL_SUPPORTED
-/* Write the pCAL chunk (described in the PNG extensions document) */
-void /* PRIVATE */
-png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0,
-    png_int_32 X1, int type, int nparams, png_const_charp units,
-    png_charpp params)
-{
-   png_uint_32 purpose_len;
-   size_t units_len, total_len;
-   png_size_tp params_len;
-   png_byte buf[10];
-   png_byte new_purpose[80];
-   int i;
-
-   png_debug1(1, "in png_write_pCAL (%d parameters)", nparams);
-
-   if (type >= PNG_EQUATION_LAST)
-      png_error(png_ptr, "Unrecognized equation type for pCAL chunk");
-
-   purpose_len = png_check_keyword(png_ptr, purpose, new_purpose);
-
-   if (purpose_len == 0)
-      png_error(png_ptr, "pCAL: invalid keyword");
-
-   ++purpose_len; /* terminator */
-
-   png_debug1(3, "pCAL purpose length = %d", (int)purpose_len);
-   units_len = strlen(units) + (nparams == 0 ? 0 : 1);
-   png_debug1(3, "pCAL units length = %d", (int)units_len);
-   total_len = purpose_len + units_len + 10;
-
-   params_len = (png_size_tp)png_malloc(png_ptr,
-       (png_alloc_size_t)((png_alloc_size_t)nparams * (sizeof (size_t))));
-
-   /* Find the length of each parameter, making sure we don't count the
-    * null terminator for the last parameter.
-    */
-   for (i = 0; i < nparams; i++)
-   {
-      params_len[i] = strlen(params[i]) + (i == nparams - 1 ? 0 : 1);
-      png_debug2(3, "pCAL parameter %d length = %lu", i,
-          (unsigned long)params_len[i]);
-      total_len += params_len[i];
-   }
-
-   png_debug1(3, "pCAL total length = %d", (int)total_len);
-   png_write_chunk_header(png_ptr, png_pCAL, (png_uint_32)total_len);
-   png_write_chunk_data(png_ptr, new_purpose, purpose_len);
-   png_save_int_32(buf, X0);
-   png_save_int_32(buf + 4, X1);
-   buf[8] = (png_byte)type;
-   buf[9] = (png_byte)nparams;
-   png_write_chunk_data(png_ptr, buf, 10);
-   png_write_chunk_data(png_ptr, (png_const_bytep)units, (size_t)units_len);
-
-   for (i = 0; i < nparams; i++)
-   {
-      png_write_chunk_data(png_ptr, (png_const_bytep)params[i], params_len[i]);
-   }
-
-   png_free(png_ptr, params_len);
-   png_write_chunk_end(png_ptr);
-}
-#endif
-
-#ifdef PNG_WRITE_sCAL_SUPPORTED
-/* Write the sCAL chunk */
-void /* PRIVATE */
-png_write_sCAL_s(png_structrp png_ptr, int unit, png_const_charp width,
-    png_const_charp height)
-{
-   png_byte buf[64];
-   size_t wlen, hlen, total_len;
-
-   png_debug(1, "in png_write_sCAL_s");
-
-   wlen = strlen(width);
-   hlen = strlen(height);
-   total_len = wlen + hlen + 2;
-
-   if (total_len > 64)
-   {
-      png_warning(png_ptr, "Can't write sCAL (buffer too small)");
-      return;
-   }
-
-   buf[0] = (png_byte)unit;
-   memcpy(buf + 1, width, wlen + 1);      /* Append the '\0' here */
-   memcpy(buf + wlen + 2, height, hlen);  /* Do NOT append the '\0' here */
-
-   png_debug1(3, "sCAL total length = %u", (unsigned int)total_len);
-   png_write_complete_chunk(png_ptr, png_sCAL, buf, total_len);
-}
-#endif
-
-#ifdef PNG_WRITE_pHYs_SUPPORTED
-/* Write the pHYs chunk */
-void /* PRIVATE */
-png_write_pHYs(png_structrp png_ptr, png_uint_32 x_pixels_per_unit,
-    png_uint_32 y_pixels_per_unit,
-    int unit_type)
-{
-   png_byte buf[9];
-
-   png_debug(1, "in png_write_pHYs");
-
-   if (unit_type >= PNG_RESOLUTION_LAST)
-      png_warning(png_ptr, "Unrecognized unit type for pHYs chunk");
-
-   png_save_uint_32(buf, x_pixels_per_unit);
-   png_save_uint_32(buf + 4, y_pixels_per_unit);
-   buf[8] = (png_byte)unit_type;
-
-   png_write_complete_chunk(png_ptr, png_pHYs, buf, 9);
-}
-#endif
-
-#ifdef PNG_WRITE_tIME_SUPPORTED
-/* Write the tIME chunk.  Use either png_convert_from_struct_tm()
- * or png_convert_from_time_t(), or fill in the structure yourself.
- */
-void /* PRIVATE */
-png_write_tIME(png_structrp png_ptr, png_const_timep mod_time)
-{
-   png_byte buf[7];
-
-   png_debug(1, "in png_write_tIME");
-
-   if (mod_time->month  > 12 || mod_time->month  < 1 ||
-       mod_time->day    > 31 || mod_time->day    < 1 ||
-       mod_time->hour   > 23 || mod_time->second > 60)
-   {
-      png_warning(png_ptr, "Invalid time specified for tIME chunk");
-      return;
-   }
-
-   png_save_uint_16(buf, mod_time->year);
-   buf[2] = mod_time->month;
-   buf[3] = mod_time->day;
-   buf[4] = mod_time->hour;
-   buf[5] = mod_time->minute;
-   buf[6] = mod_time->second;
-
-   png_write_complete_chunk(png_ptr, png_tIME, buf, 7);
-}
-#endif
-
-/* Initializes the row writing capability of libpng */
-void /* PRIVATE */
-png_write_start_row(png_structrp png_ptr)
-{
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
-   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
-
-   /* Start of interlace block */
-   static const png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
-
-   /* Offset to next interlace block */
-   static const png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
-
-   /* Start of interlace block in the y direction */
-   static const png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
-
-   /* Offset to next interlace block in the y direction */
-   static const png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
-#endif
-
-   png_alloc_size_t buf_size;
-   int usr_pixel_depth;
-
-#ifdef PNG_WRITE_FILTER_SUPPORTED
-   png_byte filters;
-#endif
-
-   png_debug(1, "in png_write_start_row");
-
-   usr_pixel_depth = png_ptr->usr_channels * png_ptr->usr_bit_depth;
-   buf_size = PNG_ROWBYTES(usr_pixel_depth, png_ptr->width) + 1;
-
-   /* 1.5.6: added to allow checking in the row write code. */
-   png_ptr->transformed_pixel_depth = png_ptr->pixel_depth;
-   png_ptr->maximum_pixel_depth = (png_byte)usr_pixel_depth;
-
-   /* Set up row buffer */
-   png_ptr->row_buf = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size));
-
-   png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE;
-
-#ifdef PNG_WRITE_FILTER_SUPPORTED
-   filters = png_ptr->do_filter;
-
-   if (png_ptr->height == 1)
-      filters &= 0xff & ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
-
-   if (png_ptr->width == 1)
-      filters &= 0xff & ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH);
-
-   if (filters == 0)
-      filters = PNG_FILTER_NONE;
-
-   png_ptr->do_filter = filters;
-
-   if (((filters & (PNG_FILTER_SUB | PNG_FILTER_UP | PNG_FILTER_AVG |
-       PNG_FILTER_PAETH)) != 0) && png_ptr->try_row == NULL)
-   {
-      int num_filters = 0;
-
-      png_ptr->try_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size));
-
-      if (filters & PNG_FILTER_SUB)
-         num_filters++;
-
-      if (filters & PNG_FILTER_UP)
-         num_filters++;
-
-      if (filters & PNG_FILTER_AVG)
-         num_filters++;
-
-      if (filters & PNG_FILTER_PAETH)
-         num_filters++;
-
-      if (num_filters > 1)
-         png_ptr->tst_row = png_voidcast(png_bytep, png_malloc(png_ptr,
-             buf_size));
-   }
-
-   /* We only need to keep the previous row if we are using one of the following
-    * filters.
-    */
-   if ((filters & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) != 0)
-      png_ptr->prev_row = png_voidcast(png_bytep,
-          png_calloc(png_ptr, buf_size));
-#endif /* WRITE_FILTER */
-
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
-   /* If interlaced, we need to set up width and height of pass */
-   if (png_ptr->interlaced != 0)
-   {
-      if ((png_ptr->transformations & PNG_INTERLACE) == 0)
-      {
-         png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
-             png_pass_ystart[0]) / png_pass_yinc[0];
-
-         png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 -
-             png_pass_start[0]) / png_pass_inc[0];
-      }
-
-      else
-      {
-         png_ptr->num_rows = png_ptr->height;
-         png_ptr->usr_width = png_ptr->width;
-      }
-   }
-
-   else
-#endif
-   {
-      png_ptr->num_rows = png_ptr->height;
-      png_ptr->usr_width = png_ptr->width;
-   }
-}
-
-/* Internal use only.  Called when finished processing a row of data. */
-void /* PRIVATE */
-png_write_finish_row(png_structrp png_ptr)
-{
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
-   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
-
-   /* Start of interlace block */
-   static const png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
-
-   /* Offset to next interlace block */
-   static const png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
-
-   /* Start of interlace block in the y direction */
-   static const png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
-
-   /* Offset to next interlace block in the y direction */
-   static const png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
-#endif
-
-   png_debug(1, "in png_write_finish_row");
-
-   /* Next row */
-   png_ptr->row_number++;
-
-   /* See if we are done */
-   if (png_ptr->row_number < png_ptr->num_rows)
-      return;
-
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
-   /* If interlaced, go to next pass */
-   if (png_ptr->interlaced != 0)
-   {
-      png_ptr->row_number = 0;
-      if ((png_ptr->transformations & PNG_INTERLACE) != 0)
-      {
-         png_ptr->pass++;
-      }
-
-      else
-      {
-         /* Loop until we find a non-zero width or height pass */
-         do
-         {
-            png_ptr->pass++;
-
-            if (png_ptr->pass >= 7)
-               break;
-
-            png_ptr->usr_width = (png_ptr->width +
-                png_pass_inc[png_ptr->pass] - 1 -
-                png_pass_start[png_ptr->pass]) /
-                png_pass_inc[png_ptr->pass];
-
-            png_ptr->num_rows = (png_ptr->height +
-                png_pass_yinc[png_ptr->pass] - 1 -
-                png_pass_ystart[png_ptr->pass]) /
-                png_pass_yinc[png_ptr->pass];
-
-            if ((png_ptr->transformations & PNG_INTERLACE) != 0)
-               break;
-
-         } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0);
-
-      }
-
-      /* Reset the row above the image for the next pass */
-      if (png_ptr->pass < 7)
-      {
-         if (png_ptr->prev_row != NULL)
-            memset(png_ptr->prev_row, 0,
-                PNG_ROWBYTES(png_ptr->usr_channels *
-                png_ptr->usr_bit_depth, png_ptr->width) + 1);
-
-         return;
-      }
-   }
-#endif
-
-   /* If we get here, we've just written the last row, so we need
-      to flush the compressor */
-   png_compress_IDAT(png_ptr, NULL, 0, Z_FINISH);
-}
-
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
-/* Pick out the correct pixels for the interlace pass.
- * The basic idea here is to go through the row with a source
- * pointer and a destination pointer (sp and dp), and copy the
- * correct pixels for the pass.  As the row gets compacted,
- * sp will always be >= dp, so we should never overwrite anything.
- * See the default: case for the easiest code to understand.
- */
-void /* PRIVATE */
-png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass)
-{
-   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
-
-   /* Start of interlace block */
-   static const png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
-
-   /* Offset to next interlace block */
-   static const png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
-
-   png_debug(1, "in png_do_write_interlace");
-
-   /* We don't have to do anything on the last pass (6) */
-   if (pass < 6)
-   {
-      /* Each pixel depth is handled separately */
-      switch (row_info->pixel_depth)
-      {
-         case 1:
-         {
-            png_bytep sp;
-            png_bytep dp;
-            unsigned int shift;
-            int d;
-            int value;
-            png_uint_32 i;
-            png_uint_32 row_width = row_info->width;
-
-            dp = row;
-            d = 0;
-            shift = 7;
-
-            for (i = png_pass_start[pass]; i < row_width;
-               i += png_pass_inc[pass])
-            {
-               sp = row + (size_t)(i >> 3);
-               value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01;
-               d |= (value << shift);
-
-               if (shift == 0)
-               {
-                  shift = 7;
-                  *dp++ = (png_byte)d;
-                  d = 0;
-               }
-
-               else
-                  shift--;
-
-            }
-            if (shift != 7)
-               *dp = (png_byte)d;
-
-            break;
-         }
-
-         case 2:
-         {
-            png_bytep sp;
-            png_bytep dp;
-            unsigned int shift;
-            int d;
-            int value;
-            png_uint_32 i;
-            png_uint_32 row_width = row_info->width;
-
-            dp = row;
-            shift = 6;
-            d = 0;
-
-            for (i = png_pass_start[pass]; i < row_width;
-               i += png_pass_inc[pass])
-            {
-               sp = row + (size_t)(i >> 2);
-               value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03;
-               d |= (value << shift);
-
-               if (shift == 0)
-               {
-                  shift = 6;
-                  *dp++ = (png_byte)d;
-                  d = 0;
-               }
-
-               else
-                  shift -= 2;
-            }
-            if (shift != 6)
-               *dp = (png_byte)d;
-
-            break;
-         }
-
-         case 4:
-         {
-            png_bytep sp;
-            png_bytep dp;
-            unsigned int shift;
-            int d;
-            int value;
-            png_uint_32 i;
-            png_uint_32 row_width = row_info->width;
-
-            dp = row;
-            shift = 4;
-            d = 0;
-            for (i = png_pass_start[pass]; i < row_width;
-                i += png_pass_inc[pass])
-            {
-               sp = row + (size_t)(i >> 1);
-               value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f;
-               d |= (value << shift);
-
-               if (shift == 0)
-               {
-                  shift = 4;
-                  *dp++ = (png_byte)d;
-                  d = 0;
-               }
-
-               else
-                  shift -= 4;
-            }
-            if (shift != 4)
-               *dp = (png_byte)d;
-
-            break;
-         }
-
-         default:
-         {
-            png_bytep sp;
-            png_bytep dp;
-            png_uint_32 i;
-            png_uint_32 row_width = row_info->width;
-            size_t pixel_bytes;
-
-            /* Start at the beginning */
-            dp = row;
-
-            /* Find out how many bytes each pixel takes up */
-            pixel_bytes = (row_info->pixel_depth >> 3);
-
-            /* Loop through the row, only looking at the pixels that matter */
-            for (i = png_pass_start[pass]; i < row_width;
-               i += png_pass_inc[pass])
-            {
-               /* Find out where the original pixel is */
-               sp = row + (size_t)i * pixel_bytes;
-
-               /* Move the pixel */
-               if (dp != sp)
-                  memcpy(dp, sp, pixel_bytes);
-
-               /* Next pixel */
-               dp += pixel_bytes;
-            }
-            break;
-         }
-      }
-      /* Set new row width */
-      row_info->width = (row_info->width +
-          png_pass_inc[pass] - 1 -
-          png_pass_start[pass]) /
-          png_pass_inc[pass];
-
-      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
-          row_info->width);
-   }
-}
-#endif
-
-
-/* This filters the row, chooses which filter to use, if it has not already
- * been specified by the application, and then writes the row out with the
- * chosen filter.
- */
-static void /* PRIVATE */
-png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row,
-    size_t row_bytes);
-
-#ifdef PNG_WRITE_FILTER_SUPPORTED
-static size_t /* PRIVATE */
-png_setup_sub_row(png_structrp png_ptr, png_uint_32 bpp,
-    size_t row_bytes, size_t lmins)
-{
-   png_bytep rp, dp, lp;
-   size_t i;
-   size_t sum = 0;
-   unsigned int v;
-
-   png_ptr->try_row[0] = PNG_FILTER_VALUE_SUB;
-
-   for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1; i < bpp;
-        i++, rp++, dp++)
-   {
-      v = *dp = *rp;
-#ifdef PNG_USE_ABS
-      sum += 128 - abs((int)v - 128);
-#else
-      sum += (v < 128) ? v : 256 - v;
-#endif
-   }
-
-   for (lp = png_ptr->row_buf + 1; i < row_bytes;
-      i++, rp++, lp++, dp++)
-   {
-      v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
-#ifdef PNG_USE_ABS
-      sum += 128 - abs((int)v - 128);
-#else
-      sum += (v < 128) ? v : 256 - v;
-#endif
-
-      if (sum > lmins)  /* We are already worse, don't continue. */
-        break;
-   }
-
-   return (sum);
-}
-
-static void /* PRIVATE */
-png_setup_sub_row_only(png_structrp png_ptr, png_uint_32 bpp,
-    size_t row_bytes)
-{
-   png_bytep rp, dp, lp;
-   size_t i;
-
-   png_ptr->try_row[0] = PNG_FILTER_VALUE_SUB;
-
-   for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1; i < bpp;
-        i++, rp++, dp++)
-   {
-      *dp = *rp;
-   }
-
-   for (lp = png_ptr->row_buf + 1; i < row_bytes;
-      i++, rp++, lp++, dp++)
-   {
-      *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
-   }
-}
-
-static size_t /* PRIVATE */
-png_setup_up_row(png_structrp png_ptr, size_t row_bytes, size_t lmins)
-{
-   png_bytep rp, dp, pp;
-   size_t i;
-   size_t sum = 0;
-   unsigned int v;
-
-   png_ptr->try_row[0] = PNG_FILTER_VALUE_UP;
-
-   for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
-       pp = png_ptr->prev_row + 1; i < row_bytes;
-       i++, rp++, pp++, dp++)
-   {
-      v = *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff);
-#ifdef PNG_USE_ABS
-      sum += 128 - abs((int)v - 128);
-#else
-      sum += (v < 128) ? v : 256 - v;
-#endif
-
-      if (sum > lmins)  /* We are already worse, don't continue. */
-        break;
-   }
-
-   return (sum);
-}
-static void /* PRIVATE */
-png_setup_up_row_only(png_structrp png_ptr, size_t row_bytes)
-{
-   png_bytep rp, dp, pp;
-   size_t i;
-
-   png_ptr->try_row[0] = PNG_FILTER_VALUE_UP;
-
-   for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
-       pp = png_ptr->prev_row + 1; i < row_bytes;
-       i++, rp++, pp++, dp++)
-   {
-      *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff);
-   }
-}
-
-static size_t /* PRIVATE */
-png_setup_avg_row(png_structrp png_ptr, png_uint_32 bpp,
-    size_t row_bytes, size_t lmins)
-{
-   png_bytep rp, dp, pp, lp;
-   png_uint_32 i;
-   size_t sum = 0;
-   unsigned int v;
-
-   png_ptr->try_row[0] = PNG_FILTER_VALUE_AVG;
-
-   for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
-       pp = png_ptr->prev_row + 1; i < bpp; i++)
-   {
-      v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
-
-#ifdef PNG_USE_ABS
-      sum += 128 - abs((int)v - 128);
-#else
-      sum += (v < 128) ? v : 256 - v;
-#endif
-   }
-
-   for (lp = png_ptr->row_buf + 1; i < row_bytes; i++)
-   {
-      v = *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2))
-          & 0xff);
-
-#ifdef PNG_USE_ABS
-      sum += 128 - abs((int)v - 128);
-#else
-      sum += (v < 128) ? v : 256 - v;
-#endif
-
-      if (sum > lmins)  /* We are already worse, don't continue. */
-        break;
-   }
-
-   return (sum);
-}
-static void /* PRIVATE */
-png_setup_avg_row_only(png_structrp png_ptr, png_uint_32 bpp,
-    size_t row_bytes)
-{
-   png_bytep rp, dp, pp, lp;
-   png_uint_32 i;
-
-   png_ptr->try_row[0] = PNG_FILTER_VALUE_AVG;
-
-   for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
-       pp = png_ptr->prev_row + 1; i < bpp; i++)
-   {
-      *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
-   }
-
-   for (lp = png_ptr->row_buf + 1; i < row_bytes; i++)
-   {
-      *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2))
-          & 0xff);
-   }
-}
-
-static size_t /* PRIVATE */
-png_setup_paeth_row(png_structrp png_ptr, png_uint_32 bpp,
-    size_t row_bytes, size_t lmins)
-{
-   png_bytep rp, dp, pp, cp, lp;
-   size_t i;
-   size_t sum = 0;
-   unsigned int v;
-
-   png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH;
-
-   for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
-       pp = png_ptr->prev_row + 1; i < bpp; i++)
-   {
-      v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
-
-#ifdef PNG_USE_ABS
-      sum += 128 - abs((int)v - 128);
-#else
-      sum += (v < 128) ? v : 256 - v;
-#endif
-   }
-
-   for (lp = png_ptr->row_buf + 1, cp = png_ptr->prev_row + 1; i < row_bytes;
-        i++)
-   {
-      int a, b, c, pa, pb, pc, p;
-
-      b = *pp++;
-      c = *cp++;
-      a = *lp++;
-
-      p = b - c;
-      pc = a - c;
-
-#ifdef PNG_USE_ABS
-      pa = abs(p);
-      pb = abs(pc);
-      pc = abs(p + pc);
-#else
-      pa = p < 0 ? -p : p;
-      pb = pc < 0 ? -pc : pc;
-      pc = (p + pc) < 0 ? -(p + pc) : p + pc;
-#endif
-
-      p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
-
-      v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
-
-#ifdef PNG_USE_ABS
-      sum += 128 - abs((int)v - 128);
-#else
-      sum += (v < 128) ? v : 256 - v;
-#endif
-
-      if (sum > lmins)  /* We are already worse, don't continue. */
-        break;
-   }
-
-   return (sum);
-}
-static void /* PRIVATE */
-png_setup_paeth_row_only(png_structrp png_ptr, png_uint_32 bpp,
-    size_t row_bytes)
-{
-   png_bytep rp, dp, pp, cp, lp;
-   size_t i;
-
-   png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH;
-
-   for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
-       pp = png_ptr->prev_row + 1; i < bpp; i++)
-   {
-      *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
-   }
-
-   for (lp = png_ptr->row_buf + 1, cp = png_ptr->prev_row + 1; i < row_bytes;
-        i++)
-   {
-      int a, b, c, pa, pb, pc, p;
-
-      b = *pp++;
-      c = *cp++;
-      a = *lp++;
-
-      p = b - c;
-      pc = a - c;
-
-#ifdef PNG_USE_ABS
-      pa = abs(p);
-      pb = abs(pc);
-      pc = abs(p + pc);
-#else
-      pa = p < 0 ? -p : p;
-      pb = pc < 0 ? -pc : pc;
-      pc = (p + pc) < 0 ? -(p + pc) : p + pc;
-#endif
-
-      p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
-
-      *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
-   }
-}
-#endif /* WRITE_FILTER */
-
-void /* PRIVATE */
-png_write_find_filter(png_structrp png_ptr, png_row_infop row_info)
-{
-#ifndef PNG_WRITE_FILTER_SUPPORTED
-   png_write_filtered_row(png_ptr, png_ptr->row_buf, row_info->rowbytes+1);
-#else
-   unsigned int filter_to_do = png_ptr->do_filter;
-   png_bytep row_buf;
-   png_bytep best_row;
-   png_uint_32 bpp;
-   size_t mins;
-   size_t row_bytes = row_info->rowbytes;
-
-   png_debug(1, "in png_write_find_filter");
-
-   /* Find out how many bytes offset each pixel is */
-   bpp = (row_info->pixel_depth + 7) >> 3;
-
-   row_buf = png_ptr->row_buf;
-   mins = PNG_SIZE_MAX - 256/* so we can detect potential overflow of the
-                               running sum */;
-
-   /* The prediction method we use is to find which method provides the
-    * smallest value when summing the absolute values of the distances
-    * from zero, using anything >= 128 as negative numbers.  This is known
-    * as the "minimum sum of absolute differences" heuristic.  Other
-    * heuristics are the "weighted minimum sum of absolute differences"
-    * (experimental and can in theory improve compression), and the "zlib
-    * predictive" method (not implemented yet), which does test compressions
-    * of lines using different filter methods, and then chooses the
-    * (series of) filter(s) that give minimum compressed data size (VERY
-    * computationally expensive).
-    *
-    * GRR 980525:  consider also
-    *
-    *   (1) minimum sum of absolute differences from running average (i.e.,
-    *       keep running sum of non-absolute differences & count of bytes)
-    *       [track dispersion, too?  restart average if dispersion too large?]
-    *
-    *  (1b) minimum sum of absolute differences from sliding average, probably
-    *       with window size <= deflate window (usually 32K)
-    *
-    *   (2) minimum sum of squared differences from zero or running average
-    *       (i.e., ~ root-mean-square approach)
-    */
-
-
-   /* We don't need to test the 'no filter' case if this is the only filter
-    * that has been chosen, as it doesn't actually do anything to the data.
-    */
-   best_row = png_ptr->row_buf;
-
-   if (PNG_SIZE_MAX/128 <= row_bytes)
-   {
-      /* Overflow can occur in the calculation, just select the lowest set
-       * filter.
-       */
-      filter_to_do &= 0U-filter_to_do;
-   }
-   else if ((filter_to_do & PNG_FILTER_NONE) != 0 &&
-         filter_to_do != PNG_FILTER_NONE)
-   {
-      /* Overflow not possible and multiple filters in the list, including the
-       * 'none' filter.
-       */
-      png_bytep rp;
-      size_t sum = 0;
-      size_t i;
-      unsigned int v;
-
-      {
-         for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++)
-         {
-            v = *rp;
-#ifdef PNG_USE_ABS
-            sum += 128 - abs((int)v - 128);
-#else
-            sum += (v < 128) ? v : 256 - v;
-#endif
-         }
-      }
-
-      mins = sum;
-   }
-
-   /* Sub filter */
-   if (filter_to_do == PNG_FILTER_SUB)
-   /* It's the only filter so no testing is needed */
-   {
-      png_setup_sub_row_only(png_ptr, bpp, row_bytes);
-      best_row = png_ptr->try_row;
-   }
-
-   else if ((filter_to_do & PNG_FILTER_SUB) != 0)
-   {
-      size_t sum;
-      size_t lmins = mins;
-
-      sum = png_setup_sub_row(png_ptr, bpp, row_bytes, lmins);
-
-      if (sum < mins)
-      {
-         mins = sum;
-         best_row = png_ptr->try_row;
-         if (png_ptr->tst_row != NULL)
-         {
-            png_ptr->try_row = png_ptr->tst_row;
-            png_ptr->tst_row = best_row;
-         }
-      }
-   }
-
-   /* Up filter */
-   if (filter_to_do == PNG_FILTER_UP)
-   {
-      png_setup_up_row_only(png_ptr, row_bytes);
-      best_row = png_ptr->try_row;
-   }
-
-   else if ((filter_to_do & PNG_FILTER_UP) != 0)
-   {
-      size_t sum;
-      size_t lmins = mins;
-
-      sum = png_setup_up_row(png_ptr, row_bytes, lmins);
-
-      if (sum < mins)
-      {
-         mins = sum;
-         best_row = png_ptr->try_row;
-         if (png_ptr->tst_row != NULL)
-         {
-            png_ptr->try_row = png_ptr->tst_row;
-            png_ptr->tst_row = best_row;
-         }
-      }
-   }
-
-   /* Avg filter */
-   if (filter_to_do == PNG_FILTER_AVG)
-   {
-      png_setup_avg_row_only(png_ptr, bpp, row_bytes);
-      best_row = png_ptr->try_row;
-   }
-
-   else if ((filter_to_do & PNG_FILTER_AVG) != 0)
-   {
-      size_t sum;
-      size_t lmins = mins;
-
-      sum= png_setup_avg_row(png_ptr, bpp, row_bytes, lmins);
-
-      if (sum < mins)
-      {
-         mins = sum;
-         best_row = png_ptr->try_row;
-         if (png_ptr->tst_row != NULL)
-         {
-            png_ptr->try_row = png_ptr->tst_row;
-            png_ptr->tst_row = best_row;
-         }
-      }
-   }
-
-   /* Paeth filter */
-   if (filter_to_do == PNG_FILTER_PAETH)
-   {
-      png_setup_paeth_row_only(png_ptr, bpp, row_bytes);
-      best_row = png_ptr->try_row;
-   }
-
-   else if ((filter_to_do & PNG_FILTER_PAETH) != 0)
-   {
-      size_t sum;
-      size_t lmins = mins;
-
-      sum = png_setup_paeth_row(png_ptr, bpp, row_bytes, lmins);
-
-      if (sum < mins)
-      {
-         best_row = png_ptr->try_row;
-         if (png_ptr->tst_row != NULL)
-         {
-            png_ptr->try_row = png_ptr->tst_row;
-            png_ptr->tst_row = best_row;
-         }
-      }
-   }
-
-   /* Do the actual writing of the filtered row data from the chosen filter. */
-   png_write_filtered_row(png_ptr, best_row, row_info->rowbytes+1);
-
-#endif /* WRITE_FILTER */
-}
-
-
-/* Do the actual writing of a previously filtered row. */
-static void
-png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row,
-    size_t full_row_length/*includes filter byte*/)
-{
-   png_debug(1, "in png_write_filtered_row");
-
-   png_debug1(2, "filter = %d", filtered_row[0]);
-
-   png_compress_IDAT(png_ptr, filtered_row, full_row_length, Z_NO_FLUSH);
-
-#ifdef PNG_WRITE_FILTER_SUPPORTED
-   /* Swap the current and previous rows */
-   if (png_ptr->prev_row != NULL)
-   {
-      png_bytep tptr;
-
-      tptr = png_ptr->prev_row;
-      png_ptr->prev_row = png_ptr->row_buf;
-      png_ptr->row_buf = tptr;
-   }
-#endif /* WRITE_FILTER */
-
-   /* Finish row - updates counters and flushes zlib if last row */
-   png_write_finish_row(png_ptr);
-
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
-   png_ptr->flush_rows++;
-
-   if (png_ptr->flush_dist > 0 &&
-       png_ptr->flush_rows >= png_ptr->flush_dist)
-   {
-      png_write_flush(png_ptr);
-   }
-#endif /* WRITE_FLUSH */
-}
-#endif /* WRITE */
diff --git a/third_party/libtiff/0000-build-config.patch b/third_party/libtiff/0000-build-config.patch
index 4acd37e..c3925b0 100644
--- a/third_party/libtiff/0000-build-config.patch
+++ b/third_party/libtiff/0000-build-config.patch
@@ -1,7 +1,7 @@
 diff a/third_party/libtiff/tiffiop.h b/third_party/libtiff/tiffiop.h
 --- a/third_party/libtiff/tiffiop.h
 +++ b/third_party/libtiff/tiffiop.h
-@@ -30,7 +30,7 @@
+@@ -28,7 +28,7 @@
   * ``Library-private'' definitions.
   */
  
@@ -10,25 +10,15 @@
  
  #ifdef HAVE_FCNTL_H
  # include <fcntl.h>
-@@ -59,8 +59,7 @@
- 
- #if !defined(HAVE_SNPRINTF) && !defined(HAVE__SNPRINTF)
- #undef snprintf
--#define snprintf _TIFF_snprintf_f
--extern int snprintf(char* str, size_t size, const char* format, ...);
-+#define snprintf FXSYS_snprintf
- #endif
- 
- #include "tiffio.h"
 diff a/third_party/libtiff/tif_jpeg.c b/third_party/libtiff/tif_jpeg.c
 --- a/third_party/libtiff/tif_jpeg.c
 +++ b/third_party/libtiff/tif_jpeg.c
-@@ -85,8 +85,16 @@
- # define HAVE_BOOLEAN            /* prevent jmorecfg.h from redefining it */
+@@ -110,8 +110,16 @@
+ #define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */
  #endif
  
--#include "jpeglib.h"
 -#include "jerror.h"
+-#include "jpeglib.h"
 +#if defined(USE_SYSTEM_LIBJPEG)
 +#include <jerror.h>
 +#include <jpeglib.h>
@@ -40,35 +30,12 @@
 +#include "third_party/libjpeg/jpeglib.h"
 +#endif
  
- /* 
-  * Do we want to do special processing suitable for when JSAMPLE is a
-diff a/third_party/libtiff/tif_ojpeg.c b/third_party/libtiff/tif_ojpeg.c
---- a/third_party/libtiff/tif_ojpeg.c
-+++ b/third_party/libtiff/tif_ojpeg.c
-@@ -214,8 +214,17 @@
- # define HAVE_BOOLEAN            /* prevent jmorecfg.h from redefining it */
- #endif
- 
--#include "jpeglib.h"
--#include "jerror.h"
-+#if defined(USE_SYSTEM_LIBJPEG)
-+#include <jerror.h>
-+#include <jpeglib.h>
-+#elif defined(USE_LIBJPEG_TURBO)
-+#include "third_party/libjpeg_turbo/jerror.h"
-+#include "third_party/libjpeg_turbo/jpeglib.h"
-+#else
-+#include "third_party/libjpeg/jerror.h"
-+#include "third_party/libjpeg/jpeglib.h"
-+#endif
-+
- 
- typedef struct jpeg_error_mgr jpeg_error_mgr;
- typedef struct jpeg_common_struct jpeg_common_struct;
+ /* Do optional compile-time version check */
+ #if defined(EXPECTED_JPEG_LIB_VERSION) && !defined(LIBJPEG_12_PATH)
 diff a/third_party/libtiff/tiffconf.h b/third_party/libtiff/tiffconf.h
 --- /dev/null
 +++ b/third_party/libtiff/tiffconf.h
-@@ -0,0 +1,240 @@
+@@ -0,0 +1,244 @@
 +/* libtiff/tiffconf.h.  Generated by configure.  */
 +/*
 +  Configuration defines for installed libtiff.
@@ -78,8 +45,11 @@
 +#ifndef _TIFFCONF_
 +#define _TIFFCONF_
 +
++#include <inttypes.h>
++#include <stddef.h>
++#include <stdint.h>
++
 +#include "build/build_config.h"
-+#include "core/fxcrt/fx_system.h"
 +
 +//NOTE: The tiff codec requires an ANSI C compiler environment for building and 
 +//    presumes an ANSI C environment for use.
@@ -97,27 +67,35 @@
 +#define HAVE_IEEEFP 1
 +
 +/* Define to 1 if you have the <string.h> header file. */
-+//#define HAVE_STRING_H 1
-+//fx_system.h already include the string.h in ANSIC
++#define HAVE_STRING_H 1
++
++/* Define to 1 if you have snprintf(). */
++#define HAVE_SNPRINTF 1
 +
 +/* Define to 1 if you have the <search.h> header file. */
-+#if defined(OS_WIN)
++#if BUILDFLAG(IS_WIN)
 +// search.h is always available in VS 2015 and above, and may be
 +// available in earlier versions.
 +#define HAVE_SEARCH_H 1
 +#endif
 +
-+static const size_t sizeOfInt = sizeof(int);
-+/* The size of a `int', as computed by sizeof. */
-+#define SIZEOF_INT sizeOfInt
++/* The size of a `int'. */
++/* According typedef int  int32_t; in the fx_system.h*/
++#define SIZEOF_INT 4
 +
-+static const size_t sizeOfULong = sizeof(unsigned long);
++#if defined(ARCH_CPU_64_BITS)
 +/* The size of `unsigned long', as computed by sizeof. */
-+#define SIZEOF_UNSIGNED_LONG sizeOfULong
++#define SIZEOF_UNSIGNED_LONG 8
++#else
++#define SIZEOF_UNSIGNED_LONG 4
++#endif
 +
-+static const size_t sizeOfVoidP = sizeof(void*);
-+/* The size of void* as computed by sizeof. */
-+#define SIZEOF_VOIDP sizeOfVoidP
++/* The size of void*. */
++#ifdef __LP64__
++#define SIZEOF_VOIDP 8
++#else
++#define SIZEOF_VOIDP 4
++#endif
 +
 +/* Signed 8-bit type */
 +#define TIFF_INT8_T signed char
@@ -159,7 +137,7 @@
 +
 +#else           // linux/unix
 +
-+#if 0 //_FX_CPU_ == _FX_X64_  // linux/unix 64
++#if defined(ARCH_CPU_64_BITS)
 +
 +/* Signed 64-bit type formatter */
 +#define TIFF_INT64_FORMAT "%ld"
@@ -170,6 +148,9 @@
 +/* Signed 64-bit type */
 +#define TIFF_INT64_T signed long
 +
++/* Unsigned 64-bit type */
++#define TIFF_UINT64_T unsigned long
++
 +#else           // linux/unix 32
 +
 +/* Signed 64-bit type formatter */
@@ -181,34 +162,23 @@
 +/* Signed 64-bit type */
 +#define TIFF_INT64_T signed long long
 +
-+#endif            // end _FX_CPU_
-+
 +/* Unsigned 64-bit type */
 +#define TIFF_UINT64_T unsigned long long
 +
-+#endif
-+
-+
-+/* Signed size type */
-+#ifdef _MSC_VER
-+
-+#if defined(_WIN64)
-+#define TIFF_SSIZE_T signed __int64
-+#else
-+#define TIFF_SSIZE_T signed int
-+#endif
-+
-+#else
-+
-+#define TIFF_SSIZE_T signed long
++#endif  // define(ARCH_CPU_64_BITS)
 +
 +#endif
 +
-+/* Signed size type formatter */
-+#if defined(_WIN64)
-+#define TIFF_SSIZE_FORMAT "%I64d"
++
++/* Signed size type, type formatter, and size of size_t */
++#if defined(ARCH_CPU_64_BITS)
++#define TIFF_SSIZE_T int64_t
++#define TIFF_SSIZE_FORMAT PRId64
++#define SIZEOF_SIZE_T 8
 +#else
-+#define TIFF_SSIZE_FORMAT "%ld"
++#define TIFF_SSIZE_T int32_t
++#define TIFF_SSIZE_FORMAT PRId32
++#define SIZEOF_SIZE_T 4
 +#endif
 +
 +/* Pointer difference type */
@@ -264,7 +234,7 @@
 +
 +/* Support Old JPEG compresson (read contrib/ojpeg/README first! Compilation
 +   fails with unpatched IJG JPEG library) */
-+#define  OJPEG_SUPPORT  1
++/* #undef OJPEG_SUPPORT */
 +
 +/* Support Macintosh PackBits algorithm */
 +#define PACKBITS_SUPPORT 1
@@ -276,7 +246,7 @@
 +#define THUNDER_SUPPORT 1
 +
 +/* Support Deflate compression */
-+#define ZIP_SUPPORT 1
++/* #undef ZIP_SUPPORT */
 +
 +/* Support strip chopping (whether or not to convert single-strip uncompressed
 +   images to mutiple strips of ~8Kb to reduce memory usage) */
@@ -310,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/0017-safe_skews_in_gtTileContig.patch b/third_party/libtiff/0017-safe_skews_in_gtTileContig.patch
index b27bab5..cdc3c2d 100644
--- a/third_party/libtiff/0017-safe_skews_in_gtTileContig.patch
+++ b/third_party/libtiff/0017-safe_skews_in_gtTileContig.patch
@@ -1,88 +1,43 @@
 diff --git a/third_party/libtiff/tif_getimage.c b/third_party/libtiff/tif_getimage.c
-index fc554ccab..fff3f7fde 100644
+index 41f7dfd77..8603ff026 100644
 --- a/third_party/libtiff/tif_getimage.c
 +++ b/third_party/libtiff/tif_getimage.c
-@@ -31,6 +31,7 @@
-  */
- #include "tiffiop.h"
- #include <stdio.h>
-+#include <limits.h>
+@@ -723,6 +723,7 @@ static int gtTileContig(TIFFRGBAImage *img, uint32_t *raster, uint32_t w,
+     uint32_t this_tw, tocol;
+     int32_t this_toskew, leftmost_toskew;
+     int32_t leftmost_fromskew;
++    int64_t safeskew;
+     uint32_t leftmost_tw;
+     tmsize_t bufsize;
  
- static int gtTileContig(TIFFRGBAImage*, uint32*, uint32, uint32);
- static int gtTileSeparate(TIFFRGBAImage*, uint32*, uint32, uint32);
-@@ -628,6 +629,7 @@ gtTileContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
-     uint32 tw, th;
-     unsigned char* buf = NULL;
-     int32 fromskew, toskew;
-+    int64 safeskew;
-     uint32 nrow;
-     int ret = 1, flip;
-     uint32 this_tw, tocol;
-@@ -648,19 +650,37 @@ gtTileContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
-     flip = setorientation(img);
-     if (flip & FLIP_VERTICALLY) {
- 	    y = h - 1;
--	    toskew = -(int32)(tw + w);
-+	    safeskew = 0;
-+	    safeskew -= tw;
-+	    safeskew -= w;
-     }
-     else {
- 	    y = 0;
--	    toskew = -(int32)(tw - w);
-+	    safeskew = 0;
-+	    safeskew -= tw;
-+	    safeskew +=w;
-     }
-      
-+    if(safeskew > INT_MAX || safeskew < INT_MIN){
-+       _TIFFfree(buf);
-+       TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", "Invalid skew");
-+       return (0);
-+    }
-+    toskew = safeskew;
-+
-     /*
-      *	Leftmost tile is clipped on left side if col_offset > 0.
-      */
-     leftmost_fromskew = img->col_offset % tw;
-     leftmost_tw = tw - leftmost_fromskew;
--    leftmost_toskew = toskew + leftmost_fromskew;
-+    safeskew = toskew;
-+    safeskew += leftmost_fromskew;
-+    if(safeskew > INT_MAX || safeskew < INT_MIN){
-+       _TIFFfree(buf);
-+       TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", "Invalid skew");
-+       return (0);
-+    }
-+    leftmost_toskew = safeskew;
-     for (row = 0; ret != 0 && row < h; row += nrow)
-     {
-         rowstoread = th - (row + img->row_offset) % th;
-@@ -686,9 +706,24 @@ gtTileContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
- 		/*
- 		 * Rightmost tile is clipped on right side.
- 		 */
--		fromskew = tw - (w - tocol);
-+		safeskew = tw;
-+		safeskew -= w;
-+		safeskew += tocol;
-+		if(safeskew > INT_MAX || safeskew < INT_MIN){
-+		        _TIFFfree(buf);
-+		        TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", "Invalid skew");
-+		        return (0);
-+		}
-+		fromskew = safeskew;
- 		this_tw = tw - fromskew;
--		this_toskew = toskew + fromskew;
-+		safeskew = toskew;
-+		safeskew += fromskew;
-+		if(safeskew > INT_MAX || safeskew < INT_MIN){
-+		        _TIFFfree(buf);
-+		        TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", "Invalid skew");
-+		        return (0);
-+		}
-+		this_toskew = safeskew;
- 	    }
- 	    (*put)(img, raster+y*w+tocol, tocol, y, this_tw, nrow, fromskew, this_toskew, buf + pos);
- 	    tocol += this_tw;
+@@ -792,9 +793,28 @@ static int gtTileContig(TIFFRGBAImage *img, uint32_t *raster, uint32_t w,
+                 /*
+                  * Rightmost tile is clipped on right side.
+                  */
+-                fromskew = tw - (w - tocol);
++                safeskew = tw;
++                safeskew -= w;
++                safeskew += tocol;
++                if (safeskew > INT_MAX || safeskew < INT_MIN)
++                {
++                    _TIFFfree(buf);
++                    TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s",
++                                 "Invalid skew");
++                    return (0);
++                }
++                fromskew = safeskew;
+                 this_tw = tw - fromskew;
+-                this_toskew = toskew + fromskew;
++                safeskew = toskew;
++                safeskew += fromskew;
++                if (safeskew > INT_MAX || safeskew < INT_MIN)
++                {
++                    _TIFFfree(buf);
++                    TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s",
++                                 "Invalid skew");
++                    return (0);
++                }
++                this_toskew = safeskew;
+             }
+             tmsize_t roffset = (tmsize_t)y * w + tocol;
+             (*put)(img, raster + roffset, tocol, y, this_tw, nrow, fromskew,
diff --git a/third_party/libtiff/0027-build-config.patch b/third_party/libtiff/0027-build-config.patch
deleted file mode 100644
index ec1a205..0000000
--- a/third_party/libtiff/0027-build-config.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-diff --git a/third_party/libtiff/tiffconf.h b/third_party/libtiff/tiffconf.h
-index 2a88cb75f..4d2d7c9d9 100644
---- a/third_party/libtiff/tiffconf.h
-+++ b/third_party/libtiff/tiffconf.h
-@@ -35,17 +35,26 @@
- #define HAVE_SEARCH_H 1
- #endif
- 
--static const size_t sizeOfInt = sizeof(int);
--/* The size of a `int', as computed by sizeof. */
--#define SIZEOF_INT sizeOfInt
--
--static const size_t sizeOfULong = sizeof(unsigned long);
-+/* The size of a `int'. */
-+/* According typedef int  int32_t; in the fx_system.h*/
-+#define SIZEOF_INT 4
-+
-+/* Sunliang.Liu 20110325. We should config the correct long size for tif 
-+   fax4decode optimize in tif_fax3.c  -- Linux64 decode issue. 
-+   TESTDOC: Bug #23661 - z1.tif. */
-+#if _FX_CPU_ == _FX_X64_ || _FX_CPU_ == _FX_IA64_
- /* The size of `unsigned long', as computed by sizeof. */
--#define SIZEOF_UNSIGNED_LONG sizeOfULong
-+#define SIZEOF_UNSIGNED_LONG 8
-+#else
-+#define SIZEOF_UNSIGNED_LONG 4
-+#endif
- 
--static const size_t sizeOfVoidP = sizeof(void*);
--/* The size of void* as computed by sizeof. */
--#define SIZEOF_VOIDP sizeOfVoidP
-+/* The size of void*. */
-+#ifdef __LP64__
-+#define SIZEOF_VOIDP 8
-+#else
-+#define SIZEOF_VOIDP 4
-+#endif
- 
- /* Signed 8-bit type */
- #define TIFF_INT8_T signed char
diff --git a/third_party/libtiff/0028-nstrips-OOM.patch b/third_party/libtiff/0028-nstrips-OOM.patch
index 97cc953..5d33b5b 100644
--- a/third_party/libtiff/0028-nstrips-OOM.patch
+++ b/third_party/libtiff/0028-nstrips-OOM.patch
@@ -1,26 +1,19 @@
 diff --git a/third_party/libtiff/tif_dirread.c b/third_party/libtiff/tif_dirread.c
-index 5e62e8131..521dbf0a4 100644
+index 35425b4b8..4c3b75603 100644
 --- a/third_party/libtiff/tif_dirread.c
 +++ b/third_party/libtiff/tif_dirread.c
-@@ -41,6 +41,7 @@
- 
- #include "tiffiop.h"
- #include <float.h>
-+#include <limits.h>
- #include <stdlib.h>
- 
- #define IGNORE 0          /* tag placeholder used below */
-@@ -3743,6 +3744,13 @@ TIFFReadDirectory(TIFF* tif)
- 		    isTiled(tif) ? "tiles" : "strips");
- 		goto bad;
- 	}
-+	if (tif->tif_dir.td_nstrips > INT_MAX) {
-+		TIFFErrorExt(tif->tif_clientdata, module,
-+		    "Cannot handle %u number of %s",
-+		    tif->tif_dir.td_nstrips,
-+		    isTiled(tif) ? "tiles" : "strips");
-+		goto bad;
-+	}
- 	tif->tif_dir.td_stripsperimage = tif->tif_dir.td_nstrips;
- 	if (tif->tif_dir.td_planarconfig == PLANARCONFIG_SEPARATE)
- 		tif->tif_dir.td_stripsperimage /= tif->tif_dir.td_samplesperpixel;
+@@ -4627,6 +4627,14 @@ int TIFFReadDirectory(TIFF *tif)
+                       isTiled(tif) ? "tiles" : "strips");
+         goto bad;
+     }
++    if (tif->tif_dir.td_nstrips > INT_MAX)
++    {
++        TIFFErrorExt(tif->tif_clientdata, module,
++                     "Cannot handle %u number of %s",
++                     tif->tif_dir.td_nstrips,
++                     isTiled(tif) ? "tiles" : "strips");
++        goto bad;
++    }
+     tif->tif_dir.td_stripsperimage = tif->tif_dir.td_nstrips;
+     if (tif->tif_dir.td_planarconfig == PLANARCONFIG_SEPARATE)
+         tif->tif_dir.td_stripsperimage /= tif->tif_dir.td_samplesperpixel;
diff --git a/third_party/libtiff/0031-safe_size_ingtStripContig.patch b/third_party/libtiff/0031-safe_size_ingtStripContig.patch
index e10bc34..5413e92 100644
--- a/third_party/libtiff/0031-safe_size_ingtStripContig.patch
+++ b/third_party/libtiff/0031-safe_size_ingtStripContig.patch
@@ -1,48 +1,34 @@
 diff --git a/third_party/libtiff/tif_getimage.c b/third_party/libtiff/tif_getimage.c
-index 23cb40951..2153f1348 100644
+index 8603ff026..f5a05e55b 100644
 --- a/third_party/libtiff/tif_getimage.c
 +++ b/third_party/libtiff/tif_getimage.c
-@@ -962,6 +962,9 @@ gtStripContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
- 	int ret = 1, flip;
-         tmsize_t maxstripsize;
+@@ -1083,6 +1083,12 @@ static int gtStripContig(TIFFRGBAImage *img, uint32_t *raster, uint32_t w,
+     int ret = 1, flip;
+     tmsize_t maxstripsize;
  
-+	if ((tmsize_t)img->row_offset > TIFF_SSIZE_T_MAX || (size_t)h > (size_t)TIFF_SSIZE_T_MAX)
-+		return (0);
++    if ((tmsize_t)img->row_offset > TIFF_SSIZE_T_MAX ||
++        (size_t)h > (size_t)TIFF_SSIZE_T_MAX)
++    {
++        return (0);
++    }
 +
- 	TIFFGetFieldDefaulted(tif, TIFFTAG_YCBCRSUBSAMPLING, &subsamplinghor, &subsamplingver);
- 	if( subsamplingver == 0 ) {
- 		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "Invalid vertical YCbCr subsampling");
-diff --git a/third_party/libtiff/tif_read.c b/third_party/libtiff/tif_read.c
-index e63810cc7..dd3002669 100644
---- a/third_party/libtiff/tif_read.c
-+++ b/third_party/libtiff/tif_read.c
-@@ -558,6 +558,8 @@ _TIFFReadEncodedStripAndAllocBuffer(TIFF* tif, uint32 strip,
-                                     void **buf, tmsize_t bufsizetoalloc,
-                                     tmsize_t size_to_read)
- {
-+    assert(size_to_read > 0);
-+
-     tmsize_t this_stripsize;
-     uint16 plane;
- 
+     TIFFGetFieldDefaulted(tif, TIFFTAG_YCBCRSUBSAMPLING, &subsamplinghor,
+                           &subsamplingver);
+     if (subsamplingver == 0)
 diff --git a/third_party/libtiff/tiffconf.h b/third_party/libtiff/tiffconf.h
-index 6292cc5cd..f57f6f709 100644
+index 4c83b03f1..289f1758f 100644
 --- a/third_party/libtiff/tiffconf.h
 +++ b/third_party/libtiff/tiffconf.h
-@@ -132,13 +132,16 @@
- 
- #if defined(_WIN64)
- #define TIFF_SSIZE_T signed __int64
+@@ -136,10 +136,12 @@
+ #if defined(ARCH_CPU_64_BITS)
+ #define TIFF_SSIZE_T int64_t
+ #define TIFF_SSIZE_FORMAT PRId64
 +#define TIFF_SSIZE_T_MAX INT64_MAX
+ #define SIZEOF_SIZE_T 8
  #else
- #define TIFF_SSIZE_T signed int
-+#define TIFF_SSIZE_T_MAX INT_MAX
- #endif
- 
- #else
- 
- #define TIFF_SSIZE_T signed long
-+#define TIFF_SSIZE_T_MAX LONG_MAX
- 
+ #define TIFF_SSIZE_T int32_t
+ #define TIFF_SSIZE_FORMAT PRId32
++#define TIFF_SSIZE_T_MAX INT32_MAX
+ #define SIZEOF_SIZE_T 4
  #endif
  
diff --git a/third_party/libtiff/0033-avail-out-overflow.patch b/third_party/libtiff/0033-avail-out-overflow.patch
new file mode 100644
index 0000000..9a31348
--- /dev/null
+++ b/third_party/libtiff/0033-avail-out-overflow.patch
@@ -0,0 +1,13 @@
+diff --git a/third_party/libtiff/tif_pixarlog.c b/third_party/libtiff/tif_pixarlog.c
+index 5c0346b6e..2e22b3318 100644
+--- a/third_party/libtiff/tif_pixarlog.c
++++ b/third_party/libtiff/tif_pixarlog.c
+@@ -882,7 +882,7 @@ static int PixarLogDecode(TIFF *tif, uint8_t *op, tmsize_t occ, uint16_t s)
+         return (0);
+     }
+     /* Check that we will not fill more than what was allocated */
+-    if ((tmsize_t)sp->stream.avail_out > sp->tbuf_size)
++    if (sp->tbuf_size < 0 || sp->stream.avail_out > (uInt) sp->tbuf_size)
+     {
+         TIFFErrorExtR(tif, module, "sp->stream.avail_out > sp->tbuf_size");
+         return (0);
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 4c97a25..9eec6dc 100644
--- a/third_party/libtiff/README.pdfium
+++ b/third_party/libtiff/README.pdfium
@@ -1,7 +1,9 @@
 Name: LibTIFF
 URL: http://www.simplesystems.org/libtiff/
-Version: 4.1.0
+Version: 4.5.1
+CPEPrefix: cpe:/a:libtiff:libtiff:4.5.1
 Security Critical: yes
+Shipped: yes
 License: BSD
 
 Description:
@@ -11,6 +13,6 @@
 
 0000-build-config.patch: Local build configuration changes.
 0017-safe_skews_in_gtTileContig.patch: return error if to/from skews overflow from int32.
-0027-build-config.patch: #define variables so their value can be used by #if.
 0028-nstrips-OOM.patch: return error for excess number of tiles/strips.
 0031-safe_size_ingtStripContig.patch: return error if the size to read overflow from int32.
+0033-avail-out-overflow.patch: signed comparison in PixarLogDecode().
diff --git a/third_party/libtiff/t4.h b/third_party/libtiff/t4.h
index fb0951a..f933d4a 100644
--- a/third_party/libtiff/t4.h
+++ b/third_party/libtiff/t4.h
@@ -23,26 +23,27 @@
  */
 
 #ifndef _T4_
-#define	_T4_
+#define _T4_
 /*
  * CCITT T.4 1D Huffman runlength codes and
  * related definitions.  Given the small sizes
  * of these tables it does not seem
  * worthwhile to make code & length 8 bits.
  */
-typedef struct tableentry {
-    unsigned short length;  /* bit length of g3 code */
-    unsigned short code;    /* g3 code */
-    short runlen;           /* run length in bits */
+typedef struct tableentry
+{
+    unsigned short length; /* bit length of g3 code */
+    unsigned short code;   /* g3 code */
+    short runlen;          /* run length in bits */
 } tableentry;
 
-#define EOL	0x001	/* EOL code value - 0000 0000 0000 1 */
+#define EOL 0x001 /* EOL code value - 0000 0000 0000 1 */
 
 /* status values returned instead of a run length */
-#define G3CODE_EOL	-1	/* NB: ACT_EOL - ACT_WRUNT */
-#define G3CODE_INVALID	-2	/* NB: ACT_INVALID - ACT_WRUNT */
-#define G3CODE_EOF	-3	/* end of input data */
-#define G3CODE_INCOMP	-4	/* incomplete run code */
+#define G3CODE_EOL -1     /* NB: ACT_EOL - ACT_WRUNT */
+#define G3CODE_INVALID -2 /* NB: ACT_INVALID - ACT_WRUNT */
+#define G3CODE_EOF -3     /* end of input data */
+#define G3CODE_INCOMP -4  /* incomplete run code */
 
 /*
  * Note that these tables are ordered such that the
@@ -54,237 +55,230 @@
  */
 #ifdef G3CODES
 const tableentry TIFFFaxWhiteCodes[] = {
-    { 8, 0x35, 0 },	/* 0011 0101 */
-    { 6, 0x7, 1 },	/* 0001 11 */
-    { 4, 0x7, 2 },	/* 0111 */
-    { 4, 0x8, 3 },	/* 1000 */
-    { 4, 0xB, 4 },	/* 1011 */
-    { 4, 0xC, 5 },	/* 1100 */
-    { 4, 0xE, 6 },	/* 1110 */
-    { 4, 0xF, 7 },	/* 1111 */
-    { 5, 0x13, 8 },	/* 1001 1 */
-    { 5, 0x14, 9 },	/* 1010 0 */
-    { 5, 0x7, 10 },	/* 0011 1 */
-    { 5, 0x8, 11 },	/* 0100 0 */
-    { 6, 0x8, 12 },	/* 0010 00 */
-    { 6, 0x3, 13 },	/* 0000 11 */
-    { 6, 0x34, 14 },	/* 1101 00 */
-    { 6, 0x35, 15 },	/* 1101 01 */
-    { 6, 0x2A, 16 },	/* 1010 10 */
-    { 6, 0x2B, 17 },	/* 1010 11 */
-    { 7, 0x27, 18 },	/* 0100 111 */
-    { 7, 0xC, 19 },	/* 0001 100 */
-    { 7, 0x8, 20 },	/* 0001 000 */
-    { 7, 0x17, 21 },	/* 0010 111 */
-    { 7, 0x3, 22 },	/* 0000 011 */
-    { 7, 0x4, 23 },	/* 0000 100 */
-    { 7, 0x28, 24 },	/* 0101 000 */
-    { 7, 0x2B, 25 },	/* 0101 011 */
-    { 7, 0x13, 26 },	/* 0010 011 */
-    { 7, 0x24, 27 },	/* 0100 100 */
-    { 7, 0x18, 28 },	/* 0011 000 */
-    { 8, 0x2, 29 },	/* 0000 0010 */
-    { 8, 0x3, 30 },	/* 0000 0011 */
-    { 8, 0x1A, 31 },	/* 0001 1010 */
-    { 8, 0x1B, 32 },	/* 0001 1011 */
-    { 8, 0x12, 33 },	/* 0001 0010 */
-    { 8, 0x13, 34 },	/* 0001 0011 */
-    { 8, 0x14, 35 },	/* 0001 0100 */
-    { 8, 0x15, 36 },	/* 0001 0101 */
-    { 8, 0x16, 37 },	/* 0001 0110 */
-    { 8, 0x17, 38 },	/* 0001 0111 */
-    { 8, 0x28, 39 },	/* 0010 1000 */
-    { 8, 0x29, 40 },	/* 0010 1001 */
-    { 8, 0x2A, 41 },	/* 0010 1010 */
-    { 8, 0x2B, 42 },	/* 0010 1011 */
-    { 8, 0x2C, 43 },	/* 0010 1100 */
-    { 8, 0x2D, 44 },	/* 0010 1101 */
-    { 8, 0x4, 45 },	/* 0000 0100 */
-    { 8, 0x5, 46 },	/* 0000 0101 */
-    { 8, 0xA, 47 },	/* 0000 1010 */
-    { 8, 0xB, 48 },	/* 0000 1011 */
-    { 8, 0x52, 49 },	/* 0101 0010 */
-    { 8, 0x53, 50 },	/* 0101 0011 */
-    { 8, 0x54, 51 },	/* 0101 0100 */
-    { 8, 0x55, 52 },	/* 0101 0101 */
-    { 8, 0x24, 53 },	/* 0010 0100 */
-    { 8, 0x25, 54 },	/* 0010 0101 */
-    { 8, 0x58, 55 },	/* 0101 1000 */
-    { 8, 0x59, 56 },	/* 0101 1001 */
-    { 8, 0x5A, 57 },	/* 0101 1010 */
-    { 8, 0x5B, 58 },	/* 0101 1011 */
-    { 8, 0x4A, 59 },	/* 0100 1010 */
-    { 8, 0x4B, 60 },	/* 0100 1011 */
-    { 8, 0x32, 61 },	/* 0011 0010 */
-    { 8, 0x33, 62 },	/* 0011 0011 */
-    { 8, 0x34, 63 },	/* 0011 0100 */
-    { 5, 0x1B, 64 },	/* 1101 1 */
-    { 5, 0x12, 128 },	/* 1001 0 */
-    { 6, 0x17, 192 },	/* 0101 11 */
-    { 7, 0x37, 256 },	/* 0110 111 */
-    { 8, 0x36, 320 },	/* 0011 0110 */
-    { 8, 0x37, 384 },	/* 0011 0111 */
-    { 8, 0x64, 448 },	/* 0110 0100 */
-    { 8, 0x65, 512 },	/* 0110 0101 */
-    { 8, 0x68, 576 },	/* 0110 1000 */
-    { 8, 0x67, 640 },	/* 0110 0111 */
-    { 9, 0xCC, 704 },	/* 0110 0110 0 */
-    { 9, 0xCD, 768 },	/* 0110 0110 1 */
-    { 9, 0xD2, 832 },	/* 0110 1001 0 */
-    { 9, 0xD3, 896 },	/* 0110 1001 1 */
-    { 9, 0xD4, 960 },	/* 0110 1010 0 */
-    { 9, 0xD5, 1024 },	/* 0110 1010 1 */
-    { 9, 0xD6, 1088 },	/* 0110 1011 0 */
-    { 9, 0xD7, 1152 },	/* 0110 1011 1 */
-    { 9, 0xD8, 1216 },	/* 0110 1100 0 */
-    { 9, 0xD9, 1280 },	/* 0110 1100 1 */
-    { 9, 0xDA, 1344 },	/* 0110 1101 0 */
-    { 9, 0xDB, 1408 },	/* 0110 1101 1 */
-    { 9, 0x98, 1472 },	/* 0100 1100 0 */
-    { 9, 0x99, 1536 },	/* 0100 1100 1 */
-    { 9, 0x9A, 1600 },	/* 0100 1101 0 */
-    { 6, 0x18, 1664 },	/* 0110 00 */
-    { 9, 0x9B, 1728 },	/* 0100 1101 1 */
-    { 11, 0x8, 1792 },	/* 0000 0001 000 */
-    { 11, 0xC, 1856 },	/* 0000 0001 100 */
-    { 11, 0xD, 1920 },	/* 0000 0001 101 */
-    { 12, 0x12, 1984 },	/* 0000 0001 0010 */
-    { 12, 0x13, 2048 },	/* 0000 0001 0011 */
-    { 12, 0x14, 2112 },	/* 0000 0001 0100 */
-    { 12, 0x15, 2176 },	/* 0000 0001 0101 */
-    { 12, 0x16, 2240 },	/* 0000 0001 0110 */
-    { 12, 0x17, 2304 },	/* 0000 0001 0111 */
-    { 12, 0x1C, 2368 },	/* 0000 0001 1100 */
-    { 12, 0x1D, 2432 },	/* 0000 0001 1101 */
-    { 12, 0x1E, 2496 },	/* 0000 0001 1110 */
-    { 12, 0x1F, 2560 },	/* 0000 0001 1111 */
-    { 12, 0x1, G3CODE_EOL },	/* 0000 0000 0001 */
-    { 9, 0x1, G3CODE_INVALID },	/* 0000 0000 1 */
-    { 10, 0x1, G3CODE_INVALID },	/* 0000 0000 01 */
-    { 11, 0x1, G3CODE_INVALID },	/* 0000 0000 001 */
-    { 12, 0x0, G3CODE_INVALID },	/* 0000 0000 0000 */
+    {8, 0x35, 0},              /* 0011 0101 */
+    {6, 0x7, 1},               /* 0001 11 */
+    {4, 0x7, 2},               /* 0111 */
+    {4, 0x8, 3},               /* 1000 */
+    {4, 0xB, 4},               /* 1011 */
+    {4, 0xC, 5},               /* 1100 */
+    {4, 0xE, 6},               /* 1110 */
+    {4, 0xF, 7},               /* 1111 */
+    {5, 0x13, 8},              /* 1001 1 */
+    {5, 0x14, 9},              /* 1010 0 */
+    {5, 0x7, 10},              /* 0011 1 */
+    {5, 0x8, 11},              /* 0100 0 */
+    {6, 0x8, 12},              /* 0010 00 */
+    {6, 0x3, 13},              /* 0000 11 */
+    {6, 0x34, 14},             /* 1101 00 */
+    {6, 0x35, 15},             /* 1101 01 */
+    {6, 0x2A, 16},             /* 1010 10 */
+    {6, 0x2B, 17},             /* 1010 11 */
+    {7, 0x27, 18},             /* 0100 111 */
+    {7, 0xC, 19},              /* 0001 100 */
+    {7, 0x8, 20},              /* 0001 000 */
+    {7, 0x17, 21},             /* 0010 111 */
+    {7, 0x3, 22},              /* 0000 011 */
+    {7, 0x4, 23},              /* 0000 100 */
+    {7, 0x28, 24},             /* 0101 000 */
+    {7, 0x2B, 25},             /* 0101 011 */
+    {7, 0x13, 26},             /* 0010 011 */
+    {7, 0x24, 27},             /* 0100 100 */
+    {7, 0x18, 28},             /* 0011 000 */
+    {8, 0x2, 29},              /* 0000 0010 */
+    {8, 0x3, 30},              /* 0000 0011 */
+    {8, 0x1A, 31},             /* 0001 1010 */
+    {8, 0x1B, 32},             /* 0001 1011 */
+    {8, 0x12, 33},             /* 0001 0010 */
+    {8, 0x13, 34},             /* 0001 0011 */
+    {8, 0x14, 35},             /* 0001 0100 */
+    {8, 0x15, 36},             /* 0001 0101 */
+    {8, 0x16, 37},             /* 0001 0110 */
+    {8, 0x17, 38},             /* 0001 0111 */
+    {8, 0x28, 39},             /* 0010 1000 */
+    {8, 0x29, 40},             /* 0010 1001 */
+    {8, 0x2A, 41},             /* 0010 1010 */
+    {8, 0x2B, 42},             /* 0010 1011 */
+    {8, 0x2C, 43},             /* 0010 1100 */
+    {8, 0x2D, 44},             /* 0010 1101 */
+    {8, 0x4, 45},              /* 0000 0100 */
+    {8, 0x5, 46},              /* 0000 0101 */
+    {8, 0xA, 47},              /* 0000 1010 */
+    {8, 0xB, 48},              /* 0000 1011 */
+    {8, 0x52, 49},             /* 0101 0010 */
+    {8, 0x53, 50},             /* 0101 0011 */
+    {8, 0x54, 51},             /* 0101 0100 */
+    {8, 0x55, 52},             /* 0101 0101 */
+    {8, 0x24, 53},             /* 0010 0100 */
+    {8, 0x25, 54},             /* 0010 0101 */
+    {8, 0x58, 55},             /* 0101 1000 */
+    {8, 0x59, 56},             /* 0101 1001 */
+    {8, 0x5A, 57},             /* 0101 1010 */
+    {8, 0x5B, 58},             /* 0101 1011 */
+    {8, 0x4A, 59},             /* 0100 1010 */
+    {8, 0x4B, 60},             /* 0100 1011 */
+    {8, 0x32, 61},             /* 0011 0010 */
+    {8, 0x33, 62},             /* 0011 0011 */
+    {8, 0x34, 63},             /* 0011 0100 */
+    {5, 0x1B, 64},             /* 1101 1 */
+    {5, 0x12, 128},            /* 1001 0 */
+    {6, 0x17, 192},            /* 0101 11 */
+    {7, 0x37, 256},            /* 0110 111 */
+    {8, 0x36, 320},            /* 0011 0110 */
+    {8, 0x37, 384},            /* 0011 0111 */
+    {8, 0x64, 448},            /* 0110 0100 */
+    {8, 0x65, 512},            /* 0110 0101 */
+    {8, 0x68, 576},            /* 0110 1000 */
+    {8, 0x67, 640},            /* 0110 0111 */
+    {9, 0xCC, 704},            /* 0110 0110 0 */
+    {9, 0xCD, 768},            /* 0110 0110 1 */
+    {9, 0xD2, 832},            /* 0110 1001 0 */
+    {9, 0xD3, 896},            /* 0110 1001 1 */
+    {9, 0xD4, 960},            /* 0110 1010 0 */
+    {9, 0xD5, 1024},           /* 0110 1010 1 */
+    {9, 0xD6, 1088},           /* 0110 1011 0 */
+    {9, 0xD7, 1152},           /* 0110 1011 1 */
+    {9, 0xD8, 1216},           /* 0110 1100 0 */
+    {9, 0xD9, 1280},           /* 0110 1100 1 */
+    {9, 0xDA, 1344},           /* 0110 1101 0 */
+    {9, 0xDB, 1408},           /* 0110 1101 1 */
+    {9, 0x98, 1472},           /* 0100 1100 0 */
+    {9, 0x99, 1536},           /* 0100 1100 1 */
+    {9, 0x9A, 1600},           /* 0100 1101 0 */
+    {6, 0x18, 1664},           /* 0110 00 */
+    {9, 0x9B, 1728},           /* 0100 1101 1 */
+    {11, 0x8, 1792},           /* 0000 0001 000 */
+    {11, 0xC, 1856},           /* 0000 0001 100 */
+    {11, 0xD, 1920},           /* 0000 0001 101 */
+    {12, 0x12, 1984},          /* 0000 0001 0010 */
+    {12, 0x13, 2048},          /* 0000 0001 0011 */
+    {12, 0x14, 2112},          /* 0000 0001 0100 */
+    {12, 0x15, 2176},          /* 0000 0001 0101 */
+    {12, 0x16, 2240},          /* 0000 0001 0110 */
+    {12, 0x17, 2304},          /* 0000 0001 0111 */
+    {12, 0x1C, 2368},          /* 0000 0001 1100 */
+    {12, 0x1D, 2432},          /* 0000 0001 1101 */
+    {12, 0x1E, 2496},          /* 0000 0001 1110 */
+    {12, 0x1F, 2560},          /* 0000 0001 1111 */
+    {12, 0x1, G3CODE_EOL},     /* 0000 0000 0001 */
+    {9, 0x1, G3CODE_INVALID},  /* 0000 0000 1 */
+    {10, 0x1, G3CODE_INVALID}, /* 0000 0000 01 */
+    {11, 0x1, G3CODE_INVALID}, /* 0000 0000 001 */
+    {12, 0x0, G3CODE_INVALID}, /* 0000 0000 0000 */
 };
 
 const tableentry TIFFFaxBlackCodes[] = {
-    { 10, 0x37, 0 },	/* 0000 1101 11 */
-    { 3, 0x2, 1 },	/* 010 */
-    { 2, 0x3, 2 },	/* 11 */
-    { 2, 0x2, 3 },	/* 10 */
-    { 3, 0x3, 4 },	/* 011 */
-    { 4, 0x3, 5 },	/* 0011 */
-    { 4, 0x2, 6 },	/* 0010 */
-    { 5, 0x3, 7 },	/* 0001 1 */
-    { 6, 0x5, 8 },	/* 0001 01 */
-    { 6, 0x4, 9 },	/* 0001 00 */
-    { 7, 0x4, 10 },	/* 0000 100 */
-    { 7, 0x5, 11 },	/* 0000 101 */
-    { 7, 0x7, 12 },	/* 0000 111 */
-    { 8, 0x4, 13 },	/* 0000 0100 */
-    { 8, 0x7, 14 },	/* 0000 0111 */
-    { 9, 0x18, 15 },	/* 0000 1100 0 */
-    { 10, 0x17, 16 },	/* 0000 0101 11 */
-    { 10, 0x18, 17 },	/* 0000 0110 00 */
-    { 10, 0x8, 18 },	/* 0000 0010 00 */
-    { 11, 0x67, 19 },	/* 0000 1100 111 */
-    { 11, 0x68, 20 },	/* 0000 1101 000 */
-    { 11, 0x6C, 21 },	/* 0000 1101 100 */
-    { 11, 0x37, 22 },	/* 0000 0110 111 */
-    { 11, 0x28, 23 },	/* 0000 0101 000 */
-    { 11, 0x17, 24 },	/* 0000 0010 111 */
-    { 11, 0x18, 25 },	/* 0000 0011 000 */
-    { 12, 0xCA, 26 },	/* 0000 1100 1010 */
-    { 12, 0xCB, 27 },	/* 0000 1100 1011 */
-    { 12, 0xCC, 28 },	/* 0000 1100 1100 */
-    { 12, 0xCD, 29 },	/* 0000 1100 1101 */
-    { 12, 0x68, 30 },	/* 0000 0110 1000 */
-    { 12, 0x69, 31 },	/* 0000 0110 1001 */
-    { 12, 0x6A, 32 },	/* 0000 0110 1010 */
-    { 12, 0x6B, 33 },	/* 0000 0110 1011 */
-    { 12, 0xD2, 34 },	/* 0000 1101 0010 */
-    { 12, 0xD3, 35 },	/* 0000 1101 0011 */
-    { 12, 0xD4, 36 },	/* 0000 1101 0100 */
-    { 12, 0xD5, 37 },	/* 0000 1101 0101 */
-    { 12, 0xD6, 38 },	/* 0000 1101 0110 */
-    { 12, 0xD7, 39 },	/* 0000 1101 0111 */
-    { 12, 0x6C, 40 },	/* 0000 0110 1100 */
-    { 12, 0x6D, 41 },	/* 0000 0110 1101 */
-    { 12, 0xDA, 42 },	/* 0000 1101 1010 */
-    { 12, 0xDB, 43 },	/* 0000 1101 1011 */
-    { 12, 0x54, 44 },	/* 0000 0101 0100 */
-    { 12, 0x55, 45 },	/* 0000 0101 0101 */
-    { 12, 0x56, 46 },	/* 0000 0101 0110 */
-    { 12, 0x57, 47 },	/* 0000 0101 0111 */
-    { 12, 0x64, 48 },	/* 0000 0110 0100 */
-    { 12, 0x65, 49 },	/* 0000 0110 0101 */
-    { 12, 0x52, 50 },	/* 0000 0101 0010 */
-    { 12, 0x53, 51 },	/* 0000 0101 0011 */
-    { 12, 0x24, 52 },	/* 0000 0010 0100 */
-    { 12, 0x37, 53 },	/* 0000 0011 0111 */
-    { 12, 0x38, 54 },	/* 0000 0011 1000 */
-    { 12, 0x27, 55 },	/* 0000 0010 0111 */
-    { 12, 0x28, 56 },	/* 0000 0010 1000 */
-    { 12, 0x58, 57 },	/* 0000 0101 1000 */
-    { 12, 0x59, 58 },	/* 0000 0101 1001 */
-    { 12, 0x2B, 59 },	/* 0000 0010 1011 */
-    { 12, 0x2C, 60 },	/* 0000 0010 1100 */
-    { 12, 0x5A, 61 },	/* 0000 0101 1010 */
-    { 12, 0x66, 62 },	/* 0000 0110 0110 */
-    { 12, 0x67, 63 },	/* 0000 0110 0111 */
-    { 10, 0xF, 64 },	/* 0000 0011 11 */
-    { 12, 0xC8, 128 },	/* 0000 1100 1000 */
-    { 12, 0xC9, 192 },	/* 0000 1100 1001 */
-    { 12, 0x5B, 256 },	/* 0000 0101 1011 */
-    { 12, 0x33, 320 },	/* 0000 0011 0011 */
-    { 12, 0x34, 384 },	/* 0000 0011 0100 */
-    { 12, 0x35, 448 },	/* 0000 0011 0101 */
-    { 13, 0x6C, 512 },	/* 0000 0011 0110 0 */
-    { 13, 0x6D, 576 },	/* 0000 0011 0110 1 */
-    { 13, 0x4A, 640 },	/* 0000 0010 0101 0 */
-    { 13, 0x4B, 704 },	/* 0000 0010 0101 1 */
-    { 13, 0x4C, 768 },	/* 0000 0010 0110 0 */
-    { 13, 0x4D, 832 },	/* 0000 0010 0110 1 */
-    { 13, 0x72, 896 },	/* 0000 0011 1001 0 */
-    { 13, 0x73, 960 },	/* 0000 0011 1001 1 */
-    { 13, 0x74, 1024 },	/* 0000 0011 1010 0 */
-    { 13, 0x75, 1088 },	/* 0000 0011 1010 1 */
-    { 13, 0x76, 1152 },	/* 0000 0011 1011 0 */
-    { 13, 0x77, 1216 },	/* 0000 0011 1011 1 */
-    { 13, 0x52, 1280 },	/* 0000 0010 1001 0 */
-    { 13, 0x53, 1344 },	/* 0000 0010 1001 1 */
-    { 13, 0x54, 1408 },	/* 0000 0010 1010 0 */
-    { 13, 0x55, 1472 },	/* 0000 0010 1010 1 */
-    { 13, 0x5A, 1536 },	/* 0000 0010 1101 0 */
-    { 13, 0x5B, 1600 },	/* 0000 0010 1101 1 */
-    { 13, 0x64, 1664 },	/* 0000 0011 0010 0 */
-    { 13, 0x65, 1728 },	/* 0000 0011 0010 1 */
-    { 11, 0x8, 1792 },	/* 0000 0001 000 */
-    { 11, 0xC, 1856 },	/* 0000 0001 100 */
-    { 11, 0xD, 1920 },	/* 0000 0001 101 */
-    { 12, 0x12, 1984 },	/* 0000 0001 0010 */
-    { 12, 0x13, 2048 },	/* 0000 0001 0011 */
-    { 12, 0x14, 2112 },	/* 0000 0001 0100 */
-    { 12, 0x15, 2176 },	/* 0000 0001 0101 */
-    { 12, 0x16, 2240 },	/* 0000 0001 0110 */
-    { 12, 0x17, 2304 },	/* 0000 0001 0111 */
-    { 12, 0x1C, 2368 },	/* 0000 0001 1100 */
-    { 12, 0x1D, 2432 },	/* 0000 0001 1101 */
-    { 12, 0x1E, 2496 },	/* 0000 0001 1110 */
-    { 12, 0x1F, 2560 },	/* 0000 0001 1111 */
-    { 12, 0x1, G3CODE_EOL },	/* 0000 0000 0001 */
-    { 9, 0x1, G3CODE_INVALID },	/* 0000 0000 1 */
-    { 10, 0x1, G3CODE_INVALID },	/* 0000 0000 01 */
-    { 11, 0x1, G3CODE_INVALID },	/* 0000 0000 001 */
-    { 12, 0x0, G3CODE_INVALID },	/* 0000 0000 0000 */
+    {10, 0x37, 0},             /* 0000 1101 11 */
+    {3, 0x2, 1},               /* 010 */
+    {2, 0x3, 2},               /* 11 */
+    {2, 0x2, 3},               /* 10 */
+    {3, 0x3, 4},               /* 011 */
+    {4, 0x3, 5},               /* 0011 */
+    {4, 0x2, 6},               /* 0010 */
+    {5, 0x3, 7},               /* 0001 1 */
+    {6, 0x5, 8},               /* 0001 01 */
+    {6, 0x4, 9},               /* 0001 00 */
+    {7, 0x4, 10},              /* 0000 100 */
+    {7, 0x5, 11},              /* 0000 101 */
+    {7, 0x7, 12},              /* 0000 111 */
+    {8, 0x4, 13},              /* 0000 0100 */
+    {8, 0x7, 14},              /* 0000 0111 */
+    {9, 0x18, 15},             /* 0000 1100 0 */
+    {10, 0x17, 16},            /* 0000 0101 11 */
+    {10, 0x18, 17},            /* 0000 0110 00 */
+    {10, 0x8, 18},             /* 0000 0010 00 */
+    {11, 0x67, 19},            /* 0000 1100 111 */
+    {11, 0x68, 20},            /* 0000 1101 000 */
+    {11, 0x6C, 21},            /* 0000 1101 100 */
+    {11, 0x37, 22},            /* 0000 0110 111 */
+    {11, 0x28, 23},            /* 0000 0101 000 */
+    {11, 0x17, 24},            /* 0000 0010 111 */
+    {11, 0x18, 25},            /* 0000 0011 000 */
+    {12, 0xCA, 26},            /* 0000 1100 1010 */
+    {12, 0xCB, 27},            /* 0000 1100 1011 */
+    {12, 0xCC, 28},            /* 0000 1100 1100 */
+    {12, 0xCD, 29},            /* 0000 1100 1101 */
+    {12, 0x68, 30},            /* 0000 0110 1000 */
+    {12, 0x69, 31},            /* 0000 0110 1001 */
+    {12, 0x6A, 32},            /* 0000 0110 1010 */
+    {12, 0x6B, 33},            /* 0000 0110 1011 */
+    {12, 0xD2, 34},            /* 0000 1101 0010 */
+    {12, 0xD3, 35},            /* 0000 1101 0011 */
+    {12, 0xD4, 36},            /* 0000 1101 0100 */
+    {12, 0xD5, 37},            /* 0000 1101 0101 */
+    {12, 0xD6, 38},            /* 0000 1101 0110 */
+    {12, 0xD7, 39},            /* 0000 1101 0111 */
+    {12, 0x6C, 40},            /* 0000 0110 1100 */
+    {12, 0x6D, 41},            /* 0000 0110 1101 */
+    {12, 0xDA, 42},            /* 0000 1101 1010 */
+    {12, 0xDB, 43},            /* 0000 1101 1011 */
+    {12, 0x54, 44},            /* 0000 0101 0100 */
+    {12, 0x55, 45},            /* 0000 0101 0101 */
+    {12, 0x56, 46},            /* 0000 0101 0110 */
+    {12, 0x57, 47},            /* 0000 0101 0111 */
+    {12, 0x64, 48},            /* 0000 0110 0100 */
+    {12, 0x65, 49},            /* 0000 0110 0101 */
+    {12, 0x52, 50},            /* 0000 0101 0010 */
+    {12, 0x53, 51},            /* 0000 0101 0011 */
+    {12, 0x24, 52},            /* 0000 0010 0100 */
+    {12, 0x37, 53},            /* 0000 0011 0111 */
+    {12, 0x38, 54},            /* 0000 0011 1000 */
+    {12, 0x27, 55},            /* 0000 0010 0111 */
+    {12, 0x28, 56},            /* 0000 0010 1000 */
+    {12, 0x58, 57},            /* 0000 0101 1000 */
+    {12, 0x59, 58},            /* 0000 0101 1001 */
+    {12, 0x2B, 59},            /* 0000 0010 1011 */
+    {12, 0x2C, 60},            /* 0000 0010 1100 */
+    {12, 0x5A, 61},            /* 0000 0101 1010 */
+    {12, 0x66, 62},            /* 0000 0110 0110 */
+    {12, 0x67, 63},            /* 0000 0110 0111 */
+    {10, 0xF, 64},             /* 0000 0011 11 */
+    {12, 0xC8, 128},           /* 0000 1100 1000 */
+    {12, 0xC9, 192},           /* 0000 1100 1001 */
+    {12, 0x5B, 256},           /* 0000 0101 1011 */
+    {12, 0x33, 320},           /* 0000 0011 0011 */
+    {12, 0x34, 384},           /* 0000 0011 0100 */
+    {12, 0x35, 448},           /* 0000 0011 0101 */
+    {13, 0x6C, 512},           /* 0000 0011 0110 0 */
+    {13, 0x6D, 576},           /* 0000 0011 0110 1 */
+    {13, 0x4A, 640},           /* 0000 0010 0101 0 */
+    {13, 0x4B, 704},           /* 0000 0010 0101 1 */
+    {13, 0x4C, 768},           /* 0000 0010 0110 0 */
+    {13, 0x4D, 832},           /* 0000 0010 0110 1 */
+    {13, 0x72, 896},           /* 0000 0011 1001 0 */
+    {13, 0x73, 960},           /* 0000 0011 1001 1 */
+    {13, 0x74, 1024},          /* 0000 0011 1010 0 */
+    {13, 0x75, 1088},          /* 0000 0011 1010 1 */
+    {13, 0x76, 1152},          /* 0000 0011 1011 0 */
+    {13, 0x77, 1216},          /* 0000 0011 1011 1 */
+    {13, 0x52, 1280},          /* 0000 0010 1001 0 */
+    {13, 0x53, 1344},          /* 0000 0010 1001 1 */
+    {13, 0x54, 1408},          /* 0000 0010 1010 0 */
+    {13, 0x55, 1472},          /* 0000 0010 1010 1 */
+    {13, 0x5A, 1536},          /* 0000 0010 1101 0 */
+    {13, 0x5B, 1600},          /* 0000 0010 1101 1 */
+    {13, 0x64, 1664},          /* 0000 0011 0010 0 */
+    {13, 0x65, 1728},          /* 0000 0011 0010 1 */
+    {11, 0x8, 1792},           /* 0000 0001 000 */
+    {11, 0xC, 1856},           /* 0000 0001 100 */
+    {11, 0xD, 1920},           /* 0000 0001 101 */
+    {12, 0x12, 1984},          /* 0000 0001 0010 */
+    {12, 0x13, 2048},          /* 0000 0001 0011 */
+    {12, 0x14, 2112},          /* 0000 0001 0100 */
+    {12, 0x15, 2176},          /* 0000 0001 0101 */
+    {12, 0x16, 2240},          /* 0000 0001 0110 */
+    {12, 0x17, 2304},          /* 0000 0001 0111 */
+    {12, 0x1C, 2368},          /* 0000 0001 1100 */
+    {12, 0x1D, 2432},          /* 0000 0001 1101 */
+    {12, 0x1E, 2496},          /* 0000 0001 1110 */
+    {12, 0x1F, 2560},          /* 0000 0001 1111 */
+    {12, 0x1, G3CODE_EOL},     /* 0000 0000 0001 */
+    {9, 0x1, G3CODE_INVALID},  /* 0000 0000 1 */
+    {10, 0x1, G3CODE_INVALID}, /* 0000 0000 01 */
+    {11, 0x1, G3CODE_INVALID}, /* 0000 0000 001 */
+    {12, 0x0, G3CODE_INVALID}, /* 0000 0000 0000 */
 };
 #else
 extern const tableentry TIFFFaxWhiteCodes[];
 extern const tableentry TIFFFaxBlackCodes[];
 #endif
 #endif /* _T4_ */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_aux.c b/third_party/libtiff/tif_aux.c
index 8188db5..49855bb 100644
--- a/third_party/libtiff/tif_aux.c
+++ b/third_party/libtiff/tif_aux.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1991-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -27,173 +27,180 @@
  *
  * Auxiliary Support Routines.
  */
-#include "tiffiop.h"
 #include "tif_predict.h"
-#include <math.h>
+#include "tiffiop.h"
 #include <float.h>
+#include <math.h>
 
-uint32
-_TIFFMultiply32(TIFF* tif, uint32 first, uint32 second, const char* where)
+uint32_t _TIFFMultiply32(TIFF *tif, uint32_t first, uint32_t second,
+                         const char *where)
 {
-	if (second && first > TIFF_UINT32_MAX / second) {
-		TIFFErrorExt(tif->tif_clientdata, where, "Integer overflow in %s", where);
-		return 0;
-	}
-
-	return first * second;
-}
-
-uint64
-_TIFFMultiply64(TIFF* tif, uint64 first, uint64 second, const char* where)
-{
-	if (second && first > TIFF_UINT64_MAX / second) {
-		TIFFErrorExt(tif->tif_clientdata, where, "Integer overflow in %s", where);
-		return 0;
-	}
-
-	return first * second;
-}
-
-tmsize_t
-_TIFFMultiplySSize(TIFF* tif, tmsize_t first, tmsize_t second, const char* where)
-{
-    if( first <= 0 || second <= 0 )
+    if (second && first > UINT32_MAX / second)
     {
-        if( tif != NULL && where != NULL )
+        TIFFErrorExtR(tif, where, "Integer overflow in %s", where);
+        return 0;
+    }
+
+    return first * second;
+}
+
+uint64_t _TIFFMultiply64(TIFF *tif, uint64_t first, uint64_t second,
+                         const char *where)
+{
+    if (second && first > UINT64_MAX / second)
+    {
+        TIFFErrorExtR(tif, where, "Integer overflow in %s", where);
+        return 0;
+    }
+
+    return first * second;
+}
+
+tmsize_t _TIFFMultiplySSize(TIFF *tif, tmsize_t first, tmsize_t second,
+                            const char *where)
+{
+    if (first <= 0 || second <= 0)
+    {
+        if (tif != NULL && where != NULL)
         {
-            TIFFErrorExt(tif->tif_clientdata, where,
-                        "Invalid argument to _TIFFMultiplySSize() in %s", where);
+            TIFFErrorExtR(tif, where,
+                          "Invalid argument to _TIFFMultiplySSize() in %s",
+                          where);
         }
         return 0;
     }
 
-    if( first > TIFF_TMSIZE_T_MAX / second )
+    if (first > TIFF_TMSIZE_T_MAX / second)
     {
-        if( tif != NULL && where != NULL )
+        if (tif != NULL && where != NULL)
         {
-            TIFFErrorExt(tif->tif_clientdata, where,
-                        "Integer overflow in %s", where);
+            TIFFErrorExtR(tif, where, "Integer overflow in %s", where);
         }
         return 0;
     }
     return first * second;
 }
 
-tmsize_t _TIFFCastUInt64ToSSize(TIFF* tif, uint64 val, const char* module)
+tmsize_t _TIFFCastUInt64ToSSize(TIFF *tif, uint64_t val, const char *module)
 {
-    if( val > (uint64)TIFF_TMSIZE_T_MAX )
+    if (val > (uint64_t)TIFF_TMSIZE_T_MAX)
     {
-        if( tif != NULL && module != NULL )
+        if (tif != NULL && module != NULL)
         {
-            TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow");
+            TIFFErrorExtR(tif, module, "Integer overflow");
         }
         return 0;
     }
     return (tmsize_t)val;
 }
 
-void*
-_TIFFCheckRealloc(TIFF* tif, void* buffer,
-		  tmsize_t nmemb, tmsize_t elem_size, const char* what)
+void *_TIFFCheckRealloc(TIFF *tif, void *buffer, tmsize_t nmemb,
+                        tmsize_t elem_size, const char *what)
 {
-	void* cp = NULL;
-        tmsize_t count = _TIFFMultiplySSize(tif, nmemb, elem_size, NULL);
-	/*
-	 * Check for integer overflow.
-	 */
-	if (count != 0)
-	{
-		cp = _TIFFrealloc(buffer, count);
-	}
+    void *cp = NULL;
+    tmsize_t count = _TIFFMultiplySSize(tif, nmemb, elem_size, NULL);
+    /*
+     * Check for integer overflow.
+     */
+    if (count != 0)
+    {
+        cp = _TIFFreallocExt(tif, buffer, count);
+    }
 
-	if (cp == NULL) {
-		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-			     "Failed to allocate memory for %s "
-			     "(%ld elements of %ld bytes each)",
-			     what,(long) nmemb, (long) elem_size);
-	}
+    if (cp == NULL)
+    {
+        TIFFErrorExtR(tif, tif->tif_name,
+                      "Failed to allocate memory for %s "
+                      "(%" TIFF_SSIZE_FORMAT " elements of %" TIFF_SSIZE_FORMAT
+                      " bytes each)",
+                      what, nmemb, elem_size);
+    }
 
-	return cp;
+    return cp;
 }
 
-void*
-_TIFFCheckMalloc(TIFF* tif, tmsize_t nmemb, tmsize_t elem_size, const char* what)
+void *_TIFFCheckMalloc(TIFF *tif, tmsize_t nmemb, tmsize_t elem_size,
+                       const char *what)
 {
-	return _TIFFCheckRealloc(tif, NULL, nmemb, elem_size, what);  
+    return _TIFFCheckRealloc(tif, NULL, nmemb, elem_size, what);
 }
 
-static int
-TIFFDefaultTransferFunction(TIFFDirectory* td)
+static int TIFFDefaultTransferFunction(TIFF *tif, TIFFDirectory *td)
 {
-	uint16 **tf = td->td_transferfunction;
-	tmsize_t i, n, nbytes;
+    uint16_t **tf = td->td_transferfunction;
+    tmsize_t i, n, nbytes;
 
-	tf[0] = tf[1] = tf[2] = 0;
-	if (td->td_bitspersample >= sizeof(tmsize_t) * 8 - 2)
-		return 0;
+    tf[0] = tf[1] = tf[2] = 0;
+    if (td->td_bitspersample >= sizeof(tmsize_t) * 8 - 2)
+        return 0;
 
-	n = ((tmsize_t)1)<<td->td_bitspersample;
-	nbytes = n * sizeof (uint16);
-        tf[0] = (uint16 *)_TIFFmalloc(nbytes);
-	if (tf[0] == NULL)
-		return 0;
-	tf[0][0] = 0;
-	for (i = 1; i < n; i++) {
-		double t = (double)i/((double) n-1.);
-		tf[0][i] = (uint16)floor(65535.*pow(t, 2.2) + .5);
-	}
+    n = ((tmsize_t)1) << td->td_bitspersample;
+    nbytes = n * sizeof(uint16_t);
+    tf[0] = (uint16_t *)_TIFFmallocExt(tif, nbytes);
+    if (tf[0] == NULL)
+        return 0;
+    tf[0][0] = 0;
+    for (i = 1; i < n; i++)
+    {
+        double t = (double)i / ((double)n - 1.);
+        tf[0][i] = (uint16_t)floor(65535. * pow(t, 2.2) + .5);
+    }
 
-	if (td->td_samplesperpixel - td->td_extrasamples > 1) {
-                tf[1] = (uint16 *)_TIFFmalloc(nbytes);
-		if(tf[1] == NULL)
-			goto bad;
-		_TIFFmemcpy(tf[1], tf[0], nbytes);
-                tf[2] = (uint16 *)_TIFFmalloc(nbytes);
-		if (tf[2] == NULL)
-			goto bad;
-		_TIFFmemcpy(tf[2], tf[0], nbytes);
-	}
-	return 1;
+    if (td->td_samplesperpixel - td->td_extrasamples > 1)
+    {
+        tf[1] = (uint16_t *)_TIFFmallocExt(tif, nbytes);
+        if (tf[1] == NULL)
+            goto bad;
+        _TIFFmemcpy(tf[1], tf[0], nbytes);
+        tf[2] = (uint16_t *)_TIFFmallocExt(tif, nbytes);
+        if (tf[2] == NULL)
+            goto bad;
+        _TIFFmemcpy(tf[2], tf[0], nbytes);
+    }
+    return 1;
 
 bad:
-	if (tf[0])
-		_TIFFfree(tf[0]);
-	if (tf[1])
-		_TIFFfree(tf[1]);
-	if (tf[2])
-		_TIFFfree(tf[2]);
-	tf[0] = tf[1] = tf[2] = 0;
-	return 0;
+    if (tf[0])
+        _TIFFfreeExt(tif, tf[0]);
+    if (tf[1])
+        _TIFFfreeExt(tif, tf[1]);
+    if (tf[2])
+        _TIFFfreeExt(tif, tf[2]);
+    tf[0] = tf[1] = tf[2] = 0;
+    return 0;
 }
 
-static int
-TIFFDefaultRefBlackWhite(TIFFDirectory* td)
+static int TIFFDefaultRefBlackWhite(TIFF *tif, TIFFDirectory *td)
 {
-	int i;
+    int i;
 
-        td->td_refblackwhite = (float *)_TIFFmalloc(6*sizeof (float));
-	if (td->td_refblackwhite == NULL)
-		return 0;
-        if (td->td_photometric == PHOTOMETRIC_YCBCR) {
-		/*
-		 * YCbCr (Class Y) images must have the ReferenceBlackWhite
-		 * tag set. Fix the broken images, which lacks that tag.
-		 */
-		td->td_refblackwhite[0] = 0.0F;
-		td->td_refblackwhite[1] = td->td_refblackwhite[3] =
-			td->td_refblackwhite[5] = 255.0F;
-		td->td_refblackwhite[2] = td->td_refblackwhite[4] = 128.0F;
-	} else {
-		/*
-		 * Assume RGB (Class R)
-		 */
-		for (i = 0; i < 3; i++) {
-		    td->td_refblackwhite[2*i+0] = 0;
-		    td->td_refblackwhite[2*i+1] =
-			    (float)((1L<<td->td_bitspersample)-1L);
-		}
-	}
-	return 1;
+    td->td_refblackwhite = (float *)_TIFFmallocExt(tif, 6 * sizeof(float));
+    if (td->td_refblackwhite == NULL)
+        return 0;
+    if (td->td_photometric == PHOTOMETRIC_YCBCR)
+    {
+        /*
+         * YCbCr (Class Y) images must have the ReferenceBlackWhite
+         * tag set. Fix the broken images, which lacks that tag.
+         */
+        td->td_refblackwhite[0] = 0.0F;
+        td->td_refblackwhite[1] = td->td_refblackwhite[3] =
+            td->td_refblackwhite[5] = 255.0F;
+        td->td_refblackwhite[2] = td->td_refblackwhite[4] = 128.0F;
+    }
+    else
+    {
+        /*
+         * Assume RGB (Class R)
+         */
+        for (i = 0; i < 3; i++)
+        {
+            td->td_refblackwhite[2 * i + 0] = 0;
+            td->td_refblackwhite[2 * i + 1] =
+                (float)((1L << td->td_bitspersample) - 1L);
+        }
+    }
+    return 1;
 }
 
 /*
@@ -204,216 +211,248 @@
  *	explicit values so that defaults exist only one
  *	place in the library -- in TIFFDefaultDirectory.
  */
-int
-TIFFVGetFieldDefaulted(TIFF* tif, uint32 tag, va_list ap)
+int TIFFVGetFieldDefaulted(TIFF *tif, uint32_t tag, va_list ap)
 {
-	TIFFDirectory *td = &tif->tif_dir;
+    TIFFDirectory *td = &tif->tif_dir;
 
-	if (TIFFVGetField(tif, tag, ap))
-		return (1);
-	switch (tag) {
-	case TIFFTAG_SUBFILETYPE:
-		*va_arg(ap, uint32 *) = td->td_subfiletype;
-		return (1);
-	case TIFFTAG_BITSPERSAMPLE:
-		*va_arg(ap, uint16 *) = td->td_bitspersample;
-		return (1);
-	case TIFFTAG_THRESHHOLDING:
-		*va_arg(ap, uint16 *) = td->td_threshholding;
-		return (1);
-	case TIFFTAG_FILLORDER:
-		*va_arg(ap, uint16 *) = td->td_fillorder;
-		return (1);
-	case TIFFTAG_ORIENTATION:
-		*va_arg(ap, uint16 *) = td->td_orientation;
-		return (1);
-	case TIFFTAG_SAMPLESPERPIXEL:
-		*va_arg(ap, uint16 *) = td->td_samplesperpixel;
-		return (1);
-	case TIFFTAG_ROWSPERSTRIP:
-		*va_arg(ap, uint32 *) = td->td_rowsperstrip;
-		return (1);
-	case TIFFTAG_MINSAMPLEVALUE:
-		*va_arg(ap, uint16 *) = td->td_minsamplevalue;
-		return (1);
-	case TIFFTAG_MAXSAMPLEVALUE:
-		*va_arg(ap, uint16 *) = td->td_maxsamplevalue;
-		return (1);
-	case TIFFTAG_PLANARCONFIG:
-		*va_arg(ap, uint16 *) = td->td_planarconfig;
-		return (1);
-	case TIFFTAG_RESOLUTIONUNIT:
-		*va_arg(ap, uint16 *) = td->td_resolutionunit;
-		return (1);
-	case TIFFTAG_PREDICTOR:
+    if (TIFFVGetField(tif, tag, ap))
+        return (1);
+    switch (tag)
     {
-        TIFFPredictorState* sp = (TIFFPredictorState*) tif->tif_data;
-        if( sp == NULL )
+        case TIFFTAG_SUBFILETYPE:
+            *va_arg(ap, uint32_t *) = td->td_subfiletype;
+            return (1);
+        case TIFFTAG_BITSPERSAMPLE:
+            *va_arg(ap, uint16_t *) = td->td_bitspersample;
+            return (1);
+        case TIFFTAG_THRESHHOLDING:
+            *va_arg(ap, uint16_t *) = td->td_threshholding;
+            return (1);
+        case TIFFTAG_FILLORDER:
+            *va_arg(ap, uint16_t *) = td->td_fillorder;
+            return (1);
+        case TIFFTAG_ORIENTATION:
+            *va_arg(ap, uint16_t *) = td->td_orientation;
+            return (1);
+        case TIFFTAG_SAMPLESPERPIXEL:
+            *va_arg(ap, uint16_t *) = td->td_samplesperpixel;
+            return (1);
+        case TIFFTAG_ROWSPERSTRIP:
+            *va_arg(ap, uint32_t *) = td->td_rowsperstrip;
+            return (1);
+        case TIFFTAG_MINSAMPLEVALUE:
+            *va_arg(ap, uint16_t *) = td->td_minsamplevalue;
+            return (1);
+        case TIFFTAG_MAXSAMPLEVALUE:
         {
-            TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-                         "Cannot get \"Predictor\" tag as plugin is not configured");
-            *va_arg(ap, uint16*) = 0;
-            return 0;
+            uint16_t maxsamplevalue;
+            /* td_bitspersample=1 is always set in TIFFDefaultDirectory().
+             * Therefore, td_maxsamplevalue has to be re-calculated in
+             * TIFFGetFieldDefaulted(). */
+            if (td->td_bitspersample > 0)
+            {
+                /* This shift operation into a uint16_t limits the value to
+                 * 65535 even if td_bitspersamle is > 16 */
+                if (td->td_bitspersample <= 16)
+                {
+                    maxsamplevalue = (1 << td->td_bitspersample) -
+                                     1; /* 2**(BitsPerSample) - 1 */
+                }
+                else
+                {
+                    maxsamplevalue = 65535;
+                }
+            }
+            else
+            {
+                maxsamplevalue = 0;
+            }
+            *va_arg(ap, uint16_t *) = maxsamplevalue;
+            return (1);
         }
-        *va_arg(ap, uint16*) = (uint16) sp->predictor;
-        return 1;
+        case TIFFTAG_PLANARCONFIG:
+            *va_arg(ap, uint16_t *) = td->td_planarconfig;
+            return (1);
+        case TIFFTAG_RESOLUTIONUNIT:
+            *va_arg(ap, uint16_t *) = td->td_resolutionunit;
+            return (1);
+        case TIFFTAG_PREDICTOR:
+        {
+            TIFFPredictorState *sp = (TIFFPredictorState *)tif->tif_data;
+            if (sp == NULL)
+            {
+                TIFFErrorExtR(
+                    tif, tif->tif_name,
+                    "Cannot get \"Predictor\" tag as plugin is not configured");
+                *va_arg(ap, uint16_t *) = 0;
+                return 0;
+            }
+            *va_arg(ap, uint16_t *) = (uint16_t)sp->predictor;
+            return 1;
+        }
+        case TIFFTAG_DOTRANGE:
+            *va_arg(ap, uint16_t *) = 0;
+            *va_arg(ap, uint16_t *) = (1 << td->td_bitspersample) - 1;
+            return (1);
+        case TIFFTAG_INKSET:
+            *va_arg(ap, uint16_t *) = INKSET_CMYK;
+            return 1;
+        case TIFFTAG_NUMBEROFINKS:
+            *va_arg(ap, uint16_t *) = 4;
+            return (1);
+        case TIFFTAG_EXTRASAMPLES:
+            *va_arg(ap, uint16_t *) = td->td_extrasamples;
+            *va_arg(ap, const uint16_t **) = td->td_sampleinfo;
+            return (1);
+        case TIFFTAG_MATTEING:
+            *va_arg(ap, uint16_t *) =
+                (td->td_extrasamples == 1 &&
+                 td->td_sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA);
+            return (1);
+        case TIFFTAG_TILEDEPTH:
+            *va_arg(ap, uint32_t *) = td->td_tiledepth;
+            return (1);
+        case TIFFTAG_DATATYPE:
+            *va_arg(ap, uint16_t *) = td->td_sampleformat - 1;
+            return (1);
+        case TIFFTAG_SAMPLEFORMAT:
+            *va_arg(ap, uint16_t *) = td->td_sampleformat;
+            return (1);
+        case TIFFTAG_IMAGEDEPTH:
+            *va_arg(ap, uint32_t *) = td->td_imagedepth;
+            return (1);
+        case TIFFTAG_YCBCRCOEFFICIENTS:
+        {
+            /* defaults are from CCIR Recommendation 601-1 */
+            static const float ycbcrcoeffs[] = {0.299f, 0.587f, 0.114f};
+            *va_arg(ap, const float **) = ycbcrcoeffs;
+            return 1;
+        }
+        case TIFFTAG_YCBCRSUBSAMPLING:
+            *va_arg(ap, uint16_t *) = td->td_ycbcrsubsampling[0];
+            *va_arg(ap, uint16_t *) = td->td_ycbcrsubsampling[1];
+            return (1);
+        case TIFFTAG_YCBCRPOSITIONING:
+            *va_arg(ap, uint16_t *) = td->td_ycbcrpositioning;
+            return (1);
+        case TIFFTAG_WHITEPOINT:
+        {
+            /* TIFF 6.0 specification tells that it is no default
+               value for the WhitePoint, but AdobePhotoshop TIFF
+               Technical Note tells that it should be CIE D50. */
+            static const float whitepoint[] = {
+                D50_X0 / (D50_X0 + D50_Y0 + D50_Z0),
+                D50_Y0 / (D50_X0 + D50_Y0 + D50_Z0)};
+            *va_arg(ap, const float **) = whitepoint;
+            return 1;
+        }
+        case TIFFTAG_TRANSFERFUNCTION:
+            if (!td->td_transferfunction[0] &&
+                !TIFFDefaultTransferFunction(tif, td))
+            {
+                TIFFErrorExtR(tif, tif->tif_name,
+                              "No space for \"TransferFunction\" tag");
+                return (0);
+            }
+            *va_arg(ap, const uint16_t **) = td->td_transferfunction[0];
+            if (td->td_samplesperpixel - td->td_extrasamples > 1)
+            {
+                *va_arg(ap, const uint16_t **) = td->td_transferfunction[1];
+                *va_arg(ap, const uint16_t **) = td->td_transferfunction[2];
+            }
+            return (1);
+        case TIFFTAG_REFERENCEBLACKWHITE:
+            if (!td->td_refblackwhite && !TIFFDefaultRefBlackWhite(tif, td))
+                return (0);
+            *va_arg(ap, const float **) = td->td_refblackwhite;
+            return (1);
     }
-	case TIFFTAG_DOTRANGE:
-		*va_arg(ap, uint16 *) = 0;
-		*va_arg(ap, uint16 *) = (1<<td->td_bitspersample)-1;
-		return (1);
-	case TIFFTAG_INKSET:
-		*va_arg(ap, uint16 *) = INKSET_CMYK;
-		return 1;
-	case TIFFTAG_NUMBEROFINKS:
-		*va_arg(ap, uint16 *) = 4;
-		return (1);
-	case TIFFTAG_EXTRASAMPLES:
-		*va_arg(ap, uint16 *) = td->td_extrasamples;
-		*va_arg(ap, uint16 **) = td->td_sampleinfo;
-		return (1);
-	case TIFFTAG_MATTEING:
-		*va_arg(ap, uint16 *) =
-		    (td->td_extrasamples == 1 &&
-		     td->td_sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA);
-		return (1);
-	case TIFFTAG_TILEDEPTH:
-		*va_arg(ap, uint32 *) = td->td_tiledepth;
-		return (1);
-	case TIFFTAG_DATATYPE:
-		*va_arg(ap, uint16 *) = td->td_sampleformat-1;
-		return (1);
-	case TIFFTAG_SAMPLEFORMAT:
-		*va_arg(ap, uint16 *) = td->td_sampleformat;
-                return(1);
-	case TIFFTAG_IMAGEDEPTH:
-		*va_arg(ap, uint32 *) = td->td_imagedepth;
-		return (1);
-	case TIFFTAG_YCBCRCOEFFICIENTS:
-		{
-			/* defaults are from CCIR Recommendation 601-1 */
-			static float ycbcrcoeffs[] = { 0.299f, 0.587f, 0.114f };
-			*va_arg(ap, float **) = ycbcrcoeffs;
-			return 1;
-		}
-	case TIFFTAG_YCBCRSUBSAMPLING:
-		*va_arg(ap, uint16 *) = td->td_ycbcrsubsampling[0];
-		*va_arg(ap, uint16 *) = td->td_ycbcrsubsampling[1];
-		return (1);
-	case TIFFTAG_YCBCRPOSITIONING:
-		*va_arg(ap, uint16 *) = td->td_ycbcrpositioning;
-		return (1);
-	case TIFFTAG_WHITEPOINT:
-		{
-			static float whitepoint[2];
-
-			/* TIFF 6.0 specification tells that it is no default
-			   value for the WhitePoint, but AdobePhotoshop TIFF
-			   Technical Note tells that it should be CIE D50. */
-			whitepoint[0] =	D50_X0 / (D50_X0 + D50_Y0 + D50_Z0);
-			whitepoint[1] =	D50_Y0 / (D50_X0 + D50_Y0 + D50_Z0);
-			*va_arg(ap, float **) = whitepoint;
-			return 1;
-		}
-	case TIFFTAG_TRANSFERFUNCTION:
-		if (!td->td_transferfunction[0] &&
-		    !TIFFDefaultTransferFunction(td)) {
-			TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "No space for \"TransferFunction\" tag");
-			return (0);
-		}
-		*va_arg(ap, uint16 **) = td->td_transferfunction[0];
-		if (td->td_samplesperpixel - td->td_extrasamples > 1) {
-			*va_arg(ap, uint16 **) = td->td_transferfunction[1];
-			*va_arg(ap, uint16 **) = td->td_transferfunction[2];
-		}
-		return (1);
-	case TIFFTAG_REFERENCEBLACKWHITE:
-		if (!td->td_refblackwhite && !TIFFDefaultRefBlackWhite(td))
-			return (0);
-		*va_arg(ap, float **) = td->td_refblackwhite;
-		return (1);
-	}
-	return 0;
+    return 0;
 }
 
 /*
  * Like TIFFGetField, but return any default
  * value if the tag is not present in the directory.
  */
-int
-TIFFGetFieldDefaulted(TIFF* tif, uint32 tag, ...)
+int TIFFGetFieldDefaulted(TIFF *tif, uint32_t tag, ...)
 {
-	int ok;
-	va_list ap;
+    int ok;
+    va_list ap;
 
-	va_start(ap, tag);
-	ok =  TIFFVGetFieldDefaulted(tif, tag, ap);
-	va_end(ap);
-	return (ok);
+    va_start(ap, tag);
+    ok = TIFFVGetFieldDefaulted(tif, tag, ap);
+    va_end(ap);
+    return (ok);
 }
 
-struct _Int64Parts {
-	int32 low, high;
+struct _Int64Parts
+{
+    int32_t low, high;
 };
 
-typedef union {
-	struct _Int64Parts part;
-	int64 value;
+typedef union
+{
+    struct _Int64Parts part;
+    int64_t value;
 } _Int64;
 
-float
-_TIFFUInt64ToFloat(uint64 ui64)
+float _TIFFUInt64ToFloat(uint64_t ui64)
 {
-	_Int64 i;
+    _Int64 i;
 
-	i.value = ui64;
-	if (i.part.high >= 0) {
-		return (float)i.value;
-	} else {
-		long double df;
-		df = (long double)i.value;
-		df += 18446744073709551616.0; /* adding 2**64 */
-		return (float)df;
-	}
+    i.value = ui64;
+    if (i.part.high >= 0)
+    {
+        return (float)i.value;
+    }
+    else
+    {
+        long double df;
+        df = (long double)i.value;
+        df += 18446744073709551616.0; /* adding 2**64 */
+        return (float)df;
+    }
 }
 
-double
-_TIFFUInt64ToDouble(uint64 ui64)
+double _TIFFUInt64ToDouble(uint64_t ui64)
 {
-	_Int64 i;
+    _Int64 i;
 
-	i.value = ui64;
-	if (i.part.high >= 0) {
-		return (double)i.value;
-	} else {
-		long double df;
-		df = (long double)i.value;
-		df += 18446744073709551616.0; /* adding 2**64 */
-		return (double)df;
-	}
+    i.value = ui64;
+    if (i.part.high >= 0)
+    {
+        return (double)i.value;
+    }
+    else
+    {
+        long double df;
+        df = (long double)i.value;
+        df += 18446744073709551616.0; /* adding 2**64 */
+        return (double)df;
+    }
 }
 
-float _TIFFClampDoubleToFloat( double val )
+float _TIFFClampDoubleToFloat(double val)
 {
-    if( val > FLT_MAX )
+    if (val > FLT_MAX)
         return FLT_MAX;
-    if( val < -FLT_MAX )
+    if (val < -FLT_MAX)
         return -FLT_MAX;
     return (float)val;
 }
 
-int _TIFFSeekOK(TIFF* tif, toff_t off)
+uint32_t _TIFFClampDoubleToUInt32(double val)
+{
+    if (val < 0)
+        return 0;
+    if (val > 0xFFFFFFFFU || val != val)
+        return 0xFFFFFFFFU;
+    return (uint32_t)val;
+}
+
+int _TIFFSeekOK(TIFF *tif, toff_t off)
 {
     /* Huge offsets, especially -1 / UINT64_MAX, can cause issues */
     /* See http://bugzilla.maptools.org/show_bug.cgi?id=2726 */
-    return off <= (~(uint64)0)/2 && TIFFSeekFile(tif,off,SEEK_SET)==off;
+    return off <= (~(uint64_t)0) / 2 && TIFFSeekFile(tif, off, SEEK_SET) == off;
 }
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_close.c b/third_party/libtiff/tif_close.c
index e4228df..907d7f1 100644
--- a/third_party/libtiff/tif_close.c
+++ b/third_party/libtiff/tif_close.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -37,70 +37,98 @@
  * completely freed, so you should save opened file handle and pointer
  * to the close procedure in external variables before calling
  * _TIFFCleanup(), if you will need these ones to close the file.
- * 
+ *
  * @param tif A TIFF pointer.
  */
 
-void
-TIFFCleanup(TIFF* tif)
+void TIFFCleanup(TIFF *tif)
 {
-	/*
-         * Flush buffered data and directory (if dirty).
-         */
-	if (tif->tif_mode != O_RDONLY)
-		TIFFFlush(tif);
-	(*tif->tif_cleanup)(tif);
-	TIFFFreeDirectory(tif);
+    /*
+     * Flush buffered data and directory (if dirty).
+     */
+    if (tif->tif_mode != O_RDONLY)
+        TIFFFlush(tif);
+    (*tif->tif_cleanup)(tif);
+    TIFFFreeDirectory(tif);
 
-	if (tif->tif_dirlist)
-		_TIFFfree(tif->tif_dirlist);
+    _TIFFCleanupIFDOffsetAndNumberMaps(tif);
 
-	/*
-         * Clean up client info links.
-         */
-	while( tif->tif_clientinfo )
-	{
-		TIFFClientInfoLink *psLink = tif->tif_clientinfo;
+    /*
+     * Clean up client info links.
+     */
+    while (tif->tif_clientinfo)
+    {
+        TIFFClientInfoLink *psLink = tif->tif_clientinfo;
 
-		tif->tif_clientinfo = psLink->next;
-		_TIFFfree( psLink->name );
-		_TIFFfree( psLink );
-	}
+        tif->tif_clientinfo = psLink->next;
+        _TIFFfreeExt(tif, psLink->name);
+        _TIFFfreeExt(tif, psLink);
+    }
 
-	if (tif->tif_rawdata && (tif->tif_flags&TIFF_MYBUFFER))
-		_TIFFfree(tif->tif_rawdata);
-	if (isMapped(tif))
-		TIFFUnmapFileContents(tif, tif->tif_base, (toff_t)tif->tif_size);
+    if (tif->tif_rawdata && (tif->tif_flags & TIFF_MYBUFFER))
+        _TIFFfreeExt(tif, tif->tif_rawdata);
+    if (isMapped(tif))
+        TIFFUnmapFileContents(tif, tif->tif_base, (toff_t)tif->tif_size);
 
-	/*
-         * Clean up custom fields.
-         */
-	if (tif->tif_fields && tif->tif_nfields > 0) {
-		uint32 i;
+    /*
+     * Clean up custom fields.
+     */
+    if (tif->tif_fields && tif->tif_nfields > 0)
+    {
+        uint32_t i;
 
-		for (i = 0; i < tif->tif_nfields; i++) {
-			TIFFField *fld = tif->tif_fields[i];
-			if (fld->field_bit == FIELD_CUSTOM &&
-			    strncmp("Tag ", fld->field_name, 4) == 0) {
-				_TIFFfree(fld->field_name);
-				_TIFFfree(fld);
-			}
-		}
-
-		_TIFFfree(tif->tif_fields);
-	}
-
-        if (tif->tif_nfieldscompat > 0) {
-                uint32 i;
-
-                for (i = 0; i < tif->tif_nfieldscompat; i++) {
-                        if (tif->tif_fieldscompat[i].allocated_size)
-                                _TIFFfree(tif->tif_fieldscompat[i].fields);
+        for (i = 0; i < tif->tif_nfields; i++)
+        {
+            TIFFField *fld = tif->tif_fields[i];
+            if (fld->field_name != NULL)
+            {
+                if (fld->field_bit == FIELD_CUSTOM &&
+                    /* caution: tif_fields[i] must not be the beginning of a
+                     * fields-array. Otherwise the following tags are also freed
+                     * with the first free().
+                     */
+                    TIFFFieldIsAnonymous(fld))
+                {
+                    _TIFFfreeExt(tif, fld->field_name);
+                    _TIFFfreeExt(tif, fld);
                 }
-                _TIFFfree(tif->tif_fieldscompat);
+            }
         }
 
-	_TIFFfree(tif);
+        _TIFFfreeExt(tif, tif->tif_fields);
+    }
+
+    if (tif->tif_nfieldscompat > 0)
+    {
+        uint32_t i;
+
+        for (i = 0; i < tif->tif_nfieldscompat; i++)
+        {
+            if (tif->tif_fieldscompat[i].allocated_size)
+                _TIFFfreeExt(tif, tif->tif_fieldscompat[i].fields);
+        }
+        _TIFFfreeExt(tif, tif->tif_fieldscompat);
+    }
+
+    _TIFFfreeExt(NULL, tif);
+}
+
+/************************************************************************/
+/*                    _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;
+    }
 }
 
 /************************************************************************/
@@ -113,26 +141,18 @@
  * TIFFClose closes a file that was previously opened with TIFFOpen().
  * Any buffered data are flushed to the file, including the contents of
  * the current directory (if modified); and all resources are reclaimed.
- * 
+ *
  * @param tif A TIFF pointer.
  */
 
-void
-TIFFClose(TIFF* tif)
+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);
+    }
 }
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_codec.c b/third_party/libtiff/tif_codec.c
index b6c04f0..d499b63 100644
--- a/third_party/libtiff/tif_codec.c
+++ b/third_party/libtiff/tif_codec.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -29,7 +29,7 @@
  */
 #include "tiffiop.h"
 
-static int NotConfigured(TIFF*, int);
+static int NotConfigured(TIFF *, int);
 
 #ifndef LZW_SUPPORT
 #define TIFFInitLZW NotConfigured
@@ -67,6 +67,9 @@
 #ifndef LOGLUV_SUPPORT
 #define TIFFInitSGILog NotConfigured
 #endif
+#ifndef LERC_SUPPORT
+#define TIFFInitLERC NotConfigured
+#endif
 #ifndef LZMA_SUPPORT
 #define TIFFInitLZMA NotConfigured
 #endif
@@ -80,58 +83,53 @@
 /*
  * Compression schemes statically built into the library.
  */
-#ifdef VMS
 const TIFFCodec _TIFFBuiltinCODECS[] = {
-#else
-TIFFCodec _TIFFBuiltinCODECS[] = {
-#endif
-    { "None",		COMPRESSION_NONE,	TIFFInitDumpMode },
-    { "LZW",		COMPRESSION_LZW,	TIFFInitLZW },
-    { "PackBits",	COMPRESSION_PACKBITS,	TIFFInitPackBits },
-    { "ThunderScan",	COMPRESSION_THUNDERSCAN,TIFFInitThunderScan },
-    { "NeXT",		COMPRESSION_NEXT,	TIFFInitNeXT },
-    { "JPEG",		COMPRESSION_JPEG,	TIFFInitJPEG },
-    { "Old-style JPEG",	COMPRESSION_OJPEG,	TIFFInitOJPEG },
-    { "CCITT RLE",	COMPRESSION_CCITTRLE,	TIFFInitCCITTRLE },
-    { "CCITT RLE/W",	COMPRESSION_CCITTRLEW,	TIFFInitCCITTRLEW },
-    { "CCITT Group 3",	COMPRESSION_CCITTFAX3,	TIFFInitCCITTFax3 },
-    { "CCITT Group 4",	COMPRESSION_CCITTFAX4,	TIFFInitCCITTFax4 },
-    { "ISO JBIG",	COMPRESSION_JBIG,	TIFFInitJBIG },
-    { "Deflate",	COMPRESSION_DEFLATE,	TIFFInitZIP },
-    { "AdobeDeflate",   COMPRESSION_ADOBE_DEFLATE , TIFFInitZIP }, 
-    { "PixarLog",	COMPRESSION_PIXARLOG,	TIFFInitPixarLog },
-    { "SGILog",		COMPRESSION_SGILOG,	TIFFInitSGILog },
-    { "SGILog24",	COMPRESSION_SGILOG24,	TIFFInitSGILog },
-    { "LZMA",		COMPRESSION_LZMA,	TIFFInitLZMA },
-    { "ZSTD",		COMPRESSION_ZSTD,	TIFFInitZSTD },
-    { "WEBP",		COMPRESSION_WEBP,	TIFFInitWebP },
-    { NULL,             0,                      NULL }
-};
+    {"None", COMPRESSION_NONE, TIFFInitDumpMode},
+    {"LZW", COMPRESSION_LZW, TIFFInitLZW},
+    {"PackBits", COMPRESSION_PACKBITS, TIFFInitPackBits},
+    {"ThunderScan", COMPRESSION_THUNDERSCAN, TIFFInitThunderScan},
+    {"NeXT", COMPRESSION_NEXT, TIFFInitNeXT},
+    {"JPEG", COMPRESSION_JPEG, TIFFInitJPEG},
+    {"Old-style JPEG", COMPRESSION_OJPEG, TIFFInitOJPEG},
+    {"CCITT RLE", COMPRESSION_CCITTRLE, TIFFInitCCITTRLE},
+    {"CCITT RLE/W", COMPRESSION_CCITTRLEW, TIFFInitCCITTRLEW},
+    {"CCITT Group 3", COMPRESSION_CCITTFAX3, TIFFInitCCITTFax3},
+    {"CCITT Group 4", COMPRESSION_CCITTFAX4, TIFFInitCCITTFax4},
+    {"ISO JBIG", COMPRESSION_JBIG, TIFFInitJBIG},
+    {"Deflate", COMPRESSION_DEFLATE, TIFFInitZIP},
+    {"AdobeDeflate", COMPRESSION_ADOBE_DEFLATE, TIFFInitZIP},
+    {"PixarLog", COMPRESSION_PIXARLOG, TIFFInitPixarLog},
+    {"SGILog", COMPRESSION_SGILOG, TIFFInitSGILog},
+    {"SGILog24", COMPRESSION_SGILOG24, TIFFInitSGILog},
+    {"LZMA", COMPRESSION_LZMA, TIFFInitLZMA},
+    {"ZSTD", COMPRESSION_ZSTD, TIFFInitZSTD},
+    {"WEBP", COMPRESSION_WEBP, TIFFInitWebP},
+    {"LERC", COMPRESSION_LERC, TIFFInitLERC},
+    {NULL, 0, NULL}};
 
-static int
-_notConfigured(TIFF* tif)
+static int _notConfigured(TIFF *tif)
 {
-	const TIFFCodec* c = TIFFFindCODEC(tif->tif_dir.td_compression);
-        char compression_code[20];
-        
-        sprintf(compression_code, "%d",tif->tif_dir.td_compression );
-	TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-                     "%s compression support is not configured", 
-                     c ? c->name : compression_code );
-	return (0);
+    const TIFFCodec *c = TIFFFindCODEC(tif->tif_dir.td_compression);
+    char compression_code[20];
+
+    snprintf(compression_code, sizeof(compression_code), "%" PRIu16,
+             tif->tif_dir.td_compression);
+    TIFFErrorExtR(tif, tif->tif_name,
+                  "%s compression support is not configured",
+                  c ? c->name : compression_code);
+    return (0);
 }
 
-static int
-NotConfigured(TIFF* tif, int scheme)
+static int NotConfigured(TIFF *tif, int scheme)
 {
-	(void) scheme;
+    (void)scheme;
 
-	tif->tif_fixuptags = _notConfigured;
-	tif->tif_decodestatus = FALSE;
-	tif->tif_setupdecode = _notConfigured;
-	tif->tif_encodestatus = FALSE;
-	tif->tif_setupencode = _notConfigured;
-	return (1);
+    tif->tif_fixuptags = _notConfigured;
+    tif->tif_decodestatus = FALSE;
+    tif->tif_setupdecode = _notConfigured;
+    tif->tif_encodestatus = FALSE;
+    tif->tif_setupencode = _notConfigured;
+    return (1);
 }
 
 /************************************************************************/
@@ -145,27 +143,21 @@
  * 0 will be returned.
  */
 
-int
-TIFFIsCODECConfigured(uint16 scheme)
+int TIFFIsCODECConfigured(uint16_t scheme)
 {
-	const TIFFCodec* codec = TIFFFindCODEC(scheme);
+    const TIFFCodec *codec = TIFFFindCODEC(scheme);
 
-	if(codec == NULL) {
-		return 0;
-	}
-	if(codec->init == NULL) {
-		return 0;
-	}
-	if(codec->init != NotConfigured){
-		return 1;
-	}
-	return 0;
+    if (codec == NULL)
+    {
+        return 0;
+    }
+    if (codec->init == NULL)
+    {
+        return 0;
+    }
+    if (codec->init != NotConfigured)
+    {
+        return 1;
+    }
+    return 0;
 }
-
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_color.c b/third_party/libtiff/tif_color.c
index 8fae40e..2d7dcac 100644
--- a/third_party/libtiff/tif_color.c
+++ b/third_party/libtiff/tif_color.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -40,173 +40,191 @@
 /*
  * Convert color value from the CIE L*a*b* 1976 space to CIE XYZ.
  */
-void
-TIFFCIELabToXYZ(TIFFCIELabToRGB *cielab, uint32 l, int32 a, int32 b,
-		float *X, float *Y, float *Z)
+void TIFFCIELabToXYZ(TIFFCIELabToRGB *cielab, uint32_t l, int32_t a, int32_t b,
+                     float *X, float *Y, float *Z)
 {
-	float L = (float)l * 100.0F / 255.0F;
-	float cby, tmp;
-
-	if( L < 8.856F ) {
-		*Y = (L * cielab->Y0) / 903.292F;
-		cby = 7.787F * (*Y / cielab->Y0) + 16.0F / 116.0F;
-	} else {
-		cby = (L + 16.0F) / 116.0F;
-		*Y = cielab->Y0 * cby * cby * cby;
-	}
-
-	tmp = (float)a / 500.0F + cby;
-	if( tmp < 0.2069F )
-		*X = cielab->X0 * (tmp - 0.13793F) / 7.787F;
-	else    
-		*X = cielab->X0 * tmp * tmp * tmp;
-
-	tmp = cby - (float)b / 200.0F;
-	if( tmp < 0.2069F )
-		*Z = cielab->Z0 * (tmp - 0.13793F) / 7.787F;
-	else    
-		*Z = cielab->Z0 * tmp * tmp * tmp;
+    TIFFCIELab16ToXYZ(cielab, l * 257, a * 256, b * 256, X, Y, Z);
 }
 
-#define RINT(R) ((uint32)((R)>0?((R)+0.5):((R)-0.5)))
+/*
+ * For CIELab encoded in 16 bits, L is an unsigned integer range [0,65535].
+ * The a* and b* components are signed integers range [-32768,32767]. The 16
+ * bit chrominance values are encoded as 256 times the 1976 CIE a* and b*
+ * values
+ */
+void TIFFCIELab16ToXYZ(TIFFCIELabToRGB *cielab, uint32_t l, int32_t a,
+                       int32_t b, float *X, float *Y, float *Z)
+{
+    float L = (float)l * 100.0F / 65535.0F;
+    float cby, tmp;
+
+    if (L < 8.856F)
+    {
+        *Y = (L * cielab->Y0) / 903.292F;
+        cby = 7.787F * (*Y / cielab->Y0) + 16.0F / 116.0F;
+    }
+    else
+    {
+        cby = (L + 16.0F) / 116.0F;
+        *Y = cielab->Y0 * cby * cby * cby;
+    }
+
+    tmp = (float)a / 256.0F / 500.0F + cby;
+    if (tmp < 0.2069F)
+        *X = cielab->X0 * (tmp - 0.13793F) / 7.787F;
+    else
+        *X = cielab->X0 * tmp * tmp * tmp;
+
+    tmp = cby - (float)b / 256.0F / 200.0F;
+    if (tmp < 0.2069F)
+        *Z = cielab->Z0 * (tmp - 0.13793F) / 7.787F;
+    else
+        *Z = cielab->Z0 * tmp * tmp * tmp;
+}
+
+#define RINT(R) ((uint32_t)((R) > 0 ? ((R) + 0.5) : ((R)-0.5)))
 /*
  * Convert color value from the XYZ space to RGB.
  */
-void
-TIFFXYZToRGB(TIFFCIELabToRGB *cielab, float X, float Y, float Z,
-	     uint32 *r, uint32 *g, uint32 *b)
+void TIFFXYZToRGB(TIFFCIELabToRGB *cielab, float X, float Y, float Z,
+                  uint32_t *r, uint32_t *g, uint32_t *b)
 {
-	int i;
-	float Yr, Yg, Yb;
-	float *matrix = &cielab->display.d_mat[0][0];
+    int i;
+    float Yr, Yg, Yb;
+    float *matrix = &cielab->display.d_mat[0][0];
 
-	/* Multiply through the matrix to get luminosity values. */
-	Yr =  matrix[0] * X + matrix[1] * Y + matrix[2] * Z;
-	Yg =  matrix[3] * X + matrix[4] * Y + matrix[5] * Z;
-	Yb =  matrix[6] * X + matrix[7] * Y + matrix[8] * Z;
+    /* Multiply through the matrix to get luminosity values. */
+    Yr = matrix[0] * X + matrix[1] * Y + matrix[2] * Z;
+    Yg = matrix[3] * X + matrix[4] * Y + matrix[5] * Z;
+    Yb = matrix[6] * X + matrix[7] * Y + matrix[8] * Z;
 
-	/* Clip input */
-	Yr = TIFFmax(Yr, cielab->display.d_Y0R);
-	Yg = TIFFmax(Yg, cielab->display.d_Y0G);
-	Yb = TIFFmax(Yb, cielab->display.d_Y0B);
+    /* Clip input */
+    Yr = TIFFmax(Yr, cielab->display.d_Y0R);
+    Yg = TIFFmax(Yg, cielab->display.d_Y0G);
+    Yb = TIFFmax(Yb, cielab->display.d_Y0B);
 
-	/* Avoid overflow in case of wrong input values */
-	Yr = TIFFmin(Yr, cielab->display.d_YCR);
-	Yg = TIFFmin(Yg, cielab->display.d_YCG);
-	Yb = TIFFmin(Yb, cielab->display.d_YCB);
+    /* Avoid overflow in case of wrong input values */
+    Yr = TIFFmin(Yr, cielab->display.d_YCR);
+    Yg = TIFFmin(Yg, cielab->display.d_YCG);
+    Yb = TIFFmin(Yb, cielab->display.d_YCB);
 
-	/* Turn luminosity to colour value. */
-	i = (int)((Yr - cielab->display.d_Y0R) / cielab->rstep);
-	i = TIFFmin(cielab->range, i);
-	*r = RINT(cielab->Yr2r[i]);
+    /* Turn luminosity to colour value. */
+    i = (int)((Yr - cielab->display.d_Y0R) / cielab->rstep);
+    i = TIFFmin(cielab->range, i);
+    *r = RINT(cielab->Yr2r[i]);
 
-	i = (int)((Yg - cielab->display.d_Y0G) / cielab->gstep);
-	i = TIFFmin(cielab->range, i);
-	*g = RINT(cielab->Yg2g[i]);
+    i = (int)((Yg - cielab->display.d_Y0G) / cielab->gstep);
+    i = TIFFmin(cielab->range, i);
+    *g = RINT(cielab->Yg2g[i]);
 
-	i = (int)((Yb - cielab->display.d_Y0B) / cielab->bstep);
-	i = TIFFmin(cielab->range, i);
-	*b = RINT(cielab->Yb2b[i]);
+    i = (int)((Yb - cielab->display.d_Y0B) / cielab->bstep);
+    i = TIFFmin(cielab->range, i);
+    *b = RINT(cielab->Yb2b[i]);
 
-	/* Clip output. */
-	*r = TIFFmin(*r, cielab->display.d_Vrwr);
-	*g = TIFFmin(*g, cielab->display.d_Vrwg);
-	*b = TIFFmin(*b, cielab->display.d_Vrwb);
+    /* Clip output. */
+    *r = TIFFmin(*r, cielab->display.d_Vrwr);
+    *g = TIFFmin(*g, cielab->display.d_Vrwg);
+    *b = TIFFmin(*b, cielab->display.d_Vrwb);
 }
 #undef RINT
 
-/* 
+/*
  * Allocate conversion state structures and make look_up tables for
  * the Yr,Yb,Yg <=> r,g,b conversions.
  */
-int
-TIFFCIELabToRGBInit(TIFFCIELabToRGB* cielab,
-		    const TIFFDisplay *display, float *refWhite)
+int TIFFCIELabToRGBInit(TIFFCIELabToRGB *cielab, const TIFFDisplay *display,
+                        float *refWhite)
 {
-	int i;
-	double dfGamma;
+    int i;
+    double dfGamma;
 
-	cielab->range = CIELABTORGB_TABLE_RANGE;
+    cielab->range = CIELABTORGB_TABLE_RANGE;
 
-	_TIFFmemcpy(&cielab->display, display, sizeof(TIFFDisplay));
+    _TIFFmemcpy(&cielab->display, display, sizeof(TIFFDisplay));
 
-	/* Red */
-	dfGamma = 1.0 / cielab->display.d_gammaR ;
-	cielab->rstep =
-		(cielab->display.d_YCR - cielab->display.d_Y0R)	/ cielab->range;
-	for(i = 0; i <= cielab->range; i++) {
-		cielab->Yr2r[i] = cielab->display.d_Vrwr
-		    * ((float)pow((double)i / cielab->range, dfGamma));
-	}
+    /* Red */
+    dfGamma = 1.0 / cielab->display.d_gammaR;
+    cielab->rstep =
+        (cielab->display.d_YCR - cielab->display.d_Y0R) / cielab->range;
+    for (i = 0; i <= cielab->range; i++)
+    {
+        cielab->Yr2r[i] = cielab->display.d_Vrwr *
+                          ((float)pow((double)i / cielab->range, dfGamma));
+    }
 
-	/* Green */
-	dfGamma = 1.0 / cielab->display.d_gammaG ;
-	cielab->gstep =
-	    (cielab->display.d_YCR - cielab->display.d_Y0R) / cielab->range;
-	for(i = 0; i <= cielab->range; i++) {
-		cielab->Yg2g[i] = cielab->display.d_Vrwg
-		    * ((float)pow((double)i / cielab->range, dfGamma));
-	}
+    /* Green */
+    dfGamma = 1.0 / cielab->display.d_gammaG;
+    cielab->gstep =
+        (cielab->display.d_YCR - cielab->display.d_Y0R) / cielab->range;
+    for (i = 0; i <= cielab->range; i++)
+    {
+        cielab->Yg2g[i] = cielab->display.d_Vrwg *
+                          ((float)pow((double)i / cielab->range, dfGamma));
+    }
 
-	/* Blue */
-	dfGamma = 1.0 / cielab->display.d_gammaB ;
-	cielab->bstep =
-	    (cielab->display.d_YCR - cielab->display.d_Y0R) / cielab->range;
-	for(i = 0; i <= cielab->range; i++) {
-		cielab->Yb2b[i] = cielab->display.d_Vrwb
-		    * ((float)pow((double)i / cielab->range, dfGamma));
-	}
+    /* Blue */
+    dfGamma = 1.0 / cielab->display.d_gammaB;
+    cielab->bstep =
+        (cielab->display.d_YCR - cielab->display.d_Y0R) / cielab->range;
+    for (i = 0; i <= cielab->range; i++)
+    {
+        cielab->Yb2b[i] = cielab->display.d_Vrwb *
+                          ((float)pow((double)i / cielab->range, dfGamma));
+    }
 
-	/* Init reference white point */
-	cielab->X0 = refWhite[0];
-	cielab->Y0 = refWhite[1];
-	cielab->Z0 = refWhite[2];
+    /* Init reference white point */
+    cielab->X0 = refWhite[0];
+    cielab->Y0 = refWhite[1];
+    cielab->Z0 = refWhite[2];
 
-	return 0;
+    return 0;
 }
 
-/* 
+/*
  * Convert color value from the YCbCr space to RGB.
  * The colorspace conversion algorithm comes from the IJG v5a code;
  * see below for more information on how it works.
  */
-#define	SHIFT			16
-#define	FIX(x)			((int32)((x) * (1L<<SHIFT) + 0.5))
-#define	ONE_HALF		((int32)(1<<(SHIFT-1)))
-#define	Code2V(c, RB, RW, CR)	((((c)-(int32)(RB))*(float)(CR))/(float)(((RW)-(RB)!=0) ? ((RW)-(RB)) : 1))
-#define	CLAMP(f,min,max)	((f)<(min)?(min):(f)>(max)?(max):(f))
-#define HICLAMP(f,max)		((f)>(max)?(max):(f))
+#define SHIFT 16
+#define FIX(x) ((int32_t)((x) * (1L << SHIFT) + 0.5))
+#define ONE_HALF ((int32_t)(1 << (SHIFT - 1)))
+#define Code2V(c, RB, RW, CR)                                                  \
+    ((((c) - (int32_t)(RB)) * (float)(CR)) /                                   \
+     (float)(((RW) - (RB) != 0) ? ((RW) - (RB)) : 1))
+/* !((f)>=(min)) written that way to deal with NaN */
+#define CLAMP(f, min, max)                                                     \
+    ((!((f) >= (min))) ? (min) : (f) > (max) ? (max) : (f))
+#define HICLAMP(f, max) ((f) > (max) ? (max) : (f))
 
-void
-TIFFYCbCrtoRGB(TIFFYCbCrToRGB *ycbcr, uint32 Y, int32 Cb, int32 Cr,
-	       uint32 *r, uint32 *g, uint32 *b)
+void TIFFYCbCrtoRGB(TIFFYCbCrToRGB *ycbcr, uint32_t Y, int32_t Cb, int32_t Cr,
+                    uint32_t *r, uint32_t *g, uint32_t *b)
 {
-	int32 i;
+    int32_t i;
 
-	/* XXX: Only 8-bit YCbCr input supported for now */
-	Y = HICLAMP(Y, 255);
-	Cb = CLAMP(Cb, 0, 255);
-	Cr = CLAMP(Cr, 0, 255);
+    /* XXX: Only 8-bit YCbCr input supported for now */
+    Y = HICLAMP(Y, 255);
+    Cb = CLAMP(Cb, 0, 255);
+    Cr = CLAMP(Cr, 0, 255);
 
-	i = ycbcr->Y_tab[Y] + ycbcr->Cr_r_tab[Cr];
-	*r = CLAMP(i, 0, 255);
-	i = ycbcr->Y_tab[Y]
-	    + (int)((ycbcr->Cb_g_tab[Cb] + ycbcr->Cr_g_tab[Cr]) >> SHIFT);
-	*g = CLAMP(i, 0, 255);
-	i = ycbcr->Y_tab[Y] + ycbcr->Cb_b_tab[Cb];
-	*b = CLAMP(i, 0, 255);
+    i = ycbcr->Y_tab[Y] + ycbcr->Cr_r_tab[Cr];
+    *r = CLAMP(i, 0, 255);
+    i = ycbcr->Y_tab[Y] +
+        (int)((ycbcr->Cb_g_tab[Cb] + ycbcr->Cr_g_tab[Cr]) >> SHIFT);
+    *g = CLAMP(i, 0, 255);
+    i = ycbcr->Y_tab[Y] + ycbcr->Cb_b_tab[Cb];
+    *b = CLAMP(i, 0, 255);
 }
 
 /* Clamp function for sanitization purposes. Normally clamping should not */
 /* occur for well behaved chroma and refBlackWhite coefficients */
 static float CLAMPw(float v, float vmin, float vmax)
 {
-    if( v < vmin )
+    if (v < vmin)
     {
         /* printf("%f clamped to %f\n", v, vmin); */
         return vmin;
     }
-    if( v > vmax )
+    if (v > vmax)
     {
         /* printf("%f clamped to %f\n", v, vmax); */
         return vmax;
@@ -230,78 +248,75 @@
  * pre-calculating possible values indexed by Cb and Cr (this code
  * assumes conversion is being done for 8-bit samples).
  */
-int
-TIFFYCbCrToRGBInit(TIFFYCbCrToRGB* ycbcr, float *luma, float *refBlackWhite)
+int TIFFYCbCrToRGBInit(TIFFYCbCrToRGB *ycbcr, float *luma, float *refBlackWhite)
 {
-    TIFFRGBValue* clamptab;
+    TIFFRGBValue *clamptab;
     int i;
-    
-#define LumaRed	    luma[0]
-#define LumaGreen   luma[1]
-#define LumaBlue    luma[2]
 
-    clamptab = (TIFFRGBValue*)(
-	(uint8*) ycbcr+TIFFroundup_32(sizeof (TIFFYCbCrToRGB), sizeof (long)));  
-    _TIFFmemset(clamptab, 0, 256);		/* v < 0 => 0 */
+#define LumaRed luma[0]
+#define LumaGreen luma[1]
+#define LumaBlue luma[2]
+
+    clamptab =
+        (TIFFRGBValue *)((uint8_t *)ycbcr +
+                         TIFFroundup_32(sizeof(TIFFYCbCrToRGB), sizeof(long)));
+    _TIFFmemset(clamptab, 0, 256); /* v < 0 => 0 */
     ycbcr->clamptab = (clamptab += 256);
     for (i = 0; i < 256; i++)
-	clamptab[i] = (TIFFRGBValue) i;
-    _TIFFmemset(clamptab+256, 255, 2*256);	/* v > 255 => 255 */
-    ycbcr->Cr_r_tab = (int*) (clamptab + 3*256);
+        clamptab[i] = (TIFFRGBValue)i;
+    _TIFFmemset(clamptab + 256, 255, 2 * 256); /* v > 255 => 255 */
+    ycbcr->Cr_r_tab = (int *)(clamptab + 3 * 256);
     ycbcr->Cb_b_tab = ycbcr->Cr_r_tab + 256;
-    ycbcr->Cr_g_tab = (int32*) (ycbcr->Cb_b_tab + 256);
+    ycbcr->Cr_g_tab = (int32_t *)(ycbcr->Cb_b_tab + 256);
     ycbcr->Cb_g_tab = ycbcr->Cr_g_tab + 256;
     ycbcr->Y_tab = ycbcr->Cb_g_tab + 256;
 
-    { float f1 = 2-2*LumaRed;		int32 D1 = FIX(CLAMP(f1,0.0F,2.0F));
-      float f2 = LumaRed*f1/LumaGreen;	int32 D2 = -FIX(CLAMP(f2,0.0F,2.0F));
-      float f3 = 2-2*LumaBlue;		int32 D3 = FIX(CLAMP(f3,0.0F,2.0F));
-      float f4 = LumaBlue*f3/LumaGreen;	int32 D4 = -FIX(CLAMP(f4,0.0F,2.0F));
-      int x;
+    {
+        float f1 = 2 - 2 * LumaRed;
+        int32_t D1 = FIX(CLAMP(f1, 0.0F, 2.0F));
+        float f2 = LumaRed * f1 / LumaGreen;
+        int32_t D2 = -FIX(CLAMP(f2, 0.0F, 2.0F));
+        float f3 = 2 - 2 * LumaBlue;
+        int32_t D3 = FIX(CLAMP(f3, 0.0F, 2.0F));
+        float f4 = LumaBlue * f3 / LumaGreen;
+        int32_t D4 = -FIX(CLAMP(f4, 0.0F, 2.0F));
+        int x;
 
 #undef LumaBlue
 #undef LumaGreen
 #undef LumaRed
-      
-      /*
-       * i is the actual input pixel value in the range 0..255
-       * Cb and Cr values are in the range -128..127 (actually
-       * they are in a range defined by the ReferenceBlackWhite
-       * tag) so there is some range shifting to do here when
-       * constructing tables indexed by the raw pixel data.
-       */
-      for (i = 0, x = -128; i < 256; i++, x++) {
-	    int32 Cr = (int32)CLAMPw(Code2V(x, refBlackWhite[4] - 128.0F,
-			    refBlackWhite[5] - 128.0F, 127),
-                            -128.0F * 32, 128.0F * 32);
-	    int32 Cb = (int32)CLAMPw(Code2V(x, refBlackWhite[2] - 128.0F,
-			    refBlackWhite[3] - 128.0F, 127),
-                            -128.0F * 32, 128.0F * 32);
 
-	    ycbcr->Cr_r_tab[i] = (int32)((D1*Cr + ONE_HALF)>>SHIFT);
-	    ycbcr->Cb_b_tab[i] = (int32)((D3*Cb + ONE_HALF)>>SHIFT);
-	    ycbcr->Cr_g_tab[i] = D2*Cr;
-	    ycbcr->Cb_g_tab[i] = D4*Cb + ONE_HALF;
-	    ycbcr->Y_tab[i] =
-		    (int32)CLAMPw(Code2V(x + 128, refBlackWhite[0], refBlackWhite[1], 255),
-                                  -128.0F * 32, 128.0F * 32);
-      }
+        /*
+         * i is the actual input pixel value in the range 0..255
+         * Cb and Cr values are in the range -128..127 (actually
+         * they are in a range defined by the ReferenceBlackWhite
+         * tag) so there is some range shifting to do here when
+         * constructing tables indexed by the raw pixel data.
+         */
+        for (i = 0, x = -128; i < 256; i++, x++)
+        {
+            int32_t Cr = (int32_t)CLAMPw(Code2V(x, refBlackWhite[4] - 128.0F,
+                                                refBlackWhite[5] - 128.0F, 127),
+                                         -128.0F * 32, 128.0F * 32);
+            int32_t Cb = (int32_t)CLAMPw(Code2V(x, refBlackWhite[2] - 128.0F,
+                                                refBlackWhite[3] - 128.0F, 127),
+                                         -128.0F * 32, 128.0F * 32);
+
+            ycbcr->Cr_r_tab[i] = (int32_t)((D1 * Cr + ONE_HALF) >> SHIFT);
+            ycbcr->Cb_b_tab[i] = (int32_t)((D3 * Cb + ONE_HALF) >> SHIFT);
+            ycbcr->Cr_g_tab[i] = D2 * Cr;
+            ycbcr->Cb_g_tab[i] = D4 * Cb + ONE_HALF;
+            ycbcr->Y_tab[i] = (int32_t)CLAMPw(
+                Code2V(x + 128, refBlackWhite[0], refBlackWhite[1], 255),
+                -128.0F * 32, 128.0F * 32);
+        }
     }
 
     return 0;
 }
-#undef	HICLAMP
-#undef	CLAMP
-#undef	Code2V
-#undef	SHIFT
-#undef	ONE_HALF
-#undef	FIX
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
+#undef HICLAMP
+#undef CLAMP
+#undef Code2V
+#undef SHIFT
+#undef ONE_HALF
+#undef FIX
diff --git a/third_party/libtiff/tif_compress.c b/third_party/libtiff/tif_compress.c
index 8130ef0..c6e17d3 100644
--- a/third_party/libtiff/tif_compress.c
+++ b/third_party/libtiff/tif_compress.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -29,145 +29,152 @@
  */
 #include "tiffiop.h"
 
-static int
-TIFFNoEncode(TIFF* tif, const char* method)
+static int TIFFNoEncode(TIFF *tif, const char *method)
 {
-	const TIFFCodec* c = TIFFFindCODEC(tif->tif_dir.td_compression);
+    const TIFFCodec *c = TIFFFindCODEC(tif->tif_dir.td_compression);
 
-	if (c) {
-		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-			     "%s %s encoding is not implemented",
-			     c->name, method);
-	} else {
-		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-			"Compression scheme %u %s encoding is not implemented",
-			     tif->tif_dir.td_compression, method);
-	}
-	return (-1);
+    if (c)
+    {
+        TIFFErrorExtR(tif, tif->tif_name, "%s %s encoding is not implemented",
+                      c->name, method);
+    }
+    else
+    {
+        TIFFErrorExtR(tif, tif->tif_name,
+                      "Compression scheme %" PRIu16
+                      " %s encoding is not implemented",
+                      tif->tif_dir.td_compression, method);
+    }
+    return (-1);
 }
 
-int
-_TIFFNoRowEncode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s)
+int _TIFFNoRowEncode(TIFF *tif, uint8_t *pp, tmsize_t cc, uint16_t s)
 {
-	(void) pp; (void) cc; (void) s;
-	return (TIFFNoEncode(tif, "scanline"));
+    (void)pp;
+    (void)cc;
+    (void)s;
+    return (TIFFNoEncode(tif, "scanline"));
 }
 
-int
-_TIFFNoStripEncode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s)
+int _TIFFNoStripEncode(TIFF *tif, uint8_t *pp, tmsize_t cc, uint16_t s)
 {
-	(void) pp; (void) cc; (void) s;
-	return (TIFFNoEncode(tif, "strip"));
+    (void)pp;
+    (void)cc;
+    (void)s;
+    return (TIFFNoEncode(tif, "strip"));
 }
 
-int
-_TIFFNoTileEncode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s)
+int _TIFFNoTileEncode(TIFF *tif, uint8_t *pp, tmsize_t cc, uint16_t s)
 {
-	(void) pp; (void) cc; (void) s;
-	return (TIFFNoEncode(tif, "tile"));
+    (void)pp;
+    (void)cc;
+    (void)s;
+    return (TIFFNoEncode(tif, "tile"));
 }
 
-static int
-TIFFNoDecode(TIFF* tif, const char* method)
+static int TIFFNoDecode(TIFF *tif, const char *method)
 {
-	const TIFFCodec* c = TIFFFindCODEC(tif->tif_dir.td_compression);
+    const TIFFCodec *c = TIFFFindCODEC(tif->tif_dir.td_compression);
 
-	if (c)
-		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-			     "%s %s decoding is not implemented",
-			     c->name, method);
-	else
-		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-			     "Compression scheme %u %s decoding is not implemented",
-			     tif->tif_dir.td_compression, method);
-	return (0);
+    if (c)
+        TIFFErrorExtR(tif, tif->tif_name, "%s %s decoding is not implemented",
+                      c->name, method);
+    else
+        TIFFErrorExtR(tif, tif->tif_name,
+                      "Compression scheme %" PRIu16
+                      " %s decoding is not implemented",
+                      tif->tif_dir.td_compression, method);
+    return (0);
 }
 
-static int
-_TIFFNoFixupTags(TIFF* tif)
+static int _TIFFNoFixupTags(TIFF *tif)
 {
-	(void) tif;
-	return (1);
+    (void)tif;
+    return (1);
 }
 
-int
-_TIFFNoRowDecode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s)
+int _TIFFNoRowDecode(TIFF *tif, uint8_t *pp, tmsize_t cc, uint16_t s)
 {
-	(void) pp; (void) cc; (void) s;
-	return (TIFFNoDecode(tif, "scanline"));
+    (void)pp;
+    (void)cc;
+    (void)s;
+    return (TIFFNoDecode(tif, "scanline"));
 }
 
-int
-_TIFFNoStripDecode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s)
+int _TIFFNoStripDecode(TIFF *tif, uint8_t *pp, tmsize_t cc, uint16_t s)
 {
-	(void) pp; (void) cc; (void) s;
-	return (TIFFNoDecode(tif, "strip"));
+    (void)pp;
+    (void)cc;
+    (void)s;
+    return (TIFFNoDecode(tif, "strip"));
 }
 
-int
-_TIFFNoTileDecode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s)
+int _TIFFNoTileDecode(TIFF *tif, uint8_t *pp, tmsize_t cc, uint16_t s)
 {
-	(void) pp; (void) cc; (void) s;
-	return (TIFFNoDecode(tif, "tile"));
+    (void)pp;
+    (void)cc;
+    (void)s;
+    return (TIFFNoDecode(tif, "tile"));
 }
 
-int
-_TIFFNoSeek(TIFF* tif, uint32 off)
+int _TIFFNoSeek(TIFF *tif, uint32_t off)
 {
-	(void) off;
-	TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-		     "Compression algorithm does not support random access");
-	return (0);
+    (void)off;
+    TIFFErrorExtR(tif, tif->tif_name,
+                  "Compression algorithm does not support random access");
+    return (0);
 }
 
-int
-_TIFFNoPreCode(TIFF* tif, uint16 s)
+int _TIFFNoPreCode(TIFF *tif, uint16_t s)
 {
-	(void) tif; (void) s;
-	return (1);
+    (void)tif;
+    (void)s;
+    return (1);
 }
 
-static int _TIFFtrue(TIFF* tif) { (void) tif; return (1); }
-static void _TIFFvoid(TIFF* tif) { (void) tif; }
-
-void
-_TIFFSetDefaultCompressionState(TIFF* tif)
+static int _TIFFtrue(TIFF *tif)
 {
-	tif->tif_fixuptags = _TIFFNoFixupTags; 
-	tif->tif_decodestatus = TRUE;
-	tif->tif_setupdecode = _TIFFtrue;
-	tif->tif_predecode = _TIFFNoPreCode;
-	tif->tif_decoderow = _TIFFNoRowDecode;  
-	tif->tif_decodestrip = _TIFFNoStripDecode;
-	tif->tif_decodetile = _TIFFNoTileDecode;  
-	tif->tif_encodestatus = TRUE;
-	tif->tif_setupencode = _TIFFtrue;
-	tif->tif_preencode = _TIFFNoPreCode;
-	tif->tif_postencode = _TIFFtrue;
-	tif->tif_encoderow = _TIFFNoRowEncode;
-	tif->tif_encodestrip = _TIFFNoStripEncode;  
-	tif->tif_encodetile = _TIFFNoTileEncode;  
-	tif->tif_close = _TIFFvoid;
-	tif->tif_seek = _TIFFNoSeek;
-	tif->tif_cleanup = _TIFFvoid;
-	tif->tif_defstripsize = _TIFFDefaultStripSize;
-	tif->tif_deftilesize = _TIFFDefaultTileSize;
-	tif->tif_flags &= ~(TIFF_NOBITREV|TIFF_NOREADRAW);
+    (void)tif;
+    return (1);
+}
+static void _TIFFvoid(TIFF *tif) { (void)tif; }
+
+void _TIFFSetDefaultCompressionState(TIFF *tif)
+{
+    tif->tif_fixuptags = _TIFFNoFixupTags;
+    tif->tif_decodestatus = TRUE;
+    tif->tif_setupdecode = _TIFFtrue;
+    tif->tif_predecode = _TIFFNoPreCode;
+    tif->tif_decoderow = _TIFFNoRowDecode;
+    tif->tif_decodestrip = _TIFFNoStripDecode;
+    tif->tif_decodetile = _TIFFNoTileDecode;
+    tif->tif_encodestatus = TRUE;
+    tif->tif_setupencode = _TIFFtrue;
+    tif->tif_preencode = _TIFFNoPreCode;
+    tif->tif_postencode = _TIFFtrue;
+    tif->tif_encoderow = _TIFFNoRowEncode;
+    tif->tif_encodestrip = _TIFFNoStripEncode;
+    tif->tif_encodetile = _TIFFNoTileEncode;
+    tif->tif_close = _TIFFvoid;
+    tif->tif_seek = _TIFFNoSeek;
+    tif->tif_cleanup = _TIFFvoid;
+    tif->tif_defstripsize = _TIFFDefaultStripSize;
+    tif->tif_deftilesize = _TIFFDefaultTileSize;
+    tif->tif_flags &= ~(TIFF_NOBITREV | TIFF_NOREADRAW);
 }
 
-int
-TIFFSetCompressionScheme(TIFF* tif, int scheme)
+int TIFFSetCompressionScheme(TIFF *tif, int scheme)
 {
-	const TIFFCodec *c = TIFFFindCODEC((uint16) scheme);
+    const TIFFCodec *c = TIFFFindCODEC((uint16_t)scheme);
 
-	_TIFFSetDefaultCompressionState(tif);
-	/*
-	 * Don't treat an unknown compression scheme as an error.
-	 * This permits applications to open files with data that
-	 * the library does not have builtin support for, but which
-	 * may still be meaningful.
-	 */
-	return (c ? (*c->init)(tif, scheme) : 1);
+    _TIFFSetDefaultCompressionState(tif);
+    /*
+     * Don't treat an unknown compression scheme as an error.
+     * This permits applications to open files with data that
+     * the library does not have builtin support for, but which
+     * may still be meaningful.
+     */
+    return (c ? (*c->init)(tif, scheme) : 1);
 }
 
 /*
@@ -175,64 +182,68 @@
  * schemes can also override the builtin versions provided
  * by this library.
  */
-typedef struct _codec {
-	struct _codec* next;
-	TIFFCodec* info;
+typedef struct _codec
+{
+    struct _codec *next;
+    TIFFCodec *info;
 } codec_t;
-static codec_t* registeredCODECS = NULL;
+static codec_t *registeredCODECS = NULL;
 
-const TIFFCodec*
-TIFFFindCODEC(uint16 scheme)
+const TIFFCodec *TIFFFindCODEC(uint16_t scheme)
 {
-	const TIFFCodec* c;
-	codec_t* cd;
+    const TIFFCodec *c;
+    codec_t *cd;
 
-	for (cd = registeredCODECS; cd; cd = cd->next)
-		if (cd->info->scheme == scheme)
-			return ((const TIFFCodec*) cd->info);
-	for (c = _TIFFBuiltinCODECS; c->name; c++)
-		if (c->scheme == scheme)
-			return (c);
-	return ((const TIFFCodec*) 0);
+    for (cd = registeredCODECS; cd; cd = cd->next)
+        if (cd->info->scheme == scheme)
+            return ((const TIFFCodec *)cd->info);
+    for (c = _TIFFBuiltinCODECS; c->name; c++)
+        if (c->scheme == scheme)
+            return (c);
+    return ((const TIFFCodec *)0);
 }
 
-TIFFCodec*
-TIFFRegisterCODEC(uint16 scheme, const char* name, TIFFInitMethod init)
+TIFFCodec *TIFFRegisterCODEC(uint16_t scheme, const char *name,
+                             TIFFInitMethod init)
 {
-	codec_t* cd = (codec_t*)
-	    _TIFFmalloc((tmsize_t)(sizeof (codec_t) + sizeof (TIFFCodec) + strlen(name)+1));
+    codec_t *cd = (codec_t *)_TIFFmallocExt(
+        NULL,
+        (tmsize_t)(sizeof(codec_t) + sizeof(TIFFCodec) + strlen(name) + 1));
 
-	if (cd != NULL) {
-		cd->info = (TIFFCodec*) ((uint8*) cd + sizeof (codec_t));
-		cd->info->name = (char*)
-		    ((uint8*) cd->info + sizeof (TIFFCodec));
-		strcpy(cd->info->name, name);
-		cd->info->scheme = scheme;
-		cd->info->init = init;
-		cd->next = registeredCODECS;
-		registeredCODECS = cd;
-	} else {
-		TIFFErrorExt(0, "TIFFRegisterCODEC",
-		    "No space to register compression scheme %s", name);
-		return NULL;
-	}
-	return (cd->info);
+    if (cd != NULL)
+    {
+        cd->info = (TIFFCodec *)((uint8_t *)cd + sizeof(codec_t));
+        cd->info->name = (char *)((uint8_t *)cd->info + sizeof(TIFFCodec));
+        strcpy(cd->info->name, name);
+        cd->info->scheme = scheme;
+        cd->info->init = init;
+        cd->next = registeredCODECS;
+        registeredCODECS = cd;
+    }
+    else
+    {
+        TIFFErrorExt(0, "TIFFRegisterCODEC",
+                     "No space to register compression scheme %s", name);
+        return NULL;
+    }
+    return (cd->info);
 }
 
-void
-TIFFUnRegisterCODEC(TIFFCodec* c)
+void TIFFUnRegisterCODEC(TIFFCodec *c)
 {
-	codec_t* cd;
-	codec_t** pcd;
+    codec_t *cd;
+    codec_t **pcd;
 
-	for (pcd = &registeredCODECS; (cd = *pcd) != NULL; pcd = &cd->next)
-		if (cd->info == c) {
-			*pcd = cd->next;
-			_TIFFfree(cd);
-			return;
-		}
-	TIFFErrorExt(0, "TIFFUnRegisterCODEC",
-	    "Cannot remove compression scheme %s; not registered", c->name);
+    for (pcd = &registeredCODECS; (cd = *pcd) != NULL; pcd = &cd->next)
+        if (cd->info == c)
+        {
+            *pcd = cd->next;
+            _TIFFfreeExt(NULL, cd);
+            return;
+        }
+    TIFFErrorExt(0, "TIFFUnRegisterCODEC",
+                 "Cannot remove compression scheme %s; not registered",
+                 c->name);
 }
 
 /************************************************************************/
@@ -242,61 +253,58 @@
 /**
  * Get list of configured codecs, both built-in and registered by user.
  * Caller is responsible to free this structure.
- * 
+ *
  * @return returns array of TIFFCodec records (the last record should be NULL)
  * or NULL if function failed.
  */
 
-TIFFCodec*
-TIFFGetConfiguredCODECs()
+TIFFCodec *TIFFGetConfiguredCODECs()
 {
-	int i = 1;
-	codec_t *cd;
-	const TIFFCodec* c;
-	TIFFCodec* codecs = NULL;
-	TIFFCodec* new_codecs;
+    int i = 1;
+    codec_t *cd;
+    const TIFFCodec *c;
+    TIFFCodec *codecs = NULL;
+    TIFFCodec *new_codecs;
 
-	for (cd = registeredCODECS; cd; cd = cd->next) {
-		new_codecs = (TIFFCodec *)
-			_TIFFrealloc(codecs, i * sizeof(TIFFCodec));
-		if (!new_codecs) {
-			_TIFFfree (codecs);
-			return NULL;
-		}
-		codecs = new_codecs;
-		_TIFFmemcpy(codecs + i - 1, cd, sizeof(TIFFCodec));
-		i++;
-	}
-	for (c = _TIFFBuiltinCODECS; c->name; c++) {
-		if (TIFFIsCODECConfigured(c->scheme)) {
-			new_codecs = (TIFFCodec *)
-				_TIFFrealloc(codecs, i * sizeof(TIFFCodec));
-			if (!new_codecs) {
-				_TIFFfree (codecs);
-				return NULL;
-			}
-			codecs = new_codecs;
-			_TIFFmemcpy(codecs + i - 1, (const void*)c, sizeof(TIFFCodec));
-			i++;
-		}
-	}
+    for (cd = registeredCODECS; cd; cd = cd->next)
+    {
+        new_codecs =
+            (TIFFCodec *)_TIFFreallocExt(NULL, codecs, i * sizeof(TIFFCodec));
+        if (!new_codecs)
+        {
+            _TIFFfreeExt(NULL, codecs);
+            return NULL;
+        }
+        codecs = new_codecs;
+        _TIFFmemcpy(codecs + i - 1, cd->info, sizeof(TIFFCodec));
+        i++;
+    }
+    for (c = _TIFFBuiltinCODECS; c->name; c++)
+    {
+        if (TIFFIsCODECConfigured(c->scheme))
+        {
+            new_codecs = (TIFFCodec *)_TIFFreallocExt(NULL, codecs,
+                                                      i * sizeof(TIFFCodec));
+            if (!new_codecs)
+            {
+                _TIFFfreeExt(NULL, codecs);
+                return NULL;
+            }
+            codecs = new_codecs;
+            _TIFFmemcpy(codecs + i - 1, (const void *)c, sizeof(TIFFCodec));
+            i++;
+        }
+    }
 
-	new_codecs = (TIFFCodec *) _TIFFrealloc(codecs, i * sizeof(TIFFCodec));
-	if (!new_codecs) {
-		_TIFFfree (codecs);
-		return NULL;
-	}
-	codecs = new_codecs;
-	_TIFFmemset(codecs + i - 1, 0, sizeof(TIFFCodec));
+    new_codecs =
+        (TIFFCodec *)_TIFFreallocExt(NULL, codecs, i * sizeof(TIFFCodec));
+    if (!new_codecs)
+    {
+        _TIFFfreeExt(NULL, codecs);
+        return NULL;
+    }
+    codecs = new_codecs;
+    _TIFFmemset(codecs + i - 1, 0, sizeof(TIFFCodec));
 
-	return codecs;
+    return codecs;
 }
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_dir.c b/third_party/libtiff/tif_dir.c
index 1e0a76c..8500621 100644
--- a/third_party/libtiff/tif_dir.c
+++ b/third_party/libtiff/tif_dir.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -29,738 +29,1078 @@
  * (and also some miscellaneous stuff)
  */
 #include "tiffiop.h"
+#include <float.h> /*--: for Rational2Double */
+#include <limits.h>
 
 /*
  * These are used in the backwards compatibility code...
  */
-#define DATATYPE_VOID		0       /* !untyped data */
-#define DATATYPE_INT		1       /* !signed integer data */
-#define DATATYPE_UINT		2       /* !unsigned integer data */
-#define DATATYPE_IEEEFP		3       /* !IEEE floating point data */
+#define DATATYPE_VOID 0   /* !untyped data */
+#define DATATYPE_INT 1    /* !signed integer data */
+#define DATATYPE_UINT 2   /* !unsigned integer data */
+#define DATATYPE_IEEEFP 3 /* !IEEE floating point data */
 
-static void
-setByteArray(void** vpp, void* vp, size_t nmemb, size_t elem_size)
+static void setByteArray(TIFF *tif, void **vpp, const void *vp, size_t nmemb,
+                         size_t elem_size)
 {
-	if (*vpp) {
-		_TIFFfree(*vpp);
-		*vpp = 0;
-	}
-	if (vp) {
-		tmsize_t bytes = _TIFFMultiplySSize(NULL, nmemb, elem_size, NULL);
-		if (bytes)
-			*vpp = (void*) _TIFFmalloc(bytes);
-		if (*vpp)
-			_TIFFmemcpy(*vpp, vp, bytes);
-	}
+    if (*vpp)
+    {
+        _TIFFfreeExt(tif, *vpp);
+        *vpp = 0;
+    }
+    if (vp)
+    {
+        tmsize_t bytes = _TIFFMultiplySSize(NULL, nmemb, elem_size, NULL);
+        if (bytes)
+            *vpp = (void *)_TIFFmallocExt(tif, bytes);
+        if (*vpp)
+            _TIFFmemcpy(*vpp, vp, bytes);
+    }
 }
-void _TIFFsetByteArray(void** vpp, void* vp, uint32 n)
-    { setByteArray(vpp, vp, n, 1); }
-void _TIFFsetString(char** cpp, char* cp)
-    { setByteArray((void**) cpp, (void*) cp, strlen(cp)+1, 1); }
-static void _TIFFsetNString(char** cpp, char* cp, uint32 n)
-    { setByteArray((void**) cpp, (void*) cp, n, 1); }
-void _TIFFsetShortArray(uint16** wpp, uint16* wp, uint32 n)
-    { setByteArray((void**) wpp, (void*) wp, n, sizeof (uint16)); }
-void _TIFFsetLongArray(uint32** lpp, uint32* lp, uint32 n)
-    { setByteArray((void**) lpp, (void*) lp, n, sizeof (uint32)); }
-static void _TIFFsetLong8Array(uint64** lpp, uint64* lp, uint32 n)
-    { setByteArray((void**) lpp, (void*) lp, n, sizeof (uint64)); }
-void _TIFFsetFloatArray(float** fpp, float* fp, uint32 n)
-    { setByteArray((void**) fpp, (void*) fp, n, sizeof (float)); }
-void _TIFFsetDoubleArray(double** dpp, double* dp, uint32 n)
-    { setByteArray((void**) dpp, (void*) dp, n, sizeof (double)); }
-
-static void
-setDoubleArrayOneValue(double** vpp, double value, size_t nmemb)
+void _TIFFsetByteArray(void **vpp, const void *vp, uint32_t n)
 {
-	if (*vpp)
-		_TIFFfree(*vpp);
-	*vpp = _TIFFmalloc(nmemb*sizeof(double));
-	if (*vpp)
-	{
-		while (nmemb--)
-			((double*)*vpp)[nmemb] = value;
-	}
+    setByteArray(NULL, vpp, vp, n, 1);
+}
+void _TIFFsetByteArrayExt(TIFF *tif, void **vpp, const void *vp, uint32_t n)
+{
+    setByteArray(tif, vpp, vp, n, 1);
+}
+
+static void _TIFFsetNString(TIFF *tif, char **cpp, const char *cp, uint32_t n)
+{
+    setByteArray(tif, (void **)cpp, cp, n, 1);
+}
+
+void _TIFFsetShortArray(uint16_t **wpp, const uint16_t *wp, uint32_t n)
+{
+    setByteArray(NULL, (void **)wpp, wp, n, sizeof(uint16_t));
+}
+void _TIFFsetShortArrayExt(TIFF *tif, uint16_t **wpp, const uint16_t *wp,
+                           uint32_t n)
+{
+    setByteArray(tif, (void **)wpp, wp, n, sizeof(uint16_t));
+}
+
+void _TIFFsetLongArray(uint32_t **lpp, const uint32_t *lp, uint32_t n)
+{
+    setByteArray(NULL, (void **)lpp, lp, n, sizeof(uint32_t));
+}
+void _TIFFsetLongArrayExt(TIFF *tif, uint32_t **lpp, const uint32_t *lp,
+                          uint32_t n)
+{
+    setByteArray(tif, (void **)lpp, lp, n, sizeof(uint32_t));
+}
+
+static void _TIFFsetLong8Array(TIFF *tif, uint64_t **lpp, const uint64_t *lp,
+                               uint32_t n)
+{
+    setByteArray(tif, (void **)lpp, lp, n, sizeof(uint64_t));
+}
+
+void _TIFFsetFloatArray(float **fpp, const float *fp, uint32_t n)
+{
+    setByteArray(NULL, (void **)fpp, fp, n, sizeof(float));
+}
+void _TIFFsetFloatArrayExt(TIFF *tif, float **fpp, const float *fp, uint32_t n)
+{
+    setByteArray(tif, (void **)fpp, fp, n, sizeof(float));
+}
+
+void _TIFFsetDoubleArray(double **dpp, const double *dp, uint32_t n)
+{
+    setByteArray(NULL, (void **)dpp, dp, n, sizeof(double));
+}
+void _TIFFsetDoubleArrayExt(TIFF *tif, double **dpp, const double *dp,
+                            uint32_t n)
+{
+    setByteArray(tif, (void **)dpp, dp, n, sizeof(double));
+}
+
+static void setDoubleArrayOneValue(TIFF *tif, double **vpp, double value,
+                                   size_t nmemb)
+{
+    if (*vpp)
+        _TIFFfreeExt(tif, *vpp);
+    *vpp = _TIFFmallocExt(tif, nmemb * sizeof(double));
+    if (*vpp)
+    {
+        while (nmemb--)
+            ((double *)*vpp)[nmemb] = value;
+    }
 }
 
 /*
  * Install extra samples information.
  */
-static int
-setExtraSamples(TIFF* tif, va_list ap, uint32* v)
+static int setExtraSamples(TIFF *tif, va_list ap, uint32_t *v)
 {
 /* XXX: Unassociated alpha data == 999 is a known Corel Draw bug, see below */
-#define EXTRASAMPLE_COREL_UNASSALPHA 999 
+#define EXTRASAMPLE_COREL_UNASSALPHA 999
 
-	uint16* va;
-	uint32 i;
-        TIFFDirectory* td = &tif->tif_dir;
-        static const char module[] = "setExtraSamples";
+    uint16_t *va;
+    uint32_t i;
+    TIFFDirectory *td = &tif->tif_dir;
+    static const char module[] = "setExtraSamples";
 
-	*v = (uint16) va_arg(ap, uint16_vap);
-	if ((uint16) *v > td->td_samplesperpixel)
-		return 0;
-	va = va_arg(ap, uint16*);
-	if (*v > 0 && va == NULL)		/* typically missing param */
-		return 0;
-	for (i = 0; i < *v; i++) {
-		if (va[i] > EXTRASAMPLE_UNASSALPHA) {
-			/*
-			 * XXX: Corel Draw is known to produce incorrect
-			 * ExtraSamples tags which must be patched here if we
-			 * want to be able to open some of the damaged TIFF
-			 * files: 
-			 */
-			if (va[i] == EXTRASAMPLE_COREL_UNASSALPHA)
-				va[i] = EXTRASAMPLE_UNASSALPHA;
-			else
-				return 0;
-		}
-	}
-
-        if ( td->td_transferfunction[0] != NULL && (td->td_samplesperpixel - *v > 1) &&
-                !(td->td_samplesperpixel - td->td_extrasamples > 1))
+    *v = (uint16_t)va_arg(ap, uint16_vap);
+    if ((uint16_t)*v > td->td_samplesperpixel)
+        return 0;
+    va = va_arg(ap, uint16_t *);
+    if (*v > 0 && va == NULL) /* typically missing param */
+        return 0;
+    for (i = 0; i < *v; i++)
+    {
+        if (va[i] > EXTRASAMPLE_UNASSALPHA)
         {
-                TIFFWarningExt(tif->tif_clientdata,module,
-                    "ExtraSamples tag value is changing, "
-                    "but TransferFunction was read with a different value. Cancelling it");
-                TIFFClrFieldBit(tif,FIELD_TRANSFERFUNCTION);
-                _TIFFfree(td->td_transferfunction[0]);
-                td->td_transferfunction[0] = NULL;
+            /*
+             * XXX: Corel Draw is known to produce incorrect
+             * ExtraSamples tags which must be patched here if we
+             * want to be able to open some of the damaged TIFF
+             * files:
+             */
+            if (va[i] == EXTRASAMPLE_COREL_UNASSALPHA)
+                va[i] = EXTRASAMPLE_UNASSALPHA;
+            else
+                return 0;
         }
+    }
 
-	td->td_extrasamples = (uint16) *v;
-	_TIFFsetShortArray(&td->td_sampleinfo, va, td->td_extrasamples);
-	return 1;
+    if (td->td_transferfunction[0] != NULL &&
+        (td->td_samplesperpixel - *v > 1) &&
+        !(td->td_samplesperpixel - td->td_extrasamples > 1))
+    {
+        TIFFWarningExtR(tif, module,
+                        "ExtraSamples tag value is changing, "
+                        "but TransferFunction was read with a different value. "
+                        "Canceling it");
+        TIFFClrFieldBit(tif, FIELD_TRANSFERFUNCTION);
+        _TIFFfreeExt(tif, td->td_transferfunction[0]);
+        td->td_transferfunction[0] = NULL;
+    }
+
+    td->td_extrasamples = (uint16_t)*v;
+    _TIFFsetShortArrayExt(tif, &td->td_sampleinfo, va, td->td_extrasamples);
+    return 1;
 
 #undef EXTRASAMPLE_COREL_UNASSALPHA
 }
 
 /*
- * Confirm we have "samplesperpixel" ink names separated by \0.  Returns 
+ * Count ink names separated by \0.  Returns
  * zero if the ink names are not as expected.
  */
-static uint32
-checkInkNamesString(TIFF* tif, uint32 slen, const char* s)
+static uint16_t countInkNamesString(TIFF *tif, uint32_t slen, const char *s)
 {
-	TIFFDirectory* td = &tif->tif_dir;
-	uint16 i = td->td_samplesperpixel;
+    uint16_t i = 0;
 
-	if (slen > 0) {
-		const char* ep = s+slen;
-		const char* cp = s;
-		for (; i > 0; i--) {
-			for (; cp < ep && *cp != '\0'; cp++) {}
-			if (cp >= ep)
-				goto bad;
-			cp++;				/* skip \0 */
-		}
-		return ((uint32)(cp-s));
-	}
+    if (slen > 0)
+    {
+        const char *ep = s + slen;
+        const char *cp = s;
+        do
+        {
+            for (; cp < ep && *cp != '\0'; cp++)
+            {
+            }
+            if (cp >= ep)
+                goto bad;
+            cp++; /* skip \0 */
+            i++;
+        } while (cp < ep);
+        return (i);
+    }
 bad:
-	TIFFErrorExt(tif->tif_clientdata, "TIFFSetField",
-	    "%s: Invalid InkNames value; expecting %d names, found %d",
-	    tif->tif_name,
-	    td->td_samplesperpixel,
-	    td->td_samplesperpixel-i);
-	return (0);
-}
-
-static int
-_TIFFVSetField(TIFF* tif, uint32 tag, va_list ap)
-{
-	static const char module[] = "_TIFFVSetField";
-
-	TIFFDirectory* td = &tif->tif_dir;
-	int status = 1;
-	uint32 v32, i, v;
-    double dblval;
-	char* s;
-	const TIFFField *fip = TIFFFindField(tif, tag, TIFF_ANY);
-	uint32 standard_tag = tag;
-	if( fip == NULL ) /* cannot happen since OkToChangeTag() already checks it */
-	    return 0;
-	/*
-	 * We want to force the custom code to be used for custom
-	 * fields even if the tag happens to match a well known 
-	 * one - important for reinterpreted handling of standard
-	 * tag values in custom directories (i.e. EXIF) 
-	 */
-	if (fip->field_bit == FIELD_CUSTOM) {
-		standard_tag = 0;
-	}
-
-	switch (standard_tag) {
-	case TIFFTAG_SUBFILETYPE:
-		td->td_subfiletype = (uint32) va_arg(ap, uint32);
-		break;
-	case TIFFTAG_IMAGEWIDTH:
-		td->td_imagewidth = (uint32) va_arg(ap, uint32);
-		break;
-	case TIFFTAG_IMAGELENGTH:
-		td->td_imagelength = (uint32) va_arg(ap, uint32);
-		break;
-	case TIFFTAG_BITSPERSAMPLE:
-		td->td_bitspersample = (uint16) va_arg(ap, uint16_vap);
-		/*
-		 * If the data require post-decoding processing to byte-swap
-		 * samples, set it up here.  Note that since tags are required
-		 * to be ordered, compression code can override this behaviour
-		 * in the setup method if it wants to roll the post decoding
-		 * work in with its normal work.
-		 */
-		if (tif->tif_flags & TIFF_SWAB) {
-			if (td->td_bitspersample == 8)
-				tif->tif_postdecode = _TIFFNoPostDecode;
-			else if (td->td_bitspersample == 16)
-				tif->tif_postdecode = _TIFFSwab16BitData;
-			else if (td->td_bitspersample == 24)
-				tif->tif_postdecode = _TIFFSwab24BitData;
-			else if (td->td_bitspersample == 32)
-				tif->tif_postdecode = _TIFFSwab32BitData;
-			else if (td->td_bitspersample == 64)
-				tif->tif_postdecode = _TIFFSwab64BitData;
-			else if (td->td_bitspersample == 128) /* two 64's */
-				tif->tif_postdecode = _TIFFSwab64BitData;
-		}
-		break;
-	case TIFFTAG_COMPRESSION:
-		v = (uint16) va_arg(ap, uint16_vap);
-		/*
-		 * If we're changing the compression scheme, the notify the
-		 * previous module so that it can cleanup any state it's
-		 * setup.
-		 */
-		if (TIFFFieldSet(tif, FIELD_COMPRESSION)) {
-			if ((uint32)td->td_compression == v)
-				break;
-			(*tif->tif_cleanup)(tif);
-			tif->tif_flags &= ~TIFF_CODERSETUP;
-		}
-		/*
-		 * Setup new compression routine state.
-		 */
-		if( (status = TIFFSetCompressionScheme(tif, v)) != 0 )
-		    td->td_compression = (uint16) v;
-		else
-		    status = 0;
-		break;
-	case TIFFTAG_PHOTOMETRIC:
-		td->td_photometric = (uint16) va_arg(ap, uint16_vap);
-		break;
-	case TIFFTAG_THRESHHOLDING:
-		td->td_threshholding = (uint16) va_arg(ap, uint16_vap);
-		break;
-	case TIFFTAG_FILLORDER:
-		v = (uint16) va_arg(ap, uint16_vap);
-		if (v != FILLORDER_LSB2MSB && v != FILLORDER_MSB2LSB)
-			goto badvalue;
-		td->td_fillorder = (uint16) v;
-		break;
-	case TIFFTAG_ORIENTATION:
-		v = (uint16) va_arg(ap, uint16_vap);
-		if (v < ORIENTATION_TOPLEFT || ORIENTATION_LEFTBOT < v)
-			goto badvalue;
-		else
-			td->td_orientation = (uint16) v;
-		break;
-	case TIFFTAG_SAMPLESPERPIXEL:
-		v = (uint16) va_arg(ap, uint16_vap);
-		if (v == 0)
-			goto badvalue;
-        if( v != td->td_samplesperpixel )
-        {
-            /* See http://bugzilla.maptools.org/show_bug.cgi?id=2500 */
-            if( td->td_sminsamplevalue != NULL )
-            {
-                TIFFWarningExt(tif->tif_clientdata,module,
-                    "SamplesPerPixel tag value is changing, "
-                    "but SMinSampleValue tag was read with a different value. Cancelling it");
-                TIFFClrFieldBit(tif,FIELD_SMINSAMPLEVALUE);
-                _TIFFfree(td->td_sminsamplevalue);
-                td->td_sminsamplevalue = NULL;
-            }
-            if( td->td_smaxsamplevalue != NULL )
-            {
-                TIFFWarningExt(tif->tif_clientdata,module,
-                    "SamplesPerPixel tag value is changing, "
-                    "but SMaxSampleValue tag was read with a different value. Cancelling it");
-                TIFFClrFieldBit(tif,FIELD_SMAXSAMPLEVALUE);
-                _TIFFfree(td->td_smaxsamplevalue);
-                td->td_smaxsamplevalue = NULL;
-            }
-            /* Test if 3 transfer functions instead of just one are now needed
-               See http://bugzilla.maptools.org/show_bug.cgi?id=2820 */
-            if( td->td_transferfunction[0] != NULL && (v - td->td_extrasamples > 1) &&
-                !(td->td_samplesperpixel - td->td_extrasamples > 1))
-            {
-                    TIFFWarningExt(tif->tif_clientdata,module,
-                        "SamplesPerPixel tag value is changing, "
-                        "but TransferFunction was read with a different value. Cancelling it");
-                    TIFFClrFieldBit(tif,FIELD_TRANSFERFUNCTION);
-                    _TIFFfree(td->td_transferfunction[0]);
-                    td->td_transferfunction[0] = NULL;
-            }
-        }
-		td->td_samplesperpixel = (uint16) v;
-		break;
-	case TIFFTAG_ROWSPERSTRIP:
-		v32 = (uint32) va_arg(ap, uint32);
-		if (v32 == 0)
-			goto badvalue32;
-		td->td_rowsperstrip = v32;
-		if (!TIFFFieldSet(tif, FIELD_TILEDIMENSIONS)) {
-			td->td_tilelength = v32;
-			td->td_tilewidth = td->td_imagewidth;
-		}
-		break;
-	case TIFFTAG_MINSAMPLEVALUE:
-		td->td_minsamplevalue = (uint16) va_arg(ap, uint16_vap);
-		break;
-	case TIFFTAG_MAXSAMPLEVALUE:
-		td->td_maxsamplevalue = (uint16) va_arg(ap, uint16_vap);
-		break;
-	case TIFFTAG_SMINSAMPLEVALUE:
-		if (tif->tif_flags & TIFF_PERSAMPLE)
-			_TIFFsetDoubleArray(&td->td_sminsamplevalue, va_arg(ap, double*), td->td_samplesperpixel);
-		else
-			setDoubleArrayOneValue(&td->td_sminsamplevalue, va_arg(ap, double), td->td_samplesperpixel);
-		break;
-	case TIFFTAG_SMAXSAMPLEVALUE:
-		if (tif->tif_flags & TIFF_PERSAMPLE)
-			_TIFFsetDoubleArray(&td->td_smaxsamplevalue, va_arg(ap, double*), td->td_samplesperpixel);
-		else
-			setDoubleArrayOneValue(&td->td_smaxsamplevalue, va_arg(ap, double), td->td_samplesperpixel);
-		break;
-	case TIFFTAG_XRESOLUTION:
-        dblval = va_arg(ap, double);
-        if( dblval < 0 )
-            goto badvaluedouble;
-		td->td_xresolution = _TIFFClampDoubleToFloat( dblval );
-		break;
-	case TIFFTAG_YRESOLUTION:
-        dblval = va_arg(ap, double);
-        if( dblval < 0 )
-            goto badvaluedouble;
-		td->td_yresolution = _TIFFClampDoubleToFloat( dblval );
-		break;
-	case TIFFTAG_PLANARCONFIG:
-		v = (uint16) va_arg(ap, uint16_vap);
-		if (v != PLANARCONFIG_CONTIG && v != PLANARCONFIG_SEPARATE)
-			goto badvalue;
-		td->td_planarconfig = (uint16) v;
-		break;
-	case TIFFTAG_XPOSITION:
-		td->td_xposition = _TIFFClampDoubleToFloat( va_arg(ap, double) );
-		break;
-	case TIFFTAG_YPOSITION:
-		td->td_yposition = _TIFFClampDoubleToFloat( va_arg(ap, double) );
-		break;
-	case TIFFTAG_RESOLUTIONUNIT:
-		v = (uint16) va_arg(ap, uint16_vap);
-		if (v < RESUNIT_NONE || RESUNIT_CENTIMETER < v)
-			goto badvalue;
-		td->td_resolutionunit = (uint16) v;
-		break;
-	case TIFFTAG_PAGENUMBER:
-		td->td_pagenumber[0] = (uint16) va_arg(ap, uint16_vap);
-		td->td_pagenumber[1] = (uint16) va_arg(ap, uint16_vap);
-		break;
-	case TIFFTAG_HALFTONEHINTS:
-		td->td_halftonehints[0] = (uint16) va_arg(ap, uint16_vap);
-		td->td_halftonehints[1] = (uint16) va_arg(ap, uint16_vap);
-		break;
-	case TIFFTAG_COLORMAP:
-		v32 = (uint32)(1L<<td->td_bitspersample);
-		_TIFFsetShortArray(&td->td_colormap[0], va_arg(ap, uint16*), v32);
-		_TIFFsetShortArray(&td->td_colormap[1], va_arg(ap, uint16*), v32);
-		_TIFFsetShortArray(&td->td_colormap[2], va_arg(ap, uint16*), v32);
-		break;
-	case TIFFTAG_EXTRASAMPLES:
-		if (!setExtraSamples(tif, ap, &v))
-			goto badvalue;
-		break;
-	case TIFFTAG_MATTEING:
-		td->td_extrasamples =  (((uint16) va_arg(ap, uint16_vap)) != 0);
-		if (td->td_extrasamples) {
-			uint16 sv = EXTRASAMPLE_ASSOCALPHA;
-			_TIFFsetShortArray(&td->td_sampleinfo, &sv, 1);
-		}
-		break;
-	case TIFFTAG_TILEWIDTH:
-		v32 = (uint32) va_arg(ap, uint32);
-		if (v32 % 16) {
-			if (tif->tif_mode != O_RDONLY)
-				goto badvalue32;
-			TIFFWarningExt(tif->tif_clientdata, tif->tif_name,
-				"Nonstandard tile width %d, convert file", v32);
-		}
-		td->td_tilewidth = v32;
-		tif->tif_flags |= TIFF_ISTILED;
-		break;
-	case TIFFTAG_TILELENGTH:
-		v32 = (uint32) va_arg(ap, uint32);
-		if (v32 % 16) {
-			if (tif->tif_mode != O_RDONLY)
-				goto badvalue32;
-			TIFFWarningExt(tif->tif_clientdata, tif->tif_name,
-			    "Nonstandard tile length %d, convert file", v32);
-		}
-		td->td_tilelength = v32;
-		tif->tif_flags |= TIFF_ISTILED;
-		break;
-	case TIFFTAG_TILEDEPTH:
-		v32 = (uint32) va_arg(ap, uint32);
-		if (v32 == 0)
-			goto badvalue32;
-		td->td_tiledepth = v32;
-		break;
-	case TIFFTAG_DATATYPE:
-		v = (uint16) va_arg(ap, uint16_vap);
-		switch (v) {
-		case DATATYPE_VOID:	v = SAMPLEFORMAT_VOID;	break;
-		case DATATYPE_INT:	v = SAMPLEFORMAT_INT;	break;
-		case DATATYPE_UINT:	v = SAMPLEFORMAT_UINT;	break;
-		case DATATYPE_IEEEFP:	v = SAMPLEFORMAT_IEEEFP;break;
-		default:		goto badvalue;
-		}
-		td->td_sampleformat = (uint16) v;
-		break;
-	case TIFFTAG_SAMPLEFORMAT:
-		v = (uint16) va_arg(ap, uint16_vap);
-		if (v < SAMPLEFORMAT_UINT || SAMPLEFORMAT_COMPLEXIEEEFP < v)
-			goto badvalue;
-		td->td_sampleformat = (uint16) v;
-
-		/*  Try to fix up the SWAB function for complex data. */
-		if( td->td_sampleformat == SAMPLEFORMAT_COMPLEXINT
-		    && td->td_bitspersample == 32
-		    && tif->tif_postdecode == _TIFFSwab32BitData )
-		    tif->tif_postdecode = _TIFFSwab16BitData;
-		else if( (td->td_sampleformat == SAMPLEFORMAT_COMPLEXINT
-			  || td->td_sampleformat == SAMPLEFORMAT_COMPLEXIEEEFP)
-			 && td->td_bitspersample == 64
-			 && tif->tif_postdecode == _TIFFSwab64BitData )
-		    tif->tif_postdecode = _TIFFSwab32BitData;
-		break;
-	case TIFFTAG_IMAGEDEPTH:
-		td->td_imagedepth = (uint32) va_arg(ap, uint32);
-		break;
-	case TIFFTAG_SUBIFD:
-		if ((tif->tif_flags & TIFF_INSUBIFD) == 0) {
-			td->td_nsubifd = (uint16) va_arg(ap, uint16_vap);
-			_TIFFsetLong8Array(&td->td_subifd, (uint64*) va_arg(ap, uint64*),
-			    (uint32) td->td_nsubifd);
-		} else {
-			TIFFErrorExt(tif->tif_clientdata, module,
-				     "%s: Sorry, cannot nest SubIFDs",
-				     tif->tif_name);
-			status = 0;
-		}
-		break;
-	case TIFFTAG_YCBCRPOSITIONING:
-		td->td_ycbcrpositioning = (uint16) va_arg(ap, uint16_vap);
-		break;
-	case TIFFTAG_YCBCRSUBSAMPLING:
-		td->td_ycbcrsubsampling[0] = (uint16) va_arg(ap, uint16_vap);
-		td->td_ycbcrsubsampling[1] = (uint16) va_arg(ap, uint16_vap);
-		break;
-	case TIFFTAG_TRANSFERFUNCTION:
-		v = (td->td_samplesperpixel - td->td_extrasamples) > 1 ? 3 : 1;
-		for (i = 0; i < v; i++)
-			_TIFFsetShortArray(&td->td_transferfunction[i],
-			    va_arg(ap, uint16*), 1U<<td->td_bitspersample);
-		break;
-	case TIFFTAG_REFERENCEBLACKWHITE:
-		/* XXX should check for null range */
-		_TIFFsetFloatArray(&td->td_refblackwhite, va_arg(ap, float*), 6);
-		break;
-	case TIFFTAG_INKNAMES:
-		v = (uint16) va_arg(ap, uint16_vap);
-		s = va_arg(ap, char*);
-		v = checkInkNamesString(tif, v, s);
-		status = v > 0;
-		if( v > 0 ) {
-			_TIFFsetNString(&td->td_inknames, s, v);
-			td->td_inknameslen = v;
-		}
-		break;
-	case TIFFTAG_PERSAMPLE:
-		v = (uint16) va_arg(ap, uint16_vap);
-		if( v == PERSAMPLE_MULTI )
-			tif->tif_flags |= TIFF_PERSAMPLE;
-		else
-			tif->tif_flags &= ~TIFF_PERSAMPLE;
-		break;
-	default: {
-		TIFFTagValue *tv;
-		int tv_size, iCustom;
-
-		/*
-		 * This can happen if multiple images are open with different
-		 * codecs which have private tags.  The global tag information
-		 * table may then have tags that are valid for one file but not
-		 * the other. If the client tries to set a tag that is not valid
-		 * for the image's codec then we'll arrive here.  This
-		 * happens, for example, when tiffcp is used to convert between
-		 * compression schemes and codec-specific tags are blindly copied.
-		 */
-		if(fip->field_bit != FIELD_CUSTOM) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "%s: Invalid %stag \"%s\" (not supported by codec)",
-			    tif->tif_name, isPseudoTag(tag) ? "pseudo-" : "",
-			    fip->field_name);
-			status = 0;
-			break;
-		}
-
-		/*
-		 * Find the existing entry for this custom value.
-		 */
-		tv = NULL;
-		for (iCustom = 0; iCustom < td->td_customValueCount; iCustom++) {
-			if (td->td_customValues[iCustom].info->field_tag == tag) {
-				tv = td->td_customValues + iCustom;
-				if (tv->value != NULL) {
-					_TIFFfree(tv->value);
-					tv->value = NULL;
-				}
-				break;
-			}
-		}
-
-		/*
-		 * Grow the custom list if the entry was not found.
-		 */
-		if(tv == NULL) {
-			TIFFTagValue *new_customValues;
-
-			td->td_customValueCount++;
-			new_customValues = (TIFFTagValue *)
-			    _TIFFrealloc(td->td_customValues,
-			    sizeof(TIFFTagValue) * td->td_customValueCount);
-			if (!new_customValues) {
-				TIFFErrorExt(tif->tif_clientdata, module,
-				    "%s: Failed to allocate space for list of custom values",
-				    tif->tif_name);
-				status = 0;
-				goto end;
-			}
-
-			td->td_customValues = new_customValues;
-
-			tv = td->td_customValues + (td->td_customValueCount - 1);
-			tv->info = fip;
-			tv->value = NULL;
-			tv->count = 0;
-		}
-
-		/*
-		 * Set custom value ... save a copy of the custom tag value.
-		 */
-		tv_size = _TIFFDataSize(fip->field_type);
-		if (tv_size == 0) {
-			status = 0;
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "%s: Bad field type %d for \"%s\"",
-			    tif->tif_name, fip->field_type,
-			    fip->field_name);
-			goto end;
-		}
-
-		if (fip->field_type == TIFF_ASCII)
-		{
-			uint32 ma;
-			char* mb;
-			if (fip->field_passcount)
-			{
-				assert(fip->field_writecount==TIFF_VARIABLE2);
-				ma=(uint32)va_arg(ap,uint32);
-				mb=(char*)va_arg(ap,char*);
-			}
-			else
-			{
-				mb=(char*)va_arg(ap,char*);
-				ma=(uint32)(strlen(mb)+1);
-			}
-			tv->count=ma;
-			setByteArray(&tv->value,mb,ma,1);
-		}
-		else
-		{
-			if (fip->field_passcount) {
-				if (fip->field_writecount == TIFF_VARIABLE2)
-					tv->count = (uint32) va_arg(ap, uint32);
-				else
-					tv->count = (int) va_arg(ap, int);
-			} else if (fip->field_writecount == TIFF_VARIABLE
-			   || fip->field_writecount == TIFF_VARIABLE2)
-				tv->count = 1;
-			else if (fip->field_writecount == TIFF_SPP)
-				tv->count = td->td_samplesperpixel;
-			else
-				tv->count = fip->field_writecount;
-
-			if (tv->count == 0) {
-				status = 0;
-				TIFFErrorExt(tif->tif_clientdata, module,
-					     "%s: Null count for \"%s\" (type "
-					     "%d, writecount %d, passcount %d)",
-					     tif->tif_name,
-					     fip->field_name,
-					     fip->field_type,
-					     fip->field_writecount,
-					     fip->field_passcount);
-				goto end;
-			}
-
-			tv->value = _TIFFCheckMalloc(tif, tv->count, tv_size,
-			    "custom tag binary object");
-			if (!tv->value) {
-				status = 0;
-				goto end;
-			}
-
-			if (fip->field_tag == TIFFTAG_DOTRANGE 
-			    && strcmp(fip->field_name,"DotRange") == 0) {
-				/* TODO: This is an evil exception and should not have been
-				   handled this way ... likely best if we move it into
-				   the directory structure with an explicit field in 
-				   libtiff 4.1 and assign it a FIELD_ value */
-				uint16 v2[2];
-				v2[0] = (uint16)va_arg(ap, int);
-				v2[1] = (uint16)va_arg(ap, int);
-				_TIFFmemcpy(tv->value, &v2, 4);
-			}
-
-			else if (fip->field_passcount
-				  || fip->field_writecount == TIFF_VARIABLE
-				  || fip->field_writecount == TIFF_VARIABLE2
-				  || fip->field_writecount == TIFF_SPP
-				  || tv->count > 1) {
-				_TIFFmemcpy(tv->value, va_arg(ap, void *),
-				    tv->count * tv_size);
-			} else {
-				char *val = (char *)tv->value;
-				assert( tv->count == 1 );
-
-				switch (fip->field_type) {
-				case TIFF_BYTE:
-				case TIFF_UNDEFINED:
-					{
-						uint8 v2 = (uint8)va_arg(ap, int);
-						_TIFFmemcpy(val, &v2, tv_size);
-					}
-					break;
-				case TIFF_SBYTE:
-					{
-						int8 v2 = (int8)va_arg(ap, int);
-						_TIFFmemcpy(val, &v2, tv_size);
-					}
-					break;
-				case TIFF_SHORT:
-					{
-						uint16 v2 = (uint16)va_arg(ap, int);
-						_TIFFmemcpy(val, &v2, tv_size);
-					}
-					break;
-				case TIFF_SSHORT:
-					{
-						int16 v2 = (int16)va_arg(ap, int);
-						_TIFFmemcpy(val, &v2, tv_size);
-					}
-					break;
-				case TIFF_LONG:
-				case TIFF_IFD:
-					{
-						uint32 v2 = va_arg(ap, uint32);
-						_TIFFmemcpy(val, &v2, tv_size);
-					}
-					break;
-				case TIFF_SLONG:
-					{
-						int32 v2 = va_arg(ap, int32);
-						_TIFFmemcpy(val, &v2, tv_size);
-					}
-					break;
-				case TIFF_LONG8:
-				case TIFF_IFD8:
-					{
-						uint64 v2 = va_arg(ap, uint64);
-						_TIFFmemcpy(val, &v2, tv_size);
-					}
-					break;
-				case TIFF_SLONG8:
-					{
-						int64 v2 = va_arg(ap, int64);
-						_TIFFmemcpy(val, &v2, tv_size);
-					}
-					break;
-				case TIFF_RATIONAL:
-				case TIFF_SRATIONAL:
-				case TIFF_FLOAT:
-					{
-						float v2 = _TIFFClampDoubleToFloat(va_arg(ap, double));
-						_TIFFmemcpy(val, &v2, tv_size);
-					}
-					break;
-				case TIFF_DOUBLE:
-					{
-						double v2 = va_arg(ap, double);
-						_TIFFmemcpy(val, &v2, tv_size);
-					}
-					break;
-				default:
-					_TIFFmemset(val, 0, tv_size);
-					status = 0;
-					break;
-				}
-			}
-		}
-	}
-	}
-	if (status) {
-		const TIFFField* fip2=TIFFFieldWithTag(tif,tag);
-		if (fip2)                
-			TIFFSetFieldBit(tif, fip2->field_bit);
-		tif->tif_flags |= TIFF_DIRTYDIRECT;
-	}
-
-end:
-	va_end(ap);
-	return (status);
-badvalue:
-        {
-		const TIFFField* fip2=TIFFFieldWithTag(tif,tag);
-		TIFFErrorExt(tif->tif_clientdata, module,
-		     "%s: Bad value %u for \"%s\" tag",
-		     tif->tif_name, v,
-		     fip2 ? fip2->field_name : "Unknown");
-		va_end(ap);
-        }
-	return (0);
-badvalue32:
-        {
-		const TIFFField* fip2=TIFFFieldWithTag(tif,tag);
-		TIFFErrorExt(tif->tif_clientdata, module,
-		     "%s: Bad value %u for \"%s\" tag",
-		     tif->tif_name, v32,
-		     fip2 ? fip2->field_name : "Unknown");
-		va_end(ap);
-        }
-	return (0);
-badvaluedouble:
-        {
-        const TIFFField* fip2=TIFFFieldWithTag(tif,tag);
-        TIFFErrorExt(tif->tif_clientdata, module,
-             "%s: Bad value %f for \"%s\" tag",
-             tif->tif_name, dblval,
-             fip2 ? fip2->field_name : "Unknown");
-        va_end(ap);
-        }
+    TIFFErrorExtR(tif, "TIFFSetField",
+                  "%s: Invalid InkNames value; no NUL at given buffer end "
+                  "location %" PRIu32 ", after %" PRIu16 " ink",
+                  tif->tif_name, slen, i);
     return (0);
 }
 
+static int _TIFFVSetField(TIFF *tif, uint32_t tag, va_list ap)
+{
+    static const char module[] = "_TIFFVSetField";
+
+    TIFFDirectory *td = &tif->tif_dir;
+    int status = 1;
+    uint32_t v32, v;
+    double dblval;
+    char *s;
+    const TIFFField *fip = TIFFFindField(tif, tag, TIFF_ANY);
+    uint32_t standard_tag = tag;
+    if (fip == NULL) /* cannot happen since OkToChangeTag() already checks it */
+        return 0;
+    /*
+     * We want to force the custom code to be used for custom
+     * fields even if the tag happens to match a well known
+     * one - important for reinterpreted handling of standard
+     * tag values in custom directories (i.e. EXIF)
+     */
+    if (fip->field_bit == FIELD_CUSTOM)
+    {
+        standard_tag = 0;
+    }
+
+    switch (standard_tag)
+    {
+        case TIFFTAG_SUBFILETYPE:
+            td->td_subfiletype = (uint32_t)va_arg(ap, uint32_t);
+            break;
+        case TIFFTAG_IMAGEWIDTH:
+            td->td_imagewidth = (uint32_t)va_arg(ap, uint32_t);
+            break;
+        case TIFFTAG_IMAGELENGTH:
+            td->td_imagelength = (uint32_t)va_arg(ap, uint32_t);
+            break;
+        case TIFFTAG_BITSPERSAMPLE:
+            td->td_bitspersample = (uint16_t)va_arg(ap, uint16_vap);
+            /*
+             * If the data require post-decoding processing to byte-swap
+             * samples, set it up here.  Note that since tags are required
+             * to be ordered, compression code can override this behavior
+             * in the setup method if it wants to roll the post decoding
+             * work in with its normal work.
+             */
+            if (tif->tif_flags & TIFF_SWAB)
+            {
+                if (td->td_bitspersample == 8)
+                    tif->tif_postdecode = _TIFFNoPostDecode;
+                else if (td->td_bitspersample == 16)
+                    tif->tif_postdecode = _TIFFSwab16BitData;
+                else if (td->td_bitspersample == 24)
+                    tif->tif_postdecode = _TIFFSwab24BitData;
+                else if (td->td_bitspersample == 32)
+                    tif->tif_postdecode = _TIFFSwab32BitData;
+                else if (td->td_bitspersample == 64)
+                    tif->tif_postdecode = _TIFFSwab64BitData;
+                else if (td->td_bitspersample == 128) /* two 64's */
+                    tif->tif_postdecode = _TIFFSwab64BitData;
+            }
+            break;
+        case TIFFTAG_COMPRESSION:
+            v = (uint16_t)va_arg(ap, uint16_vap);
+            /*
+             * If we're changing the compression scheme, notify the
+             * previous module so that it can cleanup any state it's
+             * setup.
+             */
+            if (TIFFFieldSet(tif, FIELD_COMPRESSION))
+            {
+                if ((uint32_t)td->td_compression == v)
+                    break;
+                (*tif->tif_cleanup)(tif);
+                tif->tif_flags &= ~TIFF_CODERSETUP;
+            }
+            /*
+             * Setup new compression routine state.
+             */
+            if ((status = TIFFSetCompressionScheme(tif, v)) != 0)
+                td->td_compression = (uint16_t)v;
+            else
+                status = 0;
+            break;
+        case TIFFTAG_PHOTOMETRIC:
+            td->td_photometric = (uint16_t)va_arg(ap, uint16_vap);
+            break;
+        case TIFFTAG_THRESHHOLDING:
+            td->td_threshholding = (uint16_t)va_arg(ap, uint16_vap);
+            break;
+        case TIFFTAG_FILLORDER:
+            v = (uint16_t)va_arg(ap, uint16_vap);
+            if (v != FILLORDER_LSB2MSB && v != FILLORDER_MSB2LSB)
+                goto badvalue;
+            td->td_fillorder = (uint16_t)v;
+            break;
+        case TIFFTAG_ORIENTATION:
+            v = (uint16_t)va_arg(ap, uint16_vap);
+            if (v < ORIENTATION_TOPLEFT || ORIENTATION_LEFTBOT < v)
+                goto badvalue;
+            else
+                td->td_orientation = (uint16_t)v;
+            break;
+        case TIFFTAG_SAMPLESPERPIXEL:
+            v = (uint16_t)va_arg(ap, uint16_vap);
+            if (v == 0)
+                goto badvalue;
+            if (v != td->td_samplesperpixel)
+            {
+                /* See http://bugzilla.maptools.org/show_bug.cgi?id=2500 */
+                if (td->td_sminsamplevalue != NULL)
+                {
+                    TIFFWarningExtR(tif, module,
+                                    "SamplesPerPixel tag value is changing, "
+                                    "but SMinSampleValue tag was read with a "
+                                    "different value. Canceling it");
+                    TIFFClrFieldBit(tif, FIELD_SMINSAMPLEVALUE);
+                    _TIFFfreeExt(tif, td->td_sminsamplevalue);
+                    td->td_sminsamplevalue = NULL;
+                }
+                if (td->td_smaxsamplevalue != NULL)
+                {
+                    TIFFWarningExtR(tif, module,
+                                    "SamplesPerPixel tag value is changing, "
+                                    "but SMaxSampleValue tag was read with a "
+                                    "different value. Canceling it");
+                    TIFFClrFieldBit(tif, FIELD_SMAXSAMPLEVALUE);
+                    _TIFFfreeExt(tif, td->td_smaxsamplevalue);
+                    td->td_smaxsamplevalue = NULL;
+                }
+                /* Test if 3 transfer functions instead of just one are now
+                   needed See http://bugzilla.maptools.org/show_bug.cgi?id=2820
+                 */
+                if (td->td_transferfunction[0] != NULL &&
+                    (v - td->td_extrasamples > 1) &&
+                    !(td->td_samplesperpixel - td->td_extrasamples > 1))
+                {
+                    TIFFWarningExtR(tif, module,
+                                    "SamplesPerPixel tag value is changing, "
+                                    "but TransferFunction was read with a "
+                                    "different value. Canceling it");
+                    TIFFClrFieldBit(tif, FIELD_TRANSFERFUNCTION);
+                    _TIFFfreeExt(tif, td->td_transferfunction[0]);
+                    td->td_transferfunction[0] = NULL;
+                }
+            }
+            td->td_samplesperpixel = (uint16_t)v;
+            break;
+        case TIFFTAG_ROWSPERSTRIP:
+            v32 = (uint32_t)va_arg(ap, uint32_t);
+            if (v32 == 0)
+                goto badvalue32;
+            td->td_rowsperstrip = v32;
+            if (!TIFFFieldSet(tif, FIELD_TILEDIMENSIONS))
+            {
+                td->td_tilelength = v32;
+                td->td_tilewidth = td->td_imagewidth;
+            }
+            break;
+        case TIFFTAG_MINSAMPLEVALUE:
+            td->td_minsamplevalue = (uint16_t)va_arg(ap, uint16_vap);
+            break;
+        case TIFFTAG_MAXSAMPLEVALUE:
+            td->td_maxsamplevalue = (uint16_t)va_arg(ap, uint16_vap);
+            break;
+        case TIFFTAG_SMINSAMPLEVALUE:
+            if (tif->tif_flags & TIFF_PERSAMPLE)
+                _TIFFsetDoubleArrayExt(tif, &td->td_sminsamplevalue,
+                                       va_arg(ap, double *),
+                                       td->td_samplesperpixel);
+            else
+                setDoubleArrayOneValue(tif, &td->td_sminsamplevalue,
+                                       va_arg(ap, double),
+                                       td->td_samplesperpixel);
+            break;
+        case TIFFTAG_SMAXSAMPLEVALUE:
+            if (tif->tif_flags & TIFF_PERSAMPLE)
+                _TIFFsetDoubleArrayExt(tif, &td->td_smaxsamplevalue,
+                                       va_arg(ap, double *),
+                                       td->td_samplesperpixel);
+            else
+                setDoubleArrayOneValue(tif, &td->td_smaxsamplevalue,
+                                       va_arg(ap, double),
+                                       td->td_samplesperpixel);
+            break;
+        case TIFFTAG_XRESOLUTION:
+            dblval = va_arg(ap, double);
+            if (dblval != dblval || dblval < 0)
+                goto badvaluedouble;
+            td->td_xresolution = _TIFFClampDoubleToFloat(dblval);
+            break;
+        case TIFFTAG_YRESOLUTION:
+            dblval = va_arg(ap, double);
+            if (dblval != dblval || dblval < 0)
+                goto badvaluedouble;
+            td->td_yresolution = _TIFFClampDoubleToFloat(dblval);
+            break;
+        case TIFFTAG_PLANARCONFIG:
+            v = (uint16_t)va_arg(ap, uint16_vap);
+            if (v != PLANARCONFIG_CONTIG && v != PLANARCONFIG_SEPARATE)
+                goto badvalue;
+            td->td_planarconfig = (uint16_t)v;
+            break;
+        case TIFFTAG_XPOSITION:
+            td->td_xposition = _TIFFClampDoubleToFloat(va_arg(ap, double));
+            break;
+        case TIFFTAG_YPOSITION:
+            td->td_yposition = _TIFFClampDoubleToFloat(va_arg(ap, double));
+            break;
+        case TIFFTAG_RESOLUTIONUNIT:
+            v = (uint16_t)va_arg(ap, uint16_vap);
+            if (v < RESUNIT_NONE || RESUNIT_CENTIMETER < v)
+                goto badvalue;
+            td->td_resolutionunit = (uint16_t)v;
+            break;
+        case TIFFTAG_PAGENUMBER:
+            td->td_pagenumber[0] = (uint16_t)va_arg(ap, uint16_vap);
+            td->td_pagenumber[1] = (uint16_t)va_arg(ap, uint16_vap);
+            break;
+        case TIFFTAG_HALFTONEHINTS:
+            td->td_halftonehints[0] = (uint16_t)va_arg(ap, uint16_vap);
+            td->td_halftonehints[1] = (uint16_t)va_arg(ap, uint16_vap);
+            break;
+        case TIFFTAG_COLORMAP:
+            v32 = (uint32_t)(1L << td->td_bitspersample);
+            _TIFFsetShortArrayExt(tif, &td->td_colormap[0],
+                                  va_arg(ap, uint16_t *), v32);
+            _TIFFsetShortArrayExt(tif, &td->td_colormap[1],
+                                  va_arg(ap, uint16_t *), v32);
+            _TIFFsetShortArrayExt(tif, &td->td_colormap[2],
+                                  va_arg(ap, uint16_t *), v32);
+            break;
+        case TIFFTAG_EXTRASAMPLES:
+            if (!setExtraSamples(tif, ap, &v))
+                goto badvalue;
+            break;
+        case TIFFTAG_MATTEING:
+            td->td_extrasamples = (((uint16_t)va_arg(ap, uint16_vap)) != 0);
+            if (td->td_extrasamples)
+            {
+                uint16_t sv = EXTRASAMPLE_ASSOCALPHA;
+                _TIFFsetShortArrayExt(tif, &td->td_sampleinfo, &sv, 1);
+            }
+            break;
+        case TIFFTAG_TILEWIDTH:
+            v32 = (uint32_t)va_arg(ap, uint32_t);
+            if (v32 % 16)
+            {
+                if (tif->tif_mode != O_RDONLY)
+                    goto badvalue32;
+                TIFFWarningExtR(
+                    tif, tif->tif_name,
+                    "Nonstandard tile width %" PRIu32 ", convert file", v32);
+            }
+            td->td_tilewidth = v32;
+            tif->tif_flags |= TIFF_ISTILED;
+            break;
+        case TIFFTAG_TILELENGTH:
+            v32 = (uint32_t)va_arg(ap, uint32_t);
+            if (v32 % 16)
+            {
+                if (tif->tif_mode != O_RDONLY)
+                    goto badvalue32;
+                TIFFWarningExtR(
+                    tif, tif->tif_name,
+                    "Nonstandard tile length %" PRIu32 ", convert file", v32);
+            }
+            td->td_tilelength = v32;
+            tif->tif_flags |= TIFF_ISTILED;
+            break;
+        case TIFFTAG_TILEDEPTH:
+            v32 = (uint32_t)va_arg(ap, uint32_t);
+            if (v32 == 0)
+                goto badvalue32;
+            td->td_tiledepth = v32;
+            break;
+        case TIFFTAG_DATATYPE:
+            v = (uint16_t)va_arg(ap, uint16_vap);
+            switch (v)
+            {
+                case DATATYPE_VOID:
+                    v = SAMPLEFORMAT_VOID;
+                    break;
+                case DATATYPE_INT:
+                    v = SAMPLEFORMAT_INT;
+                    break;
+                case DATATYPE_UINT:
+                    v = SAMPLEFORMAT_UINT;
+                    break;
+                case DATATYPE_IEEEFP:
+                    v = SAMPLEFORMAT_IEEEFP;
+                    break;
+                default:
+                    goto badvalue;
+            }
+            td->td_sampleformat = (uint16_t)v;
+            break;
+        case TIFFTAG_SAMPLEFORMAT:
+            v = (uint16_t)va_arg(ap, uint16_vap);
+            if (v < SAMPLEFORMAT_UINT || SAMPLEFORMAT_COMPLEXIEEEFP < v)
+                goto badvalue;
+            td->td_sampleformat = (uint16_t)v;
+
+            /*  Try to fix up the SWAB function for complex data. */
+            if (td->td_sampleformat == SAMPLEFORMAT_COMPLEXINT &&
+                td->td_bitspersample == 32 &&
+                tif->tif_postdecode == _TIFFSwab32BitData)
+                tif->tif_postdecode = _TIFFSwab16BitData;
+            else if ((td->td_sampleformat == SAMPLEFORMAT_COMPLEXINT ||
+                      td->td_sampleformat == SAMPLEFORMAT_COMPLEXIEEEFP) &&
+                     td->td_bitspersample == 64 &&
+                     tif->tif_postdecode == _TIFFSwab64BitData)
+                tif->tif_postdecode = _TIFFSwab32BitData;
+            break;
+        case TIFFTAG_IMAGEDEPTH:
+            td->td_imagedepth = (uint32_t)va_arg(ap, uint32_t);
+            break;
+        case TIFFTAG_SUBIFD:
+            if ((tif->tif_flags & TIFF_INSUBIFD) == 0)
+            {
+                td->td_nsubifd = (uint16_t)va_arg(ap, uint16_vap);
+                _TIFFsetLong8Array(tif, &td->td_subifd,
+                                   (uint64_t *)va_arg(ap, uint64_t *),
+                                   (uint32_t)td->td_nsubifd);
+            }
+            else
+            {
+                TIFFErrorExtR(tif, module, "%s: Sorry, cannot nest SubIFDs",
+                              tif->tif_name);
+                status = 0;
+            }
+            break;
+        case TIFFTAG_YCBCRPOSITIONING:
+            td->td_ycbcrpositioning = (uint16_t)va_arg(ap, uint16_vap);
+            break;
+        case TIFFTAG_YCBCRSUBSAMPLING:
+            td->td_ycbcrsubsampling[0] = (uint16_t)va_arg(ap, uint16_vap);
+            td->td_ycbcrsubsampling[1] = (uint16_t)va_arg(ap, uint16_vap);
+            break;
+        case TIFFTAG_TRANSFERFUNCTION:
+        {
+            uint32_t i;
+            v = (td->td_samplesperpixel - td->td_extrasamples) > 1 ? 3 : 1;
+            for (i = 0; i < v; i++)
+                _TIFFsetShortArrayExt(tif, &td->td_transferfunction[i],
+                                      va_arg(ap, uint16_t *),
+                                      1U << td->td_bitspersample);
+            break;
+        }
+        case TIFFTAG_REFERENCEBLACKWHITE:
+            /* XXX should check for null range */
+            _TIFFsetFloatArrayExt(tif, &td->td_refblackwhite,
+                                  va_arg(ap, float *), 6);
+            break;
+        case TIFFTAG_INKNAMES:
+        {
+            v = (uint16_t)va_arg(ap, uint16_vap);
+            s = va_arg(ap, char *);
+            uint16_t ninksinstring;
+            ninksinstring = countInkNamesString(tif, v, s);
+            status = ninksinstring > 0;
+            if (ninksinstring > 0)
+            {
+                _TIFFsetNString(tif, &td->td_inknames, s, v);
+                td->td_inknameslen = v;
+                /* Set NumberOfInks to the value ninksinstring */
+                if (TIFFFieldSet(tif, FIELD_NUMBEROFINKS))
+                {
+                    if (td->td_numberofinks != ninksinstring)
+                    {
+                        TIFFErrorExtR(
+                            tif, module,
+                            "Warning %s; Tag %s:\n  Value %" PRIu16
+                            " of NumberOfInks is different from the number of "
+                            "inks %" PRIu16
+                            ".\n  -> NumberOfInks value adapted to %" PRIu16 "",
+                            tif->tif_name, fip->field_name, td->td_numberofinks,
+                            ninksinstring, ninksinstring);
+                        td->td_numberofinks = ninksinstring;
+                    }
+                }
+                else
+                {
+                    td->td_numberofinks = ninksinstring;
+                    TIFFSetFieldBit(tif, FIELD_NUMBEROFINKS);
+                }
+                if (TIFFFieldSet(tif, FIELD_SAMPLESPERPIXEL))
+                {
+                    if (td->td_numberofinks != td->td_samplesperpixel)
+                    {
+                        TIFFErrorExtR(tif, module,
+                                      "Warning %s; Tag %s:\n  Value %" PRIu16
+                                      " of NumberOfInks is different from the "
+                                      "SamplesPerPixel value %" PRIu16 "",
+                                      tif->tif_name, fip->field_name,
+                                      td->td_numberofinks,
+                                      td->td_samplesperpixel);
+                    }
+                }
+            }
+        }
+        break;
+        case TIFFTAG_NUMBEROFINKS:
+            v = (uint16_t)va_arg(ap, uint16_vap);
+            /* If InkNames already set also NumberOfInks is set accordingly and
+             * should be equal */
+            if (TIFFFieldSet(tif, FIELD_INKNAMES))
+            {
+                if (v != td->td_numberofinks)
+                {
+                    TIFFErrorExtR(
+                        tif, module,
+                        "Error %s; Tag %s:\n  It is not possible to set the "
+                        "value %" PRIu32
+                        " for NumberOfInks\n  which is different from the "
+                        "number of inks in the InkNames tag (%" PRIu16 ")",
+                        tif->tif_name, fip->field_name, v, td->td_numberofinks);
+                    /* Do not set / overwrite number of inks already set by
+                     * InkNames case accordingly. */
+                    status = 0;
+                }
+            }
+            else
+            {
+                td->td_numberofinks = (uint16_t)v;
+                if (TIFFFieldSet(tif, FIELD_SAMPLESPERPIXEL))
+                {
+                    if (td->td_numberofinks != td->td_samplesperpixel)
+                    {
+                        TIFFErrorExtR(tif, module,
+                                      "Warning %s; Tag %s:\n  Value %" PRIu32
+                                      " of NumberOfInks is different from the "
+                                      "SamplesPerPixel value %" PRIu16 "",
+                                      tif->tif_name, fip->field_name, v,
+                                      td->td_samplesperpixel);
+                    }
+                }
+            }
+            break;
+        case TIFFTAG_PERSAMPLE:
+            v = (uint16_t)va_arg(ap, uint16_vap);
+            if (v == PERSAMPLE_MULTI)
+                tif->tif_flags |= TIFF_PERSAMPLE;
+            else
+                tif->tif_flags &= ~TIFF_PERSAMPLE;
+            break;
+        default:
+        {
+            TIFFTagValue *tv;
+            int tv_size, iCustom;
+
+            /*
+             * This can happen if multiple images are open with different
+             * codecs which have private tags.  The global tag information
+             * table may then have tags that are valid for one file but not
+             * the other. If the client tries to set a tag that is not valid
+             * for the image's codec then we'll arrive here.  This
+             * happens, for example, when tiffcp is used to convert between
+             * compression schemes and codec-specific tags are blindly copied.
+             *
+             * This also happens when a FIELD_IGNORE tag is written.
+             */
+            if (fip->field_bit == FIELD_IGNORE)
+            {
+                TIFFErrorExtR(
+                    tif, module,
+                    "%s: Ignored %stag \"%s\" (not supported by libtiff)",
+                    tif->tif_name, isPseudoTag(tag) ? "pseudo-" : "",
+                    fip->field_name);
+                status = 0;
+                break;
+            }
+            if (fip->field_bit != FIELD_CUSTOM)
+            {
+                TIFFErrorExtR(
+                    tif, module,
+                    "%s: Invalid %stag \"%s\" (not supported by codec)",
+                    tif->tif_name, isPseudoTag(tag) ? "pseudo-" : "",
+                    fip->field_name);
+                status = 0;
+                break;
+            }
+
+            /*
+             * Find the existing entry for this custom value.
+             */
+            tv = NULL;
+            for (iCustom = 0; iCustom < td->td_customValueCount; iCustom++)
+            {
+                if (td->td_customValues[iCustom].info->field_tag == tag)
+                {
+                    tv = td->td_customValues + iCustom;
+                    if (tv->value != NULL)
+                    {
+                        _TIFFfreeExt(tif, tv->value);
+                        tv->value = NULL;
+                    }
+                    break;
+                }
+            }
+
+            /*
+             * Grow the custom list if the entry was not found.
+             */
+            if (tv == NULL)
+            {
+                TIFFTagValue *new_customValues;
+
+                td->td_customValueCount++;
+                new_customValues = (TIFFTagValue *)_TIFFreallocExt(
+                    tif, td->td_customValues,
+                    sizeof(TIFFTagValue) * td->td_customValueCount);
+                if (!new_customValues)
+                {
+                    TIFFErrorExtR(tif, module,
+                                  "%s: Failed to allocate space for list of "
+                                  "custom values",
+                                  tif->tif_name);
+                    status = 0;
+                    goto end;
+                }
+
+                td->td_customValues = new_customValues;
+
+                tv = td->td_customValues + (td->td_customValueCount - 1);
+                tv->info = fip;
+                tv->value = NULL;
+                tv->count = 0;
+            }
+
+            /*
+             * Set custom value ... save a copy of the custom tag value.
+             */
+            /*--: Rational2Double: For Rationals evaluate "set_field_type" to
+             * determine internal storage size. */
+            tv_size = TIFFFieldSetGetSize(fip);
+            if (tv_size == 0)
+            {
+                status = 0;
+                TIFFErrorExtR(tif, module, "%s: Bad field type %d for \"%s\"",
+                              tif->tif_name, fip->field_type, fip->field_name);
+                goto end;
+            }
+
+            if (fip->field_type == TIFF_ASCII)
+            {
+                uint32_t ma;
+                const char *mb;
+                if (fip->field_passcount)
+                {
+                    assert(fip->field_writecount == TIFF_VARIABLE2);
+                    ma = (uint32_t)va_arg(ap, uint32_t);
+                    mb = (const char *)va_arg(ap, const char *);
+                }
+                else
+                {
+                    mb = (const char *)va_arg(ap, const char *);
+                    size_t len = strlen(mb) + 1;
+                    if (len >= 0x80000000U)
+                    {
+                        status = 0;
+                        TIFFErrorExtR(tif, module,
+                                      "%s: Too long string value for \"%s\". "
+                                      "Maximum supported is 2147483647 bytes",
+                                      tif->tif_name, fip->field_name);
+                        goto end;
+                    }
+                    ma = (uint32_t)len;
+                }
+                tv->count = ma;
+                setByteArray(tif, &tv->value, mb, ma, 1);
+            }
+            else
+            {
+                if (fip->field_passcount)
+                {
+                    if (fip->field_writecount == TIFF_VARIABLE2)
+                        tv->count = (uint32_t)va_arg(ap, uint32_t);
+                    else
+                        tv->count = (int)va_arg(ap, int);
+                }
+                else if (fip->field_writecount == TIFF_VARIABLE ||
+                         fip->field_writecount == TIFF_VARIABLE2)
+                    tv->count = 1;
+                else if (fip->field_writecount == TIFF_SPP)
+                    tv->count = td->td_samplesperpixel;
+                else
+                    tv->count = fip->field_writecount;
+
+                if (tv->count == 0)
+                {
+                    status = 0;
+                    TIFFErrorExtR(tif, module,
+                                  "%s: Null count for \"%s\" (type "
+                                  "%d, writecount %d, passcount %d)",
+                                  tif->tif_name, fip->field_name,
+                                  fip->field_type, fip->field_writecount,
+                                  fip->field_passcount);
+                    goto end;
+                }
+
+                tv->value = _TIFFCheckMalloc(tif, tv->count, tv_size,
+                                             "custom tag binary object");
+                if (!tv->value)
+                {
+                    status = 0;
+                    goto end;
+                }
+
+                if (fip->field_tag == TIFFTAG_DOTRANGE &&
+                    strcmp(fip->field_name, "DotRange") == 0)
+                {
+                    /* TODO: This is an evil exception and should not have been
+                       handled this way ... likely best if we move it into
+                       the directory structure with an explicit field in
+                       libtiff 4.1 and assign it a FIELD_ value */
+                    uint16_t v2[2];
+                    v2[0] = (uint16_t)va_arg(ap, int);
+                    v2[1] = (uint16_t)va_arg(ap, int);
+                    _TIFFmemcpy(tv->value, &v2, 4);
+                }
+
+                else if (fip->field_passcount ||
+                         fip->field_writecount == TIFF_VARIABLE ||
+                         fip->field_writecount == TIFF_VARIABLE2 ||
+                         fip->field_writecount == TIFF_SPP || tv->count > 1)
+                {
+                    /*--: Rational2Double: For Rationals tv_size is set above to
+                     * 4 or 8 according to fip->set_field_type! */
+                    _TIFFmemcpy(tv->value, va_arg(ap, void *),
+                                tv->count * tv_size);
+                    /* Test here for too big values for LONG8, SLONG8 in
+                     * ClassicTIFF and delete custom field from custom list */
+                    if (!(tif->tif_flags & TIFF_BIGTIFF))
+                    {
+                        if (tv->info->field_type == TIFF_LONG8)
+                        {
+                            uint64_t *pui64 = (uint64_t *)tv->value;
+                            for (int i = 0; i < tv->count; i++)
+                            {
+                                if (pui64[i] > 0xffffffffu)
+                                {
+                                    TIFFErrorExtR(
+                                        tif, module,
+                                        "%s: Bad LONG8 value %" PRIu64
+                                        " at %d. array position for \"%s\" tag "
+                                        "%d in ClassicTIFF. Tag won't be "
+                                        "written to file",
+                                        tif->tif_name, pui64[i], i,
+                                        fip->field_name, tag);
+                                    goto badvalueifd8long8;
+                                }
+                            }
+                        }
+                        else if (tv->info->field_type == TIFF_SLONG8)
+                        {
+                            int64_t *pi64 = (int64_t *)tv->value;
+                            for (int i = 0; i < tv->count; i++)
+                            {
+                                if (pi64[i] > 2147483647 ||
+                                    pi64[i] < (-2147483647 - 1))
+                                {
+                                    TIFFErrorExtR(
+                                        tif, module,
+                                        "%s: Bad SLONG8 value %" PRIi64
+                                        " at %d. array position for \"%s\" tag "
+                                        "%d in ClassicTIFF. Tag won't be "
+                                        "written to file",
+                                        tif->tif_name, pi64[i], i,
+                                        fip->field_name, tag);
+                                    goto badvalueifd8long8;
+                                }
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    char *val = (char *)tv->value;
+                    assert(tv->count == 1);
+
+                    switch (fip->field_type)
+                    {
+                        case TIFF_BYTE:
+                        case TIFF_UNDEFINED:
+                        {
+                            uint8_t v2 = (uint8_t)va_arg(ap, int);
+                            _TIFFmemcpy(val, &v2, tv_size);
+                        }
+                        break;
+                        case TIFF_SBYTE:
+                        {
+                            int8_t v2 = (int8_t)va_arg(ap, int);
+                            _TIFFmemcpy(val, &v2, tv_size);
+                        }
+                        break;
+                        case TIFF_SHORT:
+                        {
+                            uint16_t v2 = (uint16_t)va_arg(ap, int);
+                            _TIFFmemcpy(val, &v2, tv_size);
+                        }
+                        break;
+                        case TIFF_SSHORT:
+                        {
+                            int16_t v2 = (int16_t)va_arg(ap, int);
+                            _TIFFmemcpy(val, &v2, tv_size);
+                        }
+                        break;
+                        case TIFF_LONG:
+                        case TIFF_IFD:
+                        {
+                            uint32_t v2 = va_arg(ap, uint32_t);
+                            _TIFFmemcpy(val, &v2, tv_size);
+                        }
+                        break;
+                        case TIFF_SLONG:
+                        {
+                            int32_t v2 = va_arg(ap, int32_t);
+                            _TIFFmemcpy(val, &v2, tv_size);
+                        }
+                        break;
+                        case TIFF_LONG8:
+                        case TIFF_IFD8:
+                        {
+                            uint64_t v2 = va_arg(ap, uint64_t);
+                            _TIFFmemcpy(val, &v2, tv_size);
+                            /* Test here for too big values for ClassicTIFF and
+                             * delete custom field from custom list */
+                            if (!(tif->tif_flags & TIFF_BIGTIFF) &&
+                                (v2 > 0xffffffffu))
+                            {
+                                TIFFErrorExtR(
+                                    tif, module,
+                                    "%s: Bad LONG8 or IFD8 value %" PRIu64
+                                    " for \"%s\" tag %d in ClassicTIFF. Tag "
+                                    "won't be written to file",
+                                    tif->tif_name, v2, fip->field_name, tag);
+                                goto badvalueifd8long8;
+                            }
+                        }
+                        break;
+                        case TIFF_SLONG8:
+                        {
+                            int64_t v2 = va_arg(ap, int64_t);
+                            _TIFFmemcpy(val, &v2, tv_size);
+                            /* Test here for too big values for ClassicTIFF and
+                             * delete custom field from custom list */
+                            if (!(tif->tif_flags & TIFF_BIGTIFF) &&
+                                ((v2 > 2147483647) || (v2 < (-2147483647 - 1))))
+                            {
+                                TIFFErrorExtR(
+                                    tif, module,
+                                    "%s: Bad SLONG8 value %" PRIi64
+                                    " for \"%s\" tag %d in ClassicTIFF. Tag "
+                                    "won't be written to file",
+                                    tif->tif_name, v2, fip->field_name, tag);
+                                goto badvalueifd8long8;
+                            }
+                        }
+                        break;
+                        case TIFF_RATIONAL:
+                        case TIFF_SRATIONAL:
+                            /*-- Rational2Double: For Rationals tv_size is set
+                             * above to 4 or 8 according to fip->set_field_type!
+                             */
+                            {
+                                if (tv_size == 8)
+                                {
+                                    double v2 = va_arg(ap, double);
+                                    _TIFFmemcpy(val, &v2, tv_size);
+                                }
+                                else
+                                {
+                                    /*-- default should be tv_size == 4 */
+                                    float v3 = (float)va_arg(ap, double);
+                                    _TIFFmemcpy(val, &v3, tv_size);
+                                    /*-- ToDo: After Testing, this should be
+                                     * removed and tv_size==4 should be set as
+                                     * default. */
+                                    if (tv_size != 4)
+                                    {
+                                        TIFFErrorExtR(
+                                            tif, module,
+                                            "Rational2Double: .set_field_type "
+                                            "in not 4 but %d",
+                                            tv_size);
+                                    }
+                                }
+                            }
+                            break;
+                        case TIFF_FLOAT:
+                        {
+                            float v2 =
+                                _TIFFClampDoubleToFloat(va_arg(ap, double));
+                            _TIFFmemcpy(val, &v2, tv_size);
+                        }
+                        break;
+                        case TIFF_DOUBLE:
+                        {
+                            double v2 = va_arg(ap, double);
+                            _TIFFmemcpy(val, &v2, tv_size);
+                        }
+                        break;
+                        default:
+                            _TIFFmemset(val, 0, tv_size);
+                            status = 0;
+                            break;
+                    }
+                }
+            }
+        }
+    }
+    if (status)
+    {
+        const TIFFField *fip2 = TIFFFieldWithTag(tif, tag);
+        if (fip2)
+            TIFFSetFieldBit(tif, fip2->field_bit);
+        tif->tif_flags |= TIFF_DIRTYDIRECT;
+    }
+
+end:
+    va_end(ap);
+    return (status);
+badvalue:
+{
+    const TIFFField *fip2 = TIFFFieldWithTag(tif, tag);
+    TIFFErrorExtR(tif, module, "%s: Bad value %" PRIu32 " for \"%s\" tag",
+                  tif->tif_name, v, fip2 ? fip2->field_name : "Unknown");
+    va_end(ap);
+}
+    return (0);
+badvalue32:
+{
+    const TIFFField *fip2 = TIFFFieldWithTag(tif, tag);
+    TIFFErrorExtR(tif, module, "%s: Bad value %" PRIu32 " for \"%s\" tag",
+                  tif->tif_name, v32, fip2 ? fip2->field_name : "Unknown");
+    va_end(ap);
+}
+    return (0);
+badvaluedouble:
+{
+    const TIFFField *fip2 = TIFFFieldWithTag(tif, tag);
+    TIFFErrorExtR(tif, module, "%s: Bad value %f for \"%s\" tag", tif->tif_name,
+                  dblval, fip2 ? fip2->field_name : "Unknown");
+    va_end(ap);
+}
+    return (0);
+badvalueifd8long8:
+{
+    /* Error message issued already above. */
+    TIFFTagValue *tv2 = NULL;
+    int iCustom2, iC2;
+    /* Find the existing entry for this custom value. */
+    for (iCustom2 = 0; iCustom2 < td->td_customValueCount; iCustom2++)
+    {
+        if (td->td_customValues[iCustom2].info->field_tag == tag)
+        {
+            tv2 = td->td_customValues + (iCustom2);
+            break;
+        }
+    }
+    if (tv2 != NULL)
+    {
+        /* Remove custom field from custom list */
+        if (tv2->value != NULL)
+        {
+            _TIFFfreeExt(tif, tv2->value);
+            tv2->value = NULL;
+        }
+        /* Shorten list and close gap in customValues list.
+         * Re-allocation of td_customValues not necessary here. */
+        td->td_customValueCount--;
+        for (iC2 = iCustom2; iC2 < td->td_customValueCount; iC2++)
+        {
+            td->td_customValues[iC2] = td->td_customValues[iC2 + 1];
+        }
+    }
+    else
+    {
+        assert(0);
+    }
+    va_end(ap);
+}
+    return (0);
+} /*-- _TIFFVSetField() --*/
+
 /*
  * Return 1/0 according to whether or not
  * it is permissible to set the tag's value.
@@ -770,29 +1110,30 @@
  * has commenced, unless its value has no effect
  * on the format of the data that is written.
  */
-static int
-OkToChangeTag(TIFF* tif, uint32 tag)
+static int OkToChangeTag(TIFF *tif, uint32_t tag)
 {
-	const TIFFField* fip = TIFFFindField(tif, tag, TIFF_ANY);
-	if (!fip) {			/* unknown tag */
-		TIFFErrorExt(tif->tif_clientdata, "TIFFSetField", "%s: Unknown %stag %u",
-		    tif->tif_name, isPseudoTag(tag) ? "pseudo-" : "", tag);
-		return (0);
-	}
-	if (tag != TIFFTAG_IMAGELENGTH && (tif->tif_flags & TIFF_BEENWRITING) &&
-	    !fip->field_oktochange) {
-		/*
-		 * Consult info table to see if tag can be changed
-		 * after we've started writing.  We only allow changes
-		 * to those tags that don't/shouldn't affect the
-		 * compression and/or format of the data.
-		 */
-		TIFFErrorExt(tif->tif_clientdata, "TIFFSetField",
-		    "%s: Cannot modify tag \"%s\" while writing",
-		    tif->tif_name, fip->field_name);
-		return (0);
-	}
-	return (1);
+    const TIFFField *fip = TIFFFindField(tif, tag, TIFF_ANY);
+    if (!fip)
+    { /* unknown tag */
+        TIFFErrorExtR(tif, "TIFFSetField", "%s: Unknown %stag %" PRIu32,
+                      tif->tif_name, isPseudoTag(tag) ? "pseudo-" : "", tag);
+        return (0);
+    }
+    if (tag != TIFFTAG_IMAGELENGTH && (tif->tif_flags & TIFF_BEENWRITING) &&
+        !fip->field_oktochange)
+    {
+        /*
+         * Consult info table to see if tag can be changed
+         * after we've started writing.  We only allow changes
+         * to those tags that don't/shouldn't affect the
+         * compression and/or format of the data.
+         */
+        TIFFErrorExtR(tif, "TIFFSetField",
+                      "%s: Cannot modify tag \"%s\" while writing",
+                      tif->tif_name, fip->field_name);
+        return (0);
+    }
+    return (1);
 }
 
 /*
@@ -802,54 +1143,54 @@
  * when/if the directory structure is
  * updated.
  */
-int
-TIFFSetField(TIFF* tif, uint32 tag, ...)
+int TIFFSetField(TIFF *tif, uint32_t tag, ...)
 {
-	va_list ap;
-	int status;
+    va_list ap;
+    int status;
 
-	va_start(ap, tag);
-	status = TIFFVSetField(tif, tag, ap);
-	va_end(ap);
-	return (status);
+    va_start(ap, tag);
+    status = TIFFVSetField(tif, tag, ap);
+    va_end(ap);
+    return (status);
 }
 
 /*
  * Clear the contents of the field in the internal structure.
  */
-int
-TIFFUnsetField(TIFF* tif, uint32 tag)
+int TIFFUnsetField(TIFF *tif, uint32_t tag)
 {
-    const TIFFField *fip =  TIFFFieldWithTag(tif, tag);
-    TIFFDirectory* td = &tif->tif_dir;
+    const TIFFField *fip = TIFFFieldWithTag(tif, tag);
+    TIFFDirectory *td = &tif->tif_dir;
 
-    if( !fip )
+    if (!fip)
         return 0;
 
-    if( fip->field_bit != FIELD_CUSTOM )
+    if (fip->field_bit != FIELD_CUSTOM)
         TIFFClrFieldBit(tif, fip->field_bit);
     else
     {
         TIFFTagValue *tv = NULL;
         int i;
 
-        for (i = 0; i < td->td_customValueCount; i++) {
-                
+        for (i = 0; i < td->td_customValueCount; i++)
+        {
+
             tv = td->td_customValues + i;
-            if( tv->info->field_tag == tag )
+            if (tv->info->field_tag == tag)
                 break;
         }
 
-        if( i < td->td_customValueCount )
+        if (i < td->td_customValueCount)
         {
-            _TIFFfree(tv->value);
-            for( ; i < td->td_customValueCount-1; i++) {
-                td->td_customValues[i] = td->td_customValues[i+1];
+            _TIFFfreeExt(tif, tv->value);
+            for (; i < td->td_customValueCount - 1; i++)
+            {
+                td->td_customValues[i] = td->td_customValues[i + 1];
             }
             td->td_customValueCount--;
         }
     }
-        
+
     tif->tif_flags |= TIFF_DIRTYDIRECT;
 
     return (1);
@@ -861,382 +1202,392 @@
  * for building higher-level interfaces on
  * top of the library.
  */
-int
-TIFFVSetField(TIFF* tif, uint32 tag, va_list ap)
+int TIFFVSetField(TIFF *tif, uint32_t tag, va_list ap)
 {
-	return OkToChangeTag(tif, tag) ?
-	    (*tif->tif_tagmethods.vsetfield)(tif, tag, ap) : 0;
+    return OkToChangeTag(tif, tag)
+               ? (*tif->tif_tagmethods.vsetfield)(tif, tag, ap)
+               : 0;
 }
 
-static int
-_TIFFVGetField(TIFF* tif, uint32 tag, va_list ap)
+static int _TIFFVGetField(TIFF *tif, uint32_t tag, va_list ap)
 {
-	TIFFDirectory* td = &tif->tif_dir;
-	int ret_val = 1;
-	uint32 standard_tag = tag;
-	const TIFFField* fip = TIFFFindField(tif, tag, TIFF_ANY);
-	if( fip == NULL ) /* cannot happen since TIFFGetField() already checks it */
-	    return 0;
+    TIFFDirectory *td = &tif->tif_dir;
+    int ret_val = 1;
+    uint32_t standard_tag = tag;
+    const TIFFField *fip = TIFFFindField(tif, tag, TIFF_ANY);
+    if (fip == NULL) /* cannot happen since TIFFGetField() already checks it */
+        return 0;
 
-	/*
-	 * We want to force the custom code to be used for custom
-	 * fields even if the tag happens to match a well known 
-	 * one - important for reinterpreted handling of standard
-	 * tag values in custom directories (i.e. EXIF) 
-	 */
-	if (fip->field_bit == FIELD_CUSTOM) {
-		standard_tag = 0;
-	}
-	
-        if( standard_tag == TIFFTAG_NUMBEROFINKS )
+    /*
+     * We want to force the custom code to be used for custom
+     * fields even if the tag happens to match a well known
+     * one - important for reinterpreted handling of standard
+     * tag values in custom directories (i.e. EXIF)
+     */
+    if (fip->field_bit == FIELD_CUSTOM)
+    {
+        standard_tag = 0;
+    }
+
+    switch (standard_tag)
+    {
+        case TIFFTAG_SUBFILETYPE:
+            *va_arg(ap, uint32_t *) = td->td_subfiletype;
+            break;
+        case TIFFTAG_IMAGEWIDTH:
+            *va_arg(ap, uint32_t *) = td->td_imagewidth;
+            break;
+        case TIFFTAG_IMAGELENGTH:
+            *va_arg(ap, uint32_t *) = td->td_imagelength;
+            break;
+        case TIFFTAG_BITSPERSAMPLE:
+            *va_arg(ap, uint16_t *) = td->td_bitspersample;
+            break;
+        case TIFFTAG_COMPRESSION:
+            *va_arg(ap, uint16_t *) = td->td_compression;
+            break;
+        case TIFFTAG_PHOTOMETRIC:
+            *va_arg(ap, uint16_t *) = td->td_photometric;
+            break;
+        case TIFFTAG_THRESHHOLDING:
+            *va_arg(ap, uint16_t *) = td->td_threshholding;
+            break;
+        case TIFFTAG_FILLORDER:
+            *va_arg(ap, uint16_t *) = td->td_fillorder;
+            break;
+        case TIFFTAG_ORIENTATION:
+            *va_arg(ap, uint16_t *) = td->td_orientation;
+            break;
+        case TIFFTAG_SAMPLESPERPIXEL:
+            *va_arg(ap, uint16_t *) = td->td_samplesperpixel;
+            break;
+        case TIFFTAG_ROWSPERSTRIP:
+            *va_arg(ap, uint32_t *) = td->td_rowsperstrip;
+            break;
+        case TIFFTAG_MINSAMPLEVALUE:
+            *va_arg(ap, uint16_t *) = td->td_minsamplevalue;
+            break;
+        case TIFFTAG_MAXSAMPLEVALUE:
+            *va_arg(ap, uint16_t *) = td->td_maxsamplevalue;
+            break;
+        case TIFFTAG_SMINSAMPLEVALUE:
+            if (tif->tif_flags & TIFF_PERSAMPLE)
+                *va_arg(ap, double **) = td->td_sminsamplevalue;
+            else
+            {
+                /* libtiff historically treats this as a single value. */
+                uint16_t i;
+                double v = td->td_sminsamplevalue[0];
+                for (i = 1; i < td->td_samplesperpixel; ++i)
+                    if (td->td_sminsamplevalue[i] < v)
+                        v = td->td_sminsamplevalue[i];
+                *va_arg(ap, double *) = v;
+            }
+            break;
+        case TIFFTAG_SMAXSAMPLEVALUE:
+            if (tif->tif_flags & TIFF_PERSAMPLE)
+                *va_arg(ap, double **) = td->td_smaxsamplevalue;
+            else
+            {
+                /* libtiff historically treats this as a single value. */
+                uint16_t i;
+                double v = td->td_smaxsamplevalue[0];
+                for (i = 1; i < td->td_samplesperpixel; ++i)
+                    if (td->td_smaxsamplevalue[i] > v)
+                        v = td->td_smaxsamplevalue[i];
+                *va_arg(ap, double *) = v;
+            }
+            break;
+        case TIFFTAG_XRESOLUTION:
+            *va_arg(ap, float *) = td->td_xresolution;
+            break;
+        case TIFFTAG_YRESOLUTION:
+            *va_arg(ap, float *) = td->td_yresolution;
+            break;
+        case TIFFTAG_PLANARCONFIG:
+            *va_arg(ap, uint16_t *) = td->td_planarconfig;
+            break;
+        case TIFFTAG_XPOSITION:
+            *va_arg(ap, float *) = td->td_xposition;
+            break;
+        case TIFFTAG_YPOSITION:
+            *va_arg(ap, float *) = td->td_yposition;
+            break;
+        case TIFFTAG_RESOLUTIONUNIT:
+            *va_arg(ap, uint16_t *) = td->td_resolutionunit;
+            break;
+        case TIFFTAG_PAGENUMBER:
+            *va_arg(ap, uint16_t *) = td->td_pagenumber[0];
+            *va_arg(ap, uint16_t *) = td->td_pagenumber[1];
+            break;
+        case TIFFTAG_HALFTONEHINTS:
+            *va_arg(ap, uint16_t *) = td->td_halftonehints[0];
+            *va_arg(ap, uint16_t *) = td->td_halftonehints[1];
+            break;
+        case TIFFTAG_COLORMAP:
+            *va_arg(ap, const uint16_t **) = td->td_colormap[0];
+            *va_arg(ap, const uint16_t **) = td->td_colormap[1];
+            *va_arg(ap, const uint16_t **) = td->td_colormap[2];
+            break;
+        case TIFFTAG_STRIPOFFSETS:
+        case TIFFTAG_TILEOFFSETS:
+            _TIFFFillStriles(tif);
+            *va_arg(ap, const uint64_t **) = td->td_stripoffset_p;
+            if (td->td_stripoffset_p == NULL)
+                ret_val = 0;
+            break;
+        case TIFFTAG_STRIPBYTECOUNTS:
+        case TIFFTAG_TILEBYTECOUNTS:
+            _TIFFFillStriles(tif);
+            *va_arg(ap, const uint64_t **) = td->td_stripbytecount_p;
+            if (td->td_stripbytecount_p == NULL)
+                ret_val = 0;
+            break;
+        case TIFFTAG_MATTEING:
+            *va_arg(ap, uint16_t *) =
+                (td->td_extrasamples == 1 &&
+                 td->td_sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA);
+            break;
+        case TIFFTAG_EXTRASAMPLES:
+            *va_arg(ap, uint16_t *) = td->td_extrasamples;
+            *va_arg(ap, const uint16_t **) = td->td_sampleinfo;
+            break;
+        case TIFFTAG_TILEWIDTH:
+            *va_arg(ap, uint32_t *) = td->td_tilewidth;
+            break;
+        case TIFFTAG_TILELENGTH:
+            *va_arg(ap, uint32_t *) = td->td_tilelength;
+            break;
+        case TIFFTAG_TILEDEPTH:
+            *va_arg(ap, uint32_t *) = td->td_tiledepth;
+            break;
+        case TIFFTAG_DATATYPE:
+            switch (td->td_sampleformat)
+            {
+                case SAMPLEFORMAT_UINT:
+                    *va_arg(ap, uint16_t *) = DATATYPE_UINT;
+                    break;
+                case SAMPLEFORMAT_INT:
+                    *va_arg(ap, uint16_t *) = DATATYPE_INT;
+                    break;
+                case SAMPLEFORMAT_IEEEFP:
+                    *va_arg(ap, uint16_t *) = DATATYPE_IEEEFP;
+                    break;
+                case SAMPLEFORMAT_VOID:
+                    *va_arg(ap, uint16_t *) = DATATYPE_VOID;
+                    break;
+            }
+            break;
+        case TIFFTAG_SAMPLEFORMAT:
+            *va_arg(ap, uint16_t *) = td->td_sampleformat;
+            break;
+        case TIFFTAG_IMAGEDEPTH:
+            *va_arg(ap, uint32_t *) = td->td_imagedepth;
+            break;
+        case TIFFTAG_SUBIFD:
+            *va_arg(ap, uint16_t *) = td->td_nsubifd;
+            *va_arg(ap, const uint64_t **) = td->td_subifd;
+            break;
+        case TIFFTAG_YCBCRPOSITIONING:
+            *va_arg(ap, uint16_t *) = td->td_ycbcrpositioning;
+            break;
+        case TIFFTAG_YCBCRSUBSAMPLING:
+            *va_arg(ap, uint16_t *) = td->td_ycbcrsubsampling[0];
+            *va_arg(ap, uint16_t *) = td->td_ycbcrsubsampling[1];
+            break;
+        case TIFFTAG_TRANSFERFUNCTION:
+            *va_arg(ap, const uint16_t **) = td->td_transferfunction[0];
+            if (td->td_samplesperpixel - td->td_extrasamples > 1)
+            {
+                *va_arg(ap, const uint16_t **) = td->td_transferfunction[1];
+                *va_arg(ap, const uint16_t **) = td->td_transferfunction[2];
+            }
+            else
+            {
+                *va_arg(ap, const uint16_t **) = NULL;
+                *va_arg(ap, const uint16_t **) = NULL;
+            }
+            break;
+        case TIFFTAG_REFERENCEBLACKWHITE:
+            *va_arg(ap, const float **) = td->td_refblackwhite;
+            break;
+        case TIFFTAG_INKNAMES:
+            *va_arg(ap, const char **) = td->td_inknames;
+            break;
+        case TIFFTAG_NUMBEROFINKS:
+            *va_arg(ap, uint16_t *) = td->td_numberofinks;
+            break;
+        default:
         {
             int i;
-            for (i = 0; i < td->td_customValueCount; i++) {
-                uint16 val;
-                TIFFTagValue *tv = td->td_customValues + i;
-                if (tv->info->field_tag != standard_tag)
-                    continue;
-                if( tv->value == NULL )
-                    return 0;
-                val = *(uint16 *)tv->value;
-                /* Truncate to SamplesPerPixel, since the */
-                /* setting code for INKNAMES assume that there are SamplesPerPixel */
-                /* inknames. */
-                /* Fixes http://bugzilla.maptools.org/show_bug.cgi?id=2599 */
-                if( val > td->td_samplesperpixel )
-                {
-                    TIFFWarningExt(tif->tif_clientdata,"_TIFFVGetField",
-                                   "Truncating NumberOfInks from %u to %u",
-                                   val, td->td_samplesperpixel);
-                    val = td->td_samplesperpixel;
-                }
-                *va_arg(ap, uint16*) = val;
-                return 1;
+
+            /*
+             * This can happen if multiple images are open
+             * with different codecs which have private
+             * tags.  The global tag information table may
+             * then have tags that are valid for one file
+             * but not the other. If the client tries to
+             * get a tag that is not valid for the image's
+             * codec then we'll arrive here.
+             */
+            if (fip->field_bit != FIELD_CUSTOM)
+            {
+                TIFFErrorExtR(tif, "_TIFFVGetField",
+                              "%s: Invalid %stag \"%s\" "
+                              "(not supported by codec)",
+                              tif->tif_name, isPseudoTag(tag) ? "pseudo-" : "",
+                              fip->field_name);
+                ret_val = 0;
+                break;
             }
-            return 0;
+
+            /*
+             * Do we have a custom value?
+             */
+            ret_val = 0;
+            for (i = 0; i < td->td_customValueCount; i++)
+            {
+                TIFFTagValue *tv = td->td_customValues + i;
+
+                if (tv->info->field_tag != tag)
+                    continue;
+
+                if (fip->field_passcount)
+                {
+                    if (fip->field_readcount == TIFF_VARIABLE2)
+                        *va_arg(ap, uint32_t *) = (uint32_t)tv->count;
+                    else /* Assume TIFF_VARIABLE */
+                        *va_arg(ap, uint16_t *) = (uint16_t)tv->count;
+                    *va_arg(ap, const void **) = tv->value;
+                    ret_val = 1;
+                }
+                else if (fip->field_tag == TIFFTAG_DOTRANGE &&
+                         strcmp(fip->field_name, "DotRange") == 0)
+                {
+                    /* TODO: This is an evil exception and should not have been
+                       handled this way ... likely best if we move it into
+                       the directory structure with an explicit field in
+                       libtiff 4.1 and assign it a FIELD_ value */
+                    *va_arg(ap, uint16_t *) = ((uint16_t *)tv->value)[0];
+                    *va_arg(ap, uint16_t *) = ((uint16_t *)tv->value)[1];
+                    ret_val = 1;
+                }
+                else
+                {
+                    if (fip->field_type == TIFF_ASCII ||
+                        fip->field_readcount == TIFF_VARIABLE ||
+                        fip->field_readcount == TIFF_VARIABLE2 ||
+                        fip->field_readcount == TIFF_SPP || tv->count > 1)
+                    {
+                        *va_arg(ap, void **) = tv->value;
+                        ret_val = 1;
+                    }
+                    else
+                    {
+                        char *val = (char *)tv->value;
+                        assert(tv->count == 1);
+                        switch (fip->field_type)
+                        {
+                            case TIFF_BYTE:
+                            case TIFF_UNDEFINED:
+                                *va_arg(ap, uint8_t *) = *(uint8_t *)val;
+                                ret_val = 1;
+                                break;
+                            case TIFF_SBYTE:
+                                *va_arg(ap, int8_t *) = *(int8_t *)val;
+                                ret_val = 1;
+                                break;
+                            case TIFF_SHORT:
+                                *va_arg(ap, uint16_t *) = *(uint16_t *)val;
+                                ret_val = 1;
+                                break;
+                            case TIFF_SSHORT:
+                                *va_arg(ap, int16_t *) = *(int16_t *)val;
+                                ret_val = 1;
+                                break;
+                            case TIFF_LONG:
+                            case TIFF_IFD:
+                                *va_arg(ap, uint32_t *) = *(uint32_t *)val;
+                                ret_val = 1;
+                                break;
+                            case TIFF_SLONG:
+                                *va_arg(ap, int32_t *) = *(int32_t *)val;
+                                ret_val = 1;
+                                break;
+                            case TIFF_LONG8:
+                            case TIFF_IFD8:
+                                *va_arg(ap, uint64_t *) = *(uint64_t *)val;
+                                ret_val = 1;
+                                break;
+                            case TIFF_SLONG8:
+                                *va_arg(ap, int64_t *) = *(int64_t *)val;
+                                ret_val = 1;
+                                break;
+                            case TIFF_RATIONAL:
+                            case TIFF_SRATIONAL:
+                            {
+                                /*-- Rational2Double: For Rationals evaluate
+                                 * "set_field_type" to determine internal
+                                 * storage size and return value size. */
+                                int tv_size = TIFFFieldSetGetSize(fip);
+                                if (tv_size == 8)
+                                {
+                                    *va_arg(ap, double *) = *(double *)val;
+                                    ret_val = 1;
+                                }
+                                else
+                                {
+                                    /*-- default should be tv_size == 4  */
+                                    *va_arg(ap, float *) = *(float *)val;
+                                    ret_val = 1;
+                                    /*-- ToDo: After Testing, this should be
+                                     * removed and tv_size==4 should be set as
+                                     * default. */
+                                    if (tv_size != 4)
+                                    {
+                                        TIFFErrorExtR(
+                                            tif, "_TIFFVGetField",
+                                            "Rational2Double: .set_field_type "
+                                            "in not 4 but %d",
+                                            tv_size);
+                                    }
+                                }
+                            }
+                            break;
+                            case TIFF_FLOAT:
+                                *va_arg(ap, float *) = *(float *)val;
+                                ret_val = 1;
+                                break;
+                            case TIFF_DOUBLE:
+                                *va_arg(ap, double *) = *(double *)val;
+                                ret_val = 1;
+                                break;
+                            default:
+                                ret_val = 0;
+                                break;
+                        }
+                    }
+                }
+                break;
+            }
         }
-
-	switch (standard_tag) {
-		case TIFFTAG_SUBFILETYPE:
-			*va_arg(ap, uint32*) = td->td_subfiletype;
-			break;
-		case TIFFTAG_IMAGEWIDTH:
-			*va_arg(ap, uint32*) = td->td_imagewidth;
-			break;
-		case TIFFTAG_IMAGELENGTH:
-			*va_arg(ap, uint32*) = td->td_imagelength;
-			break;
-		case TIFFTAG_BITSPERSAMPLE:
-			*va_arg(ap, uint16*) = td->td_bitspersample;
-			break;
-		case TIFFTAG_COMPRESSION:
-			*va_arg(ap, uint16*) = td->td_compression;
-			break;
-		case TIFFTAG_PHOTOMETRIC:
-			*va_arg(ap, uint16*) = td->td_photometric;
-			break;
-		case TIFFTAG_THRESHHOLDING:
-			*va_arg(ap, uint16*) = td->td_threshholding;
-			break;
-		case TIFFTAG_FILLORDER:
-			*va_arg(ap, uint16*) = td->td_fillorder;
-			break;
-		case TIFFTAG_ORIENTATION:
-			*va_arg(ap, uint16*) = td->td_orientation;
-			break;
-		case TIFFTAG_SAMPLESPERPIXEL:
-			*va_arg(ap, uint16*) = td->td_samplesperpixel;
-			break;
-		case TIFFTAG_ROWSPERSTRIP:
-			*va_arg(ap, uint32*) = td->td_rowsperstrip;
-			break;
-		case TIFFTAG_MINSAMPLEVALUE:
-			*va_arg(ap, uint16*) = td->td_minsamplevalue;
-			break;
-		case TIFFTAG_MAXSAMPLEVALUE:
-			*va_arg(ap, uint16*) = td->td_maxsamplevalue;
-			break;
-		case TIFFTAG_SMINSAMPLEVALUE:
-			if (tif->tif_flags & TIFF_PERSAMPLE)
-				*va_arg(ap, double**) = td->td_sminsamplevalue;
-			else
-			{
-				/* libtiff historically treats this as a single value. */
-				uint16 i;
-				double v = td->td_sminsamplevalue[0];
-				for (i=1; i < td->td_samplesperpixel; ++i)
-					if( td->td_sminsamplevalue[i] < v )
-						v = td->td_sminsamplevalue[i];
-				*va_arg(ap, double*) = v;
-			}
-			break;
-		case TIFFTAG_SMAXSAMPLEVALUE:
-			if (tif->tif_flags & TIFF_PERSAMPLE)
-				*va_arg(ap, double**) = td->td_smaxsamplevalue;
-			else
-			{
-				/* libtiff historically treats this as a single value. */
-				uint16 i;
-				double v = td->td_smaxsamplevalue[0];
-				for (i=1; i < td->td_samplesperpixel; ++i)
-					if( td->td_smaxsamplevalue[i] > v )
-						v = td->td_smaxsamplevalue[i];
-				*va_arg(ap, double*) = v;
-			}
-			break;
-		case TIFFTAG_XRESOLUTION:
-			*va_arg(ap, float*) = td->td_xresolution;
-			break;
-		case TIFFTAG_YRESOLUTION:
-			*va_arg(ap, float*) = td->td_yresolution;
-			break;
-		case TIFFTAG_PLANARCONFIG:
-			*va_arg(ap, uint16*) = td->td_planarconfig;
-			break;
-		case TIFFTAG_XPOSITION:
-			*va_arg(ap, float*) = td->td_xposition;
-			break;
-		case TIFFTAG_YPOSITION:
-			*va_arg(ap, float*) = td->td_yposition;
-			break;
-		case TIFFTAG_RESOLUTIONUNIT:
-			*va_arg(ap, uint16*) = td->td_resolutionunit;
-			break;
-		case TIFFTAG_PAGENUMBER:
-			*va_arg(ap, uint16*) = td->td_pagenumber[0];
-			*va_arg(ap, uint16*) = td->td_pagenumber[1];
-			break;
-		case TIFFTAG_HALFTONEHINTS:
-			*va_arg(ap, uint16*) = td->td_halftonehints[0];
-			*va_arg(ap, uint16*) = td->td_halftonehints[1];
-			break;
-		case TIFFTAG_COLORMAP:
-			*va_arg(ap, uint16**) = td->td_colormap[0];
-			*va_arg(ap, uint16**) = td->td_colormap[1];
-			*va_arg(ap, uint16**) = td->td_colormap[2];
-			break;
-		case TIFFTAG_STRIPOFFSETS:
-		case TIFFTAG_TILEOFFSETS:
-			_TIFFFillStriles( tif );
-			*va_arg(ap, uint64**) = td->td_stripoffset_p;
-			break;
-		case TIFFTAG_STRIPBYTECOUNTS:
-		case TIFFTAG_TILEBYTECOUNTS:
-			_TIFFFillStriles( tif );
-			*va_arg(ap, uint64**) = td->td_stripbytecount_p;
-			break;
-		case TIFFTAG_MATTEING:
-			*va_arg(ap, uint16*) =
-			    (td->td_extrasamples == 1 &&
-			    td->td_sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA);
-			break;
-		case TIFFTAG_EXTRASAMPLES:
-			*va_arg(ap, uint16*) = td->td_extrasamples;
-			*va_arg(ap, uint16**) = td->td_sampleinfo;
-			break;
-		case TIFFTAG_TILEWIDTH:
-			*va_arg(ap, uint32*) = td->td_tilewidth;
-			break;
-		case TIFFTAG_TILELENGTH:
-			*va_arg(ap, uint32*) = td->td_tilelength;
-			break;
-		case TIFFTAG_TILEDEPTH:
-			*va_arg(ap, uint32*) = td->td_tiledepth;
-			break;
-		case TIFFTAG_DATATYPE:
-			switch (td->td_sampleformat) {
-				case SAMPLEFORMAT_UINT:
-					*va_arg(ap, uint16*) = DATATYPE_UINT;
-					break;
-				case SAMPLEFORMAT_INT:
-					*va_arg(ap, uint16*) = DATATYPE_INT;
-					break;
-				case SAMPLEFORMAT_IEEEFP:
-					*va_arg(ap, uint16*) = DATATYPE_IEEEFP;
-					break;
-				case SAMPLEFORMAT_VOID:
-					*va_arg(ap, uint16*) = DATATYPE_VOID;
-					break;
-			}
-			break;
-		case TIFFTAG_SAMPLEFORMAT:
-			*va_arg(ap, uint16*) = td->td_sampleformat;
-			break;
-		case TIFFTAG_IMAGEDEPTH:
-			*va_arg(ap, uint32*) = td->td_imagedepth;
-			break;
-		case TIFFTAG_SUBIFD:
-			*va_arg(ap, uint16*) = td->td_nsubifd;
-			*va_arg(ap, uint64**) = td->td_subifd;
-			break;
-		case TIFFTAG_YCBCRPOSITIONING:
-			*va_arg(ap, uint16*) = td->td_ycbcrpositioning;
-			break;
-		case TIFFTAG_YCBCRSUBSAMPLING:
-			*va_arg(ap, uint16*) = td->td_ycbcrsubsampling[0];
-			*va_arg(ap, uint16*) = td->td_ycbcrsubsampling[1];
-			break;
-		case TIFFTAG_TRANSFERFUNCTION:
-			*va_arg(ap, uint16**) = td->td_transferfunction[0];
-			if (td->td_samplesperpixel - td->td_extrasamples > 1) {
-				*va_arg(ap, uint16**) = td->td_transferfunction[1];
-				*va_arg(ap, uint16**) = td->td_transferfunction[2];
-			} else {
-				*va_arg(ap, uint16**) = NULL;
-				*va_arg(ap, uint16**) = NULL;
-			}
-			break;
-		case TIFFTAG_REFERENCEBLACKWHITE:
-			*va_arg(ap, float**) = td->td_refblackwhite;
-			break;
-		case TIFFTAG_INKNAMES:
-			*va_arg(ap, char**) = td->td_inknames;
-			break;
-		default:
-			{
-				int i;
-
-				/*
-				 * This can happen if multiple images are open
-				 * with different codecs which have private
-				 * tags.  The global tag information table may
-				 * then have tags that are valid for one file
-				 * but not the other. If the client tries to
-				 * get a tag that is not valid for the image's
-				 * codec then we'll arrive here.
-				 */
-				if( fip->field_bit != FIELD_CUSTOM )
-				{
-					TIFFErrorExt(tif->tif_clientdata, "_TIFFVGetField",
-					    "%s: Invalid %stag \"%s\" "
-					    "(not supported by codec)",
-					    tif->tif_name,
-					    isPseudoTag(tag) ? "pseudo-" : "",
-					    fip->field_name);
-					ret_val = 0;
-					break;
-				}
-
-				/*
-				 * Do we have a custom value?
-				 */
-				ret_val = 0;
-				for (i = 0; i < td->td_customValueCount; i++) {
-					TIFFTagValue *tv = td->td_customValues + i;
-
-					if (tv->info->field_tag != tag)
-						continue;
-
-					if (fip->field_passcount) {
-						if (fip->field_readcount == TIFF_VARIABLE2)
-							*va_arg(ap, uint32*) = (uint32)tv->count;
-						else  /* Assume TIFF_VARIABLE */
-							*va_arg(ap, uint16*) = (uint16)tv->count;
-						*va_arg(ap, void **) = tv->value;
-						ret_val = 1;
-					} else if (fip->field_tag == TIFFTAG_DOTRANGE
-						   && strcmp(fip->field_name,"DotRange") == 0) {
-						/* TODO: This is an evil exception and should not have been
-						   handled this way ... likely best if we move it into
-						   the directory structure with an explicit field in 
-						   libtiff 4.1 and assign it a FIELD_ value */
-						*va_arg(ap, uint16*) = ((uint16 *)tv->value)[0];
-						*va_arg(ap, uint16*) = ((uint16 *)tv->value)[1];
-						ret_val = 1;
-					} else {
-						if (fip->field_type == TIFF_ASCII
-						    || fip->field_readcount == TIFF_VARIABLE
-						    || fip->field_readcount == TIFF_VARIABLE2
-						    || fip->field_readcount == TIFF_SPP
-						    || tv->count > 1) {
-							*va_arg(ap, void **) = tv->value;
-							ret_val = 1;
-						} else {
-							char *val = (char *)tv->value;
-							assert( tv->count == 1 );
-							switch (fip->field_type) {
-							case TIFF_BYTE:
-							case TIFF_UNDEFINED:
-								*va_arg(ap, uint8*) =
-									*(uint8 *)val;
-								ret_val = 1;
-								break;
-							case TIFF_SBYTE:
-								*va_arg(ap, int8*) =
-									*(int8 *)val;
-								ret_val = 1;
-								break;
-							case TIFF_SHORT:
-								*va_arg(ap, uint16*) =
-									*(uint16 *)val;
-								ret_val = 1;
-								break;
-							case TIFF_SSHORT:
-								*va_arg(ap, int16*) =
-									*(int16 *)val;
-								ret_val = 1;
-								break;
-							case TIFF_LONG:
-							case TIFF_IFD:
-								*va_arg(ap, uint32*) =
-									*(uint32 *)val;
-								ret_val = 1;
-								break;
-							case TIFF_SLONG:
-								*va_arg(ap, int32*) =
-									*(int32 *)val;
-								ret_val = 1;
-								break;
-							case TIFF_LONG8:
-							case TIFF_IFD8:
-								*va_arg(ap, uint64*) =
-									*(uint64 *)val;
-								ret_val = 1;
-								break;
-							case TIFF_SLONG8:
-								*va_arg(ap, int64*) =
-									*(int64 *)val;
-								ret_val = 1;
-								break;
-							case TIFF_RATIONAL:
-							case TIFF_SRATIONAL:
-							case TIFF_FLOAT:
-								*va_arg(ap, float*) =
-									*(float *)val;
-								ret_val = 1;
-								break;
-							case TIFF_DOUBLE:
-								*va_arg(ap, double*) =
-									*(double *)val;
-								ret_val = 1;
-								break;
-							default:
-								ret_val = 0;
-								break;
-							}
-						}
-					}
-					break;
-				}
-			}
-	}
-	return(ret_val);
+    }
+    return (ret_val);
 }
 
 /*
  * Return the value of a field in the
  * internal directory structure.
  */
-int
-TIFFGetField(TIFF* tif, uint32 tag, ...)
+int TIFFGetField(TIFF *tif, uint32_t tag, ...)
 {
-	int status;
-	va_list ap;
+    int status;
+    va_list ap;
 
-	va_start(ap, tag);
-	status = TIFFVGetField(tif, tag, ap);
-	va_end(ap);
-	return (status);
+    va_start(ap, tag);
+    status = TIFFVGetField(tif, tag, ap);
+    va_end(ap);
+    return (status);
 }
 
 /*
@@ -1245,74 +1596,75 @@
  * for building higher-level interfaces on
  * top of the library.
  */
-int
-TIFFVGetField(TIFF* tif, uint32 tag, va_list ap)
+int TIFFVGetField(TIFF *tif, uint32_t tag, va_list ap)
 {
-	const TIFFField* fip = TIFFFindField(tif, tag, TIFF_ANY);
-	return (fip && (isPseudoTag(tag) || TIFFFieldSet(tif, fip->field_bit)) ?
-	    (*tif->tif_tagmethods.vgetfield)(tif, tag, ap) : 0);
+    const TIFFField *fip = TIFFFindField(tif, tag, TIFF_ANY);
+    return (fip && (isPseudoTag(tag) || TIFFFieldSet(tif, fip->field_bit))
+                ? (*tif->tif_tagmethods.vgetfield)(tif, tag, ap)
+                : 0);
 }
 
-#define	CleanupField(member) {		\
-    if (td->member) {			\
-	_TIFFfree(td->member);		\
-	td->member = 0;			\
-    }					\
-}
+#define CleanupField(member)                                                   \
+    {                                                                          \
+        if (td->member)                                                        \
+        {                                                                      \
+            _TIFFfreeExt(tif, td->member);                                     \
+            td->member = 0;                                                    \
+        }                                                                      \
+    }
 
 /*
  * Release storage associated with a directory.
  */
-void
-TIFFFreeDirectory(TIFF* tif)
+void TIFFFreeDirectory(TIFF *tif)
 {
-	TIFFDirectory *td = &tif->tif_dir;
-	int            i;
+    TIFFDirectory *td = &tif->tif_dir;
+    int i;
 
-	_TIFFmemset(td->td_fieldsset, 0, FIELD_SETLONGS);
-	CleanupField(td_sminsamplevalue);
-	CleanupField(td_smaxsamplevalue);
-	CleanupField(td_colormap[0]);
-	CleanupField(td_colormap[1]);
-	CleanupField(td_colormap[2]);
-	CleanupField(td_sampleinfo);
-	CleanupField(td_subifd);
-	CleanupField(td_inknames);
-	CleanupField(td_refblackwhite);
-	CleanupField(td_transferfunction[0]);
-	CleanupField(td_transferfunction[1]);
-	CleanupField(td_transferfunction[2]);
-	CleanupField(td_stripoffset_p);
-	CleanupField(td_stripbytecount_p);
-        td->td_stripoffsetbyteallocsize = 0;
-	TIFFClrFieldBit(tif, FIELD_YCBCRSUBSAMPLING);
-	TIFFClrFieldBit(tif, FIELD_YCBCRPOSITIONING);
+    _TIFFmemset(td->td_fieldsset, 0, sizeof(td->td_fieldsset));
+    CleanupField(td_sminsamplevalue);
+    CleanupField(td_smaxsamplevalue);
+    CleanupField(td_colormap[0]);
+    CleanupField(td_colormap[1]);
+    CleanupField(td_colormap[2]);
+    CleanupField(td_sampleinfo);
+    CleanupField(td_subifd);
+    CleanupField(td_inknames);
+    CleanupField(td_refblackwhite);
+    CleanupField(td_transferfunction[0]);
+    CleanupField(td_transferfunction[1]);
+    CleanupField(td_transferfunction[2]);
+    CleanupField(td_stripoffset_p);
+    CleanupField(td_stripbytecount_p);
+    td->td_stripoffsetbyteallocsize = 0;
+    TIFFClrFieldBit(tif, FIELD_YCBCRSUBSAMPLING);
+    TIFFClrFieldBit(tif, FIELD_YCBCRPOSITIONING);
 
-	/* Cleanup custom tag values */
-	for( i = 0; i < td->td_customValueCount; i++ ) {
-		if (td->td_customValues[i].value)
-			_TIFFfree(td->td_customValues[i].value);
-	}
+    /* Cleanup custom tag values */
+    for (i = 0; i < td->td_customValueCount; i++)
+    {
+        if (td->td_customValues[i].value)
+            _TIFFfreeExt(tif, td->td_customValues[i].value);
+    }
 
-	td->td_customValueCount = 0;
-	CleanupField(td_customValues);
+    td->td_customValueCount = 0;
+    CleanupField(td_customValues);
 
-        _TIFFmemset( &(td->td_stripoffset_entry), 0, sizeof(TIFFDirEntry));
-        _TIFFmemset( &(td->td_stripbytecount_entry), 0, sizeof(TIFFDirEntry));
+    _TIFFmemset(&(td->td_stripoffset_entry), 0, sizeof(TIFFDirEntry));
+    _TIFFmemset(&(td->td_stripbytecount_entry), 0, sizeof(TIFFDirEntry));
 }
 #undef CleanupField
 
 /*
  * Client Tag extension support (from Niles Ritter).
  */
-static TIFFExtendProc _TIFFextender = (TIFFExtendProc) NULL;
+static TIFFExtendProc _TIFFextender = (TIFFExtendProc)NULL;
 
-TIFFExtendProc
-TIFFSetTagExtender(TIFFExtendProc extender)
+TIFFExtendProc TIFFSetTagExtender(TIFFExtendProc extender)
 {
-	TIFFExtendProc prev = _TIFFextender;
-	_TIFFextender = extender;
-	return (prev);
+    TIFFExtendProc prev = _TIFFextender;
+    _TIFFextender = extender;
+    return (prev);
 }
 
 /*
@@ -1322,322 +1674,433 @@
  * The newly created directory will not exist on the file till
  * TIFFWriteDirectory(), TIFFFlush() or TIFFClose() is called.
  */
-int
-TIFFCreateDirectory(TIFF* tif)
+int TIFFCreateDirectory(TIFF *tif)
 {
-	TIFFDefaultDirectory(tif);
-	tif->tif_diroff = 0;
-	tif->tif_nextdiroff = 0;
-	tif->tif_curoff = 0;
-	tif->tif_row = (uint32) -1;
-	tif->tif_curstrip = (uint32) -1;
+    TIFFDefaultDirectory(tif);
+    tif->tif_diroff = 0;
+    tif->tif_nextdiroff = 0;
+    tif->tif_curoff = 0;
+    tif->tif_row = (uint32_t)-1;
+    tif->tif_curstrip = (uint32_t)-1;
 
-	return 0;
+    return 0;
 }
 
-int
-TIFFCreateCustomDirectory(TIFF* tif, const TIFFFieldArray* infoarray)
+int TIFFCreateCustomDirectory(TIFF *tif, const TIFFFieldArray *infoarray)
 {
-	TIFFDefaultDirectory(tif);
+    TIFFDefaultDirectory(tif);
 
-	/*
-	 * Reset the field definitions to match the application provided list. 
-	 * Hopefully TIFFDefaultDirectory() won't have done anything irreversable
-	 * based on it's assumption this is an image directory.
-	 */
-	_TIFFSetupFields(tif, infoarray);
+    /*
+     * Reset the field definitions to match the application provided list.
+     * Hopefully TIFFDefaultDirectory() won't have done anything irreversible
+     * based on it's assumption this is an image directory.
+     */
+    _TIFFSetupFields(tif, infoarray);
 
-	tif->tif_diroff = 0;
-	tif->tif_nextdiroff = 0;
-	tif->tif_curoff = 0;
-	tif->tif_row = (uint32) -1;
-	tif->tif_curstrip = (uint32) -1;
+    tif->tif_diroff = 0;
+    tif->tif_nextdiroff = 0;
+    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;
+    return 0;
 }
 
-int
-TIFFCreateEXIFDirectory(TIFF* tif)
+int TIFFCreateEXIFDirectory(TIFF *tif)
 {
-	const TIFFFieldArray* exifFieldArray;
-	exifFieldArray = _TIFFGetExifFields();
-	return TIFFCreateCustomDirectory(tif, exifFieldArray);
+    const TIFFFieldArray *exifFieldArray;
+    exifFieldArray = _TIFFGetExifFields();
+    return TIFFCreateCustomDirectory(tif, exifFieldArray);
+}
+
+/*
+ * Creates the EXIF GPS custom directory
+ */
+int TIFFCreateGPSDirectory(TIFF *tif)
+{
+    const TIFFFieldArray *gpsFieldArray;
+    gpsFieldArray = _TIFFGetGpsFields();
+    return TIFFCreateCustomDirectory(tif, gpsFieldArray);
 }
 
 /*
  * Setup a default directory structure.
  */
-int
-TIFFDefaultDirectory(TIFF* tif)
+int TIFFDefaultDirectory(TIFF *tif)
 {
-	register TIFFDirectory* td = &tif->tif_dir;
-	const TIFFFieldArray* tiffFieldArray;
+    register TIFFDirectory *td = &tif->tif_dir;
+    const TIFFFieldArray *tiffFieldArray;
 
-	tiffFieldArray = _TIFFGetFields();
-	_TIFFSetupFields(tif, tiffFieldArray);   
+    tiffFieldArray = _TIFFGetFields();
+    _TIFFSetupFields(tif, tiffFieldArray);
 
-	_TIFFmemset(td, 0, sizeof (*td));
-	td->td_fillorder = FILLORDER_MSB2LSB;
-	td->td_bitspersample = 1;
-	td->td_threshholding = THRESHHOLD_BILEVEL;
-	td->td_orientation = ORIENTATION_TOPLEFT;
-	td->td_samplesperpixel = 1;
-	td->td_rowsperstrip = (uint32) -1;
-	td->td_tilewidth = 0;
-	td->td_tilelength = 0;
-	td->td_tiledepth = 1;
+    _TIFFmemset(td, 0, sizeof(*td));
+    td->td_fillorder = FILLORDER_MSB2LSB;
+    td->td_bitspersample = 1;
+    td->td_threshholding = THRESHHOLD_BILEVEL;
+    td->td_orientation = ORIENTATION_TOPLEFT;
+    td->td_samplesperpixel = 1;
+    td->td_rowsperstrip = (uint32_t)-1;
+    td->td_tilewidth = 0;
+    td->td_tilelength = 0;
+    td->td_tiledepth = 1;
 #ifdef STRIPBYTECOUNTSORTED_UNUSED
-	td->td_stripbytecountsorted = 1; /* Our own arrays always sorted. */  
+    td->td_stripbytecountsorted = 1; /* Our own arrays always sorted. */
 #endif
-	td->td_resolutionunit = RESUNIT_INCH;
-	td->td_sampleformat = SAMPLEFORMAT_UINT;
-	td->td_imagedepth = 1;
-	td->td_ycbcrsubsampling[0] = 2;
-	td->td_ycbcrsubsampling[1] = 2;
-	td->td_ycbcrpositioning = YCBCRPOSITION_CENTERED;
-	tif->tif_postdecode = _TIFFNoPostDecode;  
-	tif->tif_foundfield = NULL;
-	tif->tif_tagmethods.vsetfield = _TIFFVSetField;  
-	tif->tif_tagmethods.vgetfield = _TIFFVGetField;
-	tif->tif_tagmethods.printdir = NULL;
-	/*
-	 *  Give client code a chance to install their own
-	 *  tag extensions & methods, prior to compression overloads,
-	 *  but do some prior cleanup first. (http://trac.osgeo.org/gdal/ticket/5054)
-	 */
-	if (tif->tif_nfieldscompat > 0) {
-		uint32 i;
+    td->td_resolutionunit = RESUNIT_INCH;
+    td->td_sampleformat = SAMPLEFORMAT_UINT;
+    td->td_imagedepth = 1;
+    td->td_ycbcrsubsampling[0] = 2;
+    td->td_ycbcrsubsampling[1] = 2;
+    td->td_ycbcrpositioning = YCBCRPOSITION_CENTERED;
+    tif->tif_postdecode = _TIFFNoPostDecode;
+    tif->tif_foundfield = NULL;
+    tif->tif_tagmethods.vsetfield = _TIFFVSetField;
+    tif->tif_tagmethods.vgetfield = _TIFFVGetField;
+    tif->tif_tagmethods.printdir = NULL;
+    /* additional default values */
+    td->td_planarconfig = PLANARCONFIG_CONTIG;
+    td->td_compression = COMPRESSION_NONE;
+    td->td_subfiletype = 0;
+    td->td_minsamplevalue = 0;
+    /* td_bitspersample=1 is always set in TIFFDefaultDirectory().
+     * Therefore, td_maxsamplevalue has to be re-calculated in
+     * TIFFGetFieldDefaulted(). */
+    td->td_maxsamplevalue = 1; /* Default for td_bitspersample=1 */
+    td->td_extrasamples = 0;
+    td->td_sampleinfo = NULL;
 
-		for (i = 0; i < tif->tif_nfieldscompat; i++) {
-				if (tif->tif_fieldscompat[i].allocated_size)
-						_TIFFfree(tif->tif_fieldscompat[i].fields);
-		}
-		_TIFFfree(tif->tif_fieldscompat);
-		tif->tif_nfieldscompat = 0;
-		tif->tif_fieldscompat = NULL;
-	}
-	if (_TIFFextender)
-		(*_TIFFextender)(tif);
-	(void) TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
-	/*
-	 * NB: The directory is marked dirty as a result of setting
-	 * up the default compression scheme.  However, this really
-	 * isn't correct -- we want TIFF_DIRTYDIRECT to be set only
-	 * if the user does something.  We could just do the setup
-	 * by hand, but it seems better to use the normal mechanism
-	 * (i.e. TIFFSetField).
-	 */
-	tif->tif_flags &= ~TIFF_DIRTYDIRECT;
+    /*
+     *  Give client code a chance to install their own
+     *  tag extensions & methods, prior to compression overloads,
+     *  but do some prior cleanup first.
+     * (http://trac.osgeo.org/gdal/ticket/5054)
+     */
+    if (tif->tif_nfieldscompat > 0)
+    {
+        uint32_t i;
 
-	/*
-	 * As per http://bugzilla.remotesensing.org/show_bug.cgi?id=19
-	 * we clear the ISTILED flag when setting up a new directory.
-	 * Should we also be clearing stuff like INSUBIFD?
-	 */
-	tif->tif_flags &= ~TIFF_ISTILED;
+        for (i = 0; i < tif->tif_nfieldscompat; i++)
+        {
+            if (tif->tif_fieldscompat[i].allocated_size)
+                _TIFFfreeExt(tif, tif->tif_fieldscompat[i].fields);
+        }
+        _TIFFfreeExt(tif, tif->tif_fieldscompat);
+        tif->tif_nfieldscompat = 0;
+        tif->tif_fieldscompat = NULL;
+    }
+    if (_TIFFextender)
+        (*_TIFFextender)(tif);
+    (void)TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
+    /*
+     * NB: The directory is marked dirty as a result of setting
+     * up the default compression scheme.  However, this really
+     * isn't correct -- we want TIFF_DIRTYDIRECT to be set only
+     * if the user does something.  We could just do the setup
+     * by hand, but it seems better to use the normal mechanism
+     * (i.e. TIFFSetField).
+     */
+    tif->tif_flags &= ~TIFF_DIRTYDIRECT;
 
-	return (1);
+    /*
+     * As per http://bugzilla.remotesensing.org/show_bug.cgi?id=19
+     * we clear the ISTILED flag when setting up a new directory.
+     * Should we also be clearing stuff like INSUBIFD?
+     */
+    tif->tif_flags &= ~TIFF_ISTILED;
+
+    return (1);
 }
 
-static int
-TIFFAdvanceDirectory(TIFF* tif, uint64* nextdir, uint64* off)
+static int TIFFAdvanceDirectory(TIFF *tif, uint64_t *nextdiroff, uint64_t *off,
+                                tdir_t *nextdirnum)
 {
-	static const char module[] = "TIFFAdvanceDirectory";
-	if (isMapped(tif))
-	{
-		uint64 poff=*nextdir;
-		if (!(tif->tif_flags&TIFF_BIGTIFF))
-		{
-			tmsize_t poffa,poffb,poffc,poffd;
-			uint16 dircount;
-			uint32 nextdir32;
-			poffa=(tmsize_t)poff;
-			poffb=poffa+sizeof(uint16);
-			if (((uint64)poffa!=poff)||(poffb<poffa)||(poffb<(tmsize_t)sizeof(uint16))||(poffb>tif->tif_size))
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Error fetching directory count");
-                                  *nextdir=0;
-				return(0);
-			}
-			_TIFFmemcpy(&dircount,tif->tif_base+poffa,sizeof(uint16));
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabShort(&dircount);
-			poffc=poffb+dircount*12;
-			poffd=poffc+sizeof(uint32);
-			if ((poffc<poffb)||(poffc<dircount*12)||(poffd<poffc)||(poffd<(tmsize_t)sizeof(uint32))||(poffd>tif->tif_size))
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Error fetching directory link");
-				return(0);
-			}
-			if (off!=NULL)
-				*off=(uint64)poffc;
-			_TIFFmemcpy(&nextdir32,tif->tif_base+poffc,sizeof(uint32));
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabLong(&nextdir32);
-			*nextdir=nextdir32;
-		}
-		else
-		{
-			tmsize_t poffa,poffb,poffc,poffd;
-			uint64 dircount64;
-			uint16 dircount16;
-			poffa=(tmsize_t)poff;
-			poffb=poffa+sizeof(uint64);
-			if (((uint64)poffa!=poff)||(poffb<poffa)||(poffb<(tmsize_t)sizeof(uint64))||(poffb>tif->tif_size))
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Error fetching directory count");
-				return(0);
-			}
-			_TIFFmemcpy(&dircount64,tif->tif_base+poffa,sizeof(uint64));
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabLong8(&dircount64);
-			if (dircount64>0xFFFF)
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Sanity check on directory count failed");
-				return(0);
-			}
-			dircount16=(uint16)dircount64;
-			poffc=poffb+dircount16*20;
-			poffd=poffc+sizeof(uint64);
-			if ((poffc<poffb)||(poffc<dircount16*20)||(poffd<poffc)||(poffd<(tmsize_t)sizeof(uint64))||(poffd>tif->tif_size))
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Error fetching directory link");
-				return(0);
-			}
-			if (off!=NULL)
-				*off=(uint64)poffc;
-			_TIFFmemcpy(nextdir,tif->tif_base+poffc,sizeof(uint64));
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabLong8(nextdir);
-		}
-		return(1);
-	}
-	else
-	{
-		if (!(tif->tif_flags&TIFF_BIGTIFF))
-		{
-			uint16 dircount;
-			uint32 nextdir32;
-			if (!SeekOK(tif, *nextdir) ||
-			    !ReadOK(tif, &dircount, sizeof (uint16))) {
-				TIFFErrorExt(tif->tif_clientdata, module, "%s: Error fetching directory count",
-				    tif->tif_name);
-				return (0);
-			}
-			if (tif->tif_flags & TIFF_SWAB)
-				TIFFSwabShort(&dircount);
-			if (off != NULL)
-				*off = TIFFSeekFile(tif,
-				    dircount*12, SEEK_CUR);
-			else
-				(void) TIFFSeekFile(tif,
-				    dircount*12, SEEK_CUR);
-			if (!ReadOK(tif, &nextdir32, sizeof (uint32))) {
-				TIFFErrorExt(tif->tif_clientdata, module, "%s: Error fetching directory link",
-				    tif->tif_name);
-				return (0);
-			}
-			if (tif->tif_flags & TIFF_SWAB)
-				TIFFSwabLong(&nextdir32);
-			*nextdir=nextdir32;
-		}
-		else
-		{
-			uint64 dircount64;
-			uint16 dircount16;
-			if (!SeekOK(tif, *nextdir) ||
-			    !ReadOK(tif, &dircount64, sizeof (uint64))) {
-				TIFFErrorExt(tif->tif_clientdata, module, "%s: Error fetching directory count",
-				    tif->tif_name);
-				return (0);
-			}
-			if (tif->tif_flags & TIFF_SWAB)
-				TIFFSwabLong8(&dircount64);
-			if (dircount64>0xFFFF)
-			{
-				TIFFErrorExt(tif->tif_clientdata, module, "Error fetching directory count");
-				return(0);
-			}
-			dircount16 = (uint16)dircount64;
-			if (off != NULL)
-				*off = TIFFSeekFile(tif,
-				    dircount16*20, SEEK_CUR);
-			else
-				(void) TIFFSeekFile(tif,
-				    dircount16*20, SEEK_CUR);
-			if (!ReadOK(tif, nextdir, sizeof (uint64))) {
-				TIFFErrorExt(tif->tif_clientdata, module,
-                                             "%s: Error fetching directory link",
-				    tif->tif_name);
-				return (0);
-			}
-			if (tif->tif_flags & TIFF_SWAB)
-				TIFFSwabLong8(nextdir);
-		}
-		return (1);
-	}
+    static const char module[] = "TIFFAdvanceDirectory";
+
+    /* Add this directory to the directory list, if not already in. */
+    if (!_TIFFCheckDirNumberAndOffset(tif, *nextdirnum, *nextdiroff))
+    {
+        TIFFErrorExtR(tif, module,
+                      "Starting directory %u at offset 0x%" PRIx64 " (%" PRIu64
+                      ") might cause an IFD loop",
+                      *nextdirnum, *nextdiroff, *nextdiroff);
+        *nextdiroff = 0;
+        *nextdirnum = 0;
+        return (0);
+    }
+
+    if (isMapped(tif))
+    {
+        uint64_t poff = *nextdiroff;
+        if (!(tif->tif_flags & TIFF_BIGTIFF))
+        {
+            tmsize_t poffa, poffb, poffc, poffd;
+            uint16_t dircount;
+            uint32_t nextdir32;
+            poffa = (tmsize_t)poff;
+            poffb = poffa + sizeof(uint16_t);
+            if (((uint64_t)poffa != poff) || (poffb < poffa) ||
+                (poffb < (tmsize_t)sizeof(uint16_t)) || (poffb > tif->tif_size))
+            {
+                TIFFErrorExtR(tif, module, "Error fetching directory count");
+                *nextdiroff = 0;
+                return (0);
+            }
+            _TIFFmemcpy(&dircount, tif->tif_base + poffa, sizeof(uint16_t));
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabShort(&dircount);
+            poffc = poffb + dircount * 12;
+            poffd = poffc + sizeof(uint32_t);
+            if ((poffc < poffb) || (poffc < dircount * 12) || (poffd < poffc) ||
+                (poffd < (tmsize_t)sizeof(uint32_t)) || (poffd > tif->tif_size))
+            {
+                TIFFErrorExtR(tif, module, "Error fetching directory link");
+                return (0);
+            }
+            if (off != NULL)
+                *off = (uint64_t)poffc;
+            _TIFFmemcpy(&nextdir32, tif->tif_base + poffc, sizeof(uint32_t));
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong(&nextdir32);
+            *nextdiroff = nextdir32;
+        }
+        else
+        {
+            tmsize_t poffa, poffb, poffc, poffd;
+            uint64_t dircount64;
+            uint16_t dircount16;
+            if (poff > (uint64_t)TIFF_TMSIZE_T_MAX - sizeof(uint64_t))
+            {
+                TIFFErrorExtR(tif, module, "Error fetching directory count");
+                return (0);
+            }
+            poffa = (tmsize_t)poff;
+            poffb = poffa + sizeof(uint64_t);
+            if (poffb > tif->tif_size)
+            {
+                TIFFErrorExtR(tif, module, "Error fetching directory count");
+                return (0);
+            }
+            _TIFFmemcpy(&dircount64, tif->tif_base + poffa, sizeof(uint64_t));
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong8(&dircount64);
+            if (dircount64 > 0xFFFF)
+            {
+                TIFFErrorExtR(tif, module,
+                              "Sanity check on directory count failed");
+                return (0);
+            }
+            dircount16 = (uint16_t)dircount64;
+            if (poffb > TIFF_TMSIZE_T_MAX - (tmsize_t)(dircount16 * 20) -
+                            (tmsize_t)sizeof(uint64_t))
+            {
+                TIFFErrorExtR(tif, module, "Error fetching directory link");
+                return (0);
+            }
+            poffc = poffb + dircount16 * 20;
+            poffd = poffc + sizeof(uint64_t);
+            if (poffd > tif->tif_size)
+            {
+                TIFFErrorExtR(tif, module, "Error fetching directory link");
+                return (0);
+            }
+            if (off != NULL)
+                *off = (uint64_t)poffc;
+            _TIFFmemcpy(nextdiroff, tif->tif_base + poffc, sizeof(uint64_t));
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong8(nextdiroff);
+        }
+    }
+    else
+    {
+        if (!(tif->tif_flags & TIFF_BIGTIFF))
+        {
+            uint16_t dircount;
+            uint32_t nextdir32;
+            if (!SeekOK(tif, *nextdiroff) ||
+                !ReadOK(tif, &dircount, sizeof(uint16_t)))
+            {
+                TIFFErrorExtR(tif, module, "%s: Error fetching directory count",
+                              tif->tif_name);
+                return (0);
+            }
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabShort(&dircount);
+            if (off != NULL)
+                *off = TIFFSeekFile(tif, dircount * 12, SEEK_CUR);
+            else
+                (void)TIFFSeekFile(tif, dircount * 12, SEEK_CUR);
+            if (!ReadOK(tif, &nextdir32, sizeof(uint32_t)))
+            {
+                TIFFErrorExtR(tif, module, "%s: Error fetching directory link",
+                              tif->tif_name);
+                return (0);
+            }
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong(&nextdir32);
+            *nextdiroff = nextdir32;
+        }
+        else
+        {
+            uint64_t dircount64;
+            uint16_t dircount16;
+            if (!SeekOK(tif, *nextdiroff) ||
+                !ReadOK(tif, &dircount64, sizeof(uint64_t)))
+            {
+                TIFFErrorExtR(tif, module, "%s: Error fetching directory count",
+                              tif->tif_name);
+                return (0);
+            }
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong8(&dircount64);
+            if (dircount64 > 0xFFFF)
+            {
+                TIFFErrorExtR(tif, module, "Error fetching directory count");
+                return (0);
+            }
+            dircount16 = (uint16_t)dircount64;
+            if (off != NULL)
+                *off = TIFFSeekFile(tif, dircount16 * 20, SEEK_CUR);
+            else
+                (void)TIFFSeekFile(tif, dircount16 * 20, SEEK_CUR);
+            if (!ReadOK(tif, nextdiroff, sizeof(uint64_t)))
+            {
+                TIFFErrorExtR(tif, module, "%s: Error fetching directory link",
+                              tif->tif_name);
+                return (0);
+            }
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong8(nextdiroff);
+        }
+    }
+    if (*nextdiroff != 0)
+    {
+        (*nextdirnum)++;
+        /* Check next directory for IFD looping and if so, set it as last
+         * directory. */
+        if (!_TIFFCheckDirNumberAndOffset(tif, *nextdirnum, *nextdiroff))
+        {
+            TIFFWarningExtR(
+                tif, module,
+                "the next directory %u at offset 0x%" PRIx64 " (%" PRIu64
+                ") might be an IFD loop. Treating directory %d as "
+                "last directory",
+                *nextdirnum, *nextdiroff, *nextdiroff, (int)(*nextdirnum) - 1);
+            *nextdiroff = 0;
+            (*nextdirnum)--;
+        }
+    }
+    return (1);
 }
 
 /*
  * Count the number of directories in a file.
  */
-uint16
-TIFFNumberOfDirectories(TIFF* tif)
+tdir_t TIFFNumberOfDirectories(TIFF *tif)
 {
-	static const char module[] = "TIFFNumberOfDirectories";
-	uint64 nextdir;
-	uint16 n;
-	if (!(tif->tif_flags&TIFF_BIGTIFF))
-		nextdir = tif->tif_header.classic.tiff_diroff;
-	else
-		nextdir = tif->tif_header.big.tiff_diroff;
-	n = 0;
-	while (nextdir != 0 && TIFFAdvanceDirectory(tif, &nextdir, NULL))
-        {
-                if (n != 65535) {
-                        ++n;
-                }
-		else
-                {
-                        TIFFErrorExt(tif->tif_clientdata, module,
-                                     "Directory count exceeded 65535 limit,"
-                                     " giving up on counting.");
-                        return (65535);
-                }
-        }
-	return (n);
+    uint64_t nextdiroff;
+    tdir_t nextdirnum;
+    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;
+    n = 0;
+    while (nextdiroff != 0 &&
+           TIFFAdvanceDirectory(tif, &nextdiroff, NULL, &nextdirnum))
+    {
+        ++n;
+    }
+    return (n);
 }
 
 /*
  * Set the n-th directory as the current directory.
  * NB: Directories are numbered starting at 0.
  */
-int
-TIFFSetDirectory(TIFF* tif, uint16 dirn)
+int TIFFSetDirectory(TIFF *tif, tdir_t dirn)
 {
-	uint64 nextdir;
-	uint16 n;
+    uint64_t nextdiroff;
+    tdir_t nextdirnum = 0;
+    tdir_t n;
 
-	if (!(tif->tif_flags&TIFF_BIGTIFF))
-		nextdir = tif->tif_header.classic.tiff_diroff;
-	else
-		nextdir = tif->tif_header.big.tiff_diroff;
-	for (n = dirn; n > 0 && nextdir != 0; n--)
-		if (!TIFFAdvanceDirectory(tif, &nextdir, NULL))
-			return (0);
-	tif->tif_nextdiroff = nextdir;
-	/*
-	 * 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;
-	/*
-	 * Reset tif_dirnumber counter and start new list of seen directories.
-	 * We need this to prevent IFD loops.
-	 */
-	tif->tif_dirnumber = 0;
-	return (TIFFReadDirectory(tif));
+    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 */
+    }
+
+    /* 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));
 }
 
 /*
@@ -1646,140 +2109,200 @@
  * is used mainly to access directories linked with
  * the SubIFD tag (e.g. thumbnail images).
  */
-int
-TIFFSetSubDirectory(TIFF* tif, uint64 diroff)
+int TIFFSetSubDirectory(TIFF *tif, uint64_t diroff)
 {
-	tif->tif_nextdiroff = diroff;
-	/*
-	 * Reset tif_dirnumber counter and start new list of seen directories.
-	 * We need this to prevent IFD loops.
-	 */
-	tif->tif_dirnumber = 0;
-	return (TIFFReadDirectory(tif));
+    /* Match nextdiroff and curdir for consistent IFD-loop checking.
+     * Only with TIFFSetSubDirectory() the IFD list can be corrupted with
+     * invalid offsets within the main IFD tree. In the case of several subIFDs
+     * of a main image, there are two possibilities that are not even mutually
+     * exclusive. a.) The subIFD tag contains an array with all offsets of the
+     * subIFDs. b.) The SubIFDs are concatenated with their NextIFD parameters.
+     * (refer to
+     * https://www.awaresystems.be/imaging/tiff/specification/TIFFPM6.pdf.)
+     */
+    int retval;
+    uint32_t curdir = 0;
+    int8_t probablySubIFD = 0;
+    if (diroff == 0)
+    {
+        /* Special case to invalidate the tif_lastdiroff member. */
+        tif->tif_curdir = TIFF_NON_EXISTENT_DIR_NUMBER;
+    }
+    else
+    {
+        if (!_TIFFGetDirNumberFromOffset(tif, diroff, &curdir))
+        {
+            /* Non-existing offsets might point to a SubIFD or invalid IFD.*/
+            probablySubIFD = 1;
+        }
+        /* -1 because TIFFReadDirectory() will increment tif_curdir. */
+        tif->tif_curdir =
+            curdir == 0 ? TIFF_NON_EXISTENT_DIR_NUMBER : curdir - 1;
+    }
+
+    tif->tif_nextdiroff = diroff;
+    retval = TIFFReadDirectory(tif);
+    /* If failed, curdir was not incremented in TIFFReadDirectory(), so set it
+     * back, but leave it for diroff==0. */
+    if (!retval && diroff != 0)
+    {
+        if (tif->tif_curdir == TIFF_NON_EXISTENT_DIR_NUMBER)
+            tif->tif_curdir = 0;
+        else
+            tif->tif_curdir++;
+    }
+    if (retval && probablySubIFD)
+    {
+        /* Reset IFD list to start new one for SubIFD chain and also start
+         * SubIFD chain with tif_curdir=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);
 }
 
 /*
  * Return file offset of the current directory.
  */
-uint64
-TIFFCurrentDirOffset(TIFF* tif)
-{
-	return (tif->tif_diroff);
-}
+uint64_t TIFFCurrentDirOffset(TIFF *tif) { return (tif->tif_diroff); }
 
 /*
  * Return an indication of whether or not we are
  * at the last directory in the file.
  */
-int
-TIFFLastDirectory(TIFF* tif)
-{
-	return (tif->tif_nextdiroff == 0);
-}
+int TIFFLastDirectory(TIFF *tif) { return (tif->tif_nextdiroff == 0); }
 
 /*
  * Unlink the specified directory from the directory chain.
+ * Note: First directory starts with number dirn=1.
+ * This is different to TIFFSetDirectory() where the first directory starts with
+ * zero.
  */
-int
-TIFFUnlinkDirectory(TIFF* tif, uint16 dirn)
+int TIFFUnlinkDirectory(TIFF *tif, tdir_t dirn)
 {
-	static const char module[] = "TIFFUnlinkDirectory";
-	uint64 nextdir;
-	uint64 off;
-	uint16 n;
+    static const char module[] = "TIFFUnlinkDirectory";
+    uint64_t nextdir;
+    tdir_t nextdirnum;
+    uint64_t off;
+    tdir_t n;
 
-	if (tif->tif_mode == O_RDONLY) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-                             "Can not unlink directory in read-only file");
-		return (0);
-	}
-	/*
-	 * Go to the directory before the one we want
-	 * to unlink and nab the offset of the link
-	 * field we'll need to patch.
-	 */
-	if (!(tif->tif_flags&TIFF_BIGTIFF))
-	{
-		nextdir = tif->tif_header.classic.tiff_diroff;
-		off = 4;
-	}
-	else
-	{
-		nextdir = tif->tif_header.big.tiff_diroff;
-		off = 8;
-	}
-	for (n = dirn-1; n > 0; n--) {
-		if (nextdir == 0) {
-			TIFFErrorExt(tif->tif_clientdata, module, "Directory %d does not exist", dirn);
-			return (0);
-		}
-		if (!TIFFAdvanceDirectory(tif, &nextdir, &off))
-			return (0);
-	}
-	/*
-	 * Advance to the directory to be unlinked and fetch
-	 * the offset of the directory that follows.
-	 */
-	if (!TIFFAdvanceDirectory(tif, &nextdir, NULL))
-		return (0);
-	/*
-	 * Go back and patch the link field of the preceding
-	 * directory to point to the offset of the directory
-	 * that follows.
-	 */
-	(void) TIFFSeekFile(tif, off, SEEK_SET);
-	if (!(tif->tif_flags&TIFF_BIGTIFF))
-	{
-		uint32 nextdir32;
-		nextdir32=(uint32)nextdir;
-		assert((uint64)nextdir32==nextdir);
-		if (tif->tif_flags & TIFF_SWAB)
-			TIFFSwabLong(&nextdir32);
-		if (!WriteOK(tif, &nextdir32, sizeof (uint32))) {
-			TIFFErrorExt(tif->tif_clientdata, module, "Error writing directory link");
-			return (0);
-		}
-	}
-	else
-	{
-		if (tif->tif_flags & TIFF_SWAB)
-			TIFFSwabLong8(&nextdir);
-		if (!WriteOK(tif, &nextdir, sizeof (uint64))) {
-			TIFFErrorExt(tif->tif_clientdata, module, "Error writing directory link");
-			return (0);
-		}
-	}
-	/*
-	 * Leave directory state setup safely.  We don't have
-	 * facilities for doing inserting and removing directories,
-	 * so it's safest to just invalidate everything.  This
-	 * means that the caller can only append to the directory
-	 * chain.
-	 */
-	(*tif->tif_cleanup)(tif);
-	if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata) {
-		_TIFFfree(tif->tif_rawdata);
-		tif->tif_rawdata = NULL;
-		tif->tif_rawcc = 0;
-                tif->tif_rawdataoff = 0;
-                tif->tif_rawdataloaded = 0;
-	}
-	tif->tif_flags &= ~(TIFF_BEENWRITING|TIFF_BUFFERSETUP|TIFF_POSTENCODE|TIFF_BUF4WRITE);
-	TIFFFreeDirectory(tif);
-	TIFFDefaultDirectory(tif);
-	tif->tif_diroff = 0;			/* force link on next write */
-	tif->tif_nextdiroff = 0;		/* next write must be at end */
-	tif->tif_curoff = 0;
-	tif->tif_row = (uint32) -1;
-	tif->tif_curstrip = (uint32) -1;
-	return (1);
+    if (tif->tif_mode == O_RDONLY)
+    {
+        TIFFErrorExtR(tif, module,
+                      "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
+     * field we'll need to patch.
+     */
+    if (!(tif->tif_flags & TIFF_BIGTIFF))
+    {
+        nextdir = tif->tif_header.classic.tiff_diroff;
+        off = 4;
+    }
+    else
+    {
+        nextdir = tif->tif_header.big.tiff_diroff;
+        off = 8;
+    }
+    nextdirnum = 0; /* First directory is dirn=0 */
+
+    for (n = dirn - 1; n > 0; n--)
+    {
+        if (nextdir == 0)
+        {
+            TIFFErrorExtR(tif, module, "Directory %u does not exist", dirn);
+            return (0);
+        }
+        if (!TIFFAdvanceDirectory(tif, &nextdir, &off, &nextdirnum))
+            return (0);
+    }
+    /*
+     * Advance to the directory to be unlinked and fetch
+     * the offset of the directory that follows.
+     */
+    if (!TIFFAdvanceDirectory(tif, &nextdir, NULL, &nextdirnum))
+        return (0);
+    /*
+     * Go back and patch the link field of the preceding
+     * directory to point to the offset of the directory
+     * that follows.
+     */
+    (void)TIFFSeekFile(tif, off, SEEK_SET);
+    if (!(tif->tif_flags & TIFF_BIGTIFF))
+    {
+        uint32_t nextdir32;
+        nextdir32 = (uint32_t)nextdir;
+        assert((uint64_t)nextdir32 == nextdir);
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabLong(&nextdir32);
+        if (!WriteOK(tif, &nextdir32, sizeof(uint32_t)))
+        {
+            TIFFErrorExtR(tif, module, "Error writing directory link");
+            return (0);
+        }
+    }
+    else
+    {
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabLong8(&nextdir);
+        if (!WriteOK(tif, &nextdir, sizeof(uint64_t)))
+        {
+            TIFFErrorExtR(tif, module, "Error writing directory link");
+            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,
+     * so it's safest to just invalidate everything.  This
+     * means that the caller can only append to the directory
+     * chain.
+     */
+    (*tif->tif_cleanup)(tif);
+    if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata)
+    {
+        _TIFFfreeExt(tif, tif->tif_rawdata);
+        tif->tif_rawdata = NULL;
+        tif->tif_rawcc = 0;
+        tif->tif_rawdataoff = 0;
+        tif->tif_rawdataloaded = 0;
+    }
+    tif->tif_flags &= ~(TIFF_BEENWRITING | TIFF_BUFFERSETUP | TIFF_POSTENCODE |
+                        TIFF_BUF4WRITE);
+    TIFFFreeDirectory(tif);
+    TIFFDefaultDirectory(tif);
+    tif->tif_diroff = 0;     /* force link on next write */
+    tif->tif_nextdiroff = 0; /* next write must be at end */
+    tif->tif_lastdiroff = 0; /* will be updated on next link */
+    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);
 }
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_dir.h b/third_party/libtiff/tif_dir.h
index e7f0667..9eaf22f 100644
--- a/third_party/libtiff/tif_dir.h
+++ b/third_party/libtiff/tif_dir.h
@@ -2,28 +2,28 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
 #ifndef _TIFFDIR_
-#define	_TIFFDIR_
+#define _TIFFDIR_
 
 #include "tiff.h"
 #include "tiffio.h"
@@ -32,10 +32,11 @@
  * ``Library-private'' Directory-related Definitions.
  */
 
-typedef struct {
-	const TIFFField *info;
-	int             count;
-	void           *value;
+typedef struct
+{
+    const TIFFField *info;
+    int count;
+    void *value;
 } TIFFTagValue;
 
 /*
@@ -49,79 +50,91 @@
  * BigTIFF, then it is placed in the offset field to save space. If so,
  * it is left-justified in the offset field.
  */
-typedef struct {
-	uint16 tdir_tag;        /* see below */
-	uint16 tdir_type;       /* data type; see below */
-	uint64 tdir_count;      /* number of items; length in spec */
-	union {
-		uint16 toff_short;
-		uint32 toff_long;
-		uint64 toff_long8;
-	} tdir_offset;		/* either offset or the data itself if fits */
-	uint8  tdir_ignore;	/* flag status to ignore tag when parsing tags in tif_dirread.c */
+typedef struct
+{
+    uint16_t tdir_tag;   /* see below */
+    uint16_t tdir_type;  /* data type; see below */
+    uint64_t tdir_count; /* number of items; length in spec */
+    union
+    {
+        uint16_t toff_short;
+        uint32_t toff_long;
+        uint64_t toff_long8;
+    } tdir_offset;       /* either offset or the data itself if fits */
+    uint8_t tdir_ignore; /* flag status to ignore tag when parsing tags in
+                            tif_dirread.c */
 } TIFFDirEntry;
 
 /*
  * Internal format of a TIFF directory entry.
  */
-typedef struct {
-#define FIELD_SETLONGS 4
-	/* bit vector of fields that are set */
-	unsigned long td_fieldsset[FIELD_SETLONGS];
+typedef struct
+{
+#define FIELDSET_ITEMS 4
+    /* bit vector of fields that are set */
+    uint32_t td_fieldsset[FIELDSET_ITEMS];
 
-	uint32  td_imagewidth, td_imagelength, td_imagedepth;
-	uint32  td_tilewidth, td_tilelength, td_tiledepth;
-	uint32  td_subfiletype;
-	uint16  td_bitspersample;
-	uint16  td_sampleformat;
-	uint16  td_compression;
-	uint16  td_photometric;
-	uint16  td_threshholding;
-	uint16  td_fillorder;
-	uint16  td_orientation;
-	uint16  td_samplesperpixel;
-	uint32  td_rowsperstrip;
-	uint16  td_minsamplevalue, td_maxsamplevalue;
-	double* td_sminsamplevalue;
-	double* td_smaxsamplevalue;
-	float   td_xresolution, td_yresolution;
-	uint16  td_resolutionunit;
-	uint16  td_planarconfig;
-	float   td_xposition, td_yposition;
-	uint16  td_pagenumber[2];
-	uint16* td_colormap[3];
-	uint16  td_halftonehints[2];
-	uint16  td_extrasamples;
-	uint16* td_sampleinfo;
-	/* even though the name is misleading, td_stripsperimage is the number
-	 * of striles (=strips or tiles) per plane, and td_nstrips the total
-	 * number of striles */
-	uint32  td_stripsperimage;  
-	uint32  td_nstrips;              /* size of offset & bytecount arrays */
-	uint64* td_stripoffset_p;        /* should be accessed with TIFFGetStrileOffset */
-	uint64* td_stripbytecount_p;     /* should be accessed with TIFFGetStrileByteCount */
-        uint32  td_stripoffsetbyteallocsize; /* number of elements currently allocated for td_stripoffset/td_stripbytecount. Only used if TIFF_LAZYSTRILELOAD is set */
+    uint32_t td_imagewidth, td_imagelength, td_imagedepth;
+    uint32_t td_tilewidth, td_tilelength, td_tiledepth;
+    uint32_t td_subfiletype;
+    uint16_t td_bitspersample;
+    uint16_t td_sampleformat;
+    uint16_t td_compression;
+    uint16_t td_photometric;
+    uint16_t td_threshholding;
+    uint16_t td_fillorder;
+    uint16_t td_orientation;
+    uint16_t td_samplesperpixel;
+    uint32_t td_rowsperstrip;
+    uint16_t td_minsamplevalue, td_maxsamplevalue;
+    double *td_sminsamplevalue;
+    double *td_smaxsamplevalue;
+    float td_xresolution, td_yresolution;
+    uint16_t td_resolutionunit;
+    uint16_t td_planarconfig;
+    float td_xposition, td_yposition;
+    uint16_t td_pagenumber[2];
+    uint16_t *td_colormap[3];
+    uint16_t td_halftonehints[2];
+    uint16_t td_extrasamples;
+    uint16_t *td_sampleinfo;
+    /* even though the name is misleading, td_stripsperimage is the number
+     * of striles (=strips or tiles) per plane, and td_nstrips the total
+     * number of striles */
+    uint32_t td_stripsperimage;
+    uint32_t td_nstrips; /* size of offset & bytecount arrays */
+    uint64_t
+        *td_stripoffset_p; /* should be accessed with TIFFGetStrileOffset */
+    uint64_t *td_stripbytecount_p; /* should be accessed with
+                                      TIFFGetStrileByteCount */
+    uint32_t
+        td_stripoffsetbyteallocsize; /* number of elements currently allocated
+                                        for td_stripoffset/td_stripbytecount.
+                                        Only used if TIFF_LAZYSTRILELOAD is set
+                                      */
 #ifdef STRIPBYTECOUNTSORTED_UNUSED
-	int     td_stripbytecountsorted; /* is the bytecount array sorted ascending? */
+    int td_stripbytecountsorted; /* is the bytecount array sorted ascending? */
 #endif
-        TIFFDirEntry td_stripoffset_entry;    /* for deferred loading */
-        TIFFDirEntry td_stripbytecount_entry; /* for deferred loading */
-	uint16  td_nsubifd;
-	uint64* td_subifd;
-	/* YCbCr parameters */
-	uint16  td_ycbcrsubsampling[2];
-	uint16  td_ycbcrpositioning;
-	/* Colorimetry parameters */
-	uint16* td_transferfunction[3];
-	float*	td_refblackwhite;
-	/* CMYK parameters */
-	int     td_inknameslen;
-	char*   td_inknames;
+    TIFFDirEntry td_stripoffset_entry;    /* for deferred loading */
+    TIFFDirEntry td_stripbytecount_entry; /* for deferred loading */
+    uint16_t td_nsubifd;
+    uint64_t *td_subifd;
+    /* YCbCr parameters */
+    uint16_t td_ycbcrsubsampling[2];
+    uint16_t td_ycbcrpositioning;
+    /* Colorimetry parameters */
+    uint16_t *td_transferfunction[3];
+    float *td_refblackwhite;
+    /* CMYK parameters */
+    int td_inknameslen;
+    char *td_inknames;
+    uint16_t td_numberofinks; /* number of inks in InkNames string */
 
-	int     td_customValueCount;
-        TIFFTagValue *td_customValues;
+    int td_customValueCount;
+    TIFFTagValue *td_customValues;
 
-        unsigned char td_deferstrilearraywriting; /* see TIFFDeferStrileArrayWriting() */
+    unsigned char
+        td_deferstrilearraywriting; /* see TIFFDeferStrileArrayWriting() */
 } TIFFDirectory;
 
 /*
@@ -135,49 +148,49 @@
  * Note that a bit *is* allocated for ignored tags; this is understood by the
  * directory reading logic which uses this fact to avoid special-case handling
  */
-#define FIELD_IGNORE                   0
+#define FIELD_IGNORE 0
 
 /* multi-item fields */
-#define FIELD_IMAGEDIMENSIONS          1
-#define FIELD_TILEDIMENSIONS           2
-#define FIELD_RESOLUTION               3
-#define FIELD_POSITION                 4
+#define FIELD_IMAGEDIMENSIONS 1
+#define FIELD_TILEDIMENSIONS 2
+#define FIELD_RESOLUTION 3
+#define FIELD_POSITION 4
 
 /* single-item fields */
-#define FIELD_SUBFILETYPE              5
-#define FIELD_BITSPERSAMPLE            6
-#define FIELD_COMPRESSION              7
-#define FIELD_PHOTOMETRIC              8
-#define FIELD_THRESHHOLDING            9
-#define FIELD_FILLORDER                10
-#define FIELD_ORIENTATION              15
-#define FIELD_SAMPLESPERPIXEL          16
-#define FIELD_ROWSPERSTRIP             17
-#define FIELD_MINSAMPLEVALUE           18
-#define FIELD_MAXSAMPLEVALUE           19
-#define FIELD_PLANARCONFIG             20
-#define FIELD_RESOLUTIONUNIT           22
-#define FIELD_PAGENUMBER               23
-#define FIELD_STRIPBYTECOUNTS          24
-#define FIELD_STRIPOFFSETS             25
-#define FIELD_COLORMAP                 26
-#define FIELD_EXTRASAMPLES             31
-#define FIELD_SAMPLEFORMAT             32
-#define FIELD_SMINSAMPLEVALUE          33
-#define FIELD_SMAXSAMPLEVALUE          34
-#define FIELD_IMAGEDEPTH               35
-#define FIELD_TILEDEPTH                36
-#define FIELD_HALFTONEHINTS            37
-#define FIELD_YCBCRSUBSAMPLING         39
-#define FIELD_YCBCRPOSITIONING         40
-#define	FIELD_REFBLACKWHITE            41
-#define FIELD_TRANSFERFUNCTION         44
-#define FIELD_INKNAMES                 46
-#define FIELD_SUBIFD                   49
+#define FIELD_SUBFILETYPE 5
+#define FIELD_BITSPERSAMPLE 6
+#define FIELD_COMPRESSION 7
+#define FIELD_PHOTOMETRIC 8
+#define FIELD_THRESHHOLDING 9
+#define FIELD_FILLORDER 10
+#define FIELD_ORIENTATION 15
+#define FIELD_SAMPLESPERPIXEL 16
+#define FIELD_ROWSPERSTRIP 17
+#define FIELD_MINSAMPLEVALUE 18
+#define FIELD_MAXSAMPLEVALUE 19
+#define FIELD_PLANARCONFIG 20
+#define FIELD_RESOLUTIONUNIT 22
+#define FIELD_PAGENUMBER 23
+#define FIELD_STRIPBYTECOUNTS 24
+#define FIELD_STRIPOFFSETS 25
+#define FIELD_COLORMAP 26
+#define FIELD_EXTRASAMPLES 31
+#define FIELD_SAMPLEFORMAT 32
+#define FIELD_SMINSAMPLEVALUE 33
+#define FIELD_SMAXSAMPLEVALUE 34
+#define FIELD_IMAGEDEPTH 35
+#define FIELD_TILEDEPTH 36
+#define FIELD_HALFTONEHINTS 37
+#define FIELD_YCBCRSUBSAMPLING 39
+#define FIELD_YCBCRPOSITIONING 40
+#define FIELD_REFBLACKWHITE 41
+#define FIELD_TRANSFERFUNCTION 44
+#define FIELD_INKNAMES 46
+#define FIELD_SUBIFD 49
+#define FIELD_NUMBEROFINKS 50
 /*      FIELD_CUSTOM (see tiffio.h)    65 */
 /* end of support for well-known tags; codec-private tags follow */
-#define FIELD_CODEC                    66  /* base of codec-private tags */
-
+#define FIELD_CODEC 66 /* base of codec-private tags */
 
 /*
  * Pseudo-tags don't normally need field bits since they are not written to an
@@ -187,129 +200,141 @@
  * or ``unset'' then it can do using internal state flags without polluting
  * the field bit space defined for real tags.
  */
-#define FIELD_PSEUDO			0
+#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 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))
-#define TIFFClrFieldBit(tif, field)	(BITFIELDn(tif, field) &= ~BITn(field))
+#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))
+#define TIFFClrFieldBit(tif, field) (BITFIELDn(tif, field) &= ~BITn(field))
 
-#define FieldSet(fields, f)		(fields[(f)/32] & BITn(f))
-#define ResetFieldBit(fields, f)	(fields[(f)/32] &= ~BITn(f))
+#define FieldSet(fields, f) (fields[(f) / 32] & BITn(f))
+#define ResetFieldBit(fields, f) (fields[(f) / 32] &= ~BITn(f))
 
-typedef enum {
-	TIFF_SETGET_UNDEFINED = 0,
-	TIFF_SETGET_ASCII = 1,
-	TIFF_SETGET_UINT8 = 2,
-	TIFF_SETGET_SINT8 = 3,
-	TIFF_SETGET_UINT16 = 4,
-	TIFF_SETGET_SINT16 = 5,
-	TIFF_SETGET_UINT32 = 6,
-	TIFF_SETGET_SINT32 = 7,
-	TIFF_SETGET_UINT64 = 8,
-	TIFF_SETGET_SINT64 = 9,
-	TIFF_SETGET_FLOAT = 10,
-	TIFF_SETGET_DOUBLE = 11,
-	TIFF_SETGET_IFD8 = 12,
-	TIFF_SETGET_INT = 13,
-	TIFF_SETGET_UINT16_PAIR = 14,
-	TIFF_SETGET_C0_ASCII = 15,
-	TIFF_SETGET_C0_UINT8 = 16,
-	TIFF_SETGET_C0_SINT8 = 17,
-	TIFF_SETGET_C0_UINT16 = 18,
-	TIFF_SETGET_C0_SINT16 = 19,
-	TIFF_SETGET_C0_UINT32 = 20,
-	TIFF_SETGET_C0_SINT32 = 21,
-	TIFF_SETGET_C0_UINT64 = 22,
-	TIFF_SETGET_C0_SINT64 = 23,
-	TIFF_SETGET_C0_FLOAT = 24,
-	TIFF_SETGET_C0_DOUBLE = 25,
-	TIFF_SETGET_C0_IFD8 = 26,
-	TIFF_SETGET_C16_ASCII = 27,
-	TIFF_SETGET_C16_UINT8 = 28,
-	TIFF_SETGET_C16_SINT8 = 29,
-	TIFF_SETGET_C16_UINT16 = 30,
-	TIFF_SETGET_C16_SINT16 = 31,
-	TIFF_SETGET_C16_UINT32 = 32,
-	TIFF_SETGET_C16_SINT32 = 33,
-	TIFF_SETGET_C16_UINT64 = 34,
-	TIFF_SETGET_C16_SINT64 = 35,
-	TIFF_SETGET_C16_FLOAT = 36,
-	TIFF_SETGET_C16_DOUBLE = 37,
-	TIFF_SETGET_C16_IFD8 = 38,
-	TIFF_SETGET_C32_ASCII = 39,
-	TIFF_SETGET_C32_UINT8 = 40,
-	TIFF_SETGET_C32_SINT8 = 41,
-	TIFF_SETGET_C32_UINT16 = 42,
-	TIFF_SETGET_C32_SINT16 = 43,
-	TIFF_SETGET_C32_UINT32 = 44,
-	TIFF_SETGET_C32_SINT32 = 45,
-	TIFF_SETGET_C32_UINT64 = 46,
-	TIFF_SETGET_C32_SINT64 = 47,
-	TIFF_SETGET_C32_FLOAT = 48,
-	TIFF_SETGET_C32_DOUBLE = 49,
-	TIFF_SETGET_C32_IFD8 = 50,
-	TIFF_SETGET_OTHER = 51
+typedef enum
+{
+    TIFF_SETGET_UNDEFINED = 0,
+    TIFF_SETGET_ASCII = 1,
+    TIFF_SETGET_UINT8 = 2,
+    TIFF_SETGET_SINT8 = 3,
+    TIFF_SETGET_UINT16 = 4,
+    TIFF_SETGET_SINT16 = 5,
+    TIFF_SETGET_UINT32 = 6,
+    TIFF_SETGET_SINT32 = 7,
+    TIFF_SETGET_UINT64 = 8,
+    TIFF_SETGET_SINT64 = 9,
+    TIFF_SETGET_FLOAT = 10,
+    TIFF_SETGET_DOUBLE = 11,
+    TIFF_SETGET_IFD8 = 12,
+    TIFF_SETGET_INT = 13,
+    TIFF_SETGET_UINT16_PAIR = 14,
+    TIFF_SETGET_C0_ASCII = 15,
+    TIFF_SETGET_C0_UINT8 = 16,
+    TIFF_SETGET_C0_SINT8 = 17,
+    TIFF_SETGET_C0_UINT16 = 18,
+    TIFF_SETGET_C0_SINT16 = 19,
+    TIFF_SETGET_C0_UINT32 = 20,
+    TIFF_SETGET_C0_SINT32 = 21,
+    TIFF_SETGET_C0_UINT64 = 22,
+    TIFF_SETGET_C0_SINT64 = 23,
+    TIFF_SETGET_C0_FLOAT = 24,
+    TIFF_SETGET_C0_DOUBLE = 25,
+    TIFF_SETGET_C0_IFD8 = 26,
+    TIFF_SETGET_C16_ASCII = 27,
+    TIFF_SETGET_C16_UINT8 = 28,
+    TIFF_SETGET_C16_SINT8 = 29,
+    TIFF_SETGET_C16_UINT16 = 30,
+    TIFF_SETGET_C16_SINT16 = 31,
+    TIFF_SETGET_C16_UINT32 = 32,
+    TIFF_SETGET_C16_SINT32 = 33,
+    TIFF_SETGET_C16_UINT64 = 34,
+    TIFF_SETGET_C16_SINT64 = 35,
+    TIFF_SETGET_C16_FLOAT = 36,
+    TIFF_SETGET_C16_DOUBLE = 37,
+    TIFF_SETGET_C16_IFD8 = 38,
+    TIFF_SETGET_C32_ASCII = 39,
+    TIFF_SETGET_C32_UINT8 = 40,
+    TIFF_SETGET_C32_SINT8 = 41,
+    TIFF_SETGET_C32_UINT16 = 42,
+    TIFF_SETGET_C32_SINT16 = 43,
+    TIFF_SETGET_C32_UINT32 = 44,
+    TIFF_SETGET_C32_SINT32 = 45,
+    TIFF_SETGET_C32_UINT64 = 46,
+    TIFF_SETGET_C32_SINT64 = 47,
+    TIFF_SETGET_C32_FLOAT = 48,
+    TIFF_SETGET_C32_DOUBLE = 49,
+    TIFF_SETGET_C32_IFD8 = 50,
+    TIFF_SETGET_OTHER = 51
 } TIFFSetGetFieldType;
 
 #if defined(__cplusplus)
-extern "C" {
+extern "C"
+{
 #endif
 
-extern const TIFFFieldArray* _TIFFGetFields(void);
-extern const TIFFFieldArray* _TIFFGetExifFields(void);
-extern void _TIFFSetupFields(TIFF* tif, const TIFFFieldArray* infoarray);
-extern void _TIFFPrintFieldInfo(TIFF*, FILE*);
+    extern const TIFFFieldArray *_TIFFGetFields(void);
+    extern const TIFFFieldArray *_TIFFGetExifFields(void);
+    extern const TIFFFieldArray *_TIFFGetGpsFields(void);
+    extern void _TIFFSetupFields(TIFF *tif, const TIFFFieldArray *infoarray);
+    extern void _TIFFPrintFieldInfo(TIFF *, FILE *);
 
-extern int _TIFFFillStriles(TIFF*);        
+    extern int _TIFFFillStriles(TIFF *);
 
-typedef enum {
-	tfiatImage,
-	tfiatExif,
-	tfiatOther
-} TIFFFieldArrayType;
+    typedef enum
+    {
+        tfiatImage,
+        tfiatExif,
+        tfiatGps, /* EXIF-GPS fields array type */
+        tfiatOther
+    } TIFFFieldArrayType;
 
-struct _TIFFFieldArray {
-	TIFFFieldArrayType type;    /* array type, will be used to determine if IFD is image and such */
-	uint32 allocated_size;      /* 0 if array is constant, other if modified by future definition extension support */
-	uint32 count;               /* number of elements in fields array */
-	TIFFField* fields;          /* actual field info */
-};
+    struct _TIFFFieldArray
+    {
+        TIFFFieldArrayType type; /* array type, will be used to determine if IFD
+                                    is image and such */
+        uint32_t allocated_size; /* 0 if array is constant, other if modified by
+                                    future definition extension support */
+        uint32_t count;          /* number of elements in fields array */
+        TIFFField *fields;       /* actual field info */
+    };
 
-struct _TIFFField {
-	uint32 field_tag;                       /* field's tag */
-	short field_readcount;                  /* read count/TIFF_VARIABLE/TIFF_SPP */
-	short field_writecount;                 /* write count/TIFF_VARIABLE */
-	TIFFDataType field_type;                /* type of associated data */
-	uint32 reserved;                        /* reserved for future extension */
-	TIFFSetGetFieldType set_field_type;     /* type to be passed to TIFFSetField */
-	TIFFSetGetFieldType get_field_type;     /* type to be passed to TIFFGetField */
-	unsigned short field_bit;               /* bit in fieldsset bit vector */
-	unsigned char field_oktochange;         /* if true, can change while writing */
-	unsigned char field_passcount;          /* if true, pass dir count on set */
-	char* field_name;                       /* ASCII name */
-	TIFFFieldArray* field_subfields;        /* if field points to child ifds, child ifd field definition array */
-};
+    struct _TIFFField
+    {
+        uint32_t field_tag;      /* field's tag */
+        short field_readcount;   /* read count/TIFF_VARIABLE/TIFF_SPP */
+        short field_writecount;  /* write count/TIFF_VARIABLE */
+        TIFFDataType field_type; /* type of associated data */
+        uint32_t
+            field_anonymous; /* if true, this is a unknown / anonymous tag */
+        TIFFSetGetFieldType
+            set_field_type; /* type to be passed to TIFFSetField */
+        TIFFSetGetFieldType
+            get_field_type;              /* type to be passed to TIFFGetField */
+        unsigned short field_bit;        /* bit in fieldsset bit vector */
+        unsigned char field_oktochange;  /* if true, can change while writing */
+        unsigned char field_passcount;   /* if true, pass dir count on set */
+        char *field_name;                /* ASCII name */
+        TIFFFieldArray *field_subfields; /* if field points to child ifds, child
+                                            ifd field definition array */
+    };
 
-extern int _TIFFMergeFields(TIFF*, const TIFFField[], uint32);
-extern const TIFFField* _TIFFFindOrRegisterField(TIFF *, uint32, TIFFDataType);
-extern  TIFFField* _TIFFCreateAnonField(TIFF *, uint32, TIFFDataType);
-extern int _TIFFCheckFieldIsValidForCodec(TIFF *tif, ttag_t tag);
+    extern int _TIFFMergeFields(TIFF *, const TIFFField[], uint32_t);
+    extern const TIFFField *_TIFFFindOrRegisterField(TIFF *, uint32_t,
+                                                     TIFFDataType);
+    extern TIFFField *_TIFFCreateAnonField(TIFF *, uint32_t, TIFFDataType);
+    extern int _TIFFCheckFieldIsValidForCodec(TIFF *tif, ttag_t tag);
+    extern int _TIFFCheckDirNumberAndOffset(TIFF *tif, tdir_t dirn,
+                                            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)
 }
 #endif
 #endif /* _TIFFDIR_ */
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_dirinfo.c b/third_party/libtiff/tif_dirinfo.c
index e1f6b23..0e705e8 100644
--- a/third_party/libtiff/tif_dirinfo.c
+++ b/third_party/libtiff/tif_dirinfo.c
@@ -42,686 +42,954 @@
 
 /* const object should be initialized */
 #ifdef _MSC_VER
-#pragma warning( push )
-#pragma warning( disable : 4132 )
+#pragma warning(push)
+#pragma warning(disable : 4132)
 #endif
 static const TIFFFieldArray tiffFieldArray;
 static const TIFFFieldArray exifFieldArray;
+static const TIFFFieldArray gpsFieldArray;
 #ifdef _MSC_VER
-#pragma warning( pop )
+#pragma warning(pop)
 #endif
+/*--: Rational2Double: --
+ * The Rational2Double upgraded libtiff functionality allows the definition and
+ * achievement of true double-precision accuracy for TIFF tags of RATIONAL type
+ * and field_bit=FIELD_CUSTOM using the set_field_type = TIFF_SETGET_DOUBLE.
+ * Unfortunately, that changes the old implemented interface for TIFFGetField().
+ * In order to keep the old TIFFGetField() interface behavior those tags have to
+ * be redefined with set_field_type = TIFF_SETGET_FLOAT!
+ *
+ *  Rational custom arrays are already defined as _Cxx_FLOAT, thus can stay.
+ *
+ */
 
-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_SUBFILETYPE, 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_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 1, 0, "CellWidth", NULL },
-	{ TIFFTAG_CELLLENGTH, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 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_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_RESOLUTION, 1, 0, "XResolution", NULL },
-	{ TIFFTAG_YRESOLUTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, 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_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_POSITION, 1, 0, "XPosition", NULL },
-	{ TIFFTAG_YPOSITION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, 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_CUSTOM, 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, -1, -3, TIFF_BYTE, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "ClipPath", NULL },
-	{ TIFFTAG_XCLIPPATHUNITS, 1, 1, TIFF_SLONG, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "XClipPathUnits", NULL },
-	{ TIFFTAG_XCLIPPATHUNITS, 1, 1, TIFF_SBYTE, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "XClipPathUnits", NULL },
-	{ TIFFTAG_YCLIPPATHUNITS, 1, 1, TIFF_SLONG, 0, TIFF_SETGET_UNDEFINED, 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 },
-	/* 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 },
-	/* 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,	4, 4,	TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0,	0,	"CFAPattern" , 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_LONG, 0, TIFF_SETGET_C32_UINT32, 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, 0, 1, "Photoshop", NULL },
-	{ TIFFTAG_EXIFIFD, 1, 1, TIFF_IFD8, 0, TIFF_SETGET_IFD8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 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, 0, 0, "GPSIFDOffset", NULL },
-	{ 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_INTEROPERABILITYIFD, 1, 1, TIFF_IFD8, 0, TIFF_SETGET_UNDEFINED, 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_DOUBLE, 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_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "BaselineExposure", NULL },
-	{ TIFFTAG_BASELINENOISE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "BaselineNoise", NULL },
-	{ TIFFTAG_BASELINESHARPNESS, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, 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_DOUBLE, 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_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "ChromaBlurRadius", NULL },
-	{ TIFFTAG_ANTIALIASSTRENGTH, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "AntiAliasStrength", NULL },
-	{ TIFFTAG_SHADOWSCALE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, 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},
-	/* end DNG tags */
-	/* 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 },
-	/* end TIFF/FX tags */
-	/* begin pseudo tags */
+/* 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, 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},
+    /* 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_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, 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, 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, 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 */
 };
 
-static const TIFFField
-exifFields[] = {
-	{ EXIFTAG_EXPOSURETIME, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExposureTime", NULL },
-	{ EXIFTAG_FNUMBER, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, 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_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_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_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CompressedBitsPerPixel", NULL },
-	{ EXIFTAG_SHUTTERSPEEDVALUE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ShutterSpeedValue", NULL },
-	{ EXIFTAG_APERTUREVALUE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ApertureValue", NULL },
-	{ EXIFTAG_BRIGHTNESSVALUE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "BrightnessValue", NULL },
-	{ EXIFTAG_EXPOSUREBIASVALUE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExposureBiasValue", NULL },
-	{ EXIFTAG_MAXAPERTUREVALUE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "MaxApertureValue", NULL },
-	{ EXIFTAG_SUBJECTDISTANCE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, 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_DOUBLE, 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_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_DOUBLE, 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_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FocalPlaneXResolution", NULL },
-	{ EXIFTAG_FOCALPLANEYRESOLUTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, 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_DOUBLE, 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_DOUBLE, 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_DOUBLE, 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 }
-};
+/*
+ * 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},
+    /* 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
+ * 2019)
+ */
 
-static const TIFFFieldArray
-tiffFieldArray = { tfiatImage, 0, TIFFArrayCount(tiffFields), (TIFFField*) tiffFields };
-static const TIFFFieldArray
-exifFieldArray = { tfiatExif, 0, TIFFArrayCount(exifFields), (TIFFField*) exifFields };
+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.
+     */
+    {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};
+static const TIFFFieldArray exifFieldArray = {
+    tfiatExif, 0, TIFFArrayCount(exifFields), (TIFFField *)exifFields};
+static const TIFFFieldArray gpsFieldArray = {
+    tfiatGps, 0, TIFFArrayCount(gpsFields), (TIFFField *)gpsFields};
 
 /*
  *  We have our own local lfind() equivalent to avoid subtle differences
- *  in types passed to lfind() on different systems. 
+ *  in types passed to lfind() on different systems.
  */
 
-static void *
-td_lfind(const void *key, const void *base, size_t *nmemb, size_t size,
-         int(*compar)(const void *, const void *))
+static void *td_lfind(const void *key, const void *base, size_t *nmemb,
+                      size_t size, int (*compar)(const void *, const void *))
 {
     char *element, *end;
 
     end = (char *)base + *nmemb * size;
     for (element = (char *)base; element < end; element += size)
-        if (!compar(key, element))		/* key found */
+        if (!compar(key, element)) /* key found */
             return element;
 
     return NULL;
 }
 
-const TIFFFieldArray*
-_TIFFGetFields(void)
+const TIFFFieldArray *_TIFFGetFields(void) { return (&tiffFieldArray); }
+
+const TIFFFieldArray *_TIFFGetExifFields(void) { return (&exifFieldArray); }
+
+const TIFFFieldArray *_TIFFGetGpsFields(void) { return (&gpsFieldArray); }
+
+void _TIFFSetupFields(TIFF *tif, const TIFFFieldArray *fieldarray)
 {
-	return(&tiffFieldArray);
-}
+    if (tif->tif_fields && tif->tif_nfields > 0)
+    {
+        uint32_t i;
 
-const TIFFFieldArray*
-_TIFFGetExifFields(void)
-{
-	return(&exifFieldArray);
-}
-
-void
-_TIFFSetupFields(TIFF* tif, const TIFFFieldArray* fieldarray)
-{
-	if (tif->tif_fields && tif->tif_nfields > 0) {
-		uint32 i;
-
-		for (i = 0; i < tif->tif_nfields; i++) {
-			TIFFField *fld = tif->tif_fields[i];
-			if (fld->field_bit == FIELD_CUSTOM &&
-				strncmp("Tag ", fld->field_name, 4) == 0) {
-					_TIFFfree(fld->field_name);
-					_TIFFfree(fld);
-				}
-		}
-
-		_TIFFfree(tif->tif_fields);
-		tif->tif_fields = NULL;
-		tif->tif_nfields = 0;
-	}
-	if (!_TIFFMergeFields(tif, fieldarray->fields, fieldarray->count)) {
-		TIFFErrorExt(tif->tif_clientdata, "_TIFFSetupFields",
-			     "Setting up field info failed");
-	}
-}
-
-static int
-tagCompare(const void* a, const void* b)
-{
-	const TIFFField* ta = *(const TIFFField**) a;
-	const TIFFField* tb = *(const TIFFField**) b;
-	/* NB: be careful of return values for 16-bit platforms */
-	if (ta->field_tag != tb->field_tag)
-		return (int)ta->field_tag - (int)tb->field_tag;
-	else
-		return (ta->field_type == TIFF_ANY) ?
-			0 : ((int)tb->field_type - (int)ta->field_type);
-}
-
-static int
-tagNameCompare(const void* a, const void* b)
-{
-	const TIFFField* ta = *(const TIFFField**) a;
-	const TIFFField* tb = *(const TIFFField**) b;
-	int ret = strcmp(ta->field_name, tb->field_name);
-
-	if (ret)
-		return ret;
-	else
-		return (ta->field_type == TIFF_ANY) ?
-			0 : ((int)tb->field_type - (int)ta->field_type);
-}
-
-int
-_TIFFMergeFields(TIFF* tif, const TIFFField info[], uint32 n)
-{
-	static const char module[] = "_TIFFMergeFields";
-	static const char reason[] = "for fields array";
-	/* TIFFField** tp; */
-	uint32 i;
-
-        tif->tif_foundfield = NULL;
-
-	if (tif->tif_fields && tif->tif_nfields > 0) {
-		tif->tif_fields = (TIFFField**)
-			_TIFFCheckRealloc(tif, tif->tif_fields,
-					  (tif->tif_nfields + n),
-					  sizeof(TIFFField *), reason);
-	} else {
-		tif->tif_fields = (TIFFField **)
-			_TIFFCheckMalloc(tif, n, sizeof(TIFFField *),
-					 reason);
-	}
-	if (!tif->tif_fields) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-			     "Failed to allocate fields array");
-		return 0;
-	}
-
-	/* tp = tif->tif_fields + tif->tif_nfields; */
-	for (i = 0; i < n; i++) {
-		const TIFFField *fip =
-			TIFFFindField(tif, info[i].field_tag, TIFF_ANY);
-
-                /* only add definitions that aren't already present */
-		if (!fip) {
-                        tif->tif_fields[tif->tif_nfields] = (TIFFField *) (info+i);
-                        tif->tif_nfields++;
+        for (i = 0; i < tif->tif_nfields; i++)
+        {
+            TIFFField *fld = tif->tif_fields[i];
+            if (fld->field_name != NULL)
+            {
+                if (fld->field_bit == FIELD_CUSTOM && TIFFFieldIsAnonymous(fld))
+                {
+                    _TIFFfreeExt(tif, fld->field_name);
+                    /* caution: tif_fields[i] must not be the beginning of a
+                     * fields-array. Otherwise the following tags are also freed
+                     * with the first free().
+                     */
+                    _TIFFfreeExt(tif, fld);
                 }
-	}
+            }
+        }
 
-        /* Sort the field info by tag number */
-	qsort(tif->tif_fields, tif->tif_nfields,
-	      sizeof(TIFFField *), tagCompare);
-
-	return n;
+        _TIFFfreeExt(tif, tif->tif_fields);
+        tif->tif_fields = NULL;
+        tif->tif_nfields = 0;
+    }
+    if (!_TIFFMergeFields(tif, fieldarray->fields, fieldarray->count))
+    {
+        TIFFErrorExtR(tif, "_TIFFSetupFields", "Setting up field info failed");
+    }
 }
 
-void
-_TIFFPrintFieldInfo(TIFF* tif, FILE* fd)
+static int tagCompare(const void *a, const void *b)
 {
-	uint32 i;
+    const TIFFField *ta = *(const TIFFField **)a;
+    const TIFFField *tb = *(const TIFFField **)b;
+    /* NB: be careful of return values for 16-bit platforms */
+    if (ta->field_tag != tb->field_tag)
+        return (int)ta->field_tag - (int)tb->field_tag;
+    else
+        return (ta->field_type == TIFF_ANY)
+                   ? 0
+                   : ((int)tb->field_type - (int)ta->field_type);
+}
 
-	fprintf(fd, "%s: \n", tif->tif_name);
-	for (i = 0; i < tif->tif_nfields; i++) {
-		const TIFFField* fip = tif->tif_fields[i];
-		fprintf(fd, "field[%2d] %5lu, %2d, %2d, %d, %2d, %5s, %5s, %s\n"
-			, (int)i
-			, (unsigned long) fip->field_tag
-			, fip->field_readcount, fip->field_writecount
-			, fip->field_type
-			, fip->field_bit
-			, fip->field_oktochange ? "TRUE" : "FALSE"
-			, fip->field_passcount ? "TRUE" : "FALSE"
-			, fip->field_name
-		);
-	}
+static int tagNameCompare(const void *a, const void *b)
+{
+    const TIFFField *ta = *(const TIFFField **)a;
+    const TIFFField *tb = *(const TIFFField **)b;
+    int ret = strcmp(ta->field_name, tb->field_name);
+
+    if (ret)
+        return ret;
+    else
+        return (ta->field_type == TIFF_ANY)
+                   ? 0
+                   : ((int)tb->field_type - (int)ta->field_type);
+}
+
+int _TIFFMergeFields(TIFF *tif, const TIFFField info[], uint32_t n)
+{
+    static const char module[] = "_TIFFMergeFields";
+    static const char reason[] = "for fields array";
+    /* TIFFField** tp; */
+    uint32_t i;
+
+    tif->tif_foundfield = NULL;
+
+    if (tif->tif_fields && tif->tif_nfields > 0)
+    {
+        tif->tif_fields = (TIFFField **)_TIFFCheckRealloc(
+            tif, tif->tif_fields, (tif->tif_nfields + n), sizeof(TIFFField *),
+            reason);
+    }
+    else
+    {
+        tif->tif_fields =
+            (TIFFField **)_TIFFCheckMalloc(tif, n, sizeof(TIFFField *), reason);
+    }
+    if (!tif->tif_fields)
+    {
+        TIFFErrorExtR(tif, module, "Failed to allocate fields array");
+        return 0;
+    }
+
+    /* tp = tif->tif_fields + tif->tif_nfields; */
+    for (i = 0; i < n; i++)
+    {
+        const TIFFField *fip = TIFFFindField(tif, info[i].field_tag, TIFF_ANY);
+
+        /* only add definitions that aren't already present */
+        if (!fip)
+        {
+            tif->tif_fields[tif->tif_nfields] = (TIFFField *)(info + i);
+            tif->tif_nfields++;
+        }
+    }
+
+    /* Sort the field info by tag number */
+    qsort(tif->tif_fields, tif->tif_nfields, sizeof(TIFFField *), tagCompare);
+
+    return n;
+}
+
+void _TIFFPrintFieldInfo(TIFF *tif, FILE *fd)
+{
+    uint32_t i;
+
+    fprintf(fd, "%s: \n", tif->tif_name);
+    for (i = 0; i < tif->tif_nfields; i++)
+    {
+        const TIFFField *fip = tif->tif_fields[i];
+        fprintf(fd, "field[%2d] %5lu, %2d, %2d, %d, %2d, %5s, %5s, %s\n",
+                (int)i, (unsigned long)fip->field_tag, fip->field_readcount,
+                fip->field_writecount, fip->field_type, fip->field_bit,
+                fip->field_oktochange ? "TRUE" : "FALSE",
+                fip->field_passcount ? "TRUE" : "FALSE", fip->field_name);
+    }
 }
 
 /*
- * Return size of TIFFDataType in bytes
+ * Return size of TIFFDataType within TIFF-file in bytes
  */
-int
-TIFFDataWidth(TIFFDataType type)
+int TIFFDataWidth(TIFFDataType type)
 {
-	switch(type)
-	{
-		case 0:  /* nothing */
-		case TIFF_BYTE:
-		case TIFF_ASCII:
-		case TIFF_SBYTE:
-		case TIFF_UNDEFINED:
-			return 1;
-		case TIFF_SHORT:
-		case TIFF_SSHORT:
-			return 2;
-		case TIFF_LONG:
-		case TIFF_SLONG:
-		case TIFF_FLOAT:
-		case TIFF_IFD:
-			return 4;
-		case TIFF_RATIONAL:
-		case TIFF_SRATIONAL:
-		case TIFF_DOUBLE:
-		case TIFF_LONG8:
-		case TIFF_SLONG8:
-		case TIFF_IFD8:
-			return 8;
-		default:
-			return 0; /* will return 0 for unknown types */
-	}
+    switch (type)
+    {
+        case 0: /* nothing */
+        case TIFF_BYTE:
+        case TIFF_ASCII:
+        case TIFF_SBYTE:
+        case TIFF_UNDEFINED:
+            return 1;
+        case TIFF_SHORT:
+        case TIFF_SSHORT:
+            return 2;
+        case TIFF_LONG:
+        case TIFF_SLONG:
+        case TIFF_FLOAT:
+        case TIFF_IFD:
+            return 4;
+        case TIFF_RATIONAL:
+        case TIFF_SRATIONAL:
+        case TIFF_DOUBLE:
+        case TIFF_LONG8:
+        case TIFF_SLONG8:
+        case TIFF_IFD8:
+            return 8;
+        default:
+            return 0; /* will return 0 for unknown types */
+    }
 }
 
 /*
- * Return size of TIFFDataType in bytes.
- *
- * XXX: We need a separate function to determine the space needed
- * to store the value. For TIFF_RATIONAL values TIFFDataWidth() returns 8,
- * but we use 4-byte float to represent rationals.
+ * Return internal storage size of TIFFSetGetFieldType in bytes.
+ * TIFFSetField() and TIFFGetField() have to provide the parameter accordingly.
+ * Replaces internal functions _TIFFDataSize() and _TIFFSetGetFieldSize()
+ * with now extern available function TIFFFieldSetGetSize().
  */
-int
-_TIFFDataSize(TIFFDataType type)
+int TIFFFieldSetGetSize(const TIFFField *fip)
 {
-	switch (type)
-	{
-		case TIFF_BYTE:
-		case TIFF_SBYTE:
-		case TIFF_ASCII:
-		case TIFF_UNDEFINED:
-		    return 1;
-		case TIFF_SHORT:
-		case TIFF_SSHORT:
-		    return 2;
-		case TIFF_LONG:
-		case TIFF_SLONG:
-		case TIFF_FLOAT:
-		case TIFF_IFD:
-		case TIFF_RATIONAL:
-		case TIFF_SRATIONAL:
-		    return 4;
-		case TIFF_DOUBLE:
-		case TIFF_LONG8:
-		case TIFF_SLONG8:
-		case TIFF_IFD8:
-		    return 8;
-		default:
-		    return 0;
-	}
+    /*
+     * TIFFSetField() and TIFFGetField() must provide the parameter accordingly
+     * to the definition of "set_field_type" of the tag definition in
+     * dir_info.c. This function returns the data size for that purpose.
+     *
+     * Furthermore, this data size is also used for the internal storage,
+     * even for TIFF_RATIONAL values for FIELD_CUSTOM, which are stored
+     * internally as 4-byte float, but some of them should be stored internally
+     * as 8-byte double, depending on the "set_field_type" _FLOAT_ or _DOUBLE_.
+     */
+    if (fip == NULL)
+        return 0;
+
+    switch (fip->set_field_type)
+    {
+        case TIFF_SETGET_UNDEFINED:
+        case TIFF_SETGET_ASCII:
+        case TIFF_SETGET_C0_ASCII:
+        case TIFF_SETGET_C16_ASCII:
+        case TIFF_SETGET_C32_ASCII:
+        case TIFF_SETGET_OTHER:
+            return 1;
+        case TIFF_SETGET_UINT8:
+        case TIFF_SETGET_SINT8:
+        case TIFF_SETGET_C0_UINT8:
+        case TIFF_SETGET_C0_SINT8:
+        case TIFF_SETGET_C16_UINT8:
+        case TIFF_SETGET_C16_SINT8:
+        case TIFF_SETGET_C32_UINT8:
+        case TIFF_SETGET_C32_SINT8:
+            return 1;
+        case TIFF_SETGET_UINT16:
+        case TIFF_SETGET_SINT16:
+        case TIFF_SETGET_C0_UINT16:
+        case TIFF_SETGET_C0_SINT16:
+        case TIFF_SETGET_C16_UINT16:
+        case TIFF_SETGET_C16_SINT16:
+        case TIFF_SETGET_C32_UINT16:
+        case TIFF_SETGET_C32_SINT16:
+            return 2;
+        case TIFF_SETGET_INT:
+        case TIFF_SETGET_UINT32:
+        case TIFF_SETGET_SINT32:
+        case TIFF_SETGET_FLOAT:
+        case TIFF_SETGET_UINT16_PAIR:
+        case TIFF_SETGET_C0_UINT32:
+        case TIFF_SETGET_C0_SINT32:
+        case TIFF_SETGET_C0_FLOAT:
+        case TIFF_SETGET_C16_UINT32:
+        case TIFF_SETGET_C16_SINT32:
+        case TIFF_SETGET_C16_FLOAT:
+        case TIFF_SETGET_C32_UINT32:
+        case TIFF_SETGET_C32_SINT32:
+        case TIFF_SETGET_C32_FLOAT:
+            return 4;
+        case TIFF_SETGET_UINT64:
+        case TIFF_SETGET_SINT64:
+        case TIFF_SETGET_DOUBLE:
+        case TIFF_SETGET_IFD8:
+        case TIFF_SETGET_C0_UINT64:
+        case TIFF_SETGET_C0_SINT64:
+        case TIFF_SETGET_C0_DOUBLE:
+        case TIFF_SETGET_C0_IFD8:
+        case TIFF_SETGET_C16_UINT64:
+        case TIFF_SETGET_C16_SINT64:
+        case TIFF_SETGET_C16_DOUBLE:
+        case TIFF_SETGET_C16_IFD8:
+        case TIFF_SETGET_C32_UINT64:
+        case TIFF_SETGET_C32_SINT64:
+        case TIFF_SETGET_C32_DOUBLE:
+        case TIFF_SETGET_C32_IFD8:
+            return 8;
+        default:
+            return 0;
+    }
+} /*-- TIFFFieldSetGetSize() --- */
+
+/*
+ * Return size of count parameter of TIFFSetField() and TIFFGetField()
+ * and also if it is required:  0=none, 2=uint16_t, 4=uint32_t
+ */
+int TIFFFieldSetGetCountSize(const TIFFField *fip)
+{
+    if (fip == NULL)
+        return 0;
+
+    switch (fip->set_field_type)
+    {
+        case TIFF_SETGET_C16_ASCII:
+        case TIFF_SETGET_C16_UINT8:
+        case TIFF_SETGET_C16_SINT8:
+        case TIFF_SETGET_C16_UINT16:
+        case TIFF_SETGET_C16_SINT16:
+        case TIFF_SETGET_C16_UINT32:
+        case TIFF_SETGET_C16_SINT32:
+        case TIFF_SETGET_C16_FLOAT:
+        case TIFF_SETGET_C16_UINT64:
+        case TIFF_SETGET_C16_SINT64:
+        case TIFF_SETGET_C16_DOUBLE:
+        case TIFF_SETGET_C16_IFD8:
+            return 2;
+        case TIFF_SETGET_C32_ASCII:
+        case TIFF_SETGET_C32_UINT8:
+        case TIFF_SETGET_C32_SINT8:
+        case TIFF_SETGET_C32_UINT16:
+        case TIFF_SETGET_C32_SINT16:
+        case TIFF_SETGET_C32_UINT32:
+        case TIFF_SETGET_C32_SINT32:
+        case TIFF_SETGET_C32_FLOAT:
+        case TIFF_SETGET_C32_UINT64:
+        case TIFF_SETGET_C32_SINT64:
+        case TIFF_SETGET_C32_DOUBLE:
+        case TIFF_SETGET_C32_IFD8:
+            return 4;
+        default:
+            return 0;
+    }
+} /*-- TIFFFieldSetGetCountSize() --- */
+
+const TIFFField *TIFFFindField(TIFF *tif, uint32_t tag, TIFFDataType dt)
+{
+    TIFFField key = {0, 0, 0, TIFF_NOTYPE, 0, 0, 0, 0, 0, 0, NULL, NULL};
+    TIFFField *pkey = &key;
+    const TIFFField **ret;
+    if (tif->tif_foundfield && tif->tif_foundfield->field_tag == tag &&
+        (dt == TIFF_ANY || dt == tif->tif_foundfield->field_type))
+        return tif->tif_foundfield;
+
+    /* If we are invoked with no field information, then just return. */
+    if (!tif->tif_fields)
+        return NULL;
+
+    /* NB: use sorted search (e.g. binary search) */
+
+    key.field_tag = tag;
+    key.field_type = dt;
+
+    ret = (const TIFFField **)bsearch(&pkey, tif->tif_fields, tif->tif_nfields,
+                                      sizeof(TIFFField *), tagCompare);
+    return tif->tif_foundfield = (ret ? *ret : NULL);
 }
 
-const TIFFField*
-TIFFFindField(TIFF* tif, uint32 tag, TIFFDataType dt)
+static const TIFFField *_TIFFFindFieldByName(TIFF *tif, const char *field_name,
+                                             TIFFDataType dt)
 {
-	TIFFField key = {0, 0, 0, TIFF_NOTYPE, 0, 0, 0, 0, 0, 0, NULL, NULL};
-	TIFFField* pkey = &key;
-	const TIFFField **ret;
-	if (tif->tif_foundfield && tif->tif_foundfield->field_tag == tag &&
-	    (dt == TIFF_ANY || dt == tif->tif_foundfield->field_type))
-		return tif->tif_foundfield;
+    TIFFField key = {0, 0, 0, TIFF_NOTYPE, 0, 0, 0, 0, 0, 0, NULL, NULL};
+    TIFFField *pkey = &key;
+    const TIFFField **ret;
+    if (tif->tif_foundfield &&
+        streq(tif->tif_foundfield->field_name, field_name) &&
+        (dt == TIFF_ANY || dt == tif->tif_foundfield->field_type))
+        return (tif->tif_foundfield);
 
-	/* If we are invoked with no field information, then just return. */
-	if (!tif->tif_fields)
-		return NULL;
+    /* If we are invoked with no field information, then just return. */
+    if (!tif->tif_fields)
+        return NULL;
 
-	/* NB: use sorted search (e.g. binary search) */
+    /* NB: use linear search since list is sorted by key#, not name */
 
-	key.field_tag = tag;
-	key.field_type = dt;
+    key.field_name = (char *)field_name;
+    key.field_type = dt;
 
-	ret = (const TIFFField **) bsearch(&pkey, tif->tif_fields,
-					   tif->tif_nfields,
-					   sizeof(TIFFField *), tagCompare);
-	return tif->tif_foundfield = (ret ? *ret : NULL);
+    ret =
+        (const TIFFField **)td_lfind(&pkey, tif->tif_fields, &tif->tif_nfields,
+                                     sizeof(TIFFField *), tagNameCompare);
+
+    return tif->tif_foundfield = (ret ? *ret : NULL);
 }
 
-static const TIFFField*
-_TIFFFindFieldByName(TIFF* tif, const char *field_name, TIFFDataType dt)
+const TIFFField *TIFFFieldWithTag(TIFF *tif, uint32_t tag)
 {
-	TIFFField key = {0, 0, 0, TIFF_NOTYPE, 0, 0, 0, 0, 0, 0, NULL, NULL};
-	TIFFField* pkey = &key;
-	const TIFFField **ret;
-	if (tif->tif_foundfield
-	    && streq(tif->tif_foundfield->field_name, field_name)
-	    && (dt == TIFF_ANY || dt == tif->tif_foundfield->field_type))
-		return (tif->tif_foundfield);
-
-	/* If we are invoked with no field information, then just return. */
-	if (!tif->tif_fields)
-		return NULL;
-
-	/* NB: use linear search since list is sorted by key#, not name */
-
-	key.field_name = (char *)field_name;
-	key.field_type = dt;
-
-	ret = (const TIFFField **) 
-            td_lfind(&pkey, tif->tif_fields, &tif->tif_nfields,
-                     sizeof(TIFFField *), tagNameCompare);
-
-	return tif->tif_foundfield = (ret ? *ret : NULL);
+    const TIFFField *fip = TIFFFindField(tif, tag, TIFF_ANY);
+    if (!fip)
+    {
+        TIFFWarningExtR(tif, "TIFFFieldWithTag", "Warning, unknown tag 0x%x",
+                        (unsigned int)tag);
+    }
+    return (fip);
 }
 
-const TIFFField*
-TIFFFieldWithTag(TIFF* tif, uint32 tag)
+const TIFFField *TIFFFieldWithName(TIFF *tif, const char *field_name)
 {
-	const TIFFField* fip = TIFFFindField(tif, tag, TIFF_ANY);
-	if (!fip) {
-		TIFFErrorExt(tif->tif_clientdata, "TIFFFieldWithTag",
-			     "Internal error, unknown tag 0x%x",
-			     (unsigned int) tag);
-	}
-	return (fip);
+    const TIFFField *fip = _TIFFFindFieldByName(tif, field_name, TIFF_ANY);
+    if (!fip)
+    {
+        TIFFWarningExtR(tif, "TIFFFieldWithName", "Warning, unknown tag %s",
+                        field_name);
+    }
+    return (fip);
 }
 
-const TIFFField*
-TIFFFieldWithName(TIFF* tif, const char *field_name)
+uint32_t TIFFFieldTag(const TIFFField *fip) { return fip->field_tag; }
+
+const char *TIFFFieldName(const TIFFField *fip) { return fip->field_name; }
+
+TIFFDataType TIFFFieldDataType(const TIFFField *fip) { return fip->field_type; }
+
+int TIFFFieldPassCount(const TIFFField *fip) { return fip->field_passcount; }
+
+int TIFFFieldReadCount(const TIFFField *fip) { return fip->field_readcount; }
+
+int TIFFFieldWriteCount(const TIFFField *fip) { return fip->field_writecount; }
+
+int TIFFFieldIsAnonymous(const TIFFField *fip) { return fip->field_anonymous; }
+
+const TIFFField *_TIFFFindOrRegisterField(TIFF *tif, uint32_t tag,
+                                          TIFFDataType dt)
+
 {
-	const TIFFField* fip =
-		_TIFFFindFieldByName(tif, field_name, TIFF_ANY);
-	if (!fip) {
-		TIFFErrorExt(tif->tif_clientdata, "TIFFFieldWithName",
-			     "Internal error, unknown tag %s", field_name);
-	}
-	return (fip);
+    const TIFFField *fld;
+
+    fld = TIFFFindField(tif, tag, dt);
+    if (fld == NULL)
+    {
+        fld = _TIFFCreateAnonField(tif, tag, dt);
+        if (!_TIFFMergeFields(tif, fld, 1))
+            return NULL;
+    }
+
+    return fld;
 }
 
-uint32
-TIFFFieldTag(const TIFFField* fip)
+TIFFField *_TIFFCreateAnonField(TIFF *tif, uint32_t tag,
+                                TIFFDataType field_type)
 {
-	return fip->field_tag;
-}
+    TIFFField *fld;
+    (void)tif;
 
-const char *
-TIFFFieldName(const TIFFField* fip)
-{
-	return fip->field_name;
-}
+    fld = (TIFFField *)_TIFFmallocExt(tif, sizeof(TIFFField));
+    if (fld == NULL)
+        return NULL;
+    _TIFFmemset(fld, 0, sizeof(TIFFField));
 
-TIFFDataType
-TIFFFieldDataType(const TIFFField* fip)
-{
-	return fip->field_type;
-}
+    fld->field_tag = tag;
+    fld->field_readcount = TIFF_VARIABLE2;
+    fld->field_writecount = TIFF_VARIABLE2;
+    fld->field_type = field_type;
+    fld->field_anonymous =
+        1; /* indicate that this is an anonymous / unknown tag */
+    switch (field_type)
+    {
+        case TIFF_BYTE:
+        case TIFF_UNDEFINED:
+            fld->set_field_type = TIFF_SETGET_C32_UINT8;
+            fld->get_field_type = TIFF_SETGET_C32_UINT8;
+            break;
+        case TIFF_ASCII:
+            fld->set_field_type = TIFF_SETGET_C32_ASCII;
+            fld->get_field_type = TIFF_SETGET_C32_ASCII;
+            break;
+        case TIFF_SHORT:
+            fld->set_field_type = TIFF_SETGET_C32_UINT16;
+            fld->get_field_type = TIFF_SETGET_C32_UINT16;
+            break;
+        case TIFF_LONG:
+            fld->set_field_type = TIFF_SETGET_C32_UINT32;
+            fld->get_field_type = TIFF_SETGET_C32_UINT32;
+            break;
+        case TIFF_RATIONAL:
+        case TIFF_SRATIONAL:
+        case TIFF_FLOAT:
+            fld->set_field_type = TIFF_SETGET_C32_FLOAT;
+            fld->get_field_type = TIFF_SETGET_C32_FLOAT;
+            break;
+        case TIFF_SBYTE:
+            fld->set_field_type = TIFF_SETGET_C32_SINT8;
+            fld->get_field_type = TIFF_SETGET_C32_SINT8;
+            break;
+        case TIFF_SSHORT:
+            fld->set_field_type = TIFF_SETGET_C32_SINT16;
+            fld->get_field_type = TIFF_SETGET_C32_SINT16;
+            break;
+        case TIFF_SLONG:
+            fld->set_field_type = TIFF_SETGET_C32_SINT32;
+            fld->get_field_type = TIFF_SETGET_C32_SINT32;
+            break;
+        case TIFF_DOUBLE:
+            fld->set_field_type = TIFF_SETGET_C32_DOUBLE;
+            fld->get_field_type = TIFF_SETGET_C32_DOUBLE;
+            break;
+        case TIFF_IFD:
+        case TIFF_IFD8:
+            fld->set_field_type = TIFF_SETGET_C32_IFD8;
+            fld->get_field_type = TIFF_SETGET_C32_IFD8;
+            break;
+        case TIFF_LONG8:
+            fld->set_field_type = TIFF_SETGET_C32_UINT64;
+            fld->get_field_type = TIFF_SETGET_C32_UINT64;
+            break;
+        case TIFF_SLONG8:
+            fld->set_field_type = TIFF_SETGET_C32_SINT64;
+            fld->get_field_type = TIFF_SETGET_C32_SINT64;
+            break;
+        default:
+            fld->set_field_type = TIFF_SETGET_UNDEFINED;
+            fld->get_field_type = TIFF_SETGET_UNDEFINED;
+            break;
+    }
+    fld->field_bit = FIELD_CUSTOM;
+    fld->field_oktochange = TRUE;
+    fld->field_passcount = TRUE;
+    fld->field_name = (char *)_TIFFmallocExt(tif, 32);
+    if (fld->field_name == NULL)
+    {
+        _TIFFfreeExt(tif, fld);
+        return NULL;
+    }
+    fld->field_subfields = NULL;
 
-int
-TIFFFieldPassCount(const TIFFField* fip)
-{
-	return fip->field_passcount;
-}
+    /*
+     * note that this name is a special sign to TIFFClose() and
+     * _TIFFSetupFields() to free the field
+     * Update:
+     *   This special sign is replaced by fld->field_anonymous  flag.
+     */
+    (void)snprintf(fld->field_name, 32, "Tag %d", (int)tag);
 
-int
-TIFFFieldReadCount(const TIFFField* fip)
-{
-	return fip->field_readcount;
-}
-
-int
-TIFFFieldWriteCount(const TIFFField* fip)
-{
-	return fip->field_writecount;
-}
-
-const TIFFField*
-_TIFFFindOrRegisterField(TIFF *tif, uint32 tag, TIFFDataType dt)
-
-{
-	const TIFFField *fld;
-
-	fld = TIFFFindField(tif, tag, dt);
-	if (fld == NULL) {
-		fld = _TIFFCreateAnonField(tif, tag, dt);
-		if (!_TIFFMergeFields(tif, fld, 1))
-			return NULL;
-	}
-
-	return fld;
-}
-
-TIFFField*
-_TIFFCreateAnonField(TIFF *tif, uint32 tag, TIFFDataType field_type)
-{
-	TIFFField *fld;
-	(void) tif;
-
-	fld = (TIFFField *) _TIFFmalloc(sizeof (TIFFField));
-	if (fld == NULL)
-	    return NULL;
-	_TIFFmemset(fld, 0, sizeof(TIFFField));
-
-	fld->field_tag = tag;
-	fld->field_readcount = TIFF_VARIABLE2;
-	fld->field_writecount = TIFF_VARIABLE2;
-	fld->field_type = field_type;
-	fld->reserved = 0;
-	switch (field_type)
-	{
-		case TIFF_BYTE:
-		case TIFF_UNDEFINED:
-			fld->set_field_type = TIFF_SETGET_C32_UINT8;
-			fld->get_field_type = TIFF_SETGET_C32_UINT8;
-			break;
-		case TIFF_ASCII:
-			fld->set_field_type = TIFF_SETGET_C32_ASCII;
-			fld->get_field_type = TIFF_SETGET_C32_ASCII;
-			break;
-		case TIFF_SHORT:
-			fld->set_field_type = TIFF_SETGET_C32_UINT16;
-			fld->get_field_type = TIFF_SETGET_C32_UINT16;
-			break;
-		case TIFF_LONG:
-			fld->set_field_type = TIFF_SETGET_C32_UINT32;
-			fld->get_field_type = TIFF_SETGET_C32_UINT32;
-			break;
-		case TIFF_RATIONAL:
-		case TIFF_SRATIONAL:
-		case TIFF_FLOAT:
-			fld->set_field_type = TIFF_SETGET_C32_FLOAT;
-			fld->get_field_type = TIFF_SETGET_C32_FLOAT;
-			break;
-		case TIFF_SBYTE:
-			fld->set_field_type = TIFF_SETGET_C32_SINT8;
-			fld->get_field_type = TIFF_SETGET_C32_SINT8;
-			break;
-		case TIFF_SSHORT:
-			fld->set_field_type = TIFF_SETGET_C32_SINT16;
-			fld->get_field_type = TIFF_SETGET_C32_SINT16;
-			break;
-		case TIFF_SLONG:
-			fld->set_field_type = TIFF_SETGET_C32_SINT32;
-			fld->get_field_type = TIFF_SETGET_C32_SINT32;
-			break;
-		case TIFF_DOUBLE:
-			fld->set_field_type = TIFF_SETGET_C32_DOUBLE;
-			fld->get_field_type = TIFF_SETGET_C32_DOUBLE;
-			break;
-		case TIFF_IFD:
-		case TIFF_IFD8:
-			fld->set_field_type = TIFF_SETGET_C32_IFD8;
-			fld->get_field_type = TIFF_SETGET_C32_IFD8;
-			break;
-		case TIFF_LONG8:
-			fld->set_field_type = TIFF_SETGET_C32_UINT64;
-			fld->get_field_type = TIFF_SETGET_C32_UINT64;
-			break;
-		case TIFF_SLONG8:
-			fld->set_field_type = TIFF_SETGET_C32_SINT64;
-			fld->get_field_type = TIFF_SETGET_C32_SINT64;
-			break;
-		default:
-			fld->set_field_type = TIFF_SETGET_UNDEFINED;
-			fld->get_field_type = TIFF_SETGET_UNDEFINED;
-			break;
-	}
-	fld->field_bit = FIELD_CUSTOM;
-	fld->field_oktochange = TRUE;
-	fld->field_passcount = TRUE;
-	fld->field_name = (char *) _TIFFmalloc(32);
-	if (fld->field_name == NULL) {
-	    _TIFFfree(fld);
-	    return NULL;
-	}
-	fld->field_subfields = NULL;
-
-	/* 
-	 * note that this name is a special sign to TIFFClose() and
-	 * _TIFFSetupFields() to free the field
-	 */
-	(void) snprintf(fld->field_name, 32, "Tag %d", (int) tag);
-
-	return fld;    
+    return fld;
 }
 
 /****************************************************************************
@@ -731,351 +999,353 @@
  * libtiff versions.
  ****************************************************************************/
 
-static TIFFSetGetFieldType
-_TIFFSetGetType(TIFFDataType type, short count, unsigned char passcount)
+static TIFFSetGetFieldType _TIFFSetGetType(TIFFDataType type, short count,
+                                           unsigned char passcount)
 {
-	if (type == TIFF_ASCII && count == TIFF_VARIABLE && passcount == 0)
-		return TIFF_SETGET_ASCII;
+    if (type == TIFF_ASCII && count == TIFF_VARIABLE && passcount == 0)
+        return TIFF_SETGET_ASCII;
 
-	else if (count == 1 && passcount == 0) {
-		switch (type)
-		{
-			case TIFF_BYTE:
-			case TIFF_UNDEFINED:
-				return TIFF_SETGET_UINT8;
-			case TIFF_ASCII:
-				return TIFF_SETGET_ASCII;
-			case TIFF_SHORT:
-				return TIFF_SETGET_UINT16;
-			case TIFF_LONG:
-				return TIFF_SETGET_UINT32;
-			case TIFF_RATIONAL:
-			case TIFF_SRATIONAL:
-			case TIFF_FLOAT:
-				return TIFF_SETGET_FLOAT;
-			case TIFF_SBYTE:
-				return TIFF_SETGET_SINT8;
-			case TIFF_SSHORT:
-				return TIFF_SETGET_SINT16;
-			case TIFF_SLONG:
-				return TIFF_SETGET_SINT32;
-			case TIFF_DOUBLE:
-				return TIFF_SETGET_DOUBLE;
-			case TIFF_IFD:
-			case TIFF_IFD8:
-				return TIFF_SETGET_IFD8;
-			case TIFF_LONG8:
-				return TIFF_SETGET_UINT64;
-			case TIFF_SLONG8:
-				return TIFF_SETGET_SINT64;
-			default:
-				return TIFF_SETGET_UNDEFINED;
-		}
-	}
+    else if (count == 1 && passcount == 0)
+    {
+        switch (type)
+        {
+            case TIFF_BYTE:
+            case TIFF_UNDEFINED:
+                return TIFF_SETGET_UINT8;
+            case TIFF_ASCII:
+                return TIFF_SETGET_ASCII;
+            case TIFF_SHORT:
+                return TIFF_SETGET_UINT16;
+            case TIFF_LONG:
+                return TIFF_SETGET_UINT32;
+            case TIFF_RATIONAL:
+            case TIFF_SRATIONAL:
+            case TIFF_FLOAT:
+                return TIFF_SETGET_FLOAT;
+            case TIFF_SBYTE:
+                return TIFF_SETGET_SINT8;
+            case TIFF_SSHORT:
+                return TIFF_SETGET_SINT16;
+            case TIFF_SLONG:
+                return TIFF_SETGET_SINT32;
+            case TIFF_DOUBLE:
+                return TIFF_SETGET_DOUBLE;
+            case TIFF_IFD:
+            case TIFF_IFD8:
+                return TIFF_SETGET_IFD8;
+            case TIFF_LONG8:
+                return TIFF_SETGET_UINT64;
+            case TIFF_SLONG8:
+                return TIFF_SETGET_SINT64;
+            default:
+                return TIFF_SETGET_UNDEFINED;
+        }
+    }
 
-	else if (count >= 1 && passcount == 0) {
-		switch (type)
-		{
-			case TIFF_BYTE:
-			case TIFF_UNDEFINED:
-				return TIFF_SETGET_C0_UINT8;
-			case TIFF_ASCII:
-				return TIFF_SETGET_C0_ASCII;
-			case TIFF_SHORT:
-				return TIFF_SETGET_C0_UINT16;
-			case TIFF_LONG:
-				return TIFF_SETGET_C0_UINT32;
-			case TIFF_RATIONAL:
-			case TIFF_SRATIONAL:
-			case TIFF_FLOAT:
-				return TIFF_SETGET_C0_FLOAT;
-			case TIFF_SBYTE:
-				return TIFF_SETGET_C0_SINT8;
-			case TIFF_SSHORT:
-				return TIFF_SETGET_C0_SINT16;
-			case TIFF_SLONG:
-				return TIFF_SETGET_C0_SINT32;
-			case TIFF_DOUBLE:
-				return TIFF_SETGET_C0_DOUBLE;
-			case TIFF_IFD:
-			case TIFF_IFD8:
-				return TIFF_SETGET_C0_IFD8;
-			case TIFF_LONG8:
-				return TIFF_SETGET_C0_UINT64;
-			case TIFF_SLONG8:
-				return TIFF_SETGET_C0_SINT64;
-			default:
-				return TIFF_SETGET_UNDEFINED;
-		}
-	}
+    else if (count >= 1 && passcount == 0)
+    {
+        switch (type)
+        {
+            case TIFF_BYTE:
+            case TIFF_UNDEFINED:
+                return TIFF_SETGET_C0_UINT8;
+            case TIFF_ASCII:
+                return TIFF_SETGET_C0_ASCII;
+            case TIFF_SHORT:
+                return TIFF_SETGET_C0_UINT16;
+            case TIFF_LONG:
+                return TIFF_SETGET_C0_UINT32;
+            case TIFF_RATIONAL:
+            case TIFF_SRATIONAL:
+            case TIFF_FLOAT:
+                return TIFF_SETGET_C0_FLOAT;
+            case TIFF_SBYTE:
+                return TIFF_SETGET_C0_SINT8;
+            case TIFF_SSHORT:
+                return TIFF_SETGET_C0_SINT16;
+            case TIFF_SLONG:
+                return TIFF_SETGET_C0_SINT32;
+            case TIFF_DOUBLE:
+                return TIFF_SETGET_C0_DOUBLE;
+            case TIFF_IFD:
+            case TIFF_IFD8:
+                return TIFF_SETGET_C0_IFD8;
+            case TIFF_LONG8:
+                return TIFF_SETGET_C0_UINT64;
+            case TIFF_SLONG8:
+                return TIFF_SETGET_C0_SINT64;
+            default:
+                return TIFF_SETGET_UNDEFINED;
+        }
+    }
 
-	else if (count == TIFF_VARIABLE && passcount == 1) {
-		switch (type)
-		{
-			case TIFF_BYTE:
-			case TIFF_UNDEFINED:
-				return TIFF_SETGET_C16_UINT8;
-			case TIFF_ASCII:
-				return TIFF_SETGET_C16_ASCII;
-			case TIFF_SHORT:
-				return TIFF_SETGET_C16_UINT16;
-			case TIFF_LONG:
-				return TIFF_SETGET_C16_UINT32;
-			case TIFF_RATIONAL:
-			case TIFF_SRATIONAL:
-			case TIFF_FLOAT:
-				return TIFF_SETGET_C16_FLOAT;
-			case TIFF_SBYTE:
-				return TIFF_SETGET_C16_SINT8;
-			case TIFF_SSHORT:
-				return TIFF_SETGET_C16_SINT16;
-			case TIFF_SLONG:
-				return TIFF_SETGET_C16_SINT32;
-			case TIFF_DOUBLE:
-				return TIFF_SETGET_C16_DOUBLE;
-			case TIFF_IFD:
-			case TIFF_IFD8:
-				return TIFF_SETGET_C16_IFD8;
-			case TIFF_LONG8:
-				return TIFF_SETGET_C16_UINT64;
-			case TIFF_SLONG8:
-				return TIFF_SETGET_C16_SINT64;
-			default:
-				return TIFF_SETGET_UNDEFINED;
-		}
-	}
+    else if (count == TIFF_VARIABLE && passcount == 1)
+    {
+        switch (type)
+        {
+            case TIFF_BYTE:
+            case TIFF_UNDEFINED:
+                return TIFF_SETGET_C16_UINT8;
+            case TIFF_ASCII:
+                return TIFF_SETGET_C16_ASCII;
+            case TIFF_SHORT:
+                return TIFF_SETGET_C16_UINT16;
+            case TIFF_LONG:
+                return TIFF_SETGET_C16_UINT32;
+            case TIFF_RATIONAL:
+            case TIFF_SRATIONAL:
+            case TIFF_FLOAT:
+                return TIFF_SETGET_C16_FLOAT;
+            case TIFF_SBYTE:
+                return TIFF_SETGET_C16_SINT8;
+            case TIFF_SSHORT:
+                return TIFF_SETGET_C16_SINT16;
+            case TIFF_SLONG:
+                return TIFF_SETGET_C16_SINT32;
+            case TIFF_DOUBLE:
+                return TIFF_SETGET_C16_DOUBLE;
+            case TIFF_IFD:
+            case TIFF_IFD8:
+                return TIFF_SETGET_C16_IFD8;
+            case TIFF_LONG8:
+                return TIFF_SETGET_C16_UINT64;
+            case TIFF_SLONG8:
+                return TIFF_SETGET_C16_SINT64;
+            default:
+                return TIFF_SETGET_UNDEFINED;
+        }
+    }
 
-	else if (count == TIFF_VARIABLE2 && passcount == 1) {
-		switch (type)
-		{
-			case TIFF_BYTE:
-			case TIFF_UNDEFINED:
-				return TIFF_SETGET_C32_UINT8;
-			case TIFF_ASCII:
-				return TIFF_SETGET_C32_ASCII;
-			case TIFF_SHORT:
-				return TIFF_SETGET_C32_UINT16;
-			case TIFF_LONG:
-				return TIFF_SETGET_C32_UINT32;
-			case TIFF_RATIONAL:
-			case TIFF_SRATIONAL:
-			case TIFF_FLOAT:
-				return TIFF_SETGET_C32_FLOAT;
-			case TIFF_SBYTE:
-				return TIFF_SETGET_C32_SINT8;
-			case TIFF_SSHORT:
-				return TIFF_SETGET_C32_SINT16;
-			case TIFF_SLONG:
-				return TIFF_SETGET_C32_SINT32;
-			case TIFF_DOUBLE:
-				return TIFF_SETGET_C32_DOUBLE;
-			case TIFF_IFD:
-			case TIFF_IFD8:
-				return TIFF_SETGET_C32_IFD8;
-			case TIFF_LONG8:
-				return TIFF_SETGET_C32_UINT64;
-			case TIFF_SLONG8:
-				return TIFF_SETGET_C32_SINT64;
-			default:
-				return TIFF_SETGET_UNDEFINED;
-		}
-	}
+    else if (count == TIFF_VARIABLE2 && passcount == 1)
+    {
+        switch (type)
+        {
+            case TIFF_BYTE:
+            case TIFF_UNDEFINED:
+                return TIFF_SETGET_C32_UINT8;
+            case TIFF_ASCII:
+                return TIFF_SETGET_C32_ASCII;
+            case TIFF_SHORT:
+                return TIFF_SETGET_C32_UINT16;
+            case TIFF_LONG:
+                return TIFF_SETGET_C32_UINT32;
+            case TIFF_RATIONAL:
+            case TIFF_SRATIONAL:
+            case TIFF_FLOAT:
+                return TIFF_SETGET_C32_FLOAT;
+            case TIFF_SBYTE:
+                return TIFF_SETGET_C32_SINT8;
+            case TIFF_SSHORT:
+                return TIFF_SETGET_C32_SINT16;
+            case TIFF_SLONG:
+                return TIFF_SETGET_C32_SINT32;
+            case TIFF_DOUBLE:
+                return TIFF_SETGET_C32_DOUBLE;
+            case TIFF_IFD:
+            case TIFF_IFD8:
+                return TIFF_SETGET_C32_IFD8;
+            case TIFF_LONG8:
+                return TIFF_SETGET_C32_UINT64;
+            case TIFF_SLONG8:
+                return TIFF_SETGET_C32_SINT64;
+            default:
+                return TIFF_SETGET_UNDEFINED;
+        }
+    }
 
-	return TIFF_SETGET_UNDEFINED;
+    return TIFF_SETGET_UNDEFINED;
 }
 
-int
-TIFFMergeFieldInfo(TIFF* tif, const TIFFFieldInfo info[], uint32 n)
+int TIFFMergeFieldInfo(TIFF *tif, const TIFFFieldInfo info[], uint32_t n)
 {
-	static const char module[] = "TIFFMergeFieldInfo";
-	static const char reason[] = "for fields array";
-	TIFFField *tp;
-	size_t nfields;
-	uint32 i;
+    static const char module[] = "TIFFMergeFieldInfo";
+    static const char reason[] = "for fields array";
+    TIFFField *tp;
+    size_t nfields;
+    uint32_t i;
 
-	if (tif->tif_nfieldscompat > 0) {
-		tif->tif_fieldscompat = (TIFFFieldArray *)
-			_TIFFCheckRealloc(tif, tif->tif_fieldscompat,
-					  tif->tif_nfieldscompat + 1,
-					  sizeof(TIFFFieldArray), reason);
-	} else {
-		tif->tif_fieldscompat = (TIFFFieldArray *)
-			_TIFFCheckMalloc(tif, 1, sizeof(TIFFFieldArray),
-					 reason);
-	}
-	if (!tif->tif_fieldscompat) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-			     "Failed to allocate fields array");
-		return -1;
-	}
-	nfields = tif->tif_nfieldscompat++;
+    if (tif->tif_nfieldscompat > 0)
+    {
+        tif->tif_fieldscompat = (TIFFFieldArray *)_TIFFCheckRealloc(
+            tif, tif->tif_fieldscompat, tif->tif_nfieldscompat + 1,
+            sizeof(TIFFFieldArray), reason);
+    }
+    else
+    {
+        tif->tif_fieldscompat = (TIFFFieldArray *)_TIFFCheckMalloc(
+            tif, 1, sizeof(TIFFFieldArray), reason);
+    }
+    if (!tif->tif_fieldscompat)
+    {
+        TIFFErrorExtR(tif, module, "Failed to allocate fields array");
+        return -1;
+    }
+    nfields = tif->tif_nfieldscompat++;
 
-	tif->tif_fieldscompat[nfields].type = tfiatOther;
-	tif->tif_fieldscompat[nfields].allocated_size = n;
-	tif->tif_fieldscompat[nfields].count = n;
-	tif->tif_fieldscompat[nfields].fields =
-		(TIFFField *)_TIFFCheckMalloc(tif, n, sizeof(TIFFField),
-					      reason);
-	if (!tif->tif_fieldscompat[nfields].fields) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-			     "Failed to allocate fields array");
-		return -1;
-	}
+    tif->tif_fieldscompat[nfields].type = tfiatOther;
+    tif->tif_fieldscompat[nfields].allocated_size = n;
+    tif->tif_fieldscompat[nfields].count = n;
+    tif->tif_fieldscompat[nfields].fields =
+        (TIFFField *)_TIFFCheckMalloc(tif, n, sizeof(TIFFField), reason);
+    if (!tif->tif_fieldscompat[nfields].fields)
+    {
+        TIFFErrorExtR(tif, module, "Failed to allocate fields array");
+        return -1;
+    }
 
-	tp = tif->tif_fieldscompat[nfields].fields;
-	for (i = 0; i < n; i++) {
-		tp->field_tag = info[i].field_tag;
-		tp->field_readcount = info[i].field_readcount;
-		tp->field_writecount = info[i].field_writecount;
-		tp->field_type = info[i].field_type;
-		tp->reserved = 0;
-		tp->set_field_type =
-		     _TIFFSetGetType(info[i].field_type,
-				info[i].field_readcount,
-				info[i].field_passcount);
-		tp->get_field_type =
-		     _TIFFSetGetType(info[i].field_type,
-				info[i].field_readcount,
-				info[i].field_passcount);
-		tp->field_bit = info[i].field_bit;
-		tp->field_oktochange = info[i].field_oktochange;
-		tp->field_passcount = info[i].field_passcount;
-		tp->field_name = info[i].field_name;
-		tp->field_subfields = NULL;
-		tp++;
-	}
+    tp = tif->tif_fieldscompat[nfields].fields;
+    for (i = 0; i < n; i++)
+    {
+        tp->field_tag = info[i].field_tag;
+        tp->field_readcount = info[i].field_readcount;
+        tp->field_writecount = info[i].field_writecount;
+        tp->field_type = info[i].field_type;
+        tp->field_anonymous = 0;
+        tp->set_field_type =
+            _TIFFSetGetType(info[i].field_type, info[i].field_readcount,
+                            info[i].field_passcount);
+        tp->get_field_type =
+            _TIFFSetGetType(info[i].field_type, info[i].field_readcount,
+                            info[i].field_passcount);
+        tp->field_bit = info[i].field_bit;
+        tp->field_oktochange = info[i].field_oktochange;
+        tp->field_passcount = info[i].field_passcount;
+        if (info[i].field_name == NULL)
+        {
+            TIFFErrorExtR(tif, module,
+                          "Field_name of %d.th allocation tag %d is NULL", i,
+                          info[i].field_tag);
+            return -1;
+        }
+        tp->field_name = info[i].field_name;
+        tp->field_subfields = NULL;
+        tp++;
+    }
 
-	if (!_TIFFMergeFields(tif, tif->tif_fieldscompat[nfields].fields, n)) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-			     "Setting up field info failed");
-		return -1;
-	}
+    if (!_TIFFMergeFields(tif, tif->tif_fieldscompat[nfields].fields, n))
+    {
+        TIFFErrorExtR(tif, module, "Setting up field info failed");
+        return -1;
+    }
 
-	return 0;
+    return 0;
 }
 
-int
-_TIFFCheckFieldIsValidForCodec(TIFF *tif, ttag_t tag)
+int _TIFFCheckFieldIsValidForCodec(TIFF *tif, ttag_t tag)
 {
-	/* Filter out non-codec specific tags */
-	switch (tag) {
-	    /* Shared tags */
-	    case TIFFTAG_PREDICTOR:
-	    /* JPEG tags */
-	    case TIFFTAG_JPEGTABLES:
-	    /* OJPEG tags */
-	    case TIFFTAG_JPEGIFOFFSET:
-	    case TIFFTAG_JPEGIFBYTECOUNT:
-	    case TIFFTAG_JPEGQTABLES:
-	    case TIFFTAG_JPEGDCTABLES:
-	    case TIFFTAG_JPEGACTABLES:
-	    case TIFFTAG_JPEGPROC:
-	    case TIFFTAG_JPEGRESTARTINTERVAL:
-	    /* CCITT* */
-	    case TIFFTAG_BADFAXLINES:
-	    case TIFFTAG_CLEANFAXDATA:
-	    case TIFFTAG_CONSECUTIVEBADFAXLINES:
-	    case TIFFTAG_GROUP3OPTIONS:
-	    case TIFFTAG_GROUP4OPTIONS:
-	    /* LERC */
-	    case TIFFTAG_LERC_PARAMETERS:
-		break;
-	    default:
-		return 1;
-	}
-	/* Check if codec specific tags are allowed for the current
-	 * compression scheme (codec) */
-	switch (tif->tif_dir.td_compression) {
-	    case COMPRESSION_LZW:
-		if (tag == TIFFTAG_PREDICTOR)
-		    return 1;
-		break;
-	    case COMPRESSION_PACKBITS:
-		/* No codec-specific tags */
-		break;
-	    case COMPRESSION_THUNDERSCAN:
-		/* No codec-specific tags */
-		break;
-	    case COMPRESSION_NEXT:
-		/* No codec-specific tags */
-		break;
-	    case COMPRESSION_JPEG:
-		if (tag == TIFFTAG_JPEGTABLES)
-		    return 1;
-		break;
-	    case COMPRESSION_OJPEG:
-		switch (tag) {
-		    case TIFFTAG_JPEGIFOFFSET:
-		    case TIFFTAG_JPEGIFBYTECOUNT:
-		    case TIFFTAG_JPEGQTABLES:
-		    case TIFFTAG_JPEGDCTABLES:
-		    case TIFFTAG_JPEGACTABLES:
-		    case TIFFTAG_JPEGPROC:
-		    case TIFFTAG_JPEGRESTARTINTERVAL:
-			return 1;
-		}
-		break;
-	    case COMPRESSION_CCITTRLE:
-	    case COMPRESSION_CCITTRLEW:
-	    case COMPRESSION_CCITTFAX3:
-	    case COMPRESSION_CCITTFAX4:
-		switch (tag) {
-		    case TIFFTAG_BADFAXLINES:
-		    case TIFFTAG_CLEANFAXDATA:
-		    case TIFFTAG_CONSECUTIVEBADFAXLINES:
-			return 1;
-		    case TIFFTAG_GROUP3OPTIONS:
-			if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX3)
-			    return 1;
-			break;
-		    case TIFFTAG_GROUP4OPTIONS:
-			if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX4)
-			    return 1;
-			break;
-		}
-		break;
-	    case COMPRESSION_JBIG:
-		/* No codec-specific tags */
-		break;
-	    case COMPRESSION_DEFLATE:
-	    case COMPRESSION_ADOBE_DEFLATE:
-		if (tag == TIFFTAG_PREDICTOR)
-		    return 1;
-		break;
-	   case COMPRESSION_PIXARLOG:
-		if (tag == TIFFTAG_PREDICTOR)
-		    return 1;
-		break;
-	    case COMPRESSION_SGILOG:
-	    case COMPRESSION_SGILOG24:
-		/* No codec-specific tags */
-		break;
-	    case COMPRESSION_LZMA:
-		if (tag == TIFFTAG_PREDICTOR)
-		    return 1;
-		break;
-	    case COMPRESSION_ZSTD:
-		if (tag == TIFFTAG_PREDICTOR)
-		    return 1;
-		break;
-	    case COMPRESSION_LERC:
-		if (tag == TIFFTAG_LERC_PARAMETERS)
-		    return 1;
-		break;
-		  case COMPRESSION_WEBP:
-		if (tag == TIFFTAG_PREDICTOR)
-				return 1;
-		break;
-	}
-	return 0;
+    /* Filter out non-codec specific tags */
+    switch (tag)
+    {
+        /* Shared tags */
+        case TIFFTAG_PREDICTOR:
+        /* JPEG tags */
+        case TIFFTAG_JPEGTABLES:
+        /* OJPEG tags */
+        case TIFFTAG_JPEGIFOFFSET:
+        case TIFFTAG_JPEGIFBYTECOUNT:
+        case TIFFTAG_JPEGQTABLES:
+        case TIFFTAG_JPEGDCTABLES:
+        case TIFFTAG_JPEGACTABLES:
+        case TIFFTAG_JPEGPROC:
+        case TIFFTAG_JPEGRESTARTINTERVAL:
+        /* CCITT* */
+        case TIFFTAG_BADFAXLINES:
+        case TIFFTAG_CLEANFAXDATA:
+        case TIFFTAG_CONSECUTIVEBADFAXLINES:
+        case TIFFTAG_GROUP3OPTIONS:
+        case TIFFTAG_GROUP4OPTIONS:
+        /* LERC */
+        case TIFFTAG_LERC_PARAMETERS:
+            break;
+        default:
+            return 1;
+    }
+    if (!TIFFIsCODECConfigured(tif->tif_dir.td_compression))
+    {
+        return 0;
+    }
+    /* Check if codec specific tags are allowed for the current
+     * compression scheme (codec) */
+    switch (tif->tif_dir.td_compression)
+    {
+        case COMPRESSION_LZW:
+            if (tag == TIFFTAG_PREDICTOR)
+                return 1;
+            break;
+        case COMPRESSION_PACKBITS:
+            /* No codec-specific tags */
+            break;
+        case COMPRESSION_THUNDERSCAN:
+            /* No codec-specific tags */
+            break;
+        case COMPRESSION_NEXT:
+            /* No codec-specific tags */
+            break;
+        case COMPRESSION_JPEG:
+            if (tag == TIFFTAG_JPEGTABLES)
+                return 1;
+            break;
+        case COMPRESSION_OJPEG:
+            switch (tag)
+            {
+                case TIFFTAG_JPEGIFOFFSET:
+                case TIFFTAG_JPEGIFBYTECOUNT:
+                case TIFFTAG_JPEGQTABLES:
+                case TIFFTAG_JPEGDCTABLES:
+                case TIFFTAG_JPEGACTABLES:
+                case TIFFTAG_JPEGPROC:
+                case TIFFTAG_JPEGRESTARTINTERVAL:
+                    return 1;
+            }
+            break;
+        case COMPRESSION_CCITTRLE:
+        case COMPRESSION_CCITTRLEW:
+        case COMPRESSION_CCITTFAX3:
+        case COMPRESSION_CCITTFAX4:
+            switch (tag)
+            {
+                case TIFFTAG_BADFAXLINES:
+                case TIFFTAG_CLEANFAXDATA:
+                case TIFFTAG_CONSECUTIVEBADFAXLINES:
+                    return 1;
+                case TIFFTAG_GROUP3OPTIONS:
+                    if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX3)
+                        return 1;
+                    break;
+                case TIFFTAG_GROUP4OPTIONS:
+                    if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX4)
+                        return 1;
+                    break;
+            }
+            break;
+        case COMPRESSION_JBIG:
+            /* No codec-specific tags */
+            break;
+        case COMPRESSION_DEFLATE:
+        case COMPRESSION_ADOBE_DEFLATE:
+            if (tag == TIFFTAG_PREDICTOR)
+                return 1;
+            break;
+        case COMPRESSION_PIXARLOG:
+            if (tag == TIFFTAG_PREDICTOR)
+                return 1;
+            break;
+        case COMPRESSION_SGILOG:
+        case COMPRESSION_SGILOG24:
+            /* No codec-specific tags */
+            break;
+        case COMPRESSION_LZMA:
+            if (tag == TIFFTAG_PREDICTOR)
+                return 1;
+            break;
+        case COMPRESSION_ZSTD:
+            if (tag == TIFFTAG_PREDICTOR)
+                return 1;
+            break;
+        case COMPRESSION_LERC:
+            if (tag == TIFFTAG_LERC_PARAMETERS)
+                return 1;
+            break;
+    }
+    return 0;
 }
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_dirread.c b/third_party/libtiff/tif_dirread.c
index ceb166c..838f64d 100644
--- a/third_party/libtiff/tif_dirread.c
+++ b/third_party/libtiff/tif_dirread.c
@@ -34,3465 +34,3993 @@
  *   TIFFReadDirectory, so as to eliminate current possibly repetitive lookup.
  */
 
+#include "tiffconf.h"
 #include "tiffiop.h"
 #include <float.h>
 #include <limits.h>
 #include <stdlib.h>
+#include <string.h>
 
-#define FAILED_FII    ((uint32) -1)
-
-/*
- * Largest 64-bit signed integer value.
- */
-#define TIFF_INT64_MAX ((int64)(TIFF_UINT64_MAX >> 1))
+#define FAILED_FII ((uint32_t)-1)
 
 #ifdef HAVE_IEEEFP
-# define TIFFCvtIEEEFloatToNative(tif, n, fp)
-# define TIFFCvtIEEEDoubleToNative(tif, n, dp)
+#define TIFFCvtIEEEFloatToNative(tif, n, fp)
+#define TIFFCvtIEEEDoubleToNative(tif, n, dp)
 #else
-extern void TIFFCvtIEEEFloatToNative(TIFF*, uint32, float*);
-extern void TIFFCvtIEEEDoubleToNative(TIFF*, uint32, double*);
+extern void TIFFCvtIEEEFloatToNative(TIFF *, uint32_t, float *);
+extern void TIFFCvtIEEEDoubleToNative(TIFF *, uint32_t, double *);
 #endif
 
-enum TIFFReadDirEntryErr {
-	TIFFReadDirEntryErrOk = 0,
-	TIFFReadDirEntryErrCount = 1,
-	TIFFReadDirEntryErrType = 2,
-	TIFFReadDirEntryErrIo = 3,
-	TIFFReadDirEntryErrRange = 4,
-	TIFFReadDirEntryErrPsdif = 5,
-	TIFFReadDirEntryErrSizesan = 6,
-	TIFFReadDirEntryErrAlloc = 7,
+enum TIFFReadDirEntryErr
+{
+    TIFFReadDirEntryErrOk = 0,
+    TIFFReadDirEntryErrCount = 1,
+    TIFFReadDirEntryErrType = 2,
+    TIFFReadDirEntryErrIo = 3,
+    TIFFReadDirEntryErrRange = 4,
+    TIFFReadDirEntryErrPsdif = 5,
+    TIFFReadDirEntryErrSizesan = 6,
+    TIFFReadDirEntryErrAlloc = 7,
 };
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryByte(TIFF* tif, TIFFDirEntry* direntry, uint8* value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryShort(TIFF* tif, TIFFDirEntry* direntry, uint16* value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryLong(TIFF* tif, TIFFDirEntry* direntry, uint32* value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryLong8(TIFF* tif, TIFFDirEntry* direntry, uint64* value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryFloat(TIFF* tif, TIFFDirEntry* direntry, float* value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryDouble(TIFF* tif, TIFFDirEntry* direntry, double* value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryIfd8(TIFF* tif, TIFFDirEntry* direntry, uint64* value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryByte(TIFF *tif, TIFFDirEntry *direntry, uint8_t *value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntrySbyte(TIFF *tif, TIFFDirEntry *direntry, int8_t *value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryShort(TIFF *tif, TIFFDirEntry *direntry, uint16_t *value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntrySshort(TIFF *tif, TIFFDirEntry *direntry, int16_t *value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryLong(TIFF *tif, TIFFDirEntry *direntry, uint32_t *value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntrySlong(TIFF *tif, TIFFDirEntry *direntry, int32_t *value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryLong8(TIFF *tif, TIFFDirEntry *direntry, uint64_t *value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntrySlong8(TIFF *tif, TIFFDirEntry *direntry, int64_t *value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryFloat(TIFF *tif, TIFFDirEntry *direntry, float *value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryDouble(TIFF *tif, TIFFDirEntry *direntry, double *value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryIfd8(TIFF *tif, TIFFDirEntry *direntry, uint64_t *value);
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryArray(TIFF* tif, TIFFDirEntry* direntry, uint32* count, uint32 desttypesize, void** value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryByteArray(TIFF* tif, TIFFDirEntry* direntry, uint8** value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntrySbyteArray(TIFF* tif, TIFFDirEntry* direntry, int8** value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryShortArray(TIFF* tif, TIFFDirEntry* direntry, uint16** value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntrySshortArray(TIFF* tif, TIFFDirEntry* direntry, int16** value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryLongArray(TIFF* tif, TIFFDirEntry* direntry, uint32** value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntrySlongArray(TIFF* tif, TIFFDirEntry* direntry, int32** value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryLong8Array(TIFF* tif, TIFFDirEntry* direntry, uint64** value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntrySlong8Array(TIFF* tif, TIFFDirEntry* direntry, int64** value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryFloatArray(TIFF* tif, TIFFDirEntry* direntry, float** value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryDoubleArray(TIFF* tif, TIFFDirEntry* direntry, double** value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryIfd8Array(TIFF* tif, TIFFDirEntry* direntry, uint64** value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryArray(TIFF *tif, TIFFDirEntry *direntry, uint32_t *count,
+                      uint32_t desttypesize, void **value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryByteArray(TIFF *tif, TIFFDirEntry *direntry, uint8_t **value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntrySbyteArray(TIFF *tif, TIFFDirEntry *direntry, int8_t **value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryShortArray(TIFF *tif, TIFFDirEntry *direntry, uint16_t **value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntrySshortArray(TIFF *tif, TIFFDirEntry *direntry, int16_t **value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryLongArray(TIFF *tif, TIFFDirEntry *direntry, uint32_t **value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntrySlongArray(TIFF *tif, TIFFDirEntry *direntry, int32_t **value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryLong8Array(TIFF *tif, TIFFDirEntry *direntry, uint64_t **value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntrySlong8Array(TIFF *tif, TIFFDirEntry *direntry, int64_t **value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryFloatArray(TIFF *tif, TIFFDirEntry *direntry, float **value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryDoubleArray(TIFF *tif, TIFFDirEntry *direntry, double **value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryIfd8Array(TIFF *tif, TIFFDirEntry *direntry, uint64_t **value);
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryPersampleShort(TIFF* tif, TIFFDirEntry* direntry, uint16* value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryPersampleShort(TIFF *tif, TIFFDirEntry *direntry,
+                               uint16_t *value);
+
+static void TIFFReadDirEntryCheckedByte(TIFF *tif, TIFFDirEntry *direntry,
+                                        uint8_t *value);
+static void TIFFReadDirEntryCheckedSbyte(TIFF *tif, TIFFDirEntry *direntry,
+                                         int8_t *value);
+static void TIFFReadDirEntryCheckedShort(TIFF *tif, TIFFDirEntry *direntry,
+                                         uint16_t *value);
+static void TIFFReadDirEntryCheckedSshort(TIFF *tif, TIFFDirEntry *direntry,
+                                          int16_t *value);
+static void TIFFReadDirEntryCheckedLong(TIFF *tif, TIFFDirEntry *direntry,
+                                        uint32_t *value);
+static void TIFFReadDirEntryCheckedSlong(TIFF *tif, TIFFDirEntry *direntry,
+                                         int32_t *value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckedLong8(TIFF *tif, TIFFDirEntry *direntry,
+                             uint64_t *value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckedSlong8(TIFF *tif, TIFFDirEntry *direntry,
+                              int64_t *value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckedRational(TIFF *tif, TIFFDirEntry *direntry,
+                                double *value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckedSrational(TIFF *tif, TIFFDirEntry *direntry,
+                                 double *value);
+static void TIFFReadDirEntryCheckedFloat(TIFF *tif, TIFFDirEntry *direntry,
+                                         float *value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckedDouble(TIFF *tif, TIFFDirEntry *direntry, double *value);
 #if 0
-static enum TIFFReadDirEntryErr TIFFReadDirEntryPersampleDouble(TIFF* tif, TIFFDirEntry* direntry, double* value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckedRationalDirect(TIFF *tif, TIFFDirEntry *direntry,
+                                      TIFFRational_t *value);
 #endif
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeByteSbyte(int8_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeByteShort(uint16_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeByteSshort(int16_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeByteLong(uint32_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeByteSlong(int32_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeByteLong8(uint64_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeByteSlong8(int64_t value);
 
-static void TIFFReadDirEntryCheckedByte(TIFF* tif, TIFFDirEntry* direntry, uint8* value);
-static void TIFFReadDirEntryCheckedSbyte(TIFF* tif, TIFFDirEntry* direntry, int8* value);
-static void TIFFReadDirEntryCheckedShort(TIFF* tif, TIFFDirEntry* direntry, uint16* value);
-static void TIFFReadDirEntryCheckedSshort(TIFF* tif, TIFFDirEntry* direntry, int16* value);
-static void TIFFReadDirEntryCheckedLong(TIFF* tif, TIFFDirEntry* direntry, uint32* value);
-static void TIFFReadDirEntryCheckedSlong(TIFF* tif, TIFFDirEntry* direntry, int32* value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedLong8(TIFF* tif, TIFFDirEntry* direntry, uint64* value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedSlong8(TIFF* tif, TIFFDirEntry* direntry, int64* value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedRational(TIFF* tif, TIFFDirEntry* direntry, double* value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedSrational(TIFF* tif, TIFFDirEntry* direntry, double* value);
-static void TIFFReadDirEntryCheckedFloat(TIFF* tif, TIFFDirEntry* direntry, float* value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedDouble(TIFF* tif, TIFFDirEntry* direntry, double* value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSbyteByte(uint8_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSbyteShort(uint16_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSbyteSshort(int16_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSbyteLong(uint32_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSbyteSlong(int32_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSbyteLong8(uint64_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSbyteSlong8(int64_t value);
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteSbyte(int8 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteShort(uint16 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteSshort(int16 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteLong(uint32 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteSlong(int32 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteLong8(uint64 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteSlong8(int64 value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeShortSbyte(int8_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeShortSshort(int16_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeShortLong(uint32_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeShortSlong(int32_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeShortLong8(uint64_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeShortSlong8(int64_t value);
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteByte(uint8 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteShort(uint16 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteSshort(int16 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteLong(uint32 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteSlong(int32 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteLong8(uint64 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteSlong8(int64 value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSshortShort(uint16_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSshortLong(uint32_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSshortSlong(int32_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSshortLong8(uint64_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSshortSlong8(int64_t value);
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortSbyte(int8 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortSshort(int16 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortLong(uint32 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortSlong(int32 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortLong8(uint64 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortSlong8(int64 value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeLongSbyte(int8_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeLongSshort(int16_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeLongSlong(int32_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeLongLong8(uint64_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeLongSlong8(int64_t value);
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortShort(uint16 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortLong(uint32 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortSlong(int32 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortLong8(uint64 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortSlong8(int64 value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSlongLong(uint32_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSlongLong8(uint64_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSlongSlong8(int64_t value);
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLongSbyte(int8 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLongSshort(int16 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLongSlong(int32 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLongLong8(uint64 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLongSlong8(int64 value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeLong8Sbyte(int8_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeLong8Sshort(int16_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeLong8Slong(int32_t value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeLong8Slong8(int64_t value);
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSlongLong(uint32 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSlongLong8(uint64 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSlongSlong8(int64 value);
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSlong8Long8(uint64_t value);
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLong8Sbyte(int8 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLong8Sshort(int16 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLong8Slong(int32 value);
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLong8Slong8(int64 value);
+static enum TIFFReadDirEntryErr TIFFReadDirEntryData(TIFF *tif, uint64_t offset,
+                                                     tmsize_t size, void *dest);
+static void TIFFReadDirEntryOutputErr(TIFF *tif, enum TIFFReadDirEntryErr err,
+                                      const char *module, const char *tagname,
+                                      int recover);
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSlong8Long8(uint64 value);
+static void TIFFReadDirectoryCheckOrder(TIFF *tif, TIFFDirEntry *dir,
+                                        uint16_t dircount);
+static TIFFDirEntry *TIFFReadDirectoryFindEntry(TIFF *tif, TIFFDirEntry *dir,
+                                                uint16_t dircount,
+                                                uint16_t tagid);
+static void TIFFReadDirectoryFindFieldInfo(TIFF *tif, uint16_t tagid,
+                                           uint32_t *fii);
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryData(TIFF* tif, uint64 offset, tmsize_t size, void* dest);
-static void TIFFReadDirEntryOutputErr(TIFF* tif, enum TIFFReadDirEntryErr err, const char* module, const char* tagname, int recover);
+static int EstimateStripByteCounts(TIFF *tif, TIFFDirEntry *dir,
+                                   uint16_t dircount);
+static void MissingRequired(TIFF *, const char *);
+static int CheckDirCount(TIFF *, TIFFDirEntry *, uint32_t);
+static uint16_t TIFFFetchDirectory(TIFF *tif, uint64_t diroff,
+                                   TIFFDirEntry **pdir, uint64_t *nextdiroff);
+static int TIFFFetchNormalTag(TIFF *, TIFFDirEntry *, int recover);
+static int TIFFFetchStripThing(TIFF *tif, TIFFDirEntry *dir, uint32_t nstrips,
+                               uint64_t **lpp);
+static int TIFFFetchSubjectDistance(TIFF *, TIFFDirEntry *);
+static void ChopUpSingleUncompressedStrip(TIFF *);
+static void TryChopUpUncompressedBigTiff(TIFF *);
+static uint64_t TIFFReadUInt64(const uint8_t *value);
+static int _TIFFGetMaxColorChannels(uint16_t photometric);
 
-static void TIFFReadDirectoryCheckOrder(TIFF* tif, TIFFDirEntry* dir, uint16 dircount);
-static TIFFDirEntry* TIFFReadDirectoryFindEntry(TIFF* tif, TIFFDirEntry* dir, uint16 dircount, uint16 tagid);
-static void TIFFReadDirectoryFindFieldInfo(TIFF* tif, uint16 tagid, uint32* fii);
-
-static int EstimateStripByteCounts(TIFF* tif, TIFFDirEntry* dir, uint16 dircount);
-static void MissingRequired(TIFF*, const char*);
-static int TIFFCheckDirOffset(TIFF* tif, uint64 diroff);
-static int CheckDirCount(TIFF*, TIFFDirEntry*, uint32);
-static uint16 TIFFFetchDirectory(TIFF* tif, uint64 diroff, TIFFDirEntry** pdir, uint64* nextdiroff);
-static int TIFFFetchNormalTag(TIFF*, TIFFDirEntry*, int recover);
-static int TIFFFetchStripThing(TIFF* tif, TIFFDirEntry* dir, uint32 nstrips, uint64** lpp);
-static int TIFFFetchSubjectDistance(TIFF*, TIFFDirEntry*);
-static void ChopUpSingleUncompressedStrip(TIFF*);
-static void TryChopUpUncompressedBigTiff(TIFF*);
-static uint64 TIFFReadUInt64(const uint8 *value);
-static int _TIFFGetMaxColorChannels(uint16 photometric);
-
-static int _TIFFFillStrilesInternal( TIFF *tif, int loadStripByteCount );
+static int _TIFFFillStrilesInternal(TIFF *tif, int loadStripByteCount);
 
 typedef union _UInt64Aligned_t
 {
-        double d;
-	uint64 l;
-	uint32 i[2];
-	uint16 s[4];
-	uint8  c[8];
+    double d;
+    uint64_t l;
+    uint32_t i[2];
+    uint16_t s[4];
+    uint8_t c[8];
 } UInt64Aligned_t;
 
 /*
-  Unaligned safe copy of a uint64 value from an octet array.
+  Unaligned safe copy of a uint64_t value from an octet array.
 */
-static uint64 TIFFReadUInt64(const uint8 *value)
+static uint64_t TIFFReadUInt64(const uint8_t *value)
 {
-	UInt64Aligned_t result;
+    UInt64Aligned_t result;
 
-	result.c[0]=value[0];
-	result.c[1]=value[1];
-	result.c[2]=value[2];
-	result.c[3]=value[3];
-	result.c[4]=value[4];
-	result.c[5]=value[5];
-	result.c[6]=value[6];
-	result.c[7]=value[7];
+    result.c[0] = value[0];
+    result.c[1] = value[1];
+    result.c[2] = value[2];
+    result.c[3] = value[3];
+    result.c[4] = value[4];
+    result.c[5] = value[5];
+    result.c[6] = value[6];
+    result.c[7] = value[7];
 
-	return result.l;
+    return result.l;
 }
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryByte(TIFF* tif, TIFFDirEntry* direntry, uint8* value)
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryByte(TIFF *tif, TIFFDirEntry *direntry, uint8_t *value)
 {
-	enum TIFFReadDirEntryErr err;
-	if (direntry->tdir_count!=1)
-		return(TIFFReadDirEntryErrCount);
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-		case TIFF_UNDEFINED:	/* Support to read TIFF_UNDEFINED with field_readcount==1 */
-			TIFFReadDirEntryCheckedByte(tif,direntry,value);
-			return(TIFFReadDirEntryErrOk);
-		case TIFF_SBYTE:
-			{
-				int8 m;
-				TIFFReadDirEntryCheckedSbyte(tif,direntry,&m);
-				err=TIFFReadDirEntryCheckRangeByteSbyte(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint8)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SHORT:
-			{
-				uint16 m;
-				TIFFReadDirEntryCheckedShort(tif,direntry,&m);
-				err=TIFFReadDirEntryCheckRangeByteShort(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint8)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SSHORT:
-			{
-				int16 m;
-				TIFFReadDirEntryCheckedSshort(tif,direntry,&m);
-				err=TIFFReadDirEntryCheckRangeByteSshort(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint8)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_LONG:
-			{
-				uint32 m;
-				TIFFReadDirEntryCheckedLong(tif,direntry,&m);
-				err=TIFFReadDirEntryCheckRangeByteLong(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint8)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SLONG:
-			{
-				int32 m;
-				TIFFReadDirEntryCheckedSlong(tif,direntry,&m);
-				err=TIFFReadDirEntryCheckRangeByteSlong(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint8)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_LONG8:
-			{
-				uint64 m;
-				err=TIFFReadDirEntryCheckedLong8(tif,direntry,&m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				err=TIFFReadDirEntryCheckRangeByteLong8(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint8)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SLONG8:
-			{
-				int64 m;
-				err=TIFFReadDirEntryCheckedSlong8(tif,direntry,&m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				err=TIFFReadDirEntryCheckRangeByteSlong8(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint8)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
+    enum TIFFReadDirEntryErr err;
+    if (direntry->tdir_count != 1)
+        return (TIFFReadDirEntryErrCount);
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        case TIFF_UNDEFINED: /* Support to read TIFF_UNDEFINED with
+                                field_readcount==1 */
+            TIFFReadDirEntryCheckedByte(tif, direntry, value);
+            return (TIFFReadDirEntryErrOk);
+        case TIFF_SBYTE:
+        {
+            int8_t m;
+            TIFFReadDirEntryCheckedSbyte(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeByteSbyte(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint8_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SHORT:
+        {
+            uint16_t m;
+            TIFFReadDirEntryCheckedShort(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeByteShort(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint8_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SSHORT:
+        {
+            int16_t m;
+            TIFFReadDirEntryCheckedSshort(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeByteSshort(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint8_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG:
+        {
+            uint32_t m;
+            TIFFReadDirEntryCheckedLong(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeByteLong(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint8_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG:
+        {
+            int32_t m;
+            TIFFReadDirEntryCheckedSlong(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeByteSlong(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint8_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG8:
+        {
+            uint64_t m;
+            err = TIFFReadDirEntryCheckedLong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            err = TIFFReadDirEntryCheckRangeByteLong8(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint8_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG8:
+        {
+            int64_t m;
+            err = TIFFReadDirEntryCheckedSlong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            err = TIFFReadDirEntryCheckRangeByteSlong8(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint8_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
 }
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryShort(TIFF* tif, TIFFDirEntry* direntry, uint16* value)
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntrySbyte(TIFF *tif, TIFFDirEntry *direntry, int8_t *value)
 {
-	enum TIFFReadDirEntryErr err;
-	if (direntry->tdir_count!=1)
-		return(TIFFReadDirEntryErrCount);
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-			{
-				uint8 m;
-				TIFFReadDirEntryCheckedByte(tif,direntry,&m);
-				*value=(uint16)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SBYTE:
-			{
-				int8 m;
-				TIFFReadDirEntryCheckedSbyte(tif,direntry,&m);
-				err=TIFFReadDirEntryCheckRangeShortSbyte(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint16)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SHORT:
-			TIFFReadDirEntryCheckedShort(tif,direntry,value);
-			return(TIFFReadDirEntryErrOk);
-		case TIFF_SSHORT:
-			{
-				int16 m;
-				TIFFReadDirEntryCheckedSshort(tif,direntry,&m);
-				err=TIFFReadDirEntryCheckRangeShortSshort(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint16)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_LONG:
-			{
-				uint32 m;
-				TIFFReadDirEntryCheckedLong(tif,direntry,&m);
-				err=TIFFReadDirEntryCheckRangeShortLong(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint16)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SLONG:
-			{
-				int32 m;
-				TIFFReadDirEntryCheckedSlong(tif,direntry,&m);
-				err=TIFFReadDirEntryCheckRangeShortSlong(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint16)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_LONG8:
-			{
-				uint64 m;
-				err=TIFFReadDirEntryCheckedLong8(tif,direntry,&m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				err=TIFFReadDirEntryCheckRangeShortLong8(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint16)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SLONG8:
-			{
-				int64 m;
-				err=TIFFReadDirEntryCheckedSlong8(tif,direntry,&m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				err=TIFFReadDirEntryCheckRangeShortSlong8(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint16)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
-}
+    enum TIFFReadDirEntryErr err;
+    if (direntry->tdir_count != 1)
+        return (TIFFReadDirEntryErrCount);
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        case TIFF_UNDEFINED: /* Support to read TIFF_UNDEFINED with
+                                field_readcount==1 */
+        {
+            uint8_t m;
+            TIFFReadDirEntryCheckedByte(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeSbyteByte(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (int8_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SBYTE:
+        {
+            TIFFReadDirEntryCheckedSbyte(tif, direntry, value);
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SHORT:
+        {
+            uint16_t m;
+            TIFFReadDirEntryCheckedShort(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeSbyteShort(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (int8_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SSHORT:
+        {
+            int16_t m;
+            TIFFReadDirEntryCheckedSshort(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeSbyteSshort(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (int8_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG:
+        {
+            uint32_t m;
+            TIFFReadDirEntryCheckedLong(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeSbyteLong(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (int8_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG:
+        {
+            int32_t m;
+            TIFFReadDirEntryCheckedSlong(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeSbyteSlong(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (int8_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG8:
+        {
+            uint64_t m;
+            err = TIFFReadDirEntryCheckedLong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            err = TIFFReadDirEntryCheckRangeSbyteLong8(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (int8_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG8:
+        {
+            int64_t m;
+            err = TIFFReadDirEntryCheckedSlong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            err = TIFFReadDirEntryCheckRangeSbyteSlong8(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (int8_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+} /*-- TIFFReadDirEntrySbyte() --*/
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryLong(TIFF* tif, TIFFDirEntry* direntry, uint32* value)
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryShort(TIFF *tif, TIFFDirEntry *direntry, uint16_t *value)
 {
-	enum TIFFReadDirEntryErr err;
-	if (direntry->tdir_count!=1)
-		return(TIFFReadDirEntryErrCount);
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-			{
-				uint8 m;
-				TIFFReadDirEntryCheckedByte(tif,direntry,&m);
-				*value=(uint32)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SBYTE:
-			{
-				int8 m;
-				TIFFReadDirEntryCheckedSbyte(tif,direntry,&m);
-				err=TIFFReadDirEntryCheckRangeLongSbyte(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint32)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SHORT:
-			{
-				uint16 m;
-				TIFFReadDirEntryCheckedShort(tif,direntry,&m);
-				*value=(uint32)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SSHORT:
-			{
-				int16 m;
-				TIFFReadDirEntryCheckedSshort(tif,direntry,&m);
-				err=TIFFReadDirEntryCheckRangeLongSshort(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint32)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_LONG:
-			TIFFReadDirEntryCheckedLong(tif,direntry,value);
-			return(TIFFReadDirEntryErrOk);
-		case TIFF_SLONG:
-			{
-				int32 m;
-				TIFFReadDirEntryCheckedSlong(tif,direntry,&m);
-				err=TIFFReadDirEntryCheckRangeLongSlong(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint32)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_LONG8:
-			{
-				uint64 m;
-				err=TIFFReadDirEntryCheckedLong8(tif,direntry,&m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				err=TIFFReadDirEntryCheckRangeLongLong8(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint32)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SLONG8:
-			{
-				int64 m;
-				err=TIFFReadDirEntryCheckedSlong8(tif,direntry,&m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				err=TIFFReadDirEntryCheckRangeLongSlong8(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint32)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
-}
+    enum TIFFReadDirEntryErr err;
+    if (direntry->tdir_count != 1)
+        return (TIFFReadDirEntryErrCount);
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        {
+            uint8_t m;
+            TIFFReadDirEntryCheckedByte(tif, direntry, &m);
+            *value = (uint16_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SBYTE:
+        {
+            int8_t m;
+            TIFFReadDirEntryCheckedSbyte(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeShortSbyte(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint16_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SHORT:
+            TIFFReadDirEntryCheckedShort(tif, direntry, value);
+            return (TIFFReadDirEntryErrOk);
+        case TIFF_SSHORT:
+        {
+            int16_t m;
+            TIFFReadDirEntryCheckedSshort(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeShortSshort(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint16_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG:
+        {
+            uint32_t m;
+            TIFFReadDirEntryCheckedLong(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeShortLong(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint16_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG:
+        {
+            int32_t m;
+            TIFFReadDirEntryCheckedSlong(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeShortSlong(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint16_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG8:
+        {
+            uint64_t m;
+            err = TIFFReadDirEntryCheckedLong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            err = TIFFReadDirEntryCheckRangeShortLong8(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint16_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG8:
+        {
+            int64_t m;
+            err = TIFFReadDirEntryCheckedSlong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            err = TIFFReadDirEntryCheckRangeShortSlong8(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint16_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+} /*-- TIFFReadDirEntryShort() --*/
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryLong8(TIFF* tif, TIFFDirEntry* direntry, uint64* value)
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntrySshort(TIFF *tif, TIFFDirEntry *direntry, int16_t *value)
 {
-	enum TIFFReadDirEntryErr err;
-	if (direntry->tdir_count!=1)
-		return(TIFFReadDirEntryErrCount);
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-			{
-				uint8 m;
-				TIFFReadDirEntryCheckedByte(tif,direntry,&m);
-				*value=(uint64)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SBYTE:
-			{
-				int8 m;
-				TIFFReadDirEntryCheckedSbyte(tif,direntry,&m);
-				err=TIFFReadDirEntryCheckRangeLong8Sbyte(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint64)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SHORT:
-			{
-				uint16 m;
-				TIFFReadDirEntryCheckedShort(tif,direntry,&m);
-				*value=(uint64)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SSHORT:
-			{
-				int16 m;
-				TIFFReadDirEntryCheckedSshort(tif,direntry,&m);
-				err=TIFFReadDirEntryCheckRangeLong8Sshort(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint64)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_LONG:
-			{
-				uint32 m;
-				TIFFReadDirEntryCheckedLong(tif,direntry,&m);
-				*value=(uint64)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SLONG:
-			{
-				int32 m;
-				TIFFReadDirEntryCheckedSlong(tif,direntry,&m);
-				err=TIFFReadDirEntryCheckRangeLong8Slong(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint64)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_LONG8:
-			err=TIFFReadDirEntryCheckedLong8(tif,direntry,value);
-			return(err);
-		case TIFF_SLONG8:
-			{
-				int64 m;
-				err=TIFFReadDirEntryCheckedSlong8(tif,direntry,&m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				err=TIFFReadDirEntryCheckRangeLong8Slong8(m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(uint64)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
-}
+    enum TIFFReadDirEntryErr err;
+    if (direntry->tdir_count != 1)
+        return (TIFFReadDirEntryErrCount);
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        {
+            uint8_t m;
+            TIFFReadDirEntryCheckedByte(tif, direntry, &m);
+            *value = (int16_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SBYTE:
+        {
+            int8_t m;
+            TIFFReadDirEntryCheckedSbyte(tif, direntry, &m);
+            *value = (int16_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SHORT:
+        {
+            uint16_t m;
+            TIFFReadDirEntryCheckedShort(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeSshortShort(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint16_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SSHORT:
+            TIFFReadDirEntryCheckedSshort(tif, direntry, value);
+            return (TIFFReadDirEntryErrOk);
+        case TIFF_LONG:
+        {
+            uint32_t m;
+            TIFFReadDirEntryCheckedLong(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeSshortLong(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (int16_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG:
+        {
+            int32_t m;
+            TIFFReadDirEntryCheckedSlong(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeSshortSlong(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (int16_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG8:
+        {
+            uint64_t m;
+            err = TIFFReadDirEntryCheckedLong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            err = TIFFReadDirEntryCheckRangeSshortLong8(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (int16_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG8:
+        {
+            int64_t m;
+            err = TIFFReadDirEntryCheckedSlong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            err = TIFFReadDirEntryCheckRangeSshortSlong8(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (int16_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+} /*-- TIFFReadDirEntrySshort() --*/
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryFloat(TIFF* tif, TIFFDirEntry* direntry, float* value)
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryLong(TIFF *tif, TIFFDirEntry *direntry, uint32_t *value)
 {
-	enum TIFFReadDirEntryErr err;
-	if (direntry->tdir_count!=1)
-		return(TIFFReadDirEntryErrCount);
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-			{
-				uint8 m;
-				TIFFReadDirEntryCheckedByte(tif,direntry,&m);
-				*value=(float)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SBYTE:
-			{
-				int8 m;
-				TIFFReadDirEntryCheckedSbyte(tif,direntry,&m);
-				*value=(float)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SHORT:
-			{
-				uint16 m;
-				TIFFReadDirEntryCheckedShort(tif,direntry,&m);
-				*value=(float)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SSHORT:
-			{
-				int16 m;
-				TIFFReadDirEntryCheckedSshort(tif,direntry,&m);
-				*value=(float)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_LONG:
-			{
-				uint32 m;
-				TIFFReadDirEntryCheckedLong(tif,direntry,&m);
-				*value=(float)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SLONG:
-			{
-				int32 m;
-				TIFFReadDirEntryCheckedSlong(tif,direntry,&m);
-				*value=(float)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_LONG8:
-			{
-				uint64 m;
-				err=TIFFReadDirEntryCheckedLong8(tif,direntry,&m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
+    enum TIFFReadDirEntryErr err;
+    if (direntry->tdir_count != 1)
+        return (TIFFReadDirEntryErrCount);
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        {
+            uint8_t m;
+            TIFFReadDirEntryCheckedByte(tif, direntry, &m);
+            *value = (uint32_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SBYTE:
+        {
+            int8_t m;
+            TIFFReadDirEntryCheckedSbyte(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeLongSbyte(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint32_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SHORT:
+        {
+            uint16_t m;
+            TIFFReadDirEntryCheckedShort(tif, direntry, &m);
+            *value = (uint32_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SSHORT:
+        {
+            int16_t m;
+            TIFFReadDirEntryCheckedSshort(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeLongSshort(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint32_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG:
+            TIFFReadDirEntryCheckedLong(tif, direntry, value);
+            return (TIFFReadDirEntryErrOk);
+        case TIFF_SLONG:
+        {
+            int32_t m;
+            TIFFReadDirEntryCheckedSlong(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeLongSlong(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint32_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG8:
+        {
+            uint64_t m;
+            err = TIFFReadDirEntryCheckedLong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            err = TIFFReadDirEntryCheckRangeLongLong8(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint32_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG8:
+        {
+            int64_t m;
+            err = TIFFReadDirEntryCheckedSlong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            err = TIFFReadDirEntryCheckRangeLongSlong8(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint32_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+} /*-- TIFFReadDirEntryLong() --*/
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntrySlong(TIFF *tif, TIFFDirEntry *direntry, int32_t *value)
+{
+    enum TIFFReadDirEntryErr err;
+    if (direntry->tdir_count != 1)
+        return (TIFFReadDirEntryErrCount);
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        {
+            uint8_t m;
+            TIFFReadDirEntryCheckedByte(tif, direntry, &m);
+            *value = (int32_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SBYTE:
+        {
+            int8_t m;
+            TIFFReadDirEntryCheckedSbyte(tif, direntry, &m);
+            *value = (int32_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SHORT:
+        {
+            uint16_t m;
+            TIFFReadDirEntryCheckedShort(tif, direntry, &m);
+            *value = (int32_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SSHORT:
+        {
+            int16_t m;
+            TIFFReadDirEntryCheckedSshort(tif, direntry, &m);
+            *value = (int32_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG:
+        {
+            uint32_t m;
+            TIFFReadDirEntryCheckedLong(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeSlongLong(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (int32_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG:
+            TIFFReadDirEntryCheckedSlong(tif, direntry, value);
+            return (TIFFReadDirEntryErrOk);
+        case TIFF_LONG8:
+        {
+            uint64_t m;
+            err = TIFFReadDirEntryCheckedLong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            err = TIFFReadDirEntryCheckRangeSlongLong8(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (int32_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG8:
+        {
+            int64_t m;
+            err = TIFFReadDirEntryCheckedSlong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            err = TIFFReadDirEntryCheckRangeSlongSlong8(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (int32_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+} /*-- TIFFReadDirEntrySlong() --*/
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryLong8(TIFF *tif, TIFFDirEntry *direntry, uint64_t *value)
+{
+    enum TIFFReadDirEntryErr err;
+    if (direntry->tdir_count != 1)
+        return (TIFFReadDirEntryErrCount);
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        {
+            uint8_t m;
+            TIFFReadDirEntryCheckedByte(tif, direntry, &m);
+            *value = (uint64_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SBYTE:
+        {
+            int8_t m;
+            TIFFReadDirEntryCheckedSbyte(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeLong8Sbyte(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint64_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SHORT:
+        {
+            uint16_t m;
+            TIFFReadDirEntryCheckedShort(tif, direntry, &m);
+            *value = (uint64_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SSHORT:
+        {
+            int16_t m;
+            TIFFReadDirEntryCheckedSshort(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeLong8Sshort(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint64_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG:
+        {
+            uint32_t m;
+            TIFFReadDirEntryCheckedLong(tif, direntry, &m);
+            *value = (uint64_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG:
+        {
+            int32_t m;
+            TIFFReadDirEntryCheckedSlong(tif, direntry, &m);
+            err = TIFFReadDirEntryCheckRangeLong8Slong(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint64_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG8:
+            err = TIFFReadDirEntryCheckedLong8(tif, direntry, value);
+            return (err);
+        case TIFF_SLONG8:
+        {
+            int64_t m;
+            err = TIFFReadDirEntryCheckedSlong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            err = TIFFReadDirEntryCheckRangeLong8Slong8(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (uint64_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+} /*-- TIFFReadDirEntryLong8() --*/
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntrySlong8(TIFF *tif, TIFFDirEntry *direntry, int64_t *value)
+{
+    enum TIFFReadDirEntryErr err;
+    if (direntry->tdir_count != 1)
+        return (TIFFReadDirEntryErrCount);
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        {
+            uint8_t m;
+            TIFFReadDirEntryCheckedByte(tif, direntry, &m);
+            *value = (int64_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SBYTE:
+        {
+            int8_t m;
+            TIFFReadDirEntryCheckedSbyte(tif, direntry, &m);
+            *value = (int64_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SHORT:
+        {
+            uint16_t m;
+            TIFFReadDirEntryCheckedShort(tif, direntry, &m);
+            *value = (int64_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SSHORT:
+        {
+            int16_t m;
+            TIFFReadDirEntryCheckedSshort(tif, direntry, &m);
+            *value = (int64_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG:
+        {
+            uint32_t m;
+            TIFFReadDirEntryCheckedLong(tif, direntry, &m);
+            *value = (int64_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG:
+        {
+            int32_t m;
+            TIFFReadDirEntryCheckedSlong(tif, direntry, &m);
+            *value = (int64_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG8:
+        {
+            uint64_t m;
+            err = TIFFReadDirEntryCheckedLong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            err = TIFFReadDirEntryCheckRangeSlong8Long8(m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (int64_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG8:
+            err = TIFFReadDirEntryCheckedSlong8(tif, direntry, value);
+            return (err);
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+} /*-- TIFFReadDirEntrySlong8() --*/
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryFloat(TIFF *tif, TIFFDirEntry *direntry, float *value)
+{
+    enum TIFFReadDirEntryErr err;
+    if (direntry->tdir_count != 1)
+        return (TIFFReadDirEntryErrCount);
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        {
+            uint8_t m;
+            TIFFReadDirEntryCheckedByte(tif, direntry, &m);
+            *value = (float)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SBYTE:
+        {
+            int8_t m;
+            TIFFReadDirEntryCheckedSbyte(tif, direntry, &m);
+            *value = (float)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SHORT:
+        {
+            uint16_t m;
+            TIFFReadDirEntryCheckedShort(tif, direntry, &m);
+            *value = (float)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SSHORT:
+        {
+            int16_t m;
+            TIFFReadDirEntryCheckedSshort(tif, direntry, &m);
+            *value = (float)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG:
+        {
+            uint32_t m;
+            TIFFReadDirEntryCheckedLong(tif, direntry, &m);
+            *value = (float)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG:
+        {
+            int32_t m;
+            TIFFReadDirEntryCheckedSlong(tif, direntry, &m);
+            *value = (float)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG8:
+        {
+            uint64_t m;
+            err = TIFFReadDirEntryCheckedLong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
 #if defined(__WIN32__) && (_MSC_VER < 1500)
-				/*
-				 * XXX: MSVC 6.0 does not support conversion
-				 * of 64-bit integers into floating point
-				 * values.
-				 */
-				*value = _TIFFUInt64ToFloat(m);
+            /*
+             * XXX: MSVC 6.0 does not support conversion
+             * of 64-bit integers into floating point
+             * values.
+             */
+            *value = _TIFFUInt64ToFloat(m);
 #else
-				*value=(float)m;
+            *value = (float)m;
 #endif
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SLONG8:
-			{
-				int64 m;
-				err=TIFFReadDirEntryCheckedSlong8(tif,direntry,&m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(float)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_RATIONAL:
-			{
-				double m;
-				err=TIFFReadDirEntryCheckedRational(tif,direntry,&m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(float)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SRATIONAL:
-			{
-				double m;
-				err=TIFFReadDirEntryCheckedSrational(tif,direntry,&m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(float)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_FLOAT:
-			TIFFReadDirEntryCheckedFloat(tif,direntry,value);
-			return(TIFFReadDirEntryErrOk);
-		case TIFF_DOUBLE:
-			{
-				double m;
-				err=TIFFReadDirEntryCheckedDouble(tif,direntry,&m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				if ((m > FLT_MAX) || (m < FLT_MIN))
-					return(TIFFReadDirEntryErrRange);
-				*value=(float)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG8:
+        {
+            int64_t m;
+            err = TIFFReadDirEntryCheckedSlong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (float)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_RATIONAL:
+        {
+            double m;
+            err = TIFFReadDirEntryCheckedRational(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (float)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SRATIONAL:
+        {
+            double m;
+            err = TIFFReadDirEntryCheckedSrational(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (float)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_FLOAT:
+            TIFFReadDirEntryCheckedFloat(tif, direntry, value);
+            return (TIFFReadDirEntryErrOk);
+        case TIFF_DOUBLE:
+        {
+            double m;
+            err = TIFFReadDirEntryCheckedDouble(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            if ((m > FLT_MAX) || (m < -FLT_MAX))
+                return (TIFFReadDirEntryErrRange);
+            *value = (float)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
 }
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryDouble(TIFF* tif, TIFFDirEntry* direntry, double* value)
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryDouble(TIFF *tif, TIFFDirEntry *direntry, double *value)
 {
-	enum TIFFReadDirEntryErr err;
-	if (direntry->tdir_count!=1)
-		return(TIFFReadDirEntryErrCount);
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-			{
-				uint8 m;
-				TIFFReadDirEntryCheckedByte(tif,direntry,&m);
-				*value=(double)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SBYTE:
-			{
-				int8 m;
-				TIFFReadDirEntryCheckedSbyte(tif,direntry,&m);
-				*value=(double)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SHORT:
-			{
-				uint16 m;
-				TIFFReadDirEntryCheckedShort(tif,direntry,&m);
-				*value=(double)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SSHORT:
-			{
-				int16 m;
-				TIFFReadDirEntryCheckedSshort(tif,direntry,&m);
-				*value=(double)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_LONG:
-			{
-				uint32 m;
-				TIFFReadDirEntryCheckedLong(tif,direntry,&m);
-				*value=(double)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SLONG:
-			{
-				int32 m;
-				TIFFReadDirEntryCheckedSlong(tif,direntry,&m);
-				*value=(double)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_LONG8:
-			{
-				uint64 m;
-				err=TIFFReadDirEntryCheckedLong8(tif,direntry,&m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
+    enum TIFFReadDirEntryErr err;
+    if (direntry->tdir_count != 1)
+        return (TIFFReadDirEntryErrCount);
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        {
+            uint8_t m;
+            TIFFReadDirEntryCheckedByte(tif, direntry, &m);
+            *value = (double)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SBYTE:
+        {
+            int8_t m;
+            TIFFReadDirEntryCheckedSbyte(tif, direntry, &m);
+            *value = (double)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SHORT:
+        {
+            uint16_t m;
+            TIFFReadDirEntryCheckedShort(tif, direntry, &m);
+            *value = (double)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SSHORT:
+        {
+            int16_t m;
+            TIFFReadDirEntryCheckedSshort(tif, direntry, &m);
+            *value = (double)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG:
+        {
+            uint32_t m;
+            TIFFReadDirEntryCheckedLong(tif, direntry, &m);
+            *value = (double)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG:
+        {
+            int32_t m;
+            TIFFReadDirEntryCheckedSlong(tif, direntry, &m);
+            *value = (double)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG8:
+        {
+            uint64_t m;
+            err = TIFFReadDirEntryCheckedLong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
 #if defined(__WIN32__) && (_MSC_VER < 1500)
-				/*
-				 * XXX: MSVC 6.0 does not support conversion
-				 * of 64-bit integers into floating point
-				 * values.
-				 */
-				*value = _TIFFUInt64ToDouble(m);
+            /*
+             * XXX: MSVC 6.0 does not support conversion
+             * of 64-bit integers into floating point
+             * values.
+             */
+            *value = _TIFFUInt64ToDouble(m);
 #else
-				*value = (double)m;
+            *value = (double)m;
 #endif
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SLONG8:
-			{
-				int64 m;
-				err=TIFFReadDirEntryCheckedSlong8(tif,direntry,&m);
-				if (err!=TIFFReadDirEntryErrOk)
-					return(err);
-				*value=(double)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_RATIONAL:
-			err=TIFFReadDirEntryCheckedRational(tif,direntry,value);
-			return(err);
-		case TIFF_SRATIONAL:
-			err=TIFFReadDirEntryCheckedSrational(tif,direntry,value);
-			return(err);
-		case TIFF_FLOAT:
-			{
-				float m;
-				TIFFReadDirEntryCheckedFloat(tif,direntry,&m);
-				*value=(double)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_DOUBLE:
-			err=TIFFReadDirEntryCheckedDouble(tif,direntry,value);
-			return(err);
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG8:
+        {
+            int64_t m;
+            err = TIFFReadDirEntryCheckedSlong8(tif, direntry, &m);
+            if (err != TIFFReadDirEntryErrOk)
+                return (err);
+            *value = (double)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_RATIONAL:
+            err = TIFFReadDirEntryCheckedRational(tif, direntry, value);
+            return (err);
+        case TIFF_SRATIONAL:
+            err = TIFFReadDirEntryCheckedSrational(tif, direntry, value);
+            return (err);
+        case TIFF_FLOAT:
+        {
+            float m;
+            TIFFReadDirEntryCheckedFloat(tif, direntry, &m);
+            *value = (double)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_DOUBLE:
+            err = TIFFReadDirEntryCheckedDouble(tif, direntry, value);
+            return (err);
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
 }
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryIfd8(TIFF* tif, TIFFDirEntry* direntry, uint64* value)
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryIfd8(TIFF *tif, TIFFDirEntry *direntry, uint64_t *value)
 {
-	enum TIFFReadDirEntryErr err;
-	if (direntry->tdir_count!=1)
-		return(TIFFReadDirEntryErrCount);
-	switch (direntry->tdir_type)
-	{
-		case TIFF_LONG:
-		case TIFF_IFD:
-			{
-				uint32 m;
-				TIFFReadDirEntryCheckedLong(tif,direntry,&m);
-				*value=(uint64)m;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_LONG8:
-		case TIFF_IFD8:
-			err=TIFFReadDirEntryCheckedLong8(tif,direntry,value);
-			return(err);
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
+    enum TIFFReadDirEntryErr err;
+    if (direntry->tdir_count != 1)
+        return (TIFFReadDirEntryErrCount);
+    switch (direntry->tdir_type)
+    {
+        case TIFF_LONG:
+        case TIFF_IFD:
+        {
+            uint32_t m;
+            TIFFReadDirEntryCheckedLong(tif, direntry, &m);
+            *value = (uint64_t)m;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_LONG8:
+        case TIFF_IFD8:
+            err = TIFFReadDirEntryCheckedLong8(tif, direntry, value);
+            return (err);
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
 }
 
-
 #define INITIAL_THRESHOLD (1024 * 1024)
 #define THRESHOLD_MULTIPLIER 10
-#define MAX_THRESHOLD (THRESHOLD_MULTIPLIER * THRESHOLD_MULTIPLIER * THRESHOLD_MULTIPLIER * INITIAL_THRESHOLD)
+#define MAX_THRESHOLD                                                          \
+    (THRESHOLD_MULTIPLIER * THRESHOLD_MULTIPLIER * THRESHOLD_MULTIPLIER *      \
+     INITIAL_THRESHOLD)
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryDataAndRealloc(
-                    TIFF* tif, uint64 offset, tmsize_t size, void** pdest)
+static enum TIFFReadDirEntryErr TIFFReadDirEntryDataAndRealloc(TIFF *tif,
+                                                               uint64_t offset,
+                                                               tmsize_t size,
+                                                               void **pdest)
 {
 #if SIZEOF_SIZE_T == 8
-        tmsize_t threshold = INITIAL_THRESHOLD;
+    tmsize_t threshold = INITIAL_THRESHOLD;
 #endif
-        tmsize_t already_read = 0;
+    tmsize_t already_read = 0;
 
-        assert( !isMapped(tif) );
+    assert(!isMapped(tif));
 
-        if (!SeekOK(tif,offset))
-                return(TIFFReadDirEntryErrIo);
+    if (!SeekOK(tif, offset))
+        return (TIFFReadDirEntryErrIo);
 
-        /* On 64 bit processes, read first a maximum of 1 MB, then 10 MB, etc */
-        /* so as to avoid allocating too much memory in case the file is too */
-        /* short. We could ask for the file size, but this might be */
-        /* expensive with some I/O layers (think of reading a gzipped file) */
-        /* Restrict to 64 bit processes, so as to avoid reallocs() */
-        /* on 32 bit processes where virtual memory is scarce.  */
-        while( already_read < size )
-        {
-            void* new_dest;
-            tmsize_t bytes_read;
-            tmsize_t to_read = size - already_read;
+    /* On 64 bit processes, read first a maximum of 1 MB, then 10 MB, etc */
+    /* so as to avoid allocating too much memory in case the file is too */
+    /* short. We could ask for the file size, but this might be */
+    /* expensive with some I/O layers (think of reading a gzipped file) */
+    /* Restrict to 64 bit processes, so as to avoid reallocs() */
+    /* on 32 bit processes where virtual memory is scarce.  */
+    while (already_read < size)
+    {
+        void *new_dest;
+        tmsize_t bytes_read;
+        tmsize_t to_read = size - already_read;
 #if SIZEOF_SIZE_T == 8
-            if( to_read >= threshold && threshold < MAX_THRESHOLD )
-            {
-                to_read = threshold;
-                threshold *= THRESHOLD_MULTIPLIER;
-            }
+        if (to_read >= threshold && threshold < MAX_THRESHOLD)
+        {
+            to_read = threshold;
+            threshold *= THRESHOLD_MULTIPLIER;
+        }
 #endif
 
-            new_dest = (uint8*) _TIFFrealloc(
-                            *pdest, already_read + to_read);
-            if( new_dest == NULL )
-            {
-                TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-                            "Failed to allocate memory for %s "
-                            "(%ld elements of %ld bytes each)",
-                            "TIFFReadDirEntryArray",
-                             (long) 1, (long) (already_read + to_read));
-                return TIFFReadDirEntryErrAlloc;
-            }
-            *pdest = new_dest;
+        new_dest =
+            (uint8_t *)_TIFFreallocExt(tif, *pdest, already_read + to_read);
+        if (new_dest == NULL)
+        {
+            TIFFErrorExtR(tif, tif->tif_name,
+                          "Failed to allocate memory for %s "
+                          "(%" TIFF_SSIZE_FORMAT
+                          " elements of %" TIFF_SSIZE_FORMAT " bytes each)",
+                          "TIFFReadDirEntryArray", (tmsize_t)1,
+                          already_read + to_read);
+            return TIFFReadDirEntryErrAlloc;
+        }
+        *pdest = new_dest;
 
-            bytes_read = TIFFReadFile(tif,
-                (char*)*pdest + already_read, to_read);
-            already_read += bytes_read;
-            if (bytes_read != to_read) {
-                return TIFFReadDirEntryErrIo;
+        bytes_read = TIFFReadFile(tif, (char *)*pdest + already_read, to_read);
+        already_read += bytes_read;
+        if (bytes_read != to_read)
+        {
+            return TIFFReadDirEntryErrIo;
+        }
+    }
+    return TIFFReadDirEntryErrOk;
+}
+
+/* Caution: if raising that value, make sure int32 / uint32 overflows can't
+ * occur elsewhere */
+#define MAX_SIZE_TAG_DATA 2147483647U
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryArrayWithLimit(TIFF *tif, TIFFDirEntry *direntry,
+                               uint32_t *count, uint32_t desttypesize,
+                               void **value, uint64_t maxcount)
+{
+    int typesize;
+    uint32_t datasize;
+    void *data;
+    uint64_t target_count64;
+    int original_datasize_clamped;
+    typesize = TIFFDataWidth(direntry->tdir_type);
+
+    target_count64 =
+        (direntry->tdir_count > maxcount) ? maxcount : direntry->tdir_count;
+
+    if ((target_count64 == 0) || (typesize == 0))
+    {
+        *value = 0;
+        return (TIFFReadDirEntryErrOk);
+    }
+    (void)desttypesize;
+
+    /* We just want to know if the original tag size is more than 4 bytes
+     * (classic TIFF) or 8 bytes (BigTIFF)
+     */
+    original_datasize_clamped =
+        ((direntry->tdir_count > 10) ? 10 : (int)direntry->tdir_count) *
+        typesize;
+
+    /*
+     * As a sanity check, make sure we have no more than a 2GB tag array
+     * in either the current data type or the dest data type.  This also
+     * avoids problems with overflow of tmsize_t on 32bit systems.
+     */
+    if ((uint64_t)(MAX_SIZE_TAG_DATA / typesize) < target_count64)
+        return (TIFFReadDirEntryErrSizesan);
+    if ((uint64_t)(MAX_SIZE_TAG_DATA / desttypesize) < target_count64)
+        return (TIFFReadDirEntryErrSizesan);
+
+    *count = (uint32_t)target_count64;
+    datasize = (*count) * typesize;
+    assert((tmsize_t)datasize > 0);
+
+    if (isMapped(tif) && datasize > (uint64_t)tif->tif_size)
+        return TIFFReadDirEntryErrIo;
+
+    if (!isMapped(tif) && (((tif->tif_flags & TIFF_BIGTIFF) && datasize > 8) ||
+                           (!(tif->tif_flags & TIFF_BIGTIFF) && datasize > 4)))
+    {
+        data = NULL;
+    }
+    else
+    {
+        data = _TIFFCheckMalloc(tif, *count, typesize, "ReadDirEntryArray");
+        if (data == 0)
+            return (TIFFReadDirEntryErrAlloc);
+    }
+    if (!(tif->tif_flags & TIFF_BIGTIFF))
+    {
+        /* Only the condition on original_datasize_clamped. The second
+         * one is implied, but Coverity Scan cannot see it. */
+        if (original_datasize_clamped <= 4 && datasize <= 4)
+            _TIFFmemcpy(data, &direntry->tdir_offset, datasize);
+        else
+        {
+            enum TIFFReadDirEntryErr err;
+            uint32_t offset = direntry->tdir_offset.toff_long;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong(&offset);
+            if (isMapped(tif))
+                err = TIFFReadDirEntryData(tif, (uint64_t)offset,
+                                           (tmsize_t)datasize, data);
+            else
+                err = TIFFReadDirEntryDataAndRealloc(tif, (uint64_t)offset,
+                                                     (tmsize_t)datasize, &data);
+            if (err != TIFFReadDirEntryErrOk)
+            {
+                _TIFFfreeExt(tif, data);
+                return (err);
             }
         }
-        return TIFFReadDirEntryErrOk;
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryArrayWithLimit(
-    TIFF* tif, TIFFDirEntry* direntry, uint32* count, uint32 desttypesize,
-    void** value, uint64 maxcount)
-{
-	int typesize;
-	uint32 datasize;
-	void* data;
-        uint64 target_count64;
-	typesize=TIFFDataWidth(direntry->tdir_type);
-
-        target_count64 = (direntry->tdir_count > maxcount) ?
-                maxcount : direntry->tdir_count;
-
-	if ((target_count64==0)||(typesize==0))
-	{
-		*value=0;
-		return(TIFFReadDirEntryErrOk);
-	}
-        (void) desttypesize;
-
-        /* 
-         * As a sanity check, make sure we have no more than a 2GB tag array 
-         * in either the current data type or the dest data type.  This also
-         * avoids problems with overflow of tmsize_t on 32bit systems.
-         */
-	if ((uint64)(2147483647/typesize)<target_count64)
-		return(TIFFReadDirEntryErrSizesan);
-	if ((uint64)(2147483647/desttypesize)<target_count64)
-		return(TIFFReadDirEntryErrSizesan);
-
-	*count=(uint32)target_count64;
-	datasize=(*count)*typesize;
-	assert((tmsize_t)datasize>0);
-
-	if( isMapped(tif) && datasize > (uint32)tif->tif_size )
-		return TIFFReadDirEntryErrIo;
-
-	if( !isMapped(tif) &&
-		(((tif->tif_flags&TIFF_BIGTIFF) && datasize > 8) ||
-		(!(tif->tif_flags&TIFF_BIGTIFF) && datasize > 4)) )
-	{
-		data = NULL;
-	}
-	else
-	{
-		data=_TIFFCheckMalloc(tif, *count, typesize, "ReadDirEntryArray");
-		if (data==0)
-			return(TIFFReadDirEntryErrAlloc);
-	}
-	if (!(tif->tif_flags&TIFF_BIGTIFF))
-	{
-		if (datasize<=4)
-			_TIFFmemcpy(data,&direntry->tdir_offset,datasize);
-		else
-		{
-			enum TIFFReadDirEntryErr err;
-			uint32 offset = direntry->tdir_offset.toff_long;
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabLong(&offset);
-			if( isMapped(tif) )
-				err=TIFFReadDirEntryData(tif,(uint64)offset,(tmsize_t)datasize,data);
-			else
-				err=TIFFReadDirEntryDataAndRealloc(tif,(uint64)offset,(tmsize_t)datasize,&data);
-			if (err!=TIFFReadDirEntryErrOk)
-			{
-				_TIFFfree(data);
-				return(err);
-			}
-		}
-	}
-	else
-	{
-		if (datasize<=8)
-			_TIFFmemcpy(data,&direntry->tdir_offset,datasize);
-		else
-		{
-			enum TIFFReadDirEntryErr err;
-			uint64 offset = direntry->tdir_offset.toff_long8;
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabLong8(&offset);
-			if( isMapped(tif) )
-				err=TIFFReadDirEntryData(tif,(uint64)offset,(tmsize_t)datasize,data);
-			else
-				err=TIFFReadDirEntryDataAndRealloc(tif,(uint64)offset,(tmsize_t)datasize,&data);
-			if (err!=TIFFReadDirEntryErrOk)
-			{
-				_TIFFfree(data);
-				return(err);
-			}
-		}
-	}
-	*value=data;
-	return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryArray(TIFF* tif, TIFFDirEntry* direntry, uint32* count, uint32 desttypesize, void** value)
-{
-    return TIFFReadDirEntryArrayWithLimit(tif, direntry, count,
-                                          desttypesize, value, ~((uint64)0));
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryByteArray(TIFF* tif, TIFFDirEntry* direntry, uint8** value)
-{
-	enum TIFFReadDirEntryErr err;
-	uint32 count;
-	void* origdata;
-	uint8* data;
-	switch (direntry->tdir_type)
-	{
-		case TIFF_ASCII:
-		case TIFF_UNDEFINED:
-		case TIFF_BYTE:
-		case TIFF_SBYTE:
-		case TIFF_SHORT:
-		case TIFF_SSHORT:
-		case TIFF_LONG:
-		case TIFF_SLONG:
-		case TIFF_LONG8:
-		case TIFF_SLONG8:
-			break;
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
-	err=TIFFReadDirEntryArray(tif,direntry,&count,1,&origdata);
-	if ((err!=TIFFReadDirEntryErrOk)||(origdata==0))
-	{
-		*value=0;
-		return(err);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_ASCII:
-		case TIFF_UNDEFINED:
-		case TIFF_BYTE:
-			*value=(uint8*)origdata;
-			return(TIFFReadDirEntryErrOk);
-		case TIFF_SBYTE:
-			{
-				int8* m;
-				uint32 n;
-				m=(int8*)origdata;
-				for (n=0; n<count; n++)
-				{
-					err=TIFFReadDirEntryCheckRangeByteSbyte(*m);
-					if (err!=TIFFReadDirEntryErrOk)
-					{
-						_TIFFfree(origdata);
-						return(err);
-					}
-					m++;
-				}
-				*value=(uint8*)origdata;
-				return(TIFFReadDirEntryErrOk);
-			}
-	}
-	data=(uint8*)_TIFFmalloc(count);
-	if (data==0)
-	{
-		_TIFFfree(origdata);
-		return(TIFFReadDirEntryErrAlloc);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_SHORT:
-			{
-				uint16* ma;
-				uint8* mb;
-				uint32 n;
-				ma=(uint16*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort(ma);
-					err=TIFFReadDirEntryCheckRangeByteShort(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint8)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SSHORT:
-			{
-				int16* ma;
-				uint8* mb;
-				uint32 n;
-				ma=(int16*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort((uint16*)ma);
-					err=TIFFReadDirEntryCheckRangeByteSshort(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint8)(*ma++);
-				}
-			}
-			break;
-		case TIFF_LONG:
-			{
-				uint32* ma;
-				uint8* mb;
-				uint32 n;
-				ma=(uint32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong(ma);
-					err=TIFFReadDirEntryCheckRangeByteLong(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint8)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SLONG:
-			{
-				int32* ma;
-				uint8* mb;
-				uint32 n;
-				ma=(int32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong((uint32*)ma);
-					err=TIFFReadDirEntryCheckRangeByteSlong(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint8)(*ma++);
-				}
-			}
-			break;
-		case TIFF_LONG8:
-			{
-				uint64* ma;
-				uint8* mb;
-				uint32 n;
-				ma=(uint64*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8(ma);
-					err=TIFFReadDirEntryCheckRangeByteLong8(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint8)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SLONG8:
-			{
-				int64* ma;
-				uint8* mb;
-				uint32 n;
-				ma=(int64*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8((uint64*)ma);
-					err=TIFFReadDirEntryCheckRangeByteSlong8(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint8)(*ma++);
-				}
-			}
-			break;
-	}
-	_TIFFfree(origdata);
-	if (err!=TIFFReadDirEntryErrOk)
-	{
-		_TIFFfree(data);
-		return(err);
-	}
-	*value=data;
-	return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntrySbyteArray(TIFF* tif, TIFFDirEntry* direntry, int8** value)
-{
-	enum TIFFReadDirEntryErr err;
-	uint32 count;
-	void* origdata;
-	int8* data;
-	switch (direntry->tdir_type)
-	{
-		case TIFF_UNDEFINED:
-		case TIFF_BYTE:
-		case TIFF_SBYTE:
-		case TIFF_SHORT:
-		case TIFF_SSHORT:
-		case TIFF_LONG:
-		case TIFF_SLONG:
-		case TIFF_LONG8:
-		case TIFF_SLONG8:
-			break;
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
-	err=TIFFReadDirEntryArray(tif,direntry,&count,1,&origdata);
-	if ((err!=TIFFReadDirEntryErrOk)||(origdata==0))
-	{
-		*value=0;
-		return(err);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_UNDEFINED:
-		case TIFF_BYTE:
-			{
-				uint8* m;
-				uint32 n;
-				m=(uint8*)origdata;
-				for (n=0; n<count; n++)
-				{
-					err=TIFFReadDirEntryCheckRangeSbyteByte(*m);
-					if (err!=TIFFReadDirEntryErrOk)
-					{
-						_TIFFfree(origdata);
-						return(err);
-					}
-					m++;
-				}
-				*value=(int8*)origdata;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SBYTE:
-			*value=(int8*)origdata;
-			return(TIFFReadDirEntryErrOk);
-	}
-	data=(int8*)_TIFFmalloc(count);
-	if (data==0)
-	{
-		_TIFFfree(origdata);
-		return(TIFFReadDirEntryErrAlloc);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_SHORT:
-			{
-				uint16* ma;
-				int8* mb;
-				uint32 n;
-				ma=(uint16*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort(ma);
-					err=TIFFReadDirEntryCheckRangeSbyteShort(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(int8)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SSHORT:
-			{
-				int16* ma;
-				int8* mb;
-				uint32 n;
-				ma=(int16*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort((uint16*)ma);
-					err=TIFFReadDirEntryCheckRangeSbyteSshort(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(int8)(*ma++);
-				}
-			}
-			break;
-		case TIFF_LONG:
-			{
-				uint32* ma;
-				int8* mb;
-				uint32 n;
-				ma=(uint32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong(ma);
-					err=TIFFReadDirEntryCheckRangeSbyteLong(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(int8)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SLONG:
-			{
-				int32* ma;
-				int8* mb;
-				uint32 n;
-				ma=(int32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong((uint32*)ma);
-					err=TIFFReadDirEntryCheckRangeSbyteSlong(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(int8)(*ma++);
-				}
-			}
-			break;
-		case TIFF_LONG8:
-			{
-				uint64* ma;
-				int8* mb;
-				uint32 n;
-				ma=(uint64*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8(ma);
-					err=TIFFReadDirEntryCheckRangeSbyteLong8(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(int8)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SLONG8:
-			{
-				int64* ma;
-				int8* mb;
-				uint32 n;
-				ma=(int64*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8((uint64*)ma);
-					err=TIFFReadDirEntryCheckRangeSbyteSlong8(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(int8)(*ma++);
-				}
-			}
-			break;
-	}
-	_TIFFfree(origdata);
-	if (err!=TIFFReadDirEntryErrOk)
-	{
-		_TIFFfree(data);
-		return(err);
-	}
-	*value=data;
-	return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryShortArray(TIFF* tif, TIFFDirEntry* direntry, uint16** value)
-{
-	enum TIFFReadDirEntryErr err;
-	uint32 count;
-	void* origdata;
-	uint16* data;
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-		case TIFF_SBYTE:
-		case TIFF_SHORT:
-		case TIFF_SSHORT:
-		case TIFF_LONG:
-		case TIFF_SLONG:
-		case TIFF_LONG8:
-		case TIFF_SLONG8:
-			break;
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
-	err=TIFFReadDirEntryArray(tif,direntry,&count,2,&origdata);
-	if ((err!=TIFFReadDirEntryErrOk)||(origdata==0))
-	{
-		*value=0;
-		return(err);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_SHORT:
-			*value=(uint16*)origdata;
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabArrayOfShort(*value,count);  
-			return(TIFFReadDirEntryErrOk);
-		case TIFF_SSHORT:
-			{
-				int16* m;
-				uint32 n;
-				m=(int16*)origdata;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort((uint16*)m);
-					err=TIFFReadDirEntryCheckRangeShortSshort(*m);
-					if (err!=TIFFReadDirEntryErrOk)
-					{
-						_TIFFfree(origdata);
-						return(err);
-					}
-					m++;
-				}
-				*value=(uint16*)origdata;
-				return(TIFFReadDirEntryErrOk);
-			}
-	}
-	data=(uint16*)_TIFFmalloc(count*2);
-	if (data==0)
-	{
-		_TIFFfree(origdata);
-		return(TIFFReadDirEntryErrAlloc);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-			{
-				uint8* ma;
-				uint16* mb;
-				uint32 n;
-				ma=(uint8*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-					*mb++=(uint16)(*ma++);
-			}
-			break;
-		case TIFF_SBYTE:
-			{
-				int8* ma;
-				uint16* mb;
-				uint32 n;
-				ma=(int8*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					err=TIFFReadDirEntryCheckRangeShortSbyte(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint16)(*ma++);
-				}
-			}
-			break;
-		case TIFF_LONG:
-			{
-				uint32* ma;
-				uint16* mb;
-				uint32 n;
-				ma=(uint32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong(ma);
-					err=TIFFReadDirEntryCheckRangeShortLong(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint16)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SLONG:
-			{
-				int32* ma;
-				uint16* mb;
-				uint32 n;
-				ma=(int32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong((uint32*)ma);
-					err=TIFFReadDirEntryCheckRangeShortSlong(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint16)(*ma++);
-				}
-			}
-			break;
-		case TIFF_LONG8:
-			{
-				uint64* ma;
-				uint16* mb;
-				uint32 n;
-				ma=(uint64*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8(ma);
-					err=TIFFReadDirEntryCheckRangeShortLong8(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint16)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SLONG8:
-			{
-				int64* ma;
-				uint16* mb;
-				uint32 n;
-				ma=(int64*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8((uint64*)ma);
-					err=TIFFReadDirEntryCheckRangeShortSlong8(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint16)(*ma++);
-				}
-			}
-			break;
-	}
-	_TIFFfree(origdata);
-	if (err!=TIFFReadDirEntryErrOk)
-	{
-		_TIFFfree(data);
-		return(err);
-	}
-	*value=data;
-	return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntrySshortArray(TIFF* tif, TIFFDirEntry* direntry, int16** value)
-{
-	enum TIFFReadDirEntryErr err;
-	uint32 count;
-	void* origdata;
-	int16* data;
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-		case TIFF_SBYTE:
-		case TIFF_SHORT:
-		case TIFF_SSHORT:
-		case TIFF_LONG:
-		case TIFF_SLONG:
-		case TIFF_LONG8:
-		case TIFF_SLONG8:
-			break;
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
-	err=TIFFReadDirEntryArray(tif,direntry,&count,2,&origdata);
-	if ((err!=TIFFReadDirEntryErrOk)||(origdata==0))
-	{
-		*value=0;
-		return(err);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_SHORT:
-			{
-				uint16* m;
-				uint32 n;
-				m=(uint16*)origdata;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort(m);
-					err=TIFFReadDirEntryCheckRangeSshortShort(*m);
-					if (err!=TIFFReadDirEntryErrOk)
-					{
-						_TIFFfree(origdata);
-						return(err);
-					}
-					m++;
-				}
-				*value=(int16*)origdata;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SSHORT:
-			*value=(int16*)origdata;
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabArrayOfShort((uint16*)(*value),count);
-			return(TIFFReadDirEntryErrOk);
-	}
-	data=(int16*)_TIFFmalloc(count*2);
-	if (data==0)
-	{
-		_TIFFfree(origdata);
-		return(TIFFReadDirEntryErrAlloc);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-			{
-				uint8* ma;
-				int16* mb;
-				uint32 n;
-				ma=(uint8*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-					*mb++=(int16)(*ma++);
-			}
-			break;
-		case TIFF_SBYTE:
-			{
-				int8* ma;
-				int16* mb;
-				uint32 n;
-				ma=(int8*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-					*mb++=(int16)(*ma++);
-			}
-			break;
-		case TIFF_LONG:
-			{
-				uint32* ma;
-				int16* mb;
-				uint32 n;
-				ma=(uint32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong(ma);
-					err=TIFFReadDirEntryCheckRangeSshortLong(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(int16)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SLONG:
-			{
-				int32* ma;
-				int16* mb;
-				uint32 n;
-				ma=(int32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong((uint32*)ma);
-					err=TIFFReadDirEntryCheckRangeSshortSlong(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(int16)(*ma++);
-				}
-			}
-			break;
-		case TIFF_LONG8:
-			{
-				uint64* ma;
-				int16* mb;
-				uint32 n;
-				ma=(uint64*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8(ma);
-					err=TIFFReadDirEntryCheckRangeSshortLong8(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(int16)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SLONG8:
-			{
-				int64* ma;
-				int16* mb;
-				uint32 n;
-				ma=(int64*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8((uint64*)ma);
-					err=TIFFReadDirEntryCheckRangeSshortSlong8(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(int16)(*ma++);
-				}
-			}
-			break;
-	}
-	_TIFFfree(origdata);
-	if (err!=TIFFReadDirEntryErrOk)
-	{
-		_TIFFfree(data);
-		return(err);
-	}
-	*value=data;
-	return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryLongArray(TIFF* tif, TIFFDirEntry* direntry, uint32** value)
-{
-	enum TIFFReadDirEntryErr err;
-	uint32 count;
-	void* origdata;
-	uint32* data;
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-		case TIFF_SBYTE:
-		case TIFF_SHORT:
-		case TIFF_SSHORT:
-		case TIFF_LONG:
-		case TIFF_SLONG:
-		case TIFF_LONG8:
-		case TIFF_SLONG8:
-			break;
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
-	err=TIFFReadDirEntryArray(tif,direntry,&count,4,&origdata);
-	if ((err!=TIFFReadDirEntryErrOk)||(origdata==0))
-	{
-		*value=0;
-		return(err);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_LONG:
-			*value=(uint32*)origdata;
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabArrayOfLong(*value,count);
-			return(TIFFReadDirEntryErrOk);
-		case TIFF_SLONG:
-			{
-				int32* m;
-				uint32 n;
-				m=(int32*)origdata;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong((uint32*)m);
-					err=TIFFReadDirEntryCheckRangeLongSlong(*m);
-					if (err!=TIFFReadDirEntryErrOk)
-					{
-						_TIFFfree(origdata);
-						return(err);
-					}
-					m++;
-				}
-				*value=(uint32*)origdata;
-				return(TIFFReadDirEntryErrOk);
-			}
-	}
-	data=(uint32*)_TIFFmalloc(count*4);
-	if (data==0)
-	{
-		_TIFFfree(origdata);
-		return(TIFFReadDirEntryErrAlloc);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-			{
-				uint8* ma;
-				uint32* mb;
-				uint32 n;
-				ma=(uint8*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-					*mb++=(uint32)(*ma++);
-			}
-			break;
-		case TIFF_SBYTE:
-			{
-				int8* ma;
-				uint32* mb;
-				uint32 n;
-				ma=(int8*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					err=TIFFReadDirEntryCheckRangeLongSbyte(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint32)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SHORT:
-			{
-				uint16* ma;
-				uint32* mb;
-				uint32 n;
-				ma=(uint16*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort(ma);
-					*mb++=(uint32)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SSHORT:
-			{
-				int16* ma;
-				uint32* mb;
-				uint32 n;
-				ma=(int16*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort((uint16*)ma);
-					err=TIFFReadDirEntryCheckRangeLongSshort(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint32)(*ma++);
-				}
-			}
-			break;
-		case TIFF_LONG8:
-			{
-				uint64* ma;
-				uint32* mb;
-				uint32 n;
-				ma=(uint64*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8(ma);
-					err=TIFFReadDirEntryCheckRangeLongLong8(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint32)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SLONG8:
-			{
-				int64* ma;
-				uint32* mb;
-				uint32 n;
-				ma=(int64*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8((uint64*)ma);
-					err=TIFFReadDirEntryCheckRangeLongSlong8(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint32)(*ma++);
-				}
-			}
-			break;
-	}
-	_TIFFfree(origdata);
-	if (err!=TIFFReadDirEntryErrOk)
-	{
-		_TIFFfree(data);
-		return(err);
-	}
-	*value=data;
-	return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntrySlongArray(TIFF* tif, TIFFDirEntry* direntry, int32** value)
-{
-	enum TIFFReadDirEntryErr err;
-	uint32 count;
-	void* origdata;
-	int32* data;
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-		case TIFF_SBYTE:
-		case TIFF_SHORT:
-		case TIFF_SSHORT:
-		case TIFF_LONG:
-		case TIFF_SLONG:
-		case TIFF_LONG8:
-		case TIFF_SLONG8:
-			break;
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
-	err=TIFFReadDirEntryArray(tif,direntry,&count,4,&origdata);
-	if ((err!=TIFFReadDirEntryErrOk)||(origdata==0))
-	{
-		*value=0;
-		return(err);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_LONG:
-			{
-				uint32* m;
-				uint32 n;
-				m=(uint32*)origdata;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong((uint32*)m);
-					err=TIFFReadDirEntryCheckRangeSlongLong(*m);
-					if (err!=TIFFReadDirEntryErrOk)
-					{
-						_TIFFfree(origdata);
-						return(err);
-					}
-					m++;
-				}
-				*value=(int32*)origdata;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SLONG:
-			*value=(int32*)origdata;
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabArrayOfLong((uint32*)(*value),count);
-			return(TIFFReadDirEntryErrOk);
-	}
-	data=(int32*)_TIFFmalloc(count*4);
-	if (data==0)
-	{
-		_TIFFfree(origdata);
-		return(TIFFReadDirEntryErrAlloc);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-			{
-				uint8* ma;
-				int32* mb;
-				uint32 n;
-				ma=(uint8*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-					*mb++=(int32)(*ma++);
-			}
-			break;
-		case TIFF_SBYTE:
-			{
-				int8* ma;
-				int32* mb;
-				uint32 n;
-				ma=(int8*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-					*mb++=(int32)(*ma++);
-			}
-			break;
-		case TIFF_SHORT:
-			{
-				uint16* ma;
-				int32* mb;
-				uint32 n;
-				ma=(uint16*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort(ma);
-					*mb++=(int32)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SSHORT:
-			{
-				int16* ma;
-				int32* mb;
-				uint32 n;
-				ma=(int16*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort((uint16*)ma);
-					*mb++=(int32)(*ma++);
-				}
-			}
-			break;
-		case TIFF_LONG8:
-			{
-				uint64* ma;
-				int32* mb;
-				uint32 n;
-				ma=(uint64*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8(ma);
-					err=TIFFReadDirEntryCheckRangeSlongLong8(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(int32)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SLONG8:
-			{
-				int64* ma;
-				int32* mb;
-				uint32 n;
-				ma=(int64*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8((uint64*)ma);
-					err=TIFFReadDirEntryCheckRangeSlongSlong8(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(int32)(*ma++);
-				}
-			}
-			break;
-	}
-	_TIFFfree(origdata);
-	if (err!=TIFFReadDirEntryErrOk)
-	{
-		_TIFFfree(data);
-		return(err);
-	}
-	*value=data;
-	return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryLong8ArrayWithLimit(
-        TIFF* tif, TIFFDirEntry* direntry, uint64** value, uint64 maxcount)
-{
-	enum TIFFReadDirEntryErr err;
-	uint32 count;
-	void* origdata;
-	uint64* data;
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-		case TIFF_SBYTE:
-		case TIFF_SHORT:
-		case TIFF_SSHORT:
-		case TIFF_LONG:
-		case TIFF_SLONG:
-		case TIFF_LONG8:
-		case TIFF_SLONG8:
-			break;
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
-	err=TIFFReadDirEntryArrayWithLimit(tif,direntry,&count,8,&origdata,maxcount);
-	if ((err!=TIFFReadDirEntryErrOk)||(origdata==0))
-	{
-		*value=0;
-		return(err);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_LONG8:
-			*value=(uint64*)origdata;
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabArrayOfLong8(*value,count);
-			return(TIFFReadDirEntryErrOk);
-		case TIFF_SLONG8:
-			{
-				int64* m;
-				uint32 n;
-				m=(int64*)origdata;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8((uint64*)m);
-					err=TIFFReadDirEntryCheckRangeLong8Slong8(*m);
-					if (err!=TIFFReadDirEntryErrOk)
-					{
-						_TIFFfree(origdata);
-						return(err);
-					}
-					m++;
-				}
-				*value=(uint64*)origdata;
-				return(TIFFReadDirEntryErrOk);
-			}
-	}
-	data=(uint64*)_TIFFmalloc(count*8);
-	if (data==0)
-	{
-		_TIFFfree(origdata);
-		return(TIFFReadDirEntryErrAlloc);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-			{
-				uint8* ma;
-				uint64* mb;
-				uint32 n;
-				ma=(uint8*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-					*mb++=(uint64)(*ma++);
-			}
-			break;
-		case TIFF_SBYTE:
-			{
-				int8* ma;
-				uint64* mb;
-				uint32 n;
-				ma=(int8*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					err=TIFFReadDirEntryCheckRangeLong8Sbyte(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint64)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SHORT:
-			{
-				uint16* ma;
-				uint64* mb;
-				uint32 n;
-				ma=(uint16*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort(ma);
-					*mb++=(uint64)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SSHORT:
-			{
-				int16* ma;
-				uint64* mb;
-				uint32 n;
-				ma=(int16*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort((uint16*)ma);
-					err=TIFFReadDirEntryCheckRangeLong8Sshort(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint64)(*ma++);
-				}
-			}
-			break;
-		case TIFF_LONG:
-			{
-				uint32* ma;
-				uint64* mb;
-				uint32 n;
-				ma=(uint32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong(ma);
-					*mb++=(uint64)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SLONG:
-			{
-				int32* ma;
-				uint64* mb;
-				uint32 n;
-				ma=(int32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong((uint32*)ma);
-					err=TIFFReadDirEntryCheckRangeLong8Slong(*ma);
-					if (err!=TIFFReadDirEntryErrOk)
-						break;
-					*mb++=(uint64)(*ma++);
-				}
-			}
-			break;
-	}
-	_TIFFfree(origdata);
-	if (err!=TIFFReadDirEntryErrOk)
-	{
-		_TIFFfree(data);
-		return(err);
-	}
-	*value=data;
-	return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryLong8Array(TIFF* tif, TIFFDirEntry* direntry, uint64** value)
-{
-    return TIFFReadDirEntryLong8ArrayWithLimit(tif, direntry, value, ~((uint64)0));
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntrySlong8Array(TIFF* tif, TIFFDirEntry* direntry, int64** value)
-{
-	enum TIFFReadDirEntryErr err;
-	uint32 count;
-	void* origdata;
-	int64* data;
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-		case TIFF_SBYTE:
-		case TIFF_SHORT:
-		case TIFF_SSHORT:
-		case TIFF_LONG:
-		case TIFF_SLONG:
-		case TIFF_LONG8:
-		case TIFF_SLONG8:
-			break;
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
-	err=TIFFReadDirEntryArray(tif,direntry,&count,8,&origdata);
-	if ((err!=TIFFReadDirEntryErrOk)||(origdata==0))
-	{
-		*value=0;
-		return(err);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_LONG8:
-			{
-				uint64* m;
-				uint32 n;
-				m=(uint64*)origdata;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8(m);
-					err=TIFFReadDirEntryCheckRangeSlong8Long8(*m);
-					if (err!=TIFFReadDirEntryErrOk)
-					{
-						_TIFFfree(origdata);
-						return(err);
-					}
-					m++;
-				}
-				*value=(int64*)origdata;
-				return(TIFFReadDirEntryErrOk);
-			}
-		case TIFF_SLONG8:
-			*value=(int64*)origdata;
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabArrayOfLong8((uint64*)(*value),count);
-			return(TIFFReadDirEntryErrOk);
-	}
-	data=(int64*)_TIFFmalloc(count*8);
-	if (data==0)
-	{
-		_TIFFfree(origdata);
-		return(TIFFReadDirEntryErrAlloc);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-			{
-				uint8* ma;
-				int64* mb;
-				uint32 n;
-				ma=(uint8*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-					*mb++=(int64)(*ma++);
-			}
-			break;
-		case TIFF_SBYTE:
-			{
-				int8* ma;
-				int64* mb;
-				uint32 n;
-				ma=(int8*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-					*mb++=(int64)(*ma++);
-			}
-			break;
-		case TIFF_SHORT:
-			{
-				uint16* ma;
-				int64* mb;
-				uint32 n;
-				ma=(uint16*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort(ma);
-					*mb++=(int64)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SSHORT:
-			{
-				int16* ma;
-				int64* mb;
-				uint32 n;
-				ma=(int16*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort((uint16*)ma);
-					*mb++=(int64)(*ma++);
-				}
-			}
-			break;
-		case TIFF_LONG:
-			{
-				uint32* ma;
-				int64* mb;
-				uint32 n;
-				ma=(uint32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong(ma);
-					*mb++=(int64)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SLONG:
-			{
-				int32* ma;
-				int64* mb;
-				uint32 n;
-				ma=(int32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong((uint32*)ma);
-					*mb++=(int64)(*ma++);
-				}
-			}
-			break;
-	}
-	_TIFFfree(origdata);
-	*value=data;
-	return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryFloatArray(TIFF* tif, TIFFDirEntry* direntry, float** value)
-{
-	enum TIFFReadDirEntryErr err;
-	uint32 count;
-	void* origdata;
-	float* data;
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-		case TIFF_SBYTE:
-		case TIFF_SHORT:
-		case TIFF_SSHORT:
-		case TIFF_LONG:
-		case TIFF_SLONG:
-		case TIFF_LONG8:
-		case TIFF_SLONG8:
-		case TIFF_RATIONAL:
-		case TIFF_SRATIONAL:
-		case TIFF_FLOAT:
-		case TIFF_DOUBLE:
-			break;
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
-	err=TIFFReadDirEntryArray(tif,direntry,&count,4,&origdata);
-	if ((err!=TIFFReadDirEntryErrOk)||(origdata==0))
-	{
-		*value=0;
-		return(err);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_FLOAT:
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabArrayOfLong((uint32*)origdata,count);  
-			TIFFCvtIEEEDoubleToNative(tif,count,(float*)origdata);
-			*value=(float*)origdata;
-			return(TIFFReadDirEntryErrOk);
-	}
-	data=(float*)_TIFFmalloc(count*sizeof(float));
-	if (data==0)
-	{
-		_TIFFfree(origdata);
-		return(TIFFReadDirEntryErrAlloc);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-			{
-				uint8* ma;
-				float* mb;
-				uint32 n;
-				ma=(uint8*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-					*mb++=(float)(*ma++);
-			}
-			break;
-		case TIFF_SBYTE:
-			{
-				int8* ma;
-				float* mb;
-				uint32 n;
-				ma=(int8*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-					*mb++=(float)(*ma++);
-			}
-			break;
-		case TIFF_SHORT:
-			{
-				uint16* ma;
-				float* mb;
-				uint32 n;
-				ma=(uint16*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort(ma);
-					*mb++=(float)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SSHORT:
-			{
-				int16* ma;
-				float* mb;
-				uint32 n;
-				ma=(int16*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort((uint16*)ma);
-					*mb++=(float)(*ma++);
-				}
-			}
-			break;
-		case TIFF_LONG:
-			{
-				uint32* ma;
-				float* mb;
-				uint32 n;
-				ma=(uint32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong(ma);
-					*mb++=(float)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SLONG:
-			{
-				int32* ma;
-				float* mb;
-				uint32 n;
-				ma=(int32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong((uint32*)ma);
-					*mb++=(float)(*ma++);
-				}
-			}
-			break;
-		case TIFF_LONG8:
-			{
-				uint64* ma;
-				float* mb;
-				uint32 n;
-				ma=(uint64*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8(ma);
-#if defined(__WIN32__) && (_MSC_VER < 1500)
-					/*
-					 * XXX: MSVC 6.0 does not support
-					 * conversion of 64-bit integers into
-					 * floating point values.
-					 */
-					*mb++ = _TIFFUInt64ToFloat(*ma++);
-#else
-					*mb++ = (float)(*ma++);
-#endif
-				}
-			}
-			break;
-		case TIFF_SLONG8:
-			{
-				int64* ma;
-				float* mb;
-				uint32 n;
-				ma=(int64*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8((uint64*)ma);
-					*mb++=(float)(*ma++);
-				}
-			}
-			break;
-		case TIFF_RATIONAL:
-			{
-				uint32* ma;
-				uint32 maa;
-				uint32 mab;
-				float* mb;
-				uint32 n;
-				ma=(uint32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong(ma);
-					maa=*ma++;
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong(ma);
-					mab=*ma++;
-					if (mab==0)
-						*mb++=0.0;
-					else
-						*mb++=(float)maa/(float)mab;
-				}
-			}
-			break;
-		case TIFF_SRATIONAL:
-			{
-				uint32* ma;
-				int32 maa;
-				uint32 mab;
-				float* mb;
-				uint32 n;
-				ma=(uint32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong(ma);
-					maa=*(int32*)ma;
-					ma++;
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong(ma);
-					mab=*ma++;
-					if (mab==0)
-						*mb++=0.0;
-					else
-						*mb++=(float)maa/(float)mab;
-				}
-			}
-			break;
-		case TIFF_DOUBLE:
-			{
-				double* ma;
-				float* mb;
-				uint32 n;
-				if (tif->tif_flags&TIFF_SWAB)
-					TIFFSwabArrayOfLong8((uint64*)origdata,count);
-				TIFFCvtIEEEDoubleToNative(tif,count,(double*)origdata);
-				ma=(double*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-                                {
-                                    double val = *ma++;
-                                    if( val > FLT_MAX )
-                                        val = FLT_MAX;
-                                    else if( val < -FLT_MAX )
-                                        val = -FLT_MAX;
-                                    *mb++=(float)val;
-                                }
-			}
-			break;
-	}
-	_TIFFfree(origdata);
-	*value=data;
-	return(TIFFReadDirEntryErrOk);
+    }
+    else
+    {
+        /* See above comment for the Classic TIFF case */
+        if (original_datasize_clamped <= 8 && datasize <= 8)
+            _TIFFmemcpy(data, &direntry->tdir_offset, datasize);
+        else
+        {
+            enum TIFFReadDirEntryErr err;
+            uint64_t offset = direntry->tdir_offset.toff_long8;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong8(&offset);
+            if (isMapped(tif))
+                err = TIFFReadDirEntryData(tif, (uint64_t)offset,
+                                           (tmsize_t)datasize, data);
+            else
+                err = TIFFReadDirEntryDataAndRealloc(tif, (uint64_t)offset,
+                                                     (tmsize_t)datasize, &data);
+            if (err != TIFFReadDirEntryErrOk)
+            {
+                _TIFFfreeExt(tif, data);
+                return (err);
+            }
+        }
+    }
+    *value = data;
+    return (TIFFReadDirEntryErrOk);
 }
 
 static enum TIFFReadDirEntryErr
-TIFFReadDirEntryDoubleArray(TIFF* tif, TIFFDirEntry* direntry, double** value)
+TIFFReadDirEntryArray(TIFF *tif, TIFFDirEntry *direntry, uint32_t *count,
+                      uint32_t desttypesize, void **value)
 {
-	enum TIFFReadDirEntryErr err;
-	uint32 count;
-	void* origdata;
-	double* data;
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-		case TIFF_SBYTE:
-		case TIFF_SHORT:
-		case TIFF_SSHORT:
-		case TIFF_LONG:
-		case TIFF_SLONG:
-		case TIFF_LONG8:
-		case TIFF_SLONG8:
-		case TIFF_RATIONAL:
-		case TIFF_SRATIONAL:
-		case TIFF_FLOAT:
-		case TIFF_DOUBLE:
-			break;
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
-	err=TIFFReadDirEntryArray(tif,direntry,&count,8,&origdata);
-	if ((err!=TIFFReadDirEntryErrOk)||(origdata==0))
-	{
-		*value=0;
-		return(err);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_DOUBLE:
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabArrayOfLong8((uint64*)origdata,count);
-			TIFFCvtIEEEDoubleToNative(tif,count,(double*)origdata);
-			*value=(double*)origdata;
-			return(TIFFReadDirEntryErrOk);
-	}
-	data=(double*)_TIFFmalloc(count*sizeof(double));
-	if (data==0)
-	{
-		_TIFFfree(origdata);
-		return(TIFFReadDirEntryErrAlloc);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_BYTE:
-			{
-				uint8* ma;
-				double* mb;
-				uint32 n;
-				ma=(uint8*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-					*mb++=(double)(*ma++);
-			}
-			break;
-		case TIFF_SBYTE:
-			{
-				int8* ma;
-				double* mb;
-				uint32 n;
-				ma=(int8*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-					*mb++=(double)(*ma++);
-			}
-			break;
-		case TIFF_SHORT:
-			{
-				uint16* ma;
-				double* mb;
-				uint32 n;
-				ma=(uint16*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort(ma);
-					*mb++=(double)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SSHORT:
-			{
-				int16* ma;
-				double* mb;
-				uint32 n;
-				ma=(int16*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabShort((uint16*)ma);
-					*mb++=(double)(*ma++);
-				}
-			}
-			break;
-		case TIFF_LONG:
-			{
-				uint32* ma;
-				double* mb;
-				uint32 n;
-				ma=(uint32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong(ma);
-					*mb++=(double)(*ma++);
-				}
-			}
-			break;
-		case TIFF_SLONG:
-			{
-				int32* ma;
-				double* mb;
-				uint32 n;
-				ma=(int32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong((uint32*)ma);
-					*mb++=(double)(*ma++);
-				}
-			}
-			break;
-		case TIFF_LONG8:
-			{
-				uint64* ma;
-				double* mb;
-				uint32 n;
-				ma=(uint64*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8(ma);
+    return TIFFReadDirEntryArrayWithLimit(tif, direntry, count, desttypesize,
+                                          value, ~((uint64_t)0));
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryByteArray(TIFF *tif, TIFFDirEntry *direntry, uint8_t **value)
+{
+    enum TIFFReadDirEntryErr err;
+    uint32_t count;
+    void *origdata;
+    uint8_t *data;
+    switch (direntry->tdir_type)
+    {
+        case TIFF_ASCII:
+        case TIFF_UNDEFINED:
+        case TIFF_BYTE:
+        case TIFF_SBYTE:
+        case TIFF_SHORT:
+        case TIFF_SSHORT:
+        case TIFF_LONG:
+        case TIFF_SLONG:
+        case TIFF_LONG8:
+        case TIFF_SLONG8:
+            break;
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+    err = TIFFReadDirEntryArray(tif, direntry, &count, 1, &origdata);
+    if ((err != TIFFReadDirEntryErrOk) || (origdata == 0))
+    {
+        *value = 0;
+        return (err);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_ASCII:
+        case TIFF_UNDEFINED:
+        case TIFF_BYTE:
+            *value = (uint8_t *)origdata;
+            return (TIFFReadDirEntryErrOk);
+        case TIFF_SBYTE:
+        {
+            int8_t *m;
+            uint32_t n;
+            m = (int8_t *)origdata;
+            for (n = 0; n < count; n++)
+            {
+                err = TIFFReadDirEntryCheckRangeByteSbyte(*m);
+                if (err != TIFFReadDirEntryErrOk)
+                {
+                    _TIFFfreeExt(tif, origdata);
+                    return (err);
+                }
+                m++;
+            }
+            *value = (uint8_t *)origdata;
+            return (TIFFReadDirEntryErrOk);
+        }
+    }
+    data = (uint8_t *)_TIFFmallocExt(tif, count);
+    if (data == 0)
+    {
+        _TIFFfreeExt(tif, origdata);
+        return (TIFFReadDirEntryErrAlloc);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_SHORT:
+        {
+            uint16_t *ma;
+            uint8_t *mb;
+            uint32_t n;
+            ma = (uint16_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort(ma);
+                err = TIFFReadDirEntryCheckRangeByteShort(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint8_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SSHORT:
+        {
+            int16_t *ma;
+            uint8_t *mb;
+            uint32_t n;
+            ma = (int16_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort((uint16_t *)ma);
+                err = TIFFReadDirEntryCheckRangeByteSshort(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint8_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_LONG:
+        {
+            uint32_t *ma;
+            uint8_t *mb;
+            uint32_t n;
+            ma = (uint32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(ma);
+                err = TIFFReadDirEntryCheckRangeByteLong(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint8_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SLONG:
+        {
+            int32_t *ma;
+            uint8_t *mb;
+            uint32_t n;
+            ma = (int32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong((uint32_t *)ma);
+                err = TIFFReadDirEntryCheckRangeByteSlong(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint8_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_LONG8:
+        {
+            uint64_t *ma;
+            uint8_t *mb;
+            uint32_t n;
+            ma = (uint64_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8(ma);
+                err = TIFFReadDirEntryCheckRangeByteLong8(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint8_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SLONG8:
+        {
+            int64_t *ma;
+            uint8_t *mb;
+            uint32_t n;
+            ma = (int64_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8((uint64_t *)ma);
+                err = TIFFReadDirEntryCheckRangeByteSlong8(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint8_t)(*ma++);
+            }
+        }
+        break;
+    }
+    _TIFFfreeExt(tif, origdata);
+    if (err != TIFFReadDirEntryErrOk)
+    {
+        _TIFFfreeExt(tif, data);
+        return (err);
+    }
+    *value = data;
+    return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntrySbyteArray(TIFF *tif, TIFFDirEntry *direntry, int8_t **value)
+{
+    enum TIFFReadDirEntryErr err;
+    uint32_t count;
+    void *origdata;
+    int8_t *data;
+    switch (direntry->tdir_type)
+    {
+        case TIFF_UNDEFINED:
+        case TIFF_BYTE:
+        case TIFF_SBYTE:
+        case TIFF_SHORT:
+        case TIFF_SSHORT:
+        case TIFF_LONG:
+        case TIFF_SLONG:
+        case TIFF_LONG8:
+        case TIFF_SLONG8:
+            break;
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+    err = TIFFReadDirEntryArray(tif, direntry, &count, 1, &origdata);
+    if ((err != TIFFReadDirEntryErrOk) || (origdata == 0))
+    {
+        *value = 0;
+        return (err);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_UNDEFINED:
+        case TIFF_BYTE:
+        {
+            uint8_t *m;
+            uint32_t n;
+            m = (uint8_t *)origdata;
+            for (n = 0; n < count; n++)
+            {
+                err = TIFFReadDirEntryCheckRangeSbyteByte(*m);
+                if (err != TIFFReadDirEntryErrOk)
+                {
+                    _TIFFfreeExt(tif, origdata);
+                    return (err);
+                }
+                m++;
+            }
+            *value = (int8_t *)origdata;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SBYTE:
+            *value = (int8_t *)origdata;
+            return (TIFFReadDirEntryErrOk);
+    }
+    data = (int8_t *)_TIFFmallocExt(tif, count);
+    if (data == 0)
+    {
+        _TIFFfreeExt(tif, origdata);
+        return (TIFFReadDirEntryErrAlloc);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_SHORT:
+        {
+            uint16_t *ma;
+            int8_t *mb;
+            uint32_t n;
+            ma = (uint16_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort(ma);
+                err = TIFFReadDirEntryCheckRangeSbyteShort(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (int8_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SSHORT:
+        {
+            int16_t *ma;
+            int8_t *mb;
+            uint32_t n;
+            ma = (int16_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort((uint16_t *)ma);
+                err = TIFFReadDirEntryCheckRangeSbyteSshort(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (int8_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_LONG:
+        {
+            uint32_t *ma;
+            int8_t *mb;
+            uint32_t n;
+            ma = (uint32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(ma);
+                err = TIFFReadDirEntryCheckRangeSbyteLong(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (int8_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SLONG:
+        {
+            int32_t *ma;
+            int8_t *mb;
+            uint32_t n;
+            ma = (int32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong((uint32_t *)ma);
+                err = TIFFReadDirEntryCheckRangeSbyteSlong(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (int8_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_LONG8:
+        {
+            uint64_t *ma;
+            int8_t *mb;
+            uint32_t n;
+            ma = (uint64_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8(ma);
+                err = TIFFReadDirEntryCheckRangeSbyteLong8(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (int8_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SLONG8:
+        {
+            int64_t *ma;
+            int8_t *mb;
+            uint32_t n;
+            ma = (int64_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8((uint64_t *)ma);
+                err = TIFFReadDirEntryCheckRangeSbyteSlong8(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (int8_t)(*ma++);
+            }
+        }
+        break;
+    }
+    _TIFFfreeExt(tif, origdata);
+    if (err != TIFFReadDirEntryErrOk)
+    {
+        _TIFFfreeExt(tif, data);
+        return (err);
+    }
+    *value = data;
+    return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryShortArray(TIFF *tif, TIFFDirEntry *direntry, uint16_t **value)
+{
+    enum TIFFReadDirEntryErr err;
+    uint32_t count;
+    void *origdata;
+    uint16_t *data;
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        case TIFF_SBYTE:
+        case TIFF_SHORT:
+        case TIFF_SSHORT:
+        case TIFF_LONG:
+        case TIFF_SLONG:
+        case TIFF_LONG8:
+        case TIFF_SLONG8:
+            break;
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+    err = TIFFReadDirEntryArray(tif, direntry, &count, 2, &origdata);
+    if ((err != TIFFReadDirEntryErrOk) || (origdata == 0))
+    {
+        *value = 0;
+        return (err);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_SHORT:
+            *value = (uint16_t *)origdata;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabArrayOfShort(*value, count);
+            return (TIFFReadDirEntryErrOk);
+        case TIFF_SSHORT:
+        {
+            int16_t *m;
+            uint32_t n;
+            m = (int16_t *)origdata;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort((uint16_t *)m);
+                err = TIFFReadDirEntryCheckRangeShortSshort(*m);
+                if (err != TIFFReadDirEntryErrOk)
+                {
+                    _TIFFfreeExt(tif, origdata);
+                    return (err);
+                }
+                m++;
+            }
+            *value = (uint16_t *)origdata;
+            return (TIFFReadDirEntryErrOk);
+        }
+    }
+    data = (uint16_t *)_TIFFmallocExt(tif, count * 2);
+    if (data == 0)
+    {
+        _TIFFfreeExt(tif, origdata);
+        return (TIFFReadDirEntryErrAlloc);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        {
+            uint8_t *ma;
+            uint16_t *mb;
+            uint32_t n;
+            ma = (uint8_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+                *mb++ = (uint16_t)(*ma++);
+        }
+        break;
+        case TIFF_SBYTE:
+        {
+            int8_t *ma;
+            uint16_t *mb;
+            uint32_t n;
+            ma = (int8_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                err = TIFFReadDirEntryCheckRangeShortSbyte(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint16_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_LONG:
+        {
+            uint32_t *ma;
+            uint16_t *mb;
+            uint32_t n;
+            ma = (uint32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(ma);
+                err = TIFFReadDirEntryCheckRangeShortLong(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint16_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SLONG:
+        {
+            int32_t *ma;
+            uint16_t *mb;
+            uint32_t n;
+            ma = (int32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong((uint32_t *)ma);
+                err = TIFFReadDirEntryCheckRangeShortSlong(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint16_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_LONG8:
+        {
+            uint64_t *ma;
+            uint16_t *mb;
+            uint32_t n;
+            ma = (uint64_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8(ma);
+                err = TIFFReadDirEntryCheckRangeShortLong8(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint16_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SLONG8:
+        {
+            int64_t *ma;
+            uint16_t *mb;
+            uint32_t n;
+            ma = (int64_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8((uint64_t *)ma);
+                err = TIFFReadDirEntryCheckRangeShortSlong8(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint16_t)(*ma++);
+            }
+        }
+        break;
+    }
+    _TIFFfreeExt(tif, origdata);
+    if (err != TIFFReadDirEntryErrOk)
+    {
+        _TIFFfreeExt(tif, data);
+        return (err);
+    }
+    *value = data;
+    return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntrySshortArray(TIFF *tif, TIFFDirEntry *direntry, int16_t **value)
+{
+    enum TIFFReadDirEntryErr err;
+    uint32_t count;
+    void *origdata;
+    int16_t *data;
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        case TIFF_SBYTE:
+        case TIFF_SHORT:
+        case TIFF_SSHORT:
+        case TIFF_LONG:
+        case TIFF_SLONG:
+        case TIFF_LONG8:
+        case TIFF_SLONG8:
+            break;
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+    err = TIFFReadDirEntryArray(tif, direntry, &count, 2, &origdata);
+    if ((err != TIFFReadDirEntryErrOk) || (origdata == 0))
+    {
+        *value = 0;
+        return (err);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_SHORT:
+        {
+            uint16_t *m;
+            uint32_t n;
+            m = (uint16_t *)origdata;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort(m);
+                err = TIFFReadDirEntryCheckRangeSshortShort(*m);
+                if (err != TIFFReadDirEntryErrOk)
+                {
+                    _TIFFfreeExt(tif, origdata);
+                    return (err);
+                }
+                m++;
+            }
+            *value = (int16_t *)origdata;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SSHORT:
+            *value = (int16_t *)origdata;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabArrayOfShort((uint16_t *)(*value), count);
+            return (TIFFReadDirEntryErrOk);
+    }
+    data = (int16_t *)_TIFFmallocExt(tif, count * 2);
+    if (data == 0)
+    {
+        _TIFFfreeExt(tif, origdata);
+        return (TIFFReadDirEntryErrAlloc);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        {
+            uint8_t *ma;
+            int16_t *mb;
+            uint32_t n;
+            ma = (uint8_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+                *mb++ = (int16_t)(*ma++);
+        }
+        break;
+        case TIFF_SBYTE:
+        {
+            int8_t *ma;
+            int16_t *mb;
+            uint32_t n;
+            ma = (int8_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+                *mb++ = (int16_t)(*ma++);
+        }
+        break;
+        case TIFF_LONG:
+        {
+            uint32_t *ma;
+            int16_t *mb;
+            uint32_t n;
+            ma = (uint32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(ma);
+                err = TIFFReadDirEntryCheckRangeSshortLong(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (int16_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SLONG:
+        {
+            int32_t *ma;
+            int16_t *mb;
+            uint32_t n;
+            ma = (int32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong((uint32_t *)ma);
+                err = TIFFReadDirEntryCheckRangeSshortSlong(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (int16_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_LONG8:
+        {
+            uint64_t *ma;
+            int16_t *mb;
+            uint32_t n;
+            ma = (uint64_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8(ma);
+                err = TIFFReadDirEntryCheckRangeSshortLong8(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (int16_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SLONG8:
+        {
+            int64_t *ma;
+            int16_t *mb;
+            uint32_t n;
+            ma = (int64_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8((uint64_t *)ma);
+                err = TIFFReadDirEntryCheckRangeSshortSlong8(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (int16_t)(*ma++);
+            }
+        }
+        break;
+    }
+    _TIFFfreeExt(tif, origdata);
+    if (err != TIFFReadDirEntryErrOk)
+    {
+        _TIFFfreeExt(tif, data);
+        return (err);
+    }
+    *value = data;
+    return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryLongArray(TIFF *tif, TIFFDirEntry *direntry, uint32_t **value)
+{
+    enum TIFFReadDirEntryErr err;
+    uint32_t count;
+    void *origdata;
+    uint32_t *data;
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        case TIFF_SBYTE:
+        case TIFF_SHORT:
+        case TIFF_SSHORT:
+        case TIFF_LONG:
+        case TIFF_SLONG:
+        case TIFF_LONG8:
+        case TIFF_SLONG8:
+            break;
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+    err = TIFFReadDirEntryArray(tif, direntry, &count, 4, &origdata);
+    if ((err != TIFFReadDirEntryErrOk) || (origdata == 0))
+    {
+        *value = 0;
+        return (err);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_LONG:
+            *value = (uint32_t *)origdata;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabArrayOfLong(*value, count);
+            return (TIFFReadDirEntryErrOk);
+        case TIFF_SLONG:
+        {
+            int32_t *m;
+            uint32_t n;
+            m = (int32_t *)origdata;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong((uint32_t *)m);
+                err = TIFFReadDirEntryCheckRangeLongSlong(*m);
+                if (err != TIFFReadDirEntryErrOk)
+                {
+                    _TIFFfreeExt(tif, origdata);
+                    return (err);
+                }
+                m++;
+            }
+            *value = (uint32_t *)origdata;
+            return (TIFFReadDirEntryErrOk);
+        }
+    }
+    data = (uint32_t *)_TIFFmallocExt(tif, count * 4);
+    if (data == 0)
+    {
+        _TIFFfreeExt(tif, origdata);
+        return (TIFFReadDirEntryErrAlloc);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        {
+            uint8_t *ma;
+            uint32_t *mb;
+            uint32_t n;
+            ma = (uint8_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+                *mb++ = (uint32_t)(*ma++);
+        }
+        break;
+        case TIFF_SBYTE:
+        {
+            int8_t *ma;
+            uint32_t *mb;
+            uint32_t n;
+            ma = (int8_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                err = TIFFReadDirEntryCheckRangeLongSbyte(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint32_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SHORT:
+        {
+            uint16_t *ma;
+            uint32_t *mb;
+            uint32_t n;
+            ma = (uint16_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort(ma);
+                *mb++ = (uint32_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SSHORT:
+        {
+            int16_t *ma;
+            uint32_t *mb;
+            uint32_t n;
+            ma = (int16_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort((uint16_t *)ma);
+                err = TIFFReadDirEntryCheckRangeLongSshort(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint32_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_LONG8:
+        {
+            uint64_t *ma;
+            uint32_t *mb;
+            uint32_t n;
+            ma = (uint64_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8(ma);
+                err = TIFFReadDirEntryCheckRangeLongLong8(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint32_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SLONG8:
+        {
+            int64_t *ma;
+            uint32_t *mb;
+            uint32_t n;
+            ma = (int64_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8((uint64_t *)ma);
+                err = TIFFReadDirEntryCheckRangeLongSlong8(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint32_t)(*ma++);
+            }
+        }
+        break;
+    }
+    _TIFFfreeExt(tif, origdata);
+    if (err != TIFFReadDirEntryErrOk)
+    {
+        _TIFFfreeExt(tif, data);
+        return (err);
+    }
+    *value = data;
+    return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntrySlongArray(TIFF *tif, TIFFDirEntry *direntry, int32_t **value)
+{
+    enum TIFFReadDirEntryErr err;
+    uint32_t count;
+    void *origdata;
+    int32_t *data;
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        case TIFF_SBYTE:
+        case TIFF_SHORT:
+        case TIFF_SSHORT:
+        case TIFF_LONG:
+        case TIFF_SLONG:
+        case TIFF_LONG8:
+        case TIFF_SLONG8:
+            break;
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+    err = TIFFReadDirEntryArray(tif, direntry, &count, 4, &origdata);
+    if ((err != TIFFReadDirEntryErrOk) || (origdata == 0))
+    {
+        *value = 0;
+        return (err);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_LONG:
+        {
+            uint32_t *m;
+            uint32_t n;
+            m = (uint32_t *)origdata;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong((uint32_t *)m);
+                err = TIFFReadDirEntryCheckRangeSlongLong(*m);
+                if (err != TIFFReadDirEntryErrOk)
+                {
+                    _TIFFfreeExt(tif, origdata);
+                    return (err);
+                }
+                m++;
+            }
+            *value = (int32_t *)origdata;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG:
+            *value = (int32_t *)origdata;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabArrayOfLong((uint32_t *)(*value), count);
+            return (TIFFReadDirEntryErrOk);
+    }
+    data = (int32_t *)_TIFFmallocExt(tif, count * 4);
+    if (data == 0)
+    {
+        _TIFFfreeExt(tif, origdata);
+        return (TIFFReadDirEntryErrAlloc);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        {
+            uint8_t *ma;
+            int32_t *mb;
+            uint32_t n;
+            ma = (uint8_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+                *mb++ = (int32_t)(*ma++);
+        }
+        break;
+        case TIFF_SBYTE:
+        {
+            int8_t *ma;
+            int32_t *mb;
+            uint32_t n;
+            ma = (int8_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+                *mb++ = (int32_t)(*ma++);
+        }
+        break;
+        case TIFF_SHORT:
+        {
+            uint16_t *ma;
+            int32_t *mb;
+            uint32_t n;
+            ma = (uint16_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort(ma);
+                *mb++ = (int32_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SSHORT:
+        {
+            int16_t *ma;
+            int32_t *mb;
+            uint32_t n;
+            ma = (int16_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort((uint16_t *)ma);
+                *mb++ = (int32_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_LONG8:
+        {
+            uint64_t *ma;
+            int32_t *mb;
+            uint32_t n;
+            ma = (uint64_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8(ma);
+                err = TIFFReadDirEntryCheckRangeSlongLong8(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (int32_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SLONG8:
+        {
+            int64_t *ma;
+            int32_t *mb;
+            uint32_t n;
+            ma = (int64_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8((uint64_t *)ma);
+                err = TIFFReadDirEntryCheckRangeSlongSlong8(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (int32_t)(*ma++);
+            }
+        }
+        break;
+    }
+    _TIFFfreeExt(tif, origdata);
+    if (err != TIFFReadDirEntryErrOk)
+    {
+        _TIFFfreeExt(tif, data);
+        return (err);
+    }
+    *value = data;
+    return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryLong8ArrayWithLimit(TIFF *tif, TIFFDirEntry *direntry,
+                                    uint64_t **value, uint64_t maxcount)
+{
+    enum TIFFReadDirEntryErr err;
+    uint32_t count;
+    void *origdata;
+    uint64_t *data;
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        case TIFF_SBYTE:
+        case TIFF_SHORT:
+        case TIFF_SSHORT:
+        case TIFF_LONG:
+        case TIFF_SLONG:
+        case TIFF_LONG8:
+        case TIFF_SLONG8:
+            break;
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+    err = TIFFReadDirEntryArrayWithLimit(tif, direntry, &count, 8, &origdata,
+                                         maxcount);
+    if ((err != TIFFReadDirEntryErrOk) || (origdata == 0))
+    {
+        *value = 0;
+        return (err);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_LONG8:
+            *value = (uint64_t *)origdata;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabArrayOfLong8(*value, count);
+            return (TIFFReadDirEntryErrOk);
+        case TIFF_SLONG8:
+        {
+            int64_t *m;
+            uint32_t n;
+            m = (int64_t *)origdata;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8((uint64_t *)m);
+                err = TIFFReadDirEntryCheckRangeLong8Slong8(*m);
+                if (err != TIFFReadDirEntryErrOk)
+                {
+                    _TIFFfreeExt(tif, origdata);
+                    return (err);
+                }
+                m++;
+            }
+            *value = (uint64_t *)origdata;
+            return (TIFFReadDirEntryErrOk);
+        }
+    }
+    data = (uint64_t *)_TIFFmallocExt(tif, count * 8);
+    if (data == 0)
+    {
+        _TIFFfreeExt(tif, origdata);
+        return (TIFFReadDirEntryErrAlloc);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        {
+            uint8_t *ma;
+            uint64_t *mb;
+            uint32_t n;
+            ma = (uint8_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+                *mb++ = (uint64_t)(*ma++);
+        }
+        break;
+        case TIFF_SBYTE:
+        {
+            int8_t *ma;
+            uint64_t *mb;
+            uint32_t n;
+            ma = (int8_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                err = TIFFReadDirEntryCheckRangeLong8Sbyte(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint64_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SHORT:
+        {
+            uint16_t *ma;
+            uint64_t *mb;
+            uint32_t n;
+            ma = (uint16_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort(ma);
+                *mb++ = (uint64_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SSHORT:
+        {
+            int16_t *ma;
+            uint64_t *mb;
+            uint32_t n;
+            ma = (int16_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort((uint16_t *)ma);
+                err = TIFFReadDirEntryCheckRangeLong8Sshort(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint64_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_LONG:
+        {
+            uint32_t *ma;
+            uint64_t *mb;
+            uint32_t n;
+            ma = (uint32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(ma);
+                *mb++ = (uint64_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SLONG:
+        {
+            int32_t *ma;
+            uint64_t *mb;
+            uint32_t n;
+            ma = (int32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong((uint32_t *)ma);
+                err = TIFFReadDirEntryCheckRangeLong8Slong(*ma);
+                if (err != TIFFReadDirEntryErrOk)
+                    break;
+                *mb++ = (uint64_t)(*ma++);
+            }
+        }
+        break;
+    }
+    _TIFFfreeExt(tif, origdata);
+    if (err != TIFFReadDirEntryErrOk)
+    {
+        _TIFFfreeExt(tif, data);
+        return (err);
+    }
+    *value = data;
+    return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryLong8Array(TIFF *tif, TIFFDirEntry *direntry, uint64_t **value)
+{
+    return TIFFReadDirEntryLong8ArrayWithLimit(tif, direntry, value,
+                                               ~((uint64_t)0));
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntrySlong8Array(TIFF *tif, TIFFDirEntry *direntry, int64_t **value)
+{
+    enum TIFFReadDirEntryErr err;
+    uint32_t count;
+    void *origdata;
+    int64_t *data;
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        case TIFF_SBYTE:
+        case TIFF_SHORT:
+        case TIFF_SSHORT:
+        case TIFF_LONG:
+        case TIFF_SLONG:
+        case TIFF_LONG8:
+        case TIFF_SLONG8:
+            break;
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+    err = TIFFReadDirEntryArray(tif, direntry, &count, 8, &origdata);
+    if ((err != TIFFReadDirEntryErrOk) || (origdata == 0))
+    {
+        *value = 0;
+        return (err);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_LONG8:
+        {
+            uint64_t *m;
+            uint32_t n;
+            m = (uint64_t *)origdata;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8(m);
+                err = TIFFReadDirEntryCheckRangeSlong8Long8(*m);
+                if (err != TIFFReadDirEntryErrOk)
+                {
+                    _TIFFfreeExt(tif, origdata);
+                    return (err);
+                }
+                m++;
+            }
+            *value = (int64_t *)origdata;
+            return (TIFFReadDirEntryErrOk);
+        }
+        case TIFF_SLONG8:
+            *value = (int64_t *)origdata;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabArrayOfLong8((uint64_t *)(*value), count);
+            return (TIFFReadDirEntryErrOk);
+    }
+    data = (int64_t *)_TIFFmallocExt(tif, count * 8);
+    if (data == 0)
+    {
+        _TIFFfreeExt(tif, origdata);
+        return (TIFFReadDirEntryErrAlloc);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        {
+            uint8_t *ma;
+            int64_t *mb;
+            uint32_t n;
+            ma = (uint8_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+                *mb++ = (int64_t)(*ma++);
+        }
+        break;
+        case TIFF_SBYTE:
+        {
+            int8_t *ma;
+            int64_t *mb;
+            uint32_t n;
+            ma = (int8_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+                *mb++ = (int64_t)(*ma++);
+        }
+        break;
+        case TIFF_SHORT:
+        {
+            uint16_t *ma;
+            int64_t *mb;
+            uint32_t n;
+            ma = (uint16_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort(ma);
+                *mb++ = (int64_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SSHORT:
+        {
+            int16_t *ma;
+            int64_t *mb;
+            uint32_t n;
+            ma = (int16_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort((uint16_t *)ma);
+                *mb++ = (int64_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_LONG:
+        {
+            uint32_t *ma;
+            int64_t *mb;
+            uint32_t n;
+            ma = (uint32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(ma);
+                *mb++ = (int64_t)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SLONG:
+        {
+            int32_t *ma;
+            int64_t *mb;
+            uint32_t n;
+            ma = (int32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong((uint32_t *)ma);
+                *mb++ = (int64_t)(*ma++);
+            }
+        }
+        break;
+    }
+    _TIFFfreeExt(tif, origdata);
+    *value = data;
+    return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryFloatArray(TIFF *tif, TIFFDirEntry *direntry, float **value)
+{
+    enum TIFFReadDirEntryErr err;
+    uint32_t count;
+    void *origdata;
+    float *data;
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        case TIFF_SBYTE:
+        case TIFF_SHORT:
+        case TIFF_SSHORT:
+        case TIFF_LONG:
+        case TIFF_SLONG:
+        case TIFF_LONG8:
+        case TIFF_SLONG8:
+        case TIFF_RATIONAL:
+        case TIFF_SRATIONAL:
+        case TIFF_FLOAT:
+        case TIFF_DOUBLE:
+            break;
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+    err = TIFFReadDirEntryArray(tif, direntry, &count, 4, &origdata);
+    if ((err != TIFFReadDirEntryErrOk) || (origdata == 0))
+    {
+        *value = 0;
+        return (err);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_FLOAT:
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabArrayOfLong((uint32_t *)origdata, count);
+            TIFFCvtIEEEDoubleToNative(tif, count, (float *)origdata);
+            *value = (float *)origdata;
+            return (TIFFReadDirEntryErrOk);
+    }
+    data = (float *)_TIFFmallocExt(tif, count * sizeof(float));
+    if (data == 0)
+    {
+        _TIFFfreeExt(tif, origdata);
+        return (TIFFReadDirEntryErrAlloc);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        {
+            uint8_t *ma;
+            float *mb;
+            uint32_t n;
+            ma = (uint8_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+                *mb++ = (float)(*ma++);
+        }
+        break;
+        case TIFF_SBYTE:
+        {
+            int8_t *ma;
+            float *mb;
+            uint32_t n;
+            ma = (int8_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+                *mb++ = (float)(*ma++);
+        }
+        break;
+        case TIFF_SHORT:
+        {
+            uint16_t *ma;
+            float *mb;
+            uint32_t n;
+            ma = (uint16_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort(ma);
+                *mb++ = (float)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SSHORT:
+        {
+            int16_t *ma;
+            float *mb;
+            uint32_t n;
+            ma = (int16_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort((uint16_t *)ma);
+                *mb++ = (float)(*ma++);
+            }
+        }
+        break;
+        case TIFF_LONG:
+        {
+            uint32_t *ma;
+            float *mb;
+            uint32_t n;
+            ma = (uint32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(ma);
+                *mb++ = (float)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SLONG:
+        {
+            int32_t *ma;
+            float *mb;
+            uint32_t n;
+            ma = (int32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong((uint32_t *)ma);
+                *mb++ = (float)(*ma++);
+            }
+        }
+        break;
+        case TIFF_LONG8:
+        {
+            uint64_t *ma;
+            float *mb;
+            uint32_t n;
+            ma = (uint64_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8(ma);
 #if defined(__WIN32__) && (_MSC_VER < 1500)
-					/*
-					 * XXX: MSVC 6.0 does not support
-					 * conversion of 64-bit integers into
-					 * floating point values.
-					 */
-					*mb++ = _TIFFUInt64ToDouble(*ma++);
+                /*
+                 * XXX: MSVC 6.0 does not support
+                 * conversion of 64-bit integers into
+                 * floating point values.
+                 */
+                *mb++ = _TIFFUInt64ToFloat(*ma++);
 #else
-					*mb++ = (double)(*ma++);
+                *mb++ = (float)(*ma++);
 #endif
-				}
-			}
-			break;
-		case TIFF_SLONG8:
-			{
-				int64* ma;
-				double* mb;
-				uint32 n;
-				ma=(int64*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong8((uint64*)ma);
-					*mb++=(double)(*ma++);
-				}
-			}
-			break;
-		case TIFF_RATIONAL:
-			{
-				uint32* ma;
-				uint32 maa;
-				uint32 mab;
-				double* mb;
-				uint32 n;
-				ma=(uint32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong(ma);
-					maa=*ma++;
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong(ma);
-					mab=*ma++;
-					if (mab==0)
-						*mb++=0.0;
-					else
-						*mb++=(double)maa/(double)mab;
-				}
-			}
-			break;
-		case TIFF_SRATIONAL:
-			{
-				uint32* ma;
-				int32 maa;
-				uint32 mab;
-				double* mb;
-				uint32 n;
-				ma=(uint32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong(ma);
-					maa=*(int32*)ma;
-					ma++;
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong(ma);
-					mab=*ma++;
-					if (mab==0)
-						*mb++=0.0;
-					else
-						*mb++=(double)maa/(double)mab;
-				}
-			}
-			break;
-		case TIFF_FLOAT:
-			{
-				float* ma;
-				double* mb;
-				uint32 n;
-				if (tif->tif_flags&TIFF_SWAB)
-					TIFFSwabArrayOfLong((uint32*)origdata,count);  
-				TIFFCvtIEEEFloatToNative(tif,count,(float*)origdata);
-				ma=(float*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-					*mb++=(double)(*ma++);
-			}
-			break;
-	}
-	_TIFFfree(origdata);
-	*value=data;
-	return(TIFFReadDirEntryErrOk);
+            }
+        }
+        break;
+        case TIFF_SLONG8:
+        {
+            int64_t *ma;
+            float *mb;
+            uint32_t n;
+            ma = (int64_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8((uint64_t *)ma);
+                *mb++ = (float)(*ma++);
+            }
+        }
+        break;
+        case TIFF_RATIONAL:
+        {
+            uint32_t *ma;
+            uint32_t maa;
+            uint32_t mab;
+            float *mb;
+            uint32_t n;
+            ma = (uint32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(ma);
+                maa = *ma++;
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(ma);
+                mab = *ma++;
+                if (mab == 0)
+                    *mb++ = 0.0;
+                else
+                    *mb++ = (float)maa / (float)mab;
+            }
+        }
+        break;
+        case TIFF_SRATIONAL:
+        {
+            uint32_t *ma;
+            int32_t maa;
+            uint32_t mab;
+            float *mb;
+            uint32_t n;
+            ma = (uint32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(ma);
+                maa = *(int32_t *)ma;
+                ma++;
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(ma);
+                mab = *ma++;
+                if (mab == 0)
+                    *mb++ = 0.0;
+                else
+                    *mb++ = (float)maa / (float)mab;
+            }
+        }
+        break;
+        case TIFF_DOUBLE:
+        {
+            double *ma;
+            float *mb;
+            uint32_t n;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabArrayOfLong8((uint64_t *)origdata, count);
+            TIFFCvtIEEEDoubleToNative(tif, count, (double *)origdata);
+            ma = (double *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                double val = *ma++;
+                if (val > FLT_MAX)
+                    val = FLT_MAX;
+                else if (val < -FLT_MAX)
+                    val = -FLT_MAX;
+                *mb++ = (float)val;
+            }
+        }
+        break;
+    }
+    _TIFFfreeExt(tif, origdata);
+    *value = data;
+    return (TIFFReadDirEntryErrOk);
 }
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryIfd8Array(TIFF* tif, TIFFDirEntry* direntry, uint64** value)
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryDoubleArray(TIFF *tif, TIFFDirEntry *direntry, double **value)
 {
-	enum TIFFReadDirEntryErr err;
-	uint32 count;
-	void* origdata;
-	uint64* data;
-	switch (direntry->tdir_type)
-	{
-		case TIFF_LONG:
-		case TIFF_LONG8:
-		case TIFF_IFD:
-		case TIFF_IFD8:
-			break;
-		default:
-			return(TIFFReadDirEntryErrType);
-	}
-	err=TIFFReadDirEntryArray(tif,direntry,&count,8,&origdata);
-	if ((err!=TIFFReadDirEntryErrOk)||(origdata==0))
-	{
-		*value=0;
-		return(err);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_LONG8:
-		case TIFF_IFD8:
-			*value=(uint64*)origdata;
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabArrayOfLong8(*value,count);
-			return(TIFFReadDirEntryErrOk);
-	}
-	data=(uint64*)_TIFFmalloc(count*8);
-	if (data==0)
-	{
-		_TIFFfree(origdata);
-		return(TIFFReadDirEntryErrAlloc);
-	}
-	switch (direntry->tdir_type)
-	{
-		case TIFF_LONG:
-		case TIFF_IFD:
-			{
-				uint32* ma;
-				uint64* mb;
-				uint32 n;
-				ma=(uint32*)origdata;
-				mb=data;
-				for (n=0; n<count; n++)
-				{
-					if (tif->tif_flags&TIFF_SWAB)
-						TIFFSwabLong(ma);
-					*mb++=(uint64)(*ma++);
-				}
-			}
-			break;
-	}
-	_TIFFfree(origdata);
-	*value=data;
-	return(TIFFReadDirEntryErrOk);
+    enum TIFFReadDirEntryErr err;
+    uint32_t count;
+    void *origdata;
+    double *data;
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        case TIFF_SBYTE:
+        case TIFF_SHORT:
+        case TIFF_SSHORT:
+        case TIFF_LONG:
+        case TIFF_SLONG:
+        case TIFF_LONG8:
+        case TIFF_SLONG8:
+        case TIFF_RATIONAL:
+        case TIFF_SRATIONAL:
+        case TIFF_FLOAT:
+        case TIFF_DOUBLE:
+            break;
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+    err = TIFFReadDirEntryArray(tif, direntry, &count, 8, &origdata);
+    if ((err != TIFFReadDirEntryErrOk) || (origdata == 0))
+    {
+        *value = 0;
+        return (err);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_DOUBLE:
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabArrayOfLong8((uint64_t *)origdata, count);
+            TIFFCvtIEEEDoubleToNative(tif, count, (double *)origdata);
+            *value = (double *)origdata;
+            return (TIFFReadDirEntryErrOk);
+    }
+    data = (double *)_TIFFmallocExt(tif, count * sizeof(double));
+    if (data == 0)
+    {
+        _TIFFfreeExt(tif, origdata);
+        return (TIFFReadDirEntryErrAlloc);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_BYTE:
+        {
+            uint8_t *ma;
+            double *mb;
+            uint32_t n;
+            ma = (uint8_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+                *mb++ = (double)(*ma++);
+        }
+        break;
+        case TIFF_SBYTE:
+        {
+            int8_t *ma;
+            double *mb;
+            uint32_t n;
+            ma = (int8_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+                *mb++ = (double)(*ma++);
+        }
+        break;
+        case TIFF_SHORT:
+        {
+            uint16_t *ma;
+            double *mb;
+            uint32_t n;
+            ma = (uint16_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort(ma);
+                *mb++ = (double)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SSHORT:
+        {
+            int16_t *ma;
+            double *mb;
+            uint32_t n;
+            ma = (int16_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort((uint16_t *)ma);
+                *mb++ = (double)(*ma++);
+            }
+        }
+        break;
+        case TIFF_LONG:
+        {
+            uint32_t *ma;
+            double *mb;
+            uint32_t n;
+            ma = (uint32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(ma);
+                *mb++ = (double)(*ma++);
+            }
+        }
+        break;
+        case TIFF_SLONG:
+        {
+            int32_t *ma;
+            double *mb;
+            uint32_t n;
+            ma = (int32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong((uint32_t *)ma);
+                *mb++ = (double)(*ma++);
+            }
+        }
+        break;
+        case TIFF_LONG8:
+        {
+            uint64_t *ma;
+            double *mb;
+            uint32_t n;
+            ma = (uint64_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8(ma);
+#if defined(__WIN32__) && (_MSC_VER < 1500)
+                /*
+                 * XXX: MSVC 6.0 does not support
+                 * conversion of 64-bit integers into
+                 * floating point values.
+                 */
+                *mb++ = _TIFFUInt64ToDouble(*ma++);
+#else
+                *mb++ = (double)(*ma++);
+#endif
+            }
+        }
+        break;
+        case TIFF_SLONG8:
+        {
+            int64_t *ma;
+            double *mb;
+            uint32_t n;
+            ma = (int64_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8((uint64_t *)ma);
+                *mb++ = (double)(*ma++);
+            }
+        }
+        break;
+        case TIFF_RATIONAL:
+        {
+            uint32_t *ma;
+            uint32_t maa;
+            uint32_t mab;
+            double *mb;
+            uint32_t n;
+            ma = (uint32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(ma);
+                maa = *ma++;
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(ma);
+                mab = *ma++;
+                if (mab == 0)
+                    *mb++ = 0.0;
+                else
+                    *mb++ = (double)maa / (double)mab;
+            }
+        }
+        break;
+        case TIFF_SRATIONAL:
+        {
+            uint32_t *ma;
+            int32_t maa;
+            uint32_t mab;
+            double *mb;
+            uint32_t n;
+            ma = (uint32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(ma);
+                maa = *(int32_t *)ma;
+                ma++;
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(ma);
+                mab = *ma++;
+                if (mab == 0)
+                    *mb++ = 0.0;
+                else
+                    *mb++ = (double)maa / (double)mab;
+            }
+        }
+        break;
+        case TIFF_FLOAT:
+        {
+            float *ma;
+            double *mb;
+            uint32_t n;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabArrayOfLong((uint32_t *)origdata, count);
+            TIFFCvtIEEEFloatToNative(tif, count, (float *)origdata);
+            ma = (float *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+                *mb++ = (double)(*ma++);
+        }
+        break;
+    }
+    _TIFFfreeExt(tif, origdata);
+    *value = data;
+    return (TIFFReadDirEntryErrOk);
 }
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryPersampleShort(TIFF* tif, TIFFDirEntry* direntry, uint16* value)
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryIfd8Array(TIFF *tif, TIFFDirEntry *direntry, uint64_t **value)
 {
-	enum TIFFReadDirEntryErr err;
-	uint16* m;
-	uint16* na;
-	uint16 nb;
-	if (direntry->tdir_count<(uint64)tif->tif_dir.td_samplesperpixel)
-		return(TIFFReadDirEntryErrCount);
-	err=TIFFReadDirEntryShortArray(tif,direntry,&m);
-	if (err!=TIFFReadDirEntryErrOk || m == NULL)
-		return(err);
-	na=m;
-	nb=tif->tif_dir.td_samplesperpixel;
-	*value=*na++;
-	nb--;
-	while (nb>0)
-	{
-		if (*na++!=*value)
-		{
-			err=TIFFReadDirEntryErrPsdif;
-			break;
-		}
-		nb--;
-	}
-	_TIFFfree(m);
-	return(err);
+    enum TIFFReadDirEntryErr err;
+    uint32_t count;
+    void *origdata;
+    uint64_t *data;
+    switch (direntry->tdir_type)
+    {
+        case TIFF_LONG:
+        case TIFF_LONG8:
+        case TIFF_IFD:
+        case TIFF_IFD8:
+            break;
+        default:
+            return (TIFFReadDirEntryErrType);
+    }
+    err = TIFFReadDirEntryArray(tif, direntry, &count, 8, &origdata);
+    if ((err != TIFFReadDirEntryErrOk) || (origdata == 0))
+    {
+        *value = 0;
+        return (err);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_LONG8:
+        case TIFF_IFD8:
+            *value = (uint64_t *)origdata;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabArrayOfLong8(*value, count);
+            return (TIFFReadDirEntryErrOk);
+    }
+    data = (uint64_t *)_TIFFmallocExt(tif, count * 8);
+    if (data == 0)
+    {
+        _TIFFfreeExt(tif, origdata);
+        return (TIFFReadDirEntryErrAlloc);
+    }
+    switch (direntry->tdir_type)
+    {
+        case TIFF_LONG:
+        case TIFF_IFD:
+        {
+            uint32_t *ma;
+            uint64_t *mb;
+            uint32_t n;
+            ma = (uint32_t *)origdata;
+            mb = data;
+            for (n = 0; n < count; n++)
+            {
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(ma);
+                *mb++ = (uint64_t)(*ma++);
+            }
+        }
+        break;
+    }
+    _TIFFfreeExt(tif, origdata);
+    *value = data;
+    return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryPersampleShort(TIFF *tif, TIFFDirEntry *direntry,
+                               uint16_t *value)
+{
+    enum TIFFReadDirEntryErr err;
+    uint16_t *m;
+    uint16_t *na;
+    uint16_t nb;
+    if (direntry->tdir_count < (uint64_t)tif->tif_dir.td_samplesperpixel)
+        return (TIFFReadDirEntryErrCount);
+    err = TIFFReadDirEntryShortArray(tif, direntry, &m);
+    if (err != TIFFReadDirEntryErrOk || m == NULL)
+        return (err);
+    na = m;
+    nb = tif->tif_dir.td_samplesperpixel;
+    *value = *na++;
+    nb--;
+    while (nb > 0)
+    {
+        if (*na++ != *value)
+        {
+            err = TIFFReadDirEntryErrPsdif;
+            break;
+        }
+        nb--;
+    }
+    _TIFFfreeExt(tif, m);
+    return (err);
+}
+
+static void TIFFReadDirEntryCheckedByte(TIFF *tif, TIFFDirEntry *direntry,
+                                        uint8_t *value)
+{
+    (void)tif;
+    *value = *(uint8_t *)(&direntry->tdir_offset);
+}
+
+static void TIFFReadDirEntryCheckedSbyte(TIFF *tif, TIFFDirEntry *direntry,
+                                         int8_t *value)
+{
+    (void)tif;
+    *value = *(int8_t *)(&direntry->tdir_offset);
+}
+
+static void TIFFReadDirEntryCheckedShort(TIFF *tif, TIFFDirEntry *direntry,
+                                         uint16_t *value)
+{
+    *value = direntry->tdir_offset.toff_short;
+    /* *value=*(uint16_t*)(&direntry->tdir_offset); */
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabShort(value);
+}
+
+static void TIFFReadDirEntryCheckedSshort(TIFF *tif, TIFFDirEntry *direntry,
+                                          int16_t *value)
+{
+    *value = *(int16_t *)(&direntry->tdir_offset);
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabShort((uint16_t *)value);
+}
+
+static void TIFFReadDirEntryCheckedLong(TIFF *tif, TIFFDirEntry *direntry,
+                                        uint32_t *value)
+{
+    *value = *(uint32_t *)(&direntry->tdir_offset);
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabLong(value);
+}
+
+static void TIFFReadDirEntryCheckedSlong(TIFF *tif, TIFFDirEntry *direntry,
+                                         int32_t *value)
+{
+    *value = *(int32_t *)(&direntry->tdir_offset);
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabLong((uint32_t *)value);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckedLong8(TIFF *tif, TIFFDirEntry *direntry, uint64_t *value)
+{
+    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, value);
+        if (err != TIFFReadDirEntryErrOk)
+            return (err);
+    }
+    else
+        *value = direntry->tdir_offset.toff_long8;
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabLong8(value);
+    return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckedSlong8(TIFF *tif, TIFFDirEntry *direntry, int64_t *value)
+{
+    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, value);
+        if (err != TIFFReadDirEntryErrOk)
+            return (err);
+    }
+    else
+        *value = *(int64_t *)(&direntry->tdir_offset);
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabLong8((uint64_t *)value);
+    return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckedRational(TIFF *tif, TIFFDirEntry *direntry,
+                                double *value)
+{
+    UInt64Aligned_t m;
+
+    assert(sizeof(double) == 8);
+    assert(sizeof(uint64_t) == 8);
+    assert(sizeof(uint32_t) == 4);
+    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);
+    /* Not completely sure what we should do when m.i[1]==0, but some */
+    /* sanitizers do not like division by 0.0: */
+    /* http://bugzilla.maptools.org/show_bug.cgi?id=2644 */
+    if (m.i[0] == 0 || m.i[1] == 0)
+        *value = 0.0;
+    else
+        *value = (double)m.i[0] / (double)m.i[1];
+    return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckedSrational(TIFF *tif, TIFFDirEntry *direntry,
+                                 double *value)
+{
+    UInt64Aligned_t m;
+    assert(sizeof(double) == 8);
+    assert(sizeof(uint64_t) == 8);
+    assert(sizeof(int32_t) == 4);
+    assert(sizeof(uint32_t) == 4);
+    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);
+    /* Not completely sure what we should do when m.i[1]==0, but some */
+    /* sanitizers do not like division by 0.0: */
+    /* http://bugzilla.maptools.org/show_bug.cgi?id=2644 */
+    if ((int32_t)m.i[0] == 0 || m.i[1] == 0)
+        *value = 0.0;
+    else
+        *value = (double)((int32_t)m.i[0]) / (double)m.i[1];
+    return (TIFFReadDirEntryErrOk);
 }
 
 #if 0
-static enum TIFFReadDirEntryErr TIFFReadDirEntryPersampleDouble(TIFF* tif, TIFFDirEntry* direntry, double* value)
-{
-	enum TIFFReadDirEntryErr err;
-	double* m;
-	double* na;
-	uint16 nb;
-	if (direntry->tdir_count<(uint64)tif->tif_dir.td_samplesperpixel)
-		return(TIFFReadDirEntryErrCount);
-	err=TIFFReadDirEntryDoubleArray(tif,direntry,&m);
-	if (err!=TIFFReadDirEntryErrOk)
-		return(err);
-	na=m;
-	nb=tif->tif_dir.td_samplesperpixel;
-	*value=*na++;
-	nb--;
-	while (nb>0)
-	{
-		if (*na++!=*value)
-		{
-			err=TIFFReadDirEntryErrPsdif;
-			break;
-		}
-		nb--;
-	}
-	_TIFFfree(m);
-	return(err);
-}
+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 TIFFReadDirEntryCheckedByte(TIFF* tif, TIFFDirEntry* direntry, uint8* value)
+static void TIFFReadDirEntryCheckedFloat(TIFF *tif, TIFFDirEntry *direntry,
+                                         float *value)
 {
-	(void) tif;
-	*value=*(uint8*)(&direntry->tdir_offset);
-}
-
-static void TIFFReadDirEntryCheckedSbyte(TIFF* tif, TIFFDirEntry* direntry, int8* value)
-{
-	(void) tif;
-	*value=*(int8*)(&direntry->tdir_offset);
-}
-
-static void TIFFReadDirEntryCheckedShort(TIFF* tif, TIFFDirEntry* direntry, uint16* value)
-{
-	*value = direntry->tdir_offset.toff_short;
-	/* *value=*(uint16*)(&direntry->tdir_offset); */
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabShort(value);
-}
-
-static void TIFFReadDirEntryCheckedSshort(TIFF* tif, TIFFDirEntry* direntry, int16* value)
-{
-	*value=*(int16*)(&direntry->tdir_offset);
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabShort((uint16*)value);
-}
-
-static void TIFFReadDirEntryCheckedLong(TIFF* tif, TIFFDirEntry* direntry, uint32* value)
-{
-	*value=*(uint32*)(&direntry->tdir_offset);
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabLong(value);
-}
-
-static void TIFFReadDirEntryCheckedSlong(TIFF* tif, TIFFDirEntry* direntry, int32* value)
-{
-	*value=*(int32*)(&direntry->tdir_offset);
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabLong((uint32*)value);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedLong8(TIFF* tif, TIFFDirEntry* direntry, uint64* value)
-{
-	if (!(tif->tif_flags&TIFF_BIGTIFF))
-	{
-		enum TIFFReadDirEntryErr err;
-		uint32 offset = direntry->tdir_offset.toff_long;
-		if (tif->tif_flags&TIFF_SWAB)
-			TIFFSwabLong(&offset);
-		err=TIFFReadDirEntryData(tif,offset,8,value);
-		if (err!=TIFFReadDirEntryErrOk)
-			return(err);
-	}
-	else
-		*value = direntry->tdir_offset.toff_long8;
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabLong8(value);
-	return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedSlong8(TIFF* tif, TIFFDirEntry* direntry, int64* value)
-{
-	if (!(tif->tif_flags&TIFF_BIGTIFF))
-	{
-		enum TIFFReadDirEntryErr err;
-		uint32 offset = direntry->tdir_offset.toff_long;
-		if (tif->tif_flags&TIFF_SWAB)
-			TIFFSwabLong(&offset);
-		err=TIFFReadDirEntryData(tif,offset,8,value);
-		if (err!=TIFFReadDirEntryErrOk)
-			return(err);
-	}
-	else
-		*value=*(int64*)(&direntry->tdir_offset);
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabLong8((uint64*)value);
-	return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedRational(TIFF* tif, TIFFDirEntry* direntry, double* value)
-{
-	UInt64Aligned_t m;
-
-	assert(sizeof(double)==8);
-	assert(sizeof(uint64)==8);
-	assert(sizeof(uint32)==4);
-	if (!(tif->tif_flags&TIFF_BIGTIFF))
-	{
-		enum TIFFReadDirEntryErr err;
-		uint32 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);
-        /* Not completely sure what we should do when m.i[1]==0, but some */
-        /* sanitizers do not like division by 0.0: */
-        /* http://bugzilla.maptools.org/show_bug.cgi?id=2644 */
-	if (m.i[0]==0 || m.i[1]==0)
-		*value=0.0;
-	else
-		*value=(double)m.i[0]/(double)m.i[1];
-	return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedSrational(TIFF* tif, TIFFDirEntry* direntry, double* value)
-{
-	UInt64Aligned_t m;
-	assert(sizeof(double)==8);
-	assert(sizeof(uint64)==8);
-	assert(sizeof(int32)==4);
-	assert(sizeof(uint32)==4);
-	if (!(tif->tif_flags&TIFF_BIGTIFF))
-	{
-		enum TIFFReadDirEntryErr err;
-		uint32 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);
-        /* Not completely sure what we should do when m.i[1]==0, but some */
-        /* sanitizers do not like division by 0.0: */
-        /* http://bugzilla.maptools.org/show_bug.cgi?id=2644 */
-	if ((int32)m.i[0]==0 || m.i[1]==0)
-		*value=0.0;
-	else
-		*value=(double)((int32)m.i[0])/(double)m.i[1];
-	return(TIFFReadDirEntryErrOk);
-}
-
-static void TIFFReadDirEntryCheckedFloat(TIFF* tif, TIFFDirEntry* direntry, float* value)
-{
-         union
-	 {
-	   float  f;
-	   uint32 i;
-	 } float_union;
-	assert(sizeof(float)==4);
-	assert(sizeof(uint32)==4);
-	assert(sizeof(float_union)==4);
-	float_union.i=*(uint32*)(&direntry->tdir_offset);
-	*value=float_union.f;
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabLong((uint32*)value);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedDouble(TIFF* tif, TIFFDirEntry* direntry, double* value)
-{
-	assert(sizeof(double)==8);
-	assert(sizeof(uint64)==8);
-	assert(sizeof(UInt64Aligned_t)==8);
-	if (!(tif->tif_flags&TIFF_BIGTIFF))
-	{
-		enum TIFFReadDirEntryErr err;
-		uint32 offset = direntry->tdir_offset.toff_long;
-		if (tif->tif_flags&TIFF_SWAB)
-			TIFFSwabLong(&offset);
-		err=TIFFReadDirEntryData(tif,offset,8,value);
-		if (err!=TIFFReadDirEntryErrOk)
-			return(err);
-	}
-	else
-	{
-	       UInt64Aligned_t uint64_union;
-	       uint64_union.l=direntry->tdir_offset.toff_long8;
-	       *value=uint64_union.d;
-	}
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabLong8((uint64*)value);
-	return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteSbyte(int8 value)
-{
-	if (value<0)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteShort(uint16 value)
-{
-	if (value>0xFF)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteSshort(int16 value)
-{
-	if ((value<0)||(value>0xFF))
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteLong(uint32 value)
-{
-	if (value>0xFF)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteSlong(int32 value)
-{
-	if ((value<0)||(value>0xFF))
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteLong8(uint64 value)
-{
-	if (value>0xFF)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteSlong8(int64 value)
-{
-	if ((value<0)||(value>0xFF))
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteByte(uint8 value)
-{
-	if (value>0x7F)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteShort(uint16 value)
-{
-	if (value>0x7F)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteSshort(int16 value)
-{
-	if ((value<-0x80)||(value>0x7F))
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteLong(uint32 value)
-{
-	if (value>0x7F)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteSlong(int32 value)
-{
-	if ((value<-0x80)||(value>0x7F))
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteLong8(uint64 value)
-{
-	if (value>0x7F)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteSlong8(int64 value)
-{
-	if ((value<-0x80)||(value>0x7F))
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortSbyte(int8 value)
-{
-	if (value<0)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortSshort(int16 value)
-{
-	if (value<0)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortLong(uint32 value)
-{
-	if (value>0xFFFF)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortSlong(int32 value)
-{
-	if ((value<0)||(value>0xFFFF))
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortLong8(uint64 value)
-{
-	if (value>0xFFFF)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortSlong8(int64 value)
-{
-	if ((value<0)||(value>0xFFFF))
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortShort(uint16 value)
-{
-	if (value>0x7FFF)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortLong(uint32 value)
-{
-	if (value>0x7FFF)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortSlong(int32 value)
-{
-	if ((value<-0x8000)||(value>0x7FFF))
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortLong8(uint64 value)
-{
-	if (value>0x7FFF)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortSlong8(int64 value)
-{
-	if ((value<-0x8000)||(value>0x7FFF))
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLongSbyte(int8 value)
-{
-	if (value<0)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLongSshort(int16 value)
-{
-	if (value<0)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
-}
-
-static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLongSlong(int32 value)
-{
-	if (value<0)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
+    union
+    {
+        float f;
+        uint32_t i;
+    } float_union;
+    assert(sizeof(float) == 4);
+    assert(sizeof(uint32_t) == 4);
+    assert(sizeof(float_union) == 4);
+    float_union.i = *(uint32_t *)(&direntry->tdir_offset);
+    *value = float_union.f;
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabLong((uint32_t *)value);
 }
 
 static enum TIFFReadDirEntryErr
-TIFFReadDirEntryCheckRangeLongLong8(uint64 value)
+TIFFReadDirEntryCheckedDouble(TIFF *tif, TIFFDirEntry *direntry, double *value)
 {
-	if (value > TIFF_UINT32_MAX)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
+    assert(sizeof(double) == 8);
+    assert(sizeof(uint64_t) == 8);
+    assert(sizeof(UInt64Aligned_t) == 8);
+    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, value);
+        if (err != TIFFReadDirEntryErrOk)
+            return (err);
+    }
+    else
+    {
+        UInt64Aligned_t uint64_union;
+        uint64_union.l = direntry->tdir_offset.toff_long8;
+        *value = uint64_union.d;
+    }
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabLong8((uint64_t *)value);
+    return (TIFFReadDirEntryErrOk);
 }
 
 static enum TIFFReadDirEntryErr
-TIFFReadDirEntryCheckRangeLongSlong8(int64 value)
+TIFFReadDirEntryCheckRangeByteSbyte(int8_t value)
 {
-	if ((value < 0) || (value > (int64) TIFF_UINT32_MAX))
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
+    if (value < 0)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
 }
 
 static enum TIFFReadDirEntryErr
-TIFFReadDirEntryCheckRangeSlongLong(uint32 value)
+TIFFReadDirEntryCheckRangeByteShort(uint16_t value)
 {
-	if (value > 0x7FFFFFFFUL)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
+    if (value > 0xFF)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeByteSshort(int16_t value)
+{
+    if ((value < 0) || (value > 0xFF))
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeByteLong(uint32_t value)
+{
+    if (value > 0xFF)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeByteSlong(int32_t value)
+{
+    if ((value < 0) || (value > 0xFF))
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeByteLong8(uint64_t value)
+{
+    if (value > 0xFF)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeByteSlong8(int64_t value)
+{
+    if ((value < 0) || (value > 0xFF))
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSbyteByte(uint8_t value)
+{
+    if (value > 0x7F)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSbyteShort(uint16_t value)
+{
+    if (value > 0x7F)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSbyteSshort(int16_t value)
+{
+    if ((value < -0x80) || (value > 0x7F))
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSbyteLong(uint32_t value)
+{
+    if (value > 0x7F)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSbyteSlong(int32_t value)
+{
+    if ((value < -0x80) || (value > 0x7F))
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSbyteLong8(uint64_t value)
+{
+    if (value > 0x7F)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSbyteSlong8(int64_t value)
+{
+    if ((value < -0x80) || (value > 0x7F))
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeShortSbyte(int8_t value)
+{
+    if (value < 0)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeShortSshort(int16_t value)
+{
+    if (value < 0)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeShortLong(uint32_t value)
+{
+    if (value > 0xFFFF)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeShortSlong(int32_t value)
+{
+    if ((value < 0) || (value > 0xFFFF))
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeShortLong8(uint64_t value)
+{
+    if (value > 0xFFFF)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeShortSlong8(int64_t value)
+{
+    if ((value < 0) || (value > 0xFFFF))
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSshortShort(uint16_t value)
+{
+    if (value > 0x7FFF)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSshortLong(uint32_t value)
+{
+    if (value > 0x7FFF)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSshortSlong(int32_t value)
+{
+    if ((value < -0x8000) || (value > 0x7FFF))
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSshortLong8(uint64_t value)
+{
+    if (value > 0x7FFF)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSshortSlong8(int64_t value)
+{
+    if ((value < -0x8000) || (value > 0x7FFF))
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeLongSbyte(int8_t value)
+{
+    if (value < 0)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeLongSshort(int16_t value)
+{
+    if (value < 0)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeLongSlong(int32_t value)
+{
+    if (value < 0)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeLongLong8(uint64_t value)
+{
+    if (value > UINT32_MAX)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeLongSlong8(int64_t value)
+{
+    if ((value < 0) || (value > (int64_t)UINT32_MAX))
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
+}
+
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckRangeSlongLong(uint32_t value)
+{
+    if (value > 0x7FFFFFFFUL)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
 }
 
 /* Check that the 8-byte unsigned value can fit in a 4-byte unsigned range */
 static enum TIFFReadDirEntryErr
-TIFFReadDirEntryCheckRangeSlongLong8(uint64 value)
+TIFFReadDirEntryCheckRangeSlongLong8(uint64_t value)
 {
-	if (value > 0x7FFFFFFF)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
+    if (value > 0x7FFFFFFF)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
 }
 
 /* Check that the 8-byte signed value can fit in a 4-byte signed range */
 static enum TIFFReadDirEntryErr
-TIFFReadDirEntryCheckRangeSlongSlong8(int64 value)
+TIFFReadDirEntryCheckRangeSlongSlong8(int64_t value)
 {
-        if ((value < 0-((int64) 0x7FFFFFFF+1)) || (value > 0x7FFFFFFF))
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
+    if ((value < 0 - ((int64_t)0x7FFFFFFF + 1)) || (value > 0x7FFFFFFF))
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
 }
 
 static enum TIFFReadDirEntryErr
-TIFFReadDirEntryCheckRangeLong8Sbyte(int8 value)
+TIFFReadDirEntryCheckRangeLong8Sbyte(int8_t value)
 {
-	if (value < 0)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
+    if (value < 0)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
 }
 
 static enum TIFFReadDirEntryErr
-TIFFReadDirEntryCheckRangeLong8Sshort(int16 value)
+TIFFReadDirEntryCheckRangeLong8Sshort(int16_t value)
 {
-	if (value < 0)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
+    if (value < 0)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
 }
 
 static enum TIFFReadDirEntryErr
-TIFFReadDirEntryCheckRangeLong8Slong(int32 value)
+TIFFReadDirEntryCheckRangeLong8Slong(int32_t value)
 {
-	if (value < 0)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
+    if (value < 0)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
 }
 
 static enum TIFFReadDirEntryErr
-TIFFReadDirEntryCheckRangeLong8Slong8(int64 value)
+TIFFReadDirEntryCheckRangeLong8Slong8(int64_t value)
 {
-	if (value < 0)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
+    if (value < 0)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
 }
 
 static enum TIFFReadDirEntryErr
-TIFFReadDirEntryCheckRangeSlong8Long8(uint64 value)
+TIFFReadDirEntryCheckRangeSlong8Long8(uint64_t value)
 {
-	if (value > TIFF_INT64_MAX)
-		return(TIFFReadDirEntryErrRange);
-	else
-		return(TIFFReadDirEntryErrOk);
+    if (value > INT64_MAX)
+        return (TIFFReadDirEntryErrRange);
+    else
+        return (TIFFReadDirEntryErrOk);
 }
 
-static enum TIFFReadDirEntryErr
-TIFFReadDirEntryData(TIFF* tif, uint64 offset, tmsize_t size, void* dest)
+static enum TIFFReadDirEntryErr TIFFReadDirEntryData(TIFF *tif, uint64_t offset,
+                                                     tmsize_t size, void *dest)
 {
-	assert(size>0);
-	if (!isMapped(tif)) {
-		if (!SeekOK(tif,offset))
-			return(TIFFReadDirEntryErrIo);
-		if (!ReadOK(tif,dest,size))
-			return(TIFFReadDirEntryErrIo);
-	} else {
-		size_t ma,mb;
-		ma=(size_t)offset;
-                if( (uint64)ma!=offset ||
-                    ma > (~(size_t)0) - (size_t)size )
-                {
-                    return TIFFReadDirEntryErrIo;
-                }
-		mb=ma+size;
-		if (mb > (size_t)tif->tif_size)
-			return(TIFFReadDirEntryErrIo);
-		_TIFFmemcpy(dest,tif->tif_base+ma,size);
-	}
-	return(TIFFReadDirEntryErrOk);
+    assert(size > 0);
+    if (!isMapped(tif))
+    {
+        if (!SeekOK(tif, offset))
+            return (TIFFReadDirEntryErrIo);
+        if (!ReadOK(tif, dest, size))
+            return (TIFFReadDirEntryErrIo);
+    }
+    else
+    {
+        size_t ma, mb;
+        ma = (size_t)offset;
+        if ((uint64_t)ma != offset || ma > (~(size_t)0) - (size_t)size)
+        {
+            return TIFFReadDirEntryErrIo;
+        }
+        mb = ma + size;
+        if (mb > (uint64_t)tif->tif_size)
+            return (TIFFReadDirEntryErrIo);
+        _TIFFmemcpy(dest, tif->tif_base + ma, size);
+    }
+    return (TIFFReadDirEntryErrOk);
 }
 
-static void TIFFReadDirEntryOutputErr(TIFF* tif, enum TIFFReadDirEntryErr err, const char* module, const char* tagname, int recover)
+static void TIFFReadDirEntryOutputErr(TIFF *tif, enum TIFFReadDirEntryErr err,
+                                      const char *module, const char *tagname,
+                                      int recover)
 {
-	if (!recover) {
-		switch (err) {
-			case TIFFReadDirEntryErrCount:
-				TIFFErrorExt(tif->tif_clientdata, module,
-					     "Incorrect count for \"%s\"",
-					     tagname);
-				break;
-			case TIFFReadDirEntryErrType:
-				TIFFErrorExt(tif->tif_clientdata, module,
-					     "Incompatible type for \"%s\"",
-					     tagname);
-				break;
-			case TIFFReadDirEntryErrIo:
-				TIFFErrorExt(tif->tif_clientdata, module,
-					     "IO error during reading of \"%s\"",
-					     tagname);
-				break;
-			case TIFFReadDirEntryErrRange:
-				TIFFErrorExt(tif->tif_clientdata, module,
-					     "Incorrect value for \"%s\"",
-					     tagname);
-				break;
-			case TIFFReadDirEntryErrPsdif:
-				TIFFErrorExt(tif->tif_clientdata, module,
-			"Cannot handle different values per sample for \"%s\"",
-					     tagname);
-				break;
-			case TIFFReadDirEntryErrSizesan:
-				TIFFErrorExt(tif->tif_clientdata, module,
-				"Sanity check on size of \"%s\" value failed",
-					     tagname);
-				break;
-			case TIFFReadDirEntryErrAlloc:
-				TIFFErrorExt(tif->tif_clientdata, module,
-					     "Out of memory reading of \"%s\"",
-					     tagname);
-				break;
-			default:
-				assert(0);   /* we should never get here */
-				break;
-		}
-	} else {
-		switch (err) {
-			case TIFFReadDirEntryErrCount:
-				TIFFWarningExt(tif->tif_clientdata, module,
-				"Incorrect count for \"%s\"; tag ignored",
-					     tagname);
-				break;
-			case TIFFReadDirEntryErrType:
-				TIFFWarningExt(tif->tif_clientdata, module,
-				"Incompatible type for \"%s\"; tag ignored",
-					       tagname);
-				break;
-			case TIFFReadDirEntryErrIo:
-				TIFFWarningExt(tif->tif_clientdata, module,
-			"IO error during reading of \"%s\"; tag ignored",
-					       tagname);
-				break;
-			case TIFFReadDirEntryErrRange:
-				TIFFWarningExt(tif->tif_clientdata, module,
-				"Incorrect value for \"%s\"; tag ignored",
-					       tagname);
-				break;
-			case TIFFReadDirEntryErrPsdif:
-				TIFFWarningExt(tif->tif_clientdata, module,
-	"Cannot handle different values per sample for \"%s\"; tag ignored",
-					       tagname);
-				break;
-			case TIFFReadDirEntryErrSizesan:
-				TIFFWarningExt(tif->tif_clientdata, module,
-		"Sanity check on size of \"%s\" value failed; tag ignored",
-					       tagname);
-				break;
-			case TIFFReadDirEntryErrAlloc:
-				TIFFWarningExt(tif->tif_clientdata, module,
-				"Out of memory reading of \"%s\"; tag ignored",
-					       tagname);
-				break;
-			default:
-				assert(0);   /* we should never get here */
-				break;
-		}
-	}
+    if (!recover)
+    {
+        switch (err)
+        {
+            case TIFFReadDirEntryErrCount:
+                TIFFErrorExtR(tif, module, "Incorrect count for \"%s\"",
+                              tagname);
+                break;
+            case TIFFReadDirEntryErrType:
+                TIFFErrorExtR(tif, module, "Incompatible type for \"%s\"",
+                              tagname);
+                break;
+            case TIFFReadDirEntryErrIo:
+                TIFFErrorExtR(tif, module, "IO error during reading of \"%s\"",
+                              tagname);
+                break;
+            case TIFFReadDirEntryErrRange:
+                TIFFErrorExtR(tif, module, "Incorrect value for \"%s\"",
+                              tagname);
+                break;
+            case TIFFReadDirEntryErrPsdif:
+                TIFFErrorExtR(
+                    tif, module,
+                    "Cannot handle different values per sample for \"%s\"",
+                    tagname);
+                break;
+            case TIFFReadDirEntryErrSizesan:
+                TIFFErrorExtR(tif, module,
+                              "Sanity check on size of \"%s\" value failed",
+                              tagname);
+                break;
+            case TIFFReadDirEntryErrAlloc:
+                TIFFErrorExtR(tif, module, "Out of memory reading of \"%s\"",
+                              tagname);
+                break;
+            default:
+                assert(0); /* we should never get here */
+                break;
+        }
+    }
+    else
+    {
+        switch (err)
+        {
+            case TIFFReadDirEntryErrCount:
+                TIFFWarningExtR(tif, module,
+                                "Incorrect count for \"%s\"; tag ignored",
+                                tagname);
+                break;
+            case TIFFReadDirEntryErrType:
+                TIFFWarningExtR(tif, module,
+                                "Incompatible type for \"%s\"; tag ignored",
+                                tagname);
+                break;
+            case TIFFReadDirEntryErrIo:
+                TIFFWarningExtR(
+                    tif, module,
+                    "IO error during reading of \"%s\"; tag ignored", tagname);
+                break;
+            case TIFFReadDirEntryErrRange:
+                TIFFWarningExtR(tif, module,
+                                "Incorrect value for \"%s\"; tag ignored",
+                                tagname);
+                break;
+            case TIFFReadDirEntryErrPsdif:
+                TIFFWarningExtR(tif, module,
+                                "Cannot handle different values per sample for "
+                                "\"%s\"; tag ignored",
+                                tagname);
+                break;
+            case TIFFReadDirEntryErrSizesan:
+                TIFFWarningExtR(
+                    tif, module,
+                    "Sanity check on size of \"%s\" value failed; tag ignored",
+                    tagname);
+                break;
+            case TIFFReadDirEntryErrAlloc:
+                TIFFWarningExtR(tif, module,
+                                "Out of memory reading of \"%s\"; tag ignored",
+                                tagname);
+                break;
+            default:
+                assert(0); /* we should never get here */
+                break;
+        }
+    }
 }
 
 /*
@@ -3500,1209 +4028,1717 @@
  * type. 0 is returned if photometric type isn't supported or no default value
  * is defined by the specification.
  */
-static int _TIFFGetMaxColorChannels( uint16 photometric )
+static int _TIFFGetMaxColorChannels(uint16_t photometric)
 {
-    switch (photometric) {
-	case PHOTOMETRIC_PALETTE:
-	case PHOTOMETRIC_MINISWHITE:
-	case PHOTOMETRIC_MINISBLACK:
+    switch (photometric)
+    {
+        case PHOTOMETRIC_PALETTE:
+        case PHOTOMETRIC_MINISWHITE:
+        case PHOTOMETRIC_MINISBLACK:
             return 1;
-	case PHOTOMETRIC_YCBCR:
-	case PHOTOMETRIC_RGB:
-	case PHOTOMETRIC_CIELAB:
-	case PHOTOMETRIC_LOGLUV:
-	case PHOTOMETRIC_ITULAB:
-	case PHOTOMETRIC_ICCLAB:
+        case PHOTOMETRIC_YCBCR:
+        case PHOTOMETRIC_RGB:
+        case PHOTOMETRIC_CIELAB:
+        case PHOTOMETRIC_LOGLUV:
+        case PHOTOMETRIC_ITULAB:
+        case PHOTOMETRIC_ICCLAB:
             return 3;
-	case PHOTOMETRIC_SEPARATED:
-	case PHOTOMETRIC_MASK:
+        case PHOTOMETRIC_SEPARATED:
+        case PHOTOMETRIC_MASK:
             return 4;
-	case PHOTOMETRIC_LOGL:
-	case PHOTOMETRIC_CFA:
-	default:
+        case PHOTOMETRIC_LOGL:
+        case PHOTOMETRIC_CFA:
+        default:
             return 0;
     }
 }
 
-static int ByteCountLooksBad(TIFF* tif)
+static int ByteCountLooksBad(TIFF *tif)
 {
     /*
-        * Assume we have wrong StripByteCount value (in case
-        * of single strip) in following cases:
-        *   - it is equal to zero along with StripOffset;
-        *   - it is larger than file itself (in case of uncompressed
-        *     image);
-        *   - it is smaller than the size of the bytes per row
-        *     multiplied on the number of rows.  The last case should
-        *     not be checked in the case of writing new image,
-        *     because we may do not know the exact strip size
-        *     until the whole image will be written and directory
-        *     dumped out.
-        */
-    uint64 bytecount = TIFFGetStrileByteCount(tif, 0);
-    uint64 offset = TIFFGetStrileOffset(tif, 0);
-    uint64 filesize;
+     * Assume we have wrong StripByteCount value (in case
+     * of single strip) in following cases:
+     *   - it is equal to zero along with StripOffset;
+     *   - it is larger than file itself (in case of uncompressed
+     *     image);
+     *   - it is smaller than the size of the bytes per row
+     *     multiplied on the number of rows.  The last case should
+     *     not be checked in the case of writing new image,
+     *     because we may do not know the exact strip size
+     *     until the whole image will be written and directory
+     *     dumped out.
+     */
+    uint64_t bytecount = TIFFGetStrileByteCount(tif, 0);
+    uint64_t offset = TIFFGetStrileOffset(tif, 0);
+    uint64_t filesize;
 
-    if( offset == 0 )
+    if (offset == 0)
         return 0;
     if (bytecount == 0)
         return 1;
-    if ( tif->tif_dir.td_compression != COMPRESSION_NONE )
+    if (tif->tif_dir.td_compression != COMPRESSION_NONE)
         return 0;
     filesize = TIFFGetFileSize(tif);
-    if( offset <= filesize && bytecount > filesize - offset )
+    if (offset <= filesize && bytecount > filesize - offset)
         return 1;
-    if( tif->tif_mode == O_RDONLY )
+    if (tif->tif_mode == O_RDONLY)
     {
-        uint64 scanlinesize = TIFFScanlineSize64(tif);
-        if( tif->tif_dir.td_imagelength > 0 &&
-            scanlinesize > TIFF_UINT64_MAX / tif->tif_dir.td_imagelength )
+        uint64_t scanlinesize = TIFFScanlineSize64(tif);
+        if (tif->tif_dir.td_imagelength > 0 &&
+            scanlinesize > UINT64_MAX / tif->tif_dir.td_imagelength)
         {
             return 1;
         }
-        if( bytecount < scanlinesize * tif->tif_dir.td_imagelength)
+        if (bytecount < scanlinesize * tif->tif_dir.td_imagelength)
             return 1;
     }
     return 0;
 }
 
-
 /*
  * Read the next TIFF directory from a file and convert it to the internal
  * format. We read directories sequentially.
  */
-int
-TIFFReadDirectory(TIFF* tif)
+int TIFFReadDirectory(TIFF *tif)
 {
-	static const char module[] = "TIFFReadDirectory";
-	TIFFDirEntry* dir;
-	uint16 dircount;
-	TIFFDirEntry* dp;
-	uint16 di;
-	const TIFFField* fip;
-	uint32 fii=FAILED_FII;
-        toff_t nextdiroff;
+    static const char module[] = "TIFFReadDirectory";
+    TIFFDirEntry *dir;
+    uint16_t dircount;
+    TIFFDirEntry *dp;
+    uint16_t di;
+    const TIFFField *fip;
+    uint32_t fii = FAILED_FII;
+    toff_t nextdiroff;
     int bitspersample_read = FALSE;
-        int color_channels;
+    int color_channels;
 
-	tif->tif_diroff=tif->tif_nextdiroff;
-	if (!TIFFCheckDirOffset(tif,tif->tif_nextdiroff))
-		return 0;           /* last offset or bad offset (IFD looping) */
-	(*tif->tif_cleanup)(tif);   /* cleanup any previous compression state */
-	tif->tif_curdir++;
-        nextdiroff = tif->tif_nextdiroff;
-	dircount=TIFFFetchDirectory(tif,nextdiroff,&dir,&tif->tif_nextdiroff);
-	if (!dircount)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,
-		    "Failed to read directory at offset " TIFF_UINT64_FORMAT,nextdiroff);
-		return 0;
-	}
-	TIFFReadDirectoryCheckOrder(tif,dir,dircount);
-
-        /*
-         * Mark duplicates of any tag to be ignored (bugzilla 1994)
-         * to avoid certain pathological problems.
+    if (tif->tif_nextdiroff == 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.
          */
-	{
-		TIFFDirEntry* ma;
-		uint16 mb;
-		for (ma=dir, mb=0; mb<dircount; ma++, mb++)
-		{
-			TIFFDirEntry* na;
-			uint16 nb;
-			for (na=ma+1, nb=mb+1; nb<dircount; na++, nb++)
-			{
-				if (ma->tdir_tag == na->tdir_tag) {
-					na->tdir_ignore = TRUE;
-				}
-			}
-		}
-	}
-        
-	tif->tif_flags &= ~TIFF_BEENWRITING;    /* reset before new dir */
-	tif->tif_flags &= ~TIFF_BUF4WRITE;      /* reset before new dir */
-	tif->tif_flags &= ~TIFF_CHOPPEDUPARRAYS;
+        tif->tif_diroff = tif->tif_nextdiroff;
+        return 0;
+    }
 
-	/* free any old stuff and reinit */
-	TIFFFreeDirectory(tif);
-	TIFFDefaultDirectory(tif);
-	/*
-	 * Electronic Arts writes gray-scale TIFF files
-	 * without a PlanarConfiguration directory entry.
-	 * Thus we setup a default value here, even though
-	 * the TIFF spec says there is no default value.
-	 */
-	TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG);
-	/*
-	 * Setup default value and then make a pass over
-	 * the fields to check type and tag information,
-	 * and to extract info required to size data
-	 * structures.  A second pass is made afterwards
-	 * to read in everything not taken in the first pass.
-	 * But we must process the Compression tag first
-	 * in order to merge in codec-private tag definitions (otherwise
-	 * we may get complaints about unknown tags).  However, the
-	 * Compression tag may be dependent on the SamplesPerPixel
-	 * tag value because older TIFF specs permitted Compression
-	 * to be written as a SamplesPerPixel-count tag entry.
-	 * Thus if we don't first figure out the correct SamplesPerPixel
-	 * tag value then we may end up ignoring the Compression tag
-	 * value because it has an incorrect count value (if the
-	 * true value of SamplesPerPixel is not 1).
-	 */
-	dp=TIFFReadDirectoryFindEntry(tif,dir,dircount,TIFFTAG_SAMPLESPERPIXEL);
-	if (dp)
-	{
-		if (!TIFFFetchNormalTag(tif,dp,0))
-			goto bad;
-		dp->tdir_ignore = TRUE;
-	}
-	dp=TIFFReadDirectoryFindEntry(tif,dir,dircount,TIFFTAG_COMPRESSION);
-	if (dp)
-	{
-		/*
-		 * The 5.0 spec says the Compression tag has one value, while
-		 * earlier specs say it has one value per sample.  Because of
-		 * this, we accept the tag if one value is supplied with either
-		 * count.
-		 */
-		uint16 value;
-		enum TIFFReadDirEntryErr err;
-		err=TIFFReadDirEntryShort(tif,dp,&value);
-		if (err==TIFFReadDirEntryErrCount)
-			err=TIFFReadDirEntryPersampleShort(tif,dp,&value);
-		if (err!=TIFFReadDirEntryErrOk)
-		{
-			TIFFReadDirEntryOutputErr(tif,err,module,"Compression",0);
-			goto bad;
-		}
-		if (!TIFFSetField(tif,TIFFTAG_COMPRESSION,value))
-			goto bad;
-		dp->tdir_ignore = TRUE;
-	}
-	else
-	{
-		if (!TIFFSetField(tif,TIFFTAG_COMPRESSION,COMPRESSION_NONE))
-			goto bad;
-	}
-	/*
-	 * First real pass over the directory.
-	 */
-	for (di=0, dp=dir; di<dircount; di++, dp++)
-	{
-		if (!dp->tdir_ignore)
-		{
-			TIFFReadDirectoryFindFieldInfo(tif,dp->tdir_tag,&fii);
-			if (fii == FAILED_FII)
-			{
-				TIFFWarningExt(tif->tif_clientdata, module,
-				    "Unknown field with tag %d (0x%x) encountered",
-				    dp->tdir_tag,dp->tdir_tag);
-				/* the following knowingly leaks the 
-				   anonymous field structure */
-				if (!_TIFFMergeFields(tif,
-					_TIFFCreateAnonField(tif,
-						dp->tdir_tag,
-						(TIFFDataType) dp->tdir_type),
-					1)) {
-					TIFFWarningExt(tif->tif_clientdata,
-					    module,
-					    "Registering anonymous field with tag %d (0x%x) failed",
-					    dp->tdir_tag,
-					    dp->tdir_tag);
-					dp->tdir_ignore = TRUE;
-				} else {
-					TIFFReadDirectoryFindFieldInfo(tif,dp->tdir_tag,&fii);
-					assert(fii != FAILED_FII);
-				}
-			}
-		}
-		if (!dp->tdir_ignore)
-		{
-			fip=tif->tif_fields[fii];
-			if (fip->field_bit==FIELD_IGNORE)
-				dp->tdir_ignore = TRUE;
-			else
-			{
-				switch (dp->tdir_tag)
-				{
-					case TIFFTAG_STRIPOFFSETS:
-					case TIFFTAG_STRIPBYTECOUNTS:
-					case TIFFTAG_TILEOFFSETS:
-					case TIFFTAG_TILEBYTECOUNTS:
-						TIFFSetFieldBit(tif,fip->field_bit);
-						break;
-					case TIFFTAG_IMAGEWIDTH:
-					case TIFFTAG_IMAGELENGTH:
-					case TIFFTAG_IMAGEDEPTH:
-					case TIFFTAG_TILELENGTH:
-					case TIFFTAG_TILEWIDTH:
-					case TIFFTAG_TILEDEPTH:
-					case TIFFTAG_PLANARCONFIG:
-					case TIFFTAG_ROWSPERSTRIP:
-					case TIFFTAG_EXTRASAMPLES:
-						if (!TIFFFetchNormalTag(tif,dp,0))
-							goto bad;
-						dp->tdir_ignore = TRUE;
-						break;
-					default:
-						if( !_TIFFCheckFieldIsValidForCodec(tif, dp->tdir_tag) )
-							dp->tdir_ignore = TRUE;
-						break;
-				}
-			}
-		}
-	}
-	/*
-	 * XXX: OJPEG hack.
-	 * If a) compression is OJPEG, b) planarconfig tag says it's separate,
-	 * c) strip offsets/bytecounts tag are both present and
-	 * d) both contain exactly one value, then we consistently find
-	 * that the buggy implementation of the buggy compression scheme
-	 * matches contig planarconfig best. So we 'fix-up' the tag here
-	 */
-	if ((tif->tif_dir.td_compression==COMPRESSION_OJPEG)&&
-	    (tif->tif_dir.td_planarconfig==PLANARCONFIG_SEPARATE))
-	{
-		if (!_TIFFFillStriles(tif))
-		    goto bad;
-		dp=TIFFReadDirectoryFindEntry(tif,dir,dircount,TIFFTAG_STRIPOFFSETS);
-		if ((dp!=0)&&(dp->tdir_count==1))
-		{
-			dp=TIFFReadDirectoryFindEntry(tif,dir,dircount,
-			    TIFFTAG_STRIPBYTECOUNTS);
-			if ((dp!=0)&&(dp->tdir_count==1))
-			{
-				tif->tif_dir.td_planarconfig=PLANARCONFIG_CONTIG;
-				TIFFWarningExt(tif->tif_clientdata,module,
-				    "Planarconfig tag value assumed incorrect, "
-				    "assuming data is contig instead of chunky");
-			}
-		}
-	}
-	/*
-	 * Allocate directory structure and setup defaults.
-	 */
-	if (!TIFFFieldSet(tif,FIELD_IMAGEDIMENSIONS))
-	{
-		MissingRequired(tif,"ImageLength");
-		goto bad;
-	}
-	/*
-	 * Setup appropriate structures (by strip or by tile)
-	 */
-	if (!TIFFFieldSet(tif, FIELD_TILEDIMENSIONS)) {
-		tif->tif_dir.td_nstrips = TIFFNumberOfStrips(tif);  
-		tif->tif_dir.td_tilewidth = tif->tif_dir.td_imagewidth;
-		tif->tif_dir.td_tilelength = tif->tif_dir.td_rowsperstrip;
-		tif->tif_dir.td_tiledepth = tif->tif_dir.td_imagedepth;
-		tif->tif_flags &= ~TIFF_ISTILED;
-	} else {
-		tif->tif_dir.td_nstrips = TIFFNumberOfTiles(tif);
-		tif->tif_flags |= TIFF_ISTILED;
-	}
-	if (!tif->tif_dir.td_nstrips) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "Cannot handle zero number of %s",
-		    isTiled(tif) ? "tiles" : "strips");
-		goto bad;
-	}
-	if (tif->tif_dir.td_nstrips > INT_MAX) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "Cannot handle %u number of %s",
-		    tif->tif_dir.td_nstrips,
-		    isTiled(tif) ? "tiles" : "strips");
-		goto bad;
-	}
-	tif->tif_dir.td_stripsperimage = tif->tif_dir.td_nstrips;
-	if (tif->tif_dir.td_planarconfig == PLANARCONFIG_SEPARATE)
-		tif->tif_dir.td_stripsperimage /= tif->tif_dir.td_samplesperpixel;
-	if (!TIFFFieldSet(tif, FIELD_STRIPOFFSETS)) {
+    nextdiroff = tif->tif_nextdiroff;
+    /* tif_curdir++ and tif_nextdiroff should only be updated after SUCCESSFUL
+     * reading of the directory. Otherwise, invalid IFD offsets could corrupt
+     * the IFD list. */
+    if (!_TIFFCheckDirNumberAndOffset(tif,
+                                      tif->tif_curdir ==
+                                              TIFF_NON_EXISTENT_DIR_NUMBER
+                                          ? 0
+                                          : tif->tif_curdir + 1,
+                                      nextdiroff))
+    {
+        return 0; /* bad offset (IFD looping or more than TIFF_MAX_DIR_COUNT
+                     IFDs) */
+    }
+    dircount = TIFFFetchDirectory(tif, nextdiroff, &dir, &tif->tif_nextdiroff);
+    if (!dircount)
+    {
+        TIFFErrorExtR(tif, module,
+                      "Failed to read directory at offset %" PRIu64,
+                      nextdiroff);
+        return 0;
+    }
+    /* Set global values after a valid directory has been fetched.
+     * tif_diroff is already set to nextdiroff in TIFFFetchDirectory() in the
+     * beginning. */
+    if (tif->tif_curdir == TIFF_NON_EXISTENT_DIR_NUMBER)
+        tif->tif_curdir = 0;
+    else
+        tif->tif_curdir++;
+    (*tif->tif_cleanup)(tif); /* cleanup any previous compression state */
+
+    TIFFReadDirectoryCheckOrder(tif, dir, dircount);
+
+    /*
+     * Mark duplicates of any tag to be ignored (bugzilla 1994)
+     * to avoid certain pathological problems.
+     */
+    {
+        TIFFDirEntry *ma;
+        uint16_t mb;
+        for (ma = dir, mb = 0; mb < dircount; ma++, mb++)
+        {
+            TIFFDirEntry *na;
+            uint16_t nb;
+            for (na = ma + 1, nb = mb + 1; nb < dircount; na++, nb++)
+            {
+                if (ma->tdir_tag == na->tdir_tag)
+                {
+                    na->tdir_ignore = TRUE;
+                }
+            }
+        }
+    }
+
+    tif->tif_flags &= ~TIFF_BEENWRITING; /* reset before new dir */
+    tif->tif_flags &= ~TIFF_BUF4WRITE;   /* reset before new dir */
+    tif->tif_flags &= ~TIFF_CHOPPEDUPARRAYS;
+
+    /* free any old stuff and reinit */
+    TIFFFreeDirectory(tif);
+    TIFFDefaultDirectory(tif);
+    /*
+     * Electronic Arts writes gray-scale TIFF files
+     * without a PlanarConfiguration directory entry.
+     * Thus we setup a default value here, even though
+     * the TIFF spec says there is no default value.
+     * After PlanarConfiguration is preset in TIFFDefaultDirectory()
+     * the following setting is not needed, but does not harm either.
+     */
+    TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+    /*
+     * Setup default value and then make a pass over
+     * the fields to check type and tag information,
+     * and to extract info required to size data
+     * structures.  A second pass is made afterwards
+     * to read in everything not taken in the first pass.
+     * But we must process the Compression tag first
+     * in order to merge in codec-private tag definitions (otherwise
+     * we may get complaints about unknown tags).  However, the
+     * Compression tag may be dependent on the SamplesPerPixel
+     * tag value because older TIFF specs permitted Compression
+     * to be written as a SamplesPerPixel-count tag entry.
+     * Thus if we don't first figure out the correct SamplesPerPixel
+     * tag value then we may end up ignoring the Compression tag
+     * value because it has an incorrect count value (if the
+     * true value of SamplesPerPixel is not 1).
+     */
+    dp =
+        TIFFReadDirectoryFindEntry(tif, dir, dircount, TIFFTAG_SAMPLESPERPIXEL);
+    if (dp)
+    {
+        if (!TIFFFetchNormalTag(tif, dp, 0))
+            goto bad;
+        dp->tdir_ignore = TRUE;
+    }
+    dp = TIFFReadDirectoryFindEntry(tif, dir, dircount, TIFFTAG_COMPRESSION);
+    if (dp)
+    {
+        /*
+         * The 5.0 spec says the Compression tag has one value, while
+         * earlier specs say it has one value per sample.  Because of
+         * this, we accept the tag if one value is supplied with either
+         * count.
+         */
+        uint16_t value;
+        enum TIFFReadDirEntryErr err;
+        err = TIFFReadDirEntryShort(tif, dp, &value);
+        if (err == TIFFReadDirEntryErrCount)
+            err = TIFFReadDirEntryPersampleShort(tif, dp, &value);
+        if (err != TIFFReadDirEntryErrOk)
+        {
+            TIFFReadDirEntryOutputErr(tif, err, module, "Compression", 0);
+            goto bad;
+        }
+        if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, value))
+            goto bad;
+        dp->tdir_ignore = TRUE;
+    }
+    else
+    {
+        if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE))
+            goto bad;
+    }
+    /*
+     * First real pass over the directory.
+     */
+    for (di = 0, dp = dir; di < dircount; di++, dp++)
+    {
+        if (!dp->tdir_ignore)
+        {
+            TIFFReadDirectoryFindFieldInfo(tif, dp->tdir_tag, &fii);
+            if (fii == FAILED_FII)
+            {
+                TIFFWarningExtR(tif, module,
+                                "Unknown field with tag %" PRIu16 " (0x%" PRIx16
+                                ") encountered",
+                                dp->tdir_tag, dp->tdir_tag);
+                /* the following knowingly leaks the
+                   anonymous field structure */
+                if (!_TIFFMergeFields(
+                        tif,
+                        _TIFFCreateAnonField(tif, dp->tdir_tag,
+                                             (TIFFDataType)dp->tdir_type),
+                        1))
+                {
+                    TIFFWarningExtR(
+                        tif, module,
+                        "Registering anonymous field with tag %" PRIu16
+                        " (0x%" PRIx16 ") failed",
+                        dp->tdir_tag, dp->tdir_tag);
+                    dp->tdir_ignore = TRUE;
+                }
+                else
+                {
+                    TIFFReadDirectoryFindFieldInfo(tif, dp->tdir_tag, &fii);
+                    assert(fii != FAILED_FII);
+                }
+            }
+        }
+        if (!dp->tdir_ignore)
+        {
+            fip = tif->tif_fields[fii];
+            if (fip->field_bit == FIELD_IGNORE)
+                dp->tdir_ignore = TRUE;
+            else
+            {
+                switch (dp->tdir_tag)
+                {
+                    case TIFFTAG_STRIPOFFSETS:
+                    case TIFFTAG_STRIPBYTECOUNTS:
+                    case TIFFTAG_TILEOFFSETS:
+                    case TIFFTAG_TILEBYTECOUNTS:
+                        TIFFSetFieldBit(tif, fip->field_bit);
+                        break;
+                    case TIFFTAG_IMAGEWIDTH:
+                    case TIFFTAG_IMAGELENGTH:
+                    case TIFFTAG_IMAGEDEPTH:
+                    case TIFFTAG_TILELENGTH:
+                    case TIFFTAG_TILEWIDTH:
+                    case TIFFTAG_TILEDEPTH:
+                    case TIFFTAG_PLANARCONFIG:
+                    case TIFFTAG_ROWSPERSTRIP:
+                    case TIFFTAG_EXTRASAMPLES:
+                        if (!TIFFFetchNormalTag(tif, dp, 0))
+                            goto bad;
+                        dp->tdir_ignore = TRUE;
+                        break;
+                    default:
+                        if (!_TIFFCheckFieldIsValidForCodec(tif, dp->tdir_tag))
+                            dp->tdir_ignore = TRUE;
+                        break;
+                }
+            }
+        }
+    }
+    /*
+     * XXX: OJPEG hack.
+     * If a) compression is OJPEG, b) planarconfig tag says it's separate,
+     * c) strip offsets/bytecounts tag are both present and
+     * d) both contain exactly one value, then we consistently find
+     * that the buggy implementation of the buggy compression scheme
+     * matches contig planarconfig best. So we 'fix-up' the tag here
+     */
+    if ((tif->tif_dir.td_compression == COMPRESSION_OJPEG) &&
+        (tif->tif_dir.td_planarconfig == PLANARCONFIG_SEPARATE))
+    {
+        if (!_TIFFFillStriles(tif))
+            goto bad;
+        dp = TIFFReadDirectoryFindEntry(tif, dir, dircount,
+                                        TIFFTAG_STRIPOFFSETS);
+        if ((dp != 0) && (dp->tdir_count == 1))
+        {
+            dp = TIFFReadDirectoryFindEntry(tif, dir, dircount,
+                                            TIFFTAG_STRIPBYTECOUNTS);
+            if ((dp != 0) && (dp->tdir_count == 1))
+            {
+                tif->tif_dir.td_planarconfig = PLANARCONFIG_CONTIG;
+                TIFFWarningExtR(tif, module,
+                                "Planarconfig tag value assumed incorrect, "
+                                "assuming data is contig instead of chunky");
+            }
+        }
+    }
+    /*
+     * Allocate directory structure and setup defaults.
+     */
+    if (!TIFFFieldSet(tif, FIELD_IMAGEDIMENSIONS))
+    {
+        MissingRequired(tif, "ImageLength");
+        goto bad;
+    }
+
+    /*
+     * Second pass: extract other information.
+     */
+    for (di = 0, dp = dir; di < dircount; di++, dp++)
+    {
+        if (!dp->tdir_ignore)
+        {
+            switch (dp->tdir_tag)
+            {
+                case TIFFTAG_MINSAMPLEVALUE:
+                case TIFFTAG_MAXSAMPLEVALUE:
+                case TIFFTAG_BITSPERSAMPLE:
+                case TIFFTAG_DATATYPE:
+                case TIFFTAG_SAMPLEFORMAT:
+                    /*
+                     * The MinSampleValue, MaxSampleValue, BitsPerSample
+                     * DataType and SampleFormat tags are supposed to be
+                     * written as one value/sample, but some vendors
+                     * incorrectly write one value only -- so we accept
+                     * that as well (yuck). Other vendors write correct
+                     * value for NumberOfSamples, but incorrect one for
+                     * BitsPerSample and friends, and we will read this
+                     * too.
+                     */
+                    {
+                        uint16_t value;
+                        enum TIFFReadDirEntryErr err;
+                        err = TIFFReadDirEntryShort(tif, dp, &value);
+                        if (err == TIFFReadDirEntryErrCount)
+                            err =
+                                TIFFReadDirEntryPersampleShort(tif, dp, &value);
+                        if (err != TIFFReadDirEntryErrOk)
+                        {
+                            fip = TIFFFieldWithTag(tif, dp->tdir_tag);
+                            TIFFReadDirEntryOutputErr(
+                                tif, err, module,
+                                fip ? fip->field_name : "unknown tagname", 0);
+                            goto bad;
+                        }
+                        if (!TIFFSetField(tif, dp->tdir_tag, value))
+                            goto bad;
+                        if (dp->tdir_tag == TIFFTAG_BITSPERSAMPLE)
+                            bitspersample_read = TRUE;
+                    }
+                    break;
+                case TIFFTAG_SMINSAMPLEVALUE:
+                case TIFFTAG_SMAXSAMPLEVALUE:
+                {
+
+                    double *data = NULL;
+                    enum TIFFReadDirEntryErr err;
+                    uint32_t saved_flags;
+                    int m;
+                    if (dp->tdir_count !=
+                        (uint64_t)tif->tif_dir.td_samplesperpixel)
+                        err = TIFFReadDirEntryErrCount;
+                    else
+                        err = TIFFReadDirEntryDoubleArray(tif, dp, &data);
+                    if (err != TIFFReadDirEntryErrOk)
+                    {
+                        fip = TIFFFieldWithTag(tif, dp->tdir_tag);
+                        TIFFReadDirEntryOutputErr(
+                            tif, err, module,
+                            fip ? fip->field_name : "unknown tagname", 0);
+                        goto bad;
+                    }
+                    saved_flags = tif->tif_flags;
+                    tif->tif_flags |= TIFF_PERSAMPLE;
+                    m = TIFFSetField(tif, dp->tdir_tag, data);
+                    tif->tif_flags = saved_flags;
+                    _TIFFfreeExt(tif, data);
+                    if (!m)
+                        goto bad;
+                }
+                break;
+                case TIFFTAG_STRIPOFFSETS:
+                case TIFFTAG_TILEOFFSETS:
+                    switch (dp->tdir_type)
+                    {
+                        case TIFF_SHORT:
+                        case TIFF_LONG:
+                        case TIFF_LONG8:
+                            break;
+                        default:
+                            /* Warn except if directory typically created with
+                             * TIFFDeferStrileArrayWriting() */
+                            if (!(tif->tif_mode == O_RDWR &&
+                                  dp->tdir_count == 0 && dp->tdir_type == 0 &&
+                                  dp->tdir_offset.toff_long8 == 0))
+                            {
+                                fip = TIFFFieldWithTag(tif, dp->tdir_tag);
+                                TIFFWarningExtR(
+                                    tif, module, "Invalid data type for tag %s",
+                                    fip ? fip->field_name : "unknown tagname");
+                            }
+                            break;
+                    }
+                    _TIFFmemcpy(&(tif->tif_dir.td_stripoffset_entry), dp,
+                                sizeof(TIFFDirEntry));
+                    break;
+                case TIFFTAG_STRIPBYTECOUNTS:
+                case TIFFTAG_TILEBYTECOUNTS:
+                    switch (dp->tdir_type)
+                    {
+                        case TIFF_SHORT:
+                        case TIFF_LONG:
+                        case TIFF_LONG8:
+                            break;
+                        default:
+                            /* Warn except if directory typically created with
+                             * TIFFDeferStrileArrayWriting() */
+                            if (!(tif->tif_mode == O_RDWR &&
+                                  dp->tdir_count == 0 && dp->tdir_type == 0 &&
+                                  dp->tdir_offset.toff_long8 == 0))
+                            {
+                                fip = TIFFFieldWithTag(tif, dp->tdir_tag);
+                                TIFFWarningExtR(
+                                    tif, module, "Invalid data type for tag %s",
+                                    fip ? fip->field_name : "unknown tagname");
+                            }
+                            break;
+                    }
+                    _TIFFmemcpy(&(tif->tif_dir.td_stripbytecount_entry), dp,
+                                sizeof(TIFFDirEntry));
+                    break;
+                case TIFFTAG_COLORMAP:
+                case TIFFTAG_TRANSFERFUNCTION:
+                {
+                    enum TIFFReadDirEntryErr err;
+                    uint32_t countpersample;
+                    uint32_t countrequired;
+                    uint32_t incrementpersample;
+                    uint16_t *value = NULL;
+                    /* It would be dangerous to instantiate those tag values */
+                    /* since if td_bitspersample has not yet been read (due to
+                     */
+                    /* unordered tags), it could be read afterwards with a */
+                    /* values greater than the default one (1), which may cause
+                     */
+                    /* crashes in user code */
+                    if (!bitspersample_read)
+                    {
+                        fip = TIFFFieldWithTag(tif, dp->tdir_tag);
+                        TIFFWarningExtR(
+                            tif, module,
+                            "Ignoring %s since BitsPerSample tag not found",
+                            fip ? fip->field_name : "unknown tagname");
+                        continue;
+                    }
+                    /* ColorMap or TransferFunction for high bit */
+                    /* depths do not make much sense and could be */
+                    /* used as a denial of service vector */
+                    if (tif->tif_dir.td_bitspersample > 24)
+                    {
+                        fip = TIFFFieldWithTag(tif, dp->tdir_tag);
+                        TIFFWarningExtR(
+                            tif, module,
+                            "Ignoring %s because BitsPerSample=%" PRIu16 ">24",
+                            fip ? fip->field_name : "unknown tagname",
+                            tif->tif_dir.td_bitspersample);
+                        continue;
+                    }
+                    countpersample = (1U << tif->tif_dir.td_bitspersample);
+                    if ((dp->tdir_tag == TIFFTAG_TRANSFERFUNCTION) &&
+                        (dp->tdir_count == (uint64_t)countpersample))
+                    {
+                        countrequired = countpersample;
+                        incrementpersample = 0;
+                    }
+                    else
+                    {
+                        countrequired = 3 * countpersample;
+                        incrementpersample = countpersample;
+                    }
+                    if (dp->tdir_count != (uint64_t)countrequired)
+                        err = TIFFReadDirEntryErrCount;
+                    else
+                        err = TIFFReadDirEntryShortArray(tif, dp, &value);
+                    if (err != TIFFReadDirEntryErrOk)
+                    {
+                        fip = TIFFFieldWithTag(tif, dp->tdir_tag);
+                        TIFFReadDirEntryOutputErr(
+                            tif, err, module,
+                            fip ? fip->field_name : "unknown tagname", 1);
+                    }
+                    else
+                    {
+                        TIFFSetField(tif, dp->tdir_tag, value,
+                                     value + incrementpersample,
+                                     value + 2 * incrementpersample);
+                        _TIFFfreeExt(tif, value);
+                    }
+                }
+                break;
+                    /* BEGIN REV 4.0 COMPATIBILITY */
+                case TIFFTAG_OSUBFILETYPE:
+                {
+                    uint16_t valueo;
+                    uint32_t value;
+                    if (TIFFReadDirEntryShort(tif, dp, &valueo) ==
+                        TIFFReadDirEntryErrOk)
+                    {
+                        switch (valueo)
+                        {
+                            case OFILETYPE_REDUCEDIMAGE:
+                                value = FILETYPE_REDUCEDIMAGE;
+                                break;
+                            case OFILETYPE_PAGE:
+                                value = FILETYPE_PAGE;
+                                break;
+                            default:
+                                value = 0;
+                                break;
+                        }
+                        if (value != 0)
+                            TIFFSetField(tif, TIFFTAG_SUBFILETYPE, value);
+                    }
+                }
+                break;
+                /* 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;
+            }
+        } /* -- if (!dp->tdir_ignore) */
+    }     /* -- for-loop -- */
+
+    /*
+     * OJPEG hack:
+     * - If a) compression is OJPEG, and b) photometric tag is missing,
+     * then we consistently find that photometric should be YCbCr
+     * - If a) compression is OJPEG, and b) photometric tag says it's RGB,
+     * then we consistently find that the buggy implementation of the
+     * buggy compression scheme matches photometric YCbCr instead.
+     * - If a) compression is OJPEG, and b) bitspersample tag is missing,
+     * then we consistently find bitspersample should be 8.
+     * - If a) compression is OJPEG, b) samplesperpixel tag is missing,
+     * and c) photometric is RGB or YCbCr, then we consistently find
+     * samplesperpixel should be 3
+     * - If a) compression is OJPEG, b) samplesperpixel tag is missing,
+     * and c) photometric is MINISWHITE or MINISBLACK, then we consistently
+     * find samplesperpixel should be 3
+     */
+    if (tif->tif_dir.td_compression == COMPRESSION_OJPEG)
+    {
+        if (!TIFFFieldSet(tif, FIELD_PHOTOMETRIC))
+        {
+            TIFFWarningExtR(
+                tif, module,
+                "Photometric tag is missing, assuming data is YCbCr");
+            if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR))
+                goto bad;
+        }
+        else if (tif->tif_dir.td_photometric == PHOTOMETRIC_RGB)
+        {
+            tif->tif_dir.td_photometric = PHOTOMETRIC_YCBCR;
+            TIFFWarningExtR(tif, module,
+                            "Photometric tag value assumed incorrect, "
+                            "assuming data is YCbCr instead of RGB");
+        }
+        if (!TIFFFieldSet(tif, FIELD_BITSPERSAMPLE))
+        {
+            TIFFWarningExtR(
+                tif, module,
+                "BitsPerSample tag is missing, assuming 8 bits per sample");
+            if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8))
+                goto bad;
+        }
+        if (!TIFFFieldSet(tif, FIELD_SAMPLESPERPIXEL))
+        {
+            if (tif->tif_dir.td_photometric == PHOTOMETRIC_RGB)
+            {
+                TIFFWarningExtR(tif, module,
+                                "SamplesPerPixel tag is missing, "
+                                "assuming correct SamplesPerPixel value is 3");
+                if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3))
+                    goto bad;
+            }
+            if (tif->tif_dir.td_photometric == PHOTOMETRIC_YCBCR)
+            {
+                TIFFWarningExtR(tif, module,
+                                "SamplesPerPixel tag is missing, "
+                                "applying correct SamplesPerPixel value of 3");
+                if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3))
+                    goto bad;
+            }
+            else if ((tif->tif_dir.td_photometric == PHOTOMETRIC_MINISWHITE) ||
+                     (tif->tif_dir.td_photometric == PHOTOMETRIC_MINISBLACK))
+            {
+                /*
+                 * SamplesPerPixel tag is missing, but is not required
+                 * by spec.  Assume correct SamplesPerPixel value of 1.
+                 */
+                if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1))
+                    goto bad;
+            }
+        }
+    }
+
+    /*
+     * Setup appropriate structures (by strip or by tile)
+     * We do that only after the above OJPEG hack which alters SamplesPerPixel
+     * and thus influences the number of strips in the separate planarconfig.
+     */
+    if (!TIFFFieldSet(tif, FIELD_TILEDIMENSIONS))
+    {
+        tif->tif_dir.td_nstrips = TIFFNumberOfStrips(tif);
+        tif->tif_dir.td_tilewidth = tif->tif_dir.td_imagewidth;
+        tif->tif_dir.td_tilelength = tif->tif_dir.td_rowsperstrip;
+        tif->tif_dir.td_tiledepth = tif->tif_dir.td_imagedepth;
+        tif->tif_flags &= ~TIFF_ISTILED;
+    }
+    else
+    {
+        tif->tif_dir.td_nstrips = TIFFNumberOfTiles(tif);
+        tif->tif_flags |= TIFF_ISTILED;
+    }
+    if (!tif->tif_dir.td_nstrips)
+    {
+        TIFFErrorExtR(tif, module, "Cannot handle zero number of %s",
+                      isTiled(tif) ? "tiles" : "strips");
+        goto bad;
+    }
+    if (tif->tif_dir.td_nstrips > INT_MAX)
+    {
+        TIFFErrorExt(tif->tif_clientdata, module,
+                     "Cannot handle %u number of %s",
+                     tif->tif_dir.td_nstrips,
+                     isTiled(tif) ? "tiles" : "strips");
+        goto bad;
+    }
+    tif->tif_dir.td_stripsperimage = tif->tif_dir.td_nstrips;
+    if (tif->tif_dir.td_planarconfig == PLANARCONFIG_SEPARATE)
+        tif->tif_dir.td_stripsperimage /= tif->tif_dir.td_samplesperpixel;
+    if (!TIFFFieldSet(tif, FIELD_STRIPOFFSETS))
+    {
 #ifdef OJPEG_SUPPORT
-		if ((tif->tif_dir.td_compression==COMPRESSION_OJPEG) &&
-		    (isTiled(tif)==0) &&
-		    (tif->tif_dir.td_nstrips==1)) {
-			/*
-			 * XXX: OJPEG hack.
-			 * If a) compression is OJPEG, b) it's not a tiled TIFF,
-			 * and c) the number of strips is 1,
-			 * then we tolerate the absence of stripoffsets tag,
-			 * because, presumably, all required data is in the
-			 * JpegInterchangeFormat stream.
-			 */
-			TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS);
-		} else
+        if ((tif->tif_dir.td_compression == COMPRESSION_OJPEG) &&
+            (isTiled(tif) == 0) && (tif->tif_dir.td_nstrips == 1))
+        {
+            /*
+             * XXX: OJPEG hack.
+             * If a) compression is OJPEG, b) it's not a tiled TIFF,
+             * and c) the number of strips is 1,
+             * then we tolerate the absence of stripoffsets tag,
+             * because, presumably, all required data is in the
+             * JpegInterchangeFormat stream.
+             */
+            TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS);
+        }
+        else
 #endif
         {
-			MissingRequired(tif,
-				isTiled(tif) ? "TileOffsets" : "StripOffsets");
-			goto bad;
-		}
-	}
-	/*
-	 * Second pass: extract other information.
-	 */
-	for (di=0, dp=dir; di<dircount; di++, dp++)
-	{
-		if (!dp->tdir_ignore) {
-			switch (dp->tdir_tag) 
-			{
-				case TIFFTAG_MINSAMPLEVALUE:
-				case TIFFTAG_MAXSAMPLEVALUE:
-				case TIFFTAG_BITSPERSAMPLE:
-				case TIFFTAG_DATATYPE:
-				case TIFFTAG_SAMPLEFORMAT:
-					/*
-					 * The MinSampleValue, MaxSampleValue, BitsPerSample
-					 * DataType and SampleFormat tags are supposed to be
-					 * written as one value/sample, but some vendors
-					 * incorrectly write one value only -- so we accept
-					 * that as well (yuck). Other vendors write correct
-					 * value for NumberOfSamples, but incorrect one for
-					 * BitsPerSample and friends, and we will read this
-					 * too.
-					 */
-					{
-						uint16 value;
-						enum TIFFReadDirEntryErr err;
-						err=TIFFReadDirEntryShort(tif,dp,&value);
-						if (err==TIFFReadDirEntryErrCount)
-							err=TIFFReadDirEntryPersampleShort(tif,dp,&value);
-						if (err!=TIFFReadDirEntryErrOk)
-						{
-							fip = TIFFFieldWithTag(tif,dp->tdir_tag);
-							TIFFReadDirEntryOutputErr(tif,err,module,fip ? fip->field_name : "unknown tagname",0);
-							goto bad;
-						}
-						if (!TIFFSetField(tif,dp->tdir_tag,value))
-							goto bad;
-						if( dp->tdir_tag == TIFFTAG_BITSPERSAMPLE )
-						    bitspersample_read = TRUE;
-					}
-					break;
-				case TIFFTAG_SMINSAMPLEVALUE:
-				case TIFFTAG_SMAXSAMPLEVALUE:
-					{
-
-						double *data = NULL;
-						enum TIFFReadDirEntryErr err;
-						uint32 saved_flags;
-						int m;
-						if (dp->tdir_count != (uint64)tif->tif_dir.td_samplesperpixel)
-							err = TIFFReadDirEntryErrCount;
-						else
-							err = TIFFReadDirEntryDoubleArray(tif, dp, &data);
-						if (err!=TIFFReadDirEntryErrOk)
-						{
-							fip = TIFFFieldWithTag(tif,dp->tdir_tag);
-							TIFFReadDirEntryOutputErr(tif,err,module,fip ? fip->field_name : "unknown tagname",0);
-							goto bad;
-						}
-						saved_flags = tif->tif_flags;
-						tif->tif_flags |= TIFF_PERSAMPLE;
-						m = TIFFSetField(tif,dp->tdir_tag,data);
-						tif->tif_flags = saved_flags;
-						_TIFFfree(data);
-						if (!m)
-							goto bad;
-					}
-					break;
-				case TIFFTAG_STRIPOFFSETS:
-				case TIFFTAG_TILEOFFSETS:
-					_TIFFmemcpy( &(tif->tif_dir.td_stripoffset_entry),
-					   dp, sizeof(TIFFDirEntry) );
-					break;
-				case TIFFTAG_STRIPBYTECOUNTS:
-				case TIFFTAG_TILEBYTECOUNTS:
-					_TIFFmemcpy( &(tif->tif_dir.td_stripbytecount_entry),
-					   dp, sizeof(TIFFDirEntry) );
-					break;
-				case TIFFTAG_COLORMAP:
-				case TIFFTAG_TRANSFERFUNCTION:
-					{
-						enum TIFFReadDirEntryErr err;
-						uint32 countpersample;
-						uint32 countrequired;
-						uint32 incrementpersample;
-						uint16* value=NULL;
-						/* It would be dangerous to instantiate those tag values */
-						/* since if td_bitspersample has not yet been read (due to */
-						/* unordered tags), it could be read afterwards with a */
-						/* values greater than the default one (1), which may cause */
-						/* crashes in user code */
-						if( !bitspersample_read )
-						{
-							fip = TIFFFieldWithTag(tif,dp->tdir_tag);
-							TIFFWarningExt(tif->tif_clientdata,module,
-								"Ignoring %s since BitsPerSample tag not found",
-								fip ? fip->field_name : "unknown tagname");
-							continue;
-						}
-						/* ColorMap or TransferFunction for high bit */
-						/* depths do not make much sense and could be */
-						/* used as a denial of service vector */
-						if (tif->tif_dir.td_bitspersample > 24)
-						{
-							fip = TIFFFieldWithTag(tif,dp->tdir_tag);
-							TIFFWarningExt(tif->tif_clientdata,module,
-								"Ignoring %s because BitsPerSample=%d>24",
-								fip ? fip->field_name : "unknown tagname",
-								tif->tif_dir.td_bitspersample);
-							continue;
-						}
-						countpersample=(1U<<tif->tif_dir.td_bitspersample);
-						if ((dp->tdir_tag==TIFFTAG_TRANSFERFUNCTION)&&(dp->tdir_count==(uint64)countpersample))
-						{
-							countrequired=countpersample;
-							incrementpersample=0;
-						}
-						else
-						{
-							countrequired=3*countpersample;
-							incrementpersample=countpersample;
-						}
-						if (dp->tdir_count!=(uint64)countrequired)
-							err=TIFFReadDirEntryErrCount;
-						else
-							err=TIFFReadDirEntryShortArray(tif,dp,&value);
-						if (err!=TIFFReadDirEntryErrOk)
-						{
-							fip = TIFFFieldWithTag(tif,dp->tdir_tag);
-							TIFFReadDirEntryOutputErr(tif,err,module,fip ? fip->field_name : "unknown tagname",1);
-						}
-						else
-						{
-							TIFFSetField(tif,dp->tdir_tag,value,value+incrementpersample,value+2*incrementpersample);
-							_TIFFfree(value);
-						}
-					}
-					break;
-/* BEGIN REV 4.0 COMPATIBILITY */
-				case TIFFTAG_OSUBFILETYPE:
-					{
-						uint16 valueo;
-						uint32 value;
-						if (TIFFReadDirEntryShort(tif,dp,&valueo)==TIFFReadDirEntryErrOk)
-						{
-							switch (valueo)
-							{
-								case OFILETYPE_REDUCEDIMAGE: value=FILETYPE_REDUCEDIMAGE; break;
-								case OFILETYPE_PAGE: value=FILETYPE_PAGE; break;
-								default: value=0; break;
-							}
-							if (value!=0)
-								TIFFSetField(tif,TIFFTAG_SUBFILETYPE,value);
-						}
-					}
-					break;
-/* END REV 4.0 COMPATIBILITY */
-				default:
-					(void) TIFFFetchNormalTag(tif, dp, TRUE);
-					break;
-				}
-			} /* -- if (!dp->tdir_ignore) */
-		} /* -- for-loop -- */
-
-        if( tif->tif_mode == O_RDWR &&
-            tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 &&
-            tif->tif_dir.td_stripoffset_entry.tdir_count == 0 &&
-            tif->tif_dir.td_stripoffset_entry.tdir_type == 0 &&
-            tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0 &&
-            tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 &&
-            tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 &&
-            tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 &&
-            tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0 )
-        {
-            /* Directory typically created with TIFFDeferStrileArrayWriting() */
-            TIFFSetupStrips(tif);
+            MissingRequired(tif, isTiled(tif) ? "TileOffsets" : "StripOffsets");
+            goto bad;
         }
-        else if( !(tif->tif_flags&TIFF_DEFERSTRILELOAD) )
+    }
+
+    if (tif->tif_mode == O_RDWR &&
+        tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 &&
+        tif->tif_dir.td_stripoffset_entry.tdir_count == 0 &&
+        tif->tif_dir.td_stripoffset_entry.tdir_type == 0 &&
+        tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0 &&
+        tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 &&
+        tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 &&
+        tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 &&
+        tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0)
+    {
+        /* Directory typically created with TIFFDeferStrileArrayWriting() */
+        TIFFSetupStrips(tif);
+    }
+    else if (!(tif->tif_flags & TIFF_DEFERSTRILELOAD))
+    {
+        if (tif->tif_dir.td_stripoffset_entry.tdir_tag != 0)
         {
-            if( tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 )
+            if (!TIFFFetchStripThing(tif, &(tif->tif_dir.td_stripoffset_entry),
+                                     tif->tif_dir.td_nstrips,
+                                     &tif->tif_dir.td_stripoffset_p))
             {
-                if (!TIFFFetchStripThing(tif,&(tif->tif_dir.td_stripoffset_entry),
-                                         tif->tif_dir.td_nstrips,
-                                         &tif->tif_dir.td_stripoffset_p))
-                {
-                    goto bad;
-                }
-            }
-            if( tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 )
-            {
-                if (!TIFFFetchStripThing(tif,&(tif->tif_dir.td_stripbytecount_entry),
-                                         tif->tif_dir.td_nstrips,
-                                         &tif->tif_dir.td_stripbytecount_p))
-                {
-                    goto bad;
-                }
+                goto bad;
             }
         }
+        if (tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0)
+        {
+            if (!TIFFFetchStripThing(
+                    tif, &(tif->tif_dir.td_stripbytecount_entry),
+                    tif->tif_dir.td_nstrips, &tif->tif_dir.td_stripbytecount_p))
+            {
+                goto bad;
+            }
+        }
+    }
 
-	/*
-	 * OJPEG hack:
-	 * - If a) compression is OJPEG, and b) photometric tag is missing,
-	 * then we consistently find that photometric should be YCbCr
-	 * - If a) compression is OJPEG, and b) photometric tag says it's RGB,
-	 * then we consistently find that the buggy implementation of the
-	 * buggy compression scheme matches photometric YCbCr instead.
-	 * - If a) compression is OJPEG, and b) bitspersample tag is missing,
-	 * then we consistently find bitspersample should be 8.
-	 * - If a) compression is OJPEG, b) samplesperpixel tag is missing,
-	 * and c) photometric is RGB or YCbCr, then we consistently find
-	 * samplesperpixel should be 3
-	 * - If a) compression is OJPEG, b) samplesperpixel tag is missing,
-	 * and c) photometric is MINISWHITE or MINISBLACK, then we consistently
-	 * find samplesperpixel should be 3
-	 */
-	if (tif->tif_dir.td_compression==COMPRESSION_OJPEG)
-	{
-		if (!TIFFFieldSet(tif,FIELD_PHOTOMETRIC))
-		{
-			TIFFWarningExt(tif->tif_clientdata, module,
-			    "Photometric tag is missing, assuming data is YCbCr");
-			if (!TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,PHOTOMETRIC_YCBCR))
-				goto bad;
-		}
-		else if (tif->tif_dir.td_photometric==PHOTOMETRIC_RGB)
-		{
-			tif->tif_dir.td_photometric=PHOTOMETRIC_YCBCR;
-			TIFFWarningExt(tif->tif_clientdata, module,
-			    "Photometric tag value assumed incorrect, "
-			    "assuming data is YCbCr instead of RGB");
-		}
-		if (!TIFFFieldSet(tif,FIELD_BITSPERSAMPLE))
-		{
-			TIFFWarningExt(tif->tif_clientdata,module,
-			    "BitsPerSample tag is missing, assuming 8 bits per sample");
-			if (!TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,8))
-				goto bad;
-		}
-		if (!TIFFFieldSet(tif,FIELD_SAMPLESPERPIXEL))
-		{
-			if (tif->tif_dir.td_photometric==PHOTOMETRIC_RGB)
-			{
-				TIFFWarningExt(tif->tif_clientdata,module,
-				    "SamplesPerPixel tag is missing, "
-				    "assuming correct SamplesPerPixel value is 3");
-				if (!TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,3))
-					goto bad;
-			}
-			if (tif->tif_dir.td_photometric==PHOTOMETRIC_YCBCR)
-			{
-				TIFFWarningExt(tif->tif_clientdata,module,
-				    "SamplesPerPixel tag is missing, "
-				    "applying correct SamplesPerPixel value of 3");
-				if (!TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,3))
-					goto bad;
-			}
-			else if ((tif->tif_dir.td_photometric==PHOTOMETRIC_MINISWHITE)
-				 || (tif->tif_dir.td_photometric==PHOTOMETRIC_MINISBLACK))
-			{
-				/*
-				 * SamplesPerPixel tag is missing, but is not required
-				 * by spec.  Assume correct SamplesPerPixel value of 1.
-				 */
-				if (!TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,1))
-					goto bad;
-			}
-		}
-	}
+    /*
+     * Make sure all non-color channels are extrasamples.
+     * If it's not the case, define them as such.
+     */
+    color_channels = _TIFFGetMaxColorChannels(tif->tif_dir.td_photometric);
+    if (color_channels &&
+        tif->tif_dir.td_samplesperpixel - tif->tif_dir.td_extrasamples >
+            color_channels)
+    {
+        uint16_t old_extrasamples;
+        uint16_t *new_sampleinfo;
 
-	/*
-	 * Make sure all non-color channels are extrasamples.
-	 * If it's not the case, define them as such.
-	 */
-        color_channels = _TIFFGetMaxColorChannels(tif->tif_dir.td_photometric);
-        if (color_channels && tif->tif_dir.td_samplesperpixel - tif->tif_dir.td_extrasamples > color_channels) {
-                uint16 old_extrasamples;
-                uint16 *new_sampleinfo;
+        TIFFWarningExtR(
+            tif, module,
+            "Sum of Photometric type-related "
+            "color channels and ExtraSamples doesn't match SamplesPerPixel. "
+            "Defining non-color channels as ExtraSamples.");
 
-                TIFFWarningExt(tif->tif_clientdata,module, "Sum of Photometric type-related "
-                    "color channels and ExtraSamples doesn't match SamplesPerPixel. "
-                    "Defining non-color channels as ExtraSamples.");
+        old_extrasamples = tif->tif_dir.td_extrasamples;
+        tif->tif_dir.td_extrasamples =
+            (uint16_t)(tif->tif_dir.td_samplesperpixel - color_channels);
 
-                old_extrasamples = tif->tif_dir.td_extrasamples;
-                tif->tif_dir.td_extrasamples = (uint16) (tif->tif_dir.td_samplesperpixel - color_channels);
-
-                // sampleinfo should contain information relative to these new extra samples
-                new_sampleinfo = (uint16*) _TIFFcalloc(tif->tif_dir.td_extrasamples, sizeof(uint16));
-                if (!new_sampleinfo) {
-                    TIFFErrorExt(tif->tif_clientdata, module, "Failed to allocate memory for "
-                                "temporary new sampleinfo array (%d 16 bit elements)",
-                                tif->tif_dir.td_extrasamples);
-                    goto bad;
-                }
-
-                memcpy(new_sampleinfo, tif->tif_dir.td_sampleinfo, old_extrasamples * sizeof(uint16));
-                _TIFFsetShortArray(&tif->tif_dir.td_sampleinfo, new_sampleinfo, tif->tif_dir.td_extrasamples);
-                _TIFFfree(new_sampleinfo);
+        // sampleinfo should contain information relative to these new extra
+        // samples
+        new_sampleinfo = (uint16_t *)_TIFFcallocExt(
+            tif, tif->tif_dir.td_extrasamples, sizeof(uint16_t));
+        if (!new_sampleinfo)
+        {
+            TIFFErrorExtR(tif, module,
+                          "Failed to allocate memory for "
+                          "temporary new sampleinfo array "
+                          "(%" PRIu16 " 16 bit elements)",
+                          tif->tif_dir.td_extrasamples);
+            goto bad;
         }
 
-	/*
-	 * Verify Palette image has a Colormap.
-	 */
-	if (tif->tif_dir.td_photometric == PHOTOMETRIC_PALETTE &&
-	    !TIFFFieldSet(tif, FIELD_COLORMAP)) {
-		if ( tif->tif_dir.td_bitspersample>=8 && tif->tif_dir.td_samplesperpixel==3)
-			tif->tif_dir.td_photometric = PHOTOMETRIC_RGB;
-		else if (tif->tif_dir.td_bitspersample>=8)
-			tif->tif_dir.td_photometric = PHOTOMETRIC_MINISBLACK;
-		else {
-			MissingRequired(tif, "Colormap");
-			goto bad;
-		}
-	}
-	/*
-	 * OJPEG hack:
-	 * We do no further messing with strip/tile offsets/bytecounts in OJPEG
-	 * TIFFs
-	 */
-	if (tif->tif_dir.td_compression!=COMPRESSION_OJPEG)
-	{
-		/*
-		 * Attempt to deal with a missing StripByteCounts tag.
-		 */
-		if (!TIFFFieldSet(tif, FIELD_STRIPBYTECOUNTS)) {
-			/*
-			 * Some manufacturers violate the spec by not giving
-			 * the size of the strips.  In this case, assume there
-			 * is one uncompressed strip of data.
-			 */
-			if ((tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG &&
-			    tif->tif_dir.td_nstrips > 1) ||
-			    (tif->tif_dir.td_planarconfig == PLANARCONFIG_SEPARATE &&
-			     tif->tif_dir.td_nstrips != (uint32)tif->tif_dir.td_samplesperpixel)) {
-			    MissingRequired(tif, "StripByteCounts");
-			    goto bad;
-			}
-			TIFFWarningExt(tif->tif_clientdata, module,
-				"TIFF directory is missing required "
-				"\"StripByteCounts\" field, calculating from imagelength");
-			if (EstimateStripByteCounts(tif, dir, dircount) < 0)
-			    goto bad;
+        if (old_extrasamples > 0)
+            memcpy(new_sampleinfo, tif->tif_dir.td_sampleinfo,
+                   old_extrasamples * sizeof(uint16_t));
+        _TIFFsetShortArrayExt(tif, &tif->tif_dir.td_sampleinfo, new_sampleinfo,
+                              tif->tif_dir.td_extrasamples);
+        _TIFFfreeExt(tif, new_sampleinfo);
+    }
 
-		} else if (tif->tif_dir.td_nstrips == 1
-                           && !(tif->tif_flags&TIFF_ISTILED)
-			   && ByteCountLooksBad(tif)) {
-			/*
-			 * XXX: Plexus (and others) sometimes give a value of
-			 * zero for a tag when they don't know what the
-			 * correct value is!  Try and handle the simple case
-			 * of estimating the size of a one strip image.
-			 */
-			TIFFWarningExt(tif->tif_clientdata, module,
-			    "Bogus \"StripByteCounts\" field, ignoring and calculating from imagelength");
-			if(EstimateStripByteCounts(tif, dir, dircount) < 0)
-			    goto bad;
-
-		} else if (!(tif->tif_flags&TIFF_DEFERSTRILELOAD)
-			   && tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG
-			   && tif->tif_dir.td_nstrips > 2
-			   && tif->tif_dir.td_compression == COMPRESSION_NONE
-			   && TIFFGetStrileByteCount(tif, 0) != TIFFGetStrileByteCount(tif, 1)
-			   && TIFFGetStrileByteCount(tif, 0) != 0
-			   && TIFFGetStrileByteCount(tif, 1) != 0 ) {
-			/*
-			 * XXX: Some vendors fill StripByteCount array with
-			 * absolutely wrong values (it can be equal to
-			 * StripOffset array, for example). Catch this case
-			 * here.
-                         *
-                         * We avoid this check if deferring strile loading
-                         * as it would always force us to load the strip/tile
-                         * information.
-			 */
-			TIFFWarningExt(tif->tif_clientdata, module,
-			    "Wrong \"StripByteCounts\" field, ignoring and calculating from imagelength");
-			if (EstimateStripByteCounts(tif, dir, dircount) < 0)
-			    goto bad;
-		}
-	}
-	if (dir)
-	{
-		_TIFFfree(dir);
-		dir=NULL;
-	}
-	if (!TIFFFieldSet(tif, FIELD_MAXSAMPLEVALUE))
-	{
-		if (tif->tif_dir.td_bitspersample>=16)
-			tif->tif_dir.td_maxsamplevalue=0xFFFF;
-		else
-			tif->tif_dir.td_maxsamplevalue = (uint16)((1L<<tif->tif_dir.td_bitspersample)-1);
-	}
+    /*
+     * Verify Palette image has a Colormap.
+     */
+    if (tif->tif_dir.td_photometric == PHOTOMETRIC_PALETTE &&
+        !TIFFFieldSet(tif, FIELD_COLORMAP))
+    {
+        if (tif->tif_dir.td_bitspersample >= 8 &&
+            tif->tif_dir.td_samplesperpixel == 3)
+            tif->tif_dir.td_photometric = PHOTOMETRIC_RGB;
+        else if (tif->tif_dir.td_bitspersample >= 8)
+            tif->tif_dir.td_photometric = PHOTOMETRIC_MINISBLACK;
+        else
+        {
+            MissingRequired(tif, "Colormap");
+            goto bad;
+        }
+    }
+    /*
+     * OJPEG hack:
+     * We do no further messing with strip/tile offsets/bytecounts in OJPEG
+     * TIFFs
+     */
+    if (tif->tif_dir.td_compression != COMPRESSION_OJPEG)
+    {
+        /*
+         * Attempt to deal with a missing StripByteCounts tag.
+         */
+        if (!TIFFFieldSet(tif, FIELD_STRIPBYTECOUNTS))
+        {
+            /*
+             * Some manufacturers violate the spec by not giving
+             * the size of the strips.  In this case, assume there
+             * is one uncompressed strip of data.
+             */
+            if ((tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG &&
+                 tif->tif_dir.td_nstrips > 1) ||
+                (tif->tif_dir.td_planarconfig == PLANARCONFIG_SEPARATE &&
+                 tif->tif_dir.td_nstrips !=
+                     (uint32_t)tif->tif_dir.td_samplesperpixel))
+            {
+                MissingRequired(tif, "StripByteCounts");
+                goto bad;
+            }
+            TIFFWarningExtR(
+                tif, module,
+                "TIFF directory is missing required "
+                "\"StripByteCounts\" field, calculating from imagelength");
+            if (EstimateStripByteCounts(tif, dir, dircount) < 0)
+                goto bad;
+        }
+        else if (tif->tif_dir.td_nstrips == 1 &&
+                 !(tif->tif_flags & TIFF_ISTILED) && ByteCountLooksBad(tif))
+        {
+            /*
+             * XXX: Plexus (and others) sometimes give a value of
+             * zero for a tag when they don't know what the
+             * correct value is!  Try and handle the simple case
+             * of estimating the size of a one strip image.
+             */
+            TIFFWarningExtR(tif, module,
+                            "Bogus \"StripByteCounts\" field, ignoring and "
+                            "calculating from imagelength");
+            if (EstimateStripByteCounts(tif, dir, dircount) < 0)
+                goto bad;
+        }
+        else if (!(tif->tif_flags & TIFF_DEFERSTRILELOAD) &&
+                 tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG &&
+                 tif->tif_dir.td_nstrips > 2 &&
+                 tif->tif_dir.td_compression == COMPRESSION_NONE &&
+                 TIFFGetStrileByteCount(tif, 0) !=
+                     TIFFGetStrileByteCount(tif, 1) &&
+                 TIFFGetStrileByteCount(tif, 0) != 0 &&
+                 TIFFGetStrileByteCount(tif, 1) != 0)
+        {
+            /*
+             * XXX: Some vendors fill StripByteCount array with
+             * absolutely wrong values (it can be equal to
+             * StripOffset array, for example). Catch this case
+             * here.
+             *
+             * We avoid this check if deferring strile loading
+             * as it would always force us to load the strip/tile
+             * information.
+             */
+            TIFFWarningExtR(tif, module,
+                            "Wrong \"StripByteCounts\" field, ignoring and "
+                            "calculating from imagelength");
+            if (EstimateStripByteCounts(tif, dir, dircount) < 0)
+                goto bad;
+        }
+    }
+    if (dir)
+    {
+        _TIFFfreeExt(tif, dir);
+        dir = NULL;
+    }
+    if (!TIFFFieldSet(tif, FIELD_MAXSAMPLEVALUE))
+    {
+        if (tif->tif_dir.td_bitspersample >= 16)
+            tif->tif_dir.td_maxsamplevalue = 0xFFFF;
+        else
+            tif->tif_dir.td_maxsamplevalue =
+                (uint16_t)((1L << tif->tif_dir.td_bitspersample) - 1);
+    }
 
 #ifdef STRIPBYTECOUNTSORTED_UNUSED
-	/*
-	 * XXX: We can optimize checking for the strip bounds using the sorted
-	 * bytecounts array. See also comments for TIFFAppendToStrip()
-	 * function in tif_write.c.
-	 */
-	if (!(tif->tif_flags&TIFF_DEFERSTRILELOAD) && tif->tif_dir.td_nstrips > 1) {
-		uint32 strip;
+    /*
+     * XXX: We can optimize checking for the strip bounds using the sorted
+     * bytecounts array. See also comments for TIFFAppendToStrip()
+     * function in tif_write.c.
+     */
+    if (!(tif->tif_flags & TIFF_DEFERSTRILELOAD) && tif->tif_dir.td_nstrips > 1)
+    {
+        uint32_t strip;
 
-		tif->tif_dir.td_stripbytecountsorted = 1;
-		for (strip = 1; strip < tif->tif_dir.td_nstrips; strip++) {
-			if (TIFFGetStrileOffset(tif, strip - 1) >
-			    TIFFGetStrileOffset(tif, strip)) {
-				tif->tif_dir.td_stripbytecountsorted = 0;
-				break;
-			}
-		}
-	}
+        tif->tif_dir.td_stripbytecountsorted = 1;
+        for (strip = 1; strip < tif->tif_dir.td_nstrips; strip++)
+        {
+            if (TIFFGetStrileOffset(tif, strip - 1) >
+                TIFFGetStrileOffset(tif, strip))
+            {
+                tif->tif_dir.td_stripbytecountsorted = 0;
+                break;
+            }
+        }
+    }
 #endif
 
-	/*
-	 * An opportunity for compression mode dependent tag fixup
-	 */
-	(*tif->tif_fixuptags)(tif);
+    /*
+     * An opportunity for compression mode dependent tag fixup
+     */
+    (*tif->tif_fixuptags)(tif);
 
-	/*
-	 * Some manufacturers make life difficult by writing
-	 * large amounts of uncompressed data as a single strip.
-	 * This is contrary to the recommendations of the spec.
-	 * The following makes an attempt at breaking such images
-	 * into strips closer to the recommended 8k bytes.  A
-	 * side effect, however, is that the RowsPerStrip tag
-	 * value may be changed.
-	 */
-	if ((tif->tif_dir.td_planarconfig==PLANARCONFIG_CONTIG)&&
-	    (tif->tif_dir.td_nstrips==1)&&
-	    (tif->tif_dir.td_compression==COMPRESSION_NONE)&&  
-	    ((tif->tif_flags&(TIFF_STRIPCHOP|TIFF_ISTILED))==TIFF_STRIPCHOP))
+    /*
+     * Some manufacturers make life difficult by writing
+     * large amounts of uncompressed data as a single strip.
+     * This is contrary to the recommendations of the spec.
+     * The following makes an attempt at breaking such images
+     * into strips closer to the recommended 8k bytes.  A
+     * side effect, however, is that the RowsPerStrip tag
+     * value may be changed.
+     */
+    if ((tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG) &&
+        (tif->tif_dir.td_nstrips == 1) &&
+        (tif->tif_dir.td_compression == COMPRESSION_NONE) &&
+        ((tif->tif_flags & (TIFF_STRIPCHOP | TIFF_ISTILED)) == TIFF_STRIPCHOP))
+    {
+        ChopUpSingleUncompressedStrip(tif);
+    }
+
+    /* There are also uncompressed striped files with strips larger than */
+    /* 2 GB, which make them unfriendly with a lot of code. If possible, */
+    /* try to expose smaller "virtual" strips. */
+    if (tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG &&
+        tif->tif_dir.td_compression == COMPRESSION_NONE &&
+        (tif->tif_flags & (TIFF_STRIPCHOP | TIFF_ISTILED)) == TIFF_STRIPCHOP &&
+        TIFFStripSize64(tif) > 0x7FFFFFFFUL)
+    {
+        TryChopUpUncompressedBigTiff(tif);
+    }
+
+    /*
+     * Clear the dirty directory flag.
+     */
+    tif->tif_flags &= ~TIFF_DIRTYDIRECT;
+    tif->tif_flags &= ~TIFF_DIRTYSTRIP;
+
+    /*
+     * Reinitialize i/o since we are starting on a new directory.
+     */
+    tif->tif_row = (uint32_t)-1;
+    tif->tif_curstrip = (uint32_t)-1;
+    tif->tif_col = (uint32_t)-1;
+    tif->tif_curtile = (uint32_t)-1;
+    tif->tif_tilesize = (tmsize_t)-1;
+
+    tif->tif_scanlinesize = TIFFScanlineSize(tif);
+    if (!tif->tif_scanlinesize)
+    {
+        TIFFErrorExtR(tif, module, "Cannot handle zero scanline size");
+        return (0);
+    }
+
+    if (isTiled(tif))
+    {
+        tif->tif_tilesize = TIFFTileSize(tif);
+        if (!tif->tif_tilesize)
         {
-            ChopUpSingleUncompressedStrip(tif);
+            TIFFErrorExtR(tif, module, "Cannot handle zero tile size");
+            return (0);
         }
-
-        /* There are also uncompressed striped files with strips larger than */
-        /* 2 GB, which make them unfriendly with a lot of code. If possible, */
-        /* try to expose smaller "virtual" strips. */
-        if( tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG &&
-            tif->tif_dir.td_compression == COMPRESSION_NONE &&
-            (tif->tif_flags&(TIFF_STRIPCHOP|TIFF_ISTILED)) == TIFF_STRIPCHOP &&
-            TIFFStripSize64(tif) > 0x7FFFFFFFUL )
+    }
+    else
+    {
+        if (!TIFFStripSize(tif))
         {
-            TryChopUpUncompressedBigTiff(tif);
+            TIFFErrorExtR(tif, module, "Cannot handle zero strip size");
+            return (0);
         }
-
-        /*
-         * Clear the dirty directory flag. 
-         */
-	tif->tif_flags &= ~TIFF_DIRTYDIRECT;
-	tif->tif_flags &= ~TIFF_DIRTYSTRIP;
-
-	/*
-	 * Reinitialize i/o since we are starting on a new directory.
-	 */
-	tif->tif_row = (uint32) -1;
-	tif->tif_curstrip = (uint32) -1;
-	tif->tif_col = (uint32) -1;
-	tif->tif_curtile = (uint32) -1;
-	tif->tif_tilesize = (tmsize_t) -1;
-
-	tif->tif_scanlinesize = TIFFScanlineSize(tif);
-	if (!tif->tif_scanlinesize) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "Cannot handle zero scanline size");
-		return (0);
-	}
-
-	if (isTiled(tif)) {
-		tif->tif_tilesize = TIFFTileSize(tif);
-		if (!tif->tif_tilesize) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			     "Cannot handle zero tile size");
-			return (0);
-		}
-	} else {
-		if (!TIFFStripSize(tif)) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Cannot handle zero strip size");
-			return (0);
-		}
-	}
-	return (1);
+    }
+    return (1);
 bad:
-	if (dir)
-		_TIFFfree(dir);
-	return (0);
+    if (dir)
+        _TIFFfreeExt(tif, dir);
+    return (0);
 }
 
-static void
-TIFFReadDirectoryCheckOrder(TIFF* tif, TIFFDirEntry* dir, uint16 dircount)
+static void TIFFReadDirectoryCheckOrder(TIFF *tif, TIFFDirEntry *dir,
+                                        uint16_t dircount)
 {
-	static const char module[] = "TIFFReadDirectoryCheckOrder";
-	uint16 m;
-	uint16 n;
-	TIFFDirEntry* o;
-	m=0;
-	for (n=0, o=dir; n<dircount; n++, o++)
-	{
-		if (o->tdir_tag<m)
-		{
-			TIFFWarningExt(tif->tif_clientdata,module,
-			    "Invalid TIFF directory; tags are not sorted in ascending order");
-			break;
-		}
-		m=o->tdir_tag+1;
-	}
+    static const char module[] = "TIFFReadDirectoryCheckOrder";
+    uint16_t m;
+    uint16_t n;
+    TIFFDirEntry *o;
+    m = 0;
+    for (n = 0, o = dir; n < dircount; n++, o++)
+    {
+        if (o->tdir_tag < m)
+        {
+            TIFFWarningExtR(tif, module,
+                            "Invalid TIFF directory; tags are not sorted in "
+                            "ascending order");
+            break;
+        }
+        m = o->tdir_tag + 1;
+    }
 }
 
-static TIFFDirEntry*
-TIFFReadDirectoryFindEntry(TIFF* tif, TIFFDirEntry* dir, uint16 dircount, uint16 tagid)
+static TIFFDirEntry *TIFFReadDirectoryFindEntry(TIFF *tif, TIFFDirEntry *dir,
+                                                uint16_t dircount,
+                                                uint16_t tagid)
 {
-	TIFFDirEntry* m;
-	uint16 n;
-	(void) tif;
-	for (m=dir, n=0; n<dircount; m++, n++)
-	{
-		if (m->tdir_tag==tagid)
-			return(m);
-	}
-	return(0);
+    TIFFDirEntry *m;
+    uint16_t n;
+    (void)tif;
+    for (m = dir, n = 0; n < dircount; m++, n++)
+    {
+        if (m->tdir_tag == tagid)
+            return (m);
+    }
+    return (0);
 }
 
-static void
-TIFFReadDirectoryFindFieldInfo(TIFF* tif, uint16 tagid, uint32* fii)
+static void TIFFReadDirectoryFindFieldInfo(TIFF *tif, uint16_t tagid,
+                                           uint32_t *fii)
 {
-	int32 ma,mb,mc;
-	ma=-1;
-	mc=(int32)tif->tif_nfields;
-	while (1)
-	{
-		if (ma+1==mc)
-		{
-			*fii = FAILED_FII;
-			return;
-		}
-		mb=(ma+mc)/2;
-		if (tif->tif_fields[mb]->field_tag==(uint32)tagid)
-			break;
-		if (tif->tif_fields[mb]->field_tag<(uint32)tagid)
-			ma=mb;
-		else
-			mc=mb;
-	}
-	while (1)
-	{
-		if (mb==0)
-			break;
-		if (tif->tif_fields[mb-1]->field_tag!=(uint32)tagid)
-			break;
-		mb--;
-	}
-	*fii=mb;
+    int32_t ma, mb, mc;
+    ma = -1;
+    mc = (int32_t)tif->tif_nfields;
+    while (1)
+    {
+        if (ma + 1 == mc)
+        {
+            *fii = FAILED_FII;
+            return;
+        }
+        mb = (ma + mc) / 2;
+        if (tif->tif_fields[mb]->field_tag == (uint32_t)tagid)
+            break;
+        if (tif->tif_fields[mb]->field_tag < (uint32_t)tagid)
+            ma = mb;
+        else
+            mc = mb;
+    }
+    while (1)
+    {
+        if (mb == 0)
+            break;
+        if (tif->tif_fields[mb - 1]->field_tag != (uint32_t)tagid)
+            break;
+        mb--;
+    }
+    *fii = mb;
 }
 
 /*
  * Read custom directory from the arbitrary offset.
  * The code is very similar to TIFFReadDirectory().
  */
-int
-TIFFReadCustomDirectory(TIFF* tif, toff_t diroff,
-			const TIFFFieldArray* infoarray)
+int TIFFReadCustomDirectory(TIFF *tif, toff_t diroff,
+                            const TIFFFieldArray *infoarray)
 {
-	static const char module[] = "TIFFReadCustomDirectory";
-	TIFFDirEntry* dir;
-	uint16 dircount;
-	TIFFDirEntry* dp;
-	uint16 di;
-	const TIFFField* fip;
-	uint32 fii;
-	_TIFFSetupFields(tif, infoarray);
-	dircount=TIFFFetchDirectory(tif,diroff,&dir,NULL);
-	if (!dircount)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,
-		    "Failed to read custom directory at offset " TIFF_UINT64_FORMAT,diroff);
-		return 0;
-	}
-	TIFFFreeDirectory(tif);
-	_TIFFmemset(&tif->tif_dir, 0, sizeof(TIFFDirectory));
-	TIFFReadDirectoryCheckOrder(tif,dir,dircount);
-	for (di=0, dp=dir; di<dircount; di++, dp++)
-	{
-		TIFFReadDirectoryFindFieldInfo(tif,dp->tdir_tag,&fii);
-		if (fii == FAILED_FII)
-		{
-			TIFFWarningExt(tif->tif_clientdata, module,
-			    "Unknown field with tag %d (0x%x) encountered",
-			    dp->tdir_tag, dp->tdir_tag);
-			if (!_TIFFMergeFields(tif, _TIFFCreateAnonField(tif,
-						dp->tdir_tag,
-						(TIFFDataType) dp->tdir_type),
-					     1)) {
-				TIFFWarningExt(tif->tif_clientdata, module,
-				    "Registering anonymous field with tag %d (0x%x) failed",
-				    dp->tdir_tag, dp->tdir_tag);
-				dp->tdir_ignore = TRUE;
-			} else {
-				TIFFReadDirectoryFindFieldInfo(tif,dp->tdir_tag,&fii);
-				assert( fii != FAILED_FII );
-			}
-		}
-		if (!dp->tdir_ignore)
-		{
-			fip=tif->tif_fields[fii];
-			if (fip->field_bit==FIELD_IGNORE)
-				dp->tdir_ignore = TRUE;
-			else
-			{
-				/* check data type */
-				while ((fip->field_type!=TIFF_ANY)&&(fip->field_type!=dp->tdir_type))
-				{
-					fii++;
-					if ((fii==tif->tif_nfields)||
-					    (tif->tif_fields[fii]->field_tag!=(uint32)dp->tdir_tag))
-					{
-						fii=0xFFFF;
-						break;
-					}
-					fip=tif->tif_fields[fii];
-				}
-				if (fii==0xFFFF)
-				{
-					TIFFWarningExt(tif->tif_clientdata, module,
-					    "Wrong data type %d for \"%s\"; tag ignored",
-					    dp->tdir_type,fip->field_name);
-					dp->tdir_ignore = TRUE;
-				}
-				else
-				{
-					/* check count if known in advance */
-					if ((fip->field_readcount!=TIFF_VARIABLE)&&
-					    (fip->field_readcount!=TIFF_VARIABLE2))
-					{
-						uint32 expected;
-						if (fip->field_readcount==TIFF_SPP)
-							expected=(uint32)tif->tif_dir.td_samplesperpixel;
-						else
-							expected=(uint32)fip->field_readcount;
-						if (!CheckDirCount(tif,dp,expected))
-							dp->tdir_ignore = TRUE;
-					}
-				}
-			}
-			if (!dp->tdir_ignore) {
-				switch (dp->tdir_tag) 
-				{
-					case EXIFTAG_SUBJECTDISTANCE:
-						(void)TIFFFetchSubjectDistance(tif, dp);
-						break;
-					default:
-						(void)TIFFFetchNormalTag(tif, dp, TRUE);
-						break;
-				}
-			} /*-- if (!dp->tdir_ignore) */
-		}
-	}
-	if (dir)
-		_TIFFfree(dir);
-	return 1;
+    static const char module[] = "TIFFReadCustomDirectory";
+    TIFFDirEntry *dir;
+    uint16_t dircount;
+    TIFFDirEntry *dp;
+    uint16_t di;
+    const TIFFField *fip;
+    uint32_t fii;
+    (*tif->tif_cleanup)(tif); /* cleanup any previous compression state */
+    _TIFFSetupFields(tif, infoarray);
+    dircount = TIFFFetchDirectory(tif, diroff, &dir, NULL);
+    if (!dircount)
+    {
+        TIFFErrorExtR(tif, module,
+                      "Failed to read custom directory at offset %" PRIu64,
+                      diroff);
+        return 0;
+    }
+    TIFFFreeDirectory(tif);
+    _TIFFmemset(&tif->tif_dir, 0, sizeof(TIFFDirectory));
+    TIFFReadDirectoryCheckOrder(tif, dir, dircount);
+    for (di = 0, dp = dir; di < dircount; di++, dp++)
+    {
+        TIFFReadDirectoryFindFieldInfo(tif, dp->tdir_tag, &fii);
+        if (fii == FAILED_FII)
+        {
+            TIFFWarningExtR(tif, module,
+                            "Unknown field with tag %" PRIu16 " (0x%" PRIx16
+                            ") encountered",
+                            dp->tdir_tag, dp->tdir_tag);
+            if (!_TIFFMergeFields(
+                    tif,
+                    _TIFFCreateAnonField(tif, dp->tdir_tag,
+                                         (TIFFDataType)dp->tdir_type),
+                    1))
+            {
+                TIFFWarningExtR(tif, module,
+                                "Registering anonymous field with tag %" PRIu16
+                                " (0x%" PRIx16 ") failed",
+                                dp->tdir_tag, dp->tdir_tag);
+                dp->tdir_ignore = TRUE;
+            }
+            else
+            {
+                TIFFReadDirectoryFindFieldInfo(tif, dp->tdir_tag, &fii);
+                assert(fii != FAILED_FII);
+            }
+        }
+        if (!dp->tdir_ignore)
+        {
+            fip = tif->tif_fields[fii];
+            if (fip->field_bit == FIELD_IGNORE)
+                dp->tdir_ignore = TRUE;
+            else
+            {
+                /* check data type */
+                while ((fip->field_type != TIFF_ANY) &&
+                       (fip->field_type != dp->tdir_type))
+                {
+                    fii++;
+                    if ((fii == tif->tif_nfields) ||
+                        (tif->tif_fields[fii]->field_tag !=
+                         (uint32_t)dp->tdir_tag))
+                    {
+                        fii = 0xFFFF;
+                        break;
+                    }
+                    fip = tif->tif_fields[fii];
+                }
+                if (fii == 0xFFFF)
+                {
+                    TIFFWarningExtR(tif, module,
+                                    "Wrong data type %" PRIu16
+                                    " for \"%s\"; tag ignored",
+                                    dp->tdir_type, fip->field_name);
+                    dp->tdir_ignore = TRUE;
+                }
+                else
+                {
+                    /* check count if known in advance */
+                    if ((fip->field_readcount != TIFF_VARIABLE) &&
+                        (fip->field_readcount != TIFF_VARIABLE2))
+                    {
+                        uint32_t expected;
+                        if (fip->field_readcount == TIFF_SPP)
+                            expected =
+                                (uint32_t)tif->tif_dir.td_samplesperpixel;
+                        else
+                            expected = (uint32_t)fip->field_readcount;
+                        if (!CheckDirCount(tif, dp, expected))
+                            dp->tdir_ignore = TRUE;
+                    }
+                }
+            }
+            if (!dp->tdir_ignore)
+            {
+                switch (dp->tdir_tag)
+                {
+                    case EXIFTAG_SUBJECTDISTANCE:
+                        if (!TIFFFieldIsAnonymous(fip))
+                        {
+                            /* should only be called on a Exif directory */
+                            /* when exifFields[] is active */
+                            (void)TIFFFetchSubjectDistance(tif, dp);
+                        }
+                        else
+                        {
+                            (void)TIFFFetchNormalTag(tif, dp, TRUE);
+                        }
+                        break;
+                    default:
+                        (void)TIFFFetchNormalTag(tif, dp, TRUE);
+                        break;
+                }
+            } /*-- 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;
 }
 
 /*
  * EXIF is important special case of custom IFD, so we have a special
  * function to read it.
  */
-int
-TIFFReadEXIFDirectory(TIFF* tif, toff_t diroff)
+int TIFFReadEXIFDirectory(TIFF *tif, toff_t diroff)
 {
-	const TIFFFieldArray* exifFieldArray;
-	exifFieldArray = _TIFFGetExifFields();
-	return TIFFReadCustomDirectory(tif, diroff, exifFieldArray);  
-}
-
-static int
-EstimateStripByteCounts(TIFF* tif, TIFFDirEntry* dir, uint16 dircount)
-{
-	static const char module[] = "EstimateStripByteCounts";
-
-	TIFFDirEntry *dp;
-	TIFFDirectory *td = &tif->tif_dir;
-	uint32 strip;
-
-    /* Do not try to load stripbytecount as we will compute it */
-        if( !_TIFFFillStrilesInternal( tif, 0 ) )
-            return -1;
-
-	if (td->td_stripbytecount_p)
-		_TIFFfree(td->td_stripbytecount_p);
-	td->td_stripbytecount_p = (uint64*)
-	    _TIFFCheckMalloc(tif, td->td_nstrips, sizeof (uint64),
-		"for \"StripByteCounts\" array");
-        if( td->td_stripbytecount_p == NULL )
-            return -1;
-
-	if (td->td_compression != COMPRESSION_NONE) {
-		uint64 space;
-		uint64 filesize;
-		uint16 n;
-		filesize = TIFFGetFileSize(tif);
-		if (!(tif->tif_flags&TIFF_BIGTIFF))
-			space=sizeof(TIFFHeaderClassic)+2+dircount*12+4;
-		else
-			space=sizeof(TIFFHeaderBig)+8+dircount*20+8;
-		/* calculate amount of space used by indirect values */
-		for (dp = dir, n = dircount; n > 0; n--, dp++)
-		{
-			uint32 typewidth;
-			uint64 datasize;
-			typewidth = TIFFDataWidth((TIFFDataType) dp->tdir_type);
-			if (typewidth == 0) {
-				TIFFErrorExt(tif->tif_clientdata, module,
-				    "Cannot determine size of unknown tag type %d",
-				    dp->tdir_type);
-				return -1;
-			}
-			if( dp->tdir_count > TIFF_UINT64_MAX / typewidth )
-                            return -1;
-			datasize=(uint64)typewidth*dp->tdir_count;
-			if (!(tif->tif_flags&TIFF_BIGTIFF))
-			{
-				if (datasize<=4)
-					datasize=0;
-			}
-			else
-			{
-				if (datasize<=8)
-					datasize=0;
-			}
-			if( space > TIFF_UINT64_MAX - datasize )
-                            return -1;
-			space+=datasize;
-		}
-		if( filesize < space )
-                    /* we should perhaps return in error ? */
-                    space = filesize;
-                else
-                    space = filesize - space;
-		if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
-			space /= td->td_samplesperpixel;
-		for (strip = 0; strip < td->td_nstrips; strip++)
-			td->td_stripbytecount_p[strip] = space;
-		/*
-		 * This gross hack handles the case were the offset to
-		 * the last strip is past the place where we think the strip
-		 * should begin.  Since a strip of data must be contiguous,
-		 * it's safe to assume that we've overestimated the amount
-		 * of data in the strip and trim this number back accordingly.
-		 */
-		strip--;
-                if (td->td_stripoffset_p[strip] > TIFF_UINT64_MAX - td->td_stripbytecount_p[strip])
-                    return -1;
-		if (td->td_stripoffset_p[strip]+td->td_stripbytecount_p[strip] > filesize) {
-                    if( td->td_stripoffset_p[strip] >= filesize ) {
-                        /* Not sure what we should in that case... */
-                        td->td_stripbytecount_p[strip] = 0;
-                    } else {
-                        td->td_stripbytecount_p[strip] = filesize - td->td_stripoffset_p[strip];
-                    }
-                }
-	} else if (isTiled(tif)) {
-		uint64 bytespertile = TIFFTileSize64(tif);
-
-		for (strip = 0; strip < td->td_nstrips; strip++)
-		    td->td_stripbytecount_p[strip] = bytespertile;
-	} else {
-		uint64 rowbytes = TIFFScanlineSize64(tif);
-		uint32 rowsperstrip = td->td_imagelength/td->td_stripsperimage;
-		for (strip = 0; strip < td->td_nstrips; strip++)
-                {
-                    if( rowbytes > 0 && rowsperstrip > TIFF_UINT64_MAX / rowbytes )
-                        return -1;
-                    td->td_stripbytecount_p[strip] = rowbytes * rowsperstrip;
-                }
-	}
-	TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS);
-	if (!TIFFFieldSet(tif, FIELD_ROWSPERSTRIP))
-		td->td_rowsperstrip = td->td_imagelength;
-	return 1;
-}
-
-static void
-MissingRequired(TIFF* tif, const char* tagname)
-{
-	static const char module[] = "MissingRequired";
-
-	TIFFErrorExt(tif->tif_clientdata, module,
-	    "TIFF directory is missing required \"%s\" field",
-	    tagname);
+    const TIFFFieldArray *exifFieldArray;
+    exifFieldArray = _TIFFGetExifFields();
+    return TIFFReadCustomDirectory(tif, diroff, exifFieldArray);
 }
 
 /*
- * Check the directory offset against the list of already seen directory
- * offsets. This is a trick to prevent IFD looping. The one can create TIFF
- * file with looped directory pointers. We will maintain a list of already
- * seen directories and check every IFD offset against that list.
+ *--: EXIF-GPS custom directory reading as another special case of custom IFD.
  */
-static int
-TIFFCheckDirOffset(TIFF* tif, uint64 diroff)
+int TIFFReadGPSDirectory(TIFF *tif, toff_t diroff)
 {
-	uint16 n;
-
-	if (diroff == 0)			/* no more directories */
-		return 0;
-	if (tif->tif_dirnumber == 65535) {
-	    TIFFErrorExt(tif->tif_clientdata, "TIFFCheckDirOffset",
-			 "Cannot handle more than 65535 TIFF directories");
-	    return 0;
-	}
-
-	for (n = 0; n < tif->tif_dirnumber && tif->tif_dirlist; n++) {
-		if (tif->tif_dirlist[n] == diroff)
-			return 0;
-	}
-
-	tif->tif_dirnumber++;
-
-	if (tif->tif_dirlist == NULL || tif->tif_dirnumber > tif->tif_dirlistsize) {
-		uint64* new_dirlist;
-
-		/*
-		 * XXX: Reduce memory allocation granularity of the dirlist
-		 * array.
-		 */
-		new_dirlist = (uint64*)_TIFFCheckRealloc(tif, tif->tif_dirlist,
-		    tif->tif_dirnumber, 2 * sizeof(uint64), "for IFD list");
-		if (!new_dirlist)
-			return 0;
-		if( tif->tif_dirnumber >= 32768 )
-		    tif->tif_dirlistsize = 65535;
-		else
-		    tif->tif_dirlistsize = 2 * tif->tif_dirnumber;
-		tif->tif_dirlist = new_dirlist;
-	}
-
-	tif->tif_dirlist[tif->tif_dirnumber - 1] = diroff;
-
-	return 1;
+    const TIFFFieldArray *gpsFieldArray;
+    gpsFieldArray = _TIFFGetGpsFields();
+    return TIFFReadCustomDirectory(tif, diroff, gpsFieldArray);
 }
 
+static int EstimateStripByteCounts(TIFF *tif, TIFFDirEntry *dir,
+                                   uint16_t dircount)
+{
+    static const char module[] = "EstimateStripByteCounts";
+
+    TIFFDirEntry *dp;
+    TIFFDirectory *td = &tif->tif_dir;
+    uint32_t strip;
+
+    /* Do not try to load stripbytecount as we will compute it */
+    if (!_TIFFFillStrilesInternal(tif, 0))
+        return -1;
+
+    if (td->td_stripbytecount_p)
+        _TIFFfreeExt(tif, td->td_stripbytecount_p);
+    td->td_stripbytecount_p = (uint64_t *)_TIFFCheckMalloc(
+        tif, td->td_nstrips, sizeof(uint64_t), "for \"StripByteCounts\" array");
+    if (td->td_stripbytecount_p == NULL)
+        return -1;
+
+    if (td->td_compression != COMPRESSION_NONE)
+    {
+        uint64_t space;
+        uint64_t filesize;
+        uint16_t n;
+        filesize = TIFFGetFileSize(tif);
+        if (!(tif->tif_flags & TIFF_BIGTIFF))
+            space = sizeof(TIFFHeaderClassic) + 2 + dircount * 12 + 4;
+        else
+            space = sizeof(TIFFHeaderBig) + 8 + dircount * 20 + 8;
+        /* calculate amount of space used by indirect values */
+        for (dp = dir, n = dircount; n > 0; n--, dp++)
+        {
+            uint32_t typewidth;
+            uint64_t datasize;
+            typewidth = TIFFDataWidth((TIFFDataType)dp->tdir_type);
+            if (typewidth == 0)
+            {
+                TIFFErrorExtR(
+                    tif, module,
+                    "Cannot determine size of unknown tag type %" PRIu16,
+                    dp->tdir_type);
+                return -1;
+            }
+            if (dp->tdir_count > UINT64_MAX / typewidth)
+                return -1;
+            datasize = (uint64_t)typewidth * dp->tdir_count;
+            if (!(tif->tif_flags & TIFF_BIGTIFF))
+            {
+                if (datasize <= 4)
+                    datasize = 0;
+            }
+            else
+            {
+                if (datasize <= 8)
+                    datasize = 0;
+            }
+            if (space > UINT64_MAX - datasize)
+                return -1;
+            space += datasize;
+        }
+        if (filesize < space)
+            /* we should perhaps return in error ? */
+            space = filesize;
+        else
+            space = filesize - space;
+        if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
+            space /= td->td_samplesperpixel;
+        for (strip = 0; strip < td->td_nstrips; strip++)
+            td->td_stripbytecount_p[strip] = space;
+        /*
+         * This gross hack handles the case were the offset to
+         * the last strip is past the place where we think the strip
+         * should begin.  Since a strip of data must be contiguous,
+         * it's safe to assume that we've overestimated the amount
+         * of data in the strip and trim this number back accordingly.
+         */
+        strip--;
+        if (td->td_stripoffset_p[strip] >
+            UINT64_MAX - td->td_stripbytecount_p[strip])
+            return -1;
+        if (td->td_stripoffset_p[strip] + td->td_stripbytecount_p[strip] >
+            filesize)
+        {
+            if (td->td_stripoffset_p[strip] >= filesize)
+            {
+                /* Not sure what we should in that case... */
+                td->td_stripbytecount_p[strip] = 0;
+            }
+            else
+            {
+                td->td_stripbytecount_p[strip] =
+                    filesize - td->td_stripoffset_p[strip];
+            }
+        }
+    }
+    else if (isTiled(tif))
+    {
+        uint64_t bytespertile = TIFFTileSize64(tif);
+
+        for (strip = 0; strip < td->td_nstrips; strip++)
+            td->td_stripbytecount_p[strip] = bytespertile;
+    }
+    else
+    {
+        uint64_t rowbytes = TIFFScanlineSize64(tif);
+        uint32_t rowsperstrip = td->td_imagelength / td->td_stripsperimage;
+        for (strip = 0; strip < td->td_nstrips; strip++)
+        {
+            if (rowbytes > 0 && rowsperstrip > UINT64_MAX / rowbytes)
+                return -1;
+            td->td_stripbytecount_p[strip] = rowbytes * rowsperstrip;
+        }
+    }
+    TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS);
+    if (!TIFFFieldSet(tif, FIELD_ROWSPERSTRIP))
+        td->td_rowsperstrip = td->td_imagelength;
+    return 1;
+}
+
+static void MissingRequired(TIFF *tif, const char *tagname)
+{
+    static const char module[] = "MissingRequired";
+
+    TIFFErrorExtR(tif, module,
+                  "TIFF directory is missing required \"%s\" field", tagname);
+}
+
+static unsigned long hashFuncOffsetToNumber(const void *elt)
+{
+    const TIFFOffsetAndDirNumber *offsetAndDirNumber =
+        (const TIFFOffsetAndDirNumber *)elt;
+    const uint32_t hash = (uint32_t)(offsetAndDirNumber->offset >> 32) ^
+                          ((uint32_t)offsetAndDirNumber->offset & 0xFFFFFFFFU);
+    return hash;
+}
+
+static bool equalFuncOffsetToNumber(const void *elt1, const void *elt2)
+{
+    const TIFFOffsetAndDirNumber *offsetAndDirNumber1 =
+        (const TIFFOffsetAndDirNumber *)elt1;
+    const TIFFOffsetAndDirNumber *offsetAndDirNumber2 =
+        (const TIFFOffsetAndDirNumber *)elt2;
+    return offsetAndDirNumber1->offset == offsetAndDirNumber2->offset;
+}
+
+static unsigned long hashFuncNumberToOffset(const void *elt)
+{
+    const TIFFOffsetAndDirNumber *offsetAndDirNumber =
+        (const TIFFOffsetAndDirNumber *)elt;
+    return offsetAndDirNumber->dirNumber;
+}
+
+static bool equalFuncNumberToOffset(const void *elt1, const void *elt2)
+{
+    const TIFFOffsetAndDirNumber *offsetAndDirNumber1 =
+        (const TIFFOffsetAndDirNumber *)elt1;
+    const TIFFOffsetAndDirNumber *offsetAndDirNumber2 =
+        (const TIFFOffsetAndDirNumber *)elt2;
+    return offsetAndDirNumber1->dirNumber == offsetAndDirNumber2->dirNumber;
+}
+
+/*
+ * Check the directory number and offset against the list of already seen
+ * directory numbers and offsets. This is a trick to prevent IFD looping.
+ * The one can create TIFF file with looped directory pointers. We will
+ * maintain a list of already seen directories and check every IFD offset
+ * and its IFD number against that list. However, the offset of an IFD number
+ * can change - e.g. when writing updates to file.
+ * Returns 1 if all is ok; 0 if last directory or IFD loop is encountered,
+ * or an error has occurred.
+ */
+int _TIFFCheckDirNumberAndOffset(TIFF *tif, tdir_t dirn, uint64_t diroff)
+{
+    if (diroff == 0) /* no more directories */
+        return 0;
+
+    if (tif->tif_map_dir_offset_to_number == NULL)
+    {
+        tif->tif_map_dir_offset_to_number = TIFFHashSetNew(
+            hashFuncOffsetToNumber, equalFuncOffsetToNumber, free);
+        if (tif->tif_map_dir_offset_to_number == NULL)
+        {
+            TIFFErrorExtR(tif, "_TIFFCheckDirNumberAndOffset",
+                          "Not enough memory");
+            return 1;
+        }
+    }
+
+    if (tif->tif_map_dir_number_to_offset == NULL)
+    {
+        /* No free callback for this map, as it shares the same items as
+         * tif->tif_map_dir_offset_to_number. */
+        tif->tif_map_dir_number_to_offset = TIFFHashSetNew(
+            hashFuncNumberToOffset, equalFuncNumberToOffset, NULL);
+        if (tif->tif_map_dir_number_to_offset == NULL)
+        {
+            TIFFErrorExtR(tif, "_TIFFCheckDirNumberAndOffset",
+                          "Not enough memory");
+            return 1;
+        }
+    }
+
+    /* Check if offset is already in the list:
+     * - yes: check, if offset is at the same IFD number - if not, it is an IFD
+     * loop
+     * -  no: add to list or update offset at that IFD number
+     */
+    TIFFOffsetAndDirNumber entry;
+    entry.offset = diroff;
+    entry.dirNumber = dirn;
+
+    TIFFOffsetAndDirNumber *foundEntry =
+        (TIFFOffsetAndDirNumber *)TIFFHashSetLookup(
+            tif->tif_map_dir_offset_to_number, &entry);
+    if (foundEntry)
+    {
+        if (foundEntry->dirNumber == dirn)
+        {
+            return 1;
+        }
+        else
+        {
+            TIFFWarningExtR(tif, "_TIFFCheckDirNumberAndOffset",
+                            "TIFF directory %d has IFD looping to directory %u "
+                            "at offset 0x%" PRIx64 " (%" PRIu64 ")",
+                            (int)dirn - 1, foundEntry->dirNumber, diroff,
+                            diroff);
+            return 0;
+        }
+    }
+
+    /* Check if offset of an IFD has been changed and update offset of that IFD
+     * number. */
+    foundEntry = (TIFFOffsetAndDirNumber *)TIFFHashSetLookup(
+        tif->tif_map_dir_number_to_offset, &entry);
+    if (foundEntry)
+    {
+        if (foundEntry->offset != diroff)
+        {
+            TIFFOffsetAndDirNumber entryOld;
+            entryOld.offset = foundEntry->offset;
+            entryOld.dirNumber = dirn;
+            /* 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 */
+            TIFFOffsetAndDirNumber *foundEntryOld =
+                (TIFFOffsetAndDirNumber *)TIFFHashSetLookup(
+                    tif->tif_map_dir_number_to_offset, &entryOld);
+            if (foundEntryOld)
+            {
+                TIFFHashSetRemove(tif->tif_map_dir_number_to_offset,
+                                  foundEntryOld);
+            }
+            foundEntryOld = (TIFFOffsetAndDirNumber *)TIFFHashSetLookup(
+                tif->tif_map_dir_offset_to_number, &entryOld);
+            if (foundEntryOld)
+            {
+                TIFFHashSetRemove(tif->tif_map_dir_offset_to_number,
+                                  foundEntryOld);
+            }
+
+            TIFFOffsetAndDirNumber *entryPtr = (TIFFOffsetAndDirNumber *)malloc(
+                sizeof(TIFFOffsetAndDirNumber));
+            if (entryPtr == NULL)
+            {
+                return 0;
+            }
+
+            /* Add IFD offset and dirn to IFD directory list */
+            *entryPtr = entry;
+
+            if (!TIFFHashSetInsert(tif->tif_map_dir_offset_to_number, entryPtr))
+            {
+                TIFFErrorExtR(
+                    tif, "_TIFFCheckDirNumberAndOffset",
+                    "Insertion in tif_map_dir_offset_to_number failed");
+                return 0;
+            }
+            if (!TIFFHashSetInsert(tif->tif_map_dir_number_to_offset, entryPtr))
+            {
+                TIFFErrorExtR(
+                    tif, "_TIFFCheckDirNumberAndOffset",
+                    "Insertion in tif_map_dir_number_to_offset failed");
+                return 0;
+            }
+        }
+        return 1;
+    }
+
+    /* Arbitrary (hopefully big enough) limit */
+    if (TIFFHashSetSize(tif->tif_map_dir_offset_to_number) >=
+        TIFF_MAX_DIR_COUNT)
+    {
+        TIFFErrorExtR(tif, "_TIFFCheckDirNumberAndOffset",
+                      "Cannot handle more than %u TIFF directories",
+                      TIFF_MAX_DIR_COUNT);
+        return 0;
+    }
+
+    TIFFOffsetAndDirNumber *entryPtr =
+        (TIFFOffsetAndDirNumber *)malloc(sizeof(TIFFOffsetAndDirNumber));
+    if (entryPtr == NULL)
+    {
+        TIFFErrorExtR(tif, "_TIFFCheckDirNumberAndOffset",
+                      "malloc(sizeof(TIFFOffsetAndDirNumber)) failed");
+        return 0;
+    }
+
+    /* Add IFD offset and dirn to IFD directory list */
+    *entryPtr = entry;
+
+    if (!TIFFHashSetInsert(tif->tif_map_dir_offset_to_number, entryPtr))
+    {
+        TIFFErrorExtR(tif, "_TIFFCheckDirNumberAndOffset",
+                      "Insertion in tif_map_dir_offset_to_number failed");
+        return 0;
+    }
+    if (!TIFFHashSetInsert(tif->tif_map_dir_number_to_offset, entryPtr))
+    {
+        TIFFErrorExtR(tif, "_TIFFCheckDirNumberAndOffset",
+                      "Insertion in tif_map_dir_number_to_offset failed");
+        return 0;
+    }
+
+    return 1;
+} /* --- _TIFFCheckDirNumberAndOffset() ---*/
+
+/*
+ * Retrieve the matching IFD directory number of a given IFD offset
+ * from the list of directories already seen.
+ * Returns 1 if the offset was in the list and the directory number
+ * can be returned.
+ * Otherwise returns 0 or if an error occurred.
+ */
+int _TIFFGetDirNumberFromOffset(TIFF *tif, uint64_t diroff, tdir_t *dirn)
+{
+    if (diroff == 0) /* no more directories */
+        return 0;
+
+    /* Check if offset is already in the list and return matching directory
+     * number. Otherwise update IFD list using TIFFNumberOfDirectories() and
+     * search again in IFD list.
+     */
+    if (tif->tif_map_dir_offset_to_number == NULL)
+        return 0;
+    TIFFOffsetAndDirNumber entry;
+    entry.offset = diroff;
+    entry.dirNumber = 0; /* not used */
+
+    TIFFOffsetAndDirNumber *foundEntry =
+        (TIFFOffsetAndDirNumber *)TIFFHashSetLookup(
+            tif->tif_map_dir_offset_to_number, &entry);
+    if (foundEntry)
+    {
+        *dirn = foundEntry->dirNumber;
+        return 1;
+    }
+
+    /* This updates the directory list for all main-IFDs in the file. */
+    TIFFNumberOfDirectories(tif);
+
+    foundEntry = (TIFFOffsetAndDirNumber *)TIFFHashSetLookup(
+        tif->tif_map_dir_offset_to_number, &entry);
+    if (foundEntry)
+    {
+        *dirn = foundEntry->dirNumber;
+        return 1;
+    }
+
+    return 0;
+} /*--- _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.
  */
-static int
-CheckDirCount(TIFF* tif, TIFFDirEntry* dir, uint32 count)
+static int CheckDirCount(TIFF *tif, TIFFDirEntry *dir, uint32_t count)
 {
-	if ((uint64)count > dir->tdir_count) {
-		const TIFFField* fip = TIFFFieldWithTag(tif, dir->tdir_tag);
-		TIFFWarningExt(tif->tif_clientdata, tif->tif_name,
-	"incorrect count for field \"%s\" (" TIFF_UINT64_FORMAT ", expecting %u); tag ignored",
-		    fip ? fip->field_name : "unknown tagname",
-		    dir->tdir_count, count);
-		return (0);
-	} else if ((uint64)count < dir->tdir_count) {
-		const TIFFField* fip = TIFFFieldWithTag(tif, dir->tdir_tag);
-		TIFFWarningExt(tif->tif_clientdata, tif->tif_name,
-	"incorrect count for field \"%s\" (" TIFF_UINT64_FORMAT ", expecting %u); tag trimmed",
-		    fip ? fip->field_name : "unknown tagname",
-		    dir->tdir_count, count);
-		dir->tdir_count = count;
-		return (1);
-	}
-	return (1);
+    if ((uint64_t)count > dir->tdir_count)
+    {
+        const TIFFField *fip = TIFFFieldWithTag(tif, dir->tdir_tag);
+        TIFFWarningExtR(tif, tif->tif_name,
+                        "incorrect count for field \"%s\" (%" PRIu64
+                        ", expecting %" PRIu32 "); tag ignored",
+                        fip ? fip->field_name : "unknown tagname",
+                        dir->tdir_count, count);
+        return (0);
+    }
+    else if ((uint64_t)count < dir->tdir_count)
+    {
+        const TIFFField *fip = TIFFFieldWithTag(tif, dir->tdir_tag);
+        TIFFWarningExtR(tif, tif->tif_name,
+                        "incorrect count for field \"%s\" (%" PRIu64
+                        ", expecting %" PRIu32 "); tag trimmed",
+                        fip ? fip->field_name : "unknown tagname",
+                        dir->tdir_count, count);
+        dir->tdir_count = count;
+        return (1);
+    }
+    return (1);
 }
 
 /*
@@ -4710,1088 +5746,1546 @@
  * nextdiroff variable has been specified, read it too. Function returns a
  * number of fields in the directory or 0 if failed.
  */
-static uint16
-TIFFFetchDirectory(TIFF* tif, uint64 diroff, TIFFDirEntry** pdir,
-                   uint64 *nextdiroff)
+static uint16_t TIFFFetchDirectory(TIFF *tif, uint64_t diroff,
+                                   TIFFDirEntry **pdir, uint64_t *nextdiroff)
 {
-	static const char module[] = "TIFFFetchDirectory";
+    static const char module[] = "TIFFFetchDirectory";
 
-	void* origdir;
-	uint16 dircount16;
-	uint32 dirsize;
-	TIFFDirEntry* dir;
-	uint8* ma;
-	TIFFDirEntry* mb;
-	uint16 n;
+    void *origdir;
+    uint16_t dircount16;
+    uint32_t dirsize;
+    TIFFDirEntry *dir;
+    uint8_t *ma;
+    TIFFDirEntry *mb;
+    uint16_t n;
 
-	assert(pdir);
+    assert(pdir);
 
-	tif->tif_diroff = diroff;
-	if (nextdiroff)
-		*nextdiroff = 0;
-	if (!isMapped(tif)) {
-		if (!SeekOK(tif, tif->tif_diroff)) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-				"%s: Seek error accessing TIFF directory",
-				tif->tif_name);
-			return 0;
-		}
-		if (!(tif->tif_flags&TIFF_BIGTIFF))
-		{
-			if (!ReadOK(tif, &dircount16, sizeof (uint16))) {
-				TIFFErrorExt(tif->tif_clientdata, module,
-				    "%s: Can not read TIFF directory count",
-				    tif->tif_name);
-				return 0;
-			}
-			if (tif->tif_flags & TIFF_SWAB)
-				TIFFSwabShort(&dircount16);
-			if (dircount16>4096)
-			{
-				TIFFErrorExt(tif->tif_clientdata, module,
-				    "Sanity check on directory count failed, this is probably not a valid IFD offset");
-				return 0;
-			}
-			dirsize = 12;
-		} else {
-			uint64 dircount64;
-			if (!ReadOK(tif, &dircount64, sizeof (uint64))) {
-				TIFFErrorExt(tif->tif_clientdata, module,
-					"%s: Can not read TIFF directory count",
-					tif->tif_name);
-				return 0;
-			}
-			if (tif->tif_flags & TIFF_SWAB)
-				TIFFSwabLong8(&dircount64);
-			if (dircount64>4096)
-			{
-				TIFFErrorExt(tif->tif_clientdata, module,
-				    "Sanity check on directory count failed, this is probably not a valid IFD offset");
-				return 0;
-			}
-			dircount16 = (uint16)dircount64;
-			dirsize = 20;
-		}
-		origdir = _TIFFCheckMalloc(tif, dircount16,
-		    dirsize, "to read TIFF directory");
-		if (origdir == NULL)
-			return 0;
-		if (!ReadOK(tif, origdir, (tmsize_t)(dircount16*dirsize))) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-				"%.100s: Can not read TIFF directory",
-				tif->tif_name);
-			_TIFFfree(origdir);
-			return 0;
-		}
-		/*
-		 * Read offset to next directory for sequential scans if
-		 * needed.
-		 */
-		if (nextdiroff)
-		{
-			if (!(tif->tif_flags&TIFF_BIGTIFF))
-			{
-				uint32 nextdiroff32;
-				if (!ReadOK(tif, &nextdiroff32, sizeof(uint32)))
-					nextdiroff32 = 0;
-				if (tif->tif_flags&TIFF_SWAB)
-					TIFFSwabLong(&nextdiroff32);
-				*nextdiroff=nextdiroff32;
-			} else {
-				if (!ReadOK(tif, nextdiroff, sizeof(uint64)))
-					*nextdiroff = 0;
-				if (tif->tif_flags&TIFF_SWAB)
-					TIFFSwabLong8(nextdiroff);
-			}
-		}
-	} else {
-		tmsize_t m;
-		tmsize_t off;
-		if (tif->tif_diroff > (uint64)TIFF_INT64_MAX)
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"Can not read TIFF directory count");
-			return(0);
-		}
-		off = (tmsize_t) tif->tif_diroff;
+    tif->tif_diroff = diroff;
+    if (nextdiroff)
+        *nextdiroff = 0;
+    if (!isMapped(tif))
+    {
+        if (!SeekOK(tif, tif->tif_diroff))
+        {
+            TIFFErrorExtR(tif, module,
+                          "%s: Seek error accessing TIFF directory",
+                          tif->tif_name);
+            return 0;
+        }
+        if (!(tif->tif_flags & TIFF_BIGTIFF))
+        {
+            if (!ReadOK(tif, &dircount16, sizeof(uint16_t)))
+            {
+                TIFFErrorExtR(tif, module,
+                              "%s: Can not read TIFF directory count",
+                              tif->tif_name);
+                return 0;
+            }
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabShort(&dircount16);
+            if (dircount16 > 4096)
+            {
+                TIFFErrorExtR(tif, module,
+                              "Sanity check on directory count failed, this is "
+                              "probably not a valid IFD offset");
+                return 0;
+            }
+            dirsize = 12;
+        }
+        else
+        {
+            uint64_t dircount64;
+            if (!ReadOK(tif, &dircount64, sizeof(uint64_t)))
+            {
+                TIFFErrorExtR(tif, module,
+                              "%s: Can not read TIFF directory count",
+                              tif->tif_name);
+                return 0;
+            }
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong8(&dircount64);
+            if (dircount64 > 4096)
+            {
+                TIFFErrorExtR(tif, module,
+                              "Sanity check on directory count failed, this is "
+                              "probably not a valid IFD offset");
+                return 0;
+            }
+            dircount16 = (uint16_t)dircount64;
+            dirsize = 20;
+        }
+        origdir = _TIFFCheckMalloc(tif, dircount16, dirsize,
+                                   "to read TIFF directory");
+        if (origdir == NULL)
+            return 0;
+        if (!ReadOK(tif, origdir, (tmsize_t)(dircount16 * dirsize)))
+        {
+            TIFFErrorExtR(tif, module, "%.100s: Can not read TIFF directory",
+                          tif->tif_name);
+            _TIFFfreeExt(tif, origdir);
+            return 0;
+        }
+        /*
+         * Read offset to next directory for sequential scans if
+         * needed.
+         */
+        if (nextdiroff)
+        {
+            if (!(tif->tif_flags & TIFF_BIGTIFF))
+            {
+                uint32_t nextdiroff32;
+                if (!ReadOK(tif, &nextdiroff32, sizeof(uint32_t)))
+                    nextdiroff32 = 0;
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(&nextdiroff32);
+                *nextdiroff = nextdiroff32;
+            }
+            else
+            {
+                if (!ReadOK(tif, nextdiroff, sizeof(uint64_t)))
+                    *nextdiroff = 0;
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8(nextdiroff);
+            }
+        }
+    }
+    else
+    {
+        tmsize_t m;
+        tmsize_t off;
+        if (tif->tif_diroff > (uint64_t)INT64_MAX)
+        {
+            TIFFErrorExtR(tif, module, "Can not read TIFF directory count");
+            return (0);
+        }
+        off = (tmsize_t)tif->tif_diroff;
 
-		/*
-		 * Check for integer overflow when validating the dir_off,
-		 * otherwise a very high offset may cause an OOB read and
-		 * crash the client. Make two comparisons instead of
-		 *
-		 *  off + sizeof(uint16) > tif->tif_size
-		 *
-		 * to avoid overflow.
-		 */
-		if (!(tif->tif_flags&TIFF_BIGTIFF))
-		{
-			m=off+sizeof(uint16);
-			if ((m<off)||(m<(tmsize_t)sizeof(uint16))||(m>tif->tif_size)) {
-				TIFFErrorExt(tif->tif_clientdata, module,
-					"Can not read TIFF directory count");
-				return 0;
-			} else {
-				_TIFFmemcpy(&dircount16, tif->tif_base + off,
-					    sizeof(uint16));
-			}
-			off += sizeof (uint16);
-			if (tif->tif_flags & TIFF_SWAB)
-				TIFFSwabShort(&dircount16);
-			if (dircount16>4096)
-			{
-				TIFFErrorExt(tif->tif_clientdata, module,
-				    "Sanity check on directory count failed, this is probably not a valid IFD offset");
-				return 0;
-			}
-			dirsize = 12;
-		}
-		else
-		{
-			uint64 dircount64;
-			m=off+sizeof(uint64);
-			if ((m<off)||(m<(tmsize_t)sizeof(uint64))||(m>tif->tif_size)) {
-				TIFFErrorExt(tif->tif_clientdata, module,
-					"Can not read TIFF directory count");
-				return 0;
-			} else {
-				_TIFFmemcpy(&dircount64, tif->tif_base + off,
-					    sizeof(uint64));
-			}
-			off += sizeof (uint64);
-			if (tif->tif_flags & TIFF_SWAB)
-				TIFFSwabLong8(&dircount64);
-			if (dircount64>4096)
-			{
-				TIFFErrorExt(tif->tif_clientdata, module,
-				    "Sanity check on directory count failed, this is probably not a valid IFD offset");
-				return 0;
-			}
-			dircount16 = (uint16)dircount64;
-			dirsize = 20;
-		}
-		if (dircount16 == 0 )
-		{
-			TIFFErrorExt(tif->tif_clientdata, module,
-			             "Sanity check on directory count failed, zero tag directories not supported");
-			return 0;
-		}
-		origdir = _TIFFCheckMalloc(tif, dircount16,
-						dirsize,
-						"to read TIFF directory");
-		if (origdir == NULL)
-			return 0;
-		m=off+dircount16*dirsize;
-		if ((m<off)||(m<(tmsize_t)(dircount16*dirsize))||(m>tif->tif_size)) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-				     "Can not read TIFF directory");
-			_TIFFfree(origdir);
-			return 0;
-		} else {
-			_TIFFmemcpy(origdir, tif->tif_base + off,
-				    dircount16 * dirsize);
-		}
-		if (nextdiroff) {
-			off += dircount16 * dirsize;
-			if (!(tif->tif_flags&TIFF_BIGTIFF))
-			{
-				uint32 nextdiroff32;
-				m=off+sizeof(uint32);
-				if ((m<off)||(m<(tmsize_t)sizeof(uint32))||(m>tif->tif_size))
-					nextdiroff32 = 0;
-				else
-					_TIFFmemcpy(&nextdiroff32, tif->tif_base + off,
-						    sizeof (uint32));
-				if (tif->tif_flags&TIFF_SWAB)
-					TIFFSwabLong(&nextdiroff32);
-				*nextdiroff = nextdiroff32;
-			}
-			else
-			{
-				m=off+sizeof(uint64);
-				if ((m<off)||(m<(tmsize_t)sizeof(uint64))||(m>tif->tif_size))
-					*nextdiroff = 0;
-				else
-					_TIFFmemcpy(nextdiroff, tif->tif_base + off,
-						    sizeof (uint64));
-				if (tif->tif_flags&TIFF_SWAB)
-					TIFFSwabLong8(nextdiroff);
-			}
-		}
-	}
-	dir = (TIFFDirEntry*)_TIFFCheckMalloc(tif, dircount16,
-						sizeof(TIFFDirEntry),
-						"to read TIFF directory");
-	if (dir==0)
-	{
-		_TIFFfree(origdir);
-		return 0;
-	}
-	ma=(uint8*)origdir;
-	mb=dir;
-	for (n=0; n<dircount16; n++)
-	{
-		mb->tdir_ignore = FALSE;
-		if (tif->tif_flags&TIFF_SWAB)
-			TIFFSwabShort((uint16*)ma);
-		mb->tdir_tag=*(uint16*)ma;
-		ma+=sizeof(uint16);
-		if (tif->tif_flags&TIFF_SWAB)
-			TIFFSwabShort((uint16*)ma);
-		mb->tdir_type=*(uint16*)ma;
-		ma+=sizeof(uint16);
-		if (!(tif->tif_flags&TIFF_BIGTIFF))
-		{
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabLong((uint32*)ma);
-			mb->tdir_count=(uint64)(*(uint32*)ma);
-			ma+=sizeof(uint32);
-			mb->tdir_offset.toff_long8=0;
-			*(uint32*)(&mb->tdir_offset)=*(uint32*)ma;
-			ma+=sizeof(uint32);
-		}
-		else
-		{
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabLong8((uint64*)ma);
-                        mb->tdir_count=TIFFReadUInt64(ma);
-			ma+=sizeof(uint64);
-			mb->tdir_offset.toff_long8=TIFFReadUInt64(ma);
-			ma+=sizeof(uint64);
-		}
-		mb++;
-	}
-	_TIFFfree(origdir);
-	*pdir = dir;
-	return dircount16;
+        /*
+         * Check for integer overflow when validating the dir_off,
+         * otherwise a very high offset may cause an OOB read and
+         * crash the client. Make two comparisons instead of
+         *
+         *  off + sizeof(uint16_t) > tif->tif_size
+         *
+         * to avoid overflow.
+         */
+        if (!(tif->tif_flags & TIFF_BIGTIFF))
+        {
+            m = off + sizeof(uint16_t);
+            if ((m < off) || (m < (tmsize_t)sizeof(uint16_t)) ||
+                (m > tif->tif_size))
+            {
+                TIFFErrorExtR(tif, module, "Can not read TIFF directory count");
+                return 0;
+            }
+            else
+            {
+                _TIFFmemcpy(&dircount16, tif->tif_base + off, sizeof(uint16_t));
+            }
+            off += sizeof(uint16_t);
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabShort(&dircount16);
+            if (dircount16 > 4096)
+            {
+                TIFFErrorExtR(tif, module,
+                              "Sanity check on directory count failed, this is "
+                              "probably not a valid IFD offset");
+                return 0;
+            }
+            dirsize = 12;
+        }
+        else
+        {
+            uint64_t dircount64;
+            m = off + sizeof(uint64_t);
+            if ((m < off) || (m < (tmsize_t)sizeof(uint64_t)) ||
+                (m > tif->tif_size))
+            {
+                TIFFErrorExtR(tif, module, "Can not read TIFF directory count");
+                return 0;
+            }
+            else
+            {
+                _TIFFmemcpy(&dircount64, tif->tif_base + off, sizeof(uint64_t));
+            }
+            off += sizeof(uint64_t);
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong8(&dircount64);
+            if (dircount64 > 4096)
+            {
+                TIFFErrorExtR(tif, module,
+                              "Sanity check on directory count failed, this is "
+                              "probably not a valid IFD offset");
+                return 0;
+            }
+            dircount16 = (uint16_t)dircount64;
+            dirsize = 20;
+        }
+        if (dircount16 == 0)
+        {
+            TIFFErrorExtR(tif, module,
+                          "Sanity check on directory count failed, zero tag "
+                          "directories not supported");
+            return 0;
+        }
+        origdir = _TIFFCheckMalloc(tif, dircount16, dirsize,
+                                   "to read TIFF directory");
+        if (origdir == NULL)
+            return 0;
+        m = off + dircount16 * dirsize;
+        if ((m < off) || (m < (tmsize_t)(dircount16 * dirsize)) ||
+            (m > tif->tif_size))
+        {
+            TIFFErrorExtR(tif, module, "Can not read TIFF directory");
+            _TIFFfreeExt(tif, origdir);
+            return 0;
+        }
+        else
+        {
+            _TIFFmemcpy(origdir, tif->tif_base + off, dircount16 * dirsize);
+        }
+        if (nextdiroff)
+        {
+            off += dircount16 * dirsize;
+            if (!(tif->tif_flags & TIFF_BIGTIFF))
+            {
+                uint32_t nextdiroff32;
+                m = off + sizeof(uint32_t);
+                if ((m < off) || (m < (tmsize_t)sizeof(uint32_t)) ||
+                    (m > tif->tif_size))
+                    nextdiroff32 = 0;
+                else
+                    _TIFFmemcpy(&nextdiroff32, tif->tif_base + off,
+                                sizeof(uint32_t));
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(&nextdiroff32);
+                *nextdiroff = nextdiroff32;
+            }
+            else
+            {
+                m = off + sizeof(uint64_t);
+                if ((m < off) || (m < (tmsize_t)sizeof(uint64_t)) ||
+                    (m > tif->tif_size))
+                    *nextdiroff = 0;
+                else
+                    _TIFFmemcpy(nextdiroff, tif->tif_base + off,
+                                sizeof(uint64_t));
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8(nextdiroff);
+            }
+        }
+    }
+    dir = (TIFFDirEntry *)_TIFFCheckMalloc(
+        tif, dircount16, sizeof(TIFFDirEntry), "to read TIFF directory");
+    if (dir == 0)
+    {
+        _TIFFfreeExt(tif, origdir);
+        return 0;
+    }
+    ma = (uint8_t *)origdir;
+    mb = dir;
+    for (n = 0; n < dircount16; n++)
+    {
+        mb->tdir_ignore = FALSE;
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabShort((uint16_t *)ma);
+        mb->tdir_tag = *(uint16_t *)ma;
+        ma += sizeof(uint16_t);
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabShort((uint16_t *)ma);
+        mb->tdir_type = *(uint16_t *)ma;
+        ma += sizeof(uint16_t);
+        if (!(tif->tif_flags & TIFF_BIGTIFF))
+        {
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong((uint32_t *)ma);
+            mb->tdir_count = (uint64_t)(*(uint32_t *)ma);
+            ma += sizeof(uint32_t);
+            mb->tdir_offset.toff_long8 = 0;
+            *(uint32_t *)(&mb->tdir_offset) = *(uint32_t *)ma;
+            ma += sizeof(uint32_t);
+        }
+        else
+        {
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong8((uint64_t *)ma);
+            mb->tdir_count = TIFFReadUInt64(ma);
+            ma += sizeof(uint64_t);
+            mb->tdir_offset.toff_long8 = TIFFReadUInt64(ma);
+            ma += sizeof(uint64_t);
+        }
+        mb++;
+    }
+    _TIFFfreeExt(tif, origdir);
+    *pdir = dir;
+    return dircount16;
 }
 
 /*
  * Fetch a tag that is not handled by special case code.
  */
-static int
-TIFFFetchNormalTag(TIFF* tif, TIFFDirEntry* dp, int recover)
+static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
 {
-	static const char module[] = "TIFFFetchNormalTag";
-	enum TIFFReadDirEntryErr err;
-	uint32 fii;
-	const TIFFField* fip = NULL;
-	TIFFReadDirectoryFindFieldInfo(tif,dp->tdir_tag,&fii);
-        if( fii == FAILED_FII )
+    static const char module[] = "TIFFFetchNormalTag";
+    enum TIFFReadDirEntryErr err;
+    uint32_t fii;
+    const TIFFField *fip = NULL;
+    TIFFReadDirectoryFindFieldInfo(tif, dp->tdir_tag, &fii);
+    if (fii == FAILED_FII)
+    {
+        TIFFErrorExtR(tif, "TIFFFetchNormalTag",
+                      "No definition found for tag %" PRIu16, dp->tdir_tag);
+        return 0;
+    }
+    fip = tif->tif_fields[fii];
+    assert(fip != NULL); /* should not happen */
+    assert(fip->set_field_type !=
+           TIFF_SETGET_OTHER); /* if so, we shouldn't arrive here but deal with
+                                  this in specialized code */
+    assert(fip->set_field_type !=
+           TIFF_SETGET_INT); /* if so, we shouldn't arrive here as this is only
+                                the case for pseudo-tags */
+    err = TIFFReadDirEntryErrOk;
+    switch (fip->set_field_type)
+    {
+        case TIFF_SETGET_UNDEFINED:
+            TIFFErrorExtR(
+                tif, "TIFFFetchNormalTag",
+                "Defined set_field_type of custom tag %u (%s) is "
+                "TIFF_SETGET_UNDEFINED and thus tag is not read from file",
+                fip->field_tag, fip->field_name);
+            break;
+        case TIFF_SETGET_ASCII:
         {
-            TIFFErrorExt(tif->tif_clientdata, "TIFFFetchNormalTag",
-                         "No definition found for tag %d",
-                         dp->tdir_tag);
-            return 0;
+            uint8_t *data;
+            assert(fip->field_passcount == 0);
+            err = TIFFReadDirEntryByteArray(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                size_t mb = 0;
+                int n;
+                if (data != NULL)
+                {
+                    if (dp->tdir_count > 0 && data[dp->tdir_count - 1] == 0)
+                    {
+                        /* optimization: if data is known to be 0 terminated, we
+                         * can use strlen() */
+                        mb = strlen((const char *)data);
+                    }
+                    else
+                    {
+                        /* general case. equivalent to non-portable */
+                        /* mb = strnlen((const char*)data,
+                         * (uint32_t)dp->tdir_count); */
+                        uint8_t *ma = data;
+                        while (mb < (uint32_t)dp->tdir_count)
+                        {
+                            if (*ma == 0)
+                                break;
+                            ma++;
+                            mb++;
+                        }
+                    }
+                }
+                if (mb + 1 < (uint32_t)dp->tdir_count)
+                    TIFFWarningExtR(
+                        tif, module,
+                        "ASCII value for tag \"%s\" contains null byte in "
+                        "value; value incorrectly truncated during reading due "
+                        "to implementation limitations",
+                        fip->field_name);
+                else if (mb + 1 > (uint32_t)dp->tdir_count)
+                {
+                    uint8_t *o;
+                    TIFFWarningExtR(
+                        tif, module,
+                        "ASCII value for tag \"%s\" does not end in null byte",
+                        fip->field_name);
+                    /* TIFFReadDirEntryArrayWithLimit() ensures this can't be
+                     * larger than MAX_SIZE_TAG_DATA */
+                    assert((uint32_t)dp->tdir_count + 1 == dp->tdir_count + 1);
+                    o = _TIFFmallocExt(tif, (uint32_t)dp->tdir_count + 1);
+                    if (o == NULL)
+                    {
+                        if (data != NULL)
+                            _TIFFfreeExt(tif, data);
+                        return (0);
+                    }
+                    if (dp->tdir_count > 0)
+                    {
+                        _TIFFmemcpy(o, data, (uint32_t)dp->tdir_count);
+                    }
+                    o[(uint32_t)dp->tdir_count] = 0;
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    data = o;
+                }
+                n = TIFFSetField(tif, dp->tdir_tag, data);
+                if (data != 0)
+                    _TIFFfreeExt(tif, data);
+                if (!n)
+                    return (0);
+            }
         }
-	fip=tif->tif_fields[fii];
-	assert(fip != NULL); /* should not happen */
-	assert(fip->set_field_type!=TIFF_SETGET_OTHER);  /* if so, we shouldn't arrive here but deal with this in specialized code */
-	assert(fip->set_field_type!=TIFF_SETGET_INT);    /* if so, we shouldn't arrive here as this is only the case for pseudo-tags */
-	err=TIFFReadDirEntryErrOk;
-	switch (fip->set_field_type)
-	{
-		case TIFF_SETGET_UNDEFINED:
-			break;
-		case TIFF_SETGET_ASCII:
-			{
-				uint8* data;
-				assert(fip->field_passcount==0);
-				err=TIFFReadDirEntryByteArray(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					uint32 mb = 0;
-					int n;
-					if (data != NULL)
-					{
-					    uint8* ma = data;
-					    while (mb<(uint32)dp->tdir_count)
-					    {
-					            if (*ma==0)
-					                    break;
-					            ma++;
-					            mb++;
-					    }
-					}
-					if (mb+1<(uint32)dp->tdir_count)
-						TIFFWarningExt(tif->tif_clientdata,module,"ASCII value for tag \"%s\" contains null byte in value; value incorrectly truncated during reading due to implementation limitations",fip->field_name);
-					else if (mb+1>(uint32)dp->tdir_count)
-					{
-						uint8* o;
-						TIFFWarningExt(tif->tif_clientdata,module,"ASCII value for tag \"%s\" does not end in null byte",fip->field_name);
-						if ((uint32)dp->tdir_count+1!=dp->tdir_count+1)
-							o=NULL;
-						else
-							o=_TIFFmalloc((uint32)dp->tdir_count+1);
-						if (o==NULL)
-						{
-							if (data!=NULL)
-								_TIFFfree(data);
-							return(0);
-						}
-						_TIFFmemcpy(o,data,(uint32)dp->tdir_count);
-						o[(uint32)dp->tdir_count]=0;
-						if (data!=0)
-							_TIFFfree(data);
-						data=o;
-					}
-					n=TIFFSetField(tif,dp->tdir_tag,data);
-					if (data!=0)
-						_TIFFfree(data);
-					if (!n)
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_UINT8:
-			{
-				uint8 data=0;
-				assert(fip->field_readcount==1);
-				assert(fip->field_passcount==0);
-				err=TIFFReadDirEntryByte(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					if (!TIFFSetField(tif,dp->tdir_tag,data))
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_UINT16:
-			{
-				uint16 data;
-				assert(fip->field_readcount==1);
-				assert(fip->field_passcount==0);
-				err=TIFFReadDirEntryShort(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					if (!TIFFSetField(tif,dp->tdir_tag,data))
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_UINT32:
-			{
-				uint32 data;
-				assert(fip->field_readcount==1);
-				assert(fip->field_passcount==0);
-				err=TIFFReadDirEntryLong(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					if (!TIFFSetField(tif,dp->tdir_tag,data))
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_UINT64:
-			{
-				uint64 data;
-				assert(fip->field_readcount==1);
-				assert(fip->field_passcount==0);
-				err=TIFFReadDirEntryLong8(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					if (!TIFFSetField(tif,dp->tdir_tag,data))
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_FLOAT:
-			{
-				float data;
-				assert(fip->field_readcount==1);
-				assert(fip->field_passcount==0);
-				err=TIFFReadDirEntryFloat(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					if (!TIFFSetField(tif,dp->tdir_tag,data))
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_DOUBLE:
-			{
-				double data;
-				assert(fip->field_readcount==1);
-				assert(fip->field_passcount==0);
-				err=TIFFReadDirEntryDouble(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					if (!TIFFSetField(tif,dp->tdir_tag,data))
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_IFD8:
-			{
-				uint64 data;
-				assert(fip->field_readcount==1);
-				assert(fip->field_passcount==0);
-				err=TIFFReadDirEntryIfd8(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					if (!TIFFSetField(tif,dp->tdir_tag,data))
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_UINT16_PAIR:
-			{
-				uint16* data;
-				assert(fip->field_readcount==2);
-				assert(fip->field_passcount==0);
-				if (dp->tdir_count!=2) {
-					TIFFWarningExt(tif->tif_clientdata,module,
-						       "incorrect count for field \"%s\", expected 2, got %d",
-						       fip->field_name,(int)dp->tdir_count);
-					return(0);
-				}
-				err=TIFFReadDirEntryShortArray(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					int m;
-					m=TIFFSetField(tif,dp->tdir_tag,data[0],data[1]);
-					_TIFFfree(data);
-					if (!m)
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_C0_UINT8:
-			{
-				uint8* data;
-				assert(fip->field_readcount>=1);
-				assert(fip->field_passcount==0);
-				if (dp->tdir_count!=(uint64)fip->field_readcount) {
-					TIFFWarningExt(tif->tif_clientdata,module,
-						       "incorrect count for field \"%s\", expected %d, got %d",
-						       fip->field_name,(int) fip->field_readcount, (int)dp->tdir_count);
-					return 0;
-				}
-				else
-				{
-					err=TIFFReadDirEntryByteArray(tif,dp,&data);
-					if (err==TIFFReadDirEntryErrOk)
-					{
-						int m;
-						m=TIFFSetField(tif,dp->tdir_tag,data);
-						if (data!=0)
-							_TIFFfree(data);
-						if (!m)
-							return(0);
-					}
-				}
-			}
-			break;
-		case TIFF_SETGET_C0_UINT16:
-			{
-				uint16* data;
-				assert(fip->field_readcount>=1);
-				assert(fip->field_passcount==0);
-				if (dp->tdir_count!=(uint64)fip->field_readcount)
-                                    /* corrupt file */;
-				else
-				{
-					err=TIFFReadDirEntryShortArray(tif,dp,&data);
-					if (err==TIFFReadDirEntryErrOk)
-					{
-						int m;
-						m=TIFFSetField(tif,dp->tdir_tag,data);
-						if (data!=0)
-							_TIFFfree(data);
-						if (!m)
-							return(0);
-					}
-				}
-			}
-			break;
-		case TIFF_SETGET_C0_UINT32:
-			{
-				uint32* data;
-				assert(fip->field_readcount>=1);
-				assert(fip->field_passcount==0);
-				if (dp->tdir_count!=(uint64)fip->field_readcount)
-                                    /* corrupt file */;
-				else
-				{
-					err=TIFFReadDirEntryLongArray(tif,dp,&data);
-					if (err==TIFFReadDirEntryErrOk)
-					{
-						int m;
-						m=TIFFSetField(tif,dp->tdir_tag,data);
-						if (data!=0)
-							_TIFFfree(data);
-						if (!m)
-							return(0);
-					}
-				}
-			}
-			break;
-		case TIFF_SETGET_C0_FLOAT:
-			{
-				float* data;
-				assert(fip->field_readcount>=1);
-				assert(fip->field_passcount==0);
-				if (dp->tdir_count!=(uint64)fip->field_readcount)
-                                    /* corrupt file */;
-				else
-				{
-					err=TIFFReadDirEntryFloatArray(tif,dp,&data);
-					if (err==TIFFReadDirEntryErrOk)
-					{
-						int m;
-						m=TIFFSetField(tif,dp->tdir_tag,data);
-						if (data!=0)
-							_TIFFfree(data);
-						if (!m)
-							return(0);
-					}
-				}
-			}
-			break;
-		case TIFF_SETGET_C16_ASCII:
-			{
-				uint8* data;
-				assert(fip->field_readcount==TIFF_VARIABLE);
-				assert(fip->field_passcount==1);
-				if (dp->tdir_count>0xFFFF)
-					err=TIFFReadDirEntryErrCount;
-				else
-				{
-					err=TIFFReadDirEntryByteArray(tif,dp,&data);
-					if (err==TIFFReadDirEntryErrOk)
-					{
-						int m;
-						if( data != 0 && dp->tdir_count > 0 && data[dp->tdir_count-1] != '\0' )
-						{
-						    TIFFWarningExt(tif->tif_clientdata,module,"ASCII value for tag \"%s\" does not end in null byte. Forcing it to be null",fip->field_name);
-						    data[dp->tdir_count-1] = '\0';
-						}
-						m=TIFFSetField(tif,dp->tdir_tag,(uint16)(dp->tdir_count),data);
-						if (data!=0)
-							_TIFFfree(data);
-						if (!m)
-							return(0);
-					}
-				}
-			}
-			break;
-		case TIFF_SETGET_C16_UINT8:
-			{
-				uint8* data;
-				assert(fip->field_readcount==TIFF_VARIABLE);
-				assert(fip->field_passcount==1);
-				if (dp->tdir_count>0xFFFF)
-					err=TIFFReadDirEntryErrCount;
-				else
-				{
-					err=TIFFReadDirEntryByteArray(tif,dp,&data);
-					if (err==TIFFReadDirEntryErrOk)
-					{
-						int m;
-						m=TIFFSetField(tif,dp->tdir_tag,(uint16)(dp->tdir_count),data);
-						if (data!=0)
-							_TIFFfree(data);
-						if (!m)
-							return(0);
-					}
-				}
-			}
-			break;
-		case TIFF_SETGET_C16_UINT16:
-			{
-				uint16* data;
-				assert(fip->field_readcount==TIFF_VARIABLE);
-				assert(fip->field_passcount==1);
-				if (dp->tdir_count>0xFFFF)
-					err=TIFFReadDirEntryErrCount;
-				else
-				{
-					err=TIFFReadDirEntryShortArray(tif,dp,&data);
-					if (err==TIFFReadDirEntryErrOk)
-					{
-						int m;
-						m=TIFFSetField(tif,dp->tdir_tag,(uint16)(dp->tdir_count),data);
-						if (data!=0)
-							_TIFFfree(data);
-						if (!m)
-							return(0);
-					}
-				}
-			}
-			break;
-		case TIFF_SETGET_C16_UINT32:
-			{
-				uint32* data;
-				assert(fip->field_readcount==TIFF_VARIABLE);
-				assert(fip->field_passcount==1);
-				if (dp->tdir_count>0xFFFF)
-					err=TIFFReadDirEntryErrCount;
-				else
-				{
-					err=TIFFReadDirEntryLongArray(tif,dp,&data);
-					if (err==TIFFReadDirEntryErrOk)
-					{
-						int m;
-						m=TIFFSetField(tif,dp->tdir_tag,(uint16)(dp->tdir_count),data);
-						if (data!=0)
-							_TIFFfree(data);
-						if (!m)
-							return(0);
-					}
-				}
-			}
-			break;
-		case TIFF_SETGET_C16_UINT64:
-			{
-				uint64* data;
-				assert(fip->field_readcount==TIFF_VARIABLE);
-				assert(fip->field_passcount==1);
-				if (dp->tdir_count>0xFFFF)
-					err=TIFFReadDirEntryErrCount;
-				else
-				{
-					err=TIFFReadDirEntryLong8Array(tif,dp,&data);
-					if (err==TIFFReadDirEntryErrOk)
-					{
-						int m;
-						m=TIFFSetField(tif,dp->tdir_tag,(uint16)(dp->tdir_count),data);
-						if (data!=0)
-							_TIFFfree(data);
-						if (!m)
-							return(0);
-					}
-				}
-			}
-			break;
-		case TIFF_SETGET_C16_FLOAT:
-			{
-				float* data;
-				assert(fip->field_readcount==TIFF_VARIABLE);
-				assert(fip->field_passcount==1);
-				if (dp->tdir_count>0xFFFF)
-					err=TIFFReadDirEntryErrCount;
-				else
-				{
-					err=TIFFReadDirEntryFloatArray(tif,dp,&data);
-					if (err==TIFFReadDirEntryErrOk)
-					{
-						int m;
-						m=TIFFSetField(tif,dp->tdir_tag,(uint16)(dp->tdir_count),data);
-						if (data!=0)
-							_TIFFfree(data);
-						if (!m)
-							return(0);
-					}
-				}
-			}
-			break;
-		case TIFF_SETGET_C16_DOUBLE:
-			{
-				double* data;
-				assert(fip->field_readcount==TIFF_VARIABLE);
-				assert(fip->field_passcount==1);
-				if (dp->tdir_count>0xFFFF)
-					err=TIFFReadDirEntryErrCount;
-				else
-				{
-					err=TIFFReadDirEntryDoubleArray(tif,dp,&data);
-					if (err==TIFFReadDirEntryErrOk)
-					{
-						int m;
-						m=TIFFSetField(tif,dp->tdir_tag,(uint16)(dp->tdir_count),data);
-						if (data!=0)
-							_TIFFfree(data);
-						if (!m)
-							return(0);
-					}
-				}
-			}
-			break;
-		case TIFF_SETGET_C16_IFD8:
-			{
-				uint64* data;
-				assert(fip->field_readcount==TIFF_VARIABLE);
-				assert(fip->field_passcount==1);
-				if (dp->tdir_count>0xFFFF)
-					err=TIFFReadDirEntryErrCount;
-				else
-				{
-					err=TIFFReadDirEntryIfd8Array(tif,dp,&data);
-					if (err==TIFFReadDirEntryErrOk)
-					{
-						int m;
-						m=TIFFSetField(tif,dp->tdir_tag,(uint16)(dp->tdir_count),data);
-						if (data!=0)
-							_TIFFfree(data);
-						if (!m)
-							return(0);
-					}
-				}
-			}
-			break;
-		case TIFF_SETGET_C32_ASCII:
-			{
-				uint8* data;
-				assert(fip->field_readcount==TIFF_VARIABLE2);
-				assert(fip->field_passcount==1);
-				err=TIFFReadDirEntryByteArray(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					int m;
-					if( data != 0 && dp->tdir_count > 0 && data[dp->tdir_count-1] != '\0' )
-					{
-					    TIFFWarningExt(tif->tif_clientdata,module,"ASCII value for tag \"%s\" does not end in null byte. Forcing it to be null",fip->field_name);
-                                            data[dp->tdir_count-1] = '\0';
-					}
-					m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data);
-					if (data!=0)
-						_TIFFfree(data);
-					if (!m)
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_C32_UINT8:
-			{
-				uint8* data;
-				assert(fip->field_readcount==TIFF_VARIABLE2);
-				assert(fip->field_passcount==1);
-				err=TIFFReadDirEntryByteArray(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					int m;
-					m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data);
-					if (data!=0)
-						_TIFFfree(data);
-					if (!m)
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_C32_SINT8:
-			{
-				int8* data = NULL;
-				assert(fip->field_readcount==TIFF_VARIABLE2);
-				assert(fip->field_passcount==1);
-				err=TIFFReadDirEntrySbyteArray(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					int m;
-					m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data);
-					if (data!=0)
-						_TIFFfree(data);
-					if (!m)
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_C32_UINT16:
-			{
-				uint16* data;
-				assert(fip->field_readcount==TIFF_VARIABLE2);
-				assert(fip->field_passcount==1);
-				err=TIFFReadDirEntryShortArray(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					int m;
-					m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data);
-					if (data!=0)
-						_TIFFfree(data);
-					if (!m)
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_C32_SINT16:
-			{
-				int16* data = NULL;
-				assert(fip->field_readcount==TIFF_VARIABLE2);
-				assert(fip->field_passcount==1);
-				err=TIFFReadDirEntrySshortArray(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					int m;
-					m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data);
-					if (data!=0)
-						_TIFFfree(data);
-					if (!m)
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_C32_UINT32:
-			{
-				uint32* data;
-				assert(fip->field_readcount==TIFF_VARIABLE2);
-				assert(fip->field_passcount==1);
-				err=TIFFReadDirEntryLongArray(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					int m;
-					m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data);
-					if (data!=0)
-						_TIFFfree(data);
-					if (!m)
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_C32_SINT32:
-			{
-				int32* data = NULL;
-				assert(fip->field_readcount==TIFF_VARIABLE2);
-				assert(fip->field_passcount==1);
-				err=TIFFReadDirEntrySlongArray(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					int m;
-					m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data);
-					if (data!=0)
-						_TIFFfree(data);
-					if (!m)
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_C32_UINT64:
-			{
-				uint64* data;
-				assert(fip->field_readcount==TIFF_VARIABLE2);
-				assert(fip->field_passcount==1);
-				err=TIFFReadDirEntryLong8Array(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					int m;
-					m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data);
-					if (data!=0)
-						_TIFFfree(data);
-					if (!m)
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_C32_SINT64:
-			{
-				int64* data = NULL;
-				assert(fip->field_readcount==TIFF_VARIABLE2);
-				assert(fip->field_passcount==1);
-				err=TIFFReadDirEntrySlong8Array(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					int m;
-					m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data);
-					if (data!=0)
-						_TIFFfree(data);
-					if (!m)
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_C32_FLOAT:
-			{
-				float* data;
-				assert(fip->field_readcount==TIFF_VARIABLE2);
-				assert(fip->field_passcount==1);
-				err=TIFFReadDirEntryFloatArray(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					int m;
-					m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data);
-					if (data!=0)
-						_TIFFfree(data);
-					if (!m)
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_C32_DOUBLE:
-			{
-				double* data;
-				assert(fip->field_readcount==TIFF_VARIABLE2);
-				assert(fip->field_passcount==1);
-				err=TIFFReadDirEntryDoubleArray(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					int m;
-					m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data);
-					if (data!=0)
-						_TIFFfree(data);
-					if (!m)
-						return(0);
-				}
-			}
-			break;
-		case TIFF_SETGET_C32_IFD8:
-			{
-				uint64* data;
-				assert(fip->field_readcount==TIFF_VARIABLE2);
-				assert(fip->field_passcount==1);
-				err=TIFFReadDirEntryIfd8Array(tif,dp,&data);
-				if (err==TIFFReadDirEntryErrOk)
-				{
-					int m;
-					m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data);
-					if (data!=0)
-						_TIFFfree(data);
-					if (!m)
-						return(0);
-				}
-			}
-			break;
-		default:
-			assert(0);    /* we should never get here */
-			break;
-	}
-	if (err!=TIFFReadDirEntryErrOk)
-	{
-		TIFFReadDirEntryOutputErr(tif,err,module,fip->field_name,recover);
-		return(0);
-	}
-	return(1);
+        break;
+        case TIFF_SETGET_UINT8:
+        {
+            uint8_t data = 0;
+            assert(fip->field_readcount == 1);
+            assert(fip->field_passcount == 0);
+            err = TIFFReadDirEntryByte(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                if (!TIFFSetField(tif, dp->tdir_tag, data))
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_SINT8:
+        {
+            int8_t data = 0;
+            assert(fip->field_readcount == 1);
+            assert(fip->field_passcount == 0);
+            err = TIFFReadDirEntrySbyte(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                if (!TIFFSetField(tif, dp->tdir_tag, data))
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_UINT16:
+        {
+            uint16_t data;
+            assert(fip->field_readcount == 1);
+            assert(fip->field_passcount == 0);
+            err = TIFFReadDirEntryShort(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                if (!TIFFSetField(tif, dp->tdir_tag, data))
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_SINT16:
+        {
+            int16_t data;
+            assert(fip->field_readcount == 1);
+            assert(fip->field_passcount == 0);
+            err = TIFFReadDirEntrySshort(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                if (!TIFFSetField(tif, dp->tdir_tag, data))
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_UINT32:
+        {
+            uint32_t data;
+            assert(fip->field_readcount == 1);
+            assert(fip->field_passcount == 0);
+            err = TIFFReadDirEntryLong(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                if (!TIFFSetField(tif, dp->tdir_tag, data))
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_SINT32:
+        {
+            int32_t data;
+            assert(fip->field_readcount == 1);
+            assert(fip->field_passcount == 0);
+            err = TIFFReadDirEntrySlong(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                if (!TIFFSetField(tif, dp->tdir_tag, data))
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_UINT64:
+        {
+            uint64_t data;
+            assert(fip->field_readcount == 1);
+            assert(fip->field_passcount == 0);
+            err = TIFFReadDirEntryLong8(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                if (!TIFFSetField(tif, dp->tdir_tag, data))
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_SINT64:
+        {
+            int64_t data;
+            assert(fip->field_readcount == 1);
+            assert(fip->field_passcount == 0);
+            err = TIFFReadDirEntrySlong8(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                if (!TIFFSetField(tif, dp->tdir_tag, data))
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_FLOAT:
+        {
+            float data;
+            assert(fip->field_readcount == 1);
+            assert(fip->field_passcount == 0);
+            err = TIFFReadDirEntryFloat(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                if (!TIFFSetField(tif, dp->tdir_tag, data))
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_DOUBLE:
+        {
+            double data;
+            assert(fip->field_readcount == 1);
+            assert(fip->field_passcount == 0);
+            err = TIFFReadDirEntryDouble(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                if (!TIFFSetField(tif, dp->tdir_tag, data))
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_IFD8:
+        {
+            uint64_t data;
+            assert(fip->field_readcount == 1);
+            assert(fip->field_passcount == 0);
+            err = TIFFReadDirEntryIfd8(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                if (!TIFFSetField(tif, dp->tdir_tag, data))
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_UINT16_PAIR:
+        {
+            uint16_t *data;
+            assert(fip->field_readcount == 2);
+            assert(fip->field_passcount == 0);
+            if (dp->tdir_count != 2)
+            {
+                TIFFWarningExtR(tif, module,
+                                "incorrect count for field \"%s\", expected 2, "
+                                "got %" PRIu64,
+                                fip->field_name, dp->tdir_count);
+                return (0);
+            }
+            err = TIFFReadDirEntryShortArray(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                int m;
+                assert(data); /* avoid CLang static Analyzer false positive */
+                m = TIFFSetField(tif, dp->tdir_tag, data[0], data[1]);
+                _TIFFfreeExt(tif, data);
+                if (!m)
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_C0_UINT8:
+        {
+            uint8_t *data;
+            assert(fip->field_readcount >= 1);
+            assert(fip->field_passcount == 0);
+            if (dp->tdir_count != (uint64_t)fip->field_readcount)
+            {
+                TIFFWarningExtR(tif, module,
+                                "incorrect count for field \"%s\", expected "
+                                "%d, got %" PRIu64,
+                                fip->field_name, (int)fip->field_readcount,
+                                dp->tdir_count);
+                return (0);
+            }
+            else
+            {
+                err = TIFFReadDirEntryByteArray(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag, data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C0_SINT8:
+        {
+            int8_t *data;
+            assert(fip->field_readcount >= 1);
+            assert(fip->field_passcount == 0);
+            if (dp->tdir_count != (uint64_t)fip->field_readcount)
+            {
+                TIFFWarningExtR(tif, module,
+                                "incorrect count for field \"%s\", expected "
+                                "%d, got %" PRIu64,
+                                fip->field_name, (int)fip->field_readcount,
+                                dp->tdir_count);
+                return (0);
+            }
+            else
+            {
+                err = TIFFReadDirEntrySbyteArray(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag, data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C0_UINT16:
+        {
+            uint16_t *data;
+            assert(fip->field_readcount >= 1);
+            assert(fip->field_passcount == 0);
+            if (dp->tdir_count != (uint64_t)fip->field_readcount)
+            {
+                TIFFWarningExtR(tif, module,
+                                "incorrect count for field \"%s\", expected "
+                                "%d, got %" PRIu64,
+                                fip->field_name, (int)fip->field_readcount,
+                                dp->tdir_count);
+                return (0);
+            }
+            else
+            {
+                err = TIFFReadDirEntryShortArray(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag, data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C0_SINT16:
+        {
+            int16_t *data;
+            assert(fip->field_readcount >= 1);
+            assert(fip->field_passcount == 0);
+            if (dp->tdir_count != (uint64_t)fip->field_readcount)
+            {
+                TIFFWarningExtR(tif, module,
+                                "incorrect count for field \"%s\", expected "
+                                "%d, got %" PRIu64,
+                                fip->field_name, (int)fip->field_readcount,
+                                dp->tdir_count);
+                return (0);
+            }
+            else
+            {
+                err = TIFFReadDirEntrySshortArray(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag, data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C0_UINT32:
+        {
+            uint32_t *data;
+            assert(fip->field_readcount >= 1);
+            assert(fip->field_passcount == 0);
+            if (dp->tdir_count != (uint64_t)fip->field_readcount)
+            {
+                TIFFWarningExtR(tif, module,
+                                "incorrect count for field \"%s\", expected "
+                                "%d, got %" PRIu64,
+                                fip->field_name, (int)fip->field_readcount,
+                                dp->tdir_count);
+                return (0);
+            }
+            else
+            {
+                err = TIFFReadDirEntryLongArray(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag, data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C0_SINT32:
+        {
+            int32_t *data;
+            assert(fip->field_readcount >= 1);
+            assert(fip->field_passcount == 0);
+            if (dp->tdir_count != (uint64_t)fip->field_readcount)
+            {
+                TIFFWarningExtR(tif, module,
+                                "incorrect count for field \"%s\", expected "
+                                "%d, got %" PRIu64,
+                                fip->field_name, (int)fip->field_readcount,
+                                dp->tdir_count);
+                return (0);
+            }
+            else
+            {
+                err = TIFFReadDirEntrySlongArray(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag, data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C0_UINT64:
+        {
+            uint64_t *data;
+            assert(fip->field_readcount >= 1);
+            assert(fip->field_passcount == 0);
+            if (dp->tdir_count != (uint64_t)fip->field_readcount)
+            {
+                TIFFWarningExtR(tif, module,
+                                "incorrect count for field \"%s\", expected "
+                                "%d, got %" PRIu64,
+                                fip->field_name, (int)fip->field_readcount,
+                                dp->tdir_count);
+                return (0);
+            }
+            else
+            {
+                err = TIFFReadDirEntryLong8Array(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag, data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C0_SINT64:
+        {
+            int64_t *data;
+            assert(fip->field_readcount >= 1);
+            assert(fip->field_passcount == 0);
+            if (dp->tdir_count != (uint64_t)fip->field_readcount)
+            {
+                TIFFWarningExtR(tif, module,
+                                "incorrect count for field \"%s\", expected "
+                                "%d, got %" PRIu64,
+                                fip->field_name, (int)fip->field_readcount,
+                                dp->tdir_count);
+                return (0);
+            }
+            else
+            {
+                err = TIFFReadDirEntrySlong8Array(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag, data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C0_FLOAT:
+        {
+            float *data;
+            assert(fip->field_readcount >= 1);
+            assert(fip->field_passcount == 0);
+            if (dp->tdir_count != (uint64_t)fip->field_readcount)
+            {
+                TIFFWarningExtR(tif, module,
+                                "incorrect count for field \"%s\", expected "
+                                "%d, got %" PRIu64,
+                                fip->field_name, (int)fip->field_readcount,
+                                dp->tdir_count);
+                return (0);
+            }
+            else
+            {
+                err = TIFFReadDirEntryFloatArray(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag, data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        /*--: Rational2Double: Extend for Double Arrays and Rational-Arrays read
+         * into Double-Arrays. */
+        case TIFF_SETGET_C0_DOUBLE:
+        {
+            double *data;
+            assert(fip->field_readcount >= 1);
+            assert(fip->field_passcount == 0);
+            if (dp->tdir_count != (uint64_t)fip->field_readcount)
+            {
+                TIFFWarningExtR(tif, module,
+                                "incorrect count for field \"%s\", expected "
+                                "%d, got %" PRIu64,
+                                fip->field_name, (int)fip->field_readcount,
+                                dp->tdir_count);
+                return (0);
+            }
+            else
+            {
+                err = TIFFReadDirEntryDoubleArray(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag, data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C16_ASCII:
+        {
+            uint8_t *data;
+            assert(fip->field_readcount == TIFF_VARIABLE);
+            assert(fip->field_passcount == 1);
+            if (dp->tdir_count > 0xFFFF)
+                err = TIFFReadDirEntryErrCount;
+            else
+            {
+                err = TIFFReadDirEntryByteArray(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    if (data != 0 && dp->tdir_count > 0 &&
+                        data[dp->tdir_count - 1] != '\0')
+                    {
+                        TIFFWarningExtR(
+                            tif, module,
+                            "ASCII value for tag \"%s\" does not end in null "
+                            "byte. Forcing it to be null",
+                            fip->field_name);
+                        data[dp->tdir_count - 1] = '\0';
+                    }
+                    m = TIFFSetField(tif, dp->tdir_tag,
+                                     (uint16_t)(dp->tdir_count), data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C16_UINT8:
+        {
+            uint8_t *data;
+            assert(fip->field_readcount == TIFF_VARIABLE);
+            assert(fip->field_passcount == 1);
+            if (dp->tdir_count > 0xFFFF)
+                err = TIFFReadDirEntryErrCount;
+            else
+            {
+                err = TIFFReadDirEntryByteArray(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag,
+                                     (uint16_t)(dp->tdir_count), data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C16_SINT8:
+        {
+            int8_t *data;
+            assert(fip->field_readcount == TIFF_VARIABLE);
+            assert(fip->field_passcount == 1);
+            if (dp->tdir_count > 0xFFFF)
+                err = TIFFReadDirEntryErrCount;
+            else
+            {
+                err = TIFFReadDirEntrySbyteArray(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag,
+                                     (uint16_t)(dp->tdir_count), data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C16_UINT16:
+        {
+            uint16_t *data;
+            assert(fip->field_readcount == TIFF_VARIABLE);
+            assert(fip->field_passcount == 1);
+            if (dp->tdir_count > 0xFFFF)
+                err = TIFFReadDirEntryErrCount;
+            else
+            {
+                err = TIFFReadDirEntryShortArray(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag,
+                                     (uint16_t)(dp->tdir_count), data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C16_SINT16:
+        {
+            int16_t *data;
+            assert(fip->field_readcount == TIFF_VARIABLE);
+            assert(fip->field_passcount == 1);
+            if (dp->tdir_count > 0xFFFF)
+                err = TIFFReadDirEntryErrCount;
+            else
+            {
+                err = TIFFReadDirEntrySshortArray(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag,
+                                     (uint16_t)(dp->tdir_count), data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C16_UINT32:
+        {
+            uint32_t *data;
+            assert(fip->field_readcount == TIFF_VARIABLE);
+            assert(fip->field_passcount == 1);
+            if (dp->tdir_count > 0xFFFF)
+                err = TIFFReadDirEntryErrCount;
+            else
+            {
+                err = TIFFReadDirEntryLongArray(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag,
+                                     (uint16_t)(dp->tdir_count), data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C16_SINT32:
+        {
+            int32_t *data;
+            assert(fip->field_readcount == TIFF_VARIABLE);
+            assert(fip->field_passcount == 1);
+            if (dp->tdir_count > 0xFFFF)
+                err = TIFFReadDirEntryErrCount;
+            else
+            {
+                err = TIFFReadDirEntrySlongArray(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag,
+                                     (uint16_t)(dp->tdir_count), data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C16_UINT64:
+        {
+            uint64_t *data;
+            assert(fip->field_readcount == TIFF_VARIABLE);
+            assert(fip->field_passcount == 1);
+            if (dp->tdir_count > 0xFFFF)
+                err = TIFFReadDirEntryErrCount;
+            else
+            {
+                err = TIFFReadDirEntryLong8Array(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag,
+                                     (uint16_t)(dp->tdir_count), data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C16_SINT64:
+        {
+            int64_t *data;
+            assert(fip->field_readcount == TIFF_VARIABLE);
+            assert(fip->field_passcount == 1);
+            if (dp->tdir_count > 0xFFFF)
+                err = TIFFReadDirEntryErrCount;
+            else
+            {
+                err = TIFFReadDirEntrySlong8Array(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag,
+                                     (uint16_t)(dp->tdir_count), data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C16_FLOAT:
+        {
+            float *data;
+            assert(fip->field_readcount == TIFF_VARIABLE);
+            assert(fip->field_passcount == 1);
+            if (dp->tdir_count > 0xFFFF)
+                err = TIFFReadDirEntryErrCount;
+            else
+            {
+                err = TIFFReadDirEntryFloatArray(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag,
+                                     (uint16_t)(dp->tdir_count), data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C16_DOUBLE:
+        {
+            double *data;
+            assert(fip->field_readcount == TIFF_VARIABLE);
+            assert(fip->field_passcount == 1);
+            if (dp->tdir_count > 0xFFFF)
+                err = TIFFReadDirEntryErrCount;
+            else
+            {
+                err = TIFFReadDirEntryDoubleArray(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag,
+                                     (uint16_t)(dp->tdir_count), data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C16_IFD8:
+        {
+            uint64_t *data;
+            assert(fip->field_readcount == TIFF_VARIABLE);
+            assert(fip->field_passcount == 1);
+            if (dp->tdir_count > 0xFFFF)
+                err = TIFFReadDirEntryErrCount;
+            else
+            {
+                err = TIFFReadDirEntryIfd8Array(tif, dp, &data);
+                if (err == TIFFReadDirEntryErrOk)
+                {
+                    int m;
+                    m = TIFFSetField(tif, dp->tdir_tag,
+                                     (uint16_t)(dp->tdir_count), data);
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    if (!m)
+                        return (0);
+                }
+            }
+        }
+        break;
+        case TIFF_SETGET_C32_ASCII:
+        {
+            uint8_t *data;
+            assert(fip->field_readcount == TIFF_VARIABLE2);
+            assert(fip->field_passcount == 1);
+            err = TIFFReadDirEntryByteArray(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                int m;
+                if (data != 0 && dp->tdir_count > 0 &&
+                    data[dp->tdir_count - 1] != '\0')
+                {
+                    TIFFWarningExtR(tif, module,
+                                    "ASCII value for tag \"%s\" does not end "
+                                    "in null byte. Forcing it to be null",
+                                    fip->field_name);
+                    data[dp->tdir_count - 1] = '\0';
+                }
+                m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
+                                 data);
+                if (data != 0)
+                    _TIFFfreeExt(tif, data);
+                if (!m)
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_C32_UINT8:
+        {
+            uint8_t *data;
+            uint32_t count = 0;
+            assert(fip->field_readcount == TIFF_VARIABLE2);
+            assert(fip->field_passcount == 1);
+            if (fip->field_tag == TIFFTAG_RICHTIFFIPTC &&
+                dp->tdir_type == TIFF_LONG)
+            {
+                /* Adobe's software (wrongly) writes RichTIFFIPTC tag with
+                 * data type LONG instead of UNDEFINED. Work around this
+                 * frequently found issue */
+                void *origdata;
+                err = TIFFReadDirEntryArray(tif, dp, &count, 4, &origdata);
+                if ((err != TIFFReadDirEntryErrOk) || (origdata == 0))
+                {
+                    data = NULL;
+                }
+                else
+                {
+                    if (tif->tif_flags & TIFF_SWAB)
+                        TIFFSwabArrayOfLong((uint32_t *)origdata, count);
+                    data = (uint8_t *)origdata;
+                    count = (uint32_t)(count * 4);
+                }
+            }
+            else
+            {
+                err = TIFFReadDirEntryByteArray(tif, dp, &data);
+                count = (uint32_t)(dp->tdir_count);
+            }
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                int m;
+                m = TIFFSetField(tif, dp->tdir_tag, count, data);
+                if (data != 0)
+                    _TIFFfreeExt(tif, data);
+                if (!m)
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_C32_SINT8:
+        {
+            int8_t *data = NULL;
+            assert(fip->field_readcount == TIFF_VARIABLE2);
+            assert(fip->field_passcount == 1);
+            err = TIFFReadDirEntrySbyteArray(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                int m;
+                m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
+                                 data);
+                if (data != 0)
+                    _TIFFfreeExt(tif, data);
+                if (!m)
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_C32_UINT16:
+        {
+            uint16_t *data;
+            assert(fip->field_readcount == TIFF_VARIABLE2);
+            assert(fip->field_passcount == 1);
+            err = TIFFReadDirEntryShortArray(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                int m;
+                m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
+                                 data);
+                if (data != 0)
+                    _TIFFfreeExt(tif, data);
+                if (!m)
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_C32_SINT16:
+        {
+            int16_t *data = NULL;
+            assert(fip->field_readcount == TIFF_VARIABLE2);
+            assert(fip->field_passcount == 1);
+            err = TIFFReadDirEntrySshortArray(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                int m;
+                m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
+                                 data);
+                if (data != 0)
+                    _TIFFfreeExt(tif, data);
+                if (!m)
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_C32_UINT32:
+        {
+            uint32_t *data;
+            assert(fip->field_readcount == TIFF_VARIABLE2);
+            assert(fip->field_passcount == 1);
+            err = TIFFReadDirEntryLongArray(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                int m;
+                m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
+                                 data);
+                if (data != 0)
+                    _TIFFfreeExt(tif, data);
+                if (!m)
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_C32_SINT32:
+        {
+            int32_t *data = NULL;
+            assert(fip->field_readcount == TIFF_VARIABLE2);
+            assert(fip->field_passcount == 1);
+            err = TIFFReadDirEntrySlongArray(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                int m;
+                m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
+                                 data);
+                if (data != 0)
+                    _TIFFfreeExt(tif, data);
+                if (!m)
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_C32_UINT64:
+        {
+            uint64_t *data;
+            assert(fip->field_readcount == TIFF_VARIABLE2);
+            assert(fip->field_passcount == 1);
+            err = TIFFReadDirEntryLong8Array(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                int m;
+                m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
+                                 data);
+                if (data != 0)
+                    _TIFFfreeExt(tif, data);
+                if (!m)
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_C32_SINT64:
+        {
+            int64_t *data = NULL;
+            assert(fip->field_readcount == TIFF_VARIABLE2);
+            assert(fip->field_passcount == 1);
+            err = TIFFReadDirEntrySlong8Array(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                int m;
+                m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
+                                 data);
+                if (data != 0)
+                    _TIFFfreeExt(tif, data);
+                if (!m)
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_C32_FLOAT:
+        {
+            float *data;
+            assert(fip->field_readcount == TIFF_VARIABLE2);
+            assert(fip->field_passcount == 1);
+            err = TIFFReadDirEntryFloatArray(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                int m;
+                m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
+                                 data);
+                if (data != 0)
+                    _TIFFfreeExt(tif, data);
+                if (!m)
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_C32_DOUBLE:
+        {
+            double *data;
+            assert(fip->field_readcount == TIFF_VARIABLE2);
+            assert(fip->field_passcount == 1);
+            err = TIFFReadDirEntryDoubleArray(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                int m;
+                m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
+                                 data);
+                if (data != 0)
+                    _TIFFfreeExt(tif, data);
+                if (!m)
+                    return (0);
+            }
+        }
+        break;
+        case TIFF_SETGET_C32_IFD8:
+        {
+            uint64_t *data;
+            assert(fip->field_readcount == TIFF_VARIABLE2);
+            assert(fip->field_passcount == 1);
+            err = TIFFReadDirEntryIfd8Array(tif, dp, &data);
+            if (err == TIFFReadDirEntryErrOk)
+            {
+                int m;
+                m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
+                                 data);
+                if (data != 0)
+                    _TIFFfreeExt(tif, data);
+                if (!m)
+                    return (0);
+            }
+        }
+        break;
+        default:
+            assert(0); /* we should never get here */
+            break;
+    }
+    if (err != TIFFReadDirEntryErrOk)
+    {
+        TIFFReadDirEntryOutputErr(tif, err, module, fip->field_name, recover);
+        return (0);
+    }
+    return (1);
 }
 
 /*
  * Fetch a set of offsets or lengths.
  * While this routine says "strips", in fact it's also used for tiles.
  */
-static int
-TIFFFetchStripThing(TIFF* tif, TIFFDirEntry* dir, uint32 nstrips, uint64** lpp)
+static int TIFFFetchStripThing(TIFF *tif, TIFFDirEntry *dir, uint32_t nstrips,
+                               uint64_t **lpp)
 {
-	static const char module[] = "TIFFFetchStripThing";
-	enum TIFFReadDirEntryErr err;
-	uint64* data;
-	err=TIFFReadDirEntryLong8ArrayWithLimit(tif,dir,&data,nstrips);
-	if (err!=TIFFReadDirEntryErrOk)
-	{
-		const TIFFField* fip = TIFFFieldWithTag(tif,dir->tdir_tag); 
-		TIFFReadDirEntryOutputErr(tif,err,module,fip ? fip->field_name : "unknown tagname",0);
-		return(0);
-	}
-	if (dir->tdir_count<(uint64)nstrips)
-	{
-		uint64* resizeddata;
-		const TIFFField* fip = TIFFFieldWithTag(tif,dir->tdir_tag);
-		const char* pszMax = getenv("LIBTIFF_STRILE_ARRAY_MAX_RESIZE_COUNT");
-		uint32 max_nstrips = 1000000;
-		if( pszMax )
-			max_nstrips = (uint32) atoi(pszMax);
-		TIFFReadDirEntryOutputErr(tif,TIFFReadDirEntryErrCount,
-		            module,
-		            fip ? fip->field_name : "unknown tagname",
-		            ( nstrips <= max_nstrips ) );
+    static const char module[] = "TIFFFetchStripThing";
+    enum TIFFReadDirEntryErr err;
+    uint64_t *data;
+    err = TIFFReadDirEntryLong8ArrayWithLimit(tif, dir, &data, nstrips);
+    if (err != TIFFReadDirEntryErrOk)
+    {
+        const TIFFField *fip = TIFFFieldWithTag(tif, dir->tdir_tag);
+        TIFFReadDirEntryOutputErr(tif, err, module,
+                                  fip ? fip->field_name : "unknown tagname", 0);
+        return (0);
+    }
+    if (dir->tdir_count < (uint64_t)nstrips)
+    {
+        uint64_t *resizeddata;
+        const TIFFField *fip = TIFFFieldWithTag(tif, dir->tdir_tag);
+        const char *pszMax = getenv("LIBTIFF_STRILE_ARRAY_MAX_RESIZE_COUNT");
+        uint32_t max_nstrips = 1000000;
+        if (pszMax)
+            max_nstrips = (uint32_t)atoi(pszMax);
+        TIFFReadDirEntryOutputErr(tif, TIFFReadDirEntryErrCount, module,
+                                  fip ? fip->field_name : "unknown tagname",
+                                  (nstrips <= max_nstrips));
 
-		if( nstrips > max_nstrips )
-		{
-			_TIFFfree(data);
-			return(0);
-		}
+        if (nstrips > max_nstrips)
+        {
+            _TIFFfreeExt(tif, data);
+            return (0);
+        }
 
-		resizeddata=(uint64*)_TIFFCheckMalloc(tif,nstrips,sizeof(uint64),"for strip array");
-		if (resizeddata==0) {
-			_TIFFfree(data);
-			return(0);
-		}
-                _TIFFmemcpy(resizeddata,data,(uint32)dir->tdir_count*sizeof(uint64));
-                _TIFFmemset(resizeddata+(uint32)dir->tdir_count,0,(nstrips-(uint32)dir->tdir_count)*sizeof(uint64));
-		_TIFFfree(data);
-		data=resizeddata;
-	}
-	*lpp=data;
-	return(1);
+        resizeddata = (uint64_t *)_TIFFCheckMalloc(
+            tif, nstrips, sizeof(uint64_t), "for strip array");
+        if (resizeddata == 0)
+        {
+            _TIFFfreeExt(tif, data);
+            return (0);
+        }
+        if (dir->tdir_count)
+            _TIFFmemcpy(resizeddata, data,
+                        (uint32_t)dir->tdir_count * sizeof(uint64_t));
+        _TIFFmemset(resizeddata + (uint32_t)dir->tdir_count, 0,
+                    (nstrips - (uint32_t)dir->tdir_count) * sizeof(uint64_t));
+        _TIFFfreeExt(tif, data);
+        data = resizeddata;
+    }
+    *lpp = data;
+    return (1);
 }
 
 /*
  * Fetch and set the SubjectDistance EXIF tag.
  */
-static int
-TIFFFetchSubjectDistance(TIFF* tif, TIFFDirEntry* dir)
+static int TIFFFetchSubjectDistance(TIFF *tif, TIFFDirEntry *dir)
 {
-	static const char module[] = "TIFFFetchSubjectDistance";
-	enum TIFFReadDirEntryErr err;
-	UInt64Aligned_t m;
-    m.l=0;
-	assert(sizeof(double)==8);
-	assert(sizeof(uint64)==8);
-	assert(sizeof(uint32)==4);
-	if (dir->tdir_count!=1)
-		err=TIFFReadDirEntryErrCount;
-	else if (dir->tdir_type!=TIFF_RATIONAL)
-		err=TIFFReadDirEntryErrType;
-	else
-	{
-		if (!(tif->tif_flags&TIFF_BIGTIFF))
-		{
-			uint32 offset;
-			offset=*(uint32*)(&dir->tdir_offset);
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabLong(&offset);
-			err=TIFFReadDirEntryData(tif,offset,8,m.i);
-		}
-		else
-		{
-			m.l=dir->tdir_offset.toff_long8;
-			err=TIFFReadDirEntryErrOk;
-		}
-	}
-	if (err==TIFFReadDirEntryErrOk)
-	{
-		double n;
-		if (tif->tif_flags&TIFF_SWAB)
-			TIFFSwabArrayOfLong(m.i,2);
-		if (m.i[0]==0)
-			n=0.0;
-		else if (m.i[0]==0xFFFFFFFF || m.i[1]==0)
-			/*
-			 * XXX: Numerator 0xFFFFFFFF means that we have infinite
-			 * distance. Indicate that with a negative floating point
-			 * SubjectDistance value.
-			 */
-			n=-1.0;
-		else
-			n=(double)m.i[0]/(double)m.i[1];
-		return(TIFFSetField(tif,dir->tdir_tag,n));
-	}
-	else
-	{
-		TIFFReadDirEntryOutputErr(tif,err,module,"SubjectDistance",TRUE);
-		return(0);
-	}
+    static const char module[] = "TIFFFetchSubjectDistance";
+    enum TIFFReadDirEntryErr err;
+    UInt64Aligned_t m;
+    m.l = 0;
+    assert(sizeof(double) == 8);
+    assert(sizeof(uint64_t) == 8);
+    assert(sizeof(uint32_t) == 4);
+    if (dir->tdir_count != 1)
+        err = TIFFReadDirEntryErrCount;
+    else if (dir->tdir_type != TIFF_RATIONAL)
+        err = TIFFReadDirEntryErrType;
+    else
+    {
+        if (!(tif->tif_flags & TIFF_BIGTIFF))
+        {
+            uint32_t offset;
+            offset = *(uint32_t *)(&dir->tdir_offset);
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong(&offset);
+            err = TIFFReadDirEntryData(tif, offset, 8, m.i);
+        }
+        else
+        {
+            m.l = dir->tdir_offset.toff_long8;
+            err = TIFFReadDirEntryErrOk;
+        }
+    }
+    if (err == TIFFReadDirEntryErrOk)
+    {
+        double n;
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabArrayOfLong(m.i, 2);
+        if (m.i[0] == 0)
+            n = 0.0;
+        else if (m.i[0] == 0xFFFFFFFF || m.i[1] == 0)
+            /*
+             * XXX: Numerator 0xFFFFFFFF means that we have infinite
+             * distance. Indicate that with a negative floating point
+             * SubjectDistance value.
+             */
+            n = -1.0;
+        else
+            n = (double)m.i[0] / (double)m.i[1];
+        return (TIFFSetField(tif, dir->tdir_tag, n));
+    }
+    else
+    {
+        TIFFReadDirEntryOutputErr(tif, err, module, "SubjectDistance", TRUE);
+        return (0);
+    }
 }
 
-static void allocChoppedUpStripArrays(TIFF* tif, uint32 nstrips,
-                                      uint64 stripbytes, uint32 rowsperstrip)
+static void allocChoppedUpStripArrays(TIFF *tif, uint32_t nstrips,
+                                      uint64_t stripbytes,
+                                      uint32_t rowsperstrip)
 {
     TIFFDirectory *td = &tif->tif_dir;
-    uint64 bytecount;
-    uint64 offset;
-    uint64 last_offset;
-    uint64 last_bytecount;
-    uint32 i;
-    uint64 *newcounts;
-    uint64 *newoffsets;
+    uint64_t bytecount;
+    uint64_t offset;
+    uint64_t last_offset;
+    uint64_t last_bytecount;
+    uint32_t i;
+    uint64_t *newcounts;
+    uint64_t *newoffsets;
 
     offset = TIFFGetStrileOffset(tif, 0);
-    last_offset = TIFFGetStrileOffset(tif, td->td_nstrips-1);
-    last_bytecount = TIFFGetStrileByteCount(tif, td->td_nstrips-1);
-    if( last_offset > TIFF_UINT64_MAX - last_bytecount ||
-        last_offset + last_bytecount < offset )
+    last_offset = TIFFGetStrileOffset(tif, td->td_nstrips - 1);
+    last_bytecount = TIFFGetStrileByteCount(tif, td->td_nstrips - 1);
+    if (last_offset > UINT64_MAX - last_bytecount ||
+        last_offset + last_bytecount < offset)
     {
         return;
     }
     bytecount = last_offset + last_bytecount - offset;
 
-    newcounts = (uint64*) _TIFFCheckMalloc(tif, nstrips, sizeof (uint64),
-                            "for chopped \"StripByteCounts\" array");
-    newoffsets = (uint64*) _TIFFCheckMalloc(tif, nstrips, sizeof (uint64),
-                            "for chopped \"StripOffsets\" array");
-    if (newcounts == NULL || newoffsets == NULL) {
+    newcounts =
+        (uint64_t *)_TIFFCheckMalloc(tif, nstrips, sizeof(uint64_t),
+                                     "for chopped \"StripByteCounts\" array");
+    newoffsets = (uint64_t *)_TIFFCheckMalloc(
+        tif, nstrips, sizeof(uint64_t), "for chopped \"StripOffsets\" array");
+    if (newcounts == NULL || newoffsets == NULL)
+    {
         /*
-        * Unable to allocate new strip information, give up and use
-        * the original one strip information.
-        */
+         * Unable to allocate new strip information, give up and use
+         * the original one strip information.
+         */
         if (newcounts != NULL)
-            _TIFFfree(newcounts);
+            _TIFFfreeExt(tif, newcounts);
         if (newoffsets != NULL)
-            _TIFFfree(newoffsets);
+            _TIFFfreeExt(tif, newoffsets);
         return;
     }
 
@@ -5815,8 +7309,8 @@
     td->td_stripsperimage = td->td_nstrips = nstrips;
     TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
 
-    _TIFFfree(td->td_stripbytecount_p);
-    _TIFFfree(td->td_stripoffset_p);
+    _TIFFfreeExt(tif, td->td_stripbytecount_p);
+    _TIFFfreeExt(tif, td->td_stripoffset_p);
     td->td_stripbytecount_p = newcounts;
     td->td_stripoffset_p = newoffsets;
 #ifdef STRIPBYTECOUNTSORTED_UNUSED
@@ -5825,140 +7319,140 @@
     tif->tif_flags |= TIFF_CHOPPEDUPARRAYS;
 }
 
-
 /*
  * Replace a single strip (tile) of uncompressed data by multiple strips
  * (tiles), each approximately STRIP_SIZE_DEFAULT bytes. This is useful for
  * dealing with large images or for dealing with machines with a limited
  * amount memory.
  */
-static void
-ChopUpSingleUncompressedStrip(TIFF* tif)
+static void ChopUpSingleUncompressedStrip(TIFF *tif)
 {
-	register TIFFDirectory *td = &tif->tif_dir;
-	uint64 bytecount;
-	uint64 offset;
-	uint32 rowblock;
-	uint64 rowblockbytes;
-	uint64 stripbytes;
-	uint32 nstrips;
-	uint32 rowsperstrip;
+    register TIFFDirectory *td = &tif->tif_dir;
+    uint64_t bytecount;
+    uint64_t offset;
+    uint32_t rowblock;
+    uint64_t rowblockbytes;
+    uint64_t stripbytes;
+    uint32_t nstrips;
+    uint32_t rowsperstrip;
 
-	bytecount = TIFFGetStrileByteCount(tif, 0);
-        /* On a newly created file, just re-opened to be filled, we */
-        /* don't want strip chop to trigger as it is going to cause issues */
-        /* later ( StripOffsets and StripByteCounts improperly filled) . */
-        if( bytecount == 0 && tif->tif_mode != O_RDONLY )
-            return;
-	offset = TIFFGetStrileByteCount(tif, 0);
-	assert(td->td_planarconfig == PLANARCONFIG_CONTIG);
-	if ((td->td_photometric == PHOTOMETRIC_YCBCR)&&
-	    (!isUpSampled(tif)))
-		rowblock = td->td_ycbcrsubsampling[1];
-	else
-		rowblock = 1;
-	rowblockbytes = TIFFVTileSize64(tif, rowblock);
-	/*
-	 * Make the rows hold at least one scanline, but fill specified amount
-	 * of data if possible.
-	 */
-	if (rowblockbytes > STRIP_SIZE_DEFAULT) {
-		stripbytes = rowblockbytes;
-		rowsperstrip = rowblock;
-	} else if (rowblockbytes > 0 ) {
-		uint32 rowblocksperstrip;
-		rowblocksperstrip = (uint32) (STRIP_SIZE_DEFAULT / rowblockbytes);
-		rowsperstrip = rowblocksperstrip * rowblock;
-		stripbytes = rowblocksperstrip * rowblockbytes;
-	}
-	else
-	    return;
+    bytecount = TIFFGetStrileByteCount(tif, 0);
+    /* On a newly created file, just re-opened to be filled, we */
+    /* don't want strip chop to trigger as it is going to cause issues */
+    /* later ( StripOffsets and StripByteCounts improperly filled) . */
+    if (bytecount == 0 && tif->tif_mode != O_RDONLY)
+        return;
+    offset = TIFFGetStrileByteCount(tif, 0);
+    assert(td->td_planarconfig == PLANARCONFIG_CONTIG);
+    if ((td->td_photometric == PHOTOMETRIC_YCBCR) && (!isUpSampled(tif)))
+        rowblock = td->td_ycbcrsubsampling[1];
+    else
+        rowblock = 1;
+    rowblockbytes = TIFFVTileSize64(tif, rowblock);
+    /*
+     * Make the rows hold at least one scanline, but fill specified amount
+     * of data if possible.
+     */
+    if (rowblockbytes > STRIP_SIZE_DEFAULT)
+    {
+        stripbytes = rowblockbytes;
+        rowsperstrip = rowblock;
+    }
+    else if (rowblockbytes > 0)
+    {
+        uint32_t rowblocksperstrip;
+        rowblocksperstrip = (uint32_t)(STRIP_SIZE_DEFAULT / rowblockbytes);
+        rowsperstrip = rowblocksperstrip * rowblock;
+        stripbytes = rowblocksperstrip * rowblockbytes;
+    }
+    else
+        return;
 
-	/*
-	 * never increase the number of rows per strip
-	 */
-	if (rowsperstrip >= td->td_rowsperstrip)
-		return;
-        nstrips = TIFFhowmany_32(td->td_imagelength, rowsperstrip);
-        if( nstrips == 0 )
-            return;
+    /*
+     * never increase the number of rows per strip
+     */
+    if (rowsperstrip >= td->td_rowsperstrip)
+        return;
+    nstrips = TIFFhowmany_32(td->td_imagelength, rowsperstrip);
+    if (nstrips == 0)
+        return;
 
-        /* If we are going to allocate a lot of memory, make sure that the */
-        /* file is as big as needed */
-        if( tif->tif_mode == O_RDONLY &&
-            nstrips > 1000000 &&
-            (offset >= TIFFGetFileSize(tif) ||
-             stripbytes > (TIFFGetFileSize(tif) - offset) / (nstrips - 1)) )
-        {
-            return;
-        }
+    /* If we are going to allocate a lot of memory, make sure that the */
+    /* file is as big as needed */
+    if (tif->tif_mode == O_RDONLY && nstrips > 1000000 &&
+        (offset >= TIFFGetFileSize(tif) ||
+         stripbytes > (TIFFGetFileSize(tif) - offset) / (nstrips - 1)))
+    {
+        return;
+    }
 
-        allocChoppedUpStripArrays(tif, nstrips, stripbytes, rowsperstrip);
+    allocChoppedUpStripArrays(tif, nstrips, stripbytes, rowsperstrip);
 }
 
-
 /*
  * Replace a file with contiguous strips > 2 GB of uncompressed data by
  * multiple smaller strips. This is useful for
  * dealing with large images or for dealing with machines with a limited
  * amount memory.
  */
-static void TryChopUpUncompressedBigTiff( TIFF* tif )
+static void TryChopUpUncompressedBigTiff(TIFF *tif)
 {
     TIFFDirectory *td = &tif->tif_dir;
-    uint32 rowblock;
-    uint64 rowblockbytes;
-    uint32 i;
-    uint64 stripsize;
-    uint32 rowblocksperstrip;
-    uint32 rowsperstrip;
-    uint64 stripbytes;
-    uint32 nstrips;
+    uint32_t rowblock;
+    uint64_t rowblockbytes;
+    uint32_t i;
+    uint64_t stripsize;
+    uint32_t rowblocksperstrip;
+    uint32_t rowsperstrip;
+    uint64_t stripbytes;
+    uint32_t nstrips;
 
     stripsize = TIFFStripSize64(tif);
 
-    assert( tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG );
-    assert( tif->tif_dir.td_compression == COMPRESSION_NONE );
-    assert( (tif->tif_flags&(TIFF_STRIPCHOP|TIFF_ISTILED)) == TIFF_STRIPCHOP );
-    assert( stripsize > 0x7FFFFFFFUL );
+    assert(tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG);
+    assert(tif->tif_dir.td_compression == COMPRESSION_NONE);
+    assert((tif->tif_flags & (TIFF_STRIPCHOP | TIFF_ISTILED)) ==
+           TIFF_STRIPCHOP);
+    assert(stripsize > 0x7FFFFFFFUL);
 
     /* On a newly created file, just re-opened to be filled, we */
     /* don't want strip chop to trigger as it is going to cause issues */
     /* later ( StripOffsets and StripByteCounts improperly filled) . */
-    if( TIFFGetStrileByteCount(tif, 0) == 0 && tif->tif_mode != O_RDONLY )
+    if (TIFFGetStrileByteCount(tif, 0) == 0 && tif->tif_mode != O_RDONLY)
         return;
 
-    if ((td->td_photometric == PHOTOMETRIC_YCBCR)&&
-        (!isUpSampled(tif)))
+    if ((td->td_photometric == PHOTOMETRIC_YCBCR) && (!isUpSampled(tif)))
         rowblock = td->td_ycbcrsubsampling[1];
     else
         rowblock = 1;
     rowblockbytes = TIFFVStripSize64(tif, rowblock);
-    if( rowblockbytes == 0 || rowblockbytes > 0x7FFFFFFFUL )
+    if (rowblockbytes == 0 || rowblockbytes > 0x7FFFFFFFUL)
     {
         /* In case of file with gigantic width */
         return;
     }
 
     /* Check that the strips are contiguous and of the expected size */
-    for( i = 0; i < td->td_nstrips; i++ )
+    for (i = 0; i < td->td_nstrips; i++)
     {
-        if( i == td->td_nstrips - 1 )
+        if (i == td->td_nstrips - 1)
         {
-            if( TIFFGetStrileByteCount(tif, i) < TIFFVStripSize64(
-                    tif, td->td_imagelength - i * td->td_rowsperstrip ) )
+            if (TIFFGetStrileByteCount(tif, i) <
+                TIFFVStripSize64(tif,
+                                 td->td_imagelength - i * td->td_rowsperstrip))
             {
                 return;
             }
         }
         else
         {
-            if( TIFFGetStrileByteCount(tif, i) != stripsize )
+            if (TIFFGetStrileByteCount(tif, i) != stripsize)
             {
                 return;
             }
-            if( i > 0 && TIFFGetStrileOffset(tif, i) !=
-                    TIFFGetStrileOffset(tif, i-1) + TIFFGetStrileByteCount(tif, i-1) )
+            if (i > 0 && TIFFGetStrileOffset(tif, i) !=
+                             TIFFGetStrileOffset(tif, i - 1) +
+                                 TIFFGetStrileByteCount(tif, i - 1))
             {
                 return;
             }
@@ -5966,27 +7460,26 @@
     }
 
     /* Aim for 512 MB strips (that will still be manageable by 32 bit builds */
-    rowblocksperstrip = (uint32) (512 * 1024 * 1024 / rowblockbytes);
-    if( rowblocksperstrip == 0 )
+    rowblocksperstrip = (uint32_t)(512 * 1024 * 1024 / rowblockbytes);
+    if (rowblocksperstrip == 0)
         rowblocksperstrip = 1;
     rowsperstrip = rowblocksperstrip * rowblock;
     stripbytes = rowblocksperstrip * rowblockbytes;
-    assert( stripbytes <= 0x7FFFFFFFUL );
+    assert(stripbytes <= 0x7FFFFFFFUL);
 
     nstrips = TIFFhowmany_32(td->td_imagelength, rowsperstrip);
-    if( nstrips == 0 )
+    if (nstrips == 0)
         return;
 
     /* If we are going to allocate a lot of memory, make sure that the */
     /* file is as big as needed */
-    if( tif->tif_mode == O_RDONLY &&
-        nstrips > 1000000 )
+    if (tif->tif_mode == O_RDONLY && nstrips > 1000000)
     {
-        uint64 last_offset = TIFFGetStrileOffset(tif, td->td_nstrips-1);
-        uint64 filesize = TIFFGetFileSize(tif);
-        uint64 last_bytecount = TIFFGetStrileByteCount(tif, td->td_nstrips-1);
-        if( last_offset > filesize ||
-            last_bytecount > filesize - last_offset )
+        uint64_t last_offset = TIFFGetStrileOffset(tif, td->td_nstrips - 1);
+        uint64_t filesize = TIFFGetFileSize(tif);
+        uint64_t last_bytecount =
+            TIFFGetStrileByteCount(tif, td->td_nstrips - 1);
+        if (last_offset > filesize || last_bytecount > filesize - last_offset)
         {
             return;
         }
@@ -5995,9 +7488,8 @@
     allocChoppedUpStripArrays(tif, nstrips, stripbytes, rowsperstrip);
 }
 
-
 TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
-static uint64 _TIFFUnsanitizedAddUInt64AndInt(uint64 a, int b)
+static uint64_t _TIFFUnsanitizedAddUInt64AndInt(uint64_t a, int b)
 {
     return a + b;
 }
@@ -6006,9 +7498,8 @@
  * strip/tile of number strile. Also fetch the neighbouring values using a
  * 4096 byte page size.
  */
-static
-int _TIFFPartialReadStripArray( TIFF* tif, TIFFDirEntry* dirent,
-                                int strile, uint64* panVals )
+static int _TIFFPartialReadStripArray(TIFF *tif, TIFFDirEntry *dirent,
+                                      int strile, uint64_t *panVals)
 {
     static const char module[] = "_TIFFPartialReadStripArray";
 #define IO_CACHE_PAGE_SIZE 4096
@@ -6016,83 +7507,88 @@
     size_t sizeofval;
     const int bSwab = (tif->tif_flags & TIFF_SWAB) != 0;
     int sizeofvalint;
-    uint64 nBaseOffset;
-    uint64 nOffset;
-    uint64 nOffsetStartPage;
-    uint64 nOffsetEndPage;
+    uint64_t nBaseOffset;
+    uint64_t nOffset;
+    uint64_t nOffsetStartPage;
+    uint64_t nOffsetEndPage;
     tmsize_t nToRead;
     tmsize_t nRead;
-    uint64 nLastStripOffset;
+    uint64_t nLastStripOffset;
     int iStartBefore;
     int i;
-    const uint32 arraySize = tif->tif_dir.td_stripoffsetbyteallocsize;
+    const uint32_t arraySize = tif->tif_dir.td_stripoffsetbyteallocsize;
     unsigned char buffer[2 * IO_CACHE_PAGE_SIZE];
 
-    assert( dirent->tdir_count > 4 );
+    assert(dirent->tdir_count > 4);
 
-    if( dirent->tdir_type == TIFF_SHORT )
+    if (dirent->tdir_type == TIFF_SHORT)
     {
-        sizeofval = sizeof(uint16);
+        sizeofval = sizeof(uint16_t);
     }
-    else if( dirent->tdir_type == TIFF_LONG )
+    else if (dirent->tdir_type == TIFF_LONG)
     {
-        sizeofval = sizeof(uint32);
+        sizeofval = sizeof(uint32_t);
     }
-    else if( dirent->tdir_type == TIFF_LONG8 )
+    else if (dirent->tdir_type == TIFF_LONG8)
     {
-        sizeofval = sizeof(uint64);
+        sizeofval = sizeof(uint64_t);
+    }
+    else if (dirent->tdir_type == TIFF_SLONG8)
+    {
+        /* Non conformant but used by some images as in */
+        /* https://github.com/OSGeo/gdal/issues/2165 */
+        sizeofval = sizeof(int64_t);
     }
     else
     {
-        TIFFErrorExt(tif->tif_clientdata, module,
-                 "Invalid type for [Strip|Tile][Offset/ByteCount] tag");
+        TIFFErrorExtR(tif, module,
+                      "Invalid type for [Strip|Tile][Offset/ByteCount] tag");
         panVals[strile] = 0;
         return 0;
     }
     sizeofvalint = (int)(sizeofval);
 
-    if( tif->tif_flags&TIFF_BIGTIFF )
+    if (tif->tif_flags & TIFF_BIGTIFF)
     {
-        uint64 offset = dirent->tdir_offset.toff_long8;
-        if( bSwab )
+        uint64_t offset = dirent->tdir_offset.toff_long8;
+        if (bSwab)
             TIFFSwabLong8(&offset);
         nBaseOffset = offset;
     }
     else
     {
-        uint32 offset = dirent->tdir_offset.toff_long;
-        if( bSwab )
+        uint32_t offset = dirent->tdir_offset.toff_long;
+        if (bSwab)
             TIFFSwabLong(&offset);
         nBaseOffset = offset;
     }
     /* To avoid later unsigned integer overflows */
-    if( nBaseOffset > (uint64)TIFF_INT64_MAX )
+    if (nBaseOffset > (uint64_t)INT64_MAX)
     {
-        TIFFErrorExt(tif->tif_clientdata, module,
-                 "Cannot read offset/size for strile %d", strile);
+        TIFFErrorExtR(tif, module, "Cannot read offset/size for strile %d",
+                      strile);
         panVals[strile] = 0;
         return 0;
     }
     nOffset = nBaseOffset + sizeofval * strile;
-    nOffsetStartPage =
-        (nOffset / IO_CACHE_PAGE_SIZE) * IO_CACHE_PAGE_SIZE;
+    nOffsetStartPage = (nOffset / IO_CACHE_PAGE_SIZE) * IO_CACHE_PAGE_SIZE;
     nOffsetEndPage = nOffsetStartPage + IO_CACHE_PAGE_SIZE;
 
-    if( nOffset + sizeofval > nOffsetEndPage )
+    if (nOffset + sizeofval > nOffsetEndPage)
         nOffsetEndPage += IO_CACHE_PAGE_SIZE;
 #undef IO_CACHE_PAGE_SIZE
 
     nLastStripOffset = nBaseOffset + arraySize * sizeofval;
-    if( nLastStripOffset < nOffsetEndPage )
+    if (nLastStripOffset < nOffsetEndPage)
         nOffsetEndPage = nLastStripOffset;
-    if( nOffsetStartPage >= nOffsetEndPage )
+    if (nOffsetStartPage >= nOffsetEndPage)
     {
-        TIFFErrorExt(tif->tif_clientdata, module,
-                 "Cannot read offset/size for strile %d", strile);
+        TIFFErrorExtR(tif, module, "Cannot read offset/size for strile %d",
+                      strile);
         panVals[strile] = 0;
         return 0;
     }
-    if (!SeekOK(tif,nOffsetStartPage))
+    if (!SeekOK(tif, nOffsetStartPage))
     {
         panVals[strile] = 0;
         return 0;
@@ -6100,148 +7596,160 @@
 
     nToRead = (tmsize_t)(nOffsetEndPage - nOffsetStartPage);
     nRead = TIFFReadFile(tif, buffer, nToRead);
-    if( nRead < nToRead )
+    if (nRead < nToRead)
     {
-        TIFFErrorExt(tif->tif_clientdata, module,
-                 "Cannot read offset/size for strile around ~%d", strile);
+        TIFFErrorExtR(tif, module,
+                      "Cannot read offset/size for strile around ~%d", strile);
         return 0;
     }
     iStartBefore = -(int)((nOffset - nOffsetStartPage) / sizeofval);
-    if( strile + iStartBefore < 0 )
+    if (strile + iStartBefore < 0)
         iStartBefore = -strile;
-    for( i = iStartBefore;
-         (uint32)(strile + i) < arraySize &&
-         _TIFFUnsanitizedAddUInt64AndInt(nOffset, (i + 1) * sizeofvalint) <= nOffsetEndPage;
-         ++i )
+    for (i = iStartBefore;
+         (uint32_t)(strile + i) < arraySize &&
+         _TIFFUnsanitizedAddUInt64AndInt(nOffset, (i + 1) * sizeofvalint) <=
+             nOffsetEndPage;
+         ++i)
     {
-        if( sizeofval == sizeof(uint16) )
+        if (dirent->tdir_type == TIFF_SHORT)
         {
-            uint16 val;
+            uint16_t val;
             memcpy(&val,
                    buffer + (nOffset - nOffsetStartPage) + i * sizeofvalint,
                    sizeof(val));
-            if( bSwab )
+            if (bSwab)
                 TIFFSwabShort(&val);
             panVals[strile + i] = val;
         }
-        else if( sizeofval == sizeof(uint32) )
+        else if (dirent->tdir_type == TIFF_LONG)
         {
-            uint32 val;
+            uint32_t val;
             memcpy(&val,
                    buffer + (nOffset - nOffsetStartPage) + i * sizeofvalint,
                    sizeof(val));
-            if( bSwab )
+            if (bSwab)
                 TIFFSwabLong(&val);
             panVals[strile + i] = val;
         }
-        else
+        else if (dirent->tdir_type == TIFF_LONG8)
         {
-            uint64 val;
+            uint64_t val;
             memcpy(&val,
                    buffer + (nOffset - nOffsetStartPage) + i * sizeofvalint,
                    sizeof(val));
-            if( bSwab )
+            if (bSwab)
                 TIFFSwabLong8(&val);
             panVals[strile + i] = val;
         }
+        else /* if( dirent->tdir_type == TIFF_SLONG8 ) */
+        {
+            /* Non conformant data type */
+            int64_t val;
+            memcpy(&val,
+                   buffer + (nOffset - nOffsetStartPage) + i * sizeofvalint,
+                   sizeof(val));
+            if (bSwab)
+                TIFFSwabLong8((uint64_t *)&val);
+            panVals[strile + i] = (uint64_t)val;
+        }
     }
     return 1;
 }
 
-static int _TIFFFetchStrileValue(TIFF* tif,
-                                 uint32 strile,
-                                 TIFFDirEntry* dirent,
-                                 uint64** parray)
+static int _TIFFFetchStrileValue(TIFF *tif, uint32_t strile,
+                                 TIFFDirEntry *dirent, uint64_t **parray)
 {
     static const char module[] = "_TIFFFetchStrileValue";
     TIFFDirectory *td = &tif->tif_dir;
-    if( strile >= dirent->tdir_count )
+    if (strile >= dirent->tdir_count)
     {
         return 0;
     }
-    if( strile >= td->td_stripoffsetbyteallocsize )
+    if (strile >= td->td_stripoffsetbyteallocsize)
     {
-        uint32 nStripArrayAllocBefore = td->td_stripoffsetbyteallocsize;
-        uint32 nStripArrayAllocNew;
-        uint64 nArraySize64;
+        uint32_t nStripArrayAllocBefore = td->td_stripoffsetbyteallocsize;
+        uint32_t nStripArrayAllocNew;
+        uint64_t nArraySize64;
         size_t nArraySize;
-        uint64* offsetArray;
-        uint64* bytecountArray;
+        uint64_t *offsetArray;
+        uint64_t *bytecountArray;
 
-        if( strile > 1000000 )
+        if (strile > 1000000)
         {
-            uint64 filesize = TIFFGetFileSize(tif);
+            uint64_t filesize = TIFFGetFileSize(tif);
             /* Avoid excessive memory allocation attempt */
             /* For such a big blockid we need at least a TIFF_LONG per strile */
             /* for the offset array. */
-            if( strile > filesize / sizeof(uint32) )
+            if (strile > filesize / sizeof(uint32_t))
             {
-                TIFFErrorExt(tif->tif_clientdata, module, "File too short");
+                TIFFErrorExtR(tif, module, "File too short");
                 return 0;
             }
         }
 
-        if( td->td_stripoffsetbyteallocsize == 0 &&
-            td->td_nstrips < 1024 * 1024 )
+        if (td->td_stripoffsetbyteallocsize == 0 &&
+            td->td_nstrips < 1024 * 1024)
         {
             nStripArrayAllocNew = td->td_nstrips;
         }
         else
         {
-#define TIFF_MAX(a,b) (((a)>(b)) ? (a) : (b))
-#define TIFF_MIN(a,b) (((a)<(b)) ? (a) : (b))
-            nStripArrayAllocNew = TIFF_MAX(strile + 1, 1024U * 512U );
-            if( nStripArrayAllocNew < 0xFFFFFFFFU / 2  )
+#define TIFF_MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define TIFF_MIN(a, b) (((a) < (b)) ? (a) : (b))
+            nStripArrayAllocNew = TIFF_MAX(strile + 1, 1024U * 512U);
+            if (nStripArrayAllocNew < 0xFFFFFFFFU / 2)
                 nStripArrayAllocNew *= 2;
             nStripArrayAllocNew = TIFF_MIN(nStripArrayAllocNew, td->td_nstrips);
         }
-        assert( strile < nStripArrayAllocNew );
-        nArraySize64 = (uint64)sizeof(uint64) * nStripArrayAllocNew;
+        assert(strile < nStripArrayAllocNew);
+        nArraySize64 = (uint64_t)sizeof(uint64_t) * nStripArrayAllocNew;
         nArraySize = (size_t)(nArraySize64);
 #if SIZEOF_SIZE_T == 4
-        if( nArraySize != nArraySize64 )
+        if (nArraySize != nArraySize64)
         {
-            TIFFErrorExt(tif->tif_clientdata, module,
-                        "Cannot allocate strip offset and bytecount arrays");
+            TIFFErrorExtR(tif, module,
+                          "Cannot allocate strip offset and bytecount arrays");
             return 0;
         }
 #endif
-        offsetArray = (uint64*)(
-            _TIFFrealloc( td->td_stripoffset_p, nArraySize ) );
-        bytecountArray = (uint64*)(
-            _TIFFrealloc( td->td_stripbytecount_p, nArraySize ) );
-        if( offsetArray )
+        offsetArray = (uint64_t *)(_TIFFreallocExt(tif, td->td_stripoffset_p,
+                                                   nArraySize));
+        bytecountArray = (uint64_t *)(_TIFFreallocExt(
+            tif, td->td_stripbytecount_p, nArraySize));
+        if (offsetArray)
             td->td_stripoffset_p = offsetArray;
-        if( bytecountArray )
+        if (bytecountArray)
             td->td_stripbytecount_p = bytecountArray;
-        if( offsetArray && bytecountArray )
+        if (offsetArray && bytecountArray)
         {
             td->td_stripoffsetbyteallocsize = nStripArrayAllocNew;
             /* Initialize new entries to ~0 / -1 */
-            memset(td->td_stripoffset_p + nStripArrayAllocBefore,
-                0xFF,
-                (td->td_stripoffsetbyteallocsize - nStripArrayAllocBefore) * sizeof(uint64) );
-            memset(td->td_stripbytecount_p + nStripArrayAllocBefore,
-                0xFF,
-                (td->td_stripoffsetbyteallocsize - nStripArrayAllocBefore) * sizeof(uint64) );
+            /* coverity[overrun-buffer-arg] */
+            memset(td->td_stripoffset_p + nStripArrayAllocBefore, 0xFF,
+                   (td->td_stripoffsetbyteallocsize - nStripArrayAllocBefore) *
+                       sizeof(uint64_t));
+            /* coverity[overrun-buffer-arg] */
+            memset(td->td_stripbytecount_p + nStripArrayAllocBefore, 0xFF,
+                   (td->td_stripoffsetbyteallocsize - nStripArrayAllocBefore) *
+                       sizeof(uint64_t));
         }
         else
         {
-            TIFFErrorExt(tif->tif_clientdata, module,
-                        "Cannot allocate strip offset and bytecount arrays");
-            _TIFFfree(td->td_stripoffset_p);
+            TIFFErrorExtR(tif, module,
+                          "Cannot allocate strip offset and bytecount arrays");
+            _TIFFfreeExt(tif, td->td_stripoffset_p);
             td->td_stripoffset_p = NULL;
-            _TIFFfree(td->td_stripbytecount_p);
+            _TIFFfreeExt(tif, td->td_stripbytecount_p);
             td->td_stripbytecount_p = NULL;
             td->td_stripoffsetbyteallocsize = 0;
         }
     }
-    if( *parray == NULL || strile >= td->td_stripoffsetbyteallocsize )
+    if (*parray == NULL || strile >= td->td_stripoffsetbyteallocsize)
         return 0;
 
-    if( ~((*parray)[strile]) == 0 )
+    if (~((*parray)[strile]) == 0)
     {
-        if( !_TIFFPartialReadStripArray( tif, dirent, strile, *parray ) )
+        if (!_TIFFPartialReadStripArray(tif, dirent, strile, *parray))
         {
             (*parray)[strile] = 0;
             return 0;
@@ -6251,24 +7759,25 @@
     return 1;
 }
 
-static uint64 _TIFFGetStrileOffsetOrByteCountValue(TIFF *tif, uint32 strile,
-                                                   TIFFDirEntry* dirent,
-                                                   uint64** parray,
-                                                   int *pbErr)
+static uint64_t _TIFFGetStrileOffsetOrByteCountValue(TIFF *tif, uint32_t strile,
+                                                     TIFFDirEntry *dirent,
+                                                     uint64_t **parray,
+                                                     int *pbErr)
 {
     TIFFDirectory *td = &tif->tif_dir;
-    if( pbErr )
+    if (pbErr)
         *pbErr = 0;
-    if( (tif->tif_flags&TIFF_DEFERSTRILELOAD) && !(tif->tif_flags&TIFF_CHOPPEDUPARRAYS) )
+    if ((tif->tif_flags & TIFF_DEFERSTRILELOAD) &&
+        !(tif->tif_flags & TIFF_CHOPPEDUPARRAYS))
     {
-        if( !(tif->tif_flags&TIFF_LAZYSTRILELOAD) ||
+        if (!(tif->tif_flags & TIFF_LAZYSTRILELOAD) ||
             /* If the values may fit in the toff_long/toff_long8 member */
             /* then use _TIFFFillStriles to simplify _TIFFFetchStrileValue */
-            dirent->tdir_count <= 4 )
+            dirent->tdir_count <= 4)
         {
-            if( !_TIFFFillStriles(tif) )
+            if (!_TIFFFillStriles(tif))
             {
-                if( pbErr )
+                if (pbErr)
                     *pbErr = 1;
                 /* Do not return, as we want this function to always */
                 /* return the same value if called several times with */
@@ -6277,73 +7786,74 @@
         }
         else
         {
-             if( !_TIFFFetchStrileValue(tif, strile, dirent, parray) )
-             {
-                if( pbErr )
+            if (!_TIFFFetchStrileValue(tif, strile, dirent, parray))
+            {
+                if (pbErr)
                     *pbErr = 1;
-                 return 0;
-             }
+                return 0;
+            }
         }
     }
-    if( *parray == NULL || strile >= td->td_nstrips )
+    if (*parray == NULL || strile >= td->td_nstrips)
     {
-        if( pbErr )
+        if (pbErr)
             *pbErr = 1;
         return 0;
     }
     return (*parray)[strile];
 }
 
-/* Return the value of the TileOffsets/StripOffsets array for the specified tile/strile */
-uint64 TIFFGetStrileOffset(TIFF *tif, uint32 strile)
+/* Return the value of the TileOffsets/StripOffsets array for the specified
+ * tile/strile */
+uint64_t TIFFGetStrileOffset(TIFF *tif, uint32_t strile)
 {
     return TIFFGetStrileOffsetWithErr(tif, strile, NULL);
 }
 
-/* Return the value of the TileOffsets/StripOffsets array for the specified tile/strile */
-uint64 TIFFGetStrileOffsetWithErr(TIFF *tif, uint32 strile, int *pbErr)
+/* Return the value of the TileOffsets/StripOffsets array for the specified
+ * tile/strile */
+uint64_t TIFFGetStrileOffsetWithErr(TIFF *tif, uint32_t strile, int *pbErr)
 {
     TIFFDirectory *td = &tif->tif_dir;
     return _TIFFGetStrileOffsetOrByteCountValue(tif, strile,
-                               &(td->td_stripoffset_entry),
-                               &(td->td_stripoffset_p), pbErr);
+                                                &(td->td_stripoffset_entry),
+                                                &(td->td_stripoffset_p), pbErr);
 }
 
-/* Return the value of the TileByteCounts/StripByteCounts array for the specified tile/strile */
-uint64 TIFFGetStrileByteCount(TIFF *tif, uint32 strile)
+/* Return the value of the TileByteCounts/StripByteCounts array for the
+ * specified tile/strile */
+uint64_t TIFFGetStrileByteCount(TIFF *tif, uint32_t strile)
 {
     return TIFFGetStrileByteCountWithErr(tif, strile, NULL);
 }
 
-/* Return the value of the TileByteCounts/StripByteCounts array for the specified tile/strile */
-uint64 TIFFGetStrileByteCountWithErr(TIFF *tif, uint32 strile, int *pbErr)
+/* Return the value of the TileByteCounts/StripByteCounts array for the
+ * specified tile/strile */
+uint64_t TIFFGetStrileByteCountWithErr(TIFF *tif, uint32_t strile, int *pbErr)
 {
     TIFFDirectory *td = &tif->tif_dir;
-    return _TIFFGetStrileOffsetOrByteCountValue(tif, strile,
-                               &(td->td_stripbytecount_entry),
-                               &(td->td_stripbytecount_p), pbErr);
+    return _TIFFGetStrileOffsetOrByteCountValue(
+        tif, strile, &(td->td_stripbytecount_entry), &(td->td_stripbytecount_p),
+        pbErr);
 }
 
+int _TIFFFillStriles(TIFF *tif) { return _TIFFFillStrilesInternal(tif, 1); }
 
-int _TIFFFillStriles( TIFF *tif )
-{
-    return _TIFFFillStrilesInternal( tif, 1 );
-}
-
-static int _TIFFFillStrilesInternal( TIFF *tif, int loadStripByteCount )
+static int _TIFFFillStrilesInternal(TIFF *tif, int loadStripByteCount)
 {
     register TIFFDirectory *td = &tif->tif_dir;
     int return_value = 1;
 
     /* Do not do anything if TIFF_DEFERSTRILELOAD is not set */
-    if( !(tif->tif_flags&TIFF_DEFERSTRILELOAD) || (tif->tif_flags&TIFF_CHOPPEDUPARRAYS) != 0 )
+    if (!(tif->tif_flags & TIFF_DEFERSTRILELOAD) ||
+        (tif->tif_flags & TIFF_CHOPPEDUPARRAYS) != 0)
         return 1;
 
-    if( tif->tif_flags&TIFF_LAZYSTRILELOAD )
+    if (tif->tif_flags & TIFF_LAZYSTRILELOAD)
     {
         /* In case of lazy loading, reload completely the arrays */
-        _TIFFfree(td->td_stripoffset_p);
-        _TIFFfree(td->td_stripbytecount_p);
+        _TIFFfreeExt(tif, td->td_stripoffset_p);
+        _TIFFfreeExt(tif, td->td_stripbytecount_p);
         td->td_stripoffset_p = NULL;
         td->td_stripbytecount_p = NULL;
         td->td_stripoffsetbyteallocsize = 0;
@@ -6351,53 +7861,46 @@
     }
 
     /* If stripoffset array is already loaded, exit with success */
-    if( td->td_stripoffset_p != NULL )
-            return 1;
+    if (td->td_stripoffset_p != NULL)
+        return 1;
 
-    /* If tdir_count was cancelled, then we already got there, but in error */
-    if( td->td_stripoffset_entry.tdir_count == 0 )
-            return 0;
+    /* If tdir_count was canceled, then we already got there, but in error */
+    if (td->td_stripoffset_entry.tdir_count == 0)
+        return 0;
 
-    if (!TIFFFetchStripThing(tif,&(td->td_stripoffset_entry),
-                                td->td_nstrips,&td->td_stripoffset_p))
+    if (!TIFFFetchStripThing(tif, &(td->td_stripoffset_entry), td->td_nstrips,
+                             &td->td_stripoffset_p))
     {
-            return_value = 0;
+        return_value = 0;
     }
 
     if (loadStripByteCount &&
-        !TIFFFetchStripThing(tif,&(td->td_stripbytecount_entry),
-                                td->td_nstrips,&td->td_stripbytecount_p))
+        !TIFFFetchStripThing(tif, &(td->td_stripbytecount_entry),
+                             td->td_nstrips, &td->td_stripbytecount_p))
     {
-            return_value = 0;
+        return_value = 0;
     }
 
-    _TIFFmemset( &(td->td_stripoffset_entry), 0, sizeof(TIFFDirEntry));
-    _TIFFmemset( &(td->td_stripbytecount_entry), 0, sizeof(TIFFDirEntry));
+    _TIFFmemset(&(td->td_stripoffset_entry), 0, sizeof(TIFFDirEntry));
+    _TIFFmemset(&(td->td_stripbytecount_entry), 0, sizeof(TIFFDirEntry));
 
 #ifdef STRIPBYTECOUNTSORTED_UNUSED
-    if (tif->tif_dir.td_nstrips > 1 && return_value == 1 ) {
-            uint32 strip;
+    if (tif->tif_dir.td_nstrips > 1 && return_value == 1)
+    {
+        uint32_t strip;
 
-            tif->tif_dir.td_stripbytecountsorted = 1;
-            for (strip = 1; strip < tif->tif_dir.td_nstrips; strip++) {
-                    if (tif->tif_dir.td_stripoffset_p[strip - 1] >
-                        tif->tif_dir.td_stripoffset_p[strip]) {
-                            tif->tif_dir.td_stripbytecountsorted = 0;
-                            break;
-                    }
+        tif->tif_dir.td_stripbytecountsorted = 1;
+        for (strip = 1; strip < tif->tif_dir.td_nstrips; strip++)
+        {
+            if (tif->tif_dir.td_stripoffset_p[strip - 1] >
+                tif->tif_dir.td_stripoffset_p[strip])
+            {
+                tif->tif_dir.td_stripbytecountsorted = 0;
+                break;
             }
+        }
     }
 #endif
 
     return return_value;
 }
-
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_dirwrite.c b/third_party/libtiff/tif_dirwrite.c
index 9e4d306..a6a485f 100644
--- a/third_party/libtiff/tif_dirwrite.c
+++ b/third_party/libtiff/tif_dirwrite.c
@@ -28,146 +28,203 @@
  * Directory Write Support Routines.
  */
 #include "tiffiop.h"
+#include <float.h> /*--: for Rational2Double */
+#include <math.h>  /*--: for Rational2Double */
 
 #ifdef HAVE_IEEEFP
 #define TIFFCvtNativeToIEEEFloat(tif, n, fp)
 #define TIFFCvtNativeToIEEEDouble(tif, n, dp)
 #else
-extern void TIFFCvtNativeToIEEEFloat(TIFF* tif, uint32 n, float* fp);
-extern void TIFFCvtNativeToIEEEDouble(TIFF* tif, uint32 n, double* dp);
+extern void TIFFCvtNativeToIEEEFloat(TIFF *tif, uint32_t n, float *fp);
+extern void TIFFCvtNativeToIEEEDouble(TIFF *tif, uint32_t n, double *dp);
 #endif
 
-static int TIFFWriteDirectorySec(TIFF* tif, int isimage, int imagedone, uint64* pdiroff);
+static int TIFFWriteDirectorySec(TIFF *tif, int isimage, int imagedone,
+                                 uint64_t *pdiroff);
 
-static int TIFFWriteDirectoryTagSampleformatArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, double* value);
-#if 0
-static int TIFFWriteDirectoryTagSampleformatPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value);
-#endif
+static int TIFFWriteDirectoryTagSampleformatArray(TIFF *tif, uint32_t *ndir,
+                                                  TIFFDirEntry *dir,
+                                                  uint16_t tag, uint32_t count,
+                                                  double *value);
 
-static int TIFFWriteDirectoryTagAscii(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, char* value);
-static int TIFFWriteDirectoryTagUndefinedArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint8* value);
-#ifdef notdef
-static int TIFFWriteDirectoryTagByte(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint8 value);
-#endif
-static int TIFFWriteDirectoryTagByteArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint8* value);
-#if 0
-static int TIFFWriteDirectoryTagBytePerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint8 value);
-#endif
-#ifdef notdef
-static int TIFFWriteDirectoryTagSbyte(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int8 value);
-#endif
-static int TIFFWriteDirectoryTagSbyteArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int8* value);
-#if 0
-static int TIFFWriteDirectoryTagSbytePerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int8 value);
-#endif
-static int TIFFWriteDirectoryTagShort(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint16 value);
-static int TIFFWriteDirectoryTagShortArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint16* value);
-static int TIFFWriteDirectoryTagShortPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint16 value);
-#ifdef notdef
-static int TIFFWriteDirectoryTagSshort(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int16 value);
-#endif
-static int TIFFWriteDirectoryTagSshortArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int16* value);
-#if 0
-static int TIFFWriteDirectoryTagSshortPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int16 value);
-#endif
-static int TIFFWriteDirectoryTagLong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 value);
-static int TIFFWriteDirectoryTagLongArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint32* value);
-#if 0
-static int TIFFWriteDirectoryTagLongPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 value);
-#endif
-#ifdef notdef
-static int TIFFWriteDirectoryTagSlong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int32 value);
-#endif
-static int TIFFWriteDirectoryTagSlongArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int32* value);
-#if 0
-static int TIFFWriteDirectoryTagSlongPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int32 value);
-#endif
-#ifdef notdef
-static int TIFFWriteDirectoryTagLong8(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint64 value);
-#endif
-static int TIFFWriteDirectoryTagLong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value);
-#ifdef notdef
-static int TIFFWriteDirectoryTagSlong8(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int64 value);
-#endif
-static int TIFFWriteDirectoryTagSlong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int64* value);
-static int TIFFWriteDirectoryTagRational(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value);
-static int TIFFWriteDirectoryTagRationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value);
-static int TIFFWriteDirectoryTagSrationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value);
-#ifdef notdef
-static int TIFFWriteDirectoryTagFloat(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, float value);
-#endif
-static int TIFFWriteDirectoryTagFloatArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value);
-#if 0
-static int TIFFWriteDirectoryTagFloatPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, float value);
-#endif
-#ifdef notdef
-static int TIFFWriteDirectoryTagDouble(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value);
-#endif
-static int TIFFWriteDirectoryTagDoubleArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, double* value);
-#if 0
-static int TIFFWriteDirectoryTagDoublePerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value);
-#endif
-static int TIFFWriteDirectoryTagIfdArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint32* value);
-#ifdef notdef
-static int TIFFWriteDirectoryTagIfd8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value);
-#endif
-static int TIFFWriteDirectoryTagShortLong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 value);
-static int TIFFWriteDirectoryTagLongLong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value);
-static int TIFFWriteDirectoryTagIfdIfd8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value);
-#ifdef notdef
-static int TIFFWriteDirectoryTagShortLongLong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value);
-#endif
-static int TIFFWriteDirectoryTagColormap(TIFF* tif, uint32* ndir, TIFFDirEntry* dir);
-static int TIFFWriteDirectoryTagTransferfunction(TIFF* tif, uint32* ndir, TIFFDirEntry* dir);
-static int TIFFWriteDirectoryTagSubifd(TIFF* tif, uint32* ndir, TIFFDirEntry* dir);
+static int TIFFWriteDirectoryTagAscii(TIFF *tif, uint32_t *ndir,
+                                      TIFFDirEntry *dir, uint16_t tag,
+                                      uint32_t count, char *value);
+static int TIFFWriteDirectoryTagUndefinedArray(TIFF *tif, uint32_t *ndir,
+                                               TIFFDirEntry *dir, uint16_t tag,
+                                               uint32_t count, uint8_t *value);
+static int TIFFWriteDirectoryTagByteArray(TIFF *tif, uint32_t *ndir,
+                                          TIFFDirEntry *dir, uint16_t tag,
+                                          uint32_t count, uint8_t *value);
+static int TIFFWriteDirectoryTagSbyteArray(TIFF *tif, uint32_t *ndir,
+                                           TIFFDirEntry *dir, uint16_t tag,
+                                           uint32_t count, int8_t *value);
+static int TIFFWriteDirectoryTagShort(TIFF *tif, uint32_t *ndir,
+                                      TIFFDirEntry *dir, uint16_t tag,
+                                      uint16_t value);
+static int TIFFWriteDirectoryTagShortArray(TIFF *tif, uint32_t *ndir,
+                                           TIFFDirEntry *dir, uint16_t tag,
+                                           uint32_t count, uint16_t *value);
+static int TIFFWriteDirectoryTagShortPerSample(TIFF *tif, uint32_t *ndir,
+                                               TIFFDirEntry *dir, uint16_t tag,
+                                               uint16_t value);
+static int TIFFWriteDirectoryTagSshortArray(TIFF *tif, uint32_t *ndir,
+                                            TIFFDirEntry *dir, uint16_t tag,
+                                            uint32_t count, int16_t *value);
+static int TIFFWriteDirectoryTagLong(TIFF *tif, uint32_t *ndir,
+                                     TIFFDirEntry *dir, uint16_t tag,
+                                     uint32_t value);
+static int TIFFWriteDirectoryTagLongArray(TIFF *tif, uint32_t *ndir,
+                                          TIFFDirEntry *dir, uint16_t tag,
+                                          uint32_t count, uint32_t *value);
+static int TIFFWriteDirectoryTagSlongArray(TIFF *tif, uint32_t *ndir,
+                                           TIFFDirEntry *dir, uint16_t tag,
+                                           uint32_t count, int32_t *value);
+static int TIFFWriteDirectoryTagLong8Array(TIFF *tif, uint32_t *ndir,
+                                           TIFFDirEntry *dir, uint16_t tag,
+                                           uint32_t count, uint64_t *value);
+static int TIFFWriteDirectoryTagSlong8Array(TIFF *tif, uint32_t *ndir,
+                                            TIFFDirEntry *dir, uint16_t tag,
+                                            uint32_t count, int64_t *value);
+static int TIFFWriteDirectoryTagRational(TIFF *tif, uint32_t *ndir,
+                                         TIFFDirEntry *dir, uint16_t tag,
+                                         double value);
+static int TIFFWriteDirectoryTagRationalArray(TIFF *tif, uint32_t *ndir,
+                                              TIFFDirEntry *dir, uint16_t tag,
+                                              uint32_t count, float *value);
+static int TIFFWriteDirectoryTagSrationalArray(TIFF *tif, uint32_t *ndir,
+                                               TIFFDirEntry *dir, uint16_t tag,
+                                               uint32_t count, float *value);
+static int TIFFWriteDirectoryTagFloatArray(TIFF *tif, uint32_t *ndir,
+                                           TIFFDirEntry *dir, uint16_t tag,
+                                           uint32_t count, float *value);
+static int TIFFWriteDirectoryTagDoubleArray(TIFF *tif, uint32_t *ndir,
+                                            TIFFDirEntry *dir, uint16_t tag,
+                                            uint32_t count, double *value);
+static int TIFFWriteDirectoryTagIfdArray(TIFF *tif, uint32_t *ndir,
+                                         TIFFDirEntry *dir, uint16_t tag,
+                                         uint32_t count, uint32_t *value);
+static int TIFFWriteDirectoryTagShortLong(TIFF *tif, uint32_t *ndir,
+                                          TIFFDirEntry *dir, uint16_t tag,
+                                          uint32_t value);
+static int TIFFWriteDirectoryTagLongLong8Array(TIFF *tif, uint32_t *ndir,
+                                               TIFFDirEntry *dir, uint16_t tag,
+                                               uint32_t count, uint64_t *value);
+static int TIFFWriteDirectoryTagIfdIfd8Array(TIFF *tif, uint32_t *ndir,
+                                             TIFFDirEntry *dir, uint16_t tag,
+                                             uint32_t count, uint64_t *value);
+static int TIFFWriteDirectoryTagColormap(TIFF *tif, uint32_t *ndir,
+                                         TIFFDirEntry *dir);
+static int TIFFWriteDirectoryTagTransferfunction(TIFF *tif, uint32_t *ndir,
+                                                 TIFFDirEntry *dir);
+static int TIFFWriteDirectoryTagSubifd(TIFF *tif, uint32_t *ndir,
+                                       TIFFDirEntry *dir);
 
-static int TIFFWriteDirectoryTagCheckedAscii(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, char* value);
-static int TIFFWriteDirectoryTagCheckedUndefinedArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint8* value);
-#ifdef notdef
-static int TIFFWriteDirectoryTagCheckedByte(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint8 value);
-#endif
-static int TIFFWriteDirectoryTagCheckedByteArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint8* value);
-#ifdef notdef
-static int TIFFWriteDirectoryTagCheckedSbyte(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int8 value);
-#endif
-static int TIFFWriteDirectoryTagCheckedSbyteArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int8* value);
-static int TIFFWriteDirectoryTagCheckedShort(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint16 value);
-static int TIFFWriteDirectoryTagCheckedShortArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint16* value);
-#ifdef notdef
-static int TIFFWriteDirectoryTagCheckedSshort(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int16 value);
-#endif
-static int TIFFWriteDirectoryTagCheckedSshortArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int16* value);
-static int TIFFWriteDirectoryTagCheckedLong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 value);
-static int TIFFWriteDirectoryTagCheckedLongArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint32* value);
-#ifdef notdef
-static int TIFFWriteDirectoryTagCheckedSlong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int32 value);
-#endif
-static int TIFFWriteDirectoryTagCheckedSlongArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int32* value);
-#ifdef notdef
-static int TIFFWriteDirectoryTagCheckedLong8(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint64 value);
-#endif
-static int TIFFWriteDirectoryTagCheckedLong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value);
-#ifdef notdef
-static int TIFFWriteDirectoryTagCheckedSlong8(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int64 value);
-#endif
-static int TIFFWriteDirectoryTagCheckedSlong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int64* value);
-static int TIFFWriteDirectoryTagCheckedRational(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value);
-static int TIFFWriteDirectoryTagCheckedRationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value);
-static int TIFFWriteDirectoryTagCheckedSrationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value);
-#ifdef notdef
-static int TIFFWriteDirectoryTagCheckedFloat(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, float value);
-#endif
-static int TIFFWriteDirectoryTagCheckedFloatArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value);
-#ifdef notdef
-static int TIFFWriteDirectoryTagCheckedDouble(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value);
-#endif
-static int TIFFWriteDirectoryTagCheckedDoubleArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, double* value);
-static int TIFFWriteDirectoryTagCheckedIfdArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint32* value);
-static int TIFFWriteDirectoryTagCheckedIfd8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value);
+static int TIFFWriteDirectoryTagCheckedAscii(TIFF *tif, uint32_t *ndir,
+                                             TIFFDirEntry *dir, uint16_t tag,
+                                             uint32_t count, char *value);
+static int TIFFWriteDirectoryTagCheckedUndefinedArray(TIFF *tif, uint32_t *ndir,
+                                                      TIFFDirEntry *dir,
+                                                      uint16_t tag,
+                                                      uint32_t count,
+                                                      uint8_t *value);
+static int TIFFWriteDirectoryTagCheckedByteArray(TIFF *tif, uint32_t *ndir,
+                                                 TIFFDirEntry *dir,
+                                                 uint16_t tag, uint32_t count,
+                                                 uint8_t *value);
+static int TIFFWriteDirectoryTagCheckedSbyteArray(TIFF *tif, uint32_t *ndir,
+                                                  TIFFDirEntry *dir,
+                                                  uint16_t tag, uint32_t count,
+                                                  int8_t *value);
+static int TIFFWriteDirectoryTagCheckedShort(TIFF *tif, uint32_t *ndir,
+                                             TIFFDirEntry *dir, uint16_t tag,
+                                             uint16_t value);
+static int TIFFWriteDirectoryTagCheckedShortArray(TIFF *tif, uint32_t *ndir,
+                                                  TIFFDirEntry *dir,
+                                                  uint16_t tag, uint32_t count,
+                                                  uint16_t *value);
+static int TIFFWriteDirectoryTagCheckedSshortArray(TIFF *tif, uint32_t *ndir,
+                                                   TIFFDirEntry *dir,
+                                                   uint16_t tag, uint32_t count,
+                                                   int16_t *value);
+static int TIFFWriteDirectoryTagCheckedLong(TIFF *tif, uint32_t *ndir,
+                                            TIFFDirEntry *dir, uint16_t tag,
+                                            uint32_t value);
+static int TIFFWriteDirectoryTagCheckedLongArray(TIFF *tif, uint32_t *ndir,
+                                                 TIFFDirEntry *dir,
+                                                 uint16_t tag, uint32_t count,
+                                                 uint32_t *value);
+static int TIFFWriteDirectoryTagCheckedSlongArray(TIFF *tif, uint32_t *ndir,
+                                                  TIFFDirEntry *dir,
+                                                  uint16_t tag, uint32_t count,
+                                                  int32_t *value);
+static int TIFFWriteDirectoryTagCheckedLong8Array(TIFF *tif, uint32_t *ndir,
+                                                  TIFFDirEntry *dir,
+                                                  uint16_t tag, uint32_t count,
+                                                  uint64_t *value);
+static int TIFFWriteDirectoryTagCheckedSlong8Array(TIFF *tif, uint32_t *ndir,
+                                                   TIFFDirEntry *dir,
+                                                   uint16_t tag, uint32_t count,
+                                                   int64_t *value);
+static int TIFFWriteDirectoryTagCheckedRational(TIFF *tif, uint32_t *ndir,
+                                                TIFFDirEntry *dir, uint16_t tag,
+                                                double value);
+static int TIFFWriteDirectoryTagCheckedRationalArray(TIFF *tif, uint32_t *ndir,
+                                                     TIFFDirEntry *dir,
+                                                     uint16_t tag,
+                                                     uint32_t count,
+                                                     float *value);
+static int TIFFWriteDirectoryTagCheckedSrationalArray(TIFF *tif, uint32_t *ndir,
+                                                      TIFFDirEntry *dir,
+                                                      uint16_t tag,
+                                                      uint32_t count,
+                                                      float *value);
 
-static int TIFFWriteDirectoryTagData(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint16 datatype, uint32 count, uint32 datalength, void* data);
+/*--: Rational2Double: New functions to support true double-precision for custom
+ * rational tag types. */
+static int TIFFWriteDirectoryTagRationalDoubleArray(TIFF *tif, uint32_t *ndir,
+                                                    TIFFDirEntry *dir,
+                                                    uint16_t tag,
+                                                    uint32_t count,
+                                                    double *value);
+static int TIFFWriteDirectoryTagSrationalDoubleArray(TIFF *tif, uint32_t *ndir,
+                                                     TIFFDirEntry *dir,
+                                                     uint16_t tag,
+                                                     uint32_t count,
+                                                     double *value);
+static int
+TIFFWriteDirectoryTagCheckedRationalDoubleArray(TIFF *tif, uint32_t *ndir,
+                                                TIFFDirEntry *dir, uint16_t tag,
+                                                uint32_t count, double *value);
+static int TIFFWriteDirectoryTagCheckedSrationalDoubleArray(
+    TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count,
+    double *value);
+static void DoubleToRational(double value, uint32_t *num, uint32_t *denom);
+static void DoubleToSrational(double value, int32_t *num, int32_t *denom);
 
-static int TIFFLinkDirectory(TIFF*);
+static int TIFFWriteDirectoryTagCheckedFloatArray(TIFF *tif, uint32_t *ndir,
+                                                  TIFFDirEntry *dir,
+                                                  uint16_t tag, uint32_t count,
+                                                  float *value);
+static int TIFFWriteDirectoryTagCheckedDoubleArray(TIFF *tif, uint32_t *ndir,
+                                                   TIFFDirEntry *dir,
+                                                   uint16_t tag, uint32_t count,
+                                                   double *value);
+static int TIFFWriteDirectoryTagCheckedIfdArray(TIFF *tif, uint32_t *ndir,
+                                                TIFFDirEntry *dir, uint16_t tag,
+                                                uint32_t count,
+                                                uint32_t *value);
+static int TIFFWriteDirectoryTagCheckedIfd8Array(TIFF *tif, uint32_t *ndir,
+                                                 TIFFDirEntry *dir,
+                                                 uint16_t tag, uint32_t count,
+                                                 uint64_t *value);
+
+static int TIFFWriteDirectoryTagData(TIFF *tif, uint32_t *ndir,
+                                     TIFFDirEntry *dir, uint16_t tag,
+                                     uint16_t datatype, uint32_t count,
+                                     uint32_t datalength, void *data);
+
+static int TIFFLinkDirectory(TIFF *);
 
 /*
  * Write the contents of the current directory
@@ -175,10 +232,9 @@
  * handle overwriting a directory with auxiliary
  * storage that's been changed.
  */
-int
-TIFFWriteDirectory(TIFF* tif)
+int TIFFWriteDirectory(TIFF *tif)
 {
-	return TIFFWriteDirectorySec(tif,TRUE,TRUE,NULL);
+    return TIFFWriteDirectorySec(tif, TRUE, TRUE, NULL);
 }
 
 /*
@@ -206,19 +262,17 @@
  *
  * Returns 1 in case of success, 0 otherwise.
  */
-int TIFFDeferStrileArrayWriting(TIFF* tif)
+int TIFFDeferStrileArrayWriting(TIFF *tif)
 {
     static const char module[] = "TIFFDeferStrileArrayWriting";
     if (tif->tif_mode == O_RDONLY)
     {
-        TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-                     "File opened in read-only mode");
+        TIFFErrorExtR(tif, tif->tif_name, "File opened in read-only mode");
         return 0;
     }
-    if( tif->tif_diroff != 0 )
+    if (tif->tif_diroff != 0)
     {
-        TIFFErrorExt(tif->tif_clientdata, module,
-                     "Directory has already been written");
+        TIFFErrorExtR(tif, module, "Directory has already been written");
         return 0;
     }
 
@@ -232,359 +286,430 @@
  * written again.  This will make a partially written TIFF file
  * readable before it is successfully completed/closed.
  */
-int
-TIFFCheckpointDirectory(TIFF* tif)
+int TIFFCheckpointDirectory(TIFF *tif)
 {
-	int rc;
-	/* Setup the strips arrays, if they haven't already been. */
-	if (tif->tif_dir.td_stripoffset_p == NULL)
-	    (void) TIFFSetupStrips(tif);
-	rc = TIFFWriteDirectorySec(tif,TRUE,FALSE,NULL);
-	(void) TIFFSetWriteOffset(tif, TIFFSeekFile(tif, 0, SEEK_END));
-	return rc;
+    int rc;
+    /* Setup the strips arrays, if they haven't already been. */
+    if (tif->tif_dir.td_stripoffset_p == NULL)
+        (void)TIFFSetupStrips(tif);
+    rc = TIFFWriteDirectorySec(tif, TRUE, FALSE, NULL);
+    (void)TIFFSetWriteOffset(tif, TIFFSeekFile(tif, 0, SEEK_END));
+    return rc;
 }
 
-int
-TIFFWriteCustomDirectory(TIFF* tif, uint64* pdiroff)
+int TIFFWriteCustomDirectory(TIFF *tif, uint64_t *pdiroff)
 {
-	return TIFFWriteDirectorySec(tif,FALSE,FALSE,pdiroff);
+    return TIFFWriteDirectorySec(tif, FALSE, FALSE, pdiroff);
 }
 
 /*
  * Similar to TIFFWriteDirectory(), but if the directory has already
  * been written once, it is relocated to the end of the file, in case it
  * has changed in size.  Note that this will result in the loss of the
- * previously used directory space. 
- */ 
-int
-TIFFRewriteDirectory( TIFF *tif )
+ * previously used directory space.
+ */
+int TIFFRewriteDirectory(TIFF *tif)
 {
-	static const char module[] = "TIFFRewriteDirectory";
+    static const char module[] = "TIFFRewriteDirectory";
 
-	/* We don't need to do anything special if it hasn't been written. */
-	if( tif->tif_diroff == 0 )
-		return TIFFWriteDirectory( tif );
+    /* We don't need to do anything special if it hasn't been written. */
+    if (tif->tif_diroff == 0)
+        return TIFFWriteDirectory(tif);
 
-	/*
-	 * Find and zero the pointer to this directory, so that TIFFLinkDirectory
-	 * will cause it to be added after this directories current pre-link.
-	 */
+    /*
+     * 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))
-	{
-		if (tif->tif_header.classic.tiff_diroff == tif->tif_diroff)
-		{
-			tif->tif_header.classic.tiff_diroff = 0;
-			tif->tif_diroff = 0;
+    if (!(tif->tif_flags & TIFF_BIGTIFF))
+    {
+        if (tif->tif_header.classic.tiff_diroff == tif->tif_diroff)
+        {
+            tif->tif_header.classic.tiff_diroff = 0;
+            tif->tif_diroff = 0;
 
-			TIFFSeekFile(tif,4,SEEK_SET);
-			if (!WriteOK(tif, &(tif->tif_header.classic.tiff_diroff),4))
-			{
-				TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-				    "Error updating TIFF header");
-				return (0);
-			}
-		}
-		else
-		{
-			uint32 nextdir;
-			nextdir = tif->tif_header.classic.tiff_diroff;
-			while(1) {
-				uint16 dircount;
-				uint32 nextnextdir;
+            TIFFSeekFile(tif, 4, SEEK_SET);
+            if (!WriteOK(tif, &(tif->tif_header.classic.tiff_diroff), 4))
+            {
+                TIFFErrorExtR(tif, tif->tif_name, "Error updating TIFF header");
+                return (0);
+            }
+        }
+        else if (tif->tif_diroff > 0xFFFFFFFFU)
+        {
+            TIFFErrorExtR(tif, module,
+                          "tif->tif_diroff exceeds 32 bit range allowed for "
+                          "Classic TIFF");
+            return (0);
+        }
+        else
+        {
+            uint32_t nextdir;
+            nextdir = tif->tif_header.classic.tiff_diroff;
+            while (1)
+            {
+                uint16_t dircount;
+                uint32_t nextnextdir;
 
-				if (!SeekOK(tif, nextdir) ||
-				    !ReadOK(tif, &dircount, 2)) {
-					TIFFErrorExt(tif->tif_clientdata, module,
-					     "Error fetching directory count");
-					return (0);
-				}
-				if (tif->tif_flags & TIFF_SWAB)
-					TIFFSwabShort(&dircount);
-				(void) TIFFSeekFile(tif,
-				    nextdir+2+dircount*12, SEEK_SET);
-				if (!ReadOK(tif, &nextnextdir, 4)) {
-					TIFFErrorExt(tif->tif_clientdata, module,
-					     "Error fetching directory link");
-					return (0);
-				}
-				if (tif->tif_flags & TIFF_SWAB)
-					TIFFSwabLong(&nextnextdir);
-				if (nextnextdir==tif->tif_diroff)
-				{
-					uint32 m;
-					m=0;
-					(void) TIFFSeekFile(tif,
-					    nextdir+2+dircount*12, SEEK_SET);
-					if (!WriteOK(tif, &m, 4)) {
-						TIFFErrorExt(tif->tif_clientdata, module,
-						     "Error writing directory link");
-						return (0);
-					}
-					tif->tif_diroff=0;
-					break;
-				}
-				nextdir=nextnextdir;
-			}
-		}
-	}
-	else
-	{
-		if (tif->tif_header.big.tiff_diroff == tif->tif_diroff)
-		{
-			tif->tif_header.big.tiff_diroff = 0;
-			tif->tif_diroff = 0;
+                if (!SeekOK(tif, nextdir) || !ReadOK(tif, &dircount, 2))
+                {
+                    TIFFErrorExtR(tif, module,
+                                  "Error fetching directory count");
+                    return (0);
+                }
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabShort(&dircount);
+                (void)TIFFSeekFile(tif, nextdir + 2 + dircount * 12, SEEK_SET);
+                if (!ReadOK(tif, &nextnextdir, 4))
+                {
+                    TIFFErrorExtR(tif, module, "Error fetching directory link");
+                    return (0);
+                }
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong(&nextnextdir);
+                if (nextnextdir == tif->tif_diroff)
+                {
+                    uint32_t m;
+                    m = 0;
+                    (void)TIFFSeekFile(tif, nextdir + 2 + dircount * 12,
+                                       SEEK_SET);
+                    if (!WriteOK(tif, &m, 4))
+                    {
+                        TIFFErrorExtR(tif, module,
+                                      "Error writing directory link");
+                        return (0);
+                    }
+                    tif->tif_diroff = 0;
+                    /* Force a full-traversal to reach the zeroed pointer */
+                    tif->tif_lastdiroff = 0;
+                    break;
+                }
+                nextdir = nextnextdir;
+            }
+        }
+        /* Remove skipped offset from IFD loop directory list. */
+        _TIFFRemoveEntryFromDirectoryListByOffset(tif, torewritediroff);
+    }
+    else
+    {
+        if (tif->tif_header.big.tiff_diroff == tif->tif_diroff)
+        {
+            tif->tif_header.big.tiff_diroff = 0;
+            tif->tif_diroff = 0;
 
-			TIFFSeekFile(tif,8,SEEK_SET);
-			if (!WriteOK(tif, &(tif->tif_header.big.tiff_diroff),8))
-			{
-				TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-				    "Error updating TIFF header");
-				return (0);
-			}
-		}
-		else
-		{
-			uint64 nextdir;
-			nextdir = tif->tif_header.big.tiff_diroff;
-			while(1) {
-				uint64 dircount64;
-				uint16 dircount;
-				uint64 nextnextdir;
+            TIFFSeekFile(tif, 8, SEEK_SET);
+            if (!WriteOK(tif, &(tif->tif_header.big.tiff_diroff), 8))
+            {
+                TIFFErrorExtR(tif, tif->tif_name, "Error updating TIFF header");
+                return (0);
+            }
+        }
+        else
+        {
+            uint64_t nextdir;
+            nextdir = tif->tif_header.big.tiff_diroff;
+            while (1)
+            {
+                uint64_t dircount64;
+                uint16_t dircount;
+                uint64_t nextnextdir;
 
-				if (!SeekOK(tif, nextdir) ||
-				    !ReadOK(tif, &dircount64, 8)) {
-					TIFFErrorExt(tif->tif_clientdata, module,
-					     "Error fetching directory count");
-					return (0);
-				}
-				if (tif->tif_flags & TIFF_SWAB)
-					TIFFSwabLong8(&dircount64);
-				if (dircount64>0xFFFF)
-				{
-					TIFFErrorExt(tif->tif_clientdata, module,
-					     "Sanity check on tag count failed, likely corrupt TIFF");
-					return (0);
-				}
-				dircount=(uint16)dircount64;
-				(void) TIFFSeekFile(tif,
-				    nextdir+8+dircount*20, SEEK_SET);
-				if (!ReadOK(tif, &nextnextdir, 8)) {
-					TIFFErrorExt(tif->tif_clientdata, module,
-					     "Error fetching directory link");
-					return (0);
-				}
-				if (tif->tif_flags & TIFF_SWAB)
-					TIFFSwabLong8(&nextnextdir);
-				if (nextnextdir==tif->tif_diroff)
-				{
-					uint64 m;
-					m=0;
-					(void) TIFFSeekFile(tif,
-					    nextdir+8+dircount*20, SEEK_SET);
-					if (!WriteOK(tif, &m, 8)) {
-						TIFFErrorExt(tif->tif_clientdata, module,
-						     "Error writing directory link");
-						return (0);
-					}
-					tif->tif_diroff=0;
-					break;
-				}
-				nextdir=nextnextdir;
-			}
-		}
-	}
+                if (!SeekOK(tif, nextdir) || !ReadOK(tif, &dircount64, 8))
+                {
+                    TIFFErrorExtR(tif, module,
+                                  "Error fetching directory count");
+                    return (0);
+                }
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8(&dircount64);
+                if (dircount64 > 0xFFFF)
+                {
+                    TIFFErrorExtR(tif, module,
+                                  "Sanity check on tag count failed, likely "
+                                  "corrupt TIFF");
+                    return (0);
+                }
+                dircount = (uint16_t)dircount64;
+                (void)TIFFSeekFile(tif, nextdir + 8 + dircount * 20, SEEK_SET);
+                if (!ReadOK(tif, &nextnextdir, 8))
+                {
+                    TIFFErrorExtR(tif, module, "Error fetching directory link");
+                    return (0);
+                }
+                if (tif->tif_flags & TIFF_SWAB)
+                    TIFFSwabLong8(&nextnextdir);
+                if (nextnextdir == tif->tif_diroff)
+                {
+                    uint64_t m;
+                    m = 0;
+                    (void)TIFFSeekFile(tif, nextdir + 8 + dircount * 20,
+                                       SEEK_SET);
+                    if (!WriteOK(tif, &m, 8))
+                    {
+                        TIFFErrorExtR(tif, module,
+                                      "Error writing directory link");
+                        return (0);
+                    }
+                    tif->tif_diroff = 0;
+                    /* Force a full-traversal to reach the zeroed pointer */
+                    tif->tif_lastdiroff = 0;
+                    break;
+                }
+                nextdir = nextnextdir;
+            }
+        }
+        /* Remove skipped offset from IFD loop directory list. */
+        _TIFFRemoveEntryFromDirectoryListByOffset(tif, torewritediroff);
+    }
 
-	/*
-	 * Now use TIFFWriteDirectory() normally.
-	 */
+    /*
+     * Now use TIFFWriteDirectory() normally.
+     */
 
-	return TIFFWriteDirectory( tif );
+    return TIFFWriteDirectory(tif);
 }
 
-static int
-TIFFWriteDirectorySec(TIFF* tif, int isimage, int imagedone, uint64* pdiroff)
+static int TIFFWriteDirectorySec(TIFF *tif, int isimage, int imagedone,
+                                 uint64_t *pdiroff)
 {
-	static const char module[] = "TIFFWriteDirectorySec";
-	uint32 ndir;
-	TIFFDirEntry* dir;
-	uint32 dirsize;
-	void* dirmem;
-	uint32 m;
-	if (tif->tif_mode == O_RDONLY)
-		return (1);
+    static const char module[] = "TIFFWriteDirectorySec";
+    uint32_t ndir;
+    TIFFDirEntry *dir;
+    uint32_t dirsize;
+    void *dirmem;
+    uint32_t m;
+    if (tif->tif_mode == O_RDONLY)
+        return (1);
 
-        _TIFFFillStriles( tif );
-        
-	/*
-	 * Clear write state so that subsequent images with
-	 * different characteristics get the right buffers
-	 * setup for them.
-	 */
-	if (imagedone)
-	{
-		if (tif->tif_flags & TIFF_POSTENCODE)
-		{
-			tif->tif_flags &= ~TIFF_POSTENCODE;
-			if (!(*tif->tif_postencode)(tif))
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,
-				    "Error post-encoding before directory write");
-				return (0);
-			}
-		}
-		(*tif->tif_close)(tif);       /* shutdown encoder */
-		/*
-		 * Flush any data that might have been written
-		 * by the compression close+cleanup routines.  But
-                 * be careful not to write stuff if we didn't add data
-                 * in the previous steps as the "rawcc" data may well be
-                 * a previously read tile/strip in mixed read/write mode.
-		 */
-		if (tif->tif_rawcc > 0 
-		    && (tif->tif_flags & TIFF_BEENWRITING) != 0 )
-		{
-		    if( !TIFFFlushData1(tif) )
-                    {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Error flushing data before directory write");
-			return (0);
-                    }
-		}
-		if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata)
-		{
-			_TIFFfree(tif->tif_rawdata);
-			tif->tif_rawdata = NULL;
-			tif->tif_rawcc = 0;
-			tif->tif_rawdatasize = 0;
-                        tif->tif_rawdataoff = 0;
-                        tif->tif_rawdataloaded = 0;
-		}
-		tif->tif_flags &= ~(TIFF_BEENWRITING|TIFF_BUFFERSETUP);
-	}
-	dir=NULL;
-	dirmem=NULL;
-	dirsize=0;
-	while (1)
-	{
-		ndir=0;
-		if (isimage)
-		{
-			if (TIFFFieldSet(tif,FIELD_IMAGEDIMENSIONS))
-			{
-				if (!TIFFWriteDirectoryTagShortLong(tif,&ndir,dir,TIFFTAG_IMAGEWIDTH,tif->tif_dir.td_imagewidth))
-					goto bad;
-				if (!TIFFWriteDirectoryTagShortLong(tif,&ndir,dir,TIFFTAG_IMAGELENGTH,tif->tif_dir.td_imagelength))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_TILEDIMENSIONS))
-			{
-				if (!TIFFWriteDirectoryTagShortLong(tif,&ndir,dir,TIFFTAG_TILEWIDTH,tif->tif_dir.td_tilewidth))
-					goto bad;
-				if (!TIFFWriteDirectoryTagShortLong(tif,&ndir,dir,TIFFTAG_TILELENGTH,tif->tif_dir.td_tilelength))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_RESOLUTION))
-			{
-				if (!TIFFWriteDirectoryTagRational(tif,&ndir,dir,TIFFTAG_XRESOLUTION,tif->tif_dir.td_xresolution))
-					goto bad;
-				if (!TIFFWriteDirectoryTagRational(tif,&ndir,dir,TIFFTAG_YRESOLUTION,tif->tif_dir.td_yresolution))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_POSITION))
-			{
-				if (!TIFFWriteDirectoryTagRational(tif,&ndir,dir,TIFFTAG_XPOSITION,tif->tif_dir.td_xposition))
-					goto bad;
-				if (!TIFFWriteDirectoryTagRational(tif,&ndir,dir,TIFFTAG_YPOSITION,tif->tif_dir.td_yposition))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_SUBFILETYPE))
-			{
-				if (!TIFFWriteDirectoryTagLong(tif,&ndir,dir,TIFFTAG_SUBFILETYPE,tif->tif_dir.td_subfiletype))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_BITSPERSAMPLE))
-			{
-				if (!TIFFWriteDirectoryTagShortPerSample(tif,&ndir,dir,TIFFTAG_BITSPERSAMPLE,tif->tif_dir.td_bitspersample))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_COMPRESSION))
-			{
-				if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_COMPRESSION,tif->tif_dir.td_compression))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_PHOTOMETRIC))
-			{
-				if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_PHOTOMETRIC,tif->tif_dir.td_photometric))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_THRESHHOLDING))
-			{
-				if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_THRESHHOLDING,tif->tif_dir.td_threshholding))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_FILLORDER))
-			{
-				if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_FILLORDER,tif->tif_dir.td_fillorder))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_ORIENTATION))
-			{
-				if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_ORIENTATION,tif->tif_dir.td_orientation))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_SAMPLESPERPIXEL))
-			{
-				if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_SAMPLESPERPIXEL,tif->tif_dir.td_samplesperpixel))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_ROWSPERSTRIP))
-			{
-				if (!TIFFWriteDirectoryTagShortLong(tif,&ndir,dir,TIFFTAG_ROWSPERSTRIP,tif->tif_dir.td_rowsperstrip))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_MINSAMPLEVALUE))
-			{
-				if (!TIFFWriteDirectoryTagShortPerSample(tif,&ndir,dir,TIFFTAG_MINSAMPLEVALUE,tif->tif_dir.td_minsamplevalue))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_MAXSAMPLEVALUE))
-			{
-				if (!TIFFWriteDirectoryTagShortPerSample(tif,&ndir,dir,TIFFTAG_MAXSAMPLEVALUE,tif->tif_dir.td_maxsamplevalue))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_PLANARCONFIG))
-			{
-				if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_PLANARCONFIG,tif->tif_dir.td_planarconfig))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_RESOLUTIONUNIT))
-			{
-				if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_RESOLUTIONUNIT,tif->tif_dir.td_resolutionunit))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_PAGENUMBER))
-			{
-				if (!TIFFWriteDirectoryTagShortArray(tif,&ndir,dir,TIFFTAG_PAGENUMBER,2,&tif->tif_dir.td_pagenumber[0]))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_STRIPBYTECOUNTS))
-			{
-				if (!isTiled(tif))
-				{
-					if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_STRIPBYTECOUNTS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripbytecount_p))
-						goto bad;
-				}
-				else
-				{
-					if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_TILEBYTECOUNTS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripbytecount_p))
-						goto bad;
-				}
-			}
-			if (TIFFFieldSet(tif,FIELD_STRIPOFFSETS))
-			{
-				if (!isTiled(tif))
-				{
+    _TIFFFillStriles(tif);
+
+    /*
+     * Clear write state so that subsequent images with
+     * different characteristics get the right buffers
+     * setup for them.
+     */
+    if (imagedone)
+    {
+        if (tif->tif_flags & TIFF_POSTENCODE)
+        {
+            tif->tif_flags &= ~TIFF_POSTENCODE;
+            if (!(*tif->tif_postencode)(tif))
+            {
+                TIFFErrorExtR(tif, module,
+                              "Error post-encoding before directory write");
+                return (0);
+            }
+        }
+        (*tif->tif_close)(tif); /* shutdown encoder */
+        /*
+         * Flush any data that might have been written
+         * by the compression close+cleanup routines.  But
+         * be careful not to write stuff if we didn't add data
+         * in the previous steps as the "rawcc" data may well be
+         * a previously read tile/strip in mixed read/write mode.
+         */
+        if (tif->tif_rawcc > 0 && (tif->tif_flags & TIFF_BEENWRITING) != 0)
+        {
+            if (!TIFFFlushData1(tif))
+            {
+                TIFFErrorExtR(tif, module,
+                              "Error flushing data before directory write");
+                return (0);
+            }
+        }
+        if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata)
+        {
+            _TIFFfreeExt(tif, tif->tif_rawdata);
+            tif->tif_rawdata = NULL;
+            tif->tif_rawcc = 0;
+            tif->tif_rawdatasize = 0;
+            tif->tif_rawdataoff = 0;
+            tif->tif_rawdataloaded = 0;
+        }
+        tif->tif_flags &= ~(TIFF_BEENWRITING | TIFF_BUFFERSETUP);
+    }
+
+    if (TIFFFieldSet(tif, FIELD_COMPRESSION) &&
+        (tif->tif_dir.td_compression == COMPRESSION_DEFLATE))
+    {
+        TIFFWarningExtR(tif, module,
+                        "Creating TIFF with legacy Deflate codec identifier, "
+                        "COMPRESSION_ADOBE_DEFLATE is more widely supported");
+    }
+    dir = NULL;
+    dirmem = NULL;
+    dirsize = 0;
+    while (1)
+    {
+        ndir = 0;
+        if (isimage)
+        {
+            if (TIFFFieldSet(tif, FIELD_IMAGEDIMENSIONS))
+            {
+                if (!TIFFWriteDirectoryTagShortLong(tif, &ndir, dir,
+                                                    TIFFTAG_IMAGEWIDTH,
+                                                    tif->tif_dir.td_imagewidth))
+                    goto bad;
+                if (!TIFFWriteDirectoryTagShortLong(
+                        tif, &ndir, dir, TIFFTAG_IMAGELENGTH,
+                        tif->tif_dir.td_imagelength))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_TILEDIMENSIONS))
+            {
+                if (!TIFFWriteDirectoryTagShortLong(tif, &ndir, dir,
+                                                    TIFFTAG_TILEWIDTH,
+                                                    tif->tif_dir.td_tilewidth))
+                    goto bad;
+                if (!TIFFWriteDirectoryTagShortLong(tif, &ndir, dir,
+                                                    TIFFTAG_TILELENGTH,
+                                                    tif->tif_dir.td_tilelength))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_RESOLUTION))
+            {
+                if (!TIFFWriteDirectoryTagRational(tif, &ndir, dir,
+                                                   TIFFTAG_XRESOLUTION,
+                                                   tif->tif_dir.td_xresolution))
+                    goto bad;
+                if (!TIFFWriteDirectoryTagRational(tif, &ndir, dir,
+                                                   TIFFTAG_YRESOLUTION,
+                                                   tif->tif_dir.td_yresolution))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_POSITION))
+            {
+                if (!TIFFWriteDirectoryTagRational(tif, &ndir, dir,
+                                                   TIFFTAG_XPOSITION,
+                                                   tif->tif_dir.td_xposition))
+                    goto bad;
+                if (!TIFFWriteDirectoryTagRational(tif, &ndir, dir,
+                                                   TIFFTAG_YPOSITION,
+                                                   tif->tif_dir.td_yposition))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_SUBFILETYPE))
+            {
+                if (!TIFFWriteDirectoryTagLong(tif, &ndir, dir,
+                                               TIFFTAG_SUBFILETYPE,
+                                               tif->tif_dir.td_subfiletype))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_BITSPERSAMPLE))
+            {
+                if (!TIFFWriteDirectoryTagShortPerSample(
+                        tif, &ndir, dir, TIFFTAG_BITSPERSAMPLE,
+                        tif->tif_dir.td_bitspersample))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_COMPRESSION))
+            {
+                if (!TIFFWriteDirectoryTagShort(tif, &ndir, dir,
+                                                TIFFTAG_COMPRESSION,
+                                                tif->tif_dir.td_compression))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_PHOTOMETRIC))
+            {
+                if (!TIFFWriteDirectoryTagShort(tif, &ndir, dir,
+                                                TIFFTAG_PHOTOMETRIC,
+                                                tif->tif_dir.td_photometric))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_THRESHHOLDING))
+            {
+                if (!TIFFWriteDirectoryTagShort(tif, &ndir, dir,
+                                                TIFFTAG_THRESHHOLDING,
+                                                tif->tif_dir.td_threshholding))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_FILLORDER))
+            {
+                if (!TIFFWriteDirectoryTagShort(tif, &ndir, dir,
+                                                TIFFTAG_FILLORDER,
+                                                tif->tif_dir.td_fillorder))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_ORIENTATION))
+            {
+                if (!TIFFWriteDirectoryTagShort(tif, &ndir, dir,
+                                                TIFFTAG_ORIENTATION,
+                                                tif->tif_dir.td_orientation))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_SAMPLESPERPIXEL))
+            {
+                if (!TIFFWriteDirectoryTagShort(
+                        tif, &ndir, dir, TIFFTAG_SAMPLESPERPIXEL,
+                        tif->tif_dir.td_samplesperpixel))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_ROWSPERSTRIP))
+            {
+                if (!TIFFWriteDirectoryTagShortLong(
+                        tif, &ndir, dir, TIFFTAG_ROWSPERSTRIP,
+                        tif->tif_dir.td_rowsperstrip))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_MINSAMPLEVALUE))
+            {
+                if (!TIFFWriteDirectoryTagShortPerSample(
+                        tif, &ndir, dir, TIFFTAG_MINSAMPLEVALUE,
+                        tif->tif_dir.td_minsamplevalue))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_MAXSAMPLEVALUE))
+            {
+                if (!TIFFWriteDirectoryTagShortPerSample(
+                        tif, &ndir, dir, TIFFTAG_MAXSAMPLEVALUE,
+                        tif->tif_dir.td_maxsamplevalue))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_PLANARCONFIG))
+            {
+                if (!TIFFWriteDirectoryTagShort(tif, &ndir, dir,
+                                                TIFFTAG_PLANARCONFIG,
+                                                tif->tif_dir.td_planarconfig))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_RESOLUTIONUNIT))
+            {
+                if (!TIFFWriteDirectoryTagShort(tif, &ndir, dir,
+                                                TIFFTAG_RESOLUTIONUNIT,
+                                                tif->tif_dir.td_resolutionunit))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_PAGENUMBER))
+            {
+                if (!TIFFWriteDirectoryTagShortArray(
+                        tif, &ndir, dir, TIFFTAG_PAGENUMBER, 2,
+                        &tif->tif_dir.td_pagenumber[0]))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_STRIPBYTECOUNTS))
+            {
+                if (!isTiled(tif))
+                {
+                    if (!TIFFWriteDirectoryTagLongLong8Array(
+                            tif, &ndir, dir, TIFFTAG_STRIPBYTECOUNTS,
+                            tif->tif_dir.td_nstrips,
+                            tif->tif_dir.td_stripbytecount_p))
+                        goto bad;
+                }
+                else
+                {
+                    if (!TIFFWriteDirectoryTagLongLong8Array(
+                            tif, &ndir, dir, TIFFTAG_TILEBYTECOUNTS,
+                            tif->tif_dir.td_nstrips,
+                            tif->tif_dir.td_stripbytecount_p))
+                        goto bad;
+                }
+            }
+            if (TIFFFieldSet(tif, FIELD_STRIPOFFSETS))
+            {
+                if (!isTiled(tif))
+                {
                     /* td_stripoffset_p might be NULL in an odd OJPEG case. See
                      *  tif_dirread.c around line 3634.
                      * XXX: OJPEG hack.
@@ -595,1121 +720,1108 @@
                      * JpegInterchangeFormat stream.
                      * We can get here when using tiffset on such a file.
                      * See http://bugzilla.maptools.org/show_bug.cgi?id=2500
-                    */
+                     */
                     if (tif->tif_dir.td_stripoffset_p != NULL &&
-                        !TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_STRIPOFFSETS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripoffset_p))
+                        !TIFFWriteDirectoryTagLongLong8Array(
+                            tif, &ndir, dir, TIFFTAG_STRIPOFFSETS,
+                            tif->tif_dir.td_nstrips,
+                            tif->tif_dir.td_stripoffset_p))
                         goto bad;
-				}
-				else
-				{
-					if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_TILEOFFSETS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripoffset_p))
-						goto bad;
-				}
-			}
-			if (TIFFFieldSet(tif,FIELD_COLORMAP))
-			{
-				if (!TIFFWriteDirectoryTagColormap(tif,&ndir,dir))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_EXTRASAMPLES))
-			{
-				if (tif->tif_dir.td_extrasamples)
-				{
-					uint16 na;
-					uint16* nb;
-					TIFFGetFieldDefaulted(tif,TIFFTAG_EXTRASAMPLES,&na,&nb);
-					if (!TIFFWriteDirectoryTagShortArray(tif,&ndir,dir,TIFFTAG_EXTRASAMPLES,na,nb))
-						goto bad;
-				}
-			}
-			if (TIFFFieldSet(tif,FIELD_SAMPLEFORMAT))
-			{
-				if (!TIFFWriteDirectoryTagShortPerSample(tif,&ndir,dir,TIFFTAG_SAMPLEFORMAT,tif->tif_dir.td_sampleformat))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_SMINSAMPLEVALUE))
-			{
-				if (!TIFFWriteDirectoryTagSampleformatArray(tif,&ndir,dir,TIFFTAG_SMINSAMPLEVALUE,tif->tif_dir.td_samplesperpixel,tif->tif_dir.td_sminsamplevalue))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_SMAXSAMPLEVALUE))
-			{
-				if (!TIFFWriteDirectoryTagSampleformatArray(tif,&ndir,dir,TIFFTAG_SMAXSAMPLEVALUE,tif->tif_dir.td_samplesperpixel,tif->tif_dir.td_smaxsamplevalue))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_IMAGEDEPTH))
-			{
-				if (!TIFFWriteDirectoryTagLong(tif,&ndir,dir,TIFFTAG_IMAGEDEPTH,tif->tif_dir.td_imagedepth))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_TILEDEPTH))
-			{
-				if (!TIFFWriteDirectoryTagLong(tif,&ndir,dir,TIFFTAG_TILEDEPTH,tif->tif_dir.td_tiledepth))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_HALFTONEHINTS))
-			{
-				if (!TIFFWriteDirectoryTagShortArray(tif,&ndir,dir,TIFFTAG_HALFTONEHINTS,2,&tif->tif_dir.td_halftonehints[0]))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_YCBCRSUBSAMPLING))
-			{
-				if (!TIFFWriteDirectoryTagShortArray(tif,&ndir,dir,TIFFTAG_YCBCRSUBSAMPLING,2,&tif->tif_dir.td_ycbcrsubsampling[0]))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_YCBCRPOSITIONING))
-			{
-				if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_YCBCRPOSITIONING,tif->tif_dir.td_ycbcrpositioning))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_REFBLACKWHITE))
-			{
-				if (!TIFFWriteDirectoryTagRationalArray(tif,&ndir,dir,TIFFTAG_REFERENCEBLACKWHITE,6,tif->tif_dir.td_refblackwhite))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_TRANSFERFUNCTION))
-			{
-				if (!TIFFWriteDirectoryTagTransferfunction(tif,&ndir,dir))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_INKNAMES))
-			{
-				if (!TIFFWriteDirectoryTagAscii(tif,&ndir,dir,TIFFTAG_INKNAMES,tif->tif_dir.td_inknameslen,tif->tif_dir.td_inknames))
-					goto bad;
-			}
-			if (TIFFFieldSet(tif,FIELD_SUBIFD))
-			{
-				if (!TIFFWriteDirectoryTagSubifd(tif,&ndir,dir))
-					goto bad;
-			}
-			{
-				uint32 n;
-				for (n=0; n<tif->tif_nfields; n++) {
-					const TIFFField* o;
-					o = tif->tif_fields[n];
-					if ((o->field_bit>=FIELD_CODEC)&&(TIFFFieldSet(tif,o->field_bit)))
-					{
-						switch (o->get_field_type)
-						{
-							case TIFF_SETGET_ASCII:
-								{
-									uint32 pa;
-									char* pb;
-									assert(o->field_type==TIFF_ASCII);
-									assert(o->field_readcount==TIFF_VARIABLE);
-									assert(o->field_passcount==0);
-									TIFFGetField(tif,o->field_tag,&pb);
-									pa=(uint32)(strlen(pb));
-									if (!TIFFWriteDirectoryTagAscii(tif,&ndir,dir,(uint16)o->field_tag,pa,pb))
-										goto bad;
-								}
-								break;
-							case TIFF_SETGET_UINT16:
-								{
-									uint16 p;
-									assert(o->field_type==TIFF_SHORT);
-									assert(o->field_readcount==1);
-									assert(o->field_passcount==0);
-									TIFFGetField(tif,o->field_tag,&p);
-									if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,(uint16)o->field_tag,p))
-										goto bad;
-								}
-								break;
-							case TIFF_SETGET_UINT32:
-								{
-									uint32 p;
-									assert(o->field_type==TIFF_LONG);
-									assert(o->field_readcount==1);
-									assert(o->field_passcount==0);
-									TIFFGetField(tif,o->field_tag,&p);
-									if (!TIFFWriteDirectoryTagLong(tif,&ndir,dir,(uint16)o->field_tag,p))
-										goto bad;
-								}
-								break;
-							case TIFF_SETGET_C32_UINT8:
-								{
-									uint32 pa;
-									void* pb;
-									assert(o->field_type==TIFF_UNDEFINED);
-									assert(o->field_readcount==TIFF_VARIABLE2);
-									assert(o->field_passcount==1);
-									TIFFGetField(tif,o->field_tag,&pa,&pb);
-									if (!TIFFWriteDirectoryTagUndefinedArray(tif,&ndir,dir,(uint16)o->field_tag,pa,pb))
-										goto bad;
-								}
-								break;
-							default:
-								TIFFErrorExt(tif->tif_clientdata,module,
-								            "Cannot write tag %d (%s)",
-								            TIFFFieldTag(o),
-                                                                            o->field_name ? o->field_name : "unknown");
-								goto bad;
-						}
-					}
-				}
-			}
-		}
-		for (m=0; m<(uint32)(tif->tif_dir.td_customValueCount); m++)
-		{
-                        uint16 tag = (uint16)tif->tif_dir.td_customValues[m].info->field_tag;
-                        uint32 count = tif->tif_dir.td_customValues[m].count;
-			switch (tif->tif_dir.td_customValues[m].info->field_type)
-			{
-				case TIFF_ASCII:
-					if (!TIFFWriteDirectoryTagAscii(tif,&ndir,dir,tag,count,tif->tif_dir.td_customValues[m].value))
-						goto bad;
-					break;
-				case TIFF_UNDEFINED:
-					if (!TIFFWriteDirectoryTagUndefinedArray(tif,&ndir,dir,tag,count,tif->tif_dir.td_customValues[m].value))
-						goto bad;
-					break;
-				case TIFF_BYTE:
-					if (!TIFFWriteDirectoryTagByteArray(tif,&ndir,dir,tag,count,tif->tif_dir.td_customValues[m].value))
-						goto bad;
-					break;
-				case TIFF_SBYTE:
-					if (!TIFFWriteDirectoryTagSbyteArray(tif,&ndir,dir,tag,count,tif->tif_dir.td_customValues[m].value))
-						goto bad;
-					break;
-				case TIFF_SHORT:
-					if (!TIFFWriteDirectoryTagShortArray(tif,&ndir,dir,tag,count,tif->tif_dir.td_customValues[m].value))
-						goto bad;
-					break;
-				case TIFF_SSHORT:
-					if (!TIFFWriteDirectoryTagSshortArray(tif,&ndir,dir,tag,count,tif->tif_dir.td_customValues[m].value))
-						goto bad;
-					break;
-				case TIFF_LONG:
-					if (!TIFFWriteDirectoryTagLongArray(tif,&ndir,dir,tag,count,tif->tif_dir.td_customValues[m].value))
-						goto bad;
-					break;
-				case TIFF_SLONG:
-					if (!TIFFWriteDirectoryTagSlongArray(tif,&ndir,dir,tag,count,tif->tif_dir.td_customValues[m].value))
-						goto bad;
-					break;
-				case TIFF_LONG8:
-					if (!TIFFWriteDirectoryTagLong8Array(tif,&ndir,dir,tag,count,tif->tif_dir.td_customValues[m].value))
-						goto bad;
-					break;
-				case TIFF_SLONG8:
-					if (!TIFFWriteDirectoryTagSlong8Array(tif,&ndir,dir,tag,count,tif->tif_dir.td_customValues[m].value))
-						goto bad;
-					break;
-				case TIFF_RATIONAL:
-					if (!TIFFWriteDirectoryTagRationalArray(tif,&ndir,dir,tag,count,tif->tif_dir.td_customValues[m].value))
-						goto bad;
-					break;
-				case TIFF_SRATIONAL:
-					if (!TIFFWriteDirectoryTagSrationalArray(tif,&ndir,dir,tag,count,tif->tif_dir.td_customValues[m].value))
-						goto bad;
-					break;
-				case TIFF_FLOAT:
-					if (!TIFFWriteDirectoryTagFloatArray(tif,&ndir,dir,tag,count,tif->tif_dir.td_customValues[m].value))
-						goto bad;
-					break;
-				case TIFF_DOUBLE:
-					if (!TIFFWriteDirectoryTagDoubleArray(tif,&ndir,dir,tag,count,tif->tif_dir.td_customValues[m].value))
-						goto bad;
-					break;
-				case TIFF_IFD:
-					if (!TIFFWriteDirectoryTagIfdArray(tif,&ndir,dir,tag,count,tif->tif_dir.td_customValues[m].value))
-						goto bad;
-					break;
-				case TIFF_IFD8:
-					if (!TIFFWriteDirectoryTagIfdIfd8Array(tif,&ndir,dir,tag,count,tif->tif_dir.td_customValues[m].value))
-						goto bad;
-					break;
-				default:
-					assert(0);   /* we should never get here */
-					break;
-			}
-		}
-		if (dir!=NULL)
-			break;
-		dir=_TIFFmalloc(ndir*sizeof(TIFFDirEntry));
-		if (dir==NULL)
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-			goto bad;
-		}
-		if (isimage)
-		{
-			if ((tif->tif_diroff==0)&&(!TIFFLinkDirectory(tif)))
-				goto bad;
-		}
-		else
-			tif->tif_diroff=(TIFFSeekFile(tif,0,SEEK_END)+1)&(~((toff_t)1));
-		if (pdiroff!=NULL)
-			*pdiroff=tif->tif_diroff;
-		if (!(tif->tif_flags&TIFF_BIGTIFF))
-			dirsize=2+ndir*12+4;
-		else
-			dirsize=8+ndir*20+8;
-		tif->tif_dataoff=tif->tif_diroff+dirsize;
-		if (!(tif->tif_flags&TIFF_BIGTIFF))
-			tif->tif_dataoff=(uint32)tif->tif_dataoff;
-		if ((tif->tif_dataoff<tif->tif_diroff)||(tif->tif_dataoff<(uint64)dirsize))
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"Maximum TIFF file size exceeded");
-			goto bad;
-		}
-		if (tif->tif_dataoff&1)
-			tif->tif_dataoff++;
-		if (isimage)
-			tif->tif_curdir++;
-	}
-	if (isimage)
-	{
-		if (TIFFFieldSet(tif,FIELD_SUBIFD)&&(tif->tif_subifdoff==0))
-		{
-			uint32 na;
-			TIFFDirEntry* nb;
-			for (na=0, nb=dir; ; na++, nb++)
-			{
-				if( na == ndir )
-                                {
-                                    TIFFErrorExt(tif->tif_clientdata,module,
-                                                 "Cannot find SubIFD tag");
+                }
+                else
+                {
+                    if (!TIFFWriteDirectoryTagLongLong8Array(
+                            tif, &ndir, dir, TIFFTAG_TILEOFFSETS,
+                            tif->tif_dir.td_nstrips,
+                            tif->tif_dir.td_stripoffset_p))
+                        goto bad;
+                }
+            }
+            if (TIFFFieldSet(tif, FIELD_COLORMAP))
+            {
+                if (!TIFFWriteDirectoryTagColormap(tif, &ndir, dir))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_EXTRASAMPLES))
+            {
+                if (tif->tif_dir.td_extrasamples)
+                {
+                    uint16_t na;
+                    uint16_t *nb;
+                    TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES, &na, &nb);
+                    if (!TIFFWriteDirectoryTagShortArray(
+                            tif, &ndir, dir, TIFFTAG_EXTRASAMPLES, na, nb))
+                        goto bad;
+                }
+            }
+            if (TIFFFieldSet(tif, FIELD_SAMPLEFORMAT))
+            {
+                if (!TIFFWriteDirectoryTagShortPerSample(
+                        tif, &ndir, dir, TIFFTAG_SAMPLEFORMAT,
+                        tif->tif_dir.td_sampleformat))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_SMINSAMPLEVALUE))
+            {
+                if (!TIFFWriteDirectoryTagSampleformatArray(
+                        tif, &ndir, dir, TIFFTAG_SMINSAMPLEVALUE,
+                        tif->tif_dir.td_samplesperpixel,
+                        tif->tif_dir.td_sminsamplevalue))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_SMAXSAMPLEVALUE))
+            {
+                if (!TIFFWriteDirectoryTagSampleformatArray(
+                        tif, &ndir, dir, TIFFTAG_SMAXSAMPLEVALUE,
+                        tif->tif_dir.td_samplesperpixel,
+                        tif->tif_dir.td_smaxsamplevalue))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_IMAGEDEPTH))
+            {
+                if (!TIFFWriteDirectoryTagLong(tif, &ndir, dir,
+                                               TIFFTAG_IMAGEDEPTH,
+                                               tif->tif_dir.td_imagedepth))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_TILEDEPTH))
+            {
+                if (!TIFFWriteDirectoryTagLong(tif, &ndir, dir,
+                                               TIFFTAG_TILEDEPTH,
+                                               tif->tif_dir.td_tiledepth))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_HALFTONEHINTS))
+            {
+                if (!TIFFWriteDirectoryTagShortArray(
+                        tif, &ndir, dir, TIFFTAG_HALFTONEHINTS, 2,
+                        &tif->tif_dir.td_halftonehints[0]))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_YCBCRSUBSAMPLING))
+            {
+                if (!TIFFWriteDirectoryTagShortArray(
+                        tif, &ndir, dir, TIFFTAG_YCBCRSUBSAMPLING, 2,
+                        &tif->tif_dir.td_ycbcrsubsampling[0]))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_YCBCRPOSITIONING))
+            {
+                if (!TIFFWriteDirectoryTagShort(
+                        tif, &ndir, dir, TIFFTAG_YCBCRPOSITIONING,
+                        tif->tif_dir.td_ycbcrpositioning))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_REFBLACKWHITE))
+            {
+                if (!TIFFWriteDirectoryTagRationalArray(
+                        tif, &ndir, dir, TIFFTAG_REFERENCEBLACKWHITE, 6,
+                        tif->tif_dir.td_refblackwhite))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_TRANSFERFUNCTION))
+            {
+                if (!TIFFWriteDirectoryTagTransferfunction(tif, &ndir, dir))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_INKNAMES))
+            {
+                if (!TIFFWriteDirectoryTagAscii(
+                        tif, &ndir, dir, TIFFTAG_INKNAMES,
+                        tif->tif_dir.td_inknameslen, tif->tif_dir.td_inknames))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_NUMBEROFINKS))
+            {
+                if (!TIFFWriteDirectoryTagShort(tif, &ndir, dir,
+                                                TIFFTAG_NUMBEROFINKS,
+                                                tif->tif_dir.td_numberofinks))
+                    goto bad;
+            }
+            if (TIFFFieldSet(tif, FIELD_SUBIFD))
+            {
+                if (!TIFFWriteDirectoryTagSubifd(tif, &ndir, dir))
+                    goto bad;
+            }
+            {
+                uint32_t n;
+                for (n = 0; n < tif->tif_nfields; n++)
+                {
+                    const TIFFField *o;
+                    o = tif->tif_fields[n];
+                    if ((o->field_bit >= FIELD_CODEC) &&
+                        (TIFFFieldSet(tif, o->field_bit)))
+                    {
+                        switch (o->get_field_type)
+                        {
+                            case TIFF_SETGET_ASCII:
+                            {
+                                uint32_t pa;
+                                char *pb;
+                                assert(o->field_type == TIFF_ASCII);
+                                assert(o->field_readcount == TIFF_VARIABLE);
+                                assert(o->field_passcount == 0);
+                                TIFFGetField(tif, o->field_tag, &pb);
+                                pa = (uint32_t)(strlen(pb));
+                                if (!TIFFWriteDirectoryTagAscii(
+                                        tif, &ndir, dir, (uint16_t)o->field_tag,
+                                        pa, pb))
                                     goto bad;
-                                }
-				if (nb->tdir_tag==TIFFTAG_SUBIFD)
-					break;
-			}
-			if (!(tif->tif_flags&TIFF_BIGTIFF))
-				tif->tif_subifdoff=tif->tif_diroff+2+na*12+8;
-			else
-				tif->tif_subifdoff=tif->tif_diroff+8+na*20+12;
-		}
-	}
-	dirmem=_TIFFmalloc(dirsize);
-	if (dirmem==NULL)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-		goto bad;
-	}
-	if (!(tif->tif_flags&TIFF_BIGTIFF))
-	{
-		uint8* n;
-		uint32 nTmp;
-		TIFFDirEntry* o;
-		n=dirmem;
-		*(uint16*)n=(uint16)ndir;
-		if (tif->tif_flags&TIFF_SWAB)
-			TIFFSwabShort((uint16*)n);
-		n+=2;
-		o=dir;
-		for (m=0; m<ndir; m++)
-		{
-			*(uint16*)n=o->tdir_tag;
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabShort((uint16*)n);
-			n+=2;
-			*(uint16*)n=o->tdir_type;
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabShort((uint16*)n);
-			n+=2;
-			nTmp = (uint32)o->tdir_count;
-			_TIFFmemcpy(n,&nTmp,4);
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabLong((uint32*)n);
-			n+=4;
-			/* This is correct. The data has been */
-			/* swabbed previously in TIFFWriteDirectoryTagData */
-			_TIFFmemcpy(n,&o->tdir_offset,4);
-			n+=4;
-			o++;
-		}
-		nTmp = (uint32)tif->tif_nextdiroff;
-		if (tif->tif_flags&TIFF_SWAB)
-			TIFFSwabLong(&nTmp);
-		_TIFFmemcpy(n,&nTmp,4);
-	}
-	else
-	{
-		uint8* n;
-		TIFFDirEntry* o;
-		n=dirmem;
-		*(uint64*)n=ndir;
-		if (tif->tif_flags&TIFF_SWAB)
-			TIFFSwabLong8((uint64*)n);
-		n+=8;
-		o=dir;
-		for (m=0; m<ndir; m++)
-		{
-			*(uint16*)n=o->tdir_tag;
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabShort((uint16*)n);
-			n+=2;
-			*(uint16*)n=o->tdir_type;
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabShort((uint16*)n);
-			n+=2;
-			_TIFFmemcpy(n,&o->tdir_count,8);
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabLong8((uint64*)n);
-			n+=8;
-			_TIFFmemcpy(n,&o->tdir_offset,8);
-			n+=8;
-			o++;
-		}
-		_TIFFmemcpy(n,&tif->tif_nextdiroff,8);
-		if (tif->tif_flags&TIFF_SWAB)
-			TIFFSwabLong8((uint64*)n);
-	}
-	_TIFFfree(dir);
-	dir=NULL;
-	if (!SeekOK(tif,tif->tif_diroff))
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"IO error writing directory");
-		goto bad;
-	}
-	if (!WriteOK(tif,dirmem,(tmsize_t)dirsize))
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"IO error writing directory");
-		goto bad;
-	}
-	_TIFFfree(dirmem);
-	if (imagedone)
-	{
-		TIFFFreeDirectory(tif);
-		tif->tif_flags &= ~TIFF_DIRTYDIRECT;
-		tif->tif_flags &= ~TIFF_DIRTYSTRIP;
-		(*tif->tif_cleanup)(tif);
-		/*
-		* Reset directory-related state for subsequent
-		* directories.
-		*/
-		TIFFCreateDirectory(tif);
-	}
-	return(1);
+                            }
+                            break;
+                            case TIFF_SETGET_UINT16:
+                            {
+                                uint16_t p;
+                                assert(o->field_type == TIFF_SHORT);
+                                assert(o->field_readcount == 1);
+                                assert(o->field_passcount == 0);
+                                TIFFGetField(tif, o->field_tag, &p);
+                                if (!TIFFWriteDirectoryTagShort(
+                                        tif, &ndir, dir, (uint16_t)o->field_tag,
+                                        p))
+                                    goto bad;
+                            }
+                            break;
+                            case TIFF_SETGET_UINT32:
+                            {
+                                uint32_t p;
+                                assert(o->field_type == TIFF_LONG);
+                                assert(o->field_readcount == 1);
+                                assert(o->field_passcount == 0);
+                                TIFFGetField(tif, o->field_tag, &p);
+                                if (!TIFFWriteDirectoryTagLong(
+                                        tif, &ndir, dir, (uint16_t)o->field_tag,
+                                        p))
+                                    goto bad;
+                            }
+                            break;
+                            case TIFF_SETGET_C32_UINT8:
+                            {
+                                uint32_t pa;
+                                void *pb;
+                                assert(o->field_type == TIFF_UNDEFINED);
+                                assert(o->field_readcount == TIFF_VARIABLE2);
+                                assert(o->field_passcount == 1);
+                                TIFFGetField(tif, o->field_tag, &pa, &pb);
+                                if (!TIFFWriteDirectoryTagUndefinedArray(
+                                        tif, &ndir, dir, (uint16_t)o->field_tag,
+                                        pa, pb))
+                                    goto bad;
+                            }
+                            break;
+                            default:
+                                TIFFErrorExtR(
+                                    tif, module,
+                                    "Cannot write tag %" PRIu32 " (%s)",
+                                    TIFFFieldTag(o),
+                                    o->field_name ? o->field_name : "unknown");
+                                goto bad;
+                        }
+                    }
+                }
+            }
+        }
+        for (m = 0; m < (uint32_t)(tif->tif_dir.td_customValueCount); m++)
+        {
+            uint16_t tag =
+                (uint16_t)tif->tif_dir.td_customValues[m].info->field_tag;
+            uint32_t count = tif->tif_dir.td_customValues[m].count;
+            switch (tif->tif_dir.td_customValues[m].info->field_type)
+            {
+                case TIFF_ASCII:
+                    if (!TIFFWriteDirectoryTagAscii(
+                            tif, &ndir, dir, tag, count,
+                            tif->tif_dir.td_customValues[m].value))
+                        goto bad;
+                    break;
+                case TIFF_UNDEFINED:
+                    if (!TIFFWriteDirectoryTagUndefinedArray(
+                            tif, &ndir, dir, tag, count,
+                            tif->tif_dir.td_customValues[m].value))
+                        goto bad;
+                    break;
+                case TIFF_BYTE:
+                    if (!TIFFWriteDirectoryTagByteArray(
+                            tif, &ndir, dir, tag, count,
+                            tif->tif_dir.td_customValues[m].value))
+                        goto bad;
+                    break;
+                case TIFF_SBYTE:
+                    if (!TIFFWriteDirectoryTagSbyteArray(
+                            tif, &ndir, dir, tag, count,
+                            tif->tif_dir.td_customValues[m].value))
+                        goto bad;
+                    break;
+                case TIFF_SHORT:
+                    if (!TIFFWriteDirectoryTagShortArray(
+                            tif, &ndir, dir, tag, count,
+                            tif->tif_dir.td_customValues[m].value))
+                        goto bad;
+                    break;
+                case TIFF_SSHORT:
+                    if (!TIFFWriteDirectoryTagSshortArray(
+                            tif, &ndir, dir, tag, count,
+                            tif->tif_dir.td_customValues[m].value))
+                        goto bad;
+                    break;
+                case TIFF_LONG:
+                    if (!TIFFWriteDirectoryTagLongArray(
+                            tif, &ndir, dir, tag, count,
+                            tif->tif_dir.td_customValues[m].value))
+                        goto bad;
+                    break;
+                case TIFF_SLONG:
+                    if (!TIFFWriteDirectoryTagSlongArray(
+                            tif, &ndir, dir, tag, count,
+                            tif->tif_dir.td_customValues[m].value))
+                        goto bad;
+                    break;
+                case TIFF_LONG8:
+                    if (!TIFFWriteDirectoryTagLong8Array(
+                            tif, &ndir, dir, tag, count,
+                            tif->tif_dir.td_customValues[m].value))
+                        goto bad;
+                    break;
+                case TIFF_SLONG8:
+                    if (!TIFFWriteDirectoryTagSlong8Array(
+                            tif, &ndir, dir, tag, count,
+                            tif->tif_dir.td_customValues[m].value))
+                        goto bad;
+                    break;
+                case TIFF_RATIONAL:
+                {
+                    /*-- Rational2Double: For Rationals evaluate
+                     * "set_field_type" to determine internal storage size. */
+                    int tv_size;
+                    tv_size = TIFFFieldSetGetSize(
+                        tif->tif_dir.td_customValues[m].info);
+                    if (tv_size == 8)
+                    {
+                        if (!TIFFWriteDirectoryTagRationalDoubleArray(
+                                tif, &ndir, dir, tag, count,
+                                tif->tif_dir.td_customValues[m].value))
+                            goto bad;
+                    }
+                    else
+                    {
+                        /*-- default should be tv_size == 4 */
+                        if (!TIFFWriteDirectoryTagRationalArray(
+                                tif, &ndir, dir, tag, count,
+                                tif->tif_dir.td_customValues[m].value))
+                            goto bad;
+                        /*-- ToDo: After Testing, this should be removed and
+                         * tv_size==4 should be set as default. */
+                        if (tv_size != 4)
+                        {
+                            TIFFErrorExtR(tif,
+                                          "TIFFLib: _TIFFWriteDirectorySec()",
+                                          "Rational2Double: .set_field_type is "
+                                          "not 4 but %d",
+                                          tv_size);
+                        }
+                    }
+                }
+                break;
+                case TIFF_SRATIONAL:
+                {
+                    /*-- Rational2Double: For Rationals evaluate
+                     * "set_field_type" to determine internal storage size. */
+                    int tv_size;
+                    tv_size = TIFFFieldSetGetSize(
+                        tif->tif_dir.td_customValues[m].info);
+                    if (tv_size == 8)
+                    {
+                        if (!TIFFWriteDirectoryTagSrationalDoubleArray(
+                                tif, &ndir, dir, tag, count,
+                                tif->tif_dir.td_customValues[m].value))
+                            goto bad;
+                    }
+                    else
+                    {
+                        /*-- default should be tv_size == 4 */
+                        if (!TIFFWriteDirectoryTagSrationalArray(
+                                tif, &ndir, dir, tag, count,
+                                tif->tif_dir.td_customValues[m].value))
+                            goto bad;
+                        /*-- ToDo: After Testing, this should be removed and
+                         * tv_size==4 should be set as default. */
+                        if (tv_size != 4)
+                        {
+                            TIFFErrorExtR(tif,
+                                          "TIFFLib: _TIFFWriteDirectorySec()",
+                                          "Rational2Double: .set_field_type is "
+                                          "not 4 but %d",
+                                          tv_size);
+                        }
+                    }
+                }
+                break;
+                case TIFF_FLOAT:
+                    if (!TIFFWriteDirectoryTagFloatArray(
+                            tif, &ndir, dir, tag, count,
+                            tif->tif_dir.td_customValues[m].value))
+                        goto bad;
+                    break;
+                case TIFF_DOUBLE:
+                    if (!TIFFWriteDirectoryTagDoubleArray(
+                            tif, &ndir, dir, tag, count,
+                            tif->tif_dir.td_customValues[m].value))
+                        goto bad;
+                    break;
+                case TIFF_IFD:
+                    if (!TIFFWriteDirectoryTagIfdArray(
+                            tif, &ndir, dir, tag, count,
+                            tif->tif_dir.td_customValues[m].value))
+                        goto bad;
+                    break;
+                case TIFF_IFD8:
+                    if (!TIFFWriteDirectoryTagIfdIfd8Array(
+                            tif, &ndir, dir, tag, count,
+                            tif->tif_dir.td_customValues[m].value))
+                        goto bad;
+                    break;
+                default:
+                    assert(0); /* we should never get here */
+                    break;
+            }
+        }
+        if (dir != NULL)
+            break;
+        dir = _TIFFmallocExt(tif, ndir * sizeof(TIFFDirEntry));
+        if (dir == NULL)
+        {
+            TIFFErrorExtR(tif, module, "Out of memory");
+            goto bad;
+        }
+        if (isimage)
+        {
+            if ((tif->tif_diroff == 0) && (!TIFFLinkDirectory(tif)))
+                goto bad;
+        }
+        else
+            tif->tif_diroff =
+                (TIFFSeekFile(tif, 0, SEEK_END) + 1) & (~((toff_t)1));
+        if (pdiroff != NULL)
+            *pdiroff = tif->tif_diroff;
+        if (!(tif->tif_flags & TIFF_BIGTIFF))
+            dirsize = 2 + ndir * 12 + 4;
+        else
+            dirsize = 8 + ndir * 20 + 8;
+        tif->tif_dataoff = tif->tif_diroff + dirsize;
+        if (!(tif->tif_flags & TIFF_BIGTIFF))
+            tif->tif_dataoff = (uint32_t)tif->tif_dataoff;
+        if ((tif->tif_dataoff < tif->tif_diroff) ||
+            (tif->tif_dataoff < (uint64_t)dirsize))
+        {
+            TIFFErrorExtR(tif, module, "Maximum TIFF file size exceeded");
+            goto bad;
+        }
+        if (tif->tif_dataoff & 1)
+            tif->tif_dataoff++;
+        if (isimage)
+        {
+            if (tif->tif_curdir == TIFF_NON_EXISTENT_DIR_NUMBER)
+                tif->tif_curdir = 0;
+            else
+                tif->tif_curdir++;
+        }
+    }
+    if (isimage)
+    {
+        if (TIFFFieldSet(tif, FIELD_SUBIFD) && (tif->tif_subifdoff == 0))
+        {
+            uint32_t na;
+            TIFFDirEntry *nb;
+            for (na = 0, nb = dir;; na++, nb++)
+            {
+                if (na == ndir)
+                {
+                    TIFFErrorExtR(tif, module, "Cannot find SubIFD tag");
+                    goto bad;
+                }
+                if (nb->tdir_tag == TIFFTAG_SUBIFD)
+                    break;
+            }
+            if (!(tif->tif_flags & TIFF_BIGTIFF))
+                tif->tif_subifdoff = tif->tif_diroff + 2 + na * 12 + 8;
+            else
+                tif->tif_subifdoff = tif->tif_diroff + 8 + na * 20 + 12;
+        }
+    }
+    dirmem = _TIFFmallocExt(tif, dirsize);
+    if (dirmem == NULL)
+    {
+        TIFFErrorExtR(tif, module, "Out of memory");
+        goto bad;
+    }
+    if (!(tif->tif_flags & TIFF_BIGTIFF))
+    {
+        uint8_t *n;
+        uint32_t nTmp;
+        TIFFDirEntry *o;
+        n = dirmem;
+        *(uint16_t *)n = (uint16_t)ndir;
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabShort((uint16_t *)n);
+        n += 2;
+        o = dir;
+        for (m = 0; m < ndir; m++)
+        {
+            *(uint16_t *)n = o->tdir_tag;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabShort((uint16_t *)n);
+            n += 2;
+            *(uint16_t *)n = o->tdir_type;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabShort((uint16_t *)n);
+            n += 2;
+            nTmp = (uint32_t)o->tdir_count;
+            _TIFFmemcpy(n, &nTmp, 4);
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong((uint32_t *)n);
+            n += 4;
+            /* This is correct. The data has been */
+            /* swabbed previously in TIFFWriteDirectoryTagData */
+            _TIFFmemcpy(n, &o->tdir_offset, 4);
+            n += 4;
+            o++;
+        }
+        nTmp = (uint32_t)tif->tif_nextdiroff;
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabLong(&nTmp);
+        _TIFFmemcpy(n, &nTmp, 4);
+    }
+    else
+    {
+        uint8_t *n;
+        TIFFDirEntry *o;
+        n = dirmem;
+        *(uint64_t *)n = ndir;
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabLong8((uint64_t *)n);
+        n += 8;
+        o = dir;
+        for (m = 0; m < ndir; m++)
+        {
+            *(uint16_t *)n = o->tdir_tag;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabShort((uint16_t *)n);
+            n += 2;
+            *(uint16_t *)n = o->tdir_type;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabShort((uint16_t *)n);
+            n += 2;
+            _TIFFmemcpy(n, &o->tdir_count, 8);
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong8((uint64_t *)n);
+            n += 8;
+            _TIFFmemcpy(n, &o->tdir_offset, 8);
+            n += 8;
+            o++;
+        }
+        _TIFFmemcpy(n, &tif->tif_nextdiroff, 8);
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabLong8((uint64_t *)n);
+    }
+    _TIFFfreeExt(tif, dir);
+    dir = NULL;
+    if (!SeekOK(tif, tif->tif_diroff))
+    {
+        TIFFErrorExtR(tif, module, "IO error writing directory");
+        goto bad;
+    }
+    if (!WriteOK(tif, dirmem, (tmsize_t)dirsize))
+    {
+        TIFFErrorExtR(tif, module, "IO error writing directory");
+        goto bad;
+    }
+    _TIFFfreeExt(tif, dirmem);
+    if (imagedone)
+    {
+        TIFFFreeDirectory(tif);
+        tif->tif_flags &= ~TIFF_DIRTYDIRECT;
+        tif->tif_flags &= ~TIFF_DIRTYSTRIP;
+        (*tif->tif_cleanup)(tif);
+        /*
+         * Reset directory-related state for subsequent
+         * directories.
+         */
+        TIFFCreateDirectory(tif);
+    }
+    return (1);
 bad:
-	if (dir!=NULL)
-		_TIFFfree(dir);
-	if (dirmem!=NULL)
-		_TIFFfree(dirmem);
-	return(0);
+    if (dir != NULL)
+        _TIFFfreeExt(tif, dir);
+    if (dirmem != NULL)
+        _TIFFfreeExt(tif, dirmem);
+    return (0);
 }
 
-static int8 TIFFClampDoubleToInt8( double val )
+static int8_t TIFFClampDoubleToInt8(double val)
 {
-    if( val > 127 )
+    if (val > 127)
         return 127;
-    if( val < -128 || val != val )
+    if (val < -128 || val != val)
         return -128;
-    return (int8)val;
+    return (int8_t)val;
 }
 
-static int16 TIFFClampDoubleToInt16( double val )
+static int16_t TIFFClampDoubleToInt16(double val)
 {
-    if( val > 32767 )
+    if (val > 32767)
         return 32767;
-    if( val < -32768 || val != val )
+    if (val < -32768 || val != val)
         return -32768;
-    return (int16)val;
+    return (int16_t)val;
 }
 
-static int32 TIFFClampDoubleToInt32( double val )
+static int32_t TIFFClampDoubleToInt32(double val)
 {
-    if( val > 0x7FFFFFFF )
+    if (val > 0x7FFFFFFF)
         return 0x7FFFFFFF;
-    if( val < -0x7FFFFFFF-1 || val != val )
-        return -0x7FFFFFFF-1;
-    return (int32)val;
+    if (val < -0x7FFFFFFF - 1 || val != val)
+        return -0x7FFFFFFF - 1;
+    return (int32_t)val;
 }
 
-static uint8 TIFFClampDoubleToUInt8( double val )
+static uint8_t TIFFClampDoubleToUInt8(double val)
 {
-    if( val < 0 )
+    if (val < 0)
         return 0;
-    if( val > 255 || val != val )
+    if (val > 255 || val != val)
         return 255;
-    return (uint8)val;
+    return (uint8_t)val;
 }
 
-static uint16 TIFFClampDoubleToUInt16( double val )
+static uint16_t TIFFClampDoubleToUInt16(double val)
 {
-    if( val < 0 )
+    if (val < 0)
         return 0;
-    if( val > 65535 || val != val )
+    if (val > 65535 || val != val)
         return 65535;
-    return (uint16)val;
+    return (uint16_t)val;
 }
 
-static uint32 TIFFClampDoubleToUInt32( double val )
+static uint32_t TIFFClampDoubleToUInt32(double val)
 {
-    if( val < 0 )
+    if (val < 0)
         return 0;
-    if( val > 0xFFFFFFFFU || val != val )
+    if (val > 0xFFFFFFFFU || val != val)
         return 0xFFFFFFFFU;
-    return (uint32)val;
+    return (uint32_t)val;
 }
 
-static int
-TIFFWriteDirectoryTagSampleformatArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, double* value)
+static int TIFFWriteDirectoryTagSampleformatArray(TIFF *tif, uint32_t *ndir,
+                                                  TIFFDirEntry *dir,
+                                                  uint16_t tag, uint32_t count,
+                                                  double *value)
 {
-	static const char module[] = "TIFFWriteDirectoryTagSampleformatArray";
-	void* conv;
-	uint32 i;
-	int ok;
-	conv = _TIFFmalloc(count*sizeof(double));
-	if (conv == NULL)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "Out of memory");
-		return (0);
-	}
+    static const char module[] = "TIFFWriteDirectoryTagSampleformatArray";
+    void *conv;
+    uint32_t i;
+    int ok;
+    conv = _TIFFmallocExt(tif, count * sizeof(double));
+    if (conv == NULL)
+    {
+        TIFFErrorExtR(tif, module, "Out of memory");
+        return (0);
+    }
 
-	switch (tif->tif_dir.td_sampleformat)
-	{
-		case SAMPLEFORMAT_IEEEFP:
-			if (tif->tif_dir.td_bitspersample<=32)
-			{
-				for (i = 0; i < count; ++i)
-					((float*)conv)[i] = _TIFFClampDoubleToFloat(value[i]);
-				ok = TIFFWriteDirectoryTagFloatArray(tif,ndir,dir,tag,count,(float*)conv);
-			}
-			else
-			{
-				ok = TIFFWriteDirectoryTagDoubleArray(tif,ndir,dir,tag,count,value);
-			}
-			break;
-		case SAMPLEFORMAT_INT:
-			if (tif->tif_dir.td_bitspersample<=8)
-			{
-				for (i = 0; i < count; ++i)
-					((int8*)conv)[i] = TIFFClampDoubleToInt8(value[i]);
-				ok = TIFFWriteDirectoryTagSbyteArray(tif,ndir,dir,tag,count,(int8*)conv);
-			}
-			else if (tif->tif_dir.td_bitspersample<=16)
-			{
-				for (i = 0; i < count; ++i)
-					((int16*)conv)[i] = TIFFClampDoubleToInt16(value[i]);
-				ok = TIFFWriteDirectoryTagSshortArray(tif,ndir,dir,tag,count,(int16*)conv);
-			}
-			else
-			{
-				for (i = 0; i < count; ++i)
-					((int32*)conv)[i] = TIFFClampDoubleToInt32(value[i]);
-				ok = TIFFWriteDirectoryTagSlongArray(tif,ndir,dir,tag,count,(int32*)conv);
-			}
-			break;
-		case SAMPLEFORMAT_UINT:
-			if (tif->tif_dir.td_bitspersample<=8)
-			{
-				for (i = 0; i < count; ++i)
-					((uint8*)conv)[i] = TIFFClampDoubleToUInt8(value[i]);
-				ok = TIFFWriteDirectoryTagByteArray(tif,ndir,dir,tag,count,(uint8*)conv);
-			}
-			else if (tif->tif_dir.td_bitspersample<=16)
-			{
-				for (i = 0; i < count; ++i)
-					((uint16*)conv)[i] = TIFFClampDoubleToUInt16(value[i]);
-				ok = TIFFWriteDirectoryTagShortArray(tif,ndir,dir,tag,count,(uint16*)conv);
-			}
-			else
-			{
-				for (i = 0; i < count; ++i)
-					((uint32*)conv)[i] = TIFFClampDoubleToUInt32(value[i]);
-				ok = TIFFWriteDirectoryTagLongArray(tif,ndir,dir,tag,count,(uint32*)conv);
-			}
-			break;
-		default:
-			ok = 0;
-	}
+    switch (tif->tif_dir.td_sampleformat)
+    {
+        case SAMPLEFORMAT_IEEEFP:
+            if (tif->tif_dir.td_bitspersample <= 32)
+            {
+                for (i = 0; i < count; ++i)
+                    ((float *)conv)[i] = _TIFFClampDoubleToFloat(value[i]);
+                ok = TIFFWriteDirectoryTagFloatArray(tif, ndir, dir, tag, count,
+                                                     (float *)conv);
+            }
+            else
+            {
+                ok = TIFFWriteDirectoryTagDoubleArray(tif, ndir, dir, tag,
+                                                      count, value);
+            }
+            break;
+        case SAMPLEFORMAT_INT:
+            if (tif->tif_dir.td_bitspersample <= 8)
+            {
+                for (i = 0; i < count; ++i)
+                    ((int8_t *)conv)[i] = TIFFClampDoubleToInt8(value[i]);
+                ok = TIFFWriteDirectoryTagSbyteArray(tif, ndir, dir, tag, count,
+                                                     (int8_t *)conv);
+            }
+            else if (tif->tif_dir.td_bitspersample <= 16)
+            {
+                for (i = 0; i < count; ++i)
+                    ((int16_t *)conv)[i] = TIFFClampDoubleToInt16(value[i]);
+                ok = TIFFWriteDirectoryTagSshortArray(tif, ndir, dir, tag,
+                                                      count, (int16_t *)conv);
+            }
+            else
+            {
+                for (i = 0; i < count; ++i)
+                    ((int32_t *)conv)[i] = TIFFClampDoubleToInt32(value[i]);
+                ok = TIFFWriteDirectoryTagSlongArray(tif, ndir, dir, tag, count,
+                                                     (int32_t *)conv);
+            }
+            break;
+        case SAMPLEFORMAT_UINT:
+            if (tif->tif_dir.td_bitspersample <= 8)
+            {
+                for (i = 0; i < count; ++i)
+                    ((uint8_t *)conv)[i] = TIFFClampDoubleToUInt8(value[i]);
+                ok = TIFFWriteDirectoryTagByteArray(tif, ndir, dir, tag, count,
+                                                    (uint8_t *)conv);
+            }
+            else if (tif->tif_dir.td_bitspersample <= 16)
+            {
+                for (i = 0; i < count; ++i)
+                    ((uint16_t *)conv)[i] = TIFFClampDoubleToUInt16(value[i]);
+                ok = TIFFWriteDirectoryTagShortArray(tif, ndir, dir, tag, count,
+                                                     (uint16_t *)conv);
+            }
+            else
+            {
+                for (i = 0; i < count; ++i)
+                    ((uint32_t *)conv)[i] = TIFFClampDoubleToUInt32(value[i]);
+                ok = TIFFWriteDirectoryTagLongArray(tif, ndir, dir, tag, count,
+                                                    (uint32_t *)conv);
+            }
+            break;
+        default:
+            ok = 0;
+    }
 
-	_TIFFfree(conv);
-	return (ok);
+    _TIFFfreeExt(tif, conv);
+    return (ok);
 }
 
-#if 0
-static int
-TIFFWriteDirectoryTagSampleformatPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value)
+static int TIFFWriteDirectoryTagAscii(TIFF *tif, uint32_t *ndir,
+                                      TIFFDirEntry *dir, uint16_t tag,
+                                      uint32_t count, char *value)
 {
-	switch (tif->tif_dir.td_sampleformat)
-	{
-		case SAMPLEFORMAT_IEEEFP:
-			if (tif->tif_dir.td_bitspersample<=32)
-				return(TIFFWriteDirectoryTagFloatPerSample(tif,ndir,dir,tag,(float)value));
-			else
-				return(TIFFWriteDirectoryTagDoublePerSample(tif,ndir,dir,tag,value));
-		case SAMPLEFORMAT_INT:
-			if (tif->tif_dir.td_bitspersample<=8)
-				return(TIFFWriteDirectoryTagSbytePerSample(tif,ndir,dir,tag,(int8)value));
-			else if (tif->tif_dir.td_bitspersample<=16)
-				return(TIFFWriteDirectoryTagSshortPerSample(tif,ndir,dir,tag,(int16)value));
-			else
-				return(TIFFWriteDirectoryTagSlongPerSample(tif,ndir,dir,tag,(int32)value));
-		case SAMPLEFORMAT_UINT:
-			if (tif->tif_dir.td_bitspersample<=8)
-				return(TIFFWriteDirectoryTagBytePerSample(tif,ndir,dir,tag,(uint8)value));
-			else if (tif->tif_dir.td_bitspersample<=16)
-				return(TIFFWriteDirectoryTagShortPerSample(tif,ndir,dir,tag,(uint16)value));
-			else
-				return(TIFFWriteDirectoryTagLongPerSample(tif,ndir,dir,tag,(uint32)value));
-		default:
-			return(1);
-	}
-}
-#endif
-
-static int
-TIFFWriteDirectoryTagAscii(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, char* value)
-{
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedAscii(tif,ndir,dir,tag,count,value));
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (
+        TIFFWriteDirectoryTagCheckedAscii(tif, ndir, dir, tag, count, value));
 }
 
-static int
-TIFFWriteDirectoryTagUndefinedArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint8* value)
+static int TIFFWriteDirectoryTagUndefinedArray(TIFF *tif, uint32_t *ndir,
+                                               TIFFDirEntry *dir, uint16_t tag,
+                                               uint32_t count, uint8_t *value)
 {
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedUndefinedArray(tif,ndir,dir,tag,count,value));
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (TIFFWriteDirectoryTagCheckedUndefinedArray(tif, ndir, dir, tag,
+                                                       count, value));
 }
 
-#ifdef notdef
-static int
-TIFFWriteDirectoryTagByte(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint8 value)
+static int TIFFWriteDirectoryTagByteArray(TIFF *tif, uint32_t *ndir,
+                                          TIFFDirEntry *dir, uint16_t tag,
+                                          uint32_t count, uint8_t *value)
 {
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedByte(tif,ndir,dir,tag,value));
-}
-#endif
-
-static int
-TIFFWriteDirectoryTagByteArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint8* value)
-{
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedByteArray(tif,ndir,dir,tag,count,value));
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (TIFFWriteDirectoryTagCheckedByteArray(tif, ndir, dir, tag, count,
+                                                  value));
 }
 
-#if 0
-static int
-TIFFWriteDirectoryTagBytePerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint8 value)
+static int TIFFWriteDirectoryTagSbyteArray(TIFF *tif, uint32_t *ndir,
+                                           TIFFDirEntry *dir, uint16_t tag,
+                                           uint32_t count, int8_t *value)
 {
-	static const char module[] = "TIFFWriteDirectoryTagBytePerSample";
-	uint8* m;
-	uint8* na;
-	uint16 nb;
-	int o;
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	m=_TIFFmalloc(tif->tif_dir.td_samplesperpixel*sizeof(uint8));
-	if (m==NULL)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-		return(0);
-	}
-	for (na=m, nb=0; nb<tif->tif_dir.td_samplesperpixel; na++, nb++)
-		*na=value;
-	o=TIFFWriteDirectoryTagCheckedByteArray(tif,ndir,dir,tag,tif->tif_dir.td_samplesperpixel,m);
-	_TIFFfree(m);
-	return(o);
-}
-#endif
-
-#ifdef notdef
-static int
-TIFFWriteDirectoryTagSbyte(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int8 value)
-{
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedSbyte(tif,ndir,dir,tag,value));
-}
-#endif
-
-static int
-TIFFWriteDirectoryTagSbyteArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int8* value)
-{
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedSbyteArray(tif,ndir,dir,tag,count,value));
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (TIFFWriteDirectoryTagCheckedSbyteArray(tif, ndir, dir, tag, count,
+                                                   value));
 }
 
-#if 0
-static int
-TIFFWriteDirectoryTagSbytePerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int8 value)
+static int TIFFWriteDirectoryTagShort(TIFF *tif, uint32_t *ndir,
+                                      TIFFDirEntry *dir, uint16_t tag,
+                                      uint16_t value)
 {
-	static const char module[] = "TIFFWriteDirectoryTagSbytePerSample";
-	int8* m;
-	int8* na;
-	uint16 nb;
-	int o;
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	m=_TIFFmalloc(tif->tif_dir.td_samplesperpixel*sizeof(int8));
-	if (m==NULL)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-		return(0);
-	}
-	for (na=m, nb=0; nb<tif->tif_dir.td_samplesperpixel; na++, nb++)
-		*na=value;
-	o=TIFFWriteDirectoryTagCheckedSbyteArray(tif,ndir,dir,tag,tif->tif_dir.td_samplesperpixel,m);
-	_TIFFfree(m);
-	return(o);
-}
-#endif
-
-static int
-TIFFWriteDirectoryTagShort(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint16 value)
-{
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedShort(tif,ndir,dir,tag,value));
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (TIFFWriteDirectoryTagCheckedShort(tif, ndir, dir, tag, value));
 }
 
-static int
-TIFFWriteDirectoryTagShortArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint16* value)
+static int TIFFWriteDirectoryTagShortArray(TIFF *tif, uint32_t *ndir,
+                                           TIFFDirEntry *dir, uint16_t tag,
+                                           uint32_t count, uint16_t *value)
 {
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedShortArray(tif,ndir,dir,tag,count,value));
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (TIFFWriteDirectoryTagCheckedShortArray(tif, ndir, dir, tag, count,
+                                                   value));
 }
 
-static int
-TIFFWriteDirectoryTagShortPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint16 value)
+static int TIFFWriteDirectoryTagShortPerSample(TIFF *tif, uint32_t *ndir,
+                                               TIFFDirEntry *dir, uint16_t tag,
+                                               uint16_t value)
 {
-	static const char module[] = "TIFFWriteDirectoryTagShortPerSample";
-	uint16* m;
-	uint16* na;
-	uint16 nb;
-	int o;
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	m=_TIFFmalloc(tif->tif_dir.td_samplesperpixel*sizeof(uint16));
-	if (m==NULL)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-		return(0);
-	}
-	for (na=m, nb=0; nb<tif->tif_dir.td_samplesperpixel; na++, nb++)
-		*na=value;
-	o=TIFFWriteDirectoryTagCheckedShortArray(tif,ndir,dir,tag,tif->tif_dir.td_samplesperpixel,m);
-	_TIFFfree(m);
-	return(o);
+    static const char module[] = "TIFFWriteDirectoryTagShortPerSample";
+    uint16_t *m;
+    uint16_t *na;
+    uint16_t nb;
+    int o;
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    m = _TIFFmallocExt(tif, tif->tif_dir.td_samplesperpixel * sizeof(uint16_t));
+    if (m == NULL)
+    {
+        TIFFErrorExtR(tif, module, "Out of memory");
+        return (0);
+    }
+    for (na = m, nb = 0; nb < tif->tif_dir.td_samplesperpixel; na++, nb++)
+        *na = value;
+    o = TIFFWriteDirectoryTagCheckedShortArray(
+        tif, ndir, dir, tag, tif->tif_dir.td_samplesperpixel, m);
+    _TIFFfreeExt(tif, m);
+    return (o);
 }
 
-#ifdef notdef
-static int
-TIFFWriteDirectoryTagSshort(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int16 value)
+static int TIFFWriteDirectoryTagSshortArray(TIFF *tif, uint32_t *ndir,
+                                            TIFFDirEntry *dir, uint16_t tag,
+                                            uint32_t count, int16_t *value)
 {
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedSshort(tif,ndir,dir,tag,value));
-}
-#endif
-
-static int
-TIFFWriteDirectoryTagSshortArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int16* value)
-{
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedSshortArray(tif,ndir,dir,tag,count,value));
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (TIFFWriteDirectoryTagCheckedSshortArray(tif, ndir, dir, tag, count,
+                                                    value));
 }
 
-#if 0
-static int
-TIFFWriteDirectoryTagSshortPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int16 value)
+static int TIFFWriteDirectoryTagLong(TIFF *tif, uint32_t *ndir,
+                                     TIFFDirEntry *dir, uint16_t tag,
+                                     uint32_t value)
 {
-	static const char module[] = "TIFFWriteDirectoryTagSshortPerSample";
-	int16* m;
-	int16* na;
-	uint16 nb;
-	int o;
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	m=_TIFFmalloc(tif->tif_dir.td_samplesperpixel*sizeof(int16));
-	if (m==NULL)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-		return(0);
-	}
-	for (na=m, nb=0; nb<tif->tif_dir.td_samplesperpixel; na++, nb++)
-		*na=value;
-	o=TIFFWriteDirectoryTagCheckedSshortArray(tif,ndir,dir,tag,tif->tif_dir.td_samplesperpixel,m);
-	_TIFFfree(m);
-	return(o);
-}
-#endif
-
-static int
-TIFFWriteDirectoryTagLong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 value)
-{
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedLong(tif,ndir,dir,tag,value));
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (TIFFWriteDirectoryTagCheckedLong(tif, ndir, dir, tag, value));
 }
 
-static int
-TIFFWriteDirectoryTagLongArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint32* value)
+static int TIFFWriteDirectoryTagLongArray(TIFF *tif, uint32_t *ndir,
+                                          TIFFDirEntry *dir, uint16_t tag,
+                                          uint32_t count, uint32_t *value)
 {
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedLongArray(tif,ndir,dir,tag,count,value));
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (TIFFWriteDirectoryTagCheckedLongArray(tif, ndir, dir, tag, count,
+                                                  value));
 }
 
-#if 0
-static int
-TIFFWriteDirectoryTagLongPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 value)
+static int TIFFWriteDirectoryTagSlongArray(TIFF *tif, uint32_t *ndir,
+                                           TIFFDirEntry *dir, uint16_t tag,
+                                           uint32_t count, int32_t *value)
 {
-	static const char module[] = "TIFFWriteDirectoryTagLongPerSample";
-	uint32* m;
-	uint32* na;
-	uint16 nb;
-	int o;
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	m=_TIFFmalloc(tif->tif_dir.td_samplesperpixel*sizeof(uint32));
-	if (m==NULL)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-		return(0);
-	}
-	for (na=m, nb=0; nb<tif->tif_dir.td_samplesperpixel; na++, nb++)
-		*na=value;
-	o=TIFFWriteDirectoryTagCheckedLongArray(tif,ndir,dir,tag,tif->tif_dir.td_samplesperpixel,m);
-	_TIFFfree(m);
-	return(o);
-}
-#endif
-
-#ifdef notdef
-static int
-TIFFWriteDirectoryTagSlong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int32 value)
-{
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedSlong(tif,ndir,dir,tag,value));
-}
-#endif
-
-static int
-TIFFWriteDirectoryTagSlongArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int32* value)
-{
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedSlongArray(tif,ndir,dir,tag,count,value));
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (TIFFWriteDirectoryTagCheckedSlongArray(tif, ndir, dir, tag, count,
+                                                   value));
 }
 
-#if 0
-static int
-TIFFWriteDirectoryTagSlongPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int32 value)
+/************************************************************************/
+/*                 TIFFWriteDirectoryTagLong8Array()                    */
+/*                                                                      */
+/*      Write either Long8 or Long array depending on file type.        */
+/************************************************************************/
+static int TIFFWriteDirectoryTagLong8Array(TIFF *tif, uint32_t *ndir,
+                                           TIFFDirEntry *dir, uint16_t tag,
+                                           uint32_t count, uint64_t *value)
 {
-	static const char module[] = "TIFFWriteDirectoryTagSlongPerSample";
-	int32* m;
-	int32* na;
-	uint16 nb;
-	int o;
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	m=_TIFFmalloc(tif->tif_dir.td_samplesperpixel*sizeof(int32));
-	if (m==NULL)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-		return(0);
-	}
-	for (na=m, nb=0; nb<tif->tif_dir.td_samplesperpixel; na++, nb++)
-		*na=value;
-	o=TIFFWriteDirectoryTagCheckedSlongArray(tif,ndir,dir,tag,tif->tif_dir.td_samplesperpixel,m);
-	_TIFFfree(m);
-	return(o);
-}
-#endif
+    static const char module[] = "TIFFWriteDirectoryTagLong8Array";
+    uint64_t *ma;
+    uint32_t mb;
+    uint32_t *p;
+    uint32_t *q;
+    int o;
 
-#ifdef notdef
-static int
-TIFFWriteDirectoryTagLong8(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint64 value)
-{
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedLong8(tif,ndir,dir,tag,value));
-}
-#endif
+    /* is this just a counting pass? */
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
 
-static int
-TIFFWriteDirectoryTagLong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value)
-{
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedLong8Array(tif,ndir,dir,tag,count,value));
+    /* We always write Long8 for BigTIFF, no checking needed. */
+    if (tif->tif_flags & TIFF_BIGTIFF)
+        return (TIFFWriteDirectoryTagCheckedLong8Array(tif, ndir, dir, tag,
+                                                       count, value));
+
+    /*
+    ** For classic tiff we want to verify everything is in range for long
+    ** and convert to long format.
+    */
+    p = _TIFFmallocExt(tif, count * sizeof(uint32_t));
+    if (p == NULL)
+    {
+        TIFFErrorExtR(tif, module, "Out of memory");
+        return (0);
+    }
+
+    for (q = p, ma = value, mb = 0; mb < count; ma++, mb++, q++)
+    {
+        if (*ma > 0xFFFFFFFF)
+        {
+            TIFFErrorExtR(tif, module,
+                          "Attempt to write unsigned long value %" PRIu64
+                          " larger than 0xFFFFFFFF for tag %d in Classic TIFF "
+                          "file. TIFF file writing aborted",
+                          *ma, tag);
+            _TIFFfreeExt(tif, p);
+            return (0);
+        }
+        *q = (uint32_t)(*ma);
+    }
+
+    o = TIFFWriteDirectoryTagCheckedLongArray(tif, ndir, dir, tag, count, p);
+    _TIFFfreeExt(tif, p);
+
+    return (o);
 }
 
-#ifdef notdef
-static int
-TIFFWriteDirectoryTagSlong8(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int64 value)
+/************************************************************************/
+/*                 TIFFWriteDirectoryTagSlong8Array()                   */
+/*                                                                      */
+/*      Write either SLong8 or SLong array depending on file type.      */
+/************************************************************************/
+static int TIFFWriteDirectoryTagSlong8Array(TIFF *tif, uint32_t *ndir,
+                                            TIFFDirEntry *dir, uint16_t tag,
+                                            uint32_t count, int64_t *value)
 {
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedSlong8(tif,ndir,dir,tag,value));
-}
-#endif
+    static const char module[] = "TIFFWriteDirectoryTagSlong8Array";
+    int64_t *ma;
+    uint32_t mb;
+    int32_t *p;
+    int32_t *q;
+    int o;
 
-static int
-TIFFWriteDirectoryTagSlong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int64* value)
-{
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedSlong8Array(tif,ndir,dir,tag,count,value));
+    /* is this just a counting pass? */
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    /* We always write SLong8 for BigTIFF, no checking needed. */
+    if (tif->tif_flags & TIFF_BIGTIFF)
+        return (TIFFWriteDirectoryTagCheckedSlong8Array(tif, ndir, dir, tag,
+                                                        count, value));
+
+    /*
+    ** For classic tiff we want to verify everything is in range for signed-long
+    ** and convert to signed-long format.
+    */
+    p = _TIFFmallocExt(tif, count * sizeof(uint32_t));
+    if (p == NULL)
+    {
+        TIFFErrorExtR(tif, module, "Out of memory");
+        return (0);
+    }
+
+    for (q = p, ma = value, mb = 0; mb < count; ma++, mb++, q++)
+    {
+        if (*ma > (2147483647))
+        {
+            TIFFErrorExtR(tif, module,
+                          "Attempt to write signed long value %" PRIi64
+                          " larger than 0x7FFFFFFF (2147483647) for tag %d in "
+                          "Classic TIFF file. TIFF writing to file aborted",
+                          *ma, tag);
+            _TIFFfreeExt(tif, p);
+            return (0);
+        }
+        else if (*ma < (-2147483647 - 1))
+        {
+            TIFFErrorExtR(tif, module,
+                          "Attempt to write signed long value %" PRIi64
+                          " smaller than 0x80000000 (-2147483648) for tag %d "
+                          "in Classic TIFF file. TIFF writing to file aborted",
+                          *ma, tag);
+            _TIFFfreeExt(tif, p);
+            return (0);
+        }
+        *q = (int32_t)(*ma);
+    }
+
+    o = TIFFWriteDirectoryTagCheckedSlongArray(tif, ndir, dir, tag, count, p);
+    _TIFFfreeExt(tif, p);
+
+    return (o);
 }
 
-static int
-TIFFWriteDirectoryTagRational(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value)
+static int TIFFWriteDirectoryTagRational(TIFF *tif, uint32_t *ndir,
+                                         TIFFDirEntry *dir, uint16_t tag,
+                                         double value)
 {
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedRational(tif,ndir,dir,tag,value));
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (TIFFWriteDirectoryTagCheckedRational(tif, ndir, dir, tag, value));
 }
 
-static int
-TIFFWriteDirectoryTagRationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value)
+static int TIFFWriteDirectoryTagRationalArray(TIFF *tif, uint32_t *ndir,
+                                              TIFFDirEntry *dir, uint16_t tag,
+                                              uint32_t count, float *value)
 {
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedRationalArray(tif,ndir,dir,tag,count,value));
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (TIFFWriteDirectoryTagCheckedRationalArray(tif, ndir, dir, tag,
+                                                      count, value));
 }
 
-static int
-TIFFWriteDirectoryTagSrationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value)
+static int TIFFWriteDirectoryTagSrationalArray(TIFF *tif, uint32_t *ndir,
+                                               TIFFDirEntry *dir, uint16_t tag,
+                                               uint32_t count, float *value)
 {
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedSrationalArray(tif,ndir,dir,tag,count,value));
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (TIFFWriteDirectoryTagCheckedSrationalArray(tif, ndir, dir, tag,
+                                                       count, value));
 }
 
-#ifdef notdef
-static int TIFFWriteDirectoryTagFloat(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, float value)
+/*-- Rational2Double: additional write functions */
+static int TIFFWriteDirectoryTagRationalDoubleArray(TIFF *tif, uint32_t *ndir,
+                                                    TIFFDirEntry *dir,
+                                                    uint16_t tag,
+                                                    uint32_t count,
+                                                    double *value)
 {
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedFloat(tif,ndir,dir,tag,value));
-}
-#endif
-
-static int TIFFWriteDirectoryTagFloatArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value)
-{
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedFloatArray(tif,ndir,dir,tag,count,value));
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (TIFFWriteDirectoryTagCheckedRationalDoubleArray(tif, ndir, dir, tag,
+                                                            count, value));
 }
 
-#if 0
-static int TIFFWriteDirectoryTagFloatPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, float value)
+static int TIFFWriteDirectoryTagSrationalDoubleArray(TIFF *tif, uint32_t *ndir,
+                                                     TIFFDirEntry *dir,
+                                                     uint16_t tag,
+                                                     uint32_t count,
+                                                     double *value)
 {
-	static const char module[] = "TIFFWriteDirectoryTagFloatPerSample";
-	float* m;
-	float* na;
-	uint16 nb;
-	int o;
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	m=_TIFFmalloc(tif->tif_dir.td_samplesperpixel*sizeof(float));
-	if (m==NULL)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-		return(0);
-	}
-	for (na=m, nb=0; nb<tif->tif_dir.td_samplesperpixel; na++, nb++)
-		*na=value;
-	o=TIFFWriteDirectoryTagCheckedFloatArray(tif,ndir,dir,tag,tif->tif_dir.td_samplesperpixel,m);
-	_TIFFfree(m);
-	return(o);
-}
-#endif
-
-#ifdef notdef
-static int TIFFWriteDirectoryTagDouble(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value)
-{
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedDouble(tif,ndir,dir,tag,value));
-}
-#endif
-
-static int TIFFWriteDirectoryTagDoubleArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, double* value)
-{
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedDoubleArray(tif,ndir,dir,tag,count,value));
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (TIFFWriteDirectoryTagCheckedSrationalDoubleArray(
+        tif, ndir, dir, tag, count, value));
 }
 
-#if 0
-static int TIFFWriteDirectoryTagDoublePerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value)
+static int TIFFWriteDirectoryTagFloatArray(TIFF *tif, uint32_t *ndir,
+                                           TIFFDirEntry *dir, uint16_t tag,
+                                           uint32_t count, float *value)
 {
-	static const char module[] = "TIFFWriteDirectoryTagDoublePerSample";
-	double* m;
-	double* na;
-	uint16 nb;
-	int o;
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	m=_TIFFmalloc(tif->tif_dir.td_samplesperpixel*sizeof(double));
-	if (m==NULL)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-		return(0);
-	}
-	for (na=m, nb=0; nb<tif->tif_dir.td_samplesperpixel; na++, nb++)
-		*na=value;
-	o=TIFFWriteDirectoryTagCheckedDoubleArray(tif,ndir,dir,tag,tif->tif_dir.td_samplesperpixel,m);
-	_TIFFfree(m);
-	return(o);
-}
-#endif
-
-static int
-TIFFWriteDirectoryTagIfdArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint32* value)
-{
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedIfdArray(tif,ndir,dir,tag,count,value));
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (TIFFWriteDirectoryTagCheckedFloatArray(tif, ndir, dir, tag, count,
+                                                   value));
 }
 
-#ifdef notdef
-static int
-TIFFWriteDirectoryTagIfd8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value)
+static int TIFFWriteDirectoryTagDoubleArray(TIFF *tif, uint32_t *ndir,
+                                            TIFFDirEntry *dir, uint16_t tag,
+                                            uint32_t count, double *value)
 {
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	return(TIFFWriteDirectoryTagCheckedIfd8Array(tif,ndir,dir,tag,count,value));
-}
-#endif
-
-static int
-TIFFWriteDirectoryTagShortLong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 value)
-{
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	if (value<=0xFFFF)
-		return(TIFFWriteDirectoryTagCheckedShort(tif,ndir,dir,tag,(uint16)value));
-	else
-		return(TIFFWriteDirectoryTagCheckedLong(tif,ndir,dir,tag,value));
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (TIFFWriteDirectoryTagCheckedDoubleArray(tif, ndir, dir, tag, count,
+                                                    value));
 }
 
-static int _WriteAsType(TIFF* tif, uint64 strile_size, uint64 uncompressed_threshold)
+static int TIFFWriteDirectoryTagIfdArray(TIFF *tif, uint32_t *ndir,
+                                         TIFFDirEntry *dir, uint16_t tag,
+                                         uint32_t count, uint32_t *value)
 {
-    const uint16 compression = tif->tif_dir.td_compression;
-    if ( compression == COMPRESSION_NONE )
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    return (TIFFWriteDirectoryTagCheckedIfdArray(tif, ndir, dir, tag, count,
+                                                 value));
+}
+
+static int TIFFWriteDirectoryTagShortLong(TIFF *tif, uint32_t *ndir,
+                                          TIFFDirEntry *dir, uint16_t tag,
+                                          uint32_t value)
+{
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    if (value <= 0xFFFF)
+        return (TIFFWriteDirectoryTagCheckedShort(tif, ndir, dir, tag,
+                                                  (uint16_t)value));
+    else
+        return (TIFFWriteDirectoryTagCheckedLong(tif, ndir, dir, tag, value));
+}
+
+static int _WriteAsType(TIFF *tif, uint64_t strile_size,
+                        uint64_t uncompressed_threshold)
+{
+    const uint16_t compression = tif->tif_dir.td_compression;
+    if (compression == COMPRESSION_NONE)
     {
         return strile_size > uncompressed_threshold;
     }
-    else if ( compression == COMPRESSION_JPEG ||
-              compression == COMPRESSION_LZW ||
-              compression == COMPRESSION_ADOBE_DEFLATE ||
-              compression == COMPRESSION_LZMA ||
-              compression == COMPRESSION_LERC ||
-              compression == COMPRESSION_ZSTD ||
-              compression == COMPRESSION_WEBP )
+    else if (compression == COMPRESSION_JPEG ||
+             compression == COMPRESSION_LZW ||
+             compression == COMPRESSION_ADOBE_DEFLATE ||
+             compression == COMPRESSION_DEFLATE ||
+             compression == COMPRESSION_LZMA ||
+             compression == COMPRESSION_LERC ||
+             compression == COMPRESSION_ZSTD ||
+             compression == COMPRESSION_WEBP || compression == COMPRESSION_JXL)
     {
         /* For a few select compression types, we assume that in the worst */
         /* case the compressed size will be 10 times the uncompressed size */
@@ -1719,12 +1831,12 @@
     return 1;
 }
 
-static int WriteAsLong8(TIFF* tif, uint64 strile_size)
+static int WriteAsLong8(TIFF *tif, uint64_t strile_size)
 {
     return _WriteAsType(tif, strile_size, 0xFFFFFFFFU);
 }
 
-static int WriteAsLong4(TIFF* tif, uint64 strile_size)
+static int WriteAsLong4(TIFF *tif, uint64_t strile_size)
 {
     return _WriteAsType(tif, strile_size, 0xFFFFU);
 }
@@ -1736,121 +1848,128 @@
 /*      on strile size and Classic/BigTIFF mode.                        */
 /************************************************************************/
 
-static int
-TIFFWriteDirectoryTagLongLong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value)
+static int TIFFWriteDirectoryTagLongLong8Array(TIFF *tif, uint32_t *ndir,
+                                               TIFFDirEntry *dir, uint16_t tag,
+                                               uint32_t count, uint64_t *value)
 {
     static const char module[] = "TIFFWriteDirectoryTagLongLong8Array";
     int o;
     int write_aslong4;
 
     /* is this just a counting pass? */
-    if (dir==NULL)
+    if (dir == NULL)
     {
         (*ndir)++;
-        return(1);
+        return (1);
     }
 
-    if( tif->tif_dir.td_deferstrilearraywriting )
+    if (tif->tif_dir.td_deferstrilearraywriting)
     {
-        return TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_NOTYPE, 0, 0, NULL);
+        return TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_NOTYPE, 0, 0,
+                                         NULL);
     }
 
-    if( tif->tif_flags&TIFF_BIGTIFF )
+    if (tif->tif_flags & TIFF_BIGTIFF)
     {
         int write_aslong8 = 1;
         /* In the case of ByteCounts array, we may be able to write them on */
         /* LONG if the strip/tilesize is not too big. */
-        /* Also do that for count > 1 in the case someone would want to create */
+        /* Also do that for count > 1 in the case someone would want to create
+         */
         /* a single-strip file with a growing height, in which case using */
         /* LONG8 will be safer. */
-        if( count > 1 && tag == TIFFTAG_STRIPBYTECOUNTS )
+        if (count > 1 && tag == TIFFTAG_STRIPBYTECOUNTS)
         {
             write_aslong8 = WriteAsLong8(tif, TIFFStripSize64(tif));
         }
-        else if( count > 1 && tag == TIFFTAG_TILEBYTECOUNTS )
+        else if (count > 1 && tag == TIFFTAG_TILEBYTECOUNTS)
         {
             write_aslong8 = WriteAsLong8(tif, TIFFTileSize64(tif));
         }
-        if( write_aslong8 )
+        if (write_aslong8)
         {
-            return TIFFWriteDirectoryTagCheckedLong8Array(tif,ndir,dir,
-                                                        tag,count,value);
+            return TIFFWriteDirectoryTagCheckedLong8Array(tif, ndir, dir, tag,
+                                                          count, value);
         }
     }
 
     write_aslong4 = 1;
-    if( count > 1 && tag == TIFFTAG_STRIPBYTECOUNTS )
+    if (count > 1 && tag == TIFFTAG_STRIPBYTECOUNTS)
     {
         write_aslong4 = WriteAsLong4(tif, TIFFStripSize64(tif));
     }
-    else if( count > 1 && tag == TIFFTAG_TILEBYTECOUNTS )
+    else if (count > 1 && tag == TIFFTAG_TILEBYTECOUNTS)
     {
         write_aslong4 = WriteAsLong4(tif, TIFFTileSize64(tif));
     }
-    if( write_aslong4 )
+    if (write_aslong4)
     {
         /*
         ** For classic tiff we want to verify everything is in range for LONG
         ** and convert to long format.
         */
 
-        uint32* p = _TIFFmalloc(count*sizeof(uint32));
-        uint32* q;
-        uint64* ma;
-        uint32 mb;
+        uint32_t *p = _TIFFmallocExt(tif, count * sizeof(uint32_t));
+        uint32_t *q;
+        uint64_t *ma;
+        uint32_t mb;
 
-        if (p==NULL)
+        if (p == NULL)
         {
-            TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-            return(0);
+            TIFFErrorExtR(tif, module, "Out of memory");
+            return (0);
         }
 
-        for (q=p, ma=value, mb=0; mb<count; ma++, mb++, q++)
+        for (q = p, ma = value, mb = 0; mb < count; ma++, mb++, q++)
         {
-            if (*ma>0xFFFFFFFF)
+            if (*ma > 0xFFFFFFFF)
             {
-                TIFFErrorExt(tif->tif_clientdata,module,
-                            "Attempt to write value larger than 0xFFFFFFFF in LONG array.");
-                _TIFFfree(p);
-                return(0);
+                TIFFErrorExtR(tif, module,
+                              "Attempt to write value larger than 0xFFFFFFFF "
+                              "in LONG array.");
+                _TIFFfreeExt(tif, p);
+                return (0);
             }
-            *q= (uint32)(*ma);
+            *q = (uint32_t)(*ma);
         }
 
-        o=TIFFWriteDirectoryTagCheckedLongArray(tif,ndir,dir,tag,count,p);
-        _TIFFfree(p);
+        o = TIFFWriteDirectoryTagCheckedLongArray(tif, ndir, dir, tag, count,
+                                                  p);
+        _TIFFfreeExt(tif, p);
     }
     else
     {
-        uint16* p = _TIFFmalloc(count*sizeof(uint16));
-        uint16* q;
-        uint64* ma;
-        uint32 mb;
+        uint16_t *p = _TIFFmallocExt(tif, count * sizeof(uint16_t));
+        uint16_t *q;
+        uint64_t *ma;
+        uint32_t mb;
 
-        if (p==NULL)
+        if (p == NULL)
         {
-            TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-            return(0);
+            TIFFErrorExtR(tif, module, "Out of memory");
+            return (0);
         }
 
-        for (q=p, ma=value, mb=0; mb<count; ma++, mb++, q++)
+        for (q = p, ma = value, mb = 0; mb < count; ma++, mb++, q++)
         {
-            if (*ma>0xFFFF)
+            if (*ma > 0xFFFF)
             {
                 /* Should not happen normally given the check we did before */
-                TIFFErrorExt(tif->tif_clientdata,module,
-                            "Attempt to write value larger than 0xFFFF in SHORT array.");
-                _TIFFfree(p);
-                return(0);
+                TIFFErrorExtR(tif, module,
+                              "Attempt to write value larger than 0xFFFF in "
+                              "SHORT array.");
+                _TIFFfreeExt(tif, p);
+                return (0);
             }
-            *q= (uint16)(*ma);
+            *q = (uint16_t)(*ma);
         }
 
-        o=TIFFWriteDirectoryTagCheckedShortArray(tif,ndir,dir,tag,count,p);
-        _TIFFfree(p);
+        o = TIFFWriteDirectoryTagCheckedShortArray(tif, ndir, dir, tag, count,
+                                                   p);
+        _TIFFfreeExt(tif, p);
     }
 
-    return(o);
+    return (o);
 }
 
 /************************************************************************/
@@ -1859,951 +1978,1176 @@
 /*      Write either IFD8 or IFD array depending on file type.          */
 /************************************************************************/
 
-static int
-TIFFWriteDirectoryTagIfdIfd8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value)
+static int TIFFWriteDirectoryTagIfdIfd8Array(TIFF *tif, uint32_t *ndir,
+                                             TIFFDirEntry *dir, uint16_t tag,
+                                             uint32_t count, uint64_t *value)
 {
     static const char module[] = "TIFFWriteDirectoryTagIfdIfd8Array";
-    uint64* ma;
-    uint32 mb;
-    uint32* p;
-    uint32* q;
+    uint64_t *ma;
+    uint32_t mb;
+    uint32_t *p;
+    uint32_t *q;
     int o;
 
     /* is this just a counting pass? */
-    if (dir==NULL)
+    if (dir == NULL)
     {
         (*ndir)++;
-        return(1);
+        return (1);
     }
 
     /* We always write IFD8 for BigTIFF, no checking needed. */
-    if( tif->tif_flags&TIFF_BIGTIFF )
-        return TIFFWriteDirectoryTagCheckedIfd8Array(tif,ndir,dir,
-                                                     tag,count,value);
+    if (tif->tif_flags & TIFF_BIGTIFF)
+        return TIFFWriteDirectoryTagCheckedIfd8Array(tif, ndir, dir, tag, count,
+                                                     value);
 
     /*
     ** For classic tiff we want to verify everything is in range for IFD
     ** and convert to long format.
     */
 
-    p = _TIFFmalloc(count*sizeof(uint32));
-    if (p==NULL)
+    p = _TIFFmallocExt(tif, count * sizeof(uint32_t));
+    if (p == NULL)
     {
-        TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-        return(0);
+        TIFFErrorExtR(tif, module, "Out of memory");
+        return (0);
     }
 
-    for (q=p, ma=value, mb=0; mb<count; ma++, mb++, q++)
+    for (q = p, ma = value, mb = 0; mb < count; ma++, mb++, q++)
     {
-        if (*ma>0xFFFFFFFF)
+        if (*ma > 0xFFFFFFFF)
         {
-            TIFFErrorExt(tif->tif_clientdata,module,
-                         "Attempt to write value larger than 0xFFFFFFFF in Classic TIFF file.");
-            _TIFFfree(p);
-            return(0);
+            TIFFErrorExtR(tif, module,
+                          "Attempt to write value larger than 0xFFFFFFFF in "
+                          "Classic TIFF file.");
+            _TIFFfreeExt(tif, p);
+            return (0);
         }
-        *q= (uint32)(*ma);
+        *q = (uint32_t)(*ma);
     }
 
-    o=TIFFWriteDirectoryTagCheckedIfdArray(tif,ndir,dir,tag,count,p);
-    _TIFFfree(p);
+    o = TIFFWriteDirectoryTagCheckedIfdArray(tif, ndir, dir, tag, count, p);
+    _TIFFfreeExt(tif, p);
 
-    return(o);
+    return (o);
 }
 
-#ifdef notdef
-static int
-TIFFWriteDirectoryTagShortLongLong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value)
+static int TIFFWriteDirectoryTagColormap(TIFF *tif, uint32_t *ndir,
+                                         TIFFDirEntry *dir)
 {
-	static const char module[] = "TIFFWriteDirectoryTagShortLongLong8Array";
-	uint64* ma;
-	uint32 mb;
-	uint8 n;
-	int o;
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	n=0;
-	for (ma=value, mb=0; mb<count; ma++, mb++)
-	{
-		if ((n==0)&&(*ma>0xFFFF))
-			n=1;
-		if ((n==1)&&(*ma>0xFFFFFFFF))
-		{
-			n=2;
-			break;
-		}
-	}
-	if (n==0)
-	{
-		uint16* p;
-		uint16* q;
-		p=_TIFFmalloc(count*sizeof(uint16));
-		if (p==NULL)
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-			return(0);
-		}
-		for (ma=value, mb=0, q=p; mb<count; ma++, mb++, q++)
-			*q=(uint16)(*ma);
-		o=TIFFWriteDirectoryTagCheckedShortArray(tif,ndir,dir,tag,count,p);
-		_TIFFfree(p);
-	}
-	else if (n==1)
-	{
-		uint32* p;
-		uint32* q;
-		p=_TIFFmalloc(count*sizeof(uint32));
-		if (p==NULL)
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-			return(0);
-		}
-		for (ma=value, mb=0, q=p; mb<count; ma++, mb++, q++)
-			*q=(uint32)(*ma);
-		o=TIFFWriteDirectoryTagCheckedLongArray(tif,ndir,dir,tag,count,p);
-		_TIFFfree(p);
-	}
-	else
-	{
-		assert(n==2);
-		o=TIFFWriteDirectoryTagCheckedLong8Array(tif,ndir,dir,tag,count,value);
-	}
-	return(o);
-}
-#endif
-static int
-TIFFWriteDirectoryTagColormap(TIFF* tif, uint32* ndir, TIFFDirEntry* dir)
-{
-	static const char module[] = "TIFFWriteDirectoryTagColormap";
-	uint32 m;
-	uint16* n;
-	int o;
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	m=(1<<tif->tif_dir.td_bitspersample);
-	n=_TIFFmalloc(3*m*sizeof(uint16));
-	if (n==NULL)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-		return(0);
-	}
-	_TIFFmemcpy(&n[0],tif->tif_dir.td_colormap[0],m*sizeof(uint16));
-	_TIFFmemcpy(&n[m],tif->tif_dir.td_colormap[1],m*sizeof(uint16));
-	_TIFFmemcpy(&n[2*m],tif->tif_dir.td_colormap[2],m*sizeof(uint16));
-	o=TIFFWriteDirectoryTagCheckedShortArray(tif,ndir,dir,TIFFTAG_COLORMAP,3*m,n);
-	_TIFFfree(n);
-	return(o);
+    static const char module[] = "TIFFWriteDirectoryTagColormap";
+    uint32_t m;
+    uint16_t *n;
+    int o;
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    m = (1 << tif->tif_dir.td_bitspersample);
+    n = _TIFFmallocExt(tif, 3 * m * sizeof(uint16_t));
+    if (n == NULL)
+    {
+        TIFFErrorExtR(tif, module, "Out of memory");
+        return (0);
+    }
+    _TIFFmemcpy(&n[0], tif->tif_dir.td_colormap[0], m * sizeof(uint16_t));
+    _TIFFmemcpy(&n[m], tif->tif_dir.td_colormap[1], m * sizeof(uint16_t));
+    _TIFFmemcpy(&n[2 * m], tif->tif_dir.td_colormap[2], m * sizeof(uint16_t));
+    o = TIFFWriteDirectoryTagCheckedShortArray(tif, ndir, dir, TIFFTAG_COLORMAP,
+                                               3 * m, n);
+    _TIFFfreeExt(tif, n);
+    return (o);
 }
 
-static int
-TIFFWriteDirectoryTagTransferfunction(TIFF* tif, uint32* ndir, TIFFDirEntry* dir)
+static int TIFFWriteDirectoryTagTransferfunction(TIFF *tif, uint32_t *ndir,
+                                                 TIFFDirEntry *dir)
 {
-	static const char module[] = "TIFFWriteDirectoryTagTransferfunction";
-	uint32 m;
-	uint16 n;
-	uint16* o;
-	int p;
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	m=(1<<tif->tif_dir.td_bitspersample);
-	n=tif->tif_dir.td_samplesperpixel-tif->tif_dir.td_extrasamples;
-	/*
-	 * Check if the table can be written as a single column,
-	 * or if it must be written as 3 columns.  Note that we
-	 * write a 3-column tag if there are 2 samples/pixel and
-	 * a single column of data won't suffice--hmm.
-	 */
-	if (n>3)
-		n=3;
-	if (n==3)
-	{
-		if (tif->tif_dir.td_transferfunction[2] == NULL ||
-		    !_TIFFmemcmp(tif->tif_dir.td_transferfunction[0],tif->tif_dir.td_transferfunction[2],m*sizeof(uint16)))
-			n=2;
-	}
-	if (n==2)
-	{
-		if (tif->tif_dir.td_transferfunction[1] == NULL ||
-		    !_TIFFmemcmp(tif->tif_dir.td_transferfunction[0],tif->tif_dir.td_transferfunction[1],m*sizeof(uint16)))
-			n=1;
-	}
-	if (n==0)
-		n=1;
-	o=_TIFFmalloc(n*m*sizeof(uint16));
-	if (o==NULL)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-		return(0);
-	}
-	_TIFFmemcpy(&o[0],tif->tif_dir.td_transferfunction[0],m*sizeof(uint16));
-	if (n>1)
-		_TIFFmemcpy(&o[m],tif->tif_dir.td_transferfunction[1],m*sizeof(uint16));
-	if (n>2)
-		_TIFFmemcpy(&o[2*m],tif->tif_dir.td_transferfunction[2],m*sizeof(uint16));
-	p=TIFFWriteDirectoryTagCheckedShortArray(tif,ndir,dir,TIFFTAG_TRANSFERFUNCTION,n*m,o);
-	_TIFFfree(o);
-	return(p);
+    static const char module[] = "TIFFWriteDirectoryTagTransferfunction";
+    uint32_t m;
+    uint16_t n;
+    uint16_t *o;
+    int p;
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    m = (1 << tif->tif_dir.td_bitspersample);
+    n = tif->tif_dir.td_samplesperpixel - tif->tif_dir.td_extrasamples;
+    /*
+     * Check if the table can be written as a single column,
+     * or if it must be written as 3 columns.  Note that we
+     * write a 3-column tag if there are 2 samples/pixel and
+     * a single column of data won't suffice--hmm.
+     */
+    if (n > 3)
+        n = 3;
+    if (n == 3)
+    {
+        if (tif->tif_dir.td_transferfunction[2] == NULL ||
+            !_TIFFmemcmp(tif->tif_dir.td_transferfunction[0],
+                         tif->tif_dir.td_transferfunction[2],
+                         m * sizeof(uint16_t)))
+            n = 2;
+    }
+    if (n == 2)
+    {
+        if (tif->tif_dir.td_transferfunction[1] == NULL ||
+            !_TIFFmemcmp(tif->tif_dir.td_transferfunction[0],
+                         tif->tif_dir.td_transferfunction[1],
+                         m * sizeof(uint16_t)))
+            n = 1;
+    }
+    if (n == 0)
+        n = 1;
+    o = _TIFFmallocExt(tif, n * m * sizeof(uint16_t));
+    if (o == NULL)
+    {
+        TIFFErrorExtR(tif, module, "Out of memory");
+        return (0);
+    }
+    _TIFFmemcpy(&o[0], tif->tif_dir.td_transferfunction[0],
+                m * sizeof(uint16_t));
+    if (n > 1)
+        _TIFFmemcpy(&o[m], tif->tif_dir.td_transferfunction[1],
+                    m * sizeof(uint16_t));
+    if (n > 2)
+        _TIFFmemcpy(&o[2 * m], tif->tif_dir.td_transferfunction[2],
+                    m * sizeof(uint16_t));
+    p = TIFFWriteDirectoryTagCheckedShortArray(
+        tif, ndir, dir, TIFFTAG_TRANSFERFUNCTION, n * m, o);
+    _TIFFfreeExt(tif, o);
+    return (p);
 }
 
-static int
-TIFFWriteDirectoryTagSubifd(TIFF* tif, uint32* ndir, TIFFDirEntry* dir)
+static int TIFFWriteDirectoryTagSubifd(TIFF *tif, uint32_t *ndir,
+                                       TIFFDirEntry *dir)
 {
-	static const char module[] = "TIFFWriteDirectoryTagSubifd";
-	uint64 m;
-	int n;
-	if (tif->tif_dir.td_nsubifd==0)
-		return(1);
-	if (dir==NULL)
-	{
-		(*ndir)++;
-		return(1);
-	}
-	m=tif->tif_dataoff;
-	if (!(tif->tif_flags&TIFF_BIGTIFF))
-	{
-		uint32* o;
-		uint64* pa;
-		uint32* pb;
-		uint16 p;
-		o=_TIFFmalloc(tif->tif_dir.td_nsubifd*sizeof(uint32));
-		if (o==NULL)
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-			return(0);
-		}
-		pa=tif->tif_dir.td_subifd;
-		pb=o;
-		for (p=0; p < tif->tif_dir.td_nsubifd; p++)
-		{
-                        assert(pa != 0);
-
-                        /* Could happen if an classicTIFF has a SubIFD of type LONG8 (which is illegal) */
-                        if( *pa > 0xFFFFFFFFUL)
-                        {
-                            TIFFErrorExt(tif->tif_clientdata,module,"Illegal value for SubIFD tag");
-                            _TIFFfree(o);
-                            return(0);
-                        }
-			*pb++=(uint32)(*pa++);
-		}
-		n=TIFFWriteDirectoryTagCheckedIfdArray(tif,ndir,dir,TIFFTAG_SUBIFD,tif->tif_dir.td_nsubifd,o);
-		_TIFFfree(o);
-	}
-	else
-		n=TIFFWriteDirectoryTagCheckedIfd8Array(tif,ndir,dir,TIFFTAG_SUBIFD,tif->tif_dir.td_nsubifd,tif->tif_dir.td_subifd);
-	if (!n)
-		return(0);
-	/*
-	 * Total hack: if this directory includes a SubIFD
-	 * tag then force the next <n> directories to be
-	 * written as ``sub directories'' of this one.  This
-	 * is used to write things like thumbnails and
-	 * image masks that one wants to keep out of the
-	 * normal directory linkage access mechanism.
-	 */
-	tif->tif_flags|=TIFF_INSUBIFD;
-	tif->tif_nsubifd=tif->tif_dir.td_nsubifd;
-	if (tif->tif_dir.td_nsubifd==1)
-		tif->tif_subifdoff=0;
-	else
-		tif->tif_subifdoff=m;
-	return(1);
-}
-
-static int
-TIFFWriteDirectoryTagCheckedAscii(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, char* value)
-{
-	assert(sizeof(char)==1);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_ASCII,count,count,value));
-}
-
-static int
-TIFFWriteDirectoryTagCheckedUndefinedArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint8* value)
-{
-	assert(sizeof(uint8)==1);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_UNDEFINED,count,count,value));
-}
-
-#ifdef notdef
-static int
-TIFFWriteDirectoryTagCheckedByte(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint8 value)
-{
-	assert(sizeof(uint8)==1);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_BYTE,1,1,&value));
-}
-#endif
-
-static int
-TIFFWriteDirectoryTagCheckedByteArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint8* value)
-{
-	assert(sizeof(uint8)==1);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_BYTE,count,count,value));
-}
-
-#ifdef notdef
-static int
-TIFFWriteDirectoryTagCheckedSbyte(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int8 value)
-{
-	assert(sizeof(int8)==1);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SBYTE,1,1,&value));
-}
-#endif
-
-static int
-TIFFWriteDirectoryTagCheckedSbyteArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int8* value)
-{
-	assert(sizeof(int8)==1);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SBYTE,count,count,value));
-}
-
-static int
-TIFFWriteDirectoryTagCheckedShort(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint16 value)
-{
-	uint16 m;
-	assert(sizeof(uint16)==2);
-	m=value;
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabShort(&m);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SHORT,1,2,&m));
-}
-
-static int
-TIFFWriteDirectoryTagCheckedShortArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint16* value)
-{
-	assert(count<0x80000000);
-	assert(sizeof(uint16)==2);
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabArrayOfShort(value,count);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SHORT,count,count*2,value));
-}
-
-#ifdef notdef
-static int
-TIFFWriteDirectoryTagCheckedSshort(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int16 value)
-{
-	int16 m;
-	assert(sizeof(int16)==2);
-	m=value;
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabShort((uint16*)(&m));
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SSHORT,1,2,&m));
-}
-#endif
-
-static int
-TIFFWriteDirectoryTagCheckedSshortArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int16* value)
-{
-	assert(count<0x80000000);
-	assert(sizeof(int16)==2);
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabArrayOfShort((uint16*)value,count);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SSHORT,count,count*2,value));
-}
-
-static int
-TIFFWriteDirectoryTagCheckedLong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 value)
-{
-	uint32 m;
-	assert(sizeof(uint32)==4);
-	m=value;
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabLong(&m);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_LONG,1,4,&m));
-}
-
-static int
-TIFFWriteDirectoryTagCheckedLongArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint32* value)
-{
-	assert(count<0x40000000);
-	assert(sizeof(uint32)==4);
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabArrayOfLong(value,count);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_LONG,count,count*4,value));
-}
-
-#ifdef notdef
-static int
-TIFFWriteDirectoryTagCheckedSlong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int32 value)
-{
-	int32 m;
-	assert(sizeof(int32)==4);
-	m=value;
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabLong((uint32*)(&m));
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SLONG,1,4,&m));
-}
-#endif
-
-static int
-TIFFWriteDirectoryTagCheckedSlongArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int32* value)
-{
-	assert(count<0x40000000);
-	assert(sizeof(int32)==4);
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabArrayOfLong((uint32*)value,count);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SLONG,count,count*4,value));
-}
-
-#ifdef notdef
-static int
-TIFFWriteDirectoryTagCheckedLong8(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint64 value)
-{
-	uint64 m;
-	assert(sizeof(uint64)==8);
-	if( !(tif->tif_flags&TIFF_BIGTIFF) ) {
-		TIFFErrorExt(tif->tif_clientdata,"TIFFWriteDirectoryTagCheckedLong8","LONG8 not allowed for ClassicTIFF");
-		return(0);
-	}
-	m=value;
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabLong8(&m);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_LONG8,1,8,&m));
-}
-#endif
-
-static int
-TIFFWriteDirectoryTagCheckedLong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value)
-{
-	assert(count<0x20000000);
-	assert(sizeof(uint64)==8);
-	if( !(tif->tif_flags&TIFF_BIGTIFF) ) {
-		TIFFErrorExt(tif->tif_clientdata,"TIFFWriteDirectoryTagCheckedLong8Array","LONG8 not allowed for ClassicTIFF");
-		return(0);
-	}
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabArrayOfLong8(value,count);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_LONG8,count,count*8,value));
-}
-
-#ifdef notdef
-static int
-TIFFWriteDirectoryTagCheckedSlong8(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int64 value)
-{
-	int64 m;
-	assert(sizeof(int64)==8);
-	if( !(tif->tif_flags&TIFF_BIGTIFF) ) {
-		TIFFErrorExt(tif->tif_clientdata,"TIFFWriteDirectoryTagCheckedSlong8","SLONG8 not allowed for ClassicTIFF");
-		return(0);
-	}
-	m=value;
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabLong8((uint64*)(&m));
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SLONG8,1,8,&m));
-}
-#endif
-
-static int
-TIFFWriteDirectoryTagCheckedSlong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int64* value)
-{
-	assert(count<0x20000000);
-	assert(sizeof(int64)==8);
-	if( !(tif->tif_flags&TIFF_BIGTIFF) ) {
-		TIFFErrorExt(tif->tif_clientdata,"TIFFWriteDirectoryTagCheckedSlong8Array","SLONG8 not allowed for ClassicTIFF");
-		return(0);
-	}
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabArrayOfLong8((uint64*)value,count);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SLONG8,count,count*8,value));
-}
-
-static int
-TIFFWriteDirectoryTagCheckedRational(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value)
-{
-        static const char module[] = "TIFFWriteDirectoryTagCheckedRational";
-	uint32 m[2];
-	assert(sizeof(uint32)==4);
-        if( value < 0 )
+    static const char module[] = "TIFFWriteDirectoryTagSubifd";
+    uint64_t m;
+    int n;
+    if (tif->tif_dir.td_nsubifd == 0)
+        return (1);
+    if (dir == NULL)
+    {
+        (*ndir)++;
+        return (1);
+    }
+    m = tif->tif_dataoff;
+    if (!(tif->tif_flags & TIFF_BIGTIFF))
+    {
+        uint32_t *o;
+        uint64_t *pa;
+        uint32_t *pb;
+        uint16_t p;
+        o = _TIFFmallocExt(tif, tif->tif_dir.td_nsubifd * sizeof(uint32_t));
+        if (o == NULL)
         {
-            TIFFErrorExt(tif->tif_clientdata,module,"Negative value is illegal");
-            return 0;
+            TIFFErrorExtR(tif, module, "Out of memory");
+            return (0);
         }
-        else if( value != value )
+        pa = tif->tif_dir.td_subifd;
+        pb = o;
+        for (p = 0; p < tif->tif_dir.td_nsubifd; p++)
         {
-            TIFFErrorExt(tif->tif_clientdata,module,"Not-a-number value is illegal");
-            return 0;
-        }
-	else if (value==0.0)
-	{
-		m[0]=0;
-		m[1]=1;
-	}
-	else if (value <= 0xFFFFFFFFU && value==(double)(uint32)value)
-	{
-		m[0]=(uint32)value;
-		m[1]=1;
-	}
-	else if (value<1.0)
-	{
-		m[0]=(uint32)(value*0xFFFFFFFF);
-		m[1]=0xFFFFFFFF;
-	}
-	else
-	{
-		m[0]=0xFFFFFFFF;
-		m[1]=(uint32)(0xFFFFFFFF/value);
-	}
-	if (tif->tif_flags&TIFF_SWAB)
-	{
-		TIFFSwabLong(&m[0]);
-		TIFFSwabLong(&m[1]);
-	}
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_RATIONAL,1,8,&m[0]));
-}
+            assert(pa != 0);
 
-static int
-TIFFWriteDirectoryTagCheckedRationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value)
-{
-	static const char module[] = "TIFFWriteDirectoryTagCheckedRationalArray";
-	uint32* m;
-	float* na;
-	uint32* nb;
-	uint32 nc;
-	int o;
-	assert(sizeof(uint32)==4);
-	m=_TIFFmalloc(count*2*sizeof(uint32));
-	if (m==NULL)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-		return(0);
-	}
-	for (na=value, nb=m, nc=0; nc<count; na++, nb+=2, nc++)
-	{
-		if (*na<=0.0 || *na != *na)
-		{
-			nb[0]=0;
-			nb[1]=1;
-		}
-		else if (*na >= 0 && *na <= (float)0xFFFFFFFFU &&
-                         *na==(float)(uint32)(*na))
-		{
-			nb[0]=(uint32)(*na);
-			nb[1]=1;
-		}
-		else if (*na<1.0)
-		{
-			nb[0]=(uint32)((double)(*na)*0xFFFFFFFF);
-			nb[1]=0xFFFFFFFF;
-		}
-		else
-		{
-			nb[0]=0xFFFFFFFF;
-			nb[1]=(uint32)((double)0xFFFFFFFF/(*na));
-		}
-	}
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabArrayOfLong(m,count*2);
-	o=TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_RATIONAL,count,count*8,&m[0]);
-	_TIFFfree(m);
-	return(o);
-}
-
-static int
-TIFFWriteDirectoryTagCheckedSrationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value)
-{
-	static const char module[] = "TIFFWriteDirectoryTagCheckedSrationalArray";
-	int32* m;
-	float* na;
-	int32* nb;
-	uint32 nc;
-	int o;
-	assert(sizeof(int32)==4);
-	m=_TIFFmalloc(count*2*sizeof(int32));
-	if (m==NULL)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-		return(0);
-	}
-	for (na=value, nb=m, nc=0; nc<count; na++, nb+=2, nc++)
-	{
-		if (*na<0.0)
-		{
-			if (*na==(int32)(*na))
-			{
-				nb[0]=(int32)(*na);
-				nb[1]=1;
-			}
-			else if (*na>-1.0)
-			{
-				nb[0]=-(int32)((double)(-*na)*0x7FFFFFFF);
-				nb[1]=0x7FFFFFFF;
-			}
-			else
-			{
-				nb[0]=-0x7FFFFFFF;
-				nb[1]=(int32)((double)0x7FFFFFFF/(-*na));
-			}
-		}
-		else
-		{
-			if (*na==(int32)(*na))
-			{
-				nb[0]=(int32)(*na);
-				nb[1]=1;
-			}
-			else if (*na<1.0)
-			{
-				nb[0]=(int32)((double)(*na)*0x7FFFFFFF);
-				nb[1]=0x7FFFFFFF;
-			}
-			else
-			{
-				nb[0]=0x7FFFFFFF;
-				nb[1]=(int32)((double)0x7FFFFFFF/(*na));
-			}
-		}
-	}
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabArrayOfLong((uint32*)m,count*2);
-	o=TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SRATIONAL,count,count*8,&m[0]);
-	_TIFFfree(m);
-	return(o);
-}
-
-#ifdef notdef
-static int
-TIFFWriteDirectoryTagCheckedFloat(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, float value)
-{
-	float m;
-	assert(sizeof(float)==4);
-	m=value;
-	TIFFCvtNativeToIEEEFloat(tif,1,&m);
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabFloat(&m);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_FLOAT,1,4,&m));
-}
-#endif
-
-static int
-TIFFWriteDirectoryTagCheckedFloatArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value)
-{
-	assert(count<0x40000000);
-	assert(sizeof(float)==4);
-	TIFFCvtNativeToIEEEFloat(tif,count,&value);
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabArrayOfFloat(value,count);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_FLOAT,count,count*4,value));
-}
-
-#ifdef notdef
-static int
-TIFFWriteDirectoryTagCheckedDouble(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value)
-{
-	double m;
-	assert(sizeof(double)==8);
-	m=value;
-	TIFFCvtNativeToIEEEDouble(tif,1,&m);
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabDouble(&m);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_DOUBLE,1,8,&m));
-}
-#endif
-
-static int
-TIFFWriteDirectoryTagCheckedDoubleArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, double* value)
-{
-	assert(count<0x20000000);
-	assert(sizeof(double)==8);
-	TIFFCvtNativeToIEEEDouble(tif,count,&value);
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabArrayOfDouble(value,count);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_DOUBLE,count,count*8,value));
-}
-
-static int
-TIFFWriteDirectoryTagCheckedIfdArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint32* value)
-{
-	assert(count<0x40000000);
-	assert(sizeof(uint32)==4);
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabArrayOfLong(value,count);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_IFD,count,count*4,value));
-}
-
-static int
-TIFFWriteDirectoryTagCheckedIfd8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value)
-{
-	assert(count<0x20000000);
-	assert(sizeof(uint64)==8);
-	assert(tif->tif_flags&TIFF_BIGTIFF);
-	if (tif->tif_flags&TIFF_SWAB)
-		TIFFSwabArrayOfLong8(value,count);
-	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_IFD8,count,count*8,value));
-}
-
-static int
-TIFFWriteDirectoryTagData(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint16 datatype, uint32 count, uint32 datalength, void* data)
-{
-	static const char module[] = "TIFFWriteDirectoryTagData";
-	uint32 m;
-	m=0;
-	while (m<(*ndir))
-	{
-		assert(dir[m].tdir_tag!=tag);
-		if (dir[m].tdir_tag>tag)
-			break;
-		m++;
-	}
-	if (m<(*ndir))
-	{
-		uint32 n;
-		for (n=*ndir; n>m; n--)
-			dir[n]=dir[n-1];
-	}
-	dir[m].tdir_tag=tag;
-	dir[m].tdir_type=datatype;
-	dir[m].tdir_count=count;
-	dir[m].tdir_offset.toff_long8 = 0;
-	if (datalength<=((tif->tif_flags&TIFF_BIGTIFF)?0x8U:0x4U))
-        {
-            if( data && datalength )
+            /* Could happen if an classicTIFF has a SubIFD of type LONG8 (which
+             * is illegal) */
+            if (*pa > 0xFFFFFFFFUL)
             {
-                _TIFFmemcpy(&dir[m].tdir_offset,data,datalength);
+                TIFFErrorExtR(tif, module, "Illegal value for SubIFD tag");
+                _TIFFfreeExt(tif, o);
+                return (0);
             }
+            *pb++ = (uint32_t)(*pa++);
         }
-	else
-	{
-		uint64 na,nb;
-		na=tif->tif_dataoff;
-		nb=na+datalength;
-		if (!(tif->tif_flags&TIFF_BIGTIFF))
-			nb=(uint32)nb;
-		if ((nb<na)||(nb<datalength))
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"Maximum TIFF file size exceeded");
-			return(0);
-		}
-		if (!SeekOK(tif,na))
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"IO error writing tag data");
-			return(0);
-		}
-		assert(datalength<0x80000000UL);
-		if (!WriteOK(tif,data,(tmsize_t)datalength))
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"IO error writing tag data");
-			return(0);
-		}
-		tif->tif_dataoff=nb;
-		if (tif->tif_dataoff&1)
-			tif->tif_dataoff++;
-		if (!(tif->tif_flags&TIFF_BIGTIFF))
-		{
-			uint32 o;
-			o=(uint32)na;
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabLong(&o);
-			_TIFFmemcpy(&dir[m].tdir_offset,&o,4);
-		}
-		else
-		{
-			dir[m].tdir_offset.toff_long8 = na;
-			if (tif->tif_flags&TIFF_SWAB)
-				TIFFSwabLong8(&dir[m].tdir_offset.toff_long8);
-		}
-	}
-	(*ndir)++;
-	return(1);
+        n = TIFFWriteDirectoryTagCheckedIfdArray(tif, ndir, dir, TIFFTAG_SUBIFD,
+                                                 tif->tif_dir.td_nsubifd, o);
+        _TIFFfreeExt(tif, o);
+    }
+    else
+        n = TIFFWriteDirectoryTagCheckedIfd8Array(
+            tif, ndir, dir, TIFFTAG_SUBIFD, tif->tif_dir.td_nsubifd,
+            tif->tif_dir.td_subifd);
+    if (!n)
+        return (0);
+    /*
+     * Total hack: if this directory includes a SubIFD
+     * tag then force the next <n> directories to be
+     * written as ``sub directories'' of this one.  This
+     * is used to write things like thumbnails and
+     * image masks that one wants to keep out of the
+     * normal directory linkage access mechanism.
+     */
+    tif->tif_flags |= TIFF_INSUBIFD;
+    tif->tif_nsubifd = tif->tif_dir.td_nsubifd;
+    if (tif->tif_dir.td_nsubifd == 1)
+        tif->tif_subifdoff = 0;
+    else
+        tif->tif_subifdoff = m;
+    return (1);
+}
+
+static int TIFFWriteDirectoryTagCheckedAscii(TIFF *tif, uint32_t *ndir,
+                                             TIFFDirEntry *dir, uint16_t tag,
+                                             uint32_t count, char *value)
+{
+    assert(sizeof(char) == 1);
+    return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_ASCII, count,
+                                      count, value));
+}
+
+static int TIFFWriteDirectoryTagCheckedUndefinedArray(TIFF *tif, uint32_t *ndir,
+                                                      TIFFDirEntry *dir,
+                                                      uint16_t tag,
+                                                      uint32_t count,
+                                                      uint8_t *value)
+{
+    assert(sizeof(uint8_t) == 1);
+    return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_UNDEFINED,
+                                      count, count, value));
+}
+
+static int TIFFWriteDirectoryTagCheckedByteArray(TIFF *tif, uint32_t *ndir,
+                                                 TIFFDirEntry *dir,
+                                                 uint16_t tag, uint32_t count,
+                                                 uint8_t *value)
+{
+    assert(sizeof(uint8_t) == 1);
+    return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_BYTE, count,
+                                      count, value));
+}
+
+static int TIFFWriteDirectoryTagCheckedSbyteArray(TIFF *tif, uint32_t *ndir,
+                                                  TIFFDirEntry *dir,
+                                                  uint16_t tag, uint32_t count,
+                                                  int8_t *value)
+{
+    assert(sizeof(int8_t) == 1);
+    return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_SBYTE, count,
+                                      count, value));
+}
+
+static int TIFFWriteDirectoryTagCheckedShort(TIFF *tif, uint32_t *ndir,
+                                             TIFFDirEntry *dir, uint16_t tag,
+                                             uint16_t value)
+{
+    uint16_t m;
+    assert(sizeof(uint16_t) == 2);
+    m = value;
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabShort(&m);
+    return (
+        TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_SHORT, 1, 2, &m));
+}
+
+static int TIFFWriteDirectoryTagCheckedShortArray(TIFF *tif, uint32_t *ndir,
+                                                  TIFFDirEntry *dir,
+                                                  uint16_t tag, uint32_t count,
+                                                  uint16_t *value)
+{
+    assert(count < 0x80000000);
+    assert(sizeof(uint16_t) == 2);
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabArrayOfShort(value, count);
+    return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_SHORT, count,
+                                      count * 2, value));
+}
+
+static int TIFFWriteDirectoryTagCheckedSshortArray(TIFF *tif, uint32_t *ndir,
+                                                   TIFFDirEntry *dir,
+                                                   uint16_t tag, uint32_t count,
+                                                   int16_t *value)
+{
+    assert(count < 0x80000000);
+    assert(sizeof(int16_t) == 2);
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabArrayOfShort((uint16_t *)value, count);
+    return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_SSHORT, count,
+                                      count * 2, value));
+}
+
+static int TIFFWriteDirectoryTagCheckedLong(TIFF *tif, uint32_t *ndir,
+                                            TIFFDirEntry *dir, uint16_t tag,
+                                            uint32_t value)
+{
+    uint32_t m;
+    assert(sizeof(uint32_t) == 4);
+    m = value;
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabLong(&m);
+    return (
+        TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_LONG, 1, 4, &m));
+}
+
+static int TIFFWriteDirectoryTagCheckedLongArray(TIFF *tif, uint32_t *ndir,
+                                                 TIFFDirEntry *dir,
+                                                 uint16_t tag, uint32_t count,
+                                                 uint32_t *value)
+{
+    assert(count < 0x40000000);
+    assert(sizeof(uint32_t) == 4);
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabArrayOfLong(value, count);
+    return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_LONG, count,
+                                      count * 4, value));
+}
+
+static int TIFFWriteDirectoryTagCheckedSlongArray(TIFF *tif, uint32_t *ndir,
+                                                  TIFFDirEntry *dir,
+                                                  uint16_t tag, uint32_t count,
+                                                  int32_t *value)
+{
+    assert(count < 0x40000000);
+    assert(sizeof(int32_t) == 4);
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabArrayOfLong((uint32_t *)value, count);
+    return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_SLONG, count,
+                                      count * 4, value));
+}
+
+static int TIFFWriteDirectoryTagCheckedLong8Array(TIFF *tif, uint32_t *ndir,
+                                                  TIFFDirEntry *dir,
+                                                  uint16_t tag, uint32_t count,
+                                                  uint64_t *value)
+{
+    assert(count < 0x20000000);
+    assert(sizeof(uint64_t) == 8);
+    if (!(tif->tif_flags & TIFF_BIGTIFF))
+    {
+        TIFFErrorExtR(tif, "TIFFWriteDirectoryTagCheckedLong8Array",
+                      "LONG8 not allowed for ClassicTIFF");
+        return (0);
+    }
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabArrayOfLong8(value, count);
+    return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_LONG8, count,
+                                      count * 8, value));
+}
+
+static int TIFFWriteDirectoryTagCheckedSlong8Array(TIFF *tif, uint32_t *ndir,
+                                                   TIFFDirEntry *dir,
+                                                   uint16_t tag, uint32_t count,
+                                                   int64_t *value)
+{
+    assert(count < 0x20000000);
+    assert(sizeof(int64_t) == 8);
+    if (!(tif->tif_flags & TIFF_BIGTIFF))
+    {
+        TIFFErrorExtR(tif, "TIFFWriteDirectoryTagCheckedSlong8Array",
+                      "SLONG8 not allowed for ClassicTIFF");
+        return (0);
+    }
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabArrayOfLong8((uint64_t *)value, count);
+    return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_SLONG8, count,
+                                      count * 8, value));
+}
+
+static int TIFFWriteDirectoryTagCheckedRational(TIFF *tif, uint32_t *ndir,
+                                                TIFFDirEntry *dir, uint16_t tag,
+                                                double value)
+{
+    static const char module[] = "TIFFWriteDirectoryTagCheckedRational";
+    uint32_t m[2];
+    assert(sizeof(uint32_t) == 4);
+    if (value < 0)
+    {
+        TIFFErrorExtR(tif, module, "Negative value is illegal");
+        return 0;
+    }
+    else if (value != value)
+    {
+        TIFFErrorExtR(tif, module, "Not-a-number value is illegal");
+        return 0;
+    }
+    /*--Rational2Double: New function also used for non-custom rational tags.
+     *  However, could be omitted here, because
+     * TIFFWriteDirectoryTagCheckedRational() is not used by code for custom
+     * tags, only by code for named-tiff-tags like FIELD_RESOLUTION and
+     * FIELD_POSITION */
+    else
+    {
+        DoubleToRational(value, &m[0], &m[1]);
+    }
+
+    if (tif->tif_flags & TIFF_SWAB)
+    {
+        TIFFSwabLong(&m[0]);
+        TIFFSwabLong(&m[1]);
+    }
+    return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_RATIONAL, 1, 8,
+                                      &m[0]));
+}
+
+static int TIFFWriteDirectoryTagCheckedRationalArray(TIFF *tif, uint32_t *ndir,
+                                                     TIFFDirEntry *dir,
+                                                     uint16_t tag,
+                                                     uint32_t count,
+                                                     float *value)
+{
+    static const char module[] = "TIFFWriteDirectoryTagCheckedRationalArray";
+    uint32_t *m;
+    float *na;
+    uint32_t *nb;
+    uint32_t nc;
+    int o;
+    assert(sizeof(uint32_t) == 4);
+    m = _TIFFmallocExt(tif, count * 2 * sizeof(uint32_t));
+    if (m == NULL)
+    {
+        TIFFErrorExtR(tif, module, "Out of memory");
+        return (0);
+    }
+    for (na = value, nb = m, nc = 0; nc < count; na++, nb += 2, nc++)
+    {
+        DoubleToRational(*na, &nb[0], &nb[1]);
+    }
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabArrayOfLong(m, count * 2);
+    o = TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_RATIONAL, count,
+                                  count * 8, &m[0]);
+    _TIFFfreeExt(tif, m);
+    return (o);
+}
+
+static int TIFFWriteDirectoryTagCheckedSrationalArray(TIFF *tif, uint32_t *ndir,
+                                                      TIFFDirEntry *dir,
+                                                      uint16_t tag,
+                                                      uint32_t count,
+                                                      float *value)
+{
+    static const char module[] = "TIFFWriteDirectoryTagCheckedSrationalArray";
+    int32_t *m;
+    float *na;
+    int32_t *nb;
+    uint32_t nc;
+    int o;
+    assert(sizeof(int32_t) == 4);
+    m = _TIFFmallocExt(tif, count * 2 * sizeof(int32_t));
+    if (m == NULL)
+    {
+        TIFFErrorExtR(tif, module, "Out of memory");
+        return (0);
+    }
+    for (na = value, nb = m, nc = 0; nc < count; na++, nb += 2, nc++)
+    {
+        DoubleToSrational(*na, &nb[0], &nb[1]);
+    }
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabArrayOfLong((uint32_t *)m, count * 2);
+    o = TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_SRATIONAL, count,
+                                  count * 8, &m[0]);
+    _TIFFfreeExt(tif, m);
+    return (o);
+}
+
+/*-- Rational2Double: additional write functions for double arrays */
+static int
+TIFFWriteDirectoryTagCheckedRationalDoubleArray(TIFF *tif, uint32_t *ndir,
+                                                TIFFDirEntry *dir, uint16_t tag,
+                                                uint32_t count, double *value)
+{
+    static const char module[] =
+        "TIFFWriteDirectoryTagCheckedRationalDoubleArray";
+    uint32_t *m;
+    double *na;
+    uint32_t *nb;
+    uint32_t nc;
+    int o;
+    assert(sizeof(uint32_t) == 4);
+    m = _TIFFmallocExt(tif, count * 2 * sizeof(uint32_t));
+    if (m == NULL)
+    {
+        TIFFErrorExtR(tif, module, "Out of memory");
+        return (0);
+    }
+    for (na = value, nb = m, nc = 0; nc < count; na++, nb += 2, nc++)
+    {
+        DoubleToRational(*na, &nb[0], &nb[1]);
+    }
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabArrayOfLong(m, count * 2);
+    o = TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_RATIONAL, count,
+                                  count * 8, &m[0]);
+    _TIFFfreeExt(tif, m);
+    return (o);
+} /*-- TIFFWriteDirectoryTagCheckedRationalDoubleArray() ------- */
+
+static int TIFFWriteDirectoryTagCheckedSrationalDoubleArray(
+    TIFF *tif, uint32_t *ndir, TIFFDirEntry *dir, uint16_t tag, uint32_t count,
+    double *value)
+{
+    static const char module[] =
+        "TIFFWriteDirectoryTagCheckedSrationalDoubleArray";
+    int32_t *m;
+    double *na;
+    int32_t *nb;
+    uint32_t nc;
+    int o;
+    assert(sizeof(int32_t) == 4);
+    m = _TIFFmallocExt(tif, count * 2 * sizeof(int32_t));
+    if (m == NULL)
+    {
+        TIFFErrorExtR(tif, module, "Out of memory");
+        return (0);
+    }
+    for (na = value, nb = m, nc = 0; nc < count; na++, nb += 2, nc++)
+    {
+        DoubleToSrational(*na, &nb[0], &nb[1]);
+    }
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabArrayOfLong((uint32_t *)m, count * 2);
+    o = TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_SRATIONAL, count,
+                                  count * 8, &m[0]);
+    _TIFFfreeExt(tif, m);
+    return (o);
+} /*--- TIFFWriteDirectoryTagCheckedSrationalDoubleArray() -------- */
+
+/** -----  Rational2Double: Double To Rational Conversion
+----------------------------------------------------------
+* There is a mathematical theorem to convert real numbers into a rational
+(integer fraction) number.
+* This is called "continuous fraction" which uses the Euclidean algorithm to
+find the greatest common divisor (GCD).
+*  (ref. e.g. https://de.wikipedia.org/wiki/Kettenbruch or
+https://en.wikipedia.org/wiki/Continued_fraction
+*             https://en.wikipedia.org/wiki/Euclidean_algorithm)
+* The following functions implement the
+* - ToRationalEuclideanGCD()		auxiliary function which mainly
+implements euclidean GCD
+* - DoubleToRational()			conversion function for un-signed
+rationals
+* - DoubleToSrational()			conversion function for signed rationals
+------------------------------------------------------------------------------------------------------------------*/
+
+/**---- ToRationalEuclideanGCD() -----------------------------------------
+* Calculates the rational fractional of a double input value
+* using the Euclidean algorithm to find the greatest common divisor (GCD)
+------------------------------------------------------------------------*/
+static void ToRationalEuclideanGCD(double value, int blnUseSignedRange,
+                                   int blnUseSmallRange, uint64_t *ullNum,
+                                   uint64_t *ullDenom)
+{
+    /* Internally, the integer variables can be bigger than the external ones,
+     * as long as the result will fit into the external variable size.
+     */
+    uint64_t numSum[3] = {0, 1, 0}, denomSum[3] = {1, 0, 0};
+    uint64_t aux, bigNum, bigDenom;
+    uint64_t returnLimit;
+    int i;
+    uint64_t nMax;
+    double fMax;
+    unsigned long maxDenom;
+    /*-- nMax and fMax defines the initial accuracy of the starting fractional,
+     *   or better, the highest used integer numbers used within the starting
+     * fractional (bigNum/bigDenom). There are two approaches, which can
+     * accidentally lead to different accuracies just depending on the value.
+     *   Therefore, blnUseSmallRange steers this behavior.
+     *   For long long nMax = ((9223372036854775807-1)/2); for long nMax =
+     * ((2147483647-1)/2);
+     */
+    if (blnUseSmallRange)
+    {
+        nMax = (uint64_t)((2147483647 - 1) / 2); /* for ULONG range */
+    }
+    else
+    {
+        nMax = ((9223372036854775807 - 1) / 2); /* for ULLONG range */
+    }
+    fMax = (double)nMax;
+
+    /*-- For the Euclidean GCD define the denominator range, so that it stays
+     * within size of unsigned long variables. maxDenom should be LONG_MAX for
+     * negative values and ULONG_MAX for positive ones. Also the final returned
+     * value of ullNum and ullDenom is limited according to signed- or
+     * unsigned-range.
+     */
+    if (blnUseSignedRange)
+    {
+        maxDenom = 2147483647UL; /*LONG_MAX = 0x7FFFFFFFUL*/
+        returnLimit = maxDenom;
+    }
+    else
+    {
+        maxDenom = 0xFFFFFFFFUL; /*ULONG_MAX = 0xFFFFFFFFUL*/
+        returnLimit = maxDenom;
+    }
+
+    /*-- First generate a rational fraction (bigNum/bigDenom) which represents
+     *the value as a rational number with the highest accuracy. Therefore,
+     *uint64_t (uint64_t) is needed. This rational fraction is then reduced
+     *using the Euclidean algorithm to find the greatest common divisor (GCD).
+     *   bigNum   = big numinator of value without fraction (or cut residual
+     *fraction) bigDenom = big denominator of value
+     *-- Break-criteria so that uint64_t cast to "bigNum" introduces no error
+     *and bigDenom has no overflow, and stop with enlargement of fraction when
+     *the double-value of it reaches an integer number without fractional part.
+     */
+    bigDenom = 1;
+    while ((value != floor(value)) && (value < fMax) && (bigDenom < nMax))
+    {
+        bigDenom <<= 1;
+        value *= 2;
+    }
+    bigNum = (uint64_t)value;
+
+    /*-- Start Euclidean algorithm to find the greatest common divisor (GCD) --
+     */
+#define MAX_ITERATIONS 64
+    for (i = 0; i < MAX_ITERATIONS; i++)
+    {
+        uint64_t val;
+        /* if bigDenom is not zero, calculate integer part of fraction. */
+        if (bigDenom == 0)
+        {
+            break;
+        }
+        val = bigNum / bigDenom;
+
+        /* Set bigDenom to reminder of bigNum/bigDenom and bigNum to previous
+         * denominator bigDenom. */
+        aux = bigNum;
+        bigNum = bigDenom;
+        bigDenom = aux % bigDenom;
+
+        /* calculate next denominator and check for its given maximum */
+        aux = val;
+        if (denomSum[1] * val + denomSum[0] >= maxDenom)
+        {
+            aux = (maxDenom - denomSum[0]) / denomSum[1];
+            if (aux * 2 >= val || denomSum[1] >= maxDenom)
+                i = (MAX_ITERATIONS +
+                     1); /* exit but execute rest of for-loop */
+            else
+                break;
+        }
+        /* calculate next numerator to numSum2 and save previous one to numSum0;
+         * numSum1 just copy of numSum2. */
+        numSum[2] = aux * numSum[1] + numSum[0];
+        numSum[0] = numSum[1];
+        numSum[1] = numSum[2];
+        /* calculate next denominator to denomSum2 and save previous one to
+         * denomSum0; denomSum1 just copy of denomSum2. */
+        denomSum[2] = aux * denomSum[1] + denomSum[0];
+        denomSum[0] = denomSum[1];
+        denomSum[1] = denomSum[2];
+    }
+
+    /*-- Check and adapt for final variable size and return values; reduces
+     * internal accuracy; denominator is kept in ULONG-range with maxDenom -- */
+    while (numSum[1] > returnLimit || denomSum[1] > returnLimit)
+    {
+        numSum[1] = numSum[1] / 2;
+        denomSum[1] = denomSum[1] / 2;
+    }
+
+    /* return values */
+    *ullNum = numSum[1];
+    *ullDenom = denomSum[1];
+
+} /*-- ToRationalEuclideanGCD() -------------- */
+
+/**---- DoubleToRational() -----------------------------------------------
+* Calculates the rational fractional of a double input value
+* for UN-SIGNED rationals,
+* using the Euclidean algorithm to find the greatest common divisor (GCD)
+------------------------------------------------------------------------*/
+static void DoubleToRational(double value, uint32_t *num, uint32_t *denom)
+{
+    /*---- UN-SIGNED RATIONAL ---- */
+    double dblDiff, dblDiff2;
+    uint64_t ullNum, ullDenom, ullNum2, ullDenom2;
+
+    /*-- Check for negative values. If so it is an error. */
+    /* Test written that way to catch NaN */
+    if (!(value >= 0))
+    {
+        *num = *denom = 0;
+        TIFFErrorExt(0, "TIFFLib: DoubleToRational()",
+                     " Negative Value for Unsigned Rational given.");
+        return;
+    }
+
+    /*-- Check for too big numbers (> ULONG_MAX) -- */
+    if (value > 0xFFFFFFFFUL)
+    {
+        *num = 0xFFFFFFFFU;
+        *denom = 0;
+        return;
+    }
+    /*-- Check for easy integer numbers -- */
+    if (value == (uint32_t)(value))
+    {
+        *num = (uint32_t)value;
+        *denom = 1;
+        return;
+    }
+    /*-- Check for too small numbers for "unsigned long" type rationals -- */
+    if (value < 1.0 / (double)0xFFFFFFFFUL)
+    {
+        *num = 0;
+        *denom = 0xFFFFFFFFU;
+        return;
+    }
+
+    /*-- There are two approaches using the Euclidean algorithm,
+     *   which can accidentally lead to different accuracies just depending on
+     * the value. Try both and define which one was better.
+     */
+    ToRationalEuclideanGCD(value, FALSE, FALSE, &ullNum, &ullDenom);
+    ToRationalEuclideanGCD(value, FALSE, TRUE, &ullNum2, &ullDenom2);
+    /*-- Double-Check, that returned values fit into ULONG :*/
+    if (ullNum > 0xFFFFFFFFUL || ullDenom > 0xFFFFFFFFUL ||
+        ullNum2 > 0xFFFFFFFFUL || ullDenom2 > 0xFFFFFFFFUL)
+    {
+        TIFFErrorExt(0, "TIFFLib: DoubleToRational()",
+                     " Num or Denom exceeds ULONG: val=%14.6f, num=%12" PRIu64
+                     ", denom=%12" PRIu64 " | num2=%12" PRIu64
+                     ", denom2=%12" PRIu64 "",
+                     value, ullNum, ullDenom, ullNum2, ullDenom2);
+        assert(0);
+    }
+
+    /* Check, which one has higher accuracy and take that. */
+    dblDiff = fabs(value - ((double)ullNum / (double)ullDenom));
+    dblDiff2 = fabs(value - ((double)ullNum2 / (double)ullDenom2));
+    if (dblDiff < dblDiff2)
+    {
+        *num = (uint32_t)ullNum;
+        *denom = (uint32_t)ullDenom;
+    }
+    else
+    {
+        *num = (uint32_t)ullNum2;
+        *denom = (uint32_t)ullDenom2;
+    }
+} /*-- DoubleToRational() -------------- */
+
+/**---- DoubleToSrational() -----------------------------------------------
+* Calculates the rational fractional of a double input value
+* for SIGNED rationals,
+* using the Euclidean algorithm to find the greatest common divisor (GCD)
+------------------------------------------------------------------------*/
+static void DoubleToSrational(double value, int32_t *num, int32_t *denom)
+{
+    /*---- SIGNED RATIONAL ----*/
+    int neg = 1;
+    double dblDiff, dblDiff2;
+    uint64_t ullNum, ullDenom, ullNum2, ullDenom2;
+
+    /*-- Check for negative values and use then the positive one for internal
+     * calculations, but take the sign into account before returning. */
+    if (value < 0)
+    {
+        neg = -1;
+        value = -value;
+    }
+
+    /*-- Check for too big numbers (> LONG_MAX) -- */
+    if (value > 0x7FFFFFFFL)
+    {
+        *num = 0x7FFFFFFFL;
+        *denom = 0;
+        return;
+    }
+    /*-- Check for easy numbers -- */
+    if (value == (int32_t)(value))
+    {
+        *num = (int32_t)(neg * value);
+        *denom = 1;
+        return;
+    }
+    /*-- Check for too small numbers for "long" type rationals -- */
+    if (value < 1.0 / (double)0x7FFFFFFFL)
+    {
+        *num = 0;
+        *denom = 0x7FFFFFFFL;
+        return;
+    }
+
+    /*-- There are two approaches using the Euclidean algorithm,
+     *   which can accidentally lead to different accuracies just depending on
+     * the value. Try both and define which one was better. Furthermore, set
+     * behavior of ToRationalEuclideanGCD() to the range of signed-long.
+     */
+    ToRationalEuclideanGCD(value, TRUE, FALSE, &ullNum, &ullDenom);
+    ToRationalEuclideanGCD(value, TRUE, TRUE, &ullNum2, &ullDenom2);
+    /*-- Double-Check, that returned values fit into LONG :*/
+    if (ullNum > 0x7FFFFFFFL || ullDenom > 0x7FFFFFFFL ||
+        ullNum2 > 0x7FFFFFFFL || ullDenom2 > 0x7FFFFFFFL)
+    {
+        TIFFErrorExt(0, "TIFFLib: DoubleToSrational()",
+                     " Num or Denom exceeds LONG: val=%14.6f, num=%12" PRIu64
+                     ", denom=%12" PRIu64 " | num2=%12" PRIu64
+                     ", denom2=%12" PRIu64 "",
+                     neg * value, ullNum, ullDenom, ullNum2, ullDenom2);
+        assert(0);
+    }
+
+    /* Check, which one has higher accuracy and take that. */
+    dblDiff = fabs(value - ((double)ullNum / (double)ullDenom));
+    dblDiff2 = fabs(value - ((double)ullNum2 / (double)ullDenom2));
+    if (dblDiff < dblDiff2)
+    {
+        *num = (int32_t)(neg * (long)ullNum);
+        *denom = (int32_t)ullDenom;
+    }
+    else
+    {
+        *num = (int32_t)(neg * (long)ullNum2);
+        *denom = (int32_t)ullDenom2;
+    }
+} /*-- DoubleToSrational() --------------*/
+
+static int TIFFWriteDirectoryTagCheckedFloatArray(TIFF *tif, uint32_t *ndir,
+                                                  TIFFDirEntry *dir,
+                                                  uint16_t tag, uint32_t count,
+                                                  float *value)
+{
+    assert(count < 0x40000000);
+    assert(sizeof(float) == 4);
+    TIFFCvtNativeToIEEEFloat(tif, count, &value);
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabArrayOfFloat(value, count);
+    return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_FLOAT, count,
+                                      count * 4, value));
+}
+
+static int TIFFWriteDirectoryTagCheckedDoubleArray(TIFF *tif, uint32_t *ndir,
+                                                   TIFFDirEntry *dir,
+                                                   uint16_t tag, uint32_t count,
+                                                   double *value)
+{
+    assert(count < 0x20000000);
+    assert(sizeof(double) == 8);
+    TIFFCvtNativeToIEEEDouble(tif, count, &value);
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabArrayOfDouble(value, count);
+    return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_DOUBLE, count,
+                                      count * 8, value));
+}
+
+static int TIFFWriteDirectoryTagCheckedIfdArray(TIFF *tif, uint32_t *ndir,
+                                                TIFFDirEntry *dir, uint16_t tag,
+                                                uint32_t count, uint32_t *value)
+{
+    assert(count < 0x40000000);
+    assert(sizeof(uint32_t) == 4);
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabArrayOfLong(value, count);
+    return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_IFD, count,
+                                      count * 4, value));
+}
+
+static int TIFFWriteDirectoryTagCheckedIfd8Array(TIFF *tif, uint32_t *ndir,
+                                                 TIFFDirEntry *dir,
+                                                 uint16_t tag, uint32_t count,
+                                                 uint64_t *value)
+{
+    assert(count < 0x20000000);
+    assert(sizeof(uint64_t) == 8);
+    assert(tif->tif_flags & TIFF_BIGTIFF);
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabArrayOfLong8(value, count);
+    return (TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_IFD8, count,
+                                      count * 8, value));
+}
+
+static int TIFFWriteDirectoryTagData(TIFF *tif, uint32_t *ndir,
+                                     TIFFDirEntry *dir, uint16_t tag,
+                                     uint16_t datatype, uint32_t count,
+                                     uint32_t datalength, void *data)
+{
+    static const char module[] = "TIFFWriteDirectoryTagData";
+    uint32_t m;
+    m = 0;
+    while (m < (*ndir))
+    {
+        assert(dir[m].tdir_tag != tag);
+        if (dir[m].tdir_tag > tag)
+            break;
+        m++;
+    }
+    if (m < (*ndir))
+    {
+        uint32_t n;
+        for (n = *ndir; n > m; n--)
+            dir[n] = dir[n - 1];
+    }
+    dir[m].tdir_tag = tag;
+    dir[m].tdir_type = datatype;
+    dir[m].tdir_count = count;
+    dir[m].tdir_offset.toff_long8 = 0;
+    if (datalength <= ((tif->tif_flags & TIFF_BIGTIFF) ? 0x8U : 0x4U))
+    {
+        if (data && datalength)
+        {
+            _TIFFmemcpy(&dir[m].tdir_offset, data, datalength);
+        }
+    }
+    else
+    {
+        uint64_t na, nb;
+        na = tif->tif_dataoff;
+        nb = na + datalength;
+        if (!(tif->tif_flags & TIFF_BIGTIFF))
+            nb = (uint32_t)nb;
+        if ((nb < na) || (nb < datalength))
+        {
+            TIFFErrorExtR(tif, module, "Maximum TIFF file size exceeded");
+            return (0);
+        }
+        if (!SeekOK(tif, na))
+        {
+            TIFFErrorExtR(tif, module, "IO error writing tag data");
+            return (0);
+        }
+        if (datalength >= 0x80000000UL)
+        {
+            TIFFErrorExtR(tif, module,
+                          "libtiff does not allow writing more than 2147483647 "
+                          "bytes in a tag");
+            return (0);
+        }
+        if (!WriteOK(tif, data, (tmsize_t)datalength))
+        {
+            TIFFErrorExtR(tif, module, "IO error writing tag data");
+            return (0);
+        }
+        tif->tif_dataoff = nb;
+        if (tif->tif_dataoff & 1)
+            tif->tif_dataoff++;
+        if (!(tif->tif_flags & TIFF_BIGTIFF))
+        {
+            uint32_t o;
+            o = (uint32_t)na;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong(&o);
+            _TIFFmemcpy(&dir[m].tdir_offset, &o, 4);
+        }
+        else
+        {
+            dir[m].tdir_offset.toff_long8 = na;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong8(&dir[m].tdir_offset.toff_long8);
+        }
+    }
+    (*ndir)++;
+    return (1);
 }
 
 /*
  * Link the current directory into the directory chain for the file.
  */
-static int
-TIFFLinkDirectory(TIFF* tif)
+static int TIFFLinkDirectory(TIFF *tif)
 {
-	static const char module[] = "TIFFLinkDirectory";
+    static const char module[] = "TIFFLinkDirectory";
 
-	tif->tif_diroff = (TIFFSeekFile(tif,0,SEEK_END)+1) & (~((toff_t)1));
+    tif->tif_diroff = (TIFFSeekFile(tif, 0, SEEK_END) + 1) & (~((toff_t)1));
 
-	/*
-	 * Handle SubIFDs
-	 */
-	if (tif->tif_flags & TIFF_INSUBIFD)
-	{
-		if (!(tif->tif_flags&TIFF_BIGTIFF))
-		{
-			uint32 m;
-			m = (uint32)tif->tif_diroff;
-			if (tif->tif_flags & TIFF_SWAB)
-				TIFFSwabLong(&m);
-			(void) TIFFSeekFile(tif, tif->tif_subifdoff, SEEK_SET);
-			if (!WriteOK(tif, &m, 4)) {
-				TIFFErrorExt(tif->tif_clientdata, module,
-				     "Error writing SubIFD directory link");
-				return (0);
-			}
-			/*
-			 * Advance to the next SubIFD or, if this is
-			 * the last one configured, revert back to the
-			 * normal directory linkage.
-			 */
-			if (--tif->tif_nsubifd)
-				tif->tif_subifdoff += 4;
-			else
-				tif->tif_flags &= ~TIFF_INSUBIFD;
-			return (1);
-		}
-		else
-		{
-			uint64 m;
-			m = tif->tif_diroff;
-			if (tif->tif_flags & TIFF_SWAB)
-				TIFFSwabLong8(&m);
-			(void) TIFFSeekFile(tif, tif->tif_subifdoff, SEEK_SET);
-			if (!WriteOK(tif, &m, 8)) {
-				TIFFErrorExt(tif->tif_clientdata, module,
-				     "Error writing SubIFD directory link");
-				return (0);
-			}
-			/*
-			 * Advance to the next SubIFD or, if this is
-			 * the last one configured, revert back to the
-			 * normal directory linkage.
-			 */
-			if (--tif->tif_nsubifd)
-				tif->tif_subifdoff += 8;
-			else
-				tif->tif_flags &= ~TIFF_INSUBIFD;
-			return (1);
-		}
-	}
+    /*
+     * Handle SubIFDs
+     */
+    if (tif->tif_flags & TIFF_INSUBIFD)
+    {
+        if (!(tif->tif_flags & TIFF_BIGTIFF))
+        {
+            uint32_t m;
+            m = (uint32_t)tif->tif_diroff;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong(&m);
+            (void)TIFFSeekFile(tif, tif->tif_subifdoff, SEEK_SET);
+            if (!WriteOK(tif, &m, 4))
+            {
+                TIFFErrorExtR(tif, module,
+                              "Error writing SubIFD directory link");
+                return (0);
+            }
+            /*
+             * Advance to the next SubIFD or, if this is
+             * the last one configured, revert back to the
+             * normal directory linkage.
+             */
+            if (--tif->tif_nsubifd)
+                tif->tif_subifdoff += 4;
+            else
+                tif->tif_flags &= ~TIFF_INSUBIFD;
+            return (1);
+        }
+        else
+        {
+            uint64_t m;
+            m = tif->tif_diroff;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong8(&m);
+            (void)TIFFSeekFile(tif, tif->tif_subifdoff, SEEK_SET);
+            if (!WriteOK(tif, &m, 8))
+            {
+                TIFFErrorExtR(tif, module,
+                              "Error writing SubIFD directory link");
+                return (0);
+            }
+            /*
+             * Advance to the next SubIFD or, if this is
+             * the last one configured, revert back to the
+             * normal directory linkage.
+             */
+            if (--tif->tif_nsubifd)
+                tif->tif_subifdoff += 8;
+            else
+                tif->tif_flags &= ~TIFF_INSUBIFD;
+            return (1);
+        }
+    }
 
-	if (!(tif->tif_flags&TIFF_BIGTIFF))
-	{
-		uint32 m;
-		uint32 nextdir;
-		m = (uint32)(tif->tif_diroff);
-		if (tif->tif_flags & TIFF_SWAB)
-			TIFFSwabLong(&m);
-		if (tif->tif_header.classic.tiff_diroff == 0) {
-			/*
-			 * First directory, overwrite offset in header.
-			 */
-			tif->tif_header.classic.tiff_diroff = (uint32) tif->tif_diroff;
-			(void) TIFFSeekFile(tif,4, SEEK_SET);
-			if (!WriteOK(tif, &m, 4)) {
-				TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-					     "Error writing TIFF header");
-				return (0);
-			}
-			return (1);
-		}
-		/*
-		 * Not the first directory, search to the last and append.
-		 */
-		nextdir = tif->tif_header.classic.tiff_diroff;
-		while(1) {
-			uint16 dircount;
-			uint32 nextnextdir;
+    if (!(tif->tif_flags & TIFF_BIGTIFF))
+    {
+        uint32_t m;
+        uint32_t nextdir;
+        m = (uint32_t)(tif->tif_diroff);
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabLong(&m);
+        if (tif->tif_header.classic.tiff_diroff == 0)
+        {
+            /*
+             * First directory, overwrite offset in header.
+             */
+            tif->tif_header.classic.tiff_diroff = (uint32_t)tif->tif_diroff;
+            tif->tif_lastdiroff = tif->tif_diroff;
+            (void)TIFFSeekFile(tif, 4, SEEK_SET);
+            if (!WriteOK(tif, &m, 4))
+            {
+                TIFFErrorExtR(tif, tif->tif_name, "Error writing TIFF header");
+                return (0);
+            }
+            return (1);
+        }
+        /*
+         * Not the first directory, search to the last and append.
+         */
+        if (tif->tif_lastdiroff != 0)
+        {
+            nextdir = (uint32_t)tif->tif_lastdiroff;
+        }
+        else
+        {
+            nextdir = tif->tif_header.classic.tiff_diroff;
+        }
 
-			if (!SeekOK(tif, nextdir) ||
-			    !ReadOK(tif, &dircount, 2)) {
-				TIFFErrorExt(tif->tif_clientdata, module,
-					     "Error fetching directory count");
-				return (0);
-			}
-			if (tif->tif_flags & TIFF_SWAB)
-				TIFFSwabShort(&dircount);
-			(void) TIFFSeekFile(tif,
-			    nextdir+2+dircount*12, SEEK_SET);
-			if (!ReadOK(tif, &nextnextdir, 4)) {
-				TIFFErrorExt(tif->tif_clientdata, module,
-					     "Error fetching directory link");
-				return (0);
-			}
-			if (tif->tif_flags & TIFF_SWAB)
-				TIFFSwabLong(&nextnextdir);
-			if (nextnextdir==0)
-			{
-				(void) TIFFSeekFile(tif,
-				    nextdir+2+dircount*12, SEEK_SET);
-				if (!WriteOK(tif, &m, 4)) {
-					TIFFErrorExt(tif->tif_clientdata, module,
-					     "Error writing directory link");
-					return (0);
-				}
-				break;
-			}
-			nextdir=nextnextdir;
-		}
-	}
-	else
-	{
-		uint64 m;
-		uint64 nextdir;
-		m = tif->tif_diroff;
-		if (tif->tif_flags & TIFF_SWAB)
-			TIFFSwabLong8(&m);
-		if (tif->tif_header.big.tiff_diroff == 0) {
-			/*
-			 * First directory, overwrite offset in header.
-			 */
-			tif->tif_header.big.tiff_diroff = tif->tif_diroff;
-			(void) TIFFSeekFile(tif,8, SEEK_SET);
-			if (!WriteOK(tif, &m, 8)) {
-				TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-					     "Error writing TIFF header");
-				return (0);
-			}
-			return (1);
-		}
-		/*
-		 * Not the first directory, search to the last and append.
-		 */
-		nextdir = tif->tif_header.big.tiff_diroff;
-		while(1) {
-			uint64 dircount64;
-			uint16 dircount;
-			uint64 nextnextdir;
+        while (1)
+        {
+            uint16_t dircount;
+            uint32_t nextnextdir;
 
-			if (!SeekOK(tif, nextdir) ||
-			    !ReadOK(tif, &dircount64, 8)) {
-				TIFFErrorExt(tif->tif_clientdata, module,
-					     "Error fetching directory count");
-				return (0);
-			}
-			if (tif->tif_flags & TIFF_SWAB)
-				TIFFSwabLong8(&dircount64);
-			if (dircount64>0xFFFF)
-			{
-				TIFFErrorExt(tif->tif_clientdata, module,
-					     "Sanity check on tag count failed, likely corrupt TIFF");
-				return (0);
-			}
-			dircount=(uint16)dircount64;
-			(void) TIFFSeekFile(tif,
-			    nextdir+8+dircount*20, SEEK_SET);
-			if (!ReadOK(tif, &nextnextdir, 8)) {
-				TIFFErrorExt(tif->tif_clientdata, module,
-					     "Error fetching directory link");
-				return (0);
-			}
-			if (tif->tif_flags & TIFF_SWAB)
-				TIFFSwabLong8(&nextnextdir);
-			if (nextnextdir==0)
-			{
-				(void) TIFFSeekFile(tif,
-				    nextdir+8+dircount*20, SEEK_SET);
-				if (!WriteOK(tif, &m, 8)) {
-					TIFFErrorExt(tif->tif_clientdata, module,
-					     "Error writing directory link");
-					return (0);
-				}
-				break;
-			}
-			nextdir=nextnextdir;
-		}
-	}
-	return (1);
+            if (!SeekOK(tif, nextdir) || !ReadOK(tif, &dircount, 2))
+            {
+                TIFFErrorExtR(tif, module, "Error fetching directory count");
+                return (0);
+            }
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabShort(&dircount);
+            (void)TIFFSeekFile(tif, nextdir + 2 + dircount * 12, SEEK_SET);
+            if (!ReadOK(tif, &nextnextdir, 4))
+            {
+                TIFFErrorExtR(tif, module, "Error fetching directory link");
+                return (0);
+            }
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong(&nextnextdir);
+            if (nextnextdir == 0)
+            {
+                (void)TIFFSeekFile(tif, nextdir + 2 + dircount * 12, SEEK_SET);
+                if (!WriteOK(tif, &m, 4))
+                {
+                    TIFFErrorExtR(tif, module, "Error writing directory link");
+                    return (0);
+                }
+                tif->tif_lastdiroff = tif->tif_diroff;
+                break;
+            }
+            nextdir = nextnextdir;
+        }
+    }
+    else
+    {
+        uint64_t m;
+        uint64_t nextdir;
+        m = tif->tif_diroff;
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabLong8(&m);
+        if (tif->tif_header.big.tiff_diroff == 0)
+        {
+            /*
+             * First directory, overwrite offset in header.
+             */
+            tif->tif_header.big.tiff_diroff = tif->tif_diroff;
+            tif->tif_lastdiroff = tif->tif_diroff;
+            (void)TIFFSeekFile(tif, 8, SEEK_SET);
+            if (!WriteOK(tif, &m, 8))
+            {
+                TIFFErrorExtR(tif, tif->tif_name, "Error writing TIFF header");
+                return (0);
+            }
+            return (1);
+        }
+        /*
+         * Not the first directory, search to the last and append.
+         */
+        if (tif->tif_lastdiroff != 0)
+        {
+            nextdir = tif->tif_lastdiroff;
+        }
+        else
+        {
+            nextdir = tif->tif_header.big.tiff_diroff;
+        }
+        while (1)
+        {
+            uint64_t dircount64;
+            uint16_t dircount;
+            uint64_t nextnextdir;
+
+            if (!SeekOK(tif, nextdir) || !ReadOK(tif, &dircount64, 8))
+            {
+                TIFFErrorExtR(tif, module, "Error fetching directory count");
+                return (0);
+            }
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong8(&dircount64);
+            if (dircount64 > 0xFFFF)
+            {
+                TIFFErrorExtR(
+                    tif, module,
+                    "Sanity check on tag count failed, likely corrupt TIFF");
+                return (0);
+            }
+            dircount = (uint16_t)dircount64;
+            (void)TIFFSeekFile(tif, nextdir + 8 + dircount * 20, SEEK_SET);
+            if (!ReadOK(tif, &nextnextdir, 8))
+            {
+                TIFFErrorExtR(tif, module, "Error fetching directory link");
+                return (0);
+            }
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong8(&nextnextdir);
+            if (nextnextdir == 0)
+            {
+                (void)TIFFSeekFile(tif, nextdir + 8 + dircount * 20, SEEK_SET);
+                if (!WriteOK(tif, &m, 8))
+                {
+                    TIFFErrorExtR(tif, module, "Error writing directory link");
+                    return (0);
+                }
+                tif->tif_lastdiroff = tif->tif_diroff;
+                break;
+            }
+            nextdir = nextnextdir;
+        }
+    }
+    return (1);
 }
 
 /************************************************************************/
@@ -2819,183 +3163,186 @@
 /*      Returns zero on failure, and one on success.                    */
 /************************************************************************/
 
-int
-_TIFFRewriteField(TIFF* tif, uint16 tag, TIFFDataType in_datatype, 
-                  tmsize_t count, void* data)
+int _TIFFRewriteField(TIFF *tif, uint16_t tag, TIFFDataType in_datatype,
+                      tmsize_t count, void *data)
 {
     static const char module[] = "TIFFResetField";
     /* const TIFFField* fip = NULL; */
-    uint16 dircount;
+    uint16_t dircount;
     tmsize_t dirsize;
-    uint8 direntry_raw[20];
-    uint16 entry_tag = 0;
-    uint16 entry_type = 0;
-    uint64 entry_count = 0;
-    uint64 entry_offset = 0;
-    int    value_in_entry = 0;
-    uint64 read_offset;
-    uint8 *buf_to_write = NULL;
+    uint8_t direntry_raw[20];
+    uint16_t entry_tag = 0;
+    uint16_t entry_type = 0;
+    uint64_t entry_count = 0;
+    uint64_t entry_offset = 0;
+    int value_in_entry = 0;
+    uint64_t read_offset;
+    uint8_t *buf_to_write = NULL;
     TIFFDataType datatype;
 
-/* -------------------------------------------------------------------- */
-/*      Find field definition.                                          */
-/* -------------------------------------------------------------------- */
-    /*fip =*/ TIFFFindField(tif, tag, TIFF_ANY);
+    /* -------------------------------------------------------------------- */
+    /*      Find field definition.                                          */
+    /* -------------------------------------------------------------------- */
+    /*fip =*/TIFFFindField(tif, tag, TIFF_ANY);
 
-/* -------------------------------------------------------------------- */
-/*      Do some checking this is a straight forward case.               */
-/* -------------------------------------------------------------------- */
-    if( isMapped(tif) )
+    /* -------------------------------------------------------------------- */
+    /*      Do some checking this is a straight forward case.               */
+    /* -------------------------------------------------------------------- */
+    if (isMapped(tif))
     {
-        TIFFErrorExt( tif->tif_clientdata, module, 
-                      "Memory mapped files not currently supported for this operation." );
+        TIFFErrorExtR(
+            tif, module,
+            "Memory mapped files not currently supported for this operation.");
         return 0;
     }
 
-    if( tif->tif_diroff == 0 )
+    if (tif->tif_diroff == 0)
     {
-        TIFFErrorExt( tif->tif_clientdata, module, 
-                      "Attempt to reset field on directory not already on disk." );
+        TIFFErrorExtR(
+            tif, module,
+            "Attempt to reset field on directory not already on disk.");
         return 0;
     }
 
-/* -------------------------------------------------------------------- */
-/*      Read the directory entry count.                                 */
-/* -------------------------------------------------------------------- */
-    if (!SeekOK(tif, tif->tif_diroff)) {
-        TIFFErrorExt(tif->tif_clientdata, module,
-                     "%s: Seek error accessing TIFF directory",
-                     tif->tif_name);
+    /* -------------------------------------------------------------------- */
+    /*      Read the directory entry count.                                 */
+    /* -------------------------------------------------------------------- */
+    if (!SeekOK(tif, tif->tif_diroff))
+    {
+        TIFFErrorExtR(tif, module, "%s: Seek error accessing TIFF directory",
+                      tif->tif_name);
         return 0;
     }
 
     read_offset = tif->tif_diroff;
 
-    if (!(tif->tif_flags&TIFF_BIGTIFF))
+    if (!(tif->tif_flags & TIFF_BIGTIFF))
     {
-        if (!ReadOK(tif, &dircount, sizeof (uint16))) {
-            TIFFErrorExt(tif->tif_clientdata, module,
-                         "%s: Can not read TIFF directory count",
-                         tif->tif_name);
+        if (!ReadOK(tif, &dircount, sizeof(uint16_t)))
+        {
+            TIFFErrorExtR(tif, module, "%s: Can not read TIFF directory count",
+                          tif->tif_name);
             return 0;
         }
         if (tif->tif_flags & TIFF_SWAB)
             TIFFSwabShort(&dircount);
         dirsize = 12;
         read_offset += 2;
-    } else {
-        uint64 dircount64;
-        if (!ReadOK(tif, &dircount64, sizeof (uint64))) {
-            TIFFErrorExt(tif->tif_clientdata, module,
-                         "%s: Can not read TIFF directory count",
-                         tif->tif_name);
+    }
+    else
+    {
+        uint64_t dircount64;
+        if (!ReadOK(tif, &dircount64, sizeof(uint64_t)))
+        {
+            TIFFErrorExtR(tif, module, "%s: Can not read TIFF directory count",
+                          tif->tif_name);
             return 0;
         }
         if (tif->tif_flags & TIFF_SWAB)
             TIFFSwabLong8(&dircount64);
-        dircount = (uint16)dircount64;
+        dircount = (uint16_t)dircount64;
         dirsize = 20;
         read_offset += 8;
     }
 
-/* -------------------------------------------------------------------- */
-/*      Read through directory to find target tag.                      */
-/* -------------------------------------------------------------------- */
-    while( dircount > 0 )
+    /* -------------------------------------------------------------------- */
+    /*      Read through directory to find target tag.                      */
+    /* -------------------------------------------------------------------- */
+    while (dircount > 0)
     {
-        if (!ReadOK(tif, direntry_raw, dirsize)) {
-            TIFFErrorExt(tif->tif_clientdata, module,
-                         "%s: Can not read TIFF directory entry.",
-                         tif->tif_name);
+        if (!ReadOK(tif, direntry_raw, dirsize))
+        {
+            TIFFErrorExtR(tif, module, "%s: Can not read TIFF directory entry.",
+                          tif->tif_name);
             return 0;
         }
 
-        memcpy( &entry_tag, direntry_raw + 0, sizeof(uint16) );
-        if (tif->tif_flags&TIFF_SWAB)
-            TIFFSwabShort( &entry_tag );
+        memcpy(&entry_tag, direntry_raw + 0, sizeof(uint16_t));
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabShort(&entry_tag);
 
-        if( entry_tag == tag )
+        if (entry_tag == tag)
             break;
 
         read_offset += dirsize;
     }
 
-    if( entry_tag != tag )
+    if (entry_tag != tag)
     {
-        TIFFErrorExt(tif->tif_clientdata, module,
-                     "%s: Could not find tag %d.",
-                     tif->tif_name, tag );
+        TIFFErrorExtR(tif, module, "%s: Could not find tag %" PRIu16 ".",
+                      tif->tif_name, tag);
         return 0;
     }
 
-/* -------------------------------------------------------------------- */
-/*      Extract the type, count and offset for this entry.              */
-/* -------------------------------------------------------------------- */
-    memcpy( &entry_type, direntry_raw + 2, sizeof(uint16) );
-    if (tif->tif_flags&TIFF_SWAB)
-        TIFFSwabShort( &entry_type );
+    /* -------------------------------------------------------------------- */
+    /*      Extract the type, count and offset for this entry.              */
+    /* -------------------------------------------------------------------- */
+    memcpy(&entry_type, direntry_raw + 2, sizeof(uint16_t));
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabShort(&entry_type);
 
-    if (!(tif->tif_flags&TIFF_BIGTIFF))
+    if (!(tif->tif_flags & TIFF_BIGTIFF))
     {
-        uint32 value;
-        
-        memcpy( &value, direntry_raw + 4, sizeof(uint32) );
-        if (tif->tif_flags&TIFF_SWAB)
-            TIFFSwabLong( &value );
+        uint32_t value;
+
+        memcpy(&value, direntry_raw + 4, sizeof(uint32_t));
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabLong(&value);
         entry_count = value;
 
-        memcpy( &value, direntry_raw + 8, sizeof(uint32) );
-        if (tif->tif_flags&TIFF_SWAB)
-            TIFFSwabLong( &value );
+        memcpy(&value, direntry_raw + 8, sizeof(uint32_t));
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabLong(&value);
         entry_offset = value;
     }
     else
     {
-        memcpy( &entry_count, direntry_raw + 4, sizeof(uint64) );
-        if (tif->tif_flags&TIFF_SWAB)
-            TIFFSwabLong8( &entry_count );
+        memcpy(&entry_count, direntry_raw + 4, sizeof(uint64_t));
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabLong8(&entry_count);
 
-        memcpy( &entry_offset, direntry_raw + 12, sizeof(uint64) );
-        if (tif->tif_flags&TIFF_SWAB)
-            TIFFSwabLong8( &entry_offset );
+        memcpy(&entry_offset, direntry_raw + 12, sizeof(uint64_t));
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabLong8(&entry_offset);
     }
 
-/* -------------------------------------------------------------------- */
-/*      When a dummy tag was written due to TIFFDeferStrileArrayWriting() */
-/* -------------------------------------------------------------------- */
-    if( entry_offset == 0 && entry_count == 0 && entry_type == 0 )
+    /* -------------------------------------------------------------------- */
+    /*      When a dummy tag was written due to TIFFDeferStrileArrayWriting() */
+    /* -------------------------------------------------------------------- */
+    if (entry_offset == 0 && entry_count == 0 && entry_type == 0)
     {
-        if( tag == TIFFTAG_TILEOFFSETS || tag == TIFFTAG_STRIPOFFSETS )
+        if (tag == TIFFTAG_TILEOFFSETS || tag == TIFFTAG_STRIPOFFSETS)
         {
-            entry_type = (tif->tif_flags&TIFF_BIGTIFF) ? TIFF_LONG8 : TIFF_LONG; 
+            entry_type =
+                (tif->tif_flags & TIFF_BIGTIFF) ? TIFF_LONG8 : TIFF_LONG;
         }
         else
         {
             int write_aslong8 = 1;
-            if( count > 1 && tag == TIFFTAG_STRIPBYTECOUNTS )
+            if (count > 1 && tag == TIFFTAG_STRIPBYTECOUNTS)
             {
                 write_aslong8 = WriteAsLong8(tif, TIFFStripSize64(tif));
             }
-            else if( count > 1 && tag == TIFFTAG_TILEBYTECOUNTS )
+            else if (count > 1 && tag == TIFFTAG_TILEBYTECOUNTS)
             {
                 write_aslong8 = WriteAsLong8(tif, TIFFTileSize64(tif));
             }
-            if( write_aslong8 )
+            if (write_aslong8)
             {
                 entry_type = TIFF_LONG8;
             }
             else
             {
                 int write_aslong4 = 1;
-                if( count > 1 && tag == TIFFTAG_STRIPBYTECOUNTS )
+                if (count > 1 && tag == TIFFTAG_STRIPBYTECOUNTS)
                 {
                     write_aslong4 = WriteAsLong4(tif, TIFFStripSize64(tif));
                 }
-                else if( count > 1 && tag == TIFFTAG_TILEBYTECOUNTS )
+                else if (count > 1 && tag == TIFFTAG_TILEBYTECOUNTS)
                 {
                     write_aslong4 = WriteAsLong4(tif, TIFFTileSize64(tif));
                 }
-                if( write_aslong4 )
+                if (write_aslong4)
                 {
                     entry_type = TIFF_LONG;
                 }
@@ -3007,123 +3354,120 @@
         }
     }
 
-/* -------------------------------------------------------------------- */
-/*      What data type do we want to write this as?                     */
-/* -------------------------------------------------------------------- */
-    if( TIFFDataWidth(in_datatype) == 8 && !(tif->tif_flags&TIFF_BIGTIFF) )
+    /* -------------------------------------------------------------------- */
+    /*      What data type do we want to write this as?                     */
+    /* -------------------------------------------------------------------- */
+    if (TIFFDataWidth(in_datatype) == 8 && !(tif->tif_flags & TIFF_BIGTIFF))
     {
-        if( in_datatype == TIFF_LONG8 )
+        if (in_datatype == TIFF_LONG8)
             datatype = entry_type == TIFF_SHORT ? TIFF_SHORT : TIFF_LONG;
-        else if( in_datatype == TIFF_SLONG8 )
+        else if (in_datatype == TIFF_SLONG8)
             datatype = TIFF_SLONG;
-        else if( in_datatype == TIFF_IFD8 )
+        else if (in_datatype == TIFF_IFD8)
             datatype = TIFF_IFD;
         else
             datatype = in_datatype;
     }
     else
     {
-        if( in_datatype == TIFF_LONG8 &&
+        if (in_datatype == TIFF_LONG8 &&
             (entry_type == TIFF_SHORT || entry_type == TIFF_LONG ||
-             entry_type == TIFF_LONG8 ) )
+             entry_type == TIFF_LONG8))
             datatype = entry_type;
-        else if( in_datatype == TIFF_SLONG8 &&
-            (entry_type == TIFF_SLONG || entry_type == TIFF_SLONG8 ) )
+        else if (in_datatype == TIFF_SLONG8 &&
+                 (entry_type == TIFF_SLONG || entry_type == TIFF_SLONG8))
             datatype = entry_type;
-        else if( in_datatype == TIFF_IFD8 &&
-            (entry_type == TIFF_IFD || entry_type == TIFF_IFD8 ) )
+        else if (in_datatype == TIFF_IFD8 &&
+                 (entry_type == TIFF_IFD || entry_type == TIFF_IFD8))
             datatype = entry_type;
         else
             datatype = in_datatype;
     }
 
-/* -------------------------------------------------------------------- */
-/*      Prepare buffer of actual data to write.  This includes          */
-/*      swabbing as needed.                                             */
-/* -------------------------------------------------------------------- */
-    buf_to_write =
-	    (uint8 *)_TIFFCheckMalloc(tif, count, TIFFDataWidth(datatype),
-				      "for field buffer.");
+    /* -------------------------------------------------------------------- */
+    /*      Prepare buffer of actual data to write.  This includes          */
+    /*      swabbing as needed.                                             */
+    /* -------------------------------------------------------------------- */
+    buf_to_write = (uint8_t *)_TIFFCheckMalloc(
+        tif, count, TIFFDataWidth(datatype), "for field buffer.");
     if (!buf_to_write)
         return 0;
 
-    if( datatype == in_datatype )
-        memcpy( buf_to_write, data, count * TIFFDataWidth(datatype) );
-    else if( datatype == TIFF_SLONG && in_datatype == TIFF_SLONG8 )
+    if (datatype == in_datatype)
+        memcpy(buf_to_write, data, count * TIFFDataWidth(datatype));
+    else if (datatype == TIFF_SLONG && in_datatype == TIFF_SLONG8)
     {
-	tmsize_t i;
+        tmsize_t i;
 
-        for( i = 0; i < count; i++ )
+        for (i = 0; i < count; i++)
         {
-            ((int32 *) buf_to_write)[i] = 
-                (int32) ((int64 *) data)[i];
-            if( (int64) ((int32 *) buf_to_write)[i] != ((int64 *) data)[i] )
+            ((int32_t *)buf_to_write)[i] = (int32_t)((int64_t *)data)[i];
+            if ((int64_t)((int32_t *)buf_to_write)[i] != ((int64_t *)data)[i])
             {
-                _TIFFfree( buf_to_write );
-                TIFFErrorExt( tif->tif_clientdata, module, 
-                              "Value exceeds 32bit range of output type." );
+                _TIFFfreeExt(tif, buf_to_write);
+                TIFFErrorExtR(tif, module,
+                              "Value exceeds 32bit range of output type.");
                 return 0;
             }
         }
     }
-    else if( (datatype == TIFF_LONG && in_datatype == TIFF_LONG8)
-             || (datatype == TIFF_IFD && in_datatype == TIFF_IFD8) )
+    else if ((datatype == TIFF_LONG && in_datatype == TIFF_LONG8) ||
+             (datatype == TIFF_IFD && in_datatype == TIFF_IFD8))
     {
-	tmsize_t i;
+        tmsize_t i;
 
-        for( i = 0; i < count; i++ )
+        for (i = 0; i < count; i++)
         {
-            ((uint32 *) buf_to_write)[i] = 
-                (uint32) ((uint64 *) data)[i];
-            if( (uint64) ((uint32 *) buf_to_write)[i] != ((uint64 *) data)[i] )
+            ((uint32_t *)buf_to_write)[i] = (uint32_t)((uint64_t *)data)[i];
+            if ((uint64_t)((uint32_t *)buf_to_write)[i] !=
+                ((uint64_t *)data)[i])
             {
-                _TIFFfree( buf_to_write );
-                TIFFErrorExt( tif->tif_clientdata, module, 
-                              "Value exceeds 32bit range of output type." );
+                _TIFFfreeExt(tif, buf_to_write);
+                TIFFErrorExtR(tif, module,
+                              "Value exceeds 32bit range of output type.");
                 return 0;
             }
         }
     }
-    else if( datatype == TIFF_SHORT && in_datatype == TIFF_LONG8 )
+    else if (datatype == TIFF_SHORT && in_datatype == TIFF_LONG8)
     {
-	tmsize_t i;
+        tmsize_t i;
 
-        for( i = 0; i < count; i++ )
+        for (i = 0; i < count; i++)
         {
-            ((uint16 *) buf_to_write)[i] =
-                (uint16) ((uint64 *) data)[i];
-            if( (uint64) ((uint16 *) buf_to_write)[i] != ((uint64 *) data)[i] )
+            ((uint16_t *)buf_to_write)[i] = (uint16_t)((uint64_t *)data)[i];
+            if ((uint64_t)((uint16_t *)buf_to_write)[i] !=
+                ((uint64_t *)data)[i])
             {
-                _TIFFfree( buf_to_write );
-                TIFFErrorExt( tif->tif_clientdata, module,
-                              "Value exceeds 16bit range of output type." );
+                _TIFFfreeExt(tif, buf_to_write);
+                TIFFErrorExtR(tif, module,
+                              "Value exceeds 16bit range of output type.");
                 return 0;
             }
         }
     }
     else
     {
-        TIFFErrorExt( tif->tif_clientdata, module,
-                      "Unhandled type conversion." );
+        TIFFErrorExtR(tif, module, "Unhandled type conversion.");
         return 0;
     }
 
-    if( TIFFDataWidth(datatype) > 1 && (tif->tif_flags&TIFF_SWAB) )
+    if (TIFFDataWidth(datatype) > 1 && (tif->tif_flags & TIFF_SWAB))
     {
-        if( TIFFDataWidth(datatype) == 2 )
-            TIFFSwabArrayOfShort( (uint16 *) buf_to_write, count );
-        else if( TIFFDataWidth(datatype) == 4 )
-            TIFFSwabArrayOfLong( (uint32 *) buf_to_write, count );
-        else if( TIFFDataWidth(datatype) == 8 )
-            TIFFSwabArrayOfLong8( (uint64 *) buf_to_write, count );
+        if (TIFFDataWidth(datatype) == 2)
+            TIFFSwabArrayOfShort((uint16_t *)buf_to_write, count);
+        else if (TIFFDataWidth(datatype) == 4)
+            TIFFSwabArrayOfLong((uint32_t *)buf_to_write, count);
+        else if (TIFFDataWidth(datatype) == 8)
+            TIFFSwabArrayOfLong8((uint64_t *)buf_to_write, count);
     }
 
-/* -------------------------------------------------------------------- */
-/*      Is this a value that fits into the directory entry?             */
-/* -------------------------------------------------------------------- */
-    if (!(tif->tif_flags&TIFF_BIGTIFF))
+    /* -------------------------------------------------------------------- */
+    /*      Is this a value that fits into the directory entry?             */
+    /* -------------------------------------------------------------------- */
+    if (!(tif->tif_flags & TIFF_BIGTIFF))
     {
-        if( TIFFDataWidth(datatype) * count <= 4 )
+        if (TIFFDataWidth(datatype) * count <= 4)
         {
             entry_offset = read_offset + 8;
             value_in_entry = 1;
@@ -3131,136 +3475,139 @@
     }
     else
     {
-        if( TIFFDataWidth(datatype) * count <= 8 )
+        if (TIFFDataWidth(datatype) * count <= 8)
         {
             entry_offset = read_offset + 12;
             value_in_entry = 1;
         }
     }
 
-    if( (tag == TIFFTAG_TILEOFFSETS || tag == TIFFTAG_STRIPOFFSETS) &&
+    if ((tag == TIFFTAG_TILEOFFSETS || tag == TIFFTAG_STRIPOFFSETS) &&
         tif->tif_dir.td_stripoffset_entry.tdir_count == 0 &&
         tif->tif_dir.td_stripoffset_entry.tdir_type == 0 &&
-        tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0 )
+        tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0)
     {
         tif->tif_dir.td_stripoffset_entry.tdir_type = datatype;
         tif->tif_dir.td_stripoffset_entry.tdir_count = count;
     }
-    else if( (tag == TIFFTAG_TILEBYTECOUNTS || tag == TIFFTAG_STRIPBYTECOUNTS) &&
-        tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 &&
-        tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 &&
-        tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0 )
+    else if ((tag == TIFFTAG_TILEBYTECOUNTS ||
+              tag == TIFFTAG_STRIPBYTECOUNTS) &&
+             tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 &&
+             tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 &&
+             tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0)
     {
         tif->tif_dir.td_stripbytecount_entry.tdir_type = datatype;
         tif->tif_dir.td_stripbytecount_entry.tdir_count = count;
     }
 
-/* -------------------------------------------------------------------- */
-/*      If the tag type, and count match, then we just write it out     */
-/*      over the old values without altering the directory entry at     */
-/*      all.                                                            */
-/* -------------------------------------------------------------------- */
-    if( entry_count == (uint64)count && entry_type == (uint16) datatype )
+    /* -------------------------------------------------------------------- */
+    /*      If the tag type, and count match, then we just write it out     */
+    /*      over the old values without altering the directory entry at     */
+    /*      all.                                                            */
+    /* -------------------------------------------------------------------- */
+    if (entry_count == (uint64_t)count && entry_type == (uint16_t)datatype)
     {
-        if (!SeekOK(tif, entry_offset)) {
-            _TIFFfree( buf_to_write );
-            TIFFErrorExt(tif->tif_clientdata, module,
-                         "%s: Seek error accessing TIFF directory",
-                         tif->tif_name);
+        if (!SeekOK(tif, entry_offset))
+        {
+            _TIFFfreeExt(tif, buf_to_write);
+            TIFFErrorExtR(tif, module,
+                          "%s: Seek error accessing TIFF directory",
+                          tif->tif_name);
             return 0;
         }
-        if (!WriteOK(tif, buf_to_write, count*TIFFDataWidth(datatype))) {
-            _TIFFfree( buf_to_write );
-            TIFFErrorExt(tif->tif_clientdata, module,
-                         "Error writing directory link");
+        if (!WriteOK(tif, buf_to_write, count * TIFFDataWidth(datatype)))
+        {
+            _TIFFfreeExt(tif, buf_to_write);
+            TIFFErrorExtR(tif, module, "Error writing directory link");
             return (0);
         }
 
-        _TIFFfree( buf_to_write );
+        _TIFFfreeExt(tif, buf_to_write);
         return 1;
     }
 
-/* -------------------------------------------------------------------- */
-/*      Otherwise, we write the new tag data at the end of the file.    */
-/* -------------------------------------------------------------------- */
-    if( !value_in_entry )
+    /* -------------------------------------------------------------------- */
+    /*      Otherwise, we write the new tag data at the end of the file.    */
+    /* -------------------------------------------------------------------- */
+    if (!value_in_entry)
     {
-        entry_offset = TIFFSeekFile(tif,0,SEEK_END);
-        
-        if (!WriteOK(tif, buf_to_write, count*TIFFDataWidth(datatype))) {
-            _TIFFfree( buf_to_write );
-            TIFFErrorExt(tif->tif_clientdata, module,
-                         "Error writing directory link");
+        entry_offset = TIFFSeekFile(tif, 0, SEEK_END);
+
+        if (!WriteOK(tif, buf_to_write, count * TIFFDataWidth(datatype)))
+        {
+            _TIFFfreeExt(tif, buf_to_write);
+            TIFFErrorExtR(tif, module, "Error writing directory link");
             return (0);
         }
     }
     else
     {
-        memcpy( &entry_offset, buf_to_write, count*TIFFDataWidth(datatype));
+        if (count * TIFFDataWidth(datatype) == 4)
+        {
+            uint32_t value;
+            memcpy(&value, buf_to_write, count * TIFFDataWidth(datatype));
+            entry_offset = value;
+        }
+        else
+        {
+            memcpy(&entry_offset, buf_to_write,
+                   count * TIFFDataWidth(datatype));
+        }
     }
 
-    _TIFFfree( buf_to_write );
+    _TIFFfreeExt(tif, buf_to_write);
     buf_to_write = 0;
 
-/* -------------------------------------------------------------------- */
-/*      Adjust the directory entry.                                     */
-/* -------------------------------------------------------------------- */
+    /* -------------------------------------------------------------------- */
+    /*      Adjust the directory entry.                                     */
+    /* -------------------------------------------------------------------- */
     entry_type = datatype;
-    entry_count = (uint64)count;
-    memcpy( direntry_raw + 2, &entry_type, sizeof(uint16) );
-    if (tif->tif_flags&TIFF_SWAB)
-        TIFFSwabShort( (uint16 *) (direntry_raw + 2) );
+    entry_count = (uint64_t)count;
+    memcpy(direntry_raw + 2, &entry_type, sizeof(uint16_t));
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabShort((uint16_t *)(direntry_raw + 2));
 
-    if (!(tif->tif_flags&TIFF_BIGTIFF))
+    if (!(tif->tif_flags & TIFF_BIGTIFF))
     {
-        uint32 value;
+        uint32_t value;
 
-        value = (uint32) entry_count;
-        memcpy( direntry_raw + 4, &value, sizeof(uint32) );
-        if (tif->tif_flags&TIFF_SWAB)
-            TIFFSwabLong( (uint32 *) (direntry_raw + 4) );
+        value = (uint32_t)entry_count;
+        memcpy(direntry_raw + 4, &value, sizeof(uint32_t));
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabLong((uint32_t *)(direntry_raw + 4));
 
-        value = (uint32) entry_offset;
-        memcpy( direntry_raw + 8, &value, sizeof(uint32) );
-        if (tif->tif_flags&TIFF_SWAB)
-            TIFFSwabLong( (uint32 *) (direntry_raw + 8) );
+        value = (uint32_t)entry_offset;
+        memcpy(direntry_raw + 8, &value, sizeof(uint32_t));
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabLong((uint32_t *)(direntry_raw + 8));
     }
     else
     {
-        memcpy( direntry_raw + 4, &entry_count, sizeof(uint64) );
-        if (tif->tif_flags&TIFF_SWAB)
-            TIFFSwabLong8( (uint64 *) (direntry_raw + 4) );
+        memcpy(direntry_raw + 4, &entry_count, sizeof(uint64_t));
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabLong8((uint64_t *)(direntry_raw + 4));
 
-        memcpy( direntry_raw + 12, &entry_offset, sizeof(uint64) );
-        if (tif->tif_flags&TIFF_SWAB)
-            TIFFSwabLong8( (uint64 *) (direntry_raw + 12) );
+        memcpy(direntry_raw + 12, &entry_offset, sizeof(uint64_t));
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabLong8((uint64_t *)(direntry_raw + 12));
     }
 
-/* -------------------------------------------------------------------- */
-/*      Write the directory entry out to disk.                          */
-/* -------------------------------------------------------------------- */
-    if (!SeekOK(tif, read_offset )) {
-        TIFFErrorExt(tif->tif_clientdata, module,
-                     "%s: Seek error accessing TIFF directory",
-                     tif->tif_name);
-        return 0;
-    }
-
-    if (!WriteOK(tif, direntry_raw,dirsize))
+    /* -------------------------------------------------------------------- */
+    /*      Write the directory entry out to disk.                          */
+    /* -------------------------------------------------------------------- */
+    if (!SeekOK(tif, read_offset))
     {
-        TIFFErrorExt(tif->tif_clientdata, module,
-                     "%s: Can not write TIFF directory entry.",
-                     tif->tif_name);
+        TIFFErrorExtR(tif, module, "%s: Seek error accessing TIFF directory",
+                      tif->tif_name);
         return 0;
     }
-    
+
+    if (!WriteOK(tif, direntry_raw, dirsize))
+    {
+        TIFFErrorExtR(tif, module, "%s: Can not write TIFF directory entry.",
+                      tif->tif_name);
+        return 0;
+    }
+
     return 1;
 }
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_dumpmode.c b/third_party/libtiff/tif_dumpmode.c
index 4a0b07f..267d5d2 100644
--- a/third_party/libtiff/tif_dumpmode.c
+++ b/third_party/libtiff/tif_dumpmode.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -29,113 +29,94 @@
  */
 #include "tiffiop.h"
 
-static int
-DumpFixupTags(TIFF* tif)
+static int DumpFixupTags(TIFF *tif)
 {
-	(void) tif;
-	return (1);
+    (void)tif;
+    return (1);
 }
 
 /*
  * Encode a hunk of pixels.
  */
-static int
-DumpModeEncode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s)
+static int DumpModeEncode(TIFF *tif, uint8_t *pp, tmsize_t cc, uint16_t s)
 {
-	(void) s;
-	while (cc > 0) {
-		tmsize_t n;
+    (void)s;
+    while (cc > 0)
+    {
+        tmsize_t n;
 
-		n = cc;
-		if (tif->tif_rawcc + n > tif->tif_rawdatasize)
-			n = tif->tif_rawdatasize - tif->tif_rawcc;
+        n = cc;
+        if (tif->tif_rawcc + n > tif->tif_rawdatasize)
+            n = tif->tif_rawdatasize - tif->tif_rawcc;
 
-		assert( n > 0 );
+        assert(n > 0);
 
-		/*
-		 * Avoid copy if client has setup raw
-		 * data buffer to avoid extra copy.
-		 */
-		if (tif->tif_rawcp != pp)
-			_TIFFmemcpy(tif->tif_rawcp, pp, n);
-		tif->tif_rawcp += n;
-		tif->tif_rawcc += n;
-		pp += n;
-		cc -= n;
-		if (tif->tif_rawcc >= tif->tif_rawdatasize &&
-		    !TIFFFlushData1(tif))
-			return (0);
-	}
-	return (1);
+        /*
+         * Avoid copy if client has setup raw
+         * data buffer to avoid extra copy.
+         */
+        if (tif->tif_rawcp != pp)
+            _TIFFmemcpy(tif->tif_rawcp, pp, n);
+        tif->tif_rawcp += n;
+        tif->tif_rawcc += n;
+        pp += n;
+        cc -= n;
+        if (tif->tif_rawcc >= tif->tif_rawdatasize && !TIFFFlushData1(tif))
+            return (0);
+    }
+    return (1);
 }
 
 /*
  * Decode a hunk of pixels.
  */
-static int
-DumpModeDecode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s)
+static int DumpModeDecode(TIFF *tif, uint8_t *buf, tmsize_t cc, uint16_t s)
 {
-	static const char module[] = "DumpModeDecode";
-	(void) s;
-	if (tif->tif_rawcc < cc) {
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-		TIFFErrorExt(tif->tif_clientdata, module,
-"Not enough data for scanline %lu, expected a request for at most %I64d bytes, got a request for %I64d bytes",
-		             (unsigned long) tif->tif_row,
-		             (signed __int64) tif->tif_rawcc,
-		             (signed __int64) cc);
-#else
-		TIFFErrorExt(tif->tif_clientdata, module,
-"Not enough data for scanline %lu, expected a request for at most %lld bytes, got a request for %lld bytes",
-		             (unsigned long) tif->tif_row,
-		             (signed long long) tif->tif_rawcc,
-		             (signed long long) cc);
-#endif
-		return (0);
-	}
-	/*
-	 * Avoid copy if client has setup raw
-	 * data buffer to avoid extra copy.
-	 */
-	if (tif->tif_rawcp != buf)
-		_TIFFmemcpy(buf, tif->tif_rawcp, cc);
-	tif->tif_rawcp += cc;
-	tif->tif_rawcc -= cc;  
-	return (1);
+    static const char module[] = "DumpModeDecode";
+    (void)s;
+    if (tif->tif_rawcc < cc)
+    {
+        TIFFErrorExtR(tif, module,
+                      "Not enough data for scanline %" PRIu32
+                      ", expected a request for at most %" TIFF_SSIZE_FORMAT
+                      " bytes, got a request for %" TIFF_SSIZE_FORMAT " bytes",
+                      tif->tif_row, tif->tif_rawcc, cc);
+        return (0);
+    }
+    /*
+     * Avoid copy if client has setup raw
+     * data buffer to avoid extra copy.
+     */
+    if (tif->tif_rawcp != buf)
+        _TIFFmemcpy(buf, tif->tif_rawcp, cc);
+    tif->tif_rawcp += cc;
+    tif->tif_rawcc -= cc;
+    return (1);
 }
 
 /*
  * Seek forwards nrows in the current strip.
  */
-static int
-DumpModeSeek(TIFF* tif, uint32 nrows)
+static int DumpModeSeek(TIFF *tif, uint32_t nrows)
 {
-	tif->tif_rawcp += nrows * tif->tif_scanlinesize;
-	tif->tif_rawcc -= nrows * tif->tif_scanlinesize;
-	return (1);
+    tif->tif_rawcp += nrows * tif->tif_scanlinesize;
+    tif->tif_rawcc -= nrows * tif->tif_scanlinesize;
+    return (1);
 }
 
 /*
  * Initialize dump mode.
  */
-int
-TIFFInitDumpMode(TIFF* tif, int scheme)
+int TIFFInitDumpMode(TIFF *tif, int scheme)
 {
-	(void) scheme;
-	tif->tif_fixuptags = DumpFixupTags;  
-	tif->tif_decoderow = DumpModeDecode;
-	tif->tif_decodestrip = DumpModeDecode;
-	tif->tif_decodetile = DumpModeDecode;
-	tif->tif_encoderow = DumpModeEncode;
-	tif->tif_encodestrip = DumpModeEncode;
-	tif->tif_encodetile = DumpModeEncode; 
-	tif->tif_seek = DumpModeSeek;
-	return (1);
+    (void)scheme;
+    tif->tif_fixuptags = DumpFixupTags;
+    tif->tif_decoderow = DumpModeDecode;
+    tif->tif_decodestrip = DumpModeDecode;
+    tif->tif_decodetile = DumpModeDecode;
+    tif->tif_encoderow = DumpModeEncode;
+    tif->tif_encodestrip = DumpModeEncode;
+    tif->tif_encodetile = DumpModeEncode;
+    tif->tif_seek = DumpModeSeek;
+    return (1);
 }
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_error.c b/third_party/libtiff/tif_error.c
index 651168f..ac0b9c3 100644
--- a/third_party/libtiff/tif_error.c
+++ b/third_party/libtiff/tif_error.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -29,58 +29,104 @@
 
 TIFFErrorHandlerExt _TIFFerrorHandlerExt = NULL;
 
-TIFFErrorHandler
-TIFFSetErrorHandler(TIFFErrorHandler handler)
+TIFFErrorHandler TIFFSetErrorHandler(TIFFErrorHandler handler)
 {
-	TIFFErrorHandler prev = _TIFFerrorHandler;
-	_TIFFerrorHandler = handler;
-	return (prev);
+    TIFFErrorHandler prev = _TIFFerrorHandler;
+    _TIFFerrorHandler = handler;
+    return (prev);
 }
 
-TIFFErrorHandlerExt
-TIFFSetErrorHandlerExt(TIFFErrorHandlerExt handler)
+TIFFErrorHandlerExt TIFFSetErrorHandlerExt(TIFFErrorHandlerExt handler)
 {
-	TIFFErrorHandlerExt prev = _TIFFerrorHandlerExt;
-	_TIFFerrorHandlerExt = handler;
-	return (prev);
+    TIFFErrorHandlerExt prev = _TIFFerrorHandlerExt;
+    _TIFFerrorHandlerExt = handler;
+    return (prev);
 }
 
-void
-TIFFError(const char* module, const char* fmt, ...)
+void TIFFError(const char *module, const char *fmt, ...)
 {
-	va_list ap;
-	if (_TIFFerrorHandler) {
-		va_start(ap, fmt);	
-		(*_TIFFerrorHandler)(module, fmt, ap);
-		va_end(ap);
-	}
-	if (_TIFFerrorHandlerExt) {
-		va_start(ap, fmt);
-		(*_TIFFerrorHandlerExt)(0, module, fmt, ap);
-		va_end(ap);
-	}
+    va_list ap;
+    if (_TIFFerrorHandler)
+    {
+        va_start(ap, fmt);
+        (*_TIFFerrorHandler)(module, fmt, ap);
+        va_end(ap);
+    }
+    if (_TIFFerrorHandlerExt)
+    {
+        va_start(ap, fmt);
+        (*_TIFFerrorHandlerExt)(0, module, fmt, ap);
+        va_end(ap);
+    }
 }
 
-void
-TIFFErrorExt(thandle_t fd, const char* module, const char* fmt, ...)
+void TIFFErrorExt(thandle_t fd, const char *module, const char *fmt, ...)
 {
-	va_list ap;
-	if (_TIFFerrorHandler) {
-		va_start(ap, fmt);
-		(*_TIFFerrorHandler)(module, fmt, ap);
-		va_end(ap);
-	}
-	if (_TIFFerrorHandlerExt) {
-		va_start(ap, fmt);
-		(*_TIFFerrorHandlerExt)(fd, module, fmt, ap);
-		va_end(ap);
-	}
+    va_list ap;
+    if (_TIFFerrorHandler)
+    {
+        va_start(ap, fmt);
+        (*_TIFFerrorHandler)(module, fmt, ap);
+        va_end(ap);
+    }
+    if (_TIFFerrorHandlerExt)
+    {
+        va_start(ap, fmt);
+        (*_TIFFerrorHandlerExt)(fd, module, fmt, ap);
+        va_end(ap);
+    }
 }
 
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
+void _TIFFErrorEarly(TIFFOpenOptions *opts, thandle_t clientdata,
+                     const char *module, const char *fmt, ...)
+{
+    va_list ap;
+    if (opts && opts->errorhandler)
+    {
+        va_start(ap, fmt);
+        int stop = opts->errorhandler(NULL, opts->errorhandler_user_data,
+                                      module, fmt, ap);
+        va_end(ap);
+        if (stop)
+            return;
+    }
+    if (_TIFFerrorHandler)
+    {
+        va_start(ap, fmt);
+        (*_TIFFerrorHandler)(module, fmt, ap);
+        va_end(ap);
+    }
+    if (_TIFFerrorHandlerExt)
+    {
+        va_start(ap, fmt);
+        (*_TIFFerrorHandlerExt)(clientdata, module, fmt, ap);
+        va_end(ap);
+    }
+}
+
+void TIFFErrorExtR(TIFF *tif, const char *module, const char *fmt, ...)
+{
+    va_list ap;
+    if (tif && tif->tif_errorhandler)
+    {
+        va_start(ap, fmt);
+        int stop = (*tif->tif_errorhandler)(
+            tif, tif->tif_errorhandler_user_data, module, fmt, ap);
+        va_end(ap);
+        if (stop)
+            return;
+    }
+    if (_TIFFerrorHandler)
+    {
+        va_start(ap, fmt);
+        (*_TIFFerrorHandler)(module, fmt, ap);
+        va_end(ap);
+    }
+    if (_TIFFerrorHandlerExt)
+    {
+        va_start(ap, fmt);
+        (*_TIFFerrorHandlerExt)(tif ? tif->tif_clientdata : NULL, module, fmt,
+                                ap);
+        va_end(ap);
+    }
+}
diff --git a/third_party/libtiff/tif_extension.c b/third_party/libtiff/tif_extension.c
index 87d3cfc..1a09e98 100644
--- a/third_party/libtiff/tif_extension.c
+++ b/third_party/libtiff/tif_extension.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -26,26 +26,26 @@
  * TIFF Library.
  *
  * Various routines support external extension of the tag set, and other
- * application extension capabilities. 
+ * application extension capabilities.
  */
 
 #include "tiffiop.h"
 
-int TIFFGetTagListCount( TIFF *tif )
+int TIFFGetTagListCount(TIFF *tif)
 
 {
-    TIFFDirectory* td = &tif->tif_dir;
-    
+    TIFFDirectory *td = &tif->tif_dir;
+
     return td->td_customValueCount;
 }
 
-uint32 TIFFGetTagListEntry( TIFF *tif, int tag_index )
+uint32_t TIFFGetTagListEntry(TIFF *tif, int tag_index)
 
 {
-    TIFFDirectory* td = &tif->tif_dir;
+    TIFFDirectory *td = &tif->tif_dir;
 
-    if( tag_index < 0 || tag_index >= td->td_customValueCount )
-        return (uint32)(-1);
+    if (tag_index < 0 || tag_index >= td->td_customValueCount)
+        return (uint32_t)(-1);
     else
         return td->td_customValues[tag_index].info->field_tag;
 }
@@ -55,27 +55,27 @@
 ** structure to application code without giving access to the private
 ** TIFF structure.
 */
-TIFFTagMethods *TIFFAccessTagMethods( TIFF *tif )
+TIFFTagMethods *TIFFAccessTagMethods(TIFF *tif)
 
 {
     return &(tif->tif_tagmethods);
 }
 
-void *TIFFGetClientInfo( TIFF *tif, const char *name )
+void *TIFFGetClientInfo(TIFF *tif, const char *name)
 
 {
     TIFFClientInfoLink *psLink = tif->tif_clientinfo;
 
-    while( psLink != NULL && strcmp(psLink->name,name) != 0 )
+    while (psLink != NULL && strcmp(psLink->name, name) != 0)
         psLink = psLink->next;
 
-    if( psLink != NULL )
+    if (psLink != NULL)
         return psLink->data;
     else
         return NULL;
 }
 
-void TIFFSetClientInfo( TIFF *tif, void *data, const char *name )
+void TIFFSetClientInfo(TIFF *tif, void *data, const char *name)
 
 {
     TIFFClientInfoLink *psLink = tif->tif_clientinfo;
@@ -84,10 +84,10 @@
     ** Do we have an existing link with this name?  If so, just
     ** set it.
     */
-    while( psLink != NULL && strcmp(psLink->name,name) != 0 )
+    while (psLink != NULL && strcmp(psLink->name, name) != 0)
         psLink = psLink->next;
 
-    if( psLink != NULL )
+    if (psLink != NULL)
     {
         psLink->data = data;
         return;
@@ -97,20 +97,14 @@
     ** Create a new link.
     */
 
-    psLink = (TIFFClientInfoLink *) _TIFFmalloc(sizeof(TIFFClientInfoLink));
-    assert (psLink != NULL);
+    psLink =
+        (TIFFClientInfoLink *)_TIFFmallocExt(tif, sizeof(TIFFClientInfoLink));
+    assert(psLink != NULL);
     psLink->next = tif->tif_clientinfo;
-    psLink->name = (char *) _TIFFmalloc((tmsize_t)(strlen(name)+1));
-    assert (psLink->name != NULL);
+    psLink->name = (char *)_TIFFmallocExt(tif, (tmsize_t)(strlen(name) + 1));
+    assert(psLink->name != NULL);
     strcpy(psLink->name, name);
     psLink->data = data;
 
     tif->tif_clientinfo = psLink;
 }
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_fax3.c b/third_party/libtiff/tif_fax3.c
index d11c968..a3c645c 100644
--- a/third_party/libtiff/tif_fax3.c
+++ b/third_party/libtiff/tif_fax3.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1990-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -37,7 +37,7 @@
  *      Copyright (C) 1990, 1995  Frank D. Cringle.
  */
 #include "tif_fax3.h"
-#define	G3CODES
+#define G3CODES
 #include "t4.h"
 #include <stdio.h>
 
@@ -45,50 +45,57 @@
  * Compression+decompression state blocks are
  * derived from this ``base state'' block.
  */
-typedef struct {
-	int      rw_mode;                /* O_RDONLY for decode, else encode */
-	int      mode;                   /* operating mode */
-	tmsize_t rowbytes;               /* bytes in a decoded scanline */
-	uint32   rowpixels;              /* pixels in a scanline */
+typedef struct
+{
+    int rw_mode;        /* O_RDONLY for decode, else encode */
+    int mode;           /* operating mode */
+    tmsize_t rowbytes;  /* bytes in a decoded scanline */
+    uint32_t rowpixels; /* pixels in a scanline */
 
-	uint16   cleanfaxdata;           /* CleanFaxData tag */
-	uint32   badfaxrun;              /* BadFaxRun tag */
-	uint32   badfaxlines;            /* BadFaxLines tag */
-	uint32   groupoptions;           /* Group 3/4 options tag */
+    uint16_t cleanfaxdata; /* CleanFaxData tag */
+    uint32_t badfaxrun;    /* BadFaxRun tag */
+    uint32_t badfaxlines;  /* BadFaxLines tag */
+    uint32_t groupoptions; /* Group 3/4 options tag */
 
-	TIFFVGetMethod  vgetparent;      /* super-class method */
-	TIFFVSetMethod  vsetparent;      /* super-class method */
-	TIFFPrintMethod printdir;        /* super-class method */
+    TIFFVGetMethod vgetparent; /* super-class method */
+    TIFFVSetMethod vsetparent; /* super-class method */
+    TIFFPrintMethod printdir;  /* super-class method */
 } Fax3BaseState;
-#define	Fax3State(tif)		((Fax3BaseState*) (tif)->tif_data)
+#define Fax3State(tif) ((Fax3BaseState *)(tif)->tif_data)
 
-typedef enum { G3_1D, G3_2D } Ttag;
-typedef struct {
-	Fax3BaseState b;
+typedef enum
+{
+    G3_1D,
+    G3_2D
+} Ttag;
+typedef struct
+{
+    Fax3BaseState b;
 
-	/* Decoder state info */
-	const unsigned char* bitmap;	/* bit reversal table */
-	uint32	data;			/* current i/o byte/word */
-	int	bit;			/* current i/o bit in byte */
-	int	EOLcnt;			/* count of EOL codes recognized */
-	TIFFFaxFillFunc fill;		/* fill routine */
-	uint32*	runs;			/* b&w runs for current/previous row */
-	uint32*	refruns;		/* runs for reference line */
-	uint32*	curruns;		/* runs for current line */
+    /* Decoder state info */
+    const unsigned char *bitmap; /* bit reversal table */
+    uint32_t data;               /* current i/o byte/word */
+    int bit;                     /* current i/o bit in byte */
+    int EOLcnt;                  /* count of EOL codes recognized */
+    TIFFFaxFillFunc fill;        /* fill routine */
+    uint32_t *runs;              /* b&w runs for current/previous row */
+    uint32_t nruns;              /* size of the refruns / curruns arrays */
+    uint32_t *refruns;           /* runs for reference line */
+    uint32_t *curruns;           /* runs for current line */
 
-	/* Encoder state info */
-	Ttag    tag;			/* encoding state */
-	unsigned char*	refline;	/* reference line for 2d decoding */
-	int	k;			/* #rows left that can be 2d encoded */
-	int	maxk;			/* max #rows that can be 2d encoded */
+    /* Encoder state info */
+    Ttag tag;               /* encoding state */
+    unsigned char *refline; /* reference line for 2d decoding */
+    int k;                  /* #rows left that can be 2d encoded */
+    int maxk;               /* max #rows that can be 2d encoded */
 
-	int line;
+    int line;
 } Fax3CodecState;
-#define DecoderState(tif) ((Fax3CodecState*) Fax3State(tif))
-#define EncoderState(tif) ((Fax3CodecState*) Fax3State(tif))
+#define DecoderState(tif) ((Fax3CodecState *)Fax3State(tif))
+#define EncoderState(tif) ((Fax3CodecState *)Fax3State(tif))
 
 #define is2DEncoding(sp) (sp->b.groupoptions & GROUP3OPT_2DENCODING)
-#define isAligned(p,t) ((((size_t)(p)) & (sizeof (t)-1)) == 0)
+#define isAligned(p, t) ((((size_t)(p)) & (sizeof(t) - 1)) == 0)
 
 /*
  * Group 3 and Group 4 Decoding.
@@ -98,74 +105,81 @@
  * These macros glue the TIFF library state to
  * the state expected by Frank's decoder.
  */
-#define	DECLARE_STATE(tif, sp, mod)					\
-    static const char module[] = mod;					\
-    Fax3CodecState* sp = DecoderState(tif);				\
-    int a0;				/* reference element */		\
-    int lastx = sp->b.rowpixels;	/* last element in row */	\
-    uint32 BitAcc;			/* bit accumulator */		\
-    int BitsAvail;			/* # valid bits in BitAcc */	\
-    int RunLength;			/* length of current run */	\
-    unsigned char* cp;			/* next byte of input data */	\
-    unsigned char* ep;			/* end of input data */		\
-    uint32* pa;				/* place to stuff next run */	\
-    uint32* thisrun;			/* current row's run array */	\
-    int EOLcnt;				/* # EOL codes recognized */	\
-    const unsigned char* bitmap = sp->bitmap;	/* input data bit reverser */	\
-    const TIFFFaxTabEnt* TabEnt
-#define	DECLARE_STATE_2D(tif, sp, mod)					\
-    DECLARE_STATE(tif, sp, mod);					\
-    int b1;				/* next change on prev line */	\
-    uint32* pb				/* next run in reference line */\
-/*
- * Load any state that may be changed during decoding.
- */
-#define	CACHE_STATE(tif, sp) do {					\
-    BitAcc = sp->data;							\
-    BitsAvail = sp->bit;						\
-    EOLcnt = sp->EOLcnt;						\
-    cp = (unsigned char*) tif->tif_rawcp;				\
-    ep = cp + tif->tif_rawcc;						\
-} while (0)
+#define DECLARE_STATE(tif, sp, mod)                                            \
+    static const char module[] = mod;                                          \
+    Fax3CodecState *sp = DecoderState(tif);                                    \
+    int a0;                                   /* reference element */          \
+    int lastx = sp->b.rowpixels;              /* last element in row */        \
+    uint32_t BitAcc;                          /* bit accumulator */            \
+    int BitsAvail;                            /* # valid bits in BitAcc */     \
+    int RunLength;                            /* length of current run */      \
+    unsigned char *cp;                        /* next byte of input data */    \
+    unsigned char *ep;                        /* end of input data */          \
+    uint32_t *pa;                             /* place to stuff next run */    \
+    uint32_t *thisrun;                        /* current row's run array */    \
+    int EOLcnt;                               /* # EOL codes recognized */     \
+    const unsigned char *bitmap = sp->bitmap; /* input data bit reverser */    \
+    const TIFFFaxTabEnt *TabEnt
+#define DECLARE_STATE_2D(tif, sp, mod)                                         \
+    DECLARE_STATE(tif, sp, mod);                                               \
+    int b1; /* next change on prev line */                                     \
+    uint32_t                                                                   \
+        *pb /* next run in reference line */ /*                                \
+                                              * Load any state that may be     \
+                                              * changed during decoding.       \
+                                              */
+#define CACHE_STATE(tif, sp)                                                   \
+    do                                                                         \
+    {                                                                          \
+        BitAcc = sp->data;                                                     \
+        BitsAvail = sp->bit;                                                   \
+        EOLcnt = sp->EOLcnt;                                                   \
+        cp = (unsigned char *)tif->tif_rawcp;                                  \
+        ep = cp + tif->tif_rawcc;                                              \
+    } while (0)
 /*
  * Save state possibly changed during decoding.
  */
-#define	UNCACHE_STATE(tif, sp) do {					\
-    sp->bit = BitsAvail;						\
-    sp->data = BitAcc;							\
-    sp->EOLcnt = EOLcnt;						\
-    tif->tif_rawcc -= (tmsize_t)((uint8*) cp - tif->tif_rawcp);		\
-    tif->tif_rawcp = (uint8*) cp;					\
-} while (0)
+#define UNCACHE_STATE(tif, sp)                                                 \
+    do                                                                         \
+    {                                                                          \
+        sp->bit = BitsAvail;                                                   \
+        sp->data = BitAcc;                                                     \
+        sp->EOLcnt = EOLcnt;                                                   \
+        tif->tif_rawcc -= (tmsize_t)((uint8_t *)cp - tif->tif_rawcp);          \
+        tif->tif_rawcp = (uint8_t *)cp;                                        \
+    } while (0)
 
 /*
  * Setup state for decoding a strip.
  */
-static int
-Fax3PreDecode(TIFF* tif, uint16 s)
+static int Fax3PreDecode(TIFF *tif, uint16_t s)
 {
-	Fax3CodecState* sp = DecoderState(tif);
+    Fax3CodecState *sp = DecoderState(tif);
 
-	(void) s;
-	assert(sp != NULL);
-	sp->bit = 0;			/* force initial read */
-	sp->data = 0;
-	sp->EOLcnt = 0;			/* force initial scan for EOL */
-	/*
-	 * Decoder assumes lsb-to-msb bit order.  Note that we select
-	 * this here rather than in Fax3SetupState so that viewers can
-	 * hold the image open, fiddle with the FillOrder tag value,
-	 * and then re-decode the image.  Otherwise they'd need to close
-	 * and open the image to get the state reset.
-	 */
-	sp->bitmap =
-	    TIFFGetBitRevTable(tif->tif_dir.td_fillorder != FILLORDER_LSB2MSB);
-	if (sp->refruns) {		/* init reference line to white */
-		sp->refruns[0] = (uint32) sp->b.rowpixels;
-		sp->refruns[1] = 0;
-	}
-	sp->line = 0;
-	return (1);
+    (void)s;
+    assert(sp != NULL);
+    sp->bit = 0; /* force initial read */
+    sp->data = 0;
+    sp->EOLcnt = 0; /* force initial scan for EOL */
+    /*
+     * Decoder assumes lsb-to-msb bit order.  Note that we select
+     * this here rather than in Fax3SetupState so that viewers can
+     * hold the image open, fiddle with the FillOrder tag value,
+     * and then re-decode the image.  Otherwise they'd need to close
+     * and open the image to get the state reset.
+     */
+    sp->bitmap =
+        TIFFGetBitRevTable(tif->tif_dir.td_fillorder != FILLORDER_LSB2MSB);
+    sp->curruns = sp->runs;
+    if (sp->refruns)
+    { /* init reference line to white */
+        sp->refruns = sp->runs + sp->nruns;
+        sp->refruns[0] = (uint32_t)sp->b.rowpixels;
+        sp->refruns[1] = 0;
+    }
+    sp->line = 0;
+    return (1);
 }
 
 /*
@@ -174,320 +188,295 @@
  * overriding the definitions used by the decoder.
  */
 
-static void
-Fax3Unexpected(const char* module, TIFF* tif, uint32 line, uint32 a0)
+static void Fax3Unexpected(const char *module, TIFF *tif, uint32_t line,
+                           uint32_t a0)
 {
-	TIFFErrorExt(tif->tif_clientdata, module, "Bad code word at line %u of %s %u (x %u)",
-	    line, isTiled(tif) ? "tile" : "strip",
-	    (isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip),
-	    a0);
+    TIFFErrorExtR(tif, module,
+                  "Bad code word at line %" PRIu32 " of %s %" PRIu32
+                  " (x %" PRIu32 ")",
+                  line, isTiled(tif) ? "tile" : "strip",
+                  (isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip), a0);
 }
-#define	unexpected(table, a0)	Fax3Unexpected(module, tif, sp->line, a0)
+#define unexpected(table, a0) Fax3Unexpected(module, tif, sp->line, a0)
 
-static void
-Fax3Extension(const char* module, TIFF* tif, uint32 line, uint32 a0)
+static void Fax3Extension(const char *module, TIFF *tif, uint32_t line,
+                          uint32_t a0)
 {
-	TIFFErrorExt(tif->tif_clientdata, module,
-	    "Uncompressed data (not supported) at line %u of %s %u (x %u)",
-	    line, isTiled(tif) ? "tile" : "strip",
-	    (isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip),
-	    a0);
+    TIFFErrorExtR(tif, module,
+                  "Uncompressed data (not supported) at line %" PRIu32
+                  " of %s %" PRIu32 " (x %" PRIu32 ")",
+                  line, isTiled(tif) ? "tile" : "strip",
+                  (isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip), a0);
 }
-#define	extension(a0)	Fax3Extension(module, tif, sp->line, a0)
+#define extension(a0) Fax3Extension(module, tif, sp->line, a0)
 
-static void
-Fax3BadLength(const char* module, TIFF* tif, uint32 line, uint32 a0, uint32 lastx)
+static void Fax3BadLength(const char *module, TIFF *tif, uint32_t line,
+                          uint32_t a0, uint32_t lastx)
 {
-	TIFFWarningExt(tif->tif_clientdata, module, "%s at line %u of %s %u (got %u, expected %u)",
-	    a0 < lastx ? "Premature EOL" : "Line length mismatch",
-	    line, isTiled(tif) ? "tile" : "strip",
-	    (isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip),
-	    a0, lastx);
+    TIFFWarningExtR(tif, module,
+                    "%s at line %" PRIu32 " of %s %" PRIu32 " (got %" PRIu32
+                    ", expected %" PRIu32 ")",
+                    a0 < lastx ? "Premature EOL" : "Line length mismatch", line,
+                    isTiled(tif) ? "tile" : "strip",
+                    (isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip), a0,
+                    lastx);
 }
-#define	badlength(a0,lastx)	Fax3BadLength(module, tif, sp->line, a0, lastx)
+#define badlength(a0, lastx) Fax3BadLength(module, tif, sp->line, a0, lastx)
 
-static void
-Fax3PrematureEOF(const char* module, TIFF* tif, uint32 line, uint32 a0)
+static void Fax3PrematureEOF(const char *module, TIFF *tif, uint32_t line,
+                             uint32_t a0)
 {
-	TIFFWarningExt(tif->tif_clientdata, module, "Premature EOF at line %u of %s %u (x %u)",
-	    line, isTiled(tif) ? "tile" : "strip",
-	    (isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip),
-	    a0);
+    TIFFWarningExtR(tif, module,
+                    "Premature EOF at line %" PRIu32 " of %s %" PRIu32
+                    " (x %" PRIu32 ")",
+                    line, isTiled(tif) ? "tile" : "strip",
+                    (isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip), a0);
 }
-#define	prematureEOF(a0)	Fax3PrematureEOF(module, tif, sp->line, a0)
+#define prematureEOF(a0) Fax3PrematureEOF(module, tif, sp->line, a0)
 
-#define	Nop
+#define Nop
 
-/*
+/**
  * Decode the requested amount of G3 1D-encoded data.
+ * @param buf destination buffer
+ * @param occ available bytes in destination buffer
+ * @param s number of planes (ignored)
+ * @returns 1 for success, -1 in case of error
  */
-static int
-Fax3Decode1D(TIFF* tif, uint8* buf, tmsize_t occ, uint16 s)
+static int Fax3Decode1D(TIFF *tif, uint8_t *buf, tmsize_t occ, uint16_t s)
 {
-	DECLARE_STATE(tif, sp, "Fax3Decode1D");
-	(void) s;
-	if (occ % sp->b.rowbytes)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "Fractional scanlines cannot be read");
-		return (-1);
-	}
-	CACHE_STATE(tif, sp);
-	thisrun = sp->curruns;
-	while (occ > 0) {
-		a0 = 0;
-		RunLength = 0;
-		pa = thisrun;
+    DECLARE_STATE(tif, sp, "Fax3Decode1D");
+    (void)s;
+    if (occ % sp->b.rowbytes)
+    {
+        TIFFErrorExtR(tif, module, "Fractional scanlines cannot be read");
+        return (-1);
+    }
+    CACHE_STATE(tif, sp);
+    thisrun = sp->curruns;
+    while (occ > 0)
+    {
+        a0 = 0;
+        RunLength = 0;
+        pa = thisrun;
 #ifdef FAX3_DEBUG
-		printf("\nBitAcc=%08X, BitsAvail = %d\n", BitAcc, BitsAvail);
-		printf("-------------------- %d\n", tif->tif_row);
-		fflush(stdout);
+        printf("\nBitAcc=%08" PRIX32 ", BitsAvail = %d\n", BitAcc, BitsAvail);
+        printf("-------------------- %" PRIu32 "\n", tif->tif_row);
+        fflush(stdout);
 #endif
-		SYNC_EOL(EOF1D);
-		EXPAND1D(EOF1Da);
-		(*sp->fill)(buf, thisrun, pa, lastx);
-		buf += sp->b.rowbytes;
-		occ -= sp->b.rowbytes;
-		sp->line++;
-		continue;
-	EOF1D:				/* premature EOF */
-		CLEANUP_RUNS();
-	EOF1Da:				/* premature EOF */
-		(*sp->fill)(buf, thisrun, pa, lastx);
-		UNCACHE_STATE(tif, sp);
-		return (-1);
-	}
-	UNCACHE_STATE(tif, sp);
-	return (1);
+        SYNC_EOL(EOF1D);
+        EXPAND1D(EOF1Da);
+        (*sp->fill)(buf, thisrun, pa, lastx);
+        buf += sp->b.rowbytes;
+        occ -= sp->b.rowbytes;
+        sp->line++;
+        continue;
+    EOF1D: /* premature EOF */
+        CLEANUP_RUNS();
+    EOF1Da: /* premature EOF */
+        (*sp->fill)(buf, thisrun, pa, lastx);
+        UNCACHE_STATE(tif, sp);
+        return (-1);
+    }
+    UNCACHE_STATE(tif, sp);
+    return (1);
 }
 
-#define	SWAP(t,a,b)	{ t x; x = (a); (a) = (b); (b) = x; }
+#define SWAP(t, a, b)                                                          \
+    {                                                                          \
+        t x;                                                                   \
+        x = (a);                                                               \
+        (a) = (b);                                                             \
+        (b) = x;                                                               \
+    }
 /*
  * Decode the requested amount of G3 2D-encoded data.
  */
-static int
-Fax3Decode2D(TIFF* tif, uint8* buf, tmsize_t occ, uint16 s)
+static int Fax3Decode2D(TIFF *tif, uint8_t *buf, tmsize_t occ, uint16_t s)
 {
-	DECLARE_STATE_2D(tif, sp, "Fax3Decode2D");
-	int is1D;			/* current line is 1d/2d-encoded */
-	(void) s;
-	if (occ % sp->b.rowbytes)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "Fractional scanlines cannot be read");
-		return (-1);
-	}
-	CACHE_STATE(tif, sp);
-	while (occ > 0) {
-		a0 = 0;
-		RunLength = 0;
-		pa = thisrun = sp->curruns;
+    DECLARE_STATE_2D(tif, sp, "Fax3Decode2D");
+    int is1D; /* current line is 1d/2d-encoded */
+    (void)s;
+    if (occ % sp->b.rowbytes)
+    {
+        TIFFErrorExtR(tif, module, "Fractional scanlines cannot be read");
+        return (-1);
+    }
+    CACHE_STATE(tif, sp);
+    while (occ > 0)
+    {
+        a0 = 0;
+        RunLength = 0;
+        pa = thisrun = sp->curruns;
 #ifdef FAX3_DEBUG
-		printf("\nBitAcc=%08X, BitsAvail = %d EOLcnt = %d",
-		    BitAcc, BitsAvail, EOLcnt);
+        printf("\nBitAcc=%08" PRIX32 ", BitsAvail = %d EOLcnt = %d", BitAcc,
+               BitsAvail, EOLcnt);
 #endif
-		SYNC_EOL(EOF2D);
-		NeedBits8(1, EOF2D);
-		is1D = GetBits(1);	/* 1D/2D-encoding tag bit */
-		ClrBits(1);
+        SYNC_EOL(EOF2D);
+        NeedBits8(1, EOF2D);
+        is1D = GetBits(1); /* 1D/2D-encoding tag bit */
+        ClrBits(1);
 #ifdef FAX3_DEBUG
-		printf(" %s\n-------------------- %d\n",
-		    is1D ? "1D" : "2D", tif->tif_row);
-		fflush(stdout);
+        printf(" %s\n-------------------- %" PRIu32 "\n", is1D ? "1D" : "2D",
+               tif->tif_row);
+        fflush(stdout);
 #endif
-		pb = sp->refruns;
-		b1 = *pb++;
-		if (is1D)
-			EXPAND1D(EOF2Da);
-		else
-			EXPAND2D(EOF2Da);
-		(*sp->fill)(buf, thisrun, pa, lastx);
-		SETVALUE(0);		/* imaginary change for reference */
-		SWAP(uint32*, sp->curruns, sp->refruns);
-		buf += sp->b.rowbytes;
-		occ -= sp->b.rowbytes;
-		sp->line++;
-		continue;
-	EOF2D:				/* premature EOF */
-		CLEANUP_RUNS();
-	EOF2Da:				/* premature EOF */
-		(*sp->fill)(buf, thisrun, pa, lastx);
-		UNCACHE_STATE(tif, sp);
-		return (-1);
-	}
-	UNCACHE_STATE(tif, sp);
-	return (1);
+        pb = sp->refruns;
+        b1 = *pb++;
+        if (is1D)
+            EXPAND1D(EOF2Da);
+        else
+            EXPAND2D(EOF2Da);
+        (*sp->fill)(buf, thisrun, pa, lastx);
+        if (pa < thisrun + sp->nruns)
+        {
+            SETVALUE(0); /* imaginary change for reference */
+        }
+        SWAP(uint32_t *, sp->curruns, sp->refruns);
+        buf += sp->b.rowbytes;
+        occ -= sp->b.rowbytes;
+        sp->line++;
+        continue;
+    EOF2D: /* premature EOF */
+        CLEANUP_RUNS();
+    EOF2Da: /* premature EOF */
+        (*sp->fill)(buf, thisrun, pa, lastx);
+        UNCACHE_STATE(tif, sp);
+        return (-1);
+    }
+    UNCACHE_STATE(tif, sp);
+    return (1);
 }
 #undef SWAP
 
-/*
- * The ZERO & FILL macros must handle spans < 2*sizeof(long) bytes.
- * For machines with 64-bit longs this is <16 bytes; otherwise
- * this is <8 bytes.  We optimize the code here to reflect the
- * machine characteristics.
- */
-#if SIZEOF_UNSIGNED_LONG == 8
-# define FILL(n, cp)							    \
-    switch (n) {							    \
-    case 15:(cp)[14] = 0xff; /*-fallthrough*/ \
-    case 14:(cp)[13] = 0xff; /*-fallthrough*/ \
-    case 13:(cp)[12] = 0xff; /*-fallthrough*/ \
-    case 12:(cp)[11] = 0xff; /*-fallthrough*/ \
-    case 11:(cp)[10] = 0xff; /*-fallthrough*/ \
-    case 10: (cp)[9] = 0xff; /*-fallthrough*/ \
-    case  9: (cp)[8] = 0xff; /*-fallthrough*/ \
-    case  8: (cp)[7] = 0xff; /*-fallthrough*/ \
-    case  7: (cp)[6] = 0xff; /*-fallthrough*/ \
-    case  6: (cp)[5] = 0xff; /*-fallthrough*/ \
-    case  5: (cp)[4] = 0xff; /*-fallthrough*/ \
-    case  4: (cp)[3] = 0xff; /*-fallthrough*/ \
-    case  3: (cp)[2] = 0xff; /*-fallthrough*/ \
-    case  2: (cp)[1] = 0xff; /*-fallthrough*/ \
-    case  1: (cp)[0] = 0xff; (cp) += (n); /*-fallthrough*/ \
-    case 0:  ;			      \
-    }
-# define ZERO(n, cp)							\
-    switch (n) {							\
-    case 15:(cp)[14] = 0; /*-fallthrough*/ \
-    case 14:(cp)[13] = 0; /*-fallthrough*/ \
-    case 13:(cp)[12] = 0; /*-fallthrough*/ \
-    case 12:(cp)[11] = 0; /*-fallthrough*/ \
-    case 11:(cp)[10] = 0; /*-fallthrough*/ \
-    case 10: (cp)[9] = 0; /*-fallthrough*/ \
-    case  9: (cp)[8] = 0; /*-fallthrough*/ \
-    case  8: (cp)[7] = 0; /*-fallthrough*/ \
-    case  7: (cp)[6] = 0; /*-fallthrough*/ \
-    case  6: (cp)[5] = 0; /*-fallthrough*/ \
-    case  5: (cp)[4] = 0; /*-fallthrough*/ \
-    case  4: (cp)[3] = 0; /*-fallthrough*/ \
-    case  3: (cp)[2] = 0; /*-fallthrough*/ \
-    case  2: (cp)[1] = 0; /*-fallthrough*/ \
-    case  1: (cp)[0] = 0; (cp) += (n); /*-fallthrough*/ \
-    case 0:  ;			\
-    }
-#else
-# define FILL(n, cp)							    \
-    switch (n) {							    \
-    case 7: (cp)[6] = 0xff; /*-fallthrough*/ \
-    case 6: (cp)[5] = 0xff; /*-fallthrough*/ \
-    case 5: (cp)[4] = 0xff; /*-fallthrough*/ \
-    case 4: (cp)[3] = 0xff; /*-fallthrough*/ \
-    case 3: (cp)[2] = 0xff; /*-fallthrough*/ \
-    case 2: (cp)[1] = 0xff; /*-fallthrough*/ \
-    case 1: (cp)[0] = 0xff; (cp) += (n);  /*-fallthrough*/ \
-    case 0:  ;			    \
-    }
-# define ZERO(n, cp)							\
-    switch (n) {							\
-    case 7: (cp)[6] = 0; /*-fallthrough*/ \
-    case 6: (cp)[5] = 0; /*-fallthrough*/ \
-    case 5: (cp)[4] = 0; /*-fallthrough*/ \
-    case 4: (cp)[3] = 0; /*-fallthrough*/ \
-    case 3: (cp)[2] = 0; /*-fallthrough*/ \
-    case 2: (cp)[1] = 0; /*-fallthrough*/ \
-    case 1: (cp)[0] = 0; (cp) += (n); /*-fallthrough*/ \
-    case 0:  ;			\
-    }
-#endif
+#define FILL(n, cp)                                                            \
+    for (int32_t ifill = 0; ifill < (n); ++ifill)                              \
+    {                                                                          \
+        (cp)[ifill] = 0xff;                                                    \
+    }                                                                          \
+    (cp) += (n);
+
+#define ZERO(n, cp)                                                            \
+    for (int32_t izero = 0; izero < (n); ++izero)                              \
+    {                                                                          \
+        (cp)[izero] = 0;                                                       \
+    }                                                                          \
+    (cp) += (n);
 
 /*
  * Bit-fill a row according to the white/black
  * runs generated during G3/G4 decoding.
  */
-void
-_TIFFFax3fillruns(unsigned char* buf, uint32* runs, uint32* erun, uint32 lastx)
+void _TIFFFax3fillruns(unsigned char *buf, uint32_t *runs, uint32_t *erun,
+                       uint32_t lastx)
 {
-	static const unsigned char _fillmasks[] =
-	    { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
-	unsigned char* cp;
-	uint32 x, bx, run;
-	int32 n, nw;
-	long* lp;
+    static const unsigned char _fillmasks[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
+                                               0xf8, 0xfc, 0xfe, 0xff};
+    unsigned char *cp;
+    uint32_t x, bx, run;
+    int32_t n, nw;
+    int64_t *lp;
 
-	if ((erun-runs)&1)
-	    *erun++ = 0;
-	x = 0;
-	for (; runs < erun; runs += 2) {
-	    run = runs[0];
-	    if (x+run > lastx || run > lastx )
-		run = runs[0] = (uint32) (lastx - x);
-	    if (run) {
-		cp = buf + (x>>3);
-		bx = x&7;
-		if (run > 8-bx) {
-		    if (bx) {			/* align to byte boundary */
-			*cp++ &= 0xff << (8-bx);
-			run -= 8-bx;
-		    }
-		    if( (n = run >> 3) != 0 ) {	/* multiple bytes to fill */
-			if ((n/sizeof (long)) > 1) {
-			    /*
-			     * Align to longword boundary and fill.
-			     */
-			    for (; n && !isAligned(cp, long); n--)
-				    *cp++ = 0x00;
-			    lp = (long*) cp;
-			    nw = (int32)(n / sizeof (long));
-			    n -= nw * sizeof (long);
-			    do {
-				    *lp++ = 0L;
-			    } while (--nw);
-			    cp = (unsigned char*) lp;
-			}
-			ZERO(n, cp);
-			run &= 7;
-		    }
-		    if (run)
-			cp[0] &= 0xff >> run;
-		} else
-		    cp[0] &= ~(_fillmasks[run]>>bx);
-		x += runs[0];
-	    }
-	    run = runs[1];
-	    if (x+run > lastx || run > lastx )
-		run = runs[1] = lastx - x;
-	    if (run) {
-		cp = buf + (x>>3);
-		bx = x&7;
-		if (run > 8-bx) {
-		    if (bx) {			/* align to byte boundary */
-			*cp++ |= 0xff >> bx;
-			run -= 8-bx;
-		    }
-		    if( (n = run>>3) != 0 ) {	/* multiple bytes to fill */
-			if ((n/sizeof (long)) > 1) {
-			    /*
-			     * Align to longword boundary and fill.
-			     */
-			    for (; n && !isAligned(cp, long); n--)
-				*cp++ = 0xff;
-			    lp = (long*) cp;
-			    nw = (int32)(n / sizeof (long));
-			    n -= nw * sizeof (long);
-			    do {
-				*lp++ = -1L;
-			    } while (--nw);
-			    cp = (unsigned char*) lp;
-			}
-			FILL(n, cp);
-			run &= 7;
-		    }
-                    /* Explicit 0xff masking to make icc -check=conversions happy */
-		    if (run)
-			cp[0] = (unsigned char)((cp[0] | (0xff00 >> run))&0xff);
-		} else
-		    cp[0] |= _fillmasks[run]>>bx;
-		x += runs[1];
-	    }
-	}
-	assert(x == lastx);
+    if ((erun - runs) & 1)
+        *erun++ = 0;
+    x = 0;
+    for (; runs < erun; runs += 2)
+    {
+        run = runs[0];
+        if (x + run > lastx || run > lastx)
+            run = runs[0] = (uint32_t)(lastx - x);
+        if (run)
+        {
+            cp = buf + (x >> 3);
+            bx = x & 7;
+            if (run > 8 - bx)
+            {
+                if (bx)
+                { /* align to byte boundary */
+                    *cp++ &= 0xff << (8 - bx);
+                    run -= 8 - bx;
+                }
+                if ((n = run >> 3) != 0)
+                { /* multiple bytes to fill */
+                    if ((n / sizeof(int64_t)) > 1)
+                    {
+                        /*
+                         * Align to int64_tword boundary and fill.
+                         */
+                        for (; n && !isAligned(cp, int64_t); n--)
+                            *cp++ = 0x00;
+                        lp = (int64_t *)cp;
+                        nw = (int32_t)(n / sizeof(int64_t));
+                        n -= nw * sizeof(int64_t);
+                        do
+                        {
+                            *lp++ = 0L;
+                        } while (--nw);
+                        cp = (unsigned char *)lp;
+                    }
+                    ZERO(n, cp);
+                    run &= 7;
+                }
+                if (run)
+                    cp[0] &= 0xff >> run;
+            }
+            else
+                cp[0] &= ~(_fillmasks[run] >> bx);
+            x += runs[0];
+        }
+        run = runs[1];
+        if (x + run > lastx || run > lastx)
+            run = runs[1] = lastx - x;
+        if (run)
+        {
+            cp = buf + (x >> 3);
+            bx = x & 7;
+            if (run > 8 - bx)
+            {
+                if (bx)
+                { /* align to byte boundary */
+                    *cp++ |= 0xff >> bx;
+                    run -= 8 - bx;
+                }
+                if ((n = run >> 3) != 0)
+                { /* multiple bytes to fill */
+                    if ((n / sizeof(int64_t)) > 1)
+                    {
+                        /*
+                         * Align to int64_t boundary and fill.
+                         */
+                        for (; n && !isAligned(cp, int64_t); n--)
+                            *cp++ = 0xff;
+                        lp = (int64_t *)cp;
+                        nw = (int32_t)(n / sizeof(int64_t));
+                        n -= nw * sizeof(int64_t);
+                        do
+                        {
+                            *lp++ = -1L;
+                        } while (--nw);
+                        cp = (unsigned char *)lp;
+                    }
+                    FILL(n, cp);
+                    run &= 7;
+                }
+                /* Explicit 0xff masking to make icc -check=conversions happy */
+                if (run)
+                    cp[0] = (unsigned char)((cp[0] | (0xff00 >> run)) & 0xff);
+            }
+            else
+                cp[0] |= _fillmasks[run] >> bx;
+            x += runs[1];
+        }
+    }
+    assert(x == lastx);
 }
-#undef	ZERO
-#undef	FILL
+#undef ZERO
+#undef FILL
 
-static int
-Fax3FixupTags(TIFF* tif)
+static int Fax3FixupTags(TIFF *tif)
 {
-	(void) tif;
-	return (1);
+    (void)tif;
+    return (1);
 }
 
 /*
@@ -497,163 +486,188 @@
  * or not decoding or encoding is being done and whether
  * 1D- or 2D-encoded data is involved.
  */
-static int
-Fax3SetupState(TIFF* tif)
+static int Fax3SetupState(TIFF *tif)
 {
-	static const char module[] = "Fax3SetupState";
-	TIFFDirectory* td = &tif->tif_dir;
-	Fax3BaseState* sp = Fax3State(tif);
-	int needsRefLine;
-	Fax3CodecState* dsp = (Fax3CodecState*) Fax3State(tif);
-	tmsize_t rowbytes;
-	uint32 rowpixels, nruns;
+    static const char module[] = "Fax3SetupState";
+    TIFFDirectory *td = &tif->tif_dir;
+    Fax3BaseState *sp = Fax3State(tif);
+    int needsRefLine;
+    Fax3CodecState *dsp = (Fax3CodecState *)Fax3State(tif);
+    tmsize_t rowbytes;
+    uint32_t rowpixels;
 
-	if (td->td_bitspersample != 1) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "Bits/sample must be 1 for Group 3/4 encoding/decoding");
-		return (0);
-	}
-	/*
-	 * Calculate the scanline/tile widths.
-	 */
-	if (isTiled(tif)) {
-		rowbytes = TIFFTileRowSize(tif);
-		rowpixels = td->td_tilewidth;
-	} else {
-		rowbytes = TIFFScanlineSize(tif);
-		rowpixels = td->td_imagewidth;
-	}
-	sp->rowbytes = rowbytes;
-	sp->rowpixels = rowpixels;
-	/*
-	 * Allocate any additional space required for decoding/encoding.
-	 */
-	needsRefLine = (
-	    (sp->groupoptions & GROUP3OPT_2DENCODING) ||
-	    td->td_compression == COMPRESSION_CCITTFAX4
-	);
+    if (td->td_bitspersample != 1)
+    {
+        TIFFErrorExtR(tif, module,
+                      "Bits/sample must be 1 for Group 3/4 encoding/decoding");
+        return (0);
+    }
+    /*
+     * Calculate the scanline/tile widths.
+     */
+    if (isTiled(tif))
+    {
+        rowbytes = TIFFTileRowSize(tif);
+        rowpixels = td->td_tilewidth;
+    }
+    else
+    {
+        rowbytes = TIFFScanlineSize(tif);
+        rowpixels = td->td_imagewidth;
+    }
+    if ((int64_t)rowbytes < ((int64_t)rowpixels + 7) / 8)
+    {
+        TIFFErrorExtR(tif, module,
+                      "Inconsistent number of bytes per row : rowbytes=%" PRId64
+                      " rowpixels=%" PRIu32,
+                      (int64_t)rowbytes, rowpixels);
+        return (0);
+    }
+    sp->rowbytes = rowbytes;
+    sp->rowpixels = rowpixels;
+    /*
+     * Allocate any additional space required for decoding/encoding.
+     */
+    needsRefLine = ((sp->groupoptions & GROUP3OPT_2DENCODING) ||
+                    td->td_compression == COMPRESSION_CCITTFAX4);
 
-	/*
-	  Assure that allocation computations do not overflow.
-	  
-	  TIFFroundup and TIFFSafeMultiply return zero on integer overflow
-	*/
-	dsp->runs=(uint32*) NULL;
-	nruns = TIFFroundup_32(rowpixels,32);
-	if (needsRefLine) {
-		nruns = TIFFSafeMultiply(uint32,nruns,2);
-	}
-	if ((nruns == 0) || (TIFFSafeMultiply(uint32,nruns,2) == 0)) {
-		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-			     "Row pixels integer overflow (rowpixels %u)",
-			     rowpixels);
-		return (0);
-	}
-	dsp->runs = (uint32*) _TIFFCheckMalloc(tif,
-					       TIFFSafeMultiply(uint32,nruns,2),
-					       sizeof (uint32),
-					       "for Group 3/4 run arrays");
-	if (dsp->runs == NULL)
-		return (0);
-	memset( dsp->runs, 0, TIFFSafeMultiply(uint32,nruns,2)*sizeof(uint32));
-	dsp->curruns = dsp->runs;
-	if (needsRefLine)
-		dsp->refruns = dsp->runs + nruns;
-	else
-		dsp->refruns = NULL;
-	if (td->td_compression == COMPRESSION_CCITTFAX3
-	    && is2DEncoding(dsp)) {	/* NB: default is 1D routine */
-		tif->tif_decoderow = Fax3Decode2D;
-		tif->tif_decodestrip = Fax3Decode2D;
-		tif->tif_decodetile = Fax3Decode2D;
-	}
+    /*
+      Assure that allocation computations do not overflow.
 
-	if (needsRefLine) {		/* 2d encoding */
-		Fax3CodecState* esp = EncoderState(tif);
-		/*
-		 * 2d encoding requires a scanline
-		 * buffer for the ``reference line''; the
-		 * scanline against which delta encoding
-		 * is referenced.  The reference line must
-		 * be initialized to be ``white'' (done elsewhere).
-		 */
-		esp->refline = (unsigned char*) _TIFFmalloc(rowbytes);
-		if (esp->refline == NULL) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "No space for Group 3/4 reference line");
-			return (0);
-		}
-	} else					/* 1d encoding */
-		EncoderState(tif)->refline = NULL;
+      TIFFroundup and TIFFSafeMultiply return zero on integer overflow
+    */
+    dsp->runs = (uint32_t *)NULL;
+    dsp->nruns = TIFFroundup_32(rowpixels + 1, 32);
+    if (needsRefLine)
+    {
+        dsp->nruns = TIFFSafeMultiply(uint32_t, dsp->nruns, 2);
+    }
+    if ((dsp->nruns == 0) || (TIFFSafeMultiply(uint32_t, dsp->nruns, 2) == 0))
+    {
+        TIFFErrorExtR(tif, tif->tif_name,
+                      "Row pixels integer overflow (rowpixels %" PRIu32 ")",
+                      rowpixels);
+        return (0);
+    }
+    dsp->runs = (uint32_t *)_TIFFCheckMalloc(
+        tif, TIFFSafeMultiply(uint32_t, dsp->nruns, 2), sizeof(uint32_t),
+        "for Group 3/4 run arrays");
+    if (dsp->runs == NULL)
+        return (0);
+    memset(dsp->runs, 0,
+           TIFFSafeMultiply(uint32_t, dsp->nruns, 2) * sizeof(uint32_t));
+    dsp->curruns = dsp->runs;
+    if (needsRefLine)
+        dsp->refruns = dsp->runs + dsp->nruns;
+    else
+        dsp->refruns = NULL;
+    if (td->td_compression == COMPRESSION_CCITTFAX3 && is2DEncoding(dsp))
+    { /* NB: default is 1D routine */
+        tif->tif_decoderow = Fax3Decode2D;
+        tif->tif_decodestrip = Fax3Decode2D;
+        tif->tif_decodetile = Fax3Decode2D;
+    }
 
-	return (1);
+    if (needsRefLine)
+    { /* 2d encoding */
+        Fax3CodecState *esp = EncoderState(tif);
+        /*
+         * 2d encoding requires a scanline
+         * buffer for the ``reference line''; the
+         * scanline against which delta encoding
+         * is referenced.  The reference line must
+         * be initialized to be ``white'' (done elsewhere).
+         */
+        esp->refline = (unsigned char *)_TIFFmallocExt(tif, rowbytes);
+        if (esp->refline == NULL)
+        {
+            TIFFErrorExtR(tif, module, "No space for Group 3/4 reference line");
+            return (0);
+        }
+    }
+    else /* 1d encoding */
+        EncoderState(tif)->refline = NULL;
+
+    return (1);
 }
 
 /*
  * CCITT Group 3 FAX Encoding.
  */
 
-#define	Fax3FlushBits(tif, sp) {				\
-	if ((tif)->tif_rawcc >= (tif)->tif_rawdatasize)		\
-		(void) TIFFFlushData1(tif);			\
-	*(tif)->tif_rawcp++ = (uint8) (sp)->data;		\
-	(tif)->tif_rawcc++;					\
-	(sp)->data = 0, (sp)->bit = 8;				\
-}
-#define	_FlushBits(tif) {					\
-	if ((tif)->tif_rawcc >= (tif)->tif_rawdatasize)		\
-		(void) TIFFFlushData1(tif);			\
-	*(tif)->tif_rawcp++ = (uint8) data;		\
-	(tif)->tif_rawcc++;					\
-	data = 0, bit = 8;					\
-}
-static const int _msbmask[9] =
-    { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
-#define	_PutBits(tif, bits, length) {				\
-	while (length > bit) {					\
-		data |= bits >> (length - bit);			\
-		length -= bit;					\
-		_FlushBits(tif);				\
-	}							\
-        assert( length < 9 );                                   \
-	data |= (bits & _msbmask[length]) << (bit - length);	\
-	bit -= length;						\
-	if (bit == 0)						\
-		_FlushBits(tif);				\
-}
-	
+#define Fax3FlushBits(tif, sp)                                                 \
+    {                                                                          \
+        if ((tif)->tif_rawcc >= (tif)->tif_rawdatasize)                        \
+        {                                                                      \
+            if (!TIFFFlushData1(tif))                                          \
+                return 0;                                                      \
+        }                                                                      \
+        *(tif)->tif_rawcp++ = (uint8_t)(sp)->data;                             \
+        (tif)->tif_rawcc++;                                                    \
+        (sp)->data = 0, (sp)->bit = 8;                                         \
+    }
+#define _FlushBits(tif)                                                        \
+    {                                                                          \
+        if ((tif)->tif_rawcc >= (tif)->tif_rawdatasize)                        \
+        {                                                                      \
+            if (!TIFFFlushData1(tif))                                          \
+                return 0;                                                      \
+        }                                                                      \
+        *(tif)->tif_rawcp++ = (uint8_t)data;                                   \
+        (tif)->tif_rawcc++;                                                    \
+        data = 0, bit = 8;                                                     \
+    }
+static const int _msbmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f,
+                                0x1f, 0x3f, 0x7f, 0xff};
+#define _PutBits(tif, bits, length)                                            \
+    {                                                                          \
+        while (length > bit)                                                   \
+        {                                                                      \
+            data |= bits >> (length - bit);                                    \
+            length -= bit;                                                     \
+            _FlushBits(tif);                                                   \
+        }                                                                      \
+        assert(length < 9);                                                    \
+        data |= (bits & _msbmask[length]) << (bit - length);                   \
+        bit -= length;                                                         \
+        if (bit == 0)                                                          \
+            _FlushBits(tif);                                                   \
+    }
+
 /*
  * Write a variable-length bit-value to
  * the output stream.  Values are
  * assumed to be at most 16 bits.
  */
-static void
-Fax3PutBits(TIFF* tif, unsigned int bits, unsigned int length)
+static int Fax3PutBits(TIFF *tif, unsigned int bits, unsigned int length)
 {
-	Fax3CodecState* sp = EncoderState(tif);
-	unsigned int bit = sp->bit;
-	int data = sp->data;
+    Fax3CodecState *sp = EncoderState(tif);
+    unsigned int bit = sp->bit;
+    int data = sp->data;
 
-	_PutBits(tif, bits, length);
+    _PutBits(tif, bits, length);
 
-	sp->data = data;
-	sp->bit = bit;
+    sp->data = data;
+    sp->bit = bit;
+    return 1;
 }
 
 /*
  * Write a code to the output stream.
  */
-#define putcode(tif, te)	Fax3PutBits(tif, (te)->code, (te)->length)
+#define putcode(tif, te) Fax3PutBits(tif, (te)->code, (te)->length)
 
 #ifdef FAX3_DEBUG
-#define	DEBUG_COLOR(w) (tab == TIFFFaxWhiteCodes ? w "W" : w "B")
-#define	DEBUG_PRINT(what,len) {						\
-    int t;								\
-    printf("%08X/%-2d: %s%5d\t", data, bit, DEBUG_COLOR(what), len);	\
-    for (t = length-1; t >= 0; t--)					\
-	putchar(code & (1<<t) ? '1' : '0');				\
-    putchar('\n');							\
-}
+#define DEBUG_COLOR(w) (tab == TIFFFaxWhiteCodes ? w "W" : w "B")
+#define DEBUG_PRINT(what, len)                                                 \
+    {                                                                          \
+        int t;                                                                 \
+        printf("%08" PRIX32 "/%-2d: %s%5d\t", data, bit, DEBUG_COLOR(what),    \
+               len);                                                           \
+        for (t = length - 1; t >= 0; t--)                                      \
+            putchar(code & (1 << t) ? '1' : '0');                              \
+        putchar('\n');                                                         \
+    }
 #endif
 
 /*
@@ -662,44 +676,47 @@
  * appropriate table that holds the make-up and
  * terminating codes is supplied.
  */
-static void
-putspan(TIFF* tif, int32 span, const tableentry* tab)
+static int putspan(TIFF *tif, int32_t span, const tableentry *tab)
 {
-	Fax3CodecState* sp = EncoderState(tif);
-	unsigned int bit = sp->bit;
-	int data = sp->data;
-	unsigned int code, length;
+    Fax3CodecState *sp = EncoderState(tif);
+    unsigned int bit = sp->bit;
+    int data = sp->data;
+    unsigned int code, length;
 
-	while (span >= 2624) {
-		const tableentry* te = &tab[63 + (2560>>6)];
-		code = te->code;
-		length = te->length;
+    while (span >= 2624)
+    {
+        const tableentry *te = &tab[63 + (2560 >> 6)];
+        code = te->code;
+        length = te->length;
 #ifdef FAX3_DEBUG
-		DEBUG_PRINT("MakeUp", te->runlen);
+        DEBUG_PRINT("MakeUp", te->runlen);
 #endif
-		_PutBits(tif, code, length);
-		span -= te->runlen;
-	}
-	if (span >= 64) {
-		const tableentry* te = &tab[63 + (span>>6)];
-		assert(te->runlen == 64*(span>>6));
-		code = te->code;
-		length = te->length;
+        _PutBits(tif, code, length);
+        span -= te->runlen;
+    }
+    if (span >= 64)
+    {
+        const tableentry *te = &tab[63 + (span >> 6)];
+        assert(te->runlen == 64 * (span >> 6));
+        code = te->code;
+        length = te->length;
 #ifdef FAX3_DEBUG
-		DEBUG_PRINT("MakeUp", te->runlen);
+        DEBUG_PRINT("MakeUp", te->runlen);
 #endif
-		_PutBits(tif, code, length);
-		span -= te->runlen;
-	}
-	code = tab[span].code;
-	length = tab[span].length;
+        _PutBits(tif, code, length);
+        span -= te->runlen;
+    }
+    code = tab[span].code;
+    length = tab[span].length;
 #ifdef FAX3_DEBUG
-	DEBUG_PRINT("  Term", tab[span].runlen);
+    DEBUG_PRINT("  Term", tab[span].runlen);
 #endif
-	_PutBits(tif, code, length);
+    _PutBits(tif, code, length);
 
-	sp->data = data;
-	sp->bit = bit;
+    sp->data = data;
+    sp->bit = bit;
+
+    return 1;
 }
 
 /*
@@ -708,258 +725,266 @@
  * here.  We also handle writing the tag bit for the next
  * scanline when doing 2d encoding.
  */
-static void
-Fax3PutEOL(TIFF* tif)
+static int Fax3PutEOL(TIFF *tif)
 {
-	Fax3CodecState* sp = EncoderState(tif);
-	unsigned int bit = sp->bit;
-	int data = sp->data;
-	unsigned int code, length, tparm;
+    Fax3CodecState *sp = EncoderState(tif);
+    unsigned int bit = sp->bit;
+    int data = sp->data;
+    unsigned int code, length, tparm;
 
-	if (sp->b.groupoptions & GROUP3OPT_FILLBITS) {
-		/*
-		 * Force bit alignment so EOL will terminate on
-		 * a byte boundary.  That is, force the bit alignment
-		 * to 16-12 = 4 before putting out the EOL code.
-		 */
-		int align = 8 - 4;
-		if (align != sp->bit) {
-			if (align > sp->bit)
-				align = sp->bit + (8 - align);
-			else
-				align = sp->bit - align;
-			tparm=align; 
-			_PutBits(tif, 0, tparm);
-		}
-	}
-	code = EOL;
-	length = 12;
-	if (is2DEncoding(sp)) {
-		code = (code<<1) | (sp->tag == G3_1D);
-		length++;
-	}
-	_PutBits(tif, code, length);
+    if (sp->b.groupoptions & GROUP3OPT_FILLBITS)
+    {
+        /*
+         * Force bit alignment so EOL will terminate on
+         * a byte boundary.  That is, force the bit alignment
+         * to 16-12 = 4 before putting out the EOL code.
+         */
+        int align = 8 - 4;
+        if (align != sp->bit)
+        {
+            if (align > sp->bit)
+                align = sp->bit + (8 - align);
+            else
+                align = sp->bit - align;
+            tparm = align;
+            _PutBits(tif, 0, tparm);
+        }
+    }
+    code = EOL;
+    length = 12;
+    if (is2DEncoding(sp))
+    {
+        code = (code << 1) | (sp->tag == G3_1D);
+        length++;
+    }
+    _PutBits(tif, code, length);
 
-	sp->data = data;
-	sp->bit = bit;
+    sp->data = data;
+    sp->bit = bit;
+
+    return 1;
 }
 
 /*
  * Reset encoding state at the start of a strip.
  */
-static int
-Fax3PreEncode(TIFF* tif, uint16 s)
+static int Fax3PreEncode(TIFF *tif, uint16_t s)
 {
-	Fax3CodecState* sp = EncoderState(tif);
+    Fax3CodecState *sp = EncoderState(tif);
 
-	(void) s;
-	assert(sp != NULL);
-	sp->bit = 8;
-	sp->data = 0;
-	sp->tag = G3_1D;
-	/*
-	 * This is necessary for Group 4; otherwise it isn't
-	 * needed because the first scanline of each strip ends
-	 * up being copied into the refline.
-	 */
-	if (sp->refline)
-		_TIFFmemset(sp->refline, 0x00, sp->b.rowbytes);
-	if (is2DEncoding(sp)) {
-		float res = tif->tif_dir.td_yresolution;
-		/*
-		 * The CCITT spec says that when doing 2d encoding, you
-		 * should only do it on K consecutive scanlines, where K
-		 * depends on the resolution of the image being encoded
-		 * (2 for <= 200 lpi, 4 for > 200 lpi).  Since the directory
-		 * code initializes td_yresolution to 0, this code will
-		 * select a K of 2 unless the YResolution tag is set
-		 * appropriately.  (Note also that we fudge a little here
-		 * and use 150 lpi to avoid problems with units conversion.)
-		 */
-		if (tif->tif_dir.td_resolutionunit == RESUNIT_CENTIMETER)
-			res *= 2.54f;		/* convert to inches */
-		sp->maxk = (res > 150 ? 4 : 2);
-		sp->k = sp->maxk-1;
-	} else
-		sp->k = sp->maxk = 0;
-	sp->line = 0;
-	return (1);
+    (void)s;
+    assert(sp != NULL);
+    sp->bit = 8;
+    sp->data = 0;
+    sp->tag = G3_1D;
+    /*
+     * This is necessary for Group 4; otherwise it isn't
+     * needed because the first scanline of each strip ends
+     * up being copied into the refline.
+     */
+    if (sp->refline)
+        _TIFFmemset(sp->refline, 0x00, sp->b.rowbytes);
+    if (is2DEncoding(sp))
+    {
+        float res = tif->tif_dir.td_yresolution;
+        /*
+         * The CCITT spec says that when doing 2d encoding, you
+         * should only do it on K consecutive scanlines, where K
+         * depends on the resolution of the image being encoded
+         * (2 for <= 200 lpi, 4 for > 200 lpi).  Since the directory
+         * code initializes td_yresolution to 0, this code will
+         * select a K of 2 unless the YResolution tag is set
+         * appropriately.  (Note also that we fudge a little here
+         * and use 150 lpi to avoid problems with units conversion.)
+         */
+        if (tif->tif_dir.td_resolutionunit == RESUNIT_CENTIMETER)
+            res *= 2.54f; /* convert to inches */
+        sp->maxk = (res > 150 ? 4 : 2);
+        sp->k = sp->maxk - 1;
+    }
+    else
+        sp->k = sp->maxk = 0;
+    sp->line = 0;
+    return (1);
 }
 
 static const unsigned char zeroruns[256] = {
-    8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,	/* 0x00 - 0x0f */
-    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,	/* 0x10 - 0x1f */
-    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,	/* 0x20 - 0x2f */
-    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,	/* 0x30 - 0x3f */
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x40 - 0x4f */
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x50 - 0x5f */
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x60 - 0x6f */
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x70 - 0x7f */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x80 - 0x8f */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x90 - 0x9f */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xa0 - 0xaf */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xb0 - 0xbf */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xc0 - 0xcf */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xd0 - 0xdf */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xe0 - 0xef */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xf0 - 0xff */
+    8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */
+    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1f */
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 - 0x2f */
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 - 0x3f */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x50 - 0x5f */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x70 - 0x7f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 - 0xff */
 };
 static const unsigned char oneruns[256] = {
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x00 - 0x0f */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x10 - 0x1f */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x20 - 0x2f */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x30 - 0x3f */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x40 - 0x4f */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x50 - 0x5f */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x60 - 0x6f */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x70 - 0x7f */
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x80 - 0x8f */
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x90 - 0x9f */
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0xa0 - 0xaf */
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0xb0 - 0xbf */
-    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,	/* 0xc0 - 0xcf */
-    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,	/* 0xd0 - 0xdf */
-    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,	/* 0xe0 - 0xef */
-    4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8,	/* 0xf0 - 0xff */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 - 0x8f */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 - 0x9f */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xa0 - 0xaf */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xb0 - 0xbf */
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 - 0xcf */
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xd0 - 0xdf */
+    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 - 0xef */
+    4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, /* 0xf0 - 0xff */
 };
 
 /*
- * On certain systems it pays to inline
- * the routines that find pixel spans.
- */
-#ifdef VAXC
-static	int32 find0span(unsigned char*, int32, int32);
-static	int32 find1span(unsigned char*, int32, int32);
-#pragma inline(find0span,find1span)
-#endif
-
-/*
  * Find a span of ones or zeros using the supplied
  * table.  The ``base'' of the bit string is supplied
  * along with the start+end bit indices.
  */
-inline static int32
-find0span(unsigned char* bp, int32 bs, int32 be)
+static inline int32_t find0span(unsigned char *bp, int32_t bs, int32_t be)
 {
-	int32 bits = be - bs;
-	int32 n, span;
+    int32_t bits = be - bs;
+    int32_t n, span;
 
-	bp += bs>>3;
-	/*
-	 * Check partial byte on lhs.
-	 */
-	if (bits > 0 && (n = (bs & 7)) != 0) {
-		span = zeroruns[(*bp << n) & 0xff];
-		if (span > 8-n)		/* table value too generous */
-			span = 8-n;
-		if (span > bits)	/* constrain span to bit range */
-			span = bits;
-		if (n+span < 8)		/* doesn't extend to edge of byte */
-			return (span);
-		bits -= span;
-		bp++;
-	} else
-		span = 0;
-	if (bits >= (int32)(2 * 8 * sizeof(long))) {
-		long* lp;
-		/*
-		 * Align to longword boundary and check longwords.
-		 */
-		while (!isAligned(bp, long)) {
-			if (*bp != 0x00)
-				return (span + zeroruns[*bp]);
-			span += 8;
-			bits -= 8;
-			bp++;
-		}
-		lp = (long*) bp;
-		while ((bits >= (int32)(8 * sizeof(long))) && (0 == *lp)) {
-			span += 8*sizeof (long);
-			bits -= 8*sizeof (long);
-			lp++;
-		}
-		bp = (unsigned char*) lp;
-	}
-	/*
-	 * Scan full bytes for all 0's.
-	 */
-	while (bits >= 8) {
-		if (*bp != 0x00)	/* end of run */
-			return (span + zeroruns[*bp]);
-		span += 8;
-		bits -= 8;
-		bp++;
-	}
-	/*
-	 * Check partial byte on rhs.
-	 */
-	if (bits > 0) {
-		n = zeroruns[*bp];
-		span += (n > bits ? bits : n);
-	}
-	return (span);
+    bp += bs >> 3;
+    /*
+     * Check partial byte on lhs.
+     */
+    if (bits > 0 && (n = (bs & 7)) != 0)
+    {
+        span = zeroruns[(*bp << n) & 0xff];
+        if (span > 8 - n) /* table value too generous */
+            span = 8 - n;
+        if (span > bits) /* constrain span to bit range */
+            span = bits;
+        if (n + span < 8) /* doesn't extend to edge of byte */
+            return (span);
+        bits -= span;
+        bp++;
+    }
+    else
+        span = 0;
+    if (bits >= (int32_t)(2 * 8 * sizeof(int64_t)))
+    {
+        int64_t *lp;
+        /*
+         * Align to int64_t boundary and check int64_t words.
+         */
+        while (!isAligned(bp, int64_t))
+        {
+            if (*bp != 0x00)
+                return (span + zeroruns[*bp]);
+            span += 8;
+            bits -= 8;
+            bp++;
+        }
+        lp = (int64_t *)bp;
+        while ((bits >= (int32_t)(8 * sizeof(int64_t))) && (0 == *lp))
+        {
+            span += 8 * sizeof(int64_t);
+            bits -= 8 * sizeof(int64_t);
+            lp++;
+        }
+        bp = (unsigned char *)lp;
+    }
+    /*
+     * Scan full bytes for all 0's.
+     */
+    while (bits >= 8)
+    {
+        if (*bp != 0x00) /* end of run */
+            return (span + zeroruns[*bp]);
+        span += 8;
+        bits -= 8;
+        bp++;
+    }
+    /*
+     * Check partial byte on rhs.
+     */
+    if (bits > 0)
+    {
+        n = zeroruns[*bp];
+        span += (n > bits ? bits : n);
+    }
+    return (span);
 }
 
-inline static int32
-find1span(unsigned char* bp, int32 bs, int32 be)
+static inline int32_t find1span(unsigned char *bp, int32_t bs, int32_t be)
 {
-	int32 bits = be - bs;
-	int32 n, span;
+    int32_t bits = be - bs;
+    int32_t n, span;
 
-	bp += bs>>3;
-	/*
-	 * Check partial byte on lhs.
-	 */
-	if (bits > 0 && (n = (bs & 7)) != 0) {
-		span = oneruns[(*bp << n) & 0xff];
-		if (span > 8-n)		/* table value too generous */
-			span = 8-n;
-		if (span > bits)	/* constrain span to bit range */
-			span = bits;
-		if (n+span < 8)		/* doesn't extend to edge of byte */
-			return (span);
-		bits -= span;
-		bp++;
-	} else
-		span = 0;
-	if (bits >= (int32)(2 * 8 * sizeof(long))) {
-		long* lp;
-		/*
-		 * Align to longword boundary and check longwords.
-		 */
-		while (!isAligned(bp, long)) {
-			if (*bp != 0xff)
-				return (span + oneruns[*bp]);
-			span += 8;
-			bits -= 8;
-			bp++;
-		}
-		lp = (long*) bp;
-		while ((bits >= (int32)(8 * sizeof(long))) && (~0 == *lp)) {
-			span += 8*sizeof (long);
-			bits -= 8*sizeof (long);
-			lp++;
-		}
-		bp = (unsigned char*) lp;
-	}
-	/*
-	 * Scan full bytes for all 1's.
-	 */
-	while (bits >= 8) {
-		if (*bp != 0xff)	/* end of run */
-			return (span + oneruns[*bp]);
-		span += 8;
-		bits -= 8;
-		bp++;
-	}
-	/*
-	 * Check partial byte on rhs.
-	 */
-	if (bits > 0) {
-		n = oneruns[*bp];
-		span += (n > bits ? bits : n);
-	}
-	return (span);
+    bp += bs >> 3;
+    /*
+     * Check partial byte on lhs.
+     */
+    if (bits > 0 && (n = (bs & 7)) != 0)
+    {
+        span = oneruns[(*bp << n) & 0xff];
+        if (span > 8 - n) /* table value too generous */
+            span = 8 - n;
+        if (span > bits) /* constrain span to bit range */
+            span = bits;
+        if (n + span < 8) /* doesn't extend to edge of byte */
+            return (span);
+        bits -= span;
+        bp++;
+    }
+    else
+        span = 0;
+    if (bits >= (int32_t)(2 * 8 * sizeof(int64_t)))
+    {
+        int64_t *lp;
+        /*
+         * Align to int64_t boundary and check int64_t words.
+         */
+        while (!isAligned(bp, int64_t))
+        {
+            if (*bp != 0xff)
+                return (span + oneruns[*bp]);
+            span += 8;
+            bits -= 8;
+            bp++;
+        }
+        lp = (int64_t *)bp;
+        while ((bits >= (int32_t)(8 * sizeof(int64_t))) &&
+               (~((uint64_t)0) == (uint64_t)*lp))
+        {
+            span += 8 * sizeof(int64_t);
+            bits -= 8 * sizeof(int64_t);
+            lp++;
+        }
+        bp = (unsigned char *)lp;
+    }
+    /*
+     * Scan full bytes for all 1's.
+     */
+    while (bits >= 8)
+    {
+        if (*bp != 0xff) /* end of run */
+            return (span + oneruns[*bp]);
+        span += 8;
+        bits -= 8;
+        bp++;
+    }
+    /*
+     * Check partial byte on rhs.
+     */
+    if (bits > 0)
+    {
+        n = oneruns[*bp];
+        span += (n > bits ? bits : n);
+    }
+    return (span);
 }
 
 /*
@@ -968,455 +993,501 @@
  * color.  The end, be, is returned if no such bit
  * exists.
  */
-#define	finddiff(_cp, _bs, _be, _color)	\
-	(_bs + (_color ? find1span(_cp,_bs,_be) : find0span(_cp,_bs,_be)))
+#define finddiff(_cp, _bs, _be, _color)                                        \
+    (_bs + (_color ? find1span(_cp, _bs, _be) : find0span(_cp, _bs, _be)))
 /*
  * Like finddiff, but also check the starting bit
  * against the end in case start > end.
  */
-#define	finddiff2(_cp, _bs, _be, _color) \
-	(_bs < _be ? finddiff(_cp,_bs,_be,_color) : _be)
+#define finddiff2(_cp, _bs, _be, _color)                                       \
+    (_bs < _be ? finddiff(_cp, _bs, _be, _color) : _be)
 
 /*
  * 1d-encode a row of pixels.  The encoding is
  * a sequence of all-white or all-black spans
  * of pixels encoded with Huffman codes.
  */
-static int
-Fax3Encode1DRow(TIFF* tif, unsigned char* bp, uint32 bits)
+static int Fax3Encode1DRow(TIFF *tif, unsigned char *bp, uint32_t bits)
 {
-	Fax3CodecState* sp = EncoderState(tif);
-	int32 span;
-        uint32 bs = 0;
+    Fax3CodecState *sp = EncoderState(tif);
+    int32_t span;
+    uint32_t bs = 0;
 
-	for (;;) {
-		span = find0span(bp, bs, bits);		/* white span */
-		putspan(tif, span, TIFFFaxWhiteCodes);
-		bs += span;
-		if (bs >= bits)
-			break;
-		span = find1span(bp, bs, bits);		/* black span */
-		putspan(tif, span, TIFFFaxBlackCodes);
-		bs += span;
-		if (bs >= bits)
-			break;
-	}
-	if (sp->b.mode & (FAXMODE_BYTEALIGN|FAXMODE_WORDALIGN)) {
-		if (sp->bit != 8)			/* byte-align */
-			Fax3FlushBits(tif, sp);
-		if ((sp->b.mode&FAXMODE_WORDALIGN) &&
-		    !isAligned(tif->tif_rawcp, uint16))
-			Fax3FlushBits(tif, sp);
-	}
-	return (1);
+    for (;;)
+    {
+        span = find0span(bp, bs, bits); /* white span */
+        if (!putspan(tif, span, TIFFFaxWhiteCodes))
+            return 0;
+        bs += span;
+        if (bs >= bits)
+            break;
+        span = find1span(bp, bs, bits); /* black span */
+        if (!putspan(tif, span, TIFFFaxBlackCodes))
+            return 0;
+        bs += span;
+        if (bs >= bits)
+            break;
+    }
+    if (sp->b.mode & (FAXMODE_BYTEALIGN | FAXMODE_WORDALIGN))
+    {
+        if (sp->bit != 8) /* byte-align */
+            Fax3FlushBits(tif, sp);
+        if ((sp->b.mode & FAXMODE_WORDALIGN) &&
+            !isAligned(tif->tif_rawcp, uint16_t))
+            Fax3FlushBits(tif, sp);
+    }
+    return (1);
 }
 
-static const tableentry horizcode =
-    { 3, 0x1, 0 };	/* 001 */
-static const tableentry passcode =
-    { 4, 0x1, 0 };	/* 0001 */
+static const tableentry horizcode = {3, 0x1, 0}; /* 001 */
+static const tableentry passcode = {4, 0x1, 0};  /* 0001 */
 static const tableentry vcodes[7] = {
-    { 7, 0x03, 0 },	/* 0000 011 */
-    { 6, 0x03, 0 },	/* 0000 11 */
-    { 3, 0x03, 0 },	/* 011 */
-    { 1, 0x1, 0 },	/* 1 */
-    { 3, 0x2, 0 },	/* 010 */
-    { 6, 0x02, 0 },	/* 0000 10 */
-    { 7, 0x02, 0 }	/* 0000 010 */
+    {7, 0x03, 0}, /* 0000 011 */
+    {6, 0x03, 0}, /* 0000 11 */
+    {3, 0x03, 0}, /* 011 */
+    {1, 0x1, 0},  /* 1 */
+    {3, 0x2, 0},  /* 010 */
+    {6, 0x02, 0}, /* 0000 10 */
+    {7, 0x02, 0}  /* 0000 010 */
 };
 
 /*
  * 2d-encode a row of pixels.  Consult the CCITT
  * documentation for the algorithm.
  */
-static int
-Fax3Encode2DRow(TIFF* tif, unsigned char* bp, unsigned char* rp, uint32 bits)
+static int Fax3Encode2DRow(TIFF *tif, unsigned char *bp, unsigned char *rp,
+                           uint32_t bits)
 {
-#define	PIXEL(buf,ix)	((((buf)[(ix)>>3]) >> (7-((ix)&7))) & 1)
-        uint32 a0 = 0;
-	uint32 a1 = (PIXEL(bp, 0) != 0 ? 0 : finddiff(bp, 0, bits, 0));
-	uint32 b1 = (PIXEL(rp, 0) != 0 ? 0 : finddiff(rp, 0, bits, 0));
-	uint32 a2, b2;
+#define PIXEL(buf, ix) ((((buf)[(ix) >> 3]) >> (7 - ((ix)&7))) & 1)
+    uint32_t a0 = 0;
+    uint32_t a1 = (PIXEL(bp, 0) != 0 ? 0 : finddiff(bp, 0, bits, 0));
+    uint32_t b1 = (PIXEL(rp, 0) != 0 ? 0 : finddiff(rp, 0, bits, 0));
+    uint32_t a2, b2;
 
-	for (;;) {
-		b2 = finddiff2(rp, b1, bits, PIXEL(rp,b1));
-		if (b2 >= a1) {
-			/* Naive computation triggers -fsanitize=undefined,unsigned-integer-overflow */
-			/* although it is correct unless the difference between both is < 31 bit */
-			/* int32 d = b1 - a1; */
-			int32 d = (b1 >= a1 && b1 - a1 <= 3U) ? (int32)(b1 - a1):
-			          (b1 < a1 && a1 - b1 <= 3U) ? -(int32)(a1 - b1) : 0x7FFFFFFF;
-			if (!(-3 <= d && d <= 3)) {	/* horizontal mode */
-				a2 = finddiff2(bp, a1, bits, PIXEL(bp,a1));
-				putcode(tif, &horizcode);
-				if (a0+a1 == 0 || PIXEL(bp, a0) == 0) {
-					putspan(tif, a1-a0, TIFFFaxWhiteCodes);
-					putspan(tif, a2-a1, TIFFFaxBlackCodes);
-				} else {
-					putspan(tif, a1-a0, TIFFFaxBlackCodes);
-					putspan(tif, a2-a1, TIFFFaxWhiteCodes);
-				}
-				a0 = a2;
-			} else {			/* vertical mode */
-				putcode(tif, &vcodes[d+3]);
-				a0 = a1;
-			}
-		} else {				/* pass mode */
-			putcode(tif, &passcode);
-			a0 = b2;
-		}
-		if (a0 >= bits)
-			break;
-		a1 = finddiff(bp, a0, bits, PIXEL(bp,a0));
-		b1 = finddiff(rp, a0, bits, !PIXEL(bp,a0));
-		b1 = finddiff(rp, b1, bits, PIXEL(bp,a0));
-	}
-	return (1);
+    for (;;)
+    {
+        b2 = finddiff2(rp, b1, bits, PIXEL(rp, b1));
+        if (b2 >= a1)
+        {
+            /* Naive computation triggers
+             * -fsanitize=undefined,unsigned-integer-overflow */
+            /* although it is correct unless the difference between both is < 31
+             * bit */
+            /* int32_t d = b1 - a1; */
+            int32_t d = (b1 >= a1 && b1 - a1 <= 3U)  ? (int32_t)(b1 - a1)
+                        : (b1 < a1 && a1 - b1 <= 3U) ? -(int32_t)(a1 - b1)
+                                                     : 0x7FFFFFFF;
+            if (!(-3 <= d && d <= 3))
+            { /* horizontal mode */
+                a2 = finddiff2(bp, a1, bits, PIXEL(bp, a1));
+                if (!putcode(tif, &horizcode))
+                    return 0;
+                if (a0 + a1 == 0 || PIXEL(bp, a0) == 0)
+                {
+                    if (!putspan(tif, a1 - a0, TIFFFaxWhiteCodes))
+                        return 0;
+                    if (!putspan(tif, a2 - a1, TIFFFaxBlackCodes))
+                        return 0;
+                }
+                else
+                {
+                    if (!putspan(tif, a1 - a0, TIFFFaxBlackCodes))
+                        return 0;
+                    if (!putspan(tif, a2 - a1, TIFFFaxWhiteCodes))
+                        return 0;
+                }
+                a0 = a2;
+            }
+            else
+            { /* vertical mode */
+                if (!putcode(tif, &vcodes[d + 3]))
+                    return 0;
+                a0 = a1;
+            }
+        }
+        else
+        { /* pass mode */
+            if (!putcode(tif, &passcode))
+                return 0;
+            a0 = b2;
+        }
+        if (a0 >= bits)
+            break;
+        a1 = finddiff(bp, a0, bits, PIXEL(bp, a0));
+        b1 = finddiff(rp, a0, bits, !PIXEL(bp, a0));
+        b1 = finddiff(rp, b1, bits, PIXEL(bp, a0));
+    }
+    return (1);
 #undef PIXEL
 }
 
 /*
  * Encode a buffer of pixels.
  */
-static int
-Fax3Encode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s)
+static int Fax3Encode(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s)
 {
-	static const char module[] = "Fax3Encode";
-	Fax3CodecState* sp = EncoderState(tif);
-	(void) s;
-	if (cc % sp->b.rowbytes)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "Fractional scanlines cannot be written");
-		return (0);
-	}
-	while (cc > 0) {
-		if ((sp->b.mode & FAXMODE_NOEOL) == 0)
-			Fax3PutEOL(tif);
-		if (is2DEncoding(sp)) {
-			if (sp->tag == G3_1D) {
-				if (!Fax3Encode1DRow(tif, bp, sp->b.rowpixels))
-					return (0);
-				sp->tag = G3_2D;
-			} else {
-				if (!Fax3Encode2DRow(tif, bp, sp->refline,
-				    sp->b.rowpixels))
-					return (0);
-				sp->k--;
-			}
-			if (sp->k == 0) {
-				sp->tag = G3_1D;
-				sp->k = sp->maxk-1;
-			} else
-				_TIFFmemcpy(sp->refline, bp, sp->b.rowbytes);
-		} else {
-			if (!Fax3Encode1DRow(tif, bp, sp->b.rowpixels))
-				return (0);
-		}
-		bp += sp->b.rowbytes;
-		cc -= sp->b.rowbytes;
-	}
-	return (1);
+    static const char module[] = "Fax3Encode";
+    Fax3CodecState *sp = EncoderState(tif);
+    (void)s;
+    if (cc % sp->b.rowbytes)
+    {
+        TIFFErrorExtR(tif, module, "Fractional scanlines cannot be written");
+        return (0);
+    }
+    while (cc > 0)
+    {
+        if ((sp->b.mode & FAXMODE_NOEOL) == 0)
+        {
+            if (!Fax3PutEOL(tif))
+                return 0;
+        }
+        if (is2DEncoding(sp))
+        {
+            if (sp->tag == G3_1D)
+            {
+                if (!Fax3Encode1DRow(tif, bp, sp->b.rowpixels))
+                    return (0);
+                sp->tag = G3_2D;
+            }
+            else
+            {
+                if (!Fax3Encode2DRow(tif, bp, sp->refline, sp->b.rowpixels))
+                    return (0);
+                sp->k--;
+            }
+            if (sp->k == 0)
+            {
+                sp->tag = G3_1D;
+                sp->k = sp->maxk - 1;
+            }
+            else
+                _TIFFmemcpy(sp->refline, bp, sp->b.rowbytes);
+        }
+        else
+        {
+            if (!Fax3Encode1DRow(tif, bp, sp->b.rowpixels))
+                return (0);
+        }
+        bp += sp->b.rowbytes;
+        cc -= sp->b.rowbytes;
+    }
+    return (1);
 }
 
-static int
-Fax3PostEncode(TIFF* tif)
+static int Fax3PostEncode(TIFF *tif)
 {
-	Fax3CodecState* sp = EncoderState(tif);
+    Fax3CodecState *sp = EncoderState(tif);
 
-	if (sp->bit != 8)
-		Fax3FlushBits(tif, sp);
-	return (1);
+    if (sp->bit != 8)
+        Fax3FlushBits(tif, sp);
+    return (1);
 }
 
-static void
-Fax3Close(TIFF* tif)
+static int _Fax3Close(TIFF *tif)
 {
-	if ((Fax3State(tif)->mode & FAXMODE_NORTC) == 0 && tif->tif_rawcp) {
-		Fax3CodecState* sp = EncoderState(tif);
-		unsigned int code = EOL;
-		unsigned int length = 12;
-		int i;
+    if ((Fax3State(tif)->mode & FAXMODE_NORTC) == 0 && tif->tif_rawcp)
+    {
+        Fax3CodecState *sp = EncoderState(tif);
+        unsigned int code = EOL;
+        unsigned int length = 12;
+        int i;
 
-		if (is2DEncoding(sp)) {
-			code = (code<<1) | (sp->tag == G3_1D);
-			length++;
-		}
-		for (i = 0; i < 6; i++)
-			Fax3PutBits(tif, code, length);
-		Fax3FlushBits(tif, sp);
-	}
+        if (is2DEncoding(sp))
+        {
+            code = (code << 1) | (sp->tag == G3_1D);
+            length++;
+        }
+        for (i = 0; i < 6; i++)
+            Fax3PutBits(tif, code, length);
+        Fax3FlushBits(tif, sp);
+    }
+    return 1;
 }
 
-static void
-Fax3Cleanup(TIFF* tif)
+static void Fax3Close(TIFF *tif) { _Fax3Close(tif); }
+
+static void Fax3Cleanup(TIFF *tif)
 {
-	Fax3CodecState* sp = DecoderState(tif);
-	
-	assert(sp != 0);
+    Fax3CodecState *sp = DecoderState(tif);
 
-	tif->tif_tagmethods.vgetfield = sp->b.vgetparent;
-	tif->tif_tagmethods.vsetfield = sp->b.vsetparent;
-	tif->tif_tagmethods.printdir = sp->b.printdir;
+    assert(sp != 0);
 
-	if (sp->runs)
-		_TIFFfree(sp->runs);
-	if (sp->refline)
-		_TIFFfree(sp->refline);
+    tif->tif_tagmethods.vgetfield = sp->b.vgetparent;
+    tif->tif_tagmethods.vsetfield = sp->b.vsetparent;
+    tif->tif_tagmethods.printdir = sp->b.printdir;
 
-	_TIFFfree(tif->tif_data);
-	tif->tif_data = NULL;
+    if (sp->runs)
+        _TIFFfreeExt(tif, sp->runs);
+    if (sp->refline)
+        _TIFFfreeExt(tif, sp->refline);
 
-	_TIFFSetDefaultCompressionState(tif);
+    _TIFFfreeExt(tif, tif->tif_data);
+    tif->tif_data = NULL;
+
+    _TIFFSetDefaultCompressionState(tif);
 }
 
-#define	FIELD_BADFAXLINES	(FIELD_CODEC+0)
-#define	FIELD_CLEANFAXDATA	(FIELD_CODEC+1)
-#define	FIELD_BADFAXRUN		(FIELD_CODEC+2)
+#define FIELD_BADFAXLINES (FIELD_CODEC + 0)
+#define FIELD_CLEANFAXDATA (FIELD_CODEC + 1)
+#define FIELD_BADFAXRUN (FIELD_CODEC + 2)
 
-#define	FIELD_OPTIONS		(FIELD_CODEC+7)
+#define FIELD_OPTIONS (FIELD_CODEC + 7)
 
 static const TIFFField faxFields[] = {
-    { TIFFTAG_FAXMODE, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "FaxMode", NULL },
-    { TIFFTAG_FAXFILLFUNC, 0, 0, TIFF_ANY, 0, TIFF_SETGET_OTHER, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "FaxFillFunc", NULL },
-    { TIFFTAG_BADFAXLINES, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UINT32, FIELD_BADFAXLINES, TRUE, FALSE, "BadFaxLines", NULL },
-    { TIFFTAG_CLEANFAXDATA, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UINT16, FIELD_CLEANFAXDATA, TRUE, FALSE, "CleanFaxData", NULL },
-    { TIFFTAG_CONSECUTIVEBADFAXLINES, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UINT32, FIELD_BADFAXRUN, TRUE, FALSE, "ConsecutiveBadFaxLines", NULL }};
+    {TIFFTAG_FAXMODE, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED,
+     FIELD_PSEUDO, FALSE, FALSE, "FaxMode", NULL},
+    {TIFFTAG_FAXFILLFUNC, 0, 0, TIFF_ANY, 0, TIFF_SETGET_OTHER,
+     TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "FaxFillFunc", NULL},
+    {TIFFTAG_BADFAXLINES, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
+     TIFF_SETGET_UINT32, FIELD_BADFAXLINES, TRUE, FALSE, "BadFaxLines", NULL},
+    {TIFFTAG_CLEANFAXDATA, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
+     TIFF_SETGET_UINT16, FIELD_CLEANFAXDATA, TRUE, FALSE, "CleanFaxData", NULL},
+    {TIFFTAG_CONSECUTIVEBADFAXLINES, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
+     TIFF_SETGET_UINT32, FIELD_BADFAXRUN, TRUE, FALSE, "ConsecutiveBadFaxLines",
+     NULL}};
 static const TIFFField fax3Fields[] = {
-    { TIFFTAG_GROUP3OPTIONS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UINT32, FIELD_OPTIONS, FALSE, FALSE, "Group3Options", NULL },
+    {TIFFTAG_GROUP3OPTIONS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
+     TIFF_SETGET_UINT32, FIELD_OPTIONS, FALSE, FALSE, "Group3Options", NULL},
 };
 static const TIFFField fax4Fields[] = {
-    { TIFFTAG_GROUP4OPTIONS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UINT32, FIELD_OPTIONS, FALSE, FALSE, "Group4Options", NULL },
+    {TIFFTAG_GROUP4OPTIONS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
+     TIFF_SETGET_UINT32, FIELD_OPTIONS, FALSE, FALSE, "Group4Options", NULL},
 };
 
-static int
-Fax3VSetField(TIFF* tif, uint32 tag, va_list ap)
+static int Fax3VSetField(TIFF *tif, uint32_t tag, va_list ap)
 {
-	Fax3BaseState* sp = Fax3State(tif);
-	const TIFFField* fip;
+    Fax3BaseState *sp = Fax3State(tif);
+    const TIFFField *fip;
 
-	assert(sp != 0);
-	assert(sp->vsetparent != 0);
+    assert(sp != 0);
+    assert(sp->vsetparent != 0);
 
-	switch (tag) {
-	case TIFFTAG_FAXMODE:
-		sp->mode = (int) va_arg(ap, int);
-		return 1;			/* NB: pseudo tag */
-	case TIFFTAG_FAXFILLFUNC:
-		DecoderState(tif)->fill = va_arg(ap, TIFFFaxFillFunc);
-		return 1;			/* NB: pseudo tag */
-	case TIFFTAG_GROUP3OPTIONS:
-		/* XXX: avoid reading options if compression mismatches. */
-		if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX3)
-			sp->groupoptions = (uint32) va_arg(ap, uint32);
-		break;
-	case TIFFTAG_GROUP4OPTIONS:
-		/* XXX: avoid reading options if compression mismatches. */
-		if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX4)
-			sp->groupoptions = (uint32) va_arg(ap, uint32);
-		break;
-	case TIFFTAG_BADFAXLINES:
-		sp->badfaxlines = (uint32) va_arg(ap, uint32);
-		break;
-	case TIFFTAG_CLEANFAXDATA:
-		sp->cleanfaxdata = (uint16) va_arg(ap, uint16_vap);
-		break;
-	case TIFFTAG_CONSECUTIVEBADFAXLINES:
-		sp->badfaxrun = (uint32) va_arg(ap, uint32);
-		break;
-	default:
-		return (*sp->vsetparent)(tif, tag, ap);
-	}
-	
-	if ((fip = TIFFFieldWithTag(tif, tag)) != NULL)
-		TIFFSetFieldBit(tif, fip->field_bit);
-	else
-		return 0;
+    switch (tag)
+    {
+        case TIFFTAG_FAXMODE:
+            sp->mode = (int)va_arg(ap, int);
+            return 1; /* NB: pseudo tag */
+        case TIFFTAG_FAXFILLFUNC:
+            DecoderState(tif)->fill = va_arg(ap, TIFFFaxFillFunc);
+            return 1; /* NB: pseudo tag */
+        case TIFFTAG_GROUP3OPTIONS:
+            /* XXX: avoid reading options if compression mismatches. */
+            if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX3)
+                sp->groupoptions = (uint32_t)va_arg(ap, uint32_t);
+            break;
+        case TIFFTAG_GROUP4OPTIONS:
+            /* XXX: avoid reading options if compression mismatches. */
+            if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX4)
+                sp->groupoptions = (uint32_t)va_arg(ap, uint32_t);
+            break;
+        case TIFFTAG_BADFAXLINES:
+            sp->badfaxlines = (uint32_t)va_arg(ap, uint32_t);
+            break;
+        case TIFFTAG_CLEANFAXDATA:
+            sp->cleanfaxdata = (uint16_t)va_arg(ap, uint16_vap);
+            break;
+        case TIFFTAG_CONSECUTIVEBADFAXLINES:
+            sp->badfaxrun = (uint32_t)va_arg(ap, uint32_t);
+            break;
+        default:
+            return (*sp->vsetparent)(tif, tag, ap);
+    }
 
-	tif->tif_flags |= TIFF_DIRTYDIRECT;
-	return 1;
+    if ((fip = TIFFFieldWithTag(tif, tag)) != NULL)
+        TIFFSetFieldBit(tif, fip->field_bit);
+    else
+        return 0;
+
+    tif->tif_flags |= TIFF_DIRTYDIRECT;
+    return 1;
 }
 
-static int
-Fax3VGetField(TIFF* tif, uint32 tag, va_list ap)
+static int Fax3VGetField(TIFF *tif, uint32_t tag, va_list ap)
 {
-	Fax3BaseState* sp = Fax3State(tif);
+    Fax3BaseState *sp = Fax3State(tif);
 
-	assert(sp != 0);
+    assert(sp != 0);
 
-	switch (tag) {
-	case TIFFTAG_FAXMODE:
-		*va_arg(ap, int*) = sp->mode;
-		break;
-	case TIFFTAG_FAXFILLFUNC:
-		*va_arg(ap, TIFFFaxFillFunc*) = DecoderState(tif)->fill;
-		break;
-	case TIFFTAG_GROUP3OPTIONS:
-	case TIFFTAG_GROUP4OPTIONS:
-		*va_arg(ap, uint32*) = sp->groupoptions;
-		break;
-	case TIFFTAG_BADFAXLINES:
-		*va_arg(ap, uint32*) = sp->badfaxlines;
-		break;
-	case TIFFTAG_CLEANFAXDATA:
-		*va_arg(ap, uint16*) = sp->cleanfaxdata;
-		break;
-	case TIFFTAG_CONSECUTIVEBADFAXLINES:
-		*va_arg(ap, uint32*) = sp->badfaxrun;
-		break;
-	default:
-		return (*sp->vgetparent)(tif, tag, ap);
-	}
-	return (1);
+    switch (tag)
+    {
+        case TIFFTAG_FAXMODE:
+            *va_arg(ap, int *) = sp->mode;
+            break;
+        case TIFFTAG_FAXFILLFUNC:
+            *va_arg(ap, TIFFFaxFillFunc *) = DecoderState(tif)->fill;
+            break;
+        case TIFFTAG_GROUP3OPTIONS:
+        case TIFFTAG_GROUP4OPTIONS:
+            *va_arg(ap, uint32_t *) = sp->groupoptions;
+            break;
+        case TIFFTAG_BADFAXLINES:
+            *va_arg(ap, uint32_t *) = sp->badfaxlines;
+            break;
+        case TIFFTAG_CLEANFAXDATA:
+            *va_arg(ap, uint16_t *) = sp->cleanfaxdata;
+            break;
+        case TIFFTAG_CONSECUTIVEBADFAXLINES:
+            *va_arg(ap, uint32_t *) = sp->badfaxrun;
+            break;
+        default:
+            return (*sp->vgetparent)(tif, tag, ap);
+    }
+    return (1);
 }
 
-static void
-Fax3PrintDir(TIFF* tif, FILE* fd, long flags)
+static void Fax3PrintDir(TIFF *tif, FILE *fd, long flags)
 {
-	Fax3BaseState* sp = Fax3State(tif);
+    Fax3BaseState *sp = Fax3State(tif);
 
-	assert(sp != 0);
+    assert(sp != 0);
 
-	(void) flags;
-	if (TIFFFieldSet(tif,FIELD_OPTIONS)) {
-		const char* sep = " ";
-		if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX4) {
-			fprintf(fd, "  Group 4 Options:");
-			if (sp->groupoptions & GROUP4OPT_UNCOMPRESSED)
-				fprintf(fd, "%suncompressed data", sep);
-		} else {
+    (void)flags;
+    if (TIFFFieldSet(tif, FIELD_OPTIONS))
+    {
+        const char *sep = " ";
+        if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX4)
+        {
+            fprintf(fd, "  Group 4 Options:");
+            if (sp->groupoptions & GROUP4OPT_UNCOMPRESSED)
+                fprintf(fd, "%suncompressed data", sep);
+        }
+        else
+        {
 
-			fprintf(fd, "  Group 3 Options:");
-			if (sp->groupoptions & GROUP3OPT_2DENCODING) {
-				fprintf(fd, "%s2-d encoding", sep);
-				sep = "+";
-			}
-			if (sp->groupoptions & GROUP3OPT_FILLBITS) {
-				fprintf(fd, "%sEOL padding", sep);
-				sep = "+";
-			}
-			if (sp->groupoptions & GROUP3OPT_UNCOMPRESSED)
-				fprintf(fd, "%suncompressed data", sep);
-		}
-		fprintf(fd, " (%lu = 0x%lx)\n",
-                        (unsigned long) sp->groupoptions,
-                        (unsigned long) sp->groupoptions);
-	}
-	if (TIFFFieldSet(tif,FIELD_CLEANFAXDATA)) {
-		fprintf(fd, "  Fax Data:");
-		switch (sp->cleanfaxdata) {
-		case CLEANFAXDATA_CLEAN:
-			fprintf(fd, " clean");
-			break;
-		case CLEANFAXDATA_REGENERATED:
-			fprintf(fd, " receiver regenerated");
-			break;
-		case CLEANFAXDATA_UNCLEAN:
-			fprintf(fd, " uncorrected errors");
-			break;
-		}
-		fprintf(fd, " (%u = 0x%x)\n",
-		    sp->cleanfaxdata, sp->cleanfaxdata);
-	}
-	if (TIFFFieldSet(tif,FIELD_BADFAXLINES))
-		fprintf(fd, "  Bad Fax Lines: %lu\n",
-                        (unsigned long) sp->badfaxlines);
-	if (TIFFFieldSet(tif,FIELD_BADFAXRUN))
-		fprintf(fd, "  Consecutive Bad Fax Lines: %lu\n",
-		    (unsigned long) sp->badfaxrun);
-	if (sp->printdir)
-		(*sp->printdir)(tif, fd, flags);
+            fprintf(fd, "  Group 3 Options:");
+            if (sp->groupoptions & GROUP3OPT_2DENCODING)
+            {
+                fprintf(fd, "%s2-d encoding", sep);
+                sep = "+";
+            }
+            if (sp->groupoptions & GROUP3OPT_FILLBITS)
+            {
+                fprintf(fd, "%sEOL padding", sep);
+                sep = "+";
+            }
+            if (sp->groupoptions & GROUP3OPT_UNCOMPRESSED)
+                fprintf(fd, "%suncompressed data", sep);
+        }
+        fprintf(fd, " (%" PRIu32 " = 0x%" PRIx32 ")\n", sp->groupoptions,
+                sp->groupoptions);
+    }
+    if (TIFFFieldSet(tif, FIELD_CLEANFAXDATA))
+    {
+        fprintf(fd, "  Fax Data:");
+        switch (sp->cleanfaxdata)
+        {
+            case CLEANFAXDATA_CLEAN:
+                fprintf(fd, " clean");
+                break;
+            case CLEANFAXDATA_REGENERATED:
+                fprintf(fd, " receiver regenerated");
+                break;
+            case CLEANFAXDATA_UNCLEAN:
+                fprintf(fd, " uncorrected errors");
+                break;
+        }
+        fprintf(fd, " (%" PRIu16 " = 0x%" PRIx16 ")\n", sp->cleanfaxdata,
+                sp->cleanfaxdata);
+    }
+    if (TIFFFieldSet(tif, FIELD_BADFAXLINES))
+        fprintf(fd, "  Bad Fax Lines: %" PRIu32 "\n", sp->badfaxlines);
+    if (TIFFFieldSet(tif, FIELD_BADFAXRUN))
+        fprintf(fd, "  Consecutive Bad Fax Lines: %" PRIu32 "\n",
+                sp->badfaxrun);
+    if (sp->printdir)
+        (*sp->printdir)(tif, fd, flags);
 }
 
-static int
-InitCCITTFax3(TIFF* tif)
+static int InitCCITTFax3(TIFF *tif)
 {
-	static const char module[] = "InitCCITTFax3";
-	Fax3BaseState* sp;
+    static const char module[] = "InitCCITTFax3";
+    Fax3BaseState *sp;
 
-	/*
-	 * Merge codec-specific tag information.
-	 */
-	if (!_TIFFMergeFields(tif, faxFields, TIFFArrayCount(faxFields))) {
-		TIFFErrorExt(tif->tif_clientdata, "InitCCITTFax3",
-			"Merging common CCITT Fax codec-specific tags failed");
-		return 0;
-	}
+    /*
+     * Merge codec-specific tag information.
+     */
+    if (!_TIFFMergeFields(tif, faxFields, TIFFArrayCount(faxFields)))
+    {
+        TIFFErrorExtR(tif, "InitCCITTFax3",
+                      "Merging common CCITT Fax codec-specific tags failed");
+        return 0;
+    }
 
-	/*
-	 * Allocate state block so tag methods have storage to record values.
-	 */
-	tif->tif_data = (uint8*)
-		_TIFFmalloc(sizeof (Fax3CodecState));
+    /*
+     * Allocate state block so tag methods have storage to record values.
+     */
+    tif->tif_data = (uint8_t *)_TIFFmallocExt(tif, sizeof(Fax3CodecState));
 
-	if (tif->tif_data == NULL) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "No space for state block");
-		return (0);
-	}
-	_TIFFmemset(tif->tif_data, 0, sizeof (Fax3CodecState));
+    if (tif->tif_data == NULL)
+    {
+        TIFFErrorExtR(tif, module, "No space for state block");
+        return (0);
+    }
+    _TIFFmemset(tif->tif_data, 0, sizeof(Fax3CodecState));
 
-	sp = Fax3State(tif);
-        sp->rw_mode = tif->tif_mode;
+    sp = Fax3State(tif);
+    sp->rw_mode = tif->tif_mode;
 
-	/*
-	 * Override parent get/set field methods.
-	 */
-	sp->vgetparent = tif->tif_tagmethods.vgetfield;
-	tif->tif_tagmethods.vgetfield = Fax3VGetField; /* hook for codec tags */
-	sp->vsetparent = tif->tif_tagmethods.vsetfield;
-	tif->tif_tagmethods.vsetfield = Fax3VSetField; /* hook for codec tags */
-	sp->printdir = tif->tif_tagmethods.printdir;
-	tif->tif_tagmethods.printdir = Fax3PrintDir;   /* hook for codec tags */
-	sp->groupoptions = 0;	
+    /*
+     * Override parent get/set field methods.
+     */
+    sp->vgetparent = tif->tif_tagmethods.vgetfield;
+    tif->tif_tagmethods.vgetfield = Fax3VGetField; /* hook for codec tags */
+    sp->vsetparent = tif->tif_tagmethods.vsetfield;
+    tif->tif_tagmethods.vsetfield = Fax3VSetField; /* hook for codec tags */
+    sp->printdir = tif->tif_tagmethods.printdir;
+    tif->tif_tagmethods.printdir = Fax3PrintDir; /* hook for codec tags */
+    sp->groupoptions = 0;
 
-	if (sp->rw_mode == O_RDONLY) /* FIXME: improve for in place update */
-		tif->tif_flags |= TIFF_NOBITREV; /* decoder does bit reversal */
-	DecoderState(tif)->runs = NULL;
-	TIFFSetField(tif, TIFFTAG_FAXFILLFUNC, _TIFFFax3fillruns);
-	EncoderState(tif)->refline = NULL;
+    if (sp->rw_mode == O_RDONLY) /* FIXME: improve for in place update */
+        tif->tif_flags |= TIFF_NOBITREV; /* decoder does bit reversal */
+    DecoderState(tif)->runs = NULL;
+    TIFFSetField(tif, TIFFTAG_FAXFILLFUNC, _TIFFFax3fillruns);
+    EncoderState(tif)->refline = NULL;
 
-	/*
-	 * Install codec methods.
-	 */
-	tif->tif_fixuptags = Fax3FixupTags;
-	tif->tif_setupdecode = Fax3SetupState;
-	tif->tif_predecode = Fax3PreDecode;
-	tif->tif_decoderow = Fax3Decode1D;
-	tif->tif_decodestrip = Fax3Decode1D;
-	tif->tif_decodetile = Fax3Decode1D;
-	tif->tif_setupencode = Fax3SetupState;
-	tif->tif_preencode = Fax3PreEncode;
-	tif->tif_postencode = Fax3PostEncode;
-	tif->tif_encoderow = Fax3Encode;
-	tif->tif_encodestrip = Fax3Encode;
-	tif->tif_encodetile = Fax3Encode;
-	tif->tif_close = Fax3Close;
-	tif->tif_cleanup = Fax3Cleanup;
+    /*
+     * Install codec methods.
+     */
+    tif->tif_fixuptags = Fax3FixupTags;
+    tif->tif_setupdecode = Fax3SetupState;
+    tif->tif_predecode = Fax3PreDecode;
+    tif->tif_decoderow = Fax3Decode1D;
+    tif->tif_decodestrip = Fax3Decode1D;
+    tif->tif_decodetile = Fax3Decode1D;
+    tif->tif_setupencode = Fax3SetupState;
+    tif->tif_preencode = Fax3PreEncode;
+    tif->tif_postencode = Fax3PostEncode;
+    tif->tif_encoderow = Fax3Encode;
+    tif->tif_encodestrip = Fax3Encode;
+    tif->tif_encodetile = Fax3Encode;
+    tif->tif_close = Fax3Close;
+    tif->tif_cleanup = Fax3Cleanup;
 
-	return (1);
+    return (1);
 }
 
-int
-TIFFInitCCITTFax3(TIFF* tif, int scheme)
+int TIFFInitCCITTFax3(TIFF *tif, int scheme)
 {
-	(void) scheme;
-	if (InitCCITTFax3(tif)) {
-		/*
-		 * Merge codec-specific tag information.
-		 */
-		if (!_TIFFMergeFields(tif, fax3Fields,
-				      TIFFArrayCount(fax3Fields))) {
-			TIFFErrorExt(tif->tif_clientdata, "TIFFInitCCITTFax3",
-			"Merging CCITT Fax 3 codec-specific tags failed");
-			return 0;
-		}
+    (void)scheme;
+    if (InitCCITTFax3(tif))
+    {
+        /*
+         * Merge codec-specific tag information.
+         */
+        if (!_TIFFMergeFields(tif, fax3Fields, TIFFArrayCount(fax3Fields)))
+        {
+            TIFFErrorExtR(tif, "TIFFInitCCITTFax3",
+                          "Merging CCITT Fax 3 codec-specific tags failed");
+            return 0;
+        }
 
-		/*
-		 * The default format is Class/F-style w/o RTC.
-		 */
-		return TIFFSetField(tif, TIFFTAG_FAXMODE, FAXMODE_CLASSF);
-	} else
-		return 01;
+        /*
+         * The default format is Class/F-style w/o RTC.
+         */
+        return TIFFSetField(tif, TIFFTAG_FAXMODE, FAXMODE_CLASSF);
+    }
+    else
+        return 01;
 }
 
 /*
@@ -1424,124 +1495,146 @@
  * Compression Scheme Support.
  */
 
-#define SWAP(t,a,b) { t x; x = (a); (a) = (b); (b) = x; }
+#define SWAP(t, a, b)                                                          \
+    {                                                                          \
+        t x;                                                                   \
+        x = (a);                                                               \
+        (a) = (b);                                                             \
+        (b) = x;                                                               \
+    }
 /*
  * Decode the requested amount of G4-encoded data.
  */
-static int
-Fax4Decode(TIFF* tif, uint8* buf, tmsize_t occ, uint16 s)
+static int Fax4Decode(TIFF *tif, uint8_t *buf, tmsize_t occ, uint16_t s)
 {
-	DECLARE_STATE_2D(tif, sp, "Fax4Decode");
-	(void) s;
-	if (occ % sp->b.rowbytes)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "Fractional scanlines cannot be read");
-		return (-1);
-	}
-	CACHE_STATE(tif, sp);
-	while (occ > 0) {
-		a0 = 0;
-		RunLength = 0;
-		pa = thisrun = sp->curruns;
-		pb = sp->refruns;
-		b1 = *pb++;
+    DECLARE_STATE_2D(tif, sp, "Fax4Decode");
+    (void)s;
+    if (occ % sp->b.rowbytes)
+    {
+        TIFFErrorExtR(tif, module, "Fractional scanlines cannot be read");
+        return (-1);
+    }
+    CACHE_STATE(tif, sp);
+    while (occ > 0)
+    {
+        a0 = 0;
+        RunLength = 0;
+        pa = thisrun = sp->curruns;
+        pb = sp->refruns;
+        b1 = *pb++;
 #ifdef FAX3_DEBUG
-		printf("\nBitAcc=%08X, BitsAvail = %d\n", BitAcc, BitsAvail);
-		printf("-------------------- %d\n", tif->tif_row);
-		fflush(stdout);
+        printf("\nBitAcc=%08" PRIX32 ", BitsAvail = %d\n", BitAcc, BitsAvail);
+        printf("-------------------- %d\n", tif->tif_row);
+        fflush(stdout);
 #endif
-		EXPAND2D(EOFG4);
-                if (EOLcnt)
-                    goto EOFG4;
-		(*sp->fill)(buf, thisrun, pa, lastx);
-		SETVALUE(0);		/* imaginary change for reference */
-		SWAP(uint32*, sp->curruns, sp->refruns);
-		buf += sp->b.rowbytes;
-		occ -= sp->b.rowbytes;
-		sp->line++;
-		continue;
-	EOFG4:
-                NeedBits16( 13, BADG4 );
-        BADG4:
+        EXPAND2D(EOFG4);
+        if (EOLcnt)
+            goto EOFG4;
+        if (((lastx + 7) >> 3) > (int)occ) /* check for buffer overrun */
+        {
+            TIFFErrorExtR(tif, module,
+                          "Buffer overrun detected : %" TIFF_SSIZE_FORMAT
+                          " bytes available, %d bits needed",
+                          occ, lastx);
+            return -1;
+        }
+        (*sp->fill)(buf, thisrun, pa, lastx);
+        SETVALUE(0); /* imaginary change for reference */
+        SWAP(uint32_t *, sp->curruns, sp->refruns);
+        buf += sp->b.rowbytes;
+        occ -= sp->b.rowbytes;
+        sp->line++;
+        continue;
+    EOFG4:
+        NeedBits16(13, BADG4);
+    BADG4:
 #ifdef FAX3_DEBUG
-                if( GetBits(13) != 0x1001 )
-                    fputs( "Bad EOFB\n", stderr );
-#endif                
-                ClrBits( 13 );
-		(*sp->fill)(buf, thisrun, pa, lastx);
-		UNCACHE_STATE(tif, sp);
-		return ( sp->line ? 1 : -1);	/* don't error on badly-terminated strips */
-	}
-	UNCACHE_STATE(tif, sp);
-	return (1);
+        if (GetBits(13) != 0x1001)
+            fputs("Bad EOFB\n", stderr);
+#endif
+        ClrBits(13);
+        if (((lastx + 7) >> 3) > (int)occ) /* check for buffer overrun */
+        {
+            TIFFErrorExtR(tif, module,
+                          "Buffer overrun detected : %" TIFF_SSIZE_FORMAT
+                          " bytes available, %d bits needed",
+                          occ, lastx);
+            return -1;
+        }
+        (*sp->fill)(buf, thisrun, pa, lastx);
+        UNCACHE_STATE(tif, sp);
+        return (sp->line ? 1 : -1); /* don't error on badly-terminated strips */
+    }
+    UNCACHE_STATE(tif, sp);
+    return (1);
 }
-#undef	SWAP
+#undef SWAP
 
 /*
  * Encode the requested amount of data.
  */
-static int
-Fax4Encode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s)
+static int Fax4Encode(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s)
 {
-	static const char module[] = "Fax4Encode";
-	Fax3CodecState *sp = EncoderState(tif);
-	(void) s;
-	if (cc % sp->b.rowbytes)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "Fractional scanlines cannot be written");
-		return (0);
-	}
-	while (cc > 0) {
-		if (!Fax3Encode2DRow(tif, bp, sp->refline, sp->b.rowpixels))
-			return (0);
-		_TIFFmemcpy(sp->refline, bp, sp->b.rowbytes);
-		bp += sp->b.rowbytes;
-		cc -= sp->b.rowbytes;
-	}
-	return (1);
+    static const char module[] = "Fax4Encode";
+    Fax3CodecState *sp = EncoderState(tif);
+    (void)s;
+    if (cc % sp->b.rowbytes)
+    {
+        TIFFErrorExtR(tif, module, "Fractional scanlines cannot be written");
+        return (0);
+    }
+    while (cc > 0)
+    {
+        if (!Fax3Encode2DRow(tif, bp, sp->refline, sp->b.rowpixels))
+            return (0);
+        _TIFFmemcpy(sp->refline, bp, sp->b.rowbytes);
+        bp += sp->b.rowbytes;
+        cc -= sp->b.rowbytes;
+    }
+    return (1);
 }
 
-static int
-Fax4PostEncode(TIFF* tif)
+static int Fax4PostEncode(TIFF *tif)
 {
-	Fax3CodecState *sp = EncoderState(tif);
+    Fax3CodecState *sp = EncoderState(tif);
 
-	/* terminate strip w/ EOFB */
-	Fax3PutBits(tif, EOL, 12);
-	Fax3PutBits(tif, EOL, 12);
-	if (sp->bit != 8)
-		Fax3FlushBits(tif, sp);
-	return (1);
+    /* terminate strip w/ EOFB */
+    Fax3PutBits(tif, EOL, 12);
+    Fax3PutBits(tif, EOL, 12);
+    if (sp->bit != 8)
+        Fax3FlushBits(tif, sp);
+    return (1);
 }
 
-int
-TIFFInitCCITTFax4(TIFF* tif, int scheme)
+int TIFFInitCCITTFax4(TIFF *tif, int scheme)
 {
-	(void) scheme;
-	if (InitCCITTFax3(tif)) {		/* reuse G3 support */
-		/*
-		 * Merge codec-specific tag information.
-		 */
-		if (!_TIFFMergeFields(tif, fax4Fields,
-				      TIFFArrayCount(fax4Fields))) {
-			TIFFErrorExt(tif->tif_clientdata, "TIFFInitCCITTFax4",
-			"Merging CCITT Fax 4 codec-specific tags failed");
-			return 0;
-		}
+    (void)scheme;
+    if (InitCCITTFax3(tif))
+    { /* reuse G3 support */
+        /*
+         * Merge codec-specific tag information.
+         */
+        if (!_TIFFMergeFields(tif, fax4Fields, TIFFArrayCount(fax4Fields)))
+        {
+            TIFFErrorExtR(tif, "TIFFInitCCITTFax4",
+                          "Merging CCITT Fax 4 codec-specific tags failed");
+            return 0;
+        }
 
-		tif->tif_decoderow = Fax4Decode;
-		tif->tif_decodestrip = Fax4Decode;
-		tif->tif_decodetile = Fax4Decode;
-		tif->tif_encoderow = Fax4Encode;
-		tif->tif_encodestrip = Fax4Encode;
-		tif->tif_encodetile = Fax4Encode;
-		tif->tif_postencode = Fax4PostEncode;
-		/*
-		 * Suppress RTC at the end of each strip.
-		 */
-		return TIFFSetField(tif, TIFFTAG_FAXMODE, FAXMODE_NORTC);
-	} else
-		return (0);
+        tif->tif_decoderow = Fax4Decode;
+        tif->tif_decodestrip = Fax4Decode;
+        tif->tif_decodetile = Fax4Decode;
+        tif->tif_encoderow = Fax4Encode;
+        tif->tif_encodestrip = Fax4Encode;
+        tif->tif_encodetile = Fax4Encode;
+        tif->tif_postencode = Fax4PostEncode;
+        /*
+         * Suppress RTC at the end of each strip.
+         */
+        return TIFFSetField(tif, TIFFTAG_FAXMODE, FAXMODE_NORTC);
+    }
+    else
+        return (0);
 }
 
 /*
@@ -1552,95 +1645,91 @@
 /*
  * Decode the requested amount of RLE-encoded data.
  */
-static int
-Fax3DecodeRLE(TIFF* tif, uint8* buf, tmsize_t occ, uint16 s)
+static int Fax3DecodeRLE(TIFF *tif, uint8_t *buf, tmsize_t occ, uint16_t s)
 {
-	DECLARE_STATE(tif, sp, "Fax3DecodeRLE");
-	int mode = sp->b.mode;
-	(void) s;
-	if (occ % sp->b.rowbytes)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "Fractional scanlines cannot be read");
-		return (-1);
-	}
-	CACHE_STATE(tif, sp);
-	thisrun = sp->curruns;
-	while (occ > 0) {
-		a0 = 0;
-		RunLength = 0;
-		pa = thisrun;
+    DECLARE_STATE(tif, sp, "Fax3DecodeRLE");
+    int mode = sp->b.mode;
+    (void)s;
+    if (occ % sp->b.rowbytes)
+    {
+        TIFFErrorExtR(tif, module, "Fractional scanlines cannot be read");
+        return (-1);
+    }
+    CACHE_STATE(tif, sp);
+    thisrun = sp->curruns;
+    while (occ > 0)
+    {
+        a0 = 0;
+        RunLength = 0;
+        pa = thisrun;
 #ifdef FAX3_DEBUG
-		printf("\nBitAcc=%08X, BitsAvail = %d\n", BitAcc, BitsAvail);
-		printf("-------------------- %d\n", tif->tif_row);
-		fflush(stdout);
+        printf("\nBitAcc=%08" PRIX32 ", BitsAvail = %d\n", BitAcc, BitsAvail);
+        printf("-------------------- %" PRIu32 "\n", tif->tif_row);
+        fflush(stdout);
 #endif
-		EXPAND1D(EOFRLE);
-		(*sp->fill)(buf, thisrun, pa, lastx);
-		/*
-		 * Cleanup at the end of the row.
-		 */
-		if (mode & FAXMODE_BYTEALIGN) {
-			int n = BitsAvail - (BitsAvail &~ 7);
-			ClrBits(n);
-		} else if (mode & FAXMODE_WORDALIGN) {
-			int n = BitsAvail - (BitsAvail &~ 15);
-			ClrBits(n);
-			if (BitsAvail == 0 && !isAligned(cp, uint16))
-			    cp++;
-		}
-		buf += sp->b.rowbytes;
-		occ -= sp->b.rowbytes;
-		sp->line++;
-		continue;
-	EOFRLE:				/* premature EOF */
-		(*sp->fill)(buf, thisrun, pa, lastx);
-		UNCACHE_STATE(tif, sp);
-		return (-1);
-	}
-	UNCACHE_STATE(tif, sp);
-	return (1);
+        EXPAND1D(EOFRLE);
+        (*sp->fill)(buf, thisrun, pa, lastx);
+        /*
+         * Cleanup at the end of the row.
+         */
+        if (mode & FAXMODE_BYTEALIGN)
+        {
+            int n = BitsAvail - (BitsAvail & ~7);
+            ClrBits(n);
+        }
+        else if (mode & FAXMODE_WORDALIGN)
+        {
+            int n = BitsAvail - (BitsAvail & ~15);
+            ClrBits(n);
+            if (BitsAvail == 0 && !isAligned(cp, uint16_t))
+                cp++;
+        }
+        buf += sp->b.rowbytes;
+        occ -= sp->b.rowbytes;
+        sp->line++;
+        continue;
+    EOFRLE: /* premature EOF */
+        (*sp->fill)(buf, thisrun, pa, lastx);
+        UNCACHE_STATE(tif, sp);
+        return (-1);
+    }
+    UNCACHE_STATE(tif, sp);
+    return (1);
 }
 
-int
-TIFFInitCCITTRLE(TIFF* tif, int scheme)
+int TIFFInitCCITTRLE(TIFF *tif, int scheme)
 {
-	(void) scheme;
-	if (InitCCITTFax3(tif)) {		/* reuse G3 support */
-		tif->tif_decoderow = Fax3DecodeRLE;
-		tif->tif_decodestrip = Fax3DecodeRLE;
-		tif->tif_decodetile = Fax3DecodeRLE;
-		/*
-		 * Suppress RTC+EOLs when encoding and byte-align data.
-		 */
-		return TIFFSetField(tif, TIFFTAG_FAXMODE,
-		    FAXMODE_NORTC|FAXMODE_NOEOL|FAXMODE_BYTEALIGN);
-	} else
-		return (0);
+    (void)scheme;
+    if (InitCCITTFax3(tif))
+    { /* reuse G3 support */
+        tif->tif_decoderow = Fax3DecodeRLE;
+        tif->tif_decodestrip = Fax3DecodeRLE;
+        tif->tif_decodetile = Fax3DecodeRLE;
+        /*
+         * Suppress RTC+EOLs when encoding and byte-align data.
+         */
+        return TIFFSetField(tif, TIFFTAG_FAXMODE,
+                            FAXMODE_NORTC | FAXMODE_NOEOL | FAXMODE_BYTEALIGN);
+    }
+    else
+        return (0);
 }
 
-int
-TIFFInitCCITTRLEW(TIFF* tif, int scheme)
+int TIFFInitCCITTRLEW(TIFF *tif, int scheme)
 {
-	(void) scheme;
-	if (InitCCITTFax3(tif)) {		/* reuse G3 support */
-		tif->tif_decoderow = Fax3DecodeRLE;
-		tif->tif_decodestrip = Fax3DecodeRLE;
-		tif->tif_decodetile = Fax3DecodeRLE;  
-		/*
-		 * Suppress RTC+EOLs when encoding and word-align data.
-		 */
-		return TIFFSetField(tif, TIFFTAG_FAXMODE,
-		    FAXMODE_NORTC|FAXMODE_NOEOL|FAXMODE_WORDALIGN);
-	} else
-		return (0);
+    (void)scheme;
+    if (InitCCITTFax3(tif))
+    { /* reuse G3 support */
+        tif->tif_decoderow = Fax3DecodeRLE;
+        tif->tif_decodestrip = Fax3DecodeRLE;
+        tif->tif_decodetile = Fax3DecodeRLE;
+        /*
+         * Suppress RTC+EOLs when encoding and word-align data.
+         */
+        return TIFFSetField(tif, TIFFTAG_FAXMODE,
+                            FAXMODE_NORTC | FAXMODE_NOEOL | FAXMODE_WORDALIGN);
+    }
+    else
+        return (0);
 }
 #endif /* CCITT_SUPPORT */
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_fax3.h b/third_party/libtiff/tif_fax3.h
index abadcd9..e095009 100644
--- a/third_party/libtiff/tif_fax3.h
+++ b/third_party/libtiff/tif_fax3.h
@@ -2,28 +2,28 @@
  * Copyright (c) 1990-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
 #ifndef _FAX3_
-#define	_FAX3_
+#define _FAX3_
 /*
  * TIFF Library.
  *
@@ -41,7 +41,7 @@
  * The routine must have the type signature given below;
  * for example:
  *
- * fillruns(unsigned char* buf, uint32* runs, uint32* erun, uint32 lastx)
+ * fillruns(unsigned char* buf, uint32_t* runs, uint32_t* erun, uint32_t lastx)
  *
  * where buf is place to set the bits, runs is the array of b&w run
  * lengths (white then black), erun is the last run in the array, and
@@ -50,41 +50,47 @@
  * data in the run array as needed (e.g. to append zero runs to bring
  * the count up to a nice multiple).
  */
-typedef void (*TIFFFaxFillFunc)(unsigned char*, uint32*, uint32*, uint32);
+typedef void (*TIFFFaxFillFunc)(unsigned char *, uint32_t *, uint32_t *,
+                                uint32_t);
 
 /*
  * The default run filler; made external for other decoders.
  */
 #if defined(__cplusplus)
-extern "C" {
+extern "C"
+{
 #endif
-extern void _TIFFFax3fillruns(unsigned char*, uint32*, uint32*, uint32);
+    extern void _TIFFFax3fillruns(unsigned char *, uint32_t *, uint32_t *,
+                                  uint32_t);
 #if defined(__cplusplus)
 }
 #endif
 
-
 /* finite state machine codes */
-#define S_Null     0
-#define S_Pass     1
-#define S_Horiz    2
-#define S_V0       3
-#define S_VR       4
-#define S_VL       5
-#define S_Ext      6
-#define S_TermW    7
-#define S_TermB    8
-#define S_MakeUpW  9
-#define S_MakeUpB  10
-#define S_MakeUp   11
-#define S_EOL      12
+#define S_Null 0
+#define S_Pass 1
+#define S_Horiz 2
+#define S_V0 3
+#define S_VR 4
+#define S_VL 5
+#define S_Ext 6
+#define S_TermW 7
+#define S_TermB 8
+#define S_MakeUpW 9
+#define S_MakeUpB 10
+#define S_MakeUp 11
+#define S_EOL 12
 
-/* WARNING: do not change the layout of this structure as the HylaFAX software */
-/* really depends on it. See http://bugzilla.maptools.org/show_bug.cgi?id=2636 */
-typedef struct {                /* state table entry */
-	unsigned char State;    /* see above */
-	unsigned char Width;    /* width of code in bits */
-	uint32 Param;           /* unsigned 32-bit run length in bits (holds on 16 bit actually, but cannot be changed. See above warning) */
+/* WARNING: do not change the layout of this structure as the HylaFAX software
+ */
+/* really depends on it. See http://bugzilla.maptools.org/show_bug.cgi?id=2636
+ */
+typedef struct
+{                        /* state table entry */
+    unsigned char State; /* see above */
+    unsigned char Width; /* width of code in bits */
+    uint32_t Param;      /* unsigned 32-bit run length in bits (holds on 16 bit
+                            actually, but cannot be changed. See above warning) */
 } TIFFFaxTabEnt;
 
 extern const TIFFFaxTabEnt TIFFFaxMainTable[];
@@ -108,7 +114,7 @@
  */
 
 #ifndef EndOfData
-#define EndOfData()	(cp >= ep)
+#define EndOfData() (cp >= ep)
 #endif
 /*
  * Need <=8 or <=16 bits of input data.  Unlike viewfax we
@@ -134,116 +140,143 @@
  * otherwise we should get the right answer.
  */
 #ifndef NeedBits8
-#define NeedBits8(n,eoflab) do {					\
-    if (BitsAvail < (n)) {						\
-	if (EndOfData()) {						\
-	    if (BitsAvail == 0)			/* no valid bits */	\
-		goto eoflab;						\
-	    BitsAvail = (n);			/* pad with zeros */	\
-	} else {							\
-	    BitAcc |= ((uint32) bitmap[*cp++])<<BitsAvail;		\
-	    BitsAvail += 8;						\
-	}								\
-    }									\
-} while (0)
+#define NeedBits8(n, eoflab)                                                   \
+    do                                                                         \
+    {                                                                          \
+        if (BitsAvail < (n))                                                   \
+        {                                                                      \
+            if (EndOfData())                                                   \
+            {                                                                  \
+                if (BitsAvail == 0) /* no valid bits */                        \
+                    goto eoflab;                                               \
+                BitsAvail = (n); /* pad with zeros */                          \
+            }                                                                  \
+            else                                                               \
+            {                                                                  \
+                BitAcc |= ((uint32_t)bitmap[*cp++]) << BitsAvail;              \
+                BitsAvail += 8;                                                \
+            }                                                                  \
+        }                                                                      \
+    } while (0)
 #endif
 #ifndef NeedBits16
-#define NeedBits16(n,eoflab) do {					\
-    if (BitsAvail < (n)) {						\
-	if (EndOfData()) {						\
-	    if (BitsAvail == 0)			/* no valid bits */	\
-		goto eoflab;						\
-	    BitsAvail = (n);			/* pad with zeros */	\
-	} else {							\
-	    BitAcc |= ((uint32) bitmap[*cp++])<<BitsAvail;		\
-	    if ((BitsAvail += 8) < (n)) {				\
-		if (EndOfData()) {					\
-		    /* NB: we know BitsAvail is non-zero here */	\
-		    BitsAvail = (n);		/* pad with zeros */	\
-		} else {						\
-		    BitAcc |= ((uint32) bitmap[*cp++])<<BitsAvail;	\
-		    BitsAvail += 8;					\
-		}							\
-	    }								\
-	}								\
-    }									\
-} while (0)
+#define NeedBits16(n, eoflab)                                                  \
+    do                                                                         \
+    {                                                                          \
+        if (BitsAvail < (n))                                                   \
+        {                                                                      \
+            if (EndOfData())                                                   \
+            {                                                                  \
+                if (BitsAvail == 0) /* no valid bits */                        \
+                    goto eoflab;                                               \
+                BitsAvail = (n); /* pad with zeros */                          \
+            }                                                                  \
+            else                                                               \
+            {                                                                  \
+                BitAcc |= ((uint32_t)bitmap[*cp++]) << BitsAvail;              \
+                if ((BitsAvail += 8) < (n))                                    \
+                {                                                              \
+                    if (EndOfData())                                           \
+                    {                                                          \
+                        /* NB: we know BitsAvail is non-zero here */           \
+                        BitsAvail = (n); /* pad with zeros */                  \
+                    }                                                          \
+                    else                                                       \
+                    {                                                          \
+                        BitAcc |= ((uint32_t)bitmap[*cp++]) << BitsAvail;      \
+                        BitsAvail += 8;                                        \
+                    }                                                          \
+                }                                                              \
+            }                                                                  \
+        }                                                                      \
+    } while (0)
 #endif
-#define GetBits(n)	(BitAcc & ((1<<(n))-1))
-#define ClrBits(n) do {							\
-    BitsAvail -= (n);							\
-    BitAcc >>= (n);							\
-} while (0)
+#define GetBits(n) (BitAcc & ((1 << (n)) - 1))
+#define ClrBits(n)                                                             \
+    do                                                                         \
+    {                                                                          \
+        BitsAvail -= (n);                                                      \
+        BitAcc >>= (n);                                                        \
+    } while (0)
 
 #ifdef FAX3_DEBUG
-static const char* StateNames[] = {
-    "Null   ",
-    "Pass   ",
-    "Horiz  ",
-    "V0     ",
-    "VR     ",
-    "VL     ",
-    "Ext    ",
-    "TermW  ",
-    "TermB  ",
-    "MakeUpW",
-    "MakeUpB",
-    "MakeUp ",
-    "EOL    ",
+static const char *StateNames[] = {
+    "Null   ", "Pass   ", "Horiz  ", "V0     ", "VR     ", "VL     ", "Ext    ",
+    "TermW  ", "TermB  ", "MakeUpW", "MakeUpB", "MakeUp ", "EOL    ",
 };
 #define DEBUG_SHOW putchar(BitAcc & (1 << t) ? '1' : '0')
-#define LOOKUP8(wid,tab,eoflab) do {					\
-    int t;								\
-    NeedBits8(wid,eoflab);						\
-    TabEnt = tab + GetBits(wid);					\
-    printf("%08lX/%d: %s%5d\t", (long) BitAcc, BitsAvail,		\
-	   StateNames[TabEnt->State], TabEnt->Param);			\
-    for (t = 0; t < TabEnt->Width; t++)					\
-	DEBUG_SHOW;							\
-    putchar('\n');							\
-    fflush(stdout);							\
-    ClrBits(TabEnt->Width);						\
-} while (0)
-#define LOOKUP16(wid,tab,eoflab) do {					\
-    int t;								\
-    NeedBits16(wid,eoflab);						\
-    TabEnt = tab + GetBits(wid);					\
-    printf("%08lX/%d: %s%5d\t", (long) BitAcc, BitsAvail,		\
-	   StateNames[TabEnt->State], TabEnt->Param);			\
-    for (t = 0; t < TabEnt->Width; t++)					\
-	DEBUG_SHOW;							\
-    putchar('\n');							\
-    fflush(stdout);							\
-    ClrBits(TabEnt->Width);						\
-} while (0)
+#define LOOKUP8(wid, tab, eoflab)                                              \
+    do                                                                         \
+    {                                                                          \
+        int t;                                                                 \
+        NeedBits8(wid, eoflab);                                                \
+        TabEnt = tab + GetBits(wid);                                           \
+        printf("%08lX/%d: %s%5d\t", (long)BitAcc, BitsAvail,                   \
+               StateNames[TabEnt->State], TabEnt->Param);                      \
+        for (t = 0; t < TabEnt->Width; t++)                                    \
+            DEBUG_SHOW;                                                        \
+        putchar('\n');                                                         \
+        fflush(stdout);                                                        \
+        ClrBits(TabEnt->Width);                                                \
+    } while (0)
+#define LOOKUP16(wid, tab, eoflab)                                             \
+    do                                                                         \
+    {                                                                          \
+        int t;                                                                 \
+        NeedBits16(wid, eoflab);                                               \
+        TabEnt = tab + GetBits(wid);                                           \
+        printf("%08lX/%d: %s%5d\t", (long)BitAcc, BitsAvail,                   \
+               StateNames[TabEnt->State], TabEnt->Param);                      \
+        for (t = 0; t < TabEnt->Width; t++)                                    \
+            DEBUG_SHOW;                                                        \
+        putchar('\n');                                                         \
+        fflush(stdout);                                                        \
+        ClrBits(TabEnt->Width);                                                \
+    } while (0)
 
-#define SETVALUE(x) do {							\
-    *pa++ = RunLength + (x);						\
-    printf("SETVALUE: %d\t%d\n", RunLength + (x), a0);			\
-    a0 += x;								\
-    RunLength = 0;							\
-} while (0)
+#define SETVALUE(x)                                                            \
+    do                                                                         \
+    {                                                                          \
+        *pa++ = RunLength + (x);                                               \
+        printf("SETVALUE: %d\t%d\n", RunLength + (x), a0);                     \
+        a0 += x;                                                               \
+        RunLength = 0;                                                         \
+    } while (0)
 #else
-#define LOOKUP8(wid,tab,eoflab) do {					\
-    NeedBits8(wid,eoflab);						\
-    TabEnt = tab + GetBits(wid);					\
-    ClrBits(TabEnt->Width);						\
-} while (0)
-#define LOOKUP16(wid,tab,eoflab) do {					\
-    NeedBits16(wid,eoflab);						\
-    TabEnt = tab + GetBits(wid);					\
-    ClrBits(TabEnt->Width);						\
-} while (0)
+#define LOOKUP8(wid, tab, eoflab)                                              \
+    do                                                                         \
+    {                                                                          \
+        NeedBits8(wid, eoflab);                                                \
+        TabEnt = tab + GetBits(wid);                                           \
+        ClrBits(TabEnt->Width);                                                \
+    } while (0)
+#define LOOKUP16(wid, tab, eoflab)                                             \
+    do                                                                         \
+    {                                                                          \
+        NeedBits16(wid, eoflab);                                               \
+        TabEnt = tab + GetBits(wid);                                           \
+        ClrBits(TabEnt->Width);                                                \
+    } while (0)
 
 /*
  * Append a run to the run length array for the
  * current row and reset decoding state.
  */
-#define SETVALUE(x) do {							\
-    *pa++ = RunLength + (x);						\
-    a0 += (x);								\
-    RunLength = 0;							\
-} while (0)
+#define SETVALUE(x)                                                            \
+    do                                                                         \
+    {                                                                          \
+        if (pa >= thisrun + sp->nruns)                                         \
+        {                                                                      \
+            TIFFErrorExtR(tif, module, "Buffer overflow at line %u of %s %u",  \
+                          sp->line, isTiled(tif) ? "tile" : "strip",           \
+                          isTiled(tif) ? tif->tif_curtile                      \
+                                       : tif->tif_curstrip);                   \
+            return (-1);                                                       \
+        }                                                                      \
+        *pa++ = RunLength + (x);                                               \
+        a0 += (x);                                                             \
+        RunLength = 0;                                                         \
+    } while (0)
 #endif
 
 /*
@@ -256,51 +289,62 @@
  * is non-zero then we still need to scan for the final flag
  * bit that is part of the EOL code.
  */
-#define	SYNC_EOL(eoflab) do {						\
-    if (EOLcnt == 0) {							\
-	for (;;) {							\
-	    NeedBits16(11,eoflab);					\
-	    if (GetBits(11) == 0)					\
-		break;							\
-	    ClrBits(1);							\
-	}								\
-    }									\
-    for (;;) {								\
-	NeedBits8(8,eoflab);						\
-	if (GetBits(8))							\
-	    break;							\
-	ClrBits(8);							\
-    }									\
-    while (GetBits(1) == 0)						\
-	ClrBits(1);							\
-    ClrBits(1);				/* EOL bit */			\
-    EOLcnt = 0;				/* reset EOL counter/flag */	\
-} while (0)
+#define SYNC_EOL(eoflab)                                                       \
+    do                                                                         \
+    {                                                                          \
+        if (EOLcnt == 0)                                                       \
+        {                                                                      \
+            for (;;)                                                           \
+            {                                                                  \
+                NeedBits16(11, eoflab);                                        \
+                if (GetBits(11) == 0)                                          \
+                    break;                                                     \
+                ClrBits(1);                                                    \
+            }                                                                  \
+        }                                                                      \
+        for (;;)                                                               \
+        {                                                                      \
+            NeedBits8(8, eoflab);                                              \
+            if (GetBits(8))                                                    \
+                break;                                                         \
+            ClrBits(8);                                                        \
+        }                                                                      \
+        while (GetBits(1) == 0)                                                \
+            ClrBits(1);                                                        \
+        ClrBits(1); /* EOL bit */                                              \
+        EOLcnt = 0; /* reset EOL counter/flag */                               \
+    } while (0)
 
 /*
  * Cleanup the array of runs after decoding a row.
  * We adjust final runs to insure the user buffer is not
  * overwritten and/or undecoded area is white filled.
  */
-#define	CLEANUP_RUNS() do {						\
-    if (RunLength)							\
-	SETVALUE(0);							\
-    if (a0 != lastx) {							\
-	badlength(a0, lastx);						\
-	while (a0 > lastx && pa > thisrun)				\
-	    a0 -= *--pa;						\
-	if (a0 < lastx) {						\
-	    if (a0 < 0)							\
-		a0 = 0;							\
-	    if ((pa-thisrun)&1)						\
-		SETVALUE(0);						\
-	    SETVALUE(lastx - a0);						\
-	} else if (a0 > lastx) {					\
-	    SETVALUE(lastx);						\
-	    SETVALUE(0);							\
-	}								\
-    }									\
-} while (0)
+#define CLEANUP_RUNS()                                                         \
+    do                                                                         \
+    {                                                                          \
+        if (RunLength)                                                         \
+            SETVALUE(0);                                                       \
+        if (a0 != lastx)                                                       \
+        {                                                                      \
+            badlength(a0, lastx);                                              \
+            while (a0 > lastx && pa > thisrun)                                 \
+                a0 -= *--pa;                                                   \
+            if (a0 < lastx)                                                    \
+            {                                                                  \
+                if (a0 < 0)                                                    \
+                    a0 = 0;                                                    \
+                if ((pa - thisrun) & 1)                                        \
+                    SETVALUE(0);                                               \
+                SETVALUE(lastx - a0);                                          \
+            }                                                                  \
+            else if (a0 > lastx)                                               \
+            {                                                                  \
+                SETVALUE(lastx);                                               \
+                SETVALUE(0);                                                   \
+            }                                                                  \
+        }                                                                      \
+    } while (0)
 
 /*
  * Decode a line of 1D-encoded data.
@@ -314,225 +358,291 @@
  * the original code depended on the input data being zero-padded to
  * insure the decoder recognized an EOL before running out of data.
  */
-#define EXPAND1D(eoflab) do {						\
-    for (;;) {								\
-	for (;;) {							\
-	    LOOKUP16(12, TIFFFaxWhiteTable, eof1d);			\
-	    switch (TabEnt->State) {					\
-	    case S_EOL:							\
-		EOLcnt = 1;						\
-		goto done1d;						\
-	    case S_TermW:						\
-		SETVALUE(TabEnt->Param);					\
-		goto doneWhite1d;					\
-	    case S_MakeUpW:						\
-	    case S_MakeUp:						\
-		a0 += TabEnt->Param;					\
-		RunLength += TabEnt->Param;				\
-		break;							\
-	    default:							\
-		unexpected("WhiteTable", a0);				\
-		goto done1d;						\
-	    }								\
-	}								\
-    doneWhite1d:							\
-	if (a0 >= lastx)						\
-	    goto done1d;						\
-	for (;;) {							\
-	    LOOKUP16(13, TIFFFaxBlackTable, eof1d);			\
-	    switch (TabEnt->State) {					\
-	    case S_EOL:							\
-		EOLcnt = 1;						\
-		goto done1d;						\
-	    case S_TermB:						\
-		SETVALUE(TabEnt->Param);					\
-		goto doneBlack1d;					\
-	    case S_MakeUpB:						\
-	    case S_MakeUp:						\
-		a0 += TabEnt->Param;					\
-		RunLength += TabEnt->Param;				\
-		break;							\
-	    default:							\
-		unexpected("BlackTable", a0);				\
-		goto done1d;						\
-	    }								\
-	}								\
-    doneBlack1d:							\
-	if (a0 >= lastx)						\
-	    goto done1d;						\
-        if( *(pa-1) == 0 && *(pa-2) == 0 )				\
-            pa -= 2;                                                    \
-    }									\
-eof1d:									\
-    prematureEOF(a0);							\
-    CLEANUP_RUNS();							\
-    goto eoflab;							\
-done1d:									\
-    CLEANUP_RUNS();							\
-} while (0)
+#define EXPAND1D(eoflab)                                                       \
+    do                                                                         \
+    {                                                                          \
+        for (;;)                                                               \
+        {                                                                      \
+            for (;;)                                                           \
+            {                                                                  \
+                LOOKUP16(12, TIFFFaxWhiteTable, eof1d);                        \
+                switch (TabEnt->State)                                         \
+                {                                                              \
+                    case S_EOL:                                                \
+                        EOLcnt = 1;                                            \
+                        goto done1d;                                           \
+                    case S_TermW:                                              \
+                        SETVALUE(TabEnt->Param);                               \
+                        goto doneWhite1d;                                      \
+                    case S_MakeUpW:                                            \
+                    case S_MakeUp:                                             \
+                        a0 += TabEnt->Param;                                   \
+                        RunLength += TabEnt->Param;                            \
+                        break;                                                 \
+                    default:                                                   \
+                        unexpected("WhiteTable", a0);                          \
+                        goto done1d;                                           \
+                }                                                              \
+            }                                                                  \
+        doneWhite1d:                                                           \
+            if (a0 >= lastx)                                                   \
+                goto done1d;                                                   \
+            for (;;)                                                           \
+            {                                                                  \
+                LOOKUP16(13, TIFFFaxBlackTable, eof1d);                        \
+                switch (TabEnt->State)                                         \
+                {                                                              \
+                    case S_EOL:                                                \
+                        EOLcnt = 1;                                            \
+                        goto done1d;                                           \
+                    case S_TermB:                                              \
+                        SETVALUE(TabEnt->Param);                               \
+                        goto doneBlack1d;                                      \
+                    case S_MakeUpB:                                            \
+                    case S_MakeUp:                                             \
+                        a0 += TabEnt->Param;                                   \
+                        RunLength += TabEnt->Param;                            \
+                        break;                                                 \
+                    default:                                                   \
+                        unexpected("BlackTable", a0);                          \
+                        goto done1d;                                           \
+                }                                                              \
+            }                                                                  \
+        doneBlack1d:                                                           \
+            if (a0 >= lastx)                                                   \
+                goto done1d;                                                   \
+            if (*(pa - 1) == 0 && *(pa - 2) == 0)                              \
+                pa -= 2;                                                       \
+        }                                                                      \
+    eof1d:                                                                     \
+        prematureEOF(a0);                                                      \
+        CLEANUP_RUNS();                                                        \
+        goto eoflab;                                                           \
+    done1d:                                                                    \
+        CLEANUP_RUNS();                                                        \
+    } while (0)
 
 /*
  * Update the value of b1 using the array
  * of runs for the reference line.
  */
-#define CHECK_b1 do {							\
-    if (pa != thisrun) while (b1 <= a0 && b1 < lastx) {			\
-	b1 += pb[0] + pb[1];						\
-	pb += 2;							\
-    }									\
-} while (0)
+#define CHECK_b1                                                               \
+    do                                                                         \
+    {                                                                          \
+        if (pa != thisrun)                                                     \
+            while (b1 <= a0 && b1 < lastx)                                     \
+            {                                                                  \
+                if (pb + 1 >= sp->refruns + sp->nruns)                         \
+                {                                                              \
+                    TIFFErrorExtR(                                             \
+                        tif, module, "Buffer overflow at line %u of %s %u",    \
+                        sp->line, isTiled(tif) ? "tile" : "strip",             \
+                        isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip);  \
+                    return (-1);                                               \
+                }                                                              \
+                b1 += pb[0] + pb[1];                                           \
+                pb += 2;                                                       \
+            }                                                                  \
+    } while (0)
 
 /*
  * Expand a row of 2D-encoded data.
  */
-#define EXPAND2D(eoflab) do {						\
-    while (a0 < lastx) {						\
-	LOOKUP8(7, TIFFFaxMainTable, eof2d);				\
-	switch (TabEnt->State) {					\
-	case S_Pass:							\
-	    CHECK_b1;							\
-	    b1 += *pb++;						\
-	    RunLength += b1 - a0;					\
-	    a0 = b1;							\
-	    b1 += *pb++;						\
-	    break;							\
-	case S_Horiz:							\
-	    if ((pa-thisrun)&1) {					\
-		for (;;) {	/* black first */			\
-		    LOOKUP16(13, TIFFFaxBlackTable, eof2d);		\
-		    switch (TabEnt->State) {				\
-		    case S_TermB:					\
-			SETVALUE(TabEnt->Param);				\
-			goto doneWhite2da;				\
-		    case S_MakeUpB:					\
-		    case S_MakeUp:					\
-			a0 += TabEnt->Param;				\
-			RunLength += TabEnt->Param;			\
-			break;						\
-		    default:						\
-			goto badBlack2d;				\
-		    }							\
-		}							\
-	    doneWhite2da:;						\
-		for (;;) {	/* then white */			\
-		    LOOKUP16(12, TIFFFaxWhiteTable, eof2d);		\
-		    switch (TabEnt->State) {				\
-		    case S_TermW:					\
-			SETVALUE(TabEnt->Param);				\
-			goto doneBlack2da;				\
-		    case S_MakeUpW:					\
-		    case S_MakeUp:					\
-			a0 += TabEnt->Param;				\
-			RunLength += TabEnt->Param;			\
-			break;						\
-		    default:						\
-			goto badWhite2d;				\
-		    }							\
-		}							\
-	    doneBlack2da:;						\
-	    } else {							\
-		for (;;) {	/* white first */			\
-		    LOOKUP16(12, TIFFFaxWhiteTable, eof2d);		\
-		    switch (TabEnt->State) {				\
-		    case S_TermW:					\
-			SETVALUE(TabEnt->Param);				\
-			goto doneWhite2db;				\
-		    case S_MakeUpW:					\
-		    case S_MakeUp:					\
-			a0 += TabEnt->Param;				\
-			RunLength += TabEnt->Param;			\
-			break;						\
-		    default:						\
-			goto badWhite2d;				\
-		    }							\
-		}							\
-	    doneWhite2db:;						\
-		for (;;) {	/* then black */			\
-		    LOOKUP16(13, TIFFFaxBlackTable, eof2d);		\
-		    switch (TabEnt->State) {				\
-		    case S_TermB:					\
-			SETVALUE(TabEnt->Param);				\
-			goto doneBlack2db;				\
-		    case S_MakeUpB:					\
-		    case S_MakeUp:					\
-			a0 += TabEnt->Param;				\
-			RunLength += TabEnt->Param;			\
-			break;						\
-		    default:						\
-			goto badBlack2d;				\
-		    }							\
-		}							\
-	    doneBlack2db:;						\
-	    }								\
-	    CHECK_b1;							\
-	    break;							\
-	case S_V0:							\
-	    CHECK_b1;							\
-	    SETVALUE(b1 - a0);						\
-	    b1 += *pb++;						\
-	    break;							\
-	case S_VR:							\
-	    CHECK_b1;							\
-	    SETVALUE(b1 - a0 + TabEnt->Param);				\
-	    b1 += *pb++;						\
-	    break;							\
-	case S_VL:							\
-	    CHECK_b1;							\
-	    if (b1 <= (int) (a0 + TabEnt->Param)) {			\
-		if (b1 < (int) (a0 + TabEnt->Param) || pa != thisrun) {	\
-		    unexpected("VL", a0);				\
-		    goto eol2d;						\
-		}							\
-	    }								\
-	    SETVALUE(b1 - a0 - TabEnt->Param);				\
-	    b1 -= *--pb;						\
-	    break;							\
-	case S_Ext:							\
-	    *pa++ = lastx - a0;						\
-	    extension(a0);						\
-	    goto eol2d;							\
-	case S_EOL:							\
-	    *pa++ = lastx - a0;						\
-	    NeedBits8(4,eof2d);						\
-	    if (GetBits(4))						\
-		unexpected("EOL", a0);					\
-            ClrBits(4);                                                 \
-	    EOLcnt = 1;							\
-	    goto eol2d;							\
-	default:							\
-	badMain2d:							\
-	    unexpected("MainTable", a0);				\
-	    goto eol2d;							\
-	badBlack2d:							\
-	    unexpected("BlackTable", a0);				\
-	    goto eol2d;							\
-	badWhite2d:							\
-	    unexpected("WhiteTable", a0);				\
-	    goto eol2d;							\
-	eof2d:								\
-	    prematureEOF(a0);						\
-	    CLEANUP_RUNS();						\
-	    goto eoflab;						\
-	}								\
-    }									\
-    if (RunLength) {							\
-	if (RunLength + a0 < lastx) {					\
-	    /* expect a final V0 */					\
-	    NeedBits8(1,eof2d);						\
-	    if (!GetBits(1))						\
-		goto badMain2d;						\
-	    ClrBits(1);							\
-	}								\
-	SETVALUE(0);							\
-    }									\
-eol2d:									\
-    CLEANUP_RUNS();							\
-} while (0)
+#define EXPAND2D(eoflab)                                                       \
+    do                                                                         \
+    {                                                                          \
+        while (a0 < lastx)                                                     \
+        {                                                                      \
+            if (pa >= thisrun + sp->nruns)                                     \
+            {                                                                  \
+                TIFFErrorExtR(                                                 \
+                    tif, module, "Buffer overflow at line %u of %s %u",        \
+                    sp->line, isTiled(tif) ? "tile" : "strip",                 \
+                    isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip);      \
+                return (-1);                                                   \
+            }                                                                  \
+            LOOKUP8(7, TIFFFaxMainTable, eof2d);                               \
+            switch (TabEnt->State)                                             \
+            {                                                                  \
+                case S_Pass:                                                   \
+                    CHECK_b1;                                                  \
+                    if (pb + 1 >= sp->refruns + sp->nruns)                     \
+                    {                                                          \
+                        TIFFErrorExtR(tif, module,                             \
+                                      "Buffer overflow at line %u of %s %u",   \
+                                      sp->line,                                \
+                                      isTiled(tif) ? "tile" : "strip",         \
+                                      isTiled(tif) ? tif->tif_curtile          \
+                                                   : tif->tif_curstrip);       \
+                        return (-1);                                           \
+                    }                                                          \
+                    b1 += *pb++;                                               \
+                    RunLength += b1 - a0;                                      \
+                    a0 = b1;                                                   \
+                    b1 += *pb++;                                               \
+                    break;                                                     \
+                case S_Horiz:                                                  \
+                    if ((pa - thisrun) & 1)                                    \
+                    {                                                          \
+                        for (;;)                                               \
+                        { /* black first */                                    \
+                            LOOKUP16(13, TIFFFaxBlackTable, eof2d);            \
+                            switch (TabEnt->State)                             \
+                            {                                                  \
+                                case S_TermB:                                  \
+                                    SETVALUE(TabEnt->Param);                   \
+                                    goto doneWhite2da;                         \
+                                case S_MakeUpB:                                \
+                                case S_MakeUp:                                 \
+                                    a0 += TabEnt->Param;                       \
+                                    RunLength += TabEnt->Param;                \
+                                    break;                                     \
+                                default:                                       \
+                                    goto badBlack2d;                           \
+                            }                                                  \
+                        }                                                      \
+                    doneWhite2da:;                                             \
+                        for (;;)                                               \
+                        { /* then white */                                     \
+                            LOOKUP16(12, TIFFFaxWhiteTable, eof2d);            \
+                            switch (TabEnt->State)                             \
+                            {                                                  \
+                                case S_TermW:                                  \
+                                    SETVALUE(TabEnt->Param);                   \
+                                    goto doneBlack2da;                         \
+                                case S_MakeUpW:                                \
+                                case S_MakeUp:                                 \
+                                    a0 += TabEnt->Param;                       \
+                                    RunLength += TabEnt->Param;                \
+                                    break;                                     \
+                                default:                                       \
+                                    goto badWhite2d;                           \
+                            }                                                  \
+                        }                                                      \
+                    doneBlack2da:;                                             \
+                    }                                                          \
+                    else                                                       \
+                    {                                                          \
+                        for (;;)                                               \
+                        { /* white first */                                    \
+                            LOOKUP16(12, TIFFFaxWhiteTable, eof2d);            \
+                            switch (TabEnt->State)                             \
+                            {                                                  \
+                                case S_TermW:                                  \
+                                    SETVALUE(TabEnt->Param);                   \
+                                    goto doneWhite2db;                         \
+                                case S_MakeUpW:                                \
+                                case S_MakeUp:                                 \
+                                    a0 += TabEnt->Param;                       \
+                                    RunLength += TabEnt->Param;                \
+                                    break;                                     \
+                                default:                                       \
+                                    goto badWhite2d;                           \
+                            }                                                  \
+                        }                                                      \
+                    doneWhite2db:;                                             \
+                        for (;;)                                               \
+                        { /* then black */                                     \
+                            LOOKUP16(13, TIFFFaxBlackTable, eof2d);            \
+                            switch (TabEnt->State)                             \
+                            {                                                  \
+                                case S_TermB:                                  \
+                                    SETVALUE(TabEnt->Param);                   \
+                                    goto doneBlack2db;                         \
+                                case S_MakeUpB:                                \
+                                case S_MakeUp:                                 \
+                                    a0 += TabEnt->Param;                       \
+                                    RunLength += TabEnt->Param;                \
+                                    break;                                     \
+                                default:                                       \
+                                    goto badBlack2d;                           \
+                            }                                                  \
+                        }                                                      \
+                    doneBlack2db:;                                             \
+                    }                                                          \
+                    CHECK_b1;                                                  \
+                    break;                                                     \
+                case S_V0:                                                     \
+                    CHECK_b1;                                                  \
+                    SETVALUE(b1 - a0);                                         \
+                    if (pb >= sp->refruns + sp->nruns)                         \
+                    {                                                          \
+                        TIFFErrorExtR(tif, module,                             \
+                                      "Buffer overflow at line %u of %s %u",   \
+                                      sp->line,                                \
+                                      isTiled(tif) ? "tile" : "strip",         \
+                                      isTiled(tif) ? tif->tif_curtile          \
+                                                   : tif->tif_curstrip);       \
+                        return (-1);                                           \
+                    }                                                          \
+                    b1 += *pb++;                                               \
+                    break;                                                     \
+                case S_VR:                                                     \
+                    CHECK_b1;                                                  \
+                    SETVALUE(b1 - a0 + TabEnt->Param);                         \
+                    if (pb >= sp->refruns + sp->nruns)                         \
+                    {                                                          \
+                        TIFFErrorExtR(tif, module,                             \
+                                      "Buffer overflow at line %u of %s %u",   \
+                                      sp->line,                                \
+                                      isTiled(tif) ? "tile" : "strip",         \
+                                      isTiled(tif) ? tif->tif_curtile          \
+                                                   : tif->tif_curstrip);       \
+                        return (-1);                                           \
+                    }                                                          \
+                    b1 += *pb++;                                               \
+                    break;                                                     \
+                case S_VL:                                                     \
+                    CHECK_b1;                                                  \
+                    if (b1 < (int)(a0 + TabEnt->Param))                        \
+                    {                                                          \
+                        unexpected("VL", a0);                                  \
+                        goto eol2d;                                            \
+                    }                                                          \
+                    SETVALUE(b1 - a0 - TabEnt->Param);                         \
+                    b1 -= *--pb;                                               \
+                    break;                                                     \
+                case S_Ext:                                                    \
+                    *pa++ = lastx - a0;                                        \
+                    extension(a0);                                             \
+                    goto eol2d;                                                \
+                case S_EOL:                                                    \
+                    *pa++ = lastx - a0;                                        \
+                    NeedBits8(4, eof2d);                                       \
+                    if (GetBits(4))                                            \
+                        unexpected("EOL", a0);                                 \
+                    ClrBits(4);                                                \
+                    EOLcnt = 1;                                                \
+                    goto eol2d;                                                \
+                default:                                                       \
+                badMain2d:                                                     \
+                    unexpected("MainTable", a0);                               \
+                    goto eol2d;                                                \
+                badBlack2d:                                                    \
+                    unexpected("BlackTable", a0);                              \
+                    goto eol2d;                                                \
+                badWhite2d:                                                    \
+                    unexpected("WhiteTable", a0);                              \
+                    goto eol2d;                                                \
+                eof2d:                                                         \
+                    prematureEOF(a0);                                          \
+                    CLEANUP_RUNS();                                            \
+                    goto eoflab;                                               \
+            }                                                                  \
+        }                                                                      \
+        if (RunLength)                                                         \
+        {                                                                      \
+            if (RunLength + a0 < lastx)                                        \
+            {                                                                  \
+                /* expect a final V0 */                                        \
+                NeedBits8(1, eof2d);                                           \
+                if (!GetBits(1))                                               \
+                    goto badMain2d;                                            \
+                ClrBits(1);                                                    \
+            }                                                                  \
+            SETVALUE(0);                                                       \
+        }                                                                      \
+    eol2d:                                                                     \
+        CLEANUP_RUNS();                                                        \
+    } while (0)
 #endif /* _FAX3_ */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_fax3sm.c b/third_party/libtiff/tif_fax3sm.c
index 822191e..ba2fc53 100644
--- a/third_party/libtiff/tif_fax3sm.c
+++ b/third_party/libtiff/tif_fax3sm.c
@@ -1,5 +1,6 @@
 /* WARNING, this file was automatically generated by the
     mkg3states program */
+#include <stdint.h>
 #include "tiff.h"
 #include "tif_fax3.h"
  const TIFFFaxTabEnt TIFFFaxMainTable[128] = {
diff --git a/third_party/libtiff/tif_flush.c b/third_party/libtiff/tif_flush.c
index f7fa207..ff9c1e2 100644
--- a/third_party/libtiff/tif_flush.c
+++ b/third_party/libtiff/tif_flush.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -27,30 +27,28 @@
  */
 #include "tiffiop.h"
 
-int
-TIFFFlush(TIFF* tif)
+int TIFFFlush(TIFF *tif)
 {
-    if( tif->tif_mode == O_RDONLY )
+    if (tif->tif_mode == O_RDONLY)
         return 1;
 
     if (!TIFFFlushData(tif))
         return (0);
-                
-    /* In update (r+) mode we try to detect the case where 
-       only the strip/tile map has been altered, and we try to 
-       rewrite only that portion of the directory without 
+
+    /* In update (r+) mode we try to detect the case where
+       only the strip/tile map has been altered, and we try to
+       rewrite only that portion of the directory without
        making any other changes */
-                
-    if( (tif->tif_flags & TIFF_DIRTYSTRIP)
-        && !(tif->tif_flags & TIFF_DIRTYDIRECT) 
-        && tif->tif_mode == O_RDWR )
+
+    if ((tif->tif_flags & TIFF_DIRTYSTRIP) &&
+        !(tif->tif_flags & TIFF_DIRTYDIRECT) && tif->tif_mode == O_RDWR)
     {
-        if( TIFFForceStrileArrayWriting(tif) )
+        if (TIFFForceStrileArrayWriting(tif))
             return 1;
     }
 
-    if ((tif->tif_flags & (TIFF_DIRTYDIRECT|TIFF_DIRTYSTRIP)) 
-        && !TIFFRewriteDirectory(tif))
+    if ((tif->tif_flags & (TIFF_DIRTYDIRECT | TIFF_DIRTYSTRIP)) &&
+        !TIFFRewriteDirectory(tif))
         return (0);
 
     return (1);
@@ -75,45 +73,43 @@
  *
  * Returns 1 in case of success, 0 otherwise.
  */
-int TIFFForceStrileArrayWriting(TIFF* tif)
+int TIFFForceStrileArrayWriting(TIFF *tif)
 {
     static const char module[] = "TIFFForceStrileArrayWriting";
     const int isTiled = TIFFIsTiled(tif);
 
     if (tif->tif_mode == O_RDONLY)
     {
-        TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-                     "File opened in read-only mode");
+        TIFFErrorExtR(tif, tif->tif_name, "File opened in read-only mode");
         return 0;
     }
-    if( tif->tif_diroff == 0 )
+    if (tif->tif_diroff == 0)
     {
-        TIFFErrorExt(tif->tif_clientdata, module,
-                     "Directory has not yet been written");
+        TIFFErrorExtR(tif, module, "Directory has not yet been written");
         return 0;
     }
-    if( (tif->tif_flags & TIFF_DIRTYDIRECT) != 0 )
+    if ((tif->tif_flags & TIFF_DIRTYDIRECT) != 0)
     {
-        TIFFErrorExt(tif->tif_clientdata, module,
-                     "Directory has changes other than the strile arrays. "
-                     "TIFFRewriteDirectory() should be called instead");
+        TIFFErrorExtR(tif, module,
+                      "Directory has changes other than the strile arrays. "
+                      "TIFFRewriteDirectory() should be called instead");
         return 0;
     }
 
-    if( !(tif->tif_flags & TIFF_DIRTYSTRIP) )
+    if (!(tif->tif_flags & TIFF_DIRTYSTRIP))
     {
-        if( !(tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 &&
-             tif->tif_dir.td_stripoffset_entry.tdir_count == 0 &&
-             tif->tif_dir.td_stripoffset_entry.tdir_type == 0 &&
-             tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0 &&
-             tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 &&
-             tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 &&
-             tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 &&
-             tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0) )
+        if (!(tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 &&
+              tif->tif_dir.td_stripoffset_entry.tdir_count == 0 &&
+              tif->tif_dir.td_stripoffset_entry.tdir_type == 0 &&
+              tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0 &&
+              tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 &&
+              tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 &&
+              tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 &&
+              tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0))
         {
-            TIFFErrorExt(tif->tif_clientdata, module,
-                        "Function not called together with "
-                        "TIFFDeferStrileArrayWriting()");
+            TIFFErrorExtR(tif, module,
+                          "Function not called together with "
+                          "TIFFDeferStrileArrayWriting()");
             return 0;
         }
 
@@ -121,18 +117,14 @@
             return 0;
     }
 
-    if( _TIFFRewriteField( tif,
-                           isTiled ? TIFFTAG_TILEOFFSETS :
-                                     TIFFTAG_STRIPOFFSETS,
-                           TIFF_LONG8,
-                           tif->tif_dir.td_nstrips,
-                           tif->tif_dir.td_stripoffset_p )
-        && _TIFFRewriteField( tif,
-                              isTiled ? TIFFTAG_TILEBYTECOUNTS :
-                                        TIFFTAG_STRIPBYTECOUNTS,
-                              TIFF_LONG8,
-                              tif->tif_dir.td_nstrips,
-                              tif->tif_dir.td_stripbytecount_p ) )
+    if (_TIFFRewriteField(tif,
+                          isTiled ? TIFFTAG_TILEOFFSETS : TIFFTAG_STRIPOFFSETS,
+                          TIFF_LONG8, tif->tif_dir.td_nstrips,
+                          tif->tif_dir.td_stripoffset_p) &&
+        _TIFFRewriteField(
+            tif, isTiled ? TIFFTAG_TILEBYTECOUNTS : TIFFTAG_STRIPBYTECOUNTS,
+            TIFF_LONG8, tif->tif_dir.td_nstrips,
+            tif->tif_dir.td_stripbytecount_p))
     {
         tif->tif_flags &= ~TIFF_DIRTYSTRIP;
         tif->tif_flags &= ~TIFF_BEENWRITING;
@@ -149,26 +141,17 @@
  * is not set, so that TIFFFlush() will proceed to write out the directory.
  * The documentation says returning 1 is an error indicator, but not having
  * been writing isn't exactly a an error.  Hopefully this doesn't cause
- * problems for other people. 
+ * problems for other people.
  */
-int
-TIFFFlushData(TIFF* tif)
+int TIFFFlushData(TIFF *tif)
 {
-	if ((tif->tif_flags & TIFF_BEENWRITING) == 0)
-		return (1);
-	if (tif->tif_flags & TIFF_POSTENCODE) {
-		tif->tif_flags &= ~TIFF_POSTENCODE;
-		if (!(*tif->tif_postencode)(tif))
-			return (0);
-	}
-	return (TIFFFlushData1(tif));
+    if ((tif->tif_flags & TIFF_BEENWRITING) == 0)
+        return (1);
+    if (tif->tif_flags & TIFF_POSTENCODE)
+    {
+        tif->tif_flags &= ~TIFF_POSTENCODE;
+        if (!(*tif->tif_postencode)(tif))
+            return (0);
+    }
+    return (TIFFFlushData1(tif));
 }
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_getimage.c b/third_party/libtiff/tif_getimage.c
index 3a86bcf..f5a05e5 100644
--- a/third_party/libtiff/tif_getimage.c
+++ b/third_party/libtiff/tif_getimage.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1991-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -28,41 +28,50 @@
  * Read and return a packed RGBA image.
  */
 #include "tiffiop.h"
-#include <stdio.h>
 #include <limits.h>
+#include <stdio.h>
 
-static int gtTileContig(TIFFRGBAImage*, uint32*, uint32, uint32);
-static int gtTileSeparate(TIFFRGBAImage*, uint32*, uint32, uint32);
-static int gtStripContig(TIFFRGBAImage*, uint32*, uint32, uint32);
-static int gtStripSeparate(TIFFRGBAImage*, uint32*, uint32, uint32);
-static int PickContigCase(TIFFRGBAImage*);
-static int PickSeparateCase(TIFFRGBAImage*);
+static int gtTileContig(TIFFRGBAImage *, uint32_t *, uint32_t, uint32_t);
+static int gtTileSeparate(TIFFRGBAImage *, uint32_t *, uint32_t, uint32_t);
+static int gtStripContig(TIFFRGBAImage *, uint32_t *, uint32_t, uint32_t);
+static int gtStripSeparate(TIFFRGBAImage *, uint32_t *, uint32_t, uint32_t);
+static int PickContigCase(TIFFRGBAImage *);
+static int PickSeparateCase(TIFFRGBAImage *);
 
-static int BuildMapUaToAa(TIFFRGBAImage* img);
-static int BuildMapBitdepth16To8(TIFFRGBAImage* img);
+static int BuildMapUaToAa(TIFFRGBAImage *img);
+static int BuildMapBitdepth16To8(TIFFRGBAImage *img);
 
 static const char photoTag[] = "PhotometricInterpretation";
 
-/* 
+/*
  * Helper constants used in Orientation tag handling
  */
 #define FLIP_VERTICALLY 0x01
 #define FLIP_HORIZONTALLY 0x02
 
+#define EMSG_BUF_SIZE 1024
+
 /*
  * Color conversion constants. We will define display types here.
  */
 
 static const TIFFDisplay display_sRGB = {
-	{			/* XYZ -> luminance matrix */
-		{  3.2410F, -1.5374F, -0.4986F },
-		{  -0.9692F, 1.8760F, 0.0416F },
-		{  0.0556F, -0.2040F, 1.0570F }
-	},	
-	100.0F, 100.0F, 100.0F,	/* Light o/p for reference white */
-	255, 255, 255,		/* Pixel values for ref. white */
-	1.0F, 1.0F, 1.0F,	/* Residual light o/p for black pixel */
-	2.4F, 2.4F, 2.4F,	/* Gamma values for the three guns */
+    {/* XYZ -> luminance matrix */
+     {3.2410F, -1.5374F, -0.4986F},
+     {-0.9692F, 1.8760F, 0.0416F},
+     {0.0556F, -0.2040F, 1.0570F}},
+    100.0F,
+    100.0F,
+    100.0F, /* Light o/p for reference white */
+    255,
+    255,
+    255, /* Pixel values for ref. white */
+    1.0F,
+    1.0F,
+    1.0F, /* Residual light o/p for black pixel */
+    2.4F,
+    2.4F,
+    2.4F, /* Gamma values for the three guns */
 };
 
 /*
@@ -71,443 +80,525 @@
  * be handled.  If 0 is returned, emsg contains the reason
  * why it is being rejected.
  */
-int
-TIFFRGBAImageOK(TIFF* tif, char emsg[1024])
+int TIFFRGBAImageOK(TIFF *tif, char emsg[EMSG_BUF_SIZE])
 {
-	TIFFDirectory* td = &tif->tif_dir;
-	uint16 photometric;
-	int colorchannels;
+    TIFFDirectory *td = &tif->tif_dir;
+    uint16_t photometric;
+    int colorchannels;
 
-	if (!tif->tif_decodestatus) {
-		sprintf(emsg, "Sorry, requested compression method is not configured");
-		return (0);
-	}
-	switch (td->td_bitspersample) {
-		case 1:
-		case 2:
-		case 4:
-		case 8:
-		case 16:
-			break;
-		default:
-			sprintf(emsg, "Sorry, can not handle images with %d-bit samples",
-			    td->td_bitspersample);
-			return (0);
-	}
-        if (td->td_sampleformat == SAMPLEFORMAT_IEEEFP) {
-                sprintf(emsg, "Sorry, can not handle images with IEEE floating-point samples");
+    if (!tif->tif_decodestatus)
+    {
+        snprintf(emsg, EMSG_BUF_SIZE,
+                 "Sorry, requested compression method is not configured");
+        return (0);
+    }
+    switch (td->td_bitspersample)
+    {
+        case 1:
+        case 2:
+        case 4:
+        case 8:
+        case 16:
+            break;
+        default:
+            snprintf(emsg, EMSG_BUF_SIZE,
+                     "Sorry, can not handle images with %" PRIu16
+                     "-bit samples",
+                     td->td_bitspersample);
+            return (0);
+    }
+    if (td->td_sampleformat == SAMPLEFORMAT_IEEEFP)
+    {
+        snprintf(
+            emsg, EMSG_BUF_SIZE,
+            "Sorry, can not handle images with IEEE floating-point samples");
+        return (0);
+    }
+    colorchannels = td->td_samplesperpixel - td->td_extrasamples;
+    if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric))
+    {
+        switch (colorchannels)
+        {
+            case 1:
+                photometric = PHOTOMETRIC_MINISBLACK;
+                break;
+            case 3:
+                photometric = PHOTOMETRIC_RGB;
+                break;
+            default:
+                snprintf(emsg, EMSG_BUF_SIZE, "Missing needed %s tag",
+                         photoTag);
                 return (0);
         }
-	colorchannels = td->td_samplesperpixel - td->td_extrasamples;
-	if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric)) {
-		switch (colorchannels) {
-			case 1:
-				photometric = PHOTOMETRIC_MINISBLACK;
-				break;
-			case 3:
-				photometric = PHOTOMETRIC_RGB;
-				break;
-			default:
-				sprintf(emsg, "Missing needed %s tag", photoTag);
-				return (0);
-		}
-	}
-	switch (photometric) {
-		case PHOTOMETRIC_MINISWHITE:
-		case PHOTOMETRIC_MINISBLACK:
-		case PHOTOMETRIC_PALETTE:
-			if (td->td_planarconfig == PLANARCONFIG_CONTIG
-			    && td->td_samplesperpixel != 1
-			    && td->td_bitspersample < 8 ) {
-				sprintf(emsg,
-				    "Sorry, can not handle contiguous data with %s=%d, "
-				    "and %s=%d and Bits/Sample=%d",
-				    photoTag, photometric,
-				    "Samples/pixel", td->td_samplesperpixel,
-				    td->td_bitspersample);
-				return (0);
-			}
-			/*
-			 * We should likely validate that any extra samples are either
-			 * to be ignored, or are alpha, and if alpha we should try to use
-			 * them.  But for now we won't bother with this.
-			*/
-			break;
-		case PHOTOMETRIC_YCBCR:
-			/*
-			 * TODO: if at all meaningful and useful, make more complete
-			 * support check here, or better still, refactor to let supporting
-			 * code decide whether there is support and what meaningful
-			 * error to return
-			 */
-			break;
-		case PHOTOMETRIC_RGB:
-			if (colorchannels < 3) {
-				sprintf(emsg, "Sorry, can not handle RGB image with %s=%d",
-				    "Color channels", colorchannels);
-				return (0);
-			}
-			break;
-		case PHOTOMETRIC_SEPARATED:
-			{
-				uint16 inkset;
-				TIFFGetFieldDefaulted(tif, TIFFTAG_INKSET, &inkset);
-				if (inkset != INKSET_CMYK) {
-					sprintf(emsg,
-					    "Sorry, can not handle separated image with %s=%d",
-					    "InkSet", inkset);
-					return 0;
-				}
-				if (td->td_samplesperpixel < 4) {
-					sprintf(emsg,
-					    "Sorry, can not handle separated image with %s=%d",
-					    "Samples/pixel", td->td_samplesperpixel);
-					return 0;
-				}
-				break;
-			}
-		case PHOTOMETRIC_LOGL:
-			if (td->td_compression != COMPRESSION_SGILOG) {
-				sprintf(emsg, "Sorry, LogL data must have %s=%d",
-				    "Compression", COMPRESSION_SGILOG);
-				return (0);
-			}
-			break;
-		case PHOTOMETRIC_LOGLUV:
-			if (td->td_compression != COMPRESSION_SGILOG &&
-			    td->td_compression != COMPRESSION_SGILOG24) {
-				sprintf(emsg, "Sorry, LogLuv data must have %s=%d or %d",
-				    "Compression", COMPRESSION_SGILOG, COMPRESSION_SGILOG24);
-				return (0);
-			}
-			if (td->td_planarconfig != PLANARCONFIG_CONTIG) {
-				sprintf(emsg, "Sorry, can not handle LogLuv images with %s=%d",
-				    "Planarconfiguration", td->td_planarconfig);
-				return (0);
-			}
-			if ( td->td_samplesperpixel != 3 || colorchannels != 3 ) {
-                                sprintf(emsg,
-                                        "Sorry, can not handle image with %s=%d, %s=%d",
-                                        "Samples/pixel", td->td_samplesperpixel,
-                                        "colorchannels", colorchannels);
-                                return 0;
-                        }
-			break;
-		case PHOTOMETRIC_CIELAB:
-                        if ( td->td_samplesperpixel != 3 || colorchannels != 3 || td->td_bitspersample != 8 ) {
-                                sprintf(emsg,
-                                        "Sorry, can not handle image with %s=%d, %s=%d and %s=%d",
-                                        "Samples/pixel", td->td_samplesperpixel,
-                                        "colorchannels", colorchannels,
-                                        "Bits/sample", td->td_bitspersample);
-                                return 0;
-                        }
-			break;
-                default:
-			sprintf(emsg, "Sorry, can not handle image with %s=%d",
-			    photoTag, photometric);
-			return (0);
-	}
-	return (1);
+    }
+    switch (photometric)
+    {
+        case PHOTOMETRIC_MINISWHITE:
+        case PHOTOMETRIC_MINISBLACK:
+        case PHOTOMETRIC_PALETTE:
+            if (td->td_planarconfig == PLANARCONFIG_CONTIG &&
+                td->td_samplesperpixel != 1 && td->td_bitspersample < 8)
+            {
+                snprintf(
+                    emsg, EMSG_BUF_SIZE,
+                    "Sorry, can not handle contiguous data with %s=%" PRIu16
+                    ", "
+                    "and %s=%" PRIu16 " and Bits/Sample=%" PRIu16 "",
+                    photoTag, photometric, "Samples/pixel",
+                    td->td_samplesperpixel, td->td_bitspersample);
+                return (0);
+            }
+            /*
+             * We should likely validate that any extra samples are either
+             * to be ignored, or are alpha, and if alpha we should try to use
+             * them.  But for now we won't bother with this.
+             */
+            break;
+        case PHOTOMETRIC_YCBCR:
+            /*
+             * TODO: if at all meaningful and useful, make more complete
+             * support check here, or better still, refactor to let supporting
+             * code decide whether there is support and what meaningful
+             * error to return
+             */
+            break;
+        case PHOTOMETRIC_RGB:
+            if (colorchannels < 3)
+            {
+                snprintf(emsg, EMSG_BUF_SIZE,
+                         "Sorry, can not handle RGB image with %s=%d",
+                         "Color channels", colorchannels);
+                return (0);
+            }
+            break;
+        case PHOTOMETRIC_SEPARATED:
+        {
+            uint16_t inkset;
+            TIFFGetFieldDefaulted(tif, TIFFTAG_INKSET, &inkset);
+            if (inkset != INKSET_CMYK)
+            {
+                snprintf(emsg, EMSG_BUF_SIZE,
+                         "Sorry, can not handle separated image with %s=%d",
+                         "InkSet", inkset);
+                return 0;
+            }
+            if (td->td_samplesperpixel < 4)
+            {
+                snprintf(
+                    emsg, EMSG_BUF_SIZE,
+                    "Sorry, can not handle separated image with %s=%" PRIu16,
+                    "Samples/pixel", td->td_samplesperpixel);
+                return 0;
+            }
+            break;
+        }
+        case PHOTOMETRIC_LOGL:
+            if (td->td_compression != COMPRESSION_SGILOG)
+            {
+                snprintf(emsg, EMSG_BUF_SIZE,
+                         "Sorry, LogL data must have %s=%d", "Compression",
+                         COMPRESSION_SGILOG);
+                return (0);
+            }
+            break;
+        case PHOTOMETRIC_LOGLUV:
+            if (td->td_compression != COMPRESSION_SGILOG &&
+                td->td_compression != COMPRESSION_SGILOG24)
+            {
+                snprintf(emsg, EMSG_BUF_SIZE,
+                         "Sorry, LogLuv data must have %s=%d or %d",
+                         "Compression", COMPRESSION_SGILOG,
+                         COMPRESSION_SGILOG24);
+                return (0);
+            }
+            if (td->td_planarconfig != PLANARCONFIG_CONTIG)
+            {
+                snprintf(emsg, EMSG_BUF_SIZE,
+                         "Sorry, can not handle LogLuv images with %s=%" PRIu16,
+                         "Planarconfiguration", td->td_planarconfig);
+                return (0);
+            }
+            if (td->td_samplesperpixel != 3 || colorchannels != 3)
+            {
+                snprintf(emsg, EMSG_BUF_SIZE,
+                         "Sorry, can not handle image with %s=%" PRIu16
+                         ", %s=%d",
+                         "Samples/pixel", td->td_samplesperpixel,
+                         "colorchannels", colorchannels);
+                return 0;
+            }
+            break;
+        case PHOTOMETRIC_CIELAB:
+            if (td->td_samplesperpixel != 3 || colorchannels != 3 ||
+                (td->td_bitspersample != 8 && td->td_bitspersample != 16))
+            {
+                snprintf(emsg, EMSG_BUF_SIZE,
+                         "Sorry, can not handle image with %s=%" PRIu16
+                         ", %s=%d and %s=%" PRIu16,
+                         "Samples/pixel", td->td_samplesperpixel,
+                         "colorchannels", colorchannels, "Bits/sample",
+                         td->td_bitspersample);
+                return 0;
+            }
+            break;
+        default:
+            snprintf(emsg, EMSG_BUF_SIZE,
+                     "Sorry, can not handle image with %s=%" PRIu16, photoTag,
+                     photometric);
+            return (0);
+    }
+    return (1);
 }
 
-void
-TIFFRGBAImageEnd(TIFFRGBAImage* img)
+void TIFFRGBAImageEnd(TIFFRGBAImage *img)
 {
-	if (img->Map) {
-		_TIFFfree(img->Map);
-		img->Map = NULL;
-	}
-	if (img->BWmap) {
-		_TIFFfree(img->BWmap);
-		img->BWmap = NULL;
-	}
-	if (img->PALmap) {
-		_TIFFfree(img->PALmap);
-		img->PALmap = NULL;
-	}
-	if (img->ycbcr) {
-		_TIFFfree(img->ycbcr);
-		img->ycbcr = NULL;
-	}
-	if (img->cielab) {
-		_TIFFfree(img->cielab);
-		img->cielab = NULL;
-	}
-	if (img->UaToAa) {
-		_TIFFfree(img->UaToAa);
-		img->UaToAa = NULL;
-	}
-	if (img->Bitdepth16To8) {
-		_TIFFfree(img->Bitdepth16To8);
-		img->Bitdepth16To8 = NULL;
-	}
+    if (img->Map)
+    {
+        _TIFFfreeExt(img->tif, img->Map);
+        img->Map = NULL;
+    }
+    if (img->BWmap)
+    {
+        _TIFFfreeExt(img->tif, img->BWmap);
+        img->BWmap = NULL;
+    }
+    if (img->PALmap)
+    {
+        _TIFFfreeExt(img->tif, img->PALmap);
+        img->PALmap = NULL;
+    }
+    if (img->ycbcr)
+    {
+        _TIFFfreeExt(img->tif, img->ycbcr);
+        img->ycbcr = NULL;
+    }
+    if (img->cielab)
+    {
+        _TIFFfreeExt(img->tif, img->cielab);
+        img->cielab = NULL;
+    }
+    if (img->UaToAa)
+    {
+        _TIFFfreeExt(img->tif, img->UaToAa);
+        img->UaToAa = NULL;
+    }
+    if (img->Bitdepth16To8)
+    {
+        _TIFFfreeExt(img->tif, img->Bitdepth16To8);
+        img->Bitdepth16To8 = NULL;
+    }
 
-	if( img->redcmap ) {
-		_TIFFfree( img->redcmap );
-		_TIFFfree( img->greencmap );
-		_TIFFfree( img->bluecmap );
-                img->redcmap = img->greencmap = img->bluecmap = NULL;
-	}
+    if (img->redcmap)
+    {
+        _TIFFfreeExt(img->tif, img->redcmap);
+        _TIFFfreeExt(img->tif, img->greencmap);
+        _TIFFfreeExt(img->tif, img->bluecmap);
+        img->redcmap = img->greencmap = img->bluecmap = NULL;
+    }
 }
 
-static int
-isCCITTCompression(TIFF* tif)
+static int isCCITTCompression(TIFF *tif)
 {
-    uint16 compress;
+    uint16_t compress;
     TIFFGetField(tif, TIFFTAG_COMPRESSION, &compress);
     return (compress == COMPRESSION_CCITTFAX3 ||
-	    compress == COMPRESSION_CCITTFAX4 ||
-	    compress == COMPRESSION_CCITTRLE ||
-	    compress == COMPRESSION_CCITTRLEW);
+            compress == COMPRESSION_CCITTFAX4 ||
+            compress == COMPRESSION_CCITTRLE ||
+            compress == COMPRESSION_CCITTRLEW);
 }
 
-int
-TIFFRGBAImageBegin(TIFFRGBAImage* img, TIFF* tif, int stop, char emsg[1024])
+int TIFFRGBAImageBegin(TIFFRGBAImage *img, TIFF *tif, int stop,
+                       char emsg[EMSG_BUF_SIZE])
 {
-	uint16* sampleinfo;
-	uint16 extrasamples;
-	uint16 planarconfig;
-	uint16 compress;
-	int colorchannels;
-	uint16 *red_orig, *green_orig, *blue_orig;
-	int n_color;
-	
-	if( !TIFFRGBAImageOK(tif, emsg) )
-		return 0;
+    uint16_t *sampleinfo;
+    uint16_t extrasamples;
+    uint16_t planarconfig;
+    uint16_t compress;
+    int colorchannels;
+    uint16_t *red_orig, *green_orig, *blue_orig;
+    int n_color;
 
-	/* Initialize to normal values */
-	img->row_offset = 0;
-	img->col_offset = 0;
-	img->redcmap = NULL;
-	img->greencmap = NULL;
-	img->bluecmap = NULL;
-	img->Map = NULL;
-	img->BWmap = NULL;
-	img->PALmap = NULL;
-	img->ycbcr = NULL;
-	img->cielab = NULL;
-	img->UaToAa = NULL;
-	img->Bitdepth16To8 = NULL;
-	img->req_orientation = ORIENTATION_BOTLEFT;     /* It is the default */
+    if (!TIFFRGBAImageOK(tif, emsg))
+        return 0;
 
-	img->tif = tif;
-	img->stoponerr = stop;
-	TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &img->bitspersample);
-	switch (img->bitspersample) {
-		case 1:
-		case 2:
-		case 4:
-		case 8:
-		case 16:
-			break;
-		default:
-			sprintf(emsg, "Sorry, can not handle images with %d-bit samples",
-			    img->bitspersample);
-			goto fail_return;
-	}
-	img->alpha = 0;
-	TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &img->samplesperpixel);
-	TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES,
-	    &extrasamples, &sampleinfo);
-	if (extrasamples >= 1)
-	{
-		switch (sampleinfo[0]) {
-			case EXTRASAMPLE_UNSPECIFIED:          /* Workaround for some images without */
-				if (img->samplesperpixel > 3)  /* correct info about alpha channel */
-					img->alpha = EXTRASAMPLE_ASSOCALPHA;
-				break;
-			case EXTRASAMPLE_ASSOCALPHA:           /* data is pre-multiplied */
-			case EXTRASAMPLE_UNASSALPHA:           /* data is not pre-multiplied */
-				img->alpha = sampleinfo[0];
-				break;
-		}
-	}
+    /* Initialize to normal values */
+    img->row_offset = 0;
+    img->col_offset = 0;
+    img->redcmap = NULL;
+    img->greencmap = NULL;
+    img->bluecmap = NULL;
+    img->Map = NULL;
+    img->BWmap = NULL;
+    img->PALmap = NULL;
+    img->ycbcr = NULL;
+    img->cielab = NULL;
+    img->UaToAa = NULL;
+    img->Bitdepth16To8 = NULL;
+    img->req_orientation = ORIENTATION_BOTLEFT; /* It is the default */
+
+    img->tif = tif;
+    img->stoponerr = stop;
+    TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &img->bitspersample);
+    switch (img->bitspersample)
+    {
+        case 1:
+        case 2:
+        case 4:
+        case 8:
+        case 16:
+            break;
+        default:
+            snprintf(emsg, EMSG_BUF_SIZE,
+                     "Sorry, can not handle images with %" PRIu16
+                     "-bit samples",
+                     img->bitspersample);
+            goto fail_return;
+    }
+    img->alpha = 0;
+    TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &img->samplesperpixel);
+    TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES, &extrasamples,
+                          &sampleinfo);
+    if (extrasamples >= 1)
+    {
+        switch (sampleinfo[0])
+        {
+            case EXTRASAMPLE_UNSPECIFIED: /* Workaround for some images without
+                                           */
+                if (img->samplesperpixel >
+                    3) /* correct info about alpha channel */
+                    img->alpha = EXTRASAMPLE_ASSOCALPHA;
+                break;
+            case EXTRASAMPLE_ASSOCALPHA: /* data is pre-multiplied */
+            case EXTRASAMPLE_UNASSALPHA: /* data is not pre-multiplied */
+                img->alpha = sampleinfo[0];
+                break;
+        }
+    }
 
 #ifdef DEFAULT_EXTRASAMPLE_AS_ALPHA
-	if( !TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &img->photometric))
-		img->photometric = PHOTOMETRIC_MINISWHITE;
+    if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &img->photometric))
+        img->photometric = PHOTOMETRIC_MINISWHITE;
 
-	if( extrasamples == 0
-	    && img->samplesperpixel == 4
-	    && img->photometric == PHOTOMETRIC_RGB )
-	{
-		img->alpha = EXTRASAMPLE_ASSOCALPHA;
-		extrasamples = 1;
-	}
+    if (extrasamples == 0 && img->samplesperpixel == 4 &&
+        img->photometric == PHOTOMETRIC_RGB)
+    {
+        img->alpha = EXTRASAMPLE_ASSOCALPHA;
+        extrasamples = 1;
+    }
 #endif
 
-	colorchannels = img->samplesperpixel - extrasamples;
-	TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compress);
-	TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planarconfig);
-	if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &img->photometric)) {
-		switch (colorchannels) {
-			case 1:
-				if (isCCITTCompression(tif))
-					img->photometric = PHOTOMETRIC_MINISWHITE;
-				else
-					img->photometric = PHOTOMETRIC_MINISBLACK;
-				break;
-			case 3:
-				img->photometric = PHOTOMETRIC_RGB;
-				break;
-			default:
-				sprintf(emsg, "Missing needed %s tag", photoTag);
-                                goto fail_return;
-		}
-	}
-	switch (img->photometric) {
-		case PHOTOMETRIC_PALETTE:
-			if (!TIFFGetField(tif, TIFFTAG_COLORMAP,
-			    &red_orig, &green_orig, &blue_orig)) {
-				sprintf(emsg, "Missing required \"Colormap\" tag");
-                                goto fail_return;
-			}
+    colorchannels = img->samplesperpixel - extrasamples;
+    TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compress);
+    TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planarconfig);
+    if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &img->photometric))
+    {
+        switch (colorchannels)
+        {
+            case 1:
+                if (isCCITTCompression(tif))
+                    img->photometric = PHOTOMETRIC_MINISWHITE;
+                else
+                    img->photometric = PHOTOMETRIC_MINISBLACK;
+                break;
+            case 3:
+                img->photometric = PHOTOMETRIC_RGB;
+                break;
+            default:
+                snprintf(emsg, EMSG_BUF_SIZE, "Missing needed %s tag",
+                         photoTag);
+                goto fail_return;
+        }
+    }
+    switch (img->photometric)
+    {
+        case PHOTOMETRIC_PALETTE:
+            if (!TIFFGetField(tif, TIFFTAG_COLORMAP, &red_orig, &green_orig,
+                              &blue_orig))
+            {
+                snprintf(emsg, EMSG_BUF_SIZE,
+                         "Missing required \"Colormap\" tag");
+                goto fail_return;
+            }
 
-			/* copy the colormaps so we can modify them */
-			n_color = (1U << img->bitspersample);
-			img->redcmap = (uint16 *) _TIFFmalloc(sizeof(uint16)*n_color);
-			img->greencmap = (uint16 *) _TIFFmalloc(sizeof(uint16)*n_color);
-			img->bluecmap = (uint16 *) _TIFFmalloc(sizeof(uint16)*n_color);
-			if( !img->redcmap || !img->greencmap || !img->bluecmap ) {
-				sprintf(emsg, "Out of memory for colormap copy");
-                                goto fail_return;
-			}
+            /* copy the colormaps so we can modify them */
+            n_color = (1U << img->bitspersample);
+            img->redcmap =
+                (uint16_t *)_TIFFmallocExt(tif, sizeof(uint16_t) * n_color);
+            img->greencmap =
+                (uint16_t *)_TIFFmallocExt(tif, sizeof(uint16_t) * n_color);
+            img->bluecmap =
+                (uint16_t *)_TIFFmallocExt(tif, sizeof(uint16_t) * n_color);
+            if (!img->redcmap || !img->greencmap || !img->bluecmap)
+            {
+                snprintf(emsg, EMSG_BUF_SIZE,
+                         "Out of memory for colormap copy");
+                goto fail_return;
+            }
 
-			_TIFFmemcpy( img->redcmap, red_orig, n_color * 2 );
-			_TIFFmemcpy( img->greencmap, green_orig, n_color * 2 );
-			_TIFFmemcpy( img->bluecmap, blue_orig, n_color * 2 );
+            _TIFFmemcpy(img->redcmap, red_orig, n_color * 2);
+            _TIFFmemcpy(img->greencmap, green_orig, n_color * 2);
+            _TIFFmemcpy(img->bluecmap, blue_orig, n_color * 2);
 
-			/* fall through... */
-		case PHOTOMETRIC_MINISWHITE:
-		case PHOTOMETRIC_MINISBLACK:
-			if (planarconfig == PLANARCONFIG_CONTIG
-			    && img->samplesperpixel != 1
-			    && img->bitspersample < 8 ) {
-				sprintf(emsg,
-				    "Sorry, can not handle contiguous data with %s=%d, "
-				    "and %s=%d and Bits/Sample=%d",
-				    photoTag, img->photometric,
-				    "Samples/pixel", img->samplesperpixel,
-				    img->bitspersample);
-                                goto fail_return;
-			}
-			break;
-		case PHOTOMETRIC_YCBCR:
-			/* It would probably be nice to have a reality check here. */
-			if (planarconfig == PLANARCONFIG_CONTIG)
-				/* can rely on libjpeg to convert to RGB */
-				/* XXX should restore current state on exit */
-				switch (compress) {
-					case COMPRESSION_JPEG:
-						/*
-						 * TODO: when complete tests verify complete desubsampling
-						 * and YCbCr handling, remove use of TIFFTAG_JPEGCOLORMODE in
-						 * favor of tif_getimage.c native handling
-						 */
-						TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
-						img->photometric = PHOTOMETRIC_RGB;
-						break;
-					default:
-						/* do nothing */;
-						break;
-				}
-			/*
-			 * TODO: if at all meaningful and useful, make more complete
-			 * support check here, or better still, refactor to let supporting
-			 * code decide whether there is support and what meaningful
-			 * error to return
-			 */
-			break;
-		case PHOTOMETRIC_RGB:
-			if (colorchannels < 3) {
-				sprintf(emsg, "Sorry, can not handle RGB image with %s=%d",
-				    "Color channels", colorchannels);
-                                goto fail_return;
-			}
-			break;
-		case PHOTOMETRIC_SEPARATED:
-			{
-				uint16 inkset;
-				TIFFGetFieldDefaulted(tif, TIFFTAG_INKSET, &inkset);
-				if (inkset != INKSET_CMYK) {
-					sprintf(emsg, "Sorry, can not handle separated image with %s=%d",
-					    "InkSet", inkset);
-                                        goto fail_return;
-				}
-				if (img->samplesperpixel < 4) {
-					sprintf(emsg, "Sorry, can not handle separated image with %s=%d",
-					    "Samples/pixel", img->samplesperpixel);
-                                        goto fail_return;
-				}
-			}
-			break;
-		case PHOTOMETRIC_LOGL:
-			if (compress != COMPRESSION_SGILOG) {
-				sprintf(emsg, "Sorry, LogL data must have %s=%d",
-				    "Compression", COMPRESSION_SGILOG);
-                                goto fail_return;
-			}
-			TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_8BIT);
-			img->photometric = PHOTOMETRIC_MINISBLACK;	/* little white lie */
-			img->bitspersample = 8;
-			break;
-		case PHOTOMETRIC_LOGLUV:
-			if (compress != COMPRESSION_SGILOG && compress != COMPRESSION_SGILOG24) {
-				sprintf(emsg, "Sorry, LogLuv data must have %s=%d or %d",
-				    "Compression", COMPRESSION_SGILOG, COMPRESSION_SGILOG24);
-                                goto fail_return;
-			}
-			if (planarconfig != PLANARCONFIG_CONTIG) {
-				sprintf(emsg, "Sorry, can not handle LogLuv images with %s=%d",
-				    "Planarconfiguration", planarconfig);
-				return (0);
-			}
-			TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_8BIT);
-			img->photometric = PHOTOMETRIC_RGB;		/* little white lie */
-			img->bitspersample = 8;
-			break;
-		case PHOTOMETRIC_CIELAB:
-			break;
-		default:
-			sprintf(emsg, "Sorry, can not handle image with %s=%d",
-			    photoTag, img->photometric);
-                        goto fail_return;
-	}
-	TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &img->width);
-	TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &img->height);
-	TIFFGetFieldDefaulted(tif, TIFFTAG_ORIENTATION, &img->orientation);
-	img->isContig =
-	    !(planarconfig == PLANARCONFIG_SEPARATE && img->samplesperpixel > 1);
-	if (img->isContig) {
-		if (!PickContigCase(img)) {
-			sprintf(emsg, "Sorry, can not handle image");
-			goto fail_return;
-		}
-	} else {
-		if (!PickSeparateCase(img)) {
-			sprintf(emsg, "Sorry, can not handle image");
-			goto fail_return;
-		}
-	}
-	return 1;
+            /* fall through... */
+        case PHOTOMETRIC_MINISWHITE:
+        case PHOTOMETRIC_MINISBLACK:
+            if (planarconfig == PLANARCONFIG_CONTIG &&
+                img->samplesperpixel != 1 && img->bitspersample < 8)
+            {
+                snprintf(
+                    emsg, EMSG_BUF_SIZE,
+                    "Sorry, can not handle contiguous data with %s=%" PRIu16
+                    ", "
+                    "and %s=%" PRIu16 " and Bits/Sample=%" PRIu16,
+                    photoTag, img->photometric, "Samples/pixel",
+                    img->samplesperpixel, img->bitspersample);
+                goto fail_return;
+            }
+            break;
+        case PHOTOMETRIC_YCBCR:
+            /* It would probably be nice to have a reality check here. */
+            if (planarconfig == PLANARCONFIG_CONTIG)
+                /* can rely on libjpeg to convert to RGB */
+                /* XXX should restore current state on exit */
+                switch (compress)
+                {
+                    case COMPRESSION_JPEG:
+                        /*
+                         * TODO: when complete tests verify complete
+                         * desubsampling and YCbCr handling, remove use of
+                         * TIFFTAG_JPEGCOLORMODE in favor of tif_getimage.c
+                         * native handling
+                         */
+                        TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE,
+                                     JPEGCOLORMODE_RGB);
+                        img->photometric = PHOTOMETRIC_RGB;
+                        break;
+                    default:
+                        /* do nothing */;
+                        break;
+                }
+            /*
+             * TODO: if at all meaningful and useful, make more complete
+             * support check here, or better still, refactor to let supporting
+             * code decide whether there is support and what meaningful
+             * error to return
+             */
+            break;
+        case PHOTOMETRIC_RGB:
+            if (colorchannels < 3)
+            {
+                snprintf(emsg, EMSG_BUF_SIZE,
+                         "Sorry, can not handle RGB image with %s=%d",
+                         "Color channels", colorchannels);
+                goto fail_return;
+            }
+            break;
+        case PHOTOMETRIC_SEPARATED:
+        {
+            uint16_t inkset;
+            TIFFGetFieldDefaulted(tif, TIFFTAG_INKSET, &inkset);
+            if (inkset != INKSET_CMYK)
+            {
+                snprintf(
+                    emsg, EMSG_BUF_SIZE,
+                    "Sorry, can not handle separated image with %s=%" PRIu16,
+                    "InkSet", inkset);
+                goto fail_return;
+            }
+            if (img->samplesperpixel < 4)
+            {
+                snprintf(
+                    emsg, EMSG_BUF_SIZE,
+                    "Sorry, can not handle separated image with %s=%" PRIu16,
+                    "Samples/pixel", img->samplesperpixel);
+                goto fail_return;
+            }
+        }
+        break;
+        case PHOTOMETRIC_LOGL:
+            if (compress != COMPRESSION_SGILOG)
+            {
+                snprintf(emsg, EMSG_BUF_SIZE,
+                         "Sorry, LogL data must have %s=%d", "Compression",
+                         COMPRESSION_SGILOG);
+                goto fail_return;
+            }
+            TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_8BIT);
+            img->photometric = PHOTOMETRIC_MINISBLACK; /* little white lie */
+            img->bitspersample = 8;
+            break;
+        case PHOTOMETRIC_LOGLUV:
+            if (compress != COMPRESSION_SGILOG &&
+                compress != COMPRESSION_SGILOG24)
+            {
+                snprintf(emsg, EMSG_BUF_SIZE,
+                         "Sorry, LogLuv data must have %s=%d or %d",
+                         "Compression", COMPRESSION_SGILOG,
+                         COMPRESSION_SGILOG24);
+                goto fail_return;
+            }
+            if (planarconfig != PLANARCONFIG_CONTIG)
+            {
+                snprintf(emsg, EMSG_BUF_SIZE,
+                         "Sorry, can not handle LogLuv images with %s=%" PRIu16,
+                         "Planarconfiguration", planarconfig);
+                return (0);
+            }
+            TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_8BIT);
+            img->photometric = PHOTOMETRIC_RGB; /* little white lie */
+            img->bitspersample = 8;
+            break;
+        case PHOTOMETRIC_CIELAB:
+            break;
+        default:
+            snprintf(emsg, EMSG_BUF_SIZE,
+                     "Sorry, can not handle image with %s=%" PRIu16, photoTag,
+                     img->photometric);
+            goto fail_return;
+    }
+    TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &img->width);
+    TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &img->height);
+    TIFFGetFieldDefaulted(tif, TIFFTAG_ORIENTATION, &img->orientation);
+    img->isContig =
+        !(planarconfig == PLANARCONFIG_SEPARATE && img->samplesperpixel > 1);
+    if (img->isContig)
+    {
+        if (!PickContigCase(img))
+        {
+            snprintf(emsg, EMSG_BUF_SIZE, "Sorry, can not handle image");
+            goto fail_return;
+        }
+    }
+    else
+    {
+        if (!PickSeparateCase(img))
+        {
+            snprintf(emsg, EMSG_BUF_SIZE, "Sorry, can not handle image");
+            goto fail_return;
+        }
+    }
+    return 1;
 
-  fail_return:
-        TIFFRGBAImageEnd( img );
-        return 0;
+fail_return:
+    TIFFRGBAImageEnd(img);
+    return 0;
 }
 
-int
-TIFFRGBAImageGet(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
+int TIFFRGBAImageGet(TIFFRGBAImage *img, uint32_t *raster, uint32_t w,
+                     uint32_t h)
 {
-    if (img->get == NULL) {
-		TIFFErrorExt(img->tif->tif_clientdata, TIFFFileName(img->tif), "No \"get\" routine setup");
-		return (0);
-	}
-	if (img->put.any == NULL) {
-		TIFFErrorExt(img->tif->tif_clientdata, TIFFFileName(img->tif),
-		"No \"put\" routine setupl; probably can not handle image format");
-		return (0);
+    if (img->get == NULL)
+    {
+        TIFFErrorExtR(img->tif, TIFFFileName(img->tif),
+                      "No \"get\" routine setup");
+        return (0);
+    }
+    if (img->put.any == NULL)
+    {
+        TIFFErrorExtR(
+            img->tif, TIFFFileName(img->tif),
+            "No \"put\" routine setupl; probably can not handle image format");
+        return (0);
     }
     return (*img->get)(img, raster, w, h);
 }
@@ -516,24 +607,25 @@
  * Read the specified image into an ABGR-format rastertaking in account
  * specified orientation.
  */
-int
-TIFFReadRGBAImageOriented(TIFF* tif,
-			  uint32 rwidth, uint32 rheight, uint32* raster,
-			  int orientation, int stop)
+int TIFFReadRGBAImageOriented(TIFF *tif, uint32_t rwidth, uint32_t rheight,
+                              uint32_t *raster, int orientation, int stop)
 {
-    char emsg[1024] = "";
+    char emsg[EMSG_BUF_SIZE] = "";
     TIFFRGBAImage img;
     int ok;
 
-	if (TIFFRGBAImageOK(tif, emsg) && TIFFRGBAImageBegin(&img, tif, stop, emsg)) {
-		img.req_orientation = (uint16)orientation;
-		/* XXX verify rwidth and rheight against width and height */
-		ok = TIFFRGBAImageGet(&img, raster+(rheight-img.height)*rwidth,
-			rwidth, img.height);
-		TIFFRGBAImageEnd(&img);
-	} else {
-		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", emsg);
-		ok = 0;
+    if (TIFFRGBAImageOK(tif, emsg) && TIFFRGBAImageBegin(&img, tif, stop, emsg))
+    {
+        img.req_orientation = (uint16_t)orientation;
+        /* XXX verify rwidth and rheight against width and height */
+        ok = TIFFRGBAImageGet(&img, raster + (rheight - img.height) * rwidth,
+                              rwidth, img.height);
+        TIFFRGBAImageEnd(&img);
+    }
+    else
+    {
+        TIFFErrorExtR(tif, TIFFFileName(tif), "%s", emsg);
+        ok = 0;
     }
     return (ok);
 }
@@ -542,73 +634,72 @@
  * Read the specified image into an ABGR-format raster. Use bottom left
  * origin for raster by default.
  */
-int
-TIFFReadRGBAImage(TIFF* tif,
-		  uint32 rwidth, uint32 rheight, uint32* raster, int stop)
+int TIFFReadRGBAImage(TIFF *tif, uint32_t rwidth, uint32_t rheight,
+                      uint32_t *raster, int stop)
 {
-	return TIFFReadRGBAImageOriented(tif, rwidth, rheight, raster,
-					 ORIENTATION_BOTLEFT, stop);
+    return TIFFReadRGBAImageOriented(tif, rwidth, rheight, raster,
+                                     ORIENTATION_BOTLEFT, stop);
 }
 
-static int 
-setorientation(TIFFRGBAImage* img)
+static int setorientation(TIFFRGBAImage *img)
 {
-	switch (img->orientation) {
-		case ORIENTATION_TOPLEFT:
-		case ORIENTATION_LEFTTOP:
-			if (img->req_orientation == ORIENTATION_TOPRIGHT ||
-			    img->req_orientation == ORIENTATION_RIGHTTOP)
-				return FLIP_HORIZONTALLY;
-			else if (img->req_orientation == ORIENTATION_BOTRIGHT ||
-			    img->req_orientation == ORIENTATION_RIGHTBOT)
-				return FLIP_HORIZONTALLY | FLIP_VERTICALLY;
-			else if (img->req_orientation == ORIENTATION_BOTLEFT ||
-			    img->req_orientation == ORIENTATION_LEFTBOT)
-				return FLIP_VERTICALLY;
-			else
-				return 0;
-		case ORIENTATION_TOPRIGHT:
-		case ORIENTATION_RIGHTTOP:
-			if (img->req_orientation == ORIENTATION_TOPLEFT ||
-			    img->req_orientation == ORIENTATION_LEFTTOP)
-				return FLIP_HORIZONTALLY;
-			else if (img->req_orientation == ORIENTATION_BOTRIGHT ||
-			    img->req_orientation == ORIENTATION_RIGHTBOT)
-				return FLIP_VERTICALLY;
-			else if (img->req_orientation == ORIENTATION_BOTLEFT ||
-			    img->req_orientation == ORIENTATION_LEFTBOT)
-				return FLIP_HORIZONTALLY | FLIP_VERTICALLY;
-			else
-				return 0;
-		case ORIENTATION_BOTRIGHT:
-		case ORIENTATION_RIGHTBOT:
-			if (img->req_orientation == ORIENTATION_TOPLEFT ||
-			    img->req_orientation == ORIENTATION_LEFTTOP)
-				return FLIP_HORIZONTALLY | FLIP_VERTICALLY;
-			else if (img->req_orientation == ORIENTATION_TOPRIGHT ||
-			    img->req_orientation == ORIENTATION_RIGHTTOP)
-				return FLIP_VERTICALLY;
-			else if (img->req_orientation == ORIENTATION_BOTLEFT ||
-			    img->req_orientation == ORIENTATION_LEFTBOT)
-				return FLIP_HORIZONTALLY;
-			else
-				return 0;
-		case ORIENTATION_BOTLEFT:
-		case ORIENTATION_LEFTBOT:
-			if (img->req_orientation == ORIENTATION_TOPLEFT ||
-			    img->req_orientation == ORIENTATION_LEFTTOP)
-				return FLIP_VERTICALLY;
-			else if (img->req_orientation == ORIENTATION_TOPRIGHT ||
-			    img->req_orientation == ORIENTATION_RIGHTTOP)
-				return FLIP_HORIZONTALLY | FLIP_VERTICALLY;
-			else if (img->req_orientation == ORIENTATION_BOTRIGHT ||
-			    img->req_orientation == ORIENTATION_RIGHTBOT)
-				return FLIP_HORIZONTALLY;
-			else
-				return 0;
-		default:	/* NOTREACHED */
-			return 0;
-	}
+    switch (img->orientation)
+    {
+        case ORIENTATION_TOPLEFT:
+        case ORIENTATION_LEFTTOP:
+            if (img->req_orientation == ORIENTATION_TOPRIGHT ||
+                img->req_orientation == ORIENTATION_RIGHTTOP)
+                return FLIP_HORIZONTALLY;
+            else if (img->req_orientation == ORIENTATION_BOTRIGHT ||
+                     img->req_orientation == ORIENTATION_RIGHTBOT)
+                return FLIP_HORIZONTALLY | FLIP_VERTICALLY;
+            else if (img->req_orientation == ORIENTATION_BOTLEFT ||
+                     img->req_orientation == ORIENTATION_LEFTBOT)
+                return FLIP_VERTICALLY;
+            else
+                return 0;
+        case ORIENTATION_TOPRIGHT:
+        case ORIENTATION_RIGHTTOP:
+            if (img->req_orientation == ORIENTATION_TOPLEFT ||
+                img->req_orientation == ORIENTATION_LEFTTOP)
+                return FLIP_HORIZONTALLY;
+            else if (img->req_orientation == ORIENTATION_BOTRIGHT ||
+                     img->req_orientation == ORIENTATION_RIGHTBOT)
+                return FLIP_VERTICALLY;
+            else if (img->req_orientation == ORIENTATION_BOTLEFT ||
+                     img->req_orientation == ORIENTATION_LEFTBOT)
+                return FLIP_HORIZONTALLY | FLIP_VERTICALLY;
+            else
+                return 0;
+        case ORIENTATION_BOTRIGHT:
+        case ORIENTATION_RIGHTBOT:
+            if (img->req_orientation == ORIENTATION_TOPLEFT ||
+                img->req_orientation == ORIENTATION_LEFTTOP)
+                return FLIP_HORIZONTALLY | FLIP_VERTICALLY;
+            else if (img->req_orientation == ORIENTATION_TOPRIGHT ||
+                     img->req_orientation == ORIENTATION_RIGHTTOP)
+                return FLIP_VERTICALLY;
+            else if (img->req_orientation == ORIENTATION_BOTLEFT ||
+                     img->req_orientation == ORIENTATION_LEFTBOT)
+                return FLIP_HORIZONTALLY;
+            else
+                return 0;
+        case ORIENTATION_BOTLEFT:
+        case ORIENTATION_LEFTBOT:
+            if (img->req_orientation == ORIENTATION_TOPLEFT ||
+                img->req_orientation == ORIENTATION_LEFTTOP)
+                return FLIP_VERTICALLY;
+            else if (img->req_orientation == ORIENTATION_TOPRIGHT ||
+                     img->req_orientation == ORIENTATION_RIGHTTOP)
+                return FLIP_HORIZONTALLY | FLIP_VERTICALLY;
+            else if (img->req_orientation == ORIENTATION_BOTRIGHT ||
+                     img->req_orientation == ORIENTATION_RIGHTBOT)
+                return FLIP_HORIZONTALLY;
+            else
+                return 0;
+        default: /* NOTREACHED */
+            return 0;
+    }
 }
 
 /*
@@ -616,29 +707,30 @@
  *	PlanarConfiguration contiguous if SamplesPerPixel > 1
  * or
  *	SamplesPerPixel == 1
- */	
-static int
-gtTileContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
+ */
+static int gtTileContig(TIFFRGBAImage *img, uint32_t *raster, uint32_t w,
+                        uint32_t h)
 {
-    TIFF* tif = img->tif;
+    TIFF *tif = img->tif;
     tileContigRoutine put = img->put.contig;
-    uint32 col, row, y, rowstoread;
+    uint32_t col, row, y, rowstoread;
     tmsize_t pos;
-    uint32 tw, th;
-    unsigned char* buf = NULL;
-    int32 fromskew, toskew;
-    int64 safeskew;
-    uint32 nrow;
+    uint32_t tw, th;
+    unsigned char *buf = NULL;
+    int32_t fromskew, toskew;
+    uint32_t nrow;
     int ret = 1, flip;
-    uint32 this_tw, tocol;
-    int32 this_toskew, leftmost_toskew;
-    int32 leftmost_fromskew;
-    uint32 leftmost_tw;
+    uint32_t this_tw, tocol;
+    int32_t this_toskew, leftmost_toskew;
+    int32_t leftmost_fromskew;
+    int64_t safeskew;
+    uint32_t leftmost_tw;
     tmsize_t bufsize;
 
     bufsize = TIFFTileSize(tif);
-    if (bufsize == 0) {
-        TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", "No space for tile buffer");
+    if (bufsize == 0)
+    {
+        TIFFErrorExtR(tif, TIFFFileName(tif), "%s", "No space for tile buffer");
         return (0);
     }
 
@@ -646,113 +738,120 @@
     TIFFGetField(tif, TIFFTAG_TILELENGTH, &th);
 
     flip = setorientation(img);
-    if (flip & FLIP_VERTICALLY) {
-	    y = h - 1;
-	    safeskew = 0;
-	    safeskew -= tw;
-	    safeskew -= w;
+    if (flip & FLIP_VERTICALLY)
+    {
+        if ((tw + w) > INT_MAX)
+        {
+            TIFFErrorExtR(tif, TIFFFileName(tif), "%s",
+                          "unsupported tile size (too wide)");
+            return (0);
+        }
+        y = h - 1;
+        toskew = -(int32_t)(tw + w);
     }
-    else {
-	    y = 0;
-	    safeskew = 0;
-	    safeskew -= tw;
-	    safeskew +=w;
+    else
+    {
+        if (tw > (INT_MAX + w))
+        {
+            TIFFErrorExtR(tif, TIFFFileName(tif), "%s",
+                          "unsupported tile size (too wide)");
+            return (0);
+        }
+        y = 0;
+        toskew = -(int32_t)(tw - w);
     }
-     
-    if(safeskew > INT_MAX || safeskew < INT_MIN){
-       _TIFFfree(buf);
-       TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", "Invalid skew");
-       return (0);
-    }
-    toskew = safeskew;
 
     /*
      *	Leftmost tile is clipped on left side if col_offset > 0.
      */
     leftmost_fromskew = img->col_offset % tw;
     leftmost_tw = tw - leftmost_fromskew;
-    safeskew = toskew;
-    safeskew += leftmost_fromskew;
-    if(safeskew > INT_MAX || safeskew < INT_MIN){
-       _TIFFfree(buf);
-       TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", "Invalid skew");
-       return (0);
-    }
-    leftmost_toskew = safeskew;
+    leftmost_toskew = toskew + leftmost_fromskew;
     for (row = 0; ret != 0 && row < h; row += nrow)
     {
         rowstoread = th - (row + img->row_offset) % th;
-    	nrow = (row + rowstoread > h ? h - row : rowstoread);
-	fromskew = leftmost_fromskew;
-	this_tw = leftmost_tw;
-	this_toskew = leftmost_toskew;
-	tocol = 0;
-	col = img->col_offset;
-	while (tocol < w)
+        nrow = (row + rowstoread > h ? h - row : rowstoread);
+        fromskew = leftmost_fromskew;
+        this_tw = leftmost_tw;
+        this_toskew = leftmost_toskew;
+        tocol = 0;
+        col = img->col_offset;
+        while (tocol < w)
         {
-	    if (_TIFFReadTileAndAllocBuffer(tif, (void**) &buf, bufsize, col,
-			     row+img->row_offset, 0, 0)==(tmsize_t)(-1) &&
+            if (_TIFFReadTileAndAllocBuffer(tif, (void **)&buf, bufsize, col,
+                                            row + img->row_offset, 0,
+                                            0) == (tmsize_t)(-1) &&
                 (buf == NULL || img->stoponerr))
             {
                 ret = 0;
                 break;
             }
-            pos = ((row+img->row_offset) % th) * TIFFTileRowSize(tif) + \
-		   ((tmsize_t) fromskew * img->samplesperpixel);
-	    if (tocol + this_tw > w) 
-	    {
-		/*
-		 * Rightmost tile is clipped on right side.
-		 */
-		safeskew = tw;
-		safeskew -= w;
-		safeskew += tocol;
-		if(safeskew > INT_MAX || safeskew < INT_MIN){
-		        _TIFFfree(buf);
-		        TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", "Invalid skew");
-		        return (0);
-		}
-		fromskew = safeskew;
-		this_tw = tw - fromskew;
-		safeskew = toskew;
-		safeskew += fromskew;
-		if(safeskew > INT_MAX || safeskew < INT_MIN){
-		        _TIFFfree(buf);
-		        TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", "Invalid skew");
-		        return (0);
-		}
-		this_toskew = safeskew;
-	    }
-	    (*put)(img, raster+y*w+tocol, tocol, y, this_tw, nrow, fromskew, this_toskew, buf + pos);
-	    tocol += this_tw;
-	    col += this_tw;
-	    /*
-	     * After the leftmost tile, tiles are no longer clipped on left side.
-	     */
-	    fromskew = 0;
-	    this_tw = tw;
-	    this_toskew = toskew;
-	}
+            pos = ((row + img->row_offset) % th) * TIFFTileRowSize(tif) +
+                  ((tmsize_t)fromskew * img->samplesperpixel);
+            if (tocol + this_tw > w)
+            {
+                /*
+                 * Rightmost tile is clipped on right side.
+                 */
+                safeskew = tw;
+                safeskew -= w;
+                safeskew += tocol;
+                if (safeskew > INT_MAX || safeskew < INT_MIN)
+                {
+                    _TIFFfree(buf);
+                    TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s",
+                                 "Invalid skew");
+                    return (0);
+                }
+                fromskew = safeskew;
+                this_tw = tw - fromskew;
+                safeskew = toskew;
+                safeskew += fromskew;
+                if (safeskew > INT_MAX || safeskew < INT_MIN)
+                {
+                    _TIFFfree(buf);
+                    TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s",
+                                 "Invalid skew");
+                    return (0);
+                }
+                this_toskew = safeskew;
+            }
+            tmsize_t roffset = (tmsize_t)y * w + tocol;
+            (*put)(img, raster + roffset, tocol, y, this_tw, nrow, fromskew,
+                   this_toskew, buf + pos);
+            tocol += this_tw;
+            col += this_tw;
+            /*
+             * After the leftmost tile, tiles are no longer clipped on left
+             * side.
+             */
+            fromskew = 0;
+            this_tw = tw;
+            this_toskew = toskew;
+        }
 
-        y += ((flip & FLIP_VERTICALLY) ? -(int32) nrow : (int32) nrow);
+        y += ((flip & FLIP_VERTICALLY) ? -(int32_t)nrow : (int32_t)nrow);
     }
-    _TIFFfree(buf);
+    _TIFFfreeExt(img->tif, buf);
 
-    if (flip & FLIP_HORIZONTALLY) {
-	    uint32 line;
+    if (flip & FLIP_HORIZONTALLY)
+    {
+        uint32_t line;
 
-	    for (line = 0; line < h; line++) {
-		    uint32 *left = raster + (line * w);
-		    uint32 *right = left + w - 1;
-		    
-		    while ( left < right ) {
-			    uint32 temp = *left;
-			    *left = *right;
-			    *right = temp;
-			    left++;
-				right--;
-		    }
-	    }
+        for (line = 0; line < h; line++)
+        {
+            uint32_t *left = raster + (line * w);
+            uint32_t *right = left + w - 1;
+
+            while (left < right)
+            {
+                uint32_t temp = *left;
+                *left = *right;
+                *right = temp;
+                left++;
+                right--;
+            }
+        }
     }
 
     return (ret);
@@ -763,180 +862,203 @@
  *	 SamplesPerPixel > 1
  *	 PlanarConfiguration separated
  * We assume that all such images are RGB.
- */	
-static int
-gtTileSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
+ */
+static int gtTileSeparate(TIFFRGBAImage *img, uint32_t *raster, uint32_t w,
+                          uint32_t h)
 {
-	TIFF* tif = img->tif;
-	tileSeparateRoutine put = img->put.separate;
-	uint32 col, row, y, rowstoread;
-	tmsize_t pos;
-	uint32 tw, th;
-	unsigned char* buf = NULL;
-	unsigned char* p0 = NULL;
-	unsigned char* p1 = NULL;
-	unsigned char* p2 = NULL;
-	unsigned char* pa = NULL;
-	tmsize_t tilesize;
-	tmsize_t bufsize;
-	int32 fromskew, toskew;
-	int alpha = img->alpha;
-	uint32 nrow;
-	int ret = 1, flip;
-        uint16 colorchannels;
-	uint32 this_tw, tocol;
-	int32 this_toskew, leftmost_toskew;
-	int32 leftmost_fromskew;
-	uint32 leftmost_tw;
+    TIFF *tif = img->tif;
+    tileSeparateRoutine put = img->put.separate;
+    uint32_t col, row, y, rowstoread;
+    tmsize_t pos;
+    uint32_t tw, th;
+    unsigned char *buf = NULL;
+    unsigned char *p0 = NULL;
+    unsigned char *p1 = NULL;
+    unsigned char *p2 = NULL;
+    unsigned char *pa = NULL;
+    tmsize_t tilesize;
+    tmsize_t bufsize;
+    int32_t fromskew, toskew;
+    int alpha = img->alpha;
+    uint32_t nrow;
+    int ret = 1, flip;
+    uint16_t colorchannels;
+    uint32_t this_tw, tocol;
+    int32_t this_toskew, leftmost_toskew;
+    int32_t leftmost_fromskew;
+    uint32_t leftmost_tw;
 
-	tilesize = TIFFTileSize(tif);  
-	bufsize = _TIFFMultiplySSize(tif, alpha?4:3,tilesize, "gtTileSeparate");
-	if (bufsize == 0) {
-		return (0);
-	}
+    tilesize = TIFFTileSize(tif);
+    bufsize =
+        _TIFFMultiplySSize(tif, alpha ? 4 : 3, tilesize, "gtTileSeparate");
+    if (bufsize == 0)
+    {
+        return (0);
+    }
 
-	TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw);
-	TIFFGetField(tif, TIFFTAG_TILELENGTH, &th);
+    TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw);
+    TIFFGetField(tif, TIFFTAG_TILELENGTH, &th);
 
-	flip = setorientation(img);
-	if (flip & FLIP_VERTICALLY) {
-		y = h - 1;
-		toskew = -(int32)(tw + w);
-	}
-	else {
-		y = 0;
-		toskew = -(int32)(tw - w);
-	}
-
-        switch( img->photometric )
+    flip = setorientation(img);
+    if (flip & FLIP_VERTICALLY)
+    {
+        if ((tw + w) > INT_MAX)
         {
-          case PHOTOMETRIC_MINISWHITE:
-          case PHOTOMETRIC_MINISBLACK:
-          case PHOTOMETRIC_PALETTE:
+            TIFFErrorExtR(tif, TIFFFileName(tif), "%s",
+                          "unsupported tile size (too wide)");
+            return (0);
+        }
+        y = h - 1;
+        toskew = -(int32_t)(tw + w);
+    }
+    else
+    {
+        if (tw > (INT_MAX + w))
+        {
+            TIFFErrorExtR(tif, TIFFFileName(tif), "%s",
+                          "unsupported tile size (too wide)");
+            return (0);
+        }
+        y = 0;
+        toskew = -(int32_t)(tw - w);
+    }
+
+    switch (img->photometric)
+    {
+        case PHOTOMETRIC_MINISWHITE:
+        case PHOTOMETRIC_MINISBLACK:
+        case PHOTOMETRIC_PALETTE:
             colorchannels = 1;
             break;
 
-          default:
+        default:
             colorchannels = 3;
             break;
+    }
+
+    /*
+     *	Leftmost tile is clipped on left side if col_offset > 0.
+     */
+    leftmost_fromskew = img->col_offset % tw;
+    leftmost_tw = tw - leftmost_fromskew;
+    leftmost_toskew = toskew + leftmost_fromskew;
+    for (row = 0; ret != 0 && row < h; row += nrow)
+    {
+        rowstoread = th - (row + img->row_offset) % th;
+        nrow = (row + rowstoread > h ? h - row : rowstoread);
+        fromskew = leftmost_fromskew;
+        this_tw = leftmost_tw;
+        this_toskew = leftmost_toskew;
+        tocol = 0;
+        col = img->col_offset;
+        while (tocol < w)
+        {
+            if (buf == NULL)
+            {
+                if (_TIFFReadTileAndAllocBuffer(tif, (void **)&buf, bufsize,
+                                                col, row + img->row_offset, 0,
+                                                0) == (tmsize_t)(-1) &&
+                    (buf == NULL || img->stoponerr))
+                {
+                    ret = 0;
+                    break;
+                }
+                p0 = buf;
+                if (colorchannels == 1)
+                {
+                    p2 = p1 = p0;
+                    pa = (alpha ? (p0 + 3 * tilesize) : NULL);
+                }
+                else
+                {
+                    p1 = p0 + tilesize;
+                    p2 = p1 + tilesize;
+                    pa = (alpha ? (p2 + tilesize) : NULL);
+                }
+            }
+            else if (TIFFReadTile(tif, p0, col, row + img->row_offset, 0, 0) ==
+                         (tmsize_t)(-1) &&
+                     img->stoponerr)
+            {
+                ret = 0;
+                break;
+            }
+            if (colorchannels > 1 &&
+                TIFFReadTile(tif, p1, col, row + img->row_offset, 0, 1) ==
+                    (tmsize_t)(-1) &&
+                img->stoponerr)
+            {
+                ret = 0;
+                break;
+            }
+            if (colorchannels > 1 &&
+                TIFFReadTile(tif, p2, col, row + img->row_offset, 0, 2) ==
+                    (tmsize_t)(-1) &&
+                img->stoponerr)
+            {
+                ret = 0;
+                break;
+            }
+            if (alpha &&
+                TIFFReadTile(tif, pa, col, row + img->row_offset, 0,
+                             colorchannels) == (tmsize_t)(-1) &&
+                img->stoponerr)
+            {
+                ret = 0;
+                break;
+            }
+
+            pos = ((row + img->row_offset) % th) * TIFFTileRowSize(tif) +
+                  ((tmsize_t)fromskew * img->samplesperpixel);
+            if (tocol + this_tw > w)
+            {
+                /*
+                 * Rightmost tile is clipped on right side.
+                 */
+                fromskew = tw - (w - tocol);
+                this_tw = tw - fromskew;
+                this_toskew = toskew + fromskew;
+            }
+            tmsize_t roffset = (tmsize_t)y * w + tocol;
+            (*put)(img, raster + roffset, tocol, y, this_tw, nrow, fromskew,
+                   this_toskew, p0 + pos, p1 + pos, p2 + pos,
+                   (alpha ? (pa + pos) : NULL));
+            tocol += this_tw;
+            col += this_tw;
+            /*
+             * After the leftmost tile, tiles are no longer clipped on left
+             * side.
+             */
+            fromskew = 0;
+            this_tw = tw;
+            this_toskew = toskew;
         }
 
-	/*
-	 *	Leftmost tile is clipped on left side if col_offset > 0.
-	 */
-	leftmost_fromskew = img->col_offset % tw;
-	leftmost_tw = tw - leftmost_fromskew;
-	leftmost_toskew = toskew + leftmost_fromskew;
-	for (row = 0; ret != 0 && row < h; row += nrow)
-	{
-		rowstoread = th - (row + img->row_offset) % th;
-		nrow = (row + rowstoread > h ? h - row : rowstoread);
-		fromskew = leftmost_fromskew;
-		this_tw = leftmost_tw;
-		this_toskew = leftmost_toskew;
-		tocol = 0;
-		col = img->col_offset;
-		while (tocol < w)
-		{
-                        if( buf == NULL )
-                        {
-                            if (_TIFFReadTileAndAllocBuffer(
-                                    tif, (void**) &buf, bufsize, col,
-                                    row+img->row_offset,0,0)==(tmsize_t)(-1)
-                                && (buf == NULL || img->stoponerr))
-                            {
-                                    ret = 0;
-                                    break;
-                            }
-                            p0 = buf;
-                            if( colorchannels == 1 )
-                            {
-                                p2 = p1 = p0;
-                                pa = (alpha?(p0+3*tilesize):NULL);
-                            }
-                            else
-                            {
-                                p1 = p0 + tilesize;
-                                p2 = p1 + tilesize;
-                                pa = (alpha?(p2+tilesize):NULL);
-                            }
-                        }
-			else if (TIFFReadTile(tif, p0, col,  
-			    row+img->row_offset,0,0)==(tmsize_t)(-1) && img->stoponerr)
-			{
-				ret = 0;
-				break;
-			}
-			if (colorchannels > 1 
-                            && TIFFReadTile(tif, p1, col,  
-                                            row+img->row_offset,0,1) == (tmsize_t)(-1) 
-                            && img->stoponerr)
-			{
-				ret = 0;
-				break;
-			}
-			if (colorchannels > 1 
-                            && TIFFReadTile(tif, p2, col,  
-                                            row+img->row_offset,0,2) == (tmsize_t)(-1) 
-                            && img->stoponerr)
-			{
-				ret = 0;
-				break;
-			}
-			if (alpha
-                            && TIFFReadTile(tif,pa,col,  
-                                            row+img->row_offset,0,colorchannels) == (tmsize_t)(-1) 
-                            && img->stoponerr)
-                        {
-                            ret = 0;
-                            break;
-			}
+        y += ((flip & FLIP_VERTICALLY) ? -(int32_t)nrow : (int32_t)nrow);
+    }
 
-			pos = ((row+img->row_offset) % th) * TIFFTileRowSize(tif) + \
-			   ((tmsize_t) fromskew * img->samplesperpixel);
-			if (tocol + this_tw > w) 
-			{
-				/*
-				 * Rightmost tile is clipped on right side.
-				 */
-				fromskew = tw - (w - tocol);
-				this_tw = tw - fromskew;
-				this_toskew = toskew + fromskew;
-			}
-			(*put)(img, raster+y*w+tocol, tocol, y, this_tw, nrow, fromskew, this_toskew, \
-				p0 + pos, p1 + pos, p2 + pos, (alpha?(pa+pos):NULL));
-			tocol += this_tw;
-			col += this_tw;
-			/*
-			* After the leftmost tile, tiles are no longer clipped on left side.
-			*/
-			fromskew = 0;
-			this_tw = tw;
-			this_toskew = toskew;
-		}
+    if (flip & FLIP_HORIZONTALLY)
+    {
+        uint32_t line;
 
-		y += ((flip & FLIP_VERTICALLY) ?-(int32) nrow : (int32) nrow);
-	}
+        for (line = 0; line < h; line++)
+        {
+            uint32_t *left = raster + (line * w);
+            uint32_t *right = left + w - 1;
 
-	if (flip & FLIP_HORIZONTALLY) {
-		uint32 line;
+            while (left < right)
+            {
+                uint32_t temp = *left;
+                *left = *right;
+                *right = temp;
+                left++;
+                right--;
+            }
+        }
+    }
 
-		for (line = 0; line < h; line++) {
-			uint32 *left = raster + (line * w);
-			uint32 *right = left + w - 1;
-
-			while ( left < right ) {
-				uint32 temp = *left;
-				*left = *right;
-				*right = temp;
-				left++;
-				right--;
-			}
-		}
-	}
-
-	_TIFFfree(buf);
-	return (ret);
+    _TIFFfreeExt(img->tif, buf);
+    return (ret);
 }
 
 /*
@@ -944,97 +1066,116 @@
  *	PlanarConfiguration contiguous if SamplesPerPixel > 1
  * or
  *	SamplesPerPixel == 1
- */	
-static int
-gtStripContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
+ */
+static int gtStripContig(TIFFRGBAImage *img, uint32_t *raster, uint32_t w,
+                         uint32_t h)
 {
-	TIFF* tif = img->tif;
-	tileContigRoutine put = img->put.contig;
-	uint32 row, y, nrow, nrowsub, rowstoread;
-	tmsize_t pos;
-	unsigned char* buf = NULL;
-	uint32 rowsperstrip;
-	uint16 subsamplinghor,subsamplingver;
-	uint32 imagewidth = img->width;
-	tmsize_t scanline;
-	int32 fromskew, toskew;
-	int ret = 1, flip;
-        tmsize_t maxstripsize;
+    TIFF *tif = img->tif;
+    tileContigRoutine put = img->put.contig;
+    uint32_t row, y, nrow, nrowsub, rowstoread;
+    tmsize_t pos;
+    unsigned char *buf = NULL;
+    uint32_t rowsperstrip;
+    uint16_t subsamplinghor, subsamplingver;
+    uint32_t imagewidth = img->width;
+    tmsize_t scanline;
+    int32_t fromskew, toskew;
+    int ret = 1, flip;
+    tmsize_t maxstripsize;
 
-	if ((tmsize_t)img->row_offset > TIFF_SSIZE_T_MAX || (size_t)h > (size_t)TIFF_SSIZE_T_MAX)
-		return (0);
+    if ((tmsize_t)img->row_offset > TIFF_SSIZE_T_MAX ||
+        (size_t)h > (size_t)TIFF_SSIZE_T_MAX)
+    {
+        return (0);
+    }
 
-	TIFFGetFieldDefaulted(tif, TIFFTAG_YCBCRSUBSAMPLING, &subsamplinghor, &subsamplingver);
-	if( subsamplingver == 0 ) {
-		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "Invalid vertical YCbCr subsampling");
-		return (0);
-	}
-	
-	maxstripsize = TIFFStripSize(tif);
+    TIFFGetFieldDefaulted(tif, TIFFTAG_YCBCRSUBSAMPLING, &subsamplinghor,
+                          &subsamplingver);
+    if (subsamplingver == 0)
+    {
+        TIFFErrorExtR(tif, TIFFFileName(tif),
+                      "Invalid vertical YCbCr subsampling");
+        return (0);
+    }
 
-	flip = setorientation(img);
-	if (flip & FLIP_VERTICALLY) {
-		y = h - 1;
-		toskew = -(int32)(w + w);
-	} else {
-		y = 0;
-		toskew = -(int32)(w - w);
-	}
+    maxstripsize = TIFFStripSize(tif);
 
-	TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
+    flip = setorientation(img);
+    if (flip & FLIP_VERTICALLY)
+    {
+        if (w > INT_MAX)
+        {
+            TIFFErrorExtR(tif, TIFFFileName(tif), "Width overflow");
+            return (0);
+        }
+        y = h - 1;
+        toskew = -(int32_t)(w + w);
+    }
+    else
+    {
+        y = 0;
+        toskew = -(int32_t)(w - w);
+    }
 
-	scanline = TIFFScanlineSize(tif);
-	fromskew = (w < imagewidth ? imagewidth - w : 0);
-	for (row = 0; row < h; row += nrow)
-	{
-		uint32 temp;
-		rowstoread = rowsperstrip - (row + img->row_offset) % rowsperstrip;
-		nrow = (row + rowstoread > h ? h - row : rowstoread);
-		nrowsub = nrow;
-		if ((nrowsub%subsamplingver)!=0)
-			nrowsub+=subsamplingver-nrowsub%subsamplingver;
-		temp = (row + img->row_offset)%rowsperstrip + nrowsub;
-		if( scanline > 0 && temp > (size_t)(TIFF_TMSIZE_T_MAX / scanline) )
-		{
-			TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "Integer overflow in gtStripContig");
-			return 0;
-		}
-		if (_TIFFReadEncodedStripAndAllocBuffer(tif,
-		    TIFFComputeStrip(tif,row+img->row_offset, 0),
-		    (void**)(&buf),
-                    maxstripsize,
-		    temp * scanline)==(tmsize_t)(-1)
-		    && (buf == NULL || img->stoponerr))
-		{
-			ret = 0;
-			break;
-		}
+    TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
 
-		pos = ((row + img->row_offset) % rowsperstrip) * scanline + \
-			((tmsize_t) img->col_offset * img->samplesperpixel);
-		(*put)(img, raster+y*w, 0, y, w, nrow, fromskew, toskew, buf + pos);
-		y += ((flip & FLIP_VERTICALLY) ? -(int32) nrow : (int32) nrow);
-	}
+    scanline = TIFFScanlineSize(tif);
+    fromskew = (w < imagewidth ? imagewidth - w : 0);
+    for (row = 0; row < h; row += nrow)
+    {
+        uint32_t temp;
+        rowstoread = rowsperstrip - (row + img->row_offset) % rowsperstrip;
+        nrow = (row + rowstoread > h ? h - row : rowstoread);
+        nrowsub = nrow;
+        if ((nrowsub % subsamplingver) != 0)
+            nrowsub += subsamplingver - nrowsub % subsamplingver;
+        temp = (row + img->row_offset) % rowsperstrip + nrowsub;
+        if (scanline > 0 && temp > (size_t)(TIFF_TMSIZE_T_MAX / scanline))
+        {
+            TIFFErrorExtR(tif, TIFFFileName(tif),
+                          "Integer overflow in gtStripContig");
+            return 0;
+        }
+        if (_TIFFReadEncodedStripAndAllocBuffer(
+                tif, TIFFComputeStrip(tif, row + img->row_offset, 0),
+                (void **)(&buf), maxstripsize,
+                temp * scanline) == (tmsize_t)(-1) &&
+            (buf == NULL || img->stoponerr))
+        {
+            ret = 0;
+            break;
+        }
 
-	if (flip & FLIP_HORIZONTALLY) {
-		uint32 line;
+        pos = ((row + img->row_offset) % rowsperstrip) * scanline +
+              ((tmsize_t)img->col_offset * img->samplesperpixel);
+        tmsize_t roffset = (tmsize_t)y * w;
+        (*put)(img, raster + roffset, 0, y, w, nrow, fromskew, toskew,
+               buf + pos);
+        y += ((flip & FLIP_VERTICALLY) ? -(int32_t)nrow : (int32_t)nrow);
+    }
 
-		for (line = 0; line < h; line++) {
-			uint32 *left = raster + (line * w);
-			uint32 *right = left + w - 1;
+    if (flip & FLIP_HORIZONTALLY)
+    {
+        uint32_t line;
 
-			while ( left < right ) {
-				uint32 temp = *left;
-				*left = *right;
-				*right = temp;
-				left++;
-				right--;
-			}
-		}
-	}
+        for (line = 0; line < h; line++)
+        {
+            uint32_t *left = raster + (line * w);
+            uint32_t *right = left + w - 1;
 
-	_TIFFfree(buf);
-	return (ret);
+            while (left < right)
+            {
+                uint32_t temp = *left;
+                *left = *right;
+                *right = temp;
+                left++;
+                right--;
+            }
+        }
+    }
+
+    _TIFFfreeExt(img->tif, buf);
+    return (ret);
 }
 
 /*
@@ -1043,153 +1184,167 @@
  *	 PlanarConfiguration separated
  * We assume that all such images are RGB.
  */
-static int
-gtStripSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
+static int gtStripSeparate(TIFFRGBAImage *img, uint32_t *raster, uint32_t w,
+                           uint32_t h)
 {
-	TIFF* tif = img->tif;
-	tileSeparateRoutine put = img->put.separate;
-	unsigned char *buf = NULL;
-	unsigned char *p0 = NULL, *p1 = NULL, *p2 = NULL, *pa = NULL;
-	uint32 row, y, nrow, rowstoread;
-	tmsize_t pos;
-	tmsize_t scanline;
-	uint32 rowsperstrip, offset_row;
-	uint32 imagewidth = img->width;
-	tmsize_t stripsize;
-	tmsize_t bufsize;
-	int32 fromskew, toskew;
-	int alpha = img->alpha;
-	int ret = 1, flip;
-        uint16 colorchannels;
+    TIFF *tif = img->tif;
+    tileSeparateRoutine put = img->put.separate;
+    unsigned char *buf = NULL;
+    unsigned char *p0 = NULL, *p1 = NULL, *p2 = NULL, *pa = NULL;
+    uint32_t row, y, nrow, rowstoread;
+    tmsize_t pos;
+    tmsize_t scanline;
+    uint32_t rowsperstrip, offset_row;
+    uint32_t imagewidth = img->width;
+    tmsize_t stripsize;
+    tmsize_t bufsize;
+    int32_t fromskew, toskew;
+    int alpha = img->alpha;
+    int ret = 1, flip;
+    uint16_t colorchannels;
 
-	stripsize = TIFFStripSize(tif);  
-	bufsize = _TIFFMultiplySSize(tif,alpha?4:3,stripsize, "gtStripSeparate");
-	if (bufsize == 0) {
-		return (0);
-	}
+    stripsize = TIFFStripSize(tif);
+    bufsize =
+        _TIFFMultiplySSize(tif, alpha ? 4 : 3, stripsize, "gtStripSeparate");
+    if (bufsize == 0)
+    {
+        return (0);
+    }
 
-	flip = setorientation(img);
-	if (flip & FLIP_VERTICALLY) {
-		y = h - 1;
-		toskew = -(int32)(w + w);
-	}
-	else {
-		y = 0;
-		toskew = -(int32)(w - w);
-	}
-
-        switch( img->photometric )
+    flip = setorientation(img);
+    if (flip & FLIP_VERTICALLY)
+    {
+        if (w > INT_MAX)
         {
-          case PHOTOMETRIC_MINISWHITE:
-          case PHOTOMETRIC_MINISBLACK:
-          case PHOTOMETRIC_PALETTE:
+            TIFFErrorExtR(tif, TIFFFileName(tif), "Width overflow");
+            return (0);
+        }
+        y = h - 1;
+        toskew = -(int32_t)(w + w);
+    }
+    else
+    {
+        y = 0;
+        toskew = -(int32_t)(w - w);
+    }
+
+    switch (img->photometric)
+    {
+        case PHOTOMETRIC_MINISWHITE:
+        case PHOTOMETRIC_MINISBLACK:
+        case PHOTOMETRIC_PALETTE:
             colorchannels = 1;
             break;
 
-          default:
+        default:
             colorchannels = 3;
             break;
+    }
+
+    TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
+    scanline = TIFFScanlineSize(tif);
+    fromskew = (w < imagewidth ? imagewidth - w : 0);
+    for (row = 0; row < h; row += nrow)
+    {
+        uint32_t temp;
+        rowstoread = rowsperstrip - (row + img->row_offset) % rowsperstrip;
+        nrow = (row + rowstoread > h ? h - row : rowstoread);
+        offset_row = row + img->row_offset;
+        temp = (row + img->row_offset) % rowsperstrip + nrow;
+        if (scanline > 0 && temp > (size_t)(TIFF_TMSIZE_T_MAX / scanline))
+        {
+            TIFFErrorExtR(tif, TIFFFileName(tif),
+                          "Integer overflow in gtStripSeparate");
+            return 0;
+        }
+        if (buf == NULL)
+        {
+            if (_TIFFReadEncodedStripAndAllocBuffer(
+                    tif, TIFFComputeStrip(tif, offset_row, 0), (void **)&buf,
+                    bufsize, temp * scanline) == (tmsize_t)(-1) &&
+                (buf == NULL || img->stoponerr))
+            {
+                ret = 0;
+                break;
+            }
+            p0 = buf;
+            if (colorchannels == 1)
+            {
+                p2 = p1 = p0;
+                pa = (alpha ? (p0 + 3 * stripsize) : NULL);
+            }
+            else
+            {
+                p1 = p0 + stripsize;
+                p2 = p1 + stripsize;
+                pa = (alpha ? (p2 + stripsize) : NULL);
+            }
+        }
+        else if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 0),
+                                      p0, temp * scanline) == (tmsize_t)(-1) &&
+                 img->stoponerr)
+        {
+            ret = 0;
+            break;
+        }
+        if (colorchannels > 1 &&
+            TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 1), p1,
+                                 temp * scanline) == (tmsize_t)(-1) &&
+            img->stoponerr)
+        {
+            ret = 0;
+            break;
+        }
+        if (colorchannels > 1 &&
+            TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 2), p2,
+                                 temp * scanline) == (tmsize_t)(-1) &&
+            img->stoponerr)
+        {
+            ret = 0;
+            break;
+        }
+        if (alpha)
+        {
+            if (TIFFReadEncodedStrip(
+                    tif, TIFFComputeStrip(tif, offset_row, colorchannels), pa,
+                    temp * scanline) == (tmsize_t)(-1) &&
+                img->stoponerr)
+            {
+                ret = 0;
+                break;
+            }
         }
 
-	TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
-	scanline = TIFFScanlineSize(tif);  
-	fromskew = (w < imagewidth ? imagewidth - w : 0);
-	for (row = 0; row < h; row += nrow)
-	{
-                uint32 temp;
-		rowstoread = rowsperstrip - (row + img->row_offset) % rowsperstrip;
-		nrow = (row + rowstoread > h ? h - row : rowstoread);
-		offset_row = row + img->row_offset;
-                temp = (row + img->row_offset)%rowsperstrip + nrow;
-                if( scanline > 0 && temp > (size_t)(TIFF_TMSIZE_T_MAX / scanline) )
-                {
-                        TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "Integer overflow in gtStripSeparate");
-                        return 0;
-                }
-                if( buf == NULL )
-                {
-                    if (_TIFFReadEncodedStripAndAllocBuffer(
-                            tif, TIFFComputeStrip(tif, offset_row, 0),
-                            (void**) &buf, bufsize,
-                            temp * scanline)==(tmsize_t)(-1)
-                        && (buf == NULL || img->stoponerr))
-                    {
-                            ret = 0;
-                            break;
-                    }
-                    p0 = buf;
-                    if( colorchannels == 1 )
-                    {
-                        p2 = p1 = p0;
-                        pa = (alpha?(p0+3*stripsize):NULL);
-                    }
-                    else
-                    {
-                        p1 = p0 + stripsize;
-                        p2 = p1 + stripsize;
-                        pa = (alpha?(p2+stripsize):NULL);
-                    }
-                }
-		else if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 0),
-		    p0, temp * scanline)==(tmsize_t)(-1)
-		    && img->stoponerr)
-		{
-			ret = 0;
-			break;
-		}
-		if (colorchannels > 1 
-                    && TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 1),
-                                            p1, temp * scanline) == (tmsize_t)(-1)
-		    && img->stoponerr)
-		{
-			ret = 0;
-			break;
-		}
-		if (colorchannels > 1 
-                    && TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 2),
-                                            p2, temp * scanline) == (tmsize_t)(-1)
-		    && img->stoponerr)
-		{
-			ret = 0;
-			break;
-		}
-		if (alpha)
-		{
-			if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, colorchannels),
-			    pa, temp * scanline)==(tmsize_t)(-1)
-			    && img->stoponerr)
-			{
-				ret = 0;
-				break;
-			}
-		}
+        pos = ((row + img->row_offset) % rowsperstrip) * scanline +
+              ((tmsize_t)img->col_offset * img->samplesperpixel);
+        tmsize_t roffset = (tmsize_t)y * w;
+        (*put)(img, raster + roffset, 0, y, w, nrow, fromskew, toskew, p0 + pos,
+               p1 + pos, p2 + pos, (alpha ? (pa + pos) : NULL));
+        y += ((flip & FLIP_VERTICALLY) ? -(int32_t)nrow : (int32_t)nrow);
+    }
 
-		pos = ((row + img->row_offset) % rowsperstrip) * scanline + \
-			((tmsize_t) img->col_offset * img->samplesperpixel);
-		(*put)(img, raster+y*w, 0, y, w, nrow, fromskew, toskew, p0 + pos, p1 + pos,
-		    p2 + pos, (alpha?(pa+pos):NULL));
-		y += ((flip & FLIP_VERTICALLY) ? -(int32) nrow : (int32) nrow);
-	}
+    if (flip & FLIP_HORIZONTALLY)
+    {
+        uint32_t line;
 
-	if (flip & FLIP_HORIZONTALLY) {
-		uint32 line;
+        for (line = 0; line < h; line++)
+        {
+            uint32_t *left = raster + (line * w);
+            uint32_t *right = left + w - 1;
 
-		for (line = 0; line < h; line++) {
-			uint32 *left = raster + (line * w);
-			uint32 *right = left + w - 1;
+            while (left < right)
+            {
+                uint32_t temp = *left;
+                *left = *right;
+                *right = temp;
+                left++;
+                right--;
+            }
+        }
+    }
 
-			while ( left < right ) {
-				uint32 temp = *left;
-				*left = *right;
-				*right = temp;
-				left++;
-				right--;
-			}
-		}
-	}
-
-	_TIFFfree(buf);
-	return (ret);
+    _TIFFfreeExt(img->tif, buf);
+    return (ret);
 }
 
 /*
@@ -1202,98 +1357,139 @@
  * PickSeparateCase analyze the parameters and select
  * the appropriate "get" and "put" routine to use.
  */
-#define	REPEAT8(op)	REPEAT4(op); REPEAT4(op)
-#define	REPEAT4(op)	REPEAT2(op); REPEAT2(op)
-#define	REPEAT2(op)	op; op
-#define	CASE8(x,op)			\
-    switch (x) {			\
-    case 7: op; /*-fallthrough*/ \
-    case 6: op; /*-fallthrough*/ \
-    case 5: op; /*-fallthrough*/ \
-    case 4: op; /*-fallthrough*/ \
-    case 3: op; /*-fallthrough*/ \
-    case 2: op; /*-fallthrough*/ \
-    case 1: op;				\
+#define REPEAT8(op)                                                            \
+    REPEAT4(op);                                                               \
+    REPEAT4(op)
+#define REPEAT4(op)                                                            \
+    REPEAT2(op);                                                               \
+    REPEAT2(op)
+#define REPEAT2(op)                                                            \
+    op;                                                                        \
+    op
+#define CASE8(x, op)                                                           \
+    switch (x)                                                                 \
+    {                                                                          \
+        case 7:                                                                \
+            op; /*-fallthrough*/                                               \
+        case 6:                                                                \
+            op; /*-fallthrough*/                                               \
+        case 5:                                                                \
+            op; /*-fallthrough*/                                               \
+        case 4:                                                                \
+            op; /*-fallthrough*/                                               \
+        case 3:                                                                \
+            op; /*-fallthrough*/                                               \
+        case 2:                                                                \
+            op; /*-fallthrough*/                                               \
+        case 1:                                                                \
+            op;                                                                \
     }
-#define	CASE4(x,op)	switch (x) { case 3: op; /*-fallthrough*/ case 2: op; /*-fallthrough*/ case 1: op; }
-#define	NOP
+#define CASE4(x, op)                                                           \
+    switch (x)                                                                 \
+    {                                                                          \
+        case 3:                                                                \
+            op; /*-fallthrough*/                                               \
+        case 2:                                                                \
+            op; /*-fallthrough*/                                               \
+        case 1:                                                                \
+            op;                                                                \
+    }
+#define NOP
 
-#define	UNROLL8(w, op1, op2) {		\
-    uint32 _x;				\
-    for (_x = w; _x >= 8; _x -= 8) {	\
-	op1;				\
-	REPEAT8(op2);			\
-    }					\
-    if (_x > 0) {			\
-	op1;				\
-	CASE8(_x,op2);			\
-    }					\
-}
-#define	UNROLL4(w, op1, op2) {		\
-    uint32 _x;				\
-    for (_x = w; _x >= 4; _x -= 4) {	\
-	op1;				\
-	REPEAT4(op2);			\
-    }					\
-    if (_x > 0) {			\
-	op1;				\
-	CASE4(_x,op2);			\
-    }					\
-}
-#define	UNROLL2(w, op1, op2) {		\
-    uint32 _x;				\
-    for (_x = w; _x >= 2; _x -= 2) {	\
-	op1;				\
-	REPEAT2(op2);			\
-    }					\
-    if (_x) {				\
-	op1;				\
-	op2;				\
-    }					\
-}
-    
-#define	SKEW(r,g,b,skew)	{ r += skew; g += skew; b += skew; }
-#define	SKEW4(r,g,b,a,skew)	{ r += skew; g += skew; b += skew; a+= skew; }
+#define UNROLL8(w, op1, op2)                                                   \
+    {                                                                          \
+        uint32_t _x;                                                           \
+        for (_x = w; _x >= 8; _x -= 8)                                         \
+        {                                                                      \
+            op1;                                                               \
+            REPEAT8(op2);                                                      \
+        }                                                                      \
+        if (_x > 0)                                                            \
+        {                                                                      \
+            op1;                                                               \
+            CASE8(_x, op2);                                                    \
+        }                                                                      \
+    }
+#define UNROLL4(w, op1, op2)                                                   \
+    {                                                                          \
+        uint32_t _x;                                                           \
+        for (_x = w; _x >= 4; _x -= 4)                                         \
+        {                                                                      \
+            op1;                                                               \
+            REPEAT4(op2);                                                      \
+        }                                                                      \
+        if (_x > 0)                                                            \
+        {                                                                      \
+            op1;                                                               \
+            CASE4(_x, op2);                                                    \
+        }                                                                      \
+    }
+#define UNROLL2(w, op1, op2)                                                   \
+    {                                                                          \
+        uint32_t _x;                                                           \
+        for (_x = w; _x >= 2; _x -= 2)                                         \
+        {                                                                      \
+            op1;                                                               \
+            REPEAT2(op2);                                                      \
+        }                                                                      \
+        if (_x)                                                                \
+        {                                                                      \
+            op1;                                                               \
+            op2;                                                               \
+        }                                                                      \
+    }
 
-#define A1 (((uint32)0xffL)<<24)
-#define	PACK(r,g,b)	\
-	((uint32)(r)|((uint32)(g)<<8)|((uint32)(b)<<16)|A1)
-#define	PACK4(r,g,b,a)	\
-	((uint32)(r)|((uint32)(g)<<8)|((uint32)(b)<<16)|((uint32)(a)<<24))
-#define W2B(v) (((v)>>8)&0xff)
+#define SKEW(r, g, b, skew)                                                    \
+    {                                                                          \
+        r += skew;                                                             \
+        g += skew;                                                             \
+        b += skew;                                                             \
+    }
+#define SKEW4(r, g, b, a, skew)                                                \
+    {                                                                          \
+        r += skew;                                                             \
+        g += skew;                                                             \
+        b += skew;                                                             \
+        a += skew;                                                             \
+    }
+
+#define A1 (((uint32_t)0xffL) << 24)
+#define PACK(r, g, b)                                                          \
+    ((uint32_t)(r) | ((uint32_t)(g) << 8) | ((uint32_t)(b) << 16) | A1)
+#define PACK4(r, g, b, a)                                                      \
+    ((uint32_t)(r) | ((uint32_t)(g) << 8) | ((uint32_t)(b) << 16) |            \
+     ((uint32_t)(a) << 24))
+#define W2B(v) (((v) >> 8) & 0xff)
 /* TODO: PACKW should have be made redundant in favor of Bitdepth16To8 LUT */
-#define	PACKW(r,g,b)	\
-	((uint32)W2B(r)|((uint32)W2B(g)<<8)|((uint32)W2B(b)<<16)|A1)
-#define	PACKW4(r,g,b,a)	\
-	((uint32)W2B(r)|((uint32)W2B(g)<<8)|((uint32)W2B(b)<<16)|((uint32)W2B(a)<<24))
+#define PACKW(r, g, b)                                                         \
+    ((uint32_t)W2B(r) | ((uint32_t)W2B(g) << 8) | ((uint32_t)W2B(b) << 16) | A1)
+#define PACKW4(r, g, b, a)                                                     \
+    ((uint32_t)W2B(r) | ((uint32_t)W2B(g) << 8) | ((uint32_t)W2B(b) << 16) |   \
+     ((uint32_t)W2B(a) << 24))
 
-#define	DECLAREContigPutFunc(name) \
-static void name(\
-    TIFFRGBAImage* img, \
-    uint32* cp, \
-    uint32 x, uint32 y, \
-    uint32 w, uint32 h, \
-    int32 fromskew, int32 toskew, \
-    unsigned char* pp \
-)
+#define DECLAREContigPutFunc(name)                                             \
+    static void name(TIFFRGBAImage *img, uint32_t *cp, uint32_t x, uint32_t y, \
+                     uint32_t w, uint32_t h, int32_t fromskew, int32_t toskew, \
+                     unsigned char *pp)
 
 /*
  * 8-bit palette => colormap/RGB
  */
 DECLAREContigPutFunc(put8bitcmaptile)
 {
-    uint32** PALmap = img->PALmap;
+    uint32_t **PALmap = img->PALmap;
     int samplesperpixel = img->samplesperpixel;
 
-    (void) y;
-    for( ; h > 0; --h) {
-	for (x = w; x > 0; --x)
+    (void)y;
+    for (; h > 0; --h)
+    {
+        for (x = w; x > 0; --x)
         {
-	    *cp++ = PALmap[*pp][0];
+            *cp++ = PALmap[*pp][0];
             pp += samplesperpixel;
         }
-	cp += toskew;
-	pp += fromskew;
+        cp += toskew;
+        pp += fromskew;
     }
 }
 
@@ -1302,15 +1498,17 @@
  */
 DECLAREContigPutFunc(put4bitcmaptile)
 {
-    uint32** PALmap = img->PALmap;
+    uint32_t **PALmap = img->PALmap;
 
-    (void) x; (void) y;
+    (void)x;
+    (void)y;
     fromskew /= 2;
-    for( ; h > 0; --h) {
-	uint32* bw;
-	UNROLL2(w, bw = PALmap[*pp++], *cp++ = *bw++);
-	cp += toskew;
-	pp += fromskew;
+    for (; h > 0; --h)
+    {
+        uint32_t *bw;
+        UNROLL2(w, bw = PALmap[*pp++], *cp++ = *bw++);
+        cp += toskew;
+        pp += fromskew;
     }
 }
 
@@ -1319,15 +1517,17 @@
  */
 DECLAREContigPutFunc(put2bitcmaptile)
 {
-    uint32** PALmap = img->PALmap;
+    uint32_t **PALmap = img->PALmap;
 
-    (void) x; (void) y;
+    (void)x;
+    (void)y;
     fromskew /= 4;
-    for( ; h > 0; --h) {
-	uint32* bw;
-	UNROLL4(w, bw = PALmap[*pp++], *cp++ = *bw++);
-	cp += toskew;
-	pp += fromskew;
+    for (; h > 0; --h)
+    {
+        uint32_t *bw;
+        UNROLL4(w, bw = PALmap[*pp++], *cp++ = *bw++);
+        cp += toskew;
+        pp += fromskew;
     }
 }
 
@@ -1336,15 +1536,17 @@
  */
 DECLAREContigPutFunc(put1bitcmaptile)
 {
-    uint32** PALmap = img->PALmap;
+    uint32_t **PALmap = img->PALmap;
 
-    (void) x; (void) y;
+    (void)x;
+    (void)y;
     fromskew /= 8;
-    for( ; h > 0; --h) {
-	uint32* bw;
-	UNROLL8(w, bw = PALmap[*pp++], *cp++ = *bw++);
-	cp += toskew;
-	pp += fromskew;
+    for (; h > 0; --h)
+    {
+        uint32_t *bw;
+        UNROLL8(w, bw = PALmap[*pp++], *cp++ = *bw++);
+        cp += toskew;
+        pp += fromskew;
     }
 }
 
@@ -1354,17 +1556,18 @@
 DECLAREContigPutFunc(putgreytile)
 {
     int samplesperpixel = img->samplesperpixel;
-    uint32** BWmap = img->BWmap;
+    uint32_t **BWmap = img->BWmap;
 
-    (void) y;
-    for( ; h > 0; --h) {
-	for (x = w; x > 0; --x)
+    (void)y;
+    for (; h > 0; --h)
+    {
+        for (x = w; x > 0; --x)
         {
-	    *cp++ = BWmap[*pp][0];
+            *cp++ = BWmap[*pp][0];
             pp += samplesperpixel;
         }
-	cp += toskew;
-	pp += fromskew;
+        cp += toskew;
+        pp += fromskew;
     }
 }
 
@@ -1374,17 +1577,18 @@
 DECLAREContigPutFunc(putagreytile)
 {
     int samplesperpixel = img->samplesperpixel;
-    uint32** BWmap = img->BWmap;
+    uint32_t **BWmap = img->BWmap;
 
-    (void) y;
-    for( ; h > 0; --h) {
-	for (x = w; x > 0; --x)
+    (void)y;
+    for (; h > 0; --h)
+    {
+        for (x = w; x > 0; --x)
         {
-            *cp++ = BWmap[*pp][0] & ((uint32)*(pp+1) << 24 | ~A1);
+            *cp++ = BWmap[*pp][0] & ((uint32_t) * (pp + 1) << 24 | ~A1);
             pp += samplesperpixel;
         }
-	cp += toskew;
-	pp += fromskew;
+        cp += toskew;
+        pp += fromskew;
     }
 }
 
@@ -1394,22 +1598,23 @@
 DECLAREContigPutFunc(put16bitbwtile)
 {
     int samplesperpixel = img->samplesperpixel;
-    uint32** BWmap = img->BWmap;
+    uint32_t **BWmap = img->BWmap;
 
-    (void) y;
-    for( ; h > 0; --h) {
-        uint16 *wp = (uint16 *) pp;
+    (void)y;
+    for (; h > 0; --h)
+    {
+        uint16_t *wp = (uint16_t *)pp;
 
-	for (x = w; x > 0; --x)
+        for (x = w; x > 0; --x)
         {
             /* use high order byte of 16bit value */
 
-	    *cp++ = BWmap[*wp >> 8][0];
+            *cp++ = BWmap[*wp >> 8][0];
             pp += 2 * samplesperpixel;
             wp += samplesperpixel;
         }
-	cp += toskew;
-	pp += fromskew;
+        cp += toskew;
+        pp += fromskew;
     }
 }
 
@@ -1418,15 +1623,17 @@
  */
 DECLAREContigPutFunc(put1bitbwtile)
 {
-    uint32** BWmap = img->BWmap;
+    uint32_t **BWmap = img->BWmap;
 
-    (void) x; (void) y;
+    (void)x;
+    (void)y;
     fromskew /= 8;
-    for( ; h > 0; --h) {
-	uint32* bw;
-	UNROLL8(w, bw = BWmap[*pp++], *cp++ = *bw++);
-	cp += toskew;
-	pp += fromskew;
+    for (; h > 0; --h)
+    {
+        uint32_t *bw;
+        UNROLL8(w, bw = BWmap[*pp++], *cp++ = *bw++);
+        cp += toskew;
+        pp += fromskew;
     }
 }
 
@@ -1435,15 +1642,17 @@
  */
 DECLAREContigPutFunc(put2bitbwtile)
 {
-    uint32** BWmap = img->BWmap;
+    uint32_t **BWmap = img->BWmap;
 
-    (void) x; (void) y;
+    (void)x;
+    (void)y;
     fromskew /= 4;
-    for( ; h > 0; --h) {
-	uint32* bw;
-	UNROLL4(w, bw = BWmap[*pp++], *cp++ = *bw++);
-	cp += toskew;
-	pp += fromskew;
+    for (; h > 0; --h)
+    {
+        uint32_t *bw;
+        UNROLL4(w, bw = BWmap[*pp++], *cp++ = *bw++);
+        cp += toskew;
+        pp += fromskew;
     }
 }
 
@@ -1452,15 +1661,17 @@
  */
 DECLAREContigPutFunc(put4bitbwtile)
 {
-    uint32** BWmap = img->BWmap;
+    uint32_t **BWmap = img->BWmap;
 
-    (void) x; (void) y;
+    (void)x;
+    (void)y;
     fromskew /= 2;
-    for( ; h > 0; --h) {
-	uint32* bw;
-	UNROLL2(w, bw = BWmap[*pp++], *cp++ = *bw++);
-	cp += toskew;
-	pp += fromskew;
+    for (; h > 0; --h)
+    {
+        uint32_t *bw;
+        UNROLL2(w, bw = BWmap[*pp++], *cp++ = *bw++);
+        cp += toskew;
+        pp += fromskew;
     }
 }
 
@@ -1471,14 +1682,15 @@
 {
     int samplesperpixel = img->samplesperpixel;
 
-    (void) x; (void) y;
+    (void)x;
+    (void)y;
     fromskew *= samplesperpixel;
-    for( ; h > 0; --h) {
-	UNROLL8(w, NOP,
-	    *cp++ = PACK(pp[0], pp[1], pp[2]);
-	    pp += samplesperpixel);
-	cp += toskew;
-	pp += fromskew;
+    for (; h > 0; --h)
+    {
+        UNROLL8(w, NOP, *cp++ = PACK(pp[0], pp[1], pp[2]);
+                pp += samplesperpixel);
+        cp += toskew;
+        pp += fromskew;
     }
 }
 
@@ -1490,14 +1702,15 @@
 {
     int samplesperpixel = img->samplesperpixel;
 
-    (void) x; (void) y;
+    (void)x;
+    (void)y;
     fromskew *= samplesperpixel;
-    for( ; h > 0; --h) {
-	UNROLL8(w, NOP,
-	    *cp++ = PACK4(pp[0], pp[1], pp[2], pp[3]);
-	    pp += samplesperpixel);
-	cp += toskew;
-	pp += fromskew;
+    for (; h > 0; --h)
+    {
+        UNROLL8(w, NOP, *cp++ = PACK4(pp[0], pp[1], pp[2], pp[3]);
+                pp += samplesperpixel);
+        cp += toskew;
+        pp += fromskew;
     }
 }
 
@@ -1507,24 +1720,26 @@
  */
 DECLAREContigPutFunc(putRGBUAcontig8bittile)
 {
-	int samplesperpixel = img->samplesperpixel;
-	(void) y;
-	fromskew *= samplesperpixel;
-	for( ; h > 0; --h) {
-		uint32 r, g, b, a;
-		uint8* m;
-		for (x = w; x > 0; --x) {
-			a = pp[3];
-			m = img->UaToAa+((size_t) a<<8);
-			r = m[pp[0]];
-			g = m[pp[1]];
-			b = m[pp[2]];
-			*cp++ = PACK4(r,g,b,a);
-			pp += samplesperpixel;
-		}
-		cp += toskew;
-		pp += fromskew;
-	}
+    int samplesperpixel = img->samplesperpixel;
+    (void)y;
+    fromskew *= samplesperpixel;
+    for (; h > 0; --h)
+    {
+        uint32_t r, g, b, a;
+        uint8_t *m;
+        for (x = w; x > 0; --x)
+        {
+            a = pp[3];
+            m = img->UaToAa + ((size_t)a << 8);
+            r = m[pp[0]];
+            g = m[pp[1]];
+            b = m[pp[2]];
+            *cp++ = PACK4(r, g, b, a);
+            pp += samplesperpixel;
+        }
+        cp += toskew;
+        pp += fromskew;
+    }
 }
 
 /*
@@ -1532,20 +1747,21 @@
  */
 DECLAREContigPutFunc(putRGBcontig16bittile)
 {
-	int samplesperpixel = img->samplesperpixel;
-	uint16 *wp = (uint16 *)pp;
-	(void) y;
-	fromskew *= samplesperpixel;
-	for( ; h > 0; --h) {
-		for (x = w; x > 0; --x) {
-			*cp++ = PACK(img->Bitdepth16To8[wp[0]],
-			    img->Bitdepth16To8[wp[1]],
-			    img->Bitdepth16To8[wp[2]]);
-			wp += samplesperpixel;
-		}
-		cp += toskew;
-		wp += fromskew;
-	}
+    int samplesperpixel = img->samplesperpixel;
+    uint16_t *wp = (uint16_t *)pp;
+    (void)y;
+    fromskew *= samplesperpixel;
+    for (; h > 0; --h)
+    {
+        for (x = w; x > 0; --x)
+        {
+            *cp++ = PACK(img->Bitdepth16To8[wp[0]], img->Bitdepth16To8[wp[1]],
+                         img->Bitdepth16To8[wp[2]]);
+            wp += samplesperpixel;
+        }
+        cp += toskew;
+        wp += fromskew;
+    }
 }
 
 /*
@@ -1554,21 +1770,21 @@
  */
 DECLAREContigPutFunc(putRGBAAcontig16bittile)
 {
-	int samplesperpixel = img->samplesperpixel;
-	uint16 *wp = (uint16 *)pp;
-	(void) y;
-	fromskew *= samplesperpixel;
-	for( ; h > 0; --h) {
-		for (x = w; x > 0; --x) {
-			*cp++ = PACK4(img->Bitdepth16To8[wp[0]],
-			    img->Bitdepth16To8[wp[1]],
-			    img->Bitdepth16To8[wp[2]],
-			    img->Bitdepth16To8[wp[3]]);
-			wp += samplesperpixel;
-		}
-		cp += toskew;
-		wp += fromskew;
-	}
+    int samplesperpixel = img->samplesperpixel;
+    uint16_t *wp = (uint16_t *)pp;
+    (void)y;
+    fromskew *= samplesperpixel;
+    for (; h > 0; --h)
+    {
+        for (x = w; x > 0; --x)
+        {
+            *cp++ = PACK4(img->Bitdepth16To8[wp[0]], img->Bitdepth16To8[wp[1]],
+                          img->Bitdepth16To8[wp[2]], img->Bitdepth16To8[wp[3]]);
+            wp += samplesperpixel;
+        }
+        cp += toskew;
+        wp += fromskew;
+    }
 }
 
 /*
@@ -1577,25 +1793,27 @@
  */
 DECLAREContigPutFunc(putRGBUAcontig16bittile)
 {
-	int samplesperpixel = img->samplesperpixel;
-	uint16 *wp = (uint16 *)pp;
-	(void) y;
-	fromskew *= samplesperpixel;
-	for( ; h > 0; --h) {
-		uint32 r,g,b,a;
-		uint8* m;
-		for (x = w; x > 0; --x) {
-			a = img->Bitdepth16To8[wp[3]];
-			m = img->UaToAa+((size_t) a<<8);
-			r = m[img->Bitdepth16To8[wp[0]]];
-			g = m[img->Bitdepth16To8[wp[1]]];
-			b = m[img->Bitdepth16To8[wp[2]]];
-			*cp++ = PACK4(r,g,b,a);
-			wp += samplesperpixel;
-		}
-		cp += toskew;
-		wp += fromskew;
-	}
+    int samplesperpixel = img->samplesperpixel;
+    uint16_t *wp = (uint16_t *)pp;
+    (void)y;
+    fromskew *= samplesperpixel;
+    for (; h > 0; --h)
+    {
+        uint32_t r, g, b, a;
+        uint8_t *m;
+        for (x = w; x > 0; --x)
+        {
+            a = img->Bitdepth16To8[wp[3]];
+            m = img->UaToAa + ((size_t)a << 8);
+            r = m[img->Bitdepth16To8[wp[0]]];
+            g = m[img->Bitdepth16To8[wp[1]]];
+            b = m[img->Bitdepth16To8[wp[2]]];
+            *cp++ = PACK4(r, g, b, a);
+            wp += samplesperpixel;
+        }
+        cp += toskew;
+        wp += fromskew;
+    }
 }
 
 /*
@@ -1606,20 +1824,18 @@
 DECLAREContigPutFunc(putRGBcontig8bitCMYKtile)
 {
     int samplesperpixel = img->samplesperpixel;
-    uint16 r, g, b, k;
+    uint16_t r, g, b, k;
 
-    (void) x; (void) y;
+    (void)x;
+    (void)y;
     fromskew *= samplesperpixel;
-    for( ; h > 0; --h) {
-	UNROLL8(w, NOP,
-	    k = 255 - pp[3];
-	    r = (k*(255-pp[0]))/255;
-	    g = (k*(255-pp[1]))/255;
-	    b = (k*(255-pp[2]))/255;
-	    *cp++ = PACK(r, g, b);
-	    pp += samplesperpixel);
-	cp += toskew;
-	pp += fromskew;
+    for (; h > 0; --h)
+    {
+        UNROLL8(w, NOP, k = 255 - pp[3]; r = (k * (255 - pp[0])) / 255;
+                g = (k * (255 - pp[1])) / 255; b = (k * (255 - pp[2])) / 255;
+                *cp++ = PACK(r, g, b); pp += samplesperpixel);
+        cp += toskew;
+        pp += fromskew;
     }
 }
 
@@ -1631,45 +1847,47 @@
 DECLAREContigPutFunc(putRGBcontig8bitCMYKMaptile)
 {
     int samplesperpixel = img->samplesperpixel;
-    TIFFRGBValue* Map = img->Map;
-    uint16 r, g, b, k;
+    TIFFRGBValue *Map = img->Map;
+    uint16_t r, g, b, k;
 
-    (void) y;
+    (void)y;
     fromskew *= samplesperpixel;
-    for( ; h > 0; --h) {
-	for (x = w; x > 0; --x) {
-	    k = 255 - pp[3];
-	    r = (k*(255-pp[0]))/255;
-	    g = (k*(255-pp[1]))/255;
-	    b = (k*(255-pp[2]))/255;
-	    *cp++ = PACK(Map[r], Map[g], Map[b]);
-	    pp += samplesperpixel;
-	}
-	pp += fromskew;
-	cp += toskew;
+    for (; h > 0; --h)
+    {
+        for (x = w; x > 0; --x)
+        {
+            k = 255 - pp[3];
+            r = (k * (255 - pp[0])) / 255;
+            g = (k * (255 - pp[1])) / 255;
+            b = (k * (255 - pp[2])) / 255;
+            *cp++ = PACK(Map[r], Map[g], Map[b]);
+            pp += samplesperpixel;
+        }
+        pp += fromskew;
+        cp += toskew;
     }
 }
 
-#define	DECLARESepPutFunc(name) \
-static void name(\
-    TIFFRGBAImage* img,\
-    uint32* cp,\
-    uint32 x, uint32 y, \
-    uint32 w, uint32 h,\
-    int32 fromskew, int32 toskew,\
-    unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a\
-)
+#define DECLARESepPutFunc(name)                                                \
+    static void name(TIFFRGBAImage *img, uint32_t *cp, uint32_t x, uint32_t y, \
+                     uint32_t w, uint32_t h, int32_t fromskew, int32_t toskew, \
+                     unsigned char *r, unsigned char *g, unsigned char *b,     \
+                     unsigned char *a)
 
 /*
  * 8-bit unpacked samples => RGB
  */
 DECLARESepPutFunc(putRGBseparate8bittile)
 {
-    (void) img; (void) x; (void) y; (void) a;
-    for( ; h > 0; --h) {
-	UNROLL8(w, NOP, *cp++ = PACK(*r++, *g++, *b++));
-	SKEW(r, g, b, fromskew);
-	cp += toskew;
+    (void)img;
+    (void)x;
+    (void)y;
+    (void)a;
+    for (; h > 0; --h)
+    {
+        UNROLL8(w, NOP, *cp++ = PACK(*r++, *g++, *b++));
+        SKEW(r, g, b, fromskew);
+        cp += toskew;
     }
 }
 
@@ -1678,12 +1896,15 @@
  */
 DECLARESepPutFunc(putRGBAAseparate8bittile)
 {
-	(void) img; (void) x; (void) y; 
-	for( ; h > 0; --h) {
-		UNROLL8(w, NOP, *cp++ = PACK4(*r++, *g++, *b++, *a++));
-		SKEW4(r, g, b, a, fromskew);
-		cp += toskew;
-	}
+    (void)img;
+    (void)x;
+    (void)y;
+    for (; h > 0; --h)
+    {
+        UNROLL8(w, NOP, *cp++ = PACK4(*r++, *g++, *b++, *a++));
+        SKEW4(r, g, b, a, fromskew);
+        cp += toskew;
+    }
 }
 
 /*
@@ -1691,19 +1912,22 @@
  */
 DECLARESepPutFunc(putCMYKseparate8bittile)
 {
-	(void) img; (void) y;
-	for( ; h > 0; --h) {
-		uint32 rv, gv, bv, kv;
-		for (x = w; x > 0; --x) {
-			kv = 255 - *a++;
-			rv = (kv*(255-*r++))/255;
-			gv = (kv*(255-*g++))/255;
-			bv = (kv*(255-*b++))/255;
-			*cp++ = PACK4(rv,gv,bv,255);
-		}
-		SKEW4(r, g, b, a, fromskew);
-		cp += toskew;
-	}
+    (void)img;
+    (void)y;
+    for (; h > 0; --h)
+    {
+        uint32_t rv, gv, bv, kv;
+        for (x = w; x > 0; --x)
+        {
+            kv = 255 - *a++;
+            rv = (kv * (255 - *r++)) / 255;
+            gv = (kv * (255 - *g++)) / 255;
+            bv = (kv * (255 - *b++)) / 255;
+            *cp++ = PACK4(rv, gv, bv, 255);
+        }
+        SKEW4(r, g, b, a, fromskew);
+        cp += toskew;
+    }
 }
 
 /*
@@ -1711,21 +1935,24 @@
  */
 DECLARESepPutFunc(putRGBUAseparate8bittile)
 {
-	(void) img; (void) y;
-	for( ; h > 0; --h) {
-		uint32 rv, gv, bv, av;
-		uint8* m;
-		for (x = w; x > 0; --x) {
-			av = *a++;
-			m = img->UaToAa+((size_t) av<<8);
-			rv = m[*r++];
-			gv = m[*g++];
-			bv = m[*b++];
-			*cp++ = PACK4(rv,gv,bv,av);
-		}
-		SKEW4(r, g, b, a, fromskew);
-		cp += toskew;
-	}
+    (void)img;
+    (void)y;
+    for (; h > 0; --h)
+    {
+        uint32_t rv, gv, bv, av;
+        uint8_t *m;
+        for (x = w; x > 0; --x)
+        {
+            av = *a++;
+            m = img->UaToAa + ((size_t)av << 8);
+            rv = m[*r++];
+            gv = m[*g++];
+            bv = m[*b++];
+            *cp++ = PACK4(rv, gv, bv, av);
+        }
+        SKEW4(r, g, b, a, fromskew);
+        cp += toskew;
+    }
 }
 
 /*
@@ -1733,18 +1960,20 @@
  */
 DECLARESepPutFunc(putRGBseparate16bittile)
 {
-	uint16 *wr = (uint16*) r;
-	uint16 *wg = (uint16*) g;
-	uint16 *wb = (uint16*) b;
-	(void) img; (void) y; (void) a;
-	for( ; h > 0; --h) {
-		for (x = 0; x < w; x++)
-			*cp++ = PACK(img->Bitdepth16To8[*wr++],
-			    img->Bitdepth16To8[*wg++],
-			    img->Bitdepth16To8[*wb++]);
-		SKEW(wr, wg, wb, fromskew);
-		cp += toskew;
-	}
+    uint16_t *wr = (uint16_t *)r;
+    uint16_t *wg = (uint16_t *)g;
+    uint16_t *wb = (uint16_t *)b;
+    (void)img;
+    (void)y;
+    (void)a;
+    for (; h > 0; --h)
+    {
+        for (x = 0; x < w; x++)
+            *cp++ = PACK(img->Bitdepth16To8[*wr++], img->Bitdepth16To8[*wg++],
+                         img->Bitdepth16To8[*wb++]);
+        SKEW(wr, wg, wb, fromskew);
+        cp += toskew;
+    }
 }
 
 /*
@@ -1752,20 +1981,20 @@
  */
 DECLARESepPutFunc(putRGBAAseparate16bittile)
 {
-	uint16 *wr = (uint16*) r;
-	uint16 *wg = (uint16*) g;
-	uint16 *wb = (uint16*) b;
-	uint16 *wa = (uint16*) a;
-	(void) img; (void) y;
-	for( ; h > 0; --h) {
-		for (x = 0; x < w; x++)
-			*cp++ = PACK4(img->Bitdepth16To8[*wr++],
-			    img->Bitdepth16To8[*wg++],
-			    img->Bitdepth16To8[*wb++],
-			    img->Bitdepth16To8[*wa++]);
-		SKEW4(wr, wg, wb, wa, fromskew);
-		cp += toskew;
-	}
+    uint16_t *wr = (uint16_t *)r;
+    uint16_t *wg = (uint16_t *)g;
+    uint16_t *wb = (uint16_t *)b;
+    uint16_t *wa = (uint16_t *)a;
+    (void)img;
+    (void)y;
+    for (; h > 0; --h)
+    {
+        for (x = 0; x < w; x++)
+            *cp++ = PACK4(img->Bitdepth16To8[*wr++], img->Bitdepth16To8[*wg++],
+                          img->Bitdepth16To8[*wb++], img->Bitdepth16To8[*wa++]);
+        SKEW4(wr, wg, wb, wa, fromskew);
+        cp += toskew;
+    }
 }
 
 /*
@@ -1773,168 +2002,123 @@
  */
 DECLARESepPutFunc(putRGBUAseparate16bittile)
 {
-	uint16 *wr = (uint16*) r;
-	uint16 *wg = (uint16*) g;
-	uint16 *wb = (uint16*) b;
-	uint16 *wa = (uint16*) a;
-	(void) img; (void) y;
-	for( ; h > 0; --h) {
-		uint32 r2,g2,b2,a2;
-		uint8* m;
-		for (x = w; x > 0; --x) {
-			a2 = img->Bitdepth16To8[*wa++];
-			m = img->UaToAa+((size_t) a2<<8);
-			r2 = m[img->Bitdepth16To8[*wr++]];
-			g2 = m[img->Bitdepth16To8[*wg++]];
-			b2 = m[img->Bitdepth16To8[*wb++]];
-			*cp++ = PACK4(r2,g2,b2,a2);
-		}
-		SKEW4(wr, wg, wb, wa, fromskew);
-		cp += toskew;
-	}
+    uint16_t *wr = (uint16_t *)r;
+    uint16_t *wg = (uint16_t *)g;
+    uint16_t *wb = (uint16_t *)b;
+    uint16_t *wa = (uint16_t *)a;
+    (void)img;
+    (void)y;
+    for (; h > 0; --h)
+    {
+        uint32_t r2, g2, b2, a2;
+        uint8_t *m;
+        for (x = w; x > 0; --x)
+        {
+            a2 = img->Bitdepth16To8[*wa++];
+            m = img->UaToAa + ((size_t)a2 << 8);
+            r2 = m[img->Bitdepth16To8[*wr++]];
+            g2 = m[img->Bitdepth16To8[*wg++]];
+            b2 = m[img->Bitdepth16To8[*wb++]];
+            *cp++ = PACK4(r2, g2, b2, a2);
+        }
+        SKEW4(wr, wg, wb, wa, fromskew);
+        cp += toskew;
+    }
 }
 
 /*
  * 8-bit packed CIE L*a*b 1976 samples => RGB
  */
-DECLAREContigPutFunc(putcontig8bitCIELab)
+DECLAREContigPutFunc(putcontig8bitCIELab8)
 {
-	float X, Y, Z;
-	uint32 r, g, b;
-	(void) y;
-	fromskew *= 3;
-	for( ; h > 0; --h) {
-		for (x = w; x > 0; --x) {
-			TIFFCIELabToXYZ(img->cielab,
-					(unsigned char)pp[0],
-					(signed char)pp[1],
-					(signed char)pp[2],
-					&X, &Y, &Z);
-			TIFFXYZToRGB(img->cielab, X, Y, Z, &r, &g, &b);
-			*cp++ = PACK(r, g, b);
-			pp += 3;
-		}
-		cp += toskew;
-		pp += fromskew;
-	}
+    float X, Y, Z;
+    uint32_t r, g, b;
+    (void)y;
+    fromskew *= 3;
+    for (; h > 0; --h)
+    {
+        for (x = w; x > 0; --x)
+        {
+            TIFFCIELabToXYZ(img->cielab, (unsigned char)pp[0],
+                            (signed char)pp[1], (signed char)pp[2], &X, &Y, &Z);
+            TIFFXYZToRGB(img->cielab, X, Y, Z, &r, &g, &b);
+            *cp++ = PACK(r, g, b);
+            pp += 3;
+        }
+        cp += toskew;
+        pp += fromskew;
+    }
+}
+
+/*
+ * 16-bit packed CIE L*a*b 1976 samples => RGB
+ */
+DECLAREContigPutFunc(putcontig8bitCIELab16)
+{
+    float X, Y, Z;
+    uint32_t r, g, b;
+    uint16_t *wp = (uint16_t *)pp;
+    (void)y;
+    fromskew *= 3;
+    for (; h > 0; --h)
+    {
+        for (x = w; x > 0; --x)
+        {
+            TIFFCIELab16ToXYZ(img->cielab, (uint16_t)wp[0], (int16_t)wp[1],
+                              (int16_t)wp[2], &X, &Y, &Z);
+            TIFFXYZToRGB(img->cielab, X, Y, Z, &r, &g, &b);
+            *cp++ = PACK(r, g, b);
+            wp += 3;
+        }
+        cp += toskew;
+        wp += fromskew;
+    }
 }
 
 /*
  * YCbCr -> RGB conversion and packing routines.
  */
 
-#define	YCbCrtoRGB(dst, Y) {						\
-	uint32 r, g, b;							\
-	TIFFYCbCrtoRGB(img->ycbcr, (Y), Cb, Cr, &r, &g, &b);		\
-	dst = PACK(r, g, b);						\
-}
-
-/*
- * 8-bit packed YCbCr samples => RGB 
- * This function is generic for different sampling sizes, 
- * and can handle blocks sizes that aren't multiples of the
- * sampling size.  However, it is substantially less optimized
- * than the specific sampling cases.  It is used as a fallback
- * for difficult blocks.
- */
-#ifdef notdef
-static void putcontig8bitYCbCrGenericTile( 
-    TIFFRGBAImage* img, 
-    uint32* cp, 
-    uint32 x, uint32 y, 
-    uint32 w, uint32 h, 
-    int32 fromskew, int32 toskew, 
-    unsigned char* pp,
-    int h_group, 
-    int v_group )
-
-{
-    uint32* cp1 = cp+w+toskew;
-    uint32* cp2 = cp1+w+toskew;
-    uint32* cp3 = cp2+w+toskew;
-    int32 incr = 3*w+4*toskew;
-    int32   Cb, Cr;
-    int     group_size = v_group * h_group + 2;
-
-    (void) y;
-    fromskew = (fromskew * group_size) / h_group;
-
-    for( yy = 0; yy < h; yy++ )
-    {
-        unsigned char *pp_line;
-        int     y_line_group = yy / v_group;
-        int     y_remainder = yy - y_line_group * v_group;
-
-        pp_line = pp + v_line_group * 
-
-        
-        for( xx = 0; xx < w; xx++ )
-        {
-            Cb = pp
-        }
+#define YCbCrtoRGB(dst, Y)                                                     \
+    {                                                                          \
+        uint32_t r, g, b;                                                      \
+        TIFFYCbCrtoRGB(img->ycbcr, (Y), Cb, Cr, &r, &g, &b);                   \
+        dst = PACK(r, g, b);                                                   \
     }
-    for (; h >= 4; h -= 4) {
-	x = w>>2;
-	do {
-	    Cb = pp[16];
-	    Cr = pp[17];
-
-	    YCbCrtoRGB(cp [0], pp[ 0]);
-	    YCbCrtoRGB(cp [1], pp[ 1]);
-	    YCbCrtoRGB(cp [2], pp[ 2]);
-	    YCbCrtoRGB(cp [3], pp[ 3]);
-	    YCbCrtoRGB(cp1[0], pp[ 4]);
-	    YCbCrtoRGB(cp1[1], pp[ 5]);
-	    YCbCrtoRGB(cp1[2], pp[ 6]);
-	    YCbCrtoRGB(cp1[3], pp[ 7]);
-	    YCbCrtoRGB(cp2[0], pp[ 8]);
-	    YCbCrtoRGB(cp2[1], pp[ 9]);
-	    YCbCrtoRGB(cp2[2], pp[10]);
-	    YCbCrtoRGB(cp2[3], pp[11]);
-	    YCbCrtoRGB(cp3[0], pp[12]);
-	    YCbCrtoRGB(cp3[1], pp[13]);
-	    YCbCrtoRGB(cp3[2], pp[14]);
-	    YCbCrtoRGB(cp3[3], pp[15]);
-
-	    cp += 4, cp1 += 4, cp2 += 4, cp3 += 4;
-	    pp += 18;
-	} while (--x);
-	cp += incr, cp1 += incr, cp2 += incr, cp3 += incr;
-	pp += fromskew;
-    }
-}
-#endif
 
 /*
  * 8-bit packed YCbCr samples w/ 4,4 subsampling => RGB
  */
 DECLAREContigPutFunc(putcontig8bitYCbCr44tile)
 {
-    uint32* cp1 = cp+w+toskew;
-    uint32* cp2 = cp1+w+toskew;
-    uint32* cp3 = cp2+w+toskew;
-    int32 incr = 3*w+4*toskew;
+    uint32_t *cp1 = cp + w + toskew;
+    uint32_t *cp2 = cp1 + w + toskew;
+    uint32_t *cp3 = cp2 + w + toskew;
+    int32_t incr = 3 * w + 4 * toskew;
 
-    (void) y;
+    (void)y;
     /* adjust fromskew */
-    fromskew = (fromskew / 4) * (4*2+2);
-    if ((h & 3) == 0 && (w & 3) == 0) {				        
-        for (; h >= 4; h -= 4) {
-            x = w>>2;
-            do {
-                int32 Cb = pp[16];
-                int32 Cr = pp[17];
+    fromskew = (fromskew / 4) * (4 * 2 + 2);
+    if ((h & 3) == 0 && (w & 3) == 0)
+    {
+        for (; h >= 4; h -= 4)
+        {
+            x = w >> 2;
+            do
+            {
+                int32_t Cb = pp[16];
+                int32_t Cr = pp[17];
 
-                YCbCrtoRGB(cp [0], pp[ 0]);
-                YCbCrtoRGB(cp [1], pp[ 1]);
-                YCbCrtoRGB(cp [2], pp[ 2]);
-                YCbCrtoRGB(cp [3], pp[ 3]);
-                YCbCrtoRGB(cp1[0], pp[ 4]);
-                YCbCrtoRGB(cp1[1], pp[ 5]);
-                YCbCrtoRGB(cp1[2], pp[ 6]);
-                YCbCrtoRGB(cp1[3], pp[ 7]);
-                YCbCrtoRGB(cp2[0], pp[ 8]);
-                YCbCrtoRGB(cp2[1], pp[ 9]);
+                YCbCrtoRGB(cp[0], pp[0]);
+                YCbCrtoRGB(cp[1], pp[1]);
+                YCbCrtoRGB(cp[2], pp[2]);
+                YCbCrtoRGB(cp[3], pp[3]);
+                YCbCrtoRGB(cp1[0], pp[4]);
+                YCbCrtoRGB(cp1[1], pp[5]);
+                YCbCrtoRGB(cp1[2], pp[6]);
+                YCbCrtoRGB(cp1[3], pp[7]);
+                YCbCrtoRGB(cp2[0], pp[8]);
+                YCbCrtoRGB(cp2[1], pp[9]);
                 YCbCrtoRGB(cp2[2], pp[10]);
                 YCbCrtoRGB(cp2[3], pp[11]);
                 YCbCrtoRGB(cp3[0], pp[12]);
@@ -1954,47 +2138,80 @@
             cp3 += incr;
             pp += fromskew;
         }
-    } else {
-        while (h > 0) {
-            for (x = w; x > 0;) {
-                int32 Cb = pp[16];
-                int32 Cr = pp[17];
-                switch (x) {
-                default:
-                    switch (h) {
-                    default: YCbCrtoRGB(cp3[3], pp[15]); /* FALLTHROUGH */
-                    case 3:  YCbCrtoRGB(cp2[3], pp[11]); /* FALLTHROUGH */
-                    case 2:  YCbCrtoRGB(cp1[3], pp[ 7]); /* FALLTHROUGH */
-                    case 1:  YCbCrtoRGB(cp [3], pp[ 3]); /* FALLTHROUGH */
-                    }                                    /* FALLTHROUGH */
-                case 3:
-                    switch (h) {
-                    default: YCbCrtoRGB(cp3[2], pp[14]); /* FALLTHROUGH */
-                    case 3:  YCbCrtoRGB(cp2[2], pp[10]); /* FALLTHROUGH */
-                    case 2:  YCbCrtoRGB(cp1[2], pp[ 6]); /* FALLTHROUGH */
-                    case 1:  YCbCrtoRGB(cp [2], pp[ 2]); /* FALLTHROUGH */
-                    }                                    /* FALLTHROUGH */
-                case 2:
-                    switch (h) {
-                    default: YCbCrtoRGB(cp3[1], pp[13]); /* FALLTHROUGH */
-                    case 3:  YCbCrtoRGB(cp2[1], pp[ 9]); /* FALLTHROUGH */
-                    case 2:  YCbCrtoRGB(cp1[1], pp[ 5]); /* FALLTHROUGH */
-                    case 1:  YCbCrtoRGB(cp [1], pp[ 1]); /* FALLTHROUGH */
-                    }                                    /* FALLTHROUGH */
-                case 1:
-                    switch (h) {
-                    default: YCbCrtoRGB(cp3[0], pp[12]); /* FALLTHROUGH */
-                    case 3:  YCbCrtoRGB(cp2[0], pp[ 8]); /* FALLTHROUGH */
-                    case 2:  YCbCrtoRGB(cp1[0], pp[ 4]); /* FALLTHROUGH */
-                    case 1:  YCbCrtoRGB(cp [0], pp[ 0]); /* FALLTHROUGH */
-                    }                                    /* FALLTHROUGH */
+    }
+    else
+    {
+        while (h > 0)
+        {
+            for (x = w; x > 0;)
+            {
+                int32_t Cb = pp[16];
+                int32_t Cr = pp[17];
+                switch (x)
+                {
+                    default:
+                        switch (h)
+                        {
+                            default:
+                                YCbCrtoRGB(cp3[3], pp[15]); /* FALLTHROUGH */
+                            case 3:
+                                YCbCrtoRGB(cp2[3], pp[11]); /* FALLTHROUGH */
+                            case 2:
+                                YCbCrtoRGB(cp1[3], pp[7]); /* FALLTHROUGH */
+                            case 1:
+                                YCbCrtoRGB(cp[3], pp[3]); /* FALLTHROUGH */
+                        }                                 /* FALLTHROUGH */
+                    case 3:
+                        switch (h)
+                        {
+                            default:
+                                YCbCrtoRGB(cp3[2], pp[14]); /* FALLTHROUGH */
+                            case 3:
+                                YCbCrtoRGB(cp2[2], pp[10]); /* FALLTHROUGH */
+                            case 2:
+                                YCbCrtoRGB(cp1[2], pp[6]); /* FALLTHROUGH */
+                            case 1:
+                                YCbCrtoRGB(cp[2], pp[2]); /* FALLTHROUGH */
+                        }                                 /* FALLTHROUGH */
+                    case 2:
+                        switch (h)
+                        {
+                            default:
+                                YCbCrtoRGB(cp3[1], pp[13]); /* FALLTHROUGH */
+                            case 3:
+                                YCbCrtoRGB(cp2[1], pp[9]); /* FALLTHROUGH */
+                            case 2:
+                                YCbCrtoRGB(cp1[1], pp[5]); /* FALLTHROUGH */
+                            case 1:
+                                YCbCrtoRGB(cp[1], pp[1]); /* FALLTHROUGH */
+                        }                                 /* FALLTHROUGH */
+                    case 1:
+                        switch (h)
+                        {
+                            default:
+                                YCbCrtoRGB(cp3[0], pp[12]); /* FALLTHROUGH */
+                            case 3:
+                                YCbCrtoRGB(cp2[0], pp[8]); /* FALLTHROUGH */
+                            case 2:
+                                YCbCrtoRGB(cp1[0], pp[4]); /* FALLTHROUGH */
+                            case 1:
+                                YCbCrtoRGB(cp[0], pp[0]); /* FALLTHROUGH */
+                        }                                 /* FALLTHROUGH */
                 }
-                if (x < 4) {
-                    cp += x; cp1 += x; cp2 += x; cp3 += x;
+                if (x < 4)
+                {
+                    cp += x;
+                    cp1 += x;
+                    cp2 += x;
+                    cp3 += x;
                     x = 0;
                 }
-                else {
-                    cp += 4; cp1 += 4; cp2 += 4; cp3 += 4;
+                else
+                {
+                    cp += 4;
+                    cp1 += 4;
+                    cp2 += 4;
+                    cp3 += 4;
                     x -= 4;
                 }
                 pp += 18;
@@ -2016,27 +2233,30 @@
  */
 DECLAREContigPutFunc(putcontig8bitYCbCr42tile)
 {
-    uint32* cp1 = cp+w+toskew;
-    int32 incr = 2*toskew+w;
+    uint32_t *cp1 = cp + w + toskew;
+    int32_t incr = 2 * toskew + w;
 
-    (void) y;
-    fromskew = (fromskew / 4) * (4*2+2);
-    if ((w & 3) == 0 && (h & 1) == 0) {
-        for (; h >= 2; h -= 2) {
-            x = w>>2;
-            do {
-                int32 Cb = pp[8];
-                int32 Cr = pp[9];
-                
-                YCbCrtoRGB(cp [0], pp[0]);
-                YCbCrtoRGB(cp [1], pp[1]);
-                YCbCrtoRGB(cp [2], pp[2]);
-                YCbCrtoRGB(cp [3], pp[3]);
+    (void)y;
+    fromskew = (fromskew / 4) * (4 * 2 + 2);
+    if ((w & 3) == 0 && (h & 1) == 0)
+    {
+        for (; h >= 2; h -= 2)
+        {
+            x = w >> 2;
+            do
+            {
+                int32_t Cb = pp[8];
+                int32_t Cr = pp[9];
+
+                YCbCrtoRGB(cp[0], pp[0]);
+                YCbCrtoRGB(cp[1], pp[1]);
+                YCbCrtoRGB(cp[2], pp[2]);
+                YCbCrtoRGB(cp[3], pp[3]);
                 YCbCrtoRGB(cp1[0], pp[4]);
                 YCbCrtoRGB(cp1[1], pp[5]);
                 YCbCrtoRGB(cp1[2], pp[6]);
                 YCbCrtoRGB(cp1[3], pp[7]);
-                
+
                 cp += 4;
                 cp1 += 4;
                 pp += 10;
@@ -2045,39 +2265,60 @@
             cp1 += incr;
             pp += fromskew;
         }
-    } else {
-        while (h > 0) {
-            for (x = w; x > 0;) {
-                int32 Cb = pp[8];
-                int32 Cr = pp[9];
-                switch (x) {
-                default:
-                    switch (h) {
-                    default: YCbCrtoRGB(cp1[3], pp[ 7]); /* FALLTHROUGH */
-                    case 1:  YCbCrtoRGB(cp [3], pp[ 3]); /* FALLTHROUGH */
-                    }                                    /* FALLTHROUGH */
-                case 3:
-                    switch (h) {
-                    default: YCbCrtoRGB(cp1[2], pp[ 6]); /* FALLTHROUGH */
-                    case 1:  YCbCrtoRGB(cp [2], pp[ 2]); /* FALLTHROUGH */
-                    }                                    /* FALLTHROUGH */
-                case 2:
-                    switch (h) {
-                    default: YCbCrtoRGB(cp1[1], pp[ 5]); /* FALLTHROUGH */
-                    case 1:  YCbCrtoRGB(cp [1], pp[ 1]); /* FALLTHROUGH */
-                    }                                    /* FALLTHROUGH */
-                case 1:
-                    switch (h) {
-                    default: YCbCrtoRGB(cp1[0], pp[ 4]); /* FALLTHROUGH */
-                    case 1:  YCbCrtoRGB(cp [0], pp[ 0]); /* FALLTHROUGH */
-                    }                                    /* FALLTHROUGH */
+    }
+    else
+    {
+        while (h > 0)
+        {
+            for (x = w; x > 0;)
+            {
+                int32_t Cb = pp[8];
+                int32_t Cr = pp[9];
+                switch (x)
+                {
+                    default:
+                        switch (h)
+                        {
+                            default:
+                                YCbCrtoRGB(cp1[3], pp[7]); /* FALLTHROUGH */
+                            case 1:
+                                YCbCrtoRGB(cp[3], pp[3]); /* FALLTHROUGH */
+                        }                                 /* FALLTHROUGH */
+                    case 3:
+                        switch (h)
+                        {
+                            default:
+                                YCbCrtoRGB(cp1[2], pp[6]); /* FALLTHROUGH */
+                            case 1:
+                                YCbCrtoRGB(cp[2], pp[2]); /* FALLTHROUGH */
+                        }                                 /* FALLTHROUGH */
+                    case 2:
+                        switch (h)
+                        {
+                            default:
+                                YCbCrtoRGB(cp1[1], pp[5]); /* FALLTHROUGH */
+                            case 1:
+                                YCbCrtoRGB(cp[1], pp[1]); /* FALLTHROUGH */
+                        }                                 /* FALLTHROUGH */
+                    case 1:
+                        switch (h)
+                        {
+                            default:
+                                YCbCrtoRGB(cp1[0], pp[4]); /* FALLTHROUGH */
+                            case 1:
+                                YCbCrtoRGB(cp[0], pp[0]); /* FALLTHROUGH */
+                        }                                 /* FALLTHROUGH */
                 }
-                if (x < 4) {
-                    cp += x; cp1 += x;
+                if (x < 4)
+                {
+                    cp += x;
+                    cp1 += x;
                     x = 0;
                 }
-                else {
-                    cp += 4; cp1 += 4;
+                else
+                {
+                    cp += 4;
+                    cp1 += 4;
                     x -= 4;
                 }
                 pp += 10;
@@ -2097,44 +2338,50 @@
  */
 DECLAREContigPutFunc(putcontig8bitYCbCr41tile)
 {
-    (void) y;
-    fromskew = (fromskew / 4) * (4*1+2);
-    do {
-	x = w>>2;
-	while(x>0) {
-	    int32 Cb = pp[4];
-	    int32 Cr = pp[5];
-
-	    YCbCrtoRGB(cp [0], pp[0]);
-	    YCbCrtoRGB(cp [1], pp[1]);
-	    YCbCrtoRGB(cp [2], pp[2]);
-	    YCbCrtoRGB(cp [3], pp[3]);
-
-	    cp += 4;
-	    pp += 6;
-		x--;
-	}
-
-        if( (w&3) != 0 )
+    (void)y;
+    fromskew = (fromskew / 4) * (4 * 1 + 2);
+    do
+    {
+        x = w >> 2;
+        while (x > 0)
         {
-	    int32 Cb = pp[4];
-	    int32 Cr = pp[5];
+            int32_t Cb = pp[4];
+            int32_t Cr = pp[5];
 
-            switch( (w&3) ) {
-              case 3: YCbCrtoRGB(cp [2], pp[2]); /*-fallthrough*/
-              case 2: YCbCrtoRGB(cp [1], pp[1]); /*-fallthrough*/
-              case 1: YCbCrtoRGB(cp [0], pp[0]); /*-fallthrough*/
-              case 0: break;
+            YCbCrtoRGB(cp[0], pp[0]);
+            YCbCrtoRGB(cp[1], pp[1]);
+            YCbCrtoRGB(cp[2], pp[2]);
+            YCbCrtoRGB(cp[3], pp[3]);
+
+            cp += 4;
+            pp += 6;
+            x--;
+        }
+
+        if ((w & 3) != 0)
+        {
+            int32_t Cb = pp[4];
+            int32_t Cr = pp[5];
+
+            switch ((w & 3))
+            {
+                case 3:
+                    YCbCrtoRGB(cp[2], pp[2]); /*-fallthrough*/
+                case 2:
+                    YCbCrtoRGB(cp[1], pp[1]); /*-fallthrough*/
+                case 1:
+                    YCbCrtoRGB(cp[0], pp[0]); /*-fallthrough*/
+                case 0:
+                    break;
             }
 
-            cp += (w&3);
+            cp += (w & 3);
             pp += 6;
         }
 
-	cp += toskew;
-	pp += fromskew;
+        cp += toskew;
+        pp += fromskew;
     } while (--h);
-
 }
 
 /*
@@ -2142,57 +2389,63 @@
  */
 DECLAREContigPutFunc(putcontig8bitYCbCr22tile)
 {
-	uint32* cp2;
-	int32 incr = 2*toskew+w;
-	(void) y;
-	fromskew = (fromskew / 2) * (2*2+2);
-	cp2 = cp+w+toskew;
-	while (h>=2) {
-		x = w;
-		while (x>=2) {
-			uint32 Cb = pp[4];
-			uint32 Cr = pp[5];
-			YCbCrtoRGB(cp[0], pp[0]);
-			YCbCrtoRGB(cp[1], pp[1]);
-			YCbCrtoRGB(cp2[0], pp[2]);
-			YCbCrtoRGB(cp2[1], pp[3]);
-			cp += 2;
-			cp2 += 2;
-			pp += 6;
-			x -= 2;
-		}
-		if (x==1) {
-			uint32 Cb = pp[4];
-			uint32 Cr = pp[5];
-			YCbCrtoRGB(cp[0], pp[0]);
-			YCbCrtoRGB(cp2[0], pp[2]);
-			cp ++ ;
-			cp2 ++ ;
-			pp += 6;
-		}
-		cp += incr;
-		cp2 += incr;
-		pp += fromskew;
-		h-=2;
-	}
-	if (h==1) {
-		x = w;
-		while (x>=2) {
-			uint32 Cb = pp[4];
-			uint32 Cr = pp[5];
-			YCbCrtoRGB(cp[0], pp[0]);
-			YCbCrtoRGB(cp[1], pp[1]);
-			cp += 2;
-			cp2 += 2;
-			pp += 6;
-			x -= 2;
-		}
-		if (x==1) {
-			uint32 Cb = pp[4];
-			uint32 Cr = pp[5];
-			YCbCrtoRGB(cp[0], pp[0]);
-		}
-	}
+    uint32_t *cp2;
+    int32_t incr = 2 * toskew + w;
+    (void)y;
+    fromskew = (fromskew / 2) * (2 * 2 + 2);
+    cp2 = cp + w + toskew;
+    while (h >= 2)
+    {
+        x = w;
+        while (x >= 2)
+        {
+            uint32_t Cb = pp[4];
+            uint32_t Cr = pp[5];
+            YCbCrtoRGB(cp[0], pp[0]);
+            YCbCrtoRGB(cp[1], pp[1]);
+            YCbCrtoRGB(cp2[0], pp[2]);
+            YCbCrtoRGB(cp2[1], pp[3]);
+            cp += 2;
+            cp2 += 2;
+            pp += 6;
+            x -= 2;
+        }
+        if (x == 1)
+        {
+            uint32_t Cb = pp[4];
+            uint32_t Cr = pp[5];
+            YCbCrtoRGB(cp[0], pp[0]);
+            YCbCrtoRGB(cp2[0], pp[2]);
+            cp++;
+            cp2++;
+            pp += 6;
+        }
+        cp += incr;
+        cp2 += incr;
+        pp += fromskew;
+        h -= 2;
+    }
+    if (h == 1)
+    {
+        x = w;
+        while (x >= 2)
+        {
+            uint32_t Cb = pp[4];
+            uint32_t Cr = pp[5];
+            YCbCrtoRGB(cp[0], pp[0]);
+            YCbCrtoRGB(cp[1], pp[1]);
+            cp += 2;
+            cp2 += 2;
+            pp += 6;
+            x -= 2;
+        }
+        if (x == 1)
+        {
+            uint32_t Cb = pp[4];
+            uint32_t Cr = pp[5];
+            YCbCrtoRGB(cp[0], pp[0]);
+        }
+    }
 }
 
 /*
@@ -2200,36 +2453,38 @@
  */
 DECLAREContigPutFunc(putcontig8bitYCbCr21tile)
 {
-	(void) y;
-	fromskew = (fromskew / 2) * (2*1+2);
-	do {
-		x = w>>1;
-		while(x>0) {
-			int32 Cb = pp[2];
-			int32 Cr = pp[3];
+    (void)y;
+    fromskew = (fromskew / 2) * (2 * 1 + 2);
+    do
+    {
+        x = w >> 1;
+        while (x > 0)
+        {
+            int32_t Cb = pp[2];
+            int32_t Cr = pp[3];
 
-			YCbCrtoRGB(cp[0], pp[0]);
-			YCbCrtoRGB(cp[1], pp[1]);
+            YCbCrtoRGB(cp[0], pp[0]);
+            YCbCrtoRGB(cp[1], pp[1]);
 
-			cp += 2;
-			pp += 4;
-			x --;
-		}
+            cp += 2;
+            pp += 4;
+            x--;
+        }
 
-		if( (w&1) != 0 )
-		{
-			int32 Cb = pp[2];
-			int32 Cr = pp[3];
+        if ((w & 1) != 0)
+        {
+            int32_t Cb = pp[2];
+            int32_t Cr = pp[3];
 
-			YCbCrtoRGB(cp[0], pp[0]);
+            YCbCrtoRGB(cp[0], pp[0]);
 
-			cp += 1;
-			pp += 4;
-		}
+            cp += 1;
+            pp += 4;
+        }
 
-		cp += toskew;
-		pp += fromskew;
-	} while (--h);
+        cp += toskew;
+        pp += fromskew;
+    } while (--h);
 }
 
 /*
@@ -2237,37 +2492,41 @@
  */
 DECLAREContigPutFunc(putcontig8bitYCbCr12tile)
 {
-	uint32* cp2;
-	int32 incr = 2*toskew+w;
-	(void) y;
-	fromskew = (fromskew / 1) * (1 * 2 + 2);
-	cp2 = cp+w+toskew;
-	while (h>=2) {
-		x = w;
-		do {
-			uint32 Cb = pp[2];
-			uint32 Cr = pp[3];
-			YCbCrtoRGB(cp[0], pp[0]);
-			YCbCrtoRGB(cp2[0], pp[1]);
-			cp ++;
-			cp2 ++;
-			pp += 4;
-		} while (--x);
-		cp += incr;
-		cp2 += incr;
-		pp += fromskew;
-		h-=2;
-	}
-	if (h==1) {
-		x = w;
-		do {
-			uint32 Cb = pp[2];
-			uint32 Cr = pp[3];
-			YCbCrtoRGB(cp[0], pp[0]);
-			cp ++;
-			pp += 4;
-		} while (--x);
-	}
+    uint32_t *cp2;
+    int32_t incr = 2 * toskew + w;
+    (void)y;
+    fromskew = (fromskew / 1) * (1 * 2 + 2);
+    cp2 = cp + w + toskew;
+    while (h >= 2)
+    {
+        x = w;
+        do
+        {
+            uint32_t Cb = pp[2];
+            uint32_t Cr = pp[3];
+            YCbCrtoRGB(cp[0], pp[0]);
+            YCbCrtoRGB(cp2[0], pp[1]);
+            cp++;
+            cp2++;
+            pp += 4;
+        } while (--x);
+        cp += incr;
+        cp2 += incr;
+        pp += fromskew;
+        h -= 2;
+    }
+    if (h == 1)
+    {
+        x = w;
+        do
+        {
+            uint32_t Cb = pp[2];
+            uint32_t Cr = pp[3];
+            YCbCrtoRGB(cp[0], pp[0]);
+            cp++;
+            pp += 4;
+        } while (--x);
+    }
 }
 
 /*
@@ -2275,21 +2534,23 @@
  */
 DECLAREContigPutFunc(putcontig8bitYCbCr11tile)
 {
-	(void) y;
-	fromskew = (fromskew / 1) * (1 * 1 + 2);
-	do {
-		x = w; /* was x = w>>1; patched 2000/09/25 warmerda@home.com */
-		do {
-			int32 Cb = pp[1];
-			int32 Cr = pp[2];
+    (void)y;
+    fromskew = (fromskew / 1) * (1 * 1 + 2);
+    do
+    {
+        x = w; /* was x = w>>1; patched 2000/09/25 warmerda@home.com */
+        do
+        {
+            int32_t Cb = pp[1];
+            int32_t Cr = pp[2];
 
-			YCbCrtoRGB(*cp++, pp[0]);
+            YCbCrtoRGB(*cp++, pp[0]);
 
-			pp += 3;
-		} while (--x);
-		cp += toskew;
-		pp += fromskew;
-	} while (--h);
+            pp += 3;
+        } while (--x);
+        cp += toskew;
+        pp += fromskew;
+    } while (--h);
 }
 
 /*
@@ -2297,19 +2558,22 @@
  */
 DECLARESepPutFunc(putseparate8bitYCbCr11tile)
 {
-	(void) y;
-	(void) a;
-	/* TODO: naming of input vars is still off, change obfuscating declaration inside define, or resolve obfuscation */
-	for( ; h > 0; --h) {
-		x = w;
-		do {
-			uint32 dr, dg, db;
-			TIFFYCbCrtoRGB(img->ycbcr,*r++,*g++,*b++,&dr,&dg,&db);
-			*cp++ = PACK(dr,dg,db);
-		} while (--x);
-		SKEW(r, g, b, fromskew);
-		cp += toskew;
-	}
+    (void)y;
+    (void)a;
+    /* TODO: naming of input vars is still off, change obfuscating declaration
+     * inside define, or resolve obfuscation */
+    for (; h > 0; --h)
+    {
+        x = w;
+        do
+        {
+            uint32_t dr, dg, db;
+            TIFFYCbCrtoRGB(img->ycbcr, *r++, *g++, *b++, &dr, &dg, &db);
+            *cp++ = PACK(dr, dg, db);
+        } while (--x);
+        SKEW(r, g, b, fromskew);
+        cp += toskew;
+    }
 }
 #undef YCbCrtoRGB
 
@@ -2318,97 +2582,100 @@
     return f > (float)(-0x7FFFFFFF + 128) && f < (float)0x7FFFFFFF;
 }
 
-static int
-initYCbCrConversion(TIFFRGBAImage* img)
+static int initYCbCrConversion(TIFFRGBAImage *img)
 {
-	static const char module[] = "initYCbCrConversion";
+    static const char module[] = "initYCbCrConversion";
 
-	float *luma, *refBlackWhite;
+    float *luma, *refBlackWhite;
 
-	if (img->ycbcr == NULL) {
-		img->ycbcr = (TIFFYCbCrToRGB*) _TIFFmalloc(
-		    TIFFroundup_32(sizeof (TIFFYCbCrToRGB), sizeof (long))  
-		    + 4*256*sizeof (TIFFRGBValue)
-		    + 2*256*sizeof (int)
-		    + 3*256*sizeof (int32)
-		    );
-		if (img->ycbcr == NULL) {
-			TIFFErrorExt(img->tif->tif_clientdata, module,
-			    "No space for YCbCr->RGB conversion state");
-			return (0);
-		}
-	}
-
-	TIFFGetFieldDefaulted(img->tif, TIFFTAG_YCBCRCOEFFICIENTS, &luma);
-	TIFFGetFieldDefaulted(img->tif, TIFFTAG_REFERENCEBLACKWHITE,
-	    &refBlackWhite);
-
-        /* Do some validation to avoid later issues. Detect NaN for now */
-        /* and also if lumaGreen is zero since we divide by it later */
-        if( luma[0] != luma[0] ||
-            luma[1] != luma[1] ||
-            luma[1] == 0.0 ||
-            luma[2] != luma[2] )
+    if (img->ycbcr == NULL)
+    {
+        img->ycbcr = (TIFFYCbCrToRGB *)_TIFFmallocExt(
+            img->tif, TIFFroundup_32(sizeof(TIFFYCbCrToRGB), sizeof(long)) +
+                          4 * 256 * sizeof(TIFFRGBValue) +
+                          2 * 256 * sizeof(int) + 3 * 256 * sizeof(int32_t));
+        if (img->ycbcr == NULL)
         {
-            TIFFErrorExt(img->tif->tif_clientdata, module,
-                "Invalid values for YCbCrCoefficients tag");
+            TIFFErrorExtR(img->tif, module,
+                          "No space for YCbCr->RGB conversion state");
             return (0);
         }
+    }
 
-        if( !isInRefBlackWhiteRange(refBlackWhite[0]) ||
-            !isInRefBlackWhiteRange(refBlackWhite[1]) ||
-            !isInRefBlackWhiteRange(refBlackWhite[2]) ||
-            !isInRefBlackWhiteRange(refBlackWhite[3]) ||
-            !isInRefBlackWhiteRange(refBlackWhite[4]) ||
-            !isInRefBlackWhiteRange(refBlackWhite[5]) )
-        {
-            TIFFErrorExt(img->tif->tif_clientdata, module,
-                "Invalid values for ReferenceBlackWhite tag");
-            return (0);
-        }
+    TIFFGetFieldDefaulted(img->tif, TIFFTAG_YCBCRCOEFFICIENTS, &luma);
+    TIFFGetFieldDefaulted(img->tif, TIFFTAG_REFERENCEBLACKWHITE,
+                          &refBlackWhite);
 
-	if (TIFFYCbCrToRGBInit(img->ycbcr, luma, refBlackWhite) < 0)
-		return(0);
-	return (1);
+    /* Do some validation to avoid later issues. Detect NaN for now */
+    /* and also if lumaGreen is zero since we divide by it later */
+    if (luma[0] != luma[0] || luma[1] != luma[1] || luma[1] == 0.0 ||
+        luma[2] != luma[2])
+    {
+        TIFFErrorExtR(img->tif, module,
+                      "Invalid values for YCbCrCoefficients tag");
+        return (0);
+    }
+
+    if (!isInRefBlackWhiteRange(refBlackWhite[0]) ||
+        !isInRefBlackWhiteRange(refBlackWhite[1]) ||
+        !isInRefBlackWhiteRange(refBlackWhite[2]) ||
+        !isInRefBlackWhiteRange(refBlackWhite[3]) ||
+        !isInRefBlackWhiteRange(refBlackWhite[4]) ||
+        !isInRefBlackWhiteRange(refBlackWhite[5]))
+    {
+        TIFFErrorExtR(img->tif, module,
+                      "Invalid values for ReferenceBlackWhite tag");
+        return (0);
+    }
+
+    if (TIFFYCbCrToRGBInit(img->ycbcr, luma, refBlackWhite) < 0)
+        return (0);
+    return (1);
 }
 
-static tileContigRoutine
-initCIELabConversion(TIFFRGBAImage* img)
+static tileContigRoutine initCIELabConversion(TIFFRGBAImage *img)
 {
-	static const char module[] = "initCIELabConversion";
+    static const char module[] = "initCIELabConversion";
 
-	float   *whitePoint;
-	float   refWhite[3];
+    float *whitePoint;
+    float refWhite[3];
 
-	TIFFGetFieldDefaulted(img->tif, TIFFTAG_WHITEPOINT, &whitePoint);
-	if (whitePoint[1] == 0.0f ) {
-		TIFFErrorExt(img->tif->tif_clientdata, module,
-		    "Invalid value for WhitePoint tag.");
-		return NULL;
+    TIFFGetFieldDefaulted(img->tif, TIFFTAG_WHITEPOINT, &whitePoint);
+    if (whitePoint[1] == 0.0f)
+    {
+        TIFFErrorExtR(img->tif, module, "Invalid value for WhitePoint tag.");
+        return NULL;
+    }
+
+    if (!img->cielab)
+    {
+        img->cielab = (TIFFCIELabToRGB *)_TIFFmallocExt(
+            img->tif, sizeof(TIFFCIELabToRGB));
+        if (!img->cielab)
+        {
+            TIFFErrorExtR(img->tif, module,
+                          "No space for CIE L*a*b*->RGB conversion state.");
+            return NULL;
         }
+    }
 
-	if (!img->cielab) {
-		img->cielab = (TIFFCIELabToRGB *)
-			_TIFFmalloc(sizeof(TIFFCIELabToRGB));
-		if (!img->cielab) {
-			TIFFErrorExt(img->tif->tif_clientdata, module,
-			    "No space for CIE L*a*b*->RGB conversion state.");
-			return NULL;
-		}
-	}
+    refWhite[1] = 100.0F;
+    refWhite[0] = whitePoint[0] / whitePoint[1] * refWhite[1];
+    refWhite[2] =
+        (1.0F - whitePoint[0] - whitePoint[1]) / whitePoint[1] * refWhite[1];
+    if (TIFFCIELabToRGBInit(img->cielab, &display_sRGB, refWhite) < 0)
+    {
+        TIFFErrorExtR(img->tif, module,
+                      "Failed to initialize CIE L*a*b*->RGB conversion state.");
+        _TIFFfreeExt(img->tif, img->cielab);
+        return NULL;
+    }
 
-	refWhite[1] = 100.0F;
-	refWhite[0] = whitePoint[0] / whitePoint[1] * refWhite[1];
-	refWhite[2] = (1.0F - whitePoint[0] - whitePoint[1])
-		      / whitePoint[1] * refWhite[1];
-	if (TIFFCIELabToRGBInit(img->cielab, &display_sRGB, refWhite) < 0) {
-		TIFFErrorExt(img->tif->tif_clientdata, module,
-		    "Failed to initialize CIE L*a*b*->RGB conversion state.");
-		_TIFFfree(img->cielab);
-		return NULL;
-	}
-
-	return putcontig8bitCIELab;
+    if (img->bitspersample == 8)
+        return putcontig8bitCIELab8;
+    else if (img->bitspersample == 16)
+        return putcontig8bitCIELab16;
+    return NULL;
 }
 
 /*
@@ -2418,56 +2685,62 @@
  * pixel values simply by indexing into the table with one
  * number.
  */
-static int
-makebwmap(TIFFRGBAImage* img)
+static int makebwmap(TIFFRGBAImage *img)
 {
-    TIFFRGBValue* Map = img->Map;
+    TIFFRGBValue *Map = img->Map;
     int bitspersample = img->bitspersample;
     int nsamples = 8 / bitspersample;
     int i;
-    uint32* p;
+    uint32_t *p;
 
-    if( nsamples == 0 )
+    if (nsamples == 0)
         nsamples = 1;
 
-    img->BWmap = (uint32**) _TIFFmalloc(
-	256*sizeof (uint32 *)+(256*nsamples*sizeof(uint32)));
-    if (img->BWmap == NULL) {
-		TIFFErrorExt(img->tif->tif_clientdata, TIFFFileName(img->tif), "No space for B&W mapping table");
-		return (0);
+    img->BWmap = (uint32_t **)_TIFFmallocExt(
+        img->tif,
+        256 * sizeof(uint32_t *) + (256 * nsamples * sizeof(uint32_t)));
+    if (img->BWmap == NULL)
+    {
+        TIFFErrorExtR(img->tif, TIFFFileName(img->tif),
+                      "No space for B&W mapping table");
+        return (0);
     }
-    p = (uint32*)(img->BWmap + 256);
-    for (i = 0; i < 256; i++) {
-	TIFFRGBValue c;
-	img->BWmap[i] = p;
-	switch (bitspersample) {
-#define	GREY(x)	c = Map[x]; *p++ = PACK(c,c,c);
-	case 1:
-	    GREY(i>>7);
-	    GREY((i>>6)&1);
-	    GREY((i>>5)&1);
-	    GREY((i>>4)&1);
-	    GREY((i>>3)&1);
-	    GREY((i>>2)&1);
-	    GREY((i>>1)&1);
-	    GREY(i&1);
-	    break;
-	case 2:
-	    GREY(i>>6);
-	    GREY((i>>4)&3);
-	    GREY((i>>2)&3);
-	    GREY(i&3);
-	    break;
-	case 4:
-	    GREY(i>>4);
-	    GREY(i&0xf);
-	    break;
-	case 8:
-        case 16:
-	    GREY(i);
-	    break;
-	}
-#undef	GREY
+    p = (uint32_t *)(img->BWmap + 256);
+    for (i = 0; i < 256; i++)
+    {
+        TIFFRGBValue c;
+        img->BWmap[i] = p;
+        switch (bitspersample)
+        {
+#define GREY(x)                                                                \
+    c = Map[x];                                                                \
+    *p++ = PACK(c, c, c);
+            case 1:
+                GREY(i >> 7);
+                GREY((i >> 6) & 1);
+                GREY((i >> 5) & 1);
+                GREY((i >> 4) & 1);
+                GREY((i >> 3) & 1);
+                GREY((i >> 2) & 1);
+                GREY((i >> 1) & 1);
+                GREY(i & 1);
+                break;
+            case 2:
+                GREY(i >> 6);
+                GREY((i >> 4) & 3);
+                GREY((i >> 2) & 3);
+                GREY(i & 3);
+                break;
+            case 4:
+                GREY(i >> 4);
+                GREY(i & 0xf);
+                break;
+            case 8:
+            case 16:
+                GREY(i);
+                break;
+        }
+#undef GREY
     }
     return (1);
 }
@@ -2476,75 +2749,79 @@
  * Construct a mapping table to convert from the range
  * of the data samples to [0,255] --for display.  This
  * process also handles inverting B&W images when needed.
- */ 
-static int
-setupMap(TIFFRGBAImage* img)
+ */
+static int setupMap(TIFFRGBAImage *img)
 {
-    int32 x, range;
+    int32_t x, range;
 
-    range = (int32)((1L<<img->bitspersample)-1);
-    
+    range = (int32_t)((1L << img->bitspersample) - 1);
+
     /* treat 16 bit the same as eight bit */
-    if( img->bitspersample == 16 )
-        range = (int32) 255;
+    if (img->bitspersample == 16)
+        range = (int32_t)255;
 
-    img->Map = (TIFFRGBValue*) _TIFFmalloc((range+1) * sizeof (TIFFRGBValue));
-    if (img->Map == NULL) {
-		TIFFErrorExt(img->tif->tif_clientdata, TIFFFileName(img->tif),
-			"No space for photometric conversion table");
-		return (0);
+    img->Map = (TIFFRGBValue *)_TIFFmallocExt(
+        img->tif, (range + 1) * sizeof(TIFFRGBValue));
+    if (img->Map == NULL)
+    {
+        TIFFErrorExtR(img->tif, TIFFFileName(img->tif),
+                      "No space for photometric conversion table");
+        return (0);
     }
-    if (img->photometric == PHOTOMETRIC_MINISWHITE) {
-	for (x = 0; x <= range; x++)
-	    img->Map[x] = (TIFFRGBValue) (((range - x) * 255) / range);
-    } else {
-	for (x = 0; x <= range; x++)
-	    img->Map[x] = (TIFFRGBValue) ((x * 255) / range);
+    if (img->photometric == PHOTOMETRIC_MINISWHITE)
+    {
+        for (x = 0; x <= range; x++)
+            img->Map[x] = (TIFFRGBValue)(((range - x) * 255) / range);
+    }
+    else
+    {
+        for (x = 0; x <= range; x++)
+            img->Map[x] = (TIFFRGBValue)((x * 255) / range);
     }
     if (img->bitspersample <= 16 &&
-	(img->photometric == PHOTOMETRIC_MINISBLACK ||
-	 img->photometric == PHOTOMETRIC_MINISWHITE)) {
-	/*
-	 * Use photometric mapping table to construct
-	 * unpacking tables for samples <= 8 bits.
-	 */
-	if (!makebwmap(img))
-	    return (0);
-	/* no longer need Map, free it */
-	_TIFFfree(img->Map);
-	img->Map = NULL;
+        (img->photometric == PHOTOMETRIC_MINISBLACK ||
+         img->photometric == PHOTOMETRIC_MINISWHITE))
+    {
+        /*
+         * Use photometric mapping table to construct
+         * unpacking tables for samples <= 8 bits.
+         */
+        if (!makebwmap(img))
+            return (0);
+        /* no longer need Map, free it */
+        _TIFFfreeExt(img->tif, img->Map);
+        img->Map = NULL;
     }
     return (1);
 }
 
-static int
-checkcmap(TIFFRGBAImage* img)
+static int checkcmap(TIFFRGBAImage *img)
 {
-    uint16* r = img->redcmap;
-    uint16* g = img->greencmap;
-    uint16* b = img->bluecmap;
-    long n = 1L<<img->bitspersample;
+    uint16_t *r = img->redcmap;
+    uint16_t *g = img->greencmap;
+    uint16_t *b = img->bluecmap;
+    long n = 1L << img->bitspersample;
 
     while (n-- > 0)
-	if (*r++ >= 256 || *g++ >= 256 || *b++ >= 256)
-	    return (16);
+        if (*r++ >= 256 || *g++ >= 256 || *b++ >= 256)
+            return (16);
     return (8);
 }
 
-static void
-cvtcmap(TIFFRGBAImage* img)
+static void cvtcmap(TIFFRGBAImage *img)
 {
-    uint16* r = img->redcmap;
-    uint16* g = img->greencmap;
-    uint16* b = img->bluecmap;
+    uint16_t *r = img->redcmap;
+    uint16_t *g = img->greencmap;
+    uint16_t *b = img->bluecmap;
     long i;
 
-    for (i = (1L<<img->bitspersample)-1; i >= 0; i--) {
-#define	CVT(x)		((uint16)((x)>>8))
-	r[i] = CVT(r[i]);
-	g[i] = CVT(g[i]);
-	b[i] = CVT(b[i]);
-#undef	CVT
+    for (i = (1L << img->bitspersample) - 1; i >= 0; i--)
+    {
+#define CVT(x) ((uint16_t)((x) >> 8))
+        r[i] = CVT(r[i]);
+        g[i] = CVT(g[i]);
+        b[i] = CVT(b[i]);
+#undef CVT
     }
 }
 
@@ -2555,93 +2832,100 @@
  * pixel values simply by indexing into the table with one
  * number.
  */
-static int
-makecmap(TIFFRGBAImage* img)
+static int makecmap(TIFFRGBAImage *img)
 {
     int bitspersample = img->bitspersample;
     int nsamples = 8 / bitspersample;
-    uint16* r = img->redcmap;
-    uint16* g = img->greencmap;
-    uint16* b = img->bluecmap;
-    uint32 *p;
+    uint16_t *r = img->redcmap;
+    uint16_t *g = img->greencmap;
+    uint16_t *b = img->bluecmap;
+    uint32_t *p;
     int i;
 
-    img->PALmap = (uint32**) _TIFFmalloc(
-	256*sizeof (uint32 *)+(256*nsamples*sizeof(uint32)));
-    if (img->PALmap == NULL) {
-		TIFFErrorExt(img->tif->tif_clientdata, TIFFFileName(img->tif), "No space for Palette mapping table");
-		return (0);
-	}
-    p = (uint32*)(img->PALmap + 256);
-    for (i = 0; i < 256; i++) {
-	TIFFRGBValue c;
-	img->PALmap[i] = p;
-#define	CMAP(x)	c = (TIFFRGBValue) x; *p++ = PACK(r[c]&0xff, g[c]&0xff, b[c]&0xff);
-	switch (bitspersample) {
-	case 1:
-	    CMAP(i>>7);
-	    CMAP((i>>6)&1);
-	    CMAP((i>>5)&1);
-	    CMAP((i>>4)&1);
-	    CMAP((i>>3)&1);
-	    CMAP((i>>2)&1);
-	    CMAP((i>>1)&1);
-	    CMAP(i&1);
-	    break;
-	case 2:
-	    CMAP(i>>6);
-	    CMAP((i>>4)&3);
-	    CMAP((i>>2)&3);
-	    CMAP(i&3);
-	    break;
-	case 4:
-	    CMAP(i>>4);
-	    CMAP(i&0xf);
-	    break;
-	case 8:
-	    CMAP(i);
-	    break;
-	}
+    img->PALmap = (uint32_t **)_TIFFmallocExt(
+        img->tif,
+        256 * sizeof(uint32_t *) + (256 * nsamples * sizeof(uint32_t)));
+    if (img->PALmap == NULL)
+    {
+        TIFFErrorExtR(img->tif, TIFFFileName(img->tif),
+                      "No space for Palette mapping table");
+        return (0);
+    }
+    p = (uint32_t *)(img->PALmap + 256);
+    for (i = 0; i < 256; i++)
+    {
+        TIFFRGBValue c;
+        img->PALmap[i] = p;
+#define CMAP(x)                                                                \
+    c = (TIFFRGBValue)x;                                                       \
+    *p++ = PACK(r[c] & 0xff, g[c] & 0xff, b[c] & 0xff);
+        switch (bitspersample)
+        {
+            case 1:
+                CMAP(i >> 7);
+                CMAP((i >> 6) & 1);
+                CMAP((i >> 5) & 1);
+                CMAP((i >> 4) & 1);
+                CMAP((i >> 3) & 1);
+                CMAP((i >> 2) & 1);
+                CMAP((i >> 1) & 1);
+                CMAP(i & 1);
+                break;
+            case 2:
+                CMAP(i >> 6);
+                CMAP((i >> 4) & 3);
+                CMAP((i >> 2) & 3);
+                CMAP(i & 3);
+                break;
+            case 4:
+                CMAP(i >> 4);
+                CMAP(i & 0xf);
+                break;
+            case 8:
+                CMAP(i);
+                break;
+        }
 #undef CMAP
     }
     return (1);
 }
 
-/* 
+/*
  * Construct any mapping table used
  * by the associated put routine.
  */
-static int
-buildMap(TIFFRGBAImage* img)
+static int buildMap(TIFFRGBAImage *img)
 {
-    switch (img->photometric) {
-    case PHOTOMETRIC_RGB:
-    case PHOTOMETRIC_YCBCR:
-    case PHOTOMETRIC_SEPARATED:
-	if (img->bitspersample == 8)
-	    break;
-	/* fall through... */
-    case PHOTOMETRIC_MINISBLACK:
-    case PHOTOMETRIC_MINISWHITE:
-	if (!setupMap(img))
-	    return (0);
-	break;
-    case PHOTOMETRIC_PALETTE:
-	/*
-	 * Convert 16-bit colormap to 8-bit (unless it looks
-	 * like an old-style 8-bit colormap).
-	 */
-	if (checkcmap(img) == 16)
-	    cvtcmap(img);
-	else
-	    TIFFWarningExt(img->tif->tif_clientdata, TIFFFileName(img->tif), "Assuming 8-bit colormap");
-	/*
-	 * Use mapping table and colormap to construct
-	 * unpacking tables for samples < 8 bits.
-	 */
-	if (img->bitspersample <= 8 && !makecmap(img))
-	    return (0);
-	break;
+    switch (img->photometric)
+    {
+        case PHOTOMETRIC_RGB:
+        case PHOTOMETRIC_YCBCR:
+        case PHOTOMETRIC_SEPARATED:
+            if (img->bitspersample == 8)
+                break;
+            /* fall through... */
+        case PHOTOMETRIC_MINISBLACK:
+        case PHOTOMETRIC_MINISWHITE:
+            if (!setupMap(img))
+                return (0);
+            break;
+        case PHOTOMETRIC_PALETTE:
+            /*
+             * Convert 16-bit colormap to 8-bit (unless it looks
+             * like an old-style 8-bit colormap).
+             */
+            if (checkcmap(img) == 16)
+                cvtcmap(img);
+            else
+                TIFFWarningExtR(img->tif, TIFFFileName(img->tif),
+                                "Assuming 8-bit colormap");
+            /*
+             * Use mapping table and colormap to construct
+             * unpacking tables for samples < 8 bits.
+             */
+            if (img->bitspersample <= 8 && !makecmap(img))
+                return (0);
+            break;
     }
     return (1);
 }
@@ -2649,153 +2933,162 @@
 /*
  * Select the appropriate conversion routine for packed data.
  */
-static int
-PickContigCase(TIFFRGBAImage* img)
+static int PickContigCase(TIFFRGBAImage *img)
 {
-	img->get = TIFFIsTiled(img->tif) ? gtTileContig : gtStripContig;
-	img->put.contig = NULL;
-	switch (img->photometric) {
-		case PHOTOMETRIC_RGB:
-			switch (img->bitspersample) {
-				case 8:
-					if (img->alpha == EXTRASAMPLE_ASSOCALPHA &&
-						img->samplesperpixel >= 4)
-						img->put.contig = putRGBAAcontig8bittile;
-					else if (img->alpha == EXTRASAMPLE_UNASSALPHA &&
-							 img->samplesperpixel >= 4)
-					{
-						if (BuildMapUaToAa(img))
-							img->put.contig = putRGBUAcontig8bittile;
-					}
-					else if( img->samplesperpixel >= 3 )
-						img->put.contig = putRGBcontig8bittile;
-					break;
-				case 16:
-					if (img->alpha == EXTRASAMPLE_ASSOCALPHA &&
-						img->samplesperpixel >=4 )
-					{
-						if (BuildMapBitdepth16To8(img))
-							img->put.contig = putRGBAAcontig16bittile;
-					}
-					else if (img->alpha == EXTRASAMPLE_UNASSALPHA &&
-							 img->samplesperpixel >=4 )
-					{
-						if (BuildMapBitdepth16To8(img) &&
-						    BuildMapUaToAa(img))
-							img->put.contig = putRGBUAcontig16bittile;
-					}
-					else if( img->samplesperpixel >=3 )
-					{
-						if (BuildMapBitdepth16To8(img))
-							img->put.contig = putRGBcontig16bittile;
-					}
-					break;
-			}
-			break;
-		case PHOTOMETRIC_SEPARATED:
-			if (img->samplesperpixel >=4 && buildMap(img)) {
-				if (img->bitspersample == 8) {
-					if (!img->Map)
-						img->put.contig = putRGBcontig8bitCMYKtile;
-					else
-						img->put.contig = putRGBcontig8bitCMYKMaptile;
-				}
-			}
-			break;
-		case PHOTOMETRIC_PALETTE:
-			if (buildMap(img)) {
-				switch (img->bitspersample) {
-					case 8:
-						img->put.contig = put8bitcmaptile;
-						break;
-					case 4:
-						img->put.contig = put4bitcmaptile;
-						break;
-					case 2:
-						img->put.contig = put2bitcmaptile;
-						break;
-					case 1:
-						img->put.contig = put1bitcmaptile;
-						break;
-				}
-			}
-			break;
-		case PHOTOMETRIC_MINISWHITE:
-		case PHOTOMETRIC_MINISBLACK:
-			if (buildMap(img)) {
-				switch (img->bitspersample) {
-					case 16:
-						img->put.contig = put16bitbwtile;
-						break;
-					case 8:
-						if (img->alpha && img->samplesperpixel == 2)
-							img->put.contig = putagreytile;
-						else
-							img->put.contig = putgreytile;
-						break;
-					case 4:
-						img->put.contig = put4bitbwtile;
-						break;
-					case 2:
-						img->put.contig = put2bitbwtile;
-						break;
-					case 1:
-						img->put.contig = put1bitbwtile;
-						break;
-				}
-			}
-			break;
-		case PHOTOMETRIC_YCBCR:
-			if ((img->bitspersample==8) && (img->samplesperpixel==3))
-			{
-				if (initYCbCrConversion(img)!=0)
-				{
-					/*
-					 * The 6.0 spec says that subsampling must be
-					 * one of 1, 2, or 4, and that vertical subsampling
-					 * must always be <= horizontal subsampling; so
-					 * there are only a few possibilities and we just
-					 * enumerate the cases.
-					 * Joris: added support for the [1,2] case, nonetheless, to accommodate
-					 * some OJPEG files
-					 */
-					uint16 SubsamplingHor;
-					uint16 SubsamplingVer;
-					TIFFGetFieldDefaulted(img->tif, TIFFTAG_YCBCRSUBSAMPLING, &SubsamplingHor, &SubsamplingVer);
-					switch ((SubsamplingHor<<4)|SubsamplingVer) {
-						case 0x44:
-							img->put.contig = putcontig8bitYCbCr44tile;
-							break;
-						case 0x42:
-							img->put.contig = putcontig8bitYCbCr42tile;
-							break;
-						case 0x41:
-							img->put.contig = putcontig8bitYCbCr41tile;
-							break;
-						case 0x22:
-							img->put.contig = putcontig8bitYCbCr22tile;
-							break;
-						case 0x21:
-							img->put.contig = putcontig8bitYCbCr21tile;
-							break;
-						case 0x12:
-							img->put.contig = putcontig8bitYCbCr12tile;
-							break;
-						case 0x11:
-							img->put.contig = putcontig8bitYCbCr11tile;
-							break;
-					}
-				}
-			}
-			break;
-		case PHOTOMETRIC_CIELAB:
-			if (img->samplesperpixel == 3 && buildMap(img)) {
-				if (img->bitspersample == 8)
-					img->put.contig = initCIELabConversion(img);
-				break;
-			}
-	}
-	return ((img->get!=NULL) && (img->put.contig!=NULL));
+    img->get = TIFFIsTiled(img->tif) ? gtTileContig : gtStripContig;
+    img->put.contig = NULL;
+    switch (img->photometric)
+    {
+        case PHOTOMETRIC_RGB:
+            switch (img->bitspersample)
+            {
+                case 8:
+                    if (img->alpha == EXTRASAMPLE_ASSOCALPHA &&
+                        img->samplesperpixel >= 4)
+                        img->put.contig = putRGBAAcontig8bittile;
+                    else if (img->alpha == EXTRASAMPLE_UNASSALPHA &&
+                             img->samplesperpixel >= 4)
+                    {
+                        if (BuildMapUaToAa(img))
+                            img->put.contig = putRGBUAcontig8bittile;
+                    }
+                    else if (img->samplesperpixel >= 3)
+                        img->put.contig = putRGBcontig8bittile;
+                    break;
+                case 16:
+                    if (img->alpha == EXTRASAMPLE_ASSOCALPHA &&
+                        img->samplesperpixel >= 4)
+                    {
+                        if (BuildMapBitdepth16To8(img))
+                            img->put.contig = putRGBAAcontig16bittile;
+                    }
+                    else if (img->alpha == EXTRASAMPLE_UNASSALPHA &&
+                             img->samplesperpixel >= 4)
+                    {
+                        if (BuildMapBitdepth16To8(img) && BuildMapUaToAa(img))
+                            img->put.contig = putRGBUAcontig16bittile;
+                    }
+                    else if (img->samplesperpixel >= 3)
+                    {
+                        if (BuildMapBitdepth16To8(img))
+                            img->put.contig = putRGBcontig16bittile;
+                    }
+                    break;
+            }
+            break;
+        case PHOTOMETRIC_SEPARATED:
+            if (img->samplesperpixel >= 4 && buildMap(img))
+            {
+                if (img->bitspersample == 8)
+                {
+                    if (!img->Map)
+                        img->put.contig = putRGBcontig8bitCMYKtile;
+                    else
+                        img->put.contig = putRGBcontig8bitCMYKMaptile;
+                }
+            }
+            break;
+        case PHOTOMETRIC_PALETTE:
+            if (buildMap(img))
+            {
+                switch (img->bitspersample)
+                {
+                    case 8:
+                        img->put.contig = put8bitcmaptile;
+                        break;
+                    case 4:
+                        img->put.contig = put4bitcmaptile;
+                        break;
+                    case 2:
+                        img->put.contig = put2bitcmaptile;
+                        break;
+                    case 1:
+                        img->put.contig = put1bitcmaptile;
+                        break;
+                }
+            }
+            break;
+        case PHOTOMETRIC_MINISWHITE:
+        case PHOTOMETRIC_MINISBLACK:
+            if (buildMap(img))
+            {
+                switch (img->bitspersample)
+                {
+                    case 16:
+                        img->put.contig = put16bitbwtile;
+                        break;
+                    case 8:
+                        if (img->alpha && img->samplesperpixel == 2)
+                            img->put.contig = putagreytile;
+                        else
+                            img->put.contig = putgreytile;
+                        break;
+                    case 4:
+                        img->put.contig = put4bitbwtile;
+                        break;
+                    case 2:
+                        img->put.contig = put2bitbwtile;
+                        break;
+                    case 1:
+                        img->put.contig = put1bitbwtile;
+                        break;
+                }
+            }
+            break;
+        case PHOTOMETRIC_YCBCR:
+            if ((img->bitspersample == 8) && (img->samplesperpixel == 3))
+            {
+                if (initYCbCrConversion(img) != 0)
+                {
+                    /*
+                     * The 6.0 spec says that subsampling must be
+                     * one of 1, 2, or 4, and that vertical subsampling
+                     * must always be <= horizontal subsampling; so
+                     * there are only a few possibilities and we just
+                     * enumerate the cases.
+                     * Joris: added support for the [1,2] case, nonetheless, to
+                     * accommodate some OJPEG files
+                     */
+                    uint16_t SubsamplingHor;
+                    uint16_t SubsamplingVer;
+                    TIFFGetFieldDefaulted(img->tif, TIFFTAG_YCBCRSUBSAMPLING,
+                                          &SubsamplingHor, &SubsamplingVer);
+                    switch ((SubsamplingHor << 4) | SubsamplingVer)
+                    {
+                        case 0x44:
+                            img->put.contig = putcontig8bitYCbCr44tile;
+                            break;
+                        case 0x42:
+                            img->put.contig = putcontig8bitYCbCr42tile;
+                            break;
+                        case 0x41:
+                            img->put.contig = putcontig8bitYCbCr41tile;
+                            break;
+                        case 0x22:
+                            img->put.contig = putcontig8bitYCbCr22tile;
+                            break;
+                        case 0x21:
+                            img->put.contig = putcontig8bitYCbCr21tile;
+                            break;
+                        case 0x12:
+                            img->put.contig = putcontig8bitYCbCr12tile;
+                            break;
+                        case 0x11:
+                            img->put.contig = putcontig8bitYCbCr11tile;
+                            break;
+                    }
+                }
+            }
+            break;
+        case PHOTOMETRIC_CIELAB:
+            if (img->samplesperpixel == 3 && buildMap(img))
+            {
+                if (img->bitspersample == 8 || img->bitspersample == 16)
+                    img->put.contig = initCIELabConversion(img);
+                break;
+            }
+    }
+    return ((img->get != NULL) && (img->put.contig != NULL));
 }
 
 /*
@@ -2804,117 +3097,118 @@
  * NB: we assume that unpacked single channel data is directed
  *	 to the "packed routines.
  */
-static int
-PickSeparateCase(TIFFRGBAImage* img)
+static int PickSeparateCase(TIFFRGBAImage *img)
 {
-	img->get = TIFFIsTiled(img->tif) ? gtTileSeparate : gtStripSeparate;
-	img->put.separate = NULL;
-	switch (img->photometric) {
-	case PHOTOMETRIC_MINISWHITE:
-	case PHOTOMETRIC_MINISBLACK:
-		/* greyscale images processed pretty much as RGB by gtTileSeparate */
-	case PHOTOMETRIC_RGB:
-		switch (img->bitspersample) {
-		case 8:
-			if (img->alpha == EXTRASAMPLE_ASSOCALPHA)
-				img->put.separate = putRGBAAseparate8bittile;
-			else if (img->alpha == EXTRASAMPLE_UNASSALPHA)
-			{
-				if (BuildMapUaToAa(img))
-					img->put.separate = putRGBUAseparate8bittile;
-			}
-			else
-				img->put.separate = putRGBseparate8bittile;
-			break;
-		case 16:
-			if (img->alpha == EXTRASAMPLE_ASSOCALPHA)
-			{
-				if (BuildMapBitdepth16To8(img))
-					img->put.separate = putRGBAAseparate16bittile;
-			}
-			else if (img->alpha == EXTRASAMPLE_UNASSALPHA)
-			{
-				if (BuildMapBitdepth16To8(img) &&
-				    BuildMapUaToAa(img))
-					img->put.separate = putRGBUAseparate16bittile;
-			}
-			else
-			{
-				if (BuildMapBitdepth16To8(img))
-					img->put.separate = putRGBseparate16bittile;
-			}
-			break;
-		}
-		break;
-	case PHOTOMETRIC_SEPARATED:
-		if (img->bitspersample == 8 && img->samplesperpixel == 4)
-		{
-			img->alpha = 1; // Not alpha, but seems like the only way to get 4th band
-			img->put.separate = putCMYKseparate8bittile;
-		}
-		break;
-	case PHOTOMETRIC_YCBCR:
-		if ((img->bitspersample==8) && (img->samplesperpixel==3))
-		{
-			if (initYCbCrConversion(img)!=0)
-			{
-				uint16 hs, vs;
-				TIFFGetFieldDefaulted(img->tif, TIFFTAG_YCBCRSUBSAMPLING, &hs, &vs);
-				switch ((hs<<4)|vs) {
-				case 0x11:
-					img->put.separate = putseparate8bitYCbCr11tile;
-					break;
-					/* TODO: add other cases here */
-				}
-			}
-		}
-		break;
-	}
-	return ((img->get!=NULL) && (img->put.separate!=NULL));
+    img->get = TIFFIsTiled(img->tif) ? gtTileSeparate : gtStripSeparate;
+    img->put.separate = NULL;
+    switch (img->photometric)
+    {
+        case PHOTOMETRIC_MINISWHITE:
+        case PHOTOMETRIC_MINISBLACK:
+            /* greyscale images processed pretty much as RGB by gtTileSeparate
+             */
+        case PHOTOMETRIC_RGB:
+            switch (img->bitspersample)
+            {
+                case 8:
+                    if (img->alpha == EXTRASAMPLE_ASSOCALPHA)
+                        img->put.separate = putRGBAAseparate8bittile;
+                    else if (img->alpha == EXTRASAMPLE_UNASSALPHA)
+                    {
+                        if (BuildMapUaToAa(img))
+                            img->put.separate = putRGBUAseparate8bittile;
+                    }
+                    else
+                        img->put.separate = putRGBseparate8bittile;
+                    break;
+                case 16:
+                    if (img->alpha == EXTRASAMPLE_ASSOCALPHA)
+                    {
+                        if (BuildMapBitdepth16To8(img))
+                            img->put.separate = putRGBAAseparate16bittile;
+                    }
+                    else if (img->alpha == EXTRASAMPLE_UNASSALPHA)
+                    {
+                        if (BuildMapBitdepth16To8(img) && BuildMapUaToAa(img))
+                            img->put.separate = putRGBUAseparate16bittile;
+                    }
+                    else
+                    {
+                        if (BuildMapBitdepth16To8(img))
+                            img->put.separate = putRGBseparate16bittile;
+                    }
+                    break;
+            }
+            break;
+        case PHOTOMETRIC_SEPARATED:
+            if (img->bitspersample == 8 && img->samplesperpixel == 4)
+            {
+                img->alpha =
+                    1; // Not alpha, but seems like the only way to get 4th band
+                img->put.separate = putCMYKseparate8bittile;
+            }
+            break;
+        case PHOTOMETRIC_YCBCR:
+            if ((img->bitspersample == 8) && (img->samplesperpixel == 3))
+            {
+                if (initYCbCrConversion(img) != 0)
+                {
+                    uint16_t hs, vs;
+                    TIFFGetFieldDefaulted(img->tif, TIFFTAG_YCBCRSUBSAMPLING,
+                                          &hs, &vs);
+                    switch ((hs << 4) | vs)
+                    {
+                        case 0x11:
+                            img->put.separate = putseparate8bitYCbCr11tile;
+                            break;
+                            /* TODO: add other cases here */
+                    }
+                }
+            }
+            break;
+    }
+    return ((img->get != NULL) && (img->put.separate != NULL));
 }
 
-static int
-BuildMapUaToAa(TIFFRGBAImage* img)
+static int BuildMapUaToAa(TIFFRGBAImage *img)
 {
-	static const char module[]="BuildMapUaToAa";
-	uint8* m;
-	uint16 na,nv;
-	assert(img->UaToAa==NULL);
-	img->UaToAa=_TIFFmalloc(65536);
-	if (img->UaToAa==NULL)
-	{
-		TIFFErrorExt(img->tif->tif_clientdata,module,"Out of memory");
-		return(0);
-	}
-	m=img->UaToAa;
-	for (na=0; na<256; na++)
-	{
-		for (nv=0; nv<256; nv++)
-			*m++=(uint8)((nv*na+127)/255);
-	}
-	return(1);
+    static const char module[] = "BuildMapUaToAa";
+    uint8_t *m;
+    uint16_t na, nv;
+    assert(img->UaToAa == NULL);
+    img->UaToAa = _TIFFmallocExt(img->tif, 65536);
+    if (img->UaToAa == NULL)
+    {
+        TIFFErrorExtR(img->tif, module, "Out of memory");
+        return (0);
+    }
+    m = img->UaToAa;
+    for (na = 0; na < 256; na++)
+    {
+        for (nv = 0; nv < 256; nv++)
+            *m++ = (uint8_t)((nv * na + 127) / 255);
+    }
+    return (1);
 }
 
-static int
-BuildMapBitdepth16To8(TIFFRGBAImage* img)
+static int BuildMapBitdepth16To8(TIFFRGBAImage *img)
 {
-	static const char module[]="BuildMapBitdepth16To8";
-	uint8* m;
-	uint32 n;
-	assert(img->Bitdepth16To8==NULL);
-	img->Bitdepth16To8=_TIFFmalloc(65536);
-	if (img->Bitdepth16To8==NULL)
-	{
-		TIFFErrorExt(img->tif->tif_clientdata,module,"Out of memory");
-		return(0);
-	}
-	m=img->Bitdepth16To8;
-	for (n=0; n<65536; n++)
-		*m++=(uint8)((n+128)/257);
-	return(1);
+    static const char module[] = "BuildMapBitdepth16To8";
+    uint8_t *m;
+    uint32_t n;
+    assert(img->Bitdepth16To8 == NULL);
+    img->Bitdepth16To8 = _TIFFmallocExt(img->tif, 65536);
+    if (img->Bitdepth16To8 == NULL)
+    {
+        TIFFErrorExtR(img->tif, module, "Out of memory");
+        return (0);
+    }
+    m = img->Bitdepth16To8;
+    for (n = 0; n < 65536; n++)
+        *m++ = (uint8_t)((n + 128) / 257);
+    return (1);
 }
 
-
 /*
  * Read a whole strip off data from the file, and convert to RGBA form.
  * If this is the last strip, then it will only contain the portion of
@@ -2922,56 +3216,59 @@
  * organized in bottom to top form.
  */
 
-
-int
-TIFFReadRGBAStrip(TIFF* tif, uint32 row, uint32 * raster )
+int TIFFReadRGBAStrip(TIFF *tif, uint32_t row, uint32_t *raster)
 
 {
-    return TIFFReadRGBAStripExt(tif, row, raster, 0 );
+    return TIFFReadRGBAStripExt(tif, row, raster, 0);
 }
 
-int
-TIFFReadRGBAStripExt(TIFF* tif, uint32 row, uint32 * raster, int stop_on_error)
+int TIFFReadRGBAStripExt(TIFF *tif, uint32_t row, uint32_t *raster,
+                         int stop_on_error)
 
 {
-    char 	emsg[1024] = "";
+    char emsg[EMSG_BUF_SIZE] = "";
     TIFFRGBAImage img;
-    int 	ok;
-    uint32	rowsperstrip, rows_to_read;
+    int ok;
+    uint32_t rowsperstrip, rows_to_read;
 
-    if( TIFFIsTiled( tif ) )
+    if (TIFFIsTiled(tif))
     {
-		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif),
-                  "Can't use TIFFReadRGBAStrip() with tiled file.");
-	return (0);
+        TIFFErrorExtR(tif, TIFFFileName(tif),
+                      "Can't use TIFFReadRGBAStrip() with tiled file.");
+        return (0);
     }
-    
+
     TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
-    if( (row % rowsperstrip) != 0 )
+    if ((row % rowsperstrip) != 0)
     {
-		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif),
-				"Row passed to TIFFReadRGBAStrip() must be first in a strip.");
-		return (0);
+        TIFFErrorExtR(
+            tif, TIFFFileName(tif),
+            "Row passed to TIFFReadRGBAStrip() must be first in a strip.");
+        return (0);
     }
 
-    if (TIFFRGBAImageOK(tif, emsg) && TIFFRGBAImageBegin(&img, tif, stop_on_error, emsg)) {
+    if (TIFFRGBAImageOK(tif, emsg) &&
+        TIFFRGBAImageBegin(&img, tif, stop_on_error, emsg))
+    {
 
         img.row_offset = row;
         img.col_offset = 0;
 
-        if( row + rowsperstrip > img.height )
+        if (row + rowsperstrip > img.height)
             rows_to_read = img.height - row;
         else
             rows_to_read = rowsperstrip;
-        
-	ok = TIFFRGBAImageGet(&img, raster, img.width, rows_to_read );
-        
-	TIFFRGBAImageEnd(&img);
-    } else {
-		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", emsg);
-		ok = 0;
+
+        ok = TIFFRGBAImageGet(&img, raster, img.width, rows_to_read);
+
+        TIFFRGBAImageEnd(&img);
     }
-    
+    else
+    {
+        TIFFErrorExtR(tif, TIFFFileName(tif), "%s", emsg);
+        ok = 0;
+    }
+
     return (ok);
 }
 
@@ -2981,54 +3278,53 @@
  * and may include zeroed areas if the tile extends off the image.
  */
 
-int
-TIFFReadRGBATile(TIFF* tif, uint32 col, uint32 row, uint32 * raster)
+int TIFFReadRGBATile(TIFF *tif, uint32_t col, uint32_t row, uint32_t *raster)
 
 {
-    return TIFFReadRGBATileExt(tif, col, row, raster, 0 );
+    return TIFFReadRGBATileExt(tif, col, row, raster, 0);
 }
 
-
-int
-TIFFReadRGBATileExt(TIFF* tif, uint32 col, uint32 row, uint32 * raster, int stop_on_error )
+int TIFFReadRGBATileExt(TIFF *tif, uint32_t col, uint32_t row, uint32_t *raster,
+                        int stop_on_error)
 {
-    char 	emsg[1024] = "";
+    char emsg[EMSG_BUF_SIZE] = "";
     TIFFRGBAImage img;
-    int 	ok;
-    uint32	tile_xsize, tile_ysize;
-    uint32	read_xsize, read_ysize;
-    uint32	i_row;
+    int ok;
+    uint32_t tile_xsize, tile_ysize;
+    uint32_t read_xsize, read_ysize;
+    uint32_t i_row;
 
     /*
      * Verify that our request is legal - on a tile file, and on a
      * tile boundary.
      */
-    
-    if( !TIFFIsTiled( tif ) )
+
+    if (!TIFFIsTiled(tif))
     {
-		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif),
-				  "Can't use TIFFReadRGBATile() with striped file.");
-		return (0);
+        TIFFErrorExtR(tif, TIFFFileName(tif),
+                      "Can't use TIFFReadRGBATile() with striped file.");
+        return (0);
     }
-    
+
     TIFFGetFieldDefaulted(tif, TIFFTAG_TILEWIDTH, &tile_xsize);
     TIFFGetFieldDefaulted(tif, TIFFTAG_TILELENGTH, &tile_ysize);
-    if( (col % tile_xsize) != 0 || (row % tile_ysize) != 0 )
+    if ((col % tile_xsize) != 0 || (row % tile_ysize) != 0)
     {
-		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif),
-                  "Row/col passed to TIFFReadRGBATile() must be top"
-                  "left corner of a tile.");
-	return (0);
+        TIFFErrorExtR(tif, TIFFFileName(tif),
+                      "Row/col passed to TIFFReadRGBATile() must be top"
+                      "left corner of a tile.");
+        return (0);
     }
 
     /*
      * Setup the RGBA reader.
      */
-    
-    if (!TIFFRGBAImageOK(tif, emsg) 
-	|| !TIFFRGBAImageBegin(&img, tif, stop_on_error, emsg)) {
-	    TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", emsg);
-	    return( 0 );
+
+    if (!TIFFRGBAImageOK(tif, emsg) ||
+        !TIFFRGBAImageBegin(&img, tif, stop_on_error, emsg))
+    {
+        TIFFErrorExtR(tif, TIFFFileName(tif), "%s", emsg);
+        return (0);
     }
 
     /*
@@ -3038,12 +3334,12 @@
      * a full tile configuration afterwards.
      */
 
-    if( row + tile_ysize > img.height )
+    if (row + tile_ysize > img.height)
         read_ysize = img.height - row;
     else
         read_ysize = tile_ysize;
-    
-    if( col + tile_xsize > img.width )
+
+    if (col + tile_xsize > img.width)
         read_xsize = img.width - col;
     else
         read_xsize = tile_xsize;
@@ -3051,12 +3347,12 @@
     /*
      * Read the chunk of imagery.
      */
-    
+
     img.row_offset = row;
     img.col_offset = col;
 
-    ok = TIFFRGBAImageGet(&img, raster, read_xsize, read_ysize );
-        
+    ok = TIFFRGBAImageGet(&img, raster, read_xsize, read_ysize);
+
     TIFFRGBAImageEnd(&img);
 
     /*
@@ -3064,33 +3360,27 @@
      * shifting the data around as if a full tile of data is being returned.
      *
      * This is all the more complicated because the image is organized in
-     * bottom to top format. 
+     * bottom to top format.
      */
 
-    if( read_xsize == tile_xsize && read_ysize == tile_ysize )
-        return( ok );
+    if (read_xsize == tile_xsize && read_ysize == tile_ysize)
+        return (ok);
 
-    for( i_row = 0; i_row < read_ysize; i_row++ ) {
-        memmove( raster + (tile_ysize - i_row - 1) * tile_xsize,
-                 raster + (read_ysize - i_row - 1) * read_xsize,
-                 read_xsize * sizeof(uint32) );
-        _TIFFmemset( raster + (tile_ysize - i_row - 1) * tile_xsize+read_xsize,
-                     0, sizeof(uint32) * (tile_xsize - read_xsize) );
+    for (i_row = 0; i_row < read_ysize; i_row++)
+    {
+        memmove(raster + (size_t)(tile_ysize - i_row - 1) * tile_xsize,
+                raster + (size_t)(read_ysize - i_row - 1) * read_xsize,
+                read_xsize * sizeof(uint32_t));
+        _TIFFmemset(raster + (size_t)(tile_ysize - i_row - 1) * tile_xsize +
+                        read_xsize,
+                    0, sizeof(uint32_t) * (tile_xsize - read_xsize));
     }
 
-    for( i_row = read_ysize; i_row < tile_ysize; i_row++ ) {
-        _TIFFmemset( raster + (tile_ysize - i_row - 1) * tile_xsize,
-                     0, sizeof(uint32) * tile_xsize );
+    for (i_row = read_ysize; i_row < tile_ysize; i_row++)
+    {
+        _TIFFmemset(raster + (size_t)(tile_ysize - i_row - 1) * tile_xsize, 0,
+                    sizeof(uint32_t) * tile_xsize);
     }
 
     return (ok);
 }
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_hash_set.c b/third_party/libtiff/tif_hash_set.c
new file mode 100644
index 0000000..cec22ab
--- /dev/null
+++ b/third_party/libtiff/tif_hash_set.c
@@ -0,0 +1,603 @@
+/**********************************************************************
+ *
+ * Name:     tif_hash_set.c
+ * Purpose:  Hash set functions.
+ * Author:   Even Rouault, <even dot rouault at spatialys.com>
+ *
+ **********************************************************************
+ * Copyright (c) 2008-2009, Even Rouault <even dot rouault at spatialys.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#include "tiffconf.h"
+
+#include "tif_hash_set.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/** List element structure. */
+typedef struct _TIFFList TIFFList;
+
+/** List element structure. */
+struct _TIFFList
+{
+    /*! Pointer to the data object. Should be allocated and freed by the
+     * caller.
+     * */
+    void *pData;
+    /*! Pointer to the next element in list. NULL, if current element is the
+     * last one.
+     */
+    struct _TIFFList *psNext;
+};
+
+struct _TIFFHashSet
+{
+    TIFFHashSetHashFunc fnHashFunc;
+    TIFFHashSetEqualFunc fnEqualFunc;
+    TIFFHashSetFreeEltFunc fnFreeEltFunc;
+    TIFFList **tabList;
+    int nSize;
+    int nIndiceAllocatedSize;
+    int nAllocatedSize;
+    TIFFList *psRecyclingList;
+    int nRecyclingListSize;
+    bool bRehash;
+#ifdef HASH_DEBUG
+    int nCollisions;
+#endif
+};
+
+static const int anPrimes[] = {
+    53,        97,        193,       389,       769,       1543,     3079,
+    6151,      12289,     24593,     49157,     98317,     196613,   393241,
+    786433,    1572869,   3145739,   6291469,   12582917,  25165843, 50331653,
+    100663319, 201326611, 402653189, 805306457, 1610612741};
+
+/************************************************************************/
+/*                    TIFFHashSetHashPointer()                          */
+/************************************************************************/
+
+/**
+ * Hash function for an arbitrary pointer
+ *
+ * @param elt the arbitrary pointer to hash
+ *
+ * @return the hash value of the pointer
+ */
+
+static unsigned long TIFFHashSetHashPointer(const void *elt)
+{
+    return (unsigned long)(uintptr_t)((void *)(elt));
+}
+
+/************************************************************************/
+/*                   TIFFHashSetEqualPointer()                          */
+/************************************************************************/
+
+/**
+ * Equality function for arbitrary pointers
+ *
+ * @param elt1 the first arbitrary pointer to compare
+ * @param elt2 the second arbitrary pointer to compare
+ *
+ * @return true if the pointers are equal
+ */
+
+static bool TIFFHashSetEqualPointer(const void *elt1, const void *elt2)
+{
+    return elt1 == elt2;
+}
+
+/************************************************************************/
+/*                          TIFFHashSetNew()                             */
+/************************************************************************/
+
+/**
+ * Creates a new hash set
+ *
+ * The hash function must return a hash value for the elements to insert.
+ * If fnHashFunc is NULL, TIFFHashSetHashPointer will be used.
+ *
+ * The equal function must return if two elements are equal.
+ * If fnEqualFunc is NULL, TIFFHashSetEqualPointer will be used.
+ *
+ * The free function is used to free elements inserted in the hash set,
+ * when the hash set is destroyed, when elements are removed or replaced.
+ * If fnFreeEltFunc is NULL, elements inserted into the hash set will not be
+ * freed.
+ *
+ * @param fnHashFunc hash function. May be NULL.
+ * @param fnEqualFunc equal function. May be NULL.
+ * @param fnFreeEltFunc element free function. May be NULL.
+ *
+ * @return a new hash set
+ */
+
+TIFFHashSet *TIFFHashSetNew(TIFFHashSetHashFunc fnHashFunc,
+                            TIFFHashSetEqualFunc fnEqualFunc,
+                            TIFFHashSetFreeEltFunc fnFreeEltFunc)
+{
+    TIFFHashSet *set = (TIFFHashSet *)malloc(sizeof(TIFFHashSet));
+    if (set == NULL)
+        return NULL;
+    set->fnHashFunc = fnHashFunc ? fnHashFunc : TIFFHashSetHashPointer;
+    set->fnEqualFunc = fnEqualFunc ? fnEqualFunc : TIFFHashSetEqualPointer;
+    set->fnFreeEltFunc = fnFreeEltFunc;
+    set->nSize = 0;
+    set->tabList = (TIFFList **)(calloc(sizeof(TIFFList *), 53));
+    if (set->tabList == NULL)
+    {
+        free(set);
+        return NULL;
+    }
+    set->nIndiceAllocatedSize = 0;
+    set->nAllocatedSize = 53;
+    set->psRecyclingList = NULL;
+    set->nRecyclingListSize = 0;
+    set->bRehash = false;
+#ifdef HASH_DEBUG
+    set->nCollisions = 0;
+#endif
+    return set;
+}
+
+/************************************************************************/
+/*                          TIFFHashSetSize()                            */
+/************************************************************************/
+
+/**
+ * Returns the number of elements inserted in the hash set
+ *
+ * Note: this is not the internal size of the hash set
+ *
+ * @param set the hash set
+ *
+ * @return the number of elements in the hash set
+ */
+
+int TIFFHashSetSize(const TIFFHashSet *set)
+{
+    assert(set != NULL);
+    return set->nSize;
+}
+
+/************************************************************************/
+/*                       TIFFHashSetGetNewListElt()                      */
+/************************************************************************/
+
+static TIFFList *TIFFHashSetGetNewListElt(TIFFHashSet *set)
+{
+    if (set->psRecyclingList)
+    {
+        TIFFList *psRet = set->psRecyclingList;
+        psRet->pData = NULL;
+        set->nRecyclingListSize--;
+        set->psRecyclingList = psRet->psNext;
+        return psRet;
+    }
+
+    return (TIFFList *)malloc(sizeof(TIFFList));
+}
+
+/************************************************************************/
+/*                       TIFFHashSetReturnListElt()                      */
+/************************************************************************/
+
+static void TIFFHashSetReturnListElt(TIFFHashSet *set, TIFFList *psList)
+{
+    if (set->nRecyclingListSize < 128)
+    {
+        psList->psNext = set->psRecyclingList;
+        set->psRecyclingList = psList;
+        set->nRecyclingListSize++;
+    }
+    else
+    {
+        free(psList);
+    }
+}
+
+/************************************************************************/
+/*                   TIFFHashSetClearInternal()                          */
+/************************************************************************/
+
+static void TIFFHashSetClearInternal(TIFFHashSet *set, bool bFinalize)
+{
+    assert(set != NULL);
+    for (int i = 0; i < set->nAllocatedSize; i++)
+    {
+        TIFFList *cur = set->tabList[i];
+        while (cur)
+        {
+            if (set->fnFreeEltFunc)
+                set->fnFreeEltFunc(cur->pData);
+            TIFFList *psNext = cur->psNext;
+            if (bFinalize)
+                free(cur);
+            else
+                TIFFHashSetReturnListElt(set, cur);
+            cur = psNext;
+        }
+        set->tabList[i] = NULL;
+    }
+    set->bRehash = false;
+}
+
+/************************************************************************/
+/*                         TIFFListDestroy()                            */
+/************************************************************************/
+
+/**
+ * Destroy a list. Caller responsible for freeing data objects contained in
+ * list elements.
+ *
+ * @param psList pointer to list head.
+ *
+ */
+
+static void TIFFListDestroy(TIFFList *psList)
+{
+    TIFFList *psCurrent = psList;
+
+    while (psCurrent)
+    {
+        TIFFList *const psNext = psCurrent->psNext;
+        free(psCurrent);
+        psCurrent = psNext;
+    }
+}
+
+/************************************************************************/
+/*                        TIFFHashSetDestroy()                          */
+/************************************************************************/
+
+/**
+ * Destroys an allocated hash set.
+ *
+ * This function also frees the elements if a free function was
+ * provided at the creation of the hash set.
+ *
+ * @param set the hash set
+ */
+
+void TIFFHashSetDestroy(TIFFHashSet *set)
+{
+    if (set)
+    {
+        TIFFHashSetClearInternal(set, true);
+        free(set->tabList);
+        TIFFListDestroy(set->psRecyclingList);
+        free(set);
+    }
+}
+
+#ifdef notused
+/************************************************************************/
+/*                        TIFFHashSetClear()                             */
+/************************************************************************/
+
+/**
+ * Clear all elements from a hash set.
+ *
+ * This function also frees the elements if a free function was
+ * provided at the creation of the hash set.
+ *
+ * @param set the hash set
+ */
+
+void TIFFHashSetClear(TIFFHashSet *set)
+{
+    TIFFHashSetClearInternal(set, false);
+    set->nIndiceAllocatedSize = 0;
+    set->nAllocatedSize = 53;
+#ifdef HASH_DEBUG
+    set->nCollisions = 0;
+#endif
+    set->nSize = 0;
+}
+
+/************************************************************************/
+/*                       TIFFHashSetForeach()                           */
+/************************************************************************/
+
+/**
+ * Walk through the hash set and runs the provided function on all the
+ * elements
+ *
+ * This function is provided the user_data argument of TIFFHashSetForeach.
+ * It must return true to go on the walk through the hash set, or FALSE to
+ * make it stop.
+ *
+ * Note : the structure of the hash set must *NOT* be modified during the
+ * walk.
+ *
+ * @param set the hash set.
+ * @param fnIterFunc the function called on each element.
+ * @param user_data the user data provided to the function.
+ */
+
+void TIFFHashSetForeach(TIFFHashSet *set, TIFFHashSetIterEltFunc fnIterFunc,
+                        void *user_data)
+{
+    assert(set != NULL);
+    if (!fnIterFunc)
+        return;
+
+    for (int i = 0; i < set->nAllocatedSize; i++)
+    {
+        TIFFList *cur = set->tabList[i];
+        while (cur)
+        {
+            if (!fnIterFunc(cur->pData, user_data))
+                return;
+
+            cur = cur->psNext;
+        }
+    }
+}
+#endif
+
+/************************************************************************/
+/*                        TIFFHashSetRehash()                           */
+/************************************************************************/
+
+static bool TIFFHashSetRehash(TIFFHashSet *set)
+{
+    int nNewAllocatedSize = anPrimes[set->nIndiceAllocatedSize];
+    TIFFList **newTabList =
+        (TIFFList **)(calloc(sizeof(TIFFList *), nNewAllocatedSize));
+    if (newTabList == NULL)
+        return false;
+#ifdef HASH_DEBUG
+    TIFFDebug("TIFFHASH",
+              "hashSet=%p, nSize=%d, nCollisions=%d, "
+              "fCollisionRate=%.02f",
+              set, set->nSize, set->nCollisions,
+              set->nCollisions * 100.0 / set->nSize);
+    set->nCollisions = 0;
+#endif
+    for (int i = 0; i < set->nAllocatedSize; i++)
+    {
+        TIFFList *cur = set->tabList[i];
+        while (cur)
+        {
+            const unsigned long nNewHashVal =
+                set->fnHashFunc(cur->pData) % nNewAllocatedSize;
+#ifdef HASH_DEBUG
+            if (newTabList[nNewHashVal])
+                set->nCollisions++;
+#endif
+            TIFFList *psNext = cur->psNext;
+            cur->psNext = newTabList[nNewHashVal];
+            newTabList[nNewHashVal] = cur;
+            cur = psNext;
+        }
+    }
+    free(set->tabList);
+    set->tabList = newTabList;
+    set->nAllocatedSize = nNewAllocatedSize;
+    set->bRehash = false;
+    return true;
+}
+
+/************************************************************************/
+/*                        TIFFHashSetFindPtr()                          */
+/************************************************************************/
+
+static void **TIFFHashSetFindPtr(TIFFHashSet *set, const void *elt)
+{
+    const unsigned long nHashVal = set->fnHashFunc(elt) % set->nAllocatedSize;
+    TIFFList *cur = set->tabList[nHashVal];
+    while (cur)
+    {
+        if (set->fnEqualFunc(cur->pData, elt))
+            return &cur->pData;
+        cur = cur->psNext;
+    }
+    return NULL;
+}
+
+/************************************************************************/
+/*                         TIFFHashSetInsert()                          */
+/************************************************************************/
+
+/**
+ * Inserts an element into a hash set.
+ *
+ * If the element was already inserted in the hash set, the previous
+ * element is replaced by the new element. If a free function was provided,
+ * it is used to free the previously inserted element
+ *
+ * @param set the hash set
+ * @param elt the new element to insert in the hash set
+ *
+ * @return true if success. If false is returned, elt has not been inserted,
+ * but TIFFHashSetInsert() will have run the free function if provided.
+ */
+
+bool TIFFHashSetInsert(TIFFHashSet *set, void *elt)
+{
+    assert(set != NULL);
+    void **pElt = TIFFHashSetFindPtr(set, elt);
+    if (pElt)
+    {
+        if (set->fnFreeEltFunc)
+            set->fnFreeEltFunc(*pElt);
+
+        *pElt = elt;
+        return true;
+    }
+
+    if (set->nSize >= 2 * set->nAllocatedSize / 3 ||
+        (set->bRehash && set->nIndiceAllocatedSize > 0 &&
+         set->nSize <= set->nAllocatedSize / 2))
+    {
+        set->nIndiceAllocatedSize++;
+        if (!TIFFHashSetRehash(set))
+        {
+            set->nIndiceAllocatedSize--;
+            if (set->fnFreeEltFunc)
+                set->fnFreeEltFunc(elt);
+            return false;
+        }
+    }
+
+    const unsigned long nHashVal = set->fnHashFunc(elt) % set->nAllocatedSize;
+#ifdef HASH_DEBUG
+    if (set->tabList[nHashVal])
+        set->nCollisions++;
+#endif
+
+    TIFFList *new_elt = TIFFHashSetGetNewListElt(set);
+    if (new_elt == NULL)
+    {
+        if (set->fnFreeEltFunc)
+            set->fnFreeEltFunc(elt);
+        return false;
+    }
+    new_elt->pData = elt;
+    new_elt->psNext = set->tabList[nHashVal];
+    set->tabList[nHashVal] = new_elt;
+    set->nSize++;
+
+    return true;
+}
+
+/************************************************************************/
+/*                        TIFFHashSetLookup()                           */
+/************************************************************************/
+
+/**
+ * Returns the element found in the hash set corresponding to the element to
+ * look up The element must not be modified.
+ *
+ * @param set the hash set
+ * @param elt the element to look up in the hash set
+ *
+ * @return the element found in the hash set or NULL
+ */
+
+void *TIFFHashSetLookup(TIFFHashSet *set, const void *elt)
+{
+    assert(set != NULL);
+    void **pElt = TIFFHashSetFindPtr(set, elt);
+    if (pElt)
+        return *pElt;
+
+    return NULL;
+}
+
+/************************************************************************/
+/*                     TIFFHashSetRemoveInternal()                      */
+/************************************************************************/
+
+static bool TIFFHashSetRemoveInternal(TIFFHashSet *set, const void *elt,
+                                      bool bDeferRehash)
+{
+    assert(set != NULL);
+    if (set->nIndiceAllocatedSize > 0 && set->nSize <= set->nAllocatedSize / 2)
+    {
+        set->nIndiceAllocatedSize--;
+        if (bDeferRehash)
+            set->bRehash = true;
+        else
+        {
+            if (!TIFFHashSetRehash(set))
+            {
+                set->nIndiceAllocatedSize++;
+                return false;
+            }
+        }
+    }
+
+    int nHashVal = (int)(set->fnHashFunc(elt) % set->nAllocatedSize);
+    TIFFList *cur = set->tabList[nHashVal];
+    TIFFList *prev = NULL;
+    while (cur)
+    {
+        if (set->fnEqualFunc(cur->pData, elt))
+        {
+            if (prev)
+                prev->psNext = cur->psNext;
+            else
+                set->tabList[nHashVal] = cur->psNext;
+
+            if (set->fnFreeEltFunc)
+                set->fnFreeEltFunc(cur->pData);
+
+            TIFFHashSetReturnListElt(set, cur);
+#ifdef HASH_DEBUG
+            if (set->tabList[nHashVal])
+                set->nCollisions--;
+#endif
+            set->nSize--;
+            return true;
+        }
+        prev = cur;
+        cur = cur->psNext;
+    }
+    return false;
+}
+
+/************************************************************************/
+/*                         TIFFHashSetRemove()                          */
+/************************************************************************/
+
+/**
+ * Removes an element from a hash set
+ *
+ * @param set the hash set
+ * @param elt the new element to remove from the hash set
+ *
+ * @return true if the element was in the hash set
+ */
+
+bool TIFFHashSetRemove(TIFFHashSet *set, const void *elt)
+{
+    return TIFFHashSetRemoveInternal(set, elt, false);
+}
+
+#ifdef notused
+/************************************************************************/
+/*                     TIFFHashSetRemoveDeferRehash()                   */
+/************************************************************************/
+
+/**
+ * Removes an element from a hash set.
+ *
+ * This will defer potential rehashing of the set to later calls to
+ * TIFFHashSetInsert() or TIFFHashSetRemove().
+ *
+ * @param set the hash set
+ * @param elt the new element to remove from the hash set
+ *
+ * @return true if the element was in the hash set
+ */
+
+bool TIFFHashSetRemoveDeferRehash(TIFFHashSet *set, const void *elt)
+{
+    return TIFFHashSetRemoveInternal(set, elt, true);
+}
+#endif
diff --git a/third_party/libtiff/tif_hash_set.h b/third_party/libtiff/tif_hash_set.h
new file mode 100644
index 0000000..f60e2c6
--- /dev/null
+++ b/third_party/libtiff/tif_hash_set.h
@@ -0,0 +1,100 @@
+/**********************************************************************
+ * $Id$
+ *
+ * Name:     tif_hash_set.h
+ * Project:  TIFF - Common Portability Library
+ * Purpose:  Hash set functions.
+ * Author:   Even Rouault, <even dot rouault at spatialys.com>
+ *
+ **********************************************************************
+ * Copyright (c) 2008-2009, Even Rouault <even dot rouault at spatialys.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#ifndef TIFF_HASH_SET_H_INCLUDED
+#define TIFF_HASH_SET_H_INCLUDED
+
+#include <stdbool.h>
+
+/**
+ * \file tif_hash_set.h
+ *
+ * Hash set implementation.
+ *
+ * An hash set is a data structure that holds elements that are unique
+ * according to a comparison function. Operations on the hash set, such as
+ * insertion, removal or lookup, are supposed to be fast if an efficient
+ * "hash" function is provided.
+ */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+    /* Types */
+
+    /** Opaque type for a hash set */
+    typedef struct _TIFFHashSet TIFFHashSet;
+
+    /** TIFFHashSetHashFunc */
+    typedef unsigned long (*TIFFHashSetHashFunc)(const void *elt);
+
+    /** TIFFHashSetEqualFunc */
+    typedef bool (*TIFFHashSetEqualFunc)(const void *elt1, const void *elt2);
+
+    /** TIFFHashSetFreeEltFunc */
+    typedef void (*TIFFHashSetFreeEltFunc)(void *elt);
+
+    /* Functions */
+
+    TIFFHashSet *TIFFHashSetNew(TIFFHashSetHashFunc fnHashFunc,
+                                TIFFHashSetEqualFunc fnEqualFunc,
+                                TIFFHashSetFreeEltFunc fnFreeEltFunc);
+
+    void TIFFHashSetDestroy(TIFFHashSet *set);
+
+    int TIFFHashSetSize(const TIFFHashSet *set);
+
+#ifdef notused
+    void TIFFHashSetClear(TIFFHashSet *set);
+
+    /** TIFFHashSetIterEltFunc */
+    typedef int (*TIFFHashSetIterEltFunc)(void *elt, void *user_data);
+
+    void TIFFHashSetForeach(TIFFHashSet *set, TIFFHashSetIterEltFunc fnIterFunc,
+                            void *user_data);
+#endif
+
+    bool TIFFHashSetInsert(TIFFHashSet *set, void *elt);
+
+    void *TIFFHashSetLookup(TIFFHashSet *set, const void *elt);
+
+    bool TIFFHashSetRemove(TIFFHashSet *set, const void *elt);
+
+#ifdef notused
+    bool TIFFHashSetRemoveDeferRehash(TIFFHashSet *set, const void *elt);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TIFF_HASH_SET_H_INCLUDED */
diff --git a/third_party/libtiff/tif_jpeg.c b/third_party/libtiff/tif_jpeg.c
index 3b1ed9f..a7dbde7 100644
--- a/third_party/libtiff/tif_jpeg.c
+++ b/third_party/libtiff/tif_jpeg.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1994-1997 Sam Leffler
  * Copyright (c) 1994-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -44,10 +44,34 @@
  */
 #include <setjmp.h>
 
-int TIFFFillStrip(TIFF* tif, uint32 strip);
-int TIFFFillTile(TIFF* tif, uint32 tile);
-int TIFFReInitJPEG_12( TIFF *tif, int scheme, int is_encode );
-int TIFFJPEGIsFullStripRequired_12(TIFF* tif);
+/* Settings that are independent of libjpeg ABI. Used when reinitializing the */
+/* JPEGState from libjpegs 8 bit to libjpeg 12 bits, which have potentially */
+/* different ABI */
+typedef struct
+{
+    TIFFVGetMethod vgetparent;  /* super-class method */
+    TIFFVSetMethod vsetparent;  /* super-class method */
+    TIFFPrintMethod printdir;   /* super-class method */
+    TIFFStripMethod defsparent; /* super-class method */
+    TIFFTileMethod deftparent;  /* super-class method */
+
+    /* pseudo-tag fields */
+    void *jpegtables;           /* JPEGTables tag value, or NULL */
+    uint32_t jpegtables_length; /* number of bytes in same */
+    int jpegquality;            /* Compression quality level */
+    int jpegcolormode;          /* Auto RGB<=>YCbCr convert? */
+    int jpegtablesmode;         /* What to put in JPEGTables */
+
+    int ycbcrsampling_fetched;
+    int max_allowed_scan_number;
+    int has_warned_about_progressive_mode;
+} JPEGOtherSettings;
+
+int TIFFFillStrip(TIFF *tif, uint32_t strip);
+int TIFFFillTile(TIFF *tif, uint32_t tile);
+int TIFFReInitJPEG_12(TIFF *tif, const JPEGOtherSettings *otherSettings,
+                      int scheme, int is_encode);
+int TIFFJPEGIsFullStripRequired_12(TIFF *tif);
 
 /* We undefine FAR to avoid conflict with JPEG definition */
 
@@ -62,7 +86,7 @@
   a conflicting typedef given the headers which are included.
 */
 #if defined(__BORLANDC__) || defined(__MINGW32__)
-# define XMD_H 1
+#define XMD_H 1
 #endif
 
 /*
@@ -80,10 +104,10 @@
 
 /* Define "boolean" as unsigned char, not int, per Windows custom. */
 #if defined(__WIN32__) && !defined(__MINGW32__)
-# ifndef __RPCNDR_H__            /* don't conflict if rpcndr.h already read */
-   typedef unsigned char boolean;
-# endif
-# define HAVE_BOOLEAN            /* prevent jmorecfg.h from redefining it */
+#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */
+typedef unsigned char boolean;
+#endif
+#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */
 #endif
 
 #if defined(USE_SYSTEM_LIBJPEG)
@@ -97,15 +121,54 @@
 #include "third_party/libjpeg/jpeglib.h"
 #endif
 
-/* 
+/* Do optional compile-time version check */
+#if defined(EXPECTED_JPEG_LIB_VERSION) && !defined(LIBJPEG_12_PATH)
+#if EXPECTED_JPEG_LIB_VERSION != JPEG_LIB_VERSION
+#error EXPECTED_JPEG_LIB_VERSION != JPEG_LIB_VERSION
+#endif
+#endif
+
+/*
  * Do we want to do special processing suitable for when JSAMPLE is a
- * 16bit value?  
+ * 16bit value?
  */
 
+/* HAVE_JPEGTURBO_DUAL_MODE_8_12 is defined for libjpeg-turbo >= 2.2 which
+ * adds a dual-mode 8/12 bit API in the same library.
+ */
+
+#if defined(HAVE_JPEGTURBO_DUAL_MODE_8_12)
+#define JPEG_DUAL_MODE_8_12
+/* Start by undefining BITS_IN_JSAMPLE which is always set to 8 in libjpeg-turbo
+ * >= 2.2 Cf
+ * https://github.com/libjpeg-turbo/libjpeg-turbo/commit/8b9bc4b9635a2a047fb23ebe70c9acd728d3f99b
+ */
+#undef BITS_IN_JSAMPLE
+/* libjpeg-turbo >= 2.2 adds J12xxxx datatypes for the 12-bit mode. */
+#if defined(FROM_TIF_JPEG_12)
+#define BITS_IN_JSAMPLE 12
+#define TIFF_JSAMPLE J12SAMPLE
+#define TIFF_JSAMPARRAY J12SAMPARRAY
+#define TIFF_JSAMPIMAGE J12SAMPIMAGE
+#define TIFF_JSAMPROW J12SAMPROW
+#else
+#define BITS_IN_JSAMPLE 8
+#define TIFF_JSAMPLE JSAMPLE
+#define TIFF_JSAMPARRAY JSAMPARRAY
+#define TIFF_JSAMPIMAGE JSAMPIMAGE
+#define TIFF_JSAMPROW JSAMPROW
+#endif
+#else
+#define TIFF_JSAMPLE JSAMPLE
+#define TIFF_JSAMPARRAY JSAMPARRAY
+#define TIFF_JSAMPIMAGE JSAMPIMAGE
+#define TIFF_JSAMPROW JSAMPROW
+#endif
+
 #if defined(JPEG_LIB_MK1)
-#  define JPEG_LIB_MK1_OR_12BIT 1
+#define JPEG_LIB_MK1_OR_12BIT 1
 #elif BITS_IN_JSAMPLE == 12
-#  define JPEG_LIB_MK1_OR_12BIT 1
+#define JPEG_LIB_MK1_OR_12BIT 1
 #endif
 
 /*
@@ -123,9 +186,9 @@
  * On some machines it may be worthwhile to use _setjmp or sigsetjmp
  * in place of plain setjmp.  These macros will make it easier.
  */
-#define SETJMP(jbuf)		setjmp(jbuf)
-#define LONGJMP(jbuf,code)	longjmp(jbuf,code)
-#define JMP_BUF			jmp_buf
+#define SETJMP(jbuf) setjmp(jbuf)
+#define LONGJMP(jbuf, code) longjmp(jbuf, code)
+#define JMP_BUF jmp_buf
 
 typedef struct jpeg_destination_mgr jpeg_destination_mgr;
 typedef struct jpeg_source_mgr jpeg_source_mgr;
@@ -144,68 +207,60 @@
  *     so we can safely cast JPEGState* -> jpeg_xxx_struct*
  *     and vice versa!
  */
-typedef struct {
-	union {
-		struct jpeg_compress_struct c;
-		struct jpeg_decompress_struct d;
-		struct jpeg_common_struct comm;
-	} cinfo;			/* NB: must be first */
-	int             cinfo_initialized;
+typedef struct
+{
+    union
+    {
+        struct jpeg_compress_struct c;
+        struct jpeg_decompress_struct d;
+        struct jpeg_common_struct comm;
+    } cinfo; /* NB: must be first */
+    int cinfo_initialized;
 
-	jpeg_error_mgr	err;		/* libjpeg error manager */
-	JMP_BUF		exit_jmpbuf;	/* for catching libjpeg failures */
-	
-	struct jpeg_progress_mgr progress;
-	/*
-	 * The following two members could be a union, but
-	 * they're small enough that it's not worth the effort.
-	 */
-	jpeg_destination_mgr dest;	/* data dest for compression */
-	jpeg_source_mgr	src;		/* data source for decompression */
-					/* private state */
-	TIFF*		tif;		/* back link needed by some code */
-	uint16		photometric;	/* copy of PhotometricInterpretation */
-	uint16		h_sampling;	/* luminance sampling factors */
-	uint16		v_sampling;
-	tmsize_t   	bytesperline;	/* decompressed bytes per scanline */
-	/* pointers to intermediate buffers when processing downsampled data */
-	JSAMPARRAY	ds_buffer[MAX_COMPONENTS];
-	int		scancount;	/* number of "scanlines" accumulated */
-	int		samplesperclump;
+    jpeg_error_mgr err;  /* libjpeg error manager */
+    JMP_BUF exit_jmpbuf; /* for catching libjpeg failures */
 
-	TIFFVGetMethod	vgetparent;	/* super-class method */
-	TIFFVSetMethod	vsetparent;	/* super-class method */
-	TIFFPrintMethod printdir;	/* super-class method */
-	TIFFStripMethod	defsparent;	/* super-class method */
-	TIFFTileMethod	deftparent;	/* super-class method */
-					/* pseudo-tag fields */
-	void*		jpegtables;	/* JPEGTables tag value, or NULL */
-	uint32		jpegtables_length; /* number of bytes in same */
-	int		jpegquality;	/* Compression quality level */
-	int		jpegcolormode;	/* Auto RGB<=>YCbCr convert? */
-	int		jpegtablesmode;	/* What to put in JPEGTables */
+    struct jpeg_progress_mgr progress;
+    /*
+     * The following two members could be a union, but
+     * they're small enough that it's not worth the effort.
+     */
+    jpeg_destination_mgr dest; /* data dest for compression */
+    jpeg_source_mgr src;       /* data source for decompression */
+                               /* private state */
+    TIFF *tif;                 /* back link needed by some code */
+    uint16_t photometric;      /* copy of PhotometricInterpretation */
+    uint16_t h_sampling;       /* luminance sampling factors */
+    uint16_t v_sampling;
+    tmsize_t bytesperline; /* decompressed bytes per scanline */
+    /* pointers to intermediate buffers when processing downsampled data */
+    TIFF_JSAMPARRAY ds_buffer[MAX_COMPONENTS];
+    int scancount; /* number of "scanlines" accumulated */
+    int samplesperclump;
 
-        int             ycbcrsampling_fetched;
-        int             max_allowed_scan_number;
+    JPEGOtherSettings otherSettings;
 } JPEGState;
 
-#define	JState(tif)	((JPEGState*)(tif)->tif_data)
+#define JState(tif) ((JPEGState *)(tif)->tif_data)
 
-static int JPEGDecode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s);
-static int JPEGDecodeRaw(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s);
-static int JPEGEncode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s);
-static int JPEGEncodeRaw(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s);
-static int JPEGInitializeLibJPEG(TIFF * tif, int decode );
-static int DecodeRowError(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s);
+static int JPEGDecode(TIFF *tif, uint8_t *buf, tmsize_t cc, uint16_t s);
+static int JPEGDecodeRaw(TIFF *tif, uint8_t *buf, tmsize_t cc, uint16_t s);
+static int JPEGEncode(TIFF *tif, uint8_t *buf, tmsize_t cc, uint16_t s);
+static int JPEGEncodeRaw(TIFF *tif, uint8_t *buf, tmsize_t cc, uint16_t s);
+static int JPEGInitializeLibJPEG(TIFF *tif, int decode);
+static int DecodeRowError(TIFF *tif, uint8_t *buf, tmsize_t cc, uint16_t s);
 
-#define	FIELD_JPEGTABLES	(FIELD_CODEC+0)
+#define FIELD_JPEGTABLES (FIELD_CODEC + 0)
 
 static const TIFFField jpegFields[] = {
-    { TIFFTAG_JPEGTABLES, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_C32_UINT8, FIELD_JPEGTABLES, FALSE, TRUE, "JPEGTables", NULL },
-    { TIFFTAG_JPEGQUALITY, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "", NULL },
-    { TIFFTAG_JPEGCOLORMODE, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "", NULL },
-    { TIFFTAG_JPEGTABLESMODE, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "", NULL }
-};
+    {TIFFTAG_JPEGTABLES, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8,
+     TIFF_SETGET_C32_UINT8, FIELD_JPEGTABLES, FALSE, TRUE, "JPEGTables", NULL},
+    {TIFFTAG_JPEGQUALITY, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT,
+     TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "", NULL},
+    {TIFFTAG_JPEGCOLORMODE, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT,
+     TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "", NULL},
+    {TIFFTAG_JPEGTABLESMODE, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT,
+     TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "", NULL}};
 
 /*
  * libjpeg interface layer.
@@ -221,16 +276,16 @@
  * IJG routines from jerror.c).  These are used for both
  * compression and decompression.
  */
-static void
-TIFFjpeg_error_exit(j_common_ptr cinfo)
+static void TIFFjpeg_error_exit(j_common_ptr cinfo)
 {
-	JPEGState *sp = (JPEGState *) cinfo;	/* NB: cinfo assumed first */
-	char buffer[JMSG_LENGTH_MAX];
+    JPEGState *sp = (JPEGState *)cinfo; /* NB: cinfo assumed first */
+    char buffer[JMSG_LENGTH_MAX];
 
-	(*cinfo->err->format_message) (cinfo, buffer);
-	TIFFErrorExt(sp->tif->tif_clientdata, "JPEGLib", "%s", buffer);		/* display the error message */
-	jpeg_abort(cinfo);			/* clean up libjpeg state */
-	LONGJMP(sp->exit_jmpbuf, 1);		/* return to libtiff caller */
+    (*cinfo->err->format_message)(cinfo, buffer);
+    TIFFErrorExtR(sp->tif, "JPEGLib", "%s",
+                  buffer);       /* display the error message */
+    jpeg_abort(cinfo);           /* clean up libjpeg state */
+    LONGJMP(sp->exit_jmpbuf, 1); /* return to libtiff caller */
 }
 
 /*
@@ -238,203 +293,216 @@
  * since error_exit does its own thing and trace_level
  * is never set > 0.
  */
-static void
-TIFFjpeg_output_message(j_common_ptr cinfo)
+static void TIFFjpeg_output_message(j_common_ptr cinfo)
 {
-	char buffer[JMSG_LENGTH_MAX];
+    char buffer[JMSG_LENGTH_MAX];
 
-	(*cinfo->err->format_message) (cinfo, buffer);
-	TIFFWarningExt(((JPEGState *) cinfo)->tif->tif_clientdata, "JPEGLib", "%s", buffer);
+    (*cinfo->err->format_message)(cinfo, buffer);
+    TIFFWarningExtR(((JPEGState *)cinfo)->tif, "JPEGLib", "%s", buffer);
 }
 
 /* Avoid the risk of denial-of-service on crafted JPEGs with an insane */
 /* number of scans. */
-/* See http://www.libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf */
-static void
-TIFFjpeg_progress_monitor(j_common_ptr cinfo)
+/* See
+ * http://www.libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf
+ */
+static void TIFFjpeg_progress_monitor(j_common_ptr cinfo)
 {
-    JPEGState *sp = (JPEGState *) cinfo;	/* NB: cinfo assumed first */
+    JPEGState *sp = (JPEGState *)cinfo; /* NB: cinfo assumed first */
     if (cinfo->is_decompressor)
     {
-        const int scan_no =
-            ((j_decompress_ptr)cinfo)->input_scan_number;
-        if (scan_no >= sp->max_allowed_scan_number)
+        const int scan_no = ((j_decompress_ptr)cinfo)->input_scan_number;
+        if (scan_no >= sp->otherSettings.max_allowed_scan_number)
         {
-            TIFFErrorExt(((JPEGState *) cinfo)->tif->tif_clientdata, 
-                     "TIFFjpeg_progress_monitor",
-                     "Scan number %d exceeds maximum scans (%d). This limit "
-                     "can be raised through the LIBTIFF_JPEG_MAX_ALLOWED_SCAN_NUMBER "
-                     "environment variable.",
-                     scan_no, sp->max_allowed_scan_number);
+            TIFFErrorExtR(
+                ((JPEGState *)cinfo)->tif, "TIFFjpeg_progress_monitor",
+                "Scan number %d exceeds maximum scans (%d). This limit "
+                "can be raised through the "
+                "LIBTIFF_JPEG_MAX_ALLOWED_SCAN_NUMBER "
+                "environment variable.",
+                scan_no, sp->otherSettings.max_allowed_scan_number);
 
-            jpeg_abort(cinfo);			/* clean up libjpeg state */
-            LONGJMP(sp->exit_jmpbuf, 1);		/* return to libtiff caller */
+            jpeg_abort(cinfo);           /* clean up libjpeg state */
+            LONGJMP(sp->exit_jmpbuf, 1); /* return to libtiff caller */
         }
     }
 }
 
-
 /*
  * Interface routines.  This layer of routines exists
  * primarily to limit side-effects from using setjmp.
  * Also, normal/error returns are converted into return
  * values per libtiff practice.
  */
-#define	CALLJPEG(sp, fail, op)	(SETJMP((sp)->exit_jmpbuf) ? (fail) : (op))
-#define	CALLVJPEG(sp, op)	CALLJPEG(sp, 0, ((op),1))
+#define CALLJPEG(sp, fail, op) (SETJMP((sp)->exit_jmpbuf) ? (fail) : (op))
+#define CALLVJPEG(sp, op) CALLJPEG(sp, 0, ((op), 1))
 
-static int
-TIFFjpeg_create_compress(JPEGState* sp)
+static int TIFFjpeg_create_compress(JPEGState *sp)
 {
-	/* initialize JPEG error handling */
-	sp->cinfo.c.err = jpeg_std_error(&sp->err);
-	sp->err.error_exit = TIFFjpeg_error_exit;
-	sp->err.output_message = TIFFjpeg_output_message;
+    /* initialize JPEG error handling */
+    sp->cinfo.c.err = jpeg_std_error(&sp->err);
+    sp->err.error_exit = TIFFjpeg_error_exit;
+    sp->err.output_message = TIFFjpeg_output_message;
 
-	/* set client_data to avoid UMR warning from tools like Purify */
-	sp->cinfo.c.client_data = NULL;
+    /* set client_data to avoid UMR warning from tools like Purify */
+    sp->cinfo.c.client_data = NULL;
 
-	return CALLVJPEG(sp, jpeg_create_compress(&sp->cinfo.c));
+    return CALLVJPEG(sp, jpeg_create_compress(&sp->cinfo.c));
 }
 
-static int
-TIFFjpeg_create_decompress(JPEGState* sp)
+static int TIFFjpeg_create_decompress(JPEGState *sp)
 {
-	/* initialize JPEG error handling */
-	sp->cinfo.d.err = jpeg_std_error(&sp->err);
-	sp->err.error_exit = TIFFjpeg_error_exit;
-	sp->err.output_message = TIFFjpeg_output_message;
+    /* initialize JPEG error handling */
+    sp->cinfo.d.err = jpeg_std_error(&sp->err);
+    sp->err.error_exit = TIFFjpeg_error_exit;
+    sp->err.output_message = TIFFjpeg_output_message;
 
-	/* set client_data to avoid UMR warning from tools like Purify */
-	sp->cinfo.d.client_data = NULL;
+    /* set client_data to avoid UMR warning from tools like Purify */
+    sp->cinfo.d.client_data = NULL;
 
-	return CALLVJPEG(sp, jpeg_create_decompress(&sp->cinfo.d));
+    return CALLVJPEG(sp, jpeg_create_decompress(&sp->cinfo.d));
 }
 
-static int
-TIFFjpeg_set_defaults(JPEGState* sp)
+static int TIFFjpeg_set_defaults(JPEGState *sp)
 {
-	return CALLVJPEG(sp, jpeg_set_defaults(&sp->cinfo.c));
+    return CALLVJPEG(sp, jpeg_set_defaults(&sp->cinfo.c));
 }
 
-static int
-TIFFjpeg_set_colorspace(JPEGState* sp, J_COLOR_SPACE colorspace)
+static int TIFFjpeg_set_colorspace(JPEGState *sp, J_COLOR_SPACE colorspace)
 {
-	return CALLVJPEG(sp, jpeg_set_colorspace(&sp->cinfo.c, colorspace));
+    return CALLVJPEG(sp, jpeg_set_colorspace(&sp->cinfo.c, colorspace));
 }
 
-static int
-TIFFjpeg_set_quality(JPEGState* sp, int quality, boolean force_baseline)
+static int TIFFjpeg_set_quality(JPEGState *sp, int quality,
+                                boolean force_baseline)
 {
-	return CALLVJPEG(sp,
-	    jpeg_set_quality(&sp->cinfo.c, quality, force_baseline));
+    return CALLVJPEG(sp,
+                     jpeg_set_quality(&sp->cinfo.c, quality, force_baseline));
 }
 
-static int
-TIFFjpeg_suppress_tables(JPEGState* sp, boolean suppress)
+static int TIFFjpeg_suppress_tables(JPEGState *sp, boolean suppress)
 {
-	return CALLVJPEG(sp, jpeg_suppress_tables(&sp->cinfo.c, suppress));
+    return CALLVJPEG(sp, jpeg_suppress_tables(&sp->cinfo.c, suppress));
 }
 
-static int
-TIFFjpeg_start_compress(JPEGState* sp, boolean write_all_tables)
+static int TIFFjpeg_start_compress(JPEGState *sp, boolean write_all_tables)
 {
-	return CALLVJPEG(sp,
-	    jpeg_start_compress(&sp->cinfo.c, write_all_tables));
+    return CALLVJPEG(sp, jpeg_start_compress(&sp->cinfo.c, write_all_tables));
 }
 
-static int
-TIFFjpeg_write_scanlines(JPEGState* sp, JSAMPARRAY scanlines, int num_lines)
+static int TIFFjpeg_write_scanlines(JPEGState *sp, TIFF_JSAMPARRAY scanlines,
+                                    int num_lines)
 {
-	return CALLJPEG(sp, -1, (int) jpeg_write_scanlines(&sp->cinfo.c,
-	    scanlines, (JDIMENSION) num_lines));
+#if defined(HAVE_JPEGTURBO_DUAL_MODE_8_12) && BITS_IN_JSAMPLE == 12
+    return CALLJPEG(sp, -1,
+                    (int)jpeg12_write_scanlines(&sp->cinfo.c, scanlines,
+                                                (JDIMENSION)num_lines));
+#else
+    return CALLJPEG(sp, -1,
+                    (int)jpeg_write_scanlines(&sp->cinfo.c, scanlines,
+                                              (JDIMENSION)num_lines));
+#endif
 }
 
-static int
-TIFFjpeg_write_raw_data(JPEGState* sp, JSAMPIMAGE data, int num_lines)
+static int TIFFjpeg_write_raw_data(JPEGState *sp, TIFF_JSAMPIMAGE data,
+                                   int num_lines)
 {
-	return CALLJPEG(sp, -1, (int) jpeg_write_raw_data(&sp->cinfo.c,
-	    data, (JDIMENSION) num_lines));
+#if defined(HAVE_JPEGTURBO_DUAL_MODE_8_12) && BITS_IN_JSAMPLE == 12
+    return CALLJPEG(
+        sp, -1,
+        (int)jpeg12_write_raw_data(&sp->cinfo.c, data, (JDIMENSION)num_lines));
+#else
+    return CALLJPEG(
+        sp, -1,
+        (int)jpeg_write_raw_data(&sp->cinfo.c, data, (JDIMENSION)num_lines));
+#endif
 }
 
-static int
-TIFFjpeg_finish_compress(JPEGState* sp)
+static int TIFFjpeg_finish_compress(JPEGState *sp)
 {
-	return CALLVJPEG(sp, jpeg_finish_compress(&sp->cinfo.c));
+    return CALLVJPEG(sp, jpeg_finish_compress(&sp->cinfo.c));
 }
 
-static int
-TIFFjpeg_write_tables(JPEGState* sp)
+static int TIFFjpeg_write_tables(JPEGState *sp)
 {
-	return CALLVJPEG(sp, jpeg_write_tables(&sp->cinfo.c));
+    return CALLVJPEG(sp, jpeg_write_tables(&sp->cinfo.c));
 }
 
-static int
-TIFFjpeg_read_header(JPEGState* sp, boolean require_image)
+static int TIFFjpeg_read_header(JPEGState *sp, boolean require_image)
 {
-	return CALLJPEG(sp, -1, jpeg_read_header(&sp->cinfo.d, require_image));
+    return CALLJPEG(sp, -1, jpeg_read_header(&sp->cinfo.d, require_image));
 }
 
-static int
-TIFFjpeg_has_multiple_scans(JPEGState* sp)
+static int TIFFjpeg_has_multiple_scans(JPEGState *sp)
 {
-	return CALLJPEG(sp, 0, jpeg_has_multiple_scans(&sp->cinfo.d));
+    return CALLJPEG(sp, 0, jpeg_has_multiple_scans(&sp->cinfo.d));
 }
 
-static int
-TIFFjpeg_start_decompress(JPEGState* sp)
+static int TIFFjpeg_start_decompress(JPEGState *sp)
 {
-        const char* sz_max_allowed_scan_number;
-        /* progress monitor */
-        sp->cinfo.d.progress = &sp->progress;
-        sp->progress.progress_monitor = TIFFjpeg_progress_monitor;
-        sp->max_allowed_scan_number = 100;
-        sz_max_allowed_scan_number = getenv("LIBTIFF_JPEG_MAX_ALLOWED_SCAN_NUMBER");
-        if( sz_max_allowed_scan_number )
-            sp->max_allowed_scan_number = atoi(sz_max_allowed_scan_number);
+    const char *sz_max_allowed_scan_number;
+    /* progress monitor */
+    sp->cinfo.d.progress = &sp->progress;
+    sp->progress.progress_monitor = TIFFjpeg_progress_monitor;
+    sp->otherSettings.max_allowed_scan_number = 100;
+    sz_max_allowed_scan_number = getenv("LIBTIFF_JPEG_MAX_ALLOWED_SCAN_NUMBER");
+    if (sz_max_allowed_scan_number)
+        sp->otherSettings.max_allowed_scan_number =
+            atoi(sz_max_allowed_scan_number);
 
-	return CALLVJPEG(sp, jpeg_start_decompress(&sp->cinfo.d));
+    return CALLVJPEG(sp, jpeg_start_decompress(&sp->cinfo.d));
 }
 
-static int
-TIFFjpeg_read_scanlines(JPEGState* sp, JSAMPARRAY scanlines, int max_lines)
+static int TIFFjpeg_read_scanlines(JPEGState *sp, TIFF_JSAMPARRAY scanlines,
+                                   int max_lines)
 {
-	return CALLJPEG(sp, -1, (int) jpeg_read_scanlines(&sp->cinfo.d,
-	    scanlines, (JDIMENSION) max_lines));
+#if defined(HAVE_JPEGTURBO_DUAL_MODE_8_12) && BITS_IN_JSAMPLE == 12
+    return CALLJPEG(sp, -1,
+                    (int)jpeg12_read_scanlines(&sp->cinfo.d, scanlines,
+                                               (JDIMENSION)max_lines));
+#else
+    return CALLJPEG(sp, -1,
+                    (int)jpeg_read_scanlines(&sp->cinfo.d, scanlines,
+                                             (JDIMENSION)max_lines));
+#endif
 }
 
-static int
-TIFFjpeg_read_raw_data(JPEGState* sp, JSAMPIMAGE data, int max_lines)
+static int TIFFjpeg_read_raw_data(JPEGState *sp, TIFF_JSAMPIMAGE data,
+                                  int max_lines)
 {
-	return CALLJPEG(sp, -1, (int) jpeg_read_raw_data(&sp->cinfo.d,
-	    data, (JDIMENSION) max_lines));
+#if defined(HAVE_JPEGTURBO_DUAL_MODE_8_12) && BITS_IN_JSAMPLE == 12
+    return CALLJPEG(
+        sp, -1,
+        (int)jpeg12_read_raw_data(&sp->cinfo.d, data, (JDIMENSION)max_lines));
+#else
+    return CALLJPEG(
+        sp, -1,
+        (int)jpeg_read_raw_data(&sp->cinfo.d, data, (JDIMENSION)max_lines));
+#endif
 }
 
-static int
-TIFFjpeg_finish_decompress(JPEGState* sp)
+static int TIFFjpeg_finish_decompress(JPEGState *sp)
 {
-	return CALLJPEG(sp, -1, (int) jpeg_finish_decompress(&sp->cinfo.d));
+    return CALLJPEG(sp, -1, (int)jpeg_finish_decompress(&sp->cinfo.d));
 }
 
-static int
-TIFFjpeg_abort(JPEGState* sp)
+static int TIFFjpeg_abort(JPEGState *sp)
 {
-	return CALLVJPEG(sp, jpeg_abort(&sp->cinfo.comm));
+    return CALLVJPEG(sp, jpeg_abort(&sp->cinfo.comm));
 }
 
-static int
-TIFFjpeg_destroy(JPEGState* sp)
+static int TIFFjpeg_destroy(JPEGState *sp)
 {
-	return CALLVJPEG(sp, jpeg_destroy(&sp->cinfo.comm));
+    return CALLVJPEG(sp, jpeg_destroy(&sp->cinfo.comm));
 }
 
-static JSAMPARRAY
-TIFFjpeg_alloc_sarray(JPEGState* sp, int pool_id,
-		      JDIMENSION samplesperrow, JDIMENSION numrows)
+static JSAMPARRAY TIFFjpeg_alloc_sarray(JPEGState *sp, int pool_id,
+                                        JDIMENSION samplesperrow,
+                                        JDIMENSION numrows)
 {
-	return CALLJPEG(sp, (JSAMPARRAY) NULL,
-	    (*sp->cinfo.comm.mem->alloc_sarray)
-		(&sp->cinfo.comm, pool_id, samplesperrow, numrows));
+    return CALLJPEG(sp, (JSAMPARRAY)NULL,
+                    (*sp->cinfo.comm.mem->alloc_sarray)(
+                        &sp->cinfo.comm, pool_id, samplesperrow, numrows));
 }
 
 /*
@@ -443,129 +511,128 @@
  * libtiff output buffer.
  */
 
-static void
-std_init_destination(j_compress_ptr cinfo)
+static void std_init_destination(j_compress_ptr cinfo)
 {
-	JPEGState* sp = (JPEGState*) cinfo;
-	TIFF* tif = sp->tif;
+    JPEGState *sp = (JPEGState *)cinfo;
+    TIFF *tif = sp->tif;
 
-	sp->dest.next_output_byte = (JOCTET*) tif->tif_rawdata;
-	sp->dest.free_in_buffer = (size_t) tif->tif_rawdatasize;
+    sp->dest.next_output_byte = (JOCTET *)tif->tif_rawdata;
+    sp->dest.free_in_buffer = (size_t)tif->tif_rawdatasize;
 }
 
-static boolean
-std_empty_output_buffer(j_compress_ptr cinfo)
+static boolean std_empty_output_buffer(j_compress_ptr cinfo)
 {
-	JPEGState* sp = (JPEGState*) cinfo;
-	TIFF* tif = sp->tif;
+    JPEGState *sp = (JPEGState *)cinfo;
+    TIFF *tif = sp->tif;
 
-	/* the entire buffer has been filled */
-	tif->tif_rawcc = tif->tif_rawdatasize;
+    /* the entire buffer has been filled */
+    tif->tif_rawcc = tif->tif_rawdatasize;
 
 #ifdef IPPJ_HUFF
-       /*
-        * The Intel IPP performance library does not necessarily fill up
-        * the whole output buffer on each pass, so only dump out the parts
-        * that have been filled.
-        *   http://trac.osgeo.org/gdal/wiki/JpegIPP
-        */
-       if ( sp->dest.free_in_buffer >= 0 ) {
-               tif->tif_rawcc = tif->tif_rawdatasize - sp->dest.free_in_buffer;
-       }
+    /*
+     * The Intel IPP performance library does not necessarily fill up
+     * the whole output buffer on each pass, so only dump out the parts
+     * that have been filled.
+     *   http://trac.osgeo.org/gdal/wiki/JpegIPP
+     */
+    if (sp->dest.free_in_buffer >= 0)
+    {
+        tif->tif_rawcc = tif->tif_rawdatasize - sp->dest.free_in_buffer;
+    }
 #endif
 
-	TIFFFlushData1(tif);
-	sp->dest.next_output_byte = (JOCTET*) tif->tif_rawdata;
-	sp->dest.free_in_buffer = (size_t) tif->tif_rawdatasize;
+    if (!TIFFFlushData1(tif))
+        return FALSE;
+    sp->dest.next_output_byte = (JOCTET *)tif->tif_rawdata;
+    sp->dest.free_in_buffer = (size_t)tif->tif_rawdatasize;
 
-	return (TRUE);
+    return (TRUE);
 }
 
-static void
-std_term_destination(j_compress_ptr cinfo)
+static void std_term_destination(j_compress_ptr cinfo)
 {
-	JPEGState* sp = (JPEGState*) cinfo;
-	TIFF* tif = sp->tif;
+    JPEGState *sp = (JPEGState *)cinfo;
+    TIFF *tif = sp->tif;
 
-	tif->tif_rawcp = (uint8*) sp->dest.next_output_byte;
-	tif->tif_rawcc =
-	    tif->tif_rawdatasize - (tmsize_t) sp->dest.free_in_buffer;
-	/* NB: libtiff does the final buffer flush */
+    tif->tif_rawcp = (uint8_t *)sp->dest.next_output_byte;
+    tif->tif_rawcc = tif->tif_rawdatasize - (tmsize_t)sp->dest.free_in_buffer;
+    /* NB: libtiff does the final buffer flush */
 }
 
-static void
-TIFFjpeg_data_dest(JPEGState* sp, TIFF* tif)
+static void TIFFjpeg_data_dest(JPEGState *sp, TIFF *tif)
 {
-	(void) tif;
-	sp->cinfo.c.dest = &sp->dest;
-	sp->dest.init_destination = std_init_destination;
-	sp->dest.empty_output_buffer = std_empty_output_buffer;
-	sp->dest.term_destination = std_term_destination;
+    (void)tif;
+    sp->cinfo.c.dest = &sp->dest;
+    sp->dest.init_destination = std_init_destination;
+    sp->dest.empty_output_buffer = std_empty_output_buffer;
+    sp->dest.term_destination = std_term_destination;
 }
 
 /*
  * Alternate destination manager for outputting to JPEGTables field.
  */
 
-static void
-tables_init_destination(j_compress_ptr cinfo)
+static void tables_init_destination(j_compress_ptr cinfo)
 {
-	JPEGState* sp = (JPEGState*) cinfo;
+    JPEGState *sp = (JPEGState *)cinfo;
 
-	/* while building, jpegtables_length is allocated buffer size */
-	sp->dest.next_output_byte = (JOCTET*) sp->jpegtables;
-	sp->dest.free_in_buffer = (size_t) sp->jpegtables_length;
+    /* while building, otherSettings.jpegtables_length is allocated buffer size
+     */
+    sp->dest.next_output_byte = (JOCTET *)sp->otherSettings.jpegtables;
+    sp->dest.free_in_buffer = (size_t)sp->otherSettings.jpegtables_length;
 }
 
-static boolean
-tables_empty_output_buffer(j_compress_ptr cinfo)
+static boolean tables_empty_output_buffer(j_compress_ptr cinfo)
 {
-	JPEGState* sp = (JPEGState*) cinfo;
-	void* newbuf;
+    JPEGState *sp = (JPEGState *)cinfo;
+    void *newbuf;
 
-	/* the entire buffer has been filled; enlarge it by 1000 bytes */
-	newbuf = _TIFFrealloc((void*) sp->jpegtables,
-			      (tmsize_t) (sp->jpegtables_length + 1000));
-	if (newbuf == NULL)
-		ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 100);
-	sp->dest.next_output_byte = (JOCTET*) newbuf + sp->jpegtables_length;
-	sp->dest.free_in_buffer = (size_t) 1000;
-	sp->jpegtables = newbuf;
-	sp->jpegtables_length += 1000;
-	return (TRUE);
+    /* the entire buffer has been filled; enlarge it by 1000 bytes */
+    newbuf =
+        _TIFFreallocExt(sp->tif, (void *)sp->otherSettings.jpegtables,
+                        (tmsize_t)(sp->otherSettings.jpegtables_length + 1000));
+    if (newbuf == NULL)
+        ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 100);
+    sp->dest.next_output_byte =
+        (JOCTET *)newbuf + sp->otherSettings.jpegtables_length;
+    sp->dest.free_in_buffer = (size_t)1000;
+    sp->otherSettings.jpegtables = newbuf;
+    sp->otherSettings.jpegtables_length += 1000;
+    return (TRUE);
 }
 
-static void
-tables_term_destination(j_compress_ptr cinfo)
+static void tables_term_destination(j_compress_ptr cinfo)
 {
-	JPEGState* sp = (JPEGState*) cinfo;
+    JPEGState *sp = (JPEGState *)cinfo;
 
-	/* set tables length to number of bytes actually emitted */
-	sp->jpegtables_length -= (uint32) sp->dest.free_in_buffer;
+    /* set tables length to number of bytes actually emitted */
+    sp->otherSettings.jpegtables_length -= (uint32_t)sp->dest.free_in_buffer;
 }
 
-static int
-TIFFjpeg_tables_dest(JPEGState* sp, TIFF* tif)
+static int TIFFjpeg_tables_dest(JPEGState *sp, TIFF *tif)
 {
-	(void) tif;
-	/*
-	 * Allocate a working buffer for building tables.
-	 * Initial size is 1000 bytes, which is usually adequate.
-	 */
-	if (sp->jpegtables)
-		_TIFFfree(sp->jpegtables);
-	sp->jpegtables_length = 1000;
-	sp->jpegtables = (void*) _TIFFmalloc((tmsize_t) sp->jpegtables_length);
-	if (sp->jpegtables == NULL) {
-		sp->jpegtables_length = 0;
-		TIFFErrorExt(sp->tif->tif_clientdata, "TIFFjpeg_tables_dest", "No space for JPEGTables");
-		return (0);
-	}
-	sp->cinfo.c.dest = &sp->dest;
-	sp->dest.init_destination = tables_init_destination;
-	sp->dest.empty_output_buffer = tables_empty_output_buffer;
-	sp->dest.term_destination = tables_term_destination;
-	return (1);
+    (void)tif;
+    /*
+     * Allocate a working buffer for building tables.
+     * Initial size is 1000 bytes, which is usually adequate.
+     */
+    if (sp->otherSettings.jpegtables)
+        _TIFFfreeExt(tif, sp->otherSettings.jpegtables);
+    sp->otherSettings.jpegtables_length = 1000;
+    sp->otherSettings.jpegtables = (void *)_TIFFmallocExt(
+        tif, (tmsize_t)sp->otherSettings.jpegtables_length);
+    if (sp->otherSettings.jpegtables == NULL)
+    {
+        sp->otherSettings.jpegtables_length = 0;
+        TIFFErrorExtR(sp->tif, "TIFFjpeg_tables_dest",
+                      "No space for JPEGTables");
+        return (0);
+    }
+    sp->cinfo.c.dest = &sp->dest;
+    sp->dest.init_destination = tables_init_destination;
+    sp->dest.empty_output_buffer = tables_empty_output_buffer;
+    sp->dest.term_destination = tables_term_destination;
+    return (1);
 }
 
 /*
@@ -573,86 +640,86 @@
  * These routines supply compressed data to libjpeg.
  */
 
-static void
-std_init_source(j_decompress_ptr cinfo)
+static void std_init_source(j_decompress_ptr cinfo)
 {
-	JPEGState* sp = (JPEGState*) cinfo;
-	TIFF* tif = sp->tif;
+    JPEGState *sp = (JPEGState *)cinfo;
+    TIFF *tif = sp->tif;
 
-	sp->src.next_input_byte = (const JOCTET*) tif->tif_rawdata;
-	sp->src.bytes_in_buffer = (size_t) tif->tif_rawcc;
+    sp->src.next_input_byte = (const JOCTET *)tif->tif_rawdata;
+    sp->src.bytes_in_buffer = (size_t)tif->tif_rawcc;
 }
 
-static boolean
-std_fill_input_buffer(j_decompress_ptr cinfo)
+static boolean std_fill_input_buffer(j_decompress_ptr cinfo)
 {
-	JPEGState* sp = (JPEGState* ) cinfo;
-	static const JOCTET dummy_EOI[2] = { 0xFF, JPEG_EOI };
+    JPEGState *sp = (JPEGState *)cinfo;
+    static const JOCTET dummy_EOI[2] = {0xFF, JPEG_EOI};
 
 #ifdef IPPJ_HUFF
-        /*
-         * The Intel IPP performance library does not necessarily read the whole
-         * input buffer in one pass, so it is possible to get here with data
-         * yet to read. 
-         * 
-         * We just return without doing anything, until the entire buffer has
-         * been read.  
-         * http://trac.osgeo.org/gdal/wiki/JpegIPP
-         */
-        if( sp->src.bytes_in_buffer > 0 ) {
-            return (TRUE);
-        }
+    /*
+     * The Intel IPP performance library does not necessarily read the whole
+     * input buffer in one pass, so it is possible to get here with data
+     * yet to read.
+     *
+     * We just return without doing anything, until the entire buffer has
+     * been read.
+     * http://trac.osgeo.org/gdal/wiki/JpegIPP
+     */
+    if (sp->src.bytes_in_buffer > 0)
+    {
+        return (TRUE);
+    }
 #endif
 
-	/*
-         * Normally the whole strip/tile is read and so we don't need to do
-         * a fill.  In the case of CHUNKY_STRIP_READ_SUPPORT we might not have
-         * all the data, but the rawdata is refreshed between scanlines and
-         * we push this into the io machinery in JPEGDecode(). 	 
-         * http://trac.osgeo.org/gdal/ticket/3894
-	 */
-        
-	WARNMS(cinfo, JWRN_JPEG_EOF);
-	/* insert a fake EOI marker */
-	sp->src.next_input_byte = dummy_EOI;
-	sp->src.bytes_in_buffer = 2;
-	return (TRUE);
+    /*
+     * Normally the whole strip/tile is read and so we don't need to do
+     * a fill.  In the case of CHUNKY_STRIP_READ_SUPPORT we might not have
+     * all the data, but the rawdata is refreshed between scanlines and
+     * we push this into the io machinery in JPEGDecode().
+     * http://trac.osgeo.org/gdal/ticket/3894
+     */
+
+    WARNMS(cinfo, JWRN_JPEG_EOF);
+    /* insert a fake EOI marker */
+    sp->src.next_input_byte = dummy_EOI;
+    sp->src.bytes_in_buffer = 2;
+    return (TRUE);
 }
 
-static void
-std_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
+static void std_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
 {
-	JPEGState* sp = (JPEGState*) cinfo;
+    JPEGState *sp = (JPEGState *)cinfo;
 
-	if (num_bytes > 0) {
-		if ((size_t)num_bytes > sp->src.bytes_in_buffer) {
-			/* oops, buffer overrun */
-			(void) std_fill_input_buffer(cinfo);
-		} else {
-			sp->src.next_input_byte += (size_t) num_bytes;
-			sp->src.bytes_in_buffer -= (size_t) num_bytes;
-		}
-	}
+    if (num_bytes > 0)
+    {
+        if ((size_t)num_bytes > sp->src.bytes_in_buffer)
+        {
+            /* oops, buffer overrun */
+            (void)std_fill_input_buffer(cinfo);
+        }
+        else
+        {
+            sp->src.next_input_byte += (size_t)num_bytes;
+            sp->src.bytes_in_buffer -= (size_t)num_bytes;
+        }
+    }
 }
 
-static void
-std_term_source(j_decompress_ptr cinfo)
+static void std_term_source(j_decompress_ptr cinfo)
 {
-	/* No work necessary here */
-	(void) cinfo;
+    /* No work necessary here */
+    (void)cinfo;
 }
 
-static void
-TIFFjpeg_data_src(JPEGState* sp)
+static void TIFFjpeg_data_src(JPEGState *sp)
 {
-	sp->cinfo.d.src = &sp->src;
-	sp->src.init_source = std_init_source;
-	sp->src.fill_input_buffer = std_fill_input_buffer;
-	sp->src.skip_input_data = std_skip_input_data;
-	sp->src.resync_to_restart = jpeg_resync_to_restart;
-	sp->src.term_source = std_term_source;
-	sp->src.bytes_in_buffer = 0;		/* for safety */
-	sp->src.next_input_byte = NULL;
+    sp->cinfo.d.src = &sp->src;
+    sp->src.init_source = std_init_source;
+    sp->src.fill_input_buffer = std_fill_input_buffer;
+    sp->src.skip_input_data = std_skip_input_data;
+    sp->src.resync_to_restart = jpeg_resync_to_restart;
+    sp->src.term_source = std_term_source;
+    sp->src.bytes_in_buffer = 0; /* for safety */
+    sp->src.next_input_byte = NULL;
 }
 
 /*
@@ -660,20 +727,18 @@
  * We can share all the code except for the init routine.
  */
 
-static void
-tables_init_source(j_decompress_ptr cinfo)
+static void tables_init_source(j_decompress_ptr cinfo)
 {
-	JPEGState* sp = (JPEGState*) cinfo;
+    JPEGState *sp = (JPEGState *)cinfo;
 
-	sp->src.next_input_byte = (const JOCTET*) sp->jpegtables;
-	sp->src.bytes_in_buffer = (size_t) sp->jpegtables_length;
+    sp->src.next_input_byte = (const JOCTET *)sp->otherSettings.jpegtables;
+    sp->src.bytes_in_buffer = (size_t)sp->otherSettings.jpegtables_length;
 }
 
-static void
-TIFFjpeg_tables_src(JPEGState* sp)
+static void TIFFjpeg_tables_src(JPEGState *sp)
 {
-	TIFFjpeg_data_src(sp);
-	sp->src.init_source = tables_init_source;
+    TIFFjpeg_data_src(sp);
+    sp->src.init_source = tables_init_source;
 }
 
 /*
@@ -683,32 +748,29 @@
  * when done with strip/tile.
  * This is also a handy place to compute samplesperclump, bytesperline.
  */
-static int
-alloc_downsampled_buffers(TIFF* tif, jpeg_component_info* comp_info,
-			  int num_components)
+static int alloc_downsampled_buffers(TIFF *tif, jpeg_component_info *comp_info,
+                                     int num_components)
 {
-	JPEGState* sp = JState(tif);
-	int ci;
-	jpeg_component_info* compptr;
-	JSAMPARRAY buf;
-	int samples_per_clump = 0;
+    JPEGState *sp = JState(tif);
+    int ci;
+    jpeg_component_info *compptr;
+    TIFF_JSAMPARRAY buf;
+    int samples_per_clump = 0;
 
-	for (ci = 0, compptr = comp_info; ci < num_components;
-	     ci++, compptr++) {
-		samples_per_clump += compptr->h_samp_factor *
-			compptr->v_samp_factor;
-		buf = TIFFjpeg_alloc_sarray(sp, JPOOL_IMAGE,
-				compptr->width_in_blocks * DCTSIZE,
-				(JDIMENSION) (compptr->v_samp_factor*DCTSIZE));
-		if (buf == NULL)
-			return (0);
-		sp->ds_buffer[ci] = buf;
-	}
-	sp->samplesperclump = samples_per_clump;
-	return (1);
+    for (ci = 0, compptr = comp_info; ci < num_components; ci++, compptr++)
+    {
+        samples_per_clump += compptr->h_samp_factor * compptr->v_samp_factor;
+        buf = (TIFF_JSAMPARRAY)TIFFjpeg_alloc_sarray(
+            sp, JPOOL_IMAGE, compptr->width_in_blocks * DCTSIZE,
+            (JDIMENSION)(compptr->v_samp_factor * DCTSIZE));
+        if (buf == NULL)
+            return (0);
+        sp->ds_buffer[ci] = buf;
+    }
+    sp->samplesperclump = samples_per_clump;
+    return (1);
 }
 
-
 /*
  * JPEG Decoding.
  */
@@ -729,328 +791,367 @@
 #define JPEG_MARKER_COM 0xFE
 struct JPEGFixupTagsSubsamplingData
 {
-	TIFF* tif;
-	void* buffer;
-	uint32 buffersize;
-	uint8* buffercurrentbyte;
-	uint32 bufferbytesleft;
-	uint64 fileoffset;
-	uint64 filebytesleft;
-	uint8 filepositioned;
+    TIFF *tif;
+    void *buffer;
+    uint32_t buffersize;
+    uint8_t *buffercurrentbyte;
+    uint32_t bufferbytesleft;
+    uint64_t fileoffset;
+    uint64_t filebytesleft;
+    uint8_t filepositioned;
 };
-static void JPEGFixupTagsSubsampling(TIFF* tif);
-static int JPEGFixupTagsSubsamplingSec(struct JPEGFixupTagsSubsamplingData* data);
-static int JPEGFixupTagsSubsamplingReadByte(struct JPEGFixupTagsSubsamplingData* data, uint8* result);
-static int JPEGFixupTagsSubsamplingReadWord(struct JPEGFixupTagsSubsamplingData* data, uint16* result);
-static void JPEGFixupTagsSubsamplingSkip(struct JPEGFixupTagsSubsamplingData* data, uint16 skiplength);
+static void JPEGFixupTagsSubsampling(TIFF *tif);
+static int
+JPEGFixupTagsSubsamplingSec(struct JPEGFixupTagsSubsamplingData *data);
+static int
+JPEGFixupTagsSubsamplingReadByte(struct JPEGFixupTagsSubsamplingData *data,
+                                 uint8_t *result);
+static int
+JPEGFixupTagsSubsamplingReadWord(struct JPEGFixupTagsSubsamplingData *data,
+                                 uint16_t *result);
+static void
+JPEGFixupTagsSubsamplingSkip(struct JPEGFixupTagsSubsamplingData *data,
+                             uint16_t skiplength);
 
 #endif
 
-static int
-JPEGFixupTags(TIFF* tif)
+static int JPEGFixupTags(TIFF *tif)
 {
 #ifdef CHECK_JPEG_YCBCR_SUBSAMPLING
-        JPEGState* sp = JState(tif);
-	if ((tif->tif_dir.td_photometric==PHOTOMETRIC_YCBCR)&&
-	    (tif->tif_dir.td_planarconfig==PLANARCONFIG_CONTIG)&&
-	    (tif->tif_dir.td_samplesperpixel==3) &&
-            !sp->ycbcrsampling_fetched)
-		JPEGFixupTagsSubsampling(tif);
+    JPEGState *sp = JState(tif);
+    if ((tif->tif_dir.td_photometric == PHOTOMETRIC_YCBCR) &&
+        (tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG) &&
+        (tif->tif_dir.td_samplesperpixel == 3) &&
+        !sp->otherSettings.ycbcrsampling_fetched)
+        JPEGFixupTagsSubsampling(tif);
 #endif
-        
-	return(1);
+
+    return (1);
 }
 
 #ifdef CHECK_JPEG_YCBCR_SUBSAMPLING
 
-static void
-JPEGFixupTagsSubsampling(TIFF* tif)
+static void JPEGFixupTagsSubsampling(TIFF *tif)
 {
-	/*
-	 * Some JPEG-in-TIFF produces do not emit the YCBCRSUBSAMPLING values in
-	 * the TIFF tags, but still use non-default (2,2) values within the jpeg
-	 * data stream itself.  In order for TIFF applications to work properly
-	 * - for instance to get the strip buffer size right - it is imperative
-	 * that the subsampling be available before we start reading the image
-	 * data normally.  This function will attempt to analyze the first strip in
-	 * order to get the sampling values from the jpeg data stream.
-	 *
-	 * Note that JPEGPreDeocode() will produce a fairly loud warning when the
-	 * discovered sampling does not match the default sampling (2,2) or whatever
-	 * was actually in the tiff tags.
-	 *
-	 * See the bug in bugzilla for details:
-	 *
-	 * http://bugzilla.remotesensing.org/show_bug.cgi?id=168
-	 *
-	 * Frank Warmerdam, July 2002
-	 * Joris Van Damme, May 2007
-	 */
-	static const char module[] = "JPEGFixupTagsSubsampling";
-	struct JPEGFixupTagsSubsamplingData m;
-        uint64 fileoffset = TIFFGetStrileOffset(tif, 0);
+    /*
+     * Some JPEG-in-TIFF produces do not emit the YCBCRSUBSAMPLING values in
+     * the TIFF tags, but still use non-default (2,2) values within the jpeg
+     * data stream itself.  In order for TIFF applications to work properly
+     * - for instance to get the strip buffer size right - it is imperative
+     * that the subsampling be available before we start reading the image
+     * data normally.  This function will attempt to analyze the first strip in
+     * order to get the sampling values from the jpeg data stream.
+     *
+     * Note that JPEGPreDeocode() will produce a fairly loud warning when the
+     * discovered sampling does not match the default sampling (2,2) or whatever
+     * was actually in the tiff tags.
+     *
+     * See the bug in bugzilla for details:
+     *
+     * http://bugzilla.remotesensing.org/show_bug.cgi?id=168
+     *
+     * Frank Warmerdam, July 2002
+     * Joris Van Damme, May 2007
+     */
+    static const char module[] = "JPEGFixupTagsSubsampling";
+    struct JPEGFixupTagsSubsamplingData m;
+    uint64_t fileoffset = TIFFGetStrileOffset(tif, 0);
 
-        if( fileoffset == 0 )
+    if (fileoffset == 0)
+    {
+        /* Do not even try to check if the first strip/tile does not
+           yet exist, as occurs when GDAL has created a new NULL file
+           for instance. */
+        return;
+    }
+
+    m.tif = tif;
+    m.buffersize = 2048;
+    m.buffer = _TIFFmallocExt(tif, m.buffersize);
+    if (m.buffer == NULL)
+    {
+        TIFFWarningExtR(tif, module,
+                        "Unable to allocate memory for auto-correcting of "
+                        "subsampling values; auto-correcting skipped");
+        return;
+    }
+    m.buffercurrentbyte = NULL;
+    m.bufferbytesleft = 0;
+    m.fileoffset = fileoffset;
+    m.filepositioned = 0;
+    m.filebytesleft = TIFFGetStrileByteCount(tif, 0);
+    if (!JPEGFixupTagsSubsamplingSec(&m))
+        TIFFWarningExtR(
+            tif, module,
+            "Unable to auto-correct subsampling values, likely corrupt JPEG "
+            "compressed data in first strip/tile; auto-correcting skipped");
+    _TIFFfreeExt(tif, m.buffer);
+}
+
+static int
+JPEGFixupTagsSubsamplingSec(struct JPEGFixupTagsSubsamplingData *data)
+{
+    static const char module[] = "JPEGFixupTagsSubsamplingSec";
+    uint8_t m;
+    while (1)
+    {
+        while (1)
         {
-            /* Do not even try to check if the first strip/tile does not
-               yet exist, as occurs when GDAL has created a new NULL file
-               for instance. */
-            return;
+            if (!JPEGFixupTagsSubsamplingReadByte(data, &m))
+                return (0);
+            if (m == 255)
+                break;
         }
-
-	m.tif=tif;
-	m.buffersize=2048;
-	m.buffer=_TIFFmalloc(m.buffersize);
-	if (m.buffer==NULL)
-	{
-		TIFFWarningExt(tif->tif_clientdata,module,
-		    "Unable to allocate memory for auto-correcting of subsampling values; auto-correcting skipped");
-		return;
-	}
-	m.buffercurrentbyte=NULL;
-	m.bufferbytesleft=0;
-	m.fileoffset=fileoffset;
-	m.filepositioned=0;
-	m.filebytesleft=TIFFGetStrileByteCount(tif, 0);
-	if (!JPEGFixupTagsSubsamplingSec(&m))
-		TIFFWarningExt(tif->tif_clientdata,module,
-		    "Unable to auto-correct subsampling values, likely corrupt JPEG compressed data in first strip/tile; auto-correcting skipped");
-	_TIFFfree(m.buffer);
+        while (1)
+        {
+            if (!JPEGFixupTagsSubsamplingReadByte(data, &m))
+                return (0);
+            if (m != 255)
+                break;
+        }
+        switch (m)
+        {
+            case JPEG_MARKER_SOI:
+                /* this type of marker has no data and should be skipped */
+                break;
+            case JPEG_MARKER_COM:
+            case JPEG_MARKER_APP0:
+            case JPEG_MARKER_APP0 + 1:
+            case JPEG_MARKER_APP0 + 2:
+            case JPEG_MARKER_APP0 + 3:
+            case JPEG_MARKER_APP0 + 4:
+            case JPEG_MARKER_APP0 + 5:
+            case JPEG_MARKER_APP0 + 6:
+            case JPEG_MARKER_APP0 + 7:
+            case JPEG_MARKER_APP0 + 8:
+            case JPEG_MARKER_APP0 + 9:
+            case JPEG_MARKER_APP0 + 10:
+            case JPEG_MARKER_APP0 + 11:
+            case JPEG_MARKER_APP0 + 12:
+            case JPEG_MARKER_APP0 + 13:
+            case JPEG_MARKER_APP0 + 14:
+            case JPEG_MARKER_APP0 + 15:
+            case JPEG_MARKER_DQT:
+            case JPEG_MARKER_SOS:
+            case JPEG_MARKER_DHT:
+            case JPEG_MARKER_DRI:
+                /* this type of marker has data, but it has no use to us and
+                 * should be skipped */
+                {
+                    uint16_t n;
+                    if (!JPEGFixupTagsSubsamplingReadWord(data, &n))
+                        return (0);
+                    if (n < 2)
+                        return (0);
+                    n -= 2;
+                    if (n > 0)
+                        JPEGFixupTagsSubsamplingSkip(data, n);
+                }
+                break;
+            case JPEG_MARKER_SOF0:  /* Baseline sequential Huffman */
+            case JPEG_MARKER_SOF1:  /* Extended sequential Huffman */
+            case JPEG_MARKER_SOF2:  /* Progressive Huffman: normally not allowed
+                                       by  TechNote, but that doesn't hurt
+                                       supporting it */
+            case JPEG_MARKER_SOF9:  /* Extended sequential arithmetic */
+            case JPEG_MARKER_SOF10: /* Progressive arithmetic: normally not
+                                       allowed by TechNote, but that doesn't
+                                       hurt supporting it */
+                /* this marker contains the subsampling factors we're scanning
+                 * for */
+                {
+                    uint16_t n;
+                    uint16_t o;
+                    uint8_t p;
+                    uint8_t ph, pv;
+                    if (!JPEGFixupTagsSubsamplingReadWord(data, &n))
+                        return (0);
+                    if (n != 8 + data->tif->tif_dir.td_samplesperpixel * 3)
+                        return (0);
+                    JPEGFixupTagsSubsamplingSkip(data, 7);
+                    if (!JPEGFixupTagsSubsamplingReadByte(data, &p))
+                        return (0);
+                    ph = (p >> 4);
+                    pv = (p & 15);
+                    JPEGFixupTagsSubsamplingSkip(data, 1);
+                    for (o = 1; o < data->tif->tif_dir.td_samplesperpixel; o++)
+                    {
+                        JPEGFixupTagsSubsamplingSkip(data, 1);
+                        if (!JPEGFixupTagsSubsamplingReadByte(data, &p))
+                            return (0);
+                        if (p != 0x11)
+                        {
+                            TIFFWarningExtR(data->tif, module,
+                                            "Subsampling values inside JPEG "
+                                            "compressed data "
+                                            "have no TIFF equivalent, "
+                                            "auto-correction of TIFF "
+                                            "subsampling values failed");
+                            return (1);
+                        }
+                        JPEGFixupTagsSubsamplingSkip(data, 1);
+                    }
+                    if (((ph != 1) && (ph != 2) && (ph != 4)) ||
+                        ((pv != 1) && (pv != 2) && (pv != 4)))
+                    {
+                        TIFFWarningExtR(data->tif, module,
+                                        "Subsampling values inside JPEG "
+                                        "compressed data have no TIFF "
+                                        "equivalent, auto-correction of TIFF "
+                                        "subsampling values failed");
+                        return (1);
+                    }
+                    if ((ph != data->tif->tif_dir.td_ycbcrsubsampling[0]) ||
+                        (pv != data->tif->tif_dir.td_ycbcrsubsampling[1]))
+                    {
+                        TIFFWarningExtR(
+                            data->tif, module,
+                            "Auto-corrected former TIFF subsampling values "
+                            "[%" PRIu16 ",%" PRIu16
+                            "] to match subsampling values inside JPEG "
+                            "compressed data [%" PRIu8 ",%" PRIu8 "]",
+                            data->tif->tif_dir.td_ycbcrsubsampling[0],
+                            data->tif->tif_dir.td_ycbcrsubsampling[1], ph, pv);
+                        data->tif->tif_dir.td_ycbcrsubsampling[0] = ph;
+                        data->tif->tif_dir.td_ycbcrsubsampling[1] = pv;
+                    }
+                }
+                return (1);
+            default:
+                return (0);
+        }
+    }
 }
 
 static int
-JPEGFixupTagsSubsamplingSec(struct JPEGFixupTagsSubsamplingData* data)
+JPEGFixupTagsSubsamplingReadByte(struct JPEGFixupTagsSubsamplingData *data,
+                                 uint8_t *result)
 {
-	static const char module[] = "JPEGFixupTagsSubsamplingSec";
-	uint8 m;
-	while (1)
-	{
-		while (1)
-		{
-			if (!JPEGFixupTagsSubsamplingReadByte(data,&m))
-				return(0);
-			if (m==255)
-				break;
-		}
-		while (1)
-		{
-			if (!JPEGFixupTagsSubsamplingReadByte(data,&m))
-				return(0);
-			if (m!=255)
-				break;
-		}
-		switch (m)
-		{
-			case JPEG_MARKER_SOI:
-				/* this type of marker has no data and should be skipped */
-				break;
-			case JPEG_MARKER_COM:
-			case JPEG_MARKER_APP0:
-			case JPEG_MARKER_APP0+1:
-			case JPEG_MARKER_APP0+2:
-			case JPEG_MARKER_APP0+3:
-			case JPEG_MARKER_APP0+4:
-			case JPEG_MARKER_APP0+5:
-			case JPEG_MARKER_APP0+6:
-			case JPEG_MARKER_APP0+7:
-			case JPEG_MARKER_APP0+8:
-			case JPEG_MARKER_APP0+9:
-			case JPEG_MARKER_APP0+10:
-			case JPEG_MARKER_APP0+11:
-			case JPEG_MARKER_APP0+12:
-			case JPEG_MARKER_APP0+13:
-			case JPEG_MARKER_APP0+14:
-			case JPEG_MARKER_APP0+15:
-			case JPEG_MARKER_DQT:
-			case JPEG_MARKER_SOS:
-			case JPEG_MARKER_DHT:
-			case JPEG_MARKER_DRI:
-				/* this type of marker has data, but it has no use to us and should be skipped */
-				{
-					uint16 n;
-					if (!JPEGFixupTagsSubsamplingReadWord(data,&n))
-						return(0);
-					if (n<2)
-						return(0);
-					n-=2;
-					if (n>0)
-						JPEGFixupTagsSubsamplingSkip(data,n);
-				}
-				break;
-			case JPEG_MARKER_SOF0: /* Baseline sequential Huffman */
-			case JPEG_MARKER_SOF1: /* Extended sequential Huffman */
-			case JPEG_MARKER_SOF2: /* Progressive Huffman: normally not allowed by TechNote, but that doesn't hurt supporting it */
-			case JPEG_MARKER_SOF9: /* Extended sequential arithmetic */
-			case JPEG_MARKER_SOF10: /* Progressive arithmetic: normally not allowed by TechNote, but that doesn't hurt supporting it */
-				/* this marker contains the subsampling factors we're scanning for */
-				{
-					uint16 n;
-					uint16 o;
-					uint8 p;
-					uint8 ph,pv;
-					if (!JPEGFixupTagsSubsamplingReadWord(data,&n))
-						return(0);
-					if (n!=8+data->tif->tif_dir.td_samplesperpixel*3)
-						return(0);
-					JPEGFixupTagsSubsamplingSkip(data,7);
-					if (!JPEGFixupTagsSubsamplingReadByte(data,&p))
-						return(0);
-					ph=(p>>4);
-					pv=(p&15);
-					JPEGFixupTagsSubsamplingSkip(data,1);
-					for (o=1; o<data->tif->tif_dir.td_samplesperpixel; o++)
-					{
-						JPEGFixupTagsSubsamplingSkip(data,1);
-						if (!JPEGFixupTagsSubsamplingReadByte(data,&p))
-							return(0);
-						if (p!=0x11)
-						{
-							TIFFWarningExt(data->tif->tif_clientdata,module,
-							    "Subsampling values inside JPEG compressed data have no TIFF equivalent, auto-correction of TIFF subsampling values failed");
-							return(1);
-						}
-						JPEGFixupTagsSubsamplingSkip(data,1);
-					}
-					if (((ph!=1)&&(ph!=2)&&(ph!=4))||((pv!=1)&&(pv!=2)&&(pv!=4)))
-					{
-						TIFFWarningExt(data->tif->tif_clientdata,module,
-						    "Subsampling values inside JPEG compressed data have no TIFF equivalent, auto-correction of TIFF subsampling values failed");
-						return(1);
-					}
-					if ((ph!=data->tif->tif_dir.td_ycbcrsubsampling[0])||(pv!=data->tif->tif_dir.td_ycbcrsubsampling[1]))
-					{
-						TIFFWarningExt(data->tif->tif_clientdata,module,
-						    "Auto-corrected former TIFF subsampling values [%d,%d] to match subsampling values inside JPEG compressed data [%d,%d]",
-						    (int)data->tif->tif_dir.td_ycbcrsubsampling[0],
-						    (int)data->tif->tif_dir.td_ycbcrsubsampling[1],
-						    (int)ph,(int)pv);
-						data->tif->tif_dir.td_ycbcrsubsampling[0]=ph;
-						data->tif->tif_dir.td_ycbcrsubsampling[1]=pv;
-					}
-				}
-				return(1);
-			default:
-				return(0);
-		}
-	}
+    if (data->bufferbytesleft == 0)
+    {
+        uint32_t m;
+        if (data->filebytesleft == 0)
+            return (0);
+        if (!data->filepositioned)
+        {
+            if (TIFFSeekFile(data->tif, data->fileoffset, SEEK_SET) ==
+                (toff_t)-1)
+            {
+                return 0;
+            }
+            data->filepositioned = 1;
+        }
+        m = data->buffersize;
+        if ((uint64_t)m > data->filebytesleft)
+            m = (uint32_t)data->filebytesleft;
+        assert(m < 0x80000000UL);
+        if (TIFFReadFile(data->tif, data->buffer, (tmsize_t)m) != (tmsize_t)m)
+            return (0);
+        data->buffercurrentbyte = data->buffer;
+        data->bufferbytesleft = m;
+        data->fileoffset += m;
+        data->filebytesleft -= m;
+    }
+    *result = *data->buffercurrentbyte;
+    data->buffercurrentbyte++;
+    data->bufferbytesleft--;
+    return (1);
 }
 
 static int
-JPEGFixupTagsSubsamplingReadByte(struct JPEGFixupTagsSubsamplingData* data, uint8* result)
+JPEGFixupTagsSubsamplingReadWord(struct JPEGFixupTagsSubsamplingData *data,
+                                 uint16_t *result)
 {
-	if (data->bufferbytesleft==0)
-	{
-		uint32 m;
-		if (data->filebytesleft==0)
-			return(0);
-		if (!data->filepositioned)
-		{
-			TIFFSeekFile(data->tif,data->fileoffset,SEEK_SET);
-			data->filepositioned=1;
-		}
-		m=data->buffersize;
-		if ((uint64)m>data->filebytesleft)
-			m=(uint32)data->filebytesleft;
-		assert(m<0x80000000UL);
-		if (TIFFReadFile(data->tif,data->buffer,(tmsize_t)m)!=(tmsize_t)m)
-			return(0);
-		data->buffercurrentbyte=data->buffer;
-		data->bufferbytesleft=m;
-		data->fileoffset+=m;
-		data->filebytesleft-=m;
-	}
-	*result=*data->buffercurrentbyte;
-	data->buffercurrentbyte++;
-	data->bufferbytesleft--;
-	return(1);
-}
-
-static int
-JPEGFixupTagsSubsamplingReadWord(struct JPEGFixupTagsSubsamplingData* data, uint16* result)
-{
-	uint8 ma;
-	uint8 mb;
-	if (!JPEGFixupTagsSubsamplingReadByte(data,&ma))
-		return(0);
-	if (!JPEGFixupTagsSubsamplingReadByte(data,&mb))
-		return(0);
-	*result=(ma<<8)|mb;
-	return(1);
+    uint8_t ma;
+    uint8_t mb;
+    if (!JPEGFixupTagsSubsamplingReadByte(data, &ma))
+        return (0);
+    if (!JPEGFixupTagsSubsamplingReadByte(data, &mb))
+        return (0);
+    *result = (ma << 8) | mb;
+    return (1);
 }
 
 static void
-JPEGFixupTagsSubsamplingSkip(struct JPEGFixupTagsSubsamplingData* data, uint16 skiplength)
+JPEGFixupTagsSubsamplingSkip(struct JPEGFixupTagsSubsamplingData *data,
+                             uint16_t skiplength)
 {
-	if ((uint32)skiplength<=data->bufferbytesleft)
-	{
-		data->buffercurrentbyte+=skiplength;
-		data->bufferbytesleft-=skiplength;
-	}
-	else
-	{
-		uint16 m;
-		m=(uint16)(skiplength-data->bufferbytesleft);
-		if (m<=data->filebytesleft)
-		{
-			data->bufferbytesleft=0;
-			data->fileoffset+=m;
-			data->filebytesleft-=m;
-			data->filepositioned=0;
-		}
-		else
-		{
-			data->bufferbytesleft=0;
-			data->filebytesleft=0;
-		}
-	}
+    if ((uint32_t)skiplength <= data->bufferbytesleft)
+    {
+        data->buffercurrentbyte += skiplength;
+        data->bufferbytesleft -= skiplength;
+    }
+    else
+    {
+        uint16_t m;
+        m = (uint16_t)(skiplength - data->bufferbytesleft);
+        if (m <= data->filebytesleft)
+        {
+            data->bufferbytesleft = 0;
+            data->fileoffset += m;
+            data->filebytesleft -= m;
+            data->filepositioned = 0;
+        }
+        else
+        {
+            data->bufferbytesleft = 0;
+            data->filebytesleft = 0;
+        }
+    }
 }
 
 #endif
 
-
-static int
-JPEGSetupDecode(TIFF* tif)
+static int JPEGSetupDecode(TIFF *tif)
 {
-	JPEGState* sp = JState(tif);
-	TIFFDirectory *td = &tif->tif_dir;
+    JPEGState *sp = JState(tif);
+    TIFFDirectory *td = &tif->tif_dir;
 
-#if defined(JPEG_DUAL_MODE_8_12) && !defined(TIFFInitJPEG)
-        if( tif->tif_dir.td_bitspersample == 12 )
-            return TIFFReInitJPEG_12( tif, COMPRESSION_JPEG, 0 );
+#if defined(JPEG_DUAL_MODE_8_12) && !defined(FROM_TIF_JPEG_12)
+    if (tif->tif_dir.td_bitspersample == 12)
+    {
+        /* We pass a pointer to a copy of otherSettings, since */
+        /* TIFFReInitJPEG_12() will clear sp */
+        JPEGOtherSettings savedOtherSettings = sp->otherSettings;
+        return TIFFReInitJPEG_12(tif, &savedOtherSettings, COMPRESSION_JPEG, 0);
+    }
 #endif
 
-	JPEGInitializeLibJPEG( tif, TRUE );
+    JPEGInitializeLibJPEG(tif, TRUE);
 
-	assert(sp != NULL);
-	assert(sp->cinfo.comm.is_decompressor);
+    assert(sp != NULL);
+    assert(sp->cinfo.comm.is_decompressor);
 
-	/* Read JPEGTables if it is present */
-	if (TIFFFieldSet(tif,FIELD_JPEGTABLES)) {
-		TIFFjpeg_tables_src(sp);
-		if(TIFFjpeg_read_header(sp,FALSE) != JPEG_HEADER_TABLES_ONLY) {
-			TIFFErrorExt(tif->tif_clientdata, "JPEGSetupDecode", "Bogus JPEGTables field");
-			return (0);
-		}
-	}
+    /* Read JPEGTables if it is present */
+    if (TIFFFieldSet(tif, FIELD_JPEGTABLES))
+    {
+        TIFFjpeg_tables_src(sp);
+        if (TIFFjpeg_read_header(sp, FALSE) != JPEG_HEADER_TABLES_ONLY)
+        {
+            TIFFErrorExtR(tif, "JPEGSetupDecode", "Bogus JPEGTables field");
+            return (0);
+        }
+    }
 
-	/* Grab parameters that are same for all strips/tiles */
-	sp->photometric = td->td_photometric;
-	switch (sp->photometric) {
-	case PHOTOMETRIC_YCBCR:
-		sp->h_sampling = td->td_ycbcrsubsampling[0];
-		sp->v_sampling = td->td_ycbcrsubsampling[1];
-		break;
-	default:
-		/* TIFF 6.0 forbids subsampling of all other color spaces */
-		sp->h_sampling = 1;
-		sp->v_sampling = 1;
-		break;
-	}
+    /* Grab parameters that are same for all strips/tiles */
+    sp->photometric = td->td_photometric;
+    switch (sp->photometric)
+    {
+        case PHOTOMETRIC_YCBCR:
+            sp->h_sampling = td->td_ycbcrsubsampling[0];
+            sp->v_sampling = td->td_ycbcrsubsampling[1];
+            break;
+        default:
+            /* TIFF 6.0 forbids subsampling of all other color spaces */
+            sp->h_sampling = 1;
+            sp->v_sampling = 1;
+            break;
+    }
 
-	/* Set up for reading normal data */
-	TIFFjpeg_data_src(sp);
-	tif->tif_postdecode = _TIFFNoPostDecode; /* override byte swapping */
-	return (1);
+    /* Set up for reading normal data */
+    TIFFjpeg_data_src(sp);
+    tif->tif_postdecode = _TIFFNoPostDecode; /* override byte swapping */
+    return (1);
 }
 
 /* Returns 1 if the full strip should be read, even when doing scanline per */
@@ -1060,14 +1161,14 @@
 /* Only reads tif->tif_dir.td_bitspersample, tif->tif_rawdata and */
 /* tif->tif_rawcc members. */
 /* Can be called independently of the usual setup/predecode/decode states */
-int TIFFJPEGIsFullStripRequired(TIFF* tif)
+int TIFFJPEGIsFullStripRequired(TIFF *tif)
 {
     int ret;
     JPEGState state;
 
-#if defined(JPEG_DUAL_MODE_8_12) && !defined(TIFFJPEGIsFullStripRequired)
-    if( tif->tif_dir.td_bitspersample == 12 )
-        return TIFFJPEGIsFullStripRequired_12( tif );
+#if defined(JPEG_DUAL_MODE_8_12) && !defined(FROM_TIF_JPEG_12)
+    if (tif->tif_dir.td_bitspersample == 12)
+        return TIFFJPEGIsFullStripRequired_12(tif);
 #endif
 
     memset(&state, 0, sizeof(JPEGState));
@@ -1092,231 +1193,271 @@
 /*
  * Set up for decoding a strip or tile.
  */
-/*ARGSUSED*/ static int
-JPEGPreDecode(TIFF* tif, uint16 s)
+/*ARGSUSED*/ static int JPEGPreDecode(TIFF *tif, uint16_t s)
 {
-	JPEGState *sp = JState(tif);
-	TIFFDirectory *td = &tif->tif_dir;
-	static const char module[] = "JPEGPreDecode";
-	uint32 segment_width, segment_height;
-	int downsampled_output;
-	int ci;
+    JPEGState *sp = JState(tif);
+    TIFFDirectory *td = &tif->tif_dir;
+    static const char module[] = "JPEGPreDecode";
+    uint32_t segment_width, segment_height;
+    int downsampled_output;
+    int ci;
 
-	assert(sp != NULL);
-  
-	if (sp->cinfo.comm.is_decompressor == 0)
-	{
-		tif->tif_setupdecode( tif );
-	}
-  
-	assert(sp->cinfo.comm.is_decompressor);
-	/*
-	 * Reset decoder state from any previous strip/tile,
-	 * in case application didn't read the whole strip.
-	 */
-	if (!TIFFjpeg_abort(sp))
-		return (0);
-	/*
-	 * Read the header for this strip/tile.
-	 */
-        
-	if (TIFFjpeg_read_header(sp, TRUE) != JPEG_HEADER_OK)
-		return (0);
+    assert(sp != NULL);
 
-        tif->tif_rawcp = (uint8*) sp->src.next_input_byte;
-        tif->tif_rawcc = sp->src.bytes_in_buffer;
+    if (sp->cinfo.comm.is_decompressor == 0)
+    {
+        tif->tif_setupdecode(tif);
+    }
 
-	/*
-	 * Check image parameters and set decompression parameters.
-	 */
-	if (isTiled(tif)) {
-                segment_width = td->td_tilewidth;
-                segment_height = td->td_tilelength;
-		sp->bytesperline = TIFFTileRowSize(tif);
-	} else {
-		segment_width = td->td_imagewidth;
-		segment_height = td->td_imagelength - tif->tif_row;
-		if (segment_height > td->td_rowsperstrip)
-			segment_height = td->td_rowsperstrip;
-		sp->bytesperline = TIFFScanlineSize(tif);
-	}
-	if (td->td_planarconfig == PLANARCONFIG_SEPARATE && s > 0) {
-		/*
-		 * For PC 2, scale down the expected strip/tile size
-		 * to match a downsampled component
-		 */
-		segment_width = TIFFhowmany_32(segment_width, sp->h_sampling);
-		segment_height = TIFFhowmany_32(segment_height, sp->v_sampling);
-	}
-	if (sp->cinfo.d.image_width < segment_width ||
-	    sp->cinfo.d.image_height < segment_height) {
-		TIFFWarningExt(tif->tif_clientdata, module,
-			       "Improper JPEG strip/tile size, "
-			       "expected %dx%d, got %dx%d",
-			       segment_width, segment_height,
-			       sp->cinfo.d.image_width,
-			       sp->cinfo.d.image_height);
-	}
-	if( sp->cinfo.d.image_width == segment_width &&
-	    sp->cinfo.d.image_height > segment_height &&
-	    tif->tif_row + segment_height == td->td_imagelength &&
-	    !isTiled(tif) ) {
-		/* Some files have a last strip, that should be truncated, */
-		/* but their JPEG codestream has still the maximum strip */
-		/* height. Warn about this as this is non compliant, but */
-		/* we can safely recover from that. */
-		TIFFWarningExt(tif->tif_clientdata, module,
-			     "JPEG strip size exceeds expected dimensions,"
-			     " expected %dx%d, got %dx%d",
-			     segment_width, segment_height,
-			     sp->cinfo.d.image_width, sp->cinfo.d.image_height);
-	}
-	else if (sp->cinfo.d.image_width > segment_width ||
-		 sp->cinfo.d.image_height > segment_height) {
-		/*
-		 * This case could be dangerous, if the strip or tile size has
-		 * been reported as less than the amount of data jpeg will
-		 * return, some potential security issues arise. Catch this
-		 * case and error out.
-		 */
-		TIFFErrorExt(tif->tif_clientdata, module,
-			     "JPEG strip/tile size exceeds expected dimensions,"
-			     " expected %dx%d, got %dx%d",
-			     segment_width, segment_height,
-			     sp->cinfo.d.image_width, sp->cinfo.d.image_height);
-		return (0);
-	}
-	if (sp->cinfo.d.num_components !=
-	    (td->td_planarconfig == PLANARCONFIG_CONTIG ?
-	     td->td_samplesperpixel : 1)) {
-		TIFFErrorExt(tif->tif_clientdata, module, "Improper JPEG component count");
-		return (0);
-	}
+    assert(sp->cinfo.comm.is_decompressor);
+    /*
+     * Reset decoder state from any previous strip/tile,
+     * in case application didn't read the whole strip.
+     */
+    if (!TIFFjpeg_abort(sp))
+        return (0);
+    /*
+     * Read the header for this strip/tile.
+     */
+
+    if (TIFFjpeg_read_header(sp, TRUE) != JPEG_HEADER_OK)
+        return (0);
+
+    tif->tif_rawcp = (uint8_t *)sp->src.next_input_byte;
+    tif->tif_rawcc = sp->src.bytes_in_buffer;
+
+    /*
+     * Check image parameters and set decompression parameters.
+     */
+    if (isTiled(tif))
+    {
+        segment_width = td->td_tilewidth;
+        segment_height = td->td_tilelength;
+        sp->bytesperline = TIFFTileRowSize(tif);
+    }
+    else
+    {
+        segment_width = td->td_imagewidth;
+        segment_height = td->td_imagelength - tif->tif_row;
+        if (segment_height > td->td_rowsperstrip)
+            segment_height = td->td_rowsperstrip;
+        sp->bytesperline = TIFFScanlineSize(tif);
+    }
+    if (td->td_planarconfig == PLANARCONFIG_SEPARATE && s > 0)
+    {
+        /*
+         * For PC 2, scale down the expected strip/tile size
+         * to match a downsampled component
+         */
+        segment_width = TIFFhowmany_32(segment_width, sp->h_sampling);
+        segment_height = TIFFhowmany_32(segment_height, sp->v_sampling);
+    }
+    if (sp->cinfo.d.image_width < segment_width ||
+        sp->cinfo.d.image_height < segment_height)
+    {
+        TIFFWarningExtR(tif, module,
+                        "Improper JPEG strip/tile size, "
+                        "expected %" PRIu32 "x%" PRIu32 ", got %ux%u",
+                        segment_width, segment_height, sp->cinfo.d.image_width,
+                        sp->cinfo.d.image_height);
+    }
+    if (sp->cinfo.d.image_width == segment_width &&
+        sp->cinfo.d.image_height > segment_height &&
+        tif->tif_row + segment_height == td->td_imagelength && !isTiled(tif))
+    {
+        /* Some files have a last strip, that should be truncated, */
+        /* but their JPEG codestream has still the maximum strip */
+        /* height. Warn about this as this is non compliant, but */
+        /* we can safely recover from that. */
+        TIFFWarningExtR(tif, module,
+                        "JPEG strip size exceeds expected dimensions,"
+                        " expected %" PRIu32 "x%" PRIu32 ", got %ux%u",
+                        segment_width, segment_height, sp->cinfo.d.image_width,
+                        sp->cinfo.d.image_height);
+    }
+    else if (sp->cinfo.d.image_width > segment_width ||
+             sp->cinfo.d.image_height > segment_height)
+    {
+        /*
+         * This case could be dangerous, if the strip or tile size has
+         * been reported as less than the amount of data jpeg will
+         * return, some potential security issues arise. Catch this
+         * case and error out.
+         */
+        TIFFErrorExtR(tif, module,
+                      "JPEG strip/tile size exceeds expected dimensions,"
+                      " expected %" PRIu32 "x%" PRIu32 ", got %ux%u",
+                      segment_width, segment_height, sp->cinfo.d.image_width,
+                      sp->cinfo.d.image_height);
+        return (0);
+    }
+    if (sp->cinfo.d.num_components !=
+        (td->td_planarconfig == PLANARCONFIG_CONTIG ? td->td_samplesperpixel
+                                                    : 1))
+    {
+        TIFFErrorExtR(tif, module, "Improper JPEG component count");
+        return (0);
+    }
 #ifdef JPEG_LIB_MK1
-	if (12 != td->td_bitspersample && 8 != td->td_bitspersample) {
-		TIFFErrorExt(tif->tif_clientdata, module, "Improper JPEG data precision");
-		return (0);
-	}
-	sp->cinfo.d.data_precision = td->td_bitspersample;
-	sp->cinfo.d.bits_in_jsample = td->td_bitspersample;
+    if (12 != td->td_bitspersample && 8 != td->td_bitspersample)
+    {
+        TIFFErrorExtR(tif, module, "Improper JPEG data precision");
+        return (0);
+    }
+    sp->cinfo.d.data_precision = td->td_bitspersample;
+    sp->cinfo.d.bits_in_jsample = td->td_bitspersample;
 #else
-	if (sp->cinfo.d.data_precision != td->td_bitspersample) {
-		TIFFErrorExt(tif->tif_clientdata, module, "Improper JPEG data precision");
-		return (0);
-	}
+    if (sp->cinfo.d.data_precision != td->td_bitspersample)
+    {
+        TIFFErrorExtR(tif, module, "Improper JPEG data precision");
+        return (0);
+    }
 #endif
 
-        /* In some cases, libjpeg needs to allocate a lot of memory */
-        /* http://www.libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf */
-        if( TIFFjpeg_has_multiple_scans(sp) )
+    if (sp->cinfo.d.progressive_mode &&
+        !sp->otherSettings.has_warned_about_progressive_mode)
+    {
+        TIFFWarningExtR(tif, module,
+                        "The JPEG strip/tile is encoded with progressive mode, "
+                        "which is normally not legal for JPEG-in-TIFF.\n"
+                        "libtiff should be able to decode it, but it might "
+                        "cause compatibility issues with other readers");
+        sp->otherSettings.has_warned_about_progressive_mode = TRUE;
+    }
+
+    /* In some cases, libjpeg needs to allocate a lot of memory */
+    /* http://www.libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf
+     */
+    if (TIFFjpeg_has_multiple_scans(sp))
+    {
+        /* In this case libjpeg will need to allocate memory or backing */
+        /* store for all coefficients */
+        /* See call to jinit_d_coef_controller() from master_selection() */
+        /* in libjpeg */
+
+        /* 1 MB for regular libjpeg usage */
+        toff_t nRequiredMemory = 1024 * 1024;
+
+        for (ci = 0; ci < sp->cinfo.d.num_components; ci++)
         {
-            /* In this case libjpeg will need to allocate memory or backing */
-            /* store for all coefficients */
-            /* See call to jinit_d_coef_controller() from master_selection() */
-            /* in libjpeg */
-            toff_t nRequiredMemory = (toff_t)sp->cinfo.d.image_width *
-                                     sp->cinfo.d.image_height *
-                                     sp->cinfo.d.num_components *
-                                     ((td->td_bitspersample+7)/8);
-            /* BLOCK_SMOOTHING_SUPPORTED is generally defined, so we need */
-            /* to replicate the logic of jinit_d_coef_controller() */
-            if( sp->cinfo.d.progressive_mode )
-                nRequiredMemory *= 3;
-
-#ifndef TIFF_LIBJPEG_LARGEST_MEM_ALLOC
-#define TIFF_LIBJPEG_LARGEST_MEM_ALLOC (100 * 1024 * 1024)
-#endif
-
-            if( nRequiredMemory > TIFF_LIBJPEG_LARGEST_MEM_ALLOC &&
-                getenv("LIBTIFF_ALLOW_LARGE_LIBJPEG_MEM_ALLOC") == NULL )
+            const jpeg_component_info *compptr = &(sp->cinfo.d.comp_info[ci]);
+            if (compptr->h_samp_factor > 0 && compptr->v_samp_factor > 0)
             {
-                    TIFFErrorExt(tif->tif_clientdata, module,
-                        "Reading this strip would require libjpeg to allocate "
-                        "at least %u bytes. "
-                        "This is disabled since above the %u threshold. "
-                        "You may override this restriction by defining the "
-                        "LIBTIFF_ALLOW_LARGE_LIBJPEG_MEM_ALLOC environment variable, "
-                        "or recompile libtiff by defining the "
-                        "TIFF_LIBJPEG_LARGEST_MEM_ALLOC macro to a value greater "
-                        "than %u",
-                        (unsigned)nRequiredMemory,
-                        (unsigned)TIFF_LIBJPEG_LARGEST_MEM_ALLOC,
-                        (unsigned)TIFF_LIBJPEG_LARGEST_MEM_ALLOC);
-                    return (0);
+                nRequiredMemory +=
+                    (toff_t)(((compptr->width_in_blocks +
+                               compptr->h_samp_factor - 1) /
+                              compptr->h_samp_factor)) *
+                    ((compptr->height_in_blocks + compptr->v_samp_factor - 1) /
+                     compptr->v_samp_factor) *
+                    sizeof(JBLOCK);
             }
         }
 
-	if (td->td_planarconfig == PLANARCONFIG_CONTIG) {
-		/* Component 0 should have expected sampling factors */
-		if (sp->cinfo.d.comp_info[0].h_samp_factor != sp->h_sampling ||
-		    sp->cinfo.d.comp_info[0].v_samp_factor != sp->v_sampling) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-				       "Improper JPEG sampling factors %d,%d\n"
-				       "Apparently should be %d,%d.",
-				       sp->cinfo.d.comp_info[0].h_samp_factor,
-				       sp->cinfo.d.comp_info[0].v_samp_factor,
-				       sp->h_sampling, sp->v_sampling);
-			return (0);
-		}
-		/* Rest should have sampling factors 1,1 */
-		for (ci = 1; ci < sp->cinfo.d.num_components; ci++) {
-			if (sp->cinfo.d.comp_info[ci].h_samp_factor != 1 ||
-			    sp->cinfo.d.comp_info[ci].v_samp_factor != 1) {
-				TIFFErrorExt(tif->tif_clientdata, module, "Improper JPEG sampling factors");
-				return (0);
-			}
-		}
-	} else {
-		/* PC 2's single component should have sampling factors 1,1 */
-		if (sp->cinfo.d.comp_info[0].h_samp_factor != 1 ||
-		    sp->cinfo.d.comp_info[0].v_samp_factor != 1) {
-			TIFFErrorExt(tif->tif_clientdata, module, "Improper JPEG sampling factors");
-			return (0);
-		}
-	}
-	downsampled_output = FALSE;
-	if (td->td_planarconfig == PLANARCONFIG_CONTIG &&
-	    sp->photometric == PHOTOMETRIC_YCBCR &&
-	    sp->jpegcolormode == JPEGCOLORMODE_RGB) {
-		/* Convert YCbCr to RGB */
-		sp->cinfo.d.jpeg_color_space = JCS_YCbCr;
-		sp->cinfo.d.out_color_space = JCS_RGB;
-	} else {
-		/* Suppress colorspace handling */
-		sp->cinfo.d.jpeg_color_space = JCS_UNKNOWN;
-		sp->cinfo.d.out_color_space = JCS_UNKNOWN;
-		if (td->td_planarconfig == PLANARCONFIG_CONTIG &&
-		    (sp->h_sampling != 1 || sp->v_sampling != 1))
-			downsampled_output = TRUE;
-		/* XXX what about up-sampling? */
-	}
-	if (downsampled_output) {
-		/* Need to use raw-data interface to libjpeg */
-		sp->cinfo.d.raw_data_out = TRUE;
+        if (sp->cinfo.d.mem->max_memory_to_use > 0 &&
+            nRequiredMemory > (toff_t)(sp->cinfo.d.mem->max_memory_to_use) &&
+            getenv("LIBTIFF_ALLOW_LARGE_LIBJPEG_MEM_ALLOC") == NULL)
+        {
+            TIFFErrorExtR(
+                tif, module,
+                "Reading this image would require libjpeg to allocate "
+                "at least %" PRIu64 " bytes. "
+                "This is disabled since above the %ld threshold. "
+                "You may override this restriction by defining the "
+                "LIBTIFF_ALLOW_LARGE_LIBJPEG_MEM_ALLOC environment variable, "
+                "or setting the JPEGMEM environment variable to a value "
+                "greater "
+                "or equal to '%" PRIu64 "M'",
+                nRequiredMemory, sp->cinfo.d.mem->max_memory_to_use,
+                (nRequiredMemory + 1000000u - 1u) / 1000000u);
+            return 0;
+        }
+    }
+
+    if (td->td_planarconfig == PLANARCONFIG_CONTIG)
+    {
+        /* Component 0 should have expected sampling factors */
+        if (sp->cinfo.d.comp_info[0].h_samp_factor != sp->h_sampling ||
+            sp->cinfo.d.comp_info[0].v_samp_factor != sp->v_sampling)
+        {
+            TIFFErrorExtR(tif, module,
+                          "Improper JPEG sampling factors %d,%d\n"
+                          "Apparently should be %" PRIu16 ",%" PRIu16 ".",
+                          sp->cinfo.d.comp_info[0].h_samp_factor,
+                          sp->cinfo.d.comp_info[0].v_samp_factor,
+                          sp->h_sampling, sp->v_sampling);
+            return (0);
+        }
+        /* Rest should have sampling factors 1,1 */
+        for (ci = 1; ci < sp->cinfo.d.num_components; ci++)
+        {
+            if (sp->cinfo.d.comp_info[ci].h_samp_factor != 1 ||
+                sp->cinfo.d.comp_info[ci].v_samp_factor != 1)
+            {
+                TIFFErrorExtR(tif, module, "Improper JPEG sampling factors");
+                return (0);
+            }
+        }
+    }
+    else
+    {
+        /* PC 2's single component should have sampling factors 1,1 */
+        if (sp->cinfo.d.comp_info[0].h_samp_factor != 1 ||
+            sp->cinfo.d.comp_info[0].v_samp_factor != 1)
+        {
+            TIFFErrorExtR(tif, module, "Improper JPEG sampling factors");
+            return (0);
+        }
+    }
+    downsampled_output = FALSE;
+    if (td->td_planarconfig == PLANARCONFIG_CONTIG &&
+        sp->photometric == PHOTOMETRIC_YCBCR &&
+        sp->otherSettings.jpegcolormode == JPEGCOLORMODE_RGB)
+    {
+        /* Convert YCbCr to RGB */
+        sp->cinfo.d.jpeg_color_space = JCS_YCbCr;
+        sp->cinfo.d.out_color_space = JCS_RGB;
+    }
+    else
+    {
+        /* Suppress colorspace handling */
+        sp->cinfo.d.jpeg_color_space = JCS_UNKNOWN;
+        sp->cinfo.d.out_color_space = JCS_UNKNOWN;
+        if (td->td_planarconfig == PLANARCONFIG_CONTIG &&
+            (sp->h_sampling != 1 || sp->v_sampling != 1))
+            downsampled_output = TRUE;
+        /* XXX what about up-sampling? */
+    }
+    if (downsampled_output)
+    {
+        /* Need to use raw-data interface to libjpeg */
+        sp->cinfo.d.raw_data_out = TRUE;
 #if JPEG_LIB_VERSION >= 70
-		sp->cinfo.d.do_fancy_upsampling = FALSE;
+        sp->cinfo.d.do_fancy_upsampling = FALSE;
 #endif /* JPEG_LIB_VERSION >= 70 */
-		tif->tif_decoderow = DecodeRowError;
-		tif->tif_decodestrip = JPEGDecodeRaw;
-		tif->tif_decodetile = JPEGDecodeRaw;
-	} else {
-		/* Use normal interface to libjpeg */
-		sp->cinfo.d.raw_data_out = FALSE;
-		tif->tif_decoderow = JPEGDecode;
-		tif->tif_decodestrip = JPEGDecode;
-		tif->tif_decodetile = JPEGDecode;  
-	}
-	/* Start JPEG decompressor */
-	if (!TIFFjpeg_start_decompress(sp))
-		return (0);
-	/* Allocate downsampled-data buffers if needed */
-	if (downsampled_output) {
-		if (!alloc_downsampled_buffers(tif, sp->cinfo.d.comp_info,
-					       sp->cinfo.d.num_components))
-			return (0);
-		sp->scancount = DCTSIZE;	/* mark buffer empty */
-	}
-	return (1);
+        tif->tif_decoderow = DecodeRowError;
+        tif->tif_decodestrip = JPEGDecodeRaw;
+        tif->tif_decodetile = JPEGDecodeRaw;
+    }
+    else
+    {
+        /* Use normal interface to libjpeg */
+        sp->cinfo.d.raw_data_out = FALSE;
+        tif->tif_decoderow = JPEGDecode;
+        tif->tif_decodestrip = JPEGDecode;
+        tif->tif_decodetile = JPEGDecode;
+    }
+    /* Start JPEG decompressor */
+    if (!TIFFjpeg_start_decompress(sp))
+        return (0);
+    /* Allocate downsampled-data buffers if needed */
+    if (downsampled_output)
+    {
+        if (!alloc_downsampled_buffers(tif, sp->cinfo.d.comp_info,
+                                       sp->cinfo.d.num_components))
+            return (0);
+        sp->scancount = DCTSIZE; /* mark buffer empty */
+    }
+    return (1);
 }
 
 /*
@@ -1324,177 +1465,179 @@
  * "Standard" case: returned data is not downsampled.
  */
 #if !JPEG_LIB_MK1_OR_12BIT
-static int
-JPEGDecode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s)
+static int JPEGDecode(TIFF *tif, uint8_t *buf, tmsize_t cc, uint16_t s)
 {
-	JPEGState *sp = JState(tif);
-	tmsize_t nrows;
-	(void) s;
+    JPEGState *sp = JState(tif);
+    tmsize_t nrows;
+    (void)s;
 
-        /*
-        ** Update available information, buffer may have been refilled
-        ** between decode requests
-        */
-	sp->src.next_input_byte = (const JOCTET*) tif->tif_rawcp;
-	sp->src.bytes_in_buffer = (size_t) tif->tif_rawcc;
+    /*
+    ** Update available information, buffer may have been refilled
+    ** between decode requests
+    */
+    sp->src.next_input_byte = (const JOCTET *)tif->tif_rawcp;
+    sp->src.bytes_in_buffer = (size_t)tif->tif_rawcc;
 
-        if( sp->bytesperline == 0 )
-                return 0;
-        
-	nrows = cc / sp->bytesperline;
-	if (cc % sp->bytesperline)
-		TIFFWarningExt(tif->tif_clientdata, tif->tif_name,
-                               "fractional scanline not read");
+    if (sp->bytesperline == 0)
+        return 0;
 
-	if( nrows > (tmsize_t) sp->cinfo.d.image_height )
-		nrows = sp->cinfo.d.image_height;
+    nrows = cc / sp->bytesperline;
+    if (cc % sp->bytesperline)
+        TIFFWarningExtR(tif, tif->tif_name, "fractional scanline not read");
 
-	/* data is expected to be read in multiples of a scanline */
-	if (nrows)
+    if (nrows > (tmsize_t)sp->cinfo.d.image_height)
+        nrows = sp->cinfo.d.image_height;
+
+    /* data is expected to be read in multiples of a scanline */
+    if (nrows)
+    {
+        do
         {
-                do
-                {
-                        /*
-                         * In the libjpeg6b-9a 8bit case.  We read directly into
-                         * the TIFF buffer.
-                         */
-                        JSAMPROW bufptr = (JSAMPROW)buf;
+            /*
+             * In the libjpeg6b-9a 8bit case.  We read directly into
+             * the TIFF buffer.
+             */
+            JSAMPROW bufptr = (JSAMPROW)buf;
 
-                        if (TIFFjpeg_read_scanlines(sp, &bufptr, 1) != 1)
-                                return (0);
+            if (TIFFjpeg_read_scanlines(sp, &bufptr, 1) != 1)
+                return (0);
 
-                        ++tif->tif_row;
-                        buf += sp->bytesperline;
-                        cc -= sp->bytesperline;
-                } while (--nrows > 0);
-        }
+            ++tif->tif_row;
+            buf += sp->bytesperline;
+            cc -= sp->bytesperline;
+        } while (--nrows > 0);
+    }
 
-        /* Update information on consumed data */
-        tif->tif_rawcp = (uint8*) sp->src.next_input_byte;
-        tif->tif_rawcc = sp->src.bytes_in_buffer;
-                
-	/* Close down the decompressor if we've finished the strip or tile. */
-	return sp->cinfo.d.output_scanline < sp->cinfo.d.output_height
-                || TIFFjpeg_finish_decompress(sp);
+    /* Update information on consumed data */
+    tif->tif_rawcp = (uint8_t *)sp->src.next_input_byte;
+    tif->tif_rawcc = sp->src.bytes_in_buffer;
+
+    /* Close down the decompressor if we've finished the strip or tile. */
+    return sp->cinfo.d.output_scanline < sp->cinfo.d.output_height ||
+           TIFFjpeg_finish_decompress(sp);
 }
 #endif /* !JPEG_LIB_MK1_OR_12BIT */
 
 #if JPEG_LIB_MK1_OR_12BIT
-/*ARGSUSED*/ static int
-JPEGDecode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s)
+/*ARGSUSED*/ static int JPEGDecode(TIFF *tif, uint8_t *buf, tmsize_t cc,
+                                   uint16_t s)
 {
-	JPEGState *sp = JState(tif);
-	tmsize_t nrows;
-	(void) s;
+    JPEGState *sp = JState(tif);
+    tmsize_t nrows;
+    (void)s;
+
+    /*
+    ** Update available information, buffer may have been refilled
+    ** between decode requests
+    */
+    sp->src.next_input_byte = (const JOCTET *)tif->tif_rawcp;
+    sp->src.bytes_in_buffer = (size_t)tif->tif_rawcc;
+
+    if (sp->bytesperline == 0)
+        return 0;
+
+    nrows = cc / sp->bytesperline;
+    if (cc % sp->bytesperline)
+        TIFFWarningExtR(tif, tif->tif_name, "fractional scanline not read");
+
+    if (nrows > (tmsize_t)sp->cinfo.d.image_height)
+        nrows = sp->cinfo.d.image_height;
+
+    /* data is expected to be read in multiples of a scanline */
+    if (nrows)
+    {
+        TIFF_JSAMPROW line_work_buf = NULL;
 
         /*
-        ** Update available information, buffer may have been refilled
-        ** between decode requests
-        */
-	sp->src.next_input_byte = (const JOCTET*) tif->tif_rawcp;
-	sp->src.bytes_in_buffer = (size_t) tif->tif_rawcc;
-
-        if( sp->bytesperline == 0 )
-                return 0;
-        
-	nrows = cc / sp->bytesperline;
-	if (cc % sp->bytesperline)
-		TIFFWarningExt(tif->tif_clientdata, tif->tif_name,
-                               "fractional scanline not read");
-
-	if( nrows > (tmsize_t) sp->cinfo.d.image_height )
-		nrows = sp->cinfo.d.image_height;
-
-	/* data is expected to be read in multiples of a scanline */
-	if (nrows)
+         * For 6B, only use temporary buffer for 12 bit imagery.
+         * For Mk1 always use it.
+         */
+        if (sp->cinfo.d.data_precision == 12)
         {
-                JSAMPROW line_work_buf = NULL;
-
-                /*
-                 * For 6B, only use temporary buffer for 12 bit imagery.
-                 * For Mk1 always use it.
-                 */
-                if( sp->cinfo.d.data_precision == 12 )
-                {
-                        line_work_buf = (JSAMPROW)
-                                _TIFFmalloc(sizeof(short) * sp->cinfo.d.output_width
-                                            * sp->cinfo.d.num_components );
-                }
-
-               do
-               {
-                       if( line_work_buf != NULL )
-                       {
-                               /*
-                                * In the MK1 case, we always read into a 16bit
-                                * buffer, and then pack down to 12bit or 8bit.
-                                * In 6B case we only read into 16 bit buffer
-                                * for 12bit data, which we need to repack.
-                                */
-                               if (TIFFjpeg_read_scanlines(sp, &line_work_buf, 1) != 1)
-                                       return (0);
-
-                               if( sp->cinfo.d.data_precision == 12 )
-                               {
-                                       int value_pairs = (sp->cinfo.d.output_width
-                                                          * sp->cinfo.d.num_components) / 2;
-                                       int iPair;
-
-                                       for( iPair = 0; iPair < value_pairs; iPair++ )
-                                       {
-                                               unsigned char *out_ptr =
-                                                       ((unsigned char *) buf) + iPair * 3;
-                                               JSAMPLE *in_ptr = line_work_buf + iPair * 2;
-
-                                               out_ptr[0] = (unsigned char)((in_ptr[0] & 0xff0) >> 4);
-                                               out_ptr[1] = (unsigned char)(((in_ptr[0] & 0xf) << 4)
-                                                       | ((in_ptr[1] & 0xf00) >> 8));
-                                               out_ptr[2] = (unsigned char)(((in_ptr[1] & 0xff) >> 0));
-                                       }
-                               }
-                               else if( sp->cinfo.d.data_precision == 8 )
-                               {
-                                       int value_count = (sp->cinfo.d.output_width
-                                                          * sp->cinfo.d.num_components);
-                                       int iValue;
-
-                                       for( iValue = 0; iValue < value_count; iValue++ )
-                                       {
-                                               ((unsigned char *) buf)[iValue] =
-                                                       line_work_buf[iValue] & 0xff;
-                                       }
-                               }
-                       }
-
-                       ++tif->tif_row;
-                       buf += sp->bytesperline;
-                       cc -= sp->bytesperline;
-               } while (--nrows > 0);
-
-               if( line_work_buf != NULL )
-                       _TIFFfree( line_work_buf );
+            line_work_buf = (TIFF_JSAMPROW)_TIFFmallocExt(
+                tif, sizeof(short) * sp->cinfo.d.output_width *
+                         sp->cinfo.d.num_components);
         }
 
-        /* Update information on consumed data */
-        tif->tif_rawcp = (uint8*) sp->src.next_input_byte;
-        tif->tif_rawcc = sp->src.bytes_in_buffer;
-                
-	/* Close down the decompressor if we've finished the strip or tile. */
-	return sp->cinfo.d.output_scanline < sp->cinfo.d.output_height
-                || TIFFjpeg_finish_decompress(sp);
+        do
+        {
+            if (line_work_buf != NULL)
+            {
+                /*
+                 * In the MK1 case, we always read into a 16bit
+                 * buffer, and then pack down to 12bit or 8bit.
+                 * In 6B case we only read into 16 bit buffer
+                 * for 12bit data, which we need to repack.
+                 */
+                if (TIFFjpeg_read_scanlines(sp, &line_work_buf, 1) != 1)
+                    return (0);
+
+                if (sp->cinfo.d.data_precision == 12)
+                {
+                    int value_pairs = (sp->cinfo.d.output_width *
+                                       sp->cinfo.d.num_components) /
+                                      2;
+                    int iPair;
+
+                    for (iPair = 0; iPair < value_pairs; iPair++)
+                    {
+                        unsigned char *out_ptr =
+                            ((unsigned char *)buf) + iPair * 3;
+                        TIFF_JSAMPLE *in_ptr = line_work_buf + iPair * 2;
+
+                        out_ptr[0] = (unsigned char)((in_ptr[0] & 0xff0) >> 4);
+                        out_ptr[1] =
+                            (unsigned char)(((in_ptr[0] & 0xf) << 4) |
+                                            ((in_ptr[1] & 0xf00) >> 8));
+                        out_ptr[2] = (unsigned char)(((in_ptr[1] & 0xff) >> 0));
+                    }
+                }
+                else if (sp->cinfo.d.data_precision == 8)
+                {
+                    int value_count =
+                        (sp->cinfo.d.output_width * sp->cinfo.d.num_components);
+                    int iValue;
+
+                    for (iValue = 0; iValue < value_count; iValue++)
+                    {
+                        ((unsigned char *)buf)[iValue] =
+                            line_work_buf[iValue] & 0xff;
+                    }
+                }
+            }
+
+            ++tif->tif_row;
+            buf += sp->bytesperline;
+            cc -= sp->bytesperline;
+        } while (--nrows > 0);
+
+        if (line_work_buf != NULL)
+            _TIFFfreeExt(tif, line_work_buf);
+    }
+
+    /* Update information on consumed data */
+    tif->tif_rawcp = (uint8_t *)sp->src.next_input_byte;
+    tif->tif_rawcc = sp->src.bytes_in_buffer;
+
+    /* Close down the decompressor if we've finished the strip or tile. */
+    return sp->cinfo.d.output_scanline < sp->cinfo.d.output_height ||
+           TIFFjpeg_finish_decompress(sp);
 }
 #endif /* JPEG_LIB_MK1_OR_12BIT */
 
-/*ARGSUSED*/ static int
-DecodeRowError(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s)
+/*ARGSUSED*/ static int DecodeRowError(TIFF *tif, uint8_t *buf, tmsize_t cc,
+                                       uint16_t s)
 
 {
-    (void) buf;
-    (void) cc;
-    (void) s;
+    (void)buf;
+    (void)cc;
+    (void)s;
 
-    TIFFErrorExt(tif->tif_clientdata, "TIFFReadScanline",
-                 "scanline oriented access is not supported for downsampled JPEG compressed images, consider enabling TIFF_JPEGCOLORMODE as JPEGCOLORMODE_RGB." );
+    TIFFErrorExtR(
+        tif, "TIFFReadScanline",
+        "scanline oriented access is not supported for downsampled JPEG "
+        "compressed images, consider enabling TIFFTAG_JPEGCOLORMODE as "
+        "JPEGCOLORMODE_RGB.");
     return 0;
 }
 
@@ -1502,926 +1645,1079 @@
  * Decode a chunk of pixels.
  * Returned data is downsampled per sampling factors.
  */
-/*ARGSUSED*/ static int
-JPEGDecodeRaw(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s)
+/*ARGSUSED*/ static int JPEGDecodeRaw(TIFF *tif, uint8_t *buf, tmsize_t cc,
+                                      uint16_t s)
 {
-	JPEGState *sp = JState(tif);
-	tmsize_t nrows;
-        TIFFDirectory *td = &tif->tif_dir;
-	(void) s;
+    JPEGState *sp = JState(tif);
+    tmsize_t nrows;
+    TIFFDirectory *td = &tif->tif_dir;
+    (void)s;
 
-        nrows = sp->cinfo.d.image_height;
-        /* For last strip, limit number of rows to its truncated height */
-        /* even if the codestream height is larger (which is not compliant, */
-        /* but that we tolerate) */
-        if( (uint32)nrows > td->td_imagelength - tif->tif_row && !isTiled(tif) )
-            nrows = td->td_imagelength - tif->tif_row;
-
-	/* data is expected to be read in multiples of a scanline */
-	if ( nrows != 0 ) {
-
-		/* Cb,Cr both have sampling factors 1, so this is correct */
-		JDIMENSION clumps_per_line = sp->cinfo.d.comp_info[1].downsampled_width;            
-		int samples_per_clump = sp->samplesperclump;
+    nrows = sp->cinfo.d.image_height;
+    /* For last strip, limit number of rows to its truncated height */
+    /* even if the codestream height is larger (which is not compliant, */
+    /* but that we tolerate) */
+    if ((uint32_t)nrows > td->td_imagelength - tif->tif_row && !isTiled(tif))
+        nrows = td->td_imagelength - tif->tif_row;
 
 #if defined(JPEG_LIB_MK1_OR_12BIT)
-		unsigned short* tmpbuf = _TIFFmalloc(sizeof(unsigned short) *
-						     sp->cinfo.d.output_width *
-						     sp->cinfo.d.num_components);
-		if(tmpbuf==NULL) {
-                        TIFFErrorExt(tif->tif_clientdata, "JPEGDecodeRaw",
-				     "Out of memory");
-			return 0;
-                }
+    unsigned short *tmpbuf = NULL;
 #endif
 
-		do {
-			jpeg_component_info *compptr;
-			int ci, clumpoffset;
+    /* data is expected to be read in multiples of a scanline */
+    if (nrows != 0)
+    {
 
-                        if( cc < sp->bytesperline ) {
-				TIFFErrorExt(tif->tif_clientdata, "JPEGDecodeRaw",
-					     "application buffer not large enough for all data.");
-				return 0;
-                        }
+        /* Cb,Cr both have sampling factors 1, so this is correct */
+        JDIMENSION clumps_per_line = sp->cinfo.d.comp_info[1].downsampled_width;
+        int samples_per_clump = sp->samplesperclump;
 
-			/* Reload downsampled-data buffer if needed */
-			if (sp->scancount >= DCTSIZE) {
-				int n = sp->cinfo.d.max_v_samp_factor * DCTSIZE;
-				if (TIFFjpeg_read_raw_data(sp, sp->ds_buffer, n) != n)
-					return (0);
-				sp->scancount = 0;
-			}
-			/*
-			 * Fastest way to unseparate data is to make one pass
-			 * over the scanline for each row of each component.
-			 */
-			clumpoffset = 0;    /* first sample in clump */
-			for (ci = 0, compptr = sp->cinfo.d.comp_info;
-			     ci < sp->cinfo.d.num_components;
-			     ci++, compptr++) {
-				int hsamp = compptr->h_samp_factor;
-				int vsamp = compptr->v_samp_factor;
-				int ypos;
-
-				for (ypos = 0; ypos < vsamp; ypos++) {
-					JSAMPLE *inptr = sp->ds_buffer[ci][sp->scancount*vsamp + ypos];
-					JDIMENSION nclump;
 #if defined(JPEG_LIB_MK1_OR_12BIT)
-					JSAMPLE *outptr = (JSAMPLE*)tmpbuf + clumpoffset;
+        tmpbuf = _TIFFmallocExt(tif, sizeof(unsigned short) *
+                                         sp->cinfo.d.output_width *
+                                         sp->cinfo.d.num_components);
+        if (tmpbuf == NULL)
+        {
+            TIFFErrorExtR(tif, "JPEGDecodeRaw", "Out of memory");
+            return 0;
+        }
+#endif
+
+        do
+        {
+            jpeg_component_info *compptr;
+            int ci, clumpoffset;
+
+            if (cc < sp->bytesperline)
+            {
+                TIFFErrorExtR(
+                    tif, "JPEGDecodeRaw",
+                    "application buffer not large enough for all data.");
+                goto error;
+            }
+
+            /* Reload downsampled-data buffer if needed */
+            if (sp->scancount >= DCTSIZE)
+            {
+                int n = sp->cinfo.d.max_v_samp_factor * DCTSIZE;
+                if (TIFFjpeg_read_raw_data(sp, sp->ds_buffer, n) != n)
+                    goto error;
+                sp->scancount = 0;
+            }
+            /*
+             * Fastest way to unseparate data is to make one pass
+             * over the scanline for each row of each component.
+             */
+            clumpoffset = 0; /* first sample in clump */
+            for (ci = 0, compptr = sp->cinfo.d.comp_info;
+                 ci < sp->cinfo.d.num_components; ci++, compptr++)
+            {
+                int hsamp = compptr->h_samp_factor;
+                int vsamp = compptr->v_samp_factor;
+                int ypos;
+
+                for (ypos = 0; ypos < vsamp; ypos++)
+                {
+                    TIFF_JSAMPLE *inptr =
+                        sp->ds_buffer[ci][sp->scancount * vsamp + ypos];
+                    JDIMENSION nclump;
+#if defined(JPEG_LIB_MK1_OR_12BIT)
+                    TIFF_JSAMPLE *outptr = (TIFF_JSAMPLE *)tmpbuf + clumpoffset;
 #else
-					JSAMPLE *outptr = (JSAMPLE*)buf + clumpoffset;
-					if (cc < (tmsize_t)(clumpoffset + (tmsize_t)samples_per_clump*(clumps_per_line-1) + hsamp)) {
-						TIFFErrorExt(tif->tif_clientdata, "JPEGDecodeRaw",
-							     "application buffer not large enough for all data, possible subsampling issue");
-						return 0;
-					}
+                    TIFF_JSAMPLE *outptr = (TIFF_JSAMPLE *)buf + clumpoffset;
+                    if (cc < (tmsize_t)(clumpoffset +
+                                        (tmsize_t)samples_per_clump *
+                                            (clumps_per_line - 1) +
+                                        hsamp))
+                    {
+                        TIFFErrorExtR(
+                            tif, "JPEGDecodeRaw",
+                            "application buffer not large enough for all data, "
+                            "possible subsampling issue");
+                        goto error;
+                    }
 #endif
 
-					if (hsamp == 1) {
-						/* fast path for at least Cb and Cr */
-						for (nclump = clumps_per_line; nclump-- > 0; ) {
-							outptr[0] = *inptr++;
-							outptr += samples_per_clump;
-						}
-					} else {
-						int xpos;
+                    if (hsamp == 1)
+                    {
+                        /* fast path for at least Cb and Cr */
+                        for (nclump = clumps_per_line; nclump-- > 0;)
+                        {
+                            outptr[0] = *inptr++;
+                            outptr += samples_per_clump;
+                        }
+                    }
+                    else
+                    {
+                        int xpos;
 
-						/* general case */
-						for (nclump = clumps_per_line; nclump-- > 0; ) {
-							for (xpos = 0; xpos < hsamp; xpos++)
-								outptr[xpos] = *inptr++;
-							outptr += samples_per_clump;
-						}
-					}
-					clumpoffset += hsamp;
-				}
-			}
+                        /* general case */
+                        for (nclump = clumps_per_line; nclump-- > 0;)
+                        {
+                            for (xpos = 0; xpos < hsamp; xpos++)
+                                outptr[xpos] = *inptr++;
+                            outptr += samples_per_clump;
+                        }
+                    }
+                    clumpoffset += hsamp;
+                }
+            }
 
 #if defined(JPEG_LIB_MK1_OR_12BIT)
-			{
-				if (sp->cinfo.d.data_precision == 8)
-				{
-					int i=0;
-					int len = sp->cinfo.d.output_width * sp->cinfo.d.num_components;
-					for (i=0; i<len; i++)
-					{
-						((unsigned char*)buf)[i] = tmpbuf[i] & 0xff;
-					}
-				}
-				else
-				{         /* 12-bit */
-					int value_pairs = (sp->cinfo.d.output_width
-							   * sp->cinfo.d.num_components) / 2;
-					int iPair;
-					for( iPair = 0; iPair < value_pairs; iPair++ )
-					{
-						unsigned char *out_ptr = ((unsigned char *) buf) + iPair * 3;
-						JSAMPLE *in_ptr = (JSAMPLE *) (tmpbuf + iPair * 2);
-						out_ptr[0] = (unsigned char)((in_ptr[0] & 0xff0) >> 4);
-						out_ptr[1] = (unsigned char)(((in_ptr[0] & 0xf) << 4)
-							| ((in_ptr[1] & 0xf00) >> 8));
-						out_ptr[2] = (unsigned char)(((in_ptr[1] & 0xff) >> 0));
-					}
-				}
-			}
+            {
+                if (sp->cinfo.d.data_precision == 8)
+                {
+                    int i = 0;
+                    int len =
+                        sp->cinfo.d.output_width * sp->cinfo.d.num_components;
+                    for (i = 0; i < len; i++)
+                    {
+                        ((unsigned char *)buf)[i] = tmpbuf[i] & 0xff;
+                    }
+                }
+                else
+                { /* 12-bit */
+                    int value_pairs = (sp->cinfo.d.output_width *
+                                       sp->cinfo.d.num_components) /
+                                      2;
+                    int iPair;
+                    for (iPair = 0; iPair < value_pairs; iPair++)
+                    {
+                        unsigned char *out_ptr =
+                            ((unsigned char *)buf) + iPair * 3;
+                        JSAMPLE *in_ptr = (JSAMPLE *)(tmpbuf + iPair * 2);
+                        out_ptr[0] = (unsigned char)((in_ptr[0] & 0xff0) >> 4);
+                        out_ptr[1] =
+                            (unsigned char)(((in_ptr[0] & 0xf) << 4) |
+                                            ((in_ptr[1] & 0xf00) >> 8));
+                        out_ptr[2] = (unsigned char)(((in_ptr[1] & 0xff) >> 0));
+                    }
+                }
+            }
 #endif
 
-			sp->scancount ++;
-			tif->tif_row += sp->v_sampling;
+            sp->scancount++;
+            tif->tif_row += sp->v_sampling;
 
-			buf += sp->bytesperline;
-			cc -= sp->bytesperline;
+            buf += sp->bytesperline;
+            cc -= sp->bytesperline;
 
-			nrows -= sp->v_sampling;
-		} while (nrows > 0);
+            nrows -= sp->v_sampling;
+        } while (nrows > 0);
 
 #if defined(JPEG_LIB_MK1_OR_12BIT)
-		_TIFFfree(tmpbuf);
+        _TIFFfreeExt(tif, tmpbuf);
 #endif
+    }
 
-	}
+    /* Close down the decompressor if done. */
+    return sp->cinfo.d.output_scanline < sp->cinfo.d.output_height ||
+           TIFFjpeg_finish_decompress(sp);
 
-	/* Close down the decompressor if done. */
-	return sp->cinfo.d.output_scanline < sp->cinfo.d.output_height
-		|| TIFFjpeg_finish_decompress(sp);
+error:
+#if defined(JPEG_LIB_MK1_OR_12BIT)
+    _TIFFfreeExt(tif, tmpbuf);
+#endif
+    return 0;
 }
 
-
 /*
  * JPEG Encoding.
  */
 
-static void
-unsuppress_quant_table (JPEGState* sp, int tblno)
+static void unsuppress_quant_table(JPEGState *sp, int tblno)
 {
-	JQUANT_TBL* qtbl;
+    JQUANT_TBL *qtbl;
 
-	if ((qtbl = sp->cinfo.c.quant_tbl_ptrs[tblno]) != NULL)
-		qtbl->sent_table = FALSE;
+    if ((qtbl = sp->cinfo.c.quant_tbl_ptrs[tblno]) != NULL)
+        qtbl->sent_table = FALSE;
 }
 
-static void
-suppress_quant_table (JPEGState* sp, int tblno)
+static void suppress_quant_table(JPEGState *sp, int tblno)
 {
-	JQUANT_TBL* qtbl;
+    JQUANT_TBL *qtbl;
 
-	if ((qtbl = sp->cinfo.c.quant_tbl_ptrs[tblno]) != NULL)
-		qtbl->sent_table = TRUE;
+    if ((qtbl = sp->cinfo.c.quant_tbl_ptrs[tblno]) != NULL)
+        qtbl->sent_table = TRUE;
 }
 
-static void
-unsuppress_huff_table (JPEGState* sp, int tblno)
+static void unsuppress_huff_table(JPEGState *sp, int tblno)
 {
-	JHUFF_TBL* htbl;
+    JHUFF_TBL *htbl;
 
-	if ((htbl = sp->cinfo.c.dc_huff_tbl_ptrs[tblno]) != NULL)
-		htbl->sent_table = FALSE;
-	if ((htbl = sp->cinfo.c.ac_huff_tbl_ptrs[tblno]) != NULL)
-		htbl->sent_table = FALSE;
+    if ((htbl = sp->cinfo.c.dc_huff_tbl_ptrs[tblno]) != NULL)
+        htbl->sent_table = FALSE;
+    if ((htbl = sp->cinfo.c.ac_huff_tbl_ptrs[tblno]) != NULL)
+        htbl->sent_table = FALSE;
 }
 
-static void
-suppress_huff_table (JPEGState* sp, int tblno)
+static void suppress_huff_table(JPEGState *sp, int tblno)
 {
-	JHUFF_TBL* htbl;
+    JHUFF_TBL *htbl;
 
-	if ((htbl = sp->cinfo.c.dc_huff_tbl_ptrs[tblno]) != NULL)
-		htbl->sent_table = TRUE;
-	if ((htbl = sp->cinfo.c.ac_huff_tbl_ptrs[tblno]) != NULL)
-		htbl->sent_table = TRUE;
+    if ((htbl = sp->cinfo.c.dc_huff_tbl_ptrs[tblno]) != NULL)
+        htbl->sent_table = TRUE;
+    if ((htbl = sp->cinfo.c.ac_huff_tbl_ptrs[tblno]) != NULL)
+        htbl->sent_table = TRUE;
 }
 
-static int
-prepare_JPEGTables(TIFF* tif)
+static int prepare_JPEGTables(TIFF *tif)
 {
-	JPEGState* sp = JState(tif);
+    JPEGState *sp = JState(tif);
 
-	/* Initialize quant tables for current quality setting */
-	if (!TIFFjpeg_set_quality(sp, sp->jpegquality, FALSE))
-		return (0);
-	/* Mark only the tables we want for output */
-	/* NB: chrominance tables are currently used only with YCbCr */
-	if (!TIFFjpeg_suppress_tables(sp, TRUE))
-		return (0);
-	if (sp->jpegtablesmode & JPEGTABLESMODE_QUANT) {
-		unsuppress_quant_table(sp, 0);
-		if (sp->photometric == PHOTOMETRIC_YCBCR)
-			unsuppress_quant_table(sp, 1);
-	}
-	if (sp->jpegtablesmode & JPEGTABLESMODE_HUFF) {
-		unsuppress_huff_table(sp, 0);
-		if (sp->photometric == PHOTOMETRIC_YCBCR)
-			unsuppress_huff_table(sp, 1);
-	}
-	/* Direct libjpeg output into jpegtables */
-	if (!TIFFjpeg_tables_dest(sp, tif))
-		return (0);
-	/* Emit tables-only datastream */
-	if (!TIFFjpeg_write_tables(sp))
-		return (0);
+    /* Initialize quant tables for current quality setting */
+    if (!TIFFjpeg_set_quality(sp, sp->otherSettings.jpegquality, FALSE))
+        return (0);
+    /* Mark only the tables we want for output */
+    /* NB: chrominance tables are currently used only with YCbCr */
+    if (!TIFFjpeg_suppress_tables(sp, TRUE))
+        return (0);
+    if (sp->otherSettings.jpegtablesmode & JPEGTABLESMODE_QUANT)
+    {
+        unsuppress_quant_table(sp, 0);
+        if (sp->photometric == PHOTOMETRIC_YCBCR)
+            unsuppress_quant_table(sp, 1);
+    }
+    if (sp->otherSettings.jpegtablesmode & JPEGTABLESMODE_HUFF)
+    {
+        unsuppress_huff_table(sp, 0);
+        if (sp->photometric == PHOTOMETRIC_YCBCR)
+            unsuppress_huff_table(sp, 1);
+    }
+    /* Direct libjpeg output into otherSettings.jpegtables */
+    if (!TIFFjpeg_tables_dest(sp, tif))
+        return (0);
+    /* Emit tables-only datastream */
+    if (!TIFFjpeg_write_tables(sp))
+        return (0);
 
-	return (1);
+    return (1);
 }
 
-static int
-JPEGSetupEncode(TIFF* tif)
+#if defined(JPEG_LIB_VERSION_MAJOR) &&                                         \
+    (JPEG_LIB_VERSION_MAJOR > 9 ||                                             \
+     (JPEG_LIB_VERSION_MAJOR == 9 && JPEG_LIB_VERSION_MINOR >= 4))
+/* This is a modified version of std_huff_tables() from jcparam.c
+ * in libjpeg-9d because it no longer initializes default Huffman
+ * tables in jpeg_set_defaults(). */
+static void TIFF_std_huff_tables(j_compress_ptr cinfo)
 {
-	JPEGState* sp = JState(tif);
-	TIFFDirectory *td = &tif->tif_dir;
-	static const char module[] = "JPEGSetupEncode";
 
-#if defined(JPEG_DUAL_MODE_8_12) && !defined(TIFFInitJPEG)
-        if( tif->tif_dir.td_bitspersample == 12 )
-            return TIFFReInitJPEG_12( tif, COMPRESSION_JPEG, 1 );
+    if (cinfo->dc_huff_tbl_ptrs[0] == NULL)
+    {
+        (void)jpeg_std_huff_table((j_common_ptr)cinfo, TRUE, 0);
+    }
+    if (cinfo->ac_huff_tbl_ptrs[0] == NULL)
+    {
+        (void)jpeg_std_huff_table((j_common_ptr)cinfo, FALSE, 0);
+    }
+    if (cinfo->dc_huff_tbl_ptrs[1] == NULL)
+    {
+        (void)jpeg_std_huff_table((j_common_ptr)cinfo, TRUE, 1);
+    }
+    if (cinfo->ac_huff_tbl_ptrs[1] == NULL)
+    {
+        (void)jpeg_std_huff_table((j_common_ptr)cinfo, FALSE, 1);
+    }
+}
 #endif
 
-        JPEGInitializeLibJPEG( tif, FALSE );
+static int JPEGSetupEncode(TIFF *tif)
+{
+    JPEGState *sp = JState(tif);
+    TIFFDirectory *td = &tif->tif_dir;
+    static const char module[] = "JPEGSetupEncode";
 
-	assert(sp != NULL);
-	assert(!sp->cinfo.comm.is_decompressor);
+#if defined(JPEG_DUAL_MODE_8_12) && !defined(FROM_TIF_JPEG_12)
+    if (tif->tif_dir.td_bitspersample == 12)
+    {
+        /* We pass a pointer to a copy of otherSettings, since */
+        /* TIFFReInitJPEG_12() will clear sp */
+        JPEGOtherSettings savedOtherSettings = sp->otherSettings;
+        return TIFFReInitJPEG_12(tif, &savedOtherSettings, COMPRESSION_JPEG, 1);
+    }
+#endif
 
-	sp->photometric = td->td_photometric;
+    JPEGInitializeLibJPEG(tif, FALSE);
 
-	/*
-	 * Initialize all JPEG parameters to default values.
-	 * Note that jpeg_set_defaults needs legal values for
-	 * in_color_space and input_components.
-	 */
-	if (td->td_planarconfig == PLANARCONFIG_CONTIG) {
-		sp->cinfo.c.input_components = td->td_samplesperpixel;
-		if (sp->photometric == PHOTOMETRIC_YCBCR) {
-			if (sp->jpegcolormode == JPEGCOLORMODE_RGB) {
-				sp->cinfo.c.in_color_space = JCS_RGB;
-			} else {
-				sp->cinfo.c.in_color_space = JCS_YCbCr;
-			}
-		} else {
-			if ((td->td_photometric == PHOTOMETRIC_MINISWHITE || td->td_photometric == PHOTOMETRIC_MINISBLACK) && td->td_samplesperpixel == 1)
-				sp->cinfo.c.in_color_space = JCS_GRAYSCALE;
-			else if (td->td_photometric == PHOTOMETRIC_RGB && td->td_samplesperpixel == 3)
-				sp->cinfo.c.in_color_space = JCS_RGB;
-			else if (td->td_photometric == PHOTOMETRIC_SEPARATED && td->td_samplesperpixel == 4)
-				sp->cinfo.c.in_color_space = JCS_CMYK;
-			else
-				sp->cinfo.c.in_color_space = JCS_UNKNOWN;
-		}
-	} else {
-		sp->cinfo.c.input_components = 1;
-		sp->cinfo.c.in_color_space = JCS_UNKNOWN;
-	}
-	if (!TIFFjpeg_set_defaults(sp))
-		return (0);
-	/* Set per-file parameters */
-	switch (sp->photometric) {
-	case PHOTOMETRIC_YCBCR:
-		sp->h_sampling = td->td_ycbcrsubsampling[0];
-		sp->v_sampling = td->td_ycbcrsubsampling[1];
-                if( sp->h_sampling == 0 || sp->v_sampling == 0 )
+    assert(sp != NULL);
+    assert(!sp->cinfo.comm.is_decompressor);
+
+    sp->photometric = td->td_photometric;
+
+    /*
+     * Initialize all JPEG parameters to default values.
+     * Note that jpeg_set_defaults needs legal values for
+     * in_color_space and input_components.
+     */
+    if (td->td_planarconfig == PLANARCONFIG_CONTIG)
+    {
+        sp->cinfo.c.input_components = td->td_samplesperpixel;
+        if (sp->photometric == PHOTOMETRIC_YCBCR)
+        {
+            if (sp->otherSettings.jpegcolormode == JPEGCOLORMODE_RGB)
+            {
+                sp->cinfo.c.in_color_space = JCS_RGB;
+            }
+            else
+            {
+                sp->cinfo.c.in_color_space = JCS_YCbCr;
+            }
+        }
+        else
+        {
+            if ((td->td_photometric == PHOTOMETRIC_MINISWHITE ||
+                 td->td_photometric == PHOTOMETRIC_MINISBLACK) &&
+                td->td_samplesperpixel == 1)
+                sp->cinfo.c.in_color_space = JCS_GRAYSCALE;
+            else if (td->td_photometric == PHOTOMETRIC_RGB &&
+                     td->td_samplesperpixel == 3)
+                sp->cinfo.c.in_color_space = JCS_RGB;
+            else if (td->td_photometric == PHOTOMETRIC_SEPARATED &&
+                     td->td_samplesperpixel == 4)
+                sp->cinfo.c.in_color_space = JCS_CMYK;
+            else
+                sp->cinfo.c.in_color_space = JCS_UNKNOWN;
+        }
+    }
+    else
+    {
+        sp->cinfo.c.input_components = 1;
+        sp->cinfo.c.in_color_space = JCS_UNKNOWN;
+    }
+    if (!TIFFjpeg_set_defaults(sp))
+        return (0);
+
+    /* mozjpeg by default enables progressive JPEG, which is illegal in
+     * JPEG-in-TIFF */
+    /* So explicitly disable it. */
+    if (sp->cinfo.c.num_scans != 0 &&
+        (sp->otherSettings.jpegtablesmode & JPEGTABLESMODE_HUFF) != 0)
+    {
+        /* it has been found that mozjpeg could create corrupt strips/tiles */
+        /* in non optimize_coding mode. */
+        TIFFWarningExtR(
+            tif, module,
+            "mozjpeg library likely detected. Disable emission of "
+            "Huffman tables in JpegTables tag, and use optimize_coding "
+            "to avoid potential issues");
+        sp->otherSettings.jpegtablesmode &= ~JPEGTABLESMODE_HUFF;
+    }
+    sp->cinfo.c.num_scans = 0;
+    sp->cinfo.c.scan_info = NULL;
+
+    /* Set per-file parameters */
+    switch (sp->photometric)
+    {
+        case PHOTOMETRIC_YCBCR:
+            sp->h_sampling = td->td_ycbcrsubsampling[0];
+            sp->v_sampling = td->td_ycbcrsubsampling[1];
+            if (sp->h_sampling == 0 || sp->v_sampling == 0)
+            {
+                TIFFErrorExtR(tif, module,
+                              "Invalig horizontal/vertical sampling value");
+                return (0);
+            }
+            if (td->td_bitspersample > 16)
+            {
+                TIFFErrorExtR(tif, module,
+                              "BitsPerSample %" PRIu16 " not allowed for JPEG",
+                              td->td_bitspersample);
+                return (0);
+            }
+
+            /*
+             * A ReferenceBlackWhite field *must* be present since the
+             * default value is inappropriate for YCbCr.  Fill in the
+             * proper value if application didn't set it.
+             */
+            {
+                float *ref;
+                if (!TIFFGetField(tif, TIFFTAG_REFERENCEBLACKWHITE, &ref))
                 {
-                    TIFFErrorExt(tif->tif_clientdata, module,
-                            "Invalig horizontal/vertical sampling value");
-                    return (0);
+                    float refbw[6];
+                    long top = 1L << td->td_bitspersample;
+                    refbw[0] = 0;
+                    refbw[1] = (float)(top - 1L);
+                    refbw[2] = (float)(top >> 1);
+                    refbw[3] = refbw[1];
+                    refbw[4] = refbw[2];
+                    refbw[5] = refbw[1];
+                    TIFFSetField(tif, TIFFTAG_REFERENCEBLACKWHITE, refbw);
                 }
-                if( td->td_bitspersample > 16 )
-                {
-                    TIFFErrorExt(tif->tif_clientdata, module,
-                                 "BitsPerSample %d not allowed for JPEG",
-                                 td->td_bitspersample);
-                    return (0);
-                }
+            }
+            break;
+        case PHOTOMETRIC_PALETTE: /* disallowed by Tech Note */
+        case PHOTOMETRIC_MASK:
+            TIFFErrorExtR(tif, module,
+                          "PhotometricInterpretation %" PRIu16
+                          " not allowed for JPEG",
+                          sp->photometric);
+            return (0);
+        default:
+            /* TIFF 6.0 forbids subsampling of all other color spaces */
+            sp->h_sampling = 1;
+            sp->v_sampling = 1;
+            break;
+    }
 
-		/*
-		 * A ReferenceBlackWhite field *must* be present since the
-		 * default value is inappropriate for YCbCr.  Fill in the
-		 * proper value if application didn't set it.
-		 */
-		{
-			float *ref;
-			if (!TIFFGetField(tif, TIFFTAG_REFERENCEBLACKWHITE,
-					  &ref)) {
-				float refbw[6];
-				long top = 1L << td->td_bitspersample;
-				refbw[0] = 0;
-				refbw[1] = (float)(top-1L);
-				refbw[2] = (float)(top>>1);
-				refbw[3] = refbw[1];
-				refbw[4] = refbw[2];
-				refbw[5] = refbw[1];
-				TIFFSetField(tif, TIFFTAG_REFERENCEBLACKWHITE,
-					     refbw);
-			}
-		}
-		break;
-	case PHOTOMETRIC_PALETTE:		/* disallowed by Tech Note */
-	case PHOTOMETRIC_MASK:
-		TIFFErrorExt(tif->tif_clientdata, module,
-			  "PhotometricInterpretation %d not allowed for JPEG",
-			  (int) sp->photometric);
-		return (0);
-	default:
-		/* TIFF 6.0 forbids subsampling of all other color spaces */
-		sp->h_sampling = 1;
-		sp->v_sampling = 1;
-		break;
-	}
+        /* Verify miscellaneous parameters */
 
-	/* Verify miscellaneous parameters */
-
-	/*
-	 * This would need work if libtiff ever supports different
-	 * depths for different components, or if libjpeg ever supports
-	 * run-time selection of depth.  Neither is imminent.
-	 */
+        /*
+         * This would need work if libtiff ever supports different
+         * depths for different components, or if libjpeg ever supports
+         * run-time selection of depth.  Neither is imminent.
+         */
 #ifdef JPEG_LIB_MK1
-        /* BITS_IN_JSAMPLE now permits 8 and 12 --- dgilbert */
-	if (td->td_bitspersample != 8 && td->td_bitspersample != 12) 
+    /* BITS_IN_JSAMPLE now permits 8 and 12 --- dgilbert */
+    if (td->td_bitspersample != 8 && td->td_bitspersample != 12)
 #else
-	if (td->td_bitspersample != BITS_IN_JSAMPLE )
+    if (td->td_bitspersample != BITS_IN_JSAMPLE)
 #endif
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "BitsPerSample %d not allowed for JPEG",
-			  (int) td->td_bitspersample);
-		return (0);
-	}
-	sp->cinfo.c.data_precision = td->td_bitspersample;
+    {
+        TIFFErrorExtR(tif, module,
+                      "BitsPerSample %" PRIu16 " not allowed for JPEG",
+                      td->td_bitspersample);
+        return (0);
+    }
+    sp->cinfo.c.data_precision = td->td_bitspersample;
 #ifdef JPEG_LIB_MK1
-        sp->cinfo.c.bits_in_jsample = td->td_bitspersample;
+    sp->cinfo.c.bits_in_jsample = td->td_bitspersample;
 #endif
-	if (isTiled(tif)) {
-		if ((td->td_tilelength % (sp->v_sampling * DCTSIZE)) != 0) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-				  "JPEG tile height must be multiple of %d",
-				  sp->v_sampling * DCTSIZE);
-			return (0);
-		}
-		if ((td->td_tilewidth % (sp->h_sampling * DCTSIZE)) != 0) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-				  "JPEG tile width must be multiple of %d",
-				  sp->h_sampling * DCTSIZE);
-			return (0);
-		}
-	} else {
-		if (td->td_rowsperstrip < td->td_imagelength &&
-		    (td->td_rowsperstrip % (sp->v_sampling * DCTSIZE)) != 0) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-				  "RowsPerStrip must be multiple of %d for JPEG",
-				  sp->v_sampling * DCTSIZE);
-			return (0);
-		}
-	}
+    if (isTiled(tif))
+    {
+        if ((td->td_tilelength % (sp->v_sampling * DCTSIZE)) != 0)
+        {
+            TIFFErrorExtR(tif, module,
+                          "JPEG tile height must be multiple of %" PRIu32,
+                          (uint32_t)(sp->v_sampling * DCTSIZE));
+            return (0);
+        }
+        if ((td->td_tilewidth % (sp->h_sampling * DCTSIZE)) != 0)
+        {
+            TIFFErrorExtR(tif, module,
+                          "JPEG tile width must be multiple of %" PRIu32,
+                          (uint32_t)(sp->h_sampling * DCTSIZE));
+            return (0);
+        }
+    }
+    else
+    {
+        if (td->td_rowsperstrip < td->td_imagelength &&
+            (td->td_rowsperstrip % (sp->v_sampling * DCTSIZE)) != 0)
+        {
+            TIFFErrorExtR(tif, module,
+                          "RowsPerStrip must be multiple of %" PRIu32
+                          " for JPEG",
+                          (uint32_t)(sp->v_sampling * DCTSIZE));
+            return (0);
+        }
+    }
 
-	/* Create a JPEGTables field if appropriate */
-	if (sp->jpegtablesmode & (JPEGTABLESMODE_QUANT|JPEGTABLESMODE_HUFF)) {
-                if( sp->jpegtables == NULL
-                    || memcmp(sp->jpegtables,"\0\0\0\0\0\0\0\0\0",8) == 0 )
-                {
-                        if (!prepare_JPEGTables(tif))
-                                return (0);
-                        /* Mark the field present */
-                        /* Can't use TIFFSetField since BEENWRITING is already set! */
-                        tif->tif_flags |= TIFF_DIRTYDIRECT;
-                        TIFFSetFieldBit(tif, FIELD_JPEGTABLES);
-                }
-	} else {
-		/* We do not support application-supplied JPEGTables, */
-		/* so mark the field not present */
-		TIFFClrFieldBit(tif, FIELD_JPEGTABLES);
-	}
+    /* Create a JPEGTables field if appropriate */
+    if (sp->otherSettings.jpegtablesmode &
+        (JPEGTABLESMODE_QUANT | JPEGTABLESMODE_HUFF))
+    {
+        if (sp->otherSettings.jpegtables == NULL ||
+            memcmp(sp->otherSettings.jpegtables, "\0\0\0\0\0\0\0\0\0", 8) == 0)
+        {
+#if defined(JPEG_LIB_VERSION_MAJOR) &&                                         \
+    (JPEG_LIB_VERSION_MAJOR > 9 ||                                             \
+     (JPEG_LIB_VERSION_MAJOR == 9 && JPEG_LIB_VERSION_MINOR >= 4))
+            if ((sp->otherSettings.jpegtablesmode & JPEGTABLESMODE_HUFF) != 0 &&
+                (sp->cinfo.c.dc_huff_tbl_ptrs[0] == NULL ||
+                 sp->cinfo.c.dc_huff_tbl_ptrs[1] == NULL ||
+                 sp->cinfo.c.ac_huff_tbl_ptrs[0] == NULL ||
+                 sp->cinfo.c.ac_huff_tbl_ptrs[1] == NULL))
+            {
+                /* libjpeg-9d no longer initializes default Huffman tables in */
+                /* jpeg_set_defaults() */
+                TIFF_std_huff_tables(&sp->cinfo.c);
+            }
+#endif
 
-	/* Direct libjpeg output to libtiff's output buffer */
-	TIFFjpeg_data_dest(sp, tif);
+            if (!prepare_JPEGTables(tif))
+                return (0);
+            /* Mark the field present */
+            /* Can't use TIFFSetField since BEENWRITING is already set! */
+            tif->tif_flags |= TIFF_DIRTYDIRECT;
+            TIFFSetFieldBit(tif, FIELD_JPEGTABLES);
+        }
+    }
+    else
+    {
+        /* We do not support application-supplied JPEGTables, */
+        /* so mark the field not present */
+        TIFFClrFieldBit(tif, FIELD_JPEGTABLES);
+    }
 
-	return (1);
+    /* Direct libjpeg output to libtiff's output buffer */
+    TIFFjpeg_data_dest(sp, tif);
+
+    return (1);
 }
 
 /*
  * Set encoding state at the start of a strip or tile.
  */
-static int
-JPEGPreEncode(TIFF* tif, uint16 s)
+static int JPEGPreEncode(TIFF *tif, uint16_t s)
 {
-	JPEGState *sp = JState(tif);
-	TIFFDirectory *td = &tif->tif_dir;
-	static const char module[] = "JPEGPreEncode";
-	uint32 segment_width, segment_height;
-	int downsampled_input;
+    JPEGState *sp = JState(tif);
+    TIFFDirectory *td = &tif->tif_dir;
+    static const char module[] = "JPEGPreEncode";
+    uint32_t segment_width, segment_height;
+    int downsampled_input;
 
-	assert(sp != NULL);
-  
-	if (sp->cinfo.comm.is_decompressor == 1)
-	{
-		tif->tif_setupencode( tif );
-	}
-  
-	assert(!sp->cinfo.comm.is_decompressor);
-	/*
-	 * Set encoding parameters for this strip/tile.
-	 */
-	if (isTiled(tif)) {
-		segment_width = td->td_tilewidth;
-		segment_height = td->td_tilelength;
-		sp->bytesperline = TIFFTileRowSize(tif);
-	} else {
-		segment_width = td->td_imagewidth;
-		segment_height = td->td_imagelength - tif->tif_row;
-		if (segment_height > td->td_rowsperstrip)
-			segment_height = td->td_rowsperstrip;
-		sp->bytesperline = TIFFScanlineSize(tif);
-	}
-	if (td->td_planarconfig == PLANARCONFIG_SEPARATE && s > 0) {
-		/* for PC 2, scale down the strip/tile size
-		 * to match a downsampled component
-		 */
-		segment_width = TIFFhowmany_32(segment_width, sp->h_sampling); 
-		segment_height = TIFFhowmany_32(segment_height, sp->v_sampling);
-	}
-	if (segment_width > 65535 || segment_height > 65535) {
-		TIFFErrorExt(tif->tif_clientdata, module, "Strip/tile too large for JPEG");
-		return (0);
-	}
-	sp->cinfo.c.image_width = segment_width;
-	sp->cinfo.c.image_height = segment_height;
-	downsampled_input = FALSE;
-	if (td->td_planarconfig == PLANARCONFIG_CONTIG) {
-		sp->cinfo.c.input_components = td->td_samplesperpixel;
-		if (sp->photometric == PHOTOMETRIC_YCBCR) {
-			if (sp->jpegcolormode != JPEGCOLORMODE_RGB) {
-				if (sp->h_sampling != 1 || sp->v_sampling != 1)
-					downsampled_input = TRUE;
-			}
-			if (!TIFFjpeg_set_colorspace(sp, JCS_YCbCr))
-				return (0);
-			/*
-			 * Set Y sampling factors;
-			 * we assume jpeg_set_colorspace() set the rest to 1
-			 */
-			sp->cinfo.c.comp_info[0].h_samp_factor = sp->h_sampling;
-			sp->cinfo.c.comp_info[0].v_samp_factor = sp->v_sampling;
-		} else {
-			if (!TIFFjpeg_set_colorspace(sp, sp->cinfo.c.in_color_space))
-				return (0);
-			/* jpeg_set_colorspace set all sampling factors to 1 */
-		}
-	} else {
-		if (!TIFFjpeg_set_colorspace(sp, JCS_UNKNOWN))
-			return (0);
-		sp->cinfo.c.comp_info[0].component_id = s;
-		/* jpeg_set_colorspace() set sampling factors to 1 */
-		if (sp->photometric == PHOTOMETRIC_YCBCR && s > 0) {
-			sp->cinfo.c.comp_info[0].quant_tbl_no = 1;
-			sp->cinfo.c.comp_info[0].dc_tbl_no = 1;
-			sp->cinfo.c.comp_info[0].ac_tbl_no = 1;
-		}
-	}
-	/* ensure libjpeg won't write any extraneous markers */
-	sp->cinfo.c.write_JFIF_header = FALSE;
-	sp->cinfo.c.write_Adobe_marker = FALSE;
-	/* set up table handling correctly */
-	/* calling TIFFjpeg_set_quality() causes quantization tables to be flagged */
-	/* as being to be emitted, which we don't want in the JPEGTABLESMODE_QUANT */
-	/* mode, so we must manually suppress them. However TIFFjpeg_set_quality() */
-	/* should really be called when dealing with files with directories with */
-	/* mixed qualities. see http://trac.osgeo.org/gdal/ticket/3539 */
-	if (!TIFFjpeg_set_quality(sp, sp->jpegquality, FALSE))
-		return (0);
-	if (sp->jpegtablesmode & JPEGTABLESMODE_QUANT) {
-		suppress_quant_table(sp, 0);
-		suppress_quant_table(sp, 1);
-	}
-	else {
-		unsuppress_quant_table(sp, 0);
-		unsuppress_quant_table(sp, 1);
-	}
-	if (sp->jpegtablesmode & JPEGTABLESMODE_HUFF)
-	{
-		/* Explicit suppression is only needed if we did not go through the */
-		/* prepare_JPEGTables() code path, which may be the case if updating */
-		/* an existing file */
-		suppress_huff_table(sp, 0);
-		suppress_huff_table(sp, 1);
-		sp->cinfo.c.optimize_coding = FALSE;
-	}
-	else
-		sp->cinfo.c.optimize_coding = TRUE;
-	if (downsampled_input) {
-		/* Need to use raw-data interface to libjpeg */
-		sp->cinfo.c.raw_data_in = TRUE;
-		tif->tif_encoderow = JPEGEncodeRaw;
-		tif->tif_encodestrip = JPEGEncodeRaw;
-		tif->tif_encodetile = JPEGEncodeRaw;
-	} else {
-		/* Use normal interface to libjpeg */
-		sp->cinfo.c.raw_data_in = FALSE;
-		tif->tif_encoderow = JPEGEncode;
-		tif->tif_encodestrip = JPEGEncode;
-		tif->tif_encodetile = JPEGEncode;
-	}
-	/* Start JPEG compressor */
-	if (!TIFFjpeg_start_compress(sp, FALSE))
-		return (0);
-	/* Allocate downsampled-data buffers if needed */
-	if (downsampled_input) {
-		if (!alloc_downsampled_buffers(tif, sp->cinfo.c.comp_info,
-					       sp->cinfo.c.num_components))
-			return (0);
-	}
-	sp->scancount = 0;
+    assert(sp != NULL);
 
-	return (1);
+    if (sp->cinfo.comm.is_decompressor == 1)
+    {
+        tif->tif_setupencode(tif);
+    }
+
+    assert(!sp->cinfo.comm.is_decompressor);
+    /*
+     * Set encoding parameters for this strip/tile.
+     */
+    if (isTiled(tif))
+    {
+        segment_width = td->td_tilewidth;
+        segment_height = td->td_tilelength;
+        sp->bytesperline = TIFFTileRowSize(tif);
+    }
+    else
+    {
+        segment_width = td->td_imagewidth;
+        segment_height = td->td_imagelength - tif->tif_row;
+        if (segment_height > td->td_rowsperstrip)
+            segment_height = td->td_rowsperstrip;
+        sp->bytesperline = TIFFScanlineSize(tif);
+    }
+    if (td->td_planarconfig == PLANARCONFIG_SEPARATE && s > 0)
+    {
+        /* for PC 2, scale down the strip/tile size
+         * to match a downsampled component
+         */
+        segment_width = TIFFhowmany_32(segment_width, sp->h_sampling);
+        segment_height = TIFFhowmany_32(segment_height, sp->v_sampling);
+    }
+    if (segment_width > 65535 || segment_height > 65535)
+    {
+        TIFFErrorExtR(tif, module, "Strip/tile too large for JPEG");
+        return (0);
+    }
+    sp->cinfo.c.image_width = segment_width;
+    sp->cinfo.c.image_height = segment_height;
+    downsampled_input = FALSE;
+    if (td->td_planarconfig == PLANARCONFIG_CONTIG)
+    {
+        sp->cinfo.c.input_components = td->td_samplesperpixel;
+        if (sp->photometric == PHOTOMETRIC_YCBCR)
+        {
+            if (sp->otherSettings.jpegcolormode != JPEGCOLORMODE_RGB)
+            {
+                if (sp->h_sampling != 1 || sp->v_sampling != 1)
+                    downsampled_input = TRUE;
+            }
+            if (!TIFFjpeg_set_colorspace(sp, JCS_YCbCr))
+                return (0);
+            /*
+             * Set Y sampling factors;
+             * we assume jpeg_set_colorspace() set the rest to 1
+             */
+            sp->cinfo.c.comp_info[0].h_samp_factor = sp->h_sampling;
+            sp->cinfo.c.comp_info[0].v_samp_factor = sp->v_sampling;
+        }
+        else
+        {
+            if (!TIFFjpeg_set_colorspace(sp, sp->cinfo.c.in_color_space))
+                return (0);
+            /* jpeg_set_colorspace set all sampling factors to 1 */
+        }
+    }
+    else
+    {
+        if (!TIFFjpeg_set_colorspace(sp, JCS_UNKNOWN))
+            return (0);
+        sp->cinfo.c.comp_info[0].component_id = s;
+        /* jpeg_set_colorspace() set sampling factors to 1 */
+        if (sp->photometric == PHOTOMETRIC_YCBCR && s > 0)
+        {
+            sp->cinfo.c.comp_info[0].quant_tbl_no = 1;
+            sp->cinfo.c.comp_info[0].dc_tbl_no = 1;
+            sp->cinfo.c.comp_info[0].ac_tbl_no = 1;
+        }
+    }
+    /* ensure libjpeg won't write any extraneous markers */
+    sp->cinfo.c.write_JFIF_header = FALSE;
+    sp->cinfo.c.write_Adobe_marker = FALSE;
+    /* set up table handling correctly */
+    /* calling TIFFjpeg_set_quality() causes quantization tables to be flagged
+     */
+    /* as being to be emitted, which we don't want in the JPEGTABLESMODE_QUANT
+     */
+    /* mode, so we must manually suppress them. However TIFFjpeg_set_quality()
+     */
+    /* should really be called when dealing with files with directories with */
+    /* mixed qualities. see http://trac.osgeo.org/gdal/ticket/3539 */
+    if (!TIFFjpeg_set_quality(sp, sp->otherSettings.jpegquality, FALSE))
+        return (0);
+    if (sp->otherSettings.jpegtablesmode & JPEGTABLESMODE_QUANT)
+    {
+        suppress_quant_table(sp, 0);
+        suppress_quant_table(sp, 1);
+    }
+    else
+    {
+        unsuppress_quant_table(sp, 0);
+        unsuppress_quant_table(sp, 1);
+    }
+    if (sp->otherSettings.jpegtablesmode & JPEGTABLESMODE_HUFF)
+    {
+        /* Explicit suppression is only needed if we did not go through the */
+        /* prepare_JPEGTables() code path, which may be the case if updating */
+        /* an existing file */
+        suppress_huff_table(sp, 0);
+        suppress_huff_table(sp, 1);
+        sp->cinfo.c.optimize_coding = FALSE;
+    }
+    else
+        sp->cinfo.c.optimize_coding = TRUE;
+    if (downsampled_input)
+    {
+        /* Need to use raw-data interface to libjpeg */
+        sp->cinfo.c.raw_data_in = TRUE;
+        tif->tif_encoderow = JPEGEncodeRaw;
+        tif->tif_encodestrip = JPEGEncodeRaw;
+        tif->tif_encodetile = JPEGEncodeRaw;
+    }
+    else
+    {
+        /* Use normal interface to libjpeg */
+        sp->cinfo.c.raw_data_in = FALSE;
+        tif->tif_encoderow = JPEGEncode;
+        tif->tif_encodestrip = JPEGEncode;
+        tif->tif_encodetile = JPEGEncode;
+    }
+    /* Start JPEG compressor */
+    if (!TIFFjpeg_start_compress(sp, FALSE))
+        return (0);
+    /* Allocate downsampled-data buffers if needed */
+    if (downsampled_input)
+    {
+        if (!alloc_downsampled_buffers(tif, sp->cinfo.c.comp_info,
+                                       sp->cinfo.c.num_components))
+            return (0);
+    }
+    sp->scancount = 0;
+
+    return (1);
 }
 
 /*
  * Encode a chunk of pixels.
  * "Standard" case: incoming data is not downsampled.
  */
-static int
-JPEGEncode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s)
+static int JPEGEncode(TIFF *tif, uint8_t *buf, tmsize_t cc, uint16_t s)
 {
-	JPEGState *sp = JState(tif);
-	tmsize_t nrows;
-	JSAMPROW bufptr[1];
-        short *line16 = NULL;
-        int    line16_count = 0;
+    JPEGState *sp = JState(tif);
+    tmsize_t nrows;
+    TIFF_JSAMPROW bufptr[1];
+    short *line16 = NULL;
+    int line16_count = 0;
 
-	(void) s;
-	assert(sp != NULL);
-	/* data is expected to be supplied in multiples of a scanline */
-	nrows = cc / sp->bytesperline;
-	if (cc % sp->bytesperline)
-            TIFFWarningExt(tif->tif_clientdata, tif->tif_name, 
-                           "fractional scanline discarded");
+    (void)s;
+    assert(sp != NULL);
+    /* data is expected to be supplied in multiples of a scanline */
+    nrows = cc / sp->bytesperline;
+    if (cc % sp->bytesperline)
+        TIFFWarningExtR(tif, tif->tif_name, "fractional scanline discarded");
 
-        /* The last strip will be limited to image size */
-        if( !isTiled(tif) && tif->tif_row+nrows > tif->tif_dir.td_imagelength )
-            nrows = tif->tif_dir.td_imagelength - tif->tif_row;
+    /* The last strip will be limited to image size */
+    if (!isTiled(tif) && tif->tif_row + nrows > tif->tif_dir.td_imagelength)
+        nrows = tif->tif_dir.td_imagelength - tif->tif_row;
 
-        if( sp->cinfo.c.data_precision == 12 )
+    if (sp->cinfo.c.data_precision == 12)
+    {
+        line16_count = (int)((sp->bytesperline * 2) / 3);
+        line16 = (short *)_TIFFmallocExt(tif, sizeof(short) * line16_count);
+        if (!line16)
         {
-            line16_count = (int)((sp->bytesperline * 2) / 3);
-            line16 = (short *) _TIFFmalloc(sizeof(short) * line16_count);
-            if (!line16)
-            {
-                TIFFErrorExt(tif->tif_clientdata,
-			     "JPEGEncode",
-                             "Failed to allocate memory");
+            TIFFErrorExtR(tif, "JPEGEncode", "Failed to allocate memory");
 
-                return 0;
+            return 0;
+        }
+    }
+
+    while (nrows-- > 0)
+    {
+
+        if (sp->cinfo.c.data_precision == 12)
+        {
+
+            int value_pairs = line16_count / 2;
+            int iPair;
+
+            bufptr[0] = (TIFF_JSAMPROW)line16;
+
+            for (iPair = 0; iPair < value_pairs; iPair++)
+            {
+                unsigned char *in_ptr = ((unsigned char *)buf) + iPair * 3;
+                TIFF_JSAMPLE *out_ptr = (TIFF_JSAMPLE *)(line16 + iPair * 2);
+
+                out_ptr[0] = (in_ptr[0] << 4) | ((in_ptr[1] & 0xf0) >> 4);
+                out_ptr[1] = ((in_ptr[1] & 0x0f) << 8) | in_ptr[2];
             }
         }
-            
-	while (nrows-- > 0) {
-
-            if( sp->cinfo.c.data_precision == 12 )
-            {
-
-                int value_pairs = line16_count / 2;
-                int iPair;
-
-		bufptr[0] = (JSAMPROW) line16;
-
-                for( iPair = 0; iPair < value_pairs; iPair++ )
-                {
-                    unsigned char *in_ptr =
-                        ((unsigned char *) buf) + iPair * 3;
-                    JSAMPLE *out_ptr = (JSAMPLE *) (line16 + iPair * 2);
-
-                    out_ptr[0] = (in_ptr[0] << 4) | ((in_ptr[1] & 0xf0) >> 4);
-                    out_ptr[1] = ((in_ptr[1] & 0x0f) << 8) | in_ptr[2];
-                }
-            }
-            else
-            {
-		bufptr[0] = (JSAMPROW) buf;
-            }
-            if (TIFFjpeg_write_scanlines(sp, bufptr, 1) != 1)
-                return (0);
-            if (nrows > 0)
-                tif->tif_row++;
-            buf += sp->bytesperline;
-	}
-
-        if( sp->cinfo.c.data_precision == 12 )
+        else
         {
-            _TIFFfree( line16 );
+            bufptr[0] = (TIFF_JSAMPROW)buf;
         }
-            
-	return (1);
+        if (TIFFjpeg_write_scanlines(sp, bufptr, 1) != 1)
+            return (0);
+        if (nrows > 0)
+            tif->tif_row++;
+        buf += sp->bytesperline;
+    }
+
+    if (sp->cinfo.c.data_precision == 12)
+    {
+        _TIFFfreeExt(tif, line16);
+    }
+
+    return (1);
 }
 
 /*
  * Encode a chunk of pixels.
  * Incoming data is expected to be downsampled per sampling factors.
  */
-static int
-JPEGEncodeRaw(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s)
+static int JPEGEncodeRaw(TIFF *tif, uint8_t *buf, tmsize_t cc, uint16_t s)
 {
-	JPEGState *sp = JState(tif);
-	JSAMPLE* inptr;
-	JSAMPLE* outptr;
-	tmsize_t nrows;
-	JDIMENSION clumps_per_line, nclump;
-	int clumpoffset, ci, xpos, ypos;
-	jpeg_component_info* compptr;
-	int samples_per_clump = sp->samplesperclump;
-	tmsize_t bytesperclumpline;
+    JPEGState *sp = JState(tif);
+    TIFF_JSAMPLE *inptr;
+    TIFF_JSAMPLE *outptr;
+    tmsize_t nrows;
+    JDIMENSION clumps_per_line, nclump;
+    int clumpoffset, ci, xpos, ypos;
+    jpeg_component_info *compptr;
+    int samples_per_clump = sp->samplesperclump;
+    tmsize_t bytesperclumpline;
 
-	(void) s;
-	assert(sp != NULL);
-	/* data is expected to be supplied in multiples of a clumpline */
-	/* a clumpline is equivalent to v_sampling desubsampled scanlines */
-	/* TODO: the following calculation of bytesperclumpline, should substitute calculation of sp->bytesperline, except that it is per v_sampling lines */
-	bytesperclumpline = ((((tmsize_t)sp->cinfo.c.image_width+sp->h_sampling-1)/sp->h_sampling)
-			     *((tmsize_t)sp->h_sampling*sp->v_sampling+2)*sp->cinfo.c.data_precision+7)
-			    /8;
+    (void)s;
+    assert(sp != NULL);
+    /* data is expected to be supplied in multiples of a clumpline */
+    /* a clumpline is equivalent to v_sampling desubsampled scanlines */
+    /* TODO: the following calculation of bytesperclumpline, should substitute
+     * calculation of sp->bytesperline, except that it is per v_sampling lines
+     */
+    bytesperclumpline =
+        ((((tmsize_t)sp->cinfo.c.image_width + sp->h_sampling - 1) /
+          sp->h_sampling) *
+             ((tmsize_t)sp->h_sampling * sp->v_sampling + 2) *
+             sp->cinfo.c.data_precision +
+         7) /
+        8;
 
-	nrows = ( cc / bytesperclumpline ) * sp->v_sampling;
-	if (cc % bytesperclumpline)
-		TIFFWarningExt(tif->tif_clientdata, tif->tif_name, "fractional scanline discarded");
+    nrows = (cc / bytesperclumpline) * sp->v_sampling;
+    if (cc % bytesperclumpline)
+        TIFFWarningExtR(tif, tif->tif_name, "fractional scanline discarded");
 
-	/* Cb,Cr both have sampling factors 1, so this is correct */
-	clumps_per_line = sp->cinfo.c.comp_info[1].downsampled_width;
+    /* Cb,Cr both have sampling factors 1, so this is correct */
+    clumps_per_line = sp->cinfo.c.comp_info[1].downsampled_width;
 
-	while (nrows > 0) {
-		/*
-		 * Fastest way to separate the data is to make one pass
-		 * over the scanline for each row of each component.
-		 */
-		clumpoffset = 0;		/* first sample in clump */
-		for (ci = 0, compptr = sp->cinfo.c.comp_info;
-		     ci < sp->cinfo.c.num_components;
-		     ci++, compptr++) {
-		    int hsamp = compptr->h_samp_factor;
-		    int vsamp = compptr->v_samp_factor;
-		    int padding = (int) (compptr->width_in_blocks * DCTSIZE -
-					 clumps_per_line * hsamp);
-		    for (ypos = 0; ypos < vsamp; ypos++) {
-			inptr = ((JSAMPLE*) buf) + clumpoffset;
-			outptr = sp->ds_buffer[ci][sp->scancount*vsamp + ypos];
-			if (hsamp == 1) {
-			    /* fast path for at least Cb and Cr */
-			    for (nclump = clumps_per_line; nclump-- > 0; ) {
-				*outptr++ = inptr[0];
-				inptr += samples_per_clump;
-			    }
-			} else {
-			    /* general case */
-			    for (nclump = clumps_per_line; nclump-- > 0; ) {
-				for (xpos = 0; xpos < hsamp; xpos++)
-				    *outptr++ = inptr[xpos];
-				inptr += samples_per_clump;
-			    }
-			}
-			/* pad each scanline as needed */
-			for (xpos = 0; xpos < padding; xpos++) {
-			    *outptr = outptr[-1];
-			    outptr++;
-			}
-			clumpoffset += hsamp;
-		    }
-		}
-		sp->scancount++;
-		if (sp->scancount >= DCTSIZE) {
-			int n = sp->cinfo.c.max_v_samp_factor * DCTSIZE;
-			if (TIFFjpeg_write_raw_data(sp, sp->ds_buffer, n) != n)
-				return (0);
-			sp->scancount = 0;
-		}
-		tif->tif_row += sp->v_sampling;
-		buf += bytesperclumpline;
-		nrows -= sp->v_sampling;
-	}
-	return (1);
+    while (nrows > 0)
+    {
+        /*
+         * Fastest way to separate the data is to make one pass
+         * over the scanline for each row of each component.
+         */
+        clumpoffset = 0; /* first sample in clump */
+        for (ci = 0, compptr = sp->cinfo.c.comp_info;
+             ci < sp->cinfo.c.num_components; ci++, compptr++)
+        {
+            int hsamp = compptr->h_samp_factor;
+            int vsamp = compptr->v_samp_factor;
+            int padding = (int)(compptr->width_in_blocks * DCTSIZE -
+                                clumps_per_line * hsamp);
+            for (ypos = 0; ypos < vsamp; ypos++)
+            {
+                inptr = ((TIFF_JSAMPLE *)buf) + clumpoffset;
+                outptr = sp->ds_buffer[ci][sp->scancount * vsamp + ypos];
+                if (hsamp == 1)
+                {
+                    /* fast path for at least Cb and Cr */
+                    for (nclump = clumps_per_line; nclump-- > 0;)
+                    {
+                        *outptr++ = inptr[0];
+                        inptr += samples_per_clump;
+                    }
+                }
+                else
+                {
+                    /* general case */
+                    for (nclump = clumps_per_line; nclump-- > 0;)
+                    {
+                        for (xpos = 0; xpos < hsamp; xpos++)
+                            *outptr++ = inptr[xpos];
+                        inptr += samples_per_clump;
+                    }
+                }
+                /* pad each scanline as needed */
+                for (xpos = 0; xpos < padding; xpos++)
+                {
+                    *outptr = outptr[-1];
+                    outptr++;
+                }
+                clumpoffset += hsamp;
+            }
+        }
+        sp->scancount++;
+        if (sp->scancount >= DCTSIZE)
+        {
+            int n = sp->cinfo.c.max_v_samp_factor * DCTSIZE;
+            if (TIFFjpeg_write_raw_data(sp, sp->ds_buffer, n) != n)
+                return (0);
+            sp->scancount = 0;
+        }
+        tif->tif_row += sp->v_sampling;
+        buf += bytesperclumpline;
+        nrows -= sp->v_sampling;
+    }
+    return (1);
 }
 
 /*
  * Finish up at the end of a strip or tile.
  */
-static int
-JPEGPostEncode(TIFF* tif)
+static int JPEGPostEncode(TIFF *tif)
 {
-	JPEGState *sp = JState(tif);
+    JPEGState *sp = JState(tif);
 
-	if (sp->scancount > 0) {
-		/*
-		 * Need to emit a partial bufferload of downsampled data.
-		 * Pad the data vertically.
-		 */
-		int ci, ypos, n;
-		jpeg_component_info* compptr;
+    if (sp->scancount > 0)
+    {
+        /*
+         * Need to emit a partial bufferload of downsampled data.
+         * Pad the data vertically.
+         */
+        int ci, ypos, n;
+        jpeg_component_info *compptr;
 
-		for (ci = 0, compptr = sp->cinfo.c.comp_info;
-		     ci < sp->cinfo.c.num_components;
-		     ci++, compptr++) {
-			int vsamp = compptr->v_samp_factor;
-			tmsize_t row_width = compptr->width_in_blocks * DCTSIZE
-				* sizeof(JSAMPLE);
-			for (ypos = sp->scancount * vsamp;
-			     ypos < DCTSIZE * vsamp; ypos++) {
-				_TIFFmemcpy((void*)sp->ds_buffer[ci][ypos],
-					    (void*)sp->ds_buffer[ci][ypos-1],
-					    row_width);
+        for (ci = 0, compptr = sp->cinfo.c.comp_info;
+             ci < sp->cinfo.c.num_components; ci++, compptr++)
+        {
+            int vsamp = compptr->v_samp_factor;
+            tmsize_t row_width =
+                compptr->width_in_blocks * DCTSIZE * sizeof(JSAMPLE);
+            for (ypos = sp->scancount * vsamp; ypos < DCTSIZE * vsamp; ypos++)
+            {
+                _TIFFmemcpy((void *)sp->ds_buffer[ci][ypos],
+                            (void *)sp->ds_buffer[ci][ypos - 1], row_width);
+            }
+        }
+        n = sp->cinfo.c.max_v_samp_factor * DCTSIZE;
+        if (TIFFjpeg_write_raw_data(sp, sp->ds_buffer, n) != n)
+            return (0);
+    }
 
-			}
-		}
-		n = sp->cinfo.c.max_v_samp_factor * DCTSIZE;
-		if (TIFFjpeg_write_raw_data(sp, sp->ds_buffer, n) != n)
-			return (0);
-	}
-
-	return (TIFFjpeg_finish_compress(JState(tif)));
+    return (TIFFjpeg_finish_compress(JState(tif)));
 }
 
-static void
-JPEGCleanup(TIFF* tif)
+static void JPEGCleanup(TIFF *tif)
 {
-	JPEGState *sp = JState(tif);
-	
-	assert(sp != 0);
+    JPEGState *sp = JState(tif);
 
-	tif->tif_tagmethods.vgetfield = sp->vgetparent;
-	tif->tif_tagmethods.vsetfield = sp->vsetparent;
-	tif->tif_tagmethods.printdir = sp->printdir;
-        if( sp->cinfo_initialized )
-                TIFFjpeg_destroy(sp);	/* release libjpeg resources */
-        if (sp->jpegtables)		/* tag value */
-                _TIFFfree(sp->jpegtables);
-	_TIFFfree(tif->tif_data);	/* release local state */
-	tif->tif_data = NULL;
+    assert(sp != 0);
 
-	_TIFFSetDefaultCompressionState(tif);
+    tif->tif_tagmethods.vgetfield = sp->otherSettings.vgetparent;
+    tif->tif_tagmethods.vsetfield = sp->otherSettings.vsetparent;
+    tif->tif_tagmethods.printdir = sp->otherSettings.printdir;
+    if (sp->cinfo_initialized)
+        TIFFjpeg_destroy(sp);         /* release libjpeg resources */
+    if (sp->otherSettings.jpegtables) /* tag value */
+        _TIFFfreeExt(tif, sp->otherSettings.jpegtables);
+    _TIFFfreeExt(tif, tif->tif_data); /* release local state */
+    tif->tif_data = NULL;
+
+    _TIFFSetDefaultCompressionState(tif);
 }
 
-static void 
-JPEGResetUpsampled( TIFF* tif )
+static void JPEGResetUpsampled(TIFF *tif)
 {
-	JPEGState* sp = JState(tif);
-	TIFFDirectory* td = &tif->tif_dir;
+    JPEGState *sp = JState(tif);
+    TIFFDirectory *td = &tif->tif_dir;
 
-	/*
-	 * Mark whether returned data is up-sampled or not so TIFFStripSize
-	 * and TIFFTileSize return values that reflect the true amount of
-	 * data.
-	 */
-	tif->tif_flags &= ~TIFF_UPSAMPLED;
-	if (td->td_planarconfig == PLANARCONFIG_CONTIG) {
-		if (td->td_photometric == PHOTOMETRIC_YCBCR &&
-		    sp->jpegcolormode == JPEGCOLORMODE_RGB) {
-			tif->tif_flags |= TIFF_UPSAMPLED;
-		} else {
+    /*
+     * Mark whether returned data is up-sampled or not so TIFFStripSize
+     * and TIFFTileSize return values that reflect the true amount of
+     * data.
+     */
+    tif->tif_flags &= ~TIFF_UPSAMPLED;
+    if (td->td_planarconfig == PLANARCONFIG_CONTIG)
+    {
+        if (td->td_photometric == PHOTOMETRIC_YCBCR &&
+            sp->otherSettings.jpegcolormode == JPEGCOLORMODE_RGB)
+        {
+            tif->tif_flags |= TIFF_UPSAMPLED;
+        }
+        else
+        {
 #ifdef notdef
-			if (td->td_ycbcrsubsampling[0] != 1 ||
-			    td->td_ycbcrsubsampling[1] != 1)
-				; /* XXX what about up-sampling? */
+            if (td->td_ycbcrsubsampling[0] != 1 ||
+                td->td_ycbcrsubsampling[1] != 1)
+                ; /* XXX what about up-sampling? */
 #endif
-		}
-	}
+        }
+    }
 
-	/*
-	 * Must recalculate cached tile size in case sampling state changed.
-	 * Should we really be doing this now if image size isn't set? 
-	 */
-        if( tif->tif_tilesize > 0 )
-            tif->tif_tilesize = isTiled(tif) ? TIFFTileSize(tif) : (tmsize_t)(-1);   
-        if( tif->tif_scanlinesize > 0 )
-            tif->tif_scanlinesize = TIFFScanlineSize(tif); 
+    /*
+     * Must recalculate cached tile size in case sampling state changed.
+     * Should we really be doing this now if image size isn't set?
+     */
+    if (tif->tif_tilesize > 0)
+        tif->tif_tilesize = isTiled(tif) ? TIFFTileSize(tif) : (tmsize_t)(-1);
+    if (tif->tif_scanlinesize > 0)
+        tif->tif_scanlinesize = TIFFScanlineSize(tif);
 }
 
-static int
-JPEGVSetField(TIFF* tif, uint32 tag, va_list ap)
+static int JPEGVSetField(TIFF *tif, uint32_t tag, va_list ap)
 {
-	JPEGState* sp = JState(tif);
-	const TIFFField* fip;
-	uint32 v32;
+    JPEGState *sp = JState(tif);
+    const TIFFField *fip;
+    uint32_t v32;
 
-	assert(sp != NULL);
+    assert(sp != NULL);
 
-	switch (tag) {
-	case TIFFTAG_JPEGTABLES:
-		v32 = (uint32) va_arg(ap, uint32);
-		if (v32 == 0) {
-			/* XXX */
-			return (0);
-		}
-		_TIFFsetByteArray(&sp->jpegtables, va_arg(ap, void*), v32);
-		sp->jpegtables_length = v32;
-		TIFFSetFieldBit(tif, FIELD_JPEGTABLES);
-		break;
-	case TIFFTAG_JPEGQUALITY:
-		sp->jpegquality = (int) va_arg(ap, int);
-		return (1);			/* pseudo tag */
-	case TIFFTAG_JPEGCOLORMODE:
-		sp->jpegcolormode = (int) va_arg(ap, int);
-		JPEGResetUpsampled( tif );
-		return (1);			/* pseudo tag */
-	case TIFFTAG_PHOTOMETRIC:
-	{
-		int ret_value = (*sp->vsetparent)(tif, tag, ap);
-		JPEGResetUpsampled( tif );
-		return ret_value;
-	}
-	case TIFFTAG_JPEGTABLESMODE:
-		sp->jpegtablesmode = (int) va_arg(ap, int);
-		return (1);			/* pseudo tag */
-	case TIFFTAG_YCBCRSUBSAMPLING:
-		/* mark the fact that we have a real ycbcrsubsampling! */
-		sp->ycbcrsampling_fetched = 1;
-		/* should we be recomputing upsampling info here? */
-		return (*sp->vsetparent)(tif, tag, ap);
-	default:
-		return (*sp->vsetparent)(tif, tag, ap);
-	}
+    switch (tag)
+    {
+        case TIFFTAG_JPEGTABLES:
+            v32 = (uint32_t)va_arg(ap, uint32_t);
+            if (v32 == 0)
+            {
+                /* XXX */
+                return (0);
+            }
+            _TIFFsetByteArrayExt(tif, &sp->otherSettings.jpegtables,
+                                 va_arg(ap, void *), v32);
+            sp->otherSettings.jpegtables_length = v32;
+            TIFFSetFieldBit(tif, FIELD_JPEGTABLES);
+            break;
+        case TIFFTAG_JPEGQUALITY:
+            sp->otherSettings.jpegquality = (int)va_arg(ap, int);
+            return (1); /* pseudo tag */
+        case TIFFTAG_JPEGCOLORMODE:
+            sp->otherSettings.jpegcolormode = (int)va_arg(ap, int);
+            JPEGResetUpsampled(tif);
+            return (1); /* pseudo tag */
+        case TIFFTAG_PHOTOMETRIC:
+        {
+            int ret_value = (*sp->otherSettings.vsetparent)(tif, tag, ap);
+            JPEGResetUpsampled(tif);
+            return ret_value;
+        }
+        case TIFFTAG_JPEGTABLESMODE:
+            sp->otherSettings.jpegtablesmode = (int)va_arg(ap, int);
+            return (1); /* pseudo tag */
+        case TIFFTAG_YCBCRSUBSAMPLING:
+            /* mark the fact that we have a real ycbcrsubsampling! */
+            sp->otherSettings.ycbcrsampling_fetched = 1;
+            /* should we be recomputing upsampling info here? */
+            return (*sp->otherSettings.vsetparent)(tif, tag, ap);
+        default:
+            return (*sp->otherSettings.vsetparent)(tif, tag, ap);
+    }
 
-	if ((fip = TIFFFieldWithTag(tif, tag)) != NULL) {
-		TIFFSetFieldBit(tif, fip->field_bit);
-	} else {
-		return (0);
-	}
+    if ((fip = TIFFFieldWithTag(tif, tag)) != NULL)
+    {
+        TIFFSetFieldBit(tif, fip->field_bit);
+    }
+    else
+    {
+        return (0);
+    }
 
-	tif->tif_flags |= TIFF_DIRTYDIRECT;
-	return (1);
+    tif->tif_flags |= TIFF_DIRTYDIRECT;
+    return (1);
 }
 
-static int
-JPEGVGetField(TIFF* tif, uint32 tag, va_list ap)
+static int JPEGVGetField(TIFF *tif, uint32_t tag, va_list ap)
 {
-	JPEGState* sp = JState(tif);
+    JPEGState *sp = JState(tif);
 
-	assert(sp != NULL);
+    assert(sp != NULL);
 
-	switch (tag) {
-		case TIFFTAG_JPEGTABLES:
-			*va_arg(ap, uint32*) = sp->jpegtables_length;
-			*va_arg(ap, void**) = sp->jpegtables;
-			break;
-		case TIFFTAG_JPEGQUALITY:
-			*va_arg(ap, int*) = sp->jpegquality;
-			break;
-		case TIFFTAG_JPEGCOLORMODE:
-			*va_arg(ap, int*) = sp->jpegcolormode;
-			break;
-		case TIFFTAG_JPEGTABLESMODE:
-			*va_arg(ap, int*) = sp->jpegtablesmode;
-			break;
-		default:
-			return (*sp->vgetparent)(tif, tag, ap);
-	}
-	return (1);
+    switch (tag)
+    {
+        case TIFFTAG_JPEGTABLES:
+            *va_arg(ap, uint32_t *) = sp->otherSettings.jpegtables_length;
+            *va_arg(ap, const void **) = sp->otherSettings.jpegtables;
+            break;
+        case TIFFTAG_JPEGQUALITY:
+            *va_arg(ap, int *) = sp->otherSettings.jpegquality;
+            break;
+        case TIFFTAG_JPEGCOLORMODE:
+            *va_arg(ap, int *) = sp->otherSettings.jpegcolormode;
+            break;
+        case TIFFTAG_JPEGTABLESMODE:
+            *va_arg(ap, int *) = sp->otherSettings.jpegtablesmode;
+            break;
+        default:
+            return (*sp->otherSettings.vgetparent)(tif, tag, ap);
+    }
+    return (1);
 }
 
-static void
-JPEGPrintDir(TIFF* tif, FILE* fd, long flags)
+static void JPEGPrintDir(TIFF *tif, FILE *fd, long flags)
 {
-	JPEGState* sp = JState(tif);
+    JPEGState *sp = JState(tif);
 
-	assert(sp != NULL);
-	(void) flags;
+    assert(sp != NULL);
+    (void)flags;
 
-        if( sp != NULL ) {
-		if (TIFFFieldSet(tif,FIELD_JPEGTABLES))
-			fprintf(fd, "  JPEG Tables: (%lu bytes)\n",
-				(unsigned long) sp->jpegtables_length);
-		if (sp->printdir)
-			(*sp->printdir)(tif, fd, flags);
-	}
+    if (sp != NULL)
+    {
+        if (TIFFFieldSet(tif, FIELD_JPEGTABLES))
+            fprintf(fd, "  JPEG Tables: (%" PRIu32 " bytes)\n",
+                    sp->otherSettings.jpegtables_length);
+        if (sp->otherSettings.printdir)
+            (*sp->otherSettings.printdir)(tif, fd, flags);
+    }
 }
 
-static uint32
-JPEGDefaultStripSize(TIFF* tif, uint32 s)
+static uint32_t JPEGDefaultStripSize(TIFF *tif, uint32_t s)
 {
-	JPEGState* sp = JState(tif);
-	TIFFDirectory *td = &tif->tif_dir;
+    JPEGState *sp = JState(tif);
+    TIFFDirectory *td = &tif->tif_dir;
 
-	s = (*sp->defsparent)(tif, s);
-	if (s < td->td_imagelength)
-		s = TIFFroundup_32(s, td->td_ycbcrsubsampling[1] * DCTSIZE);
-	return (s);
+    s = (*sp->otherSettings.defsparent)(tif, s);
+    if (s < td->td_imagelength)
+        s = TIFFroundup_32(s, td->td_ycbcrsubsampling[1] * DCTSIZE);
+    return (s);
 }
 
-static void
-JPEGDefaultTileSize(TIFF* tif, uint32* tw, uint32* th)
+static void JPEGDefaultTileSize(TIFF *tif, uint32_t *tw, uint32_t *th)
 {
-	JPEGState* sp = JState(tif);
-	TIFFDirectory *td = &tif->tif_dir;
+    JPEGState *sp = JState(tif);
+    TIFFDirectory *td = &tif->tif_dir;
 
-	(*sp->deftparent)(tif, tw, th);
-	*tw = TIFFroundup_32(*tw, td->td_ycbcrsubsampling[0] * DCTSIZE);
-	*th = TIFFroundup_32(*th, td->td_ycbcrsubsampling[1] * DCTSIZE);
+    (*sp->otherSettings.deftparent)(tif, tw, th);
+    *tw = TIFFroundup_32(*tw, td->td_ycbcrsubsampling[0] * DCTSIZE);
+    *th = TIFFroundup_32(*th, td->td_ycbcrsubsampling[1] * DCTSIZE);
 }
 
 /*
  * The JPEG library initialized used to be done in TIFFInitJPEG(), but
  * now that we allow a TIFF file to be opened in update mode it is necessary
  * to have some way of deciding whether compression or decompression is
- * desired other than looking at tif->tif_mode.  We accomplish this by 
+ * desired other than looking at tif->tif_mode.  We accomplish this by
  * examining {TILE/STRIP}BYTECOUNTS to see if there is a non-zero entry.
- * If so, we assume decompression is desired. 
+ * If so, we assume decompression is desired.
  *
  * This is tricky, because TIFFInitJPEG() is called while the directory is
  * being read, and generally speaking the BYTECOUNTS tag won't have been read
  * at that point.  So we try to defer jpeg library initialization till we
  * do have that tag ... basically any access that might require the compressor
- * or decompressor that occurs after the reading of the directory. 
+ * or decompressor that occurs after the reading of the directory.
  *
  * In an ideal world compressors or decompressors would be setup
  * at the point where a single tile or strip was accessed (for read or write)
@@ -2431,16 +2727,16 @@
  * NFW, Feb 3rd, 2003.
  */
 
-static int JPEGInitializeLibJPEG( TIFF * tif, int decompress )
+static int JPEGInitializeLibJPEG(TIFF *tif, int decompress)
 {
-    JPEGState* sp = JState(tif);
+    JPEGState *sp = JState(tif);
 
-    if(sp->cinfo_initialized)
+    if (sp->cinfo_initialized)
     {
-        if( !decompress && sp->cinfo.comm.is_decompressor )
-            TIFFjpeg_destroy( sp );
-        else if( decompress && !sp->cinfo.comm.is_decompressor )
-            TIFFjpeg_destroy( sp );
+        if (!decompress && sp->cinfo.comm.is_decompressor)
+            TIFFjpeg_destroy(sp);
+        else if (decompress && !sp->cinfo.comm.is_decompressor)
+            TIFFjpeg_destroy(sp);
         else
             return 1;
 
@@ -2450,29 +2746,36 @@
     /*
      * Initialize libjpeg.
      */
-    if ( decompress ) {
+    if (decompress)
+    {
         if (!TIFFjpeg_create_decompress(sp))
             return (0);
-    } else {
+    }
+    else
+    {
         if (!TIFFjpeg_create_compress(sp))
             return (0);
 #ifndef TIFF_JPEG_MAX_MEMORY_TO_USE
 #define TIFF_JPEG_MAX_MEMORY_TO_USE (10 * 1024 * 1024)
 #endif
         /* libjpeg turbo 1.5.2 honours max_memory_to_use, but has no backing */
-        /* store implementation, so better not set max_memory_to_use ourselves. */
+        /* store implementation, so better not set max_memory_to_use ourselves.
+         */
         /* See https://github.com/libjpeg-turbo/libjpeg-turbo/issues/162 */
-        if( sp->cinfo.c.mem->max_memory_to_use > 0 )
+        if (sp->cinfo.c.mem->max_memory_to_use > 0)
         {
             /* This is to address bug related in ticket GDAL #1795. */
             if (getenv("JPEGMEM") == NULL)
             {
-                /* Increase the max memory usable. This helps when creating files */
+                /* Increase the max memory usable. This helps when creating
+                 * files */
                 /* with "big" tile, without using libjpeg temporary files. */
                 /* For example a 512x512 tile with 3 bands */
                 /* requires 1.5 MB which is above libjpeg 1MB default */
-                if( sp->cinfo.c.mem->max_memory_to_use < TIFF_JPEG_MAX_MEMORY_TO_USE )
-                    sp->cinfo.c.mem->max_memory_to_use = TIFF_JPEG_MAX_MEMORY_TO_USE;
+                if (sp->cinfo.c.mem->max_memory_to_use <
+                    TIFF_JPEG_MAX_MEMORY_TO_USE)
+                    sp->cinfo.c.mem->max_memory_to_use =
+                        TIFF_JPEG_MAX_MEMORY_TO_USE;
             }
         }
     }
@@ -2482,123 +2785,124 @@
     return 1;
 }
 
-int
-TIFFInitJPEG(TIFF* tif, int scheme)
+/* Common to tif_jpeg.c and tif_jpeg_12.c */
+static void TIFFInitJPEGCommon(TIFF *tif)
 {
-	JPEGState* sp;
+    JPEGState *sp;
 
-	assert(scheme == COMPRESSION_JPEG);
+    sp = JState(tif);
+    sp->tif = tif; /* back link */
 
-	/*
-	 * Merge codec-specific tag information.
-	 */
-	if (!_TIFFMergeFields(tif, jpegFields, TIFFArrayCount(jpegFields))) {
-		TIFFErrorExt(tif->tif_clientdata,
-			     "TIFFInitJPEG",
-			     "Merging JPEG codec-specific tags failed");
-		return 0;
-	}
+    /* Default values for codec-specific fields */
+    sp->otherSettings.jpegtables = NULL;
+    sp->otherSettings.jpegtables_length = 0;
+    sp->otherSettings.jpegquality = 75; /* Default IJG quality */
+    sp->otherSettings.jpegcolormode = JPEGCOLORMODE_RAW;
+    sp->otherSettings.jpegtablesmode =
+        JPEGTABLESMODE_QUANT | JPEGTABLESMODE_HUFF;
+    sp->otherSettings.ycbcrsampling_fetched = 0;
 
-	/*
-	 * Allocate state block so tag methods have storage to record values.
-	 */
-	tif->tif_data = (uint8*) _TIFFmalloc(sizeof (JPEGState));
+    tif->tif_tagmethods.vgetfield = JPEGVGetField; /* hook for codec tags */
+    tif->tif_tagmethods.vsetfield = JPEGVSetField; /* hook for codec tags */
+    tif->tif_tagmethods.printdir = JPEGPrintDir;   /* hook for codec tags */
 
-	if (tif->tif_data == NULL) {
-		TIFFErrorExt(tif->tif_clientdata,
-			     "TIFFInitJPEG", "No space for JPEG state block");
-		return 0;
-	}
-        _TIFFmemset(tif->tif_data, 0, sizeof(JPEGState));
+    /*
+     * Install codec methods.
+     */
+    tif->tif_fixuptags = JPEGFixupTags;
+    tif->tif_setupdecode = JPEGSetupDecode;
+    tif->tif_predecode = JPEGPreDecode;
+    tif->tif_decoderow = JPEGDecode;
+    tif->tif_decodestrip = JPEGDecode;
+    tif->tif_decodetile = JPEGDecode;
+    tif->tif_setupencode = JPEGSetupEncode;
+    tif->tif_preencode = JPEGPreEncode;
+    tif->tif_postencode = JPEGPostEncode;
+    tif->tif_encoderow = JPEGEncode;
+    tif->tif_encodestrip = JPEGEncode;
+    tif->tif_encodetile = JPEGEncode;
+    tif->tif_cleanup = JPEGCleanup;
 
-	sp = JState(tif);
-	sp->tif = tif;				/* back link */
+    tif->tif_defstripsize = JPEGDefaultStripSize;
+    tif->tif_deftilesize = JPEGDefaultTileSize;
+    tif->tif_flags |= TIFF_NOBITREV; /* no bit reversal, please */
+    sp->cinfo_initialized = FALSE;
+}
 
-	/*
-	 * Override parent get/set field methods.
-	 */
-	sp->vgetparent = tif->tif_tagmethods.vgetfield;
-	tif->tif_tagmethods.vgetfield = JPEGVGetField; /* hook for codec tags */
-	sp->vsetparent = tif->tif_tagmethods.vsetfield;
-	tif->tif_tagmethods.vsetfield = JPEGVSetField; /* hook for codec tags */
-	sp->printdir = tif->tif_tagmethods.printdir;
-	tif->tif_tagmethods.printdir = JPEGPrintDir;   /* hook for codec tags */
+int TIFFInitJPEG(TIFF *tif, int scheme)
+{
+    JPEGState *sp;
 
-	/* Default values for codec-specific fields */
-	sp->jpegtables = NULL;
-	sp->jpegtables_length = 0;
-	sp->jpegquality = 75;			/* Default IJG quality */
-	sp->jpegcolormode = JPEGCOLORMODE_RAW;
-	sp->jpegtablesmode = JPEGTABLESMODE_QUANT | JPEGTABLESMODE_HUFF;
-        sp->ycbcrsampling_fetched = 0;
+    (void)scheme;
+    assert(scheme == COMPRESSION_JPEG);
 
-	/*
-	 * Install codec methods.
-	 */
-	tif->tif_fixuptags = JPEGFixupTags;
-	tif->tif_setupdecode = JPEGSetupDecode;
-	tif->tif_predecode = JPEGPreDecode;
-	tif->tif_decoderow = JPEGDecode;
-	tif->tif_decodestrip = JPEGDecode;
-	tif->tif_decodetile = JPEGDecode;
-	tif->tif_setupencode = JPEGSetupEncode;
-	tif->tif_preencode = JPEGPreEncode;
-	tif->tif_postencode = JPEGPostEncode;
-	tif->tif_encoderow = JPEGEncode;
-	tif->tif_encodestrip = JPEGEncode;
-	tif->tif_encodetile = JPEGEncode;  
-	tif->tif_cleanup = JPEGCleanup;
-	sp->defsparent = tif->tif_defstripsize;
-	tif->tif_defstripsize = JPEGDefaultStripSize;
-	sp->deftparent = tif->tif_deftilesize;
-	tif->tif_deftilesize = JPEGDefaultTileSize;
-	tif->tif_flags |= TIFF_NOBITREV;	/* no bit reversal, please */
+    /*
+     * Merge codec-specific tag information.
+     */
+    if (!_TIFFMergeFields(tif, jpegFields, TIFFArrayCount(jpegFields)))
+    {
+        TIFFErrorExtR(tif, "TIFFInitJPEG",
+                      "Merging JPEG codec-specific tags failed");
+        return 0;
+    }
 
-        sp->cinfo_initialized = FALSE;
+    /*
+     * Allocate state block so tag methods have storage to record values.
+     */
+    tif->tif_data = (uint8_t *)_TIFFmallocExt(tif, sizeof(JPEGState));
 
-	/*
-        ** Create a JPEGTables field if no directory has yet been created. 
-        ** We do this just to ensure that sufficient space is reserved for
-        ** the JPEGTables field.  It will be properly created the right
-        ** size later. 
-        */
-        if( tif->tif_diroff == 0 )
-        {
+    if (tif->tif_data == NULL)
+    {
+        TIFFErrorExtR(tif, "TIFFInitJPEG", "No space for JPEG state block");
+        return 0;
+    }
+    _TIFFmemset(tif->tif_data, 0, sizeof(JPEGState));
+
+    sp = JState(tif);
+    /*
+     * Override parent get/set field methods.
+     */
+    sp->otherSettings.vgetparent = tif->tif_tagmethods.vgetfield;
+    sp->otherSettings.vsetparent = tif->tif_tagmethods.vsetfield;
+    sp->otherSettings.printdir = tif->tif_tagmethods.printdir;
+
+    sp->otherSettings.defsparent = tif->tif_defstripsize;
+    sp->otherSettings.deftparent = tif->tif_deftilesize;
+
+    TIFFInitJPEGCommon(tif);
+
+    /*
+    ** Create a JPEGTables field if no directory has yet been created.
+    ** We do this just to ensure that sufficient space is reserved for
+    ** the JPEGTables field.  It will be properly created the right
+    ** size later.
+    */
+    if (tif->tif_diroff == 0)
+    {
 #define SIZE_OF_JPEGTABLES 2000
-/*
-The following line assumes incorrectly that all JPEG-in-TIFF files will have
-a JPEGTABLES tag generated and causes null-filled JPEGTABLES tags to be written
-when the JPEG data is placed with TIFFWriteRawStrip.  The field bit should be 
-set, anyway, later when actual JPEGTABLES header is generated, so removing it 
-here hopefully is harmless.
-            TIFFSetFieldBit(tif, FIELD_JPEGTABLES);
-*/
-            sp->jpegtables_length = SIZE_OF_JPEGTABLES;
-            sp->jpegtables = (void *) _TIFFmalloc(sp->jpegtables_length);
-            if (sp->jpegtables)
-            {
-                _TIFFmemset(sp->jpegtables, 0, SIZE_OF_JPEGTABLES);
-            }
-            else
-            {
-                TIFFErrorExt(tif->tif_clientdata,
-			     "TIFFInitJPEG",
-                             "Failed to allocate memory for JPEG tables");
-                return 0;
-            }
-#undef SIZE_OF_JPEGTABLES
+        /*
+        The following line assumes incorrectly that all JPEG-in-TIFF files will
+        have a JPEGTABLES tag generated and causes null-filled JPEGTABLES tags
+        to be written when the JPEG data is placed with TIFFWriteRawStrip.  The
+        field bit should be set, anyway, later when actual JPEGTABLES header is
+        generated, so removing it here hopefully is harmless.
+        TIFFSetFieldBit(tif, FIELD_JPEGTABLES);
+        */
+        sp->otherSettings.jpegtables_length = SIZE_OF_JPEGTABLES;
+        sp->otherSettings.jpegtables =
+            (void *)_TIFFmallocExt(tif, sp->otherSettings.jpegtables_length);
+        if (sp->otherSettings.jpegtables)
+        {
+            _TIFFmemset(sp->otherSettings.jpegtables, 0, SIZE_OF_JPEGTABLES);
         }
-
-	return 1;
+        else
+        {
+            TIFFErrorExtR(tif, "TIFFInitJPEG",
+                          "Failed to allocate memory for JPEG tables");
+            return 0;
+        }
+#undef SIZE_OF_JPEGTABLES
+    }
+    return 1;
 }
 #endif /* JPEG_SUPPORT */
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_luv.c b/third_party/libtiff/tif_luv.c
index 6fe4858..021756d 100644
--- a/third_party/libtiff/tif_luv.c
+++ b/third_party/libtiff/tif_luv.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1997 Greg Ward Larson
  * Copyright (c) 1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler, Greg Larson and Silicon Graphics may not be used in any
  * advertising or publicity relating to the software without the specific,
  * prior written permission of Sam Leffler, Greg Larson and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER, GREG LARSON OR SILICON GRAPHICS BE LIABLE
  * FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -34,7 +34,7 @@
  * LogLuv image support uses the TIFF library to store 16 or 10-bit
  * log luminance values with 8 bits each of u and v or a 14-bit index.
  *
- * The codec can take as input and produce as output 32-bit IEEE float values 
+ * The codec can take as input and produce as output 32-bit IEEE float values
  * as well as 16-bit integer values.  A 16-bit luminance is interpreted
  * as a sign bit followed by a 15-bit integer that is converted
  * to and from a linear magnitude using the transformation:
@@ -145,9 +145,9 @@
  * quantization errors into noise.
  */
 
+#include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <math.h>
 
 /*
  * State block for each open TIFF
@@ -155,22 +155,23 @@
  */
 typedef struct logLuvState LogLuvState;
 
-struct logLuvState {
-        int                     encoder_state;  /* 1 if encoder correctly initialized */
-	int                     user_datafmt;   /* user data format */
-	int                     encode_meth;    /* encoding method */
-	int                     pixel_size;     /* bytes per pixel */
+struct logLuvState
+{
+    int encoder_state; /* 1 if encoder correctly initialized */
+    int user_datafmt;  /* user data format */
+    int encode_meth;   /* encoding method */
+    int pixel_size;    /* bytes per pixel */
 
-	uint8*                  tbuf;           /* translation buffer */
-	tmsize_t                tbuflen;        /* buffer length */
-	void (*tfunc)(LogLuvState*, uint8*, tmsize_t);
+    uint8_t *tbuf;    /* translation buffer */
+    tmsize_t tbuflen; /* buffer length */
+    void (*tfunc)(LogLuvState *, uint8_t *, tmsize_t);
 
-	TIFFVSetMethod          vgetparent;     /* super-class method */
-	TIFFVSetMethod          vsetparent;     /* super-class method */
+    TIFFVSetMethod vgetparent; /* super-class method */
+    TIFFVSetMethod vsetparent; /* super-class method */
 };
 
-#define DecoderState(tif)	((LogLuvState*) (tif)->tif_data)
-#define EncoderState(tif)	((LogLuvState*) (tif)->tif_data)
+#define DecoderState(tif) ((LogLuvState *)(tif)->tif_data)
+#define EncoderState(tif) ((LogLuvState *)(tif)->tif_data)
 
 #define SGILOGDATAFMT_UNKNOWN -1
 
@@ -179,211 +180,207 @@
 /*
  * Decode a string of 16-bit gray pixels.
  */
-static int
-LogL16Decode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s)
+static int LogL16Decode(TIFF *tif, uint8_t *op, tmsize_t occ, uint16_t s)
 {
-	static const char module[] = "LogL16Decode";
-	LogLuvState* sp = DecoderState(tif);
-	int shft;
-	tmsize_t i;
-	tmsize_t npixels;
-	unsigned char* bp;
-	int16* tp;
-	int16 b;
-	tmsize_t cc;
-	int rc;
+    static const char module[] = "LogL16Decode";
+    LogLuvState *sp = DecoderState(tif);
+    int shft;
+    tmsize_t i;
+    tmsize_t npixels;
+    unsigned char *bp;
+    int16_t *tp;
+    int16_t b;
+    tmsize_t cc;
+    int rc;
 
-	assert(s == 0);
-	assert(sp != NULL);
+    (void)s;
+    assert(s == 0);
+    assert(sp != NULL);
 
-	npixels = occ / sp->pixel_size;
+    npixels = occ / sp->pixel_size;
 
-	if (sp->user_datafmt == SGILOGDATAFMT_16BIT)
-		tp = (int16*) op;
-	else {
-		if(sp->tbuflen < npixels) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-						 "Translation buffer too short");
-			return (0);
-		}
-		tp = (int16*) sp->tbuf;
-	}
-	_TIFFmemset((void*) tp, 0, npixels*sizeof (tp[0]));
+    if (sp->user_datafmt == SGILOGDATAFMT_16BIT)
+        tp = (int16_t *)op;
+    else
+    {
+        if (sp->tbuflen < npixels)
+        {
+            TIFFErrorExtR(tif, module, "Translation buffer too short");
+            return (0);
+        }
+        tp = (int16_t *)sp->tbuf;
+    }
+    _TIFFmemset((void *)tp, 0, npixels * sizeof(tp[0]));
 
-	bp = (unsigned char*) tif->tif_rawcp;
-	cc = tif->tif_rawcc;
-	/* get each byte string */
-	for (shft = 8; shft >= 0; shft -=8) {
-		for (i = 0; i < npixels && cc > 0; ) {
-			if (*bp >= 128) {		/* run */
-				if( cc < 2 )
-					break;
-				rc = *bp++ + (2-128);
-				b = (int16)(*bp++ << shft);
-				cc -= 2;
-				while (rc-- && i < npixels)
-					tp[i++] |= b;
-			} else {			/* non-run */
-				rc = *bp++;		/* nul is noop */
-				while (--cc && rc-- && i < npixels)
-					tp[i++] |= (int16)*bp++ << shft;
-			}
-		}
-		if (i != npixels) {
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Not enough data at row %lu (short %I64d pixels)",
-				     (unsigned long) tif->tif_row,
-				     (unsigned __int64) (npixels - i));
-#else
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Not enough data at row %lu (short %llu pixels)",
-				     (unsigned long) tif->tif_row,
-				     (unsigned long long) (npixels - i));
-#endif
-			tif->tif_rawcp = (uint8*) bp;
-			tif->tif_rawcc = cc;
-			return (0);
-		}
-	}
-	(*sp->tfunc)(sp, op, npixels);
-	tif->tif_rawcp = (uint8*) bp;
-	tif->tif_rawcc = cc;
-	return (1);
+    bp = (unsigned char *)tif->tif_rawcp;
+    cc = tif->tif_rawcc;
+    /* get each byte string */
+    for (shft = 8; shft >= 0; shft -= 8)
+    {
+        for (i = 0; i < npixels && cc > 0;)
+        {
+            if (*bp >= 128)
+            { /* run */
+                if (cc < 2)
+                    break;
+                rc = *bp++ + (2 - 128);
+                b = (int16_t)(*bp++ << shft);
+                cc -= 2;
+                while (rc-- && i < npixels)
+                    tp[i++] |= b;
+            }
+            else
+            {               /* non-run */
+                rc = *bp++; /* nul is noop */
+                while (--cc && rc-- && i < npixels)
+                    tp[i++] |= (int16_t)*bp++ << shft;
+            }
+        }
+        if (i != npixels)
+        {
+            TIFFErrorExtR(tif, module,
+                          "Not enough data at row %" PRIu32
+                          " (short %" TIFF_SSIZE_FORMAT " pixels)",
+                          tif->tif_row, npixels - i);
+            tif->tif_rawcp = (uint8_t *)bp;
+            tif->tif_rawcc = cc;
+            return (0);
+        }
+    }
+    (*sp->tfunc)(sp, op, npixels);
+    tif->tif_rawcp = (uint8_t *)bp;
+    tif->tif_rawcc = cc;
+    return (1);
 }
 
 /*
  * Decode a string of 24-bit pixels.
  */
-static int
-LogLuvDecode24(TIFF* tif, uint8* op, tmsize_t occ, uint16 s)
+static int LogLuvDecode24(TIFF *tif, uint8_t *op, tmsize_t occ, uint16_t s)
 {
-	static const char module[] = "LogLuvDecode24";
-	LogLuvState* sp = DecoderState(tif);
-	tmsize_t cc;
-	tmsize_t i;
-	tmsize_t npixels;
-	unsigned char* bp;
-	uint32* tp;
+    static const char module[] = "LogLuvDecode24";
+    LogLuvState *sp = DecoderState(tif);
+    tmsize_t cc;
+    tmsize_t i;
+    tmsize_t npixels;
+    unsigned char *bp;
+    uint32_t *tp;
 
-	assert(s == 0);
-	assert(sp != NULL);
+    (void)s;
+    assert(s == 0);
+    assert(sp != NULL);
 
-	npixels = occ / sp->pixel_size;
+    npixels = occ / sp->pixel_size;
 
-	if (sp->user_datafmt == SGILOGDATAFMT_RAW)
-		tp = (uint32 *)op;
-	else {
-		if(sp->tbuflen < npixels) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-						 "Translation buffer too short");
-			return (0);
-		}
-		tp = (uint32 *) sp->tbuf;
-	}
-	/* copy to array of uint32 */
-	bp = (unsigned char*) tif->tif_rawcp;
-	cc = tif->tif_rawcc;
-	for (i = 0; i < npixels && cc >= 3; i++) {
-		tp[i] = bp[0] << 16 | bp[1] << 8 | bp[2];
-		bp += 3;
-		cc -= 3;
-	}
-	tif->tif_rawcp = (uint8*) bp;
-	tif->tif_rawcc = cc;
-	if (i != npixels) {
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-		TIFFErrorExt(tif->tif_clientdata, module,
-			"Not enough data at row %lu (short %I64d pixels)",
-			     (unsigned long) tif->tif_row,
-			     (unsigned __int64) (npixels - i));
-#else
-		TIFFErrorExt(tif->tif_clientdata, module,
-			"Not enough data at row %lu (short %llu pixels)",
-			     (unsigned long) tif->tif_row,
-			     (unsigned long long) (npixels - i));
-#endif
-		return (0);
-	}
-	(*sp->tfunc)(sp, op, npixels);
-	return (1);
+    if (sp->user_datafmt == SGILOGDATAFMT_RAW)
+        tp = (uint32_t *)op;
+    else
+    {
+        if (sp->tbuflen < npixels)
+        {
+            TIFFErrorExtR(tif, module, "Translation buffer too short");
+            return (0);
+        }
+        tp = (uint32_t *)sp->tbuf;
+    }
+    /* copy to array of uint32_t */
+    bp = (unsigned char *)tif->tif_rawcp;
+    cc = tif->tif_rawcc;
+    for (i = 0; i < npixels && cc >= 3; i++)
+    {
+        tp[i] = bp[0] << 16 | bp[1] << 8 | bp[2];
+        bp += 3;
+        cc -= 3;
+    }
+    tif->tif_rawcp = (uint8_t *)bp;
+    tif->tif_rawcc = cc;
+    if (i != npixels)
+    {
+        TIFFErrorExtR(tif, module,
+                      "Not enough data at row %" PRIu32
+                      " (short %" TIFF_SSIZE_FORMAT " pixels)",
+                      tif->tif_row, npixels - i);
+        return (0);
+    }
+    (*sp->tfunc)(sp, op, npixels);
+    return (1);
 }
 
 /*
  * Decode a string of 32-bit pixels.
  */
-static int
-LogLuvDecode32(TIFF* tif, uint8* op, tmsize_t occ, uint16 s)
+static int LogLuvDecode32(TIFF *tif, uint8_t *op, tmsize_t occ, uint16_t s)
 {
-	static const char module[] = "LogLuvDecode32";
-	LogLuvState* sp;
-	int shft;
-	tmsize_t i;
-	tmsize_t npixels;
-	unsigned char* bp;
-	uint32* tp;
-	uint32 b;
-	tmsize_t cc;
-	int rc;
+    static const char module[] = "LogLuvDecode32";
+    LogLuvState *sp;
+    int shft;
+    tmsize_t i;
+    tmsize_t npixels;
+    unsigned char *bp;
+    uint32_t *tp;
+    uint32_t b;
+    tmsize_t cc;
+    int rc;
 
-	assert(s == 0);
-	sp = DecoderState(tif);
-	assert(sp != NULL);
+    (void)s;
+    assert(s == 0);
+    sp = DecoderState(tif);
+    assert(sp != NULL);
 
-	npixels = occ / sp->pixel_size;
+    npixels = occ / sp->pixel_size;
 
-	if (sp->user_datafmt == SGILOGDATAFMT_RAW)
-		tp = (uint32*) op;
-	else {
-		if(sp->tbuflen < npixels) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-						 "Translation buffer too short");
-			return (0);
-		}
-		tp = (uint32*) sp->tbuf;
-	}
-	_TIFFmemset((void*) tp, 0, npixels*sizeof (tp[0]));
+    if (sp->user_datafmt == SGILOGDATAFMT_RAW)
+        tp = (uint32_t *)op;
+    else
+    {
+        if (sp->tbuflen < npixels)
+        {
+            TIFFErrorExtR(tif, module, "Translation buffer too short");
+            return (0);
+        }
+        tp = (uint32_t *)sp->tbuf;
+    }
+    _TIFFmemset((void *)tp, 0, npixels * sizeof(tp[0]));
 
-	bp = (unsigned char*) tif->tif_rawcp;
-	cc = tif->tif_rawcc;
-	/* get each byte string */
-	for (shft = 24; shft >= 0; shft -=8) {
-		for (i = 0; i < npixels && cc > 0; ) {
-			if (*bp >= 128) {		/* run */
-				if( cc < 2 )
-					break;
-				rc = *bp++ + (2-128);
-				b = (uint32)*bp++ << shft;
-				cc -= 2;
-				while (rc-- && i < npixels)
-					tp[i++] |= b;
-			} else {			/* non-run */
-				rc = *bp++;		/* nul is noop */
-				while (--cc && rc-- && i < npixels)
-					tp[i++] |= (uint32)*bp++ << shft;
-			}
-		}
-		if (i != npixels) {
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-			TIFFErrorExt(tif->tif_clientdata, module,
-			"Not enough data at row %lu (short %I64d pixels)",
-				     (unsigned long) tif->tif_row,
-				     (unsigned __int64) (npixels - i));
-#else
-			TIFFErrorExt(tif->tif_clientdata, module,
-			"Not enough data at row %lu (short %llu pixels)",
-				     (unsigned long) tif->tif_row,
-				     (unsigned long long) (npixels - i));
-#endif
-			tif->tif_rawcp = (uint8*) bp;
-			tif->tif_rawcc = cc;
-			return (0);
-		}
-	}
-	(*sp->tfunc)(sp, op, npixels);
-	tif->tif_rawcp = (uint8*) bp;
-	tif->tif_rawcc = cc;
-	return (1);
+    bp = (unsigned char *)tif->tif_rawcp;
+    cc = tif->tif_rawcc;
+    /* get each byte string */
+    for (shft = 24; shft >= 0; shft -= 8)
+    {
+        for (i = 0; i < npixels && cc > 0;)
+        {
+            if (*bp >= 128)
+            { /* run */
+                if (cc < 2)
+                    break;
+                rc = *bp++ + (2 - 128);
+                b = (uint32_t)*bp++ << shft;
+                cc -= 2;
+                while (rc-- && i < npixels)
+                    tp[i++] |= b;
+            }
+            else
+            {               /* non-run */
+                rc = *bp++; /* nul is noop */
+                while (--cc && rc-- && i < npixels)
+                    tp[i++] |= (uint32_t)*bp++ << shft;
+            }
+        }
+        if (i != npixels)
+        {
+            TIFFErrorExtR(tif, module,
+                          "Not enough data at row %" PRIu32
+                          " (short %" TIFF_SSIZE_FORMAT " pixels)",
+                          tif->tif_row, npixels - i);
+            tif->tif_rawcp = (uint8_t *)bp;
+            tif->tif_rawcc = cc;
+            return (0);
+        }
+    }
+    (*sp->tfunc)(sp, op, npixels);
+    tif->tif_rawcp = (uint8_t *)bp;
+    tif->tif_rawcc = cc;
+    return (1);
 }
 
 /*
@@ -391,20 +388,20 @@
  * maintain synchrony with the encode algorithm, which
  * is row by row.
  */
-static int
-LogLuvDecodeStrip(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s)
+static int LogLuvDecodeStrip(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s)
 {
-	tmsize_t rowlen = TIFFScanlineSize(tif);
+    tmsize_t rowlen = TIFFScanlineSize(tif);
 
-        if (rowlen == 0)
-                return 0;
+    if (rowlen == 0)
+        return 0;
 
-	assert(cc%rowlen == 0);
-	while (cc && (*tif->tif_decoderow)(tif, bp, rowlen, s)) {
-		bp += rowlen;
-		cc -= rowlen;
-	}
-	return (cc == 0);
+    assert(cc % rowlen == 0);
+    while (cc && (*tif->tif_decoderow)(tif, bp, rowlen, s))
+    {
+        bp += rowlen;
+        cc -= rowlen;
+    }
+    return (cc == 0);
 }
 
 /*
@@ -412,311 +409,342 @@
  * maintain synchrony with the encode algorithm, which
  * is row by row.
  */
-static int
-LogLuvDecodeTile(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s)
+static int LogLuvDecodeTile(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s)
 {
-	tmsize_t rowlen = TIFFTileRowSize(tif);
+    tmsize_t rowlen = TIFFTileRowSize(tif);
 
-        if (rowlen == 0)
-                return 0;
+    if (rowlen == 0)
+        return 0;
 
-	assert(cc%rowlen == 0);
-	while (cc && (*tif->tif_decoderow)(tif, bp, rowlen, s)) {
-		bp += rowlen;
-		cc -= rowlen;
-	}
-	return (cc == 0);
+    assert(cc % rowlen == 0);
+    while (cc && (*tif->tif_decoderow)(tif, bp, rowlen, s))
+    {
+        bp += rowlen;
+        cc -= rowlen;
+    }
+    return (cc == 0);
 }
 
 /*
  * Encode a row of 16-bit pixels.
  */
-static int
-LogL16Encode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s)
+static int LogL16Encode(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s)
 {
-	static const char module[] = "LogL16Encode";
-	LogLuvState* sp = EncoderState(tif);
-	int shft;
-	tmsize_t i;
-	tmsize_t j;
-	tmsize_t npixels;
-	uint8* op;
-	int16* tp;
-	int16 b;
-	tmsize_t occ;
-	int rc=0, mask;
-	tmsize_t beg;
+    static const char module[] = "LogL16Encode";
+    LogLuvState *sp = EncoderState(tif);
+    int shft;
+    tmsize_t i;
+    tmsize_t j;
+    tmsize_t npixels;
+    uint8_t *op;
+    int16_t *tp;
+    int16_t b;
+    tmsize_t occ;
+    int rc = 0, mask;
+    tmsize_t beg;
 
-	assert(s == 0);
-	assert(sp != NULL);
-	npixels = cc / sp->pixel_size;
+    (void)s;
+    assert(s == 0);
+    assert(sp != NULL);
+    npixels = cc / sp->pixel_size;
 
-	if (sp->user_datafmt == SGILOGDATAFMT_16BIT)
-		tp = (int16*) bp;
-	else {
-		tp = (int16*) sp->tbuf;
-		if(sp->tbuflen < npixels) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-						 "Translation buffer too short");
-			return (0);
-		}
-		(*sp->tfunc)(sp, bp, npixels);
-	}
-	/* compress each byte string */
-	op = tif->tif_rawcp;
-	occ = tif->tif_rawdatasize - tif->tif_rawcc;
-	for (shft = 8; shft >= 0; shft -=8) {
-		for (i = 0; i < npixels; i += rc) {
-			if (occ < 4) {
-				tif->tif_rawcp = op;
-				tif->tif_rawcc = tif->tif_rawdatasize - occ;
-				if (!TIFFFlushData1(tif))
-					return (0);
-				op = tif->tif_rawcp;
-				occ = tif->tif_rawdatasize - tif->tif_rawcc;
-			}
-			mask = 0xff << shft;		/* find next run */
-			for (beg = i; beg < npixels; beg += rc) {
-				b = (int16) (tp[beg] & mask);
-				rc = 1;
-				while (rc < 127+2 && beg+rc < npixels &&
-				    (tp[beg+rc] & mask) == b)
-					rc++;
-				if (rc >= MINRUN)
-					break;		/* long enough */
-			}
-			if (beg-i > 1 && beg-i < MINRUN) {
-				b = (int16) (tp[i] & mask);/*check short run */
-				j = i+1;
-				while ((tp[j++] & mask) == b)
-					if (j == beg) {
-						*op++ = (uint8)(128-2+j-i);
-						*op++ = (uint8)(b >> shft);
-						occ -= 2;
-						i = beg;
-						break;
-					}
-			}
-			while (i < beg) {		/* write out non-run */
-				if ((j = beg-i) > 127) j = 127;
-				if (occ < j+3) {
-					tif->tif_rawcp = op;
-					tif->tif_rawcc = tif->tif_rawdatasize - occ;
-					if (!TIFFFlushData1(tif))
-						return (0);
-					op = tif->tif_rawcp;
-					occ = tif->tif_rawdatasize - tif->tif_rawcc;
-				}
-				*op++ = (uint8) j; occ--;
-				while (j--) {
-					*op++ = (uint8) (tp[i++] >> shft & 0xff);
-					occ--;
-				}
-			}
-			if (rc >= MINRUN) {		/* write out run */
-				*op++ = (uint8) (128-2+rc);
-				*op++ = (uint8) (tp[beg] >> shft & 0xff);
-				occ -= 2;
-			} else
-				rc = 0;
-		}
-	}
-	tif->tif_rawcp = op;
-	tif->tif_rawcc = tif->tif_rawdatasize - occ;
+    if (sp->user_datafmt == SGILOGDATAFMT_16BIT)
+        tp = (int16_t *)bp;
+    else
+    {
+        tp = (int16_t *)sp->tbuf;
+        if (sp->tbuflen < npixels)
+        {
+            TIFFErrorExtR(tif, module, "Translation buffer too short");
+            return (0);
+        }
+        (*sp->tfunc)(sp, bp, npixels);
+    }
+    /* compress each byte string */
+    op = tif->tif_rawcp;
+    occ = tif->tif_rawdatasize - tif->tif_rawcc;
+    for (shft = 8; shft >= 0; shft -= 8)
+    {
+        for (i = 0; i < npixels; i += rc)
+        {
+            if (occ < 4)
+            {
+                tif->tif_rawcp = op;
+                tif->tif_rawcc = tif->tif_rawdatasize - occ;
+                if (!TIFFFlushData1(tif))
+                    return (0);
+                op = tif->tif_rawcp;
+                occ = tif->tif_rawdatasize - tif->tif_rawcc;
+            }
+            mask = 0xff << shft; /* find next run */
+            for (beg = i; beg < npixels; beg += rc)
+            {
+                b = (int16_t)(tp[beg] & mask);
+                rc = 1;
+                while (rc < 127 + 2 && beg + rc < npixels &&
+                       (tp[beg + rc] & mask) == b)
+                    rc++;
+                if (rc >= MINRUN)
+                    break; /* long enough */
+            }
+            if (beg - i > 1 && beg - i < MINRUN)
+            {
+                b = (int16_t)(tp[i] & mask); /*check short run */
+                j = i + 1;
+                while ((tp[j++] & mask) == b)
+                    if (j == beg)
+                    {
+                        *op++ = (uint8_t)(128 - 2 + j - i);
+                        *op++ = (uint8_t)(b >> shft);
+                        occ -= 2;
+                        i = beg;
+                        break;
+                    }
+            }
+            while (i < beg)
+            { /* write out non-run */
+                if ((j = beg - i) > 127)
+                    j = 127;
+                if (occ < j + 3)
+                {
+                    tif->tif_rawcp = op;
+                    tif->tif_rawcc = tif->tif_rawdatasize - occ;
+                    if (!TIFFFlushData1(tif))
+                        return (0);
+                    op = tif->tif_rawcp;
+                    occ = tif->tif_rawdatasize - tif->tif_rawcc;
+                }
+                *op++ = (uint8_t)j;
+                occ--;
+                while (j--)
+                {
+                    *op++ = (uint8_t)(tp[i++] >> shft & 0xff);
+                    occ--;
+                }
+            }
+            if (rc >= MINRUN)
+            { /* write out run */
+                *op++ = (uint8_t)(128 - 2 + rc);
+                *op++ = (uint8_t)(tp[beg] >> shft & 0xff);
+                occ -= 2;
+            }
+            else
+                rc = 0;
+        }
+    }
+    tif->tif_rawcp = op;
+    tif->tif_rawcc = tif->tif_rawdatasize - occ;
 
-	return (1);
+    return (1);
 }
 
 /*
  * Encode a row of 24-bit pixels.
  */
-static int
-LogLuvEncode24(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s)
+static int LogLuvEncode24(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s)
 {
-	static const char module[] = "LogLuvEncode24";
-	LogLuvState* sp = EncoderState(tif);
-	tmsize_t i;
-	tmsize_t npixels;
-	tmsize_t occ;
-	uint8* op;
-	uint32* tp;
+    static const char module[] = "LogLuvEncode24";
+    LogLuvState *sp = EncoderState(tif);
+    tmsize_t i;
+    tmsize_t npixels;
+    tmsize_t occ;
+    uint8_t *op;
+    uint32_t *tp;
 
-	assert(s == 0);
-	assert(sp != NULL);
-	npixels = cc / sp->pixel_size;
+    (void)s;
+    assert(s == 0);
+    assert(sp != NULL);
+    npixels = cc / sp->pixel_size;
 
-	if (sp->user_datafmt == SGILOGDATAFMT_RAW)
-		tp = (uint32*) bp;
-	else {
-		tp = (uint32*) sp->tbuf;
-		if(sp->tbuflen < npixels) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-						 "Translation buffer too short");
-			return (0);
-		}
-		(*sp->tfunc)(sp, bp, npixels);
-	}
-	/* write out encoded pixels */
-	op = tif->tif_rawcp;
-	occ = tif->tif_rawdatasize - tif->tif_rawcc;
-	for (i = npixels; i--; ) {
-		if (occ < 3) {
-			tif->tif_rawcp = op;
-			tif->tif_rawcc = tif->tif_rawdatasize - occ;
-			if (!TIFFFlushData1(tif))
-				return (0);
-			op = tif->tif_rawcp;
-			occ = tif->tif_rawdatasize - tif->tif_rawcc;
-		}
-		*op++ = (uint8)(*tp >> 16);
-		*op++ = (uint8)(*tp >> 8 & 0xff);
-		*op++ = (uint8)(*tp++ & 0xff);
-		occ -= 3;
-	}
-	tif->tif_rawcp = op;
-	tif->tif_rawcc = tif->tif_rawdatasize - occ;
+    if (sp->user_datafmt == SGILOGDATAFMT_RAW)
+        tp = (uint32_t *)bp;
+    else
+    {
+        tp = (uint32_t *)sp->tbuf;
+        if (sp->tbuflen < npixels)
+        {
+            TIFFErrorExtR(tif, module, "Translation buffer too short");
+            return (0);
+        }
+        (*sp->tfunc)(sp, bp, npixels);
+    }
+    /* write out encoded pixels */
+    op = tif->tif_rawcp;
+    occ = tif->tif_rawdatasize - tif->tif_rawcc;
+    for (i = npixels; i--;)
+    {
+        if (occ < 3)
+        {
+            tif->tif_rawcp = op;
+            tif->tif_rawcc = tif->tif_rawdatasize - occ;
+            if (!TIFFFlushData1(tif))
+                return (0);
+            op = tif->tif_rawcp;
+            occ = tif->tif_rawdatasize - tif->tif_rawcc;
+        }
+        *op++ = (uint8_t)(*tp >> 16);
+        *op++ = (uint8_t)(*tp >> 8 & 0xff);
+        *op++ = (uint8_t)(*tp++ & 0xff);
+        occ -= 3;
+    }
+    tif->tif_rawcp = op;
+    tif->tif_rawcc = tif->tif_rawdatasize - occ;
 
-	return (1);
+    return (1);
 }
 
 /*
  * Encode a row of 32-bit pixels.
  */
-static int
-LogLuvEncode32(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s)
+static int LogLuvEncode32(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s)
 {
-	static const char module[] = "LogLuvEncode32";
-	LogLuvState* sp = EncoderState(tif);
-	int shft;
-	tmsize_t i;
-	tmsize_t j;
-	tmsize_t npixels;
-	uint8* op;
-	uint32* tp;
-	uint32 b;
-	tmsize_t occ;
-	int rc=0, mask;
-	tmsize_t beg;
+    static const char module[] = "LogLuvEncode32";
+    LogLuvState *sp = EncoderState(tif);
+    int shft;
+    tmsize_t i;
+    tmsize_t j;
+    tmsize_t npixels;
+    uint8_t *op;
+    uint32_t *tp;
+    uint32_t b;
+    tmsize_t occ;
+    int rc = 0;
+    tmsize_t beg;
 
-	assert(s == 0);
-	assert(sp != NULL);
+    (void)s;
+    assert(s == 0);
+    assert(sp != NULL);
 
-	npixels = cc / sp->pixel_size;
+    npixels = cc / sp->pixel_size;
 
-	if (sp->user_datafmt == SGILOGDATAFMT_RAW)
-		tp = (uint32*) bp;
-	else {
-		tp = (uint32*) sp->tbuf;
-		if(sp->tbuflen < npixels) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-						 "Translation buffer too short");
-			return (0);
-		}
-		(*sp->tfunc)(sp, bp, npixels);
-	}
-	/* compress each byte string */
-	op = tif->tif_rawcp;
-	occ = tif->tif_rawdatasize - tif->tif_rawcc;
-	for (shft = 24; shft >= 0; shft -=8) {
-		for (i = 0; i < npixels; i += rc) {
-			if (occ < 4) {
-				tif->tif_rawcp = op;
-				tif->tif_rawcc = tif->tif_rawdatasize - occ;
-				if (!TIFFFlushData1(tif))
-					return (0);
-				op = tif->tif_rawcp;
-				occ = tif->tif_rawdatasize - tif->tif_rawcc;
-			}
-			mask = 0xff << shft;		/* find next run */
-			for (beg = i; beg < npixels; beg += rc) {
-				b = tp[beg] & mask;
-				rc = 1;
-				while (rc < 127+2 && beg+rc < npixels &&
-						(tp[beg+rc] & mask) == b)
-					rc++;
-				if (rc >= MINRUN)
-					break;		/* long enough */
-			}
-			if (beg-i > 1 && beg-i < MINRUN) {
-				b = tp[i] & mask;	/* check short run */
-				j = i+1;
-				while ((tp[j++] & mask) == b)
-					if (j == beg) {
-						*op++ = (uint8)(128-2+j-i);
-						*op++ = (uint8)(b >> shft);
-						occ -= 2;
-						i = beg;
-						break;
-					}
-			}
-			while (i < beg) {		/* write out non-run */
-				if ((j = beg-i) > 127) j = 127;
-				if (occ < j+3) {
-					tif->tif_rawcp = op;
-					tif->tif_rawcc = tif->tif_rawdatasize - occ;
-					if (!TIFFFlushData1(tif))
-						return (0);
-					op = tif->tif_rawcp;
-					occ = tif->tif_rawdatasize - tif->tif_rawcc;
-				}
-				*op++ = (uint8) j; occ--;
-				while (j--) {
-					*op++ = (uint8)(tp[i++] >> shft & 0xff);
-					occ--;
-				}
-			}
-			if (rc >= MINRUN) {		/* write out run */
-				*op++ = (uint8) (128-2+rc);
-				*op++ = (uint8)(tp[beg] >> shft & 0xff);
-				occ -= 2;
-			} else
-				rc = 0;
-		}
-	}
-	tif->tif_rawcp = op;
-	tif->tif_rawcc = tif->tif_rawdatasize - occ;
+    if (sp->user_datafmt == SGILOGDATAFMT_RAW)
+        tp = (uint32_t *)bp;
+    else
+    {
+        tp = (uint32_t *)sp->tbuf;
+        if (sp->tbuflen < npixels)
+        {
+            TIFFErrorExtR(tif, module, "Translation buffer too short");
+            return (0);
+        }
+        (*sp->tfunc)(sp, bp, npixels);
+    }
+    /* compress each byte string */
+    op = tif->tif_rawcp;
+    occ = tif->tif_rawdatasize - tif->tif_rawcc;
+    for (shft = 24; shft >= 0; shft -= 8)
+    {
+        const uint32_t mask = 0xffU << shft; /* find next run */
+        for (i = 0; i < npixels; i += rc)
+        {
+            if (occ < 4)
+            {
+                tif->tif_rawcp = op;
+                tif->tif_rawcc = tif->tif_rawdatasize - occ;
+                if (!TIFFFlushData1(tif))
+                    return (0);
+                op = tif->tif_rawcp;
+                occ = tif->tif_rawdatasize - tif->tif_rawcc;
+            }
+            for (beg = i; beg < npixels; beg += rc)
+            {
+                b = tp[beg] & mask;
+                rc = 1;
+                while (rc < 127 + 2 && beg + rc < npixels &&
+                       (tp[beg + rc] & mask) == b)
+                    rc++;
+                if (rc >= MINRUN)
+                    break; /* long enough */
+            }
+            if (beg - i > 1 && beg - i < MINRUN)
+            {
+                b = tp[i] & mask; /* check short run */
+                j = i + 1;
+                while ((tp[j++] & mask) == b)
+                    if (j == beg)
+                    {
+                        *op++ = (uint8_t)(128 - 2 + j - i);
+                        *op++ = (uint8_t)(b >> shft);
+                        occ -= 2;
+                        i = beg;
+                        break;
+                    }
+            }
+            while (i < beg)
+            { /* write out non-run */
+                if ((j = beg - i) > 127)
+                    j = 127;
+                if (occ < j + 3)
+                {
+                    tif->tif_rawcp = op;
+                    tif->tif_rawcc = tif->tif_rawdatasize - occ;
+                    if (!TIFFFlushData1(tif))
+                        return (0);
+                    op = tif->tif_rawcp;
+                    occ = tif->tif_rawdatasize - tif->tif_rawcc;
+                }
+                *op++ = (uint8_t)j;
+                occ--;
+                while (j--)
+                {
+                    *op++ = (uint8_t)(tp[i++] >> shft & 0xff);
+                    occ--;
+                }
+            }
+            if (rc >= MINRUN)
+            { /* write out run */
+                *op++ = (uint8_t)(128 - 2 + rc);
+                *op++ = (uint8_t)(tp[beg] >> shft & 0xff);
+                occ -= 2;
+            }
+            else
+                rc = 0;
+        }
+    }
+    tif->tif_rawcp = op;
+    tif->tif_rawcc = tif->tif_rawdatasize - occ;
 
-	return (1);
+    return (1);
 }
 
 /*
  * Encode a strip of pixels.  We break it into rows to
  * avoid encoding runs across row boundaries.
  */
-static int
-LogLuvEncodeStrip(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s)
+static int LogLuvEncodeStrip(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s)
 {
-	tmsize_t rowlen = TIFFScanlineSize(tif);
+    tmsize_t rowlen = TIFFScanlineSize(tif);
 
-        if (rowlen == 0)
-                return 0;
+    if (rowlen == 0)
+        return 0;
 
-	assert(cc%rowlen == 0);
-	while (cc && (*tif->tif_encoderow)(tif, bp, rowlen, s) == 1) {
-		bp += rowlen;
-		cc -= rowlen;
-	}
-	return (cc == 0);
+    assert(cc % rowlen == 0);
+    while (cc && (*tif->tif_encoderow)(tif, bp, rowlen, s) == 1)
+    {
+        bp += rowlen;
+        cc -= rowlen;
+    }
+    return (cc == 0);
 }
 
 /*
  * Encode a tile of pixels.  We break it into rows to
  * avoid encoding runs across row boundaries.
  */
-static int
-LogLuvEncodeTile(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s)
+static int LogLuvEncodeTile(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s)
 {
-	tmsize_t rowlen = TIFFTileRowSize(tif);
+    tmsize_t rowlen = TIFFTileRowSize(tif);
 
-        if (rowlen == 0)
-                return 0;
+    if (rowlen == 0)
+        return 0;
 
-	assert(cc%rowlen == 0);
-	while (cc && (*tif->tif_encoderow)(tif, bp, rowlen, s) == 1) {
-		bp += rowlen;
-		cc -= rowlen;
-	}
-	return (cc == 0);
+    assert(cc % rowlen == 0);
+    while (cc && (*tif->tif_encoderow)(tif, bp, rowlen, s) == 1)
+    {
+        bp += rowlen;
+        cc -= rowlen;
+    }
+    return (cc == 0);
 }
 
 /*
@@ -726,190 +754,192 @@
 #include "uvcode.h"
 
 #ifndef UVSCALE
-#define U_NEU		0.210526316
-#define V_NEU		0.473684211
-#define UVSCALE		410.
+#define U_NEU 0.210526316
+#define V_NEU 0.473684211
+#define UVSCALE 410.
 #endif
 
-#ifndef	M_LN2
-#define M_LN2		0.69314718055994530942
+#ifndef M_LN2
+#define M_LN2 0.69314718055994530942
 #endif
 #ifndef M_PI
-#define M_PI		3.14159265358979323846
+#define M_PI 3.14159265358979323846
 #endif
 #undef log2 /* Conflict with C'99 function */
-#define log2(x)		((1./M_LN2)*log(x))
-#undef exp2  /* Conflict with C'99 function */
-#define exp2(x)		exp(M_LN2*(x))
+#define log2(x) ((1. / M_LN2) * log(x))
+#undef exp2 /* Conflict with C'99 function */
+#define exp2(x) exp(M_LN2 *(x))
 
-static int itrunc(double x, int m)
+static int tiff_itrunc(double x, int m)
 {
-    if( m == SGILOGENCODE_NODITHER )
+    if (m == SGILOGENCODE_NODITHER)
         return (int)x;
     /* Silence CoverityScan warning about bad crypto function */
     /* coverity[dont_call] */
-    return (int)(x + rand()*(1./RAND_MAX) - .5);
+    return (int)(x + rand() * (1. / RAND_MAX) - .5);
 }
 
 #if !LOGLUV_PUBLIC
 static
 #endif
-double
-LogL16toY(int p16)		/* compute luminance from 16-bit LogL */
+    double
+    LogL16toY(int p16) /* compute luminance from 16-bit LogL */
 {
-	int	Le = p16 & 0x7fff;
-	double	Y;
+    int Le = p16 & 0x7fff;
+    double Y;
 
-	if (!Le)
-		return (0.);
-	Y = exp(M_LN2/256.*(Le+.5) - M_LN2*64.);
-	return (!(p16 & 0x8000) ? Y : -Y);
+    if (!Le)
+        return (0.);
+    Y = exp(M_LN2 / 256. * (Le + .5) - M_LN2 * 64.);
+    return (!(p16 & 0x8000) ? Y : -Y);
 }
 
 #if !LOGLUV_PUBLIC
 static
 #endif
-int
-LogL16fromY(double Y, int em)	/* get 16-bit LogL from Y */
+    int
+    LogL16fromY(double Y, int em) /* get 16-bit LogL from Y */
 {
-	if (Y >= 1.8371976e19)
-		return (0x7fff);
-	if (Y <= -1.8371976e19)
-		return (0xffff);
-	if (Y > 5.4136769e-20)
-		return itrunc(256.*(log2(Y) + 64.), em);
-	if (Y < -5.4136769e-20)
-		return (~0x7fff | itrunc(256.*(log2(-Y) + 64.), em));
-	return (0);
+    if (Y >= 1.8371976e19)
+        return (0x7fff);
+    if (Y <= -1.8371976e19)
+        return (0xffff);
+    if (Y > 5.4136769e-20)
+        return tiff_itrunc(256. * (log2(Y) + 64.), em);
+    if (Y < -5.4136769e-20)
+        return (~0x7fff | tiff_itrunc(256. * (log2(-Y) + 64.), em));
+    return (0);
 }
 
-static void
-L16toY(LogLuvState* sp, uint8* op, tmsize_t n)
+static void L16toY(LogLuvState *sp, uint8_t *op, tmsize_t n)
 {
-	int16* l16 = (int16*) sp->tbuf;
-	float* yp = (float*) op;
+    int16_t *l16 = (int16_t *)sp->tbuf;
+    float *yp = (float *)op;
 
-	while (n-- > 0)
-		*yp++ = (float)LogL16toY(*l16++);
+    while (n-- > 0)
+        *yp++ = (float)LogL16toY(*l16++);
 }
 
-static void
-L16toGry(LogLuvState* sp, uint8* op, tmsize_t n)
+static void L16toGry(LogLuvState *sp, uint8_t *op, tmsize_t n)
 {
-	int16* l16 = (int16*) sp->tbuf;
-	uint8* gp = (uint8*) op;
+    int16_t *l16 = (int16_t *)sp->tbuf;
+    uint8_t *gp = (uint8_t *)op;
 
-	while (n-- > 0) {
-		double Y = LogL16toY(*l16++);
-		*gp++ = (uint8) ((Y <= 0.) ? 0 : (Y >= 1.) ? 255 : (int)(256.*sqrt(Y)));
-	}
+    while (n-- > 0)
+    {
+        double Y = LogL16toY(*l16++);
+        *gp++ = (uint8_t)((Y <= 0.)   ? 0
+                          : (Y >= 1.) ? 255
+                                      : (int)(256. * sqrt(Y)));
+    }
 }
 
-static void
-L16fromY(LogLuvState* sp, uint8* op, tmsize_t n)
+static void L16fromY(LogLuvState *sp, uint8_t *op, tmsize_t n)
 {
-	int16* l16 = (int16*) sp->tbuf;
-	float* yp = (float*) op;
+    int16_t *l16 = (int16_t *)sp->tbuf;
+    float *yp = (float *)op;
 
-	while (n-- > 0)
-		*l16++ = (int16) (LogL16fromY(*yp++, sp->encode_meth));
+    while (n-- > 0)
+        *l16++ = (int16_t)(LogL16fromY(*yp++, sp->encode_meth));
 }
 
 #if !LOGLUV_PUBLIC
 static
 #endif
-void
-XYZtoRGB24(float xyz[3], uint8 rgb[3])
+    void
+    XYZtoRGB24(float *xyz, uint8_t *rgb)
 {
-	double	r, g, b;
-					/* assume CCIR-709 primaries */
-	r =  2.690*xyz[0] + -1.276*xyz[1] + -0.414*xyz[2];
-	g = -1.022*xyz[0] +  1.978*xyz[1] +  0.044*xyz[2];
-	b =  0.061*xyz[0] + -0.224*xyz[1] +  1.163*xyz[2];
-					/* assume 2.0 gamma for speed */
-	/* could use integer sqrt approx., but this is probably faster */
-	rgb[0] = (uint8)((r<=0.) ? 0 : (r >= 1.) ? 255 : (int)(256.*sqrt(r)));
-	rgb[1] = (uint8)((g<=0.) ? 0 : (g >= 1.) ? 255 : (int)(256.*sqrt(g)));
-	rgb[2] = (uint8)((b<=0.) ? 0 : (b >= 1.) ? 255 : (int)(256.*sqrt(b)));
+    double r, g, b;
+    /* assume CCIR-709 primaries */
+    r = 2.690 * xyz[0] + -1.276 * xyz[1] + -0.414 * xyz[2];
+    g = -1.022 * xyz[0] + 1.978 * xyz[1] + 0.044 * xyz[2];
+    b = 0.061 * xyz[0] + -0.224 * xyz[1] + 1.163 * xyz[2];
+    /* assume 2.0 gamma for speed */
+    /* could use integer sqrt approx., but this is probably faster */
+    rgb[0] = (uint8_t)((r <= 0.) ? 0 : (r >= 1.) ? 255 : (int)(256. * sqrt(r)));
+    rgb[1] = (uint8_t)((g <= 0.) ? 0 : (g >= 1.) ? 255 : (int)(256. * sqrt(g)));
+    rgb[2] = (uint8_t)((b <= 0.) ? 0 : (b >= 1.) ? 255 : (int)(256. * sqrt(b)));
 }
 
 #if !LOGLUV_PUBLIC
 static
 #endif
-double
-LogL10toY(int p10)		/* compute luminance from 10-bit LogL */
+    double
+    LogL10toY(int p10) /* compute luminance from 10-bit LogL */
 {
-	if (p10 == 0)
-		return (0.);
-	return (exp(M_LN2/64.*(p10+.5) - M_LN2*12.));
+    if (p10 == 0)
+        return (0.);
+    return (exp(M_LN2 / 64. * (p10 + .5) - M_LN2 * 12.));
 }
 
 #if !LOGLUV_PUBLIC
 static
 #endif
-int
-LogL10fromY(double Y, int em)	/* get 10-bit LogL from Y */
+    int
+    LogL10fromY(double Y, int em) /* get 10-bit LogL from Y */
 {
-	if (Y >= 15.742)
-		return (0x3ff);
-	else if (Y <= .00024283)
-		return (0);
-	else
-		return itrunc(64.*(log2(Y) + 12.), em);
+    if (Y >= 15.742)
+        return (0x3ff);
+    else if (Y <= .00024283)
+        return (0);
+    else
+        return tiff_itrunc(64. * (log2(Y) + 12.), em);
 }
 
-#define NANGLES		100
-#define uv2ang(u, v)	( (NANGLES*.499999999/M_PI) \
-				* atan2((v)-V_NEU,(u)-U_NEU) + .5*NANGLES )
+#define NANGLES 100
+#define uv2ang(u, v)                                                           \
+    ((NANGLES * .499999999 / M_PI) * atan2((v)-V_NEU, (u)-U_NEU) + .5 * NANGLES)
 
-static int
-oog_encode(double u, double v)		/* encode out-of-gamut chroma */
+static int oog_encode(double u, double v) /* encode out-of-gamut chroma */
 {
-	static int	oog_table[NANGLES];
-	static int	initialized = 0;
-	register int	i;
+    static int oog_table[NANGLES];
+    static int initialized = 0;
+    register int i;
 
-	if (!initialized) {		/* set up perimeter table */
-		double	eps[NANGLES], ua, va, ang, epsa;
-		int	ui, vi, ustep;
-		for (i = NANGLES; i--; )
-			eps[i] = 2.;
-		for (vi = UV_NVS; vi--; ) {
-			va = UV_VSTART + (vi+.5)*UV_SQSIZ;
-			ustep = uv_row[vi].nus-1;
-			if (vi == UV_NVS-1 || vi == 0 || ustep <= 0)
-				ustep = 1;
-			for (ui = uv_row[vi].nus-1; ui >= 0; ui -= ustep) {
-				ua = uv_row[vi].ustart + (ui+.5)*UV_SQSIZ;
-				ang = uv2ang(ua, va);
-				i = (int) ang;
-				epsa = fabs(ang - (i+.5));
-				if (epsa < eps[i]) {
-					oog_table[i] = uv_row[vi].ncum + ui;
-					eps[i] = epsa;
-				}
-			}
-		}
-		for (i = NANGLES; i--; )	/* fill any holes */
-			if (eps[i] > 1.5) {
-				int	i1, i2;
-				for (i1 = 1; i1 < NANGLES/2; i1++)
-					if (eps[(i+i1)%NANGLES] < 1.5)
-						break;
-				for (i2 = 1; i2 < NANGLES/2; i2++)
-					if (eps[(i+NANGLES-i2)%NANGLES] < 1.5)
-						break;
-				if (i1 < i2)
-					oog_table[i] =
-						oog_table[(i+i1)%NANGLES];
-				else
-					oog_table[i] =
-						oog_table[(i+NANGLES-i2)%NANGLES];
-			}
-		initialized = 1;
-	}
-	i = (int) uv2ang(u, v);		/* look up hue angle */
-	return (oog_table[i]);
+    if (!initialized)
+    { /* set up perimeter table */
+        double eps[NANGLES], ua, va, ang, epsa;
+        int ui, vi, ustep;
+        for (i = NANGLES; i--;)
+            eps[i] = 2.;
+        for (vi = UV_NVS; vi--;)
+        {
+            va = UV_VSTART + (vi + .5) * UV_SQSIZ;
+            ustep = uv_row[vi].nus - 1;
+            if (vi == UV_NVS - 1 || vi == 0 || ustep <= 0)
+                ustep = 1;
+            for (ui = uv_row[vi].nus - 1; ui >= 0; ui -= ustep)
+            {
+                ua = uv_row[vi].ustart + (ui + .5) * UV_SQSIZ;
+                ang = uv2ang(ua, va);
+                i = (int)ang;
+                epsa = fabs(ang - (i + .5));
+                if (epsa < eps[i])
+                {
+                    oog_table[i] = uv_row[vi].ncum + ui;
+                    eps[i] = epsa;
+                }
+            }
+        }
+        for (i = NANGLES; i--;) /* fill any holes */
+            if (eps[i] > 1.5)
+            {
+                int i1, i2;
+                for (i1 = 1; i1 < NANGLES / 2; i1++)
+                    if (eps[(i + i1) % NANGLES] < 1.5)
+                        break;
+                for (i2 = 1; i2 < NANGLES / 2; i2++)
+                    if (eps[(i + NANGLES - i2) % NANGLES] < 1.5)
+                        break;
+                if (i1 < i2)
+                    oog_table[i] = oog_table[(i + i1) % NANGLES];
+                else
+                    oog_table[i] = oog_table[(i + NANGLES - i2) % NANGLES];
+            }
+        initialized = 1;
+    }
+    i = (int)uv2ang(u, v); /* look up hue angle */
+    return (oog_table[i]);
 }
 
 #undef uv2ang
@@ -918,847 +948,891 @@
 #if !LOGLUV_PUBLIC
 static
 #endif
-int
-uv_encode(double u, double v, int em)	/* encode (u',v') coordinates */
+    int
+    uv_encode(double u, double v, int em) /* encode (u',v') coordinates */
 {
-	register int	vi, ui;
+    register int vi, ui;
 
-	if (v < UV_VSTART)
-		return oog_encode(u, v);
-	vi = itrunc((v - UV_VSTART)*(1./UV_SQSIZ), em);
-	if (vi >= UV_NVS)
-		return oog_encode(u, v);
-	if (u < uv_row[vi].ustart)
-		return oog_encode(u, v);
-	ui = itrunc((u - uv_row[vi].ustart)*(1./UV_SQSIZ), em);
-	if (ui >= uv_row[vi].nus)
-		return oog_encode(u, v);
+    /* check for NaN */
+    if (u != u || v != v)
+    {
+        u = U_NEU;
+        v = V_NEU;
+    }
 
-	return (uv_row[vi].ncum + ui);
+    if (v < UV_VSTART)
+        return oog_encode(u, v);
+    vi = tiff_itrunc((v - UV_VSTART) * (1. / UV_SQSIZ), em);
+    if (vi >= UV_NVS)
+        return oog_encode(u, v);
+    if (u < uv_row[vi].ustart)
+        return oog_encode(u, v);
+    ui = tiff_itrunc((u - uv_row[vi].ustart) * (1. / UV_SQSIZ), em);
+    if (ui >= uv_row[vi].nus)
+        return oog_encode(u, v);
+
+    return (uv_row[vi].ncum + ui);
 }
 
 #if !LOGLUV_PUBLIC
 static
 #endif
-int
-uv_decode(double *up, double *vp, int c)	/* decode (u',v') index */
+    int
+    uv_decode(double *up, double *vp, int c) /* decode (u',v') index */
 {
-	int	upper, lower;
-	register int	ui, vi;
+    int upper, lower;
+    register int ui, vi;
 
-	if (c < 0 || c >= UV_NDIVS)
-		return (-1);
-	lower = 0;				/* binary search */
-	upper = UV_NVS;
-	while (upper - lower > 1) {
-		vi = (lower + upper) >> 1;
-		ui = c - uv_row[vi].ncum;
-		if (ui > 0)
-			lower = vi;
-		else if (ui < 0)
-			upper = vi;
-		else {
-			lower = vi;
-			break;
-		}
-	}
-	vi = lower;
-	ui = c - uv_row[vi].ncum;
-	*up = uv_row[vi].ustart + (ui+.5)*UV_SQSIZ;
-	*vp = UV_VSTART + (vi+.5)*UV_SQSIZ;
-	return (0);
-}
-
-#if !LOGLUV_PUBLIC
-static
-#endif
-void
-LogLuv24toXYZ(uint32 p, float XYZ[3])
-{
-	int	Ce;
-	double	L, u, v, s, x, y;
-					/* decode luminance */
-	L = LogL10toY(p>>14 & 0x3ff);
-	if (L <= 0.) {
-		XYZ[0] = XYZ[1] = XYZ[2] = 0.;
-		return;
-	}
-					/* decode color */
-	Ce = p & 0x3fff;
-	if (uv_decode(&u, &v, Ce) < 0) {
-		u = U_NEU; v = V_NEU;
-	}
-	s = 1./(6.*u - 16.*v + 12.);
-	x = 9.*u * s;
-	y = 4.*v * s;
-					/* convert to XYZ */
-	XYZ[0] = (float)(x/y * L);
-	XYZ[1] = (float)L;
-	XYZ[2] = (float)((1.-x-y)/y * L);
-}
-
-#if !LOGLUV_PUBLIC
-static
-#endif
-uint32
-LogLuv24fromXYZ(float XYZ[3], int em)
-{
-	int	Le, Ce;
-	double	u, v, s;
-					/* encode luminance */
-	Le = LogL10fromY(XYZ[1], em);
-					/* encode color */
-	s = XYZ[0] + 15.*XYZ[1] + 3.*XYZ[2];
-	if (!Le || s <= 0.) {
-		u = U_NEU;
-		v = V_NEU;
-	} else {
-		u = 4.*XYZ[0] / s;
-		v = 9.*XYZ[1] / s;
-	}
-	Ce = uv_encode(u, v, em);
-	if (Ce < 0)			/* never happens */
-		Ce = uv_encode(U_NEU, V_NEU, SGILOGENCODE_NODITHER);
-					/* combine encodings */
-	return (Le << 14 | Ce);
-}
-
-static void
-Luv24toXYZ(LogLuvState* sp, uint8* op, tmsize_t n)
-{
-	uint32* luv = (uint32*) sp->tbuf;  
-	float* xyz = (float*) op;
-
-	while (n-- > 0) {
-		LogLuv24toXYZ(*luv, xyz);
-		xyz += 3;
-		luv++;
-	}
-}
-
-static void
-Luv24toLuv48(LogLuvState* sp, uint8* op, tmsize_t n)
-{
-	uint32* luv = (uint32*) sp->tbuf;  
-	int16* luv3 = (int16*) op;
-
-	while (n-- > 0) {
-		double u, v;
-
-		*luv3++ = (int16)((*luv >> 12 & 0xffd) + 13314);
-		if (uv_decode(&u, &v, *luv&0x3fff) < 0) {
-			u = U_NEU;
-			v = V_NEU;
-		}
-		*luv3++ = (int16)(u * (1L<<15));
-		*luv3++ = (int16)(v * (1L<<15));
-		luv++;
-	}
-}
-
-static void
-Luv24toRGB(LogLuvState* sp, uint8* op, tmsize_t n)
-{
-	uint32* luv = (uint32*) sp->tbuf;  
-	uint8* rgb = (uint8*) op;
-
-	while (n-- > 0) {
-		float xyz[3];
-
-		LogLuv24toXYZ(*luv++, xyz);
-		XYZtoRGB24(xyz, rgb);
-		rgb += 3;
-	}
-}
-
-static void
-Luv24fromXYZ(LogLuvState* sp, uint8* op, tmsize_t n)
-{
-	uint32* luv = (uint32*) sp->tbuf;  
-	float* xyz = (float*) op;
-
-	while (n-- > 0) {
-		*luv++ = LogLuv24fromXYZ(xyz, sp->encode_meth);
-		xyz += 3;
-	}
-}
-
-static void
-Luv24fromLuv48(LogLuvState* sp, uint8* op, tmsize_t n)
-{
-	uint32* luv = (uint32*) sp->tbuf;  
-	int16* luv3 = (int16*) op;
-
-	while (n-- > 0) {
-		int Le, Ce;
-
-		if (luv3[0] <= 0)
-			Le = 0;
-		else if (luv3[0] >= (1<<12)+3314)
-			Le = (1<<10) - 1;
-		else if (sp->encode_meth == SGILOGENCODE_NODITHER)
-			Le = (luv3[0]-3314) >> 2;
-		else
-			Le = itrunc(.25*(luv3[0]-3314.), sp->encode_meth);
-
-		Ce = uv_encode((luv3[1]+.5)/(1<<15), (luv3[2]+.5)/(1<<15),
-					sp->encode_meth);
-		if (Ce < 0)	/* never happens */
-			Ce = uv_encode(U_NEU, V_NEU, SGILOGENCODE_NODITHER);
-		*luv++ = (uint32)Le << 14 | Ce;
-		luv3 += 3;
-	}
-}
-
-#if !LOGLUV_PUBLIC
-static
-#endif
-void
-LogLuv32toXYZ(uint32 p, float XYZ[3])
-{
-	double	L, u, v, s, x, y;
-					/* decode luminance */
-	L = LogL16toY((int)p >> 16);
-	if (L <= 0.) {
-		XYZ[0] = XYZ[1] = XYZ[2] = 0.;
-		return;
-	}
-					/* decode color */
-	u = 1./UVSCALE * ((p>>8 & 0xff) + .5);
-	v = 1./UVSCALE * ((p & 0xff) + .5);
-	s = 1./(6.*u - 16.*v + 12.);
-	x = 9.*u * s;
-	y = 4.*v * s;
-					/* convert to XYZ */
-	XYZ[0] = (float)(x/y * L);
-	XYZ[1] = (float)L;
-	XYZ[2] = (float)((1.-x-y)/y * L);
-}
-
-#if !LOGLUV_PUBLIC
-static
-#endif
-uint32
-LogLuv32fromXYZ(float XYZ[3], int em)
-{
-	unsigned int	Le, ue, ve;
-	double	u, v, s;
-					/* encode luminance */
-	Le = (unsigned int)LogL16fromY(XYZ[1], em);
-					/* encode color */
-	s = XYZ[0] + 15.*XYZ[1] + 3.*XYZ[2];
-	if (!Le || s <= 0.) {
-		u = U_NEU;
-		v = V_NEU;
-	} else {
-		u = 4.*XYZ[0] / s;
-		v = 9.*XYZ[1] / s;
-	}
-	if (u <= 0.) ue = 0;
-	else ue = itrunc(UVSCALE*u, em);
-	if (ue > 255) ue = 255;
-	if (v <= 0.) ve = 0;
-	else ve = itrunc(UVSCALE*v, em);
-	if (ve > 255) ve = 255;
-					/* combine encodings */
-	return (Le << 16 | ue << 8 | ve);
-}
-
-static void
-Luv32toXYZ(LogLuvState* sp, uint8* op, tmsize_t n)
-{
-	uint32* luv = (uint32*) sp->tbuf;  
-	float* xyz = (float*) op;
-
-	while (n-- > 0) {
-		LogLuv32toXYZ(*luv++, xyz);
-		xyz += 3;
-	}
-}
-
-static void
-Luv32toLuv48(LogLuvState* sp, uint8* op, tmsize_t n)
-{
-	uint32* luv = (uint32*) sp->tbuf;  
-	int16* luv3 = (int16*) op;
-
-	while (n-- > 0) {
-		double u, v;
-
-		*luv3++ = (int16)(*luv >> 16);
-		u = 1./UVSCALE * ((*luv>>8 & 0xff) + .5);
-		v = 1./UVSCALE * ((*luv & 0xff) + .5);
-		*luv3++ = (int16)(u * (1L<<15));
-		*luv3++ = (int16)(v * (1L<<15));
-		luv++;
-	}
-}
-
-static void
-Luv32toRGB(LogLuvState* sp, uint8* op, tmsize_t n)
-{
-	uint32* luv = (uint32*) sp->tbuf;  
-	uint8* rgb = (uint8*) op;
-
-	while (n-- > 0) {
-		float xyz[3];
-
-		LogLuv32toXYZ(*luv++, xyz);
-		XYZtoRGB24(xyz, rgb);
-		rgb += 3;
-	}
-}
-
-static void
-Luv32fromXYZ(LogLuvState* sp, uint8* op, tmsize_t n)
-{
-	uint32* luv = (uint32*) sp->tbuf;  
-	float* xyz = (float*) op;
-
-	while (n-- > 0) {
-		*luv++ = LogLuv32fromXYZ(xyz, sp->encode_meth);
-		xyz += 3;
-	}
-}
-
-static void
-Luv32fromLuv48(LogLuvState* sp, uint8* op, tmsize_t n)
-{
-	uint32* luv = (uint32*) sp->tbuf;
-	int16* luv3 = (int16*) op;
-
-	if (sp->encode_meth == SGILOGENCODE_NODITHER) {
-		while (n-- > 0) {
-			*luv++ = (uint32)luv3[0] << 16 |
-				(luv3[1]*(uint32)(UVSCALE+.5) >> 7 & 0xff00) |
-				(luv3[2]*(uint32)(UVSCALE+.5) >> 15 & 0xff);
-			luv3 += 3;
-		}
-		return;
-	}
-	while (n-- > 0) {
-		*luv++ = (uint32)luv3[0] << 16 |
-	(itrunc(luv3[1]*(UVSCALE/(1<<15)), sp->encode_meth) << 8 & 0xff00) |
-		(itrunc(luv3[2]*(UVSCALE/(1<<15)), sp->encode_meth) & 0xff);
-		luv3 += 3;
-	}
-}
-
-static void
-_logLuvNop(LogLuvState* sp, uint8* op, tmsize_t n)
-{
-	(void) sp; (void) op; (void) n;
-}
-
-static int
-LogL16GuessDataFmt(TIFFDirectory *td)
-{
-#define	PACK(s,b,f)	(((b)<<6)|((s)<<3)|(f))
-	switch (PACK(td->td_samplesperpixel, td->td_bitspersample, td->td_sampleformat)) {
-	case PACK(1, 32, SAMPLEFORMAT_IEEEFP):
-		return (SGILOGDATAFMT_FLOAT);
-	case PACK(1, 16, SAMPLEFORMAT_VOID):
-	case PACK(1, 16, SAMPLEFORMAT_INT):
-	case PACK(1, 16, SAMPLEFORMAT_UINT):
-		return (SGILOGDATAFMT_16BIT);
-	case PACK(1,  8, SAMPLEFORMAT_VOID):
-	case PACK(1,  8, SAMPLEFORMAT_UINT):
-		return (SGILOGDATAFMT_8BIT);
-	}
-#undef PACK
-	return (SGILOGDATAFMT_UNKNOWN);
-}
-
-static tmsize_t
-multiply_ms(tmsize_t m1, tmsize_t m2)
-{
-        return _TIFFMultiplySSize(NULL, m1, m2, NULL);
-}
-
-static int
-LogL16InitState(TIFF* tif)
-{
-	static const char module[] = "LogL16InitState";
-	TIFFDirectory *td = &tif->tif_dir;
-	LogLuvState* sp = DecoderState(tif);
-
-	assert(sp != NULL);
-	assert(td->td_photometric == PHOTOMETRIC_LOGL);
-
-	if( td->td_samplesperpixel != 1 )
-	{
-		TIFFErrorExt(tif->tif_clientdata, module,
-		             "Sorry, can not handle LogL image with %s=%d",
-			     "Samples/pixel", td->td_samplesperpixel);
-		return 0;
-	}
-
-	/* for some reason, we can't do this in TIFFInitLogL16 */
-	if (sp->user_datafmt == SGILOGDATAFMT_UNKNOWN)
-		sp->user_datafmt = LogL16GuessDataFmt(td);
-	switch (sp->user_datafmt) {
-	case SGILOGDATAFMT_FLOAT:
-		sp->pixel_size = sizeof (float);
-		break;
-	case SGILOGDATAFMT_16BIT:
-		sp->pixel_size = sizeof (int16);
-		break;
-	case SGILOGDATAFMT_8BIT:
-		sp->pixel_size = sizeof (uint8);
-		break;
-	default:
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "No support for converting user data format to LogL");
-		return (0);
-	}
-        if( isTiled(tif) )
-            sp->tbuflen = multiply_ms(td->td_tilewidth, td->td_tilelength);
-        else if( td->td_rowsperstrip < td->td_imagelength )
-            sp->tbuflen = multiply_ms(td->td_imagewidth, td->td_rowsperstrip);
+    if (c < 0 || c >= UV_NDIVS)
+        return (-1);
+    lower = 0; /* binary search */
+    upper = UV_NVS;
+    while (upper - lower > 1)
+    {
+        vi = (lower + upper) >> 1;
+        ui = c - uv_row[vi].ncum;
+        if (ui > 0)
+            lower = vi;
+        else if (ui < 0)
+            upper = vi;
         else
-            sp->tbuflen = multiply_ms(td->td_imagewidth, td->td_imagelength);
-	if (multiply_ms(sp->tbuflen, sizeof (int16)) == 0 ||
-	    (sp->tbuf = (uint8*) _TIFFmalloc(sp->tbuflen * sizeof (int16))) == NULL) {
-		TIFFErrorExt(tif->tif_clientdata, module, "No space for SGILog translation buffer");
-		return (0);
-	}
-	return (1);
-}
-
-static int
-LogLuvGuessDataFmt(TIFFDirectory *td)
-{
-	int guess;
-
-	/*
-	 * If the user didn't tell us their datafmt,
-	 * take our best guess from the bitspersample.
-	 */
-#define	PACK(a,b)	(((a)<<3)|(b))
-	switch (PACK(td->td_bitspersample, td->td_sampleformat)) {
-	case PACK(32, SAMPLEFORMAT_IEEEFP):
-		guess = SGILOGDATAFMT_FLOAT;
-		break;
-	case PACK(32, SAMPLEFORMAT_VOID):
-	case PACK(32, SAMPLEFORMAT_UINT):
-	case PACK(32, SAMPLEFORMAT_INT):
-		guess = SGILOGDATAFMT_RAW;
-		break;
-	case PACK(16, SAMPLEFORMAT_VOID):
-	case PACK(16, SAMPLEFORMAT_INT):
-	case PACK(16, SAMPLEFORMAT_UINT):
-		guess = SGILOGDATAFMT_16BIT;
-		break;
-	case PACK( 8, SAMPLEFORMAT_VOID):
-	case PACK( 8, SAMPLEFORMAT_UINT):
-		guess = SGILOGDATAFMT_8BIT;
-		break;
-	default:
-		guess = SGILOGDATAFMT_UNKNOWN;
-		break;
-#undef PACK
-	}
-	/*
-	 * Double-check samples per pixel.
-	 */
-	switch (td->td_samplesperpixel) {
-	case 1:
-		if (guess != SGILOGDATAFMT_RAW)
-			guess = SGILOGDATAFMT_UNKNOWN;
-		break;
-	case 3:
-		if (guess == SGILOGDATAFMT_RAW)
-			guess = SGILOGDATAFMT_UNKNOWN;
-		break;
-	default:
-		guess = SGILOGDATAFMT_UNKNOWN;
-		break;
-	}
-	return (guess);
-}
-
-static int
-LogLuvInitState(TIFF* tif)
-{
-	static const char module[] = "LogLuvInitState";
-	TIFFDirectory* td = &tif->tif_dir;
-	LogLuvState* sp = DecoderState(tif);
-
-	assert(sp != NULL);
-	assert(td->td_photometric == PHOTOMETRIC_LOGLUV);
-
-	/* for some reason, we can't do this in TIFFInitLogLuv */
-	if (td->td_planarconfig != PLANARCONFIG_CONTIG) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "SGILog compression cannot handle non-contiguous data");
-		return (0);
-	}
-	if (sp->user_datafmt == SGILOGDATAFMT_UNKNOWN)
-		sp->user_datafmt = LogLuvGuessDataFmt(td);
-	switch (sp->user_datafmt) {
-	case SGILOGDATAFMT_FLOAT:
-		sp->pixel_size = 3*sizeof (float);
-		break;
-	case SGILOGDATAFMT_16BIT:
-		sp->pixel_size = 3*sizeof (int16);
-		break;
-	case SGILOGDATAFMT_RAW:
-		sp->pixel_size = sizeof (uint32);
-		break;
-	case SGILOGDATAFMT_8BIT:
-		sp->pixel_size = 3*sizeof (uint8);
-		break;
-	default:
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "No support for converting user data format to LogLuv");
-		return (0);
-	}
-        if( isTiled(tif) )
-            sp->tbuflen = multiply_ms(td->td_tilewidth, td->td_tilelength);
-        else if( td->td_rowsperstrip < td->td_imagelength )
-            sp->tbuflen = multiply_ms(td->td_imagewidth, td->td_rowsperstrip);
-        else
-            sp->tbuflen = multiply_ms(td->td_imagewidth, td->td_imagelength);
-	if (multiply_ms(sp->tbuflen, sizeof (uint32)) == 0 ||
-	    (sp->tbuf = (uint8*) _TIFFmalloc(sp->tbuflen * sizeof (uint32))) == NULL) {
-		TIFFErrorExt(tif->tif_clientdata, module, "No space for SGILog translation buffer");
-		return (0);
-	}
-	return (1);
-}
-
-static int
-LogLuvFixupTags(TIFF* tif)
-{
-	(void) tif;
-	return (1);
-}
-
-static int
-LogLuvSetupDecode(TIFF* tif)
-{
-	static const char module[] = "LogLuvSetupDecode";
-	LogLuvState* sp = DecoderState(tif);
-	TIFFDirectory* td = &tif->tif_dir;
-
-	tif->tif_postdecode = _TIFFNoPostDecode;
-	switch (td->td_photometric) {
-	case PHOTOMETRIC_LOGLUV:
-		if (!LogLuvInitState(tif))
-			break;
-		if (td->td_compression == COMPRESSION_SGILOG24) {
-			tif->tif_decoderow = LogLuvDecode24;
-			switch (sp->user_datafmt) {
-			case SGILOGDATAFMT_FLOAT:
-				sp->tfunc = Luv24toXYZ;  
-				break;
-			case SGILOGDATAFMT_16BIT:
-				sp->tfunc = Luv24toLuv48;  
-				break;
-			case SGILOGDATAFMT_8BIT:
-				sp->tfunc = Luv24toRGB;
-				break;
-			}
-		} else {
-			tif->tif_decoderow = LogLuvDecode32;
-			switch (sp->user_datafmt) {
-			case SGILOGDATAFMT_FLOAT:
-				sp->tfunc = Luv32toXYZ;
-				break;
-			case SGILOGDATAFMT_16BIT:
-				sp->tfunc = Luv32toLuv48;
-				break;
-			case SGILOGDATAFMT_8BIT:
-				sp->tfunc = Luv32toRGB;
-				break;
-			}
-		}
-		return (1);
-	case PHOTOMETRIC_LOGL:
-		if (!LogL16InitState(tif))
-			break;
-		tif->tif_decoderow = LogL16Decode;
-		switch (sp->user_datafmt) {
-		case SGILOGDATAFMT_FLOAT:
-			sp->tfunc = L16toY;
-			break;
-		case SGILOGDATAFMT_8BIT:
-			sp->tfunc = L16toGry;
-			break;
-		}
-		return (1);
-	default:
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "Inappropriate photometric interpretation %d for SGILog compression; %s",
-		    td->td_photometric, "must be either LogLUV or LogL");
-		break;
-	}
-	return (0);
-}
-
-static int
-LogLuvSetupEncode(TIFF* tif)
-{
-	static const char module[] = "LogLuvSetupEncode";
-	LogLuvState* sp = EncoderState(tif);
-	TIFFDirectory* td = &tif->tif_dir;
-
-	switch (td->td_photometric) {
-	case PHOTOMETRIC_LOGLUV:
-		if (!LogLuvInitState(tif))
-			return (0);
-		if (td->td_compression == COMPRESSION_SGILOG24) {
-			tif->tif_encoderow = LogLuvEncode24;
-			switch (sp->user_datafmt) {
-			case SGILOGDATAFMT_FLOAT:
-				sp->tfunc = Luv24fromXYZ;
-				break;
-			case SGILOGDATAFMT_16BIT:
-				sp->tfunc = Luv24fromLuv48;  
-				break;
-			case SGILOGDATAFMT_RAW:
-				break;
-			default:
-				goto notsupported;
-			}
-		} else {
-			tif->tif_encoderow = LogLuvEncode32;  
-			switch (sp->user_datafmt) {
-			case SGILOGDATAFMT_FLOAT:
-				sp->tfunc = Luv32fromXYZ;  
-				break;
-			case SGILOGDATAFMT_16BIT:
-				sp->tfunc = Luv32fromLuv48;  
-				break;
-			case SGILOGDATAFMT_RAW:
-				break;
-			default:
-				goto notsupported;
-			}
-		}
-		break;
-	case PHOTOMETRIC_LOGL:
-		if (!LogL16InitState(tif))
-			return (0);
-		tif->tif_encoderow = LogL16Encode;  
-		switch (sp->user_datafmt) {
-		case SGILOGDATAFMT_FLOAT:
-			sp->tfunc = L16fromY;
-			break;
-		case SGILOGDATAFMT_16BIT:
-			break;
-		default:
-			goto notsupported;
-		}
-		break;
-	default:
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "Inappropriate photometric interpretation %d for SGILog compression; %s",
-		    td->td_photometric, "must be either LogLUV or LogL");
-		return (0);
-	}
-	sp->encoder_state = 1;
-	return (1);
-notsupported:
-	TIFFErrorExt(tif->tif_clientdata, module,
-	    "SGILog compression supported only for %s, or raw data",
-	    td->td_photometric == PHOTOMETRIC_LOGL ? "Y, L" : "XYZ, Luv");
-	return (0);
-}
-
-static void
-LogLuvClose(TIFF* tif)
-{
-        LogLuvState* sp = (LogLuvState*) tif->tif_data;
-	TIFFDirectory *td = &tif->tif_dir;
-
-	assert(sp != 0);
-	/*
-	 * For consistency, we always want to write out the same
-	 * bitspersample and sampleformat for our TIFF file,
-	 * regardless of the data format being used by the application.
-	 * Since this routine is called after tags have been set but
-	 * before they have been recorded in the file, we reset them here.
-         * Note: this is really a nasty approach. See PixarLogClose
-	 */
-        if( sp->encoder_state )
         {
-            /* See PixarLogClose. Might avoid issues with tags whose size depends
-             * on those below, but not completely sure this is enough. */
-            td->td_samplesperpixel =
-                (td->td_photometric == PHOTOMETRIC_LOGL) ? 1 : 3;
-            td->td_bitspersample = 16;
-            td->td_sampleformat = SAMPLEFORMAT_INT;
+            lower = vi;
+            break;
         }
+    }
+    vi = lower;
+    ui = c - uv_row[vi].ncum;
+    *up = uv_row[vi].ustart + (ui + .5) * UV_SQSIZ;
+    *vp = UV_VSTART + (vi + .5) * UV_SQSIZ;
+    return (0);
 }
 
-static void
-LogLuvCleanup(TIFF* tif)
+#if !LOGLUV_PUBLIC
+static
+#endif
+    void
+    LogLuv24toXYZ(uint32_t p, float *XYZ)
 {
-	LogLuvState* sp = (LogLuvState *)tif->tif_data;
-
-	assert(sp != 0);
-
-	tif->tif_tagmethods.vgetfield = sp->vgetparent;
-	tif->tif_tagmethods.vsetfield = sp->vsetparent;
-
-	if (sp->tbuf)
-		_TIFFfree(sp->tbuf);
-	_TIFFfree(sp);
-	tif->tif_data = NULL;
-
-	_TIFFSetDefaultCompressionState(tif);
+    int Ce;
+    double L, u, v, s, x, y;
+    /* decode luminance */
+    L = LogL10toY(p >> 14 & 0x3ff);
+    if (L <= 0.)
+    {
+        XYZ[0] = XYZ[1] = XYZ[2] = 0.;
+        return;
+    }
+    /* decode color */
+    Ce = p & 0x3fff;
+    if (uv_decode(&u, &v, Ce) < 0)
+    {
+        u = U_NEU;
+        v = V_NEU;
+    }
+    s = 1. / (6. * u - 16. * v + 12.);
+    x = 9. * u * s;
+    y = 4. * v * s;
+    /* convert to XYZ */
+    XYZ[0] = (float)(x / y * L);
+    XYZ[1] = (float)L;
+    XYZ[2] = (float)((1. - x - y) / y * L);
 }
 
-static int
-LogLuvVSetField(TIFF* tif, uint32 tag, va_list ap)
+#if !LOGLUV_PUBLIC
+static
+#endif
+    uint32_t
+    LogLuv24fromXYZ(float *XYZ, int em)
 {
-	static const char module[] = "LogLuvVSetField";
-	LogLuvState* sp = DecoderState(tif);
-	int bps, fmt;
-
-	switch (tag) {
-	case TIFFTAG_SGILOGDATAFMT:
-		sp->user_datafmt = (int) va_arg(ap, int);
-		/*
-		 * Tweak the TIFF header so that the rest of libtiff knows what
-		 * size of data will be passed between app and library, and
-		 * assume that the app knows what it is doing and is not
-		 * confused by these header manipulations...
-		 */
-		switch (sp->user_datafmt) {
-		case SGILOGDATAFMT_FLOAT:
-			bps = 32;
-			fmt = SAMPLEFORMAT_IEEEFP;
-			break;
-		case SGILOGDATAFMT_16BIT:
-			bps = 16;
-			fmt = SAMPLEFORMAT_INT;
-			break;
-		case SGILOGDATAFMT_RAW:
-			bps = 32;
-			fmt = SAMPLEFORMAT_UINT;
-			TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
-			break;
-		case SGILOGDATAFMT_8BIT:
-			bps = 8;
-			fmt = SAMPLEFORMAT_UINT;
-			break;
-		default:
-			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-			    "Unknown data format %d for LogLuv compression",
-			    sp->user_datafmt);
-			return (0);
-		}
-		TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps);
-		TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, fmt);
-		/*
-		 * Must recalculate sizes should bits/sample change.
-		 */
-		tif->tif_tilesize = isTiled(tif) ? TIFFTileSize(tif) : (tmsize_t) -1;
-		tif->tif_scanlinesize = TIFFScanlineSize(tif);
-		return (1);
-	case TIFFTAG_SGILOGENCODE:
-		sp->encode_meth = (int) va_arg(ap, int);
-		if (sp->encode_meth != SGILOGENCODE_NODITHER &&
-		    sp->encode_meth != SGILOGENCODE_RANDITHER) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Unknown encoding %d for LogLuv compression",
-			    sp->encode_meth);
-			return (0);
-		}
-		return (1);
-	default:
-		return (*sp->vsetparent)(tif, tag, ap);
-	}
+    int Le, Ce;
+    double u, v, s;
+    /* encode luminance */
+    Le = LogL10fromY(XYZ[1], em);
+    /* encode color */
+    s = XYZ[0] + 15. * XYZ[1] + 3. * XYZ[2];
+    if (!Le || s <= 0.)
+    {
+        u = U_NEU;
+        v = V_NEU;
+    }
+    else
+    {
+        u = 4. * XYZ[0] / s;
+        v = 9. * XYZ[1] / s;
+    }
+    Ce = uv_encode(u, v, em);
+    if (Ce < 0) /* never happens */
+        Ce = uv_encode(U_NEU, V_NEU, SGILOGENCODE_NODITHER);
+    /* combine encodings */
+    return (Le << 14 | Ce);
 }
 
-static int
-LogLuvVGetField(TIFF* tif, uint32 tag, va_list ap)
+static void Luv24toXYZ(LogLuvState *sp, uint8_t *op, tmsize_t n)
 {
-	LogLuvState *sp = (LogLuvState *)tif->tif_data;
+    uint32_t *luv = (uint32_t *)sp->tbuf;
+    float *xyz = (float *)op;
 
-	switch (tag) {
-	case TIFFTAG_SGILOGDATAFMT:
-		*va_arg(ap, int*) = sp->user_datafmt;
-		return (1);
-	default:
-		return (*sp->vgetparent)(tif, tag, ap);
-	}
+    while (n-- > 0)
+    {
+        LogLuv24toXYZ(*luv, xyz);
+        xyz += 3;
+        luv++;
+    }
+}
+
+static void Luv24toLuv48(LogLuvState *sp, uint8_t *op, tmsize_t n)
+{
+    uint32_t *luv = (uint32_t *)sp->tbuf;
+    int16_t *luv3 = (int16_t *)op;
+
+    while (n-- > 0)
+    {
+        double u, v;
+
+        *luv3++ = (int16_t)((*luv >> 12 & 0xffd) + 13314);
+        if (uv_decode(&u, &v, *luv & 0x3fff) < 0)
+        {
+            u = U_NEU;
+            v = V_NEU;
+        }
+        *luv3++ = (int16_t)(u * (1L << 15));
+        *luv3++ = (int16_t)(v * (1L << 15));
+        luv++;
+    }
+}
+
+static void Luv24toRGB(LogLuvState *sp, uint8_t *op, tmsize_t n)
+{
+    uint32_t *luv = (uint32_t *)sp->tbuf;
+    uint8_t *rgb = (uint8_t *)op;
+
+    while (n-- > 0)
+    {
+        float xyz[3];
+
+        LogLuv24toXYZ(*luv++, xyz);
+        XYZtoRGB24(xyz, rgb);
+        rgb += 3;
+    }
+}
+
+static void Luv24fromXYZ(LogLuvState *sp, uint8_t *op, tmsize_t n)
+{
+    uint32_t *luv = (uint32_t *)sp->tbuf;
+    float *xyz = (float *)op;
+
+    while (n-- > 0)
+    {
+        *luv++ = LogLuv24fromXYZ(xyz, sp->encode_meth);
+        xyz += 3;
+    }
+}
+
+static void Luv24fromLuv48(LogLuvState *sp, uint8_t *op, tmsize_t n)
+{
+    uint32_t *luv = (uint32_t *)sp->tbuf;
+    int16_t *luv3 = (int16_t *)op;
+
+    while (n-- > 0)
+    {
+        int Le, Ce;
+
+        if (luv3[0] <= 0)
+            Le = 0;
+        else if (luv3[0] >= (1 << 12) + 3314)
+            Le = (1 << 10) - 1;
+        else if (sp->encode_meth == SGILOGENCODE_NODITHER)
+            Le = (luv3[0] - 3314) >> 2;
+        else
+            Le = tiff_itrunc(.25 * (luv3[0] - 3314.), sp->encode_meth);
+
+        Ce = uv_encode((luv3[1] + .5) / (1 << 15), (luv3[2] + .5) / (1 << 15),
+                       sp->encode_meth);
+        if (Ce < 0) /* never happens */
+            Ce = uv_encode(U_NEU, V_NEU, SGILOGENCODE_NODITHER);
+        *luv++ = (uint32_t)Le << 14 | Ce;
+        luv3 += 3;
+    }
+}
+
+#if !LOGLUV_PUBLIC
+static
+#endif
+    void
+    LogLuv32toXYZ(uint32_t p, float *XYZ)
+{
+    double L, u, v, s, x, y;
+    /* decode luminance */
+    L = LogL16toY((int)p >> 16);
+    if (L <= 0.)
+    {
+        XYZ[0] = XYZ[1] = XYZ[2] = 0.;
+        return;
+    }
+    /* decode color */
+    u = 1. / UVSCALE * ((p >> 8 & 0xff) + .5);
+    v = 1. / UVSCALE * ((p & 0xff) + .5);
+    s = 1. / (6. * u - 16. * v + 12.);
+    x = 9. * u * s;
+    y = 4. * v * s;
+    /* convert to XYZ */
+    XYZ[0] = (float)(x / y * L);
+    XYZ[1] = (float)L;
+    XYZ[2] = (float)((1. - x - y) / y * L);
+}
+
+#if !LOGLUV_PUBLIC
+static
+#endif
+    uint32_t
+    LogLuv32fromXYZ(float *XYZ, int em)
+{
+    unsigned int Le, ue, ve;
+    double u, v, s;
+    /* encode luminance */
+    Le = (unsigned int)LogL16fromY(XYZ[1], em);
+    /* encode color */
+    s = XYZ[0] + 15. * XYZ[1] + 3. * XYZ[2];
+    if (!Le || s <= 0.)
+    {
+        u = U_NEU;
+        v = V_NEU;
+    }
+    else
+    {
+        u = 4. * XYZ[0] / s;
+        v = 9. * XYZ[1] / s;
+    }
+    if (u <= 0.)
+        ue = 0;
+    else
+        ue = tiff_itrunc(UVSCALE * u, em);
+    if (ue > 255)
+        ue = 255;
+    if (v <= 0.)
+        ve = 0;
+    else
+        ve = tiff_itrunc(UVSCALE * v, em);
+    if (ve > 255)
+        ve = 255;
+    /* combine encodings */
+    return (Le << 16 | ue << 8 | ve);
+}
+
+static void Luv32toXYZ(LogLuvState *sp, uint8_t *op, tmsize_t n)
+{
+    uint32_t *luv = (uint32_t *)sp->tbuf;
+    float *xyz = (float *)op;
+
+    while (n-- > 0)
+    {
+        LogLuv32toXYZ(*luv++, xyz);
+        xyz += 3;
+    }
+}
+
+static void Luv32toLuv48(LogLuvState *sp, uint8_t *op, tmsize_t n)
+{
+    uint32_t *luv = (uint32_t *)sp->tbuf;
+    int16_t *luv3 = (int16_t *)op;
+
+    while (n-- > 0)
+    {
+        double u, v;
+
+        *luv3++ = (int16_t)(*luv >> 16);
+        u = 1. / UVSCALE * ((*luv >> 8 & 0xff) + .5);
+        v = 1. / UVSCALE * ((*luv & 0xff) + .5);
+        *luv3++ = (int16_t)(u * (1L << 15));
+        *luv3++ = (int16_t)(v * (1L << 15));
+        luv++;
+    }
+}
+
+static void Luv32toRGB(LogLuvState *sp, uint8_t *op, tmsize_t n)
+{
+    uint32_t *luv = (uint32_t *)sp->tbuf;
+    uint8_t *rgb = (uint8_t *)op;
+
+    while (n-- > 0)
+    {
+        float xyz[3];
+
+        LogLuv32toXYZ(*luv++, xyz);
+        XYZtoRGB24(xyz, rgb);
+        rgb += 3;
+    }
+}
+
+static void Luv32fromXYZ(LogLuvState *sp, uint8_t *op, tmsize_t n)
+{
+    uint32_t *luv = (uint32_t *)sp->tbuf;
+    float *xyz = (float *)op;
+
+    while (n-- > 0)
+    {
+        *luv++ = LogLuv32fromXYZ(xyz, sp->encode_meth);
+        xyz += 3;
+    }
+}
+
+static void Luv32fromLuv48(LogLuvState *sp, uint8_t *op, tmsize_t n)
+{
+    uint32_t *luv = (uint32_t *)sp->tbuf;
+    int16_t *luv3 = (int16_t *)op;
+
+    if (sp->encode_meth == SGILOGENCODE_NODITHER)
+    {
+        while (n-- > 0)
+        {
+            *luv++ = (uint32_t)luv3[0] << 16 |
+                     (luv3[1] * (uint32_t)(UVSCALE + .5) >> 7 & 0xff00) |
+                     (luv3[2] * (uint32_t)(UVSCALE + .5) >> 15 & 0xff);
+            luv3 += 3;
+        }
+        return;
+    }
+    while (n-- > 0)
+    {
+        *luv++ =
+            (uint32_t)luv3[0] << 16 |
+            (tiff_itrunc(luv3[1] * (UVSCALE / (1 << 15)), sp->encode_meth)
+                 << 8 &
+             0xff00) |
+            (tiff_itrunc(luv3[2] * (UVSCALE / (1 << 15)), sp->encode_meth) &
+             0xff);
+        luv3 += 3;
+    }
+}
+
+static void _logLuvNop(LogLuvState *sp, uint8_t *op, tmsize_t n)
+{
+    (void)sp;
+    (void)op;
+    (void)n;
+}
+
+static int LogL16GuessDataFmt(TIFFDirectory *td)
+{
+#define PACK(s, b, f) (((b) << 6) | ((s) << 3) | (f))
+    switch (
+        PACK(td->td_samplesperpixel, td->td_bitspersample, td->td_sampleformat))
+    {
+        case PACK(1, 32, SAMPLEFORMAT_IEEEFP):
+            return (SGILOGDATAFMT_FLOAT);
+        case PACK(1, 16, SAMPLEFORMAT_VOID):
+        case PACK(1, 16, SAMPLEFORMAT_INT):
+        case PACK(1, 16, SAMPLEFORMAT_UINT):
+            return (SGILOGDATAFMT_16BIT);
+        case PACK(1, 8, SAMPLEFORMAT_VOID):
+        case PACK(1, 8, SAMPLEFORMAT_UINT):
+            return (SGILOGDATAFMT_8BIT);
+    }
+#undef PACK
+    return (SGILOGDATAFMT_UNKNOWN);
+}
+
+static tmsize_t multiply_ms(tmsize_t m1, tmsize_t m2)
+{
+    return _TIFFMultiplySSize(NULL, m1, m2, NULL);
+}
+
+static int LogL16InitState(TIFF *tif)
+{
+    static const char module[] = "LogL16InitState";
+    TIFFDirectory *td = &tif->tif_dir;
+    LogLuvState *sp = DecoderState(tif);
+
+    assert(sp != NULL);
+    assert(td->td_photometric == PHOTOMETRIC_LOGL);
+
+    if (td->td_samplesperpixel != 1)
+    {
+        TIFFErrorExtR(tif, module,
+                      "Sorry, can not handle LogL image with %s=%" PRIu16,
+                      "Samples/pixel", td->td_samplesperpixel);
+        return 0;
+    }
+
+    /* for some reason, we can't do this in TIFFInitLogL16 */
+    if (sp->user_datafmt == SGILOGDATAFMT_UNKNOWN)
+        sp->user_datafmt = LogL16GuessDataFmt(td);
+    switch (sp->user_datafmt)
+    {
+        case SGILOGDATAFMT_FLOAT:
+            sp->pixel_size = sizeof(float);
+            break;
+        case SGILOGDATAFMT_16BIT:
+            sp->pixel_size = sizeof(int16_t);
+            break;
+        case SGILOGDATAFMT_8BIT:
+            sp->pixel_size = sizeof(uint8_t);
+            break;
+        default:
+            TIFFErrorExtR(tif, module,
+                          "No support for converting user data format to LogL");
+            return (0);
+    }
+    if (isTiled(tif))
+        sp->tbuflen = multiply_ms(td->td_tilewidth, td->td_tilelength);
+    else if (td->td_rowsperstrip < td->td_imagelength)
+        sp->tbuflen = multiply_ms(td->td_imagewidth, td->td_rowsperstrip);
+    else
+        sp->tbuflen = multiply_ms(td->td_imagewidth, td->td_imagelength);
+    if (multiply_ms(sp->tbuflen, sizeof(int16_t)) == 0 ||
+        (sp->tbuf = (uint8_t *)_TIFFmallocExt(
+             tif, sp->tbuflen * sizeof(int16_t))) == NULL)
+    {
+        TIFFErrorExtR(tif, module, "No space for SGILog translation buffer");
+        return (0);
+    }
+    return (1);
+}
+
+static int LogLuvGuessDataFmt(TIFFDirectory *td)
+{
+    int guess;
+
+    /*
+     * If the user didn't tell us their datafmt,
+     * take our best guess from the bitspersample.
+     */
+#define PACK(a, b) (((a) << 3) | (b))
+    switch (PACK(td->td_bitspersample, td->td_sampleformat))
+    {
+        case PACK(32, SAMPLEFORMAT_IEEEFP):
+            guess = SGILOGDATAFMT_FLOAT;
+            break;
+        case PACK(32, SAMPLEFORMAT_VOID):
+        case PACK(32, SAMPLEFORMAT_UINT):
+        case PACK(32, SAMPLEFORMAT_INT):
+            guess = SGILOGDATAFMT_RAW;
+            break;
+        case PACK(16, SAMPLEFORMAT_VOID):
+        case PACK(16, SAMPLEFORMAT_INT):
+        case PACK(16, SAMPLEFORMAT_UINT):
+            guess = SGILOGDATAFMT_16BIT;
+            break;
+        case PACK(8, SAMPLEFORMAT_VOID):
+        case PACK(8, SAMPLEFORMAT_UINT):
+            guess = SGILOGDATAFMT_8BIT;
+            break;
+        default:
+            guess = SGILOGDATAFMT_UNKNOWN;
+            break;
+#undef PACK
+    }
+    /*
+     * Double-check samples per pixel.
+     */
+    switch (td->td_samplesperpixel)
+    {
+        case 1:
+            if (guess != SGILOGDATAFMT_RAW)
+                guess = SGILOGDATAFMT_UNKNOWN;
+            break;
+        case 3:
+            if (guess == SGILOGDATAFMT_RAW)
+                guess = SGILOGDATAFMT_UNKNOWN;
+            break;
+        default:
+            guess = SGILOGDATAFMT_UNKNOWN;
+            break;
+    }
+    return (guess);
+}
+
+static int LogLuvInitState(TIFF *tif)
+{
+    static const char module[] = "LogLuvInitState";
+    TIFFDirectory *td = &tif->tif_dir;
+    LogLuvState *sp = DecoderState(tif);
+
+    assert(sp != NULL);
+    assert(td->td_photometric == PHOTOMETRIC_LOGLUV);
+
+    /* for some reason, we can't do this in TIFFInitLogLuv */
+    if (td->td_planarconfig != PLANARCONFIG_CONTIG)
+    {
+        TIFFErrorExtR(tif, module,
+                      "SGILog compression cannot handle non-contiguous data");
+        return (0);
+    }
+    if (sp->user_datafmt == SGILOGDATAFMT_UNKNOWN)
+        sp->user_datafmt = LogLuvGuessDataFmt(td);
+    switch (sp->user_datafmt)
+    {
+        case SGILOGDATAFMT_FLOAT:
+            sp->pixel_size = 3 * sizeof(float);
+            break;
+        case SGILOGDATAFMT_16BIT:
+            sp->pixel_size = 3 * sizeof(int16_t);
+            break;
+        case SGILOGDATAFMT_RAW:
+            sp->pixel_size = sizeof(uint32_t);
+            break;
+        case SGILOGDATAFMT_8BIT:
+            sp->pixel_size = 3 * sizeof(uint8_t);
+            break;
+        default:
+            TIFFErrorExtR(
+                tif, module,
+                "No support for converting user data format to LogLuv");
+            return (0);
+    }
+    if (isTiled(tif))
+        sp->tbuflen = multiply_ms(td->td_tilewidth, td->td_tilelength);
+    else if (td->td_rowsperstrip < td->td_imagelength)
+        sp->tbuflen = multiply_ms(td->td_imagewidth, td->td_rowsperstrip);
+    else
+        sp->tbuflen = multiply_ms(td->td_imagewidth, td->td_imagelength);
+    if (multiply_ms(sp->tbuflen, sizeof(uint32_t)) == 0 ||
+        (sp->tbuf = (uint8_t *)_TIFFmallocExt(
+             tif, sp->tbuflen * sizeof(uint32_t))) == NULL)
+    {
+        TIFFErrorExtR(tif, module, "No space for SGILog translation buffer");
+        return (0);
+    }
+    return (1);
+}
+
+static int LogLuvFixupTags(TIFF *tif)
+{
+    (void)tif;
+    return (1);
+}
+
+static int LogLuvSetupDecode(TIFF *tif)
+{
+    static const char module[] = "LogLuvSetupDecode";
+    LogLuvState *sp = DecoderState(tif);
+    TIFFDirectory *td = &tif->tif_dir;
+
+    tif->tif_postdecode = _TIFFNoPostDecode;
+    switch (td->td_photometric)
+    {
+        case PHOTOMETRIC_LOGLUV:
+            if (!LogLuvInitState(tif))
+                break;
+            if (td->td_compression == COMPRESSION_SGILOG24)
+            {
+                tif->tif_decoderow = LogLuvDecode24;
+                switch (sp->user_datafmt)
+                {
+                    case SGILOGDATAFMT_FLOAT:
+                        sp->tfunc = Luv24toXYZ;
+                        break;
+                    case SGILOGDATAFMT_16BIT:
+                        sp->tfunc = Luv24toLuv48;
+                        break;
+                    case SGILOGDATAFMT_8BIT:
+                        sp->tfunc = Luv24toRGB;
+                        break;
+                }
+            }
+            else
+            {
+                tif->tif_decoderow = LogLuvDecode32;
+                switch (sp->user_datafmt)
+                {
+                    case SGILOGDATAFMT_FLOAT:
+                        sp->tfunc = Luv32toXYZ;
+                        break;
+                    case SGILOGDATAFMT_16BIT:
+                        sp->tfunc = Luv32toLuv48;
+                        break;
+                    case SGILOGDATAFMT_8BIT:
+                        sp->tfunc = Luv32toRGB;
+                        break;
+                }
+            }
+            return (1);
+        case PHOTOMETRIC_LOGL:
+            if (!LogL16InitState(tif))
+                break;
+            tif->tif_decoderow = LogL16Decode;
+            switch (sp->user_datafmt)
+            {
+                case SGILOGDATAFMT_FLOAT:
+                    sp->tfunc = L16toY;
+                    break;
+                case SGILOGDATAFMT_8BIT:
+                    sp->tfunc = L16toGry;
+                    break;
+            }
+            return (1);
+        default:
+            TIFFErrorExtR(tif, module,
+                          "Inappropriate photometric interpretation %" PRIu16
+                          " for SGILog compression; %s",
+                          td->td_photometric, "must be either LogLUV or LogL");
+            break;
+    }
+    return (0);
+}
+
+static int LogLuvSetupEncode(TIFF *tif)
+{
+    static const char module[] = "LogLuvSetupEncode";
+    LogLuvState *sp = EncoderState(tif);
+    TIFFDirectory *td = &tif->tif_dir;
+
+    switch (td->td_photometric)
+    {
+        case PHOTOMETRIC_LOGLUV:
+            if (!LogLuvInitState(tif))
+                return (0);
+            if (td->td_compression == COMPRESSION_SGILOG24)
+            {
+                tif->tif_encoderow = LogLuvEncode24;
+                switch (sp->user_datafmt)
+                {
+                    case SGILOGDATAFMT_FLOAT:
+                        sp->tfunc = Luv24fromXYZ;
+                        break;
+                    case SGILOGDATAFMT_16BIT:
+                        sp->tfunc = Luv24fromLuv48;
+                        break;
+                    case SGILOGDATAFMT_RAW:
+                        break;
+                    default:
+                        goto notsupported;
+                }
+            }
+            else
+            {
+                tif->tif_encoderow = LogLuvEncode32;
+                switch (sp->user_datafmt)
+                {
+                    case SGILOGDATAFMT_FLOAT:
+                        sp->tfunc = Luv32fromXYZ;
+                        break;
+                    case SGILOGDATAFMT_16BIT:
+                        sp->tfunc = Luv32fromLuv48;
+                        break;
+                    case SGILOGDATAFMT_RAW:
+                        break;
+                    default:
+                        goto notsupported;
+                }
+            }
+            break;
+        case PHOTOMETRIC_LOGL:
+            if (!LogL16InitState(tif))
+                return (0);
+            tif->tif_encoderow = LogL16Encode;
+            switch (sp->user_datafmt)
+            {
+                case SGILOGDATAFMT_FLOAT:
+                    sp->tfunc = L16fromY;
+                    break;
+                case SGILOGDATAFMT_16BIT:
+                    break;
+                default:
+                    goto notsupported;
+            }
+            break;
+        default:
+            TIFFErrorExtR(tif, module,
+                          "Inappropriate photometric interpretation %" PRIu16
+                          " for SGILog compression; %s",
+                          td->td_photometric, "must be either LogLUV or LogL");
+            return (0);
+    }
+    sp->encoder_state = 1;
+    return (1);
+notsupported:
+    TIFFErrorExtR(tif, module,
+                  "SGILog compression supported only for %s, or raw data",
+                  td->td_photometric == PHOTOMETRIC_LOGL ? "Y, L" : "XYZ, Luv");
+    return (0);
+}
+
+static void LogLuvClose(TIFF *tif)
+{
+    LogLuvState *sp = (LogLuvState *)tif->tif_data;
+    TIFFDirectory *td = &tif->tif_dir;
+
+    assert(sp != 0);
+    /*
+     * For consistency, we always want to write out the same
+     * bitspersample and sampleformat for our TIFF file,
+     * regardless of the data format being used by the application.
+     * Since this routine is called after tags have been set but
+     * before they have been recorded in the file, we reset them here.
+     * Note: this is really a nasty approach. See PixarLogClose
+     */
+    if (sp->encoder_state)
+    {
+        /* See PixarLogClose. Might avoid issues with tags whose size depends
+         * on those below, but not completely sure this is enough. */
+        td->td_samplesperpixel =
+            (td->td_photometric == PHOTOMETRIC_LOGL) ? 1 : 3;
+        td->td_bitspersample = 16;
+        td->td_sampleformat = SAMPLEFORMAT_INT;
+    }
+}
+
+static void LogLuvCleanup(TIFF *tif)
+{
+    LogLuvState *sp = (LogLuvState *)tif->tif_data;
+
+    assert(sp != 0);
+
+    tif->tif_tagmethods.vgetfield = sp->vgetparent;
+    tif->tif_tagmethods.vsetfield = sp->vsetparent;
+
+    if (sp->tbuf)
+        _TIFFfreeExt(tif, sp->tbuf);
+    _TIFFfreeExt(tif, sp);
+    tif->tif_data = NULL;
+
+    _TIFFSetDefaultCompressionState(tif);
+}
+
+static int LogLuvVSetField(TIFF *tif, uint32_t tag, va_list ap)
+{
+    static const char module[] = "LogLuvVSetField";
+    LogLuvState *sp = DecoderState(tif);
+    int bps, fmt;
+
+    switch (tag)
+    {
+        case TIFFTAG_SGILOGDATAFMT:
+            sp->user_datafmt = (int)va_arg(ap, int);
+            /*
+             * Tweak the TIFF header so that the rest of libtiff knows what
+             * size of data will be passed between app and library, and
+             * assume that the app knows what it is doing and is not
+             * confused by these header manipulations...
+             */
+            switch (sp->user_datafmt)
+            {
+                case SGILOGDATAFMT_FLOAT:
+                    bps = 32;
+                    fmt = SAMPLEFORMAT_IEEEFP;
+                    break;
+                case SGILOGDATAFMT_16BIT:
+                    bps = 16;
+                    fmt = SAMPLEFORMAT_INT;
+                    break;
+                case SGILOGDATAFMT_RAW:
+                    bps = 32;
+                    fmt = SAMPLEFORMAT_UINT;
+                    TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
+                    break;
+                case SGILOGDATAFMT_8BIT:
+                    bps = 8;
+                    fmt = SAMPLEFORMAT_UINT;
+                    break;
+                default:
+                    TIFFErrorExtR(
+                        tif, tif->tif_name,
+                        "Unknown data format %d for LogLuv compression",
+                        sp->user_datafmt);
+                    return (0);
+            }
+            TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps);
+            TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, fmt);
+            /*
+             * Must recalculate sizes should bits/sample change.
+             */
+            tif->tif_tilesize = isTiled(tif) ? TIFFTileSize(tif) : (tmsize_t)-1;
+            tif->tif_scanlinesize = TIFFScanlineSize(tif);
+            return (1);
+        case TIFFTAG_SGILOGENCODE:
+            sp->encode_meth = (int)va_arg(ap, int);
+            if (sp->encode_meth != SGILOGENCODE_NODITHER &&
+                sp->encode_meth != SGILOGENCODE_RANDITHER)
+            {
+                TIFFErrorExtR(tif, module,
+                              "Unknown encoding %d for LogLuv compression",
+                              sp->encode_meth);
+                return (0);
+            }
+            return (1);
+        default:
+            return (*sp->vsetparent)(tif, tag, ap);
+    }
+}
+
+static int LogLuvVGetField(TIFF *tif, uint32_t tag, va_list ap)
+{
+    LogLuvState *sp = (LogLuvState *)tif->tif_data;
+
+    switch (tag)
+    {
+        case TIFFTAG_SGILOGDATAFMT:
+            *va_arg(ap, int *) = sp->user_datafmt;
+            return (1);
+        default:
+            return (*sp->vgetparent)(tif, tag, ap);
+    }
 }
 
 static const TIFFField LogLuvFields[] = {
-    { TIFFTAG_SGILOGDATAFMT, 0, 0, TIFF_SHORT, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "SGILogDataFmt", NULL},
-    { TIFFTAG_SGILOGENCODE, 0, 0, TIFF_SHORT, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "SGILogEncode", NULL}
-};
+    {TIFFTAG_SGILOGDATAFMT, 0, 0, TIFF_SHORT, 0, TIFF_SETGET_INT,
+     TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "SGILogDataFmt", NULL},
+    {TIFFTAG_SGILOGENCODE, 0, 0, TIFF_SHORT, 0, TIFF_SETGET_INT,
+     TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "SGILogEncode", NULL}};
 
-int
-TIFFInitSGILog(TIFF* tif, int scheme)
+int TIFFInitSGILog(TIFF *tif, int scheme)
 {
-	static const char module[] = "TIFFInitSGILog";
-	LogLuvState* sp;
+    static const char module[] = "TIFFInitSGILog";
+    LogLuvState *sp;
 
-	assert(scheme == COMPRESSION_SGILOG24 || scheme == COMPRESSION_SGILOG);
+    assert(scheme == COMPRESSION_SGILOG24 || scheme == COMPRESSION_SGILOG);
 
-	/*
-	 * Merge codec-specific tag information.
-	 */
-	if (!_TIFFMergeFields(tif, LogLuvFields,
-			      TIFFArrayCount(LogLuvFields))) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "Merging SGILog codec-specific tags failed");
-		return 0;
-	}
+    /*
+     * Merge codec-specific tag information.
+     */
+    if (!_TIFFMergeFields(tif, LogLuvFields, TIFFArrayCount(LogLuvFields)))
+    {
+        TIFFErrorExtR(tif, module, "Merging SGILog codec-specific tags failed");
+        return 0;
+    }
 
-	/*
-	 * Allocate state block so tag methods have storage to record values.
-	 */
-	tif->tif_data = (uint8*) _TIFFmalloc(sizeof (LogLuvState));
-	if (tif->tif_data == NULL)
-		goto bad;
-	sp = (LogLuvState*) tif->tif_data;
-	_TIFFmemset((void*)sp, 0, sizeof (*sp));
-	sp->user_datafmt = SGILOGDATAFMT_UNKNOWN;
-	sp->encode_meth = (scheme == COMPRESSION_SGILOG24) ?
-	    SGILOGENCODE_RANDITHER : SGILOGENCODE_NODITHER;
-	sp->tfunc = _logLuvNop;
+    /*
+     * Allocate state block so tag methods have storage to record values.
+     */
+    tif->tif_data = (uint8_t *)_TIFFmallocExt(tif, sizeof(LogLuvState));
+    if (tif->tif_data == NULL)
+        goto bad;
+    sp = (LogLuvState *)tif->tif_data;
+    _TIFFmemset((void *)sp, 0, sizeof(*sp));
+    sp->user_datafmt = SGILOGDATAFMT_UNKNOWN;
+    sp->encode_meth = (scheme == COMPRESSION_SGILOG24) ? SGILOGENCODE_RANDITHER
+                                                       : SGILOGENCODE_NODITHER;
+    sp->tfunc = _logLuvNop;
 
-	/*
-	 * Install codec methods.
-	 * NB: tif_decoderow & tif_encoderow are filled
-	 *     in at setup time.
-	 */
-	tif->tif_fixuptags = LogLuvFixupTags;  
-	tif->tif_setupdecode = LogLuvSetupDecode;
-	tif->tif_decodestrip = LogLuvDecodeStrip;
-	tif->tif_decodetile = LogLuvDecodeTile;
-	tif->tif_setupencode = LogLuvSetupEncode;
-	tif->tif_encodestrip = LogLuvEncodeStrip;  
-	tif->tif_encodetile = LogLuvEncodeTile;
-	tif->tif_close = LogLuvClose;
-	tif->tif_cleanup = LogLuvCleanup;
+    /*
+     * Install codec methods.
+     * NB: tif_decoderow & tif_encoderow are filled
+     *     in at setup time.
+     */
+    tif->tif_fixuptags = LogLuvFixupTags;
+    tif->tif_setupdecode = LogLuvSetupDecode;
+    tif->tif_decodestrip = LogLuvDecodeStrip;
+    tif->tif_decodetile = LogLuvDecodeTile;
+    tif->tif_setupencode = LogLuvSetupEncode;
+    tif->tif_encodestrip = LogLuvEncodeStrip;
+    tif->tif_encodetile = LogLuvEncodeTile;
+    tif->tif_close = LogLuvClose;
+    tif->tif_cleanup = LogLuvCleanup;
 
-	/*
-	 * Override parent get/set field methods.
-	 */
-	sp->vgetparent = tif->tif_tagmethods.vgetfield;
-	tif->tif_tagmethods.vgetfield = LogLuvVGetField;   /* hook for codec tags */
-	sp->vsetparent = tif->tif_tagmethods.vsetfield;
-	tif->tif_tagmethods.vsetfield = LogLuvVSetField;   /* hook for codec tags */
+    /*
+     * Override parent get/set field methods.
+     */
+    sp->vgetparent = tif->tif_tagmethods.vgetfield;
+    tif->tif_tagmethods.vgetfield = LogLuvVGetField; /* hook for codec tags */
+    sp->vsetparent = tif->tif_tagmethods.vsetfield;
+    tif->tif_tagmethods.vsetfield = LogLuvVSetField; /* hook for codec tags */
 
-	return (1);
+    return (1);
 bad:
-	TIFFErrorExt(tif->tif_clientdata, module,
-		     "%s: No space for LogLuv state block", tif->tif_name);
-	return (0);
+    TIFFErrorExtR(tif, module, "%s: No space for LogLuv state block",
+                  tif->tif_name);
+    return (0);
 }
 #endif /* LOGLUV_SUPPORT */
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_lzw.c b/third_party/libtiff/tif_lzw.c
index 21064f2..d631fa1 100644
--- a/third_party/libtiff/tif_lzw.c
+++ b/third_party/libtiff/tif_lzw.c
@@ -1,31 +1,32 @@
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ * Copyright (c) 2022 Even Rouault
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
 #include "tiffiop.h"
 #ifdef LZW_SUPPORT
 /*
- * TIFF Library.  
+ * TIFF Library.
  * Rev 5.0 Lempel-Ziv & Welch Compression Support
  *
  * This code is derived from the compress program whose code is
@@ -36,7 +37,13 @@
  */
 #include "tif_predict.h"
 
+#include <stdbool.h>
 #include <stdio.h>
+#include <stdlib.h>
+
+/* Select the plausible largest natural integer type for the architecture */
+#define SIZEOF_WORDTYPE SIZEOF_SIZE_T
+typedef size_t WordType;
 
 /*
  * NB: The 5.0 spec describes a different algorithm than Aldus
@@ -51,34 +58,27 @@
  *
  * Future revisions to the TIFF spec are expected to "clarify this issue".
  */
-#define LZW_COMPAT              /* include backwards compatibility code */
-/*
- * Each strip of data is supposed to be terminated by a CODE_EOI.
- * If the following #define is included, the decoder will also
- * check for end-of-strip w/o seeing this code.  This makes the
- * library more robust, but also slower.
- */
-#define LZW_CHECKEOS            /* include checks for strips w/o EOI code */
+#define LZW_COMPAT /* include backwards compatibility code */
 
-#define MAXCODE(n)	((1L<<(n))-1)
+#define MAXCODE(n) ((1L << (n)) - 1)
 /*
  * The TIFF spec specifies that encoded bit
  * strings range from 9 to 12 bits.
  */
-#define BITS_MIN        9               /* start with 9 bits */
-#define BITS_MAX        12              /* max of 12 bit strings */
+#define BITS_MIN 9  /* start with 9 bits */
+#define BITS_MAX 12 /* max of 12 bit strings */
 /* predefined codes */
-#define CODE_CLEAR      256             /* code to clear string table */
-#define CODE_EOI        257             /* end-of-information code */
-#define CODE_FIRST      258             /* first free code entry */
-#define CODE_MAX        MAXCODE(BITS_MAX)
-#define HSIZE           9001L           /* 91% occupancy */
-#define HSHIFT          (13-8)
+#define CODE_CLEAR 256 /* code to clear string table */
+#define CODE_EOI 257   /* end-of-information code */
+#define CODE_FIRST 258 /* first free code entry */
+#define CODE_MAX MAXCODE(BITS_MAX)
+#define HSIZE 9001L /* 91% occupancy */
+#define HSHIFT (13 - 8)
 #ifdef LZW_COMPAT
 /* NB: +1024 is for compatibility with old files */
-#define CSIZE           (MAXCODE(BITS_MAX)+1024L)
+#define CSIZE (MAXCODE(BITS_MAX) + 1024L)
 #else
-#define CSIZE           (MAXCODE(BITS_MAX)+1L)
+#define CSIZE (MAXCODE(BITS_MAX) + 1L)
 #endif
 
 /*
@@ -86,725 +86,936 @@
  * compression/decompression.  Note that the predictor
  * state block must be first in this data structure.
  */
-typedef struct {
-	TIFFPredictorState predict;     /* predictor super class */
+typedef struct
+{
+    TIFFPredictorState predict; /* predictor super class */
 
-	unsigned short  nbits;          /* # of bits/code */
-	unsigned short  maxcode;        /* maximum code for lzw_nbits */
-	unsigned short  free_ent;       /* next free entry in hash table */
-	unsigned long   nextdata;       /* next bits of i/o */
-	long            nextbits;       /* # of valid bits in lzw_nextdata */
+    unsigned short nbits;    /* # of bits/code */
+    unsigned short maxcode;  /* maximum code for lzw_nbits */
+    unsigned short free_ent; /* next free entry in hash table */
+    WordType nextdata;       /* next bits of i/o */
+    long nextbits;           /* # of valid bits in lzw_nextdata */
 
-	int             rw_mode;        /* preserve rw_mode from init */
+    int rw_mode; /* preserve rw_mode from init */
 } LZWBaseState;
 
-#define lzw_nbits       base.nbits
-#define lzw_maxcode     base.maxcode
-#define lzw_free_ent    base.free_ent
-#define lzw_nextdata    base.nextdata
-#define lzw_nextbits    base.nextbits
+#define lzw_nbits base.nbits
+#define lzw_maxcode base.maxcode
+#define lzw_free_ent base.free_ent
+#define lzw_nextdata base.nextdata
+#define lzw_nextbits base.nextbits
 
 /*
  * Encoding-specific state.
  */
-typedef uint16 hcode_t;			/* codes fit in 16 bits */
-typedef struct {
-	long	hash;
-	hcode_t	code;
+typedef uint16_t hcode_t; /* codes fit in 16 bits */
+typedef struct
+{
+    long hash;
+    hcode_t code;
 } hash_t;
 
 /*
  * Decoding-specific state.
  */
-typedef struct code_ent {
-	struct code_ent *next;
-	unsigned short	length;		/* string len, including this token */
-	unsigned char	value;		/* data value */
-	unsigned char	firstchar;	/* first token of string */
+typedef struct code_ent
+{
+    struct code_ent *next;
+    unsigned short length; /* string len, including this token */
+    /* firstchar should be placed immediately before value in this structure */
+    unsigned char firstchar; /* first token of string */
+    unsigned char value;     /* data value */
+    bool repeated;
 } code_t;
 
-typedef int (*decodeFunc)(TIFF*, uint8*, tmsize_t, uint16);
+typedef int (*decodeFunc)(TIFF *, uint8_t *, tmsize_t, uint16_t);
 
-typedef struct {
-	LZWBaseState base;
+typedef struct
+{
+    LZWBaseState base;
 
-	/* Decoding specific data */
-	long    dec_nbitsmask;		/* lzw_nbits 1 bits, right adjusted */
-	long    dec_restart;		/* restart count */
-#ifdef LZW_CHECKEOS
-	uint64  dec_bitsleft;		/* available bits in raw data */
-	tmsize_t old_tif_rawcc;         /* value of tif_rawcc at the end of the previous TIFLZWDecode() call */
-#endif
-	decodeFunc dec_decode;		/* regular or backwards compatible */
-	code_t* dec_codep;		/* current recognized code */
-	code_t* dec_oldcodep;		/* previously recognized code */
-	code_t* dec_free_entp;		/* next free entry */
-	code_t* dec_maxcodep;		/* max available entry */
-	code_t* dec_codetab;		/* kept separate for small machines */
+    /* Decoding specific data */
+    long dec_nbitsmask;     /* lzw_nbits 1 bits, right adjusted */
+    tmsize_t dec_restart;   /* restart count */
+    uint64_t dec_bitsleft;  /* available bits in raw data */
+    tmsize_t old_tif_rawcc; /* value of tif_rawcc at the end of the previous
+                               TIFLZWDecode() call */
+    decodeFunc dec_decode;  /* regular or backwards compatible */
+    code_t *dec_codep;      /* current recognized code */
+    code_t *dec_oldcodep;   /* previously recognized code */
+    code_t *dec_free_entp;  /* next free entry */
+    code_t *dec_maxcodep;   /* max available entry */
+    code_t *dec_codetab;    /* kept separate for small machines */
+    int read_error; /* whether a read error has occurred, and which should cause
+                       further reads in the same strip/tile to be aborted */
 
-	/* Encoding specific data */
-	int     enc_oldcode;		/* last code encountered */
-	long    enc_checkpoint;		/* point at which to clear table */
-#define CHECK_GAP	10000		/* enc_ratio check interval */
-	long    enc_ratio;		/* current compression ratio */
-	long    enc_incount;		/* (input) data bytes encoded */
-	long    enc_outcount;		/* encoded (output) bytes */
-	uint8*  enc_rawlimit;		/* bound on tif_rawdata buffer */
-	hash_t* enc_hashtab;		/* kept separate for small machines */
+    /* Encoding specific data */
+    int enc_oldcode;         /* last code encountered */
+    tmsize_t enc_checkpoint; /* point at which to clear table */
+#define CHECK_GAP 10000      /* enc_ratio check interval */
+    tmsize_t enc_ratio;      /* current compression ratio */
+    tmsize_t enc_incount;    /* (input) data bytes encoded */
+    tmsize_t enc_outcount;   /* encoded (output) bytes */
+    uint8_t *enc_rawlimit;   /* bound on tif_rawdata buffer */
+    hash_t *enc_hashtab;     /* kept separate for small machines */
 } LZWCodecState;
 
-#define LZWState(tif)		((LZWBaseState*) (tif)->tif_data)
-#define DecoderState(tif)	((LZWCodecState*) LZWState(tif))
-#define EncoderState(tif)	((LZWCodecState*) LZWState(tif))
+#define LZWState(tif) ((LZWBaseState *)(tif)->tif_data)
+#define DecoderState(tif) ((LZWCodecState *)LZWState(tif))
+#define EncoderState(tif) ((LZWCodecState *)LZWState(tif))
 
-static int LZWDecode(TIFF* tif, uint8* op0, tmsize_t occ0, uint16 s);
+static int LZWDecode(TIFF *tif, uint8_t *op0, tmsize_t occ0, uint16_t s);
 #ifdef LZW_COMPAT
-static int LZWDecodeCompat(TIFF* tif, uint8* op0, tmsize_t occ0, uint16 s);
+static int LZWDecodeCompat(TIFF *tif, uint8_t *op0, tmsize_t occ0, uint16_t s);
 #endif
-static void cl_hash(LZWCodecState*);
+static void cl_hash(LZWCodecState *);
 
 /*
  * LZW Decoder.
  */
 
-#ifdef LZW_CHECKEOS
-/*
- * This check shouldn't be necessary because each
- * strip is suppose to be terminated with CODE_EOI.
- */
-#define	NextCode(_tif, _sp, _bp, _code, _get) {				\
-	if ((_sp)->dec_bitsleft < (uint64)nbits) {			\
-		TIFFWarningExt(_tif->tif_clientdata, module,		\
-		    "LZWDecode: Strip %d not terminated with EOI code", \
-		    _tif->tif_curstrip);				\
-		_code = CODE_EOI;					\
-	} else {							\
-		_get(_sp,_bp,_code);					\
-		(_sp)->dec_bitsleft -= nbits;				\
-	}								\
-}
-#else
-#define	NextCode(tif, sp, bp, code, get) get(sp, bp, code)
-#endif
-
-static int
-LZWFixupTags(TIFF* tif)
+static int LZWFixupTags(TIFF *tif)
 {
-	(void) tif;
-	return (1);
+    (void)tif;
+    return (1);
 }
 
-static int
-LZWSetupDecode(TIFF* tif)
+static int LZWSetupDecode(TIFF *tif)
 {
-	static const char module[] = "LZWSetupDecode";
-	LZWCodecState* sp = DecoderState(tif);
-	int code;
+    static const char module[] = "LZWSetupDecode";
+    LZWCodecState *sp = DecoderState(tif);
+    int code;
 
-	if( sp == NULL )
-	{
-		/*
-		 * Allocate state block so tag methods have storage to record
-		 * values.
-		*/
-		tif->tif_data = (uint8*) _TIFFmalloc(sizeof(LZWCodecState));
-		if (tif->tif_data == NULL)
-		{
-			TIFFErrorExt(tif->tif_clientdata, module, "No space for LZW state block");
-			return (0);
-		}
+    if (sp == NULL)
+    {
+        /*
+         * Allocate state block so tag methods have storage to record
+         * values.
+         */
+        tif->tif_data = (uint8_t *)_TIFFmallocExt(tif, sizeof(LZWCodecState));
+        if (tif->tif_data == NULL)
+        {
+            TIFFErrorExtR(tif, module, "No space for LZW state block");
+            return (0);
+        }
 
-		DecoderState(tif)->dec_codetab = NULL;
-		DecoderState(tif)->dec_decode = NULL;
+        sp = DecoderState(tif);
+        sp->dec_codetab = NULL;
+        sp->dec_decode = NULL;
 
-		/*
-		 * Setup predictor setup.
-		 */
-		(void) TIFFPredictorInit(tif);
+        /*
+         * Setup predictor setup.
+         */
+        (void)TIFFPredictorInit(tif);
+    }
 
-		sp = DecoderState(tif);
-	}
-
-	assert(sp != NULL);
-
-	if (sp->dec_codetab == NULL) {
-		sp->dec_codetab = (code_t*)_TIFFmalloc(CSIZE*sizeof (code_t));
-		if (sp->dec_codetab == NULL) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-				     "No space for LZW code table");
-			return (0);
-		}
-		/*
-		 * Pre-load the table.
-		 */
-		code = 255;
-		do {
-			sp->dec_codetab[code].value = (unsigned char)code;
-			sp->dec_codetab[code].firstchar = (unsigned char)code;
-			sp->dec_codetab[code].length = 1;
-			sp->dec_codetab[code].next = NULL;
-		} while (code--);
-		/*
-		 * Zero-out the unused entries
-                 */
-                /* Silence false positive */
-                /* coverity[overrun-buffer-arg] */
-                 _TIFFmemset(&sp->dec_codetab[CODE_CLEAR], 0,
-			     (CODE_FIRST - CODE_CLEAR) * sizeof (code_t));
-	}
-	return (1);
+    if (sp->dec_codetab == NULL)
+    {
+        sp->dec_codetab = (code_t *)_TIFFmallocExt(tif, CSIZE * sizeof(code_t));
+        if (sp->dec_codetab == NULL)
+        {
+            TIFFErrorExtR(tif, module, "No space for LZW code table");
+            return (0);
+        }
+        /*
+         * Pre-load the table.
+         */
+        code = 255;
+        do
+        {
+            sp->dec_codetab[code].firstchar = (unsigned char)code;
+            sp->dec_codetab[code].value = (unsigned char)code;
+            sp->dec_codetab[code].repeated = true;
+            sp->dec_codetab[code].length = 1;
+            sp->dec_codetab[code].next = NULL;
+        } while (code--);
+        /*
+         * Zero-out the unused entries  */
+        /* Silence false positive */
+        /* coverity[overrun-buffer-arg] */
+        memset(&sp->dec_codetab[CODE_CLEAR], 0,
+               (CODE_FIRST - CODE_CLEAR) * sizeof(code_t));
+    }
+    return (1);
 }
 
 /*
  * Setup state for decoding a strip.
  */
-static int
-LZWPreDecode(TIFF* tif, uint16 s)
+static int LZWPreDecode(TIFF *tif, uint16_t s)
 {
-	static const char module[] = "LZWPreDecode";
-	LZWCodecState *sp = DecoderState(tif);
+    static const char module[] = "LZWPreDecode";
+    LZWCodecState *sp = DecoderState(tif);
 
-	(void) s;
-	assert(sp != NULL);
-	if( sp->dec_codetab == NULL )
-        {
-            tif->tif_setupdecode( tif );
-	    if( sp->dec_codetab == NULL )
-		return (0);
-        }
+    (void)s;
+    assert(sp != NULL);
+    if (sp->dec_codetab == NULL)
+    {
+        tif->tif_setupdecode(tif);
+        if (sp->dec_codetab == NULL)
+            return (0);
+    }
 
-	/*
-	 * Check for old bit-reversed codes.
-	 */
-	if (tif->tif_rawcc >= 2 &&
-	    tif->tif_rawdata[0] == 0 && (tif->tif_rawdata[1] & 0x1)) {
+    /*
+     * Check for old bit-reversed codes.
+     */
+    if (tif->tif_rawcc >= 2 && tif->tif_rawdata[0] == 0 &&
+        (tif->tif_rawdata[1] & 0x1))
+    {
 #ifdef LZW_COMPAT
-		if (!sp->dec_decode) {
-			TIFFWarningExt(tif->tif_clientdata, module,
-			    "Old-style LZW codes, convert file");
-			/*
-			 * Override default decoding methods with
-			 * ones that deal with the old coding.
-			 * Otherwise the predictor versions set
-			 * above will call the compatibility routines
-			 * through the dec_decode method.
-			 */
-			tif->tif_decoderow = LZWDecodeCompat;
-			tif->tif_decodestrip = LZWDecodeCompat;
-			tif->tif_decodetile = LZWDecodeCompat;
-			/*
-			 * If doing horizontal differencing, must
-			 * re-setup the predictor logic since we
-			 * switched the basic decoder methods...
-			 */
-			(*tif->tif_setupdecode)(tif);
-			sp->dec_decode = LZWDecodeCompat;
-		}
-		sp->lzw_maxcode = MAXCODE(BITS_MIN);
-#else /* !LZW_COMPAT */
-		if (!sp->dec_decode) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Old-style LZW codes not supported");
-			sp->dec_decode = LZWDecode;
-		}
-		return (0);
-#endif/* !LZW_COMPAT */
-	} else {
-		sp->lzw_maxcode = MAXCODE(BITS_MIN)-1;
-		sp->dec_decode = LZWDecode;
-	}
-	sp->lzw_nbits = BITS_MIN;
-	sp->lzw_nextbits = 0;
-	sp->lzw_nextdata = 0;
+        if (!sp->dec_decode)
+        {
+            TIFFWarningExtR(tif, module, "Old-style LZW codes, convert file");
+            /*
+             * Override default decoding methods with
+             * ones that deal with the old coding.
+             * Otherwise the predictor versions set
+             * above will call the compatibility routines
+             * through the dec_decode method.
+             */
+            tif->tif_decoderow = LZWDecodeCompat;
+            tif->tif_decodestrip = LZWDecodeCompat;
+            tif->tif_decodetile = LZWDecodeCompat;
+            /*
+             * If doing horizontal differencing, must
+             * re-setup the predictor logic since we
+             * switched the basic decoder methods...
+             */
+            (*tif->tif_setupdecode)(tif);
+            sp->dec_decode = LZWDecodeCompat;
+        }
+        sp->lzw_maxcode = MAXCODE(BITS_MIN);
+#else  /* !LZW_COMPAT */
+        if (!sp->dec_decode)
+        {
+            TIFFErrorExtR(tif, module, "Old-style LZW codes not supported");
+            sp->dec_decode = LZWDecode;
+        }
+        return (0);
+#endif /* !LZW_COMPAT */
+    }
+    else
+    {
+        sp->lzw_maxcode = MAXCODE(BITS_MIN) - 1;
+        sp->dec_decode = LZWDecode;
+    }
+    sp->lzw_nbits = BITS_MIN;
+    sp->lzw_nextbits = 0;
+    sp->lzw_nextdata = 0;
 
-	sp->dec_restart = 0;
-	sp->dec_nbitsmask = MAXCODE(BITS_MIN);
-#ifdef LZW_CHECKEOS
-	sp->dec_bitsleft = 0;
-        sp->old_tif_rawcc = 0;
-#endif
-	sp->dec_free_entp = sp->dec_codetab + CODE_FIRST;
-	/*
-	 * Zero entries that are not yet filled in.  We do
-	 * this to guard against bogus input data that causes
-	 * us to index into undefined entries.  If you can
-	 * come up with a way to safely bounds-check input codes
-	 * while decoding then you can remove this operation.
-	 */
-	_TIFFmemset(sp->dec_free_entp, 0, (CSIZE-CODE_FIRST)*sizeof (code_t));
-	sp->dec_oldcodep = &sp->dec_codetab[-1];
-	sp->dec_maxcodep = &sp->dec_codetab[sp->dec_nbitsmask-1];
-	return (1);
+    sp->dec_restart = 0;
+    sp->dec_nbitsmask = MAXCODE(BITS_MIN);
+    sp->dec_bitsleft = 0;
+    sp->old_tif_rawcc = 0;
+    sp->dec_free_entp = sp->dec_codetab - 1; // + CODE_FIRST;
+    /*
+     * Zero entries that are not yet filled in.  We do
+     * this to guard against bogus input data that causes
+     * us to index into undefined entries.  If you can
+     * come up with a way to safely bounds-check input codes
+     * while decoding then you can remove this operation.
+     */
+    sp->dec_oldcodep = &sp->dec_codetab[0];
+    sp->dec_maxcodep = &sp->dec_codetab[sp->dec_nbitsmask - 1];
+    sp->read_error = 0;
+    return (1);
 }
 
 /*
  * Decode a "hunk of data".
  */
-#define	GetNextCode(sp, bp, code) {				\
-	nextdata = (nextdata<<8) | *(bp)++;			\
-	nextbits += 8;						\
-	if (nextbits < nbits) {					\
-		nextdata = (nextdata<<8) | *(bp)++;		\
-		nextbits += 8;					\
-	}							\
-	code = (hcode_t)((nextdata >> (nextbits-nbits)) & nbitsmask);	\
-	nextbits -= nbits;					\
-}
 
-static void
-codeLoop(TIFF* tif, const char* module)
-{
-	TIFFErrorExt(tif->tif_clientdata, module,
-	    "Bogus encoding, loop in the code table; scanline %d",
-	    tif->tif_row);
-}
-
-static int
-LZWDecode(TIFF* tif, uint8* op0, tmsize_t occ0, uint16 s)
-{
-	static const char module[] = "LZWDecode";
-	LZWCodecState *sp = DecoderState(tif);
-	char *op = (char*) op0;
-	long occ = (long) occ0;
-	char *tp;
-	unsigned char *bp;
-	hcode_t code;
-	int len;
-	long nbits, nextbits, nbitsmask;
-        unsigned long nextdata;
-	code_t *codep, *free_entp, *maxcodep, *oldcodep;
-
-	(void) s;
-	assert(sp != NULL);
-        assert(sp->dec_codetab != NULL);
-
-	/*
-	  Fail if value does not fit in long.
-	*/
-	if ((tmsize_t) occ != occ0)
-	        return (0);
-	/*
-	 * Restart interrupted output operation.
-	 */
-	if (sp->dec_restart) {
-		long residue;
-
-		codep = sp->dec_codep;
-		residue = codep->length - sp->dec_restart;
-		if (residue > occ) {
-			/*
-			 * Residue from previous decode is sufficient
-			 * to satisfy decode request.  Skip to the
-			 * start of the decoded string, place decoded
-			 * values in the output buffer, and return.
-			 */
-			sp->dec_restart += occ;
-			do {
-				codep = codep->next;
-			} while (--residue > occ && codep);
-			if (codep) {
-				tp = op + occ;
-				do {
-					*--tp = codep->value;
-					codep = codep->next;
-				} while (--occ && codep);
-			}
-			return (1);
-		}
-		/*
-		 * Residue satisfies only part of the decode request.
-		 */
-		op += residue;
-		occ -= residue;
-		tp = op;
-		do {
-			int t;
-			--tp;
-			t = codep->value;
-			codep = codep->next;
-			*tp = (char)t;
-		} while (--residue && codep);
-		sp->dec_restart = 0;
-	}
-
-	bp = (unsigned char *)tif->tif_rawcp;
-#ifdef LZW_CHECKEOS
-	sp->dec_bitsleft += (((uint64)tif->tif_rawcc - sp->old_tif_rawcc) << 3);
-#endif
-	nbits = sp->lzw_nbits;
-	nextdata = sp->lzw_nextdata;
-	nextbits = sp->lzw_nextbits;
-	nbitsmask = sp->dec_nbitsmask;
-	oldcodep = sp->dec_oldcodep;
-	free_entp = sp->dec_free_entp;
-	maxcodep = sp->dec_maxcodep;
-
-	while (occ > 0) {
-		NextCode(tif, sp, bp, code, GetNextCode);
-		if (code == CODE_EOI)
-			break;
-		if (code == CODE_CLEAR) {
-			do {
-				free_entp = sp->dec_codetab + CODE_FIRST;
-				_TIFFmemset(free_entp, 0,
-					    (CSIZE - CODE_FIRST) * sizeof (code_t));
-				nbits = BITS_MIN;
-				nbitsmask = MAXCODE(BITS_MIN);
-				maxcodep = sp->dec_codetab + nbitsmask-1;
-				NextCode(tif, sp, bp, code, GetNextCode);
-			} while (code == CODE_CLEAR);	/* consecutive CODE_CLEAR codes */
-			if (code == CODE_EOI)
-				break;
-			if (code > CODE_CLEAR) {
-				TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-				"LZWDecode: Corrupted LZW table at scanline %d",
-					     tif->tif_row);
-				return (0);
-			}
-			*op++ = (char)code;
-			occ--;
-			oldcodep = sp->dec_codetab + code;
-			continue;
-		}
-		codep = sp->dec_codetab + code;
-
-		/*
-		 * Add the new entry to the code table.
-		 */
-		if (free_entp < &sp->dec_codetab[0] ||
-		    free_entp >= &sp->dec_codetab[CSIZE]) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Corrupted LZW table at scanline %d",
-			    tif->tif_row);
-			return (0);
-		}
-
-		free_entp->next = oldcodep;
-		if (free_entp->next < &sp->dec_codetab[0] ||
-		    free_entp->next >= &sp->dec_codetab[CSIZE]) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Corrupted LZW table at scanline %d",
-			    tif->tif_row);
-			return (0);
-		}
-		free_entp->firstchar = free_entp->next->firstchar;
-		free_entp->length = free_entp->next->length+1;
-		free_entp->value = (codep < free_entp) ?
-		    codep->firstchar : free_entp->firstchar;
-		if (++free_entp > maxcodep) {
-			if (++nbits > BITS_MAX)		/* should not happen */
-				nbits = BITS_MAX;
-			nbitsmask = MAXCODE(nbits);
-			maxcodep = sp->dec_codetab + nbitsmask-1;
-		}
-		oldcodep = codep;
-		if (code >= 256) {
-			/*
-			 * Code maps to a string, copy string
-			 * value to output (written in reverse).
-			 */
-			if(codep->length == 0) {
-				TIFFErrorExt(tif->tif_clientdata, module,
-				    "Wrong length of decoded string: "
-				    "data probably corrupted at scanline %d",
-				    tif->tif_row);
-				return (0);
-			}
-			if (codep->length > occ) {
-				/*
-				 * String is too long for decode buffer,
-				 * locate portion that will fit, copy to
-				 * the decode buffer, and setup restart
-				 * logic for the next decoding call.
-				 */
-				sp->dec_codep = codep;
-				do {
-					codep = codep->next;
-				} while (codep && codep->length > occ);
-				if (codep) {
-					sp->dec_restart = (long)occ;
-					tp = op + occ;
-					do  {
-						*--tp = codep->value;
-						codep = codep->next;
-					}  while (--occ && codep);
-					if (codep)
-						codeLoop(tif, module);
-				}
-				break;
-			}
-			len = codep->length;
-			tp = op + len;
-			do {
-				int t;
-				--tp;
-				t = codep->value;
-				codep = codep->next;
-				*tp = (char)t;
-			} while (codep && tp > op);
-			if (codep) {
-			    codeLoop(tif, module);
-			    break;
-			}
-			assert(occ >= len);
-			op += len;
-			occ -= len;
-		} else {
-			*op++ = (char)code;
-			occ--;
-		}
-	}
-
-	tif->tif_rawcc -= (tmsize_t)( (uint8*) bp - tif->tif_rawcp );
-	tif->tif_rawcp = (uint8*) bp;
-#ifdef LZW_CHECKEOS
-	sp->old_tif_rawcc = tif->tif_rawcc;
-#endif
-	sp->lzw_nbits = (unsigned short) nbits;
-	sp->lzw_nextdata = nextdata;
-	sp->lzw_nextbits = nextbits;
-	sp->dec_nbitsmask = nbitsmask;
-	sp->dec_oldcodep = oldcodep;
-	sp->dec_free_entp = free_entp;
-	sp->dec_maxcodep = maxcodep;
-
-	if (occ > 0) {
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-		TIFFErrorExt(tif->tif_clientdata, module,
-			"Not enough data at scanline %d (short %I64d bytes)",
-			     tif->tif_row, (unsigned __int64) occ);
+/* Get the next 32 or 64-bit from the input data */
+#ifdef WORDS_BIGENDIAN
+#define GetNextData(nextdata, bp) memcpy(&nextdata, bp, sizeof(nextdata))
+#elif SIZEOF_WORDTYPE == 8
+#if defined(__GNUC__) && defined(__x86_64__)
+#define GetNextData(nextdata, bp)                                              \
+    nextdata = __builtin_bswap64(*(uint64_t *)(bp))
+#elif defined(_M_X64)
+#define GetNextData(nextdata, bp) nextdata = _byteswap_uint64(*(uint64_t *)(bp))
+#elif defined(__GNUC__)
+#define GetNextData(nextdata, bp)                                              \
+    memcpy(&nextdata, bp, sizeof(nextdata));                                   \
+    nextdata = __builtin_bswap64(nextdata)
 #else
-		TIFFErrorExt(tif->tif_clientdata, module,
-			"Not enough data at scanline %d (short %llu bytes)",
-			     tif->tif_row, (unsigned long long) occ);
+#define GetNextData(nextdata, bp)                                              \
+    nextdata = (((uint64_t)bp[0]) << 56) | (((uint64_t)bp[1]) << 48) |         \
+               (((uint64_t)bp[2]) << 40) | (((uint64_t)bp[3]) << 32) |         \
+               (((uint64_t)bp[4]) << 24) | (((uint64_t)bp[5]) << 16) |         \
+               (((uint64_t)bp[6]) << 8) | (((uint64_t)bp[7]))
 #endif
-		return (0);
-	}
-	return (1);
+#elif SIZEOF_WORDTYPE == 4
+#if defined(__GNUC__) && defined(__i386__)
+#define GetNextData(nextdata, bp)                                              \
+    nextdata = __builtin_bswap32(*(uint32_t *)(bp))
+#elif defined(_M_X86)
+#define GetNextData(nextdata, bp)                                              \
+    nextdata = _byteswap_ulong(*(unsigned long *)(bp))
+#elif defined(__GNUC__)
+#define GetNextData(nextdata, bp)                                              \
+    memcpy(&nextdata, bp, sizeof(nextdata));                                   \
+    nextdata = __builtin_bswap32(nextdata)
+#else
+#define GetNextData(nextdata, bp)                                              \
+    nextdata = (((uint32_t)bp[0]) << 24) | (((uint32_t)bp[1]) << 16) |         \
+               (((uint32_t)bp[2]) << 8) | (((uint32_t)bp[3]))
+#endif
+#else
+#error "Unhandled SIZEOF_WORDTYPE"
+#endif
+
+#define GetNextCodeLZW()                                                       \
+    do                                                                         \
+    {                                                                          \
+        nextbits -= nbits;                                                     \
+        if (nextbits < 0)                                                      \
+        {                                                                      \
+            if (dec_bitsleft >= 8 * SIZEOF_WORDTYPE)                           \
+            {                                                                  \
+                unsigned codetmp = (unsigned)(nextdata << (-nextbits));        \
+                GetNextData(nextdata, bp);                                     \
+                bp += SIZEOF_WORDTYPE;                                         \
+                nextbits += 8 * SIZEOF_WORDTYPE;                               \
+                dec_bitsleft -= 8 * SIZEOF_WORDTYPE;                           \
+                code = (WordType)((codetmp | (nextdata >> nextbits)) &         \
+                                  nbitsmask);                                  \
+                break;                                                         \
+            }                                                                  \
+            else                                                               \
+            {                                                                  \
+                if (dec_bitsleft < 8)                                          \
+                {                                                              \
+                    goto no_eoi;                                               \
+                }                                                              \
+                nextdata = (nextdata << 8) | *(bp)++;                          \
+                nextbits += 8;                                                 \
+                dec_bitsleft -= 8;                                             \
+                if (nextbits < 0)                                              \
+                {                                                              \
+                    if (dec_bitsleft < 8)                                      \
+                    {                                                          \
+                        goto no_eoi;                                           \
+                    }                                                          \
+                    nextdata = (nextdata << 8) | *(bp)++;                      \
+                    nextbits += 8;                                             \
+                    dec_bitsleft -= 8;                                         \
+                }                                                              \
+            }                                                                  \
+        }                                                                      \
+        code = (WordType)((nextdata >> nextbits) & nbitsmask);                 \
+    } while (0)
+
+static int LZWDecode(TIFF *tif, uint8_t *op0, tmsize_t occ0, uint16_t s)
+{
+    static const char module[] = "LZWDecode";
+    LZWCodecState *sp = DecoderState(tif);
+    uint8_t *op = (uint8_t *)op0;
+    tmsize_t occ = occ0;
+    uint8_t *bp;
+    long nbits, nextbits, nbitsmask;
+    WordType nextdata;
+    code_t *free_entp, *maxcodep, *oldcodep;
+
+    (void)s;
+    assert(sp != NULL);
+    assert(sp->dec_codetab != NULL);
+
+    if (sp->read_error)
+    {
+        TIFFErrorExtR(tif, module,
+                      "LZWDecode: Scanline %" PRIu32 " cannot be read due to "
+                      "previous error",
+                      tif->tif_row);
+        return 0;
+    }
+
+    /*
+     * Restart interrupted output operation.
+     */
+    if (sp->dec_restart)
+    {
+        tmsize_t residue;
+
+        code_t *codep = sp->dec_codep;
+        residue = codep->length - sp->dec_restart;
+        if (residue > occ)
+        {
+            /*
+             * Residue from previous decode is sufficient
+             * to satisfy decode request.  Skip to the
+             * start of the decoded string, place decoded
+             * values in the output buffer, and return.
+             */
+            sp->dec_restart += occ;
+            do
+            {
+                codep = codep->next;
+            } while (--residue > occ && codep);
+            if (codep)
+            {
+                uint8_t *tp = op + occ;
+                do
+                {
+                    *--tp = codep->value;
+                    codep = codep->next;
+                } while (--occ && codep);
+            }
+            return (1);
+        }
+        /*
+         * Residue satisfies only part of the decode request.
+         */
+        op += residue;
+        occ -= residue;
+        uint8_t *tp = op;
+        do
+        {
+            *--tp = codep->value;
+            codep = codep->next;
+        } while (--residue && codep);
+        sp->dec_restart = 0;
+    }
+
+    bp = (uint8_t *)tif->tif_rawcp;
+    sp->dec_bitsleft += (((uint64_t)tif->tif_rawcc - sp->old_tif_rawcc) << 3);
+    uint64_t dec_bitsleft = sp->dec_bitsleft;
+    nbits = sp->lzw_nbits;
+    nextdata = sp->lzw_nextdata;
+    nextbits = sp->lzw_nextbits;
+    nbitsmask = sp->dec_nbitsmask;
+    oldcodep = sp->dec_oldcodep;
+    free_entp = sp->dec_free_entp;
+    maxcodep = sp->dec_maxcodep;
+    code_t *const dec_codetab = sp->dec_codetab;
+    code_t *codep;
+
+    if (occ == 0)
+    {
+        goto after_loop;
+    }
+
+begin:
+{
+    WordType code;
+    GetNextCodeLZW();
+    codep = dec_codetab + code;
+    if (code >= CODE_FIRST)
+        goto code_above_or_equal_to_258;
+    if (code < 256)
+        goto code_below_256;
+    if (code == CODE_EOI)
+        goto after_loop;
+    goto code_clear;
+
+code_below_256:
+{
+    if (codep > free_entp)
+        goto error_code;
+    free_entp->next = oldcodep;
+    free_entp->firstchar = oldcodep->firstchar;
+    free_entp->length = oldcodep->length + 1;
+    free_entp->value = (uint8_t)code;
+    free_entp->repeated =
+        (bool)(oldcodep->repeated & (oldcodep->value == code));
+    if (++free_entp > maxcodep)
+    {
+        if (++nbits > BITS_MAX) /* should not happen for a conformant encoder */
+            nbits = BITS_MAX;
+        nbitsmask = MAXCODE(nbits);
+        maxcodep = dec_codetab + nbitsmask - 1;
+        if (free_entp >= &dec_codetab[CSIZE])
+        {
+            /* At that point, the next valid states are either EOI or a */
+            /* CODE_CLEAR. If a regular code is read, at the next */
+            /* attempt at registering a new entry, we will error out */
+            /* due to setting free_entp before any valid code */
+            free_entp = dec_codetab - 1;
+        }
+    }
+    oldcodep = codep;
+    *op++ = (uint8_t)code;
+    occ--;
+    if (occ == 0)
+        goto after_loop;
+    goto begin;
+}
+
+code_above_or_equal_to_258:
+{
+    /*
+     * Add the new entry to the code table.
+     */
+
+    if (codep >= free_entp)
+    {
+        if (codep != free_entp)
+            goto error_code;
+        free_entp->value = oldcodep->firstchar;
+    }
+    else
+    {
+        free_entp->value = codep->firstchar;
+    }
+    free_entp->repeated =
+        (bool)(oldcodep->repeated & (oldcodep->value == free_entp->value));
+    free_entp->next = oldcodep;
+
+    free_entp->firstchar = oldcodep->firstchar;
+    free_entp->length = oldcodep->length + 1;
+    if (++free_entp > maxcodep)
+    {
+        if (++nbits > BITS_MAX) /* should not happen for a conformant encoder */
+            nbits = BITS_MAX;
+        nbitsmask = MAXCODE(nbits);
+        maxcodep = dec_codetab + nbitsmask - 1;
+        if (free_entp >= &dec_codetab[CSIZE])
+        {
+            /* At that point, the next valid states are either EOI or a */
+            /* CODE_CLEAR. If a regular code is read, at the next */
+            /* attempt at registering a new entry, we will error out */
+            /* due to setting free_entp before any valid code */
+            free_entp = dec_codetab - 1;
+        }
+    }
+    oldcodep = codep;
+
+    /*
+     * Code maps to a string, copy string
+     * value to output (written in reverse).
+     */
+    /* tiny bit faster on x86_64 to store in unsigned short than int */
+    unsigned short len = codep->length;
+
+    if (len < 3) /* equivalent to len == 2 given all other conditions */
+    {
+        if (occ <= 2)
+        {
+            if (occ == 2)
+            {
+                memcpy(op, &(codep->firstchar), 2);
+                op += 2;
+                occ -= 2;
+                goto after_loop;
+            }
+            goto too_short_buffer;
+        }
+
+        memcpy(op, &(codep->firstchar), 2);
+        op += 2;
+        occ -= 2;
+        goto begin; /* we can save the comparison occ > 0 */
+    }
+
+    if (len == 3)
+    {
+        if (occ <= 3)
+        {
+            if (occ == 3)
+            {
+                op[0] = codep->firstchar;
+                op[1] = codep->next->value;
+                op[2] = codep->value;
+                op += 3;
+                occ -= 3;
+                goto after_loop;
+            }
+            goto too_short_buffer;
+        }
+
+        op[0] = codep->firstchar;
+        op[1] = codep->next->value;
+        op[2] = codep->value;
+        op += 3;
+        occ -= 3;
+        goto begin; /* we can save the comparison occ > 0 */
+    }
+
+    if (len > occ)
+    {
+        goto too_short_buffer;
+    }
+
+    if (codep->repeated)
+    {
+        memset(op, codep->value, len);
+        op += len;
+        occ -= len;
+        if (occ == 0)
+            goto after_loop;
+        goto begin;
+    }
+
+    uint8_t *tp = op + len;
+
+    assert(len >= 4);
+
+    *--tp = codep->value;
+    codep = codep->next;
+    *--tp = codep->value;
+    codep = codep->next;
+    *--tp = codep->value;
+    codep = codep->next;
+    *--tp = codep->value;
+    if (tp > op)
+    {
+        do
+        {
+            codep = codep->next;
+            *--tp = codep->value;
+        } while (tp > op);
+    }
+
+    assert(occ >= len);
+    op += len;
+    occ -= len;
+    if (occ == 0)
+        goto after_loop;
+    goto begin;
+}
+
+code_clear:
+{
+    free_entp = dec_codetab + CODE_FIRST;
+    nbits = BITS_MIN;
+    nbitsmask = MAXCODE(BITS_MIN);
+    maxcodep = dec_codetab + nbitsmask - 1;
+    do
+    {
+        GetNextCodeLZW();
+    } while (code == CODE_CLEAR); /* consecutive CODE_CLEAR codes */
+    if (code == CODE_EOI)
+        goto after_loop;
+    if (code > CODE_EOI)
+    {
+        goto error_code;
+    }
+    *op++ = (uint8_t)code;
+    occ--;
+    oldcodep = dec_codetab + code;
+    if (occ == 0)
+        goto after_loop;
+    goto begin;
+}
+}
+
+too_short_buffer:
+{
+    /*
+     * String is too long for decode buffer,
+     * locate portion that will fit, copy to
+     * the decode buffer, and setup restart
+     * logic for the next decoding call.
+     */
+    sp->dec_codep = codep;
+    do
+    {
+        codep = codep->next;
+    } while (codep->length > occ);
+
+    sp->dec_restart = occ;
+    uint8_t *tp = op + occ;
+    do
+    {
+        *--tp = codep->value;
+        codep = codep->next;
+    } while (--occ);
+}
+
+after_loop:
+    tif->tif_rawcc -= (tmsize_t)((uint8_t *)bp - tif->tif_rawcp);
+    tif->tif_rawcp = (uint8_t *)bp;
+    sp->old_tif_rawcc = tif->tif_rawcc;
+    sp->dec_bitsleft = dec_bitsleft;
+    sp->lzw_nbits = (unsigned short)nbits;
+    sp->lzw_nextdata = nextdata;
+    sp->lzw_nextbits = nextbits;
+    sp->dec_nbitsmask = nbitsmask;
+    sp->dec_oldcodep = oldcodep;
+    sp->dec_free_entp = free_entp;
+    sp->dec_maxcodep = maxcodep;
+
+    if (occ > 0)
+    {
+        TIFFErrorExtR(tif, module,
+                      "Not enough data at scanline %" PRIu32 " (short %" PRIu64
+                      " bytes)",
+                      tif->tif_row, (uint64_t)occ);
+        return (0);
+    }
+    return (1);
+
+no_eoi:
+    sp->read_error = 1;
+    TIFFErrorExtR(tif, module,
+                  "LZWDecode: Strip %" PRIu32 " not terminated with EOI code",
+                  tif->tif_curstrip);
+    return 0;
+error_code:
+    sp->read_error = 1;
+    TIFFErrorExtR(tif, tif->tif_name, "Using code not yet in table");
+    return 0;
 }
 
 #ifdef LZW_COMPAT
+
+/*
+ * This check shouldn't be necessary because each
+ * strip is suppose to be terminated with CODE_EOI.
+ */
+#define NextCode(_tif, _sp, _bp, _code, _get, dec_bitsleft)                    \
+    {                                                                          \
+        if (dec_bitsleft < (uint64_t)nbits)                                    \
+        {                                                                      \
+            TIFFWarningExtR(_tif, module,                                      \
+                            "LZWDecode: Strip %" PRIu32                        \
+                            " not terminated with EOI code",                   \
+                            _tif->tif_curstrip);                               \
+            _code = CODE_EOI;                                                  \
+        }                                                                      \
+        else                                                                   \
+        {                                                                      \
+            _get(_sp, _bp, _code);                                             \
+            dec_bitsleft -= nbits;                                             \
+        }                                                                      \
+    }
+
 /*
  * Decode a "hunk of data" for old images.
  */
-#define	GetNextCodeCompat(sp, bp, code) {			\
-	nextdata |= (unsigned long) *(bp)++ << nextbits;	\
-	nextbits += 8;						\
-	if (nextbits < nbits) {					\
-		nextdata |= (unsigned long) *(bp)++ << nextbits;\
-		nextbits += 8;					\
-	}							\
-	code = (hcode_t)(nextdata & nbitsmask);			\
-	nextdata >>= nbits;					\
-	nextbits -= nbits;					\
-}
+#define GetNextCodeCompat(sp, bp, code)                                        \
+    {                                                                          \
+        nextdata |= (unsigned long)*(bp)++ << nextbits;                        \
+        nextbits += 8;                                                         \
+        if (nextbits < nbits)                                                  \
+        {                                                                      \
+            nextdata |= (unsigned long)*(bp)++ << nextbits;                    \
+            nextbits += 8;                                                     \
+        }                                                                      \
+        code = (hcode_t)(nextdata & nbitsmask);                                \
+        nextdata >>= nbits;                                                    \
+        nextbits -= nbits;                                                     \
+    }
 
-static int
-LZWDecodeCompat(TIFF* tif, uint8* op0, tmsize_t occ0, uint16 s)
+static int LZWDecodeCompat(TIFF *tif, uint8_t *op0, tmsize_t occ0, uint16_t s)
 {
-	static const char module[] = "LZWDecodeCompat";
-	LZWCodecState *sp = DecoderState(tif);
-	char *op = (char*) op0;
-	long occ = (long) occ0;
-	char *tp;
-	unsigned char *bp;
-	int code, nbits;
-	int len;
-	long nextbits, nextdata, nbitsmask;
-	code_t *codep, *free_entp, *maxcodep, *oldcodep;
+    static const char module[] = "LZWDecodeCompat";
+    LZWCodecState *sp = DecoderState(tif);
+    uint8_t *op = (uint8_t *)op0;
+    tmsize_t occ = occ0;
+    uint8_t *tp;
+    uint8_t *bp;
+    int code, nbits;
+    int len;
+    long nextbits, nbitsmask;
+    WordType nextdata;
+    code_t *codep, *free_entp, *maxcodep, *oldcodep;
 
-	(void) s;
-	assert(sp != NULL);
+    (void)s;
+    assert(sp != NULL);
 
-	/*
-	  Fail if value does not fit in long.
-	*/
-	if ((tmsize_t) occ != occ0)
-	        return (0);
+    /*
+     * Restart interrupted output operation.
+     */
+    if (sp->dec_restart)
+    {
+        tmsize_t residue;
 
-	/*
-	 * Restart interrupted output operation.
-	 */
-	if (sp->dec_restart) {
-		long residue;
+        codep = sp->dec_codep;
+        residue = codep->length - sp->dec_restart;
+        if (residue > occ)
+        {
+            /*
+             * Residue from previous decode is sufficient
+             * to satisfy decode request.  Skip to the
+             * start of the decoded string, place decoded
+             * values in the output buffer, and return.
+             */
+            sp->dec_restart += occ;
+            do
+            {
+                codep = codep->next;
+            } while (--residue > occ);
+            tp = op + occ;
+            do
+            {
+                *--tp = codep->value;
+                codep = codep->next;
+            } while (--occ);
+            return (1);
+        }
+        /*
+         * Residue satisfies only part of the decode request.
+         */
+        op += residue;
+        occ -= residue;
+        tp = op;
+        do
+        {
+            *--tp = codep->value;
+            codep = codep->next;
+        } while (--residue);
+        sp->dec_restart = 0;
+    }
 
-		codep = sp->dec_codep;
-		residue = codep->length - sp->dec_restart;
-		if (residue > occ) {
-			/*
-			 * Residue from previous decode is sufficient
-			 * to satisfy decode request.  Skip to the
-			 * start of the decoded string, place decoded
-			 * values in the output buffer, and return.
-			 */
-			sp->dec_restart += occ;
-			do {
-				codep = codep->next;
-			} while (--residue > occ);
-			tp = op + occ;
-			do {
-				*--tp = codep->value;
-				codep = codep->next;
-			} while (--occ);
-			return (1);
-		}
-		/*
-		 * Residue satisfies only part of the decode request.
-		 */
-		op += residue;
-		occ -= residue;
-		tp = op;
-		do {
-			*--tp = codep->value;
-			codep = codep->next;
-		} while (--residue);
-		sp->dec_restart = 0;
-	}
+    bp = (uint8_t *)tif->tif_rawcp;
 
-	bp = (unsigned char *)tif->tif_rawcp;
-#ifdef LZW_CHECKEOS
-	sp->dec_bitsleft += (((uint64)tif->tif_rawcc - sp->old_tif_rawcc) << 3);
-#endif
-	nbits = sp->lzw_nbits;
-	nextdata = sp->lzw_nextdata;
-	nextbits = sp->lzw_nextbits;
-	nbitsmask = sp->dec_nbitsmask;
-	oldcodep = sp->dec_oldcodep;
-	free_entp = sp->dec_free_entp;
-	maxcodep = sp->dec_maxcodep;
+    sp->dec_bitsleft += (((uint64_t)tif->tif_rawcc - sp->old_tif_rawcc) << 3);
+    uint64_t dec_bitsleft = sp->dec_bitsleft;
 
-	while (occ > 0) {
-		NextCode(tif, sp, bp, code, GetNextCodeCompat);
-		if (code == CODE_EOI)
-			break;
-		if (code == CODE_CLEAR) {
-			do {
-				free_entp = sp->dec_codetab + CODE_FIRST;
-				_TIFFmemset(free_entp, 0,
-					    (CSIZE - CODE_FIRST) * sizeof (code_t));
-				nbits = BITS_MIN;
-				nbitsmask = MAXCODE(BITS_MIN);
-				maxcodep = sp->dec_codetab + nbitsmask;
-				NextCode(tif, sp, bp, code, GetNextCodeCompat);
-			} while (code == CODE_CLEAR);	/* consecutive CODE_CLEAR codes */
-			if (code == CODE_EOI)
-				break;
-			if (code > CODE_CLEAR) {
-				TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-				"LZWDecode: Corrupted LZW table at scanline %d",
-					     tif->tif_row);
-				return (0);
-			}
-			*op++ = (char)code;
-			occ--;
-			oldcodep = sp->dec_codetab + code;
-			continue;
-		}
-		codep = sp->dec_codetab + code;
+    nbits = sp->lzw_nbits;
+    nextdata = sp->lzw_nextdata;
+    nextbits = sp->lzw_nextbits;
+    nbitsmask = sp->dec_nbitsmask;
+    oldcodep = sp->dec_oldcodep;
+    free_entp = sp->dec_free_entp;
+    maxcodep = sp->dec_maxcodep;
 
-		/*
-		 * Add the new entry to the code table.
-		 */
-		if (free_entp < &sp->dec_codetab[0] ||
-		    free_entp >= &sp->dec_codetab[CSIZE]) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Corrupted LZW table at scanline %d", tif->tif_row);
-			return (0);
-		}
+    while (occ > 0)
+    {
+        NextCode(tif, sp, bp, code, GetNextCodeCompat, dec_bitsleft);
+        if (code == CODE_EOI)
+            break;
+        if (code == CODE_CLEAR)
+        {
+            do
+            {
+                free_entp = sp->dec_codetab + CODE_FIRST;
+                _TIFFmemset(free_entp, 0,
+                            (CSIZE - CODE_FIRST) * sizeof(code_t));
+                nbits = BITS_MIN;
+                nbitsmask = MAXCODE(BITS_MIN);
+                maxcodep = sp->dec_codetab + nbitsmask;
+                NextCode(tif, sp, bp, code, GetNextCodeCompat, dec_bitsleft);
+            } while (code == CODE_CLEAR); /* consecutive CODE_CLEAR codes */
+            if (code == CODE_EOI)
+                break;
+            if (code > CODE_CLEAR)
+            {
+                TIFFErrorExtR(
+                    tif, tif->tif_name,
+                    "LZWDecode: Corrupted LZW table at scanline %" PRIu32,
+                    tif->tif_row);
+                return (0);
+            }
+            *op++ = (uint8_t)code;
+            occ--;
+            oldcodep = sp->dec_codetab + code;
+            continue;
+        }
+        codep = sp->dec_codetab + code;
 
-		free_entp->next = oldcodep;
-		if (free_entp->next < &sp->dec_codetab[0] ||
-		    free_entp->next >= &sp->dec_codetab[CSIZE]) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Corrupted LZW table at scanline %d", tif->tif_row);
-			return (0);
-		}
-		free_entp->firstchar = free_entp->next->firstchar;
-		free_entp->length = free_entp->next->length+1;
-		free_entp->value = (codep < free_entp) ?
-		    codep->firstchar : free_entp->firstchar;
-		if (++free_entp > maxcodep) {
-			if (++nbits > BITS_MAX)		/* should not happen */
-				nbits = BITS_MAX;
-			nbitsmask = MAXCODE(nbits);
-			maxcodep = sp->dec_codetab + nbitsmask;
-		}
-		oldcodep = codep;
-		if (code >= 256) {
-			/*
-			 * Code maps to a string, copy string
-			 * value to output (written in reverse).
-			 */
-			if(codep->length == 0) {
-				TIFFErrorExt(tif->tif_clientdata, module,
-				    "Wrong length of decoded "
-				    "string: data probably corrupted at scanline %d",
-				    tif->tif_row);
-				return (0);
-			}
-			if (codep->length > occ) {
-				/*
-				 * String is too long for decode buffer,
-				 * locate portion that will fit, copy to
-				 * the decode buffer, and setup restart
-				 * logic for the next decoding call.
-				 */
-				sp->dec_codep = codep;
-				do {
-					codep = codep->next;
-				} while (codep->length > occ);
-				sp->dec_restart = occ;
-				tp = op + occ;
-				do  {
-					*--tp = codep->value;
-					codep = codep->next;
-				}  while (--occ);
-				break;
-			}
-			len = codep->length;
-			tp = op + len;
-			do {
-				int t;
-				--tp;
-				t = codep->value;
-				codep = codep->next;
-				*tp = (char)t;
-			} while (codep && tp > op);
-			assert(occ >= len);
-			op += len;
-			occ -= len;
-		} else {
-			*op++ = (char)code;
-			occ--;
-		}
-	}
+        /*
+         * Add the new entry to the code table.
+         */
+        if (free_entp < &sp->dec_codetab[0] ||
+            free_entp >= &sp->dec_codetab[CSIZE])
+        {
+            TIFFErrorExtR(tif, module,
+                          "Corrupted LZW table at scanline %" PRIu32,
+                          tif->tif_row);
+            return (0);
+        }
 
-	tif->tif_rawcc -= (tmsize_t)( (uint8*) bp - tif->tif_rawcp );
-	tif->tif_rawcp = (uint8*) bp;
-#ifdef LZW_CHECKEOS
-	sp->old_tif_rawcc = tif->tif_rawcc;
-#endif
-	sp->lzw_nbits = (unsigned short)nbits;
-	sp->lzw_nextdata = nextdata;
-	sp->lzw_nextbits = nextbits;
-	sp->dec_nbitsmask = nbitsmask;
-	sp->dec_oldcodep = oldcodep;
-	sp->dec_free_entp = free_entp;
-	sp->dec_maxcodep = maxcodep;
+        free_entp->next = oldcodep;
+        if (free_entp->next < &sp->dec_codetab[0] ||
+            free_entp->next >= &sp->dec_codetab[CSIZE])
+        {
+            TIFFErrorExtR(tif, module,
+                          "Corrupted LZW table at scanline %" PRIu32,
+                          tif->tif_row);
+            return (0);
+        }
+        free_entp->firstchar = free_entp->next->firstchar;
+        free_entp->length = free_entp->next->length + 1;
+        free_entp->value =
+            (codep < free_entp) ? codep->firstchar : free_entp->firstchar;
+        if (++free_entp > maxcodep)
+        {
+            if (++nbits > BITS_MAX) /* should not happen */
+                nbits = BITS_MAX;
+            nbitsmask = MAXCODE(nbits);
+            maxcodep = sp->dec_codetab + nbitsmask;
+        }
+        oldcodep = codep;
+        if (code >= 256)
+        {
+            /*
+             * Code maps to a string, copy string
+             * value to output (written in reverse).
+             */
+            if (codep->length == 0)
+            {
+                TIFFErrorExtR(
+                    tif, module,
+                    "Wrong length of decoded "
+                    "string: data probably corrupted at scanline %" PRIu32,
+                    tif->tif_row);
+                return (0);
+            }
+            if (codep->length > occ)
+            {
+                /*
+                 * String is too long for decode buffer,
+                 * locate portion that will fit, copy to
+                 * the decode buffer, and setup restart
+                 * logic for the next decoding call.
+                 */
+                sp->dec_codep = codep;
+                do
+                {
+                    codep = codep->next;
+                } while (codep->length > occ);
+                sp->dec_restart = occ;
+                tp = op + occ;
+                do
+                {
+                    *--tp = codep->value;
+                    codep = codep->next;
+                } while (--occ);
+                break;
+            }
+            len = codep->length;
+            tp = op + len;
+            do
+            {
+                *--tp = codep->value;
+                codep = codep->next;
+            } while (codep && tp > op);
+            assert(occ >= len);
+            op += len;
+            occ -= len;
+        }
+        else
+        {
+            *op++ = (uint8_t)code;
+            occ--;
+        }
+    }
 
-	if (occ > 0) {
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-		TIFFErrorExt(tif->tif_clientdata, module,
-			"Not enough data at scanline %d (short %I64d bytes)",
-			     tif->tif_row, (unsigned __int64) occ);
-#else
-		TIFFErrorExt(tif->tif_clientdata, module,
-			"Not enough data at scanline %d (short %llu bytes)",
-			     tif->tif_row, (unsigned long long) occ);
-#endif
-		return (0);
-	}
-	return (1);
+    tif->tif_rawcc -= (tmsize_t)((uint8_t *)bp - tif->tif_rawcp);
+    tif->tif_rawcp = (uint8_t *)bp;
+
+    sp->old_tif_rawcc = tif->tif_rawcc;
+    sp->dec_bitsleft = dec_bitsleft;
+
+    sp->lzw_nbits = (unsigned short)nbits;
+    sp->lzw_nextdata = nextdata;
+    sp->lzw_nextbits = nextbits;
+    sp->dec_nbitsmask = nbitsmask;
+    sp->dec_oldcodep = oldcodep;
+    sp->dec_free_entp = free_entp;
+    sp->dec_maxcodep = maxcodep;
+
+    if (occ > 0)
+    {
+        TIFFErrorExtR(tif, module,
+                      "Not enough data at scanline %" PRIu32 " (short %" PRIu64
+                      " bytes)",
+                      tif->tif_row, (uint64_t)occ);
+        return (0);
+    }
+    return (1);
 }
 #endif /* LZW_COMPAT */
 
@@ -812,392 +1023,416 @@
  * LZW Encoding.
  */
 
-static int
-LZWSetupEncode(TIFF* tif)
+static int LZWSetupEncode(TIFF *tif)
 {
-	static const char module[] = "LZWSetupEncode";
-	LZWCodecState* sp = EncoderState(tif);
+    static const char module[] = "LZWSetupEncode";
+    LZWCodecState *sp = EncoderState(tif);
 
-	assert(sp != NULL);
-	sp->enc_hashtab = (hash_t*) _TIFFmalloc(HSIZE*sizeof (hash_t));
-	if (sp->enc_hashtab == NULL) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-			     "No space for LZW hash table");
-		return (0);
-	}
-	return (1);
+    assert(sp != NULL);
+    sp->enc_hashtab = (hash_t *)_TIFFmallocExt(tif, HSIZE * sizeof(hash_t));
+    if (sp->enc_hashtab == NULL)
+    {
+        TIFFErrorExtR(tif, module, "No space for LZW hash table");
+        return (0);
+    }
+    return (1);
 }
 
 /*
  * Reset encoding state at the start of a strip.
  */
-static int
-LZWPreEncode(TIFF* tif, uint16 s)
+static int LZWPreEncode(TIFF *tif, uint16_t s)
 {
-	LZWCodecState *sp = EncoderState(tif);
+    LZWCodecState *sp = EncoderState(tif);
 
-	(void) s;
-	assert(sp != NULL);
+    (void)s;
+    assert(sp != NULL);
 
-	if( sp->enc_hashtab == NULL )
-        {
-            tif->tif_setupencode( tif );
-        }
+    if (sp->enc_hashtab == NULL)
+    {
+        tif->tif_setupencode(tif);
+    }
 
-	sp->lzw_nbits = BITS_MIN;
-	sp->lzw_maxcode = MAXCODE(BITS_MIN);
-	sp->lzw_free_ent = CODE_FIRST;
-	sp->lzw_nextbits = 0;
-	sp->lzw_nextdata = 0;
-	sp->enc_checkpoint = CHECK_GAP;
-	sp->enc_ratio = 0;
-	sp->enc_incount = 0;
-	sp->enc_outcount = 0;
-	/*
-	 * The 4 here insures there is space for 2 max-sized
-	 * codes in LZWEncode and LZWPostDecode.
-	 */
-	sp->enc_rawlimit = tif->tif_rawdata + tif->tif_rawdatasize-1 - 4;
-	cl_hash(sp);		/* clear hash table */
-	sp->enc_oldcode = (hcode_t) -1;	/* generates CODE_CLEAR in LZWEncode */
-	return (1);
+    sp->lzw_nbits = BITS_MIN;
+    sp->lzw_maxcode = MAXCODE(BITS_MIN);
+    sp->lzw_free_ent = CODE_FIRST;
+    sp->lzw_nextbits = 0;
+    sp->lzw_nextdata = 0;
+    sp->enc_checkpoint = CHECK_GAP;
+    sp->enc_ratio = 0;
+    sp->enc_incount = 0;
+    sp->enc_outcount = 0;
+    /*
+     * The 4 here insures there is space for 2 max-sized
+     * codes in LZWEncode and LZWPostDecode.
+     */
+    sp->enc_rawlimit = tif->tif_rawdata + tif->tif_rawdatasize - 1 - 4;
+    cl_hash(sp);                   /* clear hash table */
+    sp->enc_oldcode = (hcode_t)-1; /* generates CODE_CLEAR in LZWEncode */
+    return (1);
 }
 
-#define	CALCRATIO(sp, rat) {					\
-	if (incount > 0x007fffff) { /* NB: shift will overflow */\
-		rat = outcount >> 8;				\
-		rat = (rat == 0 ? 0x7fffffff : incount/rat);	\
-	} else							\
-		rat = (incount<<8) / outcount;			\
-}
+#define CALCRATIO(sp, rat)                                                     \
+    {                                                                          \
+        if (incount > 0x007fffff)                                              \
+        { /* NB: shift will overflow */                                        \
+            rat = outcount >> 8;                                               \
+            rat = (rat == 0 ? 0x7fffffff : incount / rat);                     \
+        }                                                                      \
+        else                                                                   \
+            rat = (incount << 8) / outcount;                                   \
+    }
 
 /* Explicit 0xff masking to make icc -check=conversions happy */
-#define	PutNextCode(op, c) {					\
-	nextdata = (nextdata << nbits) | c;			\
-	nextbits += nbits;					\
-	*op++ = (unsigned char)((nextdata >> (nextbits-8))&0xff);		\
-	nextbits -= 8;						\
-	if (nextbits >= 8) {					\
-		*op++ = (unsigned char)((nextdata >> (nextbits-8))&0xff);	\
-		nextbits -= 8;					\
-	}							\
-	outcount += nbits;					\
-}
+#define PutNextCode(op, c)                                                     \
+    {                                                                          \
+        nextdata = (nextdata << nbits) | c;                                    \
+        nextbits += nbits;                                                     \
+        *op++ = (unsigned char)((nextdata >> (nextbits - 8)) & 0xff);          \
+        nextbits -= 8;                                                         \
+        if (nextbits >= 8)                                                     \
+        {                                                                      \
+            *op++ = (unsigned char)((nextdata >> (nextbits - 8)) & 0xff);      \
+            nextbits -= 8;                                                     \
+        }                                                                      \
+        outcount += nbits;                                                     \
+    }
 
 /*
  * Encode a chunk of pixels.
  *
- * Uses an open addressing double hashing (no chaining) on the 
+ * Uses an open addressing double hashing (no chaining) on the
  * prefix code/next character combination.  We do a variant of
  * Knuth's algorithm D (vol. 3, sec. 6.4) along with G. Knott's
  * relatively-prime secondary probe.  Here, the modular division
- * first probe is gives way to a faster exclusive-or manipulation. 
+ * first probe is gives way to a faster exclusive-or manipulation.
  * Also do block compression with an adaptive reset, whereby the
  * code table is cleared when the compression ratio decreases,
  * but after the table fills.  The variable-length output codes
  * are re-sized at this point, and a CODE_CLEAR is generated
- * for the decoder. 
+ * for the decoder.
  */
-static int
-LZWEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s)
+static int LZWEncode(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s)
 {
-	register LZWCodecState *sp = EncoderState(tif);
-	register long fcode;
-	register hash_t *hp;
-	register int h, c;
-	hcode_t ent;
-	long disp;
-	long incount, outcount, checkpoint;
-	unsigned long nextdata;
-        long nextbits;
-	int free_ent, maxcode, nbits;
-	uint8* op;
-	uint8* limit;
+    register LZWCodecState *sp = EncoderState(tif);
+    register long fcode;
+    register hash_t *hp;
+    register int h, c;
+    hcode_t ent;
+    long disp;
+    tmsize_t incount, outcount, checkpoint;
+    WordType nextdata;
+    long nextbits;
+    int free_ent, maxcode, nbits;
+    uint8_t *op;
+    uint8_t *limit;
 
-	(void) s;
-	if (sp == NULL)
-		return (0);
+    (void)s;
+    if (sp == NULL)
+        return (0);
 
-        assert(sp->enc_hashtab != NULL);
+    assert(sp->enc_hashtab != NULL);
 
-	/*
-	 * Load local state.
-	 */
-	incount = sp->enc_incount;
-	outcount = sp->enc_outcount;
-	checkpoint = sp->enc_checkpoint;
-	nextdata = sp->lzw_nextdata;
-	nextbits = sp->lzw_nextbits;
-	free_ent = sp->lzw_free_ent;
-	maxcode = sp->lzw_maxcode;
-	nbits = sp->lzw_nbits;
-	op = tif->tif_rawcp;
-	limit = sp->enc_rawlimit;
-	ent = (hcode_t)sp->enc_oldcode;
+    /*
+     * Load local state.
+     */
+    incount = sp->enc_incount;
+    outcount = sp->enc_outcount;
+    checkpoint = sp->enc_checkpoint;
+    nextdata = sp->lzw_nextdata;
+    nextbits = sp->lzw_nextbits;
+    free_ent = sp->lzw_free_ent;
+    maxcode = sp->lzw_maxcode;
+    nbits = sp->lzw_nbits;
+    op = tif->tif_rawcp;
+    limit = sp->enc_rawlimit;
+    ent = (hcode_t)sp->enc_oldcode;
 
-	if (ent == (hcode_t) -1 && cc > 0) {
-		/*
-		 * NB: This is safe because it can only happen
-		 *     at the start of a strip where we know there
-		 *     is space in the data buffer.
-		 */
-		PutNextCode(op, CODE_CLEAR);
-		ent = *bp++; cc--; incount++;
-	}
-	while (cc > 0) {
-		c = *bp++; cc--; incount++;
-		fcode = ((long)c << BITS_MAX) + ent;
-		h = (c << HSHIFT) ^ ent;	/* xor hashing */
+    if (ent == (hcode_t)-1 && cc > 0)
+    {
+        /*
+         * NB: This is safe because it can only happen
+         *     at the start of a strip where we know there
+         *     is space in the data buffer.
+         */
+        PutNextCode(op, CODE_CLEAR);
+        ent = *bp++;
+        cc--;
+        incount++;
+    }
+    while (cc > 0)
+    {
+        c = *bp++;
+        cc--;
+        incount++;
+        fcode = ((long)c << BITS_MAX) + ent;
+        h = (c << HSHIFT) ^ ent; /* xor hashing */
 #ifdef _WINDOWS
-		/*
-		 * Check hash index for an overflow.
-		 */
-		if (h >= HSIZE)
-			h -= HSIZE;
+        /*
+         * Check hash index for an overflow.
+         */
+        if (h >= HSIZE)
+            h -= HSIZE;
 #endif
-		hp = &sp->enc_hashtab[h];
-		if (hp->hash == fcode) {
-			ent = hp->code;
-			continue;
-		}
-		if (hp->hash >= 0) {
-			/*
-			 * Primary hash failed, check secondary hash.
-			 */
-			disp = HSIZE - h;
-			if (h == 0)
-				disp = 1;
-			do {
-				/*
-				 * Avoid pointer arithmetic because of
-				 * wraparound problems with segments.
-				 */
-				if ((h -= disp) < 0)
-					h += HSIZE;
-				hp = &sp->enc_hashtab[h];
-				if (hp->hash == fcode) {
-					ent = hp->code;
-					goto hit;
-				}
-			} while (hp->hash >= 0);
-		}
-		/*
-		 * New entry, emit code and add to table.
-		 */
-		/*
-		 * Verify there is space in the buffer for the code
-		 * and any potential Clear code that might be emitted
-		 * below.  The value of limit is setup so that there
-		 * are at least 4 bytes free--room for 2 codes.
-		 */
-		if (op > limit) {
-			tif->tif_rawcc = (tmsize_t)(op - tif->tif_rawdata);
-			if( !TIFFFlushData1(tif) )
-                            return 0;
-			op = tif->tif_rawdata;
-		}
-		PutNextCode(op, ent);
-		ent = (hcode_t)c;
-		hp->code = (hcode_t)(free_ent++);
-		hp->hash = fcode;
-		if (free_ent == CODE_MAX-1) {
-			/* table is full, emit clear code and reset */
-			cl_hash(sp);
-			sp->enc_ratio = 0;
-			incount = 0;
-			outcount = 0;
-			free_ent = CODE_FIRST;
-			PutNextCode(op, CODE_CLEAR);
-			nbits = BITS_MIN;
-			maxcode = MAXCODE(BITS_MIN);
-		} else {
-			/*
-			 * If the next entry is going to be too big for
-			 * the code size, then increase it, if possible.
-			 */
-			if (free_ent > maxcode) {
-				nbits++;
-				assert(nbits <= BITS_MAX);
-				maxcode = (int) MAXCODE(nbits);
-			} else if (incount >= checkpoint) {
-				long rat;
-				/*
-				 * Check compression ratio and, if things seem
-				 * to be slipping, clear the hash table and
-				 * reset state.  The compression ratio is a
-				 * 24+8-bit fractional number.
-				 */
-				checkpoint = incount+CHECK_GAP;
-				CALCRATIO(sp, rat);
-				if (rat <= sp->enc_ratio) {
-					cl_hash(sp);
-					sp->enc_ratio = 0;
-					incount = 0;
-					outcount = 0;
-					free_ent = CODE_FIRST;
-					PutNextCode(op, CODE_CLEAR);
-					nbits = BITS_MIN;
-					maxcode = MAXCODE(BITS_MIN);
-				} else
-					sp->enc_ratio = rat;
-			}
-		}
-	hit:
-		;
-	}
+        hp = &sp->enc_hashtab[h];
+        if (hp->hash == fcode)
+        {
+            ent = hp->code;
+            continue;
+        }
+        if (hp->hash >= 0)
+        {
+            /*
+             * Primary hash failed, check secondary hash.
+             */
+            disp = HSIZE - h;
+            if (h == 0)
+                disp = 1;
+            do
+            {
+                /*
+                 * Avoid pointer arithmetic because of
+                 * wraparound problems with segments.
+                 */
+                if ((h -= disp) < 0)
+                    h += HSIZE;
+                hp = &sp->enc_hashtab[h];
+                if (hp->hash == fcode)
+                {
+                    ent = hp->code;
+                    goto hit;
+                }
+            } while (hp->hash >= 0);
+        }
+        /*
+         * New entry, emit code and add to table.
+         */
+        /*
+         * Verify there is space in the buffer for the code
+         * and any potential Clear code that might be emitted
+         * below.  The value of limit is setup so that there
+         * are at least 4 bytes free--room for 2 codes.
+         */
+        if (op > limit)
+        {
+            tif->tif_rawcc = (tmsize_t)(op - tif->tif_rawdata);
+            if (!TIFFFlushData1(tif))
+                return 0;
+            op = tif->tif_rawdata;
+        }
+        PutNextCode(op, ent);
+        ent = (hcode_t)c;
+        hp->code = (hcode_t)(free_ent++);
+        hp->hash = fcode;
+        if (free_ent == CODE_MAX - 1)
+        {
+            /* table is full, emit clear code and reset */
+            cl_hash(sp);
+            sp->enc_ratio = 0;
+            incount = 0;
+            outcount = 0;
+            free_ent = CODE_FIRST;
+            PutNextCode(op, CODE_CLEAR);
+            nbits = BITS_MIN;
+            maxcode = MAXCODE(BITS_MIN);
+        }
+        else
+        {
+            /*
+             * If the next entry is going to be too big for
+             * the code size, then increase it, if possible.
+             */
+            if (free_ent > maxcode)
+            {
+                nbits++;
+                assert(nbits <= BITS_MAX);
+                maxcode = (int)MAXCODE(nbits);
+            }
+            else if (incount >= checkpoint)
+            {
+                tmsize_t rat;
+                /*
+                 * Check compression ratio and, if things seem
+                 * to be slipping, clear the hash table and
+                 * reset state.  The compression ratio is a
+                 * 24+8-bit fractional number.
+                 */
+                checkpoint = incount + CHECK_GAP;
+                CALCRATIO(sp, rat);
+                if (rat <= sp->enc_ratio)
+                {
+                    cl_hash(sp);
+                    sp->enc_ratio = 0;
+                    incount = 0;
+                    outcount = 0;
+                    free_ent = CODE_FIRST;
+                    PutNextCode(op, CODE_CLEAR);
+                    nbits = BITS_MIN;
+                    maxcode = MAXCODE(BITS_MIN);
+                }
+                else
+                    sp->enc_ratio = rat;
+            }
+        }
+    hit:;
+    }
 
-	/*
-	 * Restore global state.
-	 */
-	sp->enc_incount = incount;
-	sp->enc_outcount = outcount;
-	sp->enc_checkpoint = checkpoint;
-	sp->enc_oldcode = ent;
-	sp->lzw_nextdata = nextdata;
-	sp->lzw_nextbits = nextbits;
-	sp->lzw_free_ent = (unsigned short)free_ent;
-	sp->lzw_maxcode = (unsigned short)maxcode;
-	sp->lzw_nbits = (unsigned short)nbits;
-	tif->tif_rawcp = op;
-	return (1);
+    /*
+     * Restore global state.
+     */
+    sp->enc_incount = incount;
+    sp->enc_outcount = outcount;
+    sp->enc_checkpoint = checkpoint;
+    sp->enc_oldcode = ent;
+    sp->lzw_nextdata = nextdata;
+    sp->lzw_nextbits = nextbits;
+    sp->lzw_free_ent = (unsigned short)free_ent;
+    sp->lzw_maxcode = (unsigned short)maxcode;
+    sp->lzw_nbits = (unsigned short)nbits;
+    tif->tif_rawcp = op;
+    return (1);
 }
 
 /*
  * Finish off an encoded strip by flushing the last
  * string and tacking on an End Of Information code.
  */
-static int
-LZWPostEncode(TIFF* tif)
+static int LZWPostEncode(TIFF *tif)
 {
-	register LZWCodecState *sp = EncoderState(tif);
-	uint8* op = tif->tif_rawcp;
-	long nextbits = sp->lzw_nextbits;
-	unsigned long nextdata = sp->lzw_nextdata;
-	long outcount = sp->enc_outcount;
-	int nbits = sp->lzw_nbits;
+    register LZWCodecState *sp = EncoderState(tif);
+    uint8_t *op = tif->tif_rawcp;
+    long nextbits = sp->lzw_nextbits;
+    WordType nextdata = sp->lzw_nextdata;
+    tmsize_t outcount = sp->enc_outcount;
+    int nbits = sp->lzw_nbits;
 
-	if (op > sp->enc_rawlimit) {
-		tif->tif_rawcc = (tmsize_t)(op - tif->tif_rawdata);
-		if( !TIFFFlushData1(tif) )
-                    return 0;
-		op = tif->tif_rawdata;
-	}
-	if (sp->enc_oldcode != (hcode_t) -1) {
-                int free_ent = sp->lzw_free_ent;
+    if (op > sp->enc_rawlimit)
+    {
+        tif->tif_rawcc = (tmsize_t)(op - tif->tif_rawdata);
+        if (!TIFFFlushData1(tif))
+            return 0;
+        op = tif->tif_rawdata;
+    }
+    if (sp->enc_oldcode != (hcode_t)-1)
+    {
+        int free_ent = sp->lzw_free_ent;
 
-		PutNextCode(op, sp->enc_oldcode);
-		sp->enc_oldcode = (hcode_t) -1;
-                free_ent ++;
+        PutNextCode(op, sp->enc_oldcode);
+        sp->enc_oldcode = (hcode_t)-1;
+        free_ent++;
 
-                if (free_ent == CODE_MAX-1) {
-                        /* table is full, emit clear code and reset */
-                        outcount = 0;
-                        PutNextCode(op, CODE_CLEAR);
-                        nbits = BITS_MIN;
-                } else {
-                        /*
-                        * If the next entry is going to be too big for
-                        * the code size, then increase it, if possible.
-                        */
-                        if (free_ent > sp->lzw_maxcode) {
-                                nbits++;
-                                assert(nbits <= BITS_MAX);
-                        }
-                }
-	}
-	PutNextCode(op, CODE_EOI);
-        /* Explicit 0xff masking to make icc -check=conversions happy */
-	if (nextbits > 0) 
-		*op++ = (unsigned char)((nextdata << (8-nextbits))&0xff);
-	tif->tif_rawcc = (tmsize_t)(op - tif->tif_rawdata);
-	return (1);
+        if (free_ent == CODE_MAX - 1)
+        {
+            /* table is full, emit clear code and reset */
+            outcount = 0;
+            PutNextCode(op, CODE_CLEAR);
+            nbits = BITS_MIN;
+        }
+        else
+        {
+            /*
+             * If the next entry is going to be too big for
+             * the code size, then increase it, if possible.
+             */
+            if (free_ent > sp->lzw_maxcode)
+            {
+                nbits++;
+                assert(nbits <= BITS_MAX);
+            }
+        }
+    }
+    PutNextCode(op, CODE_EOI);
+    /* Explicit 0xff masking to make icc -check=conversions happy */
+    if (nextbits > 0)
+        *op++ = (unsigned char)((nextdata << (8 - nextbits)) & 0xff);
+    tif->tif_rawcc = (tmsize_t)(op - tif->tif_rawdata);
+    (void)outcount;
+    return (1);
 }
 
 /*
  * Reset encoding hash table.
  */
-static void
-cl_hash(LZWCodecState* sp)
+static void cl_hash(LZWCodecState *sp)
 {
-	register hash_t *hp = &sp->enc_hashtab[HSIZE-1];
-	register long i = HSIZE-8;
+    register hash_t *hp = &sp->enc_hashtab[HSIZE - 1];
+    register long i = HSIZE - 8;
 
-	do {
-		i -= 8;
-		hp[-7].hash = -1;
-		hp[-6].hash = -1;
-		hp[-5].hash = -1;
-		hp[-4].hash = -1;
-		hp[-3].hash = -1;
-		hp[-2].hash = -1;
-		hp[-1].hash = -1;
-		hp[ 0].hash = -1;
-		hp -= 8;
-	} while (i >= 0);
-	for (i += 8; i > 0; i--, hp--)
-		hp->hash = -1;
+    do
+    {
+        i -= 8;
+        hp[-7].hash = -1;
+        hp[-6].hash = -1;
+        hp[-5].hash = -1;
+        hp[-4].hash = -1;
+        hp[-3].hash = -1;
+        hp[-2].hash = -1;
+        hp[-1].hash = -1;
+        hp[0].hash = -1;
+        hp -= 8;
+    } while (i >= 0);
+    for (i += 8; i > 0; i--, hp--)
+        hp->hash = -1;
 }
 
-static void
-LZWCleanup(TIFF* tif)
+static void LZWCleanup(TIFF *tif)
 {
-	(void)TIFFPredictorCleanup(tif);
+    (void)TIFFPredictorCleanup(tif);
 
-	assert(tif->tif_data != 0);
+    assert(tif->tif_data != 0);
 
-	if (DecoderState(tif)->dec_codetab)
-		_TIFFfree(DecoderState(tif)->dec_codetab);
+    if (DecoderState(tif)->dec_codetab)
+        _TIFFfreeExt(tif, DecoderState(tif)->dec_codetab);
 
-	if (EncoderState(tif)->enc_hashtab)
-		_TIFFfree(EncoderState(tif)->enc_hashtab);
+    if (EncoderState(tif)->enc_hashtab)
+        _TIFFfreeExt(tif, EncoderState(tif)->enc_hashtab);
 
-	_TIFFfree(tif->tif_data);
-	tif->tif_data = NULL;
+    _TIFFfreeExt(tif, tif->tif_data);
+    tif->tif_data = NULL;
 
-	_TIFFSetDefaultCompressionState(tif);
+    _TIFFSetDefaultCompressionState(tif);
 }
 
-int
-TIFFInitLZW(TIFF* tif, int scheme)
+int TIFFInitLZW(TIFF *tif, int scheme)
 {
-	static const char module[] = "TIFFInitLZW";
-	assert(scheme == COMPRESSION_LZW);
-	/*
-	 * Allocate state block so tag methods have storage to record values.
-	 */
-	tif->tif_data = (uint8*) _TIFFmalloc(sizeof (LZWCodecState));
-	if (tif->tif_data == NULL)
-		goto bad;
-	DecoderState(tif)->dec_codetab = NULL;
-	DecoderState(tif)->dec_decode = NULL;
-	EncoderState(tif)->enc_hashtab = NULL;
-        LZWState(tif)->rw_mode = tif->tif_mode;
+    static const char module[] = "TIFFInitLZW";
+    (void)scheme;
+    assert(scheme == COMPRESSION_LZW);
+    /*
+     * Allocate state block so tag methods have storage to record values.
+     */
+    tif->tif_data = (uint8_t *)_TIFFmallocExt(tif, sizeof(LZWCodecState));
+    if (tif->tif_data == NULL)
+        goto bad;
+    DecoderState(tif)->dec_codetab = NULL;
+    DecoderState(tif)->dec_decode = NULL;
+    EncoderState(tif)->enc_hashtab = NULL;
+    LZWState(tif)->rw_mode = tif->tif_mode;
 
-	/*
-	 * Install codec methods.
-	 */
-	tif->tif_fixuptags = LZWFixupTags; 
-	tif->tif_setupdecode = LZWSetupDecode;
-	tif->tif_predecode = LZWPreDecode;
-	tif->tif_decoderow = LZWDecode;
-	tif->tif_decodestrip = LZWDecode;
-	tif->tif_decodetile = LZWDecode;
-	tif->tif_setupencode = LZWSetupEncode;
-	tif->tif_preencode = LZWPreEncode;
-	tif->tif_postencode = LZWPostEncode;
-	tif->tif_encoderow = LZWEncode;
-	tif->tif_encodestrip = LZWEncode;
-	tif->tif_encodetile = LZWEncode;
-	tif->tif_cleanup = LZWCleanup;
-	/*
-	 * Setup predictor setup.
-	 */
-	(void) TIFFPredictorInit(tif);
-	return (1);
+    /*
+     * Install codec methods.
+     */
+    tif->tif_fixuptags = LZWFixupTags;
+    tif->tif_setupdecode = LZWSetupDecode;
+    tif->tif_predecode = LZWPreDecode;
+    tif->tif_decoderow = LZWDecode;
+    tif->tif_decodestrip = LZWDecode;
+    tif->tif_decodetile = LZWDecode;
+    tif->tif_setupencode = LZWSetupEncode;
+    tif->tif_preencode = LZWPreEncode;
+    tif->tif_postencode = LZWPostEncode;
+    tif->tif_encoderow = LZWEncode;
+    tif->tif_encodestrip = LZWEncode;
+    tif->tif_encodetile = LZWEncode;
+    tif->tif_cleanup = LZWCleanup;
+    /*
+     * Setup predictor setup.
+     */
+    (void)TIFFPredictorInit(tif);
+    return (1);
 bad:
-	TIFFErrorExt(tif->tif_clientdata, module, 
-		     "No space for LZW state block");
-	return (0);
+    TIFFErrorExtR(tif, module, "No space for LZW state block");
+    return (0);
 }
 
 /*
@@ -1218,15 +1453,6 @@
  * from this software without specific prior written permission.
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 #endif /* LZW_SUPPORT */
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_next.c b/third_party/libtiff/tif_next.c
index 0ba61ae..f000574 100644
--- a/third_party/libtiff/tif_next.c
+++ b/third_party/libtiff/tif_next.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -30,158 +30,165 @@
  * NeXT 2-bit Grey Scale Compression Algorithm Support
  */
 
-#define SETPIXEL(op, v) {			\
-	switch (npixels++ & 3) {		\
-	case 0:	op[0]  = (unsigned char) ((v) << 6); break;	\
-	case 1:	op[0] |= (v) << 4; break;	\
-	case 2:	op[0] |= (v) << 2; break;	\
-	case 3:	*op++ |= (v);	   op_offset++; break;	\
-	}					\
-}
+#define SETPIXEL(op, v)                                                        \
+    {                                                                          \
+        switch (npixels++ & 3)                                                 \
+        {                                                                      \
+            case 0:                                                            \
+                op[0] = (unsigned char)((v) << 6);                             \
+                break;                                                         \
+            case 1:                                                            \
+                op[0] |= (v) << 4;                                             \
+                break;                                                         \
+            case 2:                                                            \
+                op[0] |= (v) << 2;                                             \
+                break;                                                         \
+            case 3:                                                            \
+                *op++ |= (v);                                                  \
+                op_offset++;                                                   \
+                break;                                                         \
+        }                                                                      \
+    }
 
-#define LITERALROW	0x00
-#define LITERALSPAN	0x40
-#define WHITE   	((1<<2)-1)
+#define LITERALROW 0x00
+#define LITERALSPAN 0x40
+#define WHITE ((1 << 2) - 1)
 
-static int
-NeXTDecode(TIFF* tif, uint8* buf, tmsize_t occ, uint16 s)
+static int NeXTDecode(TIFF *tif, uint8_t *buf, tmsize_t occ, uint16_t s)
 {
-	static const char module[] = "NeXTDecode";
-	unsigned char *bp, *op;
-	tmsize_t cc;
-	uint8* row;
-	tmsize_t scanline, n;
+    static const char module[] = "NeXTDecode";
+    unsigned char *bp, *op;
+    tmsize_t cc;
+    uint8_t *row;
+    tmsize_t scanline, n;
 
-	(void) s;
-	/*
-	 * Each scanline is assumed to start off as all
-	 * white (we assume a PhotometricInterpretation
-	 * of ``min-is-black'').
-	 */
-	for (op = (unsigned char*) buf, cc = occ; cc-- > 0;)
-		*op++ = 0xff;
+    (void)s;
+    /*
+     * Each scanline is assumed to start off as all
+     * white (we assume a PhotometricInterpretation
+     * of ``min-is-black'').
+     */
+    for (op = (unsigned char *)buf, cc = occ; cc-- > 0;)
+        *op++ = 0xff;
 
-	bp = (unsigned char *)tif->tif_rawcp;
-	cc = tif->tif_rawcc;
-	scanline = tif->tif_scanlinesize;
-	if (occ % scanline)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "Fractional scanlines cannot be read");
-		return (0);
-	}
-	for (row = buf; cc > 0 && occ > 0; occ -= scanline, row += scanline) {
-		n = *bp++;
-		cc--;
-		switch (n) {
-		case LITERALROW:
-			/*
-			 * The entire scanline is given as literal values.
-			 */
-			if (cc < scanline)
-				goto bad;
-			_TIFFmemcpy(row, bp, scanline);
-			bp += scanline;
-			cc -= scanline;
-			break;
-		case LITERALSPAN: {
-			tmsize_t off;
-			/*
-			 * The scanline has a literal span that begins at some
-			 * offset.
-			 */
-			if( cc < 4 )
-				goto bad;
-			off = (bp[0] * 256) + bp[1];
-			n = (bp[2] * 256) + bp[3];
-			if (cc < 4+n || off+n > scanline)
-				goto bad;
-			_TIFFmemcpy(row+off, bp+4, n);
-			bp += 4+n;
-			cc -= 4+n;
-			break;
-		}
-		default: {
-			uint32 npixels = 0, grey;
-			tmsize_t op_offset = 0;
-			uint32 imagewidth = tif->tif_dir.td_imagewidth;
-            if( isTiled(tif) )
-                imagewidth = tif->tif_dir.td_tilewidth;
+    bp = (unsigned char *)tif->tif_rawcp;
+    cc = tif->tif_rawcc;
+    scanline = tif->tif_scanlinesize;
+    if (occ % scanline)
+    {
+        TIFFErrorExtR(tif, module, "Fractional scanlines cannot be read");
+        return (0);
+    }
+    for (row = buf; cc > 0 && occ > 0; occ -= scanline, row += scanline)
+    {
+        n = *bp++;
+        cc--;
+        switch (n)
+        {
+            case LITERALROW:
+                /*
+                 * The entire scanline is given as literal values.
+                 */
+                if (cc < scanline)
+                    goto bad;
+                _TIFFmemcpy(row, bp, scanline);
+                bp += scanline;
+                cc -= scanline;
+                break;
+            case LITERALSPAN:
+            {
+                tmsize_t off;
+                /*
+                 * The scanline has a literal span that begins at some
+                 * offset.
+                 */
+                if (cc < 4)
+                    goto bad;
+                off = (bp[0] * 256) + bp[1];
+                n = (bp[2] * 256) + bp[3];
+                if (cc < 4 + n || off + n > scanline)
+                    goto bad;
+                _TIFFmemcpy(row + off, bp + 4, n);
+                bp += 4 + n;
+                cc -= 4 + n;
+                break;
+            }
+            default:
+            {
+                uint32_t npixels = 0, grey;
+                tmsize_t op_offset = 0;
+                uint32_t imagewidth = tif->tif_dir.td_imagewidth;
+                if (isTiled(tif))
+                    imagewidth = tif->tif_dir.td_tilewidth;
 
-			/*
-			 * The scanline is composed of a sequence of constant
-			 * color ``runs''.  We shift into ``run mode'' and
-			 * interpret bytes as codes of the form
-			 * <color><npixels> until we've filled the scanline.
-			 */
-			op = row;
-			for (;;) {
-				grey = (uint32)((n>>6) & 0x3);
-				n &= 0x3f;
-				/*
-				 * Ensure the run does not exceed the scanline
-				 * bounds, potentially resulting in a security
-				 * issue.
-				 */
-				while (n-- > 0 && npixels < imagewidth && op_offset < scanline)
-					SETPIXEL(op, grey);
-				if (npixels >= imagewidth)
-					break;
-                if (op_offset >= scanline ) {
-                    TIFFErrorExt(tif->tif_clientdata, module, "Invalid data for scanline %ld",
-                        (long) tif->tif_row);
-                    return (0);
+                /*
+                 * The scanline is composed of a sequence of constant
+                 * color ``runs''.  We shift into ``run mode'' and
+                 * interpret bytes as codes of the form
+                 * <color><npixels> until we've filled the scanline.
+                 */
+                op = row;
+                for (;;)
+                {
+                    grey = (uint32_t)((n >> 6) & 0x3);
+                    n &= 0x3f;
+                    /*
+                     * Ensure the run does not exceed the scanline
+                     * bounds, potentially resulting in a security
+                     * issue.
+                     */
+                    while (n-- > 0 && npixels < imagewidth &&
+                           op_offset < scanline)
+                        SETPIXEL(op, grey);
+                    if (npixels >= imagewidth)
+                        break;
+                    if (op_offset >= scanline)
+                    {
+                        TIFFErrorExtR(tif, module,
+                                      "Invalid data for scanline %" PRIu32,
+                                      tif->tif_row);
+                        return (0);
+                    }
+                    if (cc == 0)
+                        goto bad;
+                    n = *bp++;
+                    cc--;
                 }
-				if (cc == 0)
-					goto bad;
-				n = *bp++;
-				cc--;
-			}
-			break;
-		}
-		}
-	}
-	tif->tif_rawcp = (uint8*) bp;
-	tif->tif_rawcc = cc;
-	return (1);
+                break;
+            }
+        }
+    }
+    tif->tif_rawcp = (uint8_t *)bp;
+    tif->tif_rawcc = cc;
+    return (1);
 bad:
-	TIFFErrorExt(tif->tif_clientdata, module, "Not enough data for scanline %ld",
-	    (long) tif->tif_row);
-	return (0);
+    TIFFErrorExtR(tif, module, "Not enough data for scanline %" PRIu32,
+                  tif->tif_row);
+    return (0);
 }
 
-static int
-NeXTPreDecode(TIFF* tif, uint16 s)
+static int NeXTPreDecode(TIFF *tif, uint16_t s)
 {
-	static const char module[] = "NeXTPreDecode";
-	TIFFDirectory *td = &tif->tif_dir;
-	(void)s;
+    static const char module[] = "NeXTPreDecode";
+    TIFFDirectory *td = &tif->tif_dir;
+    (void)s;
 
-	if( td->td_bitspersample != 2 )
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "Unsupported BitsPerSample = %d",
-					 td->td_bitspersample);
-		return (0);
-	}
-	return (1);
+    if (td->td_bitspersample != 2)
+    {
+        TIFFErrorExtR(tif, module, "Unsupported BitsPerSample = %" PRIu16,
+                      td->td_bitspersample);
+        return (0);
+    }
+    return (1);
 }
-	
-int
-TIFFInitNeXT(TIFF* tif, int scheme)
+
+int TIFFInitNeXT(TIFF *tif, int scheme)
 {
-	(void) scheme;
-	tif->tif_predecode = NeXTPreDecode;  
-	tif->tif_decoderow = NeXTDecode;  
-	tif->tif_decodestrip = NeXTDecode;  
-	tif->tif_decodetile = NeXTDecode;
-	return (1);
+    (void)scheme;
+    tif->tif_predecode = NeXTPreDecode;
+    tif->tif_decoderow = NeXTDecode;
+    tif->tif_decodestrip = NeXTDecode;
+    tif->tif_decodetile = NeXTDecode;
+    return (1);
 }
 #endif /* NEXT_SUPPORT */
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_ojpeg.c b/third_party/libtiff/tif_ojpeg.c
deleted file mode 100644
index 9982272..0000000
--- a/third_party/libtiff/tif_ojpeg.c
+++ /dev/null
@@ -1,2621 +0,0 @@
-/* WARNING: The type of JPEG encapsulation defined by the TIFF Version 6.0
-   specification is now totally obsolete and deprecated for new applications and
-   images. This file was was created solely in order to read unconverted images
-   still present on some users' computer systems. It will never be extended
-   to write such files. Writing new-style JPEG compressed TIFFs is implemented
-   in tif_jpeg.c.
-
-   The code is carefully crafted to robustly read all gathered JPEG-in-TIFF
-   testfiles, and anticipate as much as possible all other... But still, it may
-   fail on some. If you encounter problems, please report them on the TIFF
-   mailing list and/or to Joris Van Damme <info@awaresystems.be>.
-
-   Please read the file called "TIFF Technical Note #2" if you need to be
-   convinced this compression scheme is bad and breaks TIFF. That document
-   is linked to from the LibTiff site <http://www.remotesensing.org/libtiff/>
-   and from AWare Systems' TIFF section
-   <http://www.awaresystems.be/imaging/tiff.html>. It is also absorbed
-   in Adobe's specification supplements, marked "draft" up to this day, but
-   supported by the TIFF community.
-
-   This file interfaces with Release 6B of the JPEG Library written by the
-   Independent JPEG Group. Previous versions of this file required a hack inside
-   the LibJpeg library. This version no longer requires that. Remember to
-   remove the hack if you update from the old version.
-
-   Copyright (c) Joris Van Damme <info@awaresystems.be>
-   Copyright (c) AWare Systems <http://www.awaresystems.be/>
-
-   The licence agreement for this file is the same as the rest of the LibTiff
-   library.
-
-   IN NO EVENT SHALL JORIS VAN DAMME OR AWARE SYSTEMS BE LIABLE FOR
-   ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
-   OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
-   WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
-   LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
-   OF THIS SOFTWARE.
-
-   Joris Van Damme and/or AWare Systems may be available for custom
-   development. If you like what you see, and need anything similar or related,
-   contact <info@awaresystems.be>.
-*/
-
-/* What is what, and what is not?
-
-   This decoder starts with an input stream, that is essentially the JpegInterchangeFormat
-   stream, if any, followed by the strile data, if any. This stream is read in
-   OJPEGReadByte and related functions.
-
-   It analyzes the start of this stream, until it encounters non-marker data, i.e.
-   compressed image data. Some of the header markers it sees have no actual content,
-   like the SOI marker, and APP/COM markers that really shouldn't even be there. Some
-   other markers do have content, and the valuable bits and pieces of information
-   in these markers are saved, checking all to verify that the stream is more or
-   less within expected bounds. This happens inside the OJPEGReadHeaderInfoSecStreamXxx
-   functions.
-
-   Some OJPEG imagery contains no valid JPEG header markers. This situation is picked
-   up on if we've seen no SOF marker when we're at the start of the compressed image
-   data. In this case, the tables are read from JpegXxxTables tags, and the other
-   bits and pieces of information is initialized to its most basic value. This is
-   implemented in the OJPEGReadHeaderInfoSecTablesXxx functions.
-
-   When this is complete, a good and valid JPEG header can be assembled, and this is
-   passed through to LibJpeg. When that's done, the remainder of the input stream, i.e.
-   the compressed image data, can be passed through unchanged. This is done in
-   OJPEGWriteStream functions.
-
-   LibTiff rightly expects to know the subsampling values before decompression. Just like
-   in new-style JPEG-in-TIFF, though, or even more so, actually, the YCbCrsubsampling
-   tag is notoriously unreliable. To correct these tag values with the ones inside
-   the JPEG stream, the first part of the input stream is pre-scanned in
-   OJPEGSubsamplingCorrect, making no note of any other data, reporting no warnings
-   or errors, up to the point where either these values are read, or it's clear they
-   aren't there. This means that some of the data is read twice, but we feel speed
-   in correcting these values is important enough to warrant this sacrifice. Although
-   there is currently no define or other configuration mechanism to disable this behaviour,
-   the actual header scanning is build to robustly respond with error report if it
-   should encounter an uncorrected mismatch of subsampling values. See
-   OJPEGReadHeaderInfoSecStreamSof.
-
-   The restart interval and restart markers are the most tricky part... The restart
-   interval can be specified in a tag. It can also be set inside the input JPEG stream.
-   It can be used inside the input JPEG stream. If reading from strile data, we've
-   consistently discovered the need to insert restart markers in between the different
-   striles, as is also probably the most likely interpretation of the original TIFF 6.0
-   specification. With all this setting of interval, and actual use of markers that is not
-   predictable at the time of valid JPEG header assembly, the restart thing may turn
-   out the Achilles heel of this implementation. Fortunately, most OJPEG writer vendors
-   succeed in reading back what they write, which may be the reason why we've been able
-   to discover ways that seem to work.
-
-   Some special provision is made for planarconfig separate OJPEG files. These seem
-   to consistently contain header info, a SOS marker, a plane, SOS marker, plane, SOS,
-   and plane. This may or may not be a valid JPEG configuration, we don't know and don't
-   care. We want LibTiff to be able to access the planes individually, without huge
-   buffering inside LibJpeg, anyway. So we compose headers to feed to LibJpeg, in this
-   case, that allow us to pass a single plane such that LibJpeg sees a valid
-   single-channel JPEG stream. Locating subsequent SOS markers, and thus subsequent
-   planes, is done inside OJPEGReadSecondarySos.
-
-   The benefit of the scheme is... that it works, basically. We know of no other that
-   does. It works without checking software tag, or otherwise going about things in an
-   OJPEG flavor specific manner. Instead, it is a single scheme, that covers the cases
-   with and without JpegInterchangeFormat, with and without striles, with part of
-   the header in JpegInterchangeFormat and remainder in first strile, etc. It is forgiving
-   and robust, may likely work with OJPEG flavors we've not seen yet, and makes most out
-   of the data.
-
-   Another nice side-effect is that a complete JPEG single valid stream is build if
-   planarconfig is not separate (vast majority). We may one day use that to build
-   converters to JPEG, and/or to new-style JPEG compression inside TIFF.
-
-   A disadvantage is the lack of random access to the individual striles. This is the
-   reason for much of the complicated restart-and-position stuff inside OJPEGPreDecode.
-   Applications would do well accessing all striles in order, as this will result in
-   a single sequential scan of the input stream, and no restarting of LibJpeg decoding
-   session.
-*/
-
-#define WIN32_LEAN_AND_MEAN
-#define VC_EXTRALEAN
-
-#include "tiffiop.h"
-#ifdef OJPEG_SUPPORT
-
-/* Configuration defines here are:
- * JPEG_ENCAP_EXTERNAL: The normal way to call libjpeg, uses longjump. In some environments,
- * 	like eg LibTiffDelphi, this is not possible. For this reason, the actual calls to
- * 	libjpeg, with longjump stuff, are encapsulated in dedicated functions. When
- * 	JPEG_ENCAP_EXTERNAL is defined, these encapsulating functions are declared external
- * 	to this unit, and can be defined elsewhere to use stuff other then longjump.
- * 	The default mode, without JPEG_ENCAP_EXTERNAL, implements the call encapsulators
- * 	here, internally, with normal longjump.
- * SETJMP, LONGJMP, JMP_BUF: On some machines/environments a longjump equivalent is
- * 	conveniently available, but still it may be worthwhile to use _setjmp or sigsetjmp
- * 	in place of plain setjmp. These macros will make it easier. It is useless
- * 	to fiddle with these if you define JPEG_ENCAP_EXTERNAL.
- * OJPEG_BUFFER: Define the size of the desired buffer here. Should be small enough so as to guarantee
- * 	instant processing, optimal streaming and optimal use of processor cache, but also big
- * 	enough so as to not result in significant call overhead. It should be at least a few
- * 	bytes to accommodate some structures (this is verified in asserts), but it would not be
- * 	sensible to make it this small anyway, and it should be at most 64K since it is indexed
- * 	with uint16. We recommend 2K.
- * EGYPTIANWALK: You could also define EGYPTIANWALK here, but it is not used anywhere and has
- * 	absolutely no effect. That is why most people insist the EGYPTIANWALK is a bit silly.
- */
-
-/* define LIBJPEG_ENCAP_EXTERNAL */
-#define SETJMP(jbuf) setjmp(jbuf)
-#define LONGJMP(jbuf,code) longjmp(jbuf,code)
-#define JMP_BUF jmp_buf
-#define OJPEG_BUFFER 2048
-/* define EGYPTIANWALK */
-
-#define JPEG_MARKER_SOF0 0xC0
-#define JPEG_MARKER_SOF1 0xC1
-#define JPEG_MARKER_SOF3 0xC3
-#define JPEG_MARKER_DHT 0xC4
-#define JPEG_MARKER_RST0 0XD0
-#define JPEG_MARKER_SOI 0xD8
-#define JPEG_MARKER_EOI 0xD9
-#define JPEG_MARKER_SOS 0xDA
-#define JPEG_MARKER_DQT 0xDB
-#define JPEG_MARKER_DRI 0xDD
-#define JPEG_MARKER_APP0 0xE0
-#define JPEG_MARKER_COM 0xFE
-
-#define FIELD_OJPEG_JPEGINTERCHANGEFORMAT (FIELD_CODEC+0)
-#define FIELD_OJPEG_JPEGINTERCHANGEFORMATLENGTH (FIELD_CODEC+1)
-#define FIELD_OJPEG_JPEGQTABLES (FIELD_CODEC+2)
-#define FIELD_OJPEG_JPEGDCTABLES (FIELD_CODEC+3)
-#define FIELD_OJPEG_JPEGACTABLES (FIELD_CODEC+4)
-#define FIELD_OJPEG_JPEGPROC (FIELD_CODEC+5)
-#define FIELD_OJPEG_JPEGRESTARTINTERVAL (FIELD_CODEC+6)
-
-static const TIFFField ojpegFields[] = {
-	{TIFFTAG_JPEGIFOFFSET,1,1,TIFF_LONG8,0,TIFF_SETGET_UINT64,TIFF_SETGET_UNDEFINED,FIELD_OJPEG_JPEGINTERCHANGEFORMAT,TRUE,FALSE,"JpegInterchangeFormat",NULL},
-	{TIFFTAG_JPEGIFBYTECOUNT,1,1,TIFF_LONG8,0,TIFF_SETGET_UINT64,TIFF_SETGET_UNDEFINED,FIELD_OJPEG_JPEGINTERCHANGEFORMATLENGTH,TRUE,FALSE,"JpegInterchangeFormatLength",NULL},
-	{TIFFTAG_JPEGQTABLES,TIFF_VARIABLE2,TIFF_VARIABLE2,TIFF_LONG8,0,TIFF_SETGET_C32_UINT64,TIFF_SETGET_UNDEFINED,FIELD_OJPEG_JPEGQTABLES,FALSE,TRUE,"JpegQTables",NULL},
-	{TIFFTAG_JPEGDCTABLES,TIFF_VARIABLE2,TIFF_VARIABLE2,TIFF_LONG8,0,TIFF_SETGET_C32_UINT64,TIFF_SETGET_UNDEFINED,FIELD_OJPEG_JPEGDCTABLES,FALSE,TRUE,"JpegDcTables",NULL},
-	{TIFFTAG_JPEGACTABLES,TIFF_VARIABLE2,TIFF_VARIABLE2,TIFF_LONG8,0,TIFF_SETGET_C32_UINT64,TIFF_SETGET_UNDEFINED,FIELD_OJPEG_JPEGACTABLES,FALSE,TRUE,"JpegAcTables",NULL},
-	{TIFFTAG_JPEGPROC,1,1,TIFF_SHORT,0,TIFF_SETGET_UINT16,TIFF_SETGET_UNDEFINED,FIELD_OJPEG_JPEGPROC,FALSE,FALSE,"JpegProc",NULL},
-	{TIFFTAG_JPEGRESTARTINTERVAL,1,1,TIFF_SHORT,0,TIFF_SETGET_UINT16,TIFF_SETGET_UNDEFINED,FIELD_OJPEG_JPEGRESTARTINTERVAL,FALSE,FALSE,"JpegRestartInterval",NULL},
-};
-
-#ifndef LIBJPEG_ENCAP_EXTERNAL
-#include <setjmp.h>
-#endif
-
-/* We undefine FAR to avoid conflict with JPEG definition */
-
-#ifdef FAR
-#undef FAR
-#endif
-
-/*
-  Libjpeg's jmorecfg.h defines INT16 and INT32, but only if XMD_H is
-  not defined.  Unfortunately, the MinGW and Borland compilers include
-  a typedef for INT32, which causes a conflict.  MSVC does not include
-  a conflicting typedef given the headers which are included.
-*/
-#if defined(__BORLANDC__) || defined(__MINGW32__)
-# define XMD_H 1
-#endif
-
-/* Define "boolean" as unsigned char, not int, per Windows custom. */
-#if defined(__WIN32__) && !defined(__MINGW32__)
-# ifndef __RPCNDR_H__            /* don't conflict if rpcndr.h already read */
-   typedef unsigned char boolean;
-# endif
-# define HAVE_BOOLEAN            /* prevent jmorecfg.h from redefining it */
-#endif
-
-#if defined(USE_SYSTEM_LIBJPEG)
-#include <jerror.h>
-#include <jpeglib.h>
-#elif defined(USE_LIBJPEG_TURBO)
-#include "third_party/libjpeg_turbo/jerror.h"
-#include "third_party/libjpeg_turbo/jpeglib.h"
-#else
-#include "third_party/libjpeg/jerror.h"
-#include "third_party/libjpeg/jpeglib.h"
-#endif
-
-
-typedef struct jpeg_error_mgr jpeg_error_mgr;
-typedef struct jpeg_common_struct jpeg_common_struct;
-typedef struct jpeg_decompress_struct jpeg_decompress_struct;
-typedef struct jpeg_source_mgr jpeg_source_mgr;
-
-typedef enum {
-	osibsNotSetYet,
-	osibsJpegInterchangeFormat,
-	osibsStrile,
-	osibsEof
-} OJPEGStateInBufferSource;
-
-typedef enum {
-	ososSoi,
-	ososQTable0,ososQTable1,ososQTable2,ososQTable3,
-	ososDcTable0,ososDcTable1,ososDcTable2,ososDcTable3,
-	ososAcTable0,ososAcTable1,ososAcTable2,ososAcTable3,
-	ososDri,
-	ososSof,
-	ososSos,
-	ososCompressed,
-	ososRst,
-	ososEoi
-} OJPEGStateOutState;
-
-typedef struct {
-	TIFF* tif;
-        int decoder_ok;
-        int error_in_raw_data_decoding;
-	#ifndef LIBJPEG_ENCAP_EXTERNAL
-	JMP_BUF exit_jmpbuf;
-	#endif
-	TIFFVGetMethod vgetparent;
-	TIFFVSetMethod vsetparent;
-	TIFFPrintMethod printdir;
-	uint64 file_size;
-	uint32 image_width;
-	uint32 image_length;
-	uint32 strile_width;
-	uint32 strile_length;
-	uint32 strile_length_total;
-	uint8 samples_per_pixel;
-	uint8 plane_sample_offset;
-	uint8 samples_per_pixel_per_plane;
-	uint64 jpeg_interchange_format;
-	uint64 jpeg_interchange_format_length;
-	uint8 jpeg_proc;
-	uint8 subsamplingcorrect;
-	uint8 subsamplingcorrect_done;
-	uint8 subsampling_tag;
-	uint8 subsampling_hor;
-	uint8 subsampling_ver;
-	uint8 subsampling_force_desubsampling_inside_decompression;
-	uint8 qtable_offset_count;
-	uint8 dctable_offset_count;
-	uint8 actable_offset_count;
-	uint64 qtable_offset[3];
-	uint64 dctable_offset[3];
-	uint64 actable_offset[3];
-	uint8* qtable[4];
-	uint8* dctable[4];
-	uint8* actable[4];
-	uint16 restart_interval;
-	uint8 restart_index;
-	uint8 sof_log;
-	uint8 sof_marker_id;
-	uint32 sof_x;
-	uint32 sof_y;
-	uint8 sof_c[3];
-	uint8 sof_hv[3];
-	uint8 sof_tq[3];
-	uint8 sos_cs[3];
-	uint8 sos_tda[3];
-	struct {
-		uint8 log;
-		OJPEGStateInBufferSource in_buffer_source;
-		uint32 in_buffer_next_strile;
-		uint64 in_buffer_file_pos;
-		uint64 in_buffer_file_togo;
-	} sos_end[3];
-	uint8 readheader_done;
-	uint8 writeheader_done;
-	uint16 write_cursample;
-	uint32 write_curstrile;
-	uint8 libjpeg_session_active;
-	uint8 libjpeg_jpeg_query_style;
-	jpeg_error_mgr libjpeg_jpeg_error_mgr;
-	jpeg_decompress_struct libjpeg_jpeg_decompress_struct;
-	jpeg_source_mgr libjpeg_jpeg_source_mgr;
-	uint8 subsampling_convert_log;
-	uint32 subsampling_convert_ylinelen;
-	uint32 subsampling_convert_ylines;
-	uint32 subsampling_convert_clinelen;
-	uint32 subsampling_convert_clines;
-	uint32 subsampling_convert_ybuflen;
-	uint32 subsampling_convert_cbuflen;
-	uint32 subsampling_convert_ycbcrbuflen;
-	uint8* subsampling_convert_ycbcrbuf;
-	uint8* subsampling_convert_ybuf;
-	uint8* subsampling_convert_cbbuf;
-	uint8* subsampling_convert_crbuf;
-	uint32 subsampling_convert_ycbcrimagelen;
-	uint8** subsampling_convert_ycbcrimage;
-	uint32 subsampling_convert_clinelenout;
-	uint32 subsampling_convert_state;
-	uint32 bytes_per_line;   /* if the codec outputs subsampled data, a 'line' in bytes_per_line */
-	uint32 lines_per_strile; /* and lines_per_strile means subsampling_ver desubsampled rows     */
-	OJPEGStateInBufferSource in_buffer_source;
-	uint32 in_buffer_next_strile;
-	uint32 in_buffer_strile_count;
-	uint64 in_buffer_file_pos;
-	uint8 in_buffer_file_pos_log;
-	uint64 in_buffer_file_togo;
-	uint16 in_buffer_togo;
-	uint8* in_buffer_cur;
-	uint8 in_buffer[OJPEG_BUFFER];
-	OJPEGStateOutState out_state;
-	uint8 out_buffer[OJPEG_BUFFER];
-	uint8* skip_buffer;
-} OJPEGState;
-
-static int OJPEGVGetField(TIFF* tif, uint32 tag, va_list ap);
-static int OJPEGVSetField(TIFF* tif, uint32 tag, va_list ap);
-static void OJPEGPrintDir(TIFF* tif, FILE* fd, long flags);
-
-static int OJPEGFixupTags(TIFF* tif);
-static int OJPEGSetupDecode(TIFF* tif);
-static int OJPEGPreDecode(TIFF* tif, uint16 s);
-static int OJPEGPreDecodeSkipRaw(TIFF* tif);
-static int OJPEGPreDecodeSkipScanlines(TIFF* tif);
-static int OJPEGDecode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s);
-static int OJPEGDecodeRaw(TIFF* tif, uint8* buf, tmsize_t cc);
-static int OJPEGDecodeScanlines(TIFF* tif, uint8* buf, tmsize_t cc);
-static void OJPEGPostDecode(TIFF* tif, uint8* buf, tmsize_t cc);
-static int OJPEGSetupEncode(TIFF* tif);
-static int OJPEGPreEncode(TIFF* tif, uint16 s);
-static int OJPEGEncode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s);
-static int OJPEGPostEncode(TIFF* tif);
-static void OJPEGCleanup(TIFF* tif);
-
-static void OJPEGSubsamplingCorrect(TIFF* tif);
-static int OJPEGReadHeaderInfo(TIFF* tif);
-static int OJPEGReadSecondarySos(TIFF* tif, uint16 s);
-static int OJPEGWriteHeaderInfo(TIFF* tif);
-static void OJPEGLibjpegSessionAbort(TIFF* tif);
-
-static int OJPEGReadHeaderInfoSec(TIFF* tif);
-static int OJPEGReadHeaderInfoSecStreamDri(TIFF* tif);
-static int OJPEGReadHeaderInfoSecStreamDqt(TIFF* tif);
-static int OJPEGReadHeaderInfoSecStreamDht(TIFF* tif);
-static int OJPEGReadHeaderInfoSecStreamSof(TIFF* tif, uint8 marker_id);
-static int OJPEGReadHeaderInfoSecStreamSos(TIFF* tif);
-static int OJPEGReadHeaderInfoSecTablesQTable(TIFF* tif);
-static int OJPEGReadHeaderInfoSecTablesDcTable(TIFF* tif);
-static int OJPEGReadHeaderInfoSecTablesAcTable(TIFF* tif);
-
-static int OJPEGReadBufferFill(OJPEGState* sp);
-static int OJPEGReadByte(OJPEGState* sp, uint8* byte);
-static int OJPEGReadBytePeek(OJPEGState* sp, uint8* byte);
-static void OJPEGReadByteAdvance(OJPEGState* sp);
-static int OJPEGReadWord(OJPEGState* sp, uint16* word);
-static int OJPEGReadBlock(OJPEGState* sp, uint16 len, void* mem);
-static void OJPEGReadSkip(OJPEGState* sp, uint16 len);
-
-static int OJPEGWriteStream(TIFF* tif, void** mem, uint32* len);
-static void OJPEGWriteStreamSoi(TIFF* tif, void** mem, uint32* len);
-static void OJPEGWriteStreamQTable(TIFF* tif, uint8 table_index, void** mem, uint32* len);
-static void OJPEGWriteStreamDcTable(TIFF* tif, uint8 table_index, void** mem, uint32* len);
-static void OJPEGWriteStreamAcTable(TIFF* tif, uint8 table_index, void** mem, uint32* len);
-static void OJPEGWriteStreamDri(TIFF* tif, void** mem, uint32* len);
-static void OJPEGWriteStreamSof(TIFF* tif, void** mem, uint32* len);
-static void OJPEGWriteStreamSos(TIFF* tif, void** mem, uint32* len);
-static int OJPEGWriteStreamCompressed(TIFF* tif, void** mem, uint32* len);
-static void OJPEGWriteStreamRst(TIFF* tif, void** mem, uint32* len);
-static void OJPEGWriteStreamEoi(TIFF* tif, void** mem, uint32* len);
-
-#ifdef LIBJPEG_ENCAP_EXTERNAL
-extern int jpeg_create_decompress_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo);
-extern int jpeg_read_header_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, uint8 require_image);
-extern int jpeg_start_decompress_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo);
-extern int jpeg_read_scanlines_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, void* scanlines, uint32 max_lines);
-extern int jpeg_read_raw_data_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, void* data, uint32 max_lines);
-extern void jpeg_encap_unwind(TIFF* tif);
-#else
-static int jpeg_create_decompress_encap(OJPEGState* sp, jpeg_decompress_struct* j);
-static int jpeg_read_header_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, uint8 require_image);
-static int jpeg_start_decompress_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo);
-static int jpeg_read_scanlines_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, void* scanlines, uint32 max_lines);
-static int jpeg_read_raw_data_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, void* data, uint32 max_lines);
-static void jpeg_encap_unwind(TIFF* tif);
-#endif
-
-static void OJPEGLibjpegJpegErrorMgrOutputMessage(jpeg_common_struct* cinfo);
-static void OJPEGLibjpegJpegErrorMgrErrorExit(jpeg_common_struct* cinfo);
-static void OJPEGLibjpegJpegSourceMgrInitSource(jpeg_decompress_struct* cinfo);
-static boolean OJPEGLibjpegJpegSourceMgrFillInputBuffer(jpeg_decompress_struct* cinfo);
-static void OJPEGLibjpegJpegSourceMgrSkipInputData(jpeg_decompress_struct* cinfo, long num_bytes);
-static boolean OJPEGLibjpegJpegSourceMgrResyncToRestart(jpeg_decompress_struct* cinfo, int desired);
-static void OJPEGLibjpegJpegSourceMgrTermSource(jpeg_decompress_struct* cinfo);
-
-int
-TIFFInitOJPEG(TIFF* tif, int scheme)
-{
-	static const char module[]="TIFFInitOJPEG";
-	OJPEGState* sp;
-
-	assert(scheme==COMPRESSION_OJPEG);
-
-        /*
-	 * Merge codec-specific tag information.
-	 */
-	if (!_TIFFMergeFields(tif, ojpegFields, TIFFArrayCount(ojpegFields))) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "Merging Old JPEG codec-specific tags failed");
-		return 0;
-	}
-
-	/* state block */
-	sp=_TIFFmalloc(sizeof(OJPEGState));
-	if (sp==NULL)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"No space for OJPEG state block");
-		return(0);
-	}
-	_TIFFmemset(sp,0,sizeof(OJPEGState));
-	sp->tif=tif;
-	sp->jpeg_proc=1;
-	sp->subsampling_hor=2;
-	sp->subsampling_ver=2;
-	TIFFSetField(tif,TIFFTAG_YCBCRSUBSAMPLING,2,2);
-	/* tif codec methods */
-	tif->tif_fixuptags=OJPEGFixupTags;  
-	tif->tif_setupdecode=OJPEGSetupDecode;
-	tif->tif_predecode=OJPEGPreDecode;
-	tif->tif_postdecode=OJPEGPostDecode;  
-	tif->tif_decoderow=OJPEGDecode;  
-	tif->tif_decodestrip=OJPEGDecode;  
-	tif->tif_decodetile=OJPEGDecode;  
-	tif->tif_setupencode=OJPEGSetupEncode;
-	tif->tif_preencode=OJPEGPreEncode;
-	tif->tif_postencode=OJPEGPostEncode;
-	tif->tif_encoderow=OJPEGEncode;  
-	tif->tif_encodestrip=OJPEGEncode;  
-	tif->tif_encodetile=OJPEGEncode;  
-	tif->tif_cleanup=OJPEGCleanup;
-	tif->tif_data=(uint8*)sp;
-	/* tif tag methods */
-	sp->vgetparent=tif->tif_tagmethods.vgetfield;
-	tif->tif_tagmethods.vgetfield=OJPEGVGetField;
-	sp->vsetparent=tif->tif_tagmethods.vsetfield;
-	tif->tif_tagmethods.vsetfield=OJPEGVSetField;
-	sp->printdir=tif->tif_tagmethods.printdir;
-	tif->tif_tagmethods.printdir=OJPEGPrintDir;
-	/* Some OJPEG files don't have strip or tile offsets or bytecounts tags.
-	   Some others do, but have totally meaningless or corrupt values
-	   in these tags. In these cases, the JpegInterchangeFormat stream is
-	   reliable. In any case, this decoder reads the compressed data itself,
-	   from the most reliable locations, and we need to notify encapsulating
-	   LibTiff not to read raw strips or tiles for us. */
-	tif->tif_flags|=TIFF_NOREADRAW;
-	return(1);
-}
-
-static int
-OJPEGVGetField(TIFF* tif, uint32 tag, va_list ap)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	switch(tag)
-	{
-		case TIFFTAG_JPEGIFOFFSET:
-			*va_arg(ap,uint64*)=(uint64)sp->jpeg_interchange_format;
-			break;
-		case TIFFTAG_JPEGIFBYTECOUNT:
-			*va_arg(ap,uint64*)=(uint64)sp->jpeg_interchange_format_length;
-			break;
-		case TIFFTAG_YCBCRSUBSAMPLING:
-			if (sp->subsamplingcorrect_done==0)
-				OJPEGSubsamplingCorrect(tif);
-			*va_arg(ap,uint16*)=(uint16)sp->subsampling_hor;
-			*va_arg(ap,uint16*)=(uint16)sp->subsampling_ver;
-			break;
-		case TIFFTAG_JPEGQTABLES:
-			*va_arg(ap,uint32*)=(uint32)sp->qtable_offset_count;
-			*va_arg(ap,void**)=(void*)sp->qtable_offset; 
-			break;
-		case TIFFTAG_JPEGDCTABLES:
-			*va_arg(ap,uint32*)=(uint32)sp->dctable_offset_count;
-			*va_arg(ap,void**)=(void*)sp->dctable_offset;  
-			break;
-		case TIFFTAG_JPEGACTABLES:
-			*va_arg(ap,uint32*)=(uint32)sp->actable_offset_count;
-			*va_arg(ap,void**)=(void*)sp->actable_offset;
-			break;
-		case TIFFTAG_JPEGPROC:
-			*va_arg(ap,uint16*)=(uint16)sp->jpeg_proc;
-			break;
-		case TIFFTAG_JPEGRESTARTINTERVAL:
-			*va_arg(ap,uint16*)=sp->restart_interval;
-			break;
-		default:
-			return (*sp->vgetparent)(tif,tag,ap);
-	}
-	return (1);
-}
-
-static int
-OJPEGVSetField(TIFF* tif, uint32 tag, va_list ap)
-{
-	static const char module[]="OJPEGVSetField";
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint32 ma;
-	uint64* mb;
-	uint32 n;
-	const TIFFField* fip;
-
-	switch(tag)
-	{
-		case TIFFTAG_JPEGIFOFFSET:
-			sp->jpeg_interchange_format=(uint64)va_arg(ap,uint64);
-			break;
-		case TIFFTAG_JPEGIFBYTECOUNT:
-			sp->jpeg_interchange_format_length=(uint64)va_arg(ap,uint64);
-			break;
-		case TIFFTAG_YCBCRSUBSAMPLING:
-			sp->subsampling_tag=1;
-			sp->subsampling_hor=(uint8)va_arg(ap,uint16_vap);
-			sp->subsampling_ver=(uint8)va_arg(ap,uint16_vap);
-			tif->tif_dir.td_ycbcrsubsampling[0]=sp->subsampling_hor;
-			tif->tif_dir.td_ycbcrsubsampling[1]=sp->subsampling_ver;
-			break;
-		case TIFFTAG_JPEGQTABLES:
-			ma=(uint32)va_arg(ap,uint32);
-			if (ma!=0)
-			{
-				if (ma>3)
-				{
-					TIFFErrorExt(tif->tif_clientdata,module,"JpegQTables tag has incorrect count");
-					return(0);
-				}
-				sp->qtable_offset_count=(uint8)ma;
-				mb=(uint64*)va_arg(ap,uint64*);
-				for (n=0; n<ma; n++)
-					sp->qtable_offset[n]=mb[n];
-			}
-			break;
-		case TIFFTAG_JPEGDCTABLES:
-			ma=(uint32)va_arg(ap,uint32);
-			if (ma!=0)
-			{
-				if (ma>3)
-				{
-					TIFFErrorExt(tif->tif_clientdata,module,"JpegDcTables tag has incorrect count");
-					return(0);
-				}
-				sp->dctable_offset_count=(uint8)ma;
-				mb=(uint64*)va_arg(ap,uint64*);
-				for (n=0; n<ma; n++)
-					sp->dctable_offset[n]=mb[n];
-			}
-			break;
-		case TIFFTAG_JPEGACTABLES:
-			ma=(uint32)va_arg(ap,uint32);
-			if (ma!=0)
-			{
-				if (ma>3)
-				{
-					TIFFErrorExt(tif->tif_clientdata,module,"JpegAcTables tag has incorrect count");
-					return(0);
-				}
-				sp->actable_offset_count=(uint8)ma;
-				mb=(uint64*)va_arg(ap,uint64*);
-				for (n=0; n<ma; n++)
-					sp->actable_offset[n]=mb[n];
-			}
-			break;
-		case TIFFTAG_JPEGPROC:
-			sp->jpeg_proc=(uint8)va_arg(ap,uint16_vap);
-			break;
-		case TIFFTAG_JPEGRESTARTINTERVAL:
-			sp->restart_interval=(uint16)va_arg(ap,uint16_vap);
-			break;
-		default:
-			return (*sp->vsetparent)(tif,tag,ap);
-	}
-	fip = TIFFFieldWithTag(tif,tag);
-	if( fip == NULL ) /* shouldn't happen */
-	    return(0);
-	TIFFSetFieldBit(tif,fip->field_bit);
-	tif->tif_flags|=TIFF_DIRTYDIRECT;
-	return(1);
-}
-
-static void
-OJPEGPrintDir(TIFF* tif, FILE* fd, long flags)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint8 m;
-	(void)flags;
-	assert(sp!=NULL);
-	if (TIFFFieldSet(tif,FIELD_OJPEG_JPEGINTERCHANGEFORMAT))
-		fprintf(fd,"  JpegInterchangeFormat: " TIFF_UINT64_FORMAT "\n",(TIFF_UINT64_T)sp->jpeg_interchange_format);  
-	if (TIFFFieldSet(tif,FIELD_OJPEG_JPEGINTERCHANGEFORMATLENGTH))
-		fprintf(fd,"  JpegInterchangeFormatLength: " TIFF_UINT64_FORMAT "\n",(TIFF_UINT64_T)sp->jpeg_interchange_format_length);  
-	if (TIFFFieldSet(tif,FIELD_OJPEG_JPEGQTABLES))
-	{
-		fprintf(fd,"  JpegQTables:");
-		for (m=0; m<sp->qtable_offset_count; m++)
-			fprintf(fd," " TIFF_UINT64_FORMAT,(TIFF_UINT64_T)sp->qtable_offset[m]);
-		fprintf(fd,"\n");
-	}
-	if (TIFFFieldSet(tif,FIELD_OJPEG_JPEGDCTABLES))
-	{
-		fprintf(fd,"  JpegDcTables:");
-		for (m=0; m<sp->dctable_offset_count; m++)
-			fprintf(fd," " TIFF_UINT64_FORMAT,(TIFF_UINT64_T)sp->dctable_offset[m]);
-		fprintf(fd,"\n");
-	}
-	if (TIFFFieldSet(tif,FIELD_OJPEG_JPEGACTABLES))
-	{
-		fprintf(fd,"  JpegAcTables:");
-		for (m=0; m<sp->actable_offset_count; m++)
-			fprintf(fd," " TIFF_UINT64_FORMAT,(TIFF_UINT64_T)sp->actable_offset[m]);
-		fprintf(fd,"\n");
-	}
-	if (TIFFFieldSet(tif,FIELD_OJPEG_JPEGPROC))
-		fprintf(fd,"  JpegProc: %u\n",(unsigned int)sp->jpeg_proc);
-	if (TIFFFieldSet(tif,FIELD_OJPEG_JPEGRESTARTINTERVAL))
-		fprintf(fd,"  JpegRestartInterval: %u\n",(unsigned int)sp->restart_interval);
-	if (sp->printdir)
-		(*sp->printdir)(tif, fd, flags);
-}
-
-static int
-OJPEGFixupTags(TIFF* tif)
-{
-	(void) tif;
-	return(1);
-}
-
-static int
-OJPEGSetupDecode(TIFF* tif)
-{
-	static const char module[]="OJPEGSetupDecode";
-	TIFFWarningExt(tif->tif_clientdata,module,"Depreciated and troublesome old-style JPEG compression mode, please convert to new-style JPEG compression and notify vendor of writing software");
-	return(1);
-}
-
-static int
-OJPEGPreDecode(TIFF* tif, uint16 s)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint32 m;
-	if (sp->subsamplingcorrect_done==0)
-		OJPEGSubsamplingCorrect(tif);
-	if (sp->readheader_done==0)
-	{
-		if (OJPEGReadHeaderInfo(tif)==0)
-			return(0);
-	}
-	if (sp->sos_end[s].log==0)
-	{
-		if (OJPEGReadSecondarySos(tif,s)==0)
-			return(0);
-	}
-	if (isTiled(tif))
-		m=tif->tif_curtile;
-	else
-		m=tif->tif_curstrip;
-	if ((sp->writeheader_done!=0) && ((sp->write_cursample!=s) || (sp->write_curstrile>m)))
-	{
-		if (sp->libjpeg_session_active!=0)
-			OJPEGLibjpegSessionAbort(tif);
-		sp->writeheader_done=0;
-	}
-	if (sp->writeheader_done==0)
-	{
-		sp->plane_sample_offset=(uint8)s;
-		sp->write_cursample=s;
-		sp->write_curstrile=s*tif->tif_dir.td_stripsperimage;
-		if ((sp->in_buffer_file_pos_log==0) ||
-		    (sp->in_buffer_file_pos-sp->in_buffer_togo!=sp->sos_end[s].in_buffer_file_pos))
-		{
-			sp->in_buffer_source=sp->sos_end[s].in_buffer_source;
-			sp->in_buffer_next_strile=sp->sos_end[s].in_buffer_next_strile;
-			sp->in_buffer_file_pos=sp->sos_end[s].in_buffer_file_pos;
-			sp->in_buffer_file_pos_log=0;
-			sp->in_buffer_file_togo=sp->sos_end[s].in_buffer_file_togo;
-			sp->in_buffer_togo=0;
-			sp->in_buffer_cur=0;
-		}
-		if (OJPEGWriteHeaderInfo(tif)==0)
-			return(0);
-	}
-	while (sp->write_curstrile<m)          
-	{
-		if (sp->libjpeg_jpeg_query_style==0)
-		{
-			if (OJPEGPreDecodeSkipRaw(tif)==0)
-				return(0);
-		}
-		else
-		{
-			if (OJPEGPreDecodeSkipScanlines(tif)==0)
-				return(0);
-		}
-		sp->write_curstrile++;
-	}
-	sp->decoder_ok = 1;
-	return(1);
-}
-
-static int
-OJPEGPreDecodeSkipRaw(TIFF* tif)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint32 m;
-	m=sp->lines_per_strile;
-	if (sp->subsampling_convert_state!=0)
-	{
-		if (sp->subsampling_convert_clines-sp->subsampling_convert_state>=m)
-		{
-			sp->subsampling_convert_state+=m;
-			if (sp->subsampling_convert_state==sp->subsampling_convert_clines)
-				sp->subsampling_convert_state=0;
-			return(1);
-		}
-		m-=sp->subsampling_convert_clines-sp->subsampling_convert_state;
-		sp->subsampling_convert_state=0;
-                sp->error_in_raw_data_decoding=0;
-	}
-	while (m>=sp->subsampling_convert_clines)
-	{
-		if (jpeg_read_raw_data_encap(sp,&(sp->libjpeg_jpeg_decompress_struct),sp->subsampling_convert_ycbcrimage,sp->subsampling_ver*8)==0)
-			return(0);
-		m-=sp->subsampling_convert_clines;
-	}
-	if (m>0)
-	{
-		if (jpeg_read_raw_data_encap(sp,&(sp->libjpeg_jpeg_decompress_struct),sp->subsampling_convert_ycbcrimage,sp->subsampling_ver*8)==0)
-			return(0);
-		sp->subsampling_convert_state=m;
-	}
-	return(1);
-}
-
-static int
-OJPEGPreDecodeSkipScanlines(TIFF* tif)
-{
-	static const char module[]="OJPEGPreDecodeSkipScanlines";
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint32 m;
-	if (sp->skip_buffer==NULL)
-	{
-		sp->skip_buffer=_TIFFmalloc(sp->bytes_per_line);
-		if (sp->skip_buffer==NULL)
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-			return(0);
-		}
-	}
-	for (m=0; m<sp->lines_per_strile; m++)
-	{
-		if (jpeg_read_scanlines_encap(sp,&(sp->libjpeg_jpeg_decompress_struct),&sp->skip_buffer,1)==0)
-			return(0);
-	}
-	return(1);
-}
-
-static int
-OJPEGDecode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s)
-{
-        static const char module[]="OJPEGDecode";
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	(void)s;
-        if( !sp->decoder_ok )
-        {
-            TIFFErrorExt(tif->tif_clientdata,module,"Cannot decode: decoder not correctly initialized");
-            return 0;
-        }
-        if( sp->error_in_raw_data_decoding )
-        {
-            return 0;
-        }
-	if (sp->libjpeg_jpeg_query_style==0)
-	{
-		if (OJPEGDecodeRaw(tif,buf,cc)==0)
-			return(0);
-	}
-	else
-	{
-		if (OJPEGDecodeScanlines(tif,buf,cc)==0)
-			return(0);
-	}
-	return(1);
-}
-
-static int
-OJPEGDecodeRaw(TIFF* tif, uint8* buf, tmsize_t cc)
-{
-	static const char module[]="OJPEGDecodeRaw";
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint8* m;
-	tmsize_t n;
-	uint8* oy;
-	uint8* ocb;
-	uint8* ocr;
-	uint8* p;
-	uint32 q;
-	uint8* r;
-	uint8 sx,sy;
-	if (cc%sp->bytes_per_line!=0)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Fractional scanline not read");
-		return(0);
-	}
-	assert(cc>0);
-	m=buf;
-	n=cc;
-	do
-	{
-		if (sp->subsampling_convert_state==0)
-		{
-			const jpeg_decompress_struct* cinfo = &sp->libjpeg_jpeg_decompress_struct;
-			int width = 0;
-			int last_col_width = 0;
-			int jpeg_bytes;
-			int expected_bytes;
-			int i;
-			if (cinfo->MCUs_per_row == 0)
-			{
-				sp->error_in_raw_data_decoding = 1;
-				return 0;
-			}
-			for (i = 0; i < cinfo->comps_in_scan; ++i)
-			{
-				const jpeg_component_info* info = cinfo->cur_comp_info[i];
-#if JPEG_LIB_VERSION >= 70
-				width += info->MCU_width * info->DCT_h_scaled_size;
-				last_col_width += info->last_col_width * info->DCT_h_scaled_size;
-#else
-				width += info->MCU_width * info->DCT_scaled_size;
-				last_col_width += info->last_col_width * info->DCT_scaled_size;
-#endif
-			}
-			jpeg_bytes = (cinfo->MCUs_per_row - 1) * width + last_col_width;
-			expected_bytes = sp->subsampling_convert_clinelenout * sp->subsampling_ver * sp->subsampling_hor;
-			if (jpeg_bytes != expected_bytes)
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Inconsistent number of MCU in codestream");
-				sp->error_in_raw_data_decoding = 1;
-				return(0);
-			}
-			if (jpeg_read_raw_data_encap(sp,&(sp->libjpeg_jpeg_decompress_struct),sp->subsampling_convert_ycbcrimage,sp->subsampling_ver*8)==0)
-			{
-				sp->error_in_raw_data_decoding = 1;
-				return(0);
-			}
-		}
-		oy=sp->subsampling_convert_ybuf+sp->subsampling_convert_state*sp->subsampling_ver*sp->subsampling_convert_ylinelen;
-		ocb=sp->subsampling_convert_cbbuf+sp->subsampling_convert_state*sp->subsampling_convert_clinelen;
-		ocr=sp->subsampling_convert_crbuf+sp->subsampling_convert_state*sp->subsampling_convert_clinelen;
-		p=m;
-		for (q=0; q<sp->subsampling_convert_clinelenout; q++)
-		{
-			r=oy;
-			for (sy=0; sy<sp->subsampling_ver; sy++)
-			{
-				for (sx=0; sx<sp->subsampling_hor; sx++)
-					*p++=*r++;
-				r+=sp->subsampling_convert_ylinelen-sp->subsampling_hor;
-			}
-			oy+=sp->subsampling_hor;
-			*p++=*ocb++;
-			*p++=*ocr++;
-		}
-		sp->subsampling_convert_state++;
-		if (sp->subsampling_convert_state==sp->subsampling_convert_clines)
-			sp->subsampling_convert_state=0;
-		m+=sp->bytes_per_line;
-		n-=sp->bytes_per_line;
-	} while(n>0);
-	return(1);
-}
-
-static int
-OJPEGDecodeScanlines(TIFF* tif, uint8* buf, tmsize_t cc)
-{
-	static const char module[]="OJPEGDecodeScanlines";
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint8* m;
-	tmsize_t n;
-	if (cc%sp->bytes_per_line!=0)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Fractional scanline not read");
-		return(0);
-	}
-	assert(cc>0);
-	m=buf;
-	n=cc;
-	do
-	{
-		if (jpeg_read_scanlines_encap(sp,&(sp->libjpeg_jpeg_decompress_struct),&m,1)==0)
-			return(0);
-		m+=sp->bytes_per_line;
-		n-=sp->bytes_per_line;
-	} while(n>0);
-	return(1);
-}
-
-static void
-OJPEGPostDecode(TIFF* tif, uint8* buf, tmsize_t cc)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	(void)buf;
-	(void)cc;
-	sp->write_curstrile++;
-	if (sp->write_curstrile%tif->tif_dir.td_stripsperimage==0)  
-	{
-		assert(sp->libjpeg_session_active!=0);
-		OJPEGLibjpegSessionAbort(tif);
-		sp->writeheader_done=0;
-	}
-}
-
-static int
-OJPEGSetupEncode(TIFF* tif)
-{
-	static const char module[]="OJPEGSetupEncode";
-	TIFFErrorExt(tif->tif_clientdata,module,"OJPEG encoding not supported; use new-style JPEG compression instead");
-	return(0);
-}
-
-static int
-OJPEGPreEncode(TIFF* tif, uint16 s)
-{
-	static const char module[]="OJPEGPreEncode";
-	(void)s;
-	TIFFErrorExt(tif->tif_clientdata,module,"OJPEG encoding not supported; use new-style JPEG compression instead");
-	return(0);
-}
-
-static int
-OJPEGEncode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s)
-{
-	static const char module[]="OJPEGEncode";
-	(void)buf;
-	(void)cc;
-	(void)s;
-	TIFFErrorExt(tif->tif_clientdata,module,"OJPEG encoding not supported; use new-style JPEG compression instead");
-	return(0);
-}
-
-static int
-OJPEGPostEncode(TIFF* tif)
-{
-	static const char module[]="OJPEGPostEncode";
-	TIFFErrorExt(tif->tif_clientdata,module,"OJPEG encoding not supported; use new-style JPEG compression instead");
-	return(0);
-}
-
-static void
-OJPEGCleanup(TIFF* tif)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	if (sp!=0)
-	{
-		tif->tif_tagmethods.vgetfield=sp->vgetparent;
-		tif->tif_tagmethods.vsetfield=sp->vsetparent;
-		tif->tif_tagmethods.printdir=sp->printdir;
-		if (sp->qtable[0]!=0)
-			_TIFFfree(sp->qtable[0]);
-		if (sp->qtable[1]!=0)
-			_TIFFfree(sp->qtable[1]);
-		if (sp->qtable[2]!=0)
-			_TIFFfree(sp->qtable[2]);
-		if (sp->qtable[3]!=0)
-			_TIFFfree(sp->qtable[3]);
-		if (sp->dctable[0]!=0)
-			_TIFFfree(sp->dctable[0]);
-		if (sp->dctable[1]!=0)
-			_TIFFfree(sp->dctable[1]);
-		if (sp->dctable[2]!=0)
-			_TIFFfree(sp->dctable[2]);
-		if (sp->dctable[3]!=0)
-			_TIFFfree(sp->dctable[3]);
-		if (sp->actable[0]!=0)
-			_TIFFfree(sp->actable[0]);
-		if (sp->actable[1]!=0)
-			_TIFFfree(sp->actable[1]);
-		if (sp->actable[2]!=0)
-			_TIFFfree(sp->actable[2]);
-		if (sp->actable[3]!=0)
-			_TIFFfree(sp->actable[3]);
-		if (sp->libjpeg_session_active!=0)
-			OJPEGLibjpegSessionAbort(tif);
-		if (sp->subsampling_convert_ycbcrbuf!=0)
-			_TIFFfree(sp->subsampling_convert_ycbcrbuf);
-		if (sp->subsampling_convert_ycbcrimage!=0)
-			_TIFFfree(sp->subsampling_convert_ycbcrimage);
-		if (sp->skip_buffer!=0)
-			_TIFFfree(sp->skip_buffer);
-		_TIFFfree(sp);
-		tif->tif_data=NULL;
-		_TIFFSetDefaultCompressionState(tif);
-	}
-}
-
-static void
-OJPEGSubsamplingCorrect(TIFF* tif)
-{
-	static const char module[]="OJPEGSubsamplingCorrect";
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint8 mh;
-	uint8 mv;
-        
-	assert(sp->subsamplingcorrect_done==0);
-	if ((tif->tif_dir.td_samplesperpixel!=3) || ((tif->tif_dir.td_photometric!=PHOTOMETRIC_YCBCR) &&
-	    (tif->tif_dir.td_photometric!=PHOTOMETRIC_ITULAB)))
-	{
-		if (sp->subsampling_tag!=0)
-			TIFFWarningExt(tif->tif_clientdata,module,"Subsampling tag not appropriate for this Photometric and/or SamplesPerPixel");
-		sp->subsampling_hor=1;
-		sp->subsampling_ver=1;
-		sp->subsampling_force_desubsampling_inside_decompression=0;
-	}
-	else
-	{
-		sp->subsamplingcorrect_done=1;
-		mh=sp->subsampling_hor;
-		mv=sp->subsampling_ver;
-		sp->subsamplingcorrect=1;
-		OJPEGReadHeaderInfoSec(tif);
-		if (sp->subsampling_force_desubsampling_inside_decompression!=0)
-		{
-			sp->subsampling_hor=1;
-			sp->subsampling_ver=1;
-		}
-		sp->subsamplingcorrect=0;
-		if (((sp->subsampling_hor!=mh) || (sp->subsampling_ver!=mv)) && (sp->subsampling_force_desubsampling_inside_decompression==0))
-		{
-			if (sp->subsampling_tag==0)
-				TIFFWarningExt(tif->tif_clientdata,module,"Subsampling tag is not set, yet subsampling inside JPEG data [%d,%d] does not match default values [2,2]; assuming subsampling inside JPEG data is correct",sp->subsampling_hor,sp->subsampling_ver);
-			else
-				TIFFWarningExt(tif->tif_clientdata,module,"Subsampling inside JPEG data [%d,%d] does not match subsampling tag values [%d,%d]; assuming subsampling inside JPEG data is correct",sp->subsampling_hor,sp->subsampling_ver,mh,mv);
-		}
-		if (sp->subsampling_force_desubsampling_inside_decompression!=0)
-		{
-			if (sp->subsampling_tag==0)
-				TIFFWarningExt(tif->tif_clientdata,module,"Subsampling tag is not set, yet subsampling inside JPEG data does not match default values [2,2] (nor any other values allowed in TIFF); assuming subsampling inside JPEG data is correct and desubsampling inside JPEG decompression");
-			else
-				TIFFWarningExt(tif->tif_clientdata,module,"Subsampling inside JPEG data does not match subsampling tag values [%d,%d] (nor any other values allowed in TIFF); assuming subsampling inside JPEG data is correct and desubsampling inside JPEG decompression",mh,mv);
-		}
-		if (sp->subsampling_force_desubsampling_inside_decompression==0)
-		{
-			if (sp->subsampling_hor<sp->subsampling_ver)
-				TIFFWarningExt(tif->tif_clientdata,module,"Subsampling values [%d,%d] are not allowed in TIFF",sp->subsampling_hor,sp->subsampling_ver);
-		}
-	}
-	sp->subsamplingcorrect_done=1;
-}
-
-static int
-OJPEGReadHeaderInfo(TIFF* tif)
-{
-	static const char module[]="OJPEGReadHeaderInfo";
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	assert(sp->readheader_done==0);
-	sp->image_width=tif->tif_dir.td_imagewidth;
-	sp->image_length=tif->tif_dir.td_imagelength;
-	if (isTiled(tif))
-	{
-		sp->strile_width=tif->tif_dir.td_tilewidth;
-		sp->strile_length=tif->tif_dir.td_tilelength;
-		sp->strile_length_total=((sp->image_length+sp->strile_length-1)/sp->strile_length)*sp->strile_length;
-	}
-	else
-	{
-		sp->strile_width=sp->image_width;
-		sp->strile_length=tif->tif_dir.td_rowsperstrip;
-		sp->strile_length_total=sp->image_length;
-	}
-	if (tif->tif_dir.td_samplesperpixel==1)
-	{
-		sp->samples_per_pixel=1;
-		sp->plane_sample_offset=0;
-		sp->samples_per_pixel_per_plane=sp->samples_per_pixel;
-		sp->subsampling_hor=1;
-		sp->subsampling_ver=1;
-	}
-	else
-	{
-		if (tif->tif_dir.td_samplesperpixel!=3)
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"SamplesPerPixel %d not supported for this compression scheme",sp->samples_per_pixel);
-			return(0);
-		}
-		sp->samples_per_pixel=3;
-		sp->plane_sample_offset=0;
-		if (tif->tif_dir.td_planarconfig==PLANARCONFIG_CONTIG)
-			sp->samples_per_pixel_per_plane=3;
-		else
-			sp->samples_per_pixel_per_plane=1;
-	}
-	if (sp->strile_length<sp->image_length)
-	{
-		if (((sp->subsampling_hor!=1) && (sp->subsampling_hor!=2) && (sp->subsampling_hor!=4)) ||
-		    ((sp->subsampling_ver!=1) && (sp->subsampling_ver!=2) && (sp->subsampling_ver!=4)))
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"Invalid subsampling values");
-			return(0);
-		}
-		if (sp->strile_length%(sp->subsampling_ver*8)!=0)
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"Incompatible vertical subsampling and image strip/tile length");
-			return(0);
-		}
-		sp->restart_interval=(uint16)(((sp->strile_width+sp->subsampling_hor*8-1)/(sp->subsampling_hor*8))*(sp->strile_length/(sp->subsampling_ver*8)));
-	}
-	if (OJPEGReadHeaderInfoSec(tif)==0)
-		return(0);
-	sp->sos_end[0].log=1;
-	sp->sos_end[0].in_buffer_source=sp->in_buffer_source;
-	sp->sos_end[0].in_buffer_next_strile=sp->in_buffer_next_strile;
-	sp->sos_end[0].in_buffer_file_pos=sp->in_buffer_file_pos-sp->in_buffer_togo;
-	sp->sos_end[0].in_buffer_file_togo=sp->in_buffer_file_togo+sp->in_buffer_togo; 
-	sp->readheader_done=1;
-	return(1);
-}
-
-static int
-OJPEGReadSecondarySos(TIFF* tif, uint16 s)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint8 m;
-	assert(s>0);
-	assert(s<3);
-	assert(sp->sos_end[0].log!=0);
-	assert(sp->sos_end[s].log==0);
-	sp->plane_sample_offset=(uint8)(s-1);
-	while(sp->sos_end[sp->plane_sample_offset].log==0)
-		sp->plane_sample_offset--;
-	sp->in_buffer_source=sp->sos_end[sp->plane_sample_offset].in_buffer_source;
-	sp->in_buffer_next_strile=sp->sos_end[sp->plane_sample_offset].in_buffer_next_strile;
-	sp->in_buffer_file_pos=sp->sos_end[sp->plane_sample_offset].in_buffer_file_pos;
-	sp->in_buffer_file_pos_log=0;
-	sp->in_buffer_file_togo=sp->sos_end[sp->plane_sample_offset].in_buffer_file_togo;
-	sp->in_buffer_togo=0;
-	sp->in_buffer_cur=0;
-	while(sp->plane_sample_offset<s)
-	{
-		do
-		{
-			if (OJPEGReadByte(sp,&m)==0)
-				return(0);
-			if (m==255)
-			{
-				do
-				{
-					if (OJPEGReadByte(sp,&m)==0)
-						return(0);
-					if (m!=255)
-						break;
-				} while(1);
-				if (m==JPEG_MARKER_SOS)
-					break;
-			}
-		} while(1);
-		sp->plane_sample_offset++;
-		if (OJPEGReadHeaderInfoSecStreamSos(tif)==0)
-			return(0);
-		sp->sos_end[sp->plane_sample_offset].log=1;
-		sp->sos_end[sp->plane_sample_offset].in_buffer_source=sp->in_buffer_source;
-		sp->sos_end[sp->plane_sample_offset].in_buffer_next_strile=sp->in_buffer_next_strile;
-		sp->sos_end[sp->plane_sample_offset].in_buffer_file_pos=sp->in_buffer_file_pos-sp->in_buffer_togo;
-		sp->sos_end[sp->plane_sample_offset].in_buffer_file_togo=sp->in_buffer_file_togo+sp->in_buffer_togo;
-	}
-	return(1);
-}
-
-static int
-OJPEGWriteHeaderInfo(TIFF* tif)
-{
-	static const char module[]="OJPEGWriteHeaderInfo";
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint8** m;
-	uint32 n;
-	/* if a previous attempt failed, don't try again */
-	if (sp->libjpeg_session_active != 0) 
-		return 0;
-	sp->out_state=ososSoi;
-	sp->restart_index=0;
-	jpeg_std_error(&(sp->libjpeg_jpeg_error_mgr));
-	sp->libjpeg_jpeg_error_mgr.output_message=OJPEGLibjpegJpegErrorMgrOutputMessage;
-	sp->libjpeg_jpeg_error_mgr.error_exit=OJPEGLibjpegJpegErrorMgrErrorExit;
-	sp->libjpeg_jpeg_decompress_struct.err=&(sp->libjpeg_jpeg_error_mgr);
-	sp->libjpeg_jpeg_decompress_struct.client_data=(void*)tif;
-	if (jpeg_create_decompress_encap(sp,&(sp->libjpeg_jpeg_decompress_struct))==0)
-		return(0);
-	sp->libjpeg_session_active=1;
-	sp->libjpeg_jpeg_source_mgr.bytes_in_buffer=0;
-	sp->libjpeg_jpeg_source_mgr.init_source=OJPEGLibjpegJpegSourceMgrInitSource;
-	sp->libjpeg_jpeg_source_mgr.fill_input_buffer=OJPEGLibjpegJpegSourceMgrFillInputBuffer;
-	sp->libjpeg_jpeg_source_mgr.skip_input_data=OJPEGLibjpegJpegSourceMgrSkipInputData;
-	sp->libjpeg_jpeg_source_mgr.resync_to_restart=OJPEGLibjpegJpegSourceMgrResyncToRestart;
-	sp->libjpeg_jpeg_source_mgr.term_source=OJPEGLibjpegJpegSourceMgrTermSource;
-	sp->libjpeg_jpeg_decompress_struct.src=&(sp->libjpeg_jpeg_source_mgr);
-	if (jpeg_read_header_encap(sp,&(sp->libjpeg_jpeg_decompress_struct),1)==0)
-		return(0);
-	if ((sp->subsampling_force_desubsampling_inside_decompression==0) && (sp->samples_per_pixel_per_plane>1))
-	{
-		sp->libjpeg_jpeg_decompress_struct.raw_data_out=1;
-#if JPEG_LIB_VERSION >= 70
-		sp->libjpeg_jpeg_decompress_struct.do_fancy_upsampling=FALSE;
-#endif
-		sp->libjpeg_jpeg_query_style=0;
-		if (sp->subsampling_convert_log==0)
-		{
-			assert(sp->subsampling_convert_ycbcrbuf==0);
-			assert(sp->subsampling_convert_ycbcrimage==0);
-			sp->subsampling_convert_ylinelen=((sp->strile_width+sp->subsampling_hor*8-1)/(sp->subsampling_hor*8)*sp->subsampling_hor*8);
-			sp->subsampling_convert_ylines=sp->subsampling_ver*8;
-			sp->subsampling_convert_clinelen=sp->subsampling_convert_ylinelen/sp->subsampling_hor;
-			sp->subsampling_convert_clines=8;
-			sp->subsampling_convert_ybuflen=sp->subsampling_convert_ylinelen*sp->subsampling_convert_ylines;
-			sp->subsampling_convert_cbuflen=sp->subsampling_convert_clinelen*sp->subsampling_convert_clines;
-			sp->subsampling_convert_ycbcrbuflen=sp->subsampling_convert_ybuflen+2*sp->subsampling_convert_cbuflen;
-                        /* The calloc is not normally necessary, except in some edge/broken cases */
-                        /* for example for a tiled image of height 1 with a tile height of 1 and subsampling_hor=subsampling_ver=2 */
-                        /* In that case, libjpeg will only fill the 8 first lines of the 16 lines */
-                        /* See https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=16844 */
-                        /* Even if this case is allowed (?), its handling is broken because OJPEGPreDecode() should also likely */
-                        /* reset subsampling_convert_state to 0 when changing tile. */
-			sp->subsampling_convert_ycbcrbuf=_TIFFcalloc(1, sp->subsampling_convert_ycbcrbuflen);
-			if (sp->subsampling_convert_ycbcrbuf==0)
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-				return(0);
-			}
-			sp->subsampling_convert_ybuf=sp->subsampling_convert_ycbcrbuf;
-			sp->subsampling_convert_cbbuf=sp->subsampling_convert_ybuf+sp->subsampling_convert_ybuflen;
-			sp->subsampling_convert_crbuf=sp->subsampling_convert_cbbuf+sp->subsampling_convert_cbuflen;
-			sp->subsampling_convert_ycbcrimagelen=3+sp->subsampling_convert_ylines+2*sp->subsampling_convert_clines;
-			sp->subsampling_convert_ycbcrimage=_TIFFmalloc(sp->subsampling_convert_ycbcrimagelen*sizeof(uint8*));
-			if (sp->subsampling_convert_ycbcrimage==0)
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-				return(0);
-			}
-			m=sp->subsampling_convert_ycbcrimage;
-			*m++=(uint8*)(sp->subsampling_convert_ycbcrimage+3);
-			*m++=(uint8*)(sp->subsampling_convert_ycbcrimage+3+sp->subsampling_convert_ylines);
-			*m++=(uint8*)(sp->subsampling_convert_ycbcrimage+3+sp->subsampling_convert_ylines+sp->subsampling_convert_clines);
-			for (n=0; n<sp->subsampling_convert_ylines; n++)
-				*m++=sp->subsampling_convert_ybuf+n*sp->subsampling_convert_ylinelen;
-			for (n=0; n<sp->subsampling_convert_clines; n++)
-				*m++=sp->subsampling_convert_cbbuf+n*sp->subsampling_convert_clinelen;
-			for (n=0; n<sp->subsampling_convert_clines; n++)
-				*m++=sp->subsampling_convert_crbuf+n*sp->subsampling_convert_clinelen;
-			sp->subsampling_convert_clinelenout=sp->strile_width/sp->subsampling_hor + ((sp->strile_width % sp->subsampling_hor) != 0 ? 1 : 0);
-			sp->subsampling_convert_state=0;
-			sp->error_in_raw_data_decoding=0;
-			sp->bytes_per_line=sp->subsampling_convert_clinelenout*(sp->subsampling_ver*sp->subsampling_hor+2);
-			sp->lines_per_strile=sp->strile_length/sp->subsampling_ver + ((sp->strile_length % sp->subsampling_ver) != 0 ? 1 : 0);
-			sp->subsampling_convert_log=1;
-		}
-	}
-	else
-	{
-		sp->libjpeg_jpeg_decompress_struct.jpeg_color_space=JCS_UNKNOWN;
-		sp->libjpeg_jpeg_decompress_struct.out_color_space=JCS_UNKNOWN;
-		sp->libjpeg_jpeg_query_style=1;
-		sp->bytes_per_line=sp->samples_per_pixel_per_plane*sp->strile_width;
-		sp->lines_per_strile=sp->strile_length;
-	}
-	if (jpeg_start_decompress_encap(sp,&(sp->libjpeg_jpeg_decompress_struct))==0)
-		return(0);
-	sp->writeheader_done=1;
-	return(1);
-}
-
-static void
-OJPEGLibjpegSessionAbort(TIFF* tif)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	assert(sp->libjpeg_session_active!=0);
-	jpeg_destroy((jpeg_common_struct*)(&(sp->libjpeg_jpeg_decompress_struct)));
-	sp->libjpeg_session_active=0;
-}
-
-static int
-OJPEGReadHeaderInfoSec(TIFF* tif)
-{
-	static const char module[]="OJPEGReadHeaderInfoSec";
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint8 m;
-	uint16 n;
-	uint8 o;
-	if (sp->file_size==0)
-		sp->file_size=TIFFGetFileSize(tif);
-	if (sp->jpeg_interchange_format!=0)
-	{
-		if (sp->jpeg_interchange_format>=sp->file_size)
-		{
-			sp->jpeg_interchange_format=0;
-			sp->jpeg_interchange_format_length=0;
-		}
-		else
-		{
-			if ((sp->jpeg_interchange_format_length==0) ||
-                            (sp->jpeg_interchange_format > TIFF_UINT64_MAX - sp->jpeg_interchange_format_length) ||
-                            (sp->jpeg_interchange_format+sp->jpeg_interchange_format_length>sp->file_size))
-				sp->jpeg_interchange_format_length=sp->file_size-sp->jpeg_interchange_format;
-		}
-	}
-	sp->in_buffer_source=osibsNotSetYet;
-	sp->in_buffer_next_strile=0;
-	sp->in_buffer_strile_count=tif->tif_dir.td_nstrips;
-	sp->in_buffer_file_togo=0;
-	sp->in_buffer_togo=0;
-	do
-	{
-		if (OJPEGReadBytePeek(sp,&m)==0)
-			return(0);
-		if (m!=255)
-			break;
-		OJPEGReadByteAdvance(sp);
-		do
-		{
-			if (OJPEGReadByte(sp,&m)==0)
-				return(0);
-		} while(m==255);
-		switch(m)
-		{
-			case JPEG_MARKER_SOI:
-				/* this type of marker has no data, and should be skipped */
-				break;
-			case JPEG_MARKER_COM:
-			case JPEG_MARKER_APP0:
-			case JPEG_MARKER_APP0+1:
-			case JPEG_MARKER_APP0+2:
-			case JPEG_MARKER_APP0+3:
-			case JPEG_MARKER_APP0+4:
-			case JPEG_MARKER_APP0+5:
-			case JPEG_MARKER_APP0+6:
-			case JPEG_MARKER_APP0+7:
-			case JPEG_MARKER_APP0+8:
-			case JPEG_MARKER_APP0+9:
-			case JPEG_MARKER_APP0+10:
-			case JPEG_MARKER_APP0+11:
-			case JPEG_MARKER_APP0+12:
-			case JPEG_MARKER_APP0+13:
-			case JPEG_MARKER_APP0+14:
-			case JPEG_MARKER_APP0+15:
-				/* this type of marker has data, but it has no use to us (and no place here) and should be skipped */
-				if (OJPEGReadWord(sp,&n)==0)
-					return(0);
-				if (n<2)
-				{
-					if (sp->subsamplingcorrect==0)
-						TIFFErrorExt(tif->tif_clientdata,module,"Corrupt JPEG data");
-					return(0);
-				}
-				if (n>2)
-					OJPEGReadSkip(sp,n-2);
-				break;
-			case JPEG_MARKER_DRI:
-				if (OJPEGReadHeaderInfoSecStreamDri(tif)==0)
-					return(0);
-				break;
-			case JPEG_MARKER_DQT:
-				if (OJPEGReadHeaderInfoSecStreamDqt(tif)==0)
-					return(0);
-				break;
-			case JPEG_MARKER_DHT:
-				if (OJPEGReadHeaderInfoSecStreamDht(tif)==0)
-					return(0);
-				break;
-			case JPEG_MARKER_SOF0:
-			case JPEG_MARKER_SOF1:
-			case JPEG_MARKER_SOF3:
-				if (OJPEGReadHeaderInfoSecStreamSof(tif,m)==0)
-					return(0);
-				if (sp->subsamplingcorrect!=0)
-					return(1);
-				break;
-			case JPEG_MARKER_SOS:
-				if (sp->subsamplingcorrect!=0)
-					return(1);
-				assert(sp->plane_sample_offset==0);
-				if (OJPEGReadHeaderInfoSecStreamSos(tif)==0)
-					return(0);
-				break;
-			default:
-				TIFFErrorExt(tif->tif_clientdata,module,"Unknown marker type %d in JPEG data",m);
-				return(0);
-		}
-	} while(m!=JPEG_MARKER_SOS);
-	if (sp->subsamplingcorrect)
-		return(1);
-	if (sp->sof_log==0)
-	{
-		if (OJPEGReadHeaderInfoSecTablesQTable(tif)==0)
-			return(0);
-		sp->sof_marker_id=JPEG_MARKER_SOF0;
-		for (o=0; o<sp->samples_per_pixel; o++)
-			sp->sof_c[o]=o;
-		sp->sof_hv[0]=((sp->subsampling_hor<<4)|sp->subsampling_ver);
-		for (o=1; o<sp->samples_per_pixel; o++)
-			sp->sof_hv[o]=17;
-		sp->sof_x=sp->strile_width;
-		sp->sof_y=sp->strile_length_total;
-		sp->sof_log=1;
-		if (OJPEGReadHeaderInfoSecTablesDcTable(tif)==0)
-			return(0);
-		if (OJPEGReadHeaderInfoSecTablesAcTable(tif)==0)
-			return(0);
-		for (o=1; o<sp->samples_per_pixel; o++)
-			sp->sos_cs[o]=o;
-	}
-	return(1);
-}
-
-static int
-OJPEGReadHeaderInfoSecStreamDri(TIFF* tif)
-{
-	/* This could easily cause trouble in some cases... but no such cases have
-           occurred so far */
-	static const char module[]="OJPEGReadHeaderInfoSecStreamDri";
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint16 m;
-	if (OJPEGReadWord(sp,&m)==0)
-		return(0);
-	if (m!=4)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Corrupt DRI marker in JPEG data");
-		return(0);
-	}
-	if (OJPEGReadWord(sp,&m)==0)
-		return(0);
-	sp->restart_interval=m;
-	return(1);
-}
-
-static int
-OJPEGReadHeaderInfoSecStreamDqt(TIFF* tif)
-{
-	/* this is a table marker, and it is to be saved as a whole for exact pushing on the jpeg stream later on */
-	static const char module[]="OJPEGReadHeaderInfoSecStreamDqt";
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint16 m;
-	uint32 na;
-	uint8* nb;
-	uint8 o;
-	if (OJPEGReadWord(sp,&m)==0)
-		return(0);
-	if (m<=2)
-	{
-		if (sp->subsamplingcorrect==0)
-			TIFFErrorExt(tif->tif_clientdata,module,"Corrupt DQT marker in JPEG data");
-		return(0);
-	}
-	if (sp->subsamplingcorrect!=0)
-		OJPEGReadSkip(sp,m-2);
-	else
-	{
-		m-=2;
-		do
-		{
-			if (m<65)
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Corrupt DQT marker in JPEG data");
-				return(0);
-			}
-			na=sizeof(uint32)+69;
-			nb=_TIFFmalloc(na);
-			if (nb==0)
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-				return(0);
-			}
-			*(uint32*)nb=na;
-			nb[sizeof(uint32)]=255;
-			nb[sizeof(uint32)+1]=JPEG_MARKER_DQT;
-			nb[sizeof(uint32)+2]=0;
-			nb[sizeof(uint32)+3]=67;
-			if (OJPEGReadBlock(sp,65,&nb[sizeof(uint32)+4])==0) {
-				_TIFFfree(nb);
-				return(0);
-			}
-			o=nb[sizeof(uint32)+4]&15;
-			if (3<o)
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Corrupt DQT marker in JPEG data");
-				_TIFFfree(nb);
-				return(0);
-			}
-			if (sp->qtable[o]!=0)
-				_TIFFfree(sp->qtable[o]);
-			sp->qtable[o]=nb;
-			m-=65;
-		} while(m>0);
-	}
-	return(1);
-}
-
-static int
-OJPEGReadHeaderInfoSecStreamDht(TIFF* tif)
-{
-	/* this is a table marker, and it is to be saved as a whole for exact pushing on the jpeg stream later on */
-	/* TODO: the following assumes there is only one table in this marker... but i'm not quite sure that assumption is guaranteed correct */
-	static const char module[]="OJPEGReadHeaderInfoSecStreamDht";
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint16 m;
-	uint32 na;
-	uint8* nb;
-	uint8 o;
-	if (OJPEGReadWord(sp,&m)==0)
-		return(0);
-	if (m<=2)
-	{
-		if (sp->subsamplingcorrect==0)
-			TIFFErrorExt(tif->tif_clientdata,module,"Corrupt DHT marker in JPEG data");
-		return(0);
-	}
-	if (sp->subsamplingcorrect!=0)
-	{
-		OJPEGReadSkip(sp,m-2);
-	}
-	else
-	{
-		na=sizeof(uint32)+2+m;
-		nb=_TIFFmalloc(na);
-		if (nb==0)
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-			return(0);
-		}
-		*(uint32*)nb=na;
-		nb[sizeof(uint32)]=255;
-		nb[sizeof(uint32)+1]=JPEG_MARKER_DHT;
-		nb[sizeof(uint32)+2]=(m>>8);
-		nb[sizeof(uint32)+3]=(m&255);
-		if (OJPEGReadBlock(sp,m-2,&nb[sizeof(uint32)+4])==0) {
-                        _TIFFfree(nb);
-			return(0);
-                }
-		o=nb[sizeof(uint32)+4];
-		if ((o&240)==0)
-		{
-			if (3<o)
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Corrupt DHT marker in JPEG data");
-                                _TIFFfree(nb);
-				return(0);
-			}
-			if (sp->dctable[o]!=0)
-				_TIFFfree(sp->dctable[o]);
-			sp->dctable[o]=nb;
-		}
-		else
-		{
-			if ((o&240)!=16)
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Corrupt DHT marker in JPEG data");
-                                _TIFFfree(nb);
-				return(0);
-			}
-			o&=15;
-			if (3<o)
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Corrupt DHT marker in JPEG data");
-                                _TIFFfree(nb);
-				return(0);
-			}
-			if (sp->actable[o]!=0)
-				_TIFFfree(sp->actable[o]);
-			sp->actable[o]=nb;
-		}
-	}
-	return(1);
-}
-
-static int
-OJPEGReadHeaderInfoSecStreamSof(TIFF* tif, uint8 marker_id)
-{
-	/* this marker needs to be checked, and part of its data needs to be saved for regeneration later on */
-	static const char module[]="OJPEGReadHeaderInfoSecStreamSof";
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint16 m;
-	uint16 n;
-	uint8 o;
-	uint16 p;
-	uint16 q;
-	if (sp->sof_log!=0)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Corrupt JPEG data");
-		return(0);
-	}
-	if (sp->subsamplingcorrect==0)
-		sp->sof_marker_id=marker_id;
-	/* Lf: data length */
-	if (OJPEGReadWord(sp,&m)==0)
-		return(0);
-	if (m<11)
-	{
-		if (sp->subsamplingcorrect==0)
-			TIFFErrorExt(tif->tif_clientdata,module,"Corrupt SOF marker in JPEG data");
-		return(0);
-	}
-	m-=8;
-	if (m%3!=0)
-	{
-		if (sp->subsamplingcorrect==0)
-			TIFFErrorExt(tif->tif_clientdata,module,"Corrupt SOF marker in JPEG data");
-		return(0);
-	}
-	n=m/3;
-	if (sp->subsamplingcorrect==0)
-	{
-		if (n!=sp->samples_per_pixel)
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"JPEG compressed data indicates unexpected number of samples");
-			return(0);
-		}
-	}
-	/* P: Sample precision */
-	if (OJPEGReadByte(sp,&o)==0)
-		return(0);
-	if (o!=8)
-	{
-		if (sp->subsamplingcorrect==0)
-			TIFFErrorExt(tif->tif_clientdata,module,"JPEG compressed data indicates unexpected number of bits per sample");
-		return(0);
-	}
-	/* Y: Number of lines, X: Number of samples per line */
-	if (sp->subsamplingcorrect)
-		OJPEGReadSkip(sp,4);
-	else
-	{
-		/* Y: Number of lines */
-		if (OJPEGReadWord(sp,&p)==0)
-			return(0);
-		if (((uint32)p<sp->image_length) && ((uint32)p<sp->strile_length_total))
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"JPEG compressed data indicates unexpected height");
-			return(0);
-		}
-		sp->sof_y=p;
-		/* X: Number of samples per line */
-		if (OJPEGReadWord(sp,&p)==0)
-			return(0);
-		if (((uint32)p<sp->image_width) && ((uint32)p<sp->strile_width))
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"JPEG compressed data indicates unexpected width");
-			return(0);
-		}
-		if ((uint32)p>sp->strile_width)
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"JPEG compressed data image width exceeds expected image width");
-			return(0);
-		}
-		sp->sof_x=p;
-	}
-	/* Nf: Number of image components in frame */
-	if (OJPEGReadByte(sp,&o)==0)
-		return(0);
-	if (o!=n)
-	{
-		if (sp->subsamplingcorrect==0)
-			TIFFErrorExt(tif->tif_clientdata,module,"Corrupt SOF marker in JPEG data");
-		return(0);
-	}
-	/* per component stuff */
-	/* TODO: double-check that flow implies that n cannot be as big as to make us overflow sof_c, sof_hv and sof_tq arrays */
-	for (q=0; q<n; q++)
-	{
-		/* C: Component identifier */
-		if (OJPEGReadByte(sp,&o)==0)
-			return(0);
-		if (sp->subsamplingcorrect==0)
-			sp->sof_c[q]=o;
-		/* H: Horizontal sampling factor, and V: Vertical sampling factor */
-		if (OJPEGReadByte(sp,&o)==0)
-			return(0);
-		if (sp->subsamplingcorrect!=0)
-		{
-			if (q==0)
-			{
-				sp->subsampling_hor=(o>>4);
-				sp->subsampling_ver=(o&15);
-				if (((sp->subsampling_hor!=1) && (sp->subsampling_hor!=2) && (sp->subsampling_hor!=4)) ||
-					((sp->subsampling_ver!=1) && (sp->subsampling_ver!=2) && (sp->subsampling_ver!=4)))
-					sp->subsampling_force_desubsampling_inside_decompression=1;
-			}
-			else
-			{
-				if (o!=17)
-					sp->subsampling_force_desubsampling_inside_decompression=1;
-			}
-		}
-		else
-		{
-			sp->sof_hv[q]=o;
-			if (sp->subsampling_force_desubsampling_inside_decompression==0)
-			{
-				if (q==0)
-				{
-					if (o!=((sp->subsampling_hor<<4)|sp->subsampling_ver))
-					{
-						TIFFErrorExt(tif->tif_clientdata,module,"JPEG compressed data indicates unexpected subsampling values");
-						return(0);
-					}
-				}
-				else
-				{
-					if (o!=17)
-					{
-						TIFFErrorExt(tif->tif_clientdata,module,"JPEG compressed data indicates unexpected subsampling values");
-						return(0);
-					}
-				}
-			}
-		}
-		/* Tq: Quantization table destination selector */
-		if (OJPEGReadByte(sp,&o)==0)
-			return(0);
-		if (sp->subsamplingcorrect==0)
-			sp->sof_tq[q]=o;
-	}
-	if (sp->subsamplingcorrect==0)
-		sp->sof_log=1;
-	return(1);
-}
-
-static int
-OJPEGReadHeaderInfoSecStreamSos(TIFF* tif)
-{
-	/* this marker needs to be checked, and part of its data needs to be saved for regeneration later on */
-	static const char module[]="OJPEGReadHeaderInfoSecStreamSos";
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint16 m;
-	uint8 n;
-	uint8 o;
-	assert(sp->subsamplingcorrect==0);
-	if (sp->sof_log==0)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Corrupt SOS marker in JPEG data");
-		return(0);
-	}
-	/* Ls */
-	if (OJPEGReadWord(sp,&m)==0)
-		return(0);
-	if (m!=6+sp->samples_per_pixel_per_plane*2)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Corrupt SOS marker in JPEG data");
-		return(0);
-	}
-	/* Ns */
-	if (OJPEGReadByte(sp,&n)==0)
-		return(0);
-	if (n!=sp->samples_per_pixel_per_plane)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Corrupt SOS marker in JPEG data");
-		return(0);
-	}
-	/* Cs, Td, and Ta */
-	for (o=0; o<sp->samples_per_pixel_per_plane; o++)
-	{
-		/* Cs */
-		if (OJPEGReadByte(sp,&n)==0)
-			return(0);
-		sp->sos_cs[sp->plane_sample_offset+o]=n;
-		/* Td and Ta */
-		if (OJPEGReadByte(sp,&n)==0)
-			return(0);
-		sp->sos_tda[sp->plane_sample_offset+o]=n;
-	}
-	/* skip Ss, Se, Ah, en Al -> no check, as per Tom Lane recommendation, as per LibJpeg source */
-	OJPEGReadSkip(sp,3);
-	return(1);
-}
-
-static int
-OJPEGReadHeaderInfoSecTablesQTable(TIFF* tif)
-{
-	static const char module[]="OJPEGReadHeaderInfoSecTablesQTable";
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint8 m;
-	uint8 n;
-	uint32 oa;
-	uint8* ob;
-	uint32 p;
-	if (sp->qtable_offset[0]==0)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Missing JPEG tables");
-		return(0);
-	}
-	sp->in_buffer_file_pos_log=0;
-	for (m=0; m<sp->samples_per_pixel; m++)
-	{
-		if ((sp->qtable_offset[m]!=0) && ((m==0) || (sp->qtable_offset[m]!=sp->qtable_offset[m-1])))
-		{
-			for (n=0; n<m-1; n++)
-			{
-				if (sp->qtable_offset[m]==sp->qtable_offset[n])
-				{
-					TIFFErrorExt(tif->tif_clientdata,module,"Corrupt JpegQTables tag value");
-					return(0);
-				}
-			}
-			oa=sizeof(uint32)+69;
-			ob=_TIFFmalloc(oa);
-			if (ob==0)
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-				return(0);
-			}
-			*(uint32*)ob=oa;
-			ob[sizeof(uint32)]=255;
-			ob[sizeof(uint32)+1]=JPEG_MARKER_DQT;
-			ob[sizeof(uint32)+2]=0;
-			ob[sizeof(uint32)+3]=67;
-			ob[sizeof(uint32)+4]=m;
-			TIFFSeekFile(tif,sp->qtable_offset[m],SEEK_SET); 
-			p=(uint32)TIFFReadFile(tif,&ob[sizeof(uint32)+5],64);
-			if (p!=64)
-                        {
-                                _TIFFfree(ob);
-				return(0);
-                        }
-			if (sp->qtable[m]!=0)
-				_TIFFfree(sp->qtable[m]);
-			sp->qtable[m]=ob;
-			sp->sof_tq[m]=m;
-		}
-		else
-			sp->sof_tq[m]=sp->sof_tq[m-1];
-	}
-	return(1);
-}
-
-static int
-OJPEGReadHeaderInfoSecTablesDcTable(TIFF* tif)
-{
-	static const char module[]="OJPEGReadHeaderInfoSecTablesDcTable";
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint8 m;
-	uint8 n;
-	uint8 o[16];
-	uint32 p;
-	uint32 q;
-	uint32 ra;
-	uint8* rb;
-	if (sp->dctable_offset[0]==0)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Missing JPEG tables");
-		return(0);
-	}
-	sp->in_buffer_file_pos_log=0;
-	for (m=0; m<sp->samples_per_pixel; m++)
-	{
-		if ((sp->dctable_offset[m]!=0) && ((m==0) || (sp->dctable_offset[m]!=sp->dctable_offset[m-1])))
-		{
-			for (n=0; n<m-1; n++)
-			{
-				if (sp->dctable_offset[m]==sp->dctable_offset[n])
-				{
-					TIFFErrorExt(tif->tif_clientdata,module,"Corrupt JpegDcTables tag value");
-					return(0);
-				}
-			}
-			TIFFSeekFile(tif,sp->dctable_offset[m],SEEK_SET);
-			p=(uint32)TIFFReadFile(tif,o,16);
-			if (p!=16)
-				return(0);
-			q=0;
-			for (n=0; n<16; n++)
-				q+=o[n];
-			ra=sizeof(uint32)+21+q;
-			rb=_TIFFmalloc(ra);
-			if (rb==0)
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-				return(0);
-			}
-			*(uint32*)rb=ra;
-			rb[sizeof(uint32)]=255;
-			rb[sizeof(uint32)+1]=JPEG_MARKER_DHT;
-			rb[sizeof(uint32)+2]=(uint8)((19+q)>>8);
-			rb[sizeof(uint32)+3]=((19+q)&255);
-			rb[sizeof(uint32)+4]=m;
-			for (n=0; n<16; n++)
-				rb[sizeof(uint32)+5+n]=o[n];
-			p=(uint32)TIFFReadFile(tif,&(rb[sizeof(uint32)+21]),q);
-			if (p!=q)
-                        {
-                                _TIFFfree(rb);
-				return(0);
-                        }
-			if (sp->dctable[m]!=0)
-				_TIFFfree(sp->dctable[m]);
-			sp->dctable[m]=rb;
-			sp->sos_tda[m]=(m<<4);
-		}
-		else
-			sp->sos_tda[m]=sp->sos_tda[m-1];
-	}
-	return(1);
-}
-
-static int
-OJPEGReadHeaderInfoSecTablesAcTable(TIFF* tif)
-{
-	static const char module[]="OJPEGReadHeaderInfoSecTablesAcTable";
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint8 m;
-	uint8 n;
-	uint8 o[16];
-	uint32 p;
-	uint32 q;
-	uint32 ra;
-	uint8* rb;
-	if (sp->actable_offset[0]==0)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Missing JPEG tables");
-		return(0);
-	}
-	sp->in_buffer_file_pos_log=0;
-	for (m=0; m<sp->samples_per_pixel; m++)
-	{
-		if ((sp->actable_offset[m]!=0) && ((m==0) || (sp->actable_offset[m]!=sp->actable_offset[m-1])))
-		{
-			for (n=0; n<m-1; n++)
-			{
-				if (sp->actable_offset[m]==sp->actable_offset[n])
-				{
-					TIFFErrorExt(tif->tif_clientdata,module,"Corrupt JpegAcTables tag value");
-					return(0);
-				}
-			}
-			TIFFSeekFile(tif,sp->actable_offset[m],SEEK_SET);  
-			p=(uint32)TIFFReadFile(tif,o,16);
-			if (p!=16)
-				return(0);
-			q=0;
-			for (n=0; n<16; n++)
-				q+=o[n];
-			ra=sizeof(uint32)+21+q;
-			rb=_TIFFmalloc(ra);
-			if (rb==0)
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-				return(0);
-			}
-			*(uint32*)rb=ra;
-			rb[sizeof(uint32)]=255;
-			rb[sizeof(uint32)+1]=JPEG_MARKER_DHT;
-			rb[sizeof(uint32)+2]=(uint8)((19+q)>>8);
-			rb[sizeof(uint32)+3]=((19+q)&255);
-			rb[sizeof(uint32)+4]=(16|m);
-			for (n=0; n<16; n++)
-				rb[sizeof(uint32)+5+n]=o[n];
-			p=(uint32)TIFFReadFile(tif,&(rb[sizeof(uint32)+21]),q);
-			if (p!=q)
-                        {
-                                _TIFFfree(rb);
-				return(0);
-                        }
-			if (sp->actable[m]!=0)
-				_TIFFfree(sp->actable[m]);
-			sp->actable[m]=rb;
-			sp->sos_tda[m]=(sp->sos_tda[m]|m);
-		}
-		else
-			sp->sos_tda[m]=(sp->sos_tda[m]|(sp->sos_tda[m-1]&15));
-	}
-	return(1);
-}
-
-static int
-OJPEGReadBufferFill(OJPEGState* sp)
-{
-	uint16 m;
-	tmsize_t n;
-	/* TODO: double-check: when subsamplingcorrect is set, no call to TIFFErrorExt or TIFFWarningExt should be made
-	 * in any other case, seek or read errors should be passed through */
-	do
-	{
-		if (sp->in_buffer_file_togo!=0)
-		{
-			if (sp->in_buffer_file_pos_log==0)
-			{
-				TIFFSeekFile(sp->tif,sp->in_buffer_file_pos,SEEK_SET);
-				sp->in_buffer_file_pos_log=1;
-			}
-			m=OJPEG_BUFFER;
-			if ((uint64)m>sp->in_buffer_file_togo)
-				m=(uint16)sp->in_buffer_file_togo;
-			n=TIFFReadFile(sp->tif,sp->in_buffer,(tmsize_t)m);
-			if (n==0)
-				return(0);
-			assert(n>0);
-			assert(n<=OJPEG_BUFFER);
-			assert(n<65536);
-			assert((uint64)n<=sp->in_buffer_file_togo);
-			m=(uint16)n;
-			sp->in_buffer_togo=m;
-			sp->in_buffer_cur=sp->in_buffer;
-			sp->in_buffer_file_togo-=m;
-			sp->in_buffer_file_pos+=m;
-			break;
-		}
-		sp->in_buffer_file_pos_log=0;
-		switch(sp->in_buffer_source)
-		{
-			case osibsNotSetYet:
-				if (sp->jpeg_interchange_format!=0)
-				{
-					sp->in_buffer_file_pos=sp->jpeg_interchange_format;
-					sp->in_buffer_file_togo=sp->jpeg_interchange_format_length;
-				}
-				sp->in_buffer_source=osibsJpegInterchangeFormat;
-				break;
-			case osibsJpegInterchangeFormat:
-				sp->in_buffer_source=osibsStrile;
-                                break;
-			case osibsStrile:
-				if (sp->in_buffer_next_strile==sp->in_buffer_strile_count)
-					sp->in_buffer_source=osibsEof;
-				else
-				{
-					int err = 0;
-					sp->in_buffer_file_pos=TIFFGetStrileOffsetWithErr(sp->tif, sp->in_buffer_next_strile, &err);
-					if( err )
-						return 0;
-					if (sp->in_buffer_file_pos!=0)
-					{
-						uint64 bytecount = TIFFGetStrileByteCountWithErr(sp->tif, sp->in_buffer_next_strile, &err);
-						if( err )
-							return 0;
-						if (sp->in_buffer_file_pos>=sp->file_size)
-							sp->in_buffer_file_pos=0;
-						else if (bytecount==0)
-							sp->in_buffer_file_togo=sp->file_size-sp->in_buffer_file_pos;
-						else
-						{
-							sp->in_buffer_file_togo=bytecount;
-							if (sp->in_buffer_file_togo==0)
-								sp->in_buffer_file_pos=0;
-							else if (sp->in_buffer_file_pos > TIFF_UINT64_MAX - sp->in_buffer_file_togo || 
-                                                                sp->in_buffer_file_pos+sp->in_buffer_file_togo>sp->file_size)
-								sp->in_buffer_file_togo=sp->file_size-sp->in_buffer_file_pos;
-						}
-					}
-					sp->in_buffer_next_strile++;
-				}
-				break;
-			default:
-				return(0);
-		}
-	} while (1);
-	return(1);
-}
-
-static int
-OJPEGReadByte(OJPEGState* sp, uint8* byte)
-{
-	if (sp->in_buffer_togo==0)
-	{
-		if (OJPEGReadBufferFill(sp)==0)
-			return(0);
-		assert(sp->in_buffer_togo>0);
-	}
-	*byte=*(sp->in_buffer_cur);
-	sp->in_buffer_cur++;
-	sp->in_buffer_togo--;
-	return(1);
-}
-
-static int
-OJPEGReadBytePeek(OJPEGState* sp, uint8* byte)
-{
-	if (sp->in_buffer_togo==0)
-	{
-		if (OJPEGReadBufferFill(sp)==0)
-			return(0);
-		assert(sp->in_buffer_togo>0);
-	}
-	*byte=*(sp->in_buffer_cur);
-	return(1);
-}
-
-static void
-OJPEGReadByteAdvance(OJPEGState* sp)
-{
-	assert(sp->in_buffer_togo>0);
-	sp->in_buffer_cur++;
-	sp->in_buffer_togo--;
-}
-
-static int
-OJPEGReadWord(OJPEGState* sp, uint16* word)
-{
-	uint8 m;
-	if (OJPEGReadByte(sp,&m)==0)
-		return(0);
-	*word=(m<<8);
-	if (OJPEGReadByte(sp,&m)==0)
-		return(0);
-	*word|=m;
-	return(1);
-}
-
-static int
-OJPEGReadBlock(OJPEGState* sp, uint16 len, void* mem)
-{
-	uint16 mlen;
-	uint8* mmem;
-	uint16 n;
-	assert(len>0);
-	mlen=len;
-	mmem=mem;
-	do
-	{
-		if (sp->in_buffer_togo==0)
-		{
-			if (OJPEGReadBufferFill(sp)==0)
-				return(0);
-			assert(sp->in_buffer_togo>0);
-		}
-		n=mlen;
-		if (n>sp->in_buffer_togo)
-			n=sp->in_buffer_togo;
-		_TIFFmemcpy(mmem,sp->in_buffer_cur,n);
-		sp->in_buffer_cur+=n;
-		sp->in_buffer_togo-=n;
-		mlen-=n;
-		mmem+=n;
-	} while(mlen>0);
-	return(1);
-}
-
-static void
-OJPEGReadSkip(OJPEGState* sp, uint16 len)
-{
-	uint16 m;
-	uint16 n;
-	m=len;
-	n=m;
-	if (n>sp->in_buffer_togo)
-		n=sp->in_buffer_togo;
-	sp->in_buffer_cur+=n;
-	sp->in_buffer_togo-=n;
-	m-=n;
-	if (m>0)
-	{
-		assert(sp->in_buffer_togo==0);
-		n=m;
-		if ((uint64)n>sp->in_buffer_file_togo)
-			n=(uint16)sp->in_buffer_file_togo;
-		sp->in_buffer_file_pos+=n;
-		sp->in_buffer_file_togo-=n;
-		sp->in_buffer_file_pos_log=0;
-		/* we don't skip past jpeginterchangeformat/strile block...
-		 * if that is asked from us, we're dealing with totally bazurk
-		 * data anyway, and we've not seen this happening on any
-		 * testfile, so we might as well likely cause some other
-		 * meaningless error to be passed at some later time
-		 */
-	}
-}
-
-static int
-OJPEGWriteStream(TIFF* tif, void** mem, uint32* len)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	*len=0;
-	do
-	{
-		assert(sp->out_state<=ososEoi);
-		switch(sp->out_state)
-		{
-			case ososSoi:
-				OJPEGWriteStreamSoi(tif,mem,len);
-				break;
-			case ososQTable0:
-				OJPEGWriteStreamQTable(tif,0,mem,len);
-				break;
-			case ososQTable1:
-				OJPEGWriteStreamQTable(tif,1,mem,len);
-				break;
-			case ososQTable2:
-				OJPEGWriteStreamQTable(tif,2,mem,len);
-				break;
-			case ososQTable3:
-				OJPEGWriteStreamQTable(tif,3,mem,len);
-				break;
-			case ososDcTable0:
-				OJPEGWriteStreamDcTable(tif,0,mem,len);
-				break;
-			case ososDcTable1:
-				OJPEGWriteStreamDcTable(tif,1,mem,len);
-				break;
-			case ososDcTable2:
-				OJPEGWriteStreamDcTable(tif,2,mem,len);
-				break;
-			case ososDcTable3:
-				OJPEGWriteStreamDcTable(tif,3,mem,len);
-				break;
-			case ososAcTable0:
-				OJPEGWriteStreamAcTable(tif,0,mem,len);
-				break;
-			case ososAcTable1:
-				OJPEGWriteStreamAcTable(tif,1,mem,len);
-				break;
-			case ososAcTable2:
-				OJPEGWriteStreamAcTable(tif,2,mem,len);
-				break;
-			case ososAcTable3:
-				OJPEGWriteStreamAcTable(tif,3,mem,len);
-				break;
-			case ososDri:
-				OJPEGWriteStreamDri(tif,mem,len);
-				break;
-			case ososSof:
-				OJPEGWriteStreamSof(tif,mem,len);
-				break;
-			case ososSos:
-				OJPEGWriteStreamSos(tif,mem,len);
-				break;
-			case ososCompressed:
-				if (OJPEGWriteStreamCompressed(tif,mem,len)==0)
-					return(0);
-				break;
-			case ososRst:
-				OJPEGWriteStreamRst(tif,mem,len);
-				break;
-			case ososEoi:
-				OJPEGWriteStreamEoi(tif,mem,len);
-				break;
-		}
-	} while (*len==0);
-	return(1);
-}
-
-static void
-OJPEGWriteStreamSoi(TIFF* tif, void** mem, uint32* len)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	assert(OJPEG_BUFFER>=2);
-	sp->out_buffer[0]=255;
-	sp->out_buffer[1]=JPEG_MARKER_SOI;
-	*len=2;
-	*mem=(void*)sp->out_buffer;
-	sp->out_state++;
-}
-
-static void
-OJPEGWriteStreamQTable(TIFF* tif, uint8 table_index, void** mem, uint32* len)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	if (sp->qtable[table_index]!=0)
-	{
-		*mem=(void*)(sp->qtable[table_index]+sizeof(uint32));
-		*len=*((uint32*)sp->qtable[table_index])-sizeof(uint32);
-	}
-	sp->out_state++;
-}
-
-static void
-OJPEGWriteStreamDcTable(TIFF* tif, uint8 table_index, void** mem, uint32* len)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	if (sp->dctable[table_index]!=0)
-	{
-		*mem=(void*)(sp->dctable[table_index]+sizeof(uint32));
-		*len=*((uint32*)sp->dctable[table_index])-sizeof(uint32);
-	}
-	sp->out_state++;
-}
-
-static void
-OJPEGWriteStreamAcTable(TIFF* tif, uint8 table_index, void** mem, uint32* len)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	if (sp->actable[table_index]!=0)
-	{
-		*mem=(void*)(sp->actable[table_index]+sizeof(uint32));
-		*len=*((uint32*)sp->actable[table_index])-sizeof(uint32);
-	}
-	sp->out_state++;
-}
-
-static void
-OJPEGWriteStreamDri(TIFF* tif, void** mem, uint32* len)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	assert(OJPEG_BUFFER>=6);
-	if (sp->restart_interval!=0)
-	{
-		sp->out_buffer[0]=255;
-		sp->out_buffer[1]=JPEG_MARKER_DRI;
-		sp->out_buffer[2]=0;
-		sp->out_buffer[3]=4;
-		sp->out_buffer[4]=(sp->restart_interval>>8);
-		sp->out_buffer[5]=(sp->restart_interval&255);
-		*len=6;
-		*mem=(void*)sp->out_buffer;
-	}
-	sp->out_state++;
-}
-
-static void
-OJPEGWriteStreamSof(TIFF* tif, void** mem, uint32* len)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint8 m;
-	assert(OJPEG_BUFFER>=2+8+sp->samples_per_pixel_per_plane*3);
-	assert(255>=8+sp->samples_per_pixel_per_plane*3);
-	sp->out_buffer[0]=255;
-	sp->out_buffer[1]=sp->sof_marker_id;
-	/* Lf */
-	sp->out_buffer[2]=0;
-	sp->out_buffer[3]=8+sp->samples_per_pixel_per_plane*3;
-	/* P */
-	sp->out_buffer[4]=8;
-	/* Y */
-	sp->out_buffer[5]=(uint8)(sp->sof_y>>8);
-	sp->out_buffer[6]=(sp->sof_y&255);
-	/* X */
-	sp->out_buffer[7]=(uint8)(sp->sof_x>>8);
-	sp->out_buffer[8]=(sp->sof_x&255);
-	/* Nf */
-	sp->out_buffer[9]=sp->samples_per_pixel_per_plane;
-	for (m=0; m<sp->samples_per_pixel_per_plane; m++)
-	{
-		/* C */
-		sp->out_buffer[10+m*3]=sp->sof_c[sp->plane_sample_offset+m];
-		/* H and V */
-		sp->out_buffer[10+m*3+1]=sp->sof_hv[sp->plane_sample_offset+m];
-		/* Tq */
-		sp->out_buffer[10+m*3+2]=sp->sof_tq[sp->plane_sample_offset+m];
-	}
-	*len=10+sp->samples_per_pixel_per_plane*3;
-	*mem=(void*)sp->out_buffer;
-	sp->out_state++;
-}
-
-static void
-OJPEGWriteStreamSos(TIFF* tif, void** mem, uint32* len)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	uint8 m;
-	assert(OJPEG_BUFFER>=2+6+sp->samples_per_pixel_per_plane*2);
-	assert(255>=6+sp->samples_per_pixel_per_plane*2);
-	sp->out_buffer[0]=255;
-	sp->out_buffer[1]=JPEG_MARKER_SOS;
-	/* Ls */
-	sp->out_buffer[2]=0;
-	sp->out_buffer[3]=6+sp->samples_per_pixel_per_plane*2;
-	/* Ns */
-	sp->out_buffer[4]=sp->samples_per_pixel_per_plane;
-	for (m=0; m<sp->samples_per_pixel_per_plane; m++)
-	{
-		/* Cs */
-		sp->out_buffer[5+m*2]=sp->sos_cs[sp->plane_sample_offset+m];
-		/* Td and Ta */
-		sp->out_buffer[5+m*2+1]=sp->sos_tda[sp->plane_sample_offset+m];
-	}
-	/* Ss */
-	sp->out_buffer[5+sp->samples_per_pixel_per_plane*2]=0;
-	/* Se */
-	sp->out_buffer[5+sp->samples_per_pixel_per_plane*2+1]=63;
-	/* Ah and Al */
-	sp->out_buffer[5+sp->samples_per_pixel_per_plane*2+2]=0;
-	*len=8+sp->samples_per_pixel_per_plane*2;
-	*mem=(void*)sp->out_buffer;
-	sp->out_state++;
-}
-
-static int
-OJPEGWriteStreamCompressed(TIFF* tif, void** mem, uint32* len)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	if (sp->in_buffer_togo==0)
-	{
-		if (OJPEGReadBufferFill(sp)==0)
-			return(0);
-		assert(sp->in_buffer_togo>0);
-	}
-	*len=sp->in_buffer_togo;
-	*mem=(void*)sp->in_buffer_cur;
-	sp->in_buffer_togo=0;
-	if (sp->in_buffer_file_togo==0)
-	{
-		switch(sp->in_buffer_source)
-		{
-			case osibsStrile:
-				if (sp->in_buffer_next_strile<sp->in_buffer_strile_count)
-					sp->out_state=ososRst;
-				else
-					sp->out_state=ososEoi;
-				break;
-			case osibsEof:
-				sp->out_state=ososEoi;
-				break;
-			default:
-				break;
-		}
-	}
-	return(1);
-}
-
-static void
-OJPEGWriteStreamRst(TIFF* tif, void** mem, uint32* len)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	assert(OJPEG_BUFFER>=2);
-	sp->out_buffer[0]=255;
-	sp->out_buffer[1]=JPEG_MARKER_RST0+sp->restart_index;
-	sp->restart_index++;
-	if (sp->restart_index==8)
-		sp->restart_index=0;
-	*len=2;
-	*mem=(void*)sp->out_buffer;
-	sp->out_state=ososCompressed;
-}
-
-static void
-OJPEGWriteStreamEoi(TIFF* tif, void** mem, uint32* len)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	assert(OJPEG_BUFFER>=2);
-	sp->out_buffer[0]=255;
-	sp->out_buffer[1]=JPEG_MARKER_EOI;
-	*len=2;
-	*mem=(void*)sp->out_buffer;
-}
-
-#ifndef LIBJPEG_ENCAP_EXTERNAL
-static int
-jpeg_create_decompress_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo)
-{
-	if( SETJMP(sp->exit_jmpbuf) )
-		return 0;
-	else {
-		jpeg_create_decompress(cinfo);
-		return 1;
-	}
-}
-#endif
-
-#ifndef LIBJPEG_ENCAP_EXTERNAL
-static int
-jpeg_read_header_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, uint8 require_image)
-{
-	if( SETJMP(sp->exit_jmpbuf) )
-		return 0;
-	else {
-		jpeg_read_header(cinfo,require_image);
-		return 1;
-	}
-}
-#endif
-
-#ifndef LIBJPEG_ENCAP_EXTERNAL
-static int
-jpeg_start_decompress_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo)
-{
-	if( SETJMP(sp->exit_jmpbuf) )
-		return 0;
-	else {
-		jpeg_start_decompress(cinfo);
-		return 1;
-	}
-}
-#endif
-
-#ifndef LIBJPEG_ENCAP_EXTERNAL
-static int
-jpeg_read_scanlines_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, void* scanlines, uint32 max_lines)
-{
-	if( SETJMP(sp->exit_jmpbuf) )
-		return 0;
-	else {
-		jpeg_read_scanlines(cinfo,scanlines,max_lines);
-		return 1;
-	}
-}
-#endif
-
-#ifndef LIBJPEG_ENCAP_EXTERNAL
-static int
-jpeg_read_raw_data_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, void* data, uint32 max_lines)
-{
-	if( SETJMP(sp->exit_jmpbuf) )
-		return 0;
-	else {
-		jpeg_read_raw_data(cinfo,data,max_lines);
-		return 1;
-	}
-}
-#endif
-
-#ifndef LIBJPEG_ENCAP_EXTERNAL
-static void
-jpeg_encap_unwind(TIFF* tif)
-{
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	LONGJMP(sp->exit_jmpbuf,1);
-}
-#endif
-
-static void
-OJPEGLibjpegJpegErrorMgrOutputMessage(jpeg_common_struct* cinfo)
-{
-	char buffer[JMSG_LENGTH_MAX];
-	(*cinfo->err->format_message)(cinfo,buffer);
-	TIFFWarningExt(((TIFF*)(cinfo->client_data))->tif_clientdata,"LibJpeg","%s",buffer);
-}
-
-static void
-OJPEGLibjpegJpegErrorMgrErrorExit(jpeg_common_struct* cinfo)
-{
-	char buffer[JMSG_LENGTH_MAX];
-	(*cinfo->err->format_message)(cinfo,buffer);
-	TIFFErrorExt(((TIFF*)(cinfo->client_data))->tif_clientdata,"LibJpeg","%s",buffer);
-	jpeg_encap_unwind((TIFF*)(cinfo->client_data));
-}
-
-static void
-OJPEGLibjpegJpegSourceMgrInitSource(jpeg_decompress_struct* cinfo)
-{
-	(void)cinfo;
-}
-
-static boolean
-OJPEGLibjpegJpegSourceMgrFillInputBuffer(jpeg_decompress_struct* cinfo)
-{
-	TIFF* tif=(TIFF*)cinfo->client_data;
-	OJPEGState* sp=(OJPEGState*)tif->tif_data;
-	void* mem=0;
-	uint32 len=0U;
-	if (OJPEGWriteStream(tif,&mem,&len)==0)
-	{
-		TIFFErrorExt(tif->tif_clientdata,"LibJpeg","Premature end of JPEG data");
-		jpeg_encap_unwind(tif);
-	}
-	sp->libjpeg_jpeg_source_mgr.bytes_in_buffer=len;
-	sp->libjpeg_jpeg_source_mgr.next_input_byte=mem;
-	return(1);
-}
-
-static void
-OJPEGLibjpegJpegSourceMgrSkipInputData(jpeg_decompress_struct* cinfo, long num_bytes)
-{
-	TIFF* tif=(TIFF*)cinfo->client_data;
-	(void)num_bytes;
-	TIFFErrorExt(tif->tif_clientdata,"LibJpeg","Unexpected error");
-	jpeg_encap_unwind(tif);
-}
-
-#ifdef _MSC_VER
-#pragma warning( push )
-#pragma warning( disable : 4702 ) /* unreachable code */
-#endif
-static boolean
-OJPEGLibjpegJpegSourceMgrResyncToRestart(jpeg_decompress_struct* cinfo, int desired)
-{
-	TIFF* tif=(TIFF*)cinfo->client_data;
-	(void)desired;
-	TIFFErrorExt(tif->tif_clientdata,"LibJpeg","Unexpected error");
-	jpeg_encap_unwind(tif);
-	return(0);
-}
-#ifdef _MSC_VER
-#pragma warning( pop ) 
-#endif
-
-static void
-OJPEGLibjpegJpegSourceMgrTermSource(jpeg_decompress_struct* cinfo)
-{
-	(void)cinfo;
-}
-
-#endif
-
-
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_open.c b/third_party/libtiff/tif_open.c
index 3cb53d4..23fcf81 100644
--- a/third_party/libtiff/tif_open.c
+++ b/third_party/libtiff/tif_open.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -26,480 +26,626 @@
  * TIFF Library.
  */
 #include "tiffiop.h"
+#include <limits.h>
 
 /*
  * Dummy functions to fill the omitted client procedures.
  */
-static int
-_tiffDummyMapProc(thandle_t fd, void** pbase, toff_t* psize)
+static int _tiffDummyMapProc(thandle_t fd, void **pbase, toff_t *psize)
 {
-	(void) fd; (void) pbase; (void) psize;
-	return (0);
+    (void)fd;
+    (void)pbase;
+    (void)psize;
+    return (0);
 }
 
-static void
-_tiffDummyUnmapProc(thandle_t fd, void* base, toff_t size)
+static void _tiffDummyUnmapProc(thandle_t fd, void *base, toff_t size)
 {
-	(void) fd; (void) base; (void) size;
+    (void)fd;
+    (void)base;
+    (void)size;
 }
 
-int
-_TIFFgetMode(const char* mode, const char* module)
+int _TIFFgetMode(TIFFOpenOptions *opts, thandle_t clientdata, const char *mode,
+                 const char *module)
 {
-	int m = -1;
+    int m = -1;
 
-	switch (mode[0]) {
-	case 'r':
-		m = O_RDONLY;
-		if (mode[1] == '+')
-			m = O_RDWR;
-		break;
-	case 'w':
-	case 'a':
-		m = O_RDWR|O_CREAT;
-		if (mode[0] == 'w')
-			m |= O_TRUNC;
-		break;
-	default:
-		TIFFErrorExt(0, module, "\"%s\": Bad mode", mode);
-		break;
-	}
-	return (m);
+    switch (mode[0])
+    {
+        case 'r':
+            m = O_RDONLY;
+            if (mode[1] == '+')
+                m = O_RDWR;
+            break;
+        case 'w':
+        case 'a':
+            m = O_RDWR | O_CREAT;
+            if (mode[0] == 'w')
+                m |= O_TRUNC;
+            break;
+        default:
+            _TIFFErrorEarly(opts, clientdata, module, "\"%s\": Bad mode", mode);
+            break;
+    }
+    return (m);
 }
 
-TIFF*
-TIFFClientOpen(
-	const char* name, const char* mode,
-	thandle_t clientdata,
-	TIFFReadWriteProc readproc,
-	TIFFReadWriteProc writeproc,
-	TIFFSeekProc seekproc,
-	TIFFCloseProc closeproc,
-	TIFFSizeProc sizeproc,
-	TIFFMapFileProc mapproc,
-	TIFFUnmapFileProc unmapproc
-)
+TIFFOpenOptions *TIFFOpenOptionsAlloc()
 {
-	static const char module[] = "TIFFClientOpen";
-	TIFF *tif;
-	int m;
-	const char* cp;
+    TIFFOpenOptions *opts =
+        (TIFFOpenOptions *)_TIFFcalloc(1, sizeof(TIFFOpenOptions));
+    return opts;
+}
 
-	/* The following are configuration checks. They should be redundant, but should not
-	 * compile to any actual code in an optimised release build anyway. If any of them
-	 * fail, (makefile-based or other) configuration is not correct */
-	assert(sizeof(uint8)==1);
-	assert(sizeof(int8)==1);
-	assert(sizeof(uint16)==2);
-	assert(sizeof(int16)==2);
-	assert(sizeof(uint32)==4);
-	assert(sizeof(int32)==4);
-	assert(sizeof(uint64)==8);
-	assert(sizeof(int64)==8);
-	assert(sizeof(tmsize_t)==sizeof(void*));
-	{
-		union{
-			uint8 a8[2];
-			uint16 a16;
-		} n;
-		n.a8[0]=1;
-		n.a8[1]=0;
-		#ifdef WORDS_BIGENDIAN
-		assert(n.a16==256);
-		#else
-		assert(n.a16==1);
-		#endif
-	}
+void TIFFOpenOptionsFree(TIFFOpenOptions *opts) { _TIFFfree(opts); }
 
-	m = _TIFFgetMode(mode, module);
-	if (m == -1)
-		goto bad2;
-	tif = (TIFF *)_TIFFmalloc((tmsize_t)(sizeof (TIFF) + strlen(name) + 1));
-	if (tif == NULL) {
-		TIFFErrorExt(clientdata, module, "%s: Out of memory (TIFF structure)", name);
-		goto bad2;
-	}
-	_TIFFmemset(tif, 0, sizeof (*tif));
-	tif->tif_name = (char *)tif + sizeof (TIFF);
-	strcpy(tif->tif_name, name);
-	tif->tif_mode = m &~ (O_CREAT|O_TRUNC);
-	tif->tif_curdir = (uint16) -1;		/* non-existent directory */
-	tif->tif_curoff = 0;
-	tif->tif_curstrip = (uint32) -1;	/* invalid strip */
-	tif->tif_row = (uint32) -1;		/* read/write pre-increment */
-	tif->tif_clientdata = clientdata;
-	if (!readproc || !writeproc || !seekproc || !closeproc || !sizeproc) {
-		TIFFErrorExt(clientdata, module,
-		    "One of the client procedures is NULL pointer.");
-		_TIFFfree(tif);
-		goto bad2;
-	}
-	tif->tif_readproc = readproc;
-	tif->tif_writeproc = writeproc;
-	tif->tif_seekproc = seekproc;
-	tif->tif_closeproc = closeproc;
-	tif->tif_sizeproc = sizeproc;
-	if (mapproc)
-		tif->tif_mapproc = mapproc;
-	else
-		tif->tif_mapproc = _tiffDummyMapProc;
-	if (unmapproc)
-		tif->tif_unmapproc = unmapproc;
-	else
-		tif->tif_unmapproc = _tiffDummyUnmapProc;
-	_TIFFSetDefaultCompressionState(tif);    /* setup default state */
-	/*
-	 * Default is to return data MSB2LSB and enable the
-	 * use of memory-mapped files and strip chopping when
-	 * a file is opened read-only.
-	 */
-	tif->tif_flags = FILLORDER_MSB2LSB;
-	if (m == O_RDONLY )
-		tif->tif_flags |= TIFF_MAPPED;
+/** Define a limit in bytes for a single memory allocation done by libtiff.
+ *  If max_single_mem_alloc is set to 0, no other limit that the underlying
+ *  _TIFFmalloc() will be applied, which is the default.
+ */
+void TIFFOpenOptionsSetMaxSingleMemAlloc(TIFFOpenOptions *opts,
+                                         tmsize_t max_single_mem_alloc)
+{
+    opts->max_single_mem_alloc = max_single_mem_alloc;
+}
 
-	#ifdef STRIPCHOP_DEFAULT
-	if (m == O_RDONLY || m == O_RDWR)
-		tif->tif_flags |= STRIPCHOP_DEFAULT;
-	#endif
+void TIFFOpenOptionsSetErrorHandlerExtR(TIFFOpenOptions *opts,
+                                        TIFFErrorHandlerExtR handler,
+                                        void *errorhandler_user_data)
+{
+    opts->errorhandler = handler;
+    opts->errorhandler_user_data = errorhandler_user_data;
+}
 
-	/*
-	 * Process library-specific flags in the open mode string.
-	 * The following flags may be used to control intrinsic library
-	 * behaviour that may or may not be desirable (usually for
-	 * compatibility with some application that claims to support
-	 * TIFF but only supports some brain dead idea of what the
-	 * vendor thinks TIFF is):
-	 *
-	 * 'l' use little-endian byte order for creating a file
-	 * 'b' use big-endian byte order for creating a file
-	 * 'L' read/write information using LSB2MSB bit order
-	 * 'B' read/write information using MSB2LSB bit order
-	 * 'H' read/write information using host bit order
-	 * 'M' enable use of memory-mapped files when supported
-	 * 'm' disable use of memory-mapped files
-	 * 'C' enable strip chopping support when reading
-	 * 'c' disable strip chopping support
-	 * 'h' read TIFF header only, do not load the first IFD
-	 * '4' ClassicTIFF for creating a file (default)
-	 * '8' BigTIFF for creating a file
-         * 'D' enable use of deferred strip/tile offset/bytecount array loading.
-         * 'O' on-demand loading of values instead of whole array loading (implies D)
-	 *
-	 * The use of the 'l' and 'b' flags is strongly discouraged.
-	 * These flags are provided solely because numerous vendors,
-	 * typically on the PC, do not correctly support TIFF; they
-	 * only support the Intel little-endian byte order.  This
-	 * support is not configured by default because it supports
-	 * the violation of the TIFF spec that says that readers *MUST*
-	 * support both byte orders.  It is strongly recommended that
-	 * you not use this feature except to deal with busted apps
-	 * that write invalid TIFF.  And even in those cases you should
-	 * bang on the vendors to fix their software.
-	 *
-	 * The 'L', 'B', and 'H' flags are intended for applications
-	 * that can optimize operations on data by using a particular
-	 * bit order.  By default the library returns data in MSB2LSB
-	 * bit order for compatibility with older versions of this
-	 * library.  Returning data in the bit order of the native CPU
-	 * makes the most sense but also requires applications to check
-	 * the value of the FillOrder tag; something they probably do
-	 * not do right now.
-	 *
-	 * The 'M' and 'm' flags are provided because some virtual memory
-	 * systems exhibit poor behaviour when large images are mapped.
-	 * These options permit clients to control the use of memory-mapped
-	 * files on a per-file basis.
-	 *
-	 * The 'C' and 'c' flags are provided because the library support
-	 * for chopping up large strips into multiple smaller strips is not
-	 * application-transparent and as such can cause problems.  The 'c'
-	 * option permits applications that only want to look at the tags,
-	 * for example, to get the unadulterated TIFF tag information.
-	 */
-	for (cp = mode; *cp; cp++)
-		switch (*cp) {
-			case 'b':
-				#ifndef WORDS_BIGENDIAN
-				if (m&O_CREAT)
-					tif->tif_flags |= TIFF_SWAB;
-				#endif
-				break;
-			case 'l':
-				#ifdef WORDS_BIGENDIAN
-				if ((m&O_CREAT))
-					tif->tif_flags |= TIFF_SWAB;
-				#endif
-				break;
-			case 'B':
-				tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) |
-				    FILLORDER_MSB2LSB;
-				break;
-			case 'L':
-				tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) |
-				    FILLORDER_LSB2MSB;
-				break;
-			case 'H':
-				tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) |
-				    HOST_FILLORDER;
-				break;
-			case 'M':
-				if (m == O_RDONLY)
-					tif->tif_flags |= TIFF_MAPPED;
-				break;
-			case 'm':
-				if (m == O_RDONLY)
-					tif->tif_flags &= ~TIFF_MAPPED;
-				break;
-			case 'C':
-				if (m == O_RDONLY)
-					tif->tif_flags |= TIFF_STRIPCHOP;
-				break;
-			case 'c':
-				if (m == O_RDONLY)
-					tif->tif_flags &= ~TIFF_STRIPCHOP;
-				break;
-			case 'h':
-				tif->tif_flags |= TIFF_HEADERONLY;
-				break;
-			case '8':
-				if (m&O_CREAT)
-					tif->tif_flags |= TIFF_BIGTIFF;
-				break;
-			case 'D':
-			        tif->tif_flags |= TIFF_DEFERSTRILELOAD;
-				break;
-			case 'O':
-				if( m == O_RDONLY )
-					tif->tif_flags |= (TIFF_LAZYSTRILELOAD | TIFF_DEFERSTRILELOAD);
-				break;
-		}
+void TIFFOpenOptionsSetWarningHandlerExtR(TIFFOpenOptions *opts,
+                                          TIFFErrorHandlerExtR handler,
+                                          void *warnhandler_user_data)
+{
+    opts->warnhandler = handler;
+    opts->warnhandler_user_data = warnhandler_user_data;
+}
 
-#ifdef DEFER_STRILE_LOAD
-        /* Compatibility with old DEFER_STRILE_LOAD compilation flag */
-        /* Probably unneeded, since to the best of my knowledge (E. Rouault) */
-        /* GDAL was the only user of this, and will now use the new 'D' flag */
-        tif->tif_flags |= TIFF_DEFERSTRILELOAD;
+static void _TIFFEmitErrorAboveMaxSingleMemAlloc(TIFF *tif,
+                                                 const char *pszFunction,
+                                                 tmsize_t s)
+{
+    TIFFErrorExtR(tif, pszFunction,
+                  "Memory allocation of %" PRIu64
+                  " bytes is beyond the %" PRIu64
+                  " byte limit defined in open options",
+                  (uint64_t)s, (uint64_t)tif->tif_max_single_mem_alloc);
+}
+
+/** malloc() version that takes into account memory-specific open options */
+void *_TIFFmallocExt(TIFF *tif, tmsize_t s)
+{
+    if (tif != NULL && tif->tif_max_single_mem_alloc > 0 &&
+        s > tif->tif_max_single_mem_alloc)
+    {
+        _TIFFEmitErrorAboveMaxSingleMemAlloc(tif, "_TIFFmallocExt", s);
+        return NULL;
+    }
+    return _TIFFmalloc(s);
+}
+
+/** calloc() version that takes into account memory-specific open options */
+void *_TIFFcallocExt(TIFF *tif, tmsize_t nmemb, tmsize_t siz)
+{
+    if (tif != NULL && tif->tif_max_single_mem_alloc > 0)
+    {
+        if (nmemb <= 0 || siz <= 0 || nmemb > TIFF_TMSIZE_T_MAX / siz)
+            return NULL;
+        if (nmemb * siz > tif->tif_max_single_mem_alloc)
+        {
+            _TIFFEmitErrorAboveMaxSingleMemAlloc(tif, "_TIFFcallocExt",
+                                                 nmemb * siz);
+            return NULL;
+        }
+    }
+    return _TIFFcalloc(nmemb, siz);
+}
+
+/** realloc() version that takes into account memory-specific open options */
+void *_TIFFreallocExt(TIFF *tif, void *p, tmsize_t s)
+{
+    if (tif != NULL && tif->tif_max_single_mem_alloc > 0 &&
+        s > tif->tif_max_single_mem_alloc)
+    {
+        _TIFFEmitErrorAboveMaxSingleMemAlloc(tif, "_TIFFreallocExt", s);
+        return NULL;
+    }
+    return _TIFFrealloc(p, s);
+}
+
+/** free() version that takes into account memory-specific open options */
+void _TIFFfreeExt(TIFF *tif, void *p)
+{
+    (void)tif;
+    _TIFFfree(p);
+}
+
+TIFF *TIFFClientOpen(const char *name, const char *mode, thandle_t clientdata,
+                     TIFFReadWriteProc readproc, TIFFReadWriteProc writeproc,
+                     TIFFSeekProc seekproc, TIFFCloseProc closeproc,
+                     TIFFSizeProc sizeproc, TIFFMapFileProc mapproc,
+                     TIFFUnmapFileProc unmapproc)
+{
+    return TIFFClientOpenExt(name, mode, clientdata, readproc, writeproc,
+                             seekproc, closeproc, sizeproc, mapproc, unmapproc,
+                             NULL);
+}
+
+TIFF *TIFFClientOpenExt(const char *name, const char *mode,
+                        thandle_t clientdata, TIFFReadWriteProc readproc,
+                        TIFFReadWriteProc writeproc, TIFFSeekProc seekproc,
+                        TIFFCloseProc closeproc, TIFFSizeProc sizeproc,
+                        TIFFMapFileProc mapproc, TIFFUnmapFileProc unmapproc,
+                        TIFFOpenOptions *opts)
+{
+    static const char module[] = "TIFFClientOpenExt";
+    TIFF *tif;
+    int m;
+    const char *cp;
+
+    /* The following are configuration checks. They should be redundant, but
+     * should not compile to any actual code in an optimised release build
+     * anyway. If any of them fail, (makefile-based or other) configuration is
+     * not correct */
+    assert(sizeof(uint8_t) == 1);
+    assert(sizeof(int8_t) == 1);
+    assert(sizeof(uint16_t) == 2);
+    assert(sizeof(int16_t) == 2);
+    assert(sizeof(uint32_t) == 4);
+    assert(sizeof(int32_t) == 4);
+    assert(sizeof(uint64_t) == 8);
+    assert(sizeof(int64_t) == 8);
+    {
+        union
+        {
+            uint8_t a8[2];
+            uint16_t a16;
+        } n;
+        n.a8[0] = 1;
+        n.a8[1] = 0;
+        (void)n;
+#ifdef WORDS_BIGENDIAN
+        assert(n.a16 == 256);
+#else
+        assert(n.a16 == 1);
+#endif
+    }
+
+    m = _TIFFgetMode(opts, clientdata, mode, module);
+    if (m == -1)
+        goto bad2;
+    tmsize_t size_to_alloc = (tmsize_t)(sizeof(TIFF) + strlen(name) + 1);
+    if (opts && opts->max_single_mem_alloc > 0 &&
+        size_to_alloc > opts->max_single_mem_alloc)
+    {
+        _TIFFErrorEarly(opts, clientdata, module,
+                        "%s: Memory allocation of %" PRIu64
+                        " bytes is beyond the %" PRIu64
+                        " byte limit defined in open options",
+                        name, (uint64_t)size_to_alloc,
+                        (uint64_t)opts->max_single_mem_alloc);
+        goto bad2;
+    }
+    tif = (TIFF *)_TIFFmallocExt(NULL, size_to_alloc);
+    if (tif == NULL)
+    {
+        _TIFFErrorEarly(opts, clientdata, module,
+                        "%s: Out of memory (TIFF structure)", name);
+        goto bad2;
+    }
+    _TIFFmemset(tif, 0, sizeof(*tif));
+    tif->tif_name = (char *)tif + sizeof(TIFF);
+    strcpy(tif->tif_name, name);
+    tif->tif_mode = m & ~(O_CREAT | O_TRUNC);
+    tif->tif_curdir = TIFF_NON_EXISTENT_DIR_NUMBER; /* non-existent directory */
+    tif->tif_curoff = 0;
+    tif->tif_curstrip = (uint32_t)-1; /* invalid strip */
+    tif->tif_row = (uint32_t)-1;      /* read/write pre-increment */
+    tif->tif_clientdata = clientdata;
+    tif->tif_readproc = readproc;
+    tif->tif_writeproc = writeproc;
+    tif->tif_seekproc = seekproc;
+    tif->tif_closeproc = closeproc;
+    tif->tif_sizeproc = sizeproc;
+    tif->tif_mapproc = mapproc ? mapproc : _tiffDummyMapProc;
+    tif->tif_unmapproc = unmapproc ? unmapproc : _tiffDummyUnmapProc;
+    if (opts)
+    {
+        tif->tif_errorhandler = opts->errorhandler;
+        tif->tif_errorhandler_user_data = opts->errorhandler_user_data;
+        tif->tif_warnhandler = opts->warnhandler;
+        tif->tif_warnhandler_user_data = opts->warnhandler_user_data;
+        tif->tif_max_single_mem_alloc = opts->max_single_mem_alloc;
+    }
+
+    if (!readproc || !writeproc || !seekproc || !closeproc || !sizeproc)
+    {
+        TIFFErrorExtR(tif, module,
+                      "One of the client procedures is NULL pointer.");
+        _TIFFfreeExt(NULL, tif);
+        goto bad2;
+    }
+
+    _TIFFSetDefaultCompressionState(tif); /* setup default state */
+    /*
+     * Default is to return data MSB2LSB and enable the
+     * use of memory-mapped files and strip chopping when
+     * a file is opened read-only.
+     */
+    tif->tif_flags = FILLORDER_MSB2LSB;
+    if (m == O_RDONLY)
+        tif->tif_flags |= TIFF_MAPPED;
+
+#ifdef STRIPCHOP_DEFAULT
+    if (m == O_RDONLY || m == O_RDWR)
+        tif->tif_flags |= STRIPCHOP_DEFAULT;
 #endif
 
-	/*
-	 * Read in TIFF header.
-	 */
-	if ((m & O_TRUNC) ||
-	    !ReadOK(tif, &tif->tif_header, sizeof (TIFFHeaderClassic))) {
-		if (tif->tif_mode == O_RDONLY) {
-			TIFFErrorExt(tif->tif_clientdata, name,
-			    "Cannot read TIFF header");
-			goto bad;
-		}
-		/*
-		 * Setup header and write.
-		 */
-		#ifdef WORDS_BIGENDIAN
-		tif->tif_header.common.tiff_magic = (tif->tif_flags & TIFF_SWAB)
-		    ? TIFF_LITTLEENDIAN : TIFF_BIGENDIAN;
-		#else
-		tif->tif_header.common.tiff_magic = (tif->tif_flags & TIFF_SWAB)
-		    ? TIFF_BIGENDIAN : TIFF_LITTLEENDIAN;
-		#endif
-		if (!(tif->tif_flags&TIFF_BIGTIFF))
-		{
-			tif->tif_header.common.tiff_version = TIFF_VERSION_CLASSIC;
-			tif->tif_header.classic.tiff_diroff = 0;
-			if (tif->tif_flags & TIFF_SWAB)
-				TIFFSwabShort(&tif->tif_header.common.tiff_version);
-			tif->tif_header_size = sizeof(TIFFHeaderClassic);
-		}
-		else
-		{
-			tif->tif_header.common.tiff_version = TIFF_VERSION_BIG;
-			tif->tif_header.big.tiff_offsetsize = 8;
-			tif->tif_header.big.tiff_unused = 0;
-			tif->tif_header.big.tiff_diroff = 0;
-			if (tif->tif_flags & TIFF_SWAB)
-			{
-				TIFFSwabShort(&tif->tif_header.common.tiff_version);
-				TIFFSwabShort(&tif->tif_header.big.tiff_offsetsize);
-			}
-			tif->tif_header_size = sizeof (TIFFHeaderBig);
-		}
-		/*
-		 * The doc for "fopen" for some STD_C_LIBs says that if you
-		 * open a file for modify ("+"), then you must fseek (or
-		 * fflush?) between any freads and fwrites.  This is not
-		 * necessary on most systems, but has been shown to be needed
-		 * on Solaris.
-		 */
-		TIFFSeekFile( tif, 0, SEEK_SET );
-		if (!WriteOK(tif, &tif->tif_header, (tmsize_t)(tif->tif_header_size))) {
-			TIFFErrorExt(tif->tif_clientdata, name,
-			    "Error writing TIFF header");
-			goto bad;
-		}
-		/*
-		 * Setup the byte order handling.
-		 */
-		if (tif->tif_header.common.tiff_magic == TIFF_BIGENDIAN) {
-			#ifndef WORDS_BIGENDIAN
-			tif->tif_flags |= TIFF_SWAB;
-			#endif
-		} else {
-			#ifdef WORDS_BIGENDIAN
-			tif->tif_flags |= TIFF_SWAB;
-			#endif
-		}
-		/*
-		 * Setup default directory.
-		 */
-		if (!TIFFDefaultDirectory(tif))
-			goto bad;
-		tif->tif_diroff = 0;
-		tif->tif_dirlist = NULL;
-		tif->tif_dirlistsize = 0;
-		tif->tif_dirnumber = 0;
-		return (tif);
-	}
-	/*
-	 * Setup the byte order handling.
-	 */
-	if (tif->tif_header.common.tiff_magic != TIFF_BIGENDIAN &&
-	    tif->tif_header.common.tiff_magic != TIFF_LITTLEENDIAN
-	    #if MDI_SUPPORT
-	    &&
-	    #if HOST_BIGENDIAN
-	    tif->tif_header.common.tiff_magic != MDI_BIGENDIAN
-	    #else
-	    tif->tif_header.common.tiff_magic != MDI_LITTLEENDIAN
-	    #endif
-	    ) {
-		TIFFErrorExt(tif->tif_clientdata, name,
-		    "Not a TIFF or MDI file, bad magic number %d (0x%x)",
-	    #else
-	    ) {
-		TIFFErrorExt(tif->tif_clientdata, name,
-		    "Not a TIFF file, bad magic number %d (0x%x)",
-	    #endif
-		    tif->tif_header.common.tiff_magic,
-		    tif->tif_header.common.tiff_magic);
-		goto bad;
-	}
-	if (tif->tif_header.common.tiff_magic == TIFF_BIGENDIAN) {
-		#ifndef WORDS_BIGENDIAN
-		tif->tif_flags |= TIFF_SWAB;
-		#endif
-	} else {
-		#ifdef WORDS_BIGENDIAN
-		tif->tif_flags |= TIFF_SWAB;
-		#endif
-	}
-	if (tif->tif_flags & TIFF_SWAB) 
-		TIFFSwabShort(&tif->tif_header.common.tiff_version);
-	if ((tif->tif_header.common.tiff_version != TIFF_VERSION_CLASSIC)&&
-	    (tif->tif_header.common.tiff_version != TIFF_VERSION_BIG)) {
-		TIFFErrorExt(tif->tif_clientdata, name,
-		    "Not a TIFF file, bad version number %d (0x%x)",
-		    tif->tif_header.common.tiff_version,
-		    tif->tif_header.common.tiff_version);
-		goto bad;
-	}
-	if (tif->tif_header.common.tiff_version == TIFF_VERSION_CLASSIC)
-	{
-		if (tif->tif_flags & TIFF_SWAB)
-			TIFFSwabLong(&tif->tif_header.classic.tiff_diroff);
-		tif->tif_header_size = sizeof(TIFFHeaderClassic);
-	}
-	else
-	{
-		if (!ReadOK(tif, ((uint8*)(&tif->tif_header) + sizeof(TIFFHeaderClassic)), (sizeof(TIFFHeaderBig)-sizeof(TIFFHeaderClassic))))
-		{
-			TIFFErrorExt(tif->tif_clientdata, name,
-			    "Cannot read TIFF header");
-			goto bad;
-		}
-		if (tif->tif_flags & TIFF_SWAB)
-		{
-			TIFFSwabShort(&tif->tif_header.big.tiff_offsetsize);
-			TIFFSwabLong8(&tif->tif_header.big.tiff_diroff);
-		}
-		if (tif->tif_header.big.tiff_offsetsize != 8)
-		{
-			TIFFErrorExt(tif->tif_clientdata, name,
-			    "Not a TIFF file, bad BigTIFF offsetsize %d (0x%x)",
-			    tif->tif_header.big.tiff_offsetsize,
-			    tif->tif_header.big.tiff_offsetsize);
-			goto bad;
-		}
-		if (tif->tif_header.big.tiff_unused != 0)
-		{
-			TIFFErrorExt(tif->tif_clientdata, name,
-			    "Not a TIFF file, bad BigTIFF unused %d (0x%x)",
-			    tif->tif_header.big.tiff_unused,
-			    tif->tif_header.big.tiff_unused);
-			goto bad;
-		}
-		tif->tif_header_size = sizeof(TIFFHeaderBig);
-		tif->tif_flags |= TIFF_BIGTIFF;
-	}
-	tif->tif_flags |= TIFF_MYBUFFER;
-	tif->tif_rawcp = tif->tif_rawdata = 0;
-	tif->tif_rawdatasize = 0;
-        tif->tif_rawdataoff = 0;
-        tif->tif_rawdataloaded = 0;
+    /*
+     * Process library-specific flags in the open mode string.
+     * The following flags may be used to control intrinsic library
+     * behavior that may or may not be desirable (usually for
+     * compatibility with some application that claims to support
+     * TIFF but only supports some brain dead idea of what the
+     * vendor thinks TIFF is):
+     *
+     * 'l' use little-endian byte order for creating a file
+     * 'b' use big-endian byte order for creating a file
+     * 'L' read/write information using LSB2MSB bit order
+     * 'B' read/write information using MSB2LSB bit order
+     * 'H' read/write information using host bit order
+     * 'M' enable use of memory-mapped files when supported
+     * 'm' disable use of memory-mapped files
+     * 'C' enable strip chopping support when reading
+     * 'c' disable strip chopping support
+     * 'h' read TIFF header only, do not load the first IFD
+     * '4' ClassicTIFF for creating a file (default)
+     * '8' BigTIFF for creating a file
+     * 'D' enable use of deferred strip/tile offset/bytecount array loading.
+     * 'O' on-demand loading of values instead of whole array loading (implies
+     * D)
+     *
+     * The use of the 'l' and 'b' flags is strongly discouraged.
+     * These flags are provided solely because numerous vendors,
+     * typically on the PC, do not correctly support TIFF; they
+     * only support the Intel little-endian byte order.  This
+     * support is not configured by default because it supports
+     * the violation of the TIFF spec that says that readers *MUST*
+     * support both byte orders.  It is strongly recommended that
+     * you not use this feature except to deal with busted apps
+     * that write invalid TIFF.  And even in those cases you should
+     * bang on the vendors to fix their software.
+     *
+     * The 'L', 'B', and 'H' flags are intended for applications
+     * that can optimize operations on data by using a particular
+     * bit order.  By default the library returns data in MSB2LSB
+     * bit order for compatibility with older versions of this
+     * library.  Returning data in the bit order of the native CPU
+     * makes the most sense but also requires applications to check
+     * the value of the FillOrder tag; something they probably do
+     * not do right now.
+     *
+     * The 'M' and 'm' flags are provided because some virtual memory
+     * systems exhibit poor behavior when large images are mapped.
+     * These options permit clients to control the use of memory-mapped
+     * files on a per-file basis.
+     *
+     * The 'C' and 'c' flags are provided because the library support
+     * for chopping up large strips into multiple smaller strips is not
+     * application-transparent and as such can cause problems.  The 'c'
+     * option permits applications that only want to look at the tags,
+     * for example, to get the unadulterated TIFF tag information.
+     */
+    for (cp = mode; *cp; cp++)
+        switch (*cp)
+        {
+            case 'b':
+#ifndef WORDS_BIGENDIAN
+                if (m & O_CREAT)
+                    tif->tif_flags |= TIFF_SWAB;
+#endif
+                break;
+            case 'l':
+#ifdef WORDS_BIGENDIAN
+                if ((m & O_CREAT))
+                    tif->tif_flags |= TIFF_SWAB;
+#endif
+                break;
+            case 'B':
+                tif->tif_flags =
+                    (tif->tif_flags & ~TIFF_FILLORDER) | FILLORDER_MSB2LSB;
+                break;
+            case 'L':
+                tif->tif_flags =
+                    (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) | FILLORDER_MSB2LSB;
+                break;
+            case 'M':
+                if (m == O_RDONLY)
+                    tif->tif_flags |= TIFF_MAPPED;
+                break;
+            case 'm':
+                if (m == O_RDONLY)
+                    tif->tif_flags &= ~TIFF_MAPPED;
+                break;
+            case 'C':
+                if (m == O_RDONLY)
+                    tif->tif_flags |= TIFF_STRIPCHOP;
+                break;
+            case 'c':
+                if (m == O_RDONLY)
+                    tif->tif_flags &= ~TIFF_STRIPCHOP;
+                break;
+            case 'h':
+                tif->tif_flags |= TIFF_HEADERONLY;
+                break;
+            case '8':
+                if (m & O_CREAT)
+                    tif->tif_flags |= TIFF_BIGTIFF;
+                break;
+            case 'D':
+                tif->tif_flags |= TIFF_DEFERSTRILELOAD;
+                break;
+            case 'O':
+                if (m == O_RDONLY)
+                    tif->tif_flags |=
+                        (TIFF_LAZYSTRILELOAD | TIFF_DEFERSTRILELOAD);
+                break;
+        }
 
-	switch (mode[0]) {
-		case 'r':
-			if (!(tif->tif_flags&TIFF_BIGTIFF))
-				tif->tif_nextdiroff = tif->tif_header.classic.tiff_diroff;
-			else
-				tif->tif_nextdiroff = tif->tif_header.big.tiff_diroff;
-			/*
-			 * Try to use a memory-mapped file if the client
-			 * has not explicitly suppressed usage with the
-			 * 'm' flag in the open mode (see above).
-			 */
-			if (tif->tif_flags & TIFF_MAPPED)
-			{
-				toff_t n;
-				if (TIFFMapFileContents(tif,(void**)(&tif->tif_base),&n))
-				{
-					tif->tif_size=(tmsize_t)n;
-					assert((toff_t)tif->tif_size==n);
-				}
-				else
-					tif->tif_flags &= ~TIFF_MAPPED;
-			}
-			/*
-			 * Sometimes we do not want to read the first directory (for example,
-			 * it may be broken) and want to proceed to other directories. I this
-			 * case we use the TIFF_HEADERONLY flag to open file and return
-			 * immediately after reading TIFF header.
-			 */
-			if (tif->tif_flags & TIFF_HEADERONLY)
-				return (tif);
+#ifdef DEFER_STRILE_LOAD
+    /* Compatibility with old DEFER_STRILE_LOAD compilation flag */
+    /* Probably unneeded, since to the best of my knowledge (E. Rouault) */
+    /* GDAL was the only user of this, and will now use the new 'D' flag */
+    tif->tif_flags |= TIFF_DEFERSTRILELOAD;
+#endif
 
-			/*
-			 * Setup initial directory.
-			 */
-			if (TIFFReadDirectory(tif)) {
-				tif->tif_rawcc = (tmsize_t)-1;
-				tif->tif_flags |= TIFF_BUFFERSETUP;
-				return (tif);
-			}
-			break;
-		case 'a':
-			/*
-			 * New directories are automatically append
-			 * to the end of the directory chain when they
-			 * are written out (see TIFFWriteDirectory).
-			 */
-			if (!TIFFDefaultDirectory(tif))
-				goto bad;
-			return (tif);
-	}
+    /*
+     * Read in TIFF header.
+     */
+    if ((m & O_TRUNC) ||
+        !ReadOK(tif, &tif->tif_header, sizeof(TIFFHeaderClassic)))
+    {
+        if (tif->tif_mode == O_RDONLY)
+        {
+            TIFFErrorExtR(tif, name, "Cannot read TIFF header");
+            goto bad;
+        }
+/*
+ * Setup header and write.
+ */
+#ifdef WORDS_BIGENDIAN
+        tif->tif_header.common.tiff_magic =
+            (tif->tif_flags & TIFF_SWAB) ? TIFF_LITTLEENDIAN : TIFF_BIGENDIAN;
+#else
+        tif->tif_header.common.tiff_magic =
+            (tif->tif_flags & TIFF_SWAB) ? TIFF_BIGENDIAN : TIFF_LITTLEENDIAN;
+#endif
+        if (!(tif->tif_flags & TIFF_BIGTIFF))
+        {
+            tif->tif_header.common.tiff_version = TIFF_VERSION_CLASSIC;
+            tif->tif_header.classic.tiff_diroff = 0;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabShort(&tif->tif_header.common.tiff_version);
+            tif->tif_header_size = sizeof(TIFFHeaderClassic);
+        }
+        else
+        {
+            tif->tif_header.common.tiff_version = TIFF_VERSION_BIG;
+            tif->tif_header.big.tiff_offsetsize = 8;
+            tif->tif_header.big.tiff_unused = 0;
+            tif->tif_header.big.tiff_diroff = 0;
+            if (tif->tif_flags & TIFF_SWAB)
+            {
+                TIFFSwabShort(&tif->tif_header.common.tiff_version);
+                TIFFSwabShort(&tif->tif_header.big.tiff_offsetsize);
+            }
+            tif->tif_header_size = sizeof(TIFFHeaderBig);
+        }
+        /*
+         * The doc for "fopen" for some STD_C_LIBs says that if you
+         * open a file for modify ("+"), then you must fseek (or
+         * fflush?) between any freads and fwrites.  This is not
+         * necessary on most systems, but has been shown to be needed
+         * on Solaris.
+         */
+        TIFFSeekFile(tif, 0, SEEK_SET);
+        if (!WriteOK(tif, &tif->tif_header, (tmsize_t)(tif->tif_header_size)))
+        {
+            TIFFErrorExtR(tif, name, "Error writing TIFF header");
+            goto bad;
+        }
+        /*
+         * Setup the byte order handling.
+         */
+        if (tif->tif_header.common.tiff_magic == TIFF_BIGENDIAN)
+        {
+#ifndef WORDS_BIGENDIAN
+            tif->tif_flags |= TIFF_SWAB;
+#endif
+        }
+        else
+        {
+#ifdef WORDS_BIGENDIAN
+            tif->tif_flags |= TIFF_SWAB;
+#endif
+        }
+        /*
+         * Setup default directory.
+         */
+        if (!TIFFDefaultDirectory(tif))
+            goto bad;
+        tif->tif_diroff = 0;
+        tif->tif_lastdiroff = 0;
+        tif->tif_setdirectory_force_absolute = FALSE;
+        return (tif);
+    }
+    /*
+     * Setup the byte order handling.
+     */
+    if (tif->tif_header.common.tiff_magic != TIFF_BIGENDIAN &&
+        tif->tif_header.common.tiff_magic != TIFF_LITTLEENDIAN
+#if MDI_SUPPORT
+        &&
+#if HOST_BIGENDIAN
+        tif->tif_header.common.tiff_magic != MDI_BIGENDIAN
+#else
+        tif->tif_header.common.tiff_magic != MDI_LITTLEENDIAN
+#endif
+    )
+    {
+        TIFFErrorExtR(tif, name,
+                      "Not a TIFF or MDI file, bad magic number %" PRIu16
+                      " (0x%" PRIx16 ")",
+#else
+    )
+    {
+        TIFFErrorExtR(tif, name,
+                      "Not a TIFF file, bad magic number %" PRIu16
+                      " (0x%" PRIx16 ")",
+#endif
+                      tif->tif_header.common.tiff_magic,
+                      tif->tif_header.common.tiff_magic);
+        goto bad;
+    }
+    if (tif->tif_header.common.tiff_magic == TIFF_BIGENDIAN)
+    {
+#ifndef WORDS_BIGENDIAN
+        tif->tif_flags |= TIFF_SWAB;
+#endif
+    }
+    else
+    {
+#ifdef WORDS_BIGENDIAN
+        tif->tif_flags |= TIFF_SWAB;
+#endif
+    }
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabShort(&tif->tif_header.common.tiff_version);
+    if ((tif->tif_header.common.tiff_version != TIFF_VERSION_CLASSIC) &&
+        (tif->tif_header.common.tiff_version != TIFF_VERSION_BIG))
+    {
+        TIFFErrorExtR(tif, name,
+                      "Not a TIFF file, bad version number %" PRIu16
+                      " (0x%" PRIx16 ")",
+                      tif->tif_header.common.tiff_version,
+                      tif->tif_header.common.tiff_version);
+        goto bad;
+    }
+    if (tif->tif_header.common.tiff_version == TIFF_VERSION_CLASSIC)
+    {
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabLong(&tif->tif_header.classic.tiff_diroff);
+        tif->tif_header_size = sizeof(TIFFHeaderClassic);
+    }
+    else
+    {
+        if (!ReadOK(tif,
+                    ((uint8_t *)(&tif->tif_header) + sizeof(TIFFHeaderClassic)),
+                    (sizeof(TIFFHeaderBig) - sizeof(TIFFHeaderClassic))))
+        {
+            TIFFErrorExtR(tif, name, "Cannot read TIFF header");
+            goto bad;
+        }
+        if (tif->tif_flags & TIFF_SWAB)
+        {
+            TIFFSwabShort(&tif->tif_header.big.tiff_offsetsize);
+            TIFFSwabLong8(&tif->tif_header.big.tiff_diroff);
+        }
+        if (tif->tif_header.big.tiff_offsetsize != 8)
+        {
+            TIFFErrorExtR(tif, name,
+                          "Not a TIFF file, bad BigTIFF offsetsize %" PRIu16
+                          " (0x%" PRIx16 ")",
+                          tif->tif_header.big.tiff_offsetsize,
+                          tif->tif_header.big.tiff_offsetsize);
+            goto bad;
+        }
+        if (tif->tif_header.big.tiff_unused != 0)
+        {
+            TIFFErrorExtR(tif, name,
+                          "Not a TIFF file, bad BigTIFF unused %" PRIu16
+                          " (0x%" PRIx16 ")",
+                          tif->tif_header.big.tiff_unused,
+                          tif->tif_header.big.tiff_unused);
+            goto bad;
+        }
+        tif->tif_header_size = sizeof(TIFFHeaderBig);
+        tif->tif_flags |= TIFF_BIGTIFF;
+    }
+    tif->tif_flags |= TIFF_MYBUFFER;
+    tif->tif_rawcp = tif->tif_rawdata = 0;
+    tif->tif_rawdatasize = 0;
+    tif->tif_rawdataoff = 0;
+    tif->tif_rawdataloaded = 0;
+
+    switch (mode[0])
+    {
+        case 'r':
+            if (!(tif->tif_flags & TIFF_BIGTIFF))
+                tif->tif_nextdiroff = tif->tif_header.classic.tiff_diroff;
+            else
+                tif->tif_nextdiroff = tif->tif_header.big.tiff_diroff;
+            /*
+             * Try to use a memory-mapped file if the client
+             * has not explicitly suppressed usage with the
+             * 'm' flag in the open mode (see above).
+             */
+            if (tif->tif_flags & TIFF_MAPPED)
+            {
+                toff_t n;
+                if (TIFFMapFileContents(tif, (void **)(&tif->tif_base), &n))
+                {
+                    tif->tif_size = (tmsize_t)n;
+                    assert((toff_t)tif->tif_size == n);
+                }
+                else
+                    tif->tif_flags &= ~TIFF_MAPPED;
+            }
+            /*
+             * Sometimes we do not want to read the first directory (for
+             * example, it may be broken) and want to proceed to other
+             * directories. I this case we use the TIFF_HEADERONLY flag to open
+             * file and return immediately after reading TIFF header.
+             */
+            if (tif->tif_flags & TIFF_HEADERONLY)
+                return (tif);
+
+            /*
+             * Setup initial directory.
+             */
+            if (TIFFReadDirectory(tif))
+            {
+                return (tif);
+            }
+            break;
+        case 'a':
+            /*
+             * New directories are automatically append
+             * to the end of the directory chain when they
+             * are written out (see TIFFWriteDirectory).
+             */
+            if (!TIFFDefaultDirectory(tif))
+                goto bad;
+            return (tif);
+    }
 bad:
-	tif->tif_mode = O_RDONLY;	/* XXX avoid flush */
-        TIFFCleanup(tif);
+    tif->tif_mode = O_RDONLY; /* XXX avoid flush */
+    TIFFCleanup(tif);
 bad2:
-	return ((TIFF*)0);
+    return ((TIFF *)0);
 }
 
 /*
@@ -509,233 +655,154 @@
 /*
  * Return open file's name.
  */
-const char *
-TIFFFileName(TIFF* tif)
-{
-	return (tif->tif_name);
-}
+const char *TIFFFileName(TIFF *tif) { return (tif->tif_name); }
 
 /*
  * Set the file name.
  */
-const char *
-TIFFSetFileName(TIFF* tif, const char *name)
+const char *TIFFSetFileName(TIFF *tif, const char *name)
 {
-	const char* old_name = tif->tif_name;
-	tif->tif_name = (char *)name;
-	return (old_name);
+    const char *old_name = tif->tif_name;
+    tif->tif_name = (char *)name;
+    return (old_name);
 }
 
 /*
  * Return open file's I/O descriptor.
  */
-int
-TIFFFileno(TIFF* tif)
-{
-	return (tif->tif_fd);
-}
+int TIFFFileno(TIFF *tif) { return (tif->tif_fd); }
 
 /*
  * Set open file's I/O descriptor, and return previous value.
  */
-int
-TIFFSetFileno(TIFF* tif, int fd)
+int TIFFSetFileno(TIFF *tif, int fd)
 {
-        int old_fd = tif->tif_fd;
-	tif->tif_fd = fd;
-	return old_fd;
+    int old_fd = tif->tif_fd;
+    tif->tif_fd = fd;
+    return old_fd;
 }
 
 /*
  * Return open file's clientdata.
  */
-thandle_t
-TIFFClientdata(TIFF* tif)
-{
-	return (tif->tif_clientdata);
-}
+thandle_t TIFFClientdata(TIFF *tif) { return (tif->tif_clientdata); }
 
 /*
  * Set open file's clientdata, and return previous value.
  */
-thandle_t
-TIFFSetClientdata(TIFF* tif, thandle_t newvalue)
+thandle_t TIFFSetClientdata(TIFF *tif, thandle_t newvalue)
 {
-	thandle_t m = tif->tif_clientdata;
-	tif->tif_clientdata = newvalue;
-	return m;
+    thandle_t m = tif->tif_clientdata;
+    tif->tif_clientdata = newvalue;
+    return m;
 }
 
 /*
  * Return read/write mode.
  */
-int
-TIFFGetMode(TIFF* tif)
-{
-	return (tif->tif_mode);
-}
+int TIFFGetMode(TIFF *tif) { return (tif->tif_mode); }
 
 /*
  * Return read/write mode.
  */
-int
-TIFFSetMode(TIFF* tif, int mode)
+int TIFFSetMode(TIFF *tif, int mode)
 {
-	int old_mode = tif->tif_mode;
-	tif->tif_mode = mode;
-	return (old_mode);
+    int old_mode = tif->tif_mode;
+    tif->tif_mode = mode;
+    return (old_mode);
 }
 
 /*
  * Return nonzero if file is organized in
  * tiles; zero if organized as strips.
  */
-int
-TIFFIsTiled(TIFF* tif)
-{
-	return (isTiled(tif));
-}
+int TIFFIsTiled(TIFF *tif) { return (isTiled(tif)); }
 
 /*
  * Return current row being read/written.
  */
-uint32
-TIFFCurrentRow(TIFF* tif)
-{
-	return (tif->tif_row);
-}
+uint32_t TIFFCurrentRow(TIFF *tif) { return (tif->tif_row); }
 
 /*
  * Return index of the current directory.
  */
-uint16
-TIFFCurrentDirectory(TIFF* tif)
-{
-	return (tif->tif_curdir);
-}
+tdir_t TIFFCurrentDirectory(TIFF *tif) { return (tif->tif_curdir); }
 
 /*
  * Return current strip.
  */
-uint32
-TIFFCurrentStrip(TIFF* tif)
-{
-	return (tif->tif_curstrip);
-}
+uint32_t TIFFCurrentStrip(TIFF *tif) { return (tif->tif_curstrip); }
 
 /*
  * Return current tile.
  */
-uint32
-TIFFCurrentTile(TIFF* tif)
-{
-	return (tif->tif_curtile);
-}
+uint32_t TIFFCurrentTile(TIFF *tif) { return (tif->tif_curtile); }
 
 /*
  * Return nonzero if the file has byte-swapped data.
  */
-int
-TIFFIsByteSwapped(TIFF* tif)
-{
-	return ((tif->tif_flags & TIFF_SWAB) != 0);
-}
+int TIFFIsByteSwapped(TIFF *tif) { return ((tif->tif_flags & TIFF_SWAB) != 0); }
 
 /*
  * Return nonzero if the data is returned up-sampled.
  */
-int
-TIFFIsUpSampled(TIFF* tif)
-{
-	return (isUpSampled(tif));
-}
+int TIFFIsUpSampled(TIFF *tif) { return (isUpSampled(tif)); }
 
 /*
  * Return nonzero if the data is returned in MSB-to-LSB bit order.
  */
-int
-TIFFIsMSB2LSB(TIFF* tif)
-{
-	return (isFillOrder(tif, FILLORDER_MSB2LSB));
-}
+int TIFFIsMSB2LSB(TIFF *tif) { return (isFillOrder(tif, FILLORDER_MSB2LSB)); }
 
 /*
  * Return nonzero if given file was written in big-endian order.
  */
-int
-TIFFIsBigEndian(TIFF* tif)
+int TIFFIsBigEndian(TIFF *tif)
 {
-	return (tif->tif_header.common.tiff_magic == TIFF_BIGENDIAN);
+    return (tif->tif_header.common.tiff_magic == TIFF_BIGENDIAN);
+}
+
+/*
+ * Return nonzero if given file is BigTIFF style.
+ */
+int TIFFIsBigTIFF(TIFF *tif)
+{
+    return (tif->tif_header.common.tiff_version == TIFF_VERSION_BIG);
 }
 
 /*
  * Return pointer to file read method.
  */
-TIFFReadWriteProc
-TIFFGetReadProc(TIFF* tif)
-{
-	return (tif->tif_readproc);
-}
+TIFFReadWriteProc TIFFGetReadProc(TIFF *tif) { return (tif->tif_readproc); }
 
 /*
  * Return pointer to file write method.
  */
-TIFFReadWriteProc
-TIFFGetWriteProc(TIFF* tif)
-{
-	return (tif->tif_writeproc);
-}
+TIFFReadWriteProc TIFFGetWriteProc(TIFF *tif) { return (tif->tif_writeproc); }
 
 /*
  * Return pointer to file seek method.
  */
-TIFFSeekProc
-TIFFGetSeekProc(TIFF* tif)
-{
-	return (tif->tif_seekproc);
-}
+TIFFSeekProc TIFFGetSeekProc(TIFF *tif) { return (tif->tif_seekproc); }
 
 /*
  * Return pointer to file close method.
  */
-TIFFCloseProc
-TIFFGetCloseProc(TIFF* tif)
-{
-	return (tif->tif_closeproc);
-}
+TIFFCloseProc TIFFGetCloseProc(TIFF *tif) { return (tif->tif_closeproc); }
 
 /*
  * Return pointer to file size requesting method.
  */
-TIFFSizeProc
-TIFFGetSizeProc(TIFF* tif)
-{
-	return (tif->tif_sizeproc);
-}
+TIFFSizeProc TIFFGetSizeProc(TIFF *tif) { return (tif->tif_sizeproc); }
 
 /*
  * Return pointer to memory mapping method.
  */
-TIFFMapFileProc
-TIFFGetMapFileProc(TIFF* tif)
-{
-	return (tif->tif_mapproc);
-}
+TIFFMapFileProc TIFFGetMapFileProc(TIFF *tif) { return (tif->tif_mapproc); }
 
 /*
  * Return pointer to memory unmapping method.
  */
-TIFFUnmapFileProc
-TIFFGetUnmapFileProc(TIFF* tif)
+TIFFUnmapFileProc TIFFGetUnmapFileProc(TIFF *tif)
 {
-	return (tif->tif_unmapproc);
+    return (tif->tif_unmapproc);
 }
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_packbits.c b/third_party/libtiff/tif_packbits.c
index a8f29e8..62849f8 100644
--- a/third_party/libtiff/tif_packbits.c
+++ b/third_party/libtiff/tif_packbits.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -31,157 +31,178 @@
  */
 #include <stdio.h>
 
-static int
-PackBitsPreEncode(TIFF* tif, uint16 s)
+static int PackBitsPreEncode(TIFF *tif, uint16_t s)
 {
-	(void) s;
+    (void)s;
 
-        tif->tif_data = (uint8*)_TIFFmalloc(sizeof(tmsize_t));
-	if (tif->tif_data == NULL)
-		return (0);
-	/*
-	 * Calculate the scanline/tile-width size in bytes.
-	 */
-	if (isTiled(tif))
-		*(tmsize_t*)tif->tif_data = TIFFTileRowSize(tif);
-	else
-		*(tmsize_t*)tif->tif_data = TIFFScanlineSize(tif);
-	return (1);
+    tif->tif_data = (uint8_t *)_TIFFmallocExt(tif, sizeof(tmsize_t));
+    if (tif->tif_data == NULL)
+        return (0);
+    /*
+     * Calculate the scanline/tile-width size in bytes.
+     */
+    if (isTiled(tif))
+        *(tmsize_t *)tif->tif_data = TIFFTileRowSize(tif);
+    else
+        *(tmsize_t *)tif->tif_data = TIFFScanlineSize(tif);
+    return (1);
 }
 
-static int
-PackBitsPostEncode(TIFF* tif)
+static int PackBitsPostEncode(TIFF *tif)
 {
-        if (tif->tif_data)
-            _TIFFfree(tif->tif_data);
-	return (1);
+    if (tif->tif_data)
+        _TIFFfreeExt(tif, tif->tif_data);
+    return (1);
 }
 
 /*
  * Encode a run of pixels.
  */
-static int
-PackBitsEncode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s)
+static int PackBitsEncode(TIFF *tif, uint8_t *buf, tmsize_t cc, uint16_t s)
 {
-	unsigned char* bp = (unsigned char*) buf;
-	uint8* op;
-	uint8* ep;
-	uint8* lastliteral;
-	long n, slop;
-	int b;
-	enum { BASE, LITERAL, RUN, LITERAL_RUN } state;
+    unsigned char *bp = (unsigned char *)buf;
+    uint8_t *op;
+    uint8_t *ep;
+    uint8_t *lastliteral;
+    long n, slop;
+    int b;
+    enum
+    {
+        BASE,
+        LITERAL,
+        RUN,
+        LITERAL_RUN
+    } state;
 
-	(void) s;
-	op = tif->tif_rawcp;
-	ep = tif->tif_rawdata + tif->tif_rawdatasize;
-	state = BASE;
-	lastliteral = 0;
-	while (cc > 0) {
-		/*
-		 * Find the longest string of identical bytes.
-		 */
-		b = *bp++;
-		cc--;
-		n = 1;
-		for (; cc > 0 && b == *bp; cc--, bp++)
-			n++;
-	again:
-		if (op + 2 >= ep) {		/* insure space for new data */
-			/*
-			 * Be careful about writing the last
-			 * literal.  Must write up to that point
-			 * and then copy the remainder to the
-			 * front of the buffer.
-			 */
-			if (state == LITERAL || state == LITERAL_RUN) {
-				slop = (long)(op - lastliteral);
-				tif->tif_rawcc += (tmsize_t)(lastliteral - tif->tif_rawcp);
-				if (!TIFFFlushData1(tif))
-					return (0);
-				op = tif->tif_rawcp;
-				while (slop-- > 0)
-					*op++ = *lastliteral++;
-				lastliteral = tif->tif_rawcp;
-			} else {
-				tif->tif_rawcc += (tmsize_t)(op - tif->tif_rawcp);
-				if (!TIFFFlushData1(tif))
-					return (0);
-				op = tif->tif_rawcp;
-			}
-		}
-		switch (state) {
-		case BASE:		/* initial state, set run/literal */
-			if (n > 1) {
-				state = RUN;
-				if (n > 128) {
-					*op++ = (uint8) -127;
-					*op++ = (uint8) b;
-					n -= 128;
-					goto again;
-				}
-				*op++ = (uint8)(-(n-1));
-				*op++ = (uint8) b;
-			} else {
-				lastliteral = op;
-				*op++ = 0;
-				*op++ = (uint8) b;
-				state = LITERAL;
-			}
-			break;
-		case LITERAL:		/* last object was literal string */
-			if (n > 1) {
-				state = LITERAL_RUN;
-				if (n > 128) {
-					*op++ = (uint8) -127;
-					*op++ = (uint8) b;
-					n -= 128;
-					goto again;
-				}
-				*op++ = (uint8)(-(n-1));	/* encode run */
-				*op++ = (uint8) b;
-			} else {			/* extend literal */
-				if (++(*lastliteral) == 127)
-					state = BASE;
-				*op++ = (uint8) b;
-			}
-			break;
-		case RUN:		/* last object was run */
-			if (n > 1) {
-				if (n > 128) {
-					*op++ = (uint8) -127;
-					*op++ = (uint8) b;
-					n -= 128;
-					goto again;
-				}
-				*op++ = (uint8)(-(n-1));
-				*op++ = (uint8) b;
-			} else {
-				lastliteral = op;
-				*op++ = 0;
-				*op++ = (uint8) b;
-				state = LITERAL;
-			}
-			break;
-		case LITERAL_RUN:	/* literal followed by a run */
-			/*
-			 * Check to see if previous run should
-			 * be converted to a literal, in which
-			 * case we convert literal-run-literal
-			 * to a single literal.
-			 */
-			if (n == 1 && op[-2] == (uint8) -1 &&
-			    *lastliteral < 126) {
-				state = (((*lastliteral) += 2) == 127 ?
-				    BASE : LITERAL);
-				op[-2] = op[-1];	/* replicate */
-			} else
-				state = RUN;
-			goto again;
-		}
-	}
-	tif->tif_rawcc += (tmsize_t)(op - tif->tif_rawcp);
-	tif->tif_rawcp = op;
-	return (1);
+    (void)s;
+    op = tif->tif_rawcp;
+    ep = tif->tif_rawdata + tif->tif_rawdatasize;
+    state = BASE;
+    lastliteral = 0;
+    while (cc > 0)
+    {
+        /*
+         * Find the longest string of identical bytes.
+         */
+        b = *bp++;
+        cc--;
+        n = 1;
+        for (; cc > 0 && b == *bp; cc--, bp++)
+            n++;
+    again:
+        if (op + 2 >= ep)
+        { /* insure space for new data */
+            /*
+             * Be careful about writing the last
+             * literal.  Must write up to that point
+             * and then copy the remainder to the
+             * front of the buffer.
+             */
+            if (state == LITERAL || state == LITERAL_RUN)
+            {
+                slop = (long)(op - lastliteral);
+                tif->tif_rawcc += (tmsize_t)(lastliteral - tif->tif_rawcp);
+                if (!TIFFFlushData1(tif))
+                    return (0);
+                op = tif->tif_rawcp;
+                while (slop-- > 0)
+                    *op++ = *lastliteral++;
+                lastliteral = tif->tif_rawcp;
+            }
+            else
+            {
+                tif->tif_rawcc += (tmsize_t)(op - tif->tif_rawcp);
+                if (!TIFFFlushData1(tif))
+                    return (0);
+                op = tif->tif_rawcp;
+            }
+        }
+        switch (state)
+        {
+            case BASE: /* initial state, set run/literal */
+                if (n > 1)
+                {
+                    state = RUN;
+                    if (n > 128)
+                    {
+                        *op++ = (uint8_t)-127;
+                        *op++ = (uint8_t)b;
+                        n -= 128;
+                        goto again;
+                    }
+                    *op++ = (uint8_t)(-(n - 1));
+                    *op++ = (uint8_t)b;
+                }
+                else
+                {
+                    lastliteral = op;
+                    *op++ = 0;
+                    *op++ = (uint8_t)b;
+                    state = LITERAL;
+                }
+                break;
+            case LITERAL: /* last object was literal string */
+                if (n > 1)
+                {
+                    state = LITERAL_RUN;
+                    if (n > 128)
+                    {
+                        *op++ = (uint8_t)-127;
+                        *op++ = (uint8_t)b;
+                        n -= 128;
+                        goto again;
+                    }
+                    *op++ = (uint8_t)(-(n - 1)); /* encode run */
+                    *op++ = (uint8_t)b;
+                }
+                else
+                { /* extend literal */
+                    if (++(*lastliteral) == 127)
+                        state = BASE;
+                    *op++ = (uint8_t)b;
+                }
+                break;
+            case RUN: /* last object was run */
+                if (n > 1)
+                {
+                    if (n > 128)
+                    {
+                        *op++ = (uint8_t)-127;
+                        *op++ = (uint8_t)b;
+                        n -= 128;
+                        goto again;
+                    }
+                    *op++ = (uint8_t)(-(n - 1));
+                    *op++ = (uint8_t)b;
+                }
+                else
+                {
+                    lastliteral = op;
+                    *op++ = 0;
+                    *op++ = (uint8_t)b;
+                    state = LITERAL;
+                }
+                break;
+            case LITERAL_RUN: /* literal followed by a run */
+                /*
+                 * Check to see if previous run should
+                 * be converted to a literal, in which
+                 * case we convert literal-run-literal
+                 * to a single literal.
+                 */
+                if (n == 1 && op[-2] == (uint8_t)-1 && *lastliteral < 126)
+                {
+                    state = (((*lastliteral) += 2) == 127 ? BASE : LITERAL);
+                    op[-2] = op[-1]; /* replicate */
+                }
+                else
+                    state = RUN;
+                goto again;
+        }
+    }
+    tif->tif_rawcc += (tmsize_t)(op - tif->tif_rawcp);
+    tif->tif_rawcp = op;
+    return (1);
 }
 
 /*
@@ -191,119 +212,112 @@
  * the decoder if data is read, for example, by scanlines
  * when it was encoded by strips.
  */
-static int
-PackBitsEncodeChunk(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s)
+static int PackBitsEncodeChunk(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s)
 {
-	tmsize_t rowsize = *(tmsize_t*)tif->tif_data;
+    tmsize_t rowsize = *(tmsize_t *)tif->tif_data;
 
-	while (cc > 0) {
-		tmsize_t chunk = rowsize;
-		
-		if( cc < chunk )
-		    chunk = cc;
+    while (cc > 0)
+    {
+        tmsize_t chunk = rowsize;
 
-		if (PackBitsEncode(tif, bp, chunk, s) < 0)
-		    return (-1);
-		bp += chunk;
-		cc -= chunk;
-	}
-	return (1);
+        if (cc < chunk)
+            chunk = cc;
+
+        if (PackBitsEncode(tif, bp, chunk, s) < 0)
+            return (-1);
+        bp += chunk;
+        cc -= chunk;
+    }
+    return (1);
 }
 
-static int
-PackBitsDecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s)
+static int PackBitsDecode(TIFF *tif, uint8_t *op, tmsize_t occ, uint16_t s)
 {
-	static const char module[] = "PackBitsDecode";
-	char *bp;
-	tmsize_t cc;
-	long n;
-	int b;
+    static const char module[] = "PackBitsDecode";
+    int8_t *bp;
+    tmsize_t cc;
+    long n;
+    int b;
 
-	(void) s;
-	bp = (char*) tif->tif_rawcp;
-	cc = tif->tif_rawcc;
-	while (cc > 0 && occ > 0) {
-		n = (long) *bp++;
-		cc--;
-		/*
-		 * Watch out for compilers that
-		 * don't sign extend chars...
-		 */
-		if (n >= 128)
-			n -= 256;
-		if (n < 0) {		/* replicate next byte -n+1 times */
-			if (n == -128)	/* nop */
-				continue;
-			n = -n + 1;
-			if( occ < (tmsize_t)n )
-			{
-				TIFFWarningExt(tif->tif_clientdata, module,
-				    "Discarding %lu bytes to avoid buffer overrun",
-				    (unsigned long) ((tmsize_t)n - occ));
-				n = (long)occ;
-			}
-			if( cc == 0 )
-			{
-				TIFFWarningExt(tif->tif_clientdata, module,
-					       "Terminating PackBitsDecode due to lack of data.");
-				break;
-			}
-			occ -= n;
-			b = *bp++;
-			cc--;
-			while (n-- > 0)
-				*op++ = (uint8) b;
-		} else {		/* copy next n+1 bytes literally */
-			if (occ < (tmsize_t)(n + 1))
-			{
-				TIFFWarningExt(tif->tif_clientdata, module,
-				    "Discarding %lu bytes to avoid buffer overrun",
-				    (unsigned long) ((tmsize_t)n - occ + 1));
-				n = (long)occ - 1;
-			}
-			if (cc < (tmsize_t) (n+1)) 
-			{
-				TIFFWarningExt(tif->tif_clientdata, module,
-					       "Terminating PackBitsDecode due to lack of data.");
-				break;
-			}
-			_TIFFmemcpy(op, bp, ++n);
-			op += n; occ -= n;
-			bp += n; cc -= n;
-		}
-	}
-	tif->tif_rawcp = (uint8*) bp;
-	tif->tif_rawcc = cc;
-	if (occ > 0) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "Not enough data for scanline %lu",
-		    (unsigned long) tif->tif_row);
-		return (0);
-	}
-	return (1);
+    (void)s;
+    bp = (int8_t *)tif->tif_rawcp;
+    cc = tif->tif_rawcc;
+    while (cc > 0 && occ > 0)
+    {
+        n = (long)*bp++;
+        cc--;
+        if (n < 0)
+        {                  /* replicate next byte -n+1 times */
+            if (n == -128) /* nop */
+                continue;
+            n = -n + 1;
+            if (occ < (tmsize_t)n)
+            {
+                TIFFWarningExtR(tif, module,
+                                "Discarding %" TIFF_SSIZE_FORMAT
+                                " bytes to avoid buffer overrun",
+                                (tmsize_t)n - occ);
+                n = (long)occ;
+            }
+            if (cc == 0)
+            {
+                TIFFWarningExtR(
+                    tif, module,
+                    "Terminating PackBitsDecode due to lack of data.");
+                break;
+            }
+            occ -= n;
+            b = *bp++;
+            cc--;
+            while (n-- > 0)
+                *op++ = (uint8_t)b;
+        }
+        else
+        { /* copy next n+1 bytes literally */
+            if (occ < (tmsize_t)(n + 1))
+            {
+                TIFFWarningExtR(tif, module,
+                                "Discarding %" TIFF_SSIZE_FORMAT
+                                " bytes to avoid buffer overrun",
+                                (tmsize_t)n - occ + 1);
+                n = (long)occ - 1;
+            }
+            if (cc < (tmsize_t)(n + 1))
+            {
+                TIFFWarningExtR(
+                    tif, module,
+                    "Terminating PackBitsDecode due to lack of data.");
+                break;
+            }
+            _TIFFmemcpy(op, bp, ++n);
+            op += n;
+            occ -= n;
+            bp += n;
+            cc -= n;
+        }
+    }
+    tif->tif_rawcp = (uint8_t *)bp;
+    tif->tif_rawcc = cc;
+    if (occ > 0)
+    {
+        TIFFErrorExtR(tif, module, "Not enough data for scanline %" PRIu32,
+                      tif->tif_row);
+        return (0);
+    }
+    return (1);
 }
 
-int
-TIFFInitPackBits(TIFF* tif, int scheme)
+int TIFFInitPackBits(TIFF *tif, int scheme)
 {
-	(void) scheme;
-	tif->tif_decoderow = PackBitsDecode;
-	tif->tif_decodestrip = PackBitsDecode;
-	tif->tif_decodetile = PackBitsDecode;
-	tif->tif_preencode = PackBitsPreEncode;
-	tif->tif_postencode = PackBitsPostEncode;
-	tif->tif_encoderow = PackBitsEncode;
-	tif->tif_encodestrip = PackBitsEncodeChunk;
-	tif->tif_encodetile = PackBitsEncodeChunk;
-	return (1);
+    (void)scheme;
+    tif->tif_decoderow = PackBitsDecode;
+    tif->tif_decodestrip = PackBitsDecode;
+    tif->tif_decodetile = PackBitsDecode;
+    tif->tif_preencode = PackBitsPreEncode;
+    tif->tif_postencode = PackBitsPostEncode;
+    tif->tif_encoderow = PackBitsEncode;
+    tif->tif_encodestrip = PackBitsEncodeChunk;
+    tif->tif_encodetile = PackBitsEncodeChunk;
+    return (1);
 }
 #endif /* PACKBITS_SUPPORT */
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_pixarlog.c b/third_party/libtiff/tif_pixarlog.c
index 6264090..2e22b33 100644
--- a/third_party/libtiff/tif_pixarlog.c
+++ b/third_party/libtiff/tif_pixarlog.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1996-1997 Sam Leffler
  * Copyright (c) 1996 Pixar
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Pixar, Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Pixar, Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL PIXAR, SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -32,10 +32,10 @@
  * Contributed by Dan McCoy.
  *
  * PixarLog film support uses the TIFF library to store companded
- * 11 bit values into a tiff file, which are compressed using the 
- * zip compressor.  
+ * 11 bit values into a tiff file, which are compressed using the
+ * zip compressor.
  *
- * The codec can take as input and produce as output 32-bit IEEE float values 
+ * The codec can take as input and produce as output 32-bit IEEE float values
  * as well as 16-bit or 8-bit unsigned integer values.
  *
  * On writing any of the above are converted into the internal
@@ -49,7 +49,7 @@
  * than the human eye can perceive with extra room to allow for
  * error introduced by further image computation.  As with any quantized
  * color format, it is possible to perform image calculations which
- * expose the quantization error. This format should certainly be less 
+ * expose the quantization error. This format should certainly be less
  * susceptible to such errors than standard 8-bit encodings, but more
  * susceptible than straight 16-bit or 32-bit encodings.
  *
@@ -90,363 +90,429 @@
 #include "tif_predict.h"
 #include "zlib.h"
 
+#include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <math.h>
 
 /* Tables for converting to/from 11 bit coded values */
 
-#define  TSIZE	 2048		/* decode table size (11-bit tokens) */
-#define  TSIZEP1 2049		/* Plus one for slop */
-#define  ONE	 1250		/* token value of 1.0 exactly */
-#define  RATIO	 1.004		/* nominal ratio for log part */
+#define TSIZE 2048   /* decode table size (11-bit tokens) */
+#define TSIZEP1 2049 /* Plus one for slop */
+#define ONE 1250     /* token value of 1.0 exactly */
+#define RATIO 1.004  /* nominal ratio for log part */
 
-#define CODE_MASK 0x7ff         /* 11 bits. */
+#define CODE_MASK 0x7ff /* 11 bits. */
 
-static float  Fltsize;
-static float  LogK1, LogK2;
+static float Fltsize;
+static float LogK1, LogK2;
 
-#define REPEAT(n, op)   { int i; i=n; do { i--; op; } while (i>0); }
+#define REPEAT(n, op)                                                          \
+    {                                                                          \
+        int i;                                                                 \
+        i = n;                                                                 \
+        do                                                                     \
+        {                                                                      \
+            i--;                                                               \
+            op;                                                                \
+        } while (i > 0);                                                       \
+    }
 
-static void
-horizontalAccumulateF(uint16 *wp, int n, int stride, float *op,
-	float *ToLinearF)
+static void horizontalAccumulateF(uint16_t *wp, int n, int stride, float *op,
+                                  float *ToLinearF)
 {
-    register unsigned int  cr, cg, cb, ca, mask;
-    register float  t0, t1, t2, t3;
+    register unsigned int cr, cg, cb, ca, mask;
+    register float t0, t1, t2, t3;
 
-    if (n >= stride) {
-	mask = CODE_MASK;
-	if (stride == 3) {
-	    t0 = ToLinearF[cr = (wp[0] & mask)];
-	    t1 = ToLinearF[cg = (wp[1] & mask)];
-	    t2 = ToLinearF[cb = (wp[2] & mask)];
-	    op[0] = t0;
-	    op[1] = t1;
-	    op[2] = t2;
-	    n -= 3;
-	    while (n > 0) {
-		wp += 3;
-		op += 3;
-		n -= 3;
-		t0 = ToLinearF[(cr += wp[0]) & mask];
-		t1 = ToLinearF[(cg += wp[1]) & mask];
-		t2 = ToLinearF[(cb += wp[2]) & mask];
-		op[0] = t0;
-		op[1] = t1;
-		op[2] = t2;
-	    }
-	} else if (stride == 4) {
-	    t0 = ToLinearF[cr = (wp[0] & mask)];
-	    t1 = ToLinearF[cg = (wp[1] & mask)];
-	    t2 = ToLinearF[cb = (wp[2] & mask)];
-	    t3 = ToLinearF[ca = (wp[3] & mask)];
-	    op[0] = t0;
-	    op[1] = t1;
-	    op[2] = t2;
-	    op[3] = t3;
-	    n -= 4;
-	    while (n > 0) {
-		wp += 4;
-		op += 4;
-		n -= 4;
-		t0 = ToLinearF[(cr += wp[0]) & mask];
-		t1 = ToLinearF[(cg += wp[1]) & mask];
-		t2 = ToLinearF[(cb += wp[2]) & mask];
-		t3 = ToLinearF[(ca += wp[3]) & mask];
-		op[0] = t0;
-		op[1] = t1;
-		op[2] = t2;
-		op[3] = t3;
-	    }
-	} else {
-	    REPEAT(stride, *op = ToLinearF[*wp&mask]; wp++; op++)
-	    n -= stride;
-	    while (n > 0) {
-		REPEAT(stride,
-		    wp[stride] += *wp; *op = ToLinearF[*wp&mask]; wp++; op++)
-		n -= stride;
-	    }
-	}
+    if (n >= stride)
+    {
+        mask = CODE_MASK;
+        if (stride == 3)
+        {
+            t0 = ToLinearF[cr = (wp[0] & mask)];
+            t1 = ToLinearF[cg = (wp[1] & mask)];
+            t2 = ToLinearF[cb = (wp[2] & mask)];
+            op[0] = t0;
+            op[1] = t1;
+            op[2] = t2;
+            n -= 3;
+            while (n > 0)
+            {
+                wp += 3;
+                op += 3;
+                n -= 3;
+                t0 = ToLinearF[(cr += wp[0]) & mask];
+                t1 = ToLinearF[(cg += wp[1]) & mask];
+                t2 = ToLinearF[(cb += wp[2]) & mask];
+                op[0] = t0;
+                op[1] = t1;
+                op[2] = t2;
+            }
+        }
+        else if (stride == 4)
+        {
+            t0 = ToLinearF[cr = (wp[0] & mask)];
+            t1 = ToLinearF[cg = (wp[1] & mask)];
+            t2 = ToLinearF[cb = (wp[2] & mask)];
+            t3 = ToLinearF[ca = (wp[3] & mask)];
+            op[0] = t0;
+            op[1] = t1;
+            op[2] = t2;
+            op[3] = t3;
+            n -= 4;
+            while (n > 0)
+            {
+                wp += 4;
+                op += 4;
+                n -= 4;
+                t0 = ToLinearF[(cr += wp[0]) & mask];
+                t1 = ToLinearF[(cg += wp[1]) & mask];
+                t2 = ToLinearF[(cb += wp[2]) & mask];
+                t3 = ToLinearF[(ca += wp[3]) & mask];
+                op[0] = t0;
+                op[1] = t1;
+                op[2] = t2;
+                op[3] = t3;
+            }
+        }
+        else
+        {
+            REPEAT(stride, *op = ToLinearF[*wp & mask]; wp++; op++)
+            n -= stride;
+            while (n > 0)
+            {
+                REPEAT(stride, wp[stride] += *wp; *op = ToLinearF[*wp & mask];
+                       wp++; op++)
+                n -= stride;
+            }
+        }
     }
 }
 
-static void
-horizontalAccumulate12(uint16 *wp, int n, int stride, int16 *op,
-	float *ToLinearF)
+static void horizontalAccumulate12(uint16_t *wp, int n, int stride, int16_t *op,
+                                   float *ToLinearF)
 {
-    register unsigned int  cr, cg, cb, ca, mask;
-    register float  t0, t1, t2, t3;
+    register unsigned int cr, cg, cb, ca, mask;
+    register float t0, t1, t2, t3;
 
 #define SCALE12 2048.0F
-#define CLAMP12(t) (((t) < 3071) ? (uint16) (t) : 3071)
+#define CLAMP12(t) (((t) < 3071) ? (uint16_t)(t) : 3071)
 
-    if (n >= stride) {
-	mask = CODE_MASK;
-	if (stride == 3) {
-	    t0 = ToLinearF[cr = (wp[0] & mask)] * SCALE12;
-	    t1 = ToLinearF[cg = (wp[1] & mask)] * SCALE12;
-	    t2 = ToLinearF[cb = (wp[2] & mask)] * SCALE12;
-	    op[0] = CLAMP12(t0);
-	    op[1] = CLAMP12(t1);
-	    op[2] = CLAMP12(t2);
-	    n -= 3;
-	    while (n > 0) {
-		wp += 3;
-		op += 3;
-		n -= 3;
-		t0 = ToLinearF[(cr += wp[0]) & mask] * SCALE12;
-		t1 = ToLinearF[(cg += wp[1]) & mask] * SCALE12;
-		t2 = ToLinearF[(cb += wp[2]) & mask] * SCALE12;
-		op[0] = CLAMP12(t0);
-		op[1] = CLAMP12(t1);
-		op[2] = CLAMP12(t2);
-	    }
-	} else if (stride == 4) {
-	    t0 = ToLinearF[cr = (wp[0] & mask)] * SCALE12;
-	    t1 = ToLinearF[cg = (wp[1] & mask)] * SCALE12;
-	    t2 = ToLinearF[cb = (wp[2] & mask)] * SCALE12;
-	    t3 = ToLinearF[ca = (wp[3] & mask)] * SCALE12;
-	    op[0] = CLAMP12(t0);
-	    op[1] = CLAMP12(t1);
-	    op[2] = CLAMP12(t2);
-	    op[3] = CLAMP12(t3);
-	    n -= 4;
-	    while (n > 0) {
-		wp += 4;
-		op += 4;
-		n -= 4;
-		t0 = ToLinearF[(cr += wp[0]) & mask] * SCALE12;
-		t1 = ToLinearF[(cg += wp[1]) & mask] * SCALE12;
-		t2 = ToLinearF[(cb += wp[2]) & mask] * SCALE12;
-		t3 = ToLinearF[(ca += wp[3]) & mask] * SCALE12;
-		op[0] = CLAMP12(t0);
-		op[1] = CLAMP12(t1);
-		op[2] = CLAMP12(t2);
-		op[3] = CLAMP12(t3);
-	    }
-	} else {
-	    REPEAT(stride, t0 = ToLinearF[*wp&mask] * SCALE12;
-                           *op = CLAMP12(t0); wp++; op++)
-	    n -= stride;
-	    while (n > 0) {
-		REPEAT(stride,
-		    wp[stride] += *wp; t0 = ToLinearF[wp[stride]&mask]*SCALE12;
-		    *op = CLAMP12(t0);  wp++; op++)
-		n -= stride;
-	    }
-	}
+    if (n >= stride)
+    {
+        mask = CODE_MASK;
+        if (stride == 3)
+        {
+            t0 = ToLinearF[cr = (wp[0] & mask)] * SCALE12;
+            t1 = ToLinearF[cg = (wp[1] & mask)] * SCALE12;
+            t2 = ToLinearF[cb = (wp[2] & mask)] * SCALE12;
+            op[0] = CLAMP12(t0);
+            op[1] = CLAMP12(t1);
+            op[2] = CLAMP12(t2);
+            n -= 3;
+            while (n > 0)
+            {
+                wp += 3;
+                op += 3;
+                n -= 3;
+                t0 = ToLinearF[(cr += wp[0]) & mask] * SCALE12;
+                t1 = ToLinearF[(cg += wp[1]) & mask] * SCALE12;
+                t2 = ToLinearF[(cb += wp[2]) & mask] * SCALE12;
+                op[0] = CLAMP12(t0);
+                op[1] = CLAMP12(t1);
+                op[2] = CLAMP12(t2);
+            }
+        }
+        else if (stride == 4)
+        {
+            t0 = ToLinearF[cr = (wp[0] & mask)] * SCALE12;
+            t1 = ToLinearF[cg = (wp[1] & mask)] * SCALE12;
+            t2 = ToLinearF[cb = (wp[2] & mask)] * SCALE12;
+            t3 = ToLinearF[ca = (wp[3] & mask)] * SCALE12;
+            op[0] = CLAMP12(t0);
+            op[1] = CLAMP12(t1);
+            op[2] = CLAMP12(t2);
+            op[3] = CLAMP12(t3);
+            n -= 4;
+            while (n > 0)
+            {
+                wp += 4;
+                op += 4;
+                n -= 4;
+                t0 = ToLinearF[(cr += wp[0]) & mask] * SCALE12;
+                t1 = ToLinearF[(cg += wp[1]) & mask] * SCALE12;
+                t2 = ToLinearF[(cb += wp[2]) & mask] * SCALE12;
+                t3 = ToLinearF[(ca += wp[3]) & mask] * SCALE12;
+                op[0] = CLAMP12(t0);
+                op[1] = CLAMP12(t1);
+                op[2] = CLAMP12(t2);
+                op[3] = CLAMP12(t3);
+            }
+        }
+        else
+        {
+            REPEAT(stride, t0 = ToLinearF[*wp & mask] * SCALE12;
+                   *op = CLAMP12(t0); wp++; op++)
+            n -= stride;
+            while (n > 0)
+            {
+                REPEAT(stride, wp[stride] += *wp;
+                       t0 = ToLinearF[wp[stride] & mask] * SCALE12;
+                       *op = CLAMP12(t0); wp++; op++)
+                n -= stride;
+            }
+        }
     }
 }
 
-static void
-horizontalAccumulate16(uint16 *wp, int n, int stride, uint16 *op,
-	uint16 *ToLinear16)
-{
-    register unsigned int  cr, cg, cb, ca, mask;
-
-    if (n >= stride) {
-	mask = CODE_MASK;
-	if (stride == 3) {
-	    op[0] = ToLinear16[cr = (wp[0] & mask)];
-	    op[1] = ToLinear16[cg = (wp[1] & mask)];
-	    op[2] = ToLinear16[cb = (wp[2] & mask)];
-	    n -= 3;
-	    while (n > 0) {
-		wp += 3;
-		op += 3;
-		n -= 3;
-		op[0] = ToLinear16[(cr += wp[0]) & mask];
-		op[1] = ToLinear16[(cg += wp[1]) & mask];
-		op[2] = ToLinear16[(cb += wp[2]) & mask];
-	    }
-	} else if (stride == 4) {
-	    op[0] = ToLinear16[cr = (wp[0] & mask)];
-	    op[1] = ToLinear16[cg = (wp[1] & mask)];
-	    op[2] = ToLinear16[cb = (wp[2] & mask)];
-	    op[3] = ToLinear16[ca = (wp[3] & mask)];
-	    n -= 4;
-	    while (n > 0) {
-		wp += 4;
-		op += 4;
-		n -= 4;
-		op[0] = ToLinear16[(cr += wp[0]) & mask];
-		op[1] = ToLinear16[(cg += wp[1]) & mask];
-		op[2] = ToLinear16[(cb += wp[2]) & mask];
-		op[3] = ToLinear16[(ca += wp[3]) & mask];
-	    }
-	} else {
-	    REPEAT(stride, *op = ToLinear16[*wp&mask]; wp++; op++)
-	    n -= stride;
-	    while (n > 0) {
-		REPEAT(stride,
-		    wp[stride] += *wp; *op = ToLinear16[*wp&mask]; wp++; op++)
-		n -= stride;
-	    }
-	}
-    }
-}
-
-/* 
- * Returns the log encoded 11-bit values with the horizontal
- * differencing undone.
- */
-static void
-horizontalAccumulate11(uint16 *wp, int n, int stride, uint16 *op)
+static void horizontalAccumulate16(uint16_t *wp, int n, int stride,
+                                   uint16_t *op, uint16_t *ToLinear16)
 {
     register unsigned int cr, cg, cb, ca, mask;
 
-    if (n >= stride) {
-	mask = CODE_MASK;
-	if (stride == 3) {
-	    op[0] = wp[0];  op[1] = wp[1];  op[2] = wp[2];
-            cr = wp[0];  cg = wp[1];  cb = wp[2];
-	    n -= 3;
-	    while (n > 0) {
-		wp += 3;
-		op += 3;
-		n -= 3;
-		op[0] = (uint16)((cr += wp[0]) & mask);
-		op[1] = (uint16)((cg += wp[1]) & mask);
-		op[2] = (uint16)((cb += wp[2]) & mask);
-	    }
-	} else if (stride == 4) {
-	    op[0] = wp[0];  op[1] = wp[1];
-	    op[2] = wp[2];  op[3] = wp[3];
-            cr = wp[0]; cg = wp[1]; cb = wp[2]; ca = wp[3];
-	    n -= 4;
-	    while (n > 0) {
-		wp += 4;
-		op += 4;
-		n -= 4;
-		op[0] = (uint16)((cr += wp[0]) & mask);
-		op[1] = (uint16)((cg += wp[1]) & mask);
-		op[2] = (uint16)((cb += wp[2]) & mask);
-		op[3] = (uint16)((ca += wp[3]) & mask);
-	    } 
-	} else {
-	    REPEAT(stride, *op = *wp&mask; wp++; op++)
-	    n -= stride;
-	    while (n > 0) {
-		REPEAT(stride,
-		    wp[stride] += *wp; *op = *wp&mask; wp++; op++)
-		n -= stride;
-	    }
-	}
+    if (n >= stride)
+    {
+        mask = CODE_MASK;
+        if (stride == 3)
+        {
+            op[0] = ToLinear16[cr = (wp[0] & mask)];
+            op[1] = ToLinear16[cg = (wp[1] & mask)];
+            op[2] = ToLinear16[cb = (wp[2] & mask)];
+            n -= 3;
+            while (n > 0)
+            {
+                wp += 3;
+                op += 3;
+                n -= 3;
+                op[0] = ToLinear16[(cr += wp[0]) & mask];
+                op[1] = ToLinear16[(cg += wp[1]) & mask];
+                op[2] = ToLinear16[(cb += wp[2]) & mask];
+            }
+        }
+        else if (stride == 4)
+        {
+            op[0] = ToLinear16[cr = (wp[0] & mask)];
+            op[1] = ToLinear16[cg = (wp[1] & mask)];
+            op[2] = ToLinear16[cb = (wp[2] & mask)];
+            op[3] = ToLinear16[ca = (wp[3] & mask)];
+            n -= 4;
+            while (n > 0)
+            {
+                wp += 4;
+                op += 4;
+                n -= 4;
+                op[0] = ToLinear16[(cr += wp[0]) & mask];
+                op[1] = ToLinear16[(cg += wp[1]) & mask];
+                op[2] = ToLinear16[(cb += wp[2]) & mask];
+                op[3] = ToLinear16[(ca += wp[3]) & mask];
+            }
+        }
+        else
+        {
+            REPEAT(stride, *op = ToLinear16[*wp & mask]; wp++; op++)
+            n -= stride;
+            while (n > 0)
+            {
+                REPEAT(stride, wp[stride] += *wp; *op = ToLinear16[*wp & mask];
+                       wp++; op++)
+                n -= stride;
+            }
+        }
     }
 }
 
-static void
-horizontalAccumulate8(uint16 *wp, int n, int stride, unsigned char *op,
-	unsigned char *ToLinear8)
+/*
+ * Returns the log encoded 11-bit values with the horizontal
+ * differencing undone.
+ */
+static void horizontalAccumulate11(uint16_t *wp, int n, int stride,
+                                   uint16_t *op)
 {
-    register unsigned int  cr, cg, cb, ca, mask;
+    register unsigned int cr, cg, cb, ca, mask;
 
-    if (n >= stride) {
-	mask = CODE_MASK;
-	if (stride == 3) {
-	    op[0] = ToLinear8[cr = (wp[0] & mask)];
-	    op[1] = ToLinear8[cg = (wp[1] & mask)];
-	    op[2] = ToLinear8[cb = (wp[2] & mask)];
-	    n -= 3;
-	    while (n > 0) {
-		n -= 3;
-		wp += 3;
-		op += 3;
-		op[0] = ToLinear8[(cr += wp[0]) & mask];
-		op[1] = ToLinear8[(cg += wp[1]) & mask];
-		op[2] = ToLinear8[(cb += wp[2]) & mask];
-	    }
-	} else if (stride == 4) {
-	    op[0] = ToLinear8[cr = (wp[0] & mask)];
-	    op[1] = ToLinear8[cg = (wp[1] & mask)];
-	    op[2] = ToLinear8[cb = (wp[2] & mask)];
-	    op[3] = ToLinear8[ca = (wp[3] & mask)];
-	    n -= 4;
-	    while (n > 0) {
-		n -= 4;
-		wp += 4;
-		op += 4;
-		op[0] = ToLinear8[(cr += wp[0]) & mask];
-		op[1] = ToLinear8[(cg += wp[1]) & mask];
-		op[2] = ToLinear8[(cb += wp[2]) & mask];
-		op[3] = ToLinear8[(ca += wp[3]) & mask];
-	    }
-	} else {
-	    REPEAT(stride, *op = ToLinear8[*wp&mask]; wp++; op++)
-	    n -= stride;
-	    while (n > 0) {
-		REPEAT(stride,
-		    wp[stride] += *wp; *op = ToLinear8[*wp&mask]; wp++; op++)
-		n -= stride;
-	    }
-	}
+    if (n >= stride)
+    {
+        mask = CODE_MASK;
+        if (stride == 3)
+        {
+            op[0] = wp[0];
+            op[1] = wp[1];
+            op[2] = wp[2];
+            cr = wp[0];
+            cg = wp[1];
+            cb = wp[2];
+            n -= 3;
+            while (n > 0)
+            {
+                wp += 3;
+                op += 3;
+                n -= 3;
+                op[0] = (uint16_t)((cr += wp[0]) & mask);
+                op[1] = (uint16_t)((cg += wp[1]) & mask);
+                op[2] = (uint16_t)((cb += wp[2]) & mask);
+            }
+        }
+        else if (stride == 4)
+        {
+            op[0] = wp[0];
+            op[1] = wp[1];
+            op[2] = wp[2];
+            op[3] = wp[3];
+            cr = wp[0];
+            cg = wp[1];
+            cb = wp[2];
+            ca = wp[3];
+            n -= 4;
+            while (n > 0)
+            {
+                wp += 4;
+                op += 4;
+                n -= 4;
+                op[0] = (uint16_t)((cr += wp[0]) & mask);
+                op[1] = (uint16_t)((cg += wp[1]) & mask);
+                op[2] = (uint16_t)((cb += wp[2]) & mask);
+                op[3] = (uint16_t)((ca += wp[3]) & mask);
+            }
+        }
+        else
+        {
+            REPEAT(stride, *op = *wp & mask; wp++; op++)
+            n -= stride;
+            while (n > 0)
+            {
+                REPEAT(stride, wp[stride] += *wp; *op = *wp & mask; wp++; op++)
+                n -= stride;
+            }
+        }
     }
 }
 
-
-static void
-horizontalAccumulate8abgr(uint16 *wp, int n, int stride, unsigned char *op,
-	unsigned char *ToLinear8)
+static void horizontalAccumulate8(uint16_t *wp, int n, int stride,
+                                  unsigned char *op, unsigned char *ToLinear8)
 {
-    register unsigned int  cr, cg, cb, ca, mask;
-    register unsigned char  t0, t1, t2, t3;
+    register unsigned int cr, cg, cb, ca, mask;
 
-    if (n >= stride) {
-	mask = CODE_MASK;
-	if (stride == 3) {
-	    op[0] = 0;
-	    t1 = ToLinear8[cb = (wp[2] & mask)];
-	    t2 = ToLinear8[cg = (wp[1] & mask)];
-	    t3 = ToLinear8[cr = (wp[0] & mask)];
-	    op[1] = t1;
-	    op[2] = t2;
-	    op[3] = t3;
-	    n -= 3;
-	    while (n > 0) {
-		n -= 3;
-		wp += 3;
-		op += 4;
-		op[0] = 0;
-		t1 = ToLinear8[(cb += wp[2]) & mask];
-		t2 = ToLinear8[(cg += wp[1]) & mask];
-		t3 = ToLinear8[(cr += wp[0]) & mask];
-		op[1] = t1;
-		op[2] = t2;
-		op[3] = t3;
-	    }
-	} else if (stride == 4) {
-	    t0 = ToLinear8[ca = (wp[3] & mask)];
-	    t1 = ToLinear8[cb = (wp[2] & mask)];
-	    t2 = ToLinear8[cg = (wp[1] & mask)];
-	    t3 = ToLinear8[cr = (wp[0] & mask)];
-	    op[0] = t0;
-	    op[1] = t1;
-	    op[2] = t2;
-	    op[3] = t3;
-	    n -= 4;
-	    while (n > 0) {
-		n -= 4;
-		wp += 4;
-		op += 4;
-		t0 = ToLinear8[(ca += wp[3]) & mask];
-		t1 = ToLinear8[(cb += wp[2]) & mask];
-		t2 = ToLinear8[(cg += wp[1]) & mask];
-		t3 = ToLinear8[(cr += wp[0]) & mask];
-		op[0] = t0;
-		op[1] = t1;
-		op[2] = t2;
-		op[3] = t3;
-	    }
-	} else {
-	    REPEAT(stride, *op = ToLinear8[*wp&mask]; wp++; op++)
-	    n -= stride;
-	    while (n > 0) {
-		REPEAT(stride,
-		    wp[stride] += *wp; *op = ToLinear8[*wp&mask]; wp++; op++)
-		n -= stride;
-	    }
-	}
+    if (n >= stride)
+    {
+        mask = CODE_MASK;
+        if (stride == 3)
+        {
+            op[0] = ToLinear8[cr = (wp[0] & mask)];
+            op[1] = ToLinear8[cg = (wp[1] & mask)];
+            op[2] = ToLinear8[cb = (wp[2] & mask)];
+            n -= 3;
+            while (n > 0)
+            {
+                n -= 3;
+                wp += 3;
+                op += 3;
+                op[0] = ToLinear8[(cr += wp[0]) & mask];
+                op[1] = ToLinear8[(cg += wp[1]) & mask];
+                op[2] = ToLinear8[(cb += wp[2]) & mask];
+            }
+        }
+        else if (stride == 4)
+        {
+            op[0] = ToLinear8[cr = (wp[0] & mask)];
+            op[1] = ToLinear8[cg = (wp[1] & mask)];
+            op[2] = ToLinear8[cb = (wp[2] & mask)];
+            op[3] = ToLinear8[ca = (wp[3] & mask)];
+            n -= 4;
+            while (n > 0)
+            {
+                n -= 4;
+                wp += 4;
+                op += 4;
+                op[0] = ToLinear8[(cr += wp[0]) & mask];
+                op[1] = ToLinear8[(cg += wp[1]) & mask];
+                op[2] = ToLinear8[(cb += wp[2]) & mask];
+                op[3] = ToLinear8[(ca += wp[3]) & mask];
+            }
+        }
+        else
+        {
+            REPEAT(stride, *op = ToLinear8[*wp & mask]; wp++; op++)
+            n -= stride;
+            while (n > 0)
+            {
+                REPEAT(stride, wp[stride] += *wp; *op = ToLinear8[*wp & mask];
+                       wp++; op++)
+                n -= stride;
+            }
+        }
+    }
+}
+
+static void horizontalAccumulate8abgr(uint16_t *wp, int n, int stride,
+                                      unsigned char *op,
+                                      unsigned char *ToLinear8)
+{
+    register unsigned int cr, cg, cb, ca, mask;
+    register unsigned char t0, t1, t2, t3;
+
+    if (n >= stride)
+    {
+        mask = CODE_MASK;
+        if (stride == 3)
+        {
+            op[0] = 0;
+            t1 = ToLinear8[cb = (wp[2] & mask)];
+            t2 = ToLinear8[cg = (wp[1] & mask)];
+            t3 = ToLinear8[cr = (wp[0] & mask)];
+            op[1] = t1;
+            op[2] = t2;
+            op[3] = t3;
+            n -= 3;
+            while (n > 0)
+            {
+                n -= 3;
+                wp += 3;
+                op += 4;
+                op[0] = 0;
+                t1 = ToLinear8[(cb += wp[2]) & mask];
+                t2 = ToLinear8[(cg += wp[1]) & mask];
+                t3 = ToLinear8[(cr += wp[0]) & mask];
+                op[1] = t1;
+                op[2] = t2;
+                op[3] = t3;
+            }
+        }
+        else if (stride == 4)
+        {
+            t0 = ToLinear8[ca = (wp[3] & mask)];
+            t1 = ToLinear8[cb = (wp[2] & mask)];
+            t2 = ToLinear8[cg = (wp[1] & mask)];
+            t3 = ToLinear8[cr = (wp[0] & mask)];
+            op[0] = t0;
+            op[1] = t1;
+            op[2] = t2;
+            op[3] = t3;
+            n -= 4;
+            while (n > 0)
+            {
+                n -= 4;
+                wp += 4;
+                op += 4;
+                t0 = ToLinear8[(ca += wp[3]) & mask];
+                t1 = ToLinear8[(cb += wp[2]) & mask];
+                t2 = ToLinear8[(cg += wp[1]) & mask];
+                t3 = ToLinear8[(cr += wp[0]) & mask];
+                op[0] = t0;
+                op[1] = t1;
+                op[2] = t2;
+                op[3] = t3;
+            }
+        }
+        else
+        {
+            REPEAT(stride, *op = ToLinear8[*wp & mask]; wp++; op++)
+            n -= stride;
+            while (n > 0)
+            {
+                REPEAT(stride, wp[stride] += *wp; *op = ToLinear8[*wp & mask];
+                       wp++; op++)
+                n -= stride;
+            }
+        }
     }
 }
 
@@ -454,110 +520,121 @@
  * State block for each open TIFF
  * file using PixarLog compression/decompression.
  */
-typedef	struct {
-	TIFFPredictorState	predict;
-	z_stream		stream;
-	tmsize_t		tbuf_size; /* only set/used on reading for now */
-	uint16			*tbuf; 
-	uint16			stride;
-	int			state;
-	int			user_datafmt;
-	int			quality;
+typedef struct
+{
+    TIFFPredictorState predict;
+    z_stream stream;
+    tmsize_t tbuf_size; /* only set/used on reading for now */
+    uint16_t *tbuf;
+    uint16_t stride;
+    int state;
+    int user_datafmt;
+    int quality;
 #define PLSTATE_INIT 1
 
-	TIFFVSetMethod		vgetparent;	/* super-class method */
-	TIFFVSetMethod		vsetparent;	/* super-class method */
+    TIFFVSetMethod vgetparent; /* super-class method */
+    TIFFVSetMethod vsetparent; /* super-class method */
 
-	float *ToLinearF;
-	uint16 *ToLinear16;
-	unsigned char *ToLinear8;
-	uint16  *FromLT2;
-	uint16  *From14; /* Really for 16-bit data, but we shift down 2 */
-	uint16  *From8;
-	
+    float *ToLinearF;
+    uint16_t *ToLinear16;
+    unsigned char *ToLinear8;
+    uint16_t *FromLT2;
+    uint16_t *From14; /* Really for 16-bit data, but we shift down 2 */
+    uint16_t *From8;
+
 } PixarLogState;
 
-static int
-PixarLogMakeTables(PixarLogState *sp)
+static int PixarLogMakeTables(TIFF *tif, PixarLogState *sp)
 {
 
-/*
- *    We make several tables here to convert between various external
- *    representations (float, 16-bit, and 8-bit) and the internal
- *    11-bit companded representation.  The 11-bit representation has two
- *    distinct regions.  A linear bottom end up through .018316 in steps
- *    of about .000073, and a region of constant ratio up to about 25.
- *    These floating point numbers are stored in the main table ToLinearF. 
- *    All other tables are derived from this one.  The tables (and the
- *    ratios) are continuous at the internal seam.
- */
+    /*
+     *    We make several tables here to convert between various external
+     *    representations (float, 16-bit, and 8-bit) and the internal
+     *    11-bit companded representation.  The 11-bit representation has two
+     *    distinct regions.  A linear bottom end up through .018316 in steps
+     *    of about .000073, and a region of constant ratio up to about 25.
+     *    These floating point numbers are stored in the main table ToLinearF.
+     *    All other tables are derived from this one.  The tables (and the
+     *    ratios) are continuous at the internal seam.
+     */
 
-    int  nlin, lt2size;
-    int  i, j;
-    double  b, c, linstep, v;
+    int nlin, lt2size;
+    int i, j;
+    double b, c, linstep, v;
     float *ToLinearF;
-    uint16 *ToLinear16;
+    uint16_t *ToLinear16;
     unsigned char *ToLinear8;
-    uint16  *FromLT2;
-    uint16  *From14; /* Really for 16-bit data, but we shift down 2 */
-    uint16  *From8;
+    uint16_t *FromLT2;
+    uint16_t *From14; /* Really for 16-bit data, but we shift down 2 */
+    uint16_t *From8;
 
-    c = log(RATIO);	
-    nlin = (int)(1./c);	/* nlin must be an integer */
-    c = 1./nlin;
-    b = exp(-c*ONE);	/* multiplicative scale factor [b*exp(c*ONE) = 1] */
-    linstep = b*c*exp(1.);
+    c = log(RATIO);
+    nlin = (int)(1. / c); /* nlin must be an integer */
+    c = 1. / nlin;
+    b = exp(-c * ONE); /* multiplicative scale factor [b*exp(c*ONE) = 1] */
+    linstep = b * c * exp(1.);
 
-    LogK1 = (float)(1./c);	/* if (v >= 2)  token = k1*log(v*k2) */
-    LogK2 = (float)(1./b);
-    lt2size = (int)(2./linstep) + 1;
-    FromLT2 = (uint16 *)_TIFFmalloc(lt2size*sizeof(uint16));
-    From14 = (uint16 *)_TIFFmalloc(16384*sizeof(uint16));
-    From8 = (uint16 *)_TIFFmalloc(256*sizeof(uint16));
-    ToLinearF = (float *)_TIFFmalloc(TSIZEP1 * sizeof(float));
-    ToLinear16 = (uint16 *)_TIFFmalloc(TSIZEP1 * sizeof(uint16));
-    ToLinear8 = (unsigned char *)_TIFFmalloc(TSIZEP1 * sizeof(unsigned char));
-    if (FromLT2 == NULL || From14  == NULL || From8   == NULL ||
-	 ToLinearF == NULL || ToLinear16 == NULL || ToLinear8 == NULL) {
-	if (FromLT2) _TIFFfree(FromLT2);
-	if (From14) _TIFFfree(From14);
-	if (From8) _TIFFfree(From8);
-	if (ToLinearF) _TIFFfree(ToLinearF);
-	if (ToLinear16) _TIFFfree(ToLinear16);
-	if (ToLinear8) _TIFFfree(ToLinear8);
-	sp->FromLT2 = NULL;
-	sp->From14 = NULL;
-	sp->From8 = NULL;
-	sp->ToLinearF = NULL;
-	sp->ToLinear16 = NULL;
-	sp->ToLinear8 = NULL;
-	return 0;
+    LogK1 = (float)(1. / c); /* if (v >= 2)  token = k1*log(v*k2) */
+    LogK2 = (float)(1. / b);
+    lt2size = (int)(2. / linstep) + 1;
+    FromLT2 = (uint16_t *)_TIFFmallocExt(tif, lt2size * sizeof(uint16_t));
+    From14 = (uint16_t *)_TIFFmallocExt(tif, 16384 * sizeof(uint16_t));
+    From8 = (uint16_t *)_TIFFmallocExt(tif, 256 * sizeof(uint16_t));
+    ToLinearF = (float *)_TIFFmallocExt(tif, TSIZEP1 * sizeof(float));
+    ToLinear16 = (uint16_t *)_TIFFmallocExt(tif, TSIZEP1 * sizeof(uint16_t));
+    ToLinear8 =
+        (unsigned char *)_TIFFmallocExt(tif, TSIZEP1 * sizeof(unsigned char));
+    if (FromLT2 == NULL || From14 == NULL || From8 == NULL ||
+        ToLinearF == NULL || ToLinear16 == NULL || ToLinear8 == NULL)
+    {
+        if (FromLT2)
+            _TIFFfreeExt(tif, FromLT2);
+        if (From14)
+            _TIFFfreeExt(tif, From14);
+        if (From8)
+            _TIFFfreeExt(tif, From8);
+        if (ToLinearF)
+            _TIFFfreeExt(tif, ToLinearF);
+        if (ToLinear16)
+            _TIFFfreeExt(tif, ToLinear16);
+        if (ToLinear8)
+            _TIFFfreeExt(tif, ToLinear8);
+        sp->FromLT2 = NULL;
+        sp->From14 = NULL;
+        sp->From8 = NULL;
+        sp->ToLinearF = NULL;
+        sp->ToLinear16 = NULL;
+        sp->ToLinear8 = NULL;
+        return 0;
     }
 
     j = 0;
 
-    for (i = 0; i < nlin; i++)  {
-	v = i * linstep;
-	ToLinearF[j++] = (float)v;
+    for (i = 0; i < nlin; i++)
+    {
+        v = i * linstep;
+        ToLinearF[j++] = (float)v;
     }
 
     for (i = nlin; i < TSIZE; i++)
-	ToLinearF[j++] = (float)(b*exp(c*i));
+        ToLinearF[j++] = (float)(b * exp(c * i));
 
     ToLinearF[2048] = ToLinearF[2047];
 
-    for (i = 0; i < TSIZEP1; i++)  {
-	v = ToLinearF[i]*65535.0 + 0.5;
-	ToLinear16[i] = (v > 65535.0) ? 65535 : (uint16)v;
-	v = ToLinearF[i]*255.0  + 0.5;
-	ToLinear8[i]  = (v > 255.0) ? 255 : (unsigned char)v;
+    for (i = 0; i < TSIZEP1; i++)
+    {
+        v = ToLinearF[i] * 65535.0 + 0.5;
+        ToLinear16[i] = (v > 65535.0) ? 65535 : (uint16_t)v;
+        v = ToLinearF[i] * 255.0 + 0.5;
+        ToLinear8[i] = (v > 255.0) ? 255 : (unsigned char)v;
     }
 
     j = 0;
-    for (i = 0; i < lt2size; i++)  {
-	if ((i*linstep)*(i*linstep) > ToLinearF[j]*ToLinearF[j+1])
-	    j++;
-	FromLT2[i] = (uint16)j;
+    for (i = 0; i < lt2size; i++)
+    {
+        if ((i * linstep) * (i * linstep) > ToLinearF[j] * ToLinearF[j + 1])
+            j++;
+        FromLT2[i] = (uint16_t)j;
     }
 
     /*
@@ -566,20 +643,22 @@
      * saves a little table space.
      */
     j = 0;
-    for (i = 0; i < 16384; i++)  {
-	while ((i/16383.)*(i/16383.) > ToLinearF[j]*ToLinearF[j+1])
-	    j++;
-	From14[i] = (uint16)j;
+    for (i = 0; i < 16384; i++)
+    {
+        while ((i / 16383.) * (i / 16383.) > ToLinearF[j] * ToLinearF[j + 1])
+            j++;
+        From14[i] = (uint16_t)j;
     }
 
     j = 0;
-    for (i = 0; i < 256; i++)  {
-	while ((i/255.)*(i/255.) > ToLinearF[j]*ToLinearF[j+1])
-	    j++;
-	From8[i] = (uint16)j;
+    for (i = 0; i < 256; i++)
+    {
+        while ((i / 255.) * (i / 255.) > ToLinearF[j] * ToLinearF[j + 1])
+            j++;
+        From8[i] = (uint16_t)j;
     }
 
-    Fltsize = (float)(lt2size/2);
+    Fltsize = (float)(lt2size / 2);
 
     sp->ToLinearF = ToLinearF;
     sp->ToLinear16 = ToLinear16;
@@ -591,621 +670,727 @@
     return 1;
 }
 
-#define DecoderState(tif)	((PixarLogState*) (tif)->tif_data)
-#define EncoderState(tif)	((PixarLogState*) (tif)->tif_data)
+#define DecoderState(tif) ((PixarLogState *)(tif)->tif_data)
+#define EncoderState(tif) ((PixarLogState *)(tif)->tif_data)
 
-static int PixarLogEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s);
-static int PixarLogDecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s);
+static int PixarLogEncode(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s);
+static int PixarLogDecode(TIFF *tif, uint8_t *op, tmsize_t occ, uint16_t s);
 
-#define PIXARLOGDATAFMT_UNKNOWN	-1
+#define PIXARLOGDATAFMT_UNKNOWN -1
 
-static int
-PixarLogGuessDataFmt(TIFFDirectory *td)
+static int PixarLogGuessDataFmt(TIFFDirectory *td)
 {
-	int guess = PIXARLOGDATAFMT_UNKNOWN;
-	int format = td->td_sampleformat;
+    int guess = PIXARLOGDATAFMT_UNKNOWN;
+    int format = td->td_sampleformat;
 
-	/* If the user didn't tell us his datafmt,
-	 * take our best guess from the bitspersample.
-	 */
-	switch (td->td_bitspersample) {
-	 case 32:
-		if (format == SAMPLEFORMAT_IEEEFP)
-			guess = PIXARLOGDATAFMT_FLOAT;
-		break;
-	 case 16:
-		if (format == SAMPLEFORMAT_VOID || format == SAMPLEFORMAT_UINT)
-			guess = PIXARLOGDATAFMT_16BIT;
-		break;
-	 case 12:
-		if (format == SAMPLEFORMAT_VOID || format == SAMPLEFORMAT_INT)
-			guess = PIXARLOGDATAFMT_12BITPICIO;
-		break;
-	 case 11:
-		if (format == SAMPLEFORMAT_VOID || format == SAMPLEFORMAT_UINT)
-			guess = PIXARLOGDATAFMT_11BITLOG;
-		break;
-	 case 8:
-		if (format == SAMPLEFORMAT_VOID || format == SAMPLEFORMAT_UINT)
-			guess = PIXARLOGDATAFMT_8BIT;
-		break;
-	}
+    /* If the user didn't tell us his datafmt,
+     * take our best guess from the bitspersample.
+     */
+    switch (td->td_bitspersample)
+    {
+        case 32:
+            if (format == SAMPLEFORMAT_IEEEFP)
+                guess = PIXARLOGDATAFMT_FLOAT;
+            break;
+        case 16:
+            if (format == SAMPLEFORMAT_VOID || format == SAMPLEFORMAT_UINT)
+                guess = PIXARLOGDATAFMT_16BIT;
+            break;
+        case 12:
+            if (format == SAMPLEFORMAT_VOID || format == SAMPLEFORMAT_INT)
+                guess = PIXARLOGDATAFMT_12BITPICIO;
+            break;
+        case 11:
+            if (format == SAMPLEFORMAT_VOID || format == SAMPLEFORMAT_UINT)
+                guess = PIXARLOGDATAFMT_11BITLOG;
+            break;
+        case 8:
+            if (format == SAMPLEFORMAT_VOID || format == SAMPLEFORMAT_UINT)
+                guess = PIXARLOGDATAFMT_8BIT;
+            break;
+    }
 
-	return guess;
+    return guess;
 }
 
-static tmsize_t
-multiply_ms(tmsize_t m1, tmsize_t m2)
+static tmsize_t multiply_ms(tmsize_t m1, tmsize_t m2)
 {
-        return _TIFFMultiplySSize(NULL, m1, m2, NULL);
+    return _TIFFMultiplySSize(NULL, m1, m2, NULL);
 }
 
-static tmsize_t
-add_ms(tmsize_t m1, tmsize_t m2)
+static tmsize_t add_ms(tmsize_t m1, tmsize_t m2)
 {
-        assert(m1 >= 0 && m2 >= 0);
-	/* if either input is zero, assume overflow already occurred */
-	if (m1 == 0 || m2 == 0)
-		return 0;
-	else if (m1 > TIFF_TMSIZE_T_MAX - m2)
-		return 0;
+    assert(m1 >= 0 && m2 >= 0);
+    /* if either input is zero, assume overflow already occurred */
+    if (m1 == 0 || m2 == 0)
+        return 0;
+    else if (m1 > TIFF_TMSIZE_T_MAX - m2)
+        return 0;
 
-	return m1 + m2;
+    return m1 + m2;
 }
 
-static int
-PixarLogFixupTags(TIFF* tif)
+static int PixarLogFixupTags(TIFF *tif)
 {
-	(void) tif;
-	return (1);
+    (void)tif;
+    return (1);
 }
 
-static int
-PixarLogSetupDecode(TIFF* tif)
+static int PixarLogSetupDecode(TIFF *tif)
 {
-	static const char module[] = "PixarLogSetupDecode";
-	TIFFDirectory *td = &tif->tif_dir;
-	PixarLogState* sp = DecoderState(tif);
-	tmsize_t tbuf_size;
-        uint32 strip_height;
+    static const char module[] = "PixarLogSetupDecode";
+    TIFFDirectory *td = &tif->tif_dir;
+    PixarLogState *sp = DecoderState(tif);
+    tmsize_t tbuf_size;
+    uint32_t strip_height;
 
-	assert(sp != NULL);
+    assert(sp != NULL);
 
-	/* This function can possibly be called several times by */
-	/* PredictorSetupDecode() if this function succeeds but */
-	/* PredictorSetup() fails */
-	if( (sp->state & PLSTATE_INIT) != 0 )
-		return 1;
+    /* This function can possibly be called several times by */
+    /* PredictorSetupDecode() if this function succeeds but */
+    /* PredictorSetup() fails */
+    if ((sp->state & PLSTATE_INIT) != 0)
+        return 1;
 
-        strip_height = td->td_rowsperstrip;
-        if( strip_height > td->td_imagelength )
-            strip_height = td->td_imagelength;
+    strip_height = td->td_rowsperstrip;
+    if (strip_height > td->td_imagelength)
+        strip_height = td->td_imagelength;
 
-	/* Make sure no byte swapping happens on the data
-	 * after decompression. */
-	tif->tif_postdecode = _TIFFNoPostDecode;  
+    /* Make sure no byte swapping happens on the data
+     * after decompression. */
+    tif->tif_postdecode = _TIFFNoPostDecode;
 
-	/* for some reason, we can't do this in TIFFInitPixarLog */
+    /* for some reason, we can't do this in TIFFInitPixarLog */
 
-	sp->stride = (td->td_planarconfig == PLANARCONFIG_CONTIG ?
-	    td->td_samplesperpixel : 1);
-	tbuf_size = multiply_ms(multiply_ms(multiply_ms(sp->stride, td->td_imagewidth),
-				      strip_height), sizeof(uint16));
-	/* add one more stride in case input ends mid-stride */
-	tbuf_size = add_ms(tbuf_size, sizeof(uint16) * sp->stride);
-	if (tbuf_size == 0)
-		return (0);   /* TODO: this is an error return without error report through TIFFErrorExt */
-	sp->tbuf = (uint16 *) _TIFFmalloc(tbuf_size);
-	if (sp->tbuf == NULL)
-		return (0);
-	sp->tbuf_size = tbuf_size;
-	if (sp->user_datafmt == PIXARLOGDATAFMT_UNKNOWN)
-		sp->user_datafmt = PixarLogGuessDataFmt(td);
-	if (sp->user_datafmt == PIXARLOGDATAFMT_UNKNOWN) {
-                _TIFFfree(sp->tbuf);
-                sp->tbuf = NULL;
-                sp->tbuf_size = 0;
-		TIFFErrorExt(tif->tif_clientdata, module,
-			"PixarLog compression can't handle bits depth/data format combination (depth: %d)", 
-			td->td_bitspersample);
-		return (0);
-	}
+    sp->stride =
+        (td->td_planarconfig == PLANARCONFIG_CONTIG ? td->td_samplesperpixel
+                                                    : 1);
+    tbuf_size = multiply_ms(
+        multiply_ms(multiply_ms(sp->stride, td->td_imagewidth), strip_height),
+        sizeof(uint16_t));
+    /* add one more stride in case input ends mid-stride */
+    tbuf_size = add_ms(tbuf_size, sizeof(uint16_t) * sp->stride);
+    if (tbuf_size == 0)
+        return (0); /* TODO: this is an error return without error report
+                       through TIFFErrorExt */
+    sp->tbuf = (uint16_t *)_TIFFmallocExt(tif, tbuf_size);
+    if (sp->tbuf == NULL)
+        return (0);
+    sp->tbuf_size = tbuf_size;
+    if (sp->user_datafmt == PIXARLOGDATAFMT_UNKNOWN)
+        sp->user_datafmt = PixarLogGuessDataFmt(td);
+    if (sp->user_datafmt == PIXARLOGDATAFMT_UNKNOWN)
+    {
+        _TIFFfreeExt(tif, sp->tbuf);
+        sp->tbuf = NULL;
+        sp->tbuf_size = 0;
+        TIFFErrorExtR(tif, module,
+                      "PixarLog compression can't handle bits depth/data "
+                      "format combination (depth: %" PRIu16 ")",
+                      td->td_bitspersample);
+        return (0);
+    }
 
-	if (inflateInit(&sp->stream) != Z_OK) {
-                _TIFFfree(sp->tbuf);
-                sp->tbuf = NULL;
-                sp->tbuf_size = 0;
-		TIFFErrorExt(tif->tif_clientdata, module, "%s", sp->stream.msg ? sp->stream.msg : "(null)");
-		return (0);
-	} else {
-		sp->state |= PLSTATE_INIT;
-		return (1);
-	}
+    if (inflateInit(&sp->stream) != Z_OK)
+    {
+        _TIFFfreeExt(tif, sp->tbuf);
+        sp->tbuf = NULL;
+        sp->tbuf_size = 0;
+        TIFFErrorExtR(tif, module, "%s",
+                      sp->stream.msg ? sp->stream.msg : "(null)");
+        return (0);
+    }
+    else
+    {
+        sp->state |= PLSTATE_INIT;
+        return (1);
+    }
 }
 
 /*
  * Setup state for decoding a strip.
  */
-static int
-PixarLogPreDecode(TIFF* tif, uint16 s)
+static int PixarLogPreDecode(TIFF *tif, uint16_t s)
 {
-	static const char module[] = "PixarLogPreDecode";
-	PixarLogState* sp = DecoderState(tif);
+    static const char module[] = "PixarLogPreDecode";
+    PixarLogState *sp = DecoderState(tif);
 
-	(void) s;
-	assert(sp != NULL);
-	sp->stream.next_in = tif->tif_rawdata;
-	assert(sizeof(sp->stream.avail_in)==4);  /* if this assert gets raised,
-	    we need to simplify this code to reflect a ZLib that is likely updated
-	    to deal with 8byte memory sizes, though this code will respond
-	    appropriately even before we simplify it */
-	sp->stream.avail_in = (uInt) tif->tif_rawcc;
-	if ((tmsize_t)sp->stream.avail_in != tif->tif_rawcc)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "ZLib cannot deal with buffers this size");
-		return (0);
-	}
-	return (inflateReset(&sp->stream) == Z_OK);
+    (void)s;
+    assert(sp != NULL);
+    sp->stream.next_in = tif->tif_rawdata;
+    assert(sizeof(sp->stream.avail_in) == 4); /* if this assert gets raised,
+         we need to simplify this code to reflect a ZLib that is likely updated
+         to deal with 8byte memory sizes, though this code will respond
+         appropriately even before we simplify it */
+    sp->stream.avail_in = (uInt)tif->tif_rawcc;
+    if ((tmsize_t)sp->stream.avail_in != tif->tif_rawcc)
+    {
+        TIFFErrorExtR(tif, module, "ZLib cannot deal with buffers this size");
+        return (0);
+    }
+    return (inflateReset(&sp->stream) == Z_OK);
 }
 
-static int
-PixarLogDecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s)
+static int PixarLogDecode(TIFF *tif, uint8_t *op, tmsize_t occ, uint16_t s)
 {
-	static const char module[] = "PixarLogDecode";
-	TIFFDirectory *td = &tif->tif_dir;
-	PixarLogState* sp = DecoderState(tif);
-	tmsize_t i;
-	tmsize_t nsamples;
-	int llen;
-	uint16 *up;
+    static const char module[] = "PixarLogDecode";
+    TIFFDirectory *td = &tif->tif_dir;
+    PixarLogState *sp = DecoderState(tif);
+    tmsize_t i;
+    tmsize_t nsamples;
+    int llen;
+    uint16_t *up;
 
-	switch (sp->user_datafmt) {
-	case PIXARLOGDATAFMT_FLOAT:
-		nsamples = occ / sizeof(float);	/* XXX float == 32 bits */
-		break;
-	case PIXARLOGDATAFMT_16BIT:
-	case PIXARLOGDATAFMT_12BITPICIO:
-	case PIXARLOGDATAFMT_11BITLOG:
-		nsamples = occ / sizeof(uint16); /* XXX uint16 == 16 bits */
-		break;
-	case PIXARLOGDATAFMT_8BIT:
-	case PIXARLOGDATAFMT_8BITABGR:
-		nsamples = occ;
-		break;
-	default:
-		TIFFErrorExt(tif->tif_clientdata, module,
-			"%d bit input not supported in PixarLog",
-			td->td_bitspersample);
-		return 0;
-	}
+    switch (sp->user_datafmt)
+    {
+        case PIXARLOGDATAFMT_FLOAT:
+            nsamples = occ / sizeof(float); /* XXX float == 32 bits */
+            break;
+        case PIXARLOGDATAFMT_16BIT:
+        case PIXARLOGDATAFMT_12BITPICIO:
+        case PIXARLOGDATAFMT_11BITLOG:
+            nsamples = occ / sizeof(uint16_t); /* XXX uint16_t == 16 bits */
+            break;
+        case PIXARLOGDATAFMT_8BIT:
+        case PIXARLOGDATAFMT_8BITABGR:
+            nsamples = occ;
+            break;
+        default:
+            TIFFErrorExtR(tif, module,
+                          "%" PRIu16 " bit input not supported in PixarLog",
+                          td->td_bitspersample);
+            return 0;
+    }
 
-	llen = sp->stride * td->td_imagewidth;
+    llen = sp->stride * td->td_imagewidth;
 
-	(void) s;
-	assert(sp != NULL);
+    (void)s;
+    assert(sp != NULL);
 
-        sp->stream.next_in = tif->tif_rawcp;
-	sp->stream.avail_in = (uInt) tif->tif_rawcc;
+    sp->stream.next_in = tif->tif_rawcp;
+    sp->stream.avail_in = (uInt)tif->tif_rawcc;
 
-	sp->stream.next_out = (unsigned char *) sp->tbuf;
-	assert(sizeof(sp->stream.avail_out)==4);  /* if this assert gets raised,
-	    we need to simplify this code to reflect a ZLib that is likely updated
-	    to deal with 8byte memory sizes, though this code will respond
-	    appropriately even before we simplify it */
-	sp->stream.avail_out = (uInt) (nsamples * sizeof(uint16));
-	if (sp->stream.avail_out != nsamples * sizeof(uint16))
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "ZLib cannot deal with buffers this size");
-		return (0);
-	}
-	/* Check that we will not fill more than what was allocated */
-	if ((tmsize_t)sp->stream.avail_out > sp->tbuf_size)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "sp->stream.avail_out > sp->tbuf_size");
-		return (0);
-	}
-	do {
-		int state = inflate(&sp->stream, Z_PARTIAL_FLUSH);
-		if (state == Z_STREAM_END) {
-			break;			/* XXX */
-		}
-		if (state == Z_DATA_ERROR) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Decoding error at scanline %lu, %s",
-			    (unsigned long) tif->tif_row, sp->stream.msg ? sp->stream.msg : "(null)");
-			return (0);
-		}
-		if (state != Z_OK) {
-			TIFFErrorExt(tif->tif_clientdata, module, "ZLib error: %s",
-			    sp->stream.msg ? sp->stream.msg : "(null)");
-			return (0);
-		}
-	} while (sp->stream.avail_out > 0);
+    sp->stream.next_out = (unsigned char *)sp->tbuf;
+    assert(sizeof(sp->stream.avail_out) == 4); /* if this assert gets raised,
+         we need to simplify this code to reflect a ZLib that is likely updated
+         to deal with 8byte memory sizes, though this code will respond
+         appropriately even before we simplify it */
+    sp->stream.avail_out = (uInt)(nsamples * sizeof(uint16_t));
+    if (sp->stream.avail_out != nsamples * sizeof(uint16_t))
+    {
+        TIFFErrorExtR(tif, module, "ZLib cannot deal with buffers this size");
+        return (0);
+    }
+    /* Check that we will not fill more than what was allocated */
+    if (sp->tbuf_size < 0 || sp->stream.avail_out > (uInt) sp->tbuf_size)
+    {
+        TIFFErrorExtR(tif, module, "sp->stream.avail_out > sp->tbuf_size");
+        return (0);
+    }
+    do
+    {
+        int state = inflate(&sp->stream, Z_PARTIAL_FLUSH);
+        if (state == Z_STREAM_END)
+        {
+            break; /* XXX */
+        }
+        if (state == Z_DATA_ERROR)
+        {
+            TIFFErrorExtR(
+                tif, module, "Decoding error at scanline %" PRIu32 ", %s",
+                tif->tif_row, sp->stream.msg ? sp->stream.msg : "(null)");
+            return (0);
+        }
+        if (state != Z_OK)
+        {
+            TIFFErrorExtR(tif, module, "ZLib error: %s",
+                          sp->stream.msg ? sp->stream.msg : "(null)");
+            return (0);
+        }
+    } while (sp->stream.avail_out > 0);
 
-	/* hopefully, we got all the bytes we needed */
-	if (sp->stream.avail_out != 0) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "Not enough data at scanline %lu (short " TIFF_UINT64_FORMAT " bytes)",
-		    (unsigned long) tif->tif_row, (TIFF_UINT64_T) sp->stream.avail_out);
-		return (0);
-	}
+    /* hopefully, we got all the bytes we needed */
+    if (sp->stream.avail_out != 0)
+    {
+        TIFFErrorExtR(tif, module,
+                      "Not enough data at scanline %" PRIu32
+                      " (short %u bytes)",
+                      tif->tif_row, sp->stream.avail_out);
+        return (0);
+    }
 
-        tif->tif_rawcp = sp->stream.next_in;
-        tif->tif_rawcc = sp->stream.avail_in;
+    tif->tif_rawcp = sp->stream.next_in;
+    tif->tif_rawcc = sp->stream.avail_in;
 
-	up = sp->tbuf;
-	/* Swap bytes in the data if from a different endian machine. */
-	if (tif->tif_flags & TIFF_SWAB)
-		TIFFSwabArrayOfShort(up, nsamples);
+    up = sp->tbuf;
+    /* Swap bytes in the data if from a different endian machine. */
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabArrayOfShort(up, nsamples);
 
-	/*
-	 * if llen is not an exact multiple of nsamples, the decode operation
-	 * may overflow the output buffer, so truncate it enough to prevent
-	 * that but still salvage as much data as possible.
-	 */
-	if (nsamples % llen) { 
-		TIFFWarningExt(tif->tif_clientdata, module,
-			"stride %lu is not a multiple of sample count, "
-			"%lu, data truncated.", (unsigned long) llen, (unsigned long) nsamples);
-		nsamples -= nsamples % llen;
-	}
+    /*
+     * if llen is not an exact multiple of nsamples, the decode operation
+     * may overflow the output buffer, so truncate it enough to prevent
+     * that but still salvage as much data as possible.
+     */
+    if (nsamples % llen)
+    {
+        TIFFWarningExtR(tif, module,
+                        "stride %d is not a multiple of sample count, "
+                        "%" TIFF_SSIZE_FORMAT ", data truncated.",
+                        llen, nsamples);
+        nsamples -= nsamples % llen;
+    }
 
-	for (i = 0; i < nsamples; i += llen, up += llen) {
-		switch (sp->user_datafmt)  {
-		case PIXARLOGDATAFMT_FLOAT:
-			horizontalAccumulateF(up, llen, sp->stride,
-					(float *)op, sp->ToLinearF);
-			op += llen * sizeof(float);
-			break;
-		case PIXARLOGDATAFMT_16BIT:
-			horizontalAccumulate16(up, llen, sp->stride,
-					(uint16 *)op, sp->ToLinear16);
-			op += llen * sizeof(uint16);
-			break;
-		case PIXARLOGDATAFMT_12BITPICIO:
-			horizontalAccumulate12(up, llen, sp->stride,
-					(int16 *)op, sp->ToLinearF);
-			op += llen * sizeof(int16);
-			break;
-		case PIXARLOGDATAFMT_11BITLOG:
-			horizontalAccumulate11(up, llen, sp->stride,
-					(uint16 *)op);
-			op += llen * sizeof(uint16);
-			break;
-		case PIXARLOGDATAFMT_8BIT:
-			horizontalAccumulate8(up, llen, sp->stride,
-					(unsigned char *)op, sp->ToLinear8);
-			op += llen * sizeof(unsigned char);
-			break;
-		case PIXARLOGDATAFMT_8BITABGR:
-			horizontalAccumulate8abgr(up, llen, sp->stride,
-					(unsigned char *)op, sp->ToLinear8);
-			op += llen * sizeof(unsigned char);
-			break;
-		default:
-			TIFFErrorExt(tif->tif_clientdata, module,
-				  "Unsupported bits/sample: %d",
-				  td->td_bitspersample);
-			return (0);
-		}
-	}
+    for (i = 0; i < nsamples; i += llen, up += llen)
+    {
+        switch (sp->user_datafmt)
+        {
+            case PIXARLOGDATAFMT_FLOAT:
+                horizontalAccumulateF(up, llen, sp->stride, (float *)op,
+                                      sp->ToLinearF);
+                op += llen * sizeof(float);
+                break;
+            case PIXARLOGDATAFMT_16BIT:
+                horizontalAccumulate16(up, llen, sp->stride, (uint16_t *)op,
+                                       sp->ToLinear16);
+                op += llen * sizeof(uint16_t);
+                break;
+            case PIXARLOGDATAFMT_12BITPICIO:
+                horizontalAccumulate12(up, llen, sp->stride, (int16_t *)op,
+                                       sp->ToLinearF);
+                op += llen * sizeof(int16_t);
+                break;
+            case PIXARLOGDATAFMT_11BITLOG:
+                horizontalAccumulate11(up, llen, sp->stride, (uint16_t *)op);
+                op += llen * sizeof(uint16_t);
+                break;
+            case PIXARLOGDATAFMT_8BIT:
+                horizontalAccumulate8(up, llen, sp->stride, (unsigned char *)op,
+                                      sp->ToLinear8);
+                op += llen * sizeof(unsigned char);
+                break;
+            case PIXARLOGDATAFMT_8BITABGR:
+                horizontalAccumulate8abgr(up, llen, sp->stride,
+                                          (unsigned char *)op, sp->ToLinear8);
+                op += llen * sizeof(unsigned char);
+                break;
+            default:
+                TIFFErrorExtR(tif, module, "Unsupported bits/sample: %" PRIu16,
+                              td->td_bitspersample);
+                return (0);
+        }
+    }
 
-	return (1);
+    return (1);
 }
 
-static int
-PixarLogSetupEncode(TIFF* tif)
+static int PixarLogSetupEncode(TIFF *tif)
 {
-	static const char module[] = "PixarLogSetupEncode";
-	TIFFDirectory *td = &tif->tif_dir;
-	PixarLogState* sp = EncoderState(tif);
-	tmsize_t tbuf_size;
+    static const char module[] = "PixarLogSetupEncode";
+    TIFFDirectory *td = &tif->tif_dir;
+    PixarLogState *sp = EncoderState(tif);
+    tmsize_t tbuf_size;
 
-	assert(sp != NULL);
+    assert(sp != NULL);
 
-	/* for some reason, we can't do this in TIFFInitPixarLog */
+    /* for some reason, we can't do this in TIFFInitPixarLog */
 
-	sp->stride = (td->td_planarconfig == PLANARCONFIG_CONTIG ?
-	    td->td_samplesperpixel : 1);
-	tbuf_size = multiply_ms(multiply_ms(multiply_ms(sp->stride, td->td_imagewidth),
-				      td->td_rowsperstrip), sizeof(uint16));
-	if (tbuf_size == 0)
-		return (0);  /* TODO: this is an error return without error report through TIFFErrorExt */
-	sp->tbuf = (uint16 *) _TIFFmalloc(tbuf_size);
-	if (sp->tbuf == NULL)
-		return (0);
-	if (sp->user_datafmt == PIXARLOGDATAFMT_UNKNOWN)
-		sp->user_datafmt = PixarLogGuessDataFmt(td);
-	if (sp->user_datafmt == PIXARLOGDATAFMT_UNKNOWN) {
-		TIFFErrorExt(tif->tif_clientdata, module, "PixarLog compression can't handle %d bit linear encodings", td->td_bitspersample);
-		return (0);
-	}
+    sp->stride =
+        (td->td_planarconfig == PLANARCONFIG_CONTIG ? td->td_samplesperpixel
+                                                    : 1);
+    tbuf_size =
+        multiply_ms(multiply_ms(multiply_ms(sp->stride, td->td_imagewidth),
+                                td->td_rowsperstrip),
+                    sizeof(uint16_t));
+    if (tbuf_size == 0)
+        return (0); /* TODO: this is an error return without error report
+                       through TIFFErrorExt */
+    sp->tbuf = (uint16_t *)_TIFFmallocExt(tif, tbuf_size);
+    if (sp->tbuf == NULL)
+        return (0);
+    if (sp->user_datafmt == PIXARLOGDATAFMT_UNKNOWN)
+        sp->user_datafmt = PixarLogGuessDataFmt(td);
+    if (sp->user_datafmt == PIXARLOGDATAFMT_UNKNOWN)
+    {
+        TIFFErrorExtR(tif, module,
+                      "PixarLog compression can't handle %" PRIu16
+                      " bit linear encodings",
+                      td->td_bitspersample);
+        return (0);
+    }
 
-	if (deflateInit(&sp->stream, sp->quality) != Z_OK) {
-		TIFFErrorExt(tif->tif_clientdata, module, "%s", sp->stream.msg ? sp->stream.msg : "(null)");
-		return (0);
-	} else {
-		sp->state |= PLSTATE_INIT;
-		return (1);
-	}
+    if (deflateInit(&sp->stream, sp->quality) != Z_OK)
+    {
+        TIFFErrorExtR(tif, module, "%s",
+                      sp->stream.msg ? sp->stream.msg : "(null)");
+        return (0);
+    }
+    else
+    {
+        sp->state |= PLSTATE_INIT;
+        return (1);
+    }
 }
 
 /*
  * Reset encoding state at the start of a strip.
  */
-static int
-PixarLogPreEncode(TIFF* tif, uint16 s)
+static int PixarLogPreEncode(TIFF *tif, uint16_t s)
 {
-	static const char module[] = "PixarLogPreEncode";
-	PixarLogState *sp = EncoderState(tif);
+    static const char module[] = "PixarLogPreEncode";
+    PixarLogState *sp = EncoderState(tif);
 
-	(void) s;
-	assert(sp != NULL);
-	sp->stream.next_out = tif->tif_rawdata;
-	assert(sizeof(sp->stream.avail_out)==4);  /* if this assert gets raised,
-	    we need to simplify this code to reflect a ZLib that is likely updated
-	    to deal with 8byte memory sizes, though this code will respond
-	    appropriately even before we simplify it */
-	sp->stream.avail_out = (uInt)tif->tif_rawdatasize;
-	if ((tmsize_t)sp->stream.avail_out != tif->tif_rawdatasize)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "ZLib cannot deal with buffers this size");
-		return (0);
-	}
-	return (deflateReset(&sp->stream) == Z_OK);
+    (void)s;
+    assert(sp != NULL);
+    sp->stream.next_out = tif->tif_rawdata;
+    assert(sizeof(sp->stream.avail_out) == 4); /* if this assert gets raised,
+         we need to simplify this code to reflect a ZLib that is likely updated
+         to deal with 8byte memory sizes, though this code will respond
+         appropriately even before we simplify it */
+    sp->stream.avail_out = (uInt)tif->tif_rawdatasize;
+    if ((tmsize_t)sp->stream.avail_out != tif->tif_rawdatasize)
+    {
+        TIFFErrorExtR(tif, module, "ZLib cannot deal with buffers this size");
+        return (0);
+    }
+    return (deflateReset(&sp->stream) == Z_OK);
 }
 
-static void
-horizontalDifferenceF(float *ip, int n, int stride, uint16 *wp, uint16 *FromLT2)
+static void horizontalDifferenceF(float *ip, int n, int stride, uint16_t *wp,
+                                  uint16_t *FromLT2)
 {
-    int32 r1, g1, b1, a1, r2, g2, b2, a2, mask;
+    int32_t r1, g1, b1, a1, r2, g2, b2, a2, mask;
     float fltsize = Fltsize;
 
-#define  CLAMP(v) ( (v<(float)0.)   ? 0				\
-		  : (v<(float)2.)   ? FromLT2[(int)(v*fltsize)]	\
-		  : (v>(float)24.2) ? 2047			\
-		  : LogK1*log(v*LogK2) + 0.5 )
+#define CLAMP(v)                                                               \
+    ((v < (float)0.)     ? 0                                                   \
+     : (v < (float)2.)   ? FromLT2[(int)(v * fltsize)]                         \
+     : (v > (float)24.2) ? 2047                                                \
+                         : LogK1 * log(v * LogK2) + 0.5)
 
     mask = CODE_MASK;
-    if (n >= stride) {
-	if (stride == 3) {
-	    r2 = wp[0] = (uint16) CLAMP(ip[0]);
-	    g2 = wp[1] = (uint16) CLAMP(ip[1]);
-	    b2 = wp[2] = (uint16) CLAMP(ip[2]);
-	    n -= 3;
-	    while (n > 0) {
-		n -= 3;
-		wp += 3;
-		ip += 3;
-		r1 = (int32) CLAMP(ip[0]); wp[0] = (uint16)((r1-r2) & mask); r2 = r1;
-		g1 = (int32) CLAMP(ip[1]); wp[1] = (uint16)((g1-g2) & mask); g2 = g1;
-		b1 = (int32) CLAMP(ip[2]); wp[2] = (uint16)((b1-b2) & mask); b2 = b1;
-	    }
-	} else if (stride == 4) {
-	    r2 = wp[0] = (uint16) CLAMP(ip[0]);
-	    g2 = wp[1] = (uint16) CLAMP(ip[1]);
-	    b2 = wp[2] = (uint16) CLAMP(ip[2]);
-	    a2 = wp[3] = (uint16) CLAMP(ip[3]);
-	    n -= 4;
-	    while (n > 0) {
-		n -= 4;
-		wp += 4;
-		ip += 4;
-		r1 = (int32) CLAMP(ip[0]); wp[0] = (uint16)((r1-r2) & mask); r2 = r1;
-		g1 = (int32) CLAMP(ip[1]); wp[1] = (uint16)((g1-g2) & mask); g2 = g1;
-		b1 = (int32) CLAMP(ip[2]); wp[2] = (uint16)((b1-b2) & mask); b2 = b1;
-		a1 = (int32) CLAMP(ip[3]); wp[3] = (uint16)((a1-a2) & mask); a2 = a1;
-	    }
-	} else {
-        REPEAT(stride, wp[0] = (uint16) CLAMP(ip[0]); wp++; ip++)
-        n -= stride;
-        while (n > 0) {
-            REPEAT(stride,
-                wp[0] = (uint16)(((int32)CLAMP(ip[0])-(int32)CLAMP(ip[-stride])) & mask);
-                wp++; ip++)
-            n -= stride;
+    if (n >= stride)
+    {
+        if (stride == 3)
+        {
+            r2 = wp[0] = (uint16_t)CLAMP(ip[0]);
+            g2 = wp[1] = (uint16_t)CLAMP(ip[1]);
+            b2 = wp[2] = (uint16_t)CLAMP(ip[2]);
+            n -= 3;
+            while (n > 0)
+            {
+                n -= 3;
+                wp += 3;
+                ip += 3;
+                r1 = (int32_t)CLAMP(ip[0]);
+                wp[0] = (uint16_t)((r1 - r2) & mask);
+                r2 = r1;
+                g1 = (int32_t)CLAMP(ip[1]);
+                wp[1] = (uint16_t)((g1 - g2) & mask);
+                g2 = g1;
+                b1 = (int32_t)CLAMP(ip[2]);
+                wp[2] = (uint16_t)((b1 - b2) & mask);
+                b2 = b1;
+            }
         }
-	}
+        else if (stride == 4)
+        {
+            r2 = wp[0] = (uint16_t)CLAMP(ip[0]);
+            g2 = wp[1] = (uint16_t)CLAMP(ip[1]);
+            b2 = wp[2] = (uint16_t)CLAMP(ip[2]);
+            a2 = wp[3] = (uint16_t)CLAMP(ip[3]);
+            n -= 4;
+            while (n > 0)
+            {
+                n -= 4;
+                wp += 4;
+                ip += 4;
+                r1 = (int32_t)CLAMP(ip[0]);
+                wp[0] = (uint16_t)((r1 - r2) & mask);
+                r2 = r1;
+                g1 = (int32_t)CLAMP(ip[1]);
+                wp[1] = (uint16_t)((g1 - g2) & mask);
+                g2 = g1;
+                b1 = (int32_t)CLAMP(ip[2]);
+                wp[2] = (uint16_t)((b1 - b2) & mask);
+                b2 = b1;
+                a1 = (int32_t)CLAMP(ip[3]);
+                wp[3] = (uint16_t)((a1 - a2) & mask);
+                a2 = a1;
+            }
+        }
+        else
+        {
+            REPEAT(stride, wp[0] = (uint16_t)CLAMP(ip[0]); wp++; ip++)
+            n -= stride;
+            while (n > 0)
+            {
+                REPEAT(stride,
+                       wp[0] = (uint16_t)(((int32_t)CLAMP(ip[0]) -
+                                           (int32_t)CLAMP(ip[-stride])) &
+                                          mask);
+                       wp++; ip++)
+                n -= stride;
+            }
+        }
     }
 }
 
-static void
-horizontalDifference16(unsigned short *ip, int n, int stride, 
-	unsigned short *wp, uint16 *From14)
+static void horizontalDifference16(unsigned short *ip, int n, int stride,
+                                   unsigned short *wp, uint16_t *From14)
 {
-    register int  r1, g1, b1, a1, r2, g2, b2, a2, mask;
+    register int r1, g1, b1, a1, r2, g2, b2, a2, mask;
 
 /* assumption is unsigned pixel values */
-#undef   CLAMP
-#define  CLAMP(v) From14[(v) >> 2]
+#undef CLAMP
+#define CLAMP(v) From14[(v) >> 2]
 
     mask = CODE_MASK;
-    if (n >= stride) {
-	if (stride == 3) {
-	    r2 = wp[0] = CLAMP(ip[0]);  g2 = wp[1] = CLAMP(ip[1]);
-	    b2 = wp[2] = CLAMP(ip[2]);
-	    n -= 3;
-	    while (n > 0) {
-		n -= 3;
-		wp += 3;
-		ip += 3;
-		r1 = CLAMP(ip[0]); wp[0] = (uint16)((r1-r2) & mask); r2 = r1;
-		g1 = CLAMP(ip[1]); wp[1] = (uint16)((g1-g2) & mask); g2 = g1;
-		b1 = CLAMP(ip[2]); wp[2] = (uint16)((b1-b2) & mask); b2 = b1;
-	    }
-	} else if (stride == 4) {
-	    r2 = wp[0] = CLAMP(ip[0]);  g2 = wp[1] = CLAMP(ip[1]);
-	    b2 = wp[2] = CLAMP(ip[2]);  a2 = wp[3] = CLAMP(ip[3]);
-	    n -= 4;
-	    while (n > 0) {
-		n -= 4;
-		wp += 4;
-		ip += 4;
-		r1 = CLAMP(ip[0]); wp[0] = (uint16)((r1-r2) & mask); r2 = r1;
-		g1 = CLAMP(ip[1]); wp[1] = (uint16)((g1-g2) & mask); g2 = g1;
-		b1 = CLAMP(ip[2]); wp[2] = (uint16)((b1-b2) & mask); b2 = b1;
-		a1 = CLAMP(ip[3]); wp[3] = (uint16)((a1-a2) & mask); a2 = a1;
-	    }
-	} else {
-        REPEAT(stride, wp[0] = CLAMP(ip[0]); wp++; ip++)
-	    n -= stride;
-	    while (n > 0) {
-            REPEAT(stride,
-                wp[0] = (uint16)((CLAMP(ip[0])-CLAMP(ip[-stride])) & mask);
-                wp++; ip++)
-            n -= stride;
+    if (n >= stride)
+    {
+        if (stride == 3)
+        {
+            r2 = wp[0] = CLAMP(ip[0]);
+            g2 = wp[1] = CLAMP(ip[1]);
+            b2 = wp[2] = CLAMP(ip[2]);
+            n -= 3;
+            while (n > 0)
+            {
+                n -= 3;
+                wp += 3;
+                ip += 3;
+                r1 = CLAMP(ip[0]);
+                wp[0] = (uint16_t)((r1 - r2) & mask);
+                r2 = r1;
+                g1 = CLAMP(ip[1]);
+                wp[1] = (uint16_t)((g1 - g2) & mask);
+                g2 = g1;
+                b1 = CLAMP(ip[2]);
+                wp[2] = (uint16_t)((b1 - b2) & mask);
+                b2 = b1;
+            }
         }
-	}
+        else if (stride == 4)
+        {
+            r2 = wp[0] = CLAMP(ip[0]);
+            g2 = wp[1] = CLAMP(ip[1]);
+            b2 = wp[2] = CLAMP(ip[2]);
+            a2 = wp[3] = CLAMP(ip[3]);
+            n -= 4;
+            while (n > 0)
+            {
+                n -= 4;
+                wp += 4;
+                ip += 4;
+                r1 = CLAMP(ip[0]);
+                wp[0] = (uint16_t)((r1 - r2) & mask);
+                r2 = r1;
+                g1 = CLAMP(ip[1]);
+                wp[1] = (uint16_t)((g1 - g2) & mask);
+                g2 = g1;
+                b1 = CLAMP(ip[2]);
+                wp[2] = (uint16_t)((b1 - b2) & mask);
+                b2 = b1;
+                a1 = CLAMP(ip[3]);
+                wp[3] = (uint16_t)((a1 - a2) & mask);
+                a2 = a1;
+            }
+        }
+        else
+        {
+            REPEAT(stride, wp[0] = CLAMP(ip[0]); wp++; ip++)
+            n -= stride;
+            while (n > 0)
+            {
+                REPEAT(stride,
+                       wp[0] = (uint16_t)((CLAMP(ip[0]) - CLAMP(ip[-stride])) &
+                                          mask);
+                       wp++; ip++)
+                n -= stride;
+            }
+        }
     }
 }
 
-
-static void
-horizontalDifference8(unsigned char *ip, int n, int stride, 
-	unsigned short *wp, uint16 *From8)
+static void horizontalDifference8(unsigned char *ip, int n, int stride,
+                                  unsigned short *wp, uint16_t *From8)
 {
-    register int  r1, g1, b1, a1, r2, g2, b2, a2, mask;
+    register int r1, g1, b1, a1, r2, g2, b2, a2, mask;
 
-#undef	 CLAMP
-#define  CLAMP(v) (From8[(v)])
+#undef CLAMP
+#define CLAMP(v) (From8[(v)])
 
     mask = CODE_MASK;
-    if (n >= stride) {
-	if (stride == 3) {
-	    r2 = wp[0] = CLAMP(ip[0]);  g2 = wp[1] = CLAMP(ip[1]);
-	    b2 = wp[2] = CLAMP(ip[2]);
-	    n -= 3;
-	    while (n > 0) {
-		n -= 3;
-		r1 = CLAMP(ip[3]); wp[3] = (uint16)((r1-r2) & mask); r2 = r1;
-		g1 = CLAMP(ip[4]); wp[4] = (uint16)((g1-g2) & mask); g2 = g1;
-		b1 = CLAMP(ip[5]); wp[5] = (uint16)((b1-b2) & mask); b2 = b1;
-		wp += 3;
-		ip += 3;
-	    }
-	} else if (stride == 4) {
-	    r2 = wp[0] = CLAMP(ip[0]);  g2 = wp[1] = CLAMP(ip[1]);
-	    b2 = wp[2] = CLAMP(ip[2]);  a2 = wp[3] = CLAMP(ip[3]);
-	    n -= 4;
-	    while (n > 0) {
-		n -= 4;
-		r1 = CLAMP(ip[4]); wp[4] = (uint16)((r1-r2) & mask); r2 = r1;
-		g1 = CLAMP(ip[5]); wp[5] = (uint16)((g1-g2) & mask); g2 = g1;
-		b1 = CLAMP(ip[6]); wp[6] = (uint16)((b1-b2) & mask); b2 = b1;
-		a1 = CLAMP(ip[7]); wp[7] = (uint16)((a1-a2) & mask); a2 = a1;
-		wp += 4;
-		ip += 4;
-	    }
-	} else {
-        REPEAT(stride, wp[0] = CLAMP(ip[0]); wp++; ip++)
-        n -= stride;
-        while (n > 0) {
-            REPEAT(stride,
-                wp[0] = (uint16)((CLAMP(ip[0])-CLAMP(ip[-stride])) & mask);
-                wp++; ip++)
-            n -= stride;
+    if (n >= stride)
+    {
+        if (stride == 3)
+        {
+            r2 = wp[0] = CLAMP(ip[0]);
+            g2 = wp[1] = CLAMP(ip[1]);
+            b2 = wp[2] = CLAMP(ip[2]);
+            n -= 3;
+            while (n > 0)
+            {
+                n -= 3;
+                r1 = CLAMP(ip[3]);
+                wp[3] = (uint16_t)((r1 - r2) & mask);
+                r2 = r1;
+                g1 = CLAMP(ip[4]);
+                wp[4] = (uint16_t)((g1 - g2) & mask);
+                g2 = g1;
+                b1 = CLAMP(ip[5]);
+                wp[5] = (uint16_t)((b1 - b2) & mask);
+                b2 = b1;
+                wp += 3;
+                ip += 3;
+            }
         }
-    }
+        else if (stride == 4)
+        {
+            r2 = wp[0] = CLAMP(ip[0]);
+            g2 = wp[1] = CLAMP(ip[1]);
+            b2 = wp[2] = CLAMP(ip[2]);
+            a2 = wp[3] = CLAMP(ip[3]);
+            n -= 4;
+            while (n > 0)
+            {
+                n -= 4;
+                r1 = CLAMP(ip[4]);
+                wp[4] = (uint16_t)((r1 - r2) & mask);
+                r2 = r1;
+                g1 = CLAMP(ip[5]);
+                wp[5] = (uint16_t)((g1 - g2) & mask);
+                g2 = g1;
+                b1 = CLAMP(ip[6]);
+                wp[6] = (uint16_t)((b1 - b2) & mask);
+                b2 = b1;
+                a1 = CLAMP(ip[7]);
+                wp[7] = (uint16_t)((a1 - a2) & mask);
+                a2 = a1;
+                wp += 4;
+                ip += 4;
+            }
+        }
+        else
+        {
+            REPEAT(stride, wp[0] = CLAMP(ip[0]); wp++; ip++)
+            n -= stride;
+            while (n > 0)
+            {
+                REPEAT(stride,
+                       wp[0] = (uint16_t)((CLAMP(ip[0]) - CLAMP(ip[-stride])) &
+                                          mask);
+                       wp++; ip++)
+                n -= stride;
+            }
+        }
     }
 }
 
 /*
  * Encode a chunk of pixels.
  */
-static int
-PixarLogEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s)
+static int PixarLogEncode(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s)
 {
-	static const char module[] = "PixarLogEncode";
-	TIFFDirectory *td = &tif->tif_dir;
-	PixarLogState *sp = EncoderState(tif);
-	tmsize_t i;
-	tmsize_t n;
-	int llen;
-	unsigned short * up;
+    static const char module[] = "PixarLogEncode";
+    TIFFDirectory *td = &tif->tif_dir;
+    PixarLogState *sp = EncoderState(tif);
+    tmsize_t i;
+    tmsize_t n;
+    int llen;
+    unsigned short *up;
 
-	(void) s;
+    (void)s;
 
-	switch (sp->user_datafmt) {
-	case PIXARLOGDATAFMT_FLOAT:
-		n = cc / sizeof(float);		/* XXX float == 32 bits */
-		break;
-	case PIXARLOGDATAFMT_16BIT:
-	case PIXARLOGDATAFMT_12BITPICIO:
-	case PIXARLOGDATAFMT_11BITLOG:
-		n = cc / sizeof(uint16);	/* XXX uint16 == 16 bits */
-		break;
-	case PIXARLOGDATAFMT_8BIT:
-	case PIXARLOGDATAFMT_8BITABGR:
-		n = cc;
-		break;
-	default:
-		TIFFErrorExt(tif->tif_clientdata, module,
-			"%d bit input not supported in PixarLog",
-			td->td_bitspersample);
-		return 0;
-	}
-
-	llen = sp->stride * td->td_imagewidth;
-    /* Check against the number of elements (of size uint16) of sp->tbuf */
-    if( n > ((tmsize_t)td->td_rowsperstrip * llen) )
+    switch (sp->user_datafmt)
     {
-        TIFFErrorExt(tif->tif_clientdata, module,
-                     "Too many input bytes provided");
+        case PIXARLOGDATAFMT_FLOAT:
+            n = cc / sizeof(float); /* XXX float == 32 bits */
+            break;
+        case PIXARLOGDATAFMT_16BIT:
+        case PIXARLOGDATAFMT_12BITPICIO:
+        case PIXARLOGDATAFMT_11BITLOG:
+            n = cc / sizeof(uint16_t); /* XXX uint16_t == 16 bits */
+            break;
+        case PIXARLOGDATAFMT_8BIT:
+        case PIXARLOGDATAFMT_8BITABGR:
+            n = cc;
+            break;
+        default:
+            TIFFErrorExtR(tif, module,
+                          "%" PRIu16 " bit input not supported in PixarLog",
+                          td->td_bitspersample);
+            return 0;
+    }
+
+    llen = sp->stride * td->td_imagewidth;
+    /* Check against the number of elements (of size uint16_t) of sp->tbuf */
+    if (n > ((tmsize_t)td->td_rowsperstrip * llen))
+    {
+        TIFFErrorExtR(tif, module, "Too many input bytes provided");
         return 0;
     }
 
-	for (i = 0, up = sp->tbuf; i < n; i += llen, up += llen) {
-		switch (sp->user_datafmt)  {
-		case PIXARLOGDATAFMT_FLOAT:
-			horizontalDifferenceF((float *)bp, llen, 
-				sp->stride, up, sp->FromLT2);
-			bp += llen * sizeof(float);
-			break;
-		case PIXARLOGDATAFMT_16BIT:
-			horizontalDifference16((uint16 *)bp, llen, 
-				sp->stride, up, sp->From14);
-			bp += llen * sizeof(uint16);
-			break;
-		case PIXARLOGDATAFMT_8BIT:
-			horizontalDifference8((unsigned char *)bp, llen, 
-				sp->stride, up, sp->From8);
-			bp += llen * sizeof(unsigned char);
-			break;
-		default:
-			TIFFErrorExt(tif->tif_clientdata, module,
-				"%d bit input not supported in PixarLog",
-				td->td_bitspersample);
-			return 0;
-		}
-	}
- 
-	sp->stream.next_in = (unsigned char *) sp->tbuf;
-	assert(sizeof(sp->stream.avail_in)==4);  /* if this assert gets raised,
-	    we need to simplify this code to reflect a ZLib that is likely updated
-	    to deal with 8byte memory sizes, though this code will respond
-	    appropriately even before we simplify it */
-	sp->stream.avail_in = (uInt) (n * sizeof(uint16));
-	if ((sp->stream.avail_in / sizeof(uint16)) != (uInt) n)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module,
-			     "ZLib cannot deal with buffers this size");
-		return (0);
-	}
+    for (i = 0, up = sp->tbuf; i < n; i += llen, up += llen)
+    {
+        switch (sp->user_datafmt)
+        {
+            case PIXARLOGDATAFMT_FLOAT:
+                horizontalDifferenceF((float *)bp, llen, sp->stride, up,
+                                      sp->FromLT2);
+                bp += llen * sizeof(float);
+                break;
+            case PIXARLOGDATAFMT_16BIT:
+                horizontalDifference16((uint16_t *)bp, llen, sp->stride, up,
+                                       sp->From14);
+                bp += llen * sizeof(uint16_t);
+                break;
+            case PIXARLOGDATAFMT_8BIT:
+                horizontalDifference8((unsigned char *)bp, llen, sp->stride, up,
+                                      sp->From8);
+                bp += llen * sizeof(unsigned char);
+                break;
+            default:
+                TIFFErrorExtR(tif, module,
+                              "%" PRIu16 " bit input not supported in PixarLog",
+                              td->td_bitspersample);
+                return 0;
+        }
+    }
 
-	do {
-		if (deflate(&sp->stream, Z_NO_FLUSH) != Z_OK) {
-			TIFFErrorExt(tif->tif_clientdata, module, "Encoder error: %s",
-			    sp->stream.msg ? sp->stream.msg : "(null)");
-			return (0);
-		}
-		if (sp->stream.avail_out == 0) {
-			tif->tif_rawcc = tif->tif_rawdatasize;
-			TIFFFlushData1(tif);
-			sp->stream.next_out = tif->tif_rawdata;
-			sp->stream.avail_out = (uInt) tif->tif_rawdatasize;  /* this is a safe typecast, as check is made already in PixarLogPreEncode */
-		}
-	} while (sp->stream.avail_in > 0);
-	return (1);
+    sp->stream.next_in = (unsigned char *)sp->tbuf;
+    assert(sizeof(sp->stream.avail_in) == 4); /* if this assert gets raised,
+         we need to simplify this code to reflect a ZLib that is likely updated
+         to deal with 8byte memory sizes, though this code will respond
+         appropriately even before we simplify it */
+    sp->stream.avail_in = (uInt)(n * sizeof(uint16_t));
+    if ((sp->stream.avail_in / sizeof(uint16_t)) != (uInt)n)
+    {
+        TIFFErrorExtR(tif, module, "ZLib cannot deal with buffers this size");
+        return (0);
+    }
+
+    do
+    {
+        if (deflate(&sp->stream, Z_NO_FLUSH) != Z_OK)
+        {
+            TIFFErrorExtR(tif, module, "Encoder error: %s",
+                          sp->stream.msg ? sp->stream.msg : "(null)");
+            return (0);
+        }
+        if (sp->stream.avail_out == 0)
+        {
+            tif->tif_rawcc = tif->tif_rawdatasize;
+            if (!TIFFFlushData1(tif))
+                return 0;
+            sp->stream.next_out = tif->tif_rawdata;
+            sp->stream.avail_out =
+                (uInt)tif
+                    ->tif_rawdatasize; /* this is a safe typecast, as check is
+                                          made already in PixarLogPreEncode */
+        }
+    } while (sp->stream.avail_in > 0);
+    return (1);
 }
 
 /*
@@ -1213,265 +1398,273 @@
  * string and tacking on an End Of Information code.
  */
 
-static int
-PixarLogPostEncode(TIFF* tif)
+static int PixarLogPostEncode(TIFF *tif)
 {
-	static const char module[] = "PixarLogPostEncode";
-	PixarLogState *sp = EncoderState(tif);
-	int state;
+    static const char module[] = "PixarLogPostEncode";
+    PixarLogState *sp = EncoderState(tif);
+    int state;
 
-	sp->stream.avail_in = 0;
+    sp->stream.avail_in = 0;
 
-	do {
-		state = deflate(&sp->stream, Z_FINISH);
-		switch (state) {
-		case Z_STREAM_END:
-		case Z_OK:
-		    if ((tmsize_t)sp->stream.avail_out != tif->tif_rawdatasize) {
-			    tif->tif_rawcc =
-				tif->tif_rawdatasize - sp->stream.avail_out;
-			    TIFFFlushData1(tif);
-			    sp->stream.next_out = tif->tif_rawdata;
-			    sp->stream.avail_out = (uInt) tif->tif_rawdatasize;  /* this is a safe typecast, as check is made already in PixarLogPreEncode */
-		    }
-		    break;
-		default:
-			TIFFErrorExt(tif->tif_clientdata, module, "ZLib error: %s",
-			sp->stream.msg ? sp->stream.msg : "(null)");
-		    return (0);
-		}
-	} while (state != Z_STREAM_END);
-	return (1);
-}
-
-static void
-PixarLogClose(TIFF* tif)
-{
-        PixarLogState* sp = (PixarLogState*) tif->tif_data;
-	TIFFDirectory *td = &tif->tif_dir;
-
-	assert(sp != 0);
-	/* In a really sneaky (and really incorrect, and untruthful, and
-	 * troublesome, and error-prone) maneuver that completely goes against
-	 * the spirit of TIFF, and breaks TIFF, on close, we covertly
-	 * modify both bitspersample and sampleformat in the directory to
-	 * indicate 8-bit linear.  This way, the decode "just works" even for
-	 * readers that don't know about PixarLog, or how to set
-	 * the PIXARLOGDATFMT pseudo-tag.
-	 */
-
-        if (sp->state&PLSTATE_INIT) {
-            /* We test the state to avoid an issue such as in
-             * http://bugzilla.maptools.org/show_bug.cgi?id=2604
-             * What appends in that case is that the bitspersample is 1 and
-             * a TransferFunction is set. The size of the TransferFunction
-             * depends on 1<<bitspersample. So if we increase it, an access
-             * out of the buffer will happen at directory flushing.
-             * Another option would be to clear those targs. 
-             */
-            td->td_bitspersample = 8;
-            td->td_sampleformat = SAMPLEFORMAT_UINT;
+    do
+    {
+        state = deflate(&sp->stream, Z_FINISH);
+        switch (state)
+        {
+            case Z_STREAM_END:
+            case Z_OK:
+                if ((tmsize_t)sp->stream.avail_out != tif->tif_rawdatasize)
+                {
+                    tif->tif_rawcc =
+                        tif->tif_rawdatasize - sp->stream.avail_out;
+                    if (!TIFFFlushData1(tif))
+                        return 0;
+                    sp->stream.next_out = tif->tif_rawdata;
+                    sp->stream.avail_out =
+                        (uInt)tif->tif_rawdatasize; /* this is a safe typecast,
+                                                       as check is made already
+                                                       in PixarLogPreEncode */
+                }
+                break;
+            default:
+                TIFFErrorExtR(tif, module, "ZLib error: %s",
+                              sp->stream.msg ? sp->stream.msg : "(null)");
+                return (0);
         }
+    } while (state != Z_STREAM_END);
+    return (1);
 }
 
-static void
-PixarLogCleanup(TIFF* tif)
+static void PixarLogClose(TIFF *tif)
 {
-	PixarLogState* sp = (PixarLogState*) tif->tif_data;
+    PixarLogState *sp = (PixarLogState *)tif->tif_data;
+    TIFFDirectory *td = &tif->tif_dir;
 
-	assert(sp != 0);
+    assert(sp != 0);
+    /* In a really sneaky (and really incorrect, and untruthful, and
+     * troublesome, and error-prone) maneuver that completely goes against
+     * the spirit of TIFF, and breaks TIFF, on close, we covertly
+     * modify both bitspersample and sampleformat in the directory to
+     * indicate 8-bit linear.  This way, the decode "just works" even for
+     * readers that don't know about PixarLog, or how to set
+     * the PIXARLOGDATFMT pseudo-tag.
+     */
 
-	(void)TIFFPredictorCleanup(tif);
-
-	tif->tif_tagmethods.vgetfield = sp->vgetparent;
-	tif->tif_tagmethods.vsetfield = sp->vsetparent;
-
-	if (sp->FromLT2) _TIFFfree(sp->FromLT2);
-	if (sp->From14) _TIFFfree(sp->From14);
-	if (sp->From8) _TIFFfree(sp->From8);
-	if (sp->ToLinearF) _TIFFfree(sp->ToLinearF);
-	if (sp->ToLinear16) _TIFFfree(sp->ToLinear16);
-	if (sp->ToLinear8) _TIFFfree(sp->ToLinear8);
-	if (sp->state&PLSTATE_INIT) {
-		if (tif->tif_mode == O_RDONLY)
-			inflateEnd(&sp->stream);
-		else
-			deflateEnd(&sp->stream);
-	}
-	if (sp->tbuf)
-		_TIFFfree(sp->tbuf);
-	_TIFFfree(sp);
-	tif->tif_data = NULL;
-
-	_TIFFSetDefaultCompressionState(tif);
+    if (sp->state & PLSTATE_INIT)
+    {
+        /* We test the state to avoid an issue such as in
+         * http://bugzilla.maptools.org/show_bug.cgi?id=2604
+         * What appends in that case is that the bitspersample is 1 and
+         * a TransferFunction is set. The size of the TransferFunction
+         * depends on 1<<bitspersample. So if we increase it, an access
+         * out of the buffer will happen at directory flushing.
+         * Another option would be to clear those targs.
+         */
+        td->td_bitspersample = 8;
+        td->td_sampleformat = SAMPLEFORMAT_UINT;
+    }
 }
 
-static int
-PixarLogVSetField(TIFF* tif, uint32 tag, va_list ap)
+static void PixarLogCleanup(TIFF *tif)
+{
+    PixarLogState *sp = (PixarLogState *)tif->tif_data;
+
+    assert(sp != 0);
+
+    (void)TIFFPredictorCleanup(tif);
+
+    tif->tif_tagmethods.vgetfield = sp->vgetparent;
+    tif->tif_tagmethods.vsetfield = sp->vsetparent;
+
+    if (sp->FromLT2)
+        _TIFFfreeExt(tif, sp->FromLT2);
+    if (sp->From14)
+        _TIFFfreeExt(tif, sp->From14);
+    if (sp->From8)
+        _TIFFfreeExt(tif, sp->From8);
+    if (sp->ToLinearF)
+        _TIFFfreeExt(tif, sp->ToLinearF);
+    if (sp->ToLinear16)
+        _TIFFfreeExt(tif, sp->ToLinear16);
+    if (sp->ToLinear8)
+        _TIFFfreeExt(tif, sp->ToLinear8);
+    if (sp->state & PLSTATE_INIT)
+    {
+        if (tif->tif_mode == O_RDONLY)
+            inflateEnd(&sp->stream);
+        else
+            deflateEnd(&sp->stream);
+    }
+    if (sp->tbuf)
+        _TIFFfreeExt(tif, sp->tbuf);
+    _TIFFfreeExt(tif, sp);
+    tif->tif_data = NULL;
+
+    _TIFFSetDefaultCompressionState(tif);
+}
+
+static int PixarLogVSetField(TIFF *tif, uint32_t tag, va_list ap)
 {
     static const char module[] = "PixarLogVSetField";
     PixarLogState *sp = (PixarLogState *)tif->tif_data;
     int result;
 
-    switch (tag) {
-     case TIFFTAG_PIXARLOGQUALITY:
-		sp->quality = (int) va_arg(ap, int);
-		if (tif->tif_mode != O_RDONLY && (sp->state&PLSTATE_INIT)) {
-			if (deflateParams(&sp->stream,
-			    sp->quality, Z_DEFAULT_STRATEGY) != Z_OK) {
-				TIFFErrorExt(tif->tif_clientdata, module, "ZLib error: %s",
-					sp->stream.msg ? sp->stream.msg : "(null)");
-				return (0);
-			}
-		}
-		return (1);
-     case TIFFTAG_PIXARLOGDATAFMT:
-	sp->user_datafmt = (int) va_arg(ap, int);
-	/* Tweak the TIFF header so that the rest of libtiff knows what
-	 * size of data will be passed between app and library, and
-	 * assume that the app knows what it is doing and is not
-	 * confused by these header manipulations...
-	 */
-	switch (sp->user_datafmt) {
-	 case PIXARLOGDATAFMT_8BIT:
-	 case PIXARLOGDATAFMT_8BITABGR:
-	    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
-	    TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
-	    break;
-	 case PIXARLOGDATAFMT_11BITLOG:
-	    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 16);
-	    TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
-	    break;
-	 case PIXARLOGDATAFMT_12BITPICIO:
-	    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 16);
-	    TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT);
-	    break;
-	 case PIXARLOGDATAFMT_16BIT:
-	    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 16);
-	    TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
-	    break;
-	 case PIXARLOGDATAFMT_FLOAT:
-	    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32);
-	    TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP);
-	    break;
-	}
-	/*
-	 * Must recalculate sizes should bits/sample change.
-	 */
-	tif->tif_tilesize = isTiled(tif) ? TIFFTileSize(tif) : (tmsize_t)(-1);
-	tif->tif_scanlinesize = TIFFScanlineSize(tif);
-	result = 1;		/* NB: pseudo tag */
-	break;
-     default:
-	result = (*sp->vsetparent)(tif, tag, ap);
+    switch (tag)
+    {
+        case TIFFTAG_PIXARLOGQUALITY:
+            sp->quality = (int)va_arg(ap, int);
+            if (tif->tif_mode != O_RDONLY && (sp->state & PLSTATE_INIT))
+            {
+                if (deflateParams(&sp->stream, sp->quality,
+                                  Z_DEFAULT_STRATEGY) != Z_OK)
+                {
+                    TIFFErrorExtR(tif, module, "ZLib error: %s",
+                                  sp->stream.msg ? sp->stream.msg : "(null)");
+                    return (0);
+                }
+            }
+            return (1);
+        case TIFFTAG_PIXARLOGDATAFMT:
+            sp->user_datafmt = (int)va_arg(ap, int);
+            /* Tweak the TIFF header so that the rest of libtiff knows what
+             * size of data will be passed between app and library, and
+             * assume that the app knows what it is doing and is not
+             * confused by these header manipulations...
+             */
+            switch (sp->user_datafmt)
+            {
+                case PIXARLOGDATAFMT_8BIT:
+                case PIXARLOGDATAFMT_8BITABGR:
+                    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
+                    TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
+                    break;
+                case PIXARLOGDATAFMT_11BITLOG:
+                    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 16);
+                    TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
+                    break;
+                case PIXARLOGDATAFMT_12BITPICIO:
+                    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 16);
+                    TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT);
+                    break;
+                case PIXARLOGDATAFMT_16BIT:
+                    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 16);
+                    TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
+                    break;
+                case PIXARLOGDATAFMT_FLOAT:
+                    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32);
+                    TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT,
+                                 SAMPLEFORMAT_IEEEFP);
+                    break;
+            }
+            /*
+             * Must recalculate sizes should bits/sample change.
+             */
+            tif->tif_tilesize =
+                isTiled(tif) ? TIFFTileSize(tif) : (tmsize_t)(-1);
+            tif->tif_scanlinesize = TIFFScanlineSize(tif);
+            result = 1; /* NB: pseudo tag */
+            break;
+        default:
+            result = (*sp->vsetparent)(tif, tag, ap);
     }
     return (result);
 }
 
-static int
-PixarLogVGetField(TIFF* tif, uint32 tag, va_list ap)
+static int PixarLogVGetField(TIFF *tif, uint32_t tag, va_list ap)
 {
     PixarLogState *sp = (PixarLogState *)tif->tif_data;
 
-    switch (tag) {
-     case TIFFTAG_PIXARLOGQUALITY:
-	*va_arg(ap, int*) = sp->quality;
-	break;
-     case TIFFTAG_PIXARLOGDATAFMT:
-	*va_arg(ap, int*) = sp->user_datafmt;
-	break;
-     default:
-	return (*sp->vgetparent)(tif, tag, ap);
+    switch (tag)
+    {
+        case TIFFTAG_PIXARLOGQUALITY:
+            *va_arg(ap, int *) = sp->quality;
+            break;
+        case TIFFTAG_PIXARLOGDATAFMT:
+            *va_arg(ap, int *) = sp->user_datafmt;
+            break;
+        default:
+            return (*sp->vgetparent)(tif, tag, ap);
     }
     return (1);
 }
 
 static const TIFFField pixarlogFields[] = {
-    {TIFFTAG_PIXARLOGDATAFMT, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "", NULL},
-    {TIFFTAG_PIXARLOGQUALITY, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "", NULL}
-};
+    {TIFFTAG_PIXARLOGDATAFMT, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT,
+     TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "", NULL},
+    {TIFFTAG_PIXARLOGQUALITY, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT,
+     TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "", NULL}};
 
-int
-TIFFInitPixarLog(TIFF* tif, int scheme)
+int TIFFInitPixarLog(TIFF *tif, int scheme)
 {
-	static const char module[] = "TIFFInitPixarLog";
+    static const char module[] = "TIFFInitPixarLog";
 
-	PixarLogState* sp;
+    PixarLogState *sp;
 
-	assert(scheme == COMPRESSION_PIXARLOG);
+    (void)scheme;
+    assert(scheme == COMPRESSION_PIXARLOG);
 
-	/*
-	 * Merge codec-specific tag information.
-	 */
-	if (!_TIFFMergeFields(tif, pixarlogFields,
-			      TIFFArrayCount(pixarlogFields))) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-			     "Merging PixarLog codec-specific tags failed");
-		return 0;
-	}
+    /*
+     * Merge codec-specific tag information.
+     */
+    if (!_TIFFMergeFields(tif, pixarlogFields, TIFFArrayCount(pixarlogFields)))
+    {
+        TIFFErrorExtR(tif, module,
+                      "Merging PixarLog codec-specific tags failed");
+        return 0;
+    }
 
-	/*
-	 * Allocate state block so tag methods have storage to record values.
-	 */
-	tif->tif_data = (uint8*) _TIFFmalloc(sizeof (PixarLogState));
-	if (tif->tif_data == NULL)
-		goto bad;
-	sp = (PixarLogState*) tif->tif_data;
-	_TIFFmemset(sp, 0, sizeof (*sp));
-	sp->stream.data_type = Z_BINARY;
-	sp->user_datafmt = PIXARLOGDATAFMT_UNKNOWN;
+    /*
+     * Allocate state block so tag methods have storage to record values.
+     */
+    tif->tif_data = (uint8_t *)_TIFFmallocExt(tif, sizeof(PixarLogState));
+    if (tif->tif_data == NULL)
+        goto bad;
+    sp = (PixarLogState *)tif->tif_data;
+    _TIFFmemset(sp, 0, sizeof(*sp));
+    sp->stream.data_type = Z_BINARY;
+    sp->user_datafmt = PIXARLOGDATAFMT_UNKNOWN;
 
-	/*
-	 * Install codec methods.
-	 */
-	tif->tif_fixuptags = PixarLogFixupTags; 
-	tif->tif_setupdecode = PixarLogSetupDecode;
-	tif->tif_predecode = PixarLogPreDecode;
-	tif->tif_decoderow = PixarLogDecode;
-	tif->tif_decodestrip = PixarLogDecode;  
-	tif->tif_decodetile = PixarLogDecode;
-	tif->tif_setupencode = PixarLogSetupEncode;
-	tif->tif_preencode = PixarLogPreEncode;
-	tif->tif_postencode = PixarLogPostEncode;
-	tif->tif_encoderow = PixarLogEncode;  
-	tif->tif_encodestrip = PixarLogEncode;
-	tif->tif_encodetile = PixarLogEncode;  
-	tif->tif_close = PixarLogClose;
-	tif->tif_cleanup = PixarLogCleanup;
+    /*
+     * Install codec methods.
+     */
+    tif->tif_fixuptags = PixarLogFixupTags;
+    tif->tif_setupdecode = PixarLogSetupDecode;
+    tif->tif_predecode = PixarLogPreDecode;
+    tif->tif_decoderow = PixarLogDecode;
+    tif->tif_decodestrip = PixarLogDecode;
+    tif->tif_decodetile = PixarLogDecode;
+    tif->tif_setupencode = PixarLogSetupEncode;
+    tif->tif_preencode = PixarLogPreEncode;
+    tif->tif_postencode = PixarLogPostEncode;
+    tif->tif_encoderow = PixarLogEncode;
+    tif->tif_encodestrip = PixarLogEncode;
+    tif->tif_encodetile = PixarLogEncode;
+    tif->tif_close = PixarLogClose;
+    tif->tif_cleanup = PixarLogCleanup;
 
-	/* Override SetField so we can handle our private pseudo-tag */
-	sp->vgetparent = tif->tif_tagmethods.vgetfield;
-	tif->tif_tagmethods.vgetfield = PixarLogVGetField;   /* hook for codec tags */
-	sp->vsetparent = tif->tif_tagmethods.vsetfield;
-	tif->tif_tagmethods.vsetfield = PixarLogVSetField;   /* hook for codec tags */
+    /* Override SetField so we can handle our private pseudo-tag */
+    sp->vgetparent = tif->tif_tagmethods.vgetfield;
+    tif->tif_tagmethods.vgetfield = PixarLogVGetField; /* hook for codec tags */
+    sp->vsetparent = tif->tif_tagmethods.vsetfield;
+    tif->tif_tagmethods.vsetfield = PixarLogVSetField; /* hook for codec tags */
 
-	/* Default values for codec-specific fields */
-	sp->quality = Z_DEFAULT_COMPRESSION; /* default comp. level */
-	sp->state = 0;
+    /* Default values for codec-specific fields */
+    sp->quality = Z_DEFAULT_COMPRESSION; /* default comp. level */
+    sp->state = 0;
 
-	/* we don't wish to use the predictor, 
-	 * the default is none, which predictor value 1
-	 */
-	(void) TIFFPredictorInit(tif);
+    /* we don't wish to use the predictor,
+     * the default is none, which predictor value 1
+     */
+    (void)TIFFPredictorInit(tif);
 
-	/*
-	 * build the companding tables 
-	 */
-	PixarLogMakeTables(sp);
+    /*
+     * build the companding tables
+     */
+    PixarLogMakeTables(tif, sp);
 
-	return (1);
+    return (1);
 bad:
-	TIFFErrorExt(tif->tif_clientdata, module,
-		     "No space for PixarLog state block");
-	return (0);
+    TIFFErrorExtR(tif, module, "No space for PixarLog state block");
+    return (0);
 }
 #endif /* PIXARLOG_SUPPORT */
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_predict.c b/third_party/libtiff/tif_predict.c
index b775663..386b5fe 100644
--- a/third_party/libtiff/tif_predict.c
+++ b/third_party/libtiff/tif_predict.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -27,445 +27,548 @@
  *
  * Predictor Tag Support (used by multiple codecs).
  */
-#include "tiffiop.h"
 #include "tif_predict.h"
+#include "tiffiop.h"
 
-#define	PredictorState(tif)	((TIFFPredictorState*) (tif)->tif_data)
+#define PredictorState(tif) ((TIFFPredictorState *)(tif)->tif_data)
 
-static int horAcc8(TIFF* tif, uint8* cp0, tmsize_t cc);
-static int horAcc16(TIFF* tif, uint8* cp0, tmsize_t cc);
-static int horAcc32(TIFF* tif, uint8* cp0, tmsize_t cc);
-static int swabHorAcc16(TIFF* tif, uint8* cp0, tmsize_t cc);
-static int swabHorAcc32(TIFF* tif, uint8* cp0, tmsize_t cc);
-static int horDiff8(TIFF* tif, uint8* cp0, tmsize_t cc);
-static int horDiff16(TIFF* tif, uint8* cp0, tmsize_t cc);
-static int horDiff32(TIFF* tif, uint8* cp0, tmsize_t cc);
-static int swabHorDiff16(TIFF* tif, uint8* cp0, tmsize_t cc);
-static int swabHorDiff32(TIFF* tif, uint8* cp0, tmsize_t cc);
-static int fpAcc(TIFF* tif, uint8* cp0, tmsize_t cc);
-static int fpDiff(TIFF* tif, uint8* cp0, tmsize_t cc);
-static int PredictorDecodeRow(TIFF* tif, uint8* op0, tmsize_t occ0, uint16 s);
-static int PredictorDecodeTile(TIFF* tif, uint8* op0, tmsize_t occ0, uint16 s);
-static int PredictorEncodeRow(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s);
-static int PredictorEncodeTile(TIFF* tif, uint8* bp0, tmsize_t cc0, uint16 s);
+static int horAcc8(TIFF *tif, uint8_t *cp0, tmsize_t cc);
+static int horAcc16(TIFF *tif, uint8_t *cp0, tmsize_t cc);
+static int horAcc32(TIFF *tif, uint8_t *cp0, tmsize_t cc);
+static int horAcc64(TIFF *tif, uint8_t *cp0, tmsize_t cc);
+static int swabHorAcc16(TIFF *tif, uint8_t *cp0, tmsize_t cc);
+static int swabHorAcc32(TIFF *tif, uint8_t *cp0, tmsize_t cc);
+static int swabHorAcc64(TIFF *tif, uint8_t *cp0, tmsize_t cc);
+static int horDiff8(TIFF *tif, uint8_t *cp0, tmsize_t cc);
+static int horDiff16(TIFF *tif, uint8_t *cp0, tmsize_t cc);
+static int horDiff32(TIFF *tif, uint8_t *cp0, tmsize_t cc);
+static int horDiff64(TIFF *tif, uint8_t *cp0, tmsize_t cc);
+static int swabHorDiff16(TIFF *tif, uint8_t *cp0, tmsize_t cc);
+static int swabHorDiff32(TIFF *tif, uint8_t *cp0, tmsize_t cc);
+static int swabHorDiff64(TIFF *tif, uint8_t *cp0, tmsize_t cc);
+static int fpAcc(TIFF *tif, uint8_t *cp0, tmsize_t cc);
+static int fpDiff(TIFF *tif, uint8_t *cp0, tmsize_t cc);
+static int PredictorDecodeRow(TIFF *tif, uint8_t *op0, tmsize_t occ0,
+                              uint16_t s);
+static int PredictorDecodeTile(TIFF *tif, uint8_t *op0, tmsize_t occ0,
+                               uint16_t s);
+static int PredictorEncodeRow(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s);
+static int PredictorEncodeTile(TIFF *tif, uint8_t *bp0, tmsize_t cc0,
+                               uint16_t s);
 
-static int
-PredictorSetup(TIFF* tif)
+static int PredictorSetup(TIFF *tif)
 {
-	static const char module[] = "PredictorSetup";
+    static const char module[] = "PredictorSetup";
 
-	TIFFPredictorState* sp = PredictorState(tif);
-	TIFFDirectory* td = &tif->tif_dir;
+    TIFFPredictorState *sp = PredictorState(tif);
+    TIFFDirectory *td = &tif->tif_dir;
 
-	switch (sp->predictor)		/* no differencing */
-	{
-		case PREDICTOR_NONE:
-			return 1;
-		case PREDICTOR_HORIZONTAL:
-			if (td->td_bitspersample != 8
-			    && td->td_bitspersample != 16
-			    && td->td_bitspersample != 32) {
-				TIFFErrorExt(tif->tif_clientdata, module,
-				    "Horizontal differencing \"Predictor\" not supported with %d-bit samples",
-				    td->td_bitspersample);
-				return 0;
-			}
-			break;
-		case PREDICTOR_FLOATINGPOINT:
-			if (td->td_sampleformat != SAMPLEFORMAT_IEEEFP) {
-				TIFFErrorExt(tif->tif_clientdata, module,
-				    "Floating point \"Predictor\" not supported with %d data format",
-				    td->td_sampleformat);
-				return 0;
-			}
-                        if (td->td_bitspersample != 16
-                            && td->td_bitspersample != 24
-                            && td->td_bitspersample != 32
-                            && td->td_bitspersample != 64) { /* Should 64 be allowed? */
-                                TIFFErrorExt(tif->tif_clientdata, module,
-                                             "Floating point \"Predictor\" not supported with %d-bit samples",
-                                             td->td_bitspersample);
-				return 0;
-                            }
-			break;
-		default:
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "\"Predictor\" value %d not supported",
-			    sp->predictor);
-			return 0;
-	}
-	sp->stride = (td->td_planarconfig == PLANARCONFIG_CONTIG ?
-	    td->td_samplesperpixel : 1);
-	/*
-	 * Calculate the scanline/tile-width size in bytes.
-	 */
-	if (isTiled(tif))
-		sp->rowsize = TIFFTileRowSize(tif);
-	else
-		sp->rowsize = TIFFScanlineSize(tif);
-	if (sp->rowsize == 0)
-		return 0;
-
-	return 1;
-}
-
-static int
-PredictorSetupDecode(TIFF* tif)
-{
-	TIFFPredictorState* sp = PredictorState(tif);
-	TIFFDirectory* td = &tif->tif_dir;
-
-	/* Note: when PredictorSetup() fails, the effets of setupdecode() */
-	/* will not be "cancelled" so setupdecode() might be robust to */
-	/* be called several times. */
-	if (!(*sp->setupdecode)(tif) || !PredictorSetup(tif))
-		return 0;
-
-	if (sp->predictor == 2) {
-		switch (td->td_bitspersample) {
-			case 8:  sp->decodepfunc = horAcc8; break;
-			case 16: sp->decodepfunc = horAcc16; break;
-			case 32: sp->decodepfunc = horAcc32; break;
-		}
-		/*
-		 * Override default decoding method with one that does the
-		 * predictor stuff.
-		 */
-                if( tif->tif_decoderow != PredictorDecodeRow )
-                {
-                    sp->decoderow = tif->tif_decoderow;
-                    tif->tif_decoderow = PredictorDecodeRow;
-                    sp->decodestrip = tif->tif_decodestrip;
-                    tif->tif_decodestrip = PredictorDecodeTile;
-                    sp->decodetile = tif->tif_decodetile;
-                    tif->tif_decodetile = PredictorDecodeTile;
-                }
-
-		/*
-		 * If the data is horizontally differenced 16-bit data that
-		 * requires byte-swapping, then it must be byte swapped before
-		 * the accumulation step.  We do this with a special-purpose
-		 * routine and override the normal post decoding logic that
-		 * the library setup when the directory was read.
-		 */
-		if (tif->tif_flags & TIFF_SWAB) {
-			if (sp->decodepfunc == horAcc16) {
-				sp->decodepfunc = swabHorAcc16;
-				tif->tif_postdecode = _TIFFNoPostDecode;
-            } else if (sp->decodepfunc == horAcc32) {
-				sp->decodepfunc = swabHorAcc32;
-				tif->tif_postdecode = _TIFFNoPostDecode;
+    switch (sp->predictor) /* no differencing */
+    {
+        case PREDICTOR_NONE:
+            return 1;
+        case PREDICTOR_HORIZONTAL:
+            if (td->td_bitspersample != 8 && td->td_bitspersample != 16 &&
+                td->td_bitspersample != 32 && td->td_bitspersample != 64)
+            {
+                TIFFErrorExtR(tif, module,
+                              "Horizontal differencing \"Predictor\" not "
+                              "supported with %" PRIu16 "-bit samples",
+                              td->td_bitspersample);
+                return 0;
             }
-		}
-	}
+            break;
+        case PREDICTOR_FLOATINGPOINT:
+            if (td->td_sampleformat != SAMPLEFORMAT_IEEEFP)
+            {
+                TIFFErrorExtR(
+                    tif, module,
+                    "Floating point \"Predictor\" not supported with %" PRIu16
+                    " data format",
+                    td->td_sampleformat);
+                return 0;
+            }
+            if (td->td_bitspersample != 16 && td->td_bitspersample != 24 &&
+                td->td_bitspersample != 32 && td->td_bitspersample != 64)
+            { /* Should 64 be allowed? */
+                TIFFErrorExtR(
+                    tif, module,
+                    "Floating point \"Predictor\" not supported with %" PRIu16
+                    "-bit samples",
+                    td->td_bitspersample);
+                return 0;
+            }
+            break;
+        default:
+            TIFFErrorExtR(tif, module, "\"Predictor\" value %d not supported",
+                          sp->predictor);
+            return 0;
+    }
+    sp->stride =
+        (td->td_planarconfig == PLANARCONFIG_CONTIG ? td->td_samplesperpixel
+                                                    : 1);
+    /*
+     * Calculate the scanline/tile-width size in bytes.
+     */
+    if (isTiled(tif))
+        sp->rowsize = TIFFTileRowSize(tif);
+    else
+        sp->rowsize = TIFFScanlineSize(tif);
+    if (sp->rowsize == 0)
+        return 0;
 
-	else if (sp->predictor == 3) {
-		sp->decodepfunc = fpAcc;
-		/*
-		 * Override default decoding method with one that does the
-		 * predictor stuff.
-		 */
-                if( tif->tif_decoderow != PredictorDecodeRow )
-                {
-                    sp->decoderow = tif->tif_decoderow;
-                    tif->tif_decoderow = PredictorDecodeRow;
-                    sp->decodestrip = tif->tif_decodestrip;
-                    tif->tif_decodestrip = PredictorDecodeTile;
-                    sp->decodetile = tif->tif_decodetile;
-                    tif->tif_decodetile = PredictorDecodeTile;
-                }
-		/*
-		 * The data should not be swapped outside of the floating
-		 * point predictor, the accumulation routine should return
-		 * byres in the native order.
-		 */
-		if (tif->tif_flags & TIFF_SWAB) {
-			tif->tif_postdecode = _TIFFNoPostDecode;
-		}
-		/*
-		 * Allocate buffer to keep the decoded bytes before
-		 * rearranging in the right order
-		 */
-	}
-
-	return 1;
+    return 1;
 }
 
-static int
-PredictorSetupEncode(TIFF* tif)
+static int PredictorSetupDecode(TIFF *tif)
 {
-	TIFFPredictorState* sp = PredictorState(tif);
-	TIFFDirectory* td = &tif->tif_dir;
+    TIFFPredictorState *sp = PredictorState(tif);
+    TIFFDirectory *td = &tif->tif_dir;
 
-	if (!(*sp->setupencode)(tif) || !PredictorSetup(tif))
-		return 0;
+    /* Note: when PredictorSetup() fails, the effets of setupdecode() */
+    /* will not be "canceled" so setupdecode() might be robust to */
+    /* be called several times. */
+    if (!(*sp->setupdecode)(tif) || !PredictorSetup(tif))
+        return 0;
 
-	if (sp->predictor == 2) {
-		switch (td->td_bitspersample) {
-			case 8:  sp->encodepfunc = horDiff8; break;
-			case 16: sp->encodepfunc = horDiff16; break;
-			case 32: sp->encodepfunc = horDiff32; break;
-		}
-		/*
-		 * Override default encoding method with one that does the
-		 * predictor stuff.
-		 */
-                if( tif->tif_encoderow != PredictorEncodeRow )
-                {
-                    sp->encoderow = tif->tif_encoderow;
-                    tif->tif_encoderow = PredictorEncodeRow;
-                    sp->encodestrip = tif->tif_encodestrip;
-                    tif->tif_encodestrip = PredictorEncodeTile;
-                    sp->encodetile = tif->tif_encodetile;
-                    tif->tif_encodetile = PredictorEncodeTile;
-                }
-
-                /*
-                 * If the data is horizontally differenced 16-bit data that
-                 * requires byte-swapping, then it must be byte swapped after
-                 * the differentiation step.  We do this with a special-purpose
-                 * routine and override the normal post decoding logic that
-                 * the library setup when the directory was read.
-                 */
-                if (tif->tif_flags & TIFF_SWAB) {
-                    if (sp->encodepfunc == horDiff16) {
-                            sp->encodepfunc = swabHorDiff16;
-                            tif->tif_postdecode = _TIFFNoPostDecode;
-                    } else if (sp->encodepfunc == horDiff32) {
-                            sp->encodepfunc = swabHorDiff32;
-                            tif->tif_postdecode = _TIFFNoPostDecode;
-                    }
-                }
+    if (sp->predictor == 2)
+    {
+        switch (td->td_bitspersample)
+        {
+            case 8:
+                sp->decodepfunc = horAcc8;
+                break;
+            case 16:
+                sp->decodepfunc = horAcc16;
+                break;
+            case 32:
+                sp->decodepfunc = horAcc32;
+                break;
+            case 64:
+                sp->decodepfunc = horAcc64;
+                break;
+        }
+        /*
+         * Override default decoding method with one that does the
+         * predictor stuff.
+         */
+        if (tif->tif_decoderow != PredictorDecodeRow)
+        {
+            sp->decoderow = tif->tif_decoderow;
+            tif->tif_decoderow = PredictorDecodeRow;
+            sp->decodestrip = tif->tif_decodestrip;
+            tif->tif_decodestrip = PredictorDecodeTile;
+            sp->decodetile = tif->tif_decodetile;
+            tif->tif_decodetile = PredictorDecodeTile;
         }
 
-	else if (sp->predictor == 3) {
-		sp->encodepfunc = fpDiff;
-		/*
-		 * Override default encoding method with one that does the
-		 * predictor stuff.
-		 */
-                if( tif->tif_encoderow != PredictorEncodeRow )
-                {
-                    sp->encoderow = tif->tif_encoderow;
-                    tif->tif_encoderow = PredictorEncodeRow;
-                    sp->encodestrip = tif->tif_encodestrip;
-                    tif->tif_encodestrip = PredictorEncodeTile;
-                    sp->encodetile = tif->tif_encodetile;
-                    tif->tif_encodetile = PredictorEncodeTile;
-                }
-	}
+        /*
+         * If the data is horizontally differenced 16-bit data that
+         * requires byte-swapping, then it must be byte swapped before
+         * the accumulation step.  We do this with a special-purpose
+         * routine and override the normal post decoding logic that
+         * the library setup when the directory was read.
+         */
+        if (tif->tif_flags & TIFF_SWAB)
+        {
+            if (sp->decodepfunc == horAcc16)
+            {
+                sp->decodepfunc = swabHorAcc16;
+                tif->tif_postdecode = _TIFFNoPostDecode;
+            }
+            else if (sp->decodepfunc == horAcc32)
+            {
+                sp->decodepfunc = swabHorAcc32;
+                tif->tif_postdecode = _TIFFNoPostDecode;
+            }
+            else if (sp->decodepfunc == horAcc64)
+            {
+                sp->decodepfunc = swabHorAcc64;
+                tif->tif_postdecode = _TIFFNoPostDecode;
+            }
+        }
+    }
 
-	return 1;
+    else if (sp->predictor == 3)
+    {
+        sp->decodepfunc = fpAcc;
+        /*
+         * Override default decoding method with one that does the
+         * predictor stuff.
+         */
+        if (tif->tif_decoderow != PredictorDecodeRow)
+        {
+            sp->decoderow = tif->tif_decoderow;
+            tif->tif_decoderow = PredictorDecodeRow;
+            sp->decodestrip = tif->tif_decodestrip;
+            tif->tif_decodestrip = PredictorDecodeTile;
+            sp->decodetile = tif->tif_decodetile;
+            tif->tif_decodetile = PredictorDecodeTile;
+        }
+        /*
+         * The data should not be swapped outside of the floating
+         * point predictor, the accumulation routine should return
+         * byres in the native order.
+         */
+        if (tif->tif_flags & TIFF_SWAB)
+        {
+            tif->tif_postdecode = _TIFFNoPostDecode;
+        }
+        /*
+         * Allocate buffer to keep the decoded bytes before
+         * rearranging in the right order
+         */
+    }
+
+    return 1;
 }
 
-#define REPEAT4(n, op)		\
-    switch (n) {		\
-    default: { \
-        tmsize_t i; for (i = n-4; i > 0; i--) { op; } }  /*-fallthrough*/  \
-    case 4:  op; /*-fallthrough*/ \
-    case 3:  op; /*-fallthrough*/ \
-    case 2:  op; /*-fallthrough*/ \
-    case 1:  op; /*-fallthrough*/ \
-    case 0:  ;			\
+static int PredictorSetupEncode(TIFF *tif)
+{
+    TIFFPredictorState *sp = PredictorState(tif);
+    TIFFDirectory *td = &tif->tif_dir;
+
+    if (!(*sp->setupencode)(tif) || !PredictorSetup(tif))
+        return 0;
+
+    if (sp->predictor == 2)
+    {
+        switch (td->td_bitspersample)
+        {
+            case 8:
+                sp->encodepfunc = horDiff8;
+                break;
+            case 16:
+                sp->encodepfunc = horDiff16;
+                break;
+            case 32:
+                sp->encodepfunc = horDiff32;
+                break;
+            case 64:
+                sp->encodepfunc = horDiff64;
+                break;
+        }
+        /*
+         * Override default encoding method with one that does the
+         * predictor stuff.
+         */
+        if (tif->tif_encoderow != PredictorEncodeRow)
+        {
+            sp->encoderow = tif->tif_encoderow;
+            tif->tif_encoderow = PredictorEncodeRow;
+            sp->encodestrip = tif->tif_encodestrip;
+            tif->tif_encodestrip = PredictorEncodeTile;
+            sp->encodetile = tif->tif_encodetile;
+            tif->tif_encodetile = PredictorEncodeTile;
+        }
+
+        /*
+         * If the data is horizontally differenced 16-bit data that
+         * requires byte-swapping, then it must be byte swapped after
+         * the differentiation step.  We do this with a special-purpose
+         * routine and override the normal post decoding logic that
+         * the library setup when the directory was read.
+         */
+        if (tif->tif_flags & TIFF_SWAB)
+        {
+            if (sp->encodepfunc == horDiff16)
+            {
+                sp->encodepfunc = swabHorDiff16;
+                tif->tif_postdecode = _TIFFNoPostDecode;
+            }
+            else if (sp->encodepfunc == horDiff32)
+            {
+                sp->encodepfunc = swabHorDiff32;
+                tif->tif_postdecode = _TIFFNoPostDecode;
+            }
+            else if (sp->encodepfunc == horDiff64)
+            {
+                sp->encodepfunc = swabHorDiff64;
+                tif->tif_postdecode = _TIFFNoPostDecode;
+            }
+        }
+    }
+
+    else if (sp->predictor == 3)
+    {
+        sp->encodepfunc = fpDiff;
+        /*
+         * Override default encoding method with one that does the
+         * predictor stuff.
+         */
+        if (tif->tif_encoderow != PredictorEncodeRow)
+        {
+            sp->encoderow = tif->tif_encoderow;
+            tif->tif_encoderow = PredictorEncodeRow;
+            sp->encodestrip = tif->tif_encodestrip;
+            tif->tif_encodestrip = PredictorEncodeTile;
+            sp->encodetile = tif->tif_encodetile;
+            tif->tif_encodetile = PredictorEncodeTile;
+        }
+    }
+
+    return 1;
+}
+
+#define REPEAT4(n, op)                                                         \
+    switch (n)                                                                 \
+    {                                                                          \
+        default:                                                               \
+        {                                                                      \
+            tmsize_t i;                                                        \
+            for (i = n - 4; i > 0; i--)                                        \
+            {                                                                  \
+                op;                                                            \
+            }                                                                  \
+        } /*-fallthrough*/                                                     \
+        case 4:                                                                \
+            op; /*-fallthrough*/                                               \
+        case 3:                                                                \
+            op; /*-fallthrough*/                                               \
+        case 2:                                                                \
+            op; /*-fallthrough*/                                               \
+        case 1:                                                                \
+            op; /*-fallthrough*/                                               \
+        case 0:;                                                               \
     }
 
 /* Remarks related to C standard compliance in all below functions : */
-/* - to avoid any undefined behaviour, we only operate on unsigned types */
-/*   since the behaviour of "overflows" is defined (wrap over) */
+/* - to avoid any undefined behavior, we only operate on unsigned types */
+/*   since the behavior of "overflows" is defined (wrap over) */
 /* - when storing into the byte stream, we explicitly mask with 0xff so */
 /*   as to make icc -check=conversions happy (not necessary by the standard) */
 
 TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
-static int
-horAcc8(TIFF* tif, uint8* cp0, tmsize_t cc)
+static int horAcc8(TIFF *tif, uint8_t *cp0, tmsize_t cc)
 {
-	tmsize_t stride = PredictorState(tif)->stride;
+    tmsize_t stride = PredictorState(tif)->stride;
 
-	unsigned char* cp = (unsigned char*) cp0;
-    if((cc%stride)!=0)
+    unsigned char *cp = (unsigned char *)cp0;
+    if ((cc % stride) != 0)
     {
-        TIFFErrorExt(tif->tif_clientdata, "horAcc8",
-                     "%s", "(cc%stride)!=0");
+        TIFFErrorExtR(tif, "horAcc8", "%s", "(cc%stride)!=0");
         return 0;
     }
 
-	if (cc > stride) {
-		/*
-		 * Pipeline the most common cases.
-		 */
-		if (stride == 3)  {
-			unsigned int cr = cp[0];
-			unsigned int cg = cp[1];
-			unsigned int cb = cp[2];
-			cc -= 3;
-			cp += 3;
-			while (cc>0) {
-				cp[0] = (unsigned char) ((cr += cp[0]) & 0xff);
-				cp[1] = (unsigned char) ((cg += cp[1]) & 0xff);
-				cp[2] = (unsigned char) ((cb += cp[2]) & 0xff);
-				cc -= 3;
-				cp += 3;
-			}
-		} else if (stride == 4)  {
-			unsigned int cr = cp[0];
-			unsigned int cg = cp[1];
-			unsigned int cb = cp[2];
-			unsigned int ca = cp[3];
-			cc -= 4;
-			cp += 4;
-			while (cc>0) {
-				cp[0] = (unsigned char) ((cr += cp[0]) & 0xff);
-				cp[1] = (unsigned char) ((cg += cp[1]) & 0xff);
-				cp[2] = (unsigned char) ((cb += cp[2]) & 0xff);
-				cp[3] = (unsigned char) ((ca += cp[3]) & 0xff);
-				cc -= 4;
-				cp += 4;
-			}
-		} else  {
-			cc -= stride;
-			do {
-				REPEAT4(stride, cp[stride] =
-					(unsigned char) ((cp[stride] + *cp) & 0xff); cp++)
-				cc -= stride;
-			} while (cc>0);
-		}
-	}
-	return 1;
+    if (cc > stride)
+    {
+        /*
+         * Pipeline the most common cases.
+         */
+        if (stride == 3)
+        {
+            unsigned int cr = cp[0];
+            unsigned int cg = cp[1];
+            unsigned int cb = cp[2];
+            tmsize_t i = stride;
+            for (; i < cc; i += stride)
+            {
+                cp[i + 0] = (unsigned char)((cr += cp[i + 0]) & 0xff);
+                cp[i + 1] = (unsigned char)((cg += cp[i + 1]) & 0xff);
+                cp[i + 2] = (unsigned char)((cb += cp[i + 2]) & 0xff);
+            }
+        }
+        else if (stride == 4)
+        {
+            unsigned int cr = cp[0];
+            unsigned int cg = cp[1];
+            unsigned int cb = cp[2];
+            unsigned int ca = cp[3];
+            tmsize_t i = stride;
+            for (; i < cc; i += stride)
+            {
+                cp[i + 0] = (unsigned char)((cr += cp[i + 0]) & 0xff);
+                cp[i + 1] = (unsigned char)((cg += cp[i + 1]) & 0xff);
+                cp[i + 2] = (unsigned char)((cb += cp[i + 2]) & 0xff);
+                cp[i + 3] = (unsigned char)((ca += cp[i + 3]) & 0xff);
+            }
+        }
+        else
+        {
+            cc -= stride;
+            do
+            {
+                REPEAT4(stride,
+                        cp[stride] = (unsigned char)((cp[stride] + *cp) & 0xff);
+                        cp++)
+                cc -= stride;
+            } while (cc > 0);
+        }
+    }
+    return 1;
 }
 
-static int
-swabHorAcc16(TIFF* tif, uint8* cp0, tmsize_t cc)
+static int swabHorAcc16(TIFF *tif, uint8_t *cp0, tmsize_t cc)
 {
-	uint16* wp = (uint16*) cp0;
-	tmsize_t wc = cc / 2;
+    uint16_t *wp = (uint16_t *)cp0;
+    tmsize_t wc = cc / 2;
 
-        TIFFSwabArrayOfShort(wp, wc);
-        return horAcc16(tif, cp0, cc);
+    TIFFSwabArrayOfShort(wp, wc);
+    return horAcc16(tif, cp0, cc);
 }
 
 TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
-static int
-horAcc16(TIFF* tif, uint8* cp0, tmsize_t cc)
+static int horAcc16(TIFF *tif, uint8_t *cp0, tmsize_t cc)
 {
-	tmsize_t stride = PredictorState(tif)->stride;
-	uint16* wp = (uint16*) cp0;
-	tmsize_t wc = cc / 2;
+    tmsize_t stride = PredictorState(tif)->stride;
+    uint16_t *wp = (uint16_t *)cp0;
+    tmsize_t wc = cc / 2;
 
-    if((cc%(2*stride))!=0)
+    if ((cc % (2 * stride)) != 0)
     {
-        TIFFErrorExt(tif->tif_clientdata, "horAcc16",
-                     "%s", "cc%(2*stride))!=0");
+        TIFFErrorExtR(tif, "horAcc16", "%s", "cc%(2*stride))!=0");
         return 0;
     }
 
-	if (wc > stride) {
-		wc -= stride;
-		do {
-			REPEAT4(stride, wp[stride] = (uint16)(((unsigned int)wp[stride] + (unsigned int)wp[0]) & 0xffff); wp++)
-			wc -= stride;
-		} while (wc > 0);
-	}
-	return 1;
+    if (wc > stride)
+    {
+        wc -= stride;
+        do
+        {
+            REPEAT4(stride, wp[stride] = (uint16_t)(((unsigned int)wp[stride] +
+                                                     (unsigned int)wp[0]) &
+                                                    0xffff);
+                    wp++)
+            wc -= stride;
+        } while (wc > 0);
+    }
+    return 1;
 }
 
-static int
-swabHorAcc32(TIFF* tif, uint8* cp0, tmsize_t cc)
+static int swabHorAcc32(TIFF *tif, uint8_t *cp0, tmsize_t cc)
 {
-	uint32* wp = (uint32*) cp0;
-	tmsize_t wc = cc / 4;
+    uint32_t *wp = (uint32_t *)cp0;
+    tmsize_t wc = cc / 4;
 
-        TIFFSwabArrayOfLong(wp, wc);
-	return horAcc32(tif, cp0, cc);
+    TIFFSwabArrayOfLong(wp, wc);
+    return horAcc32(tif, cp0, cc);
 }
 
 TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
-static int
-horAcc32(TIFF* tif, uint8* cp0, tmsize_t cc)
+static int horAcc32(TIFF *tif, uint8_t *cp0, tmsize_t cc)
 {
-	tmsize_t stride = PredictorState(tif)->stride;
-	uint32* wp = (uint32*) cp0;
-	tmsize_t wc = cc / 4;
+    tmsize_t stride = PredictorState(tif)->stride;
+    uint32_t *wp = (uint32_t *)cp0;
+    tmsize_t wc = cc / 4;
 
-    if((cc%(4*stride))!=0)
+    if ((cc % (4 * stride)) != 0)
     {
-        TIFFErrorExt(tif->tif_clientdata, "horAcc32",
-                     "%s", "cc%(4*stride))!=0");
+        TIFFErrorExtR(tif, "horAcc32", "%s", "cc%(4*stride))!=0");
         return 0;
     }
 
-	if (wc > stride) {
-		wc -= stride;
-		do {
-			REPEAT4(stride, wp[stride] += wp[0]; wp++)
-			wc -= stride;
-		} while (wc > 0);
-	}
-	return 1;
+    if (wc > stride)
+    {
+        wc -= stride;
+        do
+        {
+            REPEAT4(stride, wp[stride] += wp[0]; wp++)
+            wc -= stride;
+        } while (wc > 0);
+    }
+    return 1;
+}
+
+static int swabHorAcc64(TIFF *tif, uint8_t *cp0, tmsize_t cc)
+{
+    uint64_t *wp = (uint64_t *)cp0;
+    tmsize_t wc = cc / 8;
+
+    TIFFSwabArrayOfLong8(wp, wc);
+    return horAcc64(tif, cp0, cc);
+}
+
+TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
+static int horAcc64(TIFF *tif, uint8_t *cp0, tmsize_t cc)
+{
+    tmsize_t stride = PredictorState(tif)->stride;
+    uint64_t *wp = (uint64_t *)cp0;
+    tmsize_t wc = cc / 8;
+
+    if ((cc % (8 * stride)) != 0)
+    {
+        TIFFErrorExtR(tif, "horAcc64", "%s", "cc%(8*stride))!=0");
+        return 0;
+    }
+
+    if (wc > stride)
+    {
+        wc -= stride;
+        do
+        {
+            REPEAT4(stride, wp[stride] += wp[0]; wp++)
+            wc -= stride;
+        } while (wc > 0);
+    }
+    return 1;
 }
 
 /*
  * Floating point predictor accumulation routine.
  */
-static int
-fpAcc(TIFF* tif, uint8* cp0, tmsize_t cc)
+static int fpAcc(TIFF *tif, uint8_t *cp0, tmsize_t cc)
 {
-	tmsize_t stride = PredictorState(tif)->stride;
-	uint32 bps = tif->tif_dir.td_bitspersample / 8;
-	tmsize_t wc = cc / bps;
-	tmsize_t count = cc;
-	uint8 *cp = (uint8 *) cp0;
-	uint8 *tmp;
+    tmsize_t stride = PredictorState(tif)->stride;
+    uint32_t bps = tif->tif_dir.td_bitspersample / 8;
+    tmsize_t wc = cc / bps;
+    tmsize_t count = cc;
+    uint8_t *cp = (uint8_t *)cp0;
+    uint8_t *tmp;
 
-    if(cc%(bps*stride)!=0)
+    if (cc % (bps * stride) != 0)
     {
-        TIFFErrorExt(tif->tif_clientdata, "fpAcc",
-                     "%s", "cc%(bps*stride))!=0");
+        TIFFErrorExtR(tif, "fpAcc", "%s", "cc%(bps*stride))!=0");
         return 0;
     }
 
-    tmp = (uint8 *)_TIFFmalloc(cc);
-	if (!tmp)
-		return 0;
+    tmp = (uint8_t *)_TIFFmallocExt(tif, cc);
+    if (!tmp)
+        return 0;
 
-	while (count > stride) {
-		REPEAT4(stride, cp[stride] =
-                        (unsigned char) ((cp[stride] + cp[0]) & 0xff); cp++)
-		count -= stride;
-	}
+    while (count > stride)
+    {
+        REPEAT4(stride,
+                cp[stride] = (unsigned char)((cp[stride] + cp[0]) & 0xff);
+                cp++)
+        count -= stride;
+    }
 
-	_TIFFmemcpy(tmp, cp0, cc);
-	cp = (uint8 *) cp0;
-	for (count = 0; count < wc; count++) {
-		uint32 byte;
-		for (byte = 0; byte < bps; byte++) {
-			#if WORDS_BIGENDIAN
-			cp[bps * count + byte] = tmp[byte * wc + count];
-			#else
-			cp[bps * count + byte] =
-				tmp[(bps - byte - 1) * wc + count];
-			#endif
-		}
-	}
-	_TIFFfree(tmp);
+    _TIFFmemcpy(tmp, cp0, cc);
+    cp = (uint8_t *)cp0;
+    for (count = 0; count < wc; count++)
+    {
+        uint32_t byte;
+        for (byte = 0; byte < bps; byte++)
+        {
+#if WORDS_BIGENDIAN
+            cp[bps * count + byte] = tmp[byte * wc + count];
+#else
+            cp[bps * count + byte] = tmp[(bps - byte - 1) * wc + count];
+#endif
+        }
+    }
+    _TIFFfreeExt(tif, tmp);
     return 1;
 }
 
 /*
  * Decode a scanline and apply the predictor routine.
  */
-static int
-PredictorDecodeRow(TIFF* tif, uint8* op0, tmsize_t occ0, uint16 s)
+static int PredictorDecodeRow(TIFF *tif, uint8_t *op0, tmsize_t occ0,
+                              uint16_t s)
 {
-	TIFFPredictorState *sp = PredictorState(tif);
+    TIFFPredictorState *sp = PredictorState(tif);
 
-	assert(sp != NULL);
-	assert(sp->decoderow != NULL);
-	assert(sp->decodepfunc != NULL);  
+    assert(sp != NULL);
+    assert(sp->decoderow != NULL);
+    assert(sp->decodepfunc != NULL);
 
-	if ((*sp->decoderow)(tif, op0, occ0, s)) {
-		return (*sp->decodepfunc)(tif, op0, occ0);
-	} else
-		return 0;
+    if ((*sp->decoderow)(tif, op0, occ0, s))
+    {
+        return (*sp->decodepfunc)(tif, op0, occ0);
+    }
+    else
+        return 0;
 }
 
 /*
@@ -475,123 +578,152 @@
  * been calculated at pre-decode time according to the
  * strip/tile dimensions.
  */
-static int
-PredictorDecodeTile(TIFF* tif, uint8* op0, tmsize_t occ0, uint16 s)
+static int PredictorDecodeTile(TIFF *tif, uint8_t *op0, tmsize_t occ0,
+                               uint16_t s)
 {
-	TIFFPredictorState *sp = PredictorState(tif);
+    TIFFPredictorState *sp = PredictorState(tif);
 
-	assert(sp != NULL);
-	assert(sp->decodetile != NULL);
+    assert(sp != NULL);
+    assert(sp->decodetile != NULL);
 
-	if ((*sp->decodetile)(tif, op0, occ0, s)) {
-		tmsize_t rowsize = sp->rowsize;
-		assert(rowsize > 0);
-		if((occ0%rowsize) !=0)
+    if ((*sp->decodetile)(tif, op0, occ0, s))
+    {
+        tmsize_t rowsize = sp->rowsize;
+        assert(rowsize > 0);
+        if ((occ0 % rowsize) != 0)
         {
-            TIFFErrorExt(tif->tif_clientdata, "PredictorDecodeTile",
-                         "%s", "occ0%rowsize != 0");
+            TIFFErrorExtR(tif, "PredictorDecodeTile", "%s",
+                          "occ0%rowsize != 0");
             return 0;
         }
-		assert(sp->decodepfunc != NULL);
-		while (occ0 > 0) {
-			if( !(*sp->decodepfunc)(tif, op0, rowsize) )
+        assert(sp->decodepfunc != NULL);
+        while (occ0 > 0)
+        {
+            if (!(*sp->decodepfunc)(tif, op0, rowsize))
                 return 0;
-			occ0 -= rowsize;
-			op0 += rowsize;
-		}
-		return 1;
-	} else
-		return 0;
+            occ0 -= rowsize;
+            op0 += rowsize;
+        }
+        return 1;
+    }
+    else
+        return 0;
 }
 
 TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
-static int
-horDiff8(TIFF* tif, uint8* cp0, tmsize_t cc)
+static int horDiff8(TIFF *tif, uint8_t *cp0, tmsize_t cc)
 {
-	TIFFPredictorState* sp = PredictorState(tif);
-	tmsize_t stride = sp->stride;
-	unsigned char* cp = (unsigned char*) cp0;
+    TIFFPredictorState *sp = PredictorState(tif);
+    tmsize_t stride = sp->stride;
+    unsigned char *cp = (unsigned char *)cp0;
 
-    if((cc%stride)!=0)
+    if ((cc % stride) != 0)
     {
-        TIFFErrorExt(tif->tif_clientdata, "horDiff8",
-                     "%s", "(cc%stride)!=0");
+        TIFFErrorExtR(tif, "horDiff8", "%s", "(cc%stride)!=0");
         return 0;
     }
 
-	if (cc > stride) {
-		cc -= stride;
-		/*
-		 * Pipeline the most common cases.
-		 */
-		if (stride == 3) {
-			unsigned int r1, g1, b1;
-			unsigned int r2 = cp[0];
-			unsigned int g2 = cp[1];
-			unsigned  int b2 = cp[2];
-			do {
-				r1 = cp[3]; cp[3] = (unsigned char)((r1-r2)&0xff); r2 = r1;
-				g1 = cp[4]; cp[4] = (unsigned char)((g1-g2)&0xff); g2 = g1;
-				b1 = cp[5]; cp[5] = (unsigned char)((b1-b2)&0xff); b2 = b1;
-				cp += 3;
-			} while ((cc -= 3) > 0);
-		} else if (stride == 4) {
-			unsigned int r1, g1, b1, a1;
-			unsigned int r2 = cp[0];
-			unsigned int g2 = cp[1];
-			unsigned int b2 = cp[2];
-			unsigned int a2 = cp[3];
-			do {
-				r1 = cp[4]; cp[4] = (unsigned char)((r1-r2)&0xff); r2 = r1;
-				g1 = cp[5]; cp[5] = (unsigned char)((g1-g2)&0xff); g2 = g1;
-				b1 = cp[6]; cp[6] = (unsigned char)((b1-b2)&0xff); b2 = b1;
-				a1 = cp[7]; cp[7] = (unsigned char)((a1-a2)&0xff); a2 = a1;
-				cp += 4;
-			} while ((cc -= 4) > 0);
-		} else {
-			cp += cc - 1;
-			do {
-				REPEAT4(stride, cp[stride] = (unsigned char)((cp[stride] - cp[0])&0xff); cp--)
-			} while ((cc -= stride) > 0);
-		}
-	}
-	return 1;
+    if (cc > stride)
+    {
+        cc -= stride;
+        /*
+         * Pipeline the most common cases.
+         */
+        if (stride == 3)
+        {
+            unsigned int r1, g1, b1;
+            unsigned int r2 = cp[0];
+            unsigned int g2 = cp[1];
+            unsigned int b2 = cp[2];
+            do
+            {
+                r1 = cp[3];
+                cp[3] = (unsigned char)((r1 - r2) & 0xff);
+                r2 = r1;
+                g1 = cp[4];
+                cp[4] = (unsigned char)((g1 - g2) & 0xff);
+                g2 = g1;
+                b1 = cp[5];
+                cp[5] = (unsigned char)((b1 - b2) & 0xff);
+                b2 = b1;
+                cp += 3;
+            } while ((cc -= 3) > 0);
+        }
+        else if (stride == 4)
+        {
+            unsigned int r1, g1, b1, a1;
+            unsigned int r2 = cp[0];
+            unsigned int g2 = cp[1];
+            unsigned int b2 = cp[2];
+            unsigned int a2 = cp[3];
+            do
+            {
+                r1 = cp[4];
+                cp[4] = (unsigned char)((r1 - r2) & 0xff);
+                r2 = r1;
+                g1 = cp[5];
+                cp[5] = (unsigned char)((g1 - g2) & 0xff);
+                g2 = g1;
+                b1 = cp[6];
+                cp[6] = (unsigned char)((b1 - b2) & 0xff);
+                b2 = b1;
+                a1 = cp[7];
+                cp[7] = (unsigned char)((a1 - a2) & 0xff);
+                a2 = a1;
+                cp += 4;
+            } while ((cc -= 4) > 0);
+        }
+        else
+        {
+            cp += cc - 1;
+            do
+            {
+                REPEAT4(stride,
+                        cp[stride] =
+                            (unsigned char)((cp[stride] - cp[0]) & 0xff);
+                        cp--)
+            } while ((cc -= stride) > 0);
+        }
+    }
+    return 1;
 }
 
 TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
-static int
-horDiff16(TIFF* tif, uint8* cp0, tmsize_t cc)
+static int horDiff16(TIFF *tif, uint8_t *cp0, tmsize_t cc)
 {
-	TIFFPredictorState* sp = PredictorState(tif);
-	tmsize_t stride = sp->stride;
-	uint16 *wp = (uint16*) cp0;
-	tmsize_t wc = cc/2;
-
-    if((cc%(2*stride))!=0)
-    {
-        TIFFErrorExt(tif->tif_clientdata, "horDiff8",
-                     "%s", "(cc%(2*stride))!=0");
-        return 0;
-    }
-
-	if (wc > stride) {
-		wc -= stride;
-		wp += wc - 1;
-		do {
-			REPEAT4(stride, wp[stride] = (uint16)(((unsigned int)wp[stride] - (unsigned int)wp[0]) & 0xffff); wp--)
-			wc -= stride;
-		} while (wc > 0);
-	}
-	return 1;
-}
-
-static int
-swabHorDiff16(TIFF* tif, uint8* cp0, tmsize_t cc)
-{
-    uint16* wp = (uint16*) cp0;
+    TIFFPredictorState *sp = PredictorState(tif);
+    tmsize_t stride = sp->stride;
+    uint16_t *wp = (uint16_t *)cp0;
     tmsize_t wc = cc / 2;
 
-    if( !horDiff16(tif, cp0, cc) )
+    if ((cc % (2 * stride)) != 0)
+    {
+        TIFFErrorExtR(tif, "horDiff8", "%s", "(cc%(2*stride))!=0");
+        return 0;
+    }
+
+    if (wc > stride)
+    {
+        wc -= stride;
+        wp += wc - 1;
+        do
+        {
+            REPEAT4(stride, wp[stride] = (uint16_t)(((unsigned int)wp[stride] -
+                                                     (unsigned int)wp[0]) &
+                                                    0xffff);
+                    wp--)
+            wc -= stride;
+        } while (wc > 0);
+    }
+    return 1;
+}
+
+static int swabHorDiff16(TIFF *tif, uint8_t *cp0, tmsize_t cc)
+{
+    uint16_t *wp = (uint16_t *)cp0;
+    tmsize_t wc = cc / 2;
+
+    if (!horDiff16(tif, cp0, cc))
         return 0;
 
     TIFFSwabArrayOfShort(wp, wc);
@@ -599,281 +731,316 @@
 }
 
 TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
-static int
-horDiff32(TIFF* tif, uint8* cp0, tmsize_t cc)
+static int horDiff32(TIFF *tif, uint8_t *cp0, tmsize_t cc)
 {
-	TIFFPredictorState* sp = PredictorState(tif);
-	tmsize_t stride = sp->stride;
-	uint32 *wp = (uint32*) cp0;
-	tmsize_t wc = cc/4;
+    TIFFPredictorState *sp = PredictorState(tif);
+    tmsize_t stride = sp->stride;
+    uint32_t *wp = (uint32_t *)cp0;
+    tmsize_t wc = cc / 4;
 
-    if((cc%(4*stride))!=0)
+    if ((cc % (4 * stride)) != 0)
     {
-        TIFFErrorExt(tif->tif_clientdata, "horDiff32",
-                     "%s", "(cc%(4*stride))!=0");
+        TIFFErrorExtR(tif, "horDiff32", "%s", "(cc%(4*stride))!=0");
         return 0;
     }
 
-	if (wc > stride) {
-		wc -= stride;
-		wp += wc - 1;
-		do {
-			REPEAT4(stride, wp[stride] -= wp[0]; wp--)
-			wc -= stride;
-		} while (wc > 0);
-	}
-	return 1;
+    if (wc > stride)
+    {
+        wc -= stride;
+        wp += wc - 1;
+        do
+        {
+            REPEAT4(stride, wp[stride] -= wp[0]; wp--)
+            wc -= stride;
+        } while (wc > 0);
+    }
+    return 1;
 }
 
-static int
-swabHorDiff32(TIFF* tif, uint8* cp0, tmsize_t cc)
+static int swabHorDiff32(TIFF *tif, uint8_t *cp0, tmsize_t cc)
 {
-    uint32* wp = (uint32*) cp0;
+    uint32_t *wp = (uint32_t *)cp0;
     tmsize_t wc = cc / 4;
 
-    if( !horDiff32(tif, cp0, cc) )
+    if (!horDiff32(tif, cp0, cc))
         return 0;
 
     TIFFSwabArrayOfLong(wp, wc);
     return 1;
 }
 
+TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
+static int horDiff64(TIFF *tif, uint8_t *cp0, tmsize_t cc)
+{
+    TIFFPredictorState *sp = PredictorState(tif);
+    tmsize_t stride = sp->stride;
+    uint64_t *wp = (uint64_t *)cp0;
+    tmsize_t wc = cc / 8;
+
+    if ((cc % (8 * stride)) != 0)
+    {
+        TIFFErrorExtR(tif, "horDiff64", "%s", "(cc%(8*stride))!=0");
+        return 0;
+    }
+
+    if (wc > stride)
+    {
+        wc -= stride;
+        wp += wc - 1;
+        do
+        {
+            REPEAT4(stride, wp[stride] -= wp[0]; wp--)
+            wc -= stride;
+        } while (wc > 0);
+    }
+    return 1;
+}
+
+static int swabHorDiff64(TIFF *tif, uint8_t *cp0, tmsize_t cc)
+{
+    uint64_t *wp = (uint64_t *)cp0;
+    tmsize_t wc = cc / 8;
+
+    if (!horDiff64(tif, cp0, cc))
+        return 0;
+
+    TIFFSwabArrayOfLong8(wp, wc);
+    return 1;
+}
+
 /*
  * Floating point predictor differencing routine.
  */
 TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
-static int
-fpDiff(TIFF* tif, uint8* cp0, tmsize_t cc)
+static int fpDiff(TIFF *tif, uint8_t *cp0, tmsize_t cc)
 {
-	tmsize_t stride = PredictorState(tif)->stride;
-	uint32 bps = tif->tif_dir.td_bitspersample / 8;
-	tmsize_t wc = cc / bps;
-	tmsize_t count;
-	uint8 *cp = (uint8 *) cp0;
-	uint8 *tmp;
+    tmsize_t stride = PredictorState(tif)->stride;
+    uint32_t bps = tif->tif_dir.td_bitspersample / 8;
+    tmsize_t wc = cc / bps;
+    tmsize_t count;
+    uint8_t *cp = (uint8_t *)cp0;
+    uint8_t *tmp;
 
-    if((cc%(bps*stride))!=0)
+    if ((cc % (bps * stride)) != 0)
     {
-        TIFFErrorExt(tif->tif_clientdata, "fpDiff",
-                     "%s", "(cc%(bps*stride))!=0");
+        TIFFErrorExtR(tif, "fpDiff", "%s", "(cc%(bps*stride))!=0");
         return 0;
     }
 
-    tmp = (uint8 *)_TIFFmalloc(cc);
-	if (!tmp)
-		return 0;
+    tmp = (uint8_t *)_TIFFmallocExt(tif, cc);
+    if (!tmp)
+        return 0;
 
-	_TIFFmemcpy(tmp, cp0, cc);
-	for (count = 0; count < wc; count++) {
-		uint32 byte;
-		for (byte = 0; byte < bps; byte++) {
-			#if WORDS_BIGENDIAN
-			cp[byte * wc + count] = tmp[bps * count + byte];
-			#else
-			cp[(bps - byte - 1) * wc + count] =
-				tmp[bps * count + byte];
-			#endif
-		}
-	}
-	_TIFFfree(tmp);
+    _TIFFmemcpy(tmp, cp0, cc);
+    for (count = 0; count < wc; count++)
+    {
+        uint32_t byte;
+        for (byte = 0; byte < bps; byte++)
+        {
+#if WORDS_BIGENDIAN
+            cp[byte * wc + count] = tmp[bps * count + byte];
+#else
+            cp[(bps - byte - 1) * wc + count] = tmp[bps * count + byte];
+#endif
+        }
+    }
+    _TIFFfreeExt(tif, tmp);
 
-	cp = (uint8 *) cp0;
-	cp += cc - stride - 1;
-	for (count = cc; count > stride; count -= stride)
-		REPEAT4(stride, cp[stride] = (unsigned char)((cp[stride] - cp[0])&0xff); cp--)
+    cp = (uint8_t *)cp0;
+    cp += cc - stride - 1;
+    for (count = cc; count > stride; count -= stride)
+        REPEAT4(stride,
+                cp[stride] = (unsigned char)((cp[stride] - cp[0]) & 0xff);
+                cp--)
     return 1;
 }
 
-static int
-PredictorEncodeRow(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s)
+static int PredictorEncodeRow(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s)
 {
-	TIFFPredictorState *sp = PredictorState(tif);
+    TIFFPredictorState *sp = PredictorState(tif);
 
-	assert(sp != NULL);
-	assert(sp->encodepfunc != NULL);
-	assert(sp->encoderow != NULL);
+    assert(sp != NULL);
+    assert(sp->encodepfunc != NULL);
+    assert(sp->encoderow != NULL);
 
-	/* XXX horizontal differencing alters user's data XXX */
-	if( !(*sp->encodepfunc)(tif, bp, cc) )
+    /* XXX horizontal differencing alters user's data XXX */
+    if (!(*sp->encodepfunc)(tif, bp, cc))
         return 0;
-	return (*sp->encoderow)(tif, bp, cc, s);
+    return (*sp->encoderow)(tif, bp, cc, s);
 }
 
-static int
-PredictorEncodeTile(TIFF* tif, uint8* bp0, tmsize_t cc0, uint16 s)
+static int PredictorEncodeTile(TIFF *tif, uint8_t *bp0, tmsize_t cc0,
+                               uint16_t s)
 {
-	static const char module[] = "PredictorEncodeTile";
-	TIFFPredictorState *sp = PredictorState(tif);
-        uint8 *working_copy;
-	tmsize_t cc = cc0, rowsize;
-	unsigned char* bp;
-        int result_code;
+    static const char module[] = "PredictorEncodeTile";
+    TIFFPredictorState *sp = PredictorState(tif);
+    uint8_t *working_copy;
+    tmsize_t cc = cc0, rowsize;
+    unsigned char *bp;
+    int result_code;
 
-	assert(sp != NULL);
-	assert(sp->encodepfunc != NULL);
-	assert(sp->encodetile != NULL);
+    assert(sp != NULL);
+    assert(sp->encodepfunc != NULL);
+    assert(sp->encodetile != NULL);
 
-        /* 
-         * Do predictor manipulation in a working buffer to avoid altering
-         * the callers buffer. http://trac.osgeo.org/gdal/ticket/1965
-         */
-        working_copy = (uint8*) _TIFFmalloc(cc0);
-        if( working_copy == NULL )
-        {
-            TIFFErrorExt(tif->tif_clientdata, module, 
-                         "Out of memory allocating " TIFF_SSIZE_FORMAT " byte temp buffer.",
-                         cc0 );
-            return 0;
-        }
-        memcpy( working_copy, bp0, cc0 );
-        bp = working_copy;
-
-	rowsize = sp->rowsize;
-	assert(rowsize > 0);
-	if((cc0%rowsize)!=0)
+    /*
+     * Do predictor manipulation in a working buffer to avoid altering
+     * the callers buffer. http://trac.osgeo.org/gdal/ticket/1965
+     */
+    working_copy = (uint8_t *)_TIFFmallocExt(tif, cc0);
+    if (working_copy == NULL)
     {
-        TIFFErrorExt(tif->tif_clientdata, "PredictorEncodeTile",
-                     "%s", "(cc0%rowsize)!=0");
-        _TIFFfree( working_copy );
+        TIFFErrorExtR(tif, module,
+                      "Out of memory allocating %" PRId64 " byte temp buffer.",
+                      (int64_t)cc0);
         return 0;
     }
-	while (cc > 0) {
-		(*sp->encodepfunc)(tif, bp, rowsize);
-		cc -= rowsize;
-		bp += rowsize;
-	}
-	result_code = (*sp->encodetile)(tif, working_copy, cc0, s);
+    memcpy(working_copy, bp0, cc0);
+    bp = working_copy;
 
-        _TIFFfree( working_copy );
+    rowsize = sp->rowsize;
+    assert(rowsize > 0);
+    if ((cc0 % rowsize) != 0)
+    {
+        TIFFErrorExtR(tif, "PredictorEncodeTile", "%s", "(cc0%rowsize)!=0");
+        _TIFFfreeExt(tif, working_copy);
+        return 0;
+    }
+    while (cc > 0)
+    {
+        (*sp->encodepfunc)(tif, bp, rowsize);
+        cc -= rowsize;
+        bp += rowsize;
+    }
+    result_code = (*sp->encodetile)(tif, working_copy, cc0, s);
 
-        return result_code;
+    _TIFFfreeExt(tif, working_copy);
+
+    return result_code;
 }
 
-#define	FIELD_PREDICTOR	(FIELD_CODEC+0)		/* XXX */
+#define FIELD_PREDICTOR (FIELD_CODEC + 0) /* XXX */
 
 static const TIFFField predictFields[] = {
-    { TIFFTAG_PREDICTOR, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UINT16, FIELD_PREDICTOR, FALSE, FALSE, "Predictor", NULL },
+    {TIFFTAG_PREDICTOR, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
+     TIFF_SETGET_UINT16, FIELD_PREDICTOR, FALSE, FALSE, "Predictor", NULL},
 };
 
-static int
-PredictorVSetField(TIFF* tif, uint32 tag, va_list ap)
+static int PredictorVSetField(TIFF *tif, uint32_t tag, va_list ap)
 {
-	TIFFPredictorState *sp = PredictorState(tif);
+    TIFFPredictorState *sp = PredictorState(tif);
 
-	assert(sp != NULL);
-	assert(sp->vsetparent != NULL);
+    assert(sp != NULL);
+    assert(sp->vsetparent != NULL);
 
-	switch (tag) {
-	case TIFFTAG_PREDICTOR:
-		sp->predictor = (uint16) va_arg(ap, uint16_vap);
-		TIFFSetFieldBit(tif, FIELD_PREDICTOR);
-		break;
-	default:
-		return (*sp->vsetparent)(tif, tag, ap);
-	}
-	tif->tif_flags |= TIFF_DIRTYDIRECT;
-	return 1;
+    switch (tag)
+    {
+        case TIFFTAG_PREDICTOR:
+            sp->predictor = (uint16_t)va_arg(ap, uint16_vap);
+            TIFFSetFieldBit(tif, FIELD_PREDICTOR);
+            break;
+        default:
+            return (*sp->vsetparent)(tif, tag, ap);
+    }
+    tif->tif_flags |= TIFF_DIRTYDIRECT;
+    return 1;
 }
 
-static int
-PredictorVGetField(TIFF* tif, uint32 tag, va_list ap)
+static int PredictorVGetField(TIFF *tif, uint32_t tag, va_list ap)
 {
-	TIFFPredictorState *sp = PredictorState(tif);
+    TIFFPredictorState *sp = PredictorState(tif);
 
-	assert(sp != NULL);
-	assert(sp->vgetparent != NULL);
+    assert(sp != NULL);
+    assert(sp->vgetparent != NULL);
 
-	switch (tag) {
-	case TIFFTAG_PREDICTOR:
-		*va_arg(ap, uint16*) = (uint16)sp->predictor;
-		break;
-	default:
-		return (*sp->vgetparent)(tif, tag, ap);
-	}
-	return 1;
+    switch (tag)
+    {
+        case TIFFTAG_PREDICTOR:
+            *va_arg(ap, uint16_t *) = (uint16_t)sp->predictor;
+            break;
+        default:
+            return (*sp->vgetparent)(tif, tag, ap);
+    }
+    return 1;
 }
 
-static void
-PredictorPrintDir(TIFF* tif, FILE* fd, long flags)
+static void PredictorPrintDir(TIFF *tif, FILE *fd, long flags)
 {
-	TIFFPredictorState* sp = PredictorState(tif);
+    TIFFPredictorState *sp = PredictorState(tif);
 
-	(void) flags;
-	if (TIFFFieldSet(tif,FIELD_PREDICTOR)) {
-		fprintf(fd, "  Predictor: ");
-		switch (sp->predictor) {
-			case 1: fprintf(fd, "none "); break;
-			case 2: fprintf(fd, "horizontal differencing "); break;
-			case 3: fprintf(fd, "floating point predictor "); break;
-		}
-		fprintf(fd, "%d (0x%x)\n", sp->predictor, sp->predictor);
-	}
-	if (sp->printdir)
-		(*sp->printdir)(tif, fd, flags);
+    (void)flags;
+    if (TIFFFieldSet(tif, FIELD_PREDICTOR))
+    {
+        fprintf(fd, "  Predictor: ");
+        switch (sp->predictor)
+        {
+            case 1:
+                fprintf(fd, "none ");
+                break;
+            case 2:
+                fprintf(fd, "horizontal differencing ");
+                break;
+            case 3:
+                fprintf(fd, "floating point predictor ");
+                break;
+        }
+        fprintf(fd, "%d (0x%x)\n", sp->predictor, sp->predictor);
+    }
+    if (sp->printdir)
+        (*sp->printdir)(tif, fd, flags);
 }
 
-int
-TIFFPredictorInit(TIFF* tif)
+int TIFFPredictorInit(TIFF *tif)
 {
-	TIFFPredictorState* sp = PredictorState(tif);
+    TIFFPredictorState *sp = PredictorState(tif);
 
-	assert(sp != 0);
+    assert(sp != 0);
 
-	/*
-	 * Merge codec-specific tag information.
-	 */
-	if (!_TIFFMergeFields(tif, predictFields,
-			      TIFFArrayCount(predictFields))) {
-		TIFFErrorExt(tif->tif_clientdata, "TIFFPredictorInit",
-		    "Merging Predictor codec-specific tags failed");
-		return 0;
-	}
+    /*
+     * Merge codec-specific tag information.
+     */
+    if (!_TIFFMergeFields(tif, predictFields, TIFFArrayCount(predictFields)))
+    {
+        TIFFErrorExtR(tif, "TIFFPredictorInit",
+                      "Merging Predictor codec-specific tags failed");
+        return 0;
+    }
 
-	/*
-	 * Override parent get/set field methods.
-	 */
-	sp->vgetparent = tif->tif_tagmethods.vgetfield;
-	tif->tif_tagmethods.vgetfield =
-            PredictorVGetField;/* hook for predictor tag */
-	sp->vsetparent = tif->tif_tagmethods.vsetfield;
-	tif->tif_tagmethods.vsetfield =
-	    PredictorVSetField;/* hook for predictor tag */
-	sp->printdir = tif->tif_tagmethods.printdir;
-	tif->tif_tagmethods.printdir =
-            PredictorPrintDir;	/* hook for predictor tag */
+    /*
+     * Override parent get/set field methods.
+     */
+    sp->vgetparent = tif->tif_tagmethods.vgetfield;
+    tif->tif_tagmethods.vgetfield =
+        PredictorVGetField; /* hook for predictor tag */
+    sp->vsetparent = tif->tif_tagmethods.vsetfield;
+    tif->tif_tagmethods.vsetfield =
+        PredictorVSetField; /* hook for predictor tag */
+    sp->printdir = tif->tif_tagmethods.printdir;
+    tif->tif_tagmethods.printdir =
+        PredictorPrintDir; /* hook for predictor tag */
 
-	sp->setupdecode = tif->tif_setupdecode;
-	tif->tif_setupdecode = PredictorSetupDecode;
-	sp->setupencode = tif->tif_setupencode;
-	tif->tif_setupencode = PredictorSetupEncode;
+    sp->setupdecode = tif->tif_setupdecode;
+    tif->tif_setupdecode = PredictorSetupDecode;
+    sp->setupencode = tif->tif_setupencode;
+    tif->tif_setupencode = PredictorSetupEncode;
 
-	sp->predictor = 1;			/* default value */
-	sp->encodepfunc = NULL;			/* no predictor routine */
-	sp->decodepfunc = NULL;			/* no predictor routine */
-	return 1;
+    sp->predictor = 1;      /* default value */
+    sp->encodepfunc = NULL; /* no predictor routine */
+    sp->decodepfunc = NULL; /* no predictor routine */
+    return 1;
 }
 
-int
-TIFFPredictorCleanup(TIFF* tif)
+int TIFFPredictorCleanup(TIFF *tif)
 {
-	TIFFPredictorState* sp = PredictorState(tif);
+    TIFFPredictorState *sp = PredictorState(tif);
 
-	assert(sp != 0);
+    assert(sp != 0);
 
-	tif->tif_tagmethods.vgetfield = sp->vgetparent;
-	tif->tif_tagmethods.vsetfield = sp->vsetparent;
-	tif->tif_tagmethods.printdir = sp->printdir;
-	tif->tif_setupdecode = sp->setupdecode;
-	tif->tif_setupencode = sp->setupencode;
+    tif->tif_tagmethods.vgetfield = sp->vgetparent;
+    tif->tif_tagmethods.vsetfield = sp->vsetparent;
+    tif->tif_tagmethods.printdir = sp->printdir;
+    tif->tif_setupdecode = sp->setupdecode;
+    tif->tif_setupencode = sp->setupencode;
 
-	return 1;
+    return 1;
 }
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_predict.h b/third_party/libtiff/tif_predict.h
index a326b9b..de77328 100644
--- a/third_party/libtiff/tif_predict.h
+++ b/third_party/libtiff/tif_predict.h
@@ -23,7 +23,7 @@
  */
 
 #ifndef _TIFFPREDICT_
-#define	_TIFFPREDICT_
+#define _TIFFPREDICT_
 
 #include "tiffio.h"
 #include "tiffiop.h"
@@ -32,50 +32,43 @@
  * ``Library-private'' Support for the Predictor Tag
  */
 
-typedef int (*TIFFEncodeDecodeMethod)(TIFF* tif, uint8* buf, tmsize_t size);
+typedef int (*TIFFEncodeDecodeMethod)(TIFF *tif, uint8_t *buf, tmsize_t size);
 
 /*
  * Codecs that want to support the Predictor tag must place
  * this structure first in their private state block so that
  * the predictor code can cast tif_data to find its state.
  */
-typedef struct {
-	int             predictor;	/* predictor tag value */
-	tmsize_t        stride;		/* sample stride over data */
-	tmsize_t        rowsize;	/* tile/strip row size */
+typedef struct
+{
+    int predictor;    /* predictor tag value */
+    tmsize_t stride;  /* sample stride over data */
+    tmsize_t rowsize; /* tile/strip row size */
 
-	TIFFCodeMethod  encoderow;	/* parent codec encode/decode row */
-	TIFFCodeMethod  encodestrip;	/* parent codec encode/decode strip */
-	TIFFCodeMethod  encodetile;	/* parent codec encode/decode tile */ 
-	TIFFEncodeDecodeMethod  encodepfunc;	/* horizontal differencer */
+    TIFFCodeMethod encoderow;           /* parent codec encode/decode row */
+    TIFFCodeMethod encodestrip;         /* parent codec encode/decode strip */
+    TIFFCodeMethod encodetile;          /* parent codec encode/decode tile */
+    TIFFEncodeDecodeMethod encodepfunc; /* horizontal differencer */
 
-	TIFFCodeMethod  decoderow;	/* parent codec encode/decode row */
-	TIFFCodeMethod  decodestrip;	/* parent codec encode/decode strip */
-	TIFFCodeMethod  decodetile;	/* parent codec encode/decode tile */ 
-	TIFFEncodeDecodeMethod  decodepfunc;	/* horizontal accumulator */
+    TIFFCodeMethod decoderow;           /* parent codec encode/decode row */
+    TIFFCodeMethod decodestrip;         /* parent codec encode/decode strip */
+    TIFFCodeMethod decodetile;          /* parent codec encode/decode tile */
+    TIFFEncodeDecodeMethod decodepfunc; /* horizontal accumulator */
 
-	TIFFVGetMethod  vgetparent;	/* super-class method */
-	TIFFVSetMethod  vsetparent;	/* super-class method */
-	TIFFPrintMethod printdir;	/* super-class method */
-	TIFFBoolMethod  setupdecode;	/* super-class method */
-	TIFFBoolMethod  setupencode;	/* super-class method */
+    TIFFVGetMethod vgetparent;  /* super-class method */
+    TIFFVSetMethod vsetparent;  /* super-class method */
+    TIFFPrintMethod printdir;   /* super-class method */
+    TIFFBoolMethod setupdecode; /* super-class method */
+    TIFFBoolMethod setupencode; /* super-class method */
 } TIFFPredictorState;
 
 #if defined(__cplusplus)
-extern "C" {
+extern "C"
+{
 #endif
-extern int TIFFPredictorInit(TIFF*);
-extern int TIFFPredictorCleanup(TIFF*);
+    extern int TIFFPredictorInit(TIFF *);
+    extern int TIFFPredictorCleanup(TIFF *);
 #if defined(__cplusplus)
 }
 #endif
 #endif /* _TIFFPREDICT_ */
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_print.c b/third_party/libtiff/tif_print.c
index a073794..2b7fd17 100644
--- a/third_party/libtiff/tif_print.c
+++ b/third_party/libtiff/tif_print.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -32,687 +32,724 @@
 
 #include <ctype.h>
 
-static void
-_TIFFprintAsciiBounded(FILE* fd, const char* cp, size_t max_chars);
+static void _TIFFprintAsciiBounded(FILE *fd, const char *cp, size_t max_chars);
 
-static const char * const photoNames[] = {
-    "min-is-white",				/* PHOTOMETRIC_MINISWHITE */
-    "min-is-black",				/* PHOTOMETRIC_MINISBLACK */
-    "RGB color",				/* PHOTOMETRIC_RGB */
-    "palette color (RGB from colormap)",	/* PHOTOMETRIC_PALETTE */
-    "transparency mask",			/* PHOTOMETRIC_MASK */
-    "separated",				/* PHOTOMETRIC_SEPARATED */
-    "YCbCr",					/* PHOTOMETRIC_YCBCR */
+static const char *const photoNames[] = {
+    "min-is-white",                      /* PHOTOMETRIC_MINISWHITE */
+    "min-is-black",                      /* PHOTOMETRIC_MINISBLACK */
+    "RGB color",                         /* PHOTOMETRIC_RGB */
+    "palette color (RGB from colormap)", /* PHOTOMETRIC_PALETTE */
+    "transparency mask",                 /* PHOTOMETRIC_MASK */
+    "separated",                         /* PHOTOMETRIC_SEPARATED */
+    "YCbCr",                             /* PHOTOMETRIC_YCBCR */
     "7 (0x7)",
-    "CIE L*a*b*",				/* PHOTOMETRIC_CIELAB */
-    "ICC L*a*b*",				/* PHOTOMETRIC_ICCLAB */
-    "ITU L*a*b*" 				/* PHOTOMETRIC_ITULAB */
+    "CIE L*a*b*", /* PHOTOMETRIC_CIELAB */
+    "ICC L*a*b*", /* PHOTOMETRIC_ICCLAB */
+    "ITU L*a*b*"  /* PHOTOMETRIC_ITULAB */
 };
-#define	NPHOTONAMES	(sizeof (photoNames) / sizeof (photoNames[0]))
+#define NPHOTONAMES (sizeof(photoNames) / sizeof(photoNames[0]))
 
-static const char * const orientNames[] = {
+static const char *const orientNames[] = {
     "0 (0x0)",
-    "row 0 top, col 0 lhs",			/* ORIENTATION_TOPLEFT */
-    "row 0 top, col 0 rhs",			/* ORIENTATION_TOPRIGHT */
-    "row 0 bottom, col 0 rhs",			/* ORIENTATION_BOTRIGHT */
-    "row 0 bottom, col 0 lhs",			/* ORIENTATION_BOTLEFT */
-    "row 0 lhs, col 0 top",			/* ORIENTATION_LEFTTOP */
-    "row 0 rhs, col 0 top",			/* ORIENTATION_RIGHTTOP */
-    "row 0 rhs, col 0 bottom",			/* ORIENTATION_RIGHTBOT */
-    "row 0 lhs, col 0 bottom",			/* ORIENTATION_LEFTBOT */
+    "row 0 top, col 0 lhs",    /* ORIENTATION_TOPLEFT */
+    "row 0 top, col 0 rhs",    /* ORIENTATION_TOPRIGHT */
+    "row 0 bottom, col 0 rhs", /* ORIENTATION_BOTRIGHT */
+    "row 0 bottom, col 0 lhs", /* ORIENTATION_BOTLEFT */
+    "row 0 lhs, col 0 top",    /* ORIENTATION_LEFTTOP */
+    "row 0 rhs, col 0 top",    /* ORIENTATION_RIGHTTOP */
+    "row 0 rhs, col 0 bottom", /* ORIENTATION_RIGHTBOT */
+    "row 0 lhs, col 0 bottom", /* ORIENTATION_LEFTBOT */
 };
-#define	NORIENTNAMES	(sizeof (orientNames) / sizeof (orientNames[0]))
+#define NORIENTNAMES (sizeof(orientNames) / sizeof(orientNames[0]))
 
-static void
-_TIFFPrintField(FILE* fd, const TIFFField *fip,
-		uint32 value_count, void *raw_data)
+static const struct tagname
 {
-	uint32 j;
-		
-	fprintf(fd, "  %s: ", fip->field_name);
+    uint16_t tag;
+    const char *name;
+} tagnames[] = {
+    {TIFFTAG_GDAL_METADATA, "GDAL Metadata"},
+    {TIFFTAG_GDAL_NODATA, "GDAL NoDataValue"},
+};
+#define NTAGS (sizeof(tagnames) / sizeof(tagnames[0]))
 
-	for(j = 0; j < value_count; j++) {
-		if(fip->field_type == TIFF_BYTE)
-			fprintf(fd, "%u", ((uint8 *) raw_data)[j]);
-		else if(fip->field_type == TIFF_UNDEFINED)
-			fprintf(fd, "0x%x",
-			    (unsigned int) ((unsigned char *) raw_data)[j]);
-		else if(fip->field_type == TIFF_SBYTE)
-			fprintf(fd, "%d", ((int8 *) raw_data)[j]);
-		else if(fip->field_type == TIFF_SHORT)
-			fprintf(fd, "%u", ((uint16 *) raw_data)[j]);
-		else if(fip->field_type == TIFF_SSHORT)
-			fprintf(fd, "%d", ((int16 *) raw_data)[j]);
-		else if(fip->field_type == TIFF_LONG)
-			fprintf(fd, "%lu",
-			    (unsigned long)((uint32 *) raw_data)[j]);
-		else if(fip->field_type == TIFF_SLONG)
-			fprintf(fd, "%ld", (long)((int32 *) raw_data)[j]);
-		else if(fip->field_type == TIFF_IFD)
-			fprintf(fd, "0x%lx",
-				(unsigned long)((uint32 *) raw_data)[j]);
-		else if(fip->field_type == TIFF_RATIONAL
-			|| fip->field_type == TIFF_SRATIONAL
-			|| fip->field_type == TIFF_FLOAT)
-			fprintf(fd, "%f", ((float *) raw_data)[j]);
-		else if(fip->field_type == TIFF_LONG8)
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-			fprintf(fd, "%I64u",
-			    (unsigned __int64)((uint64 *) raw_data)[j]);
-#else
-			fprintf(fd, "%llu",
-			    (unsigned long long)((uint64 *) raw_data)[j]);
-#endif
-		else if(fip->field_type == TIFF_SLONG8)
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-			fprintf(fd, "%I64d", (__int64)((int64 *) raw_data)[j]);
-#else
-			fprintf(fd, "%lld", (long long)((int64 *) raw_data)[j]);
-#endif
-		else if(fip->field_type == TIFF_IFD8)
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-			fprintf(fd, "0x%I64x",
-				(unsigned __int64)((uint64 *) raw_data)[j]);
-#else
-			fprintf(fd, "0x%llx",
-				(unsigned long long)((uint64 *) raw_data)[j]);
-#endif
-		else if(fip->field_type == TIFF_FLOAT)
-			fprintf(fd, "%f", ((float *)raw_data)[j]);
-		else if(fip->field_type == TIFF_DOUBLE)
-			fprintf(fd, "%f", ((double *) raw_data)[j]);
-		else if(fip->field_type == TIFF_ASCII) {
-			fprintf(fd, "%s", (char *) raw_data);
-			break;
-		}
-		else {
-			fprintf(fd, "<unsupported data type in TIFFPrint>");
-			break;
-		}
+static void _TIFFPrintField(FILE *fd, const TIFFField *fip,
+                            uint32_t value_count, void *raw_data)
+{
+    uint32_t j;
 
-		if(j < value_count - 1)
-			fprintf(fd, ",");
-	}
+    /* Print a user-friendly name for tags of relatively common use, but */
+    /* which aren't registered by libtiff itself. */
+    const char *field_name = fip->field_name;
+    if (TIFFFieldIsAnonymous(fip))
+    {
+        for (size_t i = 0; i < NTAGS; ++i)
+        {
+            if (fip->field_tag == tagnames[i].tag)
+            {
+                field_name = tagnames[i].name;
+                break;
+            }
+        }
+    }
+    fprintf(fd, "  %s: ", field_name);
 
-	fprintf(fd, "\n");
+    for (j = 0; j < value_count; j++)
+    {
+        if (fip->field_type == TIFF_BYTE)
+            fprintf(fd, "%" PRIu8, ((uint8_t *)raw_data)[j]);
+        else if (fip->field_type == TIFF_UNDEFINED)
+            fprintf(fd, "0x%" PRIx8, ((uint8_t *)raw_data)[j]);
+        else if (fip->field_type == TIFF_SBYTE)
+            fprintf(fd, "%" PRId8, ((int8_t *)raw_data)[j]);
+        else if (fip->field_type == TIFF_SHORT)
+            fprintf(fd, "%" PRIu16, ((uint16_t *)raw_data)[j]);
+        else if (fip->field_type == TIFF_SSHORT)
+            fprintf(fd, "%" PRId16, ((int16_t *)raw_data)[j]);
+        else if (fip->field_type == TIFF_LONG)
+            fprintf(fd, "%" PRIu32, ((uint32_t *)raw_data)[j]);
+        else if (fip->field_type == TIFF_SLONG)
+            fprintf(fd, "%" PRId32, ((int32_t *)raw_data)[j]);
+        else if (fip->field_type == TIFF_IFD)
+            fprintf(fd, "0x%" PRIx32, ((uint32_t *)raw_data)[j]);
+        else if (fip->field_type == TIFF_RATIONAL ||
+                 fip->field_type == TIFF_SRATIONAL)
+        {
+            int tv_size = TIFFFieldSetGetSize(fip);
+            if (tv_size == 8)
+                fprintf(fd, "%lf", ((double *)raw_data)[j]);
+            else
+                fprintf(fd, "%f", ((float *)raw_data)[j]);
+        }
+        else if (fip->field_type == TIFF_FLOAT)
+            fprintf(fd, "%f", ((float *)raw_data)[j]);
+        else if (fip->field_type == TIFF_LONG8)
+            fprintf(fd, "%" PRIu64, ((uint64_t *)raw_data)[j]);
+        else if (fip->field_type == TIFF_SLONG8)
+            fprintf(fd, "%" PRId64, ((int64_t *)raw_data)[j]);
+        else if (fip->field_type == TIFF_IFD8)
+            fprintf(fd, "0x%" PRIx64, ((uint64_t *)raw_data)[j]);
+        else if (fip->field_type == TIFF_DOUBLE)
+            fprintf(fd, "%lf", ((double *)raw_data)[j]);
+        else if (fip->field_type == TIFF_ASCII)
+        {
+            fprintf(fd, "%s", (char *)raw_data);
+            break;
+        }
+        else
+        {
+            fprintf(fd, "<unsupported data type in TIFFPrint>");
+            break;
+        }
+
+        if (j < value_count - 1)
+            fprintf(fd, ",");
+    }
+
+    fprintf(fd, "\n");
 }
 
-static int
-_TIFFPrettyPrintField(TIFF* tif, const TIFFField *fip, FILE* fd, uint32 tag,
-		      uint32 value_count, void *raw_data)
+static int _TIFFPrettyPrintField(TIFF *tif, const TIFFField *fip, FILE *fd,
+                                 uint32_t tag, uint32_t value_count,
+                                 void *raw_data)
 {
-        (void) tif;
+    (void)tif;
 
-	/* do not try to pretty print auto-defined fields */
-	if (strncmp(fip->field_name,"Tag ", 4) == 0) {
-		return 0;
-	}
-        
-	switch (tag)
-	{
-		case TIFFTAG_INKSET:
-			if (value_count == 2 && fip->field_type == TIFF_SHORT) {
-				fprintf(fd, "  Ink Set: ");
-				switch (*((uint16*)raw_data)) {
-				case INKSET_CMYK:
-					fprintf(fd, "CMYK\n");
-					break;
-				default:
-					fprintf(fd, "%u (0x%x)\n",
-						*((uint16*)raw_data),
-						*((uint16*)raw_data));
-					break;
-				}
-				return 1;
-			}
-			return 0;
+    /* do not try to pretty print auto-defined fields */
+    if (TIFFFieldIsAnonymous(fip))
+    {
+        return 0;
+    }
 
-		case TIFFTAG_DOTRANGE:
-			if (value_count == 2 && fip->field_type == TIFF_SHORT) {
-				fprintf(fd, "  Dot Range: %u-%u\n",
-					((uint16*)raw_data)[0], ((uint16*)raw_data)[1]);
-				return 1;
-			}
-			return 0;
+    switch (tag)
+    {
+        case TIFFTAG_INKSET:
+            if (value_count == 2 && fip->field_type == TIFF_SHORT)
+            {
+                fprintf(fd, "  Ink Set: ");
+                switch (*((uint16_t *)raw_data))
+                {
+                    case INKSET_CMYK:
+                        fprintf(fd, "CMYK\n");
+                        break;
+                    default:
+                        fprintf(fd, "%" PRIu16 " (0x%" PRIx16 ")\n",
+                                *((uint16_t *)raw_data),
+                                *((uint16_t *)raw_data));
+                        break;
+                }
+                return 1;
+            }
+            return 0;
 
-		case TIFFTAG_WHITEPOINT:
-			if (value_count == 2 && fip->field_type == TIFF_RATIONAL) {
-				fprintf(fd, "  White Point: %g-%g\n",
-					((float *)raw_data)[0], ((float *)raw_data)[1]);
-				return 1;
-			} 
-			return 0;
+        case TIFFTAG_DOTRANGE:
+            if (value_count == 2 && fip->field_type == TIFF_SHORT)
+            {
+                fprintf(fd, "  Dot Range: %" PRIu16 "-%" PRIu16 "\n",
+                        ((uint16_t *)raw_data)[0], ((uint16_t *)raw_data)[1]);
+                return 1;
+            }
+            return 0;
 
-		case TIFFTAG_XMLPACKET:
-		{
-			uint32 i;
+        case TIFFTAG_WHITEPOINT:
+            if (value_count == 2 && fip->field_type == TIFF_RATIONAL)
+            {
+                fprintf(fd, "  White Point: %g-%g\n", ((float *)raw_data)[0],
+                        ((float *)raw_data)[1]);
+                return 1;
+            }
+            return 0;
 
-			fprintf(fd, "  XMLPacket (XMP Metadata):\n" );
-			for(i = 0; i < value_count; i++)
-				fputc(((char *)raw_data)[i], fd);
-			fprintf( fd, "\n" );
-			return 1;
-		}
-		case TIFFTAG_RICHTIFFIPTC:
-			/*
-			 * XXX: for some weird reason RichTIFFIPTC tag
-			 * defined as array of LONG values.
-			 */
-			fprintf(fd,
-			    "  RichTIFFIPTC Data: <present>, %lu bytes\n",
-			    (unsigned long) value_count * 4);
-			return 1;
+        case TIFFTAG_XMLPACKET:
+        {
+            uint32_t i;
 
-		case TIFFTAG_PHOTOSHOP:
-			fprintf(fd, "  Photoshop Data: <present>, %lu bytes\n",
-			    (unsigned long) value_count);
-			return 1;
+            fprintf(fd, "  XMLPacket (XMP Metadata):\n");
+            for (i = 0; i < value_count; i++)
+                fputc(((char *)raw_data)[i], fd);
+            fprintf(fd, "\n");
+            return 1;
+        }
+        case TIFFTAG_RICHTIFFIPTC:
+            fprintf(fd, "  RichTIFFIPTC Data: <present>, %" PRIu32 " bytes\n",
+                    value_count);
+            return 1;
 
-		case TIFFTAG_ICCPROFILE:
-			fprintf(fd, "  ICC Profile: <present>, %lu bytes\n",
-			    (unsigned long) value_count);
-			return 1;
+        case TIFFTAG_PHOTOSHOP:
+            fprintf(fd, "  Photoshop Data: <present>, %" PRIu32 " bytes\n",
+                    value_count);
+            return 1;
 
-		case TIFFTAG_STONITS:
-			if (value_count == 1 && fip->field_type == TIFF_DOUBLE) { 
-				fprintf(fd,
-					"  Sample to Nits conversion factor: %.4e\n",
-					*((double*)raw_data));
-				return 1;
-			}
-			return 0;
-	}
+        case TIFFTAG_ICCPROFILE:
+            fprintf(fd, "  ICC Profile: <present>, %" PRIu32 " bytes\n",
+                    value_count);
+            return 1;
 
-	return 0;
+        case TIFFTAG_STONITS:
+            if (value_count == 1 && fip->field_type == TIFF_DOUBLE)
+            {
+                fprintf(fd, "  Sample to Nits conversion factor: %.4e\n",
+                        *((double *)raw_data));
+                return 1;
+            }
+            return 0;
+    }
+
+    return 0;
 }
 
 /*
  * Print the contents of the current directory
  * to the specified stdio file stream.
  */
-void
-TIFFPrintDirectory(TIFF* tif, FILE* fd, long flags)
+void TIFFPrintDirectory(TIFF *tif, FILE *fd, long flags)
 {
-	TIFFDirectory *td = &tif->tif_dir;
-	char *sep;
-	long l, n;
+    TIFFDirectory *td = &tif->tif_dir;
+    char *sep;
+    long l, n;
 
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-	fprintf(fd, "TIFF Directory at offset 0x%I64x (%I64u)\n",
-		(unsigned __int64) tif->tif_diroff,
-		(unsigned __int64) tif->tif_diroff);
-#else
-	fprintf(fd, "TIFF Directory at offset 0x%llx (%llu)\n",
-		(unsigned long long) tif->tif_diroff,
-		(unsigned long long) tif->tif_diroff);
-#endif
-	if (TIFFFieldSet(tif,FIELD_SUBFILETYPE)) {
-		fprintf(fd, "  Subfile Type:");
-		sep = " ";
-		if (td->td_subfiletype & FILETYPE_REDUCEDIMAGE) {
-			fprintf(fd, "%sreduced-resolution image", sep);
-			sep = "/";
-		}
-		if (td->td_subfiletype & FILETYPE_PAGE) {
-			fprintf(fd, "%smulti-page document", sep);
-			sep = "/";
-		}
-		if (td->td_subfiletype & FILETYPE_MASK)
-			fprintf(fd, "%stransparency mask", sep);
-		fprintf(fd, " (%lu = 0x%lx)\n",
-		    (unsigned long) td->td_subfiletype, (long) td->td_subfiletype);
-	}
-	if (TIFFFieldSet(tif,FIELD_IMAGEDIMENSIONS)) {
-		fprintf(fd, "  Image Width: %lu Image Length: %lu",
-		    (unsigned long) td->td_imagewidth, (unsigned long) td->td_imagelength);
-		if (TIFFFieldSet(tif,FIELD_IMAGEDEPTH))
-			fprintf(fd, " Image Depth: %lu",
-			    (unsigned long) td->td_imagedepth);
-		fprintf(fd, "\n");
-	}
-	if (TIFFFieldSet(tif,FIELD_TILEDIMENSIONS)) {
-		fprintf(fd, "  Tile Width: %lu Tile Length: %lu",
-		    (unsigned long) td->td_tilewidth, (unsigned long) td->td_tilelength);
-		if (TIFFFieldSet(tif,FIELD_TILEDEPTH))
-			fprintf(fd, " Tile Depth: %lu",
-			    (unsigned long) td->td_tiledepth);
-		fprintf(fd, "\n");
-	}
-	if (TIFFFieldSet(tif,FIELD_RESOLUTION)) {
-		fprintf(fd, "  Resolution: %g, %g",
-		    td->td_xresolution, td->td_yresolution);
-		if (TIFFFieldSet(tif,FIELD_RESOLUTIONUNIT)) {
-			switch (td->td_resolutionunit) {
-			case RESUNIT_NONE:
-				fprintf(fd, " (unitless)");
-				break;
-			case RESUNIT_INCH:
-				fprintf(fd, " pixels/inch");
-				break;
-			case RESUNIT_CENTIMETER:
-				fprintf(fd, " pixels/cm");
-				break;
-			default:
-				fprintf(fd, " (unit %u = 0x%x)",
-				    td->td_resolutionunit,
-				    td->td_resolutionunit);
-				break;
-			}
-		}
-		fprintf(fd, "\n");
-	}
-	if (TIFFFieldSet(tif,FIELD_POSITION))
-		fprintf(fd, "  Position: %g, %g\n",
-		    td->td_xposition, td->td_yposition);
-	if (TIFFFieldSet(tif,FIELD_BITSPERSAMPLE))
-		fprintf(fd, "  Bits/Sample: %u\n", td->td_bitspersample);
-	if (TIFFFieldSet(tif,FIELD_SAMPLEFORMAT)) {
-		fprintf(fd, "  Sample Format: ");
-		switch (td->td_sampleformat) {
-		case SAMPLEFORMAT_VOID:
-			fprintf(fd, "void\n");
-			break;
-		case SAMPLEFORMAT_INT:
-			fprintf(fd, "signed integer\n");
-			break;
-		case SAMPLEFORMAT_UINT:
-			fprintf(fd, "unsigned integer\n");
-			break;
-		case SAMPLEFORMAT_IEEEFP:
-			fprintf(fd, "IEEE floating point\n");
-			break;
-		case SAMPLEFORMAT_COMPLEXINT:
-			fprintf(fd, "complex signed integer\n");
-			break;
-		case SAMPLEFORMAT_COMPLEXIEEEFP:
-			fprintf(fd, "complex IEEE floating point\n");
-			break;
-		default:
-			fprintf(fd, "%u (0x%x)\n",
-			    td->td_sampleformat, td->td_sampleformat);
-			break;
-		}
-	}
-	if (TIFFFieldSet(tif,FIELD_COMPRESSION)) {
-		const TIFFCodec* c = TIFFFindCODEC(td->td_compression);
-		fprintf(fd, "  Compression Scheme: ");
-		if (c)
-			fprintf(fd, "%s\n", c->name);
-		else
-			fprintf(fd, "%u (0x%x)\n",
-			    td->td_compression, td->td_compression);
-	}
-	if (TIFFFieldSet(tif,FIELD_PHOTOMETRIC)) {
-		fprintf(fd, "  Photometric Interpretation: ");
-		if (td->td_photometric < NPHOTONAMES)
-			fprintf(fd, "%s\n", photoNames[td->td_photometric]);
-		else {
-			switch (td->td_photometric) {
-			case PHOTOMETRIC_LOGL:
-				fprintf(fd, "CIE Log2(L)\n");
-				break;
-			case PHOTOMETRIC_LOGLUV:
-				fprintf(fd, "CIE Log2(L) (u',v')\n");
-				break;
-			default:
-				fprintf(fd, "%u (0x%x)\n",
-				    td->td_photometric, td->td_photometric);
-				break;
-			}
-		}
-	}
-	if (TIFFFieldSet(tif,FIELD_EXTRASAMPLES) && td->td_extrasamples) {
-		uint16 i;
-		fprintf(fd, "  Extra Samples: %u<", td->td_extrasamples);
-		sep = "";
-		for (i = 0; i < td->td_extrasamples; i++) {
-			switch (td->td_sampleinfo[i]) {
-			case EXTRASAMPLE_UNSPECIFIED:
-				fprintf(fd, "%sunspecified", sep);
-				break;
-			case EXTRASAMPLE_ASSOCALPHA:
-				fprintf(fd, "%sassoc-alpha", sep);
-				break;
-			case EXTRASAMPLE_UNASSALPHA:
-				fprintf(fd, "%sunassoc-alpha", sep);
-				break;
-			default:
-				fprintf(fd, "%s%u (0x%x)", sep,
-				    td->td_sampleinfo[i], td->td_sampleinfo[i]);
-				break;
-			}
-			sep = ", ";
-		}
-		fprintf(fd, ">\n");
-	}
-	if (TIFFFieldSet(tif,FIELD_INKNAMES)) {
-		char* cp;
-		uint16 i;
-		fprintf(fd, "  Ink Names: ");
-		i = td->td_samplesperpixel;
-		sep = "";
-		for (cp = td->td_inknames; 
-		     i > 0 && cp < td->td_inknames + td->td_inknameslen; 
-		     cp = strchr(cp,'\0')+1, i--) {
-			size_t max_chars = 
-				td->td_inknameslen - (cp - td->td_inknames);
-			fputs(sep, fd);
-			_TIFFprintAsciiBounded(fd, cp, max_chars);
-			sep = ", ";
-		}
-                fputs("\n", fd);
-	}
-	if (TIFFFieldSet(tif,FIELD_THRESHHOLDING)) {
-		fprintf(fd, "  Thresholding: ");
-		switch (td->td_threshholding) {
-		case THRESHHOLD_BILEVEL:
-			fprintf(fd, "bilevel art scan\n");
-			break;
-		case THRESHHOLD_HALFTONE:
-			fprintf(fd, "halftone or dithered scan\n");
-			break;
-		case THRESHHOLD_ERRORDIFFUSE:
-			fprintf(fd, "error diffused\n");
-			break;
-		default:
-			fprintf(fd, "%u (0x%x)\n",
-			    td->td_threshholding, td->td_threshholding);
-			break;
-		}
-	}
-	if (TIFFFieldSet(tif,FIELD_FILLORDER)) {
-		fprintf(fd, "  FillOrder: ");
-		switch (td->td_fillorder) {
-		case FILLORDER_MSB2LSB:
-			fprintf(fd, "msb-to-lsb\n");
-			break;
-		case FILLORDER_LSB2MSB:
-			fprintf(fd, "lsb-to-msb\n");
-			break;
-		default:
-			fprintf(fd, "%u (0x%x)\n",
-			    td->td_fillorder, td->td_fillorder);
-			break;
-		}
-	}
-	if (TIFFFieldSet(tif,FIELD_YCBCRSUBSAMPLING))
+    fprintf(fd, "TIFF Directory at offset 0x%" PRIx64 " (%" PRIu64 ")\n",
+            tif->tif_diroff, tif->tif_diroff);
+    if (TIFFFieldSet(tif, FIELD_SUBFILETYPE))
+    {
+        fprintf(fd, "  Subfile Type:");
+        sep = " ";
+        if (td->td_subfiletype & FILETYPE_REDUCEDIMAGE)
         {
-		fprintf(fd, "  YCbCr Subsampling: %u, %u\n",
-			td->td_ycbcrsubsampling[0], td->td_ycbcrsubsampling[1] );
-	}
-	if (TIFFFieldSet(tif,FIELD_YCBCRPOSITIONING)) {
-		fprintf(fd, "  YCbCr Positioning: ");
-		switch (td->td_ycbcrpositioning) {
-		case YCBCRPOSITION_CENTERED:
-			fprintf(fd, "centered\n");
-			break;
-		case YCBCRPOSITION_COSITED:
-			fprintf(fd, "cosited\n");
-			break;
-		default:
-			fprintf(fd, "%u (0x%x)\n",
-			    td->td_ycbcrpositioning, td->td_ycbcrpositioning);
-			break;
-		}
-	}
-	if (TIFFFieldSet(tif,FIELD_HALFTONEHINTS))
-		fprintf(fd, "  Halftone Hints: light %u dark %u\n",
-		    td->td_halftonehints[0], td->td_halftonehints[1]);
-	if (TIFFFieldSet(tif,FIELD_ORIENTATION)) {
-		fprintf(fd, "  Orientation: ");
-		if (td->td_orientation < NORIENTNAMES)
-			fprintf(fd, "%s\n", orientNames[td->td_orientation]);
-		else
-			fprintf(fd, "%u (0x%x)\n",
-			    td->td_orientation, td->td_orientation);
-	}
-	if (TIFFFieldSet(tif,FIELD_SAMPLESPERPIXEL))
-		fprintf(fd, "  Samples/Pixel: %u\n", td->td_samplesperpixel);
-	if (TIFFFieldSet(tif,FIELD_ROWSPERSTRIP)) {
-		fprintf(fd, "  Rows/Strip: ");
-		if (td->td_rowsperstrip == (uint32) -1)
-			fprintf(fd, "(infinite)\n");
-		else
-			fprintf(fd, "%lu\n", (unsigned long) td->td_rowsperstrip);
-	}
-	if (TIFFFieldSet(tif,FIELD_MINSAMPLEVALUE))
-		fprintf(fd, "  Min Sample Value: %u\n", td->td_minsamplevalue);
-	if (TIFFFieldSet(tif,FIELD_MAXSAMPLEVALUE))
-		fprintf(fd, "  Max Sample Value: %u\n", td->td_maxsamplevalue);
-	if (TIFFFieldSet(tif,FIELD_SMINSAMPLEVALUE)) {
-		int i;
-		int count = (tif->tif_flags & TIFF_PERSAMPLE) ? td->td_samplesperpixel : 1;
-		fprintf(fd, "  SMin Sample Value:");
-		for (i = 0; i < count; ++i)
-			fprintf(fd, " %g", td->td_sminsamplevalue[i]);
-		fprintf(fd, "\n");
-	}
-	if (TIFFFieldSet(tif,FIELD_SMAXSAMPLEVALUE)) {
-		int i;
-		int count = (tif->tif_flags & TIFF_PERSAMPLE) ? td->td_samplesperpixel : 1;
-		fprintf(fd, "  SMax Sample Value:");
-		for (i = 0; i < count; ++i)
-			fprintf(fd, " %g", td->td_smaxsamplevalue[i]);
-		fprintf(fd, "\n");
-	}
-	if (TIFFFieldSet(tif,FIELD_PLANARCONFIG)) {
-		fprintf(fd, "  Planar Configuration: ");
-		switch (td->td_planarconfig) {
-		case PLANARCONFIG_CONTIG:
-			fprintf(fd, "single image plane\n");
-			break;
-		case PLANARCONFIG_SEPARATE:
-			fprintf(fd, "separate image planes\n");
-			break;
-		default:
-			fprintf(fd, "%u (0x%x)\n",
-			    td->td_planarconfig, td->td_planarconfig);
-			break;
-		}
-	}
-	if (TIFFFieldSet(tif,FIELD_PAGENUMBER))
-		fprintf(fd, "  Page Number: %u-%u\n",
-		    td->td_pagenumber[0], td->td_pagenumber[1]);
-	if (TIFFFieldSet(tif,FIELD_COLORMAP)) {
-		fprintf(fd, "  Color Map: ");
-		if (flags & TIFFPRINT_COLORMAP) {
-			fprintf(fd, "\n");
-			n = 1L<<td->td_bitspersample;
-			for (l = 0; l < n; l++)
-				fprintf(fd, "   %5ld: %5u %5u %5u\n",
-				    l,
-				    td->td_colormap[0][l],
-				    td->td_colormap[1][l],
-				    td->td_colormap[2][l]);
-		} else
-			fprintf(fd, "(present)\n");
-	}
-	if (TIFFFieldSet(tif,FIELD_REFBLACKWHITE)) {
-		int i;
-		fprintf(fd, "  Reference Black/White:\n");
-		for (i = 0; i < 3; i++)
-		fprintf(fd, "    %2d: %5g %5g\n", i,
-			td->td_refblackwhite[2*i+0],
-			td->td_refblackwhite[2*i+1]);
-	}
-	if (TIFFFieldSet(tif,FIELD_TRANSFERFUNCTION)) {
-		fprintf(fd, "  Transfer Function: ");
-		if (flags & TIFFPRINT_CURVES) {
-			fprintf(fd, "\n");
-			n = 1L<<td->td_bitspersample;
-			for (l = 0; l < n; l++) {
-				uint16 i;
-				fprintf(fd, "    %2ld: %5u",
-				    l, td->td_transferfunction[0][l]);
-				for (i = 1; i < td->td_samplesperpixel - td->td_extrasamples && i < 3; i++)
-					fprintf(fd, " %5u",
-					    td->td_transferfunction[i][l]);
-				fputc('\n', fd);
-			}
-		} else
-			fprintf(fd, "(present)\n");
-	}
-	if (TIFFFieldSet(tif, FIELD_SUBIFD) && (td->td_subifd)) {
-		uint16 i;
-		fprintf(fd, "  SubIFD Offsets:");
-		for (i = 0; i < td->td_nsubifd; i++)
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-			fprintf(fd, " %5I64u",
-				(unsigned __int64) td->td_subifd[i]);
-#else
-			fprintf(fd, " %5llu",
-				(unsigned long long) td->td_subifd[i]);
-#endif
-		fputc('\n', fd);
-	}
+            fprintf(fd, "%sreduced-resolution image", sep);
+            sep = "/";
+        }
+        if (td->td_subfiletype & FILETYPE_PAGE)
+        {
+            fprintf(fd, "%smulti-page document", sep);
+            sep = "/";
+        }
+        if (td->td_subfiletype & FILETYPE_MASK)
+            fprintf(fd, "%stransparency mask", sep);
+        fprintf(fd, " (%" PRIu32 " = 0x%" PRIx32 ")\n", td->td_subfiletype,
+                td->td_subfiletype);
+    }
+    if (TIFFFieldSet(tif, FIELD_IMAGEDIMENSIONS))
+    {
+        fprintf(fd, "  Image Width: %" PRIu32 " Image Length: %" PRIu32,
+                td->td_imagewidth, td->td_imagelength);
+        if (TIFFFieldSet(tif, FIELD_IMAGEDEPTH))
+            fprintf(fd, " Image Depth: %" PRIu32, td->td_imagedepth);
+        fprintf(fd, "\n");
+    }
+    if (TIFFFieldSet(tif, FIELD_TILEDIMENSIONS))
+    {
+        fprintf(fd, "  Tile Width: %" PRIu32 " Tile Length: %" PRIu32,
+                td->td_tilewidth, td->td_tilelength);
+        if (TIFFFieldSet(tif, FIELD_TILEDEPTH))
+            fprintf(fd, " Tile Depth: %" PRIu32, td->td_tiledepth);
+        fprintf(fd, "\n");
+    }
+    if (TIFFFieldSet(tif, FIELD_RESOLUTION))
+    {
+        fprintf(fd, "  Resolution: %g, %g", td->td_xresolution,
+                td->td_yresolution);
+        if (TIFFFieldSet(tif, FIELD_RESOLUTIONUNIT))
+        {
+            switch (td->td_resolutionunit)
+            {
+                case RESUNIT_NONE:
+                    fprintf(fd, " (unitless)");
+                    break;
+                case RESUNIT_INCH:
+                    fprintf(fd, " pixels/inch");
+                    break;
+                case RESUNIT_CENTIMETER:
+                    fprintf(fd, " pixels/cm");
+                    break;
+                default:
+                    fprintf(fd, " (unit %" PRIu16 " = 0x%" PRIx16 ")",
+                            td->td_resolutionunit, td->td_resolutionunit);
+                    break;
+            }
+        }
+        fprintf(fd, "\n");
+    }
+    if (TIFFFieldSet(tif, FIELD_POSITION))
+        fprintf(fd, "  Position: %g, %g\n", td->td_xposition, td->td_yposition);
+    if (TIFFFieldSet(tif, FIELD_BITSPERSAMPLE))
+        fprintf(fd, "  Bits/Sample: %" PRIu16 "\n", td->td_bitspersample);
+    if (TIFFFieldSet(tif, FIELD_SAMPLEFORMAT))
+    {
+        fprintf(fd, "  Sample Format: ");
+        switch (td->td_sampleformat)
+        {
+            case SAMPLEFORMAT_VOID:
+                fprintf(fd, "void\n");
+                break;
+            case SAMPLEFORMAT_INT:
+                fprintf(fd, "signed integer\n");
+                break;
+            case SAMPLEFORMAT_UINT:
+                fprintf(fd, "unsigned integer\n");
+                break;
+            case SAMPLEFORMAT_IEEEFP:
+                fprintf(fd, "IEEE floating point\n");
+                break;
+            case SAMPLEFORMAT_COMPLEXINT:
+                fprintf(fd, "complex signed integer\n");
+                break;
+            case SAMPLEFORMAT_COMPLEXIEEEFP:
+                fprintf(fd, "complex IEEE floating point\n");
+                break;
+            default:
+                fprintf(fd, "%" PRIu16 " (0x%" PRIx16 ")\n",
+                        td->td_sampleformat, td->td_sampleformat);
+                break;
+        }
+    }
+    if (TIFFFieldSet(tif, FIELD_COMPRESSION))
+    {
+        const TIFFCodec *c = TIFFFindCODEC(td->td_compression);
+        fprintf(fd, "  Compression Scheme: ");
+        if (c)
+            fprintf(fd, "%s\n", c->name);
+        else
+            fprintf(fd, "%" PRIu16 " (0x%" PRIx16 ")\n", td->td_compression,
+                    td->td_compression);
+    }
+    if (TIFFFieldSet(tif, FIELD_PHOTOMETRIC))
+    {
+        fprintf(fd, "  Photometric Interpretation: ");
+        if (td->td_photometric < NPHOTONAMES)
+            fprintf(fd, "%s\n", photoNames[td->td_photometric]);
+        else
+        {
+            switch (td->td_photometric)
+            {
+                case PHOTOMETRIC_LOGL:
+                    fprintf(fd, "CIE Log2(L)\n");
+                    break;
+                case PHOTOMETRIC_LOGLUV:
+                    fprintf(fd, "CIE Log2(L) (u',v')\n");
+                    break;
+                default:
+                    fprintf(fd, "%" PRIu16 " (0x%" PRIx16 ")\n",
+                            td->td_photometric, td->td_photometric);
+                    break;
+            }
+        }
+    }
+    if (TIFFFieldSet(tif, FIELD_EXTRASAMPLES) && td->td_extrasamples)
+    {
+        uint16_t i;
+        fprintf(fd, "  Extra Samples: %" PRIu16 "<", td->td_extrasamples);
+        sep = "";
+        for (i = 0; i < td->td_extrasamples; i++)
+        {
+            switch (td->td_sampleinfo[i])
+            {
+                case EXTRASAMPLE_UNSPECIFIED:
+                    fprintf(fd, "%sunspecified", sep);
+                    break;
+                case EXTRASAMPLE_ASSOCALPHA:
+                    fprintf(fd, "%sassoc-alpha", sep);
+                    break;
+                case EXTRASAMPLE_UNASSALPHA:
+                    fprintf(fd, "%sunassoc-alpha", sep);
+                    break;
+                default:
+                    fprintf(fd, "%s%" PRIu16 " (0x%" PRIx16 ")", sep,
+                            td->td_sampleinfo[i], td->td_sampleinfo[i]);
+                    break;
+            }
+            sep = ", ";
+        }
+        fprintf(fd, ">\n");
+    }
+    if (TIFFFieldSet(tif, FIELD_INKNAMES))
+    {
+        char *cp;
+        uint16_t i;
+        fprintf(fd, "  Ink Names: ");
+        i = td->td_samplesperpixel;
+        sep = "";
+        for (cp = td->td_inknames;
+             i > 0 && cp < td->td_inknames + td->td_inknameslen;
+             cp = strchr(cp, '\0') + 1, i--)
+        {
+            size_t max_chars = td->td_inknameslen - (cp - td->td_inknames);
+            fputs(sep, fd);
+            _TIFFprintAsciiBounded(fd, cp, max_chars);
+            sep = ", ";
+        }
+        fputs("\n", fd);
+    }
+    if (TIFFFieldSet(tif, FIELD_NUMBEROFINKS))
+    {
+        fprintf(fd, "  NumberOfInks: %d\n", td->td_numberofinks);
+    }
+    if (TIFFFieldSet(tif, FIELD_THRESHHOLDING))
+    {
+        fprintf(fd, "  Thresholding: ");
+        switch (td->td_threshholding)
+        {
+            case THRESHHOLD_BILEVEL:
+                fprintf(fd, "bilevel art scan\n");
+                break;
+            case THRESHHOLD_HALFTONE:
+                fprintf(fd, "halftone or dithered scan\n");
+                break;
+            case THRESHHOLD_ERRORDIFFUSE:
+                fprintf(fd, "error diffused\n");
+                break;
+            default:
+                fprintf(fd, "%" PRIu16 " (0x%" PRIx16 ")\n",
+                        td->td_threshholding, td->td_threshholding);
+                break;
+        }
+    }
+    if (TIFFFieldSet(tif, FIELD_FILLORDER))
+    {
+        fprintf(fd, "  FillOrder: ");
+        switch (td->td_fillorder)
+        {
+            case FILLORDER_MSB2LSB:
+                fprintf(fd, "msb-to-lsb\n");
+                break;
+            case FILLORDER_LSB2MSB:
+                fprintf(fd, "lsb-to-msb\n");
+                break;
+            default:
+                fprintf(fd, "%" PRIu16 " (0x%" PRIx16 ")\n", td->td_fillorder,
+                        td->td_fillorder);
+                break;
+        }
+    }
+    if (TIFFFieldSet(tif, FIELD_YCBCRSUBSAMPLING))
+    {
+        fprintf(fd, "  YCbCr Subsampling: %" PRIu16 ", %" PRIu16 "\n",
+                td->td_ycbcrsubsampling[0], td->td_ycbcrsubsampling[1]);
+    }
+    if (TIFFFieldSet(tif, FIELD_YCBCRPOSITIONING))
+    {
+        fprintf(fd, "  YCbCr Positioning: ");
+        switch (td->td_ycbcrpositioning)
+        {
+            case YCBCRPOSITION_CENTERED:
+                fprintf(fd, "centered\n");
+                break;
+            case YCBCRPOSITION_COSITED:
+                fprintf(fd, "cosited\n");
+                break;
+            default:
+                fprintf(fd, "%" PRIu16 " (0x%" PRIx16 ")\n",
+                        td->td_ycbcrpositioning, td->td_ycbcrpositioning);
+                break;
+        }
+    }
+    if (TIFFFieldSet(tif, FIELD_HALFTONEHINTS))
+        fprintf(fd, "  Halftone Hints: light %" PRIu16 " dark %" PRIu16 "\n",
+                td->td_halftonehints[0], td->td_halftonehints[1]);
+    if (TIFFFieldSet(tif, FIELD_ORIENTATION))
+    {
+        fprintf(fd, "  Orientation: ");
+        if (td->td_orientation < NORIENTNAMES)
+            fprintf(fd, "%s\n", orientNames[td->td_orientation]);
+        else
+            fprintf(fd, "%" PRIu16 " (0x%" PRIx16 ")\n", td->td_orientation,
+                    td->td_orientation);
+    }
+    if (TIFFFieldSet(tif, FIELD_SAMPLESPERPIXEL))
+        fprintf(fd, "  Samples/Pixel: %" PRIx16 "\n", td->td_samplesperpixel);
+    if (TIFFFieldSet(tif, FIELD_ROWSPERSTRIP))
+    {
+        fprintf(fd, "  Rows/Strip: ");
+        if (td->td_rowsperstrip == (uint32_t)-1)
+            fprintf(fd, "(infinite)\n");
+        else
+            fprintf(fd, "%" PRIu32 "\n", td->td_rowsperstrip);
+    }
+    if (TIFFFieldSet(tif, FIELD_MINSAMPLEVALUE))
+        fprintf(fd, "  Min Sample Value: %" PRIu16 "\n", td->td_minsamplevalue);
+    if (TIFFFieldSet(tif, FIELD_MAXSAMPLEVALUE))
+        fprintf(fd, "  Max Sample Value: %" PRIu16 "\n", td->td_maxsamplevalue);
+    if (TIFFFieldSet(tif, FIELD_SMINSAMPLEVALUE))
+    {
+        int i;
+        int count =
+            (tif->tif_flags & TIFF_PERSAMPLE) ? td->td_samplesperpixel : 1;
+        fprintf(fd, "  SMin Sample Value:");
+        for (i = 0; i < count; ++i)
+            fprintf(fd, " %g", td->td_sminsamplevalue[i]);
+        fprintf(fd, "\n");
+    }
+    if (TIFFFieldSet(tif, FIELD_SMAXSAMPLEVALUE))
+    {
+        int i;
+        int count =
+            (tif->tif_flags & TIFF_PERSAMPLE) ? td->td_samplesperpixel : 1;
+        fprintf(fd, "  SMax Sample Value:");
+        for (i = 0; i < count; ++i)
+            fprintf(fd, " %g", td->td_smaxsamplevalue[i]);
+        fprintf(fd, "\n");
+    }
+    if (TIFFFieldSet(tif, FIELD_PLANARCONFIG))
+    {
+        fprintf(fd, "  Planar Configuration: ");
+        switch (td->td_planarconfig)
+        {
+            case PLANARCONFIG_CONTIG:
+                fprintf(fd, "single image plane\n");
+                break;
+            case PLANARCONFIG_SEPARATE:
+                fprintf(fd, "separate image planes\n");
+                break;
+            default:
+                fprintf(fd, "%" PRIu16 " (0x%" PRIx16 ")\n",
+                        td->td_planarconfig, td->td_planarconfig);
+                break;
+        }
+    }
+    if (TIFFFieldSet(tif, FIELD_PAGENUMBER))
+        fprintf(fd, "  Page Number: %" PRIu16 "-%" PRIu16 "\n",
+                td->td_pagenumber[0], td->td_pagenumber[1]);
+    if (TIFFFieldSet(tif, FIELD_COLORMAP))
+    {
+        fprintf(fd, "  Color Map: ");
+        if (flags & TIFFPRINT_COLORMAP)
+        {
+            fprintf(fd, "\n");
+            n = 1L << td->td_bitspersample;
+            for (l = 0; l < n; l++)
+                fprintf(fd, "   %5ld: %5" PRIu16 " %5" PRIu16 " %5" PRIu16 "\n",
+                        l, td->td_colormap[0][l], td->td_colormap[1][l],
+                        td->td_colormap[2][l]);
+        }
+        else
+            fprintf(fd, "(present)\n");
+    }
+    if (TIFFFieldSet(tif, FIELD_REFBLACKWHITE))
+    {
+        int i;
+        fprintf(fd, "  Reference Black/White:\n");
+        for (i = 0; i < 3; i++)
+            fprintf(fd, "    %2d: %5g %5g\n", i,
+                    td->td_refblackwhite[2 * i + 0],
+                    td->td_refblackwhite[2 * i + 1]);
+    }
+    if (TIFFFieldSet(tif, FIELD_TRANSFERFUNCTION))
+    {
+        fprintf(fd, "  Transfer Function: ");
+        if (flags & TIFFPRINT_CURVES)
+        {
+            fprintf(fd, "\n");
+            n = 1L << td->td_bitspersample;
+            for (l = 0; l < n; l++)
+            {
+                uint16_t i;
+                fprintf(fd, "    %2ld: %5" PRIu16, l,
+                        td->td_transferfunction[0][l]);
+                for (i = 1;
+                     i < td->td_samplesperpixel - td->td_extrasamples && i < 3;
+                     i++)
+                    fprintf(fd, " %5" PRIu16, td->td_transferfunction[i][l]);
+                fputc('\n', fd);
+            }
+        }
+        else
+            fprintf(fd, "(present)\n");
+    }
+    if (TIFFFieldSet(tif, FIELD_SUBIFD) && (td->td_subifd))
+    {
+        uint16_t i;
+        fprintf(fd, "  SubIFD Offsets:");
+        for (i = 0; i < td->td_nsubifd; i++)
+            fprintf(fd, " %5" PRIu64, td->td_subifd[i]);
+        fputc('\n', fd);
+    }
 
-	/*
-	** Custom tag support.
-	*/
-	{
-		int  i;
-		short count;
+    /*
+    ** Custom tag support.
+    */
+    {
+        int i;
+        short count;
 
-		count = (short) TIFFGetTagListCount(tif);
-		for(i = 0; i < count; i++) {
-			uint32 tag = TIFFGetTagListEntry(tif, i);
-			const TIFFField *fip;
-			uint32 value_count;
-			int mem_alloc = 0;
-			void *raw_data;
+        count = (short)TIFFGetTagListCount(tif);
+        for (i = 0; i < count; i++)
+        {
+            uint32_t tag = TIFFGetTagListEntry(tif, i);
+            const TIFFField *fip;
+            uint32_t value_count;
+            int mem_alloc = 0;
+            void *raw_data = NULL;
+            uint16_t dotrange[2]; /* must be kept in that scope and not moved in
+                                     the below TIFFTAG_DOTRANGE specific case */
 
-			fip = TIFFFieldWithTag(tif, tag);
-			if(fip == NULL)
-				continue;
+            fip = TIFFFieldWithTag(tif, tag);
+            if (fip == NULL)
+                continue;
 
-			if(fip->field_passcount) {
-				if (fip->field_readcount == TIFF_VARIABLE2 ) {
-					if(TIFFGetField(tif, tag, &value_count, &raw_data) != 1)
-						continue;
-				} else if (fip->field_readcount == TIFF_VARIABLE ) {
-					uint16 small_value_count;
-					if(TIFFGetField(tif, tag, &small_value_count, &raw_data) != 1)
-						continue;
-					value_count = small_value_count;
-				} else {
-					assert (fip->field_readcount == TIFF_VARIABLE
-						|| fip->field_readcount == TIFF_VARIABLE2);
-					continue;
-				} 
-			} else {
-				if (fip->field_readcount == TIFF_VARIABLE
-				    || fip->field_readcount == TIFF_VARIABLE2)
-					value_count = 1;
-				else if (fip->field_readcount == TIFF_SPP)
-					value_count = td->td_samplesperpixel;
-				else
-					value_count = fip->field_readcount;
-				if (fip->field_tag == TIFFTAG_DOTRANGE
-				    && strcmp(fip->field_name,"DotRange") == 0) {
-					/* TODO: This is an evil exception and should not have been
-					   handled this way ... likely best if we move it into
-					   the directory structure with an explicit field in 
-					   libtiff 4.1 and assign it a FIELD_ value */
-					static uint16 dotrange[2];
-					raw_data = dotrange;
-					TIFFGetField(tif, tag, dotrange+0, dotrange+1);
-				} else if (fip->field_type == TIFF_ASCII
-					   || fip->field_readcount == TIFF_VARIABLE
-					   || fip->field_readcount == TIFF_VARIABLE2
-					   || fip->field_readcount == TIFF_SPP
-					   || value_count > 1) {
-					if(TIFFGetField(tif, tag, &raw_data) != 1)
-						continue;
-				} else {
-					raw_data = _TIFFmalloc(
-					    _TIFFDataSize(fip->field_type)
-					    * value_count);
-					mem_alloc = 1;
-					if(TIFFGetField(tif, tag, raw_data) != 1) {
-						_TIFFfree(raw_data);
-						continue;
-					}
-				}
-			}
+            if (fip->field_passcount)
+            {
+                if (fip->field_readcount == TIFF_VARIABLE2)
+                {
+                    if (TIFFGetField(tif, tag, &value_count, &raw_data) != 1)
+                        continue;
+                }
+                else if (fip->field_readcount == TIFF_VARIABLE)
+                {
+                    uint16_t small_value_count;
+                    if (TIFFGetField(tif, tag, &small_value_count, &raw_data) !=
+                        1)
+                        continue;
+                    value_count = small_value_count;
+                }
+                else
+                {
+                    assert(fip->field_readcount == TIFF_VARIABLE ||
+                           fip->field_readcount == TIFF_VARIABLE2);
+                    continue;
+                }
+            }
+            else
+            {
+                if (fip->field_readcount == TIFF_VARIABLE ||
+                    fip->field_readcount == TIFF_VARIABLE2)
+                    value_count = 1;
+                else if (fip->field_readcount == TIFF_SPP)
+                    value_count = td->td_samplesperpixel;
+                else
+                    value_count = fip->field_readcount;
+                if (fip->field_tag == TIFFTAG_DOTRANGE &&
+                    strcmp(fip->field_name, "DotRange") == 0)
+                {
+                    /* TODO: This is an evil exception and should not have been
+                       handled this way ... likely best if we move it into
+                       the directory structure with an explicit field in
+                       libtiff 4.1 and assign it a FIELD_ value */
+                    raw_data = dotrange;
+                    TIFFGetField(tif, tag, dotrange + 0, dotrange + 1);
+                }
+                else if (fip->field_type == TIFF_ASCII ||
+                         fip->field_readcount == TIFF_VARIABLE ||
+                         fip->field_readcount == TIFF_VARIABLE2 ||
+                         fip->field_readcount == TIFF_SPP || value_count > 1)
+                {
+                    if (TIFFGetField(tif, tag, &raw_data) != 1)
+                        continue;
+                }
+                else
+                {
+                    /*--: Rational2Double: For Rationals evaluate
+                     * "set_field_type" to determine internal storage size. */
+                    int tv_size = TIFFFieldSetGetSize(fip);
+                    raw_data = _TIFFmallocExt(tif, tv_size * value_count);
+                    mem_alloc = 1;
+                    if (TIFFGetField(tif, tag, raw_data) != 1)
+                    {
+                        _TIFFfreeExt(tif, raw_data);
+                        continue;
+                    }
+                }
+            }
 
-			/*
-			 * Catch the tags which needs to be specially handled
-			 * and pretty print them. If tag not handled in
-			 * _TIFFPrettyPrintField() fall down and print it as
-			 * any other tag.
-			 */
-			if (!_TIFFPrettyPrintField(tif, fip, fd, tag, value_count, raw_data))
-				_TIFFPrintField(fd, fip, value_count, raw_data);
+            /*
+             * Catch the tags which needs to be specially handled
+             * and pretty print them. If tag not handled in
+             * _TIFFPrettyPrintField() fall down and print it as
+             * any other tag.
+             */
+            if (raw_data != NULL &&
+                !_TIFFPrettyPrintField(tif, fip, fd, tag, value_count,
+                                       raw_data))
+                _TIFFPrintField(fd, fip, value_count, raw_data);
 
-			if(mem_alloc)
-				_TIFFfree(raw_data);
-		}
-	}
-        
-	if (tif->tif_tagmethods.printdir)
-		(*tif->tif_tagmethods.printdir)(tif, fd, flags);
+            if (mem_alloc)
+                _TIFFfreeExt(tif, raw_data);
+        }
+    }
 
-	if ((flags & TIFFPRINT_STRIPS) &&
-	    TIFFFieldSet(tif,FIELD_STRIPOFFSETS)) {
-		uint32 s;
+    if (tif->tif_tagmethods.printdir)
+        (*tif->tif_tagmethods.printdir)(tif, fd, flags);
 
-		fprintf(fd, "  %lu %s:\n",
-		    (unsigned long) td->td_nstrips,
-		    isTiled(tif) ? "Tiles" : "Strips");
-		for (s = 0; s < td->td_nstrips; s++)
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-			fprintf(fd, "    %3lu: [%8I64u, %8I64u]\n",
-			    (unsigned long) s,
-			    (unsigned __int64) TIFFGetStrileOffset(tif, s),
-			    (unsigned __int64) TIFFGetStrileByteCount(tif, s));
-#else
-			fprintf(fd, "    %3lu: [%8llu, %8llu]\n",
-			    (unsigned long) s,
-			    (unsigned long long) TIFFGetStrileOffset(tif, s),
-			    (unsigned long long) TIFFGetStrileByteCount(tif, s));
-#endif
-	}
+    if ((flags & TIFFPRINT_STRIPS) && TIFFFieldSet(tif, FIELD_STRIPOFFSETS))
+    {
+        uint32_t s;
+
+        fprintf(fd, "  %" PRIu32 " %s:\n", td->td_nstrips,
+                isTiled(tif) ? "Tiles" : "Strips");
+        for (s = 0; s < td->td_nstrips; s++)
+            fprintf(fd, "    %3" PRIu32 ": [%8" PRIu64 ", %8" PRIu64 "]\n", s,
+                    TIFFGetStrileOffset(tif, s),
+                    TIFFGetStrileByteCount(tif, s));
+    }
 }
 
-void
-_TIFFprintAscii(FILE* fd, const char* cp)
+void _TIFFprintAscii(FILE *fd, const char *cp)
 {
-	_TIFFprintAsciiBounded( fd, cp, strlen(cp));
+    _TIFFprintAsciiBounded(fd, cp, strlen(cp));
 }
 
-static void
-_TIFFprintAsciiBounded(FILE* fd, const char* cp, size_t max_chars)
+static void _TIFFprintAsciiBounded(FILE *fd, const char *cp, size_t max_chars)
 {
-	for (; max_chars > 0 && *cp != '\0'; cp++, max_chars--) {
-		const char* tp;
+    for (; max_chars > 0 && *cp != '\0'; cp++, max_chars--)
+    {
+        const char *tp;
 
-		if (isprint((int)*cp)) {
-			fputc(*cp, fd);
-			continue;
-		}
-		for (tp = "\tt\bb\rr\nn\vv"; *tp; tp++)
-			if (*tp++ == *cp)
-				break;
-		if (*tp)
-			fprintf(fd, "\\%c", *tp);
-		else
-			fprintf(fd, "\\%03o", *cp & 0xff);
-	}
+        if (isprint((int)*cp))
+        {
+            fputc(*cp, fd);
+            continue;
+        }
+        for (tp = "\tt\bb\rr\nn\vv"; *tp; tp++)
+            if (*tp++ == *cp)
+                break;
+        if (*tp)
+            fprintf(fd, "\\%c", *tp);
+        else
+            fprintf(fd, "\\%03o", *cp & 0xff);
+    }
 }
 
-void
-_TIFFprintAsciiTag(FILE* fd, const char* name, const char* value)
+void _TIFFprintAsciiTag(FILE *fd, const char *name, const char *value)
 {
-	fprintf(fd, "  %s: \"", name);
-	_TIFFprintAscii(fd, value);
-	fprintf(fd, "\"\n");
+    fprintf(fd, "  %s: \"", name);
+    _TIFFprintAscii(fd, value);
+    fprintf(fd, "\"\n");
 }
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_read.c b/third_party/libtiff/tif_read.c
index d1acf33..4fec839 100644
--- a/third_party/libtiff/tif_read.c
+++ b/third_party/libtiff/tif_read.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -29,292 +29,273 @@
 #include "tiffiop.h"
 #include <stdio.h>
 
-int TIFFFillStrip(TIFF* tif, uint32 strip);
-int TIFFFillTile(TIFF* tif, uint32 tile);
-static int TIFFStartStrip(TIFF* tif, uint32 strip);
-static int TIFFStartTile(TIFF* tif, uint32 tile);
-static int TIFFCheckRead(TIFF*, int);
-static tmsize_t
-TIFFReadRawStrip1(TIFF* tif, uint32 strip, void* buf, tmsize_t size,const char* module);
-static tmsize_t
-TIFFReadRawTile1(TIFF* tif, uint32 tile, void* buf, tmsize_t size, const char* module);
+int TIFFFillStrip(TIFF *tif, uint32_t strip);
+int TIFFFillTile(TIFF *tif, uint32_t tile);
+static int TIFFStartStrip(TIFF *tif, uint32_t strip);
+static int TIFFStartTile(TIFF *tif, uint32_t tile);
+static int TIFFCheckRead(TIFF *, int);
+static tmsize_t TIFFReadRawStrip1(TIFF *tif, uint32_t strip, void *buf,
+                                  tmsize_t size, const char *module);
+static tmsize_t TIFFReadRawTile1(TIFF *tif, uint32_t tile, void *buf,
+                                 tmsize_t size, const char *module);
 
-#define NOSTRIP ((uint32)(-1))       /* undefined state */
-#define NOTILE ((uint32)(-1))         /* undefined state */
+#define NOSTRIP ((uint32_t)(-1)) /* undefined state */
+#define NOTILE ((uint32_t)(-1))  /* undefined state */
 
 #define INITIAL_THRESHOLD (1024 * 1024)
 #define THRESHOLD_MULTIPLIER 10
-#define MAX_THRESHOLD (THRESHOLD_MULTIPLIER * THRESHOLD_MULTIPLIER * THRESHOLD_MULTIPLIER * INITIAL_THRESHOLD)
+#define MAX_THRESHOLD                                                          \
+    (THRESHOLD_MULTIPLIER * THRESHOLD_MULTIPLIER * THRESHOLD_MULTIPLIER *      \
+     INITIAL_THRESHOLD)
 
-#define TIFF_INT64_MAX ((((int64)0x7FFFFFFF) << 32) | 0xFFFFFFFF)
+#define TIFF_INT64_MAX ((((int64_t)0x7FFFFFFF) << 32) | 0xFFFFFFFF)
 
 /* Read 'size' bytes in tif_rawdata buffer starting at offset 'rawdata_offset'
  * Returns 1 in case of success, 0 otherwise. */
-static int TIFFReadAndRealloc( TIFF* tif, tmsize_t size,
-                               tmsize_t rawdata_offset,
-                               int is_strip, uint32 strip_or_tile,
-                               const char* module )
+static int TIFFReadAndRealloc(TIFF *tif, tmsize_t size, tmsize_t rawdata_offset,
+                              int is_strip, uint32_t strip_or_tile,
+                              const char *module)
 {
 #if SIZEOF_SIZE_T == 8
-        tmsize_t threshold = INITIAL_THRESHOLD;
+    tmsize_t threshold = INITIAL_THRESHOLD;
 #endif
-        tmsize_t already_read = 0;
-
+    tmsize_t already_read = 0;
 
 #if SIZEOF_SIZE_T != 8
-        /* On 32 bit processes, if the request is large enough, check against */
-        /* file size */
-        if( size > 1000 * 1000 * 1000 )
+    /* On 32 bit processes, if the request is large enough, check against */
+    /* file size */
+    if (size > 1000 * 1000 * 1000)
+    {
+        uint64_t filesize = TIFFGetFileSize(tif);
+        if ((uint64_t)size >= filesize)
         {
-            uint64 filesize = TIFFGetFileSize(tif);
-            if( (uint64)size >= filesize )
-            {
-                TIFFErrorExt(tif->tif_clientdata, module,
-                             "Chunk size requested is larger than file size.");
-                return 0;
-            }
+            TIFFErrorExtR(tif, module,
+                          "Chunk size requested is larger than file size.");
+            return 0;
         }
+    }
 #endif
 
-        /* On 64 bit processes, read first a maximum of 1 MB, then 10 MB, etc */
-        /* so as to avoid allocating too much memory in case the file is too */
-        /* short. We could ask for the file size, but this might be */
-        /* expensive with some I/O layers (think of reading a gzipped file) */
-        /* Restrict to 64 bit processes, so as to avoid reallocs() */
-        /* on 32 bit processes where virtual memory is scarce.  */
-        while( already_read < size )
-        {
-            tmsize_t bytes_read;
-            tmsize_t to_read = size - already_read;
+    /* On 64 bit processes, read first a maximum of 1 MB, then 10 MB, etc */
+    /* so as to avoid allocating too much memory in case the file is too */
+    /* short. We could ask for the file size, but this might be */
+    /* expensive with some I/O layers (think of reading a gzipped file) */
+    /* Restrict to 64 bit processes, so as to avoid reallocs() */
+    /* on 32 bit processes where virtual memory is scarce.  */
+    while (already_read < size)
+    {
+        tmsize_t bytes_read;
+        tmsize_t to_read = size - already_read;
 #if SIZEOF_SIZE_T == 8
-            if( to_read >= threshold && threshold < MAX_THRESHOLD &&
-                already_read + to_read + rawdata_offset > tif->tif_rawdatasize )
-            {
-                to_read = threshold;
-                threshold *= THRESHOLD_MULTIPLIER;
-            }
-#endif
-            if (already_read + to_read + rawdata_offset > tif->tif_rawdatasize) {
-                uint8* new_rawdata;
-                assert((tif->tif_flags & TIFF_MYBUFFER) != 0);
-                tif->tif_rawdatasize = (tmsize_t)TIFFroundup_64(
-                        (uint64)already_read + to_read + rawdata_offset, 1024);
-                if (tif->tif_rawdatasize==0) {
-                    TIFFErrorExt(tif->tif_clientdata, module,
-                                "Invalid buffer size");
-                    return 0;
-                }
-                new_rawdata = (uint8*) _TIFFrealloc(
-                                tif->tif_rawdata, tif->tif_rawdatasize);
-                if( new_rawdata == 0 )
-                {
-                    TIFFErrorExt(tif->tif_clientdata, module,
-                        "No space for data buffer at scanline %lu",
-                        (unsigned long) tif->tif_row);
-                    _TIFFfree(tif->tif_rawdata);
-                    tif->tif_rawdata = 0;
-                    tif->tif_rawdatasize = 0;
-                    return 0;
-                }
-                tif->tif_rawdata = new_rawdata;
-            }
-            if( tif->tif_rawdata == NULL )
-            {
-                /* should not happen in practice but helps CoverityScan */
-                return 0;
-            }
-
-            bytes_read = TIFFReadFile(tif,
-                tif->tif_rawdata + rawdata_offset + already_read, to_read);
-            already_read += bytes_read;
-            if (bytes_read != to_read) {
-                memset( tif->tif_rawdata + rawdata_offset + already_read, 0,
-                        tif->tif_rawdatasize - rawdata_offset - already_read );
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-                if( is_strip )
-                {
-                    TIFFErrorExt(tif->tif_clientdata, module,
-                        "Read error at scanline %lu; got %I64u bytes, "
-                        "expected %I64u",
-                                        (unsigned long) tif->tif_row,
-                                        (unsigned __int64) already_read,
-                                        (unsigned __int64) size);
-                }
-                else
-                {
-                    TIFFErrorExt(tif->tif_clientdata, module,
-                        "Read error at row %lu, col %lu, tile %lu; "
-                        "got %I64u bytes, expected %I64u",
-                                        (unsigned long) tif->tif_row,
-                                        (unsigned long) tif->tif_col,
-                                        (unsigned long) strip_or_tile,
-                                        (unsigned __int64) already_read,
-                                        (unsigned __int64) size);
-                }
-#else
-                if( is_strip )
-                {
-                    TIFFErrorExt(tif->tif_clientdata, module,
-                        "Read error at scanline %lu; got %llu bytes, "
-                        "expected %llu",
-                                        (unsigned long) tif->tif_row,
-                                        (unsigned long long) already_read,
-                                        (unsigned long long) size);
-                }
-                else
-                {
-                    TIFFErrorExt(tif->tif_clientdata, module,
-                        "Read error at row %lu, col %lu, tile %lu; "
-                        "got %llu bytes, expected %llu",
-                                        (unsigned long) tif->tif_row,
-                                        (unsigned long) tif->tif_col,
-                                        (unsigned long) strip_or_tile,
-                                        (unsigned long long) already_read,
-                                        (unsigned long long) size);
-                }
-#endif
-                return 0;
-            }
+        if (to_read >= threshold && threshold < MAX_THRESHOLD &&
+            already_read + to_read + rawdata_offset > tif->tif_rawdatasize)
+        {
+            to_read = threshold;
+            threshold *= THRESHOLD_MULTIPLIER;
         }
-        return 1;
+#endif
+        if (already_read + to_read + rawdata_offset > tif->tif_rawdatasize)
+        {
+            uint8_t *new_rawdata;
+            assert((tif->tif_flags & TIFF_MYBUFFER) != 0);
+            tif->tif_rawdatasize = (tmsize_t)TIFFroundup_64(
+                (uint64_t)already_read + to_read + rawdata_offset, 1024);
+            if (tif->tif_rawdatasize == 0)
+            {
+                TIFFErrorExtR(tif, module, "Invalid buffer size");
+                return 0;
+            }
+            new_rawdata =
+                (uint8_t *)_TIFFrealloc(tif->tif_rawdata, tif->tif_rawdatasize);
+            if (new_rawdata == 0)
+            {
+                TIFFErrorExtR(tif, module,
+                              "No space for data buffer at scanline %" PRIu32,
+                              tif->tif_row);
+                _TIFFfreeExt(tif, tif->tif_rawdata);
+                tif->tif_rawdata = 0;
+                tif->tif_rawdatasize = 0;
+                return 0;
+            }
+            tif->tif_rawdata = new_rawdata;
+        }
+        if (tif->tif_rawdata == NULL)
+        {
+            /* should not happen in practice but helps CoverityScan */
+            return 0;
+        }
+
+        bytes_read = TIFFReadFile(
+            tif, tif->tif_rawdata + rawdata_offset + already_read, to_read);
+        already_read += bytes_read;
+        if (bytes_read != to_read)
+        {
+            memset(tif->tif_rawdata + rawdata_offset + already_read, 0,
+                   tif->tif_rawdatasize - rawdata_offset - already_read);
+            if (is_strip)
+            {
+                TIFFErrorExtR(tif, module,
+                              "Read error at scanline %" PRIu32
+                              "; got %" TIFF_SSIZE_FORMAT " bytes, "
+                              "expected %" TIFF_SSIZE_FORMAT,
+                              tif->tif_row, already_read, size);
+            }
+            else
+            {
+                TIFFErrorExtR(tif, module,
+                              "Read error at row %" PRIu32 ", col %" PRIu32
+                              ", tile %" PRIu32 "; "
+                              "got %" TIFF_SSIZE_FORMAT
+                              " bytes, expected %" TIFF_SSIZE_FORMAT "",
+                              tif->tif_row, tif->tif_col, strip_or_tile,
+                              already_read, size);
+            }
+            return 0;
+        }
+    }
+    return 1;
 }
 
-
-static int
-TIFFFillStripPartial( TIFF *tif, int strip, tmsize_t read_ahead, int restart )
+static int TIFFFillStripPartial(TIFF *tif, int strip, tmsize_t read_ahead,
+                                int restart)
 {
-	static const char module[] = "TIFFFillStripPartial";
-	register TIFFDirectory *td = &tif->tif_dir;
-        tmsize_t unused_data;
-        uint64 read_offset;
-        tmsize_t to_read;
-        tmsize_t read_ahead_mod;
-        /* tmsize_t bytecountm; */
+    static const char module[] = "TIFFFillStripPartial";
+    register TIFFDirectory *td = &tif->tif_dir;
+    tmsize_t unused_data;
+    uint64_t read_offset;
+    tmsize_t to_read;
+    tmsize_t read_ahead_mod;
+    /* tmsize_t bytecountm; */
 
-        /*
-         * Expand raw data buffer, if needed, to hold data
-         * strip coming from file (perhaps should set upper
-         * bound on the size of a buffer we'll use?).
-         */
+    /*
+     * Expand raw data buffer, if needed, to hold data
+     * strip coming from file (perhaps should set upper
+     * bound on the size of a buffer we'll use?).
+     */
 
-        /* bytecountm=(tmsize_t) TIFFGetStrileByteCount(tif, strip); */
+    /* bytecountm=(tmsize_t) TIFFGetStrileByteCount(tif, strip); */
 
-        /* Not completely sure where the * 2 comes from, but probably for */
-        /* an exponentional growth strategy of tif_rawdatasize */
-        if( read_ahead < TIFF_TMSIZE_T_MAX / 2 )
-                read_ahead_mod = read_ahead * 2;
-        else
-                read_ahead_mod = read_ahead;
-        if (read_ahead_mod > tif->tif_rawdatasize) {
-                assert( restart );
-                
-                tif->tif_curstrip = NOSTRIP;
-                if ((tif->tif_flags & TIFF_MYBUFFER) == 0) {
-                        TIFFErrorExt(tif->tif_clientdata, module,
-                                     "Data buffer too small to hold part of strip %lu",
-                                     (unsigned long) strip);
-                        return (0);
-                }
-        }
+    /* Not completely sure where the * 2 comes from, but probably for */
+    /* an exponentional growth strategy of tif_rawdatasize */
+    if (read_ahead < TIFF_TMSIZE_T_MAX / 2)
+        read_ahead_mod = read_ahead * 2;
+    else
+        read_ahead_mod = read_ahead;
+    if (read_ahead_mod > tif->tif_rawdatasize)
+    {
+        assert(restart);
 
-        if( restart )
+        tif->tif_curstrip = NOSTRIP;
+        if ((tif->tif_flags & TIFF_MYBUFFER) == 0)
         {
-                tif->tif_rawdataloaded = 0;
-                tif->tif_rawdataoff = 0;
+            TIFFErrorExtR(tif, module,
+                          "Data buffer too small to hold part of strip %d",
+                          strip);
+            return (0);
         }
+    }
 
-        /*
-        ** If we are reading more data, move any unused data to the
-        ** start of the buffer.
-        */
-        if( tif->tif_rawdataloaded > 0 )
-                unused_data = tif->tif_rawdataloaded - (tif->tif_rawcp - tif->tif_rawdata);
-        else
-                unused_data = 0;
-        
-        if( unused_data > 0 )
-        {
-		assert((tif->tif_flags&TIFF_BUFFERMMAP)==0);
-                memmove( tif->tif_rawdata, tif->tif_rawcp, unused_data );
-        }
+    if (restart)
+    {
+        tif->tif_rawdataloaded = 0;
+        tif->tif_rawdataoff = 0;
+    }
 
-        /*
-        ** Seek to the point in the file where more data should be read.
-        */
-        read_offset = TIFFGetStrileOffset(tif, strip)
-                + tif->tif_rawdataoff + tif->tif_rawdataloaded;
+    /*
+    ** If we are reading more data, move any unused data to the
+    ** start of the buffer.
+    */
+    if (tif->tif_rawdataloaded > 0)
+        unused_data =
+            tif->tif_rawdataloaded - (tif->tif_rawcp - tif->tif_rawdata);
+    else
+        unused_data = 0;
 
-        if (!SeekOK(tif, read_offset)) {
-                TIFFErrorExt(tif->tif_clientdata, module,
-                             "Seek error at scanline %lu, strip %lu",
-                             (unsigned long) tif->tif_row, (unsigned long) strip);
-                return 0;
-        }
+    if (unused_data > 0)
+    {
+        assert((tif->tif_flags & TIFF_BUFFERMMAP) == 0);
+        memmove(tif->tif_rawdata, tif->tif_rawcp, unused_data);
+    }
 
-        /*
-        ** How much do we want to read?
-        */
-        if( read_ahead_mod > tif->tif_rawdatasize )
-                to_read = read_ahead_mod - unused_data;
-        else
-                to_read = tif->tif_rawdatasize - unused_data;
-        if( (uint64) to_read > TIFFGetStrileByteCount(tif, strip)
-            - tif->tif_rawdataoff - tif->tif_rawdataloaded )
-        {
-                to_read = (tmsize_t) TIFFGetStrileByteCount(tif, strip)
-                        - tif->tif_rawdataoff - tif->tif_rawdataloaded;
-        }
+    /*
+    ** Seek to the point in the file where more data should be read.
+    */
+    read_offset = TIFFGetStrileOffset(tif, strip) + tif->tif_rawdataoff +
+                  tif->tif_rawdataloaded;
 
-	assert((tif->tif_flags&TIFF_BUFFERMMAP)==0);
-        if( !TIFFReadAndRealloc( tif, to_read, unused_data,
-                                 1, /* is_strip */
-                                 0, /* strip_or_tile */
-                                 module) )
-        {
-                return 0;
-        }
+    if (!SeekOK(tif, read_offset))
+    {
+        TIFFErrorExtR(tif, module,
+                      "Seek error at scanline %" PRIu32 ", strip %d",
+                      tif->tif_row, strip);
+        return 0;
+    }
 
-        tif->tif_rawdataoff = tif->tif_rawdataoff + tif->tif_rawdataloaded - unused_data ;
-        tif->tif_rawdataloaded = unused_data + to_read;
+    /*
+    ** How much do we want to read?
+    */
+    if (read_ahead_mod > tif->tif_rawdatasize)
+        to_read = read_ahead_mod - unused_data;
+    else
+        to_read = tif->tif_rawdatasize - unused_data;
+    if ((uint64_t)to_read > TIFFGetStrileByteCount(tif, strip) -
+                                tif->tif_rawdataoff - tif->tif_rawdataloaded)
+    {
+        to_read = (tmsize_t)TIFFGetStrileByteCount(tif, strip) -
+                  tif->tif_rawdataoff - tif->tif_rawdataloaded;
+    }
 
-        tif->tif_rawcc = tif->tif_rawdataloaded;
-        tif->tif_rawcp = tif->tif_rawdata;
-                        
-        if (!isFillOrder(tif, td->td_fillorder) &&
-            (tif->tif_flags & TIFF_NOBITREV) == 0) {
-		assert((tif->tif_flags&TIFF_BUFFERMMAP)==0);
-                TIFFReverseBits(tif->tif_rawdata + unused_data, to_read );
-	}
+    assert((tif->tif_flags & TIFF_BUFFERMMAP) == 0);
+    if (!TIFFReadAndRealloc(tif, to_read, unused_data, 1, /* is_strip */
+                            0,                            /* strip_or_tile */
+                            module))
+    {
+        return 0;
+    }
 
-        /*
-        ** When starting a strip from the beginning we need to
-        ** restart the decoder.
-        */
-        if( restart )
-        {
+    tif->tif_rawdataoff =
+        tif->tif_rawdataoff + tif->tif_rawdataloaded - unused_data;
+    tif->tif_rawdataloaded = unused_data + to_read;
+
+    tif->tif_rawcc = tif->tif_rawdataloaded;
+    tif->tif_rawcp = tif->tif_rawdata;
+
+    if (!isFillOrder(tif, td->td_fillorder) &&
+        (tif->tif_flags & TIFF_NOBITREV) == 0)
+    {
+        assert((tif->tif_flags & TIFF_BUFFERMMAP) == 0);
+        TIFFReverseBits(tif->tif_rawdata + unused_data, to_read);
+    }
+
+    /*
+    ** When starting a strip from the beginning we need to
+    ** restart the decoder.
+    */
+    if (restart)
+    {
 
 #ifdef JPEG_SUPPORT
-            /* A bit messy since breaks the codec abstraction. Ultimately */
-            /* there should be a function pointer for that, but it seems */
-            /* only JPEG is affected. */
-            /* For JPEG, if there are multiple scans (can generally be known */
-            /* with the  read_ahead used), we need to read the whole strip */
-            if( tif->tif_dir.td_compression==COMPRESSION_JPEG &&
-                (uint64)tif->tif_rawcc < TIFFGetStrileByteCount(tif, strip) )
+        /* A bit messy since breaks the codec abstraction. Ultimately */
+        /* there should be a function pointer for that, but it seems */
+        /* only JPEG is affected. */
+        /* For JPEG, if there are multiple scans (can generally be known */
+        /* with the  read_ahead used), we need to read the whole strip */
+        if (tif->tif_dir.td_compression == COMPRESSION_JPEG &&
+            (uint64_t)tif->tif_rawcc < TIFFGetStrileByteCount(tif, strip))
+        {
+            if (TIFFJPEGIsFullStripRequired(tif))
             {
-                if( TIFFJPEGIsFullStripRequired(tif) )
-                {
-                    return TIFFFillStrip(tif, strip);
-                }
+                return TIFFFillStrip(tif, strip);
             }
+        }
 #endif
 
-            return TIFFStartStrip(tif, strip);
-        }
-        else
-        {
-                return 1;
-        }
+        return TIFFStartStrip(tif, strip);
+    }
+    else
+    {
+        return 1;
+    }
 }
 
 /*
@@ -325,159 +306,165 @@
  * and avoid reading the whole compressed raw data for big
  * strips.
  */
-static int
-TIFFSeek(TIFF* tif, uint32 row, uint16 sample )
+static int TIFFSeek(TIFF *tif, uint32_t row, uint16_t sample)
 {
-	register TIFFDirectory *td = &tif->tif_dir;
-	uint32 strip;
-        int    whole_strip;
-	tmsize_t read_ahead = 0;
+    register TIFFDirectory *td = &tif->tif_dir;
+    uint32_t strip;
+    int whole_strip;
+    tmsize_t read_ahead = 0;
 
-        /*
-        ** Establish what strip we are working from.
-        */
-	if (row >= td->td_imagelength) {	/* out of range */
-		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-		    "%lu: Row out of range, max %lu",
-		    (unsigned long) row,
-		    (unsigned long) td->td_imagelength);
-		return (0);
-	}
-	if (td->td_planarconfig == PLANARCONFIG_SEPARATE) {
-		if (sample >= td->td_samplesperpixel) {
-			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-			    "%lu: Sample out of range, max %lu",
-			    (unsigned long) sample, (unsigned long) td->td_samplesperpixel);
-			return (0);
-		}
-		strip = (uint32)sample*td->td_stripsperimage + row/td->td_rowsperstrip;
-	} else
-		strip = row / td->td_rowsperstrip;
+    /*
+    ** Establish what strip we are working from.
+    */
+    if (row >= td->td_imagelength)
+    { /* out of range */
+        TIFFErrorExtR(tif, tif->tif_name,
+                      "%" PRIu32 ": Row out of range, max %" PRIu32 "", row,
+                      td->td_imagelength);
+        return (0);
+    }
+    if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
+    {
+        if (sample >= td->td_samplesperpixel)
+        {
+            TIFFErrorExtR(tif, tif->tif_name,
+                          "%" PRIu16 ": Sample out of range, max %" PRIu16 "",
+                          sample, td->td_samplesperpixel);
+            return (0);
+        }
+        strip = (uint32_t)sample * td->td_stripsperimage +
+                row / td->td_rowsperstrip;
+    }
+    else
+        strip = row / td->td_rowsperstrip;
 
         /*
          * Do we want to treat this strip as one whole chunk or
          * read it a few lines at a time?
          */
 #if defined(CHUNKY_STRIP_READ_SUPPORT)
-        whole_strip = TIFFGetStrileByteCount(tif, strip) < 10
-                || isMapped(tif);
-        if( td->td_compression == COMPRESSION_LERC ||
-            td->td_compression == COMPRESSION_JBIG )
-        {
-            /* Ideally plugins should have a way to declare they don't support
-             * chunk strip */
-            whole_strip = 1;
-        }
-#else
+    whole_strip = TIFFGetStrileByteCount(tif, strip) < 10 || isMapped(tif);
+    if (td->td_compression == COMPRESSION_LERC ||
+        td->td_compression == COMPRESSION_JBIG)
+    {
+        /* Ideally plugins should have a way to declare they don't support
+         * chunk strip */
         whole_strip = 1;
+    }
+#else
+    whole_strip = 1;
 #endif
-        
-        if( !whole_strip )
-        {
-                /* 16 is for YCbCr mode where we may need to read 16 */
-                /* lines at a time to get a decompressed line, and 5000 */
-                /* is some constant value, for example for JPEG tables */
-                if( tif->tif_scanlinesize < TIFF_TMSIZE_T_MAX / 16 &&
-                    tif->tif_scanlinesize * 16 < TIFF_TMSIZE_T_MAX - 5000 )
-                {
-                        read_ahead = tif->tif_scanlinesize * 16 + 5000;
-                }
-                else
-                {
-                        read_ahead = tif->tif_scanlinesize;
-                }
-        }
 
+    if (!whole_strip)
+    {
+        /* 16 is for YCbCr mode where we may need to read 16 */
+        /* lines at a time to get a decompressed line, and 5000 */
+        /* is some constant value, for example for JPEG tables */
+        if (tif->tif_scanlinesize < TIFF_TMSIZE_T_MAX / 16 &&
+            tif->tif_scanlinesize * 16 < TIFF_TMSIZE_T_MAX - 5000)
+        {
+            read_ahead = tif->tif_scanlinesize * 16 + 5000;
+        }
+        else
+        {
+            read_ahead = tif->tif_scanlinesize;
+        }
+    }
+
+    /*
+     * If we haven't loaded this strip, do so now, possibly
+     * only reading the first part.
+     */
+    if (strip != tif->tif_curstrip)
+    { /* different strip, refill */
+
+        if (whole_strip)
+        {
+            if (!TIFFFillStrip(tif, strip))
+                return (0);
+        }
+        else
+        {
+            if (!TIFFFillStripPartial(tif, strip, read_ahead, 1))
+                return 0;
+        }
+    }
+
+    /*
+    ** If we already have some data loaded, do we need to read some more?
+    */
+    else if (!whole_strip)
+    {
+        if (((tif->tif_rawdata + tif->tif_rawdataloaded) - tif->tif_rawcp) <
+                read_ahead &&
+            (uint64_t)tif->tif_rawdataoff + tif->tif_rawdataloaded <
+                TIFFGetStrileByteCount(tif, strip))
+        {
+            if (!TIFFFillStripPartial(tif, strip, read_ahead, 0))
+                return 0;
+        }
+    }
+
+    if (row < tif->tif_row)
+    {
         /*
-         * If we haven't loaded this strip, do so now, possibly
-         * only reading the first part.
+         * Moving backwards within the same strip: backup
+         * to the start and then decode forward (below).
+         *
+         * NB: If you're planning on lots of random access within a
+         * strip, it's better to just read and decode the entire
+         * strip, and then access the decoded data in a random fashion.
          */
-	if (strip != tif->tif_curstrip) {	/* different strip, refill */
-                
-                if( whole_strip )
-                {
-                        if (!TIFFFillStrip(tif, strip))
-                                return (0);
-                }
-                else
-                {
-                        if( !TIFFFillStripPartial(tif,strip,read_ahead,1) )
-                                return 0;
-                }
-	}
 
-        /*
-        ** If we already have some data loaded, do we need to read some more?
-        */
-        else if( !whole_strip )
+        if (tif->tif_rawdataoff != 0)
         {
-                if( ((tif->tif_rawdata + tif->tif_rawdataloaded) - tif->tif_rawcp) < read_ahead 
-                    && (uint64) tif->tif_rawdataoff+tif->tif_rawdataloaded < TIFFGetStrileByteCount(tif, strip) )
-                {
-                        if( !TIFFFillStripPartial(tif,strip,read_ahead,0) )
-                                return 0;
-                }
+            if (!TIFFFillStripPartial(tif, strip, read_ahead, 1))
+                return 0;
         }
+        else
+        {
+            if (!TIFFStartStrip(tif, strip))
+                return (0);
+        }
+    }
 
-        if (row < tif->tif_row) {
-		/*
-		 * Moving backwards within the same strip: backup
-		 * to the start and then decode forward (below).
-		 *
-		 * NB: If you're planning on lots of random access within a
-		 * strip, it's better to just read and decode the entire
-		 * strip, and then access the decoded data in a random fashion.
-		 */
+    if (row != tif->tif_row)
+    {
+        /*
+         * Seek forward to the desired row.
+         */
 
-                if( tif->tif_rawdataoff != 0 )
-                {
-                        if( !TIFFFillStripPartial(tif,strip,read_ahead,1) )
-                                return 0;
-                }
-                else
-                {
-                        if (!TIFFStartStrip(tif, strip))
-                                return (0);
-                }
-	}
-        
-	if (row != tif->tif_row) {
-		/*
-		 * Seek forward to the desired row.
-		 */
+        /* TODO: Will this really work with partial buffers? */
 
-                /* TODO: Will this really work with partial buffers? */
-                
-		if (!(*tif->tif_seek)(tif, row - tif->tif_row))
-			return (0);
-		tif->tif_row = row;
-	}
+        if (!(*tif->tif_seek)(tif, row - tif->tif_row))
+            return (0);
+        tif->tif_row = row;
+    }
 
-	return (1);
+    return (1);
 }
 
-int
-TIFFReadScanline(TIFF* tif, void* buf, uint32 row, uint16 sample)
+int TIFFReadScanline(TIFF *tif, void *buf, uint32_t row, uint16_t sample)
 {
-	int e;
+    int e;
 
-	if (!TIFFCheckRead(tif, 0))
-		return (-1);
-	if( (e = TIFFSeek(tif, row, sample)) != 0) {
-		/*
-		 * Decompress desired row into user buffer.
-		 */
-		e = (*tif->tif_decoderow)
-		    (tif, (uint8*) buf, tif->tif_scanlinesize, sample);  
+    if (!TIFFCheckRead(tif, 0))
+        return (-1);
+    if ((e = TIFFSeek(tif, row, sample)) != 0)
+    {
+        /*
+         * Decompress desired row into user buffer.
+         */
+        e = (*tif->tif_decoderow)(tif, (uint8_t *)buf, tif->tif_scanlinesize,
+                                  sample);
 
-		/* we are now poised at the beginning of the next row */
-		tif->tif_row = row + 1;
+        /* we are now poised at the beginning of the next row */
+        tif->tif_row = row + 1;
 
-		if (e)
-			(*tif->tif_postdecode)(tif, (uint8*) buf,
-			    tif->tif_scanlinesize);  
-	}
-	return (e > 0 ? 1 : -1);
+        if (e)
+            (*tif->tif_postdecode)(tif, (uint8_t *)buf, tif->tif_scanlinesize);
+    }
+    return (e > 0 ? 1 : -1);
 }
 
 /*
@@ -485,473 +472,436 @@
  * rows in the strip (check for truncated last strip on any
  * of the separations).
  */
-static tmsize_t TIFFReadEncodedStripGetStripSize(TIFF* tif, uint32 strip, uint16* pplane)
+static tmsize_t TIFFReadEncodedStripGetStripSize(TIFF *tif, uint32_t strip,
+                                                 uint16_t *pplane)
 {
-	static const char module[] = "TIFFReadEncodedStrip";
-	TIFFDirectory *td = &tif->tif_dir;
-	uint32 rowsperstrip;
-	uint32 stripsperplane;
-	uint32 stripinplane;
-	uint32 rows;
-	tmsize_t stripsize;
-	if (!TIFFCheckRead(tif,0))
-		return((tmsize_t)(-1));
-	if (strip>=td->td_nstrips)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,
-		    "%lu: Strip out of range, max %lu",(unsigned long)strip,
-		    (unsigned long)td->td_nstrips);
-		return((tmsize_t)(-1));
-	}
+    static const char module[] = "TIFFReadEncodedStrip";
+    TIFFDirectory *td = &tif->tif_dir;
+    uint32_t rowsperstrip;
+    uint32_t stripsperplane;
+    uint32_t stripinplane;
+    uint32_t rows;
+    tmsize_t stripsize;
+    if (!TIFFCheckRead(tif, 0))
+        return ((tmsize_t)(-1));
+    if (strip >= td->td_nstrips)
+    {
+        TIFFErrorExtR(tif, module,
+                      "%" PRIu32 ": Strip out of range, max %" PRIu32, strip,
+                      td->td_nstrips);
+        return ((tmsize_t)(-1));
+    }
 
-	rowsperstrip=td->td_rowsperstrip;
-	if (rowsperstrip>td->td_imagelength)
-		rowsperstrip=td->td_imagelength;
-	stripsperplane= TIFFhowmany_32_maxuint_compat(td->td_imagelength, rowsperstrip);
-	stripinplane=(strip%stripsperplane);
-	if( pplane ) *pplane=(uint16)(strip/stripsperplane);
-	rows=td->td_imagelength-stripinplane*rowsperstrip;
-	if (rows>rowsperstrip)
-		rows=rowsperstrip;
-	stripsize=TIFFVStripSize(tif,rows);
-	if (stripsize==0)
-		return((tmsize_t)(-1));
-	return stripsize;
+    rowsperstrip = td->td_rowsperstrip;
+    if (rowsperstrip > td->td_imagelength)
+        rowsperstrip = td->td_imagelength;
+    stripsperplane =
+        TIFFhowmany_32_maxuint_compat(td->td_imagelength, rowsperstrip);
+    stripinplane = (strip % stripsperplane);
+    if (pplane)
+        *pplane = (uint16_t)(strip / stripsperplane);
+    rows = td->td_imagelength - stripinplane * rowsperstrip;
+    if (rows > rowsperstrip)
+        rows = rowsperstrip;
+    stripsize = TIFFVStripSize(tif, rows);
+    if (stripsize == 0)
+        return ((tmsize_t)(-1));
+    return stripsize;
 }
 
 /*
  * Read a strip of data and decompress the specified
  * amount into the user-supplied buffer.
  */
-tmsize_t
-TIFFReadEncodedStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size)
+tmsize_t TIFFReadEncodedStrip(TIFF *tif, uint32_t strip, void *buf,
+                              tmsize_t size)
 {
-	static const char module[] = "TIFFReadEncodedStrip";
-	TIFFDirectory *td = &tif->tif_dir;
-	tmsize_t stripsize;
-	uint16 plane;
+    static const char module[] = "TIFFReadEncodedStrip";
+    TIFFDirectory *td = &tif->tif_dir;
+    tmsize_t stripsize;
+    uint16_t plane;
 
-	stripsize=TIFFReadEncodedStripGetStripSize(tif, strip, &plane);
-	if (stripsize==((tmsize_t)(-1)))
-		return((tmsize_t)(-1));
+    stripsize = TIFFReadEncodedStripGetStripSize(tif, strip, &plane);
+    if (stripsize == ((tmsize_t)(-1)))
+        return ((tmsize_t)(-1));
 
     /* shortcut to avoid an extra memcpy() */
-    if( td->td_compression == COMPRESSION_NONE &&
-        size!=(tmsize_t)(-1) && size >= stripsize &&
-        !isMapped(tif) &&
-        ((tif->tif_flags&TIFF_NOREADRAW)==0) )
+    if (td->td_compression == COMPRESSION_NONE && size != (tmsize_t)(-1) &&
+        size >= stripsize && !isMapped(tif) &&
+        ((tif->tif_flags & TIFF_NOREADRAW) == 0))
     {
         if (TIFFReadRawStrip1(tif, strip, buf, stripsize, module) != stripsize)
             return ((tmsize_t)(-1));
 
         if (!isFillOrder(tif, td->td_fillorder) &&
             (tif->tif_flags & TIFF_NOBITREV) == 0)
-            TIFFReverseBits(buf,stripsize);
+            TIFFReverseBits(buf, stripsize);
 
-        (*tif->tif_postdecode)(tif,buf,stripsize);
+        (*tif->tif_postdecode)(tif, buf, stripsize);
         return (stripsize);
     }
 
-	if ((size!=(tmsize_t)(-1))&&(size<stripsize))
-		stripsize=size;
-	if (!TIFFFillStrip(tif,strip))
-		return((tmsize_t)(-1));
-	if ((*tif->tif_decodestrip)(tif,buf,stripsize,plane)<=0)
-		return((tmsize_t)(-1));
-	(*tif->tif_postdecode)(tif,buf,stripsize);
-	return(stripsize);
+    if ((size != (tmsize_t)(-1)) && (size < stripsize))
+        stripsize = size;
+    if (!TIFFFillStrip(tif, strip))
+        return ((tmsize_t)(-1));
+    if ((*tif->tif_decodestrip)(tif, buf, stripsize, plane) <= 0)
+        return ((tmsize_t)(-1));
+    (*tif->tif_postdecode)(tif, buf, stripsize);
+    return (stripsize);
 }
 
-/* Variant of TIFFReadEncodedStrip() that does 
- * * if *buf == NULL, *buf = _TIFFmalloc(bufsizetoalloc) only after TIFFFillStrip() has
- *   succeeded. This avoid excessive memory allocation in case of truncated
- *   file.
+/* Variant of TIFFReadEncodedStrip() that does
+ * * if *buf == NULL, *buf = _TIFFmallocExt(tif, bufsizetoalloc) only after
+ * TIFFFillStrip() has succeeded. This avoid excessive memory allocation in case
+ * of truncated file.
  * * calls regular TIFFReadEncodedStrip() if *buf != NULL
  */
-tmsize_t
-_TIFFReadEncodedStripAndAllocBuffer(TIFF* tif, uint32 strip,
-                                    void **buf, tmsize_t bufsizetoalloc,
-                                    tmsize_t size_to_read)
+tmsize_t _TIFFReadEncodedStripAndAllocBuffer(TIFF *tif, uint32_t strip,
+                                             void **buf,
+                                             tmsize_t bufsizetoalloc,
+                                             tmsize_t size_to_read)
 {
-    assert(size_to_read > 0);
-
     tmsize_t this_stripsize;
-    uint16 plane;
+    uint16_t plane;
 
-    if( *buf != NULL )
+    if (*buf != NULL)
     {
         return TIFFReadEncodedStrip(tif, strip, *buf, size_to_read);
     }
 
-    this_stripsize=TIFFReadEncodedStripGetStripSize(tif, strip, &plane);
-    if (this_stripsize==((tmsize_t)(-1)))
-            return((tmsize_t)(-1));
+    this_stripsize = TIFFReadEncodedStripGetStripSize(tif, strip, &plane);
+    if (this_stripsize == ((tmsize_t)(-1)))
+        return ((tmsize_t)(-1));
 
-    if ((size_to_read!=(tmsize_t)(-1))&&(size_to_read<this_stripsize))
-            this_stripsize=size_to_read;
-    if (!TIFFFillStrip(tif,strip))
-            return((tmsize_t)(-1));
+    if ((size_to_read != (tmsize_t)(-1)) && (size_to_read < this_stripsize))
+        this_stripsize = size_to_read;
+    if (!TIFFFillStrip(tif, strip))
+        return ((tmsize_t)(-1));
 
-    *buf = _TIFFmalloc(bufsizetoalloc);
-    if (*buf == NULL) {
-            TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "No space for strip buffer");
-            return((tmsize_t)(-1));
+    *buf = _TIFFmallocExt(tif, bufsizetoalloc);
+    if (*buf == NULL)
+    {
+        TIFFErrorExtR(tif, TIFFFileName(tif), "No space for strip buffer");
+        return ((tmsize_t)(-1));
     }
     _TIFFmemset(*buf, 0, bufsizetoalloc);
 
-    if ((*tif->tif_decodestrip)(tif,*buf,this_stripsize,plane)<=0)
-            return((tmsize_t)(-1));
-    (*tif->tif_postdecode)(tif,*buf,this_stripsize);
-    return(this_stripsize);
-
-
+    if ((*tif->tif_decodestrip)(tif, *buf, this_stripsize, plane) <= 0)
+        return ((tmsize_t)(-1));
+    (*tif->tif_postdecode)(tif, *buf, this_stripsize);
+    return (this_stripsize);
 }
 
-static tmsize_t
-TIFFReadRawStrip1(TIFF* tif, uint32 strip, void* buf, tmsize_t size,
-    const char* module)
+static tmsize_t TIFFReadRawStrip1(TIFF *tif, uint32_t strip, void *buf,
+                                  tmsize_t size, const char *module)
 {
-	assert((tif->tif_flags&TIFF_NOREADRAW)==0);
-	if (!isMapped(tif)) {
-		tmsize_t cc;
+    assert((tif->tif_flags & TIFF_NOREADRAW) == 0);
+    if (!isMapped(tif))
+    {
+        tmsize_t cc;
 
-		if (!SeekOK(tif, TIFFGetStrileOffset(tif, strip))) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Seek error at scanline %lu, strip %lu",
-			    (unsigned long) tif->tif_row, (unsigned long) strip);
-			return ((tmsize_t)(-1));
-		}
-		cc = TIFFReadFile(tif, buf, size);
-		if (cc != size) {
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-			TIFFErrorExt(tif->tif_clientdata, module,
-		"Read error at scanline %lu; got %I64u bytes, expected %I64u",
-				     (unsigned long) tif->tif_row,
-				     (unsigned __int64) cc,
-				     (unsigned __int64) size);
-#else
-			TIFFErrorExt(tif->tif_clientdata, module,
-		"Read error at scanline %lu; got %llu bytes, expected %llu",
-				     (unsigned long) tif->tif_row,
-				     (unsigned long long) cc,
-				     (unsigned long long) size);
-#endif
-			return ((tmsize_t)(-1));
-		}
-	} else {
-		tmsize_t ma = 0;
-		tmsize_t n;
-		if ((TIFFGetStrileOffset(tif, strip) > (uint64)TIFF_TMSIZE_T_MAX)||
-                    ((ma=(tmsize_t)TIFFGetStrileOffset(tif, strip))>tif->tif_size))
-                {
-                    n=0;
-                }
-                else if( ma > TIFF_TMSIZE_T_MAX - size )
-                {
-                    n=0;
-                }
-                else
-                {
-                    tmsize_t mb=ma+size;
-                    if (mb>tif->tif_size)
-                            n=tif->tif_size-ma;
-                    else
-                            n=size;
-                }
-		if (n!=size) {
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-			TIFFErrorExt(tif->tif_clientdata, module,
-	"Read error at scanline %lu, strip %lu; got %I64u bytes, expected %I64u",
-				     (unsigned long) tif->tif_row,
-				     (unsigned long) strip,
-				     (unsigned __int64) n,
-				     (unsigned __int64) size);
-#else
-			TIFFErrorExt(tif->tif_clientdata, module,
-	"Read error at scanline %lu, strip %lu; got %llu bytes, expected %llu",
-				     (unsigned long) tif->tif_row,
-				     (unsigned long) strip,
-				     (unsigned long long) n,
-				     (unsigned long long) size);
-#endif
-			return ((tmsize_t)(-1));
-		}
-		_TIFFmemcpy(buf, tif->tif_base + ma,
-			    size);
-	}
-	return (size);
-}
-
-static tmsize_t
-TIFFReadRawStripOrTile2(TIFF* tif, uint32 strip_or_tile, int is_strip,
-                        tmsize_t size, const char* module)
-{
-        assert( !isMapped(tif) );
-        assert((tif->tif_flags&TIFF_NOREADRAW)==0);
-
-        if (!SeekOK(tif, TIFFGetStrileOffset(tif, strip_or_tile))) {
-            if( is_strip )
-            {
-                TIFFErrorExt(tif->tif_clientdata, module,
-                    "Seek error at scanline %lu, strip %lu",
-                    (unsigned long) tif->tif_row,
-                    (unsigned long) strip_or_tile);
-            }
-            else
-            {
-                TIFFErrorExt(tif->tif_clientdata, module,
-                    "Seek error at row %lu, col %lu, tile %lu",
-                    (unsigned long) tif->tif_row,
-                    (unsigned long) tif->tif_col,
-                    (unsigned long) strip_or_tile);
-            }
-            return ((tmsize_t)(-1));
-        }
-
-        if( !TIFFReadAndRealloc( tif, size, 0, is_strip,
-                                 strip_or_tile, module ) )
+        if (!SeekOK(tif, TIFFGetStrileOffset(tif, strip)))
         {
+            TIFFErrorExtR(tif, module,
+                          "Seek error at scanline %" PRIu32 ", strip %" PRIu32,
+                          tif->tif_row, strip);
             return ((tmsize_t)(-1));
         }
+        cc = TIFFReadFile(tif, buf, size);
+        if (cc != size)
+        {
+            TIFFErrorExtR(tif, module,
+                          "Read error at scanline %" PRIu32
+                          "; got %" TIFF_SSIZE_FORMAT
+                          " bytes, expected %" TIFF_SSIZE_FORMAT,
+                          tif->tif_row, cc, size);
+            return ((tmsize_t)(-1));
+        }
+    }
+    else
+    {
+        tmsize_t ma = 0;
+        tmsize_t n;
+        if ((TIFFGetStrileOffset(tif, strip) > (uint64_t)TIFF_TMSIZE_T_MAX) ||
+            ((ma = (tmsize_t)TIFFGetStrileOffset(tif, strip)) > tif->tif_size))
+        {
+            n = 0;
+        }
+        else if (ma > TIFF_TMSIZE_T_MAX - size)
+        {
+            n = 0;
+        }
+        else
+        {
+            tmsize_t mb = ma + size;
+            if (mb > tif->tif_size)
+                n = tif->tif_size - ma;
+            else
+                n = size;
+        }
+        if (n != size)
+        {
+            TIFFErrorExtR(tif, module,
+                          "Read error at scanline %" PRIu32 ", strip %" PRIu32
+                          "; got %" TIFF_SSIZE_FORMAT
+                          " bytes, expected %" TIFF_SSIZE_FORMAT,
+                          tif->tif_row, strip, n, size);
+            return ((tmsize_t)(-1));
+        }
+        _TIFFmemcpy(buf, tif->tif_base + ma, size);
+    }
+    return (size);
+}
 
-        return (size);
+static tmsize_t TIFFReadRawStripOrTile2(TIFF *tif, uint32_t strip_or_tile,
+                                        int is_strip, tmsize_t size,
+                                        const char *module)
+{
+    assert(!isMapped(tif));
+    assert((tif->tif_flags & TIFF_NOREADRAW) == 0);
+
+    if (!SeekOK(tif, TIFFGetStrileOffset(tif, strip_or_tile)))
+    {
+        if (is_strip)
+        {
+            TIFFErrorExtR(tif, module,
+                          "Seek error at scanline %" PRIu32 ", strip %" PRIu32,
+                          tif->tif_row, strip_or_tile);
+        }
+        else
+        {
+            TIFFErrorExtR(tif, module,
+                          "Seek error at row %" PRIu32 ", col %" PRIu32
+                          ", tile %" PRIu32,
+                          tif->tif_row, tif->tif_col, strip_or_tile);
+        }
+        return ((tmsize_t)(-1));
+    }
+
+    if (!TIFFReadAndRealloc(tif, size, 0, is_strip, strip_or_tile, module))
+    {
+        return ((tmsize_t)(-1));
+    }
+
+    return (size);
 }
 
 /*
  * Read a strip of data from the file.
  */
-tmsize_t
-TIFFReadRawStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size)
+tmsize_t TIFFReadRawStrip(TIFF *tif, uint32_t strip, void *buf, tmsize_t size)
 {
-	static const char module[] = "TIFFReadRawStrip";
-	TIFFDirectory *td = &tif->tif_dir;
-	uint64 bytecount64;
-	tmsize_t bytecountm;
+    static const char module[] = "TIFFReadRawStrip";
+    TIFFDirectory *td = &tif->tif_dir;
+    uint64_t bytecount64;
+    tmsize_t bytecountm;
 
-	if (!TIFFCheckRead(tif, 0))
-		return ((tmsize_t)(-1));
-	if (strip >= td->td_nstrips) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-		     "%lu: Strip out of range, max %lu",
-		     (unsigned long) strip,
-		     (unsigned long) td->td_nstrips);
-		return ((tmsize_t)(-1));
-	}
-	if (tif->tif_flags&TIFF_NOREADRAW)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "Compression scheme does not support access to raw uncompressed data");
-		return ((tmsize_t)(-1));
-	}
-	bytecount64 = TIFFGetStrileByteCount(tif, strip);
-	if (size != (tmsize_t)(-1) && (uint64)size <= bytecount64)
-		bytecountm = size;
-	else
-		bytecountm = _TIFFCastUInt64ToSSize(tif, bytecount64, module);
-	if( bytecountm == 0 ) {
-		return ((tmsize_t)(-1));
-	}
-	return (TIFFReadRawStrip1(tif, strip, buf, bytecountm, module));
+    if (!TIFFCheckRead(tif, 0))
+        return ((tmsize_t)(-1));
+    if (strip >= td->td_nstrips)
+    {
+        TIFFErrorExtR(tif, module,
+                      "%" PRIu32 ": Strip out of range, max %" PRIu32, strip,
+                      td->td_nstrips);
+        return ((tmsize_t)(-1));
+    }
+    if (tif->tif_flags & TIFF_NOREADRAW)
+    {
+        TIFFErrorExtR(tif, module,
+                      "Compression scheme does not support access to raw "
+                      "uncompressed data");
+        return ((tmsize_t)(-1));
+    }
+    bytecount64 = TIFFGetStrileByteCount(tif, strip);
+    if (size != (tmsize_t)(-1) && (uint64_t)size <= bytecount64)
+        bytecountm = size;
+    else
+        bytecountm = _TIFFCastUInt64ToSSize(tif, bytecount64, module);
+    if (bytecountm == 0)
+    {
+        return ((tmsize_t)(-1));
+    }
+    return (TIFFReadRawStrip1(tif, strip, buf, bytecountm, module));
 }
 
 TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
-static uint64 NoSantizeSubUInt64(uint64 a, uint64 b)
-{
-    return a - b;
-}
+static uint64_t NoSanitizeSubUInt64(uint64_t a, uint64_t b) { return a - b; }
 
 /*
  * Read the specified strip and setup for decoding. The data buffer is
  * expanded, as necessary, to hold the strip's data.
  */
-int
-TIFFFillStrip(TIFF* tif, uint32 strip)
+int TIFFFillStrip(TIFF *tif, uint32_t strip)
 {
-	static const char module[] = "TIFFFillStrip";
-	TIFFDirectory *td = &tif->tif_dir;
+    static const char module[] = "TIFFFillStrip";
+    TIFFDirectory *td = &tif->tif_dir;
 
-	if ((tif->tif_flags&TIFF_NOREADRAW)==0)
-	{
-		uint64 bytecount = TIFFGetStrileByteCount(tif, strip);
-		if( bytecount == 0 || bytecount > (uint64)TIFF_INT64_MAX ) {
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-			TIFFErrorExt(tif->tif_clientdata, module,
-				"Invalid strip byte count %I64u, strip %lu",
-				     (unsigned __int64) bytecount,
-				     (unsigned long) strip);
-#else
-			TIFFErrorExt(tif->tif_clientdata, module,
-				"Invalid strip byte count %llu, strip %lu",
-				     (unsigned long long) bytecount,
-				     (unsigned long) strip);
-#endif
-			return (0);
-		}
+    if ((tif->tif_flags & TIFF_NOREADRAW) == 0)
+    {
+        uint64_t bytecount = TIFFGetStrileByteCount(tif, strip);
+        if (bytecount == 0 || bytecount > (uint64_t)TIFF_INT64_MAX)
+        {
+            TIFFErrorExtR(tif, module,
+                          "Invalid strip byte count %" PRIu64
+                          ", strip %" PRIu32,
+                          bytecount, strip);
+            return (0);
+        }
 
-		/* To avoid excessive memory allocations: */
-		/* Byte count should normally not be larger than a number of */
-		/* times the uncompressed size plus some margin */
-                if( bytecount > 1024 * 1024 )
+        /* To avoid excessive memory allocations: */
+        /* Byte count should normally not be larger than a number of */
+        /* times the uncompressed size plus some margin */
+        if (bytecount > 1024 * 1024)
+        {
+            /* 10 and 4096 are just values that could be adjusted. */
+            /* Hopefully they are safe enough for all codecs */
+            tmsize_t stripsize = TIFFStripSize(tif);
+            if (stripsize != 0 && (bytecount - 4096) / 10 > (uint64_t)stripsize)
+            {
+                uint64_t newbytecount = (uint64_t)stripsize * 10 + 4096;
+                TIFFErrorExtR(tif, module,
+                              "Too large strip byte count %" PRIu64
+                              ", strip %" PRIu32 ". Limiting to %" PRIu64,
+                              bytecount, strip, newbytecount);
+                bytecount = newbytecount;
+            }
+        }
+
+        if (isMapped(tif))
+        {
+            /*
+             * We must check for overflow, potentially causing
+             * an OOB read. Instead of simple
+             *
+             *  TIFFGetStrileOffset(tif, strip)+bytecount > tif->tif_size
+             *
+             * comparison (which can overflow) we do the following
+             * two comparisons:
+             */
+            if (bytecount > (uint64_t)tif->tif_size ||
+                TIFFGetStrileOffset(tif, strip) >
+                    (uint64_t)tif->tif_size - bytecount)
+            {
+                /*
+                 * This error message might seem strange, but
+                 * it's what would happen if a read were done
+                 * instead.
+                 */
+                TIFFErrorExtR(
+                    tif, module,
+
+                    "Read error on strip %" PRIu32 "; "
+                    "got %" PRIu64 " bytes, expected %" PRIu64,
+                    strip,
+                    NoSanitizeSubUInt64(tif->tif_size,
+                                        TIFFGetStrileOffset(tif, strip)),
+                    bytecount);
+                tif->tif_curstrip = NOSTRIP;
+                return (0);
+            }
+        }
+
+        if (isMapped(tif) && (isFillOrder(tif, td->td_fillorder) ||
+                              (tif->tif_flags & TIFF_NOBITREV)))
+        {
+            /*
+             * The image is mapped into memory and we either don't
+             * need to flip bits or the compression routine is
+             * going to handle this operation itself.  In this
+             * case, avoid copying the raw data and instead just
+             * reference the data from the memory mapped file
+             * image.  This assumes that the decompression
+             * routines do not modify the contents of the raw data
+             * buffer (if they try to, the application will get a
+             * fault since the file is mapped read-only).
+             */
+            if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata)
+            {
+                _TIFFfreeExt(tif, tif->tif_rawdata);
+                tif->tif_rawdata = NULL;
+                tif->tif_rawdatasize = 0;
+            }
+            tif->tif_flags &= ~TIFF_MYBUFFER;
+            tif->tif_rawdatasize = (tmsize_t)bytecount;
+            tif->tif_rawdata =
+                tif->tif_base + (tmsize_t)TIFFGetStrileOffset(tif, strip);
+            tif->tif_rawdataoff = 0;
+            tif->tif_rawdataloaded = (tmsize_t)bytecount;
+
+            /*
+             * When we have tif_rawdata reference directly into the memory
+             * mapped file we need to be pretty careful about how we use the
+             * rawdata.  It is not a general purpose working buffer as it
+             * normally otherwise is.  So we keep track of this fact to avoid
+             * using it improperly.
+             */
+            tif->tif_flags |= TIFF_BUFFERMMAP;
+        }
+        else
+        {
+            /*
+             * Expand raw data buffer, if needed, to hold data
+             * strip coming from file (perhaps should set upper
+             * bound on the size of a buffer we'll use?).
+             */
+            tmsize_t bytecountm;
+            bytecountm = (tmsize_t)bytecount;
+            if ((uint64_t)bytecountm != bytecount)
+            {
+                TIFFErrorExtR(tif, module, "Integer overflow");
+                return (0);
+            }
+            if (bytecountm > tif->tif_rawdatasize)
+            {
+                tif->tif_curstrip = NOSTRIP;
+                if ((tif->tif_flags & TIFF_MYBUFFER) == 0)
                 {
-			/* 10 and 4096 are just values that could be adjusted. */
-			/* Hopefully they are safe enough for all codecs */
-			tmsize_t stripsize = TIFFStripSize(tif);
-			if( stripsize != 0 &&
-			    (bytecount - 4096) / 10 > (uint64)stripsize  )
-			{
-				uint64 newbytecount = (uint64)stripsize * 10 + 4096;
-				if( newbytecount == 0 || newbytecount > (uint64)TIFF_INT64_MAX )
-				{
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-					TIFFWarningExt(tif->tif_clientdata, module,
-					  "Too large strip byte count %I64u, strip %lu. Limiting to %I64u",
-					     (unsigned __int64) bytecount,
-					     (unsigned long) strip,
-					     (unsigned __int64) newbytecount);
-#else
-					TIFFErrorExt(tif->tif_clientdata, module,
-					  "Too large strip byte count %llu, strip %lu. Limiting to %llu",
-					     (unsigned long long) bytecount,
-					     (unsigned long) strip,
-					     (unsigned long long) newbytecount);
-#endif
-					bytecount = newbytecount;
-				}
-			}
-		}
-
-		if (isMapped(tif)) {
-			/*
-			 * We must check for overflow, potentially causing
-			 * an OOB read. Instead of simple
-			 *
-			 *  TIFFGetStrileOffset(tif, strip)+bytecount > tif->tif_size
-			 *
-			 * comparison (which can overflow) we do the following
-			 * two comparisons:
-			 */
-			if (bytecount > (uint64)tif->tif_size ||
-			    TIFFGetStrileOffset(tif, strip) > (uint64)tif->tif_size - bytecount) {
-				/*
-				 * This error message might seem strange, but
-				 * it's what would happen if a read were done
-				 * instead.
-				 */
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-				TIFFErrorExt(tif->tif_clientdata, module,
-
-					"Read error on strip %lu; "
-					"got %I64u bytes, expected %I64u",
-					(unsigned long) strip,
-					(unsigned __int64) NoSantizeSubUInt64(tif->tif_size, TIFFGetStrileOffset(tif, strip)),
-					(unsigned __int64) bytecount);
-#else
-				TIFFErrorExt(tif->tif_clientdata, module,
-
-					"Read error on strip %lu; "
-					"got %llu bytes, expected %llu",
-					(unsigned long) strip,
-					(unsigned long long) NoSantizeSubUInt64(tif->tif_size, TIFFGetStrileOffset(tif, strip)),
-					(unsigned long long) bytecount);
-#endif
-				tif->tif_curstrip = NOSTRIP;
-				return (0);
-			}
-		}
-
-		if (isMapped(tif) &&
-		    (isFillOrder(tif, td->td_fillorder)
-		    || (tif->tif_flags & TIFF_NOBITREV))) {
-			/*
-			 * The image is mapped into memory and we either don't
-			 * need to flip bits or the compression routine is
-			 * going to handle this operation itself.  In this
-			 * case, avoid copying the raw data and instead just
-			 * reference the data from the memory mapped file
-			 * image.  This assumes that the decompression
-			 * routines do not modify the contents of the raw data
-			 * buffer (if they try to, the application will get a
-			 * fault since the file is mapped read-only).
-			 */
-			if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata) {
-				_TIFFfree(tif->tif_rawdata);
-				tif->tif_rawdata = NULL;
-				tif->tif_rawdatasize = 0;
-			}
-			tif->tif_flags &= ~TIFF_MYBUFFER;
-			tif->tif_rawdatasize = (tmsize_t)bytecount;
-			tif->tif_rawdata = tif->tif_base + (tmsize_t)TIFFGetStrileOffset(tif, strip);
-                        tif->tif_rawdataoff = 0;
-                        tif->tif_rawdataloaded = (tmsize_t) bytecount;
-
-			/* 
-			 * When we have tif_rawdata reference directly into the memory mapped file
-			 * we need to be pretty careful about how we use the rawdata.  It is not
-			 * a general purpose working buffer as it normally otherwise is.  So we
-			 * keep track of this fact to avoid using it improperly.
-			 */
-			tif->tif_flags |= TIFF_BUFFERMMAP;
-		} else {
-			/*
-			 * Expand raw data buffer, if needed, to hold data
-			 * strip coming from file (perhaps should set upper
-			 * bound on the size of a buffer we'll use?).
-			 */
-			tmsize_t bytecountm;
-			bytecountm=(tmsize_t)bytecount;
-			if ((uint64)bytecountm!=bytecount)
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow");
-				return(0);
-			}
-			if (bytecountm > tif->tif_rawdatasize) {
-				tif->tif_curstrip = NOSTRIP;
-				if ((tif->tif_flags & TIFF_MYBUFFER) == 0) {
-					TIFFErrorExt(tif->tif_clientdata, module,
-					    "Data buffer too small to hold strip %lu",
-					    (unsigned long) strip);
-					return (0);
-				}
-			}
-			if (tif->tif_flags&TIFF_BUFFERMMAP) {
-				tif->tif_curstrip = NOSTRIP;
-				tif->tif_rawdata = NULL;
-				tif->tif_rawdatasize = 0;
-				tif->tif_flags &= ~TIFF_BUFFERMMAP;
-			}
-
-			if( isMapped(tif) )
-			{
-				if (bytecountm > tif->tif_rawdatasize &&
-				    !TIFFReadBufferSetup(tif, 0, bytecountm))
-				{
-					return (0);
-				}
-				if (TIFFReadRawStrip1(tif, strip, tif->tif_rawdata,
-				    bytecountm, module) != bytecountm)
-				{
-					return (0);
-				}
-			}
-			else
-			{
-				if (TIFFReadRawStripOrTile2(tif, strip, 1,
-				    bytecountm, module) != bytecountm)
-				{
-					return (0);
-				}
-			}
-
-
-                        tif->tif_rawdataoff = 0;
-                        tif->tif_rawdataloaded = bytecountm;
-                        
-			if (!isFillOrder(tif, td->td_fillorder) &&
-			    (tif->tif_flags & TIFF_NOBITREV) == 0)
-				TIFFReverseBits(tif->tif_rawdata, bytecountm);
+                    TIFFErrorExtR(
+                        tif, module,
+                        "Data buffer too small to hold strip %" PRIu32, strip);
+                    return (0);
                 }
-	}
-	return (TIFFStartStrip(tif, strip));
+            }
+            if (tif->tif_flags & TIFF_BUFFERMMAP)
+            {
+                tif->tif_curstrip = NOSTRIP;
+                tif->tif_rawdata = NULL;
+                tif->tif_rawdatasize = 0;
+                tif->tif_flags &= ~TIFF_BUFFERMMAP;
+            }
+
+            if (isMapped(tif))
+            {
+                if (bytecountm > tif->tif_rawdatasize &&
+                    !TIFFReadBufferSetup(tif, 0, bytecountm))
+                {
+                    return (0);
+                }
+                if (TIFFReadRawStrip1(tif, strip, tif->tif_rawdata, bytecountm,
+                                      module) != bytecountm)
+                {
+                    return (0);
+                }
+            }
+            else
+            {
+                if (TIFFReadRawStripOrTile2(tif, strip, 1, bytecountm,
+                                            module) != bytecountm)
+                {
+                    return (0);
+                }
+            }
+
+            tif->tif_rawdataoff = 0;
+            tif->tif_rawdataloaded = bytecountm;
+
+            if (!isFillOrder(tif, td->td_fillorder) &&
+                (tif->tif_flags & TIFF_NOBITREV) == 0)
+                TIFFReverseBits(tif->tif_rawdata, bytecountm);
+        }
+    }
+    return (TIFFStartStrip(tif, strip));
 }
 
 /*
@@ -963,120 +913,162 @@
  * Read and decompress a tile of data.  The
  * tile is selected by the (x,y,z,s) coordinates.
  */
-tmsize_t
-TIFFReadTile(TIFF* tif, void* buf, uint32 x, uint32 y, uint32 z, uint16 s)
+tmsize_t TIFFReadTile(TIFF *tif, void *buf, uint32_t x, uint32_t y, uint32_t z,
+                      uint16_t s)
 {
-	if (!TIFFCheckRead(tif, 1) || !TIFFCheckTile(tif, x, y, z, s))
-		return ((tmsize_t)(-1));
-	return (TIFFReadEncodedTile(tif,
-	    TIFFComputeTile(tif, x, y, z, s), buf, (tmsize_t)(-1)));
+    if (!TIFFCheckRead(tif, 1) || !TIFFCheckTile(tif, x, y, z, s))
+        return ((tmsize_t)(-1));
+    return (TIFFReadEncodedTile(tif, TIFFComputeTile(tif, x, y, z, s), buf,
+                                (tmsize_t)(-1)));
 }
 
 /*
  * Read a tile of data and decompress the specified
  * amount into the user-supplied buffer.
  */
-tmsize_t
-TIFFReadEncodedTile(TIFF* tif, uint32 tile, void* buf, tmsize_t size)
+tmsize_t TIFFReadEncodedTile(TIFF *tif, uint32_t tile, void *buf, tmsize_t size)
 {
-	static const char module[] = "TIFFReadEncodedTile";
-	TIFFDirectory *td = &tif->tif_dir;
-	tmsize_t tilesize = tif->tif_tilesize;
+    static const char module[] = "TIFFReadEncodedTile";
+    TIFFDirectory *td = &tif->tif_dir;
+    tmsize_t tilesize = tif->tif_tilesize;
 
-	if (!TIFFCheckRead(tif, 1))
-		return ((tmsize_t)(-1));
-	if (tile >= td->td_nstrips) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "%lu: Tile out of range, max %lu",
-		    (unsigned long) tile, (unsigned long) td->td_nstrips);
-		return ((tmsize_t)(-1));
-	}
+    if (!TIFFCheckRead(tif, 1))
+        return ((tmsize_t)(-1));
+    if (tile >= td->td_nstrips)
+    {
+        TIFFErrorExtR(tif, module,
+                      "%" PRIu32 ": Tile out of range, max %" PRIu32, tile,
+                      td->td_nstrips);
+        return ((tmsize_t)(-1));
+    }
 
     /* shortcut to avoid an extra memcpy() */
-    if( td->td_compression == COMPRESSION_NONE &&
-        size!=(tmsize_t)(-1) && size >= tilesize &&
-        !isMapped(tif) &&
-        ((tif->tif_flags&TIFF_NOREADRAW)==0) )
+    if (td->td_compression == COMPRESSION_NONE && size != (tmsize_t)(-1) &&
+        size >= tilesize && !isMapped(tif) &&
+        ((tif->tif_flags & TIFF_NOREADRAW) == 0))
     {
         if (TIFFReadRawTile1(tif, tile, buf, tilesize, module) != tilesize)
             return ((tmsize_t)(-1));
 
         if (!isFillOrder(tif, td->td_fillorder) &&
             (tif->tif_flags & TIFF_NOBITREV) == 0)
-            TIFFReverseBits(buf,tilesize);
+            TIFFReverseBits(buf, tilesize);
 
-        (*tif->tif_postdecode)(tif,buf,tilesize);
+        (*tif->tif_postdecode)(tif, buf, tilesize);
         return (tilesize);
     }
 
-	if (size == (tmsize_t)(-1))
-		size = tilesize;
-	else if (size > tilesize)
-		size = tilesize;
-	if (TIFFFillTile(tif, tile) && (*tif->tif_decodetile)(tif,
-	    (uint8*) buf, size, (uint16)(tile/td->td_stripsperimage))) {
-		(*tif->tif_postdecode)(tif, (uint8*) buf, size);
-		return (size);
-	} else
-		return ((tmsize_t)(-1));
+    if (size == (tmsize_t)(-1))
+        size = tilesize;
+    else if (size > tilesize)
+        size = tilesize;
+    if (TIFFFillTile(tif, tile) &&
+        (*tif->tif_decodetile)(tif, (uint8_t *)buf, size,
+                               (uint16_t)(tile / td->td_stripsperimage)))
+    {
+        (*tif->tif_postdecode)(tif, (uint8_t *)buf, size);
+        return (size);
+    }
+    else
+        return ((tmsize_t)(-1));
 }
 
-/* Variant of TIFFReadTile() that does 
- * * if *buf == NULL, *buf = _TIFFmalloc(bufsizetoalloc) only after TIFFFillTile() has
- *   succeeded. This avoid excessive memory allocation in case of truncated
- *   file.
+/* Variant of TIFFReadTile() that does
+ * * if *buf == NULL, *buf = _TIFFmallocExt(tif, bufsizetoalloc) only after
+ * TIFFFillTile() has succeeded. This avoid excessive memory allocation in case
+ * of truncated file.
  * * calls regular TIFFReadEncodedTile() if *buf != NULL
  */
-tmsize_t
-_TIFFReadTileAndAllocBuffer(TIFF* tif,
-                            void **buf, tmsize_t bufsizetoalloc,
-                            uint32 x, uint32 y, uint32 z, uint16 s)
+tmsize_t _TIFFReadTileAndAllocBuffer(TIFF *tif, void **buf,
+                                     tmsize_t bufsizetoalloc, uint32_t x,
+                                     uint32_t y, uint32_t z, uint16_t s)
 {
     if (!TIFFCheckRead(tif, 1) || !TIFFCheckTile(tif, x, y, z, s))
-            return ((tmsize_t)(-1));
-    return (_TIFFReadEncodedTileAndAllocBuffer(tif,
-                                               TIFFComputeTile(tif, x, y, z, s),
-                                               buf, bufsizetoalloc,
-                                               (tmsize_t)(-1)));
+        return ((tmsize_t)(-1));
+    return (_TIFFReadEncodedTileAndAllocBuffer(
+        tif, TIFFComputeTile(tif, x, y, z, s), buf, bufsizetoalloc,
+        (tmsize_t)(-1)));
 }
 
-/* Variant of TIFFReadEncodedTile() that does 
- * * if *buf == NULL, *buf = _TIFFmalloc(bufsizetoalloc) only after TIFFFillTile() has
- *   succeeded. This avoid excessive memory allocation in case of truncated
- *   file.
+/* Variant of TIFFReadEncodedTile() that does
+ * * if *buf == NULL, *buf = _TIFFmallocExt(tif, bufsizetoalloc) only after
+ * TIFFFillTile() has succeeded. This avoid excessive memory allocation in case
+ * of truncated file.
  * * calls regular TIFFReadEncodedTile() if *buf != NULL
  */
-tmsize_t
-_TIFFReadEncodedTileAndAllocBuffer(TIFF* tif, uint32 tile,
-                                    void **buf, tmsize_t bufsizetoalloc,
-                                    tmsize_t size_to_read)
+tmsize_t _TIFFReadEncodedTileAndAllocBuffer(TIFF *tif, uint32_t tile,
+                                            void **buf, tmsize_t bufsizetoalloc,
+                                            tmsize_t size_to_read)
 {
     static const char module[] = "_TIFFReadEncodedTileAndAllocBuffer";
     TIFFDirectory *td = &tif->tif_dir;
     tmsize_t tilesize = tif->tif_tilesize;
 
-    if( *buf != NULL )
+    if (*buf != NULL)
     {
         return TIFFReadEncodedTile(tif, tile, *buf, size_to_read);
     }
 
     if (!TIFFCheckRead(tif, 1))
-            return ((tmsize_t)(-1));
-    if (tile >= td->td_nstrips) {
-            TIFFErrorExt(tif->tif_clientdata, module,
-                "%lu: Tile out of range, max %lu",
-                (unsigned long) tile, (unsigned long) td->td_nstrips);
-            return ((tmsize_t)(-1));
+        return ((tmsize_t)(-1));
+    if (tile >= td->td_nstrips)
+    {
+        TIFFErrorExtR(tif, module,
+                      "%" PRIu32 ": Tile out of range, max %" PRIu32, tile,
+                      td->td_nstrips);
+        return ((tmsize_t)(-1));
     }
 
-    if (!TIFFFillTile(tif,tile))
-            return((tmsize_t)(-1));
+    if (!TIFFFillTile(tif, tile))
+        return ((tmsize_t)(-1));
 
-    *buf = _TIFFmalloc(bufsizetoalloc);
-    if (*buf == NULL) {
-            TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif),
-                         "No space for tile buffer");
-            return((tmsize_t)(-1));
+    /* Sanity checks to avoid excessive memory allocation */
+    /* Cf https://gitlab.com/libtiff/libtiff/-/issues/479 */
+    if (td->td_compression == COMPRESSION_NONE)
+    {
+        if (tif->tif_rawdatasize != tilesize)
+        {
+            TIFFErrorExtR(tif, TIFFFileName(tif),
+                          "Invalid tile byte count for tile %u. "
+                          "Expected %" PRIu64 ", got %" PRIu64,
+                          tile, (uint64_t)tilesize,
+                          (uint64_t)tif->tif_rawdatasize);
+            return ((tmsize_t)(-1));
+        }
+    }
+    else
+    {
+        /* Max compression ratio experimentally determined. Might be fragile...
+         * Only apply this heuristics to situations where the memory allocation
+         * would be big, to avoid breaking nominal use cases.
+         */
+        const int maxCompressionRatio =
+            td->td_compression == COMPRESSION_ZSTD ? 33000
+            : td->td_compression == COMPRESSION_JXL
+                ?
+                /* Evaluated on a 8000x8000 tile */
+                25000 * (td->td_planarconfig == PLANARCONFIG_CONTIG
+                             ? td->td_samplesperpixel
+                             : 1)
+                : td->td_compression == COMPRESSION_LZMA ? 7000 : 1000;
+        if (bufsizetoalloc > 100 * 1000 * 1000 &&
+            tif->tif_rawdatasize < tilesize / maxCompressionRatio)
+        {
+            TIFFErrorExtR(tif, TIFFFileName(tif),
+                          "Likely invalid tile byte count for tile %u. "
+                          "Uncompressed tile size is %" PRIu64 ", "
+                          "compressed one is %" PRIu64,
+                          tile, (uint64_t)tilesize,
+                          (uint64_t)tif->tif_rawdatasize);
+            return ((tmsize_t)(-1));
+        }
+    }
+
+    *buf = _TIFFmallocExt(tif, bufsizetoalloc);
+    if (*buf == NULL)
+    {
+        TIFFErrorExtR(tif, TIFFFileName(tif), "No space for tile buffer");
+        return ((tmsize_t)(-1));
     }
     _TIFFmemset(*buf, 0, bufsizetoalloc);
 
@@ -1084,287 +1076,261 @@
         size_to_read = tilesize;
     else if (size_to_read > tilesize)
         size_to_read = tilesize;
-    if( (*tif->tif_decodetile)(tif,
-        (uint8*) *buf, size_to_read, (uint16)(tile/td->td_stripsperimage))) {
-        (*tif->tif_postdecode)(tif, (uint8*) *buf, size_to_read);
+    if ((*tif->tif_decodetile)(tif, (uint8_t *)*buf, size_to_read,
+                               (uint16_t)(tile / td->td_stripsperimage)))
+    {
+        (*tif->tif_postdecode)(tif, (uint8_t *)*buf, size_to_read);
         return (size_to_read);
-    } else
+    }
+    else
         return ((tmsize_t)(-1));
 }
 
-static tmsize_t
-TIFFReadRawTile1(TIFF* tif, uint32 tile, void* buf, tmsize_t size, const char* module)
+static tmsize_t TIFFReadRawTile1(TIFF *tif, uint32_t tile, void *buf,
+                                 tmsize_t size, const char *module)
 {
-	assert((tif->tif_flags&TIFF_NOREADRAW)==0);
-	if (!isMapped(tif)) {
-		tmsize_t cc;
+    assert((tif->tif_flags & TIFF_NOREADRAW) == 0);
+    if (!isMapped(tif))
+    {
+        tmsize_t cc;
 
-		if (!SeekOK(tif, TIFFGetStrileOffset(tif, tile))) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Seek error at row %lu, col %lu, tile %lu",
-			    (unsigned long) tif->tif_row,
-			    (unsigned long) tif->tif_col,
-			    (unsigned long) tile);
-			return ((tmsize_t)(-1));
-		}
-		cc = TIFFReadFile(tif, buf, size);
-		if (cc != size) {
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-			TIFFErrorExt(tif->tif_clientdata, module,
-	"Read error at row %lu, col %lu; got %I64u bytes, expected %I64u",
-				     (unsigned long) tif->tif_row,
-				     (unsigned long) tif->tif_col,
-				     (unsigned __int64) cc,
-				     (unsigned __int64) size);
-#else
-			TIFFErrorExt(tif->tif_clientdata, module,
-	"Read error at row %lu, col %lu; got %llu bytes, expected %llu",
-				     (unsigned long) tif->tif_row,
-				     (unsigned long) tif->tif_col,
-				     (unsigned long long) cc,
-				     (unsigned long long) size);
-#endif
-			return ((tmsize_t)(-1));
-		}
-	} else {
-		tmsize_t ma,mb;
-		tmsize_t n;
-		ma=(tmsize_t)TIFFGetStrileOffset(tif, tile);
-		mb=ma+size;
-		if ((TIFFGetStrileOffset(tif, tile) > (uint64)TIFF_TMSIZE_T_MAX)||(ma>tif->tif_size))
-			n=0;
-		else if ((mb<ma)||(mb<size)||(mb>tif->tif_size))
-			n=tif->tif_size-ma;
-		else
-			n=size;
-		if (n!=size) {
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-			TIFFErrorExt(tif->tif_clientdata, module,
-"Read error at row %lu, col %lu, tile %lu; got %I64u bytes, expected %I64u",
-				     (unsigned long) tif->tif_row,
-				     (unsigned long) tif->tif_col,
-				     (unsigned long) tile,
-				     (unsigned __int64) n,
-				     (unsigned __int64) size);
-#else
-			TIFFErrorExt(tif->tif_clientdata, module,
-"Read error at row %lu, col %lu, tile %lu; got %llu bytes, expected %llu",
-				     (unsigned long) tif->tif_row,
-				     (unsigned long) tif->tif_col,
-				     (unsigned long) tile,
-				     (unsigned long long) n,
-				     (unsigned long long) size);
-#endif
-			return ((tmsize_t)(-1));
-		}
-		_TIFFmemcpy(buf, tif->tif_base + ma, size);
-	}
-	return (size);
+        if (!SeekOK(tif, TIFFGetStrileOffset(tif, tile)))
+        {
+            TIFFErrorExtR(tif, module,
+                          "Seek error at row %" PRIu32 ", col %" PRIu32
+                          ", tile %" PRIu32,
+                          tif->tif_row, tif->tif_col, tile);
+            return ((tmsize_t)(-1));
+        }
+        cc = TIFFReadFile(tif, buf, size);
+        if (cc != size)
+        {
+            TIFFErrorExtR(tif, module,
+                          "Read error at row %" PRIu32 ", col %" PRIu32
+                          "; got %" TIFF_SSIZE_FORMAT
+                          " bytes, expected %" TIFF_SSIZE_FORMAT,
+                          tif->tif_row, tif->tif_col, cc, size);
+            return ((tmsize_t)(-1));
+        }
+    }
+    else
+    {
+        tmsize_t ma, mb;
+        tmsize_t n;
+        ma = (tmsize_t)TIFFGetStrileOffset(tif, tile);
+        mb = ma + size;
+        if ((TIFFGetStrileOffset(tif, tile) > (uint64_t)TIFF_TMSIZE_T_MAX) ||
+            (ma > tif->tif_size))
+            n = 0;
+        else if ((mb < ma) || (mb < size) || (mb > tif->tif_size))
+            n = tif->tif_size - ma;
+        else
+            n = size;
+        if (n != size)
+        {
+            TIFFErrorExtR(tif, module,
+                          "Read error at row %" PRIu32 ", col %" PRIu32
+                          ", tile %" PRIu32 "; got %" TIFF_SSIZE_FORMAT
+                          " bytes, expected %" TIFF_SSIZE_FORMAT,
+                          tif->tif_row, tif->tif_col, tile, n, size);
+            return ((tmsize_t)(-1));
+        }
+        _TIFFmemcpy(buf, tif->tif_base + ma, size);
+    }
+    return (size);
 }
 
 /*
  * Read a tile of data from the file.
  */
-tmsize_t
-TIFFReadRawTile(TIFF* tif, uint32 tile, void* buf, tmsize_t size)
+tmsize_t TIFFReadRawTile(TIFF *tif, uint32_t tile, void *buf, tmsize_t size)
 {
-	static const char module[] = "TIFFReadRawTile";
-	TIFFDirectory *td = &tif->tif_dir;
-	uint64 bytecount64;
-	tmsize_t bytecountm;
+    static const char module[] = "TIFFReadRawTile";
+    TIFFDirectory *td = &tif->tif_dir;
+    uint64_t bytecount64;
+    tmsize_t bytecountm;
 
-	if (!TIFFCheckRead(tif, 1))
-		return ((tmsize_t)(-1));
-	if (tile >= td->td_nstrips) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "%lu: Tile out of range, max %lu",
-		    (unsigned long) tile, (unsigned long) td->td_nstrips);
-		return ((tmsize_t)(-1));
-	}
-	if (tif->tif_flags&TIFF_NOREADRAW)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module,
-		"Compression scheme does not support access to raw uncompressed data");
-		return ((tmsize_t)(-1));
-	}
-	bytecount64 = TIFFGetStrileByteCount(tif, tile);
-	if (size != (tmsize_t)(-1) && (uint64)size <= bytecount64)
-		bytecountm = size;
-	else
-		bytecountm = _TIFFCastUInt64ToSSize(tif, bytecount64, module);
-	if( bytecountm == 0 ) {
-		return ((tmsize_t)(-1));
-	}
-	return (TIFFReadRawTile1(tif, tile, buf, bytecountm, module));
+    if (!TIFFCheckRead(tif, 1))
+        return ((tmsize_t)(-1));
+    if (tile >= td->td_nstrips)
+    {
+        TIFFErrorExtR(tif, module,
+                      "%" PRIu32 ": Tile out of range, max %" PRIu32, tile,
+                      td->td_nstrips);
+        return ((tmsize_t)(-1));
+    }
+    if (tif->tif_flags & TIFF_NOREADRAW)
+    {
+        TIFFErrorExtR(tif, module,
+                      "Compression scheme does not support access to raw "
+                      "uncompressed data");
+        return ((tmsize_t)(-1));
+    }
+    bytecount64 = TIFFGetStrileByteCount(tif, tile);
+    if (size != (tmsize_t)(-1) && (uint64_t)size <= bytecount64)
+        bytecountm = size;
+    else
+        bytecountm = _TIFFCastUInt64ToSSize(tif, bytecount64, module);
+    if (bytecountm == 0)
+    {
+        return ((tmsize_t)(-1));
+    }
+    return (TIFFReadRawTile1(tif, tile, buf, bytecountm, module));
 }
 
 /*
  * Read the specified tile and setup for decoding. The data buffer is
  * expanded, as necessary, to hold the tile's data.
  */
-int
-TIFFFillTile(TIFF* tif, uint32 tile)
+int TIFFFillTile(TIFF *tif, uint32_t tile)
 {
-	static const char module[] = "TIFFFillTile";
-	TIFFDirectory *td = &tif->tif_dir;
+    static const char module[] = "TIFFFillTile";
+    TIFFDirectory *td = &tif->tif_dir;
 
-	if ((tif->tif_flags&TIFF_NOREADRAW)==0)
-	{
-		uint64 bytecount = TIFFGetStrileByteCount(tif, tile);
-		if( bytecount == 0 || bytecount > (uint64)TIFF_INT64_MAX ) {
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-			TIFFErrorExt(tif->tif_clientdata, module,
-				"%I64u: Invalid tile byte count, tile %lu",
-				     (unsigned __int64) bytecount,
-				     (unsigned long) tile);
-#else
-			TIFFErrorExt(tif->tif_clientdata, module,
-				"%llu: Invalid tile byte count, tile %lu",
-				     (unsigned long long) bytecount,
-				     (unsigned long) tile);
-#endif
-			return (0);
-		}
+    if ((tif->tif_flags & TIFF_NOREADRAW) == 0)
+    {
+        uint64_t bytecount = TIFFGetStrileByteCount(tif, tile);
+        if (bytecount == 0 || bytecount > (uint64_t)TIFF_INT64_MAX)
+        {
+            TIFFErrorExtR(tif, module,
+                          "%" PRIu64 ": Invalid tile byte count, tile %" PRIu32,
+                          bytecount, tile);
+            return (0);
+        }
 
-		/* To avoid excessive memory allocations: */
-		/* Byte count should normally not be larger than a number of */
-		/* times the uncompressed size plus some margin */
-                if( bytecount > 1024 * 1024 )
+        /* To avoid excessive memory allocations: */
+        /* Byte count should normally not be larger than a number of */
+        /* times the uncompressed size plus some margin */
+        if (bytecount > 1024 * 1024)
+        {
+            /* 10 and 4096 are just values that could be adjusted. */
+            /* Hopefully they are safe enough for all codecs */
+            tmsize_t stripsize = TIFFTileSize(tif);
+            if (stripsize != 0 && (bytecount - 4096) / 10 > (uint64_t)stripsize)
+            {
+                uint64_t newbytecount = (uint64_t)stripsize * 10 + 4096;
+                TIFFErrorExtR(tif, module,
+                              "Too large tile byte count %" PRIu64
+                              ", tile %" PRIu32 ". Limiting to %" PRIu64,
+                              bytecount, tile, newbytecount);
+                bytecount = newbytecount;
+            }
+        }
+
+        if (isMapped(tif))
+        {
+            /*
+             * We must check for overflow, potentially causing
+             * an OOB read. Instead of simple
+             *
+             *  TIFFGetStrileOffset(tif, tile)+bytecount > tif->tif_size
+             *
+             * comparison (which can overflow) we do the following
+             * two comparisons:
+             */
+            if (bytecount > (uint64_t)tif->tif_size ||
+                TIFFGetStrileOffset(tif, tile) >
+                    (uint64_t)tif->tif_size - bytecount)
+            {
+                tif->tif_curtile = NOTILE;
+                return (0);
+            }
+        }
+
+        if (isMapped(tif) && (isFillOrder(tif, td->td_fillorder) ||
+                              (tif->tif_flags & TIFF_NOBITREV)))
+        {
+            /*
+             * The image is mapped into memory and we either don't
+             * need to flip bits or the compression routine is
+             * going to handle this operation itself.  In this
+             * case, avoid copying the raw data and instead just
+             * reference the data from the memory mapped file
+             * image.  This assumes that the decompression
+             * routines do not modify the contents of the raw data
+             * buffer (if they try to, the application will get a
+             * fault since the file is mapped read-only).
+             */
+            if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata)
+            {
+                _TIFFfreeExt(tif, tif->tif_rawdata);
+                tif->tif_rawdata = NULL;
+                tif->tif_rawdatasize = 0;
+            }
+            tif->tif_flags &= ~TIFF_MYBUFFER;
+
+            tif->tif_rawdatasize = (tmsize_t)bytecount;
+            tif->tif_rawdata =
+                tif->tif_base + (tmsize_t)TIFFGetStrileOffset(tif, tile);
+            tif->tif_rawdataoff = 0;
+            tif->tif_rawdataloaded = (tmsize_t)bytecount;
+            tif->tif_flags |= TIFF_BUFFERMMAP;
+        }
+        else
+        {
+            /*
+             * Expand raw data buffer, if needed, to hold data
+             * tile coming from file (perhaps should set upper
+             * bound on the size of a buffer we'll use?).
+             */
+            tmsize_t bytecountm;
+            bytecountm = (tmsize_t)bytecount;
+            if ((uint64_t)bytecountm != bytecount)
+            {
+                TIFFErrorExtR(tif, module, "Integer overflow");
+                return (0);
+            }
+            if (bytecountm > tif->tif_rawdatasize)
+            {
+                tif->tif_curtile = NOTILE;
+                if ((tif->tif_flags & TIFF_MYBUFFER) == 0)
                 {
-			/* 10 and 4096 are just values that could be adjusted. */
-			/* Hopefully they are safe enough for all codecs */
-			tmsize_t stripsize = TIFFTileSize(tif);
-			if( stripsize != 0 &&
-			    (bytecount - 4096) / 10 > (uint64)stripsize  )
-			{
-				uint64 newbytecount = (uint64)stripsize * 10 + 4096;
-				if( newbytecount == 0 || newbytecount > (uint64)TIFF_INT64_MAX )
-				{
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-					TIFFWarningExt(tif->tif_clientdata, module,
-					  "Too large tile byte count %I64u, tile %lu. Limiting to %I64u",
-					     (unsigned __int64) bytecount,
-					     (unsigned long) tile,
-					     (unsigned __int64) newbytecount);
-#else
-					TIFFErrorExt(tif->tif_clientdata, module,
-					  "Too large tile byte count %llu, tile %lu. Limiting to %llu",
-					     (unsigned long long) bytecount,
-					     (unsigned long) tile,
-					     (unsigned long long) newbytecount);
-#endif
-					bytecount = newbytecount;
-				}
-			}
-		}
+                    TIFFErrorExtR(tif, module,
+                                  "Data buffer too small to hold tile %" PRIu32,
+                                  tile);
+                    return (0);
+                }
+            }
+            if (tif->tif_flags & TIFF_BUFFERMMAP)
+            {
+                tif->tif_curtile = NOTILE;
+                tif->tif_rawdata = NULL;
+                tif->tif_rawdatasize = 0;
+                tif->tif_flags &= ~TIFF_BUFFERMMAP;
+            }
 
-		if (isMapped(tif)) {
-			/*
-			 * We must check for overflow, potentially causing
-			 * an OOB read. Instead of simple
-			 *
-			 *  TIFFGetStrileOffset(tif, tile)+bytecount > tif->tif_size
-			 *
-			 * comparison (which can overflow) we do the following
-			 * two comparisons:
-			 */
-			if (bytecount > (uint64)tif->tif_size ||
-			    TIFFGetStrileOffset(tif, tile) > (uint64)tif->tif_size - bytecount) {
-				tif->tif_curtile = NOTILE;
-				return (0);
-			}
-		}
+            if (isMapped(tif))
+            {
+                if (bytecountm > tif->tif_rawdatasize &&
+                    !TIFFReadBufferSetup(tif, 0, bytecountm))
+                {
+                    return (0);
+                }
+                if (TIFFReadRawTile1(tif, tile, tif->tif_rawdata, bytecountm,
+                                     module) != bytecountm)
+                {
+                    return (0);
+                }
+            }
+            else
+            {
+                if (TIFFReadRawStripOrTile2(tif, tile, 0, bytecountm, module) !=
+                    bytecountm)
+                {
+                    return (0);
+                }
+            }
 
-		if (isMapped(tif) &&
-		    (isFillOrder(tif, td->td_fillorder)
-		     || (tif->tif_flags & TIFF_NOBITREV))) {
-			/*
-			 * The image is mapped into memory and we either don't
-			 * need to flip bits or the compression routine is
-			 * going to handle this operation itself.  In this
-			 * case, avoid copying the raw data and instead just
-			 * reference the data from the memory mapped file
-			 * image.  This assumes that the decompression
-			 * routines do not modify the contents of the raw data
-			 * buffer (if they try to, the application will get a
-			 * fault since the file is mapped read-only).
-			 */
-			if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata) {
-				_TIFFfree(tif->tif_rawdata);
-				tif->tif_rawdata = NULL;
-				tif->tif_rawdatasize = 0;
-			}
-			tif->tif_flags &= ~TIFF_MYBUFFER;
+            tif->tif_rawdataoff = 0;
+            tif->tif_rawdataloaded = bytecountm;
 
-			tif->tif_rawdatasize = (tmsize_t)bytecount;
-			tif->tif_rawdata =
-				tif->tif_base + (tmsize_t)TIFFGetStrileOffset(tif, tile);
-                        tif->tif_rawdataoff = 0;
-                        tif->tif_rawdataloaded = (tmsize_t) bytecount;
-			tif->tif_flags |= TIFF_BUFFERMMAP;
-		} else {
-			/*
-			 * Expand raw data buffer, if needed, to hold data
-			 * tile coming from file (perhaps should set upper
-			 * bound on the size of a buffer we'll use?).
-			 */
-			tmsize_t bytecountm;
-			bytecountm=(tmsize_t)bytecount;
-			if ((uint64)bytecountm!=bytecount)
-			{
-				TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow");
-				return(0);
-			}
-			if (bytecountm > tif->tif_rawdatasize) {
-				tif->tif_curtile = NOTILE;
-				if ((tif->tif_flags & TIFF_MYBUFFER) == 0) {
-					TIFFErrorExt(tif->tif_clientdata, module,
-					    "Data buffer too small to hold tile %lu",
-					    (unsigned long) tile);
-					return (0);
-				}
-			}
-			if (tif->tif_flags&TIFF_BUFFERMMAP) {
-				tif->tif_curtile = NOTILE;
-				tif->tif_rawdata = NULL;
-				tif->tif_rawdatasize = 0;
-				tif->tif_flags &= ~TIFF_BUFFERMMAP;
-			}
-
-			if( isMapped(tif) )
-			{
-				if (bytecountm > tif->tif_rawdatasize &&
-				    !TIFFReadBufferSetup(tif, 0, bytecountm))
-				{
-					return (0);
-				}
-				if (TIFFReadRawTile1(tif, tile, tif->tif_rawdata,
-				    bytecountm, module) != bytecountm)
-				{
-					return (0);
-				}
-			}
-			else
-			{
-				if (TIFFReadRawStripOrTile2(tif, tile, 0,
-				    bytecountm, module) != bytecountm)
-				{
-					return (0);
-				}
-			}
-
-
-                        tif->tif_rawdataoff = 0;
-                        tif->tif_rawdataloaded = bytecountm;
-                        
-			if (tif->tif_rawdata != NULL &&
-                            !isFillOrder(tif, td->td_fillorder) &&
-			    (tif->tif_flags & TIFF_NOBITREV) == 0)
-				TIFFReverseBits(tif->tif_rawdata,
-                                                tif->tif_rawdataloaded);
-		}
-	}
-	return (TIFFStartTile(tif, tile));
+            if (tif->tif_rawdata != NULL &&
+                !isFillOrder(tif, td->td_fillorder) &&
+                (tif->tif_flags & TIFF_NOBITREV) == 0)
+                TIFFReverseBits(tif->tif_rawdata, tif->tif_rawdataloaded);
+        }
+    }
+    return (TIFFStartTile(tif, tile));
 }
 
 /*
@@ -1376,172 +1342,191 @@
  * large enough to hold any individual strip of
  * raw data.
  */
-int
-TIFFReadBufferSetup(TIFF* tif, void* bp, tmsize_t size)
+int TIFFReadBufferSetup(TIFF *tif, void *bp, tmsize_t size)
 {
-	static const char module[] = "TIFFReadBufferSetup";
+    static const char module[] = "TIFFReadBufferSetup";
 
-	assert((tif->tif_flags&TIFF_NOREADRAW)==0);
-	tif->tif_flags &= ~TIFF_BUFFERMMAP;
+    assert((tif->tif_flags & TIFF_NOREADRAW) == 0);
+    tif->tif_flags &= ~TIFF_BUFFERMMAP;
 
-	if (tif->tif_rawdata) {
-		if (tif->tif_flags & TIFF_MYBUFFER)
-			_TIFFfree(tif->tif_rawdata);
-		tif->tif_rawdata = NULL;
-		tif->tif_rawdatasize = 0;
-	}
-	if (bp) {
-		tif->tif_rawdatasize = size;
-		tif->tif_rawdata = (uint8*) bp;
-		tif->tif_flags &= ~TIFF_MYBUFFER;
-	} else {
-		tif->tif_rawdatasize = (tmsize_t)TIFFroundup_64((uint64)size, 1024);
-		if (tif->tif_rawdatasize==0) {
-		    TIFFErrorExt(tif->tif_clientdata, module,
-				 "Invalid buffer size");
-		    return (0);
-		}
-		/* Initialize to zero to avoid uninitialized buffers in case of */
-                /* short reads (http://bugzilla.maptools.org/show_bug.cgi?id=2651) */
-		tif->tif_rawdata = (uint8*) _TIFFcalloc(1, tif->tif_rawdatasize);
-		tif->tif_flags |= TIFF_MYBUFFER;
-	}
-	if (tif->tif_rawdata == NULL) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "No space for data buffer at scanline %lu",
-		    (unsigned long) tif->tif_row);
-		tif->tif_rawdatasize = 0;
-		return (0);
-	}
-	return (1);
+    if (tif->tif_rawdata)
+    {
+        if (tif->tif_flags & TIFF_MYBUFFER)
+            _TIFFfreeExt(tif, tif->tif_rawdata);
+        tif->tif_rawdata = NULL;
+        tif->tif_rawdatasize = 0;
+    }
+    if (bp)
+    {
+        tif->tif_rawdatasize = size;
+        tif->tif_rawdata = (uint8_t *)bp;
+        tif->tif_flags &= ~TIFF_MYBUFFER;
+    }
+    else
+    {
+        tif->tif_rawdatasize = (tmsize_t)TIFFroundup_64((uint64_t)size, 1024);
+        if (tif->tif_rawdatasize == 0)
+        {
+            TIFFErrorExtR(tif, module, "Invalid buffer size");
+            return (0);
+        }
+        /* Initialize to zero to avoid uninitialized buffers in case of */
+        /* short reads (http://bugzilla.maptools.org/show_bug.cgi?id=2651) */
+        tif->tif_rawdata =
+            (uint8_t *)_TIFFcallocExt(tif, 1, tif->tif_rawdatasize);
+        tif->tif_flags |= TIFF_MYBUFFER;
+    }
+    if (tif->tif_rawdata == NULL)
+    {
+        TIFFErrorExtR(tif, module,
+                      "No space for data buffer at scanline %" PRIu32,
+                      tif->tif_row);
+        tif->tif_rawdatasize = 0;
+        return (0);
+    }
+    return (1);
 }
 
 /*
  * Set state to appear as if a
  * strip has just been read in.
  */
-static int
-TIFFStartStrip(TIFF* tif, uint32 strip)
+static int TIFFStartStrip(TIFF *tif, uint32_t strip)
 {
-	TIFFDirectory *td = &tif->tif_dir;
+    TIFFDirectory *td = &tif->tif_dir;
 
-	if ((tif->tif_flags & TIFF_CODERSETUP) == 0) {
-		if (!(*tif->tif_setupdecode)(tif))
-			return (0);
-		tif->tif_flags |= TIFF_CODERSETUP;
-	}
-	tif->tif_curstrip = strip;
-	tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip;
-        tif->tif_flags &= ~TIFF_BUF4WRITE;
+    if ((tif->tif_flags & TIFF_CODERSETUP) == 0)
+    {
+        if (!(*tif->tif_setupdecode)(tif))
+            return (0);
+        tif->tif_flags |= TIFF_CODERSETUP;
+    }
+    tif->tif_curstrip = strip;
+    tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip;
+    tif->tif_flags &= ~TIFF_BUF4WRITE;
 
-	if (tif->tif_flags&TIFF_NOREADRAW)
-	{
-		tif->tif_rawcp = NULL;
-		tif->tif_rawcc = 0;  
-	}
-	else
-	{
-		tif->tif_rawcp = tif->tif_rawdata;
-		if( tif->tif_rawdataloaded > 0 )
-			tif->tif_rawcc = tif->tif_rawdataloaded;
-		else
-			tif->tif_rawcc = (tmsize_t)TIFFGetStrileByteCount(tif, strip);
-	}
-	return ((*tif->tif_predecode)(tif,
-			(uint16)(strip / td->td_stripsperimage)));
+    if (tif->tif_flags & TIFF_NOREADRAW)
+    {
+        tif->tif_rawcp = NULL;
+        tif->tif_rawcc = 0;
+    }
+    else
+    {
+        tif->tif_rawcp = tif->tif_rawdata;
+        if (tif->tif_rawdataloaded > 0)
+            tif->tif_rawcc = tif->tif_rawdataloaded;
+        else
+            tif->tif_rawcc = (tmsize_t)TIFFGetStrileByteCount(tif, strip);
+    }
+    if ((*tif->tif_predecode)(tif, (uint16_t)(strip / td->td_stripsperimage)) ==
+        0)
+    {
+        /* Needed for example for scanline access, if tif_predecode */
+        /* fails, and we try to read the same strip again. Without invalidating
+         */
+        /* tif_curstrip, we'd call tif_decoderow() on a possibly invalid */
+        /* codec state. */
+        tif->tif_curstrip = NOSTRIP;
+        return 0;
+    }
+    return 1;
 }
 
 /*
  * Set state to appear as if a
  * tile has just been read in.
  */
-static int
-TIFFStartTile(TIFF* tif, uint32 tile)
+static int TIFFStartTile(TIFF *tif, uint32_t tile)
 {
-        static const char module[] = "TIFFStartTile";
-	TIFFDirectory *td = &tif->tif_dir;
-        uint32 howmany32;
+    static const char module[] = "TIFFStartTile";
+    TIFFDirectory *td = &tif->tif_dir;
+    uint32_t howmany32;
 
-	if ((tif->tif_flags & TIFF_CODERSETUP) == 0) {
-		if (!(*tif->tif_setupdecode)(tif))
-			return (0);
-		tif->tif_flags |= TIFF_CODERSETUP;
-	}
-	tif->tif_curtile = tile;
-        howmany32=TIFFhowmany_32(td->td_imagewidth, td->td_tilewidth);
-        if (howmany32 == 0) {
-                 TIFFErrorExt(tif->tif_clientdata,module,"Zero tiles");
-                return 0;
-        }
-	tif->tif_row = (tile % howmany32) * td->td_tilelength;
-        howmany32=TIFFhowmany_32(td->td_imagelength, td->td_tilelength);
-        if (howmany32 == 0) {
-                TIFFErrorExt(tif->tif_clientdata,module,"Zero tiles");
-                return 0;
-        }
-	tif->tif_col = (tile % howmany32) * td->td_tilewidth;
-        tif->tif_flags &= ~TIFF_BUF4WRITE;
-	if (tif->tif_flags&TIFF_NOREADRAW)
-	{
-		tif->tif_rawcp = NULL;
-		tif->tif_rawcc = 0;
-	}
-	else
-	{
-		tif->tif_rawcp = tif->tif_rawdata;
-		if( tif->tif_rawdataloaded > 0 )
-			tif->tif_rawcc = tif->tif_rawdataloaded;
-		else
-			tif->tif_rawcc = (tmsize_t)TIFFGetStrileByteCount(tif, tile);
-	}
-	return ((*tif->tif_predecode)(tif,
-			(uint16)(tile/td->td_stripsperimage)));
+    if ((tif->tif_flags & TIFF_CODERSETUP) == 0)
+    {
+        if (!(*tif->tif_setupdecode)(tif))
+            return (0);
+        tif->tif_flags |= TIFF_CODERSETUP;
+    }
+    tif->tif_curtile = tile;
+    howmany32 = TIFFhowmany_32(td->td_imagewidth, td->td_tilewidth);
+    if (howmany32 == 0)
+    {
+        TIFFErrorExtR(tif, module, "Zero tiles");
+        return 0;
+    }
+    tif->tif_row = (tile % howmany32) * td->td_tilelength;
+    howmany32 = TIFFhowmany_32(td->td_imagelength, td->td_tilelength);
+    if (howmany32 == 0)
+    {
+        TIFFErrorExtR(tif, module, "Zero tiles");
+        return 0;
+    }
+    tif->tif_col = (tile % howmany32) * td->td_tilewidth;
+    tif->tif_flags &= ~TIFF_BUF4WRITE;
+    if (tif->tif_flags & TIFF_NOREADRAW)
+    {
+        tif->tif_rawcp = NULL;
+        tif->tif_rawcc = 0;
+    }
+    else
+    {
+        tif->tif_rawcp = tif->tif_rawdata;
+        if (tif->tif_rawdataloaded > 0)
+            tif->tif_rawcc = tif->tif_rawdataloaded;
+        else
+            tif->tif_rawcc = (tmsize_t)TIFFGetStrileByteCount(tif, tile);
+    }
+    return (
+        (*tif->tif_predecode)(tif, (uint16_t)(tile / td->td_stripsperimage)));
 }
 
-static int
-TIFFCheckRead(TIFF* tif, int tiles)
+static int TIFFCheckRead(TIFF *tif, int tiles)
 {
-	if (tif->tif_mode == O_WRONLY) {
-		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "File not open for reading");
-		return (0);
-	}
-	if (tiles ^ isTiled(tif)) {
-		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, tiles ?
-		    "Can not read tiles from a striped image" :
-		    "Can not read scanlines from a tiled image");
-		return (0);
-	}
-	return (1);
+    if (tif->tif_mode == O_WRONLY)
+    {
+        TIFFErrorExtR(tif, tif->tif_name, "File not open for reading");
+        return (0);
+    }
+    if (tiles ^ isTiled(tif))
+    {
+        TIFFErrorExtR(tif, tif->tif_name,
+                      tiles ? "Can not read tiles from a striped image"
+                            : "Can not read scanlines from a tiled image");
+        return (0);
+    }
+    return (1);
 }
 
 /* Use the provided input buffer (inbuf, insize) and decompress it into
  * (outbuf, outsize).
- * This function replaces the use of TIFFReadEncodedStrip()/TIFFReadEncodedTile()
- * when the user can provide the buffer for the input data, for example when
- * he wants to avoid libtiff to read the strile offset/count values from the
- * [Strip|Tile][Offsets/ByteCounts] array.
- * inbuf content must be writable (if bit reversal is needed)
- * Returns 1 in case of success, 0 otherwise.
+ * This function replaces the use of
+ * TIFFReadEncodedStrip()/TIFFReadEncodedTile() when the user can provide the
+ * buffer for the input data, for example when he wants to avoid libtiff to read
+ * the strile offset/count values from the [Strip|Tile][Offsets/ByteCounts]
+ * array. inbuf content must be writable (if bit reversal is needed) Returns 1
+ * in case of success, 0 otherwise.
  */
-int      TIFFReadFromUserBuffer(TIFF* tif, uint32 strile,
-                                void* inbuf, tmsize_t insize,
-                                void* outbuf, tmsize_t outsize)
+int TIFFReadFromUserBuffer(TIFF *tif, uint32_t strile, void *inbuf,
+                           tmsize_t insize, void *outbuf, tmsize_t outsize)
 {
     static const char module[] = "TIFFReadFromUserBuffer";
     TIFFDirectory *td = &tif->tif_dir;
     int ret = 1;
-    uint32 old_tif_flags = tif->tif_flags;
+    uint32_t old_tif_flags = tif->tif_flags;
     tmsize_t old_rawdatasize = tif->tif_rawdatasize;
-    void* old_rawdata = tif->tif_rawdata;
+    void *old_rawdata = tif->tif_rawdata;
 
-    if (tif->tif_mode == O_WRONLY) {
-        TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "File not open for reading");
+    if (tif->tif_mode == O_WRONLY)
+    {
+        TIFFErrorExtR(tif, tif->tif_name, "File not open for reading");
         return 0;
     }
-    if (tif->tif_flags&TIFF_NOREADRAW)
+    if (tif->tif_flags & TIFF_NOREADRAW)
     {
-        TIFFErrorExt(tif->tif_clientdata, module,
-                "Compression scheme does not support access to raw uncompressed data");
+        TIFFErrorExtR(tif, module,
+                      "Compression scheme does not support access to raw "
+                      "uncompressed data");
         return 0;
     }
 
@@ -1558,32 +1543,33 @@
         TIFFReverseBits(inbuf, insize);
     }
 
-    if( TIFFIsTiled(tif) )
+    if (TIFFIsTiled(tif))
     {
-        if( !TIFFStartTile(tif, strile) ||
-            !(*tif->tif_decodetile)(tif, (uint8*) outbuf, outsize, 
-                                    (uint16)(strile/td->td_stripsperimage)) )
+        if (!TIFFStartTile(tif, strile) ||
+            !(*tif->tif_decodetile)(tif, (uint8_t *)outbuf, outsize,
+                                    (uint16_t)(strile / td->td_stripsperimage)))
         {
             ret = 0;
         }
     }
     else
     {
-        uint32 rowsperstrip=td->td_rowsperstrip;
-        uint32 stripsperplane;
-        if (rowsperstrip>td->td_imagelength)
-            rowsperstrip=td->td_imagelength;
-        stripsperplane= TIFFhowmany_32_maxuint_compat(td->td_imagelength, rowsperstrip);
-        if( !TIFFStartStrip(tif, strile) ||
-            !(*tif->tif_decodestrip)(tif, (uint8*) outbuf, outsize, 
-                                     (uint16)(strile/stripsperplane)) )
+        uint32_t rowsperstrip = td->td_rowsperstrip;
+        uint32_t stripsperplane;
+        if (rowsperstrip > td->td_imagelength)
+            rowsperstrip = td->td_imagelength;
+        stripsperplane =
+            TIFFhowmany_32_maxuint_compat(td->td_imagelength, rowsperstrip);
+        if (!TIFFStartStrip(tif, strile) ||
+            !(*tif->tif_decodestrip)(tif, (uint8_t *)outbuf, outsize,
+                                     (uint16_t)(strile / stripsperplane)))
         {
             ret = 0;
         }
     }
-    if( ret )
+    if (ret)
     {
-        (*tif->tif_postdecode)(tif, (uint8*) outbuf, outsize);
+        (*tif->tif_postdecode)(tif, (uint8_t *)outbuf, outsize);
     }
 
     if (!isFillOrder(tif, td->td_fillorder) &&
@@ -1592,7 +1578,8 @@
         TIFFReverseBits(inbuf, insize);
     }
 
-    tif->tif_flags = old_tif_flags;
+    tif->tif_flags = (old_tif_flags & (TIFF_MYBUFFER | TIFF_BUFFERMMAP)) |
+                     (tif->tif_flags & ~(TIFF_MYBUFFER | TIFF_BUFFERMMAP));
     tif->tif_rawdatasize = old_rawdatasize;
     tif->tif_rawdata = old_rawdata;
     tif->tif_rawdataoff = 0;
@@ -1601,49 +1588,37 @@
     return ret;
 }
 
-void
-_TIFFNoPostDecode(TIFF* tif, uint8* buf, tmsize_t cc)
+void _TIFFNoPostDecode(TIFF *tif, uint8_t *buf, tmsize_t cc)
 {
-    (void) tif; (void) buf; (void) cc;
+    (void)tif;
+    (void)buf;
+    (void)cc;
 }
 
-void
-_TIFFSwab16BitData(TIFF* tif, uint8* buf, tmsize_t cc)
+void _TIFFSwab16BitData(TIFF *tif, uint8_t *buf, tmsize_t cc)
 {
-    (void) tif;
+    (void)tif;
     assert((cc & 1) == 0);
-    TIFFSwabArrayOfShort((uint16*) buf, cc/2);
+    TIFFSwabArrayOfShort((uint16_t *)buf, cc / 2);
 }
 
-void
-_TIFFSwab24BitData(TIFF* tif, uint8* buf, tmsize_t cc)
+void _TIFFSwab24BitData(TIFF *tif, uint8_t *buf, tmsize_t cc)
 {
-    (void) tif;
+    (void)tif;
     assert((cc % 3) == 0);
-    TIFFSwabArrayOfTriples((uint8*) buf, cc/3);
+    TIFFSwabArrayOfTriples((uint8_t *)buf, cc / 3);
 }
 
-void
-_TIFFSwab32BitData(TIFF* tif, uint8* buf, tmsize_t cc)
+void _TIFFSwab32BitData(TIFF *tif, uint8_t *buf, tmsize_t cc)
 {
-    (void) tif;
+    (void)tif;
     assert((cc & 3) == 0);
-    TIFFSwabArrayOfLong((uint32*) buf, cc/4);
+    TIFFSwabArrayOfLong((uint32_t *)buf, cc / 4);
 }
 
-void
-_TIFFSwab64BitData(TIFF* tif, uint8* buf, tmsize_t cc)
+void _TIFFSwab64BitData(TIFF *tif, uint8_t *buf, tmsize_t cc)
 {
-    (void) tif;
+    (void)tif;
     assert((cc & 7) == 0);
-    TIFFSwabArrayOfDouble((double*) buf, cc/8);
+    TIFFSwabArrayOfDouble((double *)buf, cc / 8);
 }
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_strip.c b/third_party/libtiff/tif_strip.c
index c08c60a..820a254 100644
--- a/third_party/libtiff/tif_strip.c
+++ b/third_party/libtiff/tif_strip.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1991-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -32,153 +32,145 @@
 /*
  * Compute which strip a (row,sample) value is in.
  */
-uint32
-TIFFComputeStrip(TIFF* tif, uint32 row, uint16 sample)
+uint32_t TIFFComputeStrip(TIFF *tif, uint32_t row, uint16_t sample)
 {
-	static const char module[] = "TIFFComputeStrip";
-	TIFFDirectory *td = &tif->tif_dir;
-	uint32 strip;
+    static const char module[] = "TIFFComputeStrip";
+    TIFFDirectory *td = &tif->tif_dir;
+    uint32_t strip;
 
-	strip = row / td->td_rowsperstrip;
-	if (td->td_planarconfig == PLANARCONFIG_SEPARATE) {
-		if (sample >= td->td_samplesperpixel) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "%lu: Sample out of range, max %lu",
-			    (unsigned long) sample, (unsigned long) td->td_samplesperpixel);
-			return (0);
-		}
-		strip += (uint32)sample*td->td_stripsperimage;
-	}
-	return (strip);
+    strip = row / td->td_rowsperstrip;
+    if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
+    {
+        if (sample >= td->td_samplesperpixel)
+        {
+            TIFFErrorExtR(tif, module, "%lu: Sample out of range, max %lu",
+                          (unsigned long)sample,
+                          (unsigned long)td->td_samplesperpixel);
+            return (0);
+        }
+        strip += (uint32_t)sample * td->td_stripsperimage;
+    }
+    return (strip);
 }
 
 /*
  * Compute how many strips are in an image.
  */
-uint32
-TIFFNumberOfStrips(TIFF* tif)
+uint32_t TIFFNumberOfStrips(TIFF *tif)
 {
-	TIFFDirectory *td = &tif->tif_dir;
-	uint32 nstrips;
+    TIFFDirectory *td = &tif->tif_dir;
+    uint32_t nstrips;
 
-	nstrips = (td->td_rowsperstrip == (uint32) -1 ? 1 :
-	     TIFFhowmany_32(td->td_imagelength, td->td_rowsperstrip));
-	if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
-		nstrips = _TIFFMultiply32(tif, nstrips, (uint32)td->td_samplesperpixel,
-		    "TIFFNumberOfStrips");
-	return (nstrips);
+    nstrips = (td->td_rowsperstrip == (uint32_t)-1
+                   ? 1
+                   : TIFFhowmany_32(td->td_imagelength, td->td_rowsperstrip));
+    if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
+        nstrips =
+            _TIFFMultiply32(tif, nstrips, (uint32_t)td->td_samplesperpixel,
+                            "TIFFNumberOfStrips");
+    return (nstrips);
 }
 
 /*
  * Compute the # bytes in a variable height, row-aligned strip.
  */
-uint64
-TIFFVStripSize64(TIFF* tif, uint32 nrows)
+uint64_t TIFFVStripSize64(TIFF *tif, uint32_t nrows)
 {
-	static const char module[] = "TIFFVStripSize64";
-	TIFFDirectory *td = &tif->tif_dir;
-	if (nrows==(uint32)(-1))
-		nrows=td->td_imagelength;
-	if ((td->td_planarconfig==PLANARCONFIG_CONTIG)&&
-	    (td->td_photometric == PHOTOMETRIC_YCBCR)&&
-	    (!isUpSampled(tif)))
-	{
-		/*
-		 * Packed YCbCr data contain one Cb+Cr for every
-		 * HorizontalSampling*VerticalSampling Y values.
-		 * Must also roundup width and height when calculating
-		 * since images that are not a multiple of the
-		 * horizontal/vertical subsampling area include
-		 * YCbCr data for the extended image.
-		 */
-		uint16 ycbcrsubsampling[2];
-		uint16 samplingblock_samples;
-		uint32 samplingblocks_hor;
-		uint32 samplingblocks_ver;
-		uint64 samplingrow_samples;
-		uint64 samplingrow_size;
-		if(td->td_samplesperpixel!=3)
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,
-			    "Invalid td_samplesperpixel value");
-			return 0;
-		}
-		TIFFGetFieldDefaulted(tif,TIFFTAG_YCBCRSUBSAMPLING,ycbcrsubsampling+0,
-		    ycbcrsubsampling+1);
-		if ((ycbcrsubsampling[0] != 1 && ycbcrsubsampling[0] != 2 && ycbcrsubsampling[0] != 4)
-		    ||(ycbcrsubsampling[1] != 1 && ycbcrsubsampling[1] != 2 && ycbcrsubsampling[1] != 4))
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,
-				     "Invalid YCbCr subsampling (%dx%d)", 
-				     ycbcrsubsampling[0], 
-				     ycbcrsubsampling[1] );
-			return 0;
-		}
-		samplingblock_samples=ycbcrsubsampling[0]*ycbcrsubsampling[1]+2;
-		samplingblocks_hor=TIFFhowmany_32(td->td_imagewidth,ycbcrsubsampling[0]);
-		samplingblocks_ver=TIFFhowmany_32(nrows,ycbcrsubsampling[1]);
-		samplingrow_samples=_TIFFMultiply64(tif,samplingblocks_hor,samplingblock_samples,module);
-		samplingrow_size=TIFFhowmany8_64(_TIFFMultiply64(tif,samplingrow_samples,td->td_bitspersample,module));
-		return(_TIFFMultiply64(tif,samplingrow_size,samplingblocks_ver,module));
-	}
-	else
-		return(_TIFFMultiply64(tif,nrows,TIFFScanlineSize64(tif),module));
+    static const char module[] = "TIFFVStripSize64";
+    TIFFDirectory *td = &tif->tif_dir;
+    if (nrows == (uint32_t)(-1))
+        nrows = td->td_imagelength;
+    if ((td->td_planarconfig == PLANARCONFIG_CONTIG) &&
+        (td->td_photometric == PHOTOMETRIC_YCBCR) && (!isUpSampled(tif)))
+    {
+        /*
+         * Packed YCbCr data contain one Cb+Cr for every
+         * HorizontalSampling*VerticalSampling Y values.
+         * Must also roundup width and height when calculating
+         * since images that are not a multiple of the
+         * horizontal/vertical subsampling area include
+         * YCbCr data for the extended image.
+         */
+        uint16_t ycbcrsubsampling[2];
+        uint16_t samplingblock_samples;
+        uint32_t samplingblocks_hor;
+        uint32_t samplingblocks_ver;
+        uint64_t samplingrow_samples;
+        uint64_t samplingrow_size;
+        if (td->td_samplesperpixel != 3)
+        {
+            TIFFErrorExtR(tif, module, "Invalid td_samplesperpixel value");
+            return 0;
+        }
+        TIFFGetFieldDefaulted(tif, TIFFTAG_YCBCRSUBSAMPLING,
+                              ycbcrsubsampling + 0, ycbcrsubsampling + 1);
+        if ((ycbcrsubsampling[0] != 1 && ycbcrsubsampling[0] != 2 &&
+             ycbcrsubsampling[0] != 4) ||
+            (ycbcrsubsampling[1] != 1 && ycbcrsubsampling[1] != 2 &&
+             ycbcrsubsampling[1] != 4))
+        {
+            TIFFErrorExtR(tif, module, "Invalid YCbCr subsampling (%dx%d)",
+                          ycbcrsubsampling[0], ycbcrsubsampling[1]);
+            return 0;
+        }
+        samplingblock_samples = ycbcrsubsampling[0] * ycbcrsubsampling[1] + 2;
+        samplingblocks_hor =
+            TIFFhowmany_32(td->td_imagewidth, ycbcrsubsampling[0]);
+        samplingblocks_ver = TIFFhowmany_32(nrows, ycbcrsubsampling[1]);
+        samplingrow_samples = _TIFFMultiply64(tif, samplingblocks_hor,
+                                              samplingblock_samples, module);
+        samplingrow_size = TIFFhowmany8_64(_TIFFMultiply64(
+            tif, samplingrow_samples, td->td_bitspersample, module));
+        return (
+            _TIFFMultiply64(tif, samplingrow_size, samplingblocks_ver, module));
+    }
+    else
+        return (_TIFFMultiply64(tif, nrows, TIFFScanlineSize64(tif), module));
 }
-tmsize_t
-TIFFVStripSize(TIFF* tif, uint32 nrows)
+tmsize_t TIFFVStripSize(TIFF *tif, uint32_t nrows)
 {
-	static const char module[] = "TIFFVStripSize";
-	uint64 m;
-	m=TIFFVStripSize64(tif,nrows);
-        return _TIFFCastUInt64ToSSize(tif, m, module);
+    static const char module[] = "TIFFVStripSize";
+    uint64_t m;
+    m = TIFFVStripSize64(tif, nrows);
+    return _TIFFCastUInt64ToSSize(tif, m, module);
 }
 
 /*
  * Compute the # bytes in a raw strip.
  */
-uint64
-TIFFRawStripSize64(TIFF* tif, uint32 strip)
+uint64_t TIFFRawStripSize64(TIFF *tif, uint32_t strip)
 {
-	static const char module[] = "TIFFRawStripSize64";
-	uint64 bytecount = TIFFGetStrileByteCount(tif, strip);
+    static const char module[] = "TIFFRawStripSize64";
+    uint64_t bytecount = TIFFGetStrileByteCount(tif, strip);
 
-	if (bytecount == 0)
-	{
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-		TIFFErrorExt(tif->tif_clientdata, module,
-			     "%I64u: Invalid strip byte count, strip %lu",
-			     (unsigned __int64) bytecount,
-			     (unsigned long) strip);
-#else
-		TIFFErrorExt(tif->tif_clientdata, module,
-			     "%llu: Invalid strip byte count, strip %lu",
-			     (unsigned long long) bytecount,
-			     (unsigned long) strip);
-#endif
-		bytecount = (uint64) -1;
-	}
+    if (bytecount == 0)
+    {
+        TIFFErrorExtR(tif, module,
+                      "%" PRIu64 ": Invalid strip byte count, strip %lu",
+                      (uint64_t)bytecount, (unsigned long)strip);
+        bytecount = (uint64_t)-1;
+    }
 
-	return bytecount;
+    return bytecount;
 }
-tmsize_t
-TIFFRawStripSize(TIFF* tif, uint32 strip)
+tmsize_t TIFFRawStripSize(TIFF *tif, uint32_t strip)
 {
-	static const char module[] = "TIFFRawStripSize";
-	uint64 m;
-	tmsize_t n;
-	m=TIFFRawStripSize64(tif,strip);
-	if (m==(uint64)(-1))
-		n=(tmsize_t)(-1);
-	else
-	{
-		n=(tmsize_t)m;
-		if ((uint64)n!=m)
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow");
-			n=0;
-		}
-	}
-	return(n);
+    static const char module[] = "TIFFRawStripSize";
+    uint64_t m;
+    tmsize_t n;
+    m = TIFFRawStripSize64(tif, strip);
+    if (m == (uint64_t)(-1))
+        n = (tmsize_t)(-1);
+    else
+    {
+        n = (tmsize_t)m;
+        if ((uint64_t)n != m)
+        {
+            TIFFErrorExtR(tif, module, "Integer overflow");
+            n = 0;
+        }
+    }
+    return (n);
 }
 
 /*
@@ -189,22 +181,20 @@
  * truncated to reflect the actual space required
  * to hold the strip.
  */
-uint64
-TIFFStripSize64(TIFF* tif)
+uint64_t TIFFStripSize64(TIFF *tif)
 {
-	TIFFDirectory* td = &tif->tif_dir;
-	uint32 rps = td->td_rowsperstrip;
-	if (rps > td->td_imagelength)
-		rps = td->td_imagelength;
-	return (TIFFVStripSize64(tif, rps));
+    TIFFDirectory *td = &tif->tif_dir;
+    uint32_t rps = td->td_rowsperstrip;
+    if (rps > td->td_imagelength)
+        rps = td->td_imagelength;
+    return (TIFFVStripSize64(tif, rps));
 }
-tmsize_t
-TIFFStripSize(TIFF* tif)
+tmsize_t TIFFStripSize(TIFF *tif)
 {
-	static const char module[] = "TIFFStripSize";
-	uint64 m;
-	m=TIFFStripSize64(tif);
-	return _TIFFCastUInt64ToSSize(tif, m, module);
+    static const char module[] = "TIFFStripSize";
+    uint64_t m;
+    m = TIFFStripSize64(tif);
+    return _TIFFCastUInt64ToSSize(tif, m, module);
 }
 
 /*
@@ -213,34 +203,33 @@
  * request is <1 then we choose a strip size according
  * to certain heuristics.
  */
-uint32
-TIFFDefaultStripSize(TIFF* tif, uint32 request)
+uint32_t TIFFDefaultStripSize(TIFF *tif, uint32_t request)
 {
-	return (*tif->tif_defstripsize)(tif, request);
+    return (*tif->tif_defstripsize)(tif, request);
 }
 
-uint32
-_TIFFDefaultStripSize(TIFF* tif, uint32 s)
+uint32_t _TIFFDefaultStripSize(TIFF *tif, uint32_t s)
 {
-	if ((int32) s < 1) {
-		/*
-		 * If RowsPerStrip is unspecified, try to break the
-		 * image up into strips that are approximately
-		 * STRIP_SIZE_DEFAULT bytes long.
-		 */
-		uint64 scanlinesize;
-		uint64 rows;
-		scanlinesize=TIFFScanlineSize64(tif);
-		if (scanlinesize==0)
-			scanlinesize=1;
-		rows=(uint64)STRIP_SIZE_DEFAULT/scanlinesize;
-		if (rows==0)
-			rows=1;
-		else if (rows>0xFFFFFFFF)
-			rows=0xFFFFFFFF;
-		s=(uint32)rows;
-	}
-	return (s);
+    if ((int32_t)s < 1)
+    {
+        /*
+         * If RowsPerStrip is unspecified, try to break the
+         * image up into strips that are approximately
+         * STRIP_SIZE_DEFAULT bytes long.
+         */
+        uint64_t scanlinesize;
+        uint64_t rows;
+        scanlinesize = TIFFScanlineSize64(tif);
+        if (scanlinesize == 0)
+            scanlinesize = 1;
+        rows = (uint64_t)STRIP_SIZE_DEFAULT / scanlinesize;
+        if (rows == 0)
+            rows = 1;
+        else if (rows > 0xFFFFFFFF)
+            rows = 0xFFFFFFFF;
+        s = (uint32_t)rows;
+    }
+    return (s);
 }
 
 /*
@@ -253,70 +242,79 @@
  * subsampling lines divided by vertical subsampling. It should thus make
  * sense when multiplied by a multiple of vertical subsampling.
  */
-uint64
-TIFFScanlineSize64(TIFF* tif)
+uint64_t TIFFScanlineSize64(TIFF *tif)
 {
-	static const char module[] = "TIFFScanlineSize64";
-	TIFFDirectory *td = &tif->tif_dir;
-	uint64 scanline_size;
-	if (td->td_planarconfig==PLANARCONFIG_CONTIG)
-	{
-		if ((td->td_photometric==PHOTOMETRIC_YCBCR)&&
-		    (td->td_samplesperpixel==3)&&
-		    (!isUpSampled(tif)))
-		{
-			uint16 ycbcrsubsampling[2];
-			uint16 samplingblock_samples;
-			uint32 samplingblocks_hor;
-			uint64 samplingrow_samples;
-			uint64 samplingrow_size;
-			if(td->td_samplesperpixel!=3)
-			{
-                            TIFFErrorExt(tif->tif_clientdata,module,
-                                         "Invalid td_samplesperpixel value");
-                            return 0;
-			}
-			TIFFGetFieldDefaulted(tif,TIFFTAG_YCBCRSUBSAMPLING,
-                                              ycbcrsubsampling+0,
-                                              ycbcrsubsampling+1);
-			if (((ycbcrsubsampling[0]!=1)&&(ycbcrsubsampling[0]!=2)&&(ycbcrsubsampling[0]!=4)) ||
-			    ((ycbcrsubsampling[1]!=1)&&(ycbcrsubsampling[1]!=2)&&(ycbcrsubsampling[1]!=4)))
-			{
-                            TIFFErrorExt(tif->tif_clientdata,module,
-                                         "Invalid YCbCr subsampling");
-                            return 0;
-			}
-			samplingblock_samples = ycbcrsubsampling[0]*ycbcrsubsampling[1]+2;
-			samplingblocks_hor = TIFFhowmany_32(td->td_imagewidth,ycbcrsubsampling[0]);
-			samplingrow_samples = _TIFFMultiply64(tif,samplingblocks_hor,samplingblock_samples,module);
-			samplingrow_size = TIFFhowmany_64(_TIFFMultiply64(tif,samplingrow_samples,td->td_bitspersample,module),8);
-			scanline_size = (samplingrow_size/ycbcrsubsampling[1]);
-		}
-		else
-		{
-			uint64 scanline_samples;
-			scanline_samples=_TIFFMultiply64(tif,td->td_imagewidth,td->td_samplesperpixel,module);
-			scanline_size=TIFFhowmany_64(_TIFFMultiply64(tif,scanline_samples,td->td_bitspersample,module),8);
-		}
-	}
-	else
+    static const char module[] = "TIFFScanlineSize64";
+    TIFFDirectory *td = &tif->tif_dir;
+    uint64_t scanline_size;
+    if (td->td_planarconfig == PLANARCONFIG_CONTIG)
+    {
+        if ((td->td_photometric == PHOTOMETRIC_YCBCR) &&
+            (td->td_samplesperpixel == 3) && (!isUpSampled(tif)))
         {
-		scanline_size=TIFFhowmany_64(_TIFFMultiply64(tif,td->td_imagewidth,td->td_bitspersample,module),8);
-        }
-        if (scanline_size == 0)
-        {
-                TIFFErrorExt(tif->tif_clientdata,module,"Computed scanline size is zero");
+            uint16_t ycbcrsubsampling[2];
+            uint16_t samplingblock_samples;
+            uint32_t samplingblocks_hor;
+            uint64_t samplingrow_samples;
+            uint64_t samplingrow_size;
+            if (td->td_samplesperpixel != 3)
+            {
+                TIFFErrorExtR(tif, module, "Invalid td_samplesperpixel value");
                 return 0;
+            }
+            TIFFGetFieldDefaulted(tif, TIFFTAG_YCBCRSUBSAMPLING,
+                                  ycbcrsubsampling + 0, ycbcrsubsampling + 1);
+            if (((ycbcrsubsampling[0] != 1) && (ycbcrsubsampling[0] != 2) &&
+                 (ycbcrsubsampling[0] != 4)) ||
+                ((ycbcrsubsampling[1] != 1) && (ycbcrsubsampling[1] != 2) &&
+                 (ycbcrsubsampling[1] != 4)))
+            {
+                TIFFErrorExtR(tif, module, "Invalid YCbCr subsampling");
+                return 0;
+            }
+            samplingblock_samples =
+                ycbcrsubsampling[0] * ycbcrsubsampling[1] + 2;
+            samplingblocks_hor =
+                TIFFhowmany_32(td->td_imagewidth, ycbcrsubsampling[0]);
+            samplingrow_samples = _TIFFMultiply64(
+                tif, samplingblocks_hor, samplingblock_samples, module);
+            samplingrow_size =
+                TIFFhowmany_64(_TIFFMultiply64(tif, samplingrow_samples,
+                                               td->td_bitspersample, module),
+                               8);
+            scanline_size = (samplingrow_size / ycbcrsubsampling[1]);
         }
-	return(scanline_size);
+        else
+        {
+            uint64_t scanline_samples;
+            scanline_samples = _TIFFMultiply64(tif, td->td_imagewidth,
+                                               td->td_samplesperpixel, module);
+            scanline_size =
+                TIFFhowmany_64(_TIFFMultiply64(tif, scanline_samples,
+                                               td->td_bitspersample, module),
+                               8);
+        }
+    }
+    else
+    {
+        scanline_size =
+            TIFFhowmany_64(_TIFFMultiply64(tif, td->td_imagewidth,
+                                           td->td_bitspersample, module),
+                           8);
+    }
+    if (scanline_size == 0)
+    {
+        TIFFErrorExtR(tif, module, "Computed scanline size is zero");
+        return 0;
+    }
+    return (scanline_size);
 }
-tmsize_t
-TIFFScanlineSize(TIFF* tif)
+tmsize_t TIFFScanlineSize(TIFF *tif)
 {
-	static const char module[] = "TIFFScanlineSize";
-	uint64 m;
-	m=TIFFScanlineSize64(tif);
-	return _TIFFCastUInt64ToSSize(tif, m, module);
+    static const char module[] = "TIFFScanlineSize";
+    uint64_t m;
+    m = TIFFScanlineSize64(tif);
+    return _TIFFCastUInt64ToSSize(tif, m, module);
 }
 
 /*
@@ -325,35 +323,28 @@
  * I/O size returned by TIFFScanlineSize which may be less
  * if data is store as separate planes).
  */
-uint64
-TIFFRasterScanlineSize64(TIFF* tif)
+uint64_t TIFFRasterScanlineSize64(TIFF *tif)
 {
-	static const char module[] = "TIFFRasterScanlineSize64";
-	TIFFDirectory *td = &tif->tif_dir;
-	uint64 scanline;
+    static const char module[] = "TIFFRasterScanlineSize64";
+    TIFFDirectory *td = &tif->tif_dir;
+    uint64_t scanline;
 
-	scanline = _TIFFMultiply64(tif, td->td_bitspersample, td->td_imagewidth, module);
-	if (td->td_planarconfig == PLANARCONFIG_CONTIG) {
-		scanline = _TIFFMultiply64(tif, scanline, td->td_samplesperpixel, module);
-		return (TIFFhowmany8_64(scanline));
-	} else
-		return (_TIFFMultiply64(tif, TIFFhowmany8_64(scanline),
-		    td->td_samplesperpixel, module));
+    scanline =
+        _TIFFMultiply64(tif, td->td_bitspersample, td->td_imagewidth, module);
+    if (td->td_planarconfig == PLANARCONFIG_CONTIG)
+    {
+        scanline =
+            _TIFFMultiply64(tif, scanline, td->td_samplesperpixel, module);
+        return (TIFFhowmany8_64(scanline));
+    }
+    else
+        return (_TIFFMultiply64(tif, TIFFhowmany8_64(scanline),
+                                td->td_samplesperpixel, module));
 }
-tmsize_t
-TIFFRasterScanlineSize(TIFF* tif)
+tmsize_t TIFFRasterScanlineSize(TIFF *tif)
 {
-	static const char module[] = "TIFFRasterScanlineSize";
-	uint64 m;
-	m=TIFFRasterScanlineSize64(tif);
-	return _TIFFCastUInt64ToSSize(tif, m, module);
+    static const char module[] = "TIFFRasterScanlineSize";
+    uint64_t m;
+    m = TIFFRasterScanlineSize64(tif);
+    return _TIFFCastUInt64ToSSize(tif, m, module);
 }
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_swab.c b/third_party/libtiff/tif_swab.c
index b174ba6..827b025 100644
--- a/third_party/libtiff/tif_swab.c
+++ b/third_party/libtiff/tif_swab.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -30,169 +30,218 @@
 #include "tiffiop.h"
 
 #if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabShort)
-void
-TIFFSwabShort(uint16* wp)
+void TIFFSwabShort(uint16_t *wp)
 {
-	register unsigned char* cp = (unsigned char*) wp;
-	unsigned char t;
-	assert(sizeof(uint16)==2);
-	t = cp[1]; cp[1] = cp[0]; cp[0] = t;
+    register unsigned char *cp = (unsigned char *)wp;
+    unsigned char t;
+    assert(sizeof(uint16_t) == 2);
+    t = cp[1];
+    cp[1] = cp[0];
+    cp[0] = t;
 }
 #endif
 
 #if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabLong)
-void
-TIFFSwabLong(uint32* lp)
+void TIFFSwabLong(uint32_t *lp)
 {
-	register unsigned char* cp = (unsigned char*) lp;
-	unsigned char t;
-	assert(sizeof(uint32)==4);
-	t = cp[3]; cp[3] = cp[0]; cp[0] = t;
-	t = cp[2]; cp[2] = cp[1]; cp[1] = t;
+    register unsigned char *cp = (unsigned char *)lp;
+    unsigned char t;
+    assert(sizeof(uint32_t) == 4);
+    t = cp[3];
+    cp[3] = cp[0];
+    cp[0] = t;
+    t = cp[2];
+    cp[2] = cp[1];
+    cp[1] = t;
 }
 #endif
 
 #if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabLong8)
-void
-TIFFSwabLong8(uint64* lp)
+void TIFFSwabLong8(uint64_t *lp)
 {
-	register unsigned char* cp = (unsigned char*) lp;
-	unsigned char t;
-	assert(sizeof(uint64)==8);
-	t = cp[7]; cp[7] = cp[0]; cp[0] = t;
-	t = cp[6]; cp[6] = cp[1]; cp[1] = t;
-	t = cp[5]; cp[5] = cp[2]; cp[2] = t;
-	t = cp[4]; cp[4] = cp[3]; cp[3] = t;
+    register unsigned char *cp = (unsigned char *)lp;
+    unsigned char t;
+    assert(sizeof(uint64_t) == 8);
+    t = cp[7];
+    cp[7] = cp[0];
+    cp[0] = t;
+    t = cp[6];
+    cp[6] = cp[1];
+    cp[1] = t;
+    t = cp[5];
+    cp[5] = cp[2];
+    cp[2] = t;
+    t = cp[4];
+    cp[4] = cp[3];
+    cp[3] = t;
 }
 #endif
 
 #if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabArrayOfShort)
-void
-TIFFSwabArrayOfShort(register uint16* wp, tmsize_t n)
+void TIFFSwabArrayOfShort(register uint16_t *wp, tmsize_t n)
 {
-	register unsigned char* cp;
-	register unsigned char t;
-	assert(sizeof(uint16)==2);
-	/* XXX unroll loop some */
-	while (n-- > 0) {
-		cp = (unsigned char*) wp;
-		t = cp[1]; cp[1] = cp[0]; cp[0] = t;
-		wp++;
-	}
+    register unsigned char *cp;
+    register unsigned char t;
+    assert(sizeof(uint16_t) == 2);
+    /* XXX unroll loop some */
+    while (n-- > 0)
+    {
+        cp = (unsigned char *)wp;
+        t = cp[1];
+        cp[1] = cp[0];
+        cp[0] = t;
+        wp++;
+    }
 }
 #endif
 
 #if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabArrayOfTriples)
-void
-TIFFSwabArrayOfTriples(register uint8* tp, tmsize_t n)
+void TIFFSwabArrayOfTriples(register uint8_t *tp, tmsize_t n)
 {
-	unsigned char* cp;
-	unsigned char t;
+    unsigned char *cp;
+    unsigned char t;
 
-	/* XXX unroll loop some */
-	while (n-- > 0) {
-		cp = (unsigned char*) tp;
-		t = cp[2]; cp[2] = cp[0]; cp[0] = t;
-		tp += 3;
-	}
+    /* XXX unroll loop some */
+    while (n-- > 0)
+    {
+        cp = (unsigned char *)tp;
+        t = cp[2];
+        cp[2] = cp[0];
+        cp[0] = t;
+        tp += 3;
+    }
 }
 #endif
 
 #if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabArrayOfLong)
-void
-TIFFSwabArrayOfLong(register uint32* lp, tmsize_t n)
+void TIFFSwabArrayOfLong(register uint32_t *lp, tmsize_t n)
 {
-	register unsigned char *cp;
-	register unsigned char t;
-	assert(sizeof(uint32)==4);
-	/* XXX unroll loop some */
-	while (n-- > 0) {
-		cp = (unsigned char *)lp;
-		t = cp[3]; cp[3] = cp[0]; cp[0] = t;
-		t = cp[2]; cp[2] = cp[1]; cp[1] = t;
-		lp++;
-	}
+    register unsigned char *cp;
+    register unsigned char t;
+    assert(sizeof(uint32_t) == 4);
+    /* XXX unroll loop some */
+    while (n-- > 0)
+    {
+        cp = (unsigned char *)lp;
+        t = cp[3];
+        cp[3] = cp[0];
+        cp[0] = t;
+        t = cp[2];
+        cp[2] = cp[1];
+        cp[1] = t;
+        lp++;
+    }
 }
 #endif
 
 #if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabArrayOfLong8)
-void
-TIFFSwabArrayOfLong8(register uint64* lp, tmsize_t n)
+void TIFFSwabArrayOfLong8(register uint64_t *lp, tmsize_t n)
 {
-	register unsigned char *cp;
-	register unsigned char t;
-	assert(sizeof(uint64)==8);
-	/* XXX unroll loop some */
-	while (n-- > 0) {
-		cp = (unsigned char *)lp;
-		t = cp[7]; cp[7] = cp[0]; cp[0] = t;
-		t = cp[6]; cp[6] = cp[1]; cp[1] = t;
-		t = cp[5]; cp[5] = cp[2]; cp[2] = t;
-		t = cp[4]; cp[4] = cp[3]; cp[3] = t;
-		lp++;
-	}
+    register unsigned char *cp;
+    register unsigned char t;
+    assert(sizeof(uint64_t) == 8);
+    /* XXX unroll loop some */
+    while (n-- > 0)
+    {
+        cp = (unsigned char *)lp;
+        t = cp[7];
+        cp[7] = cp[0];
+        cp[0] = t;
+        t = cp[6];
+        cp[6] = cp[1];
+        cp[1] = t;
+        t = cp[5];
+        cp[5] = cp[2];
+        cp[2] = t;
+        t = cp[4];
+        cp[4] = cp[3];
+        cp[3] = t;
+        lp++;
+    }
 }
 #endif
 
 #if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabFloat)
-void
-TIFFSwabFloat(float* fp)
+void TIFFSwabFloat(float *fp)
 {
-	register unsigned char* cp = (unsigned char*) fp;
-	unsigned char t;
-	assert(sizeof(float)==4);
-	t = cp[3]; cp[3] = cp[0]; cp[0] = t;
-	t = cp[2]; cp[2] = cp[1]; cp[1] = t;
+    register unsigned char *cp = (unsigned char *)fp;
+    unsigned char t;
+    assert(sizeof(float) == 4);
+    t = cp[3];
+    cp[3] = cp[0];
+    cp[0] = t;
+    t = cp[2];
+    cp[2] = cp[1];
+    cp[1] = t;
 }
 #endif
 
 #if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabArrayOfFloat)
-void
-TIFFSwabArrayOfFloat(register float* fp, tmsize_t n)
+void TIFFSwabArrayOfFloat(register float *fp, tmsize_t n)
 {
-	register unsigned char *cp;
-	register unsigned char t;
-	assert(sizeof(float)==4);
-	/* XXX unroll loop some */
-	while (n-- > 0) {
-		cp = (unsigned char *)fp;
-		t = cp[3]; cp[3] = cp[0]; cp[0] = t;
-		t = cp[2]; cp[2] = cp[1]; cp[1] = t;
-		fp++;
-	}
+    register unsigned char *cp;
+    register unsigned char t;
+    assert(sizeof(float) == 4);
+    /* XXX unroll loop some */
+    while (n-- > 0)
+    {
+        cp = (unsigned char *)fp;
+        t = cp[3];
+        cp[3] = cp[0];
+        cp[0] = t;
+        t = cp[2];
+        cp[2] = cp[1];
+        cp[1] = t;
+        fp++;
+    }
 }
 #endif
 
 #if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabDouble)
-void
-TIFFSwabDouble(double *dp)
+void TIFFSwabDouble(double *dp)
 {
-	register unsigned char* cp = (unsigned char*) dp;
-	unsigned char t;
-	assert(sizeof(double)==8);
-	t = cp[7]; cp[7] = cp[0]; cp[0] = t;
-	t = cp[6]; cp[6] = cp[1]; cp[1] = t;
-	t = cp[5]; cp[5] = cp[2]; cp[2] = t;
-	t = cp[4]; cp[4] = cp[3]; cp[3] = t;
+    register unsigned char *cp = (unsigned char *)dp;
+    unsigned char t;
+    assert(sizeof(double) == 8);
+    t = cp[7];
+    cp[7] = cp[0];
+    cp[0] = t;
+    t = cp[6];
+    cp[6] = cp[1];
+    cp[1] = t;
+    t = cp[5];
+    cp[5] = cp[2];
+    cp[2] = t;
+    t = cp[4];
+    cp[4] = cp[3];
+    cp[3] = t;
 }
 #endif
 
 #if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabArrayOfDouble)
-void
-TIFFSwabArrayOfDouble(double* dp, tmsize_t n)
+void TIFFSwabArrayOfDouble(double *dp, tmsize_t n)
 {
-	register unsigned char *cp;
-	register unsigned char t;
-	assert(sizeof(double)==8);
-	/* XXX unroll loop some */
-	while (n-- > 0) {
-		cp = (unsigned char *)dp;
-		t = cp[7]; cp[7] = cp[0]; cp[0] = t;
-		t = cp[6]; cp[6] = cp[1]; cp[1] = t;
-		t = cp[5]; cp[5] = cp[2]; cp[2] = t;
-		t = cp[4]; cp[4] = cp[3]; cp[3] = t;
-		dp++;
-	}
+    register unsigned char *cp;
+    register unsigned char t;
+    assert(sizeof(double) == 8);
+    /* XXX unroll loop some */
+    while (n-- > 0)
+    {
+        cp = (unsigned char *)dp;
+        t = cp[7];
+        cp[7] = cp[0];
+        cp[0] = t;
+        t = cp[6];
+        cp[6] = cp[1];
+        cp[1] = t;
+        t = cp[5];
+        cp[5] = cp[2];
+        cp[2] = t;
+        t = cp[4];
+        cp[4] = cp[3];
+        cp[3] = t;
+        dp++;
+    }
 }
 #endif
 
@@ -206,105 +255,75 @@
  * do not reverse bit values.
  */
 static const unsigned char TIFFBitRevTable[256] = {
-    0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
-    0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
-    0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
-    0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
-    0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
-    0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
-    0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
-    0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
-    0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
-    0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
-    0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
-    0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
-    0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
-    0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
-    0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
-    0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
-    0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
-    0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
-    0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
-    0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
-    0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
-    0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
-    0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
-    0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
-    0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
-    0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
-    0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
-    0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
-    0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
-    0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
-    0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
-    0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
-};
+    0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0,
+    0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+    0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4,
+    0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+    0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc,
+    0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+    0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca,
+    0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+    0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6,
+    0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+    0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1,
+    0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+    0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9,
+    0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+    0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd,
+    0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+    0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3,
+    0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+    0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7,
+    0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+    0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf,
+    0x3f, 0xbf, 0x7f, 0xff};
 static const unsigned char TIFFNoBitRevTable[256] = {
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
-    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 
-    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 
-    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 
-    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 
-    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 
-    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 
-    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 
-    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 
-    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 
-    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 
-    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 
-    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 
-    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 
-    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 
-    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 
-    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 
-    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 
-    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 
-    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 
-    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 
-    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 
-    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 
-    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 
-    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 
-    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 
-    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 
-    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 
-    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 
-    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 
-    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 
-    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+    0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+    0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+    0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53,
+    0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
+    0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
+    0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
+    0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3,
+    0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb,
+    0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3,
+    0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,
+    0xfc, 0xfd, 0xfe, 0xff,
 };
 
-const unsigned char*
-TIFFGetBitRevTable(int reversed)
+const unsigned char *TIFFGetBitRevTable(int reversed)
 {
-	return (reversed ? TIFFBitRevTable : TIFFNoBitRevTable);
+    return (reversed ? TIFFBitRevTable : TIFFNoBitRevTable);
 }
 
-void
-TIFFReverseBits(uint8* cp, tmsize_t n)  
+void TIFFReverseBits(uint8_t *cp, tmsize_t n)
 {
-	for (; n > 8; n -= 8) {
-		cp[0] = TIFFBitRevTable[cp[0]];
-		cp[1] = TIFFBitRevTable[cp[1]];
-		cp[2] = TIFFBitRevTable[cp[2]];
-		cp[3] = TIFFBitRevTable[cp[3]];
-		cp[4] = TIFFBitRevTable[cp[4]];
-		cp[5] = TIFFBitRevTable[cp[5]];
-		cp[6] = TIFFBitRevTable[cp[6]];
-		cp[7] = TIFFBitRevTable[cp[7]];
-		cp += 8;
-	}
-	while (n-- > 0) {
-		*cp = TIFFBitRevTable[*cp];
-		cp++;
-	}
+    for (; n > 8; n -= 8)
+    {
+        cp[0] = TIFFBitRevTable[cp[0]];
+        cp[1] = TIFFBitRevTable[cp[1]];
+        cp[2] = TIFFBitRevTable[cp[2]];
+        cp[3] = TIFFBitRevTable[cp[3]];
+        cp[4] = TIFFBitRevTable[cp[4]];
+        cp[5] = TIFFBitRevTable[cp[5]];
+        cp[6] = TIFFBitRevTable[cp[6]];
+        cp[7] = TIFFBitRevTable[cp[7]];
+        cp += 8;
+    }
+    while (n-- > 0)
+    {
+        *cp = TIFFBitRevTable[*cp];
+        cp++;
+    }
 }
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_thunder.c b/third_party/libtiff/tif_thunder.c
index db6383a..1f97362 100644
--- a/third_party/libtiff/tif_thunder.c
+++ b/third_party/libtiff/tif_thunder.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -41,166 +41,158 @@
  * or 3-bit delta values are used, with the deltas packed
  * into a single byte.
  */
-#define	THUNDER_DATA		0x3f	/* mask for 6-bit data */
-#define	THUNDER_CODE		0xc0	/* mask for 2-bit code word */
+#define THUNDER_DATA 0x3f /* mask for 6-bit data */
+#define THUNDER_CODE 0xc0 /* mask for 2-bit code word */
 /* code values */
-#define	THUNDER_RUN		0x00	/* run of pixels w/ encoded count */
-#define	THUNDER_2BITDELTAS	0x40	/* 3 pixels w/ encoded 2-bit deltas */
-#define	    DELTA2_SKIP		2	/* skip code for 2-bit deltas */
-#define	THUNDER_3BITDELTAS	0x80	/* 2 pixels w/ encoded 3-bit deltas */
-#define	    DELTA3_SKIP		4	/* skip code for 3-bit deltas */
-#define	THUNDER_RAW		0xc0	/* raw data encoded */
+#define THUNDER_RUN 0x00        /* run of pixels w/ encoded count */
+#define THUNDER_2BITDELTAS 0x40 /* 3 pixels w/ encoded 2-bit deltas */
+#define DELTA2_SKIP 2           /* skip code for 2-bit deltas */
+#define THUNDER_3BITDELTAS 0x80 /* 2 pixels w/ encoded 3-bit deltas */
+#define DELTA3_SKIP 4           /* skip code for 3-bit deltas */
+#define THUNDER_RAW 0xc0        /* raw data encoded */
 
-static const int twobitdeltas[4] = { 0, 1, 0, -1 };
-static const int threebitdeltas[8] = { 0, 1, 2, 3, 0, -3, -2, -1 };
+static const int twobitdeltas[4] = {0, 1, 0, -1};
+static const int threebitdeltas[8] = {0, 1, 2, 3, 0, -3, -2, -1};
 
-#define	SETPIXEL(op, v) {                     \
-	lastpixel = (v) & 0xf;                \
-        if ( npixels < maxpixels )         \
-        {                                     \
-	  if (npixels++ & 1)                  \
-	    *op++ |= lastpixel;               \
-	  else                                \
-	    op[0] = (uint8) (lastpixel << 4); \
-        }                                     \
+#define SETPIXEL(op, v)                                                        \
+    {                                                                          \
+        lastpixel = (v)&0xf;                                                   \
+        if (npixels < maxpixels)                                               \
+        {                                                                      \
+            if (npixels++ & 1)                                                 \
+                *op++ |= lastpixel;                                            \
+            else                                                               \
+                op[0] = (uint8_t)(lastpixel << 4);                             \
+        }                                                                      \
+    }
+
+static int ThunderSetupDecode(TIFF *tif)
+{
+    static const char module[] = "ThunderSetupDecode";
+
+    if (tif->tif_dir.td_bitspersample != 4)
+    {
+        TIFFErrorExtR(tif, module,
+                      "Wrong bitspersample value (%d), Thunder decoder only "
+                      "supports 4bits per sample.",
+                      (int)tif->tif_dir.td_bitspersample);
+        return 0;
+    }
+
+    return (1);
 }
 
-static int
-ThunderSetupDecode(TIFF* tif)
+static int ThunderDecode(TIFF *tif, uint8_t *op, tmsize_t maxpixels)
 {
-	static const char module[] = "ThunderSetupDecode";
+    static const char module[] = "ThunderDecode";
+    register unsigned char *bp;
+    register tmsize_t cc;
+    unsigned int lastpixel;
+    tmsize_t npixels;
 
-        if( tif->tif_dir.td_bitspersample != 4 )
+    bp = (unsigned char *)tif->tif_rawcp;
+    cc = tif->tif_rawcc;
+    lastpixel = 0;
+    npixels = 0;
+    while (cc > 0 && npixels < maxpixels)
+    {
+        int n, delta;
+
+        n = *bp++;
+        cc--;
+        switch (n & THUNDER_CODE)
         {
-                TIFFErrorExt(tif->tif_clientdata, module,
-                             "Wrong bitspersample value (%d), Thunder decoder only supports 4bits per sample.",
-                             (int) tif->tif_dir.td_bitspersample );
-                return 0;
+            case THUNDER_RUN: /* pixel run */
+                /*
+                 * Replicate the last pixel n times,
+                 * where n is the lower-order 6 bits.
+                 */
+                if (npixels & 1)
+                {
+                    op[0] |= lastpixel;
+                    lastpixel = *op++;
+                    npixels++;
+                    n--;
+                }
+                else
+                    lastpixel |= lastpixel << 4;
+                npixels += n;
+                if (npixels < maxpixels)
+                {
+                    for (; n > 0; n -= 2)
+                        *op++ = (uint8_t)lastpixel;
+                }
+                if (n == -1)
+                    *--op &= 0xf0;
+                lastpixel &= 0xf;
+                break;
+            case THUNDER_2BITDELTAS: /* 2-bit deltas */
+                if ((delta = ((n >> 4) & 3)) != DELTA2_SKIP)
+                    SETPIXEL(op,
+                             (unsigned)((int)lastpixel + twobitdeltas[delta]));
+                if ((delta = ((n >> 2) & 3)) != DELTA2_SKIP)
+                    SETPIXEL(op,
+                             (unsigned)((int)lastpixel + twobitdeltas[delta]));
+                if ((delta = (n & 3)) != DELTA2_SKIP)
+                    SETPIXEL(op,
+                             (unsigned)((int)lastpixel + twobitdeltas[delta]));
+                break;
+            case THUNDER_3BITDELTAS: /* 3-bit deltas */
+                if ((delta = ((n >> 3) & 7)) != DELTA3_SKIP)
+                    SETPIXEL(
+                        op, (unsigned)((int)lastpixel + threebitdeltas[delta]));
+                if ((delta = (n & 7)) != DELTA3_SKIP)
+                    SETPIXEL(
+                        op, (unsigned)((int)lastpixel + threebitdeltas[delta]));
+                break;
+            case THUNDER_RAW: /* raw data */
+                SETPIXEL(op, n);
+                break;
         }
-        
+    }
+    tif->tif_rawcp = (uint8_t *)bp;
+    tif->tif_rawcc = cc;
+    if (npixels != maxpixels)
+    {
+        TIFFErrorExtR(tif, module,
+                      "%s data at scanline %lu (%" PRIu64 " != %" PRIu64 ")",
+                      npixels < maxpixels ? "Not enough" : "Too much",
+                      (unsigned long)tif->tif_row, (uint64_t)npixels,
+                      (uint64_t)maxpixels);
+        return (0);
+    }
 
-	return (1);
+    return (1);
 }
 
-static int
-ThunderDecode(TIFF* tif, uint8* op, tmsize_t maxpixels)
+static int ThunderDecodeRow(TIFF *tif, uint8_t *buf, tmsize_t occ, uint16_t s)
 {
-	static const char module[] = "ThunderDecode";
-	register unsigned char *bp;
-	register tmsize_t cc;
-	unsigned int lastpixel;
-	tmsize_t npixels;
+    static const char module[] = "ThunderDecodeRow";
+    uint8_t *row = buf;
 
-	bp = (unsigned char *)tif->tif_rawcp;
-	cc = tif->tif_rawcc;
-	lastpixel = 0;
-	npixels = 0;
-	while (cc > 0 && npixels < maxpixels) {
-		int n, delta;
-
-		n = *bp++;
-		cc--;
-		switch (n & THUNDER_CODE) {
-		case THUNDER_RUN:		/* pixel run */
-			/*
-			 * Replicate the last pixel n times,
-			 * where n is the lower-order 6 bits.
-			 */
-			if (npixels & 1) {
-				op[0] |= lastpixel;
-				lastpixel = *op++; npixels++; n--;
-			} else
-				lastpixel |= lastpixel << 4;
-			npixels += n;
-			if (npixels < maxpixels) {
-				for (; n > 0; n -= 2)
-					*op++ = (uint8) lastpixel;
-			}
-			if (n == -1)
-				*--op &= 0xf0;
-			lastpixel &= 0xf;
-			break;
-		case THUNDER_2BITDELTAS:	/* 2-bit deltas */
-			if ((delta = ((n >> 4) & 3)) != DELTA2_SKIP)
-				SETPIXEL(op, (unsigned)((int)lastpixel + twobitdeltas[delta]));
-			if ((delta = ((n >> 2) & 3)) != DELTA2_SKIP)
-				SETPIXEL(op, (unsigned)((int)lastpixel + twobitdeltas[delta]));
-			if ((delta = (n & 3)) != DELTA2_SKIP)
-				SETPIXEL(op, (unsigned)((int)lastpixel + twobitdeltas[delta]));
-			break;
-		case THUNDER_3BITDELTAS:	/* 3-bit deltas */
-			if ((delta = ((n >> 3) & 7)) != DELTA3_SKIP)
-				SETPIXEL(op, (unsigned)((int)lastpixel + threebitdeltas[delta]));
-			if ((delta = (n & 7)) != DELTA3_SKIP)
-				SETPIXEL(op, (unsigned)((int)lastpixel + threebitdeltas[delta]));
-			break;
-		case THUNDER_RAW:		/* raw data */
-			SETPIXEL(op, n);
-			break;
-		}
-	}
-	tif->tif_rawcp = (uint8*) bp;
-	tif->tif_rawcc = cc;
-	if (npixels != maxpixels) {
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-		TIFFErrorExt(tif->tif_clientdata, module,
-			     "%s data at scanline %lu (%I64u != %I64u)",
-			     npixels < maxpixels ? "Not enough" : "Too much",
-			     (unsigned long) tif->tif_row,
-			     (unsigned __int64) npixels,
-			     (unsigned __int64) maxpixels);
-#else
-		TIFFErrorExt(tif->tif_clientdata, module,
-			     "%s data at scanline %lu (%llu != %llu)",
-			     npixels < maxpixels ? "Not enough" : "Too much",
-			     (unsigned long) tif->tif_row,
-			     (unsigned long long) npixels,
-			     (unsigned long long) maxpixels);
-#endif
-		return (0);
-	}
-
-        return (1);
+    (void)s;
+    if (occ % tif->tif_scanlinesize)
+    {
+        TIFFErrorExtR(tif, module, "Fractional scanlines cannot be read");
+        return (0);
+    }
+    while (occ > 0)
+    {
+        if (!ThunderDecode(tif, row, tif->tif_dir.td_imagewidth))
+            return (0);
+        occ -= tif->tif_scanlinesize;
+        row += tif->tif_scanlinesize;
+    }
+    return (1);
 }
 
-static int
-ThunderDecodeRow(TIFF* tif, uint8* buf, tmsize_t occ, uint16 s)
+int TIFFInitThunderScan(TIFF *tif, int scheme)
 {
-	static const char module[] = "ThunderDecodeRow";
-	uint8* row = buf;
-	
-	(void) s;
-	if (occ % tif->tif_scanlinesize)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "Fractional scanlines cannot be read");
-		return (0);
-	}
-	while (occ > 0) {
-		if (!ThunderDecode(tif, row, tif->tif_dir.td_imagewidth))
-			return (0);
-		occ -= tif->tif_scanlinesize;
-		row += tif->tif_scanlinesize;
-	}
-	return (1);
-}
+    (void)scheme;
 
-int
-TIFFInitThunderScan(TIFF* tif, int scheme)
-{
-	(void) scheme;
-
-        tif->tif_setupdecode = ThunderSetupDecode;
-	tif->tif_decoderow = ThunderDecodeRow;
-	tif->tif_decodestrip = ThunderDecodeRow; 
-	return (1);
+    tif->tif_setupdecode = ThunderSetupDecode;
+    tif->tif_decoderow = ThunderDecodeRow;
+    tif->tif_decodestrip = ThunderDecodeRow;
+    return (1);
 }
 #endif /* THUNDER_SUPPORT */
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_tile.c b/third_party/libtiff/tif_tile.c
index 661cc77..f07032f 100644
--- a/third_party/libtiff/tif_tile.c
+++ b/third_party/libtiff/tif_tile.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1991-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -32,234 +32,230 @@
 /*
  * Compute which tile an (x,y,z,s) value is in.
  */
-uint32
-TIFFComputeTile(TIFF* tif, uint32 x, uint32 y, uint32 z, uint16 s)
+uint32_t TIFFComputeTile(TIFF *tif, uint32_t x, uint32_t y, uint32_t z,
+                         uint16_t s)
 {
-	TIFFDirectory *td = &tif->tif_dir;
-	uint32 dx = td->td_tilewidth;
-	uint32 dy = td->td_tilelength;
-	uint32 dz = td->td_tiledepth;
-	uint32 tile = 1;
+    TIFFDirectory *td = &tif->tif_dir;
+    uint32_t dx = td->td_tilewidth;
+    uint32_t dy = td->td_tilelength;
+    uint32_t dz = td->td_tiledepth;
+    uint32_t tile = 1;
 
-	if (td->td_imagedepth == 1)
-		z = 0;
-	if (dx == (uint32) -1)
-		dx = td->td_imagewidth;
-	if (dy == (uint32) -1)
-		dy = td->td_imagelength;
-	if (dz == (uint32) -1)
-		dz = td->td_imagedepth;
-	if (dx != 0 && dy != 0 && dz != 0) {
-		uint32 xpt = TIFFhowmany_32(td->td_imagewidth, dx);
-		uint32 ypt = TIFFhowmany_32(td->td_imagelength, dy);
-		uint32 zpt = TIFFhowmany_32(td->td_imagedepth, dz);
+    if (td->td_imagedepth == 1)
+        z = 0;
+    if (dx == (uint32_t)-1)
+        dx = td->td_imagewidth;
+    if (dy == (uint32_t)-1)
+        dy = td->td_imagelength;
+    if (dz == (uint32_t)-1)
+        dz = td->td_imagedepth;
+    if (dx != 0 && dy != 0 && dz != 0)
+    {
+        uint32_t xpt = TIFFhowmany_32(td->td_imagewidth, dx);
+        uint32_t ypt = TIFFhowmany_32(td->td_imagelength, dy);
+        uint32_t zpt = TIFFhowmany_32(td->td_imagedepth, dz);
 
-		if (td->td_planarconfig == PLANARCONFIG_SEPARATE) 
-			tile = (xpt*ypt*zpt)*s +
-			     (xpt*ypt)*(z/dz) +
-			     xpt*(y/dy) +
-			     x/dx;
-		else
-			tile = (xpt*ypt)*(z/dz) + xpt*(y/dy) + x/dx;
-	}
-	return (tile);
+        if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
+            tile = (xpt * ypt * zpt) * s + (xpt * ypt) * (z / dz) +
+                   xpt * (y / dy) + x / dx;
+        else
+            tile = (xpt * ypt) * (z / dz) + xpt * (y / dy) + x / dx;
+    }
+    return (tile);
 }
 
 /*
  * Check an (x,y,z,s) coordinate
  * against the image bounds.
  */
-int
-TIFFCheckTile(TIFF* tif, uint32 x, uint32 y, uint32 z, uint16 s)
+int TIFFCheckTile(TIFF *tif, uint32_t x, uint32_t y, uint32_t z, uint16_t s)
 {
-	TIFFDirectory *td = &tif->tif_dir;
+    TIFFDirectory *td = &tif->tif_dir;
 
-	if (x >= td->td_imagewidth) {
-		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-			     "%lu: Col out of range, max %lu",
-			     (unsigned long) x,
-			     (unsigned long) (td->td_imagewidth - 1));
-		return (0);
-	}
-	if (y >= td->td_imagelength) {
-		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-			     "%lu: Row out of range, max %lu",
-			     (unsigned long) y,
-			     (unsigned long) (td->td_imagelength - 1));
-		return (0);
-	}
-	if (z >= td->td_imagedepth) {
-		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-			     "%lu: Depth out of range, max %lu",
-			     (unsigned long) z,
-			     (unsigned long) (td->td_imagedepth - 1));
-		return (0);
-	}
-	if (td->td_planarconfig == PLANARCONFIG_SEPARATE &&
-	    s >= td->td_samplesperpixel) {
-		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
-			     "%lu: Sample out of range, max %lu",
-			     (unsigned long) s,
-			     (unsigned long) (td->td_samplesperpixel - 1));
-		return (0);
-	}
-	return (1);
+    if (x >= td->td_imagewidth)
+    {
+        TIFFErrorExtR(tif, tif->tif_name, "%lu: Col out of range, max %lu",
+                      (unsigned long)x, (unsigned long)(td->td_imagewidth - 1));
+        return (0);
+    }
+    if (y >= td->td_imagelength)
+    {
+        TIFFErrorExtR(tif, tif->tif_name, "%lu: Row out of range, max %lu",
+                      (unsigned long)y,
+                      (unsigned long)(td->td_imagelength - 1));
+        return (0);
+    }
+    if (z >= td->td_imagedepth)
+    {
+        TIFFErrorExtR(tif, tif->tif_name, "%lu: Depth out of range, max %lu",
+                      (unsigned long)z, (unsigned long)(td->td_imagedepth - 1));
+        return (0);
+    }
+    if (td->td_planarconfig == PLANARCONFIG_SEPARATE &&
+        s >= td->td_samplesperpixel)
+    {
+        TIFFErrorExtR(tif, tif->tif_name, "%lu: Sample out of range, max %lu",
+                      (unsigned long)s,
+                      (unsigned long)(td->td_samplesperpixel - 1));
+        return (0);
+    }
+    return (1);
 }
 
 /*
  * Compute how many tiles are in an image.
  */
-uint32
-TIFFNumberOfTiles(TIFF* tif)
+uint32_t TIFFNumberOfTiles(TIFF *tif)
 {
-	TIFFDirectory *td = &tif->tif_dir;
-	uint32 dx = td->td_tilewidth;
-	uint32 dy = td->td_tilelength;
-	uint32 dz = td->td_tiledepth;
-	uint32 ntiles;
+    TIFFDirectory *td = &tif->tif_dir;
+    uint32_t dx = td->td_tilewidth;
+    uint32_t dy = td->td_tilelength;
+    uint32_t dz = td->td_tiledepth;
+    uint32_t ntiles;
 
-	if (dx == (uint32) -1)
-		dx = td->td_imagewidth;
-	if (dy == (uint32) -1)
-		dy = td->td_imagelength;
-	if (dz == (uint32) -1)
-		dz = td->td_imagedepth;
-	ntiles = (dx == 0 || dy == 0 || dz == 0) ? 0 :
-	    _TIFFMultiply32(tif, _TIFFMultiply32(tif, TIFFhowmany_32(td->td_imagewidth, dx),
-	    TIFFhowmany_32(td->td_imagelength, dy),
-	    "TIFFNumberOfTiles"),
-	    TIFFhowmany_32(td->td_imagedepth, dz), "TIFFNumberOfTiles");
-	if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
-		ntiles = _TIFFMultiply32(tif, ntiles, td->td_samplesperpixel,
-		    "TIFFNumberOfTiles");
-	return (ntiles);
+    if (dx == (uint32_t)-1)
+        dx = td->td_imagewidth;
+    if (dy == (uint32_t)-1)
+        dy = td->td_imagelength;
+    if (dz == (uint32_t)-1)
+        dz = td->td_imagedepth;
+    ntiles =
+        (dx == 0 || dy == 0 || dz == 0)
+            ? 0
+            : _TIFFMultiply32(
+                  tif,
+                  _TIFFMultiply32(tif, TIFFhowmany_32(td->td_imagewidth, dx),
+                                  TIFFhowmany_32(td->td_imagelength, dy),
+                                  "TIFFNumberOfTiles"),
+                  TIFFhowmany_32(td->td_imagedepth, dz), "TIFFNumberOfTiles");
+    if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
+        ntiles = _TIFFMultiply32(tif, ntiles, td->td_samplesperpixel,
+                                 "TIFFNumberOfTiles");
+    return (ntiles);
 }
 
 /*
  * Compute the # bytes in each row of a tile.
  */
-uint64
-TIFFTileRowSize64(TIFF* tif)
+uint64_t TIFFTileRowSize64(TIFF *tif)
 {
-        static const char module[] = "TIFFTileRowSize64";
-	TIFFDirectory *td = &tif->tif_dir;
-	uint64 rowsize;
-	uint64 tilerowsize;
+    static const char module[] = "TIFFTileRowSize64";
+    TIFFDirectory *td = &tif->tif_dir;
+    uint64_t rowsize;
+    uint64_t tilerowsize;
 
-	if (td->td_tilelength == 0)
+    if (td->td_tilelength == 0)
+    {
+        TIFFErrorExtR(tif, module, "Tile length is zero");
+        return 0;
+    }
+    if (td->td_tilewidth == 0)
+    {
+        TIFFErrorExtR(tif, module, "Tile width is zero");
+        return (0);
+    }
+    rowsize = _TIFFMultiply64(tif, td->td_bitspersample, td->td_tilewidth,
+                              "TIFFTileRowSize");
+    if (td->td_planarconfig == PLANARCONFIG_CONTIG)
+    {
+        if (td->td_samplesperpixel == 0)
         {
-                TIFFErrorExt(tif->tif_clientdata,module,"Tile length is zero");
-                return 0;
+            TIFFErrorExtR(tif, module, "Samples per pixel is zero");
+            return 0;
         }
-        if (td->td_tilewidth == 0)
-        {
-                TIFFErrorExt(tif->tif_clientdata,module,"Tile width is zero");
-		return (0);
-        }
-	rowsize = _TIFFMultiply64(tif, td->td_bitspersample, td->td_tilewidth,
-	    "TIFFTileRowSize");
-	if (td->td_planarconfig == PLANARCONFIG_CONTIG)
-        {
-                if (td->td_samplesperpixel == 0)
-                {
-                        TIFFErrorExt(tif->tif_clientdata,module,"Samples per pixel is zero");
-                        return 0;
-                }
-		rowsize = _TIFFMultiply64(tif, rowsize, td->td_samplesperpixel,
-		    "TIFFTileRowSize");
-        }
-        tilerowsize=TIFFhowmany8_64(rowsize);
-        if (tilerowsize == 0)
-        {
-                TIFFErrorExt(tif->tif_clientdata,module,"Computed tile row size is zero");
-                return 0;
-        }
-	return (tilerowsize);
+        rowsize = _TIFFMultiply64(tif, rowsize, td->td_samplesperpixel,
+                                  "TIFFTileRowSize");
+    }
+    tilerowsize = TIFFhowmany8_64(rowsize);
+    if (tilerowsize == 0)
+    {
+        TIFFErrorExtR(tif, module, "Computed tile row size is zero");
+        return 0;
+    }
+    return (tilerowsize);
 }
-tmsize_t
-TIFFTileRowSize(TIFF* tif)
+tmsize_t TIFFTileRowSize(TIFF *tif)
 {
-	static const char module[] = "TIFFTileRowSize";
-	uint64 m;
-	m=TIFFTileRowSize64(tif);
-	return _TIFFCastUInt64ToSSize(tif, m, module);
+    static const char module[] = "TIFFTileRowSize";
+    uint64_t m;
+    m = TIFFTileRowSize64(tif);
+    return _TIFFCastUInt64ToSSize(tif, m, module);
 }
 
 /*
  * Compute the # bytes in a variable length, row-aligned tile.
  */
-uint64
-TIFFVTileSize64(TIFF* tif, uint32 nrows)
+uint64_t TIFFVTileSize64(TIFF *tif, uint32_t nrows)
 {
-	static const char module[] = "TIFFVTileSize64";
-	TIFFDirectory *td = &tif->tif_dir;
-	if (td->td_tilelength == 0 || td->td_tilewidth == 0 ||
-	    td->td_tiledepth == 0)
-		return (0);
-	if ((td->td_planarconfig==PLANARCONFIG_CONTIG)&&
-	    (td->td_photometric==PHOTOMETRIC_YCBCR)&&
-	    (td->td_samplesperpixel==3)&&
-	    (!isUpSampled(tif)))
-	{
-		/*
-		 * Packed YCbCr data contain one Cb+Cr for every
-		 * HorizontalSampling*VerticalSampling Y values.
-		 * Must also roundup width and height when calculating
-		 * since images that are not a multiple of the
-		 * horizontal/vertical subsampling area include
-		 * YCbCr data for the extended image.
-		 */
-		uint16 ycbcrsubsampling[2];
-		uint16 samplingblock_samples;
-		uint32 samplingblocks_hor;
-		uint32 samplingblocks_ver;
-		uint64 samplingrow_samples;
-		uint64 samplingrow_size;
-		TIFFGetFieldDefaulted(tif,TIFFTAG_YCBCRSUBSAMPLING,ycbcrsubsampling+0,
-		    ycbcrsubsampling+1);
-		if ((ycbcrsubsampling[0] != 1 && ycbcrsubsampling[0] != 2 && ycbcrsubsampling[0] != 4)
-		    ||(ycbcrsubsampling[1] != 1 && ycbcrsubsampling[1] != 2 && ycbcrsubsampling[1] != 4))
-		{
-			TIFFErrorExt(tif->tif_clientdata,module,
-				     "Invalid YCbCr subsampling (%dx%d)", 
-				     ycbcrsubsampling[0], 
-				     ycbcrsubsampling[1] );
-			return 0;
-		}
-		samplingblock_samples=ycbcrsubsampling[0]*ycbcrsubsampling[1]+2;
-		samplingblocks_hor=TIFFhowmany_32(td->td_tilewidth,ycbcrsubsampling[0]);
-		samplingblocks_ver=TIFFhowmany_32(nrows,ycbcrsubsampling[1]);
-		samplingrow_samples=_TIFFMultiply64(tif,samplingblocks_hor,samplingblock_samples,module);
-		samplingrow_size=TIFFhowmany8_64(_TIFFMultiply64(tif,samplingrow_samples,td->td_bitspersample,module));
-		return(_TIFFMultiply64(tif,samplingrow_size,samplingblocks_ver,module));
-	}
-	else
-		return(_TIFFMultiply64(tif,nrows,TIFFTileRowSize64(tif),module));
+    static const char module[] = "TIFFVTileSize64";
+    TIFFDirectory *td = &tif->tif_dir;
+    if (td->td_tilelength == 0 || td->td_tilewidth == 0 ||
+        td->td_tiledepth == 0)
+        return (0);
+    if ((td->td_planarconfig == PLANARCONFIG_CONTIG) &&
+        (td->td_photometric == PHOTOMETRIC_YCBCR) &&
+        (td->td_samplesperpixel == 3) && (!isUpSampled(tif)))
+    {
+        /*
+         * Packed YCbCr data contain one Cb+Cr for every
+         * HorizontalSampling*VerticalSampling Y values.
+         * Must also roundup width and height when calculating
+         * since images that are not a multiple of the
+         * horizontal/vertical subsampling area include
+         * YCbCr data for the extended image.
+         */
+        uint16_t ycbcrsubsampling[2];
+        uint16_t samplingblock_samples;
+        uint32_t samplingblocks_hor;
+        uint32_t samplingblocks_ver;
+        uint64_t samplingrow_samples;
+        uint64_t samplingrow_size;
+        TIFFGetFieldDefaulted(tif, TIFFTAG_YCBCRSUBSAMPLING,
+                              ycbcrsubsampling + 0, ycbcrsubsampling + 1);
+        if ((ycbcrsubsampling[0] != 1 && ycbcrsubsampling[0] != 2 &&
+             ycbcrsubsampling[0] != 4) ||
+            (ycbcrsubsampling[1] != 1 && ycbcrsubsampling[1] != 2 &&
+             ycbcrsubsampling[1] != 4))
+        {
+            TIFFErrorExtR(tif, module, "Invalid YCbCr subsampling (%dx%d)",
+                          ycbcrsubsampling[0], ycbcrsubsampling[1]);
+            return 0;
+        }
+        samplingblock_samples = ycbcrsubsampling[0] * ycbcrsubsampling[1] + 2;
+        samplingblocks_hor =
+            TIFFhowmany_32(td->td_tilewidth, ycbcrsubsampling[0]);
+        samplingblocks_ver = TIFFhowmany_32(nrows, ycbcrsubsampling[1]);
+        samplingrow_samples = _TIFFMultiply64(tif, samplingblocks_hor,
+                                              samplingblock_samples, module);
+        samplingrow_size = TIFFhowmany8_64(_TIFFMultiply64(
+            tif, samplingrow_samples, td->td_bitspersample, module));
+        return (
+            _TIFFMultiply64(tif, samplingrow_size, samplingblocks_ver, module));
+    }
+    else
+        return (_TIFFMultiply64(tif, nrows, TIFFTileRowSize64(tif), module));
 }
-tmsize_t
-TIFFVTileSize(TIFF* tif, uint32 nrows)
+tmsize_t TIFFVTileSize(TIFF *tif, uint32_t nrows)
 {
-	static const char module[] = "TIFFVTileSize";
-	uint64 m;
-	m=TIFFVTileSize64(tif,nrows);
-	return _TIFFCastUInt64ToSSize(tif, m, module);
+    static const char module[] = "TIFFVTileSize";
+    uint64_t m;
+    m = TIFFVTileSize64(tif, nrows);
+    return _TIFFCastUInt64ToSSize(tif, m, module);
 }
 
 /*
  * Compute the # bytes in a row-aligned tile.
  */
-uint64
-TIFFTileSize64(TIFF* tif)
+uint64_t TIFFTileSize64(TIFF *tif)
 {
-	return (TIFFVTileSize64(tif, tif->tif_dir.td_tilelength));
+    return (TIFFVTileSize64(tif, tif->tif_dir.td_tilelength));
 }
-tmsize_t
-TIFFTileSize(TIFF* tif)
+tmsize_t TIFFTileSize(TIFF *tif)
 {
-	static const char module[] = "TIFFTileSize";
-	uint64 m;
-	m=TIFFTileSize64(tif);
-	return _TIFFCastUInt64ToSSize(tif, m, module);
+    static const char module[] = "TIFFTileSize";
+    uint64_t m;
+    m = TIFFTileSize64(tif);
+    return _TIFFCastUInt64ToSSize(tif, m, module);
 }
 
 /*
@@ -268,32 +264,21 @@
  * request is <1 then we choose a size according
  * to certain heuristics.
  */
-void
-TIFFDefaultTileSize(TIFF* tif, uint32* tw, uint32* th)
+void TIFFDefaultTileSize(TIFF *tif, uint32_t *tw, uint32_t *th)
 {
-	(*tif->tif_deftilesize)(tif, tw, th);
+    (*tif->tif_deftilesize)(tif, tw, th);
 }
 
-void
-_TIFFDefaultTileSize(TIFF* tif, uint32* tw, uint32* th)
+void _TIFFDefaultTileSize(TIFF *tif, uint32_t *tw, uint32_t *th)
 {
-	(void) tif;
-	if (*(int32*) tw < 1)
-		*tw = 256;
-	if (*(int32*) th < 1)
-		*th = 256;
-	/* roundup to a multiple of 16 per the spec */
-	if (*tw & 0xf)
-		*tw = TIFFroundup_32(*tw, 16);
-	if (*th & 0xf)
-		*th = TIFFroundup_32(*th, 16);
+    (void)tif;
+    if (*(int32_t *)tw < 1)
+        *tw = 256;
+    if (*(int32_t *)th < 1)
+        *th = 256;
+    /* roundup to a multiple of 16 per the spec */
+    if (*tw & 0xf)
+        *tw = TIFFroundup_32(*tw, 16);
+    if (*th & 0xf)
+        *th = TIFFroundup_32(*th, 16);
 }
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_version.c b/third_party/libtiff/tif_version.c
index 60875bb..0b6c9bc 100644
--- a/third_party/libtiff/tif_version.c
+++ b/third_party/libtiff/tif_version.c
@@ -2,38 +2,27 @@
  * Copyright (c) 1992-1997 Sam Leffler
  * Copyright (c) 1992-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 #include "tiffiop.h"
 
 static const char TIFFVersion[] = TIFFLIB_VERSION_STR;
 
-const char*
-TIFFGetVersion(void)
-{
-	return (TIFFVersion);
-}
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
+const char *TIFFGetVersion(void) { return (TIFFVersion); }
diff --git a/third_party/libtiff/tif_warning.c b/third_party/libtiff/tif_warning.c
index c482785..5468de5 100644
--- a/third_party/libtiff/tif_warning.c
+++ b/third_party/libtiff/tif_warning.c
@@ -2,23 +2,23 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
@@ -29,59 +29,77 @@
 
 TIFFErrorHandlerExt _TIFFwarningHandlerExt = NULL;
 
-TIFFErrorHandler
-TIFFSetWarningHandler(TIFFErrorHandler handler)
+TIFFErrorHandler TIFFSetWarningHandler(TIFFErrorHandler handler)
 {
-	TIFFErrorHandler prev = _TIFFwarningHandler;
-	_TIFFwarningHandler = handler;
-	return (prev);
+    TIFFErrorHandler prev = _TIFFwarningHandler;
+    _TIFFwarningHandler = handler;
+    return (prev);
 }
 
-TIFFErrorHandlerExt
-TIFFSetWarningHandlerExt(TIFFErrorHandlerExt handler)
+TIFFErrorHandlerExt TIFFSetWarningHandlerExt(TIFFErrorHandlerExt handler)
 {
-	TIFFErrorHandlerExt prev = _TIFFwarningHandlerExt;
-	_TIFFwarningHandlerExt = handler;
-	return (prev);
+    TIFFErrorHandlerExt prev = _TIFFwarningHandlerExt;
+    _TIFFwarningHandlerExt = handler;
+    return (prev);
 }
 
-void
-TIFFWarning(const char* module, const char* fmt, ...)
+void TIFFWarning(const char *module, const char *fmt, ...)
 {
-	va_list ap;
-	if (_TIFFwarningHandler) {
-		va_start(ap, fmt);
-		(*_TIFFwarningHandler)(module, fmt, ap);
-		va_end(ap);
-	}
-	if (_TIFFwarningHandlerExt) {
-		va_start(ap, fmt);
-		(*_TIFFwarningHandlerExt)(0, module, fmt, ap);
-		va_end(ap);
-	}
+    va_list ap;
+    if (_TIFFwarningHandler)
+    {
+        va_start(ap, fmt);
+        (*_TIFFwarningHandler)(module, fmt, ap);
+        va_end(ap);
+    }
+    if (_TIFFwarningHandlerExt)
+    {
+        va_start(ap, fmt);
+        (*_TIFFwarningHandlerExt)(0, module, fmt, ap);
+        va_end(ap);
+    }
 }
 
-void
-TIFFWarningExt(thandle_t fd, const char* module, const char* fmt, ...)
+void TIFFWarningExt(thandle_t fd, const char *module, const char *fmt, ...)
 {
-	va_list ap;
-	if (_TIFFwarningHandler) {
-		va_start(ap, fmt);	
-		(*_TIFFwarningHandler)(module, fmt, ap);
-		va_end(ap);
-	}
-	if (_TIFFwarningHandlerExt) {
-		va_start(ap, fmt);
-		(*_TIFFwarningHandlerExt)(fd, module, fmt, ap);
-		va_end(ap);
-	}
+    va_list ap;
+    if (_TIFFwarningHandler)
+    {
+        va_start(ap, fmt);
+        (*_TIFFwarningHandler)(module, fmt, ap);
+        va_end(ap);
+    }
+    if (_TIFFwarningHandlerExt)
+    {
+        va_start(ap, fmt);
+        (*_TIFFwarningHandlerExt)(fd, module, fmt, ap);
+        va_end(ap);
+    }
 }
 
-
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
+void TIFFWarningExtR(TIFF *tif, const char *module, const char *fmt, ...)
+{
+    va_list ap;
+    if (tif && tif->tif_warnhandler)
+    {
+        va_start(ap, fmt);
+        int stop = (*tif->tif_warnhandler)(tif, tif->tif_warnhandler_user_data,
+                                           module, fmt, ap);
+        va_end(ap);
+        if (stop)
+            return;
+    }
+    if (_TIFFwarningHandler)
+    {
+        va_start(ap, fmt);
+        (*_TIFFwarningHandler)(module, fmt, ap);
+        va_end(ap);
+    }
+    if (_TIFFwarningHandlerExt)
+    {
+        va_start(ap, fmt);
+        (*_TIFFwarningHandlerExt)(tif ? tif->tif_clientdata : 0, module, fmt,
+                                  ap);
+        va_end(ap);
+    }
+}
diff --git a/third_party/libtiff/tif_write.c b/third_party/libtiff/tif_write.c
index 33e803c..6631a78 100644
--- a/third_party/libtiff/tif_write.c
+++ b/third_party/libtiff/tif_write.c
@@ -30,174 +30,178 @@
 #include "tiffiop.h"
 #include <stdio.h>
 
-#define STRIPINCR	20		/* expansion factor on strip array */
+#define STRIPINCR 20 /* expansion factor on strip array */
 
-#define WRITECHECKSTRIPS(tif, module)				\
-	(((tif)->tif_flags&TIFF_BEENWRITING) || TIFFWriteCheck((tif),0,module))
-#define WRITECHECKTILES(tif, module)				\
-	(((tif)->tif_flags&TIFF_BEENWRITING) || TIFFWriteCheck((tif),1,module))
-#define BUFFERCHECK(tif)					\
-	((((tif)->tif_flags & TIFF_BUFFERSETUP) && tif->tif_rawdata) ||	\
-	    TIFFWriteBufferSetup((tif), NULL, (tmsize_t) -1))
+#define WRITECHECKSTRIPS(tif, module)                                          \
+    (((tif)->tif_flags & TIFF_BEENWRITING) || TIFFWriteCheck((tif), 0, module))
+#define WRITECHECKTILES(tif, module)                                           \
+    (((tif)->tif_flags & TIFF_BEENWRITING) || TIFFWriteCheck((tif), 1, module))
+#define BUFFERCHECK(tif)                                                       \
+    ((((tif)->tif_flags & TIFF_BUFFERSETUP) && tif->tif_rawdata) ||            \
+     TIFFWriteBufferSetup((tif), NULL, (tmsize_t)-1))
 
-static int TIFFGrowStrips(TIFF* tif, uint32 delta, const char* module);
-static int TIFFAppendToStrip(TIFF* tif, uint32 strip, uint8* data, tmsize_t cc);
+static int TIFFGrowStrips(TIFF *tif, uint32_t delta, const char *module);
+static int TIFFAppendToStrip(TIFF *tif, uint32_t strip, uint8_t *data,
+                             tmsize_t cc);
 
-int
-TIFFWriteScanline(TIFF* tif, void* buf, uint32 row, uint16 sample)
+int TIFFWriteScanline(TIFF *tif, void *buf, uint32_t row, uint16_t sample)
 {
-	static const char module[] = "TIFFWriteScanline";
-	register TIFFDirectory *td;
-	int status, imagegrew = 0;
-	uint32 strip;
+    static const char module[] = "TIFFWriteScanline";
+    register TIFFDirectory *td;
+    int status, imagegrew = 0;
+    uint32_t strip;
 
-	if (!WRITECHECKSTRIPS(tif, module))
-		return (-1);
-	/*
-	 * Handle delayed allocation of data buffer.  This
-	 * permits it to be sized more intelligently (using
-	 * directory information).
-	 */
-	if (!BUFFERCHECK(tif))
-		return (-1);
-        tif->tif_flags |= TIFF_BUF4WRITE; /* not strictly sure this is right*/
+    if (!WRITECHECKSTRIPS(tif, module))
+        return (-1);
+    /*
+     * Handle delayed allocation of data buffer.  This
+     * permits it to be sized more intelligently (using
+     * directory information).
+     */
+    if (!BUFFERCHECK(tif))
+        return (-1);
+    tif->tif_flags |= TIFF_BUF4WRITE; /* not strictly sure this is right*/
 
-	td = &tif->tif_dir;
-	/*
-	 * Extend image length if needed
-	 * (but only for PlanarConfig=1).
-	 */
-	if (row >= td->td_imagelength) {	/* extend image */
-		if (td->td_planarconfig == PLANARCONFIG_SEPARATE) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Can not change \"ImageLength\" when using separate planes");
-			return (-1);
-		}
-		td->td_imagelength = row+1;
-		imagegrew = 1;
-	}
-	/*
-	 * Calculate strip and check for crossings.
-	 */
-	if (td->td_planarconfig == PLANARCONFIG_SEPARATE) {
-		if (sample >= td->td_samplesperpixel) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "%lu: Sample out of range, max %lu",
-			    (unsigned long) sample, (unsigned long) td->td_samplesperpixel);
-			return (-1);
-		}
-		strip = sample*td->td_stripsperimage + row/td->td_rowsperstrip;
-	} else
-		strip = row / td->td_rowsperstrip;
-	/*
-	 * Check strip array to make sure there's space. We don't support
-	 * dynamically growing files that have data organized in separate
-	 * bitplanes because it's too painful.  In that case we require that
-	 * the imagelength be set properly before the first write (so that the
-	 * strips array will be fully allocated above).
-	 */
-	if (strip >= td->td_nstrips && !TIFFGrowStrips(tif, 1, module))
-		return (-1);
-	if (strip != tif->tif_curstrip) {
-		/*
-		 * Changing strips -- flush any data present.
-		 */
-		if (!TIFFFlushData(tif))
-			return (-1);
-		tif->tif_curstrip = strip;
-		/*
-		 * Watch out for a growing image.  The value of strips/image
-		 * will initially be 1 (since it can't be deduced until the
-		 * imagelength is known).
-		 */
-		if (strip >= td->td_stripsperimage && imagegrew)
-			td->td_stripsperimage =
-			    TIFFhowmany_32(td->td_imagelength,td->td_rowsperstrip);
-                if (td->td_stripsperimage == 0) {
-                        TIFFErrorExt(tif->tif_clientdata, module, "Zero strips per image");
-                        return (-1);
-                }
-		tif->tif_row =
-		    (strip % td->td_stripsperimage) * td->td_rowsperstrip;
-		if ((tif->tif_flags & TIFF_CODERSETUP) == 0) {
-			if (!(*tif->tif_setupencode)(tif))
-				return (-1);
-			tif->tif_flags |= TIFF_CODERSETUP;
-		}
-        
-		tif->tif_rawcc = 0;
-		tif->tif_rawcp = tif->tif_rawdata;
+    td = &tif->tif_dir;
+    /*
+     * Extend image length if needed
+     * (but only for PlanarConfig=1).
+     */
+    if (row >= td->td_imagelength)
+    { /* extend image */
+        if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
+        {
+            TIFFErrorExtR(
+                tif, module,
+                "Can not change \"ImageLength\" when using separate planes");
+            return (-1);
+        }
+        td->td_imagelength = row + 1;
+        imagegrew = 1;
+    }
+    /*
+     * Calculate strip and check for crossings.
+     */
+    if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
+    {
+        if (sample >= td->td_samplesperpixel)
+        {
+            TIFFErrorExtR(tif, module, "%lu: Sample out of range, max %lu",
+                          (unsigned long)sample,
+                          (unsigned long)td->td_samplesperpixel);
+            return (-1);
+        }
+        strip = sample * td->td_stripsperimage + row / td->td_rowsperstrip;
+    }
+    else
+        strip = row / td->td_rowsperstrip;
+    /*
+     * Check strip array to make sure there's space. We don't support
+     * dynamically growing files that have data organized in separate
+     * bitplanes because it's too painful.  In that case we require that
+     * the imagelength be set properly before the first write (so that the
+     * strips array will be fully allocated above).
+     */
+    if (strip >= td->td_nstrips && !TIFFGrowStrips(tif, 1, module))
+        return (-1);
+    if (strip != tif->tif_curstrip)
+    {
+        /*
+         * Changing strips -- flush any data present.
+         */
+        if (!TIFFFlushData(tif))
+            return (-1);
+        tif->tif_curstrip = strip;
+        /*
+         * Watch out for a growing image.  The value of strips/image
+         * will initially be 1 (since it can't be deduced until the
+         * imagelength is known).
+         */
+        if (strip >= td->td_stripsperimage && imagegrew)
+            td->td_stripsperimage =
+                TIFFhowmany_32(td->td_imagelength, td->td_rowsperstrip);
+        if (td->td_stripsperimage == 0)
+        {
+            TIFFErrorExtR(tif, module, "Zero strips per image");
+            return (-1);
+        }
+        tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip;
+        if ((tif->tif_flags & TIFF_CODERSETUP) == 0)
+        {
+            if (!(*tif->tif_setupencode)(tif))
+                return (-1);
+            tif->tif_flags |= TIFF_CODERSETUP;
+        }
 
-		if( td->td_stripbytecount_p[strip] > 0 )
-		{
-			/* if we are writing over existing tiles, zero length */
-			td->td_stripbytecount_p[strip] = 0;
+        tif->tif_rawcc = 0;
+        tif->tif_rawcp = tif->tif_rawdata;
 
-			/* this forces TIFFAppendToStrip() to do a seek */
-			tif->tif_curoff = 0;
-		}
+        /* this informs TIFFAppendToStrip() we have changed strip */
+        tif->tif_curoff = 0;
 
-		if (!(*tif->tif_preencode)(tif, sample))
-			return (-1);
-		tif->tif_flags |= TIFF_POSTENCODE;
-	}
-	/*
-	 * Ensure the write is either sequential or at the
-	 * beginning of a strip (or that we can randomly
-	 * access the data -- i.e. no encoding).
-	 */
-	if (row != tif->tif_row) {
-		if (row < tif->tif_row) {
-			/*
-			 * Moving backwards within the same strip:
-			 * backup to the start and then decode
-			 * forward (below).
-			 */
-			tif->tif_row = (strip % td->td_stripsperimage) *
-			    td->td_rowsperstrip;
-			tif->tif_rawcp = tif->tif_rawdata;
-		}
-		/*
-		 * Seek forward to the desired row.
-		 */
-		if (!(*tif->tif_seek)(tif, row - tif->tif_row))
-			return (-1);
-		tif->tif_row = row;
-	}
+        if (!(*tif->tif_preencode)(tif, sample))
+            return (-1);
+        tif->tif_flags |= TIFF_POSTENCODE;
+    }
+    /*
+     * Ensure the write is either sequential or at the
+     * beginning of a strip (or that we can randomly
+     * access the data -- i.e. no encoding).
+     */
+    if (row != tif->tif_row)
+    {
+        if (row < tif->tif_row)
+        {
+            /*
+             * Moving backwards within the same strip:
+             * backup to the start and then decode
+             * forward (below).
+             */
+            tif->tif_row =
+                (strip % td->td_stripsperimage) * td->td_rowsperstrip;
+            tif->tif_rawcp = tif->tif_rawdata;
+        }
+        /*
+         * Seek forward to the desired row.
+         */
+        if (!(*tif->tif_seek)(tif, row - tif->tif_row))
+            return (-1);
+        tif->tif_row = row;
+    }
 
-	/* swab if needed - note that source buffer will be altered */
-	tif->tif_postdecode( tif, (uint8*) buf, tif->tif_scanlinesize );
+    /* swab if needed - note that source buffer will be altered */
+    tif->tif_postdecode(tif, (uint8_t *)buf, tif->tif_scanlinesize);
 
-	status = (*tif->tif_encoderow)(tif, (uint8*) buf,
-	    tif->tif_scanlinesize, sample);
+    status = (*tif->tif_encoderow)(tif, (uint8_t *)buf, tif->tif_scanlinesize,
+                                   sample);
 
-        /* we are now poised at the beginning of the next row */
-	tif->tif_row = row + 1;
-	return (status);
+    /* we are now poised at the beginning of the next row */
+    tif->tif_row = row + 1;
+    return (status);
 }
 
-/* Make sure that at the first attempt of rewriting a tile/strip, we will have */
+/* Make sure that at the first attempt of rewriting a tile/strip, we will have
+ */
 /* more bytes available in the output buffer than the previous byte count, */
-/* so that TIFFAppendToStrip() will detect the overflow when it is called the first */
+/* so that TIFFAppendToStrip() will detect the overflow when it is called the
+ * first */
 /* time if the new compressed tile is bigger than the older one. (GDAL #4771) */
-static int _TIFFReserveLargeEnoughWriteBuffer(TIFF* tif, uint32 strip_or_tile)
+static int _TIFFReserveLargeEnoughWriteBuffer(TIFF *tif, uint32_t strip_or_tile)
 {
     TIFFDirectory *td = &tif->tif_dir;
-    if( td->td_stripbytecount_p[strip_or_tile] > 0 )
+    if (td->td_stripbytecount_p[strip_or_tile] > 0)
     {
         /* The +1 is to ensure at least one extra bytes */
         /* The +4 is because the LZW encoder flushes 4 bytes before the limit */
-        uint64 safe_buffer_size = (uint64)(td->td_stripbytecount_p[strip_or_tile] + 1 + 4);
-        if( tif->tif_rawdatasize <= (tmsize_t)safe_buffer_size )
+        uint64_t safe_buffer_size =
+            (uint64_t)(td->td_stripbytecount_p[strip_or_tile] + 1 + 4);
+        if (tif->tif_rawdatasize <= (tmsize_t)safe_buffer_size)
         {
-            if( !(TIFFWriteBufferSetup(tif, NULL,
-                (tmsize_t)TIFFroundup_64(safe_buffer_size, 1024))) )
+            if (!(TIFFWriteBufferSetup(
+                    tif, NULL,
+                    (tmsize_t)TIFFroundup_64(safe_buffer_size, 1024))))
                 return 0;
         }
-
-        /* Force TIFFAppendToStrip() to consider placing data at end
-            of file. */
-        tif->tif_curoff = 0;
     }
     return 1;
 }
@@ -208,103 +212,112 @@
  *
  * NB: Image length must be setup before writing.
  */
-tmsize_t
-TIFFWriteEncodedStrip(TIFF* tif, uint32 strip, void* data, tmsize_t cc)
+tmsize_t TIFFWriteEncodedStrip(TIFF *tif, uint32_t strip, void *data,
+                               tmsize_t cc)
 {
-	static const char module[] = "TIFFWriteEncodedStrip";
-	TIFFDirectory *td = &tif->tif_dir;
-	uint16 sample;
+    static const char module[] = "TIFFWriteEncodedStrip";
+    TIFFDirectory *td = &tif->tif_dir;
+    uint16_t sample;
 
-	if (!WRITECHECKSTRIPS(tif, module))
-		return ((tmsize_t) -1);
-	/*
-	 * Check strip array to make sure there's space.
-	 * We don't support dynamically growing files that
-	 * have data organized in separate bitplanes because
-	 * it's too painful.  In that case we require that
-	 * the imagelength be set properly before the first
-	 * write (so that the strips array will be fully
-	 * allocated above).
-	 */
-	if (strip >= td->td_nstrips) {
-		if (td->td_planarconfig == PLANARCONFIG_SEPARATE) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Can not grow image by strips when using separate planes");
-			return ((tmsize_t) -1);
-		}
-		if (!TIFFGrowStrips(tif, 1, module))
-			return ((tmsize_t) -1);
-		td->td_stripsperimage =
-		    TIFFhowmany_32(td->td_imagelength, td->td_rowsperstrip);  
-	}
-	/*
-	 * Handle delayed allocation of data buffer.  This
-	 * permits it to be sized according to the directory
-	 * info.
-	 */
-	if (!BUFFERCHECK(tif))
-		return ((tmsize_t) -1);
-
-        tif->tif_flags |= TIFF_BUF4WRITE;
-	tif->tif_curstrip = strip;
-
-	if( !_TIFFReserveLargeEnoughWriteBuffer(tif, strip) ) {
-            return ((tmsize_t)(-1));
+    if (!WRITECHECKSTRIPS(tif, module))
+        return ((tmsize_t)-1);
+    /*
+     * Check strip array to make sure there's space.
+     * We don't support dynamically growing files that
+     * have data organized in separate bitplanes because
+     * it's too painful.  In that case we require that
+     * the imagelength be set properly before the first
+     * write (so that the strips array will be fully
+     * allocated above).
+     */
+    if (strip >= td->td_nstrips)
+    {
+        if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
+        {
+            TIFFErrorExtR(
+                tif, module,
+                "Can not grow image by strips when using separate planes");
+            return ((tmsize_t)-1);
         }
+        if (!TIFFGrowStrips(tif, 1, module))
+            return ((tmsize_t)-1);
+        td->td_stripsperimage =
+            TIFFhowmany_32(td->td_imagelength, td->td_rowsperstrip);
+    }
+    /*
+     * Handle delayed allocation of data buffer.  This
+     * permits it to be sized according to the directory
+     * info.
+     */
+    if (!BUFFERCHECK(tif))
+        return ((tmsize_t)-1);
 
-        tif->tif_rawcc = 0;
-        tif->tif_rawcp = tif->tif_rawdata;
+    tif->tif_flags |= TIFF_BUF4WRITE;
 
-        if (td->td_stripsperimage == 0) {
-                TIFFErrorExt(tif->tif_clientdata, module, "Zero strips per image");
-                return ((tmsize_t) -1);
-        }
+    tif->tif_curstrip = strip;
 
-	tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip;
-	if ((tif->tif_flags & TIFF_CODERSETUP) == 0) {
-		if (!(*tif->tif_setupencode)(tif))
-			return ((tmsize_t) -1);
-		tif->tif_flags |= TIFF_CODERSETUP;
-	}
+    /* this informs TIFFAppendToStrip() we have changed or reset strip */
+    tif->tif_curoff = 0;
 
-	tif->tif_flags &= ~TIFF_POSTENCODE;
+    if (!_TIFFReserveLargeEnoughWriteBuffer(tif, strip))
+    {
+        return ((tmsize_t)(-1));
+    }
+
+    tif->tif_rawcc = 0;
+    tif->tif_rawcp = tif->tif_rawdata;
+
+    if (td->td_stripsperimage == 0)
+    {
+        TIFFErrorExtR(tif, module, "Zero strips per image");
+        return ((tmsize_t)-1);
+    }
+
+    tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip;
+    if ((tif->tif_flags & TIFF_CODERSETUP) == 0)
+    {
+        if (!(*tif->tif_setupencode)(tif))
+            return ((tmsize_t)-1);
+        tif->tif_flags |= TIFF_CODERSETUP;
+    }
+
+    tif->tif_flags &= ~TIFF_POSTENCODE;
 
     /* shortcut to avoid an extra memcpy() */
-    if( td->td_compression == COMPRESSION_NONE )
+    if (td->td_compression == COMPRESSION_NONE)
     {
         /* swab if needed - note that source buffer will be altered */
-        tif->tif_postdecode( tif, (uint8*) data, cc );
+        tif->tif_postdecode(tif, (uint8_t *)data, cc);
 
         if (!isFillOrder(tif, td->td_fillorder) &&
             (tif->tif_flags & TIFF_NOBITREV) == 0)
-            TIFFReverseBits((uint8*) data, cc);
+            TIFFReverseBits((uint8_t *)data, cc);
 
-        if (cc > 0 &&
-            !TIFFAppendToStrip(tif, strip, (uint8*) data, cc))
-            return ((tmsize_t) -1);
+        if (cc > 0 && !TIFFAppendToStrip(tif, strip, (uint8_t *)data, cc))
+            return ((tmsize_t)-1);
         return (cc);
     }
 
-	sample = (uint16)(strip / td->td_stripsperimage);
-	if (!(*tif->tif_preencode)(tif, sample))
-		return ((tmsize_t) -1);
+    sample = (uint16_t)(strip / td->td_stripsperimage);
+    if (!(*tif->tif_preencode)(tif, sample))
+        return ((tmsize_t)-1);
 
-        /* swab if needed - note that source buffer will be altered */
-	tif->tif_postdecode( tif, (uint8*) data, cc );
+    /* swab if needed - note that source buffer will be altered */
+    tif->tif_postdecode(tif, (uint8_t *)data, cc);
 
-	if (!(*tif->tif_encodestrip)(tif, (uint8*) data, cc, sample))
-		return ((tmsize_t) -1);
-	if (!(*tif->tif_postencode)(tif))
-		return ((tmsize_t) -1);
-	if (!isFillOrder(tif, td->td_fillorder) &&
-	    (tif->tif_flags & TIFF_NOBITREV) == 0)
-		TIFFReverseBits(tif->tif_rawdata, tif->tif_rawcc);
-	if (tif->tif_rawcc > 0 &&
-	    !TIFFAppendToStrip(tif, strip, tif->tif_rawdata, tif->tif_rawcc))
-		return ((tmsize_t) -1);
-	tif->tif_rawcc = 0;
-	tif->tif_rawcp = tif->tif_rawdata;
-	return (cc);
+    if (!(*tif->tif_encodestrip)(tif, (uint8_t *)data, cc, sample))
+        return ((tmsize_t)-1);
+    if (!(*tif->tif_postencode)(tif))
+        return ((tmsize_t)-1);
+    if (!isFillOrder(tif, td->td_fillorder) &&
+        (tif->tif_flags & TIFF_NOBITREV) == 0)
+        TIFFReverseBits(tif->tif_rawdata, tif->tif_rawcc);
+    if (tif->tif_rawcc > 0 &&
+        !TIFFAppendToStrip(tif, strip, tif->tif_rawdata, tif->tif_rawcc))
+        return ((tmsize_t)-1);
+    tif->tif_rawcc = 0;
+    tif->tif_rawcp = tif->tif_rawdata;
+    return (cc);
 }
 
 /*
@@ -312,67 +325,78 @@
  *
  * NB: Image length must be setup before writing.
  */
-tmsize_t
-TIFFWriteRawStrip(TIFF* tif, uint32 strip, void* data, tmsize_t cc)
+tmsize_t TIFFWriteRawStrip(TIFF *tif, uint32_t strip, void *data, tmsize_t cc)
 {
-	static const char module[] = "TIFFWriteRawStrip";
-	TIFFDirectory *td = &tif->tif_dir;
+    static const char module[] = "TIFFWriteRawStrip";
+    TIFFDirectory *td = &tif->tif_dir;
 
-	if (!WRITECHECKSTRIPS(tif, module))
-		return ((tmsize_t) -1);
-	/*
-	 * Check strip array to make sure there's space.
-	 * We don't support dynamically growing files that
-	 * have data organized in separate bitplanes because
-	 * it's too painful.  In that case we require that
-	 * the imagelength be set properly before the first
-	 * write (so that the strips array will be fully
-	 * allocated above).
-	 */
-	if (strip >= td->td_nstrips) {
-		if (td->td_planarconfig == PLANARCONFIG_SEPARATE) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Can not grow image by strips when using separate planes");
-			return ((tmsize_t) -1);
-		}
-		/*
-		 * Watch out for a growing image.  The value of
-		 * strips/image will initially be 1 (since it
-		 * can't be deduced until the imagelength is known).
-		 */
-		if (strip >= td->td_stripsperimage)
-			td->td_stripsperimage =
-			    TIFFhowmany_32(td->td_imagelength,td->td_rowsperstrip);
-		if (!TIFFGrowStrips(tif, 1, module))
-			return ((tmsize_t) -1);
-	}
-	tif->tif_curstrip = strip;
-        if (td->td_stripsperimage == 0) {
-                TIFFErrorExt(tif->tif_clientdata, module,"Zero strips per image");
-                return ((tmsize_t) -1);
+    if (!WRITECHECKSTRIPS(tif, module))
+        return ((tmsize_t)-1);
+    /*
+     * Check strip array to make sure there's space.
+     * We don't support dynamically growing files that
+     * have data organized in separate bitplanes because
+     * it's too painful.  In that case we require that
+     * the imagelength be set properly before the first
+     * write (so that the strips array will be fully
+     * allocated above).
+     */
+    if (strip >= td->td_nstrips)
+    {
+        if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
+        {
+            TIFFErrorExtR(
+                tif, module,
+                "Can not grow image by strips when using separate planes");
+            return ((tmsize_t)-1);
         }
-	tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip;
-	return (TIFFAppendToStrip(tif, strip, (uint8*) data, cc) ?
-	    cc : (tmsize_t) -1);
+        /*
+         * Watch out for a growing image.  The value of
+         * strips/image will initially be 1 (since it
+         * can't be deduced until the imagelength is known).
+         */
+        if (strip >= td->td_stripsperimage)
+            td->td_stripsperimage =
+                TIFFhowmany_32(td->td_imagelength, td->td_rowsperstrip);
+        if (!TIFFGrowStrips(tif, 1, module))
+            return ((tmsize_t)-1);
+    }
+
+    if (tif->tif_curstrip != strip)
+    {
+        tif->tif_curstrip = strip;
+
+        /* this informs TIFFAppendToStrip() we have changed or reset strip */
+        tif->tif_curoff = 0;
+    }
+
+    if (td->td_stripsperimage == 0)
+    {
+        TIFFErrorExtR(tif, module, "Zero strips per image");
+        return ((tmsize_t)-1);
+    }
+    tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip;
+    return (TIFFAppendToStrip(tif, strip, (uint8_t *)data, cc) ? cc
+                                                               : (tmsize_t)-1);
 }
 
 /*
  * Write and compress a tile of data.  The
  * tile is selected by the (x,y,z,s) coordinates.
  */
-tmsize_t
-TIFFWriteTile(TIFF* tif, void* buf, uint32 x, uint32 y, uint32 z, uint16 s)
+tmsize_t TIFFWriteTile(TIFF *tif, void *buf, uint32_t x, uint32_t y, uint32_t z,
+                       uint16_t s)
 {
-	if (!TIFFCheckTile(tif, x, y, z, s))
-		return ((tmsize_t)(-1));
-	/*
-	 * NB: A tile size of -1 is used instead of tif_tilesize knowing
-	 *     that TIFFWriteEncodedTile will clamp this to the tile size.
-	 *     This is done because the tile size may not be defined until
-	 *     after the output buffer is setup in TIFFWriteBufferSetup.
-	 */
-	return (TIFFWriteEncodedTile(tif,
-	    TIFFComputeTile(tif, x, y, z, s), buf, (tmsize_t)(-1)));
+    if (!TIFFCheckTile(tif, x, y, z, s))
+        return ((tmsize_t)(-1));
+    /*
+     * NB: A tile size of -1 is used instead of tif_tilesize knowing
+     *     that TIFFWriteEncodedTile will clamp this to the tile size.
+     *     This is done because the tile size may not be defined until
+     *     after the output buffer is setup in TIFFWriteBufferSetup.
+     */
+    return (TIFFWriteEncodedTile(tif, TIFFComputeTile(tif, x, y, z, s), buf,
+                                 (tmsize_t)(-1)));
 }
 
 /*
@@ -387,104 +411,111 @@
  *     interface does not support automatically growing
  *     the image on each write (as TIFFWriteScanline does).
  */
-tmsize_t
-TIFFWriteEncodedTile(TIFF* tif, uint32 tile, void* data, tmsize_t cc)
+tmsize_t TIFFWriteEncodedTile(TIFF *tif, uint32_t tile, void *data, tmsize_t cc)
 {
-	static const char module[] = "TIFFWriteEncodedTile";
-	TIFFDirectory *td;
-	uint16 sample;
-        uint32 howmany32;
+    static const char module[] = "TIFFWriteEncodedTile";
+    TIFFDirectory *td;
+    uint16_t sample;
+    uint32_t howmany32;
 
-	if (!WRITECHECKTILES(tif, module))
-		return ((tmsize_t)(-1));
-	td = &tif->tif_dir;
-	if (tile >= td->td_nstrips) {
-		TIFFErrorExt(tif->tif_clientdata, module, "Tile %lu out of range, max %lu",
-		    (unsigned long) tile, (unsigned long) td->td_nstrips);
-		return ((tmsize_t)(-1));
-	}
-	/*
-	 * Handle delayed allocation of data buffer.  This
-	 * permits it to be sized more intelligently (using
-	 * directory information).
-	 */
-	if (!BUFFERCHECK(tif))
-		return ((tmsize_t)(-1));
+    if (!WRITECHECKTILES(tif, module))
+        return ((tmsize_t)(-1));
+    td = &tif->tif_dir;
+    if (tile >= td->td_nstrips)
+    {
+        TIFFErrorExtR(tif, module, "Tile %lu out of range, max %lu",
+                      (unsigned long)tile, (unsigned long)td->td_nstrips);
+        return ((tmsize_t)(-1));
+    }
+    /*
+     * Handle delayed allocation of data buffer.  This
+     * permits it to be sized more intelligently (using
+     * directory information).
+     */
+    if (!BUFFERCHECK(tif))
+        return ((tmsize_t)(-1));
 
-        tif->tif_flags |= TIFF_BUF4WRITE;
-	tif->tif_curtile = tile;
+    tif->tif_flags |= TIFF_BUF4WRITE;
 
-        if( !_TIFFReserveLargeEnoughWriteBuffer(tif, tile) ) {
+    tif->tif_curtile = tile;
+
+    /* this informs TIFFAppendToStrip() we have changed or reset tile */
+    tif->tif_curoff = 0;
+
+    if (!_TIFFReserveLargeEnoughWriteBuffer(tif, tile))
+    {
+        return ((tmsize_t)(-1));
+    }
+
+    tif->tif_rawcc = 0;
+    tif->tif_rawcp = tif->tif_rawdata;
+
+    /*
+     * Compute tiles per row & per column to compute
+     * current row and column
+     */
+    howmany32 = TIFFhowmany_32(td->td_imagelength, td->td_tilelength);
+    if (howmany32 == 0)
+    {
+        TIFFErrorExtR(tif, module, "Zero tiles");
+        return ((tmsize_t)(-1));
+    }
+    tif->tif_row = (tile % howmany32) * td->td_tilelength;
+    howmany32 = TIFFhowmany_32(td->td_imagewidth, td->td_tilewidth);
+    if (howmany32 == 0)
+    {
+        TIFFErrorExtR(tif, module, "Zero tiles");
+        return ((tmsize_t)(-1));
+    }
+    tif->tif_col = (tile % howmany32) * td->td_tilewidth;
+
+    if ((tif->tif_flags & TIFF_CODERSETUP) == 0)
+    {
+        if (!(*tif->tif_setupencode)(tif))
             return ((tmsize_t)(-1));
-        }
+        tif->tif_flags |= TIFF_CODERSETUP;
+    }
+    tif->tif_flags &= ~TIFF_POSTENCODE;
 
-	tif->tif_rawcc = 0;
-	tif->tif_rawcp = tif->tif_rawdata;
-
-	/* 
-	 * Compute tiles per row & per column to compute
-	 * current row and column
-	 */
-        howmany32=TIFFhowmany_32(td->td_imagelength, td->td_tilelength);
-        if (howmany32 == 0) {
-                 TIFFErrorExt(tif->tif_clientdata,module,"Zero tiles");
-                return ((tmsize_t)(-1));
-        }
-	tif->tif_row = (tile % howmany32) * td->td_tilelength;
-        howmany32=TIFFhowmany_32(td->td_imagewidth, td->td_tilewidth);
-        if (howmany32 == 0) {
-                 TIFFErrorExt(tif->tif_clientdata,module,"Zero tiles");
-                return ((tmsize_t)(-1));
-        }
-	tif->tif_col = (tile % howmany32) * td->td_tilewidth;
-
-	if ((tif->tif_flags & TIFF_CODERSETUP) == 0) {
-		if (!(*tif->tif_setupencode)(tif))
-			return ((tmsize_t)(-1));
-		tif->tif_flags |= TIFF_CODERSETUP;
-	}
-	tif->tif_flags &= ~TIFF_POSTENCODE;
-
-	/*
-	 * Clamp write amount to the tile size.  This is mostly
-	 * done so that callers can pass in some large number
-	 * (e.g. -1) and have the tile size used instead.
-	 */
-	if ( cc < 1 || cc > tif->tif_tilesize)
-		cc = tif->tif_tilesize;
+    /*
+     * Clamp write amount to the tile size.  This is mostly
+     * done so that callers can pass in some large number
+     * (e.g. -1) and have the tile size used instead.
+     */
+    if (cc < 1 || cc > tif->tif_tilesize)
+        cc = tif->tif_tilesize;
 
     /* shortcut to avoid an extra memcpy() */
-    if( td->td_compression == COMPRESSION_NONE )
+    if (td->td_compression == COMPRESSION_NONE)
     {
         /* swab if needed - note that source buffer will be altered */
-        tif->tif_postdecode( tif, (uint8*) data, cc );
+        tif->tif_postdecode(tif, (uint8_t *)data, cc);
 
         if (!isFillOrder(tif, td->td_fillorder) &&
             (tif->tif_flags & TIFF_NOBITREV) == 0)
-            TIFFReverseBits((uint8*) data, cc);
+            TIFFReverseBits((uint8_t *)data, cc);
 
-        if (cc > 0 &&
-            !TIFFAppendToStrip(tif, tile, (uint8*) data, cc))
-            return ((tmsize_t) -1);
+        if (cc > 0 && !TIFFAppendToStrip(tif, tile, (uint8_t *)data, cc))
+            return ((tmsize_t)-1);
         return (cc);
     }
 
-    sample = (uint16)(tile/td->td_stripsperimage);
+    sample = (uint16_t)(tile / td->td_stripsperimage);
     if (!(*tif->tif_preencode)(tif, sample))
         return ((tmsize_t)(-1));
     /* swab if needed - note that source buffer will be altered */
-    tif->tif_postdecode( tif, (uint8*) data, cc );
+    tif->tif_postdecode(tif, (uint8_t *)data, cc);
 
-    if (!(*tif->tif_encodetile)(tif, (uint8*) data, cc, sample))
-            return ((tmsize_t) -1);
+    if (!(*tif->tif_encodetile)(tif, (uint8_t *)data, cc, sample))
+        return ((tmsize_t)-1);
     if (!(*tif->tif_postencode)(tif))
-            return ((tmsize_t)(-1));
+        return ((tmsize_t)(-1));
     if (!isFillOrder(tif, td->td_fillorder) &&
         (tif->tif_flags & TIFF_NOBITREV) == 0)
-            TIFFReverseBits((uint8*)tif->tif_rawdata, tif->tif_rawcc);
-    if (tif->tif_rawcc > 0 && !TIFFAppendToStrip(tif, tile,
-        tif->tif_rawdata, tif->tif_rawcc))
-            return ((tmsize_t)(-1));
+        TIFFReverseBits((uint8_t *)tif->tif_rawdata, tif->tif_rawcc);
+    if (tif->tif_rawcc > 0 &&
+        !TIFFAppendToStrip(tif, tile, tif->tif_rawdata, tif->tif_rawcc))
+        return ((tmsize_t)(-1));
     tif->tif_rawcc = 0;
     tif->tif_rawcp = tif->tif_rawdata;
     return (cc);
@@ -499,59 +530,64 @@
  *     interface does not support automatically growing
  *     the image on each write (as TIFFWriteScanline does).
  */
-tmsize_t
-TIFFWriteRawTile(TIFF* tif, uint32 tile, void* data, tmsize_t cc)
+tmsize_t TIFFWriteRawTile(TIFF *tif, uint32_t tile, void *data, tmsize_t cc)
 {
-	static const char module[] = "TIFFWriteRawTile";
+    static const char module[] = "TIFFWriteRawTile";
 
-	if (!WRITECHECKTILES(tif, module))
-		return ((tmsize_t)(-1));
-	if (tile >= tif->tif_dir.td_nstrips) {
-		TIFFErrorExt(tif->tif_clientdata, module, "Tile %lu out of range, max %lu",
-		    (unsigned long) tile,
-		    (unsigned long) tif->tif_dir.td_nstrips);
-		return ((tmsize_t)(-1));
-	}
-	return (TIFFAppendToStrip(tif, tile, (uint8*) data, cc) ?
-	    cc : (tmsize_t)(-1));
+    if (!WRITECHECKTILES(tif, module))
+        return ((tmsize_t)(-1));
+    if (tile >= tif->tif_dir.td_nstrips)
+    {
+        TIFFErrorExtR(tif, module, "Tile %lu out of range, max %lu",
+                      (unsigned long)tile,
+                      (unsigned long)tif->tif_dir.td_nstrips);
+        return ((tmsize_t)(-1));
+    }
+    return (TIFFAppendToStrip(tif, tile, (uint8_t *)data, cc) ? cc
+                                                              : (tmsize_t)(-1));
 }
 
-#define	isUnspecified(tif, f) \
-    (TIFFFieldSet(tif,f) && (tif)->tif_dir.td_imagelength == 0)
+#define isUnspecified(tif, f)                                                  \
+    (TIFFFieldSet(tif, f) && (tif)->tif_dir.td_imagelength == 0)
 
-int
-TIFFSetupStrips(TIFF* tif)
+int TIFFSetupStrips(TIFF *tif)
 {
-	TIFFDirectory* td = &tif->tif_dir;
+    TIFFDirectory *td = &tif->tif_dir;
 
-	if (isTiled(tif))
-		td->td_stripsperimage =
-		    isUnspecified(tif, FIELD_TILEDIMENSIONS) ?
-			td->td_samplesperpixel : TIFFNumberOfTiles(tif);
-	else
-		td->td_stripsperimage =
-		    isUnspecified(tif, FIELD_ROWSPERSTRIP) ?
-			td->td_samplesperpixel : TIFFNumberOfStrips(tif);
-	td->td_nstrips = td->td_stripsperimage;
-	if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
-		td->td_stripsperimage /= td->td_samplesperpixel;
-	td->td_stripoffset_p = (uint64 *)
-            _TIFFCheckMalloc(tif, td->td_nstrips, sizeof (uint64),
-                             "for \"StripOffsets\" array");
-	td->td_stripbytecount_p = (uint64 *)
-            _TIFFCheckMalloc(tif, td->td_nstrips, sizeof (uint64),
-                             "for \"StripByteCounts\" array");
-	if (td->td_stripoffset_p == NULL || td->td_stripbytecount_p == NULL)
-		return (0);
-	/*
-	 * Place data at the end-of-file
-	 * (by setting offsets to zero).
-	 */
-	_TIFFmemset(td->td_stripoffset_p, 0, td->td_nstrips*sizeof (uint64));
-	_TIFFmemset(td->td_stripbytecount_p, 0, td->td_nstrips*sizeof (uint64));
-	TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS);
-	TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS);
-	return (1);
+    if (isTiled(tif))
+        td->td_stripsperimage = isUnspecified(tif, FIELD_TILEDIMENSIONS)
+                                    ? td->td_samplesperpixel
+                                    : TIFFNumberOfTiles(tif);
+    else
+        td->td_stripsperimage = isUnspecified(tif, FIELD_ROWSPERSTRIP)
+                                    ? td->td_samplesperpixel
+                                    : TIFFNumberOfStrips(tif);
+    td->td_nstrips = td->td_stripsperimage;
+    /* TIFFWriteDirectoryTagData has a limitation to 0x80000000U bytes */
+    if (td->td_nstrips >=
+        0x80000000U / ((tif->tif_flags & TIFF_BIGTIFF) ? 0x8U : 0x4U))
+    {
+        TIFFErrorExtR(tif, "TIFFSetupStrips",
+                      "Too large Strip/Tile Offsets/ByteCounts arrays");
+        return 0;
+    }
+    if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
+        td->td_stripsperimage /= td->td_samplesperpixel;
+    td->td_stripoffset_p = (uint64_t *)_TIFFCheckMalloc(
+        tif, td->td_nstrips, sizeof(uint64_t), "for \"StripOffsets\" array");
+    td->td_stripbytecount_p = (uint64_t *)_TIFFCheckMalloc(
+        tif, td->td_nstrips, sizeof(uint64_t), "for \"StripByteCounts\" array");
+    if (td->td_stripoffset_p == NULL || td->td_stripbytecount_p == NULL)
+        return (0);
+    /*
+     * Place data at the end-of-file
+     * (by setting offsets to zero).
+     */
+    _TIFFmemset(td->td_stripoffset_p, 0, td->td_nstrips * sizeof(uint64_t));
+    _TIFFmemset(td->td_stripbytecount_p, 0, td->td_nstrips * sizeof(uint64_t));
+    TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS);
+    TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS);
+    return (1);
 }
 #undef isUnspecified
 
@@ -561,237 +597,325 @@
  * we also "freeze" the state of the directory so
  * that important information is not changed.
  */
-int
-TIFFWriteCheck(TIFF* tif, int tiles, const char* module)
+int TIFFWriteCheck(TIFF *tif, int tiles, const char *module)
 {
-	if (tif->tif_mode == O_RDONLY) {
-		TIFFErrorExt(tif->tif_clientdata, module, "File not open for writing");
-		return (0);
-	}
-	if (tiles ^ isTiled(tif)) {
-		TIFFErrorExt(tif->tif_clientdata, module, tiles ?
-		    "Can not write tiles to a striped image" :
-		    "Can not write scanlines to a tiled image");
-		return (0);
-	}
+    if (tif->tif_mode == O_RDONLY)
+    {
+        TIFFErrorExtR(tif, module, "File not open for writing");
+        return (0);
+    }
+    if (tiles ^ isTiled(tif))
+    {
+        TIFFErrorExtR(tif, module,
+                      tiles ? "Can not write tiles to a striped image"
+                            : "Can not write scanlines to a tiled image");
+        return (0);
+    }
 
-        _TIFFFillStriles( tif );
-        
-	/*
-	 * On the first write verify all the required information
-	 * has been setup and initialize any data structures that
-	 * had to wait until directory information was set.
-	 * Note that a lot of our work is assumed to remain valid
-	 * because we disallow any of the important parameters
-	 * from changing after we start writing (i.e. once
-	 * TIFF_BEENWRITING is set, TIFFSetField will only allow
-	 * the image's length to be changed).
-	 */
-	if (!TIFFFieldSet(tif, FIELD_IMAGEDIMENSIONS)) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "Must set \"ImageWidth\" before writing data");
-		return (0);
-	}
-	if (tif->tif_dir.td_samplesperpixel == 1) {
-		/* 
-		 * Planarconfiguration is irrelevant in case of single band
-		 * images and need not be included. We will set it anyway,
-		 * because this field is used in other parts of library even
-		 * in the single band case.
-		 */
-		if (!TIFFFieldSet(tif, FIELD_PLANARCONFIG))
-                    tif->tif_dir.td_planarconfig = PLANARCONFIG_CONTIG;
-	} else {
-		if (!TIFFFieldSet(tif, FIELD_PLANARCONFIG)) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Must set \"PlanarConfiguration\" before writing data");
-			return (0);
-		}
-	}
-	if (tif->tif_dir.td_stripoffset_p == NULL && !TIFFSetupStrips(tif)) {
-		tif->tif_dir.td_nstrips = 0;
-		TIFFErrorExt(tif->tif_clientdata, module, "No space for %s arrays",
-		    isTiled(tif) ? "tile" : "strip");
-		return (0);
-	}
-	if (isTiled(tif))
-	{
-		tif->tif_tilesize = TIFFTileSize(tif);
-		if (tif->tif_tilesize == 0)
-			return (0);
-	}
-	else
-		tif->tif_tilesize = (tmsize_t)(-1);
-	tif->tif_scanlinesize = TIFFScanlineSize(tif);
-	if (tif->tif_scanlinesize == 0)
-		return (0);
-	tif->tif_flags |= TIFF_BEENWRITING;
+    _TIFFFillStriles(tif);
 
-        if( tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 &&
-            tif->tif_dir.td_stripoffset_entry.tdir_count == 0 &&
-            tif->tif_dir.td_stripoffset_entry.tdir_type == 0 &&
-            tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0 &&
-            tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 &&
-            tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 &&
-            tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 &&
-            tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0 &&
-            !(tif->tif_flags & TIFF_DIRTYDIRECT)  )
-        {
-            TIFFForceStrileArrayWriting(tif);
-        }
+    /*
+     * On the first write verify all the required information
+     * has been setup and initialize any data structures that
+     * had to wait until directory information was set.
+     * Note that a lot of our work is assumed to remain valid
+     * because we disallow any of the important parameters
+     * from changing after we start writing (i.e. once
+     * TIFF_BEENWRITING is set, TIFFSetField will only allow
+     * the image's length to be changed).
+     */
+    if (!TIFFFieldSet(tif, FIELD_IMAGEDIMENSIONS))
+    {
+        TIFFErrorExtR(tif, module,
+                      "Must set \"ImageWidth\" before writing data");
+        return (0);
+    }
+    if (tif->tif_dir.td_stripoffset_p == NULL && !TIFFSetupStrips(tif))
+    {
+        tif->tif_dir.td_nstrips = 0;
+        TIFFErrorExtR(tif, module, "No space for %s arrays",
+                      isTiled(tif) ? "tile" : "strip");
+        return (0);
+    }
+    if (isTiled(tif))
+    {
+        tif->tif_tilesize = TIFFTileSize(tif);
+        if (tif->tif_tilesize == 0)
+            return (0);
+    }
+    else
+        tif->tif_tilesize = (tmsize_t)(-1);
+    tif->tif_scanlinesize = TIFFScanlineSize(tif);
+    if (tif->tif_scanlinesize == 0)
+        return (0);
+    tif->tif_flags |= TIFF_BEENWRITING;
 
-	return (1);
+    if (tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 &&
+        tif->tif_dir.td_stripoffset_entry.tdir_count == 0 &&
+        tif->tif_dir.td_stripoffset_entry.tdir_type == 0 &&
+        tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0 &&
+        tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 &&
+        tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 &&
+        tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 &&
+        tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0 &&
+        !(tif->tif_flags & TIFF_DIRTYDIRECT))
+    {
+        TIFFForceStrileArrayWriting(tif);
+    }
+
+    return (1);
 }
 
 /*
  * Setup the raw data buffer used for encoding.
  */
-int
-TIFFWriteBufferSetup(TIFF* tif, void* bp, tmsize_t size)
+int TIFFWriteBufferSetup(TIFF *tif, void *bp, tmsize_t size)
 {
-	static const char module[] = "TIFFWriteBufferSetup";
+    static const char module[] = "TIFFWriteBufferSetup";
 
-	if (tif->tif_rawdata) {
-		if (tif->tif_flags & TIFF_MYBUFFER) {
-			_TIFFfree(tif->tif_rawdata);
-			tif->tif_flags &= ~TIFF_MYBUFFER;
-		}
-		tif->tif_rawdata = NULL;
-	}
-	if (size == (tmsize_t)(-1)) {
-		size = (isTiled(tif) ?
-		    tif->tif_tilesize : TIFFStripSize(tif));
-		/*
-		 * Make raw data buffer at least 8K
-		 */
-		if (size < 8*1024)
-			size = 8*1024;
-		bp = NULL;			/* NB: force malloc */
-	}
-	if (bp == NULL) {
-		bp = _TIFFmalloc(size);
-		if (bp == NULL) {
-			TIFFErrorExt(tif->tif_clientdata, module, "No space for output buffer");
-			return (0);
-		}
-		tif->tif_flags |= TIFF_MYBUFFER;
-	} else
-		tif->tif_flags &= ~TIFF_MYBUFFER;
-	tif->tif_rawdata = (uint8*) bp;
-	tif->tif_rawdatasize = size;
-	tif->tif_rawcc = 0;
-	tif->tif_rawcp = tif->tif_rawdata;
-	tif->tif_flags |= TIFF_BUFFERSETUP;
-	return (1);
+    if (tif->tif_rawdata)
+    {
+        if (tif->tif_flags & TIFF_MYBUFFER)
+        {
+            _TIFFfreeExt(tif, tif->tif_rawdata);
+            tif->tif_flags &= ~TIFF_MYBUFFER;
+        }
+        tif->tif_rawdata = NULL;
+    }
+    if (size == (tmsize_t)(-1))
+    {
+        size = (isTiled(tif) ? tif->tif_tilesize : TIFFStripSize(tif));
+
+        /* Adds 10% margin for cases where compression would expand a bit */
+        if (size < TIFF_TMSIZE_T_MAX - size / 10)
+            size += size / 10;
+        /*
+         * Make raw data buffer at least 8K
+         */
+        if (size < 8 * 1024)
+            size = 8 * 1024;
+        bp = NULL; /* NB: force malloc */
+    }
+    if (bp == NULL)
+    {
+        bp = _TIFFmallocExt(tif, size);
+        if (bp == NULL)
+        {
+            TIFFErrorExtR(tif, module, "No space for output buffer");
+            return (0);
+        }
+        tif->tif_flags |= TIFF_MYBUFFER;
+    }
+    else
+        tif->tif_flags &= ~TIFF_MYBUFFER;
+    tif->tif_rawdata = (uint8_t *)bp;
+    tif->tif_rawdatasize = size;
+    tif->tif_rawcc = 0;
+    tif->tif_rawcp = tif->tif_rawdata;
+    tif->tif_flags |= TIFF_BUFFERSETUP;
+    return (1);
 }
 
 /*
  * Grow the strip data structures by delta strips.
  */
-static int
-TIFFGrowStrips(TIFF* tif, uint32 delta, const char* module)
+static int TIFFGrowStrips(TIFF *tif, uint32_t delta, const char *module)
 {
-	TIFFDirectory *td = &tif->tif_dir;
-	uint64* new_stripoffset;
-	uint64* new_stripbytecount;
+    TIFFDirectory *td = &tif->tif_dir;
+    uint64_t *new_stripoffset;
+    uint64_t *new_stripbytecount;
 
-	assert(td->td_planarconfig == PLANARCONFIG_CONTIG);
-	new_stripoffset = (uint64*)_TIFFrealloc(td->td_stripoffset_p,
-		(td->td_nstrips + delta) * sizeof (uint64));
-	new_stripbytecount = (uint64*)_TIFFrealloc(td->td_stripbytecount_p,
-		(td->td_nstrips + delta) * sizeof (uint64));
-	if (new_stripoffset == NULL || new_stripbytecount == NULL) {
-		if (new_stripoffset)
-			_TIFFfree(new_stripoffset);
-		if (new_stripbytecount)
-			_TIFFfree(new_stripbytecount);
-		td->td_nstrips = 0;
-		TIFFErrorExt(tif->tif_clientdata, module, "No space to expand strip arrays");
-		return (0);
-	}
-	td->td_stripoffset_p = new_stripoffset;
-	td->td_stripbytecount_p = new_stripbytecount;
-	_TIFFmemset(td->td_stripoffset_p + td->td_nstrips,
-		    0, delta*sizeof (uint64));
-	_TIFFmemset(td->td_stripbytecount_p + td->td_nstrips,
-		    0, delta*sizeof (uint64));
-	td->td_nstrips += delta;
-        tif->tif_flags |= TIFF_DIRTYDIRECT;
+    assert(td->td_planarconfig == PLANARCONFIG_CONTIG);
+    new_stripoffset = (uint64_t *)_TIFFreallocExt(
+        tif, td->td_stripoffset_p, (td->td_nstrips + delta) * sizeof(uint64_t));
+    new_stripbytecount = (uint64_t *)_TIFFreallocExt(
+        tif, td->td_stripbytecount_p,
+        (td->td_nstrips + delta) * sizeof(uint64_t));
+    if (new_stripoffset == NULL || new_stripbytecount == NULL)
+    {
+        if (new_stripoffset)
+            _TIFFfreeExt(tif, new_stripoffset);
+        if (new_stripbytecount)
+            _TIFFfreeExt(tif, new_stripbytecount);
+        td->td_nstrips = 0;
+        TIFFErrorExtR(tif, module, "No space to expand strip arrays");
+        return (0);
+    }
+    td->td_stripoffset_p = new_stripoffset;
+    td->td_stripbytecount_p = new_stripbytecount;
+    _TIFFmemset(td->td_stripoffset_p + td->td_nstrips, 0,
+                delta * sizeof(uint64_t));
+    _TIFFmemset(td->td_stripbytecount_p + td->td_nstrips, 0,
+                delta * sizeof(uint64_t));
+    td->td_nstrips += delta;
+    tif->tif_flags |= TIFF_DIRTYDIRECT;
 
-	return (1);
+    return (1);
 }
 
 /*
  * Append the data to the specified strip.
  */
-static int
-TIFFAppendToStrip(TIFF* tif, uint32 strip, uint8* data, tmsize_t cc)
+static int TIFFAppendToStrip(TIFF *tif, uint32_t strip, uint8_t *data,
+                             tmsize_t cc)
 {
-	static const char module[] = "TIFFAppendToStrip";
-	TIFFDirectory *td = &tif->tif_dir;
-	uint64 m;
-        int64 old_byte_count = -1;
+    static const char module[] = "TIFFAppendToStrip";
+    TIFFDirectory *td = &tif->tif_dir;
+    uint64_t m;
+    int64_t old_byte_count = -1;
 
-	if (td->td_stripoffset_p[strip] == 0 || tif->tif_curoff == 0) {
-            assert(td->td_nstrips > 0);
+    if (tif->tif_curoff == 0)
+        tif->tif_lastvalidoff = 0;
 
-            if( td->td_stripbytecount_p[strip] != 0 
-                && td->td_stripoffset_p[strip] != 0 
-                && td->td_stripbytecount_p[strip] >= (uint64) cc )
-            {
-                /* 
-                 * There is already tile data on disk, and the new tile
-                 * data we have will fit in the same space.  The only 
-                 * aspect of this that is risky is that there could be
-                 * more data to append to this strip before we are done
-                 * depending on how we are getting called.
-                 */
-                if (!SeekOK(tif, td->td_stripoffset_p[strip])) {
-                    TIFFErrorExt(tif->tif_clientdata, module,
-                                 "Seek error at scanline %lu",
-                                 (unsigned long)tif->tif_row);
-                    return (0);
-                }
-            }
-            else
-            {
-                /* 
-                 * Seek to end of file, and set that as our location to 
-                 * write this strip.
-                 */
-                td->td_stripoffset_p[strip] = TIFFSeekFile(tif, 0, SEEK_END);
-                tif->tif_flags |= TIFF_DIRTYSTRIP;
-            }
+    if (td->td_stripoffset_p[strip] == 0 || tif->tif_curoff == 0)
+    {
+        assert(td->td_nstrips > 0);
 
-            tif->tif_curoff = td->td_stripoffset_p[strip];
-
+        if (td->td_stripbytecount_p[strip] != 0 &&
+            td->td_stripoffset_p[strip] != 0 &&
+            td->td_stripbytecount_p[strip] >= (uint64_t)cc)
+        {
             /*
-             * We are starting a fresh strip/tile, so set the size to zero.
+             * There is already tile data on disk, and the new tile
+             * data we have will fit in the same space.  The only
+             * aspect of this that is risky is that there could be
+             * more data to append to this strip before we are done
+             * depending on how we are getting called.
              */
-            old_byte_count = td->td_stripbytecount_p[strip];
-            td->td_stripbytecount_p[strip] = 0;
-	}
+            if (!SeekOK(tif, td->td_stripoffset_p[strip]))
+            {
+                TIFFErrorExtR(tif, module, "Seek error at scanline %lu",
+                              (unsigned long)tif->tif_row);
+                return (0);
+            }
 
-	m = tif->tif_curoff+cc;
-	if (!(tif->tif_flags&TIFF_BIGTIFF))
-		m = (uint32)m;
-	if ((m<tif->tif_curoff)||(m<(uint64)cc))
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "Maximum TIFF file size exceeded");
-		return (0);
-	}
-	if (!WriteOK(tif, data, cc)) {
-		TIFFErrorExt(tif->tif_clientdata, module, "Write error at scanline %lu",
-		    (unsigned long) tif->tif_row);
-		    return (0);
-	}
-	tif->tif_curoff = m;
-	td->td_stripbytecount_p[strip] += cc;
-
-        if( (int64) td->td_stripbytecount_p[strip] != old_byte_count )
+            tif->tif_lastvalidoff =
+                td->td_stripoffset_p[strip] + td->td_stripbytecount_p[strip];
+        }
+        else
+        {
+            /*
+             * Seek to end of file, and set that as our location to
+             * write this strip.
+             */
+            td->td_stripoffset_p[strip] = TIFFSeekFile(tif, 0, SEEK_END);
             tif->tif_flags |= TIFF_DIRTYSTRIP;
-            
-	return (1);
+        }
+
+        tif->tif_curoff = td->td_stripoffset_p[strip];
+
+        /*
+         * We are starting a fresh strip/tile, so set the size to zero.
+         */
+        old_byte_count = td->td_stripbytecount_p[strip];
+        td->td_stripbytecount_p[strip] = 0;
+    }
+
+    m = tif->tif_curoff + cc;
+    if (!(tif->tif_flags & TIFF_BIGTIFF))
+        m = (uint32_t)m;
+    if ((m < tif->tif_curoff) || (m < (uint64_t)cc))
+    {
+        TIFFErrorExtR(tif, module, "Maximum TIFF file size exceeded");
+        return (0);
+    }
+
+    if (tif->tif_lastvalidoff != 0 && m > tif->tif_lastvalidoff &&
+        td->td_stripbytecount_p[strip] > 0)
+    {
+        /* Ouch: we have detected that we are rewriting in place a strip/tile */
+        /* with several calls to TIFFAppendToStrip(). The first call was with */
+        /* a size smaller than the previous size of the strip/tile, so we */
+        /* opted to rewrite in place, but a following call causes us to go */
+        /* outsize of the strip/tile area, so we have to finally go for a */
+        /* append-at-end-of-file strategy, and start by moving what we already
+         */
+        /* wrote. */
+        tmsize_t tempSize;
+        void *temp;
+        uint64_t offsetRead;
+        uint64_t offsetWrite;
+        uint64_t toCopy = td->td_stripbytecount_p[strip];
+
+        if (toCopy < 1024 * 1024)
+            tempSize = (tmsize_t)toCopy;
+        else
+            tempSize = 1024 * 1024;
+
+        offsetRead = td->td_stripoffset_p[strip];
+        offsetWrite = TIFFSeekFile(tif, 0, SEEK_END);
+
+        m = offsetWrite + toCopy + cc;
+        if (!(tif->tif_flags & TIFF_BIGTIFF) && m != (uint32_t)m)
+        {
+            TIFFErrorExtR(tif, module, "Maximum TIFF file size exceeded");
+            return (0);
+        }
+
+        temp = _TIFFmallocExt(tif, tempSize);
+        if (temp == NULL)
+        {
+            TIFFErrorExtR(tif, module, "No space for output buffer");
+            return (0);
+        }
+
+        tif->tif_flags |= TIFF_DIRTYSTRIP;
+
+        td->td_stripoffset_p[strip] = offsetWrite;
+        td->td_stripbytecount_p[strip] = 0;
+
+        /* Move data written by previous calls to us at end of file */
+        while (toCopy > 0)
+        {
+            if (!SeekOK(tif, offsetRead))
+            {
+                TIFFErrorExtR(tif, module, "Seek error");
+                _TIFFfreeExt(tif, temp);
+                return (0);
+            }
+            if (!ReadOK(tif, temp, tempSize))
+            {
+                TIFFErrorExtR(tif, module, "Cannot read");
+                _TIFFfreeExt(tif, temp);
+                return (0);
+            }
+            if (!SeekOK(tif, offsetWrite))
+            {
+                TIFFErrorExtR(tif, module, "Seek error");
+                _TIFFfreeExt(tif, temp);
+                return (0);
+            }
+            if (!WriteOK(tif, temp, tempSize))
+            {
+                TIFFErrorExtR(tif, module, "Cannot write");
+                _TIFFfreeExt(tif, temp);
+                return (0);
+            }
+            offsetRead += tempSize;
+            offsetWrite += tempSize;
+            td->td_stripbytecount_p[strip] += tempSize;
+            toCopy -= tempSize;
+        }
+        _TIFFfreeExt(tif, temp);
+
+        /* Append the data of this call */
+        offsetWrite += cc;
+        m = offsetWrite;
+    }
+
+    if (!WriteOK(tif, data, cc))
+    {
+        TIFFErrorExtR(tif, module, "Write error at scanline %lu",
+                      (unsigned long)tif->tif_row);
+        return (0);
+    }
+    tif->tif_curoff = m;
+    td->td_stripbytecount_p[strip] += cc;
+
+    if ((int64_t)td->td_stripbytecount_p[strip] != old_byte_count)
+        tif->tif_flags |= TIFF_DIRTYSTRIP;
+
+    return (1);
 }
 
 /*
@@ -799,29 +923,28 @@
  * called by ``encodestrip routines'' w/o concern
  * for infinite recursion.
  */
-int
-TIFFFlushData1(TIFF* tif)
+int TIFFFlushData1(TIFF *tif)
 {
-	if (tif->tif_rawcc > 0 && tif->tif_flags & TIFF_BUF4WRITE ) {
-		if (!isFillOrder(tif, tif->tif_dir.td_fillorder) &&
-		    (tif->tif_flags & TIFF_NOBITREV) == 0)
-			TIFFReverseBits((uint8*)tif->tif_rawdata,
-			    tif->tif_rawcc);
-		if (!TIFFAppendToStrip(tif,
-		    isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip,
-		    tif->tif_rawdata, tif->tif_rawcc))
+    if (tif->tif_rawcc > 0 && tif->tif_flags & TIFF_BUF4WRITE)
+    {
+        if (!isFillOrder(tif, tif->tif_dir.td_fillorder) &&
+            (tif->tif_flags & TIFF_NOBITREV) == 0)
+            TIFFReverseBits((uint8_t *)tif->tif_rawdata, tif->tif_rawcc);
+        if (!TIFFAppendToStrip(
+                tif, isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip,
+                tif->tif_rawdata, tif->tif_rawcc))
         {
             /* We update those variables even in case of error since there's */
             /* code that doesn't really check the return code of this */
             /* function */
             tif->tif_rawcc = 0;
             tif->tif_rawcp = tif->tif_rawdata;
-			return (0);
+            return (0);
         }
-		tif->tif_rawcc = 0;
-		tif->tif_rawcp = tif->tif_rawdata;
-	}
-	return (1);
+        tif->tif_rawcc = 0;
+        tif->tif_rawcp = tif->tif_rawdata;
+    }
+    return (1);
 }
 
 /*
@@ -830,17 +953,8 @@
  * (very carefully), or to 0 so that the next write gets
  * appended to the end of the file.
  */
-void
-TIFFSetWriteOffset(TIFF* tif, toff_t off)
+void TIFFSetWriteOffset(TIFF *tif, toff_t off)
 {
-	tif->tif_curoff = off;
+    tif->tif_curoff = off;
+    tif->tif_lastvalidoff = 0;
 }
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tif_zip.c b/third_party/libtiff/tif_zip.c
deleted file mode 100644
index c750773..0000000
--- a/third_party/libtiff/tif_zip.c
+++ /dev/null
@@ -1,456 +0,0 @@
-/*
- * Copyright (c) 1995-1997 Sam Leffler
- * Copyright (c) 1995-1997 Silicon Graphics, Inc.
- *
- * Permission to use, copy, modify, distribute, and sell this software and 
- * its documentation for any purpose is hereby granted without fee, provided
- * that (i) the above copyright notices and this permission notice appear in
- * all copies of the software and related documentation, and (ii) the names of
- * Sam Leffler and Silicon Graphics may not be used in any advertising or
- * publicity relating to the software without the specific, prior written
- * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
- * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
- * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
- * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
- * OF THIS SOFTWARE.
- */
-
-#include "tiffiop.h"
-#ifdef ZIP_SUPPORT
-/*
- * TIFF Library.
- *
- * ZIP (aka Deflate) Compression Support
- *
- * This file is simply an interface to the zlib library written by
- * Jean-loup Gailly and Mark Adler.  You must use version 1.0 or later
- * of the library: this code assumes the 1.0 API and also depends on
- * the ability to write the zlib header multiple times (one per strip)
- * which was not possible with versions prior to 0.95.  Note also that
- * older versions of this codec avoided this bug by suppressing the header
- * entirely.  This means that files written with the old library cannot
- * be read; they should be converted to a different compression scheme
- * and then reconverted.
- *
- * The data format used by the zlib library is described in the files
- * zlib-3.1.doc, deflate-1.1.doc and gzip-4.1.doc, available in the
- * directory ftp://ftp.uu.net/pub/archiving/zip/doc.  The library was
- * last found at ftp://ftp.uu.net/pub/archiving/zip/zlib/zlib-0.99.tar.gz.
- */
-#include "tif_predict.h"
-#include "zlib.h"
-
-#include <stdio.h>
-
-/*
- * Sigh, ZLIB_VERSION is defined as a string so there's no
- * way to do a proper check here.  Instead we guess based
- * on the presence of #defines that were added between the
- * 0.95 and 1.0 distributions.
- */
-#if !defined(Z_NO_COMPRESSION) || !defined(Z_DEFLATED)
-#error "Antiquated ZLIB software; you must use version 1.0 or later"
-#endif
-
-#define SAFE_MSG(sp)   ((sp)->stream.msg == NULL ? "" : (sp)->stream.msg)
-
-/*
- * State block for each open TIFF
- * file using ZIP compression/decompression.
- */
-typedef struct {
-	TIFFPredictorState predict;
-        z_stream        stream;
-	int             zipquality;            /* compression level */
-	int             state;                 /* state flags */
-#define ZSTATE_INIT_DECODE 0x01
-#define ZSTATE_INIT_ENCODE 0x02
-
-	TIFFVGetMethod  vgetparent;            /* super-class method */
-	TIFFVSetMethod  vsetparent;            /* super-class method */
-} ZIPState;
-
-#define ZState(tif)             ((ZIPState*) (tif)->tif_data)
-#define DecoderState(tif)       ZState(tif)
-#define EncoderState(tif)       ZState(tif)
-
-static int ZIPEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s);
-static int ZIPDecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s);
-
-static int
-ZIPFixupTags(TIFF* tif)
-{
-	(void) tif;
-	return (1);
-}
-
-static int
-ZIPSetupDecode(TIFF* tif)
-{
-	static const char module[] = "ZIPSetupDecode";
-	ZIPState* sp = DecoderState(tif);
-
-	assert(sp != NULL);
-        
-        /* if we were last encoding, terminate this mode */
-	if (sp->state & ZSTATE_INIT_ENCODE) {
-	    deflateEnd(&sp->stream);
-	    sp->state = 0;
-	}
-
-	/* This function can possibly be called several times by */
-	/* PredictorSetupDecode() if this function succeeds but */
-	/* PredictorSetup() fails */
-	if ((sp->state & ZSTATE_INIT_DECODE) == 0 &&
-	    inflateInit(&sp->stream) != Z_OK) {
-		TIFFErrorExt(tif->tif_clientdata, module, "%s", SAFE_MSG(sp));
-		return (0);
-	} else {
-		sp->state |= ZSTATE_INIT_DECODE;
-		return (1);
-	}
-}
-
-/*
- * Setup state for decoding a strip.
- */
-static int
-ZIPPreDecode(TIFF* tif, uint16 s)
-{
-	ZIPState* sp = DecoderState(tif);
-
-	(void) s;
-	assert(sp != NULL);
-
-	if( (sp->state & ZSTATE_INIT_DECODE) == 0 )
-            tif->tif_setupdecode( tif );
-
-	sp->stream.next_in = tif->tif_rawdata;
-	assert(sizeof(sp->stream.avail_in)==4);  /* if this assert gets raised,
-	    we need to simplify this code to reflect a ZLib that is likely updated
-	    to deal with 8byte memory sizes, though this code will respond
-	    appropriately even before we simplify it */
-	sp->stream.avail_in = (uint64)tif->tif_rawcc < 0xFFFFFFFFU ? (uInt) tif->tif_rawcc : 0xFFFFFFFFU;
-	return (inflateReset(&sp->stream) == Z_OK);
-}
-
-static int
-ZIPDecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s)
-{
-	static const char module[] = "ZIPDecode";
-	ZIPState* sp = DecoderState(tif);
-
-	(void) s;
-	assert(sp != NULL);
-	assert(sp->state == ZSTATE_INIT_DECODE);
-
-        sp->stream.next_in = tif->tif_rawcp;
-        
-	sp->stream.next_out = op;
-	assert(sizeof(sp->stream.avail_out)==4);  /* if this assert gets raised,
-	    we need to simplify this code to reflect a ZLib that is likely updated
-	    to deal with 8byte memory sizes, though this code will respond
-	    appropriately even before we simplify it */
-	do {
-                int state;
-                uInt avail_in_before = (uint64)tif->tif_rawcc <= 0xFFFFFFFFU ? (uInt)tif->tif_rawcc : 0xFFFFFFFFU;
-                uInt avail_out_before = (uint64)occ < 0xFFFFFFFFU ? (uInt) occ : 0xFFFFFFFFU;
-                sp->stream.avail_in = avail_in_before;
-                sp->stream.avail_out = avail_out_before;
-		state = inflate(&sp->stream, Z_PARTIAL_FLUSH);
-		tif->tif_rawcc -= (avail_in_before - sp->stream.avail_in);
-                occ -= (avail_out_before - sp->stream.avail_out);
-		if (state == Z_STREAM_END)
-			break;
-		if (state == Z_DATA_ERROR) {
-			TIFFErrorExt(tif->tif_clientdata, module,
-			    "Decoding error at scanline %lu, %s",
-			     (unsigned long) tif->tif_row, SAFE_MSG(sp));
-			return (0);
-		}
-		if (state != Z_OK) {
-			TIFFErrorExt(tif->tif_clientdata, module, 
-				     "ZLib error: %s", SAFE_MSG(sp));
-			return (0);
-		}
-	} while (occ > 0);
-	if (occ != 0) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-		    "Not enough data at scanline %lu (short " TIFF_UINT64_FORMAT " bytes)",
-		    (unsigned long) tif->tif_row, (TIFF_UINT64_T) occ);
-		return (0);
-	}
-
-        tif->tif_rawcp = sp->stream.next_in;
-
-	return (1);
-}
-
-static int
-ZIPSetupEncode(TIFF* tif)
-{
-	static const char module[] = "ZIPSetupEncode";
-	ZIPState* sp = EncoderState(tif);
-
-	assert(sp != NULL);
-	if (sp->state & ZSTATE_INIT_DECODE) {
-		inflateEnd(&sp->stream);
-		sp->state = 0;
-	}
-
-	if (deflateInit(&sp->stream, sp->zipquality) != Z_OK) {
-		TIFFErrorExt(tif->tif_clientdata, module, "%s", SAFE_MSG(sp));
-		return (0);
-	} else {
-		sp->state |= ZSTATE_INIT_ENCODE;
-		return (1);
-	}
-}
-
-/*
- * Reset encoding state at the start of a strip.
- */
-static int
-ZIPPreEncode(TIFF* tif, uint16 s)
-{
-	ZIPState *sp = EncoderState(tif);
-
-	(void) s;
-	assert(sp != NULL);
-	if( sp->state != ZSTATE_INIT_ENCODE )
-            tif->tif_setupencode( tif );
-
-	sp->stream.next_out = tif->tif_rawdata;
-	assert(sizeof(sp->stream.avail_out)==4);  /* if this assert gets raised,
-	    we need to simplify this code to reflect a ZLib that is likely updated
-	    to deal with 8byte memory sizes, though this code will respond
-	    appropriately even before we simplify it */
-	sp->stream.avail_out = (uint64)tif->tif_rawdatasize <= 0xFFFFFFFFU ? (uInt)tif->tif_rawdatasize : 0xFFFFFFFFU;
-	return (deflateReset(&sp->stream) == Z_OK);
-}
-
-/*
- * Encode a chunk of pixels.
- */
-static int
-ZIPEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s)
-{
-	static const char module[] = "ZIPEncode";
-	ZIPState *sp = EncoderState(tif);
-
-	assert(sp != NULL);
-	assert(sp->state == ZSTATE_INIT_ENCODE);
-
-	(void) s;
-	sp->stream.next_in = bp;
-	assert(sizeof(sp->stream.avail_in)==4);  /* if this assert gets raised,
-	    we need to simplify this code to reflect a ZLib that is likely updated
-	    to deal with 8byte memory sizes, though this code will respond
-	    appropriately even before we simplify it */
-	do {
-                uInt avail_in_before = (uint64)cc <= 0xFFFFFFFFU ? (uInt)cc : 0xFFFFFFFFU;
-                sp->stream.avail_in = avail_in_before;
-		if (deflate(&sp->stream, Z_NO_FLUSH) != Z_OK) {
-			TIFFErrorExt(tif->tif_clientdata, module, 
-				     "Encoder error: %s",
-				     SAFE_MSG(sp));
-			return (0);
-		}
-		if (sp->stream.avail_out == 0) {
-			tif->tif_rawcc = tif->tif_rawdatasize;
-			TIFFFlushData1(tif);
-			sp->stream.next_out = tif->tif_rawdata;
-			sp->stream.avail_out = (uint64)tif->tif_rawdatasize <= 0xFFFFFFFFU ? (uInt)tif->tif_rawdatasize : 0xFFFFFFFFU;
-		}
-		cc -= (avail_in_before - sp->stream.avail_in);
-	} while (cc > 0);
-	return (1);
-}
-
-/*
- * Finish off an encoded strip by flushing the last
- * string and tacking on an End Of Information code.
- */
-static int
-ZIPPostEncode(TIFF* tif)
-{
-	static const char module[] = "ZIPPostEncode";
-	ZIPState *sp = EncoderState(tif);
-	int state;
-
-	sp->stream.avail_in = 0;
-	do {
-		state = deflate(&sp->stream, Z_FINISH);
-		switch (state) {
-		case Z_STREAM_END:
-		case Z_OK:
-			if ((tmsize_t)sp->stream.avail_out != tif->tif_rawdatasize)
-			{
-				tif->tif_rawcc =  tif->tif_rawdatasize - sp->stream.avail_out;
-				TIFFFlushData1(tif);
-				sp->stream.next_out = tif->tif_rawdata;
-				sp->stream.avail_out = (uint64)tif->tif_rawdatasize <= 0xFFFFFFFFU ? (uInt)tif->tif_rawdatasize : 0xFFFFFFFFU;
-			}
-			break;
-		default:
-			TIFFErrorExt(tif->tif_clientdata, module, 
-				     "ZLib error: %s", SAFE_MSG(sp));
-			return (0);
-		}
-	} while (state != Z_STREAM_END);
-	return (1);
-}
-
-static void
-ZIPCleanup(TIFF* tif)
-{
-	ZIPState* sp = ZState(tif);
-
-	assert(sp != 0);
-
-	(void)TIFFPredictorCleanup(tif);
-
-	tif->tif_tagmethods.vgetfield = sp->vgetparent;
-	tif->tif_tagmethods.vsetfield = sp->vsetparent;
-
-	if (sp->state & ZSTATE_INIT_ENCODE) {
-		deflateEnd(&sp->stream);
-		sp->state = 0;
-	} else if( sp->state & ZSTATE_INIT_DECODE) {
-		inflateEnd(&sp->stream);
-		sp->state = 0;
-	}
-	_TIFFfree(sp);
-	tif->tif_data = NULL;
-
-	_TIFFSetDefaultCompressionState(tif);
-}
-
-static int
-ZIPVSetField(TIFF* tif, uint32 tag, va_list ap)
-{
-	static const char module[] = "ZIPVSetField";
-	ZIPState* sp = ZState(tif);
-
-	switch (tag) {
-	case TIFFTAG_ZIPQUALITY:
-		sp->zipquality = (int) va_arg(ap, int);
-		if ( sp->state&ZSTATE_INIT_ENCODE ) {
-			if (deflateParams(&sp->stream,
-			    sp->zipquality, Z_DEFAULT_STRATEGY) != Z_OK) {
-				TIFFErrorExt(tif->tif_clientdata, module, "ZLib error: %s",
-					     SAFE_MSG(sp));
-				return (0);
-			}
-		}
-		return (1);
-	default:
-		return (*sp->vsetparent)(tif, tag, ap);
-	}
-	/*NOTREACHED*/
-}
-
-static int
-ZIPVGetField(TIFF* tif, uint32 tag, va_list ap)
-{
-	ZIPState* sp = ZState(tif);
-
-	switch (tag) {
-	case TIFFTAG_ZIPQUALITY:
-		*va_arg(ap, int*) = sp->zipquality;
-		break;
-	default:
-		return (*sp->vgetparent)(tif, tag, ap);
-	}
-	return (1);
-}
-
-static const TIFFField zipFields[] = {
-    { TIFFTAG_ZIPQUALITY, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "", NULL },
-};
-
-int
-TIFFInitZIP(TIFF* tif, int scheme)
-{
-	static const char module[] = "TIFFInitZIP";
-	ZIPState* sp;
-
-	assert( (scheme == COMPRESSION_DEFLATE)
-		|| (scheme == COMPRESSION_ADOBE_DEFLATE));
-
-	/*
-	 * Merge codec-specific tag information.
-	 */
-	if (!_TIFFMergeFields(tif, zipFields, TIFFArrayCount(zipFields))) {
-		TIFFErrorExt(tif->tif_clientdata, module,
-			     "Merging Deflate codec-specific tags failed");
-		return 0;
-	}
-
-	/*
-	 * Allocate state block so tag methods have storage to record values.
-	 */
-	tif->tif_data = (uint8*) _TIFFmalloc(sizeof (ZIPState));
-	if (tif->tif_data == NULL)
-		goto bad;
-	sp = ZState(tif);
-	sp->stream.zalloc = NULL;
-	sp->stream.zfree = NULL;
-	sp->stream.opaque = NULL;
-	sp->stream.data_type = Z_BINARY;
-
-	/*
-	 * Override parent get/set field methods.
-	 */
-	sp->vgetparent = tif->tif_tagmethods.vgetfield;
-	tif->tif_tagmethods.vgetfield = ZIPVGetField; /* hook for codec tags */
-	sp->vsetparent = tif->tif_tagmethods.vsetfield;
-	tif->tif_tagmethods.vsetfield = ZIPVSetField; /* hook for codec tags */
-
-	/* Default values for codec-specific fields */
-	sp->zipquality = Z_DEFAULT_COMPRESSION;	/* default comp. level */
-	sp->state = 0;
-
-	/*
-	 * Install codec methods.
-	 */
-	tif->tif_fixuptags = ZIPFixupTags; 
-	tif->tif_setupdecode = ZIPSetupDecode;
-	tif->tif_predecode = ZIPPreDecode;
-	tif->tif_decoderow = ZIPDecode;
-	tif->tif_decodestrip = ZIPDecode;
-	tif->tif_decodetile = ZIPDecode;  
-	tif->tif_setupencode = ZIPSetupEncode;
-	tif->tif_preencode = ZIPPreEncode;
-	tif->tif_postencode = ZIPPostEncode;
-	tif->tif_encoderow = ZIPEncode;
-	tif->tif_encodestrip = ZIPEncode;
-	tif->tif_encodetile = ZIPEncode;
-	tif->tif_cleanup = ZIPCleanup;
-	/*
-	 * Setup predictor setup.
-	 */
-	(void) TIFFPredictorInit(tif);
-	return (1);
-bad:
-	TIFFErrorExt(tif->tif_clientdata, module,
-		     "No space for ZIP state block");
-	return (0);
-}
-#endif /* ZIP_SUPPORT */
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tiff.h b/third_party/libtiff/tiff.h
index 5b0a0c9..b2d1186 100644
--- a/third_party/libtiff/tiff.h
+++ b/third_party/libtiff/tiff.h
@@ -2,28 +2,28 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
 #ifndef _TIFF_
-#define	_TIFF_
+#define _TIFF_
 
 #include "tiffconf.h"
 
@@ -48,32 +48,46 @@
 #define TIFF_VERSION_CLASSIC 42
 #define TIFF_VERSION_BIG 43
 
-#define TIFF_BIGENDIAN      0x4d4d
-#define TIFF_LITTLEENDIAN   0x4949
-#define MDI_LITTLEENDIAN    0x5045
-#define MDI_BIGENDIAN       0x4550
+#define TIFF_BIGENDIAN 0x4d4d
+#define TIFF_LITTLEENDIAN 0x4949
+#define MDI_LITTLEENDIAN 0x5045
+#define MDI_BIGENDIAN 0x4550
 
 /*
  * Intrinsic data types required by the file format:
  *
- * 8-bit quantities     int8/uint8
- * 16-bit quantities    int16/uint16
- * 32-bit quantities    int32/uint32
- * 64-bit quantities    int64/uint64
+ * 8-bit quantities     int8_t/uint_8_t
+ * 16-bit quantities    int16_t/uint_16_t
+ * 32-bit quantities    int32_t/uint_32_t
+ * 64-bit quantities    int64_t/uint_64_t
  * strings              unsigned char*
  */
+#ifdef __GNUC__
+#define TIFF_GCC_DEPRECATED __attribute__((deprecated))
+#else
+#define TIFF_GCC_DEPRECATED
+#endif
+#ifdef _MSC_VER
+#define TIFF_MSC_DEPRECATED                                                    \
+    __declspec(deprecated("libtiff type deprecated; please use corresponding " \
+                          "C99 stdint.h type"))
+#else
+#define TIFF_MSC_DEPRECATED
+#endif
 
-typedef TIFF_INT8_T   int8;
-typedef TIFF_UINT8_T  uint8;
+#ifndef TIFF_DISABLE_DEPRECATED
+typedef TIFF_MSC_DEPRECATED int8_t int8 TIFF_GCC_DEPRECATED;
+typedef TIFF_MSC_DEPRECATED uint8_t uint8 TIFF_GCC_DEPRECATED;
 
-typedef TIFF_INT16_T  int16;
-typedef TIFF_UINT16_T uint16;
+typedef TIFF_MSC_DEPRECATED int16_t int16 TIFF_GCC_DEPRECATED;
+typedef TIFF_MSC_DEPRECATED uint16_t uint16 TIFF_GCC_DEPRECATED;
 
-typedef TIFF_INT32_T  int32;
-typedef TIFF_UINT32_T uint32;
+typedef TIFF_MSC_DEPRECATED int32_t int32 TIFF_GCC_DEPRECATED;
+typedef TIFF_MSC_DEPRECATED uint32_t uint32 TIFF_GCC_DEPRECATED;
 
-typedef TIFF_INT64_T  int64;
-typedef TIFF_UINT64_T uint64;
+typedef TIFF_MSC_DEPRECATED int64_t int64 TIFF_GCC_DEPRECATED;
+typedef TIFF_MSC_DEPRECATED uint64_t uint64 TIFF_GCC_DEPRECATED;
+#endif /* TIFF_DISABLE_DEPRECATED */
 
 /*
  * Some types as promoted in a variable argument list
@@ -88,24 +102,26 @@
 /*
  * TIFF header.
  */
-typedef struct {
-	uint16 tiff_magic;      /* magic number (defines byte order) */
-	uint16 tiff_version;    /* TIFF version number */
+typedef struct
+{
+    uint16_t tiff_magic;   /* magic number (defines byte order) */
+    uint16_t tiff_version; /* TIFF version number */
 } TIFFHeaderCommon;
-typedef struct {
-	uint16 tiff_magic;      /* magic number (defines byte order) */
-	uint16 tiff_version;    /* TIFF version number */
-	uint32 tiff_diroff;     /* byte offset to first directory */
+typedef struct
+{
+    uint16_t tiff_magic;   /* magic number (defines byte order) */
+    uint16_t tiff_version; /* TIFF version number */
+    uint32_t tiff_diroff;  /* byte offset to first directory */
 } TIFFHeaderClassic;
-typedef struct {
-	uint16 tiff_magic;      /* magic number (defines byte order) */
-	uint16 tiff_version;    /* TIFF version number */
-	uint16 tiff_offsetsize; /* size of offsets, should be 8 */
-	uint16 tiff_unused;     /* unused word, should be 0 */
-	uint64 tiff_diroff;     /* byte offset to first directory */
+typedef struct
+{
+    uint16_t tiff_magic;      /* magic number (defines byte order) */
+    uint16_t tiff_version;    /* TIFF version number */
+    uint16_t tiff_offsetsize; /* size of offsets, should be 8 */
+    uint16_t tiff_unused;     /* unused word, should be 0 */
+    uint64_t tiff_diroff;     /* byte offset to first directory */
 } TIFFHeaderBig;
 
-
 /*
  * NB: In the comments below,
  *  - items marked with a + are obsoleted by revision 5.0,
@@ -119,424 +135,552 @@
  * Tag data type information.
  *
  * Note: RATIONALs are the ratio of two 32-bit integer values.
+ *--:
+ * Note2: TIFF_IFD8 data type is used in tiffFields[]-tag definition in order to
+ distinguish the write-handling of those tags between ClassicTIFF and BigTiff:
+                  For ClassicTIFF libtiff writes a 32-bit value and the TIFF_IFD
+ type-id into the file For BigTIFF     libtiff writes a 64-bit value and the
+ TIFF_IFD8 type-id into the file
  */
-typedef enum {
-	TIFF_NOTYPE = 0,      /* placeholder */
-	TIFF_BYTE = 1,        /* 8-bit unsigned integer */
-	TIFF_ASCII = 2,       /* 8-bit bytes w/ last byte null */
-	TIFF_SHORT = 3,       /* 16-bit unsigned integer */
-	TIFF_LONG = 4,        /* 32-bit unsigned integer */
-	TIFF_RATIONAL = 5,    /* 64-bit unsigned fraction */
-	TIFF_SBYTE = 6,       /* !8-bit signed integer */
-	TIFF_UNDEFINED = 7,   /* !8-bit untyped data */
-	TIFF_SSHORT = 8,      /* !16-bit signed integer */
-	TIFF_SLONG = 9,       /* !32-bit signed integer */
-	TIFF_SRATIONAL = 10,  /* !64-bit signed fraction */
-	TIFF_FLOAT = 11,      /* !32-bit IEEE floating point */
-	TIFF_DOUBLE = 12,     /* !64-bit IEEE floating point */
-	TIFF_IFD = 13,        /* %32-bit unsigned integer (offset) */
-	TIFF_LONG8 = 16,      /* BigTIFF 64-bit unsigned integer */
-	TIFF_SLONG8 = 17,     /* BigTIFF 64-bit signed integer */
-	TIFF_IFD8 = 18        /* BigTIFF 64-bit unsigned integer (offset) */
+typedef enum
+{
+    TIFF_NOTYPE = 0,     /* placeholder */
+    TIFF_BYTE = 1,       /* 8-bit unsigned integer */
+    TIFF_ASCII = 2,      /* 8-bit bytes w/ last byte null */
+    TIFF_SHORT = 3,      /* 16-bit unsigned integer */
+    TIFF_LONG = 4,       /* 32-bit unsigned integer */
+    TIFF_RATIONAL = 5,   /* 64-bit unsigned fraction */
+    TIFF_SBYTE = 6,      /* !8-bit signed integer */
+    TIFF_UNDEFINED = 7,  /* !8-bit untyped data */
+    TIFF_SSHORT = 8,     /* !16-bit signed integer */
+    TIFF_SLONG = 9,      /* !32-bit signed integer */
+    TIFF_SRATIONAL = 10, /* !64-bit signed fraction */
+    TIFF_FLOAT = 11,     /* !32-bit IEEE floating point */
+    TIFF_DOUBLE = 12,    /* !64-bit IEEE floating point */
+    TIFF_IFD = 13,       /* %32-bit unsigned integer (offset) */
+    TIFF_LONG8 = 16,     /* BigTIFF 64-bit unsigned integer */
+    TIFF_SLONG8 = 17,    /* BigTIFF 64-bit signed integer */
+    TIFF_IFD8 = 18       /* BigTIFF 64-bit unsigned integer (offset) */
 } TIFFDataType;
 
 /*
  * 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 */
-#define	    COMPRESSION_NEXT		32766	/* NeXT 2-bit RLE */
-#define	    COMPRESSION_CCITTRLEW	32771	/* #1 w/ word alignment */
-#define	    COMPRESSION_PACKBITS	32773	/* Macintosh RLE */
-#define	    COMPRESSION_THUNDERSCAN	32809	/* ThunderScan RLE */
+/* 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 */
+#define COMPRESSION_THUNDERSCAN 32809 /* ThunderScan RLE */
 /* codes 32895-32898 are reserved for ANSI IT8 TIFF/IT <dkelly@apago.com) */
-#define	    COMPRESSION_IT8CTPAD	32895   /* IT8 CT w/padding */
-#define	    COMPRESSION_IT8LW		32896   /* IT8 Linework RLE */
-#define	    COMPRESSION_IT8MP		32897   /* IT8 Monochrome picture */
-#define	    COMPRESSION_IT8BL		32898   /* IT8 Binary line art */
+#define COMPRESSION_IT8CTPAD 32895 /* IT8 CT w/padding */
+#define COMPRESSION_IT8LW 32896    /* IT8 Linework RLE */
+#define COMPRESSION_IT8MP 32897    /* IT8 Monochrome picture */
+#define COMPRESSION_IT8BL 32898    /* IT8 Binary line art */
 /* compression codes 32908-32911 are reserved for Pixar */
-#define     COMPRESSION_PIXARFILM	32908   /* Pixar companded 10bit LZW */
-#define	    COMPRESSION_PIXARLOG	32909   /* Pixar companded 11bit ZIP */
-#define	    COMPRESSION_DEFLATE		32946	/* Deflate compression */
-#define     COMPRESSION_ADOBE_DEFLATE   8       /* Deflate compression,
-						   as recognized by Adobe */
+#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 */
 /* 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_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 */
 /* 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	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 */
-#define	TIFFTAG_TILEWIDTH		322	/* !tile width in pixels */
-#define	TIFFTAG_TILELENGTH		323	/* !tile height in pixels */
-#define TIFFTAG_TILEOFFSETS		324	/* !offsets to data tiles */
-#define TIFFTAG_TILEBYTECOUNTS		325	/* !byte counts for tiles */
-#define	TIFFTAG_BADFAXLINES		326	/* lines w/ wrong pixel count */
-#define	TIFFTAG_CLEANFAXDATA		327	/* regenerated line info */
-#define	    CLEANFAXDATA_CLEAN		0	/* no errors detected */
-#define	    CLEANFAXDATA_REGENERATED	1	/* receiver regenerated lines */
-#define	    CLEANFAXDATA_UNCLEAN	2	/* uncorrected errors exist */
-#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 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 */
+#define TIFFTAG_TILEWIDTH 322              /* !tile width in pixels */
+#define TIFFTAG_TILELENGTH 323             /* !tile height in pixels */
+#define TIFFTAG_TILEOFFSETS 324            /* !offsets to data tiles */
+#define TIFFTAG_TILEBYTECOUNTS 325         /* !byte counts for tiles */
+#define TIFFTAG_BADFAXLINES 326            /* lines w/ wrong pixel count */
+#define TIFFTAG_CLEANFAXDATA 327           /* regenerated line info */
+#define CLEANFAXDATA_CLEAN 0               /* no errors detected */
+#define CLEANFAXDATA_REGENERATED 1         /* receiver regenerated lines */
+#define CLEANFAXDATA_UNCLEAN 2             /* uncorrected errors exist */
+#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] */
 /* Tags 400-435 are from the TIFF/FX spec */
-#define TIFFTAG_GLOBALPARAMETERSIFD	400	/* ! */
-#define TIFFTAG_PROFILETYPE			401	/* ! */
-#define     PROFILETYPE_UNSPECIFIED	0	/* ! */
-#define     PROFILETYPE_G3_FAX		1	/* ! */
-#define TIFFTAG_FAXPROFILE			402	/* ! */
-#define     FAXPROFILE_S			1	/* !TIFF/FX FAX profile S */
-#define     FAXPROFILE_F			2	/* !TIFF/FX FAX profile F */
-#define     FAXPROFILE_J			3	/* !TIFF/FX FAX profile J */
-#define     FAXPROFILE_C			4	/* !TIFF/FX FAX profile C */
-#define     FAXPROFILE_L			5	/* !TIFF/FX FAX profile L */
-#define     FAXPROFILE_M			6	/* !TIFF/FX FAX profile LM */
-#define TIFFTAG_CODINGMETHODS		403	/* !TIFF/FX coding methods */
-#define     CODINGMETHODS_T4_1D		(1 << 1)	/* !T.4 1D */
-#define     CODINGMETHODS_T4_2D		(1 << 2)	/* !T.4 2D */
-#define     CODINGMETHODS_T6		(1 << 3)	/* !T.6 */
-#define     CODINGMETHODS_T85 		(1 << 4)	/* !T.85 JBIG */
-#define     CODINGMETHODS_T42 		(1 << 5)	/* !T.42 JPEG */
-#define     CODINGMETHODS_T43		(1 << 6)	/* !T.43 colour by layered JBIG */
-#define TIFFTAG_VERSIONYEAR			404	/* !TIFF/FX version year */
-#define TIFFTAG_MODENUMBER			405	/* !TIFF/FX mode number */
-#define TIFFTAG_DECODE				433	/* !TIFF/FX decode */
-#define TIFFTAG_IMAGEBASECOLOR		434	/* !TIFF/FX image base colour */
-#define TIFFTAG_T82OPTIONS			435	/* !TIFF/FX T.82 options */
+#define TIFFTAG_GLOBALPARAMETERSIFD 400 /* ! */
+#define TIFFTAG_PROFILETYPE 401         /* ! */
+#define PROFILETYPE_UNSPECIFIED 0       /* ! */
+#define PROFILETYPE_G3_FAX 1            /* ! */
+#define TIFFTAG_FAXPROFILE 402          /* ! */
+#define FAXPROFILE_S 1                  /* !TIFF/FX FAX profile S */
+#define FAXPROFILE_F 2                  /* !TIFF/FX FAX profile F */
+#define FAXPROFILE_J 3                  /* !TIFF/FX FAX profile J */
+#define FAXPROFILE_C 4                  /* !TIFF/FX FAX profile C */
+#define FAXPROFILE_L 5                  /* !TIFF/FX FAX profile L */
+#define FAXPROFILE_M 6                  /* !TIFF/FX FAX profile LM */
+#define TIFFTAG_CODINGMETHODS 403       /* !TIFF/FX coding methods */
+#define CODINGMETHODS_T4_1D (1 << 1)    /* !T.4 1D */
+#define CODINGMETHODS_T4_2D (1 << 2)    /* !T.4 2D */
+#define CODINGMETHODS_T6 (1 << 3)       /* !T.6 */
+#define CODINGMETHODS_T85 (1 << 4)      /* !T.85 JBIG */
+#define CODINGMETHODS_T42 (1 << 5)      /* !T.42 JPEG */
+#define CODINGMETHODS_T43 (1 << 6)      /* !T.43 colour by layered JBIG */
+#define TIFFTAG_VERSIONYEAR 404         /* !TIFF/FX version year */
+#define TIFFTAG_MODENUMBER 405          /* !TIFF/FX mode number */
+#define TIFFTAG_DECODE 433              /* !TIFF/FX decode */
+#define TIFFTAG_IMAGEBASECOLOR 434      /* !TIFF/FX image base colour */
+#define TIFFTAG_T82OPTIONS 435          /* !TIFF/FX T.82 options */
 /*
  * Tags 512-521 are obsoleted by Technical Note #2 which specifies a
  * revised JPEG-in-TIFF scheme.
  */
-#define	TIFFTAG_JPEGPROC		512	/* !JPEG processing algorithm */
-#define	    JPEGPROC_BASELINE		1	/* !baseline sequential */
-#define	    JPEGPROC_LOSSLESS		14	/* !Huffman coded lossless */
-#define	TIFFTAG_JPEGIFOFFSET		513	/* !pointer to SOI marker */
-#define	TIFFTAG_JPEGIFBYTECOUNT		514	/* !JFIF stream length */
-#define	TIFFTAG_JPEGRESTARTINTERVAL	515	/* !restart interval length */
-#define	TIFFTAG_JPEGLOSSLESSPREDICTORS	517	/* !lossless proc predictor */
-#define	TIFFTAG_JPEGPOINTTRANSFORM	518	/* !lossless point transform */
-#define	TIFFTAG_JPEGQTABLES		519	/* !Q matrix offsets */
-#define	TIFFTAG_JPEGDCTABLES		520	/* !DCT table offsets */
-#define	TIFFTAG_JPEGACTABLES		521	/* !AC coefficient offsets */
-#define	TIFFTAG_YCBCRCOEFFICIENTS	529	/* !RGB -> YCbCr transform */
-#define	TIFFTAG_YCBCRSUBSAMPLING	530	/* !YCbCr subsampling factors */
-#define	TIFFTAG_YCBCRPOSITIONING	531	/* !subsample positioning */
-#define	    YCBCRPOSITION_CENTERED	1	/* !as in PostScript Level 2 */
-#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_JPEGPROC 512               /* !JPEG processing algorithm */
+#define JPEGPROC_BASELINE 1                /* !baseline sequential */
+#define JPEGPROC_LOSSLESS 14               /* !Huffman coded lossless */
+#define TIFFTAG_JPEGIFOFFSET 513           /* !pointer to SOI marker */
+#define TIFFTAG_JPEGIFBYTECOUNT 514        /* !JFIF stream length */
+#define TIFFTAG_JPEGRESTARTINTERVAL 515    /* !restart interval length */
+#define TIFFTAG_JPEGLOSSLESSPREDICTORS 517 /* !lossless proc predictor */
+#define TIFFTAG_JPEGPOINTTRANSFORM 518     /* !lossless point transform */
+#define TIFFTAG_JPEGQTABLES 519            /* !Q matrix offsets */
+#define TIFFTAG_JPEGDCTABLES 520           /* !DCT table offsets */
+#define TIFFTAG_JPEGACTABLES 521           /* !AC coefficient offsets */
+#define TIFFTAG_YCBCRCOEFFICIENTS 529      /* !RGB -> YCbCr transform */
+#define TIFFTAG_YCBCRSUBSAMPLING 530       /* !YCbCr subsampling factors */
+#define TIFFTAG_YCBCRPOSITIONING 531       /* !subsample positioning */
+#define YCBCRPOSITION_CENTERED 1           /* !as in PostScript Level 2 */
+#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] */
+/* 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 */
-#define TIFFTAG_REGIONWARPCORNERS	32955	/* warp quadrilateral */
-#define TIFFTAG_REGIONAFFINE		32956	/* affine transformation mat */
+#define TIFFTAG_REFPTS 32953            /* image reference points */
+#define TIFFTAG_REGIONTACKPOINT 32954   /* region-xform tack point */
+#define TIFFTAG_REGIONWARPCORNERS 32955 /* warp quadrilateral */
+#define TIFFTAG_REGIONAFFINE 32956      /* affine transformation mat */
 /* tags 32995-32999 are private tags registered to SGI */
-#define	TIFFTAG_MATTEING		32995	/* $use ExtraSamples */
-#define	TIFFTAG_DATATYPE		32996	/* $use SampleFormat */
-#define	TIFFTAG_IMAGEDEPTH		32997	/* z depth of image */
-#define	TIFFTAG_TILEDEPTH		32998	/* z depth/data tile */
+#define TIFFTAG_MATTEING 32995   /* $use ExtraSamples */
+#define TIFFTAG_DATATYPE 32996   /* $use SampleFormat */
+#define TIFFTAG_IMAGEDEPTH 32997 /* z depth of image */
+#define TIFFTAG_TILEDEPTH 32998  /* z depth/data tile */
 /* tags 33300-33309 are private tags registered to Pixar */
 /*
  * TIFFTAG_PIXAR_IMAGEFULLWIDTH and TIFFTAG_PIXAR_IMAGEFULLLENGTH
- * are set when an image has been cropped out of a larger image.  
+ * are set when an image has been cropped out of a larger image.
  * They reflect the size of the original uncropped image.
  * The TIFFTAG_XPOSITION and TIFFTAG_YPOSITION can be used
  * to determine the position of the smaller image in the larger one.
  */
-#define TIFFTAG_PIXAR_IMAGEFULLWIDTH    33300   /* full image size in x */
-#define TIFFTAG_PIXAR_IMAGEFULLLENGTH   33301   /* full image size in y */
- /* Tags 33302-33306 are used to identify special image modes and data
-  * used by Pixar's texture formats.
-  */
-#define TIFFTAG_PIXAR_TEXTUREFORMAT	33302	/* texture map format */
-#define TIFFTAG_PIXAR_WRAPMODES		33303	/* s & t wrap modes */
-#define TIFFTAG_PIXAR_FOVCOT		33304	/* cotan(fov) for env. maps */
+#define TIFFTAG_PIXAR_IMAGEFULLWIDTH 33300  /* full image size in x */
+#define TIFFTAG_PIXAR_IMAGEFULLLENGTH 33301 /* full image size in y */
+/* Tags 33302-33306 are used to identify special image modes and data
+ * used by Pixar's texture formats.
+ */
+#define TIFFTAG_PIXAR_TEXTUREFORMAT 33302 /* texture map format */
+#define TIFFTAG_PIXAR_WRAPMODES 33303     /* s & t wrap modes */
+#define TIFFTAG_PIXAR_FOVCOT 33304        /* cotan(fov) for env. maps */
 #define TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN 33305
 #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_WRITERSERIALNUMBER 33405  /* device serial number */
+#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 */
+#define TIFFTAG_COPYRIGHT 33432 /* copyright string */
+/* 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    /* 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_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 */
 /* 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_IT8BITSPEREXTENDEDRUNLENGTH 34021/* # of bits in long run */
-#define TIFFTAG_IT8COLORTABLE		34022	/* LW colortable */
-#define TIFFTAG_IT8IMAGECOLORINDICATOR	34023	/* BP/BL image color switch */
-#define TIFFTAG_IT8BKGCOLORINDICATOR	34024	/* BP/BL bg color switch */
-#define TIFFTAG_IT8IMAGECOLORVALUE	34025	/* BP/BL image color value */
-#define TIFFTAG_IT8BKGCOLORVALUE	34026	/* BP/BL bg color value */
-#define TIFFTAG_IT8PIXELINTENSITYRANGE	34027	/* MP pixel intensity value */
-#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_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 */
+#define TIFFTAG_IT8BKGCOLORINDICATOR 34024        /* BP/BL bg color switch */
+#define TIFFTAG_IT8IMAGECOLORVALUE 34025          /* BP/BL image color value */
+#define TIFFTAG_IT8BKGCOLORVALUE 34026            /* BP/BL bg color value */
+#define TIFFTAG_IT8PIXELINTENSITYRANGE 34027      /* MP pixel intensity value */
+#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 */
 /* tags 34232-34236 are private tags registered to Texas Instruments */
-#define TIFFTAG_FRAMECOUNT              34232   /* Sequence Frame Count */
+#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 
+#define TIFFTAG_PHOTOSHOP 34377
 /* tags 34665, 34853 and 40965 are documented in EXIF specification */
-#define TIFFTAG_EXIFIFD			34665	/* Pointer to EXIF private directory */
+#define TIFFTAG_EXIFIFD 34665 /* Pointer to EXIF private directory */
 /* tag 34750 is a private tag registered to Adobe? */
-#define TIFFTAG_ICCPROFILE		34675	/* ICC profile data */
-#define TIFFTAG_IMAGELAYER		34732	/* !TIFF/FX image layer information */
+#define TIFFTAG_ICCPROFILE 34675 /* ICC profile data */
+#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_JBIGOPTIONS 34750 /* JBIG options */
+#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. parms */
-#define	TIFFTAG_FAXSUBADDRESS		34909	/* received SubAddr string */
-#define	TIFFTAG_FAXRECVTIME		34910	/* receive time (secs) */
-#define	TIFFTAG_FAXDCS			34911	/* encoded fax ses. params, Table 2/T.30 */
+#define TIFFTAG_FAXRECVPARAMS 34908 /* encoded Class 2 ses. params */
+#define TIFFTAG_FAXSUBADDRESS 34909 /* received SubAddr string */
+#define TIFFTAG_FAXRECVTIME 34910   /* receive time (secs) */
+#define TIFFTAG_FAXDCS 34911        /* encoded fax ses. params, Table 2/T.30 */
 /* tags 37439-37443 are registered to SGI <gregl@sgi.com> */
-#define TIFFTAG_STONITS			37439	/* Sample value to Nits */
+#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_INTEROPERABILITYIFD	40965	/* Pointer to Interoperability private directory */
+#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_CURRENTICCPROFILE	50833	/* & */
-#define TIFFTAG_CURRENTPREPROFILEMATRIX	50834	/* & */
+#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 /* & */
+
+/* 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 */
+
+/* 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 */
+
 /* tag 65535 is an undefined tag used by Eastman Kodak */
-#define TIFFTAG_DCSHUESHIFTVALUES       65535   /* hue shift correction data */
+#define TIFFTAG_DCSHUESHIFTVALUES 65535 /* hue shift correction data */
 
 /*
  * The following are ``pseudo tags'' that can be used to control
@@ -549,147 +693,206 @@
  * http://www.remotesensing.org/libtiff/bugs.html with the appropriate
  * C definitions to add.
  */
-#define	TIFFTAG_FAXMODE			65536	/* Group 3/4 format control */
-#define	    FAXMODE_CLASSIC	0x0000		/* default, include RTC */
-#define	    FAXMODE_NORTC	0x0001		/* no RTC at end of data */
-#define	    FAXMODE_NOEOL	0x0002		/* no EOL code at end of row */
-#define	    FAXMODE_BYTEALIGN	0x0004		/* byte align row */
-#define	    FAXMODE_WORDALIGN	0x0008		/* word align row */
-#define	    FAXMODE_CLASSF	FAXMODE_NORTC	/* TIFF Class F */
-#define	TIFFTAG_JPEGQUALITY		65537	/* Compression quality level */
+#define TIFFTAG_FAXMODE 65536        /* Group 3/4 format control */
+#define FAXMODE_CLASSIC 0x0000       /* default, include RTC */
+#define FAXMODE_NORTC 0x0001         /* no RTC at end of data */
+#define FAXMODE_NOEOL 0x0002         /* no EOL code at end of row */
+#define FAXMODE_BYTEALIGN 0x0004     /* byte align row */
+#define FAXMODE_WORDALIGN 0x0008     /* word align row */
+#define FAXMODE_CLASSF FAXMODE_NORTC /* TIFF Class F */
+#define TIFFTAG_JPEGQUALITY 65537    /* Compression quality level */
 /* Note: quality level is on the IJG 0-100 scale.  Default value is 75 */
-#define	TIFFTAG_JPEGCOLORMODE		65538	/* Auto RGB<=>YCbCr convert? */
-#define	    JPEGCOLORMODE_RAW	0x0000		/* no conversion (default) */
-#define	    JPEGCOLORMODE_RGB	0x0001		/* do auto conversion */
-#define	TIFFTAG_JPEGTABLESMODE		65539	/* What to put in JPEGTables */
-#define	    JPEGTABLESMODE_QUANT 0x0001		/* include quantization tbls */
-#define	    JPEGTABLESMODE_HUFF	0x0002		/* include Huffman tbls */
+#define TIFFTAG_JPEGCOLORMODE 65538  /* Auto RGB<=>YCbCr convert? */
+#define JPEGCOLORMODE_RAW 0x0000     /* no conversion (default) */
+#define JPEGCOLORMODE_RGB 0x0001     /* do auto conversion */
+#define TIFFTAG_JPEGTABLESMODE 65539 /* What to put in JPEGTables */
+#define JPEGTABLESMODE_QUANT 0x0001  /* include quantization tbls */
+#define JPEGTABLESMODE_HUFF 0x0002   /* include Huffman tbls */
 /* Note: default is JPEGTABLESMODE_QUANT | JPEGTABLESMODE_HUFF */
-#define	TIFFTAG_FAXFILLFUNC		65540	/* G3/G4 fill function */
-#define	TIFFTAG_PIXARLOGDATAFMT		65549	/* PixarLogCodec I/O data sz */
-#define	    PIXARLOGDATAFMT_8BIT	0	/* regular u_char samples */
-#define	    PIXARLOGDATAFMT_8BITABGR	1	/* ABGR-order u_chars */
-#define	    PIXARLOGDATAFMT_11BITLOG	2	/* 11-bit log-encoded (raw) */
-#define	    PIXARLOGDATAFMT_12BITPICIO	3	/* as per PICIO (1.0==2048) */
-#define	    PIXARLOGDATAFMT_16BIT	4	/* signed short samples */
-#define	    PIXARLOGDATAFMT_FLOAT	5	/* IEEE float samples */
+#define TIFFTAG_FAXFILLFUNC 65540     /* G3/G4 fill function */
+#define TIFFTAG_PIXARLOGDATAFMT 65549 /* PixarLogCodec I/O data sz */
+#define PIXARLOGDATAFMT_8BIT 0        /* regular u_char samples */
+#define PIXARLOGDATAFMT_8BITABGR 1    /* ABGR-order u_chars */
+#define PIXARLOGDATAFMT_11BITLOG 2    /* 11-bit log-encoded (raw) */
+#define PIXARLOGDATAFMT_12BITPICIO 3  /* as per PICIO (1.0==2048) */
+#define PIXARLOGDATAFMT_16BIT 4       /* signed short samples */
+#define PIXARLOGDATAFMT_FLOAT 5       /* IEEE float samples */
 /* 65550-65556 are allocated to Oceana Matrix <dev@oceana.com> */
-#define TIFFTAG_DCSIMAGERTYPE           65550   /* imager model & filter */
-#define     DCSIMAGERMODEL_M3           0       /* M3 chip (1280 x 1024) */
-#define     DCSIMAGERMODEL_M5           1       /* M5 chip (1536 x 1024) */
-#define     DCSIMAGERMODEL_M6           2       /* M6 chip (3072 x 2048) */
-#define     DCSIMAGERFILTER_IR          0       /* infrared filter */
-#define     DCSIMAGERFILTER_MONO        1       /* monochrome filter */
-#define     DCSIMAGERFILTER_CFA         2       /* color filter array */
-#define     DCSIMAGERFILTER_OTHER       3       /* other filter */
-#define TIFFTAG_DCSINTERPMODE           65551   /* interpolation mode */
-#define     DCSINTERPMODE_NORMAL        0x0     /* whole image, default */
-#define     DCSINTERPMODE_PREVIEW       0x1     /* preview of image (384x256) */
-#define TIFFTAG_DCSBALANCEARRAY         65552   /* color balance values */
-#define TIFFTAG_DCSCORRECTMATRIX        65553   /* color correction values */
-#define TIFFTAG_DCSGAMMA                65554   /* gamma value */
-#define TIFFTAG_DCSTOESHOULDERPTS       65555   /* toe & shoulder points */
-#define TIFFTAG_DCSCALIBRATIONFD        65556   /* calibration file desc */
+#define TIFFTAG_DCSIMAGERTYPE 65550     /* imager model & filter */
+#define DCSIMAGERMODEL_M3 0             /* M3 chip (1280 x 1024) */
+#define DCSIMAGERMODEL_M5 1             /* M5 chip (1536 x 1024) */
+#define DCSIMAGERMODEL_M6 2             /* M6 chip (3072 x 2048) */
+#define DCSIMAGERFILTER_IR 0            /* infrared filter */
+#define DCSIMAGERFILTER_MONO 1          /* monochrome filter */
+#define DCSIMAGERFILTER_CFA 2           /* color filter array */
+#define DCSIMAGERFILTER_OTHER 3         /* other filter */
+#define TIFFTAG_DCSINTERPMODE 65551     /* interpolation mode */
+#define DCSINTERPMODE_NORMAL 0x0        /* whole image, default */
+#define DCSINTERPMODE_PREVIEW 0x1       /* preview of image (384x256) */
+#define TIFFTAG_DCSBALANCEARRAY 65552   /* color balance values */
+#define TIFFTAG_DCSCORRECTMATRIX 65553  /* color correction values */
+#define TIFFTAG_DCSGAMMA 65554          /* gamma value */
+#define TIFFTAG_DCSTOESHOULDERPTS 65555 /* toe & shoulder points */
+#define TIFFTAG_DCSCALIBRATIONFD 65556  /* calibration file desc */
 /* Note: quality level is on the ZLIB 1-9 scale. Default value is -1 */
-#define	TIFFTAG_ZIPQUALITY		65557	/* compression quality level */
-#define	TIFFTAG_PIXARLOGQUALITY		65558	/* PixarLog uses same scale */
+#define TIFFTAG_ZIPQUALITY 65557      /* compression quality level */
+#define TIFFTAG_PIXARLOGQUALITY 65558 /* PixarLog uses same scale */
 /* 65559 is allocated to Oceana Matrix <dev@oceana.com> */
-#define TIFFTAG_DCSCLIPRECTANGLE	65559	/* area of image to acquire */
-#define TIFFTAG_SGILOGDATAFMT		65560	/* SGILog user data format */
-#define     SGILOGDATAFMT_FLOAT		0	/* IEEE float samples */
-#define     SGILOGDATAFMT_16BIT		1	/* 16-bit samples */
-#define     SGILOGDATAFMT_RAW		2	/* uninterpreted data */
-#define     SGILOGDATAFMT_8BIT		3	/* 8-bit RGB monitor values */
-#define TIFFTAG_SGILOGENCODE		65561 /* SGILog data encoding control*/
-#define     SGILOGENCODE_NODITHER	0     /* do not dither encoded values*/
-#define     SGILOGENCODE_RANDITHER	1     /* randomly dither encd values */
-#define	TIFFTAG_LZMAPRESET		65562	/* LZMA2 preset (compression level) */
-#define TIFFTAG_PERSAMPLE       65563	/* interface for per sample tags */
-#define     PERSAMPLE_MERGED        0	/* present as a single value */
-#define     PERSAMPLE_MULTI         1	/* present as multiple values */
-#define TIFFTAG_ZSTD_LEVEL      65564    /* ZSTD compression level */
-#define TIFFTAG_LERC_VERSION            65565 /* LERC version */
-#define     LERC_VERSION_2_4            4
-#define TIFFTAG_LERC_ADD_COMPRESSION    65566 /* LERC additional compression */
-#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: WARNING not registered in Adobe-maintained registry */
-#define TIFFTAG_WEBP_LOSSLESS		65569	/* WebP lossless/lossy : WARNING not registered in Adobe-maintained registry */
+#define TIFFTAG_DCSCLIPRECTANGLE 65559 /* area of image to acquire */
+#define TIFFTAG_SGILOGDATAFMT 65560    /* SGILog user data format */
+#define SGILOGDATAFMT_FLOAT 0          /* IEEE float samples */
+#define SGILOGDATAFMT_16BIT 1          /* 16-bit samples */
+#define SGILOGDATAFMT_RAW 2            /* uninterpreted data */
+#define SGILOGDATAFMT_8BIT 3           /* 8-bit RGB monitor values */
+#define TIFFTAG_SGILOGENCODE 65561     /* SGILog data encoding control*/
+#define SGILOGENCODE_NODITHER 0        /* do not dither encoded values*/
+#define SGILOGENCODE_RANDITHER 1       /* randomly dither encd values */
+#define TIFFTAG_LZMAPRESET 65562       /* LZMA2 preset (compression level) */
+#define TIFFTAG_PERSAMPLE 65563        /* interface for per sample tags */
+#define PERSAMPLE_MERGED 0             /* present as a single value */
+#define PERSAMPLE_MULTI 1              /* present as multiple values */
+#define TIFFTAG_ZSTD_LEVEL 65564       /* ZSTD compression level */
+#define TIFFTAG_LERC_VERSION 65565     /* LERC version */
+#define LERC_VERSION_2_4 4
+#define TIFFTAG_LERC_ADD_COMPRESSION 65566 /* LERC additional compression */
+#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 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_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_GAINCONTROL		41991	/* Gain control */
-#define EXIFTAG_GAINCONTROL		41991	/* Gain control */
-#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 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. */
 
 #endif /* _TIFF_ */
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tiffconf.h b/third_party/libtiff/tiffconf.h
index f57f6f7..289f175 100644
--- a/third_party/libtiff/tiffconf.h
+++ b/third_party/libtiff/tiffconf.h
@@ -7,8 +7,11 @@
 #ifndef _TIFFCONF_
 #define _TIFFCONF_
 
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdint.h>
+
 #include "build/build_config.h"
-#include "core/fxcrt/fx_system.h"
 
 //NOTE: The tiff codec requires an ANSI C compiler environment for building and 
 //    presumes an ANSI C environment for use.
@@ -26,11 +29,13 @@
 #define HAVE_IEEEFP 1
 
 /* Define to 1 if you have the <string.h> header file. */
-//#define HAVE_STRING_H 1
-//fx_system.h already include the string.h in ANSIC
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have snprintf(). */
+#define HAVE_SNPRINTF 1
 
 /* Define to 1 if you have the <search.h> header file. */
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 // search.h is always available in VS 2015 and above, and may be
 // available in earlier versions.
 #define HAVE_SEARCH_H 1
@@ -40,10 +45,7 @@
 /* According typedef int  int32_t; in the fx_system.h*/
 #define SIZEOF_INT 4
 
-/* Sunliang.Liu 20110325. We should config the correct long size for tif 
-   fax4decode optimize in tif_fax3.c  -- Linux64 decode issue. 
-   TESTDOC: Bug #23661 - z1.tif. */
-#if _FX_CPU_ == _FX_X64_ || _FX_CPU_ == _FX_IA64_
+#if defined(ARCH_CPU_64_BITS)
 /* The size of `unsigned long', as computed by sizeof. */
 #define SIZEOF_UNSIGNED_LONG 8
 #else
@@ -97,7 +99,7 @@
 
 #else           // linux/unix
 
-#if 0 //_FX_CPU_ == _FX_X64_  // linux/unix 64
+#if defined(ARCH_CPU_64_BITS)
 
 /* Signed 64-bit type formatter */
 #define TIFF_INT64_FORMAT "%ld"
@@ -108,6 +110,9 @@
 /* Signed 64-bit type */
 #define TIFF_INT64_T signed long
 
+/* Unsigned 64-bit type */
+#define TIFF_UINT64_T unsigned long
+
 #else           // linux/unix 32
 
 /* Signed 64-bit type formatter */
@@ -119,37 +124,25 @@
 /* Signed 64-bit type */
 #define TIFF_INT64_T signed long long
 
-#endif            // end _FX_CPU_
-
 /* Unsigned 64-bit type */
 #define TIFF_UINT64_T unsigned long long
 
+#endif  // define(ARCH_CPU_64_BITS)
+
 #endif
 
 
-/* Signed size type */
-#ifdef _MSC_VER
-
-#if defined(_WIN64)
-#define TIFF_SSIZE_T signed __int64
+/* Signed size type, type formatter, and size of size_t */
+#if defined(ARCH_CPU_64_BITS)
+#define TIFF_SSIZE_T int64_t
+#define TIFF_SSIZE_FORMAT PRId64
 #define TIFF_SSIZE_T_MAX INT64_MAX
+#define SIZEOF_SIZE_T 8
 #else
-#define TIFF_SSIZE_T signed int
-#define TIFF_SSIZE_T_MAX INT_MAX
-#endif
-
-#else
-
-#define TIFF_SSIZE_T signed long
-#define TIFF_SSIZE_T_MAX LONG_MAX
-
-#endif
-
-/* Signed size type formatter */
-#if defined(_WIN64)
-#define TIFF_SSIZE_FORMAT "%I64d"
-#else
-#define TIFF_SSIZE_FORMAT "%ld"
+#define TIFF_SSIZE_T int32_t
+#define TIFF_SSIZE_FORMAT PRId32
+#define TIFF_SSIZE_T_MAX INT32_MAX
+#define SIZEOF_SIZE_T 4
 #endif
 
 /* Pointer difference type */
@@ -205,7 +198,7 @@
 
 /* Support Old JPEG compresson (read contrib/ojpeg/README first! Compilation
    fails with unpatched IJG JPEG library) */
-#define  OJPEG_SUPPORT  1
+/* #undef OJPEG_SUPPORT */
 
 /* Support Macintosh PackBits algorithm */
 #define PACKBITS_SUPPORT 1
@@ -217,7 +210,7 @@
 #define THUNDER_SUPPORT 1
 
 /* Support Deflate compression */
-#define ZIP_SUPPORT 1
+/* #undef ZIP_SUPPORT */
 
 /* Support strip chopping (whether or not to convert single-strip uncompressed
    images to mutiple strips of ~8Kb to reduce memory usage) */
diff --git a/third_party/libtiff/tiffio.h b/third_party/libtiff/tiffio.h
index 198481d..d6bf0cc 100644
--- a/third_party/libtiff/tiffio.h
+++ b/third_party/libtiff/tiffio.h
@@ -23,7 +23,7 @@
  */
 
 #ifndef _TIFFIO_
-#define	_TIFFIO_
+#define _TIFFIO_
 
 /*
  * TIFF I/O Library Definitions.
@@ -60,20 +60,22 @@
  */
 /*
  * this is the machine addressing size type, only it's signed, so make it
- * int32 on 32bit machines, int64 on 64bit machines
+ * int32_t on 32bit machines, int64_t on 64bit machines
  */
 typedef TIFF_SSIZE_T tmsize_t;
-typedef uint64 toff_t;          /* file offset */
+#define TIFF_TMSIZE_T_MAX (tmsize_t)(SIZE_MAX >> 1)
+
+typedef uint64_t toff_t; /* file offset */
 /* the following are deprecated and should be replaced by their defining
    counterparts */
-typedef uint32 ttag_t;          /* directory tag */
-typedef uint16 tdir_t;          /* directory index */
-typedef uint16 tsample_t;       /* sample number */
-typedef uint32 tstrile_t;       /* strip or tile number */
-typedef tstrile_t tstrip_t;     /* strip number */
-typedef tstrile_t ttile_t;      /* tile number */
-typedef tmsize_t tsize_t;       /* i/o size in bytes */
-typedef void* tdata_t;          /* image data ref */
+typedef uint32_t ttag_t;    /* directory tag */
+typedef uint32_t tdir_t;    /* directory index */
+typedef uint16_t tsample_t; /* sample number */
+typedef uint32_t tstrile_t; /* strip or tile number */
+typedef tstrile_t tstrip_t; /* strip number */
+typedef tstrile_t ttile_t;  /* tile number */
+typedef tmsize_t tsize_t;   /* i/o size in bytes */
+typedef void *tdata_t;      /* image data ref */
 
 #if !defined(__WIN32__) && (defined(_WIN32) || defined(WIN32))
 #define __WIN32__
@@ -87,21 +89,22 @@
  */
 
 #if defined(_WINDOWS) || defined(__WIN32__) || defined(_Windows)
-#  if !defined(__CYGWIN) && !defined(AVOID_WIN32_FILEIO) && !defined(USE_WIN32_FILEIO)
-#    define AVOID_WIN32_FILEIO
-#  endif
+#if !defined(__CYGWIN) && !defined(AVOID_WIN32_FILEIO) &&                      \
+    !defined(USE_WIN32_FILEIO)
+#define AVOID_WIN32_FILEIO
+#endif
 #endif
 
 #if defined(USE_WIN32_FILEIO)
-# define VC_EXTRALEAN
-# include <windows.h>
-# ifdef __WIN32__
-DECLARE_HANDLE(thandle_t);     /* Win32 file handle */
-# else
-typedef HFILE thandle_t;       /* client data handle */
-# endif /* __WIN32__ */
+#define VC_EXTRALEAN
+#include <windows.h>
+#ifdef __WIN32__
+DECLARE_HANDLE(thandle_t); /* Win32 file handle */
 #else
-typedef void* thandle_t;       /* client data handle */
+typedef HFILE thandle_t; /* client data handle */
+#endif /* __WIN32__ */
+#else
+typedef void *thandle_t; /* client data handle */
 #endif /* USE_WIN32_FILEIO */
 
 /*
@@ -110,15 +113,15 @@
  * very large.   Bit-or these flags to enable printing
  * multiple items.
  */
-#define TIFFPRINT_NONE	       0x0    /* no extra info */
-#define TIFFPRINT_STRIPS       0x1    /* strips/tiles info */
-#define TIFFPRINT_CURVES       0x2    /* color/gray response curves */
-#define TIFFPRINT_COLORMAP     0x4    /* colormap */
-#define TIFFPRINT_JPEGQTABLES  0x100  /* JPEG Q matrices */
-#define TIFFPRINT_JPEGACTABLES 0x200  /* JPEG AC tables */
-#define TIFFPRINT_JPEGDCTABLES 0x200  /* JPEG DC tables */
+#define TIFFPRINT_NONE 0x0           /* no extra info */
+#define TIFFPRINT_STRIPS 0x1         /* strips/tiles info */
+#define TIFFPRINT_CURVES 0x2         /* color/gray response curves */
+#define TIFFPRINT_COLORMAP 0x4       /* colormap */
+#define TIFFPRINT_JPEGQTABLES 0x100  /* JPEG Q matrices */
+#define TIFFPRINT_JPEGACTABLES 0x200 /* JPEG AC tables */
+#define TIFFPRINT_JPEGDCTABLES 0x200 /* JPEG DC tables */
 
-/* 
+/*
  * Colour conversion stuff
  */
 
@@ -133,42 +136,45 @@
 
 /* Structure for holding information about a display device. */
 
-typedef unsigned char TIFFRGBValue;               /* 8-bit samples */
+typedef unsigned char TIFFRGBValue; /* 8-bit samples */
 
-typedef struct {
-	float d_mat[3][3];                        /* XYZ -> luminance matrix */
-	float d_YCR;                              /* Light o/p for reference white */
-	float d_YCG;
-	float d_YCB;
-	uint32 d_Vrwr;                            /* Pixel values for ref. white */
-	uint32 d_Vrwg;
-	uint32 d_Vrwb;
-	float d_Y0R;                              /* Residual light for black pixel */
-	float d_Y0G;
-	float d_Y0B;
-	float d_gammaR;                           /* Gamma values for the three guns */
-	float d_gammaG;
-	float d_gammaB;
+typedef struct
+{
+    float d_mat[3][3]; /* XYZ -> luminance matrix */
+    float d_YCR;       /* Light o/p for reference white */
+    float d_YCG;
+    float d_YCB;
+    uint32_t d_Vrwr; /* Pixel values for ref. white */
+    uint32_t d_Vrwg;
+    uint32_t d_Vrwb;
+    float d_Y0R; /* Residual light for black pixel */
+    float d_Y0G;
+    float d_Y0B;
+    float d_gammaR; /* Gamma values for the three guns */
+    float d_gammaG;
+    float d_gammaB;
 } TIFFDisplay;
 
-typedef struct {                                  /* YCbCr->RGB support */
-	TIFFRGBValue* clamptab;                   /* range clamping table */
-	int* Cr_r_tab;
-	int* Cb_b_tab;
-	int32* Cr_g_tab;
-	int32* Cb_g_tab;
-	int32* Y_tab;
+typedef struct
+{                           /* YCbCr->RGB support */
+    TIFFRGBValue *clamptab; /* range clamping table */
+    int *Cr_r_tab;
+    int *Cb_b_tab;
+    int32_t *Cr_g_tab;
+    int32_t *Cb_g_tab;
+    int32_t *Y_tab;
 } TIFFYCbCrToRGB;
 
-typedef struct {                                  /* CIE Lab 1976->RGB support */
-	int range;                                /* Size of conversion table */
+typedef struct
+{              /* CIE Lab 1976->RGB support */
+    int range; /* Size of conversion table */
 #define CIELABTORGB_TABLE_RANGE 1500
-	float rstep, gstep, bstep;
-	float X0, Y0, Z0;                         /* Reference white point */
-	TIFFDisplay display;
-	float Yr2r[CIELABTORGB_TABLE_RANGE + 1];  /* Conversion of Yr to r */
-	float Yg2g[CIELABTORGB_TABLE_RANGE + 1];  /* Conversion of Yg to g */
-	float Yb2b[CIELABTORGB_TABLE_RANGE + 1];  /* Conversion of Yb to b */
+    float rstep, gstep, bstep;
+    float X0, Y0, Z0; /* Reference white point */
+    TIFFDisplay display;
+    float Yr2r[CIELABTORGB_TABLE_RANGE + 1]; /* Conversion of Yr to r */
+    float Yg2g[CIELABTORGB_TABLE_RANGE + 1]; /* Conversion of Yg to g */
+    float Yb2b[CIELABTORGB_TABLE_RANGE + 1]; /* Conversion of Yb to b */
 } TIFFCIELabToRGB;
 
 /*
@@ -178,63 +184,66 @@
 /*
  * The image reading and conversion routines invoke
  * ``put routines'' to copy/image/whatever tiles of
- * raw image data.  A default set of routines are 
+ * raw image data.  A default set of routines are
  * provided to convert/copy raw image data to 8-bit
  * packed ABGR format rasters.  Applications can supply
  * alternate routines that unpack the data into a
  * different format or, for example, unpack the data
  * and draw the unpacked raster on the display.
  */
-typedef void (*tileContigRoutine)
-    (TIFFRGBAImage*, uint32*, uint32, uint32, uint32, uint32, int32, int32,
-	unsigned char*);
-typedef void (*tileSeparateRoutine)
-    (TIFFRGBAImage*, uint32*, uint32, uint32, uint32, uint32, int32, int32,
-	unsigned char*, unsigned char*, unsigned char*, unsigned char*);
+typedef void (*tileContigRoutine)(TIFFRGBAImage *, uint32_t *, uint32_t,
+                                  uint32_t, uint32_t, uint32_t, int32_t,
+                                  int32_t, unsigned char *);
+typedef void (*tileSeparateRoutine)(TIFFRGBAImage *, uint32_t *, uint32_t,
+                                    uint32_t, uint32_t, uint32_t, int32_t,
+                                    int32_t, unsigned char *, unsigned char *,
+                                    unsigned char *, unsigned char *);
 /*
  * RGBA-reader state.
  */
-struct _TIFFRGBAImage {
-	TIFF* tif;                              /* image handle */
-	int stoponerr;                          /* stop on read error */
-	int isContig;                           /* data is packed/separate */
-	int alpha;                              /* type of alpha data present */
-	uint32 width;                           /* image width */
-	uint32 height;                          /* image height */
-	uint16 bitspersample;                   /* image bits/sample */
-	uint16 samplesperpixel;                 /* image samples/pixel */
-	uint16 orientation;                     /* image orientation */
-	uint16 req_orientation;                 /* requested orientation */
-	uint16 photometric;                     /* image photometric interp */
-	uint16* redcmap;                        /* colormap palette */
-	uint16* greencmap;
-	uint16* bluecmap;
-	/* get image data routine */
-	int (*get)(TIFFRGBAImage*, uint32*, uint32, uint32);
-	/* put decoded strip/tile */
-	union {
-	    void (*any)(TIFFRGBAImage*);
-	    tileContigRoutine contig;
-	    tileSeparateRoutine separate;
-	} put;
-	TIFFRGBValue* Map;                      /* sample mapping array */
-	uint32** BWmap;                         /* black&white map */
-	uint32** PALmap;                        /* palette image map */
-	TIFFYCbCrToRGB* ycbcr;                  /* YCbCr conversion state */
-	TIFFCIELabToRGB* cielab;                /* CIE L*a*b conversion state */
+struct _TIFFRGBAImage
+{
+    TIFF *tif;                /* image handle */
+    int stoponerr;            /* stop on read error */
+    int isContig;             /* data is packed/separate */
+    int alpha;                /* type of alpha data present */
+    uint32_t width;           /* image width */
+    uint32_t height;          /* image height */
+    uint16_t bitspersample;   /* image bits/sample */
+    uint16_t samplesperpixel; /* image samples/pixel */
+    uint16_t orientation;     /* image orientation */
+    uint16_t req_orientation; /* requested orientation */
+    uint16_t photometric;     /* image photometric interp */
+    uint16_t *redcmap;        /* colormap palette */
+    uint16_t *greencmap;
+    uint16_t *bluecmap;
+    /* get image data routine */
+    int (*get)(TIFFRGBAImage *, uint32_t *, uint32_t, uint32_t);
+    /* put decoded strip/tile */
+    union
+    {
+        void (*any)(TIFFRGBAImage *);
+        tileContigRoutine contig;
+        tileSeparateRoutine separate;
+    } put;
+    TIFFRGBValue *Map;       /* sample mapping array */
+    uint32_t **BWmap;        /* black&white map */
+    uint32_t **PALmap;       /* palette image map */
+    TIFFYCbCrToRGB *ycbcr;   /* YCbCr conversion state */
+    TIFFCIELabToRGB *cielab; /* CIE L*a*b conversion state */
 
-	uint8* UaToAa;                          /* Unassociated alpha to associated alpha conversion LUT */
-	uint8* Bitdepth16To8;                   /* LUT for conversion from 16bit to 8bit values */
+    uint8_t *UaToAa; /* Unassociated alpha to associated alpha conversion LUT */
+    uint8_t *Bitdepth16To8; /* LUT for conversion from 16bit to 8bit values */
 
-	int row_offset;
-	int col_offset;
+    int row_offset;
+    int col_offset;
 };
 
 /*
  * Macros for extracting components from the
  * packed ABGR form returned by TIFFReadRGBAImage.
  */
-#define TIFFGetR(abgr) ((abgr) & 0xff)
+#define TIFFGetR(abgr) ((abgr)&0xff)
 #define TIFFGetG(abgr) (((abgr) >> 8) & 0xff)
 #define TIFFGetB(abgr) (((abgr) >> 16) & 0xff)
 #define TIFFGetA(abgr) (((abgr) >> 24) & 0xff)
@@ -246,323 +255,399 @@
  * More codecs may be registered through calls to the library
  * and/or the builtin implementations may be overridden.
  */
-typedef int (*TIFFInitMethod)(TIFF*, int);
-typedef struct {
-	char* name;
-	uint16 scheme;
-	TIFFInitMethod init;
+typedef int (*TIFFInitMethod)(TIFF *, int);
+typedef struct
+{
+    char *name;
+    uint16_t scheme;
+    TIFFInitMethod init;
 } TIFFCodec;
 
-#include <stdio.h>
+typedef struct
+{
+    uint32_t uNum;
+    uint32_t uDenom;
+} TIFFRational_t;
+
 #include <stdarg.h>
+#include <stdio.h>
 
 /* share internal LogLuv conversion routines? */
 #ifndef LOGLUV_PUBLIC
 #define LOGLUV_PUBLIC 1
 #endif
 
-#if !defined(__GNUC__) && !defined(__attribute__)
-#  define __attribute__(x) /*nothing*/
-#endif
-
-#if defined(c_plusplus) || defined(__cplusplus)
-extern "C" {
-#endif
-typedef void (*TIFFErrorHandler)(const char*, const char*, va_list);
-typedef void (*TIFFErrorHandlerExt)(thandle_t, const char*, const char*, va_list);
-typedef tmsize_t (*TIFFReadWriteProc)(thandle_t, void*, tmsize_t);
-typedef toff_t (*TIFFSeekProc)(thandle_t, toff_t, int);
-typedef int (*TIFFCloseProc)(thandle_t);
-typedef toff_t (*TIFFSizeProc)(thandle_t);
-typedef int (*TIFFMapFileProc)(thandle_t, void** base, toff_t* size);
-typedef void (*TIFFUnmapFileProc)(thandle_t, void* base, toff_t size);
-typedef void (*TIFFExtendProc)(TIFF*);
-
-extern const char* TIFFGetVersion(void);
-
-extern const TIFFCodec* TIFFFindCODEC(uint16);
-extern TIFFCodec* TIFFRegisterCODEC(uint16, const char*, TIFFInitMethod);
-extern void TIFFUnRegisterCODEC(TIFFCodec*);
-extern int TIFFIsCODECConfigured(uint16);
-extern TIFFCodec* TIFFGetConfiguredCODECs(void);
-
-/*
- * Auxiliary functions.
- */
-
-extern void* _TIFFmalloc(tmsize_t s);
-extern void* _TIFFcalloc(tmsize_t nmemb, tmsize_t siz);
-extern void* _TIFFrealloc(void* p, tmsize_t s);
-extern void _TIFFmemset(void* p, int v, tmsize_t c);
-extern void _TIFFmemcpy(void* d, const void* s, tmsize_t c);
-extern int _TIFFmemcmp(const void* p1, const void* p2, tmsize_t c);
-extern void _TIFFfree(void* p);
-
-/*
-** Stuff, related to tag handling and creating custom tags.
-*/
-extern int TIFFGetTagListCount( TIFF * );
-extern uint32 TIFFGetTagListEntry( TIFF *, int tag_index );
-    
-#define TIFF_ANY       TIFF_NOTYPE     /* for field descriptor searching */
-#define TIFF_VARIABLE  -1              /* marker for variable length tags */
-#define TIFF_SPP       -2              /* marker for SamplesPerPixel tags */
-#define TIFF_VARIABLE2 -3              /* marker for uint32 var-length tags */
-
-#define FIELD_CUSTOM    65
-
-typedef struct _TIFFField TIFFField;
-typedef struct _TIFFFieldArray TIFFFieldArray;
-
-extern const TIFFField* TIFFFindField(TIFF *, uint32, TIFFDataType);
-extern const TIFFField* TIFFFieldWithTag(TIFF*, uint32);
-extern const TIFFField* TIFFFieldWithName(TIFF*, const char *);
-
-extern uint32 TIFFFieldTag(const TIFFField*);
-extern const char* TIFFFieldName(const TIFFField*);
-extern TIFFDataType TIFFFieldDataType(const TIFFField*);
-extern int TIFFFieldPassCount(const TIFFField*);
-extern int TIFFFieldReadCount(const TIFFField*);
-extern int TIFFFieldWriteCount(const TIFFField*);
-
-typedef int (*TIFFVSetMethod)(TIFF*, uint32, va_list);
-typedef int (*TIFFVGetMethod)(TIFF*, uint32, va_list);
-typedef void (*TIFFPrintMethod)(TIFF*, FILE*, long);
-
-typedef struct {
-    TIFFVSetMethod vsetfield; /* tag set routine */
-    TIFFVGetMethod vgetfield; /* tag get routine */
-    TIFFPrintMethod printdir; /* directory print routine */
-} TIFFTagMethods;
-
-extern  TIFFTagMethods *TIFFAccessTagMethods(TIFF *);
-extern  void *TIFFGetClientInfo(TIFF *, const char *);
-extern  void TIFFSetClientInfo(TIFF *, void *, const char *);
-
-extern void TIFFCleanup(TIFF* tif);
-extern void TIFFClose(TIFF* tif);
-extern int TIFFFlush(TIFF* tif);
-extern int TIFFFlushData(TIFF* tif);
-extern int TIFFGetField(TIFF* tif, uint32 tag, ...);
-extern int TIFFVGetField(TIFF* tif, uint32 tag, va_list ap);
-extern int TIFFGetFieldDefaulted(TIFF* tif, uint32 tag, ...);
-extern int TIFFVGetFieldDefaulted(TIFF* tif, uint32 tag, va_list ap);
-extern int TIFFReadDirectory(TIFF* tif);
-extern int TIFFReadCustomDirectory(TIFF* tif, toff_t diroff, const TIFFFieldArray* infoarray);
-extern int TIFFReadEXIFDirectory(TIFF* tif, toff_t diroff);
-extern uint64 TIFFScanlineSize64(TIFF* tif);
-extern tmsize_t TIFFScanlineSize(TIFF* tif);
-extern uint64 TIFFRasterScanlineSize64(TIFF* tif);
-extern tmsize_t TIFFRasterScanlineSize(TIFF* tif);
-extern uint64 TIFFStripSize64(TIFF* tif);
-extern tmsize_t TIFFStripSize(TIFF* tif);
-extern uint64 TIFFRawStripSize64(TIFF* tif, uint32 strip);
-extern tmsize_t TIFFRawStripSize(TIFF* tif, uint32 strip);
-extern uint64 TIFFVStripSize64(TIFF* tif, uint32 nrows);
-extern tmsize_t TIFFVStripSize(TIFF* tif, uint32 nrows);
-extern uint64 TIFFTileRowSize64(TIFF* tif);
-extern tmsize_t TIFFTileRowSize(TIFF* tif);
-extern uint64 TIFFTileSize64(TIFF* tif);
-extern tmsize_t TIFFTileSize(TIFF* tif);
-extern uint64 TIFFVTileSize64(TIFF* tif, uint32 nrows);
-extern tmsize_t TIFFVTileSize(TIFF* tif, uint32 nrows);
-extern uint32 TIFFDefaultStripSize(TIFF* tif, uint32 request);
-extern void TIFFDefaultTileSize(TIFF*, uint32*, uint32*);
-extern int TIFFFileno(TIFF*);
-extern int TIFFSetFileno(TIFF*, int);
-extern thandle_t TIFFClientdata(TIFF*);
-extern thandle_t TIFFSetClientdata(TIFF*, thandle_t);
-extern int TIFFGetMode(TIFF*);
-extern int TIFFSetMode(TIFF*, int);
-extern int TIFFIsTiled(TIFF*);
-extern int TIFFIsByteSwapped(TIFF*);
-extern int TIFFIsUpSampled(TIFF*);
-extern int TIFFIsMSB2LSB(TIFF*);
-extern int TIFFIsBigEndian(TIFF*);
-extern TIFFReadWriteProc TIFFGetReadProc(TIFF*);
-extern TIFFReadWriteProc TIFFGetWriteProc(TIFF*);
-extern TIFFSeekProc TIFFGetSeekProc(TIFF*);                                                          
-extern TIFFCloseProc TIFFGetCloseProc(TIFF*);
-extern TIFFSizeProc TIFFGetSizeProc(TIFF*);
-extern TIFFMapFileProc TIFFGetMapFileProc(TIFF*);
-extern TIFFUnmapFileProc TIFFGetUnmapFileProc(TIFF*);
-extern uint32 TIFFCurrentRow(TIFF*);
-extern uint16 TIFFCurrentDirectory(TIFF*);
-extern uint16 TIFFNumberOfDirectories(TIFF*);
-extern uint64 TIFFCurrentDirOffset(TIFF*);
-extern uint32 TIFFCurrentStrip(TIFF*);
-extern uint32 TIFFCurrentTile(TIFF* tif);
-extern int TIFFReadBufferSetup(TIFF* tif, void* bp, tmsize_t size);
-extern int TIFFWriteBufferSetup(TIFF* tif, void* bp, tmsize_t size);  
-extern int TIFFSetupStrips(TIFF *);
-extern int TIFFWriteCheck(TIFF*, int, const char *);
-extern void TIFFFreeDirectory(TIFF*);
-extern int TIFFCreateDirectory(TIFF*);
-extern int TIFFCreateCustomDirectory(TIFF*,const TIFFFieldArray*);
-extern int TIFFCreateEXIFDirectory(TIFF*);
-extern int TIFFLastDirectory(TIFF*);
-extern int TIFFSetDirectory(TIFF*, uint16);
-extern int TIFFSetSubDirectory(TIFF*, uint64);
-extern int TIFFUnlinkDirectory(TIFF*, uint16);
-extern int TIFFSetField(TIFF*, uint32, ...);
-extern int TIFFVSetField(TIFF*, uint32, va_list);
-extern int TIFFUnsetField(TIFF*, uint32);
-extern int TIFFWriteDirectory(TIFF *);
-extern int TIFFWriteCustomDirectory(TIFF *, uint64 *);
-extern int TIFFCheckpointDirectory(TIFF *);
-extern int TIFFRewriteDirectory(TIFF *);
-extern int TIFFDeferStrileArrayWriting(TIFF *);
-extern int TIFFForceStrileArrayWriting(TIFF* );
-
-#if defined(c_plusplus) || defined(__cplusplus)
-extern void TIFFPrintDirectory(TIFF*, FILE*, long = 0);
-extern int TIFFReadScanline(TIFF* tif, void* buf, uint32 row, uint16 sample = 0);
-extern int TIFFWriteScanline(TIFF* tif, void* buf, uint32 row, uint16 sample = 0);
-extern int TIFFReadRGBAImage(TIFF*, uint32, uint32, uint32*, int = 0);
-extern int TIFFReadRGBAImageOriented(TIFF*, uint32, uint32, uint32*,
-    int = ORIENTATION_BOTLEFT, int = 0);
+#if defined(__GNUC__) || defined(__attribute__)
+#define TIFF_ATTRIBUTE(x) __attribute__(x)
 #else
-extern void TIFFPrintDirectory(TIFF*, FILE*, long);
-extern int TIFFReadScanline(TIFF* tif, void* buf, uint32 row, uint16 sample);
-extern int TIFFWriteScanline(TIFF* tif, void* buf, uint32 row, uint16 sample);
-extern int TIFFReadRGBAImage(TIFF*, uint32, uint32, uint32*, int);
-extern int TIFFReadRGBAImageOriented(TIFF*, uint32, uint32, uint32*, int, int);
+#define TIFF_ATTRIBUTE(x) /*nothing*/
 #endif
 
-extern int TIFFReadRGBAStrip(TIFF*, uint32, uint32 * );
-extern int TIFFReadRGBATile(TIFF*, uint32, uint32, uint32 * );
-extern int TIFFReadRGBAStripExt(TIFF*, uint32, uint32 *, int stop_on_error );
-extern int TIFFReadRGBATileExt(TIFF*, uint32, uint32, uint32 *, int stop_on_error );
-extern int TIFFRGBAImageOK(TIFF*, char [1024]);
-extern int TIFFRGBAImageBegin(TIFFRGBAImage*, TIFF*, int, char [1024]);
-extern int TIFFRGBAImageGet(TIFFRGBAImage*, uint32*, uint32, uint32);
-extern void TIFFRGBAImageEnd(TIFFRGBAImage*);
-extern TIFF* TIFFOpen(const char*, const char*);
-# ifdef __WIN32__
-extern TIFF* TIFFOpenW(const wchar_t*, const char*);
-# endif /* __WIN32__ */
-extern TIFF* TIFFFdOpen(int, const char*, const char*);
-extern TIFF* TIFFClientOpen(const char*, const char*,
-	    thandle_t,
-	    TIFFReadWriteProc, TIFFReadWriteProc,
-	    TIFFSeekProc, TIFFCloseProc,
-	    TIFFSizeProc,
-	    TIFFMapFileProc, TIFFUnmapFileProc);
-extern const char* TIFFFileName(TIFF*);
-extern const char* TIFFSetFileName(TIFF*, const char *);
-extern void TIFFError(const char*, const char*, ...) __attribute__((__format__ (__printf__,2,3)));
-extern void TIFFErrorExt(thandle_t, const char*, const char*, ...) __attribute__((__format__ (__printf__,3,4)));
-extern void TIFFWarning(const char*, const char*, ...) __attribute__((__format__ (__printf__,2,3)));
-extern void TIFFWarningExt(thandle_t, const char*, const char*, ...) __attribute__((__format__ (__printf__,3,4)));
-extern TIFFErrorHandler TIFFSetErrorHandler(TIFFErrorHandler);
-extern TIFFErrorHandlerExt TIFFSetErrorHandlerExt(TIFFErrorHandlerExt);
-extern TIFFErrorHandler TIFFSetWarningHandler(TIFFErrorHandler);
-extern TIFFErrorHandlerExt TIFFSetWarningHandlerExt(TIFFErrorHandlerExt);
-extern TIFFExtendProc TIFFSetTagExtender(TIFFExtendProc);
-extern uint32 TIFFComputeTile(TIFF* tif, uint32 x, uint32 y, uint32 z, uint16 s);
-extern int TIFFCheckTile(TIFF* tif, uint32 x, uint32 y, uint32 z, uint16 s);
-extern uint32 TIFFNumberOfTiles(TIFF*);
-extern tmsize_t TIFFReadTile(TIFF* tif, void* buf, uint32 x, uint32 y, uint32 z, uint16 s);  
-extern tmsize_t TIFFWriteTile(TIFF* tif, void* buf, uint32 x, uint32 y, uint32 z, uint16 s);
-extern uint32 TIFFComputeStrip(TIFF*, uint32, uint16);
-extern uint32 TIFFNumberOfStrips(TIFF*);
-extern tmsize_t TIFFReadEncodedStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size);
-extern tmsize_t TIFFReadRawStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size);  
-extern tmsize_t TIFFReadEncodedTile(TIFF* tif, uint32 tile, void* buf, tmsize_t size);  
-extern tmsize_t TIFFReadRawTile(TIFF* tif, uint32 tile, void* buf, tmsize_t size);  
-extern int      TIFFReadFromUserBuffer(TIFF* tif, uint32 strile,
-                                       void* inbuf, tmsize_t insize,
-                                       void* outbuf, tmsize_t outsize);
-extern tmsize_t TIFFWriteEncodedStrip(TIFF* tif, uint32 strip, void* data, tmsize_t cc);
-extern tmsize_t TIFFWriteRawStrip(TIFF* tif, uint32 strip, void* data, tmsize_t cc);  
-extern tmsize_t TIFFWriteEncodedTile(TIFF* tif, uint32 tile, void* data, tmsize_t cc);  
-extern tmsize_t TIFFWriteRawTile(TIFF* tif, uint32 tile, void* data, tmsize_t cc);  
-extern int TIFFDataWidth(TIFFDataType);    /* table of tag datatype widths */
-extern void TIFFSetWriteOffset(TIFF* tif, toff_t off);
-extern void TIFFSwabShort(uint16*);
-extern void TIFFSwabLong(uint32*);
-extern void TIFFSwabLong8(uint64*);
-extern void TIFFSwabFloat(float*);
-extern void TIFFSwabDouble(double*);
-extern void TIFFSwabArrayOfShort(uint16* wp, tmsize_t n);
-extern void TIFFSwabArrayOfTriples(uint8* tp, tmsize_t n);
-extern void TIFFSwabArrayOfLong(uint32* lp, tmsize_t n);
-extern void TIFFSwabArrayOfLong8(uint64* lp, tmsize_t n);
-extern void TIFFSwabArrayOfFloat(float* fp, tmsize_t n);
-extern void TIFFSwabArrayOfDouble(double* dp, tmsize_t n);
-extern void TIFFReverseBits(uint8* cp, tmsize_t n);
-extern const unsigned char* TIFFGetBitRevTable(int);
+#if defined(c_plusplus) || defined(__cplusplus)
+extern "C"
+{
+#endif
+    typedef void (*TIFFErrorHandler)(const char *, const char *, va_list);
+    typedef void (*TIFFErrorHandlerExt)(thandle_t, const char *, const char *,
+                                        va_list);
+    typedef int (*TIFFErrorHandlerExtR)(TIFF *, void *user_data, const char *,
+                                        const char *, va_list);
+    typedef tmsize_t (*TIFFReadWriteProc)(thandle_t, void *, tmsize_t);
+    typedef toff_t (*TIFFSeekProc)(thandle_t, toff_t, int);
+    typedef int (*TIFFCloseProc)(thandle_t);
+    typedef toff_t (*TIFFSizeProc)(thandle_t);
+    typedef int (*TIFFMapFileProc)(thandle_t, void **base, toff_t *size);
+    typedef void (*TIFFUnmapFileProc)(thandle_t, void *base, toff_t size);
+    typedef void (*TIFFExtendProc)(TIFF *);
 
-extern uint64 TIFFGetStrileOffset(TIFF *tif, uint32 strile);
-extern uint64 TIFFGetStrileByteCount(TIFF *tif, uint32 strile);
-extern uint64 TIFFGetStrileOffsetWithErr(TIFF *tif, uint32 strile, int *pbErr);
-extern uint64 TIFFGetStrileByteCountWithErr(TIFF *tif, uint32 strile, int *pbErr);
+    extern const char *TIFFGetVersion(void);
+
+    extern const TIFFCodec *TIFFFindCODEC(uint16_t);
+    extern TIFFCodec *TIFFRegisterCODEC(uint16_t, const char *, TIFFInitMethod);
+    extern void TIFFUnRegisterCODEC(TIFFCodec *);
+    extern int TIFFIsCODECConfigured(uint16_t);
+    extern TIFFCodec *TIFFGetConfiguredCODECs(void);
+
+    /*
+     * Auxiliary functions.
+     */
+
+    extern void *_TIFFmalloc(tmsize_t s);
+    extern void *_TIFFcalloc(tmsize_t nmemb, tmsize_t siz);
+    extern void *_TIFFrealloc(void *p, tmsize_t s);
+    extern void _TIFFmemset(void *p, int v, tmsize_t c);
+    extern void _TIFFmemcpy(void *d, const void *s, tmsize_t c);
+    extern int _TIFFmemcmp(const void *p1, const void *p2, tmsize_t c);
+    extern void _TIFFfree(void *p);
+
+    /*
+    ** Stuff, related to tag handling and creating custom tags.
+    */
+    extern int TIFFGetTagListCount(TIFF *);
+    extern uint32_t TIFFGetTagListEntry(TIFF *, int tag_index);
+
+#define TIFF_ANY TIFF_NOTYPE /* for field descriptor searching */
+#define TIFF_VARIABLE -1     /* marker for variable length tags */
+#define TIFF_SPP -2          /* marker for SamplesPerPixel tags */
+#define TIFF_VARIABLE2 -3    /* marker for uint32_t var-length tags */
+
+#define FIELD_CUSTOM 65
+
+    typedef struct _TIFFField TIFFField;
+    typedef struct _TIFFFieldArray TIFFFieldArray;
+
+    extern const TIFFField *TIFFFindField(TIFF *, uint32_t, TIFFDataType);
+    extern const TIFFField *TIFFFieldWithTag(TIFF *, uint32_t);
+    extern const TIFFField *TIFFFieldWithName(TIFF *, const char *);
+
+    extern uint32_t TIFFFieldTag(const TIFFField *);
+    extern const char *TIFFFieldName(const TIFFField *);
+    extern TIFFDataType TIFFFieldDataType(const TIFFField *);
+    extern int TIFFFieldPassCount(const TIFFField *);
+    extern int TIFFFieldReadCount(const TIFFField *);
+    extern int TIFFFieldWriteCount(const TIFFField *);
+    extern int
+    TIFFFieldSetGetSize(const TIFFField *); /* returns internal storage size of
+                                               TIFFSetGetFieldType in bytes. */
+    extern int TIFFFieldSetGetCountSize(
+        const TIFFField *); /* returns size of count parameter 0=none,
+                               2=uint16_t, 4=uint32_t */
+    extern int TIFFFieldIsAnonymous(const TIFFField *);
+
+    typedef int (*TIFFVSetMethod)(TIFF *, uint32_t, va_list);
+    typedef int (*TIFFVGetMethod)(TIFF *, uint32_t, va_list);
+    typedef void (*TIFFPrintMethod)(TIFF *, FILE *, long);
+
+    typedef struct
+    {
+        TIFFVSetMethod vsetfield; /* tag set routine */
+        TIFFVGetMethod vgetfield; /* tag get routine */
+        TIFFPrintMethod printdir; /* directory print routine */
+    } TIFFTagMethods;
+
+    extern TIFFTagMethods *TIFFAccessTagMethods(TIFF *);
+    extern void *TIFFGetClientInfo(TIFF *, const char *);
+    extern void TIFFSetClientInfo(TIFF *, void *, const char *);
+
+    extern void TIFFCleanup(TIFF *tif);
+    extern void TIFFClose(TIFF *tif);
+    extern int TIFFFlush(TIFF *tif);
+    extern int TIFFFlushData(TIFF *tif);
+    extern int TIFFGetField(TIFF *tif, uint32_t tag, ...);
+    extern int TIFFVGetField(TIFF *tif, uint32_t tag, va_list ap);
+    extern int TIFFGetFieldDefaulted(TIFF *tif, uint32_t tag, ...);
+    extern int TIFFVGetFieldDefaulted(TIFF *tif, uint32_t tag, va_list ap);
+    extern int TIFFReadDirectory(TIFF *tif);
+    extern int TIFFReadCustomDirectory(TIFF *tif, toff_t diroff,
+                                       const TIFFFieldArray *infoarray);
+    extern int TIFFReadEXIFDirectory(TIFF *tif, toff_t diroff);
+    extern int TIFFReadGPSDirectory(TIFF *tif, toff_t diroff);
+    extern uint64_t TIFFScanlineSize64(TIFF *tif);
+    extern tmsize_t TIFFScanlineSize(TIFF *tif);
+    extern uint64_t TIFFRasterScanlineSize64(TIFF *tif);
+    extern tmsize_t TIFFRasterScanlineSize(TIFF *tif);
+    extern uint64_t TIFFStripSize64(TIFF *tif);
+    extern tmsize_t TIFFStripSize(TIFF *tif);
+    extern uint64_t TIFFRawStripSize64(TIFF *tif, uint32_t strip);
+    extern tmsize_t TIFFRawStripSize(TIFF *tif, uint32_t strip);
+    extern uint64_t TIFFVStripSize64(TIFF *tif, uint32_t nrows);
+    extern tmsize_t TIFFVStripSize(TIFF *tif, uint32_t nrows);
+    extern uint64_t TIFFTileRowSize64(TIFF *tif);
+    extern tmsize_t TIFFTileRowSize(TIFF *tif);
+    extern uint64_t TIFFTileSize64(TIFF *tif);
+    extern tmsize_t TIFFTileSize(TIFF *tif);
+    extern uint64_t TIFFVTileSize64(TIFF *tif, uint32_t nrows);
+    extern tmsize_t TIFFVTileSize(TIFF *tif, uint32_t nrows);
+    extern uint32_t TIFFDefaultStripSize(TIFF *tif, uint32_t request);
+    extern void TIFFDefaultTileSize(TIFF *, uint32_t *, uint32_t *);
+    extern int TIFFFileno(TIFF *);
+    extern int TIFFSetFileno(TIFF *, int);
+    extern thandle_t TIFFClientdata(TIFF *);
+    extern thandle_t TIFFSetClientdata(TIFF *, thandle_t);
+    extern int TIFFGetMode(TIFF *);
+    extern int TIFFSetMode(TIFF *, int);
+    extern int TIFFIsTiled(TIFF *);
+    extern int TIFFIsByteSwapped(TIFF *);
+    extern int TIFFIsUpSampled(TIFF *);
+    extern int TIFFIsMSB2LSB(TIFF *);
+    extern int TIFFIsBigEndian(TIFF *);
+    extern int TIFFIsBigTIFF(TIFF *);
+    extern TIFFReadWriteProc TIFFGetReadProc(TIFF *);
+    extern TIFFReadWriteProc TIFFGetWriteProc(TIFF *);
+    extern TIFFSeekProc TIFFGetSeekProc(TIFF *);
+    extern TIFFCloseProc TIFFGetCloseProc(TIFF *);
+    extern TIFFSizeProc TIFFGetSizeProc(TIFF *);
+    extern TIFFMapFileProc TIFFGetMapFileProc(TIFF *);
+    extern TIFFUnmapFileProc TIFFGetUnmapFileProc(TIFF *);
+    extern uint32_t TIFFCurrentRow(TIFF *);
+    extern tdir_t TIFFCurrentDirectory(TIFF *);
+    extern tdir_t TIFFNumberOfDirectories(TIFF *);
+    extern uint64_t TIFFCurrentDirOffset(TIFF *);
+    extern uint32_t TIFFCurrentStrip(TIFF *);
+    extern uint32_t TIFFCurrentTile(TIFF *tif);
+    extern int TIFFReadBufferSetup(TIFF *tif, void *bp, tmsize_t size);
+    extern int TIFFWriteBufferSetup(TIFF *tif, void *bp, tmsize_t size);
+    extern int TIFFSetupStrips(TIFF *);
+    extern int TIFFWriteCheck(TIFF *, int, const char *);
+    extern void TIFFFreeDirectory(TIFF *);
+    extern int TIFFCreateDirectory(TIFF *);
+    extern int TIFFCreateCustomDirectory(TIFF *, const TIFFFieldArray *);
+    extern int TIFFCreateEXIFDirectory(TIFF *);
+    extern int TIFFCreateGPSDirectory(TIFF *);
+    extern int TIFFLastDirectory(TIFF *);
+    extern int TIFFSetDirectory(TIFF *, tdir_t);
+    extern int TIFFSetSubDirectory(TIFF *, uint64_t);
+    extern int TIFFUnlinkDirectory(TIFF *, tdir_t);
+    extern int TIFFSetField(TIFF *, uint32_t, ...);
+    extern int TIFFVSetField(TIFF *, uint32_t, va_list);
+    extern int TIFFUnsetField(TIFF *, uint32_t);
+    extern int TIFFWriteDirectory(TIFF *);
+    extern int TIFFWriteCustomDirectory(TIFF *, uint64_t *);
+    extern int TIFFCheckpointDirectory(TIFF *);
+    extern int TIFFRewriteDirectory(TIFF *);
+    extern int TIFFDeferStrileArrayWriting(TIFF *);
+    extern int TIFFForceStrileArrayWriting(TIFF *);
+
+#if defined(c_plusplus) || defined(__cplusplus)
+    extern void TIFFPrintDirectory(TIFF *, FILE *, long = 0);
+    extern int TIFFReadScanline(TIFF *tif, void *buf, uint32_t row,
+                                uint16_t sample = 0);
+    extern int TIFFWriteScanline(TIFF *tif, void *buf, uint32_t row,
+                                 uint16_t sample = 0);
+    extern int TIFFReadRGBAImage(TIFF *, uint32_t, uint32_t, uint32_t *,
+                                 int = 0);
+    extern int TIFFReadRGBAImageOriented(TIFF *, uint32_t, uint32_t, uint32_t *,
+                                         int = ORIENTATION_BOTLEFT, int = 0);
+#else
+extern void TIFFPrintDirectory(TIFF *, FILE *, long);
+extern int TIFFReadScanline(TIFF *tif, void *buf, uint32_t row,
+                            uint16_t sample);
+extern int TIFFWriteScanline(TIFF *tif, void *buf, uint32_t row,
+                             uint16_t sample);
+extern int TIFFReadRGBAImage(TIFF *, uint32_t, uint32_t, uint32_t *, int);
+extern int TIFFReadRGBAImageOriented(TIFF *, uint32_t, uint32_t, uint32_t *,
+                                     int, int);
+#endif
+
+    extern int TIFFReadRGBAStrip(TIFF *, uint32_t, uint32_t *);
+    extern int TIFFReadRGBATile(TIFF *, uint32_t, uint32_t, uint32_t *);
+    extern int TIFFReadRGBAStripExt(TIFF *, uint32_t, uint32_t *,
+                                    int stop_on_error);
+    extern int TIFFReadRGBATileExt(TIFF *, uint32_t, uint32_t, uint32_t *,
+                                   int stop_on_error);
+    extern int TIFFRGBAImageOK(TIFF *, char[1024]);
+    extern int TIFFRGBAImageBegin(TIFFRGBAImage *, TIFF *, int, char[1024]);
+    extern int TIFFRGBAImageGet(TIFFRGBAImage *, uint32_t *, uint32_t,
+                                uint32_t);
+    extern void TIFFRGBAImageEnd(TIFFRGBAImage *);
+
+    extern const char *TIFFFileName(TIFF *);
+    extern const char *TIFFSetFileName(TIFF *, const char *);
+    extern void TIFFError(const char *, const char *, ...)
+        TIFF_ATTRIBUTE((__format__(__printf__, 2, 3)));
+    extern void TIFFErrorExt(thandle_t, const char *, const char *, ...)
+        TIFF_ATTRIBUTE((__format__(__printf__, 3, 4)));
+    extern void TIFFWarning(const char *, const char *, ...)
+        TIFF_ATTRIBUTE((__format__(__printf__, 2, 3)));
+    extern void TIFFWarningExt(thandle_t, const char *, const char *, ...)
+        TIFF_ATTRIBUTE((__format__(__printf__, 3, 4)));
+    extern TIFFErrorHandler TIFFSetErrorHandler(TIFFErrorHandler);
+    extern TIFFErrorHandlerExt TIFFSetErrorHandlerExt(TIFFErrorHandlerExt);
+    extern TIFFErrorHandler TIFFSetWarningHandler(TIFFErrorHandler);
+    extern TIFFErrorHandlerExt TIFFSetWarningHandlerExt(TIFFErrorHandlerExt);
+
+    extern void TIFFWarningExtR(TIFF *, const char *, const char *, ...)
+        TIFF_ATTRIBUTE((__format__(__printf__, 3, 4)));
+    extern void TIFFErrorExtR(TIFF *, const char *, const char *, ...)
+        TIFF_ATTRIBUTE((__format__(__printf__, 3, 4)));
+
+    typedef struct TIFFOpenOptions TIFFOpenOptions;
+    extern TIFFOpenOptions *TIFFOpenOptionsAlloc(void);
+    extern void TIFFOpenOptionsFree(TIFFOpenOptions *);
+    extern void
+    TIFFOpenOptionsSetMaxSingleMemAlloc(TIFFOpenOptions *opts,
+                                        tmsize_t max_single_mem_alloc);
+    extern void
+    TIFFOpenOptionsSetErrorHandlerExtR(TIFFOpenOptions *opts,
+                                       TIFFErrorHandlerExtR handler,
+                                       void *errorhandler_user_data);
+    extern void
+    TIFFOpenOptionsSetWarningHandlerExtR(TIFFOpenOptions *opts,
+                                         TIFFErrorHandlerExtR handler,
+                                         void *warnhandler_user_data);
+
+    extern TIFF *TIFFOpen(const char *, const char *);
+    extern TIFF *TIFFOpenExt(const char *, const char *, TIFFOpenOptions *opts);
+#ifdef __WIN32__
+    extern TIFF *TIFFOpenW(const wchar_t *, const char *);
+    extern TIFF *TIFFOpenWExt(const wchar_t *, const char *,
+                              TIFFOpenOptions *opts);
+#endif /* __WIN32__ */
+    extern TIFF *TIFFFdOpen(int, const char *, const char *);
+    extern TIFF *TIFFFdOpenExt(int, const char *, const char *,
+                               TIFFOpenOptions *opts);
+    extern TIFF *TIFFClientOpen(const char *, const char *, thandle_t,
+                                TIFFReadWriteProc, TIFFReadWriteProc,
+                                TIFFSeekProc, TIFFCloseProc, TIFFSizeProc,
+                                TIFFMapFileProc, TIFFUnmapFileProc);
+    extern TIFF *TIFFClientOpenExt(const char *, const char *, thandle_t,
+                                   TIFFReadWriteProc, TIFFReadWriteProc,
+                                   TIFFSeekProc, TIFFCloseProc, TIFFSizeProc,
+                                   TIFFMapFileProc, TIFFUnmapFileProc,
+                                   TIFFOpenOptions *opts);
+    extern TIFFExtendProc TIFFSetTagExtender(TIFFExtendProc);
+    extern uint32_t TIFFComputeTile(TIFF *tif, uint32_t x, uint32_t y,
+                                    uint32_t z, uint16_t s);
+    extern int TIFFCheckTile(TIFF *tif, uint32_t x, uint32_t y, uint32_t z,
+                             uint16_t s);
+    extern uint32_t TIFFNumberOfTiles(TIFF *);
+    extern tmsize_t TIFFReadTile(TIFF *tif, void *buf, uint32_t x, uint32_t y,
+                                 uint32_t z, uint16_t s);
+    extern tmsize_t TIFFWriteTile(TIFF *tif, void *buf, uint32_t x, uint32_t y,
+                                  uint32_t z, uint16_t s);
+    extern uint32_t TIFFComputeStrip(TIFF *, uint32_t, uint16_t);
+    extern uint32_t TIFFNumberOfStrips(TIFF *);
+    extern tmsize_t TIFFReadEncodedStrip(TIFF *tif, uint32_t strip, void *buf,
+                                         tmsize_t size);
+    extern tmsize_t TIFFReadRawStrip(TIFF *tif, uint32_t strip, void *buf,
+                                     tmsize_t size);
+    extern tmsize_t TIFFReadEncodedTile(TIFF *tif, uint32_t tile, void *buf,
+                                        tmsize_t size);
+    extern tmsize_t TIFFReadRawTile(TIFF *tif, uint32_t tile, void *buf,
+                                    tmsize_t size);
+    extern int TIFFReadFromUserBuffer(TIFF *tif, uint32_t strile, void *inbuf,
+                                      tmsize_t insize, void *outbuf,
+                                      tmsize_t outsize);
+    extern tmsize_t TIFFWriteEncodedStrip(TIFF *tif, uint32_t strip, void *data,
+                                          tmsize_t cc);
+    extern tmsize_t TIFFWriteRawStrip(TIFF *tif, uint32_t strip, void *data,
+                                      tmsize_t cc);
+    extern tmsize_t TIFFWriteEncodedTile(TIFF *tif, uint32_t tile, void *data,
+                                         tmsize_t cc);
+    extern tmsize_t TIFFWriteRawTile(TIFF *tif, uint32_t tile, void *data,
+                                     tmsize_t cc);
+    extern int TIFFDataWidth(
+        TIFFDataType); /* table of tag datatype widths within TIFF file. */
+    extern void TIFFSetWriteOffset(TIFF *tif, toff_t off);
+    extern void TIFFSwabShort(uint16_t *);
+    extern void TIFFSwabLong(uint32_t *);
+    extern void TIFFSwabLong8(uint64_t *);
+    extern void TIFFSwabFloat(float *);
+    extern void TIFFSwabDouble(double *);
+    extern void TIFFSwabArrayOfShort(uint16_t *wp, tmsize_t n);
+    extern void TIFFSwabArrayOfTriples(uint8_t *tp, tmsize_t n);
+    extern void TIFFSwabArrayOfLong(uint32_t *lp, tmsize_t n);
+    extern void TIFFSwabArrayOfLong8(uint64_t *lp, tmsize_t n);
+    extern void TIFFSwabArrayOfFloat(float *fp, tmsize_t n);
+    extern void TIFFSwabArrayOfDouble(double *dp, tmsize_t n);
+    extern void TIFFReverseBits(uint8_t *cp, tmsize_t n);
+    extern const unsigned char *TIFFGetBitRevTable(int);
+
+    extern uint64_t TIFFGetStrileOffset(TIFF *tif, uint32_t strile);
+    extern uint64_t TIFFGetStrileByteCount(TIFF *tif, uint32_t strile);
+    extern uint64_t TIFFGetStrileOffsetWithErr(TIFF *tif, uint32_t strile,
+                                               int *pbErr);
+    extern uint64_t TIFFGetStrileByteCountWithErr(TIFF *tif, uint32_t strile,
+                                                  int *pbErr);
 
 #ifdef LOGLUV_PUBLIC
-#define U_NEU		0.210526316
-#define V_NEU		0.473684211
-#define UVSCALE		410.
-extern double LogL16toY(int);
-extern double LogL10toY(int);
-extern void XYZtoRGB24(float*, uint8*);
-extern int uv_decode(double*, double*, int);
-extern void LogLuv24toXYZ(uint32, float*);
-extern void LogLuv32toXYZ(uint32, float*);
+#define U_NEU 0.210526316
+#define V_NEU 0.473684211
+#define UVSCALE 410.
+    extern double LogL16toY(int);
+    extern double LogL10toY(int);
+    extern void XYZtoRGB24(float *, uint8_t *);
+    extern int uv_decode(double *, double *, int);
+    extern void LogLuv24toXYZ(uint32_t, float *);
+    extern void LogLuv32toXYZ(uint32_t, float *);
 #if defined(c_plusplus) || defined(__cplusplus)
-extern int LogL16fromY(double, int = SGILOGENCODE_NODITHER);
-extern int LogL10fromY(double, int = SGILOGENCODE_NODITHER);
-extern int uv_encode(double, double, int = SGILOGENCODE_NODITHER);
-extern uint32 LogLuv24fromXYZ(float*, int = SGILOGENCODE_NODITHER);
-extern uint32 LogLuv32fromXYZ(float*, int = SGILOGENCODE_NODITHER);
+    extern int LogL16fromY(double, int = SGILOGENCODE_NODITHER);
+    extern int LogL10fromY(double, int = SGILOGENCODE_NODITHER);
+    extern int uv_encode(double, double, int = SGILOGENCODE_NODITHER);
+    extern uint32_t LogLuv24fromXYZ(float *, int = SGILOGENCODE_NODITHER);
+    extern uint32_t LogLuv32fromXYZ(float *, int = SGILOGENCODE_NODITHER);
 #else
-extern int LogL16fromY(double, int);
-extern int LogL10fromY(double, int);
-extern int uv_encode(double, double, int);
-extern uint32 LogLuv24fromXYZ(float*, int);
-extern uint32 LogLuv32fromXYZ(float*, int);
+    extern int LogL16fromY(double, int);
+    extern int LogL10fromY(double, int);
+    extern int uv_encode(double, double, int);
+    extern uint32_t LogLuv24fromXYZ(float *, int);
+    extern uint32_t LogLuv32fromXYZ(float *, int);
 #endif
 #endif /* LOGLUV_PUBLIC */
 
-extern int TIFFCIELabToRGBInit(TIFFCIELabToRGB*, const TIFFDisplay *, float*);
-extern void TIFFCIELabToXYZ(TIFFCIELabToRGB *, uint32, int32, int32,
-    float *, float *, float *);
-extern void TIFFXYZToRGB(TIFFCIELabToRGB *, float, float, float,
-    uint32 *, uint32 *, uint32 *);
+    extern int TIFFCIELabToRGBInit(TIFFCIELabToRGB *, const TIFFDisplay *,
+                                   float *);
+    extern void TIFFCIELabToXYZ(TIFFCIELabToRGB *, uint32_t, int32_t, int32_t,
+                                float *, float *, float *);
+    extern void TIFFXYZToRGB(TIFFCIELabToRGB *, float, float, float, uint32_t *,
+                             uint32_t *, uint32_t *);
 
-extern int TIFFYCbCrToRGBInit(TIFFYCbCrToRGB*, float*, float*);
-extern void TIFFYCbCrtoRGB(TIFFYCbCrToRGB *, uint32, int32, int32,
-    uint32 *, uint32 *, uint32 *);
+    extern int TIFFYCbCrToRGBInit(TIFFYCbCrToRGB *, float *, float *);
+    extern void TIFFYCbCrtoRGB(TIFFYCbCrToRGB *, uint32_t, int32_t, int32_t,
+                               uint32_t *, uint32_t *, uint32_t *);
 
-/****************************************************************************
- *               O B S O L E T E D    I N T E R F A C E S
- *
- * Don't use this stuff in your applications, it may be removed in the future
- * libtiff versions.
- ****************************************************************************/
-typedef	struct {
-	ttag_t	field_tag;		/* field's tag */
-	short	field_readcount;	/* read count/TIFF_VARIABLE/TIFF_SPP */
-	short	field_writecount;	/* write count/TIFF_VARIABLE */
-	TIFFDataType field_type;	/* type of associated data */
-        unsigned short field_bit;	/* bit in fieldsset bit vector */
-	unsigned char field_oktochange;	/* if true, can change while writing */
-	unsigned char field_passcount;	/* if true, pass dir count on set */
-	char	*field_name;		/* ASCII name */
-} TIFFFieldInfo;
+    /****************************************************************************
+     *               O B S O L E T E D    I N T E R F A C E S
+     *
+     * Don't use this stuff in your applications, it may be removed in the
+     *future libtiff versions.
+     ****************************************************************************/
+    typedef struct
+    {
+        ttag_t field_tag;               /* field's tag */
+        short field_readcount;          /* read count/TIFF_VARIABLE/TIFF_SPP */
+        short field_writecount;         /* write count/TIFF_VARIABLE */
+        TIFFDataType field_type;        /* type of associated data */
+        unsigned short field_bit;       /* bit in fieldsset bit vector */
+        unsigned char field_oktochange; /* if true, can change while writing */
+        unsigned char field_passcount;  /* if true, pass dir count on set */
+        char *field_name;               /* ASCII name */
+    } TIFFFieldInfo;
 
-extern int TIFFMergeFieldInfo(TIFF*, const TIFFFieldInfo[], uint32);
-        
+    extern int TIFFMergeFieldInfo(TIFF *, const TIFFFieldInfo[], uint32_t);
+
 #if defined(c_plusplus) || defined(__cplusplus)
 }
 #endif
 
 #endif /* _TIFFIO_ */
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tiffiop.h b/third_party/libtiff/tiffiop.h
index ba7b265..a7cc12b 100644
--- a/third_party/libtiff/tiffiop.h
+++ b/third_party/libtiff/tiffiop.h
@@ -2,28 +2,28 @@
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
  *
- * Permission to use, copy, modify, distribute, and sell this software and 
+ * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
  * that (i) the above copyright notices and this permission notice appear in
  * all copies of the software and related documentation, and (ii) the names of
  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  * publicity relating to the software without the specific, prior written
  * permission of Sam Leffler and Silicon Graphics.
- * 
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
- * 
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  * OF THIS SOFTWARE.
  */
 
 #ifndef _TIFFIOP_
-#define	_TIFFIOP_
+#define _TIFFIOP_
 /*
  * ``Library-private'' definitions.
  */
@@ -31,65 +31,48 @@
 #include "tiffconf.h"
 
 #ifdef HAVE_FCNTL_H
-# include <fcntl.h>
+#include <fcntl.h>
 #endif
 
 #ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
+#include <sys/types.h>
 #endif
 
-#ifdef HAVE_STRING_H
-# include <string.h>
-#endif
+#include <string.h>
 
 #ifdef HAVE_ASSERT_H
-# include <assert.h>
+#include <assert.h>
 #else
-# define assert(x) 
+#define assert(x)
 #endif
 
-#ifdef HAVE_SEARCH_H
-# include <search.h>
-#else
-extern void *lfind(const void *, const void *, size_t *, size_t,
-		   int (*)(const void *, const void *));
-#endif
-
-#if !defined(HAVE_SNPRINTF) && !defined(HAVE__SNPRINTF)
-#undef snprintf
-#define snprintf FXSYS_snprintf
-#endif
-
+#include "tif_hash_set.h"
 #include "tiffio.h"
 
 #include "tif_dir.h"
 
+#include <limits.h>
+
 #ifndef STRIP_SIZE_DEFAULT
-# define STRIP_SIZE_DEFAULT 8192
+#define STRIP_SIZE_DEFAULT 8192
 #endif
 
-#define    streq(a,b)      (strcmp(a,b) == 0)
-#define    strneq(a,b,n)   (strncmp(a,b,n) == 0)
+#ifndef TIFF_MAX_DIR_COUNT
+#define TIFF_MAX_DIR_COUNT 1048576
+#endif
+
+#define TIFF_NON_EXISTENT_DIR_NUMBER UINT_MAX
+
+#define streq(a, b) (strcmp(a, b) == 0)
+#define strneq(a, b, n) (strncmp(a, b, n) == 0)
 
 #ifndef TRUE
-#define	TRUE	1
-#define	FALSE	0
+#define TRUE 1
+#define FALSE 0
 #endif
 
-#define TIFF_SIZE_T_MAX ((size_t) ~ ((size_t)0))
-#define TIFF_TMSIZE_T_MAX (tmsize_t)(TIFF_SIZE_T_MAX >> 1)
-
-/*
- * Largest 32-bit unsigned integer value.
- */
-#define TIFF_UINT32_MAX 0xFFFFFFFFU
-
-/*
- * Largest 64-bit unsigned integer value.
- */
-#define TIFF_UINT64_MAX (((uint64)(TIFF_UINT32_MAX)) << 32 | TIFF_UINT32_MAX)
-
-typedef struct client_info {
+typedef struct client_info
+{
     struct client_info *next;
     void *data;
     char *name;
@@ -99,187 +82,231 @@
  * Typedefs for ``method pointers'' used internally.
  * these are deprecated and provided only for backwards compatibility.
  */
-typedef unsigned char tidataval_t;    /* internal image data value type */
-typedef tidataval_t* tidata_t;        /* reference to internal image data */
+typedef unsigned char tidataval_t; /* internal image data value type */
+typedef tidataval_t *tidata_t;     /* reference to internal image data */
 
-typedef void (*TIFFVoidMethod)(TIFF*);
-typedef int (*TIFFBoolMethod)(TIFF*);
-typedef int (*TIFFPreMethod)(TIFF*, uint16);
-typedef int (*TIFFCodeMethod)(TIFF* tif, uint8* buf, tmsize_t size, uint16 sample);
-typedef int (*TIFFSeekMethod)(TIFF*, uint32);
-typedef void (*TIFFPostMethod)(TIFF* tif, uint8* buf, tmsize_t size);
-typedef uint32 (*TIFFStripMethod)(TIFF*, uint32);
-typedef void (*TIFFTileMethod)(TIFF*, uint32*, uint32*);
+typedef void (*TIFFVoidMethod)(TIFF *);
+typedef int (*TIFFBoolMethod)(TIFF *);
+typedef int (*TIFFPreMethod)(TIFF *, uint16_t);
+typedef int (*TIFFCodeMethod)(TIFF *tif, uint8_t *buf, tmsize_t size,
+                              uint16_t sample);
+typedef int (*TIFFSeekMethod)(TIFF *, uint32_t);
+typedef void (*TIFFPostMethod)(TIFF *tif, uint8_t *buf, tmsize_t size);
+typedef uint32_t (*TIFFStripMethod)(TIFF *, uint32_t);
+typedef void (*TIFFTileMethod)(TIFF *, uint32_t *, uint32_t *);
 
-struct tiff {
-	char*                tif_name;         /* name of open file */
-	int                  tif_fd;           /* open file descriptor */
-	int                  tif_mode;         /* open mode (O_*) */
-	uint32               tif_flags;
-	#define TIFF_FILLORDER   0x00003U /* natural bit fill order for machine */
-	#define TIFF_DIRTYHEADER 0x00004U /* header must be written on close */
-	#define TIFF_DIRTYDIRECT 0x00008U /* current directory must be written */
-	#define TIFF_BUFFERSETUP 0x00010U /* data buffers setup */
-	#define TIFF_CODERSETUP  0x00020U /* encoder/decoder setup done */
-	#define TIFF_BEENWRITING 0x00040U /* written 1+ scanlines to file */
-	#define TIFF_SWAB        0x00080U /* byte swap file information */
-	#define TIFF_NOBITREV    0x00100U /* inhibit bit reversal logic */
-	#define TIFF_MYBUFFER    0x00200U /* my raw data buffer; free on close */
-	#define TIFF_ISTILED     0x00400U /* file is tile, not strip- based */
-	#define TIFF_MAPPED      0x00800U /* file is mapped into memory */
-	#define TIFF_POSTENCODE  0x01000U /* need call to postencode routine */
-	#define TIFF_INSUBIFD    0x02000U /* currently writing a subifd */
-	#define TIFF_UPSAMPLED   0x04000U /* library is doing data up-sampling */
-	#define TIFF_STRIPCHOP   0x08000U /* enable strip chopping support */
-	#define TIFF_HEADERONLY  0x10000U /* read header only, do not process the first directory */
-	#define TIFF_NOREADRAW   0x20000U /* skip reading of raw uncompressed image data */
-	#define TIFF_INCUSTOMIFD 0x40000U /* currently writing a custom IFD */
-	#define TIFF_BIGTIFF     0x80000U /* read/write bigtiff */
-        #define TIFF_BUF4WRITE  0x100000U /* rawcc bytes are for writing */
-        #define TIFF_DIRTYSTRIP 0x200000U /* stripoffsets/stripbytecount dirty*/
-        #define TIFF_PERSAMPLE  0x400000U /* get/set per sample tags as arrays */
-        #define TIFF_BUFFERMMAP 0x800000U /* read buffer (tif_rawdata) points into mmap() memory */
-        #define TIFF_DEFERSTRILELOAD 0x1000000U /* defer strip/tile offset/bytecount array loading. */
-        #define TIFF_LAZYSTRILELOAD  0x2000000U /* lazy/ondemand loading of strip/tile offset/bytecount values. Only used if TIFF_DEFERSTRILELOAD is set and in read-only mode */
-        #define TIFF_CHOPPEDUPARRAYS 0x4000000U /* set when allocChoppedUpStripArrays() has modified strip array */
-	uint64               tif_diroff;       /* file offset of current directory */
-	uint64               tif_nextdiroff;   /* file offset of following directory */
-	uint64*              tif_dirlist;      /* list of offsets to already seen directories to prevent IFD looping */
-	uint16               tif_dirlistsize;  /* number of entries in offset list */
-	uint16               tif_dirnumber;    /* number of already seen directories */
-	TIFFDirectory        tif_dir;          /* internal rep of current directory */
-	TIFFDirectory        tif_customdir;    /* custom IFDs are separated from the main ones */
-	union {
-		TIFFHeaderCommon common;
-		TIFFHeaderClassic classic;
-		TIFFHeaderBig big;
-	} tif_header;
-	uint16               tif_header_size;  /* file's header block and its length */
-	uint32               tif_row;          /* current scanline */
-	uint16               tif_curdir;       /* current directory (index) */
-	uint32               tif_curstrip;     /* current strip for read/write */
-	uint64               tif_curoff;       /* current offset for read/write */
-	uint64               tif_dataoff;      /* current offset for writing dir */
-	/* SubIFD support */
-	uint16               tif_nsubifd;      /* remaining subifds to write */
-	uint64               tif_subifdoff;    /* offset for patching SubIFD link */
-	/* tiling support */
-	uint32               tif_col;          /* current column (offset by row too) */
-	uint32               tif_curtile;      /* current tile for read/write */
-	tmsize_t             tif_tilesize;     /* # of bytes in a tile */
-	/* compression scheme hooks */
-	int                  tif_decodestatus;
-	TIFFBoolMethod       tif_fixuptags;    /* called in TIFFReadDirectory */
-	TIFFBoolMethod       tif_setupdecode;  /* called once before predecode */
-	TIFFPreMethod        tif_predecode;    /* pre- row/strip/tile decoding */
-	TIFFBoolMethod       tif_setupencode;  /* called once before preencode */
-	int                  tif_encodestatus;
-	TIFFPreMethod        tif_preencode;    /* pre- row/strip/tile encoding */
-	TIFFBoolMethod       tif_postencode;   /* post- row/strip/tile encoding */
-	TIFFCodeMethod       tif_decoderow;    /* scanline decoding routine */
-	TIFFCodeMethod       tif_encoderow;    /* scanline encoding routine */
-	TIFFCodeMethod       tif_decodestrip;  /* strip decoding routine */
-	TIFFCodeMethod       tif_encodestrip;  /* strip encoding routine */
-	TIFFCodeMethod       tif_decodetile;   /* tile decoding routine */
-	TIFFCodeMethod       tif_encodetile;   /* tile encoding routine */
-	TIFFVoidMethod       tif_close;        /* cleanup-on-close routine */
-	TIFFSeekMethod       tif_seek;         /* position within a strip routine */
-	TIFFVoidMethod       tif_cleanup;      /* cleanup state routine */
-	TIFFStripMethod      tif_defstripsize; /* calculate/constrain strip size */
-	TIFFTileMethod       tif_deftilesize;  /* calculate/constrain tile size */
-	uint8*               tif_data;         /* compression scheme private data */
-	/* input/output buffering */
-	tmsize_t             tif_scanlinesize; /* # of bytes in a scanline */
-	tmsize_t             tif_scanlineskew; /* scanline skew for reading strips */
-	uint8*               tif_rawdata;      /* raw data buffer */
-	tmsize_t             tif_rawdatasize;  /* # of bytes in raw data buffer */
-        tmsize_t             tif_rawdataoff;   /* rawdata offset within strip */
-        tmsize_t             tif_rawdataloaded;/* amount of data in rawdata */
-	uint8*               tif_rawcp;        /* current spot in raw buffer */
-	tmsize_t             tif_rawcc;        /* bytes unread from raw buffer */
-	/* memory-mapped file support */
-	uint8*               tif_base;         /* base of mapped file */
-	tmsize_t             tif_size;         /* size of mapped file region (bytes, thus tmsize_t) */
-	TIFFMapFileProc      tif_mapproc;      /* map file method */
-	TIFFUnmapFileProc    tif_unmapproc;    /* unmap file method */
-	/* input/output callback methods */
-	thandle_t            tif_clientdata;   /* callback parameter */
-	TIFFReadWriteProc    tif_readproc;     /* read method */
-	TIFFReadWriteProc    tif_writeproc;    /* write method */
-	TIFFSeekProc         tif_seekproc;     /* lseek method */
-	TIFFCloseProc        tif_closeproc;    /* close method */
-	TIFFSizeProc         tif_sizeproc;     /* filesize method */
-	/* post-decoding support */
-	TIFFPostMethod       tif_postdecode;   /* post decoding routine */
-	/* tag support */
-	TIFFField**          tif_fields;       /* sorted table of registered tags */
-	size_t               tif_nfields;      /* # entries in registered tag table */
-	const TIFFField*     tif_foundfield;   /* cached pointer to already found tag */
-	TIFFTagMethods       tif_tagmethods;   /* tag get/set/print routines */
-	TIFFClientInfoLink*  tif_clientinfo;   /* extra client information. */
-	/* Backward compatibility stuff. We need these two fields for
-	 * setting up an old tag extension scheme. */
-	TIFFFieldArray*      tif_fieldscompat;
-	size_t               tif_nfieldscompat;
+struct TIFFOffsetAndDirNumber
+{
+    uint64_t offset;
+    tdir_t dirNumber;
+};
+typedef struct TIFFOffsetAndDirNumber TIFFOffsetAndDirNumber;
+
+struct tiff
+{
+    char *tif_name; /* name of open file */
+    int tif_fd;     /* open file descriptor */
+    int tif_mode;   /* open mode (O_*) */
+    uint32_t tif_flags;
+#define TIFF_FILLORDER 0x00003U   /* natural bit fill order for machine */
+#define TIFF_DIRTYHEADER 0x00004U /* header must be written on close */
+#define TIFF_DIRTYDIRECT 0x00008U /* current directory must be written */
+#define TIFF_BUFFERSETUP 0x00010U /* data buffers setup */
+#define TIFF_CODERSETUP 0x00020U  /* encoder/decoder setup done */
+#define TIFF_BEENWRITING 0x00040U /* written 1+ scanlines to file */
+#define TIFF_SWAB 0x00080U        /* byte swap file information */
+#define TIFF_NOBITREV 0x00100U    /* inhibit bit reversal logic */
+#define TIFF_MYBUFFER 0x00200U    /* my raw data buffer; free on close */
+#define TIFF_ISTILED 0x00400U     /* file is tile, not strip- based */
+#define TIFF_MAPPED 0x00800U      /* file is mapped into memory */
+#define TIFF_POSTENCODE 0x01000U  /* need call to postencode routine */
+#define TIFF_INSUBIFD 0x02000U    /* currently writing a subifd */
+#define TIFF_UPSAMPLED 0x04000U   /* library is doing data up-sampling */
+#define TIFF_STRIPCHOP 0x08000U   /* enable strip chopping support */
+#define TIFF_HEADERONLY                                                        \
+    0x10000U /* read header only, do not process the first directory */
+#define TIFF_NOREADRAW                                                         \
+    0x20000U /* skip reading of raw uncompressed image data */
+#define TIFF_INCUSTOMIFD 0x40000U /* currently writing a custom IFD */
+#define TIFF_BIGTIFF 0x80000U     /* read/write bigtiff */
+#define TIFF_BUF4WRITE 0x100000U  /* rawcc bytes are for writing */
+#define TIFF_DIRTYSTRIP 0x200000U /* stripoffsets/stripbytecount dirty*/
+#define TIFF_PERSAMPLE 0x400000U  /* get/set per sample tags as arrays */
+#define TIFF_BUFFERMMAP                                                        \
+    0x800000U /* read buffer (tif_rawdata) points into mmap() memory */
+#define TIFF_DEFERSTRILELOAD                                                   \
+    0x1000000U /* defer strip/tile offset/bytecount array loading. */
+#define TIFF_LAZYSTRILELOAD                                                    \
+    0x2000000U /* lazy/ondemand loading of strip/tile offset/bytecount values. \
+                  Only used if TIFF_DEFERSTRILELOAD is set and in read-only    \
+                  mode */
+#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 */
+    TIFFHashSet *tif_map_dir_offset_to_number;
+    TIFFHashSet *tif_map_dir_number_to_offset;
+    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
+    {
+        TIFFHeaderCommon common;
+        TIFFHeaderClassic classic;
+        TIFFHeaderBig big;
+    } tif_header;
+    uint16_t tif_header_size;  /* file's header block and its length */
+    uint32_t tif_row;          /* current scanline */
+    tdir_t tif_curdir;         /* current directory (index) */
+    uint32_t tif_curstrip;     /* current strip for read/write */
+    uint64_t tif_curoff;       /* current offset for read/write */
+    uint64_t tif_lastvalidoff; /* last valid offset allowed for rewrite in
+                                  place. Used only by TIFFAppendToStrip() */
+    uint64_t tif_dataoff;      /* current offset for writing dir */
+    /* SubIFD support */
+    uint16_t tif_nsubifd;   /* remaining subifds to write */
+    uint64_t tif_subifdoff; /* offset for patching SubIFD link */
+    /* tiling support */
+    uint32_t tif_col;      /* current column (offset by row too) */
+    uint32_t tif_curtile;  /* current tile for read/write */
+    tmsize_t tif_tilesize; /* # of bytes in a tile */
+    /* compression scheme hooks */
+    int tif_decodestatus;
+    TIFFBoolMethod tif_fixuptags;   /* called in TIFFReadDirectory */
+    TIFFBoolMethod tif_setupdecode; /* called once before predecode */
+    TIFFPreMethod tif_predecode;    /* pre- row/strip/tile decoding */
+    TIFFBoolMethod tif_setupencode; /* called once before preencode */
+    int tif_encodestatus;
+    TIFFPreMethod tif_preencode;      /* pre- row/strip/tile encoding */
+    TIFFBoolMethod tif_postencode;    /* post- row/strip/tile encoding */
+    TIFFCodeMethod tif_decoderow;     /* scanline decoding routine */
+    TIFFCodeMethod tif_encoderow;     /* scanline encoding routine */
+    TIFFCodeMethod tif_decodestrip;   /* strip decoding routine */
+    TIFFCodeMethod tif_encodestrip;   /* strip encoding routine */
+    TIFFCodeMethod tif_decodetile;    /* tile decoding routine */
+    TIFFCodeMethod tif_encodetile;    /* tile encoding routine */
+    TIFFVoidMethod tif_close;         /* cleanup-on-close routine */
+    TIFFSeekMethod tif_seek;          /* position within a strip routine */
+    TIFFVoidMethod tif_cleanup;       /* cleanup state routine */
+    TIFFStripMethod tif_defstripsize; /* calculate/constrain strip size */
+    TIFFTileMethod tif_deftilesize;   /* calculate/constrain tile size */
+    uint8_t *tif_data;                /* compression scheme private data */
+    /* input/output buffering */
+    tmsize_t tif_scanlinesize;  /* # of bytes in a scanline */
+    tmsize_t tif_scanlineskew;  /* scanline skew for reading strips */
+    uint8_t *tif_rawdata;       /* raw data buffer */
+    tmsize_t tif_rawdatasize;   /* # of bytes in raw data buffer */
+    tmsize_t tif_rawdataoff;    /* rawdata offset within strip */
+    tmsize_t tif_rawdataloaded; /* amount of data in rawdata */
+    uint8_t *tif_rawcp;         /* current spot in raw buffer */
+    tmsize_t tif_rawcc;         /* bytes unread from raw buffer */
+    /* memory-mapped file support */
+    uint8_t *tif_base; /* base of mapped file */
+    tmsize_t tif_size; /* size of mapped file region (bytes, thus tmsize_t) */
+    TIFFMapFileProc tif_mapproc;     /* map file method */
+    TIFFUnmapFileProc tif_unmapproc; /* unmap file method */
+    /* input/output callback methods */
+    thandle_t tif_clientdata;        /* callback parameter */
+    TIFFReadWriteProc tif_readproc;  /* read method */
+    TIFFReadWriteProc tif_writeproc; /* write method */
+    TIFFSeekProc tif_seekproc;       /* lseek method */
+    TIFFCloseProc tif_closeproc;     /* close method */
+    TIFFSizeProc tif_sizeproc;       /* filesize method */
+    /* post-decoding support */
+    TIFFPostMethod tif_postdecode; /* post decoding routine */
+    /* tag support */
+    TIFFField **tif_fields;          /* sorted table of registered tags */
+    size_t tif_nfields;              /* # entries in registered tag table */
+    const TIFFField *tif_foundfield; /* cached pointer to already found tag */
+    TIFFTagMethods tif_tagmethods;   /* tag get/set/print routines */
+    TIFFClientInfoLink *tif_clientinfo; /* extra client information. */
+    /* Backward compatibility stuff. We need these two fields for
+     * setting up an old tag extension scheme. */
+    TIFFFieldArray *tif_fieldscompat;
+    size_t tif_nfieldscompat;
+    /* Error handler support */
+    TIFFErrorHandlerExtR tif_errorhandler;
+    void *tif_errorhandler_user_data;
+    TIFFErrorHandlerExtR tif_warnhandler;
+    void *tif_warnhandler_user_data;
+    tmsize_t tif_max_single_mem_alloc; /* in bytes. 0 for unlimited */
 };
 
-#define isPseudoTag(t) (t > 0xffff)            /* is tag value normal or pseudo */
+struct TIFFOpenOptions
+{
+    TIFFErrorHandlerExtR errorhandler; /* may be NULL */
+    void *errorhandler_user_data;      /* may be NULL */
+    TIFFErrorHandlerExtR warnhandler;  /* may be NULL */
+    void *warnhandler_user_data;       /* may be NULL */
+    tmsize_t max_single_mem_alloc;     /* in bytes. 0 for unlimited */
+};
+
+#define isPseudoTag(t) (t > 0xffff) /* is tag value normal or pseudo */
 
 #define isTiled(tif) (((tif)->tif_flags & TIFF_ISTILED) != 0)
 #define isMapped(tif) (((tif)->tif_flags & TIFF_MAPPED) != 0)
 #define isFillOrder(tif, o) (((tif)->tif_flags & (o)) != 0)
 #define isUpSampled(tif) (((tif)->tif_flags & TIFF_UPSAMPLED) != 0)
-#define TIFFReadFile(tif, buf, size) \
-	((*(tif)->tif_readproc)((tif)->tif_clientdata,(buf),(size)))
-#define TIFFWriteFile(tif, buf, size) \
-	((*(tif)->tif_writeproc)((tif)->tif_clientdata,(buf),(size)))
-#define TIFFSeekFile(tif, off, whence) \
-	((*(tif)->tif_seekproc)((tif)->tif_clientdata,(off),(whence)))
-#define TIFFCloseFile(tif) \
-	((*(tif)->tif_closeproc)((tif)->tif_clientdata))
-#define TIFFGetFileSize(tif) \
-	((*(tif)->tif_sizeproc)((tif)->tif_clientdata))
-#define TIFFMapFileContents(tif, paddr, psize) \
-	((*(tif)->tif_mapproc)((tif)->tif_clientdata,(paddr),(psize)))
-#define TIFFUnmapFileContents(tif, addr, size) \
-	((*(tif)->tif_unmapproc)((tif)->tif_clientdata,(addr),(size)))
+#define TIFFReadFile(tif, buf, size)                                           \
+    ((*(tif)->tif_readproc)((tif)->tif_clientdata, (buf), (size)))
+#define TIFFWriteFile(tif, buf, size)                                          \
+    ((*(tif)->tif_writeproc)((tif)->tif_clientdata, (buf), (size)))
+#define TIFFSeekFile(tif, off, whence)                                         \
+    ((*(tif)->tif_seekproc)((tif)->tif_clientdata, (off), (whence)))
+#define TIFFCloseFile(tif) ((*(tif)->tif_closeproc)((tif)->tif_clientdata))
+#define TIFFGetFileSize(tif) ((*(tif)->tif_sizeproc)((tif)->tif_clientdata))
+#define TIFFMapFileContents(tif, paddr, psize)                                 \
+    ((*(tif)->tif_mapproc)((tif)->tif_clientdata, (paddr), (psize)))
+#define TIFFUnmapFileContents(tif, addr, size)                                 \
+    ((*(tif)->tif_unmapproc)((tif)->tif_clientdata, (addr), (size)))
 
 /*
  * Default Read/Seek/Write definitions.
  */
 #ifndef ReadOK
-#define ReadOK(tif, buf, size) \
-	(TIFFReadFile((tif),(buf),(size))==(size))
+#define ReadOK(tif, buf, size) (TIFFReadFile((tif), (buf), (size)) == (size))
 #endif
 #ifndef SeekOK
 #define SeekOK(tif, off) _TIFFSeekOK(tif, off)
 #endif
 #ifndef WriteOK
-#define WriteOK(tif, buf, size) \
-	(TIFFWriteFile((tif),(buf),(size))==(size))
+#define WriteOK(tif, buf, size) (TIFFWriteFile((tif), (buf), (size)) == (size))
 #endif
 
-/* NB: the uint32 casts are to silence certain ANSI-C compilers */
-#define TIFFhowmany_32(x, y) (((uint32)x < (0xffffffff - (uint32)(y-1))) ? \
-			   ((((uint32)(x))+(((uint32)(y))-1))/((uint32)(y))) : \
-			   0U)
+/* NB: the uint32_t casts are to silence certain ANSI-C compilers */
+#define TIFFhowmany_32(x, y)                                                   \
+    (((uint32_t)x < (0xffffffff - (uint32_t)(y - 1)))                          \
+         ? ((((uint32_t)(x)) + (((uint32_t)(y)) - 1)) / ((uint32_t)(y)))       \
+         : 0U)
 /* Variant of TIFFhowmany_32() that doesn't return 0 if x close to MAXUINT. */
 /* Caution: TIFFhowmany_32_maxuint_compat(x,y)*y might overflow */
-#define TIFFhowmany_32_maxuint_compat(x, y) \
-			   (((uint32)(x) / (uint32)(y)) + ((((uint32)(x) % (uint32)(y)) != 0) ? 1 : 0))
-#define TIFFhowmany8_32(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3)
-#define TIFFroundup_32(x, y) (TIFFhowmany_32(x,y)*(y))
-#define TIFFhowmany_64(x, y) ((((uint64)(x))+(((uint64)(y))-1))/((uint64)(y)))
-#define TIFFhowmany8_64(x) (((x)&0x07)?((uint64)(x)>>3)+1:(uint64)(x)>>3)
-#define TIFFroundup_64(x, y) (TIFFhowmany_64(x,y)*(y))
+#define TIFFhowmany_32_maxuint_compat(x, y)                                    \
+    (((uint32_t)(x) / (uint32_t)(y)) +                                         \
+     ((((uint32_t)(x) % (uint32_t)(y)) != 0) ? 1 : 0))
+#define TIFFhowmany8_32(x)                                                     \
+    (((x)&0x07) ? ((uint32_t)(x) >> 3) + 1 : (uint32_t)(x) >> 3)
+#define TIFFroundup_32(x, y) (TIFFhowmany_32(x, y) * (y))
+#define TIFFhowmany_64(x, y)                                                   \
+    ((((uint64_t)(x)) + (((uint64_t)(y)) - 1)) / ((uint64_t)(y)))
+#define TIFFhowmany8_64(x)                                                     \
+    (((x)&0x07) ? ((uint64_t)(x) >> 3) + 1 : (uint64_t)(x) >> 3)
+#define TIFFroundup_64(x, y) (TIFFhowmany_64(x, y) * (y))
 
-/* Safe multiply which returns zero if there is an *unsigned* integer overflow. This macro is not safe for *signed* integer types */
-#define TIFFSafeMultiply(t,v,m) ((((t)(m) != (t)0) && (((t)(((v)*(m))/(m))) == (t)(v))) ? (t)((v)*(m)) : (t)0)
+/* Safe multiply which returns zero if there is an *unsigned* integer overflow.
+ * This macro is not safe for *signed* integer types */
+#define TIFFSafeMultiply(t, v, m)                                              \
+    ((((t)(m) != (t)0) && (((t)(((v) * (m)) / (m))) == (t)(v)))                \
+         ? (t)((v) * (m))                                                      \
+         : (t)0)
 
-#define TIFFmax(A,B) ((A)>(B)?(A):(B))
-#define TIFFmin(A,B) ((A)<(B)?(A):(B))
+#define TIFFmax(A, B) ((A) > (B) ? (A) : (B))
+#define TIFFmin(A, B) ((A) < (B) ? (A) : (B))
 
-#define TIFFArrayCount(a) (sizeof (a) / sizeof ((a)[0]))
+#define TIFFArrayCount(a) (sizeof(a) / sizeof((a)[0]))
 
 /*
   Support for large files.
@@ -300,28 +327,31 @@
   must be available on the target computer in order for the program to run.
 */
 #if defined(HAVE_FSEEKO)
-#  define fseek(stream,offset,whence)  fseeko(stream,offset,whence)
-#  define ftell(stream,offset,whence)  ftello(stream,offset,whence)
+#define fseek(stream, offset, whence) fseeko(stream, offset, whence)
+#define ftell(stream, offset, whence) ftello(stream, offset, whence)
 #endif
 #endif
-#if defined(__WIN32__) && \
-        !(defined(_MSC_VER) && _MSC_VER < 1400) && \
-        !(defined(__MSVCRT_VERSION__) && __MSVCRT_VERSION__ < 0x800)
+#if defined(__WIN32__) && !(defined(_MSC_VER) && _MSC_VER < 1400) &&           \
+    !(defined(__MSVCRT_VERSION__) && __MSVCRT_VERSION__ < 0x800)
 typedef unsigned int TIFFIOSize_t;
-#define _TIFF_lseek_f(fildes,offset,whence)  _lseeki64(fildes,/* __int64 */ offset,whence)
+#define _TIFF_lseek_f(fildes, offset, whence)                                  \
+    _lseeki64(fildes, /* __int64 */ offset, whence)
 /* #define _TIFF_tell_f(fildes) /\* __int64 *\/ _telli64(fildes) */
-#define _TIFF_fseek_f(stream,offset,whence) _fseeki64(stream,/* __int64 */ offset,whence)
-#define _TIFF_fstat_f(fildes,stat_buff) _fstati64(fildes,/* struct _stati64 */ stat_buff)
+#define _TIFF_fseek_f(stream, offset, whence)                                  \
+    _fseeki64(stream, /* __int64 */ offset, whence)
+#define _TIFF_fstat_f(fildes, stat_buff)                                       \
+    _fstati64(fildes, /* struct _stati64 */ stat_buff)
 /* #define _TIFF_ftell_f(stream) /\* __int64 *\/ _ftelli64(stream) */
-/* #define _TIFF_stat_f(path,stat_buff) _stati64(path,/\* struct _stati64 *\/ stat_buff) */
+/* #define _TIFF_stat_f(path,stat_buff) _stati64(path,/\* struct _stati64 *\/
+ * stat_buff) */
 #define _TIFF_stat_s struct _stati64
 #define _TIFF_off_t __int64
 #else
 typedef size_t TIFFIOSize_t;
-#define _TIFF_lseek_f(fildes,offset,whence) lseek(fildes,offset,whence)
+#define _TIFF_lseek_f(fildes, offset, whence) lseek(fildes, offset, whence)
 /* #define _TIFF_tell_f(fildes) (_TIFF_lseek_f(fildes,0,SEEK_CUR)) */
-#define _TIFF_fseek_f(stream,offset,whence) fseek(stream,offset,whence)
-#define _TIFF_fstat_f(fildes,stat_buff) fstat(fildes,stat_buff)
+#define _TIFF_fseek_f(stream, offset, whence) fseek(stream, offset, whence)
+#define _TIFF_fstat_f(fildes, stat_buff) fstat(fildes, stat_buff)
 /* #define _TIFF_ftell_f(stream) ftell(stream) */
 /* #define _TIFF_stat_f(path,stat_buff) stat(path,stat_buff) */
 #define _TIFF_stat_s struct stat
@@ -330,7 +360,8 @@
 
 #if defined(__has_attribute) && defined(__clang__)
 #if __has_attribute(no_sanitize)
-#define TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW __attribute__((no_sanitize("unsigned-integer-overflow")))
+#define TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW                                  \
+    __attribute__((no_sanitize("unsigned-integer-overflow")))
 #else
 #define TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
 #endif
@@ -338,136 +369,155 @@
 #define TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
 #endif
 
-
 #if defined(__cplusplus)
-extern "C" {
+extern "C"
+{
 #endif
-extern int _TIFFgetMode(const char* mode, const char* module);
-extern int _TIFFNoRowEncode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s);
-extern int _TIFFNoStripEncode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s);
-extern int _TIFFNoTileEncode(TIFF*, uint8* pp, tmsize_t cc, uint16 s);
-extern int _TIFFNoRowDecode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s);
-extern int _TIFFNoStripDecode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s);
-extern int _TIFFNoTileDecode(TIFF*, uint8* pp, tmsize_t cc, uint16 s);
-extern void _TIFFNoPostDecode(TIFF* tif, uint8* buf, tmsize_t cc);
-extern int _TIFFNoPreCode(TIFF* tif, uint16 s);
-extern int _TIFFNoSeek(TIFF* tif, uint32 off);
-extern void _TIFFSwab16BitData(TIFF* tif, uint8* buf, tmsize_t cc);
-extern void _TIFFSwab24BitData(TIFF* tif, uint8* buf, tmsize_t cc);
-extern void _TIFFSwab32BitData(TIFF* tif, uint8* buf, tmsize_t cc);
-extern void _TIFFSwab64BitData(TIFF* tif, uint8* buf, tmsize_t cc);
-extern int TIFFFlushData1(TIFF* tif);
-extern int TIFFDefaultDirectory(TIFF* tif);
-extern void _TIFFSetDefaultCompressionState(TIFF* tif);
-extern int _TIFFRewriteField(TIFF *, uint16, TIFFDataType, tmsize_t, void *);
-extern int TIFFSetCompressionScheme(TIFF* tif, int scheme);
-extern int TIFFSetDefaultCompressionState(TIFF* tif);
-extern uint32 _TIFFDefaultStripSize(TIFF* tif, uint32 s);
-extern void _TIFFDefaultTileSize(TIFF* tif, uint32* tw, uint32* th);
-extern int _TIFFDataSize(TIFFDataType type);
+    extern int _TIFFgetMode(TIFFOpenOptions *opts, thandle_t clientdata,
+                            const char *mode, const char *module);
+    extern int _TIFFNoRowEncode(TIFF *tif, uint8_t *pp, tmsize_t cc,
+                                uint16_t s);
+    extern int _TIFFNoStripEncode(TIFF *tif, uint8_t *pp, tmsize_t cc,
+                                  uint16_t s);
+    extern int _TIFFNoTileEncode(TIFF *, uint8_t *pp, tmsize_t cc, uint16_t s);
+    extern int _TIFFNoRowDecode(TIFF *tif, uint8_t *pp, tmsize_t cc,
+                                uint16_t s);
+    extern int _TIFFNoStripDecode(TIFF *tif, uint8_t *pp, tmsize_t cc,
+                                  uint16_t s);
+    extern int _TIFFNoTileDecode(TIFF *, uint8_t *pp, tmsize_t cc, uint16_t s);
+    extern void _TIFFNoPostDecode(TIFF *tif, uint8_t *buf, tmsize_t cc);
+    extern int _TIFFNoPreCode(TIFF *tif, uint16_t s);
+    extern int _TIFFNoSeek(TIFF *tif, uint32_t off);
+    extern void _TIFFSwab16BitData(TIFF *tif, uint8_t *buf, tmsize_t cc);
+    extern void _TIFFSwab24BitData(TIFF *tif, uint8_t *buf, tmsize_t cc);
+    extern void _TIFFSwab32BitData(TIFF *tif, uint8_t *buf, tmsize_t cc);
+    extern void _TIFFSwab64BitData(TIFF *tif, uint8_t *buf, tmsize_t cc);
+    extern int TIFFFlushData1(TIFF *tif);
+    extern int TIFFDefaultDirectory(TIFF *tif);
+    extern void _TIFFSetDefaultCompressionState(TIFF *tif);
+    extern int _TIFFRewriteField(TIFF *, uint16_t, TIFFDataType, tmsize_t,
+                                 void *);
+    extern int TIFFSetCompressionScheme(TIFF *tif, int scheme);
+    extern int TIFFSetDefaultCompressionState(TIFF *tif);
+    extern uint32_t _TIFFDefaultStripSize(TIFF *tif, uint32_t s);
+    extern void _TIFFDefaultTileSize(TIFF *tif, uint32_t *tw, uint32_t *th);
 
-extern void _TIFFsetByteArray(void**, void*, uint32);
-extern void _TIFFsetString(char**, char*);
-extern void _TIFFsetShortArray(uint16**, uint16*, uint32);
-extern void _TIFFsetLongArray(uint32**, uint32*, uint32);
-extern void _TIFFsetFloatArray(float**, float*, uint32);
-extern void _TIFFsetDoubleArray(double**, double*, uint32);
+    extern void _TIFFsetByteArray(void **, const void *, uint32_t);
+    extern void _TIFFsetByteArrayExt(TIFF *, void **, const void *, uint32_t);
+    extern void _TIFFsetShortArray(uint16_t **, const uint16_t *, uint32_t);
+    extern void _TIFFsetShortArrayExt(TIFF *, uint16_t **, const uint16_t *,
+                                      uint32_t);
+    extern void _TIFFsetLongArray(uint32_t **, const uint32_t *, uint32_t);
+    extern void _TIFFsetLongArrayExt(TIFF *, uint32_t **, const uint32_t *,
+                                     uint32_t);
+    extern void _TIFFsetFloatArray(float **, const float *, uint32_t);
+    extern void _TIFFsetFloatArrayExt(TIFF *, float **, const float *,
+                                      uint32_t);
+    extern void _TIFFsetDoubleArray(double **, const double *, uint32_t);
+    extern void _TIFFsetDoubleArrayExt(TIFF *, double **, const double *,
+                                       uint32_t);
 
-extern void _TIFFprintAscii(FILE*, const char*);
-extern void _TIFFprintAsciiTag(FILE*, const char*, const char*);
+    extern void _TIFFprintAscii(FILE *, const char *);
+    extern void _TIFFprintAsciiTag(FILE *, const char *, const char *);
 
-extern TIFFErrorHandler _TIFFwarningHandler;
-extern TIFFErrorHandler _TIFFerrorHandler;
-extern TIFFErrorHandlerExt _TIFFwarningHandlerExt;
-extern TIFFErrorHandlerExt _TIFFerrorHandlerExt;
+    extern TIFFErrorHandler _TIFFwarningHandler;
+    extern TIFFErrorHandler _TIFFerrorHandler;
+    extern TIFFErrorHandlerExt _TIFFwarningHandlerExt;
+    extern TIFFErrorHandlerExt _TIFFerrorHandlerExt;
+    void _TIFFErrorEarly(TIFFOpenOptions *opts, thandle_t clientdata,
+                         const char *module, const char *fmt, ...)
+        TIFF_ATTRIBUTE((__format__(__printf__, 4, 5)));
 
-extern uint32 _TIFFMultiply32(TIFF*, uint32, uint32, const char*);
-extern uint64 _TIFFMultiply64(TIFF*, uint64, uint64, const char*);
-extern tmsize_t _TIFFMultiplySSize(TIFF*, tmsize_t, tmsize_t, const char*);
-extern tmsize_t _TIFFCastUInt64ToSSize(TIFF*, uint64, const char*);
-extern void* _TIFFCheckMalloc(TIFF*, tmsize_t, tmsize_t, const char*);
-extern void* _TIFFCheckRealloc(TIFF*, void*, tmsize_t, tmsize_t, const char*);
+    extern uint32_t _TIFFMultiply32(TIFF *, uint32_t, uint32_t, const char *);
+    extern uint64_t _TIFFMultiply64(TIFF *, uint64_t, uint64_t, const char *);
+    extern tmsize_t _TIFFMultiplySSize(TIFF *, tmsize_t, tmsize_t,
+                                       const char *);
+    extern tmsize_t _TIFFCastUInt64ToSSize(TIFF *, uint64_t, const char *);
+    extern void *_TIFFCheckMalloc(TIFF *, tmsize_t, tmsize_t, const char *);
+    extern void *_TIFFCheckRealloc(TIFF *, void *, tmsize_t, tmsize_t,
+                                   const char *);
 
-extern double _TIFFUInt64ToDouble(uint64);
-extern float _TIFFUInt64ToFloat(uint64);
+    extern double _TIFFUInt64ToDouble(uint64_t);
+    extern float _TIFFUInt64ToFloat(uint64_t);
 
-extern float _TIFFClampDoubleToFloat(double);
+    extern float _TIFFClampDoubleToFloat(double);
+    extern uint32_t _TIFFClampDoubleToUInt32(double);
 
-extern tmsize_t
-_TIFFReadEncodedStripAndAllocBuffer(TIFF* tif, uint32 strip,
-                                    void **buf, tmsize_t bufsizetoalloc,
-                                    tmsize_t size_to_read);
-extern tmsize_t
-_TIFFReadEncodedTileAndAllocBuffer(TIFF* tif, uint32 tile,
-                                    void **buf, tmsize_t bufsizetoalloc,
-                                    tmsize_t size_to_read);
-extern tmsize_t
-_TIFFReadTileAndAllocBuffer(TIFF* tif,
-                            void **buf, tmsize_t bufsizetoalloc,
-                            uint32 x, uint32 y, uint32 z, uint16 s);
-extern int _TIFFSeekOK(TIFF* tif, toff_t off);
+    extern void _TIFFCleanupIFDOffsetAndNumberMaps(TIFF *tif);
 
-extern int TIFFInitDumpMode(TIFF*, int);
+    extern tmsize_t _TIFFReadEncodedStripAndAllocBuffer(TIFF *tif,
+                                                        uint32_t strip,
+                                                        void **buf,
+                                                        tmsize_t bufsizetoalloc,
+                                                        tmsize_t size_to_read);
+    extern tmsize_t _TIFFReadEncodedTileAndAllocBuffer(TIFF *tif, uint32_t tile,
+                                                       void **buf,
+                                                       tmsize_t bufsizetoalloc,
+                                                       tmsize_t size_to_read);
+    extern tmsize_t _TIFFReadTileAndAllocBuffer(TIFF *tif, void **buf,
+                                                tmsize_t bufsizetoalloc,
+                                                uint32_t x, uint32_t y,
+                                                uint32_t z, uint16_t s);
+    extern int _TIFFSeekOK(TIFF *tif, toff_t off);
+
+    extern int TIFFInitDumpMode(TIFF *, int);
 #ifdef PACKBITS_SUPPORT
-extern int TIFFInitPackBits(TIFF*, int);
+    extern int TIFFInitPackBits(TIFF *, int);
 #endif
 #ifdef CCITT_SUPPORT
-extern int TIFFInitCCITTRLE(TIFF*, int), TIFFInitCCITTRLEW(TIFF*, int);
-extern int TIFFInitCCITTFax3(TIFF*, int), TIFFInitCCITTFax4(TIFF*, int);
+    extern int TIFFInitCCITTRLE(TIFF *, int), TIFFInitCCITTRLEW(TIFF *, int);
+    extern int TIFFInitCCITTFax3(TIFF *, int), TIFFInitCCITTFax4(TIFF *, int);
 #endif
 #ifdef THUNDER_SUPPORT
-extern int TIFFInitThunderScan(TIFF*, int);
+    extern int TIFFInitThunderScan(TIFF *, int);
 #endif
 #ifdef NEXT_SUPPORT
-extern int TIFFInitNeXT(TIFF*, int);
+    extern int TIFFInitNeXT(TIFF *, int);
 #endif
 #ifdef LZW_SUPPORT
-extern int TIFFInitLZW(TIFF*, int);
+    extern int TIFFInitLZW(TIFF *, int);
 #endif
 #ifdef OJPEG_SUPPORT
-extern int TIFFInitOJPEG(TIFF*, int);
+    extern int TIFFInitOJPEG(TIFF *, int);
 #endif
 #ifdef JPEG_SUPPORT
-extern int TIFFInitJPEG(TIFF*, int);
-extern int TIFFJPEGIsFullStripRequired(TIFF*);
+    extern int TIFFInitJPEG(TIFF *, int);
+    extern int TIFFJPEGIsFullStripRequired(TIFF *);
 #endif
 #ifdef JBIG_SUPPORT
-extern int TIFFInitJBIG(TIFF*, int);
+    extern int TIFFInitJBIG(TIFF *, int);
 #endif
 #ifdef ZIP_SUPPORT
-extern int TIFFInitZIP(TIFF*, int);
+    extern int TIFFInitZIP(TIFF *, int);
 #endif
 #ifdef PIXARLOG_SUPPORT
-extern int TIFFInitPixarLog(TIFF*, int);
+    extern int TIFFInitPixarLog(TIFF *, int);
 #endif
 #ifdef LOGLUV_SUPPORT
-extern int TIFFInitSGILog(TIFF*, int);
+    extern int TIFFInitSGILog(TIFF *, int);
+#endif
+#ifdef LERC_SUPPORT
+    extern int TIFFInitLERC(TIFF *tif, int);
 #endif
 #ifdef LZMA_SUPPORT
-extern int TIFFInitLZMA(TIFF*, int);
+    extern int TIFFInitLZMA(TIFF *, int);
 #endif
 #ifdef ZSTD_SUPPORT
-extern int TIFFInitZSTD(TIFF*, int);
+    extern int TIFFInitZSTD(TIFF *, int);
 #endif
 #ifdef WEBP_SUPPORT
-extern int TIFFInitWebP(TIFF*, int);
+    extern int TIFFInitWebP(TIFF *, int);
 #endif
-#ifdef VMS
-extern const TIFFCodec _TIFFBuiltinCODECS[];
-#else
-extern TIFFCodec _TIFFBuiltinCODECS[];
-#endif
+    extern const TIFFCodec _TIFFBuiltinCODECS[];
+    extern void TIFFCIELab16ToXYZ(TIFFCIELabToRGB *, uint32_t l, int32_t a,
+                                  int32_t b, float *, float *, float *);
+
+    extern void *_TIFFmallocExt(TIFF *tif, tmsize_t s);
+    extern void *_TIFFcallocExt(TIFF *tif, tmsize_t nmemb, tmsize_t siz);
+    extern void *_TIFFreallocExt(TIFF *tif, void *p, tmsize_t s);
+    extern void _TIFFfreeExt(TIFF *tif, void *p);
 
 #if defined(__cplusplus)
 }
 #endif
 #endif /* _TIFFIOP_ */
-
-/* vim: set ts=8 sts=8 sw=8 noet: */
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/libtiff/tiffvers.h b/third_party/libtiff/tiffvers.h
index aa3f613..ed84776 100644
--- a/third_party/libtiff/tiffvers.h
+++ b/third_party/libtiff/tiffvers.h
@@ -1,4 +1,9 @@
-#define TIFFLIB_VERSION_STR "LIBTIFF, Version 4.1.0\nCopyright (c) 1988-1996 Sam Leffler\nCopyright (c) 1991-1996 Silicon Graphics, Inc."
+/* clang-format off */
+
+/* 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.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
@@ -6,4 +11,20 @@
  * version checking should be done based on the
  * string returned by TIFFGetVersion.
  */
-#define TIFFLIB_VERSION 20191103
+#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 1
+
+/* Macro added in 4.5.0. Returns TRUE if the current libtiff version is
+ * greater or equal to major.minor.micro
+ */
+#define TIFFLIB_AT_LEAST(major, minor, micro) \
+    (TIFFLIB_MAJOR_VERSION > (major) || \
+     (TIFFLIB_MAJOR_VERSION == (major) && TIFFLIB_MINOR_VERSION > (minor)) || \
+     (TIFFLIB_MAJOR_VERSION == (major) && TIFFLIB_MINOR_VERSION == (minor) && \
+      TIFFLIB_MICRO_VERSION >= (micro)))
+
+/* clang-format on */
diff --git a/third_party/libtiff/uvcode.h b/third_party/libtiff/uvcode.h
index 6286cfb..fc87729 100644
--- a/third_party/libtiff/uvcode.h
+++ b/third_party/libtiff/uvcode.h
@@ -1,180 +1,93 @@
 /* Version 1.0 generated April 7, 1997 by Greg Ward Larson, SGI */
-#define UV_SQSIZ	(float)0.003500
-#define UV_NDIVS	16289
-#define UV_VSTART	(float)0.016940
-#define UV_NVS		163
-static const struct {
-	float	ustart;
-	short	nus, ncum;
-}	uv_row[UV_NVS] = {
-	{ (float)0.247663,	4,	0 },
-	{ (float)0.243779,	6,	4 },
-	{ (float)0.241684,	7,	10 },
-	{ (float)0.237874,	9,	17 },
-	{ (float)0.235906,	10,	26 },
-	{ (float)0.232153,	12,	36 },
-	{ (float)0.228352,	14,	48 },
-	{ (float)0.226259,	15,	62 },
-	{ (float)0.222371,	17,	77 },
-	{ (float)0.220410,	18,	94 },
-	{ (float)0.214710,	21,	112 },
-	{ (float)0.212714,	22,	133 },
-	{ (float)0.210721,	23,	155 },
-	{ (float)0.204976,	26,	178 },
-	{ (float)0.202986,	27,	204 },
-	{ (float)0.199245,	29,	231 },
-	{ (float)0.195525,	31,	260 },
-	{ (float)0.193560,	32,	291 },
-	{ (float)0.189878,	34,	323 },
-	{ (float)0.186216,	36,	357 },
-	{ (float)0.186216,	36,	393 },
-	{ (float)0.182592,	38,	429 },
-	{ (float)0.179003,	40,	467 },
-	{ (float)0.175466,	42,	507 },
-	{ (float)0.172001,	44,	549 },
-	{ (float)0.172001,	44,	593 },
-	{ (float)0.168612,	46,	637 },
-	{ (float)0.168612,	46,	683 },
-	{ (float)0.163575,	49,	729 },
-	{ (float)0.158642,	52,	778 },
-	{ (float)0.158642,	52,	830 },
-	{ (float)0.158642,	52,	882 },
-	{ (float)0.153815,	55,	934 },
-	{ (float)0.153815,	55,	989 },
-	{ (float)0.149097,	58,	1044 },
-	{ (float)0.149097,	58,	1102 },
-	{ (float)0.142746,	62,	1160 },
-	{ (float)0.142746,	62,	1222 },
-	{ (float)0.142746,	62,	1284 },
-	{ (float)0.138270,	65,	1346 },
-	{ (float)0.138270,	65,	1411 },
-	{ (float)0.138270,	65,	1476 },
-	{ (float)0.132166,	69,	1541 },
-	{ (float)0.132166,	69,	1610 },
-	{ (float)0.126204,	73,	1679 },
-	{ (float)0.126204,	73,	1752 },
-	{ (float)0.126204,	73,	1825 },
-	{ (float)0.120381,	77,	1898 },
-	{ (float)0.120381,	77,	1975 },
-	{ (float)0.120381,	77,	2052 },
-	{ (float)0.120381,	77,	2129 },
-	{ (float)0.112962,	82,	2206 },
-	{ (float)0.112962,	82,	2288 },
-	{ (float)0.112962,	82,	2370 },
-	{ (float)0.107450,	86,	2452 },
-	{ (float)0.107450,	86,	2538 },
-	{ (float)0.107450,	86,	2624 },
-	{ (float)0.107450,	86,	2710 },
-	{ (float)0.100343,	91,	2796 },
-	{ (float)0.100343,	91,	2887 },
-	{ (float)0.100343,	91,	2978 },
-	{ (float)0.095126,	95,	3069 },
-	{ (float)0.095126,	95,	3164 },
-	{ (float)0.095126,	95,	3259 },
-	{ (float)0.095126,	95,	3354 },
-	{ (float)0.088276,	100,	3449 },
-	{ (float)0.088276,	100,	3549 },
-	{ (float)0.088276,	100,	3649 },
-	{ (float)0.088276,	100,	3749 },
-	{ (float)0.081523,	105,	3849 },
-	{ (float)0.081523,	105,	3954 },
-	{ (float)0.081523,	105,	4059 },
-	{ (float)0.081523,	105,	4164 },
-	{ (float)0.074861,	110,	4269 },
-	{ (float)0.074861,	110,	4379 },
-	{ (float)0.074861,	110,	4489 },
-	{ (float)0.074861,	110,	4599 },
-	{ (float)0.068290,	115,	4709 },
-	{ (float)0.068290,	115,	4824 },
-	{ (float)0.068290,	115,	4939 },
-	{ (float)0.068290,	115,	5054 },
-	{ (float)0.063573,	119,	5169 },
-	{ (float)0.063573,	119,	5288 },
-	{ (float)0.063573,	119,	5407 },
-	{ (float)0.063573,	119,	5526 },
-	{ (float)0.057219,	124,	5645 },
-	{ (float)0.057219,	124,	5769 },
-	{ (float)0.057219,	124,	5893 },
-	{ (float)0.057219,	124,	6017 },
-	{ (float)0.050985,	129,	6141 },
-	{ (float)0.050985,	129,	6270 },
-	{ (float)0.050985,	129,	6399 },
-	{ (float)0.050985,	129,	6528 },
-	{ (float)0.050985,	129,	6657 },
-	{ (float)0.044859,	134,	6786 },
-	{ (float)0.044859,	134,	6920 },
-	{ (float)0.044859,	134,	7054 },
-	{ (float)0.044859,	134,	7188 },
-	{ (float)0.040571,	138,	7322 },
-	{ (float)0.040571,	138,	7460 },
-	{ (float)0.040571,	138,	7598 },
-	{ (float)0.040571,	138,	7736 },
-	{ (float)0.036339,	142,	7874 },
-	{ (float)0.036339,	142,	8016 },
-	{ (float)0.036339,	142,	8158 },
-	{ (float)0.036339,	142,	8300 },
-	{ (float)0.032139,	146,	8442 },
-	{ (float)0.032139,	146,	8588 },
-	{ (float)0.032139,	146,	8734 },
-	{ (float)0.032139,	146,	8880 },
-	{ (float)0.027947,	150,	9026 },
-	{ (float)0.027947,	150,	9176 },
-	{ (float)0.027947,	150,	9326 },
-	{ (float)0.023739,	154,	9476 },
-	{ (float)0.023739,	154,	9630 },
-	{ (float)0.023739,	154,	9784 },
-	{ (float)0.023739,	154,	9938 },
-	{ (float)0.019504,	158,	10092 },
-	{ (float)0.019504,	158,	10250 },
-	{ (float)0.019504,	158,	10408 },
-	{ (float)0.016976,	161,	10566 },
-	{ (float)0.016976,	161,	10727 },
-	{ (float)0.016976,	161,	10888 },
-	{ (float)0.016976,	161,	11049 },
-	{ (float)0.012639,	165,	11210 },
-	{ (float)0.012639,	165,	11375 },
-	{ (float)0.012639,	165,	11540 },
-	{ (float)0.009991,	168,	11705 },
-	{ (float)0.009991,	168,	11873 },
-	{ (float)0.009991,	168,	12041 },
-	{ (float)0.009016,	170,	12209 },
-	{ (float)0.009016,	170,	12379 },
-	{ (float)0.009016,	170,	12549 },
-	{ (float)0.006217,	173,	12719 },
-	{ (float)0.006217,	173,	12892 },
-	{ (float)0.005097,	175,	13065 },
-	{ (float)0.005097,	175,	13240 },
-	{ (float)0.005097,	175,	13415 },
-	{ (float)0.003909,	177,	13590 },
-	{ (float)0.003909,	177,	13767 },
-	{ (float)0.002340,	177,	13944 },
-	{ (float)0.002389,	170,	14121 },
-	{ (float)0.001068,	164,	14291 },
-	{ (float)0.001653,	157,	14455 },
-	{ (float)0.000717,	150,	14612 },
-	{ (float)0.001614,	143,	14762 },
-	{ (float)0.000270,	136,	14905 },
-	{ (float)0.000484,	129,	15041 },
-	{ (float)0.001103,	123,	15170 },
-	{ (float)0.001242,	115,	15293 },
-	{ (float)0.001188,	109,	15408 },
-	{ (float)0.001011,	103,	15517 },
-	{ (float)0.000709,	97,	15620 },
-	{ (float)0.000301,	89,	15717 },
-	{ (float)0.002416,	82,	15806 },
-	{ (float)0.003251,	76,	15888 },
-	{ (float)0.003246,	69,	15964 },
-	{ (float)0.004141,	62,	16033 },
-	{ (float)0.005963,	55,	16095 },
-	{ (float)0.008839,	47,	16150 },
-	{ (float)0.010490,	40,	16197 },
-	{ (float)0.016994,	31,	16237 },
-	{ (float)0.023659,	21,	16268 },
+#define UV_SQSIZ (float)0.003500
+#define UV_NDIVS 16289
+#define UV_VSTART (float)0.016940
+#define UV_NVS 163
+static const struct
+{
+    float ustart;
+    short nus, ncum;
+} uv_row[UV_NVS] = {
+    {(float)0.247663, 4, 0},       {(float)0.243779, 6, 4},
+    {(float)0.241684, 7, 10},      {(float)0.237874, 9, 17},
+    {(float)0.235906, 10, 26},     {(float)0.232153, 12, 36},
+    {(float)0.228352, 14, 48},     {(float)0.226259, 15, 62},
+    {(float)0.222371, 17, 77},     {(float)0.220410, 18, 94},
+    {(float)0.214710, 21, 112},    {(float)0.212714, 22, 133},
+    {(float)0.210721, 23, 155},    {(float)0.204976, 26, 178},
+    {(float)0.202986, 27, 204},    {(float)0.199245, 29, 231},
+    {(float)0.195525, 31, 260},    {(float)0.193560, 32, 291},
+    {(float)0.189878, 34, 323},    {(float)0.186216, 36, 357},
+    {(float)0.186216, 36, 393},    {(float)0.182592, 38, 429},
+    {(float)0.179003, 40, 467},    {(float)0.175466, 42, 507},
+    {(float)0.172001, 44, 549},    {(float)0.172001, 44, 593},
+    {(float)0.168612, 46, 637},    {(float)0.168612, 46, 683},
+    {(float)0.163575, 49, 729},    {(float)0.158642, 52, 778},
+    {(float)0.158642, 52, 830},    {(float)0.158642, 52, 882},
+    {(float)0.153815, 55, 934},    {(float)0.153815, 55, 989},
+    {(float)0.149097, 58, 1044},   {(float)0.149097, 58, 1102},
+    {(float)0.142746, 62, 1160},   {(float)0.142746, 62, 1222},
+    {(float)0.142746, 62, 1284},   {(float)0.138270, 65, 1346},
+    {(float)0.138270, 65, 1411},   {(float)0.138270, 65, 1476},
+    {(float)0.132166, 69, 1541},   {(float)0.132166, 69, 1610},
+    {(float)0.126204, 73, 1679},   {(float)0.126204, 73, 1752},
+    {(float)0.126204, 73, 1825},   {(float)0.120381, 77, 1898},
+    {(float)0.120381, 77, 1975},   {(float)0.120381, 77, 2052},
+    {(float)0.120381, 77, 2129},   {(float)0.112962, 82, 2206},
+    {(float)0.112962, 82, 2288},   {(float)0.112962, 82, 2370},
+    {(float)0.107450, 86, 2452},   {(float)0.107450, 86, 2538},
+    {(float)0.107450, 86, 2624},   {(float)0.107450, 86, 2710},
+    {(float)0.100343, 91, 2796},   {(float)0.100343, 91, 2887},
+    {(float)0.100343, 91, 2978},   {(float)0.095126, 95, 3069},
+    {(float)0.095126, 95, 3164},   {(float)0.095126, 95, 3259},
+    {(float)0.095126, 95, 3354},   {(float)0.088276, 100, 3449},
+    {(float)0.088276, 100, 3549},  {(float)0.088276, 100, 3649},
+    {(float)0.088276, 100, 3749},  {(float)0.081523, 105, 3849},
+    {(float)0.081523, 105, 3954},  {(float)0.081523, 105, 4059},
+    {(float)0.081523, 105, 4164},  {(float)0.074861, 110, 4269},
+    {(float)0.074861, 110, 4379},  {(float)0.074861, 110, 4489},
+    {(float)0.074861, 110, 4599},  {(float)0.068290, 115, 4709},
+    {(float)0.068290, 115, 4824},  {(float)0.068290, 115, 4939},
+    {(float)0.068290, 115, 5054},  {(float)0.063573, 119, 5169},
+    {(float)0.063573, 119, 5288},  {(float)0.063573, 119, 5407},
+    {(float)0.063573, 119, 5526},  {(float)0.057219, 124, 5645},
+    {(float)0.057219, 124, 5769},  {(float)0.057219, 124, 5893},
+    {(float)0.057219, 124, 6017},  {(float)0.050985, 129, 6141},
+    {(float)0.050985, 129, 6270},  {(float)0.050985, 129, 6399},
+    {(float)0.050985, 129, 6528},  {(float)0.050985, 129, 6657},
+    {(float)0.044859, 134, 6786},  {(float)0.044859, 134, 6920},
+    {(float)0.044859, 134, 7054},  {(float)0.044859, 134, 7188},
+    {(float)0.040571, 138, 7322},  {(float)0.040571, 138, 7460},
+    {(float)0.040571, 138, 7598},  {(float)0.040571, 138, 7736},
+    {(float)0.036339, 142, 7874},  {(float)0.036339, 142, 8016},
+    {(float)0.036339, 142, 8158},  {(float)0.036339, 142, 8300},
+    {(float)0.032139, 146, 8442},  {(float)0.032139, 146, 8588},
+    {(float)0.032139, 146, 8734},  {(float)0.032139, 146, 8880},
+    {(float)0.027947, 150, 9026},  {(float)0.027947, 150, 9176},
+    {(float)0.027947, 150, 9326},  {(float)0.023739, 154, 9476},
+    {(float)0.023739, 154, 9630},  {(float)0.023739, 154, 9784},
+    {(float)0.023739, 154, 9938},  {(float)0.019504, 158, 10092},
+    {(float)0.019504, 158, 10250}, {(float)0.019504, 158, 10408},
+    {(float)0.016976, 161, 10566}, {(float)0.016976, 161, 10727},
+    {(float)0.016976, 161, 10888}, {(float)0.016976, 161, 11049},
+    {(float)0.012639, 165, 11210}, {(float)0.012639, 165, 11375},
+    {(float)0.012639, 165, 11540}, {(float)0.009991, 168, 11705},
+    {(float)0.009991, 168, 11873}, {(float)0.009991, 168, 12041},
+    {(float)0.009016, 170, 12209}, {(float)0.009016, 170, 12379},
+    {(float)0.009016, 170, 12549}, {(float)0.006217, 173, 12719},
+    {(float)0.006217, 173, 12892}, {(float)0.005097, 175, 13065},
+    {(float)0.005097, 175, 13240}, {(float)0.005097, 175, 13415},
+    {(float)0.003909, 177, 13590}, {(float)0.003909, 177, 13767},
+    {(float)0.002340, 177, 13944}, {(float)0.002389, 170, 14121},
+    {(float)0.001068, 164, 14291}, {(float)0.001653, 157, 14455},
+    {(float)0.000717, 150, 14612}, {(float)0.001614, 143, 14762},
+    {(float)0.000270, 136, 14905}, {(float)0.000484, 129, 15041},
+    {(float)0.001103, 123, 15170}, {(float)0.001242, 115, 15293},
+    {(float)0.001188, 109, 15408}, {(float)0.001011, 103, 15517},
+    {(float)0.000709, 97, 15620},  {(float)0.000301, 89, 15717},
+    {(float)0.002416, 82, 15806},  {(float)0.003251, 76, 15888},
+    {(float)0.003246, 69, 15964},  {(float)0.004141, 62, 16033},
+    {(float)0.005963, 55, 16095},  {(float)0.008839, 47, 16150},
+    {(float)0.010490, 40, 16197},  {(float)0.016994, 31, 16237},
+    {(float)0.023659, 21, 16268},
 };
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 8
- * fill-column: 78
- * End:
- */
diff --git a/third_party/ninja/README.pdfium b/third_party/ninja/README.pdfium
new file mode 100644
index 0000000..8a5f1ee
--- /dev/null
+++ b/third_party/ninja/README.pdfium
@@ -0,0 +1,18 @@
+Name: ninja
+Short Name: ninja
+URL: https://github.com/ninja-build/ninja
+Revision: See the CIPD version in DEPS
+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
+this project.
+
+The CIPD packages are built using the 3pp pipeline.
+https://chromium.googlesource.com/infra/infra/+/refs/heads/main/3pp/ninja/
+
+Local Modifications:
+None
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 774974e..0000000
--- a/third_party/skia_shared/SkFloatToDecimal.cpp
+++ /dev/null
@@ -1,182 +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 <cfloat>
-#include <climits>
-#include <cmath>
-
-//#include "SkTypes.h"
-#include <cassert>
-#define SkASSERT assert
-
-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 {
-                SkASSERT(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;
-    }
-    SkASSERT(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);
-    SkASSERT(value * power <= (double)INT_MAX);
-    int d = static_cast<int>(value * power + 0.5);
-    // SkASSERT(value == (float)(d * pow(10.0, decimalShift)));
-    SkASSERT(d <= 999999999);
-    if (d > 167772159) {  // floor(pow(10,1+log10(1<<24)))
-       // need one fewer decimal digits for 24-bit precision.
-       decimalShift = decimalExponent - 7;
-       // SkASSERT(power * 0.1 = pow10(-decimalShift));
-       // recalculate to get rounding right.
-       d = static_cast<int>(value * (power * 0.1) + 0.5);
-       SkASSERT(d <= 99999999);
-    }
-    while (d % 10 == 0) {
-        d /= 10;
-        ++decimalShift;
-    }
-    SkASSERT(d > 0);
-    // SkASSERT(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);
-    SkASSERT(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.
-            }
-        }
-    }
-    SkASSERT(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/third_party/yasm/BUILD.gn b/third_party/yasm/BUILD.gn
deleted file mode 100644
index c6543a5..0000000
--- a/third_party/yasm/BUILD.gn
+++ /dev/null
@@ -1,524 +0,0 @@
-# Copyright 2014 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.
-
-# The yasm build process creates a slew of small C subprograms that
-# dynamically generate files at various point in the build process.  This makes
-# the build integration moderately complex.
-#
-# There are three classes of dynamically generated files:
-#   1) C source files that should be included in the build (eg., lc3bid.c)
-#   2) C source files that are #included by static C sources (eg., license.c)
-#   3) Intermediate files that are used as input by other subprograms to
-#      further generate files in category #1 or #2.  (eg., version.mac)
-#
-# This structure is represented with the following targets:
-#   1) yasm -- Sources, flags for the main yasm executable. Also has most of
-#              of the actions and rules that invoke the subprograms.
-#   2) yasm_config -- General build configuration including setting a
-#              inputs listing the checked in version of files
-#              generated by manually running configure. These manually
-#              generated files are used by all binaries.
-#   3) yasm_utils -- Object files with memory management and hashing utilities
-#              shared between yasm and the genperf subprogram.
-#   4) genmacro, genmodule, etc. -- One executable target for each subprogram.
-#   5) generate_license, generate_module, etc. -- Actions that invoke programs
-#              built in #4 to generate .c files.
-#   6) compile_gperf, compile_re2c, etc. -- Actions that invoke programs that
-#              turn intermediate files into .c files.
-
-import("//build/config/compiler/compiler.gni")
-
-configs_to_delete = []
-configs_to_add = []
-if (is_debug) {
-  configs_to_delete += [
-    # Build with full optimizations even on debug configurations, because some
-    # yasm build steps (highbd_sad4d_sse2.asm) can take ~33 seconds or more in
-    # debug component builds on Windows. Enabling compiler optimizations saves
-    #  ~5 seconds.
-    "//build/config/compiler:default_optimization",
-
-    # Don't define _DEBUG. Modest savings, but good for consistency.
-    "//build/config:debug",
-
-    # Don't enable sanitizers for build tools. They slow down the overall build.
-    "//build/config/sanitizers:default_sanitizer_flags",
-  ]
-
-  configs_to_add += [
-    "//build/config:release",
-    "//build/config/compiler:optimize_max",
-  ]
-  if (is_win) {
-    # This switches to using the release CRT. On debug component builds of
-    # highbd_sad4d_sse2.asm on Windows this saves about 15 s.
-    configs_to_delete += [ "//build/config/win:default_crt" ]
-    configs_to_add += [ "//build/config/win:release_crt" ]
-
-    # Without no_default_deps, an implicit dependency on libc++ is added.
-    # libc++ may have been built referencing the debug CRT, but since we're
-    # explicitly using the release CRT, this would result in undefined symbol
-    # errors when linking, so we need to remove the implicit libc++ dependency.
-    no_default_deps = true
-  }
-}
-
-if (current_toolchain == host_toolchain) {
-  # Various files referenced by multiple targets. yasm_gen_include_dir was moved
-  # from $target_gen_dir/include to avoid conflicts with x86insn_gas.c and
-  # x86insn_nasm.c. These files were previously generated during the build but
-  # are now shipped pre-generated by yasm.
-  yasm_gen_include_dir = "$target_gen_dir/gen_include"
-  config_makefile = "source/config/Makefile"
-  version_file = "version.mac"
-
-  import("//build/compiled_action.gni")
-
-  config("yasm_config") {
-    configs = [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-    include_dirs = [
-      "source/config/$host_os",
-      "source/patched-yasm",
-    ]
-    defines = [ "HAVE_CONFIG_H" ]
-    if (is_posix || is_fuchsia) {
-      cflags = [ "-std=gnu99" ]
-    }
-  }
-
-  executable("genmacro") {
-    sources = [ "source/patched-yasm/tools/genmacro/genmacro.c" ]
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      ":yasm_config",
-      "//build/config/compiler:no_chromium_code",
-    ]
-    deps = [
-      # Default manifest on Windows (a no-op elsewhere).
-      "//build/win:default_exe_manifest",
-    ]
-  }
-
-  executable("genmodule") {
-    sources = [ "source/patched-yasm/libyasm/genmodule.c" ]
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      ":yasm_config",
-      "//build/config/compiler:no_chromium_code",
-    ]
-    deps = [
-      # Default manifest on Windows (a no-op elsewhere).
-      "//build/win:default_exe_manifest",
-    ]
-  }
-
-  executable("genperf") {
-    sources = [
-      "source/patched-yasm/tools/genperf/genperf.c",
-      "source/patched-yasm/tools/genperf/perfect.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      ":yasm_config",
-      "//build/config/compiler:no_chromium_code",
-    ]
-
-    # Must be compatible with yasm_utils/yasm
-    configs -= configs_to_delete
-    configs += configs_to_add
-
-    deps = [
-      ":yasm_utils",
-
-      # Default manifest on Windows (a no-op elsewhere).
-      "//build/win:default_exe_manifest",
-    ]
-  }
-
-  # Used by both yasm and genperf binaries.
-  static_library("yasm_utils") {
-    sources = [
-      "source/patched-yasm/libyasm/phash.c",
-      "source/patched-yasm/libyasm/xmalloc.c",
-      "source/patched-yasm/libyasm/xstrdup.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      ":yasm_config",
-      "//build/config/compiler:no_chromium_code",
-    ]
-
-    # Must be compatible with yasm
-    configs -= configs_to_delete
-    configs += configs_to_add
-  }
-
-  executable("genstring") {
-    sources = [ "source/patched-yasm/genstring.c" ]
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      ":yasm_config",
-      "//build/config/compiler:no_chromium_code",
-    ]
-    deps = [
-      # Default manifest on Windows (a no-op elsewhere).
-      "//build/win:default_exe_manifest",
-    ]
-  }
-
-  executable("genversion") {
-    sources = [ "source/patched-yasm/modules/preprocs/nasm/genversion.c" ]
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      ":yasm_config",
-      "//build/config/compiler:no_chromium_code",
-    ]
-    deps = [
-      # Default manifest on Windows (a no-op elsewhere).
-      "//build/win:default_exe_manifest",
-    ]
-  }
-
-  config("re2c_warnings") {
-    # re2c is missing CLOSEVOP from one switch.
-    if (is_clang) {
-      cflags = [
-        # re2c is missing CLOSEVOP from one switch.
-        "-Wno-switch",
-
-        # re2c contains many static functions in headers (because it's
-        # a C library predating C99.)
-        "-Wno-unused-function",
-      ]
-    }
-  }
-
-  executable("re2c") {
-    sources = [
-      "source/patched-yasm/tools/re2c/actions.c",
-      "source/patched-yasm/tools/re2c/code.c",
-      "source/patched-yasm/tools/re2c/dfa.c",
-      "source/patched-yasm/tools/re2c/main.c",
-      "source/patched-yasm/tools/re2c/mbo_getopt.c",
-      "source/patched-yasm/tools/re2c/parser.c",
-      "source/patched-yasm/tools/re2c/scanner.c",
-      "source/patched-yasm/tools/re2c/substr.c",
-      "source/patched-yasm/tools/re2c/translate.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      ":yasm_config",
-      "//build/config/compiler:no_chromium_code",
-
-      # Must be after no_chromium_code for warning flags to be ordered
-      # correctly.
-      ":re2c_warnings",
-    ]
-    deps = [
-      # Default manifest on Windows (a no-op elsewhere).
-      "//build/win:default_exe_manifest",
-    ]
-  }
-
-  config("yasm_warnings") {
-    if (is_clang) {
-      cflags = [
-        # reg3264type in x86expr.c is unused.
-        "-Wno-unused-local-typedef",
-      ]
-    } else if (is_linux) {
-      cflags = [
-        # dosexe_objfmt_output ignores the return value of ftruncate.
-        "-Wno-unused-result",
-      ]
-    }
-  }
-
-  executable("yasm") {
-    sources = [
-      "source/patched-yasm/frontends/yasm/yasm-options.c",
-      "source/patched-yasm/frontends/yasm/yasm.c",
-      "source/patched-yasm/libyasm/assocdat.c",
-      "source/patched-yasm/libyasm/bc-align.c",
-      "source/patched-yasm/libyasm/bc-data.c",
-      "source/patched-yasm/libyasm/bc-incbin.c",
-      "source/patched-yasm/libyasm/bc-org.c",
-      "source/patched-yasm/libyasm/bc-reserve.c",
-      "source/patched-yasm/libyasm/bitvect.c",
-      "source/patched-yasm/libyasm/bytecode.c",
-      "source/patched-yasm/libyasm/errwarn.c",
-      "source/patched-yasm/libyasm/expr.c",
-      "source/patched-yasm/libyasm/file.c",
-      "source/patched-yasm/libyasm/floatnum.c",
-      "source/patched-yasm/libyasm/hamt.c",
-      "source/patched-yasm/libyasm/insn.c",
-      "source/patched-yasm/libyasm/intnum.c",
-      "source/patched-yasm/libyasm/inttree.c",
-      "source/patched-yasm/libyasm/linemap.c",
-      "source/patched-yasm/libyasm/md5.c",
-      "source/patched-yasm/libyasm/mergesort.c",
-      "source/patched-yasm/libyasm/section.c",
-      "source/patched-yasm/libyasm/strcasecmp.c",
-      "source/patched-yasm/libyasm/strsep.c",
-      "source/patched-yasm/libyasm/symrec.c",
-      "source/patched-yasm/libyasm/valparam.c",
-      "source/patched-yasm/libyasm/value.c",
-      "source/patched-yasm/modules/arch/lc3b/lc3barch.c",
-      "source/patched-yasm/modules/arch/lc3b/lc3bbc.c",
-      "source/patched-yasm/modules/arch/x86/x86arch.c",
-      "source/patched-yasm/modules/arch/x86/x86bc.c",
-      "source/patched-yasm/modules/arch/x86/x86expr.c",
-      "source/patched-yasm/modules/arch/x86/x86id.c",
-      "source/patched-yasm/modules/dbgfmts/codeview/cv-dbgfmt.c",
-      "source/patched-yasm/modules/dbgfmts/codeview/cv-symline.c",
-      "source/patched-yasm/modules/dbgfmts/codeview/cv-type.c",
-      "source/patched-yasm/modules/dbgfmts/dwarf2/dwarf2-aranges.c",
-      "source/patched-yasm/modules/dbgfmts/dwarf2/dwarf2-dbgfmt.c",
-      "source/patched-yasm/modules/dbgfmts/dwarf2/dwarf2-info.c",
-      "source/patched-yasm/modules/dbgfmts/dwarf2/dwarf2-line.c",
-      "source/patched-yasm/modules/dbgfmts/null/null-dbgfmt.c",
-      "source/patched-yasm/modules/dbgfmts/stabs/stabs-dbgfmt.c",
-      "source/patched-yasm/modules/listfmts/nasm/nasm-listfmt.c",
-      "source/patched-yasm/modules/objfmts/bin/bin-objfmt.c",
-      "source/patched-yasm/modules/objfmts/coff/coff-objfmt.c",
-      "source/patched-yasm/modules/objfmts/coff/win64-except.c",
-      "source/patched-yasm/modules/objfmts/dbg/dbg-objfmt.c",
-      "source/patched-yasm/modules/objfmts/elf/elf-objfmt.c",
-      "source/patched-yasm/modules/objfmts/elf/elf-x86-amd64.c",
-      "source/patched-yasm/modules/objfmts/elf/elf-x86-x32.c",
-      "source/patched-yasm/modules/objfmts/elf/elf-x86-x86.c",
-      "source/patched-yasm/modules/objfmts/elf/elf.c",
-      "source/patched-yasm/modules/objfmts/macho/macho-objfmt.c",
-      "source/patched-yasm/modules/objfmts/rdf/rdf-objfmt.c",
-      "source/patched-yasm/modules/objfmts/xdf/xdf-objfmt.c",
-      "source/patched-yasm/modules/parsers/gas/gas-parse-intel.c",
-      "source/patched-yasm/modules/parsers/gas/gas-parse.c",
-      "source/patched-yasm/modules/parsers/gas/gas-parser.c",
-      "source/patched-yasm/modules/parsers/nasm/nasm-parse.c",
-      "source/patched-yasm/modules/parsers/nasm/nasm-parser.c",
-      "source/patched-yasm/modules/preprocs/cpp/cpp-preproc.c",
-      "source/patched-yasm/modules/preprocs/gas/gas-eval.c",
-      "source/patched-yasm/modules/preprocs/gas/gas-preproc.c",
-      "source/patched-yasm/modules/preprocs/nasm/nasm-eval.c",
-      "source/patched-yasm/modules/preprocs/nasm/nasm-pp.c",
-      "source/patched-yasm/modules/preprocs/nasm/nasm-preproc.c",
-      "source/patched-yasm/modules/preprocs/nasm/nasmlib.c",
-      "source/patched-yasm/modules/preprocs/raw/raw-preproc.c",
-
-      # Files generated by compile_gperf
-      "$target_gen_dir/x86cpu.c",
-      "$target_gen_dir/x86regtmod.c",
-
-      # Files generated by compile_re2c
-      "$target_gen_dir/gas-token.c",
-      "$target_gen_dir/nasm-token.c",
-
-      # File generated by compile_re2c_lc3b
-      "$target_gen_dir/lc3bid.c",
-
-      # File generated by generate_module
-      "$target_gen_dir/module.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      ":yasm_config",
-      "//build/config/compiler:no_chromium_code",
-      "//build/config/compiler:no_incompatible_pointer_warnings",
-
-      # Must be after no_chromium_code for warning flags to be ordered
-      # correctly.
-      ":yasm_warnings",
-    ]
-
-    # Disable WPO for yasm: crbug.com/604808
-    if (is_official_build && full_wpo_on_official) {
-      configs -= [ "//build/config/compiler:default_optimization" ]
-      configs += [ "//build/config/compiler:optimize_no_wpo" ]
-    } else {
-      configs -= configs_to_delete
-      configs += configs_to_add
-    }
-
-    # Yasm generates a bunch of .c files which its source file #include. These
-    # are placed in |yasm_gen_include_dir|.
-    include_dirs = [ yasm_gen_include_dir ]
-
-    if (!is_win) {
-      cflags = [
-        "-std=c89",
-        "-pedantic",
-      ]
-    }
-
-    # TODO(ajwong): This should take most of the generated output as
-    # inputs.
-    deps = [
-      ":compile_gperf",
-      ":compile_gperf_for_include",
-      ":compile_nasm_macros",
-      ":compile_nasm_version",
-      ":compile_re2c",
-      ":compile_re2c_lc3b",
-      ":compile_win64_gas",
-      ":compile_win64_nasm",
-      ":generate_license",
-      ":generate_module",
-      ":generate_version",
-      ":yasm_utils",
-
-      # Default manifest on Windows (a no-op elsewhere).
-      "//build/win:default_exe_manifest",
-    ]
-  }
-
-  compiled_action_foreach("compile_gperf") {
-    tool = ":genperf"
-    sources = [
-      "source/patched-yasm/modules/arch/x86/x86cpu.gperf",
-      "source/patched-yasm/modules/arch/x86/x86regtmod.gperf",
-    ]
-
-    outputs = [ "$target_gen_dir/{{source_name_part}}.c" ]
-    args = [
-      "{{source}}",
-      rebase_path(target_gen_dir, root_build_dir) + "/{{source_name_part}}.c",
-    ]
-  }
-
-  # This differs from |compile_gperf| in where it places it output files.
-  compiled_action_foreach("compile_gperf_for_include") {
-    tool = ":genperf"
-    sources = [
-      # Make sure the generated gperf files in $target_gen_dir are synced with
-      # the outputs for the related generate_*_insn actions in the
-      # generate_files target below.
-      #
-      # The output for these two are #included by
-      #   source/patched-yasm/modules/arch/x86/x86id.c
-      "source/patched-yasm/x86insn_gas.gperf",
-      "source/patched-yasm/x86insn_nasm.gperf",
-    ]
-
-    outputs = [ "$yasm_gen_include_dir/{{source_name_part}}.c" ]
-    args = [
-      "{{source}}",
-      rebase_path(yasm_gen_include_dir, root_build_dir) +
-          "/{{source_name_part}}.c",
-    ]
-  }
-
-  template("compile_macro") {
-    compiled_action(target_name) {
-      tool = ":genmacro"
-
-      # Output #included by source/patched-yasm/frontends/yasm/yasm.c.
-      inputs = invoker.sources
-      outputs = invoker.outputs
-      args = [
-        rebase_path(outputs[0], root_build_dir),
-        invoker.macro_varname,
-        rebase_path(inputs[0], root_build_dir),
-      ]
-      if (defined(invoker.deps)) {
-        deps = invoker.deps
-      }
-    }
-  }
-
-  compile_macro("compile_nasm_macros") {
-    # Output #included by
-    #   source/patched-yasm/modules/preprocs/nasm/nasm-parser.c
-    sources = [ "source/patched-yasm/modules/parsers/nasm/nasm-std.mac" ]
-    outputs = [ "$yasm_gen_include_dir/nasm-macros.c" ]
-    macro_varname = "nasm_standard_mac"
-  }
-
-  compile_macro("compile_nasm_version") {
-    # Output #included by
-    #   source/patched-yasm/modules/preprocs/nasm/nasm-preproc.c
-    sources = [ "$target_gen_dir/$version_file" ]
-    outputs = [ "$yasm_gen_include_dir/nasm-version.c" ]
-    macro_varname = "nasm_version_mac"
-    deps = [ ":generate_version" ]
-  }
-
-  compile_macro("compile_win64_gas") {
-    # Output #included by source/patched-yasm/frontends/yasm/yasm.c.
-    sources = [ "source/patched-yasm/modules/objfmts/coff/win64-gas.mac" ]
-    outputs = [ "$yasm_gen_include_dir/win64-gas.c" ]
-    macro_varname = "win64_gas_stdmac"
-  }
-
-  compile_macro("compile_win64_nasm") {
-    # Output #included by source/patched-yasm/frontends/yasm/yasm.c.
-    sources = [ "source/patched-yasm/modules/objfmts/coff/win64-nasm.mac" ]
-    outputs = [ "$yasm_gen_include_dir/win64-nasm.c" ]
-    macro_varname = "win64_nasm_stdmac"
-  }
-
-  compiled_action_foreach("compile_re2c") {
-    tool = ":re2c"
-    sources = [
-      "source/patched-yasm/modules/parsers/gas/gas-token.re",
-      "source/patched-yasm/modules/parsers/nasm/nasm-token.re",
-    ]
-    outputs = [ "$target_gen_dir/{{source_name_part}}.c" ]
-    args = [
-      "-b",
-      "-o",
-      rebase_path(target_gen_dir, root_build_dir) + "/{{source_name_part}}.c",
-      "{{source}}",
-    ]
-  }
-
-  # This call doesn't fit into the re2c template above.
-  compiled_action("compile_re2c_lc3b") {
-    tool = ":re2c"
-    inputs = [ "source/patched-yasm/modules/arch/lc3b/lc3bid.re" ]
-    outputs = [ "$target_gen_dir/lc3bid.c" ]
-    args = [
-      "-s",
-      "-o",
-      rebase_path(outputs[0], root_build_dir),
-      rebase_path(inputs[0], root_build_dir),
-    ]
-  }
-
-  compiled_action("generate_license") {
-    tool = ":genstring"
-
-    # Output #included by source/patched-yasm/frontends/yasm/yasm.c.
-    inputs = [ "source/patched-yasm/COPYING" ]
-    outputs = [ "$yasm_gen_include_dir/license.c" ]
-    args = [
-      "license_msg",
-      rebase_path(outputs[0], root_build_dir),
-      rebase_path(inputs[0], root_build_dir),
-    ]
-  }
-
-  compiled_action("generate_module") {
-    tool = ":genmodule"
-    inputs = [
-      "source/patched-yasm/libyasm/module.in",
-      config_makefile,
-    ]
-    outputs = [ "$target_gen_dir/module.c" ]
-    args = [
-      rebase_path(inputs[0], root_build_dir),
-      rebase_path(config_makefile, root_build_dir),
-      rebase_path(outputs[0], root_build_dir),
-    ]
-  }
-
-  compiled_action("generate_version") {
-    tool = ":genversion"
-    outputs = [ "$target_gen_dir/$version_file" ]
-    args = [ rebase_path(outputs[0], root_build_dir) ]
-  }
-}
diff --git a/third_party/yasm/CHROMIUM.diff b/third_party/yasm/CHROMIUM.diff
deleted file mode 100644
index 2ff0a6b..0000000
--- a/third_party/yasm/CHROMIUM.diff
+++ /dev/null
@@ -1,102 +0,0 @@
-diff --git a/frontends/tasm/tasm.c b/frontends/tasm/tasm.c
-index 58954b6..568f478 100644
---- a/frontends/tasm/tasm.c
-+++ b/frontends/tasm/tasm.c
-@@ -228,7 +228,6 @@ static opt_option options[] =
- /* version message */
- /*@observer@*/ static const char *version_msg[] = {
-     PACKAGE_STRING,
--    "Compiled on " __DATE__ ".",
-     "Copyright (c) 2001-2010 Peter Johnson and other Yasm developers.",
-     "Run yasm --license for licensing overview and summary."
- };
-diff --git a/frontends/yasm/yasm.c b/frontends/yasm/yasm.c
-index ff4c59e..015ae8d 100644
---- a/frontends/yasm/yasm.c
-+++ b/frontends/yasm/yasm.c
-@@ -217,7 +217,6 @@ static opt_option options[] =
- /* version message */
- /*@observer@*/ static const char *version_msg[] = {
-     PACKAGE_STRING,
--    "Compiled on " __DATE__ ".",
-     "Copyright (c) 2001-2014 Peter Johnson and other Yasm developers.",
-     "Run yasm --license for licensing overview and summary."
- };
-diff --git a/libyasm/genmodule.c b/libyasm/genmodule.c
-index 867d93a..027e2fe 100644
---- a/libyasm/genmodule.c
-+++ b/libyasm/genmodule.c
-@@ -58,9 +58,14 @@ main(int argc, char *argv[])
-     include *inc;
-     int isam = 0;
-     int linecont = 0;
--
--    if (argc != 3) {
--        fprintf(stderr, "Usage: %s <module.in> <Makefile[.am]>\n", argv[0]);
-+    char *outfile;
-+
-+    if (argc == 3) {
-+        outfile = OUTPUT;
-+    } else if (argc == 4) {
-+        outfile = argv[3];
-+    } else {
-+        fprintf(stderr, "Usage: %s <module.in> <Makefile[.am]> [<outfile>]\n", argv[0]);
-         return EXIT_FAILURE;
-     }
- 
-@@ -167,10 +172,10 @@ keepgoing:
-         fclose(in);
-     }
- 
--    out = fopen(OUTPUT, "wt");
-+    out = fopen(outfile, "wt");
- 
-     if (!out) {
--        fprintf(stderr, "Could not open `%s'.\n", OUTPUT);
-+        fprintf(stderr, "Could not open `%s'.\n", outfile);
-         return EXIT_FAILURE;
-     }
- 
-@@ -181,7 +186,7 @@ keepgoing:
-     if (!in) {
-         fprintf(stderr, "Could not open `%s'.\n", argv[1]);
-         fclose(out);
--        remove(OUTPUT);
-+        remove(outfile);
-         return EXIT_FAILURE;
-     }
- 
-diff --git a/tools/genperf/perfect.c b/tools/genperf/perfect.c
-index a9a14c0..e45f9c5 100644
---- a/tools/genperf/perfect.c
-+++ b/tools/genperf/perfect.c
-@@ -563,7 +563,7 @@ static int perfect(
-         if (!augment(tabb, tabh, tabq, blen, scramble, smax, &tabb[i], nkeys, 
-                      i+1, form))
-         {
--          fprintf(stderr, "fail to map group of size %ld for tab size %ld\n", j, blen);
-+          /* Do not print an error. The caller may retry with a larger table. */
-           return FALSE;
-         }
- 
-diff --git a/tools/re2c/parser.c b/tools/re2c/parser.c
-index 02d5c66..b3882af 100644
---- a/tools/re2c/parser.c
-+++ b/tools/re2c/parser.c
-@@ -226,15 +226,9 @@ void line_source(FILE *o, unsigned int line)
- }
- 
- void parse(FILE *i, FILE *o){
--    time_t now;
--
--    time(&now);
--
-     peektok = NONE;
- 
--    fputs("/* Generated by re2c 0.9.1-C on ", o);
--    fprintf(o, "%-24s", ctime(&now));
--    fputs(" */\n", o); oline+=2;
-+    fputs("/* Generated by re2c 0.9.1-C */\n", o); oline++;
- 
-     in = Scanner_new(i);
- 
diff --git a/third_party/yasm/README.pdfium b/third_party/yasm/README.pdfium
deleted file mode 100644
index 41da312..0000000
--- a/third_party/yasm/README.pdfium
+++ /dev/null
@@ -1,126 +0,0 @@
-Name: yasm
-URL: http://www.tortall.net/projects/yasm/
-Version: 1.3.0
-License: 2-clause or 3-clause BSD licensed, with the exception of bitvect, which is triple-licensed under the Artistic license, GPL, and LGPL
-License File: source/patched-yasm/COPYING
-License Android Compatible: yes
-Security Critical: no
-
-Source: http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz
-SHA-512: 572d3b45568b10f58e48f1188c2d6bcbdd16429c8afaccc8c6d37859b45635e1
-         06885d679e41d0bee78c23822108c7ae75aa7475eed5ba58057e0a6fe1b68645
-
-With these patches applied:
-* CHROMIUM.diff: Combined patch from Chromium.
-  See Chromium's third_party/yasm/README.chromium for details.
-
-
-See also the BUILD.gn file for a description of the yasm build process.
-
-Instructions for recreating the BUILD.gn file.
-  1) Update yasm and re-apply the patches.
-
-  2) Make a copy of source in a different directory (e.g., /tmp/yasm_build) and
-     run configure. Using another directory will keep the source tree clean. An
-     out-of-tree build does not appear to work reliably as of yasm 1.3.0.
-
-  3) Next, capture all the output from a build of yasm.  We will use the build
-     log as a reference for BUILD.gn.
-
-       make yasm > yasm_build_log 2> yasm_build_err
-
-  4) Check yasm_build_err to see if there are any anomalies beyond yasm's
-     compiler warnings.
-
-  5) Grab the generated libyasm-stdint.h and config.h and put into the correct
-     platform location.
-
-       src/third_party/yasm/source/config/[platform]
-
-     For android platform, copy the files generated for linux, but make sure
-     that ENABLE_NLS is not defined to allow mac host compiles to work.  For
-     ios, copy the files from mac.  For win, copy the libyasm-stdint.h from
-     linux and fix up config.h.
-
-     Find the YASM_MODULES line in the generated Makefile and update
-     src/third_party/yasm/source/config/Makefile. It is needed by the
-     "genmodule" subprogram as input for creating the available modules list.
-
-  6) Make sure all the subprograms are represented in BUILD.gn.
-
-       grep -w gcc yasm_build_log  |
-       grep -v ' -DHAVE_CONFIG_H '
-
-     The yasm build creates a bunch of subprograms that in-turn generate
-     more .c files in the build. Luckily the commands to generate the
-     subprogram do not have -DHAVE_CONFIG_H as a cflag.
-
-     From this list, make sure all the subprograms that are build have
-     appropriate targets in the BUILD.gn.
-
-     You will notice, when you get to the next step, that there are some
-     .c source files that are compiled both for yasm, and for genperf.
-
-     Those should go into the yasm_utils target so that they can be shared by
-     the genperf and yasm targets. Find the files used by genperf by appending
-
-       | grep 'gp-'
-
-     to the command above. Then grep for them without the 'gp-' prefix to see if
-     they are used in yasm as well.
-
-  7) Find all the source files used to build yasm proper.
-
-       grep -w gcc yasm_build_log  |
-       grep ' -DHAVE_CONFIG_H ' |
-       sed -e 's/[&\\]*$//' |  # Remove any trailing '&&'s and '\'s.
-       awk '{print $NF }' |
-       sed -e "s/'\.\/'\`//" |  # Removes some garbage from the build line.
-       sort -u |
-       sed -e 's/\(.*\)/      "source\/patched-yasm\/\1",/'
-
-     Reversing the -DHAVE_CONFIG_H filter from the command above should
-     list the compile lines for yasm proper.
-
-     This should get you close, but you will need to manually examine this
-     list.  However, some of the built products are still included in the
-     command above.  Generally, if the source file is in the root directory,
-     it's a generated file.  Also remove the sources in the yasm_utils target.
-
-     Inspect the current BUILD.gn for a list of the subprograms and their
-     outputs.
-
-     Update the sources list in the yasm target accordingly.  Read step #9
-     as well if you update the source list to avoid problems.
-
-  8) Update the actions for each of the subprograms.
-
-     Here is the real fun.  For each subprogram created, you will need to
-     update the actions and rules in BUILD.gn that invoke the subprogram to
-     generate the files needed by the rest of the build.
-
-     I don't have any good succinct instructions for this.  Grep the build
-     log for each subprogram invocation (eg., "./genversion"), look at
-     its command inputs and output, then verify our BUILD.gn does something
-     similar.
-
-     The good news is things likely only link or compile if this is done
-     right so you'll know if there is a problem.
-
-     Again, refer to the existing BUILD.gn for a guide to how the generated
-     files are used.
-
-     Here are a few gotchas:
-       1) genmodule, by default, writes module.c into the current
-          directory.  This does not play nicely with gn.  We have a patch
-          to allow specifying a specific output file.
-
-       2) Most of the generated files, even though they are .c files, are
-          #included by other files in the build.  Make sure they end up
-          in yasm_gen_include_dir.
-
-       3) Some of the genperf output is #included while others need to be
-          compiled directly.  That is why there are 2 different rules for
-          .gperf files in two targets.
-
-  9) If all that's is finished, attempt to build....and cross your fingers.
diff --git a/third_party/yasm/run_yasm.py b/third_party/yasm/run_yasm.py
deleted file mode 100644
index a257295..0000000
--- a/third_party/yasm/run_yasm.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright 2014 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.
-
-"""A wrapper to run yasm.
-
-Its main job is to provide a Python wrapper for GN integration, and to write
-the makefile-style output yasm generates in stdout to a .d file for dependency
-management of .inc files.
-
-Run with:
-  python run_yasm.py <yasm_binary_path> <all other yasm args>
-
-Note that <all other yasm args> must include an explicit output file (-o). This
-script will append a ".d" to this and write the dependencies there. This script
-will add "-M" to cause yasm to write the deps to stdout, so you don't need to
-specify that.
-"""
-
-import argparse
-import os
-import sys
-import subprocess
-
-# Extract the output file name from the yasm command line so we can generate a
-# .d file with the same base name.
-parser = argparse.ArgumentParser()
-parser.add_argument("-o", dest="objfile")
-options, _ = parser.parse_known_args()
-
-objfile = options.objfile
-depfile = objfile + '.d'
-
-# Set up environment for yasm.
-# Setting YASM_TEST_SUITE makes yasm output deterministic:
-# - the PE/COFF timestamp field is always 0 (this breaks link.exe /incremental,
-#   but we no longer user link.exe)
-# - in debug info, yasm identifies itself as "yasm HEAD" instead of e.g.
-#   "yasm 1.3.0" (we don't care much about this effect)
-# - in debug info, file paths are no longer absolute but relative to '.'
-os.environ['YASM_TEST_SUITE'] = '1'
-
-# Assemble.
-result_code = subprocess.call(sys.argv[1:])
-if result_code != 0:
-  sys.exit(result_code)
-
-# Now generate the .d file listing the dependencies. The -M option makes yasm
-# write the Makefile-style dependencies to stdout, but it seems that inhibits
-# generating any compiled output so we need to do this in a separate pass.
-# However, outputting deps seems faster than actually assembling, and yasm is
-# so fast anyway this is not a big deal.
-#
-# This guarantees proper dependency management for assembly files. Otherwise,
-# we would have to require people to manually specify the .inc files they
-# depend on in the build file, which will surely be wrong or out-of-date in
-# some cases.
-deps = subprocess.check_output(sys.argv[1:] + ['-M'])
-with open(depfile, "wb") as f:
-  f.write(deps)
-
diff --git a/third_party/yasm/source/config/Makefile b/third_party/yasm/source/config/Makefile
deleted file mode 100644
index 59dc34d..0000000
--- a/third_party/yasm/source/config/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-# The YASM_MODULES line below is extracted from the Makefile generated by the
-# configure script.
-YASM_MODULES = arch_x86 arch_lc3b listfmt_nasm parser_gas parser_gnu \
-	parser_nasm parser_tasm preproc_nasm preproc_tasm preproc_raw \
-	preproc_cpp preproc_gas dbgfmt_cv8 dbgfmt_dwarf2 dbgfmt_null \
-	dbgfmt_stabs objfmt_dbg objfmt_bin objfmt_dosexe objfmt_elf \
-	objfmt_elf32 objfmt_elf64 objfmt_elfx32 objfmt_coff \
-	objfmt_macho objfmt_macho32 objfmt_macho64 objfmt_rdf \
-	objfmt_win32 objfmt_win64 objfmt_x64 objfmt_xdf
diff --git a/third_party/yasm/source/config/android/config.h b/third_party/yasm/source/config/android/config.h
deleted file mode 100644
index d1b5ab4..0000000
--- a/third_party/yasm/source/config/android/config.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/* config.h.  Generated from config.h.in by configure.  */
-/* config.h.in.  Generated from configure.ac by autoheader.  */
-
-/* Command name to run C preprocessor */
-#define CPP_PROG "cc -E"
-
-/* */
-/* #undef ENABLE_NLS */
-
-/* Define to 1 if you have the `abort' function. */
-#define HAVE_ABORT 1
-
-/* */
-/* #undef HAVE_CATGETS */
-
-/* Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the
-   CoreFoundation framework. */
-/* #undef HAVE_CFLOCALECOPYCURRENT */
-
-/* Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in
-   the CoreFoundation framework. */
-/* #undef HAVE_CFPREFERENCESCOPYAPPVALUE */
-
-/* Define if the GNU dcgettext() function is already present or preinstalled.
-   */
-#define HAVE_DCGETTEXT 1
-
-/* Define to 1 if you have the <direct.h> header file. */
-/* #undef HAVE_DIRECT_H */
-
-/* Define to 1 if you have the `ftruncate' function. */
-#define HAVE_FTRUNCATE 1
-
-/* Define to 1 if you have the `getcwd' function. */
-#define HAVE_GETCWD 1
-
-/* */
-#define HAVE_GETTEXT 1
-
-/* Define to 1 if you have the GNU C Library */
-#define HAVE_GNU_C_LIBRARY 1
-
-/* Define if you have the iconv() function and it works. */
-/* #undef HAVE_ICONV */
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#define HAVE_INTTYPES_H 1
-
-/* */
-/* #undef HAVE_LC_MESSAGES */
-
-/* Define to 1 if you have the <libgen.h> header file. */
-#define HAVE_LIBGEN_H 1
-
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
-/* Define to 1 if you have the `mergesort' function. */
-/* #undef HAVE_MERGESORT */
-
-/* Define to 1 if you have the `popen' function. */
-#define HAVE_POPEN 1
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#define HAVE_STDINT_H 1
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
-
-/* */
-/* #undef HAVE_STPCPY */
-
-/* Define to 1 if you have the `strcasecmp' function. */
-#define HAVE_STRCASECMP 1
-
-/* Define to 1 if you have the `strcmpi' function. */
-/* #undef HAVE_STRCMPI */
-
-/* Define to 1 if you have the `stricmp' function. */
-/* #undef HAVE_STRICMP */
-
-/* Define to 1 if you have the <strings.h> header file. */
-#define HAVE_STRINGS_H 1
-
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H 1
-
-/* Define to 1 if you have the `strncasecmp' function. */
-#define HAVE_STRNCASECMP 1
-
-/* Define to 1 if you have the `strsep' function. */
-#define HAVE_STRSEP 1
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Define to 1 if you have the `toascii' function. */
-#define HAVE_TOASCII 1
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#define HAVE_UNISTD_H 1
-
-/* Define to 1 if you have the `vsnprintf' function. */
-#define HAVE_VSNPRINTF 1
-
-/* Define to 1 if you have the `_stricmp' function. */
-/* #undef HAVE__STRICMP */
-
-/* Name of package */
-#define PACKAGE "yasm"
-
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT "bug-yasm@tortall.net"
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME "yasm"
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "yasm 1.3.0"
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME "yasm"
-
-/* Define to the home page for this package. */
-#define PACKAGE_URL ""
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "1.3.0"
-
-/* Define to 1 if the C compiler supports function prototypes. */
-#define PROTOTYPES 1
-
-/* The size of `char', as computed by sizeof. */
-/* #undef SIZEOF_CHAR */
-
-/* The size of `int', as computed by sizeof. */
-/* #undef SIZEOF_INT */
-
-/* The size of `long', as computed by sizeof. */
-/* #undef SIZEOF_LONG */
-
-/* The size of `short', as computed by sizeof. */
-/* #undef SIZEOF_SHORT */
-
-/* The size of `void*', as computed by sizeof. */
-/* #undef SIZEOF_VOIDP */
-
-/* Define to 1 if you have the ANSI C header files. */
-#define STDC_HEADERS 1
-
-/* Version number of package */
-#define VERSION "1.3.0"
-
-/* Define if using the dmalloc debugging malloc package */
-/* #undef WITH_DMALLOC */
-
-/* Define like PROTOTYPES; this can be used by system headers. */
-#define __PROTOTYPES 1
-
-/* Define to empty if `const' does not conform to ANSI C. */
-/* #undef const */
-
-/* Define to `__inline__' or `__inline' if that's what the C compiler
-   calls it, or to nothing if 'inline' is not supported under any name.  */
-#ifndef __cplusplus
-/* #undef inline */
-#endif
-
-/* Define to `unsigned int' if <sys/types.h> does not define. */
-/* #undef size_t */
diff --git a/third_party/yasm/source/config/android/libyasm-stdint.h b/third_party/yasm/source/config/android/libyasm-stdint.h
deleted file mode 100644
index b9ce696..0000000
--- a/third_party/yasm/source/config/android/libyasm-stdint.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef _YASM_LIBYASM_STDINT_H
-#define _YASM_LIBYASM_STDINT_H 1
-#ifndef _GENERATED_STDINT_H
-#define _GENERATED_STDINT_H "yasm 1.3.0"
-/* generated using gcc -std=gnu99 */
-#define _STDINT_HAVE_STDINT_H 1
-#include <stdint.h>
-#endif
-#endif
diff --git a/third_party/yasm/source/config/ios/config.h b/third_party/yasm/source/config/ios/config.h
deleted file mode 100644
index 01695d6..0000000
--- a/third_party/yasm/source/config/ios/config.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/* config.h.  Generated from config.h.in by configure.  */
-/* config.h.in.  Generated from configure.ac by autoheader.  */
-
-/* Command name to run C preprocessor */
-#define CPP_PROG "cc -E"
-
-/* */
-/* #undef ENABLE_NLS */
-
-/* Define to 1 if you have the `abort' function. */
-#define HAVE_ABORT 1
-
-/* */
-/* #undef HAVE_CATGETS */
-
-/* Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the
-   CoreFoundation framework. */
-#define HAVE_CFLOCALECOPYCURRENT 1
-
-/* Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in
-   the CoreFoundation framework. */
-#define HAVE_CFPREFERENCESCOPYAPPVALUE 1
-
-/* Define if the GNU dcgettext() function is already present or preinstalled.
-   */
-/* #undef HAVE_DCGETTEXT */
-
-/* Define to 1 if you have the <direct.h> header file. */
-/* #undef HAVE_DIRECT_H */
-
-/* Define to 1 if you have the `ftruncate' function. */
-#define HAVE_FTRUNCATE 1
-
-/* Define to 1 if you have the `getcwd' function. */
-#define HAVE_GETCWD 1
-
-/* */
-/* #undef HAVE_GETTEXT */
-
-/* Define to 1 if you have the GNU C Library */
-/* #undef HAVE_GNU_C_LIBRARY */
-
-/* Define if you have the iconv() function and it works. */
-#define HAVE_ICONV 1
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#define HAVE_INTTYPES_H 1
-
-/* */
-/* #undef HAVE_LC_MESSAGES */
-
-/* Define to 1 if you have the <libgen.h> header file. */
-#define HAVE_LIBGEN_H 1
-
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
-/* Define to 1 if you have the `mergesort' function. */
-#define HAVE_MERGESORT 1
-
-/* Define to 1 if you have the `popen' function. */
-#define HAVE_POPEN 1
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#define HAVE_STDINT_H 1
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
-
-/* */
-/* #undef HAVE_STPCPY */
-
-/* Define to 1 if you have the `strcasecmp' function. */
-#define HAVE_STRCASECMP 1
-
-/* Define to 1 if you have the `strcmpi' function. */
-/* #undef HAVE_STRCMPI */
-
-/* Define to 1 if you have the `stricmp' function. */
-/* #undef HAVE_STRICMP */
-
-/* Define to 1 if you have the <strings.h> header file. */
-#define HAVE_STRINGS_H 1
-
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H 1
-
-/* Define to 1 if you have the `strncasecmp' function. */
-#define HAVE_STRNCASECMP 1
-
-/* Define to 1 if you have the `strsep' function. */
-#define HAVE_STRSEP 1
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Define to 1 if you have the `toascii' function. */
-#define HAVE_TOASCII 1
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#define HAVE_UNISTD_H 1
-
-/* Define to 1 if you have the `vsnprintf' function. */
-#define HAVE_VSNPRINTF 1
-
-/* Define to 1 if you have the `_stricmp' function. */
-/* #undef HAVE__STRICMP */
-
-/* Name of package */
-#define PACKAGE "yasm"
-
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT "bug-yasm@tortall.net"
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME "yasm"
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "yasm 1.3.0"
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME "yasm"
-
-/* Define to the home page for this package. */
-#define PACKAGE_URL ""
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "1.3.0"
-
-/* Define to 1 if the C compiler supports function prototypes. */
-#define PROTOTYPES 1
-
-/* The size of `char', as computed by sizeof. */
-/* #undef SIZEOF_CHAR */
-
-/* The size of `int', as computed by sizeof. */
-/* #undef SIZEOF_INT */
-
-/* The size of `long', as computed by sizeof. */
-/* #undef SIZEOF_LONG */
-
-/* The size of `short', as computed by sizeof. */
-/* #undef SIZEOF_SHORT */
-
-/* The size of `void*', as computed by sizeof. */
-/* #undef SIZEOF_VOIDP */
-
-/* Define to 1 if you have the ANSI C header files. */
-#define STDC_HEADERS 1
-
-/* Version number of package */
-#define VERSION "1.3.0"
-
-/* Define if using the dmalloc debugging malloc package */
-/* #undef WITH_DMALLOC */
-
-/* Define like PROTOTYPES; this can be used by system headers. */
-#define __PROTOTYPES 1
-
-/* Define to empty if `const' does not conform to ANSI C. */
-/* #undef const */
-
-/* Define to `__inline__' or `__inline' if that's what the C compiler
-   calls it, or to nothing if 'inline' is not supported under any name.  */
-#ifndef __cplusplus
-/* #undef inline */
-#endif
-
-/* Define to `unsigned int' if <sys/types.h> does not define. */
-/* #undef size_t */
diff --git a/third_party/yasm/source/config/ios/libyasm-stdint.h b/third_party/yasm/source/config/ios/libyasm-stdint.h
deleted file mode 100644
index 5780da8..0000000
--- a/third_party/yasm/source/config/ios/libyasm-stdint.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef _YASM_LIBYASM_STDINT_H
-#define _YASM_LIBYASM_STDINT_H 1
-#ifndef _GENERATED_STDINT_H
-#define _GENERATED_STDINT_H "yasm 1.3.0"
-/* generated using gcc */
-#define _STDINT_HAVE_STDINT_H 1
-#include <stdint.h>
-#endif
-#endif
diff --git a/third_party/yasm/source/config/linux/config.h b/third_party/yasm/source/config/linux/config.h
deleted file mode 100644
index 6ca1f6a..0000000
--- a/third_party/yasm/source/config/linux/config.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/* config.h.  Generated from config.h.in by configure.  */
-/* config.h.in.  Generated from configure.ac by autoheader.  */
-
-/* Command name to run C preprocessor */
-#define CPP_PROG "cc -E"
-
-/* */
-#define ENABLE_NLS 1
-
-/* Define to 1 if you have the `abort' function. */
-#define HAVE_ABORT 1
-
-/* */
-/* #undef HAVE_CATGETS */
-
-/* Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the
-   CoreFoundation framework. */
-/* #undef HAVE_CFLOCALECOPYCURRENT */
-
-/* Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in
-   the CoreFoundation framework. */
-/* #undef HAVE_CFPREFERENCESCOPYAPPVALUE */
-
-/* Define if the GNU dcgettext() function is already present or preinstalled.
-   */
-#define HAVE_DCGETTEXT 1
-
-/* Define to 1 if you have the <direct.h> header file. */
-/* #undef HAVE_DIRECT_H */
-
-/* Define to 1 if you have the `ftruncate' function. */
-#define HAVE_FTRUNCATE 1
-
-/* Define to 1 if you have the `getcwd' function. */
-#define HAVE_GETCWD 1
-
-/* */
-#define HAVE_GETTEXT 1
-
-/* Define to 1 if you have the GNU C Library */
-#define HAVE_GNU_C_LIBRARY 1
-
-/* Define if you have the iconv() function and it works. */
-/* #undef HAVE_ICONV */
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#define HAVE_INTTYPES_H 1
-
-/* */
-/* #undef HAVE_LC_MESSAGES */
-
-/* Define to 1 if you have the <libgen.h> header file. */
-#define HAVE_LIBGEN_H 1
-
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
-/* Define to 1 if you have the `mergesort' function. */
-/* #undef HAVE_MERGESORT */
-
-/* Define to 1 if you have the `popen' function. */
-#define HAVE_POPEN 1
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#define HAVE_STDINT_H 1
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
-
-/* */
-/* #undef HAVE_STPCPY */
-
-/* Define to 1 if you have the `strcasecmp' function. */
-#define HAVE_STRCASECMP 1
-
-/* Define to 1 if you have the `strcmpi' function. */
-/* #undef HAVE_STRCMPI */
-
-/* Define to 1 if you have the `stricmp' function. */
-/* #undef HAVE_STRICMP */
-
-/* Define to 1 if you have the <strings.h> header file. */
-#define HAVE_STRINGS_H 1
-
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H 1
-
-/* Define to 1 if you have the `strncasecmp' function. */
-#define HAVE_STRNCASECMP 1
-
-/* Define to 1 if you have the `strsep' function. */
-#define HAVE_STRSEP 1
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Define to 1 if you have the `toascii' function. */
-#define HAVE_TOASCII 1
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#define HAVE_UNISTD_H 1
-
-/* Define to 1 if you have the `vsnprintf' function. */
-#define HAVE_VSNPRINTF 1
-
-/* Define to 1 if you have the `_stricmp' function. */
-/* #undef HAVE__STRICMP */
-
-/* Name of package */
-#define PACKAGE "yasm"
-
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT "bug-yasm@tortall.net"
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME "yasm"
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "yasm 1.3.0"
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME "yasm"
-
-/* Define to the home page for this package. */
-#define PACKAGE_URL ""
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "1.3.0"
-
-/* Define to 1 if the C compiler supports function prototypes. */
-#define PROTOTYPES 1
-
-/* The size of `char', as computed by sizeof. */
-/* #undef SIZEOF_CHAR */
-
-/* The size of `int', as computed by sizeof. */
-/* #undef SIZEOF_INT */
-
-/* The size of `long', as computed by sizeof. */
-/* #undef SIZEOF_LONG */
-
-/* The size of `short', as computed by sizeof. */
-/* #undef SIZEOF_SHORT */
-
-/* The size of `void*', as computed by sizeof. */
-/* #undef SIZEOF_VOIDP */
-
-/* Define to 1 if you have the ANSI C header files. */
-#define STDC_HEADERS 1
-
-/* Version number of package */
-#define VERSION "1.3.0"
-
-/* Define if using the dmalloc debugging malloc package */
-/* #undef WITH_DMALLOC */
-
-/* Define like PROTOTYPES; this can be used by system headers. */
-#define __PROTOTYPES 1
-
-/* Define to empty if `const' does not conform to ANSI C. */
-/* #undef const */
-
-/* Define to `__inline__' or `__inline' if that's what the C compiler
-   calls it, or to nothing if 'inline' is not supported under any name.  */
-#ifndef __cplusplus
-/* #undef inline */
-#endif
-
-/* Define to `unsigned int' if <sys/types.h> does not define. */
-/* #undef size_t */
diff --git a/third_party/yasm/source/config/linux/libyasm-stdint.h b/third_party/yasm/source/config/linux/libyasm-stdint.h
deleted file mode 100644
index b9ce696..0000000
--- a/third_party/yasm/source/config/linux/libyasm-stdint.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef _YASM_LIBYASM_STDINT_H
-#define _YASM_LIBYASM_STDINT_H 1
-#ifndef _GENERATED_STDINT_H
-#define _GENERATED_STDINT_H "yasm 1.3.0"
-/* generated using gcc -std=gnu99 */
-#define _STDINT_HAVE_STDINT_H 1
-#include <stdint.h>
-#endif
-#endif
diff --git a/third_party/yasm/source/config/mac/config.h b/third_party/yasm/source/config/mac/config.h
deleted file mode 100644
index 01695d6..0000000
--- a/third_party/yasm/source/config/mac/config.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/* config.h.  Generated from config.h.in by configure.  */
-/* config.h.in.  Generated from configure.ac by autoheader.  */
-
-/* Command name to run C preprocessor */
-#define CPP_PROG "cc -E"
-
-/* */
-/* #undef ENABLE_NLS */
-
-/* Define to 1 if you have the `abort' function. */
-#define HAVE_ABORT 1
-
-/* */
-/* #undef HAVE_CATGETS */
-
-/* Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the
-   CoreFoundation framework. */
-#define HAVE_CFLOCALECOPYCURRENT 1
-
-/* Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in
-   the CoreFoundation framework. */
-#define HAVE_CFPREFERENCESCOPYAPPVALUE 1
-
-/* Define if the GNU dcgettext() function is already present or preinstalled.
-   */
-/* #undef HAVE_DCGETTEXT */
-
-/* Define to 1 if you have the <direct.h> header file. */
-/* #undef HAVE_DIRECT_H */
-
-/* Define to 1 if you have the `ftruncate' function. */
-#define HAVE_FTRUNCATE 1
-
-/* Define to 1 if you have the `getcwd' function. */
-#define HAVE_GETCWD 1
-
-/* */
-/* #undef HAVE_GETTEXT */
-
-/* Define to 1 if you have the GNU C Library */
-/* #undef HAVE_GNU_C_LIBRARY */
-
-/* Define if you have the iconv() function and it works. */
-#define HAVE_ICONV 1
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#define HAVE_INTTYPES_H 1
-
-/* */
-/* #undef HAVE_LC_MESSAGES */
-
-/* Define to 1 if you have the <libgen.h> header file. */
-#define HAVE_LIBGEN_H 1
-
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
-/* Define to 1 if you have the `mergesort' function. */
-#define HAVE_MERGESORT 1
-
-/* Define to 1 if you have the `popen' function. */
-#define HAVE_POPEN 1
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#define HAVE_STDINT_H 1
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
-
-/* */
-/* #undef HAVE_STPCPY */
-
-/* Define to 1 if you have the `strcasecmp' function. */
-#define HAVE_STRCASECMP 1
-
-/* Define to 1 if you have the `strcmpi' function. */
-/* #undef HAVE_STRCMPI */
-
-/* Define to 1 if you have the `stricmp' function. */
-/* #undef HAVE_STRICMP */
-
-/* Define to 1 if you have the <strings.h> header file. */
-#define HAVE_STRINGS_H 1
-
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H 1
-
-/* Define to 1 if you have the `strncasecmp' function. */
-#define HAVE_STRNCASECMP 1
-
-/* Define to 1 if you have the `strsep' function. */
-#define HAVE_STRSEP 1
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Define to 1 if you have the `toascii' function. */
-#define HAVE_TOASCII 1
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#define HAVE_UNISTD_H 1
-
-/* Define to 1 if you have the `vsnprintf' function. */
-#define HAVE_VSNPRINTF 1
-
-/* Define to 1 if you have the `_stricmp' function. */
-/* #undef HAVE__STRICMP */
-
-/* Name of package */
-#define PACKAGE "yasm"
-
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT "bug-yasm@tortall.net"
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME "yasm"
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "yasm 1.3.0"
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME "yasm"
-
-/* Define to the home page for this package. */
-#define PACKAGE_URL ""
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "1.3.0"
-
-/* Define to 1 if the C compiler supports function prototypes. */
-#define PROTOTYPES 1
-
-/* The size of `char', as computed by sizeof. */
-/* #undef SIZEOF_CHAR */
-
-/* The size of `int', as computed by sizeof. */
-/* #undef SIZEOF_INT */
-
-/* The size of `long', as computed by sizeof. */
-/* #undef SIZEOF_LONG */
-
-/* The size of `short', as computed by sizeof. */
-/* #undef SIZEOF_SHORT */
-
-/* The size of `void*', as computed by sizeof. */
-/* #undef SIZEOF_VOIDP */
-
-/* Define to 1 if you have the ANSI C header files. */
-#define STDC_HEADERS 1
-
-/* Version number of package */
-#define VERSION "1.3.0"
-
-/* Define if using the dmalloc debugging malloc package */
-/* #undef WITH_DMALLOC */
-
-/* Define like PROTOTYPES; this can be used by system headers. */
-#define __PROTOTYPES 1
-
-/* Define to empty if `const' does not conform to ANSI C. */
-/* #undef const */
-
-/* Define to `__inline__' or `__inline' if that's what the C compiler
-   calls it, or to nothing if 'inline' is not supported under any name.  */
-#ifndef __cplusplus
-/* #undef inline */
-#endif
-
-/* Define to `unsigned int' if <sys/types.h> does not define. */
-/* #undef size_t */
diff --git a/third_party/yasm/source/config/mac/libyasm-stdint.h b/third_party/yasm/source/config/mac/libyasm-stdint.h
deleted file mode 100644
index 5780da8..0000000
--- a/third_party/yasm/source/config/mac/libyasm-stdint.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef _YASM_LIBYASM_STDINT_H
-#define _YASM_LIBYASM_STDINT_H 1
-#ifndef _GENERATED_STDINT_H
-#define _GENERATED_STDINT_H "yasm 1.3.0"
-/* generated using gcc */
-#define _STDINT_HAVE_STDINT_H 1
-#include <stdint.h>
-#endif
-#endif
diff --git a/third_party/yasm/source/config/openbsd/config.h b/third_party/yasm/source/config/openbsd/config.h
deleted file mode 100644
index 4f74288..0000000
--- a/third_party/yasm/source/config/openbsd/config.h
+++ /dev/null
@@ -1,165 +0,0 @@
-/* config.h.  Generated from config.h.in by configure.  */
-/* config.h.in.  Generated from configure.ac by autoheader.  */
-
-/* Command name to run C preprocessor */
-#define CPP_PROG "cc -E"
-
-/* */
-/* #undef ENABLE_NLS */
-
-/* Define to 1 if you have the `abort' function. */
-#define HAVE_ABORT 1
-
-/* */
-/* #undef HAVE_CATGETS */
-
-/* Define if the GNU dcgettext() function is already present or preinstalled.
-   */
-/* #undef HAVE_DCGETTEXT */
-
-/* Define to 1 if you have the <direct.h> header file. */
-/* #undef HAVE_DIRECT_H */
-
-/* Define to 1 if you have the `ftruncate' function. */
-#define HAVE_FTRUNCATE 1
-
-/* Define to 1 if you have the `getcwd' function. */
-#define HAVE_GETCWD 1
-
-/* */
-/* #undef HAVE_GETTEXT */
-
-/* Define to 1 if you have the GNU C Library */
-/* #undef HAVE_GNU_C_LIBRARY */
-
-/* Define if you have the iconv() function and it works. */
-/* #undef HAVE_ICONV */
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#define HAVE_INTTYPES_H 1
-
-/* */
-/* #undef HAVE_LC_MESSAGES */
-
-/* Define to 1 if you have the <libgen.h> header file. */
-#define HAVE_LIBGEN_H 1
-
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
-/* Define to 1 if you have the `mergesort' function. */
-#define HAVE_MERGESORT 1
-
-/* Define to 1 if you have the `popen' function. */
-#define HAVE_POPEN 1
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#define HAVE_STDINT_H 1
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
-
-/* */
-/* #undef HAVE_STPCPY */
-
-/* Define to 1 if you have the `strcasecmp' function. */
-#define HAVE_STRCASECMP 1
-
-/* Define to 1 if you have the `strcmpi' function. */
-/* #undef HAVE_STRCMPI */
-
-/* Define to 1 if you have the `stricmp' function. */
-/* #undef HAVE_STRICMP */
-
-/* Define to 1 if you have the <strings.h> header file. */
-#define HAVE_STRINGS_H 1
-
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H 1
-
-/* Define to 1 if you have the `strncasecmp' function. */
-#define HAVE_STRNCASECMP 1
-
-/* Define to 1 if you have the `strsep' function. */
-#define HAVE_STRSEP 1
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Define to 1 if you have the `toascii' function. */
-#define HAVE_TOASCII 1
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#define HAVE_UNISTD_H 1
-
-/* Define to 1 if you have the `vsnprintf' function. */
-#define HAVE_VSNPRINTF 1
-
-/* Define to 1 if you have the `_stricmp' function. */
-/* #undef HAVE__STRICMP */
-
-/* Name of package */
-#define PACKAGE "yasm"
-
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT "bug-yasm@tortall.net"
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME "yasm"
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "yasm 1.2.0"
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME "yasm"
-
-/* Define to the home page for this package. */
-#define PACKAGE_URL ""
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "1.2.0"
-
-/* Define to 1 if the C compiler supports function prototypes. */
-#define PROTOTYPES 1
-
-/* The size of `char', as computed by sizeof. */
-/* #undef SIZEOF_CHAR */
-
-/* The size of `int', as computed by sizeof. */
-/* #undef SIZEOF_INT */
-
-/* The size of `long', as computed by sizeof. */
-/* #undef SIZEOF_LONG */
-
-/* The size of `short', as computed by sizeof. */
-/* #undef SIZEOF_SHORT */
-
-/* The size of `void*', as computed by sizeof. */
-/* #undef SIZEOF_VOIDP */
-
-/* Define to 1 if you have the ANSI C header files. */
-#define STDC_HEADERS 1
-
-/* Version number of package */
-#define VERSION "1.2.0"
-
-/* Define if using the dmalloc debugging malloc package */
-/* #undef WITH_DMALLOC */
-
-/* Define like PROTOTYPES; this can be used by system headers. */
-#define __PROTOTYPES 1
-
-/* Define to empty if `const' does not conform to ANSI C. */
-/* #undef const */
-
-/* Define to `__inline__' or `__inline' if that's what the C compiler
-   calls it, or to nothing if 'inline' is not supported under any name.  */
-#ifndef __cplusplus
-/* #undef inline */
-#endif
-
-/* Define to `unsigned int' if <sys/types.h> does not define. */
-/* #undef size_t */
diff --git a/third_party/yasm/source/config/openbsd/libyasm-stdint.h b/third_party/yasm/source/config/openbsd/libyasm-stdint.h
deleted file mode 100644
index b875214..0000000
--- a/third_party/yasm/source/config/openbsd/libyasm-stdint.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef _YASM_LIBYASM_STDINT_H
-#define _YASM_LIBYASM_STDINT_H 1
-#ifndef _GENERATED_STDINT_H
-#define _GENERATED_STDINT_H "yasm 1.1.0"
-/* generated using gcc -std=gnu99 */
-#define _STDINT_HAVE_STDINT_H 1
-#include <stdint.h>
-#endif
-#endif
diff --git a/third_party/yasm/source/config/win/config.h b/third_party/yasm/source/config/win/config.h
deleted file mode 100644
index 12c081d..0000000
--- a/third_party/yasm/source/config/win/config.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/* config.h.  Generated from config.h.in by configure.  */
-/* config.h.in.  Generated from configure.ac by autoheader.  */
-
-/* Command name to run C preprocessor */
-#define CPP_PROG "cc -E"
-
-/* */
-/* #undef ENABLE_NLS */
-
-/* Define to 1 if you have the `abort' function. */
-#define HAVE_ABORT 1
-
-/* */
-/* #undef HAVE_CATGETS */
-
-/* Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the
-   CoreFoundation framework. */
-/* #undef HAVE_CFLOCALECOPYCURRENT */
-
-/* Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue i
-   the CoreFoundation framework. */
-/* #undef HAVE_CFPREFERENCESCOPYAPPVALUE */
-
-/* Define if the GNU dcgettext() function is already present or preinstalled.
-   */
-#define HAVE_DCGETTEXT 1
-
-/* Define to 1 if you have the <direct.h> header file. */
-#define HAVE_DIRECT_H 1
-
-/* Define to 1 if you have the `ftruncate' function. */
-/* #undef HAVE_FTRUNCATE */
-
-/* Define to 1 if you have the `getcwd' function. */
-#define HAVE_GETCWD 1
-
-/* */
-#define HAVE_GETTEXT 1
-
-/* Define to 1 if you have the GNU C Library */
-/* #undef HAVE_GNU_C_LIBRARY */
-
-/* Define if you have the iconv() function and it works. */
-/* #undef HAVE_ICONV */
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#define HAVE_INTTYPES_H 1
-
-/* */
-/* #undef HAVE_LC_MESSAGES */
-
-/* Define to 1 if you have the <libgen.h> header file. */
-/* #undef HAVE_LIBGEN_H */
-
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
-/* Define to 1 if you have the `mergesort' function. */
-/* #undef HAVE_MERGESORT */
-
-/* Define to 1 if you have the `popen' function. */
-/* #undef HAVE_POPEN */
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#define HAVE_STDINT_H 1
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
-
-/* */
-/* #undef HAVE_STPCPY */
-
-/* Define to 1 if you have the `strcasecmp' function. */
-/* #undef HAVE_STRCASECMP */
-
-/* Define to 1 if you have the `strcmpi' function. */
-/* #undef HAVE_STRCMPI */
-
-/* Define to 1 if you have the `stricmp' function. */
-/* #undef HAVE_STRICMP */
-
-/* Define to 1 if you have the <strings.h> header file. */
-/* #undef HAVE_STRINGS_H */
-
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H 1
-
-/* Define to 1 if you have the `strncasecmp' function. */
-#define HAVE_STRNCASECMP 1
-
-/* Define to 1 if you have the `strsep' function. */
-/* #undef HAVE_STRSEP */
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Define to 1 if you have the `toascii' function. */
-#define HAVE_TOASCII 1
-
-/* Define to 1 if you have the <unistd.h> header file. */
-/* #undef HAVE_UNISTD_H */
-
-/* Define to 1 if you have the `vsnprintf' function. */
-#define HAVE_VSNPRINTF 1
-
-/* Define to 1 if you have the `_stricmp' function. */
-/* #undef HAVE__STRICMP */
-
-/* Name of package */
-#define PACKAGE "yasm"
-
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT "bug-yasm@tortall.net"
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME "yasm"
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "yasm 1.3.0"
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME "yasm"
-
-/* Define to the home page for this package. */
-#define PACKAGE_URL ""
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "1.3.0"
-
-/* Define to 1 if the C compiler supports function prototypes. */
-#define PROTOTYPES 1
-
-/* The size of `char', as computed by sizeof. */
-/* #undef SIZEOF_CHAR */
-
-/* The size of `int', as computed by sizeof. */
-/* #undef SIZEOF_INT */
-
-/* The size of `long', as computed by sizeof. */
-/* #undef SIZEOF_LONG */
-
-/* The size of `short', as computed by sizeof. */
-/* #undef SIZEOF_SHORT */
-
-/* The size of `void*', as computed by sizeof. */
-/* #undef SIZEOF_VOIDP */
-
-/* Define to 1 if you have the ANSI C header files. */
-#define STDC_HEADERS 1
-
-/* Version number of package */
-#define VERSION "1.3.0"
-
-/* Define if using the dmalloc debugging malloc package */
-/* #undef WITH_DMALLOC */
-
-/* Define like PROTOTYPES; this can be used by system headers. */
-#define __PROTOTYPES 1
-
-/* Define to empty if `const' does not conform to ANSI C. */
-/* #undef const */
-
-/* Define to `__inline__' or `__inline' if that's what the C compiler
-   calls it, or to nothing if 'inline' is not supported under any name.  */
-#ifndef __cplusplus
-/* #undef inline */
-#endif
-
-/* Define to `unsigned int' if <sys/types.h> does not define. */
-/* #undef size_t */
diff --git a/third_party/yasm/source/config/win/libyasm-stdint.h b/third_party/yasm/source/config/win/libyasm-stdint.h
deleted file mode 100644
index b9ce696..0000000
--- a/third_party/yasm/source/config/win/libyasm-stdint.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef _YASM_LIBYASM_STDINT_H
-#define _YASM_LIBYASM_STDINT_H 1
-#ifndef _GENERATED_STDINT_H
-#define _GENERATED_STDINT_H "yasm 1.3.0"
-/* generated using gcc -std=gnu99 */
-#define _STDINT_HAVE_STDINT_H 1
-#include <stdint.h>
-#endif
-#endif
diff --git a/third_party/yasm/yasm_assemble.gni b/third_party/yasm/yasm_assemble.gni
deleted file mode 100644
index f94fa6b..0000000
--- a/third_party/yasm/yasm_assemble.gni
+++ /dev/null
@@ -1,199 +0,0 @@
-# Copyright 2014 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.
-
-# This provides the yasm_assemble() template which uses YASM to assemble
-# assembly files.
-#
-# Files to be assembled with YASM should have an extension of .asm.
-#
-# Parameters
-#
-#   yasm_flags (optional)
-#       [list of strings] Pass additional flags into YASM. These are appended
-#       to the command line. Note that the target machine type and system is
-#       already set up based on the current toolchain so you don't need to
-#       specify these things (see below).
-#
-#       Example: yasm_flags = [ "--force-strict" ]
-#
-#   include_dirs (optional)
-#       [list of dir names] List of additional include dirs. Note that the
-#       source root and the root generated file dir is always added, just like
-#       our C++ build sets up.
-#
-#       Example: include_dirs = [ "//some/other/path", target_gen_dir ]
-#
-#   defines (optional)
-#       [list of strings] List of defines, as with the native code defines.
-#
-#       Example: defines = [ "FOO", "BAR=1" ]
-#
-#   inputs, deps, visibility  (optional)
-#       These have the same meaning as in an action.
-#
-# Example
-#
-#   yasm_assemble("my_yasm_target") {
-#     sources = [
-#       "ultra_optimized_awesome.asm",
-#     ]
-#     include_dirs = [ "assembly_include" ]
-#   }
-
-if (is_mac || is_ios) {
-  if (current_cpu == "x86") {
-    _yasm_flags = [
-      "-fmacho32",
-      "-m",
-      "x86",
-    ]
-  } else if (current_cpu == "x64") {
-    _yasm_flags = [
-      "-fmacho64",
-      "-m",
-      "amd64",
-    ]
-  }
-} else if (is_posix || is_fuchsia) {
-  if (current_cpu == "x86") {
-    _yasm_flags = [
-      "-felf32",
-      "-m",
-      "x86",
-    ]
-  } else if (current_cpu == "x64") {
-    _yasm_flags = [
-      "-DPIC",
-      "-felf64",
-      "-m",
-      "amd64",
-    ]
-  }
-} else if (is_win) {
-  if (current_cpu == "x86") {
-    _yasm_flags = [
-      "-DPREFIX",
-      "-fwin32",
-      "-m",
-      "x86",
-    ]
-  } else if (current_cpu == "x64") {
-    _yasm_flags = [
-      "-fwin64",
-      "-m",
-      "amd64",
-    ]
-  }
-}
-
-if (is_win) {
-  asm_obj_extension = "obj"
-} else {
-  asm_obj_extension = "o"
-}
-
-template("yasm_assemble") {
-  assert(defined(invoker.sources), "Need sources defined for $target_name")
-
-  # Only depend on YASM on x86 systems. Force compilation of .asm files for
-  # ARM to fail.
-  assert(current_cpu == "x86" || current_cpu == "x64")
-
-  action_name = "${target_name}_action"
-  source_set_name = target_name
-
-  action_foreach(action_name) {
-    # Only the source set can depend on this.
-    visibility = [ ":$source_set_name" ]
-
-    script = "//third_party/yasm/run_yasm.py"
-    sources = invoker.sources
-
-    if (defined(invoker.inputs)) {
-      inputs = invoker.inputs
-    }
-
-    # Executable (first in the args). The binary might be in the root build dir
-    # (no cross-compiling) or in a toolchain-specific subdirectory of that
-    # (when cross-compiling).
-    yasm_label = "//third_party/yasm($host_toolchain)"
-    args = [ "./" +  # Force current dir.
-             rebase_path(get_label_info(yasm_label, "root_out_dir") + "/yasm",
-                         root_build_dir) ]
-
-    # Deps.
-    deps = [
-      yasm_label,
-    ]
-    if (defined(invoker.deps)) {
-      deps += invoker.deps
-    }
-
-    # Flags.
-    args += _yasm_flags
-    if (defined(invoker.yasm_flags)) {
-      args += invoker.yasm_flags
-    }
-
-    # User defined include dirs go first.
-    if (defined(invoker.include_dirs)) {
-      foreach(include, invoker.include_dirs) {
-        args += [ "-I" + rebase_path(include, root_build_dir) ]
-      }
-    }
-
-    # Default yasm include dirs. Make it match the native build (source root and
-    # root generated code directory).
-    # This goes to the end of include list.
-    args += [
-      "-I.",
-
-      # Using "//." will produce a relative path "../.." which looks better than
-      # "../../" which will result from using "//" as the base (although both
-      # work). This is because rebase_path will terminate the result in a
-      # slash if the input ends in a slash.
-      "-I" + rebase_path("//.", root_build_dir),
-      "-I" + rebase_path(root_gen_dir, root_build_dir),
-    ]
-
-    # Extra defines.
-    if (defined(invoker.defines)) {
-      foreach(def, invoker.defines) {
-        args += [ "-D$def" ]
-      }
-    }
-
-    # Output file.
-    outputs = [
-      "$target_out_dir/$source_set_name/{{source_name_part}}.o",
-    ]
-    args += [
-      "-o",
-      rebase_path(outputs[0], root_build_dir),
-      "{{source}}",
-    ]
-
-    # The wrapper script run_yasm will write the depfile to the same name as
-    # the output but with .d appended (like gcc will).
-    depfile = outputs[0] + ".d"
-  }
-
-  # Gather the .o files into a linkable thing. This doesn't actually link
-  # anything (a source set just compiles files to link later), but will pass
-  # the object files generated by the action up the dependency chain.
-  static_library(source_set_name) {
-    if (defined(invoker.visibility)) {
-      visibility = invoker.visibility
-    }
-
-    sources = get_target_outputs(":$action_name")
-
-    # Do not publicize any header to remove build dependency.
-    public = []
-
-    deps = [
-      ":$action_name",
-    ]
-  }
-}
diff --git a/tools/android/md5sum/BUILD.gn b/tools/android/md5sum/BUILD.gn
new file mode 100644
index 0000000..e6fd16c
--- /dev/null
+++ b/tools/android/md5sum/BUILD.gn
@@ -0,0 +1,10 @@
+# Copyright 2020 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Just a dummy group for now.
+# TODO(crbug.com/pdfium/415): Check if an actual md5sum tool is necessary to
+# run tests on Android.
+group("md5sum") {
+  data_deps = []
+}
diff --git a/tools/modules/graph_modules.sh b/tools/modules/graph_modules.sh
index e579c4b..df6c940 100755
--- a/tools/modules/graph_modules.sh
+++ b/tools/modules/graph_modules.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 #
-# Copyright 2019 PDFium Authors. All rights reserved.
+# Copyright 2019 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 #
diff --git a/tools/msan/blacklist.txt b/tools/msan/ignorelist.txt
similarity index 100%
rename from tools/msan/blacklist.txt
rename to tools/msan/ignorelist.txt
diff --git a/tools/ubsan/blacklist.txt b/tools/ubsan/blacklist.txt
deleted file mode 100644
index 29ebcf9..0000000
--- a/tools/ubsan/blacklist.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-#############################################################################
-# UBSan blacklist.
-
-#############################################################################
-# YASM does some funny things that UBsan doesn't like.
-# https://crbug.com/489901
-src:*/third_party/yasm/*
-
-#############################################################################
-# V8 gives too many false positives. Ignore them for now.
-src:*/v8/*
-
-#############################################################################
-# Ignore system libraries.
-src:*/usr/*
-
-#############################################################################
-# ICU supressions. Mostly hash functions where integer overflow is OK.
-fun:*hashEntry*
-fun:*LocaleCacheKey*hashCode*
-fun:*google*protobuf*hash*
-fun:*(hash|Hash)*
-
-#############################################################################
-# Bounds blacklist.
-# Array at the end of struct pattern:
-# Maybe UBSan itself can be improved here?
-# e.g.
-# struct blah {
-#   int a;
-#   char foo[2]; // not actually 2
-# }
-src:*/third_party/icu/source/common/rbbi.cpp
-src:*/third_party/icu/source/common/rbbitblb.cpp
-src:*/third_party/icu/source/common/ucmndata.c
diff --git a/tools/ubsan/ignorelist.txt b/tools/ubsan/ignorelist.txt
new file mode 100644
index 0000000..7a2271d
--- /dev/null
+++ b/tools/ubsan/ignorelist.txt
@@ -0,0 +1,35 @@
+#############################################################################
+# UBSan ignore list.
+
+#############################################################################
+# YASM does some funny things that UBsan doesn't like.
+# https://crbug.com/489901
+src:*/third_party/yasm/*
+
+#############################################################################
+# V8 gives too many false positives. Ignore them for now.
+src:*/v8/*
+
+#############################################################################
+# Ignore system libraries.
+src:*/usr/*
+
+#############################################################################
+# ICU supressions. Mostly hash functions where integer overflow is OK.
+fun:*hashEntry*
+fun:*LocaleCacheKey*hashCode*
+fun:*google*protobuf*hash*
+fun:*(hash|Hash)*
+
+#############################################################################
+# Bounds ignore list.
+# Array at the end of struct pattern:
+# Maybe UBSan itself can be improved here?
+# e.g.
+# struct blah {
+#   int a;
+#   char foo[2]; // not actually 2
+# }
+src:*/third_party/icu/source/common/rbbi.cpp
+src:*/third_party/icu/source/common/rbbitblb.cpp
+src:*/third_party/icu/source/common/ucmndata.c
diff --git a/tools/ubsan/security_blacklist.txt b/tools/ubsan/security_blacklist.txt
deleted file mode 100644
index e9bb1ed..0000000
--- a/tools/ubsan/security_blacklist.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-# This black list is a merge of blacklist.txt and vptr_blacklist.txt.
-
-#############################################################################
-# UBSan blacklist.
-
-#############################################################################
-# YASM does some funny things that UBsan doesn't like.
-# https://crbug.com/489901
-src:*/third_party/yasm/*
-
-#############################################################################
-# V8 gives too many false positives. Ignore them for now.
-src:*/v8/*
-
-#############################################################################
-# Ignore system libraries.
-src:*/usr/*
-
-#############################################################################
-# ICU supressions. Mostly hash functions where integer overflow is OK.
-fun:*hashEntry*
-fun:*LocaleCacheKey*hashCode*
-fun:*google*protobuf*hash*
-fun:*(hash|Hash)*
-
-#############################################################################
-# Bounds blacklist.
-# Array at the end of struct pattern:
-# Maybe UBSan itself can be improved here?
-# e.g.
-# struct blah {
-#   int a;
-#   char foo[2]; // not actually 2
-# }
-src:*/third_party/icu/source/common/rbbi.cpp
-src:*/third_party/icu/source/common/rbbitblb.cpp
-src:*/third_party/icu/source/common/ucmndata.c
-
-#############################################################################
-# UBSan vptr blacklist.
-# Function and type based blacklisting use a mangled name, and it is especially
-# tricky to represent C++ types. For now, any possible changes by name manglings
-# are simply represented as wildcard expressions of regexp, and thus it might be
-# over-blacklisted.
-
-#############################################################################
-# UBSan seems to be emit false positives when virtual base classes are
-# involved, see e.g. crbug.com/448102.
-
-type:*v8*internal*OFStream*
-
-#############################################################################
-# UBsan goes into an infinite recursion when __dynamic_cast instrumented with
-# "vptr". See crbug.com/609786.
-
-src:*/third_party/libc\+\+abi/trunk/src/private_typeinfo.cpp
diff --git a/tools/ubsan/security_ignorelist.txt b/tools/ubsan/security_ignorelist.txt
new file mode 100644
index 0000000..30f66a4
--- /dev/null
+++ b/tools/ubsan/security_ignorelist.txt
@@ -0,0 +1,56 @@
+# This ignore  list is a merge of ignorelist.txt and vptr_ignorelist.txt.
+
+#############################################################################
+# UBSan ignore list.
+
+#############################################################################
+# YASM does some funny things that UBsan doesn't like.
+# https://crbug.com/489901
+src:*/third_party/yasm/*
+
+#############################################################################
+# V8 gives too many false positives. Ignore them for now.
+src:*/v8/*
+
+#############################################################################
+# Ignore system libraries.
+src:*/usr/*
+
+#############################################################################
+# ICU supressions. Mostly hash functions where integer overflow is OK.
+fun:*hashEntry*
+fun:*LocaleCacheKey*hashCode*
+fun:*google*protobuf*hash*
+fun:*(hash|Hash)*
+
+#############################################################################
+# Bounds ignore list.
+# Array at the end of struct pattern:
+# Maybe UBSan itself can be improved here?
+# e.g.
+# struct blah {
+#   int a;
+#   char foo[2]; // not actually 2
+# }
+src:*/third_party/icu/source/common/rbbi.cpp
+src:*/third_party/icu/source/common/rbbitblb.cpp
+src:*/third_party/icu/source/common/ucmndata.c
+
+#############################################################################
+# UBSan vptr ignore list.
+# Function and type based ignorelisting use a mangled name, and it is
+# especially tricky to represent C++ types. For now, any possible changes by
+# name manglings are simply represented as wildcard expressions of regexp,
+# and thus it might be over-ignorelisted.
+
+#############################################################################
+# UBSan seems to be emit false positives when virtual base classes are
+# involved, see e.g. crbug.com/448102.
+
+type:*v8*internal*OFStream*
+
+#############################################################################
+# UBsan goes into an infinite recursion when __dynamic_cast instrumented with
+# "vptr". See crbug.com/609786.
+
+src:*/third_party/libc\+\+abi/trunk/src/private_typeinfo.cpp
diff --git a/tools/ubsan/vptr_blacklist.txt b/tools/ubsan/vptr_blacklist.txt
deleted file mode 100644
index ceb6813..0000000
--- a/tools/ubsan/vptr_blacklist.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-#############################################################################
-# UBSan vptr blacklist.
-# Function and type based blacklisting use a mangled name, and it is especially
-# tricky to represent C++ types. For now, any possible changes by name manglings
-# are simply represented as wildcard expressions of regexp, and thus it might be
-# over-blacklisted.
-
-#############################################################################
-# UBSan seems to be emit false positives when virtual base classes are
-# involved, see e.g. crbug.com/448102.
-
-type:*v8*internal*OFStream*
-
-#############################################################################
-# UBsan goes into an infinite recursion when __dynamic_cast instrumented with
-# "vptr". See crbug.com/609786.
-
-src:*/third_party/libc\+\+abi/trunk/src/private_typeinfo.cpp
diff --git a/tools/ubsan/vptr_ignorelist.txt b/tools/ubsan/vptr_ignorelist.txt
new file mode 100644
index 0000000..5a06149
--- /dev/null
+++ b/tools/ubsan/vptr_ignorelist.txt
@@ -0,0 +1,18 @@
+#############################################################################
+# UBSan vptr ignore list.
+# Function and type based ignorelisting use a mangled name, and it is
+# especially tricky to represent C++ types. For now, any possible changes by
+# name manglings are simply represented as wildcard expressions of regexp,
+# and thus it might be over-ignorelisted.
+
+#############################################################################
+# UBSan seems to be emit false positives when virtual base classes are
+# involved, see e.g. crbug.com/448102.
+
+type:*v8*internal*OFStream*
+
+#############################################################################
+# UBsan goes into an infinite recursion when __dynamic_cast instrumented with
+# "vptr". See crbug.com/609786.
+
+src:*/third_party/libc\+\+abi/trunk/src/private_typeinfo.cpp
diff --git a/update_pdfium.py b/update_pdfium.py
index 6e36b65..cf8433c 100755
--- a/update_pdfium.py
+++ b/update_pdfium.py
@@ -15,29 +15,28 @@
 PDFIUM_GIT_REPO = "https://pdfium.googlesource.com/pdfium.git"
 
 MAKE_FILES = ["Android.bp",
-              "third_party/pdfiumopenjpeg.bp",
-              "third_party/pdfiumlcms.bp",
-              "third_party/pdfiumjpeg.bp",
-              "third_party/pdfiumagg23.bp",
-              "third_party/pdfiumzlib.bp",
-              "third_party/pdfiumbigint.bp",
-              "third_party/Android.bp",
-              "pdfiumfpdftext.bp",
-              "pdfiumfpdfdoc.bp",
-              "pdfiumfdrm.bp",
-              "pdfiumfxcodec.bp",
-              "pdfiumfpdfapi.bp",
-              "pdfiumfxcrt.bp",
-              "pdfiumfxge.bp",
-              "pdfiumjavascript.bp",
-              "pdfiumformfiller.bp",
-              "pdfiumfxedit.bp",
-              "pdfiumpdfwindow.bp",
-              "pdfium.bp"]
+              "constants/Android.bp",
+              "core/fdrm/Android.bp",
+              "core/fpdfapi/cmaps/Android.bp",
+              "core/fpdfapi/edit/Android.bp",
+              "core/fpdfapi/font/Android.bp",
+              "core/fpdfapi/page/Android.bp",
+              "core/fpdfapi/parser/Android.bp",
+              "core/fpdfapi/render/Android.bp",
+              "core/fpdfdoc/Android.bp",
+              "core/fpdftext/Android.bp",
+              "core/fxcodec/Android.bp",
+              "core/fxcrt/Android.bp",
+              "core/fxge/Android.bp",
+              "fpdfsdk/Android.bp",
+              "fpdfsdk/formfiller/Android.bp",
+              "fpdfsdk/pwl/Android.bp",
+              "fxjs/Android.bp",
+              "third_party/Android.bp"]
 
 OWNERS_FILES = ["OWNERS", "docs/OWNERS", "third_party/base/numerics/OWNERS"]
 
-COPY_FILES = [os.path.basename(__file__), ".git", "MODULE_LICENSE_BSD", "NOTICE"] + MAKE_FILES
+COPY_FILES = [os.path.basename(__file__), ".git", "MODULE_LICENSE_BSD"] + MAKE_FILES
 REMOVE_FILES = [os.path.basename(__file__), ".git", ".gitignore"] + OWNERS_FILES
 
 def getStableChromiumVersion():
diff --git a/xfa/BUILD.gn b/xfa/BUILD.gn
index df29737..627ff21 100644
--- a/xfa/BUILD.gn
+++ b/xfa/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 assert(pdf_enable_xfa)
 
 config("xfa_warnings") {
-  visibility = [ ":*" ]
+  visibility = [ "./*" ]
   if (is_posix && !is_clang) {  # When GCC.
     cflags = [ "-Wno-strict-overflow" ]
   }
diff --git a/xfa/DEPS b/xfa/DEPS
index dba1791..a279684 100644
--- a/xfa/DEPS
+++ b/xfa/DEPS
@@ -1,7 +1,7 @@
 include_rules = [
   '+core',
   '+fxbarcode',
-
-  # xfa/fwl should be standalone. https://crbug.com/pdfium/507
+  '+fxjs/gc',
+  '+v8/include/cppgc',
   '-xfa/fwl',
 ]
diff --git a/xfa/fde/BUILD.gn b/xfa/fde/BUILD.gn
index 1806683..31d6e55 100644
--- a/xfa/fde/BUILD.gn
+++ b/xfa/fde/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -17,26 +17,30 @@
     "cfde_wordbreak_data.cpp",
     "cfde_wordbreak_data.h",
   ]
+  configs += [
+    "../../:pdfium_strict_config",
+    "../../:pdfium_noshorten_config",
+    "../:xfa_warnings",
+  ]
   deps = [
     "../../core/fxcrt",
     "../../core/fxge",
-    "../fgas",
+    "../fgas/font",
     "../fgas/layout",
   ]
-  configs += [
-    "../../:pdfium_core_config",
-    "../:xfa_warnings",
-  ]
   visibility = [ "../../*" ]
 }
 
 pdfium_unittest_source_set("unittests") {
-  sources = [ "cfde_texteditengine_unittest.cpp" ]
+  sources = [
+    "cfde_texteditengine_unittest.cpp",
+    "cfde_textout_unittest.cpp",
+  ]
   deps = [
     ":fde",
+    "../../core/fdrm",
     "../../core/fxge",
-    "../../testing:unit_test_support",
-    "../fgas",
+    "../fgas/font",
   ]
   pdfium_root_dir = "../../"
 }
diff --git a/xfa/fde/cfde_data.h b/xfa/fde/cfde_data.h
index ad0740e..bfc0ef3 100644
--- a/xfa/fde/cfde_data.h
+++ b/xfa/fde/cfde_data.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fde/cfde_texteditengine.cpp b/xfa/fde/cfde_texteditengine.cpp
index 957b6ff..b28cced 100644
--- a/xfa/fde/cfde_texteditengine.cpp
+++ b/xfa/fde/cfde_texteditengine.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,21 +7,20 @@
 #include "xfa/fde/cfde_texteditengine.h"
 
 #include <algorithm>
-#include <limits>
 #include <utility>
 
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/span_util.h"
 #include "core/fxge/text_char_pos.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/numerics/safe_conversions.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fde/cfde_wordbreak_data.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
 
 namespace {
 
-constexpr size_t kMaxEditOperations = 128;
-constexpr size_t kGapSize = 128;
-constexpr size_t kPageWidthMax = 0xffff;
-
 class InsertOperation final : public CFDE_TextEditEngine::Operation {
  public:
   InsertOperation(CFDE_TextEditEngine* engine,
@@ -29,7 +28,7 @@
                   const WideString& added_text)
       : engine_(engine), start_idx_(start_idx), added_text_(added_text) {}
 
-  ~InsertOperation() override {}
+  ~InsertOperation() override = default;
 
   void Redo() const override {
     engine_->Insert(start_idx_, added_text_,
@@ -54,7 +53,7 @@
                   const WideString& removed_text)
       : engine_(engine), start_idx_(start_idx), removed_text_(removed_text) {}
 
-  ~DeleteOperation() override {}
+  ~DeleteOperation() override = default;
 
   void Redo() const override {
     engine_->Delete(start_idx_, removed_text_.GetLength(),
@@ -81,7 +80,7 @@
       : insert_op_(engine, start_idx, added_text),
         delete_op_(engine, start_idx, removed_text) {}
 
-  ~ReplaceOperation() override {}
+  ~ReplaceOperation() override = default;
 
   void Redo() const override {
     delete_op_.Redo();
@@ -121,32 +120,7 @@
 
 }  // namespace
 
-CFDE_TextEditEngine::CFDE_TextEditEngine()
-    : font_color_(0xff000000),
-      font_size_(10.0f),
-      line_spacing_(10.0f),
-      text_length_(0),
-      gap_position_(0),
-      gap_size_(kGapSize),
-      available_width_(kPageWidthMax),
-      character_limit_(std::numeric_limits<size_t>::max()),
-      visible_line_count_(1),
-      next_operation_index_to_undo_(kMaxEditOperations - 1),
-      next_operation_index_to_insert_(0),
-      max_edit_operations_(kMaxEditOperations),
-      character_alignment_(CFX_TxtLineAlignment_Left),
-      has_character_limit_(false),
-      is_comb_text_(false),
-      is_dirty_(false),
-      validation_enabled_(false),
-      is_multiline_(false),
-      is_linewrap_enabled_(false),
-      limit_horizontal_area_(false),
-      limit_vertical_area_(false),
-      password_mode_(false),
-      password_alias_(L'*'),
-      has_selection_(false),
-      selection_({0, 0}) {
+CFDE_TextEditEngine::CFDE_TextEditEngine() {
   content_.resize(gap_size_);
   operation_buffer_.resize(max_edit_operations_);
 
@@ -155,7 +129,7 @@
   text_break_.SetTabWidth(36);
 }
 
-CFDE_TextEditEngine::~CFDE_TextEditEngine() {}
+CFDE_TextEditEngine::~CFDE_TextEditEngine() = default;
 
 void CFDE_TextEditEngine::Clear() {
   text_length_ = 0;
@@ -209,7 +183,7 @@
   if (!limit_horizontal_area_ && !limit_vertical_area_)
     return 0;
 
-  auto text_out = pdfium::MakeUnique<CFDE_TextOut>();
+  auto text_out = std::make_unique<CFDE_TextOut>();
   text_out->SetLineSpace(line_spacing_);
   text_out->SetFont(font_);
   text_out->SetFontSize(font_size_);
@@ -299,7 +273,7 @@
       // Raise the limit to allow subsequent changes to expanded text.
       character_limit_ = text_length_ + length;
     } else {
-      // Trucate the text to comply with the limit.
+      // Truncate the text to comply with the limit.
       CHECK(text_length_ <= character_limit_);
       length = character_limit_ - text_length_;
       exceeded_limit = true;
@@ -344,7 +318,7 @@
 
   if (add_operation == RecordOperation::kInsertRecord) {
     AddOperationRecord(
-        pdfium::MakeUnique<InsertOperation>(this, gap_position_, text));
+        std::make_unique<InsertOperation>(this, gap_position_, text));
   }
 
   WideString previous_text;
@@ -352,8 +326,8 @@
     previous_text = GetText();
 
   // Copy the new text into the gap.
-  static const size_t char_size = sizeof(WideString::CharType);
-  memcpy(content_.data() + gap_position_, text.c_str(), length * char_size);
+  fxcrt::spancpy(pdfium::make_span(content_).subspan(gap_position_),
+                 text.span().first(length));
   gap_position_ += length;
   gap_size_ -= length;
   text_length_ += length;
@@ -421,14 +395,6 @@
   next_operation_index_to_insert_ = 0;
 }
 
-size_t CFDE_TextEditEngine::GetIndexBefore(size_t pos) {
-  int32_t bidi_level;
-  CFX_RectF rect;
-  // Possible |Layout| triggered by |GetCharacterInfo|.
-  std::tie(bidi_level, rect) = GetCharacterInfo(pos);
-  return FX_IsOdd(bidi_level) ? GetIndexRight(pos) : GetIndexLeft(pos);
-}
-
 size_t CFDE_TextEditEngine::GetIndexLeft(size_t pos) const {
   if (pos == 0)
     return 0;
@@ -654,7 +620,7 @@
   if (font_ == font)
     return;
 
-  font_ = font;
+  font_ = std::move(font);
   text_break_.SetFont(font_);
   is_dirty_ = true;
 }
@@ -702,13 +668,13 @@
   if (is_multiline_ == val)
     return;
 
-  is_multiline_ = true;
-
-  uint32_t style = text_break_.GetLayoutStyles();
+  is_multiline_ = val;
+  Mask<CFGAS_Break::LayoutStyle> style = text_break_.GetLayoutStyles();
   if (is_multiline_)
-    style &= ~FX_LAYOUTSTYLE_SingleLine;
+    style.Clear(CFGAS_Break::LayoutStyle::kSingleLine);
   else
-    style |= FX_LAYOUTSTYLE_SingleLine;
+    style |= CFGAS_Break::LayoutStyle::kSingleLine;
+
   text_break_.SetLayoutStyles(style);
   is_dirty_ = true;
 }
@@ -729,12 +695,12 @@
 
   is_comb_text_ = enable;
 
-  uint32_t style = text_break_.GetLayoutStyles();
+  Mask<CFGAS_Break::LayoutStyle> style = text_break_.GetLayoutStyles();
   if (enable) {
-    style |= FX_LAYOUTSTYLE_CombText;
+    style |= CFGAS_Break::LayoutStyle::kCombText;
     SetCombTextWidth();
   } else {
-    style &= ~FX_LAYOUTSTYLE_CombText;
+    style.Clear(CFGAS_Break::LayoutStyle::kCombText);
   }
   text_break_.SetLayoutStyles(style);
   is_dirty_ = true;
@@ -855,8 +821,7 @@
   ret += WideStringView(content_.data() + start_idx, length);
 
   if (add_operation == RecordOperation::kInsertRecord) {
-    AddOperationRecord(
-        pdfium::MakeUnique<DeleteOperation>(this, start_idx, ret));
+    AddOperationRecord(std::make_unique<DeleteOperation>(this, start_idx, ret));
   }
 
   WideString previous_text = GetText();
@@ -903,7 +868,7 @@
   Insert(gap_position_, rep, RecordOperation::kSkipRecord);
 
   AddOperationRecord(
-      pdfium::MakeUnique<ReplaceOperation>(this, start_idx, txt, rep));
+      std::make_unique<ReplaceOperation>(this, start_idx, txt, rep));
 }
 
 WideString CFDE_TextEditEngine::GetText() const {
@@ -932,7 +897,7 @@
              : content_[gap_position_ + gap_size_ + (idx - gap_position_)];
 }
 
-size_t CFDE_TextEditEngine::GetWidthOfChar(size_t idx) {
+int32_t CFDE_TextEditEngine::GetWidthOfChar(size_t idx) {
   // Recalculate the widths if necessary.
   Layout();
   return idx < char_widths_.size() ? char_widths_[idx] : 0;
@@ -984,7 +949,7 @@
       // move the cursor after it.
       bool closer_to_left =
           (point.x - rects[i].left < rects[i].right() - point.x);
-      int caret_pos = (closer_to_left ? i : i + 1);
+      size_t caret_pos = closer_to_left ? i : i + 1;
       size_t pos = start_it->nStart + caret_pos;
       if (pos >= text_length_)
         return text_length_;
@@ -1038,7 +1003,7 @@
   if (piece.nCount < 1)
     return std::vector<CFX_RectF>();
 
-  CFX_TxtBreak::Run tr;
+  CFGAS_TxtBreak::Run tr;
   tr.pEdtEngine = this;
   tr.iStart = piece.nStart;
   tr.iLength = piece.nCount;
@@ -1047,7 +1012,7 @@
   tr.dwStyles = text_break_.GetLayoutStyles();
   tr.dwCharStyles = piece.dwCharStyles;
   tr.pRect = &piece.rtPiece;
-  return text_break_.GetCharRects(&tr, false);
+  return text_break_.GetCharRects(tr);
 }
 
 std::vector<TextCharPos> CFDE_TextEditEngine::GetDisplayPos(
@@ -1055,7 +1020,7 @@
   if (piece.nCount < 1)
     return std::vector<TextCharPos>();
 
-  CFX_TxtBreak::Run tr;
+  CFGAS_TxtBreak::Run tr;
   tr.pEdtEngine = this;
   tr.iStart = piece.nStart;
   tr.iLength = piece.nCount;
@@ -1065,20 +1030,20 @@
   tr.dwCharStyles = piece.dwCharStyles;
   tr.pRect = &piece.rtPiece;
 
-  std::vector<TextCharPos> data(text_break_.GetDisplayPos(&tr, nullptr));
-  text_break_.GetDisplayPos(&tr, data.data());
+  std::vector<TextCharPos> data(text_break_.GetDisplayPos(tr, nullptr));
+  text_break_.GetDisplayPos(tr, data.data());
   return data;
 }
 
 void CFDE_TextEditEngine::RebuildPieces() {
-  text_break_.EndBreak(CFX_BreakType::Paragraph);
+  text_break_.EndBreak(CFGAS_Char::BreakType::kParagraph);
   text_break_.ClearBreakPieces();
 
   char_widths_.clear();
   text_piece_info_.clear();
 
   // Must have a font set in order to break the text.
-  if (text_length_ == 0 || !font_)
+  if (!CanGenerateCharacterInfo())
     return;
 
   bool initialized_bounding_box = false;
@@ -1086,31 +1051,32 @@
   size_t current_piece_start = 0;
   float current_line_start = 0;
 
-  auto iter = pdfium::MakeUnique<CFDE_TextEditEngine::Iterator>(this);
-  while (!iter->IsEOF(false)) {
-    iter->Next(false);
+  CFDE_TextEditEngine::Iterator iter(this);
+  while (!iter.IsEOF(false)) {
+    iter.Next(false);
 
-    CFX_BreakType break_status = text_break_.AppendChar(
-        password_mode_ ? password_alias_ : iter->GetChar());
-    if (iter->IsEOF(false) && CFX_BreakTypeNoneOrPiece(break_status))
-      break_status = text_break_.EndBreak(CFX_BreakType::Paragraph);
+    CFGAS_Char::BreakType break_status = text_break_.AppendChar(
+        password_mode_ ? password_alias_ : iter.GetChar());
+    if (iter.IsEOF(false) && CFX_BreakTypeNoneOrPiece(break_status))
+      break_status = text_break_.EndBreak(CFGAS_Char::BreakType::kParagraph);
 
     if (CFX_BreakTypeNoneOrPiece(break_status))
       continue;
     int32_t piece_count = text_break_.CountBreakPieces();
     for (int32_t i = 0; i < piece_count; ++i) {
-      const CFX_BreakPiece* piece = text_break_.GetBreakPieceUnstable(i);
+      const CFGAS_BreakPiece* piece = text_break_.GetBreakPieceUnstable(i);
 
       FDE_TEXTEDITPIECE txtEdtPiece;
-      txtEdtPiece.rtPiece.left = piece->m_iStartPos / 20000.0f;
+      txtEdtPiece.rtPiece.left = piece->GetStartPos() / 20000.0f;
       txtEdtPiece.rtPiece.top = current_line_start;
-      txtEdtPiece.rtPiece.width = piece->m_iWidth / 20000.0f;
+      txtEdtPiece.rtPiece.width = piece->GetWidth() / 20000.0f;
       txtEdtPiece.rtPiece.height = line_spacing_;
-      txtEdtPiece.nStart = current_piece_start;
-      txtEdtPiece.nCount = piece->GetLength();
-      txtEdtPiece.nBidiLevel = piece->m_iBidiLevel;
-      txtEdtPiece.dwCharStyles = piece->m_dwCharStyles;
-      if (FX_IsOdd(piece->m_iBidiLevel))
+      txtEdtPiece.nStart =
+          pdfium::base::checked_cast<int32_t>(current_piece_start);
+      txtEdtPiece.nCount = piece->GetCharCount();
+      txtEdtPiece.nBidiLevel = piece->GetBidiLevel();
+      txtEdtPiece.dwCharStyles = piece->GetCharStyles();
+      if (FX_IsOdd(piece->GetBidiLevel()))
         txtEdtPiece.dwCharStyles |= FX_TXTCHARSTYLE_OddBidiLevel;
 
       text_piece_info_.push_back(txtEdtPiece);
@@ -1153,8 +1119,8 @@
 
 std::pair<int32_t, CFX_RectF> CFDE_TextEditEngine::GetCharacterInfo(
     int32_t start_idx) {
-  ASSERT(start_idx >= 0);
-  ASSERT(static_cast<size_t>(start_idx) <= text_length_);
+  DCHECK(start_idx >= 0);
+  DCHECK(static_cast<size_t>(start_idx) <= text_length_);
 
   // Make sure the current available data is fresh.
   Layout();
@@ -1164,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]};
 }
 
@@ -1218,9 +1180,9 @@
 }
 
 CFDE_TextEditEngine::Iterator::Iterator(const CFDE_TextEditEngine* engine)
-    : engine_(engine), current_position_(-1) {}
+    : engine_(engine) {}
 
-CFDE_TextEditEngine::Iterator::~Iterator() {}
+CFDE_TextEditEngine::Iterator::~Iterator() = default;
 
 void CFDE_TextEditEngine::Iterator::Next(bool bPrev) {
   if (bPrev && current_position_ == -1)
@@ -1241,10 +1203,8 @@
 }
 
 void CFDE_TextEditEngine::Iterator::SetAt(size_t nIndex) {
-  if (static_cast<size_t>(nIndex) >= engine_->GetLength())
-    current_position_ = engine_->GetLength();
-  else
-    current_position_ = nIndex;
+  nIndex = std::min(nIndex, engine_->GetLength());
+  current_position_ = pdfium::base::checked_cast<int32_t>(nIndex);
 }
 
 bool CFDE_TextEditEngine::Iterator::IsEOF(bool bPrev) const {
diff --git a/xfa/fde/cfde_texteditengine.h b/xfa/fde/cfde_texteditengine.h
index d68aa41..c020e8f 100644
--- a/xfa/fde/cfde_texteditengine.h
+++ b/xfa/fde/cfde_texteditengine.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,16 @@
 #ifndef XFA_FDE_CFDE_TEXTEDITENGINE_H_
 #define XFA_FDE_CFDE_TEXTEDITENGINE_H_
 
+#include <limits>
 #include <memory>
 #include <utility>
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/fx_dib.h"
-#include "xfa/fgas/layout/cfx_txtbreak.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "xfa/fgas/layout/cfgas_txtbreak.h"
 
 class CFGAS_GEFont;
 class TextCharPos;
@@ -36,7 +38,7 @@
     default;
 inline FDE_TEXTEDITPIECE::~FDE_TEXTEDITPIECE() = default;
 
-class CFDE_TextEditEngine : public CFX_TxtBreak::Engine {
+class CFDE_TextEditEngine final : public CFGAS_TxtBreak::Engine {
  public:
   class Iterator {
    public:
@@ -50,8 +52,8 @@
     bool IsEOF(bool bPrev) const;
 
    private:
-    UnownedPtr<const CFDE_TextEditEngine> engine_;
-    int32_t current_position_;
+    UnownedPtr<const CFDE_TextEditEngine> const engine_;
+    int32_t current_position_ = -1;
   };
 
   class Operation {
@@ -86,9 +88,9 @@
   CFDE_TextEditEngine();
   ~CFDE_TextEditEngine() override;
 
-  // CFX_TxtBreak::Engine:
+  // CFGAS_TxtBreak::Engine:
   wchar_t GetChar(size_t idx) const override;
-  size_t GetWidthOfChar(size_t idx) override;
+  int32_t GetWidthOfChar(size_t idx) override;
 
   void SetDelegate(Delegate* delegate) { delegate_ = delegate; }
   void Clear();
@@ -137,8 +139,6 @@
   bool Undo();
   void ClearOperationRecords();
 
-  // This is not const it can trigger a |Layout|.
-  size_t GetIndexBefore(size_t pos);
   size_t GetIndexLeft(size_t pos) const;
   size_t GetIndexRight(size_t pos) const;
   size_t GetIndexUp(size_t pos) const;
@@ -166,6 +166,10 @@
   // <start_idx, count>
   std::pair<size_t, size_t> BoundsForWordAt(size_t idx) const;
 
+  // Note that if CanGenerateCharacterInfo() returns false, then
+  // GetCharacterInfo() cannot be called.
+  bool CanGenerateCharacterInfo() const { return text_length_ > 0 && font_; }
+
   // Returns <bidi level, character rect>
   std::pair<int32_t, CFX_RectF> GetCharacterInfo(int32_t start_idx);
   std::vector<CFX_RectF> GetCharacterRectsInRange(int32_t start_idx,
@@ -182,6 +186,15 @@
   void SetMaxEditOperationsForTesting(size_t max);
 
  private:
+  struct Selection {
+    size_t start_idx;
+    size_t count;
+  };
+
+  static constexpr size_t kGapSize = 128;
+  static constexpr size_t kMaxEditOperations = 128;
+  static constexpr size_t kPageWidthMax = 0xffff;
+
   void SetCombTextWidth();
   void AdjustGap(size_t idx, size_t length);
   void RebuildPieces();
@@ -189,7 +202,7 @@
   void AddOperationRecord(std::unique_ptr<Operation> op);
 
   bool IsAlignedRight() const {
-    return !!(character_alignment_ & CFX_TxtLineAlignment_Left);
+    return !!(character_alignment_ & CFX_TxtLineAlignment_Right);
   }
 
   bool IsAlignedCenter() const {
@@ -197,50 +210,45 @@
   }
   std::vector<CFX_RectF> GetCharRects(const FDE_TEXTEDITPIECE& piece);
 
-  struct Selection {
-    size_t start_idx;
-    size_t count;
-  };
-
   CFX_RectF contents_bounding_box_;
   UnownedPtr<Delegate> delegate_;
   std::vector<FDE_TEXTEDITPIECE> text_piece_info_;
-  std::vector<size_t> char_widths_;
-  CFX_TxtBreak text_break_;
+  std::vector<int32_t> char_widths_;  // May be negative for combining chars.
+  CFGAS_TxtBreak text_break_;
   RetainPtr<CFGAS_GEFont> font_;
-  FX_ARGB font_color_;
-  float font_size_;
-  float line_spacing_;
+  FX_ARGB font_color_ = 0xff000000;
+  float font_size_ = 10.0f;
+  float line_spacing_ = 10.0f;
   std::vector<WideString::CharType> content_;
-  size_t text_length_;
+  size_t text_length_ = 0;
 
   // See e.g. https://en.wikipedia.org/wiki/Gap_buffer
-  size_t gap_position_;
-  size_t gap_size_;
+  size_t gap_position_ = 0;
+  size_t gap_size_ = kGapSize;
 
-  size_t available_width_;
-  size_t character_limit_;
-  size_t visible_line_count_;
+  size_t available_width_ = kPageWidthMax;
+  size_t character_limit_ = std::numeric_limits<size_t>::max();
+  size_t visible_line_count_ = 1;
   // Ring buffer of edit operations
   std::vector<std::unique_ptr<Operation>> operation_buffer_;
   // Next edit operation to undo.
-  size_t next_operation_index_to_undo_;
+  size_t next_operation_index_to_undo_ = kMaxEditOperations - 1;
   // Next index to insert an edit operation into.
-  size_t next_operation_index_to_insert_;
-  size_t max_edit_operations_;
-  uint32_t character_alignment_;
-  bool has_character_limit_;
-  bool is_comb_text_;
-  bool is_dirty_;
-  bool validation_enabled_;
-  bool is_multiline_;
-  bool is_linewrap_enabled_;
-  bool limit_horizontal_area_;
-  bool limit_vertical_area_;
-  bool password_mode_;
-  wchar_t password_alias_;
-  bool has_selection_;
-  Selection selection_;
+  size_t next_operation_index_to_insert_ = 0;
+  size_t max_edit_operations_ = kMaxEditOperations;
+  uint32_t character_alignment_ = CFX_TxtLineAlignment_Left;
+  bool has_character_limit_ = false;
+  bool is_comb_text_ = false;
+  bool is_dirty_ = false;
+  bool validation_enabled_ = false;
+  bool is_multiline_ = false;
+  bool is_linewrap_enabled_ = false;
+  bool limit_horizontal_area_ = false;
+  bool limit_vertical_area_ = false;
+  bool password_mode_ = false;
+  wchar_t password_alias_ = L'*';
+  bool has_selection_ = false;
+  Selection selection_{0, 0};
 };
 
 #endif  // XFA_FDE_CFDE_TEXTEDITENGINE_H_
diff --git a/xfa/fde/cfde_texteditengine_unittest.cpp b/xfa/fde/cfde_texteditengine_unittest.cpp
index 2089dff..a580717 100644
--- a/xfa/fde/cfde_texteditengine_unittest.cpp
+++ b/xfa/fde/cfde_texteditengine_unittest.cpp
@@ -1,13 +1,14 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "xfa/fde/cfde_texteditengine.h"
 
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_extension.h"
 #include "core/fxge/text_char_pos.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/xfa_unit_test_support.h"
-#include "third_party/base/ptr_util.h"
+#include "testing/xfa_test_environment.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
 
 class CFDE_TextEditEngineTest : public testing::Test {
@@ -34,20 +35,23 @@
     bool text_is_full = false;
   };
 
-  CFDE_TextEditEngineTest() {}
-  ~CFDE_TextEditEngineTest() override {}
+  CFDE_TextEditEngineTest() = default;
+  ~CFDE_TextEditEngineTest() override = default;
 
   void SetUp() override {
-    font_ =
-        CFGAS_GEFont::LoadFont(L"Arial Black", 0, 0, GetGlobalFontManager());
-    ASSERT_TRUE(font_.Get() != nullptr);
+    const wchar_t kFontFamily[] = L"Arimo Bold";
+    font_ = CFGAS_GEFont::LoadFont(kFontFamily, 0, FX_CodePage::kDefANSI);
+    ASSERT_TRUE(font_);
 
-    engine_ = pdfium::MakeUnique<CFDE_TextEditEngine>();
+    engine_ = std::make_unique<CFDE_TextEditEngine>();
     engine_->SetFont(font_);
     engine_->SetFontSize(12.0f);
   }
 
-  void TearDown() override { engine_.reset(); }
+  void TearDown() override {
+    engine_.reset();
+    font_.Reset();
+  }
 
   CFDE_TextEditEngine* engine() const { return engine_.get(); }
 
@@ -94,7 +98,7 @@
   engine()->Clear();
 
   // With Delegate
-  auto delegate = pdfium::MakeUnique<CFDE_TextEditEngineTest::Delegate>();
+  auto delegate = std::make_unique<CFDE_TextEditEngineTest::Delegate>();
   engine()->SetDelegate(delegate.get());
 
   engine()->SetCharacterLimit(5);
@@ -137,7 +141,7 @@
 
   // Insert with limited area and over-fill
   engine()->LimitHorizontalScroll(true);
-  engine()->SetAvailableWidth(60.0f);  // Fits 'Hello Wo'.
+  engine()->SetAvailableWidth(52.0f);  // Fits 'Hello Wo'.
   engine()->Insert(0, L"Hello");
   EXPECT_FALSE(delegate->text_is_full);
   engine()->Insert(5, L" World");
@@ -261,14 +265,14 @@
 
 TEST_F(CFDE_TextEditEngineTest, GetWidthOfChar) {
   // Out of Bounds.
-  EXPECT_EQ(0U, engine()->GetWidthOfChar(0));
+  EXPECT_EQ(0, engine()->GetWidthOfChar(0));
 
   engine()->Insert(0, L"Hello World");
-  EXPECT_EQ(199920U, engine()->GetWidthOfChar(0));
-  EXPECT_EQ(159840U, engine()->GetWidthOfChar(1));
+  EXPECT_EQ(173280, engine()->GetWidthOfChar(0));
+  EXPECT_EQ(133440, engine()->GetWidthOfChar(1));
 
   engine()->Insert(0, L"\t");
-  EXPECT_EQ(0U, engine()->GetWidthOfChar(0));
+  EXPECT_EQ(0, engine()->GetWidthOfChar(0));
 }
 
 TEST_F(CFDE_TextEditEngineTest, GetDisplayPos) {
@@ -466,7 +470,7 @@
   EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 9999999.0f}));
   EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 0.0f}));
   EXPECT_EQ(1U, engine()->GetIndexForPoint({5.0f, 5.0f}));
-  EXPECT_EQ(1U, engine()->GetIndexForPoint({10.0f, 5.0f}));
+  EXPECT_EQ(2U, engine()->GetIndexForPoint({10.0f, 5.0f}));
 }
 
 TEST_F(CFDE_TextEditEngineTest, GetIndexForPointLineWrap) {
@@ -476,8 +480,8 @@
                    L"getting indexes on multi-line edits.");
   EXPECT_EQ(0U, engine()->GetIndexForPoint({0.0f, 0.0f}));
   EXPECT_EQ(87U, engine()->GetIndexForPoint({999999.0f, 9999999.0f}));
-  EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 0.0f}));
-  EXPECT_EQ(12U, engine()->GetIndexForPoint({1.0f, 10.0f}));
+  EXPECT_EQ(18U, engine()->GetIndexForPoint({999999.0f, 0.0f}));
+  EXPECT_EQ(19U, engine()->GetIndexForPoint({1.0f, 10.0f}));
   EXPECT_EQ(1U, engine()->GetIndexForPoint({5.0f, 5.0f}));
   EXPECT_EQ(2U, engine()->GetIndexForPoint({10.0f, 5.0f}));
 }
@@ -499,6 +503,64 @@
   EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 9999999.0f}));
 }
 
+TEST_F(CFDE_TextEditEngineTest, CanGenerateCharacterInfo) {
+  RetainPtr<CFGAS_GEFont> font = engine()->GetFont();
+  ASSERT_TRUE(font);
+
+  // Has font but no text.
+  EXPECT_FALSE(engine()->CanGenerateCharacterInfo());
+
+  // Has font and text.
+  engine()->Insert(0, L"Hi!");
+  EXPECT_TRUE(engine()->CanGenerateCharacterInfo());
+
+  // Has text but no font.
+  engine()->SetFont(nullptr);
+  EXPECT_FALSE(engine()->CanGenerateCharacterInfo());
+
+  // Has no text and no font.
+  engine()->Clear();
+  EXPECT_FALSE(engine()->CanGenerateCharacterInfo());
+}
+
+TEST_F(CFDE_TextEditEngineTest, GetCharacterInfo) {
+  std::pair<int32_t, CFX_RectF> char_info;
+
+  engine()->Insert(0, L"Hi!");
+  ASSERT_EQ(3U, engine()->GetLength());
+
+  char_info = engine()->GetCharacterInfo(0);
+  EXPECT_EQ(0, char_info.first);
+  EXPECT_FLOAT_EQ(0.0f, char_info.second.Left());
+  EXPECT_FLOAT_EQ(0.0f, char_info.second.Top());
+  EXPECT_FLOAT_EQ(8.664f, char_info.second.Width());
+  EXPECT_FLOAT_EQ(12.0f, char_info.second.Height());
+
+  char_info = engine()->GetCharacterInfo(1);
+  EXPECT_EQ(0, char_info.first);
+  EXPECT_FLOAT_EQ(8.664f, char_info.second.Left());
+  EXPECT_FLOAT_EQ(0.0f, char_info.second.Top());
+  EXPECT_FLOAT_EQ(3.324f, char_info.second.Width());
+  EXPECT_FLOAT_EQ(12.0f, char_info.second.Height());
+
+  char_info = engine()->GetCharacterInfo(2);
+  EXPECT_EQ(0, char_info.first);
+  EXPECT_FLOAT_EQ(11.988f, char_info.second.Left());
+  EXPECT_FLOAT_EQ(0.0f, char_info.second.Top());
+  EXPECT_FLOAT_EQ(3.996f, char_info.second.Width());
+  EXPECT_FLOAT_EQ(12.0f, char_info.second.Height());
+
+  // Allow retrieving the character info for the end of the text, as that
+  // information can be used to determine where to draw a cursor positioned at
+  // the end.
+  char_info = engine()->GetCharacterInfo(3);
+  EXPECT_EQ(0, char_info.first);
+  EXPECT_FLOAT_EQ(15.984, char_info.second.Left());
+  EXPECT_FLOAT_EQ(0.0f, char_info.second.Top());
+  EXPECT_FLOAT_EQ(0.0f, char_info.second.Width());
+  EXPECT_FLOAT_EQ(12.0f, char_info.second.Height());
+}
+
 TEST_F(CFDE_TextEditEngineTest, BoundsForWordAt) {
   size_t start_idx;
   size_t count;
@@ -663,21 +725,26 @@
   EXPECT_EQ(2U, engine()->GetIndexUp(2));
   EXPECT_EQ(2U, engine()->GetIndexDown(2));
   EXPECT_EQ(1U, engine()->GetIndexLeft(2));
-  EXPECT_EQ(1U, engine()->GetIndexBefore(2));
   EXPECT_EQ(3U, engine()->GetIndexRight(2));
   EXPECT_EQ(0U, engine()->GetIndexAtStartOfLine(2));
   EXPECT_EQ(5U, engine()->GetIndexAtEndOfLine(2));
 
   engine()->Clear();
   engine()->Insert(0, L"The book is \"مدخل إلى C++\"");
-  EXPECT_EQ(2U, engine()->GetIndexBefore(3));    // Before is to left.
-  EXPECT_EQ(16U, engine()->GetIndexBefore(15));  // Before is to right.
-  EXPECT_EQ(22U, engine()->GetIndexBefore(23));  // Before is to left.
+  EXPECT_FALSE(FX_IsOdd(engine()->GetCharacterInfo(3).first));
+  EXPECT_EQ(2U, engine()->GetIndexLeft(3));
+  EXPECT_EQ(4U, engine()->GetIndexRight(3));
+  EXPECT_TRUE(FX_IsOdd(engine()->GetCharacterInfo(15).first));
+  EXPECT_EQ(14U, engine()->GetIndexLeft(15));
+  EXPECT_EQ(16U, engine()->GetIndexRight(15));
+  EXPECT_FALSE(FX_IsOdd(engine()->GetCharacterInfo(23).first));
+  EXPECT_EQ(22U, engine()->GetIndexLeft(23));
+  EXPECT_EQ(24U, engine()->GetIndexRight(23));
 
   engine()->Clear();
   engine()->Insert(0, L"Hello\r\nWorld\r\nTest");
   // Move to end of Hello from start of World.
-  engine()->SetSelection(engine()->GetIndexBefore(7U), 7);
+  engine()->SetSelection(engine()->GetIndexLeft(7U), 7);
   EXPECT_STREQ(L"\r\nWorld", engine()->GetSelectedText().c_str());
 
   // Second letter in Hello from second letter in World.
@@ -717,7 +784,7 @@
   engine()->Clear();
   engine()->Insert(0, L"Hello\rWorld\rTest");
   // Move to end of Hello from start of World.
-  engine()->SetSelection(engine()->GetIndexBefore(6U), 6);
+  engine()->SetSelection(engine()->GetIndexLeft(6U), 6);
   EXPECT_STREQ(L"\rWorld", engine()->GetSelectedText().c_str());
 
   // Second letter in Hello from second letter in World.
@@ -748,7 +815,7 @@
   engine()->Clear();
   engine()->Insert(0, L"Hello\nWorld\nTest");
   // Move to end of Hello from start of World.
-  engine()->SetSelection(engine()->GetIndexBefore(6U), 6);
+  engine()->SetSelection(engine()->GetIndexLeft(6U), 6);
   EXPECT_STREQ(L"\nWorld", engine()->GetSelectedText().c_str());
 
   // Second letter in Hello from second letter in World.
diff --git a/xfa/fde/cfde_textout.cpp b/xfa/fde/cfde_textout.cpp
index cd419bd..d7582d8 100644
--- a/xfa/fde/cfde_textout.cpp
+++ b/xfa/fde/cfde_textout.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,17 +11,21 @@
 
 #include "build/build_config.h"
 #include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/stl_util.h"
 #include "core/fxge/cfx_font.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/cfx_substfont.h"
+#include "core/fxge/cfx_textrenderoptions.h"
 #include "core/fxge/fx_font.h"
 #include "core/fxge/text_char_pos.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/numerics/safe_conversions.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
-#include "xfa/fgas/layout/cfx_txtbreak.h"
+#include "xfa/fgas/layout/cfgas_txtbreak.h"
 
 namespace {
 
@@ -44,8 +48,8 @@
                               pdfium::span<TextCharPos> pCharPos,
                               float fFontSize,
                               const CFX_Matrix& matrix) {
-  ASSERT(pFont);
-  ASSERT(!pCharPos.empty());
+  DCHECK(pFont);
+  DCHECK(!pCharPos.empty());
 
   CFX_Font* pFxFont = pFont->GetDevFont();
   if (FontStyleIsItalic(pFont->GetFontStyles()) && !pFxFont->IsItalic()) {
@@ -56,10 +60,10 @@
     }
   }
 
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
   uint32_t dwFontStyle = pFont->GetFontStyles();
   CFX_Font FxFont;
-  auto SubstFxFont = pdfium::MakeUnique<CFX_SubstFont>();
+  auto SubstFxFont = std::make_unique<CFX_SubstFont>();
   SubstFxFont->m_Weight = FontStyleIsForceBold(dwFontStyle) ? 700 : 400;
   SubstFxFont->m_ItalicAngle = FontStyleIsItalic(dwFontStyle) ? -12 : 0;
   SubstFxFont->m_WeightCJK = SubstFxFont->m_Weight;
@@ -70,6 +74,7 @@
   RetainPtr<CFGAS_GEFont> pCurFont;
   TextCharPos* pCurCP = nullptr;
   int32_t iCurCount = 0;
+  static constexpr CFX_TextRenderOptions kOptions(CFX_TextRenderOptions::kLcd);
   for (auto& pos : pCharPos) {
     RetainPtr<CFGAS_GEFont> pSTFont =
         pFont->GetSubstFont(static_cast<int32_t>(pos.m_GlyphIndex));
@@ -80,7 +85,7 @@
         pFxFont = pCurFont->GetDevFont();
 
         CFX_Font* font;
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
         FxFont.SetFace(pFxFont->GetFace());
         FxFont.SetFontSpan(pFxFont->GetFontSpan());
         font = &FxFont;
@@ -88,8 +93,8 @@
         font = pFxFont;
 #endif
 
-        device->DrawNormalText(iCurCount, pCurCP, font, -fFontSize, matrix,
-                               color, FXTEXT_CLEARTYPE);
+        device->DrawNormalText(pdfium::make_span(pCurCP, iCurCount), font,
+                               -fFontSize, matrix, color, kOptions);
       }
       pCurFont = pSTFont;
       pCurCP = &pos;
@@ -103,7 +108,7 @@
   if (pCurFont && iCurCount) {
     pFxFont = pCurFont->GetDevFont();
     CFX_Font* font;
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
     FxFont.SetFace(pFxFont->GetFace());
     FxFont.SetFontSpan(pFxFont->GetFontSpan());
     font = &FxFont;
@@ -111,45 +116,41 @@
     font = pFxFont;
 #endif
 
-    bRet = device->DrawNormalText(iCurCount, pCurCP, font, -fFontSize, matrix,
-                                  color, FXTEXT_CLEARTYPE);
+    bRet = device->DrawNormalText(pdfium::make_span(pCurCP, iCurCount), font,
+                                  -fFontSize, matrix, color, kOptions);
   }
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
-  device->Flush(false);
-#endif
 
   return bRet;
 }
 
-FDE_TTOPIECE::FDE_TTOPIECE() = default;
+CFDE_TextOut::Piece::Piece() = default;
 
-FDE_TTOPIECE::FDE_TTOPIECE(const FDE_TTOPIECE& that) = default;
+CFDE_TextOut::Piece::Piece(const Piece& that) = default;
 
-FDE_TTOPIECE::~FDE_TTOPIECE() = default;
+CFDE_TextOut::Piece::~Piece() = default;
 
 CFDE_TextOut::CFDE_TextOut()
-    : m_pTxtBreak(pdfium::MakeUnique<CFX_TxtBreak>()), m_ttoLines(5) {}
+    : m_pTxtBreak(std::make_unique<CFGAS_TxtBreak>()) {}
 
 CFDE_TextOut::~CFDE_TextOut() = default;
 
-void CFDE_TextOut::SetFont(const RetainPtr<CFGAS_GEFont>& pFont) {
-  ASSERT(pFont);
-  m_pFont = pFont;
-  m_pTxtBreak->SetFont(pFont);
+void CFDE_TextOut::SetFont(RetainPtr<CFGAS_GEFont> pFont) {
+  DCHECK(pFont);
+  m_pFont = std::move(pFont);
+  m_pTxtBreak->SetFont(m_pFont);
 }
 
 void CFDE_TextOut::SetFontSize(float fFontSize) {
-  ASSERT(fFontSize > 0);
+  DCHECK(fFontSize > 0);
   m_fFontSize = fFontSize;
   m_pTxtBreak->SetFontSize(fFontSize);
 }
 
 void CFDE_TextOut::SetStyles(const FDE_TextStyle& dwStyles) {
   m_Styles = dwStyles;
-
-  m_dwTxtBkStyles = 0;
-  if (m_Styles.single_line_)
-    m_dwTxtBkStyles |= FX_LAYOUTSTYLE_SingleLine;
+  m_dwTxtBkStyles = m_Styles.single_line_
+                        ? CFGAS_Break::LayoutStyle::kSingleLine
+                        : CFGAS_Break::LayoutStyle::kNone;
 
   m_pTxtBreak->SetLayoutStyles(m_dwTxtBkStyles);
 }
@@ -174,7 +175,7 @@
 }
 
 void CFDE_TextOut::SetLineSpace(float fLineSpace) {
-  ASSERT(fLineSpace > 1.0f);
+  DCHECK(fLineSpace > 1.0f);
   m_fLineSpace = fLineSpace;
 }
 
@@ -196,8 +197,8 @@
     return;
   }
 
-  ASSERT(m_pFont);
-  ASSERT(m_fFontSize >= 1.0f);
+  DCHECK(m_pFont);
+  DCHECK(m_fFontSize >= 1.0f);
 
   if (!m_Styles.single_line_) {
     if (pRect->Width() < 1.0f)
@@ -210,7 +211,7 @@
   float fWidth = 0.0f;
   float fHeight = 0.0f;
   float fStartPos = pRect->right();
-  CFX_BreakType dwBreakStatus = CFX_BreakType::None;
+  CFGAS_Char::BreakType dwBreakStatus = CFGAS_Char::BreakType::kNone;
   bool break_char_is_set = false;
   for (const wchar_t& wch : str) {
     if (!break_char_is_set && (wch == L'\n' || wch == L'\r')) {
@@ -222,7 +223,7 @@
       RetrieveLineWidth(dwBreakStatus, &fStartPos, &fWidth, &fHeight);
   }
 
-  dwBreakStatus = m_pTxtBreak->EndBreak(CFX_BreakType::Paragraph);
+  dwBreakStatus = m_pTxtBreak->EndBreak(CFGAS_Char::BreakType::kParagraph);
   if (!CFX_BreakTypeNoneOrPiece(dwBreakStatus))
     RetrieveLineWidth(dwBreakStatus, &fStartPos, &fWidth, &fHeight);
 
@@ -241,26 +242,26 @@
     pRect->height -= m_fLineSpace - m_fFontSize;
 }
 
-bool CFDE_TextOut::RetrieveLineWidth(CFX_BreakType dwBreakStatus,
+bool CFDE_TextOut::RetrieveLineWidth(CFGAS_Char::BreakType dwBreakStatus,
                                      float* pStartPos,
                                      float* pWidth,
                                      float* pHeight) {
   if (CFX_BreakTypeNoneOrPiece(dwBreakStatus))
     return false;
 
-  float fLineStep = (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize;
+  float fLineStep = std::max(m_fLineSpace, m_fFontSize);
   float fLineWidth = 0.0f;
   for (int32_t i = 0; i < m_pTxtBreak->CountBreakPieces(); i++) {
-    const CFX_BreakPiece* pPiece = m_pTxtBreak->GetBreakPieceUnstable(i);
-    fLineWidth += static_cast<float>(pPiece->m_iWidth) / 20000.0f;
+    const CFGAS_BreakPiece* pPiece = m_pTxtBreak->GetBreakPieceUnstable(i);
+    fLineWidth += static_cast<float>(pPiece->GetWidth()) / 20000.0f;
     *pStartPos = std::min(*pStartPos,
-                          static_cast<float>(pPiece->m_iStartPos) / 20000.0f);
+                          static_cast<float>(pPiece->GetStartPos()) / 20000.0f);
   }
   m_pTxtBreak->ClearBreakPieces();
 
-  if (dwBreakStatus == CFX_BreakType::Paragraph)
+  if (dwBreakStatus == CFGAS_Char::BreakType::kParagraph)
     m_pTxtBreak->Reset();
-  if (!m_Styles.line_wrap_ && dwBreakStatus == CFX_BreakType::Line) {
+  if (!m_Styles.line_wrap_ && dwBreakStatus == CFGAS_Char::BreakType::kLine) {
     *pWidth += fLineWidth;
   } else {
     *pWidth = std::max(*pWidth, fLineWidth);
@@ -271,10 +272,10 @@
 }
 
 void CFDE_TextOut::DrawLogicText(CFX_RenderDevice* device,
-                                 WideStringView str,
+                                 const WideString& str,
                                  const CFX_RectF& rect) {
-  ASSERT(m_pFont);
-  ASSERT(m_fFontSize >= 1.0f);
+  DCHECK(m_pFont);
+  DCHECK(m_fFontSize >= 1.0f);
 
   if (str.IsEmpty())
     return;
@@ -286,7 +287,7 @@
   m_ttoLines.clear();
   m_wsText.clear();
 
-  LoadText(WideString(str), rect);
+  LoadText(str, rect);
   Reload(rect);
   DoAlignment(rect);
 
@@ -299,37 +300,34 @@
     device->SetClip_Rect(rtClip.GetOuterRect());
 
   for (auto& line : m_ttoLines) {
-    int32_t iPieces = line.GetSize();
-    for (int32_t j = 0; j < iPieces; j++) {
-      FDE_TTOPIECE* pPiece = line.GetPtrAt(j);
-      if (!pPiece)
+    for (size_t i = 0; i < line.GetSize(); ++i) {
+      const Piece* pPiece = line.GetPieceAtIndex(i);
+      size_t szCount = GetDisplayPos(pPiece);
+      if (szCount == 0)
         continue;
 
-      size_t szCount = GetDisplayPos(pPiece);
-      if (szCount > 0) {
-        CFDE_TextOut::DrawString(device, m_TxtColor, m_pFont,
-                                 {m_CharPos.data(), szCount}, m_fFontSize,
-                                 m_Matrix);
-      }
+      CFDE_TextOut::DrawString(device, m_TxtColor, m_pFont,
+                               {m_CharPos.data(), szCount}, m_fFontSize,
+                               m_Matrix);
     }
   }
   device->RestoreState(false);
 }
 
 void CFDE_TextOut::LoadText(const WideString& str, const CFX_RectF& rect) {
-  ASSERT(!str.IsEmpty());
+  DCHECK(!str.IsEmpty());
 
   m_wsText = str;
 
-  if (pdfium::CollectionSize<size_t>(m_CharWidths) < str.GetLength())
+  if (m_CharWidths.size() < str.GetLength())
     m_CharWidths.resize(str.GetLength(), 0);
 
-  float fLineStep = (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize;
+  float fLineStep = std::max(m_fLineSpace, m_fFontSize);
   float fLineStop = rect.bottom();
   m_fLinePos = rect.top;
-  int32_t iStartChar = 0;
+  size_t start_char = 0;
   int32_t iPieceWidths = 0;
-  CFX_BreakType dwBreakStatus;
+  CFGAS_Char::BreakType dwBreakStatus;
   bool bRet = false;
   for (const auto& wch : str) {
     dwBreakStatus = m_pTxtBreak->AppendChar(wch);
@@ -337,48 +335,56 @@
       continue;
 
     bool bEndofLine =
-        RetrievePieces(dwBreakStatus, false, rect, &iStartChar, &iPieceWidths);
-    if (bEndofLine &&
-        (m_Styles.line_wrap_ || dwBreakStatus == CFX_BreakType::Paragraph ||
-         dwBreakStatus == CFX_BreakType::Page)) {
+        RetrievePieces(dwBreakStatus, false, rect, &start_char, &iPieceWidths);
+    if (bEndofLine && (m_Styles.line_wrap_ ||
+                       dwBreakStatus == CFGAS_Char::BreakType::kParagraph ||
+                       dwBreakStatus == CFGAS_Char::BreakType::kPage)) {
       iPieceWidths = 0;
       ++m_iCurLine;
       m_fLinePos += fLineStep;
     }
     if (m_fLinePos + fLineStep > fLineStop) {
-      int32_t iCurLine = bEndofLine ? m_iCurLine - 1 : m_iCurLine;
-      m_ttoLines[iCurLine].SetNewReload(true);
+      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;
     }
   }
 
-  dwBreakStatus = m_pTxtBreak->EndBreak(CFX_BreakType::Paragraph);
+  dwBreakStatus = m_pTxtBreak->EndBreak(CFGAS_Char::BreakType::kParagraph);
   if (!CFX_BreakTypeNoneOrPiece(dwBreakStatus) && !bRet)
-    RetrievePieces(dwBreakStatus, false, rect, &iStartChar, &iPieceWidths);
+    RetrievePieces(dwBreakStatus, false, rect, &start_char, &iPieceWidths);
 
   m_pTxtBreak->ClearBreakPieces();
   m_pTxtBreak->Reset();
 }
 
-bool CFDE_TextOut::RetrievePieces(CFX_BreakType dwBreakStatus,
+bool CFDE_TextOut::RetrievePieces(CFGAS_Char::BreakType dwBreakStatus,
                                   bool bReload,
                                   const CFX_RectF& rect,
-                                  int32_t* pStartChar,
+                                  size_t* pStartChar,
                                   int32_t* pPieceWidths) {
-  float fLineStep = (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize;
+  float fLineStep = std::max(m_fLineSpace, m_fFontSize);
   bool bNeedReload = false;
   int32_t iLineWidth = FXSYS_roundf(rect.Width() * 20000.0f);
   int32_t iCount = m_pTxtBreak->CountBreakPieces();
+
+  size_t chars_to_skip = *pStartChar;
   for (int32_t i = 0; i < iCount; i++) {
-    const CFX_BreakPiece* pPiece = m_pTxtBreak->GetBreakPieceUnstable(i);
-    int32_t iPieceChars = pPiece->GetLength();
-    int32_t iChar = *pStartChar;
+    const CFGAS_BreakPiece* pPiece = m_pTxtBreak->GetBreakPieceUnstable(i);
+    size_t iPieceChars = pPiece->GetLength();
+    if (chars_to_skip > iPieceChars) {
+      chars_to_skip -= iPieceChars;
+      continue;
+    }
+
+    size_t iChar = *pStartChar;
     int32_t iWidth = 0;
-    int32_t j = 0;
+    size_t j = chars_to_skip;
     for (; j < iPieceChars; j++) {
-      const CFX_Char* pTC = pPiece->GetChar(j);
-      int32_t iCurCharWidth = pTC->m_iCharWidth > 0 ? pTC->m_iCharWidth : 0;
+      const CFGAS_Char* pTC = pPiece->GetChar(j);
+      int32_t iCurCharWidth = std::max(pTC->m_iCharWidth, 0);
       if (m_Styles.single_line_ || !m_Styles.line_wrap_) {
         if (iLineWidth - *pPieceWidths - iWidth < iCurCharWidth) {
           bNeedReload = true;
@@ -389,21 +395,22 @@
       m_CharWidths[iChar++] = iCurCharWidth;
     }
 
-    if (j == 0 && !bReload) {
-      m_ttoLines[m_iCurLine].SetNewReload(true);
-    } else if (j > 0) {
-      FDE_TTOPIECE ttoPiece;
-      ttoPiece.iStartChar = *pStartChar;
-      ttoPiece.iChars = j;
-      ttoPiece.dwCharStyles = pPiece->m_dwCharStyles;
-      ttoPiece.rtPiece = CFX_RectF(
-          rect.left + static_cast<float>(pPiece->m_iStartPos) / 20000.0f,
+    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;
+      piece.start_char = *pStartChar;
+      piece.char_count = j - chars_to_skip;
+      piece.char_styles = pPiece->GetCharStyles();
+      piece.bounds = CFX_RectF(
+          rect.left + static_cast<float>(pPiece->GetStartPos()) / 20000.0f,
           m_fLinePos, iWidth / 20000.0f, fLineStep);
 
-      if (FX_IsOdd(pPiece->m_iBidiLevel))
-        ttoPiece.dwCharStyles |= FX_TXTCHARSTYLE_OddBidiLevel;
+      if (FX_IsOdd(pPiece->GetBidiLevel()))
+        piece.char_styles |= FX_TXTCHARSTYLE_OddBidiLevel;
 
-      AppendPiece(ttoPiece, bNeedReload, (bReload && i == iCount - 1));
+      AppendPiece(piece, bNeedReload, (bReload && i == iCount - 1));
     }
     *pStartChar += iPieceChars;
     *pPieceWidths += iWidth;
@@ -411,26 +418,26 @@
   m_pTxtBreak->ClearBreakPieces();
 
   return m_Styles.single_line_ || m_Styles.line_wrap_ || bNeedReload ||
-         dwBreakStatus == CFX_BreakType::Paragraph;
+         dwBreakStatus == CFGAS_Char::BreakType::kParagraph;
 }
 
-void CFDE_TextOut::AppendPiece(const FDE_TTOPIECE& ttoPiece,
+void CFDE_TextOut::AppendPiece(const Piece& piece,
                                bool bNeedReload,
                                bool bEnd) {
-  if (m_iCurLine >= pdfium::CollectionSize<int32_t>(m_ttoLines)) {
-    CFDE_TTOLine ttoLine;
-    ttoLine.SetNewReload(bNeedReload);
+  if (m_iCurLine >= m_ttoLines.size()) {
+    Line ttoLine;
+    ttoLine.set_new_reload(bNeedReload);
 
-    m_iCurPiece = ttoLine.AddPiece(m_iCurPiece, ttoPiece);
+    m_iCurPiece = ttoLine.AddPiece(m_iCurPiece, piece);
     m_ttoLines.push_back(ttoLine);
-    m_iCurLine = pdfium::CollectionSize<int32_t>(m_ttoLines) - 1;
+    m_iCurLine = m_ttoLines.size() - 1;
   } else {
-    CFDE_TTOLine* pLine = &m_ttoLines[m_iCurLine];
-    pLine->SetNewReload(bNeedReload);
+    Line* pLine = &m_ttoLines[m_iCurLine];
+    pLine->set_new_reload(bNeedReload);
 
-    m_iCurPiece = pLine->AddPiece(m_iCurPiece, ttoPiece);
+    m_iCurPiece = pLine->AddPiece(m_iCurPiece, piece);
     if (bEnd) {
-      int32_t iPieces = pLine->GetSize();
+      size_t iPieces = pLine->GetSize();
       if (m_iCurPiece < iPieces)
         pLine->RemoveLast(iPieces - m_iCurPiece - 1);
     }
@@ -440,9 +447,9 @@
 }
 
 void CFDE_TextOut::Reload(const CFX_RectF& rect) {
-  int i = 0;
+  size_t i = 0;
   for (auto& line : m_ttoLines) {
-    if (line.GetNewReload()) {
+    if (line.new_reload()) {
       m_iCurLine = i;
       m_iCurPiece = 0;
       ReloadLinePiece(&line, rect);
@@ -451,32 +458,29 @@
   }
 }
 
-void CFDE_TextOut::ReloadLinePiece(CFDE_TTOLine* pLine, const CFX_RectF& rect) {
+void CFDE_TextOut::ReloadLinePiece(Line* line, const CFX_RectF& rect) {
   pdfium::span<const wchar_t> text_span = m_wsText.span();
-  FDE_TTOPIECE* pPiece = pLine->GetPtrAt(0);
-  int32_t iStartChar = pPiece->iStartChar;
-  int32_t iPieceCount = pLine->GetSize();
-  int32_t iPieceWidths = 0;
-  int32_t iPieceIndex = 0;
-  CFX_BreakType dwBreakStatus = CFX_BreakType::None;
-  m_fLinePos = pPiece->rtPiece.top;
-  while (iPieceIndex < iPieceCount) {
-    int32_t iStart = iStartChar;
-    int32_t iEnd = pPiece->iChars + iStart;
-    while (iStart < iEnd) {
-      dwBreakStatus = m_pTxtBreak->AppendChar(text_span[iStart]);
-      if (!CFX_BreakTypeNoneOrPiece(dwBreakStatus))
-        RetrievePieces(dwBreakStatus, true, rect, &iStartChar, &iPieceWidths);
+  size_t start_char = 0;
+  size_t piece_count = line->GetSize();
+  int32_t piece_widths = 0;
+  CFGAS_Char::BreakType break_status = CFGAS_Char::BreakType::kNone;
+  for (size_t piece_index = 0; piece_index < piece_count; ++piece_index) {
+    const Piece* piece = line->GetPieceAtIndex(piece_index);
+    if (piece_index == 0)
+      m_fLinePos = piece->bounds.top;
 
-      ++iStart;
+    start_char = piece->start_char;
+    const size_t end = piece->start_char + piece->char_count;
+    for (size_t char_index = start_char; char_index < end; ++char_index) {
+      break_status = m_pTxtBreak->AppendChar(text_span[char_index]);
+      if (!CFX_BreakTypeNoneOrPiece(break_status))
+        RetrievePieces(break_status, true, rect, &start_char, &piece_widths);
     }
-    ++iPieceIndex;
-    pPiece = pLine->GetPtrAt(iPieceIndex);
   }
 
-  dwBreakStatus = m_pTxtBreak->EndBreak(CFX_BreakType::Paragraph);
-  if (!CFX_BreakTypeNoneOrPiece(dwBreakStatus))
-    RetrievePieces(dwBreakStatus, true, rect, &iStartChar, &iPieceWidths);
+  break_status = m_pTxtBreak->EndBreak(CFGAS_Char::BreakType::kParagraph);
+  if (!CFX_BreakTypeNoneOrPiece(break_status))
+    RetrievePieces(break_status, true, rect, &start_char, &piece_widths);
 
   m_pTxtBreak->Reset();
 }
@@ -485,11 +489,11 @@
   if (m_ttoLines.empty())
     return;
 
-  FDE_TTOPIECE* pFirstPiece = m_ttoLines.back().GetPtrAt(0);
+  const Piece* pFirstPiece = m_ttoLines.back().GetPieceAtIndex(0);
   if (!pFirstPiece)
     return;
 
-  float fInc = rect.bottom() - pFirstPiece->rtPiece.bottom();
+  float fInc = rect.bottom() - pFirstPiece->bounds.bottom();
   if (TextAlignmentVerticallyCentered(m_iAlignment))
     fInc /= 2.0f;
   else if (IsTextAlignmentTop(m_iAlignment))
@@ -499,64 +503,59 @@
     return;
 
   for (auto& line : m_ttoLines) {
-    int32_t iPieces = line.GetSize();
-    for (int32_t j = 0; j < iPieces; j++)
-      line.GetPtrAt(j)->rtPiece.top += fInc;
+    for (size_t i = 0; i < line.GetSize(); ++i)
+      line.GetPieceAtIndex(i)->bounds.top += fInc;
   }
 }
 
-size_t CFDE_TextOut::GetDisplayPos(FDE_TTOPIECE* pPiece) {
-  ASSERT(pPiece->iChars >= 0);
+size_t CFDE_TextOut::GetDisplayPos(const Piece* pPiece) {
+  if (m_CharPos.size() < pPiece->char_count)
+    m_CharPos.resize(pPiece->char_count, TextCharPos());
 
-  if (pdfium::CollectionSize<int32_t>(m_CharPos) < pPiece->iChars)
-    m_CharPos.resize(pPiece->iChars, TextCharPos());
-
-  CFX_TxtBreak::Run tr;
-  tr.wsStr = m_wsText + pPiece->iStartChar;
-  tr.pWidths = &m_CharWidths[pPiece->iStartChar];
-  tr.iLength = pPiece->iChars;
+  CFGAS_TxtBreak::Run tr;
+  tr.wsStr = m_wsText.Substr(pPiece->start_char);
+  tr.pWidths = &m_CharWidths[pPiece->start_char];
+  tr.iLength = pdfium::base::checked_cast<int32_t>(pPiece->char_count);
   tr.pFont = m_pFont;
   tr.fFontSize = m_fFontSize;
   tr.dwStyles = m_dwTxtBkStyles;
-  tr.dwCharStyles = pPiece->dwCharStyles;
-  tr.pRect = &pPiece->rtPiece;
+  tr.dwCharStyles = pPiece->char_styles;
+  tr.pRect = &pPiece->bounds;
 
-  return m_pTxtBreak->GetDisplayPos(&tr, m_CharPos.data());
+  return m_pTxtBreak->GetDisplayPos(tr, m_CharPos.data());
 }
 
-CFDE_TextOut::CFDE_TTOLine::CFDE_TTOLine() : m_bNewReload(false) {}
+CFDE_TextOut::Line::Line() = default;
 
-CFDE_TextOut::CFDE_TTOLine::CFDE_TTOLine(const CFDE_TTOLine& ttoLine)
-    : m_pieces(5) {
-  m_bNewReload = ttoLine.m_bNewReload;
-  m_pieces = ttoLine.m_pieces;
-}
+CFDE_TextOut::Line::Line(const Line& that)
+    : new_reload_(that.new_reload_), pieces_(that.pieces_) {}
 
-CFDE_TextOut::CFDE_TTOLine::~CFDE_TTOLine() {}
+CFDE_TextOut::Line::~Line() = default;
 
-int32_t CFDE_TextOut::CFDE_TTOLine::AddPiece(int32_t index,
-                                             const FDE_TTOPIECE& ttoPiece) {
-  if (index >= pdfium::CollectionSize<int32_t>(m_pieces)) {
-    m_pieces.push_back(ttoPiece);
-    return pdfium::CollectionSize<int32_t>(m_pieces);
+size_t CFDE_TextOut::Line::AddPiece(size_t index, const Piece& piece) {
+  if (index >= pieces_.size()) {
+    pieces_.push_back(piece);
+    return pieces_.size();
   }
-  m_pieces[index] = ttoPiece;
+  pieces_[index] = piece;
   return index;
 }
 
-int32_t CFDE_TextOut::CFDE_TTOLine::GetSize() const {
-  return pdfium::CollectionSize<int32_t>(m_pieces);
+size_t CFDE_TextOut::Line::GetSize() const {
+  return pieces_.size();
 }
 
-FDE_TTOPIECE* CFDE_TextOut::CFDE_TTOLine::GetPtrAt(int32_t index) {
-  return pdfium::IndexInBounds(m_pieces, index) ? &m_pieces[index] : nullptr;
+const CFDE_TextOut::Piece* CFDE_TextOut::Line::GetPieceAtIndex(
+    size_t index) const {
+  CHECK(fxcrt::IndexInBounds(pieces_, index));
+  return &pieces_[index];
 }
 
-void CFDE_TextOut::CFDE_TTOLine::RemoveLast(int32_t icount) {
-  if (icount < 0)
-    return;
-  m_pieces.erase(
-      m_pieces.end() -
-          std::min(icount, pdfium::CollectionSize<int32_t>(m_pieces)),
-      m_pieces.end());
+CFDE_TextOut::Piece* CFDE_TextOut::Line::GetPieceAtIndex(size_t index) {
+  CHECK(fxcrt::IndexInBounds(pieces_, index));
+  return &pieces_[index];
+}
+
+void CFDE_TextOut::Line::RemoveLast(size_t count) {
+  pieces_.erase(pieces_.end() - std::min(count, pieces_.size()), pieces_.end());
 }
diff --git a/xfa/fde/cfde_textout.h b/xfa/fde/cfde_textout.h
index bb65d28..af2308b 100644
--- a/xfa/fde/cfde_textout.h
+++ b/xfa/fde/cfde_textout.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,28 +11,19 @@
 #include <memory>
 #include <vector>
 
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/span.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/containers/span.h"
 #include "xfa/fde/cfde_data.h"
-#include "xfa/fgas/layout/cfx_char.h"
+#include "xfa/fgas/layout/cfgas_break.h"
+#include "xfa/fgas/layout/cfgas_char.h"
 
-class CFDE_RenderDevice;
 class CFGAS_GEFont;
+class CFGAS_TxtBreak;
 class CFX_RenderDevice;
-class CFX_TxtBreak;
 class TextCharPos;
 
-struct FDE_TTOPIECE {
-  FDE_TTOPIECE();
-  FDE_TTOPIECE(const FDE_TTOPIECE& that);
-  ~FDE_TTOPIECE();
-
-  int32_t iStartChar;
-  int32_t iChars;
-  uint32_t dwCharStyles;
-  CFX_RectF rtPiece;
-};
-
 class CFDE_TextOut {
  public:
   static bool DrawString(CFX_RenderDevice* device,
@@ -45,7 +36,7 @@
   CFDE_TextOut();
   ~CFDE_TextOut();
 
-  void SetFont(const RetainPtr<CFGAS_GEFont>& pFont);
+  void SetFont(RetainPtr<CFGAS_GEFont> pFont);
   void SetFontSize(float fFontSize);
   void SetTextColor(FX_ARGB color) { m_TxtColor = color; }
   void SetStyles(const FDE_TextStyle& dwStyles);
@@ -57,47 +48,60 @@
   void CalcLogicSize(WideStringView str, CFX_SizeF* pSize);
   void CalcLogicSize(WideStringView str, CFX_RectF* pRect);
   void DrawLogicText(CFX_RenderDevice* device,
-                     WideStringView str,
+                     const WideString& str,
                      const CFX_RectF& rect);
   int32_t GetTotalLines() const { return m_iTotalLines; }
 
  private:
-  class CFDE_TTOLine {
-   public:
-    CFDE_TTOLine();
-    CFDE_TTOLine(const CFDE_TTOLine& ttoLine);
-    ~CFDE_TTOLine();
+  struct Piece {
+    Piece();
+    Piece(const Piece& that);
+    ~Piece();
 
-    bool GetNewReload() const { return m_bNewReload; }
-    void SetNewReload(bool reload) { m_bNewReload = reload; }
-    int32_t AddPiece(int32_t index, const FDE_TTOPIECE& ttoPiece);
-    int32_t GetSize() const;
-    FDE_TTOPIECE* GetPtrAt(int32_t index);
-    void RemoveLast(int32_t iCount);
-
-   private:
-    bool m_bNewReload;
-    std::deque<FDE_TTOPIECE> m_pieces;
+    size_t start_char = 0;
+    size_t char_count = 0;
+    uint32_t char_styles = 0;
+    CFX_RectF bounds;
   };
 
-  bool RetrieveLineWidth(CFX_BreakType dwBreakStatus,
+  class Line {
+   public:
+    Line();
+    Line(const Line& that);
+    ~Line();
+
+    bool new_reload() const { return new_reload_; }
+    void set_new_reload(bool reload) { new_reload_ = reload; }
+
+    size_t AddPiece(size_t index, const Piece& piece);
+    size_t GetSize() const;
+    const Piece* GetPieceAtIndex(size_t index) const;
+    Piece* GetPieceAtIndex(size_t index);
+    void RemoveLast(size_t count);
+
+   private:
+    bool new_reload_ = false;
+    std::deque<Piece> pieces_;
+  };
+
+  bool RetrieveLineWidth(CFGAS_Char::BreakType dwBreakStatus,
                          float* pStartPos,
                          float* pWidth,
                          float* pHeight);
   void LoadText(const WideString& str, const CFX_RectF& rect);
 
   void Reload(const CFX_RectF& rect);
-  void ReloadLinePiece(CFDE_TTOLine* pLine, const CFX_RectF& rect);
-  bool RetrievePieces(CFX_BreakType dwBreakStatus,
+  void ReloadLinePiece(Line* pLine, const CFX_RectF& rect);
+  bool RetrievePieces(CFGAS_Char::BreakType dwBreakStatus,
                       bool bReload,
                       const CFX_RectF& rect,
-                      int32_t* pStartChar,
+                      size_t* pStartChar,
                       int32_t* pPieceWidths);
-  void AppendPiece(const FDE_TTOPIECE& ttoPiece, bool bNeedReload, bool bEnd);
+  void AppendPiece(const Piece& piece, bool bNeedReload, bool bEnd);
   void DoAlignment(const CFX_RectF& rect);
-  size_t GetDisplayPos(FDE_TTOPIECE* pPiece);
+  size_t GetDisplayPos(const Piece* pPiece);
 
-  std::unique_ptr<CFX_TxtBreak> const m_pTxtBreak;
+  std::unique_ptr<CFGAS_TxtBreak> const m_pTxtBreak;
   RetainPtr<CFGAS_GEFont> m_pFont;
   float m_fFontSize = 12.0f;
   float m_fLineSpace = 12.0f;
@@ -107,12 +111,12 @@
   FDE_TextStyle m_Styles;
   std::vector<int32_t> m_CharWidths;
   FX_ARGB m_TxtColor = 0xFF000000;
-  uint32_t m_dwTxtBkStyles = 0;
+  Mask<CFGAS_Break::LayoutStyle> m_dwTxtBkStyles;
   WideString m_wsText;
   CFX_Matrix m_Matrix;
-  std::deque<CFDE_TTOLine> m_ttoLines;
-  int32_t m_iCurLine = 0;
-  int32_t m_iCurPiece = 0;
+  std::deque<Line> m_ttoLines;
+  size_t m_iCurLine = 0;
+  size_t m_iCurPiece = 0;
   int32_t m_iTotalLines = 0;
   std::vector<TextCharPos> m_CharPos;
 };
diff --git a/xfa/fde/cfde_textout_unittest.cpp b/xfa/fde/cfde_textout_unittest.cpp
new file mode 100644
index 0000000..0067302
--- /dev/null
+++ b/xfa/fde/cfde_textout_unittest.cpp
@@ -0,0 +1,177 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fde/cfde_textout.h"
+
+#include <memory>
+
+#include "build/build_config.h"
+#include "core/fdrm/fx_crypt.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/utils/hash.h"
+#include "xfa/fgas/font/cfgas_fontmgr.h"
+#include "xfa/fgas/font/cfgas_gefont.h"
+#include "xfa/fgas/font/cfgas_gemodule.h"
+
+class CFDETextOutTest : public testing::Test {
+ public:
+  CFDETextOutTest() = default;
+  ~CFDETextOutTest() override = default;
+
+  void SetUp() override {
+    CFX_Size bitmap_size = GetBitmapSize();
+    bitmap_ = pdfium::MakeRetain<CFX_DIBitmap>();
+    ASSERT_TRUE(bitmap_->Create(bitmap_size.width, bitmap_size.height,
+                                FXDIB_Format::kArgb));
+
+    device_ = std::make_unique<CFX_DefaultRenderDevice>();
+    device_->Attach(bitmap_);
+
+    font_ = LoadFont();
+    ASSERT_TRUE(font_);
+
+    text_out_ = std::make_unique<CFDE_TextOut>();
+    text_out_->SetFont(font_);
+    text_out_->SetFontSize(12.0f);
+
+    EXPECT_STREQ(GetEmptyBitmapChecksum(), GetBitmapChecksum().c_str());
+  }
+
+  void TearDown() override {
+    text_out_.reset();
+    font_.Reset();
+    device_.reset();
+    bitmap_.Reset();
+  }
+
+  virtual RetainPtr<CFGAS_GEFont> LoadFont() {
+    const wchar_t kFontFamily[] = L"Arimo Bold";
+    return CFGAS_GEFont::LoadFont(kFontFamily, /*dwFontStyles=*/0,
+                                  FX_CodePage::kDefANSI);
+  }
+
+  virtual CFX_Size GetBitmapSize() { return CFX_Size(200, 100); }
+
+  virtual const char* GetEmptyBitmapChecksum() {
+    static const char kEmptyBitmapChecksum[] =
+        "a042237c5493fdb9656b94a83608d11a";
+    return kEmptyBitmapChecksum;
+  }
+
+  CFX_DefaultRenderDevice* device() { return device_.get(); }
+  CFDE_TextOut& text_out() { return *text_out_; }
+
+  ByteString GetBitmapChecksum() {
+    CRYPT_md5_context context = CRYPT_MD5Start();
+    for (int i = 0; i < bitmap_->GetHeight(); ++i)
+      CRYPT_MD5Update(&context, bitmap_->GetScanline(i));
+    uint8_t digest[16];
+    CRYPT_MD5Finish(&context, digest);
+    return ByteString(CryptToBase16(digest).c_str());
+  }
+
+ private:
+  RetainPtr<CFX_DIBitmap> bitmap_;
+  std::unique_ptr<CFX_DefaultRenderDevice> device_;
+  RetainPtr<CFGAS_GEFont> font_;
+  std::unique_ptr<CFDE_TextOut> text_out_;
+};
+
+TEST_F(CFDETextOutTest, DrawLogicTextBasic) {
+  text_out().DrawLogicText(device(), L"foo", CFX_RectF(0, 0, 2100, 100));
+  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) {
+  text_out().DrawLogicText(device(), L"foo", CFX_RectF());
+  EXPECT_STREQ(GetEmptyBitmapChecksum(), GetBitmapChecksum().c_str());
+}
+
+#if !BUILDFLAG(IS_WIN)
+// This test depends on a particular font being present.
+class CFDETextOutLargeBitmapTest : public CFDETextOutTest {
+ public:
+  CFDETextOutLargeBitmapTest() = default;
+  ~CFDETextOutLargeBitmapTest() override = default;
+
+  RetainPtr<CFGAS_GEFont> LoadFont() override {
+    const wchar_t kFontFamily[] = L"DejaVu Sans";
+    auto* font_manager = CFGAS_GEModule::Get()->GetFontMgr();
+    return font_manager->LoadFont(kFontFamily, /*dwFontStyles=*/0,
+                                  FX_CodePage::kFailure);
+  }
+
+  CFX_Size GetBitmapSize() override { return CFX_Size(2100, 20); }
+
+  const char* GetEmptyBitmapChecksum() override {
+    static const char kEmptyLargeBitmapChecksum[] =
+        "101745f76351fd5d916bf3817b71563c";
+    return kEmptyLargeBitmapChecksum;
+  }
+
+  const char* GetLargeTextBlobChecksum() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "cd357c6afbf17bb2ac48817df5d9eaad";
+    }
+    return "268b71a8660b51e31c6bf30fc7ff1e08";
+  }
+};
+
+TEST_F(CFDETextOutLargeBitmapTest, DrawLogicTextBug953881) {
+  FDE_TextStyle styles;
+  styles.single_line_ = true;
+  text_out().SetStyles(styles);
+  text_out().SetAlignment(FDE_TextAlignment::kCenterLeft);
+  text_out().SetFontSize(10.0f);
+
+  static const wchar_t kText[] =
+      L"SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS"
+      L"SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSssssssssss"
+      L"sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
+      L"sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
+      L"sssssssssssssssssssssssssssssssssssssssssssssssssnnnnnnnnnnn"
+      "\xfeba"
+      L"Sssssssssssssssssss"
+      "\xfeba"
+      L"iiiiisssss";
+  text_out().DrawLogicText(device(), WideString(kText),
+                           CFX_RectF(3, 3, 2048, 10));
+  EXPECT_STREQ(GetLargeTextBlobChecksum(), GetBitmapChecksum().c_str());
+}
+
+TEST_F(CFDETextOutLargeBitmapTest, DrawLogicTextBug1342078) {
+  FDE_TextStyle styles;
+  styles.single_line_ = true;
+  text_out().SetStyles(styles);
+  text_out().SetAlignment(FDE_TextAlignment::kCenterLeft);
+  text_out().SetFontSize(10.0f);
+
+  static const wchar_t kText[] =
+      L"SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS"
+      L"SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSssssssssss"
+      L"sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
+      L"sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
+      L"sssssssssssssssssssssssssssssssssssssssssssssssssnnnnnnnnnnn"
+      "\xfeba"
+      L"Sssssssssssssssssss"
+      "\xfeba"
+      L"iiiiiiiiiisssss";
+  text_out().DrawLogicText(device(), WideString(kText),
+                           CFX_RectF(3, 3, 2048, 10));
+  EXPECT_STREQ(GetLargeTextBlobChecksum(), GetBitmapChecksum().c_str());
+}
+#endif  // !BUILDFLAG(IS_WIN)
diff --git a/xfa/fde/cfde_wordbreak_data.cpp b/xfa/fde/cfde_wordbreak_data.cpp
index 9331253..59b23b6 100644
--- a/xfa/fde/cfde_wordbreak_data.cpp
+++ b/xfa/fde/cfde_wordbreak_data.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,10 @@
 
 #include "xfa/fde/cfde_wordbreak_data.h"
 
-#include "core/fxcrt/fx_memory.h"
+#include <iterator>
+
 #include "core/fxcrt/fx_system.h"
+#include "third_party/base/check.h"
 
 namespace {
 
@@ -2818,14 +2820,14 @@
 
 bool FX_CheckStateChangeForWordBreak(WordBreakProperty from,
                                      WordBreakProperty to) {
-  ASSERT(static_cast<int>(from) < 13);
+  DCHECK(static_cast<int>(from) < 13);
   return !!(kWordBreakTable[static_cast<int>(from)] &
             static_cast<uint16_t>(1 << static_cast<int>(to)));
 }
 
 WordBreakProperty FX_GetWordBreakProperty(wchar_t wcCodePoint) {
   size_t index = static_cast<size_t>(wcCodePoint) / 2;
-  if (index >= FX_ArraySize(kCodePointProperties))
+  if (index >= std::size(kCodePointProperties))
     return WordBreakProperty::kNone;
 
   uint8_t dwProperty = kCodePointProperties[index];
diff --git a/xfa/fde/cfde_wordbreak_data.h b/xfa/fde/cfde_wordbreak_data.h
index 1465f5c..537254b 100644
--- a/xfa/fde/cfde_wordbreak_data.h
+++ b/xfa/fde/cfde_wordbreak_data.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fgas/BUILD.gn b/xfa/fgas/BUILD.gn
deleted file mode 100644
index c4c68b2..0000000
--- a/xfa/fgas/BUILD.gn
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("../../pdfium.gni")
-import("../../testing/test.gni")
-
-assert(pdf_enable_xfa)
-
-source_set("fgas") {
-  sources = [
-    "crt/cfgas_decimal.cpp",
-    "crt/cfgas_decimal.h",
-    "crt/cfgas_stringformatter.cpp",
-    "crt/cfgas_stringformatter.h",
-    "crt/locale_iface.h",
-    "crt/locale_mgr_iface.h",
-    "font/cfgas_defaultfontmanager.cpp",
-    "font/cfgas_defaultfontmanager.h",
-    "font/cfgas_fontmgr.cpp",
-    "font/cfgas_fontmgr.h",
-    "font/cfgas_gefont.cpp",
-    "font/cfgas_gefont.h",
-    "font/cfgas_pdffontmgr.cpp",
-    "font/cfgas_pdffontmgr.h",
-    "font/fgas_fontutils.cpp",
-    "font/fgas_fontutils.h",
-  ]
-  deps = [
-    "../../core/fpdfapi/font",
-    "../../core/fpdfapi/page",
-    "../../core/fpdfapi/parser",
-    "../../core/fxcrt",
-    "../../core/fxge",
-  ]
-  configs += [
-    "../../:pdfium_core_config",
-    "../:xfa_warnings",
-  ]
-  visibility = [ "../../*" ]
-
-  if (!is_win) {
-    sources += [
-      "font/cfx_fontsourceenum_file.cpp",
-      "font/cfx_fontsourceenum_file.h",
-    ]
-  }
-}
-
-pdfium_unittest_source_set("unittests") {
-  sources = [
-    "crt/cfgas_decimal_unittest.cpp",
-    "crt/cfgas_stringformatter_unittest.cpp",
-  ]
-  deps = [
-    ":fgas",
-    "../../core/fpdfapi/page",
-    "../fxfa/parser",
-  ]
-  pdfium_root_dir = "../../"
-}
diff --git a/xfa/fgas/crt/BUILD.gn b/xfa/fgas/crt/BUILD.gn
new file mode 100644
index 0000000..57a0212
--- /dev/null
+++ b/xfa/fgas/crt/BUILD.gn
@@ -0,0 +1,43 @@
+# Copyright 2018 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../../pdfium.gni")
+import("../../../testing/test.gni")
+
+assert(pdf_enable_xfa)
+
+source_set("crt") {
+  sources = [
+    "cfgas_decimal.cpp",
+    "cfgas_decimal.h",
+    "cfgas_stringformatter.cpp",
+    "cfgas_stringformatter.h",
+    "locale_iface.h",
+    "locale_mgr_iface.h",
+  ]
+  configs += [
+    "../../../:pdfium_strict_config",
+    "../../../:pdfium_noshorten_config",
+    "../../:xfa_warnings",
+  ]
+  deps = [
+    "../../../core/fpdfapi/page",
+    "../../../core/fxcrt",
+  ]
+  visibility = [ "../../../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [
+    "cfgas_decimal_unittest.cpp",
+    "cfgas_stringformatter_unittest.cpp",
+  ]
+  deps = [
+    ":crt",
+    "../../../core/fpdfapi/page",
+    "../../../fxjs:gc",
+    "../../fxfa/parser:parser",
+  ]
+  pdfium_root_dir = "../../../"
+}
diff --git a/xfa/fgas/crt/cfgas_decimal.cpp b/xfa/fgas/crt/cfgas_decimal.cpp
index 6a8a759..dfe5552 100644
--- a/xfa/fgas/crt/cfgas_decimal.cpp
+++ b/xfa/fgas/crt/cfgas_decimal.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,11 +6,14 @@
 
 #include "xfa/fgas/crt/cfgas_decimal.h"
 
+#include <math.h>
+
 #include <algorithm>
 #include <limits>
 #include <utility>
 
 #include "core/fxcrt/fx_extension.h"
+#include "third_party/base/check.h"
 
 #define FXMATH_DECIMAL_SCALELIMIT 0x1c
 #define FXMATH_DECIMAL_RSHIFT32BIT(x) ((x) >> 0x10 >> 0x10)
@@ -118,7 +121,7 @@
                                    uint8_t bl,
                                    uint64_t c[],
                                    uint8_t cl) {
-  ASSERT(al + bl <= cl);
+  DCHECK(al + bl <= cl);
   for (int i = 0; i < cl; i++)
     c[i] = 0;
 
@@ -295,13 +298,10 @@
   bool pointmet = false;
   bool negmet = false;
   uint8_t scale = 0;
-  m_uHi = 0;
-  m_uMid = 0;
-  m_uLo = 0;
   while (str != strBound && *str == ' ')
     str++;
   if (str != strBound && *str == '-') {
-    negmet = 1;
+    negmet = true;
     str++;
   } else if (str != strBound && *str == '+') {
     str++;
@@ -311,7 +311,7 @@
          scale < FXMATH_DECIMAL_SCALELIMIT) {
     if (*str == '.') {
       if (!pointmet)
-        pointmet = 1;
+        pointmet = true;
     } else {
       m_uHi = m_uHi * 0xA + FXMATH_DECIMAL_RSHIFT32BIT((uint64_t)m_uMid * 0xA);
       m_uMid = m_uMid * 0xA + FXMATH_DECIMAL_RSHIFT32BIT((uint64_t)m_uLo * 0xA);
@@ -434,7 +434,7 @@
 
   uint8_t minscale = scale;
   if (!IsNotZero())
-    return CFGAS_Decimal(0, 0, 0, 0, minscale);
+    return CFGAS_Decimal(0, 0, 0, false, minscale);
 
   while (!a[6]) {
     decimal_helper_mul10_any(a, 7);
diff --git a/xfa/fgas/crt/cfgas_decimal.h b/xfa/fgas/crt/cfgas_decimal.h
index 4751c5b..0e2b7b9 100644
--- a/xfa/fgas/crt/cfgas_decimal.h
+++ b/xfa/fgas/crt/cfgas_decimal.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #ifndef XFA_FGAS_CRT_CFGAS_DECIMAL_H_
 #define XFA_FGAS_CRT_CFGAS_DECIMAL_H_
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 
 class CFGAS_Decimal {
  public:
diff --git a/xfa/fgas/crt/cfgas_decimal_unittest.cpp b/xfa/fgas/crt/cfgas_decimal_unittest.cpp
index 816045e..ed57e83 100644
--- a/xfa/fgas/crt/cfgas_decimal_unittest.cpp
+++ b/xfa/fgas/crt/cfgas_decimal_unittest.cpp
@@ -1,9 +1,11 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "xfa/fgas/crt/cfgas_decimal.h"
 
+#include <math.h>
+
 #include <limits>
 
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/xfa/fgas/crt/cfgas_stringformatter.cpp b/xfa/fgas/crt/cfgas_stringformatter.cpp
index 1c640b9..d073dd7 100644
--- a/xfa/fgas/crt/cfgas_stringformatter.cpp
+++ b/xfa/fgas/crt/cfgas_stringformatter.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,62 +6,57 @@
 
 #include "xfa/fgas/crt/cfgas_stringformatter.h"
 
+#include <math.h>
+
 #include <algorithm>
 #include <limits>
 #include <utility>
 #include <vector>
 
+#include "core/fxcrt/cfx_datetime.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/notreached.h"
 #include "xfa/fgas/crt/cfgas_decimal.h"
+#include "xfa/fgas/crt/locale_mgr_iface.h"
 
 // NOTE: Code uses the convention for backwards-looping with unsigned types
 // that exploits the well-defined behaviour for unsigned underflow (and hence
 // the standard x < size() can be used in all cases to validate indices).
 
-#define FX_LOCALECATEGORY_DateHash 0xbde9abde
-#define FX_LOCALECATEGORY_TimeHash 0x2d71b00f
-#define FX_LOCALECATEGORY_DateTimeHash 0x158c72ed
-#define FX_LOCALECATEGORY_NumHash 0x0b4ff870
-#define FX_LOCALECATEGORY_TextHash 0x2d08af85
-#define FX_LOCALECATEGORY_ZeroHash 0x568cb500
-#define FX_LOCALECATEGORY_NullHash 0x052931bb
-
 #define FX_NUMSTYLE_Percent 0x01
 #define FX_NUMSTYLE_Exponent 0x02
 #define FX_NUMSTYLE_DotVorv 0x04
 
 namespace {
 
-struct LocaleDateTimeSubCategoryWithHash {
+struct LocaleDateTimeSubcategoryWithHash {
   uint32_t uHash;  // Hashed as wide string.
-  FX_LOCALEDATETIMESUBCATEGORY eSubCategory;
+  LocaleIface::DateTimeSubcategory eSubCategory;
 };
 
-struct LocaleNumberSubCategoryWithHash {
+struct LocaleNumberSubcategoryWithHash {
   uint32_t uHash;  // Hashed as wide string.
-  FX_LOCALENUMSUBCATEGORY eSubCategory;
+  LocaleIface::NumSubcategory eSubCategory;
 };
 
 #undef SUBC
 #define SUBC(a, b, c) a, c
-
-const LocaleDateTimeSubCategoryWithHash g_FXLocaleDateTimeSubCatData[] = {
-    {SUBC(0x14da2125, "default", FX_LOCALEDATETIMESUBCATEGORY_Default)},
-    {SUBC(0x9041d4b0, "short", FX_LOCALEDATETIMESUBCATEGORY_Short)},
-    {SUBC(0xa084a381, "medium", FX_LOCALEDATETIMESUBCATEGORY_Medium)},
-    {SUBC(0xcdce56b3, "full", FX_LOCALEDATETIMESUBCATEGORY_Full)},
-    {SUBC(0xf6b4afb0, "long", FX_LOCALEDATETIMESUBCATEGORY_Long)},
+constexpr LocaleDateTimeSubcategoryWithHash kLocaleDateTimeSubcategoryData[] = {
+    {SUBC(0x14da2125, "default", LocaleIface::DateTimeSubcategory::kDefault)},
+    {SUBC(0x9041d4b0, "short", LocaleIface::DateTimeSubcategory::kShort)},
+    {SUBC(0xa084a381, "medium", LocaleIface::DateTimeSubcategory::kMedium)},
+    {SUBC(0xcdce56b3, "full", LocaleIface::DateTimeSubcategory::kFull)},
+    {SUBC(0xf6b4afb0, "long", LocaleIface::DateTimeSubcategory::kLong)},
 };
 
-const LocaleNumberSubCategoryWithHash g_FXLocaleNumSubCatData[] = {
-    {SUBC(0x46f95531, "percent", FX_LOCALENUMPATTERN_Percent)},
-    {SUBC(0x4c4e8acb, "currency", FX_LOCALENUMPATTERN_Currency)},
-    {SUBC(0x54034c2f, "decimal", FX_LOCALENUMPATTERN_Decimal)},
-    {SUBC(0x7568e6ae, "integer", FX_LOCALENUMPATTERN_Integer)},
+constexpr LocaleNumberSubcategoryWithHash kLocaleNumSubcategoryData[] = {
+    {SUBC(0x46f95531, "percent", LocaleIface::NumSubcategory::kPercent)},
+    {SUBC(0x4c4e8acb, "currency", LocaleIface::NumSubcategory::kCurrency)},
+    {SUBC(0x54034c2f, "decimal", LocaleIface::NumSubcategory::kDecimal)},
+    {SUBC(0x7568e6ae, "integer", LocaleIface::NumSubcategory::kInteger)},
 };
-
 #undef SUBC
 
 struct FX_LOCALETIMEZONEINFO {
@@ -70,18 +65,25 @@
   int16_t iMinute;
 };
 
-const FX_LOCALETIMEZONEINFO g_FXLocaleTimeZoneData[] = {
+constexpr FX_LOCALETIMEZONEINFO kFXLocaleTimeZoneData[] = {
     {L"CDT", -5, 0}, {L"CST", -6, 0}, {L"EDT", -4, 0}, {L"EST", -5, 0},
     {L"MDT", -6, 0}, {L"MST", -7, 0}, {L"PDT", -7, 0}, {L"PST", -8, 0},
 };
 
-const wchar_t kTimeSymbols[] = L"hHkKMSFAzZ";
-const wchar_t kDateSymbols[] = L"DJMEeGgYwW";
-const wchar_t kConstChars[] = L",-:/. ";
+constexpr wchar_t kTimeSymbols[] = L"hHkKMSFAzZ";
+constexpr wchar_t kDateSymbols[] = L"DJMEeGgYwW";
+constexpr wchar_t kConstChars[] = L",-:/. ";
 
-size_t ParseTimeZone(pdfium::span<const wchar_t> spStr, FX_TIMEZONE* tz) {
-  tz->tzHour = 0;
-  tz->tzMinute = 0;
+constexpr wchar_t kDateStr[] = L"date";
+constexpr wchar_t kTimeStr[] = L"time";
+constexpr wchar_t kDateTimeStr[] = L"datetime";
+constexpr wchar_t kNumStr[] = L"num";
+constexpr wchar_t kTextStr[] = L"text";
+constexpr wchar_t kZeroStr[] = L"zero";
+constexpr wchar_t kNullStr[] = L"null";
+
+size_t ParseTimeZone(pdfium::span<const wchar_t> spStr, int* tz) {
+  *tz = 0;
   if (spStr.empty())
     return 0;
 
@@ -90,18 +92,21 @@
 
   size_t iStart = 1;
   size_t iEnd = iStart + 2;
+  int tz_hour = 0;
   while (iStart < spStr.size() && iStart < iEnd)
-    tz->tzHour = tz->tzHour * 10 + FXSYS_DecimalCharToInt(spStr[iStart++]);
+    tz_hour = tz_hour * 10 + FXSYS_DecimalCharToInt(spStr[iStart++]);
 
   if (iStart < spStr.size() && spStr[iStart] == ':')
     iStart++;
 
   iEnd = iStart + 2;
+  int tz_minute = 0;
   while (iStart < spStr.size() && iStart < iEnd)
-    tz->tzMinute = tz->tzMinute * 10 + FXSYS_DecimalCharToInt(spStr[iStart++]);
+    tz_minute = tz_minute * 10 + FXSYS_DecimalCharToInt(spStr[iStart++]);
 
+  *tz = tz_hour * 60 + tz_minute;
   if (bNegative)
-    tz->tzHour = -tz->tzHour;
+    *tz *= -1;
 
   return iStart;
 }
@@ -171,9 +176,9 @@
                spStrPattern[*iPattern + 1] == 'u') {
       (*iPattern)--;
       int32_t iKeyValue = 0;
-      int32_t iLen = wsOutput.GetLength();
-      int32_t i = 1;
-      for (; i < iLen && i < 5; i++) {
+      size_t iLen = std::min<size_t>(wsOutput.GetLength(), 5);
+      size_t i = 1;
+      for (; i < iLen; i++) {
         wchar_t ch = wsOutput[i];
         iKeyValue = ConvertHex(iKeyValue, ch);
       }
@@ -245,7 +250,7 @@
   while (*cc < spDate.size() && ccf < spDatePattern.size()) {
     if (spDatePattern[ccf] == '\'') {
       WideString wsLiteral = GetLiteralText(spDatePattern, &ccf);
-      int32_t iLiteralLen = wsLiteral.GetLength();
+      size_t iLiteralLen = wsLiteral.GetLength();
       if (*cc + iLiteralLen > spDate.size() ||
           wcsncmp(spDate.data() + *cc, wsLiteral.c_str(), iLiteralLen) != 0) {
         return false;
@@ -254,7 +259,7 @@
       ccf++;
       continue;
     }
-    if (!pdfium::ContainsValue(kDateSymbols, spDatePattern[ccf])) {
+    if (!pdfium::Contains(kDateSymbols, spDatePattern[ccf])) {
       if (spDatePattern[ccf] != spDate[*cc])
         return false;
       (*cc)++;
@@ -331,16 +336,13 @@
   return !!(*cc);
 }
 
-void ResolveZone(FX_TIMEZONE tzDiff,
+void ResolveZone(int tz_diff_minutes,
                  const LocaleIface* pLocale,
                  uint32_t* wHour,
                  uint32_t* wMinute) {
   int32_t iMinuteDiff = *wHour * 60 + *wMinute;
-  FX_TIMEZONE tzLocale = pLocale->GetTimeZone();
-  iMinuteDiff += tzLocale.tzHour * 60 +
-                 (tzLocale.tzHour < 0 ? -tzLocale.tzMinute : tzLocale.tzMinute);
-  iMinuteDiff -= tzDiff.tzHour * 60 +
-                 (tzDiff.tzHour < 0 ? -tzDiff.tzMinute : tzDiff.tzMinute);
+  iMinuteDiff += pLocale->GetTimeZoneInMinutes();
+  iMinuteDiff -= tz_diff_minutes;
 
   iMinuteDiff %= 1440;
   if (iMinuteDiff < 0)
@@ -367,7 +369,7 @@
   while (*cc < spTime.size() && ccf < spTimePattern.size()) {
     if (spTimePattern[ccf] == '\'') {
       WideString wsLiteral = GetLiteralText(spTimePattern, &ccf);
-      int32_t iLiteralLen = wsLiteral.GetLength();
+      size_t iLiteralLen = wsLiteral.GetLength();
       if (*cc + iLiteralLen > spTime.size() ||
           wcsncmp(spTime.data() + *cc, wsLiteral.c_str(), iLiteralLen) != 0) {
         return false;
@@ -376,7 +378,7 @@
       ccf++;
       continue;
     }
-    if (!pdfium::ContainsValue(kTimeSymbols, spTimePattern[ccf])) {
+    if (!pdfium::Contains(kTimeSymbols, spTimePattern[ccf])) {
       if (spTimePattern[ccf] != spTime[*cc])
         return false;
       (*cc)++;
@@ -445,17 +447,14 @@
       tz += spTime[(*cc)++];
       tz += spTime[(*cc)++];
       if (tz.EqualsASCII("GMT")) {
-        FX_TIMEZONE tzDiff;
-        tzDiff.tzHour = 0;
-        tzDiff.tzMinute = 0;
-        if (*cc < spTime.size() && (spTime[*cc] == '-' || spTime[*cc] == '+')) {
-          *cc += ParseTimeZone(spTime.subspan(*cc), &tzDiff);
-        }
-        ResolveZone(tzDiff, pLocale, &hour, &minute);
+        int tz_diff_minutes = 0;
+        if (*cc < spTime.size() && (spTime[*cc] == '-' || spTime[*cc] == '+'))
+          *cc += ParseTimeZone(spTime.subspan(*cc), &tz_diff_minutes);
+        ResolveZone(tz_diff_minutes, pLocale, &hour, &minute);
       } else {
         // Search the timezone list. There are only 8 of them, so linear scan.
-        for (size_t i = 0; i < FX_ArraySize(g_FXLocaleTimeZoneData); ++i) {
-          const FX_LOCALETIMEZONEINFO& info = g_FXLocaleTimeZoneData[i];
+        for (size_t i = 0; i < std::size(kFXLocaleTimeZoneData); ++i) {
+          const FX_LOCALETIMEZONEINFO& info = kFXLocaleTimeZoneData[i];
           if (tz != info.name)
             continue;
 
@@ -466,9 +465,9 @@
       }
     } else if (symbol.EqualsASCII("z")) {
       if (spTime[*cc] != 'Z') {
-        FX_TIMEZONE tzDiff;
-        *cc += ParseTimeZone(spTime.subspan(*cc), &tzDiff);
-        ResolveZone(tzDiff, pLocale, &hour, &minute);
+        int tz_diff_minutes = 0;
+        *cc += ParseTimeZone(spTime.subspan(*cc), &tz_diff_minutes);
+        ResolveZone(tz_diff_minutes, pLocale, &hour, &minute);
       } else {
         (*cc)++;
       }
@@ -578,7 +577,7 @@
       ccf++;
       continue;
     }
-    if (!pdfium::ContainsValue(kDateSymbols, spDatePattern[ccf])) {
+    if (!pdfium::Contains(kDateSymbols, spDatePattern[ccf])) {
       wsResult += spDatePattern[ccf++];
       continue;
     }
@@ -646,7 +645,7 @@
       ccf++;
       continue;
     }
-    if (!pdfium::ContainsValue(kTimeSymbols, spTimePattern[ccf])) {
+    if (!pdfium::Contains(kTimeSymbols, spTimePattern[ccf])) {
       wsResult += spTimePattern[ccf++];
       continue;
     }
@@ -680,11 +679,12 @@
     } else if (symbol.EqualsASCIINoCase("z")) {
       if (symbol.EqualsASCII("Z"))
         wsResult += L"GMT";
-      FX_TIMEZONE tz = pLocale->GetTimeZone();
-      if (tz.tzHour != 0 || tz.tzMinute != 0) {
-        wsResult += tz.tzHour < 0 ? L"-" : L"+";
-        wsResult +=
-            WideString::Format(L"%02d:%02d", abs(tz.tzHour), tz.tzMinute);
+      int tz_minutes = pLocale->GetTimeZoneInMinutes();
+      if (tz_minutes != 0) {
+        wsResult += tz_minutes < 0 ? L"-" : L"+";
+        int abs_tz_minutes = abs(tz_minutes);
+        wsResult += WideString::Format(L"%02d:%02d", abs_tz_minutes / 60,
+                                       abs_tz_minutes % 60);
       }
     }
   }
@@ -707,6 +707,42 @@
   return bDateFirst ? wsDateOut + wsTimeOut : wsTimeOut + wsDateOut;
 }
 
+bool HasDate(CFGAS_StringFormatter::DateTimeType type) {
+  return type == CFGAS_StringFormatter::DateTimeType::kDate ||
+         type == CFGAS_StringFormatter::DateTimeType::kDateTime ||
+         type == CFGAS_StringFormatter::DateTimeType::kTimeDate;
+}
+
+bool HasTime(CFGAS_StringFormatter::DateTimeType type) {
+  return type == CFGAS_StringFormatter::DateTimeType::kTime ||
+         type == CFGAS_StringFormatter::DateTimeType::kDateTime ||
+         type == CFGAS_StringFormatter::DateTimeType::kTimeDate;
+}
+
+CFGAS_StringFormatter::DateTimeType AddDateToDatelessType(
+    CFGAS_StringFormatter::DateTimeType type) {
+  switch (type) {
+    case CFGAS_StringFormatter::DateTimeType::kUnknown:
+      return CFGAS_StringFormatter::DateTimeType::kDate;
+    case CFGAS_StringFormatter::DateTimeType::kTime:
+      return CFGAS_StringFormatter::DateTimeType::kTimeDate;
+    default:
+      NOTREACHED_NORETURN();
+  }
+}
+
+CFGAS_StringFormatter::DateTimeType AddTimeToTimelessType(
+    CFGAS_StringFormatter::DateTimeType type) {
+  switch (type) {
+    case CFGAS_StringFormatter::DateTimeType::kUnknown:
+      return CFGAS_StringFormatter::DateTimeType::kTime;
+    case CFGAS_StringFormatter::DateTimeType::kDate:
+      return CFGAS_StringFormatter::DateTimeType::kDateTime;
+    default:
+      NOTREACHED_NORETURN();
+  }
+}
+
 }  // namespace
 
 bool FX_DateFromCanonical(pdfium::span<const wchar_t> spDate,
@@ -808,23 +844,18 @@
   }
 
   if (cc < spTime.size()) {
-    FX_TIMEZONE tzDiff;
-    tzDiff.tzHour = 0;
-    tzDiff.tzMinute = 0;
+    int tz_diff_minutes = 0;
     if (spTime[cc] != 'Z')
-      cc += ParseTimeZone(spTime.subspan(cc), &tzDiff);
-    ResolveZone(tzDiff, pLocale, &hour, &minute);
+      cc += ParseTimeZone(spTime.subspan(cc), &tz_diff_minutes);
+    ResolveZone(tz_diff_minutes, pLocale, &hour, &minute);
   }
 
   datetime->SetTime(hour, minute, second, millisecond);
   return true;
 }
 
-CFGAS_StringFormatter::CFGAS_StringFormatter(LocaleMgrIface* pLocaleMgr,
-                                             const WideString& wsPattern)
-    : m_pLocaleMgr(pLocaleMgr),
-      m_wsPattern(wsPattern),
-      m_spPattern(m_wsPattern.span()) {}
+CFGAS_StringFormatter::CFGAS_StringFormatter(const WideString& wsPattern)
+    : m_wsPattern(wsPattern), m_spPattern(m_wsPattern.span()) {}
 
 CFGAS_StringFormatter::~CFGAS_StringFormatter() = default;
 
@@ -848,15 +879,15 @@
   return wsPatterns;
 }
 
-FX_LOCALECATEGORY CFGAS_StringFormatter::GetCategory() const {
-  FX_LOCALECATEGORY eCategory = FX_LOCALECATEGORY_Unknown;
+CFGAS_StringFormatter::Category CFGAS_StringFormatter::GetCategory() const {
+  Category eCategory = Category::kUnknown;
   size_t ccf = 0;
   bool bBraceOpen = false;
   while (ccf < m_spPattern.size()) {
     if (m_spPattern[ccf] == '\'') {
       GetLiteralText(m_spPattern, &ccf);
     } else if (!bBraceOpen &&
-               !pdfium::ContainsValue(kConstChars, m_spPattern[ccf])) {
+               !pdfium::Contains(kConstChars, m_spPattern[ccf])) {
       WideString wsCategory(m_spPattern[ccf]);
       ccf++;
       while (true) {
@@ -871,25 +902,24 @@
         wsCategory += m_spPattern[ccf];
         ccf++;
       }
-      uint32_t dwHash = FX_HashCode_GetW(wsCategory.AsStringView(), false);
-      if (dwHash == FX_LOCALECATEGORY_DateTimeHash)
-        return FX_LOCALECATEGORY_DateTime;
-      if (dwHash == FX_LOCALECATEGORY_TextHash)
-        return FX_LOCALECATEGORY_Text;
-      if (dwHash == FX_LOCALECATEGORY_NumHash)
-        return FX_LOCALECATEGORY_Num;
-      if (dwHash == FX_LOCALECATEGORY_ZeroHash)
-        return FX_LOCALECATEGORY_Zero;
-      if (dwHash == FX_LOCALECATEGORY_NullHash)
-        return FX_LOCALECATEGORY_Null;
-      if (dwHash == FX_LOCALECATEGORY_DateHash) {
-        if (eCategory == FX_LOCALECATEGORY_Time)
-          return FX_LOCALECATEGORY_DateTime;
-        eCategory = FX_LOCALECATEGORY_Date;
-      } else if (dwHash == FX_LOCALECATEGORY_TimeHash) {
-        if (eCategory == FX_LOCALECATEGORY_Date)
-          return FX_LOCALECATEGORY_DateTime;
-        eCategory = FX_LOCALECATEGORY_Time;
+      if (wsCategory == kDateTimeStr)
+        return Category::kDateTime;
+      if (wsCategory == kTextStr)
+        return Category::kText;
+      if (wsCategory == kNumStr)
+        return Category::kNum;
+      if (wsCategory == kZeroStr)
+        return Category::kZero;
+      if (wsCategory == kNullStr)
+        return Category::kNull;
+      if (wsCategory == kDateStr) {
+        if (eCategory == Category::kTime)
+          return Category::kDateTime;
+        eCategory = Category::kDate;
+      } else if (wsCategory == kTimeStr) {
+        if (eCategory == Category::kDate)
+          return Category::kDateTime;
+        eCategory = Category::kTime;
       }
     } else if (m_spPattern[ccf] == '}') {
       bBraceOpen = false;
@@ -906,12 +936,12 @@
   WideString wsPurgePattern;
   while (ccf < m_spPattern.size()) {
     if (m_spPattern[ccf] == '\'') {
-      int32_t iCurChar = ccf;
+      size_t iCurChar = ccf;
       GetLiteralText(m_spPattern, &ccf);
       wsPurgePattern +=
           WideStringView(m_spPattern.data() + iCurChar, ccf - iCurChar + 1);
     } else if (!bBrackOpen &&
-               !pdfium::ContainsValue(kConstChars, m_spPattern[ccf])) {
+               !pdfium::Contains(kConstChars, m_spPattern[ccf])) {
       WideString wsSearchCategory(m_spPattern[ccf]);
       ccf++;
       while (ccf < m_spPattern.size() && m_spPattern[ccf] != '{' &&
@@ -946,6 +976,7 @@
 }
 
 LocaleIface* CFGAS_StringFormatter::GetNumericFormat(
+    LocaleMgrIface* pLocaleMgr,
     size_t* iDotIndex,
     uint32_t* dwStyle,
     WideString* wsPurgePattern) const {
@@ -956,12 +987,12 @@
   bool bBrackOpen = false;
   while (ccf < m_spPattern.size()) {
     if (m_spPattern[ccf] == '\'') {
-      int32_t iCurChar = ccf;
+      size_t iCurChar = ccf;
       GetLiteralText(m_spPattern, &ccf);
       *wsPurgePattern +=
           WideStringView(m_spPattern.data() + iCurChar, ccf - iCurChar + 1);
     } else if (!bBrackOpen &&
-               !pdfium::ContainsValue(kConstChars, m_spPattern[ccf])) {
+               !pdfium::Contains(kConstChars, m_spPattern[ccf])) {
       WideString wsCategory(m_spPattern[ccf]);
       ccf++;
       while (ccf < m_spPattern.size() && m_spPattern[ccf] != '{' &&
@@ -985,7 +1016,7 @@
           while (ccf < m_spPattern.size() && m_spPattern[ccf] != ')')
             wsLCID += m_spPattern[ccf++];
 
-          pLocale = m_pLocaleMgr->GetLocaleByName(wsLCID);
+          pLocale = pLocaleMgr->GetLocaleByName(wsLCID);
         } else if (m_spPattern[ccf] == '.') {
           WideString wsSubCategory;
           ccf++;
@@ -993,19 +1024,17 @@
                  m_spPattern[ccf] != '{') {
             wsSubCategory += m_spPattern[ccf++];
           }
-          uint32_t dwSubHash =
-              FX_HashCode_GetW(wsSubCategory.AsStringView(), false);
-          FX_LOCALENUMSUBCATEGORY eSubCategory = FX_LOCALENUMPATTERN_Decimal;
-          for (const auto& data : g_FXLocaleNumSubCatData) {
+          uint32_t dwSubHash = FX_HashCode_GetW(wsSubCategory.AsStringView());
+          LocaleIface::NumSubcategory eSubCategory =
+              LocaleIface::NumSubcategory::kDecimal;
+          for (const auto& data : kLocaleNumSubcategoryData) {
             if (data.uHash == dwSubHash) {
               eSubCategory = data.eSubCategory;
               break;
             }
           }
           if (!pLocale)
-            pLocale = m_pLocaleMgr->GetDefLocale();
-
-          ASSERT(pLocale);
+            pLocale = pLocaleMgr->GetDefLocale();
 
           wsSubCategory = pLocale->GetNumPattern(eSubCategory);
           auto result = wsSubCategory.Find('.');
@@ -1016,9 +1045,8 @@
             *dwStyle |= FX_NUMSTYLE_DotVorv;
           }
           *wsPurgePattern += wsSubCategory;
-          if (eSubCategory == FX_LOCALENUMPATTERN_Percent)
+          if (eSubCategory == LocaleIface::NumSubcategory::kPercent)
             *dwStyle |= FX_NUMSTYLE_Percent;
-
           continue;
         }
         ccf++;
@@ -1044,7 +1072,7 @@
   if (!bFindDot)
     *iDotIndex = wsPurgePattern->GetLength();
   if (!pLocale)
-    pLocale = m_pLocaleMgr->GetDefLocale();
+    pLocale = pLocaleMgr->GetDefLocale();
   return pLocale;
 }
 
@@ -1067,7 +1095,7 @@
     switch (spTextFormat[iPattern]) {
       case '\'': {
         WideString wsLiteral = GetLiteralText(spTextFormat, &iPattern);
-        int32_t iLiteralLen = wsLiteral.GetLength();
+        size_t iLiteralLen = wsLiteral.GetLength();
         if (iText + iLiteralLen > spSrcText.size() ||
             wcsncmp(spSrcText.data() + iText, wsLiteral.c_str(), iLiteralLen) !=
                 0) {
@@ -1119,7 +1147,8 @@
   return iPattern == spTextFormat.size() && iText == spSrcText.size();
 }
 
-bool CFGAS_StringFormatter::ParseNum(const WideString& wsSrcNum,
+bool CFGAS_StringFormatter::ParseNum(LocaleMgrIface* pLocaleMgr,
+                                     const WideString& wsSrcNum,
                                      WideString* wsValue) const {
   wsValue->clear();
   if (wsSrcNum.IsEmpty() || m_spPattern.empty())
@@ -1129,16 +1158,16 @@
   uint32_t dwFormatStyle = 0;
   WideString wsNumFormat;
   LocaleIface* pLocale =
-      GetNumericFormat(&dot_index_f, &dwFormatStyle, &wsNumFormat);
+      GetNumericFormat(pLocaleMgr, &dot_index_f, &dwFormatStyle, &wsNumFormat);
   if (!pLocale || wsNumFormat.IsEmpty())
     return false;
 
   int32_t iExponent = 0;
   WideString wsDotSymbol = pLocale->GetDecimalSymbol();
   WideString wsGroupSymbol = pLocale->GetGroupingSymbol();
-  int32_t iGroupLen = wsGroupSymbol.GetLength();
   WideString wsMinus = pLocale->GetMinusSymbol();
-  int32_t iMinusLen = wsMinus.GetLength();
+  size_t iGroupLen = wsGroupSymbol.GetLength();
+  size_t iMinusLen = wsMinus.GetLength();
 
   pdfium::span<const wchar_t> spSrcNum = wsSrcNum.span();
   pdfium::span<const wchar_t> spNumFormat = wsNumFormat.span();
@@ -1168,7 +1197,7 @@
     switch (spNumFormat[ccf]) {
       case '\'': {
         WideString wsLiteral = GetLiteralTextReverse(spNumFormat, &ccf);
-        int32_t iLiteralLen = wsLiteral.GetLength();
+        size_t iLiteralLen = wsLiteral.GetLength();
         cc -= iLiteralLen - 1;
         if (cc >= spSrcNum.size() ||
             wcsncmp(spSrcNum.data() + cc, wsLiteral.c_str(), iLiteralLen) !=
@@ -1249,7 +1278,7 @@
       }
       case '$': {
         WideString wsSymbol = pLocale->GetCurrencySymbol();
-        int32_t iSymbolLen = wsSymbol.GetLength();
+        size_t iSymbolLen = wsSymbol.GetLength();
         cc -= iSymbolLen - 1;
         if (cc >= spSrcNum.size() ||
             wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) != 0) {
@@ -1295,10 +1324,10 @@
         break;
       case '%': {
         WideString wsSymbol = pLocale->GetPercentSymbol();
-        int32_t iSysmbolLen = wsSymbol.GetLength();
-        cc -= iSysmbolLen - 1;
+        size_t iSymbolLen = wsSymbol.GetLength();
+        cc -= iSymbolLen - 1;
         if (cc >= spSrcNum.size() ||
-            wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSysmbolLen) != 0) {
+            wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) != 0) {
           return false;
         }
         cc--;
@@ -1361,7 +1390,7 @@
       switch (spNumFormat[ccf]) {
         case '\'': {
           WideString wsLiteral = GetLiteralText(spNumFormat, &ccf);
-          int32_t iLiteralLen = wsLiteral.GetLength();
+          size_t iLiteralLen = wsLiteral.GetLength();
           if (cc + iLiteralLen > spSrcNum.size() ||
               wcsncmp(spSrcNum.data() + cc, wsLiteral.c_str(), iLiteralLen) !=
                   0) {
@@ -1433,7 +1462,7 @@
         }
         case '$': {
           WideString wsSymbol = pLocale->GetCurrencySymbol();
-          int32_t iSymbolLen = wsSymbol.GetLength();
+          size_t iSymbolLen = wsSymbol.GetLength();
           if (cc + iSymbolLen > spSrcNum.size() ||
               wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) !=
                   0) {
@@ -1478,11 +1507,11 @@
           return false;
         case '%': {
           WideString wsSymbol = pLocale->GetPercentSymbol();
-          int32_t iSysmbolLen = wsSymbol.GetLength();
-          if (cc + iSysmbolLen <= spSrcNum.size() &&
-              wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSysmbolLen) ==
+          size_t iSymbolLen = wsSymbol.GetLength();
+          if (cc + iSymbolLen <= spSrcNum.size() &&
+              wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) ==
                   0) {
-            cc += iSysmbolLen;
+            cc += iSymbolLen;
           }
           bHavePercentSymbol = true;
         } break;
@@ -1524,13 +1553,10 @@
   }
   if (iExponent || bHavePercentSymbol) {
     CFGAS_Decimal decimal = CFGAS_Decimal(wsValue->AsStringView());
-    if (iExponent) {
-      decimal = decimal *
-                CFGAS_Decimal(FXSYS_pow(10, static_cast<float>(iExponent)), 3);
-    }
+    if (iExponent)
+      decimal = decimal * CFGAS_Decimal(powf(10, iExponent), 3);
     if (bHavePercentSymbol)
       decimal = decimal / CFGAS_Decimal(100);
-
     *wsValue = decimal.ToWideString();
   }
   if (bNeg)
@@ -1539,24 +1565,25 @@
   return true;
 }
 
-FX_DATETIMETYPE CFGAS_StringFormatter::GetDateTimeFormat(
+CFGAS_StringFormatter::DateTimeType CFGAS_StringFormatter::GetDateTimeFormat(
+    LocaleMgrIface* pLocaleMgr,
     LocaleIface** pLocale,
     WideString* wsDatePattern,
     WideString* wsTimePattern) const {
   *pLocale = nullptr;
   WideString wsTempPattern;
-  FX_LOCALECATEGORY eCategory = FX_LOCALECATEGORY_Unknown;
+  Category eCategory = Category::kUnknown;
+  DateTimeType eDateTimeType = DateTimeType::kUnknown;
   size_t ccf = 0;
-  int32_t iFindCategory = 0;
   bool bBraceOpen = false;
   while (ccf < m_spPattern.size()) {
     if (m_spPattern[ccf] == '\'') {
-      int32_t iCurChar = ccf;
+      size_t iCurChar = ccf;
       GetLiteralText(m_spPattern, &ccf);
       wsTempPattern +=
           WideStringView(m_spPattern.data() + iCurChar, ccf - iCurChar + 1);
-    } else if (!bBraceOpen && iFindCategory != 3 &&
-               !pdfium::ContainsValue(kConstChars, m_spPattern[ccf])) {
+    } else if (!bBraceOpen && eDateTimeType != DateTimeType::kDateTime &&
+               !pdfium::Contains(kConstChars, m_spPattern[ccf])) {
       WideString wsCategory(m_spPattern[ccf]);
       ccf++;
       while (ccf < m_spPattern.size() && m_spPattern[ccf] != '{' &&
@@ -1566,24 +1593,21 @@
           *wsTimePattern = m_wsPattern.Last(m_wsPattern.GetLength() - ccf);
           wsTimePattern->SetAt(0, ' ');
           if (!*pLocale)
-            *pLocale = m_pLocaleMgr->GetDefLocale();
-
-          return FX_DATETIMETYPE_DateTime;
+            *pLocale = pLocaleMgr->GetDefLocale();
+          return DateTimeType::kDateTime;
         }
         wsCategory += m_spPattern[ccf];
         ccf++;
       }
-      if (!(iFindCategory & 1) && wsCategory.EqualsASCII("date")) {
-        iFindCategory |= 1;
-        eCategory = FX_LOCALECATEGORY_Date;
-        if (iFindCategory & 2)
-          iFindCategory = 4;
-      } else if (!(iFindCategory & 2) && wsCategory.EqualsASCII("time")) {
-        iFindCategory |= 2;
-        eCategory = FX_LOCALECATEGORY_Time;
+      if (!HasDate(eDateTimeType) && wsCategory.EqualsASCII("date")) {
+        eDateTimeType = AddDateToDatelessType(eDateTimeType);
+        eCategory = Category::kDate;
+      } else if (!HasTime(eDateTimeType) && wsCategory.EqualsASCII("time")) {
+        eDateTimeType = AddTimeToTimelessType(eDateTimeType);
+        eCategory = Category::kTime;
       } else if (wsCategory.EqualsASCII("datetime")) {
-        iFindCategory = 3;
-        eCategory = FX_LOCALECATEGORY_DateTime;
+        eDateTimeType = DateTimeType::kDateTime;
+        eCategory = Category::kDateTime;
       } else {
         continue;
       }
@@ -1598,7 +1622,7 @@
           while (ccf < m_spPattern.size() && m_spPattern[ccf] != ')')
             wsLCID += m_spPattern[ccf++];
 
-          *pLocale = m_pLocaleMgr->GetLocaleByName(wsLCID);
+          *pLocale = pLocaleMgr->GetLocaleByName(wsLCID);
         } else if (m_spPattern[ccf] == '.') {
           WideString wsSubCategory;
           ccf++;
@@ -1606,30 +1630,28 @@
                  m_spPattern[ccf] != '{')
             wsSubCategory += m_spPattern[ccf++];
 
-          uint32_t dwSubHash =
-              FX_HashCode_GetW(wsSubCategory.AsStringView(), false);
-          FX_LOCALEDATETIMESUBCATEGORY eSubCategory =
-              FX_LOCALEDATETIMESUBCATEGORY_Medium;
-          for (const auto& data : g_FXLocaleDateTimeSubCatData) {
+          uint32_t dwSubHash = FX_HashCode_GetW(wsSubCategory.AsStringView());
+          LocaleIface::DateTimeSubcategory eSubCategory =
+              LocaleIface::DateTimeSubcategory::kMedium;
+          for (const auto& data : kLocaleDateTimeSubcategoryData) {
             if (data.uHash == dwSubHash) {
               eSubCategory = data.eSubCategory;
               break;
             }
           }
           if (!*pLocale)
-            *pLocale = m_pLocaleMgr->GetDefLocale();
-          ASSERT(*pLocale);
+            *pLocale = pLocaleMgr->GetDefLocale();
 
           switch (eCategory) {
-            case FX_LOCALECATEGORY_Date:
+            case Category::kDate:
               *wsDatePattern =
                   wsTempPattern + (*pLocale)->GetDatePattern(eSubCategory);
               break;
-            case FX_LOCALECATEGORY_Time:
+            case Category::kTime:
               *wsTimePattern =
                   wsTempPattern + (*pLocale)->GetTimePattern(eSubCategory);
               break;
-            case FX_LOCALECATEGORY_DateTime:
+            case Category::kDateTime:
               *wsDatePattern =
                   wsTempPattern + (*pLocale)->GetDatePattern(eSubCategory);
               *wsTimePattern = (*pLocale)->GetTimePattern(eSubCategory);
@@ -1645,9 +1667,9 @@
     } else if (m_spPattern[ccf] == '}') {
       bBraceOpen = false;
       if (!wsTempPattern.IsEmpty()) {
-        if (eCategory == FX_LOCALECATEGORY_Time)
+        if (eCategory == Category::kTime)
           *wsTimePattern = std::move(wsTempPattern);
-        else if (eCategory == FX_LOCALECATEGORY_Date)
+        else if (eCategory == Category::kDate)
           *wsDatePattern = std::move(wsTempPattern);
         else
           wsTempPattern.clear();
@@ -1659,22 +1681,23 @@
   }
 
   if (!wsTempPattern.IsEmpty()) {
-    if (eCategory == FX_LOCALECATEGORY_Date)
+    if (eCategory == Category::kDate)
       *wsDatePattern += wsTempPattern;
     else
       *wsTimePattern += wsTempPattern;
   }
   if (!*pLocale)
-    *pLocale = m_pLocaleMgr->GetDefLocale();
-  if (!iFindCategory) {
+    *pLocale = pLocaleMgr->GetDefLocale();
+  if (eDateTimeType == DateTimeType::kUnknown) {
     wsTimePattern->clear();
     *wsDatePattern = m_wsPattern;
   }
-  return (FX_DATETIMETYPE)iFindCategory;
+  return eDateTimeType;
 }
 
-bool CFGAS_StringFormatter::ParseDateTime(const WideString& wsSrcDateTime,
-                                          FX_DATETIMETYPE eDateTimeType,
+bool CFGAS_StringFormatter::ParseDateTime(LocaleMgrIface* pLocaleMgr,
+                                          const WideString& wsSrcDateTime,
+                                          DateTimeType eDateTimeType,
                                           CFX_DateTime* dtValue) const {
   dtValue->Reset();
   if (wsSrcDateTime.IsEmpty() || m_spPattern.empty())
@@ -1683,34 +1706,33 @@
   LocaleIface* pLocale = nullptr;
   WideString wsDatePattern;
   WideString wsTimePattern;
-  FX_DATETIMETYPE eCategory =
-      GetDateTimeFormat(&pLocale, &wsDatePattern, &wsTimePattern);
+  DateTimeType eCategory =
+      GetDateTimeFormat(pLocaleMgr, &pLocale, &wsDatePattern, &wsTimePattern);
   if (!pLocale)
     return false;
 
-  if (eCategory == FX_DATETIMETYPE_Unknown)
+  if (eCategory == DateTimeType::kUnknown)
     eCategory = eDateTimeType;
 
   size_t iStart = 0;
   switch (eCategory) {
-    case FX_DATETIMETYPE_Date:
+    case DateTimeType::kDate:
       return ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
                              &iStart);
-    case FX_DATETIMETYPE_Time:
+    case DateTimeType::kTime:
       return ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
                              &iStart);
-    case FX_DATETIMETYPE_DateTime:
+    case DateTimeType::kDateTime:
       return ParseLocaleDate(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
                              &iStart) &&
              ParseLocaleTime(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
                              &iStart);
-    case FX_DATETIMETYPE_TimeDate:
+    case DateTimeType::kTimeDate:
       return ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
                              &iStart) &&
              ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
                              &iStart);
-    case FX_DATETIMETYPE_Unknown:
-    default:
+    case DateTimeType::kUnknown:
       return false;
   }
 }
@@ -1725,7 +1747,7 @@
   while (iPattern < spTextFormat.size() && iText < spSrcText.size()) {
     if (spTextFormat[iPattern] == '\'') {
       WideString wsLiteral = GetLiteralText(spTextFormat, &iPattern);
-      int32_t iLiteralLen = wsLiteral.GetLength();
+      size_t iLiteralLen = wsLiteral.GetLength();
       if (iText + iLiteralLen > spSrcText.size() ||
           wcsncmp(spSrcText.data() + iText, wsLiteral.c_str(), iLiteralLen)) {
         return false;
@@ -1753,7 +1775,7 @@
   while (iPattern < spTextFormat.size() && iText < spSrcText.size()) {
     if (spTextFormat[iPattern] == '\'') {
       WideString wsLiteral = GetLiteralText(spTextFormat, &iPattern);
-      int32_t iLiteralLen = wsLiteral.GetLength();
+      size_t iLiteralLen = wsLiteral.GetLength();
       if (iText + iLiteralLen > spSrcText.size() ||
           wcsncmp(spSrcText.data() + iText, wsLiteral.c_str(), iLiteralLen)) {
         return false;
@@ -1829,7 +1851,8 @@
   return iText == spSrcText.size();
 }
 
-bool CFGAS_StringFormatter::FormatNum(const WideString& wsInputNum,
+bool CFGAS_StringFormatter::FormatNum(LocaleMgrIface* pLocaleMgr,
+                                      const WideString& wsInputNum,
                                       WideString* wsOutput) const {
   if (wsInputNum.IsEmpty() || m_spPattern.empty())
     return false;
@@ -1838,7 +1861,7 @@
   uint32_t dwNumStyle = 0;
   WideString wsNumFormat;
   LocaleIface* pLocale =
-      GetNumericFormat(&dot_index_f, &dwNumStyle, &wsNumFormat);
+      GetNumericFormat(pLocaleMgr, &dot_index_f, &dwNumStyle, &wsNumFormat);
   if (!pLocale || wsNumFormat.IsEmpty())
     return false;
 
@@ -1899,9 +1922,9 @@
   }
 
   bool bTrimTailZeros = false;
-  int32_t iTreading =
+  size_t iTreading =
       GetNumTrailingLimit(wsNumFormat, dot_index_f, &bTrimTailZeros);
-  int32_t scale = decimal.GetScale();
+  uint8_t scale = decimal.GetScale();
   if (iTreading < scale) {
     decimal.SetScale(iTreading);
     wsSrcNum = decimal.ToWideString();
@@ -2174,8 +2197,9 @@
   return true;
 }
 
-bool CFGAS_StringFormatter::FormatDateTime(const WideString& wsSrcDateTime,
-                                           FX_DATETIMETYPE eDateTimeType,
+bool CFGAS_StringFormatter::FormatDateTime(LocaleMgrIface* pLocaleMgr,
+                                           const WideString& wsSrcDateTime,
+                                           DateTimeType eDateTimeType,
                                            WideString* wsOutput) const {
   if (wsSrcDateTime.IsEmpty() || m_spPattern.empty())
     return false;
@@ -2183,31 +2207,31 @@
   WideString wsDatePattern;
   WideString wsTimePattern;
   LocaleIface* pLocale = nullptr;
-  FX_DATETIMETYPE eCategory =
-      GetDateTimeFormat(&pLocale, &wsDatePattern, &wsTimePattern);
+  DateTimeType eCategory =
+      GetDateTimeFormat(pLocaleMgr, &pLocale, &wsDatePattern, &wsTimePattern);
   if (!pLocale)
     return false;
 
-  if (eCategory == FX_DATETIMETYPE_Unknown) {
-    if (eDateTimeType == FX_DATETIMETYPE_Time) {
+  if (eCategory == DateTimeType::kUnknown) {
+    if (eDateTimeType == DateTimeType::kTime) {
       wsTimePattern = std::move(wsDatePattern);
       wsDatePattern = WideString();
     }
     eCategory = eDateTimeType;
-    if (eCategory == FX_DATETIMETYPE_Unknown)
+    if (eCategory == DateTimeType::kUnknown)
       return false;
   }
 
   CFX_DateTime dt;
   auto iT = wsSrcDateTime.Find(L"T");
   if (!iT.has_value()) {
-    if (eCategory == FX_DATETIMETYPE_Date &&
+    if (eCategory == DateTimeType::kDate &&
         FX_DateFromCanonical(wsSrcDateTime.span(), &dt)) {
       *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern, true,
                                          pLocale);
       return true;
     }
-    if (eCategory == FX_DATETIMETYPE_Time &&
+    if (eCategory == DateTimeType::kTime &&
         FX_TimeFromCanonical(pLocale, wsSrcDateTime.span(), &dt)) {
       *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern, true,
                                          pLocale);
@@ -2223,9 +2247,9 @@
 
     if (FX_DateFromCanonical(wsSrcDate, &dt) &&
         FX_TimeFromCanonical(pLocale, wsSrcTime, &dt)) {
-      *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern,
-                                         eCategory != FX_DATETIMETYPE_TimeDate,
-                                         pLocale);
+      *wsOutput =
+          FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern,
+                                 eCategory != DateTimeType::kTimeDate, pLocale);
       return true;
     }
   }
diff --git a/xfa/fgas/crt/cfgas_stringformatter.h b/xfa/fgas/crt/cfgas_stringformatter.h
index afdeeb9..0acc76d 100644
--- a/xfa/fgas/crt/cfgas_stringformatter.h
+++ b/xfa/fgas/crt/cfgas_stringformatter.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,10 +9,12 @@
 
 #include <vector>
 
-#include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/span.h"
+#include "core/fxcrt/widestring.h"
+#include "third_party/base/containers/span.h"
 #include "xfa/fgas/crt/locale_iface.h"
-#include "xfa/fgas/crt/locale_mgr_iface.h"
+
+class CFX_DateTime;
+class LocaleMgrIface;
 
 bool FX_DateFromCanonical(pdfium::span<const wchar_t> wsTime,
                           CFX_DateTime* datetime);
@@ -22,44 +24,65 @@
 
 class CFGAS_StringFormatter {
  public:
-  CFGAS_StringFormatter(LocaleMgrIface* pLocaleMgr,
-                        const WideString& wsPattern);
+  enum class Category {
+    kUnknown,
+    kDate,
+    kTime,
+    kDateTime,
+    kNum,
+    kText,
+    kZero,
+    kNull,
+  };
+
+  enum class DateTimeType {
+    kUnknown,
+    kDate,
+    kTime,
+    kDateTime,
+    kTimeDate,
+  };
+
+  explicit CFGAS_StringFormatter(const WideString& wsPattern);
   ~CFGAS_StringFormatter();
 
   static std::vector<WideString> SplitOnBars(const WideString& wsFormatString);
 
-  FX_LOCALECATEGORY GetCategory() const;
+  Category GetCategory() const;
 
-  bool ParseText(const WideString& wsSrcText,
-                 WideString* wsValue) const;
-  bool ParseNum(const WideString& wsSrcNum,
+  bool ParseText(const WideString& wsSrcText, WideString* wsValue) const;
+  bool ParseNum(LocaleMgrIface* pLocaleMgr,
+                const WideString& wsSrcNum,
                 WideString* wsValue) const;
-  bool ParseDateTime(const WideString& wsSrcDateTime,
-                     FX_DATETIMETYPE eDateTimeType,
+  bool ParseDateTime(LocaleMgrIface* pLocaleMgr,
+                     const WideString& wsSrcDateTime,
+                     DateTimeType eDateTimeType,
                      CFX_DateTime* dtValue) const;
   bool ParseZero(const WideString& wsSrcText) const;
   bool ParseNull(const WideString& wsSrcText) const;
 
-  bool FormatText(const WideString& wsSrcText,
-                  WideString* wsOutput) const;
-  bool FormatNum(const WideString& wsSrcNum,
+  bool FormatText(const WideString& wsSrcText, WideString* wsOutput) const;
+  bool FormatNum(LocaleMgrIface* pLocaleMgr,
+                 const WideString& wsSrcNum,
                  WideString* wsOutput) const;
-  bool FormatDateTime(const WideString& wsSrcDateTime,
-                      FX_DATETIMETYPE eDateTimeType,
+  bool FormatDateTime(LocaleMgrIface* pLocaleMgr,
+                      const WideString& wsSrcDateTime,
+                      DateTimeType eDateTimeType,
                       WideString* wsOutput) const;
   bool FormatZero(WideString* wsOutput) const;
   bool FormatNull(WideString* wsOutput) const;
 
  private:
   WideString GetTextFormat(WideStringView wsCategory) const;
-  LocaleIface* GetNumericFormat(size_t* iDotIndex,
+  LocaleIface* GetNumericFormat(LocaleMgrIface* pLocaleMgr,
+                                size_t* iDotIndex,
                                 uint32_t* dwStyle,
                                 WideString* wsPurgePattern) const;
-  FX_DATETIMETYPE GetDateTimeFormat(LocaleIface** pLocale,
-                                    WideString* wsDatePattern,
-                                    WideString* wsTimePattern) const;
+  DateTimeType GetDateTimeFormat(LocaleMgrIface* pLocaleMgr,
+                                 LocaleIface** pLocale,
+                                 WideString* wsDatePattern,
+                                 WideString* wsTimePattern) const;
 
-  UnownedPtr<LocaleMgrIface> const m_pLocaleMgr;
   const WideString m_wsPattern;                   // keep pattern string alive.
   const pdfium::span<const wchar_t> m_spPattern;  // span into |m_wsPattern|.
 };
diff --git a/xfa/fgas/crt/cfgas_stringformatter_unittest.cpp b/xfa/fgas/crt/cfgas_stringformatter_unittest.cpp
index 8a9e60c..93ff4c8 100644
--- a/xfa/fgas/crt/cfgas_stringformatter_unittest.cpp
+++ b/xfa/fgas/crt/cfgas_stringformatter_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,54 +6,31 @@
 
 #include "xfa/fgas/crt/cfgas_stringformatter.h"
 
-#include <time.h>
-
-#include <memory>
+#include <iterator>
 
 #include "build/build_config.h"
 #include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fxcrt/cfx_datetime.h"
 #include "testing/fx_string_testhelpers.h"
+#include "testing/fxgc_unittest.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
+#include "testing/scoped_set_tz.h"
+#include "v8/include/cppgc/persistent.h"
 #include "xfa/fxfa/parser/cxfa_localemgr.h"
 
-class CFGAS_StringFormatterTest : public testing::Test {
+class CFGAS_StringFormatterTest : public FXGCUnitTest {
  public:
-  CFGAS_StringFormatterTest() {
-    SetTZ("UTC");
-    CPDF_PageModule::Create();
-  }
+  CFGAS_StringFormatterTest() : scoped_tz_("UTC") { CPDF_PageModule::Create(); }
 
   ~CFGAS_StringFormatterTest() override { CPDF_PageModule::Destroy(); }
 
-  void TearDown() override {
-    fmt_.reset();
-    mgr_.reset();
+  CXFA_LocaleMgr* Mgr(const WideString& locale) {
+    return cppgc::MakeGarbageCollected<CXFA_LocaleMgr>(
+        heap()->GetAllocationHandle(), heap(), nullptr, locale);
   }
 
-  void SetTZ(const char* tz) {
-#if defined(OS_WIN)
-    _putenv_s("TZ", tz);
-    _tzset();
-#else
-    setenv("TZ", tz, 1);
-    tzset();
-#endif
-  }
-
-  // Note, this re-creates the fmt on each call. If you need to multiple
-  // times store it locally.
-  CFGAS_StringFormatter* fmt(const WideString& locale,
-                             const WideString& pattern) {
-    fmt_.reset();  // Can't outlive |mgr_|.
-    mgr_ = pdfium::MakeUnique<CXFA_LocaleMgr>(nullptr, locale);
-    fmt_ = pdfium::MakeUnique<CFGAS_StringFormatter>(mgr_.get(), pattern);
-    return fmt_.get();
-  }
-
- protected:
-  std::unique_ptr<CXFA_LocaleMgr> mgr_;
-  std::unique_ptr<CFGAS_StringFormatter> fmt_;
+ private:
+  ScopedSetTZ scoped_tz_;
 };
 
 // TODO(dsinclair): Looks like the formatter/parser does not handle the various
@@ -113,11 +90,12 @@
   // as they are not supported. In theory there are the full width versions
   // of DDD, DDDD, MMM, MMMM, E, e, gg, YYY, YYYYY.
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+  for (size_t i = 0; i < std::size(tests); ++i) {
     WideString result;
-    EXPECT_TRUE(
-        fmt(tests[i].locale, tests[i].pattern)
-            ->FormatDateTime(tests[i].input, FX_DATETIMETYPE_Date, &result));
+    CFGAS_StringFormatter fmt(tests[i].pattern);
+    EXPECT_TRUE(fmt.FormatDateTime(Mgr(tests[i].locale), tests[i].input,
+                                   CFGAS_StringFormatter::DateTimeType::kDate,
+                                   &result));
     EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
   }
 }
@@ -160,17 +138,18 @@
   // list the symbol.
 
   // The z modifier only appends if the TZ is outside of +0
-  SetTZ("UTC+2");
+  {
+    ScopedSetTZ scoped_tz("UTC+2");
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    WideString result;
-    EXPECT_TRUE(
-        fmt(tests[i].locale, tests[i].pattern)
-            ->FormatDateTime(tests[i].input, FX_DATETIMETYPE_Time, &result));
-    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
+    for (size_t i = 0; i < std::size(tests); ++i) {
+      WideString result;
+      CFGAS_StringFormatter fmt(tests[i].pattern);
+      EXPECT_TRUE(fmt.FormatDateTime(Mgr(tests[i].locale), tests[i].input,
+                                     CFGAS_StringFormatter::DateTimeType::kTime,
+                                     &result));
+      EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
+    }
   }
-
-  SetTZ("UTC");
 }
 
 TEST_F(CFGAS_StringFormatterTest, DateTimeFormat) {
@@ -194,11 +173,12 @@
       {L"en", L"9111T1111:", L"MMM D, YYYYTh:MM:SS A",
        L"Jan 1, 9111 11:11:00 AM"}};
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+  for (size_t i = 0; i < std::size(tests); ++i) {
     WideString result;
-    EXPECT_TRUE(fmt(tests[i].locale, tests[i].pattern)
-                    ->FormatDateTime(tests[i].input, FX_DATETIMETYPE_DateTime,
-                                     &result));
+    CFGAS_StringFormatter fmt(tests[i].pattern);
+    EXPECT_TRUE(fmt.FormatDateTime(
+        Mgr(tests[i].locale), tests[i].input,
+        CFGAS_StringFormatter::DateTimeType::kDateTime, &result));
     EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
   }
 }
@@ -222,11 +202,12 @@
        L"time{'At 'HH:MM Z}date{' on 'MMM DD, YYYY}",
        L"At 10:30 GMT on Jul 16, 1999"}};
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+  for (size_t i = 0; i < std::size(tests); ++i) {
     WideString result;
-    EXPECT_TRUE(fmt(tests[i].locale, tests[i].pattern)
-                    ->FormatDateTime(tests[i].input, FX_DATETIMETYPE_TimeDate,
-                                     &result));
+    CFGAS_StringFormatter fmt(tests[i].pattern);
+    EXPECT_TRUE(fmt.FormatDateTime(
+        Mgr(tests[i].locale), tests[i].input,
+        CFGAS_StringFormatter::DateTimeType::kTimeDate, &result));
     EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
   }
 }
@@ -283,11 +264,12 @@
   // not supported. In theory there are the full width versions of DDD,
   // DDDD, MMM, MMMM, E, e, gg, YYY, YYYYY.
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+  for (size_t i = 0; i < std::size(tests); ++i) {
     CFX_DateTime result;
-    EXPECT_TRUE(
-        fmt(tests[i].locale, tests[i].pattern)
-            ->ParseDateTime(tests[i].input, FX_DATETIMETYPE_Date, &result));
+    CFGAS_StringFormatter fmt(tests[i].pattern);
+    EXPECT_TRUE(fmt.ParseDateTime(Mgr(tests[i].locale), tests[i].input,
+                                  CFGAS_StringFormatter::DateTimeType::kDate,
+                                  &result));
     EXPECT_EQ(tests[i].output, result) << " TEST: " << i;
   }
 }
@@ -310,11 +292,12 @@
 //   // kkkk, HHH, HHHH, KKK, KKKK, MMM, MMMM, SSS, SSSS plus 2 more that the
 //   // spec apparently forgot to list the symbol.
 
-//   for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+//   for (size_t i = 0; i < std::size(tests); ++i) {
 //     CFX_DateTime result;
 //     EXPECT_TRUE(fmt(tests[i].locale)
-//                     ->ParseDateTime(tests[i].input, tests[i].pattern,
-//                                     FX_DATETIMETYPE_Time, &result));
+//                   ->ParseDateTime(tests[i].input, tests[i].pattern,
+//                                   CFGAS_StringFormatter::DateTimeType::kTime,
+//                                   &result));
 //     EXPECT_EQ(tests[i].output, result) << " TEST: " << i;
 //   }
 // }
@@ -473,7 +456,8 @@
 
   for (const auto& test : tests) {
     WideString result;
-    EXPECT_TRUE(fmt(test.locale, test.pattern)->ParseNum(test.input, &result))
+    CFGAS_StringFormatter fmt(test.pattern);
+    EXPECT_TRUE(fmt.ParseNum(Mgr(test.locale), test.input, &result))
         << " TEST: " << test.input << ", " << test.pattern;
     EXPECT_STREQ(test.output, result.c_str())
         << " TEST: " << test.input << ", " << test.pattern;
@@ -481,7 +465,8 @@
 
   for (const auto& test : failures) {
     WideString result;
-    EXPECT_FALSE(fmt(test.locale, test.pattern)->ParseNum(test.input, &result))
+    CFGAS_StringFormatter fmt(test.pattern);
+    EXPECT_FALSE(fmt.ParseNum(Mgr(test.locale), test.input, &result))
         << " TEST: " << test.input << ", " << test.pattern;
   }
 }
@@ -602,7 +587,8 @@
 
   for (const auto& test : tests) {
     WideString result;
-    EXPECT_TRUE(fmt(test.locale, test.pattern)->FormatNum(test.input, &result))
+    CFGAS_StringFormatter fmt(test.pattern);
+    EXPECT_TRUE(fmt.FormatNum(Mgr(test.locale), test.input, &result))
         << " TEST: " << test.input << ", " << test.pattern;
     EXPECT_STREQ(test.output, result.c_str())
         << " TEST: " << test.input << ", " << test.pattern;
@@ -610,14 +596,14 @@
 
   for (const auto& test : failures) {
     WideString result;
-    EXPECT_FALSE(fmt(test.locale, test.pattern)->FormatNum(test.input, &result))
+    CFGAS_StringFormatter fmt(test.pattern);
+    EXPECT_FALSE(fmt.FormatNum(Mgr(test.locale), test.input, &result))
         << " TEST: " << test.input << ", " << test.pattern;
   }
 }
 
 TEST_F(CFGAS_StringFormatterTest, TextParse) {
   struct {
-    const wchar_t* locale;
     const wchar_t* input;
     const wchar_t* pattern;
     const wchar_t* output;
@@ -626,16 +612,16 @@
                //  * - zero or more whitespace
                //  + - one or more whitespace
                // {L"en", L"555-1212", L"text(th_TH){999*9999}", L"5551212"},
-               {L"en", L"ABC-1234-5", L"AAA-9999-X", L"ABC12345"},
-               {L"en", L"ABC-1234-D", L"AAA-9999-X", L"ABC1234D"},
-               {L"en", L"A1C-1234-D", L"OOO-9999-X", L"A1C1234D"},
-               {L"en", L"A1C-1234-D", L"000-9999-X", L"A1C1234D"},
-               {L"en", L"A1C-1234-D text", L"000-9999-X 'text'", L"A1C1234D"}};
+               {L"ABC-1234-5", L"AAA-9999-X", L"ABC12345"},
+               {L"ABC-1234-D", L"AAA-9999-X", L"ABC1234D"},
+               {L"A1C-1234-D", L"OOO-9999-X", L"A1C1234D"},
+               {L"A1C-1234-D", L"000-9999-X", L"A1C1234D"},
+               {L"A1C-1234-D text", L"000-9999-X 'text'", L"A1C1234D"}};
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+  for (size_t i = 0; i < std::size(tests); ++i) {
     WideString result;
-    EXPECT_TRUE(fmt(tests[i].locale, tests[i].pattern)
-                    ->ParseText(tests[i].input, &result));
+    CFGAS_StringFormatter fmt(tests[i].pattern);
+    EXPECT_TRUE(fmt.ParseText(tests[i].input, &result));
     EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
   }
 }
@@ -643,7 +629,8 @@
 TEST_F(CFGAS_StringFormatterTest, InvalidTextParse) {
   // Input does not match mask.
   WideString result;
-  EXPECT_FALSE(fmt(L"en", L"AAA-9999-X")->ParseText(L"123-4567-8", &result));
+  CFGAS_StringFormatter fmt(L"AAA-9999-X");
+  EXPECT_FALSE(fmt.ParseText(L"123-4567-8", &result));
 }
 
 TEST_F(CFGAS_StringFormatterTest, TextFormat) {
@@ -662,96 +649,98 @@
       {L"en", L"K1#5K2", L"00X OO9", L"K1# 5K2"},
   };
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+  for (size_t i = 0; i < std::size(tests); ++i) {
     WideString result;
-    EXPECT_TRUE(fmt(tests[i].locale, tests[i].pattern)
-                    ->FormatText(tests[i].input, &result));
+    CFGAS_StringFormatter fmt(tests[i].pattern);
+    EXPECT_TRUE(fmt.FormatText(tests[i].input, &result));
     EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
   }
 }
 
 TEST_F(CFGAS_StringFormatterTest, NullParse) {
   struct {
-    const wchar_t* locale;
     const wchar_t* input;
     const wchar_t* pattern;
   } tests[] = {
-      {L"en", L"", L"null{}"},
-      {L"en", L"No data", L"null{'No data'}"},
+      {L"", L"null{}"},
+      {L"No data", L"null{'No data'}"},
   };
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(
-        fmt(tests[i].locale, tests[i].pattern)->ParseNull(tests[i].input))
-        << " TEST: " << i;
+  for (size_t i = 0; i < std::size(tests); ++i) {
+    CFGAS_StringFormatter fmt(tests[i].pattern);
+    EXPECT_TRUE(fmt.ParseNull(tests[i].input)) << " TEST: " << i;
   }
 }
 
 TEST_F(CFGAS_StringFormatterTest, NullFormat) {
   struct {
-    const wchar_t* locale;
     const wchar_t* pattern;
     const wchar_t* output;
-  } tests[] = {{L"en", L"null{'n/a'}", L"n/a"}, {L"en", L"null{}", L""}};
+  } tests[] = {{L"null{'n/a'}", L"n/a"}, {L"null{}", L""}};
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+  for (size_t i = 0; i < std::size(tests); ++i) {
     WideString result;
-    EXPECT_TRUE(fmt(tests[i].locale, tests[i].pattern)->FormatNull(&result));
+    CFGAS_StringFormatter fmt(tests[i].pattern);
+    EXPECT_TRUE(fmt.FormatNull(&result));
     EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
   }
 }
 
 TEST_F(CFGAS_StringFormatterTest, ZeroParse) {
   struct {
-    const wchar_t* locale;
     const wchar_t* input;
     const wchar_t* pattern;
-  } tests[] = {{L"en", L"", L"zero{}"},
-               {L"en", L"9", L"zero{9}"},
-               {L"en", L"a", L"zero{'a'}"}};
+  } tests[] = {{L"", L"zero{}"}, {L"9", L"zero{9}"}, {L"a", L"zero{'a'}"}};
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(
-        fmt(tests[i].locale, tests[i].pattern)->ParseZero(tests[i].input))
-        << " TEST: " << i;
+  for (size_t i = 0; i < std::size(tests); ++i) {
+    CFGAS_StringFormatter fmt(tests[i].pattern);
+    EXPECT_TRUE(fmt.ParseZero(tests[i].input)) << " TEST: " << i;
   }
 }
 
 TEST_F(CFGAS_StringFormatterTest, ZeroFormat) {
   struct {
-    const wchar_t* locale;
     const wchar_t* input;
     const wchar_t* pattern;
     const wchar_t* output;
   } tests[] = {// TODO(dsinclair): The zero format can take a number specifier
                // which we don't take into account.
-               // {L"en", L"", L"zero {9}", L""},
-               // {L"en", L"0", L"zero {9}", L"0"},
-               // {L"en", L"0.0", L"zero{9}", L"0"},
-               {L"en", L"0", L"zero{}", L""}};
+               // {L"", L"zero {9}", L""},
+               // {L"0", L"zero {9}", L"0"},
+               // {L"0.0", L"zero{9}", L"0"},
+               {L"0", L"zero{}", L""}};
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+  for (size_t i = 0; i < std::size(tests); ++i) {
     WideString result;
-    EXPECT_TRUE(fmt(tests[i].locale, tests[i].pattern)->FormatZero(&result));
+    CFGAS_StringFormatter fmt(tests[i].pattern);
+    EXPECT_TRUE(fmt.FormatZero(&result));
     EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
   }
 }
 
 TEST_F(CFGAS_StringFormatterTest, GetCategory) {
-  EXPECT_EQ(FX_LOCALECATEGORY_Unknown,
-            fmt(L"en", L"'just text'")->GetCategory());
-  EXPECT_EQ(FX_LOCALECATEGORY_Null, fmt(L"en", L"null{}")->GetCategory());
-  EXPECT_EQ(FX_LOCALECATEGORY_Zero, fmt(L"en", L"zero{}")->GetCategory());
-  EXPECT_EQ(FX_LOCALECATEGORY_Num, fmt(L"en", L"num{}")->GetCategory());
-  EXPECT_EQ(FX_LOCALECATEGORY_Text, fmt(L"en", L"text{}")->GetCategory());
-  EXPECT_EQ(FX_LOCALECATEGORY_DateTime,
-            fmt(L"en", L"datetime{}")->GetCategory());
-  EXPECT_EQ(FX_LOCALECATEGORY_Time, fmt(L"en", L"time{}")->GetCategory());
-  EXPECT_EQ(FX_LOCALECATEGORY_Date, fmt(L"en", L"date{}")->GetCategory());
-  EXPECT_EQ(FX_LOCALECATEGORY_DateTime,
-            fmt(L"en", L"time{} date{}")->GetCategory());
-  EXPECT_EQ(FX_LOCALECATEGORY_DateTime,
-            fmt(L"en", L"date{} time{}")->GetCategory());
-  EXPECT_EQ(FX_LOCALECATEGORY_Num, fmt(L"en", L"num(en_GB){}")->GetCategory());
-  EXPECT_EQ(FX_LOCALECATEGORY_Date, fmt(L"en", L"date.long{}")->GetCategory());
+  EXPECT_EQ(CFGAS_StringFormatter::Category::kUnknown,
+            CFGAS_StringFormatter(L"'just text'").GetCategory());
+  EXPECT_EQ(CFGAS_StringFormatter::Category::kNull,
+            CFGAS_StringFormatter(L"null{}").GetCategory());
+  EXPECT_EQ(CFGAS_StringFormatter::Category::kZero,
+            CFGAS_StringFormatter(L"zero{}").GetCategory());
+  EXPECT_EQ(CFGAS_StringFormatter::Category::kNum,
+            CFGAS_StringFormatter(L"num{}").GetCategory());
+  EXPECT_EQ(CFGAS_StringFormatter::Category::kText,
+            CFGAS_StringFormatter(L"text{}").GetCategory());
+  EXPECT_EQ(CFGAS_StringFormatter::Category::kDateTime,
+            CFGAS_StringFormatter(L"datetime{}").GetCategory());
+  EXPECT_EQ(CFGAS_StringFormatter::Category::kTime,
+            CFGAS_StringFormatter(L"time{}").GetCategory());
+  EXPECT_EQ(CFGAS_StringFormatter::Category::kDate,
+            CFGAS_StringFormatter(L"date{}").GetCategory());
+  EXPECT_EQ(CFGAS_StringFormatter::Category::kDateTime,
+            CFGAS_StringFormatter(L"time{} date{}").GetCategory());
+  EXPECT_EQ(CFGAS_StringFormatter::Category::kDateTime,
+            CFGAS_StringFormatter(L"date{} time{}").GetCategory());
+  EXPECT_EQ(CFGAS_StringFormatter::Category::kNum,
+            CFGAS_StringFormatter(L"num(en_GB){}").GetCategory());
+  EXPECT_EQ(CFGAS_StringFormatter::Category::kDate,
+            CFGAS_StringFormatter(L"date.long{}").GetCategory());
 }
diff --git a/xfa/fgas/crt/locale_iface.h b/xfa/fgas/crt/locale_iface.h
index b5aecdb..2c0be28 100644
--- a/xfa/fgas/crt/locale_iface.h
+++ b/xfa/fgas/crt/locale_iface.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,45 +7,25 @@
 #ifndef XFA_FGAS_CRT_LOCALE_IFACE_H_
 #define XFA_FGAS_CRT_LOCALE_IFACE_H_
 
-#include "core/fxcrt/cfx_datetime.h"
-#include "core/fxcrt/fx_string.h"
-
-enum FX_LOCALEDATETIMESUBCATEGORY {
-  FX_LOCALEDATETIMESUBCATEGORY_Default,
-  FX_LOCALEDATETIMESUBCATEGORY_Short,
-  FX_LOCALEDATETIMESUBCATEGORY_Medium,
-  FX_LOCALEDATETIMESUBCATEGORY_Full,
-  FX_LOCALEDATETIMESUBCATEGORY_Long,
-};
-
-enum FX_LOCALENUMSUBCATEGORY {
-  FX_LOCALENUMPATTERN_Percent,
-  FX_LOCALENUMPATTERN_Currency,
-  FX_LOCALENUMPATTERN_Decimal,
-  FX_LOCALENUMPATTERN_Integer,
-};
-
-enum FX_LOCALECATEGORY {
-  FX_LOCALECATEGORY_Unknown,
-  FX_LOCALECATEGORY_Date,
-  FX_LOCALECATEGORY_Time,
-  FX_LOCALECATEGORY_DateTime,
-  FX_LOCALECATEGORY_Num,
-  FX_LOCALECATEGORY_Text,
-  FX_LOCALECATEGORY_Zero,
-  FX_LOCALECATEGORY_Null,
-};
-
-enum FX_DATETIMETYPE {
-  FX_DATETIMETYPE_Unknown,
-  FX_DATETIMETYPE_Date,
-  FX_DATETIMETYPE_Time,
-  FX_DATETIMETYPE_DateTime,
-  FX_DATETIMETYPE_TimeDate,
-};
+#include "core/fxcrt/widestring.h"
 
 class LocaleIface {
  public:
+  enum class DateTimeSubcategory {
+    kDefault,
+    kShort,
+    kMedium,
+    kFull,
+    kLong,
+  };
+
+  enum class NumSubcategory {
+    kPercent,
+    kCurrency,
+    kDecimal,
+    kInteger,
+  };
+
   virtual ~LocaleIface() = default;
 
   virtual WideString GetName() const = 0;
@@ -58,13 +38,11 @@
   virtual WideString GetMonthName(int32_t nMonth, bool bAbbr) const = 0;
   virtual WideString GetDayName(int32_t nWeek, bool bAbbr) const = 0;
   virtual WideString GetMeridiemName(bool bAM) const = 0;
-  virtual FX_TIMEZONE GetTimeZone() const = 0;
+  virtual int GetTimeZoneInMinutes() const = 0;
   virtual WideString GetEraName(bool bAD) const = 0;
-  virtual WideString GetDatePattern(
-      FX_LOCALEDATETIMESUBCATEGORY eType) const = 0;
-  virtual WideString GetTimePattern(
-      FX_LOCALEDATETIMESUBCATEGORY eType) const = 0;
-  virtual WideString GetNumPattern(FX_LOCALENUMSUBCATEGORY eType) const = 0;
+  virtual WideString GetDatePattern(DateTimeSubcategory eType) const = 0;
+  virtual WideString GetTimePattern(DateTimeSubcategory eType) const = 0;
+  virtual WideString GetNumPattern(NumSubcategory eType) const = 0;
 };
 
 #endif  // XFA_FGAS_CRT_LOCALE_IFACE_H_
diff --git a/xfa/fgas/crt/locale_mgr_iface.h b/xfa/fgas/crt/locale_mgr_iface.h
index 42f5ada..bf431b7 100644
--- a/xfa/fgas/crt/locale_mgr_iface.h
+++ b/xfa/fgas/crt/locale_mgr_iface.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #ifndef XFA_FGAS_CRT_LOCALE_MGR_IFACE_H_
 #define XFA_FGAS_CRT_LOCALE_MGR_IFACE_H_
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 
 class LocaleIface;
 
diff --git a/xfa/fgas/font/BUILD.gn b/xfa/fgas/font/BUILD.gn
new file mode 100644
index 0000000..ae23883
--- /dev/null
+++ b/xfa/fgas/font/BUILD.gn
@@ -0,0 +1,47 @@
+# Copyright 2018 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../../pdfium.gni")
+import("../../../testing/test.gni")
+
+assert(pdf_enable_xfa)
+
+source_set("font") {
+  sources = [
+    "cfgas_defaultfontmanager.cpp",
+    "cfgas_defaultfontmanager.h",
+    "cfgas_fontmgr.cpp",
+    "cfgas_fontmgr.h",
+    "cfgas_gefont.cpp",
+    "cfgas_gefont.h",
+    "cfgas_gemodule.cpp",
+    "cfgas_gemodule.h",
+    "cfgas_pdffontmgr.cpp",
+    "cfgas_pdffontmgr.h",
+    "fgas_fontutils.cpp",
+    "fgas_fontutils.h",
+  ]
+  configs += [
+    "../../../:pdfium_strict_config",
+    "../../../:pdfium_noshorten_config",
+    "../../:xfa_warnings",
+  ]
+  deps = [
+    "../../../core/fpdfapi/font",
+    "../../../core/fpdfapi/page",
+    "../../../core/fpdfapi/parser",
+    "../../../core/fxcrt",
+    "../../../core/fxge",
+  ]
+  visibility = [ "../../../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [ "fgas_fontutils_unittest.cpp" ]
+  deps = [
+    ":font",
+    "../../../core/fxcrt",
+  ]
+  pdfium_root_dir = "../../../"
+}
diff --git a/xfa/fgas/font/cfgas_defaultfontmanager.cpp b/xfa/fgas/font/cfgas_defaultfontmanager.cpp
index 239dfa7..f36f4df 100644
--- a/xfa/fgas/font/cfgas_defaultfontmanager.cpp
+++ b/xfa/fgas/font/cfgas_defaultfontmanager.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,18 +6,21 @@
 
 #include "xfa/fgas/font/cfgas_defaultfontmanager.h"
 
+#include "core/fxcrt/fx_codepage.h"
 #include "core/fxge/fx_font.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "xfa/fgas/font/cfgas_fontmgr.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
+#include "xfa/fgas/font/cfgas_gemodule.h"
 #include "xfa/fgas/font/fgas_fontutils.h"
 
 // static
 RetainPtr<CFGAS_GEFont> CFGAS_DefaultFontManager::GetFont(
-    CFGAS_FontMgr* pFontMgr,
-    WideStringView wsFontFamily,
+    WideString wsFontName,
     uint32_t dwFontStyles) {
-  WideString wsFontName(wsFontFamily);
-  RetainPtr<CFGAS_GEFont> pFont =
-      pFontMgr->LoadFont(wsFontName.c_str(), dwFontStyles, 0xFFFF);
+  CFGAS_FontMgr* pFontMgr = CFGAS_GEModule::Get()->GetFontMgr();
+  RetainPtr<CFGAS_GEFont> pFont = pFontMgr->LoadFont(
+      wsFontName.c_str(), dwFontStyles, FX_CodePage::kFailure);
   if (pFont)
     return pFont;
 
@@ -34,7 +37,7 @@
     dwStyle |= FXFONT_ITALIC;
 
   const char* pReplace = pCurFont->pReplaceFont;
-  int32_t iLength = strlen(pReplace);
+  int32_t iLength = pdfium::base::checked_cast<int32_t>(strlen(pReplace));
   while (iLength > 0) {
     const char* pNameText = pReplace;
     while (*pNameText != ',' && iLength > 0) {
@@ -43,7 +46,8 @@
     }
     WideString wsReplace =
         WideString::FromASCII(ByteStringView(pReplace, pNameText - pReplace));
-    pFont = pFontMgr->LoadFont(wsReplace.c_str(), dwStyle, 0xFFFF);
+    pFont =
+        pFontMgr->LoadFont(wsReplace.c_str(), dwStyle, FX_CodePage::kFailure);
     if (pFont)
       break;
 
@@ -56,14 +60,12 @@
 
 // static
 RetainPtr<CFGAS_GEFont> CFGAS_DefaultFontManager::GetDefaultFont(
-    CFGAS_FontMgr* pFontMgr,
-    WideStringView wsFontFamily,
     uint32_t dwFontStyles) {
+  CFGAS_FontMgr* pFontMgr = CFGAS_GEModule::Get()->GetFontMgr();
   RetainPtr<CFGAS_GEFont> pFont =
-      pFontMgr->LoadFont(L"Arial Narrow", dwFontStyles, 0xFFFF);
-  if (!pFont) {
-    pFont = pFontMgr->LoadFont(static_cast<const wchar_t*>(nullptr),
-                               dwFontStyles, 0xFFFF);
-  }
-  return pFont;
+      pFontMgr->LoadFont(L"Arial Narrow", dwFontStyles, FX_CodePage::kFailure);
+  if (pFont)
+    return pFont;
+
+  return pFontMgr->LoadFont(nullptr, dwFontStyles, FX_CodePage::kFailure);
 }
diff --git a/xfa/fgas/font/cfgas_defaultfontmanager.h b/xfa/fgas/font/cfgas_defaultfontmanager.h
index ebf2f3c..c2f6b76 100644
--- a/xfa/fgas/font/cfgas_defaultfontmanager.h
+++ b/xfa/fgas/font/cfgas_defaultfontmanager.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,20 +7,16 @@
 #ifndef XFA_FGAS_FONT_CFGAS_DEFAULTFONTMANAGER_H_
 #define XFA_FGAS_FONT_CFGAS_DEFAULTFONTMANAGER_H_
 
-#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/widestring.h"
 
 class CFGAS_GEFont;
-class CFGAS_FontMgr;
 
 class CFGAS_DefaultFontManager {
  public:
-  static RetainPtr<CFGAS_GEFont> GetFont(CFGAS_FontMgr* pFontMgr,
-                                         WideStringView wsFontFamily,
+  static RetainPtr<CFGAS_GEFont> GetFont(WideString wsFontName,
                                          uint32_t dwFontStyles);
-  static RetainPtr<CFGAS_GEFont> GetDefaultFont(CFGAS_FontMgr* pFontMgr,
-                                                WideStringView wsFontFamily,
-                                                uint32_t dwFontStyles);
+  static RetainPtr<CFGAS_GEFont> GetDefaultFont(uint32_t dwFontStyles);
 
   CFGAS_DefaultFontManager() = delete;
   CFGAS_DefaultFontManager(const CFGAS_DefaultFontManager&) = delete;
diff --git a/xfa/fgas/font/cfgas_fontmgr.cpp b/xfa/fgas/font/cfgas_fontmgr.cpp
index 96407f3..bb913b4 100644
--- a/xfa/fgas/font/cfgas_fontmgr.cpp
+++ b/xfa/fgas/font/cfgas_fontmgr.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,28 +6,33 @@
 
 #include "xfa/fgas/font/cfgas_fontmgr.h"
 
+#include <stdint.h>
+
 #include <algorithm>
+#include <iterator>
 #include <memory>
 #include <utility>
 
 #include "build/build_config.h"
-#include "core/fxcrt/cfx_memorystream.h"
+#include "core/fxcrt/cfx_read_only_vector_stream.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fixed_uninit_data_vector.h"
 #include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_system.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_fontmapper.h"
 #include "core/fxge/cfx_fontmgr.h"
 #include "core/fxge/cfx_gemodule.h"
 #include "core/fxge/fx_font.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/containers/span.h"
+#include "third_party/base/numerics/safe_conversions.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
 #include "xfa/fgas/font/fgas_fontutils.h"
 
-#if !defined(OS_WIN)
-#include "xfa/fgas/font/cfx_fontsourceenum_file.h"
-#endif
-
 namespace {
 
 bool VerifyUnicode(const RetainPtr<CFGAS_GEFont>& pFont, wchar_t wcUnicode) {
@@ -36,7 +41,7 @@
     return false;
 
   FXFT_FaceRec* pFaceRec = pFace->GetRec();
-  FT_CharMap charmap = FXFT_Get_Face_Charmap(pFaceRec);
+  FT_CharMap charmap = pFaceRec->charmap;
   if (FXFT_Select_Charmap(pFaceRec, FT_ENCODING_UNICODE) != 0)
     return false;
 
@@ -47,12 +52,39 @@
   return true;
 }
 
+uint32_t ShortFormHash(FX_CodePage wCodePage,
+                       uint32_t dwFontStyles,
+                       WideStringView wsFontFamily) {
+  ByteString bsHash = ByteString::Format("%d, %d", wCodePage, dwFontStyles);
+  bsHash += FX_UTF8Encode(wsFontFamily);
+  return FX_HashCode_GetA(bsHash.AsStringView());
+}
+
+uint32_t LongFormHash(FX_CodePage wCodePage,
+                      uint16_t wBitField,
+                      uint32_t dwFontStyles,
+                      WideStringView wsFontFamily) {
+  ByteString bsHash =
+      ByteString::Format("%d, %d, %d", wCodePage, wBitField, dwFontStyles);
+  bsHash += FX_UTF8Encode(wsFontFamily);
+  return FX_HashCode_GetA(bsHash.AsStringView());
+}
+
 }  // namespace
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 
 namespace {
 
+struct FX_FONTMATCHPARAMS {
+  const wchar_t* pwsFamily;
+  uint32_t dwFontStyles;
+  uint32_t dwUSB;
+  bool matchParagraphStyle;
+  wchar_t wUnicode;
+  FX_CodePage wCodePage;
+};
+
 int32_t GetSimilarityScore(FX_FONTDESCRIPTOR const* pFont,
                            uint32_t dwFontStyles) {
   int32_t iValue = 0;
@@ -85,12 +117,12 @@
     if (pParams->pwsFamily) {
       if (FXSYS_wcsicmp(pParams->pwsFamily, font.wsFontFace))
         continue;
-      if (font.uCharSet == FX_CHARSET_Symbol)
+      if (font.uCharSet == FX_Charset::kSymbol)
         return &font;
     }
-    if (font.uCharSet == FX_CHARSET_Symbol)
+    if (font.uCharSet == FX_Charset::kSymbol)
       continue;
-    if (pParams->wCodePage != 0xFFFF) {
+    if (pParams->wCodePage != FX_CodePage::kFailure) {
       if (FX_GetCodePageFromCharset(font.uCharSet) != pParams->wCodePage)
         continue;
     } else {
@@ -144,7 +176,7 @@
     return 1;
   FX_FONTDESCRIPTOR font;
   memset(&font, 0, sizeof(FX_FONTDESCRIPTOR));
-  font.uCharSet = lf.lfCharSet;
+  font.uCharSet = FX_GetCharsetFromInt(lf.lfCharSet);
   font.dwFontStyles = GetGdiFontStyles(lf);
   FXSYS_wcsncpy(font.wsFontFace, (const wchar_t*)lf.lfFaceName, 31);
   font.wsFontFace[31] = 0;
@@ -185,7 +217,7 @@
     uint32_t dwFontStyles,
     const wchar_t* pszFontFamily,
     uint32_t dwHash,
-    uint16_t wCodePage,
+    FX_CodePage wCodePage,
     uint16_t wBitField) {
   const FX_FONTDESCRIPTOR* pFD = FindFont(pszFontFamily, dwFontStyles, false,
                                           wCodePage, wBitField, wUnicode);
@@ -196,10 +228,9 @@
   if (!pFD)
     return nullptr;
 
-  uint16_t newCodePage = FX_GetCodePageFromCharset(pFD->uCharSet);
-  const wchar_t* pFontFace = pFD->wsFontFace;
+  FX_CodePage newCodePage = FX_GetCodePageFromCharset(pFD->uCharSet);
   RetainPtr<CFGAS_GEFont> pFont =
-      CFGAS_GEFont::LoadFont(pFontFace, dwFontStyles, newCodePage, this);
+      CFGAS_GEFont::LoadFont(pFD->wsFontFace, dwFontStyles, newCodePage);
   if (!pFont)
     return nullptr;
 
@@ -216,7 +247,7 @@
 const FX_FONTDESCRIPTOR* CFGAS_FontMgr::FindFont(const wchar_t* pszFontFamily,
                                                  uint32_t dwFontStyles,
                                                  bool matchParagraphStyle,
-                                                 uint16_t wCodePage,
+                                                 FX_CodePage wCodePage,
                                                  uint32_t dwUSB,
                                                  wchar_t wUnicode) {
   FX_FONTMATCHPARAMS params;
@@ -253,78 +284,78 @@
   return &m_FontFaces.back();
 }
 
-#else  // defined(OS_WIN)
+#else  // BUILDFLAG(IS_WIN)
 
 namespace {
 
-const uint16_t g_CodePages[] = {FX_CODEPAGE_MSWin_WesternEuropean,
-                                FX_CODEPAGE_MSWin_EasternEuropean,
-                                FX_CODEPAGE_MSWin_Cyrillic,
-                                FX_CODEPAGE_MSWin_Greek,
-                                FX_CODEPAGE_MSWin_Turkish,
-                                FX_CODEPAGE_MSWin_Hebrew,
-                                FX_CODEPAGE_MSWin_Arabic,
-                                FX_CODEPAGE_MSWin_Baltic,
-                                FX_CODEPAGE_MSWin_Vietnamese,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_MSDOS_Thai,
-                                FX_CODEPAGE_ShiftJIS,
-                                FX_CODEPAGE_ChineseSimplified,
-                                FX_CODEPAGE_Hangul,
-                                FX_CODEPAGE_ChineseTraditional,
-                                FX_CODEPAGE_Johab,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_DefANSI,
-                                FX_CODEPAGE_MSDOS_Greek2,
-                                FX_CODEPAGE_MSDOS_Russian,
-                                FX_CODEPAGE_MSDOS_Norwegian,
-                                FX_CODEPAGE_MSDOS_Arabic,
-                                FX_CODEPAGE_MSDOS_FrenchCanadian,
-                                FX_CODEPAGE_MSDOS_Hebrew,
-                                FX_CODEPAGE_MSDOS_Icelandic,
-                                FX_CODEPAGE_MSDOS_Portuguese,
-                                FX_CODEPAGE_MSDOS_Turkish,
-                                FX_CODEPAGE_MSDOS_Cyrillic,
-                                FX_CODEPAGE_MSDOS_EasternEuropean,
-                                FX_CODEPAGE_MSDOS_Baltic,
-                                FX_CODEPAGE_MSDOS_Greek1,
-                                FX_CODEPAGE_Arabic_ASMO708,
-                                FX_CODEPAGE_MSDOS_WesternEuropean,
-                                FX_CODEPAGE_MSDOS_US};
+const FX_CodePage kCodePages[] = {FX_CodePage::kMSWin_WesternEuropean,
+                                  FX_CodePage::kMSWin_EasternEuropean,
+                                  FX_CodePage::kMSWin_Cyrillic,
+                                  FX_CodePage::kMSWin_Greek,
+                                  FX_CodePage::kMSWin_Turkish,
+                                  FX_CodePage::kMSWin_Hebrew,
+                                  FX_CodePage::kMSWin_Arabic,
+                                  FX_CodePage::kMSWin_Baltic,
+                                  FX_CodePage::kMSWin_Vietnamese,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kMSDOS_Thai,
+                                  FX_CodePage::kShiftJIS,
+                                  FX_CodePage::kChineseSimplified,
+                                  FX_CodePage::kHangul,
+                                  FX_CodePage::kChineseTraditional,
+                                  FX_CodePage::kJohab,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kDefANSI,
+                                  FX_CodePage::kMSDOS_Greek2,
+                                  FX_CodePage::kMSDOS_Russian,
+                                  FX_CodePage::kMSDOS_Norwegian,
+                                  FX_CodePage::kMSDOS_Arabic,
+                                  FX_CodePage::kMSDOS_FrenchCanadian,
+                                  FX_CodePage::kMSDOS_Hebrew,
+                                  FX_CodePage::kMSDOS_Icelandic,
+                                  FX_CodePage::kMSDOS_Portuguese,
+                                  FX_CodePage::kMSDOS_Turkish,
+                                  FX_CodePage::kMSDOS_Cyrillic,
+                                  FX_CodePage::kMSDOS_EasternEuropean,
+                                  FX_CodePage::kMSDOS_Baltic,
+                                  FX_CodePage::kMSDOS_Greek1,
+                                  FX_CodePage::kArabic_ASMO708,
+                                  FX_CodePage::kMSDOS_WesternEuropean,
+                                  FX_CodePage::kMSDOS_US};
 
-uint16_t FX_GetCodePageBit(uint16_t wCodePage) {
-  for (size_t i = 0; i < FX_ArraySize(g_CodePages); ++i) {
-    if (g_CodePages[i] == wCodePage)
+uint16_t FX_GetCodePageBit(FX_CodePage wCodePage) {
+  for (size_t i = 0; i < std::size(kCodePages); ++i) {
+    if (kCodePages[i] == wCodePage)
       return static_cast<uint16_t>(i);
   }
   return static_cast<uint16_t>(-1);
@@ -332,15 +363,13 @@
 
 uint16_t FX_GetUnicodeBit(wchar_t wcUnicode) {
   const FGAS_FONTUSB* x = FGAS_GetUnicodeBitField(wcUnicode);
-  return x ? x->wBitField : 999;
+  return x ? x->wBitField : FGAS_FONTUSB::kNoBitField;
 }
 
-inline uint8_t GetUInt8(const uint8_t* p) {
-  return p[0];
-}
-
-inline uint16_t GetUInt16(const uint8_t* p) {
-  return static_cast<uint16_t>(p[0] << 8 | p[1]);
+uint16_t ReadUInt16FromSpanAtOffset(pdfium::span<const uint8_t> data,
+                                    size_t offset) {
+  const uint8_t* p = &data[offset];
+  return FXSYS_UINT16_GET_MSBFIRST(p);
 }
 
 extern "C" {
@@ -354,7 +383,7 @@
 
   IFX_SeekableReadStream* pFile =
       static_cast<IFX_SeekableReadStream*>(stream->descriptor.pointer);
-  if (!pFile->ReadBlockAtOffset(buffer, offset, count))
+  if (!pFile->ReadBlockAtOffset({buffer, count}, offset))
     return 0;
 
   return count;
@@ -364,37 +393,36 @@
 
 }  // extern "C"
 
-// TODO(thestig): Pass in |name_table| as a std::vector?
-std::vector<WideString> GetNames(const uint8_t* name_table) {
+std::vector<WideString> GetNames(pdfium::span<const uint8_t> name_table) {
   std::vector<WideString> results;
-  if (!name_table)
+  if (name_table.empty())
     return results;
 
-  const uint8_t* lpTable = name_table;
-  WideString wsFamily;
-  const uint8_t* sp = lpTable + 2;
-  const uint8_t* lpNameRecord = lpTable + 6;
-  uint16_t nNameCount = GetUInt16(sp);
-  const uint8_t* lpStr = lpTable + GetUInt16(sp + 2);
-  for (uint16_t j = 0; j < nNameCount; j++) {
-    uint16_t nNameID = GetUInt16(lpNameRecord + j * 12 + 6);
+  uint16_t nNameCount = ReadUInt16FromSpanAtOffset(name_table, 2);
+  pdfium::span<const uint8_t> str =
+      name_table.subspan(ReadUInt16FromSpanAtOffset(name_table, 4));
+  pdfium::span<const uint8_t> name_record = name_table.subspan(6);
+  for (uint16_t i = 0; i < nNameCount; ++i) {
+    uint16_t nNameID = ReadUInt16FromSpanAtOffset(name_table, i * 12 + 6);
     if (nNameID != 1)
       continue;
 
-    uint16_t nPlatformID = GetUInt16(lpNameRecord + j * 12 + 0);
-    uint16_t nNameLength = GetUInt16(lpNameRecord + j * 12 + 8);
-    uint16_t nNameOffset = GetUInt16(lpNameRecord + j * 12 + 10);
-    wsFamily.clear();
+    uint16_t nPlatformID = ReadUInt16FromSpanAtOffset(name_record, i * 12);
+    uint16_t nNameLength = ReadUInt16FromSpanAtOffset(name_record, i * 12 + 8);
+    uint16_t nNameOffset = ReadUInt16FromSpanAtOffset(name_record, i * 12 + 10);
     if (nPlatformID != 1) {
-      for (uint16_t k = 0; k < nNameLength / 2; k++) {
-        wchar_t wcTemp = GetUInt16(lpStr + nNameOffset + k * 2);
+      WideString wsFamily;
+      for (uint16_t j = 0; j < nNameLength / 2; ++j) {
+        wchar_t wcTemp = ReadUInt16FromSpanAtOffset(str, nNameOffset + j * 2);
         wsFamily += wcTemp;
       }
       results.push_back(wsFamily);
       continue;
     }
-    for (uint16_t k = 0; k < nNameLength; k++) {
-      wchar_t wcTemp = GetUInt8(lpStr + nNameOffset + k);
+
+    WideString wsFamily;
+    for (uint16_t j = 0; j < nNameLength; ++j) {
+      wchar_t wcTemp = str[nNameOffset + j];
       wsFamily += wcTemp;
     }
     results.push_back(wsFamily);
@@ -413,12 +441,12 @@
     CSB[1] = 0;
     return;
   }
-  USB[0] = pOS2->ulUnicodeRange1;
-  USB[1] = pOS2->ulUnicodeRange2;
-  USB[2] = pOS2->ulUnicodeRange3;
-  USB[3] = pOS2->ulUnicodeRange4;
-  CSB[0] = pOS2->ulCodePageRange1;
-  CSB[1] = pOS2->ulCodePageRange2;
+  USB[0] = static_cast<uint32_t>(pOS2->ulUnicodeRange1);
+  USB[1] = static_cast<uint32_t>(pOS2->ulUnicodeRange2);
+  USB[2] = static_cast<uint32_t>(pOS2->ulUnicodeRange3);
+  USB[3] = static_cast<uint32_t>(pOS2->ulUnicodeRange4);
+  CSB[0] = static_cast<uint32_t>(pOS2->ulCodePageRange1);
+  CSB[1] = static_cast<uint32_t>(pOS2->ulCodePageRange2);
 }
 
 uint32_t GetFlags(FXFT_FaceRec* pFace) {
@@ -444,16 +472,13 @@
   return flags;
 }
 
-RetainPtr<IFX_SeekableReadStream> CreateFontStream(
-    CFX_FontMapper* pFontMapper,
-    uint32_t index) {
-  size_t dwFileSize = 0;
-  std::unique_ptr<uint8_t, FxFreeDeleter> pBuffer =
-      pFontMapper->RawBytesForIndex(index, &dwFileSize);
-  if (!pBuffer)
+RetainPtr<IFX_SeekableReadStream> CreateFontStream(CFX_FontMapper* pFontMapper,
+                                                   size_t index) {
+  FixedUninitDataVector<uint8_t> buffer = pFontMapper->RawBytesForIndex(index);
+  if (buffer.empty())
     return nullptr;
 
-  return pdfium::MakeRetain<CFX_MemoryStream>(std::move(pBuffer), dwFileSize);
+  return pdfium::MakeRetain<CFX_ReadOnlyVectorStream>(std::move(buffer));
 }
 
 RetainPtr<IFX_SeekableReadStream> CreateFontStream(
@@ -462,7 +487,7 @@
   CFX_FontMapper* pFontMapper = pFontMgr->GetBuiltinMapper();
   pFontMapper->LoadInstalledFonts();
 
-  for (int32_t i = 0; i < pFontMapper->GetFaceSize(); ++i) {
+  for (size_t i = 0; i < pFontMapper->GetFaceSize(); ++i) {
     if (pFontMapper->GetFaceName(i) == bsFaceName)
       return CreateFontStream(pFontMapper, i);
   }
@@ -508,7 +533,7 @@
   return pFace;
 }
 
-bool VerifyUnicodeForFontDescriptor(CFX_FontDescriptor* pDesc,
+bool VerifyUnicodeForFontDescriptor(CFGAS_FontDescriptor* pDesc,
                                     wchar_t wcUnicode) {
   RetainPtr<IFX_SeekableReadStream> pFileRead =
       CreateFontStream(pDesc->m_wsFaceName.ToUTF8());
@@ -533,8 +558,8 @@
   return name1.Contains(name2.AsStringView());
 }
 
-int32_t CalcPenalty(CFX_FontDescriptor* pInstalled,
-                    uint16_t wCodePage,
+int32_t CalcPenalty(CFGAS_FontDescriptor* pInstalled,
+                    FX_CodePage wCodePage,
                     uint32_t dwFontStyles,
                     const WideString& FontName,
                     wchar_t wcUnicode) {
@@ -581,20 +606,21 @@
   if (nPenalty >= 0xFFFF)
     return 0xFFFF;
 
-  uint16_t wBit = (wCodePage == FX_CODEPAGE_DefANSI || wCodePage == 0xFFFF)
-                      ? static_cast<uint16_t>(-1)
-                      : FX_GetCodePageBit(wCodePage);
+  uint16_t wBit =
+      (wCodePage == FX_CodePage::kDefANSI || wCodePage == FX_CodePage::kFailure)
+          ? static_cast<uint16_t>(-1)
+          : FX_GetCodePageBit(wCodePage);
   if (wBit != static_cast<uint16_t>(-1)) {
-    ASSERT(wBit < 64);
+    DCHECK(wBit < 64);
     if ((pInstalled->m_dwCsb[wBit / 32] & (1 << (wBit % 32))) == 0)
       nPenalty += 0xFFFF;
     else
       nPenalty -= 60000;
   }
-  wBit = (wcUnicode == 0 || wcUnicode == 0xFFFE) ? static_cast<uint16_t>(999)
+  wBit = (wcUnicode == 0 || wcUnicode == 0xFFFE) ? FGAS_FONTUSB::kNoBitField
                                                  : FX_GetUnicodeBit(wcUnicode);
-  if (wBit != static_cast<uint16_t>(999)) {
-    ASSERT(wBit < 128);
+  if (wBit != FGAS_FONTUSB::kNoBitField) {
+    DCHECK(wBit < 128);
     if ((pInstalled->m_dwUsb[wBit / 32] & (1 << (wBit % 32))) == 0)
       nPenalty += 0xFFFF;
     else
@@ -605,22 +631,20 @@
 
 }  // namespace
 
-CFX_FontDescriptor::CFX_FontDescriptor()
-    : m_nFaceIndex(0), m_dwFontStyles(0), m_dwUsb(), m_dwCsb() {}
+CFGAS_FontDescriptor::CFGAS_FontDescriptor() = default;
 
-CFX_FontDescriptor::~CFX_FontDescriptor() {}
+CFGAS_FontDescriptor::~CFGAS_FontDescriptor() = default;
 
-CFGAS_FontMgr::CFGAS_FontMgr()
-    : m_pFontSource(pdfium::MakeUnique<CFX_FontSourceEnum_File>()) {}
+CFGAS_FontMgr::CFGAS_FontMgr() = default;
 
-CFGAS_FontMgr::~CFGAS_FontMgr() {}
+CFGAS_FontMgr::~CFGAS_FontMgr() = default;
 
 bool CFGAS_FontMgr::EnumFontsFromFontMapper() {
   CFX_FontMapper* pFontMapper =
       CFX_GEModule::Get()->GetFontMgr()->GetBuiltinMapper();
   pFontMapper->LoadInstalledFonts();
 
-  for (int32_t i = 0; i < pFontMapper->GetFaceSize(); ++i) {
+  for (size_t i = 0; i < pFontMapper->GetFaceSize(); ++i) {
     RetainPtr<IFX_SeekableReadStream> pFontStream =
         CreateFontStream(pFontMapper, i);
     if (!pFontStream)
@@ -628,25 +652,14 @@
 
     WideString wsFaceName =
         WideString::FromDefANSI(pFontMapper->GetFaceName(i).AsStringView());
-    RegisterFaces(pFontStream, &wsFaceName);
+    RegisterFaces(pFontStream, wsFaceName);
   }
 
   return !m_InstalledFonts.empty();
 }
 
-bool CFGAS_FontMgr::EnumFontsFromFiles() {
-  m_pFontSource->GetNext();
-  while (m_pFontSource->HasNext()) {
-    RetainPtr<IFX_SeekableStream> stream = m_pFontSource->GetStream();
-    if (stream)
-      RegisterFaces(stream, nullptr);
-    m_pFontSource->GetNext();
-  }
-  return !m_InstalledFonts.empty();
-}
-
 bool CFGAS_FontMgr::EnumFonts() {
-  return EnumFontsFromFontMapper() || EnumFontsFromFiles();
+  return EnumFontsFromFontMapper();
 }
 
 RetainPtr<CFGAS_GEFont> CFGAS_FontMgr::GetFontByUnicodeImpl(
@@ -654,19 +667,14 @@
     uint32_t dwFontStyles,
     const wchar_t* pszFontFamily,
     uint32_t dwHash,
-    uint16_t wCodePage,
-    uint16_t /* wBitField */) {
-  std::vector<CFX_FontDescriptorInfo>* sortedFontInfos =
-      m_Hash2CandidateList[dwHash].get();
-  if (!sortedFontInfos) {
-    auto pNewFonts = pdfium::MakeUnique<std::vector<CFX_FontDescriptorInfo>>();
-    sortedFontInfos = pNewFonts.get();
-    MatchFonts(sortedFontInfos, wCodePage, dwFontStyles,
-               WideString(pszFontFamily), wUnicode);
-    m_Hash2CandidateList[dwHash] = std::move(pNewFonts);
+    FX_CodePage wCodePage,
+    uint16_t /* wBitField*/) {
+  if (!pdfium::Contains(m_Hash2CandidateList, dwHash)) {
+    m_Hash2CandidateList[dwHash] =
+        MatchFonts(wCodePage, dwFontStyles, pszFontFamily, wUnicode);
   }
-  for (const auto& info : *sortedFontInfos) {
-    CFX_FontDescriptor* pDesc = info.pFont;
+  for (const auto& info : m_Hash2CandidateList[dwHash]) {
+    CFGAS_FontDescriptor* pDesc = info.pFont;
     if (!VerifyUnicodeForFontDescriptor(pDesc, wUnicode))
       continue;
     RetainPtr<CFGAS_GEFont> pFont =
@@ -690,44 +698,38 @@
   if (!pFontStream)
     return nullptr;
 
-  auto pInternalFont = pdfium::MakeUnique<CFX_Font>();
-  if (!pInternalFont->LoadFile(pFontStream, iFaceIndex))
+  auto pInternalFont = std::make_unique<CFX_Font>();
+  if (!pInternalFont->LoadFile(std::move(pFontStream), iFaceIndex))
     return nullptr;
 
-  RetainPtr<CFGAS_GEFont> pFont =
-      CFGAS_GEFont::LoadFont(std::move(pInternalFont), this);
-  if (!pFont)
-    return nullptr;
-
-  m_IFXFont2FileRead[pFont] = pFontStream;
-  return pFont;
+  return CFGAS_GEFont::LoadFont(std::move(pInternalFont));
 }
 
-void CFGAS_FontMgr::MatchFonts(
-    std::vector<CFX_FontDescriptorInfo>* pMatchedFonts,
-    uint16_t wCodePage,
+std::vector<CFGAS_FontDescriptorInfo> CFGAS_FontMgr::MatchFonts(
+    FX_CodePage wCodePage,
     uint32_t dwFontStyles,
     const WideString& FontName,
     wchar_t wcUnicode) {
-  pMatchedFonts->clear();
+  std::vector<CFGAS_FontDescriptorInfo> matched_fonts;
   for (const auto& pFont : m_InstalledFonts) {
     int32_t nPenalty =
         CalcPenalty(pFont.get(), wCodePage, dwFontStyles, FontName, wcUnicode);
     if (nPenalty >= 0xffff)
       continue;
-    pMatchedFonts->push_back({pFont.get(), nPenalty});
-    if (pMatchedFonts->size() == 0xffff)
+    matched_fonts.push_back({pFont.get(), nPenalty});
+    if (matched_fonts.size() == 0xffff)
       break;
   }
-  std::sort(pMatchedFonts->begin(), pMatchedFonts->end());
+  std::stable_sort(matched_fonts.begin(), matched_fonts.end());
+  return matched_fonts;
 }
 
 void CFGAS_FontMgr::RegisterFace(RetainPtr<CFX_Face> pFace,
-                                 const WideString* pFaceName) {
+                                 const WideString& wsFaceName) {
   if ((pFace->GetRec()->face_flags & FT_FACE_FLAG_SCALABLE) == 0)
     return;
 
-  auto pFont = pdfium::MakeUnique<CFX_FontDescriptor>();
+  auto pFont = std::make_unique<CFGAS_FontDescriptor>();
   pFont->m_dwFontStyles |= GetFlags(pFace->GetRec());
 
   GetUSBCSB(pFace->GetRec(), pFont->m_dwUsb, pFont->m_dwCsb);
@@ -735,7 +737,7 @@
   FT_ULong dwTag;
   FT_ENC_TAG(dwTag, 'n', 'a', 'm', 'e');
 
-  std::vector<uint8_t> table;
+  DataVector<uint8_t> table;
   unsigned long nLength = 0;
   unsigned int error =
       FT_Load_Sfnt_Table(pFace->GetRec(), dwTag, 0, nullptr, &nLength);
@@ -744,20 +746,18 @@
     if (FT_Load_Sfnt_Table(pFace->GetRec(), dwTag, 0, table.data(), nullptr))
       table.clear();
   }
-  pFont->m_wsFamilyNames = GetNames(table.empty() ? nullptr : table.data());
+  pFont->m_wsFamilyNames = GetNames(table);
   pFont->m_wsFamilyNames.push_back(
       WideString::FromUTF8(pFace->GetRec()->family_name));
-  pFont->m_wsFaceName =
-      pFaceName
-          ? *pFaceName
-          : WideString::FromDefANSI(FT_Get_Postscript_Name(pFace->GetRec()));
-  pFont->m_nFaceIndex = pFace->GetRec()->face_index;
+  pFont->m_wsFaceName = wsFaceName;
+  pFont->m_nFaceIndex =
+      pdfium::base::checked_cast<int32_t>(pFace->GetRec()->face_index);
   m_InstalledFonts.push_back(std::move(pFont));
 }
 
 void CFGAS_FontMgr::RegisterFaces(
     const RetainPtr<IFX_SeekableReadStream>& pFontStream,
-    const WideString* pFaceName) {
+    const WideString& wsFaceName) {
   int32_t index = 0;
   int32_t num_faces = 0;
   do {
@@ -765,23 +765,23 @@
     if (!pFace)
       continue;
     // All faces keep number of faces. It can be retrieved from any one face.
-    if (num_faces == 0)
-      num_faces = pFace->GetRec()->num_faces;
-    RegisterFace(pFace, pFaceName);
+    if (num_faces == 0) {
+      num_faces =
+          pdfium::base::checked_cast<int32_t>(pFace->GetRec()->num_faces);
+    }
+    RegisterFace(pFace, wsFaceName);
     if (FXFT_Get_Face_External_Stream(pFace->GetRec()))
       FXFT_Clear_Face_External_Stream(pFace->GetRec());
   } while (index < num_faces);
 }
 
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 RetainPtr<CFGAS_GEFont> CFGAS_FontMgr::GetFontByCodePage(
-    uint16_t wCodePage,
+    FX_CodePage wCodePage,
     uint32_t dwFontStyles,
     const wchar_t* pszFontFamily) {
-  ByteString bsHash = ByteString::Format("%d, %d", wCodePage, dwFontStyles);
-  bsHash += FX_UTF8Encode(WideStringView(pszFontFamily));
-  uint32_t dwHash = FX_HashCode_GetA(bsHash.AsStringView(), false);
+  uint32_t dwHash = ShortFormHash(wCodePage, dwFontStyles, pszFontFamily);
   auto* pFontVector = &m_Hash2Fonts[dwHash];
   if (!pFontVector->empty()) {
     for (auto iter = pFontVector->begin(); iter != pFontVector->end(); ++iter) {
@@ -791,35 +791,35 @@
     return nullptr;
   }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   const FX_FONTDESCRIPTOR* pFD =
-      FindFont(pszFontFamily, dwFontStyles, true, wCodePage, 999, 0);
-  if (!pFD)
-    pFD = FindFont(nullptr, dwFontStyles, true, wCodePage, 999, 0);
-  if (!pFD)
-    pFD = FindFont(nullptr, dwFontStyles, false, wCodePage, 999, 0);
+      FindFont(pszFontFamily, dwFontStyles, true, wCodePage,
+               FGAS_FONTUSB::kNoBitField, 0);
+  if (!pFD) {
+    pFD = FindFont(nullptr, dwFontStyles, true, wCodePage,
+                   FGAS_FONTUSB::kNoBitField, 0);
+  }
+  if (!pFD) {
+    pFD = FindFont(nullptr, dwFontStyles, false, wCodePage,
+                   FGAS_FONTUSB::kNoBitField, 0);
+  }
   if (!pFD)
     return nullptr;
 
   RetainPtr<CFGAS_GEFont> pFont =
-      CFGAS_GEFont::LoadFont(pFD->wsFontFace, dwFontStyles, wCodePage, this);
-#else   // defined(OS_WIN)
-  std::vector<CFX_FontDescriptorInfo>* sortedFontInfos =
-      m_Hash2CandidateList[dwHash].get();
-  if (!sortedFontInfos) {
-    auto pNewFonts = pdfium::MakeUnique<std::vector<CFX_FontDescriptorInfo>>();
-    sortedFontInfos = pNewFonts.get();
-    MatchFonts(sortedFontInfos, wCodePage, dwFontStyles,
-               WideString(pszFontFamily), 0);
-    m_Hash2CandidateList[dwHash] = std::move(pNewFonts);
+      CFGAS_GEFont::LoadFont(pFD->wsFontFace, dwFontStyles, wCodePage);
+#else   // BUILDFLAG(IS_WIN)
+  if (!pdfium::Contains(m_Hash2CandidateList, dwHash)) {
+    m_Hash2CandidateList[dwHash] =
+        MatchFonts(wCodePage, dwFontStyles, WideString(pszFontFamily), 0);
   }
-  if (sortedFontInfos->empty())
+  if (m_Hash2CandidateList[dwHash].empty())
     return nullptr;
 
-  CFX_FontDescriptor* pDesc = (*sortedFontInfos)[0].pFont;
+  CFGAS_FontDescriptor* pDesc = m_Hash2CandidateList[dwHash].front().pFont;
   RetainPtr<CFGAS_GEFont> pFont =
       LoadFontInternal(pDesc->m_wsFaceName, pDesc->m_nFaceIndex);
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
   if (!pFont)
     return nullptr;
@@ -833,81 +833,52 @@
     wchar_t wUnicode,
     uint32_t dwFontStyles,
     const wchar_t* pszFontFamily) {
-  if (pdfium::ContainsKey(m_FailedUnicodesSet, wUnicode))
+  if (pdfium::Contains(m_FailedUnicodesSet, wUnicode))
     return nullptr;
 
   const FGAS_FONTUSB* x = FGAS_GetUnicodeBitField(wUnicode);
-  uint16_t wCodePage = x ? x->wCodePage : 0xFFFF;
-  uint16_t wBitField = x ? x->wBitField : 0x03E7;
-  ByteString bsHash;
-  if (wCodePage == 0xFFFF) {
-    bsHash =
-        ByteString::Format("%d, %d, %d", wCodePage, wBitField, dwFontStyles);
-  } else {
-    bsHash = ByteString::Format("%d, %d", wCodePage, dwFontStyles);
-  }
-  bsHash += FX_UTF8Encode(WideStringView(pszFontFamily));
-  uint32_t dwHash = FX_HashCode_GetA(bsHash.AsStringView(), false);
-  std::vector<RetainPtr<CFGAS_GEFont>>& fonts = m_Hash2Fonts[dwHash];
-  for (auto& pFont : fonts) {
+  FX_CodePage wCodePage = x ? x->wCodePage : FX_CodePage::kFailure;
+  uint16_t wBitField = x ? x->wBitField : FGAS_FONTUSB::kNoBitField;
+  uint32_t dwHash =
+      wCodePage == FX_CodePage::kFailure
+          ? LongFormHash(wCodePage, wBitField, dwFontStyles, pszFontFamily)
+          : ShortFormHash(wCodePage, dwFontStyles, pszFontFamily);
+  for (auto& pFont : m_Hash2Fonts[dwHash]) {
     if (VerifyUnicode(pFont, wUnicode))
       return pFont;
   }
-
   return GetFontByUnicodeImpl(wUnicode, dwFontStyles, pszFontFamily, dwHash,
                               wCodePage, wBitField);
 }
 
 RetainPtr<CFGAS_GEFont> CFGAS_FontMgr::LoadFont(const wchar_t* pszFontFamily,
                                                 uint32_t dwFontStyles,
-                                                uint16_t wCodePage) {
-#if defined(OS_WIN)
-  ByteString bsHash = ByteString::Format("%d, %d", wCodePage, dwFontStyles);
-  bsHash += FX_UTF8Encode(WideStringView(pszFontFamily));
-  uint32_t dwHash = FX_HashCode_GetA(bsHash.AsStringView(), false);
+                                                FX_CodePage wCodePage) {
+#if BUILDFLAG(IS_WIN)
+  uint32_t dwHash = ShortFormHash(wCodePage, dwFontStyles, pszFontFamily);
   std::vector<RetainPtr<CFGAS_GEFont>>* pFontArray = &m_Hash2Fonts[dwHash];
   if (!pFontArray->empty())
-    return (*pFontArray)[0];
+    return pFontArray->front();
 
   const FX_FONTDESCRIPTOR* pFD =
-      FindFont(pszFontFamily, dwFontStyles, true, wCodePage, 999, 0);
-  if (!pFD)
-    pFD = FindFont(pszFontFamily, dwFontStyles, false, wCodePage, 999, 0);
+      FindFont(pszFontFamily, dwFontStyles, true, wCodePage,
+               FGAS_FONTUSB::kNoBitField, 0);
+  if (!pFD) {
+    pFD = FindFont(pszFontFamily, dwFontStyles, false, wCodePage,
+                   FGAS_FONTUSB::kNoBitField, 0);
+  }
   if (!pFD)
     return nullptr;
 
   RetainPtr<CFGAS_GEFont> pFont =
-      CFGAS_GEFont::LoadFont(pFD->wsFontFace, dwFontStyles, wCodePage, this);
+      CFGAS_GEFont::LoadFont(pFD->wsFontFace, dwFontStyles, wCodePage);
   if (!pFont)
     return nullptr;
 
   pFont->SetLogicalFontStyle(dwFontStyles);
   pFontArray->push_back(pFont);
   return pFont;
-#else   // defined(OS_WIN)
+#else   // BUILDFLAG(IS_WIN)
   return GetFontByCodePage(wCodePage, dwFontStyles, pszFontFamily);
-#endif  // defined(OS_WIN)
-}
-
-void CFGAS_FontMgr::RemoveFont(const RetainPtr<CFGAS_GEFont>& pEFont) {
-  if (!pEFont)
-    return;
-
-#if !defined(OS_WIN)
-  m_IFXFont2FileRead.erase(pEFont);
-#endif
-
-  auto iter = m_Hash2Fonts.begin();
-  while (iter != m_Hash2Fonts.end()) {
-    auto old_iter = iter++;
-    bool all_empty = true;
-    for (size_t i = 0; i < old_iter->second.size(); i++) {
-      if (old_iter->second[i] == pEFont)
-        old_iter->second[i].Reset();
-      else if (old_iter->second[i])
-        all_empty = false;
-    }
-    if (all_empty)
-      m_Hash2Fonts.erase(old_iter);
-  }
+#endif  // BUILDFLAG(IS_WIN)
 }
diff --git a/xfa/fgas/font/cfgas_fontmgr.h b/xfa/fgas/font/cfgas_fontmgr.h
index bc7768e..02cb47a 100644
--- a/xfa/fgas/font/cfgas_fontmgr.h
+++ b/xfa/fgas/font/cfgas_fontmgr.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,28 +14,16 @@
 #include <vector>
 
 #include "build/build_config.h"
-#include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/observed_ptr.h"
+#include "core/fxcrt/fx_codepage_forward.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/widestring.h"
 #include "core/fxge/cfx_face.h"
-#include "core/fxge/fx_freetype.h"
-#include "xfa/fgas/font/cfgas_pdffontmgr.h"
+#include "core/fxge/freetype/fx_freetype.h"
 
 class CFGAS_GEFont;
-class CFX_FontMapper;
-class CFX_FontSourceEnum_File;
 class IFX_SeekableReadStream;
 
-#if defined(OS_WIN)
-struct FX_FONTMATCHPARAMS {
-  const wchar_t* pwsFamily;
-  uint32_t dwFontStyles;
-  uint32_t dwUSB;
-  bool matchParagraphStyle;
-  wchar_t wUnicode;
-  uint16_t wCodePage;
-};
-
+#if BUILDFLAG(IS_WIN)
 struct FX_FONTSIGNATURE {
   uint32_t fsUsb[4];
   uint32_t fsCsb[2];
@@ -51,7 +39,7 @@
 struct FX_FONTDESCRIPTOR {
   wchar_t wsFontFace[32];
   uint32_t dwFontStyles;
-  uint8_t uCharSet;
+  FX_Charset uCharSet;
   FX_FONTSIGNATURE FontSignature;
 };
 
@@ -63,45 +51,46 @@
          wcscmp(left.wsFontFace, right.wsFontFace) == 0;
 }
 
-#else  // defined(OS_WIN)
+#else  // BUILDFLAG(IS_WIN)
 
-class CFX_FontDescriptor {
+class CFGAS_FontDescriptor {
  public:
-  CFX_FontDescriptor();
-  ~CFX_FontDescriptor();
+  CFGAS_FontDescriptor();
+  ~CFGAS_FontDescriptor();
 
-  int32_t m_nFaceIndex;
-  uint32_t m_dwFontStyles;
+  int32_t m_nFaceIndex = 0;
+  uint32_t m_dwFontStyles = 0;
   WideString m_wsFaceName;
   std::vector<WideString> m_wsFamilyNames;
-  uint32_t m_dwUsb[4];
-  uint32_t m_dwCsb[2];
+  uint32_t m_dwUsb[4] = {};
+  uint32_t m_dwCsb[2] = {};
 };
 
-class CFX_FontDescriptorInfo {
+struct CFGAS_FontDescriptorInfo {
  public:
-  CFX_FontDescriptor* pFont;
+  UNOWNED_PTR_EXCLUSION CFGAS_FontDescriptor* pFont;  // POD struct.
   int32_t nPenalty;
 
-  bool operator>(const CFX_FontDescriptorInfo& other) const {
+  bool operator>(const CFGAS_FontDescriptorInfo& other) const {
     return nPenalty > other.nPenalty;
   }
-  bool operator<(const CFX_FontDescriptorInfo& other) const {
+  bool operator<(const CFGAS_FontDescriptorInfo& other) const {
     return nPenalty < other.nPenalty;
   }
-  bool operator==(const CFX_FontDescriptorInfo& other) const {
+  bool operator==(const CFGAS_FontDescriptorInfo& other) const {
     return nPenalty == other.nPenalty;
   }
 };
 
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
-class CFGAS_FontMgr final : public Observable {
+class CFGAS_FontMgr {
  public:
   CFGAS_FontMgr();
   ~CFGAS_FontMgr();
 
-  RetainPtr<CFGAS_GEFont> GetFontByCodePage(uint16_t wCodePage,
+  bool EnumFonts();
+  RetainPtr<CFGAS_GEFont> GetFontByCodePage(FX_CodePage wCodePage,
                                             uint32_t dwFontStyles,
                                             const wchar_t* pszFontFamily);
   RetainPtr<CFGAS_GEFont> GetFontByUnicode(wchar_t wUnicode,
@@ -109,55 +98,47 @@
                                            const wchar_t* pszFontFamily);
   RetainPtr<CFGAS_GEFont> LoadFont(const wchar_t* pszFontFamily,
                                    uint32_t dwFontStyles,
-                                   uint16_t wCodePage);
-  void RemoveFont(const RetainPtr<CFGAS_GEFont>& pFont);
-
-  bool EnumFonts();
+                                   FX_CodePage wCodePage);
 
  private:
   RetainPtr<CFGAS_GEFont> GetFontByUnicodeImpl(wchar_t wUnicode,
                                                uint32_t dwFontStyles,
                                                const wchar_t* pszFontFamily,
                                                uint32_t dwHash,
-                                               uint16_t wCodePage,
+                                               FX_CodePage wCodePage,
                                                uint16_t wBitField);
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   const FX_FONTDESCRIPTOR* FindFont(const wchar_t* pszFontFamily,
                                     uint32_t dwFontStyles,
                                     bool matchParagraphStyle,
-                                    uint16_t wCodePage,
+                                    FX_CodePage wCodePage,
                                     uint32_t dwUSB,
                                     wchar_t wUnicode);
 
-#else   // defined(OS_WIN)
+#else   // BUILDFLAG(IS_WIN)
   bool EnumFontsFromFontMapper();
-  bool EnumFontsFromFiles();
-  void RegisterFace(RetainPtr<CFX_Face> pFace, const WideString* pFaceName);
+  void RegisterFace(RetainPtr<CFX_Face> pFace, const WideString& wsFaceName);
   void RegisterFaces(const RetainPtr<IFX_SeekableReadStream>& pFontStream,
-                     const WideString* pFaceName);
-  void MatchFonts(std::vector<CFX_FontDescriptorInfo>* MatchedFonts,
-                  uint16_t wCodePage,
-                  uint32_t dwFontStyles,
-                  const WideString& FontName,
-                  wchar_t wcUnicode);
+                     const WideString& wsFaceName);
+  std::vector<CFGAS_FontDescriptorInfo> MatchFonts(FX_CodePage wCodePage,
+                                                   uint32_t dwFontStyles,
+                                                   const WideString& FontName,
+                                                   wchar_t wcUnicode);
   RetainPtr<CFGAS_GEFont> LoadFontInternal(const WideString& wsFaceName,
                                            int32_t iFaceIndex);
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
   std::map<uint32_t, std::vector<RetainPtr<CFGAS_GEFont>>> m_Hash2Fonts;
   std::set<wchar_t> m_FailedUnicodesSet;
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   std::deque<FX_FONTDESCRIPTOR> m_FontFaces;
 #else
-  std::unique_ptr<CFX_FontSourceEnum_File> m_pFontSource;
-  std::vector<std::unique_ptr<CFX_FontDescriptor>> m_InstalledFonts;
-  std::map<uint32_t, std::unique_ptr<std::vector<CFX_FontDescriptorInfo>>>
+  std::vector<std::unique_ptr<CFGAS_FontDescriptor>> m_InstalledFonts;
+  std::map<uint32_t, std::vector<CFGAS_FontDescriptorInfo>>
       m_Hash2CandidateList;
-  std::map<RetainPtr<CFGAS_GEFont>, RetainPtr<IFX_SeekableReadStream>>
-      m_IFXFont2FileRead;
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 };
 
 #endif  // XFA_FGAS_FONT_CFGAS_FONTMGR_H_
diff --git a/xfa/fgas/font/cfgas_gefont.cpp b/xfa/fgas/font/cfgas_gefont.cpp
index 5a7c625..369ea78 100644
--- a/xfa/fgas/font/cfgas_gefont.cpp
+++ b/xfa/fgas/font/cfgas_gefont.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,37 +11,34 @@
 
 #include "build/build_config.h"
 #include "core/fpdfapi/font/cpdf_font.h"
-#include "core/fxcrt/fx_codepage.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_substfont.h"
 #include "core/fxge/cfx_unicodeencodingex.h"
 #include "core/fxge/fx_font.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "xfa/fgas/font/cfgas_fontmgr.h"
+#include "xfa/fgas/font/cfgas_gemodule.h"
 #include "xfa/fgas/font/fgas_fontutils.h"
 
 // static
 RetainPtr<CFGAS_GEFont> CFGAS_GEFont::LoadFont(const wchar_t* pszFontFamily,
                                                uint32_t dwFontStyles,
-                                               uint16_t wCodePage,
-                                               CFGAS_FontMgr* pFontMgr) {
-#if defined(OS_WIN)
-  auto pFont = pdfium::MakeRetain<CFGAS_GEFont>(pFontMgr);
+                                               FX_CodePage wCodePage) {
+#if BUILDFLAG(IS_WIN)
+  auto pFont = pdfium::MakeRetain<CFGAS_GEFont>();
   if (!pFont->LoadFontInternal(pszFontFamily, dwFontStyles, wCodePage))
     return nullptr;
   return pFont;
 #else
-  if (!pFontMgr)
-    return nullptr;
-  return pFontMgr->GetFontByCodePage(wCodePage, dwFontStyles, pszFontFamily);
+  return CFGAS_GEModule::Get()->GetFontMgr()->GetFontByCodePage(
+      wCodePage, dwFontStyles, pszFontFamily);
 #endif
 }
 
 // static
-RetainPtr<CFGAS_GEFont> CFGAS_GEFont::LoadFont(
-    const RetainPtr<CPDF_Font>& pPDFFont,
-    CFGAS_FontMgr* pFontMgr) {
-  auto pFont = pdfium::MakeRetain<CFGAS_GEFont>(pFontMgr);
-  if (!pFont->LoadFontInternal(pPDFFont))
+RetainPtr<CFGAS_GEFont> CFGAS_GEFont::LoadFont(RetainPtr<CPDF_Font> pPDFFont) {
+  auto pFont = pdfium::MakeRetain<CFGAS_GEFont>();
+  if (!pFont->LoadFontInternal(std::move(pPDFFont)))
     return nullptr;
 
   return pFont;
@@ -49,32 +46,31 @@
 
 // static
 RetainPtr<CFGAS_GEFont> CFGAS_GEFont::LoadFont(
-    std::unique_ptr<CFX_Font> pInternalFont,
-    CFGAS_FontMgr* pFontMgr) {
-  auto pFont = pdfium::MakeRetain<CFGAS_GEFont>(pFontMgr);
+    std::unique_ptr<CFX_Font> pInternalFont) {
+  auto pFont = pdfium::MakeRetain<CFGAS_GEFont>();
   if (!pFont->LoadFontInternal(std::move(pInternalFont)))
     return nullptr;
+
   return pFont;
 }
 
 // static
 RetainPtr<CFGAS_GEFont> CFGAS_GEFont::LoadStockFont(
     CPDF_Document* pDoc,
-    CFGAS_FontMgr* pMgr,
     const ByteString& font_family) {
   RetainPtr<CPDF_Font> stock_font =
       CPDF_Font::GetStockFont(pDoc, font_family.AsStringView());
-  return stock_font ? CFGAS_GEFont::LoadFont(stock_font, pMgr) : nullptr;
+  return stock_font ? CFGAS_GEFont::LoadFont(std::move(stock_font)) : nullptr;
 }
 
-CFGAS_GEFont::CFGAS_GEFont(CFGAS_FontMgr* pFontMgr) : m_pFontMgr(pFontMgr) {}
+CFGAS_GEFont::CFGAS_GEFont() = default;
 
 CFGAS_GEFont::~CFGAS_GEFont() = default;
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 bool CFGAS_GEFont::LoadFontInternal(const wchar_t* pszFontFamily,
                                     uint32_t dwFontStyles,
-                                    uint16_t wCodePage) {
+                                    FX_CodePage wCodePage) {
   if (m_pFont)
     return false;
   ByteString csFontFamily;
@@ -83,7 +79,7 @@
 
   int32_t iWeight =
       FontStyleIsForceBold(dwFontStyles) ? FXFONT_FW_BOLD : FXFONT_FW_NORMAL;
-  m_pFont = pdfium::MakeUnique<CFX_Font>();
+  m_pFont = std::make_unique<CFX_Font>();
   if (FontStyleIsItalic(dwFontStyles) && FontStyleIsForceBold(dwFontStyles))
     csFontFamily += ",BoldItalic";
   else if (FontStyleIsForceBold(dwFontStyles))
@@ -95,19 +91,17 @@
                      false);
   return m_pFont->GetFaceRec() && InitFont();
 }
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
-bool CFGAS_GEFont::LoadFontInternal(const RetainPtr<CPDF_Font>& pPDFFont) {
-  CFX_Font* pExternalFont = pPDFFont->GetFont();
-  if (m_pFont || !pExternalFont)
+bool CFGAS_GEFont::LoadFontInternal(RetainPtr<CPDF_Font> pPDFFont) {
+  DCHECK(pPDFFont);
+
+  if (m_pFont)
     return false;
 
-  m_pFont = pExternalFont;
-  if (!InitFont())
-    return false;
-
-  m_pPDFFont = pPDFFont;  // Keep pPDFFont alive for the duration.
-  return true;
+  m_pPDFFont = std::move(pPDFFont);  // Keep `pPDFFont` alive for the duration.
+  m_pFont = m_pPDFFont->GetFont();
+  return InitFont();
 }
 
 bool CFGAS_GEFont::LoadFontInternal(std::unique_ptr<CFX_Font> pInternalFont) {
@@ -138,7 +132,7 @@
 }
 
 uint32_t CFGAS_GEFont::GetFontStyles() const {
-  ASSERT(m_pFont);
+  DCHECK(m_pFont);
   if (m_dwLogFontStyle.has_value())
     return m_dwLogFontStyle.value();
 
@@ -156,61 +150,50 @@
   return dwStyles;
 }
 
-bool CFGAS_GEFont::GetCharWidth(wchar_t wUnicode, int32_t* pWidth) {
+absl::optional<uint16_t> CFGAS_GEFont::GetCharWidth(wchar_t wUnicode) {
   auto it = m_CharWidthMap.find(wUnicode);
-  *pWidth = it != m_CharWidthMap.end() ? it->second : 0;
-  if (*pWidth == 65535)
-    return false;
-
-  if (*pWidth > 0)
-    return true;
+  if (it != m_CharWidthMap.end())
+    return it->second;
 
   RetainPtr<CFGAS_GEFont> pFont;
-  int32_t iGlyph;
-  std::tie(iGlyph, pFont) = GetGlyphIndexAndFont(wUnicode, true);
-  if (iGlyph != 0xFFFF && pFont) {
-    if (pFont == this) {
-      *pWidth = m_pFont->GetGlyphWidth(iGlyph);
-      if (*pWidth < 0)
-        *pWidth = -1;
-    } else if (pFont->GetCharWidth(wUnicode, pWidth)) {
-      return true;
-    }
-  } else {
-    *pWidth = -1;
+  int32_t glyph;
+  std::tie(glyph, pFont) = GetGlyphIndexAndFont(wUnicode, true);
+  if (!pFont || glyph == 0xffff) {
+    m_CharWidthMap[wUnicode] = absl::nullopt;
+    return absl::nullopt;
   }
+  if (pFont != this)
+    return pFont->GetCharWidth(wUnicode);
 
-  m_CharWidthMap[wUnicode] = *pWidth;
-  return *pWidth > 0;
+  int32_t width_from_cfx_font = m_pFont->GetGlyphWidth(glyph);
+  if (width_from_cfx_font < 0) {
+    m_CharWidthMap[wUnicode] = absl::nullopt;
+    return absl::nullopt;
+  }
+  uint16_t width = static_cast<uint16_t>(width_from_cfx_font);
+  m_CharWidthMap[wUnicode] = width;
+  return width;
 }
 
-bool CFGAS_GEFont::GetCharBBox(wchar_t wUnicode, FX_RECT* bbox) {
+absl::optional<FX_RECT> CFGAS_GEFont::GetCharBBox(wchar_t wUnicode) {
   auto it = m_BBoxMap.find(wUnicode);
-  if (it != m_BBoxMap.end()) {
-    *bbox = it->second;
-    return true;
-  }
+  if (it != m_BBoxMap.end())
+    return it->second;
 
   RetainPtr<CFGAS_GEFont> pFont;
   int32_t iGlyph;
   std::tie(iGlyph, pFont) = GetGlyphIndexAndFont(wUnicode, true);
   if (!pFont || iGlyph == 0xFFFF)
-    return false;
+    return absl::nullopt;
 
   if (pFont.Get() != this)
-    return pFont->GetCharBBox(wUnicode, bbox);
+    return pFont->GetCharBBox(wUnicode);
 
-  FX_RECT rtBBox;
-  if (!m_pFont->GetGlyphBBox(iGlyph, &rtBBox))
-    return false;
+  absl::optional<FX_RECT> rtBBox = m_pFont->GetGlyphBBox(iGlyph);
+  if (rtBBox.has_value())
+    m_BBoxMap[wUnicode] = rtBBox.value();
 
-  m_BBoxMap[wUnicode] = rtBBox;
-  *bbox = rtBBox;
-  return true;
-}
-
-bool CFGAS_GEFont::GetBBox(FX_RECT* bbox) {
-  return m_pFont->GetBBox(bbox);
+  return rtBBox;
 }
 
 int32_t CFGAS_GEFont::GetGlyphIndex(wchar_t wUnicode) {
@@ -247,15 +230,16 @@
       }
     }
   }
-  if (!m_pFontMgr || !bRecursive)
+  if (!bRecursive)
     return {0xFFFF, nullptr};
 
+  CFGAS_FontMgr* pFontMgr = CFGAS_GEModule::Get()->GetFontMgr();
   WideString wsFamily = GetFamilyName();
   RetainPtr<CFGAS_GEFont> pFont =
-      m_pFontMgr->GetFontByUnicode(wUnicode, GetFontStyles(), wsFamily.c_str());
-#if !defined(OS_WIN)
+      pFontMgr->GetFontByUnicode(wUnicode, GetFontStyles(), wsFamily.c_str());
+#if !BUILDFLAG(IS_WIN)
   if (!pFont)
-    pFont = m_pFontMgr->GetFontByUnicode(wUnicode, GetFontStyles(), nullptr);
+    pFont = pFontMgr->GetFontByUnicode(wUnicode, GetFontStyles(), nullptr);
 #endif
   if (!pFont || pFont == this)  // Avoids direct cycles below.
     return {0xFFFF, nullptr};
diff --git a/xfa/fgas/font/cfgas_gefont.h b/xfa/fgas/font/cfgas_gefont.h
index 7e021e1..fe910df 100644
--- a/xfa/fgas/font/cfgas_gefont.h
+++ b/xfa/fgas/font/cfgas_gefont.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,18 +7,20 @@
 #ifndef XFA_FGAS_FONT_CFGAS_GEFONT_H_
 #define XFA_FGAS_FONT_CFGAS_GEFONT_H_
 
+#include <stdint.h>
+
 #include <map>
 #include <memory>
 #include <utility>
 #include <vector>
 
 #include "build/build_config.h"
+#include "core/fxcrt/fx_codepage_forward.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/maybe_owned.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/optional.h"
-#include "xfa/fgas/font/cfgas_fontmgr.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CFX_Font;
 class CFX_UnicodeEncodingEx;
@@ -27,31 +29,22 @@
 
 class CFGAS_GEFont final : public Retainable {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   static RetainPtr<CFGAS_GEFont> LoadFont(const wchar_t* pszFontFamily,
                                           uint32_t dwFontStyles,
-                                          uint16_t wCodePage,
-                                          CFGAS_FontMgr* pFontMgr);
-  static RetainPtr<CFGAS_GEFont> LoadFont(const RetainPtr<CPDF_Font>& pPDFFont,
-                                          CFGAS_FontMgr* pFontMgr);
-  static RetainPtr<CFGAS_GEFont> LoadFont(
-      std::unique_ptr<CFX_Font> pInternalFont,
-      CFGAS_FontMgr* pFontMgr);
-
+                                          FX_CodePage wCodePage);
+  static RetainPtr<CFGAS_GEFont> LoadFont(RetainPtr<CPDF_Font> pFont);
+  static RetainPtr<CFGAS_GEFont> LoadFont(std::unique_ptr<CFX_Font> pFont);
   static RetainPtr<CFGAS_GEFont> LoadStockFont(CPDF_Document* pDoc,
-                                               CFGAS_FontMgr* pMgr,
                                                const ByteString& font_family);
 
   uint32_t GetFontStyles() const;
-  bool GetCharWidth(wchar_t wUnicode, int32_t* pWidth);
+  absl::optional<uint16_t> GetCharWidth(wchar_t wUnicode);
   int32_t GetGlyphIndex(wchar_t wUnicode);
   int32_t GetAscent() const;
   int32_t GetDescent() const;
-
-  bool GetCharBBox(wchar_t wUnicode, FX_RECT* bbox);
-  bool GetBBox(FX_RECT* bbox);
+  absl::optional<FX_RECT> GetCharBBox(wchar_t wUnicode);
 
   RetainPtr<CFGAS_GEFont> GetSubstFont(int32_t iGlyphIndex);
   CFX_Font* GetDevFont() const { return m_pFont.Get(); }
@@ -61,29 +54,27 @@
   }
 
  private:
-  explicit CFGAS_GEFont(CFGAS_FontMgr* pFontMgr);
+  CFGAS_GEFont();
   ~CFGAS_GEFont() override;
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   bool LoadFontInternal(const wchar_t* pszFontFamily,
                         uint32_t dwFontStyles,
-                        uint16_t wCodePage);
-  bool LoadFontInternal(const uint8_t* pBuffer, int32_t length);
+                        FX_CodePage wCodePage);
 #endif
   bool LoadFontInternal(std::unique_ptr<CFX_Font> pInternalFont);
-  bool LoadFontInternal(const RetainPtr<CPDF_Font>& pPDFFont);
+  bool LoadFontInternal(RetainPtr<CPDF_Font> pPDFFont);
   bool InitFont();
   std::pair<int32_t, RetainPtr<CFGAS_GEFont>> GetGlyphIndexAndFont(
       wchar_t wUnicode,
       bool bRecursive);
   WideString GetFamilyName() const;
 
-  Optional<uint32_t> m_dwLogFontStyle;
+  absl::optional<uint32_t> m_dwLogFontStyle;
   RetainPtr<CPDF_Font> m_pPDFFont;  // Must come before |m_pFont|.
   MaybeOwned<CFX_Font> m_pFont;     // Must come before |m_pFontEncoding|.
-  ObservedPtr<CFGAS_FontMgr> const m_pFontMgr;
   std::unique_ptr<CFX_UnicodeEncodingEx> m_pFontEncoding;
-  std::map<wchar_t, int32_t> m_CharWidthMap;
+  std::map<wchar_t, absl::optional<uint16_t>> m_CharWidthMap;
   std::map<wchar_t, FX_RECT> m_BBoxMap;
   std::vector<RetainPtr<CFGAS_GEFont>> m_SubstFonts;
   std::map<wchar_t, RetainPtr<CFGAS_GEFont>> m_FontMapper;
diff --git a/xfa/fgas/font/cfgas_gemodule.cpp b/xfa/fgas/font/cfgas_gemodule.cpp
new file mode 100644
index 0000000..ef3b04a
--- /dev/null
+++ b/xfa/fgas/font/cfgas_gemodule.cpp
@@ -0,0 +1,39 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fgas/font/cfgas_gemodule.h"
+
+#include "third_party/base/check.h"
+#include "xfa/fgas/font/cfgas_fontmgr.h"
+
+namespace {
+
+CFGAS_GEModule* g_module = nullptr;
+
+}  // namespace
+
+// static
+void CFGAS_GEModule::Create() {
+  DCHECK(!g_module);
+  g_module = new CFGAS_GEModule();
+  g_module->GetFontMgr()->EnumFonts();
+}
+
+// static
+void CFGAS_GEModule::Destroy() {
+  DCHECK(g_module);
+  delete g_module;
+  g_module = nullptr;
+}
+
+// static
+CFGAS_GEModule* CFGAS_GEModule::Get() {
+  DCHECK(g_module);
+  return g_module;
+}
+
+CFGAS_GEModule::CFGAS_GEModule()
+    : font_mgr_(std::make_unique<CFGAS_FontMgr>()) {}
+
+CFGAS_GEModule::~CFGAS_GEModule() = default;
diff --git a/xfa/fgas/font/cfgas_gemodule.h b/xfa/fgas/font/cfgas_gemodule.h
new file mode 100644
index 0000000..2e89017
--- /dev/null
+++ b/xfa/fgas/font/cfgas_gemodule.h
@@ -0,0 +1,27 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef XFA_FGAS_FONT_CFGAS_GEMODULE_H_
+#define XFA_FGAS_FONT_CFGAS_GEMODULE_H_
+
+#include <memory>
+
+class CFGAS_FontMgr;
+
+class CFGAS_GEModule {
+ public:
+  static void Create();
+  static void Destroy();
+  static CFGAS_GEModule* Get();
+
+  CFGAS_FontMgr* GetFontMgr() { return font_mgr_.get(); }
+
+ private:
+  CFGAS_GEModule();
+  ~CFGAS_GEModule();
+
+  std::unique_ptr<CFGAS_FontMgr> font_mgr_;
+};
+
+#endif  // XFA_FGAS_FONT_CFGAS_GEMODULE_H_
diff --git a/xfa/fgas/font/cfgas_pdffontmgr.cpp b/xfa/fgas/font/cfgas_pdffontmgr.cpp
index b342237..c3efa1d 100644
--- a/xfa/fgas/font/cfgas_pdffontmgr.cpp
+++ b/xfa/fgas/font/cfgas_pdffontmgr.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,23 @@
 #include "xfa/fgas/font/cfgas_pdffontmgr.h"
 
 #include <algorithm>
+#include <iterator>
+#include <utility>
 
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxge/fx_font.h"
+#include "third_party/base/check.h"
 #include "xfa/fgas/font/cfgas_fontmgr.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
 
 namespace {
 
 // The 5 names per entry are: PsName, Normal, Bold, Italic, BoldItalic.
-const char* const g_XFAPDFFontName[][5] = {
+const char* const kXFAPDFFontName[][5] = {
     {"Adobe PI Std", "AdobePIStd", "AdobePIStd", "AdobePIStd", "AdobePIStd"},
     {"Myriad Pro Light", "MyriadPro-Light", "MyriadPro-Semibold",
      "MyriadPro-LightIt", "MyriadPro-SemiboldIt"},
@@ -27,19 +31,17 @@
 
 }  // namespace
 
-CFGAS_PDFFontMgr::CFGAS_PDFFontMgr(CPDF_Document* pDoc, CFGAS_FontMgr* pFontMgr)
-    : m_pDoc(pDoc), m_pFontMgr(pFontMgr) {
-  ASSERT(pDoc);
-  ASSERT(pFontMgr);
+CFGAS_PDFFontMgr::CFGAS_PDFFontMgr(const CPDF_Document* pDoc) : m_pDoc(pDoc) {
+  DCHECK(pDoc);
 }
 
-CFGAS_PDFFontMgr::~CFGAS_PDFFontMgr() {}
+CFGAS_PDFFontMgr::~CFGAS_PDFFontMgr() = default;
 
 RetainPtr<CFGAS_GEFont> CFGAS_PDFFontMgr::FindFont(const ByteString& strPsName,
                                                    bool bBold,
                                                    bool bItalic,
                                                    bool bStrictMatch) {
-  CPDF_Dictionary* pFontSetDict =
+  RetainPtr<const CPDF_Dictionary> pFontSetDict =
       m_pDoc->GetRoot()->GetDictFor("AcroForm")->GetDictFor("DR");
   if (!pFontSetDict)
     return nullptr;
@@ -51,34 +53,35 @@
   ByteString name = strPsName;
   name.Remove(' ');
 
-  auto* pData = CPDF_DocPageData::FromDocument(m_pDoc.Get());
+  auto* pData = CPDF_DocPageData::FromDocument(m_pDoc);
   CPDF_DictionaryLocker locker(pFontSetDict);
   for (const auto& it : locker) {
     const ByteString& key = it.first;
-    CPDF_Object* pObj = it.second.Get();
+    const RetainPtr<CPDF_Object>& pObj = it.second;
     if (!PsNameMatchDRFontName(name.AsStringView(), bBold, bItalic, key,
                                bStrictMatch)) {
       continue;
     }
-    CPDF_Dictionary* pFontDict = ToDictionary(pObj->GetDirect());
-    if (!pFontDict || pFontDict->GetStringFor("Type") != "Font")
+    RetainPtr<CPDF_Dictionary> pFontDict =
+        ToDictionary(pObj->GetMutableDirect());
+    if (!ValidateDictType(pFontDict.Get(), "Font"))
       return nullptr;
 
     RetainPtr<CPDF_Font> pPDFFont = pData->GetFont(pFontDict);
     if (!pPDFFont || !pPDFFont->IsEmbedded())
       return nullptr;
 
-    return CFGAS_GEFont::LoadFont(pPDFFont, m_pFontMgr.Get());
+    return CFGAS_GEFont::LoadFont(std::move(pPDFFont));
   }
   return nullptr;
 }
 
-RetainPtr<CFGAS_GEFont> CFGAS_PDFFontMgr::GetFont(WideStringView wsFontFamily,
-                                                  uint32_t dwFontStyles,
-                                                  bool bStrictMatch) {
-  uint32_t dwHashCode = FX_HashCode_GetW(wsFontFamily, false);
-  ByteString strKey = ByteString::Format("%u%u", dwHashCode, dwFontStyles);
-  auto it = m_FontMap.find(strKey);
+RetainPtr<CFGAS_GEFont> CFGAS_PDFFontMgr::GetFont(
+    const WideString& wsFontFamily,
+    uint32_t dwFontStyles,
+    bool bStrictMatch) {
+  auto key = std::make_pair(wsFontFamily, dwFontStyles);
+  auto it = m_FontMap.find(key);
   if (it != m_FontMap.end())
     return it->second;
 
@@ -88,23 +91,24 @@
   ByteString strFontName = PsNameToFontName(bsPsName, bBold, bItalic);
   RetainPtr<CFGAS_GEFont> pFont =
       FindFont(strFontName, bBold, bItalic, bStrictMatch);
-  if (pFont)
-    m_FontMap[strKey] = pFont;
+  if (!pFont)
+    return nullptr;
 
+  m_FontMap[key] = pFont;
   return pFont;
 }
 
 ByteString CFGAS_PDFFontMgr::PsNameToFontName(const ByteString& strPsName,
                                               bool bBold,
                                               bool bItalic) {
-  for (size_t i = 0; i < FX_ArraySize(g_XFAPDFFontName); ++i) {
-    if (strPsName == g_XFAPDFFontName[i][0]) {
+  for (size_t i = 0; i < std::size(kXFAPDFFontName); ++i) {
+    if (strPsName == kXFAPDFFontName[i][0]) {
       size_t index = 1;
       if (bBold)
         ++index;
       if (bItalic)
         index += 2;
-      return g_XFAPDFFontName[i][index];
+      return kXFAPDFFontName[i][index];
     }
   }
   return strPsName;
diff --git a/xfa/fgas/font/cfgas_pdffontmgr.h b/xfa/fgas/font/cfgas_pdffontmgr.h
index f9c56fc..a014744 100644
--- a/xfa/fgas/font/cfgas_pdffontmgr.h
+++ b/xfa/fgas/font/cfgas_pdffontmgr.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,21 +8,22 @@
 #define XFA_FGAS_FONT_CFGAS_PDFFONTMGR_H_
 
 #include <map>
+#include <utility>
 
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "xfa/fgas/font/cfgas_fontmgr.h"
 
-class CFGAS_FontMgr;
 class CFGAS_GEFont;
 class CPDF_Document;
 
-class CFGAS_PDFFontMgr final : public Observable {
+class CFGAS_PDFFontMgr final {
  public:
-  explicit CFGAS_PDFFontMgr(CPDF_Document* pDoc, CFGAS_FontMgr* pFontMgr);
+  explicit CFGAS_PDFFontMgr(const CPDF_Document* pDoc);
   ~CFGAS_PDFFontMgr();
 
-  RetainPtr<CFGAS_GEFont> GetFont(WideStringView wsFontFamily,
+  RetainPtr<CFGAS_GEFont> GetFont(const WideString& wsFontFamily,
                                   uint32_t dwFontStyles,
                                   bool bStrictMatch);
 
@@ -40,9 +41,8 @@
                              const ByteString& bsDRFontName,
                              bool bStrictMatch);
 
-  UnownedPtr<CPDF_Document> const m_pDoc;
-  UnownedPtr<CFGAS_FontMgr> const m_pFontMgr;
-  std::map<ByteString, RetainPtr<CFGAS_GEFont>> m_FontMap;
+  UnownedPtr<const CPDF_Document> const m_pDoc;
+  std::map<std::pair<WideString, uint32_t>, RetainPtr<CFGAS_GEFont>> m_FontMap;
 };
 
 #endif  // XFA_FGAS_FONT_CFGAS_PDFFONTMGR_H_
diff --git a/xfa/fgas/font/cfx_fontsourceenum_file.cpp b/xfa/fgas/font/cfx_fontsourceenum_file.cpp
deleted file mode 100644
index 83283ec..0000000
--- a/xfa/fgas/font/cfx_fontsourceenum_file.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fgas/font/cfx_fontsourceenum_file.h"
-
-#include <iterator>
-
-#include "build/build_config.h"
-
-namespace {
-
-constexpr char kFolderSeparator = '/';
-
-constexpr const char* kFontFolders[] = {
-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
-    "/usr/share/fonts",
-    "/usr/share/X11/fonts/Type1",
-    "/usr/share/X11/fonts/TTF",
-    "/usr/local/share/fonts",
-#elif defined(OS_MACOSX)
-    "~/Library/Fonts",
-    "/Library/Fonts",
-    "/System/Library/Fonts",
-#elif defined(OS_ANDROID)
-    "/system/fonts",
-#endif
-};
-
-}  // namespace
-
-CFX_FontSourceEnum_File::CFX_FontSourceEnum_File()
-    : m_FolderPaths(std::begin(kFontFolders), std::end(kFontFolders)) {}
-
-CFX_FontSourceEnum_File::~CFX_FontSourceEnum_File() = default;
-
-ByteString CFX_FontSourceEnum_File::GetNextFile() {
-  FX_FolderHandle* pCurHandle =
-      !m_FolderQueue.empty() ? m_FolderQueue.back().pFolderHandle : nullptr;
-  if (!pCurHandle) {
-    if (m_FolderPaths.empty())
-      return ByteString();
-    pCurHandle = FX_OpenFolder(m_FolderPaths.back().c_str());
-    HandleParentPath hpp;
-    hpp.pFolderHandle = pCurHandle;
-    hpp.bsParentPath = m_FolderPaths.back();
-    m_FolderQueue.push_back(hpp);
-  }
-  ByteString bsName;
-  bool bFolder;
-  while (true) {
-    if (!FX_GetNextFile(pCurHandle, &bsName, &bFolder)) {
-      FX_CloseFolder(pCurHandle);
-      if (!m_FolderQueue.empty())
-        m_FolderQueue.pop_back();
-      if (m_FolderQueue.empty()) {
-        if (!m_FolderPaths.empty())
-          m_FolderPaths.pop_back();
-        return !m_FolderPaths.empty() ? GetNextFile() : ByteString();
-      }
-      pCurHandle = m_FolderQueue.back().pFolderHandle;
-      continue;
-    }
-    if (bsName == "." || bsName == "..")
-      continue;
-    if (bFolder) {
-      HandleParentPath hpp;
-      hpp.bsParentPath =
-          m_FolderQueue.back().bsParentPath + kFolderSeparator + bsName;
-      hpp.pFolderHandle = FX_OpenFolder(hpp.bsParentPath.c_str());
-      if (!hpp.pFolderHandle)
-        continue;
-      m_FolderQueue.push_back(hpp);
-      pCurHandle = hpp.pFolderHandle;
-      continue;
-    }
-    bsName = m_FolderQueue.back().bsParentPath + kFolderSeparator + bsName;
-    break;
-  }
-  return bsName;
-}
-
-void CFX_FontSourceEnum_File::GetNext() {
-  m_wsNext = WideString::FromUTF8(GetNextFile().AsStringView());
-}
-
-bool CFX_FontSourceEnum_File::HasNext() const {
-  return !m_wsNext.IsEmpty();
-}
-
-RetainPtr<IFX_SeekableStream> CFX_FontSourceEnum_File::GetStream() const {
-  ASSERT(HasNext());
-  return IFX_SeekableStream::CreateFromFilename(m_wsNext.c_str(),
-                                                FX_FILEMODE_ReadOnly);
-}
diff --git a/xfa/fgas/font/cfx_fontsourceenum_file.h b/xfa/fgas/font/cfx_fontsourceenum_file.h
deleted file mode 100644
index 9e93a97..0000000
--- a/xfa/fgas/font/cfx_fontsourceenum_file.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FGAS_FONT_CFX_FONTSOURCEENUM_FILE_H_
-#define XFA_FGAS_FONT_CFX_FONTSOURCEENUM_FILE_H_
-
-#include <vector>
-
-#include "build/build_config.h"
-#include "core/fxcrt/fx_stream.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/retain_ptr.h"
-
-#if defined(OS_WIN)
-#error "Not used on Windows"
-#endif
-
-class CFX_FontSourceEnum_File {
- public:
-  CFX_FontSourceEnum_File();
-  ~CFX_FontSourceEnum_File();
-
-  void GetNext();
-  bool HasNext() const;
-  RetainPtr<IFX_SeekableStream> GetStream() const;
-
- private:
-  struct HandleParentPath {
-    HandleParentPath() = default;
-    HandleParentPath(const HandleParentPath& x) {
-      pFolderHandle = x.pFolderHandle;
-      bsParentPath = x.bsParentPath;
-    }
-    FX_FolderHandle* pFolderHandle;
-    ByteString bsParentPath;
-  };
-
-  ByteString GetNextFile();
-
-  WideString m_wsNext;
-  std::vector<HandleParentPath> m_FolderQueue;
-  std::vector<ByteString> m_FolderPaths;
-};
-
-#endif  // XFA_FGAS_FONT_CFX_FONTSOURCEENUM_FILE_H_
diff --git a/xfa/fgas/font/fgas_fontutils.cpp b/xfa/fgas/font/fgas_fontutils.cpp
index cc6c922..9cc421c 100644
--- a/xfa/fgas/font/fgas_fontutils.cpp
+++ b/xfa/fgas/font/fgas_fontutils.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include "xfa/fgas/font/fgas_fontutils.h"
 
+#include <iterator>
+
 #include "build/build_config.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_extension.h"
@@ -13,1838 +15,2414 @@
 
 namespace {
 
-const FGAS_FONTUSB g_FXGdiFontUSBTable[] = {
-    {0x0000, 0x007F, 0, FX_CODEPAGE_MSWin_WesternEuropean},
-    {0x0080, 0x00FF, 1, FX_CODEPAGE_MSWin_WesternEuropean},
-    {0x0100, 0x017F, 2, FX_CODEPAGE_MSWin_EasternEuropean},
-    {0x0180, 0x024F, 3, FX_CODEPAGE_MSWin_EasternEuropean},
-    {0x0250, 0x02AF, 4, 0xFFFF},
-    {0x02B0, 0x02FF, 5, 0xFFFF},
-    {0x0300, 0x036F, 6, 0xFFFF},
-    {0x0370, 0x03FF, 7, FX_CODEPAGE_MSWin_Greek},
-    {0x0400, 0x04FF, 9, FX_CODEPAGE_MSWin_Cyrillic},
-    {0x0500, 0x052F, 9, 0xFFFF},
-    {0x0530, 0x058F, 10, 0xFFFF},
-    {0x0590, 0x05FF, 11, FX_CODEPAGE_MSWin_Hebrew},
-    {0x0600, 0x06FF, 13, FX_CODEPAGE_MSWin_Arabic},
-    {0x0700, 0x074F, 71, 0xFFFF},
-    {0x0750, 0x077F, 13, 0xFFFF},
-    {0x0780, 0x07BF, 72, 0xFFFF},
-    {0x07C0, 0x07FF, 14, 0xFFFF},
-    {0x0800, 0x08FF, 999, 0xFFFF},
-    {0x0900, 0x097F, 15, 0xFFFF},
-    {0x0980, 0x09FF, 16, 0xFFFF},
-    {0x0A00, 0x0A7F, 17, 0xFFFF},
-    {0x0A80, 0x0AFF, 18, 0xFFFF},
-    {0x0B00, 0x0B7F, 19, 0xFFFF},
-    {0x0B80, 0x0BFF, 20, 0xFFFF},
-    {0x0C00, 0x0C7F, 21, 0xFFFF},
-    {0x0C80, 0x0CFF, 22, 0xFFFF},
-    {0x0D00, 0x0D7F, 23, 0xFFFF},
-    {0x0D80, 0x0DFF, 73, 0xFFFF},
-    {0x0E00, 0x0E7F, 24, FX_CODEPAGE_MSDOS_Thai},
-    {0x0E80, 0x0EFF, 25, 0xFFFF},
-    {0x0F00, 0x0FFF, 70, 0xFFFF},
-    {0x1000, 0x109F, 74, 0xFFFF},
-    {0x10A0, 0x10FF, 26, 0xFFFF},
-    {0x1100, 0x11FF, 28, 0xFFFF},
-    {0x1200, 0x137F, 75, 0xFFFF},
-    {0x1380, 0x139F, 75, 0xFFFF},
-    {0x13A0, 0x13FF, 76, 0xFFFF},
-    {0x1400, 0x167F, 77, 0xFFFF},
-    {0x1680, 0x169F, 78, 0xFFFF},
-    {0x16A0, 0x16FF, 79, 0xFFFF},
-    {0x1700, 0x171F, 84, 0xFFFF},
-    {0x1720, 0x173F, 84, 0xFFFF},
-    {0x1740, 0x175F, 84, 0xFFFF},
-    {0x1760, 0x177F, 84, 0xFFFF},
-    {0x1780, 0x17FF, 80, 0xFFFF},
-    {0x1800, 0x18AF, 81, 0xFFFF},
-    {0x18B0, 0x18FF, 999, 0xFFFF},
-    {0x1900, 0x194F, 93, 0xFFFF},
-    {0x1950, 0x197F, 94, 0xFFFF},
-    {0x1980, 0x19DF, 95, 0xFFFF},
-    {0x19E0, 0x19FF, 80, 0xFFFF},
-    {0x1A00, 0x1A1F, 96, 0xFFFF},
-    {0x1A20, 0x1AFF, 999, 0xFFFF},
-    {0x1B00, 0x1B7F, 27, 0xFFFF},
-    {0x1B80, 0x1BBF, 112, 0xFFFF},
-    {0x1BC0, 0x1BFF, 999, 0xFFFF},
-    {0x1C00, 0x1C4F, 113, 0xFFFF},
-    {0x1C50, 0x1C7F, 114, 0xFFFF},
-    {0x1C80, 0x1CFF, 999, 0xFFFF},
-    {0x1D00, 0x1D7F, 4, 0xFFFF},
-    {0x1D80, 0x1DBF, 4, 0xFFFF},
-    {0x1DC0, 0x1DFF, 6, 0xFFFF},
-    {0x1E00, 0x1EFF, 29, 0xFFFF},
-    {0x1F00, 0x1FFF, 30, 0xFFFF},
-    {0x2000, 0x206F, 31, 0xFFFF},
-    {0x2070, 0x209F, 32, 0xFFFF},
-    {0x20A0, 0x20CF, 33, 0xFFFF},
-    {0x20D0, 0x20FF, 34, 0xFFFF},
-    {0x2100, 0x214F, 35, 0xFFFF},
-    {0x2150, 0x215F, 36, 0xFFFF},
-    {0x2160, 0x216B, 36, FX_CODEPAGE_ChineseSimplified},
-    {0x216C, 0x216F, 36, 0xFFFF},
-    {0x2170, 0x2179, 36, FX_CODEPAGE_ChineseSimplified},
-    {0x217A, 0x218F, 36, 0xFFFF},
-    {0x2190, 0x2199, 37, FX_CODEPAGE_Hangul},
-    {0x219A, 0x21FF, 37, 0xFFFF},
-    {0x2200, 0x22FF, 38, 0xFFFF},
-    {0x2300, 0x23FF, 39, 0xFFFF},
-    {0x2400, 0x243F, 40, 0xFFFF},
-    {0x2440, 0x245F, 41, 0xFFFF},
-    {0x2460, 0x2473, 42, FX_CODEPAGE_ShiftJIS},
-    {0x2474, 0x249B, 42, FX_CODEPAGE_ChineseSimplified},
-    {0x249C, 0x24E9, 42, FX_CODEPAGE_Hangul},
-    {0x24EA, 0x24FF, 42, 0xFFFF},
-    {0x2500, 0x2573, 43, FX_CODEPAGE_ChineseSimplified},
-    {0x2574, 0x257F, 43, 0xFFFF},
-    {0x2580, 0x2580, 44, 0xFFFF},
-    {0x2581, 0x258F, 44, FX_CODEPAGE_ChineseSimplified},
-    {0x2590, 0x259F, 44, 0xFFFF},
-    {0x25A0, 0x25FF, 45, 0xFFFF},
-    {0x2600, 0x26FF, 46, 0xFFFF},
-    {0x2700, 0x27BF, 47, 0xFFFF},
-    {0x27C0, 0x27EF, 38, 0xFFFF},
-    {0x27F0, 0x27FF, 37, 0xFFFF},
-    {0x2800, 0x28FF, 82, 0xFFFF},
-    {0x2900, 0x297F, 37, 0xFFFF},
-    {0x2980, 0x29FF, 38, 0xFFFF},
-    {0x2A00, 0x2AFF, 38, 0xFFFF},
-    {0x2B00, 0x2BFF, 37, 0xFFFF},
-    {0x2C00, 0x2C5F, 97, 0xFFFF},
-    {0x2C60, 0x2C7F, 29, 0xFFFF},
-    {0x2C80, 0x2CFF, 8, 0xFFFF},
-    {0x2D00, 0x2D2F, 26, 0xFFFF},
-    {0x2D30, 0x2D7F, 98, 0xFFFF},
-    {0x2D80, 0x2DDF, 75, 0xFFFF},
-    {0x2DE0, 0x2DFF, 9, 0xFFFF},
-    {0x2E00, 0x2E7F, 31, 0xFFFF},
-    {0x2E80, 0x2EFF, 59, 0xFFFF},
-    {0x2F00, 0x2FDF, 59, 0xFFFF},
-    {0x2FE0, 0x2FEF, 999, 0xFFFF},
-    {0x2FF0, 0x2FFF, 59, 0xFFFF},
-    {0x3000, 0x303F, 48, 0xFFFF},
-    {0x3040, 0x309F, 49, FX_CODEPAGE_ShiftJIS},
-    {0x30A0, 0x30FF, 50, FX_CODEPAGE_ShiftJIS},
-    {0x3100, 0x3129, 51, FX_CODEPAGE_ChineseSimplified},
-    {0x312A, 0x312F, 51, 0xFFFF},
-    {0x3130, 0x318F, 52, FX_CODEPAGE_Hangul},
-    {0x3190, 0x319F, 59, 0xFFFF},
-    {0x31A0, 0x31BF, 51, 0xFFFF},
-    {0x31C0, 0x31EF, 61, 0xFFFF},
-    {0x31F0, 0x31FF, 50, 0xFFFF},
-    {0x3200, 0x321C, 54, FX_CODEPAGE_Hangul},
-    {0x321D, 0x325F, 54, 0xFFFF},
-    {0x3260, 0x327F, 54, FX_CODEPAGE_Hangul},
-    {0x3280, 0x32FF, 54, 0xFFFF},
-    {0x3300, 0x3387, 55, 0xFFFF},
-    {0x3388, 0x33D0, 55, FX_CODEPAGE_Hangul},
-    {0x33D1, 0x33FF, 55, 0xFFFF},
-    {0x3400, 0x4DBF, 59, 0xFFFF},
-    {0x4DC0, 0x4DFF, 99, 0xFFFF},
-    {0x4E00, 0x9FA5, 59, FX_CODEPAGE_ChineseSimplified},
-    {0x9FA6, 0x9FFF, 59, 0xFFFF},
-    {0xA000, 0xA48F, 83, 0xFFFF},
-    {0xA490, 0xA4CF, 83, 0xFFFF},
-    {0xA4D0, 0xA4FF, 999, 0xFFFF},
-    {0xA500, 0xA63F, 12, 0xFFFF},
-    {0xA640, 0xA69F, 9, 0xFFFF},
-    {0xA6A0, 0xA6FF, 999, 0xFFFF},
-    {0xA700, 0xA71F, 5, 0xFFFF},
-    {0xA720, 0xA7FF, 29, 0xFFFF},
-    {0xA800, 0xA82F, 100, 0xFFFF},
-    {0xA830, 0xA8FF, 999, 0xFFFF},
-    {0xA840, 0xA87F, 53, 0xFFFF},
-    {0xA880, 0xA8DF, 115, 0xFFFF},
-    {0xA8E0, 0xA8FF, 999, 0xFFFF},
-    {0xA900, 0xA92F, 116, 0xFFFF},
-    {0xA930, 0xA95F, 117, 0xFFFF},
-    {0xA960, 0xA9FF, 999, 0xFFFF},
-    {0xAA00, 0xAA5F, 118, 0xFFFF},
-    {0xAA60, 0xABFF, 999, 0xFFFF},
-    {0xAC00, 0xD7AF, 56, FX_CODEPAGE_Hangul},
-    {0xD7B0, 0xD7FF, 999, 0xFFFF},
-    {0xD800, 0xDB7F, 57, 0xFFFF},
-    {0xDB80, 0xDBFF, 57, 0xFFFF},
-    {0xDC00, 0xDFFF, 57, 0xFFFF},
-    {0xE000, 0xE814, 60, 0xFFFF},
-    {0xE815, 0xE864, 60, FX_CODEPAGE_ChineseSimplified},
-    {0xE865, 0xF8FF, 60, 0xFFFF},
-    {0xF900, 0xFA0B, 61, FX_CODEPAGE_Hangul},
-    {0xFA0C, 0xFA0D, 61, FX_CODEPAGE_ChineseSimplified},
-    {0xFA0E, 0xFA2D, 61, FX_CODEPAGE_ShiftJIS},
-    {0xFA2E, 0xFAFF, 61, 0xFFFF},
-    {0xFB00, 0xFB4F, 62, 0xFFFF},
-    {0xFB50, 0xFDFF, 63, FX_CODEPAGE_MSWin_Arabic},
-    {0xFE00, 0xFE0F, 91, 0xFFFF},
-    {0xFE10, 0xFE1F, 65, 0xFFFF},
-    {0xFE20, 0xFE2F, 64, 0xFFFF},
-    {0xFE30, 0xFE4F, 65, 0xFFFF},
-    {0xFE50, 0xFE6F, 66, 0xFFFF},
-    {0xFE70, 0xFEFF, 67, FX_CODEPAGE_MSWin_Arabic},
-    {0xFF00, 0xFF5F, 68, FX_CODEPAGE_ChineseSimplified},
-    {0xFF60, 0xFF9F, 68, FX_CODEPAGE_ShiftJIS},
-    {0xFFA0, 0xFFEF, 68, 0xFFFF},
+const FGAS_FONTUSB kFXGdiFontUSBTable[] = {
+    {0x0000, 0x007F, 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x0080, 0x00FF, 1, FX_CodePage::kMSWin_WesternEuropean},
+    {0x0100, 0x017F, 2, FX_CodePage::kMSWin_EasternEuropean},
+    {0x0180, 0x024F, 3, FX_CodePage::kMSWin_EasternEuropean},
+    {0x0250, 0x02AF, 4, FX_CodePage::kFailure},
+    {0x02B0, 0x02FF, 5, FX_CodePage::kFailure},
+    {0x0300, 0x036F, 6, FX_CodePage::kFailure},
+    {0x0370, 0x03FF, 7, FX_CodePage::kMSWin_Greek},
+    {0x0400, 0x04FF, 9, FX_CodePage::kMSWin_Cyrillic},
+    {0x0500, 0x052F, 9, FX_CodePage::kFailure},
+    {0x0530, 0x058F, 10, FX_CodePage::kFailure},
+    {0x0590, 0x05FF, 11, FX_CodePage::kMSWin_Hebrew},
+    {0x0600, 0x06FF, 13, FX_CodePage::kMSWin_Arabic},
+    {0x0700, 0x074F, 71, FX_CodePage::kFailure},
+    {0x0750, 0x077F, 13, FX_CodePage::kFailure},
+    {0x0780, 0x07BF, 72, FX_CodePage::kFailure},
+    {0x07C0, 0x07FF, 14, FX_CodePage::kFailure},
+    {0x0800, 0x08FF, FGAS_FONTUSB::kNoBitField, FX_CodePage::kFailure},
+    {0x0900, 0x097F, 15, FX_CodePage::kFailure},
+    {0x0980, 0x09FF, 16, FX_CodePage::kFailure},
+    {0x0A00, 0x0A7F, 17, FX_CodePage::kFailure},
+    {0x0A80, 0x0AFF, 18, FX_CodePage::kFailure},
+    {0x0B00, 0x0B7F, 19, FX_CodePage::kFailure},
+    {0x0B80, 0x0BFF, 20, FX_CodePage::kFailure},
+    {0x0C00, 0x0C7F, 21, FX_CodePage::kFailure},
+    {0x0C80, 0x0CFF, 22, FX_CodePage::kFailure},
+    {0x0D00, 0x0D7F, 23, FX_CodePage::kFailure},
+    {0x0D80, 0x0DFF, 73, FX_CodePage::kFailure},
+    {0x0E00, 0x0E7F, 24, FX_CodePage::kMSDOS_Thai},
+    {0x0E80, 0x0EFF, 25, FX_CodePage::kFailure},
+    {0x0F00, 0x0FFF, 70, FX_CodePage::kFailure},
+    {0x1000, 0x109F, 74, FX_CodePage::kFailure},
+    {0x10A0, 0x10FF, 26, FX_CodePage::kFailure},
+    {0x1100, 0x11FF, 28, FX_CodePage::kFailure},
+    {0x1200, 0x137F, 75, FX_CodePage::kFailure},
+    {0x1380, 0x139F, 75, FX_CodePage::kFailure},
+    {0x13A0, 0x13FF, 76, FX_CodePage::kFailure},
+    {0x1400, 0x167F, 77, FX_CodePage::kFailure},
+    {0x1680, 0x169F, 78, FX_CodePage::kFailure},
+    {0x16A0, 0x16FF, 79, FX_CodePage::kFailure},
+    {0x1700, 0x171F, 84, FX_CodePage::kFailure},
+    {0x1720, 0x173F, 84, FX_CodePage::kFailure},
+    {0x1740, 0x175F, 84, FX_CodePage::kFailure},
+    {0x1760, 0x177F, 84, FX_CodePage::kFailure},
+    {0x1780, 0x17FF, 80, FX_CodePage::kFailure},
+    {0x1800, 0x18AF, 81, FX_CodePage::kFailure},
+    {0x18B0, 0x18FF, FGAS_FONTUSB::kNoBitField, FX_CodePage::kFailure},
+    {0x1900, 0x194F, 93, FX_CodePage::kFailure},
+    {0x1950, 0x197F, 94, FX_CodePage::kFailure},
+    {0x1980, 0x19DF, 95, FX_CodePage::kFailure},
+    {0x19E0, 0x19FF, 80, FX_CodePage::kFailure},
+    {0x1A00, 0x1A1F, 96, FX_CodePage::kFailure},
+    {0x1A20, 0x1AFF, FGAS_FONTUSB::kNoBitField, FX_CodePage::kFailure},
+    {0x1B00, 0x1B7F, 27, FX_CodePage::kFailure},
+    {0x1B80, 0x1BBF, 112, FX_CodePage::kFailure},
+    {0x1BC0, 0x1BFF, FGAS_FONTUSB::kNoBitField, FX_CodePage::kFailure},
+    {0x1C00, 0x1C4F, 113, FX_CodePage::kFailure},
+    {0x1C50, 0x1C7F, 114, FX_CodePage::kFailure},
+    {0x1C80, 0x1CFF, FGAS_FONTUSB::kNoBitField, FX_CodePage::kFailure},
+    {0x1D00, 0x1D7F, 4, FX_CodePage::kFailure},
+    {0x1D80, 0x1DBF, 4, FX_CodePage::kFailure},
+    {0x1DC0, 0x1DFF, 6, FX_CodePage::kFailure},
+    {0x1E00, 0x1EFF, 29, FX_CodePage::kFailure},
+    {0x1F00, 0x1FFF, 30, FX_CodePage::kFailure},
+    {0x2000, 0x206F, 31, FX_CodePage::kFailure},
+    {0x2070, 0x209F, 32, FX_CodePage::kFailure},
+    {0x20A0, 0x20CF, 33, FX_CodePage::kFailure},
+    {0x20D0, 0x20FF, 34, FX_CodePage::kFailure},
+    {0x2100, 0x214F, 35, FX_CodePage::kFailure},
+    {0x2150, 0x215F, 36, FX_CodePage::kFailure},
+    {0x2160, 0x216B, 36, FX_CodePage::kChineseSimplified},
+    {0x216C, 0x216F, 36, FX_CodePage::kFailure},
+    {0x2170, 0x2179, 36, FX_CodePage::kChineseSimplified},
+    {0x217A, 0x218F, 36, FX_CodePage::kFailure},
+    {0x2190, 0x2199, 37, FX_CodePage::kHangul},
+    {0x219A, 0x21FF, 37, FX_CodePage::kFailure},
+    {0x2200, 0x22FF, 38, FX_CodePage::kFailure},
+    {0x2300, 0x23FF, 39, FX_CodePage::kFailure},
+    {0x2400, 0x243F, 40, FX_CodePage::kFailure},
+    {0x2440, 0x245F, 41, FX_CodePage::kFailure},
+    {0x2460, 0x2473, 42, FX_CodePage::kShiftJIS},
+    {0x2474, 0x249B, 42, FX_CodePage::kChineseSimplified},
+    {0x249C, 0x24E9, 42, FX_CodePage::kHangul},
+    {0x24EA, 0x24FF, 42, FX_CodePage::kFailure},
+    {0x2500, 0x2573, 43, FX_CodePage::kChineseSimplified},
+    {0x2574, 0x257F, 43, FX_CodePage::kFailure},
+    {0x2580, 0x2580, 44, FX_CodePage::kFailure},
+    {0x2581, 0x258F, 44, FX_CodePage::kChineseSimplified},
+    {0x2590, 0x259F, 44, FX_CodePage::kFailure},
+    {0x25A0, 0x25FF, 45, FX_CodePage::kFailure},
+    {0x2600, 0x26FF, 46, FX_CodePage::kFailure},
+    {0x2700, 0x27BF, 47, FX_CodePage::kFailure},
+    {0x27C0, 0x27EF, 38, FX_CodePage::kFailure},
+    {0x27F0, 0x27FF, 37, FX_CodePage::kFailure},
+    {0x2800, 0x28FF, 82, FX_CodePage::kFailure},
+    {0x2900, 0x297F, 37, FX_CodePage::kFailure},
+    {0x2980, 0x29FF, 38, FX_CodePage::kFailure},
+    {0x2A00, 0x2AFF, 38, FX_CodePage::kFailure},
+    {0x2B00, 0x2BFF, 37, FX_CodePage::kFailure},
+    {0x2C00, 0x2C5F, 97, FX_CodePage::kFailure},
+    {0x2C60, 0x2C7F, 29, FX_CodePage::kFailure},
+    {0x2C80, 0x2CFF, 8, FX_CodePage::kFailure},
+    {0x2D00, 0x2D2F, 26, FX_CodePage::kFailure},
+    {0x2D30, 0x2D7F, 98, FX_CodePage::kFailure},
+    {0x2D80, 0x2DDF, 75, FX_CodePage::kFailure},
+    {0x2DE0, 0x2DFF, 9, FX_CodePage::kFailure},
+    {0x2E00, 0x2E7F, 31, FX_CodePage::kFailure},
+    {0x2E80, 0x2EFF, 59, FX_CodePage::kFailure},
+    {0x2F00, 0x2FDF, 59, FX_CodePage::kFailure},
+    {0x2FE0, 0x2FEF, FGAS_FONTUSB::kNoBitField, FX_CodePage::kFailure},
+    {0x2FF0, 0x2FFF, 59, FX_CodePage::kFailure},
+    {0x3000, 0x303F, 48, FX_CodePage::kFailure},
+    {0x3040, 0x309F, 49, FX_CodePage::kShiftJIS},
+    {0x30A0, 0x30FF, 50, FX_CodePage::kShiftJIS},
+    {0x3100, 0x3129, 51, FX_CodePage::kChineseSimplified},
+    {0x312A, 0x312F, 51, FX_CodePage::kFailure},
+    {0x3130, 0x318F, 52, FX_CodePage::kHangul},
+    {0x3190, 0x319F, 59, FX_CodePage::kFailure},
+    {0x31A0, 0x31BF, 51, FX_CodePage::kFailure},
+    {0x31C0, 0x31EF, 61, FX_CodePage::kFailure},
+    {0x31F0, 0x31FF, 50, FX_CodePage::kFailure},
+    {0x3200, 0x321C, 54, FX_CodePage::kHangul},
+    {0x321D, 0x325F, 54, FX_CodePage::kFailure},
+    {0x3260, 0x327F, 54, FX_CodePage::kHangul},
+    {0x3280, 0x32FF, 54, FX_CodePage::kFailure},
+    {0x3300, 0x3387, 55, FX_CodePage::kFailure},
+    {0x3388, 0x33D0, 55, FX_CodePage::kHangul},
+    {0x33D1, 0x33FF, 55, FX_CodePage::kFailure},
+    {0x3400, 0x4DBF, 59, FX_CodePage::kFailure},
+    {0x4DC0, 0x4DFF, 99, FX_CodePage::kFailure},
+    {0x4E00, 0x9FA5, 59, FX_CodePage::kChineseSimplified},
+    {0x9FA6, 0x9FFF, 59, FX_CodePage::kFailure},
+    {0xA000, 0xA48F, 83, FX_CodePage::kFailure},
+    {0xA490, 0xA4CF, 83, FX_CodePage::kFailure},
+    {0xA4D0, 0xA4FF, FGAS_FONTUSB::kNoBitField, FX_CodePage::kFailure},
+    {0xA500, 0xA63F, 12, FX_CodePage::kFailure},
+    {0xA640, 0xA69F, 9, FX_CodePage::kFailure},
+    {0xA6A0, 0xA6FF, FGAS_FONTUSB::kNoBitField, FX_CodePage::kFailure},
+    {0xA700, 0xA71F, 5, FX_CodePage::kFailure},
+    {0xA720, 0xA7FF, 29, FX_CodePage::kFailure},
+    {0xA800, 0xA82F, 100, FX_CodePage::kFailure},
+    {0xA830, 0xA8FF, FGAS_FONTUSB::kNoBitField, FX_CodePage::kFailure},
+    {0xA840, 0xA87F, 53, FX_CodePage::kFailure},
+    {0xA880, 0xA8DF, 115, FX_CodePage::kFailure},
+    {0xA8E0, 0xA8FF, FGAS_FONTUSB::kNoBitField, FX_CodePage::kFailure},
+    {0xA900, 0xA92F, 116, FX_CodePage::kFailure},
+    {0xA930, 0xA95F, 117, FX_CodePage::kFailure},
+    {0xA960, 0xA9FF, FGAS_FONTUSB::kNoBitField, FX_CodePage::kFailure},
+    {0xAA00, 0xAA5F, 118, FX_CodePage::kFailure},
+    {0xAA60, 0xABFF, FGAS_FONTUSB::kNoBitField, FX_CodePage::kFailure},
+    {0xAC00, 0xD7AF, 56, FX_CodePage::kHangul},
+    {0xD7B0, 0xD7FF, FGAS_FONTUSB::kNoBitField, FX_CodePage::kFailure},
+    {0xD800, 0xDB7F, 57, FX_CodePage::kFailure},
+    {0xDB80, 0xDBFF, 57, FX_CodePage::kFailure},
+    {0xDC00, 0xDFFF, 57, FX_CodePage::kFailure},
+    {0xE000, 0xE814, 60, FX_CodePage::kFailure},
+    {0xE815, 0xE864, 60, FX_CodePage::kChineseSimplified},
+    {0xE865, 0xF8FF, 60, FX_CodePage::kFailure},
+    {0xF900, 0xFA0B, 61, FX_CodePage::kHangul},
+    {0xFA0C, 0xFA0D, 61, FX_CodePage::kChineseSimplified},
+    {0xFA0E, 0xFA2D, 61, FX_CodePage::kShiftJIS},
+    {0xFA2E, 0xFAFF, 61, FX_CodePage::kFailure},
+    {0xFB00, 0xFB4F, 62, FX_CodePage::kFailure},
+    {0xFB50, 0xFDFF, 63, FX_CodePage::kMSWin_Arabic},
+    {0xFE00, 0xFE0F, 91, FX_CodePage::kFailure},
+    {0xFE10, 0xFE1F, 65, FX_CodePage::kFailure},
+    {0xFE20, 0xFE2F, 64, FX_CodePage::kFailure},
+    {0xFE30, 0xFE4F, 65, FX_CodePage::kFailure},
+    {0xFE50, 0xFE6F, 66, FX_CodePage::kFailure},
+    {0xFE70, 0xFEFF, 67, FX_CodePage::kMSWin_Arabic},
+    {0xFF00, 0xFF5F, 68, FX_CodePage::kChineseSimplified},
+    {0xFF60, 0xFF9F, 68, FX_CodePage::kShiftJIS},
+    {0xFFA0, 0xFFEF, 68, FX_CodePage::kFailure},
 };
 
-#if defined(OS_WIN)
-const FGAS_FontInfo g_XFAFontsMap[] = {
-    {0x01d5d33e, "SimSun", "Arial", 0, 936},
-    {0x01e4f102, "YouYuan", "Arial", 1, 936},
-    {0x030549dc, "LiSu", "Arial", 1, 936},
-    {0x032edd44, "Simhei", "Arial", 1, 936},
-    {0x03eac6fc, "PoorRichard-Regular", "Arial", 2, 1252},
-    {0x03ed90e6, "Nina", "Arial", 0, 1252},
-    {0x077b56b3, "KingsoftPhoneticPlain", "Arial", 0, 1252},
-    {0x078ed524, "MicrosoftSansSerif", "Arial", 0, 1252},
-    {0x089b18a9, "Arial", "Arial", 0, 1252},
-    {0x0b2cad72, "MonotypeCorsiva", "Arial", 8, 1252},
-    {0x0bb003e7, "Kartika", "Arial", 2, 1252},
-    {0x0bb469df, "VinerHandITC", "Arial", 8, 1252},
-    {0x0bc1a851, "SegoeUI", "Arial", 0, 1252},
-    {0x0c112ebd, "KozukaGothicPro-VIM", "Arial", 0, 1252},
-    {0x0cfcb9c1, "AdobeThai", "Kokila,Arial Narrow", 0, 847},
-    {0x0e7de0f9, "Playbill", "Arial", 0, 1252},
-    {0x0eff47c3, "STHupo", "Arial", 0, 936},
-    {0x107ad374, "Constantia", "Arial", 2, 1252},
-    {0x12194c2d, "KunstlerScript", "Arial", 8, 1252},
+#if BUILDFLAG(IS_WIN)
+const FGAS_FontInfo kXFAFontsMap[] = {
+    {0x01d5d33e, "SimSun", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0x01e4f102, "YouYuan", "Arial", 1, FX_CodePage::kChineseSimplified},
+    {0x030549dc, "LiSu", "Arial", 1, FX_CodePage::kChineseSimplified},
+    {0x032edd44, "Simhei", "Arial", 1, FX_CodePage::kChineseSimplified},
+    {0x03eac6fc, "PoorRichard-Regular", "Arial", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x03ed90e6, "Nina", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x077b56b3, "KingsoftPhoneticPlain", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x078ed524, "MicrosoftSansSerif", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x089b18a9, "Arial", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x0b2cad72, "MonotypeCorsiva", "Arial", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x0bb003e7, "Kartika", "Arial", 2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x0bb469df, "VinerHandITC", "Arial", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x0bc1a851, "SegoeUI", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x0c112ebd, "KozukaGothicPro-VIM", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    // Was 847 (not defined), presumably 874 (Thai).
+    {0x0cfcb9c1, "AdobeThai", "Kokila,Arial Narrow", 0,
+     FX_CodePage::kMSDOS_Thai},
+    {0x0e7de0f9, "Playbill", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x0eff47c3, "STHupo", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0x107ad374, "Constantia", "Arial", 2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x12194c2d, "KunstlerScript", "Arial", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x135ef6a1, "MinionProSmBd",
-     "Bell MT,Corbel,Times New Roman,Cambria,Berlin Sans FB", 0, 1252},
-    {0x158c4049, "Garamond", "Arial", 2, 1252},
-    {0x160ecb24, "STZhongsong", "Arial", 0, 936},
-    {0x161ed07e, "MSGothic", "Arial", 1, 1252},
-    {0x171d1ed1, "SnapITC-Regular", "Arial", 0, 1252},
-    {0x18d1188f, "Cambria", "Arial", 2, 1252},
-    {0x18eaf350, "ArialUnicodeMS", "Arial", 0, 936},
-    {0x1a92d115, "MingLiU", "Arial", 1, 1252},
-    {0x1cc217c6, "TrebuchetMS", "Arial", 0, 1252},
-    {0x1d649596, "BasemicTimes", "Arial", 0, 1252},
-    {0x1e34ee60, "BellMT", "Arial", 2, 1252},
-    {0x1eb36945, "CooperBlack", "Arial", 2, 1252},
-    {0x1ef7787d, "BatangChe", "Arial", 1, 1252},
-    {0x20b3bd3a, "BrushScriptMT", "Arial", 8, 1252},
-    {0x220877aa, "Candara", "Arial", 0, 1252},
-    {0x22135007, "FreestyleScript-Regular", "Arial", 8, 1252},
-    {0x251059c3, "Chiller", "Arial", 0, 1252},
-    {0x25bed6dd, "MSReferenceSansSerif", "Arial", 0, 1252},
-    {0x28154c81, "Parchment-Regular", "Arial", 8, 1252},
-    {0x29711eb9, "STLiti", "Arial", 0, 936},
-    {0x2b1993b4, "Basemic", "Arial", 0, 1252},
-    {0x2b316339, "NiagaraSolid-Reg", "Arial", 0, 1252},
-    {0x2c147529, "FootlightMTLight", "Arial", 0, 1252},
-    {0x2c198928, "HarlowSolid", "Arial", 0, 1252},
-    {0x2c6ac6b2, "LucidaBright", "Arial", 2, 1252},
-    {0x2c9f38e2, "KozukaMinchoPro-VIR", "Arial", 0, 1252},
-    {0x2d5a47b0, "STCaiyun", "Arial", 0, 936},
-    {0x2def26bf, "BernardMT-Condensed", "Arial", 0, 1252},
-    {0x2fd8930b, "KozukaMinchoPr6NR", "Arial", 0, 1252},
-    {0x3115525a, "FangSong_GB2312", "Arial", 0, 1252},
+     "Bell MT,Corbel,Times New Roman,Cambria,Berlin Sans FB", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x158c4049, "Garamond", "Arial", 2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x160ecb24, "STZhongsong", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0x161ed07e, "MSGothic", "Arial", 1, FX_CodePage::kMSWin_WesternEuropean},
+    {0x171d1ed1, "SnapITC-Regular", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x18d1188f, "Cambria", "Arial", 2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x18eaf350, "ArialUnicodeMS", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0x1a92d115, "MingLiU", "Arial", 1, FX_CodePage::kMSWin_WesternEuropean},
+    {0x1cc217c6, "TrebuchetMS", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x1d649596, "BasemicTimes", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x1e34ee60, "BellMT", "Arial", 2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x1eb36945, "CooperBlack", "Arial", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x1ef7787d, "BatangChe", "Arial", 1, FX_CodePage::kMSWin_WesternEuropean},
+    {0x20b3bd3a, "BrushScriptMT", "Arial", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x220877aa, "Candara", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x22135007, "FreestyleScript-Regular", "Arial", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x251059c3, "Chiller", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x25bed6dd, "MSReferenceSansSerif", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x28154c81, "Parchment-Regular", "Arial", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x29711eb9, "STLiti", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0x2b1993b4, "Basemic", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x2b316339, "NiagaraSolid-Reg", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x2c147529, "FootlightMTLight", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x2c198928, "HarlowSolid", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x2c6ac6b2, "LucidaBright", "Arial", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x2c9f38e2, "KozukaMinchoPro-VIR", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x2d5a47b0, "STCaiyun", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0x2def26bf, "BernardMT-Condensed", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x2fd8930b, "KozukaMinchoPr6NR", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3115525a, "FangSong_GB2312", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x31327817, "MyriadPro",
      "Calibri,Corbel,Candara,Cambria Math,Franklin Gothic Medium,Arial "
      "Narrow,Times New Roman",
-     0, 1252},
-    {0x32244975, "Helvetica", "Arial", 0, 1252},
-    {0x32ac995c, "Terminal", "Arial", 0, 1252},
-    {0x338d648a, "NiagaraEngraved-Reg", "Arial", 0, 1252},
-    {0x33bb65f2, "Sylfaen", "Arial", 2, 1252},
-    {0x3402c30e, "MSPMincho", "Arial", 2, 1252},
-    {0x3412bf31, "SimSun-PUA", "Arial", 0, 936},
-    {0x36eb39b9, "BerlinSansFB", "Arial", 0, 1252},
-    {0x36f42055, "UniversATT", "Microsoft Sans Serif", 0, 1252},
-    {0x3864c4f6, "HighTowerText", "Arial", 2, 1252},
-    {0x3a257d03, "FangSong_GB2312", "Arial", 0, 1252},
-    {0x3cdae668, "FreestyleScript", "Arial", 8, 1252},
-    {0x3d55aed7, "Jokerman", "Arial", 0, 1252},
-    {0x3d5b4385, "PMingLiU", "Arial", 2, 1252},
-    {0x3d9b7669, "EstrangeloEdessa", "Arial", 0, 1252},
-    {0x3e532d74, "FranklinGothicMedium", "Arial", 0, 1252},
-    {0x3e6aa32d, "NSimSun", "Arial", 1, 936},
-    {0x3f6c36a8, "Gautami", "Arial", 0, 1252},
-    {0x3ff32662, "Chiller-Regular", "Arial", 0, 1252},
-    {0x409de312, "ModernNo.20", "Arial", 2, 1252},
-    {0x41443c5e, "Georgia", "Arial", 2, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x32244975, "Helvetica", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x32ac995c, "Terminal", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x338d648a, "NiagaraEngraved-Reg", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x33bb65f2, "Sylfaen", "Arial", 2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x3402c30e, "MSPMincho", "Arial", 2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x3412bf31, "SimSun-PUA", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0x36eb39b9, "BerlinSansFB", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x36f42055, "UniversATT", "Microsoft Sans Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3864c4f6, "HighTowerText", "Arial", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3a257d03, "FangSong_GB2312", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3cdae668, "FreestyleScript", "Arial", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3d55aed7, "Jokerman", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x3d5b4385, "PMingLiU", "Arial", 2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x3d9b7669, "EstrangeloEdessa", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3e532d74, "FranklinGothicMedium", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3e6aa32d, "NSimSun", "Arial", 1, FX_CodePage::kChineseSimplified},
+    {0x3f6c36a8, "Gautami", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x3ff32662, "Chiller-Regular", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x409de312, "ModernNo.20", "Arial", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x41443c5e, "Georgia", "Arial", 2, FX_CodePage::kMSWin_WesternEuropean},
     {0x4160ade5, "BellGothicStdBlack",
-     "Arial,Arial Unicode MS,Book Antiqua,Dotum,Georgia", 0, 1252},
-    {0x421976c4, "Modern-Regular", "Arial", 2, 1252},
-    {0x422a7252, "Stencil", "Arial", 0, 1252},
-    {0x42c8554f, "Fixedsys", "Arial", 0, 1252},
-    {0x435cb41d, "Roman", "Arial", 0, 1252},
-    {0x47882383, "CourierNew", "Arial", 1, 1252},
-    {0x480a2338, "BerlinSansFBDemi", "Arial", 0, 1252},
-    {0x480bf7a4, "CourierStd", "Courier New,Verdana", 0, 1252},
-    {0x481ad6ed, "VladimirScript", "Arial", 8, 1252},
-    {0x4911577a, "YouYuan", "Arial", 1, 936},
-    {0x4a788d72, "STXingkai", "Arial", 0, 936},
-    {0x4bf88566, "SegoeCondensed", "Arial", 0, 1252},
-    {0x4ccf51a4, "BerlinSansFB-Reg", "Arial", 0, 1252},
-    {0x4ea967ce, "GulimChe", "Arial", 1, 1252},
-    {0x4f68bd79, "LetterGothicStd", "Courier New,Verdana", 0, 1252},
-    {0x51a0d0e6, "KozukaGothicPr6NM", "Arial", 0, 1252},
-    {0x531b3dea, "BasemicSymbol", "Arial", 0, 1252},
-    {0x5333fd39, "CalifornianFB-Reg", "Arial", 2, 1252},
-    {0x53561a54, "FZYTK--GBK1-0", "Arial", 0, 936},
-    {0x55e0dde6, "LucidaSansTypewriter", "Arial", 0, 1252},
-    {0x574d4d3d, "AdobeArabic", "Arial Narrow", 0, 1252},
-    {0x5792e759, "STKaiti", "Arial", 0, 936},
-    {0x5921978e, "LucidaSansUnicode", "Arial", 0, 1252},
-    {0x594e2da4, "Vrinda", "Arial", 0, 1252},
-    {0x59baa9a2, "KaiTi_GB2312", "Arial", 0, 1252},
-    {0x5cfedf4f, "BaskOldFace", "Arial", 0, 1252},
+     "Arial,Arial Unicode MS,Book Antiqua,Dotum,Georgia", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x421976c4, "Modern-Regular", "Arial", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x422a7252, "Stencil", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x42c8554f, "Fixedsys", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x435cb41d, "Roman", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x47882383, "CourierNew", "Arial", 1, FX_CodePage::kMSWin_WesternEuropean},
+    {0x480a2338, "BerlinSansFBDemi", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x480bf7a4, "CourierStd", "Courier New,Verdana", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x481ad6ed, "VladimirScript", "Arial", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x4911577a, "YouYuan", "Arial", 1, FX_CodePage::kChineseSimplified},
+    {0x4a788d72, "STXingkai", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0x4bf88566, "SegoeCondensed", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x4ccf51a4, "BerlinSansFB-Reg", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x4ea967ce, "GulimChe", "Arial", 1, FX_CodePage::kMSWin_WesternEuropean},
+    {0x4f68bd79, "LetterGothicStd", "Courier New,Verdana", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x51a0d0e6, "KozukaGothicPr6NM", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x531b3dea, "BasemicSymbol", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x5333fd39, "CalifornianFB-Reg", "Arial", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x53561a54, "FZYTK--GBK1-0", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0x55e0dde6, "LucidaSansTypewriter", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x574d4d3d, "AdobeArabic", "Arial Narrow", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x5792e759, "STKaiti", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0x5921978e, "LucidaSansUnicode", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x594e2da4, "Vrinda", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x59baa9a2, "KaiTi_GB2312", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x5cfedf4f, "BaskOldFace", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x5f97921c, "AdobeMyungjoStdM",
-     "Batang,Bookman Old Style,Consolas,STZhongsong", 0, 936},
-    {0x5fefbfad, "Batang", "Arial", 2, 1252},
-    {0x605342b9, "DotumChe", "Arial", 1, 1252},
-    {0x608c5f9a, "KaiTi_GB2312", "Arial", 0, 936},
-    {0x61efd0d1, "MaturaMTScriptCapitals", "Arial", 0, 1252},
-    {0x626608a9, "MVBoli", "Arial", 0, 1252},
-    {0x630501a3, "SmallFonts", "Arial", 0, 1252},
-    {0x65d0e2a9, "FZYTK--GBK1-0", "Arial", 0, 936},
-    {0x669f29e1, "FZSTK--GBK1-0", "Arial", 0, 936},
-    {0x673a9e5f, "Tunga", "Arial", 0, 1252},
-    {0x691aa4ce, "NiagaraSolid", "Arial", 0, 1252},
-    {0x696259b7, "Corbel", "Arial", 0, 1252},
-    {0x696ee9be, "STXihei", "Arial", 0, 936},
-    {0x6c59cf69, "Dotum", "Arial", 0, 1252},
-    {0x707fa561, "Gungsuh", "Arial", 2, 1252},
-    {0x71416bb2, "ZWAdobeF", "Arial", 0, 1252},
-    {0x71b41801, "Verdana", "Arial", 0, 1252},
-    {0x73f25e4c, "PalatinoLinotype", "Arial", 0, 1252},
-    {0x73f4d19f, "NiagaraEngraved", "Arial", 0, 1252},
+     "Batang,Bookman Old Style,Consolas,STZhongsong", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x5fefbfad, "Batang", "Arial", 2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x605342b9, "DotumChe", "Arial", 1, FX_CodePage::kMSWin_WesternEuropean},
+    {0x608c5f9a, "KaiTi_GB2312", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0x61efd0d1, "MaturaMTScriptCapitals", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x626608a9, "MVBoli", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x630501a3, "SmallFonts", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x65d0e2a9, "FZYTK--GBK1-0", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0x669f29e1, "FZSTK--GBK1-0", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0x673a9e5f, "Tunga", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x691aa4ce, "NiagaraSolid", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x696259b7, "Corbel", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x696ee9be, "STXihei", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0x6c59cf69, "Dotum", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x707fa561, "Gungsuh", "Arial", 2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x71416bb2, "ZWAdobeF", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x71b41801, "Verdana", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x73f25e4c, "PalatinoLinotype", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x73f4d19f, "NiagaraEngraved", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x74001694, "MyriadProBlack", "Book Antiqua,Constantia,Dotum,Georgia", 0,
-     1252},
-    {0x74b14d8f, "Haettenschweiler", "Arial", 0, 1252},
-    {0x74cb44ee, "NSimSun", "Arial", 1, 936},
-    {0x76b4d7ff, "Shruti", "Arial", 0, 1252},
-    {0x788b3533, "Webdings", "Arial", 6, 42},
-    {0x797dde99, "MSSerif", "Arial", 0, 1252},
-    {0x7a0f9e9e, "MSMincho", "Arial", 1, 1252},
-    {0x7b439caf, "OldEnglishTextMT", "Arial", 0, 1252},
-    {0x8213a433, "LucidaSans-Typewriter", "Arial", 0, 1252},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x74b14d8f, "Haettenschweiler", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x74cb44ee, "NSimSun", "Arial", 1, FX_CodePage::kChineseSimplified},
+    {0x76b4d7ff, "Shruti", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x788b3533, "Webdings", "Arial", 6, FX_CodePage::kSymbol},
+    {0x797dde99, "MSSerif", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x7a0f9e9e, "MSMincho", "Arial", 1, FX_CodePage::kMSWin_WesternEuropean},
+    {0x7b439caf, "OldEnglishTextMT", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x8213a433, "LucidaSans-Typewriter", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x82fec929, "AdobeSongStd",
-     "Centaur,Calibri,STSong,Bell MT,Garamond,Times New Roman", 0, 936},
-    {0x83581825, "Modern", "Arial", 0, 1252},
-    {0x835a2823, "Algerian", "Arial", 0, 1252},
-    {0x83dab9f5, "Script", "Arial", 0, 1252},
-    {0x847b56da, "Tahoma", "Arial", 0, 1252},
-    {0x8a783cb2, "SimSun-PUA", "Arial", 0, 1252},
-    {0x8b5cac0e, "Onyx", "Arial", 0, 1252},
-    {0x8c6a499e, "Gulim", "Arial", 0, 1252},
-    {0x8e0af790, "JuiceITC", "Arial", 0, 1252},
-    {0x8e8d43b2, "Centaur", "Arial", 2, 1252},
-    {0x8ee4dcca, "BookshelfSymbol7", "Arial", 0, 1252},
+     "Centaur,Calibri,STSong,Bell MT,Garamond,Times New Roman", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x83581825, "Modern", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x835a2823, "Algerian", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x83dab9f5, "Script", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x847b56da, "Tahoma", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x8a783cb2, "SimSun-PUA", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x8b5cac0e, "Onyx", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x8c6a499e, "Gulim", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x8e0af790, "JuiceITC", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x8e8d43b2, "Centaur", "Arial", 2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x8ee4dcca, "BookshelfSymbol7", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x90794800, "BellGothicStdLight", "Bell MT,Calibri,Times New Roman", 0,
-     1252},
-    {0x909b516a, "Century", "Arial", 2, 1252},
-    {0x92ae370d, "MSOutlook", "Arial", 4, 42},
-    {0x93c9fbf1, "LucidaFax", "Arial", 2, 1252},
-    {0x9565085e, "BookAntiqua", "Arial", 2, 1252},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x909b516a, "Century", "Arial", 2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x92ae370d, "MSOutlook", "Arial", 4, FX_CodePage::kSymbol},
+    {0x93c9fbf1, "LucidaFax", "Arial", 2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x9565085e, "BookAntiqua", "Arial", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x9856d95d, "AdobeMingStd", "Arial,Arial Unicode MS,Cambria,BatangChe", 0,
-     949},
-    {0x9bbadd6b, "ColonnaMT", "Arial", 0, 1252},
-    {0x9cbd16a4, "ShowcardGothic-Reg", "Arial", 0, 1252},
-    {0x9d73008e, "MSSansSerif", "Arial", 0, 1252},
-    {0xa0607db1, "GungsuhChe", "Arial", 1, 1252},
-    {0xa0bcf6a1, "LatinWide", "Arial", 2, 1252},
-    {0xa1429b36, "Symbol", "Arial", 6, 42},
-    {0xa1fa5abc, "Wingdings2", "Arial", 6, 42},
-    {0xa1fa5abd, "Wingdings3", "Arial", 6, 42},
-    {0xa427bad4, "InformalRoman-Regular", "Arial", 8, 1252},
-    {0xa8b92ece, "FZSTK--GBK1-0", "Arial", 0, 936},
-    {0xa8d83ece, "CalifornianFB", "Arial", 2, 1252},
-    {0xaa3e082c, "Kingsoft-Phonetic", "Arial", 0, 1252},
-    {0xaa6bcabe, "HarlowSolidItalic", "Arial", 0, 1252},
-    {0xade5337c, "MSUIGothic", "Arial", 0, 1252},
-    {0xb08dd941, "WideLatin", "Arial", 2, 1252},
-    {0xb207f05d, "PoorRichard", "Arial", 2, 1252},
-    {0xb3bc492f, "JuiceITC-Regular", "Arial", 0, 1252},
-    {0xb5545399, "Marlett", "Arial", 4, 42},
-    {0xb5dd1ebb, "BritannicBold", "Arial", 0, 1252},
-    {0xb699c1c5, "LucidaCalligraphy-Italic", "Arial", 0, 1252},
-    {0xb725d629, "TimesNewRoman", "Arial", 2, 1252},
-    {0xb7eaebeb, "AdobeHeitiStdR", "Batang,Century,Dotum", 0, 936},
-    {0xbd29c486, "BerlinSansFBDemi-Bold", "Arial", 0, 1252},
-    {0xbe8a8db4, "BookshelfSymbolSeven", "Arial", 0, 1252},
-    {0xc16c0118, "AdobeHebrew", "Bell MT,Berlin Sans FB,Calibri", 0, 1252},
+     FX_CodePage::kHangul},
+    {0x9bbadd6b, "ColonnaMT", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x9cbd16a4, "ShowcardGothic-Reg", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x9d73008e, "MSSansSerif", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xa0607db1, "GungsuhChe", "Arial", 1, FX_CodePage::kMSWin_WesternEuropean},
+    {0xa0bcf6a1, "LatinWide", "Arial", 2, FX_CodePage::kMSWin_WesternEuropean},
+    {0xa1429b36, "Symbol", "Arial", 6, FX_CodePage::kSymbol},
+    {0xa1fa5abc, "Wingdings2", "Arial", 6, FX_CodePage::kSymbol},
+    {0xa1fa5abd, "Wingdings3", "Arial", 6, FX_CodePage::kSymbol},
+    {0xa427bad4, "InformalRoman-Regular", "Arial", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xa8b92ece, "FZSTK--GBK1-0", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0xa8d83ece, "CalifornianFB", "Arial", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xaa3e082c, "Kingsoft-Phonetic", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xaa6bcabe, "HarlowSolidItalic", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xade5337c, "MSUIGothic", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xb08dd941, "WideLatin", "Arial", 2, FX_CodePage::kMSWin_WesternEuropean},
+    {0xb207f05d, "PoorRichard", "Arial", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xb3bc492f, "JuiceITC-Regular", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xb5545399, "Marlett", "Arial", 4, FX_CodePage::kSymbol},
+    {0xb5dd1ebb, "BritannicBold", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xb699c1c5, "LucidaCalligraphy-Italic", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xb725d629, "TimesNewRoman", "Arial", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xb7eaebeb, "AdobeHeitiStdR", "Batang,Century,Dotum", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xbd29c486, "BerlinSansFBDemi-Bold", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xbe8a8db4, "BookshelfSymbolSeven", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xc16c0118, "AdobeHebrew", "Bell MT,Berlin Sans FB,Calibri", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xc318b0af, "MyriadProLight", "Calibri,STFangsong,Times New Roman", 0,
-     1252},
-    {0xc65e5659, "CambriaMath", "Arial", 2, 1252},
-    {0xc75c8f05, "LucidaConsole", "Arial", 1, 1252},
-    {0xca7c35d6, "Calibri", "Arial", 0, 1252},
-    {0xcb053f53, "MicrosoftYaHei", "Arial", 0, 936},
-    {0xcb7190f9, "Magneto-Bold", "Arial", 0, 1252},
-    {0xcca00cc5, "System", "Arial", 0, 1252},
-    {0xccad6f76, "Jokerman-Regular", "Arial", 0, 1252},
-    {0xccc5818c, "EuroSign", "Arial", 0, 1252},
-    {0xcf3d7234, "LucidaHandwriting-Italic", "Arial", 0, 1252},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xc65e5659, "CambriaMath", "Arial", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xc75c8f05, "LucidaConsole", "Arial", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xca7c35d6, "Calibri", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xcb053f53, "MicrosoftYaHei", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0xcb7190f9, "Magneto-Bold", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xcca00cc5, "System", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xccad6f76, "Jokerman-Regular", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xccc5818c, "EuroSign", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xcf3d7234, "LucidaHandwriting-Italic", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xcf7b8fdb, "MinionPro",
-     "Bell MT,Corbel,Times New Roman,Cambria,Berlin Sans FB", 0, 1252},
-    {0xcfe5755f, "Simhei", "Arial", 1, 936},
-    {0xd011f4ee, "MSPGothic", "Arial", 0, 1252},
-    {0xd060e7ef, "Vivaldi", "Arial", 8, 1252},
-    {0xd07edec1, "FranklinGothic-Medium", "Arial", 0, 1252},
-    {0xd107243f, "SimSun", "Arial", 0, 936},
-    {0xd1881562, "ArialNarrow", "Arial Narrow", 0, 1252},
-    {0xd22b7dce, "BodoniMTPosterCompressed", "Arial", 0, 1252},
-    {0xd22bfa60, "ComicSansMS", "Arial", 8, 1252},
-    {0xd3bd0e35, "Bauhaus93", "Arial", 0, 1252},
-    {0xd429ee7a, "STFangsong", "Arial", 0, 936},
-    {0xd6679c12, "BernardMTCondensed", "Arial", 0, 1252},
-    {0xd8e8a027, "LucidaSans", "Arial", 0, 1252},
-    {0xd9fe7761, "HighTowerText-Reg", "Arial", 2, 1252},
-    {0xda7e551e, "STSong", "Arial", 0, 936},
-    {0xdaa6842d, "STZhongsong", "Arial", 0, 936},
-    {0xdaaab93f, "STFangsong", "Arial", 0, 936},
-    {0xdaeb0713, "STSong", "Arial", 0, 936},
-    {0xdafedbef, "STCaiyun", "Arial", 0, 936},
-    {0xdb00a3d9, "Broadway", "Arial", 0, 1252},
-    {0xdb1f5ad4, "STXinwei", "Arial", 0, 936},
-    {0xdb326e7f, "STKaiti", "Arial", 0, 936},
-    {0xdb69595a, "STHupo", "Arial", 0, 936},
-    {0xdba0082c, "STXihei", "Arial", 0, 936},
-    {0xdbd0ab18, "STXingkai", "Arial", 0, 936},
-    {0xdc1a7db1, "STLiti", "Arial", 0, 936},
-    {0xdc33075f, "KristenITC-Regular", "Arial", 8, 1252},
-    {0xdcc7009c, "Harrington", "Arial", 0, 1252},
-    {0xdd712466, "ArialBlack", "Arial", 0, 1252},
-    {0xdde87b3e, "Impact", "Arial", 0, 1252},
-    {0xdf69fb32, "SnapITC", "Arial", 0, 1252},
-    {0xdf8b25e8, "CenturyGothic", "Arial", 0, 1252},
-    {0xe0f705c0, "KristenITC", "Arial", 8, 1252},
-    {0xe1427573, "Raavi", "Arial", 0, 1252},
-    {0xe2cea0cb, "Magneto", "Arial", 0, 1252},
-    {0xe36a9e17, "Ravie", "Arial", 0, 1252},
-    {0xe433f8e2, "Parchment", "Arial", 8, 1252},
-    {0xe43dff4a, "Wingdings", "Arial", 4, 42},
-    {0xe4e2c405, "MTExtra", "Arial", 6, 42},
-    {0xe618cc35, "InformalRoman", "Arial", 8, 1252},
-    {0xe6c27ffc, "Mistral", "Arial", 8, 1252},
-    {0xe7ebf4b9, "Courier", "Courier New", 0, 1252},
-    {0xe8bc4a9d, "MSReferenceSpecialty", "Arial", 0, 1252},
-    {0xe90fb013, "TempusSansITC", "Arial", 0, 1252},
-    {0xec637b42, "Consolas", "Verdana", 1, 1252},
-    {0xed3a683b, "STXinwei", "Arial", 0, 936},
-    {0xef264cd1, "LucidaHandwriting", "Arial", 0, 1252},
-    {0xf086bca2, "BaskervilleOldFace", "Arial", 0, 1252},
-    {0xf1028030, "Mangal", "Arial", 2, 1252},
-    {0xf1da7eb9, "ShowcardGothic", "Arial", 0, 1252},
-    {0xf210f06a, "ArialMT", "Arial", 0, 1252},
-    {0xf477f16a, "Latha", "Arial", 0, 1252},
-    {0xf616f3dd, "LiSu", "Arial", 1, 936},
-    {0xfa479aa6, "MicrosoftYaHei", "Arial", 0, 936},
-    {0xfcd19697, "BookmanOldStyle", "Arial", 0, 1252},
-    {0xfe209a82, "LucidaCalligraphy", "Arial", 0, 1252},
-    {0xfef135f8, "AdobeHeitiStd-Regular", "Batang,Century,Dotum", 0, 936},
+     "Bell MT,Corbel,Times New Roman,Cambria,Berlin Sans FB", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xcfe5755f, "Simhei", "Arial", 1, FX_CodePage::kChineseSimplified},
+    {0xd011f4ee, "MSPGothic", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xd060e7ef, "Vivaldi", "Arial", 8, FX_CodePage::kMSWin_WesternEuropean},
+    {0xd07edec1, "FranklinGothic-Medium", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xd107243f, "SimSun", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0xd1881562, "ArialNarrow", "Arial Narrow", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xd22b7dce, "BodoniMTPosterCompressed", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xd22bfa60, "ComicSansMS", "Arial", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xd3bd0e35, "Bauhaus93", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xd429ee7a, "STFangsong", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0xd6679c12, "BernardMTCondensed", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xd8e8a027, "LucidaSans", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xd9fe7761, "HighTowerText-Reg", "Arial", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xda7e551e, "STSong", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0xdaa6842d, "STZhongsong", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0xdaaab93f, "STFangsong", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0xdaeb0713, "STSong", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0xdafedbef, "STCaiyun", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0xdb00a3d9, "Broadway", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xdb1f5ad4, "STXinwei", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0xdb326e7f, "STKaiti", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0xdb69595a, "STHupo", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0xdba0082c, "STXihei", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0xdbd0ab18, "STXingkai", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0xdc1a7db1, "STLiti", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0xdc33075f, "KristenITC-Regular", "Arial", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xdcc7009c, "Harrington", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xdd712466, "ArialBlack", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xdde87b3e, "Impact", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xdf69fb32, "SnapITC", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xdf8b25e8, "CenturyGothic", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xe0f705c0, "KristenITC", "Arial", 8, FX_CodePage::kMSWin_WesternEuropean},
+    {0xe1427573, "Raavi", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xe2cea0cb, "Magneto", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xe36a9e17, "Ravie", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xe433f8e2, "Parchment", "Arial", 8, FX_CodePage::kMSWin_WesternEuropean},
+    {0xe43dff4a, "Wingdings", "Arial", 4, FX_CodePage::kSymbol},
+    {0xe4e2c405, "MTExtra", "Arial", 6, FX_CodePage::kSymbol},
+    {0xe618cc35, "InformalRoman", "Arial", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xe6c27ffc, "Mistral", "Arial", 8, FX_CodePage::kMSWin_WesternEuropean},
+    {0xe7ebf4b9, "Courier", "Courier New", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xe8bc4a9d, "MSReferenceSpecialty", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xe90fb013, "TempusSansITC", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xec637b42, "Consolas", "Verdana", 1, FX_CodePage::kMSWin_WesternEuropean},
+    {0xed3a683b, "STXinwei", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0xef264cd1, "LucidaHandwriting", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xf086bca2, "BaskervilleOldFace", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xf1028030, "Mangal", "Arial", 2, FX_CodePage::kMSWin_WesternEuropean},
+    {0xf1da7eb9, "ShowcardGothic", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xf210f06a, "ArialMT", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xf477f16a, "Latha", "Arial", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xf616f3dd, "LiSu", "Arial", 1, FX_CodePage::kChineseSimplified},
+    {0xfa479aa6, "MicrosoftYaHei", "Arial", 0, FX_CodePage::kChineseSimplified},
+    {0xfcd19697, "BookmanOldStyle", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xfe209a82, "LucidaCalligraphy", "Arial", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xfef135f8, "AdobeHeitiStd-Regular", "Batang,Century,Dotum", 0,
+     FX_CodePage::kChineseSimplified},
 };
-#elif _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
-const FGAS_FontInfo g_XFAFontsMap[] = {
-    {0x01d5d33e, "SimSun",
-     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
-     "PL UMing TW MBE",
-     0, 936},
-    {0x01e4f102, "YouYuan",
-     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
-     "PL UMing TW MBE",
-     1, 936},
-    {0x030549dc, "LiSu",
-     "WenQuanYi Zen Hei,WenQuanYi Zen Hei Sharp,WenQuanYi Zen Hei "
-     "Mono,WenQuanYi Micro Hei",
-     1, 936},
-    {0x032edd44, "Simhei",
-     "WenQuanYi Zen Hei,WenQuanYi Zen Hei Sharp,WenQuanYi Zen Hei "
-     "Mono,WenQuanYi Micro Hei",
-     1, 936},
-    {0x03eac6fc, "PoorRichard-Regular", "Droid Sans Japanese,FreeSerif", 2,
-     1252},
-    {0x03ed90e6, "Nina", "FreeSerif", 0, 1252},
-    {0x077b56b3, "KingsoftPhoneticPlain",
-     "Tibetan Machine Uni,LKLUG,Samyak Gujarati,Droid Sans Thai,Droid Sans "
-     "Armenian,Untitled1,utkal,Lohit Oriya",
-     0, 1252},
-    {0x078ed524, "MicrosoftSansSerif",
-     "Droid Sans Japanese,FreeSerif,WenQuanYi Micro Hei", 0, 1252},
-    {0x089b18a9, "Arial",
-     "Droid Sans Japanese,DejaVu Sans Condensed,FreeSerif,WenQuanYi Micro Hei",
-     0, 1252},
-    {0x0b2cad72, "MonotypeCorsiva", "Droid Sans Japanese,FreeSerif", 8, 1252},
-    {0x0bb003e7, "Kartika",
-     "FreeSans,Liberation Sans,Liberation Sans Narrow,Nimbus Sans "
-     "L,Garuda,FreeSerif,WenQuanYi Micro Hei",
-     2, 1252},
-    {0x0bb469df, "VinerHandITC",
-     "Droid Sans Japanese,Ubuntu,Liberation Sans,Liberation Serif", 8, 1252},
-    {0x0bc1a851, "SegoeUI", "Droid Sans Japanese,DejaVu Sans", 0, 1252},
-    {0x0c112ebd, "KozukaGothicPro-VIM", "FreeSerif", 0, 1252},
-    {0x0cfcb9c1, "AdobeThai", "Droid Sans Japanese,Waree", 0, 847},
-    {0x0e7de0f9, "Playbill",
-     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
-     "Sans Ethiopic,Droid Sans Japanese,FreeSerif",
-     0, 1252},
-    {0x0eff47c3, "STHupo", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
-     936},
-    {0x107ad374, "Constantia",
-     "Droid Sans Japanese,FreeSerif,WenQuanYi Micro Hei,Ubuntu", 2, 1252},
-    {0x12194c2d, "KunstlerScript", "Droid Sans Japanese,Liberation Serif", 8,
-     1252},
-    {0x135ef6a1, "MinionProSmBd", "Liberation Serif", 0, 1252},
-    {0x158c4049, "Garamond",
-     "Droid Sans Japanese,Liberation Serif,Ubuntu,FreeSerif", 2, 1252},
-    {0x160ecb24, "STZhongsong",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     0, 936},
-    {0x161ed07e, "MSGothic",
-     "WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,AR PL "
-     "UMing CN,AR PL UMing HK,AR PL UMing TW",
-     1, 1252},
-    {0x171d1ed1, "SnapITC-Regular",
-     "Liberation Sans Narrow,Ubuntu Condensed,Nimbus Sans L,DejaVu Sans", 0,
-     1252},
-    {0x18d1188f, "Cambria", "Droid Sans Japanese,FreeSerif,FreeMono", 2, 1252},
-    {0x18eaf350, "ArialUnicodeMS",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     0, 936},
-    {0x1a92d115, "MingLiU",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     1, 1252},
-    {0x1cc217c6, "TrebuchetMS",
-     "Droid Sans Japanese,Liberation Serif,FreeSerif,Ubuntu", 0, 1252},
-    {0x1d649596, "BasemicTimes",
-     "Liberation Serif,Times New Roman,Droid Sans Japanese,FreeSerif,Ubuntu", 0,
-     1252},
-    {0x1e34ee60, "BellMT",
-     "KacstQurn,Droid Sans Japanese,Ubuntu,Liberation Serif", 2, 1252},
-    {0x1eb36945, "CooperBlack",
-     "KacstQurn,Droid Sans Japanese,FreeMono,Liberation Mono, WenQuanYi Micro "
-     "Hei Mono",
-     2, 1252},
-    {0x1ef7787d, "BatangChe",
-     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing "
-     "TW,WenQuanYi Zen Hei,WenQuanYi Micro Hei",
-     1, 1252},
-    {0x20b3bd3a, "BrushScriptMT",
-     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,Droid Sans "
-     "Japanese,URW Chancery L,Liberation Sans",
-     8, 1252},
-    {0x220877aa, "Candara", "Droid Sans Japanese,DejaVu Sans", 0, 1252},
-    {0x22135007, "FreestyleScript-Regular",
-     "KacstQurn,Droid Sans Japanese,Liberation Sans", 8, 1252},
-    {0x251059c3, "Chiller",
-     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,Droid Sans "
-     "Japanese,Liberation Sans",
-     0, 1252},
-    {0x25bed6dd, "MSReferenceSansSerif",
-     "DejaVu Sans Condensed,Ubuntu Condensed,Droid Sans Japanese,AR PL UKai "
-     "HK",
-     0, 1252},
-    {0x28154c81, "Parchment-Regular", "Droid Sans Japanese,Liberation Sans", 8,
-     1252},
-    {0x29711eb9, "STLiti", "AR PL UKai HK", 0, 936},
-    {0x2b1993b4, "Basemic",
-     "Liberation Serif,Droid Sans Japanese,Liberation Sans", 0, 1252},
-    {0x2b316339, "NiagaraSolid-Reg", "Droid Sans Japanese,Liberation Sans", 0,
-     1252},
-    {0x2c147529, "FootlightMTLight",
-     "KacstQurn,Droid Sans Japanese,Liberation Sans", 0, 1252},
-    {0x2c198928, "HarlowSolid", "KacstQurn,Droid Sans Japanese,Liberation Sans",
-     0, 1252},
-    {0x2c6ac6b2, "LucidaBright",
-     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
-     "Sans Japanese,Liberation Sans",
-     2, 1252},
-    {0x2c9f38e2, "KozukaMinchoPro-VIR", "DejaVu Sans", 0, 1252},
-    {0x2d5a47b0, "STCaiyun", "AR PL UKai HK", 0, 936},
-    {0x2def26bf, "BernardMT-Condensed",
-     "KacstQurn,Droid Sans Japanese,DejaVu Serif", 0, 1252},
-    {0x2fd8930b, "KozukaMinchoPr6NR", "DejaVu Serif", 0, 1252},
-    {0x3115525a, "FangSong_GB2312",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     0, 1252},
-    {0x31327817, "MyriadPro", "Ubuntu Condensed,Droid Sans Japanese, FreeSerif",
-     0, 1252},
-    {0x32244975, "Helvetica",
-     "Ubuntu,DejaVu Sans Condensed,Liberation Sans,Liberation Sans "
-     "Narrow,Nimbus Sans ",
-     0, 1252},
-    {0x32ac995c, "Terminal", "DejaVu Serif", 0, 1252},
-    {0x338d648a, "NiagaraEngraved-Reg", "Droid Sans Japanese,DejaVu Serif", 0,
-     1252},
-    {0x33bb65f2, "Sylfaen", "Droid Sans Japanese,DejaVu Sans", 2, 1252},
-    {0x3402c30e, "MSPMincho",
-     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 2,
-     1252},
-    {0x3412bf31, "SimSun-PUA",
-     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing CN,AR PL UMing HK", 0,
-     936},
-    {0x36eb39b9, "BerlinSansFB",
-     "Droid Sans Japanese,Liberation Serif,Ubuntu,FreeSerif", 0, 1252},
-    {0x36f42055, "UniversATT", "Microsoft Sans Serif", 0, 1252},
-    {0x3864c4f6, "HighTowerText", "Droid Sans Japanese,DejaVu Serif", 2, 1252},
-    {0x3a257d03, "FangSong_GB2312", "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei",
-     0, 1252},
-    {0x3c7d1d07, "Garamond3LTStd",
-     "Droid Sans Japanese,Ubuntu Condensed,DejaVu Sans Condensed,Liberation "
-     "Serif,Ubuntu,FreeSerif",
-     2, 1252},
-    {0x3cdae668, "FreestyleScript", "KacstQurn,Droid Sans Japanese,DejaVu Sans",
-     8, 1252},
-    {0x3d55aed7, "Jokerman", "Droid Sans Japanese,DejaVu Sans", 0, 1252},
-    {0x3d5b4385, "PMingLiU",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     2, 1252},
-    {0x3d9b7669, "EstrangeloEdessa", "Droid Sans Japanese,DejaVu Sans", 0,
-     1252},
-    {0x3e532d74, "FranklinGothicMedium", "Droid Sans Japanese,Ubuntu", 0, 1252},
-    {0x3e6aa32d, "NSimSun",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     1, 936},
-    {0x3f6c36a8, "Gautami",
-     "Droid Arabic Naskh,Droid Sans Ethiopic, mry_KacstQurn,Droid Sans "
-     "Japanese,FreeSans",
-     0, 1252},
-    {0x3ff32662, "Chiller-Regular",
-     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,FreeSans", 0, 1252},
-    {0x409de312, "ModernNo.20",
-     "KacstQurn,Droid Sans Japanese,Nimbus Sans L,Nimbus Sans L,FreeSans", 2,
-     1252},
-    {0x41443c5e, "Georgia", "Droid Sans Japanese,FreeSans", 2, 1252},
-    {0x4160ade5, "BellGothicStdBlack", "FreeSans", 0, 1252},
-    {0x421976c4, "Modern-Regular", "FreeSans", 2, 1252},
-    {0x422a7252, "Stencil", "Droid Sans Japanese,FreeSans,Liberation Sans", 0,
-     1252},
-    {0x42c8554f, "Fixedsys", "FreeSerif", 0, 1252},
-    {0x435cb41d, "Roman", "FreeSerif", 0, 1252},
-    {0x47882383, "CourierNew",
-     "FreeMono,WenQuanYi Micro Hei Mono,AR PL UKai CN,AR PL UKai HK,AR PL "
-     "UKai TW,AR PL UKai TW MBE,DejaVu Sans",
-     1, 1252},
-    {0x480a2338, "BerlinSansFBDemi", "Droid Sans Japanese,Liberation Serif", 0,
-     1252},
-    {0x480bf7a4, "CourierStd", "DejaVu Sans", 0, 1252},
-    {0x481ad6ed, "VladimirScript", "Droid Sans Japanese,DejaVu Serif", 8, 1252},
-    {0x4911577a, "YouYuan",
-     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 1,
-     936},
-    {0x4a788d72, "STXingkai", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
-     936},
-    {0x4bf88566, "SegoeCondensed", "FreeSerif", 0, 1252},
-    {0x4ccf51a4, "BerlinSansFB-Reg", "Droid Sans Japanese,Liberation Serif", 0,
-     1252},
-    {0x4ea967ce, "GulimChe",
-     "WenQuanYi Zen Hei Mono,AR PL UKai CN,AR PL UKai HK,AR PL UKai TW,AR PL "
-     "UKai TW MBE",
-     1, 1252},
-    {0x4f68bd79, "LetterGothicStd",
-     "FreeMono,Liberation Mono,Andale Mono,WenQuanYi Micro Hei Mono", 0, 1252},
-    {0x51a0d0e6, "KozukaGothicPr6NM", "FreeSerif", 0, 1252},
-    {0x531b3dea, "BasemicSymbol", "FreeSerif", 0, 1252},
-    {0x5333fd39, "CalifornianFB-Reg",
-     "Droid Sans Japanese,URW Chancery L,FreeSerif", 2, 1252},
-    {0x53561a54, "FZYTK--GBK1-0",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     0, 936},
-    {0x55e0dde6, "LucidaSansTypewriter",
-     "Ubuntu Mono,DejaVu Sans Mono,Nimbus Mono L,Liberation Mono,Courier 10 "
-     "Pitch,FreeMono",
-     0, 1252},
-    {0x574d4d3d, "AdobeArabic", "Droid Sans Japanese,DejaVu Sans", 0, 1252},
-    {0x5792e759, "STKaiti", "WenQuanYi Micro Hei Mono", 0, 936},
-    {0x5921978e, "LucidaSansUnicode", "Droid Sans Japanese,DejaVu Sans", 0,
-     1252},
-    {0x594e2da4, "Vrinda",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Arabic "
-     "Naskh,mry_KacstQurn,Droid Sans Japanese,FreeSans,FreeSerif",
-     0, 1252},
-    {0x59baa9a2, "KaiTi_GB2312",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     0, 1252},
-    {0x5cfedf4f, "BaskOldFace",
-     "KacstQurn,Droid Sans Japanese,Ubuntu,Liberation Serif", 0, 1252},
-    {0x5e16ac91, "TrajanPro",
-     "Nimbus Sans L,AR PL UMing HK,AR PL UKai HK,AR PL UMing TW,AR PL UMing "
-     "TW MBE,DejaVu Sans,DejaVu Serif",
-     0, 1252},
-    {0x5f388196, "ITCLegacySansStdMedium",
-     "Liberation Serif,FreeSerif,FreeSans,Ubuntu", 0, 1252},
-    {0x5f97921c, "AdobeMyungjoStdM",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     0, 936},
-    {0x5fefbfad, "Batang",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     2, 1252},
-    {0x605342b9, "DotumChe",
-     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 1,
-     1252},
-    {0x608c5f9a, "KaiTi_GB2312",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     0, 936},
-    {0x61efd0d1, "MaturaMTScriptCapitals",
-     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
-     "Sans Japanese,DejaVu Serif,DejaVu Sans",
-     0, 1252},
-    {0x626608a9, "MVBoli",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
-     "Ethiopic,Droid Sans Japanese,DejaVu Sans",
-     0, 1252},
-    {0x630501a3, "SmallFonts", "DejaVu Serif", 0, 1252},
-    {0x65d0e2a9, "FZYTK--GBK1-0",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     0, 936},
-    {0x669f29e1, "FZSTK--GBK1-0",
-     "AR PL UMing CN,AR PL UKai CN, AR PL UMing HK", 0, 936},
-    {0x673a9e5f, "Tunga",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
-     "Japanese,DejaVu Serif",
-     0, 1252},
-    {0x691aa4ce, "NiagaraSolid", "Droid Sans Japanese,DejaVu Serif", 0, 1252},
-    {0x696259b7, "Corbel", "Droid Sans Japanese,DejaVu Sans", 0, 1252},
-    {0x696ee9be, "STXihei", "WenQuanYi Micro Hei Mono", 0, 936},
-    {0x6c59cf69, "Dotum", "WenQuanYi Zen Hei Mono", 0, 1252},
-    {0x707fa561, "Gungsuh", "WenQuanYi Zen Hei Mono", 2, 1252},
-    {0x71416bb2, "ZWAdobeF",
-     "KacstArt,KacstBookm,KacstDecorative,KacstDigital,KacstFarsi,KacstLetter,"
-     "KacstOffice,Dingbats,FreeSerif",
-     0, 1252},
-    {0x71b41801, "Verdana",
-     "DejaVu Sans Condensed,Ubuntu Condensed,Droid Sans Japanese,DejaVu Sans",
-     0, 1252},
-    {0x73f25e4c, "PalatinoLinotype", "Droid Sans Japanese,FreeSerif", 0, 1252},
-    {0x73f4d19f, "NiagaraEngraved", "Droid Sans Japanese,FreeSerif", 0, 1252},
-    {0x74001694, "MyriadProBlack", "Droid Sans Japanese,AR PL UKai HK", 0,
-     1252},
-    {0x74b14d8f, "Haettenschweiler", "Droid Sans Japanese,DejaVu Serif", 0,
-     1252},
-    {0x74cb44ee, "NSimSun", "WenQuanYi Zen Hei Mono", 1, 936},
-    {0x76b4d7ff, "Shruti",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
-     "Japanese,FreeSans",
-     0, 1252},
-    {0x788b3533, "Webdings", "FreeSans", 6, 42},
-    {0x797dde99, "MSSerif", "FreeSans", 0, 1252},
-    {0x7a0f9e9e, "MSMincho",
-     "WenQuanYi Micro Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 1,
-     1252},
-    {0x7b439caf, "OldEnglishTextMT",
-     "Droid Sans Japanese,Liberation Sans,Ubuntu", 0, 1252},
-    {0x8213a433, "LucidaSans-Typewriter",
-     "Ubuntu Mono,Liberation Mono,DejaVu Sans Mono", 0, 1252},
-    {0x82fec929, "AdobeSongStd",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     0, 936},
-    {0x83581825, "Modern", "FreeSans", 0, 1252},
-    {0x835a2823, "Algerian",
-     "KacstQurn,Droid Sans Japanese,FreeSans,Liberation Sans,Ubuntu", 0, 1252},
-    {0x83dab9f5, "Script", "FreeSans", 0, 1252},
-    {0x847b56da, "Tahoma",
-     "Droid Sans Japanese,DejaVu Sans Condensed,FreeSerif", 0, 1252},
-    {0x8a783cb2, "SimSun-PUA",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     0, 1252},
-    {0x8b5cac0e, "Onyx", "Droid Sans Japanese,Liberation Sans", 0, 1252},
-    {0x8c6a499e, "Gulim",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     0, 1252},
-    {0x8e0af790, "JuiceITC", "Droid Sans Japanese,Liberation Sans", 0, 1252},
-    {0x8e8d43b2, "Centaur",
-     "KacstQurn,Droid Sans Japanese,Khmer OS,Khmer OS System", 2, 1252},
-    {0x8ee4dcca, "BookshelfSymbol7", "Liberation Sans", 0, 1252},
-    {0x90794800, "BellGothicStdLight", "Liberation Sans", 0, 1252},
-    {0x909b516a, "Century",
-     "Droid Sans Japanese,Liberation Sans,Liberation Mono,Liberation Serif", 2,
-     1252},
-    {0x92ae370d, "MSOutlook", "Liberation Sans", 4, 42},
-    {0x93c9fbf1, "LucidaFax",
-     "KacstQurn,Droid Arabic Naskh,Droid Sans "
-     "Ethiopic,mry_KacstQurn,Liberation Sans",
-     2, 1252},
-    {0x9565085e, "BookAntiqua",
-     "Droid Sans Japanese,Liberation Sans,Liberation Serif", 2, 1252},
-    {0x9856d95d, "AdobeMingStd", "AR PL UMing HK", 0, 949},
-    {0x9bbadd6b, "ColonnaMT",
-     "KacstQurn,Droid Sans Japanese,Khmer OS,Khmer OS System", 0, 1252},
-    {0x9cbd16a4, "ShowcardGothic-Reg",
-     "Droid Sans Japanese,Liberation Sans,Ubuntu", 0, 1252},
-    {0x9d73008e, "MSSansSerif", "FreeSerif", 0, 1252},
-    {0xa0607db1, "GungsuhChe",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     1, 1252},
-    {0xa0bcf6a1, "LatinWide", "FreeSerif", 2, 1252},
-    {0xa1429b36, "Symbol", "FreeSerif", 6, 42},
-    {0xa1fa5abc, "Wingdings2", "FreeSerif", 6, 42},
-    {0xa1fa5abd, "Wingdings3", "FreeSerif", 6, 42},
-    {0xa427bad4, "InformalRoman-Regular",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
-     "Japanese,FreeSerif",
-     8, 1252},
-    {0xa8b92ece, "FZSTK--GBK1-0", "AR PL UMing CN", 0, 936},
-    {0xa8d83ece, "CalifornianFB", "Droid Sans Japanese,FreeSerif", 2, 1252},
-    {0xaa3e082c, "Kingsoft-Phonetic",
-     "Tibetan Machine Uni,LKLUG,Samyak Gujarati,Droid Sans "
-     "Thai,utkal,Kedage,Mallige,AR PL UKai CN",
-     0, 1252},
-    {0xaa6bcabe, "HarlowSolidItalic",
-     "KacstQurn,Droid Sans Japanese,Liberation Serif", 0, 1252},
-    {0xade5337c, "MSUIGothic",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     0, 1252},
-    {0xb08dd941, "WideLatin",
-     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
-     "Sans Japanese,Liberation Serif",
-     2, 1252},
-    {0xb12765e0, "ITCLegacySansStdBook",
-     "AR PL UMing HK,AR PL UKai HK,FreeSerif,Ubuntu,FreeSans", 0, 1252},
-    {0xb207f05d, "PoorRichard", "Droid Sans Japanese,Liberation Serif", 2,
-     1252},
-    {0xb3bc492f, "JuiceITC-Regular", "Droid Sans Japanese,Liberation Serif", 0,
-     1252},
-    {0xb5545399, "Marlett", "Liberation Serif", 4, 42},
-    {0xb5dd1ebb, "BritannicBold",
-     "KacstQurn,Droid Arabic Naskh,Droid Sans "
-     "Ethiopic,mry_KacstQurn,Liberation Serif",
-     0, 1252},
-    {0xb699c1c5, "LucidaCalligraphy-Italic",
-     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
-     "Sans Japanese,DejaVu Serif",
-     0, 1252},
-    {0xb725d629, "TimesNewRoman", "Droid Sans Japanese,Liberation Sans", 2,
-     1252},
-    {0xb7eaebeb, "AdobeHeitiStdR",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     0, 936},
-    {0xbd29c486, "BerlinSansFBDemi-Bold", "Droid Sans Japanese,DejaVu Serif", 0,
-     1252},
-    {0xbe8a8db4, "BookshelfSymbolSeven", "DejaVu Sans", 0, 1252},
-    {0xc16c0118, "AdobeHebrew", "Droid Sans Japanese,Ubuntu,Liberation Serif",
-     0, 1252},
-    {0xc318b0af, "MyriadProLight",
-     "Droid Sans Japanese,AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0, 1252},
-    {0xc65e5659, "CambriaMath", "Droid Sans Japanese,FreeSerif,FreeMono", 2,
-     1252},
-    {0xc75c8f05, "LucidaConsole",
-     "Liberation Mono,DejaVu Sans Mono,FreeMono,WenQuanYi Micro Hei Mono", 1,
-     1252},
-    {0xca7c35d6, "Calibri", "Droid Sans Japanese,DejaVu Sans", 0, 1252},
-    {0xcb053f53, "MicrosoftYaHei",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     0, 936},
-    {0xcb7190f9, "Magneto-Bold",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
-     "Japanese,DejaVu Serif",
-     0, 1252},
-    {0xcca00cc5, "System", "DejaVu Sans", 0, 1252},
-    {0xccad6f76, "Jokerman-Regular", "Droid Sans Japanese,DejaVu Sans", 0,
-     1252},
-    {0xccc5818c, "EuroSign", "DejaVu Serif", 0, 1252},
-    {0xcf3d7234, "LucidaHandwriting-Italic",
-     "Liberation Sans Narrow,Ubuntu Condensed,Nimbus Sans L,DejaVu Serif", 0,
-     1252},
-    {0xcf7b8fdb, "MinionPro", "DejaVu Sans", 0, 1252},
-    {0xcfe5755f, "Simhei",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     1, 936},
-    {0xd011f4ee, "MSPGothic",
-     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 0,
-     1252},
-    {0xd060e7ef, "Vivaldi",
-     "KacstQurn,Droid Sans Japanese,Liberation Sans,Ubuntu", 8, 1252},
-    {0xd07edec1, "FranklinGothic-Medium", "Droid Sans Japanese,Ubuntu", 0,
-     1252},
-    {0xd107243f, "SimSun", "WenQuanYi Zen Hei Mono", 0, 936},
-    {0xd1881562, "ArialNarrow",
-     "Liberation Sans Narrow,Droid Sans Japanese,FreeSerif", 0, 1252},
-    {0xd22b7dce, "BodoniMTPosterCompressed", "Droid Sans Japanese,DejaVu Serif",
-     0, 1252},
-    {0xd22bfa60, "ComicSansMS", "Droid Sans Japanese,FreeMono,Liberation Mono",
-     8, 1252},
-    {0xd3bd0e35, "Bauhaus93",
-     "KacstQurn,Droid Sans Japanese,Liberation Sans,Ubuntu", 0, 1252},
-    {0xd429ee7a, "STFangsong", "WenQuanYi Micro Hei Mono", 0, 936},
-    {0xd6679c12, "BernardMTCondensed",
-     "KacstQurn,Droid Sans Japanese,Nimbus Sans L,URW Chancery "
-     "L,KacstOne,Liberation Sans",
-     0, 1252},
-    {0xd8e8a027, "LucidaSans",
-     "Liberation Sans Narrow,Nimbus Sans L,KacstQurn,Droid Arabic Naskh,Droid "
-     "Sans Ethiopic,DejaVu Serif Condensed,Liberation Mono,Ubuntu",
-     0, 1252},
-    {0xd9fe7761, "HighTowerText-Reg",
-     "Droid Sans Japanese,Ubuntu,Liberation Serif", 2, 1252},
-    {0xda7e551e, "STSong", "WenQuanYi Micro Hei Mono", 0, 936},
-    {0xdaa6842d, "STZhongsong",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     0, 936},
-    {0xdaaab93f, "STFangsong",
-     "WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
-     "Hei,WenQuanYi Zen Hei Sharp",
-     0, 936},
-    {0xdaeb0713, "STSong",
-     "WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
-     "Hei,WenQuanYi Zen Hei Sharp",
-     0, 936},
-    {0xdafedbef, "STCaiyun", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
-     936},
-    {0xdb00a3d9, "Broadway",
-     "KacstQurn,Droid Sans Japanese,DejaVu Sans,FreeMono,Liberation Mono", 0,
-     1252},
-    {0xdb1f5ad4, "STXinwei", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
-     936},
-    {0xdb326e7f, "STKaiti",
-     "WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
-     "Hei,WenQuanYi Zen Hei Sharp",
-     0, 936},
-    {0xdb69595a, "STHupo",
-     "WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
-     "Hei,WenQuanYi Zen Hei Sharp",
-     0, 936},
-    {0xdba0082c, "STXihei",
-     " WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
-     "Hei,WenQuanYi Zen Hei Sharp",
-     0, 936},
-    {0xdbd0ab18, "STXingkai", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
-     936},
-    {0xdc1a7db1, "STLiti", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
-     936},
-    {0xdc33075f, "KristenITC-Regular",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,DejaVu Sans "
-     "Condensed,Ubuntu,Liberation Sans",
-     8, 1252},
-    {0xdcc7009c, "Harrington",
-     "KacstQurn,Droid Sans Japanese,Liberation Serif,FreeSerif,Ubuntu", 0,
-     1252},
-    {0xdd712466, "ArialBlack",
-     "Droid Sans Japanese,DejaVu Sans,DejaVu Serif,FreeMono", 0, 1252},
-    {0xdde87b3e, "Impact", "Droid Sans Japanese,DejaVu Serif", 0, 1252},
-    {0xdf69fb32, "SnapITC",
-     "Liberation Sans Narrow,Ubuntu Condensed,DejaVu Sans,DejaVu "
-     "Serif,FreeMono",
-     0, 1252},
-    {0xdf8b25e8, "CenturyGothic",
-     "Droid Sans Japanese,Liberation Mono,Liberation Sans,Liberation Serif", 0,
-     1252},
-    {0xe0f705c0, "KristenITC",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,DejaVu Sans "
-     "Condensed,Ubuntu,Liberation Sans",
-     8, 1252},
-    {0xe1427573, "Raavi",
-     "Droid Arabic Naskh,Droid Sans "
-     "Ethiopic,mry_KacstQurn,FreeSerif,Liberation Serif,Khmer OS",
-     0, 1252},
-    {0xe2cea0cb, "Magneto",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,DejaVu "
-     "Serif,DejaVu Serif Condensed,DejaVu Sans",
-     0, 1252},
-    {0xe36a9e17, "Ravie",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,DejaVu "
-     "Serif,DejaVu Sans,FreeMono",
-     0, 1252},
-    {0xe433f8e2, "Parchment", "Droid Sans Japanese,DejaVu Serif", 8, 1252},
-    {0xe43dff4a, "Wingdings", "DejaVu Serif", 4, 42},
-    {0xe4e2c405, "MTExtra", "DejaVu Serif", 6, 42},
-    {0xe618cc35, "InformalRoman",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
-     "Japanese,Nimbus Sans L,DejaVu Sans Condensed,Ubuntu,Liberation Sans",
-     8, 1252},
-    {0xe6c27ffc, "Mistral", "Droid Sans Japanese,DejaVu Serif", 8, 1252},
-    {0xe7ebf4b9, "Courier", "DejaVu Sans,DejaVu Sans Condensed,FreeSerif", 0,
-     1252},
-    {0xe8bc4a9d, "MSReferenceSpecialty", "DejaVu Serif", 0, 1252},
-    {0xe90fb013, "TempusSansITC",
-     "Droid Sans Japanese,Ubuntu,Liberation Serif,FreeSerif", 0, 1252},
-    {0xec637b42, "Consolas",
-     "DejaVu Sans Condensed,AR PL UKai CN,AR PL UKai HK,AR PL UKai "
-     "TW,FreeSerif,FreeSans",
-     1, 1252},
-    {0xed3a683b, "STXinwei", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
-     936},
-    {0xef264cd1, "LucidaHandwriting",
-     "Liberation Sans Narrow,Ubuntu Condensed,Nimbus Sans "
-     "L,KacstQurn,Liberation Mono",
-     0, 1252},
-    {0xf086bca2, "BaskervilleOldFace",
-     "KacstQurn,Droid Sans Japanese,Liberation Serif,Ubuntu,FreeSerif", 0,
-     1252},
-    {0xf1028030, "Mangal",
-     "FreeSans,TSCu_Paranar,Garuda,Liberation Sans,Liberation Sans "
-     "Narrow,Nimbus Sans ",
-     2, 1252},
-    {0xf1da7eb9, "ShowcardGothic",
-     "Droid Sans Japanese,DejaVu Serif Condensed,DejaVu Sans "
-     "Condensed,Liberation Sans,Ubuntu",
-     0, 1252},
-    {0xf210f06a, "ArialMT",
-     "Liberation Sans,Liberation Sans Narrow,FreeSans,Nimbus Sans L,Khmer OS "
-     "System,Khmer OS",
-     0, 1252},
-    {0xf477f16a, "Latha",
-     "Liberation Sans Narrow,Nimbus Sans L,Droid Arabic "
-     "Naskh,mry_KacstQurn,FreeSerif,Nimbus Sans ",
-     0, 1252},
-    {0xf616f3dd, "LiSu",
-     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
-     "PL UMing TW MBE",
-     1, 936},
-    {0xfa479aa6, "MicrosoftYaHei",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     0, 936},
-    {0xfcd19697, "BookmanOldStyle",
-     "Droid Sans Japanese,Liberation Mono,Liberation Sans,Liberation Serif", 0,
-     1252},
-    {0xfe209a82, "LucidaCalligraphy",
-     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
-     "Sans Japanese,DejaVu Serif,DejaVu Sans,FreeMono",
-     0, 1252},
-    {0xfef135f8, "AdobeHeitiStd-Regular",
-     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     "Sharp,WenQuanYi Micro Hei",
-     0, 936},
-};
-#elif defined(OS_MACOSX)
-const FGAS_FontInfo g_XFAFontsMap[] = {
-    {0x01d5d33e, "SimSun", "STHeiti,Heiti TC,STFangsong", 0, 936},
-    {0x01e4f102, "YouYuan", "STHeiti,Heiti TC,STFangsong", 1, 936},
-    {0x030549dc, "LiSu", "STHeiti,Heiti TC,STFangsong", 1, 936},
-    {0x032edd44, "Simhei", "STHeiti,Heiti TC,STFangsong", 1, 936},
+#elif BUILDFLAG(IS_APPLE)
+const FGAS_FontInfo kXFAFontsMap[] = {
+    {0x01d5d33e, "SimSun", "STHeiti,Heiti TC,STFangsong", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x01e4f102, "YouYuan", "STHeiti,Heiti TC,STFangsong", 1,
+     FX_CodePage::kChineseSimplified},
+    {0x030549dc, "LiSu", "STHeiti,Heiti TC,STFangsong", 1,
+     FX_CodePage::kChineseSimplified},
+    {0x032edd44, "Simhei", "STHeiti,Heiti TC,STFangsong", 1,
+     FX_CodePage::kChineseSimplified},
     {0x03eac6fc, "PoorRichard-Regular",
-     "Noteworthy,Avenir Next Condensed,Impact", 2, 1252},
-    {0x03ed90e6, "Nina", "Microsoft Sans Serif", 0, 1252},
+     "Noteworthy,Avenir Next Condensed,Impact", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x03ed90e6, "Nina", "Microsoft Sans Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x077b56b3, "KingsoftPhoneticPlain",
      "LastResort,Apple "
      "Chancery,STIXVariants,STIXSizeOneSym,STIXSizeOneSym,Apple Braille",
-     0, 1252},
-    {0x078ed524, "MicrosoftSansSerif", "Songti SC,Apple Symbols", 0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x078ed524, "MicrosoftSansSerif", "Songti SC,Apple Symbols", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x089b18a9, "Arial", "Arial Unicode MS,Microsoft Sans Serif,Apple Symbols",
-     0, 1252},
-    {0x0b2cad72, "MonotypeCorsiva", "Arial Narrow,Impact", 8, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x0b2cad72, "MonotypeCorsiva", "Arial Narrow,Impact", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x0bb003e7, "Kartika",
-     "Arial Unicode MS,Microsoft Sans Serif,Arial Narrow,Damascus", 2, 1252},
-    {0x0bb469df, "VinerHandITC", "Comic Sans MS,Songti SC,STSong", 8, 1252},
-    {0x0bc1a851, "SegoeUI", "Apple Symbols", 0, 1252},
+     "Arial Unicode MS,Microsoft Sans Serif,Arial Narrow,Damascus", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x0bb469df, "VinerHandITC", "Comic Sans MS,Songti SC,STSong", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x0bc1a851, "SegoeUI", "Apple Symbols", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x0c112ebd, "KozukaGothicPro-VIM", "Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
-    {0x0cfcb9c1, "AdobeThai", "Avenir Next Condensed Ultra Light", 0, 847},
-    {0x0e7de0f9, "Playbill", "STIXNonUnicode", 0, 1252},
-    {0x0eff47c3, "STHupo", "Kaiti SC,Songti SC,STHeiti", 0, 936},
+     FX_CodePage::kMSWin_WesternEuropean},
+    // Was 847 (not defined), presumably 874 (Thai).
+    {0x0cfcb9c1, "AdobeThai", "Avenir Next Condensed Ultra Light", 0,
+     FX_CodePage::kMSDOS_Thai},
+    {0x0e7de0f9, "Playbill", "STIXNonUnicode", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x0eff47c3, "STHupo", "Kaiti SC,Songti SC,STHeiti", 0,
+     FX_CodePage::kChineseSimplified},
     {0x107ad374, "Constantia", "Arial Unicode MS,Palatino,Baskerville", 2,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x12194c2d, "KunstlerScript",
-     "Avenir Next Condensed Demi Bold,Arial Narrow", 8, 1252},
+     "Avenir Next Condensed Demi Bold,Arial Narrow", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x135ef6a1, "MinionProSmBd", "Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
-    {0x158c4049, "Garamond", "Impact,Arial Narrow", 2, 1252},
-    {0x160ecb24, "STZhongsong", "STFangsong,Songti SC", 0, 936},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x158c4049, "Garamond", "Impact,Arial Narrow", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x160ecb24, "STZhongsong", "STFangsong,Songti SC", 0,
+     FX_CodePage::kChineseSimplified},
     {0x161ed07e, "MSGothic",
      "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing "
      "TW,Microsoft Sans Serif,Apple Symbols",
-     1, 1252},
-    {0x171d1ed1, "SnapITC-Regular", "STHeiti,Arial Black", 0, 1252},
-    {0x18d1188f, "Cambria", "Arial Unicode MS", 2, 1252},
+     1, FX_CodePage::kMSWin_WesternEuropean},
+    {0x171d1ed1, "SnapITC-Regular", "STHeiti,Arial Black", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x18d1188f, "Cambria", "Arial Unicode MS", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x18eaf350, "ArialUnicodeMS", "Microsoft Sans Serif,Apple Symbols", 0,
-     936},
-    {0x1a92d115, "MingLiU", "Heiti SC,STHeiti", 1, 1252},
-    {0x1cc217c6, "TrebuchetMS", "Damascus,Impact,Arial Narrow", 0, 1252},
+     FX_CodePage::kChineseSimplified},
+    {0x1a92d115, "MingLiU", "Heiti SC,STHeiti", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x1cc217c6, "TrebuchetMS", "Damascus,Impact,Arial Narrow", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x1d649596, "BasemicTimes", "Liberation Serif,Impact,Arial Narrow", 0,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x1e34ee60, "BellMT",
-     "Papyrus,STIXNonUnicode,Microsoft Sans Serif,Avenir Light", 2, 1252},
+     "Papyrus,STIXNonUnicode,Microsoft Sans Serif,Avenir Light", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x1eb36945, "CooperBlack",
-     "Marion,STIXNonUnicode,Arial Rounded MT Bold,Lucida Grande", 2, 1252},
+     "Marion,STIXNonUnicode,Arial Rounded MT Bold,Lucida Grande", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x1ef7787d, "BatangChe",
      "WenQuanYi Zen Hei Mono,AR PL UMing CN,,AR PL UMing HK,AR PL UMing TW,AR "
      "PL UMing TW MBE,Arial Unicode MS,Heiti TC",
-     1, 1252},
+     1, FX_CodePage::kMSWin_WesternEuropean},
     {0x20b3bd3a, "BrushScriptMT",
      "STIXNonUnicode,Damascus,Arial Narrow,Avenir Next Condensed,Cochin", 8,
-     1252},
-    {0x220877aa, "Candara", "Cochin,Baskerville,Marion", 0, 1252},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x220877aa, "Candara", "Cochin,Baskerville,Marion", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x22135007, "FreestyleScript-Regular",
-     "STIXNonUnicode,Nadeem,Zapf Dingbats", 8, 1252},
+     "STIXNonUnicode,Nadeem,Zapf Dingbats", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x251059c3, "Chiller",
      "Zapf Dingbats,Damascus,STIXNonUnicode,Papyrus,KufiStandardGK,Baghdad", 0,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x25bed6dd, "MSReferenceSansSerif",
      "Tahoma,Apple Symbols,Apple LiGothic,Arial Unicode MS,Lucida "
      "Grande,Microsoft Sans Serif",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0x28154c81, "Parchment-Regular", "Microsoft Sans Serif,Apple Symbols", 8,
-     1252},
-    {0x29711eb9, "STLiti", "Kaiti SC,Songti SC", 0, 936},
-    {0x2b1993b4, "Basemic", "Impact,Arial Narrow", 0, 1252},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x29711eb9, "STLiti", "Kaiti SC,Songti SC", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x2b1993b4, "Basemic", "Impact,Arial Narrow", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x2b316339, "NiagaraSolid-Reg", "Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x2c147529, "FootlightMTLight",
-     "STIXNonUnicode,Avenir Next Condensed Heavy,PT Sans,Noteworthy", 0, 1252},
+     "STIXNonUnicode,Avenir Next Condensed Heavy,PT Sans,Noteworthy", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x2c198928, "HarlowSolid",
-     "Avenir Medium,Avenir Next Medium,Arial Unicode MS", 0, 1252},
+     "Avenir Medium,Avenir Next Medium,Arial Unicode MS", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x2c6ac6b2, "LucidaBright",
      "PT Sans Narrow,Papyrus,Damascus,STIXNonUnicode,Arial Rounded MT "
      "Bold,Comic Sans MS,Avenir Next",
-     2, 1252},
+     2, FX_CodePage::kMSWin_WesternEuropean},
     {0x2c9f38e2, "KozukaMinchoPro-VIR", "Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
-    {0x2d5a47b0, "STCaiyun", "Kaiti SC,Songti SC", 0, 936},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x2d5a47b0, "STCaiyun", "Kaiti SC,Songti SC", 0,
+     FX_CodePage::kChineseSimplified},
     {0x2def26bf, "BernardMT-Condensed",
-     "Impact,Avenir Next Condensed Demi Bold,American Typewriter", 0, 1252},
+     "Impact,Avenir Next Condensed Demi Bold,American Typewriter", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x2fd8930b, "KozukaMinchoPr6NR", "Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
-    {0x3115525a, "FangSong_GB2312", "Hiragino Sans GB,STHeiti", 0, 1252},
-    {0x31327817, "MyriadPro", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3115525a, "FangSong_GB2312", "Hiragino Sans GB,STHeiti", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x31327817, "MyriadPro", "Microsoft Sans Serif,Apple Symbols", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x32244975, "Helvetica",
-     "Arial Narrow,Arial Unicode MS,Damascus,STIXNonUnicode", 0, 1252},
-    {0x32ac995c, "Terminal", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
+     "Arial Narrow,Arial Unicode MS,Damascus,STIXNonUnicode", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x32ac995c, "Terminal", "Microsoft Sans Serif,Apple Symbols", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x338d648a, "NiagaraEngraved-Reg", "Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
-    {0x33bb65f2, "Sylfaen", "Arial Unicode MS,Marion", 2, 1252},
-    {0x3402c30e, "MSPMincho", "Arial Unicode MS,Apple SD Gothic Neo", 2, 1252},
-    {0x3412bf31, "SimSun-PUA", "STHeiti,Heiti TC,STFangsong", 0, 936},
-    {0x36eb39b9, "BerlinSansFB", "American Typewriter,Impact", 0, 1252},
-    {0x36f42055, "UniversATT", "Microsoft Sans Serif", 0, 1252},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x33bb65f2, "Sylfaen", "Arial Unicode MS,Marion", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3402c30e, "MSPMincho", "Arial Unicode MS,Apple SD Gothic Neo", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3412bf31, "SimSun-PUA", "STHeiti,Heiti TC,STFangsong", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x36eb39b9, "BerlinSansFB", "American Typewriter,Impact", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x36f42055, "UniversATT", "Microsoft Sans Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x3864c4f6, "HighTowerText", "STIXGeneral,.Helvetica Neue Desk UI", 2,
-     1252},
-    {0x3a257d03, "FangSong_GB2312", "Hiragino Sans GB,STHeiti", 0, 1252},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3a257d03, "FangSong_GB2312", "Hiragino Sans GB,STHeiti", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x3cdae668, "FreestyleScript", "Nadeem,Zapf Dingbats,STIXNonUnicode", 8,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x3d55aed7, "Jokerman",
-     "Papyrus,Lucida Grande,Heiti TC,American Typewriter", 0, 1252},
-    {0x3d5b4385, "PMingLiU", "Heiti SC,STHeiti", 2, 1252},
-    {0x3d9b7669, "EstrangeloEdessa", "American Typewriter,Marion", 0, 1252},
-    {0x3e532d74, "FranklinGothicMedium", "Impact,Arial Narrow", 0, 1252},
-    {0x3e6aa32d, "NSimSun", "STHeiti,STFangsong", 1, 936},
+     "Papyrus,Lucida Grande,Heiti TC,American Typewriter", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3d5b4385, "PMingLiU", "Heiti SC,STHeiti", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3d9b7669, "EstrangeloEdessa", "American Typewriter,Marion", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3e532d74, "FranklinGothicMedium", "Impact,Arial Narrow", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3e6aa32d, "NSimSun", "STHeiti,STFangsong", 1,
+     FX_CodePage::kChineseSimplified},
     {0x3f6c36a8, "Gautami",
-     "Damascus,STIXNonUnicode,STIXGeneral,American Typewriter", 0, 1252},
-    {0x3ff32662, "Chiller-Regular", "Papyrus,KufiStandardGK,Baghdad", 0, 1252},
-    {0x409de312, "ModernNo.20", "Avenir Next Condensed,Impact", 2, 1252},
+     "Damascus,STIXNonUnicode,STIXGeneral,American Typewriter", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3ff32662, "Chiller-Regular", "Papyrus,KufiStandardGK,Baghdad", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x409de312, "ModernNo.20", "Avenir Next Condensed,Impact", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x41443c5e, "Georgia", ".Helvetica Neue Desk UI,Arial Unicode MS", 2,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x4160ade5, "BellGothicStdBlack", "Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
-    {0x421976c4, "Modern-Regular", "Impact", 2, 1252},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x421976c4, "Modern-Regular", "Impact", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x422a7252, "Stencil", "STIXNonUnicode,Songti SC,Georgia,Baskerville", 0,
-     1252},
-    {0x42c8554f, "Fixedsys", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0x435cb41d, "Roman", "Arial Narrow", 0, 1252},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x42c8554f, "Fixedsys", "Microsoft Sans Serif,Apple Symbols", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x435cb41d, "Roman", "Arial Narrow", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x47882383, "CourierNew", "PCMyungjo,Osaka,Arial Unicode MS,Songti SC", 1,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x480a2338, "BerlinSansFBDemi",
-     "STIXNonUnicode,American Typewriter,Avenir Next Condensed Heavy", 0, 1252},
-    {0x480bf7a4, "CourierStd", "Courier New", 0, 1252},
+     "STIXNonUnicode,American Typewriter,Avenir Next Condensed Heavy", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x480bf7a4, "CourierStd", "Courier New", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x481ad6ed, "VladimirScript",
-     "STIXNonUnicode,Avenir Next Condensed,Impact", 8, 1252},
-    {0x4911577a, "YouYuan", "STHeiti,Heiti TC", 1, 936},
-    {0x4a788d72, "STXingkai", "Kaiti SC,Songti SC", 0, 936},
+     "STIXNonUnicode,Avenir Next Condensed,Impact", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x4911577a, "YouYuan", "STHeiti,Heiti TC", 1,
+     FX_CodePage::kChineseSimplified},
+    {0x4a788d72, "STXingkai", "Kaiti SC,Songti SC", 0,
+     FX_CodePage::kChineseSimplified},
     {0x4bf88566, "SegoeCondensed", "Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x4ccf51a4, "BerlinSansFB-Reg",
-     "STIXNonUnicode,American Typewriter,Impact", 0, 1252},
-    {0x4ea967ce, "GulimChe", "Arial Unicode MS,Heiti TC,STFangsong", 1, 1252},
+     "STIXNonUnicode,American Typewriter,Impact", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x4ea967ce, "GulimChe", "Arial Unicode MS,Heiti TC,STFangsong", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x4f68bd79, "LetterGothicStd",
-     "Courier New,Andale Mono,Ayuthaya,PCMyungjo,Osaka", 0, 1252},
+     "Courier New,Andale Mono,Ayuthaya,PCMyungjo,Osaka", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x51a0d0e6, "KozukaGothicPr6NM", "Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x531b3dea, "BasemicSymbol", "Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x5333fd39, "CalifornianFB-Reg",
-     "American Typewriter,Avenir Next Condensed,Impact", 2, 1252},
-    {0x53561a54, "FZYTK--GBK1-0", "STFangsong,Songti SC,STSong", 0, 936},
+     "American Typewriter,Avenir Next Condensed,Impact", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x53561a54, "FZYTK--GBK1-0", "STFangsong,Songti SC,STSong", 0,
+     FX_CodePage::kChineseSimplified},
     {0x55e0dde6, "LucidaSansTypewriter", "Menlo,Courier New,Andale Mono", 0,
-     1252},
-    {0x574d4d3d, "AdobeArabic", "Arial Narrow", 0, 1252},
-    {0x5792e759, "STKaiti", "Songti SC,Arial Unicode MS", 0, 936},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x574d4d3d, "AdobeArabic", "Arial Narrow", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x5792e759, "STKaiti", "Songti SC,Arial Unicode MS", 0,
+     FX_CodePage::kChineseSimplified},
     {0x5921978e, "LucidaSansUnicode", "Lucida Grande,Arial Unicode MS,Menlo", 0,
-     1252},
-    {0x594e2da4, "Vrinda", "Geeza Pro,Damascus,STIXGeneral,Gill Sans", 0, 1252},
-    {0x59baa9a2, "KaiTi_GB2312", "Hiragino Sans GB,STHeiti", 0, 1252},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x594e2da4, "Vrinda", "Geeza Pro,Damascus,STIXGeneral,Gill Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x59baa9a2, "KaiTi_GB2312", "Hiragino Sans GB,STHeiti", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x5cfedf4f, "BaskOldFace",
-     "Avenir Next Condensed Heavy,PT Sans,Avenir Next Condensed", 0, 1252},
-    {0x5e16ac91, "TrajanPro", "Arial Narrow,PT Sans Narrow,Damascus", 0, 1252},
+     "Avenir Next Condensed Heavy,PT Sans,Avenir Next Condensed", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x5e16ac91, "TrajanPro", "Arial Narrow,PT Sans Narrow,Damascus", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x5f97921c, "AdobeMyungjoStdM",
-     "AppleMyungjo,AppleGothic,Arial Unicode MS", 0, 936},
-    {0x5fefbfad, "Batang", "Arial Unicode MS,Songti SC", 2, 1252},
-    {0x605342b9, "DotumChe", "Arial Unicode MS,Heiti TC", 1, 1252},
-    {0x608c5f9a, "KaiTi_GB2312", "Hiragino Sans GB,STHeiti,Heiti TC", 0, 936},
+     "AppleMyungjo,AppleGothic,Arial Unicode MS", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x5fefbfad, "Batang", "Arial Unicode MS,Songti SC", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x605342b9, "DotumChe", "Arial Unicode MS,Heiti TC", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x608c5f9a, "KaiTi_GB2312", "Hiragino Sans GB,STHeiti,Heiti TC", 0,
+     FX_CodePage::kChineseSimplified},
     {0x61efd0d1, "MaturaMTScriptCapitals",
      "Kokonor,Damascus,STIXNonUnicode,STHeiti,Arial Black,Avenir Next Heavy", 0,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x626608a9, "MVBoli",
-     "Apple Braille,Geeza Pro,Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0x630501a3, "SmallFonts", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0x65d0e2a9, "FZYTK--GBK1-0", "STFangsong,Songti SC,STSong", 0, 936},
-    {0x669f29e1, "FZSTK--GBK1-0", "STHeiti,Heiti TC", 0, 936},
+     "Apple Braille,Geeza Pro,Microsoft Sans Serif,Apple Symbols", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x630501a3, "SmallFonts", "Microsoft Sans Serif,Apple Symbols", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x65d0e2a9, "FZYTK--GBK1-0", "STFangsong,Songti SC,STSong", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x669f29e1, "FZSTK--GBK1-0", "STHeiti,Heiti TC", 0,
+     FX_CodePage::kChineseSimplified},
     {0x673a9e5f, "Tunga",
      "Damascus,STIXNonUnicode,Avenir Next Condensed,Avenir Next Condensed "
      "Ultra Light,Futura",
-     0, 1252},
-    {0x691aa4ce, "NiagaraSolid", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0x696259b7, "Corbel", "Cochin,Baskerville,Marion", 0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x691aa4ce, "NiagaraSolid", "Microsoft Sans Serif,Apple Symbols", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x696259b7, "Corbel", "Cochin,Baskerville,Marion", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x696ee9be, "STXihei", "STHeiti,Heiti TC,Songti SC,Arial Unicode MS", 0,
-     936},
-    {0x6c59cf69, "Dotum", "Arial Unicode MS,Songti SC", 0, 1252},
-    {0x707fa561, "Gungsuh", "Arial Unicode MS,Heiti TC", 2, 1252},
+     FX_CodePage::kChineseSimplified},
+    {0x6c59cf69, "Dotum", "Arial Unicode MS,Songti SC", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x707fa561, "Gungsuh", "Arial Unicode MS,Heiti TC", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x71416bb2, "ZWAdobeF",
-     "STIXSizeFourSym,STIXSizeThreeSym,STIXSizeTwoSym,STIXSizeOneSym", 0, 1252},
+     "STIXSizeFourSym,STIXSizeThreeSym,STIXSizeTwoSym,STIXSizeOneSym", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x71b41801, "Verdana",
      "Tahoma,Marion,Apple Symbols,.Helvetica Neue Desk UI,Lucida "
      "Grande,Courier New",
-     0, 1252},
-    {0x73f25e4c, "PalatinoLinotype", "Palatino,Arial Unicode MS", 0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x73f25e4c, "PalatinoLinotype", "Palatino,Arial Unicode MS", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x73f4d19f, "NiagaraEngraved", "Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x74001694, "MyriadProBlack", "Palatino,Baskerville,Marion,Cochin", 0,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x74b14d8f, "Haettenschweiler", "Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
-    {0x74cb44ee, "NSimSun", "STHeiti,Heiti TC,STFangsong", 1, 936},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x74cb44ee, "NSimSun", "STHeiti,Heiti TC,STFangsong", 1,
+     FX_CodePage::kChineseSimplified},
     {0x76b4d7ff, "Shruti",
-     "Damascus,STIXNonUnicode,Arial Unicode MS,American Typewriter", 0, 1252},
-    {0x788b3533, "Webdings", "Microsoft Sans Serif,Apple Symbols", 6, 42},
-    {0x797dde99, "MSSerif", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
+     "Damascus,STIXNonUnicode,Arial Unicode MS,American Typewriter", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x788b3533, "Webdings", "Microsoft Sans Serif,Apple Symbols", 6,
+     FX_CodePage::kSymbol},
+    {0x797dde99, "MSSerif", "Microsoft Sans Serif,Apple Symbols", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x7a0f9e9e, "MSMincho",
      "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
      "PL UMing TW MBE,Arial Unicode MS,Apple SD Gothic Neo",
-     1, 1252},
+     1, FX_CodePage::kMSWin_WesternEuropean},
     {0x7b439caf, "OldEnglishTextMT",
-     "STIXNonUnicode,Arial Unicode MS,Baskerville,Avenir Next Medium", 0, 1252},
+     "STIXNonUnicode,Arial Unicode MS,Baskerville,Avenir Next Medium", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x8213a433, "LucidaSans-Typewriter",
-     "Comic Sans MS,Avenir Next,Arial Rounded MT Bold", 0, 1252},
-    {0x82fec929, "AdobeSongStd", "Heiti TC,STHeiti", 0, 936},
-    {0x83581825, "Modern", "Avenir Next Condensed,Impact", 0, 1252},
+     "Comic Sans MS,Avenir Next,Arial Rounded MT Bold", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x82fec929, "AdobeSongStd", "Heiti TC,STHeiti", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x83581825, "Modern", "Avenir Next Condensed,Impact", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x835a2823, "Algerian",
      "STIXNonUnicode,Baskerville,Avenir Next Medium,American Typewriter", 0,
-     1252},
-    {0x83dab9f5, "Script", "Arial Narrow", 0, 1252},
-    {0x847b56da, "Tahoma", "Songti SC,Apple Symbols", 0, 1252},
-    {0x8a783cb2, "SimSun-PUA", "STHeiti,Heiti TC,STFangsong", 0, 1252},
-    {0x8b5cac0e, "Onyx", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0x8c6a499e, "Gulim", "Arial Unicode MS,Songti SC", 0, 1252},
-    {0x8e0af790, "JuiceITC", "Nadeem,Al Bayan", 0, 1252},
-    {0x8e8d43b2, "Centaur", "Avenir Next Condensed,Noteworthy,Impact", 2, 1252},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x83dab9f5, "Script", "Arial Narrow", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x847b56da, "Tahoma", "Songti SC,Apple Symbols", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x8a783cb2, "SimSun-PUA", "STHeiti,Heiti TC,STFangsong", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x8b5cac0e, "Onyx", "Microsoft Sans Serif,Apple Symbols", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x8c6a499e, "Gulim", "Arial Unicode MS,Songti SC", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x8e0af790, "JuiceITC", "Nadeem,Al Bayan", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x8e8d43b2, "Centaur", "Avenir Next Condensed,Noteworthy,Impact", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x8ee4dcca, "BookshelfSymbol7", "Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x90794800, "BellGothicStdLight", "Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x909b516a, "Century", "Damascus,Andale Mono,Songti SC,Arial Unicode MS",
-     2, 1252},
-    {0x92ae370d, "MSOutlook", "Microsoft Sans Serif,Apple Symbols", 4, 42},
+     2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x92ae370d, "MSOutlook", "Microsoft Sans Serif,Apple Symbols", 4,
+     FX_CodePage::kSymbol},
     {0x93c9fbf1, "LucidaFax",
      "PT Sans Narrow,Papyrus,Kokonor,Geeza Pro,Arial Rounded MT Bold,Lucida "
      "Grande,Futura",
-     2, 1252},
+     2, FX_CodePage::kMSWin_WesternEuropean},
     {0x9565085e, "BookAntiqua", "Palatino,Microsoft Sans Serif,Apple Symbols",
-     2, 1252},
-    {0x9856d95d, "AdobeMingStd", "AHiragino Sans GB,Heiti TC,STHeiti", 0, 949},
+     2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x9856d95d, "AdobeMingStd", "AHiragino Sans GB,Heiti TC,STHeiti", 0,
+     FX_CodePage::kHangul},
     {0x9bbadd6b, "ColonnaMT", "Noteworthy,Avenir Next Condensed,Impact", 0,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x9cbd16a4, "ShowcardGothic-Reg",
-     "Arial Unicode MS,Georgia,American Typewriter", 0, 1252},
-    {0x9d73008e, "MSSansSerif", "Songti SC,Apple Symbols", 0, 1252},
+     "Arial Unicode MS,Georgia,American Typewriter", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x9d73008e, "MSSansSerif", "Songti SC,Apple Symbols", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xa0607db1, "GungsuhChe",
      "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
      "PL UMing TW MBE,Arial Unicode MS,Heiti TC,STFangsong",
-     1, 1252},
-    {0xa0bcf6a1, "LatinWide", "Zapfino,Arial Black,STHeiti", 2, 1252},
-    {0xa1429b36, "Symbol", "Microsoft Sans Serif,Apple Symbols", 6, 42},
-    {0xa1fa5abc, "Wingdings2", "Microsoft Sans Serif,Apple Symbols", 6, 42},
-    {0xa1fa5abd, "Wingdings3", "Microsoft Sans Serif,Apple Symbols", 6, 42},
+     1, FX_CodePage::kMSWin_WesternEuropean},
+    {0xa0bcf6a1, "LatinWide", "Zapfino,Arial Black,STHeiti", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xa1429b36, "Symbol", "Microsoft Sans Serif,Apple Symbols", 6,
+     FX_CodePage::kSymbol},
+    {0xa1fa5abc, "Wingdings2", "Microsoft Sans Serif,Apple Symbols", 6,
+     FX_CodePage::kSymbol},
+    {0xa1fa5abd, "Wingdings3", "Microsoft Sans Serif,Apple Symbols", 6,
+     FX_CodePage::kSymbol},
     {0xa427bad4, "InformalRoman-Regular",
-     "STIXNonUnicode,Arial Narrow,Avenir Next Condensed Demi Bold", 8, 1252},
-    {0xa8b92ece, "FZSTK--GBK1-0", "STHeiti,Heiti TC,STFangsong", 0, 936},
+     "STIXNonUnicode,Arial Narrow,Avenir Next Condensed Demi Bold", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xa8b92ece, "FZSTK--GBK1-0", "STHeiti,Heiti TC,STFangsong", 0,
+     FX_CodePage::kChineseSimplified},
     {0xa8d83ece, "CalifornianFB",
-     "American Typewriter,Avenir Next Condensed,Impact", 2, 1252},
+     "American Typewriter,Avenir Next Condensed,Impact", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xaa3e082c, "Kingsoft-Phonetic",
-     "STIXVariants,STIXSizeOneSym,Apple Braille", 0, 1252},
+     "STIXVariants,STIXSizeOneSym,Apple Braille", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xaa6bcabe, "HarlowSolidItalic",
      "STIXNonUnicode,Avenir Medium,Avenir Next Medium,Arial Unicode MS", 0,
-     1252},
-    {0xade5337c, "MSUIGothic", "Arial Unicode MS,Apple SD Gothic Neo", 0, 1252},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xade5337c, "MSUIGothic", "Arial Unicode MS,Apple SD Gothic Neo", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xb08dd941, "WideLatin",
      "Marion,Papyrus,Nanum Pen Script,Zapf Dingbats,Damascus,Zapfino,Arial "
      "Black,STHeiti",
-     2, 1252},
+     2, FX_CodePage::kMSWin_WesternEuropean},
     {0xb12765e0, "ITCLegacySansStdBook",
-     "LastResort,.Helvetica Neue Desk UI,Arial Unicode MS,Palatino", 0, 1252},
+     "LastResort,.Helvetica Neue Desk UI,Arial Unicode MS,Palatino", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xb207f05d, "PoorRichard", "Noteworthy,Avenir Next Condensed,Impact", 2,
-     1252},
-    {0xb3bc492f, "JuiceITC-Regular", "Nadeem,Al Bayan,STIXNonUnicode", 0, 1252},
-    {0xb5545399, "Marlett", "Microsoft Sans Serif,Apple Symbols", 4, 42},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xb3bc492f, "JuiceITC-Regular", "Nadeem,Al Bayan,STIXNonUnicode", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xb5545399, "Marlett", "Microsoft Sans Serif,Apple Symbols", 4,
+     FX_CodePage::kSymbol},
     {0xb5dd1ebb, "BritannicBold",
-     "Damascus,STIXNonUnicode,Avenir Next Condensed Heavy,PT Sans", 0, 1252},
-    {0xb699c1c5, "LucidaCalligraphy-Italic", "STHeiti,Arial Black", 0, 1252},
+     "Damascus,STIXNonUnicode,Avenir Next Condensed Heavy,PT Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xb699c1c5, "LucidaCalligraphy-Italic", "STHeiti,Arial Black", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xb725d629, "TimesNewRoman", "Microsoft Sans Serif,Apple Symbols", 2,
-     1252},
-    {0xb7eaebeb, "AdobeHeitiStdR", "Heiti TC,STHeiti", 0, 936},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xb7eaebeb, "AdobeHeitiStdR", "Heiti TC,STHeiti", 0,
+     FX_CodePage::kChineseSimplified},
     {0xbd29c486, "BerlinSansFBDemi-Bold",
-     "American Typewriter,Avenir Next Condensed Heavy", 0, 1252},
+     "American Typewriter,Avenir Next Condensed Heavy", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xbe8a8db4, "BookshelfSymbolSeven", "Microsoft Sans Serif,Apple Symbols",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0xc16c0118, "AdobeHebrew",
-     ".Helvetica Neue Desk UI,Palatino,American Typewriter", 0, 1252},
-    {0xc318b0af, "MyriadProLight", "Palatino,Baskerville,Marion", 0, 1252},
-    {0xc65e5659, "CambriaMath", "Arial Unicode MS", 2, 1252},
-    {0xc75c8f05, "LucidaConsole", "Courier New,Menlo,Andale Mono", 1, 1252},
-    {0xca7c35d6, "Calibri", "Apple Symbols,HeadLineA", 0, 1252},
-    {0xcb053f53, "MicrosoftYaHei", "Arial Unicode MS", 0, 936},
-    {0xcb7190f9, "Magneto-Bold", "Lucida Grande", 0, 1252},
-    {0xcca00cc5, "System", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0xccad6f76, "Jokerman-Regular", "Lucida Grande", 0, 1252},
-    {0xccc5818c, "EuroSign", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
+     ".Helvetica Neue Desk UI,Palatino,American Typewriter", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xc318b0af, "MyriadProLight", "Palatino,Baskerville,Marion", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xc65e5659, "CambriaMath", "Arial Unicode MS", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xc75c8f05, "LucidaConsole", "Courier New,Menlo,Andale Mono", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xca7c35d6, "Calibri", "Apple Symbols,HeadLineA", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xcb053f53, "MicrosoftYaHei", "Arial Unicode MS", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xcb7190f9, "Magneto-Bold", "Lucida Grande", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xcca00cc5, "System", "Microsoft Sans Serif,Apple Symbols", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xccad6f76, "Jokerman-Regular", "Lucida Grande", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xccc5818c, "EuroSign", "Microsoft Sans Serif,Apple Symbols", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xcf3d7234, "LucidaHandwriting-Italic",
-     "Microsoft Sans Serif,Apple Symbols", 0, 1252},
+     "Microsoft Sans Serif,Apple Symbols", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xcf7b8fdb, "MinionPro",
-     "Bell MT,Corbel,Times New Roman,Cambria,Berlin Sans FB", 0, 1252},
-    {0xcfe5755f, "Simhei", "STHeiti,Heiti TC,STFangsong", 1, 936},
-    {0xd011f4ee, "MSPGothic", "Arial Unicode MS,Apple SD Gothic Neo", 0, 1252},
+     "Bell MT,Corbel,Times New Roman,Cambria,Berlin Sans FB", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xcfe5755f, "Simhei", "STHeiti,Heiti TC,STFangsong", 1,
+     FX_CodePage::kChineseSimplified},
+    {0xd011f4ee, "MSPGothic", "Arial Unicode MS,Apple SD Gothic Neo", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xd060e7ef, "Vivaldi",
      "STIXNonUnicode,Arial Unicode MS,Avenir Medium,Avenir Next Medium", 8,
-     1252},
-    {0xd07edec1, "FranklinGothic-Medium", "Impact,Arial Narrow", 0, 1252},
-    {0xd107243f, "SimSun", "STHeiti,Heiti TC,STFangsong", 0, 936},
-    {0xd1881562, "ArialNarrow", "PT Sans Narrow,Apple Symbols", 0, 1252},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xd07edec1, "FranklinGothic-Medium", "Impact,Arial Narrow", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xd107243f, "SimSun", "STHeiti,Heiti TC,STFangsong", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xd1881562, "ArialNarrow", "PT Sans Narrow,Apple Symbols", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xd22b7dce, "BodoniMTPosterCompressed",
-     "Microsoft Sans Serif,Apple Symbols", 0, 1252},
+     "Microsoft Sans Serif,Apple Symbols", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xd22bfa60, "ComicSansMS",
      "Damascus,Georgia,.Helvetica Neue Desk UI,Lucida Grande,Arial Unicode MS",
-     8, 1252},
+     8, FX_CodePage::kMSWin_WesternEuropean},
     {0xd3bd0e35, "Bauhaus93",
-     "STIXNonUnicode,Arial Unicode MS,Avenir Next,Avenir", 0, 1252},
-    {0xd429ee7a, "STFangsong", "Songti SC,Arial Unicode MS", 0, 936},
+     "STIXNonUnicode,Arial Unicode MS,Avenir Next,Avenir", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xd429ee7a, "STFangsong", "Songti SC,Arial Unicode MS", 0,
+     FX_CodePage::kChineseSimplified},
     {0xd6679c12, "BernardMTCondensed", "Impact,Avenir Next Condensed Demi Bold",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0xd8e8a027, "LucidaSans",
      "Arial Narrow,Khmer MN,Kokonor,Damascus,Microsoft Sans Serif,Apple "
      "Symbols",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0xd9fe7761, "HighTowerText-Reg",
-     "STIXGeneral,.Helvetica Neue Desk UI,Trebuchet MS", 2, 1252},
-    {0xda7e551e, "STSong", "Arial Unicode MS", 0, 936},
-    {0xdaa6842d, "STZhongsong", "STFangsong,Songti SC,STSong", 0, 936},
-    {0xdaaab93f, "STFangsong", "Songti SC,Arial Unicode MS", 0, 936},
-    {0xdaeb0713, "STSong", "Songti SC,Arial Unicode MS", 0, 936},
-    {0xdafedbef, "STCaiyun", "Kaiti SC,Songti SC,STHeiti", 0, 936},
+     "STIXGeneral,.Helvetica Neue Desk UI,Trebuchet MS", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xda7e551e, "STSong", "Arial Unicode MS", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdaa6842d, "STZhongsong", "STFangsong,Songti SC,STSong", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdaaab93f, "STFangsong", "Songti SC,Arial Unicode MS", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdaeb0713, "STSong", "Songti SC,Arial Unicode MS", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdafedbef, "STCaiyun", "Kaiti SC,Songti SC,STHeiti", 0,
+     FX_CodePage::kChineseSimplified},
     {0xdb00a3d9, "Broadway",
-     "Papyrus,STIXNonUnicode,Arial Black,Avenir Next Heavy,Heiti TC", 0, 1252},
-    {0xdb1f5ad4, "STXinwei", "Kaiti SC,Songti SC,STHeiti", 0, 936},
-    {0xdb326e7f, "STKaiti", "Songti SC,Arial Unicode MS", 0, 936},
-    {0xdb69595a, "STHupo", "Kaiti SC,Songti SC,STHeiti", 0, 936},
-    {0xdba0082c, "STXihei", "Songti SC,Arial Unicode MS", 0, 936},
-    {0xdbd0ab18, "STXingkai", "Kaiti SC,Songti SC", 0, 936},
-    {0xdc1a7db1, "STLiti", "Kaiti SC,Songti SC", 0, 936},
+     "Papyrus,STIXNonUnicode,Arial Black,Avenir Next Heavy,Heiti TC", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xdb1f5ad4, "STXinwei", "Kaiti SC,Songti SC,STHeiti", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdb326e7f, "STKaiti", "Songti SC,Arial Unicode MS", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdb69595a, "STHupo", "Kaiti SC,Songti SC,STHeiti", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdba0082c, "STXihei", "Songti SC,Arial Unicode MS", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdbd0ab18, "STXingkai", "Kaiti SC,Songti SC", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdc1a7db1, "STLiti", "Kaiti SC,Songti SC", 0,
+     FX_CodePage::kChineseSimplified},
     {0xdc33075f, "KristenITC-Regular",
-     "STIXNonUnicode,Damascus,Songti SC,STSong", 8, 1252},
+     "STIXNonUnicode,Damascus,Songti SC,STSong", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xdcc7009c, "Harrington",
-     "STIXNonUnicode,Avenir Next Condensed Heavy,Noteworthy", 0, 1252},
-    {0xdd712466, "ArialBlack", "Geeza Pro,Damascus,Songti SC,STSong", 0, 1252},
-    {0xdde87b3e, "Impact", "Arial Narrow,Marion", 0, 1252},
+     "STIXNonUnicode,Avenir Next Condensed Heavy,Noteworthy", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xdd712466, "ArialBlack", "Geeza Pro,Damascus,Songti SC,STSong", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xdde87b3e, "Impact", "Arial Narrow,Marion", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xdf69fb32, "SnapITC",
-     "Arial Narrow,PT Sans Narrow,Marion,STHeiti,Arial Black", 0, 1252},
+     "Arial Narrow,PT Sans Narrow,Marion,STHeiti,Arial Black", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xdf8b25e8, "CenturyGothic",
-     "Damascus,Andale Mono,Songti SC,Arial Unicode MS", 0, 1252},
-    {0xe0f705c0, "KristenITC", "Songti SC,STSong", 8, 1252},
+     "Damascus,Andale Mono,Songti SC,Arial Unicode MS", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xe0f705c0, "KristenITC", "Songti SC,STSong", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xe1427573, "Raavi",
      "Damascus,STIXNonUnicode,Marion,Papyrus,Avenir Next Condensed "
      "Heavy,American Typewriter",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0xe2cea0cb, "Magneto",
      "STIXNonUnicode,Damascus,Geeza Pro,Lucida Grande,Georgia,Heiti TC", 0,
-     1252},
-    {0xe36a9e17, "Ravie", "STHeiti,Arial Black", 0, 1252},
-    {0xe433f8e2, "Parchment", "Microsoft Sans Serif,Apple Symbols", 8, 1252},
-    {0xe43dff4a, "Wingdings", "Microsoft Sans Serif,Apple Symbols", 4, 42},
-    {0xe4e2c405, "MTExtra", "Microsoft Sans Serif,Apple Symbols", 6, 42},
-    {0xe618cc35, "InformalRoman", "Arial Narrow", 8, 1252},
-    {0xe6c27ffc, "Mistral", "Apple Symbols", 8, 1252},
-    {0xe7ebf4b9, "Courier", "Courier New", 0, 1252},
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xe36a9e17, "Ravie", "STHeiti,Arial Black", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xe433f8e2, "Parchment", "Microsoft Sans Serif,Apple Symbols", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xe43dff4a, "Wingdings", "Microsoft Sans Serif,Apple Symbols", 4,
+     FX_CodePage::kSymbol},
+    {0xe4e2c405, "MTExtra", "Microsoft Sans Serif,Apple Symbols", 6,
+     FX_CodePage::kSymbol},
+    {0xe618cc35, "InformalRoman", "Arial Narrow", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xe6c27ffc, "Mistral", "Apple Symbols", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xe7ebf4b9, "Courier", "Courier New", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xe8bc4a9d, "MSReferenceSpecialty", "Microsoft Sans Serif,Apple Symbols",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0xe90fb013, "TempusSansITC",
-     "STIXNonUnicode,Microsoft Sans Serif,Avenir Light", 0, 1252},
+     "STIXNonUnicode,Microsoft Sans Serif,Avenir Light", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xec637b42, "Consolas",
      "AR PL UKai CN,AR PL UKai HK,AR PL UKai TW,AR PL UKai TW MBE,AR PL UMing "
      "CN,AR PL UMing HK,Microsoft Sans Serif,Tahoma",
-     1, 1252},
-    {0xed3a683b, "STXinwei", "Kaiti SC,Songti SC,", 0, 936},
+     1, FX_CodePage::kMSWin_WesternEuropean},
+    {0xed3a683b, "STXinwei", "Kaiti SC,Songti SC,", 0,
+     FX_CodePage::kChineseSimplified},
     {0xef264cd1, "LucidaHandwriting",
      "Arial Narrow,Avenir Next Condensed Demi Bold,Avenir Next "
      "Condensed,Avenir Next Condensed Medium,STHeiti,Arial Black",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0xf086bca2, "BaskervilleOldFace",
-     "STIXNonUnicode,Avenir Next Condensed Heavy,PT Sans", 0, 1252},
+     "STIXNonUnicode,Avenir Next Condensed Heavy,PT Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xf1028030, "Mangal",
-     "Arial Unicode MS,Microsoft Sans Serif,Arial Narrow,Tahoma", 2, 1252},
+     "Arial Unicode MS,Microsoft Sans Serif,Arial Narrow,Tahoma", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xf1da7eb9, "ShowcardGothic",
-     "Papyrus,Arial Unicode MS,Georgia,American Typewriter", 0, 1252},
+     "Papyrus,Arial Unicode MS,Georgia,American Typewriter", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xf210f06a, "ArialMT",
      "Arial Unicode MS,Arial Narrow,STIXNonUnicode,Damascus,Avenir Next "
      "Condensed Demi Bold,Avenir Next Condensed Medium,Avenir Next Condensed",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0xf477f16a, "Latha",
-     "Arial Narrow,Damascus,STIXNonUnicode,American Typewriter", 0, 1252},
-    {0xf616f3dd, "LiSu", "STHeiti,Heiti TC,STFangsong", 1, 936},
-    {0xfa479aa6, "MicrosoftYaHei", "Arial Unicode MS", 0, 936},
+     "Arial Narrow,Damascus,STIXNonUnicode,American Typewriter", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xf616f3dd, "LiSu", "STHeiti,Heiti TC,STFangsong", 1,
+     FX_CodePage::kChineseSimplified},
+    {0xfa479aa6, "MicrosoftYaHei", "Arial Unicode MS", 0,
+     FX_CodePage::kChineseSimplified},
     {0xfcd19697, "BookmanOldStyle",
-     "Geeza Pro,Damascus,Andale Mono,Songti SC,Arial Unicode MS", 0, 1252},
+     "Geeza Pro,Damascus,Andale Mono,Songti SC,Arial Unicode MS", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xfe209a82, "LucidaCalligraphy",
-     "Kokonor,Damascus,STIXNonUnicode,STHeiti,Arial Black", 0, 1252},
-    {0xfef135f8, "AdobeHeitiStd-Regular", "Heiti TC,STHeiti", 0, 936},
+     "Kokonor,Damascus,STIXNonUnicode,STHeiti,Arial Black", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xfef135f8, "AdobeHeitiStd-Regular", "Heiti TC,STHeiti", 0,
+     FX_CodePage::kChineseSimplified},
 };
-#elif defined(OS_ANDROID)
-const FGAS_FontInfo g_XFAFontsMap[] = {
-    {0x01d5d33e, "SimSun", "Droid Sans Fallback", 0, 936},
-    {0x01e4f102, "YouYuan", "Droid Sans Fallback", 1, 936},
-    {0x030549dc, "LiSu", "Droid Sans Fallback", 1, 936},
-    {0x032edd44, "Simhei", "Droid Sans Fallback", 1, 936},
+#elif BUILDFLAG(IS_ANDROID)
+const FGAS_FontInfo kXFAFontsMap[] = {
+    {0x01d5d33e, "SimSun", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x01e4f102, "YouYuan", "Droid Sans Fallback", 1,
+     FX_CodePage::kChineseSimplified},
+    {0x030549dc, "LiSu", "Droid Sans Fallback", 1,
+     FX_CodePage::kChineseSimplified},
+    {0x032edd44, "Simhei", "Droid Sans Fallback", 1,
+     FX_CodePage::kChineseSimplified},
     {0x03eac6fc, "PoorRichard-Regular",
      "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback,Droid Arabic "
      "Naskh,Droid Sans Ethiopic",
-     2, 1252},
+     2, FX_CodePage::kMSWin_WesternEuropean},
     {0x03ed90e6, "Nina",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x077b56b3, "KingsoftPhoneticPlain",
      "Droid Sans Thai,Droid Sans Armenian,Droid Arabic Naskh,Droid Sans "
      "Ethiopic,Droid Sans Fallback",
-     0, 1252},
-    {0x078ed524, "MicrosoftSansSerif", "Droid Sans Fallback", 0, 1252},
-    {0x089b18a9, "Arial", "Droid Sans Fallback", 0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x078ed524, "MicrosoftSansSerif", "Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x089b18a9, "Arial", "Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x0b2cad72, "MonotypeCorsiva",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x0bb003e7, "Kartika",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
      "Mono",
-     2, 1252},
+     2, FX_CodePage::kMSWin_WesternEuropean},
     {0x0bb469df, "VinerHandITC",
-     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
-    {0x0bc1a851, "SegoeUI", "Droid Sans Fallback", 0, 1252},
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x0bc1a851, "SegoeUI", "Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x0c112ebd, "KozukaGothicPro-VIM",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x0cfcb9c1, "AdobeThai",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 847},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSDOS_Thai},
     {0x0e7de0f9, "Playbill",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
      "Mono",
-     0, 1252},
-    {0x0eff47c3, "STHupo", "Droid Sans Fallback", 0, 936},
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x0eff47c3, "STHupo", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0x107ad374, "Constantia",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x12194c2d, "KunstlerScript",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x135ef6a1, "MinionProSmBd",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x158c4049, "Garamond",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x160ecb24, "STZhongsong", "Droid Sans Fallback", 0, 936},
-    {0x161ed07e, "MSGothic", "Droid Sans Fallback", 1, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x160ecb24, "STZhongsong", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x161ed07e, "MSGothic", "Droid Sans Fallback", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x171d1ed1, "SnapITC-Regular",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x18d1188f, "Cambria", "Droid Sans Fallback", 2, 1252},
-    {0x18eaf350, "ArialUnicodeMS", "Droid Sans Fallback", 0, 936},
-    {0x1a92d115, "MingLiU", "Droid Sans Fallback", 1, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x18d1188f, "Cambria", "Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x18eaf350, "ArialUnicodeMS", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x1a92d115, "MingLiU", "Droid Sans Fallback", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x1cc217c6, "TrebuchetMS",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x1d649596, "BasemicTimes",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x1e34ee60, "BellMT",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x1eb36945, "CooperBlack",
-     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x1ef7787d, "BatangChe", "Droid Sans Fallback", 1, 1252},
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x1ef7787d, "BatangChe", "Droid Sans Fallback", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x20b3bd3a, "BrushScriptMT", "Droid Arabic Naskh,Droid Sans Ethiopic", 8,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x220877aa, "Candara",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x22135007, "FreestyleScript-Regular",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x251059c3, "Chiller",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif", 0, 1252},
-    {0x25bed6dd, "MSReferenceSansSerif", "Droid Sans Fallback", 0, 1252},
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x25bed6dd, "MSReferenceSansSerif", "Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x28154c81, "Parchment-Regular",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
-    {0x29711eb9, "STLiti", "Droid Sans Fallback", 0, 936},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x29711eb9, "STLiti", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0x2b1993b4, "Basemic",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x2b316339, "NiagaraSolid-Reg",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x2c147529, "FootlightMTLight",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x2c198928, "HarlowSolid",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x2c6ac6b2, "LucidaBright",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 2, 1252},
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x2c9f38e2, "KozukaMinchoPro-VIR",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x2d5a47b0, "STCaiyun", "Droid Sans Fallback", 0, 936},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x2d5a47b0, "STCaiyun", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0x2def26bf, "BernardMT-Condensed",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x2fd8930b, "KozukaMinchoPr6NR",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x3115525a, "FangSong_GB2312", "Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3115525a, "FangSong_GB2312", "Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x31327817, "MyriadPro",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x32244975, "Helvetica",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 0, 1252},
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x32ac995c, "Terminal",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x338d648a, "NiagaraEngraved-Reg",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x33bb65f2, "Sylfaen",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x3402c30e, "MSPMincho", "Droid Sans Fallback", 2, 1252},
-    {0x3412bf31, "SimSun-PUA", "Droid Sans Fallback", 0, 936},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3402c30e, "MSPMincho", "Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3412bf31, "SimSun-PUA", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0x36eb39b9, "BerlinSansFB",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x36f42055, "UniversATT", "Microsoft Sans Serif", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x36f42055, "UniversATT", "Microsoft Sans Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x3864c4f6, "HighTowerText",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x3a257d03, "FangSong_GB2312", "Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3a257d03, "FangSong_GB2312", "Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x3cdae668, "FreestyleScript",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x3d55aed7, "Jokerman",
-     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x3d5b4385, "PMingLiU", "Droid Sans Fallback", 2, 1252},
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3d5b4385, "PMingLiU", "Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x3d9b7669, "EstrangeloEdessa",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x3e532d74, "FranklinGothicMedium",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x3e6aa32d, "NSimSun", "Droid Sans Fallback", 1, 936},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3e6aa32d, "NSimSun", "Droid Sans Fallback", 1,
+     FX_CodePage::kChineseSimplified},
     {0x3f6c36a8, "Gautami",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
      "Mono,Droid Sans Fallback",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0x3ff32662, "Chiller-Regular",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x409de312, "ModernNo.20",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x41443c5e, "Georgia",
-     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x4160ade5, "BellGothicStdBlack",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x421976c4, "Modern-Regular",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x422a7252, "Stencil",
-     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x42c8554f, "Fixedsys",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x435cb41d, "Roman",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x47882383, "CourierNew", "Droid Sans Fallback", 1, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x47882383, "CourierNew", "Droid Sans Fallback", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x480a2338, "BerlinSansFBDemi",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x480bf7a4, "CourierStd", "Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x480bf7a4, "CourierStd", "Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x481ad6ed, "VladimirScript",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
-    {0x4911577a, "YouYuan", "Droid Sans Fallback", 1, 936},
-    {0x4a788d72, "STXingkai", "Droid Sans Fallback", 0, 936},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x4911577a, "YouYuan", "Droid Sans Fallback", 1,
+     FX_CodePage::kChineseSimplified},
+    {0x4a788d72, "STXingkai", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0x4bf88566, "SegoeCondensed",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x4ccf51a4, "BerlinSansFB-Reg",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x4ea967ce, "GulimChe", "Droid Sans Fallback", 1, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x4ea967ce, "GulimChe", "Droid Sans Fallback", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x4f68bd79, "LetterGothicStd",
      "Droid Sans Mono,Droid Arabic Naskh,Droid Sans Ethiopic,Droid Sans "
      "Mono,Droid Serif,Droid Sans Fallback",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0x51a0d0e6, "KozukaGothicPr6NM",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x531b3dea, "BasemicSymbol",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x5333fd39, "CalifornianFB-Reg",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x53561a54, "FZYTK--GBK1-0", "Droid Sans Fallback", 0, 936},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x53561a54, "FZYTK--GBK1-0", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0x55e0dde6, "LucidaSansTypewriter",
-     "Droid Sans Mono,Droid Arabic Naskh,Droid Sans Ethiopic", 0, 1252},
+     "Droid Sans Mono,Droid Arabic Naskh,Droid Sans Ethiopic", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x574d4d3d, "AdobeArabic",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x5792e759, "STKaiti", "Droid Sans Fallback", 0, 936},
-    {0x5921978e, "LucidaSansUnicode", "Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x5792e759, "STKaiti", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x5921978e, "LucidaSansUnicode", "Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x594e2da4, "Vrinda",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
      "Mono",
-     0, 1252},
-    {0x59baa9a2, "KaiTi_GB2312", "Droid Sans Fallback", 0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x59baa9a2, "KaiTi_GB2312", "Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x5cfedf4f, "BaskOldFace",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x5f97921c, "AdobeMyungjoStdM", "Droid Sans Fallback", 0, 936},
-    {0x5fefbfad, "Batang", "Droid Sans Fallback", 2, 1252},
-    {0x605342b9, "DotumChe", "Droid Sans Fallback", 1, 1252},
-    {0x608c5f9a, "KaiTi_GB2312", "Droid Sans Fallback", 0, 936},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x5f97921c, "AdobeMyungjoStdM", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x5fefbfad, "Batang", "Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x605342b9, "DotumChe", "Droid Sans Fallback", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x608c5f9a, "KaiTi_GB2312", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0x61efd0d1, "MaturaMTScriptCapitals",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
      "Mono",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0x626608a9, "MVBoli",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
      "Mono",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0x630501a3, "SmallFonts",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x65d0e2a9, "FZYTK--GBK1-0", "Droid Sans Fallback", 0, 936},
-    {0x669f29e1, "FZSTK--GBK1-0", "Droid Sans Fallback", 0, 936},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x65d0e2a9, "FZYTK--GBK1-0", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x669f29e1, "FZSTK--GBK1-0", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0x673a9e5f, "Tunga",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
      "Mono,Droid Sans Fallback",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0x691aa4ce, "NiagaraSolid",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x696259b7, "Corbel",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x696ee9be, "STXihei", "Droid Sans Fallback", 0, 936},
-    {0x6c59cf69, "Dotum", "Droid Sans Fallback", 0, 1252},
-    {0x707fa561, "Gungsuh", "Droid Sans Fallback", 2, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x696ee9be, "STXihei", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x6c59cf69, "Dotum", "Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x707fa561, "Gungsuh", "Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x71416bb2, "ZWAdobeF",
      "Droid Arabic Naskh,Droid Sans Armenian,Droid Sans Ethiopic,Droid Sans "
      "Georgian,Droid Sans Hebrew,Droid Sans Thai",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0x71b41801, "Verdana",
-     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x73f25e4c, "PalatinoLinotype", "Droid Sans Fallback", 0, 1252},
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x73f25e4c, "PalatinoLinotype", "Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x73f4d19f, "NiagaraEngraved",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x74001694, "MyriadProBlack", "Book Antiqua,Constantia,Dotum,Georgia", 0,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x74b14d8f, "Haettenschweiler",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x74cb44ee, "NSimSun", "Droid Sans Fallback", 1, 936},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x74cb44ee, "NSimSun", "Droid Sans Fallback", 1,
+     FX_CodePage::kChineseSimplified},
     {0x76b4d7ff, "Shruti",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
      "Mono",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0x788b3533, "Webdings",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6, 42},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6,
+     FX_CodePage::kSymbol},
     {0x797dde99, "MSSerif",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x7a0f9e9e, "MSMincho", "Droid Sans Fallback", 1, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x7a0f9e9e, "MSMincho", "Droid Sans Fallback", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x7b439caf, "OldEnglishTextMT",
-     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x8213a433, "LucidaSans-Typewriter",
-     "Droid Sans Mono,Droid Serif,Roboto,Droid Sans Fallback", 0, 1252},
-    {0x82fec929, "AdobeSongStd", "Droid Sans Fallback", 0, 936},
+     "Droid Sans Mono,Droid Serif,Roboto,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x82fec929, "AdobeSongStd", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0x83581825, "Modern",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x835a2823, "Algerian",
-     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x83dab9f5, "Script",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x847b56da, "Tahoma", "Droid Sans Fallback", 0, 1252},
-    {0x8a783cb2, "SimSun-PUA", "Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x847b56da, "Tahoma", "Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x8a783cb2, "SimSun-PUA", "Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x8b5cac0e, "Onyx",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x8c6a499e, "Gulim", "Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x8c6a499e, "Gulim", "Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x8e0af790, "JuiceITC",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x8e8d43b2, "Centaur",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x8ee4dcca, "BookshelfSymbol7",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x90794800, "BellGothicStdLight",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x909b516a, "Century",
-     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x92ae370d, "MSOutlook",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 4, 42},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 4,
+     FX_CodePage::kSymbol},
     {0x93c9fbf1, "LucidaFax",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
      "Mono",
-     2, 1252},
+     2, FX_CodePage::kMSWin_WesternEuropean},
     {0x9565085e, "BookAntiqua",
-     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x9856d95d, "AdobeMingStd", "Droid Sans Fallback", 0, 949},
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x9856d95d, "AdobeMingStd", "Droid Sans Fallback", 0,
+     FX_CodePage::kHangul},
     {0x9bbadd6b, "ColonnaMT",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x9cbd16a4, "ShowcardGothic-Reg",
-     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallbac", 0, 1252},
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallbac", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0x9d73008e, "MSSansSerif",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xa0607db1, "GungsuhChe", "Droid Sans Fallback", 1, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xa0607db1, "GungsuhChe", "Droid Sans Fallback", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xa0bcf6a1, "LatinWide",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xa1429b36, "Symbol",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6, 42},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6,
+     FX_CodePage::kSymbol},
     {0xa1fa5abc, "Wingdings2",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6, 42},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6,
+     FX_CodePage::kSymbol},
     {0xa1fa5abd, "Wingdings3",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6, 42},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6,
+     FX_CodePage::kSymbol},
     {0xa427bad4, "InformalRoman-Regular",
-     "Droid Arabic Naskh,Droid Sans Ethiopic", 8, 1252},
-    {0xa8b92ece, "FZSTK--GBK1-0", "Droid Sans Fallback", 0, 936},
+     "Droid Arabic Naskh,Droid Sans Ethiopic", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xa8b92ece, "FZSTK--GBK1-0", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0xa8d83ece, "CalifornianFB",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xaa3e082c, "Kingsoft-Phonetic",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xaa6bcabe, "HarlowSolidItalic",
-     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xade5337c, "MSUIGothic",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xb08dd941, "WideLatin",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
      "Mono",
-     2, 1252},
+     2, FX_CodePage::kMSWin_WesternEuropean},
     {0xb207f05d, "PoorRichard",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xb3bc492f, "JuiceITC-Regular",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xb5545399, "Marlett",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 4, 42},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 4,
+     FX_CodePage::kSymbol},
     {0xb5dd1ebb, "BritannicBold", "Droid Arabic Naskh,Droid Sans Ethiopic", 0,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xb699c1c5, "LucidaCalligraphy-Italic",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xb725d629, "TimesNewRoman", "Droid Sans Fallback", 2, 1252},
-    {0xb7eaebeb, "AdobeHeitiStdR", "Droid Sans Fallback", 0, 936},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xb725d629, "TimesNewRoman", "Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xb7eaebeb, "AdobeHeitiStdR", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0xbd29c486, "BerlinSansFBDemi-Bold",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xbe8a8db4, "BookshelfSymbolSeven",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xc16c0118, "AdobeHebrew",
      "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback,Droid Arabic "
      "Naskh,Droid Sans Ethiopic",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0xc318b0af, "MyriadProLight",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xc65e5659, "CambriaMath", "Droid Sans Fallback", 2, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xc65e5659, "CambriaMath", "Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xc75c8f05, "LucidaConsole",
-     "Droid Sans Mono,Droid Serif,Roboto,Droid Sans Fallback", 1, 1252},
-    {0xca7c35d6, "Calibri", "Droid Sans Fallback", 0, 1252},
-    {0xcb053f53, "MicrosoftYaHei", "Droid Sans Fallback", 0, 936},
+     "Droid Sans Mono,Droid Serif,Roboto,Droid Sans Fallback", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xca7c35d6, "Calibri", "Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xcb053f53, "MicrosoftYaHei", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0xcb7190f9, "Magneto-Bold",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xcca00cc5, "System",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xccad6f76, "Jokerman-Regular",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xccc5818c, "EuroSign",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xcf3d7234, "LucidaHandwriting-Italic",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xcf7b8fdb, "MinionPro",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xcfe5755f, "Simhei", "Droid Sans Fallback", 1, 936},
-    {0xd011f4ee, "MSPGothic", "Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xcfe5755f, "Simhei", "Droid Sans Fallback", 1,
+     FX_CodePage::kChineseSimplified},
+    {0xd011f4ee, "MSPGothic", "Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xd060e7ef, "Vivaldi",
-     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xd07edec1, "FranklinGothic-Medium",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xd107243f, "SimSun", "Droid Sans Fallback", 0, 936},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xd107243f, "SimSun", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0xd1881562, "ArialNarrow",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xd22b7dce, "BodoniMTPosterCompressed",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xd22bfa60, "ComicSansMS", "Droid Serif,Roboto,Droid Sans Fallback", 8,
-     1252},
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xd3bd0e35, "Bauhaus93",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xd429ee7a, "STFangsong", "Droid Sans Fallback", 0, 936},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xd429ee7a, "STFangsong", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0xd6679c12, "BernardMTCondensed",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xd8e8a027, "LucidaSans",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 0, 1252},
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xd9fe7761, "HighTowerText-Reg",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0xda7e551e, "STSong", "Droid Sans Fallback", 0, 936},
-    {0xdaa6842d, "STZhongsong", "Droid Sans Fallback", 0, 936},
-    {0xdaaab93f, "STFangsong", "Droid Sans Fallback", 0, 936},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xda7e551e, "STSong", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdaa6842d, "STZhongsong", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdaaab93f, "STFangsong", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0xdaeb0713, "STSong",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 936},
-    {0xdafedbef, "STCaiyun", "Droid Sans Fallback", 0, 936},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdafedbef, "STCaiyun", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0xdb00a3d9, "Broadway",
-     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xdb1f5ad4, "STXinwei", "Droid Sans Fallback", 0, 936},
-    {0xdb326e7f, "STKaiti", "Droid Sans Fallback", 0, 936},
-    {0xdb69595a, "STHupo", "Droid Sans Fallback", 0, 936},
-    {0xdba0082c, "STXihei", "Droid Sans Fallback", 0, 936},
-    {0xdbd0ab18, "STXingkai", "Droid Sans Fallback", 0, 936},
-    {0xdc1a7db1, "STLiti", "Droid Sans Fallback", 0, 936},
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xdb1f5ad4, "STXinwei", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdb326e7f, "STKaiti", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdb69595a, "STHupo", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdba0082c, "STXihei", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdbd0ab18, "STXingkai", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdc1a7db1, "STLiti", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0xdc33075f, "KristenITC-Regular",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 8, 1252},
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xdcc7009c, "Harrington",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xdd712466, "ArialBlack",
-     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xdde87b3e, "Impact",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xdf69fb32, "SnapITC",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
      "Mono",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0xdf8b25e8, "CenturyGothic",
-     "Droid Serif,Roboto,Droid Serif,Droid Sans Mono", 0, 1252},
+     "Droid Serif,Roboto,Droid Serif,Droid Sans Mono", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xe0f705c0, "KristenITC",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 8, 1252},
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xe1427573, "Raavi",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
      "Mono",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0xe2cea0cb, "Magneto",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
      "Mono",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0xe36a9e17, "Ravie",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
      "Mono",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0xe433f8e2, "Parchment",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xe43dff4a, "Wingdings",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 4, 42},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 4,
+     FX_CodePage::kSymbol},
     {0xe4e2c405, "MTExtra",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6, 42},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6,
+     FX_CodePage::kSymbol},
     {0xe618cc35, "InformalRoman",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif", 8, 1252},
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xe6c27ffc, "Mistral",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
-    {0xe7ebf4b9, "Courier", "Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xe7ebf4b9, "Courier", "Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xe8bc4a9d, "MSReferenceSpecialty",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xe90fb013, "TempusSansITC",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xec637b42, "Consolas", "Droid Sans Fallback", 1, 1252},
-    {0xed3a683b, "STXinwei", "Droid Sans Fallback", 0, 936},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xec637b42, "Consolas", "Droid Sans Fallback", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xed3a683b, "STXinwei", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0xef264cd1, "LucidaHandwriting",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
      "Mono",
-     0, 1252},
+     0, FX_CodePage::kMSWin_WesternEuropean},
     {0xf086bca2, "BaskervilleOldFace",
-     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xf1028030, "Mangal",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
      "Mono",
-     2, 1252},
+     2, FX_CodePage::kMSWin_WesternEuropean},
     {0xf1da7eb9, "ShowcardGothic",
-     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallbac", 0, 1252},
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallbac", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xf210f06a, "ArialMT",
-     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif", 0, 1252},
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xf477f16a, "Latha",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
      "Mono",
-     0, 1252},
-    {0xf616f3dd, "LiSu", "Droid Sans Fallback", 1, 936},
-    {0xfa479aa6, "MicrosoftYaHei", "Droid Sans Fallback", 0, 936},
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xf616f3dd, "LiSu", "Droid Sans Fallback", 1,
+     FX_CodePage::kChineseSimplified},
+    {0xfa479aa6, "MicrosoftYaHei", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
     {0xfcd19697, "BookmanOldStyle",
-     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
     {0xfe209a82, "LucidaCalligraphy",
      "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
      "Mono",
-     0, 1252},
-    {0xfef135f8, "AdobeHeitiStd-Regular", "Droid Sans Fallback", 0, 936},
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xfef135f8, "AdobeHeitiStd-Regular", "Droid Sans Fallback", 0,
+     FX_CodePage::kChineseSimplified},
 };
+#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
+const FGAS_FontInfo kXFAFontsMap[] = {
+    {0x01d5d33e, "SimSun",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
+     "PL UMing TW MBE",
+     0, FX_CodePage::kChineseSimplified},
+    {0x01e4f102, "YouYuan",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
+     "PL UMing TW MBE",
+     1, FX_CodePage::kChineseSimplified},
+    {0x030549dc, "LiSu",
+     "WenQuanYi Zen Hei,WenQuanYi Zen Hei Sharp,WenQuanYi Zen Hei "
+     "Mono,WenQuanYi Micro Hei",
+     1, FX_CodePage::kChineseSimplified},
+    {0x032edd44, "Simhei",
+     "WenQuanYi Zen Hei,WenQuanYi Zen Hei Sharp,WenQuanYi Zen Hei "
+     "Mono,WenQuanYi Micro Hei",
+     1, FX_CodePage::kChineseSimplified},
+    {0x03eac6fc, "PoorRichard-Regular", "Droid Sans Japanese,FreeSerif", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x03ed90e6, "Nina", "FreeSerif", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x077b56b3, "KingsoftPhoneticPlain",
+     "Tibetan Machine Uni,LKLUG,Samyak Gujarati,Droid Sans Thai,Droid Sans "
+     "Armenian,Untitled1,utkal,Lohit Oriya",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x078ed524, "MicrosoftSansSerif",
+     "Droid Sans Japanese,FreeSerif,WenQuanYi Micro Hei", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x089b18a9, "Arial",
+     "Droid Sans Japanese,DejaVu Sans Condensed,FreeSerif,WenQuanYi Micro Hei",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x0b2cad72, "MonotypeCorsiva", "Droid Sans Japanese,FreeSerif", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x0bb003e7, "Kartika",
+     "FreeSans,Liberation Sans,Liberation Sans Narrow,Nimbus Sans "
+     "L,Garuda,FreeSerif,WenQuanYi Micro Hei",
+     2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x0bb469df, "VinerHandITC",
+     "Droid Sans Japanese,Ubuntu,Liberation Sans,Liberation Serif", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x0bc1a851, "SegoeUI", "Droid Sans Japanese,DejaVu Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x0c112ebd, "KozukaGothicPro-VIM", "FreeSerif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x0cfcb9c1, "AdobeThai", "Droid Sans Japanese,Waree", 0,
+     FX_CodePage::kMSDOS_Thai},
+    {0x0e7de0f9, "Playbill",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
+     "Sans Ethiopic,Droid Sans Japanese,FreeSerif",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x0eff47c3, "STHupo", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x107ad374, "Constantia",
+     "Droid Sans Japanese,FreeSerif,WenQuanYi Micro Hei,Ubuntu", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x12194c2d, "KunstlerScript", "Droid Sans Japanese,Liberation Serif", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x135ef6a1, "MinionProSmBd", "Liberation Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x158c4049, "Garamond",
+     "Droid Sans Japanese,Liberation Serif,Ubuntu,FreeSerif", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x160ecb24, "STZhongsong",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, FX_CodePage::kChineseSimplified},
+    {0x161ed07e, "MSGothic",
+     "WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,AR PL "
+     "UMing CN,AR PL UMing HK,AR PL UMing TW",
+     1, FX_CodePage::kMSWin_WesternEuropean},
+    {0x171d1ed1, "SnapITC-Regular",
+     "Liberation Sans Narrow,Ubuntu Condensed,Nimbus Sans L,DejaVu Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x18d1188f, "Cambria", "Droid Sans Japanese,FreeSerif,FreeMono", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x18eaf350, "ArialUnicodeMS",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, FX_CodePage::kChineseSimplified},
+    {0x1a92d115, "MingLiU",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     1, FX_CodePage::kMSWin_WesternEuropean},
+    {0x1cc217c6, "TrebuchetMS",
+     "Droid Sans Japanese,Liberation Serif,FreeSerif,Ubuntu", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x1d649596, "BasemicTimes",
+     "Liberation Serif,Times New Roman,Droid Sans Japanese,FreeSerif,Ubuntu", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x1e34ee60, "BellMT",
+     "KacstQurn,Droid Sans Japanese,Ubuntu,Liberation Serif", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x1eb36945, "CooperBlack",
+     "KacstQurn,Droid Sans Japanese,FreeMono,Liberation Mono, WenQuanYi Micro "
+     "Hei Mono",
+     2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x1ef7787d, "BatangChe",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing "
+     "TW,WenQuanYi Zen Hei,WenQuanYi Micro Hei",
+     1, FX_CodePage::kMSWin_WesternEuropean},
+    {0x20b3bd3a, "BrushScriptMT",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,Droid Sans "
+     "Japanese,URW Chancery L,Liberation Sans",
+     8, FX_CodePage::kMSWin_WesternEuropean},
+    {0x220877aa, "Candara", "Droid Sans Japanese,DejaVu Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x22135007, "FreestyleScript-Regular",
+     "KacstQurn,Droid Sans Japanese,Liberation Sans", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x251059c3, "Chiller",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,Droid Sans "
+     "Japanese,Liberation Sans",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x25bed6dd, "MSReferenceSansSerif",
+     "DejaVu Sans Condensed,Ubuntu Condensed,Droid Sans Japanese,AR PL UKai "
+     "HK",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x28154c81, "Parchment-Regular", "Droid Sans Japanese,Liberation Sans", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x29711eb9, "STLiti", "AR PL UKai HK", 0, FX_CodePage::kChineseSimplified},
+    {0x2b1993b4, "Basemic",
+     "Liberation Serif,Droid Sans Japanese,Liberation Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x2b316339, "NiagaraSolid-Reg", "Droid Sans Japanese,Liberation Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x2c147529, "FootlightMTLight",
+     "KacstQurn,Droid Sans Japanese,Liberation Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x2c198928, "HarlowSolid", "KacstQurn,Droid Sans Japanese,Liberation Sans",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x2c6ac6b2, "LucidaBright",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
+     "Sans Japanese,Liberation Sans",
+     2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x2c9f38e2, "KozukaMinchoPro-VIR", "DejaVu Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x2d5a47b0, "STCaiyun", "AR PL UKai HK", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x2def26bf, "BernardMT-Condensed",
+     "KacstQurn,Droid Sans Japanese,DejaVu Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x2fd8930b, "KozukaMinchoPr6NR", "DejaVu Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3115525a, "FangSong_GB2312",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x31327817, "MyriadPro", "Ubuntu Condensed,Droid Sans Japanese, FreeSerif",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x32244975, "Helvetica",
+     "Ubuntu,DejaVu Sans Condensed,Liberation Sans,Liberation Sans "
+     "Narrow,Nimbus Sans ",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x32ac995c, "Terminal", "DejaVu Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x338d648a, "NiagaraEngraved-Reg", "Droid Sans Japanese,DejaVu Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x33bb65f2, "Sylfaen", "Droid Sans Japanese,DejaVu Sans", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3402c30e, "MSPMincho",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3412bf31, "SimSun-PUA",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing CN,AR PL UMing HK", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x36eb39b9, "BerlinSansFB",
+     "Droid Sans Japanese,Liberation Serif,Ubuntu,FreeSerif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x36f42055, "UniversATT", "Microsoft Sans Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3864c4f6, "HighTowerText", "Droid Sans Japanese,DejaVu Serif", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3a257d03, "FangSong_GB2312", "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x3c7d1d07, "Garamond3LTStd",
+     "Droid Sans Japanese,Ubuntu Condensed,DejaVu Sans Condensed,Liberation "
+     "Serif,Ubuntu,FreeSerif",
+     2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x3cdae668, "FreestyleScript", "KacstQurn,Droid Sans Japanese,DejaVu Sans",
+     8, FX_CodePage::kMSWin_WesternEuropean},
+    {0x3d55aed7, "Jokerman", "Droid Sans Japanese,DejaVu Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3d5b4385, "PMingLiU",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x3d9b7669, "EstrangeloEdessa", "Droid Sans Japanese,DejaVu Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3e532d74, "FranklinGothicMedium", "Droid Sans Japanese,Ubuntu", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x3e6aa32d, "NSimSun",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     1, FX_CodePage::kChineseSimplified},
+    {0x3f6c36a8, "Gautami",
+     "Droid Arabic Naskh,Droid Sans Ethiopic, mry_KacstQurn,Droid Sans "
+     "Japanese,FreeSans",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x3ff32662, "Chiller-Regular",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,FreeSans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x409de312, "ModernNo.20",
+     "KacstQurn,Droid Sans Japanese,Nimbus Sans L,Nimbus Sans L,FreeSans", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x41443c5e, "Georgia", "Droid Sans Japanese,FreeSans", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x4160ade5, "BellGothicStdBlack", "FreeSans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x421976c4, "Modern-Regular", "FreeSans", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x422a7252, "Stencil", "Droid Sans Japanese,FreeSans,Liberation Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x42c8554f, "Fixedsys", "FreeSerif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x435cb41d, "Roman", "FreeSerif", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x47882383, "CourierNew",
+     "FreeMono,WenQuanYi Micro Hei Mono,AR PL UKai CN,AR PL UKai HK,AR PL "
+     "UKai TW,AR PL UKai TW MBE,DejaVu Sans",
+     1, FX_CodePage::kMSWin_WesternEuropean},
+    {0x480a2338, "BerlinSansFBDemi", "Droid Sans Japanese,Liberation Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x480bf7a4, "CourierStd", "DejaVu Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x481ad6ed, "VladimirScript", "Droid Sans Japanese,DejaVu Serif", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x4911577a, "YouYuan",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 1,
+     FX_CodePage::kChineseSimplified},
+    {0x4a788d72, "STXingkai", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x4bf88566, "SegoeCondensed", "FreeSerif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x4ccf51a4, "BerlinSansFB-Reg", "Droid Sans Japanese,Liberation Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x4ea967ce, "GulimChe",
+     "WenQuanYi Zen Hei Mono,AR PL UKai CN,AR PL UKai HK,AR PL UKai TW,AR PL "
+     "UKai TW MBE",
+     1, FX_CodePage::kMSWin_WesternEuropean},
+    {0x4f68bd79, "LetterGothicStd",
+     "FreeMono,Liberation Mono,Andale Mono,WenQuanYi Micro Hei Mono", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x51a0d0e6, "KozukaGothicPr6NM", "FreeSerif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x531b3dea, "BasemicSymbol", "FreeSerif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x5333fd39, "CalifornianFB-Reg",
+     "Droid Sans Japanese,URW Chancery L,FreeSerif", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x53561a54, "FZYTK--GBK1-0",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, FX_CodePage::kChineseSimplified},
+    {0x55e0dde6, "LucidaSansTypewriter",
+     "Ubuntu Mono,DejaVu Sans Mono,Nimbus Mono L,Liberation Mono,Courier 10 "
+     "Pitch,FreeMono",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x574d4d3d, "AdobeArabic", "Droid Sans Japanese,DejaVu Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x5792e759, "STKaiti", "WenQuanYi Micro Hei Mono", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x5921978e, "LucidaSansUnicode", "Droid Sans Japanese,DejaVu Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x594e2da4, "Vrinda",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Arabic "
+     "Naskh,mry_KacstQurn,Droid Sans Japanese,FreeSans,FreeSerif",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x59baa9a2, "KaiTi_GB2312",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x5cfedf4f, "BaskOldFace",
+     "KacstQurn,Droid Sans Japanese,Ubuntu,Liberation Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x5e16ac91, "TrajanPro",
+     "Nimbus Sans L,AR PL UMing HK,AR PL UKai HK,AR PL UMing TW,AR PL UMing "
+     "TW MBE,DejaVu Sans,DejaVu Serif",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x5f388196, "ITCLegacySansStdMedium",
+     "Liberation Serif,FreeSerif,FreeSans,Ubuntu", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x5f97921c, "AdobeMyungjoStdM",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, FX_CodePage::kChineseSimplified},
+    {0x5fefbfad, "Batang",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x605342b9, "DotumChe",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x608c5f9a, "KaiTi_GB2312",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, FX_CodePage::kChineseSimplified},
+    {0x61efd0d1, "MaturaMTScriptCapitals",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
+     "Sans Japanese,DejaVu Serif,DejaVu Sans",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x626608a9, "MVBoli",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
+     "Ethiopic,Droid Sans Japanese,DejaVu Sans",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x630501a3, "SmallFonts", "DejaVu Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x65d0e2a9, "FZYTK--GBK1-0",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, FX_CodePage::kChineseSimplified},
+    {0x669f29e1, "FZSTK--GBK1-0",
+     "AR PL UMing CN,AR PL UKai CN, AR PL UMing HK", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x673a9e5f, "Tunga",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
+     "Japanese,DejaVu Serif",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x691aa4ce, "NiagaraSolid", "Droid Sans Japanese,DejaVu Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x696259b7, "Corbel", "Droid Sans Japanese,DejaVu Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x696ee9be, "STXihei", "WenQuanYi Micro Hei Mono", 0,
+     FX_CodePage::kChineseSimplified},
+    {0x6c59cf69, "Dotum", "WenQuanYi Zen Hei Mono", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x707fa561, "Gungsuh", "WenQuanYi Zen Hei Mono", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x71416bb2, "ZWAdobeF",
+     "KacstArt,KacstBookm,KacstDecorative,KacstDigital,KacstFarsi,KacstLetter,"
+     "KacstOffice,Dingbats,FreeSerif",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x71b41801, "Verdana",
+     "DejaVu Sans Condensed,Ubuntu Condensed,Droid Sans Japanese,DejaVu Sans",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x73f25e4c, "PalatinoLinotype", "Droid Sans Japanese,FreeSerif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x73f4d19f, "NiagaraEngraved", "Droid Sans Japanese,FreeSerif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x74001694, "MyriadProBlack", "Droid Sans Japanese,AR PL UKai HK", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x74b14d8f, "Haettenschweiler", "Droid Sans Japanese,DejaVu Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x74cb44ee, "NSimSun", "WenQuanYi Zen Hei Mono", 1,
+     FX_CodePage::kChineseSimplified},
+    {0x76b4d7ff, "Shruti",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
+     "Japanese,FreeSans",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x788b3533, "Webdings", "FreeSans", 6, FX_CodePage::kSymbol},
+    {0x797dde99, "MSSerif", "FreeSans", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x7a0f9e9e, "MSMincho",
+     "WenQuanYi Micro Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x7b439caf, "OldEnglishTextMT",
+     "Droid Sans Japanese,Liberation Sans,Ubuntu", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x8213a433, "LucidaSans-Typewriter",
+     "Ubuntu Mono,Liberation Mono,DejaVu Sans Mono", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x82fec929, "AdobeSongStd",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, FX_CodePage::kChineseSimplified},
+    {0x83581825, "Modern", "FreeSans", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x835a2823, "Algerian",
+     "KacstQurn,Droid Sans Japanese,FreeSans,Liberation Sans,Ubuntu", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x83dab9f5, "Script", "FreeSans", 0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x847b56da, "Tahoma",
+     "Droid Sans Japanese,DejaVu Sans Condensed,FreeSerif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x8a783cb2, "SimSun-PUA",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x8b5cac0e, "Onyx", "Droid Sans Japanese,Liberation Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x8c6a499e, "Gulim",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0x8e0af790, "JuiceITC", "Droid Sans Japanese,Liberation Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x8e8d43b2, "Centaur",
+     "KacstQurn,Droid Sans Japanese,Khmer OS,Khmer OS System", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x8ee4dcca, "BookshelfSymbol7", "Liberation Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x90794800, "BellGothicStdLight", "Liberation Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x909b516a, "Century",
+     "Droid Sans Japanese,Liberation Sans,Liberation Mono,Liberation Serif", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x92ae370d, "MSOutlook", "Liberation Sans", 4, FX_CodePage::kSymbol},
+    {0x93c9fbf1, "LucidaFax",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans "
+     "Ethiopic,mry_KacstQurn,Liberation Sans",
+     2, FX_CodePage::kMSWin_WesternEuropean},
+    {0x9565085e, "BookAntiqua",
+     "Droid Sans Japanese,Liberation Sans,Liberation Serif", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x9856d95d, "AdobeMingStd", "AR PL UMing HK", 0, FX_CodePage::kHangul},
+    {0x9bbadd6b, "ColonnaMT",
+     "KacstQurn,Droid Sans Japanese,Khmer OS,Khmer OS System", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x9cbd16a4, "ShowcardGothic-Reg",
+     "Droid Sans Japanese,Liberation Sans,Ubuntu", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0x9d73008e, "MSSansSerif", "FreeSerif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xa0607db1, "GungsuhChe",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     1, FX_CodePage::kMSWin_WesternEuropean},
+    {0xa0bcf6a1, "LatinWide", "FreeSerif", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xa1429b36, "Symbol", "FreeSerif", 6, FX_CodePage::kSymbol},
+    {0xa1fa5abc, "Wingdings2", "FreeSerif", 6, FX_CodePage::kSymbol},
+    {0xa1fa5abd, "Wingdings3", "FreeSerif", 6, FX_CodePage::kSymbol},
+    {0xa427bad4, "InformalRoman-Regular",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
+     "Japanese,FreeSerif",
+     8, FX_CodePage::kMSWin_WesternEuropean},
+    {0xa8b92ece, "FZSTK--GBK1-0", "AR PL UMing CN", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xa8d83ece, "CalifornianFB", "Droid Sans Japanese,FreeSerif", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xaa3e082c, "Kingsoft-Phonetic",
+     "Tibetan Machine Uni,LKLUG,Samyak Gujarati,Droid Sans "
+     "Thai,utkal,Kedage,Mallige,AR PL UKai CN",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xaa6bcabe, "HarlowSolidItalic",
+     "KacstQurn,Droid Sans Japanese,Liberation Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xade5337c, "MSUIGothic",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xb08dd941, "WideLatin",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
+     "Sans Japanese,Liberation Serif",
+     2, FX_CodePage::kMSWin_WesternEuropean},
+    {0xb12765e0, "ITCLegacySansStdBook",
+     "AR PL UMing HK,AR PL UKai HK,FreeSerif,Ubuntu,FreeSans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xb207f05d, "PoorRichard", "Droid Sans Japanese,Liberation Serif", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xb3bc492f, "JuiceITC-Regular", "Droid Sans Japanese,Liberation Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xb5545399, "Marlett", "Liberation Serif", 4, FX_CodePage::kSymbol},
+    {0xb5dd1ebb, "BritannicBold",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans "
+     "Ethiopic,mry_KacstQurn,Liberation Serif",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xb699c1c5, "LucidaCalligraphy-Italic",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
+     "Sans Japanese,DejaVu Serif",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xb725d629, "TimesNewRoman", "Droid Sans Japanese,Liberation Sans", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xb7eaebeb, "AdobeHeitiStdR",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, FX_CodePage::kChineseSimplified},
+    {0xbd29c486, "BerlinSansFBDemi-Bold", "Droid Sans Japanese,DejaVu Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xbe8a8db4, "BookshelfSymbolSeven", "DejaVu Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xc16c0118, "AdobeHebrew", "Droid Sans Japanese,Ubuntu,Liberation Serif",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xc318b0af, "MyriadProLight",
+     "Droid Sans Japanese,AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xc65e5659, "CambriaMath", "Droid Sans Japanese,FreeSerif,FreeMono", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xc75c8f05, "LucidaConsole",
+     "Liberation Mono,DejaVu Sans Mono,FreeMono,WenQuanYi Micro Hei Mono", 1,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xca7c35d6, "Calibri", "Droid Sans Japanese,DejaVu Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xcb053f53, "MicrosoftYaHei",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, FX_CodePage::kChineseSimplified},
+    {0xcb7190f9, "Magneto-Bold",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
+     "Japanese,DejaVu Serif",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xcca00cc5, "System", "DejaVu Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xccad6f76, "Jokerman-Regular", "Droid Sans Japanese,DejaVu Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xccc5818c, "EuroSign", "DejaVu Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xcf3d7234, "LucidaHandwriting-Italic",
+     "Liberation Sans Narrow,Ubuntu Condensed,Nimbus Sans L,DejaVu Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xcf7b8fdb, "MinionPro", "DejaVu Sans", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xcfe5755f, "Simhei",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     1, FX_CodePage::kChineseSimplified},
+    {0xd011f4ee, "MSPGothic",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xd060e7ef, "Vivaldi",
+     "KacstQurn,Droid Sans Japanese,Liberation Sans,Ubuntu", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xd07edec1, "FranklinGothic-Medium", "Droid Sans Japanese,Ubuntu", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xd107243f, "SimSun", "WenQuanYi Zen Hei Mono", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xd1881562, "ArialNarrow",
+     "Liberation Sans Narrow,Droid Sans Japanese,FreeSerif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xd22b7dce, "BodoniMTPosterCompressed", "Droid Sans Japanese,DejaVu Serif",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xd22bfa60, "ComicSansMS", "Droid Sans Japanese,FreeMono,Liberation Mono",
+     8, FX_CodePage::kMSWin_WesternEuropean},
+    {0xd3bd0e35, "Bauhaus93",
+     "KacstQurn,Droid Sans Japanese,Liberation Sans,Ubuntu", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xd429ee7a, "STFangsong", "WenQuanYi Micro Hei Mono", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xd6679c12, "BernardMTCondensed",
+     "KacstQurn,Droid Sans Japanese,Nimbus Sans L,URW Chancery "
+     "L,KacstOne,Liberation Sans",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xd8e8a027, "LucidaSans",
+     "Liberation Sans Narrow,Nimbus Sans L,KacstQurn,Droid Arabic Naskh,Droid "
+     "Sans Ethiopic,DejaVu Serif Condensed,Liberation Mono,Ubuntu",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xd9fe7761, "HighTowerText-Reg",
+     "Droid Sans Japanese,Ubuntu,Liberation Serif", 2,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xda7e551e, "STSong", "WenQuanYi Micro Hei Mono", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdaa6842d, "STZhongsong",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, FX_CodePage::kChineseSimplified},
+    {0xdaaab93f, "STFangsong",
+     "WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
+     "Hei,WenQuanYi Zen Hei Sharp",
+     0, FX_CodePage::kChineseSimplified},
+    {0xdaeb0713, "STSong",
+     "WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
+     "Hei,WenQuanYi Zen Hei Sharp",
+     0, FX_CodePage::kChineseSimplified},
+    {0xdafedbef, "STCaiyun", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdb00a3d9, "Broadway",
+     "KacstQurn,Droid Sans Japanese,DejaVu Sans,FreeMono,Liberation Mono", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xdb1f5ad4, "STXinwei", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdb326e7f, "STKaiti",
+     "WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
+     "Hei,WenQuanYi Zen Hei Sharp",
+     0, FX_CodePage::kChineseSimplified},
+    {0xdb69595a, "STHupo",
+     "WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
+     "Hei,WenQuanYi Zen Hei Sharp",
+     0, FX_CodePage::kChineseSimplified},
+    {0xdba0082c, "STXihei",
+     " WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
+     "Hei,WenQuanYi Zen Hei Sharp",
+     0, FX_CodePage::kChineseSimplified},
+    {0xdbd0ab18, "STXingkai", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdc1a7db1, "STLiti", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xdc33075f, "KristenITC-Regular",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,DejaVu Sans "
+     "Condensed,Ubuntu,Liberation Sans",
+     8, FX_CodePage::kMSWin_WesternEuropean},
+    {0xdcc7009c, "Harrington",
+     "KacstQurn,Droid Sans Japanese,Liberation Serif,FreeSerif,Ubuntu", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xdd712466, "ArialBlack",
+     "Droid Sans Japanese,DejaVu Sans,DejaVu Serif,FreeMono", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xdde87b3e, "Impact", "Droid Sans Japanese,DejaVu Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xdf69fb32, "SnapITC",
+     "Liberation Sans Narrow,Ubuntu Condensed,DejaVu Sans,DejaVu "
+     "Serif,FreeMono",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xdf8b25e8, "CenturyGothic",
+     "Droid Sans Japanese,Liberation Mono,Liberation Sans,Liberation Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xe0f705c0, "KristenITC",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,DejaVu Sans "
+     "Condensed,Ubuntu,Liberation Sans",
+     8, FX_CodePage::kMSWin_WesternEuropean},
+    {0xe1427573, "Raavi",
+     "Droid Arabic Naskh,Droid Sans "
+     "Ethiopic,mry_KacstQurn,FreeSerif,Liberation Serif,Khmer OS",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xe2cea0cb, "Magneto",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,DejaVu "
+     "Serif,DejaVu Serif Condensed,DejaVu Sans",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xe36a9e17, "Ravie",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,DejaVu "
+     "Serif,DejaVu Sans,FreeMono",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xe433f8e2, "Parchment", "Droid Sans Japanese,DejaVu Serif", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xe43dff4a, "Wingdings", "DejaVu Serif", 4, FX_CodePage::kSymbol},
+    {0xe4e2c405, "MTExtra", "DejaVu Serif", 6, FX_CodePage::kSymbol},
+    {0xe618cc35, "InformalRoman",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
+     "Japanese,Nimbus Sans L,DejaVu Sans Condensed,Ubuntu,Liberation Sans",
+     8, FX_CodePage::kMSWin_WesternEuropean},
+    {0xe6c27ffc, "Mistral", "Droid Sans Japanese,DejaVu Serif", 8,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xe7ebf4b9, "Courier", "DejaVu Sans,DejaVu Sans Condensed,FreeSerif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xe8bc4a9d, "MSReferenceSpecialty", "DejaVu Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xe90fb013, "TempusSansITC",
+     "Droid Sans Japanese,Ubuntu,Liberation Serif,FreeSerif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xec637b42, "Consolas",
+     "DejaVu Sans Condensed,AR PL UKai CN,AR PL UKai HK,AR PL UKai "
+     "TW,FreeSerif,FreeSans",
+     1, FX_CodePage::kMSWin_WesternEuropean},
+    {0xed3a683b, "STXinwei", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
+     FX_CodePage::kChineseSimplified},
+    {0xef264cd1, "LucidaHandwriting",
+     "Liberation Sans Narrow,Ubuntu Condensed,Nimbus Sans "
+     "L,KacstQurn,Liberation Mono",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xf086bca2, "BaskervilleOldFace",
+     "KacstQurn,Droid Sans Japanese,Liberation Serif,Ubuntu,FreeSerif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xf1028030, "Mangal",
+     "FreeSans,TSCu_Paranar,Garuda,Liberation Sans,Liberation Sans "
+     "Narrow,Nimbus Sans ",
+     2, FX_CodePage::kMSWin_WesternEuropean},
+    {0xf1da7eb9, "ShowcardGothic",
+     "Droid Sans Japanese,DejaVu Serif Condensed,DejaVu Sans "
+     "Condensed,Liberation Sans,Ubuntu",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xf210f06a, "ArialMT",
+     "Liberation Sans,Liberation Sans Narrow,FreeSans,Nimbus Sans L,Khmer OS "
+     "System,Khmer OS",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xf477f16a, "Latha",
+     "Liberation Sans Narrow,Nimbus Sans L,Droid Arabic "
+     "Naskh,mry_KacstQurn,FreeSerif,Nimbus Sans ",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xf616f3dd, "LiSu",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
+     "PL UMing TW MBE",
+     1, FX_CodePage::kChineseSimplified},
+    {0xfa479aa6, "MicrosoftYaHei",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, FX_CodePage::kChineseSimplified},
+    {0xfcd19697, "BookmanOldStyle",
+     "Droid Sans Japanese,Liberation Mono,Liberation Sans,Liberation Serif", 0,
+     FX_CodePage::kMSWin_WesternEuropean},
+    {0xfe209a82, "LucidaCalligraphy",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
+     "Sans Japanese,DejaVu Serif,DejaVu Sans,FreeMono",
+     0, FX_CodePage::kMSWin_WesternEuropean},
+    {0xfef135f8, "AdobeHeitiStd-Regular",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, FX_CodePage::kChineseSimplified},
+};
+#else
+#error "Unsupported platform"
 #endif
 
 }  // namespace
@@ -1859,41 +2437,48 @@
   // ascending order, the correct entry is the first one with an end greater,
   // aka after, the value.
   auto* result = std::upper_bound(
-      std::begin(g_FXGdiFontUSBTable), std::end(g_FXGdiFontUSBTable), unicode,
+      std::begin(kFXGdiFontUSBTable), std::end(kFXGdiFontUSBTable), unicode,
       [](const wchar_t unicode, const FGAS_FONTUSB& iter) {
         return iter.wEndUnicode > unicode;
       });
-  if (result != std::end(g_FXGdiFontUSBTable) &&
+  if (result != std::end(kFXGdiFontUSBTable) &&
       result->wStartUnicode <= unicode && result->wEndUnicode >= unicode)
     return result;
   return nullptr;
 }
 
-WideString FGAS_FontNameToEnglishName(WideStringView wsLocalName) {
-  uint32_t dwLocalNameHash = FX_HashCode_GetW(wsLocalName, true);
-  const FGAS_FontInfo* pEnd = g_XFAFontsMap + FX_ArraySize(g_XFAFontsMap);
+WideString FGAS_FontNameToEnglishName(const WideString& wsLocalName) {
+  uint32_t dwLocalNameHash =
+      FX_HashCode_GetLoweredW(wsLocalName.AsStringView());
+  const FGAS_FontInfo* pBegin = std::begin(kXFAFontsMap);
+  const FGAS_FontInfo* pEnd = std::end(kXFAFontsMap);
   const FGAS_FontInfo* pFontInfo =
-      std::lower_bound(g_XFAFontsMap, 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 WideString(wsLocalName);
+  }
+  return wsLocalName;
 }
 
 const FGAS_FontInfo* FGAS_FontInfoByFontName(WideStringView wsFontName) {
   WideString wsFontNameTemp(wsFontName);
   wsFontNameTemp.Remove(L' ');
   uint32_t dwCurFontNameHash =
-      FX_HashCode_GetW(wsFontNameTemp.AsStringView(), true);
-  const FGAS_FontInfo* pEnd = g_XFAFontsMap + FX_ArraySize(g_XFAFontsMap);
+      FX_HashCode_GetLoweredW(wsFontNameTemp.AsStringView());
+  const FGAS_FontInfo* pBegin = std::begin(kXFAFontsMap);
+  const FGAS_FontInfo* pEnd = std::end(kXFAFontsMap);
   const FGAS_FontInfo* pFontInfo =
-      std::lower_bound(g_XFAFontsMap, 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/font/fgas_fontutils.h b/xfa/fgas/font/fgas_fontutils.h
index 91998d5..9ae25a3 100644
--- a/xfa/fgas/font/fgas_fontutils.h
+++ b/xfa/fgas/font/fgas_fontutils.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,18 @@
 #ifndef XFA_FGAS_FONT_FGAS_FONTUTILS_H_
 #define XFA_FGAS_FONT_FGAS_FONTUTILS_H_
 
+#include <stdint.h>
+
+#include "core/fxcrt/fx_codepage_forward.h"
 #include "core/fxcrt/widestring.h"
 
 struct FGAS_FONTUSB {
+  static constexpr uint16_t kNoBitField = 999;
+
   uint16_t wStartUnicode;
   uint16_t wEndUnicode;
   uint16_t wBitField;
-  uint16_t wCodePage;
+  FX_CodePage wCodePage;
 };
 
 const FGAS_FONTUSB* FGAS_GetUnicodeBitField(wchar_t wUnicode);
@@ -23,11 +28,10 @@
   const char* pPsName;       // Raw, POD struct.
   const char* pReplaceFont;  // Raw, POD struct.
   uint16_t dwStyles;
-  uint16_t wCodePage;
+  FX_CodePage wCodePage;
 };
 
-WideString FGAS_FontNameToEnglishName(WideStringView wsLocalName);
-
+WideString FGAS_FontNameToEnglishName(const WideString& wsLocalName);
 const FGAS_FontInfo* FGAS_FontInfoByFontName(WideStringView wsFontName);
 
 #endif  // XFA_FGAS_FONT_FGAS_FONTUTILS_H_
diff --git a/xfa/fgas/font/fgas_fontutils_unittest.cpp b/xfa/fgas/font/fgas_fontutils_unittest.cpp
new file mode 100644
index 0000000..eb11558
--- /dev/null
+++ b/xfa/fgas/font/fgas_fontutils_unittest.cpp
@@ -0,0 +1,60 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fgas/font/fgas_fontutils.h"
+
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/widestring.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(FGAS, GetUnicodeBitField) {
+  const auto* pResult = FGAS_GetUnicodeBitField(0);
+  ASSERT_TRUE(pResult);
+  EXPECT_EQ(0u, pResult->wBitField);
+  EXPECT_EQ(FX_CodePage::kMSWin_WesternEuropean, pResult->wCodePage);
+
+  pResult = FGAS_GetUnicodeBitField(65535);
+  EXPECT_FALSE(pResult);
+
+  // Try arbitrary values.
+  pResult = FGAS_GetUnicodeBitField(1313);
+  ASSERT_TRUE(pResult);
+  EXPECT_EQ(9u, pResult->wBitField);
+  EXPECT_EQ(FX_CodePage::kFailure, pResult->wCodePage);
+
+  pResult = FGAS_GetUnicodeBitField(14321);
+  ASSERT_TRUE(pResult);
+  EXPECT_EQ(59u, pResult->wBitField);
+  EXPECT_EQ(FX_CodePage::kFailure, pResult->wCodePage);
+}
+
+TEST(FGAS, FontNameToEnglishName) {
+  // These aren't found with spaces.
+  WideString result = FGAS_FontNameToEnglishName(L"Myriad Pro");
+  EXPECT_EQ(L"Myriad Pro", result);
+
+  result = FGAS_FontNameToEnglishName(L"mYriad pRo");
+  EXPECT_EQ(L"mYriad pRo", result);
+
+  result = FGAS_FontNameToEnglishName(L"MyriadPro");
+  EXPECT_EQ(L"MyriadPro", result);
+
+  result = FGAS_FontNameToEnglishName(L"mYriadpRo");
+  EXPECT_EQ(L"MyriadPro", result);
+}
+
+TEST(FGAS, FontInfoByFontName) {
+  // And yet, these are found despite spaces.
+  const auto* result = FGAS_FontInfoByFontName(L"Myriad Pro");
+  EXPECT_TRUE(result);
+
+  result = FGAS_FontInfoByFontName(L"mYriad pRo");
+  EXPECT_TRUE(result);
+
+  result = FGAS_FontInfoByFontName(L"MyriadPro");
+  EXPECT_TRUE(result);
+
+  result = FGAS_FontInfoByFontName(L"mYriadpRo");
+  EXPECT_TRUE(result);
+}
diff --git a/xfa/fgas/graphics/BUILD.gn b/xfa/fgas/graphics/BUILD.gn
new file mode 100644
index 0000000..a1408c4
--- /dev/null
+++ b/xfa/fgas/graphics/BUILD.gn
@@ -0,0 +1,32 @@
+# Copyright 2018 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../../pdfium.gni")
+
+assert(pdf_enable_xfa)
+
+source_set("graphics") {
+  sources = [
+    "cfgas_gecolor.cpp",
+    "cfgas_gecolor.h",
+    "cfgas_gegraphics.cpp",
+    "cfgas_gegraphics.h",
+    "cfgas_gepath.cpp",
+    "cfgas_gepath.h",
+    "cfgas_gepattern.cpp",
+    "cfgas_gepattern.h",
+    "cfgas_geshading.cpp",
+    "cfgas_geshading.h",
+  ]
+  configs += [
+    "../../../:pdfium_strict_config",
+    "../../../:pdfium_noshorten_config",
+    "../../:xfa_warnings",
+  ]
+  deps = [
+    "../../../core/fxcrt",
+    "../../../core/fxge",
+  ]
+  visibility = [ "../../../*" ]
+}
diff --git a/xfa/fgas/graphics/cfgas_gecolor.cpp b/xfa/fgas/graphics/cfgas_gecolor.cpp
new file mode 100644
index 0000000..861d2c3
--- /dev/null
+++ b/xfa/fgas/graphics/cfgas_gecolor.cpp
@@ -0,0 +1,22 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/graphics/cfgas_gecolor.h"
+
+CFGAS_GEColor::CFGAS_GEColor(const FX_ARGB argb)
+    : m_type(Solid), m_argb(argb) {}
+
+CFGAS_GEColor::CFGAS_GEColor(CFGAS_GEPattern* pattern, const FX_ARGB argb)
+    : m_type(Pattern), m_argb(argb), m_pPattern(pattern) {}
+
+CFGAS_GEColor::CFGAS_GEColor(CFGAS_GEShading* shading)
+    : m_type(Shading), m_pShading(shading) {}
+
+CFGAS_GEColor::CFGAS_GEColor(const CFGAS_GEColor& that) = default;
+
+CFGAS_GEColor::~CFGAS_GEColor() = default;
+
+CFGAS_GEColor& CFGAS_GEColor::operator=(const CFGAS_GEColor& that) = default;
diff --git a/xfa/fgas/graphics/cfgas_gecolor.h b/xfa/fgas/graphics/cfgas_gecolor.h
new file mode 100644
index 0000000..cbd6ed7
--- /dev/null
+++ b/xfa/fgas/graphics/cfgas_gecolor.h
@@ -0,0 +1,51 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_GRAPHICS_CFGAS_GECOLOR_H_
+#define XFA_FGAS_GRAPHICS_CFGAS_GECOLOR_H_
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+
+class CFGAS_GEPattern;
+class CFGAS_GEShading;
+
+class CFGAS_GEColor {
+ public:
+  enum Type { Invalid, Solid, Pattern, Shading };
+
+  explicit CFGAS_GEColor(const FX_ARGB argb);
+  explicit CFGAS_GEColor(CFGAS_GEShading* shading);
+  CFGAS_GEColor(CFGAS_GEPattern* pattern, const FX_ARGB argb);
+  CFGAS_GEColor(const CFGAS_GEColor& that);
+  ~CFGAS_GEColor();
+
+  Type GetType() const { return m_type; }
+  FX_ARGB GetArgb() const {
+    DCHECK(m_type == Solid || m_type == Pattern);
+    return m_argb;
+  }
+  CFGAS_GEPattern* GetPattern() const {
+    DCHECK_EQ(m_type, Pattern);
+    return m_pPattern;
+  }
+  CFGAS_GEShading* GetShading() const {
+    DCHECK_EQ(m_type, Shading);
+    return m_pShading;
+  }
+
+  CFGAS_GEColor& operator=(const CFGAS_GEColor& that);
+
+ private:
+  Type m_type = Invalid;
+  FX_ARGB m_argb = 0;
+  UnownedPtr<CFGAS_GEPattern> m_pPattern;
+  UnownedPtr<CFGAS_GEShading> m_pShading;
+};
+
+#endif  // XFA_FGAS_GRAPHICS_CFGAS_GECOLOR_H_
diff --git a/xfa/fgas/graphics/cfgas_gegraphics.cpp b/xfa/fgas/graphics/cfgas_gegraphics.cpp
new file mode 100644
index 0000000..cb2b959
--- /dev/null
+++ b/xfa/fgas/graphics/cfgas_gegraphics.cpp
@@ -0,0 +1,439 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+
+#include <math.h>
+
+#include <iterator>
+#include <memory>
+#include <utility>
+
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/cfx_renderdevice.h"
+#include "core/fxge/cfx_unicodeencoding.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "third_party/base/check.h"
+#include "xfa/fgas/graphics/cfgas_gecolor.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
+#include "xfa/fgas/graphics/cfgas_gepattern.h"
+#include "xfa/fgas/graphics/cfgas_geshading.h"
+
+namespace {
+
+struct FX_HATCHDATA {
+  int32_t width;
+  int32_t height;
+  uint8_t maskBits[64];
+};
+
+const FX_HATCHDATA kHatchBitmapData[] = {
+    {16,  // Horizontal
+     16,
+     {
+         0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+         0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     }},
+    {16,  // Vertical
+     16,
+     {
+         0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
+         0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80,
+         0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
+         0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
+         0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
+         0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
+     }},
+    {16,  // ForwardDiagonal
+     16,
+     {
+         0x80, 0x80, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x20, 0x00,
+         0x00, 0x10, 0x10, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04, 0x04,
+         0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x80,
+         0x80, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00,
+         0x10, 0x10, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04, 0x04, 0x00,
+         0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
+     }},
+    {16,  // BackwardDiagonal
+     16,
+     {
+         0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x04, 0x04, 0x00,
+         0x00, 0x08, 0x08, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x20, 0x20,
+         0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x01,
+         0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00,
+         0x08, 0x08, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x20, 0x20, 0x00,
+         0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
+     }},
+    {16,  // Cross
+     16,
+     {
+         0xff, 0xff, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
+         0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80,
+         0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0xff,
+         0xff, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
+         0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
+         0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
+     }},
+    {16,  // DiagonalCross
+     16,
+     {
+         0x81, 0x81, 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x24, 0x24, 0x00,
+         0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x24, 0x24,
+         0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00, 0x81,
+         0x81, 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x24, 0x24, 0x00, 0x00,
+         0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x24, 0x24, 0x00,
+         0x00, 0x42, 0x42, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00,
+     }},
+};
+
+const FX_HATCHDATA kHatchPlaceHolder = {
+    0,
+    0,
+    {
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    }};
+
+const FX_HATCHDATA& GetHatchBitmapData(size_t index) {
+  return index < std::size(kHatchBitmapData) ? kHatchBitmapData[index]
+                                             : kHatchPlaceHolder;
+}
+
+}  // namespace
+
+CFGAS_GEGraphics::CFGAS_GEGraphics(CFX_RenderDevice* renderDevice)
+    : m_renderDevice(renderDevice) {
+  DCHECK(m_renderDevice);
+}
+
+CFGAS_GEGraphics::~CFGAS_GEGraphics() = default;
+
+void CFGAS_GEGraphics::SaveGraphState() {
+  m_renderDevice->SaveState();
+  m_infoStack.push_back(std::make_unique<TInfo>(m_info));
+}
+
+void CFGAS_GEGraphics::RestoreGraphState() {
+  m_renderDevice->RestoreState(false);
+  CHECK(!m_infoStack.empty());
+  m_info = *m_infoStack.back();
+  m_infoStack.pop_back();
+  return;
+}
+
+void CFGAS_GEGraphics::SetLineCap(CFX_GraphStateData::LineCap lineCap) {
+  m_info.graphState.m_LineCap = lineCap;
+}
+
+void CFGAS_GEGraphics::SetLineDash(float dashPhase,
+                                   pdfium::span<const float> dashArray) {
+  DCHECK(!dashArray.empty());
+  float scale = m_info.isActOnDash ? m_info.graphState.m_LineWidth : 1.0;
+  m_info.graphState.m_DashPhase = dashPhase;
+  m_info.graphState.m_DashArray.resize(dashArray.size());
+  for (size_t i = 0; i < dashArray.size(); ++i)
+    m_info.graphState.m_DashArray[i] = dashArray[i] * scale;
+}
+
+void CFGAS_GEGraphics::SetSolidLineDash() {
+  m_info.graphState.m_DashArray.clear();
+}
+
+void CFGAS_GEGraphics::SetLineWidth(float lineWidth) {
+  m_info.graphState.m_LineWidth = lineWidth;
+}
+
+void CFGAS_GEGraphics::EnableActOnDash() {
+  m_info.isActOnDash = true;
+}
+
+void CFGAS_GEGraphics::SetStrokeColor(const CFGAS_GEColor& color) {
+  m_info.strokeColor = color;
+}
+
+void CFGAS_GEGraphics::SetFillColor(const CFGAS_GEColor& color) {
+  m_info.fillColor = color;
+}
+
+void CFGAS_GEGraphics::StrokePath(const CFGAS_GEPath& path,
+                                  const CFX_Matrix& matrix) {
+  RenderDeviceStrokePath(path, matrix);
+}
+
+void CFGAS_GEGraphics::FillPath(const CFGAS_GEPath& path,
+                                CFX_FillRenderOptions::FillType fill_type,
+                                const CFX_Matrix& matrix) {
+  RenderDeviceFillPath(path, fill_type, matrix);
+}
+
+void CFGAS_GEGraphics::ConcatMatrix(const CFX_Matrix& matrix) {
+  m_info.CTM.Concat(matrix);
+}
+
+const CFX_Matrix* CFGAS_GEGraphics::GetMatrix() const {
+  return &m_info.CTM;
+}
+
+CFX_RectF CFGAS_GEGraphics::GetClipRect() const {
+  FX_RECT r = m_renderDevice->GetClipBox();
+  return CFX_RectF(r.left, r.top, r.Width(), r.Height());
+}
+
+void CFGAS_GEGraphics::SetClipRect(const CFX_RectF& rect) {
+  m_renderDevice->SetClip_Rect(
+      FX_RECT(FXSYS_roundf(rect.left), FXSYS_roundf(rect.top),
+              FXSYS_roundf(rect.right()), FXSYS_roundf(rect.bottom())));
+}
+
+CFX_RenderDevice* CFGAS_GEGraphics::GetRenderDevice() {
+  return m_renderDevice;
+}
+
+void CFGAS_GEGraphics::RenderDeviceStrokePath(const CFGAS_GEPath& path,
+                                              const CFX_Matrix& matrix) {
+  if (m_info.strokeColor.GetType() != CFGAS_GEColor::Solid)
+    return;
+
+  CFX_Matrix m = m_info.CTM;
+  m.Concat(matrix);
+  m_renderDevice->DrawPath(path.GetPath(), &m, &m_info.graphState, 0x0,
+                           m_info.strokeColor.GetArgb(),
+                           CFX_FillRenderOptions());
+}
+
+void CFGAS_GEGraphics::RenderDeviceFillPath(
+    const CFGAS_GEPath& path,
+    CFX_FillRenderOptions::FillType fill_type,
+    const CFX_Matrix& matrix) {
+  CFX_Matrix m = m_info.CTM;
+  m.Concat(matrix);
+
+  const CFX_FillRenderOptions fill_options(fill_type);
+  switch (m_info.fillColor.GetType()) {
+    case CFGAS_GEColor::Solid:
+      m_renderDevice->DrawPath(path.GetPath(), &m, &m_info.graphState,
+                               m_info.fillColor.GetArgb(), 0x0, fill_options);
+      return;
+    case CFGAS_GEColor::Pattern:
+      FillPathWithPattern(path, fill_options, m);
+      return;
+    case CFGAS_GEColor::Shading:
+      FillPathWithShading(path, fill_options, m);
+      return;
+    default:
+      return;
+  }
+}
+
+void CFGAS_GEGraphics::FillPathWithPattern(
+    const CFGAS_GEPath& path,
+    const CFX_FillRenderOptions& fill_options,
+    const CFX_Matrix& matrix) {
+  RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap();
+  int32_t width = bitmap->GetWidth();
+  int32_t height = bitmap->GetHeight();
+  auto bmp = pdfium::MakeRetain<CFX_DIBitmap>();
+  bmp->Create(width, height, FXDIB_Format::kArgb);
+  m_renderDevice->GetDIBits(bmp, 0, 0);
+
+  CFGAS_GEPattern::HatchStyle hatchStyle =
+      m_info.fillColor.GetPattern()->GetHatchStyle();
+  const FX_HATCHDATA& data =
+      GetHatchBitmapData(static_cast<size_t>(hatchStyle));
+
+  auto mask = pdfium::MakeRetain<CFX_DIBitmap>();
+  mask->Create(data.width, data.height, FXDIB_Format::k1bppMask);
+  fxcrt::spancpy(
+      mask->GetWritableBuffer(),
+      pdfium::make_span(data.maskBits).first(mask->GetPitch() * data.height));
+  const CFX_FloatRect rectf =
+      matrix.TransformRect(path.GetPath().GetBoundingBox());
+  const FX_RECT rect = rectf.ToRoundedFxRect();
+
+  CFX_DefaultRenderDevice device;
+  device.Attach(bmp);
+  device.FillRect(rect, m_info.fillColor.GetPattern()->GetBackArgb());
+  for (int32_t j = rect.bottom; j < rect.top; j += mask->GetHeight()) {
+    for (int32_t i = rect.left; i < rect.right; i += mask->GetWidth()) {
+      device.SetBitMask(mask, i, j,
+                        m_info.fillColor.GetPattern()->GetForeArgb());
+    }
+  }
+  CFX_RenderDevice::StateRestorer restorer(m_renderDevice);
+  m_renderDevice->SetClip_PathFill(path.GetPath(), &matrix, fill_options);
+  SetDIBitsWithMatrix(std::move(bmp), CFX_Matrix());
+}
+
+void CFGAS_GEGraphics::FillPathWithShading(
+    const CFGAS_GEPath& path,
+    const CFX_FillRenderOptions& fill_options,
+    const CFX_Matrix& matrix) {
+  RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap();
+  int32_t width = bitmap->GetWidth();
+  int32_t height = bitmap->GetHeight();
+  float start_x = m_info.fillColor.GetShading()->GetBeginPoint().x;
+  float start_y = m_info.fillColor.GetShading()->GetBeginPoint().y;
+  float end_x = m_info.fillColor.GetShading()->GetEndPoint().x;
+  float end_y = m_info.fillColor.GetShading()->GetEndPoint().y;
+  auto bmp = pdfium::MakeRetain<CFX_DIBitmap>();
+  bmp->Create(width, height, FXDIB_Format::kArgb);
+  m_renderDevice->GetDIBits(bmp, 0, 0);
+  bool result = false;
+  switch (m_info.fillColor.GetShading()->GetType()) {
+    case CFGAS_GEShading::Type::kAxial: {
+      float x_span = end_x - start_x;
+      float y_span = end_y - start_y;
+      float axis_len_square = (x_span * x_span) + (y_span * y_span);
+      for (int32_t row = 0; row < height; row++) {
+        uint32_t* dib_buf =
+            reinterpret_cast<uint32_t*>(bmp->GetWritableScanline(row).data());
+        for (int32_t column = 0; column < width; column++) {
+          float scale = 0.0f;
+          if (axis_len_square) {
+            float y = static_cast<float>(row);
+            float x = static_cast<float>(column);
+            scale = (((x - start_x) * x_span) + ((y - start_y) * y_span)) /
+                    axis_len_square;
+            if (isnan(scale) || scale < 0.0f) {
+              if (!m_info.fillColor.GetShading()->IsExtendedBegin())
+                continue;
+              scale = 0.0f;
+            } else if (scale > 1.0f) {
+              if (!m_info.fillColor.GetShading()->IsExtendedEnd())
+                continue;
+              scale = 1.0f;
+            }
+          }
+          int32_t index =
+              static_cast<int32_t>(scale * (CFGAS_GEShading::kSteps - 1));
+          dib_buf[column] = m_info.fillColor.GetShading()->GetArgb(index);
+        }
+      }
+      result = true;
+      break;
+    }
+    case CFGAS_GEShading::Type::kRadial: {
+      float start_r = m_info.fillColor.GetShading()->GetBeginRadius();
+      float end_r = m_info.fillColor.GetShading()->GetEndRadius();
+      float a = ((start_x - end_x) * (start_x - end_x)) +
+                ((start_y - end_y) * (start_y - end_y)) -
+                ((start_r - end_r) * (start_r - end_r));
+      for (int32_t row = 0; row < height; row++) {
+        uint32_t* dib_buf =
+            reinterpret_cast<uint32_t*>(bmp->GetWritableScanline(row).data());
+        for (int32_t column = 0; column < width; column++) {
+          float x = (float)(column);
+          float y = (float)(row);
+          float b = -2 * (((x - start_x) * (end_x - start_x)) +
+                          ((y - start_y) * (end_y - start_y)) +
+                          (start_r * (end_r - start_r)));
+          float c = ((x - start_x) * (x - start_x)) +
+                    ((y - start_y) * (y - start_y)) - (start_r * start_r);
+          float s;
+          if (a == 0) {
+            s = -c / b;
+          } else {
+            float b2_4ac = (b * b) - 4 * (a * c);
+            if (b2_4ac < 0) {
+              continue;
+            }
+            float root = (sqrt(b2_4ac));
+            float s1;
+            float s2;
+            if (a > 0) {
+              s1 = (-b - root) / (2 * a);
+              s2 = (-b + root) / (2 * a);
+            } else {
+              s2 = (-b - root) / (2 * a);
+              s1 = (-b + root) / (2 * a);
+            }
+            if (s2 <= 1.0f || m_info.fillColor.GetShading()->IsExtendedEnd()) {
+              s = (s2);
+            } else {
+              s = (s1);
+            }
+            if ((start_r) + s * (end_r - start_r) < 0) {
+              continue;
+            }
+          }
+          if (isnan(s) || s < 0.0f) {
+            if (!m_info.fillColor.GetShading()->IsExtendedBegin())
+              continue;
+            s = 0.0f;
+          }
+          if (s > 1.0f) {
+            if (!m_info.fillColor.GetShading()->IsExtendedEnd())
+              continue;
+            s = 1.0f;
+          }
+          int index = static_cast<int32_t>(s * (CFGAS_GEShading::kSteps - 1));
+          dib_buf[column] = m_info.fillColor.GetShading()->GetArgb(index);
+        }
+      }
+      result = true;
+      break;
+    }
+  }
+  if (result) {
+    CFX_RenderDevice::StateRestorer restorer(m_renderDevice);
+    m_renderDevice->SetClip_PathFill(path.GetPath(), &matrix, fill_options);
+    SetDIBitsWithMatrix(std::move(bmp), matrix);
+  }
+}
+
+void CFGAS_GEGraphics::SetDIBitsWithMatrix(RetainPtr<CFX_DIBBase> source,
+                                           const CFX_Matrix& matrix) {
+  if (matrix.IsIdentity()) {
+    m_renderDevice->SetDIBits(source, 0, 0);
+  } else {
+    CFX_Matrix m((float)source->GetWidth(), 0, 0, (float)source->GetHeight(), 0,
+                 0);
+    m.Concat(matrix);
+    int32_t left;
+    int32_t top;
+    RetainPtr<CFX_DIBitmap> bmp1 = source->FlipImage(false, true);
+    RetainPtr<CFX_DIBitmap> bmp2 = bmp1->TransformTo(m, &left, &top);
+    m_renderDevice->SetDIBits(bmp2, left, top);
+  }
+}
+
+CFGAS_GEGraphics::TInfo::TInfo() = default;
+
+CFGAS_GEGraphics::TInfo::TInfo(const TInfo& info)
+    : graphState(info.graphState),
+      CTM(info.CTM),
+      isActOnDash(info.isActOnDash),
+      strokeColor(info.strokeColor),
+      fillColor(info.fillColor) {}
+
+CFGAS_GEGraphics::TInfo& CFGAS_GEGraphics::TInfo::operator=(
+    const TInfo& other) {
+  graphState = other.graphState;
+  CTM = other.CTM;
+  isActOnDash = other.isActOnDash;
+  strokeColor = other.strokeColor;
+  fillColor = other.fillColor;
+  return *this;
+}
+
+CFGAS_GEGraphics::StateRestorer::StateRestorer(CFGAS_GEGraphics* graphics)
+    : graphics_(graphics) {
+  graphics_->SaveGraphState();
+}
+
+CFGAS_GEGraphics::StateRestorer::~StateRestorer() {
+  graphics_->RestoreGraphState();
+}
diff --git a/xfa/fgas/graphics/cfgas_gegraphics.h b/xfa/fgas/graphics/cfgas_gegraphics.h
new file mode 100644
index 0000000..8e4e408
--- /dev/null
+++ b/xfa/fgas/graphics/cfgas_gegraphics.h
@@ -0,0 +1,95 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_GRAPHICS_CFGAS_GEGRAPHICS_H_
+#define XFA_FGAS_GRAPHICS_CFGAS_GEGRAPHICS_H_
+
+#include <memory>
+#include <vector>
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "core/fxge/cfx_graphstatedata.h"
+#include "third_party/base/containers/span.h"
+#include "xfa/fgas/graphics/cfgas_gecolor.h"
+
+class CFGAS_GEPath;
+class CFX_DIBBase;
+class CFX_RenderDevice;
+
+class CFGAS_GEGraphics {
+ public:
+  class StateRestorer {
+   public:
+    FX_STACK_ALLOCATED();
+
+    explicit StateRestorer(CFGAS_GEGraphics* graphics);
+    ~StateRestorer();
+
+   private:
+    UnownedPtr<CFGAS_GEGraphics> const graphics_;
+  };
+
+  explicit CFGAS_GEGraphics(CFX_RenderDevice* renderDevice);
+  ~CFGAS_GEGraphics();
+
+  CFX_RectF GetClipRect() const;
+  const CFX_Matrix* GetMatrix() const;
+  CFX_RenderDevice* GetRenderDevice();
+
+  void SetLineCap(CFX_GraphStateData::LineCap lineCap);
+  void SetLineDash(float dashPhase, pdfium::span<const float> dashArray);
+  void SetSolidLineDash();
+  void SetLineWidth(float lineWidth);
+  void EnableActOnDash();
+  void SetStrokeColor(const CFGAS_GEColor& color);
+  void SetFillColor(const CFGAS_GEColor& color);
+  void SetClipRect(const CFX_RectF& rect);
+  void StrokePath(const CFGAS_GEPath& path, const CFX_Matrix& matrix);
+  void FillPath(const CFGAS_GEPath& path,
+                CFX_FillRenderOptions::FillType fill_type,
+                const CFX_Matrix& matrix);
+  void ConcatMatrix(const CFX_Matrix& matrix);
+
+ private:
+  struct TInfo {
+    TInfo();
+    explicit TInfo(const TInfo& info);
+    TInfo& operator=(const TInfo& other);
+
+    CFX_GraphStateData graphState;
+    CFX_Matrix CTM;
+    bool isActOnDash = false;
+    CFGAS_GEColor strokeColor{nullptr};
+    CFGAS_GEColor fillColor{nullptr};
+  };
+
+  void SaveGraphState();
+  void RestoreGraphState();
+
+  void RenderDeviceStrokePath(const CFGAS_GEPath& path,
+                              const CFX_Matrix& matrix);
+  void RenderDeviceFillPath(const CFGAS_GEPath& path,
+                            CFX_FillRenderOptions::FillType fill_type,
+                            const CFX_Matrix& matrix);
+  void FillPathWithPattern(const CFGAS_GEPath& path,
+                           const CFX_FillRenderOptions& fill_options,
+                           const CFX_Matrix& matrix);
+  void FillPathWithShading(const CFGAS_GEPath& path,
+                           const CFX_FillRenderOptions& fill_options,
+                           const CFX_Matrix& matrix);
+  void SetDIBitsWithMatrix(RetainPtr<CFX_DIBBase> source,
+                           const CFX_Matrix& matrix);
+
+  UnownedPtr<CFX_RenderDevice> const m_renderDevice;
+  TInfo m_info;
+  std::vector<std::unique_ptr<TInfo>> m_infoStack;
+};
+
+#endif  // XFA_FGAS_GRAPHICS_CFGAS_GEGRAPHICS_H_
diff --git a/xfa/fgas/graphics/cfgas_gepath.cpp b/xfa/fgas/graphics/cfgas_gepath.cpp
new file mode 100644
index 0000000..8e1a58c
--- /dev/null
+++ b/xfa/fgas/graphics/cfgas_gepath.cpp
@@ -0,0 +1,151 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/graphics/cfgas_gepath.h"
+
+#include <math.h>
+
+#include "core/fxcrt/fx_system.h"
+#include "core/fxge/cfx_path.h"
+
+CFGAS_GEPath::CFGAS_GEPath() = default;
+
+CFGAS_GEPath::~CFGAS_GEPath() = default;
+
+void CFGAS_GEPath::Clear() {
+  path_.Clear();
+}
+
+void CFGAS_GEPath::Close() {
+  path_.ClosePath();
+}
+
+void CFGAS_GEPath::MoveTo(const CFX_PointF& point) {
+  path_.AppendPoint(point, CFX_Path::Point::Type::kMove);
+}
+
+void CFGAS_GEPath::LineTo(const CFX_PointF& point) {
+  path_.AppendPoint(point, CFX_Path::Point::Type::kLine);
+}
+
+void CFGAS_GEPath::BezierTo(const CFX_PointF& c1,
+                            const CFX_PointF& c2,
+                            const CFX_PointF& to) {
+  path_.AppendPoint(c1, CFX_Path::Point::Type::kBezier);
+  path_.AppendPoint(c2, CFX_Path::Point::Type::kBezier);
+  path_.AppendPoint(to, CFX_Path::Point::Type::kBezier);
+}
+
+void CFGAS_GEPath::ArcTo(const CFX_PointF& pos,
+                         const CFX_SizeF& size,
+                         float start_angle,
+                         float sweep_angle) {
+  CFX_SizeF new_size = size / 2.0f;
+  ArcToInternal(CFX_PointF(pos.x + new_size.width, pos.y + new_size.height),
+                new_size, start_angle, sweep_angle);
+}
+
+void CFGAS_GEPath::ArcToInternal(const CFX_PointF& pos,
+                                 const CFX_SizeF& size,
+                                 float start_angle,
+                                 float sweep_angle) {
+  float x0 = cos(sweep_angle / 2);
+  float y0 = sin(sweep_angle / 2);
+  float tx = ((1.0f - x0) * 4) / (3 * 1.0f);
+  float ty = y0 - ((tx * x0) / y0);
+
+  CFX_PointF points[] = {CFX_PointF(x0 + tx, -ty), CFX_PointF(x0 + tx, ty)};
+  float sn = sin(start_angle + sweep_angle / 2);
+  float cs = cos(start_angle + sweep_angle / 2);
+
+  CFX_PointF bezier;
+  bezier.x = pos.x + (size.width * ((points[0].x * cs) - (points[0].y * sn)));
+  bezier.y = pos.y + (size.height * ((points[0].x * sn) + (points[0].y * cs)));
+  path_.AppendPoint(bezier, CFX_Path::Point::Type::kBezier);
+
+  bezier.x = pos.x + (size.width * ((points[1].x * cs) - (points[1].y * sn)));
+  bezier.y = pos.y + (size.height * ((points[1].x * sn) + (points[1].y * cs)));
+  path_.AppendPoint(bezier, CFX_Path::Point::Type::kBezier);
+
+  bezier.x = pos.x + (size.width * cos(start_angle + sweep_angle));
+  bezier.y = pos.y + (size.height * sin(start_angle + sweep_angle));
+  path_.AppendPoint(bezier, CFX_Path::Point::Type::kBezier);
+}
+
+void CFGAS_GEPath::AddLine(const CFX_PointF& p1, const CFX_PointF& p2) {
+  path_.AppendPoint(p1, CFX_Path::Point::Type::kMove);
+  path_.AppendPoint(p2, CFX_Path::Point::Type::kLine);
+}
+
+void CFGAS_GEPath::AddRectangle(float left,
+                                float top,
+                                float width,
+                                float height) {
+  path_.AppendRect(left, top, left + width, top + height);
+}
+
+void CFGAS_GEPath::AddEllipse(const CFX_RectF& rect) {
+  AddArc(rect.TopLeft(), rect.Size(), 0, FXSYS_PI * 2);
+}
+
+void CFGAS_GEPath::AddArc(const CFX_PointF& original_pos,
+                          const CFX_SizeF& original_size,
+                          float start_angle,
+                          float sweep_angle) {
+  if (sweep_angle == 0)
+    return;
+
+  const float bezier_arc_angle_epsilon = 0.01f;
+  while (start_angle > FXSYS_PI * 2)
+    start_angle -= FXSYS_PI * 2;
+  while (start_angle < 0)
+    start_angle += FXSYS_PI * 2;
+  if (sweep_angle >= FXSYS_PI * 2)
+    sweep_angle = FXSYS_PI * 2;
+  if (sweep_angle <= -FXSYS_PI * 2)
+    sweep_angle = -FXSYS_PI * 2;
+
+  CFX_SizeF size = original_size / 2;
+  CFX_PointF pos(original_pos.x + size.width, original_pos.y + size.height);
+  path_.AppendPoint(pos + CFX_PointF(size.width * cos(start_angle),
+                                     size.height * sin(start_angle)),
+                    CFX_Path::Point::Type::kMove);
+
+  float total_sweep = 0;
+  float local_sweep = 0;
+  float prev_sweep = 0;
+  bool done = false;
+  do {
+    if (sweep_angle < 0) {
+      prev_sweep = total_sweep;
+      local_sweep = -FXSYS_PI / 2;
+      total_sweep -= FXSYS_PI / 2;
+      if (total_sweep <= sweep_angle + bezier_arc_angle_epsilon) {
+        local_sweep = sweep_angle - prev_sweep;
+        done = true;
+      }
+    } else {
+      prev_sweep = total_sweep;
+      local_sweep = FXSYS_PI / 2;
+      total_sweep += FXSYS_PI / 2;
+      if (total_sweep >= sweep_angle - bezier_arc_angle_epsilon) {
+        local_sweep = sweep_angle - prev_sweep;
+        done = true;
+      }
+    }
+
+    ArcToInternal(pos, size, start_angle, local_sweep);
+    start_angle += local_sweep;
+  } while (!done);
+}
+
+void CFGAS_GEPath::AddSubpath(const CFGAS_GEPath& path) {
+  path_.Append(path.path_, nullptr);
+}
+
+void CFGAS_GEPath::TransformBy(const CFX_Matrix& mt) {
+  path_.Transform(mt);
+}
diff --git a/xfa/fgas/graphics/cfgas_gepath.h b/xfa/fgas/graphics/cfgas_gepath.h
new file mode 100644
index 0000000..dbadb3c
--- /dev/null
+++ b/xfa/fgas/graphics/cfgas_gepath.h
@@ -0,0 +1,54 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_GRAPHICS_CFGAS_GEPATH_H_
+#define XFA_FGAS_GRAPHICS_CFGAS_GEPATH_H_
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxge/cfx_path.h"
+
+class CFGAS_GEPath final {
+ public:
+  CFGAS_GEPath();
+  ~CFGAS_GEPath();
+
+  const CFX_Path& GetPath() const { return path_; }
+
+  void Clear();
+  bool IsEmpty() const { return path_.GetPoints().empty(); }
+  void TransformBy(const CFX_Matrix& mt);
+
+  void Close();
+  void MoveTo(const CFX_PointF& point);
+  void LineTo(const CFX_PointF& point);
+  void BezierTo(const CFX_PointF& c1,
+                const CFX_PointF& c2,
+                const CFX_PointF& to);
+  void ArcTo(const CFX_PointF& pos,
+             const CFX_SizeF& size,
+             float startAngle,
+             float sweepAngle);
+
+  void AddLine(const CFX_PointF& p1, const CFX_PointF& p2);
+  void AddRectangle(float left, float top, float width, float height);
+  void AddEllipse(const CFX_RectF& rect);
+  void AddArc(const CFX_PointF& pos,
+              const CFX_SizeF& size,
+              float startAngle,
+              float sweepAngle);
+
+  void AddSubpath(const CFGAS_GEPath& path);
+
+ private:
+  void ArcToInternal(const CFX_PointF& pos,
+                     const CFX_SizeF& size,
+                     float start_angle,
+                     float sweep_angle);
+
+  CFX_Path path_;
+};
+
+#endif  // XFA_FGAS_GRAPHICS_CFGAS_GEPATH_H_
diff --git a/xfa/fgas/graphics/cfgas_gepattern.cpp b/xfa/fgas/graphics/cfgas_gepattern.cpp
new file mode 100644
index 0000000..e3d6000
--- /dev/null
+++ b/xfa/fgas/graphics/cfgas_gepattern.cpp
@@ -0,0 +1,14 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/graphics/cfgas_gepattern.h"
+
+CFGAS_GEPattern::CFGAS_GEPattern(HatchStyle hatchStyle,
+                                 FX_ARGB foreArgb,
+                                 FX_ARGB backArgb)
+    : m_hatchStyle(hatchStyle), m_foreArgb(foreArgb), m_backArgb(backArgb) {}
+
+CFGAS_GEPattern::~CFGAS_GEPattern() = default;
diff --git a/xfa/fgas/graphics/cfgas_gepattern.h b/xfa/fgas/graphics/cfgas_gepattern.h
new file mode 100644
index 0000000..c04c3ac
--- /dev/null
+++ b/xfa/fgas/graphics/cfgas_gepattern.h
@@ -0,0 +1,36 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_GRAPHICS_CFGAS_GEPATTERN_H_
+#define XFA_FGAS_GRAPHICS_CFGAS_GEPATTERN_H_
+
+#include "core/fxge/dib/fx_dib.h"
+
+class CFGAS_GEPattern final {
+ public:
+  enum class HatchStyle {
+    Horizontal = 0,
+    Vertical = 1,
+    ForwardDiagonal = 2,
+    BackwardDiagonal = 3,
+    Cross = 4,
+    DiagonalCross = 5
+  };
+
+  CFGAS_GEPattern(HatchStyle hatchStyle, FX_ARGB foreArgb, FX_ARGB backArgb);
+  ~CFGAS_GEPattern();
+
+  HatchStyle GetHatchStyle() const { return m_hatchStyle; }
+  FX_ARGB GetForeArgb() const { return m_foreArgb; }
+  FX_ARGB GetBackArgb() const { return m_backArgb; }
+
+ private:
+  const HatchStyle m_hatchStyle;
+  const FX_ARGB m_foreArgb;
+  const FX_ARGB m_backArgb;
+};
+
+#endif  // XFA_FGAS_GRAPHICS_CFGAS_GEPATTERN_H_
diff --git a/xfa/fgas/graphics/cfgas_geshading.cpp b/xfa/fgas/graphics/cfgas_geshading.cpp
new file mode 100644
index 0000000..961a0e8
--- /dev/null
+++ b/xfa/fgas/graphics/cfgas_geshading.cpp
@@ -0,0 +1,73 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/graphics/cfgas_geshading.h"
+
+CFGAS_GEShading::CFGAS_GEShading(const CFX_PointF& beginPoint,
+                                 const CFX_PointF& endPoint,
+                                 bool isExtendedBegin,
+                                 bool isExtendedEnd,
+                                 FX_ARGB beginArgb,
+                                 FX_ARGB endArgb)
+    : m_type(Type::kAxial),
+      m_beginPoint(beginPoint),
+      m_endPoint(endPoint),
+      m_beginRadius(0),
+      m_endRadius(0),
+      m_isExtendedBegin(isExtendedBegin),
+      m_isExtendedEnd(isExtendedEnd) {
+  InitArgbArray(beginArgb, endArgb);
+}
+
+CFGAS_GEShading::CFGAS_GEShading(const CFX_PointF& beginPoint,
+                                 const CFX_PointF& endPoint,
+                                 float beginRadius,
+                                 float endRadius,
+                                 bool isExtendedBegin,
+                                 bool isExtendedEnd,
+                                 FX_ARGB beginArgb,
+                                 FX_ARGB endArgb)
+    : m_type(Type::kRadial),
+      m_beginPoint(beginPoint),
+      m_endPoint(endPoint),
+      m_beginRadius(beginRadius),
+      m_endRadius(endRadius),
+      m_isExtendedBegin(isExtendedBegin),
+      m_isExtendedEnd(isExtendedEnd) {
+  InitArgbArray(beginArgb, endArgb);
+}
+
+CFGAS_GEShading::~CFGAS_GEShading() = default;
+
+void CFGAS_GEShading::InitArgbArray(FX_ARGB beginArgb, FX_ARGB endArgb) {
+  int32_t a1;
+  int32_t r1;
+  int32_t g1;
+  int32_t b1;
+  std::tie(a1, r1, g1, b1) = ArgbDecode(beginArgb);
+
+  int32_t a2;
+  int32_t r2;
+  int32_t g2;
+  int32_t b2;
+  std::tie(a2, r2, g2, b2) = ArgbDecode(endArgb);
+
+  float f = static_cast<float>(kSteps - 1);
+  float aScale = 1.0 * (a2 - a1) / f;
+  float rScale = 1.0 * (r2 - r1) / f;
+  float gScale = 1.0 * (g2 - g1) / f;
+  float bScale = 1.0 * (b2 - b1) / f;
+
+  for (size_t i = 0; i < kSteps; i++) {
+    int32_t a3 = static_cast<int32_t>(i * aScale);
+    int32_t r3 = static_cast<int32_t>(i * rScale);
+    int32_t g3 = static_cast<int32_t>(i * gScale);
+    int32_t b3 = static_cast<int32_t>(i * bScale);
+
+    // TODO(dsinclair): Add overloads for FX_ARGB. pdfium:437
+    m_argbArray[i] = ArgbEncode(a1 + a3, r1 + r3, g1 + g3, b1 + b3);
+  }
+}
diff --git a/xfa/fgas/graphics/cfgas_geshading.h b/xfa/fgas/graphics/cfgas_geshading.h
new file mode 100644
index 0000000..9c88ad2
--- /dev/null
+++ b/xfa/fgas/graphics/cfgas_geshading.h
@@ -0,0 +1,63 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_GRAPHICS_CFGAS_GESHADING_H_
+#define XFA_FGAS_GRAPHICS_CFGAS_GESHADING_H_
+
+#include <stddef.h>
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxge/dib/fx_dib.h"
+
+class CFGAS_GEShading final {
+ public:
+  enum class Type { kAxial = 1, kRadial };
+
+  static constexpr size_t kSteps = 256;
+
+  // Axial shading.
+  CFGAS_GEShading(const CFX_PointF& beginPoint,
+                  const CFX_PointF& endPoint,
+                  bool isExtendedBegin,
+                  bool isExtendedEnd,
+                  FX_ARGB beginArgb,
+                  FX_ARGB endArgb);
+
+  // Radial shading.
+  CFGAS_GEShading(const CFX_PointF& beginPoint,
+                  const CFX_PointF& endPoint,
+                  float beginRadius,
+                  float endRadius,
+                  bool isExtendedBegin,
+                  bool isExtendedEnd,
+                  FX_ARGB beginArgb,
+                  FX_ARGB endArgb);
+
+  ~CFGAS_GEShading();
+
+  Type GetType() const { return m_type; }
+  CFX_PointF GetBeginPoint() const { return m_beginPoint; }
+  CFX_PointF GetEndPoint() const { return m_endPoint; }
+  float GetBeginRadius() const { return m_beginRadius; }
+  float GetEndRadius() const { return m_endRadius; }
+  bool IsExtendedBegin() const { return m_isExtendedBegin; }
+  bool IsExtendedEnd() const { return m_isExtendedEnd; }
+  FX_ARGB GetArgb(size_t index) const { return m_argbArray[index]; }
+
+ private:
+  void InitArgbArray(FX_ARGB beginArgb, FX_ARGB endArgb);
+
+  const Type m_type;
+  const CFX_PointF m_beginPoint;
+  const CFX_PointF m_endPoint;
+  const float m_beginRadius;
+  const float m_endRadius;
+  const bool m_isExtendedBegin;
+  const bool m_isExtendedEnd;
+  FX_ARGB m_argbArray[kSteps];
+};
+
+#endif  // XFA_FGAS_GRAPHICS_CFGAS_GESHADING_H_
diff --git a/xfa/fgas/layout/BUILD.gn b/xfa/fgas/layout/BUILD.gn
index 61d2e75..b00430a 100644
--- a/xfa/fgas/layout/BUILD.gn
+++ b/xfa/fgas/layout/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -9,37 +9,38 @@
 
 source_set("layout") {
   sources = [
-    "cfx_break.cpp",
-    "cfx_break.h",
-    "cfx_breakline.cpp",
-    "cfx_breakline.h",
-    "cfx_breakpiece.cpp",
-    "cfx_breakpiece.h",
-    "cfx_char.cpp",
-    "cfx_char.h",
-    "cfx_linkuserdata.cpp",
-    "cfx_linkuserdata.h",
-    "cfx_rtfbreak.cpp",
-    "cfx_rtfbreak.h",
-    "cfx_textpiece.cpp",
-    "cfx_textpiece.h",
-    "cfx_textuserdata.cpp",
-    "cfx_textuserdata.h",
-    "cfx_txtbreak.cpp",
-    "cfx_txtbreak.h",
-    "fx_arabic.cpp",
-    "fx_arabic.h",
-    "fx_linebreak.cpp",
-    "fx_linebreak.h",
+    "cfgas_break.cpp",
+    "cfgas_break.h",
+    "cfgas_breakline.cpp",
+    "cfgas_breakline.h",
+    "cfgas_breakpiece.cpp",
+    "cfgas_breakpiece.h",
+    "cfgas_char.cpp",
+    "cfgas_char.h",
+    "cfgas_linkuserdata.cpp",
+    "cfgas_linkuserdata.h",
+    "cfgas_rtfbreak.cpp",
+    "cfgas_rtfbreak.h",
+    "cfgas_textpiece.cpp",
+    "cfgas_textpiece.h",
+    "cfgas_textuserdata.cpp",
+    "cfgas_textuserdata.h",
+    "cfgas_txtbreak.cpp",
+    "cfgas_txtbreak.h",
+    "fgas_arabic.cpp",
+    "fgas_arabic.h",
+    "fgas_linebreak.cpp",
+    "fgas_linebreak.h",
   ]
   deps = [
-    "../:fgas",
     "../../../core/fxcrt",
     "../../../core/fxcrt/css",
     "../../../core/fxge",
+    "../font",
   ]
   configs += [
-    "../../../:pdfium_core_config",
+    "../../../:pdfium_strict_config",
+    "../../../:pdfium_noshorten_config",
     "../../:xfa_warnings",
   ]
   visibility = [ "../../../*" ]
@@ -47,14 +48,13 @@
 
 pdfium_unittest_source_set("unittests") {
   sources = [
-    "cfx_rtfbreak_unittest.cpp",
-    "cfx_txtbreak_unittest.cpp",
+    "cfgas_rtfbreak_unittest.cpp",
+    "cfgas_txtbreak_unittest.cpp",
   ]
   deps = [
     ":layout",
-    "../:fgas",
     "../../../core/fxge",
-    "../../../testing:unit_test_support",
+    "../font",
   ]
   pdfium_root_dir = "../../../"
 }
diff --git a/xfa/fgas/layout/cfgas_break.cpp b/xfa/fgas/layout/cfgas_break.cpp
new file mode 100644
index 0000000..23f2320
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_break.cpp
@@ -0,0 +1,162 @@
+// Copyright 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 "xfa/fgas/layout/cfgas_break.h"
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/stl_util.h"
+#include "xfa/fgas/font/cfgas_gefont.h"
+
+const float CFGAS_Break::kConversionFactor = 20000.0f;
+const int CFGAS_Break::kMinimumTabWidth = 160000;
+
+CFGAS_Break::CFGAS_Break(Mask<LayoutStyle> dwLayoutStyles)
+    : m_dwLayoutStyles(dwLayoutStyles), m_pCurLine(&m_Lines[0]) {}
+
+CFGAS_Break::~CFGAS_Break() = default;
+
+void CFGAS_Break::Reset() {
+  m_eCharType = FX_CHARTYPE::kUnknown;
+  for (CFGAS_BreakLine& line : m_Lines)
+    line.Clear();
+}
+
+void CFGAS_Break::SetLayoutStyles(Mask<LayoutStyle> dwLayoutStyles) {
+  m_dwLayoutStyles = dwLayoutStyles;
+  m_bSingleLine = !!(m_dwLayoutStyles & LayoutStyle::kSingleLine);
+  m_bCombText = !!(m_dwLayoutStyles & LayoutStyle::kCombText);
+}
+
+void CFGAS_Break::SetHorizontalScale(int32_t iScale) {
+  iScale = std::max(iScale, 0);
+  if (m_iHorizontalScale == iScale)
+    return;
+
+  SetBreakStatus();
+  m_iHorizontalScale = iScale;
+}
+
+void CFGAS_Break::SetVerticalScale(int32_t iScale) {
+  if (iScale < 0)
+    iScale = 0;
+  if (m_iVerticalScale == iScale)
+    return;
+
+  SetBreakStatus();
+  m_iVerticalScale = iScale;
+}
+
+void CFGAS_Break::SetFont(RetainPtr<CFGAS_GEFont> pFont) {
+  if (!pFont || pFont == m_pFont)
+    return;
+
+  SetBreakStatus();
+  m_pFont = std::move(pFont);
+}
+
+void CFGAS_Break::SetFontSize(float fFontSize) {
+  int32_t iFontSize = FXSYS_roundf(fFontSize * 20.0f);
+  if (m_iFontSize == iFontSize)
+    return;
+
+  SetBreakStatus();
+  m_iFontSize = iFontSize;
+}
+
+void CFGAS_Break::SetBreakStatus() {
+  ++m_dwIdentity;
+
+  CFGAS_Char* tc = m_pCurLine->LastChar();
+  if (tc && tc->m_dwStatus == CFGAS_Char::BreakType::kNone)
+    tc->m_dwStatus = CFGAS_Char::BreakType::kPiece;
+}
+
+bool CFGAS_Break::IsGreaterThanLineWidth(int32_t width) const {
+  FX_SAFE_INT32 line_width = m_iLineWidth;
+  line_width += m_iTolerance;
+  return line_width.IsValid() && width > line_width.ValueOrDie();
+}
+
+FX_CHARTYPE CFGAS_Break::GetUnifiedCharType(FX_CHARTYPE chartype) const {
+  return chartype >= FX_CHARTYPE::kArabicAlef ? FX_CHARTYPE::kArabic : chartype;
+}
+
+void CFGAS_Break::SetTabWidth(float fTabWidth) {
+  // Note, the use of max here was only done in the TxtBreak code. Leaving this
+  // in for the RTFBreak code for consistency. If we see issues with tab widths
+  // we may need to fix this.
+  m_iTabWidth =
+      std::max(FXSYS_roundf(fTabWidth * kConversionFactor), kMinimumTabWidth);
+}
+
+void CFGAS_Break::SetParagraphBreakChar(wchar_t wch) {
+  if (wch != L'\r' && wch != L'\n')
+    return;
+  m_wParagraphBreakChar = wch;
+}
+
+void CFGAS_Break::SetLineBreakTolerance(float fTolerance) {
+  m_iTolerance = FXSYS_roundf(fTolerance * kConversionFactor);
+}
+
+void CFGAS_Break::SetCharSpace(float fCharSpace) {
+  m_iCharSpace = FXSYS_roundf(fCharSpace * kConversionFactor);
+}
+
+void CFGAS_Break::SetLineBoundary(float fLineStart, float fLineEnd) {
+  if (fLineStart > fLineEnd)
+    return;
+
+  m_iLineStart = FXSYS_roundf(fLineStart * kConversionFactor);
+  m_iLineWidth = FXSYS_roundf(fLineEnd * kConversionFactor);
+  m_pCurLine->m_iStart = std::min(m_pCurLine->m_iStart, m_iLineWidth);
+  m_pCurLine->m_iStart = std::max(m_pCurLine->m_iStart, m_iLineStart);
+}
+
+CFGAS_Char* CFGAS_Break::GetLastChar(int32_t index,
+                                     bool bOmitChar,
+                                     bool bRichText) const {
+  std::vector<CFGAS_Char>& tca = m_pCurLine->m_LineChars;
+  if (!fxcrt::IndexInBounds(tca, index))
+    return nullptr;
+
+  int32_t iStart = fxcrt::CollectionSize<int32_t>(tca) - 1;
+  while (iStart > -1) {
+    CFGAS_Char* pTC = &tca[iStart--];
+    if (((bRichText && pTC->m_iCharWidth < 0) || bOmitChar) &&
+        pTC->GetCharType() == FX_CHARTYPE::kCombination) {
+      continue;
+    }
+    if (--index < 0)
+      return pTC;
+  }
+  return nullptr;
+}
+
+int32_t CFGAS_Break::CountBreakPieces() const {
+  return HasLine() ? fxcrt::CollectionSize<int32_t>(
+                         m_Lines[m_iReadyLineIndex].m_LinePieces)
+                   : 0;
+}
+
+const CFGAS_BreakPiece* CFGAS_Break::GetBreakPieceUnstable(
+    int32_t index) const {
+  if (!HasLine())
+    return nullptr;
+  if (!fxcrt::IndexInBounds(m_Lines[m_iReadyLineIndex].m_LinePieces, index))
+    return nullptr;
+  return &m_Lines[m_iReadyLineIndex].m_LinePieces[index];
+}
+
+void CFGAS_Break::ClearBreakPieces() {
+  if (HasLine())
+    m_Lines[m_iReadyLineIndex].Clear();
+  m_iReadyLineIndex = -1;
+}
diff --git a/xfa/fgas/layout/cfgas_break.h b/xfa/fgas/layout/cfgas_break.h
new file mode 100644
index 0000000..a20a37a
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_break.h
@@ -0,0 +1,94 @@
+// Copyright 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 XFA_FGAS_LAYOUT_CFGAS_BREAK_H_
+#define XFA_FGAS_LAYOUT_CFGAS_BREAK_H_
+
+#include <stdint.h>
+
+#include "core/fxcrt/mask.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "xfa/fgas/layout/cfgas_breakline.h"
+
+class CFGAS_GEFont;
+
+class CFGAS_Break {
+ public:
+  enum class LayoutStyle : uint8_t {
+    kNone = 0,
+    kPagination = 1 << 0,
+    kExpandTab = 1 << 1,
+    kSingleLine = 1 << 2,
+    kCombText = 1 << 3,
+  };
+
+  virtual ~CFGAS_Break();
+
+  void Reset();
+
+  void SetLayoutStyles(Mask<LayoutStyle> dwLayoutStyles);
+  Mask<LayoutStyle> GetLayoutStyles() const { return m_dwLayoutStyles; }
+
+  void SetFont(RetainPtr<CFGAS_GEFont> pFont);
+  void SetFontSize(float fFontSize);
+  void SetTabWidth(float fTabWidth);
+  int32_t GetTabWidth() const { return m_iTabWidth; }
+
+  void SetHorizontalScale(int32_t iScale);
+  void SetVerticalScale(int32_t iScale);
+  void SetLineBreakTolerance(float fTolerance);
+  void SetLineBoundary(float fLineStart, float fLineEnd);
+
+  void SetCharSpace(float fCharSpace);
+  void SetParagraphBreakChar(wchar_t wch);
+
+  int32_t CountBreakPieces() const;
+  const CFGAS_BreakPiece* GetBreakPieceUnstable(int32_t index) const;
+  void ClearBreakPieces();
+
+  CFGAS_Char* GetLastChar(int32_t index, bool bOmitChar, bool bRichText) const;
+  const CFGAS_BreakLine* GetCurrentLineForTesting() const { return m_pCurLine; }
+
+ protected:
+  struct TPO {
+    bool operator<(const TPO& that) const { return pos < that.pos; }
+
+    int32_t index;
+    int32_t pos;
+  };
+
+  static const int kMinimumTabWidth;
+  static const float kConversionFactor;
+
+  explicit CFGAS_Break(Mask<LayoutStyle> dwLayoutStyles);
+
+  void SetBreakStatus();
+  bool HasLine() const { return m_iReadyLineIndex >= 0; }
+  bool IsGreaterThanLineWidth(int32_t width) const;
+  FX_CHARTYPE GetUnifiedCharType(FX_CHARTYPE dwType) const;
+
+  FX_CHARTYPE m_eCharType = FX_CHARTYPE::kUnknown;
+  bool m_bSingleLine = false;
+  bool m_bCombText = false;
+  Mask<LayoutStyle> m_dwLayoutStyles = LayoutStyle::kNone;
+  uint32_t m_dwIdentity = 0;
+  int32_t m_iLineStart = 0;
+  int32_t m_iLineWidth = 2000000;
+  wchar_t m_wParagraphBreakChar = L'\n';
+  int32_t m_iFontSize = 240;
+  int32_t m_iTabWidth = 720000;
+  int32_t m_iHorizontalScale = 100;
+  int32_t m_iVerticalScale = 100;
+  int32_t m_iTolerance = 0;
+  int32_t m_iCharSpace = 0;
+  RetainPtr<CFGAS_GEFont> m_pFont;
+  UnownedPtr<CFGAS_BreakLine> m_pCurLine;
+  int8_t m_iReadyLineIndex = -1;
+  CFGAS_BreakLine m_Lines[2];
+};
+
+#endif  // XFA_FGAS_LAYOUT_CFGAS_BREAK_H_
diff --git a/xfa/fgas/layout/cfgas_breakline.cpp b/xfa/fgas/layout/cfgas_breakline.cpp
new file mode 100644
index 0000000..a55e874
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_breakline.cpp
@@ -0,0 +1,41 @@
+// Copyright 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 "xfa/fgas/layout/cfgas_breakline.h"
+
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/check.h"
+
+CFGAS_BreakLine::CFGAS_BreakLine() = default;
+
+CFGAS_BreakLine::~CFGAS_BreakLine() = default;
+
+CFGAS_Char* CFGAS_BreakLine::LastChar() {
+  if (m_LineChars.empty())
+    return nullptr;
+
+  return &m_LineChars.back();
+}
+
+int32_t CFGAS_BreakLine::GetLineEnd() const {
+  return m_iStart + m_iWidth;
+}
+
+void CFGAS_BreakLine::Clear() {
+  m_LineChars.clear();
+  m_LinePieces.clear();
+  m_iWidth = 0;
+  m_iArabicChars = 0;
+}
+
+void CFGAS_BreakLine::IncrementArabicCharCount() {
+  ++m_iArabicChars;
+}
+
+void CFGAS_BreakLine::DecrementArabicCharCount() {
+  DCHECK(m_iArabicChars > 0);
+  --m_iArabicChars;
+}
diff --git a/xfa/fgas/layout/cfgas_breakline.h b/xfa/fgas/layout/cfgas_breakline.h
new file mode 100644
index 0000000..ec239cc
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_breakline.h
@@ -0,0 +1,38 @@
+// Copyright 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 XFA_FGAS_LAYOUT_CFGAS_BREAKLINE_H_
+#define XFA_FGAS_LAYOUT_CFGAS_BREAKLINE_H_
+
+#include <vector>
+
+#include "xfa/fgas/layout/cfgas_breakpiece.h"
+#include "xfa/fgas/layout/cfgas_char.h"
+
+class CFGAS_BreakLine {
+ public:
+  CFGAS_BreakLine();
+  ~CFGAS_BreakLine();
+
+  CFGAS_Char* LastChar();
+  int32_t GetLineEnd() const;
+
+  void Clear();
+
+  void IncrementArabicCharCount();
+  void DecrementArabicCharCount();
+  bool HasArabicChar() const { return m_iArabicChars > 0; }
+
+  std::vector<CFGAS_Char> m_LineChars;
+  std::vector<CFGAS_BreakPiece> m_LinePieces;
+  int32_t m_iStart = 0;
+  int32_t m_iWidth = 0;
+
+ private:
+  int32_t m_iArabicChars = 0;
+};
+
+#endif  // XFA_FGAS_LAYOUT_CFGAS_BREAKLINE_H_
diff --git a/xfa/fgas/layout/cfgas_breakpiece.cpp b/xfa/fgas/layout/cfgas_breakpiece.cpp
new file mode 100644
index 0000000..0ef7bc3
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_breakpiece.cpp
@@ -0,0 +1,51 @@
+// Copyright 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 "xfa/fgas/layout/cfgas_breakpiece.h"
+
+#include "third_party/base/check_op.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "xfa/fgas/layout/cfgas_textuserdata.h"
+
+CFGAS_BreakPiece::CFGAS_BreakPiece() = default;
+
+CFGAS_BreakPiece::CFGAS_BreakPiece(const CFGAS_BreakPiece& other) = default;
+
+CFGAS_BreakPiece::~CFGAS_BreakPiece() = default;
+
+int32_t CFGAS_BreakPiece::GetEndPos() const {
+  return m_iWidth < 0 ? m_iStartPos : m_iStartPos + m_iWidth;
+}
+
+size_t CFGAS_BreakPiece::GetLength() const {
+  return pdfium::base::checked_cast<size_t>(m_iCharCount);
+}
+
+CFGAS_Char* CFGAS_BreakPiece::GetChar(int32_t index) const {
+  return GetChar(pdfium::base::checked_cast<size_t>(index));
+}
+
+CFGAS_Char* CFGAS_BreakPiece::GetChar(size_t index) const {
+  DCHECK_LT(index, GetLength());
+  DCHECK(m_pChars);
+  return &(*m_pChars)[m_iStartChar + index];
+}
+
+WideString CFGAS_BreakPiece::GetString() const {
+  WideString ret;
+  ret.Reserve(m_iCharCount);
+  for (int32_t i = m_iStartChar; i < m_iStartChar + m_iCharCount; i++)
+    ret += static_cast<wchar_t>((*m_pChars)[i].char_code());
+  return ret;
+}
+
+std::vector<int32_t> CFGAS_BreakPiece::GetWidths() const {
+  std::vector<int32_t> ret;
+  ret.reserve(m_iCharCount);
+  for (int32_t i = m_iStartChar; i < m_iStartChar + m_iCharCount; i++)
+    ret.push_back((*m_pChars)[i].m_iCharWidth);
+  return ret;
+}
diff --git a/xfa/fgas/layout/cfgas_breakpiece.h b/xfa/fgas/layout/cfgas_breakpiece.h
new file mode 100644
index 0000000..92c39a9
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_breakpiece.h
@@ -0,0 +1,93 @@
+// Copyright 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 XFA_FGAS_LAYOUT_CFGAS_BREAKPIECE_H_
+#define XFA_FGAS_LAYOUT_CFGAS_BREAKPIECE_H_
+
+#include <vector>
+
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "xfa/fgas/layout/cfgas_char.h"
+
+class CFGAS_TextUserData;
+
+class CFGAS_BreakPiece {
+ public:
+  CFGAS_BreakPiece();
+  CFGAS_BreakPiece(const CFGAS_BreakPiece& other);
+  ~CFGAS_BreakPiece();
+
+  int32_t GetEndPos() const;
+
+  // TODO(thestig): When GetCharCount() returns size_t, remove this.
+  size_t GetLength() const;
+
+  CFGAS_Char* GetChar(int32_t index) const;
+  CFGAS_Char* GetChar(size_t index) const;
+  WideString GetString() const;
+  std::vector<int32_t> GetWidths() const;
+
+  CFGAS_Char::BreakType GetStatus() const { return m_dwStatus; }
+  void SetStatus(CFGAS_Char::BreakType status) { m_dwStatus = status; }
+
+  int32_t GetStartPos() const { return m_iStartPos; }
+  void SetStartPos(int32_t pos) { m_iStartPos = pos; }
+  void IncrementStartPos(int32_t count) { m_iStartPos += count; }
+
+  int32_t GetWidth() const { return m_iWidth; }
+  void SetWidth(int32_t width) { m_iWidth = width; }
+  void IncrementWidth(int32_t width) { m_iWidth += width; }
+
+  int32_t GetStartChar() const { return m_iStartChar; }
+  void SetStartChar(int32_t pos) { m_iStartChar = pos; }
+
+  int32_t GetCharCount() const { return m_iCharCount; }
+  void SetCharCount(int32_t count) { m_iCharCount = count; }
+
+  int32_t GetBidiLevel() const { return m_iBidiLevel; }
+  void SetBidiLevel(int32_t level) { m_iBidiLevel = level; }
+
+  int32_t GetBidiPos() const { return m_iBidiPos; }
+  void SetBidiPos(int32_t pos) { m_iBidiPos = pos; }
+
+  int32_t GetFontSize() const { return m_iFontSize; }
+  void SetFontSize(int32_t font_size) { m_iFontSize = font_size; }
+
+  int32_t GetHorizontalScale() const { return m_iHorizontalScale; }
+  void SetHorizontalScale(int32_t scale) { m_iHorizontalScale = scale; }
+
+  int32_t GetVerticalScale() const { return m_iVerticalScale; }
+  void SetVerticalScale(int32_t scale) { m_iVerticalScale = scale; }
+
+  uint32_t GetCharStyles() const { return m_dwCharStyles; }
+  void SetCharStyles(uint32_t styles) { m_dwCharStyles = styles; }
+
+  void SetChars(std::vector<CFGAS_Char>* chars) { m_pChars = chars; }
+
+  const CFGAS_TextUserData* GetUserData() const { return m_pUserData.Get(); }
+  void SetUserData(const RetainPtr<CFGAS_TextUserData>& user_data) {
+    m_pUserData = user_data;
+  }
+
+ private:
+  CFGAS_Char::BreakType m_dwStatus = CFGAS_Char::BreakType::kPiece;
+  int32_t m_iStartPos = 0;
+  int32_t m_iWidth = -1;
+  int32_t m_iStartChar = 0;
+  int32_t m_iCharCount = 0;
+  int32_t m_iBidiLevel = 0;
+  int32_t m_iBidiPos = 0;
+  int32_t m_iFontSize = 0;
+  int32_t m_iHorizontalScale = 100;
+  int32_t m_iVerticalScale = 100;
+  uint32_t m_dwCharStyles = 0;
+  UnownedPtr<std::vector<CFGAS_Char>> m_pChars;
+  RetainPtr<CFGAS_TextUserData> m_pUserData;
+};
+
+#endif  // XFA_FGAS_LAYOUT_CFGAS_BREAKPIECE_H_
diff --git a/xfa/fgas/layout/cfgas_char.cpp b/xfa/fgas/layout/cfgas_char.cpp
new file mode 100644
index 0000000..f1f6bfc
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_char.cpp
@@ -0,0 +1,569 @@
+// Copyright 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 "xfa/fgas/layout/cfgas_char.h"
+
+#include <algorithm>
+#include <iterator>
+
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/check.h"
+
+namespace {
+
+#if DCHECK_IS_ON()
+constexpr int32_t kBidiMaxLevel = 61;
+#endif
+
+#undef PACK_NIBBLES
+#define PACK_NIBBLES(hi, lo) \
+  ((static_cast<uint32_t>(hi) << 4) + static_cast<uint32_t>(lo))
+
+enum FX_BIDIWEAKSTATE : uint8_t {
+  FX_BWSxa = 0,
+  FX_BWSxr,
+  FX_BWSxl,
+  FX_BWSao,
+  FX_BWSro,
+  FX_BWSlo,
+  FX_BWSrt,
+  FX_BWSlt,
+  FX_BWScn,
+  FX_BWSra,
+  FX_BWSre,
+  FX_BWSla,
+  FX_BWSle,
+  FX_BWSac,
+  FX_BWSrc,
+  FX_BWSrs,
+  FX_BWSlc,
+  FX_BWSls,
+  FX_BWSret,
+  FX_BWSlet
+};
+
+// NOTE: Range of FX_BIDICLASS prevents encoding all possible values in this
+// manner, but the ones used manage to fit. Except that I suspect that 0xF
+// was intended to be used as a sentinel, even though it also means kRLE.
+// TODO(tsepez): pick a better representation.
+enum FX_BIDIWEAKACTION : uint16_t {
+  FX_BWAIX = 0x100,
+  FX_BWAXX = 0x0F,
+  FX_BWAxxx = 0xFF,
+  FX_BWAxIx = 0x100 + FX_BWAxxx,
+  FX_BWAxxN = PACK_NIBBLES(0x0F, FX_BIDICLASS::kON),
+  FX_BWAxxE = PACK_NIBBLES(0x0F, FX_BIDICLASS::kEN),
+  FX_BWAxxA = PACK_NIBBLES(0x0F, FX_BIDICLASS::kAN),
+  FX_BWAxxR = PACK_NIBBLES(0x0F, FX_BIDICLASS::kR),
+  FX_BWAxxL = PACK_NIBBLES(0x0F, FX_BIDICLASS::kL),
+  FX_BWANxx = PACK_NIBBLES(FX_BIDICLASS::kON, 0x0F),
+  FX_BWAAxx = PACK_NIBBLES(FX_BIDICLASS::kAN, 0x0F),
+  FX_BWAExE = PACK_NIBBLES(FX_BIDICLASS::kEN, FX_BIDICLASS::kEN),
+  FX_BWANIx = 0x100 + PACK_NIBBLES(FX_BIDICLASS::kON, 0x0F),
+  FX_BWANxN = PACK_NIBBLES(FX_BIDICLASS::kON, FX_BIDICLASS::kON),
+  FX_BWANxR = PACK_NIBBLES(FX_BIDICLASS::kON, FX_BIDICLASS::kR),
+  FX_BWANxE = PACK_NIBBLES(FX_BIDICLASS::kON, FX_BIDICLASS::kEN),
+  FX_BWAAxA = PACK_NIBBLES(FX_BIDICLASS::kAN, FX_BIDICLASS::kAN),
+  FX_BWANxL = PACK_NIBBLES(FX_BIDICLASS::kON, FX_BIDICLASS::kL),
+  FX_BWALxL = PACK_NIBBLES(FX_BIDICLASS::kL, FX_BIDICLASS::kL),
+  FX_BWAxIL = 0x100 + PACK_NIBBLES(0x0F, FX_BIDICLASS::kL),
+  FX_BWAAxR = PACK_NIBBLES(FX_BIDICLASS::kAN, FX_BIDICLASS::kR),
+  FX_BWALxx = PACK_NIBBLES(FX_BIDICLASS::kL, 0x0F),
+};
+
+enum FX_BIDINEUTRALSTATE : uint8_t {
+  FX_BNSr = 0,
+  FX_BNSl,
+  FX_BNSrn,
+  FX_BNSln,
+  FX_BNSa,
+  FX_BNSna
+};
+
+enum FX_BIDINEUTRALACTION : uint16_t {
+  // For placeholders in table.
+  FX_BNAZero = 0,
+
+  // Other values.
+  FX_BNAnL = PACK_NIBBLES(0, FX_BIDICLASS::kL),
+  FX_BNAEn = PACK_NIBBLES(FX_BIDICLASS::kAN, 0),
+  FX_BNARn = PACK_NIBBLES(FX_BIDICLASS::kR, 0),
+  FX_BNALn = PACK_NIBBLES(FX_BIDICLASS::kL, 0),
+  FX_BNAIn = FX_BWAIX,
+  FX_BNALnL = PACK_NIBBLES(FX_BIDICLASS::kL, FX_BIDICLASS::kL),
+};
+#undef PACK_NIBBLES
+
+const FX_BIDICLASS kNTypes[] = {
+    FX_BIDICLASS::kN,   FX_BIDICLASS::kL,   FX_BIDICLASS::kR,
+    FX_BIDICLASS::kAN,  FX_BIDICLASS::kEN,  FX_BIDICLASS::kAL,
+    FX_BIDICLASS::kNSM, FX_BIDICLASS::kCS,  FX_BIDICLASS::kES,
+    FX_BIDICLASS::kET,  FX_BIDICLASS::kBN,  FX_BIDICLASS::kBN,
+    FX_BIDICLASS::kN,   FX_BIDICLASS::kB,   FX_BIDICLASS::kRLO,
+    FX_BIDICLASS::kRLE, FX_BIDICLASS::kLRO, FX_BIDICLASS::kLRE,
+    FX_BIDICLASS::kPDF, FX_BIDICLASS::kON,
+};
+
+const FX_BIDIWEAKSTATE kWeakStates[20][10] = {
+    {FX_BWSao, FX_BWSxl, FX_BWSxr, FX_BWScn, FX_BWScn, FX_BWSxa, FX_BWSxa,
+     FX_BWSao, FX_BWSao, FX_BWSao},
+    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSxr,
+     FX_BWSro, FX_BWSro, FX_BWSrt},
+    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSxl,
+     FX_BWSlo, FX_BWSlo, FX_BWSlt},
+    {FX_BWSao, FX_BWSxl, FX_BWSxr, FX_BWScn, FX_BWScn, FX_BWSxa, FX_BWSao,
+     FX_BWSao, FX_BWSao, FX_BWSao},
+    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSro,
+     FX_BWSro, FX_BWSro, FX_BWSrt},
+    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlo,
+     FX_BWSlo, FX_BWSlo, FX_BWSlt},
+    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSrt,
+     FX_BWSro, FX_BWSro, FX_BWSrt},
+    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlt,
+     FX_BWSlo, FX_BWSlo, FX_BWSlt},
+    {FX_BWSao, FX_BWSxl, FX_BWSxr, FX_BWScn, FX_BWScn, FX_BWSxa, FX_BWScn,
+     FX_BWSac, FX_BWSao, FX_BWSao},
+    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSra,
+     FX_BWSrc, FX_BWSro, FX_BWSrt},
+    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSre,
+     FX_BWSrs, FX_BWSrs, FX_BWSret},
+    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSla,
+     FX_BWSlc, FX_BWSlo, FX_BWSlt},
+    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSle,
+     FX_BWSls, FX_BWSls, FX_BWSlet},
+    {FX_BWSao, FX_BWSxl, FX_BWSxr, FX_BWScn, FX_BWScn, FX_BWSxa, FX_BWSao,
+     FX_BWSao, FX_BWSao, FX_BWSao},
+    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSro,
+     FX_BWSro, FX_BWSro, FX_BWSrt},
+    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSro,
+     FX_BWSro, FX_BWSro, FX_BWSrt},
+    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlo,
+     FX_BWSlo, FX_BWSlo, FX_BWSlt},
+    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlo,
+     FX_BWSlo, FX_BWSlo, FX_BWSlt},
+    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSret,
+     FX_BWSro, FX_BWSro, FX_BWSret},
+    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlet,
+     FX_BWSlo, FX_BWSlo, FX_BWSlet},
+};
+
+const FX_BIDIWEAKACTION kWeakActions[20][10] = {
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxA, FX_BWAxxR,
+     FX_BWAxxR, FX_BWAxxN, FX_BWAxxN, FX_BWAxxN},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
+     FX_BWAxxR, FX_BWAxxN, FX_BWAxxN, FX_BWAxIx},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
+     FX_BWAxxL, FX_BWAxxN, FX_BWAxxN, FX_BWAxIx},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxA, FX_BWAxxR,
+     FX_BWAxxN, FX_BWAxxN, FX_BWAxxN, FX_BWAxxN},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
+     FX_BWAxxN, FX_BWAxxN, FX_BWAxxN, FX_BWAxIx},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
+     FX_BWAxxN, FX_BWAxxN, FX_BWAxxN, FX_BWAxIx},
+    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAExE, FX_BWANxR,
+     FX_BWAxIx, FX_BWANxN, FX_BWANxN, FX_BWAxIx},
+    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWALxL, FX_BWANxR,
+     FX_BWAxIx, FX_BWANxN, FX_BWANxN, FX_BWAxIx},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxA, FX_BWAxxR,
+     FX_BWAxxA, FX_BWAxIx, FX_BWAxxN, FX_BWAxxN},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
+     FX_BWAxxA, FX_BWAxIx, FX_BWAxxN, FX_BWAxIx},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
+     FX_BWAxxE, FX_BWAxIx, FX_BWAxIx, FX_BWAxxE},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
+     FX_BWAxxA, FX_BWAxIx, FX_BWAxxN, FX_BWAxIx},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
+     FX_BWAxxL, FX_BWAxIx, FX_BWAxIx, FX_BWAxxL},
+    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAAxx, FX_BWAAxA, FX_BWANxR,
+     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANxN},
+    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAAxx, FX_BWANxE, FX_BWANxR,
+     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANIx},
+    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAExE, FX_BWANxR,
+     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANIx},
+    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAAxx, FX_BWANxL, FX_BWANxR,
+     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANIx},
+    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWALxL, FX_BWANxR,
+     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANIx},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
+     FX_BWAxxE, FX_BWAxxN, FX_BWAxxN, FX_BWAxxE},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
+     FX_BWAxxL, FX_BWAxxN, FX_BWAxxN, FX_BWAxxL},
+};
+
+const FX_BIDINEUTRALSTATE kNeutralStates[6][5] = {
+    {FX_BNSrn, FX_BNSl, FX_BNSr, FX_BNSr, FX_BNSr},
+    {FX_BNSln, FX_BNSl, FX_BNSr, FX_BNSa, FX_BNSl},
+    {FX_BNSrn, FX_BNSl, FX_BNSr, FX_BNSr, FX_BNSr},
+    {FX_BNSln, FX_BNSl, FX_BNSr, FX_BNSa, FX_BNSl},
+    {FX_BNSna, FX_BNSl, FX_BNSr, FX_BNSa, FX_BNSl},
+    {FX_BNSna, FX_BNSl, FX_BNSr, FX_BNSa, FX_BNSl},
+};
+
+const FX_BIDINEUTRALACTION kNeutralActions[6][5] = {
+    {FX_BNAIn, FX_BNAZero, FX_BNAZero, FX_BNAZero, FX_BNAZero},
+    {FX_BNAIn, FX_BNAZero, FX_BNAZero, FX_BNAZero, FX_BNAnL},
+    {FX_BNAIn, FX_BNAEn, FX_BNARn, FX_BNARn, FX_BNARn},
+    {FX_BNAIn, FX_BNALn, FX_BNAEn, FX_BNAEn, FX_BNALnL},
+    {FX_BNAIn, FX_BNAZero, FX_BNAZero, FX_BNAZero, FX_BNAnL},
+    {FX_BNAIn, FX_BNAEn, FX_BNARn, FX_BNARn, FX_BNAEn},
+};
+
+const uint8_t kAddLevel[2][4] = {
+    {0, 1, 2, 2},
+    {1, 0, 1, 1},
+};
+
+FX_BIDICLASS Direction(int32_t val) {
+  return FX_IsOdd(val) ? FX_BIDICLASS::kR : FX_BIDICLASS::kL;
+}
+
+FX_BIDICLASS GetDeferredType(int32_t val) {
+  return static_cast<FX_BIDICLASS>((val >> 4) & 0x0F);
+}
+
+FX_BIDICLASS GetResolvedType(int32_t val) {
+  return static_cast<FX_BIDICLASS>(val & 0x0F);
+}
+
+FX_BIDICLASS GetDeferredNeutrals(int32_t iAction, int32_t iLevel) {
+  FX_BIDICLASS eClass = GetDeferredType(iAction);
+  return eClass == FX_BIDICLASS::kAN ? Direction(iLevel) : eClass;
+}
+
+FX_BIDICLASS GetResolvedNeutrals(int32_t iAction) {
+  return GetResolvedType(iAction);
+}
+
+FX_BIDIWEAKSTATE GetWeakState(FX_BIDIWEAKSTATE eState, FX_BIDICLASS eClass) {
+  DCHECK(static_cast<size_t>(eState) < std::size(kWeakStates));
+  DCHECK(static_cast<size_t>(eClass) < std::size(kWeakStates[0]));
+  return kWeakStates[static_cast<size_t>(eState)][static_cast<size_t>(eClass)];
+}
+
+FX_BIDIWEAKACTION GetWeakAction(FX_BIDIWEAKSTATE eState, FX_BIDICLASS eClass) {
+  DCHECK(static_cast<size_t>(eState) < std::size(kWeakActions));
+  DCHECK(static_cast<size_t>(eClass) < std::size(kWeakActions[0]));
+  return kWeakActions[static_cast<size_t>(eState)][static_cast<size_t>(eClass)];
+}
+
+FX_BIDINEUTRALSTATE GetNeutralState(FX_BIDINEUTRALSTATE eState,
+                                    FX_BIDICLASS eClass) {
+  DCHECK(static_cast<size_t>(eState) < std::size(kNeutralStates));
+  DCHECK(static_cast<size_t>(eClass) < std::size(kNeutralStates[0]));
+  return kNeutralStates[static_cast<size_t>(eState)]
+                       [static_cast<size_t>(eClass)];
+}
+
+FX_BIDINEUTRALACTION GetNeutralAction(FX_BIDINEUTRALSTATE eState,
+                                      FX_BIDICLASS eClass) {
+  DCHECK(static_cast<size_t>(eState) < std::size(kNeutralActions));
+  DCHECK(static_cast<size_t>(eClass) < std::size(kNeutralActions[0]));
+  return kNeutralActions[static_cast<size_t>(eState)]
+                        [static_cast<size_t>(eClass)];
+}
+
+void ReverseString(std::vector<CFGAS_Char>* chars,
+                   size_t iStart,
+                   size_t iCount) {
+  DCHECK(fxcrt::IndexInBounds(*chars, iStart));
+  DCHECK(iStart + iCount <= chars->size());
+
+  std::reverse(chars->begin() + iStart, chars->begin() + iStart + iCount);
+}
+
+void SetDeferredRunClass(std::vector<CFGAS_Char>* chars,
+                         size_t iStart,
+                         size_t iCount,
+                         FX_BIDICLASS eValue) {
+  DCHECK(iStart <= chars->size());
+  DCHECK(iStart >= iCount);
+
+  size_t iLast = iStart - iCount;
+  for (size_t i = iStart; i > iLast; --i)
+    (*chars)[i - 1].m_iBidiClass = eValue;
+}
+
+void SetDeferredRunLevel(std::vector<CFGAS_Char>* chars,
+                         size_t iStart,
+                         size_t iCount,
+                         int32_t iValue) {
+  DCHECK(iStart <= chars->size());
+  DCHECK(iStart >= iCount);
+
+  size_t iLast = iStart - iCount;
+  for (size_t i = iStart; i > iLast; --i)
+    (*chars)[i - 1].m_iBidiLevel = static_cast<int16_t>(iValue);
+}
+
+void Classify(std::vector<CFGAS_Char>* chars, size_t iCount) {
+  for (size_t i = 0; i < iCount; ++i) {
+    CFGAS_Char& cur = (*chars)[i];
+    cur.m_iBidiClass = pdfium::unicode::GetBidiClass(cur.char_code());
+  }
+}
+
+void ClassifyWithTransform(std::vector<CFGAS_Char>* chars, size_t iCount) {
+  for (size_t i = 0; i < iCount; ++i) {
+    CFGAS_Char& cur = (*chars)[i];
+    cur.m_iBidiClass = kNTypes[static_cast<size_t>(
+        pdfium::unicode::GetBidiClass(cur.char_code()))];
+  }
+}
+
+void ResolveExplicit(std::vector<CFGAS_Char>* chars, size_t iCount) {
+  for (size_t i = 0; i < iCount; ++i)
+    (*chars)[i].m_iBidiLevel = 0;
+}
+
+void ResolveWeak(std::vector<CFGAS_Char>* chars, size_t iCount) {
+  if (iCount <= 1)
+    return;
+  --iCount;
+
+  int32_t iLevelCur = 0;
+  size_t iNum = 0;
+  FX_BIDIWEAKSTATE eState = FX_BWSxl;
+  FX_BIDICLASS eClsCur;
+  FX_BIDICLASS eClsRun;
+  FX_BIDICLASS eClsNew;
+  size_t i = 0;
+  for (; i <= iCount; ++i) {
+    CFGAS_Char* pTC = &(*chars)[i];
+    eClsCur = pTC->m_iBidiClass;
+    if (eClsCur == FX_BIDICLASS::kBN) {
+      pTC->m_iBidiLevel = (int16_t)iLevelCur;
+      if (i == iCount && iLevelCur != 0) {
+        eClsCur = Direction(iLevelCur);
+        pTC->m_iBidiClass = eClsCur;
+      } else if (i < iCount) {
+        CFGAS_Char* pTCNext = &(*chars)[i + 1];
+        eClsNew = pTCNext->m_iBidiClass;
+        int32_t iLevelNext = pTCNext->m_iBidiLevel;
+        if (eClsNew != FX_BIDICLASS::kBN && iLevelCur != iLevelNext) {
+          int32_t iLevelNew = std::max(iLevelNext, iLevelCur);
+          pTC->m_iBidiLevel = static_cast<int16_t>(iLevelNew);
+          eClsCur = Direction(iLevelNew);
+          pTC->m_iBidiClass = eClsCur;
+          iLevelCur = iLevelNext;
+        } else {
+          if (iNum > 0)
+            ++iNum;
+          continue;
+        }
+      } else {
+        if (iNum > 0)
+          ++iNum;
+        continue;
+      }
+    }
+    if (eClsCur > FX_BIDICLASS::kBN)
+      continue;
+
+    FX_BIDIWEAKACTION eAction = GetWeakAction(eState, eClsCur);
+    eClsRun = GetDeferredType(eAction);
+    if (eClsRun != static_cast<FX_BIDICLASS>(0xF) && iNum > 0) {
+      SetDeferredRunClass(chars, i, iNum, eClsRun);
+      iNum = 0;
+    }
+    eClsNew = GetResolvedType(eAction);
+    if (eClsNew != static_cast<FX_BIDICLASS>(0xF))
+      pTC->m_iBidiClass = eClsNew;
+    if (FX_BWAIX & eAction)
+      ++iNum;
+
+    eState = GetWeakState(eState, eClsCur);
+  }
+  if (iNum == 0)
+    return;
+
+  eClsCur = Direction(0);
+  eClsRun = GetDeferredType(GetWeakAction(eState, eClsCur));
+  if (eClsRun != static_cast<FX_BIDICLASS>(0xF))
+    SetDeferredRunClass(chars, i, iNum, eClsRun);
+}
+
+void ResolveNeutrals(std::vector<CFGAS_Char>* chars, size_t iCount) {
+  if (iCount <= 1)
+    return;
+  --iCount;
+
+  CFGAS_Char* pTC;
+  int32_t iLevel = 0;
+  size_t i = 0;
+  size_t iNum = 0;
+  FX_BIDINEUTRALSTATE eState = FX_BNSl;
+  FX_BIDICLASS eClsCur;
+  FX_BIDICLASS eClsRun;
+  FX_BIDICLASS eClsNew;
+  for (; i <= iCount; ++i) {
+    pTC = &(*chars)[i];
+    eClsCur = pTC->m_iBidiClass;
+    if (eClsCur == FX_BIDICLASS::kBN) {
+      if (iNum)
+        ++iNum;
+      continue;
+    }
+    if (eClsCur >= FX_BIDICLASS::kAL)
+      continue;
+
+    FX_BIDINEUTRALACTION eAction = GetNeutralAction(eState, eClsCur);
+    eClsRun = GetDeferredNeutrals(eAction, iLevel);
+    if (eClsRun != FX_BIDICLASS::kN && iNum > 0) {
+      SetDeferredRunClass(chars, i, iNum, eClsRun);
+      iNum = 0;
+    }
+
+    eClsNew = GetResolvedNeutrals(eAction);
+    if (eClsNew != FX_BIDICLASS::kN)
+      pTC->m_iBidiClass = eClsNew;
+    if (FX_BNAIn & eAction)
+      ++iNum;
+
+    eState = GetNeutralState(eState, eClsCur);
+    iLevel = pTC->m_iBidiLevel;
+  }
+  if (iNum == 0)
+    return;
+
+  eClsCur = Direction(iLevel);
+  eClsRun = GetDeferredNeutrals(GetNeutralAction(eState, eClsCur), iLevel);
+  if (eClsRun != FX_BIDICLASS::kN)
+    SetDeferredRunClass(chars, i, iNum, eClsRun);
+}
+
+void ResolveImplicit(std::vector<CFGAS_Char>* chars, size_t iCount) {
+  for (size_t i = 0; i < iCount; ++i) {
+    FX_BIDICLASS eCls = (*chars)[i].m_iBidiClass;
+    if (eCls == FX_BIDICLASS::kBN || eCls <= FX_BIDICLASS::kON ||
+        eCls >= FX_BIDICLASS::kAL) {
+      continue;
+    }
+    (*chars)[i].m_iBidiLevel += kAddLevel[FX_IsOdd((*chars)[i].m_iBidiLevel)]
+                                         [static_cast<size_t>(eCls) - 1];
+  }
+}
+
+void ResolveWhitespace(std::vector<CFGAS_Char>* chars, size_t iCount) {
+  if (iCount <= 1)
+    return;
+  iCount--;
+
+  int32_t iLevel = 0;
+  size_t i = 0;
+  size_t iNum = 0;
+  for (; i <= iCount; ++i) {
+    switch (static_cast<FX_BIDICLASS>((*chars)[i].m_iBidiClass)) {
+      case FX_BIDICLASS::kWS:
+        ++iNum;
+        break;
+      case FX_BIDICLASS::kRLE:
+      case FX_BIDICLASS::kLRE:
+      case FX_BIDICLASS::kLRO:
+      case FX_BIDICLASS::kRLO:
+      case FX_BIDICLASS::kPDF:
+      case FX_BIDICLASS::kBN:
+        (*chars)[i].m_iBidiLevel = static_cast<int16_t>(iLevel);
+        ++iNum;
+        break;
+      case FX_BIDICLASS::kS:
+      case FX_BIDICLASS::kB:
+        if (iNum > 0)
+          SetDeferredRunLevel(chars, i, iNum, 0);
+
+        (*chars)[i].m_iBidiLevel = 0;
+        iNum = 0;
+        break;
+      default:
+        iNum = 0;
+        break;
+    }
+    iLevel = (*chars)[i].m_iBidiLevel;
+  }
+  if (iNum > 0)
+    SetDeferredRunLevel(chars, i, iNum, 0);
+}
+
+size_t ReorderLevel(std::vector<CFGAS_Char>* chars,
+                    size_t iCount,
+                    int32_t iBaseLevel,
+                    size_t iStart,
+                    bool bReverse) {
+  DCHECK(iBaseLevel >= 0);
+  DCHECK(iBaseLevel <= kBidiMaxLevel);
+  DCHECK(iStart < iCount);
+
+  if (iCount < 1)
+    return 0;
+
+  bReverse = bReverse || FX_IsOdd(iBaseLevel);
+  size_t i = iStart;
+  for (; i < iCount; ++i) {
+    int32_t iLevel = (*chars)[i].m_iBidiLevel;
+    if (iLevel == iBaseLevel)
+      continue;
+    if (iLevel < iBaseLevel)
+      break;
+
+    i += ReorderLevel(chars, iCount, iBaseLevel + 1, i, bReverse) - 1;
+  }
+
+  size_t iNum = i - iStart;
+  if (bReverse && iNum > 1)
+    ReverseString(chars, iStart, iNum);
+
+  return iNum;
+}
+
+void Reorder(std::vector<CFGAS_Char>* chars, size_t iCount) {
+  for (size_t i = 0; i < iCount;)
+    i += ReorderLevel(chars, iCount, 0, i, false);
+}
+
+void Position(std::vector<CFGAS_Char>* chars, size_t iCount) {
+  for (size_t i = 0; i < iCount; ++i) {
+    if ((*chars)[i].m_iBidiPos > iCount)
+      continue;
+
+    (*chars)[(*chars)[i].m_iBidiPos].m_iBidiOrder = i;
+  }
+}
+
+}  // namespace
+
+// static
+void CFGAS_Char::BidiLine(std::vector<CFGAS_Char>* chars, size_t iCount) {
+  DCHECK(iCount <= chars->size());
+  if (iCount < 2)
+    return;
+
+  ClassifyWithTransform(chars, iCount);
+  ResolveExplicit(chars, iCount);
+  ResolveWeak(chars, iCount);
+  ResolveNeutrals(chars, iCount);
+  ResolveImplicit(chars, iCount);
+  Classify(chars, iCount);
+  ResolveWhitespace(chars, iCount);
+  Reorder(chars, iCount);
+  Position(chars, iCount);
+}
+
+CFGAS_Char::CFGAS_Char(uint16_t wCharCode) : CFGAS_Char(wCharCode, 100, 100) {}
+
+CFGAS_Char::CFGAS_Char(uint16_t wCharCode,
+                       int32_t iHorizontalScale,
+                       int32_t iVerticalScale)
+    : m_wCharCode(wCharCode),
+      m_iHorizontalScale(iHorizontalScale),
+      m_iVerticalScale(iVerticalScale) {}
+
+CFGAS_Char::CFGAS_Char(const CFGAS_Char& other) = default;
+
+CFGAS_Char::~CFGAS_Char() = default;
+
+FX_CHARTYPE CFGAS_Char::GetCharType() const {
+  return pdfium::unicode::GetCharType(m_wCharCode);
+}
diff --git a/xfa/fgas/layout/cfgas_char.h b/xfa/fgas/layout/cfgas_char.h
new file mode 100644
index 0000000..314ac6c
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_char.h
@@ -0,0 +1,61 @@
+// Copyright 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 XFA_FGAS_LAYOUT_CFGAS_CHAR_H_
+#define XFA_FGAS_LAYOUT_CFGAS_CHAR_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "core/fxcrt/fx_unicode.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "xfa/fgas/layout/cfgas_textuserdata.h"
+#include "xfa/fgas/layout/fgas_linebreak.h"
+
+class CFGAS_Char {
+ public:
+  enum class BreakType : uint8_t {
+    kNone = 0,
+    kPiece,
+    kLine,
+    kParagraph,
+    kPage
+  };
+
+  static void BidiLine(std::vector<CFGAS_Char>* chars, size_t iCount);
+
+  explicit CFGAS_Char(uint16_t wCharCode);
+  CFGAS_Char(uint16_t wCharCode,
+             int32_t iHorizontalScale,
+             int32_t iVerticalScale);
+  CFGAS_Char(const CFGAS_Char& other);
+  ~CFGAS_Char();
+
+  FX_CHARTYPE GetCharType() const;
+  uint16_t char_code() const { return m_wCharCode; }
+  int16_t horizonal_scale() const { return m_iHorizontalScale; }
+  int16_t vertical_scale() const { return m_iVerticalScale; }
+
+  BreakType m_dwStatus = BreakType::kNone;
+  FX_BIDICLASS m_iBidiClass = FX_BIDICLASS::kON;
+  FX_LINEBREAKTYPE m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
+  uint32_t m_dwCharStyles = 0;
+  int32_t m_iCharWidth = 0;
+  uint16_t m_iBidiLevel = 0;
+  uint16_t m_iBidiPos = 0;
+  uint16_t m_iBidiOrder = 0;
+  int32_t m_iFontSize = 0;
+  uint32_t m_dwIdentity = 0;
+  RetainPtr<CFGAS_TextUserData> m_pUserData;
+
+ private:
+  uint16_t m_wCharCode;
+  int32_t m_iHorizontalScale;
+  int32_t m_iVerticalScale;
+};
+
+#endif  // XFA_FGAS_LAYOUT_CFGAS_CHAR_H_
diff --git a/xfa/fgas/layout/cfgas_linkuserdata.cpp b/xfa/fgas/layout/cfgas_linkuserdata.cpp
new file mode 100644
index 0000000..cd64034
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_linkuserdata.cpp
@@ -0,0 +1,12 @@
+// Copyright 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 "xfa/fgas/layout/cfgas_linkuserdata.h"
+
+CFGAS_LinkUserData::CFGAS_LinkUserData(const WideString& wsText)
+    : m_wsURLContent(wsText) {}
+
+CFGAS_LinkUserData::~CFGAS_LinkUserData() = default;
diff --git a/xfa/fgas/layout/cfgas_linkuserdata.h b/xfa/fgas/layout/cfgas_linkuserdata.h
new file mode 100644
index 0000000..aeae940
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_linkuserdata.h
@@ -0,0 +1,26 @@
+// Copyright 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 XFA_FGAS_LAYOUT_CFGAS_LINKUSERDATA_H_
+#define XFA_FGAS_LAYOUT_CFGAS_LINKUSERDATA_H_
+
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/widestring.h"
+
+class CFGAS_LinkUserData final : public Retainable {
+ public:
+  CONSTRUCT_VIA_MAKE_RETAIN;
+
+  WideString GetLinkURL() const { return m_wsURLContent; }
+
+ private:
+  explicit CFGAS_LinkUserData(const WideString& wsText);
+  ~CFGAS_LinkUserData() override;
+
+  WideString m_wsURLContent;
+};
+
+#endif  // XFA_FGAS_LAYOUT_CFGAS_LINKUSERDATA_H_
diff --git a/xfa/fgas/layout/cfgas_rtfbreak.cpp b/xfa/fgas/layout/cfgas_rtfbreak.cpp
new file mode 100644
index 0000000..540a226
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_rtfbreak.cpp
@@ -0,0 +1,807 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/layout/cfgas_rtfbreak.h"
+
+#include <algorithm>
+
+#include "build/build_config.h"
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/stl_util.h"
+#include "core/fxge/text_char_pos.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/adapters.h"
+#include "third_party/base/numerics/safe_math.h"
+#include "xfa/fgas/font/cfgas_gefont.h"
+#include "xfa/fgas/layout/cfgas_char.h"
+#include "xfa/fgas/layout/cfgas_textpiece.h"
+#include "xfa/fgas/layout/cfgas_textuserdata.h"
+#include "xfa/fgas/layout/fgas_arabic.h"
+#include "xfa/fgas/layout/fgas_linebreak.h"
+
+CFGAS_RTFBreak::CFGAS_RTFBreak(Mask<LayoutStyle> dwLayoutStyles)
+    : CFGAS_Break(dwLayoutStyles) {
+  SetBreakStatus();
+  m_bPagination = !!(m_dwLayoutStyles & LayoutStyle::kPagination);
+}
+
+CFGAS_RTFBreak::~CFGAS_RTFBreak() = default;
+
+void CFGAS_RTFBreak::SetLineStartPos(float fLinePos) {
+  int32_t iLinePos = FXSYS_roundf(fLinePos * kConversionFactor);
+  iLinePos = std::min(iLinePos, m_iLineWidth);
+  iLinePos = std::max(iLinePos, m_iLineStart);
+  m_pCurLine->m_iStart = iLinePos;
+}
+
+void CFGAS_RTFBreak::AddPositionedTab(float fTabPos) {
+  int32_t iTabPos = std::min(
+      FXSYS_roundf(fTabPos * kConversionFactor) + m_iLineStart, m_iLineWidth);
+  auto it = std::lower_bound(m_PositionedTabs.begin(), m_PositionedTabs.end(),
+                             iTabPos);
+  if (it != m_PositionedTabs.end() && *it == iTabPos)
+    return;
+  m_PositionedTabs.insert(it, iTabPos);
+}
+
+void CFGAS_RTFBreak::SetUserData(
+    const RetainPtr<CFGAS_TextUserData>& pUserData) {
+  if (m_pUserData == pUserData)
+    return;
+
+  SetBreakStatus();
+  m_pUserData = pUserData;
+}
+
+bool CFGAS_RTFBreak::GetPositionedTab(int32_t* iTabPos) const {
+  auto it = std::upper_bound(m_PositionedTabs.begin(), m_PositionedTabs.end(),
+                             *iTabPos);
+  if (it == m_PositionedTabs.end())
+    return false;
+
+  *iTabPos = *it;
+  return true;
+}
+
+CFGAS_Char::BreakType CFGAS_RTFBreak::AppendChar(wchar_t wch) {
+  DCHECK(m_pCurLine);
+
+  FX_CHARTYPE chartype = pdfium::unicode::GetCharType(wch);
+  m_pCurLine->m_LineChars.emplace_back(wch, m_iHorizontalScale,
+                                       m_iVerticalScale);
+  CFGAS_Char* pCurChar = &m_pCurLine->m_LineChars.back();
+  pCurChar->m_iFontSize = m_iFontSize;
+  pCurChar->m_dwIdentity = m_dwIdentity;
+  pCurChar->m_pUserData = m_pUserData;
+
+  CFGAS_Char::BreakType dwRet1 = CFGAS_Char::BreakType::kNone;
+  if (chartype != FX_CHARTYPE::kCombination &&
+      GetUnifiedCharType(m_eCharType) != GetUnifiedCharType(chartype) &&
+      m_eCharType != FX_CHARTYPE::kUnknown &&
+      IsGreaterThanLineWidth(m_pCurLine->GetLineEnd()) &&
+      (m_eCharType != FX_CHARTYPE::kSpace ||
+       chartype != FX_CHARTYPE::kControl)) {
+    dwRet1 = EndBreak(CFGAS_Char::BreakType::kLine);
+    if (!m_pCurLine->m_LineChars.empty())
+      pCurChar = &m_pCurLine->m_LineChars.back();
+  }
+
+  CFGAS_Char::BreakType dwRet2 = CFGAS_Char::BreakType::kNone;
+  switch (chartype) {
+    case FX_CHARTYPE::kTab:
+      AppendChar_Tab(pCurChar);
+      break;
+    case FX_CHARTYPE::kControl:
+      dwRet2 = AppendChar_Control(pCurChar);
+      break;
+    case FX_CHARTYPE::kCombination:
+      AppendChar_Combination(pCurChar);
+      break;
+    case FX_CHARTYPE::kArabicAlef:
+    case FX_CHARTYPE::kArabicSpecial:
+    case FX_CHARTYPE::kArabicDistortion:
+    case FX_CHARTYPE::kArabicNormal:
+    case FX_CHARTYPE::kArabicForm:
+    case FX_CHARTYPE::kArabic:
+      dwRet2 = AppendChar_Arabic(pCurChar);
+      break;
+    case FX_CHARTYPE::kUnknown:
+    case FX_CHARTYPE::kSpace:
+    case FX_CHARTYPE::kNumeric:
+    case FX_CHARTYPE::kNormal:
+      dwRet2 = AppendChar_Others(pCurChar);
+      break;
+  }
+
+  m_eCharType = chartype;
+  return std::max(dwRet1, dwRet2);
+}
+
+void CFGAS_RTFBreak::AppendChar_Combination(CFGAS_Char* pCurChar) {
+  absl::optional<uint16_t> iCharWidthRet;
+  if (m_pFont) {
+    iCharWidthRet = m_pFont->GetCharWidth(pCurChar->char_code());
+  }
+  FX_SAFE_INT32 iCharWidth = iCharWidthRet.value_or(0);
+  iCharWidth *= m_iFontSize;
+  iCharWidth *= m_iHorizontalScale;
+  iCharWidth /= 100;
+  CFGAS_Char* pLastChar = GetLastChar(0, false, true);
+  if (pLastChar && pLastChar->GetCharType() > FX_CHARTYPE::kCombination)
+    iCharWidth *= -1;
+  else
+    m_eCharType = FX_CHARTYPE::kCombination;
+
+  int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
+  pCurChar->m_iCharWidth = iCharWidthValid;
+  if (iCharWidthValid > 0) {
+    FX_SAFE_INT32 checked_width = m_pCurLine->m_iWidth;
+    checked_width += iCharWidthValid;
+    if (!checked_width.IsValid())
+      return;
+
+    m_pCurLine->m_iWidth = checked_width.ValueOrDie();
+  }
+}
+
+void CFGAS_RTFBreak::AppendChar_Tab(CFGAS_Char* pCurChar) {
+  if (!(m_dwLayoutStyles & LayoutStyle::kExpandTab))
+    return;
+
+  int32_t& iLineWidth = m_pCurLine->m_iWidth;
+  int32_t iCharWidth = iLineWidth;
+  FX_SAFE_INT32 iSafeCharWidth;
+  if (GetPositionedTab(&iCharWidth)) {
+    iSafeCharWidth = iCharWidth;
+  } else {
+    // Tab width is >= 160000, so this part does not need to be checked.
+    DCHECK(m_iTabWidth >= kMinimumTabWidth);
+    iSafeCharWidth = iLineWidth / m_iTabWidth + 1;
+    iSafeCharWidth *= m_iTabWidth;
+  }
+  iSafeCharWidth -= iLineWidth;
+
+  iCharWidth = iSafeCharWidth.ValueOrDefault(0);
+
+  pCurChar->m_iCharWidth = iCharWidth;
+  iLineWidth += iCharWidth;
+}
+
+CFGAS_Char::BreakType CFGAS_RTFBreak::AppendChar_Control(CFGAS_Char* pCurChar) {
+  CFGAS_Char::BreakType dwRet2 = CFGAS_Char::BreakType::kNone;
+  switch (pCurChar->char_code()) {
+    case L'\v':
+    case pdfium::unicode::kLineSeparator:
+      dwRet2 = CFGAS_Char::BreakType::kLine;
+      break;
+    case L'\f':
+      dwRet2 = CFGAS_Char::BreakType::kPage;
+      break;
+    case pdfium::unicode::kParagraphSeparator:
+      dwRet2 = CFGAS_Char::BreakType::kParagraph;
+      break;
+    default:
+      if (pCurChar->char_code() == m_wParagraphBreakChar)
+        dwRet2 = CFGAS_Char::BreakType::kParagraph;
+      break;
+  }
+  if (dwRet2 != CFGAS_Char::BreakType::kNone)
+    dwRet2 = EndBreak(dwRet2);
+
+  return dwRet2;
+}
+
+CFGAS_Char::BreakType CFGAS_RTFBreak::AppendChar_Arabic(CFGAS_Char* pCurChar) {
+  m_pCurLine->IncrementArabicCharCount();
+
+  CFGAS_Char* pLastChar = nullptr;
+  wchar_t wForm;
+  bool bAlef = false;
+  if (m_eCharType >= FX_CHARTYPE::kArabicAlef &&
+      m_eCharType <= FX_CHARTYPE::kArabicDistortion) {
+    pLastChar = GetLastChar(1, false, true);
+    if (pLastChar) {
+      m_pCurLine->m_iWidth -= pLastChar->m_iCharWidth;
+      CFGAS_Char* pPrevChar = GetLastChar(2, false, true);
+      wForm = pdfium::arabic::GetFormChar(pLastChar, pPrevChar, pCurChar);
+      bAlef = (wForm == pdfium::unicode::kZeroWidthNoBreakSpace &&
+               pLastChar->GetCharType() == FX_CHARTYPE::kArabicAlef);
+      FX_SAFE_INT32 iCharWidth = 0;
+      if (m_pFont) {
+        absl::optional<uint16_t> iCharWidthRet = m_pFont->GetCharWidth(wForm);
+        if (iCharWidthRet.has_value()) {
+          iCharWidth = iCharWidthRet.value();
+        } else {
+          iCharWidthRet = m_pFont->GetCharWidth(pLastChar->char_code());
+          iCharWidth = iCharWidthRet.value_or(0);
+        }
+      }
+      iCharWidth *= m_iFontSize;
+      iCharWidth *= m_iHorizontalScale;
+      iCharWidth /= 100;
+
+      int iCharWidthValid = iCharWidth.ValueOrDefault(0);
+      pLastChar->m_iCharWidth = iCharWidthValid;
+
+      FX_SAFE_INT32 checked_width = m_pCurLine->m_iWidth;
+      checked_width += iCharWidthValid;
+      if (!checked_width.IsValid())
+        return CFGAS_Char::BreakType::kNone;
+
+      m_pCurLine->m_iWidth = checked_width.ValueOrDie();
+      iCharWidth = 0;
+    }
+  }
+
+  wForm = pdfium::arabic::GetFormChar(pCurChar, bAlef ? nullptr : pLastChar,
+                                      nullptr);
+  FX_SAFE_INT32 iCharWidth = 0;
+  if (m_pFont) {
+    absl::optional<uint16_t> iCharWidthRet = m_pFont->GetCharWidth(wForm);
+    if (!iCharWidthRet.has_value())
+      iCharWidthRet = m_pFont->GetCharWidth(pCurChar->char_code());
+    iCharWidth = iCharWidthRet.value_or(0);
+    iCharWidth *= m_iFontSize;
+    iCharWidth *= m_iHorizontalScale;
+    iCharWidth /= 100;
+  }
+
+  int iCharWidthValid = iCharWidth.ValueOrDefault(0);
+  pCurChar->m_iCharWidth = iCharWidthValid;
+
+  FX_SAFE_INT32 checked_width = m_pCurLine->m_iWidth;
+  checked_width += iCharWidthValid;
+  if (!checked_width.IsValid())
+    return CFGAS_Char::BreakType::kNone;
+
+  m_pCurLine->m_iWidth = checked_width.ValueOrDie();
+
+  if (IsGreaterThanLineWidth(m_pCurLine->GetLineEnd()))
+    return EndBreak(CFGAS_Char::BreakType::kLine);
+  return CFGAS_Char::BreakType::kNone;
+}
+
+CFGAS_Char::BreakType CFGAS_RTFBreak::AppendChar_Others(CFGAS_Char* pCurChar) {
+  FX_CHARTYPE chartype = pCurChar->GetCharType();
+  wchar_t wForm = pCurChar->char_code();
+  FX_SAFE_INT32 iCharWidth = 0;
+  if (m_pFont) {
+    iCharWidth = m_pFont->GetCharWidth(wForm).value_or(0);
+  }
+  iCharWidth *= m_iFontSize;
+  iCharWidth *= m_iHorizontalScale;
+  iCharWidth /= 100;
+  iCharWidth += m_iCharSpace;
+
+  int iValidCharWidth = iCharWidth.ValueOrDefault(0);
+  pCurChar->m_iCharWidth = iValidCharWidth;
+
+  FX_SAFE_INT32 checked_width = m_pCurLine->m_iWidth;
+  checked_width += iValidCharWidth;
+  if (!checked_width.IsValid())
+    return CFGAS_Char::BreakType::kNone;
+
+  m_pCurLine->m_iWidth = checked_width.ValueOrDie();
+  if (chartype != FX_CHARTYPE::kSpace &&
+      IsGreaterThanLineWidth(m_pCurLine->GetLineEnd())) {
+    return EndBreak(CFGAS_Char::BreakType::kLine);
+  }
+  return CFGAS_Char::BreakType::kNone;
+}
+
+CFGAS_Char::BreakType CFGAS_RTFBreak::EndBreak(CFGAS_Char::BreakType dwStatus) {
+  DCHECK(dwStatus != CFGAS_Char::BreakType::kNone);
+
+  ++m_dwIdentity;
+  if (!m_pCurLine->m_LinePieces.empty()) {
+    if (dwStatus != CFGAS_Char::BreakType::kPiece)
+      m_pCurLine->m_LinePieces.back().SetStatus(dwStatus);
+    return m_pCurLine->m_LinePieces.back().GetStatus();
+  }
+
+  if (HasLine()) {
+    if (m_Lines[m_iReadyLineIndex].m_LinePieces.empty())
+      return CFGAS_Char::BreakType::kNone;
+
+    if (dwStatus != CFGAS_Char::BreakType::kPiece)
+      m_Lines[m_iReadyLineIndex].m_LinePieces.back().SetStatus(dwStatus);
+    return m_Lines[m_iReadyLineIndex].m_LinePieces.back().GetStatus();
+  }
+
+  CFGAS_Char* tc = m_pCurLine->LastChar();
+  if (!tc)
+    return CFGAS_Char::BreakType::kNone;
+
+  tc->m_dwStatus = dwStatus;
+  if (dwStatus == CFGAS_Char::BreakType::kPiece)
+    return dwStatus;
+
+  m_iReadyLineIndex = m_pCurLine == &m_Lines[0] ? 0 : 1;
+  CFGAS_BreakLine* pNextLine = &m_Lines[1 - m_iReadyLineIndex];
+  bool bAllChars = m_iAlignment == LineAlignment::Justified ||
+                   m_iAlignment == LineAlignment::Distributed;
+
+  if (!EndBreakSplitLine(pNextLine, bAllChars, dwStatus)) {
+    std::deque<TPO> tpos = EndBreakBidiLine(dwStatus);
+    if (!m_bPagination && m_iAlignment != LineAlignment::Left)
+      EndBreakAlignment(tpos, bAllChars, dwStatus);
+  }
+  m_pCurLine = pNextLine;
+  m_pCurLine->m_iStart = m_iLineStart;
+
+  CFGAS_Char* pTC = GetLastChar(0, false, true);
+  m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE::kUnknown;
+  return dwStatus;
+}
+
+bool CFGAS_RTFBreak::EndBreakSplitLine(CFGAS_BreakLine* pNextLine,
+                                       bool bAllChars,
+                                       CFGAS_Char::BreakType dwStatus) {
+  bool bDone = false;
+  if (IsGreaterThanLineWidth(m_pCurLine->GetLineEnd())) {
+    const CFGAS_Char* tc = m_pCurLine->LastChar();
+    switch (tc->GetCharType()) {
+      case FX_CHARTYPE::kTab:
+      case FX_CHARTYPE::kControl:
+      case FX_CHARTYPE::kSpace:
+        break;
+      default:
+        SplitTextLine(m_pCurLine, pNextLine, !m_bPagination && bAllChars);
+        bDone = true;
+        break;
+    }
+  }
+
+  if (!m_bPagination) {
+    if (bAllChars && !bDone) {
+      int32_t endPos = m_pCurLine->GetLineEnd();
+      GetBreakPos(m_pCurLine->m_LineChars, bAllChars, true, &endPos);
+    }
+    return false;
+  }
+
+  const CFGAS_Char* pCurChars = m_pCurLine->m_LineChars.data();
+  CFGAS_BreakPiece tp;
+  tp.SetChars(&m_pCurLine->m_LineChars);
+  bool bNew = true;
+  uint32_t dwIdentity = static_cast<uint32_t>(-1);
+  int32_t iLast = fxcrt::CollectionSize<int32_t>(m_pCurLine->m_LineChars) - 1;
+  int32_t j = 0;
+  for (int32_t i = 0; i <= iLast;) {
+    const CFGAS_Char* pTC = pCurChars + i;
+    if (bNew) {
+      tp.SetStartChar(i);
+      tp.IncrementStartPos(tp.GetWidth());
+      tp.SetWidth(0);
+      tp.SetStatus(pTC->m_dwStatus);
+      tp.SetFontSize(pTC->m_iFontSize);
+      tp.SetHorizontalScale(pTC->horizonal_scale());
+      tp.SetVerticalScale(pTC->vertical_scale());
+      dwIdentity = pTC->m_dwIdentity;
+      tp.SetUserData(pTC->m_pUserData);
+      j = i;
+      bNew = false;
+    }
+
+    if (i == iLast || pTC->m_dwStatus != CFGAS_Char::BreakType::kNone ||
+        pTC->m_dwIdentity != dwIdentity) {
+      if (pTC->m_dwIdentity == dwIdentity) {
+        tp.SetStatus(pTC->m_dwStatus);
+        tp.IncrementWidth(pTC->m_iCharWidth);
+        ++i;
+      }
+      tp.SetCharCount(i - j);
+      m_pCurLine->m_LinePieces.push_back(tp);
+      bNew = true;
+    } else {
+      tp.IncrementWidth(pTC->m_iCharWidth);
+      ++i;
+    }
+  }
+  return true;
+}
+
+std::deque<CFGAS_Break::TPO> CFGAS_RTFBreak::EndBreakBidiLine(
+    CFGAS_Char::BreakType dwStatus) {
+  CFGAS_Char* pTC;
+  std::vector<CFGAS_Char>& chars = m_pCurLine->m_LineChars;
+  if (!m_bPagination && m_pCurLine->HasArabicChar()) {
+    size_t iBidiNum = 0;
+    for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
+      pTC = &chars[i];
+      pTC->m_iBidiPos = static_cast<int32_t>(i);
+      if (pTC->GetCharType() != FX_CHARTYPE::kControl)
+        iBidiNum = i;
+      if (i == 0)
+        pTC->m_iBidiLevel = 1;
+    }
+    CFGAS_Char::BidiLine(&chars, iBidiNum + 1);
+  } else {
+    for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
+      pTC = &chars[i];
+      pTC->m_iBidiLevel = 0;
+      pTC->m_iBidiPos = 0;
+      pTC->m_iBidiOrder = 0;
+    }
+  }
+
+  CFGAS_BreakPiece tp;
+  tp.SetStatus(CFGAS_Char::BreakType::kPiece);
+  tp.SetStartPos(m_pCurLine->m_iStart);
+  tp.SetChars(&chars);
+
+  int32_t iBidiLevel = -1;
+  int32_t iCharWidth;
+  std::deque<TPO> tpos;
+  uint32_t dwIdentity = static_cast<uint32_t>(-1);
+  int32_t i = 0;
+  int32_t j = 0;
+  int32_t iCount = fxcrt::CollectionSize<int32_t>(m_pCurLine->m_LineChars);
+  while (i < iCount) {
+    pTC = &chars[i];
+    if (iBidiLevel < 0) {
+      iBidiLevel = pTC->m_iBidiLevel;
+      iCharWidth = pTC->m_iCharWidth;
+      tp.SetWidth(iCharWidth < 1 ? 0 : iCharWidth);
+      tp.SetBidiLevel(iBidiLevel);
+      tp.SetBidiPos(pTC->m_iBidiOrder);
+      tp.SetFontSize(pTC->m_iFontSize);
+      tp.SetHorizontalScale(pTC->horizonal_scale());
+      tp.SetVerticalScale(pTC->vertical_scale());
+      dwIdentity = pTC->m_dwIdentity;
+      tp.SetUserData(pTC->m_pUserData);
+      tp.SetStatus(CFGAS_Char::BreakType::kPiece);
+      ++i;
+    } else if (iBidiLevel != pTC->m_iBidiLevel ||
+               pTC->m_dwIdentity != dwIdentity) {
+      tp.SetCharCount(i - tp.GetStartChar());
+      m_pCurLine->m_LinePieces.push_back(tp);
+      tp.IncrementStartPos(tp.GetWidth());
+      tp.SetStartChar(i);
+      tpos.push_back({j++, tp.GetBidiPos()});
+      iBidiLevel = -1;
+    } else {
+      iCharWidth = pTC->m_iCharWidth;
+      if (iCharWidth > 0)
+        tp.IncrementWidth(iCharWidth);
+      ++i;
+    }
+  }
+
+  if (i > tp.GetStartChar()) {
+    tp.SetStatus(dwStatus);
+    tp.SetCharCount(i - tp.GetStartChar());
+    m_pCurLine->m_LinePieces.push_back(tp);
+    tpos.push_back({j, tp.GetBidiPos()});
+  }
+
+  std::sort(tpos.begin(), tpos.end());
+  int32_t iStartPos = m_pCurLine->m_iStart;
+  for (const auto& it : tpos) {
+    CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[it.index];
+    ttp.SetStartPos(iStartPos);
+    iStartPos += ttp.GetWidth();
+  }
+  return tpos;
+}
+
+void CFGAS_RTFBreak::EndBreakAlignment(const std::deque<TPO>& tpos,
+                                       bool bAllChars,
+                                       CFGAS_Char::BreakType dwStatus) {
+  int32_t iNetWidth = m_pCurLine->m_iWidth;
+  int32_t iGapChars = 0;
+  bool bFind = false;
+  for (const TPO& pos : pdfium::base::Reversed(tpos)) {
+    const CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[pos.index];
+    if (!bFind)
+      iNetWidth = ttp.GetEndPos();
+
+    bool bArabic = FX_IsOdd(ttp.GetBidiLevel());
+    int32_t j = bArabic ? 0 : ttp.GetCharCount() - 1;
+    while (j > -1 && j < ttp.GetCharCount()) {
+      const CFGAS_Char* tc = ttp.GetChar(j);
+      if (tc->m_eLineBreakType == FX_LINEBREAKTYPE::kDIRECT_BRK)
+        ++iGapChars;
+
+      if (!bFind || !bAllChars) {
+        FX_CHARTYPE dwCharType = tc->GetCharType();
+        if (dwCharType == FX_CHARTYPE::kSpace ||
+            dwCharType == FX_CHARTYPE::kControl) {
+          if (!bFind) {
+            int32_t iCharWidth = tc->m_iCharWidth;
+            if (bAllChars && iCharWidth > 0)
+              iNetWidth -= iCharWidth;
+          }
+        } else {
+          bFind = true;
+          if (!bAllChars)
+            break;
+        }
+      }
+      j += bArabic ? 1 : -1;
+    }
+    if (!bAllChars && bFind)
+      break;
+  }
+
+  int32_t iOffset = m_iLineWidth - iNetWidth;
+  if (iGapChars > 0 && (m_iAlignment == LineAlignment::Distributed ||
+                        (m_iAlignment == LineAlignment::Justified &&
+                         dwStatus != CFGAS_Char::BreakType::kParagraph))) {
+    int32_t iStart = -1;
+    for (const auto& tpo : tpos) {
+      CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[tpo.index];
+      if (iStart < 0)
+        iStart = ttp.GetStartPos();
+      else
+        ttp.SetStartPos(iStart);
+
+      for (int32_t j = 0; j < ttp.GetCharCount(); ++j) {
+        CFGAS_Char* tc = ttp.GetChar(j);
+        if (tc->m_eLineBreakType != FX_LINEBREAKTYPE::kDIRECT_BRK ||
+            tc->m_iCharWidth < 0) {
+          continue;
+        }
+        int32_t k = iOffset / iGapChars;
+        tc->m_iCharWidth += k;
+        ttp.IncrementWidth(k);
+        iOffset -= k;
+        --iGapChars;
+        if (iGapChars < 1)
+          break;
+      }
+      iStart += ttp.GetWidth();
+    }
+  } else if (m_iAlignment == LineAlignment::Right ||
+             m_iAlignment == LineAlignment::Center) {
+    if (m_iAlignment == LineAlignment::Center)
+      iOffset /= 2;
+    if (iOffset > 0) {
+      for (auto& ttp : m_pCurLine->m_LinePieces)
+        ttp.IncrementStartPos(iOffset);
+    }
+  }
+}
+
+int32_t CFGAS_RTFBreak::GetBreakPos(std::vector<CFGAS_Char>& tca,
+                                    bool bAllChars,
+                                    bool bOnlyBrk,
+                                    int32_t* pEndPos) {
+  int32_t iLength = fxcrt::CollectionSize<int32_t>(tca) - 1;
+  if (iLength < 1)
+    return iLength;
+
+  int32_t iBreak = -1;
+  int32_t iBreakPos = -1;
+  int32_t iIndirect = -1;
+  int32_t iIndirectPos = -1;
+  int32_t iLast = -1;
+  int32_t iLastPos = -1;
+  if (*pEndPos <= m_iLineWidth) {
+    if (!bAllChars)
+      return iLength;
+
+    iBreak = iLength;
+    iBreakPos = *pEndPos;
+  }
+
+  CFGAS_Char* pCharArray = tca.data();
+  CFGAS_Char* pCur = pCharArray + iLength;
+  --iLength;
+  if (bAllChars)
+    pCur->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
+
+  FX_BREAKPROPERTY nNext = pdfium::unicode::GetBreakProperty(pCur->char_code());
+  int32_t iCharWidth = pCur->m_iCharWidth;
+  if (iCharWidth > 0)
+    *pEndPos -= iCharWidth;
+
+  while (iLength >= 0) {
+    pCur = pCharArray + iLength;
+    FX_BREAKPROPERTY nCur =
+        pdfium::unicode::GetBreakProperty(pCur->char_code());
+    bool bNeedBreak = false;
+    FX_LINEBREAKTYPE eType;
+    if (nCur == FX_BREAKPROPERTY::kTB) {
+      bNeedBreak = true;
+      eType = nNext == FX_BREAKPROPERTY::kTB
+                  ? FX_LINEBREAKTYPE::kPROHIBITED_BRK
+                  : GetLineBreakTypeFromPair(nCur, nNext);
+    } else {
+      if (nCur == FX_BREAKPROPERTY::kSP)
+        bNeedBreak = true;
+
+      eType = nNext == FX_BREAKPROPERTY::kSP
+                  ? FX_LINEBREAKTYPE::kPROHIBITED_BRK
+                  : GetLineBreakTypeFromPair(nCur, nNext);
+    }
+    if (bAllChars)
+      pCur->m_eLineBreakType = eType;
+
+    if (!bOnlyBrk) {
+      iCharWidth = pCur->m_iCharWidth;
+      if (*pEndPos <= m_iLineWidth || bNeedBreak) {
+        if (eType == FX_LINEBREAKTYPE::kDIRECT_BRK && iBreak < 0) {
+          iBreak = iLength;
+          iBreakPos = *pEndPos;
+          if (!bAllChars)
+            return iLength;
+        } else if (eType == FX_LINEBREAKTYPE::kINDIRECT_BRK && iIndirect < 0) {
+          iIndirect = iLength;
+          iIndirectPos = *pEndPos;
+        }
+        if (iLast < 0) {
+          iLast = iLength;
+          iLastPos = *pEndPos;
+        }
+      }
+      if (iCharWidth > 0)
+        *pEndPos -= iCharWidth;
+    }
+    nNext = nCur;
+    --iLength;
+  }
+  if (bOnlyBrk)
+    return 0;
+
+  if (iBreak > -1) {
+    *pEndPos = iBreakPos;
+    return iBreak;
+  }
+  if (iIndirect > -1) {
+    *pEndPos = iIndirectPos;
+    return iIndirect;
+  }
+  if (iLast > -1) {
+    *pEndPos = iLastPos;
+    return iLast;
+  }
+  return 0;
+}
+
+void CFGAS_RTFBreak::SplitTextLine(CFGAS_BreakLine* pCurLine,
+                                   CFGAS_BreakLine* pNextLine,
+                                   bool bAllChars) {
+  DCHECK(pCurLine);
+  DCHECK(pNextLine);
+
+  if (pCurLine->m_LineChars.size() < 2)
+    return;
+
+  int32_t iEndPos = pCurLine->GetLineEnd();
+  std::vector<CFGAS_Char>& curChars = pCurLine->m_LineChars;
+  int32_t iCharPos = GetBreakPos(curChars, bAllChars, false, &iEndPos);
+  if (iCharPos < 0)
+    iCharPos = 0;
+
+  ++iCharPos;
+  if (iCharPos >= fxcrt::CollectionSize<int32_t>(pCurLine->m_LineChars)) {
+    pNextLine->Clear();
+    curChars[iCharPos - 1].m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
+    return;
+  }
+
+  pNextLine->m_LineChars =
+      std::vector<CFGAS_Char>(curChars.begin() + iCharPos, curChars.end());
+  curChars.erase(curChars.begin() + iCharPos, curChars.end());
+  pNextLine->m_iStart = pCurLine->m_iStart;
+  pNextLine->m_iWidth = pCurLine->GetLineEnd() - iEndPos;
+  pCurLine->m_iWidth = iEndPos;
+  curChars[iCharPos - 1].m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
+
+  for (size_t i = 0; i < pNextLine->m_LineChars.size(); ++i) {
+    if (pNextLine->m_LineChars[i].GetCharType() >= FX_CHARTYPE::kArabicAlef) {
+      pCurLine->DecrementArabicCharCount();
+      pNextLine->IncrementArabicCharCount();
+    }
+    pNextLine->m_LineChars[i].m_dwStatus = CFGAS_Char::BreakType::kNone;
+  }
+}
+
+size_t CFGAS_RTFBreak::GetDisplayPos(const CFGAS_TextPiece* pPiece,
+                                     std::vector<TextCharPos>* pCharPos) const {
+  if (pPiece->iChars == 0 || !pPiece->pFont)
+    return 0;
+
+  RetainPtr<CFGAS_GEFont> pFont = pPiece->pFont;
+  CFX_RectF rtText(pPiece->rtPiece);
+  const bool bRTLPiece = FX_IsOdd(pPiece->iBidiLevel);
+  const float fFontSize = pPiece->fFontSize;
+  const int32_t iFontSize = FXSYS_roundf(fFontSize * 20.0f);
+  if (iFontSize == 0)
+    return 0;
+
+  const int32_t iAscent = pFont->GetAscent();
+  const int32_t iDescent = pFont->GetDescent();
+  const int32_t iMaxHeight = iAscent - iDescent;
+  const float fAscent = iMaxHeight ? fFontSize * iAscent / iMaxHeight : 0;
+  wchar_t wPrev = pdfium::unicode::kZeroWidthNoBreakSpace;
+  wchar_t wNext;
+  float fX = rtText.left;
+  int32_t iHorScale = pPiece->iHorScale;
+  int32_t iVerScale = pPiece->iVerScale;
+  if (bRTLPiece)
+    fX = rtText.right();
+
+  float fY = rtText.top + fAscent;
+  size_t szCount = 0;
+  for (int32_t i = 0; i < pPiece->iChars; ++i) {
+    TextCharPos& current_char_pos = (*pCharPos)[szCount];
+    wchar_t wch = pPiece->szText[i];
+    int32_t iWidth = pPiece->Widths[i];
+    FX_CHARTYPE dwCharType = pdfium::unicode::GetCharType(wch);
+    if (iWidth == 0) {
+      if (dwCharType == FX_CHARTYPE::kArabicAlef)
+        wPrev = pdfium::unicode::kZeroWidthNoBreakSpace;
+      continue;
+    }
+
+    int iCharWidth = abs(iWidth);
+    const bool bEmptyChar = (dwCharType >= FX_CHARTYPE::kTab &&
+                             dwCharType <= FX_CHARTYPE::kControl);
+    if (!bEmptyChar)
+      ++szCount;
+
+    iCharWidth /= iFontSize;
+    wchar_t wForm = wch;
+    if (dwCharType >= FX_CHARTYPE::kArabicAlef) {
+      if (i + 1 < pPiece->iChars) {
+        wNext = pPiece->szText[i + 1];
+        if (pPiece->Widths[i + 1] < 0 && i + 2 < pPiece->iChars)
+          wNext = pPiece->szText[i + 2];
+      } else {
+        wNext = pdfium::unicode::kZeroWidthNoBreakSpace;
+      }
+      wForm = pdfium::arabic::GetFormChar(wch, wPrev, wNext);
+    } else if (bRTLPiece) {
+      wForm = pdfium::unicode::GetMirrorChar(wch);
+    }
+
+    if (!bEmptyChar) {
+      current_char_pos.m_GlyphIndex = pFont->GetGlyphIndex(wForm);
+      if (current_char_pos.m_GlyphIndex == 0xFFFF)
+        current_char_pos.m_GlyphIndex = pFont->GetGlyphIndex(wch);
+#if BUILDFLAG(IS_APPLE)
+      current_char_pos.m_ExtGID = current_char_pos.m_GlyphIndex;
+#endif
+      current_char_pos.m_FontCharWidth = iCharWidth;
+    }
+
+    float fCharWidth = fFontSize * iCharWidth / 1000.0f;
+    if (bRTLPiece && dwCharType != FX_CHARTYPE::kCombination)
+      fX -= fCharWidth;
+
+    if (!bEmptyChar)
+      current_char_pos.m_Origin = CFX_PointF(fX, fY);
+    if (!bRTLPiece && dwCharType != FX_CHARTYPE::kCombination)
+      fX += fCharWidth;
+
+    if (!bEmptyChar) {
+      current_char_pos.m_bGlyphAdjust = true;
+      current_char_pos.m_AdjustMatrix[0] = -1;
+      current_char_pos.m_AdjustMatrix[1] = 0;
+      current_char_pos.m_AdjustMatrix[2] = 0;
+      current_char_pos.m_AdjustMatrix[3] = 1;
+      current_char_pos.m_Origin.y += fAscent * iVerScale / 100.0f;
+      current_char_pos.m_Origin.y -= fAscent;
+
+      if (iHorScale != 100 || iVerScale != 100) {
+        current_char_pos.m_AdjustMatrix[0] =
+            current_char_pos.m_AdjustMatrix[0] * iHorScale / 100.0f;
+        current_char_pos.m_AdjustMatrix[1] =
+            current_char_pos.m_AdjustMatrix[1] * iHorScale / 100.0f;
+        current_char_pos.m_AdjustMatrix[2] =
+            current_char_pos.m_AdjustMatrix[2] * iVerScale / 100.0f;
+        current_char_pos.m_AdjustMatrix[3] =
+            current_char_pos.m_AdjustMatrix[3] * iVerScale / 100.0f;
+      }
+    }
+    if (iWidth > 0)
+      wPrev = wch;
+  }
+  return szCount;
+}
diff --git a/xfa/fgas/layout/cfgas_rtfbreak.h b/xfa/fgas/layout/cfgas_rtfbreak.h
new file mode 100644
index 0000000..8f94e9a
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_rtfbreak.h
@@ -0,0 +1,78 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_LAYOUT_CFGAS_RTFBREAK_H_
+#define XFA_FGAS_LAYOUT_CFGAS_RTFBREAK_H_
+
+#include <deque>
+#include <vector>
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_unicode.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "xfa/fgas/layout/cfgas_break.h"
+
+class CFGAS_TextPiece;
+class CFGAS_TextUserData;
+class TextCharPos;
+
+class CFGAS_RTFBreak final : public CFGAS_Break {
+ public:
+  enum class LineAlignment : uint8_t {
+    Left = 0,
+    Center,
+    Right,
+    Justified,
+    Distributed
+  };
+
+  explicit CFGAS_RTFBreak(Mask<LayoutStyle> dwLayoutStyles);
+  ~CFGAS_RTFBreak() override;
+
+  void SetLineStartPos(float fLinePos);
+
+  void SetAlignment(LineAlignment align) { m_iAlignment = align; }
+  void SetUserData(const RetainPtr<CFGAS_TextUserData>& pUserData);
+
+  void AddPositionedTab(float fTabPos);
+
+  CFGAS_Char::BreakType EndBreak(CFGAS_Char::BreakType dwStatus);
+
+  size_t GetDisplayPos(const CFGAS_TextPiece* pPiece,
+                       std::vector<TextCharPos>* pCharPos) const;
+
+  CFGAS_Char::BreakType AppendChar(wchar_t wch);
+
+ private:
+  void AppendChar_Combination(CFGAS_Char* pCurChar);
+  void AppendChar_Tab(CFGAS_Char* pCurChar);
+  CFGAS_Char::BreakType AppendChar_Control(CFGAS_Char* pCurChar);
+  CFGAS_Char::BreakType AppendChar_Arabic(CFGAS_Char* pCurChar);
+  CFGAS_Char::BreakType AppendChar_Others(CFGAS_Char* pCurChar);
+  bool GetPositionedTab(int32_t* iTabPos) const;
+
+  int32_t GetBreakPos(std::vector<CFGAS_Char>& tca,
+                      bool bAllChars,
+                      bool bOnlyBrk,
+                      int32_t* pEndPos);
+  void SplitTextLine(CFGAS_BreakLine* pCurLine,
+                     CFGAS_BreakLine* pNextLine,
+                     bool bAllChars);
+  bool EndBreakSplitLine(CFGAS_BreakLine* pNextLine,
+                         bool bAllChars,
+                         CFGAS_Char::BreakType dwStatus);
+  std::deque<TPO> EndBreakBidiLine(CFGAS_Char::BreakType dwStatus);
+  void EndBreakAlignment(const std::deque<TPO>& tpos,
+                         bool bAllChars,
+                         CFGAS_Char::BreakType dwStatus);
+
+  bool m_bPagination = false;
+  LineAlignment m_iAlignment = LineAlignment::Left;
+  std::vector<int32_t> m_PositionedTabs;
+  RetainPtr<CFGAS_TextUserData> m_pUserData;
+};
+
+#endif  // XFA_FGAS_LAYOUT_CFGAS_RTFBREAK_H_
diff --git a/xfa/fgas/layout/cfgas_rtfbreak_unittest.cpp b/xfa/fgas/layout/cfgas_rtfbreak_unittest.cpp
new file mode 100644
index 0000000..22421f3
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_rtfbreak_unittest.cpp
@@ -0,0 +1,91 @@
+// Copyright 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 "xfa/fgas/layout/cfgas_rtfbreak.h"
+
+#include <memory>
+#include <utility>
+
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxge/cfx_font.h"
+#include "core/fxge/cfx_gemodule.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "xfa/fgas/font/cfgas_fontmgr.h"
+#include "xfa/fgas/font/cfgas_gefont.h"
+#include "xfa/fgas/layout/cfgas_char.h"
+
+class CFGAS_RTFBreakTest : public testing::Test {
+ public:
+  void SetUp() override {
+    const wchar_t kFontFamily[] = L"Arimo Bold";
+    font_ = CFGAS_GEFont::LoadFont(kFontFamily, 0, FX_CodePage::kDefANSI);
+    ASSERT_TRUE(font_);
+  }
+
+  std::unique_ptr<CFGAS_RTFBreak> CreateBreak(
+      Mask<CFGAS_Break::LayoutStyle> layout_styles) {
+    auto rtf_break = std::make_unique<CFGAS_RTFBreak>(layout_styles);
+    rtf_break->SetFont(font_);
+    return rtf_break;
+  }
+
+ private:
+  RetainPtr<CFGAS_GEFont> font_;
+};
+
+// As soon as you get one of the control characters the break is complete
+// and must be consumed before you get any more characters ....
+
+TEST_F(CFGAS_RTFBreakTest, AddChars) {
+  auto rtf_break = CreateBreak(CFGAS_Break::LayoutStyle::kExpandTab);
+  WideString str(L"Input String.");
+  for (wchar_t ch : str)
+    EXPECT_EQ(CFGAS_Char::BreakType::kNone, rtf_break->AppendChar(ch));
+
+  EXPECT_EQ(CFGAS_Char::BreakType::kParagraph, rtf_break->AppendChar(L'\n'));
+  ASSERT_EQ(1, rtf_break->CountBreakPieces());
+  EXPECT_EQ(str + L"\n", rtf_break->GetBreakPieceUnstable(0)->GetString());
+
+  rtf_break->ClearBreakPieces();
+  rtf_break->Reset();
+  EXPECT_EQ(0, rtf_break->GetCurrentLineForTesting()->GetLineEnd());
+
+  str = L"Second str.";
+  for (wchar_t ch : str)
+    EXPECT_EQ(CFGAS_Char::BreakType::kNone, rtf_break->AppendChar(ch));
+
+  // Force the end of the break at the end of the string.
+  rtf_break->EndBreak(CFGAS_Char::BreakType::kParagraph);
+  ASSERT_EQ(1, rtf_break->CountBreakPieces());
+  EXPECT_EQ(str, rtf_break->GetBreakPieceUnstable(0)->GetString());
+}
+
+TEST_F(CFGAS_RTFBreakTest, ControlCharacters) {
+  auto rtf_break = CreateBreak(CFGAS_Break::LayoutStyle::kExpandTab);
+  EXPECT_EQ(CFGAS_Char::BreakType::kLine, rtf_break->AppendChar(L'\v'));
+  EXPECT_EQ(CFGAS_Char::BreakType::kPage, rtf_break->AppendChar(L'\f'));
+  EXPECT_EQ(CFGAS_Char::BreakType::kParagraph,
+            rtf_break->AppendChar(pdfium::unicode::kParagraphSeparator));
+  EXPECT_EQ(CFGAS_Char::BreakType::kParagraph, rtf_break->AppendChar(L'\n'));
+
+  ASSERT_EQ(1, rtf_break->CountBreakPieces());
+  EXPECT_EQ(L"\v", rtf_break->GetBreakPieceUnstable(0)->GetString());
+}
+
+TEST_F(CFGAS_RTFBreakTest, BidiLine) {
+  auto rtf_break = CreateBreak(CFGAS_Break::LayoutStyle::kExpandTab);
+  rtf_break->SetLineBreakTolerance(1);
+  rtf_break->SetFontSize(12);
+
+  WideString input = WideString::FromUTF8(ByteStringView("\xa\x0\xa\xa", 4));
+  for (wchar_t ch : input)
+    rtf_break->AppendChar(ch);
+
+  std::vector<CFGAS_Char> chars =
+      rtf_break->GetCurrentLineForTesting()->m_LineChars;
+  CFGAS_Char::BidiLine(&chars, chars.size());
+  EXPECT_EQ(3u, chars.size());
+}
diff --git a/xfa/fgas/layout/cfgas_textpiece.cpp b/xfa/fgas/layout/cfgas_textpiece.cpp
new file mode 100644
index 0000000..e636e4f
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_textpiece.cpp
@@ -0,0 +1,13 @@
+// Copyright 2019 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/layout/cfgas_textpiece.h"
+
+#include "xfa/fgas/font/cfgas_gefont.h"
+
+CFGAS_TextPiece::CFGAS_TextPiece() = default;
+
+CFGAS_TextPiece::~CFGAS_TextPiece() = default;
diff --git a/xfa/fgas/layout/cfgas_textpiece.h b/xfa/fgas/layout/cfgas_textpiece.h
new file mode 100644
index 0000000..5afc30a
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_textpiece.h
@@ -0,0 +1,34 @@
+// Copyright 2019 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_LAYOUT_CFGAS_TEXTPIECE_H_
+#define XFA_FGAS_LAYOUT_CFGAS_TEXTPIECE_H_
+
+#include <vector>
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/widestring.h"
+
+class CFGAS_GEFont;
+
+class CFGAS_TextPiece {
+ public:
+  CFGAS_TextPiece();
+  ~CFGAS_TextPiece();
+
+  WideString szText;
+  std::vector<int32_t> Widths;
+  int32_t iChars = 0;
+  int32_t iHorScale = 0;
+  int32_t iVerScale = 0;
+  int32_t iBidiLevel = 0;
+  float fFontSize = 0.0f;
+  CFX_RectF rtPiece;
+  RetainPtr<CFGAS_GEFont> pFont;
+};
+
+#endif  // XFA_FGAS_LAYOUT_CFGAS_TEXTPIECE_H_
diff --git a/xfa/fgas/layout/cfgas_textuserdata.cpp b/xfa/fgas/layout/cfgas_textuserdata.cpp
new file mode 100644
index 0000000..9b3ae43
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_textuserdata.cpp
@@ -0,0 +1,23 @@
+// Copyright 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 "xfa/fgas/layout/cfgas_textuserdata.h"
+
+#include "core/fxcrt/css/cfx_css.h"
+#include "core/fxcrt/css/cfx_csscomputedstyle.h"
+#include "core/fxcrt/css/cfx_cssstyleselector.h"
+#include "xfa/fgas/layout/cfgas_linkuserdata.h"
+
+CFGAS_TextUserData::CFGAS_TextUserData(
+    const RetainPtr<CFX_CSSComputedStyle>& pStyle)
+    : m_pStyle(pStyle) {}
+
+CFGAS_TextUserData::CFGAS_TextUserData(
+    const RetainPtr<CFX_CSSComputedStyle>& pStyle,
+    const RetainPtr<CFGAS_LinkUserData>& pLinkData)
+    : m_pStyle(pStyle), m_pLinkData(pLinkData) {}
+
+CFGAS_TextUserData::~CFGAS_TextUserData() = default;
diff --git a/xfa/fgas/layout/cfgas_textuserdata.h b/xfa/fgas/layout/cfgas_textuserdata.h
new file mode 100644
index 0000000..4d56bc2
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_textuserdata.h
@@ -0,0 +1,29 @@
+// Copyright 2017 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_LAYOUT_CFGAS_TEXTUSERDATA_H_
+#define XFA_FGAS_LAYOUT_CFGAS_TEXTUSERDATA_H_
+
+#include "core/fxcrt/retain_ptr.h"
+
+class CFGAS_LinkUserData;
+class CFX_CSSComputedStyle;
+
+class CFGAS_TextUserData final : public Retainable {
+ public:
+  CONSTRUCT_VIA_MAKE_RETAIN;
+
+  RetainPtr<CFX_CSSComputedStyle> m_pStyle;
+  RetainPtr<CFGAS_LinkUserData> m_pLinkData;
+
+ private:
+  explicit CFGAS_TextUserData(const RetainPtr<CFX_CSSComputedStyle>& pStyle);
+  CFGAS_TextUserData(const RetainPtr<CFX_CSSComputedStyle>& pStyle,
+                     const RetainPtr<CFGAS_LinkUserData>& pLinkData);
+  ~CFGAS_TextUserData() override;
+};
+
+#endif  // XFA_FGAS_LAYOUT_CFGAS_TEXTUSERDATA_H_
diff --git a/xfa/fgas/layout/cfgas_txtbreak.cpp b/xfa/fgas/layout/cfgas_txtbreak.cpp
new file mode 100644
index 0000000..22a6277
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_txtbreak.cpp
@@ -0,0 +1,932 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/layout/cfgas_txtbreak.h"
+
+#include <algorithm>
+
+#include "build/build_config.h"
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/stl_util.h"
+#include "core/fxge/text_char_pos.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/adapters.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "xfa/fgas/font/cfgas_gefont.h"
+#include "xfa/fgas/layout/cfgas_char.h"
+#include "xfa/fgas/layout/fgas_arabic.h"
+#include "xfa/fgas/layout/fgas_linebreak.h"
+
+namespace {
+
+struct FX_FORMCHAR {
+  uint16_t wch;
+  uint16_t wForm;
+  int32_t iWidth;
+};
+
+bool IsCtrlCode(wchar_t wch) {
+  FX_CHARTYPE dwRet = pdfium::unicode::GetCharType(wch);
+  return dwRet == FX_CHARTYPE::kTab || dwRet == FX_CHARTYPE::kControl;
+}
+
+}  // namespace
+
+CFGAS_TxtBreak::CFGAS_TxtBreak() : CFGAS_Break(LayoutStyle::kNone) {}
+
+CFGAS_TxtBreak::~CFGAS_TxtBreak() = default;
+
+void CFGAS_TxtBreak::SetLineWidth(float fLineWidth) {
+  m_iLineWidth = FXSYS_roundf(fLineWidth * kConversionFactor);
+  DCHECK(m_iLineWidth >= 20000);
+}
+
+void CFGAS_TxtBreak::SetAlignment(int32_t iAlignment) {
+  DCHECK(iAlignment >= CFX_TxtLineAlignment_Left);
+  DCHECK(iAlignment <= CFX_TxtLineAlignment_Justified);
+  m_iAlignment = iAlignment;
+}
+
+void CFGAS_TxtBreak::SetCombWidth(float fCombWidth) {
+  m_iCombWidth = FXSYS_roundf(fCombWidth * kConversionFactor);
+}
+
+void CFGAS_TxtBreak::AppendChar_Combination(CFGAS_Char* pCurChar) {
+  FX_SAFE_INT32 iCharWidth = m_iCombWidth;
+  pCurChar->m_iCharWidth = -1;
+  if (!m_bCombText) {
+    wchar_t wch = pCurChar->char_code();
+    CFGAS_Char* pLastChar = GetLastChar(0, false, false);
+    if (pLastChar &&
+        (pLastChar->m_dwCharStyles & FX_TXTCHARSTYLE_ArabicShadda) == 0) {
+      wchar_t wLast = pLastChar->char_code();
+      absl::optional<uint16_t> maybe_shadda;
+      if (wch == pdfium::arabic::kArabicShadda) {
+        maybe_shadda = pdfium::arabic::GetArabicFromShaddaTable(wLast);
+      } else if (wLast == pdfium::arabic::kArabicShadda) {
+        maybe_shadda = pdfium::arabic::GetArabicFromShaddaTable(wch);
+      }
+      if (maybe_shadda.has_value()) {
+        wch = maybe_shadda.value();
+        pCurChar->m_dwCharStyles |= FX_TXTCHARSTYLE_ArabicShadda;
+        pLastChar->m_dwCharStyles |= FX_TXTCHARSTYLE_ArabicShadda;
+        pLastChar->m_iCharWidth = 0;
+      }
+    }
+    absl::optional<uint16_t> iCharWidthRet;
+    if (m_pFont) {
+      iCharWidthRet = m_pFont->GetCharWidth(wch);
+    }
+    iCharWidth = iCharWidthRet.value_or(0);
+    iCharWidth *= m_iFontSize;
+    iCharWidth *= m_iHorizontalScale;
+    iCharWidth /= 100;
+  }
+  iCharWidth *= -1;
+  pCurChar->m_iCharWidth = iCharWidth.ValueOrDefault(0);
+}
+
+void CFGAS_TxtBreak::AppendChar_Tab(CFGAS_Char* pCurChar) {
+  m_eCharType = FX_CHARTYPE::kTab;
+}
+
+CFGAS_Char::BreakType CFGAS_TxtBreak::AppendChar_Control(CFGAS_Char* pCurChar) {
+  m_eCharType = FX_CHARTYPE::kControl;
+  CFGAS_Char::BreakType dwRet = CFGAS_Char::BreakType::kNone;
+  if (!m_bSingleLine) {
+    wchar_t wch = pCurChar->char_code();
+    switch (wch) {
+      case L'\v':
+      case pdfium::unicode::kLineSeparator:
+        dwRet = CFGAS_Char::BreakType::kLine;
+        break;
+      case L'\f':
+        dwRet = CFGAS_Char::BreakType::kPage;
+        break;
+      case pdfium::unicode::kParagraphSeparator:
+        dwRet = CFGAS_Char::BreakType::kParagraph;
+        break;
+      default:
+        if (wch == m_wParagraphBreakChar)
+          dwRet = CFGAS_Char::BreakType::kParagraph;
+        break;
+    }
+    if (dwRet != CFGAS_Char::BreakType::kNone)
+      dwRet = EndBreak(dwRet);
+  }
+  return dwRet;
+}
+
+CFGAS_Char::BreakType CFGAS_TxtBreak::AppendChar_Arabic(CFGAS_Char* pCurChar) {
+  FX_CHARTYPE chartype = pCurChar->GetCharType();
+  int32_t& iLineWidth = m_pCurLine->m_iWidth;
+  wchar_t wForm;
+  CFGAS_Char* pLastChar = nullptr;
+  bool bAlef = false;
+  if (!m_bCombText && m_eCharType >= FX_CHARTYPE::kArabicAlef &&
+      m_eCharType <= FX_CHARTYPE::kArabicDistortion) {
+    FX_SAFE_INT32 iCharWidth = 0;
+    pLastChar = GetLastChar(1, true, false);
+    if (pLastChar) {
+      if (pLastChar->m_iCharWidth > 0)
+        iLineWidth -= pLastChar->m_iCharWidth;
+      iCharWidth = pLastChar->m_iCharWidth;
+
+      CFGAS_Char* pPrevChar = GetLastChar(2, true, false);
+      wForm = pdfium::arabic::GetFormChar(pLastChar, pPrevChar, pCurChar);
+      bAlef = (wForm == pdfium::unicode::kZeroWidthNoBreakSpace &&
+               pLastChar->GetCharType() == FX_CHARTYPE::kArabicAlef);
+      if (m_pFont) {
+        iCharWidth = m_pFont->GetCharWidth(wForm).value_or(0);
+      }
+      if (wForm == pdfium::unicode::kZeroWidthNoBreakSpace)
+        iCharWidth = 0;
+
+      iCharWidth *= m_iFontSize;
+      iCharWidth *= m_iHorizontalScale;
+      iCharWidth /= 100;
+
+      int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
+      pLastChar->m_iCharWidth = iCharWidthValid;
+      iLineWidth += iCharWidthValid;
+    }
+  }
+
+  m_eCharType = chartype;
+  wForm = pdfium::arabic::GetFormChar(pCurChar, bAlef ? nullptr : pLastChar,
+                                      nullptr);
+  FX_SAFE_INT32 iCharWidth = 0;
+  if (m_bCombText) {
+    iCharWidth = m_iCombWidth;
+  } else {
+    if (m_pFont && wForm != pdfium::unicode::kZeroWidthNoBreakSpace) {
+      iCharWidth = m_pFont->GetCharWidth(wForm).value_or(0);
+    }
+    iCharWidth *= m_iFontSize;
+    iCharWidth *= m_iHorizontalScale;
+    iCharWidth /= 100;
+  }
+
+  int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
+  pCurChar->m_iCharWidth = iCharWidthValid;
+  iLineWidth += iCharWidthValid;
+
+  m_pCurLine->IncrementArabicCharCount();
+  if (!m_bSingleLine && IsGreaterThanLineWidth(iLineWidth))
+    return EndBreak(CFGAS_Char::BreakType::kLine);
+  return CFGAS_Char::BreakType::kNone;
+}
+
+CFGAS_Char::BreakType CFGAS_TxtBreak::AppendChar_Others(CFGAS_Char* pCurChar) {
+  FX_CHARTYPE chartype = pCurChar->GetCharType();
+  int32_t& iLineWidth = m_pCurLine->m_iWidth;
+  m_eCharType = chartype;
+  wchar_t wch = pCurChar->char_code();
+  wchar_t wForm = wch;
+
+  FX_SAFE_INT32 iCharWidth = 0;
+  if (m_bCombText) {
+    iCharWidth = m_iCombWidth;
+  } else if (m_pFont) {
+    iCharWidth = m_pFont->GetCharWidth(wForm).value_or(0);
+    iCharWidth *= m_iFontSize;
+    iCharWidth *= m_iHorizontalScale;
+    iCharWidth /= 100;
+  }
+  iCharWidth += m_iCharSpace;
+
+  int32_t iValidCharWidth = iCharWidth.ValueOrDefault(0);
+  pCurChar->m_iCharWidth = iValidCharWidth;
+  iLineWidth += iValidCharWidth;
+  if (!m_bSingleLine && chartype != FX_CHARTYPE::kSpace &&
+      IsGreaterThanLineWidth(iLineWidth)) {
+    return EndBreak(CFGAS_Char::BreakType::kLine);
+  }
+
+  return CFGAS_Char::BreakType::kNone;
+}
+
+CFGAS_Char::BreakType CFGAS_TxtBreak::AppendChar(wchar_t wch) {
+  FX_CHARTYPE chartype = pdfium::unicode::GetCharType(wch);
+  m_pCurLine->m_LineChars.emplace_back(wch, m_iHorizontalScale,
+                                       m_iVerticalScale);
+  CFGAS_Char* pCurChar = &m_pCurLine->m_LineChars.back();
+  pCurChar->m_dwCharStyles = m_iAlignment | (1 << 8);
+
+  CFGAS_Char::BreakType dwRet1 = CFGAS_Char::BreakType::kNone;
+  if (chartype != FX_CHARTYPE::kCombination &&
+      GetUnifiedCharType(m_eCharType) != GetUnifiedCharType(chartype) &&
+      m_eCharType != FX_CHARTYPE::kUnknown && !m_bSingleLine &&
+      IsGreaterThanLineWidth(m_pCurLine->m_iWidth) &&
+      (m_eCharType != FX_CHARTYPE::kSpace ||
+       chartype != FX_CHARTYPE::kControl)) {
+    dwRet1 = EndBreak(CFGAS_Char::BreakType::kLine);
+    if (!m_pCurLine->m_LineChars.empty())
+      pCurChar = &m_pCurLine->m_LineChars.back();
+  }
+
+  CFGAS_Char::BreakType dwRet2 = CFGAS_Char::BreakType::kNone;
+  if (wch == m_wParagraphBreakChar) {
+    // This is handled in AppendChar_Control, but it seems like \n and \r
+    // don't get matched as control characters so we go into AppendChar_other
+    // and never detect the new paragraph ...
+    dwRet2 = CFGAS_Char::BreakType::kParagraph;
+    EndBreak(dwRet2);
+  } else {
+    switch (chartype) {
+      case FX_CHARTYPE::kTab:
+        AppendChar_Tab(pCurChar);
+        break;
+      case FX_CHARTYPE::kControl:
+        dwRet2 = AppendChar_Control(pCurChar);
+        break;
+      case FX_CHARTYPE::kCombination:
+        AppendChar_Combination(pCurChar);
+        break;
+      case FX_CHARTYPE::kArabicAlef:
+      case FX_CHARTYPE::kArabicSpecial:
+      case FX_CHARTYPE::kArabicDistortion:
+      case FX_CHARTYPE::kArabicNormal:
+      case FX_CHARTYPE::kArabicForm:
+      case FX_CHARTYPE::kArabic:
+        dwRet2 = AppendChar_Arabic(pCurChar);
+        break;
+      case FX_CHARTYPE::kUnknown:
+      case FX_CHARTYPE::kSpace:
+      case FX_CHARTYPE::kNumeric:
+      case FX_CHARTYPE::kNormal:
+        dwRet2 = AppendChar_Others(pCurChar);
+        break;
+    }
+  }
+  return std::max(dwRet1, dwRet2);
+}
+
+void CFGAS_TxtBreak::EndBreakSplitLine(CFGAS_BreakLine* pNextLine,
+                                       bool bAllChars) {
+  bool bDone = false;
+  CFGAS_Char* pTC;
+  if (!m_bSingleLine && IsGreaterThanLineWidth(m_pCurLine->m_iWidth)) {
+    pTC = m_pCurLine->LastChar();
+    switch (pTC->GetCharType()) {
+      case FX_CHARTYPE::kTab:
+      case FX_CHARTYPE::kControl:
+      case FX_CHARTYPE::kSpace:
+        break;
+      default:
+        SplitTextLine(m_pCurLine, pNextLine, bAllChars);
+        bDone = true;
+        break;
+    }
+  }
+  if (bAllChars && !bDone) {
+    int32_t iEndPos = m_pCurLine->m_iWidth;
+    GetBreakPos(&m_pCurLine->m_LineChars, bAllChars, true, &iEndPos);
+  }
+}
+
+std::deque<CFGAS_Break::TPO> CFGAS_TxtBreak::EndBreakBidiLine(
+    CFGAS_Char::BreakType dwStatus) {
+  CFGAS_BreakPiece tp;
+  std::deque<TPO> tpos;
+  CFGAS_Char* pTC;
+  std::vector<CFGAS_Char>& chars = m_pCurLine->m_LineChars;
+  if (!m_pCurLine->HasArabicChar()) {
+    tp.SetStatus(dwStatus);
+    tp.SetStartPos(m_pCurLine->m_iStart);
+    tp.SetWidth(m_pCurLine->m_iWidth);
+    tp.SetStartChar(0);
+    tp.SetCharCount(fxcrt::CollectionSize<int32_t>(m_pCurLine->m_LineChars));
+    tp.SetChars(&m_pCurLine->m_LineChars);
+    pTC = &chars[0];
+    tp.SetCharStyles(pTC->m_dwCharStyles);
+    tp.SetHorizontalScale(pTC->horizonal_scale());
+    tp.SetVerticalScale(pTC->vertical_scale());
+    m_pCurLine->m_LinePieces.push_back(tp);
+    tpos.push_back({0, 0});
+    return tpos;
+  }
+
+  size_t iBidiNum = 0;
+  for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
+    pTC = &chars[i];
+    pTC->m_iBidiPos = static_cast<int32_t>(i);
+    if (pTC->GetCharType() != FX_CHARTYPE::kControl)
+      iBidiNum = i;
+    if (i == 0)
+      pTC->m_iBidiLevel = 1;
+  }
+  CFGAS_Char::BidiLine(&chars, iBidiNum + 1);
+
+  tp.SetStatus(CFGAS_Char::BreakType::kPiece);
+  tp.SetStartPos(m_pCurLine->m_iStart);
+  tp.SetChars(&m_pCurLine->m_LineChars);
+  int32_t iBidiLevel = -1;
+  int32_t iCharWidth;
+  int32_t i = 0;
+  int32_t j = -1;
+  int32_t iCount = fxcrt::CollectionSize<int32_t>(m_pCurLine->m_LineChars);
+  while (i < iCount) {
+    pTC = &chars[i];
+    if (iBidiLevel < 0) {
+      iBidiLevel = pTC->m_iBidiLevel;
+      tp.SetWidth(0);
+      tp.SetBidiLevel(iBidiLevel);
+      tp.SetBidiPos(pTC->m_iBidiOrder);
+      tp.SetCharStyles(pTC->m_dwCharStyles);
+      tp.SetHorizontalScale(pTC->horizonal_scale());
+      tp.SetVerticalScale(pTC->vertical_scale());
+      tp.SetStatus(CFGAS_Char::BreakType::kPiece);
+    }
+    if (iBidiLevel != pTC->m_iBidiLevel ||
+        pTC->m_dwStatus != CFGAS_Char::BreakType::kNone) {
+      if (iBidiLevel == pTC->m_iBidiLevel) {
+        tp.SetStatus(pTC->m_dwStatus);
+        iCharWidth = pTC->m_iCharWidth;
+        if (iCharWidth > 0)
+          tp.IncrementWidth(iCharWidth);
+
+        i++;
+      }
+      tp.SetCharCount(i - tp.GetStartChar());
+      m_pCurLine->m_LinePieces.push_back(tp);
+      tp.IncrementStartPos(tp.GetWidth());
+      tp.SetStartChar(i);
+      tpos.push_back({++j, tp.GetBidiPos()});
+      iBidiLevel = -1;
+    } else {
+      iCharWidth = pTC->m_iCharWidth;
+      if (iCharWidth > 0)
+        tp.IncrementWidth(iCharWidth);
+
+      i++;
+    }
+  }
+  if (i > tp.GetStartChar()) {
+    tp.SetStatus(dwStatus);
+    tp.SetCharCount(i - tp.GetStartChar());
+    m_pCurLine->m_LinePieces.push_back(tp);
+    tpos.push_back({++j, tp.GetBidiPos()});
+  }
+  if (j > -1) {
+    if (j > 0) {
+      std::sort(tpos.begin(), tpos.end());
+      int32_t iStartPos = 0;
+      for (i = 0; i <= j; i++) {
+        CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[tpos[i].index];
+        ttp.SetStartPos(iStartPos);
+        iStartPos += ttp.GetWidth();
+      }
+    }
+    m_pCurLine->m_LinePieces[j].SetStatus(dwStatus);
+  }
+  return tpos;
+}
+
+void CFGAS_TxtBreak::EndBreakAlignment(const std::deque<TPO>& tpos,
+                                       bool bAllChars,
+                                       CFGAS_Char::BreakType dwStatus) {
+  int32_t iNetWidth = m_pCurLine->m_iWidth;
+  int32_t iGapChars = 0;
+  bool bFind = false;
+  for (const TPO& pos : pdfium::base::Reversed(tpos)) {
+    const CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[pos.index];
+    if (!bFind)
+      iNetWidth = ttp.GetEndPos();
+
+    bool bArabic = FX_IsOdd(ttp.GetBidiLevel());
+    int32_t j = bArabic ? 0 : ttp.GetCharCount() - 1;
+    while (j > -1 && j < ttp.GetCharCount()) {
+      const CFGAS_Char* pTC = ttp.GetChar(j);
+      if (pTC->m_eLineBreakType == FX_LINEBREAKTYPE::kDIRECT_BRK)
+        iGapChars++;
+      if (!bFind || !bAllChars) {
+        FX_CHARTYPE chartype = pTC->GetCharType();
+        if (chartype == FX_CHARTYPE::kSpace ||
+            chartype == FX_CHARTYPE::kControl) {
+          if (!bFind && bAllChars && pTC->m_iCharWidth > 0)
+            iNetWidth -= pTC->m_iCharWidth;
+        } else {
+          bFind = true;
+          if (!bAllChars)
+            break;
+        }
+      }
+      j += bArabic ? 1 : -1;
+    }
+    if (!bAllChars && bFind)
+      break;
+  }
+
+  int32_t iOffset = m_iLineWidth - iNetWidth;
+  if (iGapChars > 0 && m_iAlignment & CFX_TxtLineAlignment_Justified &&
+      dwStatus != CFGAS_Char::BreakType::kParagraph) {
+    int32_t iStart = -1;
+    for (auto& tpo : tpos) {
+      CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[tpo.index];
+      if (iStart < -1)
+        iStart = ttp.GetStartPos();
+      else
+        ttp.SetStartPos(iStart);
+
+      for (int32_t j = 0; j < ttp.GetCharCount() && iGapChars > 0;
+           j++, iGapChars--) {
+        CFGAS_Char* pTC = ttp.GetChar(j);
+        if (pTC->m_eLineBreakType != FX_LINEBREAKTYPE::kDIRECT_BRK ||
+            pTC->m_iCharWidth < 0) {
+          continue;
+        }
+        int32_t k = iOffset / iGapChars;
+        pTC->m_iCharWidth += k;
+        ttp.IncrementWidth(k);
+        iOffset -= k;
+      }
+      iStart += ttp.GetWidth();
+    }
+  } else if (m_iAlignment & CFX_TxtLineAlignment_Center ||
+             m_iAlignment & CFX_TxtLineAlignment_Right) {
+    if (m_iAlignment & CFX_TxtLineAlignment_Center &&
+        !(m_iAlignment & CFX_TxtLineAlignment_Right)) {
+      iOffset /= 2;
+    }
+    if (iOffset > 0) {
+      for (auto& ttp : m_pCurLine->m_LinePieces)
+        ttp.IncrementStartPos(iOffset);
+    }
+  }
+}
+
+CFGAS_Char::BreakType CFGAS_TxtBreak::EndBreak(CFGAS_Char::BreakType dwStatus) {
+  DCHECK(dwStatus != CFGAS_Char::BreakType::kNone);
+
+  if (!m_pCurLine->m_LinePieces.empty()) {
+    if (dwStatus != CFGAS_Char::BreakType::kPiece)
+      m_pCurLine->m_LinePieces.back().SetStatus(dwStatus);
+    return m_pCurLine->m_LinePieces.back().GetStatus();
+  }
+
+  if (HasLine()) {
+    if (m_Lines[m_iReadyLineIndex].m_LinePieces.empty())
+      return CFGAS_Char::BreakType::kNone;
+
+    if (dwStatus != CFGAS_Char::BreakType::kPiece)
+      m_Lines[m_iReadyLineIndex].m_LinePieces.back().SetStatus(dwStatus);
+    return m_Lines[m_iReadyLineIndex].m_LinePieces.back().GetStatus();
+  }
+
+  if (m_pCurLine->m_LineChars.empty())
+    return CFGAS_Char::BreakType::kNone;
+
+  m_pCurLine->m_LineChars.back().m_dwStatus = dwStatus;
+  if (dwStatus == CFGAS_Char::BreakType::kPiece)
+    return dwStatus;
+
+  m_iReadyLineIndex = m_pCurLine == &m_Lines[0] ? 0 : 1;
+  CFGAS_BreakLine* pNextLine = &m_Lines[1 - m_iReadyLineIndex];
+  const bool bAllChars = m_iAlignment > CFX_TxtLineAlignment_Right;
+  EndBreakSplitLine(pNextLine, bAllChars);
+
+  std::deque<TPO> tpos = EndBreakBidiLine(dwStatus);
+  if (m_iAlignment > CFX_TxtLineAlignment_Left)
+    EndBreakAlignment(tpos, bAllChars, dwStatus);
+
+  m_pCurLine = pNextLine;
+  CFGAS_Char* pTC = GetLastChar(0, false, false);
+  m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE::kUnknown;
+  return dwStatus;
+}
+
+int32_t CFGAS_TxtBreak::GetBreakPos(std::vector<CFGAS_Char>* pChars,
+                                    bool bAllChars,
+                                    bool bOnlyBrk,
+                                    int32_t* pEndPos) {
+  std::vector<CFGAS_Char>& chars = *pChars;
+  int32_t iLength = fxcrt::CollectionSize<int32_t>(chars) - 1;
+  if (iLength < 1)
+    return iLength;
+
+  int32_t iBreak = -1;
+  int32_t iBreakPos = -1;
+  int32_t iIndirect = -1;
+  int32_t iIndirectPos = -1;
+  int32_t iLast = -1;
+  int32_t iLastPos = -1;
+  if (m_bSingleLine || *pEndPos <= m_iLineWidth) {
+    if (!bAllChars)
+      return iLength;
+
+    iBreak = iLength;
+    iBreakPos = *pEndPos;
+  }
+
+  FX_LINEBREAKTYPE eType;
+  FX_BREAKPROPERTY nCur;
+  FX_BREAKPROPERTY nNext;
+  CFGAS_Char* pCur = &chars[iLength--];
+  if (bAllChars)
+    pCur->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
+
+  nNext = pdfium::unicode::GetBreakProperty(pCur->char_code());
+  int32_t iCharWidth = pCur->m_iCharWidth;
+  if (iCharWidth > 0)
+    *pEndPos -= iCharWidth;
+
+  while (iLength >= 0) {
+    pCur = &chars[iLength];
+    nCur = pdfium::unicode::GetBreakProperty(pCur->char_code());
+    if (nNext == FX_BREAKPROPERTY::kSP)
+      eType = FX_LINEBREAKTYPE::kPROHIBITED_BRK;
+    else
+      eType = GetLineBreakTypeFromPair(nCur, nNext);
+    if (bAllChars)
+      pCur->m_eLineBreakType = eType;
+    if (!bOnlyBrk) {
+      if (m_bSingleLine || *pEndPos <= m_iLineWidth ||
+          nCur == FX_BREAKPROPERTY::kSP) {
+        if (eType == FX_LINEBREAKTYPE::kDIRECT_BRK && iBreak < 0) {
+          iBreak = iLength;
+          iBreakPos = *pEndPos;
+          if (!bAllChars)
+            return iLength;
+        } else if (eType == FX_LINEBREAKTYPE::kINDIRECT_BRK && iIndirect < 0) {
+          iIndirect = iLength;
+          iIndirectPos = *pEndPos;
+        }
+        if (iLast < 0) {
+          iLast = iLength;
+          iLastPos = *pEndPos;
+        }
+      }
+      iCharWidth = pCur->m_iCharWidth;
+      if (iCharWidth > 0)
+        *pEndPos -= iCharWidth;
+    }
+    nNext = nCur;
+    iLength--;
+  }
+  if (bOnlyBrk)
+    return 0;
+  if (iBreak > -1) {
+    *pEndPos = iBreakPos;
+    return iBreak;
+  }
+  if (iIndirect > -1) {
+    *pEndPos = iIndirectPos;
+    return iIndirect;
+  }
+  if (iLast > -1) {
+    *pEndPos = iLastPos;
+    return iLast;
+  }
+  return 0;
+}
+
+void CFGAS_TxtBreak::SplitTextLine(CFGAS_BreakLine* pCurLine,
+                                   CFGAS_BreakLine* pNextLine,
+                                   bool bAllChars) {
+  DCHECK(pCurLine);
+  DCHECK(pNextLine);
+
+  if (pCurLine->m_LineChars.size() < 2)
+    return;
+
+  int32_t iEndPos = pCurLine->m_iWidth;
+  std::vector<CFGAS_Char>& curChars = pCurLine->m_LineChars;
+  int32_t iCharPos = GetBreakPos(&curChars, bAllChars, false, &iEndPos);
+  if (iCharPos < 0)
+    iCharPos = 0;
+
+  iCharPos++;
+  if (iCharPos >= fxcrt::CollectionSize<int32_t>(pCurLine->m_LineChars)) {
+    pNextLine->Clear();
+    CFGAS_Char* pTC = &curChars[iCharPos - 1];
+    pTC->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
+    return;
+  }
+
+  pNextLine->m_LineChars =
+      std::vector<CFGAS_Char>(curChars.begin() + iCharPos, curChars.end());
+  curChars.erase(curChars.begin() + iCharPos, curChars.end());
+  pCurLine->m_iWidth = iEndPos;
+  CFGAS_Char* pTC = &curChars[iCharPos - 1];
+  pTC->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
+  int32_t iWidth = 0;
+  for (size_t i = 0; i < pNextLine->m_LineChars.size(); ++i) {
+    if (pNextLine->m_LineChars[i].GetCharType() >= FX_CHARTYPE::kArabicAlef) {
+      pCurLine->DecrementArabicCharCount();
+      pNextLine->IncrementArabicCharCount();
+    }
+    iWidth += std::max(0, pNextLine->m_LineChars[i].m_iCharWidth);
+    pNextLine->m_LineChars[i].m_dwStatus = CFGAS_Char::BreakType::kNone;
+  }
+  pNextLine->m_iWidth = iWidth;
+}
+
+size_t CFGAS_TxtBreak::GetDisplayPos(const Run& run,
+                                     TextCharPos* pCharPos) const {
+  if (run.iLength < 1)
+    return 0;
+
+  Engine* pEngine = run.pEdtEngine;
+  const wchar_t* pStr = run.wsStr.c_str();
+  int32_t* pWidths = run.pWidths;
+  int32_t iLength = run.iLength - 1;
+  RetainPtr<CFGAS_GEFont> pFont = run.pFont;
+  Mask<LayoutStyle> dwStyles = run.dwStyles;
+  CFX_RectF rtText(*run.pRect);
+  const bool bRTLPiece = (run.dwCharStyles & FX_TXTCHARSTYLE_OddBidiLevel) != 0;
+  const float fFontSize = run.fFontSize;
+  const int32_t iFontSize = FXSYS_roundf(fFontSize * 20.0f);
+  const int32_t iAscent = pFont->GetAscent();
+  const int32_t iDescent = pFont->GetDescent();
+  const int32_t iMaxHeight = iAscent - iDescent;
+  const float fAscent = iMaxHeight ? fFontSize * iAscent / iMaxHeight : 0;
+  int32_t iHorScale = run.iHorizontalScale;
+  int32_t iVerScale = run.iVerticalScale;
+  bool bSkipSpace = run.bSkipSpace;
+
+  const float fYBase = rtText.top + (rtText.height - fFontSize) / 2.0f;
+  float fX = bRTLPiece ? rtText.right() : rtText.left;
+  float fY = fYBase + fAscent;
+
+  size_t szCount = 0;
+  int32_t iNext = 0;
+  wchar_t wPrev = pdfium::unicode::kZeroWidthNoBreakSpace;
+  wchar_t wNext = pdfium::unicode::kZeroWidthNoBreakSpace;
+  wchar_t wForm = pdfium::unicode::kZeroWidthNoBreakSpace;
+  wchar_t wLast = pdfium::unicode::kZeroWidthNoBreakSpace;
+  bool bShadda = false;
+  bool bLam = false;
+  for (int32_t i = 0; i <= iLength; i++) {
+    int32_t iAbsolute = i + run.iStart;
+    int32_t iWidth;
+    wchar_t wch;
+    if (pEngine) {
+      wch = pEngine->GetChar(iAbsolute);
+      iWidth = pEngine->GetWidthOfChar(iAbsolute);
+    } else {
+      wch = *pStr++;
+      iWidth = *pWidths++;
+    }
+
+    FX_CHARTYPE chartype = pdfium::unicode::GetCharType(wch);
+    if (chartype == FX_CHARTYPE::kArabicAlef && iWidth == 0) {
+      wPrev = pdfium::unicode::kZeroWidthNoBreakSpace;
+      wLast = wch;
+      continue;
+    }
+
+    if (chartype >= FX_CHARTYPE::kArabicAlef) {
+      if (i < iLength) {
+        if (pEngine) {
+          iNext = i + 1;
+          while (iNext <= iLength) {
+            int32_t iNextAbsolute = iNext + run.iStart;
+            wNext = pEngine->GetChar(iNextAbsolute);
+            if (pdfium::unicode::GetCharType(wNext) !=
+                FX_CHARTYPE::kCombination) {
+              break;
+            }
+            iNext++;
+          }
+          if (iNext > iLength)
+            wNext = pdfium::unicode::kZeroWidthNoBreakSpace;
+        } else {
+          int32_t j = -1;
+          do {
+            j++;
+            if (i + j >= iLength)
+              break;
+
+            wNext = pStr[j];
+          } while (pdfium::unicode::GetCharType(wNext) ==
+                   FX_CHARTYPE::kCombination);
+          if (i + j >= iLength)
+            wNext = pdfium::unicode::kZeroWidthNoBreakSpace;
+        }
+      } else {
+        wNext = pdfium::unicode::kZeroWidthNoBreakSpace;
+      }
+
+      wForm = pdfium::arabic::GetFormChar(wch, wPrev, wNext);
+      bLam = (wPrev == pdfium::arabic::kArabicLetterLam &&
+              wch == pdfium::arabic::kArabicLetterLam &&
+              wNext == pdfium::arabic::kArabicLetterHeh);
+    } else if (chartype == FX_CHARTYPE::kCombination) {
+      wForm = wch;
+      if (wch >= 0x064C && wch <= 0x0651) {
+        if (bShadda) {
+          wForm = pdfium::unicode::kZeroWidthNoBreakSpace;
+          bShadda = false;
+        } else {
+          wNext = pdfium::unicode::kZeroWidthNoBreakSpace;
+          if (pEngine) {
+            iNext = i + 1;
+            if (iNext <= iLength) {
+              int32_t iNextAbsolute = iNext + run.iStart;
+              wNext = pEngine->GetChar(iNextAbsolute);
+            }
+          } else if (i < iLength) {
+            wNext = *pStr;
+          }
+          absl::optional<uint16_t> maybe_shadda;
+          if (wch == pdfium::arabic::kArabicShadda) {
+            maybe_shadda = pdfium::arabic::GetArabicFromShaddaTable(wNext);
+          } else if (wNext == pdfium::arabic::kArabicShadda) {
+            maybe_shadda = pdfium::arabic::GetArabicFromShaddaTable(wch);
+          }
+          if (maybe_shadda.has_value()) {
+            wForm = maybe_shadda.value();
+            bShadda = true;
+          }
+        }
+      } else {
+        bShadda = false;
+      }
+    } else if (chartype == FX_CHARTYPE::kNumeric) {
+      wForm = wch;
+    } else if (wch == L'.') {
+      wForm = wch;
+    } else if (wch == L',') {
+      wForm = wch;
+    } else if (bRTLPiece) {
+      wForm = pdfium::unicode::GetMirrorChar(wch);
+    } else {
+      wForm = wch;
+    }
+    if (chartype != FX_CHARTYPE::kCombination)
+      bShadda = false;
+    if (chartype < FX_CHARTYPE::kArabicAlef)
+      bLam = false;
+
+    bool bEmptyChar =
+        (chartype >= FX_CHARTYPE::kTab && chartype <= FX_CHARTYPE::kControl);
+    if (wForm == pdfium::unicode::kZeroWidthNoBreakSpace)
+      bEmptyChar = true;
+
+    int32_t iForms = bLam ? 3 : 1;
+    szCount += (bEmptyChar && bSkipSpace) ? 0 : iForms;
+    if (!pCharPos) {
+      if (iWidth > 0)
+        wPrev = wch;
+      wLast = wch;
+      continue;
+    }
+
+    int32_t iCharWidth = iWidth;
+    if (iCharWidth < 0)
+      iCharWidth = -iCharWidth;
+
+    iCharWidth /= iFontSize;
+    FX_FORMCHAR formChars[3];
+    formChars[0].wch = wch;
+    formChars[0].wForm = wForm;
+    formChars[0].iWidth = iCharWidth;
+    if (bLam) {
+      formChars[1].wForm = pdfium::arabic::kArabicShadda;
+      formChars[1].iWidth =
+          pFont->GetCharWidth(pdfium::arabic::kArabicShadda).value_or(0);
+      formChars[2].wForm = pdfium::arabic::kArabicLetterSuperscriptAlef;
+      formChars[2].iWidth =
+          pFont->GetCharWidth(pdfium::arabic::kArabicLetterSuperscriptAlef)
+              .value_or(0);
+    }
+
+    for (int32_t j = 0; j < iForms; j++) {
+      wForm = (wchar_t)formChars[j].wForm;
+      iCharWidth = formChars[j].iWidth;
+      if (j > 0) {
+        chartype = FX_CHARTYPE::kCombination;
+        wch = wForm;
+        wLast = (wchar_t)formChars[j - 1].wForm;
+      }
+      if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
+        pCharPos->m_GlyphIndex = pFont->GetGlyphIndex(wForm);
+#if BUILDFLAG(IS_APPLE)
+        pCharPos->m_ExtGID = pCharPos->m_GlyphIndex;
+#endif
+        pCharPos->m_FontCharWidth = iCharWidth;
+      }
+
+      const float fCharWidth = fFontSize * iCharWidth / 1000.0f;
+      if (bRTLPiece && chartype != FX_CHARTYPE::kCombination)
+        fX -= fCharWidth;
+
+      if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
+        pCharPos->m_Origin = CFX_PointF(fX, fY);
+
+        if (!!(dwStyles & LayoutStyle::kCombText)) {
+          int32_t iFormWidth = pFont->GetCharWidth(wForm).value_or(iCharWidth);
+          float fOffset = fFontSize * (iCharWidth - iFormWidth) / 2000.0f;
+          pCharPos->m_Origin.x += fOffset;
+        }
+        if (chartype == FX_CHARTYPE::kCombination) {
+          absl::optional<FX_RECT> rtBBox = pFont->GetCharBBox(wForm);
+          if (rtBBox.has_value()) {
+            pCharPos->m_Origin.y =
+                fYBase + fFontSize -
+                fFontSize * rtBBox.value().Height() / iMaxHeight;
+          }
+          if (wForm == wch &&
+              wLast != pdfium::unicode::kZeroWidthNoBreakSpace) {
+            if (pdfium::unicode::GetCharType(wLast) ==
+                FX_CHARTYPE::kCombination) {
+              absl::optional<FX_RECT> rtOtherBox = pFont->GetCharBBox(wLast);
+              if (rtOtherBox.has_value()) {
+                pCharPos->m_Origin.y -=
+                    fFontSize * rtOtherBox.value().Height() / iMaxHeight;
+              }
+            }
+          }
+        }
+      }
+      if (!bRTLPiece && chartype != FX_CHARTYPE::kCombination)
+        fX += fCharWidth;
+
+      if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
+        pCharPos->m_bGlyphAdjust = true;
+        pCharPos->m_AdjustMatrix[0] = -1;
+        pCharPos->m_AdjustMatrix[1] = 0;
+        pCharPos->m_AdjustMatrix[2] = 0;
+        pCharPos->m_AdjustMatrix[3] = 1;
+
+        if (iHorScale != 100 || iVerScale != 100) {
+          pCharPos->m_AdjustMatrix[0] =
+              pCharPos->m_AdjustMatrix[0] * iHorScale / 100.0f;
+          pCharPos->m_AdjustMatrix[1] =
+              pCharPos->m_AdjustMatrix[1] * iHorScale / 100.0f;
+          pCharPos->m_AdjustMatrix[2] =
+              pCharPos->m_AdjustMatrix[2] * iVerScale / 100.0f;
+          pCharPos->m_AdjustMatrix[3] =
+              pCharPos->m_AdjustMatrix[3] * iVerScale / 100.0f;
+        }
+        pCharPos++;
+      }
+    }
+    if (iWidth > 0)
+      wPrev = static_cast<wchar_t>(formChars[0].wch);
+    wLast = wch;
+  }
+  return szCount;
+}
+
+std::vector<CFX_RectF> CFGAS_TxtBreak::GetCharRects(const Run& run) const {
+  if (run.iLength < 1)
+    return std::vector<CFX_RectF>();
+
+  Engine* pEngine = run.pEdtEngine;
+  const wchar_t* pStr = run.wsStr.c_str();
+  int32_t* pWidths = run.pWidths;
+  int32_t iLength = run.iLength;
+  CFX_RectF rect(*run.pRect);
+  float fFontSize = run.fFontSize;
+  bool bRTLPiece = !!(run.dwCharStyles & FX_TXTCHARSTYLE_OddBidiLevel);
+  bool bSingleLine = !!(run.dwStyles & LayoutStyle::kSingleLine);
+  float fStart = bRTLPiece ? rect.right() : rect.left;
+
+  std::vector<CFX_RectF> rtArray(iLength);
+  for (int32_t i = 0; i < iLength; i++) {
+    wchar_t wch;
+    int32_t iCharSize;
+    if (pEngine) {
+      int32_t iAbsolute = i + run.iStart;
+      wch = pEngine->GetChar(iAbsolute);
+      iCharSize = pEngine->GetWidthOfChar(iAbsolute);
+    } else {
+      wch = *pStr++;
+      iCharSize = *pWidths++;
+    }
+    float fCharSize = static_cast<float>(iCharSize) / kConversionFactor;
+    bool bRet = (!bSingleLine && IsCtrlCode(wch));
+    if (!(wch == L'\v' || wch == L'\f' ||
+          wch == pdfium::unicode::kLineSeparator ||
+          wch == pdfium::unicode::kParagraphSeparator || wch == L'\n')) {
+      bRet = false;
+    }
+    if (bRet)
+      fCharSize = fFontSize / 2.0f;
+    rect.left = fStart;
+    if (bRTLPiece) {
+      rect.left -= fCharSize;
+      fStart -= fCharSize;
+    } else {
+      fStart += fCharSize;
+    }
+    rect.width = fCharSize;
+    rtArray[i] = rect;
+  }
+  return rtArray;
+}
+
+CFGAS_TxtBreak::Engine::~Engine() = default;
+
+CFGAS_TxtBreak::Run::Run() = default;
+
+CFGAS_TxtBreak::Run::~Run() = default;
+
+CFGAS_TxtBreak::Run::Run(const CFGAS_TxtBreak::Run& other) = default;
diff --git a/xfa/fgas/layout/cfgas_txtbreak.h b/xfa/fgas/layout/cfgas_txtbreak.h
new file mode 100644
index 0000000..c7b9931
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_txtbreak.h
@@ -0,0 +1,107 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_LAYOUT_CFGAS_TXTBREAK_H_
+#define XFA_FGAS_LAYOUT_CFGAS_TXTBREAK_H_
+
+#include <deque>
+#include <vector>
+
+#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"
+
+class CFGAS_GEFont;
+class TextCharPos;
+
+#define FX_TXTCHARSTYLE_ArabicShadda 0x0020
+#define FX_TXTCHARSTYLE_OddBidiLevel 0x0040
+
+enum CFX_TxtLineAlignment {
+  CFX_TxtLineAlignment_Left = 0,
+  CFX_TxtLineAlignment_Center = 1 << 0,
+  CFX_TxtLineAlignment_Right = 1 << 1,
+  CFX_TxtLineAlignment_Justified = 1 << 2
+};
+
+inline bool CFX_BreakTypeNoneOrPiece(CFGAS_Char::BreakType type) {
+  return type == CFGAS_Char::BreakType::kNone ||
+         type == CFGAS_Char::BreakType::kPiece;
+}
+
+class CFGAS_TxtBreak final : public CFGAS_Break {
+ public:
+  class Engine {
+   public:
+    virtual ~Engine();
+    virtual wchar_t GetChar(size_t idx) const = 0;
+    // May return negative for combining characters. Non-const so we can force
+    // a layout if needed.
+    virtual int32_t GetWidthOfChar(size_t idx) = 0;
+  };
+
+  struct Run {
+    Run();
+    Run(const Run& other);
+    ~Run();
+
+    UnownedPtr<CFGAS_TxtBreak::Engine> pEdtEngine;
+    WideString wsStr;
+    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;
+    RetainPtr<CFGAS_GEFont> pFont;
+    float fFontSize = 12.0f;
+    Mask<LayoutStyle> dwStyles = LayoutStyle::kNone;
+    int32_t iHorizontalScale = 100;
+    int32_t iVerticalScale = 100;
+    uint32_t dwCharStyles = 0;
+    UnownedPtr<const CFX_RectF> pRect;
+    bool bSkipSpace = true;
+  };
+
+  CFGAS_TxtBreak();
+  ~CFGAS_TxtBreak() override;
+
+  void SetLineWidth(float fLineWidth);
+  void SetAlignment(int32_t iAlignment);
+  void SetCombWidth(float fCombWidth);
+  CFGAS_Char::BreakType EndBreak(CFGAS_Char::BreakType dwStatus);
+
+  size_t GetDisplayPos(const Run& run, TextCharPos* pCharPos) const;
+  std::vector<CFX_RectF> GetCharRects(const Run& run) const;
+  CFGAS_Char::BreakType AppendChar(wchar_t wch);
+
+ private:
+  void AppendChar_Combination(CFGAS_Char* pCurChar);
+  void AppendChar_Tab(CFGAS_Char* pCurChar);
+  CFGAS_Char::BreakType AppendChar_Control(CFGAS_Char* pCurChar);
+  CFGAS_Char::BreakType AppendChar_Arabic(CFGAS_Char* pCurChar);
+  CFGAS_Char::BreakType AppendChar_Others(CFGAS_Char* pCurChar);
+
+  void ResetContextCharStyles();
+  void EndBreakSplitLine(CFGAS_BreakLine* pNextLine, bool bAllChars);
+  std::deque<TPO> EndBreakBidiLine(CFGAS_Char::BreakType dwStatus);
+  void EndBreakAlignment(const std::deque<TPO>& tpos,
+                         bool bAllChars,
+                         CFGAS_Char::BreakType dwStatus);
+  int32_t GetBreakPos(std::vector<CFGAS_Char>* pChars,
+                      bool bAllChars,
+                      bool bOnlyBrk,
+                      int32_t* pEndPos);
+  void SplitTextLine(CFGAS_BreakLine* pCurLine,
+                     CFGAS_BreakLine* pNextLine,
+                     bool bAllChars);
+
+  int32_t m_iAlignment = CFX_TxtLineAlignment_Left;
+  int32_t m_iCombWidth = 360000;
+};
+
+#endif  // XFA_FGAS_LAYOUT_CFGAS_TXTBREAK_H_
diff --git a/xfa/fgas/layout/cfgas_txtbreak_unittest.cpp b/xfa/fgas/layout/cfgas_txtbreak_unittest.cpp
new file mode 100644
index 0000000..ff472ac
--- /dev/null
+++ b/xfa/fgas/layout/cfgas_txtbreak_unittest.cpp
@@ -0,0 +1,48 @@
+// Copyright 2018 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fgas/layout/cfgas_txtbreak.h"
+
+#include <memory>
+#include <utility>
+
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxge/cfx_font.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "xfa/fgas/font/cfgas_fontmgr.h"
+#include "xfa/fgas/font/cfgas_gefont.h"
+#include "xfa/fgas/layout/cfgas_char.h"
+
+class CFGAS_TxtBreakTest : public testing::Test {
+ public:
+  void SetUp() override {
+    const wchar_t kFontFamily[] = L"Arimo Bold";
+    font_ = CFGAS_GEFont::LoadFont(kFontFamily, 0, FX_CodePage::kDefANSI);
+    ASSERT_TRUE(font_);
+  }
+
+  std::unique_ptr<CFGAS_TxtBreak> CreateBreak() {
+    auto txt_break = std::make_unique<CFGAS_TxtBreak>();
+    txt_break->SetFont(font_);
+    return txt_break;
+  }
+
+ private:
+  RetainPtr<CFGAS_GEFont> font_;
+};
+
+TEST_F(CFGAS_TxtBreakTest, BidiLine) {
+  auto txt_break = CreateBreak();
+  txt_break->SetLineBreakTolerance(1);
+  txt_break->SetFontSize(12);
+
+  WideString input = WideString::FromUTF8(ByteStringView("\xa\x0\xa\xa", 4));
+  for (wchar_t ch : input)
+    txt_break->AppendChar(ch);
+
+  std::vector<CFGAS_Char> chars =
+      txt_break->GetCurrentLineForTesting()->m_LineChars;
+  CFGAS_Char::BidiLine(&chars, chars.size());
+  EXPECT_EQ(3u, chars.size());
+}
diff --git a/xfa/fgas/layout/cfx_break.cpp b/xfa/fgas/layout/cfx_break.cpp
deleted file mode 100644
index 6330a51..0000000
--- a/xfa/fgas/layout/cfx_break.cpp
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fgas/layout/cfx_break.h"
-
-#include <algorithm>
-#include <vector>
-
-#include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/stl_util.h"
-#include "xfa/fgas/font/cfgas_gefont.h"
-
-const float CFX_Break::kConversionFactor = 20000.0f;
-const int CFX_Break::kMinimumTabWidth = 160000;
-
-CFX_Break::CFX_Break(uint32_t dwLayoutStyles)
-    : m_dwLayoutStyles(dwLayoutStyles), m_pCurLine(&m_Lines[0]) {}
-
-CFX_Break::~CFX_Break() = default;
-
-void CFX_Break::Reset() {
-  m_eCharType = FX_CHARTYPE::kUnknown;
-  for (CFX_BreakLine& line : m_Lines)
-    line.Clear();
-}
-
-void CFX_Break::SetLayoutStyles(uint32_t dwLayoutStyles) {
-  m_dwLayoutStyles = dwLayoutStyles;
-  m_bSingleLine = (m_dwLayoutStyles & FX_LAYOUTSTYLE_SingleLine) != 0;
-  m_bCombText = (m_dwLayoutStyles & FX_LAYOUTSTYLE_CombText) != 0;
-}
-
-void CFX_Break::SetHorizontalScale(int32_t iScale) {
-  iScale = std::max(iScale, 0);
-  if (m_iHorizontalScale == iScale)
-    return;
-
-  SetBreakStatus();
-  m_iHorizontalScale = iScale;
-}
-
-void CFX_Break::SetVerticalScale(int32_t iScale) {
-  if (iScale < 0)
-    iScale = 0;
-  if (m_iVerticalScale == iScale)
-    return;
-
-  SetBreakStatus();
-  m_iVerticalScale = iScale;
-}
-
-void CFX_Break::SetFont(const RetainPtr<CFGAS_GEFont>& pFont) {
-  if (!pFont || pFont == m_pFont)
-    return;
-
-  SetBreakStatus();
-  m_pFont = pFont;
-}
-
-void CFX_Break::SetFontSize(float fFontSize) {
-  int32_t iFontSize = FXSYS_roundf(fFontSize * 20.0f);
-  if (m_iFontSize == iFontSize)
-    return;
-
-  SetBreakStatus();
-  m_iFontSize = iFontSize;
-}
-
-void CFX_Break::SetBreakStatus() {
-  ++m_dwIdentity;
-  if (m_pCurLine->m_LineChars.empty())
-    return;
-
-  CFX_Char* tc = m_pCurLine->GetChar(m_pCurLine->m_LineChars.size() - 1);
-  if (tc->m_dwStatus == CFX_BreakType::None)
-    tc->m_dwStatus = CFX_BreakType::Piece;
-}
-
-bool CFX_Break::IsGreaterThanLineWidth(int32_t width) const {
-  FX_SAFE_INT32 line_width = m_iLineWidth;
-  line_width += m_iTolerance;
-  return line_width.IsValid() && width > line_width.ValueOrDie();
-}
-
-FX_CHARTYPE CFX_Break::GetUnifiedCharType(FX_CHARTYPE chartype) const {
-  return chartype >= FX_CHARTYPE::kArabicAlef ? FX_CHARTYPE::kArabic : chartype;
-}
-
-void CFX_Break::SetTabWidth(float fTabWidth) {
-  // Note, the use of max here was only done in the TxtBreak code. Leaving this
-  // in for the RTFBreak code for consistency. If we see issues with tab widths
-  // we may need to fix this.
-  m_iTabWidth =
-      std::max(FXSYS_roundf(fTabWidth * kConversionFactor), kMinimumTabWidth);
-}
-
-void CFX_Break::SetParagraphBreakChar(wchar_t wch) {
-  if (wch != L'\r' && wch != L'\n')
-    return;
-  m_wParagraphBreakChar = wch;
-}
-
-void CFX_Break::SetLineBreakTolerance(float fTolerance) {
-  m_iTolerance = FXSYS_roundf(fTolerance * kConversionFactor);
-}
-
-void CFX_Break::SetCharSpace(float fCharSpace) {
-  m_iCharSpace = FXSYS_roundf(fCharSpace * kConversionFactor);
-}
-
-void CFX_Break::SetLineBoundary(float fLineStart, float fLineEnd) {
-  if (fLineStart > fLineEnd)
-    return;
-
-  m_iLineStart = FXSYS_roundf(fLineStart * kConversionFactor);
-  m_iLineWidth = FXSYS_roundf(fLineEnd * kConversionFactor);
-  m_pCurLine->m_iStart = std::min(m_pCurLine->m_iStart, m_iLineWidth);
-  m_pCurLine->m_iStart = std::max(m_pCurLine->m_iStart, m_iLineStart);
-}
-
-CFX_Char* CFX_Break::GetLastChar(int32_t index,
-                                 bool bOmitChar,
-                                 bool bRichText) const {
-  std::vector<CFX_Char>& tca = m_pCurLine->m_LineChars;
-  if (!pdfium::IndexInBounds(tca, index))
-    return nullptr;
-
-  int32_t iStart = pdfium::CollectionSize<int32_t>(tca) - 1;
-  while (iStart > -1) {
-    CFX_Char* pTC = &tca[iStart--];
-    if (((bRichText && pTC->m_iCharWidth < 0) || bOmitChar) &&
-        pTC->GetCharType() == FX_CHARTYPE::kCombination) {
-      continue;
-    }
-    if (--index < 0)
-      return pTC;
-  }
-  return nullptr;
-}
-
-int32_t CFX_Break::CountBreakPieces() const {
-  return HasLine() ? pdfium::CollectionSize<int32_t>(
-                         m_Lines[m_iReadyLineIndex].m_LinePieces)
-                   : 0;
-}
-
-const CFX_BreakPiece* CFX_Break::GetBreakPieceUnstable(int32_t index) const {
-  if (!HasLine())
-    return nullptr;
-  if (!pdfium::IndexInBounds(m_Lines[m_iReadyLineIndex].m_LinePieces, index))
-    return nullptr;
-  return &m_Lines[m_iReadyLineIndex].m_LinePieces[index];
-}
-
-void CFX_Break::ClearBreakPieces() {
-  if (HasLine())
-    m_Lines[m_iReadyLineIndex].Clear();
-  m_iReadyLineIndex = -1;
-}
diff --git a/xfa/fgas/layout/cfx_break.h b/xfa/fgas/layout/cfx_break.h
deleted file mode 100644
index 4f0dcb3..0000000
--- a/xfa/fgas/layout/cfx_break.h
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FGAS_LAYOUT_CFX_BREAK_H_
-#define XFA_FGAS_LAYOUT_CFX_BREAK_H_
-
-#include <stdint.h>
-
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "xfa/fgas/layout/cfx_breakline.h"
-
-class CFGAS_GEFont;
-
-struct FX_TPO {
-  bool operator<(const FX_TPO& that) const { return pos < that.pos; }
-
-  int32_t index;
-  int32_t pos;
-};
-
-enum FX_LAYOUTSTYLE {
-  FX_LAYOUTSTYLE_None = 0,
-  FX_LAYOUTSTYLE_Pagination = 0x01,
-  FX_LAYOUTSTYLE_ExpandTab = 0x10,
-  FX_LAYOUTSTYLE_SingleLine = 0x200,
-  FX_LAYOUTSTYLE_CombText = 0x400
-};
-
-class CFX_Break {
- public:
-  virtual ~CFX_Break();
-
-  void Reset();
-
-  void SetLayoutStyles(uint32_t dwLayoutStyles);
-  uint32_t GetLayoutStyles() const { return m_dwLayoutStyles; }
-
-  void SetFont(const RetainPtr<CFGAS_GEFont>& pFont);
-  void SetFontSize(float fFontSize);
-  void SetTabWidth(float fTabWidth);
-  int32_t GetTabWidth() const { return m_iTabWidth; }
-
-  void SetHorizontalScale(int32_t iScale);
-  void SetVerticalScale(int32_t iScale);
-  void SetLineBreakTolerance(float fTolerance);
-  void SetLineBoundary(float fLineStart, float fLineEnd);
-
-  void SetCharSpace(float fCharSpace);
-  void SetParagraphBreakChar(wchar_t wch);
-
-  int32_t CountBreakPieces() const;
-  const CFX_BreakPiece* GetBreakPieceUnstable(int32_t index) const;
-  void ClearBreakPieces();
-
-  CFX_Char* GetLastChar(int32_t index, bool bOmitChar, bool bRichText) const;
-  const CFX_BreakLine* GetCurrentLineForTesting() const {
-    return m_pCurLine.Get();
-  }
-
- protected:
-  static const int kMinimumTabWidth;
-  static const float kConversionFactor;
-
-  explicit CFX_Break(uint32_t dwLayoutStyles);
-
-  void SetBreakStatus();
-  bool HasLine() const { return m_iReadyLineIndex >= 0; }
-  bool IsGreaterThanLineWidth(int32_t width) const;
-  FX_CHARTYPE GetUnifiedCharType(FX_CHARTYPE dwType) const;
-
-  FX_CHARTYPE m_eCharType = FX_CHARTYPE::kUnknown;
-  bool m_bSingleLine = false;
-  bool m_bCombText = false;
-  uint32_t m_dwIdentity = 0;
-  uint32_t m_dwLayoutStyles = 0;
-  int32_t m_iLineStart = 0;
-  int32_t m_iLineWidth = 2000000;
-  wchar_t m_wParagraphBreakChar = L'\n';
-  int32_t m_iFontSize = 240;
-  int32_t m_iTabWidth = 720000;
-  int32_t m_iHorizontalScale = 100;
-  int32_t m_iVerticalScale = 100;
-  int32_t m_iTolerance = 0;
-  int32_t m_iCharSpace = 0;
-  RetainPtr<CFGAS_GEFont> m_pFont;
-  UnownedPtr<CFX_BreakLine> m_pCurLine;
-  int8_t m_iReadyLineIndex = -1;
-  CFX_BreakLine m_Lines[2];
-};
-
-#endif  // XFA_FGAS_LAYOUT_CFX_BREAK_H_
diff --git a/xfa/fgas/layout/cfx_breakline.cpp b/xfa/fgas/layout/cfx_breakline.cpp
deleted file mode 100644
index 02203c9..0000000
--- a/xfa/fgas/layout/cfx_breakline.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fgas/layout/cfx_breakline.h"
-
-#include "third_party/base/stl_util.h"
-
-CFX_BreakLine::CFX_BreakLine() = default;
-
-CFX_BreakLine::~CFX_BreakLine() = default;
-
-CFX_Char* CFX_BreakLine::GetChar(int32_t index) {
-  ASSERT(pdfium::IndexInBounds(m_LineChars, index));
-  return &m_LineChars[index];
-}
-
-int32_t CFX_BreakLine::GetLineEnd() const {
-  return m_iStart + m_iWidth;
-}
-
-void CFX_BreakLine::Clear() {
-  m_LineChars.clear();
-  m_LinePieces.clear();
-  m_iWidth = 0;
-  m_iArabicChars = 0;
-}
-
-void CFX_BreakLine::IncrementArabicCharCount() {
-  ++m_iArabicChars;
-}
-
-void CFX_BreakLine::DecrementArabicCharCount() {
-  ASSERT(m_iArabicChars > 0);
-  --m_iArabicChars;
-}
diff --git a/xfa/fgas/layout/cfx_breakline.h b/xfa/fgas/layout/cfx_breakline.h
deleted file mode 100644
index c432b35..0000000
--- a/xfa/fgas/layout/cfx_breakline.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FGAS_LAYOUT_CFX_BREAKLINE_H_
-#define XFA_FGAS_LAYOUT_CFX_BREAKLINE_H_
-
-#include <vector>
-
-#include "xfa/fgas/layout/cfx_breakpiece.h"
-#include "xfa/fgas/layout/cfx_char.h"
-
-class CFX_BreakLine {
- public:
-  CFX_BreakLine();
-  ~CFX_BreakLine();
-
-  CFX_Char* GetChar(int32_t index);
-  int32_t GetLineEnd() const;
-
-  void Clear();
-
-  void IncrementArabicCharCount();
-  void DecrementArabicCharCount();
-  bool HasArabicChar() const { return m_iArabicChars > 0; }
-
-  std::vector<CFX_Char> m_LineChars;
-  std::vector<CFX_BreakPiece> m_LinePieces;
-  int32_t m_iStart = 0;
-  int32_t m_iWidth = 0;
-
- private:
-  int32_t m_iArabicChars = 0;
-};
-
-#endif  // XFA_FGAS_LAYOUT_CFX_BREAKLINE_H_
diff --git a/xfa/fgas/layout/cfx_breakpiece.cpp b/xfa/fgas/layout/cfx_breakpiece.cpp
deleted file mode 100644
index b0cc754..0000000
--- a/xfa/fgas/layout/cfx_breakpiece.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fgas/layout/cfx_breakpiece.h"
-
-#include "xfa/fgas/layout/cfx_textuserdata.h"
-
-CFX_BreakPiece::CFX_BreakPiece()
-    : m_dwStatus(CFX_BreakType::Piece),
-      m_iStartPos(0),
-      m_iWidth(-1),
-      m_iStartChar(0),
-      m_iChars(0),
-      m_iBidiLevel(0),
-      m_iBidiPos(0),
-      m_iFontSize(0),
-      m_iHorizontalScale(100),
-      m_iVerticalScale(100),
-      m_dwIdentity(0),
-      m_dwCharStyles(0),
-      m_pChars(nullptr) {}
-
-CFX_BreakPiece::CFX_BreakPiece(const CFX_BreakPiece& other) = default;
-
-CFX_BreakPiece::~CFX_BreakPiece() = default;
-
-int32_t CFX_BreakPiece::GetEndPos() const {
-  return m_iWidth < 0 ? m_iStartPos : m_iStartPos + m_iWidth;
-}
-
-CFX_Char* CFX_BreakPiece::GetChar(int32_t index) const {
-  ASSERT(index >= 0);
-  ASSERT(index < m_iChars);
-  ASSERT(m_pChars);
-  return &(*m_pChars)[m_iStartChar + index];
-}
-
-WideString CFX_BreakPiece::GetString() const {
-  WideString ret;
-  ret.Reserve(m_iChars);
-  for (int32_t i = m_iStartChar; i < m_iStartChar + m_iChars; i++)
-    ret += static_cast<wchar_t>((*m_pChars)[i].char_code());
-  return ret;
-}
-
-std::vector<int32_t> CFX_BreakPiece::GetWidths() const {
-  std::vector<int32_t> ret;
-  ret.reserve(m_iChars);
-  for (int32_t i = m_iStartChar; i < m_iStartChar + m_iChars; i++)
-    ret.push_back((*m_pChars)[i].m_iCharWidth);
-  return ret;
-}
diff --git a/xfa/fgas/layout/cfx_breakpiece.h b/xfa/fgas/layout/cfx_breakpiece.h
deleted file mode 100644
index 8ea7032..0000000
--- a/xfa/fgas/layout/cfx_breakpiece.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FGAS_LAYOUT_CFX_BREAKPIECE_H_
-#define XFA_FGAS_LAYOUT_CFX_BREAKPIECE_H_
-
-#include <vector>
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "xfa/fgas/layout/cfx_char.h"
-
-class CFX_TextUserData;
-
-class CFX_BreakPiece {
- public:
-  CFX_BreakPiece();
-  CFX_BreakPiece(const CFX_BreakPiece& other);
-  ~CFX_BreakPiece();
-
-  int32_t GetEndPos() const;
-  int32_t GetLength() const { return m_iChars; }
-
-  CFX_Char* GetChar(int32_t index) const;
-  WideString GetString() const;
-  std::vector<int32_t> GetWidths() const;
-
-  CFX_BreakType m_dwStatus;
-  int32_t m_iStartPos;
-  int32_t m_iWidth;
-  int32_t m_iStartChar;
-  int32_t m_iChars;
-  int32_t m_iBidiLevel;
-  int32_t m_iBidiPos;
-  int32_t m_iFontSize;
-  int32_t m_iHorizontalScale;
-  int32_t m_iVerticalScale;
-  uint32_t m_dwIdentity;
-  uint32_t m_dwCharStyles;
-  UnownedPtr<std::vector<CFX_Char>> m_pChars;
-  RetainPtr<CFX_TextUserData> m_pUserData;
-};
-
-#endif  // XFA_FGAS_LAYOUT_CFX_BREAKPIECE_H_
diff --git a/xfa/fgas/layout/cfx_char.cpp b/xfa/fgas/layout/cfx_char.cpp
deleted file mode 100644
index 311aff4..0000000
--- a/xfa/fgas/layout/cfx_char.cpp
+++ /dev/null
@@ -1,565 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fgas/layout/cfx_char.h"
-
-#include <algorithm>
-
-#include "core/fxcrt/fx_extension.h"
-
-namespace {
-
-#ifndef NDEBUG
-constexpr int32_t kBidiMaxLevel = 61;
-#endif  // NDEBUG
-
-#undef PACK_NIBBLES
-#define PACK_NIBBLES(hi, lo) \
-  ((static_cast<uint32_t>(hi) << 4) + static_cast<uint32_t>(lo))
-
-enum FX_BIDIWEAKSTATE : uint8_t {
-  FX_BWSxa = 0,
-  FX_BWSxr,
-  FX_BWSxl,
-  FX_BWSao,
-  FX_BWSro,
-  FX_BWSlo,
-  FX_BWSrt,
-  FX_BWSlt,
-  FX_BWScn,
-  FX_BWSra,
-  FX_BWSre,
-  FX_BWSla,
-  FX_BWSle,
-  FX_BWSac,
-  FX_BWSrc,
-  FX_BWSrs,
-  FX_BWSlc,
-  FX_BWSls,
-  FX_BWSret,
-  FX_BWSlet
-};
-
-// NOTE: Range of FX_BIDICLASS prevents encoding all possible values in this
-// manner, but the ones used manage to fit. Except that I suspect that 0xF
-// was intended to be used as a sentinel, even though it also means kRLE.
-// TODO(tsepez): pick a better representation.
-enum FX_BIDIWEAKACTION : uint16_t {
-  FX_BWAIX = 0x100,
-  FX_BWAXX = 0x0F,
-  FX_BWAxxx = 0xFF,
-  FX_BWAxIx = 0x100 + FX_BWAxxx,
-  FX_BWAxxN = PACK_NIBBLES(0x0F, FX_BIDICLASS::kON),
-  FX_BWAxxE = PACK_NIBBLES(0x0F, FX_BIDICLASS::kEN),
-  FX_BWAxxA = PACK_NIBBLES(0x0F, FX_BIDICLASS::kAN),
-  FX_BWAxxR = PACK_NIBBLES(0x0F, FX_BIDICLASS::kR),
-  FX_BWAxxL = PACK_NIBBLES(0x0F, FX_BIDICLASS::kL),
-  FX_BWANxx = PACK_NIBBLES(FX_BIDICLASS::kON, 0x0F),
-  FX_BWAAxx = PACK_NIBBLES(FX_BIDICLASS::kAN, 0x0F),
-  FX_BWAExE = PACK_NIBBLES(FX_BIDICLASS::kEN, FX_BIDICLASS::kEN),
-  FX_BWANIx = 0x100 + PACK_NIBBLES(FX_BIDICLASS::kON, 0x0F),
-  FX_BWANxN = PACK_NIBBLES(FX_BIDICLASS::kON, FX_BIDICLASS::kON),
-  FX_BWANxR = PACK_NIBBLES(FX_BIDICLASS::kON, FX_BIDICLASS::kR),
-  FX_BWANxE = PACK_NIBBLES(FX_BIDICLASS::kON, FX_BIDICLASS::kEN),
-  FX_BWAAxA = PACK_NIBBLES(FX_BIDICLASS::kAN, FX_BIDICLASS::kAN),
-  FX_BWANxL = PACK_NIBBLES(FX_BIDICLASS::kON, FX_BIDICLASS::kL),
-  FX_BWALxL = PACK_NIBBLES(FX_BIDICLASS::kL, FX_BIDICLASS::kL),
-  FX_BWAxIL = 0x100 + PACK_NIBBLES(0x0F, FX_BIDICLASS::kL),
-  FX_BWAAxR = PACK_NIBBLES(FX_BIDICLASS::kAN, FX_BIDICLASS::kR),
-  FX_BWALxx = PACK_NIBBLES(FX_BIDICLASS::kL, 0x0F),
-};
-
-enum FX_BIDINEUTRALSTATE : uint8_t {
-  FX_BNSr = 0,
-  FX_BNSl,
-  FX_BNSrn,
-  FX_BNSln,
-  FX_BNSa,
-  FX_BNSna
-};
-
-enum FX_BIDINEUTRALACTION : uint16_t {
-  // For placeholders in table.
-  FX_BNAZero = 0,
-
-  // Other values.
-  FX_BNAnL = PACK_NIBBLES(0, FX_BIDICLASS::kL),
-  FX_BNAEn = PACK_NIBBLES(FX_BIDICLASS::kAN, 0),
-  FX_BNARn = PACK_NIBBLES(FX_BIDICLASS::kR, 0),
-  FX_BNALn = PACK_NIBBLES(FX_BIDICLASS::kL, 0),
-  FX_BNAIn = FX_BWAIX,
-  FX_BNALnL = PACK_NIBBLES(FX_BIDICLASS::kL, FX_BIDICLASS::kL),
-};
-#undef PACK_NIBBLES
-
-const FX_BIDICLASS kNTypes[] = {
-    FX_BIDICLASS::kN,   FX_BIDICLASS::kL,   FX_BIDICLASS::kR,
-    FX_BIDICLASS::kAN,  FX_BIDICLASS::kEN,  FX_BIDICLASS::kAL,
-    FX_BIDICLASS::kNSM, FX_BIDICLASS::kCS,  FX_BIDICLASS::kES,
-    FX_BIDICLASS::kET,  FX_BIDICLASS::kBN,  FX_BIDICLASS::kBN,
-    FX_BIDICLASS::kN,   FX_BIDICLASS::kB,   FX_BIDICLASS::kRLO,
-    FX_BIDICLASS::kRLE, FX_BIDICLASS::kLRO, FX_BIDICLASS::kLRE,
-    FX_BIDICLASS::kPDF, FX_BIDICLASS::kON,
-};
-
-const FX_BIDIWEAKSTATE kWeakStates[20][10] = {
-    {FX_BWSao, FX_BWSxl, FX_BWSxr, FX_BWScn, FX_BWScn, FX_BWSxa, FX_BWSxa,
-     FX_BWSao, FX_BWSao, FX_BWSao},
-    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSxr,
-     FX_BWSro, FX_BWSro, FX_BWSrt},
-    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSxl,
-     FX_BWSlo, FX_BWSlo, FX_BWSlt},
-    {FX_BWSao, FX_BWSxl, FX_BWSxr, FX_BWScn, FX_BWScn, FX_BWSxa, FX_BWSao,
-     FX_BWSao, FX_BWSao, FX_BWSao},
-    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSro,
-     FX_BWSro, FX_BWSro, FX_BWSrt},
-    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlo,
-     FX_BWSlo, FX_BWSlo, FX_BWSlt},
-    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSrt,
-     FX_BWSro, FX_BWSro, FX_BWSrt},
-    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlt,
-     FX_BWSlo, FX_BWSlo, FX_BWSlt},
-    {FX_BWSao, FX_BWSxl, FX_BWSxr, FX_BWScn, FX_BWScn, FX_BWSxa, FX_BWScn,
-     FX_BWSac, FX_BWSao, FX_BWSao},
-    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSra,
-     FX_BWSrc, FX_BWSro, FX_BWSrt},
-    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSre,
-     FX_BWSrs, FX_BWSrs, FX_BWSret},
-    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSla,
-     FX_BWSlc, FX_BWSlo, FX_BWSlt},
-    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSle,
-     FX_BWSls, FX_BWSls, FX_BWSlet},
-    {FX_BWSao, FX_BWSxl, FX_BWSxr, FX_BWScn, FX_BWScn, FX_BWSxa, FX_BWSao,
-     FX_BWSao, FX_BWSao, FX_BWSao},
-    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSro,
-     FX_BWSro, FX_BWSro, FX_BWSrt},
-    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSro,
-     FX_BWSro, FX_BWSro, FX_BWSrt},
-    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlo,
-     FX_BWSlo, FX_BWSlo, FX_BWSlt},
-    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlo,
-     FX_BWSlo, FX_BWSlo, FX_BWSlt},
-    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSret,
-     FX_BWSro, FX_BWSro, FX_BWSret},
-    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlet,
-     FX_BWSlo, FX_BWSlo, FX_BWSlet},
-};
-
-const FX_BIDIWEAKACTION kWeakActions[20][10] = {
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxA, FX_BWAxxR,
-     FX_BWAxxR, FX_BWAxxN, FX_BWAxxN, FX_BWAxxN},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
-     FX_BWAxxR, FX_BWAxxN, FX_BWAxxN, FX_BWAxIx},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
-     FX_BWAxxL, FX_BWAxxN, FX_BWAxxN, FX_BWAxIx},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxA, FX_BWAxxR,
-     FX_BWAxxN, FX_BWAxxN, FX_BWAxxN, FX_BWAxxN},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
-     FX_BWAxxN, FX_BWAxxN, FX_BWAxxN, FX_BWAxIx},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
-     FX_BWAxxN, FX_BWAxxN, FX_BWAxxN, FX_BWAxIx},
-    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAExE, FX_BWANxR,
-     FX_BWAxIx, FX_BWANxN, FX_BWANxN, FX_BWAxIx},
-    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWALxL, FX_BWANxR,
-     FX_BWAxIx, FX_BWANxN, FX_BWANxN, FX_BWAxIx},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxA, FX_BWAxxR,
-     FX_BWAxxA, FX_BWAxIx, FX_BWAxxN, FX_BWAxxN},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
-     FX_BWAxxA, FX_BWAxIx, FX_BWAxxN, FX_BWAxIx},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
-     FX_BWAxxE, FX_BWAxIx, FX_BWAxIx, FX_BWAxxE},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
-     FX_BWAxxA, FX_BWAxIx, FX_BWAxxN, FX_BWAxIx},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
-     FX_BWAxxL, FX_BWAxIx, FX_BWAxIx, FX_BWAxxL},
-    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAAxx, FX_BWAAxA, FX_BWANxR,
-     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANxN},
-    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAAxx, FX_BWANxE, FX_BWANxR,
-     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANIx},
-    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAExE, FX_BWANxR,
-     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANIx},
-    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAAxx, FX_BWANxL, FX_BWANxR,
-     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANIx},
-    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWALxL, FX_BWANxR,
-     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANIx},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
-     FX_BWAxxE, FX_BWAxxN, FX_BWAxxN, FX_BWAxxE},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
-     FX_BWAxxL, FX_BWAxxN, FX_BWAxxN, FX_BWAxxL},
-};
-
-const FX_BIDINEUTRALSTATE kNeutralStates[6][5] = {
-    {FX_BNSrn, FX_BNSl, FX_BNSr, FX_BNSr, FX_BNSr},
-    {FX_BNSln, FX_BNSl, FX_BNSr, FX_BNSa, FX_BNSl},
-    {FX_BNSrn, FX_BNSl, FX_BNSr, FX_BNSr, FX_BNSr},
-    {FX_BNSln, FX_BNSl, FX_BNSr, FX_BNSa, FX_BNSl},
-    {FX_BNSna, FX_BNSl, FX_BNSr, FX_BNSa, FX_BNSl},
-    {FX_BNSna, FX_BNSl, FX_BNSr, FX_BNSa, FX_BNSl},
-};
-
-const FX_BIDINEUTRALACTION kNeutralActions[6][5] = {
-    {FX_BNAIn, FX_BNAZero, FX_BNAZero, FX_BNAZero, FX_BNAZero},
-    {FX_BNAIn, FX_BNAZero, FX_BNAZero, FX_BNAZero, FX_BNAnL},
-    {FX_BNAIn, FX_BNAEn, FX_BNARn, FX_BNARn, FX_BNARn},
-    {FX_BNAIn, FX_BNALn, FX_BNAEn, FX_BNAEn, FX_BNALnL},
-    {FX_BNAIn, FX_BNAZero, FX_BNAZero, FX_BNAZero, FX_BNAnL},
-    {FX_BNAIn, FX_BNAEn, FX_BNARn, FX_BNARn, FX_BNAEn},
-};
-
-const uint8_t kAddLevel[2][4] = {
-    {0, 1, 2, 2},
-    {1, 0, 1, 1},
-};
-
-FX_BIDICLASS Direction(int32_t val) {
-  return FX_IsOdd(val) ? FX_BIDICLASS::kR : FX_BIDICLASS::kL;
-}
-
-FX_BIDICLASS GetDeferredType(int32_t val) {
-  return static_cast<FX_BIDICLASS>((val >> 4) & 0x0F);
-}
-
-FX_BIDICLASS GetResolvedType(int32_t val) {
-  return static_cast<FX_BIDICLASS>(val & 0x0F);
-}
-
-FX_BIDICLASS GetDeferredNeutrals(int32_t iAction, int32_t iLevel) {
-  FX_BIDICLASS eClass = GetDeferredType(iAction);
-  return eClass == FX_BIDICLASS::kAN ? Direction(iLevel) : eClass;
-}
-
-FX_BIDICLASS GetResolvedNeutrals(int32_t iAction) {
-  return GetResolvedType(iAction);
-}
-
-FX_BIDIWEAKSTATE GetWeakState(FX_BIDIWEAKSTATE eState, FX_BIDICLASS eClass) {
-  ASSERT(static_cast<size_t>(eState) < FX_ArraySize(kWeakStates));
-  ASSERT(static_cast<size_t>(eClass) < FX_ArraySize(kWeakStates[0]));
-  return kWeakStates[static_cast<size_t>(eState)][static_cast<size_t>(eClass)];
-}
-
-FX_BIDIWEAKACTION GetWeakAction(FX_BIDIWEAKSTATE eState, FX_BIDICLASS eClass) {
-  ASSERT(static_cast<size_t>(eState) < FX_ArraySize(kWeakActions));
-  ASSERT(static_cast<size_t>(eClass) < FX_ArraySize(kWeakActions[0]));
-  return kWeakActions[static_cast<size_t>(eState)][static_cast<size_t>(eClass)];
-}
-
-FX_BIDINEUTRALSTATE GetNeutralState(FX_BIDINEUTRALSTATE eState,
-                                    FX_BIDICLASS eClass) {
-  ASSERT(static_cast<size_t>(eState) < FX_ArraySize(kNeutralStates));
-  ASSERT(static_cast<size_t>(eClass) < FX_ArraySize(kNeutralStates[0]));
-  return kNeutralStates[static_cast<size_t>(eState)]
-                       [static_cast<size_t>(eClass)];
-}
-
-FX_BIDINEUTRALACTION GetNeutralAction(FX_BIDINEUTRALSTATE eState,
-                                      FX_BIDICLASS eClass) {
-  ASSERT(static_cast<size_t>(eState) < FX_ArraySize(kNeutralActions));
-  ASSERT(static_cast<size_t>(eClass) < FX_ArraySize(kNeutralActions[0]));
-  return kNeutralActions[static_cast<size_t>(eState)]
-                        [static_cast<size_t>(eClass)];
-}
-
-void ReverseString(std::vector<CFX_Char>* chars, size_t iStart, size_t iCount) {
-  ASSERT(pdfium::IndexInBounds(*chars, iStart));
-  ASSERT(iStart + iCount <= chars->size());
-
-  std::reverse(chars->begin() + iStart, chars->begin() + iStart + iCount);
-}
-
-void SetDeferredRunClass(std::vector<CFX_Char>* chars,
-                         size_t iStart,
-                         size_t iCount,
-                         FX_BIDICLASS eValue) {
-  ASSERT(iStart <= chars->size());
-  ASSERT(iStart >= iCount);
-
-  size_t iLast = iStart - iCount;
-  for (size_t i = iStart; i > iLast; --i)
-    (*chars)[i - 1].m_iBidiClass = eValue;
-}
-
-void SetDeferredRunLevel(std::vector<CFX_Char>* chars,
-                         size_t iStart,
-                         size_t iCount,
-                         int32_t iValue) {
-  ASSERT(iStart <= chars->size());
-  ASSERT(iStart >= iCount);
-
-  size_t iLast = iStart - iCount;
-  for (size_t i = iStart; i > iLast; --i)
-    (*chars)[i - 1].m_iBidiLevel = static_cast<int16_t>(iValue);
-}
-
-void Classify(std::vector<CFX_Char>* chars, size_t iCount) {
-  for (size_t i = 0; i < iCount; ++i) {
-    CFX_Char& cur = (*chars)[i];
-    cur.m_iBidiClass = FX_GetBidiClass(cur.char_code());
-  }
-}
-
-void ClassifyWithTransform(std::vector<CFX_Char>* chars, size_t iCount) {
-  for (size_t i = 0; i < iCount; ++i) {
-    CFX_Char& cur = (*chars)[i];
-    cur.m_iBidiClass =
-        kNTypes[static_cast<size_t>(FX_GetBidiClass(cur.char_code()))];
-  }
-}
-
-void ResolveExplicit(std::vector<CFX_Char>* chars, size_t iCount) {
-  for (size_t i = 0; i < iCount; ++i)
-    (*chars)[i].m_iBidiLevel = 0;
-}
-
-void ResolveWeak(std::vector<CFX_Char>* chars, size_t iCount) {
-  if (iCount <= 1)
-    return;
-  --iCount;
-
-  int32_t iLevelCur = 0;
-  size_t iNum = 0;
-  FX_BIDIWEAKSTATE eState = FX_BWSxl;
-  FX_BIDICLASS eClsCur;
-  FX_BIDICLASS eClsRun;
-  FX_BIDICLASS eClsNew;
-  size_t i = 0;
-  for (; i <= iCount; ++i) {
-    CFX_Char* pTC = &(*chars)[i];
-    eClsCur = pTC->m_iBidiClass;
-    if (eClsCur == FX_BIDICLASS::kBN) {
-      pTC->m_iBidiLevel = (int16_t)iLevelCur;
-      if (i == iCount && iLevelCur != 0) {
-        eClsCur = Direction(iLevelCur);
-        pTC->m_iBidiClass = eClsCur;
-      } else if (i < iCount) {
-        CFX_Char* pTCNext = &(*chars)[i + 1];
-        int32_t iLevelNext, iLevelNew;
-        eClsNew = pTCNext->m_iBidiClass;
-        iLevelNext = pTCNext->m_iBidiLevel;
-        if (eClsNew != FX_BIDICLASS::kBN && iLevelCur != iLevelNext) {
-          iLevelNew = std::max(iLevelNext, iLevelCur);
-          pTC->m_iBidiLevel = static_cast<int16_t>(iLevelNew);
-          eClsCur = Direction(iLevelNew);
-          pTC->m_iBidiClass = eClsCur;
-          iLevelCur = iLevelNext;
-        } else {
-          if (iNum > 0)
-            ++iNum;
-          continue;
-        }
-      } else {
-        if (iNum > 0)
-          ++iNum;
-        continue;
-      }
-    }
-    if (eClsCur > FX_BIDICLASS::kBN)
-      continue;
-
-    FX_BIDIWEAKACTION eAction = GetWeakAction(eState, eClsCur);
-    eClsRun = GetDeferredType(eAction);
-    if (eClsRun != static_cast<FX_BIDICLASS>(0xF) && iNum > 0) {
-      SetDeferredRunClass(chars, i, iNum, eClsRun);
-      iNum = 0;
-    }
-    eClsNew = GetResolvedType(eAction);
-    if (eClsNew != static_cast<FX_BIDICLASS>(0xF))
-      pTC->m_iBidiClass = eClsNew;
-    if (FX_BWAIX & eAction)
-      ++iNum;
-
-    eState = GetWeakState(eState, eClsCur);
-  }
-  if (iNum == 0)
-    return;
-
-  eClsCur = Direction(0);
-  eClsRun = GetDeferredType(GetWeakAction(eState, eClsCur));
-  if (eClsRun != static_cast<FX_BIDICLASS>(0xF))
-    SetDeferredRunClass(chars, i, iNum, eClsRun);
-}
-
-void ResolveNeutrals(std::vector<CFX_Char>* chars, size_t iCount) {
-  if (iCount <= 1)
-    return;
-  --iCount;
-
-  CFX_Char* pTC;
-  int32_t iLevel = 0;
-  size_t i = 0;
-  size_t iNum = 0;
-  FX_BIDINEUTRALSTATE eState = FX_BNSl;
-  FX_BIDICLASS eClsCur;
-  FX_BIDICLASS eClsRun;
-  FX_BIDICLASS eClsNew;
-  for (; i <= iCount; ++i) {
-    pTC = &(*chars)[i];
-    eClsCur = pTC->m_iBidiClass;
-    if (eClsCur == FX_BIDICLASS::kBN) {
-      if (iNum)
-        ++iNum;
-      continue;
-    }
-    if (eClsCur >= FX_BIDICLASS::kAL)
-      continue;
-
-    FX_BIDINEUTRALACTION eAction = GetNeutralAction(eState, eClsCur);
-    eClsRun = GetDeferredNeutrals(eAction, iLevel);
-    if (eClsRun != FX_BIDICLASS::kN && iNum > 0) {
-      SetDeferredRunClass(chars, i, iNum, eClsRun);
-      iNum = 0;
-    }
-
-    eClsNew = GetResolvedNeutrals(eAction);
-    if (eClsNew != FX_BIDICLASS::kN)
-      pTC->m_iBidiClass = eClsNew;
-    if (FX_BNAIn & eAction)
-      ++iNum;
-
-    eState = GetNeutralState(eState, eClsCur);
-    iLevel = pTC->m_iBidiLevel;
-  }
-  if (iNum == 0)
-    return;
-
-  eClsCur = Direction(iLevel);
-  eClsRun = GetDeferredNeutrals(GetNeutralAction(eState, eClsCur), iLevel);
-  if (eClsRun != FX_BIDICLASS::kN)
-    SetDeferredRunClass(chars, i, iNum, eClsRun);
-}
-
-void ResolveImplicit(std::vector<CFX_Char>* chars, size_t iCount) {
-  for (size_t i = 0; i < iCount; ++i) {
-    FX_BIDICLASS eCls = (*chars)[i].m_iBidiClass;
-    if (eCls == FX_BIDICLASS::kBN || eCls <= FX_BIDICLASS::kON ||
-        eCls >= FX_BIDICLASS::kAL) {
-      continue;
-    }
-    (*chars)[i].m_iBidiLevel += kAddLevel[FX_IsOdd((*chars)[i].m_iBidiLevel)]
-                                         [static_cast<size_t>(eCls) - 1];
-  }
-}
-
-void ResolveWhitespace(std::vector<CFX_Char>* chars, size_t iCount) {
-  if (iCount <= 1)
-    return;
-  iCount--;
-
-  int32_t iLevel = 0;
-  size_t i = 0;
-  size_t iNum = 0;
-  for (; i <= iCount; ++i) {
-    switch (static_cast<FX_BIDICLASS>((*chars)[i].m_iBidiClass)) {
-      case FX_BIDICLASS::kWS:
-        ++iNum;
-        break;
-      case FX_BIDICLASS::kRLE:
-      case FX_BIDICLASS::kLRE:
-      case FX_BIDICLASS::kLRO:
-      case FX_BIDICLASS::kRLO:
-      case FX_BIDICLASS::kPDF:
-      case FX_BIDICLASS::kBN:
-        (*chars)[i].m_iBidiLevel = static_cast<int16_t>(iLevel);
-        ++iNum;
-        break;
-      case FX_BIDICLASS::kS:
-      case FX_BIDICLASS::kB:
-        if (iNum > 0)
-          SetDeferredRunLevel(chars, i, iNum, 0);
-
-        (*chars)[i].m_iBidiLevel = 0;
-        iNum = 0;
-        break;
-      default:
-        iNum = 0;
-        break;
-    }
-    iLevel = (*chars)[i].m_iBidiLevel;
-  }
-  if (iNum > 0)
-    SetDeferredRunLevel(chars, i, iNum, 0);
-}
-
-size_t ReorderLevel(std::vector<CFX_Char>* chars,
-                    size_t iCount,
-                    int32_t iBaseLevel,
-                    size_t iStart,
-                    bool bReverse) {
-  ASSERT(iBaseLevel >= 0);
-  ASSERT(iBaseLevel <= kBidiMaxLevel);
-  ASSERT(iStart < iCount);
-
-  if (iCount < 1)
-    return 0;
-
-  bReverse = bReverse || FX_IsOdd(iBaseLevel);
-  size_t i = iStart;
-  for (; i < iCount; ++i) {
-    int32_t iLevel = (*chars)[i].m_iBidiLevel;
-    if (iLevel == iBaseLevel)
-      continue;
-    if (iLevel < iBaseLevel)
-      break;
-
-    i += ReorderLevel(chars, iCount, iBaseLevel + 1, i, bReverse) - 1;
-  }
-
-  size_t iNum = i - iStart;
-  if (bReverse && iNum > 1)
-    ReverseString(chars, iStart, iNum);
-
-  return iNum;
-}
-
-void Reorder(std::vector<CFX_Char>* chars, size_t iCount) {
-  for (size_t i = 0; i < iCount;)
-    i += ReorderLevel(chars, iCount, 0, i, false);
-}
-
-void Position(std::vector<CFX_Char>* chars, size_t iCount) {
-  for (size_t i = 0; i < iCount; ++i) {
-    if ((*chars)[i].m_iBidiPos > iCount)
-      continue;
-
-    (*chars)[(*chars)[i].m_iBidiPos].m_iBidiOrder = i;
-  }
-}
-
-}  // namespace
-
-// static
-void CFX_Char::BidiLine(std::vector<CFX_Char>* chars, size_t iCount) {
-  ASSERT(iCount <= chars->size());
-  if (iCount < 2)
-    return;
-
-  ClassifyWithTransform(chars, iCount);
-  ResolveExplicit(chars, iCount);
-  ResolveWeak(chars, iCount);
-  ResolveNeutrals(chars, iCount);
-  ResolveImplicit(chars, iCount);
-  Classify(chars, iCount);
-  ResolveWhitespace(chars, iCount);
-  Reorder(chars, iCount);
-  Position(chars, iCount);
-}
-
-CFX_Char::CFX_Char(uint16_t wCharCode) : CFX_Char(wCharCode, 100, 100) {}
-
-CFX_Char::CFX_Char(uint16_t wCharCode,
-                   int32_t iHorizontalScale,
-                   int32_t iVerticalScale)
-    : m_wCharCode(wCharCode),
-      m_iHorizontalScale(iHorizontalScale),
-      m_iVerticalScale(iVerticalScale) {}
-
-CFX_Char::CFX_Char(const CFX_Char& other) = default;
-
-CFX_Char::~CFX_Char() = default;
-
-FX_CHARTYPE CFX_Char::GetCharType() const {
-  return FX_GetCharType(m_wCharCode);
-}
diff --git a/xfa/fgas/layout/cfx_char.h b/xfa/fgas/layout/cfx_char.h
deleted file mode 100644
index c4bafcc..0000000
--- a/xfa/fgas/layout/cfx_char.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FGAS_LAYOUT_CFX_CHAR_H_
-#define XFA_FGAS_LAYOUT_CFX_CHAR_H_
-
-#include <stdint.h>
-
-#include <vector>
-
-#include "core/fxcrt/fx_unicode.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "xfa/fgas/layout/cfx_textuserdata.h"
-#include "xfa/fgas/layout/fx_linebreak.h"
-
-enum class CFX_BreakType : uint8_t { None = 0, Piece, Line, Paragraph, Page };
-
-class CFX_Char {
- public:
-  static void BidiLine(std::vector<CFX_Char>* chars, size_t iCount);
-
-  explicit CFX_Char(uint16_t wCharCode);
-  CFX_Char(uint16_t wCharCode,
-           int32_t iHorizontalScale,
-           int32_t iVerticalScale);
-  CFX_Char(const CFX_Char& other);
-  ~CFX_Char();
-
-  FX_CHARTYPE GetCharType() const;
-
-  uint16_t char_code() const { return m_wCharCode; }
-  int16_t horizonal_scale() const { return m_iHorizontalScale; }
-  int16_t vertical_scale() const { return m_iVerticalScale; }
-
-  CFX_BreakType m_dwStatus = CFX_BreakType::None;
-  FX_BIDICLASS m_iBidiClass = FX_BIDICLASS::kON;
-  FX_LINEBREAKTYPE m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
-  uint32_t m_dwCharStyles = 0;
-  int32_t m_iCharWidth = 0;
-  uint16_t m_iBidiLevel = 0;
-  uint16_t m_iBidiPos = 0;
-  uint16_t m_iBidiOrder = 0;
-  int32_t m_iFontSize = 0;
-  uint32_t m_dwIdentity = 0;
-  RetainPtr<CFX_TextUserData> m_pUserData;
-
- private:
-  uint16_t m_wCharCode;
-  int32_t m_iHorizontalScale;
-  int32_t m_iVerticalScale;
-};
-
-#endif  // XFA_FGAS_LAYOUT_CFX_CHAR_H_
diff --git a/xfa/fgas/layout/cfx_linkuserdata.cpp b/xfa/fgas/layout/cfx_linkuserdata.cpp
deleted file mode 100644
index d452480..0000000
--- a/xfa/fgas/layout/cfx_linkuserdata.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fgas/layout/cfx_linkuserdata.h"
-
-CFX_LinkUserData::CFX_LinkUserData(const WideString& wsText)
-    : m_wsURLContent(wsText) {}
-
-CFX_LinkUserData::~CFX_LinkUserData() {}
diff --git a/xfa/fgas/layout/cfx_linkuserdata.h b/xfa/fgas/layout/cfx_linkuserdata.h
deleted file mode 100644
index 83b43a2..0000000
--- a/xfa/fgas/layout/cfx_linkuserdata.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FGAS_LAYOUT_CFX_LINKUSERDATA_H_
-#define XFA_FGAS_LAYOUT_CFX_LINKUSERDATA_H_
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/retain_ptr.h"
-
-class CFX_LinkUserData final : public Retainable {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  const wchar_t* GetLinkURL() const { return m_wsURLContent.c_str(); }
-
- private:
-  explicit CFX_LinkUserData(const WideString& wsText);
-  ~CFX_LinkUserData() override;
-
-  WideString m_wsURLContent;
-};
-
-#endif  // XFA_FGAS_LAYOUT_CFX_LINKUSERDATA_H_
diff --git a/xfa/fgas/layout/cfx_rtfbreak.cpp b/xfa/fgas/layout/cfx_rtfbreak.cpp
deleted file mode 100644
index 78d723b..0000000
--- a/xfa/fgas/layout/cfx_rtfbreak.cpp
+++ /dev/null
@@ -1,823 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fgas/layout/cfx_rtfbreak.h"
-
-#include <algorithm>
-
-#include "build/build_config.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxge/text_char_pos.h"
-#include "third_party/base/numerics/safe_math.h"
-#include "third_party/base/stl_util.h"
-#include "xfa/fgas/font/cfgas_gefont.h"
-#include "xfa/fgas/layout/cfx_char.h"
-#include "xfa/fgas/layout/cfx_textpiece.h"
-#include "xfa/fgas/layout/cfx_textuserdata.h"
-#include "xfa/fgas/layout/fx_arabic.h"
-#include "xfa/fgas/layout/fx_linebreak.h"
-
-CFX_RTFBreak::CFX_RTFBreak(uint32_t dwLayoutStyles)
-    : CFX_Break(dwLayoutStyles),
-      m_bPagination(false),
-      m_iAlignment(CFX_RTFLineAlignment::Left) {
-  SetBreakStatus();
-  m_bPagination = !!(m_dwLayoutStyles & FX_LAYOUTSTYLE_Pagination);
-}
-
-CFX_RTFBreak::~CFX_RTFBreak() {}
-
-void CFX_RTFBreak::SetLineStartPos(float fLinePos) {
-  int32_t iLinePos = FXSYS_roundf(fLinePos * kConversionFactor);
-  iLinePos = std::min(iLinePos, m_iLineWidth);
-  iLinePos = std::max(iLinePos, m_iLineStart);
-  m_pCurLine->m_iStart = iLinePos;
-}
-
-void CFX_RTFBreak::AddPositionedTab(float fTabPos) {
-  int32_t iTabPos = std::min(
-      FXSYS_roundf(fTabPos * kConversionFactor) + m_iLineStart, m_iLineWidth);
-  auto it = std::lower_bound(m_PositionedTabs.begin(), m_PositionedTabs.end(),
-                             iTabPos);
-  if (it != m_PositionedTabs.end() && *it == iTabPos)
-    return;
-  m_PositionedTabs.insert(it, iTabPos);
-}
-
-void CFX_RTFBreak::SetUserData(const RetainPtr<CFX_TextUserData>& pUserData) {
-  if (m_pUserData == pUserData)
-    return;
-
-  SetBreakStatus();
-  m_pUserData = pUserData;
-}
-
-bool CFX_RTFBreak::GetPositionedTab(int32_t* iTabPos) const {
-  auto it = std::upper_bound(m_PositionedTabs.begin(), m_PositionedTabs.end(),
-                             *iTabPos);
-  if (it == m_PositionedTabs.end())
-    return false;
-
-  *iTabPos = *it;
-  return true;
-}
-
-CFX_BreakType CFX_RTFBreak::AppendChar(wchar_t wch) {
-  ASSERT(m_pCurLine);
-
-  FX_CHARTYPE chartype = FX_GetCharType(wch);
-  m_pCurLine->m_LineChars.emplace_back(wch, m_iHorizontalScale,
-                                       m_iVerticalScale);
-  CFX_Char* pCurChar = &m_pCurLine->m_LineChars.back();
-  pCurChar->m_iFontSize = m_iFontSize;
-  pCurChar->m_dwIdentity = m_dwIdentity;
-  pCurChar->m_pUserData = m_pUserData;
-
-  CFX_BreakType dwRet1 = CFX_BreakType::None;
-  if (chartype != FX_CHARTYPE::kCombination &&
-      GetUnifiedCharType(m_eCharType) != GetUnifiedCharType(chartype) &&
-      m_eCharType != FX_CHARTYPE::kUnknown &&
-      IsGreaterThanLineWidth(m_pCurLine->GetLineEnd()) &&
-      (m_eCharType != FX_CHARTYPE::kSpace ||
-       chartype != FX_CHARTYPE::kControl)) {
-    dwRet1 = EndBreak(CFX_BreakType::Line);
-    if (!m_pCurLine->m_LineChars.empty())
-      pCurChar = &m_pCurLine->m_LineChars.back();
-  }
-
-  CFX_BreakType dwRet2 = CFX_BreakType::None;
-  switch (chartype) {
-    case FX_CHARTYPE::kTab:
-      AppendChar_Tab(pCurChar);
-      break;
-    case FX_CHARTYPE::kControl:
-      dwRet2 = AppendChar_Control(pCurChar);
-      break;
-    case FX_CHARTYPE::kCombination:
-      AppendChar_Combination(pCurChar);
-      break;
-    case FX_CHARTYPE::kArabicAlef:
-    case FX_CHARTYPE::kArabicSpecial:
-    case FX_CHARTYPE::kArabicDistortion:
-    case FX_CHARTYPE::kArabicNormal:
-    case FX_CHARTYPE::kArabicForm:
-    case FX_CHARTYPE::kArabic:
-      dwRet2 = AppendChar_Arabic(pCurChar);
-      break;
-    case FX_CHARTYPE::kUnknown:
-    case FX_CHARTYPE::kSpace:
-    case FX_CHARTYPE::kNumeric:
-    case FX_CHARTYPE::kNormal:
-    default:
-      dwRet2 = AppendChar_Others(pCurChar);
-      break;
-  }
-
-  m_eCharType = chartype;
-  return std::max(dwRet1, dwRet2);
-}
-
-void CFX_RTFBreak::AppendChar_Combination(CFX_Char* pCurChar) {
-  FX_SAFE_INT32 iCharWidth = 0;
-  int32_t iCharWidthOut;
-  if (m_pFont && m_pFont->GetCharWidth(pCurChar->char_code(), &iCharWidthOut))
-    iCharWidth = iCharWidthOut;
-
-  iCharWidth *= m_iFontSize;
-  iCharWidth *= m_iHorizontalScale;
-  iCharWidth /= 100;
-  CFX_Char* pLastChar = GetLastChar(0, false, true);
-  if (pLastChar && pLastChar->GetCharType() > FX_CHARTYPE::kCombination)
-    iCharWidth *= -1;
-  else
-    m_eCharType = FX_CHARTYPE::kCombination;
-
-  int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
-  pCurChar->m_iCharWidth = iCharWidthValid;
-  if (iCharWidthValid > 0) {
-    FX_SAFE_INT32 checked_width = m_pCurLine->m_iWidth;
-    checked_width += iCharWidthValid;
-    if (!checked_width.IsValid())
-      return;
-
-    m_pCurLine->m_iWidth = checked_width.ValueOrDie();
-  }
-}
-
-void CFX_RTFBreak::AppendChar_Tab(CFX_Char* pCurChar) {
-  if (!(m_dwLayoutStyles & FX_LAYOUTSTYLE_ExpandTab))
-    return;
-
-  int32_t& iLineWidth = m_pCurLine->m_iWidth;
-  int32_t iCharWidth = iLineWidth;
-  FX_SAFE_INT32 iSafeCharWidth;
-  if (GetPositionedTab(&iCharWidth)) {
-    iSafeCharWidth = iCharWidth;
-  } else {
-    // Tab width is >= 160000, so this part does not need to be checked.
-    ASSERT(m_iTabWidth >= kMinimumTabWidth);
-    iSafeCharWidth = iLineWidth / m_iTabWidth + 1;
-    iSafeCharWidth *= m_iTabWidth;
-  }
-  iSafeCharWidth -= iLineWidth;
-
-  iCharWidth = iSafeCharWidth.ValueOrDefault(0);
-
-  pCurChar->m_iCharWidth = iCharWidth;
-  iLineWidth += iCharWidth;
-}
-
-CFX_BreakType CFX_RTFBreak::AppendChar_Control(CFX_Char* pCurChar) {
-  CFX_BreakType dwRet2 = CFX_BreakType::None;
-  switch (pCurChar->char_code()) {
-    case L'\v':
-    case 0x2028:
-      dwRet2 = CFX_BreakType::Line;
-      break;
-    case L'\f':
-      dwRet2 = CFX_BreakType::Page;
-      break;
-    case 0x2029:
-      dwRet2 = CFX_BreakType::Paragraph;
-      break;
-    default:
-      if (pCurChar->char_code() == m_wParagraphBreakChar)
-        dwRet2 = CFX_BreakType::Paragraph;
-      break;
-  }
-  if (dwRet2 != CFX_BreakType::None)
-    dwRet2 = EndBreak(dwRet2);
-
-  return dwRet2;
-}
-
-CFX_BreakType CFX_RTFBreak::AppendChar_Arabic(CFX_Char* pCurChar) {
-  m_pCurLine->IncrementArabicCharCount();
-
-  CFX_Char* pLastChar = nullptr;
-  wchar_t wForm;
-  bool bAlef = false;
-  if (m_eCharType >= FX_CHARTYPE::kArabicAlef &&
-      m_eCharType <= FX_CHARTYPE::kArabicDistortion) {
-    pLastChar = GetLastChar(1, false, true);
-    if (pLastChar) {
-      m_pCurLine->m_iWidth -= pLastChar->m_iCharWidth;
-      CFX_Char* pPrevChar = GetLastChar(2, false, true);
-      wForm = pdfium::arabic::GetFormChar(pLastChar, pPrevChar, pCurChar);
-      bAlef = (wForm == 0xFEFF &&
-               pLastChar->GetCharType() == FX_CHARTYPE::kArabicAlef);
-      FX_SAFE_INT32 iCharWidth;
-      int32_t iCharWidthOut;
-      if (m_pFont &&
-          (m_pFont->GetCharWidth(wForm, &iCharWidthOut) ||
-           m_pFont->GetCharWidth(pLastChar->char_code(), &iCharWidthOut))) {
-        iCharWidth = iCharWidthOut;
-      } else {
-        iCharWidth = 0;
-      }
-
-      iCharWidth *= m_iFontSize;
-      iCharWidth *= m_iHorizontalScale;
-      iCharWidth /= 100;
-
-      int iCharWidthValid = iCharWidth.ValueOrDefault(0);
-      pLastChar->m_iCharWidth = iCharWidthValid;
-
-      FX_SAFE_INT32 checked_width = m_pCurLine->m_iWidth;
-      checked_width += iCharWidthValid;
-      if (!checked_width.IsValid())
-        return CFX_BreakType::None;
-
-      m_pCurLine->m_iWidth = checked_width.ValueOrDie();
-      iCharWidth = 0;
-    }
-  }
-
-  wForm = pdfium::arabic::GetFormChar(pCurChar, bAlef ? nullptr : pLastChar,
-                                      nullptr);
-  FX_SAFE_INT32 iCharWidth;
-  int32_t iCharWidthOut;
-  if (m_pFont &&
-      (m_pFont->GetCharWidth(wForm, &iCharWidthOut) ||
-       m_pFont->GetCharWidth(pCurChar->char_code(), &iCharWidthOut))) {
-    iCharWidth = iCharWidthOut;
-  } else {
-    iCharWidth = 0;
-  }
-
-  iCharWidth *= m_iFontSize;
-  iCharWidth *= m_iHorizontalScale;
-  iCharWidth /= 100;
-
-  int iCharWidthValid = iCharWidth.ValueOrDefault(0);
-  pCurChar->m_iCharWidth = iCharWidthValid;
-
-  FX_SAFE_INT32 checked_width = m_pCurLine->m_iWidth;
-  checked_width += iCharWidthValid;
-  if (!checked_width.IsValid())
-    return CFX_BreakType::None;
-
-  m_pCurLine->m_iWidth = checked_width.ValueOrDie();
-
-  if (IsGreaterThanLineWidth(m_pCurLine->GetLineEnd()))
-    return EndBreak(CFX_BreakType::Line);
-  return CFX_BreakType::None;
-}
-
-CFX_BreakType CFX_RTFBreak::AppendChar_Others(CFX_Char* pCurChar) {
-  FX_CHARTYPE chartype = pCurChar->GetCharType();
-  wchar_t wForm = pCurChar->char_code();
-  FX_SAFE_INT32 iCharWidth;
-  int32_t iCharWidthOut;
-  if (m_pFont && m_pFont->GetCharWidth(wForm, &iCharWidthOut))
-    iCharWidth = iCharWidthOut;
-  else
-    iCharWidth = 0;
-
-  iCharWidth *= m_iFontSize;
-  iCharWidth *= m_iHorizontalScale;
-  iCharWidth /= 100;
-  iCharWidth += m_iCharSpace;
-
-  int iCharWidthValid = iCharWidth.ValueOrDefault(0);
-  pCurChar->m_iCharWidth = iCharWidthValid;
-
-  FX_SAFE_INT32 checked_width = m_pCurLine->m_iWidth;
-  checked_width += iCharWidthValid;
-  if (!checked_width.IsValid())
-    return CFX_BreakType::None;
-
-  m_pCurLine->m_iWidth = checked_width.ValueOrDie();
-  if (chartype != FX_CHARTYPE::kSpace &&
-      IsGreaterThanLineWidth(m_pCurLine->GetLineEnd())) {
-    return EndBreak(CFX_BreakType::Line);
-  }
-  return CFX_BreakType::None;
-}
-
-CFX_BreakType CFX_RTFBreak::EndBreak(CFX_BreakType dwStatus) {
-  ASSERT(dwStatus != CFX_BreakType::None);
-
-  ++m_dwIdentity;
-  if (!m_pCurLine->m_LinePieces.empty()) {
-    if (dwStatus != CFX_BreakType::Piece)
-      m_pCurLine->m_LinePieces.back().m_dwStatus = dwStatus;
-    return m_pCurLine->m_LinePieces.back().m_dwStatus;
-  }
-
-  if (HasLine()) {
-    if (m_Lines[m_iReadyLineIndex].m_LinePieces.empty())
-      return CFX_BreakType::None;
-
-    if (dwStatus != CFX_BreakType::Piece)
-      m_Lines[m_iReadyLineIndex].m_LinePieces.back().m_dwStatus = dwStatus;
-    return m_Lines[m_iReadyLineIndex].m_LinePieces.back().m_dwStatus;
-  }
-
-  if (m_pCurLine->m_LineChars.empty())
-    return CFX_BreakType::None;
-
-  CFX_Char* tc = m_pCurLine->GetChar(m_pCurLine->m_LineChars.size() - 1);
-  tc->m_dwStatus = dwStatus;
-  if (dwStatus == CFX_BreakType::Piece)
-    return dwStatus;
-
-  m_iReadyLineIndex = m_pCurLine == &m_Lines[0] ? 0 : 1;
-  CFX_BreakLine* pNextLine = &m_Lines[1 - m_iReadyLineIndex];
-  bool bAllChars = m_iAlignment == CFX_RTFLineAlignment::Justified ||
-                   m_iAlignment == CFX_RTFLineAlignment::Distributed;
-
-  if (!EndBreak_SplitLine(pNextLine, bAllChars, dwStatus)) {
-    std::deque<FX_TPO> tpos;
-    EndBreak_BidiLine(&tpos, dwStatus);
-    if (!m_bPagination && m_iAlignment != CFX_RTFLineAlignment::Left)
-      EndBreak_Alignment(tpos, bAllChars, dwStatus);
-  }
-  m_pCurLine = pNextLine;
-  m_pCurLine->m_iStart = m_iLineStart;
-
-  CFX_Char* pTC = GetLastChar(0, false, true);
-  m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE::kUnknown;
-  return dwStatus;
-}
-
-bool CFX_RTFBreak::EndBreak_SplitLine(CFX_BreakLine* pNextLine,
-                                      bool bAllChars,
-                                      CFX_BreakType dwStatus) {
-  bool bDone = false;
-  if (IsGreaterThanLineWidth(m_pCurLine->GetLineEnd())) {
-    const CFX_Char* tc =
-        m_pCurLine->GetChar(m_pCurLine->m_LineChars.size() - 1);
-    switch (tc->GetCharType()) {
-      case FX_CHARTYPE::kTab:
-      case FX_CHARTYPE::kControl:
-      case FX_CHARTYPE::kSpace:
-        break;
-      default:
-        SplitTextLine(m_pCurLine.Get(), pNextLine, !m_bPagination && bAllChars);
-        bDone = true;
-        break;
-    }
-  }
-
-  if (!m_bPagination) {
-    if (bAllChars && !bDone) {
-      int32_t endPos = m_pCurLine->GetLineEnd();
-      GetBreakPos(m_pCurLine->m_LineChars, bAllChars, true, &endPos);
-    }
-    return false;
-  }
-
-  const CFX_Char* pCurChars = m_pCurLine->m_LineChars.data();
-  CFX_BreakPiece tp;
-  tp.m_pChars = &m_pCurLine->m_LineChars;
-  bool bNew = true;
-  uint32_t dwIdentity = static_cast<uint32_t>(-1);
-  int32_t iLast = pdfium::CollectionSize<int32_t>(m_pCurLine->m_LineChars) - 1;
-  int32_t j = 0;
-  for (int32_t i = 0; i <= iLast;) {
-    const CFX_Char* pTC = pCurChars + i;
-    if (bNew) {
-      tp.m_iStartChar = i;
-      tp.m_iStartPos += tp.m_iWidth;
-      tp.m_iWidth = 0;
-      tp.m_dwStatus = pTC->m_dwStatus;
-      tp.m_iFontSize = pTC->m_iFontSize;
-      tp.m_iHorizontalScale = pTC->horizonal_scale();
-      tp.m_iVerticalScale = pTC->vertical_scale();
-      dwIdentity = pTC->m_dwIdentity;
-      tp.m_dwIdentity = dwIdentity;
-      tp.m_pUserData = pTC->m_pUserData;
-      j = i;
-      bNew = false;
-    }
-
-    if (i == iLast || pTC->m_dwStatus != CFX_BreakType::None ||
-        pTC->m_dwIdentity != dwIdentity) {
-      tp.m_iChars = i - j;
-      if (pTC->m_dwIdentity == dwIdentity) {
-        tp.m_dwStatus = pTC->m_dwStatus;
-        tp.m_iWidth += pTC->m_iCharWidth;
-        tp.m_iChars += 1;
-        ++i;
-      }
-      m_pCurLine->m_LinePieces.push_back(tp);
-      bNew = true;
-    } else {
-      tp.m_iWidth += pTC->m_iCharWidth;
-      ++i;
-    }
-  }
-  return true;
-}
-
-void CFX_RTFBreak::EndBreak_BidiLine(std::deque<FX_TPO>* tpos,
-                                     CFX_BreakType dwStatus) {
-  CFX_Char* pTC;
-  std::vector<CFX_Char>& chars = m_pCurLine->m_LineChars;
-  if (!m_bPagination && m_pCurLine->HasArabicChar()) {
-    size_t iBidiNum = 0;
-    for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
-      pTC = &chars[i];
-      pTC->m_iBidiPos = static_cast<int32_t>(i);
-      if (pTC->GetCharType() != FX_CHARTYPE::kControl)
-        iBidiNum = i;
-      if (i == 0)
-        pTC->m_iBidiLevel = 1;
-    }
-    CFX_Char::BidiLine(&chars, iBidiNum + 1);
-  } else {
-    for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
-      pTC = &chars[i];
-      pTC->m_iBidiLevel = 0;
-      pTC->m_iBidiPos = 0;
-      pTC->m_iBidiOrder = 0;
-    }
-  }
-
-  CFX_BreakPiece tp;
-  tp.m_dwStatus = CFX_BreakType::Piece;
-  tp.m_iStartPos = m_pCurLine->m_iStart;
-  tp.m_pChars = &chars;
-
-  int32_t iBidiLevel = -1;
-  int32_t iCharWidth;
-  FX_TPO tpo;
-  uint32_t dwIdentity = static_cast<uint32_t>(-1);
-  int32_t i = 0;
-  int32_t j = 0;
-  int32_t iCount = pdfium::CollectionSize<int32_t>(m_pCurLine->m_LineChars);
-  while (i < iCount) {
-    pTC = &chars[i];
-    if (iBidiLevel < 0) {
-      iBidiLevel = pTC->m_iBidiLevel;
-      iCharWidth = pTC->m_iCharWidth;
-      tp.m_iWidth = iCharWidth < 1 ? 0 : iCharWidth;
-      tp.m_iBidiLevel = iBidiLevel;
-      tp.m_iBidiPos = pTC->m_iBidiOrder;
-      tp.m_iFontSize = pTC->m_iFontSize;
-      tp.m_iHorizontalScale = pTC->horizonal_scale();
-      tp.m_iVerticalScale = pTC->vertical_scale();
-      dwIdentity = pTC->m_dwIdentity;
-      tp.m_dwIdentity = dwIdentity;
-      tp.m_pUserData = pTC->m_pUserData;
-      tp.m_dwStatus = CFX_BreakType::Piece;
-      ++i;
-    } else if (iBidiLevel != pTC->m_iBidiLevel ||
-               pTC->m_dwIdentity != dwIdentity) {
-      tp.m_iChars = i - tp.m_iStartChar;
-      m_pCurLine->m_LinePieces.push_back(tp);
-
-      tp.m_iStartPos += tp.m_iWidth;
-      tp.m_iStartChar = i;
-      tpo.index = j++;
-      tpo.pos = tp.m_iBidiPos;
-      tpos->push_back(tpo);
-      iBidiLevel = -1;
-    } else {
-      iCharWidth = pTC->m_iCharWidth;
-      if (iCharWidth > 0)
-        tp.m_iWidth += iCharWidth;
-      ++i;
-    }
-  }
-
-  if (i > tp.m_iStartChar) {
-    tp.m_dwStatus = dwStatus;
-    tp.m_iChars = i - tp.m_iStartChar;
-    m_pCurLine->m_LinePieces.push_back(tp);
-
-    tpo.index = j;
-    tpo.pos = tp.m_iBidiPos;
-    tpos->push_back(tpo);
-  }
-
-  std::sort(tpos->begin(), tpos->end());
-  int32_t iStartPos = m_pCurLine->m_iStart;
-  for (const auto& it : *tpos) {
-    CFX_BreakPiece& ttp = m_pCurLine->m_LinePieces[it.index];
-    ttp.m_iStartPos = iStartPos;
-    iStartPos += ttp.m_iWidth;
-  }
-}
-
-void CFX_RTFBreak::EndBreak_Alignment(const std::deque<FX_TPO>& tpos,
-                                      bool bAllChars,
-                                      CFX_BreakType dwStatus) {
-  int32_t iNetWidth = m_pCurLine->m_iWidth;
-  int32_t iGapChars = 0;
-  bool bFind = false;
-  for (auto it = tpos.rbegin(); it != tpos.rend(); it++) {
-    CFX_BreakPiece& ttp = m_pCurLine->m_LinePieces[it->index];
-    if (!bFind)
-      iNetWidth = ttp.GetEndPos();
-
-    bool bArabic = FX_IsOdd(ttp.m_iBidiLevel);
-    int32_t j = bArabic ? 0 : ttp.m_iChars - 1;
-    while (j > -1 && j < ttp.m_iChars) {
-      const CFX_Char* tc = ttp.GetChar(j);
-      if (tc->m_eLineBreakType == FX_LINEBREAKTYPE::kDIRECT_BRK)
-        ++iGapChars;
-
-      if (!bFind || !bAllChars) {
-        FX_CHARTYPE dwCharType = tc->GetCharType();
-        if (dwCharType == FX_CHARTYPE::kSpace ||
-            dwCharType == FX_CHARTYPE::kControl) {
-          if (!bFind) {
-            int32_t iCharWidth = tc->m_iCharWidth;
-            if (bAllChars && iCharWidth > 0)
-              iNetWidth -= iCharWidth;
-          }
-        } else {
-          bFind = true;
-          if (!bAllChars)
-            break;
-        }
-      }
-      j += bArabic ? 1 : -1;
-    }
-    if (!bAllChars && bFind)
-      break;
-  }
-
-  int32_t iOffset = m_iLineWidth - iNetWidth;
-  if (iGapChars > 0 && (m_iAlignment == CFX_RTFLineAlignment::Distributed ||
-                        (m_iAlignment == CFX_RTFLineAlignment::Justified &&
-                         dwStatus != CFX_BreakType::Paragraph))) {
-    int32_t iStart = -1;
-    for (const auto& tpo : tpos) {
-      CFX_BreakPiece& ttp = m_pCurLine->m_LinePieces[tpo.index];
-      if (iStart < 0)
-        iStart = ttp.m_iStartPos;
-      else
-        ttp.m_iStartPos = iStart;
-
-      for (int32_t j = 0; j < ttp.m_iChars; ++j) {
-        CFX_Char* tc = ttp.GetChar(j);
-        if (tc->m_eLineBreakType != FX_LINEBREAKTYPE::kDIRECT_BRK ||
-            tc->m_iCharWidth < 0) {
-          continue;
-        }
-        int32_t k = iOffset / iGapChars;
-        tc->m_iCharWidth += k;
-        ttp.m_iWidth += k;
-        iOffset -= k;
-        --iGapChars;
-        if (iGapChars < 1)
-          break;
-      }
-      iStart += ttp.m_iWidth;
-    }
-  } else if (m_iAlignment == CFX_RTFLineAlignment::Right ||
-             m_iAlignment == CFX_RTFLineAlignment::Center) {
-    if (m_iAlignment == CFX_RTFLineAlignment::Center)
-      iOffset /= 2;
-    if (iOffset > 0) {
-      for (auto& ttp : m_pCurLine->m_LinePieces)
-        ttp.m_iStartPos += iOffset;
-    }
-  }
-}
-
-int32_t CFX_RTFBreak::GetBreakPos(std::vector<CFX_Char>& tca,
-                                  bool bAllChars,
-                                  bool bOnlyBrk,
-                                  int32_t* pEndPos) {
-  int32_t iLength = pdfium::CollectionSize<int32_t>(tca) - 1;
-  if (iLength < 1)
-    return iLength;
-
-  int32_t iBreak = -1;
-  int32_t iBreakPos = -1;
-  int32_t iIndirect = -1;
-  int32_t iIndirectPos = -1;
-  int32_t iLast = -1;
-  int32_t iLastPos = -1;
-  if (*pEndPos <= m_iLineWidth) {
-    if (!bAllChars)
-      return iLength;
-
-    iBreak = iLength;
-    iBreakPos = *pEndPos;
-  }
-
-  CFX_Char* pCharArray = tca.data();
-  CFX_Char* pCur = pCharArray + iLength;
-  --iLength;
-  if (bAllChars)
-    pCur->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
-
-  FX_BREAKPROPERTY nNext = FX_GetBreakProperty(pCur->char_code());
-  int32_t iCharWidth = pCur->m_iCharWidth;
-  if (iCharWidth > 0)
-    *pEndPos -= iCharWidth;
-
-  while (iLength >= 0) {
-    pCur = pCharArray + iLength;
-    FX_BREAKPROPERTY nCur = FX_GetBreakProperty(pCur->char_code());
-    bool bNeedBreak = false;
-    FX_LINEBREAKTYPE eType;
-    if (nCur == FX_BREAKPROPERTY::kTB) {
-      bNeedBreak = true;
-      eType = nNext == FX_BREAKPROPERTY::kTB
-                  ? FX_LINEBREAKTYPE::kPROHIBITED_BRK
-                  : GetLineBreakTypeFromPair(nCur, nNext);
-    } else {
-      if (nCur == FX_BREAKPROPERTY::kSP)
-        bNeedBreak = true;
-
-      eType = nNext == FX_BREAKPROPERTY::kSP
-                  ? FX_LINEBREAKTYPE::kPROHIBITED_BRK
-                  : GetLineBreakTypeFromPair(nCur, nNext);
-    }
-    if (bAllChars)
-      pCur->m_eLineBreakType = eType;
-
-    if (!bOnlyBrk) {
-      iCharWidth = pCur->m_iCharWidth;
-      if (*pEndPos <= m_iLineWidth || bNeedBreak) {
-        if (eType == FX_LINEBREAKTYPE::kDIRECT_BRK && iBreak < 0) {
-          iBreak = iLength;
-          iBreakPos = *pEndPos;
-          if (!bAllChars)
-            return iLength;
-        } else if (eType == FX_LINEBREAKTYPE::kINDIRECT_BRK && iIndirect < 0) {
-          iIndirect = iLength;
-          iIndirectPos = *pEndPos;
-        }
-        if (iLast < 0) {
-          iLast = iLength;
-          iLastPos = *pEndPos;
-        }
-      }
-      if (iCharWidth > 0)
-        *pEndPos -= iCharWidth;
-    }
-    nNext = nCur;
-    --iLength;
-  }
-  if (bOnlyBrk)
-    return 0;
-
-  if (iBreak > -1) {
-    *pEndPos = iBreakPos;
-    return iBreak;
-  }
-  if (iIndirect > -1) {
-    *pEndPos = iIndirectPos;
-    return iIndirect;
-  }
-  if (iLast > -1) {
-    *pEndPos = iLastPos;
-    return iLast;
-  }
-  return 0;
-}
-
-void CFX_RTFBreak::SplitTextLine(CFX_BreakLine* pCurLine,
-                                 CFX_BreakLine* pNextLine,
-                                 bool bAllChars) {
-  ASSERT(pCurLine);
-  ASSERT(pNextLine);
-
-  if (pCurLine->m_LineChars.size() < 2)
-    return;
-
-  int32_t iEndPos = pCurLine->GetLineEnd();
-  std::vector<CFX_Char>& curChars = pCurLine->m_LineChars;
-  int32_t iCharPos = GetBreakPos(curChars, bAllChars, false, &iEndPos);
-  if (iCharPos < 0)
-    iCharPos = 0;
-
-  ++iCharPos;
-  if (iCharPos >= pdfium::CollectionSize<int32_t>(pCurLine->m_LineChars)) {
-    pNextLine->Clear();
-    curChars[iCharPos - 1].m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
-    return;
-  }
-
-  pNextLine->m_LineChars =
-      std::vector<CFX_Char>(curChars.begin() + iCharPos, curChars.end());
-  curChars.erase(curChars.begin() + iCharPos, curChars.end());
-  pNextLine->m_iStart = pCurLine->m_iStart;
-  pNextLine->m_iWidth = pCurLine->GetLineEnd() - iEndPos;
-  pCurLine->m_iWidth = iEndPos;
-  curChars[iCharPos - 1].m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
-
-  for (size_t i = 0; i < pNextLine->m_LineChars.size(); ++i) {
-    if (pNextLine->m_LineChars[i].GetCharType() >= FX_CHARTYPE::kArabicAlef) {
-      pCurLine->DecrementArabicCharCount();
-      pNextLine->IncrementArabicCharCount();
-    }
-    pNextLine->m_LineChars[i].m_dwStatus = CFX_BreakType::None;
-  }
-}
-
-size_t CFX_RTFBreak::GetDisplayPos(const CFX_TextPiece* pPiece,
-                                   std::vector<TextCharPos>* pCharPos) const {
-  ASSERT(pPiece->iChars > 0);
-  ASSERT(pPiece->pFont);
-
-  RetainPtr<CFGAS_GEFont> pFont = pPiece->pFont;
-  CFX_RectF rtText(pPiece->rtPiece);
-  bool bRTLPiece = FX_IsOdd(pPiece->iBidiLevel);
-  float fFontSize = pPiece->fFontSize;
-  int32_t iFontSize = FXSYS_roundf(fFontSize * 20.0f);
-  if (iFontSize == 0)
-    return 0;
-
-  int32_t iAscent = pFont->GetAscent();
-  int32_t iDescent = pFont->GetDescent();
-  int32_t iMaxHeight = iAscent - iDescent;
-  float fFontHeight = fFontSize;
-  float fAscent = fFontHeight * static_cast<float>(iAscent) /
-                  static_cast<float>(iMaxHeight);
-  wchar_t wPrev = 0xFEFF;
-  wchar_t wNext;
-  float fX = rtText.left;
-  int32_t iHorScale = pPiece->iHorScale;
-  int32_t iVerScale = pPiece->iVerScale;
-  if (bRTLPiece)
-    fX = rtText.right();
-
-  float fY = rtText.top + fAscent;
-  size_t szCount = 0;
-  for (int32_t i = 0; i < pPiece->iChars; ++i) {
-    TextCharPos& current_char_pos = (*pCharPos)[szCount];
-    wchar_t wch = pPiece->szText[i];
-    int32_t iWidth = pPiece->Widths[i];
-    FX_CHARTYPE dwCharType = FX_GetCharType(wch);
-    if (iWidth == 0) {
-      if (dwCharType == FX_CHARTYPE::kArabicAlef)
-        wPrev = 0xFEFF;
-      continue;
-    }
-
-    uint32_t iCharWidth = abs(iWidth);
-    const bool bEmptyChar = (dwCharType >= FX_CHARTYPE::kTab &&
-                             dwCharType <= FX_CHARTYPE::kControl);
-    if (!bEmptyChar)
-      ++szCount;
-
-    iCharWidth /= iFontSize;
-    wchar_t wForm = wch;
-    if (dwCharType >= FX_CHARTYPE::kArabicAlef) {
-      if (i + 1 < pPiece->iChars) {
-        wNext = pPiece->szText[i + 1];
-        if (pPiece->Widths[i + 1] < 0 && i + 2 < pPiece->iChars)
-          wNext = pPiece->szText[i + 2];
-      } else {
-        wNext = 0xFEFF;
-      }
-      wForm = pdfium::arabic::GetFormChar(wch, wPrev, wNext);
-    } else if (bRTLPiece) {
-      wForm = FX_GetMirrorChar(wch);
-    }
-
-    if (!bEmptyChar) {
-      current_char_pos.m_GlyphIndex = pFont->GetGlyphIndex(wForm);
-      if (current_char_pos.m_GlyphIndex == 0xFFFF)
-        current_char_pos.m_GlyphIndex = pFont->GetGlyphIndex(wch);
-#if defined(OS_MACOSX)
-      current_char_pos.m_ExtGID = current_char_pos.m_GlyphIndex;
-#endif
-      current_char_pos.m_FontCharWidth = iCharWidth;
-    }
-
-    float fCharWidth = fFontSize * iCharWidth / 1000.0f;
-    if (bRTLPiece && dwCharType != FX_CHARTYPE::kCombination)
-      fX -= fCharWidth;
-
-    if (!bEmptyChar)
-      current_char_pos.m_Origin = CFX_PointF(fX, fY);
-    if (!bRTLPiece && dwCharType != FX_CHARTYPE::kCombination)
-      fX += fCharWidth;
-
-    if (!bEmptyChar) {
-      current_char_pos.m_bGlyphAdjust = true;
-      current_char_pos.m_AdjustMatrix[0] = -1;
-      current_char_pos.m_AdjustMatrix[1] = 0;
-      current_char_pos.m_AdjustMatrix[2] = 0;
-      current_char_pos.m_AdjustMatrix[3] = 1;
-      current_char_pos.m_Origin.y += fAscent * iVerScale / 100.0f;
-      current_char_pos.m_Origin.y -= fAscent;
-
-      if (iHorScale != 100 || iVerScale != 100) {
-        current_char_pos.m_AdjustMatrix[0] =
-            current_char_pos.m_AdjustMatrix[0] * iHorScale / 100.0f;
-        current_char_pos.m_AdjustMatrix[1] =
-            current_char_pos.m_AdjustMatrix[1] * iHorScale / 100.0f;
-        current_char_pos.m_AdjustMatrix[2] =
-            current_char_pos.m_AdjustMatrix[2] * iVerScale / 100.0f;
-        current_char_pos.m_AdjustMatrix[3] =
-            current_char_pos.m_AdjustMatrix[3] * iVerScale / 100.0f;
-      }
-    }
-    if (iWidth > 0)
-      wPrev = wch;
-  }
-  return szCount;
-}
diff --git a/xfa/fgas/layout/cfx_rtfbreak.h b/xfa/fgas/layout/cfx_rtfbreak.h
deleted file mode 100644
index 2a722b0..0000000
--- a/xfa/fgas/layout/cfx_rtfbreak.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FGAS_LAYOUT_CFX_RTFBREAK_H_
-#define XFA_FGAS_LAYOUT_CFX_RTFBREAK_H_
-
-#include <deque>
-#include <vector>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_unicode.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "xfa/fgas/layout/cfx_break.h"
-
-class CFGAS_GEFont;
-class CFX_TextUserData;
-class CFX_TextPiece;
-class TextCharPos;
-
-enum class CFX_RTFLineAlignment {
-  Left = 0,
-  Center,
-  Right,
-  Justified,
-  Distributed
-};
-
-class CFX_RTFBreak final : public CFX_Break {
- public:
-  explicit CFX_RTFBreak(uint32_t dwLayoutStyles);
-  ~CFX_RTFBreak() override;
-
-  void SetLineStartPos(float fLinePos);
-
-  void SetAlignment(CFX_RTFLineAlignment align) { m_iAlignment = align; }
-  void SetUserData(const RetainPtr<CFX_TextUserData>& pUserData);
-
-  void AddPositionedTab(float fTabPos);
-
-  CFX_BreakType EndBreak(CFX_BreakType dwStatus);
-
-  size_t GetDisplayPos(const CFX_TextPiece* pPiece,
-                       std::vector<TextCharPos>* pCharPos) const;
-
-  CFX_BreakType AppendChar(wchar_t wch);
-
- private:
-  void AppendChar_Combination(CFX_Char* pCurChar);
-  void AppendChar_Tab(CFX_Char* pCurChar);
-  CFX_BreakType AppendChar_Control(CFX_Char* pCurChar);
-  CFX_BreakType AppendChar_Arabic(CFX_Char* pCurChar);
-  CFX_BreakType AppendChar_Others(CFX_Char* pCurChar);
-  bool GetPositionedTab(int32_t* iTabPos) const;
-
-  int32_t GetBreakPos(std::vector<CFX_Char>& tca,
-                      bool bAllChars,
-                      bool bOnlyBrk,
-                      int32_t* pEndPos);
-  void SplitTextLine(CFX_BreakLine* pCurLine,
-                     CFX_BreakLine* pNextLine,
-                     bool bAllChars);
-  bool EndBreak_SplitLine(CFX_BreakLine* pNextLine,
-                          bool bAllChars,
-                          CFX_BreakType dwStatus);
-  void EndBreak_BidiLine(std::deque<FX_TPO>* tpos, CFX_BreakType dwStatus);
-  void EndBreak_Alignment(const std::deque<FX_TPO>& tpos,
-                          bool bAllChars,
-                          CFX_BreakType dwStatus);
-
-  bool m_bPagination;
-  std::vector<int32_t> m_PositionedTabs;
-  CFX_RTFLineAlignment m_iAlignment;
-  RetainPtr<CFX_TextUserData> m_pUserData;
-};
-
-#endif  // XFA_FGAS_LAYOUT_CFX_RTFBREAK_H_
diff --git a/xfa/fgas/layout/cfx_rtfbreak_unittest.cpp b/xfa/fgas/layout/cfx_rtfbreak_unittest.cpp
deleted file mode 100644
index 0775d7b..0000000
--- a/xfa/fgas/layout/cfx_rtfbreak_unittest.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fgas/layout/cfx_rtfbreak.h"
-
-#include <memory>
-#include <utility>
-
-#include "core/fxge/cfx_font.h"
-#include "core/fxge/cfx_gemodule.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/xfa_unit_test_support.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fgas/font/cfgas_fontmgr.h"
-#include "xfa/fgas/font/cfgas_gefont.h"
-#include "xfa/fgas/layout/cfx_char.h"
-
-class CFX_RTFBreakTest : public testing::Test {
- public:
-  void SetUp() override {
-    font_ =
-        CFGAS_GEFont::LoadFont(L"Arial Black", 0, 0, GetGlobalFontManager());
-    ASSERT_TRUE(font_.Get());
-  }
-
-  std::unique_ptr<CFX_RTFBreak> CreateBreak(uint32_t layout_styles) {
-    auto rtf_break = pdfium::MakeUnique<CFX_RTFBreak>(layout_styles);
-    rtf_break->SetFont(font_);
-    return rtf_break;
-  }
-
- private:
-  RetainPtr<CFGAS_GEFont> font_;
-};
-
-// As soon as you get one of the control characters the break is complete
-// and must be consumed before you get any more characters ....
-
-TEST_F(CFX_RTFBreakTest, AddChars) {
-  auto rtf_break = CreateBreak(FX_LAYOUTSTYLE_ExpandTab);
-
-  WideString str(L"Input String.");
-  for (wchar_t ch : str)
-    EXPECT_EQ(CFX_BreakType::None, rtf_break->AppendChar(ch));
-
-  EXPECT_EQ(CFX_BreakType::Paragraph, rtf_break->AppendChar(L'\n'));
-  ASSERT_EQ(1, rtf_break->CountBreakPieces());
-  EXPECT_EQ(str + L"\n", rtf_break->GetBreakPieceUnstable(0)->GetString());
-
-  rtf_break->ClearBreakPieces();
-  rtf_break->Reset();
-  EXPECT_EQ(0, rtf_break->GetCurrentLineForTesting()->GetLineEnd());
-
-  str = L"Second str.";
-  for (wchar_t ch : str)
-    EXPECT_EQ(CFX_BreakType::None, rtf_break->AppendChar(ch));
-
-  // Force the end of the break at the end of the string.
-  rtf_break->EndBreak(CFX_BreakType::Paragraph);
-  ASSERT_EQ(1, rtf_break->CountBreakPieces());
-  EXPECT_EQ(str, rtf_break->GetBreakPieceUnstable(0)->GetString());
-}
-
-TEST_F(CFX_RTFBreakTest, ControlCharacters) {
-  auto rtf_break = CreateBreak(FX_LAYOUTSTYLE_ExpandTab);
-  EXPECT_EQ(CFX_BreakType::Line, rtf_break->AppendChar(L'\v'));
-  EXPECT_EQ(CFX_BreakType::Page, rtf_break->AppendChar(L'\f'));
-  const wchar_t kUnicodeParagraphSeparator = 0x2029;
-  EXPECT_EQ(CFX_BreakType::Paragraph,
-            rtf_break->AppendChar(kUnicodeParagraphSeparator));
-  EXPECT_EQ(CFX_BreakType::Paragraph, rtf_break->AppendChar(L'\n'));
-
-  ASSERT_EQ(1, rtf_break->CountBreakPieces());
-  EXPECT_EQ(L"\v", rtf_break->GetBreakPieceUnstable(0)->GetString());
-}
-
-TEST_F(CFX_RTFBreakTest, BidiLine) {
-  auto rtf_break = CreateBreak(FX_LAYOUTSTYLE_ExpandTab);
-  rtf_break->SetLineBreakTolerance(1);
-  rtf_break->SetFontSize(12);
-
-  WideString input = WideString::FromUTF8(ByteStringView("\xa\x0\xa\xa", 4));
-  for (wchar_t ch : input)
-    rtf_break->AppendChar(ch);
-
-  std::vector<CFX_Char> chars =
-      rtf_break->GetCurrentLineForTesting()->m_LineChars;
-  CFX_Char::BidiLine(&chars, chars.size());
-  EXPECT_EQ(3u, chars.size());
-}
diff --git a/xfa/fgas/layout/cfx_textpiece.cpp b/xfa/fgas/layout/cfx_textpiece.cpp
deleted file mode 100644
index 06f6cd6..0000000
--- a/xfa/fgas/layout/cfx_textpiece.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fgas/layout/cfx_textpiece.h"
-
-#include "xfa/fgas/font/cfgas_gefont.h"
-
-CFX_TextPiece::CFX_TextPiece() = default;
-
-CFX_TextPiece::~CFX_TextPiece() = default;
diff --git a/xfa/fgas/layout/cfx_textpiece.h b/xfa/fgas/layout/cfx_textpiece.h
deleted file mode 100644
index 409d200..0000000
--- a/xfa/fgas/layout/cfx_textpiece.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FGAS_LAYOUT_CFX_TEXTPIECE_H_
-#define XFA_FGAS_LAYOUT_CFX_TEXTPIECE_H_
-
-#include <vector>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/retain_ptr.h"
-
-class CFGAS_GEFont;
-
-class CFX_TextPiece {
- public:
-  CFX_TextPiece();
-  ~CFX_TextPiece();
-
-  WideString szText;
-  std::vector<int32_t> Widths;
-  int32_t iChars;
-  int32_t iHorScale;
-  int32_t iVerScale;
-  int32_t iBidiLevel;
-  float fFontSize;
-  CFX_RectF rtPiece;
-  RetainPtr<CFGAS_GEFont> pFont;
-};
-
-#endif  // XFA_FGAS_LAYOUT_CFX_TEXTPIECE_H_
diff --git a/xfa/fgas/layout/cfx_textuserdata.cpp b/xfa/fgas/layout/cfx_textuserdata.cpp
deleted file mode 100644
index f5151bd..0000000
--- a/xfa/fgas/layout/cfx_textuserdata.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fgas/layout/cfx_textuserdata.h"
-
-#include "core/fxcrt/css/cfx_css.h"
-#include "core/fxcrt/css/cfx_csscomputedstyle.h"
-#include "core/fxcrt/css/cfx_cssstyleselector.h"
-#include "xfa/fgas/layout/cfx_linkuserdata.h"
-
-CFX_TextUserData::CFX_TextUserData(
-    const RetainPtr<CFX_CSSComputedStyle>& pStyle)
-    : m_pStyle(pStyle) {}
-
-CFX_TextUserData::CFX_TextUserData(
-    const RetainPtr<CFX_CSSComputedStyle>& pStyle,
-    const RetainPtr<CFX_LinkUserData>& pLinkData)
-    : m_pStyle(pStyle), m_pLinkData(pLinkData) {}
-
-CFX_TextUserData::~CFX_TextUserData() {}
diff --git a/xfa/fgas/layout/cfx_textuserdata.h b/xfa/fgas/layout/cfx_textuserdata.h
deleted file mode 100644
index d5aaed9..0000000
--- a/xfa/fgas/layout/cfx_textuserdata.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FGAS_LAYOUT_CFX_TEXTUSERDATA_H_
-#define XFA_FGAS_LAYOUT_CFX_TEXTUSERDATA_H_
-
-#include "core/fxcrt/retain_ptr.h"
-
-class CFX_CSSComputedStyle;
-class CFX_LinkUserData;
-
-class CFX_TextUserData final : public Retainable {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  RetainPtr<CFX_CSSComputedStyle> m_pStyle;
-  RetainPtr<CFX_LinkUserData> m_pLinkData;
-
- private:
-  explicit CFX_TextUserData(const RetainPtr<CFX_CSSComputedStyle>& pStyle);
-  CFX_TextUserData(const RetainPtr<CFX_CSSComputedStyle>& pStyle,
-                   const RetainPtr<CFX_LinkUserData>& pLinkData);
-  ~CFX_TextUserData() override;
-};
-
-#endif  // XFA_FGAS_LAYOUT_CFX_TEXTUSERDATA_H_
diff --git a/xfa/fgas/layout/cfx_txtbreak.cpp b/xfa/fgas/layout/cfx_txtbreak.cpp
deleted file mode 100644
index 4b87728..0000000
--- a/xfa/fgas/layout/cfx_txtbreak.cpp
+++ /dev/null
@@ -1,1001 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fgas/layout/cfx_txtbreak.h"
-
-#include <algorithm>
-
-#include "build/build_config.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxge/text_char_pos.h"
-#include "third_party/base/stl_util.h"
-#include "xfa/fgas/font/cfgas_gefont.h"
-#include "xfa/fgas/layout/cfx_char.h"
-#include "xfa/fgas/layout/fx_arabic.h"
-#include "xfa/fgas/layout/fx_linebreak.h"
-
-namespace {
-
-bool IsCtrlCode(wchar_t wch) {
-  FX_CHARTYPE dwRet = FX_GetCharType(wch);
-  return dwRet == FX_CHARTYPE::kTab || dwRet == FX_CHARTYPE::kControl;
-}
-
-}  // namespace
-
-CFX_TxtBreak::CFX_TxtBreak()
-    : CFX_Break(FX_LAYOUTSTYLE_None),
-      m_iAlignment(CFX_TxtLineAlignment_Left),
-      m_iCombWidth(360000) {}
-
-CFX_TxtBreak::~CFX_TxtBreak() {}
-
-void CFX_TxtBreak::SetLineWidth(float fLineWidth) {
-  m_iLineWidth = FXSYS_roundf(fLineWidth * kConversionFactor);
-  ASSERT(m_iLineWidth >= 20000);
-}
-
-void CFX_TxtBreak::SetAlignment(int32_t iAlignment) {
-  ASSERT(iAlignment >= CFX_TxtLineAlignment_Left);
-  ASSERT(iAlignment <= CFX_TxtLineAlignment_Justified);
-  m_iAlignment = iAlignment;
-}
-
-void CFX_TxtBreak::SetCombWidth(float fCombWidth) {
-  m_iCombWidth = FXSYS_roundf(fCombWidth * kConversionFactor);
-}
-
-void CFX_TxtBreak::AppendChar_Combination(CFX_Char* pCurChar) {
-  wchar_t wch = pCurChar->char_code();
-  wchar_t wForm;
-  FX_SAFE_INT32 iCharWidth = 0;
-  pCurChar->m_iCharWidth = -1;
-  if (m_bCombText) {
-    iCharWidth = m_iCombWidth;
-  } else {
-    wForm = wch;
-    CFX_Char* pLastChar = GetLastChar(0, false, false);
-    if (pLastChar &&
-        (pLastChar->m_dwCharStyles & FX_TXTCHARSTYLE_ArabicShadda) == 0) {
-      bool bShadda = false;
-      if (wch == 0x0651) {
-        wchar_t wLast = pLastChar->char_code();
-        if (wLast >= 0x064C && wLast <= 0x0650) {
-          wForm = FX_GetArabicFromShaddaTable(wLast);
-          bShadda = true;
-        }
-      } else if (wch >= 0x064C && wch <= 0x0650) {
-        if (pLastChar->char_code() == 0x0651) {
-          wForm = FX_GetArabicFromShaddaTable(wch);
-          bShadda = true;
-        }
-      }
-      if (bShadda) {
-        pLastChar->m_dwCharStyles |= FX_TXTCHARSTYLE_ArabicShadda;
-        pLastChar->m_iCharWidth = 0;
-        pCurChar->m_dwCharStyles |= FX_TXTCHARSTYLE_ArabicShadda;
-      }
-    }
-    int32_t iCharWidthOut;
-    if (m_pFont && m_pFont->GetCharWidth(wForm, &iCharWidthOut))
-      iCharWidth = iCharWidthOut;
-    else
-      iCharWidth = 0;
-
-    iCharWidth *= m_iFontSize;
-    iCharWidth *= m_iHorizontalScale;
-    iCharWidth /= 100;
-  }
-
-  iCharWidth *= -1;
-  pCurChar->m_iCharWidth = iCharWidth.ValueOrDefault(0);
-}
-
-void CFX_TxtBreak::AppendChar_Tab(CFX_Char* pCurChar) {
-  m_eCharType = FX_CHARTYPE::kTab;
-}
-
-CFX_BreakType CFX_TxtBreak::AppendChar_Control(CFX_Char* pCurChar) {
-  m_eCharType = FX_CHARTYPE::kControl;
-  CFX_BreakType dwRet = CFX_BreakType::None;
-  if (!m_bSingleLine) {
-    wchar_t wch = pCurChar->char_code();
-    switch (wch) {
-      case L'\v':
-      case 0x2028:
-        dwRet = CFX_BreakType::Line;
-        break;
-      case L'\f':
-        dwRet = CFX_BreakType::Page;
-        break;
-      case 0x2029:
-        dwRet = CFX_BreakType::Paragraph;
-        break;
-      default:
-        if (wch == m_wParagraphBreakChar)
-          dwRet = CFX_BreakType::Paragraph;
-        break;
-    }
-    if (dwRet != CFX_BreakType::None)
-      dwRet = EndBreak(dwRet);
-  }
-  return dwRet;
-}
-
-CFX_BreakType CFX_TxtBreak::AppendChar_Arabic(CFX_Char* pCurChar) {
-  FX_CHARTYPE chartype = pCurChar->GetCharType();
-  int32_t& iLineWidth = m_pCurLine->m_iWidth;
-  wchar_t wForm;
-  CFX_Char* pLastChar = nullptr;
-  bool bAlef = false;
-  if (!m_bCombText && m_eCharType >= FX_CHARTYPE::kArabicAlef &&
-      m_eCharType <= FX_CHARTYPE::kArabicDistortion) {
-    FX_SAFE_INT32 iCharWidth = 0;
-    pLastChar = GetLastChar(1, true, false);
-    if (pLastChar) {
-      if (pLastChar->m_iCharWidth > 0)
-        iLineWidth -= pLastChar->m_iCharWidth;
-      iCharWidth = pLastChar->m_iCharWidth;
-
-      CFX_Char* pPrevChar = GetLastChar(2, true, false);
-      wForm = pdfium::arabic::GetFormChar(pLastChar, pPrevChar, pCurChar);
-      bAlef = (wForm == 0xFEFF &&
-               pLastChar->GetCharType() == FX_CHARTYPE::kArabicAlef);
-      if (m_pFont) {
-        int32_t iCharWidthOut = 0;
-        m_pFont->GetCharWidth(wForm, &iCharWidthOut);
-        iCharWidth = iCharWidthOut;
-      }
-      if (wForm == 0xFEFF)
-        iCharWidth = 0;
-
-      iCharWidth *= m_iFontSize;
-      iCharWidth *= m_iHorizontalScale;
-      iCharWidth /= 100;
-
-      int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
-      pLastChar->m_iCharWidth = iCharWidthValid;
-      iLineWidth += iCharWidthValid;
-    }
-  }
-
-  m_eCharType = chartype;
-  wForm = pdfium::arabic::GetFormChar(pCurChar, bAlef ? nullptr : pLastChar,
-                                      nullptr);
-  FX_SAFE_INT32 iCharWidth = 0;
-  if (m_bCombText) {
-    iCharWidth = m_iCombWidth;
-  } else {
-    if (m_pFont) {
-      int32_t iCharWidthOut = 0;
-      m_pFont->GetCharWidth(wForm, &iCharWidthOut);
-      iCharWidth = iCharWidthOut;
-    }
-    if (wForm == 0xFEFF)
-      iCharWidth = 0;
-
-    iCharWidth *= m_iFontSize;
-    iCharWidth *= m_iHorizontalScale;
-    iCharWidth /= 100;
-  }
-
-  int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
-  pCurChar->m_iCharWidth = iCharWidthValid;
-  iLineWidth += iCharWidthValid;
-
-  m_pCurLine->IncrementArabicCharCount();
-  if (!m_bSingleLine && IsGreaterThanLineWidth(iLineWidth))
-    return EndBreak(CFX_BreakType::Line);
-  return CFX_BreakType::None;
-}
-
-CFX_BreakType CFX_TxtBreak::AppendChar_Others(CFX_Char* pCurChar) {
-  FX_CHARTYPE chartype = pCurChar->GetCharType();
-  int32_t& iLineWidth = m_pCurLine->m_iWidth;
-  FX_SAFE_INT32 iCharWidth = 0;
-  m_eCharType = chartype;
-  wchar_t wch = pCurChar->char_code();
-  wchar_t wForm = wch;
-
-  if (m_bCombText) {
-    iCharWidth = m_iCombWidth;
-  } else {
-    int32_t iCharWidthOut;
-    if (m_pFont && m_pFont->GetCharWidth(wForm, &iCharWidthOut))
-      iCharWidth = iCharWidthOut;
-    else
-      iCharWidth = 0;
-
-    iCharWidth *= m_iFontSize;
-    iCharWidth *= m_iHorizontalScale;
-    iCharWidth /= 100;
-  }
-
-  iCharWidth += m_iCharSpace;
-
-  int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
-  pCurChar->m_iCharWidth = iCharWidthValid;
-  iLineWidth += iCharWidthValid;
-  if (!m_bSingleLine && chartype != FX_CHARTYPE::kSpace &&
-      IsGreaterThanLineWidth(iLineWidth)) {
-    return EndBreak(CFX_BreakType::Line);
-  }
-
-  return CFX_BreakType::None;
-}
-
-CFX_BreakType CFX_TxtBreak::AppendChar(wchar_t wch) {
-  FX_CHARTYPE chartype = FX_GetCharType(wch);
-  m_pCurLine->m_LineChars.emplace_back(wch, m_iHorizontalScale,
-                                       m_iVerticalScale);
-  CFX_Char* pCurChar = &m_pCurLine->m_LineChars.back();
-  pCurChar->m_dwCharStyles = m_iAlignment | (1 << 8);
-
-  CFX_BreakType dwRet1 = CFX_BreakType::None;
-  if (chartype != FX_CHARTYPE::kCombination &&
-      GetUnifiedCharType(m_eCharType) != GetUnifiedCharType(chartype) &&
-      m_eCharType != FX_CHARTYPE::kUnknown && !m_bSingleLine &&
-      IsGreaterThanLineWidth(m_pCurLine->m_iWidth) &&
-      (m_eCharType != FX_CHARTYPE::kSpace ||
-       chartype != FX_CHARTYPE::kControl)) {
-    dwRet1 = EndBreak(CFX_BreakType::Line);
-    if (!m_pCurLine->m_LineChars.empty())
-      pCurChar = &m_pCurLine->m_LineChars.back();
-  }
-
-  CFX_BreakType dwRet2 = CFX_BreakType::None;
-  if (wch == m_wParagraphBreakChar) {
-    // This is handled in AppendChar_Control, but it seems like \n and \r
-    // don't get matched as control characters so we go into AppendChar_other
-    // and never detect the new paragraph ...
-    dwRet2 = CFX_BreakType::Paragraph;
-    EndBreak(dwRet2);
-  } else {
-    switch (chartype) {
-      case FX_CHARTYPE::kTab:
-        AppendChar_Tab(pCurChar);
-        break;
-      case FX_CHARTYPE::kControl:
-        dwRet2 = AppendChar_Control(pCurChar);
-        break;
-      case FX_CHARTYPE::kCombination:
-        AppendChar_Combination(pCurChar);
-        break;
-      case FX_CHARTYPE::kArabicAlef:
-      case FX_CHARTYPE::kArabicSpecial:
-      case FX_CHARTYPE::kArabicDistortion:
-      case FX_CHARTYPE::kArabicNormal:
-      case FX_CHARTYPE::kArabicForm:
-      case FX_CHARTYPE::kArabic:
-        dwRet2 = AppendChar_Arabic(pCurChar);
-        break;
-      case FX_CHARTYPE::kUnknown:
-      case FX_CHARTYPE::kSpace:
-      case FX_CHARTYPE::kNumeric:
-      case FX_CHARTYPE::kNormal:
-      default:
-        dwRet2 = AppendChar_Others(pCurChar);
-        break;
-    }
-  }
-  return std::max(dwRet1, dwRet2);
-}
-
-bool CFX_TxtBreak::EndBreak_SplitLine(CFX_BreakLine* pNextLine,
-                                      bool bAllChars) {
-  bool bDone = false;
-  CFX_Char* pTC;
-  if (!m_bSingleLine && IsGreaterThanLineWidth(m_pCurLine->m_iWidth)) {
-    pTC = m_pCurLine->GetChar(m_pCurLine->m_LineChars.size() - 1);
-    switch (pTC->GetCharType()) {
-      case FX_CHARTYPE::kTab:
-      case FX_CHARTYPE::kControl:
-      case FX_CHARTYPE::kSpace:
-        break;
-      default:
-        SplitTextLine(m_pCurLine.Get(), pNextLine, bAllChars);
-        bDone = true;
-        break;
-    }
-  }
-
-  CFX_BreakPiece tp;
-  if (bAllChars && !bDone) {
-    int32_t iEndPos = m_pCurLine->m_iWidth;
-    GetBreakPos(&m_pCurLine->m_LineChars, bAllChars, true, &iEndPos);
-  }
-  return false;
-}
-
-void CFX_TxtBreak::EndBreak_BidiLine(std::deque<FX_TPO>* tpos,
-                                     CFX_BreakType dwStatus) {
-  CFX_BreakPiece tp;
-  FX_TPO tpo;
-  CFX_Char* pTC;
-  std::vector<CFX_Char>& chars = m_pCurLine->m_LineChars;
-  if (!m_pCurLine->HasArabicChar()) {
-    tp.m_dwStatus = dwStatus;
-    tp.m_iStartPos = m_pCurLine->m_iStart;
-    tp.m_iWidth = m_pCurLine->m_iWidth;
-    tp.m_iStartChar = 0;
-    tp.m_iChars = m_pCurLine->m_LineChars.size();
-    tp.m_pChars = &m_pCurLine->m_LineChars;
-    pTC = &chars[0];
-    tp.m_dwCharStyles = pTC->m_dwCharStyles;
-    tp.m_iHorizontalScale = pTC->horizonal_scale();
-    tp.m_iVerticalScale = pTC->vertical_scale();
-    m_pCurLine->m_LinePieces.push_back(tp);
-    tpos->push_back({0, 0});
-    return;
-  }
-
-  size_t iBidiNum = 0;
-  for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
-    pTC = &chars[i];
-    pTC->m_iBidiPos = static_cast<int32_t>(i);
-    if (pTC->GetCharType() != FX_CHARTYPE::kControl)
-      iBidiNum = i;
-    if (i == 0)
-      pTC->m_iBidiLevel = 1;
-  }
-  CFX_Char::BidiLine(&chars, iBidiNum + 1);
-
-  tp.m_dwStatus = CFX_BreakType::Piece;
-  tp.m_iStartPos = m_pCurLine->m_iStart;
-  tp.m_pChars = &m_pCurLine->m_LineChars;
-  int32_t iBidiLevel = -1;
-  int32_t iCharWidth;
-  int32_t i = 0;
-  int32_t j = -1;
-  int32_t iCount = pdfium::CollectionSize<int32_t>(m_pCurLine->m_LineChars);
-  while (i < iCount) {
-    pTC = &chars[i];
-    if (iBidiLevel < 0) {
-      iBidiLevel = pTC->m_iBidiLevel;
-      tp.m_iWidth = 0;
-      tp.m_iBidiLevel = iBidiLevel;
-      tp.m_iBidiPos = pTC->m_iBidiOrder;
-      tp.m_dwCharStyles = pTC->m_dwCharStyles;
-      tp.m_iHorizontalScale = pTC->horizonal_scale();
-      tp.m_iVerticalScale = pTC->vertical_scale();
-      tp.m_dwStatus = CFX_BreakType::Piece;
-    }
-    if (iBidiLevel != pTC->m_iBidiLevel ||
-        pTC->m_dwStatus != CFX_BreakType::None) {
-      if (iBidiLevel == pTC->m_iBidiLevel) {
-        tp.m_dwStatus = pTC->m_dwStatus;
-        iCharWidth = pTC->m_iCharWidth;
-        if (iCharWidth > 0)
-          tp.m_iWidth += iCharWidth;
-
-        i++;
-      }
-      tp.m_iChars = i - tp.m_iStartChar;
-      m_pCurLine->m_LinePieces.push_back(tp);
-      tp.m_iStartPos += tp.m_iWidth;
-      tp.m_iStartChar = i;
-      tpo.index = ++j;
-      tpo.pos = tp.m_iBidiPos;
-      tpos->push_back(tpo);
-      iBidiLevel = -1;
-    } else {
-      iCharWidth = pTC->m_iCharWidth;
-      if (iCharWidth > 0)
-        tp.m_iWidth += iCharWidth;
-
-      i++;
-    }
-  }
-  if (i > tp.m_iStartChar) {
-    tp.m_dwStatus = dwStatus;
-    tp.m_iChars = i - tp.m_iStartChar;
-    m_pCurLine->m_LinePieces.push_back(tp);
-    tpo.index = ++j;
-    tpo.pos = tp.m_iBidiPos;
-    tpos->push_back(tpo);
-  }
-  if (j > -1) {
-    if (j > 0) {
-      std::sort(tpos->begin(), tpos->end());
-      int32_t iStartPos = 0;
-      for (i = 0; i <= j; i++) {
-        tpo = (*tpos)[i];
-        CFX_BreakPiece& ttp = m_pCurLine->m_LinePieces[tpo.index];
-        ttp.m_iStartPos = iStartPos;
-        iStartPos += ttp.m_iWidth;
-      }
-    }
-    m_pCurLine->m_LinePieces[j].m_dwStatus = dwStatus;
-  }
-}
-
-void CFX_TxtBreak::EndBreak_Alignment(const std::deque<FX_TPO>& tpos,
-                                      bool bAllChars,
-                                      CFX_BreakType dwStatus) {
-  int32_t iNetWidth = m_pCurLine->m_iWidth;
-  int32_t iGapChars = 0;
-  bool bFind = false;
-  for (auto it = tpos.rbegin(); it != tpos.rend(); ++it) {
-    CFX_BreakPiece& ttp = m_pCurLine->m_LinePieces[it->index];
-    if (!bFind)
-      iNetWidth = ttp.GetEndPos();
-
-    bool bArabic = FX_IsOdd(ttp.m_iBidiLevel);
-    int32_t j = bArabic ? 0 : ttp.m_iChars - 1;
-    while (j > -1 && j < ttp.m_iChars) {
-      const CFX_Char* pTC = ttp.GetChar(j);
-      if (pTC->m_eLineBreakType == FX_LINEBREAKTYPE::kDIRECT_BRK)
-        iGapChars++;
-      if (!bFind || !bAllChars) {
-        FX_CHARTYPE chartype = pTC->GetCharType();
-        if (chartype == FX_CHARTYPE::kSpace ||
-            chartype == FX_CHARTYPE::kControl) {
-          if (!bFind && bAllChars && pTC->m_iCharWidth > 0)
-            iNetWidth -= pTC->m_iCharWidth;
-        } else {
-          bFind = true;
-          if (!bAllChars)
-            break;
-        }
-      }
-      j += bArabic ? 1 : -1;
-    }
-    if (!bAllChars && bFind)
-      break;
-  }
-
-  int32_t iOffset = m_iLineWidth - iNetWidth;
-  if (iGapChars > 0 && m_iAlignment & CFX_TxtLineAlignment_Justified &&
-      dwStatus != CFX_BreakType::Paragraph) {
-    int32_t iStart = -1;
-    for (auto& tpo : tpos) {
-      CFX_BreakPiece& ttp = m_pCurLine->m_LinePieces[tpo.index];
-      if (iStart < -1)
-        iStart = ttp.m_iStartPos;
-      else
-        ttp.m_iStartPos = iStart;
-
-      for (int32_t j = 0; j < ttp.m_iChars && iGapChars > 0; j++, iGapChars--) {
-        CFX_Char* pTC = ttp.GetChar(j);
-        if (pTC->m_eLineBreakType != FX_LINEBREAKTYPE::kDIRECT_BRK ||
-            pTC->m_iCharWidth < 0) {
-          continue;
-        }
-        int32_t k = iOffset / iGapChars;
-        pTC->m_iCharWidth += k;
-        ttp.m_iWidth += k;
-        iOffset -= k;
-      }
-      iStart += ttp.m_iWidth;
-    }
-  } else if (m_iAlignment & CFX_TxtLineAlignment_Center ||
-             m_iAlignment & CFX_TxtLineAlignment_Right) {
-    if (m_iAlignment & CFX_TxtLineAlignment_Center &&
-        !(m_iAlignment & CFX_TxtLineAlignment_Right)) {
-      iOffset /= 2;
-    }
-    if (iOffset > 0) {
-      for (auto& ttp : m_pCurLine->m_LinePieces)
-        ttp.m_iStartPos += iOffset;
-    }
-  }
-}
-
-CFX_BreakType CFX_TxtBreak::EndBreak(CFX_BreakType dwStatus) {
-  ASSERT(dwStatus != CFX_BreakType::None);
-
-  if (!m_pCurLine->m_LinePieces.empty()) {
-    if (dwStatus != CFX_BreakType::Piece)
-      m_pCurLine->m_LinePieces.back().m_dwStatus = dwStatus;
-    return m_pCurLine->m_LinePieces.back().m_dwStatus;
-  }
-
-  if (HasLine()) {
-    if (m_Lines[m_iReadyLineIndex].m_LinePieces.empty())
-      return CFX_BreakType::None;
-
-    if (dwStatus != CFX_BreakType::Piece)
-      m_Lines[m_iReadyLineIndex].m_LinePieces.back().m_dwStatus = dwStatus;
-    return m_Lines[m_iReadyLineIndex].m_LinePieces.back().m_dwStatus;
-  }
-
-  if (m_pCurLine->m_LineChars.empty())
-    return CFX_BreakType::None;
-
-  m_pCurLine->m_LineChars.back().m_dwStatus = dwStatus;
-  if (dwStatus == CFX_BreakType::Piece)
-    return dwStatus;
-
-  m_iReadyLineIndex = m_pCurLine == &m_Lines[0] ? 0 : 1;
-  CFX_BreakLine* pNextLine = &m_Lines[1 - m_iReadyLineIndex];
-  bool bAllChars = m_iAlignment > CFX_TxtLineAlignment_Right;
-  if (!EndBreak_SplitLine(pNextLine, bAllChars)) {
-    std::deque<FX_TPO> tpos;
-    EndBreak_BidiLine(&tpos, dwStatus);
-    if (m_iAlignment > CFX_TxtLineAlignment_Left)
-      EndBreak_Alignment(tpos, bAllChars, dwStatus);
-  }
-
-  m_pCurLine = pNextLine;
-  CFX_Char* pTC = GetLastChar(0, false, false);
-  m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE::kUnknown;
-
-  return dwStatus;
-}
-
-int32_t CFX_TxtBreak::GetBreakPos(std::vector<CFX_Char>* pChars,
-                                  bool bAllChars,
-                                  bool bOnlyBrk,
-                                  int32_t* pEndPos) {
-  std::vector<CFX_Char>& chars = *pChars;
-  int32_t iLength = pdfium::CollectionSize<int32_t>(chars) - 1;
-  if (iLength < 1)
-    return iLength;
-
-  int32_t iBreak = -1;
-  int32_t iBreakPos = -1;
-  int32_t iIndirect = -1;
-  int32_t iIndirectPos = -1;
-  int32_t iLast = -1;
-  int32_t iLastPos = -1;
-  if (m_bSingleLine || *pEndPos <= m_iLineWidth) {
-    if (!bAllChars)
-      return iLength;
-
-    iBreak = iLength;
-    iBreakPos = *pEndPos;
-  }
-
-  FX_LINEBREAKTYPE eType;
-  FX_BREAKPROPERTY nCur;
-  FX_BREAKPROPERTY nNext;
-  CFX_Char* pCur = &chars[iLength--];
-  if (bAllChars)
-    pCur->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
-
-  nNext = FX_GetBreakProperty(pCur->char_code());
-  int32_t iCharWidth = pCur->m_iCharWidth;
-  if (iCharWidth > 0)
-    *pEndPos -= iCharWidth;
-
-  while (iLength >= 0) {
-    pCur = &chars[iLength];
-    nCur = FX_GetBreakProperty(pCur->char_code());
-    if (nNext == FX_BREAKPROPERTY::kSP)
-      eType = FX_LINEBREAKTYPE::kPROHIBITED_BRK;
-    else
-      eType = GetLineBreakTypeFromPair(nCur, nNext);
-    if (bAllChars)
-      pCur->m_eLineBreakType = eType;
-    if (!bOnlyBrk) {
-      if (m_bSingleLine || *pEndPos <= m_iLineWidth ||
-          nCur == FX_BREAKPROPERTY::kSP) {
-        if (eType == FX_LINEBREAKTYPE::kDIRECT_BRK && iBreak < 0) {
-          iBreak = iLength;
-          iBreakPos = *pEndPos;
-          if (!bAllChars)
-            return iLength;
-        } else if (eType == FX_LINEBREAKTYPE::kINDIRECT_BRK && iIndirect < 0) {
-          iIndirect = iLength;
-          iIndirectPos = *pEndPos;
-        }
-        if (iLast < 0) {
-          iLast = iLength;
-          iLastPos = *pEndPos;
-        }
-      }
-      iCharWidth = pCur->m_iCharWidth;
-      if (iCharWidth > 0)
-        *pEndPos -= iCharWidth;
-    }
-    nNext = nCur;
-    iLength--;
-  }
-  if (bOnlyBrk)
-    return 0;
-  if (iBreak > -1) {
-    *pEndPos = iBreakPos;
-    return iBreak;
-  }
-  if (iIndirect > -1) {
-    *pEndPos = iIndirectPos;
-    return iIndirect;
-  }
-  if (iLast > -1) {
-    *pEndPos = iLastPos;
-    return iLast;
-  }
-  return 0;
-}
-
-void CFX_TxtBreak::SplitTextLine(CFX_BreakLine* pCurLine,
-                                 CFX_BreakLine* pNextLine,
-                                 bool bAllChars) {
-  ASSERT(pCurLine);
-  ASSERT(pNextLine);
-
-  if (pCurLine->m_LineChars.size() < 2)
-    return;
-
-  int32_t iEndPos = pCurLine->m_iWidth;
-  std::vector<CFX_Char>& curChars = pCurLine->m_LineChars;
-  int32_t iCharPos = GetBreakPos(&curChars, bAllChars, false, &iEndPos);
-  if (iCharPos < 0)
-    iCharPos = 0;
-
-  iCharPos++;
-  if (iCharPos >= pdfium::CollectionSize<int32_t>(pCurLine->m_LineChars)) {
-    pNextLine->Clear();
-    CFX_Char* pTC = &curChars[iCharPos - 1];
-    pTC->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
-    return;
-  }
-
-  pNextLine->m_LineChars =
-      std::vector<CFX_Char>(curChars.begin() + iCharPos, curChars.end());
-  curChars.erase(curChars.begin() + iCharPos, curChars.end());
-  pCurLine->m_iWidth = iEndPos;
-  CFX_Char* pTC = &curChars[iCharPos - 1];
-  pTC->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
-  int32_t iWidth = 0;
-  for (size_t i = 0; i < pNextLine->m_LineChars.size(); ++i) {
-    if (pNextLine->m_LineChars[i].GetCharType() >= FX_CHARTYPE::kArabicAlef) {
-      pCurLine->DecrementArabicCharCount();
-      pNextLine->IncrementArabicCharCount();
-    }
-    iWidth += std::max(0, pNextLine->m_LineChars[i].m_iCharWidth);
-    pNextLine->m_LineChars[i].m_dwStatus = CFX_BreakType::None;
-  }
-  pNextLine->m_iWidth = iWidth;
-}
-
-struct FX_FORMCHAR {
-  uint16_t wch;
-  uint16_t wForm;
-  int32_t iWidth;
-};
-
-size_t CFX_TxtBreak::GetDisplayPos(const Run* pTxtRun,
-                                   TextCharPos* pCharPos) const {
-  if (!pTxtRun || pTxtRun->iLength < 1)
-    return 0;
-
-  Engine* pEngine = pTxtRun->pEdtEngine;
-  const wchar_t* pStr = pTxtRun->wsStr.c_str();
-  int32_t* pWidths = pTxtRun->pWidths;
-  int32_t iLength = pTxtRun->iLength - 1;
-  RetainPtr<CFGAS_GEFont> pFont = pTxtRun->pFont;
-  uint32_t dwStyles = pTxtRun->dwStyles;
-  CFX_RectF rtText(*pTxtRun->pRect);
-  bool bRTLPiece = (pTxtRun->dwCharStyles & FX_TXTCHARSTYLE_OddBidiLevel) != 0;
-  float fFontSize = pTxtRun->fFontSize;
-  int32_t iFontSize = FXSYS_roundf(fFontSize * 20.0f);
-  int32_t iAscent = pFont->GetAscent();
-  int32_t iDescent = pFont->GetDescent();
-  int32_t iMaxHeight = iAscent - iDescent;
-  float fFontHeight = fFontSize;
-  float fAscent = fFontHeight * iAscent / iMaxHeight;
-  float fX = rtText.left;
-  float fY;
-  float fCharWidth;
-  int32_t iHorScale = pTxtRun->iHorizontalScale;
-  int32_t iVerScale = pTxtRun->iVerticalScale;
-  bool bSkipSpace = pTxtRun->bSkipSpace;
-  FX_FORMCHAR formChars[3];
-  float fYBase;
-
-  if (bRTLPiece)
-    fX = rtText.right();
-
-  fYBase = rtText.top + (rtText.height - fFontSize) / 2.0f;
-  fY = fYBase + fAscent;
-
-  size_t szCount = 0;
-  int32_t iNext = 0;
-  wchar_t wPrev = 0xFEFF;
-  wchar_t wNext = 0xFEFF;
-  wchar_t wForm = 0xFEFF;
-  wchar_t wLast = 0xFEFF;
-  bool bShadda = false;
-  bool bLam = false;
-  for (int32_t i = 0; i <= iLength; i++) {
-    int32_t iAbsolute = i + pTxtRun->iStart;
-    int32_t iWidth;
-    wchar_t wch;
-    if (pEngine) {
-      wch = pEngine->GetChar(iAbsolute);
-      iWidth = pEngine->GetWidthOfChar(iAbsolute);
-    } else {
-      wch = *pStr++;
-      iWidth = *pWidths++;
-    }
-
-    FX_CHARTYPE chartype = FX_GetCharType(wch);
-    if (chartype == FX_CHARTYPE::kArabicAlef && iWidth == 0) {
-      wPrev = 0xFEFF;
-      wLast = wch;
-      continue;
-    }
-
-    if (chartype >= FX_CHARTYPE::kArabicAlef) {
-      if (i < iLength) {
-        if (pEngine) {
-          iNext = i + 1;
-          while (iNext <= iLength) {
-            int32_t iNextAbsolute = iNext + pTxtRun->iStart;
-            wNext = pEngine->GetChar(iNextAbsolute);
-            if (FX_GetCharType(wNext) != FX_CHARTYPE::kCombination)
-              break;
-
-            iNext++;
-          }
-          if (iNext > iLength)
-            wNext = 0xFEFF;
-        } else {
-          int32_t j = -1;
-          do {
-            j++;
-            if (i + j >= iLength)
-              break;
-
-            wNext = pStr[j];
-          } while (FX_GetCharType(wNext) == FX_CHARTYPE::kCombination);
-          if (i + j >= iLength)
-            wNext = 0xFEFF;
-        }
-      } else {
-        wNext = 0xFEFF;
-      }
-
-      wForm = pdfium::arabic::GetFormChar(wch, wPrev, wNext);
-      bLam = (wPrev == 0x0644 && wch == 0x0644 && wNext == 0x0647);
-    } else if (chartype == FX_CHARTYPE::kCombination) {
-      wForm = wch;
-      if (wch >= 0x064C && wch <= 0x0651) {
-        if (bShadda) {
-          wForm = 0xFEFF;
-          bShadda = false;
-        } else {
-          wNext = 0xFEFF;
-          if (pEngine) {
-            iNext = i + 1;
-            if (iNext <= iLength) {
-              int32_t iNextAbsolute = iNext + pTxtRun->iStart;
-              wNext = pEngine->GetChar(iNextAbsolute);
-            }
-          } else {
-            if (i < iLength)
-              wNext = *pStr;
-          }
-          if (wch == 0x0651) {
-            if (wNext >= 0x064C && wNext <= 0x0650) {
-              wForm = FX_GetArabicFromShaddaTable(wNext);
-              bShadda = true;
-            }
-          } else {
-            if (wNext == 0x0651) {
-              wForm = FX_GetArabicFromShaddaTable(wch);
-              bShadda = true;
-            }
-          }
-        }
-      } else {
-        bShadda = false;
-      }
-    } else if (chartype == FX_CHARTYPE::kNumeric) {
-      wForm = wch;
-    } else if (wch == L'.') {
-      wForm = wch;
-    } else if (wch == L',') {
-      wForm = wch;
-    } else if (bRTLPiece) {
-      wForm = FX_GetMirrorChar(wch);
-    } else {
-      wForm = wch;
-    }
-    if (chartype != FX_CHARTYPE::kCombination)
-      bShadda = false;
-    if (chartype < FX_CHARTYPE::kArabicAlef)
-      bLam = false;
-
-    bool bEmptyChar =
-        (chartype >= FX_CHARTYPE::kTab && chartype <= FX_CHARTYPE::kControl);
-    if (wForm == 0xFEFF)
-      bEmptyChar = true;
-
-    int32_t iForms = bLam ? 3 : 1;
-    szCount += (bEmptyChar && bSkipSpace) ? 0 : iForms;
-    if (!pCharPos) {
-      if (iWidth > 0)
-        wPrev = wch;
-      wLast = wch;
-      continue;
-    }
-
-    int32_t iCharWidth = iWidth;
-    if (iCharWidth < 0)
-      iCharWidth = -iCharWidth;
-
-    iCharWidth /= iFontSize;
-    formChars[0].wch = wch;
-    formChars[0].wForm = wForm;
-    formChars[0].iWidth = iCharWidth;
-    if (bLam) {
-      formChars[1].wForm = 0x0651;
-      iCharWidth = 0;
-      pFont->GetCharWidth(0x0651, &iCharWidth);
-      formChars[1].iWidth = iCharWidth;
-      formChars[2].wForm = 0x0670;
-      iCharWidth = 0;
-      pFont->GetCharWidth(0x0670, &iCharWidth);
-      formChars[2].iWidth = iCharWidth;
-    }
-
-    for (int32_t j = 0; j < iForms; j++) {
-      wForm = (wchar_t)formChars[j].wForm;
-      iCharWidth = formChars[j].iWidth;
-      if (j > 0) {
-        chartype = FX_CHARTYPE::kCombination;
-        wch = wForm;
-        wLast = (wchar_t)formChars[j - 1].wForm;
-      }
-      if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
-        pCharPos->m_GlyphIndex = pFont->GetGlyphIndex(wForm);
-#if defined(OS_MACOSX)
-        pCharPos->m_ExtGID = pCharPos->m_GlyphIndex;
-#endif
-        // TODO(npm): change widths in this method to unsigned to avoid implicit
-        // cast in the following line.
-        pCharPos->m_FontCharWidth = iCharWidth;
-      }
-
-      fCharWidth = fFontSize * iCharWidth / 1000.0f;
-      if (bRTLPiece && chartype != FX_CHARTYPE::kCombination)
-        fX -= fCharWidth;
-
-      if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
-        pCharPos->m_Origin = CFX_PointF(fX, fY);
-
-        if ((dwStyles & FX_LAYOUTSTYLE_CombText) != 0) {
-          int32_t iFormWidth = iCharWidth;
-          pFont->GetCharWidth(wForm, &iFormWidth);
-          float fOffset = fFontSize * (iCharWidth - iFormWidth) / 2000.0f;
-          pCharPos->m_Origin.x += fOffset;
-        }
-
-        if (chartype == FX_CHARTYPE::kCombination) {
-          FX_RECT rtBBox;
-          if (pFont->GetCharBBox(wForm, &rtBBox)) {
-            pCharPos->m_Origin.y =
-                fYBase + fFontSize - fFontSize * rtBBox.Height() / iMaxHeight;
-          }
-          if (wForm == wch && wLast != 0xFEFF) {
-            if (FX_GetCharType(wLast) == FX_CHARTYPE::kCombination) {
-              FX_RECT rtBox;
-              if (pFont->GetCharBBox(wLast, &rtBox))
-                pCharPos->m_Origin.y -= fFontSize * rtBox.Height() / iMaxHeight;
-            }
-          }
-        }
-      }
-      if (!bRTLPiece && chartype != FX_CHARTYPE::kCombination)
-        fX += fCharWidth;
-
-      if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
-        pCharPos->m_bGlyphAdjust = true;
-        pCharPos->m_AdjustMatrix[0] = -1;
-        pCharPos->m_AdjustMatrix[1] = 0;
-        pCharPos->m_AdjustMatrix[2] = 0;
-        pCharPos->m_AdjustMatrix[3] = 1;
-
-        if (iHorScale != 100 || iVerScale != 100) {
-          pCharPos->m_AdjustMatrix[0] =
-              pCharPos->m_AdjustMatrix[0] * iHorScale / 100.0f;
-          pCharPos->m_AdjustMatrix[1] =
-              pCharPos->m_AdjustMatrix[1] * iHorScale / 100.0f;
-          pCharPos->m_AdjustMatrix[2] =
-              pCharPos->m_AdjustMatrix[2] * iVerScale / 100.0f;
-          pCharPos->m_AdjustMatrix[3] =
-              pCharPos->m_AdjustMatrix[3] * iVerScale / 100.0f;
-        }
-        pCharPos++;
-      }
-    }
-    if (iWidth > 0)
-      wPrev = static_cast<wchar_t>(formChars[0].wch);
-    wLast = wch;
-  }
-  return szCount;
-}
-
-std::vector<CFX_RectF> CFX_TxtBreak::GetCharRects(const Run* pTxtRun,
-                                                  bool bCharBBox) const {
-  if (!pTxtRun || pTxtRun->iLength < 1)
-    return std::vector<CFX_RectF>();
-
-  Engine* pEngine = pTxtRun->pEdtEngine;
-  const wchar_t* pStr = pTxtRun->wsStr.c_str();
-  int32_t* pWidths = pTxtRun->pWidths;
-  int32_t iLength = pTxtRun->iLength;
-  CFX_RectF rect(*pTxtRun->pRect);
-  float fFontSize = pTxtRun->fFontSize;
-  float fScale = fFontSize / 1000.0f;
-  RetainPtr<CFGAS_GEFont> pFont = pTxtRun->pFont;
-  if (!pFont)
-    bCharBBox = false;
-
-  FX_RECT bbox;
-  if (bCharBBox)
-    bCharBBox = pFont->GetBBox(&bbox);
-
-  float fLeft = std::max(0.0f, bbox.left * fScale);
-  float fHeight = fabs(bbox.Height() * fScale);
-  bool bRTLPiece = !!(pTxtRun->dwCharStyles & FX_TXTCHARSTYLE_OddBidiLevel);
-  bool bSingleLine = !!(pTxtRun->dwStyles & FX_LAYOUTSTYLE_SingleLine);
-  bool bCombText = !!(pTxtRun->dwStyles & FX_LAYOUTSTYLE_CombText);
-  wchar_t wch;
-  int32_t iCharSize;
-  float fCharSize;
-  float fStart = bRTLPiece ? rect.right() : rect.left;
-
-  std::vector<CFX_RectF> rtArray(iLength);
-  for (int32_t i = 0; i < iLength; i++) {
-    int32_t iAbsolute = i + pTxtRun->iStart;
-    if (pEngine) {
-      wch = pEngine->GetChar(iAbsolute);
-      iCharSize = pEngine->GetWidthOfChar(iAbsolute);
-    } else {
-      wch = *pStr++;
-      iCharSize = *pWidths++;
-    }
-    fCharSize = static_cast<float>(iCharSize) / kConversionFactor;
-    bool bRet = (!bSingleLine && IsCtrlCode(wch));
-    if (!(wch == L'\v' || wch == L'\f' || wch == 0x2028 || wch == 0x2029 ||
-          wch == L'\n')) {
-      bRet = false;
-    }
-    if (bRet)
-      fCharSize = fFontSize / 2.0f;
-    rect.left = fStart;
-    if (bRTLPiece) {
-      rect.left -= fCharSize;
-      fStart -= fCharSize;
-    } else {
-      fStart += fCharSize;
-    }
-    rect.width = fCharSize;
-
-    if (bCharBBox && !bRet) {
-      int32_t iCharWidth = 1000;
-      pFont->GetCharWidth(wch, &iCharWidth);
-      float fRTLeft = 0, fCharWidth = 0;
-      if (iCharWidth > 0) {
-        fCharWidth = iCharWidth * fScale;
-        fRTLeft = fLeft;
-        if (bCombText)
-          fRTLeft = (rect.width - fCharWidth) / 2.0f;
-      }
-      CFX_RectF rtBBoxF;
-      rtBBoxF.left = rect.left + fRTLeft;
-      rtBBoxF.top = rect.top + (rect.height - fHeight) / 2.0f;
-      rtBBoxF.width = fCharWidth;
-      rtBBoxF.height = fHeight;
-      rtBBoxF.top = std::max(rtBBoxF.top, 0.0f);
-      rtArray[i] = rtBBoxF;
-      continue;
-    }
-    rtArray[i] = rect;
-  }
-  return rtArray;
-}
-
-CFX_TxtBreak::Engine::~Engine() = default;
-
-CFX_TxtBreak::Run::Run() = default;
-
-CFX_TxtBreak::Run::~Run() = default;
-
-CFX_TxtBreak::Run::Run(const CFX_TxtBreak::Run& other) = default;
diff --git a/xfa/fgas/layout/cfx_txtbreak.h b/xfa/fgas/layout/cfx_txtbreak.h
deleted file mode 100644
index 7f3d4ac..0000000
--- a/xfa/fgas/layout/cfx_txtbreak.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FGAS_LAYOUT_CFX_TXTBREAK_H_
-#define XFA_FGAS_LAYOUT_CFX_TXTBREAK_H_
-
-#include <deque>
-#include <vector>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "xfa/fgas/layout/cfx_break.h"
-#include "xfa/fgas/layout/cfx_char.h"
-
-class CFGAS_GEFont;
-class TextCharPos;
-
-#define FX_TXTCHARSTYLE_ArabicShadda 0x0020
-#define FX_TXTCHARSTYLE_OddBidiLevel 0x0040
-
-enum CFX_TxtLineAlignment {
-  CFX_TxtLineAlignment_Left = 0,
-  CFX_TxtLineAlignment_Center = 1 << 0,
-  CFX_TxtLineAlignment_Right = 1 << 1,
-  CFX_TxtLineAlignment_Justified = 1 << 2
-};
-
-inline bool CFX_BreakTypeNoneOrPiece(CFX_BreakType type) {
-  return type == CFX_BreakType::None || type == CFX_BreakType::Piece;
-}
-
-class CFX_TxtBreak final : public CFX_Break {
- public:
-  class Engine {
-   public:
-    virtual ~Engine();
-    virtual wchar_t GetChar(size_t idx) const = 0;
-    // Non-const so we can force a layout if needed.
-    virtual size_t GetWidthOfChar(size_t idx) = 0;
-  };
-
-  struct Run {
-    Run();
-    Run(const Run& other);
-    ~Run();
-
-    CFX_TxtBreak::Engine* pEdtEngine = nullptr;
-    WideString wsStr;
-    int32_t* pWidths = nullptr;
-    int32_t iStart = 0;
-    int32_t iLength = 0;
-    RetainPtr<CFGAS_GEFont> pFont;
-    float fFontSize = 12.0f;
-    uint32_t dwStyles = 0;
-    int32_t iHorizontalScale = 100;
-    int32_t iVerticalScale = 100;
-    uint32_t dwCharStyles = 0;
-    const CFX_RectF* pRect = nullptr;
-    bool bSkipSpace = true;
-  };
-
-  CFX_TxtBreak();
-  ~CFX_TxtBreak() override;
-
-  void SetLineWidth(float fLineWidth);
-  void SetAlignment(int32_t iAlignment);
-  void SetCombWidth(float fCombWidth);
-  CFX_BreakType EndBreak(CFX_BreakType dwStatus);
-
-  size_t GetDisplayPos(const Run* pTxtRun, TextCharPos* pCharPos) const;
-  std::vector<CFX_RectF> GetCharRects(const Run* pTxtRun, bool bCharBBox) const;
-  CFX_BreakType AppendChar(wchar_t wch);
-
- private:
-  void AppendChar_Combination(CFX_Char* pCurChar);
-  void AppendChar_Tab(CFX_Char* pCurChar);
-  CFX_BreakType AppendChar_Control(CFX_Char* pCurChar);
-  CFX_BreakType AppendChar_Arabic(CFX_Char* pCurChar);
-  CFX_BreakType AppendChar_Others(CFX_Char* pCurChar);
-
-  void ResetContextCharStyles();
-  bool EndBreak_SplitLine(CFX_BreakLine* pNextLine, bool bAllChars);
-  void EndBreak_BidiLine(std::deque<FX_TPO>* tpos, CFX_BreakType dwStatus);
-  void EndBreak_Alignment(const std::deque<FX_TPO>& tpos,
-                          bool bAllChars,
-                          CFX_BreakType dwStatus);
-  int32_t GetBreakPos(std::vector<CFX_Char>* pChars,
-                      bool bAllChars,
-                      bool bOnlyBrk,
-                      int32_t* pEndPos);
-  void SplitTextLine(CFX_BreakLine* pCurLine,
-                     CFX_BreakLine* pNextLine,
-                     bool bAllChars);
-
-  int32_t m_iAlignment;
-  int32_t m_iCombWidth;
-};
-
-#endif  // XFA_FGAS_LAYOUT_CFX_TXTBREAK_H_
diff --git a/xfa/fgas/layout/cfx_txtbreak_unittest.cpp b/xfa/fgas/layout/cfx_txtbreak_unittest.cpp
deleted file mode 100644
index a3ae207..0000000
--- a/xfa/fgas/layout/cfx_txtbreak_unittest.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "xfa/fgas/layout/cfx_txtbreak.h"
-
-#include <memory>
-#include <utility>
-
-#include "core/fxge/cfx_font.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/xfa_unit_test_support.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fgas/font/cfgas_fontmgr.h"
-#include "xfa/fgas/font/cfgas_gefont.h"
-#include "xfa/fgas/layout/cfx_char.h"
-
-class CFX_TxtBreakTest : public testing::Test {
- public:
-  void SetUp() override {
-    font_ =
-        CFGAS_GEFont::LoadFont(L"Arial Black", 0, 0, GetGlobalFontManager());
-    ASSERT_TRUE(font_.Get());
-  }
-
-  std::unique_ptr<CFX_TxtBreak> CreateBreak() {
-    auto txt_break = pdfium::MakeUnique<CFX_TxtBreak>();
-    txt_break->SetFont(font_);
-    return txt_break;
-  }
-
- private:
-  RetainPtr<CFGAS_GEFont> font_;
-};
-
-TEST_F(CFX_TxtBreakTest, BidiLine) {
-  auto txt_break = CreateBreak();
-  txt_break->SetLineBreakTolerance(1);
-  txt_break->SetFontSize(12);
-
-  WideString input = WideString::FromUTF8(ByteStringView("\xa\x0\xa\xa", 4));
-  for (wchar_t ch : input)
-    txt_break->AppendChar(ch);
-
-  std::vector<CFX_Char> chars =
-      txt_break->GetCurrentLineForTesting()->m_LineChars;
-  CFX_Char::BidiLine(&chars, chars.size());
-  EXPECT_EQ(3u, chars.size());
-}
diff --git a/xfa/fgas/layout/fgas_arabic.cpp b/xfa/fgas/layout/fgas_arabic.cpp
new file mode 100644
index 0000000..434f195
--- /dev/null
+++ b/xfa/fgas/layout/fgas_arabic.cpp
@@ -0,0 +1,224 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/layout/fgas_arabic.h"
+
+#include <iterator>
+
+#include "core/fxcrt/fx_unicode.h"
+#include "xfa/fgas/layout/cfgas_char.h"
+
+namespace {
+
+struct FX_ARBFORMTABLE {
+  uint16_t wIsolated;
+  uint16_t wFinal;
+  uint16_t wInitial;
+  uint16_t wMedial;
+};
+
+struct FX_ARAALEF {
+  uint16_t wAlef;
+  uint16_t wIsolated;
+};
+
+constexpr FX_ARBFORMTABLE kFormTable[] = {
+    {0xFE81, 0xFE82, 0xFE81, 0xFE82}, {0xFE83, 0xFE84, 0xFE83, 0xFE84},
+    {0xFE85, 0xFE86, 0xFE85, 0xFE86}, {0xFE87, 0xFE88, 0xFE87, 0xFE88},
+    {0xFE89, 0xFE8A, 0xFE8B, 0xFE8C}, {0xFE8D, 0xFE8E, 0xFE8D, 0xFE8E},
+    {0xFE8F, 0xFE90, 0xFE91, 0xFE92}, {0xFE93, 0xFE94, 0xFE93, 0xFE94},
+    {0xFE95, 0xFE96, 0xFE97, 0xFE98}, {0xFE99, 0xFE9A, 0xFE9B, 0xFE9C},
+    {0xFE9D, 0xFE9E, 0xFE9F, 0xFEA0}, {0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4},
+    {0xFEA5, 0xFEA6, 0xFEA7, 0xFEA8}, {0xFEA9, 0xFEAA, 0xFEA9, 0xFEAA},
+    {0xFEAB, 0xFEAC, 0xFEAB, 0xFEAC}, {0xFEAD, 0xFEAE, 0xFEAD, 0xFEAE},
+    {0xFEAF, 0xFEB0, 0xFEAF, 0xFEB0}, {0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4},
+    {0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8}, {0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC},
+    {0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0}, {0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4},
+    {0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8}, {0xFEC9, 0xFECA, 0xFECB, 0xFECC},
+    {0xFECD, 0xFECE, 0xFECF, 0xFED0}, {0x063B, 0x063B, 0x063B, 0x063B},
+    {0x063C, 0x063C, 0x063C, 0x063C}, {0x063D, 0x063D, 0x063D, 0x063D},
+    {0x063E, 0x063E, 0x063E, 0x063E}, {0x063F, 0x063F, 0x063F, 0x063F},
+    {0x0640, 0x0640, 0x0640, 0x0640}, {0xFED1, 0xFED2, 0xFED3, 0xFED4},
+    {0xFED5, 0xFED6, 0xFED7, 0xFED8}, {0xFED9, 0xFEDA, 0xFEDB, 0xFEDC},
+    {0xFEDD, 0xFEDE, 0xFEDF, 0xFEE0}, {0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4},
+    {0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8}, {0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC},
+    {0xFEED, 0xFEEE, 0xFEED, 0xFEEE}, {0xFEEF, 0xFEF0, 0xFBFE, 0xFBFF},
+    {0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4}, {0x064B, 0x064B, 0x064B, 0x064B},
+    {0x064C, 0x064C, 0x064C, 0x064C}, {0x064D, 0x064D, 0x064D, 0x064D},
+    {0x064E, 0x064E, 0x064E, 0x064E}, {0x064F, 0x064F, 0x064F, 0x064F},
+    {0x0650, 0x0650, 0x0650, 0x0650}, {0x0651, 0x0651, 0x0651, 0x0651},
+    {0x0652, 0x0652, 0x0652, 0x0652}, {0x0653, 0x0653, 0x0653, 0x0653},
+    {0x0654, 0x0654, 0x0654, 0x0654}, {0x0655, 0x0655, 0x0655, 0x0655},
+    {0x0656, 0x0656, 0x0656, 0x0656}, {0x0657, 0x0657, 0x0657, 0x0657},
+    {0x0658, 0x0658, 0x0658, 0x0658}, {0x0659, 0x0659, 0x0659, 0x0659},
+    {0x065A, 0x065A, 0x065A, 0x065A}, {0x065B, 0x065B, 0x065B, 0x065B},
+    {0x065C, 0x065C, 0x065C, 0x065C}, {0x065D, 0x065D, 0x065D, 0x065D},
+    {0x065E, 0x065E, 0x065E, 0x065E}, {0x065F, 0x065F, 0x065F, 0x065F},
+    {0x0660, 0x0660, 0x0660, 0x0660}, {0x0661, 0x0661, 0x0661, 0x0661},
+    {0x0662, 0x0662, 0x0662, 0x0662}, {0x0663, 0x0663, 0x0663, 0x0663},
+    {0x0664, 0x0664, 0x0664, 0x0664}, {0x0665, 0x0665, 0x0665, 0x0665},
+    {0x0666, 0x0666, 0x0666, 0x0666}, {0x0667, 0x0667, 0x0667, 0x0667},
+    {0x0668, 0x0668, 0x0668, 0x0668}, {0x0669, 0x0669, 0x0669, 0x0669},
+    {0x066A, 0x066A, 0x066A, 0x066A}, {0x066B, 0x066B, 0x066B, 0x066B},
+    {0x066C, 0x066C, 0x066C, 0x066C}, {0x066D, 0x066D, 0x066D, 0x066D},
+    {0x066E, 0x066E, 0x066E, 0x066E}, {0x066F, 0x066F, 0x066F, 0x066F},
+    {0x0670, 0x0670, 0x0670, 0x0670}, {0xFB50, 0xFB51, 0xFB50, 0xFB51},
+    {0x0672, 0x0672, 0x0672, 0x0672}, {0x0673, 0x0673, 0x0673, 0x0673},
+    {0x0674, 0x0674, 0x0674, 0x0674}, {0x0675, 0x0675, 0x0675, 0x0675},
+    {0x0676, 0x0676, 0x0676, 0x0676}, {0x0677, 0x0677, 0x0677, 0x0677},
+    {0x0678, 0x0678, 0x0678, 0x0678}, {0xFB66, 0xFB67, 0xFB68, 0xFB69},
+    {0xFB5E, 0xFB5F, 0xFB60, 0xFB61}, {0xFB52, 0xFB53, 0xFB54, 0xFB55},
+    {0x067C, 0x067C, 0x067C, 0x067C}, {0x067D, 0x067D, 0x067D, 0x067D},
+    {0xFB56, 0xFB57, 0xFB58, 0xFB59}, {0xFB62, 0xFB63, 0xFB64, 0xFB65},
+    {0xFB5A, 0xFB5B, 0xFB5C, 0xFB5D}, {0x0681, 0x0681, 0x0681, 0x0681},
+    {0x0682, 0x0682, 0x0682, 0x0682}, {0xFB76, 0xFB77, 0xFB78, 0xFB79},
+    {0xFB72, 0xFB73, 0xFB74, 0xFB75}, {0x0685, 0x0685, 0x0685, 0x0685},
+    {0xFB7A, 0xFB7B, 0xFB7C, 0xFB7D}, {0xFB7E, 0xFB7F, 0xFB80, 0xFB81},
+    {0xFB88, 0xFB89, 0xFB88, 0xFB89}, {0x0689, 0x0689, 0x0689, 0x0689},
+    {0x068A, 0x068A, 0x068A, 0x068A}, {0x068B, 0x068B, 0x068B, 0x068B},
+    {0xFB84, 0xFB85, 0xFB84, 0xFB85}, {0xFB82, 0xFB83, 0xFB82, 0xFB83},
+    {0xFB86, 0xFB87, 0xFB86, 0xFB87}, {0x068F, 0x068F, 0x068F, 0x068F},
+    {0x0690, 0x0690, 0x0690, 0x0690}, {0xFB8C, 0xFB8D, 0xFB8C, 0xFB8D},
+    {0x0692, 0x0692, 0x0692, 0x0692}, {0x0693, 0x0693, 0x0693, 0x0693},
+    {0x0694, 0x0694, 0x0694, 0x0694}, {0x0695, 0x0695, 0x0695, 0x0695},
+    {0x0696, 0x0696, 0x0696, 0x0696}, {0x0697, 0x0697, 0x0697, 0x0697},
+    {0xFB8A, 0xFB8B, 0xFB8A, 0xFB8B}, {0x0699, 0x0699, 0x0699, 0x0699},
+    {0x069A, 0x069A, 0x069A, 0x069A}, {0x069B, 0x069B, 0x069B, 0x069B},
+    {0x069C, 0x069C, 0x069C, 0x069C}, {0x069D, 0x069D, 0x069D, 0x069D},
+    {0x069E, 0x069E, 0x069E, 0x069E}, {0x069F, 0x069F, 0x069F, 0x069F},
+    {0x06A0, 0x06A0, 0x06A0, 0x06A0}, {0x06A1, 0x06A1, 0x06A1, 0x06A1},
+    {0x06A2, 0x06A2, 0x06A2, 0x06A2}, {0x06A3, 0x06A3, 0x06A3, 0x06A3},
+    {0xFB6A, 0xFB6B, 0xFB6C, 0xFB6D}, {0x06A5, 0x06A5, 0x06A5, 0x06A5},
+    {0xFB6E, 0xFB6F, 0xFB70, 0xFB71}, {0x06A7, 0x06A7, 0x06A7, 0x06A7},
+    {0x06A8, 0x06A8, 0x06A8, 0x06A8}, {0xFB8E, 0xFB8F, 0xFB90, 0xFB91},
+    {0x06AA, 0x06AA, 0x06AA, 0x06AA}, {0x06AB, 0x06AB, 0x06AB, 0x06AB},
+    {0x06AC, 0x06AC, 0x06AC, 0x06AC}, {0xFBD3, 0xFBD4, 0xFBD5, 0xFBD6},
+    {0x06AE, 0x06AE, 0x06AE, 0x06AE}, {0xFB92, 0xFB93, 0xFB94, 0xFB95},
+    {0x06B0, 0x06B0, 0x06B0, 0x06B0}, {0xFB9A, 0xFB9B, 0xFB9C, 0xFB9D},
+    {0x06B2, 0x06B2, 0x06B2, 0x06B2}, {0xFB96, 0xFB97, 0xFB98, 0xFB99},
+    {0x06B4, 0x06B4, 0x06B4, 0x06B4}, {0x06B5, 0x06B5, 0x06B5, 0x06B5},
+    {0x06B6, 0x06B6, 0x06B6, 0x06B6}, {0x06B7, 0x06B7, 0x06B7, 0x06B7},
+    {0x06B8, 0x06B8, 0x06B8, 0x06B8}, {0x06B9, 0x06B9, 0x06B9, 0x06B9},
+    {0xFB9E, 0xFB9F, 0xFBE8, 0xFBE9}, {0xFBA0, 0xFBA1, 0xFBA2, 0xFBA3},
+    {0x06BC, 0x06BC, 0x06BC, 0x06BC}, {0x06BD, 0x06BD, 0x06BD, 0x06BD},
+    {0xFBAA, 0xFBAB, 0xFBAC, 0xFBAD}, {0x06BF, 0x06BF, 0x06BF, 0x06BF},
+    {0xFBA4, 0xFBA5, 0xFBA4, 0xFBA5}, {0xFBA6, 0xFBA7, 0xFBA8, 0xFBA9},
+    {0x06C2, 0x06C2, 0x06C2, 0x06C2}, {0x06C3, 0x06C3, 0x06C3, 0x06C3},
+    {0x06C4, 0x06C4, 0x06C4, 0x06C4}, {0xFBE0, 0xFBE1, 0xFBE0, 0xFBE1},
+    {0xFBD9, 0xFBDA, 0xFBD9, 0xFBDA}, {0xFBD7, 0xFBD8, 0xFBD7, 0xFBD8},
+    {0xFBDB, 0xFBDC, 0xFBDB, 0xFBDC}, {0xFBE2, 0xFBE3, 0xFBE2, 0xFBE3},
+    {0x06CA, 0x06CA, 0x06CA, 0x06CA}, {0xFBDE, 0xFBDF, 0xFBDE, 0xFBDF},
+    {0xFBFC, 0xFBFD, 0xFBFE, 0xFBFF}, {0x06CD, 0x06CD, 0x06CD, 0x06CD},
+    {0x06CE, 0x06CE, 0x06CE, 0x06CE}, {0x06CF, 0x06CF, 0x06CF, 0x06CF},
+    {0xFBE4, 0xFBE5, 0xFBE6, 0xFBE7}, {0x06D1, 0x06D1, 0x06D1, 0x06D1},
+    {0xFBAE, 0xFBAF, 0xFBAE, 0xFBAF}, {0xFBB0, 0xFBB1, 0xFBB0, 0xFBB1},
+    {0x06D4, 0x06D4, 0x06D4, 0x06D4}, {0x06D5, 0x06D5, 0x06D5, 0x06D5},
+};
+constexpr uint16_t kFirstFormTableEntry = 0x0622;
+constexpr uint16_t kLastFormTableEntry =
+    kFirstFormTableEntry + std::size(kFormTable) - 1;
+
+constexpr FX_ARAALEF kAlefTable[] = {
+    {0x0622, 0xFEF5},
+    {0x0623, 0xFEF7},
+    {0x0625, 0xFEF9},
+    {0x0627, 0xFEFB},
+};
+
+constexpr uint16_t kShaddaTable[] = {0xFC5E, 0xFC5F, 0xFC60, 0xFC61, 0xFC62};
+constexpr uint16_t kFirstShaddaTableEntry = 0x064c;
+constexpr uint16_t kLastShaddaTableEntry =
+    kFirstShaddaTableEntry + std::size(kShaddaTable) - 1;
+
+const FX_ARBFORMTABLE* GetArabicFormTable(wchar_t unicode) {
+  if (unicode < kFirstFormTableEntry || unicode > kLastFormTableEntry)
+    return nullptr;
+
+  return &kFormTable[unicode - kFirstFormTableEntry];
+}
+
+const FX_ARBFORMTABLE* ParseChar(const CFGAS_Char* pTC,
+                                 wchar_t* wChar,
+                                 FX_CHARTYPE* eType) {
+  if (!pTC) {
+    *eType = FX_CHARTYPE::kUnknown;
+    *wChar = pdfium::unicode::kZeroWidthNoBreakSpace;
+    return nullptr;
+  }
+
+  *eType = pTC->GetCharType();
+  *wChar = static_cast<wchar_t>(pTC->char_code());
+  const FX_ARBFORMTABLE* pFT = GetArabicFormTable(*wChar);
+  if (!pFT || *eType >= FX_CHARTYPE::kArabicNormal)
+    *eType = FX_CHARTYPE::kUnknown;
+
+  return pFT;
+}
+
+wchar_t GetArabicFromAlefTable(wchar_t alef) {
+  for (const FX_ARAALEF& v : kAlefTable) {
+    if (v.wAlef == alef)
+      return v.wIsolated;
+  }
+  return alef;
+}
+
+}  // namespace
+
+namespace pdfium {
+namespace arabic {
+
+wchar_t GetFormChar(wchar_t wch, wchar_t prev, wchar_t next) {
+  CFGAS_Char c(wch);
+  CFGAS_Char p(prev);
+  CFGAS_Char n(next);
+  return GetFormChar(&c, &p, &n);
+}
+
+wchar_t GetFormChar(const CFGAS_Char* cur,
+                    const CFGAS_Char* prev,
+                    const CFGAS_Char* next) {
+  FX_CHARTYPE eCur;
+  wchar_t wCur;
+  const FX_ARBFORMTABLE* ft = ParseChar(cur, &wCur, &eCur);
+  if (eCur < FX_CHARTYPE::kArabicAlef || eCur >= FX_CHARTYPE::kArabicNormal)
+    return wCur;
+
+  FX_CHARTYPE ePrev;
+  wchar_t wPrev;
+  ParseChar(prev, &wPrev, &ePrev);
+  if (wPrev == kArabicLetterLam && eCur == FX_CHARTYPE::kArabicAlef)
+    return pdfium::unicode::kZeroWidthNoBreakSpace;
+
+  FX_CHARTYPE eNext;
+  wchar_t wNext;
+  ParseChar(next, &wNext, &eNext);
+  bool bAlef = (eNext == FX_CHARTYPE::kArabicAlef && wCur == kArabicLetterLam);
+  if (ePrev < FX_CHARTYPE::kArabicAlef) {
+    if (bAlef)
+      return GetArabicFromAlefTable(wNext);
+    return (eNext < FX_CHARTYPE::kArabicAlef) ? ft->wIsolated : ft->wInitial;
+  }
+
+  if (bAlef) {
+    wCur = GetArabicFromAlefTable(wNext);
+    return (ePrev != FX_CHARTYPE::kArabicDistortion) ? wCur : ++wCur;
+  }
+
+  if (ePrev == FX_CHARTYPE::kArabicAlef || ePrev == FX_CHARTYPE::kArabicSpecial)
+    return (eNext < FX_CHARTYPE::kArabicAlef) ? ft->wIsolated : ft->wInitial;
+  return (eNext < FX_CHARTYPE::kArabicAlef) ? ft->wFinal : ft->wMedial;
+}
+
+absl::optional<wchar_t> GetArabicFromShaddaTable(wchar_t shadda) {
+  if (shadda < kFirstShaddaTableEntry || shadda > kLastShaddaTableEntry)
+    return absl::nullopt;
+
+  return kShaddaTable[shadda - kFirstShaddaTableEntry];
+}
+
+}  // namespace arabic
+}  // namespace pdfium
diff --git a/xfa/fgas/layout/fgas_arabic.h b/xfa/fgas/layout/fgas_arabic.h
new file mode 100644
index 0000000..6b5ba66
--- /dev/null
+++ b/xfa/fgas/layout/fgas_arabic.h
@@ -0,0 +1,31 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_LAYOUT_FGAS_ARABIC_H_
+#define XFA_FGAS_LAYOUT_FGAS_ARABIC_H_
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+class CFGAS_Char;
+
+namespace pdfium {
+namespace arabic {
+
+constexpr wchar_t kArabicLetterLam = 0x0644;
+constexpr wchar_t kArabicLetterHeh = 0x0647;
+constexpr wchar_t kArabicShadda = 0x0651;
+constexpr wchar_t kArabicLetterSuperscriptAlef = 0x0670;
+
+wchar_t GetFormChar(wchar_t wch, wchar_t prev, wchar_t next);
+wchar_t GetFormChar(const CFGAS_Char* cur,
+                    const CFGAS_Char* prev,
+                    const CFGAS_Char* next);
+absl::optional<wchar_t> GetArabicFromShaddaTable(wchar_t shadda);
+
+}  // namespace arabic
+}  // namespace pdfium
+
+#endif  // XFA_FGAS_LAYOUT_FGAS_ARABIC_H_
diff --git a/xfa/fgas/layout/fgas_linebreak.cpp b/xfa/fgas/layout/fgas_linebreak.cpp
new file mode 100644
index 0000000..b604468
--- /dev/null
+++ b/xfa/fgas/layout/fgas_linebreak.cpp
@@ -0,0 +1,234 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/layout/fgas_linebreak.h"
+
+#include <iterator>
+
+#include "core/fxcrt/fx_unicode.h"
+#include "third_party/base/check.h"
+
+namespace {
+
+#define FX_LBUN FX_LINEBREAKTYPE::kUNKNOWN
+#define FX_LBDB FX_LINEBREAKTYPE::kDIRECT_BRK
+#define FX_LBIB FX_LINEBREAKTYPE::kINDIRECT_BRK
+#define FX_LBCB FX_LINEBREAKTYPE::kCOM_INDIRECT_BRK
+#define FX_LBCP FX_LINEBREAKTYPE::kCOM_PROHIBITED_BRK
+#define FX_LBPB FX_LINEBREAKTYPE::kPROHIBITED_BRK
+#define FX_LBHS FX_LINEBREAKTYPE::kHANGUL_SPACE_BRK
+
+const FX_LINEBREAKTYPE kFX_LineBreak_PairTable[38][38] = {
+    {FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBPB, FX_LBPB, FX_LBPB, FX_LBCP, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBPB, FX_LBPB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBIB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBPB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBPB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBDB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBDB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBPB, FX_LBPB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBPB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+};
+
+#undef FX_LBUN
+#undef FX_LBDB
+#undef FX_LBIB
+#undef FX_LBCB
+#undef FX_LBCP
+#undef FX_LBPB
+#undef FX_LBHS
+
+}  // namespace
+
+FX_LINEBREAKTYPE GetLineBreakTypeFromPair(FX_BREAKPROPERTY curr_char,
+                                          FX_BREAKPROPERTY next_char) {
+  size_t row = static_cast<size_t>(curr_char);
+  size_t col = static_cast<size_t>(next_char);
+  DCHECK(row < std::size(kFX_LineBreak_PairTable));
+  DCHECK(col < std::size(kFX_LineBreak_PairTable[0]));
+  return kFX_LineBreak_PairTable[row][col];
+}
diff --git a/xfa/fgas/layout/fgas_linebreak.h b/xfa/fgas/layout/fgas_linebreak.h
new file mode 100644
index 0000000..84f56df
--- /dev/null
+++ b/xfa/fgas/layout/fgas_linebreak.h
@@ -0,0 +1,27 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_LAYOUT_FGAS_LINEBREAK_H_
+#define XFA_FGAS_LAYOUT_FGAS_LINEBREAK_H_
+
+#include <stdint.h>
+
+#include "core/fxcrt/fx_unicode.h"
+
+enum class FX_LINEBREAKTYPE : uint8_t {
+  kUNKNOWN = 0x00,
+  kDIRECT_BRK = 0x1A,
+  kINDIRECT_BRK = 0x2B,
+  kCOM_INDIRECT_BRK = 0x3C,
+  kCOM_PROHIBITED_BRK = 0x4D,
+  kPROHIBITED_BRK = 0x5E,
+  kHANGUL_SPACE_BRK = 0x6F,
+};
+
+FX_LINEBREAKTYPE GetLineBreakTypeFromPair(FX_BREAKPROPERTY curr_char,
+                                          FX_BREAKPROPERTY next_char);
+
+#endif  // XFA_FGAS_LAYOUT_FGAS_LINEBREAK_H_
diff --git a/xfa/fgas/layout/fx_arabic.cpp b/xfa/fgas/layout/fx_arabic.cpp
deleted file mode 100644
index 886bb0d..0000000
--- a/xfa/fgas/layout/fx_arabic.cpp
+++ /dev/null
@@ -1,231 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fgas/layout/fx_arabic.h"
-
-#include <algorithm>
-#include <vector>
-
-#include "core/fxcrt/fx_memory.h"
-#include "core/fxcrt/fx_unicode.h"
-
-namespace {
-
-struct FX_ARBFORMTABLE {
-  uint16_t wIsolated;
-  uint16_t wFinal;
-  uint16_t wInitial;
-  uint16_t wMedial;
-};
-
-struct FX_ARAALEF {
-  uint16_t wAlef;
-  uint16_t wIsolated;
-};
-
-struct FX_ARASHADDA {
-  uint16_t wShadda;
-  uint16_t wIsolated;
-};
-
-const FX_ARBFORMTABLE g_FX_ArabicFormTables[] = {
-    {0xFE81, 0xFE82, 0xFE81, 0xFE82}, {0xFE83, 0xFE84, 0xFE83, 0xFE84},
-    {0xFE85, 0xFE86, 0xFE85, 0xFE86}, {0xFE87, 0xFE88, 0xFE87, 0xFE88},
-    {0xFE89, 0xFE8A, 0xFE8B, 0xFE8C}, {0xFE8D, 0xFE8E, 0xFE8D, 0xFE8E},
-    {0xFE8F, 0xFE90, 0xFE91, 0xFE92}, {0xFE93, 0xFE94, 0xFE93, 0xFE94},
-    {0xFE95, 0xFE96, 0xFE97, 0xFE98}, {0xFE99, 0xFE9A, 0xFE9B, 0xFE9C},
-    {0xFE9D, 0xFE9E, 0xFE9F, 0xFEA0}, {0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4},
-    {0xFEA5, 0xFEA6, 0xFEA7, 0xFEA8}, {0xFEA9, 0xFEAA, 0xFEA9, 0xFEAA},
-    {0xFEAB, 0xFEAC, 0xFEAB, 0xFEAC}, {0xFEAD, 0xFEAE, 0xFEAD, 0xFEAE},
-    {0xFEAF, 0xFEB0, 0xFEAF, 0xFEB0}, {0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4},
-    {0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8}, {0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC},
-    {0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0}, {0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4},
-    {0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8}, {0xFEC9, 0xFECA, 0xFECB, 0xFECC},
-    {0xFECD, 0xFECE, 0xFECF, 0xFED0}, {0x063B, 0x063B, 0x063B, 0x063B},
-    {0x063C, 0x063C, 0x063C, 0x063C}, {0x063D, 0x063D, 0x063D, 0x063D},
-    {0x063E, 0x063E, 0x063E, 0x063E}, {0x063F, 0x063F, 0x063F, 0x063F},
-    {0x0640, 0x0640, 0x0640, 0x0640}, {0xFED1, 0xFED2, 0xFED3, 0xFED4},
-    {0xFED5, 0xFED6, 0xFED7, 0xFED8}, {0xFED9, 0xFEDA, 0xFEDB, 0xFEDC},
-    {0xFEDD, 0xFEDE, 0xFEDF, 0xFEE0}, {0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4},
-    {0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8}, {0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC},
-    {0xFEED, 0xFEEE, 0xFEED, 0xFEEE}, {0xFEEF, 0xFEF0, 0xFBFE, 0xFBFF},
-    {0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4}, {0x064B, 0x064B, 0x064B, 0x064B},
-    {0x064C, 0x064C, 0x064C, 0x064C}, {0x064D, 0x064D, 0x064D, 0x064D},
-    {0x064E, 0x064E, 0x064E, 0x064E}, {0x064F, 0x064F, 0x064F, 0x064F},
-    {0x0650, 0x0650, 0x0650, 0x0650}, {0x0651, 0x0651, 0x0651, 0x0651},
-    {0x0652, 0x0652, 0x0652, 0x0652}, {0x0653, 0x0653, 0x0653, 0x0653},
-    {0x0654, 0x0654, 0x0654, 0x0654}, {0x0655, 0x0655, 0x0655, 0x0655},
-    {0x0656, 0x0656, 0x0656, 0x0656}, {0x0657, 0x0657, 0x0657, 0x0657},
-    {0x0658, 0x0658, 0x0658, 0x0658}, {0x0659, 0x0659, 0x0659, 0x0659},
-    {0x065A, 0x065A, 0x065A, 0x065A}, {0x065B, 0x065B, 0x065B, 0x065B},
-    {0x065C, 0x065C, 0x065C, 0x065C}, {0x065D, 0x065D, 0x065D, 0x065D},
-    {0x065E, 0x065E, 0x065E, 0x065E}, {0x065F, 0x065F, 0x065F, 0x065F},
-    {0x0660, 0x0660, 0x0660, 0x0660}, {0x0661, 0x0661, 0x0661, 0x0661},
-    {0x0662, 0x0662, 0x0662, 0x0662}, {0x0663, 0x0663, 0x0663, 0x0663},
-    {0x0664, 0x0664, 0x0664, 0x0664}, {0x0665, 0x0665, 0x0665, 0x0665},
-    {0x0666, 0x0666, 0x0666, 0x0666}, {0x0667, 0x0667, 0x0667, 0x0667},
-    {0x0668, 0x0668, 0x0668, 0x0668}, {0x0669, 0x0669, 0x0669, 0x0669},
-    {0x066A, 0x066A, 0x066A, 0x066A}, {0x066B, 0x066B, 0x066B, 0x066B},
-    {0x066C, 0x066C, 0x066C, 0x066C}, {0x066D, 0x066D, 0x066D, 0x066D},
-    {0x066E, 0x066E, 0x066E, 0x066E}, {0x066F, 0x066F, 0x066F, 0x066F},
-    {0x0670, 0x0670, 0x0670, 0x0670}, {0xFB50, 0xFB51, 0xFB50, 0xFB51},
-    {0x0672, 0x0672, 0x0672, 0x0672}, {0x0673, 0x0673, 0x0673, 0x0673},
-    {0x0674, 0x0674, 0x0674, 0x0674}, {0x0675, 0x0675, 0x0675, 0x0675},
-    {0x0676, 0x0676, 0x0676, 0x0676}, {0x0677, 0x0677, 0x0677, 0x0677},
-    {0x0678, 0x0678, 0x0678, 0x0678}, {0xFB66, 0xFB67, 0xFB68, 0xFB69},
-    {0xFB5E, 0xFB5F, 0xFB60, 0xFB61}, {0xFB52, 0xFB53, 0xFB54, 0xFB55},
-    {0x067C, 0x067C, 0x067C, 0x067C}, {0x067D, 0x067D, 0x067D, 0x067D},
-    {0xFB56, 0xFB57, 0xFB58, 0xFB59}, {0xFB62, 0xFB63, 0xFB64, 0xFB65},
-    {0xFB5A, 0xFB5B, 0xFB5C, 0xFB5D}, {0x0681, 0x0681, 0x0681, 0x0681},
-    {0x0682, 0x0682, 0x0682, 0x0682}, {0xFB76, 0xFB77, 0xFB78, 0xFB79},
-    {0xFB72, 0xFB73, 0xFB74, 0xFB75}, {0x0685, 0x0685, 0x0685, 0x0685},
-    {0xFB7A, 0xFB7B, 0xFB7C, 0xFB7D}, {0xFB7E, 0xFB7F, 0xFB80, 0xFB81},
-    {0xFB88, 0xFB89, 0xFB88, 0xFB89}, {0x0689, 0x0689, 0x0689, 0x0689},
-    {0x068A, 0x068A, 0x068A, 0x068A}, {0x068B, 0x068B, 0x068B, 0x068B},
-    {0xFB84, 0xFB85, 0xFB84, 0xFB85}, {0xFB82, 0xFB83, 0xFB82, 0xFB83},
-    {0xFB86, 0xFB87, 0xFB86, 0xFB87}, {0x068F, 0x068F, 0x068F, 0x068F},
-    {0x0690, 0x0690, 0x0690, 0x0690}, {0xFB8C, 0xFB8D, 0xFB8C, 0xFB8D},
-    {0x0692, 0x0692, 0x0692, 0x0692}, {0x0693, 0x0693, 0x0693, 0x0693},
-    {0x0694, 0x0694, 0x0694, 0x0694}, {0x0695, 0x0695, 0x0695, 0x0695},
-    {0x0696, 0x0696, 0x0696, 0x0696}, {0x0697, 0x0697, 0x0697, 0x0697},
-    {0xFB8A, 0xFB8B, 0xFB8A, 0xFB8B}, {0x0699, 0x0699, 0x0699, 0x0699},
-    {0x069A, 0x069A, 0x069A, 0x069A}, {0x069B, 0x069B, 0x069B, 0x069B},
-    {0x069C, 0x069C, 0x069C, 0x069C}, {0x069D, 0x069D, 0x069D, 0x069D},
-    {0x069E, 0x069E, 0x069E, 0x069E}, {0x069F, 0x069F, 0x069F, 0x069F},
-    {0x06A0, 0x06A0, 0x06A0, 0x06A0}, {0x06A1, 0x06A1, 0x06A1, 0x06A1},
-    {0x06A2, 0x06A2, 0x06A2, 0x06A2}, {0x06A3, 0x06A3, 0x06A3, 0x06A3},
-    {0xFB6A, 0xFB6B, 0xFB6C, 0xFB6D}, {0x06A5, 0x06A5, 0x06A5, 0x06A5},
-    {0xFB6E, 0xFB6F, 0xFB70, 0xFB71}, {0x06A7, 0x06A7, 0x06A7, 0x06A7},
-    {0x06A8, 0x06A8, 0x06A8, 0x06A8}, {0xFB8E, 0xFB8F, 0xFB90, 0xFB91},
-    {0x06AA, 0x06AA, 0x06AA, 0x06AA}, {0x06AB, 0x06AB, 0x06AB, 0x06AB},
-    {0x06AC, 0x06AC, 0x06AC, 0x06AC}, {0xFBD3, 0xFBD4, 0xFBD5, 0xFBD6},
-    {0x06AE, 0x06AE, 0x06AE, 0x06AE}, {0xFB92, 0xFB93, 0xFB94, 0xFB95},
-    {0x06B0, 0x06B0, 0x06B0, 0x06B0}, {0xFB9A, 0xFB9B, 0xFB9C, 0xFB9D},
-    {0x06B2, 0x06B2, 0x06B2, 0x06B2}, {0xFB96, 0xFB97, 0xFB98, 0xFB99},
-    {0x06B4, 0x06B4, 0x06B4, 0x06B4}, {0x06B5, 0x06B5, 0x06B5, 0x06B5},
-    {0x06B6, 0x06B6, 0x06B6, 0x06B6}, {0x06B7, 0x06B7, 0x06B7, 0x06B7},
-    {0x06B8, 0x06B8, 0x06B8, 0x06B8}, {0x06B9, 0x06B9, 0x06B9, 0x06B9},
-    {0xFB9E, 0xFB9F, 0xFBE8, 0xFBE9}, {0xFBA0, 0xFBA1, 0xFBA2, 0xFBA3},
-    {0x06BC, 0x06BC, 0x06BC, 0x06BC}, {0x06BD, 0x06BD, 0x06BD, 0x06BD},
-    {0xFBAA, 0xFBAB, 0xFBAC, 0xFBAD}, {0x06BF, 0x06BF, 0x06BF, 0x06BF},
-    {0xFBA4, 0xFBA5, 0xFBA4, 0xFBA5}, {0xFBA6, 0xFBA7, 0xFBA8, 0xFBA9},
-    {0x06C2, 0x06C2, 0x06C2, 0x06C2}, {0x06C3, 0x06C3, 0x06C3, 0x06C3},
-    {0x06C4, 0x06C4, 0x06C4, 0x06C4}, {0xFBE0, 0xFBE1, 0xFBE0, 0xFBE1},
-    {0xFBD9, 0xFBDA, 0xFBD9, 0xFBDA}, {0xFBD7, 0xFBD8, 0xFBD7, 0xFBD8},
-    {0xFBDB, 0xFBDC, 0xFBDB, 0xFBDC}, {0xFBE2, 0xFBE3, 0xFBE2, 0xFBE3},
-    {0x06CA, 0x06CA, 0x06CA, 0x06CA}, {0xFBDE, 0xFBDF, 0xFBDE, 0xFBDF},
-    {0xFBFC, 0xFBFD, 0xFBFE, 0xFBFF}, {0x06CD, 0x06CD, 0x06CD, 0x06CD},
-    {0x06CE, 0x06CE, 0x06CE, 0x06CE}, {0x06CF, 0x06CF, 0x06CF, 0x06CF},
-    {0xFBE4, 0xFBE5, 0xFBE6, 0xFBE7}, {0x06D1, 0x06D1, 0x06D1, 0x06D1},
-    {0xFBAE, 0xFBAF, 0xFBAE, 0xFBAF}, {0xFBB0, 0xFBB1, 0xFBB0, 0xFBB1},
-    {0x06D4, 0x06D4, 0x06D4, 0x06D4}, {0x06D5, 0x06D5, 0x06D5, 0x06D5},
-};
-
-const FX_ARAALEF gs_FX_AlefTable[] = {
-    {0x0622, 0xFEF5},
-    {0x0623, 0xFEF7},
-    {0x0625, 0xFEF9},
-    {0x0627, 0xFEFB},
-};
-
-const FX_ARASHADDA gs_FX_ShaddaTable[] = {
-    {0x064C, 0xFC5E}, {0x064D, 0xFC5F}, {0x064E, 0xFC60},
-    {0x064F, 0xFC61}, {0x0650, 0xFC62},
-};
-
-const FX_ARBFORMTABLE* GetArabicFormTable(wchar_t unicode) {
-  if (unicode < 0x622 || unicode > 0x6d5)
-    return nullptr;
-  return g_FX_ArabicFormTables + unicode - 0x622;
-}
-
-const FX_ARBFORMTABLE* ParseChar(const CFX_Char* pTC,
-                                 wchar_t* wChar,
-                                 FX_CHARTYPE* eType) {
-  if (!pTC) {
-    *eType = FX_CHARTYPE::kUnknown;
-    *wChar = 0xFEFF;
-    return nullptr;
-  }
-
-  *eType = pTC->GetCharType();
-  *wChar = static_cast<wchar_t>(pTC->char_code());
-  const FX_ARBFORMTABLE* pFT = GetArabicFormTable(*wChar);
-  if (!pFT || *eType >= FX_CHARTYPE::kArabicNormal)
-    *eType = FX_CHARTYPE::kUnknown;
-
-  return pFT;
-}
-
-wchar_t GetArabicFromAlefTable(wchar_t alef) {
-  static const size_t s_iAlefCount = FX_ArraySize(gs_FX_AlefTable);
-  for (size_t iStart = 0; iStart < s_iAlefCount; iStart++) {
-    const FX_ARAALEF& v = gs_FX_AlefTable[iStart];
-    if (v.wAlef == alef)
-      return v.wIsolated;
-  }
-  return alef;
-}
-
-}  // namespace
-
-namespace pdfium {
-namespace arabic {
-
-wchar_t GetFormChar(wchar_t wch, wchar_t prev, wchar_t next) {
-  CFX_Char c(wch);
-  CFX_Char p(prev);
-  CFX_Char n(next);
-  return GetFormChar(&c, &p, &n);
-}
-
-wchar_t GetFormChar(const CFX_Char* cur,
-                    const CFX_Char* prev,
-                    const CFX_Char* next) {
-  FX_CHARTYPE eCur;
-  wchar_t wCur;
-  const FX_ARBFORMTABLE* ft = ParseChar(cur, &wCur, &eCur);
-  if (eCur < FX_CHARTYPE::kArabicAlef || eCur >= FX_CHARTYPE::kArabicNormal)
-    return wCur;
-
-  FX_CHARTYPE ePrev;
-  wchar_t wPrev;
-  ParseChar(prev, &wPrev, &ePrev);
-  if (wPrev == 0x0644 && eCur == FX_CHARTYPE::kArabicAlef)
-    return 0xFEFF;
-
-  FX_CHARTYPE eNext;
-  wchar_t wNext;
-  ParseChar(next, &wNext, &eNext);
-  bool bAlef = (eNext == FX_CHARTYPE::kArabicAlef && wCur == 0x644);
-  if (ePrev < FX_CHARTYPE::kArabicAlef) {
-    if (bAlef)
-      return GetArabicFromAlefTable(wNext);
-    return (eNext < FX_CHARTYPE::kArabicAlef) ? ft->wIsolated : ft->wInitial;
-  }
-
-  if (bAlef) {
-    wCur = GetArabicFromAlefTable(wNext);
-    return (ePrev != FX_CHARTYPE::kArabicDistortion) ? wCur : ++wCur;
-  }
-
-  if (ePrev == FX_CHARTYPE::kArabicAlef || ePrev == FX_CHARTYPE::kArabicSpecial)
-    return (eNext < FX_CHARTYPE::kArabicAlef) ? ft->wIsolated : ft->wInitial;
-  return (eNext < FX_CHARTYPE::kArabicAlef) ? ft->wFinal : ft->wMedial;
-}
-
-}  // namespace arabic
-}  // namespace pdfium
-
-wchar_t FX_GetArabicFromShaddaTable(wchar_t shadda) {
-  static const size_t s_iShaddaCount = FX_ArraySize(gs_FX_ShaddaTable);
-  for (size_t iStart = 0; iStart < s_iShaddaCount; iStart++) {
-    const FX_ARASHADDA& v = gs_FX_ShaddaTable[iStart];
-    if (v.wShadda == shadda)
-      return v.wIsolated;
-  }
-  return shadda;
-}
diff --git a/xfa/fgas/layout/fx_arabic.h b/xfa/fgas/layout/fx_arabic.h
deleted file mode 100644
index ca33aa5..0000000
--- a/xfa/fgas/layout/fx_arabic.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FGAS_LAYOUT_FX_ARABIC_H_
-#define XFA_FGAS_LAYOUT_FX_ARABIC_H_
-
-#include "core/fxcrt/fx_system.h"
-#include "xfa/fgas/layout/cfx_char.h"
-
-namespace pdfium {
-namespace arabic {
-
-wchar_t GetFormChar(wchar_t wch, wchar_t prev, wchar_t next);
-wchar_t GetFormChar(const CFX_Char* cur,
-                    const CFX_Char* prev,
-                    const CFX_Char* next);
-
-}  // namespace arabic
-}  // namespace pdfium
-
-wchar_t FX_GetArabicFromShaddaTable(wchar_t shadda);
-
-#endif  // XFA_FGAS_LAYOUT_FX_ARABIC_H_
diff --git a/xfa/fgas/layout/fx_linebreak.cpp b/xfa/fgas/layout/fx_linebreak.cpp
deleted file mode 100644
index 96821f1..0000000
--- a/xfa/fgas/layout/fx_linebreak.cpp
+++ /dev/null
@@ -1,232 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fgas/layout/fx_linebreak.h"
-
-#include "core/fxcrt/fx_memory.h"
-#include "core/fxcrt/fx_unicode.h"
-
-namespace {
-
-#define FX_LBUN FX_LINEBREAKTYPE::kUNKNOWN
-#define FX_LBDB FX_LINEBREAKTYPE::kDIRECT_BRK
-#define FX_LBIB FX_LINEBREAKTYPE::kINDIRECT_BRK
-#define FX_LBCB FX_LINEBREAKTYPE::kCOM_INDIRECT_BRK
-#define FX_LBCP FX_LINEBREAKTYPE::kCOM_PROHIBITED_BRK
-#define FX_LBPB FX_LINEBREAKTYPE::kPROHIBITED_BRK
-#define FX_LBHS FX_LINEBREAKTYPE::kHANGUL_SPACE_BRK
-
-const FX_LINEBREAKTYPE gs_FX_LineBreak_PairTable[38][38] = {
-    {FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBPB, FX_LBPB, FX_LBPB, FX_LBCP, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBPB, FX_LBPB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBIB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBPB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBPB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBDB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBDB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBPB, FX_LBPB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBPB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-};
-
-#undef FX_LBUN
-#undef FX_LBDB
-#undef FX_LBIB
-#undef FX_LBCB
-#undef FX_LBCP
-#undef FX_LBPB
-#undef FX_LBHS
-
-}  // namespace
-
-FX_LINEBREAKTYPE GetLineBreakTypeFromPair(FX_BREAKPROPERTY curr_char,
-                                          FX_BREAKPROPERTY next_char) {
-  size_t row = static_cast<size_t>(curr_char);
-  size_t col = static_cast<size_t>(next_char);
-  ASSERT(row < FX_ArraySize(gs_FX_LineBreak_PairTable));
-  ASSERT(col < FX_ArraySize(gs_FX_LineBreak_PairTable[0]));
-  return gs_FX_LineBreak_PairTable[row][col];
-}
diff --git a/xfa/fgas/layout/fx_linebreak.h b/xfa/fgas/layout/fx_linebreak.h
deleted file mode 100644
index ac46a35..0000000
--- a/xfa/fgas/layout/fx_linebreak.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FGAS_LAYOUT_FX_LINEBREAK_H_
-#define XFA_FGAS_LAYOUT_FX_LINEBREAK_H_
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/fx_unicode.h"
-
-enum class FX_LINEBREAKTYPE : uint8_t {
-  kUNKNOWN = 0x00,
-  kDIRECT_BRK = 0x1A,
-  kINDIRECT_BRK = 0x2B,
-  kCOM_INDIRECT_BRK = 0x3C,
-  kCOM_PROHIBITED_BRK = 0x4D,
-  kPROHIBITED_BRK = 0x5E,
-  kHANGUL_SPACE_BRK = 0x6F,
-};
-
-FX_LINEBREAKTYPE GetLineBreakTypeFromPair(FX_BREAKPROPERTY curr_char,
-                                          FX_BREAKPROPERTY next_char);
-
-#endif  // XFA_FGAS_LAYOUT_FX_LINEBREAK_H_
diff --git a/xfa/fwl/BUILD.gn b/xfa/fwl/BUILD.gn
index 765704d..b6b09b8 100644
--- a/xfa/fwl/BUILD.gn
+++ b/xfa/fwl/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -37,16 +37,12 @@
     "cfwl_eventscroll.h",
     "cfwl_eventselectchanged.cpp",
     "cfwl_eventselectchanged.h",
-    "cfwl_eventtarget.cpp",
-    "cfwl_eventtarget.h",
     "cfwl_eventtextwillchange.cpp",
     "cfwl_eventtextwillchange.h",
     "cfwl_eventvalidate.cpp",
     "cfwl_eventvalidate.h",
     "cfwl_listbox.cpp",
     "cfwl_listbox.h",
-    "cfwl_listitem.cpp",
-    "cfwl_listitem.h",
     "cfwl_message.cpp",
     "cfwl_message.h",
     "cfwl_messagekey.cpp",
@@ -69,18 +65,19 @@
     "cfwl_pushbutton.h",
     "cfwl_scrollbar.cpp",
     "cfwl_scrollbar.h",
+    "cfwl_themebackground.cpp",
     "cfwl_themebackground.h",
     "cfwl_themepart.cpp",
     "cfwl_themepart.h",
+    "cfwl_themetext.cpp",
     "cfwl_themetext.h",
     "cfwl_widget.cpp",
     "cfwl_widget.h",
     "cfwl_widgetmgr.cpp",
     "cfwl_widgetmgr.h",
-    "cfwl_widgetproperties.cpp",
-    "cfwl_widgetproperties.h",
     "fwl_widgetdef.h",
     "fwl_widgethit.h",
+    "ifwl_themeprovider.cpp",
     "ifwl_themeprovider.h",
     "ifwl_widgetdelegate.h",
     "theme/cfwl_barcodetp.cpp",
@@ -109,22 +106,29 @@
     "theme/cfwl_widgettp.cpp",
     "theme/cfwl_widgettp.h",
   ]
+  configs += [
+    "../../:pdfium_strict_config",
+    "../../:pdfium_noshorten_config",
+    "../:xfa_warnings",
+  ]
   deps = [
     "../../core/fxcrt",
     "../../core/fxge",
     "../../fxbarcode",
+    "../../fxjs:gc",
     "../fde",
-    "../fgas",
-    "../fxgraphics",
-  ]
-  configs += [
-    "../../:pdfium_core_config",
-    "../:xfa_warnings",
+    "../fgas/font",
+    "../fgas/graphics",
   ]
   visibility = [ "../../*" ]
 }
 
 pdfium_embeddertest_source_set("embeddertests") {
   sources = [ "cfwl_edit_embeddertest.cpp" ]
+  deps = [
+    ":fwl",
+    "../../core/fxge",
+    "../../fxjs:gc",
+  ]
   pdfium_root_dir = "../../"
 }
diff --git a/xfa/fwl/DEPS b/xfa/fwl/DEPS
new file mode 100644
index 0000000..8bea3a3
--- /dev/null
+++ b/xfa/fwl/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  # fwl is standalone vis-a-vis fxfa. https://crbug.com/pdfium/507
+  '-xfa/fxfa',
+]
diff --git a/xfa/fwl/cfwl_app.cpp b/xfa/fwl/cfwl_app.cpp
index c3a06e3..250fcec 100644
--- a/xfa/fwl/cfwl_app.cpp
+++ b/xfa/fwl/cfwl_app.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,17 +6,25 @@
 
 #include "xfa/fwl/cfwl_app.h"
 
-#include "third_party/base/ptr_util.h"
+#include "v8/include/cppgc/allocation.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
 
 CFWL_App::CFWL_App(AdapterIface* pAdapter)
-    : m_pAdapterNative(pAdapter),
-      m_pWidgetMgr(
-          pdfium::MakeUnique<CFWL_WidgetMgr>(pAdapter->GetWidgetMgrAdapter())),
-      m_pNoteDriver(pdfium::MakeUnique<CFWL_NoteDriver>()) {
-  ASSERT(m_pAdapterNative);
-}
+    : m_pAdapter(pAdapter),
+      m_pWidgetMgr(cppgc::MakeGarbageCollected<CFWL_WidgetMgr>(
+          pAdapter->GetHeap()->GetAllocationHandle(),
+          pAdapter->GetWidgetMgrAdapter(),
+          this)),
+      m_pNoteDriver(cppgc::MakeGarbageCollected<CFWL_NoteDriver>(
+          pAdapter->GetHeap()->GetAllocationHandle(),
+          this)) {}
 
 CFWL_App::~CFWL_App() = default;
+
+void CFWL_App::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pAdapter);
+  visitor->Trace(m_pWidgetMgr);
+  visitor->Trace(m_pNoteDriver);
+}
diff --git a/xfa/fwl/cfwl_app.h b/xfa/fwl/cfwl_app.h
index a911ab2..b6a76fa 100644
--- a/xfa/fwl/cfwl_app.h
+++ b/xfa/fwl/cfwl_app.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,45 +7,51 @@
 #ifndef XFA_FWL_CFWL_APP_H_
 #define XFA_FWL_CFWL_APP_H_
 
-#include <memory>
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/timerhandler_iface.h"
+#include "core/fxcrt/cfx_timer.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
 
 class CFWL_NoteDriver;
-class CFWL_WidgetMgr;
+class IFWL_ThemeProvider;
 
-enum FWL_KeyFlag {
-  FWL_KEYFLAG_Ctrl = 1 << 0,
-  FWL_KEYFLAG_Alt = 1 << 1,
-  FWL_KEYFLAG_Shift = 1 << 2,
-  FWL_KEYFLAG_Command = 1 << 3,
-  FWL_KEYFLAG_LButton = 1 << 4,
-  FWL_KEYFLAG_RButton = 1 << 5,
-  FWL_KEYFLAG_MButton = 1 << 6
-};
-
-class CFWL_App {
+class CFWL_App final : public cppgc::GarbageCollected<CFWL_App> {
  public:
-  class AdapterIface {
+  class AdapterIface : public cppgc::GarbageCollectedMixin {
    public:
     virtual ~AdapterIface() = default;
     virtual CFWL_WidgetMgr::AdapterIface* GetWidgetMgrAdapter() = 0;
-    virtual TimerHandlerIface* GetTimerHandler() = 0;
+    virtual CFX_Timer::HandlerIface* GetTimerHandler() = 0;
+    virtual IFWL_ThemeProvider* GetThemeProvider() = 0;
+    virtual cppgc::Heap* GetHeap() = 0;
   };
 
-  explicit CFWL_App(AdapterIface* pAdapter);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_App();
 
-  AdapterIface* GetAdapterNative() const { return m_pAdapterNative.Get(); }
-  CFWL_WidgetMgr* GetWidgetMgr() const { return m_pWidgetMgr.get(); }
-  CFWL_NoteDriver* GetNoteDriver() const { return m_pNoteDriver.get(); }
+  void Trace(cppgc::Visitor* visitor) const;
+
+  CFWL_WidgetMgr::AdapterIface* GetWidgetMgrAdapter() const {
+    return m_pAdapter->GetWidgetMgrAdapter();
+  }
+  CFX_Timer::HandlerIface* GetTimerHandler() const {
+    return m_pAdapter->GetTimerHandler();
+  }
+  IFWL_ThemeProvider* GetThemeProvider() const {
+    return m_pAdapter->GetThemeProvider();
+  }
+  cppgc::Heap* GetHeap() const { return m_pAdapter->GetHeap(); }
+  CFWL_WidgetMgr* GetWidgetMgr() const { return m_pWidgetMgr; }
+  CFWL_NoteDriver* GetNoteDriver() const { return m_pNoteDriver; }
 
  private:
-  UnownedPtr<AdapterIface> const m_pAdapterNative;
-  std::unique_ptr<CFWL_WidgetMgr> m_pWidgetMgr;
-  std::unique_ptr<CFWL_NoteDriver> m_pNoteDriver;
+  explicit CFWL_App(AdapterIface* pAdapter);
+
+  cppgc::Member<AdapterIface> const m_pAdapter;
+  cppgc::Member<CFWL_WidgetMgr> m_pWidgetMgr;
+  cppgc::Member<CFWL_NoteDriver> m_pNoteDriver;
 };
 
 #endif  // XFA_FWL_CFWL_APP_H_
diff --git a/xfa/fwl/cfwl_barcode.cpp b/xfa/fwl/cfwl_barcode.cpp
index 008424d..1802c7f 100644
--- a/xfa/fwl/cfwl_barcode.cpp
+++ b/xfa/fwl/cfwl_barcode.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,18 +6,15 @@
 
 #include "xfa/fwl/cfwl_barcode.h"
 
-#include <utility>
-
 #include "fxbarcode/cfx_barcode.h"
-#include "third_party/base/ptr_util.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fwl/cfwl_themepart.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
 #include "xfa/fwl/theme/cfwl_utils.h"
 
-CFWL_Barcode::CFWL_Barcode(const CFWL_App* app)
-    : CFWL_Edit(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr) {}
+CFWL_Barcode::CFWL_Barcode(CFWL_App* app)
+    : CFWL_Edit(app, Properties(), nullptr) {}
 
 CFWL_Barcode::~CFWL_Barcode() = default;
 
@@ -33,13 +30,12 @@
   GenerateBarcodeImageCache();
 }
 
-void CFWL_Barcode::DrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_Barcode::DrawWidget(CFGAS_GEGraphics* pGraphics,
                               const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
-  if (!m_pProperties->m_pThemeProvider)
-    return;
-  if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0) {
+
+  if ((m_Properties.m_dwStates & FWL_STATE_WGT_Focused) == 0) {
     GenerateBarcodeImageCache();
     if (!m_pBarcodeEngine || m_eStatus != Status::kEncodeSuccess)
       return;
@@ -49,7 +45,8 @@
     mt.f = GetRTClient().top;
     mt.Concat(matrix);
 
-    m_pBarcodeEngine->RenderDevice(pGraphics->GetRenderDevice(), &matrix);
+    // TODO(tsepez): Curious as to why |mt| is unused?
+    m_pBarcodeEngine->RenderDevice(pGraphics->GetRenderDevice(), matrix);
     return;
   }
   CFWL_Edit::DrawWidget(pGraphics, matrix);
@@ -81,8 +78,8 @@
     return true;
 
   BC_TYPE tEngineType = m_pBarcodeEngine->GetType();
-  return tEngineType == BC_QR_CODE || tEngineType == BC_PDF417 ||
-         tEngineType == BC_DATAMATRIX;
+  return tEngineType == BC_TYPE::kQRCode || tEngineType == BC_TYPE::kPDF417 ||
+         tEngineType == BC_TYPE::kDataMatrix;
 }
 
 void CFWL_Barcode::OnProcessEvent(CFWL_Event* pEvent) {
@@ -94,58 +91,47 @@
 }
 
 void CFWL_Barcode::SetCharEncoding(BC_CHAR_ENCODING encoding) {
-  m_dwAttributeMask |= FWL_BCDATTRIBUTE_CHARENCODING;
   m_eCharEncoding = encoding;
 }
 
 void CFWL_Barcode::SetModuleHeight(int32_t height) {
-  m_dwAttributeMask |= FWL_BCDATTRIBUTE_MODULEHEIGHT;
   m_nModuleHeight = height;
 }
 
 void CFWL_Barcode::SetModuleWidth(int32_t width) {
-  m_dwAttributeMask |= FWL_BCDATTRIBUTE_MODULEWIDTH;
   m_nModuleWidth = width;
 }
 
 void CFWL_Barcode::SetDataLength(int32_t dataLength) {
-  m_dwAttributeMask |= FWL_BCDATTRIBUTE_DATALENGTH;
   m_nDataLength = dataLength;
   SetLimit(dataLength);
 }
 
 void CFWL_Barcode::SetCalChecksum(bool calChecksum) {
-  m_dwAttributeMask |= FWL_BCDATTRIBUTE_CALCHECKSUM;
   m_bCalChecksum = calChecksum;
 }
 
 void CFWL_Barcode::SetPrintChecksum(bool printChecksum) {
-  m_dwAttributeMask |= FWL_BCDATTRIBUTE_PRINTCHECKSUM;
   m_bPrintChecksum = printChecksum;
 }
 
 void CFWL_Barcode::SetTextLocation(BC_TEXT_LOC location) {
-  m_dwAttributeMask |= FWL_BCDATTRIBUTE_TEXTLOCATION;
   m_eTextLocation = location;
 }
 
 void CFWL_Barcode::SetWideNarrowRatio(int8_t ratio) {
-  m_dwAttributeMask |= FWL_BCDATTRIBUTE_WIDENARROWRATIO;
   m_nWideNarrowRatio = ratio;
 }
 
 void CFWL_Barcode::SetStartChar(char startChar) {
-  m_dwAttributeMask |= FWL_BCDATTRIBUTE_STARTCHAR;
   m_cStartChar = startChar;
 }
 
 void CFWL_Barcode::SetEndChar(char endChar) {
-  m_dwAttributeMask |= FWL_BCDATTRIBUTE_ENDCHAR;
   m_cEndChar = endChar;
 }
 
 void CFWL_Barcode::SetErrorCorrectionLevel(int32_t ecLevel) {
-  m_dwAttributeMask |= FWL_BCDATTRIBUTE_ECLEVEL;
   m_nECLevel = ecLevel;
 }
 
@@ -158,44 +144,38 @@
   if (!m_pBarcodeEngine)
     return;
 
-  IFWL_ThemeProvider* pTheme = GetAvailableTheme();
-  if (pTheme) {
-    CFWL_ThemePart part;
-    part.m_pWidget = this;
-    if (RetainPtr<CFGAS_GEFont> pFont = pTheme->GetFont(part)) {
-      if (CFX_Font* pCXFont = pFont->GetDevFont())
-        m_pBarcodeEngine->SetFont(pCXFont);
-    }
-    m_pBarcodeEngine->SetFontSize(pTheme->GetFontSize(part));
-    m_pBarcodeEngine->SetFontColor(pTheme->GetTextColor(part));
-  } else {
-    m_pBarcodeEngine->SetFontSize(FWLTHEME_CAPACITY_FontSize);
+  IFWL_ThemeProvider* pTheme = GetThemeProvider();
+  CFWL_ThemePart part(CFWL_ThemePart::Part::kNone, this);
+  if (RetainPtr<CFGAS_GEFont> pFont = pTheme->GetFont(part)) {
+    if (CFX_Font* pCXFont = pFont->GetDevFont())
+      m_pBarcodeEngine->SetFont(pCXFont);
   }
-
+  m_pBarcodeEngine->SetFontSize(pTheme->GetFontSize(part));
+  m_pBarcodeEngine->SetFontColor(pTheme->GetTextColor(part));
   m_pBarcodeEngine->SetHeight(int32_t(GetRTClient().height));
   m_pBarcodeEngine->SetWidth(int32_t(GetRTClient().width));
-  if (m_dwAttributeMask & FWL_BCDATTRIBUTE_CHARENCODING)
-    m_pBarcodeEngine->SetCharEncoding(m_eCharEncoding);
-  if (m_dwAttributeMask & FWL_BCDATTRIBUTE_MODULEHEIGHT)
-    m_pBarcodeEngine->SetModuleHeight(m_nModuleHeight);
-  if (m_dwAttributeMask & FWL_BCDATTRIBUTE_MODULEWIDTH)
-    m_pBarcodeEngine->SetModuleWidth(m_nModuleWidth);
-  if (m_dwAttributeMask & FWL_BCDATTRIBUTE_DATALENGTH)
-    m_pBarcodeEngine->SetDataLength(m_nDataLength);
-  if (m_dwAttributeMask & FWL_BCDATTRIBUTE_CALCHECKSUM)
-    m_pBarcodeEngine->SetCalChecksum(m_bCalChecksum);
-  if (m_dwAttributeMask & FWL_BCDATTRIBUTE_PRINTCHECKSUM)
-    m_pBarcodeEngine->SetPrintChecksum(m_bPrintChecksum);
-  if (m_dwAttributeMask & FWL_BCDATTRIBUTE_TEXTLOCATION)
-    m_pBarcodeEngine->SetTextLocation(m_eTextLocation);
-  if (m_dwAttributeMask & FWL_BCDATTRIBUTE_WIDENARROWRATIO)
-    m_pBarcodeEngine->SetWideNarrowRatio(m_nWideNarrowRatio);
-  if (m_dwAttributeMask & FWL_BCDATTRIBUTE_STARTCHAR)
-    m_pBarcodeEngine->SetStartChar(m_cStartChar);
-  if (m_dwAttributeMask & FWL_BCDATTRIBUTE_ENDCHAR)
-    m_pBarcodeEngine->SetEndChar(m_cEndChar);
-  if (m_dwAttributeMask & FWL_BCDATTRIBUTE_ECLEVEL)
-    m_pBarcodeEngine->SetErrorCorrectionLevel(m_nECLevel);
+  if (m_eCharEncoding.has_value())
+    m_pBarcodeEngine->SetCharEncoding(m_eCharEncoding.value());
+  if (m_nModuleHeight.has_value())
+    m_pBarcodeEngine->SetModuleHeight(m_nModuleHeight.value());
+  if (m_nModuleWidth.has_value())
+    m_pBarcodeEngine->SetModuleWidth(m_nModuleWidth.value());
+  if (m_nDataLength.has_value())
+    m_pBarcodeEngine->SetDataLength(m_nDataLength.value());
+  if (m_bCalChecksum.has_value())
+    m_pBarcodeEngine->SetCalChecksum(m_bCalChecksum.value());
+  if (m_bPrintChecksum.has_value())
+    m_pBarcodeEngine->SetPrintChecksum(m_bPrintChecksum.value());
+  if (m_eTextLocation.has_value())
+    m_pBarcodeEngine->SetTextLocation(m_eTextLocation.value());
+  if (m_nWideNarrowRatio.has_value())
+    m_pBarcodeEngine->SetWideNarrowRatio(m_nWideNarrowRatio.value());
+  if (m_cStartChar.has_value())
+    m_pBarcodeEngine->SetStartChar(m_cStartChar.value());
+  if (m_cEndChar.has_value())
+    m_pBarcodeEngine->SetEndChar(m_cEndChar.value());
+  if (m_nECLevel.has_value())
+    m_pBarcodeEngine->SetErrorCorrectionLevel(m_nECLevel.value());
 
   m_eStatus = m_pBarcodeEngine->Encode(GetText().AsStringView())
                   ? Status::kEncodeSuccess
@@ -203,7 +183,7 @@
 }
 
 void CFWL_Barcode::CreateBarcodeEngine() {
-  if (m_pBarcodeEngine || m_type == BC_UNKNOWN)
+  if (m_pBarcodeEngine || m_type == BC_TYPE::kUnknown)
     return;
 
   m_pBarcodeEngine = CFX_Barcode::Create(m_type);
diff --git a/xfa/fwl/cfwl_barcode.h b/xfa/fwl/cfwl_barcode.h
index d2cd716..45da19c 100644
--- a/xfa/fwl/cfwl_barcode.h
+++ b/xfa/fwl/cfwl_barcode.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,37 +7,26 @@
 #ifndef XFA_FWL_CFWL_BARCODE_H_
 #define XFA_FWL_CFWL_BARCODE_H_
 
+#include <stdint.h>
+
 #include <memory>
 
 #include "fxbarcode/BC_Library.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "xfa/fwl/cfwl_edit.h"
 
 class CFX_Barcode;
 
-enum FWL_BCDAttribute {
-  FWL_BCDATTRIBUTE_NONE = 0,
-  FWL_BCDATTRIBUTE_CHARENCODING = 1 << 0,
-  FWL_BCDATTRIBUTE_MODULEHEIGHT = 1 << 1,
-  FWL_BCDATTRIBUTE_MODULEWIDTH = 1 << 2,
-  FWL_BCDATTRIBUTE_DATALENGTH = 1 << 3,
-  FWL_BCDATTRIBUTE_CALCHECKSUM = 1 << 4,
-  FWL_BCDATTRIBUTE_PRINTCHECKSUM = 1 << 5,
-  FWL_BCDATTRIBUTE_TEXTLOCATION = 1 << 6,
-  FWL_BCDATTRIBUTE_WIDENARROWRATIO = 1 << 7,
-  FWL_BCDATTRIBUTE_STARTCHAR = 1 << 8,
-  FWL_BCDATTRIBUTE_ENDCHAR = 1 << 9,
-  FWL_BCDATTRIBUTE_ECLEVEL = 1 << 10,
-};
-
 class CFWL_Barcode final : public CFWL_Edit {
  public:
-  explicit CFWL_Barcode(const CFWL_App* pApp);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_Barcode() override;
 
   // CFWL_Widget
   FWL_Type GetClassID() const override;
   void Update() override;
-  void DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) override;
+  void DrawWidget(CFGAS_GEGraphics* pGraphics,
+                  const CFX_Matrix& matrix) override;
   void OnProcessEvent(CFWL_Event* pEvent) override;
 
   // CFWL_Edit
@@ -66,23 +55,24 @@
     kEncodeSuccess,
   };
 
+  explicit CFWL_Barcode(CFWL_App* pApp);
+
   void GenerateBarcodeImageCache();
   void CreateBarcodeEngine();
 
-  BC_TYPE m_type = BC_UNKNOWN;
-  BC_CHAR_ENCODING m_eCharEncoding = CHAR_ENCODING_UTF8;
-  BC_TEXT_LOC m_eTextLocation = BC_TEXT_LOC_NONE;
+  BC_TYPE m_type = BC_TYPE::kUnknown;
   Status m_eStatus = Status::kNormal;
-  bool m_bCalChecksum = false;
-  bool m_bPrintChecksum = false;
-  char m_cStartChar = 0;
-  char m_cEndChar = 0;
-  int8_t m_nWideNarrowRatio = 1;
-  int32_t m_nModuleHeight = -1;
-  int32_t m_nModuleWidth = -1;
-  int32_t m_nDataLength = 0;
-  int32_t m_nECLevel = 0;
-  uint32_t m_dwAttributeMask = 0;
+  absl::optional<BC_TEXT_LOC> m_eTextLocation;
+  absl::optional<BC_CHAR_ENCODING> m_eCharEncoding;
+  absl::optional<bool> m_bCalChecksum;
+  absl::optional<bool> m_bPrintChecksum;
+  absl::optional<char> m_cStartChar;
+  absl::optional<char> m_cEndChar;
+  absl::optional<int8_t> m_nWideNarrowRatio;
+  absl::optional<int32_t> m_nModuleHeight;
+  absl::optional<int32_t> m_nModuleWidth;
+  absl::optional<int32_t> m_nDataLength;
+  absl::optional<int32_t> m_nECLevel;
   std::unique_ptr<CFX_Barcode> m_pBarcodeEngine;
 };
 
diff --git a/xfa/fwl/cfwl_caret.cpp b/xfa/fwl/cfwl_caret.cpp
index ef5bdcc..a573cae 100644
--- a/xfa/fwl/cfwl_caret.cpp
+++ b/xfa/fwl/cfwl_caret.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,11 +8,9 @@
 
 #include <utility>
 
-#include "third_party/base/ptr_util.h"
 #include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fwl/cfwl_themebackground.h"
-#include "xfa/fwl/cfwl_widgetproperties.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
 
 namespace {
@@ -23,10 +21,10 @@
 
 }  // namespace
 
-CFWL_Caret::CFWL_Caret(const CFWL_App* app,
-                       std::unique_ptr<CFWL_WidgetProperties> properties,
+CFWL_Caret::CFWL_Caret(CFWL_App* app,
+                       const Properties& properties,
                        CFWL_Widget* pOuter)
-    : CFWL_Widget(app, std::move(properties), pOuter) {
+    : CFWL_Widget(app, properties, pOuter) {
   SetStates(kStateHighlight);
 }
 
@@ -38,51 +36,42 @@
 
 void CFWL_Caret::Update() {}
 
-void CFWL_Caret::DrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_Caret::DrawWidget(CFGAS_GEGraphics* pGraphics,
                             const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
-  if (!m_pProperties->m_pThemeProvider)
-    m_pProperties->m_pThemeProvider = GetAvailableTheme();
-  if (!m_pProperties->m_pThemeProvider)
-    return;
 
-  DrawCaretBK(pGraphics, m_pProperties->m_pThemeProvider.Get(), &matrix);
+  DrawCaretBK(pGraphics, matrix);
 }
 
 void CFWL_Caret::ShowCaret() {
-  m_pTimer = pdfium::MakeUnique<CFX_Timer>(
-      GetOwnerApp()->GetAdapterNative()->GetTimerHandler(), this,
-      kBlinkPeriodMs);
-  RemoveStates(FWL_WGTSTATE_Invisible);
+  m_pTimer = std::make_unique<CFX_Timer>(GetFWLApp()->GetTimerHandler(), this,
+                                         kBlinkPeriodMs);
+  RemoveStates(FWL_STATE_WGT_Invisible);
   SetStates(kStateHighlight);
 }
 
 void CFWL_Caret::HideCaret() {
   m_pTimer.reset();
-  SetStates(FWL_WGTSTATE_Invisible);
+  SetStates(FWL_STATE_WGT_Invisible);
 }
 
-void CFWL_Caret::DrawCaretBK(CXFA_Graphics* pGraphics,
-                             IFWL_ThemeProvider* pTheme,
-                             const CFX_Matrix* pMatrix) {
-  if (!(m_pProperties->m_dwStates & kStateHighlight))
+void CFWL_Caret::DrawCaretBK(CFGAS_GEGraphics* pGraphics,
+                             const CFX_Matrix& mtMatrix) {
+  if (!(m_Properties.m_dwStates & kStateHighlight))
     return;
 
-  CFWL_ThemeBackground param;
-  param.m_pWidget = this;
-  param.m_pGraphics = pGraphics;
-  param.m_rtPart = CFX_RectF(0, 0, GetWidgetRect().Size());
-  param.m_iPart = CFWL_Part::Background;
-  param.m_dwStates = CFWL_PartState_HightLight;
-  if (pMatrix)
-    param.m_matrix.Concat(*pMatrix);
-  pTheme->DrawBackground(param);
+  CFWL_ThemeBackground param(CFWL_ThemePart::Part::kBackground, this,
+                             pGraphics);
+  param.m_PartRect = CFX_RectF(0, 0, GetWidgetRect().Size());
+  param.m_dwStates = CFWL_PartState::kHightLight;
+  param.m_matrix = mtMatrix;
+  GetThemeProvider()->DrawBackground(param);
 }
 
 void CFWL_Caret::OnProcessMessage(CFWL_Message* pMessage) {}
 
-void CFWL_Caret::OnDrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_Caret::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                               const CFX_Matrix& matrix) {
   DrawWidget(pGraphics, matrix);
 }
diff --git a/xfa/fwl/cfwl_caret.h b/xfa/fwl/cfwl_caret.h
index 7f5dfdf..4c1b395 100644
--- a/xfa/fwl/cfwl_caret.h
+++ b/xfa/fwl/cfwl_caret.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,24 +10,20 @@
 #include <memory>
 
 #include "core/fxcrt/cfx_timer.h"
+#include "xfa/fgas/graphics/cfgas_gecolor.h"
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fxgraphics/cxfa_gecolor.h"
-
-class CFWL_WidgetProperties;
-class CFWL_Widget;
 
 class CFWL_Caret final : public CFWL_Widget, public CFX_Timer::CallbackIface {
  public:
-  CFWL_Caret(const CFWL_App* app,
-             std::unique_ptr<CFWL_WidgetProperties> properties,
-             CFWL_Widget* pOuter);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_Caret() override;
 
   // CFWL_Widget:
   FWL_Type GetClassID() const override;
-  void DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) override;
+  void DrawWidget(CFGAS_GEGraphics* pGraphics,
+                  const CFX_Matrix& matrix) override;
   void OnProcessMessage(CFWL_Message* pMessage) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
+  void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix) override;
   void Update() override;
 
@@ -38,9 +34,9 @@
   void HideCaret();
 
  private:
-  void DrawCaretBK(CXFA_Graphics* pGraphics,
-                   IFWL_ThemeProvider* pTheme,
-                   const CFX_Matrix* pMatrix);
+  CFWL_Caret(CFWL_App* app, const Properties& properties, CFWL_Widget* pOuter);
+
+  void DrawCaretBK(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
 
   std::unique_ptr<CFX_Timer> m_pTimer;
 };
diff --git a/xfa/fwl/cfwl_checkbox.cpp b/xfa/fwl/cfwl_checkbox.cpp
index 1342554..ee78201 100644
--- a/xfa/fwl/cfwl_checkbox.cpp
+++ b/xfa/fwl/cfwl_checkbox.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,8 @@
 #include "xfa/fwl/cfwl_checkbox.h"
 
 #include <algorithm>
-#include <memory>
 #include <utility>
-#include <vector>
 
-#include "third_party/base/ptr_util.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_event.h"
@@ -30,12 +27,12 @@
 
 }  // namespace
 
-CFWL_CheckBox::CFWL_CheckBox(const CFWL_App* app)
-    : CFWL_Widget(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr) {
+CFWL_CheckBox::CFWL_CheckBox(CFWL_App* app)
+    : CFWL_Widget(app, Properties(), nullptr) {
   m_TTOStyles.single_line_ = true;
 }
 
-CFWL_CheckBox::~CFWL_CheckBox() {}
+CFWL_CheckBox::~CFWL_CheckBox() = default;
 
 FWL_Type CFWL_CheckBox::GetClassID() const {
   return FWL_Type::CheckBox;
@@ -48,50 +45,43 @@
 void CFWL_CheckBox::Update() {
   if (IsLocked())
     return;
-  if (!m_pProperties->m_pThemeProvider)
-    m_pProperties->m_pThemeProvider = GetAvailableTheme();
 
   UpdateTextOutStyles();
   Layout();
 }
 
-void CFWL_CheckBox::DrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_CheckBox::DrawWidget(CFGAS_GEGraphics* pGraphics,
                                const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
 
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
-  if (!pTheme)
-    return;
-
   if (HasBorder())
-    DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
+    DrawBorder(pGraphics, CFWL_ThemePart::Part::kBorder, matrix);
 
-  int32_t dwStates = GetPartStates();
-
-  CFWL_ThemeBackground param;
-  param.m_pWidget = this;
-  param.m_iPart = CFWL_Part::Background;
+  Mask<CFWL_PartState> dwStates = GetPartStates();
+  IFWL_ThemeProvider* pTheme = GetThemeProvider();
+  CFWL_ThemeBackground param(CFWL_ThemePart::Part::kBackground, this,
+                             pGraphics);
   param.m_dwStates = dwStates;
-  param.m_pGraphics = pGraphics;
-  param.m_matrix.Concat(matrix);
-  param.m_rtPart = m_rtClient;
-  if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused)
-
-    param.m_pRtData = &m_rtFocus;
+  param.m_matrix = matrix;
+  param.m_PartRect = m_ClientRect;
+  if (m_Properties.m_dwStates & FWL_STATE_WGT_Focused)
+    param.m_pRtData = &m_FocusRect;
   pTheme->DrawBackground(param);
 
-  param.m_iPart = CFWL_Part::CheckBox;
-  param.m_rtPart = m_rtBox;
-  pTheme->DrawBackground(param);
+  CFWL_ThemeBackground checkParam(CFWL_ThemePart::Part::kCheckBox, this,
+                                  pGraphics);
+  checkParam.m_dwStates = dwStates;
+  checkParam.m_matrix = matrix;
+  checkParam.m_PartRect = m_BoxRect;
+  if (m_Properties.m_dwStates & FWL_STATE_WGT_Focused)
+    checkParam.m_pRtData = &m_FocusRect;
+  pTheme->DrawBackground(checkParam);
 
-  CFWL_ThemeText textParam;
-  textParam.m_pWidget = this;
-  textParam.m_iPart = CFWL_Part::Caption;
+  CFWL_ThemeText textParam(CFWL_ThemePart::Part::kCaption, this, pGraphics);
   textParam.m_dwStates = dwStates;
-  textParam.m_pGraphics = pGraphics;
-  textParam.m_matrix.Concat(matrix);
-  textParam.m_rtPart = m_rtCaption;
+  textParam.m_matrix = matrix;
+  textParam.m_PartRect = m_CaptionRect;
   textParam.m_wsText = L"Check box";
   textParam.m_dwTTOStyles = m_TTOStyles;
   textParam.m_iTTOAlign = m_iTTOAlign;
@@ -99,63 +89,60 @@
 }
 
 void CFWL_CheckBox::SetCheckState(int32_t iCheck) {
-  m_pProperties->m_dwStates &= ~FWL_STATE_CKB_CheckMask;
+  m_Properties.m_dwStates &= ~FWL_STATE_CKB_CheckMask;
   switch (iCheck) {
     case 1:
-      m_pProperties->m_dwStates |= FWL_STATE_CKB_Checked;
+      m_Properties.m_dwStates |= FWL_STATE_CKB_Checked;
       break;
     case 2:
-      if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CKB_3State)
-        m_pProperties->m_dwStates |= FWL_STATE_CKB_Neutral;
+      if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_CKB_3State)
+        m_Properties.m_dwStates |= FWL_STATE_CKB_Neutral;
       break;
     default:
       break;
   }
-  RepaintRect(m_rtClient);
+  RepaintRect(m_ClientRect);
 }
 
 void CFWL_CheckBox::Layout() {
-  m_pProperties->m_rtWidget.width =
-      FXSYS_roundf(m_pProperties->m_rtWidget.width);
-  m_pProperties->m_rtWidget.height =
-      FXSYS_roundf(m_pProperties->m_rtWidget.height);
-  m_rtClient = GetClientRect();
+  m_WidgetRect.width = FXSYS_roundf(m_WidgetRect.width);
+  m_WidgetRect.height = FXSYS_roundf(m_WidgetRect.height);
+  m_ClientRect = GetClientRect();
 
-  float fTextLeft = m_rtClient.left + m_fBoxHeight;
-  m_rtBox = CFX_RectF(m_rtClient.TopLeft(), m_fBoxHeight, m_fBoxHeight);
-  m_rtCaption = CFX_RectF(fTextLeft, m_rtClient.top,
-                          m_rtClient.right() - fTextLeft, m_rtClient.height);
-  m_rtCaption.Inflate(-kCaptionMargin, -kCaptionMargin);
+  float fTextLeft = m_ClientRect.left + m_fBoxHeight;
+  m_BoxRect = CFX_RectF(m_ClientRect.TopLeft(), m_fBoxHeight, m_fBoxHeight);
+  m_CaptionRect =
+      CFX_RectF(fTextLeft, m_ClientRect.top, m_ClientRect.right() - fTextLeft,
+                m_ClientRect.height);
+  m_CaptionRect.Inflate(-kCaptionMargin, -kCaptionMargin);
 
-  CFX_RectF rtFocus = m_rtCaption;
-  CalcTextRect(L"Check box", m_pProperties->m_pThemeProvider.Get(), m_TTOStyles,
-               m_iTTOAlign, &rtFocus);
-
-  m_rtFocus = CFX_RectF(m_rtCaption.TopLeft(),
-                        std::max(m_rtCaption.width, rtFocus.width),
-                        std::min(m_rtCaption.height, rtFocus.height));
-  m_rtFocus.Inflate(1, 1);
+  CFX_RectF rtFocus = m_CaptionRect;
+  CalcTextRect(L"Check box", m_TTOStyles, m_iTTOAlign, &rtFocus);
+  m_FocusRect = CFX_RectF(m_CaptionRect.TopLeft(),
+                          std::max(m_CaptionRect.width, rtFocus.width),
+                          std::min(m_CaptionRect.height, rtFocus.height));
+  m_FocusRect.Inflate(1, 1);
 }
 
-uint32_t CFWL_CheckBox::GetPartStates() const {
-  int32_t dwStates = CFWL_PartState_Normal;
-  if ((m_pProperties->m_dwStates & FWL_STATE_CKB_CheckMask) ==
+Mask<CFWL_PartState> CFWL_CheckBox::GetPartStates() const {
+  Mask<CFWL_PartState> dwStates = CFWL_PartState::kNormal;
+  if ((m_Properties.m_dwStates & FWL_STATE_CKB_CheckMask) ==
       FWL_STATE_CKB_Neutral) {
-    dwStates = CFWL_PartState_Neutral;
-  } else if ((m_pProperties->m_dwStates & FWL_STATE_CKB_CheckMask) ==
+    dwStates = CFWL_PartState::kNeutral;
+  } else if ((m_Properties.m_dwStates & FWL_STATE_CKB_CheckMask) ==
              FWL_STATE_CKB_Checked) {
-    dwStates = CFWL_PartState_Checked;
+    dwStates = CFWL_PartState::kChecked;
   }
-  if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
-    dwStates |= CFWL_PartState_Disabled;
-  else if (m_pProperties->m_dwStates & FWL_STATE_CKB_Hovered)
-    dwStates |= CFWL_PartState_Hovered;
-  else if (m_pProperties->m_dwStates & FWL_STATE_CKB_Pressed)
-    dwStates |= CFWL_PartState_Pressed;
+  if (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
+    dwStates |= CFWL_PartState::kDisabled;
+  else if (m_Properties.m_dwStates & FWL_STATE_CKB_Hovered)
+    dwStates |= CFWL_PartState::kHovered;
+  else if (m_Properties.m_dwStates & FWL_STATE_CKB_Pressed)
+    dwStates |= CFWL_PartState::kPressed;
   else
-    dwStates |= CFWL_PartState_Normal;
-  if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused)
-    dwStates |= CFWL_PartState_Focused;
+    dwStates |= CFWL_PartState::kNormal;
+  if (m_Properties.m_dwStates & FWL_STATE_WGT_Focused)
+    dwStates |= CFWL_PartState::kFocused;
   return dwStates;
 }
 
@@ -166,31 +153,31 @@
 }
 
 void CFWL_CheckBox::NextStates() {
-  uint32_t dwFirststate = m_pProperties->m_dwStates;
-  if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CKB_RadioButton) {
-    if ((m_pProperties->m_dwStates & FWL_STATE_CKB_CheckMask) ==
+  uint32_t dwFirststate = m_Properties.m_dwStates;
+  if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_CKB_RadioButton) {
+    if ((m_Properties.m_dwStates & FWL_STATE_CKB_CheckMask) ==
         FWL_STATE_CKB_Unchecked) {
-      m_pProperties->m_dwStates |= FWL_STATE_CKB_Checked;
+      m_Properties.m_dwStates |= FWL_STATE_CKB_Checked;
     }
   } else {
-    if ((m_pProperties->m_dwStates & FWL_STATE_CKB_CheckMask) ==
+    if ((m_Properties.m_dwStates & FWL_STATE_CKB_CheckMask) ==
         FWL_STATE_CKB_Neutral) {
-      m_pProperties->m_dwStates &= ~FWL_STATE_CKB_CheckMask;
-      if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CKB_3State)
-        m_pProperties->m_dwStates |= FWL_STATE_CKB_Checked;
-    } else if ((m_pProperties->m_dwStates & FWL_STATE_CKB_CheckMask) ==
+      m_Properties.m_dwStates &= ~FWL_STATE_CKB_CheckMask;
+      if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_CKB_3State)
+        m_Properties.m_dwStates |= FWL_STATE_CKB_Checked;
+    } else if ((m_Properties.m_dwStates & FWL_STATE_CKB_CheckMask) ==
                FWL_STATE_CKB_Checked) {
-      m_pProperties->m_dwStates &= ~FWL_STATE_CKB_CheckMask;
+      m_Properties.m_dwStates &= ~FWL_STATE_CKB_CheckMask;
     } else {
-      if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CKB_3State)
-        m_pProperties->m_dwStates |= FWL_STATE_CKB_Neutral;
+      if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_CKB_3State)
+        m_Properties.m_dwStates |= FWL_STATE_CKB_Neutral;
       else
-        m_pProperties->m_dwStates |= FWL_STATE_CKB_Checked;
+        m_Properties.m_dwStates |= FWL_STATE_CKB_Checked;
     }
   }
 
-  RepaintRect(m_rtClient);
-  if (dwFirststate == m_pProperties->m_dwStates)
+  RepaintRect(m_ClientRect);
+  if (dwFirststate == m_Properties.m_dwStates)
     return;
 
   CFWL_Event wmCheckBoxState(CFWL_Event::Type::CheckStateChanged, this);
@@ -198,29 +185,26 @@
 }
 
 void CFWL_CheckBox::OnProcessMessage(CFWL_Message* pMessage) {
-  if (!pMessage)
-    return;
-
   switch (pMessage->GetType()) {
-    case CFWL_Message::Type::SetFocus:
-      OnFocusChanged(true);
+    case CFWL_Message::Type::kSetFocus:
+      OnFocusGained();
       break;
-    case CFWL_Message::Type::KillFocus:
-      OnFocusChanged(false);
+    case CFWL_Message::Type::kKillFocus:
+      OnFocusLost();
       break;
-    case CFWL_Message::Type::Mouse: {
+    case CFWL_Message::Type::kMouse: {
       CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
       switch (pMsg->m_dwCmd) {
-        case FWL_MouseCommand::LeftButtonDown:
+        case CFWL_MessageMouse::MouseCommand::kLeftButtonDown:
           OnLButtonDown();
           break;
-        case FWL_MouseCommand::LeftButtonUp:
+        case CFWL_MessageMouse::MouseCommand::kLeftButtonUp:
           OnLButtonUp(pMsg);
           break;
-        case FWL_MouseCommand::Move:
+        case CFWL_MessageMouse::MouseCommand::kMove:
           OnMouseMove(pMsg);
           break;
-        case FWL_MouseCommand::Leave:
+        case CFWL_MessageMouse::MouseCommand::kLeave:
           OnMouseLeave();
           break;
         default:
@@ -228,9 +212,9 @@
       }
       break;
     }
-    case CFWL_Message::Type::Key: {
+    case CFWL_Message::Type::kKey: {
       CFWL_MessageKey* pKey = static_cast<CFWL_MessageKey*>(pMessage);
-      if (pKey->m_dwCmd == FWL_KeyCommand::KeyDown)
+      if (pKey->m_dwCmd == CFWL_MessageKey::KeyCommand::kKeyDown)
         OnKeyDown(pKey);
       break;
     }
@@ -242,28 +226,29 @@
     CFWL_Widget::OnProcessMessage(pMessage);
 }
 
-void CFWL_CheckBox::OnDrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_CheckBox::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                                  const CFX_Matrix& matrix) {
   DrawWidget(pGraphics, matrix);
 }
 
-void CFWL_CheckBox::OnFocusChanged(bool bSet) {
-  if (bSet)
-    m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused;
-  else
-    m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused;
+void CFWL_CheckBox::OnFocusGained() {
+  m_Properties.m_dwStates |= FWL_STATE_WGT_Focused;
+  RepaintRect(m_ClientRect);
+}
 
-  RepaintRect(m_rtClient);
+void CFWL_CheckBox::OnFocusLost() {
+  m_Properties.m_dwStates &= ~FWL_STATE_WGT_Focused;
+  RepaintRect(m_ClientRect);
 }
 
 void CFWL_CheckBox::OnLButtonDown() {
-  if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
+  if (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
     return;
 
   m_bBtnDown = true;
-  m_pProperties->m_dwStates &= ~FWL_STATE_CKB_Hovered;
-  m_pProperties->m_dwStates |= FWL_STATE_CKB_Pressed;
-  RepaintRect(m_rtClient);
+  m_Properties.m_dwStates &= ~FWL_STATE_CKB_Hovered;
+  m_Properties.m_dwStates |= FWL_STATE_CKB_Pressed;
+  RepaintRect(m_ClientRect);
 }
 
 void CFWL_CheckBox::OnLButtonUp(CFWL_MessageMouse* pMsg) {
@@ -271,65 +256,65 @@
     return;
 
   m_bBtnDown = false;
-  if (!m_rtClient.Contains(pMsg->m_pos))
+  if (!m_ClientRect.Contains(pMsg->m_pos))
     return;
 
-  m_pProperties->m_dwStates |= FWL_STATE_CKB_Hovered;
-  m_pProperties->m_dwStates &= ~FWL_STATE_CKB_Pressed;
+  m_Properties.m_dwStates |= FWL_STATE_CKB_Hovered;
+  m_Properties.m_dwStates &= ~FWL_STATE_CKB_Pressed;
   NextStates();
 }
 
 void CFWL_CheckBox::OnMouseMove(CFWL_MessageMouse* pMsg) {
-  if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
+  if (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
     return;
 
   bool bRepaint = false;
   if (m_bBtnDown) {
-    if (m_rtClient.Contains(pMsg->m_pos)) {
-      if ((m_pProperties->m_dwStates & FWL_STATE_CKB_Pressed) == 0) {
+    if (m_ClientRect.Contains(pMsg->m_pos)) {
+      if ((m_Properties.m_dwStates & FWL_STATE_CKB_Pressed) == 0) {
         bRepaint = true;
-        m_pProperties->m_dwStates |= FWL_STATE_CKB_Pressed;
+        m_Properties.m_dwStates |= FWL_STATE_CKB_Pressed;
       }
-      if ((m_pProperties->m_dwStates & FWL_STATE_CKB_Hovered)) {
+      if ((m_Properties.m_dwStates & FWL_STATE_CKB_Hovered)) {
         bRepaint = true;
-        m_pProperties->m_dwStates &= ~FWL_STATE_CKB_Hovered;
+        m_Properties.m_dwStates &= ~FWL_STATE_CKB_Hovered;
       }
     } else {
-      if (m_pProperties->m_dwStates & FWL_STATE_CKB_Pressed) {
+      if (m_Properties.m_dwStates & FWL_STATE_CKB_Pressed) {
         bRepaint = true;
-        m_pProperties->m_dwStates &= ~FWL_STATE_CKB_Pressed;
+        m_Properties.m_dwStates &= ~FWL_STATE_CKB_Pressed;
       }
-      if ((m_pProperties->m_dwStates & FWL_STATE_CKB_Hovered) == 0) {
+      if ((m_Properties.m_dwStates & FWL_STATE_CKB_Hovered) == 0) {
         bRepaint = true;
-        m_pProperties->m_dwStates |= FWL_STATE_CKB_Hovered;
+        m_Properties.m_dwStates |= FWL_STATE_CKB_Hovered;
       }
     }
   } else {
-    if (m_rtClient.Contains(pMsg->m_pos)) {
-      if ((m_pProperties->m_dwStates & FWL_STATE_CKB_Hovered) == 0) {
+    if (m_ClientRect.Contains(pMsg->m_pos)) {
+      if ((m_Properties.m_dwStates & FWL_STATE_CKB_Hovered) == 0) {
         bRepaint = true;
-        m_pProperties->m_dwStates |= FWL_STATE_CKB_Hovered;
+        m_Properties.m_dwStates |= FWL_STATE_CKB_Hovered;
       }
     }
   }
   if (bRepaint)
-    RepaintRect(m_rtBox);
+    RepaintRect(m_BoxRect);
 }
 
 void CFWL_CheckBox::OnMouseLeave() {
   if (m_bBtnDown)
-    m_pProperties->m_dwStates |= FWL_STATE_CKB_Hovered;
+    m_Properties.m_dwStates |= FWL_STATE_CKB_Hovered;
   else
-    m_pProperties->m_dwStates &= ~FWL_STATE_CKB_Hovered;
+    m_Properties.m_dwStates &= ~FWL_STATE_CKB_Hovered;
 
-  RepaintRect(m_rtBox);
+  RepaintRect(m_BoxRect);
 }
 
 void CFWL_CheckBox::OnKeyDown(CFWL_MessageKey* pMsg) {
-  if (pMsg->m_dwKeyCode == XFA_FWL_VKEY_Tab)
+  if (pMsg->m_dwKeyCodeOrChar == XFA_FWL_VKEY_Tab)
     return;
-  if (pMsg->m_dwKeyCode == XFA_FWL_VKEY_Return ||
-      pMsg->m_dwKeyCode == XFA_FWL_VKEY_Space) {
+  if (pMsg->m_dwKeyCodeOrChar == XFA_FWL_VKEY_Return ||
+      pMsg->m_dwKeyCodeOrChar == XFA_FWL_VKEY_Space) {
     NextStates();
   }
 }
diff --git a/xfa/fwl/cfwl_checkbox.h b/xfa/fwl/cfwl_checkbox.h
index 0fa0236..9ab1cb8 100644
--- a/xfa/fwl/cfwl_checkbox.h
+++ b/xfa/fwl/cfwl_checkbox.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,6 @@
 
 #include "xfa/fwl/cfwl_event.h"
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fwl/cfwl_widgetproperties.h"
 
 #define FWL_STYLEEXT_CKB_3State (1L << 6)
 #define FWL_STYLEEXT_CKB_RadioButton (1L << 7)
@@ -20,50 +19,53 @@
 #define FWL_STYLEEXT_CKB_SignShapeSquare (4L << 10)
 #define FWL_STYLEEXT_CKB_SignShapeStar (5L << 10)
 #define FWL_STYLEEXT_CKB_SignShapeMask (7L << 10)
-#define FWL_STATE_CKB_Hovered (1 << FWL_WGTSTATE_MAX)
-#define FWL_STATE_CKB_Pressed (1 << (FWL_WGTSTATE_MAX + 1))
+#define FWL_STATE_CKB_Hovered (1 << FWL_STATE_WGT_MAX)
+#define FWL_STATE_CKB_Pressed (1 << (FWL_STATE_WGT_MAX + 1))
 #define FWL_STATE_CKB_Unchecked 0
-#define FWL_STATE_CKB_Checked (1 << (FWL_WGTSTATE_MAX + 2))
-#define FWL_STATE_CKB_Neutral (2 << (FWL_WGTSTATE_MAX + 2))
-#define FWL_STATE_CKB_CheckMask (3L << (FWL_WGTSTATE_MAX + 2))
+#define FWL_STATE_CKB_Checked (1 << (FWL_STATE_WGT_MAX + 2))
+#define FWL_STATE_CKB_Neutral (2 << (FWL_STATE_WGT_MAX + 2))
+#define FWL_STATE_CKB_CheckMask (3L << (FWL_STATE_WGT_MAX + 2))
 
+class CFWL_MessageKey;
 class CFWL_MessageMouse;
-class CFWL_WidgetProperties;
-class CFWL_Widget;
 
 class CFWL_CheckBox final : public CFWL_Widget {
  public:
-  explicit CFWL_CheckBox(const CFWL_App* pApp);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_CheckBox() override;
 
   // CFWL_Widget
   FWL_Type GetClassID() const override;
   void Update() override;
-  void DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) override;
+  void DrawWidget(CFGAS_GEGraphics* pGraphics,
+                  const CFX_Matrix& matrix) override;
 
   void OnProcessMessage(CFWL_Message* pMessage) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
+  void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix) override;
 
   void SetBoxSize(float fHeight);
 
  private:
+  explicit CFWL_CheckBox(CFWL_App* pApp);
+
   void SetCheckState(int32_t iCheck);
   void Layout();
-  uint32_t GetPartStates() const;
+  Mask<CFWL_PartState> GetPartStates() const;
   void UpdateTextOutStyles();
   void NextStates();
-  void OnFocusChanged(bool bSet);
+  void OnFocusGained();
+  void OnFocusLost();
   void OnLButtonDown();
   void OnLButtonUp(CFWL_MessageMouse* pMsg);
   void OnMouseMove(CFWL_MessageMouse* pMsg);
   void OnMouseLeave();
   void OnKeyDown(CFWL_MessageKey* pMsg);
 
-  CFX_RectF m_rtClient;
-  CFX_RectF m_rtBox;
-  CFX_RectF m_rtCaption;
-  CFX_RectF m_rtFocus;
+  CFX_RectF m_ClientRect;
+  CFX_RectF m_BoxRect;
+  CFX_RectF m_CaptionRect;
+  CFX_RectF m_FocusRect;
   FDE_TextStyle m_TTOStyles;
   FDE_TextAlignment m_iTTOAlign = FDE_TextAlignment::kCenter;
   bool m_bBtnDown = false;
diff --git a/xfa/fwl/cfwl_combobox.cpp b/xfa/fwl/cfwl_combobox.cpp
index 9c5edf4..7653b83 100644
--- a/xfa/fwl/cfwl_combobox.cpp
+++ b/xfa/fwl/cfwl_combobox.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,11 +6,7 @@
 
 #include "xfa/fwl/cfwl_combobox.h"
 
-#include <algorithm>
-#include <memory>
-#include <utility>
-
-#include "third_party/base/ptr_util.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fde/cfde_texteditengine.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fwl/cfwl_app.h"
@@ -29,14 +25,28 @@
 #include "xfa/fwl/fwl_widgetdef.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
 
-CFWL_ComboBox::CFWL_ComboBox(const CFWL_App* app)
-    : CFWL_Widget(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr) {
-  InitComboList();
-  InitComboEdit();
-}
+CFWL_ComboBox::CFWL_ComboBox(CFWL_App* app)
+    : CFWL_Widget(app, Properties(), nullptr),
+      m_pEdit(cppgc::MakeGarbageCollected<CFWL_ComboEdit>(
+          app->GetHeap()->GetAllocationHandle(),
+          app,
+          Properties(),
+          this)),
+      m_pListBox(cppgc::MakeGarbageCollected<CFWL_ComboList>(
+          app->GetHeap()->GetAllocationHandle(),
+          app,
+          Properties{FWL_STYLE_WGT_Border | FWL_STYLE_WGT_VScroll, 0,
+                     FWL_STATE_WGT_Invisible},
+          this)) {}
 
 CFWL_ComboBox::~CFWL_ComboBox() = default;
 
+void CFWL_ComboBox::Trace(cppgc::Visitor* visitor) const {
+  CFWL_Widget::Trace(visitor);
+  visitor->Trace(m_pEdit);
+  visitor->Trace(m_pListBox);
+}
+
 FWL_Type CFWL_ComboBox::GetClassID() const {
   return FWL_Type::ComboBox;
 }
@@ -53,39 +63,35 @@
   m_pListBox->DeleteAll();
 }
 
-void CFWL_ComboBox::ModifyStylesEx(uint32_t dwStylesExAdded,
-                                   uint32_t dwStylesExRemoved) {
-  if (!m_pEdit)
-    InitComboEdit();
-
-  bool bAddDropDown = !!(dwStylesExAdded & FWL_STYLEEXT_CMB_DropDown);
-  bool bDelDropDown = !!(dwStylesExRemoved & FWL_STYLEEXT_CMB_DropDown);
-
-  dwStylesExRemoved &= ~FWL_STYLEEXT_CMB_DropDown;
-  m_pProperties->m_dwStyleExes |= FWL_STYLEEXT_CMB_DropDown;
-
+void CFWL_ComboBox::ModifyStyleExts(uint32_t dwStyleExtsAdded,
+                                    uint32_t dwStyleExtsRemoved) {
+  bool bAddDropDown = !!(dwStyleExtsAdded & FWL_STYLEEXT_CMB_DropDown);
+  bool bDelDropDown = !!(dwStyleExtsRemoved & FWL_STYLEEXT_CMB_DropDown);
+  dwStyleExtsRemoved &= ~FWL_STYLEEXT_CMB_DropDown;
+  m_Properties.m_dwStyleExts |= FWL_STYLEEXT_CMB_DropDown;
   if (bAddDropDown)
-    m_pEdit->ModifyStylesEx(0, FWL_STYLEEXT_EDT_ReadOnly);
+    m_pEdit->ModifyStyleExts(0, FWL_STYLEEXT_EDT_ReadOnly);
   else if (bDelDropDown)
-    m_pEdit->ModifyStylesEx(FWL_STYLEEXT_EDT_ReadOnly, 0);
-  CFWL_Widget::ModifyStylesEx(dwStylesExAdded, dwStylesExRemoved);
+    m_pEdit->ModifyStyleExts(FWL_STYLEEXT_EDT_ReadOnly, 0);
+
+  CFWL_Widget::ModifyStyleExts(dwStyleExtsAdded, dwStyleExtsRemoved);
 }
 
 void CFWL_ComboBox::Update() {
-  if (m_iLock)
+  if (IsLocked())
     return;
+
   if (m_pEdit)
     ResetEditAlignment();
-  ResetTheme();
   Layout();
 }
 
 FWL_WidgetHit CFWL_ComboBox::HitTest(const CFX_PointF& point) {
-  CFX_RectF rect(0, 0, m_pProperties->m_rtWidget.width - m_rtBtn.width,
-                 m_pProperties->m_rtWidget.height);
+  CFX_RectF rect(0, 0, m_WidgetRect.width - m_BtnRect.width,
+                 m_WidgetRect.height);
   if (rect.Contains(point))
     return FWL_WidgetHit::Edit;
-  if (m_rtBtn.Contains(point))
+  if (m_BtnRect.Contains(point))
     return FWL_WidgetHit::Client;
   if (IsDropListVisible()) {
     rect = m_pListBox->GetWidgetRect();
@@ -95,22 +101,17 @@
   return FWL_WidgetHit::Unknown;
 }
 
-void CFWL_ComboBox::DrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_ComboBox::DrawWidget(CFGAS_GEGraphics* pGraphics,
                                const CFX_Matrix& matrix) {
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
-  pGraphics->SaveGraphState();
-  pGraphics->ConcatMatrix(&matrix);
-  if (!m_rtBtn.IsEmpty(0.1f)) {
-    CFWL_ThemeBackground param;
-    param.m_pWidget = this;
-    param.m_iPart = CFWL_Part::DropDownButton;
+  if (!m_BtnRect.IsEmpty(0.1f)) {
+    CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
+    pGraphics->ConcatMatrix(matrix);
+    CFWL_ThemeBackground param(CFWL_ThemePart::Part::kDropDownButton, this,
+                               pGraphics);
     param.m_dwStates = m_iBtnState;
-    param.m_pGraphics = pGraphics;
-    param.m_rtPart = m_rtBtn;
-    pTheme->DrawBackground(param);
+    param.m_PartRect = m_BtnRect;
+    GetThemeProvider()->DrawBackground(param);
   }
-  pGraphics->RestoreGraphState();
-
   if (m_pEdit) {
     CFX_RectF rtEdit = m_pEdit->GetWidgetRect();
     CFX_Matrix mt(1, 0, 0, 1, rtEdit.left, rtEdit.top);
@@ -125,20 +126,8 @@
   }
 }
 
-void CFWL_ComboBox::SetThemeProvider(IFWL_ThemeProvider* pThemeProvider) {
-  if (!pThemeProvider)
-    return;
-
-  m_pProperties->m_pThemeProvider = pThemeProvider;
-  if (m_pListBox)
-    m_pListBox->SetThemeProvider(pThemeProvider);
-  if (m_pEdit)
-    m_pEdit->SetThemeProvider(pThemeProvider);
-}
-
 WideString CFWL_ComboBox::GetTextByIndex(int32_t iIndex) const {
-  CFWL_ListItem* pItem = static_cast<CFWL_ListItem*>(
-      m_pListBox->GetItem(m_pListBox.get(), iIndex));
+  CFWL_ListBox::Item* pItem = m_pListBox->GetItem(m_pListBox, iIndex);
   return pItem ? pItem->GetText() : WideString();
 }
 
@@ -149,7 +138,7 @@
     if (bClearSel) {
       m_pEdit->SetText(WideString());
     } else {
-      CFWL_ListItem* hItem = m_pListBox->GetItem(this, iSel);
+      CFWL_ListBox::Item* hItem = m_pListBox->GetItem(this, iSel);
       m_pEdit->SetText(hItem ? hItem->GetText() : WideString());
     }
     m_pEdit->Update();
@@ -187,16 +176,12 @@
   if (!m_pListBox)
     return WideString();
 
-  CFWL_ListItem* hItem = m_pListBox->GetItem(this, m_iCurSel);
+  CFWL_ListBox::Item* hItem = m_pListBox->GetItem(this, m_iCurSel);
   return hItem ? hItem->GetText() : WideString();
 }
 
-void CFWL_ComboBox::OpenDropDownList(bool bActivate) {
-  ShowDropList(bActivate);
-}
-
 CFX_RectF CFWL_ComboBox::GetBBox() const {
-  CFX_RectF rect = m_pProperties->m_rtWidget;
+  CFX_RectF rect = m_WidgetRect;
   if (!m_pListBox || !IsDropListVisible())
     return rect;
 
@@ -206,52 +191,56 @@
   return rect;
 }
 
-void CFWL_ComboBox::EditModifyStylesEx(uint32_t dwStylesExAdded,
-                                       uint32_t dwStylesExRemoved) {
+void CFWL_ComboBox::EditModifyStyleExts(uint32_t dwStyleExtsAdded,
+                                        uint32_t dwStyleExtsRemoved) {
   if (m_pEdit)
-    m_pEdit->ModifyStylesEx(dwStylesExAdded, dwStylesExRemoved);
+    m_pEdit->ModifyStyleExts(dwStyleExtsAdded, dwStyleExtsRemoved);
 }
 
-void CFWL_ComboBox::ShowDropList(bool bActivate) {
-  if (IsDropListVisible() == bActivate)
+void CFWL_ComboBox::ShowDropDownList() {
+  if (IsDropListVisible())
     return;
 
-  if (bActivate) {
-    CFWL_Event preEvent(CFWL_Event::Type::PreDropDown, this);
-    DispatchEvent(&preEvent);
-    if (!preEvent.GetSrcTarget())
-      return;
+  CFWL_Event preEvent(CFWL_Event::Type::PreDropDown, this);
+  DispatchEvent(&preEvent);
+  if (!preEvent.GetSrcTarget())
+    return;
 
-    CFWL_ComboList* pComboList = m_pListBox.get();
-    int32_t iItems = pComboList->CountItems(nullptr);
-    if (iItems < 1)
-      return;
+  CFWL_ComboList* pComboList = m_pListBox;
+  int32_t iItems = pComboList->CountItems(nullptr);
+  if (iItems < 1)
+    return;
 
-    ResetListItemAlignment();
-    pComboList->ChangeSelected(m_iCurSel);
+  ResetListItemAlignment();
+  pComboList->ChangeSelected(m_iCurSel);
 
-    float fItemHeight = pComboList->CalcItemHeight();
-    float fBorder = GetCXBorderSize();
-    float fPopupMin = 0.0f;
-    if (iItems > 3)
-      fPopupMin = fItemHeight * 3 + fBorder * 2;
+  float fItemHeight = pComboList->CalcItemHeight();
+  float fBorder = GetCXBorderSize();
+  float fPopupMin = 0.0f;
+  if (iItems > 3)
+    fPopupMin = fItemHeight * 3 + fBorder * 2;
 
-    float fPopupMax = fItemHeight * iItems + fBorder * 2;
-    CFX_RectF rtList(m_rtClient.left, 0, m_pProperties->m_rtWidget.width, 0);
-    GetPopupPos(fPopupMin, fPopupMax, m_pProperties->m_rtWidget, &rtList);
+  float fPopupMax = fItemHeight * iItems + fBorder * 2;
+  CFX_RectF rtList(m_ClientRect.left, 0, m_WidgetRect.width, 0);
+  GetPopupPos(fPopupMin, fPopupMax, m_WidgetRect, &rtList);
+  m_pListBox->SetWidgetRect(rtList);
+  m_pListBox->Update();
+  m_pListBox->RemoveStates(FWL_STATE_WGT_Invisible);
 
-    m_pListBox->SetWidgetRect(rtList);
-    m_pListBox->Update();
-  }
+  CFWL_Event postEvent(CFWL_Event::Type::PostDropDown, this);
+  DispatchEvent(&postEvent);
+  RepaintInflatedListBoxRect();
+}
 
-  if (bActivate) {
-    m_pListBox->RemoveStates(FWL_WGTSTATE_Invisible);
-    CFWL_Event postEvent(CFWL_Event::Type::PostDropDown, this);
-    DispatchEvent(&postEvent);
-  } else {
-    m_pListBox->SetStates(FWL_WGTSTATE_Invisible);
-  }
+void CFWL_ComboBox::HideDropDownList() {
+  if (!IsDropListVisible())
+    return;
 
+  m_pListBox->SetStates(FWL_STATE_WGT_Invisible);
+  RepaintInflatedListBoxRect();
+}
+
+void CFWL_ComboBox::RepaintInflatedListBoxRect() {
   CFX_RectF rect = m_pListBox->GetWidgetRect();
   rect.Inflate(2, 2);
   RepaintRect(rect);
@@ -271,65 +260,51 @@
 }
 
 void CFWL_ComboBox::SyncEditText(int32_t iListItem) {
-  CFWL_ListItem* hItem = m_pListBox->GetItem(this, iListItem);
+  CFWL_ListBox::Item* hItem = m_pListBox->GetItem(this, iListItem);
   m_pEdit->SetText(hItem ? hItem->GetText() : WideString());
   m_pEdit->Update();
   m_pEdit->SetSelected();
 }
 
 void CFWL_ComboBox::Layout() {
-  m_rtClient = GetClientRect();
-  m_rtContent = m_rtClient;
-  IFWL_ThemeProvider* theme = GetAvailableTheme();
-  if (!theme)
-    return;
+  m_ClientRect = GetClientRect();
+  m_ContentRect = m_ClientRect;
 
+  IFWL_ThemeProvider* theme = GetThemeProvider();
   float borderWidth = 1;
   float fBtn = theme->GetScrollBarWidth();
-  if (!(GetStylesEx() & FWL_STYLEEXT_CMB_ReadOnly)) {
-    m_rtBtn =
-        CFX_RectF(m_rtClient.right() - fBtn, m_rtClient.top + borderWidth,
-                  fBtn - borderWidth, m_rtClient.height - 2 * borderWidth);
+  if (!(GetStyleExts() & FWL_STYLEEXT_CMB_ReadOnly)) {
+    m_BtnRect =
+        CFX_RectF(m_ClientRect.right() - fBtn, m_ClientRect.top + borderWidth,
+                  fBtn - borderWidth, m_ClientRect.height - 2 * borderWidth);
   }
 
-  CFWL_ThemePart part;
-  part.m_pWidget = this;
+  CFWL_ThemePart part(CFWL_ThemePart::Part::kNone, this);
   CFX_RectF pUIMargin = theme->GetUIMargin(part);
-  m_rtContent.Deflate(pUIMargin.left, pUIMargin.top, pUIMargin.width,
-                      pUIMargin.height);
+  m_ContentRect.Deflate(pUIMargin.left, pUIMargin.top, pUIMargin.width,
+                        pUIMargin.height);
 
   if (!IsDropDownStyle() || !m_pEdit)
     return;
 
-  CFX_RectF rtEdit(m_rtContent.left, m_rtContent.top, m_rtContent.width - fBtn,
-                   m_rtContent.height);
+  CFX_RectF rtEdit(m_ContentRect.left, m_ContentRect.top,
+                   m_ContentRect.width - fBtn, m_ContentRect.height);
   m_pEdit->SetWidgetRect(rtEdit);
 
   if (m_iCurSel >= 0) {
-    CFWL_ListItem* hItem = m_pListBox->GetItem(this, m_iCurSel);
-    ScopedUpdateLock update_lock(m_pEdit.get());
+    CFWL_ListBox::Item* hItem = m_pListBox->GetItem(this, m_iCurSel);
+    ScopedUpdateLock update_lock(m_pEdit);
     m_pEdit->SetText(hItem ? hItem->GetText() : WideString());
   }
   m_pEdit->Update();
 }
 
-void CFWL_ComboBox::ResetTheme() {
-  if (!m_pProperties->m_pThemeProvider)
-    m_pProperties->m_pThemeProvider = GetAvailableTheme();
-
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
-  if (m_pListBox && !m_pListBox->GetThemeProvider())
-    m_pListBox->SetThemeProvider(pTheme);
-  if (m_pEdit && !m_pEdit->GetThemeProvider())
-    m_pEdit->SetThemeProvider(pTheme);
-}
-
 void CFWL_ComboBox::ResetEditAlignment() {
   if (!m_pEdit)
     return;
 
   uint32_t dwAdd = 0;
-  switch (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CMB_EditHAlignMask) {
+  switch (m_Properties.m_dwStyleExts & FWL_STYLEEXT_CMB_EditHAlignMask) {
     case FWL_STYLEEXT_CMB_EditHCenter: {
       dwAdd |= FWL_STYLEEXT_EDT_HCenter;
       break;
@@ -339,7 +314,7 @@
       break;
     }
   }
-  switch (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CMB_EditVAlignMask) {
+  switch (m_Properties.m_dwStyleExts & FWL_STYLEEXT_CMB_EditVAlignMask) {
     case FWL_STYLEEXT_CMB_EditVCenter: {
       dwAdd |= FWL_STYLEEXT_EDT_VCenter;
       break;
@@ -353,12 +328,12 @@
       break;
     }
   }
-  if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CMB_EditJustified)
+  if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_CMB_EditJustified)
     dwAdd |= FWL_STYLEEXT_EDT_Justified;
 
-  m_pEdit->ModifyStylesEx(dwAdd, FWL_STYLEEXT_EDT_HAlignMask |
-                                     FWL_STYLEEXT_EDT_HAlignModeMask |
-                                     FWL_STYLEEXT_EDT_VAlignMask);
+  m_pEdit->ModifyStyleExts(dwAdd, FWL_STYLEEXT_EDT_HAlignMask |
+                                      FWL_STYLEEXT_EDT_HAlignModeMask |
+                                      FWL_STYLEEXT_EDT_VAlignMask);
 }
 
 void CFWL_ComboBox::ResetListItemAlignment() {
@@ -366,7 +341,7 @@
     return;
 
   uint32_t dwAdd = 0;
-  switch (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CMB_ListItemAlignMask) {
+  switch (m_Properties.m_dwStyleExts & FWL_STYLEEXT_CMB_ListItemAlignMask) {
     case FWL_STYLEEXT_CMB_ListItemCenterAlign: {
       dwAdd |= FWL_STYLEEXT_LTB_CenterAlign;
       break;
@@ -376,80 +351,49 @@
       break;
     }
   }
-  m_pListBox->ModifyStylesEx(dwAdd, FWL_STYLEEXT_CMB_ListItemAlignMask);
+  m_pListBox->ModifyStyleExts(dwAdd, FWL_STYLEEXT_CMB_ListItemAlignMask);
 }
 
 void CFWL_ComboBox::ProcessSelChanged(bool bLButtonUp) {
   m_iCurSel = m_pListBox->GetItemIndex(this, m_pListBox->GetSelItem(0));
   if (!IsDropDownStyle()) {
-    RepaintRect(m_rtClient);
+    RepaintRect(m_ClientRect);
     return;
   }
-
-  CFWL_ListItem* hItem = m_pListBox->GetItem(this, m_iCurSel);
+  CFWL_ListBox::Item* hItem = m_pListBox->GetItem(this, m_iCurSel);
   if (!hItem)
     return;
+
   if (m_pEdit) {
     m_pEdit->SetText(hItem->GetText());
     m_pEdit->Update();
     m_pEdit->SetSelected();
   }
-
-  CFWL_EventSelectChanged ev(this);
-  ev.bLButtonUp = bLButtonUp;
+  CFWL_EventSelectChanged ev(this, bLButtonUp);
   DispatchEvent(&ev);
 }
 
-void CFWL_ComboBox::InitComboList() {
-  if (m_pListBox)
-    return;
-
-  auto prop = pdfium::MakeUnique<CFWL_WidgetProperties>();
-  prop->m_pParent = this;
-  prop->m_dwStyles = FWL_WGTSTYLE_Border | FWL_WGTSTYLE_VScroll;
-  prop->m_dwStates = FWL_WGTSTATE_Invisible;
-  prop->m_pThemeProvider = m_pProperties->m_pThemeProvider;
-  m_pListBox = pdfium::MakeUnique<CFWL_ComboList>(m_pOwnerApp.Get(),
-                                                  std::move(prop), this);
-}
-
-void CFWL_ComboBox::InitComboEdit() {
-  if (m_pEdit)
-    return;
-
-  auto prop = pdfium::MakeUnique<CFWL_WidgetProperties>();
-  prop->m_pParent = this;
-  prop->m_pThemeProvider = m_pProperties->m_pThemeProvider;
-
-  m_pEdit = pdfium::MakeUnique<CFWL_ComboEdit>(m_pOwnerApp.Get(),
-                                               std::move(prop), this);
-  m_pEdit->SetOuter(this);
-}
-
 void CFWL_ComboBox::OnProcessMessage(CFWL_Message* pMessage) {
-  if (!pMessage)
-    return;
-
   bool backDefault = true;
   switch (pMessage->GetType()) {
-    case CFWL_Message::Type::SetFocus: {
+    case CFWL_Message::Type::kSetFocus: {
       backDefault = false;
-      OnFocusChanged(pMessage, true);
+      OnFocusGained();
       break;
     }
-    case CFWL_Message::Type::KillFocus: {
+    case CFWL_Message::Type::kKillFocus: {
       backDefault = false;
-      OnFocusChanged(pMessage, false);
+      OnFocusLost();
       break;
     }
-    case CFWL_Message::Type::Mouse: {
+    case CFWL_Message::Type::kMouse: {
       backDefault = false;
       CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
       switch (pMsg->m_dwCmd) {
-        case FWL_MouseCommand::LeftButtonDown:
+        case CFWL_MessageMouse::MouseCommand::kLeftButtonDown:
           OnLButtonDown(pMsg);
           break;
-        case FWL_MouseCommand::LeftButtonUp:
+        case CFWL_MessageMouse::MouseCommand::kLeftButtonUp:
           OnLButtonUp(pMsg);
           break;
         default:
@@ -457,16 +401,15 @@
       }
       break;
     }
-    case CFWL_Message::Type::Key: {
+    case CFWL_Message::Type::kKey: {
       backDefault = false;
       CFWL_MessageKey* pKey = static_cast<CFWL_MessageKey*>(pMessage);
-      if (pKey->m_dwCmd == FWL_KeyCommand::KeyUp)
-        break;
-      if (IsDropListVisible() && pKey->m_dwCmd == FWL_KeyCommand::KeyDown) {
-        bool bListKey = pKey->m_dwKeyCode == XFA_FWL_VKEY_Up ||
-                        pKey->m_dwKeyCode == XFA_FWL_VKEY_Down ||
-                        pKey->m_dwKeyCode == XFA_FWL_VKEY_Return ||
-                        pKey->m_dwKeyCode == XFA_FWL_VKEY_Escape;
+      if (IsDropListVisible() &&
+          pKey->m_dwCmd == CFWL_MessageKey::KeyCommand::kKeyDown) {
+        bool bListKey = pKey->m_dwKeyCodeOrChar == XFA_FWL_VKEY_Up ||
+                        pKey->m_dwKeyCodeOrChar == XFA_FWL_VKEY_Down ||
+                        pKey->m_dwKeyCodeOrChar == XFA_FWL_VKEY_Return ||
+                        pKey->m_dwKeyCodeOrChar == XFA_FWL_VKEY_Escape;
         if (bListKey) {
           m_pListBox->GetDelegate()->OnProcessMessage(pMessage);
           break;
@@ -487,9 +430,8 @@
   CFWL_Event::Type type = pEvent->GetType();
   if (type == CFWL_Event::Type::Scroll) {
     CFWL_EventScroll* pScrollEvent = static_cast<CFWL_EventScroll*>(pEvent);
-    CFWL_EventScroll pScrollEv(this);
-    pScrollEv.m_iScrollCode = pScrollEvent->m_iScrollCode;
-    pScrollEv.m_fPos = pScrollEvent->m_fPos;
+    CFWL_EventScroll pScrollEv(this, pScrollEvent->GetScrollCode(),
+                               pScrollEvent->GetPos());
     DispatchEvent(&pScrollEv);
   } else if (type == CFWL_Event::Type::TextWillChange) {
     CFWL_Event pTemp(CFWL_Event::Type::EditChanged, this);
@@ -497,56 +439,55 @@
   }
 }
 
-void CFWL_ComboBox::OnDrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_ComboBox::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                                  const CFX_Matrix& matrix) {
   DrawWidget(pGraphics, matrix);
 }
 
 void CFWL_ComboBox::OnLButtonUp(CFWL_MessageMouse* pMsg) {
-  if (m_rtBtn.Contains(pMsg->m_pos))
-    m_iBtnState = CFWL_PartState_Hovered;
+  if (m_BtnRect.Contains(pMsg->m_pos))
+    m_iBtnState = CFWL_PartState::kHovered;
   else
-    m_iBtnState = CFWL_PartState_Normal;
+    m_iBtnState = CFWL_PartState::kNormal;
 
-  RepaintRect(m_rtBtn);
+  RepaintRect(m_BtnRect);
 }
 
 void CFWL_ComboBox::OnLButtonDown(CFWL_MessageMouse* pMsg) {
-  bool bDropDown = IsDropListVisible();
-  CFX_RectF& rtBtn = bDropDown ? m_rtBtn : m_rtClient;
-  if (!rtBtn.Contains(pMsg->m_pos))
-    return;
-
   if (IsDropListVisible()) {
-    ShowDropList(false);
+    if (m_BtnRect.Contains(pMsg->m_pos))
+      HideDropDownList();
     return;
   }
+  if (!m_ClientRect.Contains(pMsg->m_pos))
+    return;
+
   if (m_pEdit)
     MatchEditText();
-  ShowDropList(true);
+  ShowDropDownList();
 }
 
-void CFWL_ComboBox::OnFocusChanged(CFWL_Message* pMsg, bool bSet) {
-  if (bSet) {
-    m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused;
-    if ((m_pEdit->GetStates() & FWL_WGTSTATE_Focused) == 0) {
-      CFWL_MessageSetFocus msg(nullptr, m_pEdit.get());
-      m_pEdit->GetDelegate()->OnProcessMessage(&msg);
-    }
-  } else {
-    m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused;
-    ShowDropList(false);
-    CFWL_MessageKillFocus msg(m_pEdit.get());
+void CFWL_ComboBox::OnFocusGained() {
+  m_Properties.m_dwStates |= FWL_STATE_WGT_Focused;
+  if ((m_pEdit->GetStates() & FWL_STATE_WGT_Focused) == 0) {
+    CFWL_MessageSetFocus msg(m_pEdit);
     m_pEdit->GetDelegate()->OnProcessMessage(&msg);
   }
 }
 
+void CFWL_ComboBox::OnFocusLost() {
+  m_Properties.m_dwStates &= ~FWL_STATE_WGT_Focused;
+  HideDropDownList();
+  CFWL_MessageKillFocus msg(nullptr);
+  m_pEdit->GetDelegate()->OnProcessMessage(&msg);
+}
+
 void CFWL_ComboBox::OnKey(CFWL_MessageKey* pMsg) {
-  uint32_t dwKeyCode = pMsg->m_dwKeyCode;
+  uint32_t dwKeyCode = pMsg->m_dwKeyCodeOrChar;
   const bool bUp = dwKeyCode == XFA_FWL_VKEY_Up;
   const bool bDown = dwKeyCode == XFA_FWL_VKEY_Down;
   if (bUp || bDown) {
-    CFWL_ComboList* pComboList = m_pListBox.get();
+    CFWL_ComboList* pComboList = m_pListBox;
     int32_t iCount = pComboList->CountItems(nullptr);
     if (iCount < 1)
       return;
@@ -557,7 +498,7 @@
       WideString wsText = m_pEdit->GetText();
       iCurSel = pComboList->MatchItem(wsText.AsStringView());
       if (iCurSel >= 0) {
-        CFWL_ListItem* item = m_pListBox->GetSelItem(iCurSel);
+        CFWL_ListBox::Item* item = m_pListBox->GetSelItem(iCurSel);
         bMatchEqual = wsText == (item ? item->GetText() : WideString());
       }
     }
@@ -583,6 +524,6 @@
                                 float fMaxHeight,
                                 const CFX_RectF& rtAnchor,
                                 CFX_RectF* pPopupRect) {
-  m_pWidgetMgr->GetAdapterPopupPos(this, fMinHeight, fMaxHeight, rtAnchor,
-                                   pPopupRect);
+  GetWidgetMgr()->GetAdapterPopupPos(this, fMinHeight, fMaxHeight, rtAnchor,
+                                     pPopupRect);
 }
diff --git a/xfa/fwl/cfwl_combobox.h b/xfa/fwl/cfwl_combobox.h
index 39c952c..590b889 100644
--- a/xfa/fwl/cfwl_combobox.h
+++ b/xfa/fwl/cfwl_combobox.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,17 +7,11 @@
 #ifndef XFA_FWL_CFWL_COMBOBOX_H_
 #define XFA_FWL_CFWL_COMBOBOX_H_
 
-#include <memory>
-
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
 #include "xfa/fwl/cfwl_comboedit.h"
 #include "xfa/fwl/cfwl_combolist.h"
 #include "xfa/fwl/cfwl_listbox.h"
-#include "xfa/fxgraphics/cxfa_graphics.h"
-
-class CFWL_WidgetProperties;
-class CFWL_ComboBox;
-class CFWL_ListBox;
-class CFWL_Widget;
+#include "xfa/fwl/cfwl_widget.h"
 
 #define FWL_STYLEEXT_CMB_DropDown (1L << 0)
 #define FWL_STYLEEXT_CMB_Sort (1L << 1)
@@ -37,22 +31,23 @@
 
 class CFWL_ComboBox final : public CFWL_Widget {
  public:
-  explicit CFWL_ComboBox(const CFWL_App* pApp);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_ComboBox() override;
 
   // CFWL_Widget
+  void Trace(cppgc::Visitor* visitor) const override;
   FWL_Type GetClassID() const override;
-  void ModifyStylesEx(uint32_t dwStylesExAdded,
-                      uint32_t dwStylesExRemoved) override;
+  void ModifyStyleExts(uint32_t dwStyleExtsAdded,
+                       uint32_t dwStyleExtsRemoved) override;
   void SetStates(uint32_t dwStates) override;
   void RemoveStates(uint32_t dwStates) override;
   void Update() override;
   FWL_WidgetHit HitTest(const CFX_PointF& point) override;
-  void DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) override;
-  void SetThemeProvider(IFWL_ThemeProvider* pThemeProvider) override;
+  void DrawWidget(CFGAS_GEGraphics* pGraphics,
+                  const CFX_Matrix& matrix) override;
   void OnProcessMessage(CFWL_Message* pMessage) override;
   void OnProcessEvent(CFWL_Event* pEvent) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
+  void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix) override;
 
   WideString GetTextByIndex(int32_t iIndex) const;
@@ -66,43 +61,44 @@
   void SetEditText(const WideString& wsText);
   WideString GetEditText() const;
 
-  void OpenDropDownList(bool bActivate);
-
   bool EditCanUndo() const { return m_pEdit->CanUndo(); }
   bool EditCanRedo() const { return m_pEdit->CanRedo(); }
   bool EditUndo() { return m_pEdit->Undo(); }
   bool EditRedo() { return m_pEdit->Redo(); }
   bool EditCanCopy() const { return m_pEdit->HasSelection(); }
   bool EditCanCut() const {
-    if (m_pEdit->GetStylesEx() & FWL_STYLEEXT_EDT_ReadOnly)
+    if (m_pEdit->GetStyleExts() & FWL_STYLEEXT_EDT_ReadOnly)
       return false;
     return EditCanCopy();
   }
   bool EditCanSelectAll() const { return m_pEdit->GetTextLength() > 0; }
-  Optional<WideString> EditCopy() const { return m_pEdit->Copy(); }
-  Optional<WideString> EditCut() { return m_pEdit->Cut(); }
+  absl::optional<WideString> EditCopy() const { return m_pEdit->Copy(); }
+  absl::optional<WideString> EditCut() { return m_pEdit->Cut(); }
   bool EditPaste(const WideString& wsPaste) { return m_pEdit->Paste(wsPaste); }
   void EditSelectAll() { m_pEdit->SelectAll(); }
   void EditDelete() { m_pEdit->ClearText(); }
   void EditDeSelect() { m_pEdit->ClearSelection(); }
 
   CFX_RectF GetBBox() const;
-  void EditModifyStylesEx(uint32_t dwStylesExAdded, uint32_t dwStylesExRemoved);
-  void ShowDropList(bool bActivate);
+  void EditModifyStyleExts(uint32_t dwStyleExtsAdded,
+                           uint32_t dwStyleExtsRemoved);
+  void ShowDropDownList();
+  void HideDropDownList();
 
-  CFWL_ComboEdit* GetComboEdit() const { return m_pEdit.get(); }
+  CFWL_ComboEdit* GetComboEdit() const { return m_pEdit; }
 
   void ProcessSelChanged(bool bLButtonUp);
   int32_t GetCurrentSelection() const { return m_iCurSel; }
 
  private:
+  explicit CFWL_ComboBox(CFWL_App* pApp);
+
   bool IsDropDownStyle() const {
-    return !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CMB_DropDown);
+    return !!(GetStyleExts() & FWL_STYLEEXT_CMB_DropDown);
   }
   void MatchEditText();
   void SyncEditText(int32_t iListItem);
   void Layout();
-  void ResetTheme();
   void ResetEditAlignment();
   void ResetListItemAlignment();
   void GetPopupPos(float fMinHeight,
@@ -110,21 +106,20 @@
                    const CFX_RectF& rtAnchor,
                    CFX_RectF* pPopupRect);
   void OnLButtonUp(CFWL_MessageMouse* pMsg);
-
-  void InitComboList();
-  void InitComboEdit();
   bool IsDropListVisible() const { return m_pListBox->IsVisible(); }
   void OnLButtonDown(CFWL_MessageMouse* pMsg);
-  void OnFocusChanged(CFWL_Message* pMsg, bool bSet);
+  void OnFocusGained();
+  void OnFocusLost();
   void OnKey(CFWL_MessageKey* pMsg);
+  void RepaintInflatedListBoxRect();
 
-  CFX_RectF m_rtClient;
-  CFX_RectF m_rtContent;
-  CFX_RectF m_rtBtn;
-  std::unique_ptr<CFWL_ComboEdit> m_pEdit;
-  std::unique_ptr<CFWL_ComboList> m_pListBox;
+  CFX_RectF m_ClientRect;
+  CFX_RectF m_ContentRect;
+  CFX_RectF m_BtnRect;
+  cppgc::Member<CFWL_ComboEdit> const m_pEdit;
+  cppgc::Member<CFWL_ComboList> const m_pListBox;
   int32_t m_iCurSel = -1;
-  int32_t m_iBtnState = CFWL_PartState_Normal;
+  Mask<CFWL_PartState> m_iBtnState = CFWL_PartState::kNormal;
 };
 
 #endif  // XFA_FWL_CFWL_COMBOBOX_H_
diff --git a/xfa/fwl/cfwl_comboedit.cpp b/xfa/fwl/cfwl_comboedit.cpp
index 6ab081a..0be03d3 100644
--- a/xfa/fwl/cfwl_comboedit.cpp
+++ b/xfa/fwl/cfwl_comboedit.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,19 +6,14 @@
 
 #include "xfa/fwl/cfwl_comboedit.h"
 
-#include <memory>
-#include <utility>
-
 #include "xfa/fde/cfde_texteditengine.h"
 #include "xfa/fwl/cfwl_combobox.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
 
-CFWL_ComboEdit::CFWL_ComboEdit(
-    const CFWL_App* app,
-    std::unique_ptr<CFWL_WidgetProperties> properties,
-    CFWL_Widget* pOuter)
-    : CFWL_Edit(app, std::move(properties), pOuter) {
-}
+CFWL_ComboEdit::CFWL_ComboEdit(CFWL_App* app,
+                               const Properties& properties,
+                               CFWL_Widget* pOuter)
+    : CFWL_Edit(app, properties, pOuter) {}
 
 CFWL_ComboEdit::~CFWL_ComboEdit() = default;
 
@@ -28,40 +23,27 @@
 }
 
 void CFWL_ComboEdit::SetSelected() {
-  FlagFocus(true);
+  m_Properties.m_dwStates |= FWL_STATE_WGT_Focused;
   SelectAll();
 }
 
-void CFWL_ComboEdit::FlagFocus(bool bSet) {
-  if (bSet) {
-    m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused;
-    return;
-  }
-
-  m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused;
-  HideCaret(nullptr);
-}
-
 void CFWL_ComboEdit::OnProcessMessage(CFWL_Message* pMessage) {
-  if (!pMessage)
-    return;
-
   bool backDefault = true;
   switch (pMessage->GetType()) {
-    case CFWL_Message::Type::SetFocus: {
-      m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused;
+    case CFWL_Message::Type::kSetFocus: {
+      m_Properties.m_dwStates |= FWL_STATE_WGT_Focused;
       backDefault = false;
       break;
     }
-    case CFWL_Message::Type::KillFocus: {
-      m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused;
+    case CFWL_Message::Type::kKillFocus: {
+      m_Properties.m_dwStates &= ~FWL_STATE_WGT_Focused;
       backDefault = false;
       break;
     }
-    case CFWL_Message::Type::Mouse: {
+    case CFWL_Message::Type::kMouse: {
       CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
-      if ((pMsg->m_dwCmd == FWL_MouseCommand::LeftButtonDown) &&
-          ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0)) {
+      if ((pMsg->m_dwCmd == CFWL_MessageMouse::MouseCommand::kLeftButtonDown) &&
+          ((m_Properties.m_dwStates & FWL_STATE_WGT_Focused) == 0)) {
         SetSelected();
       }
       break;
diff --git a/xfa/fwl/cfwl_comboedit.h b/xfa/fwl/cfwl_comboedit.h
index 68e6caa..91e7f35 100644
--- a/xfa/fwl/cfwl_comboedit.h
+++ b/xfa/fwl/cfwl_comboedit.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,27 +7,24 @@
 #ifndef XFA_FWL_CFWL_COMBOEDIT_H_
 #define XFA_FWL_CFWL_COMBOEDIT_H_
 
-#include <memory>
-
 #include "xfa/fwl/cfwl_edit.h"
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fwl/cfwl_widgetproperties.h"
-
-class CFWL_ComboBox;
 
 class CFWL_ComboEdit final : public CFWL_Edit {
  public:
-  CFWL_ComboEdit(const CFWL_App* app,
-                 std::unique_ptr<CFWL_WidgetProperties> properties,
-                 CFWL_Widget* pOuter);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_ComboEdit() override;
 
-  // CFWL_Edit.
+  // CFWL_Edit:
   void OnProcessMessage(CFWL_Message* pMessage) override;
 
   void ClearSelected();
   void SetSelected();
-  void FlagFocus(bool bSet);
+
+ private:
+  CFWL_ComboEdit(CFWL_App* app,
+                 const Properties& properties,
+                 CFWL_Widget* pOuter);
 };
 
 #endif  // XFA_FWL_CFWL_COMBOEDIT_H_
diff --git a/xfa/fwl/cfwl_combolist.cpp b/xfa/fwl/cfwl_combolist.cpp
index d25f312..938bc10 100644
--- a/xfa/fwl/cfwl_combolist.cpp
+++ b/xfa/fwl/cfwl_combolist.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,7 @@
 
 #include "xfa/fwl/cfwl_combolist.h"
 
-#include <memory>
-#include <utility>
-
+#include "third_party/base/check.h"
 #include "xfa/fwl/cfwl_combobox.h"
 #include "xfa/fwl/cfwl_comboedit.h"
 #include "xfa/fwl/cfwl_listbox.h"
@@ -17,21 +15,22 @@
 #include "xfa/fwl/cfwl_messagemouse.h"
 #include "xfa/fwl/fwl_widgetdef.h"
 
-CFWL_ComboList::CFWL_ComboList(
-    const CFWL_App* app,
-    std::unique_ptr<CFWL_WidgetProperties> properties,
-    CFWL_Widget* pOuter)
-    : CFWL_ListBox(app, std::move(properties), pOuter) {
-  ASSERT(pOuter);
+CFWL_ComboList::CFWL_ComboList(CFWL_App* app,
+                               const Properties& properties,
+                               CFWL_Widget* pOuter)
+    : CFWL_ListBox(app, properties, pOuter) {
+  DCHECK(pOuter);
 }
 
+CFWL_ComboList::~CFWL_ComboList() = default;
+
 int32_t CFWL_ComboList::MatchItem(WideStringView wsMatch) {
   if (wsMatch.IsEmpty())
     return -1;
 
   int32_t iCount = CountItems(this);
   for (int32_t i = 0; i < iCount; i++) {
-    CFWL_ListItem* hItem = GetItem(this, i);
+    CFWL_ListBox::Item* hItem = GetItem(this, i);
     WideString wsText = hItem ? hItem->GetText() : WideString();
     auto pos = wsText.Find(wsMatch);
     if (pos.has_value() && pos.value() == 0)
@@ -41,22 +40,22 @@
 }
 
 void CFWL_ComboList::ChangeSelected(int32_t iSel) {
-  CFWL_ListItem* hItem = GetItem(this, iSel);
-  CFWL_ListItem* hOld = GetSelItem(0);
+  CFWL_ListBox::Item* hItem = GetItem(this, iSel);
+  CFWL_ListBox::Item* hOld = GetSelItem(0);
   int32_t iOld = GetItemIndex(this, hOld);
   if (iOld == iSel)
     return;
 
   CFX_RectF rtInvalidate;
   if (iOld > -1) {
-    if (CFWL_ListItem* hOldItem = GetItem(this, iOld))
+    if (CFWL_ListBox::Item* hOldItem = GetItem(this, iOld))
       rtInvalidate = hOldItem->GetRect();
     SetSelItem(hOld, false);
   }
   if (hItem) {
-    if (CFWL_ListItem* hOldItem = GetItem(this, iSel))
+    if (CFWL_ListBox::Item* hOldItem = GetItem(this, iSel))
       rtInvalidate.Union(hOldItem->GetRect());
-    CFWL_ListItem* hSel = GetItem(this, iSel);
+    CFWL_ListBox::Item* hSel = GetItem(this, iSel);
     SetSelItem(hSel, true);
   }
   if (!rtInvalidate.IsEmpty())
@@ -64,25 +63,19 @@
 }
 
 CFX_PointF CFWL_ComboList::ClientToOuter(const CFX_PointF& point) {
-  CFX_PointF ret = point + CFX_PointF(m_pProperties->m_rtWidget.left,
-                                      m_pProperties->m_rtWidget.top);
-  CFWL_Widget* pOwner = GetOwner();
-  return pOwner ? pOwner->TransformTo(m_pOuter, ret) : ret;
+  return point + CFX_PointF(m_WidgetRect.left, m_WidgetRect.top);
 }
 
 void CFWL_ComboList::OnProcessMessage(CFWL_Message* pMessage) {
-  if (!pMessage)
-    return;
-
   CFWL_Message::Type type = pMessage->GetType();
   bool backDefault = true;
-  if (type == CFWL_Message::Type::SetFocus ||
-      type == CFWL_Message::Type::KillFocus) {
-    OnDropListFocusChanged(pMessage, type == CFWL_Message::Type::SetFocus);
-  } else if (type == CFWL_Message::Type::Mouse) {
+  if (type == CFWL_Message::Type::kSetFocus ||
+      type == CFWL_Message::Type::kKillFocus) {
+    OnDropListFocusChanged(pMessage, type == CFWL_Message::Type::kSetFocus);
+  } else if (type == CFWL_Message::Type::kMouse) {
     CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
     CFWL_ScrollBar* vertSB = GetVertScrollBar();
-    if (IsShowScrollBar(true) && vertSB) {
+    if (IsShowVertScrollBar() && vertSB) {
       CFX_RectF rect = vertSB->GetWidgetRect();
       if (rect.Contains(pMsg->m_pos)) {
         pMsg->m_pos -= rect.TopLeft();
@@ -91,25 +84,22 @@
       }
     }
     switch (pMsg->m_dwCmd) {
-      case FWL_MouseCommand::Move: {
+      case CFWL_MessageMouse::MouseCommand::kMove:
         backDefault = false;
         OnDropListMouseMove(pMsg);
         break;
-      }
-      case FWL_MouseCommand::LeftButtonDown: {
+      case CFWL_MessageMouse::MouseCommand::kLeftButtonDown:
         backDefault = false;
         OnDropListLButtonDown(pMsg);
         break;
-      }
-      case FWL_MouseCommand::LeftButtonUp: {
+      case CFWL_MessageMouse::MouseCommand::kLeftButtonUp:
         backDefault = false;
         OnDropListLButtonUp(pMsg);
         break;
-      }
       default:
         break;
     }
-  } else if (type == CFWL_Message::Type::Key) {
+  } else if (type == CFWL_Message::Type::kKey) {
     backDefault = !OnDropListKey(static_cast<CFWL_MessageKey*>(pMessage));
   }
   if (backDefault)
@@ -121,10 +111,10 @@
     return;
 
   CFWL_MessageKillFocus* pKill = static_cast<CFWL_MessageKillFocus*>(pMsg);
-  CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(m_pOuter);
-  if (pKill->IsFocusedOnWidget(m_pOuter) ||
+  CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(GetOuter());
+  if (pKill->IsFocusedOnWidget(pOuter) ||
       pKill->IsFocusedOnWidget(pOuter->GetComboEdit())) {
-    pOuter->ShowDropList(false);
+    pOuter->HideDropDownList();
   }
 }
 
@@ -134,13 +124,13 @@
       m_bNotifyOwner = false;
 
     CFWL_ScrollBar* vertSB = GetVertScrollBar();
-    if (IsShowScrollBar(true) && vertSB) {
+    if (IsShowVertScrollBar() && vertSB) {
       CFX_RectF rect = vertSB->GetWidgetRect();
       if (rect.Contains(pMsg->m_pos))
         return;
     }
 
-    CFWL_ListItem* hItem = GetItemAtPoint(pMsg->m_pos);
+    CFWL_ListBox::Item* hItem = GetItemAtPoint(pMsg->m_pos);
     if (!hItem)
       return;
 
@@ -148,7 +138,7 @@
   } else if (m_bNotifyOwner) {
     pMsg->m_pos = ClientToOuter(pMsg->m_pos);
 
-    CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(m_pOuter);
+    CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(GetOuter());
     pOuter->GetDelegate()->OnProcessMessage(pMsg);
   }
 }
@@ -157,12 +147,12 @@
   if (GetRTClient().Contains(pMsg->m_pos))
     return;
 
-  CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(m_pOuter);
-  pOuter->ShowDropList(false);
+  CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(GetOuter());
+  pOuter->HideDropDownList();
 }
 
 void CFWL_ComboList::OnDropListLButtonUp(CFWL_MessageMouse* pMsg) {
-  CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(m_pOuter);
+  CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(GetOuter());
   if (m_bNotifyOwner) {
     pMsg->m_pos = ClientToOuter(pMsg->m_pos);
     pOuter->GetDelegate()->OnProcessMessage(pMsg);
@@ -170,27 +160,27 @@
   }
 
   CFWL_ScrollBar* vertSB = GetVertScrollBar();
-  if (IsShowScrollBar(true) && vertSB) {
+  if (IsShowVertScrollBar() && vertSB) {
     CFX_RectF rect = vertSB->GetWidgetRect();
     if (rect.Contains(pMsg->m_pos))
       return;
   }
-  pOuter->ShowDropList(false);
+  pOuter->HideDropDownList();
 
-  CFWL_ListItem* hItem = GetItemAtPoint(pMsg->m_pos);
+  CFWL_ListBox::Item* hItem = GetItemAtPoint(pMsg->m_pos);
   if (hItem)
     pOuter->ProcessSelChanged(true);
 }
 
 bool CFWL_ComboList::OnDropListKey(CFWL_MessageKey* pKey) {
-  CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(m_pOuter);
+  CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(GetOuter());
   bool bPropagate = false;
-  if (pKey->m_dwCmd == FWL_KeyCommand::KeyDown) {
-    uint32_t dwKeyCode = pKey->m_dwKeyCode;
+  if (pKey->m_dwCmd == CFWL_MessageKey::KeyCommand::kKeyDown) {
+    uint32_t dwKeyCode = pKey->m_dwKeyCodeOrChar;
     switch (dwKeyCode) {
       case XFA_FWL_VKEY_Return:
       case XFA_FWL_VKEY_Escape: {
-        pOuter->ShowDropList(false);
+        pOuter->HideDropDownList();
         return true;
       }
       case XFA_FWL_VKEY_Up:
@@ -204,11 +194,11 @@
         break;
       }
     }
-  } else if (pKey->m_dwCmd == FWL_KeyCommand::Char) {
+  } else if (pKey->m_dwCmd == CFWL_MessageKey::KeyCommand::kChar) {
     bPropagate = true;
   }
   if (bPropagate) {
-    pKey->SetDstTarget(m_pOuter);
+    pKey->SetDstTarget(GetOuter());
     pOuter->GetDelegate()->OnProcessMessage(pKey);
     return true;
   }
@@ -216,23 +206,21 @@
 }
 
 void CFWL_ComboList::OnDropListKeyDown(CFWL_MessageKey* pKey) {
-  uint32_t dwKeyCode = pKey->m_dwKeyCode;
+  auto dwKeyCode = static_cast<XFA_FWL_VKEYCODE>(pKey->m_dwKeyCodeOrChar);
   switch (dwKeyCode) {
     case XFA_FWL_VKEY_Up:
     case XFA_FWL_VKEY_Down:
     case XFA_FWL_VKEY_Home:
     case XFA_FWL_VKEY_End: {
-      CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(m_pOuter);
-      CFWL_ListItem* hItem = GetItem(this, pOuter->GetCurrentSelection());
+      CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(GetOuter());
+      CFWL_ListBox::Item* hItem = GetItem(this, pOuter->GetCurrentSelection());
       hItem = GetListItem(hItem, dwKeyCode);
       if (!hItem)
         break;
 
       SetSelection(hItem, hItem, true);
       ScrollToVisible(hItem);
-      CFX_RectF rtInvalidate(0, 0, m_pProperties->m_rtWidget.width,
-                             m_pProperties->m_rtWidget.height);
-      RepaintRect(rtInvalidate);
+      RepaintRect(CFX_RectF(0, 0, m_WidgetRect.width, m_WidgetRect.height));
       break;
     }
     default:
diff --git a/xfa/fwl/cfwl_combolist.h b/xfa/fwl/cfwl_combolist.h
index c275c72..7ee2737 100644
--- a/xfa/fwl/cfwl_combolist.h
+++ b/xfa/fwl/cfwl_combolist.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,26 +7,25 @@
 #ifndef XFA_FWL_CFWL_COMBOLIST_H_
 #define XFA_FWL_CFWL_COMBOLIST_H_
 
-#include <memory>
-
 #include "xfa/fwl/cfwl_listbox.h"
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fwl/cfwl_widgetproperties.h"
 
 class CFWL_ComboList final : public CFWL_ListBox {
  public:
-  CFWL_ComboList(const CFWL_App* app,
-                 std::unique_ptr<CFWL_WidgetProperties> properties,
-                 CFWL_Widget* pOuter);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CFWL_ComboList() override;
 
   // CFWL_ListBox.
   void OnProcessMessage(CFWL_Message* pMessage) override;
 
   int32_t MatchItem(WideStringView wsMatch);
   void ChangeSelected(int32_t iSel);
-  void SetNotifyOwner(bool notify) { m_bNotifyOwner = notify; }
 
  private:
+  CFWL_ComboList(CFWL_App* app,
+                 const Properties& properties,
+                 CFWL_Widget* pOuter);
+
   CFX_PointF ClientToOuter(const CFX_PointF& point);
   void OnDropListFocusChanged(CFWL_Message* pMsg, bool bSet);
   void OnDropListMouseMove(CFWL_MessageMouse* pMsg);
diff --git a/xfa/fwl/cfwl_datetimeedit.cpp b/xfa/fwl/cfwl_datetimeedit.cpp
index 313e89a..2acf690 100644
--- a/xfa/fwl/cfwl_datetimeedit.cpp
+++ b/xfa/fwl/cfwl_datetimeedit.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,38 +6,34 @@
 
 #include "xfa/fwl/cfwl_datetimeedit.h"
 
-#include <memory>
-#include <utility>
-
 #include "xfa/fwl/cfwl_datetimepicker.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
 
-CFWL_DateTimeEdit::CFWL_DateTimeEdit(
-    const CFWL_App* app,
-    std::unique_ptr<CFWL_WidgetProperties> properties,
-    CFWL_Widget* pOuter)
-    : CFWL_Edit(app, std::move(properties), pOuter) {}
+CFWL_DateTimeEdit::CFWL_DateTimeEdit(CFWL_App* app,
+                                     const Properties& properties,
+                                     CFWL_Widget* pOuter)
+    : CFWL_Edit(app, properties, pOuter) {}
 
 CFWL_DateTimeEdit::~CFWL_DateTimeEdit() = default;
 
 void CFWL_DateTimeEdit::OnProcessMessage(CFWL_Message* pMessage) {
-  if (pMessage->GetType() != CFWL_Message::Type::Mouse) {
+  if (pMessage->GetType() != CFWL_Message::Type::kMouse) {
     CFWL_Edit::OnProcessMessage(pMessage);
     return;
   }
 
   CFWL_MessageMouse* pMouse = static_cast<CFWL_MessageMouse*>(pMessage);
-  if (pMouse->m_dwCmd == FWL_MouseCommand::LeftButtonDown ||
-      pMouse->m_dwCmd == FWL_MouseCommand::RightButtonDown) {
-    if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0)
-      m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused;
+  if (pMouse->m_dwCmd == CFWL_MessageMouse::MouseCommand::kLeftButtonDown ||
+      pMouse->m_dwCmd == CFWL_MessageMouse::MouseCommand::kRightButtonDown) {
+    if ((m_Properties.m_dwStates & FWL_STATE_WGT_Focused) == 0)
+      m_Properties.m_dwStates |= FWL_STATE_WGT_Focused;
 
     CFWL_DateTimePicker* pDateTime =
-        static_cast<CFWL_DateTimePicker*>(m_pOuter);
+        static_cast<CFWL_DateTimePicker*>(GetOuter());
     if (pDateTime->IsMonthCalendarVisible()) {
       CFX_RectF rtInvalidate = pDateTime->GetWidgetRect();
-      pDateTime->ShowMonthCalendar(false);
+      pDateTime->HideMonthCalendar();
       rtInvalidate.Offset(-rtInvalidate.left, -rtInvalidate.top);
       pDateTime->RepaintRect(rtInvalidate);
     }
diff --git a/xfa/fwl/cfwl_datetimeedit.h b/xfa/fwl/cfwl_datetimeedit.h
index e617ff4..a34d790 100644
--- a/xfa/fwl/cfwl_datetimeedit.h
+++ b/xfa/fwl/cfwl_datetimeedit.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,21 +7,21 @@
 #ifndef XFA_FWL_CFWL_DATETIMEEDIT_H_
 #define XFA_FWL_CFWL_DATETIMEEDIT_H_
 
-#include <memory>
-
 #include "xfa/fwl/cfwl_edit.h"
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fwl/cfwl_widgetproperties.h"
 
 class CFWL_DateTimeEdit final : public CFWL_Edit {
  public:
-  CFWL_DateTimeEdit(const CFWL_App* app,
-                    std::unique_ptr<CFWL_WidgetProperties> properties,
-                    CFWL_Widget* pOuter);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_DateTimeEdit() override;
 
-  // CFWL_Edit.
+  // CFWL_Edit:
   void OnProcessMessage(CFWL_Message* pMessage) override;
+
+ private:
+  CFWL_DateTimeEdit(CFWL_App* app,
+                    const Properties& properties,
+                    CFWL_Widget* pOuter);
 };
 
 #endif  // XFA_FWL_CFWL_DATETIMEEDIT_H_
diff --git a/xfa/fwl/cfwl_datetimepicker.cpp b/xfa/fwl/cfwl_datetimepicker.cpp
index 11ea810..dd0cf21 100644
--- a/xfa/fwl/cfwl_datetimepicker.cpp
+++ b/xfa/fwl/cfwl_datetimepicker.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,7 @@
 
 #include "xfa/fwl/cfwl_datetimepicker.h"
 
-#include <memory>
-#include <utility>
-
-#include "third_party/base/ptr_util.h"
+#include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_event.h"
 #include "xfa/fwl/cfwl_eventselectchanged.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
@@ -21,36 +18,44 @@
 
 namespace {
 
-const int kDateTimePickerHeight = 20;
+constexpr int kDateTimePickerHeight = 20;
 
 }  // namespace
-CFWL_DateTimePicker::CFWL_DateTimePicker(const CFWL_App* app)
-    : CFWL_Widget(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr) {
-  m_pProperties->m_dwStyleExes = FWL_STYLEEXT_DTP_ShortDateFormat;
 
-  auto monthProp = pdfium::MakeUnique<CFWL_WidgetProperties>();
-  monthProp->m_dwStyles = FWL_WGTSTYLE_Popup | FWL_WGTSTYLE_Border;
-  monthProp->m_dwStates = FWL_WGTSTATE_Invisible;
-  monthProp->m_pParent = this;
-  monthProp->m_pThemeProvider = m_pProperties->m_pThemeProvider;
-  m_pMonthCal = pdfium::MakeUnique<CFWL_MonthCalendar>(
-      m_pOwnerApp.Get(), std::move(monthProp), this);
-
+CFWL_DateTimePicker::CFWL_DateTimePicker(CFWL_App* app)
+    : CFWL_Widget(app,
+                  Properties{0, FWL_STYLEEXT_DTP_ShortDateFormat, 0},
+                  nullptr),
+      m_pEdit(cppgc::MakeGarbageCollected<CFWL_DateTimeEdit>(
+          app->GetHeap()->GetAllocationHandle(),
+          app,
+          Properties(),
+          this)),
+      m_pMonthCal(cppgc::MakeGarbageCollected<CFWL_MonthCalendar>(
+          app->GetHeap()->GetAllocationHandle(),
+          app,
+          Properties{FWL_STYLE_WGT_Popup | FWL_STYLE_WGT_Border, 0,
+                     FWL_STATE_WGT_Invisible},
+          this)) {
   m_pMonthCal->SetWidgetRect(
       CFX_RectF(0, 0, m_pMonthCal->GetAutosizedWidgetRect().Size()));
 
-  auto editProp = pdfium::MakeUnique<CFWL_WidgetProperties>();
-  editProp->m_pParent = this;
-  editProp->m_pThemeProvider = m_pProperties->m_pThemeProvider;
-
-  m_pEdit = pdfium::MakeUnique<CFWL_DateTimeEdit>(m_pOwnerApp.Get(),
-                                                  std::move(editProp), this);
-  RegisterEventTarget(m_pMonthCal.get());
-  RegisterEventTarget(m_pEdit.get());
+  CFWL_NoteDriver* pNoteDriver = GetFWLApp()->GetNoteDriver();
+  pNoteDriver->RegisterEventTarget(this, m_pMonthCal);
+  pNoteDriver->RegisterEventTarget(this, m_pEdit);
 }
 
-CFWL_DateTimePicker::~CFWL_DateTimePicker() {
+CFWL_DateTimePicker::~CFWL_DateTimePicker() = default;
+
+void CFWL_DateTimePicker::PreFinalize() {
   UnregisterEventTarget();
+  CFWL_Widget::PreFinalize();
+}
+
+void CFWL_DateTimePicker::Trace(cppgc::Visitor* visitor) const {
+  CFWL_Widget::Trace(visitor);
+  visitor->Trace(m_pEdit);
+  visitor->Trace(m_pMonthCal);
 }
 
 FWL_Type CFWL_DateTimePicker::GetClassID() const {
@@ -58,24 +63,15 @@
 }
 
 void CFWL_DateTimePicker::Update() {
-  if (m_iLock)
+  if (IsLocked())
     return;
 
-  if (!m_pProperties->m_pThemeProvider)
-    m_pProperties->m_pThemeProvider = GetAvailableTheme();
-  m_pEdit->SetThemeProvider(m_pProperties->m_pThemeProvider.Get());
-  m_rtClient = GetClientRect();
-  m_pEdit->SetWidgetRect(m_rtClient);
+  m_ClientRect = GetClientRect();
+  m_pEdit->SetWidgetRect(m_ClientRect);
   ResetEditAlignment();
   m_pEdit->Update();
-  if (!m_pMonthCal->GetThemeProvider())
-    m_pMonthCal->SetThemeProvider(m_pProperties->m_pThemeProvider.Get());
 
-  IFWL_ThemeProvider* theme = GetAvailableTheme();
-  if (!theme)
-    return;
-
-  m_fBtn = theme->GetScrollBarWidth();
+  m_fBtn = GetThemeProvider()->GetScrollBarWidth();
   CFX_RectF rtMonthCal = m_pMonthCal->GetAutosizedWidgetRect();
   CFX_RectF rtPopUp(rtMonthCal.left, rtMonthCal.top + kDateTimePickerHeight,
                     rtMonthCal.width, rtMonthCal.height);
@@ -84,8 +80,7 @@
 }
 
 FWL_WidgetHit CFWL_DateTimePicker::HitTest(const CFX_PointF& point) {
-  CFX_RectF rect(0, 0, m_pProperties->m_rtWidget.width,
-                 m_pProperties->m_rtWidget.height);
+  CFX_RectF rect(0, 0, m_WidgetRect.width, m_WidgetRect.height);
   if (rect.Contains(point))
     return FWL_WidgetHit::Edit;
   if (NeedsToShowButton())
@@ -99,23 +94,19 @@
   return FWL_WidgetHit::Unknown;
 }
 
-void CFWL_DateTimePicker::DrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_DateTimePicker::DrawWidget(CFGAS_GEGraphics* pGraphics,
                                      const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
 
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
-  if (!pTheme)
-    return;
-
   if (HasBorder())
-    DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
-  if (!m_rtBtn.IsEmpty())
-    DrawDropDownButton(pGraphics, pTheme, &matrix);
+    DrawBorder(pGraphics, CFWL_ThemePart::Part::kBorder, matrix);
+
+  if (!m_BtnRect.IsEmpty())
+    DrawDropDownButton(pGraphics, matrix);
 
   if (m_pEdit) {
     CFX_RectF rtEdit = m_pEdit->GetWidgetRect();
-
     CFX_Matrix mt(1, 0, 0, 1, rtEdit.left, rtEdit.top);
     mt.Concat(matrix);
     m_pEdit->DrawWidget(pGraphics, mt);
@@ -129,11 +120,6 @@
   m_pMonthCal->DrawWidget(pGraphics, mt);
 }
 
-void CFWL_DateTimePicker::SetThemeProvider(IFWL_ThemeProvider* pTP) {
-  m_pProperties->m_pThemeProvider = pTP;
-  m_pMonthCal->SetThemeProvider(pTP);
-}
-
 void CFWL_DateTimePicker::GetCurSel(int32_t& iYear,
                                     int32_t& iMonth,
                                     int32_t& iDay) {
@@ -163,7 +149,7 @@
     return;
 
   m_pEdit->SetText(wsText);
-  RepaintRect(m_rtClient);
+  RepaintRect(m_ClientRect);
 
   CFWL_Event ev(CFWL_Event::Type::EditChanged);
   DispatchEvent(&ev);
@@ -173,82 +159,80 @@
   return m_pEdit ? m_pEdit->GetText() : WideString();
 }
 
-int32_t CFWL_DateTimePicker::GetEditTextLength() const {
+size_t CFWL_DateTimePicker::GetEditTextLength() const {
   return m_pEdit ? m_pEdit->GetTextLength() : 0;
 }
 
 CFX_RectF CFWL_DateTimePicker::GetBBox() const {
-  CFX_RectF rect = m_pProperties->m_rtWidget;
+  CFX_RectF rect = m_WidgetRect;
   if (NeedsToShowButton())
     rect.width += m_fBtn;
   if (!IsMonthCalendarVisible())
     return rect;
 
   CFX_RectF rtMonth = m_pMonthCal->GetWidgetRect();
-  rtMonth.Offset(m_pProperties->m_rtWidget.left, m_pProperties->m_rtWidget.top);
+  rtMonth.Offset(m_WidgetRect.left, m_WidgetRect.top);
   rect.Union(rtMonth);
   return rect;
 }
 
-void CFWL_DateTimePicker::ModifyEditStylesEx(uint32_t dwStylesExAdded,
-                                             uint32_t dwStylesExRemoved) {
-  m_pEdit->ModifyStylesEx(dwStylesExAdded, dwStylesExRemoved);
+void CFWL_DateTimePicker::ModifyEditStyleExts(uint32_t dwStyleExtsAdded,
+                                              uint32_t dwStyleExtsRemoved) {
+  m_pEdit->ModifyStyleExts(dwStyleExtsAdded, dwStyleExtsRemoved);
 }
 
-void CFWL_DateTimePicker::DrawDropDownButton(CXFA_Graphics* pGraphics,
-                                             IFWL_ThemeProvider* pTheme,
-                                             const CFX_Matrix* pMatrix) {
-  CFWL_ThemeBackground param;
-  param.m_pWidget = this;
-  param.m_iPart = CFWL_Part::DropDownButton;
+void CFWL_DateTimePicker::DrawDropDownButton(CFGAS_GEGraphics* pGraphics,
+                                             const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeBackground param(CFWL_ThemePart::Part::kDropDownButton, this,
+                             pGraphics);
   param.m_dwStates = m_iBtnState;
-  param.m_pGraphics = pGraphics;
-  param.m_rtPart = m_rtBtn;
-  if (pMatrix)
-    param.m_matrix.Concat(*pMatrix);
-  pTheme->DrawBackground(param);
+  param.m_PartRect = m_BtnRect;
+  param.m_matrix = mtMatrix;
+  GetThemeProvider()->DrawBackground(param);
 }
 
 WideString CFWL_DateTimePicker::FormatDateString(int32_t iYear,
                                                  int32_t iMonth,
                                                  int32_t iDay) {
-  if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_DTP_ShortDateFormat)
+  if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_DTP_ShortDateFormat)
     return WideString::Format(L"%d-%d-%d", iYear, iMonth, iDay);
 
   return WideString::Format(L"%d Year %d Month %d Day", iYear, iMonth, iDay);
 }
 
-void CFWL_DateTimePicker::ShowMonthCalendar(bool bActivate) {
-  if (IsMonthCalendarVisible() == bActivate)
+void CFWL_DateTimePicker::ShowMonthCalendar() {
+  if (IsMonthCalendarVisible())
     return;
 
-  if (bActivate) {
-    CFX_RectF rtMonthCal = m_pMonthCal->GetAutosizedWidgetRect();
-    float fPopupMin = rtMonthCal.height;
-    float fPopupMax = rtMonthCal.height;
-    CFX_RectF rtAnchor(m_pProperties->m_rtWidget);
-    rtAnchor.width = rtMonthCal.width;
-    rtMonthCal.left = m_rtClient.left;
-    rtMonthCal.top = rtAnchor.Height();
-    GetPopupPos(fPopupMin, fPopupMax, rtAnchor, &rtMonthCal);
-    m_pMonthCal->SetWidgetRect(rtMonthCal);
-    if (m_iYear > 0 && m_iMonth > 0 && m_iDay > 0)
-      m_pMonthCal->SetSelect(m_iYear, m_iMonth, m_iDay);
-    m_pMonthCal->Update();
-  }
-  if (bActivate)
-    m_pMonthCal->RemoveStates(FWL_WGTSTATE_Invisible);
-  else
-    m_pMonthCal->SetStates(FWL_WGTSTATE_Invisible);
+  CFX_RectF rtMonthCal = m_pMonthCal->GetAutosizedWidgetRect();
+  float fPopupMin = rtMonthCal.height;
+  float fPopupMax = rtMonthCal.height;
+  CFX_RectF rtAnchor = m_WidgetRect;
+  rtAnchor.width = rtMonthCal.width;
+  rtMonthCal.left = m_ClientRect.left;
+  rtMonthCal.top = rtAnchor.Height();
+  GetPopupPos(fPopupMin, fPopupMax, rtAnchor, &rtMonthCal);
+  m_pMonthCal->SetWidgetRect(rtMonthCal);
+  if (m_iYear > 0 && m_iMonth > 0 && m_iDay > 0)
+    m_pMonthCal->SetSelect(m_iYear, m_iMonth, m_iDay);
+  m_pMonthCal->Update();
+  m_pMonthCal->RemoveStates(FWL_STATE_WGT_Invisible);
 
-  if (bActivate) {
-    CFWL_MessageSetFocus msg(m_pEdit.get(), m_pMonthCal.get());
-    m_pEdit->GetDelegate()->OnProcessMessage(&msg);
-  }
+  CFWL_MessageSetFocus msg(m_pMonthCal);
+  m_pEdit->GetDelegate()->OnProcessMessage(&msg);
+  RepaintInflatedMonthCalRect();
+}
 
-  CFX_RectF rtInvalidate(0, 0, m_pProperties->m_rtWidget.width,
-                         m_pProperties->m_rtWidget.height);
+void CFWL_DateTimePicker::HideMonthCalendar() {
+  if (!IsMonthCalendarVisible())
+    return;
 
+  m_pMonthCal->SetStates(FWL_STATE_WGT_Invisible);
+  RepaintInflatedMonthCalRect();
+}
+
+void CFWL_DateTimePicker::RepaintInflatedMonthCalRect() {
+  CFX_RectF rtInvalidate(0, 0, m_WidgetRect.width, m_WidgetRect.height);
   CFX_RectF rtCal = m_pMonthCal->GetWidgetRect();
   rtInvalidate.Union(rtCal);
   rtInvalidate.Inflate(2, 2);
@@ -264,7 +248,7 @@
     return;
 
   uint32_t dwAdd = 0;
-  switch (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_DTP_EditHAlignMask) {
+  switch (m_Properties.m_dwStyleExts & FWL_STYLEEXT_DTP_EditHAlignMask) {
     case FWL_STYLEEXT_DTP_EditHCenter: {
       dwAdd |= FWL_STYLEEXT_EDT_HCenter;
       break;
@@ -278,7 +262,7 @@
       break;
     }
   }
-  switch (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_DTP_EditVAlignMask) {
+  switch (m_Properties.m_dwStyleExts & FWL_STYLEEXT_DTP_EditVAlignMask) {
     case FWL_STYLEEXT_DTP_EditVCenter: {
       dwAdd |= FWL_STYLEEXT_EDT_VCenter;
       break;
@@ -292,12 +276,12 @@
       break;
     }
   }
-  if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_DTP_EditJustified)
+  if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_DTP_EditJustified)
     dwAdd |= FWL_STYLEEXT_EDT_Justified;
 
-  m_pEdit->ModifyStylesEx(dwAdd, FWL_STYLEEXT_EDT_HAlignMask |
-                                     FWL_STYLEEXT_EDT_HAlignModeMask |
-                                     FWL_STYLEEXT_EDT_VAlignMask);
+  m_pEdit->ModifyStyleExts(dwAdd, FWL_STYLEEXT_EDT_HAlignMask |
+                                      FWL_STYLEEXT_EDT_HAlignModeMask |
+                                      FWL_STYLEEXT_EDT_VAlignMask);
 }
 
 void CFWL_DateTimePicker::ProcessSelChanged(int32_t iYear,
@@ -306,49 +290,41 @@
   m_iYear = iYear;
   m_iMonth = iMonth;
   m_iDay = iDay;
-
-  WideString wsText = FormatDateString(m_iYear, m_iMonth, m_iDay);
-  m_pEdit->SetText(wsText);
+  m_pEdit->SetText(FormatDateString(m_iYear, m_iMonth, m_iDay));
   m_pEdit->Update();
-  RepaintRect(m_rtClient);
+  RepaintRect(m_ClientRect);
 
-  CFWL_EventSelectChanged ev(this);
-  ev.iYear = m_iYear;
-  ev.iMonth = m_iMonth;
-  ev.iDay = m_iDay;
+  CFWL_EventSelectChanged ev(this, m_iYear, m_iMonth, m_iDay);
   DispatchEvent(&ev);
 }
 
 bool CFWL_DateTimePicker::NeedsToShowButton() const {
-  return m_pProperties->m_dwStates & FWL_WGTSTATE_Focused ||
-         m_pMonthCal->GetStates() & FWL_WGTSTATE_Focused ||
-         m_pEdit->GetStates() & FWL_WGTSTATE_Focused;
+  return m_Properties.m_dwStates & FWL_STATE_WGT_Focused ||
+         m_pMonthCal->GetStates() & FWL_STATE_WGT_Focused ||
+         m_pEdit->GetStates() & FWL_STATE_WGT_Focused;
 }
 
 void CFWL_DateTimePicker::OnProcessMessage(CFWL_Message* pMessage) {
-  if (!pMessage)
-    return;
-
   switch (pMessage->GetType()) {
-    case CFWL_Message::Type::SetFocus:
-      OnFocusChanged(pMessage, true);
+    case CFWL_Message::Type::kSetFocus:
+      OnFocusGained(pMessage);
       break;
-    case CFWL_Message::Type::KillFocus:
-      OnFocusChanged(pMessage, false);
+    case CFWL_Message::Type::kKillFocus:
+      OnFocusLost(pMessage);
       break;
-    case CFWL_Message::Type::Mouse: {
+    case CFWL_Message::Type::kMouse: {
       CFWL_MessageMouse* pMouse = static_cast<CFWL_MessageMouse*>(pMessage);
       switch (pMouse->m_dwCmd) {
-        case FWL_MouseCommand::LeftButtonDown:
+        case CFWL_MessageMouse::MouseCommand::kLeftButtonDown:
           OnLButtonDown(pMouse);
           break;
-        case FWL_MouseCommand::LeftButtonUp:
+        case CFWL_MessageMouse::MouseCommand::kLeftButtonUp:
           OnLButtonUp(pMouse);
           break;
-        case FWL_MouseCommand::Move:
+        case CFWL_MessageMouse::MouseCommand::kMove:
           OnMouseMove(pMouse);
           break;
-        case FWL_MouseCommand::Leave:
+        case CFWL_MessageMouse::MouseCommand::kLeave:
           OnMouseLeave(pMouse);
           break;
         default:
@@ -356,8 +332,8 @@
       }
       break;
     }
-    case CFWL_Message::Type::Key: {
-      if (m_pEdit->GetStates() & FWL_WGTSTATE_Focused) {
+    case CFWL_Message::Type::kKey: {
+      if (m_pEdit->GetStates() & FWL_STATE_WGT_Focused) {
         m_pEdit->GetDelegate()->OnProcessMessage(pMessage);
         return;
       }
@@ -371,35 +347,31 @@
     CFWL_Widget::OnProcessMessage(pMessage);
 }
 
-void CFWL_DateTimePicker::OnDrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_DateTimePicker::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                                        const CFX_Matrix& matrix) {
   DrawWidget(pGraphics, matrix);
 }
 
-void CFWL_DateTimePicker::OnFocusChanged(CFWL_Message* pMsg, bool bSet) {
-  if (!pMsg)
-    return;
-
-  CFX_RectF rtInvalidate(m_rtBtn);
-  if (bSet) {
-    m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused;
-    if (m_pEdit && !(m_pEdit->GetStylesEx() & FWL_STYLEEXT_EDT_ReadOnly)) {
-      m_rtBtn = CFX_RectF(m_pProperties->m_rtWidget.width, 0, m_fBtn,
-                          m_pProperties->m_rtWidget.height - 1);
-    }
-    rtInvalidate = m_rtBtn;
-    pMsg->SetDstTarget(m_pEdit.get());
-    m_pEdit->GetDelegate()->OnProcessMessage(pMsg);
-  } else {
-    m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused;
-    m_rtBtn = CFX_RectF();
-    if (IsMonthCalendarVisible())
-      ShowMonthCalendar(false);
-    if (m_pEdit->GetStates() & FWL_WGTSTATE_Focused) {
-      pMsg->SetSrcTarget(m_pEdit.get());
-      m_pEdit->GetDelegate()->OnProcessMessage(pMsg);
-    }
+void CFWL_DateTimePicker::OnFocusGained(CFWL_Message* pMsg) {
+  m_Properties.m_dwStates |= FWL_STATE_WGT_Focused;
+  if (m_pEdit && !(m_pEdit->GetStyleExts() & FWL_STYLEEXT_EDT_ReadOnly)) {
+    m_BtnRect =
+        CFX_RectF(m_WidgetRect.width, 0, m_fBtn, m_WidgetRect.height - 1);
   }
+  CFX_RectF rtInvalidate(m_BtnRect);
+  pMsg->SetDstTarget(m_pEdit);
+  m_pEdit->GetDelegate()->OnProcessMessage(pMsg);
+  rtInvalidate.Inflate(2, 2);
+  RepaintRect(rtInvalidate);
+}
+
+void CFWL_DateTimePicker::OnFocusLost(CFWL_Message* pMsg) {
+  CFX_RectF rtInvalidate(m_BtnRect);
+  m_Properties.m_dwStates &= ~FWL_STATE_WGT_Focused;
+  m_BtnRect = CFX_RectF();
+  HideMonthCalendar();
+  if (m_pEdit->GetStates() & FWL_STATE_WGT_Focused)
+    m_pEdit->GetDelegate()->OnProcessMessage(pMsg);
   rtInvalidate.Inflate(2, 2);
   RepaintRect(rtInvalidate);
 }
@@ -407,17 +379,16 @@
 void CFWL_DateTimePicker::OnLButtonDown(CFWL_MessageMouse* pMsg) {
   if (!pMsg)
     return;
-  if (!m_rtBtn.Contains(pMsg->m_pos))
+  if (!m_BtnRect.Contains(pMsg->m_pos))
     return;
 
   if (IsMonthCalendarVisible()) {
-    ShowMonthCalendar(false);
+    HideMonthCalendar();
     return;
   }
-  ShowMonthCalendar(true);
-
+  ShowMonthCalendar();
   m_bLBtnDown = true;
-  RepaintRect(m_rtClient);
+  RepaintRect(m_ClientRect);
 }
 
 void CFWL_DateTimePicker::OnLButtonUp(CFWL_MessageMouse* pMsg) {
@@ -425,32 +396,32 @@
     return;
 
   m_bLBtnDown = false;
-  if (m_rtBtn.Contains(pMsg->m_pos))
-    m_iBtnState = CFWL_PartState_Hovered;
+  if (m_BtnRect.Contains(pMsg->m_pos))
+    m_iBtnState = CFWL_PartState::kHovered;
   else
-    m_iBtnState = CFWL_PartState_Normal;
-  RepaintRect(m_rtBtn);
+    m_iBtnState = CFWL_PartState::kNormal;
+  RepaintRect(m_BtnRect);
 }
 
 void CFWL_DateTimePicker::OnMouseMove(CFWL_MessageMouse* pMsg) {
-  if (!m_rtBtn.Contains(pMsg->m_pos))
-    m_iBtnState = CFWL_PartState_Normal;
-  RepaintRect(m_rtBtn);
+  if (!m_BtnRect.Contains(pMsg->m_pos))
+    m_iBtnState = CFWL_PartState::kNormal;
+  RepaintRect(m_BtnRect);
 }
 
 void CFWL_DateTimePicker::OnMouseLeave(CFWL_MessageMouse* pMsg) {
   if (!pMsg)
     return;
-  m_iBtnState = CFWL_PartState_Normal;
-  RepaintRect(m_rtBtn);
+  m_iBtnState = CFWL_PartState::kNormal;
+  RepaintRect(m_BtnRect);
 }
 
 void CFWL_DateTimePicker::GetPopupPos(float fMinHeight,
                                       float fMaxHeight,
                                       const CFX_RectF& rtAnchor,
                                       CFX_RectF* pPopupRect) {
-  m_pWidgetMgr->GetAdapterPopupPos(this, fMinHeight, fMaxHeight, rtAnchor,
-                                   pPopupRect);
+  GetWidgetMgr()->GetAdapterPopupPos(this, fMinHeight, fMaxHeight, rtAnchor,
+                                     pPopupRect);
 }
 
 void CFWL_DateTimePicker::ClearText() {
@@ -465,11 +436,11 @@
   m_pEdit->ClearSelection();
 }
 
-Optional<WideString> CFWL_DateTimePicker::Copy() {
+absl::optional<WideString> CFWL_DateTimePicker::Copy() {
   return m_pEdit->Copy();
 }
 
-Optional<WideString> CFWL_DateTimePicker::Cut() {
+absl::optional<WideString> CFWL_DateTimePicker::Cut() {
   return m_pEdit->Cut();
 }
 
diff --git a/xfa/fwl/cfwl_datetimepicker.h b/xfa/fwl/cfwl_datetimepicker.h
index 69520eb..75c73ac 100644
--- a/xfa/fwl/cfwl_datetimepicker.h
+++ b/xfa/fwl/cfwl_datetimepicker.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,13 @@
 #ifndef XFA_FWL_CFWL_DATETIMEPICKER_H_
 #define XFA_FWL_CFWL_DATETIMEPICKER_H_
 
-#include <memory>
 #include <utility>
 
+#include "v8/include/cppgc/member.h"
 #include "xfa/fwl/cfwl_datetimeedit.h"
 #include "xfa/fwl/cfwl_event.h"
 #include "xfa/fwl/cfwl_monthcalendar.h"
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fwl/cfwl_widgetproperties.h"
 
 #define FWL_STYLEEXT_DTP_ShortDateFormat (1L << 1)
 #define FWL_STYLEEXT_DTP_EditHAlignMask (3L << 4)
@@ -31,24 +30,26 @@
 
 class CFWL_DateTimePicker final : public CFWL_Widget {
  public:
-  explicit CFWL_DateTimePicker(const CFWL_App* pApp);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_DateTimePicker() override;
 
-  // CFWL_Widget
+  // CFWL_Widget:
+  void PreFinalize() override;
+  void Trace(cppgc::Visitor* visitor) const override;
   FWL_Type GetClassID() const override;
   void Update() override;
   FWL_WidgetHit HitTest(const CFX_PointF& point) override;
-  void DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) override;
-  void SetThemeProvider(IFWL_ThemeProvider* pTP) override;
+  void DrawWidget(CFGAS_GEGraphics* pGraphics,
+                  const CFX_Matrix& matrix) override;
   void OnProcessMessage(CFWL_Message* pMessage) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
+  void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix) override;
 
   void GetCurSel(int32_t& iYear, int32_t& iMonth, int32_t& iDay);
   void SetCurSel(int32_t iYear, int32_t iMonth, int32_t iDay);
 
   void SetEditText(const WideString& wsText);
-  int32_t GetEditTextLength() const;
+  size_t GetEditTextLength() const;
   WideString GetEditText() const;
   void ClearText();
 
@@ -59,8 +60,8 @@
   std::pair<size_t, size_t> GetSelection() const {
     return m_pEdit->GetSelection();
   }
-  Optional<WideString> Copy();
-  Optional<WideString> Cut();
+  absl::optional<WideString> Copy();
+  absl::optional<WideString> Cut();
   bool Paste(const WideString& wsPaste);
   bool Undo();
   bool Redo();
@@ -69,40 +70,44 @@
 
   CFX_RectF GetBBox() const;
   void SetEditLimit(int32_t nLimit) { m_pEdit->SetLimit(nLimit); }
-  void ModifyEditStylesEx(uint32_t dwStylesExAdded, uint32_t dwStylesExRemoved);
+  void ModifyEditStyleExts(uint32_t dwStyleExtsAdded,
+                           uint32_t dwStyleExtsRemoved);
 
   bool IsMonthCalendarVisible() const;
-  void ShowMonthCalendar(bool bActivate);
+  void ShowMonthCalendar();
+  void HideMonthCalendar();
   void ProcessSelChanged(int32_t iYear, int32_t iMonth, int32_t iDay);
 
  private:
-  void DrawDropDownButton(CXFA_Graphics* pGraphics,
-                          IFWL_ThemeProvider* pTheme,
-                          const CFX_Matrix* pMatrix);
+  explicit CFWL_DateTimePicker(CFWL_App* pApp);
+
+  void DrawDropDownButton(CFGAS_GEGraphics* pGraphics,
+                          const CFX_Matrix& mtMatrix);
   WideString FormatDateString(int32_t iYear, int32_t iMonth, int32_t iDay);
   void ResetEditAlignment();
   void GetPopupPos(float fMinHeight,
                    float fMaxHeight,
                    const CFX_RectF& rtAnchor,
                    CFX_RectF* pPopupRect);
-  void OnFocusChanged(CFWL_Message* pMsg, bool bSet);
+  void OnFocusGained(CFWL_Message* pMsg);
+  void OnFocusLost(CFWL_Message* pMsg);
   void OnLButtonDown(CFWL_MessageMouse* pMsg);
   void OnLButtonUp(CFWL_MessageMouse* pMsg);
   void OnMouseMove(CFWL_MessageMouse* pMsg);
   void OnMouseLeave(CFWL_MessageMouse* pMsg);
-
   bool NeedsToShowButton() const;
+  void RepaintInflatedMonthCalRect();
 
   bool m_bLBtnDown = false;
-  int32_t m_iBtnState = 1;
+  Mask<CFWL_PartState> m_iBtnState = CFWL_PartState::kChecked;
   int32_t m_iYear = -1;
   int32_t m_iMonth = -1;
   int32_t m_iDay = -1;
   float m_fBtn = 0.0f;
-  CFX_RectF m_rtBtn;
-  CFX_RectF m_rtClient;
-  std::unique_ptr<CFWL_DateTimeEdit> m_pEdit;
-  std::unique_ptr<CFWL_MonthCalendar> m_pMonthCal;
+  CFX_RectF m_BtnRect;
+  CFX_RectF m_ClientRect;
+  cppgc::Member<CFWL_DateTimeEdit> const m_pEdit;
+  cppgc::Member<CFWL_MonthCalendar> const m_pMonthCal;
 };
 
 #endif  // XFA_FWL_CFWL_DATETIMEPICKER_H_
diff --git a/xfa/fwl/cfwl_edit.cpp b/xfa/fwl/cfwl_edit.cpp
index 1f265b5..9d624c9 100644
--- a/xfa/fwl/cfwl_edit.cpp
+++ b/xfa/fwl/cfwl_edit.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,10 +14,13 @@
 #include "build/build_config.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/text_char_pos.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_caret.h"
 #include "xfa/fwl/cfwl_event.h"
@@ -31,32 +34,40 @@
 #include "xfa/fwl/fwl_widgetdef.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
 #include "xfa/fwl/theme/cfwl_utils.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
 
 namespace {
 
 constexpr int kEditMargin = 3;
 
-#if defined(OS_MACOSX)
-constexpr int kEditingModifier = FWL_KEYFLAG_Command;
+#if BUILDFLAG(IS_APPLE)
+constexpr XFA_FWL_KeyFlag kEditingModifier = XFA_FWL_KeyFlag::kCommand;
 #else
-constexpr int kEditingModifier = FWL_KEYFLAG_Ctrl;
+constexpr XFA_FWL_KeyFlag kEditingModifier = XFA_FWL_KeyFlag::kCtrl;
 #endif
 
 }  // namespace
 
-CFWL_Edit::CFWL_Edit(const CFWL_App* app,
-                     std::unique_ptr<CFWL_WidgetProperties> properties,
+CFWL_Edit::CFWL_Edit(CFWL_App* app,
+                     const Properties& properties,
                      CFWL_Widget* pOuter)
-    : CFWL_Widget(app, std::move(properties), pOuter),
-      m_pEditEngine(pdfium::MakeUnique<CFDE_TextEditEngine>()) {
+    : CFWL_Widget(app, properties, pOuter),
+      m_pEditEngine(std::make_unique<CFDE_TextEditEngine>()) {
   m_pEditEngine->SetDelegate(this);
 }
 
-CFWL_Edit::~CFWL_Edit() {
+CFWL_Edit::~CFWL_Edit() = default;
+
+void CFWL_Edit::PreFinalize() {
   m_pEditEngine->SetDelegate(nullptr);
-  if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused)
+  if (m_Properties.m_dwStates & FWL_STATE_WGT_Focused)
     HideCaret(nullptr);
+  CFWL_Widget::PreFinalize();
+}
+
+void CFWL_Edit::Trace(cppgc::Visitor* visitor) const {
+  CFWL_Widget::Trace(visitor);
+  visitor->Trace(m_pVertScrollBar);
+  visitor->Trace(m_pCaret);
 }
 
 FWL_Type CFWL_Edit::GetClassID() const {
@@ -64,39 +75,32 @@
 }
 
 CFX_RectF CFWL_Edit::GetWidgetRect() {
-  CFX_RectF rect = m_pProperties->m_rtWidget;
-  if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_OuterScrollbar) {
-    IFWL_ThemeProvider* theme = GetAvailableTheme();
-    float scrollbarWidth = theme ? theme->GetScrollBarWidth() : 0.0f;
-    if (IsShowScrollBar(true)) {
+  CFX_RectF rect = m_WidgetRect;
+  if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_OuterScrollbar) {
+    float scrollbarWidth = GetThemeProvider()->GetScrollBarWidth();
+    if (IsShowVertScrollBar()) {
       rect.width += scrollbarWidth;
       rect.width += kEditMargin;
     }
-    if (IsShowScrollBar(false)) {
-      rect.height += scrollbarWidth;
-      rect.height += kEditMargin;
-    }
   }
   return rect;
 }
 
 CFX_RectF CFWL_Edit::GetAutosizedWidgetRect() {
   CFX_RectF rect;
-
   if (m_pEditEngine->GetLength() > 0) {
     CFX_SizeF size = CalcTextSize(
-        m_pEditEngine->GetText(), m_pProperties->m_pThemeProvider.Get(),
-        !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_MultiLine));
+        m_pEditEngine->GetText(),
+        !!(m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_MultiLine));
     rect = CFX_RectF(0, 0, size);
   }
-
   InflateWidgetRect(rect);
   return rect;
 }
 
 void CFWL_Edit::SetStates(uint32_t dwStates) {
-  if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Invisible) ||
-      (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)) {
+  if ((m_Properties.m_dwStates & FWL_STATE_WGT_Invisible) ||
+      (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)) {
     HideCaret(nullptr);
   }
   CFWL_Widget::SetStates(dwStates);
@@ -105,11 +109,9 @@
 void CFWL_Edit::Update() {
   if (IsLocked())
     return;
-  if (!m_pProperties->m_pThemeProvider)
-    m_pProperties->m_pThemeProvider = GetAvailableTheme();
 
   Layout();
-  if (m_rtClient.IsEmpty())
+  if (m_ClientRect.IsEmpty())
     return;
 
   UpdateEditEngine();
@@ -119,47 +121,28 @@
 }
 
 FWL_WidgetHit CFWL_Edit::HitTest(const CFX_PointF& point) {
-  if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_OuterScrollbar) {
-    if (IsShowScrollBar(true)) {
+  if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_OuterScrollbar) {
+    if (IsShowVertScrollBar()) {
       if (m_pVertScrollBar->GetWidgetRect().Contains(point))
         return FWL_WidgetHit::VScrollBar;
     }
-    if (IsShowScrollBar(false)) {
-      if (m_pHorzScrollBar->GetWidgetRect().Contains(point))
-        return FWL_WidgetHit::HScrollBar;
-    }
   }
-  if (m_rtClient.Contains(point))
+  if (m_ClientRect.Contains(point))
     return FWL_WidgetHit::Edit;
   return FWL_WidgetHit::Unknown;
 }
 
-void CFWL_Edit::DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) {
+void CFWL_Edit::DrawWidget(CFGAS_GEGraphics* pGraphics,
+                           const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
 
-  if (m_rtClient.IsEmpty())
+  if (m_ClientRect.IsEmpty())
     return;
 
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
-  if (!pTheme)
-    return;
-
-  DrawContent(pGraphics, pTheme, &matrix);
+  DrawContent(pGraphics, matrix);
   if (HasBorder())
-    DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
-}
-
-void CFWL_Edit::SetThemeProvider(IFWL_ThemeProvider* pThemeProvider) {
-  if (!pThemeProvider)
-    return;
-  if (m_pHorzScrollBar)
-    m_pHorzScrollBar->SetThemeProvider(pThemeProvider);
-  if (m_pVertScrollBar)
-    m_pVertScrollBar->SetThemeProvider(pThemeProvider);
-  if (m_pCaret)
-    m_pCaret->SetThemeProvider(pThemeProvider);
-  m_pProperties->m_pThemeProvider = pThemeProvider;
+    DrawBorder(pGraphics, CFWL_ThemePart::Part::kBorder, matrix);
 }
 
 void CFWL_Edit::SetText(const WideString& wsText) {
@@ -174,7 +157,7 @@
                         CFDE_TextEditEngine::RecordOperation::kSkipNotify);
 }
 
-int32_t CFWL_Edit::GetTextLength() const {
+size_t CFWL_Edit::GetTextLength() const {
   return m_pEditEngine->GetLength();
 }
 
@@ -221,20 +204,20 @@
   m_pEditEngine->SetAliasChar(wAlias);
 }
 
-Optional<WideString> CFWL_Edit::Copy() {
+absl::optional<WideString> CFWL_Edit::Copy() {
   if (!m_pEditEngine->HasSelection())
-    return {};
+    return absl::nullopt;
 
-  return {m_pEditEngine->GetSelectedText()};
+  return m_pEditEngine->GetSelectedText();
 }
 
-Optional<WideString> CFWL_Edit::Cut() {
+absl::optional<WideString> CFWL_Edit::Cut() {
   if (!m_pEditEngine->HasSelection())
-    return {};
+    return absl::nullopt;
 
   WideString cut_text = m_pEditEngine->DeleteSelectedText();
   UpdateCaret();
-  return {cut_text};
+  return cut_text;
 }
 
 bool CFWL_Edit::Paste(const WideString& wsPaste) {
@@ -262,26 +245,22 @@
   return m_pEditEngine->CanRedo();
 }
 
-void CFWL_Edit::SetOuter(CFWL_Widget* pOuter) {
-  m_pOuter = pOuter;
-}
-
 void CFWL_Edit::NotifyTextFull() {
   CFWL_Event evt(CFWL_Event::Type::TextFull, this);
   DispatchEvent(&evt);
 }
 
 void CFWL_Edit::OnCaretChanged() {
-  if (m_rtEngine.IsEmpty())
+  if (m_EngineRect.IsEmpty())
     return;
-  if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0)
+  if ((m_Properties.m_dwStates & FWL_STATE_WGT_Focused) == 0)
     return;
 
   bool bRepaintContent = UpdateOffset();
   UpdateCaret();
   CFX_RectF rtInvalid;
   bool bRepaintScroll = false;
-  if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_MultiLine) {
+  if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_MultiLine) {
     CFWL_ScrollBar* pScroll = UpdateScroll();
     if (pScroll) {
       rtInvalid = pScroll->GetWidgetRect();
@@ -290,29 +269,25 @@
   }
   if (bRepaintContent || bRepaintScroll) {
     if (bRepaintContent)
-      rtInvalid.Union(m_rtEngine);
+      rtInvalid.Union(m_EngineRect);
     RepaintRect(rtInvalid);
   }
 }
 
 void CFWL_Edit::OnTextWillChange(CFDE_TextEditEngine::TextChange* change) {
-  CFWL_EventTextWillChange event(this);
-  event.previous_text = change->previous_text;
-  event.change_text = change->text;
-  event.selection_start = change->selection_start;
-  event.selection_end = change->selection_end;
-  event.cancelled = false;
-
+  CFWL_EventTextWillChange event(this, change->text, change->previous_text,
+                                 change->selection_start,
+                                 change->selection_end);
   DispatchEvent(&event);
 
-  change->text = event.change_text;
-  change->selection_start = event.selection_start;
-  change->selection_end = event.selection_end;
-  change->cancelled = event.cancelled;
+  change->text = event.GetChangeText();
+  change->selection_start = event.GetSelectionStart();
+  change->selection_end = event.GetSelectionEnd();
+  change->cancelled = event.GetCancelled();
 }
 
 void CFWL_Edit::OnTextChanged() {
-  if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_VAlignMask)
+  if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_VAlignMask)
     UpdateVAlignment();
 
   LayoutScrollBar();
@@ -324,76 +299,57 @@
 }
 
 bool CFWL_Edit::OnValidate(const WideString& wsText) {
-  CFWL_EventValidate event(this);
-  event.wsInsert = wsText;
-  event.bValidate = true;
+  CFWL_EventValidate event(this, wsText);
   DispatchEvent(&event);
-  return event.bValidate;
+  return event.GetValidate();
 }
 
 void CFWL_Edit::SetScrollOffset(float fScrollOffset) {
   m_fScrollOffsetY = fScrollOffset;
 }
 
-void CFWL_Edit::DrawTextBk(CXFA_Graphics* pGraphics,
-                           IFWL_ThemeProvider* pTheme,
-                           const CFX_Matrix* pMatrix) {
-  CFWL_ThemeBackground param;
-  param.m_pWidget = this;
-  param.m_iPart = CFWL_Part::Background;
-  param.m_bStaticBackground = false;
-  param.m_dwStates = m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_ReadOnly
-                         ? CFWL_PartState_ReadOnly
-                         : CFWL_PartState_Normal;
-  uint32_t dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled);
-  if (dwStates)
-    param.m_dwStates = CFWL_PartState_Disabled;
-  param.m_pGraphics = pGraphics;
-  param.m_matrix = *pMatrix;
-  param.m_rtPart = m_rtClient;
-  pTheme->DrawBackground(param);
-
-  if (!IsShowScrollBar(true) || !IsShowScrollBar(false))
-    return;
-
-  CFX_RectF rtScroll = m_pHorzScrollBar->GetWidgetRect();
-
-  CFX_RectF rtStatic(m_rtClient.right() - rtScroll.height,
-                     m_rtClient.bottom() - rtScroll.height, rtScroll.height,
-                     rtScroll.height);
-  param.m_bStaticBackground = true;
-  param.m_bMaximize = true;
-  param.m_rtPart = rtStatic;
-  pTheme->DrawBackground(param);
+void CFWL_Edit::DrawContent(CFGAS_GEGraphics* pGraphics,
+                            const CFX_Matrix& mtMatrix) {
+  DrawContentNonComb(pGraphics, mtMatrix);
+  if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_CombText) {
+    CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
+    CFGAS_GEPath path;
+    const int32_t iLimit = m_nLimit > 0 ? m_nLimit : 1;
+    const float fStep = m_EngineRect.width / iLimit;
+    float fLeft = m_EngineRect.left + 1;
+    for (int32_t i = 1; i < iLimit; i++) {
+      fLeft += fStep;
+      path.AddLine(CFX_PointF(fLeft, m_ClientRect.top),
+                   CFX_PointF(fLeft, m_ClientRect.bottom()));
+    }
+    CFWL_ThemeBackground param(CFWL_ThemePart::Part::kCombTextLine, this,
+                               pGraphics);
+    param.m_matrix = mtMatrix;
+    param.SetPath(&path);
+    GetThemeProvider()->DrawBackground(param);
+  }
 }
 
-void CFWL_Edit::DrawContent(CXFA_Graphics* pGraphics,
-                            IFWL_ThemeProvider* pTheme,
-                            const CFX_Matrix* pMatrix) {
-  pGraphics->SaveGraphState();
-
-  if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_CombText)
-    pGraphics->SaveGraphState();
-
-  CFX_RectF rtClip = m_rtEngine;
-  float fOffSetX = m_rtEngine.left - m_fScrollOffsetX;
-  float fOffSetY = m_rtEngine.top - m_fScrollOffsetY + m_fVAlignOffset;
-
+void CFWL_Edit::DrawContentNonComb(CFGAS_GEGraphics* pGraphics,
+                                   const CFX_Matrix& mtMatrix) {
+  CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
+  CFX_RectF rtClip = m_EngineRect;
+  float fOffSetX = m_EngineRect.left - m_fScrollOffsetX;
+  float fOffSetY = m_EngineRect.top - m_fScrollOffsetY + m_fVAlignOffset;
   CFX_Matrix mt(1, 0, 0, 1, fOffSetX, fOffSetY);
-  if (pMatrix) {
-    rtClip = pMatrix->TransformRect(rtClip);
-    mt.Concat(*pMatrix);
-  }
+  rtClip = mtMatrix.TransformRect(rtClip);
+  mt.Concat(mtMatrix);
 
-  bool bShowSel = !!(m_pProperties->m_dwStates & FWL_WGTSTATE_Focused);
+  bool bShowSel = !!(m_Properties.m_dwStates & FWL_STATE_WGT_Focused);
   if (bShowSel && m_pEditEngine->HasSelection()) {
     size_t sel_start;
     size_t count;
     std::tie(sel_start, count) = m_pEditEngine->GetSelection();
-    std::vector<CFX_RectF> rects =
-        m_pEditEngine->GetCharacterRectsInRange(sel_start, count);
+    std::vector<CFX_RectF> rects = m_pEditEngine->GetCharacterRectsInRange(
+        pdfium::base::checked_cast<int32_t>(sel_start),
+        pdfium::base::checked_cast<int32_t>(count));
 
-    CXFA_GEPath path;
+    CFGAS_GEPath path;
     for (auto& rect : rects) {
       rect.left += fOffSetX;
       rect.top += fOffSetY;
@@ -401,46 +357,21 @@
     }
     pGraphics->SetClipRect(rtClip);
 
-    CFWL_ThemeBackground param;
-    param.m_pGraphics = pGraphics;
-    param.m_matrix = *pMatrix;
-    param.m_pWidget = this;
-    param.m_iPart = CFWL_Part::Background;
-    param.m_pPath = &path;
-    pTheme->DrawBackground(param);
+    CFWL_ThemeBackground param(CFWL_ThemePart::Part::kBackground, this,
+                               pGraphics);
+    param.m_matrix = mtMatrix;
+    param.SetPath(&path);
+    GetThemeProvider()->DrawBackground(param);
   }
 
   CFX_RenderDevice* pRenderDev = pGraphics->GetRenderDevice();
   RenderText(pRenderDev, rtClip, mt);
-
-  if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_CombText) {
-    pGraphics->RestoreGraphState();
-
-    CXFA_GEPath path;
-    int32_t iLimit = m_nLimit > 0 ? m_nLimit : 1;
-    float fStep = m_rtEngine.width / iLimit;
-    float fLeft = m_rtEngine.left + 1;
-    for (int32_t i = 1; i < iLimit; i++) {
-      fLeft += fStep;
-      path.AddLine(CFX_PointF(fLeft, m_rtClient.top),
-                   CFX_PointF(fLeft, m_rtClient.bottom()));
-    }
-
-    CFWL_ThemeBackground param;
-    param.m_pGraphics = pGraphics;
-    param.m_matrix = *pMatrix;
-    param.m_pWidget = this;
-    param.m_iPart = CFWL_Part::CombTextLine;
-    param.m_pPath = &path;
-    pTheme->DrawBackground(param);
-  }
-  pGraphics->RestoreGraphState();
 }
 
 void CFWL_Edit::RenderText(CFX_RenderDevice* pRenderDev,
                            const CFX_RectF& clipRect,
                            const CFX_Matrix& mt) {
-  ASSERT(pRenderDev);
+  DCHECK(pRenderDev);
 
   RetainPtr<CFGAS_GEFont> font = m_pEditEngine->GetFont();
   if (!font)
@@ -477,17 +408,17 @@
 }
 
 void CFWL_Edit::UpdateEditParams() {
-  m_pEditEngine->SetAvailableWidth(m_rtEngine.width);
+  m_pEditEngine->SetAvailableWidth(m_EngineRect.width);
   m_pEditEngine->SetCombText(
-      !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_CombText));
+      !!(m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_CombText));
 
   m_pEditEngine->EnableValidation(
-      !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_Validate));
+      !!(m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_Validate));
   m_pEditEngine->EnablePasswordMode(
-      !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_Password));
+      !!(m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_Password));
 
   uint32_t alignment = 0;
-  switch (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_HAlignMask) {
+  switch (m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_HAlignMask) {
     case FWL_STYLEEXT_EDT_HNear: {
       alignment |= CFX_TxtLineAlignment_Left;
       break;
@@ -503,7 +434,7 @@
     default:
       break;
   }
-  switch (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_HAlignModeMask) {
+  switch (m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_HAlignModeMask) {
     case FWL_STYLEEXT_EDT_Justified: {
       alignment |= CFX_TxtLineAlignment_Justified;
       break;
@@ -514,13 +445,13 @@
   m_pEditEngine->SetAlignment(alignment);
 
   bool auto_hscroll =
-      !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_AutoHScroll);
-  if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_MultiLine) {
+      !!(m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_AutoHScroll);
+  if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_MultiLine) {
     m_pEditEngine->EnableMultiLine(true);
     m_pEditEngine->EnableLineWrap(!auto_hscroll);
     m_pEditEngine->LimitVerticalScroll(
-        (m_pProperties->m_dwStyles & FWL_WGTSTYLE_VScroll) == 0 &&
-        (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_AutoVScroll) == 0);
+        (m_Properties.m_dwStyles & FWL_STYLE_WGT_VScroll) == 0 &&
+        (m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_AutoVScroll) == 0);
   } else {
     m_pEditEngine->EnableMultiLine(false);
     m_pEditEngine->EnableLineWrap(false);
@@ -528,14 +459,8 @@
   }
   m_pEditEngine->LimitHorizontalScroll(!auto_hscroll);
 
-  IFWL_ThemeProvider* theme = GetAvailableTheme();
-  CFWL_ThemePart part;
-  part.m_pWidget = this;
-
-  if (!theme) {
-    m_fFontSize = FWLTHEME_CAPACITY_FontSize;
-    return;
-  }
+  IFWL_ThemeProvider* theme = GetThemeProvider();
+  CFWL_ThemePart part(CFWL_ThemePart::Part::kNone, this);
   m_fFontSize = theme->GetFontSize(part);
 
   RetainPtr<CFGAS_GEFont> pFont = theme->GetFont(part);
@@ -547,7 +472,7 @@
   m_pEditEngine->SetFontSize(m_fFontSize);
   m_pEditEngine->SetLineSpace(theme->GetLineHeight(part));
   m_pEditEngine->SetTabWidth(m_fFontSize);
-  m_pEditEngine->SetVisibleLineCount(m_rtEngine.height /
+  m_pEditEngine->SetVisibleLineCount(m_EngineRect.height /
                                      theme->GetLineHeight(part));
 }
 
@@ -556,13 +481,13 @@
 }
 
 bool CFWL_Edit::UpdateOffset() {
-  CFX_RectF rtCaret = m_rtCaret;
+  CFX_RectF rtCaret = m_CaretRect;
 
-  float fOffSetX = m_rtEngine.left - m_fScrollOffsetX;
-  float fOffSetY = m_rtEngine.top - m_fScrollOffsetY + m_fVAlignOffset;
+  float fOffSetX = m_EngineRect.left - m_fScrollOffsetX;
+  float fOffSetY = m_EngineRect.top - m_fScrollOffsetY + m_fVAlignOffset;
   rtCaret.Offset(fOffSetX, fOffSetY);
 
-  const CFX_RectF& edit_bounds = m_rtEngine;
+  const CFX_RectF& edit_bounds = m_EngineRect;
   if (edit_bounds.Contains(rtCaret)) {
     CFX_RectF contents_bounds = m_pEditEngine->GetContentsBoundingBox();
     contents_bounds.Offset(fOffSetX, fOffSetY);
@@ -591,44 +516,34 @@
 
   m_fScrollOffsetX += offsetX;
   m_fScrollOffsetY += offsetY;
-  if (m_fFontSize > m_rtEngine.height)
+  if (m_fFontSize > m_EngineRect.height)
     m_fScrollOffsetY = 0;
 
   return true;
 }
 
 bool CFWL_Edit::UpdateOffset(CFWL_ScrollBar* pScrollBar, float fPosChanged) {
-  if (pScrollBar == m_pHorzScrollBar.get())
-    m_fScrollOffsetX += fPosChanged;
-  else
-    m_fScrollOffsetY += fPosChanged;
+  m_fScrollOffsetY += fPosChanged;
   return true;
 }
 
 void CFWL_Edit::UpdateVAlignment() {
-  float fSpaceAbove = 0.0f;
-  float fSpaceBelow = 0.0f;
-  IFWL_ThemeProvider* theme = GetAvailableTheme();
-  if (theme) {
-    CFWL_ThemePart part;
-    part.m_pWidget = this;
-
-    CFX_SizeF pSpace = theme->GetSpaceAboveBelow(part);
-    fSpaceAbove = pSpace.width >= 0.1f ? pSpace.width : 0.0f;
-    fSpaceBelow = pSpace.height >= 0.1f ? pSpace.height : 0.0f;
-  }
-
+  IFWL_ThemeProvider* theme = GetThemeProvider();
+  CFWL_ThemePart part(CFWL_ThemePart::Part::kNone, this);
+  const CFX_SizeF pSpace = theme->GetSpaceAboveBelow(part);
+  const float fSpaceAbove = pSpace.width >= 0.1f ? pSpace.width : 0.0f;
+  const float fSpaceBelow = pSpace.height >= 0.1f ? pSpace.height : 0.0f;
   float fOffsetY = 0.0f;
   CFX_RectF contents_bounds = m_pEditEngine->GetContentsBoundingBox();
-  if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_VCenter) {
-    fOffsetY = (m_rtEngine.height - contents_bounds.height) / 2.0f;
+  if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_VCenter) {
+    fOffsetY = (m_EngineRect.height - contents_bounds.height) / 2.0f;
     if (fOffsetY < (fSpaceAbove + fSpaceBelow) / 2.0f &&
         fSpaceAbove < fSpaceBelow) {
       return;
     }
     fOffsetY += (fSpaceAbove - fSpaceBelow) / 2.0f;
-  } else if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_VFar) {
-    fOffsetY = (m_rtEngine.height - contents_bounds.height);
+  } else if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_VFar) {
+    fOffsetY = (m_EngineRect.height - contents_bounds.height);
     fOffsetY -= fSpaceBelow;
   } else {
     fOffsetY += fSpaceAbove;
@@ -637,9 +552,9 @@
 }
 
 void CFWL_Edit::UpdateCaret() {
-  CFX_RectF rtCaret = m_rtCaret;
-  rtCaret.Offset(m_rtEngine.left - m_fScrollOffsetX,
-                 m_rtEngine.top - m_fScrollOffsetY + m_fVAlignOffset);
+  CFX_RectF rtCaret = m_CaretRect;
+  rtCaret.Offset(m_EngineRect.left - m_fScrollOffsetX,
+                 m_EngineRect.top - m_fScrollOffsetY + m_fVAlignOffset);
 
   CFX_RectF rtClient = GetClientRect();
   rtCaret.Intersect(rtClient);
@@ -649,251 +564,139 @@
     rtCaret.width = right - rtCaret.left;
   }
 
-  if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused && !rtCaret.IsEmpty())
+  if (m_Properties.m_dwStates & FWL_STATE_WGT_Focused && !rtCaret.IsEmpty())
     ShowCaret(&rtCaret);
   else
     HideCaret(&rtCaret);
 }
 
 CFWL_ScrollBar* CFWL_Edit::UpdateScroll() {
-  bool bShowHorz = m_pHorzScrollBar && m_pHorzScrollBar->IsVisible();
   bool bShowVert = m_pVertScrollBar && m_pVertScrollBar->IsVisible();
-  if (!bShowHorz && !bShowVert)
+  if (!bShowVert)
     return nullptr;
 
   CFX_RectF contents_bounds = m_pEditEngine->GetContentsBoundingBox();
-  CFWL_ScrollBar* pRepaint = nullptr;
-  if (bShowHorz) {
-    CFX_RectF rtScroll = m_pHorzScrollBar->GetWidgetRect();
-    if (rtScroll.width < contents_bounds.width) {
-      {
-        ScopedUpdateLock update_lock(m_pHorzScrollBar.get());
-        float fRange = contents_bounds.width - rtScroll.width;
-        m_pHorzScrollBar->SetRange(0.0f, fRange);
-
-        float fPos = pdfium::clamp(m_fScrollOffsetX, 0.0f, fRange);
-        m_pHorzScrollBar->SetPos(fPos);
-        m_pHorzScrollBar->SetTrackPos(fPos);
-        m_pHorzScrollBar->SetPageSize(rtScroll.width);
-        m_pHorzScrollBar->SetStepSize(rtScroll.width / 10);
-        m_pHorzScrollBar->RemoveStates(FWL_WGTSTATE_Disabled);
-      }
-      m_pHorzScrollBar->Update();
-      pRepaint = m_pHorzScrollBar.get();
-    } else if ((m_pHorzScrollBar->GetStates() & FWL_WGTSTATE_Disabled) == 0) {
-      {
-        ScopedUpdateLock update_lock(m_pHorzScrollBar.get());
-        m_pHorzScrollBar->SetRange(0, -1);
-        m_pHorzScrollBar->SetStates(FWL_WGTSTATE_Disabled);
-      }
-      m_pHorzScrollBar->Update();
-      pRepaint = m_pHorzScrollBar.get();
-    }
+  CFX_RectF rtScroll = m_pVertScrollBar->GetWidgetRect();
+  if (rtScroll.height < contents_bounds.height) {
+    float fStep = m_pEditEngine->GetLineSpace();
+    float fRange =
+        std::max(contents_bounds.height - m_EngineRect.height, fStep);
+    m_pVertScrollBar->SetRange(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);
+    m_pVertScrollBar->SetStepSize(fStep);
+    m_pVertScrollBar->RemoveStates(FWL_STATE_WGT_Disabled);
+    m_pVertScrollBar->Update();
+    return m_pVertScrollBar;
   }
-
-  if (bShowVert) {
-    CFX_RectF rtScroll = m_pVertScrollBar->GetWidgetRect();
-    if (rtScroll.height < contents_bounds.height) {
-      {
-        ScopedUpdateLock update_lock(m_pHorzScrollBar.get());
-        float fStep = m_pEditEngine->GetLineSpace();
-        float fRange =
-            std::max(contents_bounds.height - m_rtEngine.height, fStep);
-
-        m_pVertScrollBar->SetRange(0.0f, fRange);
-        float fPos = pdfium::clamp(m_fScrollOffsetY, 0.0f, fRange);
-        m_pVertScrollBar->SetPos(fPos);
-        m_pVertScrollBar->SetTrackPos(fPos);
-        m_pVertScrollBar->SetPageSize(rtScroll.height);
-        m_pVertScrollBar->SetStepSize(fStep);
-        m_pVertScrollBar->RemoveStates(FWL_WGTSTATE_Disabled);
-      }
-      m_pVertScrollBar->Update();
-      pRepaint = m_pVertScrollBar.get();
-    } else if ((m_pVertScrollBar->GetStates() & FWL_WGTSTATE_Disabled) == 0) {
-      {
-        ScopedUpdateLock update_lock(m_pHorzScrollBar.get());
-        m_pVertScrollBar->SetRange(0, -1);
-        m_pVertScrollBar->SetStates(FWL_WGTSTATE_Disabled);
-      }
-      m_pVertScrollBar->Update();
-      pRepaint = m_pVertScrollBar.get();
-    }
+  if ((m_pVertScrollBar->GetStates() & FWL_STATE_WGT_Disabled) == 0) {
+    m_pVertScrollBar->SetRange(0, -1);
+    m_pVertScrollBar->SetStates(FWL_STATE_WGT_Disabled);
+    m_pVertScrollBar->Update();
+    return m_pVertScrollBar;
   }
-  return pRepaint;
+  return nullptr;
 }
 
-bool CFWL_Edit::IsShowScrollBar(bool bVert) {
-  if (!bVert)
-    return false;
-  bool bShow =
-      (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_ShowScrollbarFocus)
-          ? (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) ==
-                FWL_WGTSTATE_Focused
-          : true;
-  return bShow && (m_pProperties->m_dwStyles & FWL_WGTSTYLE_VScroll) &&
-         (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_MultiLine) &&
+bool CFWL_Edit::IsShowVertScrollBar() const {
+  const bool bShow =
+      !(m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_ShowScrollbarFocus) ||
+      (m_Properties.m_dwStates & FWL_STATE_WGT_Focused);
+  return bShow && (m_Properties.m_dwStyles & FWL_STYLE_WGT_VScroll) &&
+         (m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_MultiLine) &&
          IsContentHeightOverflow();
 }
 
-bool CFWL_Edit::IsContentHeightOverflow() {
+bool CFWL_Edit::IsContentHeightOverflow() const {
   return m_pEditEngine->GetContentsBoundingBox().height >
-         m_rtEngine.height + 1.0f;
+         m_EngineRect.height + 1.0f;
 }
 
 void CFWL_Edit::Layout() {
-  m_rtClient = GetClientRect();
-  m_rtEngine = m_rtClient;
-  IFWL_ThemeProvider* theme = GetAvailableTheme();
-  if (!theme)
-    return;
+  m_ClientRect = GetClientRect();
+  m_EngineRect = m_ClientRect;
 
+  IFWL_ThemeProvider* theme = GetThemeProvider();
   float fWidth = theme->GetScrollBarWidth();
-  CFWL_ThemePart part;
-  if (!m_pOuter) {
-    part.m_pWidget = this;
+  if (!GetOuter()) {
+    CFWL_ThemePart part(CFWL_ThemePart::Part::kNone, this);
     CFX_RectF pUIMargin = theme->GetUIMargin(part);
-    m_rtEngine.Deflate(pUIMargin.left, pUIMargin.top, pUIMargin.width,
-                       pUIMargin.height);
-  } else if (m_pOuter->GetClassID() == FWL_Type::DateTimePicker) {
-    part.m_pWidget = m_pOuter;
+    m_EngineRect.Deflate(pUIMargin.left, pUIMargin.top, pUIMargin.width,
+                         pUIMargin.height);
+  } else if (GetOuter()->GetClassID() == FWL_Type::DateTimePicker) {
+    CFWL_ThemePart part(CFWL_ThemePart::Part::kNone, GetOuter());
     CFX_RectF pUIMargin = theme->GetUIMargin(part);
-    m_rtEngine.Deflate(pUIMargin.left, pUIMargin.top, pUIMargin.width,
-                       pUIMargin.height);
+    m_EngineRect.Deflate(pUIMargin.left, pUIMargin.top, pUIMargin.width,
+                         pUIMargin.height);
   }
 
-  bool bShowVertScrollbar = IsShowScrollBar(true);
-  bool bShowHorzScrollbar = IsShowScrollBar(false);
+  bool bShowVertScrollbar = IsShowVertScrollBar();
   if (bShowVertScrollbar) {
     InitVerticalScrollBar();
 
     CFX_RectF rtVertScr;
-    if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_OuterScrollbar) {
-      rtVertScr = CFX_RectF(m_rtClient.right() + kEditMargin, m_rtClient.top,
-                            fWidth, m_rtClient.height);
+    if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_OuterScrollbar) {
+      rtVertScr = CFX_RectF(m_ClientRect.right() + kEditMargin,
+                            m_ClientRect.top, fWidth, m_ClientRect.height);
     } else {
-      rtVertScr = CFX_RectF(m_rtClient.right() - fWidth, m_rtClient.top, fWidth,
-                            m_rtClient.height);
-      if (bShowHorzScrollbar)
-        rtVertScr.height -= fWidth;
-      m_rtEngine.width -= fWidth;
+      rtVertScr = CFX_RectF(m_ClientRect.right() - fWidth, m_ClientRect.top,
+                            fWidth, m_ClientRect.height);
+      m_EngineRect.width -= fWidth;
     }
 
     m_pVertScrollBar->SetWidgetRect(rtVertScr);
-    m_pVertScrollBar->RemoveStates(FWL_WGTSTATE_Invisible);
+    m_pVertScrollBar->RemoveStates(FWL_STATE_WGT_Invisible);
     m_pVertScrollBar->Update();
   } else if (m_pVertScrollBar) {
-    m_pVertScrollBar->SetStates(FWL_WGTSTATE_Invisible);
-  }
-
-  if (bShowHorzScrollbar) {
-    InitHorizontalScrollBar();
-
-    CFX_RectF rtHoriScr;
-    if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_OuterScrollbar) {
-      rtHoriScr = CFX_RectF(m_rtClient.left, m_rtClient.bottom() + kEditMargin,
-                            m_rtClient.width, fWidth);
-    } else {
-      rtHoriScr = CFX_RectF(m_rtClient.left, m_rtClient.bottom() - fWidth,
-                            m_rtClient.width, fWidth);
-      if (bShowVertScrollbar)
-        rtHoriScr.width -= fWidth;
-      m_rtEngine.height -= fWidth;
-    }
-    m_pHorzScrollBar->SetWidgetRect(rtHoriScr);
-    m_pHorzScrollBar->RemoveStates(FWL_WGTSTATE_Invisible);
-    m_pHorzScrollBar->Update();
-  } else if (m_pHorzScrollBar) {
-    m_pHorzScrollBar->SetStates(FWL_WGTSTATE_Invisible);
+    m_pVertScrollBar->SetStates(FWL_STATE_WGT_Invisible);
   }
 }
 
 void CFWL_Edit::LayoutScrollBar() {
-  if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_ShowScrollbarFocus) ==
-      0) {
+  if (!(m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_ShowScrollbarFocus))
     return;
-  }
 
-  bool bShowVertScrollbar = IsShowScrollBar(true);
-  bool bShowHorzScrollbar = IsShowScrollBar(false);
-
-  IFWL_ThemeProvider* theme = GetAvailableTheme();
-  float fWidth = theme ? theme->GetScrollBarWidth() : 0;
+  bool bShowVertScrollbar = IsShowVertScrollBar();
+  IFWL_ThemeProvider* theme = GetThemeProvider();
+  float fWidth = theme->GetScrollBarWidth();
   if (bShowVertScrollbar) {
     if (!m_pVertScrollBar) {
       InitVerticalScrollBar();
       CFX_RectF rtVertScr;
-      if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_OuterScrollbar) {
-        rtVertScr = CFX_RectF(m_rtClient.right() + kEditMargin, m_rtClient.top,
-                              fWidth, m_rtClient.height);
+      if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_OuterScrollbar) {
+        rtVertScr = CFX_RectF(m_ClientRect.right() + kEditMargin,
+                              m_ClientRect.top, fWidth, m_ClientRect.height);
       } else {
-        rtVertScr = CFX_RectF(m_rtClient.right() - fWidth, m_rtClient.top,
-                              fWidth, m_rtClient.height);
-        if (bShowHorzScrollbar)
-          rtVertScr.height -= fWidth;
+        rtVertScr = CFX_RectF(m_ClientRect.right() - fWidth, m_ClientRect.top,
+                              fWidth, m_ClientRect.height);
       }
       m_pVertScrollBar->SetWidgetRect(rtVertScr);
       m_pVertScrollBar->Update();
     }
-    m_pVertScrollBar->RemoveStates(FWL_WGTSTATE_Invisible);
+    m_pVertScrollBar->RemoveStates(FWL_STATE_WGT_Invisible);
   } else if (m_pVertScrollBar) {
-    m_pVertScrollBar->SetStates(FWL_WGTSTATE_Invisible);
+    m_pVertScrollBar->SetStates(FWL_STATE_WGT_Invisible);
   }
-
-  if (bShowHorzScrollbar) {
-    if (!m_pHorzScrollBar) {
-      InitHorizontalScrollBar();
-      CFX_RectF rtHoriScr;
-      if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_OuterScrollbar) {
-        rtHoriScr =
-            CFX_RectF(m_rtClient.left, m_rtClient.bottom() + kEditMargin,
-                      m_rtClient.width, fWidth);
-      } else {
-        rtHoriScr = CFX_RectF(m_rtClient.left, m_rtClient.bottom() - fWidth,
-                              m_rtClient.width, fWidth);
-        if (bShowVertScrollbar)
-          rtHoriScr.width -= (fWidth);
-      }
-      m_pHorzScrollBar->SetWidgetRect(rtHoriScr);
-      m_pHorzScrollBar->Update();
-    }
-    m_pHorzScrollBar->RemoveStates(FWL_WGTSTATE_Invisible);
-  } else if (m_pHorzScrollBar) {
-    m_pHorzScrollBar->SetStates(FWL_WGTSTATE_Invisible);
-  }
-  if (bShowVertScrollbar || bShowHorzScrollbar)
+  if (bShowVertScrollbar)
     UpdateScroll();
 }
 
 CFX_PointF CFWL_Edit::DeviceToEngine(const CFX_PointF& pt) {
-  return pt + CFX_PointF(m_fScrollOffsetX - m_rtEngine.left,
-                         m_fScrollOffsetY - m_rtEngine.top - m_fVAlignOffset);
+  return pt + CFX_PointF(m_fScrollOffsetX - m_EngineRect.left,
+                         m_fScrollOffsetY - m_EngineRect.top - m_fVAlignOffset);
 }
 
 void CFWL_Edit::InitVerticalScrollBar() {
   if (m_pVertScrollBar)
     return;
 
-  auto prop = pdfium::MakeUnique<CFWL_WidgetProperties>();
-  prop->m_dwStyleExes = FWL_STYLEEXT_SCB_Vert;
-  prop->m_dwStates = FWL_WGTSTATE_Disabled | FWL_WGTSTATE_Invisible;
-  prop->m_pParent = this;
-  prop->m_pThemeProvider = m_pProperties->m_pThemeProvider;
-  m_pVertScrollBar = pdfium::MakeUnique<CFWL_ScrollBar>(m_pOwnerApp.Get(),
-                                                        std::move(prop), this);
-}
-
-void CFWL_Edit::InitHorizontalScrollBar() {
-  if (m_pHorzScrollBar)
-    return;
-
-  auto prop = pdfium::MakeUnique<CFWL_WidgetProperties>();
-  prop->m_dwStyleExes = FWL_STYLEEXT_SCB_Horz;
-  prop->m_dwStates = FWL_WGTSTATE_Disabled | FWL_WGTSTATE_Invisible;
-  prop->m_pParent = this;
-  prop->m_pThemeProvider = m_pProperties->m_pThemeProvider;
-  m_pHorzScrollBar = pdfium::MakeUnique<CFWL_ScrollBar>(m_pOwnerApp.Get(),
-                                                        std::move(prop), this);
+  m_pVertScrollBar = cppgc::MakeGarbageCollected<CFWL_ScrollBar>(
+      GetFWLApp()->GetHeap()->GetAllocationHandle(), GetFWLApp(),
+      Properties{0, FWL_STYLEEXT_SCB_Vert,
+                 FWL_STATE_WGT_Disabled | FWL_STATE_WGT_Invisible},
+      this);
 }
 
 void CFWL_Edit::ShowCaret(CFX_RectF* pRect) {
@@ -901,15 +704,14 @@
     m_pCaret->ShowCaret();
     if (!pRect->IsEmpty())
       m_pCaret->SetWidgetRect(*pRect);
-    RepaintRect(m_rtEngine);
+    RepaintRect(m_EngineRect);
     return;
   }
 
   CFWL_Widget* pOuter = this;
-  pRect->Offset(m_pProperties->m_rtWidget.left, m_pProperties->m_rtWidget.top);
+  pRect->Offset(m_WidgetRect.left, m_WidgetRect.top);
   while (pOuter->GetOuter()) {
     pOuter = pOuter->GetOuter();
-
     CFX_RectF rtOuter = pOuter->GetWidgetRect();
     pRect->Offset(rtOuter.left, rtOuter.top);
   }
@@ -925,7 +727,7 @@
 void CFWL_Edit::HideCaret(CFX_RectF* pRect) {
   if (m_pCaret) {
     m_pCaret->HideCaret();
-    RepaintRect(m_rtEngine);
+    RepaintRect(m_EngineRect);
     return;
   }
 
@@ -940,55 +742,35 @@
   pXFAWidget->DisplayCaret(false, pRect);
 }
 
-bool CFWL_Edit::ValidateNumberChar(wchar_t cNum) {
-  if (!m_bSetRange)
-    return true;
-
-  WideString wsText = m_pEditEngine->GetText();
-  if (wsText.IsEmpty())
-    return cNum != L'0';
-
-  if (HasSelection())
-    return wsText.GetInteger() <= m_iMax;
-  if (cNum == L'0' && m_CursorPosition == 0)
-    return false;
-
-  int32_t nLen = wsText.GetLength();
-  WideString first = wsText.First(m_CursorPosition);
-  WideString last = wsText.Last(nLen - m_CursorPosition);
-  WideString wsNew = first + cNum + last;
-  return wsNew.GetInteger() <= m_iMax;
-}
-
 void CFWL_Edit::InitCaret() {
   if (m_pCaret)
     return;
 
-  m_pCaret = pdfium::MakeUnique<CFWL_Caret>(
-      m_pOwnerApp.Get(), pdfium::MakeUnique<CFWL_WidgetProperties>(), this);
-  m_pCaret->SetParent(this);
-  m_pCaret->SetStates(m_pProperties->m_dwStates);
+  m_pCaret = cppgc::MakeGarbageCollected<CFWL_Caret>(
+      GetFWLApp()->GetHeap()->GetAllocationHandle(), GetFWLApp(), Properties(),
+      this);
+  m_pCaret->SetStates(m_Properties.m_dwStates);
   UpdateCursorRect();
 }
 
 void CFWL_Edit::UpdateCursorRect() {
   int32_t bidi_level;
-  if (m_pEditEngine->GetLength() > 0) {
-    std::tie(bidi_level, m_rtCaret) =
-        m_pEditEngine->GetCharacterInfo(m_CursorPosition);
+  if (m_pEditEngine->CanGenerateCharacterInfo()) {
+    std::tie(bidi_level, m_CaretRect) = m_pEditEngine->GetCharacterInfo(
+        pdfium::base::checked_cast<int32_t>(m_CursorPosition));
   } else {
     bidi_level = 0;
-    m_rtCaret = CFX_RectF();
+    m_CaretRect = CFX_RectF();
   }
 
   // TODO(dsinclair): This should handle bidi level  ...
 
-  m_rtCaret.width = 1.0f;
+  m_CaretRect.width = 1.0f;
 
   // TODO(hnakashima): Handle correctly edits with empty text instead of using
   // these defaults.
-  if (m_rtCaret.height == 0)
-    m_rtCaret.height = 8.0f;
+  if (m_CaretRect.height == 0)
+    m_CaretRect.height = 8.0f;
 }
 
 void CFWL_Edit::SetCursorPosition(size_t position) {
@@ -1001,32 +783,29 @@
 }
 
 void CFWL_Edit::OnProcessMessage(CFWL_Message* pMessage) {
-  if (!pMessage)
-    return;
-
   switch (pMessage->GetType()) {
-    case CFWL_Message::Type::SetFocus:
-      OnFocusChanged(pMessage, true);
+    case CFWL_Message::Type::kSetFocus:
+      OnFocusGained();
       break;
-    case CFWL_Message::Type::KillFocus:
-      OnFocusChanged(pMessage, false);
+    case CFWL_Message::Type::kKillFocus:
+      OnFocusLost();
       break;
-    case CFWL_Message::Type::Mouse: {
+    case CFWL_Message::Type::kMouse: {
       CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
       switch (pMsg->m_dwCmd) {
-        case FWL_MouseCommand::LeftButtonDown:
+        case CFWL_MessageMouse::MouseCommand::kLeftButtonDown:
           OnLButtonDown(pMsg);
           break;
-        case FWL_MouseCommand::LeftButtonUp:
+        case CFWL_MessageMouse::MouseCommand::kLeftButtonUp:
           OnLButtonUp(pMsg);
           break;
-        case FWL_MouseCommand::LeftButtonDblClk:
+        case CFWL_MessageMouse::MouseCommand::kLeftButtonDblClk:
           OnButtonDoubleClick(pMsg);
           break;
-        case FWL_MouseCommand::Move:
+        case CFWL_MessageMouse::MouseCommand::kMove:
           OnMouseMove(pMsg);
           break;
-        case FWL_MouseCommand::RightButtonDown:
+        case CFWL_MessageMouse::MouseCommand::kRightButtonDown:
           DoRButtonDown(pMsg);
           break;
         default:
@@ -1034,11 +813,11 @@
       }
       break;
     }
-    case CFWL_Message::Type::Key: {
+    case CFWL_Message::Type::kKey: {
       CFWL_MessageKey* pKey = static_cast<CFWL_MessageKey*>(pMessage);
-      if (pKey->m_dwCmd == FWL_KeyCommand::KeyDown)
+      if (pKey->m_dwCmd == CFWL_MessageKey::KeyCommand::kKeyDown)
         OnKeyDown(pKey);
-      else if (pKey->m_dwCmd == FWL_KeyCommand::Char)
+      else if (pKey->m_dwCmd == CFWL_MessageKey::KeyCommand::kChar)
         OnChar(pKey);
       break;
     }
@@ -1055,15 +834,14 @@
     return;
 
   CFWL_Widget* pSrcTarget = pEvent->GetSrcTarget();
-  if ((pSrcTarget == m_pVertScrollBar.get() && m_pVertScrollBar) ||
-      (pSrcTarget == m_pHorzScrollBar.get() && m_pHorzScrollBar)) {
+  if ((pSrcTarget == m_pVertScrollBar && m_pVertScrollBar)) {
     CFWL_EventScroll* pScrollEvent = static_cast<CFWL_EventScroll*>(pEvent);
     OnScroll(static_cast<CFWL_ScrollBar*>(pSrcTarget),
-             pScrollEvent->m_iScrollCode, pScrollEvent->m_fPos);
+             pScrollEvent->GetScrollCode(), pScrollEvent->GetPos());
   }
 }
 
-void CFWL_Edit::OnDrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_Edit::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                              const CFX_Matrix& matrix) {
   DrawWidget(pGraphics, matrix);
 }
@@ -1073,36 +851,34 @@
       m_pEditEngine->GetIndexForPoint(DeviceToEngine(pMsg->m_pos)));
 }
 
-void CFWL_Edit::OnFocusChanged(CFWL_Message* pMsg, bool bSet) {
+void CFWL_Edit::OnFocusGained() {
+  m_Properties.m_dwStates |= FWL_STATE_WGT_Focused;
+  UpdateVAlignment();
+  UpdateOffset();
+  UpdateCaret();
+  LayoutScrollBar();
+}
+
+void CFWL_Edit::OnFocusLost() {
   bool bRepaint = false;
-  if (bSet) {
-    m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused;
-
-    UpdateVAlignment();
-    UpdateOffset();
-    UpdateCaret();
-  } else if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) {
-    m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused;
+  if (m_Properties.m_dwStates & FWL_STATE_WGT_Focused) {
+    m_Properties.m_dwStates &= ~FWL_STATE_WGT_Focused;
     HideCaret(nullptr);
-
     if (HasSelection()) {
       ClearSelection();
       bRepaint = true;
     }
     UpdateOffset();
   }
-
   LayoutScrollBar();
   if (!bRepaint)
     return;
 
-  CFX_RectF rtInvalidate(0, 0, m_pProperties->m_rtWidget.width,
-                         m_pProperties->m_rtWidget.height);
-  RepaintRect(rtInvalidate);
+  RepaintRect(CFX_RectF(0, 0, m_WidgetRect.width, m_WidgetRect.height));
 }
 
 void CFWL_Edit::OnLButtonDown(CFWL_MessageMouse* pMsg) {
-  if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
+  if (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
     return;
 
   m_bLButtonDown = true;
@@ -1118,7 +894,7 @@
       m_pEditEngine->GetIndexForPoint(DeviceToEngine(pMsg->m_pos));
 
   if (index_at_click != m_CursorPosition &&
-      !!(pMsg->m_dwFlags & FWL_KEYFLAG_Shift)) {
+      !!(pMsg->m_dwFlags & XFA_FWL_KeyFlag::kShift)) {
     size_t start = std::min(m_CursorPosition, index_at_click);
     size_t end = std::max(m_CursorPosition, index_at_click);
 
@@ -1129,7 +905,7 @@
   }
 
   if (bRepaint)
-    RepaintRect(m_rtEngine);
+    RepaintRect(m_EngineRect);
 }
 
 void CFWL_Edit::OnLButtonUp(CFWL_MessageMouse* pMsg) {
@@ -1146,11 +922,11 @@
 
   m_pEditEngine->SetSelection(start_idx, count);
   m_CursorPosition = start_idx + count;
-  RepaintRect(m_rtEngine);
+  RepaintRect(m_EngineRect);
 }
 
 void CFWL_Edit::OnMouseMove(CFWL_MessageMouse* pMsg) {
-  bool shift = !!(pMsg->m_dwFlags & FWL_KEYFLAG_Shift);
+  bool shift = !!(pMsg->m_dwFlags & XFA_FWL_KeyFlag::kShift);
   if (!m_bLButtonDown || !shift)
     return;
 
@@ -1177,8 +953,8 @@
 }
 
 void CFWL_Edit::OnKeyDown(CFWL_MessageKey* pMsg) {
-  bool bShift = !!(pMsg->m_dwFlags & FWL_KEYFLAG_Shift);
-  bool bCtrl = !!(pMsg->m_dwFlags & FWL_KEYFLAG_Ctrl);
+  bool bShift = !!(pMsg->m_dwFlags & XFA_FWL_KeyFlag::kShift);
+  bool bCtrl = !!(pMsg->m_dwFlags & XFA_FWL_KeyFlag::kCtrl);
 
   size_t sel_start = m_CursorPosition;
   if (m_pEditEngine->HasSelection()) {
@@ -1188,7 +964,7 @@
     sel_start = start_idx;
   }
 
-  switch (pMsg->m_dwKeyCode) {
+  switch (pMsg->m_dwKeyCodeOrChar) {
     case XFA_FWL_VKEY_Left:
       SetCursorPosition(m_pEditEngine->GetIndexLeft(m_CursorPosition));
       break;
@@ -1211,8 +987,8 @@
                 : m_pEditEngine->GetIndexAtEndOfLine(m_CursorPosition));
       break;
     case XFA_FWL_VKEY_Delete: {
-      if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_ReadOnly) ||
-          (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)) {
+      if ((m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_ReadOnly) ||
+          (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)) {
         break;
       }
 
@@ -1231,17 +1007,17 @@
   if (bShift && sel_start != m_CursorPosition) {
     m_pEditEngine->SetSelection(std::min(sel_start, m_CursorPosition),
                                 std::max(sel_start, m_CursorPosition));
-    RepaintRect(m_rtEngine);
+    RepaintRect(m_EngineRect);
   }
 }
 
 void CFWL_Edit::OnChar(CFWL_MessageKey* pMsg) {
-  if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_ReadOnly) ||
-      (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)) {
+  if ((m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_ReadOnly) ||
+      (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)) {
     return;
   }
 
-  wchar_t c = static_cast<wchar_t>(pMsg->m_dwKeyCode);
+  wchar_t c = static_cast<wchar_t>(pMsg->m_dwKeyCodeOrChar);
   switch (c) {
     case L'\b':
       if (m_CursorPosition > 0) {
@@ -1259,7 +1035,7 @@
       SetCursorPosition(m_CursorPosition + 1);
       break;
     case L'\r':
-      if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_WantReturn) {
+      if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_EDT_WantReturn) {
         m_pEditEngine->Insert(m_CursorPosition, L"\n");
         SetCursorPosition(m_CursorPosition + 1);
       }
@@ -1278,44 +1054,45 @@
 bool CFWL_Edit::OnScroll(CFWL_ScrollBar* pScrollBar,
                          CFWL_EventScroll::Code dwCode,
                          float fPos) {
-  CFX_SizeF fs;
-  pScrollBar->GetRange(&fs.width, &fs.height);
+  float fMin;
+  float fMax;
+  pScrollBar->GetRange(&fMin, &fMax);
   float iCurPos = pScrollBar->GetPos();
   float fStep = pScrollBar->GetStepSize();
   switch (dwCode) {
     case CFWL_EventScroll::Code::Min: {
-      fPos = fs.width;
+      fPos = fMin;
       break;
     }
     case CFWL_EventScroll::Code::Max: {
-      fPos = fs.height;
+      fPos = fMax;
       break;
     }
     case CFWL_EventScroll::Code::StepBackward: {
       fPos -= fStep;
-      if (fPos < fs.width + fStep / 2) {
-        fPos = fs.width;
+      if (fPos < fMin + fStep / 2) {
+        fPos = fMin;
       }
       break;
     }
     case CFWL_EventScroll::Code::StepForward: {
       fPos += fStep;
-      if (fPos > fs.height - fStep / 2) {
-        fPos = fs.height;
+      if (fPos > fMax - fStep / 2) {
+        fPos = fMax;
       }
       break;
     }
     case CFWL_EventScroll::Code::PageBackward: {
       fPos -= pScrollBar->GetPageSize();
-      if (fPos < fs.width) {
-        fPos = fs.width;
+      if (fPos < fMin) {
+        fPos = fMin;
       }
       break;
     }
     case CFWL_EventScroll::Code::PageForward: {
       fPos += pScrollBar->GetPageSize();
-      if (fPos > fs.height) {
-        fPos = fs.height;
+      if (fPos > fMax) {
+        fPos = fMax;
       }
       break;
     }
diff --git a/xfa/fwl/cfwl_edit.h b/xfa/fwl/cfwl_edit.h
index 0242e5c..2a85935 100644
--- a/xfa/fwl/cfwl_edit.h
+++ b/xfa/fwl/cfwl_edit.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,10 +11,10 @@
 #include <utility>
 
 #include "xfa/fde/cfde_texteditengine.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fwl/cfwl_event.h"
 #include "xfa/fwl/cfwl_scrollbar.h"
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
 
 #define FWL_STYLEEXT_EDT_ReadOnly (1L << 0)
 #define FWL_STYLEEXT_EDT_MultiLine (1L << 1)
@@ -38,36 +38,36 @@
 #define FWL_STYLEEXT_EDT_ShowScrollbarFocus (1L << 25)
 #define FWL_STYLEEXT_EDT_OuterScrollbar (1L << 26)
 
-class CFWL_Edit;
+class CFWL_MessageKey;
 class CFWL_MessageMouse;
-class CFWL_WidgetProperties;
 class CFWL_Caret;
+class CFX_RenderDevice;
 
 class CFWL_Edit : public CFWL_Widget, public CFDE_TextEditEngine::Delegate {
  public:
-  CFWL_Edit(const CFWL_App* app,
-            std::unique_ptr<CFWL_WidgetProperties> properties,
-            CFWL_Widget* pOuter);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_Edit() override;
 
   // CFWL_Widget:
+  void PreFinalize() override;
+  void Trace(cppgc::Visitor* visitor) const override;
   FWL_Type GetClassID() const override;
   CFX_RectF GetAutosizedWidgetRect() override;
   CFX_RectF GetWidgetRect() override;
   void Update() override;
   FWL_WidgetHit HitTest(const CFX_PointF& point) override;
   void SetStates(uint32_t dwStates) override;
-  void DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) override;
-  void SetThemeProvider(IFWL_ThemeProvider* pThemeProvider) override;
+  void DrawWidget(CFGAS_GEGraphics* pGraphics,
+                  const CFX_Matrix& matrix) override;
   void OnProcessMessage(CFWL_Message* pMessage) override;
   void OnProcessEvent(CFWL_Event* pEvent) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
+  void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix) override;
 
   virtual void SetText(const WideString& wsText);
   virtual void SetTextSkipNotify(const WideString& wsText);
 
-  int32_t GetTextLength() const;
+  size_t GetTextLength() const;
   WideString GetText() const;
   void ClearText();
 
@@ -80,16 +80,14 @@
   int32_t GetLimit() const;
   void SetLimit(int32_t nLimit);
   void SetAliasChar(wchar_t wAlias);
-  Optional<WideString> Copy();
-  Optional<WideString> Cut();
+  absl::optional<WideString> Copy();
+  absl::optional<WideString> Cut();
   bool Paste(const WideString& wsPaste);
   bool Undo();
   bool Redo();
   bool CanUndo();
   bool CanRedo();
 
-  void SetOuter(CFWL_Widget* pOuter);
-
   // CFDE_TextEditEngine::Delegate
   void NotifyTextFull() override;
   void OnCaretChanged() override;
@@ -100,21 +98,20 @@
   void SetScrollOffset(float fScrollOffset) override;
 
  protected:
+  CFWL_Edit(CFWL_App* app, const Properties& properties, CFWL_Widget* pOuter);
+
   void ShowCaret(CFX_RectF* pRect);
   void HideCaret(CFX_RectF* pRect);
-  const CFX_RectF& GetRTClient() const { return m_rtClient; }
+  const CFX_RectF& GetRTClient() const { return m_ClientRect; }
   CFDE_TextEditEngine* GetTxtEdtEngine() { return m_pEditEngine.get(); }
 
  private:
   void RenderText(CFX_RenderDevice* pRenderDev,
                   const CFX_RectF& clipRect,
                   const CFX_Matrix& mt);
-  void DrawTextBk(CXFA_Graphics* pGraphics,
-                  IFWL_ThemeProvider* pTheme,
-                  const CFX_Matrix* pMatrix);
-  void DrawContent(CXFA_Graphics* pGraphics,
-                   IFWL_ThemeProvider* pTheme,
-                   const CFX_Matrix* pMatrix);
+  void DrawContent(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawContentNonComb(CFGAS_GEGraphics* pGraphics,
+                          const CFX_Matrix& mtMatrix);
 
   void UpdateEditEngine();
   void UpdateEditParams();
@@ -128,17 +125,16 @@
   void LayoutScrollBar();
   CFX_PointF DeviceToEngine(const CFX_PointF& pt);
   void InitVerticalScrollBar();
-  void InitHorizontalScrollBar();
   void InitEngine();
   void InitCaret();
-  bool ValidateNumberChar(wchar_t cNum);
-  bool IsShowScrollBar(bool bVert);
-  bool IsContentHeightOverflow();
+  bool IsShowVertScrollBar() const;
+  bool IsContentHeightOverflow() const;
   void SetCursorPosition(size_t position);
   void UpdateCursorRect();
 
   void DoRButtonDown(CFWL_MessageMouse* pMsg);
-  void OnFocusChanged(CFWL_Message* pMsg, bool bSet);
+  void OnFocusGained();
+  void OnFocusLost();
   void OnLButtonDown(CFWL_MessageMouse* pMsg);
   void OnLButtonUp(CFWL_MessageMouse* pMsg);
   void OnButtonDoubleClick(CFWL_MessageMouse* pMsg);
@@ -149,23 +145,20 @@
                 CFWL_EventScroll::Code dwCode,
                 float fPos);
 
-  CFX_RectF m_rtClient;
-  CFX_RectF m_rtEngine;
-  CFX_RectF m_rtStatic;
-  CFX_RectF m_rtCaret;
+  CFX_RectF m_ClientRect;
+  CFX_RectF m_EngineRect;
+  CFX_RectF m_StaticRect;
+  CFX_RectF m_CaretRect;
   bool m_bLButtonDown = false;
-  bool m_bSetRange = false;
   int32_t m_nLimit = -1;
-  int32_t m_iMax = 0xFFFFFFF;
   float m_fVAlignOffset = 0.0f;
   float m_fScrollOffsetX = 0.0f;
   float m_fScrollOffsetY = 0.0f;
   float m_fFontSize = 0.0f;
   size_t m_CursorPosition = 0;
   std::unique_ptr<CFDE_TextEditEngine> const m_pEditEngine;
-  std::unique_ptr<CFWL_ScrollBar> m_pVertScrollBar;
-  std::unique_ptr<CFWL_ScrollBar> m_pHorzScrollBar;
-  std::unique_ptr<CFWL_Caret> m_pCaret;
+  cppgc::Member<CFWL_ScrollBar> m_pVertScrollBar;
+  cppgc::Member<CFWL_Caret> m_pCaret;
   WideString m_wsCache;
   WideString m_wsFont;
 };
diff --git a/xfa/fwl/cfwl_edit_embeddertest.cpp b/xfa/fwl/cfwl_edit_embeddertest.cpp
index 9253293..6904b8d 100644
--- a/xfa/fwl/cfwl_edit_embeddertest.cpp
+++ b/xfa/fwl/cfwl_edit_embeddertest.cpp
@@ -1,29 +1,53 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "xfa/fwl/cfwl_edit.h"
+
+#include <memory>
+
 #include "core/fxcrt/widestring.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+#include "public/fpdf_ext.h"
 #include "public/fpdf_formfill.h"
 #include "public/fpdf_fwlevent.h"
 #include "testing/embedder_test.h"
+#include "testing/embedder_test_environment.h"
 #include "testing/embedder_test_timer_handling_delegate.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/xfa_js_embedder_test.h"
 
+namespace {
+
+const char kEmailRecommendedFilledChecksum[] =
+    "211e4e46eb347aa2bc7c425556d600b0";
+
+}  // namespace
+
 class CFWLEditEmbedderTest : public XFAJSEmbedderTest {
  protected:
   void SetUp() override {
     EmbedderTest::SetUp();
     SetDelegate(&delegate_);
+
+    // Arbitrary, picked nice even number, 2020-09-13 12:26:40.
+    FSDK_SetTimeFunction([]() -> time_t { return 1600000000; });
+    FSDK_SetLocaltimeFunction([](const time_t* t) { return gmtime(t); });
   }
 
   void TearDown() override {
-    UnloadPage(page());
+    FSDK_SetTimeFunction(nullptr);
+    FSDK_SetLocaltimeFunction(nullptr);
+    // TODO(crbug.com/pdfium/11): A page might not have been loaded if a test
+    // is skipped at runtime. This check for a non-null page should be able to
+    // removed once none of the tests are being skipped for Skia.
+    if (page())
+      UnloadPage(page());
     EmbedderTest::TearDown();
   }
 
   void CreateAndInitializeFormPDF(const char* filename) {
-    EXPECT_TRUE(OpenDocument(filename));
+    ASSERT_TRUE(OpenDocument(filename));
     page_ = LoadPage(0);
     ASSERT_TRUE(page_);
   }
@@ -32,7 +56,7 @@
   EmbedderTestTimerHandlingDelegate delegate() const { return delegate_; }
 
  private:
-  FPDF_PAGE page_;
+  FPDF_PAGE page_ = nullptr;
   EmbedderTestTimerHandlingDelegate delegate_;
 };
 
@@ -59,13 +83,11 @@
   EXPECT_STREQ(L"defgh", WideString::FromUTF16LE(buf, len).c_str());
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_DragMouseSelection DISABLED_DragMouseSelection
-#else
-#define MAYBE_DragMouseSelection DragMouseSelection
-#endif
-TEST_F(CFWLEditEmbedderTest, MAYBE_DragMouseSelection) {
+TEST_F(CFWLEditEmbedderTest, DragMouseSelection) {
+  // TODO(crbug.com/pdfium/11): Fix this test and enable for Skia variants.
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    return;
+
   CreateAndInitializeFormPDF("xfa/email_recommended.pdf");
   FORM_OnLButtonDown(form_handle(), page(), 0, 115, 58);
   for (size_t i = 0; i < 10; ++i)
@@ -91,13 +113,11 @@
   }
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_SimpleFill DISABLED_SimpleFill
-#else
-#define MAYBE_SimpleFill SimpleFill
-#endif
-TEST_F(CFWLEditEmbedderTest, MAYBE_SimpleFill) {
+TEST_F(CFWLEditEmbedderTest, SimpleFill) {
+  // TODO(crbug.com/pdfium/11): Fix this test and enable for Skia variants.
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    return;
+
   CreateAndInitializeFormPDF("xfa/email_recommended.pdf");
   const char kBlankMD5[] = "8dda78a3afaf9f7b5210eb81cacc4600";
   {
@@ -110,22 +130,18 @@
   for (size_t i = 0; i < 10; ++i)
     FORM_OnChar(form_handle(), page(), 'a' + i, 0);
 
-  const char kFilledMD5[] = "211e4e46eb347aa2bc7c425556d600b0";
   {
     ScopedFPDFBitmap page_bitmap =
         RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
-    CompareBitmap(page_bitmap.get(), 612, 792, kFilledMD5);
+    CompareBitmap(page_bitmap.get(), 612, 792, kEmailRecommendedFilledChecksum);
   }
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_FillWithNewLineWithoutMultiline \
-  DISABLED_FillWithNewLineWithoutMultiline
-#else
-#define MAYBE_FillWithNewLineWithoutMultiline FillWithNewLineWithoutMultiline
-#endif
-TEST_F(CFWLEditEmbedderTest, MAYBE_FillWithNewLineWithoutMultiline) {
+TEST_F(CFWLEditEmbedderTest, FillWithNewLineWithoutMultiline) {
+  // TODO(crbug.com/pdfium/11): Fix this test and enable for Skia variants.
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    return;
+
   CreateAndInitializeFormPDF("xfa/email_recommended.pdf");
   FORM_OnLButtonDown(form_handle(), page(), 0, 115, 58);
   for (size_t i = 0; i < 5; ++i)
@@ -134,11 +150,10 @@
   for (size_t i = 5; i < 10; ++i)
     FORM_OnChar(form_handle(), page(), 'a' + i, 0);
 
-  const char kFilledMD5[] = "211e4e46eb347aa2bc7c425556d600b0";
   {
     ScopedFPDFBitmap page_bitmap =
         RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
-    CompareBitmap(page_bitmap.get(), 612, 792, kFilledMD5);
+    CompareBitmap(page_bitmap.get(), 612, 792, kEmailRecommendedFilledChecksum);
   }
 }
 
@@ -157,11 +172,11 @@
   // abcde
   // fghij|
   {
-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
     const char kFilledMultilineMD5[] = "fc1f4d5fdb2c5755005fc525b0a60ec9";
 #else
     const char kFilledMultilineMD5[] = "a5654e027d8b1667c20f3b86d1918003";
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
     ScopedFPDFBitmap page_bitmap =
         RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
     CompareBitmap(page_bitmap.get(), 612, 792, kFilledMultilineMD5);
@@ -182,61 +197,117 @@
   // Should look like:
   // abcde|ghij
   {
-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
     const char kMultilineBackspaceMD5[] = "8bb62a8100ff1e1cc113d4033e0d824e";
 #else
     const char kMultilineBackspaceMD5[] = "a2f1dcab92bb1fb7c2f9ccc70100c989";
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
     ScopedFPDFBitmap page_bitmap =
         RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
     CompareBitmap(page_bitmap.get(), 612, 792, kMultilineBackspaceMD5);
   }
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_DateTimePickerTest DISABLED_DateTimePickerTest
-#else
-#define MAYBE_DateTimePickerTest DateTimePickerTest
-#endif
-TEST_F(CFWLEditEmbedderTest, MAYBE_DateTimePickerTest) {
-  CreateAndInitializeFormPDF("xfa/xfa_date_time_edit.pdf");
-  FORM_OnLButtonDown(form_handle(), page(), 0, 115, 58);
+TEST_F(CFWLEditEmbedderTest, DateTimePickerTest) {
+  // TODO(crbug.com/pdfium/11): Fix this test and enable for Skia variants.
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    return;
 
-  const char kFilledMD5[] = "1036b8837a9dba75c6bd8f9347ae2eb2";
+  CreateAndInitializeFormPDF("xfa/xfa_date_time_edit.pdf");
+
+  // Give focus to date time widget, creating down-arrow button.
+  FORM_OnLButtonDown(form_handle(), page(), 0, 115, 58);
+  FORM_OnLButtonUp(form_handle(), page(), 0, 115, 58);
+  const char kSelectedMD5[] = "1036b8837a9dba75c6bd8f9347ae2eb2";
   {
     ScopedFPDFBitmap page_bitmap =
         RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
-    CompareBitmap(page_bitmap.get(), 612, 792, kFilledMD5);
+    CompareBitmap(page_bitmap.get(), 612, 792, kSelectedMD5);
+  }
+
+  // Click down-arrow button, bringing up calendar widget.
+  FORM_OnLButtonDown(form_handle(), page(), 0, 446, 54);
+  FORM_OnLButtonUp(form_handle(), page(), 0, 446, 54);
+  {
+    ScopedFPDFBitmap page_bitmap =
+        RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
+
+    // TODO(tsepez): hermetic fonts.
+    // const char kCalendarOpenMD5[] = "02de64e7e83c82c1ef0ae484d671a51d";
+    // CompareBitmap(page_bitmap.get(), 612, 792, kCalendarOpenMD5);
+  }
+
+  // Click on date on calendar, putting result into field as text.
+  FORM_OnLButtonDown(form_handle(), page(), 0, 100, 162);
+  FORM_OnLButtonUp(form_handle(), page(), 0, 100, 162);
+  {
+    ScopedFPDFBitmap page_bitmap =
+        RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
+
+    // TODO(tsepez): hermetic fonts.
+    // const char kFilledMD5[] = "1bce66c11f1c87b8d639ce0076ac36d3";
+    // CompareBitmap(page_bitmap.get(), 612, 792, kFilledMD5);
   }
 }
 
 TEST_F(CFWLEditEmbedderTest, ImageEditTest) {
   CreateAndInitializeFormPDF("xfa/xfa_image_edit.pdf");
   FORM_OnLButtonDown(form_handle(), page(), 0, 115, 58);
-
-  const char kFilledMD5[] = "1940568c9ba33bac5d0b1ee9558c76b3";
-  {
-    ScopedFPDFBitmap page_bitmap =
-        RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
-    CompareBitmap(page_bitmap.get(), 612, 792, kFilledMD5);
-  }
+  const char* filled_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "23658ed124114f05518372d41c80e41b";
+    return "101cf6223fa2403fba4c413a8310ab02";
+  }();
+  ScopedFPDFBitmap page_bitmap = RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
+  CompareBitmap(page_bitmap.get(), 612, 792, filled_checksum);
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_ComboBoxTest DISABLED_ComboBoxTest
-#else
-#define MAYBE_ComboBoxTest ComboBoxTest
-#endif
-TEST_F(CFWLEditEmbedderTest, MAYBE_ComboBoxTest) {
+TEST_F(CFWLEditEmbedderTest, ComboBoxTest) {
   CreateAndInitializeFormPDF("xfa/xfa_combobox.pdf");
-  FORM_OnLButtonDown(form_handle(), page(), 0, 115, 58);
 
-  const char kFilledMD5[] = "dad642ae8a5afce2591ffbcabbfc58dd";
+  // Give focus to widget.
+  FORM_OnLButtonDown(form_handle(), page(), 0, 115, 58);
+  FORM_OnLButtonUp(form_handle(), page(), 0, 115, 58);
+  {
+    const char* filled_checksum = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+        return "8c555487e09ee4acf3ace77db5929bdc";
+      return "dad642ae8a5afce2591ffbcabbfc58dd";
+    }();
+    ScopedFPDFBitmap page_bitmap =
+        RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
+    CompareBitmap(page_bitmap.get(), 612, 792, filled_checksum);
+  }
+
+  // Click on down-arrow button, dropdown list appears.
+  FORM_OnLButtonDown(form_handle(), page(), 0, 438, 53);
+  FORM_OnLButtonUp(form_handle(), page(), 0, 438, 53);
   {
     ScopedFPDFBitmap page_bitmap =
         RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
-    CompareBitmap(page_bitmap.get(), 612, 792, kFilledMD5);
+    // TODO(tsepez): hermetic fonts.
+    // const char kFilledMD5[] = "dad642ae8a5afce2591ffbcabbfc58dd";
+    // CompareBitmap(page_bitmap.get(), 612, 792, kFilledMD5);
+  }
+
+  // Enter drop-down list, selection highlighted.
+  FORM_OnMouseMove(form_handle(), page(), 0, 253, 107);
+  {
+    ScopedFPDFBitmap page_bitmap =
+        RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
+    // TODO(tsepez): hermetic fonts.
+    // const char kFilledMD5[] = "dad642ae8a5afce2591ffbcabbfc58dd";
+    // CompareBitmap(page_bitmap.get(), 612, 792, kFilledMD5);
+  }
+
+  // Click on selection, putting result into field.
+  FORM_OnLButtonDown(form_handle(), page(), 0, 253, 107);
+  FORM_OnLButtonUp(form_handle(), page(), 0, 253, 107);
+  {
+    ScopedFPDFBitmap page_bitmap =
+        RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
+    // TODO(tsepez): hermetic fonts.
+    // const char kFilledMD5[] = "dad642ae8a5afce2591ffbcabbfc58dd";
+    // CompareBitmap(page_bitmap.get(), 612, 792, kFilledMD5);
   }
 }
diff --git a/xfa/fwl/cfwl_event.cpp b/xfa/fwl/cfwl_event.cpp
index 5922bc1..24321bb 100644
--- a/xfa/fwl/cfwl_event.cpp
+++ b/xfa/fwl/cfwl_event.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fwl/cfwl_event.h b/xfa/fwl/cfwl_event.h
index 832c01f..ab9606d 100644
--- a/xfa/fwl/cfwl_event.h
+++ b/xfa/fwl/cfwl_event.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,14 @@
 #ifndef XFA_FWL_CFWL_EVENT_H_
 #define XFA_FWL_CFWL_EVENT_H_
 
-#include "core/fxcrt/observed_ptr.h"
-#include "xfa/fwl/cfwl_widget.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "v8/include/cppgc/macros.h"
+
+class CFWL_Widget;
 
 class CFWL_Event {
+  CPPGC_STACK_ALLOCATED();  // Allow Raw/Unowned pointers.
+
  public:
   enum class Type {
     CheckStateChanged,
@@ -33,13 +37,13 @@
   virtual ~CFWL_Event();
 
   Type GetType() const { return m_type; }
-  CFWL_Widget* GetSrcTarget() const { return m_pSrcTarget.Get(); }
-  CFWL_Widget* GetDstTarget() const { return m_pDstTarget.Get(); }
+  CFWL_Widget* GetSrcTarget() const { return m_pSrcTarget; }
+  CFWL_Widget* GetDstTarget() const { return m_pDstTarget; }
 
  private:
   const Type m_type;
-  ObservedPtr<CFWL_Widget> const m_pSrcTarget;
-  ObservedPtr<CFWL_Widget> const m_pDstTarget;
+  UnownedPtr<CFWL_Widget> const m_pSrcTarget;
+  UnownedPtr<CFWL_Widget> const m_pDstTarget;
 };
 
 #endif  // XFA_FWL_CFWL_EVENT_H_
diff --git a/xfa/fwl/cfwl_eventmouse.cpp b/xfa/fwl/cfwl_eventmouse.cpp
index 3f8c773..2182e40 100644
--- a/xfa/fwl/cfwl_eventmouse.cpp
+++ b/xfa/fwl/cfwl_eventmouse.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,11 +6,10 @@
 
 #include "xfa/fwl/cfwl_eventmouse.h"
 
-CFWL_EventMouse::CFWL_EventMouse(CFWL_Widget* pSrcTarget)
-    : CFWL_EventMouse(pSrcTarget, nullptr) {}
-
 CFWL_EventMouse::CFWL_EventMouse(CFWL_Widget* pSrcTarget,
-                                 CFWL_Widget* pDstTarget)
-    : CFWL_Event(CFWL_Event::Type::Mouse, pSrcTarget, pDstTarget) {}
+                                 CFWL_Widget* pDstTarget,
+                                 CFWL_MessageMouse::MouseCommand cmd)
+    : CFWL_Event(CFWL_Event::Type::Mouse, pSrcTarget, pDstTarget),
+      m_dwCmd(cmd) {}
 
-CFWL_EventMouse::~CFWL_EventMouse() {}
+CFWL_EventMouse::~CFWL_EventMouse() = default;
diff --git a/xfa/fwl/cfwl_eventmouse.h b/xfa/fwl/cfwl_eventmouse.h
index e7982fb..5764e93 100644
--- a/xfa/fwl/cfwl_eventmouse.h
+++ b/xfa/fwl/cfwl_eventmouse.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,11 +12,15 @@
 
 class CFWL_EventMouse final : public CFWL_Event {
  public:
-  explicit CFWL_EventMouse(CFWL_Widget* pSrcTarget);
-  CFWL_EventMouse(CFWL_Widget* pSrcTarget, CFWL_Widget* pDstTarget);
+  CFWL_EventMouse(CFWL_Widget* pSrcTarget,
+                  CFWL_Widget* pDstTarget,
+                  CFWL_MessageMouse::MouseCommand cmd);
   ~CFWL_EventMouse() override;
 
-  FWL_MouseCommand m_dwCmd = FWL_MouseCommand::LeftButtonDown;
+  CFWL_MessageMouse::MouseCommand GetCommand() const { return m_dwCmd; }
+
+ private:
+  const CFWL_MessageMouse::MouseCommand m_dwCmd;
 };
 
 #endif  // XFA_FWL_CFWL_EVENTMOUSE_H_
diff --git a/xfa/fwl/cfwl_eventscroll.cpp b/xfa/fwl/cfwl_eventscroll.cpp
index 0e9e26c..11df109 100644
--- a/xfa/fwl/cfwl_eventscroll.cpp
+++ b/xfa/fwl/cfwl_eventscroll.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,11 @@
 
 #include "xfa/fwl/cfwl_eventscroll.h"
 
-CFWL_EventScroll::CFWL_EventScroll(CFWL_Widget* pSrcTarget)
-    : CFWL_Event(CFWL_Event::Type::Scroll, pSrcTarget) {}
+CFWL_EventScroll::CFWL_EventScroll(CFWL_Widget* pSrcTarget,
+                                   Code code,
+                                   float pos)
+    : CFWL_Event(CFWL_Event::Type::Scroll, pSrcTarget),
+      m_iScrollCode(code),
+      m_fPos(pos) {}
 
-CFWL_EventScroll::~CFWL_EventScroll() {}
+CFWL_EventScroll::~CFWL_EventScroll() = default;
diff --git a/xfa/fwl/cfwl_eventscroll.h b/xfa/fwl/cfwl_eventscroll.h
index 78960c8..ce97b0b 100644
--- a/xfa/fwl/cfwl_eventscroll.h
+++ b/xfa/fwl/cfwl_eventscroll.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -24,11 +24,15 @@
     EndScroll,
   };
 
-  explicit CFWL_EventScroll(CFWL_Widget* pSrcTarget);
+  CFWL_EventScroll(CFWL_Widget* pSrcTarget, Code code, float pos);
   ~CFWL_EventScroll() override;
 
-  Code m_iScrollCode = Code::None;
-  float m_fPos = 0.0f;
+  Code GetScrollCode() const { return m_iScrollCode; }
+  float GetPos() const { return m_fPos; }
+
+ private:
+  const Code m_iScrollCode;
+  const float m_fPos;
 };
 
 #endif  // XFA_FWL_CFWL_EVENTSCROLL_H_
diff --git a/xfa/fwl/cfwl_eventselectchanged.cpp b/xfa/fwl/cfwl_eventselectchanged.cpp
index bc68595..e385a9c 100644
--- a/xfa/fwl/cfwl_eventselectchanged.cpp
+++ b/xfa/fwl/cfwl_eventselectchanged.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,22 @@
 
 #include "xfa/fwl/cfwl_eventselectchanged.h"
 
-CFWL_EventSelectChanged::CFWL_EventSelectChanged(CFWL_Widget* pSrcTarget)
-    : CFWL_Event(CFWL_Event::Type::SelectChanged, pSrcTarget) {}
+CFWL_EventSelectChanged::CFWL_EventSelectChanged(CFWL_Widget* pSrcTarget,
+                                                 bool bLButtonUp)
+    : CFWL_Event(CFWL_Event::Type::SelectChanged, pSrcTarget),
+      m_bLButtonUp(bLButtonUp),
+      m_iYear(-1),
+      m_iMonth(-1),
+      m_iDay(-1) {}
 
-CFWL_EventSelectChanged::~CFWL_EventSelectChanged() {}
+CFWL_EventSelectChanged::CFWL_EventSelectChanged(CFWL_Widget* pSrcTarget,
+                                                 int32_t iYear,
+                                                 int32_t iMonth,
+                                                 int32_t iDay)
+    : CFWL_Event(CFWL_Event::Type::SelectChanged, pSrcTarget),
+      m_bLButtonUp(false),
+      m_iYear(iYear),
+      m_iMonth(iMonth),
+      m_iDay(iDay) {}
+
+CFWL_EventSelectChanged::~CFWL_EventSelectChanged() = default;
diff --git a/xfa/fwl/cfwl_eventselectchanged.h b/xfa/fwl/cfwl_eventselectchanged.h
index d4b68b3..8f79fe3 100644
--- a/xfa/fwl/cfwl_eventselectchanged.h
+++ b/xfa/fwl/cfwl_eventselectchanged.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,20 +7,32 @@
 #ifndef XFA_FWL_CFWL_EVENTSELECTCHANGED_H_
 #define XFA_FWL_CFWL_EVENTSELECTCHANGED_H_
 
+#include <stdint.h>
+
 #include "xfa/fwl/cfwl_event.h"
 
 class CFWL_EventSelectChanged final : public CFWL_Event {
  public:
-  explicit CFWL_EventSelectChanged(CFWL_Widget* pSrcTarget);
+  CFWL_EventSelectChanged(CFWL_Widget* pSrcTarget, bool bLButtonUp);
+  CFWL_EventSelectChanged(CFWL_Widget* pSrcTarget,
+                          int32_t iYear,
+                          int32_t iMonth,
+                          int32_t iDay);
   ~CFWL_EventSelectChanged() override;
 
+  bool GetLButtonUp() const { return m_bLButtonUp; }
+  int32_t GetYear() const { return m_iYear; }
+  int32_t GetMonth() const { return m_iMonth; }
+  int32_t GetDay() const { return m_iDay; }
+
+ protected:
   // Used by ComboBox.
-  bool bLButtonUp = false;
+  const bool m_bLButtonUp;
 
   // Used by DateTimePIcker
-  int32_t iYear = -1;
-  int32_t iMonth = -1;
-  int32_t iDay = -1;
+  const int32_t m_iYear;
+  const int32_t m_iMonth;
+  const int32_t m_iDay;
 };
 
 #endif  // XFA_FWL_CFWL_EVENTSELECTCHANGED_H_
diff --git a/xfa/fwl/cfwl_eventtarget.cpp b/xfa/fwl/cfwl_eventtarget.cpp
deleted file mode 100644
index 4b3d87c..0000000
--- a/xfa/fwl/cfwl_eventtarget.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fwl/cfwl_eventtarget.h"
-
-#include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fwl/ifwl_widgetdelegate.h"
-
-CFWL_EventTarget::CFWL_EventTarget(CFWL_Widget* pListener)
-    : m_pListener(pListener) {}
-
-CFWL_EventTarget::~CFWL_EventTarget() {}
-
-void CFWL_EventTarget::SetEventSource(CFWL_Widget* pSource) {
-  if (pSource)
-    m_widgets.insert(pSource);
-}
-
-bool CFWL_EventTarget::ProcessEvent(CFWL_Event* pEvent) {
-  IFWL_WidgetDelegate* pDelegate = m_pListener->GetDelegate();
-  if (!pDelegate)
-    return false;
-  if (!m_widgets.empty() && m_widgets.count(pEvent->GetSrcTarget()) == 0)
-    return false;
-
-  pDelegate->OnProcessEvent(pEvent);
-  return true;
-}
diff --git a/xfa/fwl/cfwl_eventtarget.h b/xfa/fwl/cfwl_eventtarget.h
deleted file mode 100644
index f614319..0000000
--- a/xfa/fwl/cfwl_eventtarget.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FWL_CFWL_EVENTTARGET_H_
-#define XFA_FWL_CFWL_EVENTTARGET_H_
-
-#include <set>
-
-#include "xfa/fwl/cfwl_event.h"
-
-class CFWL_Event;
-class CFWL_Widget;
-
-class CFWL_EventTarget {
- public:
-  explicit CFWL_EventTarget(CFWL_Widget* pListener);
-  ~CFWL_EventTarget();
-
-  void SetEventSource(CFWL_Widget* pSource);
-  bool ProcessEvent(CFWL_Event* pEvent);
-
-  bool IsValid() const { return m_bValid; }
-  void FlagInvalid() { m_bValid = false; }
-
- private:
-  bool m_bValid = true;
-  CFWL_Widget* const m_pListener;
-  std::set<CFWL_Widget*> m_widgets;
-};
-
-#endif  // XFA_FWL_CFWL_EVENTTARGET_H_
diff --git a/xfa/fwl/cfwl_eventtextwillchange.cpp b/xfa/fwl/cfwl_eventtextwillchange.cpp
index 22b1100..6052cb7 100644
--- a/xfa/fwl/cfwl_eventtextwillchange.cpp
+++ b/xfa/fwl/cfwl_eventtextwillchange.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,16 @@
 
 #include "xfa/fwl/cfwl_eventtextwillchange.h"
 
-CFWL_EventTextWillChange::CFWL_EventTextWillChange(CFWL_Widget* pSrcTarget)
-    : CFWL_Event(CFWL_Event::Type::TextWillChange, pSrcTarget) {}
+CFWL_EventTextWillChange::CFWL_EventTextWillChange(
+    CFWL_Widget* pSrcTarget,
+    const WideString& change_text,
+    const WideString& previous_text,
+    size_t selection_start,
+    size_t selection_end)
+    : CFWL_Event(CFWL_Event::Type::TextWillChange, pSrcTarget),
+      change_text_(change_text),
+      previous_text_(previous_text),
+      selection_start_(selection_start),
+      selection_end_(selection_end) {}
 
 CFWL_EventTextWillChange::~CFWL_EventTextWillChange() = default;
diff --git a/xfa/fwl/cfwl_eventtextwillchange.h b/xfa/fwl/cfwl_eventtextwillchange.h
index a1bfe8c..65c0d89 100644
--- a/xfa/fwl/cfwl_eventtextwillchange.h
+++ b/xfa/fwl/cfwl_eventtextwillchange.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,18 +7,42 @@
 #ifndef XFA_FWL_CFWL_EVENTTEXTWILLCHANGE_H_
 #define XFA_FWL_CFWL_EVENTTEXTWILLCHANGE_H_
 
+#include "core/fxcrt/widestring.h"
 #include "xfa/fwl/cfwl_event.h"
 
 class CFWL_EventTextWillChange final : public CFWL_Event {
  public:
-  explicit CFWL_EventTextWillChange(CFWL_Widget* pSrcTarget);
+  CFWL_EventTextWillChange(CFWL_Widget* pSrcTarget,
+                           const WideString& change_text,
+                           const WideString& previous_text,
+                           size_t selection_start,
+                           size_t selection_end);
   ~CFWL_EventTextWillChange() override;
 
-  WideString change_text;
-  WideString previous_text;
-  bool cancelled = false;
-  size_t selection_start = 0;
-  size_t selection_end = 0;
+  WideString GetChangeText() const { return change_text_; }
+  WideString GetPreviousText() const { return previous_text_; }
+  size_t GetSelectionStart() const { return selection_start_; }
+  size_t GetSelectionEnd() const { return selection_end_; }
+  bool GetCancelled() const { return cancelled_; }
+
+  void SetChangeText(const WideString& change_text) {
+    change_text_ = change_text;
+  }
+  void SetPreviousText(const WideString& previous_text) {
+    previous_text_ = previous_text;
+  }
+  void SetSelectionStart(size_t selection_start) {
+    selection_start_ = selection_start;
+  }
+  void SetSelectionEnd(size_t selection_end) { selection_end_ = selection_end; }
+  void SetCancelled(bool cancelled) { cancelled_ = cancelled; }
+
+ protected:
+  WideString change_text_;
+  WideString previous_text_;
+  size_t selection_start_;
+  size_t selection_end_;
+  bool cancelled_ = false;
 };
 
 #endif  // XFA_FWL_CFWL_EVENTTEXTWILLCHANGE_H_
diff --git a/xfa/fwl/cfwl_eventvalidate.cpp b/xfa/fwl/cfwl_eventvalidate.cpp
index c54d491..37a3ee7 100644
--- a/xfa/fwl/cfwl_eventvalidate.cpp
+++ b/xfa/fwl/cfwl_eventvalidate.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "xfa/fwl/cfwl_eventvalidate.h"
 
-CFWL_EventValidate::CFWL_EventValidate(CFWL_Widget* pSrcTarget)
-    : CFWL_Event(CFWL_Event::Type::Validate, pSrcTarget) {}
+CFWL_EventValidate::CFWL_EventValidate(CFWL_Widget* pSrcTarget,
+                                       const WideString& wsInsert)
+    : CFWL_Event(CFWL_Event::Type::Validate, pSrcTarget),
+      m_wsInsert(wsInsert) {}
 
-CFWL_EventValidate::~CFWL_EventValidate() {}
+CFWL_EventValidate::~CFWL_EventValidate() = default;
diff --git a/xfa/fwl/cfwl_eventvalidate.h b/xfa/fwl/cfwl_eventvalidate.h
index 26e3c7f..de2b722 100644
--- a/xfa/fwl/cfwl_eventvalidate.h
+++ b/xfa/fwl/cfwl_eventvalidate.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,21 @@
 #ifndef XFA_FWL_CFWL_EVENTVALIDATE_H_
 #define XFA_FWL_CFWL_EVENTVALIDATE_H_
 
+#include "core/fxcrt/widestring.h"
 #include "xfa/fwl/cfwl_event.h"
 
 class CFWL_EventValidate final : public CFWL_Event {
  public:
-  explicit CFWL_EventValidate(CFWL_Widget* pSrcTarget);
+  CFWL_EventValidate(CFWL_Widget* pSrcTarget, const WideString& wsInsert);
   ~CFWL_EventValidate() override;
 
-  bool bValidate = false;
-  WideString wsInsert;
+  WideString GetInsert() const { return m_wsInsert; }
+  bool GetValidate() const { return m_bValidate; }
+  void SetValidate(bool bValidate) { m_bValidate = bValidate; }
+
+ protected:
+  const WideString m_wsInsert;
+  bool m_bValidate = true;
 };
 
 #endif  // XFA_FWL_CFWL_EVENTVALIDATE_H_
diff --git a/xfa/fwl/cfwl_listbox.cpp b/xfa/fwl/cfwl_listbox.cpp
index b2b0f2e..9f4c89a 100644
--- a/xfa/fwl/cfwl_listbox.cpp
+++ b/xfa/fwl/cfwl_listbox.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,9 +10,11 @@
 #include <memory>
 #include <utility>
 
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fde/cfde_textout.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
 #include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_messagekey.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
@@ -29,12 +31,18 @@
 
 }  // namespace
 
-CFWL_ListBox::CFWL_ListBox(const CFWL_App* app,
-                           std::unique_ptr<CFWL_WidgetProperties> properties,
+CFWL_ListBox::CFWL_ListBox(CFWL_App* app,
+                           const Properties& properties,
                            CFWL_Widget* pOuter)
-    : CFWL_Widget(app, std::move(properties), pOuter) {}
+    : CFWL_Widget(app, properties, pOuter) {}
 
-CFWL_ListBox::~CFWL_ListBox() {}
+CFWL_ListBox::~CFWL_ListBox() = default;
+
+void CFWL_ListBox::Trace(cppgc::Visitor* visitor) const {
+  CFWL_Widget::Trace(visitor);
+  visitor->Trace(m_pHorzScrollBar);
+  visitor->Trace(m_pVertScrollBar);
+}
 
 FWL_Type CFWL_ListBox::GetClassID() const {
   return FWL_Type::ListBox;
@@ -43,91 +51,74 @@
 void CFWL_ListBox::Update() {
   if (IsLocked())
     return;
-  if (!m_pProperties->m_pThemeProvider)
-    m_pProperties->m_pThemeProvider = GetAvailableTheme();
 
-  switch (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_LTB_AlignMask) {
-    case FWL_STYLEEXT_LTB_LeftAlign: {
+  switch (m_Properties.m_dwStyleExts & FWL_STYLEEXT_LTB_AlignMask) {
+    case FWL_STYLEEXT_LTB_LeftAlign:
       m_iTTOAligns = FDE_TextAlignment::kCenterLeft;
       break;
-    }
-    case FWL_STYLEEXT_LTB_RightAlign: {
+    case FWL_STYLEEXT_LTB_RightAlign:
       m_iTTOAligns = FDE_TextAlignment::kCenterRight;
       break;
-    }
     case FWL_STYLEEXT_LTB_CenterAlign:
-    default: {
+    default:
       m_iTTOAligns = FDE_TextAlignment::kCenter;
       break;
-    }
   }
   m_TTOStyles.single_line_ = true;
   m_fScorllBarWidth = GetScrollWidth();
-  CalcSize(false);
+  CalcSize();
 }
 
 FWL_WidgetHit CFWL_ListBox::HitTest(const CFX_PointF& point) {
-  if (IsShowScrollBar(false)) {
+  if (IsShowHorzScrollBar()) {
     CFX_RectF rect = m_pHorzScrollBar->GetWidgetRect();
     if (rect.Contains(point))
       return FWL_WidgetHit::HScrollBar;
   }
-  if (IsShowScrollBar(true)) {
+  if (IsShowVertScrollBar()) {
     CFX_RectF rect = m_pVertScrollBar->GetWidgetRect();
     if (rect.Contains(point))
       return FWL_WidgetHit::VScrollBar;
   }
-  if (m_rtClient.Contains(point))
+  if (m_ClientRect.Contains(point))
     return FWL_WidgetHit::Client;
   return FWL_WidgetHit::Unknown;
 }
 
-void CFWL_ListBox::DrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_ListBox::DrawWidget(CFGAS_GEGraphics* pGraphics,
                               const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
 
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
-  if (!pTheme)
-    return;
-
-  pGraphics->SaveGraphState();
+  CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
   if (HasBorder())
-    DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
+    DrawBorder(pGraphics, CFWL_ThemePart::Part::kBorder, matrix);
 
-  CFX_RectF rtClip(m_rtConent);
-  if (IsShowScrollBar(false))
+  CFX_RectF rtClip(m_ContentRect);
+  if (IsShowHorzScrollBar())
     rtClip.height -= m_fScorllBarWidth;
-  if (IsShowScrollBar(true))
+  if (IsShowVertScrollBar())
     rtClip.width -= m_fScorllBarWidth;
 
   pGraphics->SetClipRect(matrix.TransformRect(rtClip));
-  if ((m_pProperties->m_dwStyles & FWL_WGTSTYLE_NoBackground) == 0)
-    DrawBkground(pGraphics, pTheme, &matrix);
+  if ((m_Properties.m_dwStyles & FWL_STYLE_WGT_NoBackground) == 0)
+    DrawBkground(pGraphics, matrix);
 
-  DrawItems(pGraphics, pTheme, &matrix);
-  pGraphics->RestoreGraphState();
-}
-
-void CFWL_ListBox::SetThemeProvider(IFWL_ThemeProvider* pThemeProvider) {
-  if (pThemeProvider)
-    m_pProperties->m_pThemeProvider = pThemeProvider;
+  DrawItems(pGraphics, matrix);
 }
 
 int32_t CFWL_ListBox::CountSelItems() {
   int32_t iRet = 0;
   int32_t iCount = CountItems(this);
   for (int32_t i = 0; i < iCount; i++) {
-    CFWL_ListItem* pItem = GetItem(this, i);
-    if (!pItem)
-      continue;
-    if (pItem->GetStates() & FWL_ITEMSTATE_LTB_Selected)
+    Item* pItem = GetItem(this, i);
+    if (pItem && pItem->IsSelected())
       iRet++;
   }
   return iRet;
 }
 
-CFWL_ListItem* CFWL_ListBox::GetSelItem(int32_t nIndexSel) {
+CFWL_ListBox::Item* CFWL_ListBox::GetSelItem(int32_t nIndexSel) {
   int32_t idx = GetSelIndex(nIndexSel);
   if (idx < 0)
     return nullptr;
@@ -138,10 +129,10 @@
   int32_t index = 0;
   int32_t iCount = CountItems(this);
   for (int32_t i = 0; i < iCount; i++) {
-    CFWL_ListItem* pItem = GetItem(this, i);
+    Item* pItem = GetItem(this, i);
     if (!pItem)
       return -1;
-    if (pItem->GetStates() & FWL_ITEMSTATE_LTB_Selected) {
+    if (pItem->IsSelected()) {
       if (index == nIndex)
         return i;
       index++;
@@ -150,7 +141,7 @@
   return -1;
 }
 
-void CFWL_ListBox::SetSelItem(CFWL_ListItem* pItem, bool bSelect) {
+void CFWL_ListBox::SetSelItem(Item* pItem, bool bSelect) {
   if (!pItem) {
     if (bSelect) {
       SelectAll();
@@ -161,14 +152,14 @@
     return;
   }
   if (IsMultiSelection())
-    SetSelectionDirect(pItem, bSelect);
+    pItem->SetSelected(bSelect);
   else
     SetSelection(pItem, pItem, bSelect);
 }
 
-CFWL_ListItem* CFWL_ListBox::GetListItem(CFWL_ListItem* pItem,
-                                         uint32_t dwKeyCode) {
-  CFWL_ListItem* hRet = nullptr;
+CFWL_ListBox::Item* CFWL_ListBox::GetListItem(Item* pItem,
+                                              XFA_FWL_VKEYCODE dwKeyCode) {
+  Item* hRet = nullptr;
   switch (dwKeyCode) {
     case XFA_FWL_VKEY_Up:
     case XFA_FWL_VKEY_Down:
@@ -196,57 +187,41 @@
   return hRet;
 }
 
-void CFWL_ListBox::SetSelection(CFWL_ListItem* hStart,
-                                CFWL_ListItem* hEnd,
-                                bool bSelected) {
+void CFWL_ListBox::SetSelection(Item* hStart, Item* hEnd, bool bSelected) {
   int32_t iStart = GetItemIndex(this, hStart);
   int32_t iEnd = GetItemIndex(this, hEnd);
-  if (iStart > iEnd) {
-    int32_t iTemp = iStart;
-    iStart = iEnd;
-    iEnd = iTemp;
-  }
+  if (iStart > iEnd)
+    std::swap(iStart, iEnd);
   if (bSelected) {
     int32_t iCount = CountItems(this);
     for (int32_t i = 0; i < iCount; i++) {
-      CFWL_ListItem* pItem = GetItem(this, i);
-      SetSelectionDirect(pItem, false);
+      Item* pItem = GetItem(this, i);
+      if (pItem)
+        pItem->SetSelected(false);
     }
   }
-  for (; iStart <= iEnd; iStart++) {
-    CFWL_ListItem* pItem = GetItem(this, iStart);
-    SetSelectionDirect(pItem, bSelected);
+  while (iStart <= iEnd) {
+    Item* pItem = GetItem(this, iStart);
+    if (pItem)
+      pItem->SetSelected(bSelected);
+    ++iStart;
   }
 }
 
-void CFWL_ListBox::SetSelectionDirect(CFWL_ListItem* pItem, bool bSelect) {
-  if (!pItem)
-    return;
-
-  uint32_t dwOldStyle = pItem->GetStates();
-  bSelect ? dwOldStyle |= FWL_ITEMSTATE_LTB_Selected
-          : dwOldStyle &= ~FWL_ITEMSTATE_LTB_Selected;
-  pItem->SetStates(dwOldStyle);
-}
-
 bool CFWL_ListBox::IsMultiSelection() const {
-  return m_pProperties->m_dwStyleExes & FWL_STYLEEXT_LTB_MultiSelection;
-}
-
-bool CFWL_ListBox::IsItemSelected(CFWL_ListItem* pItem) {
-  return pItem && (pItem->GetStates() & FWL_ITEMSTATE_LTB_Selected) != 0;
+  return m_Properties.m_dwStyleExts & FWL_STYLEEXT_LTB_MultiSelection;
 }
 
 void CFWL_ListBox::ClearSelection() {
   bool bMulti = IsMultiSelection();
   int32_t iCount = CountItems(this);
   for (int32_t i = 0; i < iCount; i++) {
-    CFWL_ListItem* pItem = GetItem(this, i);
+    Item* pItem = GetItem(this, i);
     if (!pItem)
       continue;
-    if (!(pItem->GetStates() & FWL_ITEMSTATE_LTB_Selected))
+    if (!pItem->IsSelected())
       continue;
-    SetSelectionDirect(pItem, false);
+    pItem->SetSelected(false);
     if (!bMulti)
       return;
   }
@@ -260,42 +235,36 @@
   if (iCount <= 0)
     return;
 
-  CFWL_ListItem* pItemStart = GetItem(this, 0);
-  CFWL_ListItem* pItemEnd = GetItem(this, iCount - 1);
+  Item* pItemStart = GetItem(this, 0);
+  Item* pItemEnd = GetItem(this, iCount - 1);
   SetSelection(pItemStart, pItemEnd, false);
 }
 
-CFWL_ListItem* CFWL_ListBox::GetFocusedItem() {
+CFWL_ListBox::Item* CFWL_ListBox::GetFocusedItem() {
   int32_t iCount = CountItems(this);
   for (int32_t i = 0; i < iCount; i++) {
-    CFWL_ListItem* pItem = GetItem(this, i);
+    Item* pItem = GetItem(this, i);
     if (!pItem)
-      return nullptr;
-    if (pItem->GetStates() & FWL_ITEMSTATE_LTB_Focused)
+      break;
+    if (pItem->IsFocused())
       return pItem;
   }
   return nullptr;
 }
 
-void CFWL_ListBox::SetFocusItem(CFWL_ListItem* pItem) {
-  CFWL_ListItem* hFocus = GetFocusedItem();
+void CFWL_ListBox::SetFocusItem(Item* pItem) {
+  Item* hFocus = GetFocusedItem();
   if (pItem == hFocus)
     return;
 
-  if (hFocus) {
-    uint32_t dwStyle = hFocus->GetStates();
-    dwStyle &= ~FWL_ITEMSTATE_LTB_Focused;
-    hFocus->SetStates(dwStyle);
-  }
-  if (pItem) {
-    uint32_t dwStyle = pItem->GetStates();
-    dwStyle |= FWL_ITEMSTATE_LTB_Focused;
-    pItem->SetStates(dwStyle);
-  }
+  if (hFocus)
+    hFocus->SetFocused(false);
+  if (pItem)
+    pItem->SetFocused(true);
 }
 
-CFWL_ListItem* CFWL_ListBox::GetItemAtPoint(const CFX_PointF& point) {
-  CFX_PointF pos = point - m_rtConent.TopLeft();
+CFWL_ListBox::Item* CFWL_ListBox::GetItemAtPoint(const CFX_PointF& point) {
+  CFX_PointF pos = point - m_ContentRect.TopLeft();
   float fPosX = 0.0f;
   if (m_pHorzScrollBar)
     fPosX = m_pHorzScrollBar->GetPos();
@@ -306,7 +275,7 @@
 
   int32_t nCount = CountItems(this);
   for (int32_t i = 0; i < nCount; i++) {
-    CFWL_ListItem* pItem = GetItem(this, i);
+    Item* pItem = GetItem(this, i);
     if (!pItem)
       continue;
 
@@ -318,19 +287,19 @@
   return nullptr;
 }
 
-bool CFWL_ListBox::ScrollToVisible(CFWL_ListItem* pItem) {
+bool CFWL_ListBox::ScrollToVisible(Item* pItem) {
   if (!m_pVertScrollBar)
     return false;
 
   CFX_RectF rtItem = pItem ? pItem->GetRect() : CFX_RectF();
   bool bScroll = false;
   float fPosY = m_pVertScrollBar->GetPos();
-  rtItem.Offset(0, -fPosY + m_rtConent.top);
-  if (rtItem.top < m_rtConent.top) {
-    fPosY += rtItem.top - m_rtConent.top;
+  rtItem.Offset(0, -fPosY + m_ContentRect.top);
+  if (rtItem.top < m_ContentRect.top) {
+    fPosY += rtItem.top - m_ContentRect.top;
     bScroll = true;
-  } else if (rtItem.bottom() > m_rtConent.bottom()) {
-    fPosY += rtItem.bottom() - m_rtConent.bottom();
+  } else if (rtItem.bottom() > m_ContentRect.bottom()) {
+    fPosY += rtItem.bottom() - m_ContentRect.bottom();
     bScroll = true;
   }
   if (!bScroll)
@@ -338,36 +307,28 @@
 
   m_pVertScrollBar->SetPos(fPosY);
   m_pVertScrollBar->SetTrackPos(fPosY);
-  RepaintRect(m_rtClient);
+  RepaintRect(m_ClientRect);
   return true;
 }
 
-void CFWL_ListBox::DrawBkground(CXFA_Graphics* pGraphics,
-                                IFWL_ThemeProvider* pTheme,
-                                const CFX_Matrix* pMatrix) {
+void CFWL_ListBox::DrawBkground(CFGAS_GEGraphics* pGraphics,
+                                const CFX_Matrix& mtMatrix) {
   if (!pGraphics)
     return;
-  if (!pTheme)
-    return;
 
-  CFWL_ThemeBackground param;
-  param.m_pWidget = this;
-  param.m_iPart = CFWL_Part::Background;
-  param.m_dwStates = 0;
-  param.m_pGraphics = pGraphics;
-  param.m_matrix.Concat(*pMatrix);
-  param.m_rtPart = m_rtClient;
-  if (IsShowScrollBar(false) && IsShowScrollBar(true))
-    param.m_pRtData = &m_rtStatic;
+  CFWL_ThemeBackground param(CFWL_ThemePart::Part::kBackground, this,
+                             pGraphics);
+  param.m_matrix = mtMatrix;
+  param.m_PartRect = m_ClientRect;
+  if (IsShowHorzScrollBar() && IsShowVertScrollBar())
+    param.m_pRtData = &m_StaticRect;
   if (!IsEnabled())
-    param.m_dwStates = CFWL_PartState_Disabled;
-
-  pTheme->DrawBackground(param);
+    param.m_dwStates = CFWL_PartState::kDisabled;
+  GetThemeProvider()->DrawBackground(param);
 }
 
-void CFWL_ListBox::DrawItems(CXFA_Graphics* pGraphics,
-                             IFWL_ThemeProvider* pTheme,
-                             const CFX_Matrix* pMatrix) {
+void CFWL_ListBox::DrawItems(CFGAS_GEGraphics* pGraphics,
+                             const CFX_Matrix& mtMatrix) {
   float fPosX = 0.0f;
   if (m_pHorzScrollBar)
     fPosX = m_pHorzScrollBar->GetPos();
@@ -376,7 +337,7 @@
   if (m_pVertScrollBar)
     fPosY = m_pVertScrollBar->GetPos();
 
-  CFX_RectF rtView(m_rtConent);
+  CFX_RectF rtView(m_ContentRect);
   if (m_pHorzScrollBar)
     rtView.height -= m_fScorllBarWidth;
   if (m_pVertScrollBar)
@@ -384,56 +345,53 @@
 
   int32_t iCount = CountItems(this);
   for (int32_t i = 0; i < iCount; i++) {
-    CFWL_ListItem* pItem = GetItem(this, i);
+    CFWL_ListBox::Item* pItem = GetItem(this, i);
     if (!pItem)
       continue;
 
     CFX_RectF rtItem = pItem->GetRect();
-    rtItem.Offset(m_rtConent.left - fPosX, m_rtConent.top - fPosY);
-    if (rtItem.bottom() < m_rtConent.top)
+    rtItem.Offset(m_ContentRect.left - fPosX, m_ContentRect.top - fPosY);
+    if (rtItem.bottom() < m_ContentRect.top)
       continue;
-    if (rtItem.top >= m_rtConent.bottom())
+    if (rtItem.top >= m_ContentRect.bottom())
       break;
-    DrawItem(pGraphics, pTheme, pItem, i, rtItem, pMatrix);
+    DrawItem(pGraphics, pItem, i, rtItem, mtMatrix);
   }
 }
 
-void CFWL_ListBox::DrawItem(CXFA_Graphics* pGraphics,
-                            IFWL_ThemeProvider* pTheme,
-                            CFWL_ListItem* pItem,
+void CFWL_ListBox::DrawItem(CFGAS_GEGraphics* pGraphics,
+                            Item* pItem,
                             int32_t Index,
                             const CFX_RectF& rtItem,
-                            const CFX_Matrix* pMatrix) {
-  uint32_t dwItemStyles = pItem ? pItem->GetStates() : 0;
-  uint32_t dwPartStates = CFWL_PartState_Normal;
-  if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
-    dwPartStates = CFWL_PartState_Disabled;
-  else if (dwItemStyles & FWL_ITEMSTATE_LTB_Selected)
-    dwPartStates = CFWL_PartState_Selected;
+                            const CFX_Matrix& mtMatrix) {
+  Mask<CFWL_PartState> dwPartStates = CFWL_PartState::kNormal;
+  if (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
+    dwPartStates = CFWL_PartState::kDisabled;
+  else if (pItem && pItem->IsSelected())
+    dwPartStates = CFWL_PartState::kSelected;
 
-  if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused &&
-      dwItemStyles & FWL_ITEMSTATE_LTB_Focused) {
-    dwPartStates |= CFWL_PartState_Focused;
+  if ((m_Properties.m_dwStates & FWL_STATE_WGT_Focused) && pItem &&
+      pItem->IsFocused()) {
+    dwPartStates |= CFWL_PartState::kFocused;
   }
 
-  CFWL_ThemeBackground bg_param;
-  bg_param.m_pWidget = this;
-  bg_param.m_iPart = CFWL_Part::ListItem;
+  CFX_RectF rtFocus(rtItem);  // Must outlive |bg_param|.
+  CFWL_ThemeBackground bg_param(CFWL_ThemePart::Part::kListItem, this,
+                                pGraphics);
   bg_param.m_dwStates = dwPartStates;
-  bg_param.m_pGraphics = pGraphics;
-  bg_param.m_matrix.Concat(*pMatrix);
-  bg_param.m_rtPart = rtItem;
+  bg_param.m_matrix = mtMatrix;
+  bg_param.m_PartRect = rtItem;
   bg_param.m_bMaximize = true;
-  CFX_RectF rtFocus(rtItem);
   bg_param.m_pRtData = &rtFocus;
   if (m_pVertScrollBar && !m_pHorzScrollBar &&
-      (dwPartStates & CFWL_PartState_Focused)) {
-    bg_param.m_rtPart.left += 1;
-    bg_param.m_rtPart.width -= (m_fScorllBarWidth + 1);
+      (dwPartStates & CFWL_PartState::kFocused)) {
+    bg_param.m_PartRect.left += 1;
+    bg_param.m_PartRect.width -= (m_fScorllBarWidth + 1);
     rtFocus.Deflate(0.5, 0.5, 1 + m_fScorllBarWidth, 1);
   }
-  pTheme->DrawBackground(bg_param);
 
+  IFWL_ThemeProvider* pTheme = GetThemeProvider();
+  pTheme->DrawBackground(bg_param);
   if (!pItem)
     return;
 
@@ -444,13 +402,10 @@
   CFX_RectF rtText(rtItem);
   rtText.Deflate(kItemTextMargin, kItemTextMargin);
 
-  CFWL_ThemeText textParam;
-  textParam.m_pWidget = this;
-  textParam.m_iPart = CFWL_Part::ListItem;
+  CFWL_ThemeText textParam(CFWL_ThemePart::Part::kListItem, this, pGraphics);
   textParam.m_dwStates = dwPartStates;
-  textParam.m_pGraphics = pGraphics;
-  textParam.m_matrix.Concat(*pMatrix);
-  textParam.m_rtPart = rtText;
+  textParam.m_matrix = mtMatrix;
+  textParam.m_PartRect = rtText;
   textParam.m_wsText = std::move(wsText);
   textParam.m_dwTTOStyles = m_TTOStyles;
   textParam.m_iTTOAlign = m_iTTOAligns;
@@ -458,125 +413,112 @@
   pTheme->DrawText(textParam);
 }
 
-CFX_SizeF CFWL_ListBox::CalcSize(bool bAutoSize) {
-  if (!m_pProperties->m_pThemeProvider)
-    return CFX_SizeF();
-
-  m_rtClient = GetClientRect();
-  m_rtConent = m_rtClient;
+CFX_SizeF CFWL_ListBox::CalcSize() {
+  m_ClientRect = GetClientRect();
+  m_ContentRect = m_ClientRect;
   CFX_RectF rtUIMargin;
-  if (!m_pOuter) {
-    CFWL_ThemePart part;
-    part.m_pWidget = this;
-    IFWL_ThemeProvider* theme = GetAvailableTheme();
-    CFX_RectF pUIMargin = theme ? theme->GetUIMargin(part) : CFX_RectF();
-    m_rtConent.Deflate(pUIMargin.left, pUIMargin.top, pUIMargin.width,
-                       pUIMargin.height);
+  if (!GetOuter()) {
+    CFWL_ThemePart part(CFWL_ThemePart::Part::kNone, this);
+    CFX_RectF pUIMargin = GetThemeProvider()->GetUIMargin(part);
+    m_ContentRect.Deflate(pUIMargin.left, pUIMargin.top, pUIMargin.width,
+                          pUIMargin.height);
   }
 
   float fWidth = GetMaxTextWidth();
   fWidth += 2 * kItemTextMargin;
-  if (!bAutoSize) {
-    float fActualWidth = m_rtClient.width - rtUIMargin.left - rtUIMargin.width;
-    fWidth = std::max(fWidth, fActualWidth);
-  }
+
+  float fActualWidth = m_ClientRect.width - rtUIMargin.left - rtUIMargin.width;
+  fWidth = std::max(fWidth, fActualWidth);
   m_fItemHeight = CalcItemHeight();
 
   int32_t iCount = CountItems(this);
   CFX_SizeF fs;
   for (int32_t i = 0; i < iCount; i++) {
-    CFWL_ListItem* htem = GetItem(this, i);
-    UpdateItemSize(htem, fs, fWidth, m_fItemHeight, bAutoSize);
+    Item* htem = GetItem(this, i);
+    UpdateItemSize(htem, fs, fWidth, m_fItemHeight);
   }
-  if (bAutoSize)
-    return fs;
 
-  float iHeight = m_rtClient.height;
+  float iHeight = m_ClientRect.height;
   bool bShowVertScr = false;
   bool bShowHorzScr = false;
-  if (!bShowVertScr && (m_pProperties->m_dwStyles & FWL_WGTSTYLE_VScroll))
+  if (!bShowVertScr && (m_Properties.m_dwStyles & FWL_STYLE_WGT_VScroll))
     bShowVertScr = (fs.height > iHeight);
 
-  CFX_SizeF szRange;
+  float fMax = 0.0f;
   if (bShowVertScr) {
     if (!m_pVertScrollBar)
       InitVerticalScrollBar();
 
-    CFX_RectF rtScrollBar(m_rtClient.right() - m_fScorllBarWidth,
-                          m_rtClient.top, m_fScorllBarWidth,
-                          m_rtClient.height - 1);
+    CFX_RectF rtScrollBar(m_ClientRect.right() - m_fScorllBarWidth,
+                          m_ClientRect.top, m_fScorllBarWidth,
+                          m_ClientRect.height - 1);
     if (bShowHorzScr)
       rtScrollBar.height -= m_fScorllBarWidth;
 
     m_pVertScrollBar->SetWidgetRect(rtScrollBar);
-    szRange.width = 0;
-    szRange.height = std::max(fs.height - m_rtConent.height, m_fItemHeight);
+    fMax = std::max(fs.height - m_ContentRect.height, m_fItemHeight);
 
-    m_pVertScrollBar->SetRange(szRange.width, szRange.height);
+    m_pVertScrollBar->SetRange(0.0f, fMax);
     m_pVertScrollBar->SetPageSize(rtScrollBar.height * 9 / 10);
     m_pVertScrollBar->SetStepSize(m_fItemHeight);
 
-    float fPos =
-        pdfium::clamp(m_pVertScrollBar->GetPos(), 0.0f, szRange.height);
+    float fPos = std::clamp(m_pVertScrollBar->GetPos(), 0.0f, fMax);
     m_pVertScrollBar->SetPos(fPos);
     m_pVertScrollBar->SetTrackPos(fPos);
-    if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_LTB_ShowScrollBarFocus) ==
+    if ((m_Properties.m_dwStyleExts & FWL_STYLEEXT_LTB_ShowScrollBarFocus) ==
             0 ||
-        (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused)) {
-      m_pVertScrollBar->RemoveStates(FWL_WGTSTATE_Invisible);
+        (m_Properties.m_dwStates & FWL_STATE_WGT_Focused)) {
+      m_pVertScrollBar->RemoveStates(FWL_STATE_WGT_Invisible);
     }
     m_pVertScrollBar->Update();
   } else if (m_pVertScrollBar) {
     m_pVertScrollBar->SetPos(0);
     m_pVertScrollBar->SetTrackPos(0);
-    m_pVertScrollBar->SetStates(FWL_WGTSTATE_Invisible);
+    m_pVertScrollBar->SetStates(FWL_STATE_WGT_Invisible);
   }
   if (bShowHorzScr) {
     if (!m_pHorzScrollBar)
       InitHorizontalScrollBar();
 
-    CFX_RectF rtScrollBar(m_rtClient.left,
-                          m_rtClient.bottom() - m_fScorllBarWidth,
-                          m_rtClient.width, m_fScorllBarWidth);
+    CFX_RectF rtScrollBar(m_ClientRect.left,
+                          m_ClientRect.bottom() - m_fScorllBarWidth,
+                          m_ClientRect.width, m_fScorllBarWidth);
     if (bShowVertScr)
       rtScrollBar.width -= m_fScorllBarWidth;
 
     m_pHorzScrollBar->SetWidgetRect(rtScrollBar);
-    szRange.width = 0;
-    szRange.height = fs.width - rtScrollBar.width;
-    m_pHorzScrollBar->SetRange(szRange.width, szRange.height);
+    fMax = fs.width - rtScrollBar.width;
+    m_pHorzScrollBar->SetRange(0.0f, fMax);
     m_pHorzScrollBar->SetPageSize(fWidth * 9 / 10);
     m_pHorzScrollBar->SetStepSize(fWidth / 10);
 
-    float fPos =
-        pdfium::clamp(m_pHorzScrollBar->GetPos(), 0.0f, szRange.height);
+    float fPos = std::clamp(m_pHorzScrollBar->GetPos(), 0.0f, fMax);
     m_pHorzScrollBar->SetPos(fPos);
     m_pHorzScrollBar->SetTrackPos(fPos);
-    if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_LTB_ShowScrollBarFocus) ==
+    if ((m_Properties.m_dwStyleExts & FWL_STYLEEXT_LTB_ShowScrollBarFocus) ==
             0 ||
-        (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused)) {
-      m_pHorzScrollBar->RemoveStates(FWL_WGTSTATE_Invisible);
+        (m_Properties.m_dwStates & FWL_STATE_WGT_Focused)) {
+      m_pHorzScrollBar->RemoveStates(FWL_STATE_WGT_Invisible);
     }
     m_pHorzScrollBar->Update();
   } else if (m_pHorzScrollBar) {
     m_pHorzScrollBar->SetPos(0);
     m_pHorzScrollBar->SetTrackPos(0);
-    m_pHorzScrollBar->SetStates(FWL_WGTSTATE_Invisible);
+    m_pHorzScrollBar->SetStates(FWL_STATE_WGT_Invisible);
   }
   if (bShowVertScr && bShowHorzScr) {
-    m_rtStatic = CFX_RectF(m_rtClient.right() - m_fScorllBarWidth,
-                           m_rtClient.bottom() - m_fScorllBarWidth,
-                           m_fScorllBarWidth, m_fScorllBarWidth);
+    m_StaticRect = CFX_RectF(m_ClientRect.right() - m_fScorllBarWidth,
+                             m_ClientRect.bottom() - m_fScorllBarWidth,
+                             m_fScorllBarWidth, m_fScorllBarWidth);
   }
   return fs;
 }
 
-void CFWL_ListBox::UpdateItemSize(CFWL_ListItem* pItem,
+void CFWL_ListBox::UpdateItemSize(Item* pItem,
                                   CFX_SizeF& size,
                                   float fWidth,
-                                  float fItemHeight,
-                                  bool bAutoSize) const {
-  if (!bAutoSize && pItem) {
+                                  float fItemHeight) const {
+  if (pItem) {
     CFX_RectF rtItem(0, size.height, fWidth, fItemHeight);
     pItem->SetRect(rtItem);
   }
@@ -588,86 +530,76 @@
   float fRet = 0.0f;
   int32_t iCount = CountItems(this);
   for (int32_t i = 0; i < iCount; i++) {
-    CFWL_ListItem* pItem = GetItem(this, i);
+    Item* pItem = GetItem(this, i);
     if (!pItem)
       continue;
 
-    CFX_SizeF sz = CalcTextSize(pItem->GetText(),
-                                m_pProperties->m_pThemeProvider.Get(), false);
+    CFX_SizeF sz = CalcTextSize(pItem->GetText(), false);
     fRet = std::max(fRet, sz.width);
   }
   return fRet;
 }
 
 float CFWL_ListBox::GetScrollWidth() {
-  IFWL_ThemeProvider* theme = GetAvailableTheme();
-  return theme ? theme->GetScrollBarWidth() : 0.0f;
+  return GetThemeProvider()->GetScrollBarWidth();
 }
 
 float CFWL_ListBox::CalcItemHeight() {
-  IFWL_ThemeProvider* theme = GetAvailableTheme();
-  CFWL_ThemePart part;
-  part.m_pWidget = this;
-  return (theme ? theme->GetFontSize(part) : 20.0f) + 2 * kItemTextMargin;
+  CFWL_ThemePart part(CFWL_ThemePart::Part::kNone, this);
+  return GetThemeProvider()->GetFontSize(part) + 2 * kItemTextMargin;
 }
 
 void CFWL_ListBox::InitVerticalScrollBar() {
   if (m_pVertScrollBar)
     return;
 
-  auto prop = pdfium::MakeUnique<CFWL_WidgetProperties>();
-  prop->m_dwStyleExes = FWL_STYLEEXT_SCB_Vert;
-  prop->m_dwStates = FWL_WGTSTATE_Invisible;
-  prop->m_pParent = this;
-  prop->m_pThemeProvider = m_pScrollBarTP;
-  m_pVertScrollBar = pdfium::MakeUnique<CFWL_ScrollBar>(m_pOwnerApp.Get(),
-                                                        std::move(prop), this);
+  m_pVertScrollBar = cppgc::MakeGarbageCollected<CFWL_ScrollBar>(
+      GetFWLApp()->GetHeap()->GetAllocationHandle(), GetFWLApp(),
+      Properties{0, FWL_STYLEEXT_SCB_Vert, FWL_STATE_WGT_Invisible}, this);
 }
 
 void CFWL_ListBox::InitHorizontalScrollBar() {
   if (m_pHorzScrollBar)
     return;
 
-  auto prop = pdfium::MakeUnique<CFWL_WidgetProperties>();
-  prop->m_dwStyleExes = FWL_STYLEEXT_SCB_Horz;
-  prop->m_dwStates = FWL_WGTSTATE_Invisible;
-  prop->m_pParent = this;
-  prop->m_pThemeProvider = m_pScrollBarTP;
-  m_pHorzScrollBar = pdfium::MakeUnique<CFWL_ScrollBar>(m_pOwnerApp.Get(),
-                                                        std::move(prop), this);
+  m_pHorzScrollBar = cppgc::MakeGarbageCollected<CFWL_ScrollBar>(
+      GetFWLApp()->GetHeap()->GetAllocationHandle(), GetFWLApp(),
+      Properties{0, FWL_STYLEEXT_SCB_Horz, FWL_STATE_WGT_Invisible}, this);
 }
 
-bool CFWL_ListBox::IsShowScrollBar(bool bVert) {
-  CFWL_ScrollBar* pScrollbar =
-      bVert ? m_pVertScrollBar.get() : m_pHorzScrollBar.get();
-  if (!pScrollbar || !pScrollbar->IsVisible())
-    return false;
+bool CFWL_ListBox::IsShowVertScrollBar() const {
+  return m_pVertScrollBar && m_pVertScrollBar->IsVisible() &&
+         ScrollBarPropertiesPresent();
+}
 
-  return !(m_pProperties->m_dwStyleExes &
-           FWL_STYLEEXT_LTB_ShowScrollBarFocus) ||
-         (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused);
+bool CFWL_ListBox::IsShowHorzScrollBar() const {
+  return m_pHorzScrollBar && m_pHorzScrollBar->IsVisible() &&
+         ScrollBarPropertiesPresent();
+}
+
+bool CFWL_ListBox::ScrollBarPropertiesPresent() const {
+  return !(m_Properties.m_dwStyleExts & FWL_STYLEEXT_LTB_ShowScrollBarFocus) ||
+         (m_Properties.m_dwStates & FWL_STATE_WGT_Focused);
 }
 
 void CFWL_ListBox::OnProcessMessage(CFWL_Message* pMessage) {
-  if (!pMessage)
-    return;
   if (!IsEnabled())
     return;
 
   switch (pMessage->GetType()) {
-    case CFWL_Message::Type::SetFocus:
-      OnFocusChanged(pMessage, true);
+    case CFWL_Message::Type::kSetFocus:
+      OnFocusGained();
       break;
-    case CFWL_Message::Type::KillFocus:
-      OnFocusChanged(pMessage, false);
+    case CFWL_Message::Type::kKillFocus:
+      OnFocusLost();
       break;
-    case CFWL_Message::Type::Mouse: {
+    case CFWL_Message::Type::kMouse: {
       CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
       switch (pMsg->m_dwCmd) {
-        case FWL_MouseCommand::LeftButtonDown:
+        case CFWL_MessageMouse::MouseCommand::kLeftButtonDown:
           OnLButtonDown(pMsg);
           break;
-        case FWL_MouseCommand::LeftButtonUp:
+        case CFWL_MessageMouse::MouseCommand::kLeftButtonUp:
           OnLButtonUp(pMsg);
           break;
         default:
@@ -675,17 +607,15 @@
       }
       break;
     }
-    case CFWL_Message::Type::MouseWheel:
+    case CFWL_Message::Type::kMouseWheel:
       OnMouseWheel(static_cast<CFWL_MessageMouseWheel*>(pMessage));
       break;
-    case CFWL_Message::Type::Key: {
+    case CFWL_Message::Type::kKey: {
       CFWL_MessageKey* pMsg = static_cast<CFWL_MessageKey*>(pMessage);
-      if (pMsg->m_dwCmd == FWL_KeyCommand::KeyDown)
+      if (pMsg->m_dwCmd == CFWL_MessageKey::KeyCommand::kKeyDown)
         OnKeyDown(pMsg);
       break;
     }
-    default:
-      break;
   }
   // Dst target could be |this|, continue only if not destroyed by above.
   if (pMessage->GetDstTarget())
@@ -699,59 +629,57 @@
     return;
 
   CFWL_Widget* pSrcTarget = pEvent->GetSrcTarget();
-  if ((pSrcTarget == m_pVertScrollBar.get() && m_pVertScrollBar) ||
-      (pSrcTarget == m_pHorzScrollBar.get() && m_pHorzScrollBar)) {
+  if ((pSrcTarget == m_pVertScrollBar && m_pVertScrollBar) ||
+      (pSrcTarget == m_pHorzScrollBar && m_pHorzScrollBar)) {
     CFWL_EventScroll* pScrollEvent = static_cast<CFWL_EventScroll*>(pEvent);
     OnScroll(static_cast<CFWL_ScrollBar*>(pSrcTarget),
-             pScrollEvent->m_iScrollCode, pScrollEvent->m_fPos);
+             pScrollEvent->GetScrollCode(), pScrollEvent->GetPos());
   }
 }
 
-void CFWL_ListBox::OnDrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_ListBox::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                                 const CFX_Matrix& matrix) {
   DrawWidget(pGraphics, matrix);
 }
 
-void CFWL_ListBox::OnFocusChanged(CFWL_Message* pMsg, bool bSet) {
-  if (GetStylesEx() & FWL_STYLEEXT_LTB_ShowScrollBarFocus) {
-    if (m_pVertScrollBar) {
-      if (bSet)
-        m_pVertScrollBar->RemoveStates(FWL_WGTSTATE_Invisible);
-      else
-        m_pVertScrollBar->SetStates(FWL_WGTSTATE_Invisible);
-    }
-    if (m_pHorzScrollBar) {
-      if (bSet)
-        m_pHorzScrollBar->RemoveStates(FWL_WGTSTATE_Invisible);
-      else
-        m_pHorzScrollBar->SetStates(FWL_WGTSTATE_Invisible);
-    }
+void CFWL_ListBox::OnFocusGained() {
+  if (GetStyleExts() & FWL_STYLEEXT_LTB_ShowScrollBarFocus) {
+    if (m_pVertScrollBar)
+      m_pVertScrollBar->RemoveStates(FWL_STATE_WGT_Invisible);
+    if (m_pHorzScrollBar)
+      m_pHorzScrollBar->RemoveStates(FWL_STATE_WGT_Invisible);
   }
-  if (bSet)
-    m_pProperties->m_dwStates |= (FWL_WGTSTATE_Focused);
-  else
-    m_pProperties->m_dwStates &= ~(FWL_WGTSTATE_Focused);
+  m_Properties.m_dwStates |= FWL_STATE_WGT_Focused;
+  RepaintRect(m_ClientRect);
+}
 
-  RepaintRect(m_rtClient);
+void CFWL_ListBox::OnFocusLost() {
+  if (GetStyleExts() & FWL_STYLEEXT_LTB_ShowScrollBarFocus) {
+    if (m_pVertScrollBar)
+      m_pVertScrollBar->SetStates(FWL_STATE_WGT_Invisible);
+    if (m_pHorzScrollBar)
+      m_pHorzScrollBar->SetStates(FWL_STATE_WGT_Invisible);
+  }
+  m_Properties.m_dwStates &= ~FWL_STATE_WGT_Focused;
+  RepaintRect(m_ClientRect);
 }
 
 void CFWL_ListBox::OnLButtonDown(CFWL_MessageMouse* pMsg) {
   m_bLButtonDown = true;
 
-  CFWL_ListItem* pItem = GetItemAtPoint(pMsg->m_pos);
+  Item* pItem = GetItemAtPoint(pMsg->m_pos);
   if (!pItem)
     return;
 
   if (IsMultiSelection()) {
-    if (pMsg->m_dwFlags & FWL_KEYFLAG_Ctrl) {
-      bool bSelected = IsItemSelected(pItem);
-      SetSelectionDirect(pItem, !bSelected);
+    if (pMsg->m_dwFlags & XFA_FWL_KeyFlag::kCtrl) {
+      pItem->SetSelected(!pItem->IsSelected());
       m_hAnchor = pItem;
-    } else if (pMsg->m_dwFlags & FWL_KEYFLAG_Shift) {
+    } else if (pMsg->m_dwFlags & XFA_FWL_KeyFlag::kShift) {
       if (m_hAnchor)
         SetSelection(m_hAnchor, pItem, true);
       else
-        SetSelectionDirect(pItem, true);
+        pItem->SetSelected(true);
     } else {
       SetSelection(pItem, pItem, true);
       m_hAnchor = pItem;
@@ -763,7 +691,7 @@
   SetFocusItem(pItem);
   ScrollToVisible(pItem);
   SetGrab(true);
-  RepaintRect(m_rtClient);
+  RepaintRect(m_ClientRect);
 }
 
 void CFWL_ListBox::OnLButtonUp(CFWL_MessageMouse* pMsg) {
@@ -775,22 +703,21 @@
 }
 
 void CFWL_ListBox::OnMouseWheel(CFWL_MessageMouseWheel* pMsg) {
-  if (IsShowScrollBar(true))
+  if (IsShowVertScrollBar())
     m_pVertScrollBar->GetDelegate()->OnProcessMessage(pMsg);
 }
 
 void CFWL_ListBox::OnKeyDown(CFWL_MessageKey* pMsg) {
-  uint32_t dwKeyCode = pMsg->m_dwKeyCode;
+  auto dwKeyCode = static_cast<XFA_FWL_VKEYCODE>(pMsg->m_dwKeyCodeOrChar);
   switch (dwKeyCode) {
     case XFA_FWL_VKEY_Tab:
     case XFA_FWL_VKEY_Up:
     case XFA_FWL_VKEY_Down:
     case XFA_FWL_VKEY_Home:
     case XFA_FWL_VKEY_End: {
-      CFWL_ListItem* pItem = GetFocusedItem();
-      pItem = GetListItem(pItem, dwKeyCode);
-      bool bShift = !!(pMsg->m_dwFlags & FWL_KEYFLAG_Shift);
-      bool bCtrl = !!(pMsg->m_dwFlags & FWL_KEYFLAG_Ctrl);
+      Item* pItem = GetListItem(GetFocusedItem(), dwKeyCode);
+      bool bShift = !!(pMsg->m_dwFlags & XFA_FWL_KeyFlag::kShift);
+      bool bCtrl = !!(pMsg->m_dwFlags & XFA_FWL_KeyFlag::kCtrl);
       OnVK(pItem, bShift, bCtrl);
       break;
     }
@@ -799,7 +726,7 @@
   }
 }
 
-void CFWL_ListBox::OnVK(CFWL_ListItem* pItem, bool bShift, bool bCtrl) {
+void CFWL_ListBox::OnVK(Item* pItem, bool bShift, bool bCtrl) {
   if (!pItem)
     return;
 
@@ -810,7 +737,7 @@
       if (m_hAnchor)
         SetSelection(m_hAnchor, pItem, true);
       else
-        SetSelectionDirect(pItem, true);
+        pItem->SetSelected(true);
     } else {
       SetSelection(pItem, pItem, true);
       m_hAnchor = pItem;
@@ -821,49 +748,48 @@
 
   SetFocusItem(pItem);
   ScrollToVisible(pItem);
-
-  RepaintRect(CFX_RectF(0, 0, m_pProperties->m_rtWidget.width,
-                        m_pProperties->m_rtWidget.height));
+  RepaintRect(CFX_RectF(0, 0, m_WidgetRect.width, m_WidgetRect.height));
 }
 
 bool CFWL_ListBox::OnScroll(CFWL_ScrollBar* pScrollBar,
                             CFWL_EventScroll::Code dwCode,
                             float fPos) {
-  CFX_SizeF fs;
-  pScrollBar->GetRange(&fs.width, &fs.height);
+  float fMin;
+  float fMax;
+  pScrollBar->GetRange(&fMin, &fMax);
   float iCurPos = pScrollBar->GetPos();
   float fStep = pScrollBar->GetStepSize();
   switch (dwCode) {
     case CFWL_EventScroll::Code::Min: {
-      fPos = fs.width;
+      fPos = fMin;
       break;
     }
     case CFWL_EventScroll::Code::Max: {
-      fPos = fs.height;
+      fPos = fMax;
       break;
     }
     case CFWL_EventScroll::Code::StepBackward: {
       fPos -= fStep;
-      if (fPos < fs.width + fStep / 2)
-        fPos = fs.width;
+      if (fPos < fMin + fStep / 2)
+        fPos = fMin;
       break;
     }
     case CFWL_EventScroll::Code::StepForward: {
       fPos += fStep;
-      if (fPos > fs.height - fStep / 2)
-        fPos = fs.height;
+      if (fPos > fMax - fStep / 2)
+        fPos = fMax;
       break;
     }
     case CFWL_EventScroll::Code::PageBackward: {
       fPos -= pScrollBar->GetPageSize();
-      if (fPos < fs.width)
-        fPos = fs.width;
+      if (fPos < fMin)
+        fPos = fMin;
       break;
     }
     case CFWL_EventScroll::Code::PageForward: {
       fPos += pScrollBar->GetPageSize();
-      if (fPos > fs.height)
-        fPos = fs.height;
+      if (fPos > fMax)
+        fPos = fMax;
       break;
     }
     case CFWL_EventScroll::Code::Pos:
@@ -876,33 +802,34 @@
   if (iCurPos != fPos) {
     pScrollBar->SetPos(fPos);
     pScrollBar->SetTrackPos(fPos);
-    RepaintRect(m_rtClient);
+    RepaintRect(m_ClientRect);
   }
   return true;
 }
 
 int32_t CFWL_ListBox::CountItems(const CFWL_Widget* pWidget) const {
-  return pdfium::CollectionSize<int32_t>(m_ItemArray);
+  return fxcrt::CollectionSize<int32_t>(m_ItemArray);
 }
 
-CFWL_ListItem* CFWL_ListBox::GetItem(const CFWL_Widget* pWidget,
-                                     int32_t nIndex) const {
+CFWL_ListBox::Item* CFWL_ListBox::GetItem(const CFWL_Widget* pWidget,
+                                          int32_t nIndex) const {
   if (nIndex < 0 || nIndex >= CountItems(pWidget))
     return nullptr;
   return m_ItemArray[nIndex].get();
 }
 
-int32_t CFWL_ListBox::GetItemIndex(CFWL_Widget* pWidget, CFWL_ListItem* pItem) {
-  auto it =
-      std::find_if(m_ItemArray.begin(), m_ItemArray.end(),
-                   [pItem](const std::unique_ptr<CFWL_ListItem>& candidate) {
-                     return candidate.get() == pItem;
-                   });
-  return it != m_ItemArray.end() ? it - m_ItemArray.begin() : -1;
+int32_t CFWL_ListBox::GetItemIndex(CFWL_Widget* pWidget, Item* pItem) {
+  auto it = std::find_if(m_ItemArray.begin(), m_ItemArray.end(),
+                         [pItem](const std::unique_ptr<Item>& candidate) {
+                           return candidate.get() == pItem;
+                         });
+  return it != m_ItemArray.end()
+             ? pdfium::base::checked_cast<int32_t>(it - m_ItemArray.begin())
+             : -1;
 }
 
-CFWL_ListItem* CFWL_ListBox::AddString(const WideString& wsAdd) {
-  m_ItemArray.emplace_back(pdfium::MakeUnique<CFWL_ListItem>(wsAdd));
+CFWL_ListBox::Item* CFWL_ListBox::AddString(const WideString& wsAdd) {
+  m_ItemArray.push_back(std::make_unique<Item>(wsAdd));
   return m_ItemArray.back().get();
 }
 
@@ -912,7 +839,7 @@
   m_ItemArray.erase(m_ItemArray.begin() + iIndex);
 }
 
-void CFWL_ListBox::DeleteString(CFWL_ListItem* pItem) {
+void CFWL_ListBox::DeleteString(Item* pItem) {
   int32_t nIndex = GetItemIndex(this, pItem);
   if (nIndex < 0 || static_cast<size_t>(nIndex) >= m_ItemArray.size())
     return;
@@ -921,13 +848,17 @@
   if (iSel >= CountItems(this))
     iSel = nIndex - 1;
   if (iSel >= 0) {
-    if (CFWL_ListItem* item = GetItem(this, iSel))
-      item->SetStates(item->GetStates() | FWL_ITEMSTATE_LTB_Selected);
+    Item* item = GetItem(this, iSel);
+    if (item)
+      item->SetSelected(true);
   }
-
   m_ItemArray.erase(m_ItemArray.begin() + nIndex);
 }
 
 void CFWL_ListBox::DeleteAll() {
   m_ItemArray.clear();
 }
+
+CFWL_ListBox::Item::Item(const WideString& text) : m_wsText(text) {}
+
+CFWL_ListBox::Item::~Item() = default;
diff --git a/xfa/fwl/cfwl_listbox.h b/xfa/fwl/cfwl_listbox.h
index 178e49a..2236842 100644
--- a/xfa/fwl/cfwl_listbox.h
+++ b/xfa/fwl/cfwl_listbox.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,12 +10,12 @@
 #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"
-#include "xfa/fwl/cfwl_listitem.h"
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fwl/cfwl_widgetproperties.h"
+#include "xfa/fwl/fwl_widgetdef.h"
 
 #define FWL_STYLEEXT_LTB_MultiSelection (1L << 0)
 #define FWL_STYLEEXT_LTB_LeftAlign (0L << 4)
@@ -23,113 +23,122 @@
 #define FWL_STYLEEXT_LTB_RightAlign (2L << 4)
 #define FWL_STYLEEXT_LTB_AlignMask (3L << 4)
 #define FWL_STYLEEXT_LTB_ShowScrollBarFocus (1L << 10)
-#define FWL_ITEMSTATE_LTB_Selected (1L << 0)
-#define FWL_ITEMSTATE_LTB_Focused (1L << 1)
 
-class CFWL_MessageKillFocus;
 class CFWL_MessageMouse;
 class CFWL_MessageMouseWheel;
-class CFX_DIBitmap;
 
 class CFWL_ListBox : public CFWL_Widget {
  public:
-  explicit CFWL_ListBox(const CFWL_App* pApp,
-                        std::unique_ptr<CFWL_WidgetProperties> properties,
-                        CFWL_Widget* pOuter);
+  class Item {
+   public:
+    explicit Item(const WideString& text);
+    ~Item();
+
+    bool IsSelected() const { return m_bIsSelected; }
+    void SetSelected(bool enable) { m_bIsSelected = enable; }
+    bool IsFocused() const { return m_bIsFocused; }
+    void SetFocused(bool enable) { m_bIsFocused = enable; }
+    CFX_RectF GetRect() const { return m_ItemRect; }
+    void SetRect(const CFX_RectF& rect) { m_ItemRect = rect; }
+    WideString GetText() const { return m_wsText; }
+
+   private:
+    bool m_bIsSelected = false;
+    bool m_bIsFocused = false;
+    CFX_RectF m_ItemRect;
+    const WideString m_wsText;
+  };
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_ListBox() override;
 
-  // CFWL_Widget
+  // CFWL_Widget:
+  void Trace(cppgc::Visitor* visitor) const override;
   FWL_Type GetClassID() const override;
   void Update() override;
   FWL_WidgetHit HitTest(const CFX_PointF& point) override;
-  void DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) override;
-  void SetThemeProvider(IFWL_ThemeProvider* pThemeProvider) override;
+  void DrawWidget(CFGAS_GEGraphics* pGraphics,
+                  const CFX_Matrix& matrix) override;
   void OnProcessMessage(CFWL_Message* pMessage) override;
   void OnProcessEvent(CFWL_Event* pEvent) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
+  void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix) override;
 
   int32_t CountItems(const CFWL_Widget* pWidget) const;
-  CFWL_ListItem* GetItem(const CFWL_Widget* pWidget, int32_t nIndex) const;
-  int32_t GetItemIndex(CFWL_Widget* pWidget, CFWL_ListItem* pItem);
-
-  CFWL_ListItem* AddString(const WideString& wsAdd);
+  Item* GetItem(const CFWL_Widget* pWidget, int32_t nIndex) const;
+  int32_t GetItemIndex(CFWL_Widget* pWidget, Item* pItem);
+  Item* AddString(const WideString& wsAdd);
   void RemoveAt(int32_t iIndex);
-  void DeleteString(CFWL_ListItem* pItem);
+  void DeleteString(Item* pItem);
   void DeleteAll();
-
   int32_t CountSelItems();
-  CFWL_ListItem* GetSelItem(int32_t nIndexSel);
+  Item* GetSelItem(int32_t nIndexSel);
   int32_t GetSelIndex(int32_t nIndex);
-  void SetSelItem(CFWL_ListItem* hItem, bool bSelect);
-
-  float GetItemHeight() const { return m_fItemHeight; }
+  void SetSelItem(Item* hItem, bool bSelect);
   float CalcItemHeight();
 
  protected:
-  CFWL_ListItem* GetListItem(CFWL_ListItem* hItem, uint32_t dwKeyCode);
-  void SetSelection(CFWL_ListItem* hStart, CFWL_ListItem* hEnd, bool bSelected);
-  CFWL_ListItem* GetItemAtPoint(const CFX_PointF& point);
-  bool ScrollToVisible(CFWL_ListItem* hItem);
+  CFWL_ListBox(CFWL_App* pApp,
+               const Properties& properties,
+               CFWL_Widget* pOuter);
+
+  Item* GetListItem(Item* hItem, XFA_FWL_VKEYCODE dwKeyCode);
+  void SetSelection(Item* hStart, Item* hEnd, bool bSelected);
+  Item* GetItemAtPoint(const CFX_PointF& point);
+  bool ScrollToVisible(Item* hItem);
   void InitVerticalScrollBar();
   void InitHorizontalScrollBar();
-  bool IsShowScrollBar(bool bVert);
-  CFWL_ScrollBar* GetVertScrollBar() const { return m_pVertScrollBar.get(); }
-  const CFX_RectF& GetRTClient() const { return m_rtClient; }
+  bool IsShowVertScrollBar() const;
+  bool IsShowHorzScrollBar() const;
+  bool ScrollBarPropertiesPresent() const;
+  CFWL_ScrollBar* GetVertScrollBar() const { return m_pVertScrollBar; }
+  const CFX_RectF& GetRTClient() const { return m_ClientRect; }
 
  private:
-  void SetSelectionDirect(CFWL_ListItem* hItem, bool bSelect);
   bool IsMultiSelection() const;
-  bool IsItemSelected(CFWL_ListItem* hItem);
   void ClearSelection();
   void SelectAll();
-  CFWL_ListItem* GetFocusedItem();
-  void SetFocusItem(CFWL_ListItem* hItem);
-  void DrawBkground(CXFA_Graphics* pGraphics,
-                    IFWL_ThemeProvider* pTheme,
-                    const CFX_Matrix* pMatrix);
-  void DrawItems(CXFA_Graphics* pGraphics,
-                 IFWL_ThemeProvider* pTheme,
-                 const CFX_Matrix* pMatrix);
-  void DrawItem(CXFA_Graphics* pGraphics,
-                IFWL_ThemeProvider* pTheme,
-                CFWL_ListItem* hItem,
+  Item* GetFocusedItem();
+  void SetFocusItem(Item* hItem);
+  void DrawBkground(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawItems(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawItem(CFGAS_GEGraphics* pGraphics,
+                Item* hItem,
                 int32_t Index,
                 const CFX_RectF& rtItem,
-                const CFX_Matrix* pMatrix);
-  void DrawStatic(CXFA_Graphics* pGraphics, IFWL_ThemeProvider* pTheme);
-  CFX_SizeF CalcSize(bool bAutoSize);
-  void UpdateItemSize(CFWL_ListItem* hItem,
+                const CFX_Matrix& pMatrix);
+  void DrawStatic(CFGAS_GEGraphics* pGraphics);
+  CFX_SizeF CalcSize();
+  void UpdateItemSize(Item* hItem,
                       CFX_SizeF& size,
                       float fWidth,
-                      float fHeight,
-                      bool bAutoSize) const;
+                      float fHeight) const;
   float GetMaxTextWidth();
   float GetScrollWidth();
 
-  void OnFocusChanged(CFWL_Message* pMsg, bool bSet);
+  void OnFocusGained();
+  void OnFocusLost();
   void OnLButtonDown(CFWL_MessageMouse* pMsg);
   void OnLButtonUp(CFWL_MessageMouse* pMsg);
   void OnMouseWheel(CFWL_MessageMouseWheel* pMsg);
   void OnKeyDown(CFWL_MessageKey* pMsg);
-  void OnVK(CFWL_ListItem* hItem, bool bShift, bool bCtrl);
+  void OnVK(Item* hItem, bool bShift, bool bCtrl);
   bool OnScroll(CFWL_ScrollBar* pScrollBar,
                 CFWL_EventScroll::Code dwCode,
                 float fPos);
 
-  CFX_RectF m_rtClient;
-  CFX_RectF m_rtStatic;
-  CFX_RectF m_rtConent;
-  std::unique_ptr<CFWL_ScrollBar> m_pHorzScrollBar;
-  std::unique_ptr<CFWL_ScrollBar> m_pVertScrollBar;
+  CFX_RectF m_ClientRect;
+  CFX_RectF m_StaticRect;
+  CFX_RectF m_ContentRect;
+  cppgc::Member<CFWL_ScrollBar> m_pHorzScrollBar;
+  cppgc::Member<CFWL_ScrollBar> m_pVertScrollBar;
   FDE_TextStyle m_TTOStyles;
   FDE_TextAlignment m_iTTOAligns = FDE_TextAlignment::kTopLeft;
   bool m_bLButtonDown = false;
   float m_fItemHeight = 0.0f;
   float m_fScorllBarWidth = 0.0f;
-  CFWL_ListItem* m_hAnchor = nullptr;
-  IFWL_ThemeProvider* m_pScrollBarTP = nullptr;
-  std::vector<std::unique_ptr<CFWL_ListItem>> 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_listitem.cpp b/xfa/fwl/cfwl_listitem.cpp
deleted file mode 100644
index edf1be3..0000000
--- a/xfa/fwl/cfwl_listitem.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fwl/cfwl_listitem.h"
-
-CFWL_ListItem::CFWL_ListItem(const WideString& text) : m_wsText(text) {}
-
-CFWL_ListItem::~CFWL_ListItem() {}
diff --git a/xfa/fwl/cfwl_listitem.h b/xfa/fwl/cfwl_listitem.h
deleted file mode 100644
index a6afb22..0000000
--- a/xfa/fwl/cfwl_listitem.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FWL_CFWL_LISTITEM_H_
-#define XFA_FWL_CFWL_LISTITEM_H_
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-
-class CFWL_ListItem {
- public:
-  explicit CFWL_ListItem(const WideString& text);
-  ~CFWL_ListItem();
-
-  CFX_RectF GetRect() const { return m_rtItem; }
-  void SetRect(const CFX_RectF& rect) { m_rtItem = rect; }
-
-  uint32_t GetStates() const { return m_dwStates; }
-  void SetStates(uint32_t dwStates) { m_dwStates = dwStates; }
-
-  WideString GetText() const { return m_wsText; }
-
- private:
-  uint32_t m_dwStates = 0;
-  CFX_RectF m_rtItem;
-  WideString m_wsText;
-};
-
-#endif  // XFA_FWL_CFWL_LISTITEM_H_
diff --git a/xfa/fwl/cfwl_message.cpp b/xfa/fwl/cfwl_message.cpp
index f330a08..d388f7e 100644
--- a/xfa/fwl/cfwl_message.cpp
+++ b/xfa/fwl/cfwl_message.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,7 @@
 
 #include "xfa/fwl/cfwl_message.h"
 
-CFWL_Message::CFWL_Message(Type type,
-                           CFWL_Widget* pSrcTarget,
-                           CFWL_Widget* pDstTarget)
-    : m_type(type), m_pSrcTarget(pSrcTarget), m_pDstTarget(pDstTarget) {}
+CFWL_Message::CFWL_Message(Type type, CFWL_Widget* pDstTarget)
+    : m_type(type), m_pDstTarget(pDstTarget) {}
 
 CFWL_Message::~CFWL_Message() = default;
diff --git a/xfa/fwl/cfwl_message.h b/xfa/fwl/cfwl_message.h
index 69f7bf5..3a8d53d 100644
--- a/xfa/fwl/cfwl_message.h
+++ b/xfa/fwl/cfwl_message.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,34 +7,32 @@
 #ifndef XFA_FWL_CFWL_MESSAGE_H_
 #define XFA_FWL_CFWL_MESSAGE_H_
 
-#include <memory>
+#include "core/fxcrt/mask.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "v8/include/cppgc/macros.h"
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/observed_ptr.h"
-#include "xfa/fwl/cfwl_widget.h"
+class CFWL_Widget;
 
 class CFWL_Message {
+  CPPGC_STACK_ALLOCATED();  // Allow Raw/Unowned pointers.
+
  public:
-  enum class Type { Key, KillFocus, Mouse, MouseWheel, SetFocus };
+  enum class Type { kKey, kKillFocus, kMouse, kMouseWheel, kSetFocus };
 
   virtual ~CFWL_Message();
 
   Type GetType() const { return m_type; }
-  CFWL_Widget* GetSrcTarget() const { return m_pSrcTarget.Get(); }
-  CFWL_Widget* GetDstTarget() const { return m_pDstTarget.Get(); }
-  void SetSrcTarget(CFWL_Widget* pWidget) { m_pSrcTarget.Reset(pWidget); }
-  void SetDstTarget(CFWL_Widget* pWidget) { m_pDstTarget.Reset(pWidget); }
+  CFWL_Widget* GetDstTarget() const { return m_pDstTarget; }
+  void SetDstTarget(CFWL_Widget* pWidget) { m_pDstTarget = pWidget; }
 
  protected:
-  CFWL_Message(Type type, CFWL_Widget* pSrcTarget, CFWL_Widget* pDstTarget);
+  CFWL_Message(Type type, CFWL_Widget* pDstTarget);
   CFWL_Message(const CFWL_Message& that) = delete;
   CFWL_Message& operator=(const CFWL_Message& that) = delete;
 
  private:
   const Type m_type;
-  ObservedPtr<CFWL_Widget> m_pSrcTarget;
-  ObservedPtr<CFWL_Widget> m_pDstTarget;
+  UnownedPtr<CFWL_Widget> m_pDstTarget;
 };
 
 #endif  // XFA_FWL_CFWL_MESSAGE_H_
diff --git a/xfa/fwl/cfwl_messagekey.cpp b/xfa/fwl/cfwl_messagekey.cpp
index 9abb0c1..2e9269d 100644
--- a/xfa/fwl/cfwl_messagekey.cpp
+++ b/xfa/fwl/cfwl_messagekey.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,17 +6,13 @@
 
 #include "xfa/fwl/cfwl_messagekey.h"
 
-#include <memory>
-
-#include "third_party/base/ptr_util.h"
-
 CFWL_MessageKey::CFWL_MessageKey(CFWL_Widget* pDstTarget,
-                                 FWL_KeyCommand cmd,
-                                 uint32_t flags,
-                                 uint32_t keycode)
-    : CFWL_Message(CFWL_Message::Type::Key, nullptr, pDstTarget),
+                                 KeyCommand cmd,
+                                 Mask<XFA_FWL_KeyFlag> flags,
+                                 uint32_t dwKeyCodeOrChar)
+    : CFWL_Message(CFWL_Message::Type::kKey, pDstTarget),
       m_dwCmd(cmd),
       m_dwFlags(flags),
-      m_dwKeyCode(keycode) {}
+      m_dwKeyCodeOrChar(dwKeyCodeOrChar) {}
 
 CFWL_MessageKey::~CFWL_MessageKey() = default;
diff --git a/xfa/fwl/cfwl_messagekey.h b/xfa/fwl/cfwl_messagekey.h
index b122647..96f4610 100644
--- a/xfa/fwl/cfwl_messagekey.h
+++ b/xfa/fwl/cfwl_messagekey.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,23 +7,23 @@
 #ifndef XFA_FWL_CFWL_MESSAGEKEY_H_
 #define XFA_FWL_CFWL_MESSAGEKEY_H_
 
-#include <memory>
-
+#include "core/fxcrt/mask.h"
 #include "xfa/fwl/cfwl_message.h"
-
-enum class FWL_KeyCommand { KeyDown, KeyUp, Char };
+#include "xfa/fwl/fwl_widgetdef.h"
 
 class CFWL_MessageKey final : public CFWL_Message {
  public:
+  enum class KeyCommand : uint8_t { kKeyDown, kChar };
+
   CFWL_MessageKey(CFWL_Widget* pDstTarget,
-                  FWL_KeyCommand cmd,
-                  uint32_t flags,
-                  uint32_t keycode);
+                  KeyCommand subtype,
+                  Mask<XFA_FWL_KeyFlag> flags,
+                  uint32_t dwKeyCodeOrChar);
   ~CFWL_MessageKey() override;
 
-  const FWL_KeyCommand m_dwCmd;
-  const uint32_t m_dwFlags;
-  const uint32_t m_dwKeyCode;
+  const KeyCommand m_dwCmd;
+  const Mask<XFA_FWL_KeyFlag> m_dwFlags;
+  const uint32_t m_dwKeyCodeOrChar;
 };
 
 #endif  // XFA_FWL_CFWL_MESSAGEKEY_H_
diff --git a/xfa/fwl/cfwl_messagekillfocus.cpp b/xfa/fwl/cfwl_messagekillfocus.cpp
index 15fe562..a6c0649 100644
--- a/xfa/fwl/cfwl_messagekillfocus.cpp
+++ b/xfa/fwl/cfwl_messagekillfocus.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,15 +6,7 @@
 
 #include "xfa/fwl/cfwl_messagekillfocus.h"
 
-#include <memory>
-
-#include "third_party/base/ptr_util.h"
-
-CFWL_MessageKillFocus::CFWL_MessageKillFocus(CFWL_Widget* pSrcTarget)
-    : CFWL_MessageKillFocus(pSrcTarget, nullptr) {}
-
-CFWL_MessageKillFocus::CFWL_MessageKillFocus(CFWL_Widget* pSrcTarget,
-                                             CFWL_Widget* pDstTarget)
-    : CFWL_Message(CFWL_Message::Type::KillFocus, pSrcTarget, pDstTarget) {}
+CFWL_MessageKillFocus::CFWL_MessageKillFocus(CFWL_Widget* pDstTarget)
+    : CFWL_Message(CFWL_Message::Type::kKillFocus, pDstTarget) {}
 
 CFWL_MessageKillFocus::~CFWL_MessageKillFocus() = default;
diff --git a/xfa/fwl/cfwl_messagekillfocus.h b/xfa/fwl/cfwl_messagekillfocus.h
index 18e64f9..996fc10 100644
--- a/xfa/fwl/cfwl_messagekillfocus.h
+++ b/xfa/fwl/cfwl_messagekillfocus.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,12 @@
 #ifndef XFA_FWL_CFWL_MESSAGEKILLFOCUS_H_
 #define XFA_FWL_CFWL_MESSAGEKILLFOCUS_H_
 
-#include <memory>
-
 #include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fwl/cfwl_message.h"
 
 class CFWL_MessageKillFocus final : public CFWL_Message {
  public:
-  explicit CFWL_MessageKillFocus(CFWL_Widget* pSrcTarget);
-  CFWL_MessageKillFocus(CFWL_Widget* pSrcTarget, CFWL_Widget* pDstTarget);
+  explicit CFWL_MessageKillFocus(CFWL_Widget* pDstTarget);
   ~CFWL_MessageKillFocus() override;
 
   bool IsFocusedOnWidget(const CFWL_Widget* pWidget) const {
@@ -23,7 +20,7 @@
   }
 
  private:
-  UnownedPtr<CFWL_Widget> m_pSetFocus;
+  UnownedPtr<CFWL_Widget> m_pSetFocus;  // Ok, stack-only.
 };
 
 #endif  // XFA_FWL_CFWL_MESSAGEKILLFOCUS_H_
diff --git a/xfa/fwl/cfwl_messagemouse.cpp b/xfa/fwl/cfwl_messagemouse.cpp
index 395c9e6..50aa801 100644
--- a/xfa/fwl/cfwl_messagemouse.cpp
+++ b/xfa/fwl/cfwl_messagemouse.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,20 +6,11 @@
 
 #include "xfa/fwl/cfwl_messagemouse.h"
 
-#include <memory>
-
-#include "third_party/base/ptr_util.h"
-
 CFWL_MessageMouse::CFWL_MessageMouse(CFWL_Widget* pDstTarget,
-                                     FWL_MouseCommand cmd)
-    : CFWL_Message(CFWL_Message::Type::Mouse, nullptr, pDstTarget),
-      m_dwCmd(cmd) {}
-
-CFWL_MessageMouse::CFWL_MessageMouse(CFWL_Widget* pDstTarget,
-                                     FWL_MouseCommand cmd,
-                                     uint32_t flags,
+                                     MouseCommand cmd,
+                                     Mask<XFA_FWL_KeyFlag> flags,
                                      CFX_PointF pos)
-    : CFWL_Message(CFWL_Message::Type::Mouse, nullptr, pDstTarget),
+    : CFWL_Message(CFWL_Message::Type::kMouse, pDstTarget),
       m_dwCmd(cmd),
       m_dwFlags(flags),
       m_pos(pos) {}
diff --git a/xfa/fwl/cfwl_messagemouse.h b/xfa/fwl/cfwl_messagemouse.h
index 10c298a..fda7668 100644
--- a/xfa/fwl/cfwl_messagemouse.h
+++ b/xfa/fwl/cfwl_messagemouse.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,35 +7,34 @@
 #ifndef XFA_FWL_CFWL_MESSAGEMOUSE_H_
 #define XFA_FWL_CFWL_MESSAGEMOUSE_H_
 
-#include <memory>
-
 #include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/mask.h"
 #include "xfa/fwl/cfwl_message.h"
-
-enum class FWL_MouseCommand {
-  LeftButtonDown,
-  LeftButtonUp,
-  LeftButtonDblClk,
-  RightButtonDown,
-  RightButtonUp,
-  RightButtonDblClk,
-  Move,
-  Enter,
-  Leave,
-  Hover
-};
+#include "xfa/fwl/fwl_widgetdef.h"
 
 class CFWL_MessageMouse final : public CFWL_Message {
  public:
-  CFWL_MessageMouse(CFWL_Widget* pDstTarget, FWL_MouseCommand cmd);
+  enum class MouseCommand : uint8_t {
+    kLeftButtonDown,
+    kLeftButtonUp,
+    kLeftButtonDblClk,
+    kRightButtonDown,
+    kRightButtonUp,
+    kRightButtonDblClk,
+    kMove,
+    kEnter,
+    kLeave,
+    kHover
+  };
+
   CFWL_MessageMouse(CFWL_Widget* pDstTarget,
-                    FWL_MouseCommand cmd,
-                    uint32_t flags,
+                    MouseCommand cmd,
+                    Mask<XFA_FWL_KeyFlag> flags,
                     CFX_PointF pos);
   ~CFWL_MessageMouse() override;
 
-  const FWL_MouseCommand m_dwCmd;
-  uint32_t m_dwFlags = 0;
+  const MouseCommand m_dwCmd;
+  Mask<XFA_FWL_KeyFlag> m_dwFlags;
   CFX_PointF m_pos;
 };
 
diff --git a/xfa/fwl/cfwl_messagemousewheel.cpp b/xfa/fwl/cfwl_messagemousewheel.cpp
index 3331179..8eb51cb 100644
--- a/xfa/fwl/cfwl_messagemousewheel.cpp
+++ b/xfa/fwl/cfwl_messagemousewheel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,17 +6,11 @@
 
 #include "xfa/fwl/cfwl_messagemousewheel.h"
 
-#include <memory>
-
-#include "third_party/base/ptr_util.h"
-
-CFWL_MessageMouseWheel::CFWL_MessageMouseWheel(CFWL_Widget* pDstTarget,
-                                               uint32_t flags,
-                                               CFX_PointF pos,
-                                               CFX_PointF delta)
-    : CFWL_Message(CFWL_Message::Type::MouseWheel, nullptr, pDstTarget),
-      m_dwFlags(flags),
-      m_pos(pos),
-      m_delta(delta) {}
+CFWL_MessageMouseWheel::CFWL_MessageMouseWheel(CFWL_Widget* destination,
+                                               const CFX_PointF& pos,
+                                               const CFX_Vector& delta)
+    : CFWL_Message(CFWL_Message::Type::kMouseWheel, destination),
+      pos_(pos),
+      delta_(delta) {}
 
 CFWL_MessageMouseWheel::~CFWL_MessageMouseWheel() = default;
diff --git a/xfa/fwl/cfwl_messagemousewheel.h b/xfa/fwl/cfwl_messagemousewheel.h
index 9908951..e0ea1c6 100644
--- a/xfa/fwl/cfwl_messagemousewheel.h
+++ b/xfa/fwl/cfwl_messagemousewheel.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,22 +7,24 @@
 #ifndef XFA_FWL_CFWL_MESSAGEMOUSEWHEEL_H_
 #define XFA_FWL_CFWL_MESSAGEMOUSEWHEEL_H_
 
-#include <memory>
-
 #include "core/fxcrt/fx_coordinates.h"
 #include "xfa/fwl/cfwl_message.h"
 
 class CFWL_MessageMouseWheel final : public CFWL_Message {
  public:
-  CFWL_MessageMouseWheel(CFWL_Widget* pDstTarget,
-                         uint32_t flags,
-                         CFX_PointF pos,
-                         CFX_PointF delta);
+  CFWL_MessageMouseWheel(CFWL_Widget* destination,
+                         const CFX_PointF& pos,
+                         const CFX_Vector& delta);
   ~CFWL_MessageMouseWheel() override;
 
-  const uint32_t m_dwFlags;
-  CFX_PointF m_pos;
-  CFX_PointF m_delta;
+  void set_pos(const CFX_PointF& pos) { pos_ = pos; }
+  const CFX_PointF& pos() const { return pos_; }
+
+  const CFX_Vector& delta() const { return delta_; }
+
+ private:
+  CFX_PointF pos_;
+  const CFX_Vector delta_;
 };
 
 #endif  // XFA_FWL_CFWL_MESSAGEMOUSEWHEEL_H_
diff --git a/xfa/fwl/cfwl_messagesetfocus.cpp b/xfa/fwl/cfwl_messagesetfocus.cpp
index ec0c27a..01b74b2 100644
--- a/xfa/fwl/cfwl_messagesetfocus.cpp
+++ b/xfa/fwl/cfwl_messagesetfocus.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,13 +6,7 @@
 
 #include "xfa/fwl/cfwl_messagesetfocus.h"
 
-#include <memory>
-
-#include "third_party/base/ptr_util.h"
-
-CFWL_MessageSetFocus::CFWL_MessageSetFocus(CFWL_Widget* pSrcTarget,
-                                           CFWL_Widget* pDstTarget)
-    : CFWL_Message(CFWL_Message::Type::SetFocus, pSrcTarget, pDstTarget) {}
+CFWL_MessageSetFocus::CFWL_MessageSetFocus(CFWL_Widget* pDstTarget)
+    : CFWL_Message(CFWL_Message::Type::kSetFocus, pDstTarget) {}
 
 CFWL_MessageSetFocus::~CFWL_MessageSetFocus() = default;
-
diff --git a/xfa/fwl/cfwl_messagesetfocus.h b/xfa/fwl/cfwl_messagesetfocus.h
index 67e0035..2a4f76d 100644
--- a/xfa/fwl/cfwl_messagesetfocus.h
+++ b/xfa/fwl/cfwl_messagesetfocus.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,11 @@
 #ifndef XFA_FWL_CFWL_MESSAGESETFOCUS_H_
 #define XFA_FWL_CFWL_MESSAGESETFOCUS_H_
 
-#include <memory>
-
 #include "xfa/fwl/cfwl_message.h"
 
 class CFWL_MessageSetFocus final : public CFWL_Message {
  public:
-  CFWL_MessageSetFocus(CFWL_Widget* pSrcTarget, CFWL_Widget* pDstTarget);
+  explicit CFWL_MessageSetFocus(CFWL_Widget* pDstTarget);
   ~CFWL_MessageSetFocus() override;
 };
 
diff --git a/xfa/fwl/cfwl_monthcalendar.cpp b/xfa/fwl/cfwl_monthcalendar.cpp
index 76d9156..5867ec9 100644
--- a/xfa/fwl/cfwl_monthcalendar.cpp
+++ b/xfa/fwl/cfwl_monthcalendar.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,8 +10,11 @@
 #include <memory>
 #include <utility>
 
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/cfx_datetime.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/notreached.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fwl/cfwl_datetimepicker.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
@@ -20,16 +23,16 @@
 #include "xfa/fwl/cfwl_themetext.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
 
-#define MONTHCAL_HSEP_HEIGHT 1
-#define MONTHCAL_HMARGIN 3
-#define MONTHCAL_VMARGIN 2
-#define MONTHCAL_ROWS 9
-#define MONTHCAL_COLUMNS 7
-#define MONTHCAL_HEADER_BTN_VMARGIN 7
-#define MONTHCAL_HEADER_BTN_HMARGIN 5
-
 namespace {
 
+constexpr float kMonthCalHSepHeight = 1.0f;
+constexpr float kMonthCalHMargin = 3.0f;
+constexpr float kMonthCalVMargin = 2.0f;
+constexpr float kMonthCalRows = 9.0f;
+constexpr float kMonthCalColumns = 7.0f;
+constexpr float kMonthCalHeaderBtnVMargin = 7.0f;
+constexpr float kMonthCalHeaderBtnHMargin = 5.0f;
+
 WideString GetAbbreviatedDayOfWeek(int day) {
   switch (day) {
     case 0:
@@ -47,8 +50,7 @@
     case 6:
       return L"Sat";
     default:
-      NOTREACHED();
-      return L"";
+      NOTREACHED_NORETURN();
   }
 }
 
@@ -79,18 +81,16 @@
     case 11:
       return L"December";
     default:
-      NOTREACHED();
-      return L"";
+      NOTREACHED_NORETURN();
   }
 }
 
 }  // namespace
 
-CFWL_MonthCalendar::CFWL_MonthCalendar(
-    const CFWL_App* app,
-    std::unique_ptr<CFWL_WidgetProperties> properties,
-    CFWL_Widget* pOuter)
-    : CFWL_Widget(app, std::move(properties), pOuter) {}
+CFWL_MonthCalendar::CFWL_MonthCalendar(CFWL_App* app,
+                                       const Properties& properties,
+                                       CFWL_Widget* pOuter)
+    : CFWL_Widget(app, properties, pOuter) {}
 
 CFWL_MonthCalendar::~CFWL_MonthCalendar() = default;
 
@@ -108,44 +108,36 @@
 void CFWL_MonthCalendar::Update() {
   if (IsLocked())
     return;
-  if (!m_pProperties->m_pThemeProvider)
-    m_pProperties->m_pThemeProvider = GetAvailableTheme();
 
-  GetCapValue();
   if (!m_bInitialized) {
     InitDate();
     m_bInitialized = true;
   }
-
   ClearDateItem();
   ResetDateItem();
   Layout();
 }
 
-void CFWL_MonthCalendar::DrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_MonthCalendar::DrawWidget(CFGAS_GEGraphics* pGraphics,
                                     const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
 
-  if (!m_pProperties->m_pThemeProvider)
-    m_pProperties->m_pThemeProvider = GetAvailableTheme();
-
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
   if (HasBorder())
-    DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
+    DrawBorder(pGraphics, CFWL_ThemePart::Part::kBorder, matrix);
 
-  DrawBackground(pGraphics, pTheme, &matrix);
-  DrawHeadBK(pGraphics, pTheme, &matrix);
-  DrawLButton(pGraphics, pTheme, &matrix);
-  DrawRButton(pGraphics, pTheme, &matrix);
-  DrawSeparator(pGraphics, pTheme, &matrix);
-  DrawDatesInBK(pGraphics, pTheme, &matrix);
-  DrawDatesInCircle(pGraphics, pTheme, &matrix);
-  DrawCaption(pGraphics, pTheme, &matrix);
-  DrawWeek(pGraphics, pTheme, &matrix);
-  DrawDatesIn(pGraphics, pTheme, &matrix);
-  DrawDatesOut(pGraphics, pTheme, &matrix);
-  DrawToday(pGraphics, pTheme, &matrix);
+  DrawBackground(pGraphics, matrix);
+  DrawHeadBK(pGraphics, matrix);
+  DrawLButton(pGraphics, matrix);
+  DrawRButton(pGraphics, matrix);
+  DrawSeparator(pGraphics, matrix);
+  DrawDatesInBK(pGraphics, matrix);
+  DrawDatesInCircle(pGraphics, matrix);
+  DrawCaption(pGraphics, matrix);
+  DrawWeek(pGraphics, matrix);
+  DrawDatesIn(pGraphics, matrix);
+  DrawDatesOut(pGraphics, matrix);
+  DrawToday(pGraphics, matrix);
 }
 
 void CFWL_MonthCalendar::SetSelect(int32_t iYear,
@@ -155,342 +147,269 @@
   AddSelDay(iDay);
 }
 
-void CFWL_MonthCalendar::DrawBackground(CXFA_Graphics* pGraphics,
-                                        IFWL_ThemeProvider* pTheme,
-                                        const CFX_Matrix* pMatrix) {
-  CFWL_ThemeBackground params;
-  params.m_pWidget = this;
-  params.m_iPart = CFWL_Part::Background;
-  params.m_pGraphics = pGraphics;
-  params.m_dwStates = CFWL_PartState_Normal;
-  params.m_rtPart = m_rtClient;
-  if (pMatrix)
-    params.m_matrix.Concat(*pMatrix);
-  pTheme->DrawBackground(params);
+void CFWL_MonthCalendar::DrawBackground(CFGAS_GEGraphics* pGraphics,
+                                        const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeBackground params(CFWL_ThemePart::Part::kBackground, this,
+                              pGraphics);
+  params.m_PartRect = m_ClientRect;
+  params.m_matrix = mtMatrix;
+  GetThemeProvider()->DrawBackground(params);
 }
 
-void CFWL_MonthCalendar::DrawHeadBK(CXFA_Graphics* pGraphics,
-                                    IFWL_ThemeProvider* pTheme,
-                                    const CFX_Matrix* pMatrix) {
-  CFWL_ThemeBackground params;
-  params.m_pWidget = this;
-  params.m_iPart = CFWL_Part::Header;
-  params.m_pGraphics = pGraphics;
-  params.m_dwStates = CFWL_PartState_Normal;
-  params.m_rtPart = m_rtHead;
-  if (pMatrix)
-    params.m_matrix.Concat(*pMatrix);
-  pTheme->DrawBackground(params);
+void CFWL_MonthCalendar::DrawHeadBK(CFGAS_GEGraphics* pGraphics,
+                                    const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeBackground params(CFWL_ThemePart::Part::kHeader, this, pGraphics);
+  params.m_PartRect = m_HeadRect;
+  params.m_matrix = mtMatrix;
+  GetThemeProvider()->DrawBackground(params);
 }
 
-void CFWL_MonthCalendar::DrawLButton(CXFA_Graphics* pGraphics,
-                                     IFWL_ThemeProvider* pTheme,
-                                     const CFX_Matrix* pMatrix) {
-  CFWL_ThemeBackground params;
-  params.m_pWidget = this;
-  params.m_iPart = CFWL_Part::LBtn;
-  params.m_pGraphics = pGraphics;
+void CFWL_MonthCalendar::DrawLButton(CFGAS_GEGraphics* pGraphics,
+                                     const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeBackground params(CFWL_ThemePart::Part::kLBtn, this, pGraphics);
   params.m_dwStates = m_iLBtnPartStates;
-  params.m_rtPart = m_rtLBtn;
-  if (pMatrix)
-    params.m_matrix.Concat(*pMatrix);
-  pTheme->DrawBackground(params);
+  params.m_PartRect = m_LBtnRect;
+  params.m_matrix = mtMatrix;
+  GetThemeProvider()->DrawBackground(params);
 }
 
-void CFWL_MonthCalendar::DrawRButton(CXFA_Graphics* pGraphics,
-                                     IFWL_ThemeProvider* pTheme,
-                                     const CFX_Matrix* pMatrix) {
-  CFWL_ThemeBackground params;
-  params.m_pWidget = this;
-  params.m_iPart = CFWL_Part::RBtn;
-  params.m_pGraphics = pGraphics;
+void CFWL_MonthCalendar::DrawRButton(CFGAS_GEGraphics* pGraphics,
+                                     const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeBackground params(CFWL_ThemePart::Part::kRBtn, this, pGraphics);
   params.m_dwStates = m_iRBtnPartStates;
-  params.m_rtPart = m_rtRBtn;
-  if (pMatrix)
-    params.m_matrix.Concat(*pMatrix);
-  pTheme->DrawBackground(params);
+  params.m_PartRect = m_RBtnRect;
+  params.m_matrix = mtMatrix;
+  GetThemeProvider()->DrawBackground(params);
 }
 
-void CFWL_MonthCalendar::DrawCaption(CXFA_Graphics* pGraphics,
-                                     IFWL_ThemeProvider* pTheme,
-                                     const CFX_Matrix* pMatrix) {
-  CFWL_ThemeText textParam;
-  textParam.m_pWidget = this;
-  textParam.m_iPart = CFWL_Part::Caption;
-  textParam.m_dwStates = CFWL_PartState_Normal;
-  textParam.m_pGraphics = pGraphics;
+void CFWL_MonthCalendar::DrawCaption(CFGAS_GEGraphics* pGraphics,
+                                     const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeText textParam(CFWL_ThemePart::Part::kCaption, this, pGraphics);
   textParam.m_wsText = GetHeadText(m_iCurYear, m_iCurMonth);
-  m_szHead = CalcTextSize(textParam.m_wsText,
-                          m_pProperties->m_pThemeProvider.Get(), false);
+  m_HeadSize = CalcTextSize(textParam.m_wsText, false);
   CalcHeadSize();
-  textParam.m_rtPart = m_rtHeadText;
+  textParam.m_PartRect = m_HeadTextRect;
   textParam.m_dwTTOStyles.single_line_ = true;
   textParam.m_iTTOAlign = FDE_TextAlignment::kCenter;
-  if (pMatrix)
-    textParam.m_matrix.Concat(*pMatrix);
-  pTheme->DrawText(textParam);
+  textParam.m_matrix = mtMatrix;
+  GetThemeProvider()->DrawText(textParam);
 }
 
-void CFWL_MonthCalendar::DrawSeparator(CXFA_Graphics* pGraphics,
-                                       IFWL_ThemeProvider* pTheme,
-                                       const CFX_Matrix* pMatrix) {
-  CFWL_ThemeBackground params;
-  params.m_pWidget = this;
-  params.m_iPart = CFWL_Part::HSeparator;
-  params.m_pGraphics = pGraphics;
-  params.m_dwStates = CFWL_PartState_Normal;
-  params.m_rtPart = m_rtHSep;
-  if (pMatrix)
-    params.m_matrix.Concat(*pMatrix);
-  pTheme->DrawBackground(params);
+void CFWL_MonthCalendar::DrawSeparator(CFGAS_GEGraphics* pGraphics,
+                                       const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeBackground params(CFWL_ThemePart::Part::kHSeparator, this,
+                              pGraphics);
+  params.m_PartRect = m_HSepRect;
+  params.m_matrix = mtMatrix;
+  GetThemeProvider()->DrawBackground(params);
 }
 
-void CFWL_MonthCalendar::DrawDatesInBK(CXFA_Graphics* pGraphics,
-                                       IFWL_ThemeProvider* pTheme,
-                                       const CFX_Matrix* pMatrix) {
-  CFWL_ThemeBackground params;
-  params.m_pWidget = this;
-  params.m_iPart = CFWL_Part::DateInBK;
-  params.m_pGraphics = pGraphics;
-  if (pMatrix)
-    params.m_matrix.Concat(*pMatrix);
+void CFWL_MonthCalendar::DrawDatesInBK(CFGAS_GEGraphics* pGraphics,
+                                       const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeBackground params(CFWL_ThemePart::Part::kDateInBK, this, pGraphics);
+  params.m_matrix = mtMatrix;
 
-  int32_t iCount = pdfium::CollectionSize<int32_t>(m_arrDates);
+  IFWL_ThemeProvider* pTheme = GetThemeProvider();
+  int32_t iCount = fxcrt::CollectionSize<int32_t>(m_DateArray);
   for (int32_t j = 0; j < iCount; j++) {
-    DATEINFO* pDataInfo = m_arrDates[j].get();
-    if (pDataInfo->dwStates & FWL_ITEMSTATE_MCD_Selected) {
-      params.m_dwStates |= CFWL_PartState_Selected;
-      if (pDataInfo->dwStates & FWL_ITEMSTATE_MCD_Flag) {
-        params.m_dwStates |= CFWL_PartState_Flagged;
+    DATEINFO* pDataInfo = m_DateArray[j].get();
+    if (pDataInfo->bSelected) {
+      params.m_dwStates |= CFWL_PartState::kSelected;
+      if (pDataInfo->bFlagged) {
+        params.m_dwStates |= CFWL_PartState::kFlagged;
       }
     } else if (j == m_iHovered - 1) {
-      params.m_dwStates |= CFWL_PartState_Hovered;
-    } else if (pDataInfo->dwStates & FWL_ITEMSTATE_MCD_Flag) {
-      params.m_dwStates = CFWL_PartState_Flagged;
+      params.m_dwStates |= CFWL_PartState::kHovered;
+    } else if (pDataInfo->bFlagged) {
+      params.m_dwStates = CFWL_PartState::kFlagged;
       pTheme->DrawBackground(params);
     }
-    params.m_rtPart = pDataInfo->rect;
+    params.m_PartRect = pDataInfo->rect;
     pTheme->DrawBackground(params);
-    params.m_dwStates = 0;
+    params.m_dwStates = CFWL_PartState::kNormal;
   }
 }
 
-void CFWL_MonthCalendar::DrawWeek(CXFA_Graphics* pGraphics,
-                                  IFWL_ThemeProvider* pTheme,
-                                  const CFX_Matrix* pMatrix) {
-  CFWL_ThemeText params;
-  params.m_pWidget = this;
-  params.m_iPart = CFWL_Part::Week;
-  params.m_pGraphics = pGraphics;
-  params.m_dwStates = CFWL_PartState_Normal;
+void CFWL_MonthCalendar::DrawWeek(CFGAS_GEGraphics* pGraphics,
+                                  const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeText params(CFWL_ThemePart::Part::kWeek, this, pGraphics);
   params.m_iTTOAlign = FDE_TextAlignment::kCenter;
   params.m_dwTTOStyles.single_line_ = true;
+  params.m_matrix = mtMatrix;
 
+  IFWL_ThemeProvider* pTheme = GetThemeProvider();
   CFX_RectF rtDayOfWeek;
-  if (pMatrix)
-    params.m_matrix.Concat(*pMatrix);
-
   for (int32_t i = 0; i < 7; ++i) {
-    rtDayOfWeek =
-        CFX_RectF(m_rtWeek.left + i * (m_szCell.width + MONTHCAL_HMARGIN * 2),
-                  m_rtWeek.top, m_szCell);
+    rtDayOfWeek = CFX_RectF(
+        m_WeekRect.left + i * (m_CellSize.width + kMonthCalHMargin * 2),
+        m_WeekRect.top, m_CellSize);
 
-    params.m_rtPart = rtDayOfWeek;
+    params.m_PartRect = rtDayOfWeek;
     params.m_wsText = GetAbbreviatedDayOfWeek(i);
     pTheme->DrawText(params);
   }
 }
 
-void CFWL_MonthCalendar::DrawToday(CXFA_Graphics* pGraphics,
-                                   IFWL_ThemeProvider* pTheme,
-                                   const CFX_Matrix* pMatrix) {
-  CFWL_ThemeText params;
-  params.m_pWidget = this;
-  params.m_iPart = CFWL_Part::Today;
-  params.m_pGraphics = pGraphics;
-  params.m_dwStates = CFWL_PartState_Normal;
+void CFWL_MonthCalendar::DrawToday(CFGAS_GEGraphics* pGraphics,
+                                   const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeText params(CFWL_ThemePart::Part::kToday, this, pGraphics);
   params.m_iTTOAlign = FDE_TextAlignment::kCenterLeft;
   params.m_wsText = GetTodayText(m_iYear, m_iMonth, m_iDay);
-
-  m_szToday = CalcTextSize(params.m_wsText,
-                           m_pProperties->m_pThemeProvider.Get(), false);
+  m_TodaySize = CalcTextSize(params.m_wsText, false);
   CalcTodaySize();
-  params.m_rtPart = m_rtToday;
+  params.m_PartRect = m_TodayRect;
   params.m_dwTTOStyles.single_line_ = true;
-
-  if (pMatrix)
-    params.m_matrix.Concat(*pMatrix);
-  pTheme->DrawText(params);
+  params.m_matrix = mtMatrix;
+  GetThemeProvider()->DrawText(params);
 }
 
-void CFWL_MonthCalendar::DrawDatesIn(CXFA_Graphics* pGraphics,
-                                     IFWL_ThemeProvider* pTheme,
-                                     const CFX_Matrix* pMatrix) {
-  CFWL_ThemeText params;
-  params.m_pWidget = this;
-  params.m_iPart = CFWL_Part::DatesIn;
-  params.m_pGraphics = pGraphics;
-  params.m_dwStates = CFWL_PartState_Normal;
+void CFWL_MonthCalendar::DrawDatesIn(CFGAS_GEGraphics* pGraphics,
+                                     const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeText params(CFWL_ThemePart::Part::kDatesIn, this, pGraphics);
   params.m_iTTOAlign = FDE_TextAlignment::kCenter;
-  if (pMatrix)
-    params.m_matrix.Concat(*pMatrix);
+  params.m_matrix = mtMatrix;
 
-  int32_t iCount = pdfium::CollectionSize<int32_t>(m_arrDates);
+  IFWL_ThemeProvider* pTheme = GetThemeProvider();
+  int32_t iCount = fxcrt::CollectionSize<int32_t>(m_DateArray);
   for (int32_t j = 0; j < iCount; j++) {
-    DATEINFO* pDataInfo = m_arrDates[j].get();
+    DATEINFO* pDataInfo = m_DateArray[j].get();
     params.m_wsText = pDataInfo->wsDay;
-    params.m_rtPart = pDataInfo->rect;
-    params.m_dwStates = pDataInfo->dwStates;
+    params.m_PartRect = pDataInfo->rect;
+    params.m_dwStates = pDataInfo->AsPartStateMask();
     if (j + 1 == m_iHovered)
-      params.m_dwStates |= CFWL_PartState_Hovered;
+      params.m_dwStates |= CFWL_PartState::kHovered;
 
     params.m_dwTTOStyles.single_line_ = true;
     pTheme->DrawText(params);
   }
 }
 
-void CFWL_MonthCalendar::DrawDatesOut(CXFA_Graphics* pGraphics,
-                                      IFWL_ThemeProvider* pTheme,
-                                      const CFX_Matrix* pMatrix) {
-  CFWL_ThemeText params;
-  params.m_pWidget = this;
-  params.m_iPart = CFWL_Part::DatesOut;
-  params.m_pGraphics = pGraphics;
-  params.m_dwStates = CFWL_PartState_Normal;
+void CFWL_MonthCalendar::DrawDatesOut(CFGAS_GEGraphics* pGraphics,
+                                      const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeText params(CFWL_ThemePart::Part::kDatesOut, this, pGraphics);
   params.m_iTTOAlign = FDE_TextAlignment::kCenter;
-  if (pMatrix)
-    params.m_matrix.Concat(*pMatrix);
-  pTheme->DrawText(params);
+  params.m_matrix = mtMatrix;
+  GetThemeProvider()->DrawText(params);
 }
 
-void CFWL_MonthCalendar::DrawDatesInCircle(CXFA_Graphics* pGraphics,
-                                           IFWL_ThemeProvider* pTheme,
-                                           const CFX_Matrix* pMatrix) {
+void CFWL_MonthCalendar::DrawDatesInCircle(CFGAS_GEGraphics* pGraphics,
+                                           const CFX_Matrix& mtMatrix) {
   if (m_iMonth != m_iCurMonth || m_iYear != m_iCurYear)
     return;
 
-  if (m_iDay < 1 || m_iDay > pdfium::CollectionSize<int32_t>(m_arrDates))
+  if (m_iDay < 1 || m_iDay > fxcrt::CollectionSize<int32_t>(m_DateArray))
     return;
 
-  DATEINFO* pDate = m_arrDates[m_iDay - 1].get();
+  DATEINFO* pDate = m_DateArray[m_iDay - 1].get();
   if (!pDate)
     return;
 
-  CFWL_ThemeBackground params;
-  params.m_pWidget = this;
-  params.m_iPart = CFWL_Part::DateInCircle;
-  params.m_pGraphics = pGraphics;
-  params.m_rtPart = pDate->rect;
-  params.m_dwStates = CFWL_PartState_Normal;
-  if (pMatrix)
-    params.m_matrix.Concat(*pMatrix);
-  pTheme->DrawBackground(params);
+  CFWL_ThemeBackground params(CFWL_ThemePart::Part::kDateInCircle, this,
+                              pGraphics);
+  params.m_PartRect = pDate->rect;
+  params.m_matrix = mtMatrix;
+  GetThemeProvider()->DrawBackground(params);
 }
 
 CFX_SizeF CFWL_MonthCalendar::CalcSize() {
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
-  if (!pTheme)
-    return CFX_SizeF();
-
   float fMaxWeekW = 0.0f;
   float fMaxWeekH = 0.0f;
   for (int i = 0; i < 7; ++i) {
-    CFX_SizeF sz = CalcTextSize(GetAbbreviatedDayOfWeek(i), pTheme, false);
+    CFX_SizeF sz = CalcTextSize(GetAbbreviatedDayOfWeek(i), false);
     fMaxWeekW = (fMaxWeekW >= sz.width) ? fMaxWeekW : sz.width;
     fMaxWeekH = (fMaxWeekH >= sz.height) ? fMaxWeekH : sz.height;
   }
-
   float fDayMaxW = 0.0f;
   float fDayMaxH = 0.0f;
   for (int day = 10; day <= 31; day++) {
-    CFX_SizeF sz = CalcTextSize(WideString::Format(L"%d", day), pTheme, false);
+    CFX_SizeF sz = CalcTextSize(WideString::FormatInteger(day), false);
     fDayMaxW = (fDayMaxW >= sz.width) ? fDayMaxW : sz.width;
     fDayMaxH = (fDayMaxH >= sz.height) ? fDayMaxH : sz.height;
   }
-  m_szCell.width =
+  m_CellSize.width =
       static_cast<int>(0.5 + (fMaxWeekW >= fDayMaxW ? fMaxWeekW : fDayMaxW));
-  m_szCell.height = fMaxWeekH >= fDayMaxH ? fMaxWeekH : fDayMaxH;
+  m_CellSize.height = fMaxWeekH >= fDayMaxH ? fMaxWeekH : fDayMaxH;
 
   CFX_SizeF fs;
-  fs.width = m_szCell.width * MONTHCAL_COLUMNS +
-             MONTHCAL_HMARGIN * MONTHCAL_COLUMNS * 2 +
-             MONTHCAL_HEADER_BTN_HMARGIN * 2;
+  fs.width = m_CellSize.width * kMonthCalColumns +
+             kMonthCalHMargin * kMonthCalColumns * 2 +
+             kMonthCalHeaderBtnHMargin * 2;
 
   float fMonthMaxW = 0.0f;
   float fMonthMaxH = 0.0f;
   for (int i = 0; i < 12; ++i) {
-    CFX_SizeF sz = CalcTextSize(GetMonth(i), pTheme, false);
+    CFX_SizeF sz = CalcTextSize(GetMonth(i), false);
     fMonthMaxW = (fMonthMaxW >= sz.width) ? fMonthMaxW : sz.width;
     fMonthMaxH = (fMonthMaxH >= sz.height) ? fMonthMaxH : sz.height;
   }
 
-  CFX_SizeF szYear =
-      CalcTextSize(GetHeadText(m_iYear, m_iMonth), pTheme, false);
+  CFX_SizeF szYear = CalcTextSize(GetHeadText(m_iYear, m_iMonth), false);
   fMonthMaxH = std::max(fMonthMaxH, szYear.height);
-  m_szHead = CFX_SizeF(fMonthMaxW + szYear.width, fMonthMaxH);
+  m_HeadSize = CFX_SizeF(fMonthMaxW + szYear.width, fMonthMaxH);
   fMonthMaxW =
-      m_szHead.width + MONTHCAL_HEADER_BTN_HMARGIN * 2 + m_szCell.width * 2;
+      m_HeadSize.width + kMonthCalHeaderBtnHMargin * 2 + m_CellSize.width * 2;
   fs.width = std::max(fs.width, fMonthMaxW);
 
   m_wsToday = GetTodayText(m_iYear, m_iMonth, m_iDay);
-  m_szToday = CalcTextSize(m_wsToday, pTheme, false);
-  m_szToday.height = (m_szToday.height >= m_szCell.height) ? m_szToday.height
-                                                           : m_szCell.height;
-  fs.height = m_szCell.width + m_szCell.height * (MONTHCAL_ROWS - 2) +
-              m_szToday.height + MONTHCAL_VMARGIN * MONTHCAL_ROWS * 2 +
-              MONTHCAL_HEADER_BTN_VMARGIN * 4;
+  m_TodaySize = CalcTextSize(m_wsToday, false);
+  m_TodaySize.height = (m_TodaySize.height >= m_CellSize.height)
+                           ? m_TodaySize.height
+                           : m_CellSize.height;
+  fs.height = m_CellSize.width + m_CellSize.height * (kMonthCalRows - 2) +
+              m_TodaySize.height + kMonthCalVMargin * kMonthCalRows * 2 +
+              kMonthCalHeaderBtnVMargin * 4;
   return fs;
 }
 
 void CFWL_MonthCalendar::CalcHeadSize() {
-  float fHeadHMargin = (m_rtClient.width - m_szHead.width) / 2;
-  float fHeadVMargin = (m_szCell.width - m_szHead.height) / 2;
-  m_rtHeadText = CFX_RectF(m_rtClient.left + fHeadHMargin,
-                           m_rtClient.top + MONTHCAL_HEADER_BTN_VMARGIN +
-                               MONTHCAL_VMARGIN + fHeadVMargin,
-                           m_szHead);
+  float fHeadHMargin = (m_ClientRect.width - m_HeadSize.width) / 2;
+  float fHeadVMargin = (m_CellSize.width - m_HeadSize.height) / 2;
+  m_HeadTextRect = CFX_RectF(m_ClientRect.left + fHeadHMargin,
+                             m_ClientRect.top + kMonthCalHeaderBtnVMargin +
+                                 kMonthCalVMargin + fHeadVMargin,
+                             m_HeadSize);
 }
 
 void CFWL_MonthCalendar::CalcTodaySize() {
-  m_rtTodayFlag = CFX_RectF(
-      m_rtClient.left + MONTHCAL_HEADER_BTN_HMARGIN + MONTHCAL_HMARGIN,
-      m_rtDates.bottom() + MONTHCAL_HEADER_BTN_VMARGIN + MONTHCAL_VMARGIN,
-      m_szCell.width, m_szToday.height);
-  m_rtToday = CFX_RectF(
-      m_rtClient.left + MONTHCAL_HEADER_BTN_HMARGIN + m_szCell.width +
-          MONTHCAL_HMARGIN * 2,
-      m_rtDates.bottom() + MONTHCAL_HEADER_BTN_VMARGIN + MONTHCAL_VMARGIN,
-      m_szToday);
+  m_TodayFlagRect = CFX_RectF(
+      m_ClientRect.left + kMonthCalHeaderBtnHMargin + kMonthCalHMargin,
+      m_DatesRect.bottom() + kMonthCalHeaderBtnVMargin + kMonthCalVMargin,
+      m_CellSize.width, m_TodaySize.height);
+  m_TodayRect = CFX_RectF(
+      m_ClientRect.left + kMonthCalHeaderBtnHMargin + m_CellSize.width +
+          kMonthCalHMargin * 2,
+      m_DatesRect.bottom() + kMonthCalHeaderBtnVMargin + kMonthCalVMargin,
+      m_TodaySize);
 }
 
 void CFWL_MonthCalendar::Layout() {
-  m_rtClient = GetClientRect();
+  m_ClientRect = GetClientRect();
 
-  m_rtHead = CFX_RectF(
-      m_rtClient.left + MONTHCAL_HEADER_BTN_HMARGIN, m_rtClient.top,
-      m_rtClient.width - MONTHCAL_HEADER_BTN_HMARGIN * 2,
-      m_szCell.width + (MONTHCAL_HEADER_BTN_VMARGIN + MONTHCAL_VMARGIN) * 2);
-  m_rtWeek = CFX_RectF(m_rtClient.left + MONTHCAL_HEADER_BTN_HMARGIN,
-                       m_rtHead.bottom(),
-                       m_rtClient.width - MONTHCAL_HEADER_BTN_HMARGIN * 2,
-                       m_szCell.height + MONTHCAL_VMARGIN * 2);
-  m_rtLBtn = CFX_RectF(m_rtClient.left + MONTHCAL_HEADER_BTN_HMARGIN,
-                       m_rtClient.top + MONTHCAL_HEADER_BTN_VMARGIN,
-                       m_szCell.width, m_szCell.width);
-  m_rtRBtn = CFX_RectF(m_rtClient.left + m_rtClient.width -
-                           MONTHCAL_HEADER_BTN_HMARGIN - m_szCell.width,
-                       m_rtClient.top + MONTHCAL_HEADER_BTN_VMARGIN,
-                       m_szCell.width, m_szCell.width);
-  m_rtHSep = CFX_RectF(
-      m_rtClient.left + MONTHCAL_HEADER_BTN_HMARGIN + MONTHCAL_HMARGIN,
-      m_rtWeek.bottom() - MONTHCAL_VMARGIN,
-      m_rtClient.width - (MONTHCAL_HEADER_BTN_HMARGIN + MONTHCAL_HMARGIN) * 2,
-      MONTHCAL_HSEP_HEIGHT);
-  m_rtDates = CFX_RectF(m_rtClient.left + MONTHCAL_HEADER_BTN_HMARGIN,
-                        m_rtWeek.bottom(),
-                        m_rtClient.width - MONTHCAL_HEADER_BTN_HMARGIN * 2,
-                        m_szCell.height * (MONTHCAL_ROWS - 3) +
-                            MONTHCAL_VMARGIN * (MONTHCAL_ROWS - 3) * 2);
+  m_HeadRect = CFX_RectF(
+      m_ClientRect.left + kMonthCalHeaderBtnHMargin, m_ClientRect.top,
+      m_ClientRect.width - kMonthCalHeaderBtnHMargin * 2,
+      m_CellSize.width + (kMonthCalHeaderBtnVMargin + kMonthCalVMargin) * 2);
+  m_WeekRect = CFX_RectF(m_ClientRect.left + kMonthCalHeaderBtnHMargin,
+                         m_HeadRect.bottom(),
+                         m_ClientRect.width - kMonthCalHeaderBtnHMargin * 2,
+                         m_CellSize.height + kMonthCalVMargin * 2);
+  m_LBtnRect = CFX_RectF(m_ClientRect.left + kMonthCalHeaderBtnHMargin,
+                         m_ClientRect.top + kMonthCalHeaderBtnVMargin,
+                         m_CellSize.width, m_CellSize.width);
+  m_RBtnRect = CFX_RectF(m_ClientRect.left + m_ClientRect.width -
+                             kMonthCalHeaderBtnHMargin - m_CellSize.width,
+                         m_ClientRect.top + kMonthCalHeaderBtnVMargin,
+                         m_CellSize.width, m_CellSize.width);
+  m_HSepRect = CFX_RectF(
+      m_ClientRect.left + kMonthCalHeaderBtnHMargin + kMonthCalHMargin,
+      m_WeekRect.bottom() - kMonthCalVMargin,
+      m_ClientRect.width - (kMonthCalHeaderBtnHMargin + kMonthCalHMargin) * 2,
+      kMonthCalHSepHeight);
+  m_DatesRect = CFX_RectF(m_ClientRect.left + kMonthCalHeaderBtnHMargin,
+                          m_WeekRect.bottom(),
+                          m_ClientRect.width - kMonthCalHeaderBtnHMargin * 2,
+                          m_CellSize.height * (kMonthCalRows - 3) +
+                              kMonthCalVMargin * (kMonthCalRows - 3) * 2);
 
   CalDateItem();
 }
@@ -498,29 +417,24 @@
 void CFWL_MonthCalendar::CalDateItem() {
   bool bNewWeek = false;
   int32_t iWeekOfMonth = 0;
-  float fLeft = m_rtDates.left;
-  float fTop = m_rtDates.top;
-  for (const auto& pDateInfo : m_arrDates) {
+  float fLeft = m_DatesRect.left;
+  float fTop = m_DatesRect.top;
+  for (const auto& pDateInfo : m_DateArray) {
     if (bNewWeek) {
       iWeekOfMonth++;
       bNewWeek = false;
     }
     pDateInfo->rect = CFX_RectF(
         fLeft +
-            pDateInfo->iDayOfWeek * (m_szCell.width + (MONTHCAL_HMARGIN * 2)),
-        fTop + iWeekOfMonth * (m_szCell.height + (MONTHCAL_VMARGIN * 2)),
-        m_szCell.width + (MONTHCAL_HMARGIN * 2),
-        m_szCell.height + (MONTHCAL_VMARGIN * 2));
+            pDateInfo->iDayOfWeek * (m_CellSize.width + (kMonthCalHMargin * 2)),
+        fTop + iWeekOfMonth * (m_CellSize.height + (kMonthCalVMargin * 2)),
+        m_CellSize.width + (kMonthCalHMargin * 2),
+        m_CellSize.height + (kMonthCalVMargin * 2));
     if (pDateInfo->iDayOfWeek >= 6)
       bNewWeek = true;
   }
 }
 
-void CFWL_MonthCalendar::GetCapValue() {
-  if (!m_pProperties->m_pThemeProvider)
-    m_pProperties->m_pThemeProvider = GetAvailableTheme();
-}
-
 void CFWL_MonthCalendar::InitDate() {
   CFX_DateTime now = CFX_DateTime::Now();
 
@@ -537,27 +451,23 @@
 }
 
 void CFWL_MonthCalendar::ClearDateItem() {
-  m_arrDates.clear();
+  m_DateArray.clear();
 }
 
 void CFWL_MonthCalendar::ResetDateItem() {
   int32_t iDays = FX_DaysInMonth(m_iCurYear, m_iCurMonth);
   int32_t iDayOfWeek =
       CFX_DateTime(m_iCurYear, m_iCurMonth, 1, 0, 0, 0, 0).GetDayOfWeek();
-  for (int32_t i = 0; i < iDays; i++) {
+  for (int32_t i = 0; i < iDays; ++i, ++iDayOfWeek) {
     if (iDayOfWeek >= 7)
       iDayOfWeek = 0;
 
-    uint32_t dwStates = 0;
-    if (m_iYear == m_iCurYear && m_iMonth == m_iCurMonth && m_iDay == (i + 1))
-      dwStates |= FWL_ITEMSTATE_MCD_Flag;
-    if (pdfium::ContainsValue(m_arrSelDays, i + 1))
-      dwStates |= FWL_ITEMSTATE_MCD_Selected;
-
-    CFX_RectF rtDate;
-    m_arrDates.push_back(pdfium::MakeUnique<DATEINFO>(
-        i + 1, iDayOfWeek, dwStates, rtDate, WideString::Format(L"%d", i + 1)));
-    iDayOfWeek++;
+    const bool bFlagged =
+        m_iYear == m_iCurYear && m_iMonth == m_iCurMonth && m_iDay == i + 1;
+    const bool bSelected = pdfium::Contains(m_SelDayArray, i + 1);
+    m_DateArray.push_back(
+        std::make_unique<DATEINFO>(i + 1, iDayOfWeek, bFlagged, bSelected,
+                                   WideString::FormatInteger(i + 1)));
   }
 }
 
@@ -608,24 +518,24 @@
 }
 
 void CFWL_MonthCalendar::RemoveSelDay() {
-  int32_t iDatesCount = pdfium::CollectionSize<int32_t>(m_arrDates);
-  for (int32_t iSelDay : m_arrSelDays) {
+  int32_t iDatesCount = fxcrt::CollectionSize<int32_t>(m_DateArray);
+  for (int32_t iSelDay : m_SelDayArray) {
     if (iSelDay <= iDatesCount)
-      m_arrDates[iSelDay - 1]->dwStates &= ~FWL_ITEMSTATE_MCD_Selected;
+      m_DateArray[iSelDay - 1]->bSelected = false;
   }
-  m_arrSelDays.clear();
+  m_SelDayArray.clear();
 }
 
 void CFWL_MonthCalendar::AddSelDay(int32_t iDay) {
-  ASSERT(iDay > 0);
-  if (!pdfium::ContainsValue(m_arrSelDays, iDay))
+  DCHECK(iDay > 0);
+  if (!pdfium::Contains(m_SelDayArray, iDay))
     return;
 
   RemoveSelDay();
-  if (iDay <= pdfium::CollectionSize<int32_t>(m_arrDates))
-    m_arrDates[iDay - 1]->dwStates |= FWL_ITEMSTATE_MCD_Selected;
+  if (iDay <= fxcrt::CollectionSize<int32_t>(m_DateArray))
+    m_DateArray[iDay - 1]->bSelected = true;
 
-  m_arrSelDays.push_back(iDay);
+  m_SelDayArray.push_back(iDay);
 }
 
 void CFWL_MonthCalendar::JumpToToday() {
@@ -637,13 +547,13 @@
     return;
   }
 
-  if (!pdfium::ContainsValue(m_arrSelDays, m_iDay))
+  if (!pdfium::Contains(m_SelDayArray, m_iDay))
     AddSelDay(m_iDay);
 }
 
 WideString CFWL_MonthCalendar::GetHeadText(int32_t iYear, int32_t iMonth) {
-  ASSERT(iMonth > 0);
-  ASSERT(iMonth < 13);
+  DCHECK(iMonth > 0);
+  DCHECK(iMonth < 13);
 
   static const wchar_t* const pMonth[] = {L"January", L"February", L"March",
                                           L"April",   L"May",      L"June",
@@ -660,7 +570,7 @@
 
 int32_t CFWL_MonthCalendar::GetDayAtPoint(const CFX_PointF& point) const {
   int i = 1;  // one-based day values.
-  for (const auto& pDateInfo : m_arrDates) {
+  for (const auto& pDateInfo : m_DateArray) {
     if (pDateInfo->rect.Contains(point))
       return i;
     ++i;
@@ -669,37 +579,34 @@
 }
 
 CFX_RectF CFWL_MonthCalendar::GetDayRect(int32_t iDay) {
-  if (iDay <= 0 || iDay > pdfium::CollectionSize<int32_t>(m_arrDates))
+  if (iDay <= 0 || iDay > fxcrt::CollectionSize<int32_t>(m_DateArray))
     return CFX_RectF();
 
-  DATEINFO* pDateInfo = m_arrDates[iDay - 1].get();
+  DATEINFO* pDateInfo = m_DateArray[iDay - 1].get();
   return pDateInfo ? pDateInfo->rect : CFX_RectF();
 }
 
 void CFWL_MonthCalendar::OnProcessMessage(CFWL_Message* pMessage) {
-  if (!pMessage)
-    return;
-
   switch (pMessage->GetType()) {
-    case CFWL_Message::Type::SetFocus:
-    case CFWL_Message::Type::KillFocus:
+    case CFWL_Message::Type::kSetFocus:
+    case CFWL_Message::Type::kKillFocus:
       GetOuter()->GetDelegate()->OnProcessMessage(pMessage);
       break;
-    case CFWL_Message::Type::Key:
+    case CFWL_Message::Type::kKey:
       break;
-    case CFWL_Message::Type::Mouse: {
+    case CFWL_Message::Type::kMouse: {
       CFWL_MessageMouse* pMouse = static_cast<CFWL_MessageMouse*>(pMessage);
       switch (pMouse->m_dwCmd) {
-        case FWL_MouseCommand::LeftButtonDown:
+        case CFWL_MessageMouse::MouseCommand::kLeftButtonDown:
           OnLButtonDown(pMouse);
           break;
-        case FWL_MouseCommand::LeftButtonUp:
+        case CFWL_MessageMouse::MouseCommand::kLeftButtonUp:
           OnLButtonUp(pMouse);
           break;
-        case FWL_MouseCommand::Move:
+        case CFWL_MessageMouse::MouseCommand::kMove:
           OnMouseMove(pMouse);
           break;
-        case FWL_MouseCommand::Leave:
+        case CFWL_MessageMouse::MouseCommand::kLeave:
           OnMouseLeave(pMouse);
           break;
         default:
@@ -715,64 +622,64 @@
     CFWL_Widget::OnProcessMessage(pMessage);
 }
 
-void CFWL_MonthCalendar::OnDrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_MonthCalendar::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                                       const CFX_Matrix& matrix) {
   DrawWidget(pGraphics, matrix);
 }
 
 void CFWL_MonthCalendar::OnLButtonDown(CFWL_MessageMouse* pMsg) {
-  if (m_rtLBtn.Contains(pMsg->m_pos)) {
-    m_iLBtnPartStates = CFWL_PartState_Pressed;
+  if (m_LBtnRect.Contains(pMsg->m_pos)) {
+    m_iLBtnPartStates = CFWL_PartState::kPressed;
     PrevMonth();
-    RepaintRect(m_rtClient);
-  } else if (m_rtRBtn.Contains(pMsg->m_pos)) {
-    m_iRBtnPartStates |= CFWL_PartState_Pressed;
+    RepaintRect(m_ClientRect);
+  } else if (m_RBtnRect.Contains(pMsg->m_pos)) {
+    m_iRBtnPartStates |= CFWL_PartState::kPressed;
     NextMonth();
-    RepaintRect(m_rtClient);
-  } else if (m_rtToday.Contains(pMsg->m_pos)) {
+    RepaintRect(m_ClientRect);
+  } else if (m_TodayRect.Contains(pMsg->m_pos)) {
     JumpToToday();
-    RepaintRect(m_rtClient);
+    RepaintRect(m_ClientRect);
   }
 }
 
 void CFWL_MonthCalendar::OnLButtonUp(CFWL_MessageMouse* pMsg) {
-  if (m_rtLBtn.Contains(pMsg->m_pos)) {
-    m_iLBtnPartStates = 0;
-    RepaintRect(m_rtLBtn);
+  if (m_LBtnRect.Contains(pMsg->m_pos)) {
+    m_iLBtnPartStates = CFWL_PartState::kNormal;
+    RepaintRect(m_LBtnRect);
     return;
   }
-  if (m_rtRBtn.Contains(pMsg->m_pos)) {
-    m_iRBtnPartStates = 0;
-    RepaintRect(m_rtRBtn);
+  if (m_RBtnRect.Contains(pMsg->m_pos)) {
+    m_iRBtnPartStates = CFWL_PartState::kNormal;
+    RepaintRect(m_RBtnRect);
     return;
   }
-  if (m_rtToday.Contains(pMsg->m_pos))
+  if (m_TodayRect.Contains(pMsg->m_pos))
     return;
 
   int32_t iOldSel = 0;
-  if (!m_arrSelDays.empty())
-    iOldSel = m_arrSelDays[0];
+  if (!m_SelDayArray.empty())
+    iOldSel = m_SelDayArray[0];
 
   int32_t iCurSel = GetDayAtPoint(pMsg->m_pos);
   if (iCurSel > 0) {
-    DATEINFO* lpDatesInfo = m_arrDates[iCurSel - 1].get();
-    CFX_RectF rtInvalidate(lpDatesInfo->rect);
-    if (iOldSel > 0 && iOldSel <= pdfium::CollectionSize<int32_t>(m_arrDates)) {
-      lpDatesInfo = m_arrDates[iOldSel - 1].get();
-      rtInvalidate.Union(lpDatesInfo->rect);
+    DATEINFO* pDateInfo = m_DateArray[iCurSel - 1].get();
+    CFX_RectF rtInvalidate(pDateInfo->rect);
+    if (iOldSel > 0 && iOldSel <= fxcrt::CollectionSize<int32_t>(m_DateArray)) {
+      pDateInfo = m_DateArray[iOldSel - 1].get();
+      rtInvalidate.Union(pDateInfo->rect);
     }
     AddSelDay(iCurSel);
     CFWL_DateTimePicker* pDateTime =
-        static_cast<CFWL_DateTimePicker*>(m_pOuter);
+        static_cast<CFWL_DateTimePicker*>(GetOuter());
     pDateTime->ProcessSelChanged(m_iCurYear, m_iCurMonth, iCurSel);
-    pDateTime->ShowMonthCalendar(false);
+    pDateTime->HideMonthCalendar();
   }
 }
 
 void CFWL_MonthCalendar::OnMouseMove(CFWL_MessageMouse* pMsg) {
   bool bRepaint = false;
   CFX_RectF rtInvalidate;
-  if (m_rtDates.Contains(pMsg->m_pos)) {
+  if (m_DatesRect.Contains(pMsg->m_pos)) {
     int32_t iHover = GetDayAtPoint(pMsg->m_pos);
     bRepaint = m_iHovered != iHover;
     if (bRepaint) {
@@ -810,13 +717,22 @@
 
 CFWL_MonthCalendar::DATEINFO::DATEINFO(int32_t day,
                                        int32_t dayofweek,
-                                       uint32_t dwSt,
-                                       CFX_RectF rc,
+                                       bool bFlag,
+                                       bool bSelect,
                                        const WideString& wsday)
     : iDay(day),
       iDayOfWeek(dayofweek),
-      dwStates(dwSt),
-      rect(rc),
+      bFlagged(bFlag),
+      bSelected(bSelect),
       wsDay(wsday) {}
 
-CFWL_MonthCalendar::DATEINFO::~DATEINFO() {}
+CFWL_MonthCalendar::DATEINFO::~DATEINFO() = default;
+
+Mask<CFWL_PartState> CFWL_MonthCalendar::DATEINFO::AsPartStateMask() const {
+  Mask<CFWL_PartState> dwStates = CFWL_PartState::kNormal;
+  if (bFlagged)
+    dwStates |= CFWL_PartState::kFlagged;
+  if (bSelected)
+    dwStates |= CFWL_PartState::kSelected;
+  return dwStates;
+}
diff --git a/xfa/fwl/cfwl_monthcalendar.h b/xfa/fwl/cfwl_monthcalendar.h
index a00ea0f..c7c7183 100644
--- a/xfa/fwl/cfwl_monthcalendar.h
+++ b/xfa/fwl/cfwl_monthcalendar.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,30 +10,25 @@
 #include <memory>
 #include <vector>
 
-#include "core/fxcrt/cfx_datetime.h"
+#include "core/fxcrt/widestring.h"
 #include "xfa/fwl/cfwl_event.h"
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fwl/cfwl_widgetproperties.h"
-
-#define FWL_ITEMSTATE_MCD_Flag (1L << 0)
-#define FWL_ITEMSTATE_MCD_Selected (1L << 1)
 
 class CFWL_MessageMouse;
 
 class CFWL_MonthCalendar final : public CFWL_Widget {
  public:
-  CFWL_MonthCalendar(const CFWL_App* app,
-                     std::unique_ptr<CFWL_WidgetProperties> properties,
-                     CFWL_Widget* pOuter);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_MonthCalendar() override;
 
-  // FWL_WidgetImp
+  // CFWL_Widget:
   FWL_Type GetClassID() const override;
   CFX_RectF GetAutosizedWidgetRect() override;
   void Update() override;
-  void DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) override;
+  void DrawWidget(CFGAS_GEGraphics* pGraphics,
+                  const CFX_Matrix& matrix) override;
   void OnProcessMessage(CFWL_Message* pMessage) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
+  void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix) override;
 
   void SetSelect(int32_t iYear, int32_t iMonth, int32_t iDay);
@@ -73,63 +68,47 @@
     int32_t iMonth;
     int32_t iDay;
   };
+
   struct DATEINFO {
     DATEINFO(int32_t day,
              int32_t dayofweek,
-             uint32_t dwSt,
-             CFX_RectF rc,
+             bool bFlag,
+             bool bSelect,
              const WideString& wsday);
     ~DATEINFO();
 
-    int32_t iDay;
-    int32_t iDayOfWeek;
-    uint32_t dwStates;
+    Mask<CFWL_PartState> AsPartStateMask() const;
+
+    const int32_t iDay;
+    const int32_t iDayOfWeek;
+    bool bFlagged;
+    bool bSelected;
     CFX_RectF rect;
-    WideString wsDay;
+    const WideString wsDay;
   };
 
-  void DrawBackground(CXFA_Graphics* pGraphics,
-                      IFWL_ThemeProvider* pTheme,
-                      const CFX_Matrix* pMatrix);
-  void DrawHeadBK(CXFA_Graphics* pGraphics,
-                  IFWL_ThemeProvider* pTheme,
-                  const CFX_Matrix* pMatrix);
-  void DrawLButton(CXFA_Graphics* pGraphics,
-                   IFWL_ThemeProvider* pTheme,
-                   const CFX_Matrix* pMatrix);
-  void DrawRButton(CXFA_Graphics* pGraphics,
-                   IFWL_ThemeProvider* pTheme,
-                   const CFX_Matrix* pMatrix);
-  void DrawCaption(CXFA_Graphics* pGraphics,
-                   IFWL_ThemeProvider* pTheme,
-                   const CFX_Matrix* pMatrix);
-  void DrawSeparator(CXFA_Graphics* pGraphics,
-                     IFWL_ThemeProvider* pTheme,
-                     const CFX_Matrix* pMatrix);
-  void DrawDatesInBK(CXFA_Graphics* pGraphics,
-                     IFWL_ThemeProvider* pTheme,
-                     const CFX_Matrix* pMatrix);
-  void DrawWeek(CXFA_Graphics* pGraphics,
-                IFWL_ThemeProvider* pTheme,
-                const CFX_Matrix* pMatrix);
-  void DrawToday(CXFA_Graphics* pGraphics,
-                 IFWL_ThemeProvider* pTheme,
-                 const CFX_Matrix* pMatrix);
-  void DrawDatesIn(CXFA_Graphics* pGraphics,
-                   IFWL_ThemeProvider* pTheme,
-                   const CFX_Matrix* pMatrix);
-  void DrawDatesOut(CXFA_Graphics* pGraphics,
-                    IFWL_ThemeProvider* pTheme,
-                    const CFX_Matrix* pMatrix);
-  void DrawDatesInCircle(CXFA_Graphics* pGraphics,
-                         IFWL_ThemeProvider* pTheme,
-                         const CFX_Matrix* pMatrix);
+  CFWL_MonthCalendar(CFWL_App* app,
+                     const Properties& properties,
+                     CFWL_Widget* pOuter);
+
+  void DrawBackground(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawHeadBK(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawLButton(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawRButton(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawCaption(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawSeparator(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawDatesInBK(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawWeek(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawToday(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawDatesIn(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawDatesOut(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawDatesInCircle(CFGAS_GEGraphics* pGraphics,
+                         const CFX_Matrix& mtMatrix);
   CFX_SizeF CalcSize();
   void Layout();
   void CalcHeadSize();
   void CalcTodaySize();
   void CalDateItem();
-  void GetCapValue();
   void InitDate();
   void ClearDateItem();
   void ResetDateItem();
@@ -149,33 +128,33 @@
   void OnMouseLeave(CFWL_MessageMouse* pMsg);
 
   bool m_bInitialized = false;
-  CFX_RectF m_rtHead;
-  CFX_RectF m_rtWeek;
-  CFX_RectF m_rtLBtn;
-  CFX_RectF m_rtRBtn;
-  CFX_RectF m_rtDates;
-  CFX_RectF m_rtHSep;
-  CFX_RectF m_rtHeadText;
-  CFX_RectF m_rtToday;
-  CFX_RectF m_rtTodayFlag;
+  CFX_RectF m_HeadRect;
+  CFX_RectF m_WeekRect;
+  CFX_RectF m_LBtnRect;
+  CFX_RectF m_RBtnRect;
+  CFX_RectF m_DatesRect;
+  CFX_RectF m_HSepRect;
+  CFX_RectF m_HeadTextRect;
+  CFX_RectF m_TodayRect;
+  CFX_RectF m_TodayFlagRect;
   WideString m_wsHead;
   WideString m_wsToday;
-  std::vector<std::unique_ptr<DATEINFO>> m_arrDates;
+  std::vector<std::unique_ptr<DATEINFO>> m_DateArray;
   int32_t m_iCurYear = 2011;
   int32_t m_iCurMonth = 1;
   int32_t m_iYear = 2011;
   int32_t m_iMonth = 1;
   int32_t m_iDay = 1;
   int32_t m_iHovered = -1;
-  int32_t m_iLBtnPartStates = CFWL_PartState_Normal;
-  int32_t m_iRBtnPartStates = CFWL_PartState_Normal;
+  Mask<CFWL_PartState> m_iLBtnPartStates = CFWL_PartState::kNormal;
+  Mask<CFWL_PartState> m_iRBtnPartStates = CFWL_PartState::kNormal;
   DATE m_dtMin;
   DATE m_dtMax;
-  CFX_SizeF m_szHead;
-  CFX_SizeF m_szCell;
-  CFX_SizeF m_szToday;
-  std::vector<int32_t> m_arrSelDays;
-  CFX_RectF m_rtClient;
+  CFX_SizeF m_HeadSize;
+  CFX_SizeF m_CellSize;
+  CFX_SizeF m_TodaySize;
+  std::vector<int32_t> m_SelDayArray;
+  CFX_RectF m_ClientRect;
 };
 
 #endif  // XFA_FWL_CFWL_MONTHCALENDAR_H_
diff --git a/xfa/fwl/cfwl_notedriver.cpp b/xfa/fwl/cfwl_notedriver.cpp
index 821dd5c..f3506d6 100644
--- a/xfa/fwl/cfwl_notedriver.cpp
+++ b/xfa/fwl/cfwl_notedriver.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,10 +11,9 @@
 
 #include "build/build_config.h"
 #include "core/fxcrt/fx_extension.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "fxjs/gc/container_trace.h"
 #include "xfa/fwl/cfwl_app.h"
-#include "xfa/fwl/cfwl_eventtarget.h"
+#include "xfa/fwl/cfwl_event.h"
 #include "xfa/fwl/cfwl_messagekey.h"
 #include "xfa/fwl/cfwl_messagekillfocus.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
@@ -23,10 +22,24 @@
 #include "xfa/fwl/cfwl_widgetmgr.h"
 #include "xfa/fwl/fwl_widgetdef.h"
 
-CFWL_NoteDriver::CFWL_NoteDriver() = default;
+namespace {
+
+uint64_t g_next_listener_key = 1;
+
+}  // namespace
+
+CFWL_NoteDriver::CFWL_NoteDriver(CFWL_App* pApp) : m_pApp(pApp) {}
 
 CFWL_NoteDriver::~CFWL_NoteDriver() = default;
 
+void CFWL_NoteDriver::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pApp);
+  ContainerTrace(visitor, m_eventTargets);
+  visitor->Trace(m_pHover);
+  visitor->Trace(m_pFocus);
+  visitor->Trace(m_pGrab);
+}
+
 void CFWL_NoteDriver::SendEvent(CFWL_Event* pNote) {
   for (const auto& pair : m_eventTargets) {
     if (pair.second->IsValid())
@@ -36,48 +49,26 @@
 
 void CFWL_NoteDriver::RegisterEventTarget(CFWL_Widget* pListener,
                                           CFWL_Widget* pEventSource) {
-  uint32_t key = pListener->GetEventKey();
+  uint64_t key = pListener->GetEventKey();
   if (key == 0) {
-    do {
-      key = rand();
-    } while (key == 0 || pdfium::ContainsKey(m_eventTargets, key));
+    key = g_next_listener_key++;
     pListener->SetEventKey(key);
   }
-  if (!m_eventTargets[key])
-    m_eventTargets[key] = pdfium::MakeUnique<CFWL_EventTarget>(pListener);
-
+  if (!m_eventTargets[key]) {
+    m_eventTargets[key] = cppgc::MakeGarbageCollected<Target>(
+        m_pApp->GetHeap()->GetAllocationHandle(), pListener);
+  }
   m_eventTargets[key]->SetEventSource(pEventSource);
 }
 
 void CFWL_NoteDriver::UnregisterEventTarget(CFWL_Widget* pListener) {
-  uint32_t key = pListener->GetEventKey();
+  uint64_t key = pListener->GetEventKey();
   if (key == 0)
     return;
 
   auto it = m_eventTargets.find(key);
   if (it != m_eventTargets.end())
-    it->second->FlagInvalid();
-}
-
-bool CFWL_NoteDriver::SetFocus(CFWL_Widget* pFocus) {
-  if (m_pFocus == pFocus)
-    return true;
-
-  CFWL_Widget* pPrev = m_pFocus.Get();
-  m_pFocus = pFocus;
-  if (pPrev) {
-    if (IFWL_WidgetDelegate* pDelegate = pPrev->GetDelegate()) {
-      CFWL_MessageKillFocus ms(pPrev, pPrev);
-      pDelegate->OnProcessMessage(&ms);
-    }
-  }
-  if (pFocus) {
-    if (IFWL_WidgetDelegate* pDelegate = pFocus->GetDelegate()) {
-      CFWL_MessageSetFocus ms(nullptr, pFocus);
-      pDelegate->OnProcessMessage(&ms);
-    }
-  }
-  return true;
+    it->second->Invalidate();
 }
 
 void CFWL_NoteDriver::NotifyTargetHide(CFWL_Widget* pNoteTarget) {
@@ -100,48 +91,46 @@
   UnregisterEventTarget(pNoteTarget);
 }
 
-void CFWL_NoteDriver::ProcessMessage(std::unique_ptr<CFWL_Message> pMessage) {
+void CFWL_NoteDriver::ProcessMessage(CFWL_Message* pMessage) {
   CFWL_Widget* pMessageForm = pMessage->GetDstTarget();
   if (!pMessageForm)
     return;
 
-  if (!DispatchMessage(pMessage.get(), pMessageForm))
+  if (!DispatchMessage(pMessage, pMessageForm))
     return;
 
-  if (pMessage->GetType() == CFWL_Message::Type::Mouse)
-    MouseSecondary(pMessage.get());
+  if (pMessage->GetType() == CFWL_Message::Type::kMouse)
+    MouseSecondary(pMessage);
 }
 
 bool CFWL_NoteDriver::DispatchMessage(CFWL_Message* pMessage,
                                       CFWL_Widget* pMessageForm) {
   switch (pMessage->GetType()) {
-    case CFWL_Message::Type::SetFocus: {
+    case CFWL_Message::Type::kSetFocus: {
       if (!DoSetFocus(pMessage, pMessageForm))
         return false;
       break;
     }
-    case CFWL_Message::Type::KillFocus: {
+    case CFWL_Message::Type::kKillFocus: {
       if (!DoKillFocus(pMessage, pMessageForm))
         return false;
       break;
     }
-    case CFWL_Message::Type::Key: {
+    case CFWL_Message::Type::kKey: {
       if (!DoKey(pMessage, pMessageForm))
         return false;
       break;
     }
-    case CFWL_Message::Type::Mouse: {
+    case CFWL_Message::Type::kMouse: {
       if (!DoMouse(pMessage, pMessageForm))
         return false;
       break;
     }
-    case CFWL_Message::Type::MouseWheel: {
+    case CFWL_Message::Type::kMouseWheel: {
       if (!DoWheel(pMessage, pMessageForm))
         return false;
       break;
     }
-    default:
-      break;
   }
   IFWL_WidgetDelegate* pDelegate = pMessage->GetDstTarget()->GetDelegate();
   if (pDelegate)
@@ -165,25 +154,9 @@
 
 bool CFWL_NoteDriver::DoKey(CFWL_Message* pMessage, CFWL_Widget* pMessageForm) {
   CFWL_MessageKey* pMsg = static_cast<CFWL_MessageKey*>(pMessage);
-#if !defined(OS_MACOSX)
-  if (pMsg->m_dwCmd == FWL_KeyCommand::KeyDown &&
-      pMsg->m_dwKeyCode == XFA_FWL_VKEY_Tab) {
-    CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetOwnerApp()->GetWidgetMgr();
-    CFWL_Widget* pForm = GetMessageForm(pMsg->GetDstTarget());
-    CFWL_Widget* pFocus = m_pFocus.Get();
-    if (m_pFocus && pWidgetMgr->GetSystemFormWidget(m_pFocus.Get()) != pForm)
-      pFocus = nullptr;
-
-    CFWL_Widget* pNextTabStop = nullptr;
-    if (pForm) {
-      pNextTabStop = CFWL_WidgetMgr::NextTab(pForm, pFocus);
-      if (!pNextTabStop)
-        pNextTabStop = CFWL_WidgetMgr::NextTab(pForm, nullptr);
-    }
-    if (pNextTabStop == pFocus)
-      return true;
-    if (pNextTabStop)
-      SetFocus(pNextTabStop);
+#if !BUILDFLAG(IS_APPLE)
+  if (pMsg->m_dwCmd == CFWL_MessageKey::KeyCommand::kKeyDown &&
+      pMsg->m_dwKeyCodeOrChar == XFA_FWL_VKEY_Tab) {
     return true;
   }
 #endif
@@ -193,9 +166,9 @@
     return true;
   }
 
-  if (pMsg->m_dwCmd == FWL_KeyCommand::KeyDown &&
-      pMsg->m_dwKeyCode == XFA_FWL_VKEY_Return) {
-    CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetOwnerApp()->GetWidgetMgr();
+  if (pMsg->m_dwCmd == CFWL_MessageKey::KeyCommand::kKeyDown &&
+      pMsg->m_dwKeyCodeOrChar == XFA_FWL_VKEY_Return) {
+    CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetFWLApp()->GetWidgetMgr();
     CFWL_Widget* pDefButton = pWidgetMgr->GetDefaultButton(pMessageForm);
     if (pDefButton) {
       pMsg->SetDstTarget(pDefButton);
@@ -208,9 +181,9 @@
 bool CFWL_NoteDriver::DoMouse(CFWL_Message* pMessage,
                               CFWL_Widget* pMessageForm) {
   CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
-  if (pMsg->m_dwCmd == FWL_MouseCommand::Leave ||
-      pMsg->m_dwCmd == FWL_MouseCommand::Hover ||
-      pMsg->m_dwCmd == FWL_MouseCommand::Enter) {
+  if (pMsg->m_dwCmd == CFWL_MessageMouse::MouseCommand::kLeave ||
+      pMsg->m_dwCmd == CFWL_MessageMouse::MouseCommand::kHover ||
+      pMsg->m_dwCmd == CFWL_MessageMouse::MouseCommand::kEnter) {
     return !!pMsg->GetDstTarget();
   }
   if (pMsg->GetDstTarget() != pMessageForm)
@@ -222,20 +195,20 @@
 
 bool CFWL_NoteDriver::DoWheel(CFWL_Message* pMessage,
                               CFWL_Widget* pMessageForm) {
-  CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetOwnerApp()->GetWidgetMgr();
+  CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetFWLApp()->GetWidgetMgr();
   CFWL_MessageMouseWheel* pMsg = static_cast<CFWL_MessageMouseWheel*>(pMessage);
-  CFWL_Widget* pDst = pWidgetMgr->GetWidgetAtPoint(pMessageForm, pMsg->m_pos);
+  CFWL_Widget* pDst = pWidgetMgr->GetWidgetAtPoint(pMessageForm, pMsg->pos());
   if (!pDst)
     return false;
 
-  pMsg->m_pos = pMessageForm->TransformTo(pDst, pMsg->m_pos);
+  pMsg->set_pos(pMessageForm->TransformTo(pDst, pMsg->pos()));
   pMsg->SetDstTarget(pDst);
   return true;
 }
 
 bool CFWL_NoteDriver::DoMouseEx(CFWL_Message* pMessage,
                                 CFWL_Widget* pMessageForm) {
-  CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetOwnerApp()->GetWidgetMgr();
+  CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetFWLApp()->GetWidgetMgr();
   CFWL_Widget* pTarget = nullptr;
   if (m_pGrab)
     pTarget = m_pGrab.Get();
@@ -260,7 +233,8 @@
   CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
   if (m_pHover) {
     CFWL_MessageMouse msLeave(
-        m_pHover.Get(), FWL_MouseCommand::Leave, 0,
+        m_pHover.Get(), CFWL_MessageMouse::MouseCommand::kLeave,
+        Mask<XFA_FWL_KeyFlag>(),
         pTarget->TransformTo(m_pHover.Get(), pMsg->m_pos));
     DispatchMessage(&msLeave, nullptr);
   }
@@ -270,23 +244,33 @@
   }
   m_pHover = pTarget;
 
-  CFWL_MessageMouse msHover(pTarget, FWL_MouseCommand::Hover, 0, pMsg->m_pos);
+  CFWL_MessageMouse msHover(pTarget, CFWL_MessageMouse::MouseCommand::kHover,
+                            Mask<XFA_FWL_KeyFlag>(), pMsg->m_pos);
   DispatchMessage(&msHover, nullptr);
 }
 
-CFWL_Widget* CFWL_NoteDriver::GetMessageForm(CFWL_Widget* pDstTarget) {
-  if (!pDstTarget)
-    return nullptr;
+CFWL_NoteDriver::Target::Target(CFWL_Widget* pListener)
+    : m_pListener(pListener) {}
 
-  CFWL_WidgetMgr* pWidgetMgr = pDstTarget->GetOwnerApp()->GetWidgetMgr();
-  return pWidgetMgr->GetSystemFormWidget(pDstTarget);
+CFWL_NoteDriver::Target::~Target() = default;
+
+void CFWL_NoteDriver::Target::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pListener);
+  for (auto& widget : m_widgets)
+    visitor->Trace(widget);
 }
 
-void CFWL_NoteDriver::ClearEventTargets() {
-  auto it = m_eventTargets.begin();
-  while (it != m_eventTargets.end()) {
-    auto old = it++;
-    if (!old->second->IsValid())
-      m_eventTargets.erase(old);
-  }
+void CFWL_NoteDriver::Target::SetEventSource(CFWL_Widget* pSource) {
+  if (pSource)
+    m_widgets.insert(pSource);
+}
+
+bool CFWL_NoteDriver::Target::ProcessEvent(CFWL_Event* pEvent) {
+  IFWL_WidgetDelegate* pDelegate = m_pListener->GetDelegate();
+  if (!pDelegate)
+    return false;
+  if (!m_widgets.empty() && m_widgets.count(pEvent->GetSrcTarget()) == 0)
+    return false;
+  pDelegate->OnProcessEvent(pEvent);
+  return true;
 }
diff --git a/xfa/fwl/cfwl_notedriver.h b/xfa/fwl/cfwl_notedriver.h
index 1de761b..00fd961 100644
--- a/xfa/fwl/cfwl_notedriver.h
+++ b/xfa/fwl/cfwl_notedriver.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,39 +9,52 @@
 
 #include <map>
 #include <memory>
-#include <vector>
+#include <set>
 
-#include "core/fxcrt/unowned_ptr.h"
-#include "xfa/fwl/cfwl_event.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fxgraphics/cxfa_graphics.h"
 
-class CFWL_EventTarget;
-class CFWL_TargetImp;
-class CFWL_Widget;
+class CFWL_Event;
 
-class CFWL_NoteDriver {
+class CFWL_NoteDriver final : public cppgc::GarbageCollected<CFWL_NoteDriver> {
  public:
-  CFWL_NoteDriver();
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_NoteDriver();
 
-  void SendEvent(CFWL_Event* pNote);
+  void Trace(cppgc::Visitor* visitor) const;
 
+  void SendEvent(CFWL_Event* pNote);
+  void ProcessMessage(CFWL_Message* pMessage);
   void RegisterEventTarget(CFWL_Widget* pListener, CFWL_Widget* pEventSource);
   void UnregisterEventTarget(CFWL_Widget* pListener);
-  void ClearEventTargets();
-
-  CFWL_Widget* GetFocus() const { return m_pFocus.Get(); }
-  bool SetFocus(CFWL_Widget* pFocus);
-  void SetGrab(CFWL_Widget* pGrab, bool bSet) {
-    m_pGrab = bSet ? pGrab : nullptr;
-  }
-
   void NotifyTargetHide(CFWL_Widget* pNoteTarget);
   void NotifyTargetDestroy(CFWL_Widget* pNoteTarget);
-  void ProcessMessage(std::unique_ptr<CFWL_Message> pMessage);
+  void SetGrab(CFWL_Widget* pGrab) { m_pGrab = pGrab; }
 
  private:
+  class Target : public cppgc::GarbageCollected<Target> {
+   public:
+    explicit Target(CFWL_Widget* pListener);
+    ~Target();
+
+    void Trace(cppgc::Visitor* visitor) const;
+    void SetEventSource(CFWL_Widget* pSource);
+    bool ProcessEvent(CFWL_Event* pEvent);
+    bool IsValid() const { return m_bValid; }
+    void Invalidate() { m_bValid = false; }
+
+   private:
+    bool m_bValid = true;
+    cppgc::Member<CFWL_Widget> const m_pListener;
+    std::set<cppgc::Member<CFWL_Widget>> m_widgets;
+  };
+
+  explicit CFWL_NoteDriver(CFWL_App* pApp);
+
   bool DispatchMessage(CFWL_Message* pMessage, CFWL_Widget* pMessageForm);
   bool DoSetFocus(CFWL_Message* pMsg, CFWL_Widget* pMessageForm);
   bool DoKillFocus(CFWL_Message* pMsg, CFWL_Widget* pMessageForm);
@@ -50,12 +63,12 @@
   bool DoWheel(CFWL_Message* pMsg, CFWL_Widget* pMessageForm);
   bool DoMouseEx(CFWL_Message* pMsg, CFWL_Widget* pMessageForm);
   void MouseSecondary(CFWL_Message* pMsg);
-  CFWL_Widget* GetMessageForm(CFWL_Widget* pDstTarget);
 
-  std::map<uint32_t, std::unique_ptr<CFWL_EventTarget>> m_eventTargets;
-  UnownedPtr<CFWL_Widget> m_pHover;
-  UnownedPtr<CFWL_Widget> m_pFocus;
-  UnownedPtr<CFWL_Widget> m_pGrab;
+  cppgc::Member<CFWL_App> m_pApp;
+  std::map<uint64_t, cppgc::Member<Target>> m_eventTargets;
+  cppgc::Member<CFWL_Widget> m_pHover;
+  cppgc::Member<CFWL_Widget> m_pFocus;
+  cppgc::Member<CFWL_Widget> m_pGrab;
 };
 
 #endif  // XFA_FWL_CFWL_NOTEDRIVER_H_
diff --git a/xfa/fwl/cfwl_picturebox.cpp b/xfa/fwl/cfwl_picturebox.cpp
index 6e90765..6e864db 100644
--- a/xfa/fwl/cfwl_picturebox.cpp
+++ b/xfa/fwl/cfwl_picturebox.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,15 +6,10 @@
 
 #include "xfa/fwl/cfwl_picturebox.h"
 
-#include <memory>
+CFWL_PictureBox::CFWL_PictureBox(CFWL_App* app)
+    : CFWL_Widget(app, CFWL_Widget::Properties(), nullptr) {}
 
-#include "third_party/base/ptr_util.h"
-
-CFWL_PictureBox::CFWL_PictureBox(const CFWL_App* app)
-    : CFWL_Widget(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr) {
-}
-
-CFWL_PictureBox::~CFWL_PictureBox() {}
+CFWL_PictureBox::~CFWL_PictureBox() = default;
 
 FWL_Type CFWL_PictureBox::GetClassID() const {
   return FWL_Type::PictureBox;
@@ -23,25 +18,20 @@
 void CFWL_PictureBox::Update() {
   if (IsLocked())
     return;
-  if (!m_pProperties->m_pThemeProvider)
-    m_pProperties->m_pThemeProvider = GetAvailableTheme();
 
-  m_rtClient = GetClientRect();
+  m_ClientRect = GetClientRect();
 }
 
-void CFWL_PictureBox::DrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_PictureBox::DrawWidget(CFGAS_GEGraphics* pGraphics,
                                  const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
-  if (!m_pProperties->m_pThemeProvider)
-    return;
 
-  IFWL_ThemeProvider* pTheme = GetAvailableTheme();
   if (HasBorder())
-    DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
+    DrawBorder(pGraphics, CFWL_ThemePart::Part::kBorder, matrix);
 }
 
-void CFWL_PictureBox::OnDrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_PictureBox::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                                    const CFX_Matrix& matrix) {
   DrawWidget(pGraphics, matrix);
 }
diff --git a/xfa/fwl/cfwl_picturebox.h b/xfa/fwl/cfwl_picturebox.h
index 11689d2..944bcb2 100644
--- a/xfa/fwl/cfwl_picturebox.h
+++ b/xfa/fwl/cfwl_picturebox.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,26 +8,25 @@
 #define XFA_FWL_CFWL_PICTUREBOX_H_
 
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fwl/cfwl_widgetproperties.h"
-
-class CFX_DIBitmap;
-class CFWL_Widget;
 
 class CFWL_PictureBox final : public CFWL_Widget {
  public:
-  explicit CFWL_PictureBox(const CFWL_App* pApp);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_PictureBox() override;
 
   // CFWL_Widget
   FWL_Type GetClassID() const override;
   void Update() override;
-  void DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
+  void DrawWidget(CFGAS_GEGraphics* pGraphics,
+                  const CFX_Matrix& matrix) override;
+  void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix) override;
 
  private:
-  CFX_RectF m_rtClient;
-  CFX_RectF m_rtImage;
+  explicit CFWL_PictureBox(CFWL_App* pApp);
+
+  CFX_RectF m_ClientRect;
+  CFX_RectF m_ImageRect;
   CFX_Matrix m_matrix;
 };
 
diff --git a/xfa/fwl/cfwl_pushbutton.cpp b/xfa/fwl/cfwl_pushbutton.cpp
index 7806399..715b420 100644
--- a/xfa/fwl/cfwl_pushbutton.cpp
+++ b/xfa/fwl/cfwl_pushbutton.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,6 @@
 
 #include "xfa/fwl/cfwl_pushbutton.h"
 
-#include <memory>
-#include <utility>
-
-#include "third_party/base/ptr_util.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fwl/cfwl_event.h"
 #include "xfa/fwl/cfwl_eventmouse.h"
@@ -21,18 +17,18 @@
 #include "xfa/fwl/fwl_widgetdef.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
 
-CFWL_PushButton::CFWL_PushButton(const CFWL_App* app)
-    : CFWL_Widget(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr) {}
+CFWL_PushButton::CFWL_PushButton(CFWL_App* app)
+    : CFWL_Widget(app, Properties(), nullptr) {}
 
-CFWL_PushButton::~CFWL_PushButton() {}
+CFWL_PushButton::~CFWL_PushButton() = default;
 
 FWL_Type CFWL_PushButton::GetClassID() const {
   return FWL_Type::PushButton;
 }
 
 void CFWL_PushButton::SetStates(uint32_t dwStates) {
-  if (dwStates & FWL_WGTSTATE_Disabled) {
-    m_pProperties->m_dwStates = FWL_WGTSTATE_Disabled;
+  if (dwStates & FWL_STATE_WGT_Disabled) {
+    m_Properties.m_dwStates = FWL_STATE_WGT_Disabled;
     return;
   }
   CFWL_Widget::SetStates(dwStates);
@@ -41,82 +37,71 @@
 void CFWL_PushButton::Update() {
   if (IsLocked())
     return;
-  if (!m_pProperties->m_pThemeProvider)
-    m_pProperties->m_pThemeProvider = GetAvailableTheme();
 
-  m_rtClient = GetClientRect();
-  m_rtCaption = m_rtClient;
+  m_ClientRect = GetClientRect();
+  m_CaptionRect = m_ClientRect;
 }
 
-void CFWL_PushButton::DrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_PushButton::DrawWidget(CFGAS_GEGraphics* pGraphics,
                                  const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
 
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
-  if (!pTheme)
-    return;
-
   if (HasBorder())
-    DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
-  DrawBkground(pGraphics, pTheme, &matrix);
+    DrawBorder(pGraphics, CFWL_ThemePart::Part::kBorder, matrix);
+
+  DrawBkground(pGraphics, matrix);
 }
 
-void CFWL_PushButton::DrawBkground(CXFA_Graphics* pGraphics,
-                                   IFWL_ThemeProvider* pTheme,
-                                   const CFX_Matrix* pMatrix) {
-  CFWL_ThemeBackground param;
-  param.m_pWidget = this;
-  param.m_iPart = CFWL_Part::Background;
+void CFWL_PushButton::DrawBkground(CFGAS_GEGraphics* pGraphics,
+                                   const CFX_Matrix& matrix) {
+  CFWL_ThemeBackground param(CFWL_ThemePart::Part::kBackground, this,
+                             pGraphics);
   param.m_dwStates = GetPartStates();
-  param.m_pGraphics = pGraphics;
-  if (pMatrix)
-    param.m_matrix.Concat(*pMatrix);
-  param.m_rtPart = m_rtClient;
-  if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused)
-    param.m_pRtData = &m_rtCaption;
-  pTheme->DrawBackground(param);
+  param.m_matrix = matrix;
+  param.m_PartRect = m_ClientRect;
+  if (m_Properties.m_dwStates & FWL_STATE_WGT_Focused)
+    param.m_pRtData = &m_CaptionRect;
+  GetThemeProvider()->DrawBackground(param);
 }
 
-uint32_t CFWL_PushButton::GetPartStates() {
-  uint32_t dwStates = CFWL_PartState_Normal;
-  if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused)
-    dwStates |= CFWL_PartState_Focused;
-  if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
-    dwStates = CFWL_PartState_Disabled;
-  else if (m_pProperties->m_dwStates & FWL_STATE_PSB_Pressed)
-    dwStates |= CFWL_PartState_Pressed;
-  else if (m_pProperties->m_dwStates & FWL_STATE_PSB_Hovered)
-    dwStates |= CFWL_PartState_Hovered;
+Mask<CFWL_PartState> CFWL_PushButton::GetPartStates() {
+  Mask<CFWL_PartState> dwStates = CFWL_PartState::kNormal;
+  if (m_Properties.m_dwStates & FWL_STATE_WGT_Focused)
+    dwStates |= CFWL_PartState::kFocused;
+  if (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
+    dwStates = CFWL_PartState::kDisabled;
+  else if (m_Properties.m_dwStates & FWL_STATE_PSB_Pressed)
+    dwStates |= CFWL_PartState::kPressed;
+  else if (m_Properties.m_dwStates & FWL_STATE_PSB_Hovered)
+    dwStates |= CFWL_PartState::kHovered;
   return dwStates;
 }
 
 void CFWL_PushButton::OnProcessMessage(CFWL_Message* pMessage) {
-  if (!pMessage)
-    return;
   if (!IsEnabled())
     return;
 
   switch (pMessage->GetType()) {
-    case CFWL_Message::Type::SetFocus:
-      OnFocusChanged(pMessage, true);
+    case CFWL_Message::Type::kSetFocus:
+      OnFocusGained();
       break;
-    case CFWL_Message::Type::KillFocus:
-      OnFocusChanged(pMessage, false);
+    case CFWL_Message::Type::kKillFocus:
+      OnFocusLost();
       break;
-    case CFWL_Message::Type::Mouse: {
+    case CFWL_Message::Type::kMouse: {
       CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
       switch (pMsg->m_dwCmd) {
-        case FWL_MouseCommand::LeftButtonDown:
+        case CFWL_MessageMouse::MouseCommand::kLeftButtonDown:
           OnLButtonDown(pMsg);
           break;
-        case FWL_MouseCommand::LeftButtonUp:
+        case CFWL_MessageMouse::MouseCommand::kLeftButtonUp:
           OnLButtonUp(pMsg);
           break;
-        case FWL_MouseCommand::Move:
+        case CFWL_MessageMouse::MouseCommand::kMove:
           OnMouseMove(pMsg);
           break;
-        case FWL_MouseCommand::Leave:
+        case CFWL_MessageMouse::MouseCommand::kLeave:
           OnMouseLeave(pMsg);
           break;
         default:
@@ -124,9 +109,9 @@
       }
       break;
     }
-    case CFWL_Message::Type::Key: {
+    case CFWL_Message::Type::kKey: {
       CFWL_MessageKey* pKey = static_cast<CFWL_MessageKey*>(pMessage);
-      if (pKey->m_dwCmd == FWL_KeyCommand::KeyDown)
+      if (pKey->m_dwCmd == CFWL_MessageKey::KeyCommand::kKeyDown)
         OnKeyDown(pKey);
       break;
     }
@@ -138,90 +123,91 @@
     CFWL_Widget::OnProcessMessage(pMessage);
 }
 
-void CFWL_PushButton::OnDrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_PushButton::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                                    const CFX_Matrix& matrix) {
   DrawWidget(pGraphics, matrix);
 }
 
-void CFWL_PushButton::OnFocusChanged(CFWL_Message* pMsg, bool bSet) {
-  if (bSet)
-    m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused;
-  else
-    m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused;
+void CFWL_PushButton::OnFocusGained() {
+  m_Properties.m_dwStates |= FWL_STATE_WGT_Focused;
+  RepaintRect(m_ClientRect);
+}
 
-  RepaintRect(m_rtClient);
+void CFWL_PushButton::OnFocusLost() {
+  m_Properties.m_dwStates &= ~FWL_STATE_WGT_Focused;
+  RepaintRect(m_ClientRect);
 }
 
 void CFWL_PushButton::OnLButtonDown(CFWL_MessageMouse* pMsg) {
   m_bBtnDown = true;
-  m_pProperties->m_dwStates |= FWL_STATE_PSB_Hovered;
-  m_pProperties->m_dwStates |= FWL_STATE_PSB_Pressed;
-  RepaintRect(m_rtClient);
+  m_Properties.m_dwStates |= FWL_STATE_PSB_Hovered;
+  m_Properties.m_dwStates |= FWL_STATE_PSB_Pressed;
+  RepaintRect(m_ClientRect);
 }
 
 void CFWL_PushButton::OnLButtonUp(CFWL_MessageMouse* pMsg) {
   m_bBtnDown = false;
-  if (m_rtClient.Contains(pMsg->m_pos)) {
-    m_pProperties->m_dwStates &= ~FWL_STATE_PSB_Pressed;
-    m_pProperties->m_dwStates |= FWL_STATE_PSB_Hovered;
+  if (m_ClientRect.Contains(pMsg->m_pos)) {
+    m_Properties.m_dwStates &= ~FWL_STATE_PSB_Pressed;
+    m_Properties.m_dwStates |= FWL_STATE_PSB_Hovered;
   } else {
-    m_pProperties->m_dwStates &= ~FWL_STATE_PSB_Hovered;
-    m_pProperties->m_dwStates &= ~FWL_STATE_PSB_Pressed;
+    m_Properties.m_dwStates &= ~FWL_STATE_PSB_Hovered;
+    m_Properties.m_dwStates &= ~FWL_STATE_PSB_Pressed;
   }
-  if (m_rtClient.Contains(pMsg->m_pos)) {
+  if (m_ClientRect.Contains(pMsg->m_pos)) {
     CFWL_Event wmClick(CFWL_Event::Type::Click, this);
     DispatchEvent(&wmClick);
   }
-  RepaintRect(m_rtClient);
+  RepaintRect(m_ClientRect);
 }
 
 void CFWL_PushButton::OnMouseMove(CFWL_MessageMouse* pMsg) {
   bool bRepaint = false;
   if (m_bBtnDown) {
-    if (m_rtClient.Contains(pMsg->m_pos)) {
-      if ((m_pProperties->m_dwStates & FWL_STATE_PSB_Pressed) == 0) {
-        m_pProperties->m_dwStates |= FWL_STATE_PSB_Pressed;
+    if (m_ClientRect.Contains(pMsg->m_pos)) {
+      if ((m_Properties.m_dwStates & FWL_STATE_PSB_Pressed) == 0) {
+        m_Properties.m_dwStates |= FWL_STATE_PSB_Pressed;
         bRepaint = true;
       }
-      if (m_pProperties->m_dwStates & FWL_STATE_PSB_Hovered) {
-        m_pProperties->m_dwStates &= ~FWL_STATE_PSB_Hovered;
+      if (m_Properties.m_dwStates & FWL_STATE_PSB_Hovered) {
+        m_Properties.m_dwStates &= ~FWL_STATE_PSB_Hovered;
         bRepaint = true;
       }
     } else {
-      if (m_pProperties->m_dwStates & FWL_STATE_PSB_Pressed) {
-        m_pProperties->m_dwStates &= ~FWL_STATE_PSB_Pressed;
+      if (m_Properties.m_dwStates & FWL_STATE_PSB_Pressed) {
+        m_Properties.m_dwStates &= ~FWL_STATE_PSB_Pressed;
         bRepaint = true;
       }
-      if ((m_pProperties->m_dwStates & FWL_STATE_PSB_Hovered) == 0) {
-        m_pProperties->m_dwStates |= FWL_STATE_PSB_Hovered;
+      if ((m_Properties.m_dwStates & FWL_STATE_PSB_Hovered) == 0) {
+        m_Properties.m_dwStates |= FWL_STATE_PSB_Hovered;
         bRepaint = true;
       }
     }
   } else {
-    if (!m_rtClient.Contains(pMsg->m_pos))
+    if (!m_ClientRect.Contains(pMsg->m_pos))
       return;
-    if ((m_pProperties->m_dwStates & FWL_STATE_PSB_Hovered) == 0) {
-      m_pProperties->m_dwStates |= FWL_STATE_PSB_Hovered;
+    if ((m_Properties.m_dwStates & FWL_STATE_PSB_Hovered) == 0) {
+      m_Properties.m_dwStates |= FWL_STATE_PSB_Hovered;
       bRepaint = true;
     }
   }
   if (bRepaint)
-    RepaintRect(m_rtClient);
+    RepaintRect(m_ClientRect);
 }
 
 void CFWL_PushButton::OnMouseLeave(CFWL_MessageMouse* pMsg) {
   m_bBtnDown = false;
-  m_pProperties->m_dwStates &= ~FWL_STATE_PSB_Hovered;
-  m_pProperties->m_dwStates &= ~FWL_STATE_PSB_Pressed;
-  RepaintRect(m_rtClient);
+  m_Properties.m_dwStates &= ~FWL_STATE_PSB_Hovered;
+  m_Properties.m_dwStates &= ~FWL_STATE_PSB_Pressed;
+  RepaintRect(m_ClientRect);
 }
 
 void CFWL_PushButton::OnKeyDown(CFWL_MessageKey* pMsg) {
-  if (pMsg->m_dwKeyCode != XFA_FWL_VKEY_Return)
+  if (pMsg->m_dwKeyCodeOrChar != XFA_FWL_VKEY_Return)
     return;
 
-  CFWL_EventMouse wmMouse(this);
-  wmMouse.m_dwCmd = FWL_MouseCommand::LeftButtonUp;
+  CFWL_EventMouse wmMouse(this, nullptr,
+                          CFWL_MessageMouse::MouseCommand::kLeftButtonUp);
   DispatchEvent(&wmMouse);
   if (!wmMouse.GetSrcTarget())
     return;
diff --git a/xfa/fwl/cfwl_pushbutton.h b/xfa/fwl/cfwl_pushbutton.h
index 925dbb6..412e5bc 100644
--- a/xfa/fwl/cfwl_pushbutton.h
+++ b/xfa/fwl/cfwl_pushbutton.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,36 +8,37 @@
 #define XFA_FWL_CFWL_PUSHBUTTON_H_
 
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fwl/cfwl_widgetproperties.h"
 
-#define FWL_STATE_PSB_Hovered (1 << FWL_WGTSTATE_MAX)
-#define FWL_STATE_PSB_Pressed (1 << (FWL_WGTSTATE_MAX + 1))
+#define FWL_STATE_PSB_Hovered (1 << FWL_STATE_WGT_MAX)
+#define FWL_STATE_PSB_Pressed (1 << (FWL_STATE_WGT_MAX + 1))
+#define FWL_STATE_PSB_Default (1 << (FWL_STATE_WGT_MAX + 2))
 
+class CFWL_MessageKey;
 class CFWL_MessageMouse;
-class CFX_DIBitmap;
-class CFWL_Widget;
 
 class CFWL_PushButton final : public CFWL_Widget {
  public:
-  explicit CFWL_PushButton(const CFWL_App*);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_PushButton() override;
 
   // CFWL_Widget
   FWL_Type GetClassID() const override;
   void SetStates(uint32_t dwStates) override;
   void Update() override;
-  void DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) override;
+  void DrawWidget(CFGAS_GEGraphics* pGraphics,
+                  const CFX_Matrix& matrix) override;
   void OnProcessMessage(CFWL_Message* pMessage) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
+  void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix) override;
 
  private:
-  void DrawBkground(CXFA_Graphics* pGraphics,
-                    IFWL_ThemeProvider* pTheme,
-                    const CFX_Matrix* pMatrix);
-  uint32_t GetPartStates();
+  explicit CFWL_PushButton(CFWL_App* pApp);
+
+  void DrawBkground(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  Mask<CFWL_PartState> GetPartStates();
   void UpdateTextOutStyles();
-  void OnFocusChanged(CFWL_Message* pMsg, bool bSet);
+  void OnFocusGained();
+  void OnFocusLost();
   void OnLButtonDown(CFWL_MessageMouse* pMsg);
   void OnLButtonUp(CFWL_MessageMouse* pMsg);
   void OnMouseMove(CFWL_MessageMouse* pMsg);
@@ -45,8 +46,8 @@
   void OnKeyDown(CFWL_MessageKey* pMsg);
 
   bool m_bBtnDown = false;
-  CFX_RectF m_rtClient;
-  CFX_RectF m_rtCaption;
+  CFX_RectF m_ClientRect;
+  CFX_RectF m_CaptionRect;
 };
 
 #endif  // XFA_FWL_CFWL_PUSHBUTTON_H_
diff --git a/xfa/fwl/cfwl_scrollbar.cpp b/xfa/fwl/cfwl_scrollbar.cpp
index acc144f..4bf4b36 100644
--- a/xfa/fwl/cfwl_scrollbar.cpp
+++ b/xfa/fwl/cfwl_scrollbar.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,8 +10,6 @@
 #include <memory>
 #include <utility>
 
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
 #include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
 #include "xfa/fwl/cfwl_messagemousewheel.h"
@@ -20,19 +18,17 @@
 #include "xfa/fwl/cfwl_themepart.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
 
-#define FWL_SCROLLBAR_Elapse 500
-
 namespace {
 
-const float kMinThumbSize = 5.0f;
+constexpr int kScrollbarElapsedMsecs = 500;
+constexpr float kMinThumbSize = 5.0f;
 
 }  // namespace
 
-CFWL_ScrollBar::CFWL_ScrollBar(
-    const CFWL_App* app,
-    std::unique_ptr<CFWL_WidgetProperties> properties,
-    CFWL_Widget* pOuter)
-    : CFWL_Widget(app, std::move(properties), pOuter) {}
+CFWL_ScrollBar::CFWL_ScrollBar(CFWL_App* app,
+                               const Properties& properties,
+                               CFWL_Widget* pOuter)
+    : CFWL_Widget(app, properties, pOuter) {}
 
 CFWL_ScrollBar::~CFWL_ScrollBar() = default;
 
@@ -43,34 +39,30 @@
 void CFWL_ScrollBar::Update() {
   if (IsLocked())
     return;
-  if (!m_pProperties->m_pThemeProvider)
-    m_pProperties->m_pThemeProvider = GetAvailableTheme();
 
   Layout();
 }
 
-void CFWL_ScrollBar::DrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_ScrollBar::DrawWidget(CFGAS_GEGraphics* pGraphics,
                                 const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
-  if (!m_pProperties->m_pThemeProvider)
-    return;
 
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
   if (HasBorder())
-    DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
-  DrawTrack(pGraphics, pTheme, true, &matrix);
-  DrawTrack(pGraphics, pTheme, false, &matrix);
-  DrawArrowBtn(pGraphics, pTheme, true, &matrix);
-  DrawArrowBtn(pGraphics, pTheme, false, &matrix);
-  DrawThumb(pGraphics, pTheme, &matrix);
+    DrawBorder(pGraphics, CFWL_ThemePart::Part::kBorder, matrix);
+
+  DrawLowerTrack(pGraphics, matrix);
+  DrawUpperTrack(pGraphics, matrix);
+  DrawMinArrowBtn(pGraphics, matrix);
+  DrawMaxArrowBtn(pGraphics, matrix);
+  DrawThumb(pGraphics, matrix);
 }
 
 void CFWL_ScrollBar::SetTrackPos(float fTrackPos) {
   m_fTrackPos = fTrackPos;
-  m_rtThumb = CalcThumbButtonRect(m_rtThumb);
-  m_rtMinTrack = CalcMinTrackRect(m_rtMinTrack);
-  m_rtMaxTrack = CalcMaxTrackRect(m_rtMaxTrack);
+  m_ThumbRect = CalcThumbButtonRect(m_ThumbRect);
+  m_MinTrackRect = CalcMinTrackRect(m_MinTrackRect);
+  m_MaxTrackRect = CalcMaxTrackRect(m_MaxTrackRect);
 }
 
 bool CFWL_ScrollBar::DoScroll(CFWL_EventScroll::Code dwCode, float fPos) {
@@ -79,68 +71,79 @@
   return OnScroll(dwCode, fPos);
 }
 
-void CFWL_ScrollBar::DrawTrack(CXFA_Graphics* pGraphics,
-                               IFWL_ThemeProvider* pTheme,
-                               bool bLower,
-                               const CFX_Matrix* pMatrix) {
-  CFWL_ThemeBackground param;
-  param.m_pWidget = this;
-  param.m_iPart = bLower ? CFWL_Part::LowerTrack : CFWL_Part::UpperTrack;
-  param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
-                         ? CFWL_PartState_Disabled
-                         : (bLower ? m_iMinTrackState : m_iMaxTrackState);
-  param.m_pGraphics = pGraphics;
-  param.m_matrix.Concat(*pMatrix);
-  param.m_rtPart = bLower ? m_rtMinTrack : m_rtMaxTrack;
-  pTheme->DrawBackground(param);
+void CFWL_ScrollBar::DrawUpperTrack(CFGAS_GEGraphics* pGraphics,
+                                    const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeBackground param(CFWL_ThemePart::Part::kUpperTrack, this,
+                             pGraphics);
+  param.m_dwStates = (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
+                         ? CFWL_PartState::kDisabled
+                         : m_iMaxTrackState;
+  param.m_matrix = mtMatrix;
+  param.m_PartRect = m_MaxTrackRect;
+  GetThemeProvider()->DrawBackground(param);
 }
 
-void CFWL_ScrollBar::DrawArrowBtn(CXFA_Graphics* pGraphics,
-                                  IFWL_ThemeProvider* pTheme,
-                                  bool bMinBtn,
-                                  const CFX_Matrix* pMatrix) {
-  CFWL_ThemeBackground param;
-  param.m_pWidget = this;
-  param.m_iPart = bMinBtn ? CFWL_Part::ForeArrow : CFWL_Part::BackArrow;
-  param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
-                         ? CFWL_PartState_Disabled
-                         : (bMinBtn ? m_iMinButtonState : m_iMaxButtonState);
-  param.m_pGraphics = pGraphics;
-  param.m_matrix.Concat(*pMatrix);
-  param.m_rtPart = bMinBtn ? m_rtMinBtn : m_rtMaxBtn;
-  if (param.m_rtPart.height > 0 && param.m_rtPart.width > 0)
-    pTheme->DrawBackground(param);
+void CFWL_ScrollBar::DrawLowerTrack(CFGAS_GEGraphics* pGraphics,
+                                    const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeBackground param(CFWL_ThemePart::Part::kLowerTrack, this,
+                             pGraphics);
+  param.m_dwStates = (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
+                         ? CFWL_PartState::kDisabled
+                         : m_iMinTrackState;
+  param.m_matrix = mtMatrix;
+  param.m_PartRect = m_MinTrackRect;
+  GetThemeProvider()->DrawBackground(param);
 }
 
-void CFWL_ScrollBar::DrawThumb(CXFA_Graphics* pGraphics,
-                               IFWL_ThemeProvider* pTheme,
-                               const CFX_Matrix* pMatrix) {
-  CFWL_ThemeBackground param;
-  param.m_pWidget = this;
-  param.m_iPart = CFWL_Part::Thumb;
-  param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
-                         ? CFWL_PartState_Disabled
+void CFWL_ScrollBar::DrawMaxArrowBtn(CFGAS_GEGraphics* pGraphics,
+                                     const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeBackground param(CFWL_ThemePart::Part::kBackArrow, this, pGraphics);
+  param.m_dwStates = (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
+                         ? CFWL_PartState::kDisabled
+                         : m_iMaxButtonState;
+  param.m_matrix = mtMatrix;
+  param.m_PartRect = m_MaxBtnRect;
+  if (param.m_PartRect.height > 0 && param.m_PartRect.width > 0)
+    GetThemeProvider()->DrawBackground(param);
+}
+
+void CFWL_ScrollBar::DrawMinArrowBtn(CFGAS_GEGraphics* pGraphics,
+                                     const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeBackground param(CFWL_ThemePart::Part::kForeArrow, this, pGraphics);
+  param.m_dwStates = (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
+                         ? CFWL_PartState::kDisabled
+                         : m_iMinButtonState;
+  param.m_matrix = mtMatrix;
+  param.m_PartRect = m_MinBtnRect;
+  if (param.m_PartRect.height > 0 && param.m_PartRect.width > 0)
+    GetThemeProvider()->DrawBackground(param);
+}
+
+void CFWL_ScrollBar::DrawThumb(CFGAS_GEGraphics* pGraphics,
+                               const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeBackground param(CFWL_ThemePart::Part::kThumb, this, pGraphics);
+  param.m_dwStates = (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
+                         ? CFWL_PartState::kDisabled
                          : m_iThumbButtonState;
-  param.m_pGraphics = pGraphics;
-  param.m_matrix.Concat(*pMatrix);
-  param.m_rtPart = m_rtThumb;
-  pTheme->DrawBackground(param);
+  param.m_matrix = mtMatrix;
+  param.m_PartRect = m_ThumbRect;
+  GetThemeProvider()->DrawBackground(param);
 }
 
 void CFWL_ScrollBar::Layout() {
-  m_rtClient = GetClientRect();
+  m_ClientRect = GetClientRect();
 
   CalcButtonLen();
-  m_rtMinBtn = CalcMinButtonRect();
-  m_rtMaxBtn = CalcMaxButtonRect();
-  m_rtThumb = CalcThumbButtonRect(m_rtThumb);
-  m_rtMinTrack = CalcMinTrackRect(m_rtMinTrack);
-  m_rtMaxTrack = CalcMaxTrackRect(m_rtMaxTrack);
+  m_MinBtnRect = CalcMinButtonRect();
+  m_MaxBtnRect = CalcMaxButtonRect();
+  m_ThumbRect = CalcThumbButtonRect(m_ThumbRect);
+  m_MinTrackRect = CalcMinTrackRect(m_MinTrackRect);
+  m_MaxTrackRect = CalcMaxTrackRect(m_MaxTrackRect);
 }
 
 void CFWL_ScrollBar::CalcButtonLen() {
-  m_fButtonLen = IsVertical() ? m_rtClient.width : m_rtClient.height;
-  float fLength = IsVertical() ? m_rtClient.height : m_rtClient.width;
+  m_fButtonLen = IsVertical() ? m_ClientRect.width : m_ClientRect.height;
+  float fLength = IsVertical() ? m_ClientRect.height : m_ClientRect.width;
   if (fLength < m_fButtonLen * 2) {
     m_fButtonLen = fLength / 2;
     m_bMinSize = true;
@@ -151,17 +154,17 @@
 
 CFX_RectF CFWL_ScrollBar::CalcMinButtonRect() {
   if (IsVertical())
-    return CFX_RectF(m_rtClient.TopLeft(), m_rtClient.width, m_fButtonLen);
-  return CFX_RectF(m_rtClient.TopLeft(), m_fButtonLen, m_rtClient.height);
+    return CFX_RectF(m_ClientRect.TopLeft(), m_ClientRect.width, m_fButtonLen);
+  return CFX_RectF(m_ClientRect.TopLeft(), m_fButtonLen, m_ClientRect.height);
 }
 
 CFX_RectF CFWL_ScrollBar::CalcMaxButtonRect() {
   if (IsVertical()) {
-    return CFX_RectF(m_rtClient.left, m_rtClient.bottom() - m_fButtonLen,
-                     m_rtClient.width, m_fButtonLen);
+    return CFX_RectF(m_ClientRect.left, m_ClientRect.bottom() - m_fButtonLen,
+                     m_ClientRect.width, m_fButtonLen);
   }
-  return CFX_RectF(m_rtClient.right() - m_fButtonLen, m_rtClient.top,
-                   m_fButtonLen, m_rtClient.height);
+  return CFX_RectF(m_ClientRect.right() - m_fButtonLen, m_ClientRect.top,
+                   m_fButtonLen, m_ClientRect.height);
 }
 
 CFX_RectF CFWL_ScrollBar::CalcThumbButtonRect(const CFX_RectF& rtThumb) {
@@ -178,13 +181,14 @@
   float fRange = m_fRangeMax - m_fRangeMin;
   if (fRange < 0) {
     if (IsVertical()) {
-      return CFX_RectF(m_rtClient.left, m_rtMaxBtn.bottom(), m_rtClient.width,
-                       0);
+      return CFX_RectF(m_ClientRect.left, m_MaxBtnRect.bottom(),
+                       m_ClientRect.width, 0);
     }
-    return CFX_RectF(m_rtMaxBtn.right(), m_rtClient.top, 0, m_rtClient.height);
+    return CFX_RectF(m_MaxBtnRect.right(), m_ClientRect.top, 0,
+                     m_ClientRect.height);
   }
 
-  CFX_RectF rtClient = m_rtClient;
+  CFX_RectF rtClient = m_ClientRect;
   float fLength = IsVertical() ? rtClient.height : rtClient.width;
   float fSize = m_fButtonLen;
   fLength -= fSize * 2.0f;
@@ -195,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;
 
@@ -222,14 +226,14 @@
     return rect;
   }
 
-  rect.left = m_rtClient.left;
-  rect.top = m_rtClient.top;
+  rect.left = m_ClientRect.left;
+  rect.top = m_ClientRect.top;
   if (IsVertical()) {
-    rect.width = m_rtClient.width;
-    rect.height = (m_rtThumb.top + m_rtThumb.bottom()) / 2;
+    rect.width = m_ClientRect.width;
+    rect.height = (m_ThumbRect.top + m_ThumbRect.bottom()) / 2;
   } else {
-    rect.width = (m_rtThumb.left + m_rtThumb.right()) / 2;
-    rect.height = m_rtClient.height;
+    rect.width = (m_ThumbRect.left + m_ThumbRect.right()) / 2;
+    rect.height = m_ClientRect.height;
   }
   return rect;
 }
@@ -239,14 +243,14 @@
     return CFX_RectF(rtMaxRect.TopLeft(), 0, 0);
 
   if (IsVertical()) {
-    float iy = (m_rtThumb.top + m_rtThumb.bottom()) / 2;
-    return CFX_RectF(m_rtClient.left, iy, m_rtClient.width,
-                     m_rtClient.bottom() - iy);
+    float iy = (m_ThumbRect.top + m_ThumbRect.bottom()) / 2;
+    return CFX_RectF(m_ClientRect.left, iy, m_ClientRect.width,
+                     m_ClientRect.bottom() - iy);
   }
 
-  float ix = (m_rtThumb.left + m_rtThumb.right()) / 2;
-  return CFX_RectF(ix, m_rtClient.top, m_rtClient.height - ix,
-                   m_rtClient.height);
+  float ix = (m_ThumbRect.left + m_ThumbRect.right()) / 2;
+  return CFX_RectF(ix, m_ClientRect.top, m_ClientRect.height - ix,
+                   m_ClientRect.height);
 }
 
 float CFWL_ScrollBar::GetTrackPointPos(const CFX_PointF& point) {
@@ -256,32 +260,32 @@
 
   if (IsVertical()) {
     fPos = fRange * diff.y /
-           (m_rtMaxBtn.top - m_rtMinBtn.bottom() - m_rtThumb.height);
+           (m_MaxBtnRect.top - m_MinBtnRect.bottom() - m_ThumbRect.height);
   } else {
     fPos = fRange * diff.x /
-           (m_rtMaxBtn.left - m_rtMinBtn.right() - m_rtThumb.width);
+           (m_MaxBtnRect.left - m_MinBtnRect.right() - m_ThumbRect.width);
   }
 
   fPos += m_fLastTrackPos;
-  return pdfium::clamp(fPos, m_fRangeMin, m_fRangeMax);
+  return std::clamp(fPos, m_fRangeMin, m_fRangeMax);
 }
 
 bool CFWL_ScrollBar::SendEvent() {
-  if (m_iMinButtonState == CFWL_PartState_Pressed) {
+  if (m_iMinButtonState == CFWL_PartState::kPressed) {
     DoScroll(CFWL_EventScroll::Code::StepBackward, m_fTrackPos);
     return false;
   }
-  if (m_iMaxButtonState == CFWL_PartState_Pressed) {
+  if (m_iMaxButtonState == CFWL_PartState::kPressed) {
     DoScroll(CFWL_EventScroll::Code::StepForward, m_fTrackPos);
     return false;
   }
-  if (m_iMinTrackState == CFWL_PartState_Pressed) {
+  if (m_iMinTrackState == CFWL_PartState::kPressed) {
     DoScroll(CFWL_EventScroll::Code::PageBackward, m_fTrackPos);
-    return m_rtThumb.Contains(m_cpTrackPoint);
+    return m_ThumbRect.Contains(m_cpTrackPoint);
   }
-  if (m_iMaxTrackState == CFWL_PartState_Pressed) {
+  if (m_iMaxTrackState == CFWL_PartState::kPressed) {
     DoScroll(CFWL_EventScroll::Code::PageForward, m_fTrackPos);
-    return m_rtThumb.Contains(m_cpTrackPoint);
+    return m_ThumbRect.Contains(m_cpTrackPoint);
   }
   if (m_iMouseWheel) {
     CFWL_EventScroll::Code dwCode = m_iMouseWheel < 0
@@ -293,44 +297,38 @@
 }
 
 bool CFWL_ScrollBar::OnScroll(CFWL_EventScroll::Code dwCode, float fPos) {
-  CFWL_EventScroll ev(this);
-  ev.m_iScrollCode = dwCode;
-  ev.m_fPos = fPos;
+  CFWL_EventScroll ev(this, dwCode, fPos);
   DispatchEvent(&ev);
   return true;
 }
 
 void CFWL_ScrollBar::OnProcessMessage(CFWL_Message* pMessage) {
-  if (!pMessage)
-    return;
-
   CFWL_Message::Type type = pMessage->GetType();
-  if (type == CFWL_Message::Type::Mouse) {
+  if (type == CFWL_Message::Type::kMouse) {
     CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
     switch (pMsg->m_dwCmd) {
-      case FWL_MouseCommand::LeftButtonDown:
+      case CFWL_MessageMouse::MouseCommand::kLeftButtonDown:
         OnLButtonDown(pMsg->m_pos);
         break;
-      case FWL_MouseCommand::LeftButtonUp:
+      case CFWL_MessageMouse::MouseCommand::kLeftButtonUp:
         OnLButtonUp(pMsg->m_pos);
         break;
-      case FWL_MouseCommand::Move:
+      case CFWL_MessageMouse::MouseCommand::kMove:
         OnMouseMove(pMsg->m_pos);
         break;
-      case FWL_MouseCommand::Leave:
+      case CFWL_MessageMouse::MouseCommand::kLeave:
         OnMouseLeave();
         break;
       default:
         break;
     }
-  } else if (type == CFWL_Message::Type::MouseWheel) {
-    CFWL_MessageMouseWheel* pMsg =
-        static_cast<CFWL_MessageMouseWheel*>(pMessage);
-    OnMouseWheel(pMsg->m_delta);
+  } else if (type == CFWL_Message::Type::kMouseWheel) {
+    auto* pMsg = static_cast<CFWL_MessageMouseWheel*>(pMessage);
+    OnMouseWheel(pMsg->delta());
   }
 }
 
-void CFWL_ScrollBar::OnDrawWidget(CXFA_Graphics* pGraphics,
+void CFWL_ScrollBar::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                                   const CFX_Matrix& matrix) {
   DrawWidget(pGraphics, matrix);
 }
@@ -344,97 +342,97 @@
 
   m_cpTrackPoint = point;
   m_fLastTrackPos = m_fTrackPos;
-  if (m_rtMinBtn.Contains(point))
-    DoMouseDown(0, m_rtMinBtn, m_iMinButtonState, point);
-  else if (m_rtThumb.Contains(point))
-    DoMouseDown(1, m_rtThumb, m_iThumbButtonState, point);
-  else if (m_rtMaxBtn.Contains(point))
-    DoMouseDown(2, m_rtMaxBtn, m_iMaxButtonState, point);
-  else if (m_rtMinTrack.Contains(point))
-    DoMouseDown(3, m_rtMinTrack, m_iMinTrackState, point);
+  if (m_MinBtnRect.Contains(point))
+    DoMouseDown(0, m_MinBtnRect, &m_iMinButtonState, point);
+  else if (m_ThumbRect.Contains(point))
+    DoMouseDown(1, m_ThumbRect, &m_iThumbButtonState, point);
+  else if (m_MaxBtnRect.Contains(point))
+    DoMouseDown(2, m_MaxBtnRect, &m_iMaxButtonState, point);
+  else if (m_MinTrackRect.Contains(point))
+    DoMouseDown(3, m_MinTrackRect, &m_iMinTrackState, point);
   else
-    DoMouseDown(4, m_rtMaxTrack, m_iMaxTrackState, point);
+    DoMouseDown(4, m_MaxTrackRect, &m_iMaxTrackState, point);
 
   if (!SendEvent()) {
-    m_pTimer = pdfium::MakeUnique<CFX_Timer>(
-        GetOwnerApp()->GetAdapterNative()->GetTimerHandler(), this,
-        FWL_SCROLLBAR_Elapse);
+    m_pTimer = std::make_unique<CFX_Timer>(GetFWLApp()->GetTimerHandler(), this,
+                                           kScrollbarElapsedMsecs);
   }
 }
 
 void CFWL_ScrollBar::OnLButtonUp(const CFX_PointF& point) {
   m_pTimer.reset();
   m_bMouseDown = false;
-  DoMouseUp(0, m_rtMinBtn, m_iMinButtonState, point);
-  DoMouseUp(1, m_rtThumb, m_iThumbButtonState, point);
-  DoMouseUp(2, m_rtMaxBtn, m_iMaxButtonState, point);
-  DoMouseUp(3, m_rtMinTrack, m_iMinTrackState, point);
-  DoMouseUp(4, m_rtMaxTrack, m_iMaxTrackState, point);
+  DoMouseUp(0, m_MinBtnRect, &m_iMinButtonState, point);
+  DoMouseUp(1, m_ThumbRect, &m_iThumbButtonState, point);
+  DoMouseUp(2, m_MaxBtnRect, &m_iMaxButtonState, point);
+  DoMouseUp(3, m_MinTrackRect, &m_iMinTrackState, point);
+  DoMouseUp(4, m_MaxTrackRect, &m_iMaxTrackState, point);
   SetGrab(false);
 }
 
 void CFWL_ScrollBar::OnMouseMove(const CFX_PointF& point) {
-  DoMouseMove(0, m_rtMinBtn, m_iMinButtonState, point);
-  DoMouseMove(1, m_rtThumb, m_iThumbButtonState, point);
-  DoMouseMove(2, m_rtMaxBtn, m_iMaxButtonState, point);
-  DoMouseMove(3, m_rtMinTrack, m_iMinTrackState, point);
-  DoMouseMove(4, m_rtMaxTrack, m_iMaxTrackState, point);
+  DoMouseMove(0, m_MinBtnRect, &m_iMinButtonState, point);
+  DoMouseMove(1, m_ThumbRect, &m_iThumbButtonState, point);
+  DoMouseMove(2, m_MaxBtnRect, &m_iMaxButtonState, point);
+  DoMouseMove(3, m_MinTrackRect, &m_iMinTrackState, point);
+  DoMouseMove(4, m_MaxTrackRect, &m_iMaxTrackState, point);
 }
 
 void CFWL_ScrollBar::OnMouseLeave() {
-  DoMouseLeave(0, m_rtMinBtn, m_iMinButtonState);
-  DoMouseLeave(1, m_rtThumb, m_iThumbButtonState);
-  DoMouseLeave(2, m_rtMaxBtn, m_iMaxButtonState);
-  DoMouseLeave(3, m_rtMinTrack, m_iMinTrackState);
-  DoMouseLeave(4, m_rtMaxTrack, m_iMaxTrackState);
+  DoMouseLeave(0, m_MinBtnRect, &m_iMinButtonState);
+  DoMouseLeave(1, m_ThumbRect, &m_iThumbButtonState);
+  DoMouseLeave(2, m_MaxBtnRect, &m_iMaxButtonState);
+  DoMouseLeave(3, m_MinTrackRect, &m_iMinTrackState);
+  DoMouseLeave(4, m_MaxTrackRect, &m_iMaxTrackState);
 }
 
-void CFWL_ScrollBar::OnMouseWheel(const CFX_PointF& delta) {
-  m_iMouseWheel = static_cast<int32_t>(delta.x);
+void CFWL_ScrollBar::OnMouseWheel(const CFX_Vector& delta) {
+  m_iMouseWheel = delta.y;
   SendEvent();
   m_iMouseWheel = 0;
 }
 
 void CFWL_ScrollBar::DoMouseDown(int32_t iItem,
                                  const CFX_RectF& rtItem,
-                                 int32_t& iState,
+                                 CFWL_PartState* pState,
                                  const CFX_PointF& point) {
   if (!rtItem.Contains(point))
     return;
-  if (iState == CFWL_PartState_Pressed)
+  if (*pState == CFWL_PartState::kPressed)
     return;
 
-  iState = CFWL_PartState_Pressed;
+  *pState = CFWL_PartState::kPressed;
   RepaintRect(rtItem);
 }
 
 void CFWL_ScrollBar::DoMouseUp(int32_t iItem,
                                const CFX_RectF& rtItem,
-                               int32_t& iState,
+                               CFWL_PartState* pState,
                                const CFX_PointF& point) {
-  int32_t iNewState =
-      rtItem.Contains(point) ? CFWL_PartState_Hovered : CFWL_PartState_Normal;
-  if (iState == iNewState)
+  CFWL_PartState iNewState = rtItem.Contains(point) ? CFWL_PartState::kHovered
+                                                    : CFWL_PartState::kNormal;
+  if (*pState == iNewState)
     return;
 
-  iState = iNewState;
+  *pState = iNewState;
   RepaintRect(rtItem);
   OnScroll(CFWL_EventScroll::Code::EndScroll, m_fTrackPos);
 }
 
 void CFWL_ScrollBar::DoMouseMove(int32_t iItem,
                                  const CFX_RectF& rtItem,
-                                 int32_t& iState,
+                                 CFWL_PartState* pState,
                                  const CFX_PointF& point) {
   if (!m_bMouseDown) {
-    int32_t iNewState =
-        rtItem.Contains(point) ? CFWL_PartState_Hovered : CFWL_PartState_Normal;
-    if (iState == iNewState)
+    CFWL_PartState iNewState = rtItem.Contains(point) ? CFWL_PartState::kHovered
+                                                      : CFWL_PartState::kNormal;
+    if (*pState == iNewState)
       return;
 
-    iState = iNewState;
+    *pState = iNewState;
     RepaintRect(rtItem);
-  } else if ((2 == iItem) && (m_iThumbButtonState == CFWL_PartState_Pressed)) {
+  } else if ((2 == iItem) &&
+             (m_iThumbButtonState == CFWL_PartState::kPressed)) {
     m_fTrackPos = GetTrackPointPos(point);
     OnScroll(CFWL_EventScroll::Code::TrackPos, m_fTrackPos);
   }
@@ -442,28 +440,28 @@
 
 void CFWL_ScrollBar::DoMouseLeave(int32_t iItem,
                                   const CFX_RectF& rtItem,
-                                  int32_t& iState) {
-  if (iState == CFWL_PartState_Normal)
+                                  CFWL_PartState* pState) {
+  if (*pState == CFWL_PartState::kNormal)
     return;
 
-  iState = CFWL_PartState_Normal;
+  *pState = CFWL_PartState::kNormal;
   RepaintRect(rtItem);
 }
 
 void CFWL_ScrollBar::DoMouseHover(int32_t iItem,
                                   const CFX_RectF& rtItem,
-                                  int32_t& iState) {
-  if (iState == CFWL_PartState_Hovered)
+                                  CFWL_PartState* pState) {
+  if (*pState == CFWL_PartState::kHovered)
     return;
 
-  iState = CFWL_PartState_Hovered;
+  *pState = CFWL_PartState::kHovered;
   RepaintRect(rtItem);
 }
 
 void CFWL_ScrollBar::OnTimerFired() {
   m_pTimer.reset();
   if (!SendEvent()) {
-    m_pTimer = pdfium::MakeUnique<CFX_Timer>(
-        GetOwnerApp()->GetAdapterNative()->GetTimerHandler(), this, 0);
+    m_pTimer =
+        std::make_unique<CFX_Timer>(GetFWLApp()->GetTimerHandler(), this, 0);
   }
 }
diff --git a/xfa/fwl/cfwl_scrollbar.h b/xfa/fwl/cfwl_scrollbar.h
index c98479d..598d19d 100644
--- a/xfa/fwl/cfwl_scrollbar.h
+++ b/xfa/fwl/cfwl_scrollbar.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,15 @@
 #ifndef XFA_FWL_CFWL_SCROLLBAR_H_
 #define XFA_FWL_CFWL_SCROLLBAR_H_
 
+#include <stdint.h>
+
 #include <memory>
 
 #include "core/fxcrt/cfx_timer.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/check.h"
 #include "xfa/fwl/cfwl_eventscroll.h"
+#include "xfa/fwl/cfwl_themepart.h"
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fwl/cfwl_widgetproperties.h"
-
-class CFWL_Widget;
 
 #define FWL_STYLEEXT_SCB_Horz (0L << 0)
 #define FWL_STYLEEXT_SCB_Vert (1L << 0)
@@ -24,25 +23,22 @@
 class CFWL_ScrollBar final : public CFWL_Widget,
                              public CFX_Timer::CallbackIface {
  public:
-  CFWL_ScrollBar(const CFWL_App* app,
-                 std::unique_ptr<CFWL_WidgetProperties> properties,
-                 CFWL_Widget* pOuter);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_ScrollBar() override;
 
   // CFWL_Widget:
   FWL_Type GetClassID() const override;
   void Update() override;
-  void DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) override;
+  void DrawWidget(CFGAS_GEGraphics* pGraphics,
+                  const CFX_Matrix& matrix) override;
   void OnProcessMessage(CFWL_Message* pMessage) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
+  void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix) override;
 
   // CFX_Timer::CallbackIface:
   void OnTimerFired() override;
 
   void GetRange(float* fMin, float* fMax) const {
-    ASSERT(fMin);
-    ASSERT(fMax);
     *fMin = m_fRangeMin;
     *fMax = m_fRangeMax;
   }
@@ -59,20 +55,16 @@
   void SetTrackPos(float fTrackPos);
 
  private:
-  bool IsVertical() const {
-    return !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_SCB_Vert);
-  }
-  void DrawTrack(CXFA_Graphics* pGraphics,
-                 IFWL_ThemeProvider* pTheme,
-                 bool bLower,
-                 const CFX_Matrix* pMatrix);
-  void DrawArrowBtn(CXFA_Graphics* pGraphics,
-                    IFWL_ThemeProvider* pTheme,
-                    bool bMinBtn,
-                    const CFX_Matrix* pMatrix);
-  void DrawThumb(CXFA_Graphics* pGraphics,
-                 IFWL_ThemeProvider* pTheme,
-                 const CFX_Matrix* pMatrix);
+  CFWL_ScrollBar(CFWL_App* app,
+                 const Properties& properties,
+                 CFWL_Widget* pOuter);
+
+  bool IsVertical() const { return !!(GetStyleExts() & FWL_STYLEEXT_SCB_Vert); }
+  void DrawUpperTrack(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawLowerTrack(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawMaxArrowBtn(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawMinArrowBtn(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
+  void DrawThumb(CFGAS_GEGraphics* pGraphics, const CFX_Matrix& mtMatrix);
   void Layout();
   void CalcButtonLen();
   CFX_RectF CalcMinButtonRect();
@@ -88,22 +80,26 @@
   void OnLButtonUp(const CFX_PointF& point);
   void OnMouseMove(const CFX_PointF& point);
   void OnMouseLeave();
-  void OnMouseWheel(const CFX_PointF& delta);
+  void OnMouseWheel(const CFX_Vector& delta);
   bool DoScroll(CFWL_EventScroll::Code dwCode, float fPos);
   void DoMouseDown(int32_t iItem,
                    const CFX_RectF& rtItem,
-                   int32_t& iState,
+                   CFWL_PartState* pState,
                    const CFX_PointF& point);
   void DoMouseUp(int32_t iItem,
                  const CFX_RectF& rtItem,
-                 int32_t& iState,
+                 CFWL_PartState* pState,
                  const CFX_PointF& point);
   void DoMouseMove(int32_t iItem,
                    const CFX_RectF& rtItem,
-                   int32_t& iState,
+                   CFWL_PartState* pState,
                    const CFX_PointF& point);
-  void DoMouseLeave(int32_t iItem, const CFX_RectF& rtItem, int32_t& iState);
-  void DoMouseHover(int32_t iItem, const CFX_RectF& rtItem, int32_t& iState);
+  void DoMouseLeave(int32_t iItem,
+                    const CFX_RectF& rtItem,
+                    CFWL_PartState* pState);
+  void DoMouseHover(int32_t iItem,
+                    const CFX_RectF& rtItem,
+                    CFWL_PartState* pState);
 
   float m_fRangeMin = 0.0f;
   float m_fRangeMax = -1.0f;
@@ -111,23 +107,23 @@
   float m_fStepSize = 0.0f;
   float m_fPos = 0.0f;
   float m_fTrackPos = 0.0f;
-  int32_t m_iMinButtonState = CFWL_PartState_Normal;
-  int32_t m_iMaxButtonState = CFWL_PartState_Normal;
-  int32_t m_iThumbButtonState = CFWL_PartState_Normal;
-  int32_t m_iMinTrackState = CFWL_PartState_Normal;
-  int32_t m_iMaxTrackState = CFWL_PartState_Normal;
+  CFWL_PartState m_iMinButtonState = CFWL_PartState::kNormal;
+  CFWL_PartState m_iMaxButtonState = CFWL_PartState::kNormal;
+  CFWL_PartState m_iThumbButtonState = CFWL_PartState::kNormal;
+  CFWL_PartState m_iMinTrackState = CFWL_PartState::kNormal;
+  CFWL_PartState m_iMaxTrackState = CFWL_PartState::kNormal;
   float m_fLastTrackPos = 0.0f;
   CFX_PointF m_cpTrackPoint;
   int32_t m_iMouseWheel = 0;
   float m_fButtonLen = 0.0f;
   bool m_bMouseDown = false;
   bool m_bMinSize = false;
-  CFX_RectF m_rtClient;
-  CFX_RectF m_rtThumb;
-  CFX_RectF m_rtMinBtn;
-  CFX_RectF m_rtMaxBtn;
-  CFX_RectF m_rtMinTrack;
-  CFX_RectF m_rtMaxTrack;
+  CFX_RectF m_ClientRect;
+  CFX_RectF m_ThumbRect;
+  CFX_RectF m_MinBtnRect;
+  CFX_RectF m_MaxBtnRect;
+  CFX_RectF m_MinTrackRect;
+  CFX_RectF m_MaxTrackRect;
   std::unique_ptr<CFX_Timer> m_pTimer;
 };
 
diff --git a/xfa/fwl/cfwl_themebackground.cpp b/xfa/fwl/cfwl_themebackground.cpp
new file mode 100644
index 0000000..87500ac
--- /dev/null
+++ b/xfa/fwl/cfwl_themebackground.cpp
@@ -0,0 +1,14 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fwl/cfwl_themebackground.h"
+
+CFWL_ThemeBackground::CFWL_ThemeBackground(Part iPart,
+                                           CFWL_Widget* pWidget,
+                                           CFGAS_GEGraphics* pGraphics)
+    : CFWL_ThemePart(iPart, pWidget), m_pGraphics(pGraphics) {}
+
+CFWL_ThemeBackground::~CFWL_ThemeBackground() = default;
diff --git a/xfa/fwl/cfwl_themebackground.h b/xfa/fwl/cfwl_themebackground.h
index 46df73c..704ef43 100644
--- a/xfa/fwl/cfwl_themebackground.h
+++ b/xfa/fwl/cfwl_themebackground.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,23 +7,29 @@
 #ifndef XFA_FWL_CFWL_THEMEBACKGROUND_H_
 #define XFA_FWL_CFWL_THEMEBACKGROUND_H_
 
+#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fwl/cfwl_themepart.h"
 
-class CXFA_Graphics;
-class CXFA_GEPath;
+class CFGAS_GEGraphics;
+class CFGAS_GEPath;
 
 class CFWL_ThemeBackground final : public CFWL_ThemePart {
  public:
-  CFWL_ThemeBackground();
+  FX_STACK_ALLOCATED();
+
+  CFWL_ThemeBackground(Part iPart,
+                       CFWL_Widget* pWidget,
+                       CFGAS_GEGraphics* pGraphics);
   ~CFWL_ThemeBackground();
 
-  UnownedPtr<CXFA_Graphics> m_pGraphics;
-  UnownedPtr<CXFA_GEPath> m_pPath;
+  CFGAS_GEGraphics* GetGraphics() const { return m_pGraphics; }
+  const CFGAS_GEPath* GetPath() const { return m_pPath; }
+  void SetPath(const CFGAS_GEPath* pPath) { m_pPath = pPath; }
+
+ private:
+  UnownedPtr<const CFGAS_GEPath> m_pPath;
+  UnownedPtr<CFGAS_GEGraphics> const m_pGraphics;
 };
 
-inline CFWL_ThemeBackground::CFWL_ThemeBackground() = default;
-
-inline CFWL_ThemeBackground::~CFWL_ThemeBackground() = default;
-
 #endif  // XFA_FWL_CFWL_THEMEBACKGROUND_H_
diff --git a/xfa/fwl/cfwl_themepart.cpp b/xfa/fwl/cfwl_themepart.cpp
index 831e494..7294a1a 100644
--- a/xfa/fwl/cfwl_themepart.cpp
+++ b/xfa/fwl/cfwl_themepart.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,17 @@
 
 #include "xfa/fwl/cfwl_themepart.h"
 
-CFWL_ThemePart::CFWL_ThemePart()
-    : m_pWidget(nullptr),
-      m_iPart(CFWL_Part::None),
-      m_dwStates(CFWL_PartState_Normal),
-      m_bMaximize(false),
-      m_bStaticBackground(false),
-      m_pRtData(nullptr) {}
+CFWL_ThemePart::CFWL_ThemePart(Part iPart, CFWL_Widget* pWidget)
+    : m_iPart(iPart), m_pWidget(pWidget) {}
+
+CFWL_ThemePart::~CFWL_ThemePart() = default;
+
+FWLTHEME_STATE CFWL_ThemePart::GetThemeState() const {
+  if (m_dwStates & CFWL_PartState::kDisabled)
+    return FWLTHEME_STATE::kDisable;
+  if (m_dwStates & CFWL_PartState::kPressed)
+    return FWLTHEME_STATE::kPressed;
+  if (m_dwStates & CFWL_PartState::kHovered)
+    return FWLTHEME_STATE::kHover;
+  return FWLTHEME_STATE::kNormal;
+}
diff --git a/xfa/fwl/cfwl_themepart.h b/xfa/fwl/cfwl_themepart.h
index 46be205..25f8a7f 100644
--- a/xfa/fwl/cfwl_themepart.h
+++ b/xfa/fwl/cfwl_themepart.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,87 +7,100 @@
 #ifndef XFA_FWL_CFWL_THEMEPART_H_
 #define XFA_FWL_CFWL_THEMEPART_H_
 
+#include <stdint.h>
+
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
-
-enum class CFWL_Part {
-  None = 0,
-
-  BackArrow,
-  Background,
-  Border,
-  Caption,
-  Check,
-  CheckBox,
-  CloseBox,
-  CombTextLine,
-  DateInBK,
-  DateInCircle,
-  DatesIn,
-  DatesOut,
-  DownButton,
-  DropDownButton,
-  ForeArrow,
-  HSeparator,
-  HeadText,
-  Header,
-  Icon,
-  Image,
-  LBtn,
-  ListItem,
-  LowerTrack,
-  MinimizeBox,
-  MaximizeBox,
-  NarrowCaption,
-  RBtn,
-  Thumb,
-  ThumbBackArrow,
-  ThumbForeArrow,
-  ThumbLowerTrack,
-  ThumbThumb,
-  ThumbUpperTrack,
-  Today,
-  TodayCircle,
-  UpButton,
-  UpperTrack,
-  VSeparator,
-  Week,
-  WeekNum,
-  WeekNumSep
-};
-
-enum CFWL_PartState {
-  CFWL_PartState_Normal = 0,
-
-  CFWL_PartState_Checked = 1 << 1,
-  CFWL_PartState_Default = 1 << 2,
-  CFWL_PartState_Disabled = 1 << 3,
-  CFWL_PartState_Flagged = 1 << 4,
-  CFWL_PartState_Focused = 1 << 5,
-  CFWL_PartState_HightLight = 1 << 6,
-  CFWL_PartState_Hovered = 1 << 7,
-  CFWL_PartState_Neutral = 1 << 9,
-  CFWL_PartState_Pressed = 1 << 10,
-  CFWL_PartState_ReadOnly = 1 << 11,
-  CFWL_PartState_LSelected = 1 << 12,
-  CFWL_PartState_RSelected = 1 << 13,
-  CFWL_PartState_Selected = 1 << 14
-};
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/mask.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "xfa/fwl/theme/cfwl_utils.h"
 
 class CFWL_Widget;
 
+enum class CFWL_PartState : uint16_t {
+  kNormal = 0,
+  kChecked = 1 << 1,
+  kDefault = 1 << 2,
+  kDisabled = 1 << 3,
+  kFlagged = 1 << 4,
+  kFocused = 1 << 5,
+  kHightLight = 1 << 6,
+  kHovered = 1 << 7,
+  kNeutral = 1 << 9,
+  kPressed = 1 << 10,
+  kReadOnly = 1 << 11,
+  kLSelected = 1 << 12,
+  kRSelected = 1 << 13,
+  kSelected = 1 << 14
+};
+
 class CFWL_ThemePart {
  public:
-  CFWL_ThemePart();
+  enum class Part : uint8_t {
+    kNone = 0,
+
+    kBackArrow,
+    kBackground,
+    kBorder,
+    kCaption,
+    kCheck,
+    kCheckBox,
+    kCloseBox,
+    kCombTextLine,
+    kDateInBK,
+    kDateInCircle,
+    kDatesIn,
+    kDatesOut,
+    kDownButton,
+    kDropDownButton,
+    kForeArrow,
+    kHSeparator,
+    kHeadText,
+    kHeader,
+    kIcon,
+    kImage,
+    kLBtn,
+    kListItem,
+    kLowerTrack,
+    kMinimizeBox,
+    kMaximizeBox,
+    kNarrowCaption,
+    kRBtn,
+    kThumb,
+    kThumbBackArrow,
+    kThumbForeArrow,
+    kThumbLowerTrack,
+    kThumbThumb,
+    kThumbUpperTrack,
+    kToday,
+    kTodayCircle,
+    kUpButton,
+    kUpperTrack,
+    kVSeparator,
+    kWeek,
+    kWeekNum,
+    kWeekNumSep
+  };
+
+  FX_STACK_ALLOCATED();
+
+  CFWL_ThemePart(Part iPart, CFWL_Widget* pWidget);
+  ~CFWL_ThemePart();
+
+  Part GetPart() const { return m_iPart; }
+  CFWL_Widget* GetWidget() const { return m_pWidget; }
+  FWLTHEME_STATE GetThemeState() const;
 
   CFX_Matrix m_matrix;
-  CFX_RectF m_rtPart;
-  CFWL_Widget* m_pWidget;
-  CFWL_Part m_iPart;
-  uint32_t m_dwStates;
-  bool m_bMaximize;
-  bool m_bStaticBackground;
-  CFX_RectF* m_pRtData;
+  CFX_RectF m_PartRect;
+  UnownedPtr<const CFX_RectF> m_pRtData;
+  Mask<CFWL_PartState> m_dwStates = CFWL_PartState::kNormal;
+  bool m_bMaximize = false;
+  bool m_bStaticBackground = false;
+
+ private:
+  const Part m_iPart;
+  UnownedPtr<CFWL_Widget> const m_pWidget;
 };
 
 #endif  // XFA_FWL_CFWL_THEMEPART_H_
diff --git a/xfa/fwl/cfwl_themetext.cpp b/xfa/fwl/cfwl_themetext.cpp
new file mode 100644
index 0000000..c228c93
--- /dev/null
+++ b/xfa/fwl/cfwl_themetext.cpp
@@ -0,0 +1,14 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fwl/cfwl_themetext.h"
+
+CFWL_ThemeText::CFWL_ThemeText(Part iPart,
+                               CFWL_Widget* pWidget,
+                               CFGAS_GEGraphics* pGraphics)
+    : CFWL_ThemePart(iPart, pWidget), m_pGraphics(pGraphics) {}
+
+CFWL_ThemeText::~CFWL_ThemeText() = default;
diff --git a/xfa/fwl/cfwl_themetext.h b/xfa/fwl/cfwl_themetext.h
index f0cfaa7..71f8d8d 100644
--- a/xfa/fwl/cfwl_themetext.h
+++ b/xfa/fwl/cfwl_themetext.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,18 +7,29 @@
 #ifndef XFA_FWL_CFWL_THEMETEXT_H_
 #define XFA_FWL_CFWL_THEMETEXT_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "xfa/fde/cfde_data.h"
 #include "xfa/fwl/cfwl_themepart.h"
 
+class CFGAS_GEGraphics;
+
 class CFWL_ThemeText final : public CFWL_ThemePart {
  public:
-  CFWL_ThemeText() = default;
+  FX_STACK_ALLOCATED();
+
+  CFWL_ThemeText(Part iPart, CFWL_Widget* pWidget, CFGAS_GEGraphics* pGraphics);
+  ~CFWL_ThemeText();
+
+  CFGAS_GEGraphics* GetGraphics() const { return m_pGraphics; }
 
   FDE_TextAlignment m_iTTOAlign = FDE_TextAlignment::kTopLeft;
-  CXFA_Graphics* m_pGraphics = nullptr;
-  WideString m_wsText;
   FDE_TextStyle m_dwTTOStyles;
+  WideString m_wsText;
+
+ private:
+  UnownedPtr<CFGAS_GEGraphics> const m_pGraphics;
 };
 
 #endif  // XFA_FWL_CFWL_THEMETEXT_H_
diff --git a/xfa/fwl/cfwl_widget.cpp b/xfa/fwl/cfwl_widget.cpp
index 39b706b..61d12f2 100644
--- a/xfa/fwl/cfwl_widget.cpp
+++ b/xfa/fwl/cfwl_widget.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,8 @@
 #include <utility>
 #include <vector>
 
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_combobox.h"
@@ -28,28 +29,40 @@
 #include "xfa/fwl/cfwl_widgetmgr.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
 
-#define FWL_WGT_CalcHeight 2048
-#define FWL_WGT_CalcWidth 2048
-#define FWL_WGT_CalcMultiLineDefWidth 120.0f
+namespace {
 
-CFWL_Widget::CFWL_Widget(const CFWL_App* app,
-                         std::unique_ptr<CFWL_WidgetProperties> properties,
+constexpr float kCalcHeight = 2048.0f;
+constexpr float kCalcWidth = 2048.0f;
+constexpr float kCalcMultiLineDefWidth = 120.0f;
+
+}  // namespace
+
+CFWL_Widget::CFWL_Widget(CFWL_App* app,
+                         const Properties& properties,
                          CFWL_Widget* pOuter)
-    : m_pOwnerApp(app),
+    : m_Properties(properties),
+      m_pFWLApp(app),
       m_pWidgetMgr(app->GetWidgetMgr()),
-      m_pProperties(std::move(properties)),
       m_pOuter(pOuter) {
-  ASSERT(m_pWidgetMgr);
-  ASSERT(m_pProperties);
-  m_pWidgetMgr->InsertWidget(m_pProperties->m_pParent, this);
+  m_pWidgetMgr->InsertWidget(m_pOuter, this);
 }
 
-CFWL_Widget::~CFWL_Widget() {
+CFWL_Widget::~CFWL_Widget() = default;
+
+void CFWL_Widget::PreFinalize() {
   CHECK(!IsLocked());  // Prefer hard stop to UaF.
   NotifyDriver();
   m_pWidgetMgr->RemoveWidget(this);
 }
 
+void CFWL_Widget::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pAdapterIface);
+  visitor->Trace(m_pFWLApp);
+  visitor->Trace(m_pWidgetMgr);
+  visitor->Trace(m_pDelegate);
+  visitor->Trace(m_pOuter);
+}
+
 bool CFWL_Widget::IsForm() const {
   return false;
 }
@@ -59,7 +72,7 @@
 }
 
 CFX_RectF CFWL_Widget::GetWidgetRect() {
-  return m_pProperties->m_rtWidget;
+  return m_WidgetRect;
 }
 
 void CFWL_Widget::InflateWidgetRect(CFX_RectF& rect) {
@@ -71,35 +84,23 @@
 }
 
 void CFWL_Widget::SetWidgetRect(const CFX_RectF& rect) {
-  m_pProperties->m_rtWidget = rect;
+  m_WidgetRect = rect;
 }
 
 CFX_RectF CFWL_Widget::GetClientRect() {
   return GetEdgeRect();
 }
 
-void CFWL_Widget::SetParent(CFWL_Widget* pParent) {
-  m_pProperties->m_pParent = pParent;
-  m_pWidgetMgr->SetParent(pParent, this);
-}
-
 void CFWL_Widget::ModifyStyles(uint32_t dwStylesAdded,
                                uint32_t dwStylesRemoved) {
-  m_pProperties->m_dwStyles =
-      (m_pProperties->m_dwStyles & ~dwStylesRemoved) | dwStylesAdded;
+  m_Properties.m_dwStyles &= ~dwStylesRemoved;
+  m_Properties.m_dwStyles |= dwStylesAdded;
 }
 
-uint32_t CFWL_Widget::GetStylesEx() const {
-  return m_pProperties->m_dwStyleExes;
-}
-uint32_t CFWL_Widget::GetStates() const {
-  return m_pProperties->m_dwStates;
-}
-
-void CFWL_Widget::ModifyStylesEx(uint32_t dwStylesExAdded,
-                                 uint32_t dwStylesExRemoved) {
-  m_pProperties->m_dwStyleExes =
-      (m_pProperties->m_dwStyleExes & ~dwStylesExRemoved) | dwStylesExAdded;
+void CFWL_Widget::ModifyStyleExts(uint32_t dwStyleExtsAdded,
+                                  uint32_t dwStyleExtsRemoved) {
+  m_Properties.m_dwStyleExts &= ~dwStyleExtsRemoved;
+  m_Properties.m_dwStyleExts |= dwStyleExtsAdded;
 }
 
 static void NotifyHideChildWidget(CFWL_WidgetMgr* widgetMgr,
@@ -114,25 +115,24 @@
 }
 
 void CFWL_Widget::SetStates(uint32_t dwStates) {
-  m_pProperties->m_dwStates |= dwStates;
+  m_Properties.m_dwStates |= dwStates;
   if (IsVisible())
     return;
 
-  CFWL_NoteDriver* noteDriver = GetOwnerApp()->GetNoteDriver();
+  CFWL_NoteDriver* noteDriver = GetFWLApp()->GetNoteDriver();
   noteDriver->NotifyTargetHide(this);
 
-  CFWL_WidgetMgr* widgetMgr = GetOwnerApp()->GetWidgetMgr();
+  CFWL_WidgetMgr* widgetMgr = GetFWLApp()->GetWidgetMgr();
   CFWL_Widget* child = widgetMgr->GetFirstChildWidget(this);
   while (child) {
     noteDriver->NotifyTargetHide(child);
     NotifyHideChildWidget(widgetMgr, child, noteDriver);
     child = widgetMgr->GetNextSiblingWidget(child);
   }
-  return;
 }
 
 void CFWL_Widget::RemoveStates(uint32_t dwStates) {
-  m_pProperties->m_dwStates &= ~dwStates;
+  m_Properties.m_dwStates &= ~dwStates;
 }
 
 FWL_WidgetHit CFWL_Widget::HitTest(const CFX_PointF& point) {
@@ -172,33 +172,33 @@
   return matrix;
 }
 
-void CFWL_Widget::SetThemeProvider(IFWL_ThemeProvider* pThemeProvider) {
-  m_pProperties->m_pThemeProvider = pThemeProvider;
+IFWL_ThemeProvider* CFWL_Widget::GetThemeProvider() const {
+  return GetFWLApp()->GetThemeProvider();
 }
 
 bool CFWL_Widget::IsEnabled() const {
-  return (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled) == 0;
+  return (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled) == 0;
 }
 
 bool CFWL_Widget::HasBorder() const {
-  return !!(m_pProperties->m_dwStyles & FWL_WGTSTYLE_Border);
+  return !!(m_Properties.m_dwStyles & FWL_STYLE_WGT_Border);
 }
 
 bool CFWL_Widget::IsVisible() const {
-  return !(m_pProperties->m_dwStates & FWL_WGTSTATE_Invisible);
+  return !(m_Properties.m_dwStates & FWL_STATE_WGT_Invisible);
 }
 
 bool CFWL_Widget::IsOverLapper() const {
-  return (m_pProperties->m_dwStyles & FWL_WGTSTYLE_WindowTypeMask) ==
-         FWL_WGTSTYLE_OverLapper;
+  return (m_Properties.m_dwStyles & FWL_STYLE_WGT_WindowTypeMask) ==
+         FWL_STYLE_WGT_OverLapper;
 }
 
 bool CFWL_Widget::IsPopup() const {
-  return !!(m_pProperties->m_dwStyles & FWL_WGTSTYLE_Popup);
+  return !!(m_Properties.m_dwStyles & FWL_STYLE_WGT_Popup);
 }
 
 bool CFWL_Widget::IsChild() const {
-  return !!(m_pProperties->m_dwStyles & FWL_WGTSTYLE_Child);
+  return !!(m_Properties.m_dwStyles & FWL_STYLE_WGT_Child);
 }
 
 CFWL_Widget* CFWL_Widget::GetOutmost() const {
@@ -209,53 +209,26 @@
 }
 
 CFX_RectF CFWL_Widget::GetEdgeRect() const {
-  CFX_RectF rtEdge(0, 0, m_pProperties->m_rtWidget.width,
-                   m_pProperties->m_rtWidget.height);
+  CFX_RectF rtEdge(0, 0, m_WidgetRect.width, m_WidgetRect.height);
   if (HasBorder())
     rtEdge.Deflate(GetCXBorderSize(), GetCYBorderSize());
   return rtEdge;
 }
 
 float CFWL_Widget::GetCXBorderSize() const {
-  IFWL_ThemeProvider* theme = GetAvailableTheme();
-  return theme ? theme->GetCXBorderSize() : 0.0f;
+  return GetThemeProvider()->GetCXBorderSize();
 }
 
 float CFWL_Widget::GetCYBorderSize() const {
-  IFWL_ThemeProvider* theme = GetAvailableTheme();
-  return theme ? theme->GetCYBorderSize() : 0.0f;
+  return GetThemeProvider()->GetCYBorderSize();
 }
 
 CFX_RectF CFWL_Widget::GetRelativeRect() const {
-  return CFX_RectF(0, 0, m_pProperties->m_rtWidget.width,
-                   m_pProperties->m_rtWidget.height);
+  return CFX_RectF(0, 0, m_WidgetRect.width, m_WidgetRect.height);
 }
 
-IFWL_ThemeProvider* CFWL_Widget::GetAvailableTheme() const {
-  if (m_pProperties->m_pThemeProvider)
-    return m_pProperties->m_pThemeProvider.Get();
-
-  const CFWL_Widget* pUp = this;
-  do {
-    pUp = pUp->IsPopup() ? m_pWidgetMgr->GetOwnerWidget(pUp)
-                         : m_pWidgetMgr->GetParentWidget(pUp);
-    if (pUp) {
-      IFWL_ThemeProvider* pRet = pUp->GetThemeProvider();
-      if (pRet)
-        return pRet;
-    }
-  } while (pUp);
-  return nullptr;
-}
-
-CFX_SizeF CFWL_Widget::CalcTextSize(const WideString& wsText,
-                                    IFWL_ThemeProvider* pTheme,
-                                    bool bMultiLine) {
-  if (!pTheme)
-    return CFX_SizeF();
-
-  CFWL_ThemeText calPart;
-  calPart.m_pWidget = this;
+CFX_SizeF CFWL_Widget::CalcTextSize(const WideString& wsText, bool bMultiLine) {
+  CFWL_ThemeText calPart(CFWL_ThemePart::Part::kNone, this, nullptr);
   calPart.m_wsText = wsText;
   if (bMultiLine)
     calPart.m_dwTTOStyles.line_wrap_ = true;
@@ -263,37 +236,30 @@
     calPart.m_dwTTOStyles.single_line_ = true;
 
   calPart.m_iTTOAlign = FDE_TextAlignment::kTopLeft;
-  float fWidth = bMultiLine ? FWL_WGT_CalcMultiLineDefWidth : FWL_WGT_CalcWidth;
-  CFX_RectF rect(0, 0, fWidth, FWL_WGT_CalcHeight);
-  pTheme->CalcTextRect(calPart, &rect);
+  float fWidth = bMultiLine ? kCalcMultiLineDefWidth : kCalcWidth;
+  CFX_RectF rect(0, 0, fWidth, kCalcHeight);
+  GetThemeProvider()->CalcTextRect(calPart, &rect);
   return CFX_SizeF(rect.width, rect.height);
 }
 
 void CFWL_Widget::CalcTextRect(const WideString& wsText,
-                               IFWL_ThemeProvider* pTheme,
                                const FDE_TextStyle& dwTTOStyles,
                                FDE_TextAlignment iTTOAlign,
                                CFX_RectF* pRect) {
-  CFWL_ThemeText calPart;
-  calPart.m_pWidget = this;
+  CFWL_ThemeText calPart(CFWL_ThemePart::Part::kNone, this, nullptr);
   calPart.m_wsText = wsText;
   calPart.m_dwTTOStyles = dwTTOStyles;
   calPart.m_iTTOAlign = iTTOAlign;
-  pTheme->CalcTextRect(calPart, pRect);
+  GetThemeProvider()->CalcTextRect(calPart, pRect);
 }
 
 void CFWL_Widget::SetGrab(bool bSet) {
-  CFWL_NoteDriver* pDriver = GetOwnerApp()->GetNoteDriver();
-  pDriver->SetGrab(this, bSet);
-}
-
-void CFWL_Widget::RegisterEventTarget(CFWL_Widget* pEventSource) {
-  CFWL_NoteDriver* pNoteDriver = GetOwnerApp()->GetNoteDriver();
-  pNoteDriver->RegisterEventTarget(this, pEventSource);
+  CFWL_NoteDriver* pDriver = GetFWLApp()->GetNoteDriver();
+  pDriver->SetGrab(bSet ? this : nullptr);
 }
 
 void CFWL_Widget::UnregisterEventTarget() {
-  CFWL_NoteDriver* pNoteDriver = GetOwnerApp()->GetNoteDriver();
+  CFWL_NoteDriver* pNoteDriver = GetFWLApp()->GetNoteDriver();
   pNoteDriver->UnregisterEventTarget(this);
 }
 
@@ -302,7 +268,7 @@
     m_pOuter->GetDelegate()->OnProcessEvent(pEvent);
     return;
   }
-  CFWL_NoteDriver* pNoteDriver = GetOwnerApp()->GetNoteDriver();
+  CFWL_NoteDriver* pNoteDriver = GetFWLApp()->GetNoteDriver();
   pNoteDriver->SendEvent(pEvent);
 }
 
@@ -310,35 +276,26 @@
   m_pWidgetMgr->RepaintWidget(this, pRect);
 }
 
-void CFWL_Widget::DrawBackground(CXFA_Graphics* pGraphics,
-                                 CFWL_Part iPartBk,
-                                 IFWL_ThemeProvider* pTheme,
-                                 const CFX_Matrix* pMatrix) {
-  CFWL_ThemeBackground param;
-  param.m_pWidget = this;
-  param.m_iPart = iPartBk;
-  param.m_pGraphics = pGraphics;
-  if (pMatrix)
-    param.m_matrix = *pMatrix;
-  param.m_rtPart = GetRelativeRect();
-  pTheme->DrawBackground(param);
+void CFWL_Widget::DrawBackground(CFGAS_GEGraphics* pGraphics,
+                                 CFWL_ThemePart::Part iPartBk,
+                                 const CFX_Matrix& mtMatrix) {
+  CFWL_ThemeBackground param(iPartBk, this, pGraphics);
+  param.m_matrix = mtMatrix;
+  param.m_PartRect = GetRelativeRect();
+  GetThemeProvider()->DrawBackground(param);
 }
 
-void CFWL_Widget::DrawBorder(CXFA_Graphics* pGraphics,
-                             CFWL_Part iPartBorder,
-                             IFWL_ThemeProvider* pTheme,
+void CFWL_Widget::DrawBorder(CFGAS_GEGraphics* pGraphics,
+                             CFWL_ThemePart::Part iPartBorder,
                              const CFX_Matrix& matrix) {
-  CFWL_ThemeBackground param;
-  param.m_pWidget = this;
-  param.m_iPart = iPartBorder;
-  param.m_pGraphics = pGraphics;
+  CFWL_ThemeBackground param(iPartBorder, this, pGraphics);
   param.m_matrix = matrix;
-  param.m_rtPart = GetRelativeRect();
-  pTheme->DrawBackground(param);
+  param.m_PartRect = GetRelativeRect();
+  GetThemeProvider()->DrawBackground(param);
 }
 
 void CFWL_Widget::NotifyDriver() {
-  CFWL_NoteDriver* pDriver = GetOwnerApp()->GetNoteDriver();
+  CFWL_NoteDriver* pDriver = GetFWLApp()->GetNoteDriver();
   pDriver->NotifyTargetDestroy(this);
 }
 
@@ -346,10 +303,8 @@
   if (pParent == this)
     return CFX_SizeF();
 
-  CFWL_WidgetMgr* pWidgetMgr = GetOwnerApp()->GetWidgetMgr();
-  CFX_SizeF szRet(m_pProperties->m_rtWidget.left,
-                  m_pProperties->m_rtWidget.top);
-
+  CFX_SizeF szRet(m_WidgetRect.left, m_WidgetRect.top);
+  CFWL_WidgetMgr* pWidgetMgr = GetFWLApp()->GetWidgetMgr();
   CFWL_Widget* pDstWidget = GetParent();
   while (pDstWidget && pDstWidget != pParent) {
     CFX_RectF rtDst = pDstWidget->GetWidgetRect();
@@ -375,10 +330,9 @@
     return;
 
   switch (pMessage->GetType()) {
-    case CFWL_Message::Type::Mouse: {
+    case CFWL_Message::Type::kMouse: {
       CFWL_MessageMouse* pMsgMouse = static_cast<CFWL_MessageMouse*>(pMessage);
-      CFWL_EventMouse evt(pWidget, pWidget);
-      evt.m_dwCmd = pMsgMouse->m_dwCmd;
+      CFWL_EventMouse evt(pWidget, pWidget, pMsgMouse->m_dwCmd);
       pWidget->DispatchEvent(&evt);
       break;
     }
@@ -389,9 +343,6 @@
 
 void CFWL_Widget::OnProcessEvent(CFWL_Event* pEvent) {}
 
-void CFWL_Widget::OnDrawWidget(CXFA_Graphics* pGraphics,
-                               const CFX_Matrix& matrix) {}
-
 CFWL_Widget::ScopedUpdateLock::ScopedUpdateLock(CFWL_Widget* widget)
     : widget_(widget) {
   widget_->LockUpdate();
diff --git a/xfa/fwl/cfwl_widget.h b/xfa/fwl/cfwl_widget.h
index a8112e8..8e61c80 100644
--- a/xfa/fwl/cfwl_widget.h
+++ b/xfa/fwl/cfwl_widget.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,27 +7,42 @@
 #ifndef XFA_FWL_CFWL_WIDGET_H_
 #define XFA_FWL_CFWL_WIDGET_H_
 
-#include <memory>
+#include <stdint.h>
 
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/macros.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/prefinalizer.h"
 #include "xfa/fde/cfde_data.h"
 #include "xfa/fwl/cfwl_themepart.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
-#include "xfa/fwl/cfwl_widgetproperties.h"
 #include "xfa/fwl/fwl_widgethit.h"
 #include "xfa/fwl/ifwl_widgetdelegate.h"
 
 class CFWL_App;
-class CFWL_AppImp;
 class CFWL_Event;
-class CFWL_MessageKey;
 class CFWL_Widget;
-class CFWL_WidgetMgr;
 class IFWL_ThemeProvider;
 
+#define FWL_STYLE_WGT_OverLapper 0
+#define FWL_STYLE_WGT_Popup (1L << 0)
+#define FWL_STYLE_WGT_Child (2L << 0)
+#define FWL_STYLE_WGT_WindowTypeMask (3L << 0)
+#define FWL_STYLE_WGT_Border (1L << 2)
+#define FWL_STYLE_WGT_VScroll (1L << 11)
+#define FWL_STYLE_WGT_Group (1L << 22)
+#define FWL_STYLE_WGT_NoBackground (1L << 28)
+
+#define FWL_STATE_WGT_Disabled (1L << 2)
+#define FWL_STATE_WGT_Focused (1L << 4)
+#define FWL_STATE_WGT_Invisible (1L << 5)
+#define FWL_STATE_WGT_MAX 6
+
 enum class FWL_Type {
   Unknown = 0,
 
@@ -49,72 +64,80 @@
 };
 
 // NOTE: CFWL_Widget serves as its own delegate until replaced at runtime.
-class CFWL_Widget : public Observable, public IFWL_WidgetDelegate {
+class CFWL_Widget : public cppgc::GarbageCollected<CFWL_Widget>,
+                    public IFWL_WidgetDelegate {
+  CPPGC_USING_PRE_FINALIZER(CFWL_Widget, PreFinalize);
+
  public:
-  class AdapterIface {
+  class AdapterIface : public cppgc::GarbageCollectedMixin {
    public:
-    virtual ~AdapterIface() {}
+    virtual ~AdapterIface() = default;
     virtual CFX_Matrix GetRotateMatrix() = 0;
     virtual void DisplayCaret(bool bVisible, const CFX_RectF* pRtAnchor) = 0;
     virtual void GetBorderColorAndThickness(FX_ARGB* cr, float* fWidth) = 0;
   };
 
+  class Properties {
+   public:
+    uint32_t m_dwStyles = FWL_STYLE_WGT_Child;  // Mask of FWL_STYLE_*_*.
+    uint32_t m_dwStyleExts = 0;                 // Mask of FWL_STYLEEXT_*_*.
+    uint32_t m_dwStates = 0;                    // Mask of FWL_STATE_*_*.
+  };
+
   class ScopedUpdateLock {
+    CPPGC_STACK_ALLOCATED();  // Allow raw/unowned pointers.
+
    public:
     explicit ScopedUpdateLock(CFWL_Widget* widget);
     ~ScopedUpdateLock();
 
    private:
-    UnownedPtr<CFWL_Widget> const widget_;
+    UnownedPtr<CFWL_Widget> const widget_;  // Ok, stack-only.
   };
 
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_Widget() override;
 
+  virtual void PreFinalize();
+  void Trace(cppgc::Visitor* visitor) const override;
+
   virtual FWL_Type GetClassID() const = 0;
   virtual bool IsForm() const;
   virtual CFX_RectF GetAutosizedWidgetRect();
   virtual CFX_RectF GetWidgetRect();
   virtual CFX_RectF GetClientRect();
-  virtual void ModifyStylesEx(uint32_t dwStylesExAdded,
-                              uint32_t dwStylesExRemoved);
+  virtual void ModifyStyleExts(uint32_t dwStyleExtsAdded,
+                               uint32_t dwStyleExtsRemoved);
   virtual void SetStates(uint32_t dwStates);
   virtual void RemoveStates(uint32_t dwStates);
   virtual void Update() = 0;
   virtual FWL_WidgetHit HitTest(const CFX_PointF& point);
-  virtual void DrawWidget(CXFA_Graphics* pGraphics,
+  virtual void DrawWidget(CFGAS_GEGraphics* pGraphics,
                           const CFX_Matrix& matrix) = 0;
-  virtual void SetThemeProvider(IFWL_ThemeProvider* pThemeProvider);
 
-  // IFWL_WidgetDelegate.
+  // IFWL_WidgetDelegate:
   void OnProcessMessage(CFWL_Message* pMessage) override;
   void OnProcessEvent(CFWL_Event* pEvent) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
-                    const CFX_Matrix& matrix) override;
 
   void InflateWidgetRect(CFX_RectF& rect);
   void SetWidgetRect(const CFX_RectF& rect);
 
-  void SetParent(CFWL_Widget* pParent);
-
   bool IsVisible() const;
   bool IsOverLapper() const;
   bool IsPopup() const;
   bool IsChild() const;
 
-  CFWL_Widget* GetOwner() { return m_pWidgetMgr->GetOwnerWidget(this); }
+  CFWL_WidgetMgr* GetWidgetMgr() const { return m_pWidgetMgr; }
   CFWL_Widget* GetOuter() const { return m_pOuter; }
   CFWL_Widget* GetOutmost() const;
 
   void ModifyStyles(uint32_t dwStylesAdded, uint32_t dwStylesRemoved);
-  uint32_t GetStylesEx() const;
-  uint32_t GetStates() const;
+  uint32_t GetStyleExts() const { return m_Properties.m_dwStyleExts; }
+  uint32_t GetStates() const { return m_Properties.m_dwStates; }
 
   CFX_PointF TransformTo(CFWL_Widget* pWidget, const CFX_PointF& point);
   CFX_Matrix GetMatrix() const;
-  IFWL_ThemeProvider* GetThemeProvider() const {
-    return m_pProperties->m_pThemeProvider.Get();
-  }
-
+  IFWL_ThemeProvider* GetThemeProvider() const;
   void SetDelegate(IFWL_WidgetDelegate* delegate) { m_pDelegate = delegate; }
   IFWL_WidgetDelegate* GetDelegate() {
     return m_pDelegate ? m_pDelegate.Get() : this;
@@ -123,18 +146,16 @@
     return m_pDelegate ? m_pDelegate.Get() : this;
   }
 
-  const CFWL_App* GetOwnerApp() const { return m_pOwnerApp.Get(); }
-  uint32_t GetEventKey() const { return m_nEventKey; }
-  void SetEventKey(uint32_t key) { m_nEventKey = key; }
+  CFWL_App* GetFWLApp() const { return m_pFWLApp; }
+  uint64_t GetEventKey() const { return m_nEventKey; }
+  void SetEventKey(uint64_t key) { m_nEventKey = key; }
 
   AdapterIface* GetAdapterIface() const { return m_pAdapterIface; }
   void SetAdapterIface(AdapterIface* pItem) { m_pAdapterIface = pItem; }
   void RepaintRect(const CFX_RectF& pRect);
 
  protected:
-  CFWL_Widget(const CFWL_App* app,
-              std::unique_ptr<CFWL_WidgetProperties> properties,
-              CFWL_Widget* pOuter);
+  CFWL_Widget(CFWL_App* app, const Properties& properties, CFWL_Widget* pOuter);
 
   bool IsEnabled() const;
   bool IsLocked() const { return m_iLock > 0; }
@@ -143,29 +164,20 @@
   float GetCXBorderSize() const;
   float GetCYBorderSize() const;
   CFX_RectF GetRelativeRect() const;
-  IFWL_ThemeProvider* GetAvailableTheme() const;
-  CFX_SizeF CalcTextSize(const WideString& wsText,
-                         IFWL_ThemeProvider* pTheme,
-                         bool bMultiLine);
+  CFX_SizeF CalcTextSize(const WideString& wsText, bool bMultiLine);
   void CalcTextRect(const WideString& wsText,
-                    IFWL_ThemeProvider* pTheme,
                     const FDE_TextStyle& dwTTOStyles,
                     FDE_TextAlignment iTTOAlign,
                     CFX_RectF* pRect);
   void SetGrab(bool bSet);
-  void RegisterEventTarget(CFWL_Widget* pEventSource);
   void UnregisterEventTarget();
   void DispatchEvent(CFWL_Event* pEvent);
-  void DrawBorder(CXFA_Graphics* pGraphics,
-                  CFWL_Part iPartBorder,
-                  IFWL_ThemeProvider* pTheme,
+  void DrawBorder(CFGAS_GEGraphics* pGraphics,
+                  CFWL_ThemePart::Part iPartBorder,
                   const CFX_Matrix& pMatrix);
 
-  UnownedPtr<const CFWL_App> const m_pOwnerApp;
-  UnownedPtr<CFWL_WidgetMgr> const m_pWidgetMgr;
-  std::unique_ptr<CFWL_WidgetProperties> m_pProperties;
-  CFWL_Widget* m_pOuter;
-  int32_t m_iLock = 0;
+  Properties m_Properties;
+  CFX_RectF m_WidgetRect;
 
  private:
   void LockUpdate() { m_iLock++; }
@@ -176,16 +188,19 @@
 
   CFWL_Widget* GetParent() const { return m_pWidgetMgr->GetParentWidget(this); }
   CFX_SizeF GetOffsetFromParent(CFWL_Widget* pParent);
-  void DrawBackground(CXFA_Graphics* pGraphics,
-                      CFWL_Part iPartBk,
-                      IFWL_ThemeProvider* pTheme,
-                      const CFX_Matrix* pMatrix);
+  void DrawBackground(CFGAS_GEGraphics* pGraphics,
+                      CFWL_ThemePart::Part iPartBk,
+                      const CFX_Matrix& mtMatrix);
   void NotifyDriver();
   bool IsParent(CFWL_Widget* pParent);
 
-  uint32_t m_nEventKey = 0;
-  AdapterIface* m_pAdapterIface = nullptr;
-  UnownedPtr<IFWL_WidgetDelegate> m_pDelegate;
+  int32_t m_iLock = 0;
+  uint64_t m_nEventKey = 0;
+  cppgc::Member<AdapterIface> m_pAdapterIface;
+  cppgc::Member<CFWL_App> const m_pFWLApp;
+  cppgc::Member<CFWL_WidgetMgr> const m_pWidgetMgr;
+  cppgc::Member<IFWL_WidgetDelegate> m_pDelegate;
+  cppgc::Member<CFWL_Widget> const m_pOuter;
 };
 
 #endif  // XFA_FWL_CFWL_WIDGET_H_
diff --git a/xfa/fwl/cfwl_widgetmgr.cpp b/xfa/fwl/cfwl_widgetmgr.cpp
index cb41430..b513415 100644
--- a/xfa/fwl/cfwl_widgetmgr.cpp
+++ b/xfa/fwl/cfwl_widgetmgr.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,69 +6,63 @@
 
 #include "xfa/fwl/cfwl_widgetmgr.h"
 
-#include <utility>
-
 #include "build/build_config.h"
-#include "third_party/base/ptr_util.h"
+#include "fxjs/gc/container_trace.h"
+#include "third_party/base/check.h"
 #include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_message.h"
 #include "xfa/fwl/cfwl_notedriver.h"
+#include "xfa/fwl/cfwl_pushbutton.h"
 
-CFWL_WidgetMgr::CFWL_WidgetMgr(AdapterIface* pAdapterNative)
-    : m_pAdapter(pAdapterNative) {
-  m_mapWidgetItem[nullptr] = pdfium::MakeUnique<Item>();
+CFWL_WidgetMgr::CFWL_WidgetMgr(AdapterIface* pAdapter, CFWL_App* pApp)
+    : m_pAdapter(pAdapter), m_pApp(pApp) {
+  DCHECK(m_pAdapter);
+  m_mapWidgetItem[nullptr] = cppgc::MakeGarbageCollected<Item>(
+      pApp->GetHeap()->GetAllocationHandle(), nullptr);
 }
 
 CFWL_WidgetMgr::~CFWL_WidgetMgr() = default;
 
-// static
-CFWL_Widget* CFWL_WidgetMgr::NextTab(CFWL_Widget* parent, CFWL_Widget* focus) {
-  CFWL_WidgetMgr* pMgr = parent->GetOwnerApp()->GetWidgetMgr();
-  CFWL_Widget* child = pMgr->GetFirstChildWidget(parent);
-  while (child) {
-    CFWL_Widget* bRet = NextTab(child, focus);
-    if (bRet)
-      return bRet;
-
-    child = pMgr->GetNextSiblingWidget(child);
-  }
-  return nullptr;
+void CFWL_WidgetMgr::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pApp);
+  visitor->Trace(m_pAdapter);
+  ContainerTrace(visitor, m_mapWidgetItem);
 }
 
 CFWL_Widget* CFWL_WidgetMgr::GetParentWidget(const CFWL_Widget* pWidget) const {
   Item* pItem = GetWidgetMgrItem(pWidget);
-  return pItem && pItem->pParent ? pItem->pParent->pWidget : nullptr;
-}
-
-CFWL_Widget* CFWL_WidgetMgr::GetOwnerWidget(const CFWL_Widget* pWidget) const {
-  Item* pItem = GetWidgetMgrItem(pWidget);
-  return pItem && pItem->pOwner ? pItem->pOwner->pWidget : nullptr;
-}
-
-CFWL_Widget* CFWL_WidgetMgr::GetFirstSiblingWidget(CFWL_Widget* pWidget) const {
-  Item* pItem = GetWidgetMgrItem(pWidget);
   if (!pItem)
     return nullptr;
 
-  pItem = pItem->pPrevious;
-  while (pItem && pItem->pPrevious)
-    pItem = pItem->pPrevious;
-  return pItem ? pItem->pWidget : nullptr;
+  Item* pParent = pItem->GetParent();
+  return pParent ? pParent->pWidget : nullptr;
 }
 
 CFWL_Widget* CFWL_WidgetMgr::GetPriorSiblingWidget(CFWL_Widget* pWidget) const {
   Item* pItem = GetWidgetMgrItem(pWidget);
-  return pItem && pItem->pPrevious ? pItem->pPrevious->pWidget : nullptr;
+  if (!pItem)
+    return nullptr;
+
+  Item* pSibling = pItem->GetPrevSibling();
+  return pSibling ? pSibling->pWidget : nullptr;
 }
 
 CFWL_Widget* CFWL_WidgetMgr::GetNextSiblingWidget(CFWL_Widget* pWidget) const {
   Item* pItem = GetWidgetMgrItem(pWidget);
-  return pItem && pItem->pNext ? pItem->pNext->pWidget : nullptr;
+  if (!pItem)
+    return nullptr;
+
+  Item* pSibling = pItem->GetNextSibling();
+  return pSibling ? pSibling->pWidget : nullptr;
 }
 
 CFWL_Widget* CFWL_WidgetMgr::GetFirstChildWidget(CFWL_Widget* pWidget) const {
   Item* pItem = GetWidgetMgrItem(pWidget);
-  return pItem && pItem->pChild ? pItem->pChild->pWidget : nullptr;
+  if (!pItem)
+    return nullptr;
+
+  Item* pChild = pItem->GetFirstChild();
+  return pChild ? pChild->pWidget : nullptr;
 }
 
 CFWL_Widget* CFWL_WidgetMgr::GetLastChildWidget(CFWL_Widget* pWidget) const {
@@ -76,63 +70,8 @@
   if (!pItem)
     return nullptr;
 
-  pItem = pItem->pChild;
-  while (pItem && pItem->pNext)
-    pItem = pItem->pNext;
-  return pItem ? pItem->pWidget : nullptr;
-}
-
-CFWL_Widget* CFWL_WidgetMgr::GetSystemFormWidget(CFWL_Widget* pWidget) const {
-  Item* pItem = GetWidgetMgrItem(pWidget);
-  while (pItem) {
-    if (IsAbleNative(pItem->pWidget))
-      return pItem->pWidget;
-    pItem = pItem->pParent;
-  }
-  return nullptr;
-}
-
-void CFWL_WidgetMgr::AppendWidget(CFWL_Widget* pWidget) {
-  Item* pItem = GetWidgetMgrItem(pWidget);
-  if (!pItem)
-    return;
-  if (!pItem->pParent)
-    return;
-
-  Item* pChild = pItem->pParent->pChild;
-  int32_t i = 0;
-  while (pChild) {
-    if (pChild == pItem) {
-      if (pChild->pPrevious)
-        pChild->pPrevious->pNext = pChild->pNext;
-      if (pChild->pNext)
-        pChild->pNext->pPrevious = pChild->pPrevious;
-      if (pItem->pParent->pChild == pItem)
-        pItem->pParent->pChild = pItem->pNext;
-
-      pItem->pNext = nullptr;
-      pItem->pPrevious = nullptr;
-      break;
-    }
-    if (!pChild->pNext)
-      break;
-
-    pChild = pChild->pNext;
-    ++i;
-  }
-
-  pChild = pItem->pParent->pChild;
-  if (pChild) {
-    while (pChild->pNext)
-      pChild = pChild->pNext;
-
-    pChild->pNext = pItem;
-    pItem->pPrevious = pChild;
-  } else {
-    pItem->pParent->pChild = pItem;
-    pItem->pPrevious = nullptr;
-  }
-  pItem->pNext = nullptr;
+  Item* pChild = pItem->GetLastChild();
+  return pChild ? pChild->pWidget : nullptr;
 }
 
 void CFWL_WidgetMgr::RepaintWidget(CFWL_Widget* pWidget,
@@ -147,98 +86,34 @@
     pNative = pOuter;
     pOuter = pOuter->GetOuter();
   }
-  AddRedrawCounts(pNative);
   m_pAdapter->RepaintWidget(pNative);
 }
 
 void CFWL_WidgetMgr::InsertWidget(CFWL_Widget* pParent, CFWL_Widget* pChild) {
   Item* pParentItem = GetWidgetMgrItem(pParent);
   if (!pParentItem) {
-    auto item = pdfium::MakeUnique<Item>(pParent);
-    pParentItem = item.get();
-    m_mapWidgetItem[pParent] = std::move(item);
-
-    pParentItem->pParent = GetWidgetMgrItem(nullptr);
-    AppendWidget(pParent);
+    pParentItem = CreateWidgetMgrItem(pParent);
+    GetWidgetMgrRootItem()->AppendLastChild(pParentItem);
   }
-
-  Item* pItem = GetWidgetMgrItem(pChild);
-  if (!pItem) {
-    auto item = pdfium::MakeUnique<Item>(pChild);
-    pItem = item.get();
-    m_mapWidgetItem[pChild] = std::move(item);
-  }
-  if (pItem->pParent && pItem->pParent != pParentItem) {
-    if (pItem->pPrevious)
-      pItem->pPrevious->pNext = pItem->pNext;
-    if (pItem->pNext)
-      pItem->pNext->pPrevious = pItem->pPrevious;
-    if (pItem->pParent->pChild == pItem)
-      pItem->pParent->pChild = pItem->pNext;
-  }
-  pItem->pParent = pParentItem;
-  AppendWidget(pChild);
+  Item* pChildItem = GetWidgetMgrItem(pChild);
+  if (!pChildItem)
+    pChildItem = CreateWidgetMgrItem(pChild);
+  pParentItem->AppendLastChild(pChildItem);
 }
 
 void CFWL_WidgetMgr::RemoveWidget(CFWL_Widget* pWidget) {
+  DCHECK(pWidget);
   Item* pItem = GetWidgetMgrItem(pWidget);
   if (!pItem)
     return;
-  if (pItem->pPrevious)
-    pItem->pPrevious->pNext = pItem->pNext;
-  if (pItem->pNext)
-    pItem->pNext->pPrevious = pItem->pPrevious;
-  if (pItem->pParent && pItem->pParent->pChild == pItem)
-    pItem->pParent->pChild = pItem->pNext;
 
-  Item* pChild = pItem->pChild;
-  while (pChild) {
-    Item* pNext = pChild->pNext;
-    RemoveWidget(pChild->pWidget);
-    pChild = pNext;
-  }
+  while (pItem->GetFirstChild())
+    RemoveWidget(pItem->GetFirstChild()->pWidget);
+
+  pItem->RemoveSelfIfParented();
   m_mapWidgetItem.erase(pWidget);
 }
 
-void CFWL_WidgetMgr::SetOwner(CFWL_Widget* pOwner, CFWL_Widget* pOwned) {
-  Item* pParentItem = GetWidgetMgrItem(pOwner);
-  if (!pParentItem) {
-    auto item = pdfium::MakeUnique<Item>(pOwner);
-    pParentItem = item.get();
-    m_mapWidgetItem[pOwner] = std::move(item);
-
-    pParentItem->pParent = GetWidgetMgrItem(nullptr);
-    AppendWidget(pOwner);
-  }
-
-  Item* pItem = GetWidgetMgrItem(pOwned);
-  if (!pItem) {
-    auto item = pdfium::MakeUnique<Item>(pOwned);
-    pItem = item.get();
-    m_mapWidgetItem[pOwned] = std::move(item);
-  }
-  pItem->pOwner = pParentItem;
-}
-void CFWL_WidgetMgr::SetParent(CFWL_Widget* pParent, CFWL_Widget* pChild) {
-  Item* pParentItem = GetWidgetMgrItem(pParent);
-  Item* pItem = GetWidgetMgrItem(pChild);
-  if (!pItem)
-    return;
-  if (pItem->pParent && pItem->pParent != pParentItem) {
-    if (pItem->pPrevious)
-      pItem->pPrevious->pNext = pItem->pNext;
-    if (pItem->pNext)
-      pItem->pNext->pPrevious = pItem->pPrevious;
-    if (pItem->pParent->pChild == pItem)
-      pItem->pParent->pChild = pItem->pNext;
-
-    pItem->pNext = nullptr;
-    pItem->pPrevious = nullptr;
-  }
-  pItem->pParent = pParentItem;
-  AppendWidget(pChild);
-}
-
 CFWL_Widget* CFWL_WidgetMgr::GetWidgetAtPoint(CFWL_Widget* parent,
                                               const CFX_PointF& point) const {
   if (!parent)
@@ -260,45 +135,41 @@
 }
 
 CFWL_Widget* CFWL_WidgetMgr::GetDefaultButton(CFWL_Widget* pParent) const {
-  if ((pParent->GetClassID() == FWL_Type::PushButton) &&
-      (pParent->GetStates() & (1 << (FWL_WGTSTATE_MAX + 2)))) {
+  if (pParent->GetClassID() == FWL_Type::PushButton &&
+      (pParent->GetStates() & FWL_STATE_PSB_Default)) {
     return pParent;
   }
 
-  CFWL_Widget* child =
-      pParent->GetOwnerApp()->GetWidgetMgr()->GetFirstChildWidget(pParent);
+  CFWL_Widget* child = GetFirstChildWidget(pParent);
   while (child) {
-    if ((child->GetClassID() == FWL_Type::PushButton) &&
-        (child->GetStates() & (1 << (FWL_WGTSTATE_MAX + 2)))) {
+    if (child->GetClassID() == FWL_Type::PushButton &&
+        (child->GetStates() & FWL_STATE_PSB_Default)) {
       return child;
     }
     if (CFWL_Widget* find = GetDefaultButton(child))
       return find;
 
-    child = child->GetOwnerApp()->GetWidgetMgr()->GetNextSiblingWidget(child);
+    child = GetNextSiblingWidget(child);
   }
   return nullptr;
 }
 
-void CFWL_WidgetMgr::AddRedrawCounts(CFWL_Widget* pWidget) {
-  GetWidgetMgrItem(pWidget)->iRedrawCounter++;
-}
-
-void CFWL_WidgetMgr::ResetRedrawCounts(CFWL_Widget* pWidget) {
-  GetWidgetMgrItem(pWidget)->iRedrawCounter = 0;
+CFWL_WidgetMgr::Item* CFWL_WidgetMgr::GetWidgetMgrRootItem() const {
+  return GetWidgetMgrItem(nullptr);
 }
 
 CFWL_WidgetMgr::Item* CFWL_WidgetMgr::GetWidgetMgrItem(
     const CFWL_Widget* pWidget) const {
   auto it = m_mapWidgetItem.find(pWidget);
-  return it != m_mapWidgetItem.end() ? it->second.get() : nullptr;
+  return it != m_mapWidgetItem.end() ? it->second : nullptr;
 }
 
-bool CFWL_WidgetMgr::IsAbleNative(CFWL_Widget* pWidget) const {
-  if (!pWidget || !pWidget->IsForm())
-    return false;
-
-  return pWidget->IsOverLapper() || pWidget->IsPopup();
+CFWL_WidgetMgr::Item* CFWL_WidgetMgr::CreateWidgetMgrItem(
+    CFWL_Widget* pWidget) {
+  auto* pItem = cppgc::MakeGarbageCollected<Item>(
+      m_pApp->GetHeap()->GetAllocationHandle(), pWidget);
+  m_mapWidgetItem[pWidget] = pItem;
+  return pItem;
 }
 
 void CFWL_WidgetMgr::GetAdapterPopupPos(CFWL_Widget* pWidget,
@@ -310,40 +181,32 @@
                           pPopupRect);
 }
 
-void CFWL_WidgetMgr::OnProcessMessageToForm(
-    std::unique_ptr<CFWL_Message> pMessage) {
+void CFWL_WidgetMgr::OnProcessMessageToForm(CFWL_Message* pMessage) {
   CFWL_Widget* pDstWidget = pMessage->GetDstTarget();
   if (!pDstWidget)
     return;
 
-  CFWL_NoteDriver* pNoteDriver = pDstWidget->GetOwnerApp()->GetNoteDriver();
-  pNoteDriver->ProcessMessage(std::move(pMessage));
+  CFWL_NoteDriver* pNoteDriver = pDstWidget->GetFWLApp()->GetNoteDriver();
+  pNoteDriver->ProcessMessage(pMessage);
 }
 
 void CFWL_WidgetMgr::OnDrawWidget(CFWL_Widget* pWidget,
-                                  CXFA_Graphics* pGraphics,
+                                  CFGAS_GEGraphics* pGraphics,
                                   const CFX_Matrix& matrix) {
   if (!pWidget || !pGraphics)
     return;
 
-  CFX_RectF clipCopy(0, 0, pWidget->GetWidgetRect().Size());
-  CFX_RectF clipBounds;
-
   pWidget->GetDelegate()->OnDrawWidget(pGraphics, matrix);
-  clipBounds = pGraphics->GetClipRect();
-  clipCopy = clipBounds;
 
+  CFX_RectF clipBounds = pGraphics->GetClipRect();
   if (!clipBounds.IsEmpty())
-    DrawChild(pWidget, clipBounds, pGraphics, &matrix);
-
-  GetWidgetMgrItem(pWidget)->iRedrawCounter = 0;
-  ResetRedrawCounts(pWidget);
+    DrawChildren(pWidget, clipBounds, pGraphics, matrix);
 }
 
-void CFWL_WidgetMgr::DrawChild(CFWL_Widget* parent,
-                               const CFX_RectF& rtClip,
-                               CXFA_Graphics* pGraphics,
-                               const CFX_Matrix* pMatrix) {
+void CFWL_WidgetMgr::DrawChildren(CFWL_Widget* parent,
+                                  const CFX_RectF& rtClip,
+                                  CFGAS_GEGraphics* pGraphics,
+                                  const CFX_Matrix& mtMatrix) {
   if (!parent)
     return;
 
@@ -360,27 +223,21 @@
 
     CFX_Matrix widgetMatrix;
     CFX_RectF clipBounds(rtWidget);
-    if (pMatrix)
-      widgetMatrix.Concat(*pMatrix);
-
+    widgetMatrix.Concat(mtMatrix);
     widgetMatrix.TranslatePrepend(rtWidget.left, rtWidget.top);
 
     if (IFWL_WidgetDelegate* pDelegate = child->GetDelegate())
       pDelegate->OnDrawWidget(pGraphics, widgetMatrix);
 
-    DrawChild(child, clipBounds, pGraphics, &widgetMatrix);
+    DrawChildren(child, clipBounds, pGraphics, widgetMatrix);
   }
 }
 
-CFWL_WidgetMgr::Item::Item() : CFWL_WidgetMgr::Item(nullptr) {}
+CFWL_WidgetMgr::Item::Item(CFWL_Widget* widget) : pWidget(widget) {}
 
-CFWL_WidgetMgr::Item::Item(CFWL_Widget* widget)
-    : pParent(nullptr),
-      pOwner(nullptr),
-      pChild(nullptr),
-      pPrevious(nullptr),
-      pNext(nullptr),
-      pWidget(widget),
-      iRedrawCounter(0) {}
+CFWL_WidgetMgr::Item::~Item() = default;
 
-CFWL_WidgetMgr::Item::~Item() {}
+void CFWL_WidgetMgr::Item::Trace(cppgc::Visitor* visitor) const {
+  GCedTreeNode<Item>::Trace(visitor);
+  visitor->Trace(pWidget);
+}
diff --git a/xfa/fwl/cfwl_widgetmgr.h b/xfa/fwl/cfwl_widgetmgr.h
index a401e22..8383f0f 100644
--- a/xfa/fwl/cfwl_widgetmgr.h
+++ b/xfa/fwl/cfwl_widgetmgr.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,22 +8,24 @@
 #define XFA_FWL_CFWL_WIDGETMGR_H_
 
 #include <map>
-#include <memory>
-#include <vector>
 
-#include "core/fxcrt/fx_system.h"
-#include "xfa/fxgraphics/cxfa_graphics.h"
+#include "core/fxcrt/fx_coordinates.h"
+#include "fxjs/gc/gced_tree_node.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
 
+class CFGAS_GEGraphics;
+class CFWL_App;
 class CFWL_Message;
-class CXFA_Graphics;
-class CFX_Matrix;
 class CFWL_Widget;
 
-class CFWL_WidgetMgr {
+class CFWL_WidgetMgr final : public cppgc::GarbageCollected<CFWL_WidgetMgr> {
  public:
-  class AdapterIface {
+  class AdapterIface : public cppgc::GarbageCollectedMixin {
    public:
-    virtual ~AdapterIface() {}
+    virtual ~AdapterIface() = default;
     virtual void RepaintWidget(CFWL_Widget* pWidget) = 0;
     virtual bool GetPopupPos(CFWL_Widget* pWidget,
                              float fMinHeight,
@@ -32,35 +34,29 @@
                              CFX_RectF* pPopupRect) = 0;
   };
 
-  explicit CFWL_WidgetMgr(AdapterIface* pAdapterNative);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_WidgetMgr();
 
-  static CFWL_Widget* NextTab(CFWL_Widget* parent, CFWL_Widget* focus);
+  void Trace(cppgc::Visitor* visitor) const;
 
-  void OnProcessMessageToForm(std::unique_ptr<CFWL_Message> pMessage);
+  void OnProcessMessageToForm(CFWL_Message* pMessage);
   void OnDrawWidget(CFWL_Widget* pWidget,
-                    CXFA_Graphics* pGraphics,
+                    CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix);
 
   CFWL_Widget* GetParentWidget(const CFWL_Widget* pWidget) const;
-  CFWL_Widget* GetOwnerWidget(const CFWL_Widget* pWidget) const;
   CFWL_Widget* GetNextSiblingWidget(CFWL_Widget* pWidget) const;
   CFWL_Widget* GetFirstChildWidget(CFWL_Widget* pWidget) const;
-  CFWL_Widget* GetSystemFormWidget(CFWL_Widget* pWidget) const;
 
   void RepaintWidget(CFWL_Widget* pWidget, const CFX_RectF& pRect);
 
   void InsertWidget(CFWL_Widget* pParent, CFWL_Widget* pChild);
   void RemoveWidget(CFWL_Widget* pWidget);
-  void SetOwner(CFWL_Widget* pOwner, CFWL_Widget* pOwned);
-  void SetParent(CFWL_Widget* pParent, CFWL_Widget* pChild);
 
   CFWL_Widget* GetWidgetAtPoint(CFWL_Widget* pParent,
                                 const CFX_PointF& point) const;
 
   CFWL_Widget* GetDefaultButton(CFWL_Widget* pParent) const;
-  void AddRedrawCounts(CFWL_Widget* pWidget);
-
   void GetAdapterPopupPos(CFWL_Widget* pWidget,
                           float fMinHeight,
                           float fMaxHeight,
@@ -68,38 +64,38 @@
                           CFX_RectF* pPopupRect) const;
 
  private:
-  class Item {
+  class Item final : public GCedTreeNode<Item> {
    public:
-    Item();
-    explicit Item(CFWL_Widget* widget);
-    ~Item();
+    CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+    ~Item() final;
 
-    Item* pParent;
-    Item* pOwner;
-    Item* pChild;
-    Item* pPrevious;
-    Item* pNext;
-    CFWL_Widget* const pWidget;
-    std::unique_ptr<CXFA_Graphics> pOffscreen;
-    int32_t iRedrawCounter;
+    // GcedTreeNode:
+    void Trace(cppgc::Visitor* visitor) const override;
+
+    cppgc::Member<CFWL_Widget> const pWidget;
+
+   private:
+    explicit Item(CFWL_Widget* widget);
   };
 
-  CFWL_Widget* GetFirstSiblingWidget(CFWL_Widget* pWidget) const;
+  CFWL_WidgetMgr(AdapterIface* pAdapter, CFWL_App* pApp);
+
   CFWL_Widget* GetPriorSiblingWidget(CFWL_Widget* pWidget) const;
   CFWL_Widget* GetLastChildWidget(CFWL_Widget* pWidget) const;
+
+  Item* GetWidgetMgrRootItem() const;
   Item* GetWidgetMgrItem(const CFWL_Widget* pWidget) const;
+  Item* CreateWidgetMgrItem(CFWL_Widget* pWidget);
 
-  void AppendWidget(CFWL_Widget* pWidget);
-  void ResetRedrawCounts(CFWL_Widget* pWidget);
-  void DrawChild(CFWL_Widget* pParent,
-                 const CFX_RectF& rtClip,
-                 CXFA_Graphics* pGraphics,
-                 const CFX_Matrix* pMatrix);
+  void DrawChildren(CFWL_Widget* pParent,
+                    const CFX_RectF& rtClip,
+                    CFGAS_GEGraphics* pGraphics,
+                    const CFX_Matrix& mtMatrix);
 
-  bool IsAbleNative(CFWL_Widget* pWidget) const;
-
-  std::map<const CFWL_Widget*, std::unique_ptr<Item>> m_mapWidgetItem;
-  UnownedPtr<AdapterIface> const m_pAdapter;
+  cppgc::Member<AdapterIface> const m_pAdapter;
+  cppgc::Member<CFWL_App> const m_pApp;
+  std::map<cppgc::Member<const CFWL_Widget>, cppgc::Member<Item>>
+      m_mapWidgetItem;
 };
 
 #endif  // XFA_FWL_CFWL_WIDGETMGR_H_
diff --git a/xfa/fwl/cfwl_widgetproperties.cpp b/xfa/fwl/cfwl_widgetproperties.cpp
deleted file mode 100644
index 4dc3cd5..0000000
--- a/xfa/fwl/cfwl_widgetproperties.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fwl/cfwl_widgetproperties.h"
-
-CFWL_WidgetProperties::CFWL_WidgetProperties() = default;
-
-CFWL_WidgetProperties::~CFWL_WidgetProperties() = default;
diff --git a/xfa/fwl/cfwl_widgetproperties.h b/xfa/fwl/cfwl_widgetproperties.h
deleted file mode 100644
index 4148833..0000000
--- a/xfa/fwl/cfwl_widgetproperties.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FWL_CFWL_WIDGETPROPERTIES_H_
-#define XFA_FWL_CFWL_WIDGETPROPERTIES_H_
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "xfa/fwl/fwl_widgetdef.h"
-
-class CFWL_Widget;
-class IFWL_ThemeProvider;
-
-class CFWL_WidgetProperties {
- public:
-  CFWL_WidgetProperties();
-  ~CFWL_WidgetProperties();
-
-  uint32_t m_dwStyles = FWL_WGTSTYLE_Child;
-  uint32_t m_dwStyleExes = 0;
-  uint32_t m_dwStates = 0;
-  CFX_RectF m_rtWidget;
-  UnownedPtr<IFWL_ThemeProvider> m_pThemeProvider;
-  CFWL_Widget* m_pParent = nullptr;  // Raw, this class owned by node in tree.
-};
-
-#endif  // XFA_FWL_CFWL_WIDGETPROPERTIES_H_
diff --git a/xfa/fwl/fwl_widgetdef.h b/xfa/fwl/fwl_widgetdef.h
index 19eb508..a44a442 100644
--- a/xfa/fwl/fwl_widgetdef.h
+++ b/xfa/fwl/fwl_widgetdef.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,22 +7,10 @@
 #ifndef XFA_FWL_FWL_WIDGETDEF_H_
 #define XFA_FWL_FWL_WIDGETDEF_H_
 
-#define FWL_WGTSTYLE_OverLapper 0
-#define FWL_WGTSTYLE_Popup (1L << 0)
-#define FWL_WGTSTYLE_Child (2L << 0)
-#define FWL_WGTSTYLE_WindowTypeMask (3L << 0)
-#define FWL_WGTSTYLE_Border (1L << 2)
-#define FWL_WGTSTYLE_VScroll (1L << 11)
-#define FWL_WGTSTYLE_Group (1L << 22)
-#define FWL_WGTSTYLE_NoBackground (1L << 28)
+#include <stdint.h>
 
-#define FWL_WGTSTATE_Disabled (1L << 2)
-#define FWL_WGTSTATE_Focused (1L << 4)
-#define FWL_WGTSTATE_Invisible (1L << 5)
-#define FWL_WGTSTATE_MAX (6)
-
-// Same as enum FWL_VKEYCODE in public/fpdf_fwlevent.h, but duplicated to keep
-// xfa/fwl standalone.
+// Same as enum FWL_VKEYCODE in public/fpdf_fwlevent.h, but duplicated here
+// to keep xfa/fwl standalone.
 enum XFA_FWL_VKEYCODE {
   XFA_FWL_VKEY_Back = 0x08,
   XFA_FWL_VKEY_Tab = 0x09,
@@ -195,4 +183,16 @@
   XFA_FWL_VKEY_Unknown = 0,
 };
 
+// Derived from FWL_EVENTFLAG in public/fwl_event.h, but not the same
+// values bit-for-bit, duplicated here to keep XFA standalone.
+enum class XFA_FWL_KeyFlag : uint8_t {
+  kCtrl = 1 << 0,
+  kAlt = 1 << 1,
+  kShift = 1 << 2,
+  kCommand = 1 << 3,
+  kLButton = 1 << 4,
+  kRButton = 1 << 5,
+  kMButton = 1 << 6
+};
+
 #endif  // XFA_FWL_FWL_WIDGETDEF_H_
diff --git a/xfa/fwl/fwl_widgethit.h b/xfa/fwl/fwl_widgethit.h
index ff5a806..2de257f 100644
--- a/xfa/fwl/fwl_widgethit.h
+++ b/xfa/fwl/fwl_widgethit.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fwl/ifwl_themeprovider.cpp b/xfa/fwl/ifwl_themeprovider.cpp
new file mode 100644
index 0000000..e98a3f1
--- /dev/null
+++ b/xfa/fwl/ifwl_themeprovider.cpp
@@ -0,0 +1,89 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fwl/ifwl_themeprovider.h"
+
+#include "xfa/fwl/cfwl_widget.h"
+#include "xfa/fwl/theme/cfwl_barcodetp.h"
+#include "xfa/fwl/theme/cfwl_carettp.h"
+#include "xfa/fwl/theme/cfwl_checkboxtp.h"
+#include "xfa/fwl/theme/cfwl_comboboxtp.h"
+#include "xfa/fwl/theme/cfwl_datetimepickertp.h"
+#include "xfa/fwl/theme/cfwl_edittp.h"
+#include "xfa/fwl/theme/cfwl_listboxtp.h"
+#include "xfa/fwl/theme/cfwl_monthcalendartp.h"
+#include "xfa/fwl/theme/cfwl_pictureboxtp.h"
+#include "xfa/fwl/theme/cfwl_pushbuttontp.h"
+#include "xfa/fwl/theme/cfwl_scrollbartp.h"
+
+IFWL_ThemeProvider::IFWL_ThemeProvider(cppgc::Heap* pHeap)
+    : m_pCheckBoxTP(cppgc::MakeGarbageCollected<CFWL_CheckBoxTP>(
+          pHeap->GetAllocationHandle())),
+      m_pListBoxTP(cppgc::MakeGarbageCollected<CFWL_ListBoxTP>(
+          pHeap->GetAllocationHandle())),
+      m_pPictureBoxTP(cppgc::MakeGarbageCollected<CFWL_PictureBoxTP>(
+          pHeap->GetAllocationHandle())),
+      m_pSrollBarTP(cppgc::MakeGarbageCollected<CFWL_ScrollBarTP>(
+          pHeap->GetAllocationHandle())),
+      m_pEditTP(cppgc::MakeGarbageCollected<CFWL_EditTP>(
+          pHeap->GetAllocationHandle())),
+      m_pComboBoxTP(cppgc::MakeGarbageCollected<CFWL_ComboBoxTP>(
+          pHeap->GetAllocationHandle())),
+      m_pMonthCalendarTP(cppgc::MakeGarbageCollected<CFWL_MonthCalendarTP>(
+          pHeap->GetAllocationHandle())),
+      m_pDateTimePickerTP(cppgc::MakeGarbageCollected<CFWL_DateTimePickerTP>(
+          pHeap->GetAllocationHandle())),
+      m_pPushButtonTP(cppgc::MakeGarbageCollected<CFWL_PushButtonTP>(
+          pHeap->GetAllocationHandle())),
+      m_pCaretTP(cppgc::MakeGarbageCollected<CFWL_CaretTP>(
+          pHeap->GetAllocationHandle())),
+      m_pBarcodeTP(cppgc::MakeGarbageCollected<CFWL_BarcodeTP>(
+          pHeap->GetAllocationHandle())) {}
+
+IFWL_ThemeProvider::~IFWL_ThemeProvider() = default;
+
+void IFWL_ThemeProvider::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pCheckBoxTP);
+  visitor->Trace(m_pListBoxTP);
+  visitor->Trace(m_pPictureBoxTP);
+  visitor->Trace(m_pSrollBarTP);
+  visitor->Trace(m_pEditTP);
+  visitor->Trace(m_pComboBoxTP);
+  visitor->Trace(m_pMonthCalendarTP);
+  visitor->Trace(m_pDateTimePickerTP);
+  visitor->Trace(m_pPushButtonTP);
+  visitor->Trace(m_pCaretTP);
+  visitor->Trace(m_pBarcodeTP);
+}
+
+CFWL_WidgetTP* IFWL_ThemeProvider::GetTheme(const CFWL_Widget* pWidget) const {
+  switch (pWidget->GetClassID()) {
+    case FWL_Type::CheckBox:
+      return m_pCheckBoxTP;
+    case FWL_Type::ListBox:
+      return m_pListBoxTP;
+    case FWL_Type::PictureBox:
+      return m_pPictureBoxTP;
+    case FWL_Type::ScrollBar:
+      return m_pSrollBarTP;
+    case FWL_Type::Edit:
+      return m_pEditTP;
+    case FWL_Type::ComboBox:
+      return m_pComboBoxTP;
+    case FWL_Type::MonthCalendar:
+      return m_pMonthCalendarTP;
+    case FWL_Type::DateTimePicker:
+      return m_pDateTimePickerTP;
+    case FWL_Type::PushButton:
+      return m_pPushButtonTP;
+    case FWL_Type::Caret:
+      return m_pCaretTP;
+    case FWL_Type::Barcode:
+      return m_pBarcodeTP;
+    default:
+      return nullptr;
+  }
+}
diff --git a/xfa/fwl/ifwl_themeprovider.h b/xfa/fwl/ifwl_themeprovider.h
index 5700d34..5a78394 100644
--- a/xfa/fwl/ifwl_themeprovider.h
+++ b/xfa/fwl/ifwl_themeprovider.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,17 +9,24 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
 
 class CFGAS_GEFont;
 class CFWL_ThemeBackground;
 class CFWL_ThemePart;
 class CFWL_ThemeText;
 class CFWL_Widget;
+class CFWL_WidgetTP;
 
-class IFWL_ThemeProvider {
+class IFWL_ThemeProvider : public cppgc::GarbageCollectedMixin {
  public:
-  virtual ~IFWL_ThemeProvider() = default;
+  virtual ~IFWL_ThemeProvider();
+
+  // GarbageCollectedMixin:
+  void Trace(cppgc::Visitor* visitor) const override;
 
   virtual void DrawBackground(const CFWL_ThemeBackground& pParams) = 0;
   virtual void DrawText(const CFWL_ThemeText& pParams) = 0;
@@ -29,13 +36,31 @@
   virtual float GetCYBorderSize() const = 0;
   virtual CFX_RectF GetUIMargin(const CFWL_ThemePart& pThemePart) const = 0;
   virtual float GetFontSize(const CFWL_ThemePart& pThemePart) const = 0;
-  virtual RetainPtr<CFGAS_GEFont> GetFont(
-      const CFWL_ThemePart& pThemePart) const = 0;
+  virtual RetainPtr<CFGAS_GEFont> GetFont(const CFWL_ThemePart& pThemePart) = 0;
+  virtual RetainPtr<CFGAS_GEFont> GetFWLFont() = 0;
   virtual float GetLineHeight(const CFWL_ThemePart& pThemePart) const = 0;
   virtual float GetScrollBarWidth() const = 0;
   virtual FX_COLORREF GetTextColor(const CFWL_ThemePart& pThemePart) const = 0;
   virtual CFX_SizeF GetSpaceAboveBelow(
       const CFWL_ThemePart& pThemePart) const = 0;
+
+ protected:
+  explicit IFWL_ThemeProvider(cppgc::Heap* pHeap);
+
+  CFWL_WidgetTP* GetTheme(const CFWL_Widget* pWidget) const;
+
+ private:
+  cppgc::Member<CFWL_WidgetTP> m_pCheckBoxTP;
+  cppgc::Member<CFWL_WidgetTP> m_pListBoxTP;
+  cppgc::Member<CFWL_WidgetTP> m_pPictureBoxTP;
+  cppgc::Member<CFWL_WidgetTP> m_pSrollBarTP;
+  cppgc::Member<CFWL_WidgetTP> m_pEditTP;
+  cppgc::Member<CFWL_WidgetTP> m_pComboBoxTP;
+  cppgc::Member<CFWL_WidgetTP> m_pMonthCalendarTP;
+  cppgc::Member<CFWL_WidgetTP> m_pDateTimePickerTP;
+  cppgc::Member<CFWL_WidgetTP> m_pPushButtonTP;
+  cppgc::Member<CFWL_WidgetTP> m_pCaretTP;
+  cppgc::Member<CFWL_WidgetTP> m_pBarcodeTP;
 };
 
 #endif  // XFA_FWL_IFWL_THEMEPROVIDER_H_
diff --git a/xfa/fwl/ifwl_widgetdelegate.h b/xfa/fwl/ifwl_widgetdelegate.h
index 17b8933..ef0895f 100644
--- a/xfa/fwl/ifwl_widgetdelegate.h
+++ b/xfa/fwl/ifwl_widgetdelegate.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,20 +7,20 @@
 #ifndef XFA_FWL_IFWL_WIDGETDELEGATE_H_
 #define XFA_FWL_IFWL_WIDGETDELEGATE_H_
 
-#include <stdint.h>
+#include "v8/include/cppgc/garbage-collected.h"
 
+class CFGAS_GEGraphics;
 class CFWL_Event;
 class CFWL_Message;
-class CXFA_Graphics;
 class CFX_Matrix;
 
-class IFWL_WidgetDelegate {
+class IFWL_WidgetDelegate : public cppgc::GarbageCollectedMixin {
  public:
   virtual ~IFWL_WidgetDelegate() = default;
 
   virtual void OnProcessMessage(CFWL_Message* pMessage) = 0;
   virtual void OnProcessEvent(CFWL_Event* pEvent) = 0;
-  virtual void OnDrawWidget(CXFA_Graphics* pGraphics,
+  virtual void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                             const CFX_Matrix& matrix) = 0;
 };
 
diff --git a/xfa/fwl/theme/cfwl_barcodetp.cpp b/xfa/fwl/theme/cfwl_barcodetp.cpp
index 222d008..60f5154 100644
--- a/xfa/fwl/theme/cfwl_barcodetp.cpp
+++ b/xfa/fwl/theme/cfwl_barcodetp.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,17 +10,17 @@
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_widget.h"
 
-CFWL_BarcodeTP::CFWL_BarcodeTP() {}
+CFWL_BarcodeTP::CFWL_BarcodeTP() = default;
 
-CFWL_BarcodeTP::~CFWL_BarcodeTP() {}
+CFWL_BarcodeTP::~CFWL_BarcodeTP() = default;
 
 void CFWL_BarcodeTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
-  switch (pParams.m_iPart) {
-    case CFWL_Part::Border:
-      DrawBorder(pParams.m_pGraphics.Get(), pParams.m_rtPart, pParams.m_matrix);
+  switch (pParams.GetPart()) {
+    case CFWL_ThemePart::Part::kBorder:
+      DrawBorder(pParams.GetGraphics(), pParams.m_PartRect, pParams.m_matrix);
       break;
-    case CFWL_Part::Background:
-      FillBackground(pParams.m_pGraphics.Get(), pParams.m_rtPart,
+    case CFWL_ThemePart::Part::kBackground:
+      FillBackground(pParams.GetGraphics(), pParams.m_PartRect,
                      pParams.m_matrix);
       break;
     default:
diff --git a/xfa/fwl/theme/cfwl_barcodetp.h b/xfa/fwl/theme/cfwl_barcodetp.h
index 8b88fcd..e35b9cd 100644
--- a/xfa/fwl/theme/cfwl_barcodetp.h
+++ b/xfa/fwl/theme/cfwl_barcodetp.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,20 @@
 #ifndef XFA_FWL_THEME_CFWL_BARCODETP_H_
 #define XFA_FWL_THEME_CFWL_BARCODETP_H_
 
+#include "fxjs/gc/heap.h"
 #include "xfa/fwl/theme/cfwl_utils.h"
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
 class CFWL_BarcodeTP final : public CFWL_WidgetTP {
  public:
-  CFWL_BarcodeTP();
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_BarcodeTP() override;
 
   // CFWL_WidgetTP
   void DrawBackground(const CFWL_ThemeBackground& pParams) override;
+
+ private:
+  CFWL_BarcodeTP();
 };
 
 #endif  // XFA_FWL_THEME_CFWL_BARCODETP_H_
diff --git a/xfa/fwl/theme/cfwl_carettp.cpp b/xfa/fwl/theme/cfwl_carettp.cpp
index 8d32a37..efad791 100644
--- a/xfa/fwl/theme/cfwl_carettp.cpp
+++ b/xfa/fwl/theme/cfwl_carettp.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,25 +6,25 @@
 
 #include "xfa/fwl/theme/cfwl_carettp.h"
 
-#include "core/fxge/render_defines.h"
+#include "xfa/fgas/graphics/cfgas_gecolor.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fwl/cfwl_caret.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fxgraphics/cxfa_gecolor.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
 
 CFWL_CaretTP::CFWL_CaretTP() = default;
 
 CFWL_CaretTP::~CFWL_CaretTP() = default;
 
 void CFWL_CaretTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
-  switch (pParams.m_iPart) {
-    case CFWL_Part::Background: {
-      if (!(pParams.m_dwStates & CFWL_PartState_HightLight))
+  switch (pParams.GetPart()) {
+    case CFWL_ThemePart::Part::kBackground: {
+      if (!(pParams.m_dwStates & CFWL_PartState::kHightLight))
         return;
 
-      DrawCaretBK(pParams.m_pGraphics.Get(), pParams.m_dwStates,
-                  pParams.m_rtPart, pParams.m_matrix);
+      DrawCaretBK(pParams.GetGraphics(), pParams.m_dwStates, pParams.m_PartRect,
+                  pParams.m_matrix);
       break;
     }
     default:
@@ -32,12 +32,12 @@
   }
 }
 
-void CFWL_CaretTP::DrawCaretBK(CXFA_Graphics* pGraphics,
-                               uint32_t dwStates,
+void CFWL_CaretTP::DrawCaretBK(CFGAS_GEGraphics* pGraphics,
+                               Mask<CFWL_PartState> dwStates,
                                const CFX_RectF& rect,
                                const CFX_Matrix& matrix) {
-  CXFA_GEPath path;
+  CFGAS_GEPath path;
   path.AddRectangle(rect.left, rect.top, rect.width, rect.height);
-  pGraphics->SetFillColor(CXFA_GEColor(ArgbEncode(255, 0, 0, 0)));
-  pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
+  pGraphics->SetFillColor(CFGAS_GEColor(ArgbEncode(255, 0, 0, 0)));
+  pGraphics->FillPath(path, CFX_FillRenderOptions::FillType::kWinding, matrix);
 }
diff --git a/xfa/fwl/theme/cfwl_carettp.h b/xfa/fwl/theme/cfwl_carettp.h
index a0f9dc5..e25df0a 100644
--- a/xfa/fwl/theme/cfwl_carettp.h
+++ b/xfa/fwl/theme/cfwl_carettp.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,23 @@
 #ifndef XFA_FWL_THEME_CFWL_CARETTP_H_
 #define XFA_FWL_THEME_CFWL_CARETTP_H_
 
+#include "fxjs/gc/heap.h"
+#include "xfa/fwl/cfwl_themepart.h"
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
 class CFWL_CaretTP final : public CFWL_WidgetTP {
  public:
-  CFWL_CaretTP();
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_CaretTP() override;
 
   // CFWL_WidgetTP
   void DrawBackground(const CFWL_ThemeBackground& pParams) override;
 
  private:
-  void DrawCaretBK(CXFA_Graphics* pGraphics,
-                   uint32_t dwStates,
+  CFWL_CaretTP();
+
+  void DrawCaretBK(CFGAS_GEGraphics* pGraphics,
+                   Mask<CFWL_PartState> dwStates,
                    const CFX_RectF& rect,
                    const CFX_Matrix& matrix);
 };
diff --git a/xfa/fwl/theme/cfwl_checkboxtp.cpp b/xfa/fwl/theme/cfwl_checkboxtp.cpp
index 3436389..439d07b 100644
--- a/xfa/fwl/theme/cfwl_checkboxtp.cpp
+++ b/xfa/fwl/theme/cfwl_checkboxtp.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,16 +6,19 @@
 
 #include "xfa/fwl/theme/cfwl_checkboxtp.h"
 
-#include "core/fxge/cfx_pathdata.h"
-#include "core/fxge/render_defines.h"
-#include "third_party/base/ptr_util.h"
+#include <math.h>
+
+#include <iterator>
+
+#include "core/fxge/cfx_path.h"
 #include "xfa/fde/cfde_textout.h"
+#include "xfa/fgas/graphics/cfgas_gecolor.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fwl/cfwl_checkbox.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_themetext.h"
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fxgraphics/cxfa_gecolor.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
 
 namespace {
 
@@ -23,8 +26,8 @@
 
 CFX_PointF ScaleBezierPoint(const CFX_PointF& point) {
   CFX_PointF scaled_point(point);
-  scaled_point.x *= FX_BEZIER;
-  scaled_point.y *= FX_BEZIER;
+  scaled_point.x *= FXSYS_BEZIER;
+  scaled_point.y *= FXSYS_BEZIER;
   return scaled_point;
 }
 
@@ -32,206 +35,197 @@
 
 CFWL_CheckBoxTP::CFWL_CheckBoxTP() = default;
 
-CFWL_CheckBoxTP::~CFWL_CheckBoxTP() {
-  if (m_pCheckPath)
-    m_pCheckPath->Clear();
-}
+CFWL_CheckBoxTP::~CFWL_CheckBoxTP() = default;
 
 void CFWL_CheckBoxTP::DrawText(const CFWL_ThemeText& pParams) {
-  EnsureTTOInitialized();
-  m_pTextOut->SetTextColor(pParams.m_dwStates & CFWL_PartState_Disabled
+  EnsureTTOInitialized(pParams.GetWidget()->GetThemeProvider());
+  m_pTextOut->SetTextColor(pParams.m_dwStates & CFWL_PartState::kDisabled
                                ? FWLTHEME_CAPACITY_TextDisColor
                                : FWLTHEME_CAPACITY_TextColor);
   CFWL_WidgetTP::DrawText(pParams);
 }
 
-void CFWL_CheckBoxTP::DrawSignCheck(CXFA_Graphics* pGraphics,
+void CFWL_CheckBoxTP::DrawSignCheck(CFGAS_GEGraphics* pGraphics,
                                     const CFX_RectF& rtSign,
                                     FX_ARGB argbFill,
                                     const CFX_Matrix& matrix) {
-  if (!m_pCheckPath)
-    InitCheckPath(rtSign.width);
+  EnsureCheckPathInitialized(rtSign.width);
+  DCHECK(m_pCheckPath);
 
   CFX_Matrix mt;
   mt.Translate(rtSign.left, rtSign.top);
   mt.Concat(matrix);
-  pGraphics->SaveGraphState();
-  pGraphics->SetFillColor(CXFA_GEColor(argbFill));
-  pGraphics->FillPath(m_pCheckPath.get(), FXFILL_WINDING, &mt);
-  pGraphics->RestoreGraphState();
+
+  CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
+  pGraphics->SetFillColor(CFGAS_GEColor(argbFill));
+  pGraphics->FillPath(*m_pCheckPath, CFX_FillRenderOptions::FillType::kWinding,
+                      mt);
 }
 
-void CFWL_CheckBoxTP::DrawSignCircle(CXFA_Graphics* pGraphics,
+void CFWL_CheckBoxTP::DrawSignCircle(CFGAS_GEGraphics* pGraphics,
                                      const CFX_RectF& rtSign,
                                      FX_ARGB argbFill,
                                      const CFX_Matrix& matrix) {
-  CXFA_GEPath path;
+  CFGAS_GEPath path;
   path.AddEllipse(rtSign);
-  pGraphics->SaveGraphState();
-  pGraphics->SetFillColor(CXFA_GEColor(argbFill));
-  pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
-  pGraphics->RestoreGraphState();
+
+  CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
+  pGraphics->SetFillColor(CFGAS_GEColor(argbFill));
+  pGraphics->FillPath(path, CFX_FillRenderOptions::FillType::kWinding, matrix);
 }
 
-void CFWL_CheckBoxTP::DrawSignCross(CXFA_Graphics* pGraphics,
+void CFWL_CheckBoxTP::DrawSignCross(CFGAS_GEGraphics* pGraphics,
                                     const CFX_RectF& rtSign,
                                     FX_ARGB argbFill,
                                     const CFX_Matrix& matrix) {
-  CXFA_GEPath path;
-  float fRight = rtSign.right();
-  float fBottom = rtSign.bottom();
+  CFGAS_GEPath path;
+  const float fRight = rtSign.right();
+  const float fBottom = rtSign.bottom();
   path.AddLine(rtSign.TopLeft(), CFX_PointF(fRight, fBottom));
   path.AddLine(CFX_PointF(rtSign.left, fBottom),
                CFX_PointF(fRight, rtSign.top));
 
-  pGraphics->SaveGraphState();
-  pGraphics->SetStrokeColor(CXFA_GEColor(argbFill));
+  CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
+  pGraphics->SetStrokeColor(CFGAS_GEColor(argbFill));
   pGraphics->SetLineWidth(1.0f);
-  pGraphics->StrokePath(&path, &matrix);
-  pGraphics->RestoreGraphState();
+  pGraphics->StrokePath(path, matrix);
 }
 
-void CFWL_CheckBoxTP::DrawSignDiamond(CXFA_Graphics* pGraphics,
+void CFWL_CheckBoxTP::DrawSignDiamond(CFGAS_GEGraphics* pGraphics,
                                       const CFX_RectF& rtSign,
                                       FX_ARGB argbFill,
                                       const CFX_Matrix& matrix) {
-  CXFA_GEPath path;
-  float fWidth = rtSign.width;
-  float fHeight = rtSign.height;
-  float fBottom = rtSign.bottom();
+  CFGAS_GEPath path;
+  const float fWidth = rtSign.width;
+  const float fHeight = rtSign.height;
+  const float fBottom = rtSign.bottom();
   path.MoveTo(CFX_PointF(rtSign.left + fWidth / 2, rtSign.top));
   path.LineTo(CFX_PointF(rtSign.left, rtSign.top + fHeight / 2));
   path.LineTo(CFX_PointF(rtSign.left + fWidth / 2, fBottom));
   path.LineTo(CFX_PointF(rtSign.right(), rtSign.top + fHeight / 2));
   path.LineTo(CFX_PointF(rtSign.left + fWidth / 2, rtSign.top));
 
-  pGraphics->SaveGraphState();
-  pGraphics->SetFillColor(CXFA_GEColor(argbFill));
-  pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
-  pGraphics->RestoreGraphState();
+  CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
+  pGraphics->SetFillColor(CFGAS_GEColor(argbFill));
+  pGraphics->FillPath(path, CFX_FillRenderOptions::FillType::kWinding, matrix);
 }
 
-void CFWL_CheckBoxTP::DrawSignSquare(CXFA_Graphics* pGraphics,
+void CFWL_CheckBoxTP::DrawSignSquare(CFGAS_GEGraphics* pGraphics,
                                      const CFX_RectF& rtSign,
                                      FX_ARGB argbFill,
                                      const CFX_Matrix& matrix) {
-  CXFA_GEPath path;
+  CFGAS_GEPath path;
   path.AddRectangle(rtSign.left, rtSign.top, rtSign.width, rtSign.height);
-  pGraphics->SaveGraphState();
-  pGraphics->SetFillColor(CXFA_GEColor(argbFill));
-  pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
-  pGraphics->RestoreGraphState();
+
+  CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
+  pGraphics->SetFillColor(CFGAS_GEColor(argbFill));
+  pGraphics->FillPath(path, CFX_FillRenderOptions::FillType::kWinding, matrix);
 }
 
-void CFWL_CheckBoxTP::DrawSignStar(CXFA_Graphics* pGraphics,
+void CFWL_CheckBoxTP::DrawSignStar(CFGAS_GEGraphics* pGraphics,
                                    const CFX_RectF& rtSign,
                                    FX_ARGB argbFill,
                                    const CFX_Matrix& matrix) {
-  CXFA_GEPath path;
+  CFGAS_GEPath path;
   float fBottom = rtSign.bottom();
-  float fRadius =
-      (rtSign.top - fBottom) / (1 + static_cast<float>(cos(FX_PI / 5.0f)));
+  float fRadius = (rtSign.top - fBottom) / (1 + cosf(FXSYS_PI / 5.0f));
   CFX_PointF ptCenter((rtSign.left + rtSign.right()) / 2.0f,
                       (rtSign.top + fBottom) / 2.0f);
 
   CFX_PointF points[5];
-  float fAngel = FX_PI / 10.0f;
-  for (int32_t i = 0; i < 5; i++) {
-    points[i] =
-        ptCenter + CFX_PointF(fRadius * static_cast<float>(cos(fAngel)),
-                              fRadius * static_cast<float>(sin(fAngel)));
-    fAngel += FX_PI * 2 / 5.0f;
+  float fAngle = FXSYS_PI / 10.0f;
+  for (auto& point : points) {
+    point =
+        ptCenter + CFX_PointF(fRadius * cosf(fAngle), fRadius * sinf(fAngle));
+    fAngle += FXSYS_PI * 2 / 5.0f;
   }
 
   path.MoveTo(points[0]);
-  int32_t nNext = 0;
-  for (int32_t j = 0; j < 5; j++) {
-    nNext += 2;
-    if (nNext >= 5)
-      nNext -= 5;
-
-    path.LineTo(points[nNext]);
+  int next = 0;
+  for (size_t i = 0; i < std::size(points); ++i) {
+    next = (next + 2) % std::size(points);
+    path.LineTo(points[next]);
   }
-  pGraphics->SaveGraphState();
-  pGraphics->SetFillColor(CXFA_GEColor(argbFill));
-  pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
-  pGraphics->RestoreGraphState();
+
+  CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
+  pGraphics->SetFillColor(CFGAS_GEColor(argbFill));
+  pGraphics->FillPath(path, CFX_FillRenderOptions::FillType::kWinding, matrix);
 }
 
-void CFWL_CheckBoxTP::InitCheckPath(float fCheckLen) {
-  if (!m_pCheckPath) {
-    m_pCheckPath = pdfium::MakeUnique<CXFA_GEPath>();
+void CFWL_CheckBoxTP::EnsureCheckPathInitialized(float fCheckLen) {
+  if (m_pCheckPath)
+    return;
 
-    float fWidth = kSignPath;
-    float fHeight = -kSignPath;
-    float fBottom = kSignPath;
-    CFX_PointF pt1(fWidth / 15.0f, fBottom + fHeight * 2 / 5.0f);
-    CFX_PointF pt2(fWidth / 4.5f, fBottom + fHeight / 16.0f);
-    CFX_PointF pt3(fWidth / 3.0f, fBottom);
-    CFX_PointF pt4(fWidth * 14 / 15.0f, fBottom + fHeight * 15 / 16.0f);
-    CFX_PointF pt5(fWidth / 3.6f, fBottom + fHeight / 3.5f);
-    CFX_PointF pt12(fWidth / 7.0f, fBottom + fHeight * 2 / 7.0f);
-    CFX_PointF pt21(fWidth / 5.0f, fBottom + fHeight / 5.0f);
-    CFX_PointF pt23(fWidth / 4.4f, fBottom + fHeight * 0 / 16.0f);
-    CFX_PointF pt32(fWidth / 4.0f, fBottom);
-    CFX_PointF pt34(fWidth * (1 / 7.0f + 7 / 15.0f),
-                    fBottom + fHeight * 4 / 5.0f);
-    CFX_PointF pt43(fWidth * (1 / 7.0f + 7 / 15.0f),
-                    fBottom + fHeight * 4 / 5.0f);
-    CFX_PointF pt45(fWidth * 7 / 15.0f, fBottom + fHeight * 8 / 7.0f);
-    CFX_PointF pt54(fWidth / 3.4f, fBottom + fHeight / 3.5f);
-    CFX_PointF pt51(fWidth / 3.6f, fBottom + fHeight / 4.0f);
-    CFX_PointF pt15(fWidth / 3.5f, fBottom + fHeight * 3.5f / 5.0f);
-    m_pCheckPath->MoveTo(pt1);
+  m_pCheckPath = std::make_unique<CFGAS_GEPath>();
 
-    CFX_PointF p1 = ScaleBezierPoint(pt12 - pt1);
-    CFX_PointF p2 = ScaleBezierPoint(pt21 - pt2);
-    m_pCheckPath->BezierTo(pt1 + p1, pt2 + p2, pt2);
+  float fWidth = kSignPath;
+  float fHeight = -kSignPath;
+  float fBottom = kSignPath;
+  CFX_PointF pt1(fWidth / 15.0f, fBottom + fHeight * 2 / 5.0f);
+  CFX_PointF pt2(fWidth / 4.5f, fBottom + fHeight / 16.0f);
+  CFX_PointF pt3(fWidth / 3.0f, fBottom);
+  CFX_PointF pt4(fWidth * 14 / 15.0f, fBottom + fHeight * 15 / 16.0f);
+  CFX_PointF pt5(fWidth / 3.6f, fBottom + fHeight / 3.5f);
+  CFX_PointF pt12(fWidth / 7.0f, fBottom + fHeight * 2 / 7.0f);
+  CFX_PointF pt21(fWidth / 5.0f, fBottom + fHeight / 5.0f);
+  CFX_PointF pt23(fWidth / 4.4f, fBottom + fHeight * 0 / 16.0f);
+  CFX_PointF pt32(fWidth / 4.0f, fBottom);
+  CFX_PointF pt34(fWidth * (1 / 7.0f + 7 / 15.0f),
+                  fBottom + fHeight * 4 / 5.0f);
+  CFX_PointF pt43(fWidth * (1 / 7.0f + 7 / 15.0f),
+                  fBottom + fHeight * 4 / 5.0f);
+  CFX_PointF pt45(fWidth * 7 / 15.0f, fBottom + fHeight * 8 / 7.0f);
+  CFX_PointF pt54(fWidth / 3.4f, fBottom + fHeight / 3.5f);
+  CFX_PointF pt51(fWidth / 3.6f, fBottom + fHeight / 4.0f);
+  CFX_PointF pt15(fWidth / 3.5f, fBottom + fHeight * 3.5f / 5.0f);
+  m_pCheckPath->MoveTo(pt1);
 
-    p1 = ScaleBezierPoint(pt23 - pt2);
-    p2 = ScaleBezierPoint(pt32 - pt3);
-    m_pCheckPath->BezierTo(pt2 + p1, pt3 + p2, pt3);
+  CFX_PointF p1 = ScaleBezierPoint(pt12 - pt1);
+  CFX_PointF p2 = ScaleBezierPoint(pt21 - pt2);
+  m_pCheckPath->BezierTo(pt1 + p1, pt2 + p2, pt2);
 
-    p1 = ScaleBezierPoint(pt34 - pt3);
-    p2 = ScaleBezierPoint(pt43 - pt4);
-    m_pCheckPath->BezierTo(pt3 + p1, pt4 + p2, pt4);
+  p1 = ScaleBezierPoint(pt23 - pt2);
+  p2 = ScaleBezierPoint(pt32 - pt3);
+  m_pCheckPath->BezierTo(pt2 + p1, pt3 + p2, pt3);
 
-    p1 = ScaleBezierPoint(pt45 - pt4);
-    p2 = ScaleBezierPoint(pt54 - pt5);
-    m_pCheckPath->BezierTo(pt4 + p1, pt5 + p2, pt5);
+  p1 = ScaleBezierPoint(pt34 - pt3);
+  p2 = ScaleBezierPoint(pt43 - pt4);
+  m_pCheckPath->BezierTo(pt3 + p1, pt4 + p2, pt4);
 
-    p1 = ScaleBezierPoint(pt51 - pt5);
-    p2 = ScaleBezierPoint(pt15 - pt1);
-    m_pCheckPath->BezierTo(pt5 + p1, pt1 + p2, pt1);
+  p1 = ScaleBezierPoint(pt45 - pt4);
+  p2 = ScaleBezierPoint(pt54 - pt5);
+  m_pCheckPath->BezierTo(pt4 + p1, pt5 + p2, pt5);
 
-    float fScale = fCheckLen / kSignPath;
-    CFX_Matrix mt;
-    mt.Scale(fScale, fScale);
+  p1 = ScaleBezierPoint(pt51 - pt5);
+  p2 = ScaleBezierPoint(pt15 - pt1);
+  m_pCheckPath->BezierTo(pt5 + p1, pt1 + p2, pt1);
 
-    m_pCheckPath->TransformBy(mt);
-  }
+  float fScale = fCheckLen / kSignPath;
+  CFX_Matrix mt;
+  mt.Scale(fScale, fScale);
+  m_pCheckPath->TransformBy(mt);
 }
 
 void CFWL_CheckBoxTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
-  if (pParams.m_iPart != CFWL_Part::CheckBox)
+  if (pParams.GetPart() != CFWL_ThemePart::Part::kCheckBox)
     return;
 
-  if ((pParams.m_dwStates & CFWL_PartState_Checked) ||
-      (pParams.m_dwStates & CFWL_PartState_Neutral)) {
-    DrawCheckSign(pParams.m_pWidget, pParams.m_pGraphics.Get(),
-                  pParams.m_rtPart, pParams.m_dwStates, pParams.m_matrix);
+  if ((pParams.m_dwStates & CFWL_PartState::kChecked) ||
+      (pParams.m_dwStates & CFWL_PartState::kNeutral)) {
+    DrawCheckSign(pParams.GetWidget(), pParams.GetGraphics(),
+                  pParams.m_PartRect, pParams.m_dwStates, pParams.m_matrix);
   }
 }
 
 void CFWL_CheckBoxTP::DrawCheckSign(CFWL_Widget* pWidget,
-                                    CXFA_Graphics* pGraphics,
+                                    CFGAS_GEGraphics* pGraphics,
                                     const CFX_RectF& pRtBox,
-                                    int32_t iState,
+                                    Mask<CFWL_PartState> iState,
                                     const CFX_Matrix& matrix) {
   CFX_RectF rtSign(pRtBox);
-  uint32_t dwColor = iState & CFWL_PartState_Neutral ? 0xFFA9A9A9 : 0xFF000000;
-
-  uint32_t dwStyle = pWidget->GetStylesEx();
+  uint32_t dwColor =
+      iState & CFWL_PartState::kNeutral ? 0xFFA9A9A9 : 0xFF000000;
+  uint32_t dwStyle = pWidget->GetStyleExts();
   rtSign.Deflate(rtSign.width / 4, rtSign.height / 4);
   switch (dwStyle & FWL_STYLEEXT_CKB_SignShapeMask) {
     case FWL_STYLEEXT_CKB_SignShapeCheck:
diff --git a/xfa/fwl/theme/cfwl_checkboxtp.h b/xfa/fwl/theme/cfwl_checkboxtp.h
index e79a693..810edda 100644
--- a/xfa/fwl/theme/cfwl_checkboxtp.h
+++ b/xfa/fwl/theme/cfwl_checkboxtp.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,13 +9,16 @@
 
 #include <memory>
 
+#include "fxjs/gc/heap.h"
+#include "xfa/fwl/cfwl_themepart.h"
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
+class CFGAS_GEPath;
 class CFWL_Widget;
 
 class CFWL_CheckBoxTP final : public CFWL_WidgetTP {
  public:
-  CFWL_CheckBoxTP();
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_CheckBoxTP() override;
 
   // CFWL_WidgetTP
@@ -23,39 +26,41 @@
   void DrawText(const CFWL_ThemeText& pParams) override;
 
  private:
+  CFWL_CheckBoxTP();
+
   void DrawCheckSign(CFWL_Widget* pWidget,
-                     CXFA_Graphics* pGraphics,
+                     CFGAS_GEGraphics* pGraphics,
                      const CFX_RectF& pRtBox,
-                     int32_t iState,
+                     Mask<CFWL_PartState> iState,
                      const CFX_Matrix& matrix);
-  void DrawSignCheck(CXFA_Graphics* pGraphics,
+  void DrawSignCheck(CFGAS_GEGraphics* pGraphics,
                      const CFX_RectF& rtSign,
                      FX_ARGB argbFill,
                      const CFX_Matrix& matrix);
-  void DrawSignCircle(CXFA_Graphics* pGraphics,
+  void DrawSignCircle(CFGAS_GEGraphics* pGraphics,
                       const CFX_RectF& rtSign,
                       FX_ARGB argbFill,
                       const CFX_Matrix& matrix);
-  void DrawSignCross(CXFA_Graphics* pGraphics,
+  void DrawSignCross(CFGAS_GEGraphics* pGraphics,
                      const CFX_RectF& rtSign,
                      FX_ARGB argbFill,
                      const CFX_Matrix& matrix);
-  void DrawSignDiamond(CXFA_Graphics* pGraphics,
+  void DrawSignDiamond(CFGAS_GEGraphics* pGraphics,
                        const CFX_RectF& rtSign,
                        FX_ARGB argbFill,
                        const CFX_Matrix& matrix);
-  void DrawSignSquare(CXFA_Graphics* pGraphics,
+  void DrawSignSquare(CFGAS_GEGraphics* pGraphics,
                       const CFX_RectF& rtSign,
                       FX_ARGB argbFill,
                       const CFX_Matrix& matrix);
-  void DrawSignStar(CXFA_Graphics* pGraphics,
+  void DrawSignStar(CFGAS_GEGraphics* pGraphics,
                     const CFX_RectF& rtSign,
                     FX_ARGB argbFill,
                     const CFX_Matrix& matrix);
 
-  void InitCheckPath(float fCheckLen);
+  void EnsureCheckPathInitialized(float fCheckLen);
 
-  std::unique_ptr<CXFA_GEPath> m_pCheckPath;
+  std::unique_ptr<CFGAS_GEPath> m_pCheckPath;
 };
 
 #endif  // XFA_FWL_THEME_CFWL_CHECKBOXTP_H_
diff --git a/xfa/fwl/theme/cfwl_comboboxtp.cpp b/xfa/fwl/theme/cfwl_comboboxtp.cpp
index 6cc842d..adf66ec 100644
--- a/xfa/fwl/theme/cfwl_comboboxtp.cpp
+++ b/xfa/fwl/theme/cfwl_comboboxtp.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,78 +6,48 @@
 
 #include "xfa/fwl/theme/cfwl_comboboxtp.h"
 
-#include "core/fxge/render_defines.h"
+#include "xfa/fgas/graphics/cfgas_gecolor.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fwl/cfwl_combobox.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
-#include "xfa/fxgraphics/cxfa_gecolor.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
 
 CFWL_ComboBoxTP::CFWL_ComboBoxTP() = default;
 
 CFWL_ComboBoxTP::~CFWL_ComboBoxTP() = default;
 
 void CFWL_ComboBoxTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
-  switch (pParams.m_iPart) {
-    case CFWL_Part::Border: {
-      DrawBorder(pParams.m_pGraphics.Get(), pParams.m_rtPart, pParams.m_matrix);
+  switch (pParams.GetPart()) {
+    case CFWL_ThemePart::Part::kBorder: {
+      DrawBorder(pParams.GetGraphics(), pParams.m_PartRect, pParams.m_matrix);
       break;
     }
-    case CFWL_Part::Background: {
-      CXFA_GEPath path;
-      const CFX_RectF& rect = pParams.m_rtPart;
+    case CFWL_ThemePart::Part::kBackground: {
+      CFGAS_GEPath path;
+      const CFX_RectF& rect = pParams.m_PartRect;
       path.AddRectangle(rect.left, rect.top, rect.width, rect.height);
       FX_ARGB argb_color;
-      switch (pParams.m_dwStates) {
-        case CFWL_PartState_Selected:
-          argb_color = FWLTHEME_COLOR_BKSelected;
-          break;
-        case CFWL_PartState_Disabled:
-          argb_color = FWLTHEME_COLOR_EDGERB1;
-          break;
-        default:
-          argb_color = 0xFFFFFFFF;
-      }
-      pParams.m_pGraphics->SaveGraphState();
-      pParams.m_pGraphics->SetFillColor(CXFA_GEColor(argb_color));
-      pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &pParams.m_matrix);
-      pParams.m_pGraphics->RestoreGraphState();
-      break;
-    }
-    case CFWL_Part::DropDownButton: {
-      DrawDropDownButton(pParams, pParams.m_dwStates, pParams.m_matrix);
-      break;
-    }
-    default:
-      break;
-  }
-}
+      if (pParams.m_dwStates & CFWL_PartState::kSelected)
+        argb_color = FWLTHEME_COLOR_BKSelected;
+      else if (pParams.m_dwStates & CFWL_PartState::kDisabled)
+        argb_color = FWLTHEME_COLOR_EDGERB1;
+      else
+        argb_color = 0xFFFFFFFF;
 
-void CFWL_ComboBoxTP::DrawDropDownButton(const CFWL_ThemeBackground& pParams,
-                                         uint32_t dwStates,
-                                         const CFX_Matrix& matrix) {
-  FWLTHEME_STATE eState = FWLTHEME_STATE_Normal;
-  switch (dwStates) {
-    case CFWL_PartState_Normal: {
-      eState = FWLTHEME_STATE_Normal;
+      CFGAS_GEGraphics::StateRestorer restorer(pParams.GetGraphics());
+      pParams.GetGraphics()->SetFillColor(CFGAS_GEColor(argb_color));
+      pParams.GetGraphics()->FillPath(
+          path, CFX_FillRenderOptions::FillType::kWinding, pParams.m_matrix);
       break;
     }
-    case CFWL_PartState_Hovered: {
-      eState = FWLTHEME_STATE_Hover;
-      break;
-    }
-    case CFWL_PartState_Pressed: {
-      eState = FWLTHEME_STATE_Pressed;
-      break;
-    }
-    case CFWL_PartState_Disabled: {
-      eState = FWLTHEME_STATE_Disable;
+    case CFWL_ThemePart::Part::kDropDownButton: {
+      DrawArrowBtn(pParams.GetGraphics(), pParams.m_PartRect,
+                   FWLTHEME_DIRECTION::kDown, pParams.GetThemeState(),
+                   pParams.m_matrix);
       break;
     }
     default:
       break;
   }
-  DrawArrowBtn(pParams.m_pGraphics.Get(), pParams.m_rtPart,
-               FWLTHEME_DIRECTION_Down, eState, pParams.m_matrix);
 }
diff --git a/xfa/fwl/theme/cfwl_comboboxtp.h b/xfa/fwl/theme/cfwl_comboboxtp.h
index b3a75a5..87ca2d7 100644
--- a/xfa/fwl/theme/cfwl_comboboxtp.h
+++ b/xfa/fwl/theme/cfwl_comboboxtp.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,20 +7,19 @@
 #ifndef XFA_FWL_THEME_CFWL_COMBOBOXTP_H_
 #define XFA_FWL_THEME_CFWL_COMBOBOXTP_H_
 
+#include "fxjs/gc/heap.h"
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
 class CFWL_ComboBoxTP final : public CFWL_WidgetTP {
  public:
-  CFWL_ComboBoxTP();
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_ComboBoxTP() override;
 
-  // CFWL_WidgetTP
+  // CFWL_WidgetTP:
   void DrawBackground(const CFWL_ThemeBackground& pParams) override;
 
  private:
-  void DrawDropDownButton(const CFWL_ThemeBackground& pParams,
-                          uint32_t dwStates,
-                          const CFX_Matrix& matrix);
+  CFWL_ComboBoxTP();
 };
 
 #endif  // XFA_FWL_THEME_CFWL_COMBOBOXTP_H_
diff --git a/xfa/fwl/theme/cfwl_datetimepickertp.cpp b/xfa/fwl/theme/cfwl_datetimepickertp.cpp
index a185952..5562ab1 100644
--- a/xfa/fwl/theme/cfwl_datetimepickertp.cpp
+++ b/xfa/fwl/theme/cfwl_datetimepickertp.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,50 +9,22 @@
 #include "xfa/fwl/cfwl_datetimepicker.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 
-CFWL_DateTimePickerTP::CFWL_DateTimePickerTP() {}
+CFWL_DateTimePickerTP::CFWL_DateTimePickerTP() = default;
 
-CFWL_DateTimePickerTP::~CFWL_DateTimePickerTP() {}
+CFWL_DateTimePickerTP::~CFWL_DateTimePickerTP() = default;
 
 void CFWL_DateTimePickerTP::DrawBackground(
     const CFWL_ThemeBackground& pParams) {
-  switch (pParams.m_iPart) {
-    case CFWL_Part::Border:
-      DrawBorder(pParams.m_pGraphics.Get(), pParams.m_rtPart, pParams.m_matrix);
+  switch (pParams.GetPart()) {
+    case CFWL_ThemePart::Part::kBorder:
+      DrawBorder(pParams.GetGraphics(), pParams.m_PartRect, pParams.m_matrix);
       break;
-    case CFWL_Part::DropDownButton:
-      DrawDropDownButton(pParams, pParams.m_matrix);
+    case CFWL_ThemePart::Part::kDropDownButton:
+      DrawArrowBtn(pParams.GetGraphics(), pParams.m_PartRect,
+                   FWLTHEME_DIRECTION::kDown, pParams.GetThemeState(),
+                   pParams.m_matrix);
       break;
     default:
       break;
   }
 }
-
-void CFWL_DateTimePickerTP::DrawDropDownButton(
-    const CFWL_ThemeBackground& pParams,
-    const CFX_Matrix& matrix) {
-  uint32_t dwStates = pParams.m_dwStates;
-  dwStates &= 0x03;
-  FWLTHEME_STATE eState = FWLTHEME_STATE_Normal;
-  switch (eState & dwStates) {
-    case CFWL_PartState_Normal: {
-      eState = FWLTHEME_STATE_Normal;
-      break;
-    }
-    case CFWL_PartState_Hovered: {
-      eState = FWLTHEME_STATE_Hover;
-      break;
-    }
-    case CFWL_PartState_Pressed: {
-      eState = FWLTHEME_STATE_Pressed;
-      break;
-    }
-    case CFWL_PartState_Disabled: {
-      eState = FWLTHEME_STATE_Disable;
-      break;
-    }
-    default:
-      break;
-  }
-  DrawArrowBtn(pParams.m_pGraphics.Get(), pParams.m_rtPart,
-               FWLTHEME_DIRECTION_Down, eState, matrix);
-}
diff --git a/xfa/fwl/theme/cfwl_datetimepickertp.h b/xfa/fwl/theme/cfwl_datetimepickertp.h
index e7163e0..a9cf41d 100644
--- a/xfa/fwl/theme/cfwl_datetimepickertp.h
+++ b/xfa/fwl/theme/cfwl_datetimepickertp.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,19 @@
 #ifndef XFA_FWL_THEME_CFWL_DATETIMEPICKERTP_H_
 #define XFA_FWL_THEME_CFWL_DATETIMEPICKERTP_H_
 
+#include "fxjs/gc/heap.h"
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
 class CFWL_DateTimePickerTP final : public CFWL_WidgetTP {
  public:
-  CFWL_DateTimePickerTP();
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_DateTimePickerTP() override;
 
-  // CFWL_WidgetTP
+  // CFWL_WidgetTP:
   void DrawBackground(const CFWL_ThemeBackground& pParams) override;
 
  private:
-  void DrawDropDownButton(const CFWL_ThemeBackground& pParams,
-                          const CFX_Matrix& matrix);
+  CFWL_DateTimePickerTP();
 };
 
 #endif  // XFA_FWL_THEME_CFWL_DATETIMEPICKERTP_H_
diff --git a/xfa/fwl/theme/cfwl_edittp.cpp b/xfa/fwl/theme/cfwl_edittp.cpp
index cacdf09..401390e 100644
--- a/xfa/fwl/theme/cfwl_edittp.cpp
+++ b/xfa/fwl/theme/cfwl_edittp.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,67 +6,62 @@
 
 #include "xfa/fwl/theme/cfwl_edittp.h"
 
-#include "core/fxge/render_defines.h"
+#include "xfa/fgas/graphics/cfgas_gecolor.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fwl/cfwl_edit.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fxgraphics/cxfa_gecolor.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
 
 CFWL_EditTP::CFWL_EditTP() = default;
 
 CFWL_EditTP::~CFWL_EditTP() = default;
 
 void CFWL_EditTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
-  if (CFWL_Part::CombTextLine == pParams.m_iPart) {
-    CFWL_Widget::AdapterIface* pWidget =
-        pParams.m_pWidget->GetOutmost()->GetAdapterIface();
-    FX_ARGB cr = 0xFF000000;
-    float fWidth = 1.0f;
-    pWidget->GetBorderColorAndThickness(&cr, &fWidth);
-    pParams.m_pGraphics->SetStrokeColor(CXFA_GEColor(cr));
-    pParams.m_pGraphics->SetLineWidth(fWidth);
-    pParams.m_pGraphics->StrokePath(pParams.m_pPath.Get(), &pParams.m_matrix);
-    return;
-  }
-
-  switch (pParams.m_iPart) {
-    case CFWL_Part::Border: {
-      DrawBorder(pParams.m_pGraphics.Get(), pParams.m_rtPart, pParams.m_matrix);
+  switch (pParams.GetPart()) {
+    case CFWL_ThemePart::Part::kBorder: {
+      DrawBorder(pParams.GetGraphics(), pParams.m_PartRect, pParams.m_matrix);
       break;
     }
-    case CFWL_Part::Background: {
-      if (pParams.m_pPath) {
-        CXFA_Graphics* pGraphics = pParams.m_pGraphics.Get();
-        pGraphics->SaveGraphState();
-        pGraphics->SetFillColor(CXFA_GEColor(FWLTHEME_COLOR_BKSelected));
-        pGraphics->FillPath(pParams.m_pPath.Get(), FXFILL_WINDING,
-                            &pParams.m_matrix);
-        pGraphics->RestoreGraphState();
+    case CFWL_ThemePart::Part::kBackground: {
+      CFGAS_GEGraphics* pGraphics = pParams.GetGraphics();
+      CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
+      const CFGAS_GEPath* pParamsPath = pParams.GetPath();
+      if (pParamsPath) {
+        pGraphics->SetFillColor(CFGAS_GEColor(FWLTHEME_COLOR_BKSelected));
+        pGraphics->FillPath(*pParamsPath,
+                            CFX_FillRenderOptions::FillType::kWinding,
+                            pParams.m_matrix);
       } else {
-        CXFA_GEPath path;
-        path.AddRectangle(pParams.m_rtPart.left, pParams.m_rtPart.top,
-                          pParams.m_rtPart.width, pParams.m_rtPart.height);
-        CXFA_GEColor cr(FWLTHEME_COLOR_Background);
+        CFGAS_GEPath path;
+        path.AddRectangle(pParams.m_PartRect.left, pParams.m_PartRect.top,
+                          pParams.m_PartRect.width, pParams.m_PartRect.height);
+        CFGAS_GEColor cr(FWLTHEME_COLOR_Background);
         if (!pParams.m_bStaticBackground) {
-          if (pParams.m_dwStates & CFWL_PartState_Disabled)
-            cr = CXFA_GEColor(FWLTHEME_COLOR_EDGERB1);
-          else if (pParams.m_dwStates & CFWL_PartState_ReadOnly)
-            cr = CXFA_GEColor(ArgbEncode(255, 236, 233, 216));
+          if (pParams.m_dwStates & CFWL_PartState::kDisabled)
+            cr = CFGAS_GEColor(FWLTHEME_COLOR_EDGERB1);
+          else if (pParams.m_dwStates & CFWL_PartState::kReadOnly)
+            cr = CFGAS_GEColor(ArgbEncode(255, 236, 233, 216));
           else
-            cr = CXFA_GEColor(0xFFFFFFFF);
+            cr = CFGAS_GEColor(0xFFFFFFFF);
         }
-        pParams.m_pGraphics->SaveGraphState();
-        pParams.m_pGraphics->SetFillColor(cr);
-        pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &pParams.m_matrix);
-        pParams.m_pGraphics->RestoreGraphState();
+        pGraphics->SetFillColor(cr);
+        pGraphics->FillPath(path, CFX_FillRenderOptions::FillType::kWinding,
+                            pParams.m_matrix);
       }
       break;
     }
-    case CFWL_Part::CombTextLine: {
-      pParams.m_pGraphics->SetStrokeColor(CXFA_GEColor(0xFF000000));
-      pParams.m_pGraphics->SetLineWidth(1.0f);
-      pParams.m_pGraphics->StrokePath(pParams.m_pPath.Get(), &pParams.m_matrix);
+    case CFWL_ThemePart::Part::kCombTextLine: {
+      CFWL_Widget::AdapterIface* pWidget =
+          pParams.GetWidget()->GetOutmost()->GetAdapterIface();
+      FX_ARGB cr = 0xFF000000;
+      float fWidth = 1.0f;
+      pWidget->GetBorderColorAndThickness(&cr, &fWidth);
+      pParams.GetGraphics()->SetStrokeColor(CFGAS_GEColor(cr));
+      pParams.GetGraphics()->SetLineWidth(fWidth);
+      const CFGAS_GEPath* pParamsPath = pParams.GetPath();
+      if (pParamsPath)
+        pParams.GetGraphics()->StrokePath(*pParamsPath, pParams.m_matrix);
       break;
     }
     default:
diff --git a/xfa/fwl/theme/cfwl_edittp.h b/xfa/fwl/theme/cfwl_edittp.h
index b5b58f3..e44f30c 100644
--- a/xfa/fwl/theme/cfwl_edittp.h
+++ b/xfa/fwl/theme/cfwl_edittp.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,19 @@
 #ifndef XFA_FWL_THEME_CFWL_EDITTP_H_
 #define XFA_FWL_THEME_CFWL_EDITTP_H_
 
+#include "fxjs/gc/heap.h"
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
 class CFWL_EditTP final : public CFWL_WidgetTP {
  public:
-  CFWL_EditTP();
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_EditTP() override;
 
-  // CFWL_WidgetTP
+  // CFWL_WidgetTP:
   void DrawBackground(const CFWL_ThemeBackground& pParams) override;
+
+ private:
+  CFWL_EditTP();
 };
 
 #endif  // XFA_FWL_THEME_CFWL_EDITTP_H_
diff --git a/xfa/fwl/theme/cfwl_listboxtp.cpp b/xfa/fwl/theme/cfwl_listboxtp.cpp
index 28b5e34..db78bf5 100644
--- a/xfa/fwl/theme/cfwl_listboxtp.cpp
+++ b/xfa/fwl/theme/cfwl_listboxtp.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,45 +7,45 @@
 #include "xfa/fwl/theme/cfwl_listboxtp.h"
 
 #include "build/build_config.h"
-#include "core/fxge/render_defines.h"
+#include "xfa/fgas/graphics/cfgas_gecolor.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fwl/cfwl_listbox.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fxgraphics/cxfa_gecolor.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
 
 CFWL_ListBoxTP::CFWL_ListBoxTP() = default;
 
 CFWL_ListBoxTP::~CFWL_ListBoxTP() = default;
 
 void CFWL_ListBoxTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
-  switch (pParams.m_iPart) {
-    case CFWL_Part::Border: {
-      DrawBorder(pParams.m_pGraphics.Get(), pParams.m_rtPart, pParams.m_matrix);
+  switch (pParams.GetPart()) {
+    case CFWL_ThemePart::Part::kBorder: {
+      DrawBorder(pParams.GetGraphics(), pParams.m_PartRect, pParams.m_matrix);
       break;
     }
-    case CFWL_Part::Background: {
-      FillSolidRect(pParams.m_pGraphics.Get(), ArgbEncode(255, 255, 255, 255),
-                    pParams.m_rtPart, pParams.m_matrix);
+    case CFWL_ThemePart::Part::kBackground: {
+      FillSolidRect(pParams.GetGraphics(), ArgbEncode(255, 255, 255, 255),
+                    pParams.m_PartRect, pParams.m_matrix);
       if (pParams.m_pRtData) {
-        FillSolidRect(pParams.m_pGraphics.Get(), FWLTHEME_COLOR_Background,
+        FillSolidRect(pParams.GetGraphics(), FWLTHEME_COLOR_Background,
                       *pParams.m_pRtData, pParams.m_matrix);
       }
       break;
     }
-    case CFWL_Part::ListItem: {
-      DrawListBoxItem(pParams.m_pGraphics.Get(), pParams.m_dwStates,
-                      pParams.m_rtPart, pParams.m_pRtData, pParams.m_matrix);
+    case CFWL_ThemePart::Part::kListItem: {
+      DrawListBoxItem(pParams.GetGraphics(), pParams.m_dwStates,
+                      pParams.m_PartRect, pParams.m_pRtData, pParams.m_matrix);
       break;
     }
-    case CFWL_Part::Check: {
+    case CFWL_ThemePart::Part::kCheck: {
       uint32_t color = 0xFF000000;
-      if (pParams.m_dwStates == CFWL_PartState_Checked) {
+      if (pParams.m_dwStates == CFWL_PartState::kChecked) {
         color = 0xFFFF0000;
-      } else if (pParams.m_dwStates == CFWL_PartState_Normal) {
+      } else if (pParams.m_dwStates == CFWL_PartState::kNormal) {
         color = 0xFF0000FF;
       }
-      FillSolidRect(pParams.m_pGraphics.Get(), color, pParams.m_rtPart,
+      FillSolidRect(pParams.GetGraphics(), color, pParams.m_PartRect,
                     pParams.m_matrix);
       break;
     }
@@ -54,24 +54,24 @@
   }
 }
 
-void CFWL_ListBoxTP::DrawListBoxItem(CXFA_Graphics* pGraphics,
-                                     uint32_t dwStates,
+void CFWL_ListBoxTP::DrawListBoxItem(CFGAS_GEGraphics* pGraphics,
+                                     Mask<CFWL_PartState> dwStates,
                                      const CFX_RectF& rtItem,
                                      const CFX_RectF* pData,
                                      const CFX_Matrix& matrix) {
-  if (dwStates & CFWL_PartState_Selected) {
-    pGraphics->SaveGraphState();
-    pGraphics->SetFillColor(CXFA_GEColor(FWLTHEME_COLOR_BKSelected));
-    CXFA_GEPath path;
-#if defined(OS_MACOSX)
+  if (dwStates & CFWL_PartState::kSelected) {
+    CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
+    pGraphics->SetFillColor(CFGAS_GEColor(FWLTHEME_COLOR_BKSelected));
+    CFGAS_GEPath path;
+#if BUILDFLAG(IS_APPLE)
     path.AddRectangle(rtItem.left, rtItem.top, rtItem.width - 1,
                       rtItem.height - 1);
 #else
     path.AddRectangle(rtItem.left, rtItem.top, rtItem.width, rtItem.height);
 #endif
-    pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
-    pGraphics->RestoreGraphState();
+    pGraphics->FillPath(path, CFX_FillRenderOptions::FillType::kWinding,
+                        matrix);
   }
-  if ((dwStates & CFWL_PartState_Focused) && pData)
+  if ((dwStates & CFWL_PartState::kFocused) && pData)
     DrawFocus(pGraphics, *pData, matrix);
 }
diff --git a/xfa/fwl/theme/cfwl_listboxtp.h b/xfa/fwl/theme/cfwl_listboxtp.h
index ee954b8..d85ebf4 100644
--- a/xfa/fwl/theme/cfwl_listboxtp.h
+++ b/xfa/fwl/theme/cfwl_listboxtp.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,23 @@
 #ifndef XFA_FWL_THEME_CFWL_LISTBOXTP_H_
 #define XFA_FWL_THEME_CFWL_LISTBOXTP_H_
 
+#include "fxjs/gc/heap.h"
+#include "xfa/fwl/cfwl_themepart.h"
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
 class CFWL_ListBoxTP final : public CFWL_WidgetTP {
  public:
-  CFWL_ListBoxTP();
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_ListBoxTP() override;
 
-  // CFWL_WidgetTP
+  // CFWL_WidgetTP:
   void DrawBackground(const CFWL_ThemeBackground& pParams) override;
 
  private:
-  void DrawListBoxItem(CXFA_Graphics* pGraphics,
-                       uint32_t dwStates,
+  CFWL_ListBoxTP();
+
+  void DrawListBoxItem(CFGAS_GEGraphics* pGraphics,
+                       Mask<CFWL_PartState> dwStates,
                        const CFX_RectF& rtItem,
                        const CFX_RectF* pData,
                        const CFX_Matrix& matrix);
diff --git a/xfa/fwl/theme/cfwl_monthcalendartp.cpp b/xfa/fwl/theme/cfwl_monthcalendartp.cpp
index 47ba930..0c220fb 100644
--- a/xfa/fwl/theme/cfwl_monthcalendartp.cpp
+++ b/xfa/fwl/theme/cfwl_monthcalendartp.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,15 +6,15 @@
 
 #include "xfa/fwl/theme/cfwl_monthcalendartp.h"
 
-#include "core/fxge/render_defines.h"
 #include "xfa/fde/cfde_textout.h"
+#include "xfa/fgas/graphics/cfgas_gecolor.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fwl/cfwl_monthcalendar.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_themetext.h"
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
-#include "xfa/fxgraphics/cxfa_gecolor.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
 
 namespace {
 
@@ -33,48 +33,48 @@
 CFWL_MonthCalendarTP::~CFWL_MonthCalendarTP() = default;
 
 void CFWL_MonthCalendarTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
-  switch (pParams.m_iPart) {
-    case CFWL_Part::Border: {
-      DrawBorder(pParams.m_pGraphics.Get(), pParams.m_rtPart, pParams.m_matrix);
+  switch (pParams.GetPart()) {
+    case CFWL_ThemePart::Part::kBorder: {
+      DrawBorder(pParams.GetGraphics(), pParams.m_PartRect, pParams.m_matrix);
       break;
     }
-    case CFWL_Part::Background: {
+    case CFWL_ThemePart::Part::kBackground: {
       DrawTotalBK(pParams, pParams.m_matrix);
       break;
     }
-    case CFWL_Part::Header: {
+    case CFWL_ThemePart::Part::kHeader: {
       DrawHeadBk(pParams, pParams.m_matrix);
       break;
     }
-    case CFWL_Part::LBtn: {
-      FWLTHEME_STATE eState = GetState(pParams.m_dwStates);
-      DrawArrowBtn(pParams.m_pGraphics.Get(), pParams.m_rtPart,
-                   FWLTHEME_DIRECTION_Left, eState, pParams.m_matrix);
+    case CFWL_ThemePart::Part::kLBtn: {
+      DrawArrowBtn(pParams.GetGraphics(), pParams.m_PartRect,
+                   FWLTHEME_DIRECTION::kLeft, pParams.GetThemeState(),
+                   pParams.m_matrix);
       break;
     }
-    case CFWL_Part::RBtn: {
-      FWLTHEME_STATE eState = GetState(pParams.m_dwStates);
-      DrawArrowBtn(pParams.m_pGraphics.Get(), pParams.m_rtPart,
-                   FWLTHEME_DIRECTION_Right, eState, pParams.m_matrix);
+    case CFWL_ThemePart::Part::kRBtn: {
+      DrawArrowBtn(pParams.GetGraphics(), pParams.m_PartRect,
+                   FWLTHEME_DIRECTION::kRight, pParams.GetThemeState(),
+                   pParams.m_matrix);
       break;
     }
-    case CFWL_Part::HSeparator: {
+    case CFWL_ThemePart::Part::kHSeparator: {
       DrawHSeparator(pParams, pParams.m_matrix);
       break;
     }
-    case CFWL_Part::DatesIn: {
+    case CFWL_ThemePart::Part::kDatesIn: {
       DrawDatesInBK(pParams, pParams.m_matrix);
       break;
     }
-    case CFWL_Part::TodayCircle: {
+    case CFWL_ThemePart::Part::kTodayCircle: {
       DrawTodayCircle(pParams, pParams.m_matrix);
       break;
     }
-    case CFWL_Part::DateInCircle: {
+    case CFWL_ThemePart::Part::kDateInCircle: {
       DrawDatesInCircle(pParams, pParams.m_matrix);
       break;
     }
-    case CFWL_Part::WeekNumSep: {
+    case CFWL_ThemePart::Part::kWeekNumSep: {
       DrawWeekNumSep(pParams, pParams.m_matrix);
       break;
     }
@@ -84,13 +84,13 @@
 }
 
 void CFWL_MonthCalendarTP::DrawText(const CFWL_ThemeText& pParams) {
-  EnsureTTOInitialized();
-  if ((pParams.m_iPart == CFWL_Part::DatesIn) &&
-      !(pParams.m_dwStates & FWL_ITEMSTATE_MCD_Flag) &&
-      (pParams.m_dwStates &
-       (CFWL_PartState_Hovered | CFWL_PartState_Selected))) {
+  EnsureTTOInitialized(pParams.GetWidget()->GetThemeProvider());
+  if (pParams.GetPart() == CFWL_ThemePart::Part::kDatesIn &&
+      !(pParams.m_dwStates & CFWL_PartState::kFlagged) &&
+      (pParams.m_dwStates & Mask<CFWL_PartState>{CFWL_PartState::kHovered,
+                                                 CFWL_PartState::kSelected})) {
     m_pTextOut->SetTextColor(0xFFFFFFFF);
-  } else if (pParams.m_iPart == CFWL_Part::Caption) {
+  } else if (pParams.GetPart() == CFWL_ThemePart::Part::kCaption) {
     m_pTextOut->SetTextColor(kCaptionColor);
   } else {
     m_pTextOut->SetTextColor(0xFF000000);
@@ -100,43 +100,48 @@
 
 void CFWL_MonthCalendarTP::DrawTotalBK(const CFWL_ThemeBackground& pParams,
                                        const CFX_Matrix& matrix) {
-  CXFA_GEPath path;
-  CFX_RectF rtTotal(pParams.m_rtPart);
+  CFGAS_GEPath path;
+  CFX_RectF rtTotal(pParams.m_PartRect);
   path.AddRectangle(rtTotal.left, rtTotal.top, rtTotal.width, rtTotal.height);
-  pParams.m_pGraphics->SaveGraphState();
-  pParams.m_pGraphics->SetFillColor(CXFA_GEColor(kBackgroundColor));
-  pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
-  pParams.m_pGraphics->RestoreGraphState();
+
+  CFGAS_GEGraphics::StateRestorer restorer(pParams.GetGraphics());
+  pParams.GetGraphics()->SetFillColor(CFGAS_GEColor(kBackgroundColor));
+  pParams.GetGraphics()->FillPath(
+      path, CFX_FillRenderOptions::FillType::kWinding, matrix);
 }
 
 void CFWL_MonthCalendarTP::DrawHeadBk(const CFWL_ThemeBackground& pParams,
                                       const CFX_Matrix& matrix) {
-  CXFA_GEPath path;
-  CFX_RectF rtHead = pParams.m_rtPart;
+  CFGAS_GEPath path;
+  CFX_RectF rtHead = pParams.m_PartRect;
   path.AddRectangle(rtHead.left, rtHead.top, rtHead.width, rtHead.height);
-  pParams.m_pGraphics->SaveGraphState();
-  pParams.m_pGraphics->SetFillColor(CXFA_GEColor(kBackgroundColor));
-  pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
-  pParams.m_pGraphics->RestoreGraphState();
+
+  CFGAS_GEGraphics::StateRestorer restorer(pParams.GetGraphics());
+  pParams.GetGraphics()->SetFillColor(CFGAS_GEColor(kBackgroundColor));
+  pParams.GetGraphics()->FillPath(
+      path, CFX_FillRenderOptions::FillType::kWinding, matrix);
 }
 
 void CFWL_MonthCalendarTP::DrawLButton(const CFWL_ThemeBackground& pParams,
                                        const CFX_Matrix& matrix) {
-  CXFA_GEPath path;
-  CFX_RectF rtLBtn = pParams.m_rtPart;
+  CFGAS_GEPath path;
+  CFX_RectF rtLBtn = pParams.m_PartRect;
   path.AddRectangle(rtLBtn.left, rtLBtn.top, rtLBtn.width, rtLBtn.height);
-  pParams.m_pGraphics->SaveGraphState();
-  pParams.m_pGraphics->SetStrokeColor(
-      CXFA_GEColor(ArgbEncode(0xff, 205, 219, 243)));
-  pParams.m_pGraphics->StrokePath(&path, &matrix);
-  if (pParams.m_dwStates & CFWL_PartState_Pressed) {
-    pParams.m_pGraphics->SetFillColor(
-        CXFA_GEColor(ArgbEncode(0xff, 174, 198, 242)));
-    pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
+
+  CFGAS_GEGraphics::StateRestorer restorer(pParams.GetGraphics());
+  pParams.GetGraphics()->SetStrokeColor(
+      CFGAS_GEColor(ArgbEncode(0xff, 205, 219, 243)));
+  pParams.GetGraphics()->StrokePath(path, matrix);
+  if (pParams.m_dwStates & CFWL_PartState::kPressed) {
+    pParams.GetGraphics()->SetFillColor(
+        CFGAS_GEColor(ArgbEncode(0xff, 174, 198, 242)));
+    pParams.GetGraphics()->FillPath(
+        path, CFX_FillRenderOptions::FillType::kWinding, matrix);
   } else {
-    pParams.m_pGraphics->SetFillColor(
-        CXFA_GEColor(ArgbEncode(0xff, 227, 235, 249)));
-    pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
+    pParams.GetGraphics()->SetFillColor(
+        CFGAS_GEColor(ArgbEncode(0xff, 227, 235, 249)));
+    pParams.GetGraphics()->FillPath(
+        path, CFX_FillRenderOptions::FillType::kWinding, matrix);
   }
 
   path.Clear();
@@ -147,29 +152,31 @@
   path.LineTo(CFX_PointF(rtLBtn.left + rtLBtn.Width() / 3 * 2,
                          rtLBtn.bottom() - rtLBtn.height / 4));
 
-  pParams.m_pGraphics->SetStrokeColor(
-      CXFA_GEColor(ArgbEncode(0xff, 50, 104, 205)));
-  pParams.m_pGraphics->StrokePath(&path, &matrix);
-  pParams.m_pGraphics->RestoreGraphState();
+  pParams.GetGraphics()->SetStrokeColor(
+      CFGAS_GEColor(ArgbEncode(0xff, 50, 104, 205)));
+  pParams.GetGraphics()->StrokePath(path, matrix);
 }
 
 void CFWL_MonthCalendarTP::DrawRButton(const CFWL_ThemeBackground& pParams,
                                        const CFX_Matrix& matrix) {
-  CXFA_GEPath path;
-  CFX_RectF rtRBtn = pParams.m_rtPart;
+  CFGAS_GEPath path;
+  CFX_RectF rtRBtn = pParams.m_PartRect;
   path.AddRectangle(rtRBtn.left, rtRBtn.top, rtRBtn.width, rtRBtn.height);
-  pParams.m_pGraphics->SaveGraphState();
-  pParams.m_pGraphics->SetStrokeColor(
-      CXFA_GEColor(ArgbEncode(0xff, 205, 219, 243)));
-  pParams.m_pGraphics->StrokePath(&path, &matrix);
-  if (pParams.m_dwStates & CFWL_PartState_Pressed) {
-    pParams.m_pGraphics->SetFillColor(
-        CXFA_GEColor(ArgbEncode(0xff, 174, 198, 242)));
-    pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
+
+  CFGAS_GEGraphics::StateRestorer restorer(pParams.GetGraphics());
+  pParams.GetGraphics()->SetStrokeColor(
+      CFGAS_GEColor(ArgbEncode(0xff, 205, 219, 243)));
+  pParams.GetGraphics()->StrokePath(path, matrix);
+  if (pParams.m_dwStates & CFWL_PartState::kPressed) {
+    pParams.GetGraphics()->SetFillColor(
+        CFGAS_GEColor(ArgbEncode(0xff, 174, 198, 242)));
+    pParams.GetGraphics()->FillPath(
+        path, CFX_FillRenderOptions::FillType::kWinding, matrix);
   } else {
-    pParams.m_pGraphics->SetFillColor(
-        CXFA_GEColor(ArgbEncode(0xff, 227, 235, 249)));
-    pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
+    pParams.GetGraphics()->SetFillColor(
+        CFGAS_GEColor(ArgbEncode(0xff, 227, 235, 249)));
+    pParams.GetGraphics()->FillPath(
+        path, CFX_FillRenderOptions::FillType::kWinding, matrix);
   }
 
   path.Clear();
@@ -180,87 +187,80 @@
   path.LineTo(CFX_PointF(rtRBtn.left + rtRBtn.Width() / 3,
                          rtRBtn.bottom() - rtRBtn.height / 4));
 
-  pParams.m_pGraphics->SetStrokeColor(
-      CXFA_GEColor(ArgbEncode(0xff, 50, 104, 205)));
-  pParams.m_pGraphics->StrokePath(&path, &matrix);
-  pParams.m_pGraphics->RestoreGraphState();
+  pParams.GetGraphics()->SetStrokeColor(
+      CFGAS_GEColor(ArgbEncode(0xff, 50, 104, 205)));
+  pParams.GetGraphics()->StrokePath(path, matrix);
 }
 
 void CFWL_MonthCalendarTP::DrawHSeparator(const CFWL_ThemeBackground& pParams,
                                           const CFX_Matrix& matrix) {
-  CXFA_GEPath path;
-  CFX_RectF rtHSep = pParams.m_rtPart;
+  CFGAS_GEPath path;
+  CFX_RectF rtHSep = pParams.m_PartRect;
   path.MoveTo(CFX_PointF(rtHSep.left, rtHSep.top + rtHSep.height / 2));
   path.LineTo(CFX_PointF(rtHSep.right(), rtHSep.top + rtHSep.height / 2));
-  pParams.m_pGraphics->SaveGraphState();
-  pParams.m_pGraphics->SetStrokeColor(CXFA_GEColor(kSeparatorColor));
-  pParams.m_pGraphics->StrokePath(&path, &matrix);
-  pParams.m_pGraphics->RestoreGraphState();
+
+  CFGAS_GEGraphics::StateRestorer restorer(pParams.GetGraphics());
+  pParams.GetGraphics()->SetStrokeColor(CFGAS_GEColor(kSeparatorColor));
+  pParams.GetGraphics()->StrokePath(path, matrix);
 }
 
 void CFWL_MonthCalendarTP::DrawWeekNumSep(const CFWL_ThemeBackground& pParams,
                                           const CFX_Matrix& matrix) {
-  CXFA_GEPath path;
-  CFX_RectF rtWeekSep = pParams.m_rtPart;
+  CFGAS_GEPath path;
+  CFX_RectF rtWeekSep = pParams.m_PartRect;
   path.MoveTo(rtWeekSep.TopLeft());
   path.LineTo(rtWeekSep.BottomLeft());
-  pParams.m_pGraphics->SaveGraphState();
-  pParams.m_pGraphics->SetStrokeColor(CXFA_GEColor(kSeparatorColor));
-  pParams.m_pGraphics->StrokePath(&path, &matrix);
-  pParams.m_pGraphics->RestoreGraphState();
+
+  CFGAS_GEGraphics::StateRestorer restorer(pParams.GetGraphics());
+  pParams.GetGraphics()->SetStrokeColor(CFGAS_GEColor(kSeparatorColor));
+  pParams.GetGraphics()->StrokePath(path, matrix);
 }
 
 void CFWL_MonthCalendarTP::DrawDatesInBK(const CFWL_ThemeBackground& pParams,
                                          const CFX_Matrix& matrix) {
-  pParams.m_pGraphics->SaveGraphState();
-  if (pParams.m_dwStates & CFWL_PartState_Selected) {
-    CXFA_GEPath path;
-    CFX_RectF rtSelDay = pParams.m_rtPart;
+  CFGAS_GEGraphics::StateRestorer restorer(pParams.GetGraphics());
+  if (pParams.m_dwStates & CFWL_PartState::kSelected) {
+    CFGAS_GEPath path;
+    CFX_RectF rtSelDay = pParams.m_PartRect;
     path.AddRectangle(rtSelDay.left, rtSelDay.top, rtSelDay.width,
                       rtSelDay.height);
-    pParams.m_pGraphics->SetFillColor(
-        CXFA_GEColor(kDatesSelectedBackgroundColor));
-    pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
-  } else if (pParams.m_dwStates & CFWL_PartState_Hovered) {
-    CXFA_GEPath path;
-    CFX_RectF rtSelDay = pParams.m_rtPart;
+    pParams.GetGraphics()->SetFillColor(
+        CFGAS_GEColor(kDatesSelectedBackgroundColor));
+    pParams.GetGraphics()->FillPath(
+        path, CFX_FillRenderOptions::FillType::kWinding, matrix);
+  } else if (pParams.m_dwStates & CFWL_PartState::kHovered) {
+    CFGAS_GEPath path;
+    CFX_RectF rtSelDay = pParams.m_PartRect;
     path.AddRectangle(rtSelDay.left, rtSelDay.top, rtSelDay.width,
                       rtSelDay.height);
-    pParams.m_pGraphics->SetFillColor(CXFA_GEColor(kDatesHoverBackgroundColor));
-    pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
+    pParams.GetGraphics()->SetFillColor(
+        CFGAS_GEColor(kDatesHoverBackgroundColor));
+    pParams.GetGraphics()->FillPath(
+        path, CFX_FillRenderOptions::FillType::kWinding, matrix);
   }
-  pParams.m_pGraphics->RestoreGraphState();
 }
 
 void CFWL_MonthCalendarTP::DrawDatesInCircle(
     const CFWL_ThemeBackground& pParams,
     const CFX_Matrix& matrix) {
-  CXFA_GEPath path;
-  CFX_RectF rtSelDay = pParams.m_rtPart;
+  CFGAS_GEPath path;
+  CFX_RectF rtSelDay = pParams.m_PartRect;
   path.AddRectangle(rtSelDay.left, rtSelDay.top, rtSelDay.width,
                     rtSelDay.height);
-  pParams.m_pGraphics->SaveGraphState();
-  pParams.m_pGraphics->SetStrokeColor(CXFA_GEColor(kDatesCircleColor));
-  pParams.m_pGraphics->StrokePath(&path, &matrix);
-  pParams.m_pGraphics->RestoreGraphState();
+
+  CFGAS_GEGraphics::StateRestorer restorer(pParams.GetGraphics());
+  pParams.GetGraphics()->SetStrokeColor(CFGAS_GEColor(kDatesCircleColor));
+  pParams.GetGraphics()->StrokePath(path, matrix);
 }
 
 void CFWL_MonthCalendarTP::DrawTodayCircle(const CFWL_ThemeBackground& pParams,
                                            const CFX_Matrix& matrix) {
-  CXFA_GEPath path;
-  CFX_RectF rtTodayCircle = pParams.m_rtPart;
+  CFGAS_GEPath path;
+  CFX_RectF rtTodayCircle = pParams.m_PartRect;
   path.AddRectangle(rtTodayCircle.left, rtTodayCircle.top, rtTodayCircle.width,
                     rtTodayCircle.height);
-  pParams.m_pGraphics->SaveGraphState();
-  pParams.m_pGraphics->SetStrokeColor(CXFA_GEColor(kDatesCircleColor));
-  pParams.m_pGraphics->StrokePath(&path, &matrix);
-  pParams.m_pGraphics->RestoreGraphState();
-}
 
-FWLTHEME_STATE CFWL_MonthCalendarTP::GetState(uint32_t dwFWLStates) {
-  if (dwFWLStates & CFWL_PartState_Hovered)
-    return FWLTHEME_STATE_Hover;
-  if (dwFWLStates & CFWL_PartState_Pressed)
-    return FWLTHEME_STATE_Pressed;
-  return FWLTHEME_STATE_Normal;
+  CFGAS_GEGraphics::StateRestorer restorer(pParams.GetGraphics());
+  pParams.GetGraphics()->SetStrokeColor(CFGAS_GEColor(kDatesCircleColor));
+  pParams.GetGraphics()->StrokePath(path, matrix);
 }
diff --git a/xfa/fwl/theme/cfwl_monthcalendartp.h b/xfa/fwl/theme/cfwl_monthcalendartp.h
index dd82cc1..609fa2c 100644
--- a/xfa/fwl/theme/cfwl_monthcalendartp.h
+++ b/xfa/fwl/theme/cfwl_monthcalendartp.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,18 +7,21 @@
 #ifndef XFA_FWL_THEME_CFWL_MONTHCALENDARTP_H_
 #define XFA_FWL_THEME_CFWL_MONTHCALENDARTP_H_
 
+#include "fxjs/gc/heap.h"
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
 class CFWL_MonthCalendarTP final : public CFWL_WidgetTP {
  public:
-  CFWL_MonthCalendarTP();
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_MonthCalendarTP() override;
 
-  // CFWL_WidgetTP
+  // CFWL_WidgetTP:
   void DrawBackground(const CFWL_ThemeBackground& pParams) override;
   void DrawText(const CFWL_ThemeText& pParams) override;
 
  private:
+  CFWL_MonthCalendarTP();
+
   void DrawTotalBK(const CFWL_ThemeBackground& pParams,
                    const CFX_Matrix& matrix);
   void DrawHeadBk(const CFWL_ThemeBackground& pParams,
@@ -37,7 +40,6 @@
                       const CFX_Matrix& matrix);
   void DrawWeekNumSep(const CFWL_ThemeBackground& pParams,
                       const CFX_Matrix& matrix);
-  FWLTHEME_STATE GetState(uint32_t dwFWLStates);
 };
 
 #endif  // XFA_FWL_THEME_CFWL_MONTHCALENDARTP_H_
diff --git a/xfa/fwl/theme/cfwl_pictureboxtp.cpp b/xfa/fwl/theme/cfwl_pictureboxtp.cpp
index 39611bd..51b3acb 100644
--- a/xfa/fwl/theme/cfwl_pictureboxtp.cpp
+++ b/xfa/fwl/theme/cfwl_pictureboxtp.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,14 +10,14 @@
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_widget.h"
 
-CFWL_PictureBoxTP::CFWL_PictureBoxTP() {}
+CFWL_PictureBoxTP::CFWL_PictureBoxTP() = default;
 
-CFWL_PictureBoxTP::~CFWL_PictureBoxTP() {}
+CFWL_PictureBoxTP::~CFWL_PictureBoxTP() = default;
 
 void CFWL_PictureBoxTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
-  switch (pParams.m_iPart) {
-    case CFWL_Part::Border:
-      DrawBorder(pParams.m_pGraphics.Get(), pParams.m_rtPart, pParams.m_matrix);
+  switch (pParams.GetPart()) {
+    case CFWL_ThemePart::Part::kBorder:
+      DrawBorder(pParams.GetGraphics(), pParams.m_PartRect, pParams.m_matrix);
       break;
     default:
       break;
diff --git a/xfa/fwl/theme/cfwl_pictureboxtp.h b/xfa/fwl/theme/cfwl_pictureboxtp.h
index 4bb295a..368d904 100644
--- a/xfa/fwl/theme/cfwl_pictureboxtp.h
+++ b/xfa/fwl/theme/cfwl_pictureboxtp.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,19 @@
 #ifndef XFA_FWL_THEME_CFWL_PICTUREBOXTP_H_
 #define XFA_FWL_THEME_CFWL_PICTUREBOXTP_H_
 
+#include "fxjs/gc/heap.h"
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
 class CFWL_PictureBoxTP final : public CFWL_WidgetTP {
  public:
-  CFWL_PictureBoxTP();
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_PictureBoxTP() override;
 
-  // CFWL_WidgetTP
+  // CFWL_WidgetTP:
   void DrawBackground(const CFWL_ThemeBackground& pParams) override;
+
+ private:
+  CFWL_PictureBoxTP();
 };
 
 #endif  // XFA_FWL_THEME_CFWL_PICTUREBOXTP_H_
diff --git a/xfa/fwl/theme/cfwl_pushbuttontp.cpp b/xfa/fwl/theme/cfwl_pushbuttontp.cpp
index 45144b4..c9b3c25 100644
--- a/xfa/fwl/theme/cfwl_pushbuttontp.cpp
+++ b/xfa/fwl/theme/cfwl_pushbuttontp.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,58 +6,59 @@
 
 #include "xfa/fwl/theme/cfwl_pushbuttontp.h"
 
-#include "core/fxge/render_defines.h"
+#include "xfa/fgas/graphics/cfgas_gecolor.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fwl/cfwl_pushbutton.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
-#include "xfa/fxgraphics/cxfa_gecolor.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
 
-#define PUSHBUTTON_SIZE_Corner 2
+namespace {
+
+constexpr float kPushbuttonSizeCorner = 2.0f;
+
+}  // namespace
 
 CFWL_PushButtonTP::CFWL_PushButtonTP() : m_pThemeData(new PBThemeData) {
   SetThemeData();
 }
 
-CFWL_PushButtonTP::~CFWL_PushButtonTP() {}
+CFWL_PushButtonTP::~CFWL_PushButtonTP() = default;
 
 void CFWL_PushButtonTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
-  switch (pParams.m_iPart) {
-    case CFWL_Part::Border: {
-      DrawBorder(pParams.m_pGraphics.Get(), pParams.m_rtPart, pParams.m_matrix);
+  switch (pParams.GetPart()) {
+    case CFWL_ThemePart::Part::kBorder: {
+      DrawBorder(pParams.GetGraphics(), pParams.m_PartRect, pParams.m_matrix);
       break;
     }
-    case CFWL_Part::Background: {
-      const CFX_RectF& rect = pParams.m_rtPart;
+    case CFWL_ThemePart::Part::kBackground: {
+      const CFX_RectF& rect = pParams.m_PartRect;
       float fRight = rect.right();
       float fBottom = rect.bottom();
 
-      CXFA_GEPath strokePath;
+      CFGAS_GEPath strokePath;
       strokePath.MoveTo(
-          CFX_PointF(rect.left + PUSHBUTTON_SIZE_Corner, rect.top));
-      strokePath.LineTo(CFX_PointF(fRight - PUSHBUTTON_SIZE_Corner, rect.top));
-      strokePath.LineTo(CFX_PointF(fRight, rect.top + PUSHBUTTON_SIZE_Corner));
-      strokePath.LineTo(CFX_PointF(fRight, fBottom - PUSHBUTTON_SIZE_Corner));
-      strokePath.LineTo(CFX_PointF(fRight - PUSHBUTTON_SIZE_Corner, fBottom));
+          CFX_PointF(rect.left + kPushbuttonSizeCorner, rect.top));
+      strokePath.LineTo(CFX_PointF(fRight - kPushbuttonSizeCorner, rect.top));
+      strokePath.LineTo(CFX_PointF(fRight, rect.top + kPushbuttonSizeCorner));
+      strokePath.LineTo(CFX_PointF(fRight, fBottom - kPushbuttonSizeCorner));
+      strokePath.LineTo(CFX_PointF(fRight - kPushbuttonSizeCorner, fBottom));
+      strokePath.LineTo(CFX_PointF(rect.left + kPushbuttonSizeCorner, fBottom));
+      strokePath.LineTo(CFX_PointF(rect.left, fBottom - kPushbuttonSizeCorner));
       strokePath.LineTo(
-          CFX_PointF(rect.left + PUSHBUTTON_SIZE_Corner, fBottom));
+          CFX_PointF(rect.left, rect.top + kPushbuttonSizeCorner));
       strokePath.LineTo(
-          CFX_PointF(rect.left, fBottom - PUSHBUTTON_SIZE_Corner));
-      strokePath.LineTo(
-          CFX_PointF(rect.left, rect.top + PUSHBUTTON_SIZE_Corner));
-      strokePath.LineTo(
-          CFX_PointF(rect.left + PUSHBUTTON_SIZE_Corner, rect.top));
+          CFX_PointF(rect.left + kPushbuttonSizeCorner, rect.top));
 
-      CXFA_GEPath fillPath;
-      fillPath.AddSubpath(&strokePath);
+      CFGAS_GEPath fillPath;
+      fillPath.AddSubpath(strokePath);
 
-      CXFA_Graphics* pGraphics = pParams.m_pGraphics.Get();
-      pGraphics->SaveGraphState();
-
+      CFGAS_GEGraphics* pGraphics = pParams.GetGraphics();
+      CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
       CFX_RectF rtInner(rect);
-      rtInner.Deflate(PUSHBUTTON_SIZE_Corner + 1, PUSHBUTTON_SIZE_Corner + 1,
-                      PUSHBUTTON_SIZE_Corner, PUSHBUTTON_SIZE_Corner);
+      rtInner.Deflate(kPushbuttonSizeCorner + 1, kPushbuttonSizeCorner + 1,
+                      kPushbuttonSizeCorner, kPushbuttonSizeCorner);
       fillPath.AddRectangle(rtInner.left, rtInner.top, rtInner.width,
                             rtInner.height);
 
@@ -65,20 +66,20 @@
       FillSolidRect(pGraphics, m_pThemeData->clrEnd[iColor], rect,
                     pParams.m_matrix);
 
-      pGraphics->SetStrokeColor(CXFA_GEColor(m_pThemeData->clrBorder[iColor]));
-      pGraphics->StrokePath(&strokePath, &pParams.m_matrix);
+      pGraphics->SetStrokeColor(CFGAS_GEColor(m_pThemeData->clrBorder[iColor]));
+      pGraphics->StrokePath(strokePath, pParams.m_matrix);
 
       fillPath.Clear();
       fillPath.AddRectangle(rtInner.left, rtInner.top, rtInner.width,
                             rtInner.height);
 
-      pGraphics->SetFillColor(CXFA_GEColor(m_pThemeData->clrFill[iColor]));
-      pGraphics->FillPath(&fillPath, FXFILL_WINDING, &pParams.m_matrix);
-      if (pParams.m_dwStates & CFWL_PartState_Focused) {
+      pGraphics->SetFillColor(CFGAS_GEColor(m_pThemeData->clrFill[iColor]));
+      pGraphics->FillPath(fillPath, CFX_FillRenderOptions::FillType::kWinding,
+                          pParams.m_matrix);
+      if (pParams.m_dwStates & CFWL_PartState::kFocused) {
         rtInner.Inflate(1, 1, 0, 0);
         DrawFocus(pGraphics, rtInner, pParams.m_matrix);
       }
-      pGraphics->RestoreGraphState();
       break;
     }
     default:
@@ -109,16 +110,16 @@
   m_pThemeData->clrFill[4] = ArgbEncode(255, 245, 244, 234);
 }
 
-int32_t CFWL_PushButtonTP::GetColorID(uint32_t dwStates) const {
+int32_t CFWL_PushButtonTP::GetColorID(Mask<CFWL_PartState> dwStates) const {
   int32_t color = 0;
-  if (dwStates & CFWL_PartState_Disabled)
+  if (dwStates & CFWL_PartState::kDisabled)
     color += 4;
-  if (dwStates & CFWL_PartState_Default) {
+  if (dwStates & CFWL_PartState::kDefault) {
     color += 3;
   } else {
-    if (dwStates & CFWL_PartState_Hovered)
+    if (dwStates & CFWL_PartState::kHovered)
       color += 2;
-    if (dwStates & CFWL_PartState_Pressed)
+    if (dwStates & CFWL_PartState::kPressed)
       color += 1;
   }
   return color;
diff --git a/xfa/fwl/theme/cfwl_pushbuttontp.h b/xfa/fwl/theme/cfwl_pushbuttontp.h
index c3079dd..25d3c08 100644
--- a/xfa/fwl/theme/cfwl_pushbuttontp.h
+++ b/xfa/fwl/theme/cfwl_pushbuttontp.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,14 +9,16 @@
 
 #include <memory>
 
+#include "fxjs/gc/heap.h"
+#include "xfa/fwl/cfwl_themepart.h"
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
 class CFWL_PushButtonTP final : public CFWL_WidgetTP {
  public:
-  CFWL_PushButtonTP();
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_PushButtonTP() override;
 
-  // CFWL_WidgetTP
+  // CFWL_WidgetTP:
   void DrawBackground(const CFWL_ThemeBackground& pParams) override;
 
  private:
@@ -27,14 +29,9 @@
     FX_ARGB clrFill[5];
   };
 
-  void SetTopLineColor(uint32_t* pData);
-  void SetLeftLineColor(uint32_t* pData);
-  void SetRightLineColor(uint32_t* pData);
-  void SetBottomLineColor(uint32_t* pData);
-  void SetBackgroudColor(uint32_t* pData);
-  void SetCaptionColor(uint32_t* pData);
-  void SetCornerColor(uint32_t* pData);
-  int32_t GetColorID(uint32_t dwStates) const;
+  CFWL_PushButtonTP();
+
+  int32_t GetColorID(Mask<CFWL_PartState> dwStates) const;
   void SetThemeData();
 
   std::unique_ptr<PBThemeData> m_pThemeData;
diff --git a/xfa/fwl/theme/cfwl_scrollbartp.cpp b/xfa/fwl/theme/cfwl_scrollbartp.cpp
index 55adc3e..4d0c6b7 100644
--- a/xfa/fwl/theme/cfwl_scrollbartp.cpp
+++ b/xfa/fwl/theme/cfwl_scrollbartp.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,64 +6,51 @@
 
 #include "xfa/fwl/theme/cfwl_scrollbartp.h"
 
-#include "core/fxge/render_defines.h"
+#include "xfa/fgas/graphics/cfgas_gecolor.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fwl/cfwl_scrollbar.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
-#include "xfa/fxgraphics/cxfa_gecolor.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
-
-namespace {
-
-const float kPawLength = 12.5f;
-
-}  // namespace
 
 CFWL_ScrollBarTP::CFWL_ScrollBarTP() : m_pThemeData(new SBThemeData) {
   SetThemeData();
 }
 
-CFWL_ScrollBarTP::~CFWL_ScrollBarTP() {}
+CFWL_ScrollBarTP::~CFWL_ScrollBarTP() = default;
 
 void CFWL_ScrollBarTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
-  CFWL_Widget* pWidget = pParams.m_pWidget;
-  FWLTHEME_STATE eState = FWLTHEME_STATE_Normal;
-  if (pParams.m_dwStates & CFWL_PartState_Hovered)
-    eState = FWLTHEME_STATE_Hover;
-  else if (pParams.m_dwStates & CFWL_PartState_Pressed)
-    eState = FWLTHEME_STATE_Pressed;
-  else if (pParams.m_dwStates & CFWL_PartState_Disabled)
-    eState = FWLTHEME_STATE_Disable;
-
-  CXFA_Graphics* pGraphics = pParams.m_pGraphics.Get();
-  bool bVert = !!pWidget->GetStylesEx();
-  switch (pParams.m_iPart) {
-    case CFWL_Part::ForeArrow: {
-      DrawMaxMinBtn(pGraphics, pParams.m_rtPart,
-                    bVert ? FWLTHEME_DIRECTION_Up : FWLTHEME_DIRECTION_Left,
-                    eState, pParams.m_matrix);
+  CFWL_Widget* pWidget = pParams.GetWidget();
+  CFGAS_GEGraphics* pGraphics = pParams.GetGraphics();
+  bool bVert = !!pWidget->GetStyleExts();
+  switch (pParams.GetPart()) {
+    case CFWL_ThemePart::Part::kForeArrow: {
+      DrawMaxMinBtn(pGraphics, pParams.m_PartRect,
+                    bVert ? FWLTHEME_DIRECTION::kUp : FWLTHEME_DIRECTION::kLeft,
+                    pParams.GetThemeState(), pParams.m_matrix);
       break;
     }
-    case CFWL_Part::BackArrow: {
-      DrawMaxMinBtn(pGraphics, pParams.m_rtPart,
-                    bVert ? FWLTHEME_DIRECTION_Down : FWLTHEME_DIRECTION_Right,
-                    eState, pParams.m_matrix);
+    case CFWL_ThemePart::Part::kBackArrow: {
+      DrawMaxMinBtn(
+          pGraphics, pParams.m_PartRect,
+          bVert ? FWLTHEME_DIRECTION::kDown : FWLTHEME_DIRECTION::kRight,
+          pParams.GetThemeState(), pParams.m_matrix);
       break;
     }
-    case CFWL_Part::Thumb: {
-      DrawThumbBtn(pGraphics, pParams.m_rtPart, bVert, eState, true,
-                   pParams.m_matrix);
+    case CFWL_ThemePart::Part::kThumb: {
+      DrawThumbBtn(pGraphics, pParams.m_PartRect, bVert,
+                   pParams.GetThemeState(), pParams.m_matrix);
       break;
     }
-    case CFWL_Part::LowerTrack: {
-      DrawTrack(pGraphics, pParams.m_rtPart, bVert, eState, true,
-                pParams.m_matrix);
+    case CFWL_ThemePart::Part::kLowerTrack: {
+      DrawTrack(pGraphics, pParams.m_PartRect, bVert, pParams.GetThemeState(),
+                true, pParams.m_matrix);
       break;
     }
-    case CFWL_Part::UpperTrack: {
-      DrawTrack(pGraphics, pParams.m_rtPart, bVert, eState, false,
-                pParams.m_matrix);
+    case CFWL_ThemePart::Part::kUpperTrack: {
+      DrawTrack(pGraphics, pParams.m_PartRect, bVert, pParams.GetThemeState(),
+                false, pParams.m_matrix);
       break;
     }
     default:
@@ -71,13 +58,12 @@
   }
 }
 
-void CFWL_ScrollBarTP::DrawThumbBtn(CXFA_Graphics* pGraphics,
+void CFWL_ScrollBarTP::DrawThumbBtn(CFGAS_GEGraphics* pGraphics,
                                     const CFX_RectF& input_rect,
                                     bool bVert,
                                     FWLTHEME_STATE eState,
-                                    bool bPawButton,
                                     const CFX_Matrix& matrix) {
-  if (eState < FWLTHEME_STATE_Normal || eState > FWLTHEME_STATE_Disable)
+  if (eState < FWLTHEME_STATE::kNormal || eState > FWLTHEME_STATE::kDisable)
     return;
 
   CFX_RectF rect = input_rect;
@@ -89,163 +75,75 @@
   if (rect.IsEmpty(0.1f))
     return;
 
-  FillSolidRect(pGraphics, m_pThemeData->clrBtnBK[eState - 1][1], rect, matrix);
+  FillSolidRect(pGraphics,
+                m_pThemeData->clrBtnBK[static_cast<size_t>(eState) - 1][1],
+                rect, matrix);
 
-  pGraphics->SaveGraphState();
-
-  CXFA_GEPath path;
+  CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
+  CFGAS_GEPath path;
   path.AddRectangle(rect.left, rect.top, rect.width, rect.height);
-  pGraphics->SetStrokeColor(
-      CXFA_GEColor(m_pThemeData->clrBtnBorder[eState - 1]));
-  pGraphics->StrokePath(&path, &matrix);
-  pGraphics->RestoreGraphState();
+  pGraphics->SetStrokeColor(CFGAS_GEColor(
+      m_pThemeData->clrBtnBorder[static_cast<size_t>(eState) - 1]));
+  pGraphics->StrokePath(path, matrix);
 }
 
-void CFWL_ScrollBarTP::DrawPaw(CXFA_Graphics* pGraphics,
-                               const CFX_RectF& rect,
-                               bool bVert,
-                               FWLTHEME_STATE eState,
-                               const CFX_Matrix& matrix) {
-  CXFA_GEPath path;
-  if (bVert) {
-    float fPawLen = kPawLength;
-    if (rect.width / 2 <= fPawLen) {
-      fPawLen = (rect.width - 6) / 2;
-    }
-
-    float fX = rect.left + rect.width / 4;
-    float fY = rect.top + rect.height / 2;
-    path.MoveTo(CFX_PointF(fX, fY - 4));
-    path.LineTo(CFX_PointF(fX + fPawLen, fY - 4));
-    path.MoveTo(CFX_PointF(fX, fY - 2));
-    path.LineTo(CFX_PointF(fX + fPawLen, fY - 2));
-    path.MoveTo(CFX_PointF(fX, fY));
-    path.LineTo(CFX_PointF(fX + fPawLen, fY));
-    path.MoveTo(CFX_PointF(fX, fY + 2));
-    path.LineTo(CFX_PointF(fX + fPawLen, fY + 2));
-
-    pGraphics->SetLineWidth(1);
-    pGraphics->SetStrokeColor(
-        CXFA_GEColor(m_pThemeData->clrPawColorLight[eState - 1]));
-    pGraphics->StrokePath(&path, nullptr);
-    fX++;
-
-    path.Clear();
-    path.MoveTo(CFX_PointF(fX, fY - 3));
-    path.LineTo(CFX_PointF(fX + fPawLen, fY - 3));
-    path.MoveTo(CFX_PointF(fX, fY - 1));
-    path.LineTo(CFX_PointF(fX + fPawLen, fY - 1));
-    path.MoveTo(CFX_PointF(fX, fY + 1));
-    path.LineTo(CFX_PointF(fX + fPawLen, fY + 1));
-    path.MoveTo(CFX_PointF(fX, fY + 3));
-    path.LineTo(CFX_PointF(fX + fPawLen, fY + 3));
-
-    pGraphics->SetLineWidth(1);
-    pGraphics->SetStrokeColor(
-        CXFA_GEColor(m_pThemeData->clrPawColorDark[eState - 1]));
-    pGraphics->StrokePath(&path, &matrix);
-  } else {
-    float fPawLen = kPawLength;
-    if (rect.height / 2 <= fPawLen) {
-      fPawLen = (rect.height - 6) / 2;
-    }
-
-    float fX = rect.left + rect.width / 2;
-    float fY = rect.top + rect.height / 4;
-    path.MoveTo(CFX_PointF(fX - 4, fY));
-    path.LineTo(CFX_PointF(fX - 4, fY + fPawLen));
-    path.MoveTo(CFX_PointF(fX - 2, fY));
-    path.LineTo(CFX_PointF(fX - 2, fY + fPawLen));
-    path.MoveTo(CFX_PointF(fX, fY));
-    path.LineTo(CFX_PointF(fX, fY + fPawLen));
-    path.MoveTo(CFX_PointF(fX + 2, fY));
-    path.LineTo(CFX_PointF(fX + 2, fY + fPawLen));
-
-    pGraphics->SetLineWidth(1);
-    pGraphics->SetStrokeColor(
-        CXFA_GEColor(m_pThemeData->clrPawColorLight[eState - 1]));
-    pGraphics->StrokePath(&path, &matrix);
-    fY++;
-
-    path.Clear();
-    path.MoveTo(CFX_PointF(fX - 3, fY));
-    path.LineTo(CFX_PointF(fX - 3, fY + fPawLen));
-    path.MoveTo(CFX_PointF(fX - 1, fY));
-    path.LineTo(CFX_PointF(fX - 1, fY + fPawLen));
-    path.MoveTo(CFX_PointF(fX + 1, fY));
-    path.LineTo(CFX_PointF(fX + 1, fY + fPawLen));
-    path.MoveTo(CFX_PointF(fX + 3, fY));
-    path.LineTo(CFX_PointF(fX + 3, fY + fPawLen));
-
-    pGraphics->SetLineWidth(1);
-    pGraphics->SetStrokeColor(
-        CXFA_GEColor(m_pThemeData->clrPawColorDark[eState - 1]));
-    pGraphics->StrokePath(&path, &matrix);
-  }
-}
-
-void CFWL_ScrollBarTP::DrawTrack(CXFA_Graphics* pGraphics,
+void CFWL_ScrollBarTP::DrawTrack(CFGAS_GEGraphics* pGraphics,
                                  const CFX_RectF& rect,
                                  bool bVert,
                                  FWLTHEME_STATE eState,
                                  bool bLowerTrack,
                                  const CFX_Matrix& matrix) {
-  if (eState < FWLTHEME_STATE_Normal || eState > FWLTHEME_STATE_Disable)
+  if (eState < FWLTHEME_STATE::kNormal || eState > FWLTHEME_STATE::kDisable)
     return;
 
-  pGraphics->SaveGraphState();
-  CXFA_GEPath path;
-  float fRight = rect.right();
-  float fBottom = rect.bottom();
-  if (bVert) {
-    path.AddRectangle(rect.left, rect.top, 1, rect.height);
-    path.AddRectangle(fRight - 1, rect.top, 1, rect.height);
-  } else {
-    path.AddRectangle(rect.left, rect.top, rect.width, 1);
-    path.AddRectangle(rect.left, fBottom - 1, rect.width, 1);
+  {
+    CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
+    CFGAS_GEPath path;
+    float fRight = rect.right();
+    float fBottom = rect.bottom();
+    if (bVert) {
+      path.AddRectangle(rect.left, rect.top, 1, rect.height);
+      path.AddRectangle(fRight - 1, rect.top, 1, rect.height);
+    } else {
+      path.AddRectangle(rect.left, rect.top, rect.width, 1);
+      path.AddRectangle(rect.left, fBottom - 1, rect.width, 1);
+    }
+    pGraphics->SetFillColor(CFGAS_GEColor(ArgbEncode(255, 238, 237, 229)));
+    pGraphics->FillPath(path, CFX_FillRenderOptions::FillType::kWinding,
+                        matrix);
+    path.Clear();
+    path.AddRectangle(rect.left + 1, rect.top, rect.width - 2, rect.height);
   }
-  pGraphics->SetFillColor(CXFA_GEColor(ArgbEncode(255, 238, 237, 229)));
-  pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
-  path.Clear();
-  path.AddRectangle(rect.left + 1, rect.top, rect.width - 2, rect.height);
-  pGraphics->RestoreGraphState();
   FillSolidRect(pGraphics, m_pThemeData->clrTrackBKEnd, rect, matrix);
 }
 
-void CFWL_ScrollBarTP::DrawMaxMinBtn(CXFA_Graphics* pGraphics,
+void CFWL_ScrollBarTP::DrawMaxMinBtn(CFGAS_GEGraphics* pGraphics,
                                      const CFX_RectF& rect,
                                      FWLTHEME_DIRECTION eDict,
                                      FWLTHEME_STATE eState,
                                      const CFX_Matrix& matrix) {
-  DrawTrack(pGraphics, rect,
-            eDict == FWLTHEME_DIRECTION_Up || eDict == FWLTHEME_DIRECTION_Down,
-            eState, true, matrix);
+  DrawTrack(
+      pGraphics, rect,
+      eDict == FWLTHEME_DIRECTION::kUp || eDict == FWLTHEME_DIRECTION::kDown,
+      eState, true, matrix);
   CFX_RectF rtArrowBtn = rect;
   rtArrowBtn.Deflate(1, 1, 1, 1);
   DrawArrowBtn(pGraphics, rtArrowBtn, eDict, eState, matrix);
 }
 
 void CFWL_ScrollBarTP::SetThemeData() {
-  m_pThemeData->clrPawColorLight[3] = ArgbEncode(0xff, 208, 223, 172);
-  m_pThemeData->clrPawColorDark[3] = ArgbEncode(0xff, 140, 157, 115);
-  m_pThemeData->clrBtnBK[3][0] = ArgbEncode(0xff, 164, 180, 139);
-  m_pThemeData->clrBtnBK[3][1] = ArgbEncode(0xff, 141, 157, 115);
-  m_pThemeData->clrBtnBorder[3] = ArgbEncode(0xff, 236, 233, 216);
-  m_pThemeData->clrPawColorLight[0] = ArgbEncode(0xff, 238, 244, 254);
-  m_pThemeData->clrPawColorDark[0] = ArgbEncode(0xff, 140, 176, 248);
-  m_pThemeData->clrBtnBK[0][0] = ArgbEncode(0xff, 197, 213, 252);
-  m_pThemeData->clrBtnBK[0][1] = ArgbEncode(0xff, 182, 205, 251);
-  m_pThemeData->clrBtnBorder[0] = ArgbEncode(0xff, 148, 176, 221);
-  m_pThemeData->clrPawColorLight[1] = ArgbEncode(0xff, 252, 253, 255);
-  m_pThemeData->clrPawColorDark[1] = ArgbEncode(0xff, 156, 197, 255);
-  m_pThemeData->clrBtnBK[1][0] = ArgbEncode(0xff, 216, 232, 255);
-  m_pThemeData->clrBtnBK[1][1] = ArgbEncode(0xff, 204, 225, 255);
-  m_pThemeData->clrBtnBorder[1] = ArgbEncode(0xff, 218, 230, 254);
-  m_pThemeData->clrPawColorLight[2] = ArgbEncode(0xff, 207, 221, 253);
-  m_pThemeData->clrPawColorDark[2] = ArgbEncode(0xff, 131, 158, 216);
-  m_pThemeData->clrBtnBK[2][0] = ArgbEncode(0xff, 167, 190, 245);
-  m_pThemeData->clrBtnBK[2][1] = ArgbEncode(0xff, 146, 179, 249);
-  m_pThemeData->clrBtnBorder[2] = ArgbEncode(0xff, 124, 159, 211);
   m_pThemeData->clrTrackBKStart = ArgbEncode(0xff, 243, 241, 236);
   m_pThemeData->clrTrackBKEnd = ArgbEncode(0xff, 254, 254, 251);
+  m_pThemeData->clrBtnBK[0][0] = ArgbEncode(0xff, 197, 213, 252);
+  m_pThemeData->clrBtnBK[0][1] = ArgbEncode(0xff, 182, 205, 251);
+  m_pThemeData->clrBtnBK[1][0] = ArgbEncode(0xff, 216, 232, 255);
+  m_pThemeData->clrBtnBK[1][1] = ArgbEncode(0xff, 204, 225, 255);
+  m_pThemeData->clrBtnBK[2][0] = ArgbEncode(0xff, 167, 190, 245);
+  m_pThemeData->clrBtnBK[2][1] = ArgbEncode(0xff, 146, 179, 249);
+  m_pThemeData->clrBtnBK[3][0] = ArgbEncode(0xff, 164, 180, 139);
+  m_pThemeData->clrBtnBK[3][1] = ArgbEncode(0xff, 141, 157, 115);
+  m_pThemeData->clrBtnBorder[0] = ArgbEncode(0xff, 148, 176, 221);
+  m_pThemeData->clrBtnBorder[1] = ArgbEncode(0xff, 218, 230, 254);
+  m_pThemeData->clrBtnBorder[2] = ArgbEncode(0xff, 124, 159, 211);
+  m_pThemeData->clrBtnBorder[3] = ArgbEncode(0xff, 236, 233, 216);
 }
diff --git a/xfa/fwl/theme/cfwl_scrollbartp.h b/xfa/fwl/theme/cfwl_scrollbartp.h
index f07d102..714cf4d 100644
--- a/xfa/fwl/theme/cfwl_scrollbartp.h
+++ b/xfa/fwl/theme/cfwl_scrollbartp.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,48 +9,43 @@
 
 #include <memory>
 
+#include "fxjs/gc/heap.h"
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
 class CFWL_ScrollBarTP final : public CFWL_WidgetTP {
  public:
-  CFWL_ScrollBarTP();
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CFWL_ScrollBarTP() override;
 
-  // CFWL_WidgetTP
+  // CFWL_WidgetTP:
   void DrawBackground(const CFWL_ThemeBackground& pParams) override;
 
  private:
   struct SBThemeData {
     FX_ARGB clrTrackBKStart;
     FX_ARGB clrTrackBKEnd;
-    FX_ARGB clrPawColorLight[4];
-    FX_ARGB clrPawColorDark[4];
     FX_ARGB clrBtnBK[4][2];
     FX_ARGB clrBtnBorder[4];
   };
 
-  void DrawThumbBtn(CXFA_Graphics* pGraphics,
+  CFWL_ScrollBarTP();
+
+  void DrawThumbBtn(CFGAS_GEGraphics* pGraphics,
                     const CFX_RectF& rect,
                     bool bVert,
                     FWLTHEME_STATE eState,
-                    bool bPawButton,
                     const CFX_Matrix& matrix);
-  void DrawTrack(CXFA_Graphics* pGraphics,
+  void DrawTrack(CFGAS_GEGraphics* pGraphics,
                  const CFX_RectF& rect,
                  bool bVert,
                  FWLTHEME_STATE eState,
                  bool bLowerTrack,
                  const CFX_Matrix& matrix);
-  void DrawMaxMinBtn(CXFA_Graphics* pGraphics,
+  void DrawMaxMinBtn(CFGAS_GEGraphics* pGraphics,
                      const CFX_RectF& rect,
                      FWLTHEME_DIRECTION eDict,
                      FWLTHEME_STATE eState,
                      const CFX_Matrix& matrix);
-  void DrawPaw(CXFA_Graphics* pGraphics,
-               const CFX_RectF& rect,
-               bool bVert,
-               FWLTHEME_STATE eState,
-               const CFX_Matrix& matrix);
   void SetThemeData();
 
   std::unique_ptr<SBThemeData> m_pThemeData;
diff --git a/xfa/fwl/theme/cfwl_utils.h b/xfa/fwl/theme/cfwl_utils.h
index 8aa9a3c..37bfc50 100644
--- a/xfa/fwl/theme/cfwl_utils.h
+++ b/xfa/fwl/theme/cfwl_utils.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,27 +7,14 @@
 #ifndef XFA_FWL_THEME_CFWL_UTILS_H_
 #define XFA_FWL_THEME_CFWL_UTILS_H_
 
-#include "core/fxcrt/fx_system.h"
+#include <stdint.h>
 
-enum FWLTHEME_EDGE {
-  FWLTHEME_EDGE_Flat = 0,
-  FWLTHEME_EDGE_Raised,
-  FWLTHEME_EDGE_Sunken
-};
+#include "core/fxge/dib/fx_dib.h"
 
-enum FWLTHEME_STATE {
-  FWLTHEME_STATE_Normal = 1,
-  FWLTHEME_STATE_Hover,
-  FWLTHEME_STATE_Pressed,
-  FWLTHEME_STATE_Disable
-};
+// Values matter, used for indexing.
+enum class FWLTHEME_STATE : uint8_t { kNormal = 1, kHover, kPressed, kDisable };
 
-enum FWLTHEME_DIRECTION {
-  FWLTHEME_DIRECTION_Up = 0,
-  FWLTHEME_DIRECTION_Down,
-  FWLTHEME_DIRECTION_Left,
-  FWLTHEME_DIRECTION_Right
-};
+enum class FWLTHEME_DIRECTION : uint8_t { kUp = 0, kDown, kLeft, kRight };
 
 #define FWLTHEME_COLOR_EDGERB1 (ArgbEncode(255, 241, 239, 226))
 #define FWLTHEME_COLOR_Background (ArgbEncode(255, 236, 233, 216))
diff --git a/xfa/fwl/theme/cfwl_widgettp.cpp b/xfa/fwl/theme/cfwl_widgettp.cpp
index 27caf99..8a265fe 100644
--- a/xfa/fwl/theme/cfwl_widgettp.cpp
+++ b/xfa/fwl/theme/cfwl_widgettp.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,60 +9,47 @@
 #include <algorithm>
 #include <utility>
 
-#include "core/fxge/render_defines.h"
-#include "third_party/base/ptr_util.h"
 #include "xfa/fde/cfde_textout.h"
-#include "xfa/fgas/font/cfgas_fontmgr.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
+#include "xfa/fgas/graphics/cfgas_gecolor.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_themepart.h"
 #include "xfa/fwl/cfwl_themetext.h"
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
-#include "xfa/fxgraphics/cxfa_gecolor.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
-#include "xfa/fxgraphics/cxfa_geshading.h"
-
-namespace {
-
-CFWL_FontManager* g_FontManager = nullptr;
-
-}  // namespace
 
 CFWL_WidgetTP::CFWL_WidgetTP() = default;
 
 CFWL_WidgetTP::~CFWL_WidgetTP() = default;
 
+void CFWL_WidgetTP::Trace(cppgc::Visitor* visitor) const {}
+
 void CFWL_WidgetTP::DrawBackground(const CFWL_ThemeBackground& pParams) {}
 
 void CFWL_WidgetTP::DrawText(const CFWL_ThemeText& pParams) {
-  EnsureTTOInitialized();
-  int32_t iLen = pParams.m_wsText.GetLength();
-  if (iLen <= 0)
+  EnsureTTOInitialized(pParams.GetWidget()->GetThemeProvider());
+  if (pParams.m_wsText.IsEmpty())
     return;
 
-  CXFA_Graphics* pGraphics = pParams.m_pGraphics;
+  CFGAS_GEGraphics* pGraphics = pParams.GetGraphics();
   m_pTextOut->SetStyles(pParams.m_dwTTOStyles);
   m_pTextOut->SetAlignment(pParams.m_iTTOAlign);
 
   CFX_Matrix matrix = pParams.m_matrix;
   matrix.Concat(*pGraphics->GetMatrix());
   m_pTextOut->SetMatrix(matrix);
-  m_pTextOut->DrawLogicText(pGraphics->GetRenderDevice(),
-                            WideStringView(pParams.m_wsText.c_str(), iLen),
-                            pParams.m_rtPart);
-}
-
-const RetainPtr<CFGAS_GEFont>& CFWL_WidgetTP::GetFont() const {
-  return m_pFDEFont;
+  m_pTextOut->DrawLogicText(pGraphics->GetRenderDevice(), pParams.m_wsText,
+                            pParams.m_PartRect);
 }
 
 void CFWL_WidgetTP::InitializeArrowColorData() {
   if (m_pColorData)
     return;
 
-  m_pColorData = pdfium::MakeUnique<CColorData>();
+  m_pColorData = std::make_unique<CColorData>();
   m_pColorData->clrBorder[0] = ArgbEncode(255, 202, 216, 249);
   m_pColorData->clrBorder[1] = ArgbEncode(255, 171, 190, 233);
   m_pColorData->clrBorder[2] = ArgbEncode(255, 135, 147, 219);
@@ -81,82 +68,81 @@
   m_pColorData->clrSign[3] = ArgbEncode(255, 128, 128, 128);
 }
 
-void CFWL_WidgetTP::EnsureTTOInitialized() {
+void CFWL_WidgetTP::EnsureTTOInitialized(IFWL_ThemeProvider* pProvider) {
   if (m_pTextOut)
     return;
 
-  m_pFDEFont = CFWL_FontManager::GetInstance()->FindFont(L"Helvetica", 0, 0);
-  m_pTextOut = pdfium::MakeUnique<CFDE_TextOut>();
-  m_pTextOut->SetFont(m_pFDEFont);
+  m_pTextOut = std::make_unique<CFDE_TextOut>();
+  m_pTextOut->SetFont(pProvider->GetFWLFont());
   m_pTextOut->SetFontSize(FWLTHEME_CAPACITY_FontSize);
   m_pTextOut->SetTextColor(FWLTHEME_CAPACITY_TextColor);
 }
 
-void CFWL_WidgetTP::DrawBorder(CXFA_Graphics* pGraphics,
+void CFWL_WidgetTP::DrawBorder(CFGAS_GEGraphics* pGraphics,
                                const CFX_RectF& rect,
                                const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
 
-  CXFA_GEPath path;
+  CFGAS_GEPath path;
   path.AddRectangle(rect.left, rect.top, rect.width, rect.height);
   path.AddRectangle(rect.left + 1, rect.top + 1, rect.width - 2,
                     rect.height - 2);
-  pGraphics->SaveGraphState();
-  pGraphics->SetFillColor(CXFA_GEColor(ArgbEncode(255, 0, 0, 0)));
-  pGraphics->FillPath(&path, FXFILL_ALTERNATE, &matrix);
-  pGraphics->RestoreGraphState();
+
+  CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
+  pGraphics->SetFillColor(CFGAS_GEColor(ArgbEncode(255, 0, 0, 0)));
+  pGraphics->FillPath(path, CFX_FillRenderOptions::FillType::kEvenOdd, matrix);
 }
 
-void CFWL_WidgetTP::FillBackground(CXFA_Graphics* pGraphics,
+void CFWL_WidgetTP::FillBackground(CFGAS_GEGraphics* pGraphics,
                                    const CFX_RectF& rect,
                                    const CFX_Matrix& matrix) {
   FillSolidRect(pGraphics, FWLTHEME_COLOR_Background, rect, matrix);
 }
 
-void CFWL_WidgetTP::FillSolidRect(CXFA_Graphics* pGraphics,
+void CFWL_WidgetTP::FillSolidRect(CFGAS_GEGraphics* pGraphics,
                                   FX_ARGB fillColor,
                                   const CFX_RectF& rect,
                                   const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
 
-  CXFA_GEPath path;
+  CFGAS_GEPath path;
   path.AddRectangle(rect.left, rect.top, rect.width, rect.height);
-  pGraphics->SaveGraphState();
-  pGraphics->SetFillColor(CXFA_GEColor(fillColor));
-  pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
-  pGraphics->RestoreGraphState();
+
+  CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
+  pGraphics->SetFillColor(CFGAS_GEColor(fillColor));
+  pGraphics->FillPath(path, CFX_FillRenderOptions::FillType::kWinding, matrix);
 }
 
-void CFWL_WidgetTP::DrawFocus(CXFA_Graphics* pGraphics,
+void CFWL_WidgetTP::DrawFocus(CFGAS_GEGraphics* pGraphics,
                               const CFX_RectF& rect,
                               const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
 
-  CXFA_GEPath path;
+  CFGAS_GEPath path;
   path.AddRectangle(rect.left, rect.top, rect.width, rect.height);
-  pGraphics->SaveGraphState();
-  pGraphics->SetStrokeColor(CXFA_GEColor(0xFF000000));
+
+  CFGAS_GEGraphics::StateRestorer restorer(pGraphics);
+  pGraphics->SetStrokeColor(CFGAS_GEColor(0xFF000000));
   static constexpr float kDashPattern[2] = {1, 1};
-  pGraphics->SetLineDash(0.0f, kDashPattern, FX_ArraySize(kDashPattern));
-  pGraphics->StrokePath(&path, &matrix);
-  pGraphics->RestoreGraphState();
+  pGraphics->SetLineDash(0.0f, kDashPattern);
+  pGraphics->StrokePath(path, matrix);
 }
 
-void CFWL_WidgetTP::DrawArrow(CXFA_Graphics* pGraphics,
+void CFWL_WidgetTP::DrawArrow(CFGAS_GEGraphics* pGraphics,
                               const CFX_RectF& rect,
                               FWLTHEME_DIRECTION eDict,
                               FX_ARGB argSign,
                               const CFX_Matrix& matrix) {
   bool bVert =
-      (eDict == FWLTHEME_DIRECTION_Up || eDict == FWLTHEME_DIRECTION_Down);
+      (eDict == FWLTHEME_DIRECTION::kUp || eDict == FWLTHEME_DIRECTION::kDown);
   float fLeft = ((rect.width - (bVert ? 9 : 6)) / 2 + rect.left) + 0.5f;
   float fTop = ((rect.height - (bVert ? 6 : 9)) / 2 + rect.top) + 0.5f;
-  CXFA_GEPath path;
+  CFGAS_GEPath path;
   switch (eDict) {
-    case FWLTHEME_DIRECTION_Down: {
+    case FWLTHEME_DIRECTION::kDown:
       path.MoveTo(CFX_PointF(fLeft, fTop + 1));
       path.LineTo(CFX_PointF(fLeft + 4, fTop + 5));
       path.LineTo(CFX_PointF(fLeft + 8, fTop + 1));
@@ -164,8 +150,7 @@
       path.LineTo(CFX_PointF(fLeft + 4, fTop + 3));
       path.LineTo(CFX_PointF(fLeft + 1, fTop));
       break;
-    }
-    case FWLTHEME_DIRECTION_Up: {
+    case FWLTHEME_DIRECTION::kUp:
       path.MoveTo(CFX_PointF(fLeft, fTop + 4));
       path.LineTo(CFX_PointF(fLeft + 4, fTop));
       path.LineTo(CFX_PointF(fLeft + 8, fTop + 4));
@@ -173,8 +158,7 @@
       path.LineTo(CFX_PointF(fLeft + 4, fTop + 2));
       path.LineTo(CFX_PointF(fLeft + 1, fTop + 5));
       break;
-    }
-    case FWLTHEME_DIRECTION_Right: {
+    case FWLTHEME_DIRECTION::kRight:
       path.MoveTo(CFX_PointF(fLeft + 1, fTop));
       path.LineTo(CFX_PointF(fLeft + 5, fTop + 4));
       path.LineTo(CFX_PointF(fLeft + 1, fTop + 8));
@@ -182,8 +166,7 @@
       path.LineTo(CFX_PointF(fLeft + 3, fTop + 4));
       path.LineTo(CFX_PointF(fLeft, fTop + 1));
       break;
-    }
-    case FWLTHEME_DIRECTION_Left: {
+    case FWLTHEME_DIRECTION::kLeft:
       path.MoveTo(CFX_PointF(fLeft, fTop + 4));
       path.LineTo(CFX_PointF(fLeft + 4, fTop));
       path.LineTo(CFX_PointF(fLeft + 5, fTop + 1));
@@ -191,95 +174,34 @@
       path.LineTo(CFX_PointF(fLeft + 5, fTop + 7));
       path.LineTo(CFX_PointF(fLeft + 4, fTop + 8));
       break;
-    }
   }
-  pGraphics->SetFillColor(CXFA_GEColor(argSign));
-  pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
+  pGraphics->SetFillColor(CFGAS_GEColor(argSign));
+  pGraphics->FillPath(path, CFX_FillRenderOptions::FillType::kWinding, matrix);
 }
 
-void CFWL_WidgetTP::DrawBtn(CXFA_Graphics* pGraphics,
+void CFWL_WidgetTP::DrawBtn(CFGAS_GEGraphics* pGraphics,
                             const CFX_RectF& rect,
                             FWLTHEME_STATE eState,
                             const CFX_Matrix& matrix) {
   InitializeArrowColorData();
-  FillSolidRect(pGraphics, m_pColorData->clrEnd[eState - 1], rect, matrix);
+  FillSolidRect(pGraphics,
+                m_pColorData->clrEnd[static_cast<size_t>(eState) - 1], rect,
+                matrix);
 
-  CXFA_GEPath path;
+  CFGAS_GEPath path;
   path.AddRectangle(rect.left, rect.top, rect.width, rect.height);
-  pGraphics->SetStrokeColor(CXFA_GEColor(m_pColorData->clrBorder[eState - 1]));
-  pGraphics->StrokePath(&path, &matrix);
+  pGraphics->SetStrokeColor(
+      CFGAS_GEColor(m_pColorData->clrBorder[static_cast<size_t>(eState) - 1]));
+  pGraphics->StrokePath(path, matrix);
 }
 
-void CFWL_WidgetTP::DrawArrowBtn(CXFA_Graphics* pGraphics,
+void CFWL_WidgetTP::DrawArrowBtn(CFGAS_GEGraphics* pGraphics,
                                  const CFX_RectF& rect,
                                  FWLTHEME_DIRECTION eDict,
                                  FWLTHEME_STATE eState,
                                  const CFX_Matrix& matrix) {
   DrawBtn(pGraphics, rect, eState, matrix);
   InitializeArrowColorData();
-  DrawArrow(pGraphics, rect, eDict, m_pColorData->clrSign[eState - 1], matrix);
+  DrawArrow(pGraphics, rect, eDict,
+            m_pColorData->clrSign[static_cast<size_t>(eState) - 1], matrix);
 }
-
-CFWL_FontData::CFWL_FontData() : m_dwStyles(0), m_dwCodePage(0) {}
-
-CFWL_FontData::~CFWL_FontData() {}
-
-bool CFWL_FontData::Equal(WideStringView wsFontFamily,
-                          uint32_t dwFontStyles,
-                          uint16_t wCodePage) {
-  return m_wsFamily == wsFontFamily && m_dwStyles == dwFontStyles &&
-         m_dwCodePage == wCodePage;
-}
-
-bool CFWL_FontData::LoadFont(WideStringView wsFontFamily,
-                             uint32_t dwFontStyles,
-                             uint16_t dwCodePage) {
-  m_wsFamily = wsFontFamily;
-  m_dwStyles = dwFontStyles;
-  m_dwCodePage = dwCodePage;
-  if (!m_pFontMgr) {
-    m_pFontMgr = pdfium::MakeUnique<CFGAS_FontMgr>();
-    if (!m_pFontMgr->EnumFonts())
-      m_pFontMgr = nullptr;
-  }
-
-  // TODO(tsepez): check usage of c_str() below.
-  m_pFont = CFGAS_GEFont::LoadFont(wsFontFamily.unterminated_c_str(),
-                                   dwFontStyles, dwCodePage, m_pFontMgr.get());
-  return !!m_pFont;
-}
-
-RetainPtr<CFGAS_GEFont> CFWL_FontData::GetFont() const {
-  return m_pFont;
-}
-
-CFWL_FontManager* CFWL_FontManager::GetInstance() {
-  if (!g_FontManager)
-    g_FontManager = new CFWL_FontManager;
-  return g_FontManager;
-}
-
-void CFWL_FontManager::DestroyInstance() {
-  delete g_FontManager;
-  g_FontManager = nullptr;
-}
-
-CFWL_FontManager::CFWL_FontManager() = default;
-
-CFWL_FontManager::~CFWL_FontManager() = default;
-
-RetainPtr<CFGAS_GEFont> CFWL_FontManager::FindFont(WideStringView wsFontFamily,
-                                                   uint32_t dwFontStyles,
-                                                   uint16_t wCodePage) {
-  for (const auto& pData : m_FontsArray) {
-    if (pData->Equal(wsFontFamily, dwFontStyles, wCodePage))
-      return pData->GetFont();
-  }
-  auto pFontData = pdfium::MakeUnique<CFWL_FontData>();
-  if (!pFontData->LoadFont(wsFontFamily, dwFontStyles, wCodePage))
-    return nullptr;
-
-  m_FontsArray.push_back(std::move(pFontData));
-  return m_FontsArray.back()->GetFont();
-}
-
diff --git a/xfa/fwl/theme/cfwl_widgettp.h b/xfa/fwl/theme/cfwl_widgettp.h
index c4fa61a..cbb2896 100644
--- a/xfa/fwl/theme/cfwl_widgettp.h
+++ b/xfa/fwl/theme/cfwl_widgettp.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,110 +8,73 @@
 #define XFA_FWL_THEME_CFWL_WIDGETTP_H_
 
 #include <memory>
-#include <vector>
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "v8/include/cppgc/garbage-collected.h"
 #include "xfa/fwl/theme/cfwl_utils.h"
-#include "xfa/fxgraphics/cxfa_graphics.h"
 
 class CFDE_TextOut;
-class CFGAS_FontMgr;
-class CFGAS_GEFont;
+class CFGAS_GEGraphics;
 class CFWL_ThemeBackground;
 class CFWL_ThemeText;
+class IFWL_ThemeProvider;
 
-class CFWL_WidgetTP {
+class CFWL_WidgetTP : public cppgc::GarbageCollected<CFWL_WidgetTP> {
  public:
   virtual ~CFWL_WidgetTP();
 
   virtual void DrawBackground(const CFWL_ThemeBackground& pParams);
   virtual void DrawText(const CFWL_ThemeText& pParams);
 
-  const RetainPtr<CFGAS_GEFont>& GetFont() const;
+  // Non-virtual, nothing to trace in subclasses at present.
+  void Trace(cppgc::Visitor* visitor) const;
 
  protected:
   struct CColorData {
-    FX_ARGB clrBorder[4];
-    FX_ARGB clrStart[4];
-    FX_ARGB clrEnd[4];
-    FX_ARGB clrSign[4];
+    FX_ARGB clrBorder[4];  // Indexed by enum FWLTHEME_STATE - 1.
+    FX_ARGB clrStart[4];   // Indexed by enum FWLTHEME_STATE - 1.
+    FX_ARGB clrEnd[4];     // Indexed by enum FWLTHEME_STATE - 1.
+    FX_ARGB clrSign[4];    // Indexed by enum FWLTHEME_STATE - 1.
   };
 
   CFWL_WidgetTP();
 
   void InitializeArrowColorData();
-  void EnsureTTOInitialized();
+  void EnsureTTOInitialized(IFWL_ThemeProvider* pProvider);
 
-  void DrawBorder(CXFA_Graphics* pGraphics,
+  void DrawBorder(CFGAS_GEGraphics* pGraphics,
                   const CFX_RectF& rect,
                   const CFX_Matrix& matrix);
-  void FillBackground(CXFA_Graphics* pGraphics,
+  void FillBackground(CFGAS_GEGraphics* pGraphics,
                       const CFX_RectF& rect,
                       const CFX_Matrix& matrix);
-  void FillSolidRect(CXFA_Graphics* pGraphics,
+  void FillSolidRect(CFGAS_GEGraphics* pGraphics,
                      FX_ARGB fillColor,
                      const CFX_RectF& rect,
                      const CFX_Matrix& matrix);
-  void DrawFocus(CXFA_Graphics* pGraphics,
+  void DrawFocus(CFGAS_GEGraphics* pGraphics,
                  const CFX_RectF& rect,
                  const CFX_Matrix& matrix);
-  void DrawArrow(CXFA_Graphics* pGraphics,
+  void DrawArrow(CFGAS_GEGraphics* pGraphics,
                  const CFX_RectF& rect,
                  FWLTHEME_DIRECTION eDict,
                  FX_ARGB argSign,
                  const CFX_Matrix& matrix);
-  void DrawBtn(CXFA_Graphics* pGraphics,
+  void DrawBtn(CFGAS_GEGraphics* pGraphics,
                const CFX_RectF& rect,
                FWLTHEME_STATE eState,
                const CFX_Matrix& matrix);
-  void DrawArrowBtn(CXFA_Graphics* pGraphics,
+  void DrawArrowBtn(CFGAS_GEGraphics* pGraphics,
                     const CFX_RectF& rect,
                     FWLTHEME_DIRECTION eDict,
                     FWLTHEME_STATE eState,
                     const CFX_Matrix& matrix);
 
   std::unique_ptr<CFDE_TextOut> m_pTextOut;
-  RetainPtr<CFGAS_GEFont> m_pFDEFont;
   std::unique_ptr<CColorData> m_pColorData;
 };
 
-class CFWL_FontData final {
- public:
-  CFWL_FontData();
-  ~CFWL_FontData();
-
-  bool Equal(WideStringView wsFontFamily,
-             uint32_t dwFontStyles,
-             uint16_t wCodePage);
-  bool LoadFont(WideStringView wsFontFamily,
-                uint32_t dwFontStyles,
-                uint16_t wCodePage);
-  RetainPtr<CFGAS_GEFont> GetFont() const;
-
- protected:
-  WideString m_wsFamily;
-  uint32_t m_dwStyles;
-  uint32_t m_dwCodePage;
-  std::unique_ptr<CFGAS_FontMgr> m_pFontMgr;
-  RetainPtr<CFGAS_GEFont> m_pFont;
-};
-
-class CFWL_FontManager final {
- public:
-  static CFWL_FontManager* GetInstance();
-  static void DestroyInstance();
-
-  RetainPtr<CFGAS_GEFont> FindFont(WideStringView wsFontFamily,
-                                   uint32_t dwFontStyles,
-                                   uint16_t dwCodePage);
-
- private:
-  CFWL_FontManager();
-  ~CFWL_FontManager();
-
-  std::vector<std::unique_ptr<CFWL_FontData>> m_FontsArray;
-};
-
 #endif  // XFA_FWL_THEME_CFWL_WIDGETTP_H_
diff --git a/xfa/fxfa/BUILD.gn b/xfa/fxfa/BUILD.gn
index 8490abe..6a862f6 100644
--- a/xfa/fxfa/BUILD.gn
+++ b/xfa/fxfa/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -71,22 +71,12 @@
     "cxfa_fwltheme.h",
     "cxfa_imagerenderer.cpp",
     "cxfa_imagerenderer.h",
-    "cxfa_loadercontext.cpp",
-    "cxfa_loadercontext.h",
-    "cxfa_pieceline.cpp",
-    "cxfa_pieceline.h",
     "cxfa_readynodeiterator.cpp",
     "cxfa_readynodeiterator.h",
-    "cxfa_rendercontext.cpp",
-    "cxfa_rendercontext.h",
     "cxfa_textlayout.cpp",
     "cxfa_textlayout.h",
-    "cxfa_textparsecontext.cpp",
-    "cxfa_textparsecontext.h",
     "cxfa_textparser.cpp",
     "cxfa_textparser.h",
-    "cxfa_textpiece.cpp",
-    "cxfa_textpiece.h",
     "cxfa_textprovider.cpp",
     "cxfa_textprovider.h",
     "cxfa_texttabstopscontext.cpp",
@@ -95,6 +85,7 @@
     "fxfa_basic.h",
   ]
   deps = [
+    "../../constants",
     "../../core/fpdfapi/parser",
     "../../core/fpdfdoc",
     "../../core/fxcodec",
@@ -103,11 +94,12 @@
     "../../core/fxge",
     "../../fxbarcode",
     "../../fxjs",
+    "../../fxjs:gc",
     "../fde",
-    "../fgas",
+    "../fgas/font",
+    "../fgas/graphics",
     "../fgas/layout",
     "../fwl",
-    "../fxgraphics",
     "layout",
     "parser",
   ]
@@ -117,7 +109,8 @@
     "parser",
   ]
   configs += [
-    "../../:pdfium_core_config",
+    "../../:pdfium_strict_config",
+    "../../:pdfium_noshorten_config",
     "../:xfa_warnings",
   ]
   visibility = [ "../../*" ]
@@ -129,6 +122,9 @@
     "cxfa_textparser_unittest.cpp",
     "fxfa_basic_unittest.cpp",
   ]
-  deps = [ ":fxfa" ]
+  deps = [
+    ":fxfa",
+    "../../fxjs:gc",
+  ]
   pdfium_root_dir = "../../"
 }
diff --git a/xfa/fxfa/DEPS b/xfa/fxfa/DEPS
index cd0aa8d..2498160 100644
--- a/xfa/fxfa/DEPS
+++ b/xfa/fxfa/DEPS
@@ -1,6 +1,5 @@
 include_rules = [
   '+fxjs',
-
-  # xfa/fwl should be standalone. https://crbug.com/pdfium/507
+  '+v8/include/cppgc',
   '+xfa/fwl',
 ]
diff --git a/xfa/fxfa/cxfa_eventparam.cpp b/xfa/fxfa/cxfa_eventparam.cpp
index c3f229d..49a7fb6 100644
--- a/xfa/fxfa/cxfa_eventparam.cpp
+++ b/xfa/fxfa/cxfa_eventparam.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,6 @@
 
 #include "xfa/fxfa/cxfa_eventparam.h"
 
-#include "xfa/fxfa/fxfa.h"
-
 CXFA_EventParam::CXFA_EventParam() = default;
 
 CXFA_EventParam::CXFA_EventParam(const CXFA_EventParam& other) = default;
@@ -17,7 +15,8 @@
 CXFA_EventParam& CXFA_EventParam::operator=(const CXFA_EventParam& other) =
     default;
 
-CXFA_EventParam& CXFA_EventParam::operator=(CXFA_EventParam&& other) = default;
+CXFA_EventParam& CXFA_EventParam::operator=(CXFA_EventParam&& other) noexcept =
+    default;
 
 WideString CXFA_EventParam::GetNewText() const {
   return m_wsPrevText.First(m_iSelStart) + m_wsChange +
diff --git a/xfa/fxfa/cxfa_eventparam.h b/xfa/fxfa/cxfa_eventparam.h
index a670263..c546397 100644
--- a/xfa/fxfa/cxfa_eventparam.h
+++ b/xfa/fxfa/cxfa_eventparam.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,11 @@
 #ifndef XFA_FXFA_CXFA_EVENTPARAM_H_
 #define XFA_FXFA_CXFA_EVENTPARAM_H_
 
-#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "v8/include/cppgc/macros.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
-class CXFA_Node;
-
 enum XFA_EVENTTYPE : uint8_t {
   XFA_EVENT_Click,
   XFA_EVENT_Change,
@@ -54,7 +53,7 @@
   ~CXFA_EventParam();
 
   CXFA_EventParam& operator=(const CXFA_EventParam& other);
-  CXFA_EventParam& operator=(CXFA_EventParam&& other);
+  CXFA_EventParam& operator=(CXFA_EventParam&& other) noexcept;
 
   WideString GetNewText() const;
 
@@ -65,10 +64,10 @@
   bool m_bReenter = false;
   bool m_bShift = false;
   bool m_bIsFormReady = false;
+  bool m_bTargeted = true;
   int32_t m_iCommitKey = 0;
   int32_t m_iSelEnd = 0;
   int32_t m_iSelStart = 0;
-  UnownedPtr<CXFA_Node> m_pTarget;
   WideString m_wsResult;
   WideString m_wsChange;
   WideString m_wsFullText;
diff --git a/xfa/fxfa/cxfa_ffapp.cpp b/xfa/fxfa/cxfa_ffapp.cpp
index 656b559..7a79f7d 100644
--- a/xfa/fxfa/cxfa_ffapp.cpp
+++ b/xfa/fxfa/cxfa_ffapp.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,13 +6,6 @@
 
 #include "xfa/fxfa/cxfa_ffapp.h"
 
-#include <algorithm>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "third_party/base/ptr_util.h"
-#include "xfa/fgas/font/cfgas_fontmgr.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
@@ -21,54 +14,49 @@
 #include "xfa/fxfa/cxfa_fwladapterwidgetmgr.h"
 #include "xfa/fxfa/cxfa_fwltheme.h"
 
-namespace {
-
-bool g_skipFontLoadForTesting = false;
-
-}  // namespace
-
-// static
-void CXFA_FFApp::SkipFontLoadForTesting(bool skip) {
-  g_skipFontLoadForTesting = skip;
+CXFA_FFApp::CXFA_FFApp(CallbackIface* pProvider) : m_pProvider(pProvider) {
+  // Ensure fully initialized before making objects based on |this|.
+  m_pXFAFontMgr = cppgc::MakeGarbageCollected<CXFA_FontMgr>(
+      GetHeap()->GetAllocationHandle());
+  m_pFWLApp = cppgc::MakeGarbageCollected<CFWL_App>(
+      GetHeap()->GetAllocationHandle(), this);
 }
 
-CXFA_FFApp::CXFA_FFApp(IXFA_AppProvider* pProvider) : m_pProvider(pProvider) {
-  // Ensure fully initialized before making an app based on |this|.
-  m_pFWLApp = pdfium::MakeUnique<CFWL_App>(this);
+CXFA_FFApp::~CXFA_FFApp() = default;
+
+void CXFA_FFApp::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pXFAFontMgr);
+  visitor->Trace(m_pAdapterWidgetMgr);
+  visitor->Trace(m_pFWLTheme);
+  visitor->Trace(m_pFWLApp);
 }
 
-CXFA_FFApp::~CXFA_FFApp() {}
+bool CXFA_FFApp::LoadFWLTheme(CXFA_FFDoc* doc) {
+  auto* fwl_theme = cppgc::MakeGarbageCollected<CXFA_FWLTheme>(
+      GetHeap()->GetAllocationHandle(), GetHeap(), this);
+  if (!fwl_theme->LoadCalendarFont(doc))
+    return false;
 
-CFGAS_FontMgr* CXFA_FFApp::GetFDEFontMgr() {
-  if (!m_pFDEFontMgr) {
-    m_pFDEFontMgr = pdfium::MakeUnique<CFGAS_FontMgr>();
-    if (!g_skipFontLoadForTesting) {
-      if (!m_pFDEFontMgr->EnumFonts())
-        m_pFDEFontMgr = nullptr;
-    }
-  }
-  return m_pFDEFontMgr.get();
-}
-
-CXFA_FWLTheme* CXFA_FFApp::GetFWLTheme(CXFA_FFDoc* doc) {
-  if (!m_pFWLTheme) {
-    auto fwl_theme = pdfium::MakeUnique<CXFA_FWLTheme>(this);
-    if (fwl_theme->LoadCalendarFont(doc))
-      m_pFWLTheme = std::move(fwl_theme);
-  }
-  return m_pFWLTheme.get();
+  m_pFWLTheme = fwl_theme;
+  return true;
 }
 
 CFWL_WidgetMgr::AdapterIface* CXFA_FFApp::GetWidgetMgrAdapter() {
-  if (!m_pAdapterWidgetMgr)
-    m_pAdapterWidgetMgr = pdfium::MakeUnique<CXFA_FWLAdapterWidgetMgr>();
-  return m_pAdapterWidgetMgr.get();
+  if (!m_pAdapterWidgetMgr) {
+    m_pAdapterWidgetMgr = cppgc::MakeGarbageCollected<CXFA_FWLAdapterWidgetMgr>(
+        GetHeap()->GetAllocationHandle());
+  }
+  return m_pAdapterWidgetMgr;
 }
 
-TimerHandlerIface* CXFA_FFApp::GetTimerHandler() {
+CFX_Timer::HandlerIface* CXFA_FFApp::GetTimerHandler() {
   return m_pProvider->GetTimerHandler();
 }
 
-void CXFA_FFApp::ClearEventTargets() {
-  m_pFWLApp->GetNoteDriver()->ClearEventTargets();
+IFWL_ThemeProvider* CXFA_FFApp::GetThemeProvider() {
+  return m_pFWLTheme;
+}
+
+cppgc::Heap* CXFA_FFApp::GetHeap() {
+  return m_pProvider->GetGCHeap();
 }
diff --git a/xfa/fxfa/cxfa_ffapp.h b/xfa/fxfa/cxfa_ffapp.h
index b01ba2f..d16e5fb 100644
--- a/xfa/fxfa/cxfa_ffapp.h
+++ b/xfa/fxfa/cxfa_ffapp.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,61 +7,152 @@
 #ifndef XFA_FXFA_CXFA_FFAPP_H_
 #define XFA_FXFA_CXFA_FFAPP_H_
 
-#include <memory>
-
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
 #include "xfa/fwl/cfwl_app.h"
-#include "xfa/fxfa/cxfa_fontmgr.h"
-#include "xfa/fxfa/fxfa.h"
 
-class CFGAS_FontMgr;
 class CFWL_WidgetMgr;
+class CXFA_FFDoc;
 class CXFA_FWLAdapterWidgetMgr;
 class CXFA_FWLTheme;
+class CXFA_FontMgr;
+class IFX_SeekableReadStream;
 
-class CXFA_FFApp : public CFWL_App::AdapterIface {
+class CXFA_FFApp final : public cppgc::GarbageCollected<CXFA_FFApp>,
+                         public CFWL_App::AdapterIface {
  public:
-  static void SkipFontLoadForTesting(bool skip);
+  class CallbackIface {
+   public:
+    virtual ~CallbackIface() = default;
 
-  explicit CXFA_FFApp(IXFA_AppProvider* pProvider);
+    /**
+     * Returns the language of the running host application. Such as zh_CN
+     */
+    virtual WideString GetLanguage() = 0;
+
+    /**
+     * Returns the platform of the machine running the script. Such as WIN
+     */
+    virtual WideString GetPlatform() = 0;
+
+    /**
+     * Get application name, such as Phantom.
+     */
+    virtual WideString GetAppName() = 0;
+
+    /**
+     * Get application message box title.
+     */
+    virtual WideString GetAppTitle() const = 0;
+
+    /**
+     * Causes the system to play a sound.
+     * @param[in] dwType The system code for the appropriate sound.0 (Error)1
+     * (Warning)2 (Question)3 (Status)4 (Default)
+     */
+    virtual void Beep(uint32_t dwType) = 0;
+
+    /**
+     * Displays a message box.
+     * @param[in] wsMessage    - Message string to display in box.
+     * @param[in] wsTitle      - Title string for box.
+     * @param[in] dwIconType   - Icon type, refer to XFA_MBICON.
+     * @param[in] dwButtonType - Button type, refer to XFA_MESSAGEBUTTON.
+     * @return A valid integer representing the value of the button pressed by
+     * the user, refer to XFA_ID.
+     */
+    virtual int32_t MsgBox(const WideString& wsMessage,
+                           const WideString& wsTitle,
+                           uint32_t dwIconType,
+                           uint32_t dwButtonType) = 0;
+
+    /**
+     * Get a response from the user.
+     * @param[in] wsQuestion      - Message string to display in box.
+     * @param[in] wsTitle         - Title string for box.
+     * @param[in] wsDefaultAnswer - Initial contents for answer.
+     * @param[in] bMask           - Mask the user input with asterisks when
+     * true,
+     * @return A string containing the user's response.
+     */
+    virtual WideString Response(const WideString& wsQuestion,
+                                const WideString& wsTitle,
+                                const WideString& wsDefaultAnswer,
+                                bool bMask) = 0;
+
+    /**
+     * Download something from somewhere.
+     * @param[in] wsURL - http, ftp, such as
+     * "http://www.w3.org/TR/REC-xml-names/".
+     */
+    virtual RetainPtr<IFX_SeekableReadStream> DownloadURL(
+        const WideString& wsURL) = 0;
+
+    /**
+     * POST data to the given url.
+     * @param[in] wsURL         the URL being uploaded.
+     * @param[in] wsData        the data being uploaded.
+     * @param[in] wsContentType the content type of data including text/html,
+     * text/xml, text/plain, multipart/form-data,
+     *                          application/x-www-form-urlencoded,
+     * application/octet-stream, any valid MIME type.
+     * @param[in] wsEncode      the encode of data including UTF-8, UTF-16,
+     * ISO8859-1, any recognized [IANA]character encoding
+     * @param[in] wsHeader      any additional HTTP headers to be included in
+     * the post.
+     * @param[out] wsResponse   decoded response from server.
+     * @return true Server permitted the post request, false otherwise.
+     */
+    virtual bool PostRequestURL(const WideString& wsURL,
+                                const WideString& wsData,
+                                const WideString& wsContentType,
+                                const WideString& wsEncode,
+                                const WideString& wsHeader,
+                                WideString& wsResponse) = 0;
+
+    /**
+     * PUT data to the given url.
+     * @param[in] wsURL         the URL being uploaded.
+     * @param[in] wsData            the data being uploaded.
+     * @param[in] wsEncode      the encode of data including UTF-8, UTF-16,
+     * ISO8859-1, any recognized [IANA]character encoding
+     * @return true Server permitted the post request, false otherwise.
+     */
+    virtual bool PutRequestURL(const WideString& wsURL,
+                               const WideString& wsData,
+                               const WideString& wsEncode) = 0;
+
+    virtual CFX_Timer::HandlerIface* GetTimerHandler() const = 0;
+    virtual cppgc::Heap* GetGCHeap() const = 0;
+  };
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFApp() override;
 
   // CFWL_App::AdapterIface:
+  void Trace(cppgc::Visitor* visitor) const override;
   CFWL_WidgetMgr::AdapterIface* GetWidgetMgrAdapter() override;
-  TimerHandlerIface* GetTimerHandler() override;
+  CFX_Timer::HandlerIface* GetTimerHandler() override;
+  IFWL_ThemeProvider* GetThemeProvider() override;
+  cppgc::Heap* GetHeap() override;
 
+  bool LoadFWLTheme(CXFA_FFDoc* doc);
   CFWL_WidgetMgr* GetFWLWidgetMgr() const { return m_pFWLApp->GetWidgetMgr(); }
-  CFGAS_FontMgr* GetFDEFontMgr();
-  CXFA_FWLTheme* GetFWLTheme(CXFA_FFDoc* doc);
-
-  IXFA_AppProvider* GetAppProvider() const { return m_pProvider.Get(); }
-  const CFWL_App* GetFWLApp() const { return m_pFWLApp.get(); }
-  CXFA_FontMgr* GetXFAFontMgr() { return &m_pFontMgr; }
-
-  void ClearEventTargets();
+  CallbackIface* GetAppProvider() const { return m_pProvider; }
+  CFWL_App* GetFWLApp() const { return m_pFWLApp; }
+  CXFA_FontMgr* GetXFAFontMgr() const { return m_pXFAFontMgr; }
 
  private:
-  UnownedPtr<IXFA_AppProvider> const m_pProvider;
+  explicit CXFA_FFApp(CallbackIface* pProvider);
 
-  // The fonts stored in the font manager may have been created by the default
-  // font manager. The GEFont::LoadFont call takes the manager as a param and
-  // stores it internally. When you destroy the GEFont it tries to unregister
-  // from the font manager and if the default font manager was destroyed first
-  // you get a use-after-free. The m_pFWLTheme can try to cleanup a GEFont
-  // when it frees, so make sure it gets cleaned up first. That requires
-  // m_pFWLApp to be cleaned up as well.
-  //
-  // TODO(dsinclair): The GEFont should have the FontMgr as the pointer instead
-  // of the DEFFontMgr so this goes away. Bug 561.
-  std::unique_ptr<CFGAS_FontMgr> m_pFDEFontMgr;
-  CXFA_FontMgr m_pFontMgr;
-
-  std::unique_ptr<CXFA_FWLAdapterWidgetMgr> m_pAdapterWidgetMgr;
-
-  // |m_pFWLApp| has to be released first, then |m_pFWLTheme| since the former
-  // may refers to theme manager and the latter refers to font manager.
-  std::unique_ptr<CXFA_FWLTheme> m_pFWLTheme;
-  std::unique_ptr<CFWL_App> m_pFWLApp;
+  UnownedPtr<CallbackIface> const m_pProvider;
+  cppgc::Member<CXFA_FontMgr> m_pXFAFontMgr;
+  cppgc::Member<CXFA_FWLAdapterWidgetMgr> m_pAdapterWidgetMgr;
+  cppgc::Member<CXFA_FWLTheme> m_pFWLTheme;
+  cppgc::Member<CFWL_App> m_pFWLApp;
 };
 
 #endif  // XFA_FXFA_CXFA_FFAPP_H_
diff --git a/xfa/fxfa/cxfa_ffarc.cpp b/xfa/fxfa/cxfa_ffarc.cpp
index 5b26e92..071f479 100644
--- a/xfa/fxfa/cxfa_ffarc.cpp
+++ b/xfa/fxfa/cxfa_ffarc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,9 +11,9 @@
 
 CXFA_FFArc::CXFA_FFArc(CXFA_Node* pNode) : CXFA_FFWidget(pNode) {}
 
-CXFA_FFArc::~CXFA_FFArc() {}
+CXFA_FFArc::~CXFA_FFArc() = default;
 
-void CXFA_FFArc::RenderWidget(CXFA_Graphics* pGS,
+void CXFA_FFArc::RenderWidget(CFGAS_GEGraphics* pGS,
                               const CFX_Matrix& matrix,
                               HighlightOption highlight) {
   if (!HasVisibleStatus())
diff --git a/xfa/fxfa/cxfa_ffarc.h b/xfa/fxfa/cxfa_ffarc.h
index 022e25e..ea19020 100644
--- a/xfa/fxfa/cxfa_ffarc.h
+++ b/xfa/fxfa/cxfa_ffarc.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,13 +11,16 @@
 
 class CXFA_FFArc final : public CXFA_FFWidget {
  public:
-  explicit CXFA_FFArc(CXFA_Node* pnode);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFArc() override;
 
   // CXFA_FFWidget
-  void RenderWidget(CXFA_Graphics* pGS,
+  void RenderWidget(CFGAS_GEGraphics* pGS,
                     const CFX_Matrix& matrix,
                     HighlightOption highlight) override;
+
+ private:
+  explicit CXFA_FFArc(CXFA_Node* pnode);
 };
 
 #endif  // XFA_FXFA_CXFA_FFARC_H_
diff --git a/xfa/fxfa/cxfa_ffbarcode.cpp b/xfa/fxfa/cxfa_ffbarcode.cpp
index 1c541ec..4ba37bb 100644
--- a/xfa/fxfa/cxfa_ffbarcode.cpp
+++ b/xfa/fxfa/cxfa_ffbarcode.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 #include <utility>
 
 #include "core/fxcrt/fx_extension.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 #include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_barcode.h"
 #include "xfa/fwl/cfwl_notedriver.h"
@@ -22,115 +22,119 @@
 
 namespace {
 
-const BarCodeInfo g_BarCodeData[] = {
-    {0x7fb4a18, "ean13", BarcodeType::ean13, BC_EAN13},
-    {0x8d13a3d, "code11", BarcodeType::code11, BC_UNKNOWN},
-    {0x8d149a8, "code49", BarcodeType::code49, BC_UNKNOWN},
-    {0x8d16347, "code93", BarcodeType::code93, BC_UNKNOWN},
-    {0x91a92e2, "upsMaxicode", BarcodeType::upsMaxicode, BC_UNKNOWN},
-    {0xa7d48dc, "fim", BarcodeType::fim, BC_UNKNOWN},
-    {0xb359fe9, "msi", BarcodeType::msi, BC_UNKNOWN},
-    {0x121f738c, "code2Of5Matrix", BarcodeType::code2Of5Matrix, BC_UNKNOWN},
-    {0x15358616, "ucc128", BarcodeType::ucc128, BC_UNKNOWN},
-    {0x1f4bfa05, "rfid", BarcodeType::rfid, BC_UNKNOWN},
-    {0x1fda71bc, "rss14Stacked", BarcodeType::rss14Stacked, BC_UNKNOWN},
-    {0x22065087, "ean8add2", BarcodeType::ean8add2, BC_UNKNOWN},
-    {0x2206508a, "ean8add5", BarcodeType::ean8add5, BC_UNKNOWN},
-    {0x2278366c, "codabar", BarcodeType::codabar, BC_CODABAR},
-    {0x2a039a8d, "telepen", BarcodeType::telepen, BC_UNKNOWN},
-    {0x323ed337, "upcApwcd", BarcodeType::upcApwcd, BC_UNKNOWN},
-    {0x347a1846, "postUSIMB", BarcodeType::postUSIMB, BC_UNKNOWN},
-    {0x391bb836, "code128", BarcodeType::code128, BC_CODE128},
-    {0x398eddaf, "dataMatrix", BarcodeType::dataMatrix, BC_DATAMATRIX},
-    {0x3cff60a8, "upcEadd2", BarcodeType::upcEadd2, BC_UNKNOWN},
-    {0x3cff60ab, "upcEadd5", BarcodeType::upcEadd5, BC_UNKNOWN},
-    {0x402cb188, "code2Of5Standard", BarcodeType::code2Of5Standard, BC_UNKNOWN},
-    {0x411764f7, "aztec", BarcodeType::aztec, BC_UNKNOWN},
-    {0x44d4e84c, "ean8", BarcodeType::ean8, BC_EAN8},
-    {0x48468902, "ucc128sscc", BarcodeType::ucc128sscc, BC_UNKNOWN},
-    {0x4880aea4, "upcAadd2", BarcodeType::upcAadd2, BC_UNKNOWN},
-    {0x4880aea7, "upcAadd5", BarcodeType::upcAadd5, BC_UNKNOWN},
-    {0x54f18256, "code2Of5Industrial", BarcodeType::code2Of5Industrial,
-     BC_UNKNOWN},
-    {0x58e15f25, "rss14Limited", BarcodeType::rss14Limited, BC_UNKNOWN},
-    {0x5c08d1b9, "postAUSReplyPaid", BarcodeType::postAUSReplyPaid, BC_UNKNOWN},
-    {0x5fa700bd, "rss14", BarcodeType::rss14, BC_UNKNOWN},
-    {0x631a7e35, "logmars", BarcodeType::logmars, BC_UNKNOWN},
-    {0x6a236236, "pdf417", BarcodeType::pdf417, BC_PDF417},
-    {0x6d098ece, "upcean2", BarcodeType::upcean2, BC_UNKNOWN},
-    {0x6d098ed1, "upcean5", BarcodeType::upcean5, BC_UNKNOWN},
-    {0x76b04eed, "code3Of9extended", BarcodeType::code3Of9extended, BC_UNKNOWN},
-    {0x7c7db84a, "maxicode", BarcodeType::maxicode, BC_UNKNOWN},
-    {0x8266f7f7, "ucc128random", BarcodeType::ucc128random, BC_UNKNOWN},
-    {0x83eca147, "postUSDPBC", BarcodeType::postUSDPBC, BC_UNKNOWN},
-    {0x8dd71de0, "postAUSStandard", BarcodeType::postAUSStandard, BC_UNKNOWN},
-    {0x98adad85, "plessey", BarcodeType::plessey, BC_UNKNOWN},
-    {0x9f84cce6, "ean13pwcd", BarcodeType::ean13pwcd, BC_UNKNOWN},
-    {0xb514fbe9, "upcA", BarcodeType::upcA, BC_UPCA},
-    {0xb514fbed, "upcE", BarcodeType::upcE, BC_UNKNOWN},
-    {0xb5c6a853, "ean13add2", BarcodeType::ean13add2, BC_UNKNOWN},
-    {0xb5c6a856, "ean13add5", BarcodeType::ean13add5, BC_UNKNOWN},
-    {0xb81fc512, "postUKRM4SCC", BarcodeType::postUKRM4SCC, BC_UNKNOWN},
-    {0xbad34b22, "code128SSCC", BarcodeType::code128SSCC, BC_UNKNOWN},
-    {0xbfbe0cf6, "postUS5Zip", BarcodeType::postUS5Zip, BC_UNKNOWN},
-    {0xc56618e8, "pdf417macro", BarcodeType::pdf417macro, BC_UNKNOWN},
-    {0xca730f8a, "code2Of5Interleaved", BarcodeType::code2Of5Interleaved,
-     BC_UNKNOWN},
-    {0xd0097ac6, "rss14Expanded", BarcodeType::rss14Expanded, BC_UNKNOWN},
-    {0xd25a0240, "postAUSCust2", BarcodeType::postAUSCust2, BC_UNKNOWN},
-    {0xd25a0241, "postAUSCust3", BarcodeType::postAUSCust3, BC_UNKNOWN},
-    {0xd53ed3e7, "rss14Truncated", BarcodeType::rss14Truncated, BC_UNKNOWN},
-    {0xe72bcd57, "code128A", BarcodeType::code128A, BC_UNKNOWN},
-    {0xe72bcd58, "code128B", BarcodeType::code128B, BC_CODE128_B},
-    {0xe72bcd59, "code128C", BarcodeType::code128C, BC_CODE128_C},
-    {0xee83c50f, "rss14StackedOmni", BarcodeType::rss14StackedOmni, BC_UNKNOWN},
-    {0xf2a18f7e, "QRCode", BarcodeType::QRCode, BC_QR_CODE},
-    {0xfaeaf37f, "postUSStandard", BarcodeType::postUSStandard, BC_UNKNOWN},
-    {0xfb48155c, "code3Of9", BarcodeType::code3Of9, BC_CODE39},
+struct BarCodeInfo {
+  uint32_t uHash;        // `pName` hashed as if wide string.
+  const char pName[20];  // Inline string data reduces size for small strings.
+  BC_TYPE eBCType;
 };
 
-Optional<BC_CHAR_ENCODING> CharEncodingFromString(const WideString& value) {
+const BarCodeInfo kBarCodeData[] = {
+    {0x7fb4a18, "ean13", BC_TYPE::kEAN13},
+    {0x8d13a3d, "code11", BC_TYPE::kUnknown},
+    {0x8d149a8, "code49", BC_TYPE::kUnknown},
+    {0x8d16347, "code93", BC_TYPE::kUnknown},
+    {0x91a92e2, "upsMaxicode", BC_TYPE::kUnknown},
+    {0xa7d48dc, "fim", BC_TYPE::kUnknown},
+    {0xb359fe9, "msi", BC_TYPE::kUnknown},
+    {0x121f738c, "code2Of5Matrix", BC_TYPE::kUnknown},
+    {0x15358616, "ucc128", BC_TYPE::kUnknown},
+    {0x1f4bfa05, "rfid", BC_TYPE::kUnknown},
+    {0x1fda71bc, "rss14Stacked", BC_TYPE::kUnknown},
+    {0x22065087, "ean8add2", BC_TYPE::kUnknown},
+    {0x2206508a, "ean8add5", BC_TYPE::kUnknown},
+    {0x2278366c, "codabar", BC_TYPE::kCodabar},
+    {0x2a039a8d, "telepen", BC_TYPE::kUnknown},
+    {0x323ed337, "upcApwcd", BC_TYPE::kUnknown},
+    {0x347a1846, "postUSIMB", BC_TYPE::kUnknown},
+    {0x391bb836, "code128", BC_TYPE::kCode128},
+    {0x398eddaf, "dataMatrix", BC_TYPE::kDataMatrix},
+    {0x3cff60a8, "upcEadd2", BC_TYPE::kUnknown},
+    {0x3cff60ab, "upcEadd5", BC_TYPE::kUnknown},
+    {0x402cb188, "code2Of5Standard", BC_TYPE::kUnknown},
+    {0x411764f7, "aztec", BC_TYPE::kUnknown},
+    {0x44d4e84c, "ean8", BC_TYPE::kEAN8},
+    {0x48468902, "ucc128sscc", BC_TYPE::kUnknown},
+    {0x4880aea4, "upcAadd2", BC_TYPE::kUnknown},
+    {0x4880aea7, "upcAadd5", BC_TYPE::kUnknown},
+    {0x54f18256, "code2Of5Industrial", BC_TYPE::kUnknown},
+    {0x58e15f25, "rss14Limited", BC_TYPE::kUnknown},
+    {0x5c08d1b9, "postAUSReplyPaid", BC_TYPE::kUnknown},
+    {0x5fa700bd, "rss14", BC_TYPE::kUnknown},
+    {0x631a7e35, "logmars", BC_TYPE::kUnknown},
+    {0x6a236236, "pdf417", BC_TYPE::kPDF417},
+    {0x6d098ece, "upcean2", BC_TYPE::kUnknown},
+    {0x6d098ed1, "upcean5", BC_TYPE::kUnknown},
+    {0x76b04eed, "code3Of9extended", BC_TYPE::kUnknown},
+    {0x7c7db84a, "maxicode", BC_TYPE::kUnknown},
+    {0x8266f7f7, "ucc128random", BC_TYPE::kUnknown},
+    {0x83eca147, "postUSDPBC", BC_TYPE::kUnknown},
+    {0x8dd71de0, "postAUSStandard", BC_TYPE::kUnknown},
+    {0x98adad85, "plessey", BC_TYPE::kUnknown},
+    {0x9f84cce6, "ean13pwcd", BC_TYPE::kUnknown},
+    {0xb514fbe9, "upcA", BC_TYPE::kUPCA},
+    {0xb514fbed, "upcE", BC_TYPE::kUnknown},
+    {0xb5c6a853, "ean13add2", BC_TYPE::kUnknown},
+    {0xb5c6a856, "ean13add5", BC_TYPE::kUnknown},
+    {0xb81fc512, "postUKRM4SCC", BC_TYPE::kUnknown},
+    {0xbad34b22, "code128SSCC", BC_TYPE::kUnknown},
+    {0xbfbe0cf6, "postUS5Zip", BC_TYPE::kUnknown},
+    {0xc56618e8, "pdf417macro", BC_TYPE::kUnknown},
+    {0xca730f8a, "code2Of5Interleaved", BC_TYPE::kUnknown},
+    {0xd0097ac6, "rss14Expanded", BC_TYPE::kUnknown},
+    {0xd25a0240, "postAUSCust2", BC_TYPE::kUnknown},
+    {0xd25a0241, "postAUSCust3", BC_TYPE::kUnknown},
+    {0xd53ed3e7, "rss14Truncated", BC_TYPE::kUnknown},
+    {0xe72bcd57, "code128A", BC_TYPE::kUnknown},
+    {0xe72bcd58, "code128B", BC_TYPE::kCode128B},
+    {0xe72bcd59, "code128C", BC_TYPE::kCode128C},
+    {0xee83c50f, "rss14StackedOmni", BC_TYPE::kUnknown},
+    {0xf2a18f7e, "QRCode", BC_TYPE::kQRCode},
+    {0xfaeaf37f, "postUSStandard", BC_TYPE::kUnknown},
+    {0xfb48155c, "code3Of9", BC_TYPE::kCode39},
+};
+
+absl::optional<BC_CHAR_ENCODING> CharEncodingFromString(
+    const WideString& value) {
   if (value.CompareNoCase(L"UTF-16"))
-    return CHAR_ENCODING_UNICODE;
+    return BC_CHAR_ENCODING::kUnicode;
   if (value.CompareNoCase(L"UTF-8"))
-    return CHAR_ENCODING_UTF8;
-  return {};
+    return BC_CHAR_ENCODING::kUTF8;
+  return absl::nullopt;
 }
 
-Optional<BC_TEXT_LOC> TextLocFromAttribute(XFA_AttributeValue value) {
+absl::optional<BC_TEXT_LOC> TextLocFromAttribute(XFA_AttributeValue value) {
   switch (value) {
     case XFA_AttributeValue::None:
-      return BC_TEXT_LOC_NONE;
+      return BC_TEXT_LOC::kNone;
     case XFA_AttributeValue::Above:
-      return BC_TEXT_LOC_ABOVE;
+      return BC_TEXT_LOC::kAbove;
     case XFA_AttributeValue::Below:
-      return BC_TEXT_LOC_BELOW;
+      return BC_TEXT_LOC::kBelow;
     case XFA_AttributeValue::AboveEmbedded:
-      return BC_TEXT_LOC_ABOVEEMBED;
+      return BC_TEXT_LOC::kAboveEmbed;
     case XFA_AttributeValue::BelowEmbedded:
-      return BC_TEXT_LOC_BELOWEMBED;
+      return BC_TEXT_LOC::kBelowEmbed;
     default:
-      return {};
+      return absl::nullopt;
   }
 }
 
 }  // namespace.
 
 // static
-const BarCodeInfo* CXFA_FFBarcode::GetBarcodeTypeByName(
-    const WideString& wsName) {
+BC_TYPE CXFA_FFBarcode::GetBarcodeTypeByName(const WideString& wsName) {
   if (wsName.IsEmpty())
-    return nullptr;
+    return BC_TYPE::kUnknown;
 
   auto* it = std::lower_bound(
-      std::begin(g_BarCodeData), std::end(g_BarCodeData),
-      FX_HashCode_GetW(wsName.AsStringView(), true),
+      std::begin(kBarCodeData), std::end(kBarCodeData),
+      FX_HashCode_GetLoweredW(wsName.AsStringView()),
       [](const BarCodeInfo& arg, uint32_t hash) { return arg.uHash < hash; });
 
-  if (it != std::end(g_BarCodeData) && wsName.EqualsASCII(it->pName))
-    return it;
+  if (it != std::end(kBarCodeData) && wsName.EqualsASCII(it->pName))
+    return it->eBCType;
 
-  return nullptr;
+  return BC_TYPE::kUnknown;
 }
 
 CXFA_FFBarcode::CXFA_FFBarcode(CXFA_Node* pNode, CXFA_Barcode* barcode)
@@ -138,28 +142,34 @@
 
 CXFA_FFBarcode::~CXFA_FFBarcode() = default;
 
+void CXFA_FFBarcode::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FFTextEdit::Trace(visitor);
+  visitor->Trace(barcode_);
+}
+
 bool CXFA_FFBarcode::LoadWidget() {
-  ASSERT(!IsLoaded());
-  auto pNew = pdfium::MakeUnique<CFWL_Barcode>(GetFWLApp());
-  CFWL_Barcode* pFWLBarcode = pNew.get();
-  SetNormalWidget(std::move(pNew));
+  DCHECK(!IsLoaded());
+
+  CFWL_Barcode* pFWLBarcode = cppgc::MakeGarbageCollected<CFWL_Barcode>(
+      GetFWLApp()->GetHeap()->GetAllocationHandle(), GetFWLApp());
+  SetNormalWidget(pFWLBarcode);
   pFWLBarcode->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver = pFWLBarcode->GetOwnerApp()->GetNoteDriver();
+  CFWL_NoteDriver* pNoteDriver = pFWLBarcode->GetFWLApp()->GetNoteDriver();
   pNoteDriver->RegisterEventTarget(pFWLBarcode, pFWLBarcode);
   m_pOldDelegate = pFWLBarcode->GetDelegate();
   pFWLBarcode->SetDelegate(this);
 
   {
     CFWL_Widget::ScopedUpdateLock update_lock(pFWLBarcode);
-    pFWLBarcode->SetText(m_pNode->GetValue(XFA_VALUEPICTURE_Display));
+    pFWLBarcode->SetText(m_pNode->GetValue(XFA_ValuePicture::kDisplay));
     UpdateWidgetProperty();
   }
 
   return CXFA_FFField::LoadWidget();
 }
 
-void CXFA_FFBarcode::RenderWidget(CXFA_Graphics* pGS,
+void CXFA_FFBarcode::RenderWidget(CFGAS_GEGraphics* pGS,
                                   const CFX_Matrix& matrix,
                                   HighlightOption highlight) {
   if (!HasVisibleStatus())
@@ -169,8 +179,8 @@
   mtRotate.Concat(matrix);
 
   CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight);
-  DrawBorder(pGS, m_pNode->GetUIBorder(), m_rtUI, mtRotate);
-  RenderCaption(pGS, &mtRotate);
+  DrawBorder(pGS, m_pNode->GetUIBorder(), m_UIRect, mtRotate);
+  RenderCaption(pGS, mtRotate);
   CFX_RectF rtWidget = GetNormalWidget()->GetWidgetRect();
 
   CFX_Matrix mt(1, 0, 0, 1, rtWidget.left, rtWidget.top);
@@ -181,81 +191,83 @@
 void CXFA_FFBarcode::UpdateWidgetProperty() {
   CXFA_FFTextEdit::UpdateWidgetProperty();
 
-  const BarCodeInfo* info = GetBarcodeTypeByName(barcode_->GetBarcodeType());
-  if (!info)
+  BC_TYPE bc_type = GetBarcodeTypeByName(barcode_->GetBarcodeType());
+  if (bc_type == BC_TYPE::kUnknown)
     return;
 
   auto* pBarCodeWidget = static_cast<CFWL_Barcode*>(GetNormalWidget());
-  pBarCodeWidget->SetType(info->eBCType);
+  pBarCodeWidget->SetType(bc_type);
 
-  Optional<WideString> encoding_string = barcode_->GetCharEncoding();
-  if (encoding_string) {
-    Optional<BC_CHAR_ENCODING> encoding =
-        CharEncodingFromString(*encoding_string);
-    if (encoding)
-      pBarCodeWidget->SetCharEncoding(*encoding);
+  absl::optional<WideString> encoding_string = barcode_->GetCharEncoding();
+  if (encoding_string.has_value()) {
+    absl::optional<BC_CHAR_ENCODING> encoding =
+        CharEncodingFromString(encoding_string.value());
+    if (encoding.has_value())
+      pBarCodeWidget->SetCharEncoding(encoding.value());
   }
 
-  Optional<bool> calcChecksum = barcode_->GetChecksum();
-  if (calcChecksum)
-    pBarCodeWidget->SetCalChecksum(*calcChecksum);
+  absl::optional<bool> calcChecksum = barcode_->GetChecksum();
+  if (calcChecksum.has_value())
+    pBarCodeWidget->SetCalChecksum(calcChecksum.value());
 
-  Optional<int32_t> dataLen = barcode_->GetDataLength();
-  if (dataLen)
-    pBarCodeWidget->SetDataLength(*dataLen);
+  absl::optional<int32_t> dataLen = barcode_->GetDataLength();
+  if (dataLen.has_value())
+    pBarCodeWidget->SetDataLength(dataLen.value());
 
-  Optional<char> startChar = barcode_->GetStartChar();
-  if (startChar)
-    pBarCodeWidget->SetStartChar(*startChar);
+  absl::optional<char> startChar = barcode_->GetStartChar();
+  if (startChar.has_value())
+    pBarCodeWidget->SetStartChar(startChar.value());
 
-  Optional<char> endChar = barcode_->GetEndChar();
-  if (endChar)
-    pBarCodeWidget->SetEndChar(*endChar);
+  absl::optional<char> endChar = barcode_->GetEndChar();
+  if (endChar.has_value())
+    pBarCodeWidget->SetEndChar(endChar.value());
 
-  Optional<int32_t> ecLevel = barcode_->GetECLevel();
-  if (ecLevel)
-    pBarCodeWidget->SetErrorCorrectionLevel(*ecLevel);
+  absl::optional<int32_t> ecLevel = barcode_->GetECLevel();
+  if (ecLevel.has_value())
+    pBarCodeWidget->SetErrorCorrectionLevel(ecLevel.value());
 
-  Optional<int32_t> width = barcode_->GetModuleWidth();
-  if (width)
-    pBarCodeWidget->SetModuleWidth(*width);
+  absl::optional<int32_t> width = barcode_->GetModuleWidth();
+  if (width.has_value())
+    pBarCodeWidget->SetModuleWidth(width.value());
 
-  Optional<int32_t> height = barcode_->GetModuleHeight();
-  if (height)
-    pBarCodeWidget->SetModuleHeight(*height);
+  absl::optional<int32_t> height = barcode_->GetModuleHeight();
+  if (height.has_value())
+    pBarCodeWidget->SetModuleHeight(height.value());
 
-  Optional<bool> printCheck = barcode_->GetPrintChecksum();
-  if (printCheck)
-    pBarCodeWidget->SetPrintChecksum(*printCheck);
+  absl::optional<bool> printCheck = barcode_->GetPrintChecksum();
+  if (printCheck.has_value())
+    pBarCodeWidget->SetPrintChecksum(printCheck.value());
 
-  Optional<XFA_AttributeValue> text_attr = barcode_->GetTextLocation();
-  if (text_attr) {
-    Optional<BC_TEXT_LOC> textLoc = TextLocFromAttribute(*text_attr);
-    if (textLoc)
-      pBarCodeWidget->SetTextLocation(*textLoc);
+  absl::optional<XFA_AttributeValue> text_attr = barcode_->GetTextLocation();
+  if (text_attr.has_value()) {
+    absl::optional<BC_TEXT_LOC> textLoc =
+        TextLocFromAttribute(text_attr.value());
+    if (textLoc.has_value())
+      pBarCodeWidget->SetTextLocation(textLoc.value());
   }
 
   // Truncated is currently not a supported flag.
 
-  Optional<int8_t> ratio = barcode_->GetWideNarrowRatio();
-  if (ratio)
-    pBarCodeWidget->SetWideNarrowRatio(*ratio);
+  absl::optional<int8_t> ratio = barcode_->GetWideNarrowRatio();
+  if (ratio.has_value())
+    pBarCodeWidget->SetWideNarrowRatio(ratio.value());
 
-  if (info->eName == BarcodeType::code3Of9 ||
-      info->eName == BarcodeType::ean8 || info->eName == BarcodeType::ean13 ||
-      info->eName == BarcodeType::upcA) {
+  if (bc_type == BC_TYPE::kCode39 || bc_type == BC_TYPE::kEAN8 ||
+      bc_type == BC_TYPE::kEAN13 || bc_type == BC_TYPE::kUPCA) {
     pBarCodeWidget->SetPrintChecksum(true);
   }
 }
 
-bool CXFA_FFBarcode::AcceptsFocusOnButtonDown(uint32_t dwFlags,
-                                              const CFX_PointF& point,
-                                              FWL_MouseCommand command) {
+bool CXFA_FFBarcode::AcceptsFocusOnButtonDown(
+    Mask<XFA_FWL_KeyFlag> dwFlags,
+    const CFX_PointF& point,
+    CFWL_MessageMouse::MouseCommand command) {
   auto* pBarCodeWidget = static_cast<CFWL_Barcode*>(GetNormalWidget());
   if (!pBarCodeWidget || pBarCodeWidget->IsProtectedType())
     return false;
-  if (command == FWL_MouseCommand::LeftButtonDown && !m_pNode->IsOpenAccess())
+  if (command == CFWL_MessageMouse::MouseCommand::kLeftButtonDown &&
+      !m_pNode->IsOpenAccess()) {
     return false;
-
+  }
   return CXFA_FFTextEdit::AcceptsFocusOnButtonDown(dwFlags, point, command);
 }
diff --git a/xfa/fxfa/cxfa_ffbarcode.h b/xfa/fxfa/cxfa_ffbarcode.h
index 83e0884..0db3a9d 100644
--- a/xfa/fxfa/cxfa_ffbarcode.h
+++ b/xfa/fxfa/cxfa_ffbarcode.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,104 +7,38 @@
 #ifndef XFA_FXFA_CXFA_FFBARCODE_H_
 #define XFA_FXFA_CXFA_FFBARCODE_H_
 
-#include "core/fxcrt/unowned_ptr.h"
 #include "fxbarcode/BC_Library.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fxfa/cxfa_ffpageview.h"
 #include "xfa/fxfa/cxfa_fftextedit.h"
 
-enum class BarcodeType {
-  aztec,
-  codabar,
-  code11,
-  code128,
-  code128A,
-  code128B,
-  code128C,
-  code128SSCC,
-  code2Of5Industrial,
-  code2Of5Interleaved,
-  code2Of5Matrix,
-  code2Of5Standard,
-  code3Of9,
-  code3Of9extended,
-  code49,
-  code93,
-  dataMatrix,
-  ean13,
-  ean13add2,
-  ean13add5,
-  ean13pwcd,
-  ean8,
-  ean8add2,
-  ean8add5,
-  fim,
-  logmars,
-  maxicode,
-  msi,
-  pdf417,
-  pdf417macro,
-  plessey,
-  postAUSCust2,
-  postAUSCust3,
-  postAUSReplyPaid,
-  postAUSStandard,
-  postUKRM4SCC,
-  postUS5Zip,
-  postUSDPBC,
-  postUSIMB,
-  postUSStandard,
-  QRCode,
-  rfid,
-  rss14,
-  rss14Expanded,
-  rss14Limited,
-  rss14Stacked,
-  rss14StackedOmni,
-  rss14Truncated,
-  telepen,
-  ucc128,
-  ucc128random,
-  ucc128sscc,
-  upcA,
-  upcAadd2,
-  upcAadd5,
-  upcApwcd,
-  upcE,
-  upcEadd2,
-  upcEadd5,
-  upcean2,
-  upcean5,
-  upsMaxicode
-};
-
-struct BarCodeInfo {
-  uint32_t uHash;     // |pName| hashed as if wide string.
-  const char* pName;  // Raw, POD struct.
-  BarcodeType eName;
-  BC_TYPE eBCType;
-};
-
 class CXFA_Barcode;
 
 class CXFA_FFBarcode final : public CXFA_FFTextEdit {
  public:
-  static const BarCodeInfo* GetBarcodeTypeByName(const WideString& wsName);
+  static BC_TYPE GetBarcodeTypeByName(const WideString& wsName);
 
-  CXFA_FFBarcode(CXFA_Node* pNode, CXFA_Barcode* barcode);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFBarcode() override;
 
+  void Trace(cppgc::Visitor* visitor) const override;
+
   // CXFA_FFTextEdit
   bool LoadWidget() override;
-  void RenderWidget(CXFA_Graphics* pGS,
+  void RenderWidget(CFGAS_GEGraphics* pGS,
                     const CFX_Matrix& matrix,
                     HighlightOption highlight) override;
   void UpdateWidgetProperty() override;
-  bool AcceptsFocusOnButtonDown(uint32_t dwFlags,
-                                const CFX_PointF& point,
-                                FWL_MouseCommand command) override;
+  bool AcceptsFocusOnButtonDown(
+      Mask<XFA_FWL_KeyFlag> dwFlags,
+      const CFX_PointF& point,
+      CFWL_MessageMouse::MouseCommand command) override;
 
  private:
-  UnownedPtr<CXFA_Barcode> const barcode_;
+  CXFA_FFBarcode(CXFA_Node* pNode, CXFA_Barcode* barcode);
+
+  cppgc::Member<CXFA_Barcode> const barcode_;
 };
 
 #endif  // XFA_FXFA_CXFA_FFBARCODE_H_
diff --git a/xfa/fxfa/cxfa_ffbarcode_unittest.cpp b/xfa/fxfa/cxfa_ffbarcode_unittest.cpp
index 6236b10..ac6df65 100644
--- a/xfa/fxfa/cxfa_ffbarcode_unittest.cpp
+++ b/xfa/fxfa/cxfa_ffbarcode_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,19 +6,13 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 
-TEST(XFA_FFBarcode, GetBarcodeTypeByName) {
-  EXPECT_EQ(nullptr, CXFA_FFBarcode::GetBarcodeTypeByName(L""));
-  EXPECT_EQ(nullptr, CXFA_FFBarcode::GetBarcodeTypeByName(L"not_found"));
+TEST(CXFA_FFBarcode, GetBarcodeTypeByName) {
+  EXPECT_EQ(BC_TYPE::kUnknown, CXFA_FFBarcode::GetBarcodeTypeByName(L""));
+  EXPECT_EQ(BC_TYPE::kUnknown,
+            CXFA_FFBarcode::GetBarcodeTypeByName(L"not_found"));
 
-  auto* data = CXFA_FFBarcode::GetBarcodeTypeByName(L"ean13");
-  ASSERT_NE(nullptr, data);
-  EXPECT_EQ(BarcodeType::ean13, data->eName);
-
-  data = CXFA_FFBarcode::GetBarcodeTypeByName(L"pdf417");
-  ASSERT_NE(nullptr, data);
-  EXPECT_EQ(BarcodeType::pdf417, data->eName);
-
-  data = CXFA_FFBarcode::GetBarcodeTypeByName(L"code3Of9");
-  ASSERT_NE(nullptr, data);
-  EXPECT_EQ(BarcodeType::code3Of9, data->eName);
+  EXPECT_EQ(BC_TYPE::kEAN13, CXFA_FFBarcode::GetBarcodeTypeByName(L"ean13"));
+  EXPECT_EQ(BC_TYPE::kPDF417, CXFA_FFBarcode::GetBarcodeTypeByName(L"pdf417"));
+  EXPECT_EQ(BC_TYPE::kCode39,
+            CXFA_FFBarcode::GetBarcodeTypeByName(L"code3Of9"));
 }
diff --git a/xfa/fxfa/cxfa_ffcheckbutton.cpp b/xfa/fxfa/cxfa_ffcheckbutton.cpp
index 1d39e2f..c3cbf51 100644
--- a/xfa/fxfa/cxfa_ffcheckbutton.cpp
+++ b/xfa/fxfa/cxfa_ffcheckbutton.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,8 @@
 
 #include "xfa/fxfa/cxfa_ffcheckbutton.h"
 
-#include <utility>
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fwl/cfwl_checkbox.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
 #include "xfa/fwl/cfwl_notedriver.h"
@@ -30,19 +30,26 @@
 
 CXFA_FFCheckButton::~CXFA_FFCheckButton() = default;
 
+void CXFA_FFCheckButton::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FFField::Trace(visitor);
+  visitor->Trace(m_pOldDelegate);
+  visitor->Trace(button_);
+}
+
 bool CXFA_FFCheckButton::LoadWidget() {
-  ASSERT(!IsLoaded());
-  auto pNew = pdfium::MakeUnique<CFWL_CheckBox>(GetFWLApp());
-  CFWL_CheckBox* pCheckBox = pNew.get();
-  SetNormalWidget(std::move(pNew));
+  DCHECK(!IsLoaded());
+
+  CFWL_CheckBox* pCheckBox = cppgc::MakeGarbageCollected<CFWL_CheckBox>(
+      GetFWLApp()->GetHeap()->GetAllocationHandle(), GetFWLApp());
+  SetNormalWidget(pCheckBox);
   pCheckBox->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver = pCheckBox->GetOwnerApp()->GetNoteDriver();
+  CFWL_NoteDriver* pNoteDriver = pCheckBox->GetFWLApp()->GetNoteDriver();
   pNoteDriver->RegisterEventTarget(pCheckBox, pCheckBox);
   m_pOldDelegate = pCheckBox->GetDelegate();
   pCheckBox->SetDelegate(this);
   if (m_pNode->IsRadioButton())
-    pCheckBox->ModifyStylesEx(FWL_STYLEEXT_CKB_RadioButton, 0xFFFFFFFF);
+    pCheckBox->ModifyStyleExts(FWL_STYLEEXT_CKB_RadioButton, 0xFFFFFFFF);
 
   {
     CFWL_Widget::ScopedUpdateLock update_lock(pCheckBox);
@@ -86,7 +93,7 @@
   if (button_->IsAllowNeutral())
     dwStyleEx |= FWL_STYLEEXT_CKB_3State;
 
-  pCheckBox->ModifyStylesEx(
+  pCheckBox->ModifyStyleExts(
       dwStyleEx, FWL_STYLEEXT_CKB_SignShapeMask | FWL_STYLEEXT_CKB_3State);
 }
 
@@ -102,7 +109,7 @@
   float fCapReserve = 0;
   CXFA_Caption* caption = m_pNode->GetCaptionIfExists();
   if (caption && caption->IsVisible()) {
-    m_rtCaption = rtWidget;
+    m_CaptionRect = rtWidget;
     iCapPlacement = caption->GetPlacementType();
     fCapReserve = caption->GetReserve();
     if (fCapReserve <= 0) {
@@ -123,35 +130,35 @@
     iVertAlign = para->GetVerticalAlign();
   }
 
-  m_rtUI = rtWidget;
+  m_UIRect = rtWidget;
   CXFA_Margin* captionMargin = caption ? caption->GetMarginIfExists() : nullptr;
   switch (iCapPlacement) {
     case XFA_AttributeValue::Left: {
-      m_rtCaption.width = fCapReserve;
+      m_CaptionRect.width = fCapReserve;
       CapLeftRightPlacement(captionMargin);
-      m_rtUI.width -= fCapReserve;
-      m_rtUI.left += fCapReserve;
+      m_UIRect.width -= fCapReserve;
+      m_UIRect.left += fCapReserve;
       break;
     }
     case XFA_AttributeValue::Top: {
-      m_rtCaption.height = fCapReserve;
-      XFA_RectWithoutMargin(&m_rtCaption, captionMargin);
-      m_rtUI.height -= fCapReserve;
-      m_rtUI.top += fCapReserve;
+      m_CaptionRect.height = fCapReserve;
+      XFA_RectWithoutMargin(&m_CaptionRect, captionMargin);
+      m_UIRect.height -= fCapReserve;
+      m_UIRect.top += fCapReserve;
       break;
     }
     case XFA_AttributeValue::Right: {
-      m_rtCaption.left = m_rtCaption.right() - fCapReserve;
-      m_rtCaption.width = fCapReserve;
+      m_CaptionRect.left = m_CaptionRect.right() - fCapReserve;
+      m_CaptionRect.width = fCapReserve;
       CapLeftRightPlacement(captionMargin);
-      m_rtUI.width -= fCapReserve;
+      m_UIRect.width -= fCapReserve;
       break;
     }
     case XFA_AttributeValue::Bottom: {
-      m_rtCaption.top = m_rtCaption.bottom() - fCapReserve;
-      m_rtCaption.height = fCapReserve;
-      XFA_RectWithoutMargin(&m_rtCaption, captionMargin);
-      m_rtUI.height -= fCapReserve;
+      m_CaptionRect.top = m_CaptionRect.bottom() - fCapReserve;
+      m_CaptionRect.height = fCapReserve;
+      XFA_RectWithoutMargin(&m_CaptionRect, captionMargin);
+      m_UIRect.height -= fCapReserve;
       break;
     }
     case XFA_AttributeValue::Inline:
@@ -162,26 +169,26 @@
   }
 
   if (iHorzAlign == XFA_AttributeValue::Center)
-    m_rtUI.left += (m_rtUI.width - fCheckSize) / 2;
+    m_UIRect.left += (m_UIRect.width - fCheckSize) / 2;
   else if (iHorzAlign == XFA_AttributeValue::Right)
-    m_rtUI.left = m_rtUI.right() - fCheckSize;
+    m_UIRect.left = m_UIRect.right() - fCheckSize;
 
   if (iVertAlign == XFA_AttributeValue::Middle)
-    m_rtUI.top += (m_rtUI.height - fCheckSize) / 2;
+    m_UIRect.top += (m_UIRect.height - fCheckSize) / 2;
   else if (iVertAlign == XFA_AttributeValue::Bottom)
-    m_rtUI.top = m_rtUI.bottom() - fCheckSize;
+    m_UIRect.top = m_UIRect.bottom() - fCheckSize;
 
-  m_rtUI.width = fCheckSize;
-  m_rtUI.height = fCheckSize;
+  m_UIRect.width = fCheckSize;
+  m_UIRect.height = fCheckSize;
   AddUIMargin(iCapPlacement);
-  m_rtCheckBox = m_rtUI;
+  m_CheckBoxRect = m_UIRect;
   CXFA_Border* borderUI = m_pNode->GetUIBorder();
   if (borderUI) {
     CXFA_Margin* borderMargin = borderUI->GetMarginIfExists();
-    XFA_RectWithoutMargin(&m_rtUI, borderMargin);
+    XFA_RectWithoutMargin(&m_UIRect, borderMargin);
   }
 
-  m_rtUI.Normalize();
+  m_UIRect.Normalize();
   LayoutCaption();
   SetFWLRect();
   if (GetNormalWidget())
@@ -192,40 +199,40 @@
 
 void CXFA_FFCheckButton::CapLeftRightPlacement(
     const CXFA_Margin* captionMargin) {
-  XFA_RectWithoutMargin(&m_rtCaption, captionMargin);
-  if (m_rtCaption.height < 0)
-    m_rtCaption.top += m_rtCaption.height;
-  if (m_rtCaption.width < 0) {
-    m_rtCaption.left += m_rtCaption.width;
-    m_rtCaption.width = -m_rtCaption.width;
+  XFA_RectWithoutMargin(&m_CaptionRect, captionMargin);
+  if (m_CaptionRect.height < 0)
+    m_CaptionRect.top += m_CaptionRect.height;
+  if (m_CaptionRect.width < 0) {
+    m_CaptionRect.left += m_CaptionRect.width;
+    m_CaptionRect.width = -m_CaptionRect.width;
   }
 }
 
 void CXFA_FFCheckButton::AddUIMargin(XFA_AttributeValue iCapPlacement) {
   CFX_RectF rtUIMargin = m_pNode->GetUIMargin();
-  m_rtUI.top -= rtUIMargin.top / 2 - rtUIMargin.height / 2;
+  m_UIRect.top -= rtUIMargin.top / 2 - rtUIMargin.height / 2;
 
   float fLeftAddRight = rtUIMargin.left + rtUIMargin.width;
   float fTopAddBottom = rtUIMargin.top + rtUIMargin.height;
-  if (m_rtUI.width < fLeftAddRight) {
+  if (m_UIRect.width < fLeftAddRight) {
     if (iCapPlacement == XFA_AttributeValue::Right ||
         iCapPlacement == XFA_AttributeValue::Left) {
-      m_rtUI.left -= fLeftAddRight - m_rtUI.width;
+      m_UIRect.left -= fLeftAddRight - m_UIRect.width;
     } else {
-      m_rtUI.left -= 2 * (fLeftAddRight - m_rtUI.width);
+      m_UIRect.left -= 2 * (fLeftAddRight - m_UIRect.width);
     }
-    m_rtUI.width += 2 * (fLeftAddRight - m_rtUI.width);
+    m_UIRect.width += 2 * (fLeftAddRight - m_UIRect.width);
   }
-  if (m_rtUI.height < fTopAddBottom) {
+  if (m_UIRect.height < fTopAddBottom) {
     if (iCapPlacement == XFA_AttributeValue::Right)
-      m_rtUI.left -= fTopAddBottom - m_rtUI.height;
+      m_UIRect.left -= fTopAddBottom - m_UIRect.height;
 
-    m_rtUI.top -= fTopAddBottom - m_rtUI.height;
-    m_rtUI.height += 2 * (fTopAddBottom - m_rtUI.height);
+    m_UIRect.top -= fTopAddBottom - m_UIRect.height;
+    m_UIRect.height += 2 * (fTopAddBottom - m_UIRect.height);
   }
 }
 
-void CXFA_FFCheckButton::RenderWidget(CXFA_Graphics* pGS,
+void CXFA_FFCheckButton::RenderWidget(CFGAS_GEGraphics* pGS,
                                       const CFX_Matrix& matrix,
                                       HighlightOption highlight) {
   if (!HasVisibleStatus())
@@ -235,54 +242,52 @@
   mtRotate.Concat(matrix);
 
   CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight);
-  DrawBorderWithFlag(pGS, m_pNode->GetUIBorder(), m_rtUI, mtRotate,
+  DrawBorderWithFlag(pGS, m_pNode->GetUIBorder(), m_UIRect, mtRotate,
                      button_->IsRound());
-  RenderCaption(pGS, &mtRotate);
-  DrawHighlight(pGS, &mtRotate, highlight,
+  RenderCaption(pGS, mtRotate);
+  DrawHighlight(pGS, mtRotate, highlight,
                 button_->IsRound() ? kRoundShape : kSquareShape);
-  CFX_Matrix mt(1, 0, 0, 1, m_rtCheckBox.left, m_rtCheckBox.top);
+  CFX_Matrix mt(1, 0, 0, 1, m_CheckBoxRect.left, m_CheckBoxRect.top);
   mt.Concat(mtRotate);
   GetApp()->GetFWLWidgetMgr()->OnDrawWidget(GetNormalWidget(), pGS, mt);
 }
 
-bool CXFA_FFCheckButton::OnLButtonUp(uint32_t dwFlags,
+bool CXFA_FFCheckButton::OnLButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
                                      const CFX_PointF& point) {
   if (!GetNormalWidget() || !IsButtonDown())
     return false;
 
-  ObservedPtr<CXFA_FFCheckButton> pWatched(this);
   SetButtonDown(false);
-  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
-      GetNormalWidget(), FWL_MouseCommand::LeftButtonUp, dwFlags,
-      FWLToClient(point)));
-
-  return !!pWatched;
+  CFWL_MessageMouse msg(GetNormalWidget(),
+                        CFWL_MessageMouse::MouseCommand::kLeftButtonUp, dwFlags,
+                        FWLToClient(point));
+  SendMessageToFWLWidget(&msg);
+  return true;
 }
 
-XFA_CHECKSTATE CXFA_FFCheckButton::FWLState2XFAState() {
+XFA_CheckState CXFA_FFCheckButton::FWLState2XFAState() {
   uint32_t dwState = GetNormalWidget()->GetStates();
   if (dwState & FWL_STATE_CKB_Checked)
-    return XFA_CHECKSTATE_On;
+    return XFA_CheckState::kOn;
   if (dwState & FWL_STATE_CKB_Neutral)
-    return XFA_CHECKSTATE_Neutral;
-  return XFA_CHECKSTATE_Off;
+    return XFA_CheckState::kNeutral;
+  return XFA_CheckState::kOff;
 }
 
 bool CXFA_FFCheckButton::CommitData() {
-  XFA_CHECKSTATE eCheckState = FWLState2XFAState();
-  m_pNode->SetCheckState(eCheckState, true);
+  m_pNode->SetCheckState(FWLState2XFAState());
   return true;
 }
 
 bool CXFA_FFCheckButton::IsDataChanged() {
-  XFA_CHECKSTATE eCheckState = FWLState2XFAState();
+  XFA_CheckState eCheckState = FWLState2XFAState();
   return m_pNode->GetCheckState() != eCheckState;
 }
 
-void CXFA_FFCheckButton::SetFWLCheckState(XFA_CHECKSTATE eCheckState) {
-  if (eCheckState == XFA_CHECKSTATE_Neutral)
+void CXFA_FFCheckButton::SetFWLCheckState(XFA_CheckState eCheckState) {
+  if (eCheckState == XFA_CheckState::kNeutral)
     GetNormalWidget()->SetStates(FWL_STATE_CKB_Neutral);
-  else if (eCheckState == XFA_CHECKSTATE_On)
+  else if (eCheckState == XFA_CheckState::kOn)
     GetNormalWidget()->SetStates(FWL_STATE_CKB_Checked);
   else
     GetNormalWidget()->RemoveStates(FWL_STATE_CKB_Checked);
@@ -292,8 +297,7 @@
   if (!GetNormalWidget())
     return false;
 
-  XFA_CHECKSTATE eState = m_pNode->GetCheckState();
-  SetFWLCheckState(eState);
+  SetFWLCheckState(m_pNode->GetCheckState());
   GetNormalWidget()->Update();
   return true;
 }
@@ -308,29 +312,25 @@
     case CFWL_Event::Type::CheckStateChanged: {
       CXFA_EventParam eParam;
       eParam.m_eType = XFA_EVENT_Change;
-      eParam.m_wsPrevText = m_pNode->GetValue(XFA_VALUEPICTURE_Raw);
+      eParam.m_wsPrevText = m_pNode->GetValue(XFA_ValuePicture::kRaw);
 
       CXFA_Node* exclNode = m_pNode->GetExclGroupIfExists();
       if (ProcessCommittedData()) {
-        eParam.m_pTarget = exclNode;
         if (exclNode) {
           m_pDocView->AddValidateNode(exclNode);
           m_pDocView->AddCalculateNode(exclNode);
           exclNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change,
                                  &eParam);
         }
-        eParam.m_pTarget = m_pNode.Get();
         m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change,
                               &eParam);
       } else {
         SetFWLCheckState(m_pNode->GetCheckState());
       }
       if (exclNode) {
-        eParam.m_pTarget = exclNode;
         exclNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Click,
                                &eParam);
       }
-      eParam.m_pTarget = m_pNode.Get();
       m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Click, &eParam);
       break;
     }
@@ -340,7 +340,7 @@
   m_pOldDelegate->OnProcessEvent(pEvent);
 }
 
-void CXFA_FFCheckButton::OnDrawWidget(CXFA_Graphics* pGraphics,
+void CXFA_FFCheckButton::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                                       const CFX_Matrix& matrix) {
   m_pOldDelegate->OnDrawWidget(pGraphics, matrix);
 }
diff --git a/xfa/fxfa/cxfa_ffcheckbutton.h b/xfa/fxfa/cxfa_ffcheckbutton.h
index 3e5a723..1e05878 100644
--- a/xfa/fxfa/cxfa_ffcheckbutton.h
+++ b/xfa/fxfa/cxfa_ffcheckbutton.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #ifndef XFA_FXFA_CXFA_FFCHECKBUTTON_H_
 #define XFA_FXFA_CXFA_FFCHECKBUTTON_H_
 
-#include "core/fxcrt/unowned_ptr.h"
+#include "v8/include/cppgc/member.h"
 #include "xfa/fxfa/cxfa_fffield.h"
 #include "xfa/fxfa/cxfa_ffpageview.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
@@ -16,11 +16,13 @@
 
 class CXFA_FFCheckButton final : public CXFA_FFField {
  public:
-  CXFA_FFCheckButton(CXFA_Node* pNode, CXFA_CheckButton* button);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFCheckButton() override;
 
+  void Trace(cppgc::Visitor* visitor) const override;
+
   // CXFA_FFField
-  void RenderWidget(CXFA_Graphics* pGS,
+  void RenderWidget(CFGAS_GEGraphics* pGS,
                     const CFX_Matrix& matrix,
                     HighlightOption highlight) override;
 
@@ -28,25 +30,28 @@
   bool PerformLayout() override;
   bool UpdateFWLData() override;
   void UpdateWidgetProperty() override;
-  bool OnLButtonUp(uint32_t dwFlags, const CFX_PointF& point) override;
+  bool OnLButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                   const CFX_PointF& point) override;
   void OnProcessMessage(CFWL_Message* pMessage) override;
   void OnProcessEvent(CFWL_Event* pEvent) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
+  void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix) override;
   FormFieldType GetFormFieldType() override;
 
-  void SetFWLCheckState(XFA_CHECKSTATE eCheckState);
+  void SetFWLCheckState(XFA_CheckState eCheckState);
 
  private:
+  CXFA_FFCheckButton(CXFA_Node* pNode, CXFA_CheckButton* button);
+
   bool CommitData() override;
   bool IsDataChanged() override;
   void CapLeftRightPlacement(const CXFA_Margin* captionMargin);
   void AddUIMargin(XFA_AttributeValue iCapPlacement);
-  XFA_CHECKSTATE FWLState2XFAState();
+  XFA_CheckState FWLState2XFAState();
 
-  UnownedPtr<IFWL_WidgetDelegate> m_pOldDelegate;
-  CFX_RectF m_rtCheckBox;
-  UnownedPtr<CXFA_CheckButton> const button_;
+  cppgc::Member<IFWL_WidgetDelegate> m_pOldDelegate;
+  cppgc::Member<CXFA_CheckButton> const button_;
+  CFX_RectF m_CheckBoxRect;
 };
 
 #endif  // XFA_FXFA_CXFA_FFCHECKBUTTON_H_
diff --git a/xfa/fxfa/cxfa_ffcombobox.cpp b/xfa/fxfa/cxfa_ffcombobox.cpp
index 61fa659..0fa7178 100644
--- a/xfa/fxfa/cxfa_ffcombobox.cpp
+++ b/xfa/fxfa/cxfa_ffcombobox.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,8 @@
 #include <utility>
 #include <vector>
 
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fwl/cfwl_combobox.h"
 #include "xfa/fwl/cfwl_eventselectchanged.h"
 #include "xfa/fwl/cfwl_notedriver.h"
@@ -33,6 +34,11 @@
 
 CXFA_FFComboBox::~CXFA_FFComboBox() = default;
 
+void CXFA_FFComboBox::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FFDropDown::Trace(visitor);
+  visitor->Trace(m_pOldDelegate);
+}
+
 CXFA_FFComboBox* CXFA_FFComboBox::AsComboBox() {
   return this;
 }
@@ -49,13 +55,14 @@
 }
 
 bool CXFA_FFComboBox::LoadWidget() {
-  ASSERT(!IsLoaded());
-  auto pNew = pdfium::MakeUnique<CFWL_ComboBox>(GetFWLApp());
-  CFWL_ComboBox* pComboBox = pNew.get();
-  SetNormalWidget(std::move(pNew));
+  DCHECK(!IsLoaded());
+
+  CFWL_ComboBox* pComboBox = cppgc::MakeGarbageCollected<CFWL_ComboBox>(
+      GetFWLApp()->GetHeap()->GetAllocationHandle(), GetFWLApp());
+  SetNormalWidget(pComboBox);
   pComboBox->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver = pComboBox->GetOwnerApp()->GetNoteDriver();
+  CFWL_NoteDriver* pNoteDriver = pComboBox->GetFWLApp()->GetNoteDriver();
   pNoteDriver->RegisterEventTarget(pComboBox, pComboBox);
   m_pOldDelegate = pComboBox->GetDelegate();
   pComboBox->SetDelegate(this);
@@ -67,7 +74,7 @@
 
     std::vector<int32_t> iSelArray = m_pNode->GetSelectedItems();
     if (iSelArray.empty())
-      pComboBox->SetEditText(m_pNode->GetValue(XFA_VALUEPICTURE_Raw));
+      pComboBox->SetEditText(m_pNode->GetValue(XFA_ValuePicture::kRaw));
     else
       pComboBox->SetCurSel(iSelArray.front());
 
@@ -94,43 +101,41 @@
     dwExtendedStyle |= FWL_STYLEEXT_CMB_ReadOnly;
   }
   dwExtendedStyle |= GetAlignment();
-  GetNormalWidget()->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
+  GetNormalWidget()->ModifyStyleExts(dwExtendedStyle, 0xFFFFFFFF);
 
   if (!m_pNode->IsHorizontalScrollPolicyOff())
     dwEditStyles |= FWL_STYLEEXT_EDT_AutoHScroll;
 
-  pComboBox->EditModifyStylesEx(dwEditStyles, 0xFFFFFFFF);
+  pComboBox->EditModifyStyleExts(dwEditStyles, 0xFFFFFFFF);
 }
 
-bool CXFA_FFComboBox::OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFComboBox::OnRButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                  const CFX_PointF& point) {
   if (!CXFA_FFField::OnRButtonUp(dwFlags, point))
     return false;
 
-  GetDoc()->GetDocEnvironment()->PopupMenu(this, point);
+  GetDoc()->PopupMenu(this, point);
   return true;
 }
 
 bool CXFA_FFComboBox::OnKillFocus(CXFA_FFWidget* pNewWidget) {
-  ObservedPtr<CXFA_FFWidget> pWatched(this);
-  ObservedPtr<CXFA_FFWidget> pNewWatched(pNewWidget);
   if (!ProcessCommittedData())
     UpdateFWLData();
 
-  return pWatched && pNewWatched &&
-         CXFA_FFField::OnKillFocus(pNewWatched.Get());
+  return pNewWidget && CXFA_FFField::OnKillFocus(pNewWidget);
 }
 
 void CXFA_FFComboBox::OpenDropDownList() {
-  ToComboBox(GetNormalWidget())->OpenDropDownList(true);
+  ToComboBox(GetNormalWidget())->ShowDropDownList();
 }
 
 bool CXFA_FFComboBox::CommitData() {
-  return m_pNode->SetValue(XFA_VALUEPICTURE_Raw, m_wsNewValue);
+  return m_pNode->SetValue(XFA_ValuePicture::kRaw, m_wsNewValue);
 }
 
 bool CXFA_FFComboBox::IsDataChanged() {
   WideString wsText = GetCurrentText();
-  if (m_pNode->GetValue(XFA_VALUEPICTURE_Raw) == wsText)
+  if (m_pNode->GetValue(XFA_ValuePicture::kRaw) == wsText)
     return false;
 
   m_wsNewValue = std::move(wsText);
@@ -139,7 +144,6 @@
 
 void CXFA_FFComboBox::FWLEventSelChange(CXFA_EventParam* pParam) {
   pParam->m_eType = XFA_EVENT_Change;
-  pParam->m_pTarget = m_pNode.Get();
   pParam->m_wsPrevText = ToComboBox(GetNormalWidget())->GetEditText();
   m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change, pParam);
 }
@@ -206,7 +210,7 @@
     pComboBox->SetCurSel(iSelArray.front());
   } else {
     pComboBox->SetCurSel(-1);
-    pComboBox->SetEditText(m_pNode->GetValue(XFA_VALUEPICTURE_Raw));
+    pComboBox->SetEditText(m_pNode->GetValue(XFA_ValuePicture::kRaw));
   }
   pComboBox->Update();
   return true;
@@ -222,16 +226,6 @@
          ToComboBox(GetNormalWidget())->EditCanRedo();
 }
 
-bool CXFA_FFComboBox::Undo() {
-  return m_pNode->IsChoiceListAllowTextEntry() &&
-         ToComboBox(GetNormalWidget())->EditUndo();
-}
-
-bool CXFA_FFComboBox::Redo() {
-  return m_pNode->IsChoiceListAllowTextEntry() &&
-         ToComboBox(GetNormalWidget())->EditRedo();
-}
-
 bool CXFA_FFComboBox::CanCopy() {
   return ToComboBox(GetNormalWidget())->EditCanCopy();
 }
@@ -249,13 +243,23 @@
   return ToComboBox(GetNormalWidget())->EditCanSelectAll();
 }
 
-Optional<WideString> CXFA_FFComboBox::Copy() {
+bool CXFA_FFComboBox::Undo() {
+  return m_pNode->IsChoiceListAllowTextEntry() &&
+         ToComboBox(GetNormalWidget())->EditUndo();
+}
+
+bool CXFA_FFComboBox::Redo() {
+  return m_pNode->IsChoiceListAllowTextEntry() &&
+         ToComboBox(GetNormalWidget())->EditRedo();
+}
+
+absl::optional<WideString> CXFA_FFComboBox::Copy() {
   return ToComboBox(GetNormalWidget())->EditCopy();
 }
 
-Optional<WideString> CXFA_FFComboBox::Cut() {
+absl::optional<WideString> CXFA_FFComboBox::Cut() {
   if (!m_pNode->IsChoiceListAllowTextEntry())
-    return {};
+    return absl::nullopt;
 
   return ToComboBox(GetNormalWidget())->EditCut();
 }
@@ -310,19 +314,15 @@
 void CXFA_FFComboBox::OnTextChanged(CFWL_Widget* pWidget,
                                     const WideString& wsChanged) {
   CXFA_EventParam eParam;
-  eParam.m_wsPrevText = m_pNode->GetValue(XFA_VALUEPICTURE_Raw);
+  eParam.m_wsPrevText = m_pNode->GetValue(XFA_ValuePicture::kRaw);
   eParam.m_wsChange = wsChanged;
   FWLEventSelChange(&eParam);
 }
 
 void CXFA_FFComboBox::OnSelectChanged(CFWL_Widget* pWidget, bool bLButtonUp) {
-  ObservedPtr<CXFA_FFComboBox> watched(this);
   CXFA_EventParam eParam;
-  eParam.m_wsPrevText = m_pNode->GetValue(XFA_VALUEPICTURE_Raw);
+  eParam.m_wsPrevText = m_pNode->GetValue(XFA_ValuePicture::kRaw);
   FWLEventSelChange(&eParam);
-  if (!watched)
-    return;
-
   if (m_pNode->IsChoiceListCommitOnSelect() && bLButtonUp)
     m_pDocView->SetFocusNode(nullptr);
 }
@@ -330,14 +330,12 @@
 void CXFA_FFComboBox::OnPreOpen(CFWL_Widget* pWidget) {
   CXFA_EventParam eParam;
   eParam.m_eType = XFA_EVENT_PreOpen;
-  eParam.m_pTarget = m_pNode.Get();
   m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::PreOpen, &eParam);
 }
 
 void CXFA_FFComboBox::OnPostOpen(CFWL_Widget* pWidget) {
   CXFA_EventParam eParam;
   eParam.m_eType = XFA_EVENT_PostOpen;
-  eParam.m_pTarget = m_pNode.Get();
   m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::PostOpen, &eParam);
 }
 
@@ -346,12 +344,11 @@
 }
 
 void CXFA_FFComboBox::OnProcessEvent(CFWL_Event* pEvent) {
-  ObservedPtr<CXFA_FFComboBox> watched(this);
   CXFA_FFField::OnProcessEvent(pEvent);
   switch (pEvent->GetType()) {
     case CFWL_Event::Type::SelectChanged: {
       auto* postEvent = static_cast<CFWL_EventSelectChanged*>(pEvent);
-      OnSelectChanged(GetNormalWidget(), postEvent->bLButtonUp);
+      OnSelectChanged(GetNormalWidget(), postEvent->GetLButtonUp());
       break;
     }
     case CFWL_Event::Type::EditChanged: {
@@ -370,11 +367,10 @@
     default:
       break;
   }
-  if (watched)
-    m_pOldDelegate->OnProcessEvent(pEvent);
+  m_pOldDelegate->OnProcessEvent(pEvent);
 }
 
-void CXFA_FFComboBox::OnDrawWidget(CXFA_Graphics* pGraphics,
+void CXFA_FFComboBox::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                                    const CFX_Matrix& matrix) {
   m_pOldDelegate->OnDrawWidget(pGraphics, matrix);
 }
diff --git a/xfa/fxfa/cxfa_ffcombobox.h b/xfa/fxfa/cxfa_ffcombobox.h
index 3813b6e..d1c8113 100644
--- a/xfa/fxfa/cxfa_ffcombobox.h
+++ b/xfa/fxfa/cxfa_ffcombobox.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,18 @@
 #ifndef XFA_FXFA_CXFA_FFCOMBOBOX_H_
 #define XFA_FXFA_CXFA_FFCOMBOBOX_H_
 
-#include "core/fxcrt/unowned_ptr.h"
+#include "v8/include/cppgc/member.h"
 #include "xfa/fxfa/cxfa_ffdropdown.h"
 
 class CXFA_EventParam;
 
 class CXFA_FFComboBox final : public CXFA_FFDropDown {
  public:
-  explicit CXFA_FFComboBox(CXFA_Node* pNode);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFComboBox() override;
 
+  void Trace(cppgc::Visitor* visitor) const override;
+
   // CXFA_FFDropDown:
   CXFA_FFComboBox* AsComboBox() override;
 
@@ -24,19 +26,19 @@
   CFX_RectF GetBBox(FocusOption focus) override;
   bool LoadWidget() override;
   void UpdateWidgetProperty() override;
-  bool OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnKillFocus(CXFA_FFWidget* pNewWidget) override WARN_UNUSED_RESULT;
+  bool OnRButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                   const CFX_PointF& point) override;
+  [[nodiscard]] bool OnKillFocus(CXFA_FFWidget* pNewWidget) override;
   bool CanUndo() override;
   bool CanRedo() override;
-  bool Undo() override;
-  bool Redo() override;
-
   bool CanCopy() override;
   bool CanCut() override;
   bool CanPaste() override;
   bool CanSelectAll() override;
-  Optional<WideString> Copy() override;
-  Optional<WideString> Cut() override;
+  bool Undo() override;
+  bool Redo() override;
+  absl::optional<WideString> Copy() override;
+  absl::optional<WideString> Cut() override;
   bool Paste(const WideString& wsPaste) override;
   void SelectAll() override;
   void Delete() override;
@@ -47,7 +49,7 @@
   // IFWL_WidgetDelegate
   void OnProcessMessage(CFWL_Message* pMessage) override;
   void OnProcessEvent(CFWL_Event* pEvent) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
+  void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix) override;
 
   // CXFA_FFDropDown
@@ -63,6 +65,8 @@
   void SetItemState(int32_t nIndex, bool bSelected);
 
  private:
+  explicit CXFA_FFComboBox(CXFA_Node* pNode);
+
   // CXFA_FFField:
   bool PtInActiveRect(const CFX_PointF& point) override;
   bool CommitData() override;
@@ -74,7 +78,7 @@
   WideString GetCurrentText() const;
 
   WideString m_wsNewValue;
-  UnownedPtr<IFWL_WidgetDelegate> m_pOldDelegate;
+  cppgc::Member<IFWL_WidgetDelegate> m_pOldDelegate;
 };
 
 #endif  // XFA_FXFA_CXFA_FFCOMBOBOX_H_
diff --git a/xfa/fxfa/cxfa_ffdatetimeedit.cpp b/xfa/fxfa/cxfa_ffdatetimeedit.cpp
index d8d57f5..70bd20d 100644
--- a/xfa/fxfa/cxfa_ffdatetimeedit.cpp
+++ b/xfa/fxfa/cxfa_ffdatetimeedit.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,8 @@
 
 #include "xfa/fxfa/cxfa_ffdatetimeedit.h"
 
-#include <utility>
-
-#include "third_party/base/ptr_util.h"
+#include "core/fxcrt/cfx_datetime.h"
+#include "third_party/base/check.h"
 #include "xfa/fwl/cfwl_datetimepicker.h"
 #include "xfa/fwl/cfwl_eventselectchanged.h"
 #include "xfa/fwl/cfwl_notedriver.h"
@@ -42,20 +41,22 @@
 }
 
 bool CXFA_FFDateTimeEdit::LoadWidget() {
-  ASSERT(!IsLoaded());
-  auto pNewPicker = pdfium::MakeUnique<CFWL_DateTimePicker>(GetFWLApp());
-  CFWL_DateTimePicker* pWidget = pNewPicker.get();
-  SetNormalWidget(std::move(pNewPicker));
+  DCHECK(!IsLoaded());
+
+  CFWL_DateTimePicker* pWidget =
+      cppgc::MakeGarbageCollected<CFWL_DateTimePicker>(
+          GetFWLApp()->GetHeap()->GetAllocationHandle(), GetFWLApp());
+  SetNormalWidget(pWidget);
   pWidget->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver = pWidget->GetOwnerApp()->GetNoteDriver();
+  CFWL_NoteDriver* pNoteDriver = pWidget->GetFWLApp()->GetNoteDriver();
   pNoteDriver->RegisterEventTarget(pWidget, pWidget);
   m_pOldDelegate = pWidget->GetDelegate();
   pWidget->SetDelegate(this);
 
   {
     CFWL_Widget::ScopedUpdateLock update_lock(pWidget);
-    WideString wsText = m_pNode->GetValue(XFA_VALUEPICTURE_Display);
+    WideString wsText = m_pNode->GetValue(XFA_ValuePicture::kDisplay);
     pWidget->SetEditText(wsText);
 
     CXFA_Value* value = m_pNode->GetFormValueIfExists();
@@ -88,20 +89,20 @@
   uint32_t dwExtendedStyle = FWL_STYLEEXT_DTP_ShortDateFormat;
   dwExtendedStyle |= UpdateUIProperty();
   dwExtendedStyle |= GetAlignment();
-  GetNormalWidget()->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
+  GetNormalWidget()->ModifyStyleExts(dwExtendedStyle, 0xFFFFFFFF);
 
   uint32_t dwEditStyles = 0;
-  Optional<int32_t> numCells = m_pNode->GetNumberOfCells();
-  if (numCells && *numCells > 0) {
+  absl::optional<int32_t> numCells = m_pNode->GetNumberOfCells();
+  if (numCells.has_value() && numCells.value() > 0) {
     dwEditStyles |= FWL_STYLEEXT_EDT_CombText;
-    pPicker->SetEditLimit(*numCells);
+    pPicker->SetEditLimit(numCells.value());
   }
   if (!m_pNode->IsOpenAccess() || !GetDoc()->GetXFADoc()->IsInteractive())
     dwEditStyles |= FWL_STYLEEXT_EDT_ReadOnly;
   if (!m_pNode->IsHorizontalScrollPolicyOff())
     dwEditStyles |= FWL_STYLEEXT_EDT_AutoHScroll;
 
-  pPicker->ModifyEditStylesEx(dwEditStyles, 0xFFFFFFFF);
+  pPicker->ModifyEditStyleExts(dwEditStyles, 0xFFFFFFFF);
 }
 
 uint32_t CXFA_FFDateTimeEdit::GetAlignment() {
@@ -144,7 +145,7 @@
 
 bool CXFA_FFDateTimeEdit::CommitData() {
   CFWL_DateTimePicker* pPicker = GetPickerWidget();
-  if (!m_pNode->SetValue(XFA_VALUEPICTURE_Edit, pPicker->GetEditText()))
+  if (!m_pNode->SetValue(XFA_ValuePicture::kEdit, pPicker->GetEditText()))
     return false;
 
   GetDoc()->GetDocView()->UpdateUIDisplay(m_pNode.Get(), this);
@@ -155,9 +156,9 @@
   if (!GetNormalWidget())
     return false;
 
-  XFA_VALUEPICTURE eType = XFA_VALUEPICTURE_Display;
+  XFA_ValuePicture eType = XFA_ValuePicture::kDisplay;
   if (IsFocused())
-    eType = XFA_VALUEPICTURE_Edit;
+    eType = XFA_ValuePicture::kEdit;
 
   WideString wsText = m_pNode->GetValue(eType);
   CFWL_DateTimePicker* pPicker = GetPickerWidget();
@@ -175,43 +176,42 @@
 }
 
 bool CXFA_FFDateTimeEdit::IsDataChanged() {
-  if (GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_TextEditValueChanged))
+  if (GetLayoutItem()->TestStatusBits(XFA_WidgetStatus::kTextEditValueChanged))
     return true;
 
   WideString wsText = GetPickerWidget()->GetEditText();
-  return m_pNode->GetValue(XFA_VALUEPICTURE_Edit) != wsText;
+  return m_pNode->GetValue(XFA_ValuePicture::kEdit) != wsText;
 }
 
 void CXFA_FFDateTimeEdit::OnSelectChanged(CFWL_Widget* pWidget,
                                           int32_t iYear,
                                           int32_t iMonth,
                                           int32_t iDay) {
-  WideString wsPicture = m_pNode->GetPictureContent(XFA_VALUEPICTURE_Edit);
-
-  CXFA_LocaleValue date(XFA_VT_DATE, GetDoc()->GetXFADoc()->GetLocaleMgr());
+  WideString wsPicture = m_pNode->GetPictureContent(XFA_ValuePicture::kEdit);
+  CXFA_LocaleValue date(CXFA_LocaleValue::ValueType::kDate,
+                        GetDoc()->GetXFADoc()->GetLocaleMgr());
   date.SetDate(CFX_DateTime(iYear, iMonth, iDay, 0, 0, 0, 0));
 
   WideString wsDate;
   date.FormatPatterns(wsDate, wsPicture, m_pNode->GetLocale(),
-                      XFA_VALUEPICTURE_Edit);
+                      XFA_ValuePicture::kEdit);
 
   CFWL_DateTimePicker* pPicker = GetPickerWidget();
   pPicker->SetEditText(wsDate);
   pPicker->Update();
-  GetDoc()->GetDocEnvironment()->SetFocusWidget(GetDoc(), nullptr);
+  GetDoc()->SetFocusWidget(nullptr);
 
   CXFA_EventParam eParam;
   eParam.m_eType = XFA_EVENT_Change;
-  eParam.m_pTarget = m_pNode.Get();
-  eParam.m_wsPrevText = m_pNode->GetValue(XFA_VALUEPICTURE_Raw);
+  eParam.m_wsPrevText = m_pNode->GetValue(XFA_ValuePicture::kRaw);
   m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change, &eParam);
 }
 
 void CXFA_FFDateTimeEdit::OnProcessEvent(CFWL_Event* pEvent) {
   if (pEvent->GetType() == CFWL_Event::Type::SelectChanged) {
     auto* event = static_cast<CFWL_EventSelectChanged*>(pEvent);
-    OnSelectChanged(GetNormalWidget(), event->iYear, event->iMonth,
-                    event->iDay);
+    OnSelectChanged(GetNormalWidget(), event->GetYear(), event->GetMonth(),
+                    event->GetDay());
     return;
   }
   CXFA_FFTextEdit::OnProcessEvent(pEvent);
@@ -225,6 +225,28 @@
   return GetPickerWidget()->CanRedo();
 }
 
+bool CXFA_FFDateTimeEdit::CanCopy() {
+  return GetPickerWidget()->HasSelection();
+}
+
+bool CXFA_FFDateTimeEdit::CanCut() {
+  if (GetPickerWidget()->GetStyleExts() & FWL_STYLEEXT_EDT_ReadOnly)
+    return false;
+  return GetPickerWidget()->HasSelection();
+}
+
+bool CXFA_FFDateTimeEdit::CanPaste() {
+  return !(GetPickerWidget()->GetStyleExts() & FWL_STYLEEXT_EDT_ReadOnly);
+}
+
+bool CXFA_FFDateTimeEdit::CanSelectAll() {
+  return GetPickerWidget()->GetEditTextLength() > 0;
+}
+
+absl::optional<WideString> CXFA_FFDateTimeEdit::Copy() {
+  return GetPickerWidget()->Copy();
+}
+
 bool CXFA_FFDateTimeEdit::Undo() {
   return GetPickerWidget()->Undo();
 }
@@ -233,29 +255,7 @@
   return GetPickerWidget()->Redo();
 }
 
-bool CXFA_FFDateTimeEdit::CanCopy() {
-  return GetPickerWidget()->HasSelection();
-}
-
-bool CXFA_FFDateTimeEdit::CanCut() {
-  if (GetPickerWidget()->GetStylesEx() & FWL_STYLEEXT_EDT_ReadOnly)
-    return false;
-  return GetPickerWidget()->HasSelection();
-}
-
-bool CXFA_FFDateTimeEdit::CanPaste() {
-  return !(GetPickerWidget()->GetStylesEx() & FWL_STYLEEXT_EDT_ReadOnly);
-}
-
-bool CXFA_FFDateTimeEdit::CanSelectAll() {
-  return GetPickerWidget()->GetEditTextLength() > 0;
-}
-
-Optional<WideString> CXFA_FFDateTimeEdit::Copy() {
-  return GetPickerWidget()->Copy();
-}
-
-Optional<WideString> CXFA_FFDateTimeEdit::Cut() {
+absl::optional<WideString> CXFA_FFDateTimeEdit::Cut() {
   return GetPickerWidget()->Cut();
 }
 
diff --git a/xfa/fxfa/cxfa_ffdatetimeedit.h b/xfa/fxfa/cxfa_ffdatetimeedit.h
index 5ba6867..44b2e32 100644
--- a/xfa/fxfa/cxfa_ffdatetimeedit.h
+++ b/xfa/fxfa/cxfa_ffdatetimeedit.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,12 +10,6 @@
 #include "core/fxcrt/fx_coordinates.h"
 #include "xfa/fxfa/cxfa_fftextedit.h"
 
-enum XFA_DATETIMETYPE {
-  XFA_DATETIMETYPE_Date = 0,
-  XFA_DATETIMETYPE_Time,
-  XFA_DATETIMETYPE_DateAndTime
-};
-
 class CFWL_DateTimePicker;
 class CFWL_Event;
 class CFWL_Widget;
@@ -39,14 +33,14 @@
   // CXFA_FFWidget
   bool CanUndo() override;
   bool CanRedo() override;
-  bool Undo() override;
-  bool Redo() override;
   bool CanCopy() override;
   bool CanCut() override;
   bool CanPaste() override;
   bool CanSelectAll() override;
-  Optional<WideString> Copy() override;
-  Optional<WideString> Cut() override;
+  bool Undo() override;
+  bool Redo() override;
+  absl::optional<WideString> Copy() override;
+  absl::optional<WideString> Cut() override;
   bool Paste(const WideString& wsPaste) override;
   void SelectAll() override;
   void Delete() override;
diff --git a/xfa/fxfa/cxfa_ffdoc.cpp b/xfa/fxfa/cxfa_ffdoc.cpp
index ac4b35f..0c424eb 100644
--- a/xfa/fxfa/cxfa_ffdoc.cpp
+++ b/xfa/fxfa/cxfa_ffdoc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,21 +8,25 @@
 
 #include <algorithm>
 #include <memory>
-#include <vector>
+#include <utility>
 
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfdoc/cpdf_nametree.h"
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/xml/cfx_xmldocument.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
+#include "core/fxcrt/xml/cfx_xmlparser.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "v8/include/cppgc/allocation.h"
+#include "v8/include/cppgc/heap.h"
+#include "xfa/fgas/font/cfgas_gefont.h"
 #include "xfa/fgas/font/cfgas_pdffontmgr.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
@@ -35,7 +39,7 @@
 #include "xfa/fxfa/parser/cxfa_acrobat7.h"
 #include "xfa/fxfa/parser/cxfa_dataexporter.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_document_parser.h"
+#include "xfa/fxfa/parser/cxfa_document_builder.h"
 #include "xfa/fxfa/parser/cxfa_dynamicrender.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
@@ -50,96 +54,189 @@
 
 FX_IMAGEDIB_AND_DPI::~FX_IMAGEDIB_AND_DPI() = default;
 
-// static
-std::unique_ptr<CXFA_FFDoc> CXFA_FFDoc::CreateAndOpen(
-    CXFA_FFApp* pApp,
-    IXFA_DocEnvironment* pDocEnvironment,
-    CPDF_Document* pPDFDoc,
-    const RetainPtr<IFX_SeekableStream>& stream) {
-  ASSERT(pApp);
-  ASSERT(pDocEnvironment);
-  ASSERT(pPDFDoc);
-
-  // Use WrapUnique() to keep constructor private.
-  auto result =
-      pdfium::WrapUnique(new CXFA_FFDoc(pApp, pDocEnvironment, pPDFDoc));
-  if (!result->OpenDoc(stream))
-    return nullptr;
-
-  return result;
-}
-
 CXFA_FFDoc::CXFA_FFDoc(CXFA_FFApp* pApp,
-                       IXFA_DocEnvironment* pDocEnvironment,
-                       CPDF_Document* pPDFDoc)
+                       CallbackIface* pDocEnvironment,
+                       CPDF_Document* pPDFDoc,
+                       cppgc::Heap* pHeap)
     : m_pDocEnvironment(pDocEnvironment),
-      m_pApp(pApp),
       m_pPDFDoc(pPDFDoc),
-      m_pNotify(pdfium::MakeUnique<CXFA_FFNotify>(this)),
-      m_pDocument(pdfium::MakeUnique<CXFA_Document>(
-          m_pNotify.get(),
-          pdfium::MakeUnique<CXFA_LayoutProcessor>())) {}
+      m_pHeap(pHeap),
+      m_pApp(pApp),
+      m_pNotify(cppgc::MakeGarbageCollected<CXFA_FFNotify>(
+          pHeap->GetAllocationHandle(),
+          this)),
+      m_pDocument(cppgc::MakeGarbageCollected<CXFA_Document>(
+          pHeap->GetAllocationHandle(),
+          m_pNotify,
+          pHeap,
+          cppgc::MakeGarbageCollected<CXFA_LayoutProcessor>(
+              pHeap->GetAllocationHandle(),
+              pHeap))) {}
 
-CXFA_FFDoc::~CXFA_FFDoc() {
-  if (m_DocView) {
+CXFA_FFDoc::~CXFA_FFDoc() = default;
+
+void CXFA_FFDoc::PreFinalize() {
+  if (m_DocView)
     m_DocView->RunDocClose();
-    m_DocView.reset();
-  }
+
   if (m_pDocument)
     m_pDocument->ClearLayoutData();
-
-  m_pDocument.reset();
-  m_pXMLDoc.reset();
-  m_pNotify.reset();
-  m_pPDFFontMgr.reset();
-  m_HashToDibDpiMap.clear();
-  m_pApp->ClearEventTargets();
 }
 
-bool CXFA_FFDoc::ParseDoc(const RetainPtr<IFX_SeekableStream>& stream) {
-  CXFA_DocumentParser parser(m_pDocument.get());
-  bool parsed = parser.Parse(stream, XFA_PacketType::Xdp);
+void CXFA_FFDoc::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pApp);
+  visitor->Trace(m_pNotify);
+  visitor->Trace(m_pDocument);
+  visitor->Trace(m_DocView);
+}
 
-  // We have to set the XML document before we return so that we can clean
-  // up in the OpenDoc method. If we don't, the XMLDocument will get free'd
-  // when this method returns and UnownedPtrs get unhappy.
-  m_pXMLDoc = parser.GetXMLDoc();
+bool CXFA_FFDoc::BuildDoc(CFX_XMLDocument* pXML) {
+  DCHECK(pXML);
 
-  if (!parsed)
+  CXFA_DocumentBuilder builder(m_pDocument);
+  if (!builder.BuildDocument(pXML, XFA_PacketType::Xdp))
     return false;
 
-  m_pDocument->SetRoot(parser.GetRootNode());
+  m_pDocument->SetRoot(builder.GetRootNode());
   return true;
 }
 
 CXFA_FFDocView* CXFA_FFDoc::CreateDocView() {
-  if (!m_DocView)
-    m_DocView = pdfium::MakeUnique<CXFA_FFDocView>(this);
+  if (!m_DocView) {
+    m_DocView = cppgc::MakeGarbageCollected<CXFA_FFDocView>(
+        m_pHeap->GetAllocationHandle(), this);
+  }
+  return m_DocView;
+}
 
-  return m_DocView.get();
+void CXFA_FFDoc::SetChangeMark() {
+  m_pDocEnvironment->SetChangeMark(this);
+}
+
+void CXFA_FFDoc::InvalidateRect(CXFA_FFPageView* pPageView,
+                                const CFX_RectF& rt) {
+  m_pDocEnvironment->InvalidateRect(pPageView, rt);
+}
+
+void CXFA_FFDoc::DisplayCaret(CXFA_FFWidget* hWidget,
+                              bool bVisible,
+                              const CFX_RectF* pRtAnchor) {
+  return m_pDocEnvironment->DisplayCaret(hWidget, bVisible, pRtAnchor);
+}
+
+bool CXFA_FFDoc::GetPopupPos(CXFA_FFWidget* hWidget,
+                             float fMinPopup,
+                             float fMaxPopup,
+                             const CFX_RectF& rtAnchor,
+                             CFX_RectF* pPopupRect) const {
+  return m_pDocEnvironment->GetPopupPos(hWidget, fMinPopup, fMaxPopup, rtAnchor,
+                                        pPopupRect);
+}
+
+bool CXFA_FFDoc::PopupMenu(CXFA_FFWidget* hWidget, const CFX_PointF& ptPopup) {
+  return m_pDocEnvironment->PopupMenu(hWidget, ptPopup);
+}
+
+void CXFA_FFDoc::OnPageViewEvent(CXFA_FFPageView* pPageView,
+                                 PageViewEvent eEvent) {
+  m_pDocEnvironment->OnPageViewEvent(pPageView, eEvent);
+}
+
+void CXFA_FFDoc::WidgetPostAdd(CXFA_FFWidget* hWidget) {
+  m_pDocEnvironment->WidgetPostAdd(hWidget);
+}
+
+void CXFA_FFDoc::WidgetPreRemove(CXFA_FFWidget* hWidget) {
+  m_pDocEnvironment->WidgetPreRemove(hWidget);
+}
+
+int32_t CXFA_FFDoc::CountPages() const {
+  return m_pDocEnvironment->CountPages(this);
+}
+
+int32_t CXFA_FFDoc::GetCurrentPage() const {
+  return m_pDocEnvironment->GetCurrentPage(this);
+}
+
+void CXFA_FFDoc::SetCurrentPage(int32_t iCurPage) {
+  m_pDocEnvironment->SetCurrentPage(this, iCurPage);
+}
+
+bool CXFA_FFDoc::IsCalculationsEnabled() const {
+  return m_pDocEnvironment->IsCalculationsEnabled(this);
+}
+
+void CXFA_FFDoc::SetCalculationsEnabled(bool bEnabled) {
+  return m_pDocEnvironment->SetCalculationsEnabled(this, bEnabled);
+}
+
+WideString CXFA_FFDoc::GetTitle() const {
+  return m_pDocEnvironment->GetTitle(this);
+}
+
+void CXFA_FFDoc::SetTitle(const WideString& wsTitle) {
+  m_pDocEnvironment->SetTitle(this, wsTitle);
+}
+
+void CXFA_FFDoc::ExportData(const WideString& wsFilePath, bool bXDP) {
+  m_pDocEnvironment->ExportData(this, wsFilePath, bXDP);
+}
+
+void CXFA_FFDoc::GotoURL(const WideString& bsURL) {
+  m_pDocEnvironment->GotoURL(this, bsURL);
+}
+
+bool CXFA_FFDoc::IsValidationsEnabled() const {
+  return m_pDocEnvironment->IsValidationsEnabled(this);
+}
+
+void CXFA_FFDoc::SetValidationsEnabled(bool bEnabled) {
+  m_pDocEnvironment->SetValidationsEnabled(this, bEnabled);
+}
+
+void CXFA_FFDoc::SetFocusWidget(CXFA_FFWidget* hWidget) {
+  m_pDocEnvironment->SetFocusWidget(this, hWidget);
+}
+
+void CXFA_FFDoc::Print(int32_t nStartPage,
+                       int32_t nEndPage,
+                       Mask<XFA_PrintOpt> dwOptions) {
+  m_pDocEnvironment->Print(this, nStartPage, nEndPage, dwOptions);
+}
+
+FX_ARGB CXFA_FFDoc::GetHighlightColor() const {
+  return m_pDocEnvironment->GetHighlightColor(this);
+}
+
+IJS_Runtime* CXFA_FFDoc::GetIJSRuntime() const {
+  return m_pDocEnvironment->GetIJSRuntime(this);
+}
+
+CFX_XMLDocument* CXFA_FFDoc::GetXMLDocument() const {
+  return m_pDocEnvironment->GetXMLDoc();
+}
+
+RetainPtr<IFX_SeekableReadStream> CXFA_FFDoc::OpenLinkedFile(
+    const WideString& wsLink) {
+  return m_pDocEnvironment->OpenLinkedFile(this, wsLink);
 }
 
 CXFA_FFDocView* CXFA_FFDoc::GetDocView(CXFA_LayoutProcessor* pLayout) {
-  return m_DocView && m_DocView->GetXFALayout() == pLayout ? m_DocView.get()
-                                                           : nullptr;
+  return m_DocView && m_DocView->GetLayoutProcessor() == pLayout ? m_DocView
+                                                                 : nullptr;
 }
 
 CXFA_FFDocView* CXFA_FFDoc::GetDocView() {
-  return m_DocView.get();
+  return m_DocView;
 }
 
-bool CXFA_FFDoc::OpenDoc(const RetainPtr<IFX_SeekableStream>& stream) {
-  if (!ParseDoc(stream))
-    return false;
-
-  CFGAS_FontMgr* mgr = GetApp()->GetFDEFontMgr();
-  if (!mgr)
+bool CXFA_FFDoc::OpenDoc(CFX_XMLDocument* pXML) {
+  if (!BuildDoc(pXML))
     return false;
 
   // At this point we've got an XFA document and we want to always return
   // true to signify the load succeeded.
-  m_pPDFFontMgr = pdfium::MakeUnique<CFGAS_PDFFontMgr>(GetPDFDoc(), mgr);
-
+  m_pPDFFontMgr = std::make_unique<CFGAS_PDFFontMgr>(GetPDFDoc());
   m_FormType = FormType::kXFAForeground;
   CXFA_Node* pConfig = ToNode(m_pDocument->GetXFAObject(XFA_HASHCODE_Config));
   if (!pConfig)
@@ -168,10 +265,19 @@
   return true;
 }
 
+RetainPtr<CFGAS_GEFont> CXFA_FFDoc::GetPDFFont(const WideString& family,
+                                               uint32_t styles,
+                                               bool strict) {
+  if (!m_pPDFFontMgr)
+    return nullptr;
+
+  return m_pPDFFontMgr->GetFont(family, styles, strict);
+}
+
 RetainPtr<CFX_DIBitmap> CXFA_FFDoc::GetPDFNamedImage(WideStringView wsName,
                                                      int32_t& iImageXDpi,
                                                      int32_t& iImageYDpi) {
-  uint32_t dwHash = FX_HashCode_GetW(wsName, false);
+  uint32_t dwHash = FX_HashCode_GetW(wsName);
   auto it = m_HashToDibDpiMap.find(dwHash);
   if (it != m_HashToDibDpiMap.end()) {
     iImageXDpi = it->second.iImageXDpi;
@@ -179,50 +285,43 @@
     return it->second.pDibSource.As<CFX_DIBitmap>();
   }
 
-  CPDF_Dictionary* pRoot = m_pPDFDoc->GetRoot();
-  if (!pRoot)
+  auto name_tree = CPDF_NameTree::Create(m_pPDFDoc, "XFAImages");
+  size_t count = name_tree ? name_tree->GetCount() : 0;
+  if (count == 0)
     return nullptr;
 
-  CPDF_Dictionary* pNames = pRoot->GetDictFor("Names");
-  if (!pNames)
-    return nullptr;
-
-  CPDF_Dictionary* pXFAImages = pNames->GetDictFor("XFAImages");
-  if (!pXFAImages)
-    return nullptr;
-
-  CPDF_NameTree nametree(pXFAImages);
-  CPDF_Object* pObject = nametree.LookupValue(WideString(wsName));
+  RetainPtr<const CPDF_Object> pObject =
+      name_tree->LookupValue(WideString(wsName));
   if (!pObject) {
-    for (size_t i = 0; i < nametree.GetCount(); i++) {
+    for (size_t i = 0; i < count; ++i) {
       WideString wsTemp;
-      CPDF_Object* pTempObject = nametree.LookupValueAndName(i, &wsTemp);
+      RetainPtr<CPDF_Object> pTempObject =
+          name_tree->LookupValueAndName(i, &wsTemp);
       if (wsTemp == wsName) {
-        pObject = pTempObject;
+        pObject = std::move(pTempObject);
         break;
       }
     }
   }
 
-  CPDF_Stream* pStream = ToStream(pObject);
+  RetainPtr<const CPDF_Stream> pStream = ToStream(pObject);
   if (!pStream)
     return nullptr;
 
-  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
+  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(pStream));
   pAcc->LoadAllDataFiltered();
 
   auto pImageFileRead =
-      pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(pAcc->GetSpan());
-
+      pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pAcc->GetSpan());
   RetainPtr<CFX_DIBitmap> pDibSource = XFA_LoadImageFromBuffer(
-      pImageFileRead, FXCODEC_IMAGE_UNKNOWN, iImageXDpi, iImageYDpi);
+      std::move(pImageFileRead), FXCODEC_IMAGE_UNKNOWN, iImageXDpi, iImageYDpi);
   m_HashToDibDpiMap[dwHash] = {pDibSource, iImageXDpi, iImageYDpi};
   return pDibSource;
 }
 
 bool CXFA_FFDoc::SavePackage(CXFA_Node* pNode,
                              const RetainPtr<IFX_SeekableStream>& pFile) {
-  ASSERT(pNode || GetXFADoc()->GetRoot());
+  DCHECK(pNode || GetXFADoc()->GetRoot());
 
   CXFA_DataExporter exporter;
   return exporter.Export(pFile, pNode ? pNode : GetXFADoc()->GetRoot());
diff --git a/xfa/fxfa/cxfa_ffdoc.h b/xfa/fxfa/cxfa_ffdoc.h
index 04d98bc..b5f2c45 100644
--- a/xfa/fxfa/cxfa_ffdoc.h
+++ b/xfa/fxfa/cxfa_ffdoc.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,21 +10,34 @@
 #include <map>
 #include <memory>
 
+#include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/mask.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/prefinalizer.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fxfa/fxfa.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 
+class CFGAS_GEFont;
 class CFGAS_PDFFontMgr;
-class CFX_ChecksumContext;
 class CFX_DIBBase;
 class CFX_DIBitmap;
 class CFX_XMLDocument;
 class CPDF_Document;
 class CXFA_FFApp;
-class CXFA_FFNotify;
+class CXFA_FFDoc;
 class CXFA_FFDocView;
+class CXFA_FFNotify;
+class CXFA_FFPageView;
+class CXFA_FFWidget;
 class CXFA_LayoutProcessor;
+class IJS_Runtime;
 
 struct FX_IMAGEDIB_AND_DPI {
   FX_IMAGEDIB_AND_DPI();
@@ -39,51 +52,147 @@
   int32_t iImageYDpi;
 };
 
-class CXFA_FFDoc {
- public:
-  static std::unique_ptr<CXFA_FFDoc> CreateAndOpen(
-      CXFA_FFApp* pApp,
-      IXFA_DocEnvironment* pDocEnvironment,
-      CPDF_Document* pPDFDoc,
-      const RetainPtr<IFX_SeekableStream>& stream);
+class CXFA_FFDoc : public cppgc::GarbageCollected<CXFA_FFDoc> {
+  CPPGC_USING_PRE_FINALIZER(CXFA_FFDoc, PreFinalize);
 
+ public:
+  enum class PageViewEvent {
+    kPostAdded = 1,
+    kPostRemoved = 3,
+    kStopLayout = 4,
+  };
+
+  class CallbackIface {
+   public:
+    virtual ~CallbackIface() = default;
+
+    virtual void SetChangeMark(CXFA_FFDoc* hDoc) = 0;
+    virtual void InvalidateRect(CXFA_FFPageView* pPageView,
+                                const CFX_RectF& rt) = 0;
+    // Show or hide caret.
+    virtual void DisplayCaret(CXFA_FFWidget* hWidget,
+                              bool bVisible,
+                              const CFX_RectF* pRtAnchor) = 0;
+
+    virtual bool GetPopupPos(CXFA_FFWidget* hWidget,
+                             float fMinPopup,
+                             float fMaxPopup,
+                             const CFX_RectF& rtAnchor,
+                             CFX_RectF* pPopupRect) = 0;
+    virtual bool PopupMenu(CXFA_FFWidget* hWidget,
+                           const CFX_PointF& ptPopup) = 0;
+
+    virtual void OnPageViewEvent(CXFA_FFPageView* pPageView,
+                                 PageViewEvent eEvent) = 0;
+
+    // Caller must not pass in nullptr.
+    virtual void WidgetPostAdd(CXFA_FFWidget* hWidget) = 0;
+    virtual void WidgetPreRemove(CXFA_FFWidget* hWidget) = 0;
+
+    virtual int32_t CountPages(const CXFA_FFDoc* hDoc) const = 0;
+    virtual int32_t GetCurrentPage(const CXFA_FFDoc* hDoc) const = 0;
+    virtual void SetCurrentPage(CXFA_FFDoc* hDoc, int32_t iCurPage) = 0;
+    virtual bool IsCalculationsEnabled(const CXFA_FFDoc* hDoc) const = 0;
+    virtual void SetCalculationsEnabled(CXFA_FFDoc* hDoc, bool bEnabled) = 0;
+    virtual WideString GetTitle(const CXFA_FFDoc* hDoc) const = 0;
+    virtual void SetTitle(CXFA_FFDoc* hDoc, const WideString& wsTitle) = 0;
+    virtual void ExportData(CXFA_FFDoc* hDoc,
+                            const WideString& wsFilePath,
+                            bool bXDP) = 0;
+    virtual void GotoURL(CXFA_FFDoc* hDoc, const WideString& bsURL) = 0;
+    virtual bool IsValidationsEnabled(const CXFA_FFDoc* hDoc) const = 0;
+    virtual void SetValidationsEnabled(CXFA_FFDoc* hDoc, bool bEnabled) = 0;
+    virtual void SetFocusWidget(CXFA_FFDoc* hDoc, CXFA_FFWidget* hWidget) = 0;
+    virtual void Print(CXFA_FFDoc* hDoc,
+                       int32_t nStartPage,
+                       int32_t nEndPage,
+                       Mask<XFA_PrintOpt> dwOptions) = 0;
+    virtual FX_ARGB GetHighlightColor(const CXFA_FFDoc* hDoc) const = 0;
+    virtual IJS_Runtime* GetIJSRuntime(const CXFA_FFDoc* hDoc) const = 0;
+    virtual CFX_XMLDocument* GetXMLDoc() const = 0;
+    virtual RetainPtr<IFX_SeekableReadStream> OpenLinkedFile(
+        CXFA_FFDoc* hDoc,
+        const WideString& wsLink) = 0;
+
+#ifdef PDF_XFA_ELEMENT_SUBMIT_ENABLED
+    virtual bool Submit(CXFA_FFDoc* hDoc, CXFA_Submit* submit) = 0;
+#endif  // PDF_XFA_ELEMENT_SUBMIT_ENABLED
+  };
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFDoc();
 
-  IXFA_DocEnvironment* GetDocEnvironment() const {
-    return m_pDocEnvironment.Get();
-  }
-  FormType GetFormType() const { return m_FormType; }
-  CFX_XMLDocument* GetXMLDocument() const { return m_pXMLDoc.get(); }
+  void PreFinalize();
+  void Trace(cppgc::Visitor* visitor) const;
+
+  bool OpenDoc(CFX_XMLDocument* pXML);
+
+  void SetChangeMark();
+  void InvalidateRect(CXFA_FFPageView* pPageView, const CFX_RectF& rt);
+  void DisplayCaret(CXFA_FFWidget* hWidget,
+                    bool bVisible,
+                    const CFX_RectF* pRtAnchor);
+  bool GetPopupPos(CXFA_FFWidget* hWidget,
+                   float fMinPopup,
+                   float fMaxPopup,
+                   const CFX_RectF& rtAnchor,
+                   CFX_RectF* pPopupRect) const;
+  bool PopupMenu(CXFA_FFWidget* hWidget, const CFX_PointF& ptPopup);
+  void OnPageViewEvent(CXFA_FFPageView* pPageView, PageViewEvent eEvent);
+  void WidgetPostAdd(CXFA_FFWidget* hWidget);
+  void WidgetPreRemove(CXFA_FFWidget* hWidget);
+  int32_t CountPages() const;
+  int32_t GetCurrentPage() const;
+  void SetCurrentPage(int32_t iCurPage);
+  bool IsCalculationsEnabled() const;
+  void SetCalculationsEnabled(bool bEnabled);
+  WideString GetTitle() const;
+  void SetTitle(const WideString& wsTitle);
+  void ExportData(const WideString& wsFilePath, bool bXDP);
+  void GotoURL(const WideString& bsURL);
+  bool IsValidationsEnabled() const;
+  void SetValidationsEnabled(bool bEnabled);
+  void SetFocusWidget(CXFA_FFWidget* hWidget);
+  void Print(int32_t nStartPage,
+             int32_t nEndPage,
+             Mask<XFA_PrintOpt> dwOptions);
+  FX_ARGB GetHighlightColor() const;
+  IJS_Runtime* GetIJSRuntime() const;
+  CFX_XMLDocument* GetXMLDocument() const;
+  RetainPtr<IFX_SeekableReadStream> OpenLinkedFile(const WideString& wsLink);
 
   CXFA_FFDocView* CreateDocView();
-
-  CXFA_Document* GetXFADoc() const { return m_pDocument.get(); }
-  CXFA_FFApp* GetApp() const { return m_pApp.Get(); }
-  CPDF_Document* GetPDFDoc() const { return m_pPDFDoc.Get(); }
+  FormType GetFormType() const { return m_FormType; }
+  cppgc::Heap* GetHeap() const { return m_pHeap; }
+  CXFA_Document* GetXFADoc() const { return m_pDocument; }
+  CXFA_FFApp* GetApp() const { return m_pApp; }
+  CPDF_Document* GetPDFDoc() const { return m_pPDFDoc; }
   CXFA_FFDocView* GetDocView(CXFA_LayoutProcessor* pLayout);
   CXFA_FFDocView* GetDocView();
+  RetainPtr<CFGAS_GEFont> GetPDFFont(const WideString& family,
+                                     uint32_t styles,
+                                     bool strict);
   RetainPtr<CFX_DIBitmap> GetPDFNamedImage(WideStringView wsName,
                                            int32_t& iImageXDpi,
                                            int32_t& iImageYDpi);
-  CFGAS_PDFFontMgr* GetPDFFontMgr() const { return m_pPDFFontMgr.get(); }
 
   bool SavePackage(CXFA_Node* pNode,
                    const RetainPtr<IFX_SeekableStream>& pFile);
 
  private:
   CXFA_FFDoc(CXFA_FFApp* pApp,
-             IXFA_DocEnvironment* pDocEnvironment,
-             CPDF_Document* pPDFDoc);
-  bool OpenDoc(const RetainPtr<IFX_SeekableStream>& stream);
-  bool ParseDoc(const RetainPtr<IFX_SeekableStream>& stream);
+             CallbackIface* pDocEnvironment,
+             CPDF_Document* pPDFDoc,
+             cppgc::Heap* pHeap);
+  bool BuildDoc(CFX_XMLDocument* pXML);
 
-  UnownedPtr<IXFA_DocEnvironment> const m_pDocEnvironment;
-  UnownedPtr<CXFA_FFApp> const m_pApp;
+  UnownedPtr<CallbackIface> const m_pDocEnvironment;
   UnownedPtr<CPDF_Document> const m_pPDFDoc;
-  std::unique_ptr<CFX_XMLDocument> m_pXMLDoc;
-  std::unique_ptr<CXFA_FFNotify> m_pNotify;
-  std::unique_ptr<CXFA_Document> m_pDocument;
-  std::unique_ptr<CXFA_FFDocView> m_DocView;
+  UnownedPtr<cppgc::Heap> const m_pHeap;
+  cppgc::Member<CXFA_FFApp> const m_pApp;
+  cppgc::Member<CXFA_FFNotify> m_pNotify;
+  cppgc::Member<CXFA_Document> m_pDocument;
+  cppgc::Member<CXFA_FFDocView> m_DocView;
   std::unique_ptr<CFGAS_PDFFontMgr> m_pPDFFontMgr;
   std::map<uint32_t, FX_IMAGEDIB_AND_DPI> m_HashToDibDpiMap;
   FormType m_FormType = FormType::kXFAForeground;
diff --git a/xfa/fxfa/cxfa_ffdocview.cpp b/xfa/fxfa/cxfa_ffdocview.cpp
index 3a44122..7d4ef85 100644
--- a/xfa/fxfa/cxfa_ffdocview.cpp
+++ b/xfa/fxfa/cxfa_ffdocview.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,10 +10,13 @@
 #include <utility>
 
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/stl_util.h"
+#include "core/fxcrt/xml/cfx_xmlparser.h"
+#include "fxjs/gc/container_trace.h"
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/contains.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffbarcode.h"
 #include "xfa/fxfa/cxfa_ffcheckbutton.h"
@@ -39,10 +42,24 @@
 #include "xfa/fxfa/parser/cxfa_present.h"
 #include "xfa/fxfa/parser/cxfa_subform.h"
 #include "xfa/fxfa/parser/cxfa_validate.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
-const XFA_AttributeValue gs_EventActivity[] = {
+namespace {
+
+bool IsValidXMLNameString(const WideString& str) {
+  bool first = true;
+  for (const auto ch : str) {
+    if (!CFX_XMLParser::IsXMLNameChar(ch, first)) {
+      return false;
+    }
+    first = false;
+  }
+  return true;
+}
+
+}  // namespace
+
+const XFA_AttributeValue kXFAEventActivity[] = {
     XFA_AttributeValue::Click,      XFA_AttributeValue::Change,
     XFA_AttributeValue::DocClose,   XFA_AttributeValue::DocReady,
     XFA_AttributeValue::Enter,      XFA_AttributeValue::Exit,
@@ -59,10 +76,32 @@
     XFA_AttributeValue::Unknown,
 };
 
+CXFA_FFDocView::UpdateScope::UpdateScope(CXFA_FFDocView* pDocView)
+    : m_pDocView(pDocView) {
+  m_pDocView->LockUpdate();
+}
+
+CXFA_FFDocView::UpdateScope::~UpdateScope() {
+  m_pDocView->UnlockUpdate();
+  m_pDocView->UpdateDocView();
+}
+
 CXFA_FFDocView::CXFA_FFDocView(CXFA_FFDoc* pDoc) : m_pDoc(pDoc) {}
 
 CXFA_FFDocView::~CXFA_FFDocView() = default;
 
+void CXFA_FFDocView::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pDoc);
+  visitor->Trace(m_pWidgetHandler);
+  visitor->Trace(m_pFocusNode);
+  visitor->Trace(m_pFocusWidget);
+  ContainerTrace(visitor, m_ValidateNodes);
+  ContainerTrace(visitor, m_CalculateNodes);
+  ContainerTrace(visitor, m_NewAddedNodes);
+  ContainerTrace(visitor, m_BindItems);
+  ContainerTrace(visitor, m_IndexChangedSubforms);
+}
+
 void CXFA_FFDocView::InitLayout(CXFA_Node* pNode) {
   RunBindItems();
   ExecEventActivityByDeepFirst(pNode, XFA_EVENT_Initialize, false, true);
@@ -70,12 +109,11 @@
 }
 
 int32_t CXFA_FFDocView::StartLayout() {
-  m_iStatus = XFA_DOCVIEW_LAYOUTSTATUS_Start;
+  m_iStatus = LayoutStatus::kStart;
   m_pDoc->GetXFADoc()->DoProtoMerge();
   m_pDoc->GetXFADoc()->DoDataMerge();
-  m_pXFADocLayout = GetXFALayout();
 
-  int32_t iStatus = m_pXFADocLayout->StartLayout(false);
+  int32_t iStatus = GetLayoutProcessor()->StartLayout();
   if (iStatus < 0)
     return iStatus;
 
@@ -89,16 +127,16 @@
   InitValidate(pRootItem);
 
   ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_Ready, true, true);
-  m_iStatus = XFA_DOCVIEW_LAYOUTSTATUS_Start;
+  m_iStatus = LayoutStatus::kStart;
   return iStatus;
 }
 
 int32_t CXFA_FFDocView::DoLayout() {
-  int32_t iStatus = m_pXFADocLayout->DoLayout();
+  int32_t iStatus = GetLayoutProcessor()->DoLayout();
   if (iStatus != 100)
     return iStatus;
 
-  m_iStatus = XFA_DOCVIEW_LAYOUTSTATUS_Doing;
+  m_iStatus = LayoutStatus::kDoing;
   return iStatus;
 }
 
@@ -137,21 +175,25 @@
 
   m_CalculateNodes.clear();
   if (m_pFocusNode && !m_pFocusWidget)
-    SetFocusNode(m_pFocusNode.Get());
+    SetFocusNode(m_pFocusNode);
 
-  m_iStatus = XFA_DOCVIEW_LAYOUTSTATUS_End;
+  m_iStatus = LayoutStatus::kEnd;
+}
+
+void CXFA_FFDocView::AddNullTestMsg(const WideString& msg) {
+  m_NullTestMsgArray.push_back(msg);
 }
 
 void CXFA_FFDocView::ShowNullTestMsg() {
-  int32_t iCount = pdfium::CollectionSize<int32_t>(m_arrNullTestMsg);
+  int32_t iCount = fxcrt::CollectionSize<int32_t>(m_NullTestMsgArray);
   CXFA_FFApp* pApp = m_pDoc->GetApp();
-  IXFA_AppProvider* pAppProvider = pApp->GetAppProvider();
+  CXFA_FFApp::CallbackIface* pAppProvider = pApp->GetAppProvider();
   if (pAppProvider && iCount) {
     int32_t iRemain = iCount > 7 ? iCount - 7 : 0;
     iCount -= iRemain;
     WideString wsMsg;
     for (int32_t i = 0; i < iCount; i++)
-      wsMsg += m_arrNullTestMsg[i] + L"\n";
+      wsMsg += m_NullTestMsgArray[i] + L"\n";
 
     if (iRemain > 0) {
       wsMsg += L"\n" + WideString::Format(
@@ -163,7 +205,7 @@
                          static_cast<uint32_t>(AlertIcon::kStatus),
                          static_cast<uint32_t>(AlertButton::kOK));
   }
-  m_arrNullTestMsg.clear();
+  m_NullTestMsgArray.clear();
 }
 
 void CXFA_FFDocView::UpdateDocView() {
@@ -203,28 +245,26 @@
          pWidget->IsFocused())) {
       continue;
     }
-    ObservedPtr<CXFA_FFWidget> pWatched(pWidget);
-    ObservedPtr<CXFA_FFWidget> pWatchedNext(pNext);
-    pWatched->UpdateFWLData();
-    if (pWatched)
-      pWatched->InvalidateRect();
-    if (!pWatchedNext)
-      break;
+    pWidget->UpdateFWLData();
+    pWidget->InvalidateRect();
   }
 }
 
 int32_t CXFA_FFDocView::CountPageViews() const {
-  return m_pXFADocLayout ? m_pXFADocLayout->CountPages() : 0;
+  CXFA_LayoutProcessor* pProcessor = GetLayoutProcessor();
+  return pProcessor ? pProcessor->CountPages() : 0;
 }
 
 CXFA_FFPageView* CXFA_FFDocView::GetPageView(int32_t nIndex) const {
-  if (!m_pXFADocLayout)
+  CXFA_LayoutProcessor* pProcessor = GetLayoutProcessor();
+  if (!pProcessor)
     return nullptr;
-  auto* pPage = m_pXFADocLayout->GetPage(nIndex);
+
+  auto* pPage = pProcessor->GetPage(nIndex);
   return pPage ? pPage->GetPageView() : nullptr;
 }
 
-CXFA_LayoutProcessor* CXFA_FFDocView::GetXFALayout() const {
+CXFA_LayoutProcessor* CXFA_FFDocView::GetLayoutProcessor() const {
   return CXFA_LayoutProcessor::FromDocument(m_pDoc->GetXFADoc());
 }
 
@@ -240,7 +280,7 @@
     return true;
 
   AddValidateNode(pNode);
-  validate->SetFlag(XFA_NodeFlag_NeedsInitApp);
+  validate->SetFlag(XFA_NodeFlag::kNeedsInitApp);
   return true;
 }
 
@@ -267,64 +307,58 @@
     }
   }
   if (bChanged)
-    m_pDoc->GetDocEnvironment()->SetChangeMark(m_pDoc.Get());
+    m_pDoc->SetChangeMark();
 }
 
 CXFA_FFWidget* CXFA_FFDocView::GetWidgetForNode(CXFA_Node* node) {
-  return GetFFWidget(ToContentLayoutItem(GetXFALayout()->GetLayoutItem(node)));
+  return GetFFWidget(
+      ToContentLayoutItem(GetLayoutProcessor()->GetLayoutItem(node)));
 }
 
 CXFA_FFWidgetHandler* CXFA_FFDocView::GetWidgetHandler() {
-  if (!m_pWidgetHandler)
-    m_pWidgetHandler = pdfium::MakeUnique<CXFA_FFWidgetHandler>(this);
-  return m_pWidgetHandler.get();
-}
-
-std::unique_ptr<CXFA_ReadyNodeIterator>
-CXFA_FFDocView::CreateReadyNodeIterator() {
-  CXFA_Subform* pFormRoot = GetRootSubform();
-  return pFormRoot ? pdfium::MakeUnique<CXFA_ReadyNodeIterator>(pFormRoot)
-                   : nullptr;
+  if (!m_pWidgetHandler) {
+    m_pWidgetHandler = cppgc::MakeGarbageCollected<CXFA_FFWidgetHandler>(
+        m_pDoc->GetHeap()->GetAllocationHandle(), this);
+  }
+  return m_pWidgetHandler;
 }
 
 bool CXFA_FFDocView::SetFocus(CXFA_FFWidget* pNewFocus) {
   if (pNewFocus == m_pFocusWidget)
     return false;
 
-  ObservedPtr<CXFA_FFWidget> pNewWatched(pNewFocus);
   if (m_pFocusWidget) {
     CXFA_ContentLayoutItem* pItem = m_pFocusWidget->GetLayoutItem();
-    if (pItem->TestStatusBits(XFA_WidgetStatus_Visible) &&
-        !pItem->TestStatusBits(XFA_WidgetStatus_Focused)) {
+    if (pItem->TestStatusBits(XFA_WidgetStatus::kVisible) &&
+        !pItem->TestStatusBits(XFA_WidgetStatus::kFocused)) {
       if (!m_pFocusWidget->IsLoaded())
         m_pFocusWidget->LoadWidget();
-      if (!m_pFocusWidget->OnSetFocus(m_pFocusWidget.Get()))
-        m_pFocusWidget.Reset();
+      if (!m_pFocusWidget->OnSetFocus(m_pFocusWidget))
+        m_pFocusWidget.Clear();
     }
   }
   if (m_pFocusWidget) {
-    if (!m_pFocusWidget->OnKillFocus(pNewWatched.Get()))
+    if (!m_pFocusWidget->OnKillFocus(pNewFocus))
       return false;
   }
 
-  if (pNewWatched) {
-    if (pNewWatched->GetLayoutItem()->TestStatusBits(
-            XFA_WidgetStatus_Visible)) {
-      if (!pNewWatched->IsLoaded())
-        pNewWatched->LoadWidget();
-      if (!pNewWatched->OnSetFocus(m_pFocusWidget.Get()))
-        pNewWatched.Reset();
+  if (pNewFocus) {
+    if (pNewFocus->GetLayoutItem()->TestStatusBits(
+            XFA_WidgetStatus::kVisible)) {
+      if (!pNewFocus->IsLoaded())
+        pNewFocus->LoadWidget();
+      if (!pNewFocus->OnSetFocus(m_pFocusWidget))
+        pNewFocus = nullptr;
     }
   }
-  if (pNewWatched) {
-    CXFA_Node* node = pNewWatched->GetNode();
+  if (pNewFocus) {
+    CXFA_Node* node = pNewFocus->GetNode();
     m_pFocusNode = node->IsWidgetReady() ? node : nullptr;
-    m_pFocusWidget.Reset(pNewWatched.Get());
+    m_pFocusWidget = pNewFocus;
   } else {
-    m_pFocusNode.Reset();
-    m_pFocusWidget.Reset();
+    m_pFocusNode.Clear();
+    m_pFocusWidget.Clear();
   }
-
   return true;
 }
 
@@ -334,19 +368,18 @@
     return;
 
   m_pFocusNode = node;
-  if (m_iStatus != XFA_DOCVIEW_LAYOUTSTATUS_End)
+  if (m_iStatus != LayoutStatus::kEnd)
     return;
 
-  m_pDoc->GetDocEnvironment()->SetFocusWidget(m_pDoc.Get(),
-                                              m_pFocusWidget.Get());
+  m_pDoc->SetFocusWidget(m_pFocusWidget);
 }
 
 void CXFA_FFDocView::DeleteLayoutItem(CXFA_FFWidget* pWidget) {
   if (m_pFocusNode != pWidget->GetNode())
     return;
 
-  m_pFocusNode.Reset();
-  m_pFocusWidget.Reset();
+  m_pFocusNode.Clear();
+  m_pFocusWidget.Clear();
 }
 
 static XFA_EventError XFA_ProcessEvent(CXFA_FFDocView* pDocView,
@@ -361,10 +394,8 @@
     case XFA_EVENT_Calculate:
       return pNode->ProcessCalculate(pDocView);
     case XFA_EVENT_Validate:
-      if (pDocView->GetDoc()->GetDocEnvironment()->IsValidationsEnabled(
-              pDocView->GetDoc())) {
+      if (pDocView->GetDoc()->IsValidationsEnabled())
         return pNode->ProcessValidate(pDocView, 0x01);
-      }
       return XFA_EventError::kDisabled;
     case XFA_EVENT_InitCalculate: {
       CXFA_Calculate* calc = pNode->GetCalculateIfExists();
@@ -372,11 +403,10 @@
         return XFA_EventError::kNotExist;
       if (pNode->IsUserInteractive())
         return XFA_EventError::kDisabled;
-
       return pNode->ExecuteScript(pDocView, calc->GetScriptIfExists(), pParam);
     }
     default:
-      return pNode->ProcessEvent(pDocView, gs_EventActivity[pParam->m_eType],
+      return pNode->ProcessEvent(pDocView, kXFAEventActivity[pParam->m_eType],
                                  pParam);
   }
 }
@@ -399,7 +429,6 @@
 
     CXFA_EventParam eParam;
     eParam.m_eType = eEventType;
-    eParam.m_pTarget = pFormNode;
     eParam.m_bIsFormReady = bIsFormReady;
     return XFA_ProcessEvent(this, pFormNode, &eParam);
   }
@@ -422,7 +451,6 @@
 
   CXFA_EventParam eParam;
   eParam.m_eType = eEventType;
-  eParam.m_pTarget = pFormNode;
   eParam.m_bIsFormReady = bIsFormReady;
 
   XFA_EventErrorAccumulate(&iRet, XFA_ProcessEvent(this, pFormNode, &eParam));
@@ -431,6 +459,9 @@
 
 CXFA_FFWidget* CXFA_FFDocView::GetWidgetByName(const WideString& wsName,
                                                CXFA_FFWidget* pRefWidget) {
+  if (!IsValidXMLNameString(wsName)) {
+    return nullptr;
+  }
   CFXJSE_Engine* pScriptContext = m_pDoc->GetXFADoc()->GetScriptContext();
   CXFA_Node* pRefNode = nullptr;
   if (pRefWidget) {
@@ -438,51 +469,49 @@
     pRefNode = node->IsWidgetReady() ? node : nullptr;
   }
   WideString wsExpression = (!pRefNode ? L"$form." : L"") + wsName;
-
-  XFA_RESOLVENODE_RS resolveNodeRS;
-  constexpr uint32_t kStyle = XFA_RESOLVENODE_Children |
-                              XFA_RESOLVENODE_Properties |
-                              XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent;
-  if (!pScriptContext->ResolveObjects(pRefNode, wsExpression.AsStringView(),
-                                      &resolveNodeRS, kStyle, nullptr)) {
+  absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+      pScriptContext->ResolveObjects(
+          pRefNode, wsExpression.AsStringView(),
+          Mask<XFA_ResolveFlag>{
+              XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kProperties,
+              XFA_ResolveFlag::kSiblings, XFA_ResolveFlag::kParent});
+  if (!maybeResult.has_value())
     return nullptr;
-  }
 
-  if (resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    CXFA_Node* pNode = resolveNodeRS.objects.front()->AsNode();
+  if (maybeResult.value().type == CFXJSE_Engine::ResolveResult::Type::kNodes) {
+    CXFA_Node* pNode = maybeResult.value().objects.front()->AsNode();
     if (pNode && pNode->IsWidgetReady())
       return GetWidgetForNode(pNode);
   }
   return nullptr;
 }
 
-void CXFA_FFDocView::OnPageEvent(CXFA_ViewLayoutItem* pSender,
-                                 uint32_t dwEvent) {
+void CXFA_FFDocView::OnPageViewEvent(CXFA_ViewLayoutItem* pSender,
+                                     CXFA_FFDoc::PageViewEvent eEvent) {
   CXFA_FFPageView* pFFPageView = pSender ? pSender->GetPageView() : nullptr;
-  m_pDoc->GetDocEnvironment()->PageViewEvent(pFFPageView, dwEvent);
+  m_pDoc->OnPageViewEvent(pFFPageView, eEvent);
 }
 
 void CXFA_FFDocView::InvalidateRect(CXFA_FFPageView* pPageView,
                                     const CFX_RectF& rtInvalidate) {
-  m_pDoc->GetDocEnvironment()->InvalidateRect(pPageView, rtInvalidate);
+  m_pDoc->InvalidateRect(pPageView, rtInvalidate);
 }
 
 bool CXFA_FFDocView::RunLayout() {
   LockUpdate();
   m_bInLayoutStatus = true;
-  if (!m_pXFADocLayout->IncrementLayout() &&
-      m_pXFADocLayout->StartLayout(false) < 100) {
-    m_pXFADocLayout->DoLayout();
+
+  CXFA_LayoutProcessor* pProcessor = GetLayoutProcessor();
+  if (!pProcessor->IncrementLayout() && pProcessor->StartLayout() < 100) {
+    pProcessor->DoLayout();
     UnlockUpdate();
     m_bInLayoutStatus = false;
-    m_pDoc->GetDocEnvironment()->PageViewEvent(nullptr,
-                                               XFA_PAGEVIEWEVENT_StopLayout);
+    m_pDoc->OnPageViewEvent(nullptr, CXFA_FFDoc::PageViewEvent::kStopLayout);
     return true;
   }
 
   m_bInLayoutStatus = false;
-  m_pDoc->GetDocEnvironment()->PageViewEvent(nullptr,
-                                             XFA_PAGEVIEWEVENT_StopLayout);
+  m_pDoc->OnPageViewEvent(nullptr, CXFA_FFDoc::PageViewEvent::kStopLayout);
   UnlockUpdate();
   return false;
 }
@@ -498,7 +527,6 @@
 
     CXFA_EventParam eParam;
     eParam.m_eType = XFA_EVENT_IndexChange;
-    eParam.m_pTarget = pSubformNode;
     pSubformNode->ProcessEvent(this, XFA_AttributeValue::IndexChange, &eParam);
   }
 }
@@ -508,9 +536,8 @@
   InitLayout(pNode);
 }
 
-void CXFA_FFDocView::AddIndexChangedSubform(CXFA_Node* pNode) {
-  ASSERT(pNode->GetElementType() == XFA_Element::Subform);
-  if (!pdfium::ContainsValue(m_IndexChangedSubforms, pNode))
+void CXFA_FFDocView::AddIndexChangedSubform(CXFA_Subform* pNode) {
+  if (!pdfium::Contains(m_IndexChangedSubforms, pNode))
     m_IndexChangedSubforms.push_back(pNode);
 }
 
@@ -531,11 +558,11 @@
 }
 
 void CXFA_FFDocView::AddCalculateNodeNotify(CXFA_Node* pNodeChange) {
-  CXFA_CalcData* pGlobalData = pNodeChange->JSObject()->GetCalcData();
+  CJX_Object::CalcData* pGlobalData = pNodeChange->JSObject()->GetCalcData();
   if (!pGlobalData)
     return;
 
-  for (auto* pResult : pGlobalData->m_Globals) {
+  for (auto& pResult : pGlobalData->m_Globals) {
     if (!pResult->HasRemovedChildren() && pResult->IsWidgetReady())
       AddCalculateNode(pResult);
   }
@@ -561,7 +588,7 @@
 }
 
 XFA_EventError CXFA_FFDocView::RunCalculateWidgets() {
-  if (!m_pDoc->GetDocEnvironment()->IsCalculationsEnabled(m_pDoc.Get()))
+  if (!m_pDoc->IsCalculationsEnabled())
     return XFA_EventError::kDisabled;
 
   if (!m_CalculateNodes.empty())
@@ -575,7 +602,7 @@
 }
 
 void CXFA_FFDocView::AddValidateNode(CXFA_Node* node) {
-  if (!pdfium::ContainsValue(m_ValidateNodes, node))
+  if (!pdfium::Contains(m_ValidateNodes, node))
     m_ValidateNodes.push_back(node);
 }
 
@@ -591,7 +618,7 @@
 }
 
 bool CXFA_FFDocView::InitValidate(CXFA_Node* pNode) {
-  if (!m_pDoc->GetDocEnvironment()->IsValidationsEnabled(m_pDoc.Get()))
+  if (!m_pDoc->IsValidationsEnabled())
     return false;
 
   ExecEventActivityByDeepFirst(pNode, XFA_EVENT_Validate, false, true);
@@ -600,7 +627,7 @@
 }
 
 bool CXFA_FFDocView::RunValidate() {
-  if (!m_pDoc->GetDocEnvironment()->IsValidationsEnabled(m_pDoc.Get()))
+  if (!m_pDoc->IsValidationsEnabled())
     return false;
 
   while (!m_ValidateNodes.empty()) {
@@ -637,16 +664,19 @@
     CFXJSE_Engine* pScriptContext =
         pWidgetNode->GetDocument()->GetScriptContext();
     WideString wsRef = item->GetRef();
-    constexpr uint32_t kStyle =
-        XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
-        XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_ALL;
-    XFA_RESOLVENODE_RS rs;
-    pScriptContext->ResolveObjects(pWidgetNode, wsRef.AsStringView(), &rs,
-                                   kStyle, nullptr);
+    absl::optional<CFXJSE_Engine::ResolveResult> maybeRS =
+        pScriptContext->ResolveObjects(
+            pWidgetNode, wsRef.AsStringView(),
+            Mask<XFA_ResolveFlag>{
+                XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kProperties,
+                XFA_ResolveFlag::kSiblings, XFA_ResolveFlag::kParent,
+                XFA_ResolveFlag::kALL});
     pWidgetNode->DeleteItem(-1, false, false);
-    if (rs.dwFlags != XFA_ResolveNode_RSType_Nodes || rs.objects.empty())
+    if (!maybeRS.has_value() ||
+        maybeRS.value().type != CFXJSE_Engine::ResolveResult::Type::kNodes ||
+        maybeRS.value().objects.empty()) {
       continue;
-
+    }
     WideString wsValueRef = item->GetValueRef();
     WideString wsLabelRef = item->GetLabelRef();
     const bool bUseValue = wsLabelRef.IsEmpty() || wsLabelRef == wsValueRef;
@@ -656,8 +686,8 @@
         wsValueRef.IsEmpty() || wsValueRef.EqualsASCII("$");
     WideString wsValue;
     WideString wsLabel;
-    uint32_t uValueHash = FX_HashCode_GetW(wsValueRef.AsStringView(), false);
-    for (auto& refObject : rs.objects) {
+    uint32_t uValueHash = FX_HashCode_GetW(wsValueRef.AsStringView());
+    for (auto& refObject : maybeRS.value().objects) {
       CXFA_Node* refNode = refObject->AsNode();
       if (!refNode)
         continue;
@@ -688,13 +718,13 @@
 }
 
 void CXFA_FFDocView::SetChangeMark() {
-  if (m_iStatus < XFA_DOCVIEW_LAYOUTSTATUS_End)
+  if (m_iStatus != LayoutStatus::kEnd)
     return;
 
-  m_pDoc->GetDocEnvironment()->SetChangeMark(m_pDoc.Get());
+  m_pDoc->SetChangeMark();
 }
 
-CXFA_Subform* CXFA_FFDocView::GetRootSubform() {
+CXFA_Node* CXFA_FFDocView::GetRootSubform() {
   CXFA_Node* pFormPacketNode =
       ToNode(m_pDoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form));
   if (!pFormPacketNode)
diff --git a/xfa/fxfa/cxfa_ffdocview.h b/xfa/fxfa/cxfa_ffdocview.h
index dbc5319..89c37bb 100644
--- a/xfa/fxfa/cxfa_ffdocview.h
+++ b/xfa/fxfa/cxfa_ffdocview.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,13 @@
 #ifndef XFA_FXFA_CXFA_FFDOCVIEW_H_
 #define XFA_FXFA_CXFA_FFDOCVIEW_H_
 
-#include <deque>
-#include <memory>
+#include <list>
 #include <vector>
 
-#include "core/fxcrt/observed_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
@@ -22,38 +23,39 @@
 class CXFA_FFDoc;
 class CXFA_FFWidgetHandler;
 class CXFA_Node;
-class CXFA_ReadyNodeIterator;
 class CXFA_Subform;
 class CXFA_ViewLayoutItem;
 
-extern const XFA_AttributeValue gs_EventActivity[];
-enum XFA_DOCVIEW_LAYOUTSTATUS {
-  XFA_DOCVIEW_LAYOUTSTATUS_None,
-  XFA_DOCVIEW_LAYOUTSTATUS_Start,
-  XFA_DOCVIEW_LAYOUTSTATUS_FormInitialize,
-  XFA_DOCVIEW_LAYOUTSTATUS_FormInitCalculate,
-  XFA_DOCVIEW_LAYOUTSTATUS_FormInitValidate,
-  XFA_DOCVIEW_LAYOUTSTATUS_FormFormReady,
-  XFA_DOCVIEW_LAYOUTSTATUS_Doing,
-  XFA_DOCVIEW_LAYOUTSTATUS_PagesetInitialize,
-  XFA_DOCVIEW_LAYOUTSTATUS_PagesetInitCalculate,
-  XFA_DOCVIEW_LAYOUTSTATUS_PagesetInitValidate,
-  XFA_DOCVIEW_LAYOUTSTATUS_PagesetFormReady,
-  XFA_DOCVIEW_LAYOUTSTATUS_LayoutReady,
-  XFA_DOCVIEW_LAYOUTSTATUS_DocReady,
-  XFA_DOCVIEW_LAYOUTSTATUS_End
-};
+extern const XFA_AttributeValue kXFAEventActivity[];
 
-class CXFA_FFDocView {
+class CXFA_FFDocView : public cppgc::GarbageCollected<CXFA_FFDocView> {
  public:
-  explicit CXFA_FFDocView(CXFA_FFDoc* pDoc);
+  enum class LayoutStatus : uint8_t { kNone, kStart, kDoing, kEnd };
+
+  class UpdateScope {
+    CPPGC_STACK_ALLOCATED();
+
+   public:
+    explicit UpdateScope(CXFA_FFDocView* pDocView);
+    ~UpdateScope();
+
+   private:
+    UnownedPtr<CXFA_FFDocView> const m_pDocView;
+  };
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFDocView();
 
-  CXFA_FFDoc* GetDoc() const { return m_pDoc.Get(); }
+  void Trace(cppgc::Visitor* visitor) const;
+
+  CXFA_FFDoc* GetDoc() const { return m_pDoc; }
   int32_t StartLayout();
   int32_t DoLayout();
   void StopLayout();
-  int32_t GetLayoutStatus() const { return m_iStatus; }
+
+  void SetLayoutEvent() { m_bLayoutEvent = true; }
+  bool InLayoutStatus() const { return m_bInLayoutStatus; }
+  LayoutStatus GetLayoutStatus() const { return m_iStatus; }
 
   void UpdateDocView();
   void UpdateUIDisplay(CXFA_Node* pNode, CXFA_FFWidget* pExcept);
@@ -62,15 +64,16 @@
   CXFA_FFPageView* GetPageView(int32_t nIndex) const;
 
   void ResetNode(CXFA_Node* pNode);
+  CXFA_Node* GetRootSubform();
   CXFA_FFWidgetHandler* GetWidgetHandler();
-  std::unique_ptr<CXFA_ReadyNodeIterator> CreateReadyNodeIterator();
-  CXFA_FFWidget* GetFocusWidget() const { return m_pFocusWidget.Get(); }
+  CXFA_FFWidget* GetFocusWidget() const { return m_pFocusWidget; }
   bool SetFocus(CXFA_FFWidget* pNewFocus);
   CXFA_FFWidget* GetWidgetForNode(CXFA_Node* node);
   CXFA_FFWidget* GetWidgetByName(const WideString& wsName,
                                  CXFA_FFWidget* pRefWidget);
-  CXFA_LayoutProcessor* GetXFALayout() const;
-  void OnPageEvent(CXFA_ViewLayoutItem* pSender, uint32_t dwEvent);
+  CXFA_LayoutProcessor* GetLayoutProcessor() const;
+  void OnPageViewEvent(CXFA_ViewLayoutItem* pSender,
+                       CXFA_FFDoc::PageViewEvent eEvent);
   void LockUpdate() { m_iLock++; }
   void UnlockUpdate() { m_iLock--; }
   void InvalidateRect(CXFA_FFPageView* pPageView,
@@ -86,8 +89,8 @@
 
   bool RunLayout();
   void AddNewFormNode(CXFA_Node* pNode);
-  void AddIndexChangedSubform(CXFA_Node* pNode);
-  CXFA_Node* GetFocusNode() const { return m_pFocusNode.Get(); }
+  void AddIndexChangedSubform(CXFA_Subform* pNode);
+  CXFA_Node* GetFocusNode() const { return m_pFocusNode; }
   void SetFocusNode(CXFA_Node* pNode);
   void DeleteLayoutItem(CXFA_FFWidget* pWidget);
   XFA_EventError ExecEventActivityByDeepFirst(CXFA_Node* pFormNode,
@@ -96,14 +99,11 @@
                                               bool bRecursive);
 
   void AddBindItem(CXFA_BindItems* item) { m_BindItems.push_back(item); }
-
-  bool m_bLayoutEvent = false;
-  bool m_bInLayoutStatus = false;
-  std::vector<WideString> m_arrNullTestMsg;
-
-  void ResetLayoutProcessor() { m_pXFADocLayout.Release(); }
+  void AddNullTestMsg(const WideString& msg);
 
  private:
+  explicit CXFA_FFDocView(CXFA_FFDoc* pDoc);
+
   bool RunEventLayoutReady();
   void RunBindItems();
   void InitCalculate(CXFA_Node* pNode);
@@ -111,7 +111,6 @@
   size_t RunCalculateRecursive(size_t index);
   void ShowNullTestMsg();
   bool ResetSingleNodeData(CXFA_Node* pNode);
-  CXFA_Subform* GetRootSubform();
 
   bool IsUpdateLocked() const { return m_iLock > 0; }
   bool InitValidate(CXFA_Node* pNode);
@@ -119,17 +118,19 @@
   XFA_EventError RunCalculateWidgets();
   void RunSubformIndexChange();
 
-  UnownedPtr<CXFA_FFDoc> const m_pDoc;
-  std::unique_ptr<CXFA_FFWidgetHandler> m_pWidgetHandler;
-  UnownedPtr<CXFA_LayoutProcessor> m_pXFADocLayout;
-  UnownedPtr<CXFA_Node> m_pFocusNode;
-  ObservedPtr<CXFA_FFWidget> m_pFocusWidget;
-  std::deque<CXFA_Node*> m_ValidateNodes;
-  std::vector<CXFA_Node*> m_CalculateNodes;
-  std::deque<CXFA_BindItems*> m_BindItems;
-  std::deque<CXFA_Node*> m_NewAddedNodes;
-  std::deque<CXFA_Node*> m_IndexChangedSubforms;
-  XFA_DOCVIEW_LAYOUTSTATUS m_iStatus = XFA_DOCVIEW_LAYOUTSTATUS_None;
+  cppgc::Member<CXFA_FFDoc> const m_pDoc;
+  cppgc::Member<CXFA_FFWidgetHandler> m_pWidgetHandler;
+  cppgc::Member<CXFA_Node> m_pFocusNode;
+  cppgc::Member<CXFA_FFWidget> m_pFocusWidget;
+  std::list<cppgc::Member<CXFA_Node>> m_ValidateNodes;
+  std::vector<cppgc::Member<CXFA_Node>> m_CalculateNodes;
+  std::list<cppgc::Member<CXFA_BindItems>> m_BindItems;
+  std::list<cppgc::Member<CXFA_Node>> m_NewAddedNodes;
+  std::list<cppgc::Member<CXFA_Subform>> m_IndexChangedSubforms;
+  std::vector<WideString> m_NullTestMsgArray;
+  bool m_bLayoutEvent = false;
+  bool m_bInLayoutStatus = false;
+  LayoutStatus m_iStatus = LayoutStatus::kNone;
   int32_t m_iLock = 0;
 };
 
diff --git a/xfa/fxfa/cxfa_ffdropdown.cpp b/xfa/fxfa/cxfa_ffdropdown.cpp
index 4289dc1..a445809 100644
--- a/xfa/fxfa/cxfa_ffdropdown.cpp
+++ b/xfa/fxfa/cxfa_ffdropdown.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fxfa/cxfa_ffdropdown.h b/xfa/fxfa/cxfa_ffdropdown.h
index b1751b4..b59ef1b 100644
--- a/xfa/fxfa/cxfa_ffdropdown.h
+++ b/xfa/fxfa/cxfa_ffdropdown.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,6 +14,7 @@
 
 class CXFA_FFDropDown : public CXFA_FFField {
  public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFDropDown() override;
 
   // CXFA_FFField:
diff --git a/xfa/fxfa/cxfa_ffexclgroup.cpp b/xfa/fxfa/cxfa_ffexclgroup.cpp
index dc0b4c7..3e26407 100644
--- a/xfa/fxfa/cxfa_ffexclgroup.cpp
+++ b/xfa/fxfa/cxfa_ffexclgroup.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,9 +13,9 @@
 
 CXFA_FFExclGroup::CXFA_FFExclGroup(CXFA_Node* pNode) : CXFA_FFWidget(pNode) {}
 
-CXFA_FFExclGroup::~CXFA_FFExclGroup() {}
+CXFA_FFExclGroup::~CXFA_FFExclGroup() = default;
 
-void CXFA_FFExclGroup::RenderWidget(CXFA_Graphics* pGS,
+void CXFA_FFExclGroup::RenderWidget(CFGAS_GEGraphics* pGS,
                                     const CFX_Matrix& matrix,
                                     HighlightOption highlight) {
   if (!HasVisibleStatus())
diff --git a/xfa/fxfa/cxfa_ffexclgroup.h b/xfa/fxfa/cxfa_ffexclgroup.h
index a14e071..0fea48f 100644
--- a/xfa/fxfa/cxfa_ffexclgroup.h
+++ b/xfa/fxfa/cxfa_ffexclgroup.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,13 +12,16 @@
 
 class CXFA_FFExclGroup final : public CXFA_FFWidget {
  public:
-  explicit CXFA_FFExclGroup(CXFA_Node* pNode);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFExclGroup() override;
 
   // CXFA_FFWidget
-  void RenderWidget(CXFA_Graphics* pGS,
+  void RenderWidget(CFGAS_GEGraphics* pGS,
                     const CFX_Matrix& matrix,
                     HighlightOption highlight) override;
+
+ private:
+  explicit CXFA_FFExclGroup(CXFA_Node* pNode);
 };
 
 #endif  // XFA_FXFA_CXFA_FFEXCLGROUP_H_
diff --git a/xfa/fxfa/cxfa_fffield.cpp b/xfa/fxfa/cxfa_fffield.cpp
index b031089..ea3f2dc 100644
--- a/xfa/fxfa/cxfa_fffield.cpp
+++ b/xfa/fxfa/cxfa_fffield.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,8 +9,11 @@
 #include <algorithm>
 #include <utility>
 
-#include "core/fxge/render_defines.h"
-#include "third_party/base/ptr_util.h"
+#include "constants/ascii.h"
+#include "third_party/base/check.h"
+#include "xfa/fgas/graphics/cfgas_gecolor.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fwl/cfwl_edit.h"
 #include "xfa/fwl/cfwl_eventmouse.h"
 #include "xfa/fwl/cfwl_messagekey.h"
@@ -34,8 +37,13 @@
 #include "xfa/fxfa/parser/cxfa_margin.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_script.h"
-#include "xfa/fxgraphics/cxfa_gecolor.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
+
+namespace {
+
+constexpr float kMinUIHeight = 4.32f;
+constexpr float kDefaultUIHeight = 2.0f;
+
+}  // namespace
 
 CXFA_FFField::CXFA_FFField(CXFA_Node* pNode) : CXFA_FFWidget(pNode) {}
 
@@ -49,6 +57,11 @@
   return this;
 }
 
+void CXFA_FFField::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FFWidget::Trace(visitor);
+  visitor->Trace(m_pNormalWidget);
+}
+
 CFX_RectF CXFA_FFField::GetBBox(FocusOption focus) {
   if (focus == kDoNotDrawFocus)
     return CXFA_FFWidget::GetBBox(kDoNotDrawFocus);
@@ -59,13 +72,13 @@
     case XFA_FFWidgetType::kImageEdit:
     case XFA_FFWidgetType::kSignature:
     case XFA_FFWidgetType::kChoiceList:
-      return GetRotateMatrix().TransformRect(m_rtUI);
+      return GetRotateMatrix().TransformRect(m_UIRect);
     default:
       return CFX_RectF();
   }
 }
 
-void CXFA_FFField::RenderWidget(CXFA_Graphics* pGS,
+void CXFA_FFField::RenderWidget(CFGAS_GEGraphics* pGS,
                                 const CFX_Matrix& matrix,
                                 HighlightOption highlight) {
   if (!HasVisibleStatus())
@@ -75,9 +88,9 @@
   mtRotate.Concat(matrix);
 
   CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight);
-  DrawBorder(pGS, m_pNode->GetUIBorder(), m_rtUI, mtRotate);
-  RenderCaption(pGS, &mtRotate);
-  DrawHighlight(pGS, &mtRotate, highlight, kSquareShape);
+  DrawBorder(pGS, m_pNode->GetUIBorder(), m_UIRect, mtRotate);
+  RenderCaption(pGS, mtRotate);
+  DrawHighlight(pGS, mtRotate, highlight, kSquareShape);
 
   CFX_RectF rtWidget = GetNormalWidget()->GetWidgetRect();
   CFX_Matrix mt(1, 0, 0, 1, rtWidget.left, rtWidget.top);
@@ -85,59 +98,38 @@
   GetApp()->GetFWLWidgetMgr()->OnDrawWidget(GetNormalWidget(), pGS, mt);
 }
 
-void CXFA_FFField::DrawHighlight(CXFA_Graphics* pGS,
-                                 CFX_Matrix* pMatrix,
+void CXFA_FFField::DrawHighlight(CFGAS_GEGraphics* pGS,
+                                 const CFX_Matrix& pMatrix,
                                  HighlightOption highlight,
                                  ShapeOption shape) {
   if (highlight == kNoHighlight)
     return;
 
-  if (m_rtUI.IsEmpty() || !GetDoc()->GetXFADoc()->IsInteractive() ||
+  if (m_UIRect.IsEmpty() || !GetDoc()->GetXFADoc()->IsInteractive() ||
       !m_pNode->IsOpenAccess()) {
     return;
   }
-  CXFA_FFDoc* pDoc = GetDoc();
-  pGS->SetFillColor(
-      CXFA_GEColor(pDoc->GetDocEnvironment()->GetHighlightColor(pDoc)));
-  CXFA_GEPath path;
+  pGS->SetFillColor(CFGAS_GEColor(GetDoc()->GetHighlightColor()));
+  CFGAS_GEPath path;
   if (shape == kRoundShape)
-    path.AddEllipse(m_rtUI);
+    path.AddEllipse(m_UIRect);
   else
-    path.AddRectangle(m_rtUI.left, m_rtUI.top, m_rtUI.width, m_rtUI.height);
+    path.AddRectangle(m_UIRect.left, m_UIRect.top, m_UIRect.width,
+                      m_UIRect.height);
 
-  pGS->FillPath(&path, FXFILL_WINDING, pMatrix);
-}
-
-void CXFA_FFField::DrawFocus(CXFA_Graphics* pGS, CFX_Matrix* pMatrix) {
-  if (!GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_Focused))
-    return;
-
-  pGS->SetStrokeColor(CXFA_GEColor(0xFF000000));
-
-  static constexpr float kDashPattern[2] = {1, 1};
-  pGS->SetLineDash(0.0f, kDashPattern, FX_ArraySize(kDashPattern));
-  pGS->SetLineWidth(0);
-
-  CXFA_GEPath path;
-  path.AddRectangle(m_rtUI.left, m_rtUI.top, m_rtUI.width, m_rtUI.height);
-  pGS->StrokePath(&path, pMatrix);
-}
-
-void CXFA_FFField::SetFWLThemeProvider() {
-  if (GetNormalWidget())
-    GetNormalWidget()->SetThemeProvider(GetApp()->GetFWLTheme(GetDoc()));
+  pGS->FillPath(path, CFX_FillRenderOptions::FillType::kWinding, pMatrix);
 }
 
 CFWL_Widget* CXFA_FFField::GetNormalWidget() {
-  return m_pNormalWidget.get();
+  return m_pNormalWidget;
 }
 
 const CFWL_Widget* CXFA_FFField::GetNormalWidget() const {
-  return m_pNormalWidget.get();
+  return m_pNormalWidget;
 }
 
-void CXFA_FFField::SetNormalWidget(std::unique_ptr<CFWL_Widget> widget) {
-  m_pNormalWidget = std::move(widget);
+void CXFA_FFField::SetNormalWidget(CFWL_Widget* widget) {
+  m_pNormalWidget = widget;
 }
 
 bool CXFA_FFField::IsLoaded() {
@@ -145,7 +137,6 @@
 }
 
 bool CXFA_FFField::LoadWidget() {
-  SetFWLThemeProvider();
   m_pNode->LoadCaption(GetDoc());
   PerformLayout();
   return true;
@@ -166,7 +157,7 @@
     fScrollOffset = -(m_pNode->GetUIMargin().top);
 
   while (pPrev) {
-    fScrollOffset += pPrev->m_rtUI.height;
+    fScrollOffset += pPrev->m_UIRect.height;
     pItem = pPrev->GetLayoutItem()->GetPrev();
     pPrev = pItem ? ToField(pItem->GetFFWidget()) : nullptr;
   }
@@ -214,7 +205,7 @@
          GetLayoutItem()->GetPrev()) ||
         (iCapPlacement == XFA_AttributeValue::Bottom &&
          GetLayoutItem()->GetNext())) {
-      m_rtCaption = CFX_RectF();
+      m_CaptionRect = CFX_RectF();
     } else {
       fCapReserve = caption->GetReserve();
       if (iCapPlacement == XFA_AttributeValue::Top ||
@@ -225,16 +216,16 @@
       }
       CXFA_ContentLayoutItem* pItem = GetLayoutItem();
       if (!pItem->GetPrev() && !pItem->GetNext()) {
-        m_rtCaption = rtWidget;
+        m_CaptionRect = rtWidget;
       } else {
         pItem = pItem->GetFirst();
-        m_rtCaption = pItem->GetRect(false);
+        m_CaptionRect = pItem->GetAbsoluteRect();
         pItem = pItem->GetNext();
         while (pItem) {
-          m_rtCaption.height += pItem->GetRect(false).Height();
+          m_CaptionRect.height += pItem->GetAbsoluteRect().Height();
           pItem = pItem->GetNext();
         }
-        XFA_RectWithoutMargin(&m_rtCaption, margin);
+        XFA_RectWithoutMargin(&m_CaptionRect, margin);
       }
 
       CXFA_TextLayout* pCapTextLayout = m_pNode->GetCaptionTextLayout();
@@ -252,35 +243,35 @@
     }
   }
 
-  m_rtUI = rtWidget;
+  m_UIRect = rtWidget;
   CXFA_Margin* capMargin = caption ? caption->GetMarginIfExists() : nullptr;
   switch (iCapPlacement) {
     case XFA_AttributeValue::Left: {
-      m_rtCaption.width = fCapReserve;
+      m_CaptionRect.width = fCapReserve;
       CapLeftRightPlacement(capMargin, rtWidget, iCapPlacement);
-      m_rtUI.width -= fCapReserve;
-      m_rtUI.left += fCapReserve;
+      m_UIRect.width -= fCapReserve;
+      m_UIRect.left += fCapReserve;
       break;
     }
     case XFA_AttributeValue::Top: {
-      m_rtCaption.height = fCapReserve;
+      m_CaptionRect.height = fCapReserve;
       CapTopBottomPlacement(capMargin, rtWidget, iCapPlacement);
-      m_rtUI.top += fCapReserve;
-      m_rtUI.height -= fCapReserve;
+      m_UIRect.top += fCapReserve;
+      m_UIRect.height -= fCapReserve;
       break;
     }
     case XFA_AttributeValue::Right: {
-      m_rtCaption.left = m_rtCaption.right() - fCapReserve;
-      m_rtCaption.width = fCapReserve;
+      m_CaptionRect.left = m_CaptionRect.right() - fCapReserve;
+      m_CaptionRect.width = fCapReserve;
       CapLeftRightPlacement(capMargin, rtWidget, iCapPlacement);
-      m_rtUI.width -= fCapReserve;
+      m_UIRect.width -= fCapReserve;
       break;
     }
     case XFA_AttributeValue::Bottom: {
-      m_rtCaption.top = m_rtCaption.bottom() - fCapReserve;
-      m_rtCaption.height = fCapReserve;
+      m_CaptionRect.top = m_CaptionRect.bottom() - fCapReserve;
+      m_CaptionRect.height = fCapReserve;
       CapTopBottomPlacement(capMargin, rtWidget, iCapPlacement);
-      m_rtUI.height -= fCapReserve;
+      m_UIRect.height -= fCapReserve;
       break;
     }
     case XFA_AttributeValue::Inline:
@@ -292,34 +283,34 @@
   CXFA_Border* borderUI = m_pNode->GetUIBorder();
   if (borderUI) {
     CXFA_Margin* borderMargin = borderUI->GetMarginIfExists();
-    XFA_RectWithoutMargin(&m_rtUI, borderMargin);
+    XFA_RectWithoutMargin(&m_UIRect, borderMargin);
   }
-  m_rtUI.Normalize();
+  m_UIRect.Normalize();
 }
 
 void CXFA_FFField::CapTopBottomPlacement(const CXFA_Margin* margin,
                                          const CFX_RectF& rtWidget,
                                          XFA_AttributeValue iCapPlacement) {
   CFX_RectF rtUIMargin = m_pNode->GetUIMargin();
-  m_rtCaption.left += rtUIMargin.left;
+  m_CaptionRect.left += rtUIMargin.left;
   if (margin) {
-    XFA_RectWithoutMargin(&m_rtCaption, margin);
-    if (m_rtCaption.height < 0)
-      m_rtCaption.top += m_rtCaption.height;
+    XFA_RectWithoutMargin(&m_CaptionRect, margin);
+    if (m_CaptionRect.height < 0)
+      m_CaptionRect.top += m_CaptionRect.height;
   }
 
   float fWidth = rtUIMargin.left + rtUIMargin.width;
-  float fHeight = m_rtCaption.height + rtUIMargin.top + rtUIMargin.height;
+  float fHeight = m_CaptionRect.height + rtUIMargin.top + rtUIMargin.height;
   if (fWidth > rtWidget.width)
-    m_rtUI.width += fWidth - rtWidget.width;
+    m_UIRect.width += fWidth - rtWidget.width;
 
-  if (fHeight == XFA_DEFAULTUI_HEIGHT && m_rtUI.height < XFA_MINUI_HEIGHT) {
-    m_rtUI.height = XFA_MINUI_HEIGHT;
-    m_rtCaption.top += rtUIMargin.top + rtUIMargin.height;
+  if (fHeight == kDefaultUIHeight && m_UIRect.height < kMinUIHeight) {
+    m_UIRect.height = kMinUIHeight;
+    m_CaptionRect.top += rtUIMargin.top + rtUIMargin.height;
   } else if (fHeight > rtWidget.height) {
-    m_rtUI.height += fHeight - rtWidget.height;
+    m_UIRect.height += fHeight - rtWidget.height;
     if (iCapPlacement == XFA_AttributeValue::Bottom)
-      m_rtCaption.top += fHeight - rtWidget.height;
+      m_CaptionRect.top += fHeight - rtWidget.height;
   }
 }
 
@@ -327,27 +318,27 @@
                                          const CFX_RectF& rtWidget,
                                          XFA_AttributeValue iCapPlacement) {
   CFX_RectF rtUIMargin = m_pNode->GetUIMargin();
-  m_rtCaption.top += rtUIMargin.top;
-  m_rtCaption.height -= rtUIMargin.top;
+  m_CaptionRect.top += rtUIMargin.top;
+  m_CaptionRect.height -= rtUIMargin.top;
   if (margin) {
-    XFA_RectWithoutMargin(&m_rtCaption, margin);
-    if (m_rtCaption.height < 0)
-      m_rtCaption.top += m_rtCaption.height;
+    XFA_RectWithoutMargin(&m_CaptionRect, margin);
+    if (m_CaptionRect.height < 0)
+      m_CaptionRect.top += m_CaptionRect.height;
   }
 
-  float fWidth = m_rtCaption.width + rtUIMargin.left + rtUIMargin.width;
+  float fWidth = m_CaptionRect.width + rtUIMargin.left + rtUIMargin.width;
   float fHeight = rtUIMargin.top + rtUIMargin.height;
   if (fWidth > rtWidget.width) {
-    m_rtUI.width += fWidth - rtWidget.width;
+    m_UIRect.width += fWidth - rtWidget.width;
     if (iCapPlacement == XFA_AttributeValue::Right)
-      m_rtCaption.left += fWidth - rtWidget.width;
+      m_CaptionRect.left += fWidth - rtWidget.width;
   }
 
-  if (fHeight == XFA_DEFAULTUI_HEIGHT && m_rtUI.height < XFA_MINUI_HEIGHT) {
-    m_rtUI.height = XFA_MINUI_HEIGHT;
-    m_rtCaption.top += rtUIMargin.top + rtUIMargin.height;
+  if (fHeight == kDefaultUIHeight && m_UIRect.height < kMinUIHeight) {
+    m_UIRect.height = kMinUIHeight;
+    m_CaptionRect.top += rtUIMargin.top + rtUIMargin.height;
   } else if (fHeight > rtWidget.height) {
-    m_rtUI.height += fHeight - rtWidget.height;
+    m_UIRect.height += fHeight - rtWidget.height;
   }
 }
 
@@ -367,7 +358,7 @@
   if (!GetNormalWidget())
     return;
 
-  CFX_RectF rtUi = m_rtUI;
+  CFX_RectF rtUi = m_UIRect;
   rtUi.width = std::max(rtUi.width, 1.0f);
   if (!GetDoc()->GetXFADoc()->IsInteractive()) {
     float fFontSize = m_pNode->GetFontSize();
@@ -380,22 +371,22 @@
   if (!GetNormalWidget())
     return false;
 
-  ObservedPtr<CXFA_FFField> pWatched(this);
-  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
-      GetNormalWidget(), FWL_MouseCommand::Enter));
-
-  return !!pWatched;
+  CFWL_MessageMouse msg(GetNormalWidget(),
+                        CFWL_MessageMouse::MouseCommand::kEnter,
+                        Mask<XFA_FWL_KeyFlag>(), CFX_PointF());
+  SendMessageToFWLWidget(&msg);
+  return true;
 }
 
 bool CXFA_FFField::OnMouseExit() {
   if (!GetNormalWidget())
     return false;
 
-  ObservedPtr<CXFA_FFField> pWatched(this);
-  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
-      GetNormalWidget(), FWL_MouseCommand::Leave));
-
-  return !!pWatched;
+  CFWL_MessageMouse msg(GetNormalWidget(),
+                        CFWL_MessageMouse::MouseCommand::kLeave,
+                        Mask<XFA_FWL_KeyFlag>(), CFX_PointF());
+  SendMessageToFWLWidget(&msg);
+  return true;
 }
 
 CFX_PointF CXFA_FFField::FWLToClient(const CFX_PointF& point) {
@@ -404,9 +395,10 @@
              : point;
 }
 
-bool CXFA_FFField::AcceptsFocusOnButtonDown(uint32_t dwFlags,
-                                            const CFX_PointF& point,
-                                            FWL_MouseCommand command) {
+bool CXFA_FFField::AcceptsFocusOnButtonDown(
+    Mask<XFA_FWL_KeyFlag> dwFlags,
+    const CFX_PointF& point,
+    CFWL_MessageMouse::MouseCommand command) {
   if (!GetNormalWidget())
     return false;
   if (!m_pNode->IsOpenAccess() || !GetDoc()->GetXFADoc()->IsInteractive())
@@ -417,102 +409,103 @@
   return true;
 }
 
-bool CXFA_FFField::OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
-  ObservedPtr<CXFA_FFField> pWatched(this);
+bool CXFA_FFField::OnLButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                 const CFX_PointF& point) {
   SetButtonDown(true);
-  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
-      GetNormalWidget(), FWL_MouseCommand::LeftButtonDown, dwFlags,
-      FWLToClient(point)));
-
-  return !!pWatched;
+  CFWL_MessageMouse msg(GetNormalWidget(),
+                        CFWL_MessageMouse::MouseCommand::kLeftButtonDown,
+                        dwFlags, FWLToClient(point));
+  SendMessageToFWLWidget(&msg);
+  return true;
 }
 
-bool CXFA_FFField::OnLButtonUp(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFField::OnLButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                               const CFX_PointF& point) {
   if (!GetNormalWidget())
     return false;
   if (!IsButtonDown())
     return false;
 
-  ObservedPtr<CXFA_FFField> pWatched(this);
   SetButtonDown(false);
-  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
-      GetNormalWidget(), FWL_MouseCommand::LeftButtonUp, dwFlags,
-      FWLToClient(point)));
 
-  return !!pWatched;
+  CFWL_MessageMouse msg(GetNormalWidget(),
+                        CFWL_MessageMouse::MouseCommand::kLeftButtonUp, dwFlags,
+                        FWLToClient(point));
+  SendMessageToFWLWidget(&msg);
+  return true;
 }
 
-bool CXFA_FFField::OnLButtonDblClk(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFField::OnLButtonDblClk(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                   const CFX_PointF& point) {
   if (!GetNormalWidget())
     return false;
 
-  ObservedPtr<CXFA_FFField> pWatched(this);
-  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
-      GetNormalWidget(), FWL_MouseCommand::LeftButtonDblClk, dwFlags,
-      FWLToClient(point)));
-
-  return !!pWatched;
+  CFWL_MessageMouse msg(GetNormalWidget(),
+                        CFWL_MessageMouse::MouseCommand::kLeftButtonDblClk,
+                        dwFlags, FWLToClient(point));
+  SendMessageToFWLWidget(&msg);
+  return true;
 }
 
-bool CXFA_FFField::OnMouseMove(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFField::OnMouseMove(Mask<XFA_FWL_KeyFlag> dwFlags,
+                               const CFX_PointF& point) {
   if (!GetNormalWidget())
     return false;
 
-  ObservedPtr<CXFA_FFField> pWatched(this);
-  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
-      GetNormalWidget(), FWL_MouseCommand::Move, dwFlags, FWLToClient(point)));
-
-  return !!pWatched;
+  CFWL_MessageMouse msg(GetNormalWidget(),
+                        CFWL_MessageMouse::MouseCommand::kMove, dwFlags,
+                        FWLToClient(point));
+  SendMessageToFWLWidget(&msg);
+  return true;
 }
 
-bool CXFA_FFField::OnMouseWheel(uint32_t dwFlags,
-                                int16_t zDelta,
-                                const CFX_PointF& point) {
+bool CXFA_FFField::OnMouseWheel(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                const CFX_PointF& point,
+                                const CFX_Vector& delta) {
   if (!GetNormalWidget())
     return false;
 
-  ObservedPtr<CXFA_FFField> pWatched(this);
-  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouseWheel>(
-      GetNormalWidget(), dwFlags, FWLToClient(point), CFX_PointF(zDelta, 0)));
-
-  return !!pWatched;
+  CFWL_MessageMouseWheel msg(GetNormalWidget(), FWLToClient(point), delta);
+  SendMessageToFWLWidget(&msg);
+  return true;
 }
 
-bool CXFA_FFField::OnRButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
-  ObservedPtr<CXFA_FFField> pWatched(this);
+bool CXFA_FFField::OnRButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                 const CFX_PointF& point) {
   SetButtonDown(true);
-  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
-      GetNormalWidget(), FWL_MouseCommand::RightButtonDown, dwFlags,
-      FWLToClient(point)));
 
-  return !!pWatched;
+  CFWL_MessageMouse msg(GetNormalWidget(),
+                        CFWL_MessageMouse::MouseCommand::kRightButtonDown,
+                        dwFlags, FWLToClient(point));
+  SendMessageToFWLWidget(&msg);
+  return true;
 }
 
-bool CXFA_FFField::OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFField::OnRButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                               const CFX_PointF& point) {
   if (!GetNormalWidget())
     return false;
   if (!IsButtonDown())
     return false;
 
-  ObservedPtr<CXFA_FFField> pWatched(this);
   SetButtonDown(false);
-  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
-      GetNormalWidget(), FWL_MouseCommand::RightButtonUp, dwFlags,
-      FWLToClient(point)));
-
-  return !!pWatched;
+  CFWL_MessageMouse msg(GetNormalWidget(),
+                        CFWL_MessageMouse::MouseCommand::kRightButtonUp,
+                        dwFlags, FWLToClient(point));
+  SendMessageToFWLWidget(&msg);
+  return true;
 }
 
-bool CXFA_FFField::OnRButtonDblClk(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFField::OnRButtonDblClk(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                   const CFX_PointF& point) {
   if (!GetNormalWidget())
     return false;
 
-  ObservedPtr<CXFA_FFField> pWatched(this);
-  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
-      GetNormalWidget(), FWL_MouseCommand::RightButtonDblClk, dwFlags,
-      FWLToClient(point)));
-
-  return !!pWatched;
+  CFWL_MessageMouse msg(GetNormalWidget(),
+                        CFWL_MessageMouse::MouseCommand::kRightButtonDblClk,
+                        dwFlags, FWLToClient(point));
+  SendMessageToFWLWidget(&msg);
+  return true;
 }
 
 bool CXFA_FFField::OnSetFocus(CXFA_FFWidget* pOldWidget) {
@@ -522,65 +515,49 @@
   if (!GetNormalWidget())
     return false;
 
-  ObservedPtr<CXFA_FFField> pWatched(this);
-  SendMessageToFWLWidget(
-      pdfium::MakeUnique<CFWL_MessageSetFocus>(nullptr, GetNormalWidget()));
-  GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_Focused);
+  CFWL_MessageSetFocus msg(GetNormalWidget());
+  SendMessageToFWLWidget(&msg);
+  GetLayoutItem()->SetStatusBits(XFA_WidgetStatus::kFocused);
   InvalidateRect();
 
-  return !!pWatched;
+  return true;
 }
 
 bool CXFA_FFField::OnKillFocus(CXFA_FFWidget* pNewWidget) {
-  ObservedPtr<CXFA_FFField> pWatched(this);
-  ObservedPtr<CXFA_FFWidget> pNewWatched(pNewWidget);
   if (GetNormalWidget()) {
-    SendMessageToFWLWidget(
-        pdfium::MakeUnique<CFWL_MessageKillFocus>(nullptr, GetNormalWidget()));
-    GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus_Focused);
+    CFWL_MessageKillFocus msg(GetNormalWidget());
+    SendMessageToFWLWidget(&msg);
+    GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus::kFocused);
     InvalidateRect();
   }
-  return pWatched && pNewWatched &&
-         CXFA_FFWidget::OnKillFocus(pNewWatched.Get());
+  return pNewWidget && CXFA_FFWidget::OnKillFocus(pNewWidget);
 }
 
-bool CXFA_FFField::OnKeyDown(uint32_t dwKeyCode, uint32_t dwFlags) {
+bool CXFA_FFField::OnKeyDown(XFA_FWL_VKEYCODE dwKeyCode,
+                             Mask<XFA_FWL_KeyFlag> dwFlags) {
   if (!GetNormalWidget() || !GetDoc()->GetXFADoc()->IsInteractive())
     return false;
 
-  ObservedPtr<CXFA_FFField> pWatched(this);
-  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageKey>(
-      GetNormalWidget(), FWL_KeyCommand::KeyDown, dwFlags, dwKeyCode));
-
-  return !!pWatched;
+  CFWL_MessageKey msg(GetNormalWidget(), CFWL_MessageKey::KeyCommand::kKeyDown,
+                      dwFlags, dwKeyCode);
+  SendMessageToFWLWidget(&msg);
+  return true;
 }
 
-bool CXFA_FFField::OnKeyUp(uint32_t dwKeyCode, uint32_t dwFlags) {
-  if (!GetNormalWidget() || !GetDoc()->GetXFADoc()->IsInteractive())
-    return false;
-
-  ObservedPtr<CXFA_FFField> pWatched(this);
-  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageKey>(
-      GetNormalWidget(), FWL_KeyCommand::KeyUp, dwFlags, dwKeyCode));
-
-  return !!pWatched;
-}
-
-bool CXFA_FFField::OnChar(uint32_t dwChar, uint32_t dwFlags) {
+bool CXFA_FFField::OnChar(uint32_t dwChar, Mask<XFA_FWL_KeyFlag> dwFlags) {
   if (!GetDoc()->GetXFADoc()->IsInteractive())
     return false;
-  if (dwChar == XFA_FWL_VKEY_Tab)
+  if (dwChar == pdfium::ascii::kTab)
     return true;
   if (!GetNormalWidget())
     return false;
   if (!m_pNode->IsOpenAccess())
     return false;
 
-  ObservedPtr<CXFA_FFField> pWatched(this);
-  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageKey>(
-      GetNormalWidget(), FWL_KeyCommand::Char, dwFlags, dwChar));
-
-  return !!pWatched;
+  CFWL_MessageKey msg(GetNormalWidget(), CFWL_MessageKey::KeyCommand::kChar,
+                      dwFlags, dwChar);
+  SendMessageToFWLWidget(&msg);
+  return true;
 }
 
 FWL_WidgetHit CXFA_FFField::HitTest(const CFX_PointF& point) {
@@ -589,15 +566,11 @@
     return FWL_WidgetHit::Client;
   if (!GetRectWithoutRotate().Contains(point))
     return FWL_WidgetHit::Unknown;
-  if (m_rtCaption.Contains(point))
+  if (m_CaptionRect.Contains(point))
     return FWL_WidgetHit::Titlebar;
   return FWL_WidgetHit::Border;
 }
 
-bool CXFA_FFField::OnSetCursor(const CFX_PointF& point) {
-  return true;
-}
-
 bool CXFA_FFField::PtInActiveRect(const CFX_PointF& point) {
   return GetNormalWidget() &&
          GetNormalWidget()->GetWidgetRect().Contains(point);
@@ -608,11 +581,12 @@
   if (!pCapTextLayout)
     return;
 
-  float fHeight = pCapTextLayout->Layout(m_rtCaption.Size());
-  m_rtCaption.height = std::max(m_rtCaption.height, fHeight);
+  float fHeight = pCapTextLayout->Layout(m_CaptionRect.Size());
+  m_CaptionRect.height = std::max(m_CaptionRect.height, fHeight);
 }
 
-void CXFA_FFField::RenderCaption(CXFA_Graphics* pGS, CFX_Matrix* pMatrix) {
+void CXFA_FFField::RenderCaption(CFGAS_GEGraphics* pGS,
+                                 const CFX_Matrix& pMatrix) {
   CXFA_TextLayout* pCapTextLayout = m_pNode->GetCaptionTextLayout();
   if (!pCapTextLayout)
     return;
@@ -622,16 +596,14 @@
     return;
 
   if (!pCapTextLayout->IsLoaded())
-    pCapTextLayout->Layout(m_rtCaption.Size());
+    pCapTextLayout->Layout(m_CaptionRect.Size());
 
-  CFX_RectF rtClip = m_rtCaption;
+  CFX_RectF rtClip = m_CaptionRect;
   rtClip.Intersect(GetRectWithoutRotate());
   CFX_RenderDevice* pRenderDevice = pGS->GetRenderDevice();
-  CFX_Matrix mt(1, 0, 0, 1, m_rtCaption.left, m_rtCaption.top);
-  if (pMatrix) {
-    rtClip = pMatrix->TransformRect(rtClip);
-    mt.Concat(*pMatrix);
-  }
+  CFX_Matrix mt(1, 0, 0, 1, m_CaptionRect.left, m_CaptionRect.top);
+  rtClip = pMatrix.TransformRect(rtClip);
+  mt.Concat(pMatrix);
   pCapTextLayout->DrawString(pRenderDevice, mt, rtClip, 0);
 }
 
@@ -682,7 +654,7 @@
       if (version <= XFA_VERSION_204)
         return 1;
 
-      IXFA_AppProvider* pAppProvider = GetAppProvider();
+      CXFA_FFApp::CallbackIface* pAppProvider = GetAppProvider();
       if (pAppProvider) {
         pAppProvider->MsgBox(
             WideString::FromASCII("You are not allowed to modify this field."),
@@ -702,7 +674,7 @@
       if (pNode->IsUserInteractive())
         return 1;
 
-      IXFA_AppProvider* pAppProvider = GetAppProvider();
+      CXFA_FFApp::CallbackIface* pAppProvider = GetAppProvider();
       if (!pAppProvider)
         return 0;
 
@@ -717,7 +689,7 @@
                                static_cast<uint32_t>(AlertIcon::kWarning),
                                static_cast<uint32_t>(AlertButton::kYesNo)) ==
           static_cast<uint32_t>(AlertReturn::kYes)) {
-        pNode->SetFlag(XFA_NodeFlag_UserInteractive);
+        pNode->SetFlag(XFA_NodeFlag::kUserInteractive);
         return 1;
       }
       return 0;
@@ -725,7 +697,7 @@
     case XFA_AttributeValue::Ignore:
       return 0;
     case XFA_AttributeValue::Disabled:
-      pNode->SetFlag(XFA_NodeFlag_UserInteractive);
+      pNode->SetFlag(XFA_NodeFlag::kUserInteractive);
       return 1;
     default:
       return 1;
@@ -740,9 +712,9 @@
   return false;
 }
 
-void CXFA_FFField::SendMessageToFWLWidget(
-    std::unique_ptr<CFWL_Message> pMessage) {
-  GetApp()->GetFWLWidgetMgr()->OnProcessMessageToForm(std::move(pMessage));
+void CXFA_FFField::SendMessageToFWLWidget(CFWL_Message* pMessage) {
+  DCHECK(pMessage);
+  GetApp()->GetFWLWidgetMgr()->OnProcessMessageToForm(pMessage);
 }
 
 void CXFA_FFField::OnProcessMessage(CFWL_Message* pMessage) {}
@@ -751,28 +723,25 @@
   switch (pEvent->GetType()) {
     case CFWL_Event::Type::Mouse: {
       CFWL_EventMouse* event = static_cast<CFWL_EventMouse*>(pEvent);
-      if (event->m_dwCmd == FWL_MouseCommand::Enter) {
+      CFWL_MessageMouse::MouseCommand cmd = event->GetCommand();
+      if (cmd == CFWL_MessageMouse::MouseCommand::kEnter) {
         CXFA_EventParam eParam;
         eParam.m_eType = XFA_EVENT_MouseEnter;
-        eParam.m_pTarget = m_pNode.Get();
         m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::MouseEnter,
                               &eParam);
-      } else if (event->m_dwCmd == FWL_MouseCommand::Leave) {
+      } else if (cmd == CFWL_MessageMouse::MouseCommand::kLeave) {
         CXFA_EventParam eParam;
         eParam.m_eType = XFA_EVENT_MouseExit;
-        eParam.m_pTarget = m_pNode.Get();
         m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::MouseExit,
                               &eParam);
-      } else if (event->m_dwCmd == FWL_MouseCommand::LeftButtonDown) {
+      } else if (cmd == CFWL_MessageMouse::MouseCommand::kLeftButtonDown) {
         CXFA_EventParam eParam;
         eParam.m_eType = XFA_EVENT_MouseDown;
-        eParam.m_pTarget = m_pNode.Get();
         m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::MouseDown,
                               &eParam);
-      } else if (event->m_dwCmd == FWL_MouseCommand::LeftButtonUp) {
+      } else if (cmd == CFWL_MessageMouse::MouseCommand::kLeftButtonUp) {
         CXFA_EventParam eParam;
         eParam.m_eType = XFA_EVENT_MouseUp;
-        eParam.m_pTarget = m_pNode.Get();
         m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::MouseUp,
                               &eParam);
       }
@@ -781,7 +750,6 @@
     case CFWL_Event::Type::Click: {
       CXFA_EventParam eParam;
       eParam.m_eType = XFA_EVENT_Click;
-      eParam.m_pTarget = m_pNode.Get();
       m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Click, &eParam);
       break;
     }
@@ -790,5 +758,5 @@
   }
 }
 
-void CXFA_FFField::OnDrawWidget(CXFA_Graphics* pGraphics,
+void CXFA_FFField::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                                 const CFX_Matrix& matrix) {}
diff --git a/xfa/fxfa/cxfa_fffield.h b/xfa/fxfa/cxfa_fffield.h
index dd7b914..bcd3112 100644
--- a/xfa/fxfa/cxfa_fffield.h
+++ b/xfa/fxfa/cxfa_fffield.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,6 @@
 #ifndef XFA_FXFA_CXFA_FFFIELD_H_
 #define XFA_FXFA_CXFA_FFFIELD_H_
 
-#include <memory>
-
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fwl/ifwl_widgetdelegate.h"
 #include "xfa/fxfa/cxfa_ffpageview.h"
@@ -17,82 +15,88 @@
 class CXFA_FFDropDown;
 class CXFA_Node;
 
-#define XFA_MINUI_HEIGHT 4.32f
-#define XFA_DEFAULTUI_HEIGHT 2.0f
-
 class CXFA_FFField : public CXFA_FFWidget, public IFWL_WidgetDelegate {
  public:
   enum ShapeOption { kSquareShape = 0, kRoundShape };
 
-  explicit CXFA_FFField(CXFA_Node* pNode);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFField() override;
 
   virtual CXFA_FFDropDown* AsDropDown();
 
   // CXFA_FFWidget:
+  void Trace(cppgc::Visitor* visitor) const override;
   CXFA_FFField* AsField() override;
   CFX_RectF GetBBox(FocusOption focus) override;
-  void RenderWidget(CXFA_Graphics* pGS,
+  void RenderWidget(CFGAS_GEGraphics* pGS,
                     const CFX_Matrix& matrix,
                     HighlightOption highlight) override;
   bool IsLoaded() override;
   bool LoadWidget() override;
   bool PerformLayout() override;
-  bool AcceptsFocusOnButtonDown(uint32_t dwFlags,
-                                const CFX_PointF& point,
-                                FWL_MouseCommand command) override;
+  bool AcceptsFocusOnButtonDown(
+      Mask<XFA_FWL_KeyFlag> dwFlags,
+      const CFX_PointF& point,
+      CFWL_MessageMouse::MouseCommand command) override;
   bool OnMouseEnter() override;
   bool OnMouseExit() override;
-  bool OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnLButtonUp(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnLButtonDblClk(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnMouseMove(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnMouseWheel(uint32_t dwFlags,
-                    int16_t zDelta,
-                    const CFX_PointF& point) override;
-  bool OnRButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnRButtonDblClk(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnSetFocus(CXFA_FFWidget* pOldWidget) override WARN_UNUSED_RESULT;
-  bool OnKillFocus(CXFA_FFWidget* pNewWidget) override WARN_UNUSED_RESULT;
-  bool OnKeyDown(uint32_t dwKeyCode, uint32_t dwFlags) override;
-  bool OnKeyUp(uint32_t dwKeyCode, uint32_t dwFlags) override;
-  bool OnChar(uint32_t dwChar, uint32_t dwFlags) override;
-  bool OnSetCursor(const CFX_PointF& point) override;
+  bool OnLButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
+                     const CFX_PointF& point) override;
+  bool OnLButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                   const CFX_PointF& point) override;
+  bool OnLButtonDblClk(Mask<XFA_FWL_KeyFlag> dwFlags,
+                       const CFX_PointF& point) override;
+  bool OnMouseMove(Mask<XFA_FWL_KeyFlag> dwFlags,
+                   const CFX_PointF& point) override;
+  bool OnMouseWheel(Mask<XFA_FWL_KeyFlag> dwFlags,
+                    const CFX_PointF& point,
+                    const CFX_Vector& delta) override;
+  bool OnRButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
+                     const CFX_PointF& point) override;
+  bool OnRButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                   const CFX_PointF& point) override;
+  bool OnRButtonDblClk(Mask<XFA_FWL_KeyFlag> dwFlags,
+                       const CFX_PointF& point) override;
+  [[nodiscard]] bool OnSetFocus(CXFA_FFWidget* pOldWidget) override;
+  [[nodiscard]] bool OnKillFocus(CXFA_FFWidget* pNewWidget) override;
+  bool OnKeyDown(XFA_FWL_VKEYCODE dwKeyCode,
+                 Mask<XFA_FWL_KeyFlag> dwFlags) override;
+  bool OnChar(uint32_t dwChar, Mask<XFA_FWL_KeyFlag> dwFlags) override;
   FWL_WidgetHit HitTest(const CFX_PointF& point) override;
 
   // IFWL_WidgetDelegate:
   void OnProcessMessage(CFWL_Message* pMessage) override;
   void OnProcessEvent(CFWL_Event* pEvent) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
+  void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix) override;
 
   void UpdateFWL();
   uint32_t UpdateUIProperty();
 
  protected:
+  explicit CXFA_FFField(CXFA_Node* pNode);
+
   bool PtInActiveRect(const CFX_PointF& point) override;
 
   virtual void SetFWLRect();
-  void SetFWLThemeProvider();
+  virtual bool CommitData();
+  virtual bool IsDataChanged();
+
   CFWL_Widget* GetNormalWidget();
   const CFWL_Widget* GetNormalWidget() const;
-  void SetNormalWidget(std::unique_ptr<CFWL_Widget> widget);
+  void SetNormalWidget(CFWL_Widget* widget);
   CFX_PointF FWLToClient(const CFX_PointF& point);
   void LayoutCaption();
-  void RenderCaption(CXFA_Graphics* pGS, CFX_Matrix* pMatrix);
+  void RenderCaption(CFGAS_GEGraphics* pGS, const CFX_Matrix& pMatrix);
 
   int32_t CalculateOverride();
   int32_t CalculateNode(CXFA_Node* pNode);
   bool ProcessCommittedData();
-  virtual bool CommitData();
-  virtual bool IsDataChanged();
-  void DrawHighlight(CXFA_Graphics* pGS,
-                     CFX_Matrix* pMatrix,
+  void DrawHighlight(CFGAS_GEGraphics* pGS,
+                     const CFX_Matrix& pMatrix,
                      HighlightOption highlight,
                      ShapeOption shape);
-  void DrawFocus(CXFA_Graphics* pGS, CFX_Matrix* pMatrix);
-  void SendMessageToFWLWidget(std::unique_ptr<CFWL_Message> pMessage);
+  void SendMessageToFWLWidget(CFWL_Message* pMessage);
   void CapPlacement();
   void CapTopBottomPlacement(const CXFA_Margin* margin,
                              const CFX_RectF& rtWidget,
@@ -102,11 +106,11 @@
                              XFA_AttributeValue iCapPlacement);
   void SetEditScrollOffset();
 
-  CFX_RectF m_rtUI;
-  CFX_RectF m_rtCaption;
+  CFX_RectF m_UIRect;
+  CFX_RectF m_CaptionRect;
 
  private:
-  std::unique_ptr<CFWL_Widget> m_pNormalWidget;
+  cppgc::Member<CFWL_Widget> m_pNormalWidget;
 };
 
 inline CXFA_FFDropDown* ToDropDown(CXFA_FFField* field) {
diff --git a/xfa/fxfa/cxfa_ffimage.cpp b/xfa/fxfa/cxfa_ffimage.cpp
index eeb0e24..bc1a67f 100644
--- a/xfa/fxfa/cxfa_ffimage.cpp
+++ b/xfa/fxfa/cxfa_ffimage.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include "xfa/fxfa/cxfa_ffimage.h"
 
+#include <utility>
+
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
@@ -17,22 +19,24 @@
 
 CXFA_FFImage::CXFA_FFImage(CXFA_Node* pNode) : CXFA_FFWidget(pNode) {}
 
-CXFA_FFImage::~CXFA_FFImage() {
-  GetNode()->SetImageImage(nullptr);
+CXFA_FFImage::~CXFA_FFImage() = default;
+
+void CXFA_FFImage::PreFinalize() {
+  GetNode()->SetLayoutImage(nullptr);
 }
 
 bool CXFA_FFImage::IsLoaded() {
-  return !!GetNode()->GetImageImage();
+  return !!GetNode()->GetLayoutImage();
 }
 
 bool CXFA_FFImage::LoadWidget() {
-  if (GetNode()->GetImageImage())
+  if (GetNode()->GetLayoutImage())
     return true;
 
-  return GetNode()->LoadImageImage(GetDoc()) && CXFA_FFWidget::LoadWidget();
+  return GetNode()->LoadLayoutImage(GetDoc()) && CXFA_FFWidget::LoadWidget();
 }
 
-void CXFA_FFImage::RenderWidget(CXFA_Graphics* pGS,
+void CXFA_FFImage::RenderWidget(CFGAS_GEGraphics* pGS,
                                 const CFX_Matrix& matrix,
                                 HighlightOption highlight) {
   if (!HasVisibleStatus())
@@ -43,7 +47,7 @@
 
   CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight);
 
-  RetainPtr<CFX_DIBitmap> pDIBitmap = GetNode()->GetImageImage();
+  RetainPtr<CFX_DIBitmap> pDIBitmap = GetNode()->GetLayoutImage();
   if (!pDIBitmap)
     return;
 
@@ -60,9 +64,14 @@
   }
 
   auto* value = m_pNode->GetFormValueIfExists();
-  CXFA_Image* image = value ? value->GetImageIfExists() : nullptr;
-  if (image) {
-    XFA_DrawImage(pGS, rtImage, mtRotate, pDIBitmap, image->GetAspect(),
-                  m_pNode->GetImageDpi(), iHorzAlign, iVertAlign);
-  }
+  if (!value)
+    return;
+
+  CXFA_Image* image = value->GetImageIfExists();
+  if (!image)
+    return;
+
+  XFA_DrawImage(pGS, rtImage, mtRotate, std::move(pDIBitmap),
+                image->GetAspect(), m_pNode->GetLayoutImageDpi(), iHorzAlign,
+                iVertAlign);
 }
diff --git a/xfa/fxfa/cxfa_ffimage.h b/xfa/fxfa/cxfa_ffimage.h
index 5d23f60..f007862 100644
--- a/xfa/fxfa/cxfa_ffimage.h
+++ b/xfa/fxfa/cxfa_ffimage.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,27 @@
 #ifndef XFA_FXFA_CXFA_FFIMAGE_H_
 #define XFA_FXFA_CXFA_FFIMAGE_H_
 
+#include "v8/include/cppgc/prefinalizer.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
 
 class CXFA_FFImage final : public CXFA_FFWidget {
+  CPPGC_USING_PRE_FINALIZER(CXFA_FFImage, PreFinalize);
+
  public:
-  explicit CXFA_FFImage(CXFA_Node* pNode);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFImage() override;
 
-  // CXFA_FFWidget
-  void RenderWidget(CXFA_Graphics* pGS,
+  void PreFinalize();
+
+  // CXFA_FFWidget:
+  void RenderWidget(CFGAS_GEGraphics* pGS,
                     const CFX_Matrix& matrix,
                     HighlightOption highlight) override;
   bool IsLoaded() override;
   bool LoadWidget() override;
+
+ private:
+  explicit CXFA_FFImage(CXFA_Node* pNode);
 };
 
 #endif  // XFA_FXFA_CXFA_FFIMAGE_H_
diff --git a/xfa/fxfa/cxfa_ffimageedit.cpp b/xfa/fxfa/cxfa_ffimageedit.cpp
index 60f584a..a036be8 100644
--- a/xfa/fxfa/cxfa_ffimageedit.cpp
+++ b/xfa/fxfa/cxfa_ffimageedit.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,8 @@
 #include <utility>
 
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
 #include "xfa/fwl/cfwl_notedriver.h"
@@ -26,30 +27,38 @@
 
 CXFA_FFImageEdit::CXFA_FFImageEdit(CXFA_Node* pNode) : CXFA_FFField(pNode) {}
 
-CXFA_FFImageEdit::~CXFA_FFImageEdit() {
-  m_pNode->SetImageEditImage(nullptr);
+CXFA_FFImageEdit::~CXFA_FFImageEdit() = default;
+
+void CXFA_FFImageEdit::PreFinalize() {
+  m_pNode->SetEditImage(nullptr);
+}
+
+void CXFA_FFImageEdit::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FFField::Trace(visitor);
+  visitor->Trace(m_pOldDelegate);
 }
 
 bool CXFA_FFImageEdit::LoadWidget() {
-  ASSERT(!IsLoaded());
-  auto pNew = pdfium::MakeUnique<CFWL_PictureBox>(GetFWLApp());
-  CFWL_PictureBox* pPictureBox = pNew.get();
-  SetNormalWidget(std::move(pNew));
+  DCHECK(!IsLoaded());
+
+  CFWL_PictureBox* pPictureBox = cppgc::MakeGarbageCollected<CFWL_PictureBox>(
+      GetFWLApp()->GetHeap()->GetAllocationHandle(), GetFWLApp());
+  SetNormalWidget(pPictureBox);
   pPictureBox->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver = pPictureBox->GetOwnerApp()->GetNoteDriver();
+  CFWL_NoteDriver* pNoteDriver = pPictureBox->GetFWLApp()->GetNoteDriver();
   pNoteDriver->RegisterEventTarget(pPictureBox, pPictureBox);
   m_pOldDelegate = pPictureBox->GetDelegate();
   pPictureBox->SetDelegate(this);
 
   CXFA_FFField::LoadWidget();
-  if (!m_pNode->GetImageEditImage())
+  if (!m_pNode->GetEditImage())
     UpdateFWLData();
 
   return true;
 }
 
-void CXFA_FFImageEdit::RenderWidget(CXFA_Graphics* pGS,
+void CXFA_FFImageEdit::RenderWidget(CFGAS_GEGraphics* pGS,
                                     const CFX_Matrix& matrix,
                                     HighlightOption highlight) {
   if (!HasVisibleStatus())
@@ -59,9 +68,9 @@
   mtRotate.Concat(matrix);
 
   CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight);
-  DrawBorder(pGS, m_pNode->GetUIBorder(), m_rtUI, mtRotate);
-  RenderCaption(pGS, &mtRotate);
-  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pNode->GetImageEditImage();
+  DrawBorder(pGS, m_pNode->GetUIBorder(), m_UIRect, mtRotate);
+  RenderCaption(pGS, mtRotate);
+  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pNode->GetEditImage();
   if (!pDIBitmap)
     return;
 
@@ -82,14 +91,15 @@
       iAspect = image->GetAspect();
   }
 
-  XFA_DrawImage(pGS, rtImage, mtRotate, pDIBitmap, iAspect,
-                m_pNode->GetImageEditDpi(), iHorzAlign, iVertAlign);
+  XFA_DrawImage(pGS, rtImage, mtRotate, std::move(pDIBitmap), iAspect,
+                m_pNode->GetEditImageDpi(), iHorzAlign, iVertAlign);
 }
 
-bool CXFA_FFImageEdit::AcceptsFocusOnButtonDown(uint32_t dwFlags,
-                                                const CFX_PointF& point,
-                                                FWL_MouseCommand command) {
-  if (command != FWL_MouseCommand::LeftButtonDown)
+bool CXFA_FFImageEdit::AcceptsFocusOnButtonDown(
+    Mask<XFA_FWL_KeyFlag> dwFlags,
+    const CFX_PointF& point,
+    CFWL_MessageMouse::MouseCommand command) {
+  if (command != CFWL_MessageMouse::MouseCommand::kLeftButtonDown)
     return CXFA_FFField::AcceptsFocusOnButtonDown(dwFlags, point, command);
 
   if (!m_pNode->IsOpenAccess())
@@ -100,15 +110,14 @@
   return true;
 }
 
-bool CXFA_FFImageEdit::OnLButtonDown(uint32_t dwFlags,
+bool CXFA_FFImageEdit::OnLButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
                                      const CFX_PointF& point) {
-  ObservedPtr<CXFA_FFImageEdit> pWatched(this);
   SetButtonDown(true);
-  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
-      GetNormalWidget(), FWL_MouseCommand::LeftButtonDown, dwFlags,
-      FWLToClient(point)));
-
-  return !!pWatched;
+  CFWL_MessageMouse msg(GetNormalWidget(),
+                        CFWL_MessageMouse::MouseCommand::kLeftButtonDown,
+                        dwFlags, FWLToClient(point));
+  SendMessageToFWLWidget(&msg);
+  return true;
 }
 
 void CXFA_FFImageEdit::SetFWLRect() {
@@ -116,7 +125,7 @@
     return;
 
   CFX_RectF rtUIMargin = m_pNode->GetUIMargin();
-  CFX_RectF rtImage(m_rtUI);
+  CFX_RectF rtImage(m_UIRect);
   rtImage.Deflate(rtUIMargin.left, rtUIMargin.top, rtUIMargin.width,
                   rtUIMargin.height);
   GetNormalWidget()->SetWidgetRect(rtImage);
@@ -127,8 +136,8 @@
 }
 
 bool CXFA_FFImageEdit::UpdateFWLData() {
-  m_pNode->SetImageEditImage(nullptr);
-  m_pNode->LoadImageEditImage(GetDoc());
+  m_pNode->SetEditImage(nullptr);
+  m_pNode->LoadEditImage(GetDoc());
   return true;
 }
 
@@ -141,7 +150,7 @@
   m_pOldDelegate->OnProcessEvent(pEvent);
 }
 
-void CXFA_FFImageEdit::OnDrawWidget(CXFA_Graphics* pGraphics,
+void CXFA_FFImageEdit::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                                     const CFX_Matrix& matrix) {
   m_pOldDelegate->OnDrawWidget(pGraphics, matrix);
 }
diff --git a/xfa/fxfa/cxfa_ffimageedit.h b/xfa/fxfa/cxfa_ffimageedit.h
index f1233b3..c393f8f 100644
--- a/xfa/fxfa/cxfa_ffimageedit.h
+++ b/xfa/fxfa/cxfa_ffimageedit.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,35 +7,45 @@
 #ifndef XFA_FXFA_CXFA_FFIMAGEEDIT_H_
 #define XFA_FXFA_CXFA_FFIMAGEEDIT_H_
 
-#include "core/fxcrt/unowned_ptr.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/prefinalizer.h"
 #include "xfa/fxfa/cxfa_fffield.h"
 
 class CXFA_FFImageEdit final : public CXFA_FFField {
+  CPPGC_USING_PRE_FINALIZER(CXFA_FFImageEdit, PreFinalize);
+
  public:
-  explicit CXFA_FFImageEdit(CXFA_Node* pNode);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFImageEdit() override;
 
-  // CXFA_FFField
-  void RenderWidget(CXFA_Graphics* pGS,
+  void PreFinalize();
+
+  // CXFA_FFField:
+  void Trace(cppgc::Visitor* visitor) const override;
+  void RenderWidget(CFGAS_GEGraphics* pGS,
                     const CFX_Matrix& matrix,
                     HighlightOption highlight) override;
   bool LoadWidget() override;
-  bool AcceptsFocusOnButtonDown(uint32_t dwFlags,
-                                const CFX_PointF& point,
-                                FWL_MouseCommand command) override;
-  bool OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
+  bool AcceptsFocusOnButtonDown(
+      Mask<XFA_FWL_KeyFlag> dwFlags,
+      const CFX_PointF& point,
+      CFWL_MessageMouse::MouseCommand command) override;
+  bool OnLButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
+                     const CFX_PointF& point) override;
   void OnProcessMessage(CFWL_Message* pMessage) override;
   void OnProcessEvent(CFWL_Event* pEvent) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
+  void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix) override;
   FormFieldType GetFormFieldType() override;
 
  private:
+  explicit CXFA_FFImageEdit(CXFA_Node* pNode);
+
   void SetFWLRect() override;
   bool UpdateFWLData() override;
   bool CommitData() override;
 
-  UnownedPtr<IFWL_WidgetDelegate> m_pOldDelegate;
+  cppgc::Member<IFWL_WidgetDelegate> m_pOldDelegate;
 };
 
 #endif  // XFA_FXFA_CXFA_FFIMAGEEDIT_H_
diff --git a/xfa/fxfa/cxfa_ffline.cpp b/xfa/fxfa/cxfa_ffline.cpp
index ec51040..47ebc2e 100644
--- a/xfa/fxfa/cxfa_ffline.cpp
+++ b/xfa/fxfa/cxfa_ffline.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,32 +6,32 @@
 
 #include "xfa/fxfa/cxfa_ffline.h"
 
+#include "third_party/base/notreached.h"
+#include "xfa/fgas/graphics/cfgas_gecolor.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fxfa/parser/cxfa_edge.h"
 #include "xfa/fxfa/parser/cxfa_line.h"
 #include "xfa/fxfa/parser/cxfa_value.h"
-#include "xfa/fxgraphics/cxfa_gecolor.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
-#include "xfa/fxgraphics/cxfa_graphics.h"
 
 namespace {
 
 CFX_GraphStateData::LineCap LineCapToFXGE(XFA_AttributeValue iLineCap) {
   switch (iLineCap) {
     case XFA_AttributeValue::Round:
-      return CFX_GraphStateData::LineCapRound;
+      return CFX_GraphStateData::LineCap::kRound;
     case XFA_AttributeValue::Butt:
-      return CFX_GraphStateData::LineCapButt;
+      return CFX_GraphStateData::LineCap::kButt;
     default:
-      break;
+      return CFX_GraphStateData::LineCap::kSquare;
   }
-  return CFX_GraphStateData::LineCapSquare;
 }
 
 }  // namespace
 
 CXFA_FFLine::CXFA_FFLine(CXFA_Node* pNode) : CXFA_FFWidget(pNode) {}
 
-CXFA_FFLine::~CXFA_FFLine() {}
+CXFA_FFLine::~CXFA_FFLine() = default;
 
 void CXFA_FFLine::GetRectFromHand(CFX_RectF& rect,
                                   XFA_AttributeValue iHand,
@@ -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,13 +74,12 @@
       case XFA_AttributeValue::Even:
         break;
       default:
-        NOTREACHED();
-        break;
+        NOTREACHED_NORETURN();
     }
   }
 }
 
-void CXFA_FFLine::RenderWidget(CXFA_Graphics* pGS,
+void CXFA_FFLine::RenderWidget(CFGAS_GEGraphics* pGS,
                                const CFX_Matrix& matrix,
                                HighlightOption highlight) {
   if (!HasVisibleStatus())
@@ -119,7 +116,7 @@
 
   GetRectFromHand(rtLine, line ? line->GetHand() : XFA_AttributeValue::Left,
                   fLineWidth);
-  CXFA_GEPath linePath;
+  CFGAS_GEPath linePath;
   if (line && line->GetSlope() && rtLine.right() > 0.0f &&
       rtLine.bottom() > 0.0f) {
     linePath.AddLine(rtLine.TopRight(), rtLine.BottomLeft());
@@ -127,13 +124,12 @@
     linePath.AddLine(rtLine.TopLeft(), rtLine.BottomRight());
   }
 
-  pGS->SaveGraphState();
+  CFGAS_GEGraphics::StateRestorer restorer(pGS);
   pGS->SetLineWidth(fLineWidth);
   pGS->EnableActOnDash();
   XFA_StrokeTypeSetLineDash(pGS, iStrokeType, iCap);
 
-  pGS->SetStrokeColor(CXFA_GEColor(lineColor));
+  pGS->SetStrokeColor(CFGAS_GEColor(lineColor));
   pGS->SetLineCap(LineCapToFXGE(iCap));
-  pGS->StrokePath(&linePath, &mtRotate);
-  pGS->RestoreGraphState();
+  pGS->StrokePath(linePath, mtRotate);
 }
diff --git a/xfa/fxfa/cxfa_ffline.h b/xfa/fxfa/cxfa_ffline.h
index 62750d1..739b9b1 100644
--- a/xfa/fxfa/cxfa_ffline.h
+++ b/xfa/fxfa/cxfa_ffline.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,15 +11,17 @@
 
 class CXFA_FFLine final : public CXFA_FFWidget {
  public:
-  explicit CXFA_FFLine(CXFA_Node* pNode);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFLine() override;
 
   // CXFA_FFWidget
-  void RenderWidget(CXFA_Graphics* pGS,
+  void RenderWidget(CFGAS_GEGraphics* pGS,
                     const CFX_Matrix& matrix,
                     HighlightOption highlight) override;
 
  private:
+  explicit CXFA_FFLine(CXFA_Node* pNode);
+
   void GetRectFromHand(CFX_RectF& rect,
                        XFA_AttributeValue iHand,
                        float fLineWidth);
diff --git a/xfa/fxfa/cxfa_fflistbox.cpp b/xfa/fxfa/cxfa_fflistbox.cpp
index 45162ee..69ca03b 100644
--- a/xfa/fxfa/cxfa_fflistbox.cpp
+++ b/xfa/fxfa/cxfa_fflistbox.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,8 +10,9 @@
 #include <utility>
 #include <vector>
 
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/check.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fwl/cfwl_listbox.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fwl/cfwl_widget.h"
@@ -28,33 +29,45 @@
 
 CXFA_FFListBox::CXFA_FFListBox(CXFA_Node* pNode) : CXFA_FFDropDown(pNode) {}
 
-CXFA_FFListBox::~CXFA_FFListBox() {
-  if (!GetNormalWidget())
-    return;
+CXFA_FFListBox::~CXFA_FFListBox() = default;
 
-  CFWL_NoteDriver* pNoteDriver =
-      GetNormalWidget()->GetOwnerApp()->GetNoteDriver();
-  pNoteDriver->UnregisterEventTarget(GetNormalWidget());
+void CXFA_FFListBox::PreFinalize() {
+  if (GetNormalWidget()) {
+    CFWL_NoteDriver* pNoteDriver =
+        GetNormalWidget()->GetFWLApp()->GetNoteDriver();
+    pNoteDriver->UnregisterEventTarget(GetNormalWidget());
+  }
+}
+
+void CXFA_FFListBox::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FFDropDown::Trace(visitor);
+  visitor->Trace(m_pOldDelegate);
 }
 
 bool CXFA_FFListBox::LoadWidget() {
-  ASSERT(!IsLoaded());
-  auto pNew = pdfium::MakeUnique<CFWL_ListBox>(
-      GetFWLApp(), pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr);
-  CFWL_ListBox* pListBox = pNew.get();
-  pListBox->ModifyStyles(FWL_WGTSTYLE_VScroll | FWL_WGTSTYLE_NoBackground,
+  DCHECK(!IsLoaded());
+
+  CFWL_ListBox* pListBox = cppgc::MakeGarbageCollected<CFWL_ListBox>(
+      GetFWLApp()->GetHeap()->GetAllocationHandle(), GetFWLApp(),
+      CFWL_Widget::Properties(), nullptr);
+  pListBox->ModifyStyles(FWL_STYLE_WGT_VScroll | FWL_STYLE_WGT_NoBackground,
                          0xFFFFFFFF);
-  SetNormalWidget(std::move(pNew));
+  SetNormalWidget(pListBox);
   pListBox->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver = pListBox->GetOwnerApp()->GetNoteDriver();
+  CFWL_NoteDriver* pNoteDriver = pListBox->GetFWLApp()->GetNoteDriver();
   pNoteDriver->RegisterEventTarget(pListBox, pListBox);
   m_pOldDelegate = pListBox->GetDelegate();
   pListBox->SetDelegate(this);
 
   {
     CFWL_Widget::ScopedUpdateLock update_lock(pListBox);
-    for (const auto& label : m_pNode->GetChoiceListItems(false))
+    std::vector<WideString> displayables = m_pNode->GetChoiceListItems(false);
+    std::vector<WideString> settables = m_pNode->GetChoiceListItems(true);
+    if (displayables.size() > settables.size())
+      displayables.resize(settables.size());
+
+    for (const auto& label : displayables)
       pListBox->AddString(label);
 
     uint32_t dwExtendedStyle = FWL_STYLEEXT_LTB_ShowScrollBarFocus;
@@ -62,7 +75,7 @@
       dwExtendedStyle |= FWL_STYLEEXT_LTB_MultiSelection;
 
     dwExtendedStyle |= GetAlignment();
-    pListBox->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
+    pListBox->ModifyStyleExts(dwExtendedStyle, 0xFFFFFFFF);
     for (int32_t selected : m_pNode->GetSelectedItems())
       pListBox->SetSelItem(pListBox->GetItem(nullptr, selected), true);
   }
@@ -71,13 +84,10 @@
 }
 
 bool CXFA_FFListBox::OnKillFocus(CXFA_FFWidget* pNewFocus) {
-  ObservedPtr<CXFA_FFListBox> pWatched(this);
-  ObservedPtr<CXFA_FFWidget> pNewWatched(pNewFocus);
   if (!ProcessCommittedData())
     UpdateFWLData();
 
-  return pWatched && pNewWatched &&
-         CXFA_FFField::OnKillFocus(pNewWatched.Get());
+  return pNewFocus && CXFA_FFField::OnKillFocus(pNewFocus);
 }
 
 bool CXFA_FFListBox::CommitData() {
@@ -93,15 +103,15 @@
 
 bool CXFA_FFListBox::IsDataChanged() {
   std::vector<int32_t> iSelArray = m_pNode->GetSelectedItems();
-  int32_t iOldSels = pdfium::CollectionSize<int32_t>(iSelArray);
+  int32_t iOldSels = fxcrt::CollectionSize<int32_t>(iSelArray);
   auto* pListBox = ToListBox(GetNormalWidget());
   int32_t iSels = pListBox->CountSelItems();
   if (iOldSels != iSels)
     return true;
 
   for (int32_t i = 0; i < iSels; ++i) {
-    CFWL_ListItem* hlistItem = pListBox->GetItem(nullptr, iSelArray[i]);
-    if (!(hlistItem->GetStates() & FWL_ITEMSTATE_LTB_Selected))
+    CFWL_ListBox::Item* hlistItem = pListBox->GetItem(nullptr, iSelArray[i]);
+    if (!hlistItem->IsSelected())
       return true;
   }
   return false;
@@ -134,17 +144,17 @@
 }
 
 bool CXFA_FFListBox::UpdateFWLData() {
-  if (!GetNormalWidget())
+  auto* pListBox = ToListBox(GetNormalWidget());
+  if (!pListBox)
     return false;
 
-  auto* pListBox = ToListBox(GetNormalWidget());
   std::vector<int32_t> iSelArray = m_pNode->GetSelectedItems();
-  std::vector<CFWL_ListItem*> selItemArray(iSelArray.size());
+  std::vector<CFWL_ListBox::Item*> selItemArray(iSelArray.size());
   std::transform(iSelArray.begin(), iSelArray.end(), selItemArray.begin(),
                  [pListBox](int32_t val) { return pListBox->GetSelItem(val); });
 
   pListBox->SetSelItem(pListBox->GetSelItem(-1), false);
-  for (CFWL_ListItem* pItem : selItemArray)
+  for (CFWL_ListBox::Item* pItem : selItemArray)
     pListBox->SetSelItem(pItem, true);
 
   GetNormalWidget()->Update();
@@ -154,8 +164,7 @@
 void CXFA_FFListBox::OnSelectChanged(CFWL_Widget* pWidget) {
   CXFA_EventParam eParam;
   eParam.m_eType = XFA_EVENT_Change;
-  eParam.m_pTarget = m_pNode.Get();
-  eParam.m_wsPrevText = m_pNode->GetValue(XFA_VALUEPICTURE_Raw);
+  eParam.m_wsPrevText = m_pNode->GetValue(XFA_ValuePicture::kRaw);
   m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change, &eParam);
 }
 
@@ -199,7 +208,7 @@
   m_pOldDelegate->OnProcessEvent(pEvent);
 }
 
-void CXFA_FFListBox::OnDrawWidget(CXFA_Graphics* pGraphics,
+void CXFA_FFListBox::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                                   const CFX_Matrix& matrix) {
   m_pOldDelegate->OnDrawWidget(pGraphics, matrix);
 }
diff --git a/xfa/fxfa/cxfa_fflistbox.h b/xfa/fxfa/cxfa_fflistbox.h
index 896ee61..fae01ae 100644
--- a/xfa/fxfa/cxfa_fflistbox.h
+++ b/xfa/fxfa/cxfa_fflistbox.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,24 +7,30 @@
 #ifndef XFA_FXFA_CXFA_FFLISTBOX_H_
 #define XFA_FXFA_CXFA_FFLISTBOX_H_
 
-#include "core/fxcrt/unowned_ptr.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/prefinalizer.h"
 #include "xfa/fxfa/cxfa_ffdropdown.h"
 
 class CXFA_FFListBox final : public CXFA_FFDropDown {
+  CPPGC_USING_PRE_FINALIZER(CXFA_FFListBox, PreFinalize);
+
  public:
-  explicit CXFA_FFListBox(CXFA_Node* pNode);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFListBox() override;
 
+  void PreFinalize();
+
   // CXFA_FFField:
+  void Trace(cppgc::Visitor* visitor) const override;
   bool LoadWidget() override;
-  bool OnKillFocus(CXFA_FFWidget* pNewWidget) override WARN_UNUSED_RESULT;
+  [[nodiscard]] bool OnKillFocus(CXFA_FFWidget* pNewWidget) override;
   void OnProcessMessage(CFWL_Message* pMessage) override;
   void OnProcessEvent(CFWL_Event* pEvent) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
+  void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix) override;
   FormFieldType GetFormFieldType() override;
 
-  // CXFA_FFDropDown
+  // CXFA_FFDropDown:
   void InsertItem(const WideString& wsLabel, int32_t nIndex) override;
   void DeleteItem(int32_t nIndex) override;
 
@@ -32,13 +38,15 @@
   void SetItemState(int32_t nIndex, bool bSelected);
 
  private:
+  explicit CXFA_FFListBox(CXFA_Node* pNode);
+
   bool CommitData() override;
   bool UpdateFWLData() override;
   bool IsDataChanged() override;
 
   uint32_t GetAlignment();
 
-  UnownedPtr<IFWL_WidgetDelegate> m_pOldDelegate;
+  cppgc::Member<IFWL_WidgetDelegate> m_pOldDelegate;
 };
 
 #endif  // XFA_FXFA_CXFA_FFLISTBOX_H_
diff --git a/xfa/fxfa/cxfa_ffnotify.cpp b/xfa/fxfa/cxfa_ffnotify.cpp
index fc70e6b..91f1ec0 100644
--- a/xfa/fxfa/cxfa_ffnotify.cpp
+++ b/xfa/fxfa/cxfa_ffnotify.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,7 @@
 
 #include <utility>
 
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffarc.h"
 #include "xfa/fxfa/cxfa_ffbarcode.h"
@@ -45,13 +45,17 @@
 
 CXFA_FFNotify::CXFA_FFNotify(CXFA_FFDoc* pDoc) : m_pDoc(pDoc) {}
 
-CXFA_FFNotify::~CXFA_FFNotify() {}
+CXFA_FFNotify::~CXFA_FFNotify() = default;
 
-void CXFA_FFNotify::OnPageEvent(CXFA_ViewLayoutItem* pSender,
-                                uint32_t dwEvent) {
+void CXFA_FFNotify::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pDoc);
+}
+
+void CXFA_FFNotify::OnPageViewEvent(CXFA_ViewLayoutItem* pSender,
+                                    CXFA_FFDoc::PageViewEvent eEvent) {
   CXFA_FFDocView* pDocView = m_pDoc->GetDocView(pSender->GetLayout());
   if (pDocView)
-    pDocView->OnPageEvent(pSender, dwEvent);
+    pDocView->OnPageViewEvent(pSender, eEvent);
 }
 
 void CXFA_FFNotify::OnWidgetListItemAdded(CXFA_Node* pSender,
@@ -79,110 +83,123 @@
   }
 }
 
-std::unique_ptr<CXFA_FFPageView> CXFA_FFNotify::OnCreateViewLayoutItem(
-    CXFA_Node* pNode) {
+CXFA_FFPageView* CXFA_FFNotify::OnCreateViewLayoutItem(CXFA_Node* pNode) {
   if (pNode->GetElementType() != XFA_Element::PageArea)
     return nullptr;
 
   auto* pLayout = CXFA_LayoutProcessor::FromDocument(m_pDoc->GetXFADoc());
-  return pdfium::MakeUnique<CXFA_FFPageView>(m_pDoc->GetDocView(pLayout),
-                                             pNode);
+  return cppgc::MakeGarbageCollected<CXFA_FFPageView>(
+      m_pDoc->GetHeap()->GetAllocationHandle(), m_pDoc->GetDocView(pLayout),
+      pNode);
 }
 
-std::unique_ptr<CXFA_FFWidget> CXFA_FFNotify::OnCreateContentLayoutItem(
-    CXFA_Node* pNode) {
-  ASSERT(pNode->GetElementType() != XFA_Element::ContentArea);
-  ASSERT(pNode->GetElementType() != XFA_Element::PageArea);
+CXFA_FFWidget* CXFA_FFNotify::OnCreateContentLayoutItem(CXFA_Node* pNode) {
+  DCHECK(pNode->GetElementType() != XFA_Element::ContentArea);
+  DCHECK(pNode->GetElementType() != XFA_Element::PageArea);
 
   // We only need to create the widget for certain types of objects.
   if (!pNode->HasCreatedUIWidget())
     return nullptr;
 
-  std::unique_ptr<CXFA_FFWidget> pWidget;
+  CXFA_FFWidget* pWidget = nullptr;
   switch (pNode->GetFFWidgetType()) {
     case XFA_FFWidgetType::kBarcode: {
-      CXFA_Node* child = pNode->GetUIChildNode();
-      if (child->GetElementType() != XFA_Element::Barcode)
+      auto* child = CXFA_Barcode::FromNode(pNode->GetUIChildNode());
+      if (!child)
         return nullptr;
 
-      pWidget = pdfium::MakeUnique<CXFA_FFBarcode>(
-          pNode, static_cast<CXFA_Barcode*>(child));
+      pWidget = cppgc::MakeGarbageCollected<CXFA_FFBarcode>(
+          m_pDoc->GetHeap()->GetAllocationHandle(), pNode, child);
       break;
     }
     case XFA_FFWidgetType::kButton: {
-      CXFA_Node* child = pNode->GetUIChildNode();
-      if (child->GetElementType() != XFA_Element::Button)
+      auto* child = CXFA_Button::FromNode(pNode->GetUIChildNode());
+      if (!child)
         return nullptr;
 
-      pWidget = pdfium::MakeUnique<CXFA_FFPushButton>(
-          pNode, static_cast<CXFA_Button*>(child));
+      pWidget = cppgc::MakeGarbageCollected<CXFA_FFPushButton>(
+          m_pDoc->GetHeap()->GetAllocationHandle(), pNode, child);
       break;
     }
     case XFA_FFWidgetType::kCheckButton: {
-      CXFA_Node* child = pNode->GetUIChildNode();
-      if (child->GetElementType() != XFA_Element::CheckButton)
+      auto* child = CXFA_CheckButton::FromNode(pNode->GetUIChildNode());
+      if (!child)
         return nullptr;
 
-      pWidget = pdfium::MakeUnique<CXFA_FFCheckButton>(
-          pNode, static_cast<CXFA_CheckButton*>(child));
+      pWidget = cppgc::MakeGarbageCollected<CXFA_FFCheckButton>(
+          m_pDoc->GetHeap()->GetAllocationHandle(), pNode, child);
       break;
     }
     case XFA_FFWidgetType::kChoiceList: {
-      if (pNode->IsListBox())
-        pWidget = pdfium::MakeUnique<CXFA_FFListBox>(pNode);
-      else
-        pWidget = pdfium::MakeUnique<CXFA_FFComboBox>(pNode);
+      if (pNode->IsListBox()) {
+        pWidget = cppgc::MakeGarbageCollected<CXFA_FFListBox>(
+            m_pDoc->GetHeap()->GetAllocationHandle(), pNode);
+      } else {
+        pWidget = cppgc::MakeGarbageCollected<CXFA_FFComboBox>(
+            m_pDoc->GetHeap()->GetAllocationHandle(), pNode);
+      }
       break;
     }
     case XFA_FFWidgetType::kDateTimeEdit:
-      pWidget = pdfium::MakeUnique<CXFA_FFDateTimeEdit>(pNode);
+      pWidget = cppgc::MakeGarbageCollected<CXFA_FFDateTimeEdit>(
+          m_pDoc->GetHeap()->GetAllocationHandle(), pNode);
       break;
     case XFA_FFWidgetType::kImageEdit:
-      pWidget = pdfium::MakeUnique<CXFA_FFImageEdit>(pNode);
+      pWidget = cppgc::MakeGarbageCollected<CXFA_FFImageEdit>(
+          m_pDoc->GetHeap()->GetAllocationHandle(), pNode);
       break;
     case XFA_FFWidgetType::kNumericEdit:
-      pWidget = pdfium::MakeUnique<CXFA_FFNumericEdit>(pNode);
+      pWidget = cppgc::MakeGarbageCollected<CXFA_FFNumericEdit>(
+          m_pDoc->GetHeap()->GetAllocationHandle(), pNode);
       break;
     case XFA_FFWidgetType::kPasswordEdit: {
-      CXFA_Node* child = pNode->GetUIChildNode();
-      if (child->GetElementType() != XFA_Element::PasswordEdit)
+      auto* child = CXFA_PasswordEdit::FromNode(pNode->GetUIChildNode());
+      if (!child)
         return nullptr;
 
-      pWidget = pdfium::MakeUnique<CXFA_FFPasswordEdit>(
-          pNode, static_cast<CXFA_PasswordEdit*>(child));
+      pWidget = cppgc::MakeGarbageCollected<CXFA_FFPasswordEdit>(
+          m_pDoc->GetHeap()->GetAllocationHandle(), pNode, child);
       break;
     }
     case XFA_FFWidgetType::kSignature:
-      pWidget = pdfium::MakeUnique<CXFA_FFSignature>(pNode);
+      pWidget = cppgc::MakeGarbageCollected<CXFA_FFSignature>(
+          m_pDoc->GetHeap()->GetAllocationHandle(), pNode);
       break;
     case XFA_FFWidgetType::kTextEdit:
-      pWidget = pdfium::MakeUnique<CXFA_FFTextEdit>(pNode);
+      pWidget = cppgc::MakeGarbageCollected<CXFA_FFTextEdit>(
+          m_pDoc->GetHeap()->GetAllocationHandle(), pNode);
       break;
     case XFA_FFWidgetType::kArc:
-      pWidget = pdfium::MakeUnique<CXFA_FFArc>(pNode);
+      pWidget = cppgc::MakeGarbageCollected<CXFA_FFArc>(
+          m_pDoc->GetHeap()->GetAllocationHandle(), pNode);
       break;
     case XFA_FFWidgetType::kLine:
-      pWidget = pdfium::MakeUnique<CXFA_FFLine>(pNode);
+      pWidget = cppgc::MakeGarbageCollected<CXFA_FFLine>(
+          m_pDoc->GetHeap()->GetAllocationHandle(), pNode);
       break;
     case XFA_FFWidgetType::kRectangle:
-      pWidget = pdfium::MakeUnique<CXFA_FFRectangle>(pNode);
+      pWidget = cppgc::MakeGarbageCollected<CXFA_FFRectangle>(
+          m_pDoc->GetHeap()->GetAllocationHandle(), pNode);
       break;
     case XFA_FFWidgetType::kText:
-      pWidget = pdfium::MakeUnique<CXFA_FFText>(pNode);
+      pWidget = cppgc::MakeGarbageCollected<CXFA_FFText>(
+          m_pDoc->GetHeap()->GetAllocationHandle(), pNode);
       break;
     case XFA_FFWidgetType::kImage:
-      pWidget = pdfium::MakeUnique<CXFA_FFImage>(pNode);
+      pWidget = cppgc::MakeGarbageCollected<CXFA_FFImage>(
+          m_pDoc->GetHeap()->GetAllocationHandle(), pNode);
       break;
     case XFA_FFWidgetType::kSubform:
-      pWidget = pdfium::MakeUnique<CXFA_FFWidget>(pNode);
+      pWidget = cppgc::MakeGarbageCollected<CXFA_FFWidget>(
+          m_pDoc->GetHeap()->GetAllocationHandle(), pNode);
       break;
     case XFA_FFWidgetType::kExclGroup:
-      pWidget = pdfium::MakeUnique<CXFA_FFExclGroup>(pNode);
+      pWidget = cppgc::MakeGarbageCollected<CXFA_FFExclGroup>(
+          m_pDoc->GetHeap()->GetAllocationHandle(), pNode);
       break;
     case XFA_FFWidgetType::kNone:
       return nullptr;
   }
-  ASSERT(pWidget);
   auto* pLayout = CXFA_LayoutProcessor::FromDocument(m_pDoc->GetXFADoc());
   pWidget->SetDocView(m_pDoc->GetDocView(pLayout));
   return pWidget;
@@ -201,6 +218,7 @@
 
   CXFA_EventParam EventParam;
   EventParam.m_eType = XFA_EVENT_Unknown;
+  EventParam.m_bTargeted = false;
 
   XFA_EventError iRet;
   bool bRet;
@@ -228,13 +246,21 @@
   pDocView->AddValidateNode(pNode);
 }
 
-IXFA_AppProvider* CXFA_FFNotify::GetAppProvider() {
+CXFA_FFApp::CallbackIface* CXFA_FFNotify::GetAppProvider() {
   return m_pDoc->GetApp()->GetAppProvider();
 }
 
-CXFA_FFWidgetHandler* CXFA_FFNotify::GetWidgetHandler() {
+void CXFA_FFNotify::HandleWidgetEvent(CXFA_Node* pNode,
+                                      CXFA_EventParam* pParam) {
   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
-  return pDocView ? pDocView->GetWidgetHandler() : nullptr;
+  if (!pDocView)
+    return;
+
+  CXFA_FFWidgetHandler* pHandler = pDocView->GetWidgetHandler();
+  if (!pHandler)
+    return;
+
+  pHandler->ProcessEvent(pNode, pParam);
 }
 
 void CXFA_FFNotify::OpenDropDownList(CXFA_Node* pNode) {
@@ -243,17 +269,12 @@
   if (!pLayoutItem)
     return;
 
-  CXFA_FFWidget* hWidget = XFA_GetWidgetFromLayoutItem(pLayoutItem);
+  CXFA_FFWidget* hWidget = CXFA_FFWidget::FromLayoutItem(pLayoutItem);
   if (!hWidget)
     return;
 
-  // SetFocusWidget() may destroy |hWidget| object by JS callback.
-  ObservedPtr<CXFA_FFWidget> pObservedWidget(hWidget);
-  CXFA_FFDoc* hDoc = GetHDOC();
-  hDoc->GetDocEnvironment()->SetFocusWidget(hDoc, hWidget);
-  if (!pObservedWidget)
-    return;
-
+  CXFA_FFDoc* hDoc = GetFFDoc();
+  hDoc->SetFocusWidget(hWidget);
   if (hWidget->GetNode()->GetFFWidgetType() != XFA_FFWidgetType::kChoiceList)
     return;
 
@@ -265,11 +286,8 @@
   if (!pComboBox)
     return;
 
-  CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
-  pDocView->LockUpdate();
+  CXFA_FFDocView::UpdateScope scope(m_pDoc->GetDocView());
   pComboBox->OpenDropDownList();
-  pDocView->UnlockUpdate();
-  pDocView->UpdateDocView();
 }
 
 void CXFA_FFNotify::ResetData(CXFA_Node* pNode) {
@@ -280,9 +298,10 @@
   pDocView->ResetNode(pNode);
 }
 
-int32_t CXFA_FFNotify::GetLayoutStatus() {
+CXFA_FFDocView::LayoutStatus CXFA_FFNotify::GetLayoutStatus() {
   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
-  return pDocView ? pDocView->GetLayoutStatus() : 0;
+  return pDocView ? pDocView->GetLayoutStatus()
+                  : CXFA_FFDocView::LayoutStatus::kNone;
 }
 
 void CXFA_FFNotify::RunNodeInitialize(CXFA_Node* pNode) {
@@ -293,7 +312,7 @@
   pDocView->AddNewFormNode(pNode);
 }
 
-void CXFA_FFNotify::RunSubformIndexChange(CXFA_Node* pSubformNode) {
+void CXFA_FFNotify::RunSubformIndexChange(CXFA_Subform* pSubformNode) {
   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
   if (!pDocView)
     return;
@@ -328,7 +347,7 @@
       pDocView->AddBindItem(static_cast<CXFA_BindItems*>(pNode));
       break;
     case XFA_Element::Validate:
-      pNode->SetFlag(XFA_NodeFlag_NeedsInitApp);
+      pNode->SetFlag(XFA_NodeFlag::kNeedsInitApp);
       break;
     default:
       break;
@@ -346,7 +365,7 @@
   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
   if (!pDocView)
     return;
-  if (pDocView->GetLayoutStatus() < XFA_DOCVIEW_LAYOUTSTATUS_End)
+  if (pDocView->GetLayoutStatus() != CXFA_FFDocView::LayoutStatus::kEnd)
     return;
 
   CXFA_FFWidget* pWidget = m_pDoc->GetDocView()->GetWidgetForNode(pSender);
@@ -420,8 +439,8 @@
   }
 }
 
-void CXFA_FFNotify::OnContainerChanged(CXFA_Node* pNode) {
-  m_pDoc->GetXFADoc()->GetLayoutProcessor()->AddChangedContainer(pNode);
+void CXFA_FFNotify::OnContainerChanged() {
+  m_pDoc->GetXFADoc()->GetLayoutProcessor()->SetHasChangedContainer();
 }
 
 void CXFA_FFNotify::OnChildAdded(CXFA_Node* pSender) {
@@ -432,11 +451,11 @@
   if (!pDocView)
     return;
 
-  bool bLayoutReady =
-      !(pDocView->m_bInLayoutStatus) &&
-      (pDocView->GetLayoutStatus() == XFA_DOCVIEW_LAYOUTSTATUS_End);
+  const bool bLayoutReady =
+      !pDocView->InLayoutStatus() &&
+      pDocView->GetLayoutStatus() == CXFA_FFDocView::LayoutStatus::kEnd;
   if (bLayoutReady)
-    m_pDoc->GetDocEnvironment()->SetChangeMark(m_pDoc.Get());
+    m_pDoc->SetChangeMark();
 }
 
 void CXFA_FFNotify::OnChildRemoved() {
@@ -444,38 +463,39 @@
   if (!pDocView)
     return;
 
-  bool bLayoutReady =
-      !(pDocView->m_bInLayoutStatus) &&
-      (pDocView->GetLayoutStatus() == XFA_DOCVIEW_LAYOUTSTATUS_End);
+  const bool bLayoutReady =
+      !pDocView->InLayoutStatus() &&
+      pDocView->GetLayoutStatus() == CXFA_FFDocView::LayoutStatus::kEnd;
   if (bLayoutReady)
-    m_pDoc->GetDocEnvironment()->SetChangeMark(m_pDoc.Get());
+    m_pDoc->SetChangeMark();
 }
 
 void CXFA_FFNotify::OnLayoutItemAdded(CXFA_LayoutProcessor* pLayout,
                                       CXFA_LayoutItem* pSender,
                                       int32_t iPageIdx,
-                                      uint32_t dwStatus) {
+                                      Mask<XFA_WidgetStatus> dwStatus) {
   CXFA_FFDocView* pDocView = m_pDoc->GetDocView(pLayout);
   if (!pDocView)
     return;
 
-  CXFA_FFWidget* pWidget = XFA_GetWidgetFromLayoutItem(pSender);
+  CXFA_FFWidget* pWidget = CXFA_FFWidget::FromLayoutItem(pSender);
   if (!pWidget)
     return;
 
   CXFA_FFPageView* pNewPageView = pDocView->GetPageView(iPageIdx);
-  uint32_t dwFilter = XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable |
-                      XFA_WidgetStatus_Printable;
-  pWidget->ModifyStatus(dwStatus, dwFilter);
+  constexpr Mask<XFA_WidgetStatus> kRemove{XFA_WidgetStatus::kVisible,
+                                           XFA_WidgetStatus::kViewable,
+                                           XFA_WidgetStatus::kPrintable};
+  pWidget->ModifyStatus(dwStatus, kRemove);
   CXFA_FFPageView* pPrePageView = pWidget->GetPageView();
   if (pPrePageView != pNewPageView ||
-      (dwStatus & (XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable)) ==
-          (XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable)) {
+      dwStatus.TestAll(
+          {XFA_WidgetStatus::kVisible, XFA_WidgetStatus::kViewable})) {
     pWidget->SetPageView(pNewPageView);
-    m_pDoc->GetDocEnvironment()->WidgetPostAdd(pWidget);
+    m_pDoc->WidgetPostAdd(pWidget);
   }
-  if (pDocView->GetLayoutStatus() != XFA_DOCVIEW_LAYOUTSTATUS_End ||
-      !(dwStatus & XFA_WidgetStatus_Visible)) {
+  if (pDocView->GetLayoutStatus() != CXFA_FFDocView::LayoutStatus::kEnd ||
+      !(dwStatus & XFA_WidgetStatus::kVisible)) {
     return;
   }
   if (pWidget->IsLoaded()) {
@@ -493,11 +513,11 @@
   if (!pDocView)
     return;
 
-  CXFA_FFWidget* pWidget = XFA_GetWidgetFromLayoutItem(pSender);
+  CXFA_FFWidget* pWidget = CXFA_FFWidget::FromLayoutItem(pSender);
   if (!pWidget)
     return;
 
   pDocView->DeleteLayoutItem(pWidget);
-  m_pDoc->GetDocEnvironment()->WidgetPreRemove(pWidget);
+  m_pDoc->WidgetPreRemove(pWidget);
   pWidget->InvalidateRect();
 }
diff --git a/xfa/fxfa/cxfa_ffnotify.h b/xfa/fxfa/cxfa_ffnotify.h
index 52fae1d..ff0ef84 100644
--- a/xfa/fxfa/cxfa_ffnotify.h
+++ b/xfa/fxfa/cxfa_ffnotify.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,25 +7,31 @@
 #ifndef XFA_FXFA_CXFA_FFNOTIFY_H_
 #define XFA_FXFA_CXFA_FFNOTIFY_H_
 
-#include <memory>
-
+#include "core/fxcrt/mask.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
-#include "xfa/fxfa/fxfa.h"
+#include "xfa/fxfa/cxfa_ffapp.h"
+#include "xfa/fxfa/cxfa_ffdoc.h"
+#include "xfa/fxfa/cxfa_ffdocview.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 
-class CXFA_ContentLayoutItem;
-class CXFA_FFWidgetHandler;
 class CXFA_LayoutItem;
 class CXFA_LayoutProcessor;
 class CXFA_Script;
 class CXFA_ViewLayoutItem;
 
-class CXFA_FFNotify {
+class CXFA_FFNotify : public cppgc::GarbageCollected<CXFA_FFNotify> {
  public:
-  explicit CXFA_FFNotify(CXFA_FFDoc* pDoc);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFNotify();
 
-  void OnPageEvent(CXFA_ViewLayoutItem* pSender, uint32_t dwEvent);
+  void Trace(cppgc::Visitor* visitor) const;
+
+  void OnPageViewEvent(CXFA_ViewLayoutItem* pSender,
+                       CXFA_FFDoc::PageViewEvent eEvent);
 
   void OnWidgetListItemAdded(CXFA_Node* pSender,
                              const WideString& wsLabel,
@@ -39,20 +45,20 @@
                       XFA_Attribute eAttr,
                       CXFA_Node* pParentNode,
                       CXFA_Node* pWidgetNode);
-  void OnContainerChanged(CXFA_Node* pNode);
+  void OnContainerChanged();
   void OnChildAdded(CXFA_Node* pSender);
   void OnChildRemoved();
 
-  std::unique_ptr<CXFA_FFPageView> OnCreateViewLayoutItem(CXFA_Node* pNode);
-  std::unique_ptr<CXFA_FFWidget> OnCreateContentLayoutItem(CXFA_Node* pNode);
+  // These two return new views/widgets from cppgc heap.
+  CXFA_FFPageView* OnCreateViewLayoutItem(CXFA_Node* pNode);
+  CXFA_FFWidget* OnCreateContentLayoutItem(CXFA_Node* pNode);
 
   void OnLayoutItemAdded(CXFA_LayoutProcessor* pLayout,
                          CXFA_LayoutItem* pSender,
                          int32_t iPageIdx,
-                         uint32_t dwStatus);
+                         Mask<XFA_WidgetStatus> dwStatus);
   void OnLayoutItemRemoving(CXFA_LayoutProcessor* pLayout,
                             CXFA_LayoutItem* pSender);
-
   void StartFieldDrawLayout(CXFA_Node* pItem,
                             float* pCalcWidth,
                             float* pCalcHeight);
@@ -62,19 +68,21 @@
                                       bool bIsFormReady,
                                       bool bRecursive);
   void AddCalcValidate(CXFA_Node* pNode);
-  CXFA_FFDoc* GetHDOC() const { return m_pDoc.Get(); }
-  IXFA_AppProvider* GetAppProvider();
-  CXFA_FFWidgetHandler* GetWidgetHandler();
+  CXFA_FFDoc* GetFFDoc() const { return m_pDoc; }
+  CXFA_FFApp::CallbackIface* GetAppProvider();
+  void HandleWidgetEvent(CXFA_Node* pNode, CXFA_EventParam* pParam);
   void OpenDropDownList(CXFA_Node* pNode);
   void ResetData(CXFA_Node* pNode);
-  int32_t GetLayoutStatus();
+  CXFA_FFDocView::LayoutStatus GetLayoutStatus();
   void RunNodeInitialize(CXFA_Node* pNode);
-  void RunSubformIndexChange(CXFA_Node* pSubformNode);
+  void RunSubformIndexChange(CXFA_Subform* pSubformNode);
   CXFA_Node* GetFocusWidgetNode();
   void SetFocusWidgetNode(CXFA_Node* pNode);
 
  private:
-  UnownedPtr<CXFA_FFDoc> const m_pDoc;
+  explicit CXFA_FFNotify(CXFA_FFDoc* pDoc);
+
+  cppgc::Member<CXFA_FFDoc> const m_pDoc;
 };
 
 #endif  // XFA_FXFA_CXFA_FFNOTIFY_H_
diff --git a/xfa/fxfa/cxfa_ffnumericedit.cpp b/xfa/fxfa/cxfa_ffnumericedit.cpp
index 8d1b3bb..33513e5 100644
--- a/xfa/fxfa/cxfa_ffnumericedit.cpp
+++ b/xfa/fxfa/cxfa_ffnumericedit.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,7 @@
 
 #include "xfa/fxfa/cxfa_ffnumericedit.h"
 
-#include <utility>
-
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 #include "xfa/fwl/cfwl_edit.h"
 #include "xfa/fwl/cfwl_eventvalidate.h"
 #include "xfa/fwl/cfwl_notedriver.h"
@@ -23,21 +21,22 @@
 CXFA_FFNumericEdit::~CXFA_FFNumericEdit() = default;
 
 bool CXFA_FFNumericEdit::LoadWidget() {
-  ASSERT(!IsLoaded());
-  auto pNewEdit = pdfium::MakeUnique<CFWL_Edit>(
-      GetFWLApp(), pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr);
-  CFWL_Edit* pWidget = pNewEdit.get();
-  SetNormalWidget(std::move(pNewEdit));
+  DCHECK(!IsLoaded());
+
+  CFWL_Edit* pWidget = cppgc::MakeGarbageCollected<CFWL_Edit>(
+      GetFWLApp()->GetHeap()->GetAllocationHandle(), GetFWLApp(),
+      CFWL_Widget::Properties(), nullptr);
+  SetNormalWidget(pWidget);
   pWidget->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver = pWidget->GetOwnerApp()->GetNoteDriver();
+  CFWL_NoteDriver* pNoteDriver = pWidget->GetFWLApp()->GetNoteDriver();
   pNoteDriver->RegisterEventTarget(pWidget, pWidget);
   m_pOldDelegate = pWidget->GetDelegate();
   pWidget->SetDelegate(this);
 
   {
     CFWL_Widget::ScopedUpdateLock update_lock(pWidget);
-    pWidget->SetText(m_pNode->GetValue(XFA_VALUEPICTURE_Display));
+    pWidget->SetText(m_pNode->GetValue(XFA_ValuePicture::kDisplay));
     UpdateWidgetProperty();
   }
 
@@ -56,29 +55,30 @@
   if (!m_pNode->IsHorizontalScrollPolicyOff())
     dwExtendedStyle |= FWL_STYLEEXT_EDT_AutoHScroll;
 
-  Optional<int32_t> numCells = m_pNode->GetNumberOfCells();
-  if (numCells && *numCells > 0) {
+  absl::optional<int32_t> numCells = m_pNode->GetNumberOfCells();
+  if (numCells.has_value() && numCells.value() > 0) {
     dwExtendedStyle |= FWL_STYLEEXT_EDT_CombText;
-    pWidget->SetLimit(*numCells);
+    pWidget->SetLimit(numCells.value());
   }
   dwExtendedStyle |= GetAlignment();
   if (!m_pNode->IsOpenAccess() || !GetDoc()->GetXFADoc()->IsInteractive())
     dwExtendedStyle |= FWL_STYLEEXT_EDT_ReadOnly;
 
-  GetNormalWidget()->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
+  GetNormalWidget()->ModifyStyleExts(dwExtendedStyle, 0xFFFFFFFF);
 }
 
 void CXFA_FFNumericEdit::OnProcessEvent(CFWL_Event* pEvent) {
   if (pEvent->GetType() == CFWL_Event::Type::Validate) {
     CFWL_EventValidate* event = static_cast<CFWL_EventValidate*>(pEvent);
-    event->bValidate = OnValidate(GetNormalWidget(), event->wsInsert);
+    event->SetValidate(OnValidate(GetNormalWidget(), event->GetInsert()));
     return;
   }
   CXFA_FFTextEdit::OnProcessEvent(pEvent);
 }
 
-bool CXFA_FFNumericEdit::OnValidate(CFWL_Widget* pWidget, WideString& wsText) {
-  WideString wsPattern = m_pNode->GetPictureContent(XFA_VALUEPICTURE_Edit);
+bool CXFA_FFNumericEdit::OnValidate(CFWL_Widget* pWidget,
+                                    const WideString& wsText) {
+  WideString wsPattern = m_pNode->GetPictureContent(XFA_ValuePicture::kEdit);
   if (!wsPattern.IsEmpty())
     return true;
 
diff --git a/xfa/fxfa/cxfa_ffnumericedit.h b/xfa/fxfa/cxfa_ffnumericedit.h
index b2c39ce..4a5c409 100644
--- a/xfa/fxfa/cxfa_ffnumericedit.h
+++ b/xfa/fxfa/cxfa_ffnumericedit.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #ifndef XFA_FXFA_CXFA_FFNUMERICEDIT_H_
 #define XFA_FXFA_CXFA_FFNUMERICEDIT_H_
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 #include "xfa/fxfa/cxfa_fftextedit.h"
 
 class CFWL_Event;
@@ -24,7 +24,7 @@
   void OnProcessEvent(CFWL_Event* pEvent) override;
 
  private:
-  bool OnValidate(CFWL_Widget* pWidget, WideString& wsText);
+  bool OnValidate(CFWL_Widget* pWidget, const WideString& wsText);
 };
 
 #endif  // XFA_FXFA_CXFA_FFNUMERICEDIT_H_
diff --git a/xfa/fxfa/cxfa_ffpageview.cpp b/xfa/fxfa/cxfa_ffpageview.cpp
index 1915152..5549bfb 100644
--- a/xfa/fxfa/cxfa_ffpageview.cpp
+++ b/xfa/fxfa/cxfa_ffpageview.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,13 @@
 #include "xfa/fxfa/cxfa_ffpageview.h"
 
 #include <algorithm>
-#include <memory>
 #include <vector>
 
+#include "core/fxcrt/stl_util.h"
+#include "fxjs/gc/container_trace.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
 #include "xfa/fxfa/cxfa_ffcheckbutton.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffdocview.h"
@@ -29,14 +30,11 @@
 
 CFX_Matrix GetPageMatrix(const CFX_RectF& docPageRect,
                          const FX_RECT& devicePageRect,
-                         int32_t iRotate,
-                         uint32_t dwCoordinatesType) {
-  ASSERT(iRotate >= 0);
-  ASSERT(iRotate <= 3);
+                         int32_t iRotate) {
+  DCHECK(iRotate >= 0);
+  DCHECK(iRotate <= 3);
 
-  bool bFlipX = (dwCoordinatesType & 0x01) != 0;
-  bool bFlipY = (dwCoordinatesType & 0x02) != 0;
-  CFX_Matrix m((bFlipX ? -1.0f : 1.0f), 0, 0, (bFlipY ? -1.0f : 1.0f), 0, 0);
+  CFX_Matrix m;
   if (iRotate == 0 || iRotate == 2) {
     m.a *= (float)devicePageRect.Width() / docPageRect.width;
     m.d *= (float)devicePageRect.Height() / docPageRect.height;
@@ -47,20 +45,20 @@
   m.Rotate(iRotate * 1.57079632675f);
   switch (iRotate) {
     case 0:
-      m.e = bFlipX ? devicePageRect.right : devicePageRect.left;
-      m.f = bFlipY ? devicePageRect.bottom : devicePageRect.top;
+      m.e = devicePageRect.left;
+      m.f = devicePageRect.top;
       break;
     case 1:
-      m.e = bFlipY ? devicePageRect.left : devicePageRect.right;
-      m.f = bFlipX ? devicePageRect.bottom : devicePageRect.top;
+      m.e = devicePageRect.right;
+      m.f = devicePageRect.top;
       break;
     case 2:
-      m.e = bFlipX ? devicePageRect.left : devicePageRect.right;
-      m.f = bFlipY ? devicePageRect.top : devicePageRect.bottom;
+      m.e = devicePageRect.right;
+      m.f = devicePageRect.bottom;
       break;
     case 3:
-      m.e = bFlipY ? devicePageRect.right : devicePageRect.left;
-      m.f = bFlipX ? devicePageRect.top : devicePageRect.bottom;
+      m.e = devicePageRect.left;
+      m.f = devicePageRect.bottom;
       break;
     default:
       break;
@@ -69,33 +67,30 @@
 }
 
 bool PageWidgetFilter(CXFA_FFWidget* pWidget,
-                      uint32_t dwFilter,
+                      Mask<XFA_WidgetStatus> dwFilter,
                       bool bTraversal,
                       bool bIgnoreRelevant) {
   CXFA_Node* pNode = pWidget->GetNode();
 
-  if ((dwFilter & XFA_WidgetStatus_Focused) &&
+  if ((dwFilter & XFA_WidgetStatus::kFocused) &&
       (!pNode || pNode->GetElementType() != XFA_Element::Field)) {
     return false;
   }
 
   CXFA_ContentLayoutItem* pItem = pWidget->GetLayoutItem();
-  if (bTraversal && pItem->TestStatusBits(XFA_WidgetStatus_Disabled))
+  if (bTraversal && pItem->TestStatusBits(XFA_WidgetStatus::kDisabled))
     return false;
   if (bIgnoreRelevant)
-    return pItem->TestStatusBits(XFA_WidgetStatus_Visible);
+    return pItem->TestStatusBits(XFA_WidgetStatus::kVisible);
 
-  dwFilter &= (XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable |
-               XFA_WidgetStatus_Printable);
+  dwFilter &= Mask<XFA_WidgetStatus>{XFA_WidgetStatus::kVisible,
+                                     XFA_WidgetStatus::kViewable,
+                                     XFA_WidgetStatus::kPrintable};
   return pItem->TestStatusBits(dwFilter);
 }
 
-bool IsLayoutElement(XFA_Element eElement, bool bLayoutContainer) {
+bool IsLayoutElement(XFA_Element eElement) {
   switch (eElement) {
-    case XFA_Element::Draw:
-    case XFA_Element::Field:
-    case XFA_Element::InstanceManager:
-      return !bLayoutContainer;
     case XFA_Element::Area:
     case XFA_Element::Subform:
     case XFA_Element::ExclGroup:
@@ -108,61 +103,188 @@
   }
 }
 
+CXFA_Document* GetDocForPageView(const CXFA_FFPageView* view) {
+  return view->GetDocView()->GetDoc()->GetXFADoc();
+}
+
+bool IsDocVersionBelow205(const CXFA_Document* doc) {
+  return doc->GetCurVersionMode() < XFA_VERSION_205;
+}
+
+bool EnsureWidgetLoadedIfVisible(CXFA_FFWidget* pWidget) {
+  if (!pWidget->IsLoaded() &&
+      pWidget->GetLayoutItem()->TestStatusBits(XFA_WidgetStatus::kVisible)) {
+    if (!pWidget->LoadWidget())
+      return false;
+  }
+  return true;
+}
+
+CXFA_FFWidget* LoadedWidgetFromLayoutItem(CXFA_LayoutItem* pLayoutItem) {
+  CXFA_FFWidget* pWidget = CXFA_FFWidget::FromLayoutItem(pLayoutItem);
+  if (!pWidget)
+    return nullptr;
+
+  EnsureWidgetLoadedIfVisible(pWidget);
+  return pWidget;
+}
+
+CXFA_FFWidget* FilteredLoadedWidgetFromLayoutItem(
+    CXFA_LayoutItem* pLayoutItem,
+    Mask<XFA_WidgetStatus> dwFilter,
+    bool bIgnoreRelevant) {
+  CXFA_FFWidget* pWidget = CXFA_FFWidget::FromLayoutItem(pLayoutItem);
+  if (!pWidget)
+    return nullptr;
+
+  if (!PageWidgetFilter(pWidget, dwFilter, false, bIgnoreRelevant))
+    return nullptr;
+
+  if (!EnsureWidgetLoadedIfVisible(pWidget))
+    return nullptr;
+
+  return pWidget;
+}
+
+class CXFA_TabParam {
+ public:
+  CXFA_TabParam() = default;
+  explicit CXFA_TabParam(CXFA_FFWidget* pWidget)
+      : m_pItem(pWidget->GetLayoutItem()) {}
+  CXFA_TabParam(const CXFA_TabParam&) = delete;
+  CXFA_TabParam(CXFA_TabParam&&) noexcept = default;
+  ~CXFA_TabParam() = default;
+
+  CXFA_TabParam& operator=(const CXFA_TabParam&) = delete;
+  CXFA_TabParam& operator=(CXFA_TabParam&&) noexcept = default;
+
+  CXFA_FFWidget* GetWidget() const { return m_pItem->GetFFWidget(); }
+  const std::vector<cppgc::Persistent<CXFA_ContentLayoutItem>>& GetChildren()
+      const {
+    return m_Children;
+  }
+  void ClearChildren() { m_Children.clear(); }
+  void AppendTabParam(const CXFA_TabParam* pParam) {
+    m_Children.push_back(pParam->m_pItem);
+    m_Children.insert(m_Children.end(), pParam->m_Children.begin(),
+                      pParam->m_Children.end());
+  }
+
+ private:
+  cppgc::Persistent<CXFA_ContentLayoutItem> m_pItem;
+  std::vector<cppgc::Persistent<CXFA_ContentLayoutItem>> m_Children;
+};
+
+void OrderContainer(CXFA_LayoutItemIterator* sIterator,
+                    CXFA_LayoutItem* pViewItem,
+                    CXFA_TabParam* pContainer,
+                    bool* bCurrentItem,
+                    bool* bContentArea,
+                    bool bMasterPage) {
+  std::vector<CXFA_TabParam> tabParams;
+  CXFA_LayoutItem* pSearchItem = sIterator->MoveToNext();
+  while (pSearchItem) {
+    if (!pSearchItem->IsContentLayoutItem()) {
+      *bContentArea = true;
+      pSearchItem = sIterator->MoveToNext();
+      continue;
+    }
+    if (bMasterPage && *bContentArea) {
+      break;
+    }
+    if (bMasterPage || *bContentArea) {
+      CXFA_FFWidget* hWidget = LoadedWidgetFromLayoutItem(pSearchItem);
+      if (!hWidget) {
+        pSearchItem = sIterator->MoveToNext();
+        continue;
+      }
+      if (pViewItem && (pSearchItem->GetParent() != pViewItem)) {
+        *bCurrentItem = true;
+        break;
+      }
+      tabParams.emplace_back(hWidget);
+      if (IsLayoutElement(pSearchItem->GetFormNode()->GetElementType())) {
+        OrderContainer(sIterator, pSearchItem, &tabParams.back(), bCurrentItem,
+                       bContentArea, bMasterPage);
+      }
+    }
+    if (*bCurrentItem) {
+      pSearchItem = sIterator->GetCurrent();
+      *bCurrentItem = false;
+    } else {
+      pSearchItem = sIterator->MoveToNext();
+    }
+  }
+  std::sort(tabParams.begin(), tabParams.end(),
+            [](const CXFA_TabParam& arg1, const CXFA_TabParam& arg2) {
+              const CFX_RectF& rt1 = arg1.GetWidget()->GetWidgetRect();
+              const CFX_RectF& rt2 = arg2.GetWidget()->GetWidgetRect();
+              if (rt1.top - rt2.top >= kXFAWidgetPrecision)
+                return rt1.top < rt2.top;
+              return rt1.left < rt2.left;
+            });
+  for (const auto& param : tabParams)
+    pContainer->AppendTabParam(&param);
+}
+
 }  // namespace
 
 CXFA_FFPageView::CXFA_FFPageView(CXFA_FFDocView* pDocView, CXFA_Node* pPageArea)
     : m_pPageArea(pPageArea), m_pDocView(pDocView) {}
 
-CXFA_FFPageView::~CXFA_FFPageView() {}
+CXFA_FFPageView::~CXFA_FFPageView() = default;
+
+void CXFA_FFPageView::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pPageArea);
+  visitor->Trace(m_pDocView);
+  visitor->Trace(m_pLayoutItem);
+}
 
 CXFA_FFDocView* CXFA_FFPageView::GetDocView() const {
-  return m_pDocView.Get();
+  return m_pDocView;
 }
 
 CFX_RectF CXFA_FFPageView::GetPageViewRect() const {
-  return CFX_RectF(0, 0, GetLayoutItem()->GetPageSize());
+  auto* pItem = GetLayoutItem();
+  if (!pItem)
+    return CFX_RectF();
+
+  return CFX_RectF(0, 0, pItem->GetPageSize());
 }
 
 CFX_Matrix CXFA_FFPageView::GetDisplayMatrix(const FX_RECT& rtDisp,
                                              int32_t iRotate) const {
-  return GetPageMatrix(CFX_RectF(0, 0, GetLayoutItem()->GetPageSize()), rtDisp,
-                       iRotate, 0);
+  auto* pItem = GetLayoutItem();
+  if (!pItem)
+    return CFX_Matrix();
+
+  return GetPageMatrix(CFX_RectF(0, 0, pItem->GetPageSize()), rtDisp, iRotate);
 }
 
-std::unique_ptr<IXFA_WidgetIterator> CXFA_FFPageView::CreateFormWidgetIterator(
-    uint32_t dwWidgetFilter) {
-  return pdfium::MakeUnique<CXFA_FFPageWidgetIterator>(this, dwWidgetFilter);
+CXFA_FFWidget::IteratorIface* CXFA_FFPageView::CreateGCedTraverseWidgetIterator(
+    Mask<XFA_WidgetStatus> dwWidgetFilter) {
+  return cppgc::MakeGarbageCollected<CXFA_FFTabOrderPageWidgetIterator>(
+      GetDocView()->GetDoc()->GetHeap()->GetAllocationHandle(), this,
+      dwWidgetFilter);
 }
 
-std::unique_ptr<IXFA_WidgetIterator>
-CXFA_FFPageView::CreateTraverseWidgetIterator(uint32_t dwWidgetFilter) {
-  return pdfium::MakeUnique<CXFA_FFTabOrderPageWidgetIterator>(this,
-                                                               dwWidgetFilter);
-}
-
-CXFA_FFPageWidgetIterator::CXFA_FFPageWidgetIterator(CXFA_FFPageView* pPageView,
-                                                     uint32_t dwFilter)
-    : m_pPageView(pPageView),
+CXFA_FFPageWidgetIterator::CXFA_FFPageWidgetIterator(
+    CXFA_FFPageView* pPageView,
+    Mask<XFA_WidgetStatus> dwFilter)
+    : m_sIterator(pPageView->GetLayoutItem()),
       m_dwFilter(dwFilter),
-      m_sIterator(pPageView->GetLayoutItem()) {
-  m_bIgnoreRelevant =
-      m_pPageView->GetDocView()->GetDoc()->GetXFADoc()->GetCurVersionMode() <
-      XFA_VERSION_205;
-}
+      m_bIgnoreRelevant(IsDocVersionBelow205(GetDocForPageView(pPageView))) {}
 
-CXFA_FFPageWidgetIterator::~CXFA_FFPageWidgetIterator() {}
-
-void CXFA_FFPageWidgetIterator::Reset() {
-  m_sIterator.Reset();
-}
+CXFA_FFPageWidgetIterator::~CXFA_FFPageWidgetIterator() = default;
 
 CXFA_FFWidget* CXFA_FFPageWidgetIterator::MoveToFirst() {
   m_sIterator.Reset();
   for (CXFA_LayoutItem* pLayoutItem = m_sIterator.GetCurrent(); pLayoutItem;
        pLayoutItem = m_sIterator.MoveToNext()) {
-    if (CXFA_FFWidget* hWidget = GetWidget(pLayoutItem)) {
+    CXFA_FFWidget* hWidget = FilteredLoadedWidgetFromLayoutItem(
+        pLayoutItem, m_dwFilter, m_bIgnoreRelevant);
+    if (hWidget)
       return hWidget;
-    }
   }
   return nullptr;
 }
@@ -175,9 +297,10 @@
 CXFA_FFWidget* CXFA_FFPageWidgetIterator::MoveToNext() {
   for (CXFA_LayoutItem* pLayoutItem = m_sIterator.MoveToNext(); pLayoutItem;
        pLayoutItem = m_sIterator.MoveToNext()) {
-    if (CXFA_FFWidget* hWidget = GetWidget(pLayoutItem)) {
+    CXFA_FFWidget* hWidget = FilteredLoadedWidgetFromLayoutItem(
+        pLayoutItem, m_dwFilter, m_bIgnoreRelevant);
+    if (hWidget)
       return hWidget;
-    }
   }
   return nullptr;
 }
@@ -185,85 +308,59 @@
 CXFA_FFWidget* CXFA_FFPageWidgetIterator::MoveToPrevious() {
   for (CXFA_LayoutItem* pLayoutItem = m_sIterator.MoveToPrev(); pLayoutItem;
        pLayoutItem = m_sIterator.MoveToPrev()) {
-    if (CXFA_FFWidget* hWidget = GetWidget(pLayoutItem)) {
+    CXFA_FFWidget* hWidget = FilteredLoadedWidgetFromLayoutItem(
+        pLayoutItem, m_dwFilter, m_bIgnoreRelevant);
+    if (hWidget)
       return hWidget;
-    }
   }
   return nullptr;
 }
 
 CXFA_FFWidget* CXFA_FFPageWidgetIterator::GetCurrentWidget() {
   CXFA_LayoutItem* pLayoutItem = m_sIterator.GetCurrent();
-  return pLayoutItem ? XFA_GetWidgetFromLayoutItem(pLayoutItem) : nullptr;
+  return pLayoutItem ? CXFA_FFWidget::FromLayoutItem(pLayoutItem) : nullptr;
 }
 
 bool CXFA_FFPageWidgetIterator::SetCurrentWidget(CXFA_FFWidget* pWidget) {
   return pWidget && m_sIterator.SetCurrent(pWidget->GetLayoutItem());
 }
 
-CXFA_FFWidget* CXFA_FFPageWidgetIterator::GetWidget(
-    CXFA_LayoutItem* pLayoutItem) {
-  CXFA_FFWidget* pWidget = XFA_GetWidgetFromLayoutItem(pLayoutItem);
-  if (!pWidget)
-    return nullptr;
-
-  if (!PageWidgetFilter(pWidget, m_dwFilter, false, m_bIgnoreRelevant))
-    return nullptr;
-
-  if (!pWidget->IsLoaded() &&
-      pWidget->GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_Visible)) {
-    if (!pWidget->LoadWidget())
-      return nullptr;
-  }
-  return pWidget;
-}
-
-void CXFA_TabParam::AppendTabParam(CXFA_TabParam* pParam) {
-  m_Children.push_back(pParam->GetWidget());
-  m_Children.insert(m_Children.end(), pParam->GetChildren().begin(),
-                    pParam->GetChildren().end());
-}
-
-void CXFA_TabParam::ClearChildren() {
-  m_Children.clear();
-}
-
 CXFA_FFTabOrderPageWidgetIterator::CXFA_FFTabOrderPageWidgetIterator(
     CXFA_FFPageView* pPageView,
-    uint32_t dwFilter)
-    : m_pPageView(pPageView), m_dwFilter(dwFilter), m_iCurWidget(-1) {
-  m_bIgnoreRelevant =
-      m_pPageView->GetDocView()->GetDoc()->GetXFADoc()->GetCurVersionMode() <
-      XFA_VERSION_205;
-  Reset();
+    Mask<XFA_WidgetStatus> dwFilter)
+    : m_pPageViewLayout(pPageView->GetLayoutItem()),
+      m_dwFilter(dwFilter),
+      m_bIgnoreRelevant(IsDocVersionBelow205(GetDocForPageView(pPageView))) {
+  CreateTabOrderWidgetArray();
 }
 
-CXFA_FFTabOrderPageWidgetIterator::~CXFA_FFTabOrderPageWidgetIterator() {}
+CXFA_FFTabOrderPageWidgetIterator::~CXFA_FFTabOrderPageWidgetIterator() =
+    default;
 
-void CXFA_FFTabOrderPageWidgetIterator::Reset() {
-  CreateTabOrderWidgetArray();
-  m_iCurWidget = -1;
+void CXFA_FFTabOrderPageWidgetIterator::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pPageViewLayout);
+  ContainerTrace(visitor, m_TabOrderWidgetArray);
 }
 
 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::MoveToFirst() {
-  for (int32_t i = 0;
-       i < pdfium::CollectionSize<int32_t>(m_TabOrderWidgetArray); i++) {
-    if (PageWidgetFilter(m_TabOrderWidgetArray[i].Get(), m_dwFilter, true,
-                         m_bIgnoreRelevant)) {
+  for (int32_t i = 0; i < fxcrt::CollectionSize<int32_t>(m_TabOrderWidgetArray);
+       i++) {
+    if (PageWidgetFilter(m_TabOrderWidgetArray[i]->GetFFWidget(), m_dwFilter,
+                         true, m_bIgnoreRelevant)) {
       m_iCurWidget = i;
-      return m_TabOrderWidgetArray[m_iCurWidget].Get();
+      return m_TabOrderWidgetArray[m_iCurWidget]->GetFFWidget();
     }
   }
   return nullptr;
 }
 
 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::MoveToLast() {
-  for (int32_t i = pdfium::CollectionSize<int32_t>(m_TabOrderWidgetArray) - 1;
+  for (int32_t i = fxcrt::CollectionSize<int32_t>(m_TabOrderWidgetArray) - 1;
        i >= 0; i--) {
-    if (PageWidgetFilter(m_TabOrderWidgetArray[i].Get(), m_dwFilter, true,
-                         m_bIgnoreRelevant)) {
+    if (PageWidgetFilter(m_TabOrderWidgetArray[i]->GetFFWidget(), m_dwFilter,
+                         true, m_bIgnoreRelevant)) {
       m_iCurWidget = i;
-      return m_TabOrderWidgetArray[m_iCurWidget].Get();
+      return m_TabOrderWidgetArray[m_iCurWidget]->GetFFWidget();
     }
   }
   return nullptr;
@@ -271,11 +368,11 @@
 
 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::MoveToNext() {
   for (int32_t i = m_iCurWidget + 1;
-       i < pdfium::CollectionSize<int32_t>(m_TabOrderWidgetArray); i++) {
-    if (PageWidgetFilter(m_TabOrderWidgetArray[i].Get(), m_dwFilter, true,
-                         m_bIgnoreRelevant)) {
+       i < fxcrt::CollectionSize<int32_t>(m_TabOrderWidgetArray); i++) {
+    if (PageWidgetFilter(m_TabOrderWidgetArray[i]->GetFFWidget(), m_dwFilter,
+                         true, m_bIgnoreRelevant)) {
       m_iCurWidget = i;
-      return m_TabOrderWidgetArray[m_iCurWidget].Get();
+      return m_TabOrderWidgetArray[m_iCurWidget]->GetFFWidget();
     }
   }
   m_iCurWidget = -1;
@@ -284,10 +381,10 @@
 
 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::MoveToPrevious() {
   for (int32_t i = m_iCurWidget - 1; i >= 0; i--) {
-    if (PageWidgetFilter(m_TabOrderWidgetArray[i].Get(), m_dwFilter, true,
-                         m_bIgnoreRelevant)) {
+    if (PageWidgetFilter(m_TabOrderWidgetArray[i]->GetFFWidget(), m_dwFilter,
+                         true, m_bIgnoreRelevant)) {
       m_iCurWidget = i;
-      return m_TabOrderWidgetArray[m_iCurWidget].Get();
+      return m_TabOrderWidgetArray[m_iCurWidget]->GetFFWidget();
     }
   }
   m_iCurWidget = -1;
@@ -295,18 +392,19 @@
 }
 
 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::GetCurrentWidget() {
-  return m_iCurWidget >= 0 ? m_TabOrderWidgetArray[m_iCurWidget].Get()
+  return m_iCurWidget >= 0 ? m_TabOrderWidgetArray[m_iCurWidget]->GetFFWidget()
                            : nullptr;
 }
 
 bool CXFA_FFTabOrderPageWidgetIterator::SetCurrentWidget(
     CXFA_FFWidget* hWidget) {
   auto it = std::find(m_TabOrderWidgetArray.begin(),
-                      m_TabOrderWidgetArray.end(), hWidget);
+                      m_TabOrderWidgetArray.end(), hWidget->GetLayoutItem());
   if (it == m_TabOrderWidgetArray.end())
     return false;
 
-  m_iCurWidget = it - m_TabOrderWidgetArray.begin();
+  m_iCurWidget =
+      pdfium::base::checked_cast<int32_t>(it - m_TabOrderWidgetArray.begin());
   return true;
 }
 
@@ -318,10 +416,10 @@
     CXFA_Traverse* pTraverse =
         pTraversal->GetChild<CXFA_Traverse>(0, XFA_Element::Traverse, false);
     if (pTraverse) {
-      Optional<WideString> traverseWidgetName =
+      absl::optional<WideString> traverseWidgetName =
           pTraverse->JSObject()->TryAttribute(XFA_Attribute::Ref, true);
-      if (traverseWidgetName)
-        return FindWidgetByName(*traverseWidgetName, pWidget);
+      if (traverseWidgetName.has_value())
+        return FindWidgetByName(traverseWidgetName.value(), pWidget);
     }
   }
   return nullptr;
@@ -335,137 +433,61 @@
 void CXFA_FFTabOrderPageWidgetIterator::CreateTabOrderWidgetArray() {
   m_TabOrderWidgetArray.clear();
 
-  std::vector<CXFA_FFWidget*> SpaceOrderWidgetArray;
-  CreateSpaceOrderWidgetArray(&SpaceOrderWidgetArray);
-  if (SpaceOrderWidgetArray.empty())
+  const std::vector<CXFA_ContentLayoutItem*> items =
+      CreateSpaceOrderLayoutItems();
+  if (items.empty())
     return;
 
-  int32_t nWidgetCount = pdfium::CollectionSize<int32_t>(SpaceOrderWidgetArray);
-  CXFA_FFWidget* hWidget = SpaceOrderWidgetArray[0];
-  while (pdfium::CollectionSize<int32_t>(m_TabOrderWidgetArray) <
-         nWidgetCount) {
-    if (!pdfium::ContainsValue(m_TabOrderWidgetArray, hWidget)) {
-      m_TabOrderWidgetArray.emplace_back(hWidget);
-      CXFA_Node* pNode = hWidget->GetNode();
-      if (pNode->GetFFWidgetType() == XFA_FFWidgetType::kExclGroup) {
-        auto it = std::find(SpaceOrderWidgetArray.begin(),
-                            SpaceOrderWidgetArray.end(), hWidget);
-        int32_t iWidgetIndex = it != SpaceOrderWidgetArray.end()
-                                   ? it - SpaceOrderWidgetArray.begin() + 1
-                                   : 0;
+  CXFA_ContentLayoutItem* item = items[0];
+  while (m_TabOrderWidgetArray.size() < items.size()) {
+    if (!pdfium::Contains(m_TabOrderWidgetArray, item)) {
+      m_TabOrderWidgetArray.emplace_back(item);
+      CXFA_Node* node = item->GetFFWidget()->GetNode();
+      if (node->GetFFWidgetType() == XFA_FFWidgetType::kExclGroup) {
+        auto it = std::find(items.begin(), items.end(), item);
+        size_t index = it != items.end() ? it - items.begin() + 1 : 0;
         while (true) {
-          CXFA_FFWidget* radio =
-              SpaceOrderWidgetArray[iWidgetIndex % nWidgetCount];
-          if (radio->GetNode()->GetExclGroupIfExists() != pNode)
+          CXFA_FFWidget* radio = items[index % items.size()]->GetFFWidget();
+          if (radio->GetNode()->GetExclGroupIfExists() != node)
             break;
-          if (!pdfium::ContainsValue(m_TabOrderWidgetArray, hWidget))
-            m_TabOrderWidgetArray.emplace_back(radio);
-
-          iWidgetIndex++;
+          if (!pdfium::Contains(m_TabOrderWidgetArray, item))
+            m_TabOrderWidgetArray.emplace_back(radio->GetLayoutItem());
+          ++index;
         }
       }
-      if (CXFA_FFWidget* hTraverseWidget = GetTraverseWidget(hWidget)) {
-        hWidget = hTraverseWidget;
+      CXFA_FFWidget* next_widget = GetTraverseWidget(item->GetFFWidget());
+      if (next_widget) {
+        item = next_widget->GetLayoutItem();
         continue;
       }
     }
-    auto it = std::find(SpaceOrderWidgetArray.begin(),
-                        SpaceOrderWidgetArray.end(), hWidget);
-    int32_t iWidgetIndex = it != SpaceOrderWidgetArray.end()
-                               ? it - SpaceOrderWidgetArray.begin() + 1
-                               : 0;
-    hWidget = SpaceOrderWidgetArray[iWidgetIndex % nWidgetCount];
+    auto it = std::find(items.begin(), items.end(), item);
+    size_t index = it != items.end() ? it - items.begin() + 1 : 0;
+    item = items[index % items.size()];
   }
 }
 
-void CXFA_FFTabOrderPageWidgetIterator::OrderContainer(
-    CXFA_LayoutItemIterator* sIterator,
-    CXFA_LayoutItem* pViewItem,
-    CXFA_TabParam* pContainer,
-    bool* bCurrentItem,
-    bool* bContentArea,
-    bool bMasterPage) {
-  std::vector<std::unique_ptr<CXFA_TabParam>> tabParams;
-  CXFA_LayoutItem* pSearchItem = sIterator->MoveToNext();
-  while (pSearchItem) {
-    if (!pSearchItem->IsContentLayoutItem()) {
-      *bContentArea = true;
-      pSearchItem = sIterator->MoveToNext();
-      continue;
-    }
-    if (bMasterPage && *bContentArea) {
-      break;
-    }
-    if (bMasterPage || *bContentArea) {
-      CXFA_FFWidget* hWidget = GetWidget(pSearchItem);
-      if (!hWidget) {
-        pSearchItem = sIterator->MoveToNext();
-        continue;
-      }
-      if (pViewItem && (pSearchItem->GetParent() != pViewItem)) {
-        *bCurrentItem = true;
-        break;
-      }
-      tabParams.push_back(pdfium::MakeUnique<CXFA_TabParam>(hWidget));
-      if (IsLayoutElement(pSearchItem->GetFormNode()->GetElementType(), true)) {
-        OrderContainer(sIterator, pSearchItem, tabParams.back().get(),
-                       bCurrentItem, bContentArea, bMasterPage);
-      }
-    }
-    if (*bCurrentItem) {
-      pSearchItem = sIterator->GetCurrent();
-      *bCurrentItem = false;
-    } else {
-      pSearchItem = sIterator->MoveToNext();
-    }
-  }
-  std::sort(tabParams.begin(), tabParams.end(),
-            [](const std::unique_ptr<CXFA_TabParam>& arg1,
-               const std::unique_ptr<CXFA_TabParam>& arg2) {
-              const CFX_RectF& rt1 = arg1->GetWidget()->GetWidgetRect();
-              const CFX_RectF& rt2 = arg2->GetWidget()->GetWidgetRect();
-              if (rt1.top - rt2.top >= kXFAWidgetPrecision)
-                return rt1.top < rt2.top;
-              return rt1.left < rt2.left;
-            });
-  for (const auto& pParam : tabParams)
-    pContainer->AppendTabParam(pParam.get());
-}
-
-void CXFA_FFTabOrderPageWidgetIterator::CreateSpaceOrderWidgetArray(
-    std::vector<CXFA_FFWidget*>* WidgetArray) {
-  CXFA_LayoutItemIterator sIterator(m_pPageView->GetLayoutItem());
-  auto pParam = pdfium::MakeUnique<CXFA_TabParam>(nullptr);
+std::vector<CXFA_ContentLayoutItem*>
+CXFA_FFTabOrderPageWidgetIterator::CreateSpaceOrderLayoutItems() {
+  std::vector<CXFA_ContentLayoutItem*> items;
+  CXFA_LayoutItemIterator sIterator(m_pPageViewLayout.Get());
+  CXFA_TabParam tabparam;
   bool bCurrentItem = false;
   bool bContentArea = false;
-  OrderContainer(&sIterator, nullptr, pParam.get(), &bCurrentItem,
-                 &bContentArea, false);
-  WidgetArray->insert(WidgetArray->end(), pParam->GetChildren().begin(),
-                      pParam->GetChildren().end());
+  OrderContainer(&sIterator, nullptr, &tabparam, &bCurrentItem, &bContentArea,
+                 false);
+  items.reserve(tabparam.GetChildren().size());
+  for (const auto& layout_item : tabparam.GetChildren())
+    items.push_back(layout_item);
 
   sIterator.Reset();
   bCurrentItem = false;
   bContentArea = false;
-  pParam->ClearChildren();
-  OrderContainer(&sIterator, nullptr, pParam.get(), &bCurrentItem,
-                 &bContentArea, true);
-  WidgetArray->insert(WidgetArray->end(), pParam->GetChildren().begin(),
-                      pParam->GetChildren().end());
+  tabparam.ClearChildren();
+  OrderContainer(&sIterator, nullptr, &tabparam, &bCurrentItem, &bContentArea,
+                 true);
+  for (const auto& layout_item : tabparam.GetChildren())
+    items.push_back(layout_item);
+
+  return items;
 }
-
-CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::GetWidget(
-    CXFA_LayoutItem* pLayoutItem) {
-  CXFA_FFWidget* pWidget = XFA_GetWidgetFromLayoutItem(pLayoutItem);
-  if (!pWidget)
-    return nullptr;
-
-  if (!pWidget->IsLoaded() &&
-      pWidget->GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_Visible)) {
-    pWidget->LoadWidget();
-  }
-  return pWidget;
-}
-
-CXFA_TabParam::CXFA_TabParam(CXFA_FFWidget* pWidget) : m_pWidget(pWidget) {}
-
-CXFA_TabParam::~CXFA_TabParam() {}
diff --git a/xfa/fxfa/cxfa_ffpageview.h b/xfa/fxfa/cxfa_ffpageview.h
index 65c0b95..32dd1d9 100644
--- a/xfa/fxfa/cxfa_ffpageview.h
+++ b/xfa/fxfa/cxfa_ffpageview.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,15 @@
 #ifndef XFA_FXFA_CXFA_FFPAGEVIEW_H_
 #define XFA_FXFA_CXFA_FFPAGEVIEW_H_
 
-#include <memory>
 #include <vector>
 
-#include "core/fxcrt/observed_ptr.h"
+#include "core/fxcrt/mask.h"
+#include "core/fxcrt/widestring.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
+#include "xfa/fxfa/cxfa_ffwidget.h"
 #include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
 #include "xfa/fxfa/layout/cxfa_traversestrategy_layoutitem.h"
 #include "xfa/fxfa/layout/cxfa_viewlayoutitem.h"
@@ -18,36 +23,41 @@
 class CXFA_FFWidget;
 class CXFA_FFDocView;
 
-class CXFA_FFPageView : public Observable {
+class CXFA_FFPageView final : public cppgc::GarbageCollected<CXFA_FFPageView> {
  public:
-  CXFA_FFPageView(CXFA_FFDocView* pDocView, CXFA_Node* pPageArea);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFPageView();
 
-  CXFA_ViewLayoutItem* GetLayoutItem() const { return m_pLayoutItem.Get(); }
+  void Trace(cppgc::Visitor* visitor) const;
+
+  CXFA_ViewLayoutItem* GetLayoutItem() const { return m_pLayoutItem; }
   void SetLayoutItem(CXFA_ViewLayoutItem* pItem) { m_pLayoutItem = pItem; }
 
   CXFA_FFDocView* GetDocView() const;
   CFX_RectF GetPageViewRect() const;
   CFX_Matrix GetDisplayMatrix(const FX_RECT& rtDisp, int32_t iRotate) const;
 
-  // These always return a non-null iterator.
-  std::unique_ptr<IXFA_WidgetIterator> CreateFormWidgetIterator(
-      uint32_t dwWidgetFilter);
-  std::unique_ptr<IXFA_WidgetIterator> CreateTraverseWidgetIterator(
-      uint32_t dwWidgetFilter);
+  // This always returns a non-null iterator from the gc heap.
+  CXFA_FFWidget::IteratorIface* CreateGCedTraverseWidgetIterator(
+      Mask<XFA_WidgetStatus> dwWidgetFilter);
 
  private:
-  UnownedPtr<CXFA_Node> const m_pPageArea;
-  UnownedPtr<CXFA_FFDocView> const m_pDocView;
-  UnownedPtr<CXFA_ViewLayoutItem> m_pLayoutItem;
+  CXFA_FFPageView(CXFA_FFDocView* pDocView, CXFA_Node* pPageArea);
+
+  cppgc::Member<CXFA_Node> const m_pPageArea;
+  cppgc::Member<CXFA_FFDocView> const m_pDocView;
+  cppgc::Member<CXFA_ViewLayoutItem> m_pLayoutItem;
 };
 
-class CXFA_FFPageWidgetIterator final : public IXFA_WidgetIterator {
+class CXFA_FFPageWidgetIterator final : public CXFA_FFWidget::IteratorIface {
+  CPPGC_STACK_ALLOCATED();
+
  public:
-  CXFA_FFPageWidgetIterator(CXFA_FFPageView* pPageView, uint32_t dwFilter);
+  CXFA_FFPageWidgetIterator(CXFA_FFPageView* pPageView,
+                            Mask<XFA_WidgetStatus> dwFilter);
   ~CXFA_FFPageWidgetIterator() override;
 
-  void Reset() override;
+  // CXFA_FFWidget::IteratorIface:
   CXFA_FFWidget* MoveToFirst() override;
   CXFA_FFWidget* MoveToLast() override;
   CXFA_FFWidget* MoveToNext() override;
@@ -56,37 +66,21 @@
   bool SetCurrentWidget(CXFA_FFWidget* hWidget) override;
 
  private:
-  CXFA_FFWidget* GetWidget(CXFA_LayoutItem* pLayoutItem);
-
-  UnownedPtr<CXFA_FFPageView> m_pPageView;
-  UnownedPtr<CXFA_FFWidget> m_hCurWidget;
-  uint32_t m_dwFilter;
-  bool m_bIgnoreRelevant;
   CXFA_LayoutItemIterator m_sIterator;
+  const Mask<XFA_WidgetStatus> m_dwFilter;
+  const bool m_bIgnoreRelevant;
 };
 
-class CXFA_TabParam {
+class CXFA_FFTabOrderPageWidgetIterator final
+    : public cppgc::GarbageCollected<CXFA_FFTabOrderPageWidgetIterator>,
+      public CXFA_FFWidget::IteratorIface {
  public:
-  explicit CXFA_TabParam(CXFA_FFWidget* pWidget);
-  ~CXFA_TabParam();
-
-  void AppendTabParam(CXFA_TabParam* pParam);
-  void ClearChildren();
-  CXFA_FFWidget* GetWidget() const { return m_pWidget.Get(); }
-  const std::vector<CXFA_FFWidget*>& GetChildren() const { return m_Children; }
-
- private:
-  UnownedPtr<CXFA_FFWidget> const m_pWidget;
-  std::vector<CXFA_FFWidget*> m_Children;
-};
-
-class CXFA_FFTabOrderPageWidgetIterator final : public IXFA_WidgetIterator {
- public:
-  CXFA_FFTabOrderPageWidgetIterator(CXFA_FFPageView* pPageView,
-                                    uint32_t dwFilter);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFTabOrderPageWidgetIterator() override;
 
-  void Reset() override;
+  void Trace(cppgc::Visitor* visitor) const;
+
+  // CXFA_FFWidget::IteratorIface:
   CXFA_FFWidget* MoveToFirst() override;
   CXFA_FFWidget* MoveToLast() override;
   CXFA_FFWidget* MoveToNext() override;
@@ -95,24 +89,20 @@
   bool SetCurrentWidget(CXFA_FFWidget* hWidget) override;
 
  private:
+  CXFA_FFTabOrderPageWidgetIterator(CXFA_FFPageView* pPageView,
+                                    Mask<XFA_WidgetStatus> dwFilter);
+
   CXFA_FFWidget* GetTraverseWidget(CXFA_FFWidget* pWidget);
   CXFA_FFWidget* FindWidgetByName(const WideString& wsWidgetName,
                                   CXFA_FFWidget* pRefWidget);
   void CreateTabOrderWidgetArray();
-  void CreateSpaceOrderWidgetArray(std::vector<CXFA_FFWidget*>* WidgetArray);
-  CXFA_FFWidget* GetWidget(CXFA_LayoutItem* pLayoutItem);
-  void OrderContainer(CXFA_LayoutItemIterator* sIterator,
-                      CXFA_LayoutItem* pViewItem,
-                      CXFA_TabParam* pContainer,
-                      bool* bCurrentItem,
-                      bool* bContentArea,
-                      bool bMasterPage);
+  std::vector<CXFA_ContentLayoutItem*> CreateSpaceOrderLayoutItems();
 
-  std::vector<UnownedPtr<CXFA_FFWidget>> m_TabOrderWidgetArray;
-  UnownedPtr<CXFA_FFPageView> m_pPageView;
-  uint32_t m_dwFilter;
-  int32_t m_iCurWidget;
-  bool m_bIgnoreRelevant;
+  cppgc::Member<CXFA_ViewLayoutItem> const m_pPageViewLayout;
+  std::vector<cppgc::Member<CXFA_ContentLayoutItem>> m_TabOrderWidgetArray;
+  const Mask<XFA_WidgetStatus> m_dwFilter;
+  int32_t m_iCurWidget = -1;
+  const bool m_bIgnoreRelevant;
 };
 
 #endif  // XFA_FXFA_CXFA_FFPAGEVIEW_H_
diff --git a/xfa/fxfa/cxfa_ffpasswordedit.cpp b/xfa/fxfa/cxfa_ffpasswordedit.cpp
index 8019c5c..3fb5760 100644
--- a/xfa/fxfa/cxfa_ffpasswordedit.cpp
+++ b/xfa/fxfa/cxfa_ffpasswordedit.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,7 @@
 
 #include "xfa/fxfa/cxfa_ffpasswordedit.h"
 
-#include <utility>
-
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 #include "xfa/fwl/cfwl_edit.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
@@ -21,22 +19,28 @@
 
 CXFA_FFPasswordEdit::~CXFA_FFPasswordEdit() = default;
 
+void CXFA_FFPasswordEdit::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FFTextEdit::Trace(visitor);
+  visitor->Trace(password_node_);
+}
+
 bool CXFA_FFPasswordEdit::LoadWidget() {
-  ASSERT(!IsLoaded());
-  auto pNewEdit = pdfium::MakeUnique<CFWL_Edit>(
-      GetFWLApp(), pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr);
-  CFWL_Edit* pWidget = pNewEdit.get();
-  SetNormalWidget(std::move(pNewEdit));
+  DCHECK(!IsLoaded());
+
+  CFWL_Edit* pWidget = cppgc::MakeGarbageCollected<CFWL_Edit>(
+      GetFWLApp()->GetHeap()->GetAllocationHandle(), GetFWLApp(),
+      CFWL_Widget::Properties(), nullptr);
+  SetNormalWidget(pWidget);
   pWidget->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver = pWidget->GetOwnerApp()->GetNoteDriver();
+  CFWL_NoteDriver* pNoteDriver = pWidget->GetFWLApp()->GetNoteDriver();
   pNoteDriver->RegisterEventTarget(pWidget, pWidget);
   m_pOldDelegate = pWidget->GetDelegate();
   pWidget->SetDelegate(this);
 
   {
     CFWL_Widget::ScopedUpdateLock update_lock(pWidget);
-    pWidget->SetText(m_pNode->GetValue(XFA_VALUEPICTURE_Display));
+    pWidget->SetText(m_pNode->GetValue(XFA_ValuePicture::kDisplay));
     UpdateWidgetProperty();
   }
 
@@ -62,5 +66,5 @@
     dwExtendedStyle |= FWL_STYLEEXT_EDT_ReadOnly;
 
   dwExtendedStyle |= GetAlignment();
-  GetNormalWidget()->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
+  GetNormalWidget()->ModifyStyleExts(dwExtendedStyle, 0xFFFFFFFF);
 }
diff --git a/xfa/fxfa/cxfa_ffpasswordedit.h b/xfa/fxfa/cxfa_ffpasswordedit.h
index a96efa0..b8f649b 100644
--- a/xfa/fxfa/cxfa_ffpasswordedit.h
+++ b/xfa/fxfa/cxfa_ffpasswordedit.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,22 +7,28 @@
 #ifndef XFA_FXFA_CXFA_FFPASSWORDEDIT_H_
 #define XFA_FXFA_CXFA_FFPASSWORDEDIT_H_
 
-#include "core/fxcrt/unowned_ptr.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fxfa/cxfa_fftextedit.h"
 
 class CXFA_PasswordEdit;
 
 class CXFA_FFPasswordEdit final : public CXFA_FFTextEdit {
  public:
-  CXFA_FFPasswordEdit(CXFA_Node* pNode, CXFA_PasswordEdit* password_node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFPasswordEdit() override;
 
+  void Trace(cppgc::Visitor* visitor) const override;
+
   // CXFA_FFTextEdit
   bool LoadWidget() override;
   void UpdateWidgetProperty() override;
 
  private:
-  UnownedPtr<CXFA_PasswordEdit> const password_node_;
+  CXFA_FFPasswordEdit(CXFA_Node* pNode, CXFA_PasswordEdit* password_node);
+
+  cppgc::Member<CXFA_PasswordEdit> const password_node_;
 };
 
 #endif  // XFA_FXFA_CXFA_FFPASSWORDEDIT_H_
diff --git a/xfa/fxfa/cxfa_ffpushbutton.cpp b/xfa/fxfa/cxfa_ffpushbutton.cpp
index 7f2a06e..19d695b 100644
--- a/xfa/fxfa/cxfa_ffpushbutton.cpp
+++ b/xfa/fxfa/cxfa_ffpushbutton.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/cxfa_ffpushbutton.h"
 
-#include <utility>
-
-#include "core/fxge/render_defines.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "v8/include/cppgc/visitor.h"
+#include "xfa/fgas/graphics/cfgas_gecolor.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fwl/cfwl_pushbutton.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
+#include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_fffield.h"
 #include "xfa/fxfa/cxfa_ffpageview.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
@@ -23,15 +24,23 @@
 #include "xfa/fxfa/parser/cxfa_button.h"
 #include "xfa/fxfa/parser/cxfa_caption.h"
 #include "xfa/fxfa/parser/cxfa_edge.h"
-#include "xfa/fxgraphics/cxfa_gecolor.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
 
 CXFA_FFPushButton::CXFA_FFPushButton(CXFA_Node* pNode, CXFA_Button* button)
     : CXFA_FFField(pNode), button_(button) {}
 
 CXFA_FFPushButton::~CXFA_FFPushButton() = default;
 
-void CXFA_FFPushButton::RenderWidget(CXFA_Graphics* pGS,
+void CXFA_FFPushButton::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FFField::Trace(visitor);
+  visitor->Trace(m_pRolloverTextLayout);
+  visitor->Trace(m_pDownTextLayout);
+  visitor->Trace(m_pRollProvider);
+  visitor->Trace(m_pDownProvider);
+  visitor->Trace(m_pOldDelegate);
+  visitor->Trace(button_);
+}
+
+void CXFA_FFPushButton::RenderWidget(CFGAS_GEGraphics* pGS,
                                      const CFX_Matrix& matrix,
                                      HighlightOption highlight) {
   if (!HasVisibleStatus())
@@ -50,15 +59,16 @@
 }
 
 bool CXFA_FFPushButton::LoadWidget() {
-  ASSERT(!IsLoaded());
-  auto pNew = pdfium::MakeUnique<CFWL_PushButton>(GetFWLApp());
-  CFWL_PushButton* pPushButton = pNew.get();
+  DCHECK(!IsLoaded());
+
+  CFWL_PushButton* pPushButton = cppgc::MakeGarbageCollected<CFWL_PushButton>(
+      GetFWLApp()->GetHeap()->GetAllocationHandle(), GetFWLApp());
   m_pOldDelegate = pPushButton->GetDelegate();
   pPushButton->SetDelegate(this);
-  SetNormalWidget(std::move(pNew));
+  SetNormalWidget(pPushButton);
   pPushButton->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver = pPushButton->GetOwnerApp()->GetNoteDriver();
+  CFWL_NoteDriver* pNoteDriver = pPushButton->GetFWLApp()->GetNoteDriver();
   pNoteDriver->RegisterEventTarget(pPushButton, pPushButton);
 
   {
@@ -85,22 +95,22 @@
     default:
       break;
   }
-  GetNormalWidget()->ModifyStylesEx(dwStyleEx, 0xFFFFFFFF);
+  GetNormalWidget()->ModifyStyleExts(dwStyleEx, 0xFFFFFFFF);
 }
 
 bool CXFA_FFPushButton::PerformLayout() {
   CXFA_FFWidget::PerformLayout();
   CFX_RectF rtWidget = GetRectWithoutRotate();
 
-  m_rtUI = rtWidget;
+  m_UIRect = rtWidget;
   CXFA_Margin* margin = m_pNode->GetMarginIfExists();
   XFA_RectWithoutMargin(&rtWidget, margin);
 
-  m_rtCaption = rtWidget;
+  m_CaptionRect = rtWidget;
 
   CXFA_Caption* caption = m_pNode->GetCaptionIfExists();
   CXFA_Margin* captionMargin = caption ? caption->GetMarginIfExists() : nullptr;
-  XFA_RectWithoutMargin(&m_rtCaption, captionMargin);
+  XFA_RectWithoutMargin(&m_CaptionRect, captionMargin);
 
   LayoutHighlightCaption();
   SetFWLRect();
@@ -134,25 +144,26 @@
 
   if (m_pNode->HasButtonRollover()) {
     if (!m_pRollProvider) {
-      m_pRollProvider = pdfium::MakeUnique<CXFA_TextProvider>(
-          m_pNode.Get(), XFA_TEXTPROVIDERTYPE_Rollover);
+      m_pRollProvider = cppgc::MakeGarbageCollected<CXFA_TextProvider>(
+          GetDoc()->GetHeap()->GetAllocationHandle(), m_pNode.Get(),
+          CXFA_TextProvider::Type::kRollover);
     }
-    m_pRolloverTextLayout =
-        pdfium::MakeUnique<CXFA_TextLayout>(GetDoc(), m_pRollProvider.get());
+    m_pRolloverTextLayout = cppgc::MakeGarbageCollected<CXFA_TextLayout>(
+        GetDoc()->GetHeap()->GetAllocationHandle(), GetDoc(), m_pRollProvider);
   }
-
   if (m_pNode->HasButtonDown()) {
     if (!m_pDownProvider) {
-      m_pDownProvider = pdfium::MakeUnique<CXFA_TextProvider>(
-          m_pNode.Get(), XFA_TEXTPROVIDERTYPE_Down);
+      m_pDownProvider = cppgc::MakeGarbageCollected<CXFA_TextProvider>(
+          GetDoc()->GetHeap()->GetAllocationHandle(), m_pNode.Get(),
+          CXFA_TextProvider::Type::kDown);
     }
-    m_pDownTextLayout =
-        pdfium::MakeUnique<CXFA_TextLayout>(GetDoc(), m_pDownProvider.get());
+    m_pDownTextLayout = cppgc::MakeGarbageCollected<CXFA_TextLayout>(
+        GetDoc()->GetHeap()->GetAllocationHandle(), GetDoc(), m_pDownProvider);
   }
 }
 
 void CXFA_FFPushButton::LayoutHighlightCaption() {
-  CFX_SizeF sz(m_rtCaption.width, m_rtCaption.height);
+  CFX_SizeF sz(m_CaptionRect.width, m_CaptionRect.height);
   LayoutCaption();
   if (m_pRolloverTextLayout)
     m_pRolloverTextLayout->Layout(sz);
@@ -160,7 +171,7 @@
     m_pDownTextLayout->Layout(sz);
 }
 
-void CXFA_FFPushButton::RenderHighlightCaption(CXFA_Graphics* pGS,
+void CXFA_FFPushButton::RenderHighlightCaption(CFGAS_GEGraphics* pGS,
                                                CFX_Matrix* pMatrix) {
   CXFA_TextLayout* pCapTextLayout = m_pNode->GetCaptionTextLayout();
   CXFA_Caption* caption = m_pNode->GetCaptionIfExists();
@@ -168,9 +179,9 @@
     return;
 
   CFX_RenderDevice* pRenderDevice = pGS->GetRenderDevice();
-  CFX_RectF rtClip = m_rtCaption;
+  CFX_RectF rtClip = m_CaptionRect;
   rtClip.Intersect(GetRectWithoutRotate());
-  CFX_Matrix mt(1, 0, 0, 1, m_rtCaption.left, m_rtCaption.top);
+  CFX_Matrix mt(1, 0, 0, 1, m_CaptionRect.left, m_CaptionRect.top);
   if (pMatrix) {
     rtClip = pMatrix->TransformRect(rtClip);
     mt.Concat(*pMatrix);
@@ -199,34 +210,35 @@
   CXFA_FFField::OnProcessEvent(pEvent);
 }
 
-void CXFA_FFPushButton::OnDrawWidget(CXFA_Graphics* pGraphics,
+void CXFA_FFPushButton::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                                      const CFX_Matrix& matrix) {
   auto* pWidget = GetNormalWidget();
-  if (pWidget->GetStylesEx() & XFA_FWL_PSBSTYLEEXT_HiliteInverted) {
+  if (pWidget->GetStyleExts() & XFA_FWL_PSBSTYLEEXT_HiliteInverted) {
     if ((pWidget->GetStates() & FWL_STATE_PSB_Pressed) &&
         (pWidget->GetStates() & FWL_STATE_PSB_Hovered)) {
       CFX_RectF rtFill(0, 0, pWidget->GetWidgetRect().Size());
       float fLineWith = GetLineWidth();
       rtFill.Deflate(fLineWith, fLineWith);
-      CXFA_GEPath path;
+      CFGAS_GEPath path;
       path.AddRectangle(rtFill.left, rtFill.top, rtFill.width, rtFill.height);
-      pGraphics->SetFillColor(CXFA_GEColor(ArgbEncode(128, 128, 255, 255)));
-      pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
+      pGraphics->SetFillColor(CFGAS_GEColor(ArgbEncode(128, 128, 255, 255)));
+      pGraphics->FillPath(path, CFX_FillRenderOptions::FillType::kWinding,
+                          matrix);
     }
     return;
   }
 
-  if (pWidget->GetStylesEx() & XFA_FWL_PSBSTYLEEXT_HiliteOutLine) {
+  if (pWidget->GetStyleExts() & XFA_FWL_PSBSTYLEEXT_HiliteOutLine) {
     if ((pWidget->GetStates() & FWL_STATE_PSB_Pressed) &&
         (pWidget->GetStates() & FWL_STATE_PSB_Hovered)) {
       float fLineWidth = GetLineWidth();
-      pGraphics->SetStrokeColor(CXFA_GEColor(ArgbEncode(255, 128, 255, 255)));
+      pGraphics->SetStrokeColor(CFGAS_GEColor(ArgbEncode(255, 128, 255, 255)));
       pGraphics->SetLineWidth(fLineWidth);
 
-      CXFA_GEPath path;
+      CFGAS_GEPath path;
       CFX_RectF rect = pWidget->GetWidgetRect();
       path.AddRectangle(0, 0, rect.width, rect.height);
-      pGraphics->StrokePath(&path, &matrix);
+      pGraphics->StrokePath(path, matrix);
     }
   }
 }
diff --git a/xfa/fxfa/cxfa_ffpushbutton.h b/xfa/fxfa/cxfa_ffpushbutton.h
index e4b34b5..9c07714 100644
--- a/xfa/fxfa/cxfa_ffpushbutton.h
+++ b/xfa/fxfa/cxfa_ffpushbutton.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,9 +7,7 @@
 #ifndef XFA_FXFA_CXFA_FFPUSHBUTTON_H_
 #define XFA_FXFA_CXFA_FFPUSHBUTTON_H_
 
-#include <memory>
-
-#include "core/fxcrt/unowned_ptr.h"
+#include "v8/include/cppgc/member.h"
 #include "xfa/fxfa/cxfa_fffield.h"
 
 #define XFA_FWL_PSBSTYLEEXT_HiliteInverted (1L << 0)
@@ -22,11 +20,13 @@
 
 class CXFA_FFPushButton final : public CXFA_FFField {
  public:
-  CXFA_FFPushButton(CXFA_Node* pNode, CXFA_Button* button);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFPushButton() override;
 
+  void Trace(cppgc::Visitor* visitor) const override;
+
   // CXFA_FFField
-  void RenderWidget(CXFA_Graphics* pGS,
+  void RenderWidget(CFGAS_GEGraphics* pGS,
                     const CFX_Matrix& matrix,
                     HighlightOption highlight) override;
   bool LoadWidget() override;
@@ -34,24 +34,26 @@
   void UpdateWidgetProperty() override;
   void OnProcessMessage(CFWL_Message* pMessage) override;
   void OnProcessEvent(CFWL_Event* pEvent) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
+  void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix) override;
   FormFieldType GetFormFieldType() override;
 
  private:
+  CXFA_FFPushButton(CXFA_Node* pNode, CXFA_Button* button);
+
   void LoadHighlightCaption();
   void LayoutHighlightCaption();
-  void RenderHighlightCaption(CXFA_Graphics* pGS, CFX_Matrix* pMatrix);
+  void RenderHighlightCaption(CFGAS_GEGraphics* pGS, CFX_Matrix* pMatrix);
   float GetLineWidth();
   FX_ARGB GetLineColor();
   FX_ARGB GetFillColor();
 
-  std::unique_ptr<CXFA_TextLayout> m_pRolloverTextLayout;
-  std::unique_ptr<CXFA_TextLayout> m_pDownTextLayout;
-  std::unique_ptr<CXFA_TextProvider> m_pRollProvider;
-  std::unique_ptr<CXFA_TextProvider> m_pDownProvider;
-  UnownedPtr<IFWL_WidgetDelegate> m_pOldDelegate;
-  UnownedPtr<CXFA_Button> const button_;
+  cppgc::Member<CXFA_TextLayout> m_pRolloverTextLayout;
+  cppgc::Member<CXFA_TextLayout> m_pDownTextLayout;
+  cppgc::Member<CXFA_TextProvider> m_pRollProvider;
+  cppgc::Member<CXFA_TextProvider> m_pDownProvider;
+  cppgc::Member<IFWL_WidgetDelegate> m_pOldDelegate;
+  cppgc::Member<CXFA_Button> const button_;
 };
 
 #endif  // XFA_FXFA_CXFA_FFPUSHBUTTON_H_
diff --git a/xfa/fxfa/cxfa_ffrectangle.cpp b/xfa/fxfa/cxfa_ffrectangle.cpp
index ff5e576..860c816 100644
--- a/xfa/fxfa/cxfa_ffrectangle.cpp
+++ b/xfa/fxfa/cxfa_ffrectangle.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,9 +11,9 @@
 
 CXFA_FFRectangle::CXFA_FFRectangle(CXFA_Node* pNode) : CXFA_FFWidget(pNode) {}
 
-CXFA_FFRectangle::~CXFA_FFRectangle() {}
+CXFA_FFRectangle::~CXFA_FFRectangle() = default;
 
-void CXFA_FFRectangle::RenderWidget(CXFA_Graphics* pGS,
+void CXFA_FFRectangle::RenderWidget(CFGAS_GEGraphics* pGS,
                                     const CFX_Matrix& matrix,
                                     HighlightOption highlight) {
   if (!HasVisibleStatus())
diff --git a/xfa/fxfa/cxfa_ffrectangle.h b/xfa/fxfa/cxfa_ffrectangle.h
index d78d5ff..480364d 100644
--- a/xfa/fxfa/cxfa_ffrectangle.h
+++ b/xfa/fxfa/cxfa_ffrectangle.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,13 +11,16 @@
 
 class CXFA_FFRectangle final : public CXFA_FFWidget {
  public:
-  explicit CXFA_FFRectangle(CXFA_Node* pNode);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFRectangle() override;
 
   // CXFA_FFWidget
-  void RenderWidget(CXFA_Graphics* pGS,
+  void RenderWidget(CFGAS_GEGraphics* pGS,
                     const CFX_Matrix& matrix,
                     HighlightOption highlight) override;
+
+ private:
+  explicit CXFA_FFRectangle(CXFA_Node* pNode);
 };
 
 #endif  // XFA_FXFA_CXFA_FFRECTANGLE_H_
diff --git a/xfa/fxfa/cxfa_ffsignature.cpp b/xfa/fxfa/cxfa_ffsignature.cpp
index b138991..a07a4eb 100644
--- a/xfa/fxfa/cxfa_ffsignature.cpp
+++ b/xfa/fxfa/cxfa_ffsignature.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,7 @@
 
 #include "xfa/fxfa/cxfa_ffsignature.h"
 
+#include "third_party/base/check.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_fffield.h"
 #include "xfa/fxfa/cxfa_ffpageview.h"
@@ -17,11 +18,11 @@
 CXFA_FFSignature::~CXFA_FFSignature() = default;
 
 bool CXFA_FFSignature::LoadWidget() {
-  ASSERT(!IsLoaded());
+  DCHECK(!IsLoaded());
   return CXFA_FFField::LoadWidget();
 }
 
-void CXFA_FFSignature::RenderWidget(CXFA_Graphics* pGS,
+void CXFA_FFSignature::RenderWidget(CFGAS_GEGraphics* pGS,
                                     const CFX_Matrix& matrix,
                                     HighlightOption highlight) {
   if (!HasVisibleStatus())
@@ -32,9 +33,9 @@
 
   CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight);
 
-  DrawBorder(pGS, m_pNode->GetUIBorder(), m_rtUI, mtRotate);
-  RenderCaption(pGS, &mtRotate);
-  DrawHighlight(pGS, &mtRotate, highlight, kSquareShape);
+  DrawBorder(pGS, m_pNode->GetUIBorder(), m_UIRect, mtRotate);
+  RenderCaption(pGS, mtRotate);
+  DrawHighlight(pGS, mtRotate, highlight, kSquareShape);
 }
 
 bool CXFA_FFSignature::OnMouseEnter() {
@@ -45,59 +46,60 @@
   return false;
 }
 
-bool CXFA_FFSignature::AcceptsFocusOnButtonDown(uint32_t dwFlags,
-                                                const CFX_PointF& point,
-                                                FWL_MouseCommand command) {
+bool CXFA_FFSignature::AcceptsFocusOnButtonDown(
+    Mask<XFA_FWL_KeyFlag> dwFlags,
+    const CFX_PointF& point,
+    CFWL_MessageMouse::MouseCommand command) {
   return false;
 }
 
-bool CXFA_FFSignature::OnLButtonDown(uint32_t dwFlags,
+bool CXFA_FFSignature::OnLButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
                                      const CFX_PointF& point) {
   return false;
 }
 
-bool CXFA_FFSignature::OnLButtonUp(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFSignature::OnLButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                   const CFX_PointF& point) {
   return false;
 }
 
-bool CXFA_FFSignature::OnLButtonDblClk(uint32_t dwFlags,
+bool CXFA_FFSignature::OnLButtonDblClk(Mask<XFA_FWL_KeyFlag> dwFlags,
                                        const CFX_PointF& point) {
   return false;
 }
 
-bool CXFA_FFSignature::OnMouseMove(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFSignature::OnMouseMove(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                   const CFX_PointF& point) {
   return false;
 }
 
-bool CXFA_FFSignature::OnMouseWheel(uint32_t dwFlags,
-                                    int16_t zDelta,
-                                    const CFX_PointF& point) {
+bool CXFA_FFSignature::OnMouseWheel(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                    const CFX_PointF& point,
+                                    const CFX_Vector& delta) {
   return false;
 }
 
-bool CXFA_FFSignature::OnRButtonDown(uint32_t dwFlags,
+bool CXFA_FFSignature::OnRButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
                                      const CFX_PointF& point) {
   return false;
 }
 
-bool CXFA_FFSignature::OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFSignature::OnRButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                   const CFX_PointF& point) {
   return false;
 }
 
-bool CXFA_FFSignature::OnRButtonDblClk(uint32_t dwFlags,
+bool CXFA_FFSignature::OnRButtonDblClk(Mask<XFA_FWL_KeyFlag> dwFlags,
                                        const CFX_PointF& point) {
   return false;
 }
 
-bool CXFA_FFSignature::OnKeyDown(uint32_t dwKeyCode, uint32_t dwFlags) {
+bool CXFA_FFSignature::OnKeyDown(XFA_FWL_VKEYCODE dwKeyCode,
+                                 Mask<XFA_FWL_KeyFlag> dwFlags) {
   return false;
 }
 
-bool CXFA_FFSignature::OnKeyUp(uint32_t dwKeyCode, uint32_t dwFlags) {
-  return false;
-}
-
-bool CXFA_FFSignature::OnChar(uint32_t dwChar, uint32_t dwFlags) {
+bool CXFA_FFSignature::OnChar(uint32_t nChar, Mask<XFA_FWL_KeyFlag> dwFlags) {
   return false;
 }
 
@@ -107,15 +109,11 @@
     return FWL_WidgetHit::Client;
   if (!GetRectWithoutRotate().Contains(point))
     return FWL_WidgetHit::Unknown;
-  if (m_rtCaption.Contains(point))
+  if (m_CaptionRect.Contains(point))
     return FWL_WidgetHit::Titlebar;
   return FWL_WidgetHit::Client;
 }
 
-bool CXFA_FFSignature::OnSetCursor(const CFX_PointF& point) {
-  return false;
-}
-
 FormFieldType CXFA_FFSignature::GetFormFieldType() {
   return FormFieldType::kXFA_Signature;
 }
diff --git a/xfa/fxfa/cxfa_ffsignature.h b/xfa/fxfa/cxfa_ffsignature.h
index 1aa00b6..5325c48 100644
--- a/xfa/fxfa/cxfa_ffsignature.h
+++ b/xfa/fxfa/cxfa_ffsignature.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,35 +11,45 @@
 
 class CXFA_FFSignature final : public CXFA_FFField {
  public:
-  explicit CXFA_FFSignature(CXFA_Node* pNode);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFSignature() override;
 
   // CXFA_FFField
-  void RenderWidget(CXFA_Graphics* pGS,
+  void RenderWidget(CFGAS_GEGraphics* pGS,
                     const CFX_Matrix& matrix,
                     HighlightOption highlight) override;
   bool LoadWidget() override;
-  bool AcceptsFocusOnButtonDown(uint32_t dwFlags,
-                                const CFX_PointF& point,
-                                FWL_MouseCommand command) override;
+  bool AcceptsFocusOnButtonDown(
+      Mask<XFA_FWL_KeyFlag> dwFlags,
+      const CFX_PointF& point,
+      CFWL_MessageMouse::MouseCommand command) override;
   bool OnMouseEnter() override;
   bool OnMouseExit() override;
-  bool OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnLButtonUp(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnLButtonDblClk(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnMouseMove(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnMouseWheel(uint32_t dwFlags,
-                    int16_t zDelta,
-                    const CFX_PointF& pointy) override;
-  bool OnRButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnRButtonDblClk(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnKeyDown(uint32_t dwKeyCode, uint32_t dwFlags) override;
-  bool OnKeyUp(uint32_t dwKeyCode, uint32_t dwFlags) override;
-  bool OnChar(uint32_t dwChar, uint32_t dwFlags) override;
-  bool OnSetCursor(const CFX_PointF& point) override;
+  bool OnLButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
+                     const CFX_PointF& point) override;
+  bool OnLButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                   const CFX_PointF& point) override;
+  bool OnLButtonDblClk(Mask<XFA_FWL_KeyFlag> dwFlags,
+                       const CFX_PointF& point) override;
+  bool OnMouseMove(Mask<XFA_FWL_KeyFlag> dwFlags,
+                   const CFX_PointF& point) override;
+  bool OnMouseWheel(Mask<XFA_FWL_KeyFlag> dwFlags,
+                    const CFX_PointF& point,
+                    const CFX_Vector& delta) override;
+  bool OnRButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
+                     const CFX_PointF& point) override;
+  bool OnRButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                   const CFX_PointF& point) override;
+  bool OnRButtonDblClk(Mask<XFA_FWL_KeyFlag> dwFlags,
+                       const CFX_PointF& point) override;
+  bool OnKeyDown(XFA_FWL_VKEYCODE dwKeyCode,
+                 Mask<XFA_FWL_KeyFlag> dwFlags) override;
+  bool OnChar(uint32_t dwChar, Mask<XFA_FWL_KeyFlag> dwFlags) override;
   FWL_WidgetHit HitTest(const CFX_PointF& point) override;
   FormFieldType GetFormFieldType() override;
+
+ private:
+  explicit CXFA_FFSignature(CXFA_Node* pNode);
 };
 
 #endif  // XFA_FXFA_CXFA_FFSIGNATURE_H_
diff --git a/xfa/fxfa/cxfa_fftext.cpp b/xfa/fxfa/cxfa_fftext.cpp
index f4ddbe0..a9604c8 100644
--- a/xfa/fxfa/cxfa_fftext.cpp
+++ b/xfa/fxfa/cxfa_fftext.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,23 +6,21 @@
 
 #include "xfa/fxfa/cxfa_fftext.h"
 
-#include "xfa/fgas/layout/cfx_linkuserdata.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+#include "xfa/fgas/layout/cfgas_linkuserdata.h"
 #include "xfa/fwl/fwl_widgethit.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffpageview.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
-#include "xfa/fxfa/cxfa_pieceline.h"
 #include "xfa/fxfa/cxfa_textlayout.h"
-#include "xfa/fxfa/cxfa_textpiece.h"
 #include "xfa/fxfa/parser/cxfa_margin.h"
-#include "xfa/fxgraphics/cxfa_graphics.h"
 
 CXFA_FFText::CXFA_FFText(CXFA_Node* pNode) : CXFA_FFWidget(pNode) {}
 
-CXFA_FFText::~CXFA_FFText() {}
+CXFA_FFText::~CXFA_FFText() = default;
 
-void CXFA_FFText::RenderWidget(CXFA_Graphics* pGS,
+void CXFA_FFText::RenderWidget(CFGAS_GEGraphics* pGS,
                                const CFX_Matrix& matrix,
                                HighlightOption highlight) {
   if (!HasVisibleStatus())
@@ -84,7 +82,7 @@
 
   pItem = pItem->GetFirst();
   while (pItem) {
-    CFX_RectF rtText = pItem->GetRect(false);
+    CFX_RectF rtText = pItem->GetAbsoluteRect();
     CXFA_Margin* margin = m_pNode->GetMarginIfExists();
     if (margin) {
       if (!pItem->GetPrev())
@@ -99,66 +97,54 @@
   return true;
 }
 
-bool CXFA_FFText::AcceptsFocusOnButtonDown(uint32_t dwFlags,
-                                           const CFX_PointF& point,
-                                           FWL_MouseCommand command) {
-  if (command != FWL_MouseCommand::LeftButtonDown)
-    return false;
-
-  if (!GetRectWithoutRotate().Contains(point))
-    return false;
-
-  const wchar_t* wsURLContent = GetLinkURLAtPoint(point);
-  if (!wsURLContent)
-    return false;
-
-  return true;
+bool CXFA_FFText::AcceptsFocusOnButtonDown(
+    Mask<XFA_FWL_KeyFlag> dwFlags,
+    const CFX_PointF& point,
+    CFWL_MessageMouse::MouseCommand command) {
+  return command == CFWL_MessageMouse::MouseCommand::kLeftButtonDown &&
+         GetRectWithoutRotate().Contains(point) &&
+         !GetLinkURLAtPoint(point).IsEmpty();
 }
 
-bool CXFA_FFText::OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFText::OnLButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                const CFX_PointF& point) {
   SetButtonDown(true);
   return true;
 }
 
-bool CXFA_FFText::OnMouseMove(uint32_t dwFlags, const CFX_PointF& point) {
-  return GetRectWithoutRotate().Contains(point) && !!GetLinkURLAtPoint(point);
+bool CXFA_FFText::OnMouseMove(Mask<XFA_FWL_KeyFlag> dwFlags,
+                              const CFX_PointF& point) {
+  return GetRectWithoutRotate().Contains(point) &&
+         !GetLinkURLAtPoint(point).IsEmpty();
 }
 
-bool CXFA_FFText::OnLButtonUp(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFText::OnLButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                              const CFX_PointF& point) {
   if (!IsButtonDown())
     return false;
 
   SetButtonDown(false);
-  const wchar_t* wsURLContent = GetLinkURLAtPoint(point);
-  if (!wsURLContent)
+  WideString wsURLContent = GetLinkURLAtPoint(point);
+  if (wsURLContent.IsEmpty())
     return false;
 
-  CXFA_FFDoc* pDoc = GetDoc();
-  pDoc->GetDocEnvironment()->GotoURL(pDoc, wsURLContent);
+  GetDoc()->GotoURL(wsURLContent);
   return true;
 }
 
 FWL_WidgetHit CXFA_FFText::HitTest(const CFX_PointF& point) {
-  if (!GetRectWithoutRotate().Contains(point))
-    return FWL_WidgetHit::Unknown;
-  if (!GetLinkURLAtPoint(point))
-    return FWL_WidgetHit::Unknown;
-  return FWL_WidgetHit::HyperLink;
+  if (GetRectWithoutRotate().Contains(point) &&
+      !GetLinkURLAtPoint(point).IsEmpty()) {
+    return FWL_WidgetHit::HyperLink;
+  }
+  return FWL_WidgetHit::Unknown;
 }
 
-const wchar_t* CXFA_FFText::GetLinkURLAtPoint(const CFX_PointF& point) {
+WideString CXFA_FFText::GetLinkURLAtPoint(const CFX_PointF& point) {
   CXFA_TextLayout* pTextLayout = m_pNode->GetTextLayout();
   if (!pTextLayout)
-    return nullptr;
+    return WideString();
 
   CFX_RectF rect = GetRectWithoutRotate();
-  for (const auto& pPieceLine : *pTextLayout->GetPieceLines()) {
-    for (const auto& pPiece : pPieceLine->m_textPieces) {
-      if (pPiece->pLinkData &&
-          pPiece->rtPiece.Contains(point - rect.TopLeft())) {
-        return pPiece->pLinkData->GetLinkURL();
-      }
-    }
-  }
-  return nullptr;
+  return pTextLayout->GetLinkURLAtPoint(point - rect.TopLeft());
 }
diff --git a/xfa/fxfa/cxfa_fftext.h b/xfa/fxfa/cxfa_fftext.h
index 92eafb1..c4bc471 100644
--- a/xfa/fxfa/cxfa_fftext.h
+++ b/xfa/fxfa/cxfa_fftext.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,25 +11,32 @@
 
 class CXFA_FFText final : public CXFA_FFWidget {
  public:
-  explicit CXFA_FFText(CXFA_Node* pNode);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFText() override;
 
   // CXFA_FFWidget
-  bool AcceptsFocusOnButtonDown(uint32_t dwFlags,
-                                const CFX_PointF& point,
-                                FWL_MouseCommand command) override;
-  bool OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnLButtonUp(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnMouseMove(uint32_t dwFlags, const CFX_PointF& point) override;
+  bool AcceptsFocusOnButtonDown(
+      Mask<XFA_FWL_KeyFlag> dwFlags,
+      const CFX_PointF& point,
+      CFWL_MessageMouse::MouseCommand command) override;
+  bool OnLButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
+                     const CFX_PointF& point) override;
+  bool OnLButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                   const CFX_PointF& point) override;
+  bool OnMouseMove(Mask<XFA_FWL_KeyFlag> dwFlags,
+                   const CFX_PointF& point) override;
   FWL_WidgetHit HitTest(const CFX_PointF& point) override;
-  void RenderWidget(CXFA_Graphics* pGS,
+  void RenderWidget(CFGAS_GEGraphics* pGS,
                     const CFX_Matrix& matrix,
                     HighlightOption highlight) override;
   bool IsLoaded() override;
   bool PerformLayout() override;
 
  private:
-  const wchar_t* GetLinkURLAtPoint(const CFX_PointF& point);
+  explicit CXFA_FFText(CXFA_Node* pNode);
+
+  // Returns empty string when no link is present.
+  WideString GetLinkURLAtPoint(const CFX_PointF& point);
 };
 
 #endif  // XFA_FXFA_CXFA_FFTEXT_H_
diff --git a/xfa/fxfa/cxfa_fftextedit.cpp b/xfa/fxfa/cxfa_fftextedit.cpp
index 7b1c93b..416b063 100644
--- a/xfa/fxfa/cxfa_fftextedit.cpp
+++ b/xfa/fxfa/cxfa_fftextedit.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,10 +8,9 @@
 
 #include <utility>
 
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 #include "xfa/fwl/cfwl_datetimepicker.h"
 #include "xfa/fwl/cfwl_edit.h"
-#include "xfa/fwl/cfwl_eventtarget.h"
 #include "xfa/fwl/cfwl_eventtextwillchange.h"
 #include "xfa/fwl/cfwl_messagekillfocus.h"
 #include "xfa/fwl/cfwl_messagesetfocus.h"
@@ -34,23 +33,31 @@
 
 CXFA_FFTextEdit::CXFA_FFTextEdit(CXFA_Node* pNode) : CXFA_FFField(pNode) {}
 
-CXFA_FFTextEdit::~CXFA_FFTextEdit() {
+CXFA_FFTextEdit::~CXFA_FFTextEdit() = default;
+
+void CXFA_FFTextEdit::PreFinalize() {
   if (GetNormalWidget()) {
     CFWL_NoteDriver* pNoteDriver =
-        GetNormalWidget()->GetOwnerApp()->GetNoteDriver();
+        GetNormalWidget()->GetFWLApp()->GetNoteDriver();
     pNoteDriver->UnregisterEventTarget(GetNormalWidget());
   }
 }
 
+void CXFA_FFTextEdit::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FFField::Trace(visitor);
+  visitor->Trace(m_pOldDelegate);
+}
+
 bool CXFA_FFTextEdit::LoadWidget() {
-  auto pNewWidget = pdfium::MakeUnique<CFWL_Edit>(
-      GetFWLApp(), pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr);
-  ASSERT(!IsLoaded());
-  CFWL_Edit* pFWLEdit = pNewWidget.get();
-  SetNormalWidget(std::move(pNewWidget));
+  DCHECK(!IsLoaded());
+
+  CFWL_Edit* pFWLEdit = cppgc::MakeGarbageCollected<CFWL_Edit>(
+      GetFWLApp()->GetHeap()->GetAllocationHandle(), GetFWLApp(),
+      CFWL_Widget::Properties(), nullptr);
+  SetNormalWidget(pFWLEdit);
   pFWLEdit->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver = pFWLEdit->GetOwnerApp()->GetNoteDriver();
+  CFWL_NoteDriver* pNoteDriver = pFWLEdit->GetFWLApp()->GetNoteDriver();
   pNoteDriver->RegisterEventTarget(pFWLEdit, pFWLEdit);
   m_pOldDelegate = pFWLEdit->GetDelegate();
   pFWLEdit->SetDelegate(this);
@@ -58,7 +65,7 @@
   {
     CFWL_Widget::ScopedUpdateLock update_lock(pFWLEdit);
     UpdateWidgetProperty();
-    pFWLEdit->SetText(m_pNode->GetValue(XFA_VALUEPICTURE_Display));
+    pFWLEdit->SetText(m_pNode->GetValue(XFA_ValuePicture::kDisplay));
   }
 
   return CXFA_FFField::LoadWidget();
@@ -76,7 +83,7 @@
   if (m_pNode->IsMultiLine()) {
     dwExtendedStyle |= FWL_STYLEEXT_EDT_MultiLine | FWL_STYLEEXT_EDT_WantReturn;
     if (!m_pNode->IsVerticalScrollPolicyOff()) {
-      dwStyle |= FWL_WGTSTYLE_VScroll;
+      dwStyle |= FWL_STYLE_WGT_VScroll;
       dwExtendedStyle |= FWL_STYLEEXT_EDT_AutoVScroll;
     }
   } else if (!m_pNode->IsHorizontalScrollPolicyOff()) {
@@ -93,127 +100,110 @@
   if (eType == XFA_Element::ExData)
     iMaxChars = 0;
 
-  Optional<int32_t> numCells = m_pNode->GetNumberOfCells();
-  if (!numCells) {
+  absl::optional<int32_t> numCells = m_pNode->GetNumberOfCells();
+  if (!numCells.has_value()) {
     pWidget->SetLimit(iMaxChars);
-  } else if (*numCells == 0) {
+  } else if (numCells == 0) {
     dwExtendedStyle |= FWL_STYLEEXT_EDT_CombText;
     pWidget->SetLimit(iMaxChars > 0 ? iMaxChars : 1);
   } else {
     dwExtendedStyle |= FWL_STYLEEXT_EDT_CombText;
-    pWidget->SetLimit(*numCells);
+    pWidget->SetLimit(numCells.value());
   }
 
   dwExtendedStyle |= GetAlignment();
   GetNormalWidget()->ModifyStyles(dwStyle, 0xFFFFFFFF);
-  GetNormalWidget()->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
+  GetNormalWidget()->ModifyStyleExts(dwExtendedStyle, 0xFFFFFFFF);
 }
 
-bool CXFA_FFTextEdit::AcceptsFocusOnButtonDown(uint32_t dwFlags,
-                                               const CFX_PointF& point,
-                                               FWL_MouseCommand command) {
-  if (command == FWL_MouseCommand::RightButtonDown && !m_pNode->IsOpenAccess())
+bool CXFA_FFTextEdit::AcceptsFocusOnButtonDown(
+    Mask<XFA_FWL_KeyFlag> dwFlags,
+    const CFX_PointF& point,
+    CFWL_MessageMouse::MouseCommand command) {
+  if (command == CFWL_MessageMouse::MouseCommand::kRightButtonDown &&
+      !m_pNode->IsOpenAccess()) {
     return false;
+  }
   if (!PtInActiveRect(point))
     return false;
 
   return true;
 }
 
-bool CXFA_FFTextEdit::OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
-  ObservedPtr<CXFA_FFTextEdit> pWatched(this);
+bool CXFA_FFTextEdit::OnLButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                    const CFX_PointF& point) {
   if (!IsFocused()) {
-    GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_Focused);
+    GetLayoutItem()->SetStatusBits(XFA_WidgetStatus::kFocused);
     UpdateFWLData();
-    if (!pWatched)
-      return false;
-
     InvalidateRect();
   }
   SetButtonDown(true);
-  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
-      GetNormalWidget(), FWL_MouseCommand::LeftButtonDown, dwFlags,
-      FWLToClient(point)));
-
-  return !!pWatched;
+  CFWL_MessageMouse msg(GetNormalWidget(),
+                        CFWL_MessageMouse::MouseCommand::kLeftButtonDown,
+                        dwFlags, FWLToClient(point));
+  SendMessageToFWLWidget(&msg);
+  return true;
 }
 
-bool CXFA_FFTextEdit::OnRButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
-  ObservedPtr<CXFA_FFTextEdit> pWatched(this);
+bool CXFA_FFTextEdit::OnRButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                    const CFX_PointF& point) {
   if (!IsFocused()) {
-    GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_Focused);
+    GetLayoutItem()->SetStatusBits(XFA_WidgetStatus::kFocused);
     UpdateFWLData();
-    if (!pWatched)
-      return false;
-
     InvalidateRect();
   }
   SetButtonDown(true);
-  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
-      nullptr, FWL_MouseCommand::RightButtonDown, dwFlags, FWLToClient(point)));
-
-  return !!pWatched;
+  CFWL_MessageMouse msg(nullptr,
+                        CFWL_MessageMouse::MouseCommand::kRightButtonDown,
+                        dwFlags, FWLToClient(point));
+  SendMessageToFWLWidget(&msg);
+  return true;
 }
 
-bool CXFA_FFTextEdit::OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFTextEdit::OnRButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                  const CFX_PointF& point) {
   if (!CXFA_FFField::OnRButtonUp(dwFlags, point))
     return false;
 
-  GetDoc()->GetDocEnvironment()->PopupMenu(this, point);
+  GetDoc()->PopupMenu(this, point);
   return true;
 }
 
 bool CXFA_FFTextEdit::OnSetFocus(CXFA_FFWidget* pOldWidget) {
-  ObservedPtr<CXFA_FFTextEdit> pWatched(this);
-  ObservedPtr<CXFA_FFWidget> pOldWatched(pOldWidget);
-  GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus_TextEditValueChanged);
+  GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus::kTextEditValueChanged);
   if (!IsFocused()) {
-    GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_Focused);
+    GetLayoutItem()->SetStatusBits(XFA_WidgetStatus::kFocused);
     UpdateFWLData();
-    if (!pWatched)
-      return false;
-
     InvalidateRect();
   }
-  if (!CXFA_FFWidget::OnSetFocus(pOldWatched.Get()))
+  if (!CXFA_FFWidget::OnSetFocus(pOldWidget))
     return false;
 
-  SendMessageToFWLWidget(
-      pdfium::MakeUnique<CFWL_MessageSetFocus>(nullptr, GetNormalWidget()));
-
-  return !!pWatched;
+  CFWL_MessageSetFocus msg(GetNormalWidget());
+  SendMessageToFWLWidget(&msg);
+  return true;
 }
 
 bool CXFA_FFTextEdit::OnKillFocus(CXFA_FFWidget* pNewWidget) {
-  ObservedPtr<CXFA_FFWidget> pWatched(this);
-  ObservedPtr<CXFA_FFWidget> pNewWatched(pNewWidget);
-  SendMessageToFWLWidget(
-      pdfium::MakeUnique<CFWL_MessageKillFocus>(nullptr, GetNormalWidget()));
+  CFWL_MessageKillFocus msg(GetNormalWidget());
+  SendMessageToFWLWidget(&msg);
 
-  if (!pWatched)
-    return false;
-
-  GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus_Focused);
+  GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus::kFocused);
   SetEditScrollOffset();
   ProcessCommittedData();
   UpdateFWLData();
   InvalidateRect();
-  if (!pWatched)
+
+  if (!CXFA_FFWidget::OnKillFocus(pNewWidget))
     return false;
 
-  if (!CXFA_FFWidget::OnKillFocus(pNewWatched.Get()))
-    return false;
-
-  if (!pWatched)
-    return false;
-
-  GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus_TextEditValueChanged);
+  GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus::kTextEditValueChanged);
   return true;
 }
 
 bool CXFA_FFTextEdit::CommitData() {
   WideString wsText = ToEdit(GetNormalWidget())->GetText();
-  if (m_pNode->SetValue(XFA_VALUEPICTURE_Edit, wsText)) {
+  if (m_pNode->SetValue(XFA_ValuePicture::kEdit, wsText)) {
     GetDoc()->GetDocView()->UpdateUIDisplay(m_pNode.Get(), this);
     return true;
   }
@@ -225,7 +215,7 @@
   if (GetNode()->GetFFWidgetType() != XFA_FFWidgetType::kNumericEdit)
     return;
 
-  IXFA_AppProvider* pAppProvider = GetAppProvider();
+  CXFA_FFApp::CallbackIface* pAppProvider = GetAppProvider();
   if (!pAppProvider)
     return;
 
@@ -237,7 +227,8 @@
 }
 
 bool CXFA_FFTextEdit::IsDataChanged() {
-  return GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_TextEditValueChanged);
+  return GetLayoutItem()->TestStatusBits(
+      XFA_WidgetStatus::kTextEditValueChanged);
 }
 
 uint32_t CXFA_FFTextEdit::GetAlignment() {
@@ -279,29 +270,29 @@
 }
 
 bool CXFA_FFTextEdit::UpdateFWLData() {
-  if (!GetNormalWidget())
+  CFWL_Edit* pEdit = ToEdit(GetNormalWidget());
+  if (!pEdit)
     return false;
 
-  CFWL_Edit* pEdit = ToEdit(GetNormalWidget());
-  XFA_VALUEPICTURE eType = XFA_VALUEPICTURE_Display;
+  XFA_ValuePicture eType = XFA_ValuePicture::kDisplay;
   if (IsFocused())
-    eType = XFA_VALUEPICTURE_Edit;
+    eType = XFA_ValuePicture::kEdit;
 
   bool bUpdate = false;
   if (m_pNode->GetFFWidgetType() == XFA_FFWidgetType::kTextEdit &&
-      !m_pNode->GetNumberOfCells()) {
+      !m_pNode->GetNumberOfCells().has_value()) {
     XFA_Element elementType;
     int32_t iMaxChars;
     std::tie(elementType, iMaxChars) = m_pNode->GetMaxChars();
     if (elementType == XFA_Element::ExData)
-      iMaxChars = eType == XFA_VALUEPICTURE_Edit ? iMaxChars : 0;
+      iMaxChars = eType == XFA_ValuePicture::kEdit ? iMaxChars : 0;
     if (pEdit->GetLimit() != iMaxChars) {
       pEdit->SetLimit(iMaxChars);
       bUpdate = true;
     }
   } else if (m_pNode->GetFFWidgetType() == XFA_FFWidgetType::kBarcode) {
     int32_t nDataLen = 0;
-    if (eType == XFA_VALUEPICTURE_Edit) {
+    if (eType == XFA_ValuePicture::kEdit) {
       nDataLen = static_cast<CXFA_Barcode*>(m_pNode->GetUIChildNode())
                      ->GetDataLength()
                      .value_or(0);
@@ -310,10 +301,9 @@
     pEdit->SetLimit(nDataLen);
     bUpdate = true;
   }
-
   WideString wsText = m_pNode->GetValue(eType);
   WideString wsOldText = pEdit->GetText();
-  if (wsText != wsOldText || (eType == XFA_VALUEPICTURE_Edit && bUpdate)) {
+  if (wsText != wsOldText || (eType == XFA_ValuePicture::kEdit && bUpdate)) {
     pEdit->SetTextSkipNotify(wsText);
     bUpdate = true;
   }
@@ -325,30 +315,27 @@
 
 void CXFA_FFTextEdit::OnTextWillChange(CFWL_Widget* pWidget,
                                        CFWL_EventTextWillChange* event) {
-  GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_TextEditValueChanged);
+  GetLayoutItem()->SetStatusBits(XFA_WidgetStatus::kTextEditValueChanged);
 
   CXFA_EventParam eParam;
   eParam.m_eType = XFA_EVENT_Change;
-  eParam.m_wsChange = event->change_text;
-  eParam.m_pTarget = m_pNode.Get();
-  eParam.m_wsPrevText = event->previous_text;
-  eParam.m_iSelStart = static_cast<int32_t>(event->selection_start);
-  eParam.m_iSelEnd = static_cast<int32_t>(event->selection_end);
-
+  eParam.m_wsChange = event->GetChangeText();
+  eParam.m_wsPrevText = event->GetPreviousText();
+  eParam.m_iSelStart = static_cast<int32_t>(event->GetSelectionStart());
+  eParam.m_iSelEnd = static_cast<int32_t>(event->GetSelectionEnd());
   m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change, &eParam);
 
   // Copy the data back out of the EventParam and into the TextChanged event so
   // it can propagate back to the calling widget.
-  event->cancelled = eParam.m_bCancelAction;
-  event->change_text = std::move(eParam.m_wsChange);
-  event->selection_start = static_cast<size_t>(eParam.m_iSelStart);
-  event->selection_end = static_cast<size_t>(eParam.m_iSelEnd);
+  event->SetCancelled(eParam.m_bCancelAction);
+  event->SetChangeText(eParam.m_wsChange);
+  event->SetSelectionStart(static_cast<size_t>(eParam.m_iSelStart));
+  event->SetSelectionEnd(static_cast<size_t>(eParam.m_iSelEnd));
 }
 
 void CXFA_FFTextEdit::OnTextFull(CFWL_Widget* pWidget) {
   CXFA_EventParam eParam;
   eParam.m_eType = XFA_EVENT_Full;
-  eParam.m_pTarget = m_pNode.Get();
   m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Full, &eParam);
 }
 
@@ -372,7 +359,7 @@
   m_pOldDelegate->OnProcessEvent(pEvent);
 }
 
-void CXFA_FFTextEdit::OnDrawWidget(CXFA_Graphics* pGraphics,
+void CXFA_FFTextEdit::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                                    const CFX_Matrix& matrix) {
   m_pOldDelegate->OnDrawWidget(pGraphics, matrix);
 }
@@ -385,6 +372,25 @@
   return ToEdit(GetNormalWidget())->CanRedo();
 }
 
+bool CXFA_FFTextEdit::CanCopy() {
+  return ToEdit(GetNormalWidget())->HasSelection();
+}
+
+bool CXFA_FFTextEdit::CanCut() {
+  if (ToEdit(GetNormalWidget())->GetStyleExts() & FWL_STYLEEXT_EDT_ReadOnly)
+    return false;
+  return ToEdit(GetNormalWidget())->HasSelection();
+}
+
+bool CXFA_FFTextEdit::CanPaste() {
+  return !(ToEdit(GetNormalWidget())->GetStyleExts() &
+           FWL_STYLEEXT_EDT_ReadOnly);
+}
+
+bool CXFA_FFTextEdit::CanSelectAll() {
+  return ToEdit(GetNormalWidget())->GetTextLength() > 0;
+}
+
 bool CXFA_FFTextEdit::Undo() {
   return ToEdit(GetNormalWidget())->Undo();
 }
@@ -393,30 +399,11 @@
   return ToEdit(GetNormalWidget())->Redo();
 }
 
-bool CXFA_FFTextEdit::CanCopy() {
-  return ToEdit(GetNormalWidget())->HasSelection();
-}
-
-bool CXFA_FFTextEdit::CanCut() {
-  if (ToEdit(GetNormalWidget())->GetStylesEx() & FWL_STYLEEXT_EDT_ReadOnly)
-    return false;
-  return ToEdit(GetNormalWidget())->HasSelection();
-}
-
-bool CXFA_FFTextEdit::CanPaste() {
-  return !(ToEdit(GetNormalWidget())->GetStylesEx() &
-           FWL_STYLEEXT_EDT_ReadOnly);
-}
-
-bool CXFA_FFTextEdit::CanSelectAll() {
-  return ToEdit(GetNormalWidget())->GetTextLength() > 0;
-}
-
-Optional<WideString> CXFA_FFTextEdit::Copy() {
+absl::optional<WideString> CXFA_FFTextEdit::Copy() {
   return ToEdit(GetNormalWidget())->Copy();
 }
 
-Optional<WideString> CXFA_FFTextEdit::Cut() {
+absl::optional<WideString> CXFA_FFTextEdit::Cut() {
   return ToEdit(GetNormalWidget())->Cut();
 }
 
diff --git a/xfa/fxfa/cxfa_fftextedit.h b/xfa/fxfa/cxfa_fftextedit.h
index 09b9434..53fd536 100644
--- a/xfa/fxfa/cxfa_fftextedit.h
+++ b/xfa/fxfa/cxfa_fftextedit.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,8 +8,8 @@
 #define XFA_FXFA_CXFA_FFTEXTEDIT_H_
 
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "v8/include/cppgc/prefinalizer.h"
 #include "xfa/fxfa/cxfa_fffield.h"
 
 class CFWL_Event;
@@ -20,24 +20,34 @@
 class IFWL_WidgetDelegate;
 
 class CXFA_FFTextEdit : public CXFA_FFField {
+  CPPGC_USING_PRE_FINALIZER(CXFA_FFTextEdit, PreFinalize);
+
  public:
-  explicit CXFA_FFTextEdit(CXFA_Node* pNode);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFTextEdit() override;
 
+  void PreFinalize();
+
+  void Trace(cppgc::Visitor* visitor) const override;
+
   // CXFA_FFField
   bool LoadWidget() override;
   void UpdateWidgetProperty() override;
-  bool AcceptsFocusOnButtonDown(uint32_t dwFlags,
-                                const CFX_PointF& point,
-                                FWL_MouseCommand command) override;
-  bool OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnRButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnSetFocus(CXFA_FFWidget* pOldWidget) override WARN_UNUSED_RESULT;
-  bool OnKillFocus(CXFA_FFWidget* pNewWidget) override WARN_UNUSED_RESULT;
+  bool AcceptsFocusOnButtonDown(
+      Mask<XFA_FWL_KeyFlag> dwFlags,
+      const CFX_PointF& point,
+      CFWL_MessageMouse::MouseCommand command) override;
+  bool OnLButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
+                     const CFX_PointF& point) override;
+  bool OnRButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
+                     const CFX_PointF& point) override;
+  bool OnRButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                   const CFX_PointF& point) override;
+  [[nodiscard]] bool OnSetFocus(CXFA_FFWidget* pOldWidget) override;
+  [[nodiscard]] bool OnKillFocus(CXFA_FFWidget* pNewWidget) override;
   void OnProcessMessage(CFWL_Message* pMessage) override;
   void OnProcessEvent(CFWL_Event* pEvent) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
+  void OnDrawWidget(CFGAS_GEGraphics* pGraphics,
                     const CFX_Matrix& matrix) override;
 
   void OnTextWillChange(CFWL_Widget* pWidget, CFWL_EventTextWillChange* change);
@@ -46,14 +56,14 @@
   // CXFA_FFWidget
   bool CanUndo() override;
   bool CanRedo() override;
-  bool Undo() override;
-  bool Redo() override;
   bool CanCopy() override;
   bool CanCut() override;
   bool CanPaste() override;
   bool CanSelectAll() override;
-  Optional<WideString> Copy() override;
-  Optional<WideString> Cut() override;
+  bool Undo() override;
+  bool Redo() override;
+  absl::optional<WideString> Copy() override;
+  absl::optional<WideString> Cut() override;
   bool Paste(const WideString& wsPaste) override;
   void SelectAll() override;
   void Delete() override;
@@ -62,9 +72,10 @@
   FormFieldType GetFormFieldType() override;
 
  protected:
+  explicit CXFA_FFTextEdit(CXFA_Node* pNode);
   uint32_t GetAlignment();
 
-  UnownedPtr<IFWL_WidgetDelegate> m_pOldDelegate;
+  cppgc::Member<IFWL_WidgetDelegate> m_pOldDelegate;
 
  private:
   bool CommitData() override;
diff --git a/xfa/fxfa/cxfa_ffwidget.cpp b/xfa/fxfa/cxfa_ffwidget.cpp
index 881218c..93bfb74 100644
--- a/xfa/fxfa/cxfa_ffwidget.cpp
+++ b/xfa/fxfa/cxfa_ffwidget.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,17 +7,18 @@
 #include "xfa/fxfa/cxfa_ffwidget.h"
 
 #include <algorithm>
-#include <cmath>
 #include <memory>
 #include <utility>
-#include <vector>
 
 #include "core/fxcodec/fx_codec.h"
-#include "core/fxcodec/progressivedecoder.h"
+#include "core/fxcodec/progressive_decoder.h"
 #include "core/fxcrt/maybe_owned.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
+#include "third_party/base/check.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
 #include "xfa/fwl/fwl_widgethit.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
@@ -33,14 +34,13 @@
 #include "xfa/fxfa/parser/cxfa_image.h"
 #include "xfa/fxfa/parser/cxfa_margin.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxgraphics/cxfa_graphics.h"
 
 namespace {
 
 FXDIB_Format XFA_GetDIBFormat(FXCODEC_IMAGE_TYPE type,
                               int32_t iComponents,
                               int32_t iBitsPerComponent) {
-  FXDIB_Format dibFormat = FXDIB_Argb;
+  FXDIB_Format dibFormat = FXDIB_Format::kArgb;
   switch (type) {
     case FXCODEC_IMAGE_JPG:
 #ifdef PDF_ENABLE_XFA_BMP
@@ -50,10 +50,10 @@
     case FXCODEC_IMAGE_TIFF:
 #endif  // PDF_ENABLE_XFA_TIFF
     {
-      dibFormat = FXDIB_Rgb32;
+      dibFormat = FXDIB_Format::kRgb32;
       int32_t bpp = iComponents * iBitsPerComponent;
       if (bpp <= 24) {
-        dibFormat = FXDIB_Rgb;
+        dibFormat = FXDIB_Format::kRgb;
       }
     } break;
 #ifdef PDF_ENABLE_XFA_PNG
@@ -65,28 +65,20 @@
   return dibFormat;
 }
 
-bool IsFXCodecErrorStatus(FXCODEC_STATUS status) {
-  return (status == FXCODEC_STATUS_ERROR ||
-          status == FXCODEC_STATUS_ERR_MEMORY ||
-          status == FXCODEC_STATUS_ERR_READ ||
-          status == FXCODEC_STATUS_ERR_FLUSH ||
-          status == FXCODEC_STATUS_ERR_FORMAT ||
-          status == FXCODEC_STATUS_ERR_PARAMS);
-}
-
 }  // namespace
 
-void XFA_DrawImage(CXFA_Graphics* pGS,
+void XFA_DrawImage(CFGAS_GEGraphics* pGS,
                    const CFX_RectF& rtImage,
                    const CFX_Matrix& matrix,
-                   const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                   RetainPtr<CFX_DIBitmap> pDIBitmap,
                    XFA_AttributeValue iAspect,
                    const CFX_Size& dpi,
                    XFA_AttributeValue iHorzAlign,
                    XFA_AttributeValue iVertAlign) {
   if (rtImage.IsEmpty())
     return;
-  if (!pDIBitmap || !pDIBitmap->GetBuffer())
+
+  if (!pDIBitmap || pDIBitmap->GetBuffer().empty())
     return;
 
   CFX_RectF rtFit(rtImage.TopLeft(),
@@ -134,42 +126,44 @@
 
   CFX_RenderDevice* pRenderDevice = pGS->GetRenderDevice();
   CFX_RenderDevice::StateRestorer restorer(pRenderDevice);
-  CFX_PathData path;
+  CFX_Path path;
   path.AppendRect(rtImage.left, rtImage.bottom(), rtImage.right(), rtImage.top);
-  pRenderDevice->SetClip_PathFill(&path, &matrix, FXFILL_WINDING);
+  pRenderDevice->SetClip_PathFill(path, &matrix,
+                                  CFX_FillRenderOptions::WindingOptions());
 
   CFX_Matrix mtImage(1, 0, 0, -1, 0, 1);
   mtImage.Concat(
       CFX_Matrix(rtFit.width, 0, 0, rtFit.height, rtFit.left, rtFit.top));
   mtImage.Concat(matrix);
 
-  CXFA_ImageRenderer imageRender(pRenderDevice, pDIBitmap, &mtImage);
-  if (!imageRender.Start()) {
+  CXFA_ImageRenderer imageRender(pRenderDevice, std::move(pDIBitmap), mtImage);
+  if (!imageRender.Start())
     return;
-  }
+
   while (imageRender.Continue())
     continue;
 }
 
 RetainPtr<CFX_DIBitmap> XFA_LoadImageFromBuffer(
-    const RetainPtr<IFX_SeekableReadStream>& pImageFileRead,
+    RetainPtr<IFX_SeekableReadStream> pImageFileRead,
     FXCODEC_IMAGE_TYPE type,
     int32_t& iImageXDpi,
     int32_t& iImageYDpi) {
-  auto* pCodecMgr = fxcodec::ModuleMgr::GetInstance();
-  std::unique_ptr<ProgressiveDecoder> pProgressiveDecoder =
-      pCodecMgr->CreateProgressiveDecoder();
+  auto pProgressiveDecoder = std::make_unique<ProgressiveDecoder>();
 
   CFX_DIBAttribute dibAttr;
-  pProgressiveDecoder->LoadImageInfo(pImageFileRead, type, &dibAttr, false);
+  pProgressiveDecoder->LoadImageInfo(std::move(pImageFileRead), type, &dibAttr,
+                                     false);
   switch (dibAttr.m_wDPIUnit) {
-    case FXCODEC_RESUNIT_CENTIMETER:
-      dibAttr.m_nXDPI = (int32_t)(dibAttr.m_nXDPI * 2.54f);
-      dibAttr.m_nYDPI = (int32_t)(dibAttr.m_nYDPI * 2.54f);
+    case CFX_DIBAttribute::kResUnitCentimeter:
+      dibAttr.m_nXDPI = static_cast<int32_t>(dibAttr.m_nXDPI * 2.54f);
+      dibAttr.m_nYDPI = static_cast<int32_t>(dibAttr.m_nYDPI * 2.54f);
       break;
-    case FXCODEC_RESUNIT_METER:
-      dibAttr.m_nXDPI = (int32_t)(dibAttr.m_nXDPI / (float)100 * 2.54f);
-      dibAttr.m_nYDPI = (int32_t)(dibAttr.m_nYDPI / (float)100 * 2.54f);
+    case CFX_DIBAttribute::kResUnitMeter:
+      dibAttr.m_nXDPI =
+          static_cast<int32_t>(dibAttr.m_nXDPI / (float)100 * 2.54f);
+      dibAttr.m_nYDPI =
+          static_cast<int32_t>(dibAttr.m_nYDPI / (float)100 * 2.54f);
       break;
     default:
       break;
@@ -193,24 +187,18 @@
   size_t nFrames;
   FXCODEC_STATUS status;
   std::tie(status, nFrames) = pProgressiveDecoder->GetFrames();
-  if (status != FXCODEC_STATUS_DECODE_READY || nFrames == 0) {
-    pBitmap = nullptr;
-    return pBitmap;
-  }
+  if (status != FXCODEC_STATUS::kDecodeReady || nFrames == 0)
+    return nullptr;
 
   status = pProgressiveDecoder->StartDecode(pBitmap, 0, 0, pBitmap->GetWidth(),
                                             pBitmap->GetHeight());
-  if (IsFXCodecErrorStatus(status)) {
-    pBitmap = nullptr;
-    return pBitmap;
-  }
+  if (status == FXCODEC_STATUS::kError)
+    return nullptr;
 
-  while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE) {
+  while (status == FXCODEC_STATUS::kDecodeToBeContinued) {
     status = pProgressiveDecoder->ContinueDecode();
-    if (IsFXCodecErrorStatus(status)) {
-      pBitmap = nullptr;
-      return pBitmap;
-    }
+    if (status == FXCODEC_STATUS::kError)
+      return nullptr;
   }
 
   return pBitmap;
@@ -224,22 +212,26 @@
               margin->GetRightInset(), margin->GetBottomInset());
 }
 
-CXFA_FFWidget* XFA_GetWidgetFromLayoutItem(CXFA_LayoutItem* pLayoutItem) {
+// static
+CXFA_FFWidget* CXFA_FFWidget::FromLayoutItem(CXFA_LayoutItem* pLayoutItem) {
   if (!pLayoutItem->GetFormNode()->HasCreatedUIWidget())
     return nullptr;
 
   return GetFFWidget(ToContentLayoutItem(pLayoutItem));
 }
 
-CXFA_CalcData::CXFA_CalcData() = default;
-
-CXFA_CalcData::~CXFA_CalcData() = default;
-
 CXFA_FFWidget::CXFA_FFWidget(CXFA_Node* node) : m_pNode(node) {}
 
 CXFA_FFWidget::~CXFA_FFWidget() = default;
 
-const CFWL_App* CXFA_FFWidget::GetFWLApp() {
+void CXFA_FFWidget::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pLayoutItem);
+  visitor->Trace(m_pDocView);
+  visitor->Trace(m_pPageView);
+  visitor->Trace(m_pNode);
+}
+
+CFWL_App* CXFA_FFWidget::GetFWLApp() const {
   return GetPageView()->GetDocView()->GetDoc()->GetApp()->GetFWLApp();
 }
 
@@ -248,15 +240,15 @@
 }
 
 const CFX_RectF& CXFA_FFWidget::GetWidgetRect() const {
-  if (!GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_RectCached))
+  if (!GetLayoutItem()->TestStatusBits(XFA_WidgetStatus::kRectCached))
     RecacheWidgetRect();
-  return m_rtWidget;
+  return m_WidgetRect;
 }
 
 const CFX_RectF& CXFA_FFWidget::RecacheWidgetRect() const {
-  GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_RectCached);
-  m_rtWidget = GetLayoutItem()->GetRect(false);
-  return m_rtWidget;
+  GetLayoutItem()->SetStatusBits(XFA_WidgetStatus::kRectCached);
+  m_WidgetRect = GetLayoutItem()->GetAbsoluteRect();
+  return m_WidgetRect;
 }
 
 CFX_RectF CXFA_FFWidget::GetRectWithoutRotate() {
@@ -283,7 +275,8 @@
   return rtWidget;
 }
 
-void CXFA_FFWidget::ModifyStatus(uint32_t dwAdded, uint32_t dwRemoved) {
+void CXFA_FFWidget::ModifyStatus(Mask<XFA_WidgetStatus> dwAdded,
+                                 Mask<XFA_WidgetStatus> dwRemoved) {
   GetLayoutItem()->ClearStatusBits(dwRemoved);
   GetLayoutItem()->SetStatusBits(dwAdded);
 }
@@ -298,7 +291,7 @@
   return m_pPageView->GetPageViewRect();
 }
 
-void CXFA_FFWidget::RenderWidget(CXFA_Graphics* pGS,
+void CXFA_FFWidget::RenderWidget(CFGAS_GEGraphics* pGS,
                                  const CFX_Matrix& matrix,
                                  HighlightOption highlight) {
   if (!HasVisibleStatus())
@@ -347,11 +340,10 @@
   if (!pNode->IsWidgetReady())
     return false;
 
-  params->m_pTarget = pNode;
   return pHandler->ProcessEvent(pNode, params) == XFA_EventError::kSuccess;
 }
 
-void CXFA_FFWidget::DrawBorder(CXFA_Graphics* pGS,
+void CXFA_FFWidget::DrawBorder(CFGAS_GEGraphics* pGS,
                                CXFA_Box* box,
                                const CFX_RectF& rtBorder,
                                const CFX_Matrix& matrix) {
@@ -359,7 +351,7 @@
     box->Draw(pGS, rtBorder, matrix, false);
 }
 
-void CXFA_FFWidget::DrawBorderWithFlag(CXFA_Graphics* pGS,
+void CXFA_FFWidget::DrawBorderWithFlag(CFGAS_GEGraphics* pGS,
                                        CXFA_Box* box,
                                        const CFX_RectF& rtBorder,
                                        const CFX_Matrix& matrix,
@@ -382,100 +374,88 @@
   return false;
 }
 
-bool CXFA_FFWidget::AcceptsFocusOnButtonDown(uint32_t dwFlags,
-                                             const CFX_PointF& point,
-                                             FWL_MouseCommand command) {
+bool CXFA_FFWidget::AcceptsFocusOnButtonDown(
+    Mask<XFA_FWL_KeyFlag> dwFlags,
+    const CFX_PointF& point,
+    CFWL_MessageMouse::MouseCommand command) {
   return false;
 }
 
-bool CXFA_FFWidget::OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFWidget::OnLButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                  const CFX_PointF& point) {
   return false;
 }
 
-bool CXFA_FFWidget::OnLButtonUp(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFWidget::OnLButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                const CFX_PointF& point) {
   return false;
 }
 
-bool CXFA_FFWidget::OnLButtonDblClk(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFWidget::OnLButtonDblClk(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                    const CFX_PointF& point) {
   return false;
 }
 
-bool CXFA_FFWidget::OnMouseMove(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFWidget::OnMouseMove(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                const CFX_PointF& point) {
   return false;
 }
 
-bool CXFA_FFWidget::OnMouseWheel(uint32_t dwFlags,
-                                 int16_t zDelta,
-                                 const CFX_PointF& point) {
+bool CXFA_FFWidget::OnMouseWheel(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                 const CFX_PointF& point,
+                                 const CFX_Vector& delta) {
   return false;
 }
 
-bool CXFA_FFWidget::OnRButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFWidget::OnRButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                  const CFX_PointF& point) {
   return false;
 }
 
-bool CXFA_FFWidget::OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFWidget::OnRButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                const CFX_PointF& point) {
   return false;
 }
 
-bool CXFA_FFWidget::OnRButtonDblClk(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFWidget::OnRButtonDblClk(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                    const CFX_PointF& point) {
   return false;
 }
 
 bool CXFA_FFWidget::OnSetFocus(CXFA_FFWidget* pOldWidget) {
-  // OnSetFocus event may remove this widget.
-  ObservedPtr<CXFA_FFWidget> pWatched(this);
   CXFA_FFWidget* pParent = GetFFWidget(ToContentLayoutItem(GetParent()));
   if (pParent && !pParent->IsAncestorOf(pOldWidget)) {
     if (!pParent->OnSetFocus(pOldWidget))
       return false;
   }
-  if (!pWatched)
-    return false;
-
-  GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_Focused);
+  GetLayoutItem()->SetStatusBits(XFA_WidgetStatus::kFocused);
 
   CXFA_EventParam eParam;
   eParam.m_eType = XFA_EVENT_Enter;
-  eParam.m_pTarget = m_pNode.Get();
   m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Enter, &eParam);
-
-  return !!pWatched;
+  return true;
 }
 
 bool CXFA_FFWidget::OnKillFocus(CXFA_FFWidget* pNewWidget) {
-  // OnKillFocus event may remove these widgets.
-  ObservedPtr<CXFA_FFWidget> pWatched(this);
-  ObservedPtr<CXFA_FFWidget> pNewWatched(pNewWidget);
-  GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus_Focused);
+  GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus::kFocused);
   EventKillFocus();
-  if (!pWatched)
-    return false;
-
   if (!pNewWidget)
     return true;
 
-  if (!pNewWatched)
-    return false;
-
-  // OnKillFocus event may remove |pNewWidget|.
   CXFA_FFWidget* pParent = GetFFWidget(ToContentLayoutItem(GetParent()));
   if (pParent && !pParent->IsAncestorOf(pNewWidget)) {
     if (!pParent->OnKillFocus(pNewWidget))
       return false;
   }
-  return pWatched && pNewWatched;
+  return true;
 }
 
-bool CXFA_FFWidget::OnKeyDown(uint32_t dwKeyCode, uint32_t dwFlags) {
+bool CXFA_FFWidget::OnKeyDown(XFA_FWL_VKEYCODE dwKeyCode,
+                              Mask<XFA_FWL_KeyFlag> dwFlags) {
   return false;
 }
 
-bool CXFA_FFWidget::OnKeyUp(uint32_t dwKeyCode, uint32_t dwFlags) {
-  return false;
-}
-
-bool CXFA_FFWidget::OnChar(uint32_t dwChar, uint32_t dwFlags) {
+bool CXFA_FFWidget::OnChar(uint32_t dwChar, Mask<XFA_FWL_KeyFlag> dwFlags) {
   return false;
 }
 
@@ -483,10 +463,6 @@
   return FWL_WidgetHit::Unknown;
 }
 
-bool CXFA_FFWidget::OnSetCursor(const CFX_PointF& point) {
-  return false;
-}
-
 bool CXFA_FFWidget::CanUndo() {
   return false;
 }
@@ -495,14 +471,6 @@
   return false;
 }
 
-bool CXFA_FFWidget::Undo() {
-  return false;
-}
-
-bool CXFA_FFWidget::Redo() {
-  return false;
-}
-
 bool CXFA_FFWidget::CanCopy() {
   return false;
 }
@@ -527,12 +495,20 @@
   return CanCopy();
 }
 
-Optional<WideString> CXFA_FFWidget::Copy() {
-  return {};
+bool CXFA_FFWidget::Undo() {
+  return false;
 }
 
-Optional<WideString> CXFA_FFWidget::Cut() {
-  return {};
+bool CXFA_FFWidget::Redo() {
+  return false;
+}
+
+absl::optional<WideString> CXFA_FFWidget::Copy() {
+  return absl::nullopt;
+}
+
+absl::optional<WideString> CXFA_FFWidget::Cut() {
+  return absl::nullopt;
 }
 
 bool CXFA_FFWidget::Paste(const WideString& wsPaste) {
@@ -598,15 +574,11 @@
 }
 
 void CXFA_FFWidget::DisplayCaret(bool bVisible, const CFX_RectF* pRtAnchor) {
-  IXFA_DocEnvironment* pDocEnvironment = GetDoc()->GetDocEnvironment();
-  if (!pDocEnvironment)
-    return;
-
-  pDocEnvironment->DisplayCaret(this, bVisible, pRtAnchor);
+  GetDoc()->DisplayCaret(this, bVisible, pRtAnchor);
 }
 
 void CXFA_FFWidget::GetBorderColorAndThickness(FX_ARGB* cr, float* fWidth) {
-  ASSERT(GetNode()->IsWidgetReady());
+  DCHECK(GetNode()->IsWidgetReady());
   CXFA_Border* borderUI = GetNode()->GetUIBorder();
   if (!borderUI)
     return;
@@ -629,7 +601,7 @@
   if (!pParentNode)
     return nullptr;
 
-  CXFA_LayoutProcessor* layout = GetDocView()->GetXFALayout();
+  CXFA_LayoutProcessor* layout = GetDocView()->GetLayoutProcessor();
   return layout->GetLayoutItem(pParentNode);
 }
 
@@ -659,34 +631,33 @@
   return GetDoc()->GetApp();
 }
 
-IXFA_AppProvider* CXFA_FFWidget::GetAppProvider() {
+CXFA_FFApp::CallbackIface* CXFA_FFWidget::GetAppProvider() {
   return GetApp()->GetAppProvider();
 }
 
 bool CXFA_FFWidget::HasVisibleStatus() const {
-  return GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_Visible);
+  return GetLayoutItem()->TestStatusBits(XFA_WidgetStatus::kVisible);
 }
 
 void CXFA_FFWidget::EventKillFocus() {
   CXFA_ContentLayoutItem* pItem = GetLayoutItem();
-  if (pItem->TestStatusBits(XFA_WidgetStatus_Access)) {
-    pItem->ClearStatusBits(XFA_WidgetStatus_Access);
+  if (pItem->TestStatusBits(XFA_WidgetStatus::kAccess)) {
+    pItem->ClearStatusBits(XFA_WidgetStatus::kAccess);
     return;
   }
   CXFA_EventParam eParam;
   eParam.m_eType = XFA_EVENT_Exit;
-  eParam.m_pTarget = m_pNode.Get();
   m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Exit, &eParam);
 }
 
 bool CXFA_FFWidget::IsButtonDown() {
-  return GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_ButtonDown);
+  return GetLayoutItem()->TestStatusBits(XFA_WidgetStatus::kButtonDown);
 }
 
 void CXFA_FFWidget::SetButtonDown(bool bSet) {
   CXFA_ContentLayoutItem* pItem = GetLayoutItem();
   if (bSet)
-    pItem->SetStatusBits(XFA_WidgetStatus_ButtonDown);
+    pItem->SetStatusBits(XFA_WidgetStatus::kButtonDown);
   else
-    pItem->ClearStatusBits(XFA_WidgetStatus_ButtonDown);
+    pItem->ClearStatusBits(XFA_WidgetStatus::kButtonDown);
 }
diff --git a/xfa/fxfa/cxfa_ffwidget.h b/xfa/fxfa/cxfa_ffwidget.h
index 4c362c1..e11bca7 100644
--- a/xfa/fxfa/cxfa_ffwidget.h
+++ b/xfa/fxfa/cxfa_ffwidget.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,26 @@
 #ifndef XFA_FXFA_CXFA_FFWIDGET_H_
 #define XFA_FXFA_CXFA_FFWIDGET_H_
 
-#include <vector>
+#include <stdint.h>
 
 #include "core/fpdfdoc/cpdf_formfield.h"
 #include "core/fxcodec/fx_codec_def.h"
-#include "core/fxcrt/observed_ptr.h"
+#include "core/fxcrt/mask.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/cfx_graphstatedata.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
 #include "xfa/fwl/cfwl_widget.h"
+#include "xfa/fwl/fwl_widgetdef.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
+#include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/fxfa.h"
 #include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
 
+class CFGAS_GEGraphics;
 class CFX_DIBitmap;
 class CXFA_Box;
 class CXFA_FFApp;
@@ -28,9 +35,8 @@
 class CXFA_FFField;
 class CXFA_FFPageView;
 class CXFA_FFWidgetHandler;
-class CXFA_Graphics;
-class CXFA_Image;
 class CXFA_Margin;
+class IFX_SeekableReadStream;
 enum class FWL_WidgetHit;
 
 inline float XFA_UnitPx2Pt(float fPx, float fDpi) {
@@ -39,49 +45,56 @@
 
 constexpr float kXFAWidgetPrecision = 0.001f;
 
-void XFA_DrawImage(CXFA_Graphics* pGS,
+void XFA_DrawImage(CFGAS_GEGraphics* pGS,
                    const CFX_RectF& rtImage,
                    const CFX_Matrix& matrix,
-                   const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                   RetainPtr<CFX_DIBitmap> pDIBitmap,
                    XFA_AttributeValue iAspect,
                    const CFX_Size& dpi,
                    XFA_AttributeValue iHorzAlign = XFA_AttributeValue::Left,
                    XFA_AttributeValue iVertAlign = XFA_AttributeValue::Top);
 
 RetainPtr<CFX_DIBitmap> XFA_LoadImageFromBuffer(
-    const RetainPtr<IFX_SeekableReadStream>& pImageFileRead,
+    RetainPtr<IFX_SeekableReadStream> pImageFileRead,
     FXCODEC_IMAGE_TYPE type,
     int32_t& iImageXDpi,
     int32_t& iImageYDpi);
 
 void XFA_RectWithoutMargin(CFX_RectF* rt, const CXFA_Margin* margin);
-CXFA_FFWidget* XFA_GetWidgetFromLayoutItem(CXFA_LayoutItem* pLayoutItem);
 
-class CXFA_CalcData {
- public:
-  CXFA_CalcData();
-  ~CXFA_CalcData();
-
-  std::vector<CXFA_Node*> m_Globals;
-};
-
-class CXFA_FFWidget : public Observable, public CFWL_Widget::AdapterIface {
+class CXFA_FFWidget : public cppgc::GarbageCollected<CXFA_FFWidget>,
+                      public CFWL_Widget::AdapterIface {
  public:
   enum FocusOption { kDoNotDrawFocus = 0, kDrawFocus };
   enum HighlightOption { kNoHighlight = 0, kHighlight };
 
-  explicit CXFA_FFWidget(CXFA_Node* pNode);
+  class IteratorIface {
+   public:
+    virtual ~IteratorIface() = default;
+
+    virtual CXFA_FFWidget* MoveToFirst() = 0;
+    virtual CXFA_FFWidget* MoveToLast() = 0;
+    virtual CXFA_FFWidget* MoveToNext() = 0;
+    virtual CXFA_FFWidget* MoveToPrevious() = 0;
+    virtual CXFA_FFWidget* GetCurrentWidget() = 0;
+    virtual bool SetCurrentWidget(CXFA_FFWidget* hWidget) = 0;
+  };
+
+  static CXFA_FFWidget* FromLayoutItem(CXFA_LayoutItem* pLayoutItem);
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFWidget() override;
 
+  void Trace(cppgc::Visitor* visitor) const override;
+
   // CFWL_Widget::AdapterIface:
   CFX_Matrix GetRotateMatrix() override;
   void DisplayCaret(bool bVisible, const CFX_RectF* pRtAnchor) override;
   void GetBorderColorAndThickness(FX_ARGB* cr, float* fWidth) override;
 
   virtual CXFA_FFField* AsField();
-
   virtual CFX_RectF GetBBox(FocusOption focus);
-  virtual void RenderWidget(CXFA_Graphics* pGS,
+  virtual void RenderWidget(CFGAS_GEGraphics* pGS,
                             const CFX_Matrix& matrix,
                             HighlightOption highlight);
   virtual bool IsLoaded();
@@ -90,51 +103,51 @@
   virtual bool UpdateFWLData();
   virtual void UpdateWidgetProperty();
   // |command| must be LeftButtonDown or RightButtonDown.
-  virtual bool AcceptsFocusOnButtonDown(uint32_t dwFlags,
-                                        const CFX_PointF& point,
-                                        FWL_MouseCommand command);
+  virtual bool AcceptsFocusOnButtonDown(
+      Mask<XFA_FWL_KeyFlag> dwFlags,
+      const CFX_PointF& point,
+      CFWL_MessageMouse::MouseCommand command);
 
   // Caution: Returning false from an On* method may mean |this| is destroyed.
-  virtual bool OnMouseEnter() WARN_UNUSED_RESULT;
-  virtual bool OnMouseExit() WARN_UNUSED_RESULT;
-  virtual bool OnLButtonDown(uint32_t dwFlags,
-                             const CFX_PointF& point) WARN_UNUSED_RESULT;
-  virtual bool OnLButtonUp(uint32_t dwFlags,
-                           const CFX_PointF& point) WARN_UNUSED_RESULT;
-  virtual bool OnLButtonDblClk(uint32_t dwFlags,
-                               const CFX_PointF& point) WARN_UNUSED_RESULT;
-  virtual bool OnMouseMove(uint32_t dwFlags,
-                           const CFX_PointF& point) WARN_UNUSED_RESULT;
-  virtual bool OnMouseWheel(uint32_t dwFlags,
-                            int16_t zDelta,
-                            const CFX_PointF& point) WARN_UNUSED_RESULT;
-  virtual bool OnRButtonDown(uint32_t dwFlags,
-                             const CFX_PointF& point) WARN_UNUSED_RESULT;
-  virtual bool OnRButtonUp(uint32_t dwFlags,
-                           const CFX_PointF& point) WARN_UNUSED_RESULT;
-  virtual bool OnRButtonDblClk(uint32_t dwFlags,
-                               const CFX_PointF& point) WARN_UNUSED_RESULT;
-  virtual bool OnSetFocus(CXFA_FFWidget* pOldWidget) WARN_UNUSED_RESULT;
-  virtual bool OnKillFocus(CXFA_FFWidget* pNewWidget) WARN_UNUSED_RESULT;
-  virtual bool OnKeyDown(uint32_t dwKeyCode,
-                         uint32_t dwFlags) WARN_UNUSED_RESULT;
-  virtual bool OnKeyUp(uint32_t dwKeyCode, uint32_t dwFlags) WARN_UNUSED_RESULT;
-  virtual bool OnChar(uint32_t dwChar, uint32_t dwFlags) WARN_UNUSED_RESULT;
-  virtual bool OnSetCursor(const CFX_PointF& point) WARN_UNUSED_RESULT;
+  [[nodiscard]] virtual bool OnMouseEnter();
+  [[nodiscard]] virtual bool OnMouseExit();
+  [[nodiscard]] virtual bool OnLButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                           const CFX_PointF& point);
+  [[nodiscard]] virtual bool OnLButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                         const CFX_PointF& point);
+  [[nodiscard]] virtual bool OnLButtonDblClk(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                             const CFX_PointF& point);
+  [[nodiscard]] virtual bool OnMouseMove(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                         const CFX_PointF& point);
+  [[nodiscard]] virtual bool OnMouseWheel(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                          const CFX_PointF& point,
+                                          const CFX_Vector& delta);
+  [[nodiscard]] virtual bool OnRButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                           const CFX_PointF& point);
+  [[nodiscard]] virtual bool OnRButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                         const CFX_PointF& point);
+  [[nodiscard]] virtual bool OnRButtonDblClk(Mask<XFA_FWL_KeyFlag> dwFlags,
+                                             const CFX_PointF& point);
+  [[nodiscard]] virtual bool OnSetFocus(CXFA_FFWidget* pOldWidget);
+  [[nodiscard]] virtual bool OnKillFocus(CXFA_FFWidget* pNewWidget);
+  [[nodiscard]] virtual bool OnKeyDown(XFA_FWL_VKEYCODE dwKeyCode,
+                                       Mask<XFA_FWL_KeyFlag> dwFlags);
+  [[nodiscard]] virtual bool OnChar(uint32_t dwChar,
+                                    Mask<XFA_FWL_KeyFlag> dwFlags);
 
   virtual FWL_WidgetHit HitTest(const CFX_PointF& point);
   virtual bool CanUndo();
   virtual bool CanRedo();
-  virtual bool Undo();
-  virtual bool Redo();
   virtual bool CanCopy();
   virtual bool CanCut();
   virtual bool CanPaste();
   virtual bool CanSelectAll();
   virtual bool CanDelete();
   virtual bool CanDeSelect();
-  virtual Optional<WideString> Copy();
-  virtual Optional<WideString> Cut();
+  virtual bool Undo();
+  virtual bool Redo();
+  virtual absl::optional<WideString> Copy();
+  virtual absl::optional<WideString> Cut();
   virtual bool Paste(const WideString& wsPaste);
   virtual void SelectAll();
   virtual void Delete();
@@ -142,45 +155,46 @@
   virtual WideString GetText();
   virtual FormFieldType GetFormFieldType();
 
-  CXFA_Node* GetNode() const { return m_pNode.Get(); }
-  CXFA_ContentLayoutItem* GetLayoutItem() const { return m_pLayoutItem.Get(); }
+  CXFA_Node* GetNode() const { return m_pNode; }
+  CXFA_ContentLayoutItem* GetLayoutItem() const { return m_pLayoutItem; }
   void SetLayoutItem(CXFA_ContentLayoutItem* pItem) { m_pLayoutItem = pItem; }
-  CXFA_FFPageView* GetPageView() const { return m_pPageView.Get(); }
+  CXFA_FFPageView* GetPageView() const { return m_pPageView; }
   void SetPageView(CXFA_FFPageView* pPageView) { m_pPageView = pPageView; }
-  CXFA_FFDocView* GetDocView() const { return m_pDocView.Get(); }
+  CXFA_FFDocView* GetDocView() const { return m_pDocView; }
   void SetDocView(CXFA_FFDocView* pDocView) { m_pDocView = pDocView; }
 
   CXFA_FFWidget* GetNextFFWidget() const;
   const CFX_RectF& GetWidgetRect() const;
   const CFX_RectF& RecacheWidgetRect() const;
-  void ModifyStatus(uint32_t dwAdded, uint32_t dwRemoved);
+  void ModifyStatus(Mask<XFA_WidgetStatus> dwAdded,
+                    Mask<XFA_WidgetStatus> dwRemoved);
 
   CXFA_FFDoc* GetDoc();
   CXFA_FFApp* GetApp();
-  IXFA_AppProvider* GetAppProvider();
+  CXFA_FFApp::CallbackIface* GetAppProvider();
+  CFWL_App* GetFWLApp() const;
   void InvalidateRect();
   bool IsFocused() const {
-    return GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_Focused);
+    return GetLayoutItem()->TestStatusBits(XFA_WidgetStatus::kFocused);
   }
   CFX_PointF Rotate2Normal(const CFX_PointF& point);
   bool IsLayoutRectEmpty();
   CXFA_LayoutItem* GetParent();
   bool IsAncestorOf(CXFA_FFWidget* pWidget);
-  const CFWL_App* GetFWLApp();
-
   bool HasEventUnderHandler(XFA_EVENTTYPE eEventType,
                             CXFA_FFWidgetHandler* pHandler);
   bool ProcessEventUnderHandler(CXFA_EventParam* params,
                                 CXFA_FFWidgetHandler* pHandler);
 
  protected:
+  explicit CXFA_FFWidget(CXFA_Node* pNode);
   virtual bool PtInActiveRect(const CFX_PointF& point);
 
-  void DrawBorder(CXFA_Graphics* pGS,
+  void DrawBorder(CFGAS_GEGraphics* pGS,
                   CXFA_Box* box,
                   const CFX_RectF& rtBorder,
                   const CFX_Matrix& matrix);
-  void DrawBorderWithFlag(CXFA_Graphics* pGS,
+  void DrawBorderWithFlag(CFGAS_GEGraphics* pGS,
                           CXFA_Box* box,
                           const CFX_RectF& rtBorder,
                           const CFX_Matrix& matrix,
@@ -192,11 +206,11 @@
   bool IsButtonDown();
   void SetButtonDown(bool bSet);
 
-  UnownedPtr<CXFA_ContentLayoutItem> m_pLayoutItem;
-  UnownedPtr<CXFA_FFDocView> m_pDocView;
-  UnownedPtr<CXFA_FFPageView> m_pPageView;
-  UnownedPtr<CXFA_Node> const m_pNode;
-  mutable CFX_RectF m_rtWidget;
+  cppgc::Member<CXFA_ContentLayoutItem> m_pLayoutItem;
+  cppgc::Member<CXFA_FFDocView> m_pDocView;
+  cppgc::Member<CXFA_FFPageView> m_pPageView;
+  cppgc::Member<CXFA_Node> const m_pNode;
+  mutable CFX_RectF m_WidgetRect;
 };
 
 inline CXFA_FFField* ToField(CXFA_FFWidget* widget) {
diff --git a/xfa/fxfa/cxfa_ffwidget_type.h b/xfa/fxfa/cxfa_ffwidget_type.h
index 69fbcd2..77658a0 100644
--- a/xfa/fxfa/cxfa_ffwidget_type.h
+++ b/xfa/fxfa/cxfa_ffwidget_type.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fxfa/cxfa_ffwidgethandler.cpp b/xfa/fxfa/cxfa_ffwidgethandler.cpp
index aa5dc3a..6992e57 100644
--- a/xfa/fxfa/cxfa_ffwidgethandler.cpp
+++ b/xfa/fxfa/cxfa_ffwidgethandler.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,6 @@
 
 #include "xfa/fxfa/cxfa_ffwidgethandler.h"
 
-#include <vector>
-
 #include "fxjs/xfa/cjx_object.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffdocview.h"
@@ -25,126 +23,104 @@
 CXFA_FFWidgetHandler::CXFA_FFWidgetHandler(CXFA_FFDocView* pDocView)
     : m_pDocView(pDocView) {}
 
-CXFA_FFWidgetHandler::~CXFA_FFWidgetHandler() {}
+CXFA_FFWidgetHandler::~CXFA_FFWidgetHandler() = default;
+
+void CXFA_FFWidgetHandler::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pDocView);
+}
 
 bool CXFA_FFWidgetHandler::OnMouseEnter(CXFA_FFWidget* hWidget) {
-  m_pDocView->LockUpdate();
-  bool bRet = hWidget->OnMouseEnter();
-  m_pDocView->UnlockUpdate();
-  m_pDocView->UpdateDocView();
-  return bRet;
+  CXFA_FFDocView::UpdateScope scope(m_pDocView);
+  return hWidget->OnMouseEnter();
 }
 
 bool CXFA_FFWidgetHandler::OnMouseExit(CXFA_FFWidget* hWidget) {
-  m_pDocView->LockUpdate();
-  bool bRet = hWidget->OnMouseExit();
-  m_pDocView->UnlockUpdate();
-  m_pDocView->UpdateDocView();
-  return bRet;
+  CXFA_FFDocView::UpdateScope scope(m_pDocView);
+  return hWidget->OnMouseExit();
 }
 
 bool CXFA_FFWidgetHandler::OnLButtonDown(CXFA_FFWidget* hWidget,
-                                         uint32_t dwFlags,
+                                         Mask<XFA_FWL_KeyFlag> dwFlags,
                                          const CFX_PointF& point) {
-  m_pDocView->LockUpdate();
-  bool bRet = hWidget->AcceptsFocusOnButtonDown(
-      dwFlags, hWidget->Rotate2Normal(point), FWL_MouseCommand::LeftButtonDown);
-  if (bRet) {
-    if (m_pDocView->SetFocus(hWidget)) {
-      m_pDocView->GetDoc()->GetDocEnvironment()->SetFocusWidget(
-          m_pDocView->GetDoc(), hWidget);
-    }
-    bRet = hWidget->OnLButtonDown(dwFlags, hWidget->Rotate2Normal(point));
+  CXFA_FFDocView::UpdateScope scope(m_pDocView);
+  if (!hWidget->AcceptsFocusOnButtonDown(
+          dwFlags, hWidget->Rotate2Normal(point),
+          CFWL_MessageMouse::MouseCommand::kLeftButtonDown)) {
+    return false;
   }
-  m_pDocView->UnlockUpdate();
-  m_pDocView->UpdateDocView();
-  return bRet;
+  // May re-enter JS.
+  if (m_pDocView->SetFocus(hWidget))
+    m_pDocView->GetDoc()->SetFocusWidget(hWidget);
+
+  return hWidget->OnLButtonDown(dwFlags, hWidget->Rotate2Normal(point));
 }
 
 bool CXFA_FFWidgetHandler::OnLButtonUp(CXFA_FFWidget* hWidget,
-                                       uint32_t dwFlags,
+                                       Mask<XFA_FWL_KeyFlag> dwFlags,
                                        const CFX_PointF& point) {
-  m_pDocView->LockUpdate();
-  m_pDocView->m_bLayoutEvent = true;
-  bool bRet = hWidget->OnLButtonUp(dwFlags, hWidget->Rotate2Normal(point));
-  m_pDocView->UnlockUpdate();
-  m_pDocView->UpdateDocView();
-  return bRet;
+  CXFA_FFDocView::UpdateScope scope(m_pDocView);
+  m_pDocView->SetLayoutEvent();
+  return hWidget->OnLButtonUp(dwFlags, hWidget->Rotate2Normal(point));
 }
 
 bool CXFA_FFWidgetHandler::OnLButtonDblClk(CXFA_FFWidget* hWidget,
-                                           uint32_t dwFlags,
+                                           Mask<XFA_FWL_KeyFlag> dwFlags,
                                            const CFX_PointF& point) {
-  bool bRet = hWidget->OnLButtonDblClk(dwFlags, hWidget->Rotate2Normal(point));
-  return bRet;
+  return hWidget->OnLButtonDblClk(dwFlags, hWidget->Rotate2Normal(point));
 }
 
 bool CXFA_FFWidgetHandler::OnMouseMove(CXFA_FFWidget* hWidget,
-                                       uint32_t dwFlags,
+                                       Mask<XFA_FWL_KeyFlag> dwFlags,
                                        const CFX_PointF& point) {
-  bool bRet = hWidget->OnMouseMove(dwFlags, hWidget->Rotate2Normal(point));
-  return bRet;
+  return hWidget->OnMouseMove(dwFlags, hWidget->Rotate2Normal(point));
 }
 
 bool CXFA_FFWidgetHandler::OnMouseWheel(CXFA_FFWidget* hWidget,
-                                        uint32_t dwFlags,
-                                        int16_t zDelta,
-                                        const CFX_PointF& point) {
-  bool bRet =
-      hWidget->OnMouseWheel(dwFlags, zDelta, hWidget->Rotate2Normal(point));
-  return bRet;
+                                        Mask<XFA_FWL_KeyFlag> dwFlags,
+                                        const CFX_PointF& point,
+                                        const CFX_Vector& delta) {
+  return hWidget->OnMouseWheel(dwFlags, hWidget->Rotate2Normal(point), delta);
 }
 
 bool CXFA_FFWidgetHandler::OnRButtonDown(CXFA_FFWidget* hWidget,
-                                         uint32_t dwFlags,
+                                         Mask<XFA_FWL_KeyFlag> dwFlags,
                                          const CFX_PointF& point) {
-  bool bRet =
-      hWidget->AcceptsFocusOnButtonDown(dwFlags, hWidget->Rotate2Normal(point),
-                                        FWL_MouseCommand::RightButtonDown);
-  if (bRet) {
-    if (m_pDocView->SetFocus(hWidget)) {
-      m_pDocView->GetDoc()->GetDocEnvironment()->SetFocusWidget(
-          m_pDocView->GetDoc(), hWidget);
-    }
-    bRet = hWidget->OnRButtonDown(dwFlags, hWidget->Rotate2Normal(point));
+  if (!hWidget->AcceptsFocusOnButtonDown(
+          dwFlags, hWidget->Rotate2Normal(point),
+          CFWL_MessageMouse::MouseCommand::kRightButtonDown)) {
+    return false;
   }
-  return bRet;
+  // May re-enter JS.
+  if (m_pDocView->SetFocus(hWidget)) {
+    m_pDocView->GetDoc()->SetFocusWidget(hWidget);
+  }
+  return hWidget->OnRButtonDown(dwFlags, hWidget->Rotate2Normal(point));
 }
 
 bool CXFA_FFWidgetHandler::OnRButtonUp(CXFA_FFWidget* hWidget,
-                                       uint32_t dwFlags,
+                                       Mask<XFA_FWL_KeyFlag> dwFlags,
                                        const CFX_PointF& point) {
-  bool bRet = hWidget->OnRButtonUp(dwFlags, hWidget->Rotate2Normal(point));
-  return bRet;
+  return hWidget->OnRButtonUp(dwFlags, hWidget->Rotate2Normal(point));
 }
 
 bool CXFA_FFWidgetHandler::OnRButtonDblClk(CXFA_FFWidget* hWidget,
-                                           uint32_t dwFlags,
+                                           Mask<XFA_FWL_KeyFlag> dwFlags,
                                            const CFX_PointF& point) {
-  bool bRet = hWidget->OnRButtonDblClk(dwFlags, hWidget->Rotate2Normal(point));
-  return bRet;
+  return hWidget->OnRButtonDblClk(dwFlags, hWidget->Rotate2Normal(point));
 }
 
 bool CXFA_FFWidgetHandler::OnKeyDown(CXFA_FFWidget* hWidget,
-                                     uint32_t dwKeyCode,
-                                     uint32_t dwFlags) {
+                                     XFA_FWL_VKEYCODE dwKeyCode,
+                                     Mask<XFA_FWL_KeyFlag> dwFlags) {
   bool bRet = hWidget->OnKeyDown(dwKeyCode, dwFlags);
   m_pDocView->UpdateDocView();
   return bRet;
 }
 
-bool CXFA_FFWidgetHandler::OnKeyUp(CXFA_FFWidget* hWidget,
-                                   uint32_t dwKeyCode,
-                                   uint32_t dwFlags) {
-  bool bRet = hWidget->OnKeyUp(dwKeyCode, dwFlags);
-  return bRet;
-}
-
 bool CXFA_FFWidgetHandler::OnChar(CXFA_FFWidget* hWidget,
                                   uint32_t dwChar,
-                                  uint32_t dwFlags) {
-  bool bRet = hWidget->OnChar(dwChar, dwFlags);
-  return bRet;
+                                  Mask<XFA_FWL_KeyFlag> dwFlags) {
+  return hWidget->OnChar(dwChar, dwFlags);
 }
 
 WideString CXFA_FFWidgetHandler::GetText(CXFA_FFWidget* widget) {
@@ -166,6 +142,14 @@
   widget->Paste(text);
 }
 
+bool CXFA_FFWidgetHandler::SelectAllText(CXFA_FFWidget* widget) {
+  if (!widget->CanSelectAll())
+    return false;
+
+  widget->SelectAll();
+  return true;
+}
+
 bool CXFA_FFWidgetHandler::CanUndo(CXFA_FFWidget* widget) {
   return widget->CanUndo();
 }
@@ -184,18 +168,13 @@
 
 FWL_WidgetHit CXFA_FFWidgetHandler::HitTest(CXFA_FFWidget* pWidget,
                                             const CFX_PointF& point) {
-  if (!pWidget->GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_Visible))
+  if (!pWidget->GetLayoutItem()->TestStatusBits(XFA_WidgetStatus::kVisible))
     return FWL_WidgetHit::Unknown;
   return pWidget->HitTest(pWidget->Rotate2Normal(point));
 }
 
-bool CXFA_FFWidgetHandler::OnSetCursor(CXFA_FFWidget* hWidget,
-                                       const CFX_PointF& point) {
-  return hWidget->OnSetCursor(hWidget->Rotate2Normal(point));
-}
-
 void CXFA_FFWidgetHandler::RenderWidget(CXFA_FFWidget* hWidget,
-                                        CXFA_Graphics* pGS,
+                                        CFGAS_GEGraphics* pGS,
                                         const CFX_Matrix& matrix,
                                         bool bHighlight) {
   hWidget->RenderWidget(
@@ -222,7 +201,7 @@
     default:
       break;
   }
-  return !pNode->GetEventByActivity(gs_EventActivity[eEventType], false)
+  return !pNode->GetEventByActivity(kXFAEventActivity[eEventType], false)
               .empty();
 }
 
@@ -237,10 +216,8 @@
     case XFA_EVENT_Calculate:
       return pNode->ProcessCalculate(m_pDocView.Get());
     case XFA_EVENT_Validate:
-      if (m_pDocView->GetDoc()->GetDocEnvironment()->IsValidationsEnabled(
-              m_pDocView->GetDoc())) {
+      if (m_pDocView->GetDoc()->IsValidationsEnabled())
         return pNode->ProcessValidate(m_pDocView.Get(), 0);
-      }
       return XFA_EventError::kDisabled;
     case XFA_EVENT_InitCalculate: {
       CXFA_Calculate* calc = pNode->GetCalculateIfExists();
@@ -255,5 +232,5 @@
       break;
   }
   return pNode->ProcessEvent(m_pDocView.Get(),
-                             gs_EventActivity[pParam->m_eType], pParam);
+                             kXFAEventActivity[pParam->m_eType], pParam);
 }
diff --git a/xfa/fxfa/cxfa_ffwidgethandler.h b/xfa/fxfa/cxfa_ffwidgethandler.h
index 172a8c6..eca6f67 100644
--- a/xfa/fxfa/cxfa_ffwidgethandler.h
+++ b/xfa/fxfa/cxfa_ffwidgethandler.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,71 +7,86 @@
 #ifndef XFA_FXFA_CXFA_FFWIDGETHANDLER_H_
 #define XFA_FXFA_CXFA_FFWIDGETHANDLER_H_
 
-#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/mask.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
+#include "xfa/fwl/cfwl_message.h"
+#include "xfa/fwl/fwl_widgetdef.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
-#include "xfa/fxfa/fxfa.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 
+class CFGAS_GEGraphics;
 class CXFA_FFDocView;
-class CXFA_Graphics;
+class CXFA_FFWidget;
 enum class FWL_WidgetHit;
 
-class CXFA_FFWidgetHandler {
+class CXFA_FFWidgetHandler final
+    : public cppgc::GarbageCollected<CXFA_FFWidgetHandler> {
  public:
-  explicit CXFA_FFWidgetHandler(CXFA_FFDocView* pDocView);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FFWidgetHandler();
 
+  void Trace(cppgc::Visitor* visitor) const;
+
   bool OnMouseEnter(CXFA_FFWidget* hWidget);
   bool OnMouseExit(CXFA_FFWidget* hWidget);
   bool OnLButtonDown(CXFA_FFWidget* hWidget,
-                     uint32_t dwFlags,
+                     Mask<XFA_FWL_KeyFlag> dwFlags,
                      const CFX_PointF& point);
   bool OnLButtonUp(CXFA_FFWidget* hWidget,
-                   uint32_t dwFlags,
+                   Mask<XFA_FWL_KeyFlag> dwFlags,
                    const CFX_PointF& point);
   bool OnLButtonDblClk(CXFA_FFWidget* hWidget,
-                       uint32_t dwFlags,
+                       Mask<XFA_FWL_KeyFlag> dwFlags,
                        const CFX_PointF& point);
   bool OnMouseMove(CXFA_FFWidget* hWidget,
-                   uint32_t dwFlags,
+                   Mask<XFA_FWL_KeyFlag> dwFlags,
                    const CFX_PointF& point);
   bool OnMouseWheel(CXFA_FFWidget* hWidget,
-                    uint32_t dwFlags,
-                    int16_t zDelta,
-                    const CFX_PointF& point);
+                    Mask<XFA_FWL_KeyFlag> dwFlags,
+                    const CFX_PointF& point,
+                    const CFX_Vector& delta);
   bool OnRButtonDown(CXFA_FFWidget* hWidget,
-                     uint32_t dwFlags,
+                     Mask<XFA_FWL_KeyFlag> dwFlags,
                      const CFX_PointF& point);
   bool OnRButtonUp(CXFA_FFWidget* hWidget,
-                   uint32_t dwFlags,
+                   Mask<XFA_FWL_KeyFlag> dwFlags,
                    const CFX_PointF& point);
   bool OnRButtonDblClk(CXFA_FFWidget* hWidget,
-                       uint32_t dwFlags,
+                       Mask<XFA_FWL_KeyFlag> dwFlags,
                        const CFX_PointF& point);
 
   WideString GetText(CXFA_FFWidget* widget);
   WideString GetSelectedText(CXFA_FFWidget* widget);
   void PasteText(CXFA_FFWidget* widget, const WideString& text);
+  bool SelectAllText(CXFA_FFWidget* widget);
 
   bool CanUndo(CXFA_FFWidget* widget);
   bool CanRedo(CXFA_FFWidget* widget);
   bool Undo(CXFA_FFWidget* widget);
   bool Redo(CXFA_FFWidget* widget);
 
-  bool OnKeyDown(CXFA_FFWidget* hWidget, uint32_t dwKeyCode, uint32_t dwFlags);
-  bool OnKeyUp(CXFA_FFWidget* hWidget, uint32_t dwKeyCode, uint32_t dwFlags);
-  bool OnChar(CXFA_FFWidget* hWidget, uint32_t dwChar, uint32_t dwFlags);
-  bool OnSetCursor(CXFA_FFWidget* hWidget, const CFX_PointF& point);
+  bool OnKeyDown(CXFA_FFWidget* hWidget,
+                 XFA_FWL_VKEYCODE dwKeyCode,
+                 Mask<XFA_FWL_KeyFlag> dwFlags);
+  bool OnChar(CXFA_FFWidget* hWidget,
+              uint32_t dwChar,
+              Mask<XFA_FWL_KeyFlag> dwFlags);
   FWL_WidgetHit HitTest(CXFA_FFWidget* pWidget, const CFX_PointF& point);
   void RenderWidget(CXFA_FFWidget* hWidget,
-                    CXFA_Graphics* pGS,
+                    CFGAS_GEGraphics* pGS,
                     const CFX_Matrix& matrix,
                     bool bHighlight);
   bool HasEvent(CXFA_Node* pNode, XFA_EVENTTYPE eEventType);
   XFA_EventError ProcessEvent(CXFA_Node* pNode, CXFA_EventParam* pParam);
 
  private:
-  UnownedPtr<CXFA_FFDocView> m_pDocView;
+  explicit CXFA_FFWidgetHandler(CXFA_FFDocView* pDocView);
+
+  cppgc::Member<CXFA_FFDocView> m_pDocView;
 };
 
 #endif  //  XFA_FXFA_CXFA_FFWIDGETHANDLER_H_
diff --git a/xfa/fxfa/cxfa_fontmgr.cpp b/xfa/fxfa/cxfa_fontmgr.cpp
index 7259f6c..98691fc 100644
--- a/xfa/fxfa/cxfa_fontmgr.cpp
+++ b/xfa/fxfa/cxfa_fontmgr.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,12 +6,7 @@
 
 #include "xfa/fxfa/cxfa_fontmgr.h"
 
-#include <algorithm>
-#include <memory>
-#include <utility>
-
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fxge/cfx_fontmgr.h"
 #include "core/fxge/cfx_gemodule.h"
 #include "xfa/fgas/font/cfgas_defaultfontmanager.h"
@@ -24,43 +19,38 @@
 
 CXFA_FontMgr::~CXFA_FontMgr() = default;
 
-RetainPtr<CFGAS_GEFont> CXFA_FontMgr::GetFont(CXFA_FFDoc* hDoc,
-                                              WideStringView wsFontFamily,
+void CXFA_FontMgr::Trace(cppgc::Visitor* visitor) const {}
+
+RetainPtr<CFGAS_GEFont> CXFA_FontMgr::GetFont(CXFA_FFDoc* pDoc,
+                                              const WideString& wsFontFamily,
                                               uint32_t dwFontStyles) {
-  uint32_t dwHash = FX_HashCode_GetW(wsFontFamily, false);
-  ByteString bsKey = ByteString::Format("%u%u%u", dwHash, dwFontStyles, 0xFFFF);
-  auto iter = m_FontMap.find(bsKey);
+  auto key = std::make_pair(wsFontFamily, dwFontStyles);
+  auto iter = m_FontMap.find(key);
   if (iter != m_FontMap.end())
     return iter->second;
 
   WideString wsEnglishName = FGAS_FontNameToEnglishName(wsFontFamily);
-  CFGAS_PDFFontMgr* pMgr = hDoc->GetPDFFontMgr();
-  RetainPtr<CFGAS_GEFont> pFont;
-  if (pMgr) {
-    pFont = pMgr->GetFont(wsEnglishName.AsStringView(), dwFontStyles, true);
+  RetainPtr<CFGAS_GEFont> pFont =
+      pDoc->GetPDFFont(wsEnglishName, dwFontStyles, true);
+  if (pFont)
+    return pFont;
+
+  pFont = CFGAS_DefaultFontManager::GetFont(wsFontFamily, dwFontStyles);
+  if (!pFont) {
+    pFont = pDoc->GetPDFFont(wsEnglishName, dwFontStyles, false);
     if (pFont)
       return pFont;
   }
   if (!pFont) {
-    pFont = CFGAS_DefaultFontManager::GetFont(hDoc->GetApp()->GetFDEFontMgr(),
-                                              wsFontFamily, dwFontStyles);
-  }
-  if (!pFont && pMgr) {
-    pFont = pMgr->GetFont(wsEnglishName.AsStringView(), dwFontStyles, false);
-    if (pFont)
-      return pFont;
-  }
-  if (!pFont) {
-    pFont = CFGAS_DefaultFontManager::GetDefaultFont(
-        hDoc->GetApp()->GetFDEFontMgr(), wsFontFamily, dwFontStyles);
+    pFont = CFGAS_DefaultFontManager::GetDefaultFont(dwFontStyles);
   }
   if (!pFont) {
     pFont = CFGAS_GEFont::LoadStockFont(
-        hDoc->GetPDFDoc(), hDoc->GetApp()->GetFDEFontMgr(),
-        ByteString::Format("%ls", WideString(wsFontFamily).c_str()));
+        pDoc->GetPDFDoc(), ByteString::Format("%ls", wsFontFamily.c_str()));
   }
-  if (pFont)
-    m_FontMap[bsKey] = pFont;
+  if (!pFont)
+    return nullptr;
 
+  m_FontMap[key] = pFont;
   return pFont;
 }
diff --git a/xfa/fxfa/cxfa_fontmgr.h b/xfa/fxfa/cxfa_fontmgr.h
index 2ad45dc..3191a90 100644
--- a/xfa/fxfa/cxfa_fontmgr.h
+++ b/xfa/fxfa/cxfa_fontmgr.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,23 +8,30 @@
 #define XFA_FXFA_CXFA_FONTMGR_H_
 
 #include <map>
+#include <utility>
 
 #include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
 
 class CFGAS_GEFont;
 class CXFA_FFDoc;
 
-class CXFA_FontMgr {
+class CXFA_FontMgr final : public cppgc::GarbageCollected<CXFA_FontMgr> {
  public:
-  CXFA_FontMgr();
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FontMgr();
 
+  void Trace(cppgc::Visitor* visitor) const;
   RetainPtr<CFGAS_GEFont> GetFont(CXFA_FFDoc* hDoc,
-                                  WideStringView wsFontFamily,
+                                  const WideString& wsFontFamily,
                                   uint32_t dwFontStyles);
 
  private:
-  std::map<ByteString, RetainPtr<CFGAS_GEFont>> m_FontMap;
+  CXFA_FontMgr();
+
+  std::map<std::pair<WideString, uint32_t>, RetainPtr<CFGAS_GEFont>> m_FontMap;
 };
 
 #endif  //  XFA_FXFA_CXFA_FONTMGR_H_
diff --git a/xfa/fxfa/cxfa_fwladapterwidgetmgr.cpp b/xfa/fxfa/cxfa_fwladapterwidgetmgr.cpp
index a67bf08..955d0c6 100644
--- a/xfa/fxfa/cxfa_fwladapterwidgetmgr.cpp
+++ b/xfa/fxfa/cxfa_fwladapterwidgetmgr.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,12 +6,15 @@
 
 #include "xfa/fxfa/cxfa_fwladapterwidgetmgr.h"
 
+#include "core/fxcrt/fx_coordinates.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_fffield.h"
 
-CXFA_FWLAdapterWidgetMgr::CXFA_FWLAdapterWidgetMgr() {}
+CXFA_FWLAdapterWidgetMgr::CXFA_FWLAdapterWidgetMgr() = default;
 
-CXFA_FWLAdapterWidgetMgr::~CXFA_FWLAdapterWidgetMgr() {}
+CXFA_FWLAdapterWidgetMgr::~CXFA_FWLAdapterWidgetMgr() = default;
+
+void CXFA_FWLAdapterWidgetMgr::Trace(cppgc::Visitor* visitor) const {}
 
 void CXFA_FWLAdapterWidgetMgr::RepaintWidget(CFWL_Widget* pWidget) {
   if (!pWidget)
@@ -32,7 +35,7 @@
   auto* pFFWidget = static_cast<CXFA_FFWidget*>(pWidget->GetAdapterIface());
   CFX_RectF rtRotateAnchor =
       pFFWidget->GetRotateMatrix().TransformRect(rtAnchor);
-  pFFWidget->GetDoc()->GetDocEnvironment()->GetPopupPos(
-      pFFWidget, fMinHeight, fMaxHeight, rtRotateAnchor, pPopupRect);
+  pFFWidget->GetDoc()->GetPopupPos(pFFWidget, fMinHeight, fMaxHeight,
+                                   rtRotateAnchor, pPopupRect);
   return true;
 }
diff --git a/xfa/fxfa/cxfa_fwladapterwidgetmgr.h b/xfa/fxfa/cxfa_fwladapterwidgetmgr.h
index 4a50c28..232b914 100644
--- a/xfa/fxfa/cxfa_fwladapterwidgetmgr.h
+++ b/xfa/fxfa/cxfa_fwladapterwidgetmgr.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,23 +7,31 @@
 #ifndef XFA_FXFA_CXFA_FWLADAPTERWIDGETMGR_H_
 #define XFA_FXFA_CXFA_FWLADAPTERWIDGETMGR_H_
 
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
 
 class CFWL_Widget;
+class CFX_RectF;
 
-class CXFA_FWLAdapterWidgetMgr : public CFWL_WidgetMgr::AdapterIface {
+class CXFA_FWLAdapterWidgetMgr final
+    : public cppgc::GarbageCollected<CXFA_FWLAdapterWidgetMgr>,
+      public CFWL_WidgetMgr::AdapterIface {
  public:
-  CXFA_FWLAdapterWidgetMgr();
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FWLAdapterWidgetMgr() override;
 
+  // CFWL_WidgetMgr::AdapterIface:
+  void Trace(cppgc::Visitor* visitor) const override;
   void RepaintWidget(CFWL_Widget* pWidget) override;
   bool GetPopupPos(CFWL_Widget* pWidget,
                    float fMinHeight,
                    float fMaxHeight,
                    const CFX_RectF& rtAnchor,
                    CFX_RectF* pPopupRect) override;
+
+ private:
+  CXFA_FWLAdapterWidgetMgr();
 };
 
 #endif  // XFA_FXFA_CXFA_FWLADAPTERWIDGETMGR_H_
diff --git a/xfa/fxfa/cxfa_fwltheme.cpp b/xfa/fxfa/cxfa_fwltheme.cpp
index 4c2c8da..1fa3738 100644
--- a/xfa/fxfa/cxfa_fwltheme.cpp
+++ b/xfa/fxfa/cxfa_fwltheme.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,9 +7,11 @@
 #include "xfa/fxfa/cxfa_fwltheme.h"
 
 #include "core/fxcrt/fx_codepage.h"
-#include "third_party/base/ptr_util.h"
 #include "xfa/fde/cfde_textout.h"
+#include "xfa/fgas/font/cfgas_fontmgr.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
+#include "xfa/fgas/font/cfgas_gemodule.h"
+#include "xfa/fgas/graphics/cfgas_gecolor.h"
 #include "xfa/fwl/cfwl_barcode.h"
 #include "xfa/fwl/cfwl_caret.h"
 #include "xfa/fwl/cfwl_checkbox.h"
@@ -26,18 +28,20 @@
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
+#include "xfa/fxfa/cxfa_fontmgr.h"
 #include "xfa/fxfa/parser/cxfa_para.h"
-#include "xfa/fxgraphics/cxfa_gecolor.h"
 
 namespace {
 
-const wchar_t* const g_FWLTheme_CalFonts[] = {
-    L"Arial", L"Courier New", L"DejaVu Sans",
+constexpr const wchar_t* kFWLThemeCalFonts[] = {
+    L"Arial",
+    L"Courier New",
+    L"DejaVu Sans",
 };
 
 const float kLineHeight = 12.0f;
 
-CXFA_FFWidget* XFA_ThemeGetOuterWidget(CFWL_Widget* pWidget) {
+CXFA_FFWidget* GetOutmostFFWidget(CFWL_Widget* pWidget) {
   CFWL_Widget* pOuter = pWidget ? pWidget->GetOutmost() : nullptr;
   return pOuter ? static_cast<CXFA_FFWidget*>(pOuter->GetAdapterIface())
                 : nullptr;
@@ -45,55 +49,47 @@
 
 }  // namespace
 
-CXFA_FWLTheme::CXFA_FWLTheme(CXFA_FFApp* pApp)
-    : m_pCheckBoxTP(pdfium::MakeUnique<CFWL_CheckBoxTP>()),
-      m_pListBoxTP(pdfium::MakeUnique<CFWL_ListBoxTP>()),
-      m_pPictureBoxTP(pdfium::MakeUnique<CFWL_PictureBoxTP>()),
-      m_pSrollBarTP(pdfium::MakeUnique<CFWL_ScrollBarTP>()),
-      m_pEditTP(pdfium::MakeUnique<CFWL_EditTP>()),
-      m_pComboBoxTP(pdfium::MakeUnique<CFWL_ComboBoxTP>()),
-      m_pMonthCalendarTP(pdfium::MakeUnique<CFWL_MonthCalendarTP>()),
-      m_pDateTimePickerTP(pdfium::MakeUnique<CFWL_DateTimePickerTP>()),
-      m_pPushButtonTP(pdfium::MakeUnique<CFWL_PushButtonTP>()),
-      m_pCaretTP(pdfium::MakeUnique<CFWL_CaretTP>()),
-      m_pBarcodeTP(pdfium::MakeUnique<CFWL_BarcodeTP>()),
-      m_pTextOut(pdfium::MakeUnique<CFDE_TextOut>()),
-      m_pApp(pApp) {
+CXFA_FWLTheme::CXFA_FWLTheme(cppgc::Heap* pHeap, CXFA_FFApp* pApp)
+    : IFWL_ThemeProvider(pHeap),
+      m_pTextOut(std::make_unique<CFDE_TextOut>()),
+      m_pApp(pApp) {}
+
+CXFA_FWLTheme::~CXFA_FWLTheme() = default;
+
+void CXFA_FWLTheme::PreFinalize() {
+  m_pTextOut.reset();
+}
+
+void CXFA_FWLTheme::Trace(cppgc::Visitor* visitor) const {
+  IFWL_ThemeProvider::Trace(visitor);
+  visitor->Trace(m_pApp);
 }
 
 bool CXFA_FWLTheme::LoadCalendarFont(CXFA_FFDoc* doc) {
-  for (size_t i = 0; !m_pCalendarFont && i < FX_ArraySize(g_FWLTheme_CalFonts);
-       ++i) {
-    m_pCalendarFont =
-        m_pApp->GetXFAFontMgr()->GetFont(doc, g_FWLTheme_CalFonts[i], 0);
+  if (m_pCalendarFont)
+    return true;
+
+  for (const wchar_t* font : kFWLThemeCalFonts) {
+    m_pCalendarFont = m_pApp->GetXFAFontMgr()->GetFont(doc, font, 0);
+    if (m_pCalendarFont)
+      return true;
   }
 
-  if (!m_pCalendarFont) {
-    CFGAS_FontMgr* font_mgr = m_pApp->GetFDEFontMgr();
-    if (font_mgr) {
-      m_pCalendarFont = font_mgr->GetFontByCodePage(
-          FX_CODEPAGE_MSWin_WesternEuropean, 0, nullptr);
-    }
-  }
-
-  return m_pCalendarFont != nullptr;
-}
-
-CXFA_FWLTheme::~CXFA_FWLTheme() {
-  m_pTextOut.reset();
-  CFWL_FontManager::DestroyInstance();
+  m_pCalendarFont = CFGAS_GEModule::Get()->GetFontMgr()->GetFontByCodePage(
+      FX_CodePage::kMSWin_WesternEuropean, 0, nullptr);
+  return !!m_pCalendarFont;
 }
 
 void CXFA_FWLTheme::DrawBackground(const CFWL_ThemeBackground& pParams) {
-  GetTheme(pParams.m_pWidget)->DrawBackground(pParams);
+  GetTheme(pParams.GetWidget())->DrawBackground(pParams);
 }
 
 void CXFA_FWLTheme::DrawText(const CFWL_ThemeText& pParams) {
   if (pParams.m_wsText.IsEmpty())
     return;
 
-  if (pParams.m_pWidget->GetClassID() == FWL_Type::MonthCalendar) {
-    CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pParams.m_pWidget);
+  if (pParams.GetWidget()->GetClassID() == FWL_Type::MonthCalendar) {
+    CXFA_FFWidget* pWidget = GetOutmostFFWidget(pParams.GetWidget());
     if (!pWidget)
       return;
 
@@ -102,16 +98,17 @@
     m_pTextOut->SetFont(m_pCalendarFont);
     m_pTextOut->SetFontSize(FWLTHEME_CAPACITY_FontSize);
     m_pTextOut->SetTextColor(FWLTHEME_CAPACITY_TextColor);
-    if ((pParams.m_iPart == CFWL_Part::DatesIn) &&
-        !(pParams.m_dwStates & FWL_ITEMSTATE_MCD_Flag) &&
+    if ((pParams.GetPart() == CFWL_ThemePart::Part::kDatesIn) &&
+        !(pParams.m_dwStates & CFWL_PartState::kFlagged) &&
         (pParams.m_dwStates &
-         (CFWL_PartState_Hovered | CFWL_PartState_Selected))) {
+         Mask<CFWL_PartState>{CFWL_PartState::kHovered,
+                              CFWL_PartState::kSelected})) {
       m_pTextOut->SetTextColor(0xFF888888);
     }
-    if (pParams.m_iPart == CFWL_Part::Caption)
+    if (pParams.GetPart() == CFWL_ThemePart::Part::kCaption)
       m_pTextOut->SetTextColor(ArgbEncode(0xff, 0, 153, 255));
 
-    CXFA_Graphics* pGraphics = pParams.m_pGraphics;
+    CFGAS_GEGraphics* pGraphics = pParams.GetGraphics();
     CFX_RenderDevice* pRenderDevice = pGraphics->GetRenderDevice();
     CFX_Matrix mtPart = pParams.m_matrix;
     const CFX_Matrix* pMatrix = pGraphics->GetMatrix();
@@ -119,20 +116,20 @@
       mtPart.Concat(*pMatrix);
 
     m_pTextOut->SetMatrix(mtPart);
-    m_pTextOut->DrawLogicText(pRenderDevice, pParams.m_wsText.AsStringView(),
-                              pParams.m_rtPart);
+    m_pTextOut->DrawLogicText(pRenderDevice, pParams.m_wsText,
+                              pParams.m_PartRect);
     return;
   }
-  CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pParams.m_pWidget);
+  CXFA_FFWidget* pWidget = GetOutmostFFWidget(pParams.GetWidget());
   if (!pWidget)
     return;
 
   CXFA_Node* pNode = pWidget->GetNode();
-  CXFA_Graphics* pGraphics = pParams.m_pGraphics;
+  CFGAS_GEGraphics* pGraphics = pParams.GetGraphics();
   CFX_RenderDevice* pRenderDevice = pGraphics->GetRenderDevice();
   m_pTextOut->SetStyles(pParams.m_dwTTOStyles);
   m_pTextOut->SetAlignment(pParams.m_iTTOAlign);
-  m_pTextOut->SetFont(pNode->GetFDEFont(pWidget->GetDoc()));
+  m_pTextOut->SetFont(pNode->GetFGASFont(pWidget->GetDoc()));
   m_pTextOut->SetFontSize(pNode->GetFontSize());
   m_pTextOut->SetTextColor(pNode->GetTextColor());
   CFX_Matrix mtPart = pParams.m_matrix;
@@ -141,12 +138,12 @@
     mtPart.Concat(*pMatrix);
 
   m_pTextOut->SetMatrix(mtPart);
-  m_pTextOut->DrawLogicText(pRenderDevice, pParams.m_wsText.AsStringView(),
-                            pParams.m_rtPart);
+  m_pTextOut->DrawLogicText(pRenderDevice, pParams.m_wsText,
+                            pParams.m_PartRect);
 }
 
 CFX_RectF CXFA_FWLTheme::GetUIMargin(const CFWL_ThemePart& pThemePart) const {
-  CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart.m_pWidget);
+  CXFA_FFWidget* pWidget = GetOutmostFFWidget(pThemePart.GetWidget());
   if (!pWidget)
     return CFX_RectF();
 
@@ -180,20 +177,28 @@
 }
 
 float CXFA_FWLTheme::GetFontSize(const CFWL_ThemePart& pThemePart) const {
-  if (CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart.m_pWidget))
+  if (CXFA_FFWidget* pWidget = GetOutmostFFWidget(pThemePart.GetWidget()))
     return pWidget->GetNode()->GetFontSize();
   return FWLTHEME_CAPACITY_FontSize;
 }
 
 RetainPtr<CFGAS_GEFont> CXFA_FWLTheme::GetFont(
-    const CFWL_ThemePart& pThemePart) const {
-  if (CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart.m_pWidget))
-    return pWidget->GetNode()->GetFDEFont(pWidget->GetDoc());
-  return GetTheme(pThemePart.m_pWidget)->GetFont();
+    const CFWL_ThemePart& pThemePart) {
+  if (CXFA_FFWidget* pWidget = GetOutmostFFWidget(pThemePart.GetWidget()))
+    return pWidget->GetNode()->GetFGASFont(pWidget->GetDoc());
+
+  return GetFWLFont();
+}
+
+RetainPtr<CFGAS_GEFont> CXFA_FWLTheme::GetFWLFont() {
+  if (!m_pFWLFont)
+    m_pFWLFont = CFGAS_GEFont::LoadFont(L"Helvetica", 0, FX_CodePage::kDefANSI);
+
+  return m_pFWLFont;
 }
 
 float CXFA_FWLTheme::GetLineHeight(const CFWL_ThemePart& pThemePart) const {
-  if (CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart.m_pWidget))
+  if (CXFA_FFWidget* pWidget = GetOutmostFFWidget(pThemePart.GetWidget()))
     return pWidget->GetNode()->GetLineHeight();
   return kLineHeight;
 }
@@ -204,7 +209,7 @@
 
 FX_COLORREF CXFA_FWLTheme::GetTextColor(
     const CFWL_ThemePart& pThemePart) const {
-  if (CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart.m_pWidget))
+  if (CXFA_FFWidget* pWidget = GetOutmostFFWidget(pThemePart.GetWidget()))
     return pWidget->GetNode()->GetTextColor();
   return FWLTHEME_CAPACITY_TextColor;
 }
@@ -212,7 +217,7 @@
 CFX_SizeF CXFA_FWLTheme::GetSpaceAboveBelow(
     const CFWL_ThemePart& pThemePart) const {
   CFX_SizeF sizeAboveBelow;
-  if (CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart.m_pWidget)) {
+  if (CXFA_FFWidget* pWidget = GetOutmostFFWidget(pThemePart.GetWidget())) {
     CXFA_Para* para = pWidget->GetNode()->GetParaIfExists();
     if (para) {
       sizeAboveBelow.width = para->GetSpaceAbove();
@@ -224,14 +229,11 @@
 
 void CXFA_FWLTheme::CalcTextRect(const CFWL_ThemeText& pParams,
                                  CFX_RectF* pRect) {
-  if (!m_pTextOut)
-    return;
-
-  CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pParams.m_pWidget);
+  CXFA_FFWidget* pWidget = GetOutmostFFWidget(pParams.GetWidget());
   if (!pWidget)
     return;
 
-  if (pParams.m_pWidget->GetClassID() == FWL_Type::MonthCalendar) {
+  if (pParams.GetWidget()->GetClassID() == FWL_Type::MonthCalendar) {
     m_pTextOut->SetFont(m_pCalendarFont);
     m_pTextOut->SetFontSize(FWLTHEME_CAPACITY_FontSize);
     m_pTextOut->SetTextColor(FWLTHEME_CAPACITY_TextColor);
@@ -242,39 +244,10 @@
   }
 
   CXFA_Node* pNode = pWidget->GetNode();
-  m_pTextOut->SetFont(pNode->GetFDEFont(pWidget->GetDoc()));
+  m_pTextOut->SetFont(pNode->GetFGASFont(pWidget->GetDoc()));
   m_pTextOut->SetFontSize(pNode->GetFontSize());
   m_pTextOut->SetTextColor(pNode->GetTextColor());
   m_pTextOut->SetAlignment(pParams.m_iTTOAlign);
   m_pTextOut->SetStyles(pParams.m_dwTTOStyles);
   m_pTextOut->CalcLogicSize(pParams.m_wsText.AsStringView(), pRect);
 }
-
-CFWL_WidgetTP* CXFA_FWLTheme::GetTheme(CFWL_Widget* pWidget) const {
-  switch (pWidget->GetClassID()) {
-    case FWL_Type::CheckBox:
-      return m_pCheckBoxTP.get();
-    case FWL_Type::ListBox:
-      return m_pListBoxTP.get();
-    case FWL_Type::PictureBox:
-      return m_pPictureBoxTP.get();
-    case FWL_Type::ScrollBar:
-      return m_pSrollBarTP.get();
-    case FWL_Type::Edit:
-      return m_pEditTP.get();
-    case FWL_Type::ComboBox:
-      return m_pComboBoxTP.get();
-    case FWL_Type::MonthCalendar:
-      return m_pMonthCalendarTP.get();
-    case FWL_Type::DateTimePicker:
-      return m_pDateTimePickerTP.get();
-    case FWL_Type::PushButton:
-      return m_pPushButtonTP.get();
-    case FWL_Type::Caret:
-      return m_pCaretTP.get();
-    case FWL_Type::Barcode:
-      return m_pBarcodeTP.get();
-    default:
-      return nullptr;
-  }
-}
diff --git a/xfa/fxfa/cxfa_fwltheme.h b/xfa/fxfa/cxfa_fwltheme.h
index 11f2584..b42b5c5 100644
--- a/xfa/fxfa/cxfa_fwltheme.h
+++ b/xfa/fxfa/cxfa_fwltheme.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,29 +9,30 @@
 
 #include <memory>
 
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/prefinalizer.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
-#include "xfa/fwl/theme/cfwl_barcodetp.h"
-#include "xfa/fwl/theme/cfwl_carettp.h"
-#include "xfa/fwl/theme/cfwl_checkboxtp.h"
-#include "xfa/fwl/theme/cfwl_comboboxtp.h"
-#include "xfa/fwl/theme/cfwl_datetimepickertp.h"
-#include "xfa/fwl/theme/cfwl_edittp.h"
-#include "xfa/fwl/theme/cfwl_listboxtp.h"
-#include "xfa/fwl/theme/cfwl_monthcalendartp.h"
-#include "xfa/fwl/theme/cfwl_pictureboxtp.h"
-#include "xfa/fwl/theme/cfwl_pushbuttontp.h"
-#include "xfa/fwl/theme/cfwl_scrollbartp.h"
-#include "xfa/fwl/theme/cfwl_widgettp.h"
-#include "xfa/fxfa/cxfa_ffapp.h"
 
-class CXFA_FWLTheme final : public IFWL_ThemeProvider {
+class CFDE_TextOut;
+class CXFA_FFApp;
+class CXFA_FFDoc;
+
+class CXFA_FWLTheme final : public cppgc::GarbageCollected<CXFA_FWLTheme>,
+                            public IFWL_ThemeProvider {
+  CPPGC_USING_PRE_FINALIZER(CXFA_FWLTheme, PreFinalize);
+
  public:
-  explicit CXFA_FWLTheme(CXFA_FFApp* pApp);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FWLTheme() override;
 
-  bool LoadCalendarFont(CXFA_FFDoc* doc);
+  void PreFinalize();
 
   // IFWL_ThemeProvider:
+  void Trace(cppgc::Visitor* visitor) const override;
   void DrawBackground(const CFWL_ThemeBackground& pParams) override;
   void DrawText(const CFWL_ThemeText& pParams) override;
   void CalcTextRect(const CFWL_ThemeText& pParams, CFX_RectF* pRect) override;
@@ -39,31 +40,23 @@
   float GetCYBorderSize() const override;
   CFX_RectF GetUIMargin(const CFWL_ThemePart& pThemePart) const override;
   float GetFontSize(const CFWL_ThemePart& pThemePart) const override;
-  RetainPtr<CFGAS_GEFont> GetFont(
-      const CFWL_ThemePart& pThemePart) const override;
+  RetainPtr<CFGAS_GEFont> GetFont(const CFWL_ThemePart& pThemePart) override;
+  RetainPtr<CFGAS_GEFont> GetFWLFont() override;
   float GetLineHeight(const CFWL_ThemePart& pThemePart) const override;
   float GetScrollBarWidth() const override;
   FX_COLORREF GetTextColor(const CFWL_ThemePart& pThemePart) const override;
   CFX_SizeF GetSpaceAboveBelow(const CFWL_ThemePart& pThemePart) const override;
 
- private:
-  CFWL_WidgetTP* GetTheme(CFWL_Widget* pWidget) const;
+  bool LoadCalendarFont(CXFA_FFDoc* doc);
 
-  std::unique_ptr<CFWL_CheckBoxTP> m_pCheckBoxTP;
-  std::unique_ptr<CFWL_ListBoxTP> m_pListBoxTP;
-  std::unique_ptr<CFWL_PictureBoxTP> m_pPictureBoxTP;
-  std::unique_ptr<CFWL_ScrollBarTP> m_pSrollBarTP;
-  std::unique_ptr<CFWL_EditTP> m_pEditTP;
-  std::unique_ptr<CFWL_ComboBoxTP> m_pComboBoxTP;
-  std::unique_ptr<CFWL_MonthCalendarTP> m_pMonthCalendarTP;
-  std::unique_ptr<CFWL_DateTimePickerTP> m_pDateTimePickerTP;
-  std::unique_ptr<CFWL_PushButtonTP> m_pPushButtonTP;
-  std::unique_ptr<CFWL_CaretTP> m_pCaretTP;
-  std::unique_ptr<CFWL_BarcodeTP> m_pBarcodeTP;
+ private:
+  CXFA_FWLTheme(cppgc::Heap* pHeap, CXFA_FFApp* pApp);
+
   std::unique_ptr<CFDE_TextOut> m_pTextOut;
+  RetainPtr<CFGAS_GEFont> m_pFWLFont;
   RetainPtr<CFGAS_GEFont> m_pCalendarFont;
+  cppgc::Member<CXFA_FFApp> const m_pApp;
   WideString m_wsResource;
-  UnownedPtr<CXFA_FFApp> const m_pApp;
   CFX_RectF m_Rect;
 };
 
diff --git a/xfa/fxfa/cxfa_imagerenderer.cpp b/xfa/fxfa/cxfa_imagerenderer.cpp
index d3b66c8..3b9ecc2 100644
--- a/xfa/fxfa/cxfa_imagerenderer.cpp
+++ b/xfa/fxfa/cxfa_imagerenderer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,17 +6,18 @@
 
 #include "xfa/fxfa/cxfa_imagerenderer.h"
 
+#include <math.h>
+
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/dib/cfx_dibbase.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/dib/cfx_imagerenderer.h"
 #include "core/fxge/dib/cfx_imagetransformer.h"
-#include "third_party/base/ptr_util.h"
 
 CXFA_ImageRenderer::CXFA_ImageRenderer(CFX_RenderDevice* pDevice,
                                        const RetainPtr<CFX_DIBBase>& pDIBBase,
-                                       const CFX_Matrix* pImage2Device)
-    : m_pDevice(pDevice), m_ImageMatrix(*pImage2Device), m_pDIBBase(pDIBBase) {}
+                                       const CFX_Matrix& pImage2Device)
+    : m_ImageMatrix(pImage2Device), m_pDevice(pDevice), m_pDIBBase(pDIBBase) {}
 
 CXFA_ImageRenderer::~CXFA_ImageRenderer() = default;
 
@@ -26,7 +27,7 @@
   if (m_pDevice->StartDIBits(m_pDIBBase, 255, 0, m_ImageMatrix, options,
                              &m_DeviceHandle)) {
     if (m_DeviceHandle) {
-      m_Status = 3;
+      m_State = State::kStarted;
       return true;
     }
     return false;
@@ -38,10 +39,10 @@
   if ((fabs(m_ImageMatrix.b) >= 0.5f || m_ImageMatrix.a == 0) ||
       (fabs(m_ImageMatrix.c) >= 0.5f || m_ImageMatrix.d == 0)) {
     RetainPtr<CFX_DIBBase> pDib = m_pDIBBase;
-    if (m_pDIBBase->HasAlpha() &&
+    if (m_pDIBBase->IsAlphaFormat() &&
         !(m_pDevice->GetRenderCaps() & FXRC_ALPHA_IMAGE) &&
         !(m_pDevice->GetRenderCaps() & FXRC_GET_BITS)) {
-      m_pCloneConvert = m_pDIBBase->CloneConvert(FXDIB_Rgb);
+      m_pCloneConvert = m_pDIBBase->ConvertTo(FXDIB_Format::kRgb);
       if (!m_pCloneConvert)
         return false;
 
@@ -49,18 +50,17 @@
     }
     FX_RECT clip_box = m_pDevice->GetClipBox();
     clip_box.Intersect(image_rect);
-    m_Status = 2;
-    m_pTransformer = pdfium::MakeUnique<CFX_ImageTransformer>(
-        pDib, m_ImageMatrix, options, &clip_box);
+    m_State = State::kTransforming;
+    m_pTransformer = std::make_unique<CFX_ImageTransformer>(pDib, m_ImageMatrix,
+                                                            options, &clip_box);
     return true;
   }
   if (m_ImageMatrix.a < 0)
     dest_width = -dest_width;
   if (m_ImageMatrix.d > 0)
     dest_height = -dest_height;
-  int dest_left, dest_top;
-  dest_left = dest_width > 0 ? image_rect.left : image_rect.right;
-  dest_top = dest_height > 0 ? image_rect.top : image_rect.bottom;
+  int dest_left = dest_width > 0 ? image_rect.left : image_rect.right;
+  int dest_top = dest_height > 0 ? image_rect.top : image_rect.bottom;
   if (m_pDIBBase->IsOpaqueImage()) {
     if (m_pDevice->StretchDIBitsWithFlagsAndBlend(
             m_pDIBBase, dest_left, dest_top, dest_width, dest_height, options,
@@ -68,7 +68,7 @@
       return false;
     }
   }
-  if (m_pDIBBase->IsAlphaMask()) {
+  if (m_pDIBBase->IsMaskFormat()) {
     if (m_pDevice->StretchBitMaskWithFlags(m_pDIBBase, dest_left, dest_top,
                                            dest_width, dest_height, 0,
                                            options)) {
@@ -91,7 +91,7 @@
 }
 
 bool CXFA_ImageRenderer::Continue() {
-  if (m_Status == 2) {
+  if (m_State == State::kTransforming) {
     if (m_pTransformer->Continue(nullptr))
       return true;
 
@@ -99,7 +99,7 @@
     if (!pBitmap)
       return false;
 
-    if (pBitmap->IsAlphaMask()) {
+    if (pBitmap->IsMaskFormat()) {
       m_pDevice->SetBitMask(pBitmap, m_pTransformer->result().left,
                             m_pTransformer->result().top, 0);
     } else {
@@ -109,7 +109,7 @@
     }
     return false;
   }
-  if (m_Status == 3)
+  if (m_State == State::kStarted)
     return m_pDevice->ContinueDIBits(m_DeviceHandle.get(), nullptr);
 
   return false;
@@ -122,7 +122,7 @@
   if (!pDIBitmap)
     return;
 
-  if (!pDIBitmap->IsAlphaMask()) {
+  if (!pDIBitmap->IsMaskFormat()) {
     if (m_pDevice->SetDIBits(pDIBitmap, left, top))
       return;
   } else if (m_pDevice->SetBitMask(pDIBitmap, left, top, 0)) {
@@ -133,23 +133,22 @@
                         (!(m_pDevice->GetRenderCaps() & FXRC_ALPHA_OUTPUT) &&
                          (m_pDevice->GetRenderCaps() & FXRC_GET_BITS));
   if (bGetBackGround) {
-    if (pDIBitmap->IsAlphaMask())
+    if (pDIBitmap->IsMaskFormat())
       return;
 
     m_pDevice->SetDIBitsWithBlend(pDIBitmap, left, top, BlendMode::kNormal);
     return;
   }
-  if (!pDIBitmap->HasAlpha() ||
+  if (!pDIBitmap->IsAlphaFormat() ||
       (m_pDevice->GetRenderCaps() & FXRC_ALPHA_IMAGE)) {
     return;
   }
 
-  RetainPtr<CFX_DIBitmap> pCloneConvert = pDIBitmap->CloneConvert(FXDIB_Rgb);
-  if (!pCloneConvert)
+  RetainPtr<CFX_DIBitmap> pConverted = pDIBitmap->ConvertTo(FXDIB_Format::kRgb);
+  if (!pConverted)
     return;
 
-  CXFA_ImageRenderer imageRender(m_pDevice.Get(), pCloneConvert,
-                                 &m_ImageMatrix);
+  CXFA_ImageRenderer imageRender(m_pDevice, pConverted, m_ImageMatrix);
   if (!imageRender.Start())
     return;
 
diff --git a/xfa/fxfa/cxfa_imagerenderer.h b/xfa/fxfa/cxfa_imagerenderer.h
index 37e45d0..32c9490 100644
--- a/xfa/fxfa/cxfa_imagerenderer.h
+++ b/xfa/fxfa/cxfa_imagerenderer.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,20 +23,22 @@
  public:
   CXFA_ImageRenderer(CFX_RenderDevice* pDevice,
                      const RetainPtr<CFX_DIBBase>& pDIBBase,
-                     const CFX_Matrix* pImage2Device);
+                     const CFX_Matrix& mtImage2Device);
   ~CXFA_ImageRenderer();
 
   bool Start();
   bool Continue();
 
  private:
+  enum class State : uint8_t { kInitial = 0, kTransforming, kStarted };
+
   void CompositeDIBitmap(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
                          int left,
                          int top);
 
-  UnownedPtr<CFX_RenderDevice> m_pDevice;
-  int m_Status = 0;
+  State m_State = State::kInitial;
   CFX_Matrix m_ImageMatrix;
+  UnownedPtr<CFX_RenderDevice> m_pDevice;
   RetainPtr<CFX_DIBBase> m_pDIBBase;
   RetainPtr<CFX_DIBitmap> m_pCloneConvert;
   std::unique_ptr<CFX_ImageTransformer> m_pTransformer;
diff --git a/xfa/fxfa/cxfa_loadercontext.cpp b/xfa/fxfa/cxfa_loadercontext.cpp
deleted file mode 100644
index 5f66050..0000000
--- a/xfa/fxfa/cxfa_loadercontext.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/cxfa_loadercontext.h"
-
-#include "core/fxcrt/css/cfx_csscomputedstyle.h"
-
-CXFA_LoaderContext::CXFA_LoaderContext() = default;
-
-CXFA_LoaderContext::~CXFA_LoaderContext() = default;
diff --git a/xfa/fxfa/cxfa_loadercontext.h b/xfa/fxfa/cxfa_loadercontext.h
deleted file mode 100644
index ddcb909..0000000
--- a/xfa/fxfa/cxfa_loadercontext.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_CXFA_LOADERCONTEXT_H_
-#define XFA_FXFA_CXFA_LOADERCONTEXT_H_
-
-#include <vector>
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class CFX_CSSComputedStyle;
-class CFX_XMLNode;
-class CXFA_Node;
-
-struct CXFA_BlockHeight {
-  size_t szBlockIndex;
-  float fHeight;
-};
-
-struct CXFA_LoaderContext {
-  CXFA_LoaderContext();
-  ~CXFA_LoaderContext();
-
-  bool bSaveLineHeight = false;
-  bool bFilterSpace = false;
-  float fWidth = 0;
-  float fHeight = 0;
-  float fLastPos = 0;
-  float fStartLineOffset = 0;
-  int32_t iChar = 0;
-  // TODO(thestig): Make this size_t?
-  int32_t iTotalLines = -1;
-  UnownedPtr<const CFX_XMLNode> pXMLNode;
-  UnownedPtr<CXFA_Node> pNode;
-  RetainPtr<CFX_CSSComputedStyle> pParentStyle;
-  std::vector<float> lineHeights;
-  std::vector<CXFA_BlockHeight> blockHeights;
-};
-
-#endif  // XFA_FXFA_CXFA_LOADERCONTEXT_H_
diff --git a/xfa/fxfa/cxfa_pieceline.cpp b/xfa/fxfa/cxfa_pieceline.cpp
deleted file mode 100644
index 6570ad5..0000000
--- a/xfa/fxfa/cxfa_pieceline.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/cxfa_pieceline.h"
-
-#include "xfa/fxfa/cxfa_textpiece.h"
-
-CXFA_PieceLine::CXFA_PieceLine() {}
-
-CXFA_PieceLine::~CXFA_PieceLine() {}
diff --git a/xfa/fxfa/cxfa_pieceline.h b/xfa/fxfa/cxfa_pieceline.h
deleted file mode 100644
index 0e233ac..0000000
--- a/xfa/fxfa/cxfa_pieceline.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_CXFA_PIECELINE_H_
-#define XFA_FXFA_CXFA_PIECELINE_H_
-
-#include <memory>
-#include <vector>
-
-class CXFA_TextPiece;
-
-class CXFA_PieceLine {
- public:
-  CXFA_PieceLine();
-  ~CXFA_PieceLine();
-
-  std::vector<std::unique_ptr<CXFA_TextPiece>> m_textPieces;
-  std::vector<size_t> m_charCounts;
-};
-
-#endif  // XFA_FXFA_CXFA_PIECELINE_H_
diff --git a/xfa/fxfa/cxfa_readynodeiterator.cpp b/xfa/fxfa/cxfa_readynodeiterator.cpp
index 728d9d6..d8d6b82 100644
--- a/xfa/fxfa/cxfa_readynodeiterator.cpp
+++ b/xfa/fxfa/cxfa_readynodeiterator.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,7 @@
 CXFA_ReadyNodeIterator::CXFA_ReadyNodeIterator(CXFA_Node* pTravelRoot)
     : m_ContentIterator(pTravelRoot) {}
 
-CXFA_ReadyNodeIterator::~CXFA_ReadyNodeIterator() {}
+CXFA_ReadyNodeIterator::~CXFA_ReadyNodeIterator() = default;
 
 CXFA_Node* CXFA_ReadyNodeIterator::MoveToNext() {
   CXFA_Node* pItem = m_pCurNode ? m_ContentIterator.MoveToNext()
@@ -19,7 +19,7 @@
   while (pItem) {
     m_pCurNode = pItem->IsWidgetReady() ? pItem : nullptr;
     if (m_pCurNode)
-      return m_pCurNode.Get();
+      return m_pCurNode;
     pItem = m_ContentIterator.MoveToNext();
   }
   return nullptr;
diff --git a/xfa/fxfa/cxfa_readynodeiterator.h b/xfa/fxfa/cxfa_readynodeiterator.h
index d3cd901..cc45b80 100644
--- a/xfa/fxfa/cxfa_readynodeiterator.h
+++ b/xfa/fxfa/cxfa_readynodeiterator.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,11 +8,14 @@
 #define XFA_FXFA_CXFA_READYNODEITERATOR_H_
 
 #include "core/fxcrt/unowned_ptr.h"
+#include "v8/include/cppgc/macros.h"
 #include "xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h"
 
 class CXFA_Node;
 
 class CXFA_ReadyNodeIterator {
+  CPPGC_STACK_ALLOCATED();  // Allow Raw/Unowned pointers.
+
  public:
   explicit CXFA_ReadyNodeIterator(CXFA_Node* pTravelRoot);
   ~CXFA_ReadyNodeIterator();
@@ -22,7 +25,7 @@
 
  private:
   CXFA_ContainerIterator m_ContentIterator;
-  UnownedPtr<CXFA_Node> m_pCurNode;
+  UnownedPtr<CXFA_Node> m_pCurNode;  // Ok, stack-only.
 };
 
 #endif  // XFA_FXFA_CXFA_READYNODEITERATOR_H_
diff --git a/xfa/fxfa/cxfa_rendercontext.cpp b/xfa/fxfa/cxfa_rendercontext.cpp
deleted file mode 100644
index 106fcbe..0000000
--- a/xfa/fxfa/cxfa_rendercontext.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/cxfa_rendercontext.h"
-
-#include "xfa/fxfa/cxfa_ffpageview.h"
-#include "xfa/fxfa/cxfa_ffwidget.h"
-
-CXFA_RenderContext::CXFA_RenderContext(CXFA_FFPageView* pPageView,
-                                       const CFX_RectF& clipRect,
-                                       const CFX_Matrix& matrix)
-    : m_pWidgetIterator(pPageView->CreateFormWidgetIterator(
-          XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable)),
-      m_pWidget(m_pWidgetIterator->MoveToNext()),
-      m_matrix(matrix),
-      m_rtClipRect(clipRect) {}
-
-CXFA_RenderContext::~CXFA_RenderContext() = default;
-
-void CXFA_RenderContext::DoRender(CXFA_Graphics* gs) {
-  while (m_pWidget) {
-    CFX_RectF rtWidgetBox = m_pWidget->GetBBox(CXFA_FFWidget::kDoNotDrawFocus);
-    ++rtWidgetBox.width;
-    ++rtWidgetBox.height;
-    if (rtWidgetBox.IntersectWith(m_rtClipRect))
-      m_pWidget->RenderWidget(gs, m_matrix, CXFA_FFWidget::kHighlight);
-
-    m_pWidget = m_pWidgetIterator->MoveToNext();
-  }
-}
diff --git a/xfa/fxfa/cxfa_rendercontext.h b/xfa/fxfa/cxfa_rendercontext.h
deleted file mode 100644
index 457c62f..0000000
--- a/xfa/fxfa/cxfa_rendercontext.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_CXFA_RENDERCONTEXT_H_
-#define XFA_FXFA_CXFA_RENDERCONTEXT_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class CXFA_Graphics;
-class CXFA_FFPageView;
-class CXFA_FFWidget;
-class IXFA_WidgetIterator;
-
-class CXFA_RenderContext {
- public:
-  CXFA_RenderContext(CXFA_FFPageView* pPageView,
-                     const CFX_RectF& clipRect,
-                     const CFX_Matrix& matrix);
-  ~CXFA_RenderContext();
-
-  void DoRender(CXFA_Graphics* gs);
-
- private:
-  std::unique_ptr<IXFA_WidgetIterator> const m_pWidgetIterator;
-  UnownedPtr<CXFA_FFWidget> m_pWidget;
-  const CFX_Matrix m_matrix;
-  const CFX_RectF m_rtClipRect;
-};
-
-#endif  // XFA_FXFA_CXFA_RENDERCONTEXT_H_
diff --git a/xfa/fxfa/cxfa_textlayout.cpp b/xfa/fxfa/cxfa_textlayout.cpp
index 45f682c..8228a5c 100644
--- a/xfa/fxfa/cxfa_textlayout.cpp
+++ b/xfa/fxfa/cxfa_textlayout.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,30 +6,32 @@
 
 #include "xfa/fxfa/cxfa_textlayout.h"
 
+#include <math.h>
+
 #include <algorithm>
 #include <utility>
 
 #include "core/fxcrt/css/cfx_csscomputedstyle.h"
 #include "core/fxcrt/css/cfx_cssstyleselector.h"
+#include "core/fxcrt/stl_util.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
 #include "core/fxcrt/xml/cfx_xmltext.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/text_char_pos.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/notreached.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
-#include "xfa/fgas/layout/cfx_linkuserdata.h"
-#include "xfa/fgas/layout/cfx_rtfbreak.h"
-#include "xfa/fgas/layout/cfx_textuserdata.h"
-#include "xfa/fxfa/cxfa_loadercontext.h"
-#include "xfa/fxfa/cxfa_pieceline.h"
-#include "xfa/fxfa/cxfa_textparsecontext.h"
-#include "xfa/fxfa/cxfa_textpiece.h"
+#include "xfa/fgas/layout/cfgas_linkuserdata.h"
+#include "xfa/fgas/layout/cfgas_rtfbreak.h"
+#include "xfa/fgas/layout/cfgas_textuserdata.h"
+#include "xfa/fxfa/cxfa_ffdoc.h"
+#include "xfa/fxfa/cxfa_textparser.h"
 #include "xfa/fxfa/cxfa_textprovider.h"
 #include "xfa/fxfa/cxfa_texttabstopscontext.h"
 #include "xfa/fxfa/parser/cxfa_font.h"
@@ -41,16 +43,16 @@
 constexpr float kHeightTolerance = 0.001f;
 
 void ProcessText(WideString* pText) {
-  int32_t iLen = pText->GetLength();
+  size_t iLen = pText->GetLength();
   if (iLen == 0)
     return;
 
-  int32_t iTrimLeft = 0;
+  size_t iTrimLeft = 0;
   {
     // Span's lifetime must end before ReleaseBuffer() below.
     pdfium::span<wchar_t> psz = pText->GetBuffer(iLen);
     wchar_t wPrev = 0;
-    for (int32_t i = 0; i < iLen; i++) {
+    for (size_t i = 0; i < iLen; i++) {
       wchar_t wch = psz[i];
       if (wch < 0x20)
         wch = 0x20;
@@ -66,15 +68,39 @@
 
 }  // namespace
 
-CXFA_TextLayout::CXFA_TextLayout(CXFA_FFDoc* doc,
-                                 CXFA_TextProvider* pTextProvider)
-    : m_pDoc(doc), m_pTextProvider(pTextProvider) {
-  ASSERT(m_pTextProvider);
+CXFA_TextLayout::TextPiece::TextPiece() = default;
+
+CXFA_TextLayout::TextPiece::~TextPiece() = default;
+
+CXFA_TextLayout::PieceLine::PieceLine() = default;
+
+CXFA_TextLayout::PieceLine::~PieceLine() = default;
+
+CXFA_TextLayout::LoaderContext::LoaderContext() = default;
+
+CXFA_TextLayout::LoaderContext::~LoaderContext() = default;
+
+void CXFA_TextLayout::LoaderContext::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(pNode);
 }
 
-CXFA_TextLayout::~CXFA_TextLayout() {
-  m_textParser.Reset();
-  Unload();
+CXFA_TextLayout::CXFA_TextLayout(CXFA_FFDoc* doc,
+                                 CXFA_TextProvider* pTextProvider)
+    : m_pDoc(doc),
+      m_pTextProvider(pTextProvider),
+      m_pTextParser(cppgc::MakeGarbageCollected<CXFA_TextParser>(
+          doc->GetHeap()->GetAllocationHandle())) {
+  DCHECK(m_pTextProvider);
+}
+
+CXFA_TextLayout::~CXFA_TextLayout() = default;
+
+void CXFA_TextLayout::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pDoc);
+  visitor->Trace(m_pTextProvider);
+  visitor->Trace(m_pTextDataNode);
+  visitor->Trace(m_pTextParser);
+  visitor->Trace(m_pLoader);
 }
 
 void CXFA_TextLayout::Unload() {
@@ -82,10 +108,20 @@
   m_pBreak.reset();
 }
 
+WideString CXFA_TextLayout::GetLinkURLAtPoint(const CFX_PointF& point) {
+  for (const auto& pPieceLine : m_pieceLines) {
+    for (const auto& pPiece : pPieceLine->m_textPieces) {
+      if (pPiece->pLinkData && pPiece->rtPiece.Contains(point))
+        return pPiece->pLinkData->GetLinkURL();
+    }
+  }
+  return WideString();
+}
+
 void CXFA_TextLayout::GetTextDataNode() {
   CXFA_Node* pNode = m_pTextProvider->GetTextNode(&m_bRichText);
   if (pNode && m_bRichText)
-    m_textParser.Reset();
+    m_pTextParser->Reset();
 
   m_pTextDataNode = pNode;
 }
@@ -110,15 +146,16 @@
   return nullptr;
 }
 
-std::unique_ptr<CFX_RTFBreak> CXFA_TextLayout::CreateBreak(bool bDefault) {
-  uint32_t dwStyle = FX_LAYOUTSTYLE_ExpandTab;
+std::unique_ptr<CFGAS_RTFBreak> CXFA_TextLayout::CreateBreak(bool bDefault) {
+  Mask<CFGAS_Break::LayoutStyle> dwStyle = CFGAS_Break::LayoutStyle::kExpandTab;
   if (!bDefault)
-    dwStyle |= FX_LAYOUTSTYLE_Pagination;
+    dwStyle |= CFGAS_Break::LayoutStyle::kPagination;
 
-  auto pBreak = pdfium::MakeUnique<CFX_RTFBreak>(dwStyle);
+  auto pBreak = std::make_unique<CFGAS_RTFBreak>(dwStyle);
   pBreak->SetLineBreakTolerance(1);
-  pBreak->SetFont(m_textParser.GetFont(m_pDoc.Get(), m_pTextProvider, nullptr));
-  pBreak->SetFontSize(m_textParser.GetFontSize(m_pTextProvider, nullptr));
+  pBreak->SetFont(
+      m_pTextParser->GetFont(m_pDoc.Get(), m_pTextProvider, nullptr));
+  pBreak->SetFontSize(m_pTextParser->GetFontSize(m_pTextProvider, nullptr));
   return pBreak;
 }
 
@@ -127,32 +164,31 @@
   float fStart = 0;
   float fStartPos = 0;
   if (para) {
-    CFX_RTFLineAlignment iAlign = CFX_RTFLineAlignment::Left;
+    CFGAS_RTFBreak::LineAlignment iAlign = CFGAS_RTFBreak::LineAlignment::Left;
     switch (para->GetHorizontalAlign()) {
       case XFA_AttributeValue::Center:
-        iAlign = CFX_RTFLineAlignment::Center;
+        iAlign = CFGAS_RTFBreak::LineAlignment::Center;
         break;
       case XFA_AttributeValue::Right:
-        iAlign = CFX_RTFLineAlignment::Right;
+        iAlign = CFGAS_RTFBreak::LineAlignment::Right;
         break;
       case XFA_AttributeValue::Justify:
-        iAlign = CFX_RTFLineAlignment::Justified;
+        iAlign = CFGAS_RTFBreak::LineAlignment::Justified;
         break;
       case XFA_AttributeValue::JustifyAll:
-        iAlign = CFX_RTFLineAlignment::Distributed;
+        iAlign = CFGAS_RTFBreak::LineAlignment::Distributed;
         break;
       case XFA_AttributeValue::Left:
       case XFA_AttributeValue::Radix:
         break;
       default:
-        NOTREACHED();
-        break;
+        NOTREACHED_NORETURN();
     }
     m_pBreak->SetAlignment(iAlign);
 
     fStart = para->GetMarginLeft();
     if (m_pTextProvider->IsCheckButtonAndAutoWidth()) {
-      if (iAlign != CFX_RTFLineAlignment::Left)
+      if (iAlign != CFGAS_RTFBreak::LineAlignment::Left)
         fLineWidth -= para->GetMarginRight();
     } else {
       fLineWidth -= para->GetMarginRight();
@@ -177,10 +213,10 @@
     m_pBreak->SetCharSpace(font->GetLetterSpacing());
   }
 
-  float fFontSize = m_textParser.GetFontSize(m_pTextProvider, nullptr);
+  float fFontSize = m_pTextParser->GetFontSize(m_pTextProvider, nullptr);
   m_pBreak->SetFontSize(fFontSize);
   m_pBreak->SetFont(
-      m_textParser.GetFont(m_pDoc.Get(), m_pTextProvider, nullptr));
+      m_pTextParser->GetFont(m_pDoc.Get(), m_pTextProvider, nullptr));
   m_pBreak->SetLineBreakTolerance(fFontSize * 0.2f);
 }
 
@@ -196,19 +232,19 @@
 
   if (eDisplay == CFX_CSSDisplay::Block ||
       eDisplay == CFX_CSSDisplay::ListItem) {
-    CFX_RTFLineAlignment iAlign = CFX_RTFLineAlignment::Left;
+    CFGAS_RTFBreak::LineAlignment iAlign = CFGAS_RTFBreak::LineAlignment::Left;
     switch (pStyle->GetTextAlign()) {
       case CFX_CSSTextAlign::Right:
-        iAlign = CFX_RTFLineAlignment::Right;
+        iAlign = CFGAS_RTFBreak::LineAlignment::Right;
         break;
       case CFX_CSSTextAlign::Center:
-        iAlign = CFX_RTFLineAlignment::Center;
+        iAlign = CFGAS_RTFBreak::LineAlignment::Center;
         break;
       case CFX_CSSTextAlign::Justify:
-        iAlign = CFX_RTFLineAlignment::Justified;
+        iAlign = CFGAS_RTFBreak::LineAlignment::Justified;
         break;
       case CFX_CSSTextAlign::JustifyAll:
-        iAlign = CFX_RTFLineAlignment::Distributed;
+        iAlign = CFGAS_RTFBreak::LineAlignment::Distributed;
         break;
       default:
         break;
@@ -250,21 +286,22 @@
       fStart += fIndent;
 
     m_pBreak->SetLineStartPos(fStart);
-    m_pBreak->SetTabWidth(m_textParser.GetTabInterval(pStyle));
+    m_pBreak->SetTabWidth(m_pTextParser->GetTabInterval(pStyle));
     if (!m_pTabstopContext)
-      m_pTabstopContext = pdfium::MakeUnique<CXFA_TextTabstopsContext>();
-    m_textParser.GetTabstops(pStyle, m_pTabstopContext.get());
+      m_pTabstopContext = std::make_unique<CXFA_TextTabstopsContext>();
+    m_pTextParser->GetTabstops(pStyle, m_pTabstopContext.get());
     for (const auto& stop : m_pTabstopContext->m_tabstops)
       m_pBreak->AddPositionedTab(stop.fTabstops);
   }
-  float fFontSize = m_textParser.GetFontSize(m_pTextProvider, pStyle);
+  float fFontSize = m_pTextParser->GetFontSize(m_pTextProvider, pStyle);
   m_pBreak->SetFontSize(fFontSize);
   m_pBreak->SetLineBreakTolerance(fFontSize * 0.2f);
   m_pBreak->SetFont(
-      m_textParser.GetFont(m_pDoc.Get(), m_pTextProvider, pStyle));
+      m_pTextParser->GetFont(m_pDoc.Get(), m_pTextProvider, pStyle));
   m_pBreak->SetHorizontalScale(
-      m_textParser.GetHorScale(m_pTextProvider, pStyle, pXMLNode));
-  m_pBreak->SetVerticalScale(m_textParser.GetVerScale(m_pTextProvider, pStyle));
+      m_pTextParser->GetHorScale(m_pTextProvider, pStyle, pXMLNode));
+  m_pBreak->SetVerticalScale(
+      m_pTextParser->GetVerScale(m_pTextProvider, pStyle));
   m_pBreak->SetCharSpace(pStyle->GetLetterSpacing().GetValue());
 }
 
@@ -292,7 +329,8 @@
 
 float CXFA_TextLayout::StartLayout(float fWidth) {
   if (!m_pLoader)
-    m_pLoader = pdfium::MakeUnique<CXFA_LoaderContext>();
+    m_pLoader = cppgc::MakeGarbageCollected<LoaderContext>(
+        m_pDoc->GetHeap()->GetAllocationHandle());
 
   if (fWidth < 0 ||
       (m_pLoader->fWidth > -1 && fabs(fWidth - m_pLoader->fWidth) > 0)) {
@@ -338,7 +376,7 @@
   if (m_Blocks.empty() && m_pLoader->fHeight > 0) {
     float fHeight = fTextHeight - GetLayoutHeight();
     if (fHeight > 0) {
-      XFA_AttributeValue iAlign = m_textParser.GetVAlign(m_pTextProvider);
+      XFA_AttributeValue iAlign = m_pTextParser->GetVAlign(m_pTextProvider);
       if (iAlign == XFA_AttributeValue::Middle)
         fHeight /= 2.0f;
       else if (iAlign != XFA_AttributeValue::Bottom)
@@ -422,7 +460,7 @@
   m_fMaxWidth = 0;
   Loader(width, &fLinePos, false);
   if (fLinePos < 0.1f)
-    fLinePos = m_textParser.GetFontSize(m_pTextProvider, nullptr);
+    fLinePos = m_pTextParser->GetFontSize(m_pTextProvider, nullptr);
 
   m_pTabstopContext.reset();
   return CFX_SizeF(m_fMaxWidth, fLinePos);
@@ -436,7 +474,7 @@
   m_pBreak = CreateBreak(true);
   if (m_pLoader) {
     m_pLoader->iTotalLines = -1;
-    m_pLoader->iChar = 0;
+    m_pLoader->nCharIdx = 0;
   }
 
   m_iLines = 0;
@@ -448,7 +486,7 @@
 }
 
 bool CXFA_TextLayout::LayoutInternal(size_t szBlockIndex) {
-  ASSERT(szBlockIndex < CountBlocks());
+  DCHECK(szBlockIndex < CountBlocks());
 
   if (!m_pLoader || m_pLoader->fWidth < 1)
     return false;
@@ -467,24 +505,26 @@
     for (size_t i = 0; i < m_pLoader->blockHeights.size(); ++i)
       fLinePos -= m_pLoader->blockHeights[i].fHeight;
 
-    m_pLoader->iChar = 0;
-    if (!m_Blocks.empty())
-      m_pLoader->iTotalLines = m_Blocks[szBlockIndex].szLength;
-
+    m_pLoader->nCharIdx = 0;
+    if (!m_Blocks.empty()) {
+      m_pLoader->iTotalLines =
+          pdfium::base::checked_cast<int32_t>(m_Blocks[szBlockIndex].szLength);
+    }
     Loader(szText.width, &fLinePos, true);
     if (m_Blocks.empty() && m_pLoader->fStartLineOffset < 0.1f)
       UpdateAlign(szText.height, fLinePos);
   } else if (m_pTextDataNode) {
-    if (!m_Blocks.empty() && szBlockIndex < m_Blocks.size() - 1)
-      m_pLoader->iTotalLines = m_Blocks[szBlockIndex].szLength;
-
+    if (!m_Blocks.empty() && szBlockIndex < m_Blocks.size() - 1) {
+      m_pLoader->iTotalLines =
+          pdfium::base::checked_cast<int32_t>(m_Blocks[szBlockIndex].szLength);
+    }
     m_pBreak->Reset();
     if (m_bRichText) {
       CFX_XMLNode* pContainerNode = GetXMLContainerNode();
       if (!pContainerNode)
         return true;
 
-      const CFX_XMLNode* pXMLNode = m_pLoader->pXMLNode.Get();
+      const CFX_XMLNode* pXMLNode = m_pLoader->pXMLNode;
       if (!pXMLNode)
         return true;
 
@@ -524,10 +564,6 @@
       LoadText(pNode, szText.width, &fLinePos, true);
     }
   }
-  if (szBlockIndex == m_Blocks.size()) {
-    m_pTabstopContext.reset();
-    m_pLoader.reset();
-  }
   return true;
 }
 
@@ -577,6 +613,8 @@
     size_t szBlockCount = CountBlocks();
     for (size_t i = 0; i < szBlockCount; ++i)
       LayoutInternal(i);
+    m_pTabstopContext.reset();
+    m_pLoader.Clear();
   }
 
   std::vector<TextCharPos> char_pos(1);
@@ -595,11 +633,11 @@
     if (i + szLineStart >= m_pieceLines.size())
       break;
 
-    CXFA_PieceLine* pPieceLine = m_pieceLines[i + szLineStart].get();
+    PieceLine* pPieceLine = m_pieceLines[i + szLineStart].get();
     for (size_t j = 0; j < pPieceLine->m_textPieces.size(); ++j) {
-      const CXFA_TextPiece* pPiece = pPieceLine->m_textPieces[j].get();
+      const TextPiece* pPiece = pPieceLine->m_textPieces[j].get();
       int32_t iChars = pPiece->iChars;
-      if (pdfium::CollectionSize<int32_t>(char_pos) < iChars)
+      if (fxcrt::CollectionSize<int32_t>(char_pos) < iChars)
         char_pos.resize(iChars);
       RenderString(pFxDevice, pPieceLine, j, &char_pos, mtDoc2Device);
     }
@@ -615,7 +653,7 @@
   if (fHeight < 0.1f)
     return;
 
-  switch (m_textParser.GetVAlign(m_pTextProvider)) {
+  switch (m_pTextParser->GetVAlign(m_pTextProvider)) {
     case XFA_AttributeValue::Middle:
       fHeight /= 2.0f;
       break;
@@ -647,12 +685,12 @@
   if (!pXMLContainer)
     return;
 
-  if (!m_textParser.IsParsed())
-    m_textParser.DoParse(pXMLContainer, m_pTextProvider);
+  if (!m_pTextParser->IsParsed())
+    m_pTextParser->DoParse(pXMLContainer, m_pTextProvider);
 
-  auto pRootStyle = m_textParser.CreateRootStyle(m_pTextProvider);
-  LoadRichText(pXMLContainer, textWidth, pLinePos, pRootStyle, bSavePieces,
-               nullptr, true, false, 0);
+  auto pRootStyle = m_pTextParser->CreateRootStyle(m_pTextProvider);
+  LoadRichText(pXMLContainer, textWidth, pLinePos, std::move(pRootStyle),
+               bSavePieces, nullptr, true, false, 0);
 }
 
 void CXFA_TextLayout::LoadText(CXFA_Node* pNode,
@@ -676,8 +714,7 @@
         break;
       }
       default:
-        NOTREACHED();
-        break;
+        NOTREACHED_NORETURN();
     }
   }
 
@@ -687,24 +724,23 @@
   if (bRet && m_pLoader)
     m_pLoader->pNode = pNode;
   else
-    EndBreak(CFX_BreakType::Paragraph, pLinePos, bSavePieces);
+    EndBreak(CFGAS_Char::BreakType::kParagraph, pLinePos, bSavePieces);
 }
 
-bool CXFA_TextLayout::LoadRichText(
-    const CFX_XMLNode* pXMLNode,
-    float textWidth,
-    float* pLinePos,
-    const RetainPtr<CFX_CSSComputedStyle>& pParentStyle,
-    bool bSavePieces,
-    RetainPtr<CFX_LinkUserData> pLinkData,
-    bool bEndBreak,
-    bool bIsOl,
-    int32_t iLiCount) {
+bool CXFA_TextLayout::LoadRichText(const CFX_XMLNode* pXMLNode,
+                                   float textWidth,
+                                   float* pLinePos,
+                                   RetainPtr<CFX_CSSComputedStyle> pParentStyle,
+                                   bool bSavePieces,
+                                   RetainPtr<CFGAS_LinkUserData> pLinkData,
+                                   bool bEndBreak,
+                                   bool bIsOl,
+                                   int32_t iLiCount) {
   if (!pXMLNode)
     return false;
 
-  CXFA_TextParseContext* pContext =
-      m_textParser.GetParseContextFromMap(pXMLNode);
+  CXFA_TextParser::Context* pContext =
+      m_pTextParser->GetParseContextFromMap(pXMLNode);
   CFX_CSSDisplay eDisplay = CFX_CSSDisplay::None;
   bool bContentNode = false;
   float fSpaceBelow = 0;
@@ -715,9 +751,6 @@
     bool bCurLi = false;
     const CFX_XMLElement* pElement = nullptr;
     if (pContext) {
-      if (m_bBlockContinue || (m_pLoader && pXMLNode == m_pLoader->pXMLNode)) {
-        m_bBlockContinue = true;
-      }
       if (pXMLNode->GetType() == CFX_XMLNode::Type::kText) {
         bContentNode = true;
       } else if (pXMLNode->GetType() == CFX_XMLNode::Type::kElement) {
@@ -728,101 +761,99 @@
         bIsOl = true;
         bCurOl = true;
       }
-      if (m_bBlockContinue || !bContentNode) {
-        eDisplay = pContext->GetDisplay();
-        if (eDisplay != CFX_CSSDisplay::Block &&
-            eDisplay != CFX_CSSDisplay::Inline &&
-            eDisplay != CFX_CSSDisplay::ListItem) {
-          return true;
+
+      eDisplay = pContext->GetDisplay();
+      if (eDisplay != CFX_CSSDisplay::Block &&
+          eDisplay != CFX_CSSDisplay::Inline &&
+          eDisplay != CFX_CSSDisplay::ListItem) {
+        return true;
+      }
+
+      pStyle = m_pTextParser->ComputeStyle(pXMLNode, pParentStyle);
+      InitBreak(bContentNode ? pParentStyle.Get() : pStyle.Get(), eDisplay,
+                textWidth, pXMLNode, pParentStyle.Get());
+      if ((eDisplay == CFX_CSSDisplay::Block ||
+           eDisplay == CFX_CSSDisplay::ListItem) &&
+          pStyle &&
+          (wsName.IsEmpty() ||
+           !(wsName.EqualsASCII("body") || wsName.EqualsASCII("html") ||
+             wsName.EqualsASCII("ol") || wsName.EqualsASCII("ul")))) {
+        const CFX_CSSRect* pRect = pStyle->GetMarginWidth();
+        if (pRect) {
+          *pLinePos += pRect->top.GetValue();
+          fSpaceBelow = pRect->bottom.GetValue();
+        }
+      }
+
+      if (wsName.EqualsASCII("a")) {
+        WideString wsLinkContent = pElement->GetAttribute(L"href");
+        if (!wsLinkContent.IsEmpty())
+          pLinkData = pdfium::MakeRetain<CFGAS_LinkUserData>(wsLinkContent);
+      }
+
+      int32_t iTabCount = m_pTextParser->CountTabs(
+          bContentNode ? pParentStyle.Get() : pStyle.Get());
+      bool bSpaceRun = m_pTextParser->IsSpaceRun(
+          bContentNode ? pParentStyle.Get() : pStyle.Get());
+      WideString wsText;
+      if (bContentNode && iTabCount == 0) {
+        wsText = ToXMLText(pXMLNode)->GetText();
+      } else if (wsName.EqualsASCII("br")) {
+        wsText = WideString(L'\n');
+      } else if (wsName.EqualsASCII("li")) {
+        bCurLi = true;
+        if (bIsOl)
+          wsText = WideString::Format(L"%d.  ", iLiCount);
+        else
+          wsText = 0x00B7 + WideStringView(L"  ", 1);
+      } else if (!bContentNode) {
+        if (iTabCount > 0) {
+          while (iTabCount-- > 0)
+            wsText += L'\t';
+        } else {
+          absl::optional<WideString> obj =
+              m_pTextParser->GetEmbeddedObj(m_pTextProvider, pXMLNode);
+          if (obj.has_value())
+            wsText = obj.value();
+        }
+      }
+
+      if (!wsText.IsEmpty() && bContentNode && !bSpaceRun)
+        ProcessText(&wsText);
+
+      if (m_pLoader) {
+        if (wsText.GetLength() > 0 && m_pLoader->bFilterSpace) {
+          wsText.TrimLeft(L" ");
+        }
+        if (CFX_CSSDisplay::Block == eDisplay) {
+          m_pLoader->bFilterSpace = true;
+        } else if (CFX_CSSDisplay::Inline == eDisplay &&
+                   m_pLoader->bFilterSpace) {
+          m_pLoader->bFilterSpace = false;
+        } else if (wsText.GetLength() > 0 && wsText.Back() == 0x20) {
+          m_pLoader->bFilterSpace = true;
+        } else if (wsText.GetLength() != 0) {
+          m_pLoader->bFilterSpace = false;
+        }
+      }
+
+      if (wsText.GetLength() > 0) {
+        if (!m_pLoader || m_pLoader->nCharIdx == 0) {
+          auto pUserData = pdfium::MakeRetain<CFGAS_TextUserData>(
+              bContentNode ? pParentStyle : pStyle, pLinkData);
+          m_pBreak->SetUserData(pUserData);
         }
 
-        pStyle = m_textParser.ComputeStyle(pXMLNode, pParentStyle.Get());
-        InitBreak(bContentNode ? pParentStyle.Get() : pStyle.Get(), eDisplay,
-                  textWidth, pXMLNode, pParentStyle.Get());
-        if ((eDisplay == CFX_CSSDisplay::Block ||
-             eDisplay == CFX_CSSDisplay::ListItem) &&
-            pStyle &&
-            (wsName.IsEmpty() ||
-             !(wsName.EqualsASCII("body") || wsName.EqualsASCII("html") ||
-               wsName.EqualsASCII("ol") || wsName.EqualsASCII("ul")))) {
-          const CFX_CSSRect* pRect = pStyle->GetMarginWidth();
-          if (pRect) {
-            *pLinePos += pRect->top.GetValue();
-            fSpaceBelow = pRect->bottom.GetValue();
-          }
-        }
-
-        if (wsName.EqualsASCII("a")) {
-          WideString wsLinkContent = pElement->GetAttribute(L"href");
-          if (!wsLinkContent.IsEmpty())
-            pLinkData = pdfium::MakeRetain<CFX_LinkUserData>(wsLinkContent);
-        }
-
-        int32_t iTabCount = m_textParser.CountTabs(
-            bContentNode ? pParentStyle.Get() : pStyle.Get());
-        bool bSpaceRun = m_textParser.IsSpaceRun(
-            bContentNode ? pParentStyle.Get() : pStyle.Get());
-        WideString wsText;
-        if (bContentNode && iTabCount == 0) {
-          wsText = ToXMLText(pXMLNode)->GetText();
-        } else if (wsName.EqualsASCII("br")) {
-          wsText = L'\n';
-        } else if (wsName.EqualsASCII("li")) {
-          bCurLi = true;
-          if (bIsOl)
-            wsText = WideString::Format(L"%d.  ", iLiCount);
-          else
-            wsText = 0x00B7 + WideStringView(L"  ", 1);
-        } else if (!bContentNode) {
-          if (iTabCount > 0) {
-            while (iTabCount-- > 0)
-              wsText += L'\t';
-          } else {
-            Optional<WideString> obj =
-                m_textParser.GetEmbeddedObj(m_pTextProvider, pXMLNode);
-            if (obj)
-              wsText = *obj;
-          }
-        }
-
-        int32_t iLength = wsText.GetLength();
-        if (iLength > 0 && bContentNode && !bSpaceRun)
-          ProcessText(&wsText);
-
-        if (m_pLoader) {
-          if (wsText.GetLength() > 0 && m_pLoader->bFilterSpace) {
-            wsText.TrimLeft(L" ");
-          }
-          if (CFX_CSSDisplay::Block == eDisplay) {
-            m_pLoader->bFilterSpace = true;
-          } else if (CFX_CSSDisplay::Inline == eDisplay &&
-                     m_pLoader->bFilterSpace) {
+        if (AppendChar(wsText, pLinePos, 0, bSavePieces)) {
+          if (m_pLoader)
             m_pLoader->bFilterSpace = false;
-          } else if (wsText.GetLength() > 0 && wsText.Back() == 0x20) {
-            m_pLoader->bFilterSpace = true;
-          } else if (wsText.GetLength() != 0) {
-            m_pLoader->bFilterSpace = false;
+          if (!IsEnd(bSavePieces))
+            return true;
+          if (m_pLoader && m_pLoader->iTotalLines > -1) {
+            m_pLoader->pXMLNode = pXMLNode;
+            m_pLoader->pParentStyle = pParentStyle;
           }
-        }
-
-        if (wsText.GetLength() > 0) {
-          if (!m_pLoader || m_pLoader->iChar == 0) {
-            auto pUserData = pdfium::MakeRetain<CFX_TextUserData>(
-                bContentNode ? pParentStyle : pStyle, pLinkData);
-            m_pBreak->SetUserData(pUserData);
-          }
-
-          if (AppendChar(wsText, pLinePos, 0, bSavePieces)) {
-            if (m_pLoader)
-              m_pLoader->bFilterSpace = false;
-            if (!IsEnd(bSavePieces))
-              return true;
-            if (m_pLoader && m_pLoader->iTotalLines > -1) {
-              m_pLoader->pXMLNode = pXMLNode;
-              m_pLoader->pParentStyle = pParentStyle;
-            }
-            return false;
-          }
+          return false;
         }
       }
     }
@@ -843,64 +874,61 @@
         m_pLoader->bFilterSpace = true;
     }
     if (bCurLi)
-      EndBreak(CFX_BreakType::Line, pLinePos, bSavePieces);
+      EndBreak(CFGAS_Char::BreakType::kLine, pLinePos, bSavePieces);
   } else {
     if (pContext)
       eDisplay = pContext->GetDisplay();
   }
 
-  if (m_bBlockContinue) {
-    if (pContext && !bContentNode) {
-      CFX_BreakType dwStatus = (eDisplay == CFX_CSSDisplay::Block)
-                                   ? CFX_BreakType::Paragraph
-                                   : CFX_BreakType::Piece;
-      EndBreak(dwStatus, pLinePos, bSavePieces);
-      if (eDisplay == CFX_CSSDisplay::Block) {
-        *pLinePos += fSpaceBelow;
-        if (m_pTabstopContext)
-          m_pTabstopContext->RemoveAll();
-      }
-      if (IsEnd(bSavePieces)) {
-        if (m_pLoader && m_pLoader->iTotalLines > -1) {
-          m_pLoader->pXMLNode = pXMLNode->GetNextSibling();
-          m_pLoader->pParentStyle = pParentStyle;
-        }
-        return false;
-      }
-    }
+  if (!pContext || bContentNode)
+    return true;
+
+  CFGAS_Char::BreakType dwStatus = (eDisplay == CFX_CSSDisplay::Block)
+                                       ? CFGAS_Char::BreakType::kParagraph
+                                       : CFGAS_Char::BreakType::kPiece;
+  EndBreak(dwStatus, pLinePos, bSavePieces);
+  if (eDisplay == CFX_CSSDisplay::Block) {
+    *pLinePos += fSpaceBelow;
+    if (m_pTabstopContext)
+      m_pTabstopContext->RemoveAll();
   }
-  return true;
+  if (!IsEnd(bSavePieces))
+    return true;
+
+  if (m_pLoader && m_pLoader->iTotalLines > -1) {
+    m_pLoader->pXMLNode = pXMLNode->GetNextSibling();
+    m_pLoader->pParentStyle = pParentStyle;
+  }
+  return false;
 }
 
 bool CXFA_TextLayout::AppendChar(const WideString& wsText,
                                  float* pLinePos,
                                  float fSpaceAbove,
                                  bool bSavePieces) {
-  CFX_BreakType dwStatus = CFX_BreakType::None;
-  int32_t iChar = 0;
-  if (m_pLoader)
-    iChar = m_pLoader->iChar;
-
-  int32_t iLength = wsText.GetLength();
-  for (int32_t i = iChar; i < iLength; i++) {
+  CFGAS_Char::BreakType dwStatus = CFGAS_Char::BreakType::kNone;
+  size_t iChar = m_pLoader ? m_pLoader->nCharIdx : 0;
+  size_t iLength = wsText.GetLength();
+  for (size_t i = iChar; i < iLength; i++) {
     wchar_t wch = wsText[i];
     if (wch == 0xA0)
       wch = 0x20;
 
     dwStatus = m_pBreak->AppendChar(wch);
-    if (dwStatus != CFX_BreakType::None && dwStatus != CFX_BreakType::Piece) {
+    if (dwStatus != CFGAS_Char::BreakType::kNone &&
+        dwStatus != CFGAS_Char::BreakType::kPiece) {
       AppendTextLine(dwStatus, pLinePos, bSavePieces, false);
       if (IsEnd(bSavePieces)) {
         if (m_pLoader)
-          m_pLoader->iChar = i;
+          m_pLoader->nCharIdx = i;
         return true;
       }
-      if (dwStatus == CFX_BreakType::Paragraph && m_bRichText)
+      if (dwStatus == CFGAS_Char::BreakType::kParagraph && m_bRichText)
         *pLinePos += fSpaceAbove;
     }
   }
   if (m_pLoader)
-    m_pLoader->iChar = 0;
+    m_pLoader->nCharIdx = 0;
 
   return false;
 }
@@ -913,52 +941,53 @@
   return false;
 }
 
-void CXFA_TextLayout::EndBreak(CFX_BreakType dwStatus,
+void CXFA_TextLayout::EndBreak(CFGAS_Char::BreakType dwStatus,
                                float* pLinePos,
                                bool bSavePieces) {
   dwStatus = m_pBreak->EndBreak(dwStatus);
-  if (dwStatus != CFX_BreakType::None && dwStatus != CFX_BreakType::Piece)
+  if (dwStatus != CFGAS_Char::BreakType::kNone &&
+      dwStatus != CFGAS_Char::BreakType::kPiece)
     AppendTextLine(dwStatus, pLinePos, bSavePieces, true);
 }
 
 void CXFA_TextLayout::DoTabstops(CFX_CSSComputedStyle* pStyle,
-                                 CXFA_PieceLine* pPieceLine) {
+                                 PieceLine* pPieceLine) {
   if (!pStyle || !pPieceLine)
     return;
 
   if (!m_pTabstopContext || m_pTabstopContext->m_tabstops.empty())
     return;
 
-  int32_t iPieces = pdfium::CollectionSize<int32_t>(pPieceLine->m_textPieces);
+  int32_t iPieces = fxcrt::CollectionSize<int32_t>(pPieceLine->m_textPieces);
   if (iPieces == 0)
     return;
 
-  CXFA_TextPiece* pPiece = pPieceLine->m_textPieces[iPieces - 1].get();
+  TextPiece* pPiece = pPieceLine->m_textPieces[iPieces - 1].get();
   int32_t& iTabstopsIndex = m_pTabstopContext->m_iTabIndex;
-  int32_t iCount = m_textParser.CountTabs(pStyle);
-  if (!pdfium::IndexInBounds(m_pTabstopContext->m_tabstops, iTabstopsIndex))
+  int32_t iCount = m_pTextParser->CountTabs(pStyle);
+  if (!fxcrt::IndexInBounds(m_pTabstopContext->m_tabstops, iTabstopsIndex))
     return;
 
   if (iCount > 0) {
     iTabstopsIndex++;
-    m_pTabstopContext->m_bTabstops = true;
+    m_pTabstopContext->m_bHasTabstops = true;
     float fRight = 0;
     if (iPieces > 1) {
-      CXFA_TextPiece* p = pPieceLine->m_textPieces[iPieces - 2].get();
+      const TextPiece* p = pPieceLine->m_textPieces[iPieces - 2].get();
       fRight = p->rtPiece.right();
     }
     m_pTabstopContext->m_fTabWidth =
         pPiece->rtPiece.width + pPiece->rtPiece.left - fRight;
   } else if (iTabstopsIndex > -1) {
     float fLeft = 0;
-    if (m_pTabstopContext->m_bTabstops) {
+    if (m_pTabstopContext->m_bHasTabstops) {
       uint32_t dwAlign = m_pTabstopContext->m_tabstops[iTabstopsIndex].dwAlign;
-      if (dwAlign == FX_HashCode_GetW(L"center", false)) {
+      if (dwAlign == FX_HashCode_GetW(L"center")) {
         fLeft = pPiece->rtPiece.width / 2.0f;
-      } else if (dwAlign == FX_HashCode_GetW(L"right", false) ||
-                 dwAlign == FX_HashCode_GetW(L"before", false)) {
+      } else if (dwAlign == FX_HashCode_GetW(L"right") ||
+                 dwAlign == FX_HashCode_GetW(L"before")) {
         fLeft = pPiece->rtPiece.width;
-      } else if (dwAlign == FX_HashCode_GetW(L"decimal", false)) {
+      } else if (dwAlign == FX_HashCode_GetW(L"decimal")) {
         int32_t iChars = pPiece->iChars;
         for (int32_t i = 0; i < iChars; i++) {
           if (pPiece->szText[i] == L'.')
@@ -969,14 +998,14 @@
       }
       m_pTabstopContext->m_fLeft =
           std::min(fLeft, m_pTabstopContext->m_fTabWidth);
-      m_pTabstopContext->m_bTabstops = false;
+      m_pTabstopContext->m_bHasTabstops = false;
       m_pTabstopContext->m_fTabWidth = 0;
     }
     pPiece->rtPiece.left -= m_pTabstopContext->m_fLeft;
   }
 }
 
-void CXFA_TextLayout::AppendTextLine(CFX_BreakType dwStatus,
+void CXFA_TextLayout::AppendTextLine(CFGAS_Char::BreakType dwStatus,
                                      float* pLinePos,
                                      bool bSavePieces,
                                      bool bEndBreak) {
@@ -986,45 +1015,49 @@
 
   RetainPtr<CFX_CSSComputedStyle> pStyle;
   if (bSavePieces) {
-    auto pNew = pdfium::MakeUnique<CXFA_PieceLine>();
-    CXFA_PieceLine* pPieceLine = pNew.get();
+    auto pNew = std::make_unique<PieceLine>();
+    PieceLine* pPieceLine = pNew.get();
     m_pieceLines.push_back(std::move(pNew));
     if (m_pTabstopContext)
       m_pTabstopContext->Reset();
 
-    float fLineStep = 0, fBaseLine = 0;
+    float fLineStep = 0;
+    float fBaseLine = 0;
     int32_t i = 0;
     for (i = 0; i < iPieces; i++) {
-      const CFX_BreakPiece* pPiece = m_pBreak->GetBreakPieceUnstable(i);
-      CFX_TextUserData* pUserData = pPiece->m_pUserData.Get();
+      const CFGAS_BreakPiece* pPiece = m_pBreak->GetBreakPieceUnstable(i);
+      const CFGAS_TextUserData* pUserData = pPiece->GetUserData();
       if (pUserData)
         pStyle = pUserData->m_pStyle;
-      float fVerScale = pPiece->m_iVerticalScale / 100.0f;
+      float fVerScale = pPiece->GetVerticalScale() / 100.0f;
 
-      auto pTP = pdfium::MakeUnique<CXFA_TextPiece>();
-      pTP->iChars = pPiece->m_iChars;
+      auto pTP = std::make_unique<TextPiece>();
+      pTP->iChars = pPiece->GetCharCount();
       pTP->szText = pPiece->GetString();
       pTP->Widths = pPiece->GetWidths();
-      pTP->iBidiLevel = pPiece->m_iBidiLevel;
-      pTP->iHorScale = pPiece->m_iHorizontalScale;
-      pTP->iVerScale = pPiece->m_iVerticalScale;
-      m_textParser.GetUnderline(m_pTextProvider, pStyle.Get(), pTP->iUnderline,
-                                pTP->iPeriod);
-      m_textParser.GetLinethrough(m_pTextProvider, pStyle.Get(),
-                                  pTP->iLineThrough);
-      pTP->dwColor = m_textParser.GetColor(m_pTextProvider, pStyle.Get());
+      pTP->iBidiLevel = pPiece->GetBidiLevel();
+      pTP->iHorScale = pPiece->GetHorizontalScale();
+      pTP->iVerScale = pPiece->GetVerticalScale();
+      pTP->iUnderline =
+          m_pTextParser->GetUnderline(m_pTextProvider, pStyle.Get());
+      pTP->iPeriod =
+          m_pTextParser->GetUnderlinePeriod(m_pTextProvider, pStyle.Get());
+      pTP->iLineThrough =
+          m_pTextParser->GetLinethrough(m_pTextProvider, pStyle.Get());
+      pTP->dwColor = m_pTextParser->GetColor(m_pTextProvider, pStyle.Get());
       pTP->pFont =
-          m_textParser.GetFont(m_pDoc.Get(), m_pTextProvider, pStyle.Get());
-      pTP->fFontSize = m_textParser.GetFontSize(m_pTextProvider, pStyle.Get());
-      pTP->rtPiece.left = pPiece->m_iStartPos / 20000.0f;
-      pTP->rtPiece.width = pPiece->m_iWidth / 20000.0f;
+          m_pTextParser->GetFont(m_pDoc.Get(), m_pTextProvider, pStyle.Get());
+      pTP->fFontSize =
+          m_pTextParser->GetFontSize(m_pTextProvider, pStyle.Get());
+      pTP->rtPiece.left = pPiece->GetStartPos() / 20000.0f;
+      pTP->rtPiece.width = pPiece->GetWidth() / 20000.0f;
       pTP->rtPiece.height =
-          static_cast<float>(pPiece->m_iFontSize) * fVerScale / 20.0f;
+          static_cast<float>(pPiece->GetFontSize()) * fVerScale / 20.0f;
       float fBaseLineTemp =
-          m_textParser.GetBaseline(m_pTextProvider, pStyle.Get());
+          m_pTextParser->GetBaseline(m_pTextProvider, pStyle.Get());
       pTP->rtPiece.top = fBaseLineTemp;
 
-      float fLineHeight = m_textParser.GetLineHeight(
+      float fLineHeight = m_pTextParser->GetLineHeight(
           m_pTextProvider, pStyle.Get(), m_iLines == 0, fVerScale);
       if (fBaseLineTemp > 0) {
         float fLineHeightTmp = fBaseLineTemp + pTP->rtPiece.height;
@@ -1047,24 +1080,25 @@
     float fLineStep = 0;
     float fLineWidth = 0;
     for (int32_t i = 0; i < iPieces; i++) {
-      const CFX_BreakPiece* pPiece = m_pBreak->GetBreakPieceUnstable(i);
-      CFX_TextUserData* pUserData = pPiece->m_pUserData.Get();
+      const CFGAS_BreakPiece* pPiece = m_pBreak->GetBreakPieceUnstable(i);
+      const CFGAS_TextUserData* pUserData = pPiece->GetUserData();
       if (pUserData)
         pStyle = pUserData->m_pStyle;
-      float fVerScale = pPiece->m_iVerticalScale / 100.0f;
-      float fBaseLine = m_textParser.GetBaseline(m_pTextProvider, pStyle.Get());
-      float fLineHeight = m_textParser.GetLineHeight(
+      float fVerScale = pPiece->GetVerticalScale() / 100.0f;
+      float fBaseLine =
+          m_pTextParser->GetBaseline(m_pTextProvider, pStyle.Get());
+      float fLineHeight = m_pTextParser->GetLineHeight(
           m_pTextProvider, pStyle.Get(), m_iLines == 0, fVerScale);
       if (fBaseLine > 0) {
         float fLineHeightTmp =
             fBaseLine +
-            static_cast<float>(pPiece->m_iFontSize) * fVerScale / 20.0f;
+            static_cast<float>(pPiece->GetFontSize()) * fVerScale / 20.0f;
         if (fLineHeight < fLineHeightTmp) {
           fLineHeight = fLineHeightTmp;
         }
       }
       fLineStep = std::max(fLineStep, fLineHeight);
-      fLineWidth += pPiece->m_iWidth / 20000.0f;
+      fLineWidth += pPiece->GetWidth() / 20000.0f;
     }
     *pLinePos += fLineStep;
     m_fMaxWidth = std::max(m_fMaxWidth, fLineWidth);
@@ -1076,7 +1110,7 @@
   }
 
   m_pBreak->ClearBreakPieces();
-  if (dwStatus == CFX_BreakType::Paragraph) {
+  if (dwStatus == CFGAS_Char::BreakType::kParagraph) {
     m_pBreak->Reset();
     if (!pStyle && bEndBreak) {
       CXFA_Para* para = m_pTextProvider->GetParaIfExists();
@@ -1112,11 +1146,11 @@
 }
 
 void CXFA_TextLayout::RenderString(CFX_RenderDevice* pDevice,
-                                   CXFA_PieceLine* pPieceLine,
+                                   PieceLine* pPieceLine,
                                    size_t szPiece,
                                    std::vector<TextCharPos>* pCharPos,
                                    const CFX_Matrix& mtDoc2Device) {
-  const CXFA_TextPiece* pPiece = pPieceLine->m_textPieces[szPiece].get();
+  const TextPiece* pPiece = pPieceLine->m_textPieces[szPiece].get();
   size_t szCount = GetDisplayPos(pPiece, pCharPos);
   if (szCount > 0) {
     auto span = pdfium::make_span(pCharPos->data(), szCount);
@@ -1127,17 +1161,17 @@
 }
 
 void CXFA_TextLayout::RenderPath(CFX_RenderDevice* pDevice,
-                                 CXFA_PieceLine* pPieceLine,
+                                 const PieceLine* pPieceLine,
                                  size_t szPiece,
                                  std::vector<TextCharPos>* pCharPos,
                                  const CFX_Matrix& mtDoc2Device) {
-  CXFA_TextPiece* pPiece = pPieceLine->m_textPieces[szPiece].get();
+  const TextPiece* pPiece = pPieceLine->m_textPieces[szPiece].get();
   bool bNoUnderline = pPiece->iUnderline < 1 || pPiece->iUnderline > 2;
   bool bNoLineThrough = pPiece->iLineThrough < 1 || pPiece->iLineThrough > 2;
   if (bNoUnderline && bNoLineThrough)
     return;
 
-  CFX_PathData path;
+  CFX_Path path;
   size_t szChars = GetDisplayPos(pPiece, pCharPos);
   if (szChars > 0) {
     CFX_PointF pt1;
@@ -1241,15 +1275,16 @@
   }
 
   CFX_GraphStateData graphState;
-  graphState.m_LineCap = CFX_GraphStateData::LineCapButt;
-  graphState.m_LineJoin = CFX_GraphStateData::LineJoinMiter;
+  graphState.m_LineCap = CFX_GraphStateData::LineCap::kButt;
+  graphState.m_LineJoin = CFX_GraphStateData::LineJoin::kMiter;
   graphState.m_LineWidth = 1;
   graphState.m_MiterLimit = 10;
   graphState.m_DashPhase = 0;
-  pDevice->DrawPath(&path, &mtDoc2Device, &graphState, 0, pPiece->dwColor, 0);
+  pDevice->DrawPath(path, &mtDoc2Device, &graphState, 0, pPiece->dwColor,
+                    CFX_FillRenderOptions());
 }
 
-size_t CXFA_TextLayout::GetDisplayPos(const CXFA_TextPiece* pPiece,
+size_t CXFA_TextLayout::GetDisplayPos(const TextPiece* pPiece,
                                       std::vector<TextCharPos>* pCharPos) {
   if (!pPiece || pPiece->iChars < 1)
     return 0;
diff --git a/xfa/fxfa/cxfa_textlayout.h b/xfa/fxfa/cxfa_textlayout.h
index e47475a..500a7af 100644
--- a/xfa/fxfa/cxfa_textlayout.h
+++ b/xfa/fxfa/cxfa_textlayout.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,30 +12,37 @@
 
 #include "core/fxcrt/css/cfx_css.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "xfa/fgas/layout/cfx_char.h"
-#include "xfa/fxfa/cxfa_textparser.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
+#include "xfa/fgas/layout/cfgas_char.h"
+#include "xfa/fgas/layout/cfgas_textpiece.h"
+#include "xfa/fxfa/fxfa_basic.h"
 
-class CFDE_RenderDevice;
+class CFGAS_LinkUserData;
+class CFGAS_RTFBreak;
 class CFX_CSSComputedStyle;
-class CFX_RTFBreak;
 class CFX_RenderDevice;
 class CFX_XMLNode;
-class CFX_LinkUserData;
+class CXFA_FFDoc;
 class CXFA_Node;
-class CXFA_PieceLine;
-class CXFA_TextPiece;
+class CXFA_TextParser;
 class CXFA_TextProvider;
 class CXFA_TextTabstopsContext;
 class TextCharPos;
-struct CXFA_LoaderContext;
-struct FX_RTFTEXTOBJ;
 
-class CXFA_TextLayout {
+class CXFA_TextLayout final : public cppgc::GarbageCollected<CXFA_TextLayout> {
  public:
-  CXFA_TextLayout(CXFA_FFDoc* doc, CXFA_TextProvider* pTextProvider);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_TextLayout();
 
+  void Trace(cppgc::Visitor* visitor) const;
+
   float GetLayoutHeight();
   float StartLayout(float fWidth);
   float DoLayout(float fTextHeight);
@@ -52,24 +59,72 @@
                   size_t szBlockIndex);
   bool IsLoaded() const { return !m_pieceLines.empty(); }
   void Unload();
-
-  const std::vector<std::unique_ptr<CXFA_PieceLine>>* GetPieceLines() const {
-    return &m_pieceLines;
-  }
-
   bool HasBlock() const { return m_bHasBlock; }
   void ClearBlocks() { m_Blocks.clear(); }
   void ResetHasBlock() { m_bHasBlock = false; }
 
+  // Returns empty string when no link is present.
+  WideString GetLinkURLAtPoint(const CFX_PointF& point);
+
  private:
+  class TextPiece : public CFGAS_TextPiece {
+   public:
+    TextPiece();
+    ~TextPiece();
+
+    int32_t iUnderline = 0;
+    int32_t iLineThrough = 0;
+    XFA_AttributeValue iPeriod = XFA_AttributeValue::All;
+    FX_ARGB dwColor = 0;
+    RetainPtr<CFGAS_LinkUserData> pLinkData;
+  };
+
+  class PieceLine {
+   public:
+    PieceLine();
+    ~PieceLine();
+
+    std::vector<std::unique_ptr<TextPiece>> m_textPieces;
+    std::vector<size_t> m_charCounts;
+  };
+
   struct BlockData {
     size_t szIndex;
     size_t szLength;
   };
 
+  struct BlockHeight {
+    size_t szBlockIndex;
+    float fHeight;
+  };
+
+  struct LoaderContext : public cppgc::GarbageCollected<LoaderContext> {
+    LoaderContext();
+    ~LoaderContext();
+
+    void Trace(cppgc::Visitor* visitor) const;
+
+    bool bSaveLineHeight = false;
+    bool bFilterSpace = false;
+    float fWidth = 0;
+    float fHeight = 0;
+    float fLastPos = 0;
+    float fStartLineOffset = 0;
+    size_t nCharIdx = 0;
+    // TODO(thestig): Make this size_t?
+    int32_t iTotalLines = -1;
+    UnownedPtr<const CFX_XMLNode> pXMLNode;
+    RetainPtr<CFX_CSSComputedStyle> pParentStyle;
+    cppgc::Member<CXFA_Node> pNode;
+    std::vector<float> lineHeights;
+    std::vector<BlockHeight> blockHeights;
+  };
+
+  CXFA_TextLayout(CXFA_FFDoc* doc, CXFA_TextProvider* pTextProvider);
+
   void GetTextDataNode();
   CFX_XMLNode* GetXMLContainerNode();
-  std::unique_ptr<CFX_RTFBreak> CreateBreak(bool bDefault);
+  std::unique_ptr<CFGAS_RTFBreak> CreateBreak(bool bDefault);
   void InitBreak(float fLineWidth);
   void InitBreak(CFX_CSSComputedStyle* pStyle,
                  CFX_CSSDisplay eDisplay,
@@ -84,9 +139,9 @@
   bool LoadRichText(const CFX_XMLNode* pXMLNode,
                     float textWidth,
                     float* pLinePos,
-                    const RetainPtr<CFX_CSSComputedStyle>& pParentStyle,
+                    RetainPtr<CFX_CSSComputedStyle> pParentStyle,
                     bool bSavePieces,
-                    RetainPtr<CFX_LinkUserData> pLinkData,
+                    RetainPtr<CFGAS_LinkUserData> pLinkData,
                     bool bEndBreak,
                     bool bIsOl,
                     int32_t iLiCount);
@@ -94,26 +149,26 @@
                   float* pLinePos,
                   float fSpaceAbove,
                   bool bSavePieces);
-  void AppendTextLine(CFX_BreakType dwStatus,
+  void AppendTextLine(CFGAS_Char::BreakType dwStatus,
                       float* pLinePos,
                       bool bSavePieces,
                       bool bEndBreak);
-  void EndBreak(CFX_BreakType dwStatus, float* pLinePos, bool bDefault);
+  void EndBreak(CFGAS_Char::BreakType dwStatus, float* pLinePos, bool bDefault);
   bool IsEnd(bool bSavePieces);
   void UpdateAlign(float fHeight, float fBottom);
   void RenderString(CFX_RenderDevice* pDevice,
-                    CXFA_PieceLine* pPieceLine,
+                    PieceLine* pPieceLine,
                     size_t szPiece,
                     std::vector<TextCharPos>* pCharPos,
                     const CFX_Matrix& mtDoc2Device);
   void RenderPath(CFX_RenderDevice* pDevice,
-                  CXFA_PieceLine* pPieceLine,
+                  const PieceLine* pPieceLine,
                   size_t szPiece,
                   std::vector<TextCharPos>* pCharPos,
                   const CFX_Matrix& mtDoc2Device);
-  size_t GetDisplayPos(const CXFA_TextPiece* pPiece,
+  size_t GetDisplayPos(const TextPiece* pPiece,
                        std::vector<TextCharPos>* pCharPos);
-  void DoTabstops(CFX_CSSComputedStyle* pStyle, CXFA_PieceLine* pPieceLine);
+  void DoTabstops(CFX_CSSComputedStyle* pStyle, PieceLine* pPieceLine);
   bool LayoutInternal(size_t szBlockIndex);
   size_t CountBlocks() const;
   size_t GetNextIndexFromLastBlockData() const;
@@ -121,17 +176,16 @@
 
   bool m_bHasBlock = false;
   bool m_bRichText = false;
-  bool m_bBlockContinue = true;
   int32_t m_iLines = 0;
   float m_fMaxWidth = 0;
   std::vector<BlockData> m_Blocks;
-  UnownedPtr<CXFA_FFDoc> const m_pDoc;
-  CXFA_TextProvider* const m_pTextProvider;  // Raw, owned by tree node.
-  CXFA_Node* m_pTextDataNode = nullptr;      // Raw, owned by tree node.
-  std::unique_ptr<CFX_RTFBreak> m_pBreak;
-  std::unique_ptr<CXFA_LoaderContext> m_pLoader;
-  CXFA_TextParser m_textParser;
-  std::vector<std::unique_ptr<CXFA_PieceLine>> m_pieceLines;
+  cppgc::Member<CXFA_FFDoc> const m_pDoc;
+  cppgc::Member<CXFA_TextProvider> const m_pTextProvider;
+  cppgc::Member<CXFA_Node> m_pTextDataNode;
+  cppgc::Member<CXFA_TextParser> m_pTextParser;
+  cppgc::Member<LoaderContext> m_pLoader;
+  std::unique_ptr<CFGAS_RTFBreak> m_pBreak;
+  std::vector<std::unique_ptr<PieceLine>> m_pieceLines;
   std::unique_ptr<CXFA_TextTabstopsContext> m_pTabstopContext;
 };
 
diff --git a/xfa/fxfa/cxfa_textparsecontext.cpp b/xfa/fxfa/cxfa_textparsecontext.cpp
deleted file mode 100644
index bd21afc..0000000
--- a/xfa/fxfa/cxfa_textparsecontext.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/cxfa_textparsecontext.h"
-
-#include "core/fxcrt/css/cfx_csscomputedstyle.h"
-#include "core/fxcrt/css/cfx_cssdeclaration.h"
-#include "core/fxcrt/css/cfx_cssstyleselector.h"
-
-CXFA_TextParseContext::CXFA_TextParseContext()
-    : m_pParentStyle(nullptr), m_eDisplay(CFX_CSSDisplay::None) {}
-
-CXFA_TextParseContext::~CXFA_TextParseContext() {}
diff --git a/xfa/fxfa/cxfa_textparsecontext.h b/xfa/fxfa/cxfa_textparsecontext.h
deleted file mode 100644
index a146d40..0000000
--- a/xfa/fxfa/cxfa_textparsecontext.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_CXFA_TEXTPARSECONTEXT_H_
-#define XFA_FXFA_CXFA_TEXTPARSECONTEXT_H_
-
-#include <utility>
-#include <vector>
-
-#include "core/fxcrt/css/cfx_css.h"
-#include "core/fxcrt/css/cfx_cssdeclaration.h"
-
-class CFX_CSSComputedStyle;
-
-class CXFA_TextParseContext {
- public:
-  CXFA_TextParseContext();
-  ~CXFA_TextParseContext();
-
-  void SetDisplay(CFX_CSSDisplay eDisplay) { m_eDisplay = eDisplay; }
-  CFX_CSSDisplay GetDisplay() const { return m_eDisplay; }
-
-  void SetDecls(std::vector<const CFX_CSSDeclaration*>&& decl) {
-    decls_ = std::move(decl);
-  }
-  const std::vector<const CFX_CSSDeclaration*>& GetDecls() { return decls_; }
-
-  RetainPtr<CFX_CSSComputedStyle> m_pParentStyle;
-
- private:
-  std::vector<const CFX_CSSDeclaration*> decls_;
-  CFX_CSSDisplay m_eDisplay;
-};
-
-#endif  // XFA_FXFA_CXFA_TEXTPARSECONTEXT_H_
diff --git a/xfa/fxfa/cxfa_textparser.cpp b/xfa/fxfa/cxfa_textparser.cpp
index 61b9a28..15b5178 100644
--- a/xfa/fxfa/cxfa_textparser.cpp
+++ b/xfa/fxfa/cxfa_textparser.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,23 +8,23 @@
 
 #include <algorithm>
 #include <utility>
-#include <vector>
 
 #include "core/fxcrt/css/cfx_css.h"
 #include "core/fxcrt/css/cfx_csscomputedstyle.h"
+#include "core/fxcrt/css/cfx_cssdeclaration.h"
 #include "core/fxcrt/css/cfx_cssstyleselector.h"
 #include "core/fxcrt/css/cfx_cssstylesheet.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
 #include "core/fxge/fx_font.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/notreached.h"
 #include "xfa/fgas/font/cfgas_fontmgr.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_fontmgr.h"
-#include "xfa/fxfa/cxfa_textparsecontext.h"
 #include "xfa/fxfa/cxfa_textprovider.h"
 #include "xfa/fxfa/cxfa_texttabstopscontext.h"
 #include "xfa/fxfa/parser/cxfa_font.h"
@@ -57,10 +57,9 @@
 
 }  // namespace
 
-CXFA_TextParser::CXFA_TextParser()
-    : m_bParsed(false), m_cssInitialized(false) {}
+CXFA_TextParser::CXFA_TextParser() = default;
 
-CXFA_TextParser::~CXFA_TextParser() {}
+CXFA_TextParser::~CXFA_TextParser() = default;
 
 void CXFA_TextParser::Reset() {
   m_mapXMLNodeToParseContext.clear();
@@ -72,10 +71,10 @@
     return;
 
   if (!m_pSelector) {
-    m_pSelector = pdfium::MakeUnique<CFX_CSSStyleSelector>();
+    m_pSelector = std::make_unique<CFX_CSSStyleSelector>();
 
     CXFA_Font* font = pTextProvider->GetFontIfExists();
-    m_pSelector->SetDefFontSize(font ? font->GetFontSize() : 10.0f);
+    m_pSelector->SetDefaultFontSize(font ? font->GetFontSize() : 10.0f);
   }
 
   if (m_cssInitialized)
@@ -99,8 +98,8 @@
       "sup{vertical-align:+15em;font-size:.66em}"
       "sub{vertical-align:-15em;font-size:.66em}";
   WideString ws = WideString::FromASCII(kStyle);
-  auto sheet = pdfium::MakeUnique<CFX_CSSStyleSheet>();
-  if (!sheet->LoadBuffer(ws.c_str(), ws.GetLength()))
+  auto sheet = std::make_unique<CFX_CSSStyleSheet>();
+  if (!sheet->LoadBuffer(ws.AsStringView()))
     return nullptr;
 
   return sheet;
@@ -136,8 +135,7 @@
       case XFA_AttributeValue::Radix:
         break;
       default:
-        NOTREACHED();
-        break;
+        NOTREACHED_NORETURN();
     }
     pStyle->SetTextAlign(hAlign);
     CFX_CSSRect rtMarginWidth;
@@ -159,13 +157,13 @@
     CFX_CSSLength letterSpacing;
     letterSpacing.Set(CFX_CSSLengthUnit::Point, font->GetLetterSpacing());
     pStyle->SetLetterSpacing(letterSpacing);
-    uint32_t dwDecoration = 0;
+    Mask<CFX_CSSTEXTDECORATION> dwDecoration;
     if (font->GetLineThrough() > 0)
-      dwDecoration |= CFX_CSSTEXTDECORATION_LineThrough;
+      dwDecoration |= CFX_CSSTEXTDECORATION::kLineThrough;
     if (font->GetUnderline() > 1)
-      dwDecoration |= CFX_CSSTEXTDECORATION_Double;
+      dwDecoration |= CFX_CSSTEXTDECORATION::kDouble;
     else if (font->GetUnderline() > 0)
-      dwDecoration |= CFX_CSSTEXTDECORATION_Underline;
+      dwDecoration |= CFX_CSSTEXTDECORATION::kUnderline;
 
     pStyle->SetTextDecoration(dwDecoration);
   }
@@ -175,13 +173,13 @@
 }
 
 RetainPtr<CFX_CSSComputedStyle> CXFA_TextParser::CreateStyle(
-    CFX_CSSComputedStyle* pParentStyle) {
+    const CFX_CSSComputedStyle* pParentStyle) {
   auto pNewStyle = m_pSelector->CreateComputedStyle(pParentStyle);
-  ASSERT(pNewStyle);
+  DCHECK(pNewStyle);
   if (!pParentStyle)
     return pNewStyle;
 
-  uint32_t dwDecoration = pParentStyle->GetTextDecoration();
+  Mask<CFX_CSSTEXTDECORATION> dwDecoration = pParentStyle->GetTextDecoration();
   float fBaseLine = 0;
   if (pParentStyle->GetVerticalAlign() == CFX_CSSVerticalAlign::Number)
     fBaseLine = pParentStyle->GetNumberVerticalAlign();
@@ -197,16 +195,16 @@
 
 RetainPtr<CFX_CSSComputedStyle> CXFA_TextParser::ComputeStyle(
     const CFX_XMLNode* pXMLNode,
-    CFX_CSSComputedStyle* pParentStyle) {
+    RetainPtr<const CFX_CSSComputedStyle> pParentStyle) {
   auto it = m_mapXMLNodeToParseContext.find(pXMLNode);
   if (it == m_mapXMLNodeToParseContext.end())
     return nullptr;
 
-  CXFA_TextParseContext* pContext = it->second.get();
+  Context* pContext = it->second.get();
   if (!pContext)
     return nullptr;
 
-  pContext->m_pParentStyle.Reset(pParentStyle);
+  pContext->SetParentStyle(pParentStyle);
 
   auto tagProvider = ParseTagInfo(pXMLNode);
   if (tagProvider->m_bContent)
@@ -231,7 +229,7 @@
 }
 
 void CXFA_TextParser::ParseRichText(const CFX_XMLNode* pXMLNode,
-                                    CFX_CSSComputedStyle* pParentStyle) {
+                                    const CFX_CSSComputedStyle* pParentStyle) {
   if (!pXMLNode)
     return;
 
@@ -242,7 +240,7 @@
   RetainPtr<CFX_CSSComputedStyle> pNewStyle;
   if (!(tagProvider->GetTagName().EqualsASCII("body") &&
         tagProvider->GetTagName().EqualsASCII("html"))) {
-    auto pTextContext = pdfium::MakeUnique<CXFA_TextParseContext>();
+    auto pTextContext = std::make_unique<Context>();
     CFX_CSSDisplay eDisplay = CFX_CSSDisplay::Inline;
     if (!tagProvider->m_bContent) {
       auto declArray =
@@ -284,13 +282,13 @@
       0xdb8ac455,  // html
   };
   return std::binary_search(std::begin(s_XFATagName), std::end(s_XFATagName),
-                            FX_HashCode_GetW(wsName.AsStringView(), true));
+                            FX_HashCode_GetLoweredW(wsName.AsStringView()));
 }
 
 // static
 std::unique_ptr<CXFA_TextParser::TagProvider> CXFA_TextParser::ParseTagInfo(
     const CFX_XMLNode* pXMLNode) {
-  auto tagProvider = pdfium::MakeUnique<TagProvider>();
+  auto tagProvider = std::make_unique<TagProvider>();
   const CFX_XMLElement* pXMLElement = ToXMLElement(pXMLNode);
   if (pXMLElement) {
     WideString wsName = pXMLElement->GetLocalTagName();
@@ -315,21 +313,22 @@
   return para ? para->GetVerticalAlign() : XFA_AttributeValue::Top;
 }
 
-float CXFA_TextParser::GetTabInterval(CFX_CSSComputedStyle* pStyle) const {
+float CXFA_TextParser::GetTabInterval(
+    const CFX_CSSComputedStyle* pStyle) const {
   WideString wsValue;
   if (pStyle && pStyle->GetCustomStyle(L"tab-interval", &wsValue))
     return CXFA_Measurement(wsValue.AsStringView()).ToUnit(XFA_Unit::Pt);
   return 36;
 }
 
-int32_t CXFA_TextParser::CountTabs(CFX_CSSComputedStyle* pStyle) const {
+int32_t CXFA_TextParser::CountTabs(const CFX_CSSComputedStyle* pStyle) const {
   WideString wsValue;
   if (pStyle && pStyle->GetCustomStyle(L"xfa-tab-count", &wsValue))
     return wsValue.GetInteger();
   return 0;
 }
 
-bool CXFA_TextParser::IsSpaceRun(CFX_CSSComputedStyle* pStyle) const {
+bool CXFA_TextParser::IsSpaceRun(const CFX_CSSComputedStyle* pStyle) const {
   WideString wsValue;
   return pStyle && pStyle->GetCustomStyle(L"xfa-spacerun", &wsValue) &&
          wsValue.EqualsASCIINoCase("yes");
@@ -338,7 +337,7 @@
 RetainPtr<CFGAS_GEFont> CXFA_TextParser::GetFont(
     CXFA_FFDoc* doc,
     CXFA_TextProvider* pTextProvider,
-    CFX_CSSComputedStyle* pStyle) const {
+    const CFX_CSSComputedStyle* pStyle) const {
   WideString wsFamily = L"Courier";
   uint32_t dwStyle = 0;
   CXFA_Font* font = pTextProvider->GetFontIfExists();
@@ -351,9 +350,9 @@
   }
 
   if (pStyle) {
-    int32_t iCount = pStyle->CountFontFamilies();
-    if (iCount > 0)
-      wsFamily = pStyle->GetFontFamily(iCount - 1).AsStringView();
+    absl::optional<WideString> last_family = pStyle->GetLastFontFamily();
+    if (last_family.has_value())
+      wsFamily = last_family.value();
 
     dwStyle = 0;
     if (pStyle->GetFontWeight() > FXFONT_FW_NORMAL)
@@ -363,11 +362,11 @@
   }
 
   CXFA_FontMgr* pFontMgr = doc->GetApp()->GetXFAFontMgr();
-  return pFontMgr->GetFont(doc, wsFamily.AsStringView(), dwStyle);
+  return pFontMgr->GetFont(doc, std::move(wsFamily), dwStyle);
 }
 
 float CXFA_TextParser::GetFontSize(CXFA_TextProvider* pTextProvider,
-                                   CFX_CSSComputedStyle* pStyle) const {
+                                   const CFX_CSSComputedStyle* pStyle) const {
   if (pStyle)
     return pStyle->GetFontSize();
 
@@ -376,7 +375,7 @@
 }
 
 int32_t CXFA_TextParser::GetHorScale(CXFA_TextProvider* pTextProvider,
-                                     CFX_CSSComputedStyle* pStyle,
+                                     const CFX_CSSComputedStyle* pStyle,
                                      const CFX_XMLNode* pXMLNode) const {
   if (pStyle) {
     WideString wsValue;
@@ -386,9 +385,9 @@
     while (pXMLNode) {
       auto it = m_mapXMLNodeToParseContext.find(pXMLNode);
       if (it != m_mapXMLNodeToParseContext.end()) {
-        CXFA_TextParseContext* pContext = it->second.get();
-        if (pContext && pContext->m_pParentStyle &&
-            pContext->m_pParentStyle->GetCustomStyle(
+        Context* pContext = it->second.get();
+        if (pContext && pContext->GetParentStyle() &&
+            pContext->GetParentStyle()->GetCustomStyle(
                 L"xfa-font-horizontal-scale", &wsValue)) {
           return wsValue.GetInteger();
         }
@@ -402,7 +401,7 @@
 }
 
 int32_t CXFA_TextParser::GetVerScale(CXFA_TextProvider* pTextProvider,
-                                     CFX_CSSComputedStyle* pStyle) const {
+                                     const CFX_CSSComputedStyle* pStyle) const {
   if (pStyle) {
     WideString wsValue;
     if (pStyle->GetCustomStyle(L"xfa-font-vertical-scale", &wsValue))
@@ -413,54 +412,47 @@
   return font ? static_cast<int32_t>(font->GetVerticalScale()) : 100;
 }
 
-void CXFA_TextParser::GetUnderline(CXFA_TextProvider* pTextProvider,
-                                   CFX_CSSComputedStyle* pStyle,
-                                   int32_t& iUnderline,
-                                   XFA_AttributeValue& iPeriod) const {
-  iUnderline = 0;
-  iPeriod = XFA_AttributeValue::All;
+int32_t CXFA_TextParser::GetUnderline(
+    CXFA_TextProvider* pTextProvider,
+    const CFX_CSSComputedStyle* pStyle) const {
   CXFA_Font* font = pTextProvider->GetFontIfExists();
-  if (!pStyle) {
-    if (font) {
-      iUnderline = font->GetUnderline();
-      iPeriod = font->GetUnderlinePeriod();
-    }
-    return;
-  }
+  if (!pStyle)
+    return font ? font->GetUnderline() : 0;
 
-  uint32_t dwDecoration = pStyle->GetTextDecoration();
-  if (dwDecoration & CFX_CSSTEXTDECORATION_Double)
-    iUnderline = 2;
-  else if (dwDecoration & CFX_CSSTEXTDECORATION_Underline)
-    iUnderline = 1;
-
-  WideString wsValue;
-  if (pStyle->GetCustomStyle(L"underlinePeriod", &wsValue)) {
-    if (wsValue.EqualsASCII("word"))
-      iPeriod = XFA_AttributeValue::Word;
-  } else if (font) {
-    iPeriod = font->GetUnderlinePeriod();
-  }
+  const Mask<CFX_CSSTEXTDECORATION> dwDecoration = pStyle->GetTextDecoration();
+  if (dwDecoration & CFX_CSSTEXTDECORATION::kDouble)
+    return 2;
+  if (dwDecoration & CFX_CSSTEXTDECORATION::kUnderline)
+    return 1;
+  return 0;
 }
 
-void CXFA_TextParser::GetLinethrough(CXFA_TextProvider* pTextProvider,
-                                     CFX_CSSComputedStyle* pStyle,
-                                     int32_t& iLinethrough) const {
-  iLinethrough = 0;
-  if (pStyle) {
-    uint32_t dwDecoration = pStyle->GetTextDecoration();
-    if (dwDecoration & CFX_CSSTEXTDECORATION_LineThrough)
-      iLinethrough = 1;
-    return;
+XFA_AttributeValue CXFA_TextParser::GetUnderlinePeriod(
+    CXFA_TextProvider* pTextProvider,
+    const CFX_CSSComputedStyle* pStyle) const {
+  WideString wsValue;
+  if (pStyle && pStyle->GetCustomStyle(L"underlinePeriod", &wsValue)) {
+    return wsValue.EqualsASCII("word") ? XFA_AttributeValue::Word
+                                       : XFA_AttributeValue::All;
   }
-
   CXFA_Font* font = pTextProvider->GetFontIfExists();
-  if (font)
-    iLinethrough = font->GetLineThrough();
+  return font ? font->GetUnderlinePeriod() : XFA_AttributeValue::All;
+}
+
+int32_t CXFA_TextParser::GetLinethrough(
+    CXFA_TextProvider* pTextProvider,
+    const CFX_CSSComputedStyle* pStyle) const {
+  if (pStyle) {
+    const Mask<CFX_CSSTEXTDECORATION> dwDecoration =
+        pStyle->GetTextDecoration();
+    return (dwDecoration & CFX_CSSTEXTDECORATION::kLineThrough) ? 1 : 0;
+  }
+  CXFA_Font* font = pTextProvider->GetFontIfExists();
+  return font ? font->GetLineThrough() : 0;
 }
 
 FX_ARGB CXFA_TextParser::GetColor(CXFA_TextProvider* pTextProvider,
-                                  CFX_CSSComputedStyle* pStyle) const {
+                                  const CFX_CSSComputedStyle* pStyle) const {
   if (pStyle)
     return pStyle->GetColor();
 
@@ -469,7 +461,7 @@
 }
 
 float CXFA_TextParser::GetBaseline(CXFA_TextProvider* pTextProvider,
-                                   CFX_CSSComputedStyle* pStyle) const {
+                                   const CFX_CSSComputedStyle* pStyle) const {
   if (pStyle) {
     if (pStyle->GetVerticalAlign() == CFX_CSSVerticalAlign::Number)
       return pStyle->GetNumberVerticalAlign();
@@ -482,7 +474,7 @@
 }
 
 float CXFA_TextParser::GetLineHeight(CXFA_TextProvider* pTextProvider,
-                                     CFX_CSSComputedStyle* pStyle,
+                                     const CFX_CSSComputedStyle* pStyle,
                                      bool bFirst,
                                      float fVerScale) const {
   float fLineHeight = 0;
@@ -507,19 +499,19 @@
   return fLineHeight;
 }
 
-Optional<WideString> CXFA_TextParser::GetEmbeddedObj(
+absl::optional<WideString> CXFA_TextParser::GetEmbeddedObj(
     const CXFA_TextProvider* pTextProvider,
     const CFX_XMLNode* pXMLNode) {
   if (!pXMLNode)
-    return {};
+    return absl::nullopt;
 
   const CFX_XMLElement* pElement = ToXMLElement(pXMLNode);
   if (!pElement)
-    return {};
+    return absl::nullopt;
 
   WideString wsAttr = pElement->GetAttribute(L"xfa:embed");
   if (wsAttr.IsEmpty())
-    return {};
+    return absl::nullopt;
 
   if (wsAttr[0] == L'#')
     wsAttr.Delete(0);
@@ -527,23 +519,23 @@
   WideString ws =
       GetLowerCaseElementAttributeOrDefault(pElement, L"xfa:embedType", L"som");
   if (!ws.EqualsASCII("uri"))
-    return {};
+    return absl::nullopt;
 
   ws = GetLowerCaseElementAttributeOrDefault(pElement, L"xfa:embedMode",
                                              L"formatted");
   if (!(ws.EqualsASCII("raw") || ws.EqualsASCII("formatted")))
-    return {};
+    return absl::nullopt;
 
   return pTextProvider->GetEmbeddedObj(wsAttr);
 }
 
-CXFA_TextParseContext* CXFA_TextParser::GetParseContextFromMap(
+CXFA_TextParser::Context* CXFA_TextParser::GetParseContextFromMap(
     const CFX_XMLNode* pXMLNode) {
   auto it = m_mapXMLNodeToParseContext.find(pXMLNode);
   return it != m_mapXMLNodeToParseContext.end() ? it->second.get() : nullptr;
 }
 
-bool CXFA_TextParser::GetTabstops(CFX_CSSComputedStyle* pStyle,
+bool CXFA_TextParser::GetTabstops(const CFX_CSSComputedStyle* pStyle,
                                   CXFA_TextTabstopsContext* pTabstopContext) {
   if (!pStyle || !pTabstopContext)
     return false;
@@ -607,7 +599,7 @@
         break;
       case TabStopStatus::Location:
         if (ch == ' ') {
-          uint32_t dwHashCode = FX_HashCode_GetW(wsAlign.AsStringView(), true);
+          uint32_t dwHashCode = FX_HashCode_GetLoweredW(wsAlign.AsStringView());
           CXFA_Measurement ms(
               WideStringView(spTabStops.subspan(iLast, iCur - iLast)));
           float fPos = ms.ToUnit(XFA_Unit::Pt);
@@ -623,7 +615,7 @@
   }
 
   if (!wsAlign.IsEmpty()) {
-    uint32_t dwHashCode = FX_HashCode_GetW(wsAlign.AsStringView(), true);
+    uint32_t dwHashCode = FX_HashCode_GetLoweredW(wsAlign.AsStringView());
     CXFA_Measurement ms(
         WideStringView(spTabStops.subspan(iLast, iCur - iLast)));
     float fPos = ms.ToUnit(XFA_Unit::Pt);
@@ -632,7 +624,20 @@
   return true;
 }
 
-CXFA_TextParser::TagProvider::TagProvider()
-    : m_bTagAvailable(false), m_bContent(false) {}
+CXFA_TextParser::TagProvider::TagProvider() = default;
 
-CXFA_TextParser::TagProvider::~TagProvider() {}
+CXFA_TextParser::TagProvider::~TagProvider() = default;
+
+CXFA_TextParser::Context::Context() = default;
+
+CXFA_TextParser::Context::~Context() = default;
+
+void CXFA_TextParser::Context::SetParentStyle(
+    RetainPtr<const CFX_CSSComputedStyle> style) {
+  m_pParentStyle = std::move(style);
+}
+
+void CXFA_TextParser::Context::SetDecls(
+    std::vector<const CFX_CSSDeclaration*>&& decl) {
+  decls_ = std::move(decl);
+}
diff --git a/xfa/fxfa/cxfa_textparser.h b/xfa/fxfa/cxfa_textparser.h
index b1e5878..3e92bc2 100644
--- a/xfa/fxfa/cxfa_textparser.h
+++ b/xfa/fxfa/cxfa_textparser.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,31 +7,62 @@
 #ifndef XFA_FXFA_CXFA_TEXTPARSER_H_
 #define XFA_FXFA_CXFA_TEXTPARSER_H_
 
+#include <stdint.h>
+
 #include <map>
 #include <memory>
+#include <vector>
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/css/cfx_css.h"
+#include "core/fxcrt/css/cfx_csscomputedstyle.h"
+#include "core/fxcrt/css/cfx_cssdeclaration.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/optional.h"
+#include "core/fxcrt/widestring.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "fxjs/gc/heap.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "v8/include/cppgc/garbage-collected.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
 class CFGAS_GEFont;
-class CFX_CSSComputedStyle;
 class CFX_CSSStyleSelector;
 class CFX_CSSStyleSheet;
 class CFX_XMLNode;
 class CXFA_FFDoc;
-class CXFA_TextParseContext;
 class CXFA_TextProvider;
 class CXFA_TextTabstopsContext;
 
-class CXFA_TextParser {
+class CXFA_TextParser : public cppgc::GarbageCollected<CXFA_TextParser> {
  public:
-  CXFA_TextParser();
+  class Context {
+   public:
+    Context();
+    ~Context();
+
+    void SetParentStyle(RetainPtr<const CFX_CSSComputedStyle> style);
+    RetainPtr<const CFX_CSSComputedStyle> GetParentStyle() const {
+      return m_pParentStyle;
+    }
+
+    void SetDisplay(CFX_CSSDisplay eDisplay) { m_eDisplay = eDisplay; }
+    CFX_CSSDisplay GetDisplay() const { return m_eDisplay; }
+
+    void SetDecls(std::vector<const CFX_CSSDeclaration*>&& decl);
+    const std::vector<const CFX_CSSDeclaration*>& GetDecls() const {
+      return decls_;
+    }
+
+   private:
+    RetainPtr<const CFX_CSSComputedStyle> m_pParentStyle;
+    CFX_CSSDisplay m_eDisplay = CFX_CSSDisplay::None;
+    std::vector<const CFX_CSSDeclaration*> decls_;
+  };
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   virtual ~CXFA_TextParser();
 
+  void Trace(cppgc::Visitor* visitor) const {}
+
   void Reset();
   void DoParse(const CFX_XMLNode* pXMLContainer,
                CXFA_TextProvider* pTextProvider);
@@ -40,52 +71,53 @@
       CXFA_TextProvider* pTextProvider);
   RetainPtr<CFX_CSSComputedStyle> ComputeStyle(
       const CFX_XMLNode* pXMLNode,
-      CFX_CSSComputedStyle* pParentStyle);
+      RetainPtr<const CFX_CSSComputedStyle> pParentStyle);
 
   bool IsParsed() const { return m_bParsed; }
 
   XFA_AttributeValue GetVAlign(CXFA_TextProvider* pTextProvider) const;
 
-  float GetTabInterval(CFX_CSSComputedStyle* pStyle) const;
-  int32_t CountTabs(CFX_CSSComputedStyle* pStyle) const;
+  float GetTabInterval(const CFX_CSSComputedStyle* pStyle) const;
+  int32_t CountTabs(const CFX_CSSComputedStyle* pStyle) const;
 
-  bool IsSpaceRun(CFX_CSSComputedStyle* pStyle) const;
-  bool GetTabstops(CFX_CSSComputedStyle* pStyle,
+  bool IsSpaceRun(const CFX_CSSComputedStyle* pStyle) const;
+  bool GetTabstops(const CFX_CSSComputedStyle* pStyle,
                    CXFA_TextTabstopsContext* pTabstopContext);
 
   RetainPtr<CFGAS_GEFont> GetFont(CXFA_FFDoc* doc,
                                   CXFA_TextProvider* pTextProvider,
-                                  CFX_CSSComputedStyle* pStyle) const;
+                                  const CFX_CSSComputedStyle* pStyle) const;
   float GetFontSize(CXFA_TextProvider* pTextProvider,
-                    CFX_CSSComputedStyle* pStyle) const;
-
+                    const CFX_CSSComputedStyle* pStyle) const;
   int32_t GetHorScale(CXFA_TextProvider* pTextProvider,
-                      CFX_CSSComputedStyle* pStyle,
+                      const CFX_CSSComputedStyle* pStyle,
                       const CFX_XMLNode* pXMLNode) const;
   int32_t GetVerScale(CXFA_TextProvider* pTextProvider,
-                      CFX_CSSComputedStyle* pStyle) const;
-
-  void GetUnderline(CXFA_TextProvider* pTextProvider,
-                    CFX_CSSComputedStyle* pStyle,
-                    int32_t& iUnderline,
-                    XFA_AttributeValue& iPeriod) const;
-  void GetLinethrough(CXFA_TextProvider* pTextProvider,
-                      CFX_CSSComputedStyle* pStyle,
-                      int32_t& iLinethrough) const;
+                      const CFX_CSSComputedStyle* pStyle) const;
+  int32_t GetUnderline(CXFA_TextProvider* pTextProvider,
+                       const CFX_CSSComputedStyle* pStyle) const;
+  XFA_AttributeValue GetUnderlinePeriod(
+      CXFA_TextProvider* pTextProvider,
+      const CFX_CSSComputedStyle* pStyle) const;
+  int32_t GetLinethrough(CXFA_TextProvider* pTextProvider,
+                         const CFX_CSSComputedStyle* pStyle) const;
   FX_ARGB GetColor(CXFA_TextProvider* pTextProvider,
-                   CFX_CSSComputedStyle* pStyle) const;
+                   const CFX_CSSComputedStyle* pStyle) const;
   float GetBaseline(CXFA_TextProvider* pTextProvider,
-                    CFX_CSSComputedStyle* pStyle) const;
+                    const CFX_CSSComputedStyle* pStyle) const;
   float GetLineHeight(CXFA_TextProvider* pTextProvider,
-                      CFX_CSSComputedStyle* pStyle,
+                      const CFX_CSSComputedStyle* pStyle,
                       bool bFirst,
                       float fVerScale) const;
 
-  Optional<WideString> GetEmbeddedObj(const CXFA_TextProvider* pTextProvider,
-                                      const CFX_XMLNode* pXMLNode);
-  CXFA_TextParseContext* GetParseContextFromMap(const CFX_XMLNode* pXMLNode);
+  absl::optional<WideString> GetEmbeddedObj(
+      const CXFA_TextProvider* pTextProvider,
+      const CFX_XMLNode* pXMLNode);
+  Context* GetParseContextFromMap(const CFX_XMLNode* pXMLNode);
 
  protected:
+  CXFA_TextParser();
+
   bool TagValidate(const WideString& str) const;
 
  private:
@@ -105,8 +137,8 @@
       return m_Attributes[wsAttr];
     }
 
-    bool m_bTagAvailable;
-    bool m_bContent;
+    bool m_bTagAvailable = false;
+    bool m_bContent = false;
 
    private:
     WideString m_wsTagName;
@@ -118,15 +150,15 @@
 
   void InitCSSData(CXFA_TextProvider* pTextProvider);
   void ParseRichText(const CFX_XMLNode* pXMLNode,
-                     CFX_CSSComputedStyle* pParentStyle);
+                     const CFX_CSSComputedStyle* pParentStyle);
   std::unique_ptr<CFX_CSSStyleSheet> LoadDefaultSheetStyle();
   RetainPtr<CFX_CSSComputedStyle> CreateStyle(
-      CFX_CSSComputedStyle* pParentStyle);
+      const CFX_CSSComputedStyle* pParentStyle);
 
-  bool m_bParsed;
-  bool m_cssInitialized;
+  bool m_bParsed = false;
+  bool m_cssInitialized = false;
   std::unique_ptr<CFX_CSSStyleSelector> m_pSelector;
-  std::map<const CFX_XMLNode*, std::unique_ptr<CXFA_TextParseContext>>
+  std::map<const CFX_XMLNode*, std::unique_ptr<Context>>
       m_mapXMLNodeToParseContext;
 };
 
diff --git a/xfa/fxfa/cxfa_textparser_unittest.cpp b/xfa/fxfa/cxfa_textparser_unittest.cpp
index 5198638..298362e 100644
--- a/xfa/fxfa/cxfa_textparser_unittest.cpp
+++ b/xfa/fxfa/cxfa_textparser_unittest.cpp
@@ -1,40 +1,48 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "xfa/fxfa/cxfa_textparser.h"
 
+#include "fxjs/gc/heap.h"
+#include "testing/fxgc_unittest.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "v8/include/cppgc/heap.h"
 
 class CXFA_TestTextParser final : public CXFA_TextParser {
  public:
-  CXFA_TestTextParser() : CXFA_TextParser() {}
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
 
  private:
+  CXFA_TestTextParser() = default;
+
   // Add test cases as friends to access protected member functions.
-  FRIEND_TEST(CXFA_TextParser, TagValidate);
+  FRIEND_TEST(CXFATextParserTest, TagValidate);
 };
 
-TEST(CXFA_TextParser, TagValidate) {
-  CXFA_TestTextParser parser;
-  EXPECT_TRUE(parser.TagValidate(L"br"));
-  EXPECT_TRUE(parser.TagValidate(L"Br"));
-  EXPECT_TRUE(parser.TagValidate(L"BR"));
-  EXPECT_TRUE(parser.TagValidate(L"a"));
-  EXPECT_TRUE(parser.TagValidate(L"b"));
-  EXPECT_TRUE(parser.TagValidate(L"i"));
-  EXPECT_TRUE(parser.TagValidate(L"p"));
-  EXPECT_TRUE(parser.TagValidate(L"li"));
-  EXPECT_TRUE(parser.TagValidate(L"ol"));
-  EXPECT_TRUE(parser.TagValidate(L"ul"));
-  EXPECT_TRUE(parser.TagValidate(L"sub"));
-  EXPECT_TRUE(parser.TagValidate(L"sup"));
-  EXPECT_TRUE(parser.TagValidate(L"span"));
-  EXPECT_TRUE(parser.TagValidate(L"body"));
-  EXPECT_TRUE(parser.TagValidate(L"html"));
+class CXFATextParserTest : public FXGCUnitTest {};
 
-  EXPECT_FALSE(parser.TagValidate(L""));
-  EXPECT_FALSE(parser.TagValidate(L"tml"));
-  EXPECT_FALSE(parser.TagValidate(L"xhtml"));
-  EXPECT_FALSE(parser.TagValidate(L"htmlx"));
+TEST_F(CXFATextParserTest, TagValidate) {
+  auto* parser = cppgc::MakeGarbageCollected<CXFA_TestTextParser>(
+      heap()->GetAllocationHandle());
+  EXPECT_TRUE(parser->TagValidate(L"br"));
+  EXPECT_TRUE(parser->TagValidate(L"Br"));
+  EXPECT_TRUE(parser->TagValidate(L"BR"));
+  EXPECT_TRUE(parser->TagValidate(L"a"));
+  EXPECT_TRUE(parser->TagValidate(L"b"));
+  EXPECT_TRUE(parser->TagValidate(L"i"));
+  EXPECT_TRUE(parser->TagValidate(L"p"));
+  EXPECT_TRUE(parser->TagValidate(L"li"));
+  EXPECT_TRUE(parser->TagValidate(L"ol"));
+  EXPECT_TRUE(parser->TagValidate(L"ul"));
+  EXPECT_TRUE(parser->TagValidate(L"sub"));
+  EXPECT_TRUE(parser->TagValidate(L"sup"));
+  EXPECT_TRUE(parser->TagValidate(L"span"));
+  EXPECT_TRUE(parser->TagValidate(L"body"));
+  EXPECT_TRUE(parser->TagValidate(L"html"));
+
+  EXPECT_FALSE(parser->TagValidate(L""));
+  EXPECT_FALSE(parser->TagValidate(L"tml"));
+  EXPECT_FALSE(parser->TagValidate(L"xhtml"));
+  EXPECT_FALSE(parser->TagValidate(L"htmlx"));
 }
diff --git a/xfa/fxfa/cxfa_textpiece.cpp b/xfa/fxfa/cxfa_textpiece.cpp
deleted file mode 100644
index 363361a..0000000
--- a/xfa/fxfa/cxfa_textpiece.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/cxfa_textpiece.h"
-
-#include "xfa/fgas/layout/cfx_linkuserdata.h"
-
-CXFA_TextPiece::CXFA_TextPiece() = default;
-
-CXFA_TextPiece::~CXFA_TextPiece() = default;
diff --git a/xfa/fxfa/cxfa_textpiece.h b/xfa/fxfa/cxfa_textpiece.h
deleted file mode 100644
index c9d524d..0000000
--- a/xfa/fxfa/cxfa_textpiece.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_CXFA_TEXTPIECE_H_
-#define XFA_FXFA_CXFA_TEXTPIECE_H_
-
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/fx_dib.h"
-#include "xfa/fgas/layout/cfx_textpiece.h"
-#include "xfa/fxfa/fxfa_basic.h"
-
-class CFX_LinkUserData;
-
-class CXFA_TextPiece : public CFX_TextPiece {
- public:
-  CXFA_TextPiece();
-  ~CXFA_TextPiece();
-
-  int32_t iUnderline;
-  int32_t iLineThrough;
-  XFA_AttributeValue iPeriod;
-  FX_ARGB dwColor;
-  RetainPtr<CFX_LinkUserData> pLinkData;
-};
-
-#endif  // XFA_FXFA_CXFA_TEXTPIECE_H_
diff --git a/xfa/fxfa/cxfa_textprovider.cpp b/xfa/fxfa/cxfa_textprovider.cpp
index 7a17037..2186c4b 100644
--- a/xfa/fxfa/cxfa_textprovider.cpp
+++ b/xfa/fxfa/cxfa_textprovider.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,15 +6,12 @@
 
 #include "xfa/fxfa/cxfa_textprovider.h"
 
-#include <algorithm>
-#include <memory>
-#include <vector>
-
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cfxjse_value.h"
 #include "fxjs/xfa/cjx_object.h"
+#include "third_party/base/check.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
@@ -33,12 +30,22 @@
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_para.h"
 #include "xfa/fxfa/parser/cxfa_value.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
+CXFA_TextProvider::CXFA_TextProvider(CXFA_Node* pNode, Type eType)
+    : m_pNode(pNode), m_eType(eType) {
+  DCHECK(m_pNode);
+}
+
+CXFA_TextProvider::~CXFA_TextProvider() = default;
+
+void CXFA_TextProvider::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pNode);
+}
+
 CXFA_Node* CXFA_TextProvider::GetTextNode(bool* bRichText) {
   *bRichText = false;
-  if (m_eType == XFA_TEXTPROVIDERTYPE_Text) {
+  if (m_eType == Type::kText) {
     CXFA_Value* pValueNode =
         m_pNode->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
     if (!pValueNode)
@@ -46,8 +53,9 @@
 
     CXFA_Node* pChildNode = pValueNode->GetFirstChild();
     if (pChildNode && pChildNode->GetElementType() == XFA_Element::ExData) {
-      Optional<WideString> contentType = pChildNode->JSObject()->TryAttribute(
-          XFA_Attribute::ContentType, false);
+      absl::optional<WideString> contentType =
+          pChildNode->JSObject()->TryAttribute(XFA_Attribute::ContentType,
+                                               false);
       if (contentType.has_value() &&
           contentType.value().EqualsASCII("text/html")) {
         *bRichText = true;
@@ -56,21 +64,7 @@
     return pChildNode;
   }
 
-  if (m_eType == XFA_TEXTPROVIDERTYPE_Datasets) {
-    CXFA_Node* pBind = m_pNode->GetBindData();
-    CFX_XMLNode* pXMLNode = pBind->GetXMLMappingNode();
-    for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild;
-         pXMLChild = pXMLChild->GetNextSibling()) {
-      CFX_XMLElement* pElement = ToXMLElement(pXMLChild);
-      if (pElement && XFA_RecognizeRichText(pElement)) {
-        *bRichText = true;
-        break;
-      }
-    }
-    return pBind;
-  }
-
-  if (m_eType == XFA_TEXTPROVIDERTYPE_Caption) {
+  if (m_eType == Type::kCaption) {
     CXFA_Caption* pCaptionNode =
         m_pNode->GetChild<CXFA_Caption>(0, XFA_Element::Caption, false);
     if (!pCaptionNode)
@@ -83,8 +77,9 @@
 
     CXFA_Node* pChildNode = pValueNode->GetFirstChild();
     if (pChildNode && pChildNode->GetElementType() == XFA_Element::ExData) {
-      Optional<WideString> contentType = pChildNode->JSObject()->TryAttribute(
-          XFA_Attribute::ContentType, false);
+      absl::optional<WideString> contentType =
+          pChildNode->JSObject()->TryAttribute(XFA_Attribute::ContentType,
+                                               false);
       if (contentType.has_value() &&
           contentType.value().EqualsASCII("text/html")) {
         *bRichText = true;
@@ -101,11 +96,10 @@
   CXFA_Node* pNode = pItemNode->GetFirstChild();
   while (pNode) {
     WideString wsName = pNode->JSObject()->GetCData(XFA_Attribute::Name);
-    if (m_eType == XFA_TEXTPROVIDERTYPE_Rollover &&
-        wsName.EqualsASCII("rollover")) {
+    if (m_eType == Type::kRollover && wsName.EqualsASCII("rollover")) {
       return pNode;
     }
-    if (m_eType == XFA_TEXTPROVIDERTYPE_Down && wsName.EqualsASCII("down"))
+    if (m_eType == Type::kDown && wsName.EqualsASCII("down"))
       return pNode;
 
     pNode = pNode->GetNextSibling();
@@ -114,7 +108,7 @@
 }
 
 CXFA_Para* CXFA_TextProvider::GetParaIfExists() {
-  if (m_eType == XFA_TEXTPROVIDERTYPE_Text)
+  if (m_eType == Type::kText)
     return m_pNode->GetParaIfExists();
 
   CXFA_Caption* pNode =
@@ -123,7 +117,7 @@
 }
 
 CXFA_Font* CXFA_TextProvider::GetFontIfExists() {
-  if (m_eType == XFA_TEXTPROVIDERTYPE_Text)
+  if (m_eType == Type::kText)
     return m_pNode->GetFontIfExists();
 
   CXFA_Caption* pNode =
@@ -135,13 +129,13 @@
 bool CXFA_TextProvider::IsCheckButtonAndAutoWidth() const {
   if (m_pNode->GetFFWidgetType() != XFA_FFWidgetType::kCheckButton)
     return false;
-  return !m_pNode->TryWidth();
+  return !m_pNode->TryWidth().has_value();
 }
 
-Optional<WideString> CXFA_TextProvider::GetEmbeddedObj(
+absl::optional<WideString> CXFA_TextProvider::GetEmbeddedObj(
     const WideString& wsAttr) const {
-  if (m_eType != XFA_TEXTPROVIDERTYPE_Text)
-    return {};
+  if (m_eType != Type::kText)
+    return absl::nullopt;
 
   CXFA_Node* pParent = m_pNode->GetParent();
   CXFA_Document* pDocument = m_pNode->GetDocument();
@@ -155,7 +149,7 @@
         wsAttr.AsStringView());
   }
   if (!pIDNode || !pIDNode->IsWidgetReady())
-    return {};
+    return absl::nullopt;
 
-  return pIDNode->GetValue(XFA_VALUEPICTURE_Display);
+  return pIDNode->GetValue(XFA_ValuePicture::kDisplay);
 }
diff --git a/xfa/fxfa/cxfa_textprovider.h b/xfa/fxfa/cxfa_textprovider.h
index b4a9664..b24e9e9 100644
--- a/xfa/fxfa/cxfa_textprovider.h
+++ b/xfa/fxfa/cxfa_textprovider.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,39 +7,43 @@
 #ifndef XFA_FXFA_CXFA_TEXTPROVIDER_H_
 #define XFA_FXFA_CXFA_TEXTPROVIDER_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "third_party/base/optional.h"
+#include "core/fxcrt/widestring.h"
+#include "fxjs/gc/heap.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fxfa/cxfa_textlayout.h"
 
 class CXFA_Font;
 class CXFA_Node;
 class CXFA_Para;
 
-enum XFA_TEXTPROVIDERTYPE {
-  XFA_TEXTPROVIDERTYPE_Text,
-  XFA_TEXTPROVIDERTYPE_Datasets,
-  XFA_TEXTPROVIDERTYPE_Caption,
-  XFA_TEXTPROVIDERTYPE_Rollover,
-  XFA_TEXTPROVIDERTYPE_Down,
-};
-
-class CXFA_TextProvider {
+class CXFA_TextProvider : public cppgc::GarbageCollected<CXFA_TextProvider> {
  public:
-  CXFA_TextProvider(CXFA_Node* pNode, XFA_TEXTPROVIDERTYPE eType)
-      : m_pNode(pNode), m_eType(eType) {
-    ASSERT(m_pNode);
-  }
-  ~CXFA_TextProvider() {}
+  enum class Type : uint8_t {
+    kText,
+    kCaption,
+    kRollover,
+    kDown,
+  };
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_TextProvider();
+
+  void Trace(cppgc::Visitor* visitor) const;
 
   CXFA_Node* GetTextNode(bool* bRichText);
   CXFA_Para* GetParaIfExists();
   CXFA_Font* GetFontIfExists();
   bool IsCheckButtonAndAutoWidth() const;
-  Optional<WideString> GetEmbeddedObj(const WideString& wsAttr) const;
+  absl::optional<WideString> GetEmbeddedObj(const WideString& wsAttr) const;
 
  private:
-  CXFA_Node* m_pNode;  // Raw, this class owned by tree node.
-  XFA_TEXTPROVIDERTYPE m_eType;
+  CXFA_TextProvider(CXFA_Node* pNode, Type eType);
+
+  cppgc::Member<CXFA_Node> m_pNode;
+  const Type m_eType;
 };
 
 #endif  // XFA_FXFA_CXFA_TEXTPROVIDER_H_
diff --git a/xfa/fxfa/cxfa_texttabstopscontext.cpp b/xfa/fxfa/cxfa_texttabstopscontext.cpp
index b594f1c..9590a26 100644
--- a/xfa/fxfa/cxfa_texttabstopscontext.cpp
+++ b/xfa/fxfa/cxfa_texttabstopscontext.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,10 +8,9 @@
 
 #include <algorithm>
 
-CXFA_TextTabstopsContext::CXFA_TextTabstopsContext()
-    : m_iTabIndex(-1), m_bTabstops(false), m_fTabWidth(0), m_fLeft(0) {}
+CXFA_TextTabstopsContext::CXFA_TextTabstopsContext() = default;
 
-CXFA_TextTabstopsContext::~CXFA_TextTabstopsContext() {}
+CXFA_TextTabstopsContext::~CXFA_TextTabstopsContext() = default;
 
 void CXFA_TextTabstopsContext::Append(uint32_t dwAlign, float fTabstops) {
   XFA_TABSTOPS tabstop;
@@ -28,7 +27,7 @@
 
 void CXFA_TextTabstopsContext::Reset() {
   m_iTabIndex = -1;
-  m_bTabstops = false;
+  m_bHasTabstops = false;
   m_fTabWidth = 0;
   m_fLeft = 0;
 }
diff --git a/xfa/fxfa/cxfa_texttabstopscontext.h b/xfa/fxfa/cxfa_texttabstopscontext.h
index 9837e35..ae2f86c 100644
--- a/xfa/fxfa/cxfa_texttabstopscontext.h
+++ b/xfa/fxfa/cxfa_texttabstopscontext.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -29,10 +29,10 @@
   void RemoveAll();
   void Reset();
 
-  int32_t m_iTabIndex;
-  bool m_bTabstops;
-  float m_fTabWidth;
-  float m_fLeft;
+  int32_t m_iTabIndex = -1;
+  bool m_bHasTabstops = false;
+  float m_fTabWidth = 0.0f;
+  float m_fLeft = 0.0f;
   std::vector<XFA_TABSTOPS> m_tabstops;
 };
 
diff --git a/xfa/fxfa/fm2js/BUILD.gn b/xfa/fxfa/fm2js/BUILD.gn
deleted file mode 100644
index a6b2540..0000000
--- a/xfa/fxfa/fm2js/BUILD.gn
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("../../../pdfium.gni")
-import("../../../testing/test.gni")
-
-assert(pdf_enable_xfa)
-
-source_set("fm2js") {
-  sources = [
-    "cxfa_fmexpression.cpp",
-    "cxfa_fmexpression.h",
-    "cxfa_fmlexer.cpp",
-    "cxfa_fmlexer.h",
-    "cxfa_fmparser.cpp",
-    "cxfa_fmparser.h",
-    "cxfa_fmsimpleexpression.cpp",
-    "cxfa_fmsimpleexpression.h",
-    "cxfa_fmtojavascriptdepth.cpp",
-    "cxfa_fmtojavascriptdepth.h",
-  ]
-  deps = [ "../../../core/fxcrt" ]
-  configs += [
-    "../../../:pdfium_core_config",
-    "../../:xfa_warnings",
-  ]
-  visibility = [ "../../../*" ]
-}
-
-pdfium_unittest_source_set("unittests") {
-  sources = [
-    "cxfa_fmexpression_unittest.cpp",
-    "cxfa_fmlexer_unittest.cpp",
-    "cxfa_fmparser_unittest.cpp",
-    "cxfa_fmsimpleexpression_unittest.cpp",
-  ]
-  deps = [ ":fm2js" ]
-  pdfium_root_dir = "../../../"
-}
diff --git a/xfa/fxfa/fm2js/DEPS b/xfa/fxfa/fm2js/DEPS
deleted file mode 100644
index e19a721..0000000
--- a/xfa/fxfa/fm2js/DEPS
+++ /dev/null
@@ -1,6 +0,0 @@
-include_rules = [
-  '+third_party/icu',
-
-  # xfa/fwl should be standalone. https://crbug.com/pdfium/507
-  '-xfa/fwl',
-]
diff --git a/xfa/fxfa/fm2js/cxfa_fmexpression.cpp b/xfa/fxfa/fm2js/cxfa_fmexpression.cpp
deleted file mode 100644
index a87ecca..0000000
--- a/xfa/fxfa/fm2js/cxfa_fmexpression.cpp
+++ /dev/null
@@ -1,448 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/fm2js/cxfa_fmexpression.h"
-
-#include <utility>
-
-#include "core/fxcrt/cfx_widetextbuf.h"
-#include "xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h"
-#include "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h"
-
-namespace {
-
-const wchar_t kLessEqual[] = L" <= ";
-const wchar_t kGreaterEqual[] = L" >= ";
-const wchar_t kPlusEqual[] = L" += ";
-const wchar_t kMinusEqual[] = L" -= ";
-
-WideString IdentifierToName(WideStringView ident) {
-  if (ident.IsEmpty())
-    return WideString();
-  if (ident[0] != L'!')
-    return WideString(ident);
-  return L"pfm__excl__" + ident.Last(ident.GetLength() - 1);
-}
-
-}  // namespace
-
-CXFA_FMExpression::CXFA_FMExpression() = default;
-
-CXFA_FMFunctionDefinition::CXFA_FMFunctionDefinition(
-    WideStringView wsName,
-    std::vector<WideStringView>&& arguments,
-    std::vector<std::unique_ptr<CXFA_FMExpression>>&& expressions)
-    : CXFA_FMExpression(),
-      m_wsName(wsName),
-      m_pArguments(std::move(arguments)),
-      m_pExpressions(std::move(expressions)) {
-  ASSERT(!wsName.IsEmpty());
-}
-
-CXFA_FMFunctionDefinition::~CXFA_FMFunctionDefinition() = default;
-
-bool CXFA_FMFunctionDefinition::ToJavaScript(CFX_WideTextBuf* js,
-                                             ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  if (m_wsName.IsEmpty())
-    return false;
-
-  *js << "function " << IdentifierToName(m_wsName) << "(";
-  for (const auto& identifier : m_pArguments) {
-    if (identifier != m_pArguments.front())
-      *js << ", ";
-
-    *js << IdentifierToName(identifier);
-  }
-  *js << ") {\n";
-
-  *js << "var pfm_ret = null;\n";
-  for (const auto& expr : m_pExpressions) {
-    ReturnType ret_type = expr == m_pExpressions.back() ? ReturnType::kImplied
-                                                        : ReturnType::kInfered;
-    if (!expr->ToJavaScript(js, ret_type))
-      return false;
-  }
-
-  *js << "return pfm_ret;\n";
-  *js << "}\n";
-
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMAST::CXFA_FMAST(
-    std::vector<std::unique_ptr<CXFA_FMExpression>> expressions)
-    : expressions_(std::move(expressions)) {}
-
-CXFA_FMAST::~CXFA_FMAST() = default;
-
-bool CXFA_FMAST::ToJavaScript(CFX_WideTextBuf* js) {
-  if (expressions_.empty()) {
-    *js << "// comments only";
-    return !CXFA_IsTooBig(js);
-  }
-
-  *js << "(function() {\n";
-  *js << "let pfm_method_runner = function(obj, cb) {\n";
-  *js << "  if (pfm_rt.is_ary(obj)) {\n";
-  *js << "    let pfm_method_return = null;\n";
-  *js << "    for (var idx = obj.length -1; idx > 1; idx--) {\n";
-  *js << "      pfm_method_return = cb(obj[idx]);\n";
-  *js << "    }\n";
-  *js << "    return pfm_method_return;\n";
-  *js << "  }\n";
-  *js << "  return cb(obj);\n";
-  *js << "};\n";
-  *js << "var pfm_ret = null;\n";
-
-  for (const auto& expr : expressions_) {
-    ReturnType ret_type = expr == expressions_.back() ? ReturnType::kImplied
-                                                      : ReturnType::kInfered;
-    if (!expr->ToJavaScript(js, ret_type))
-      return false;
-  }
-
-  *js << "return pfm_rt.get_val(pfm_ret);\n";
-  *js << "}).call(this);";
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMVarExpression::CXFA_FMVarExpression(
-    WideStringView wsName,
-    std::unique_ptr<CXFA_FMSimpleExpression> pInit)
-    : CXFA_FMExpression(), m_wsName(wsName), m_pInit(std::move(pInit)) {}
-
-CXFA_FMVarExpression::~CXFA_FMVarExpression() = default;
-
-bool CXFA_FMVarExpression::ToJavaScript(CFX_WideTextBuf* js, ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  WideString tempName = IdentifierToName(m_wsName);
-  *js << "var " << tempName << " = ";
-  if (m_pInit) {
-    if (!m_pInit->ToJavaScript(js, ReturnType::kInfered))
-      return false;
-
-    *js << ";\n";
-    *js << tempName << " = pfm_rt.var_filter(" << tempName << ");\n";
-  } else {
-    *js << "\"\";\n";
-  }
-
-  if (type == ReturnType::kImplied)
-    *js << "pfm_ret = " << tempName << ";\n";
-
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMExpExpression::CXFA_FMExpExpression(
-    std::unique_ptr<CXFA_FMSimpleExpression> pExpression)
-    : CXFA_FMExpression(), m_pExpression(std::move(pExpression)) {}
-
-CXFA_FMExpExpression::~CXFA_FMExpExpression() = default;
-
-bool CXFA_FMExpExpression::ToJavaScript(CFX_WideTextBuf* js, ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  if (type == ReturnType::kInfered) {
-    bool ret = m_pExpression->ToJavaScript(js, ReturnType::kInfered);
-    if (m_pExpression->GetOperatorToken() != TOKassign)
-      *js << ";\n";
-
-    return ret;
-  }
-
-  if (m_pExpression->GetOperatorToken() == TOKassign)
-    return m_pExpression->ToJavaScript(js, ReturnType::kImplied);
-
-  if (m_pExpression->GetOperatorToken() == TOKstar ||
-      m_pExpression->GetOperatorToken() == TOKdotstar ||
-      m_pExpression->GetOperatorToken() == TOKdotscream ||
-      m_pExpression->GetOperatorToken() == TOKdotdot ||
-      m_pExpression->GetOperatorToken() == TOKdot) {
-    *js << "pfm_ret = pfm_rt.get_val(";
-    if (!m_pExpression->ToJavaScript(js, ReturnType::kInfered))
-      return false;
-
-    *js << ");\n";
-    return !CXFA_IsTooBig(js);
-  }
-
-  *js << "pfm_ret = ";
-  if (!m_pExpression->ToJavaScript(js, ReturnType::kInfered))
-    return false;
-
-  *js << ";\n";
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMBlockExpression::CXFA_FMBlockExpression(
-    std::vector<std::unique_ptr<CXFA_FMExpression>>&& pExpressionList)
-    : CXFA_FMExpression(), m_ExpressionList(std::move(pExpressionList)) {}
-
-CXFA_FMBlockExpression::~CXFA_FMBlockExpression() = default;
-
-bool CXFA_FMBlockExpression::ToJavaScript(CFX_WideTextBuf* js,
-                                          ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  *js << "{\n";
-  for (const auto& expr : m_ExpressionList) {
-    if (type == ReturnType::kInfered) {
-      if (!expr->ToJavaScript(js, ReturnType::kInfered))
-        return false;
-    } else {
-      ReturnType ret_type = expr == m_ExpressionList.back()
-                                ? ReturnType::kImplied
-                                : ReturnType::kInfered;
-      if (!expr->ToJavaScript(js, ret_type))
-        return false;
-    }
-  }
-  *js << "}\n";
-
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMDoExpression::CXFA_FMDoExpression(
-    std::unique_ptr<CXFA_FMExpression> pList)
-    : CXFA_FMExpression(), m_pList(std::move(pList)) {}
-
-CXFA_FMDoExpression::~CXFA_FMDoExpression() = default;
-
-bool CXFA_FMDoExpression::ToJavaScript(CFX_WideTextBuf* js, ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  return m_pList->ToJavaScript(js, type);
-}
-
-CXFA_FMIfExpression::CXFA_FMIfExpression(
-    std::unique_ptr<CXFA_FMSimpleExpression> pExpression,
-    std::unique_ptr<CXFA_FMExpression> pIfExpression,
-    std::vector<std::unique_ptr<CXFA_FMIfExpression>> pElseIfExpressions,
-    std::unique_ptr<CXFA_FMExpression> pElseExpression)
-    : CXFA_FMExpression(),
-      m_pExpression(std::move(pExpression)),
-      m_pIfExpression(std::move(pIfExpression)),
-      m_pElseIfExpressions(std::move(pElseIfExpressions)),
-      m_pElseExpression(std::move(pElseExpression)) {
-  ASSERT(m_pExpression);
-}
-
-CXFA_FMIfExpression::~CXFA_FMIfExpression() = default;
-
-bool CXFA_FMIfExpression::ToJavaScript(CFX_WideTextBuf* js, ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  if (type == ReturnType::kImplied)
-    *js << "pfm_ret = 0;\n";
-
-  *js << "if (pfm_rt.get_val(";
-  if (!m_pExpression->ToJavaScript(js, ReturnType::kInfered))
-    return false;
-  *js << "))\n";
-
-  if (CXFA_IsTooBig(js))
-    return false;
-
-  if (m_pIfExpression) {
-    if (!m_pIfExpression->ToJavaScript(js, type))
-      return false;
-    if (CXFA_IsTooBig(js))
-      return false;
-  }
-
-  for (auto& expr : m_pElseIfExpressions) {
-    *js << "else ";
-    if (!expr->ToJavaScript(js, ReturnType::kInfered))
-      return false;
-  }
-
-  if (m_pElseExpression) {
-    *js << "else ";
-    if (!m_pElseExpression->ToJavaScript(js, type))
-      return false;
-  }
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMWhileExpression::CXFA_FMWhileExpression(
-    std::unique_ptr<CXFA_FMSimpleExpression> pCondition,
-    std::unique_ptr<CXFA_FMExpression> pExpression)
-    : CXFA_FMExpression(),
-      m_pCondition(std::move(pCondition)),
-      m_pExpression(std::move(pExpression)) {}
-
-CXFA_FMWhileExpression::~CXFA_FMWhileExpression() = default;
-
-bool CXFA_FMWhileExpression::ToJavaScript(CFX_WideTextBuf* js,
-                                          ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  if (type == ReturnType::kImplied)
-    *js << "pfm_ret = 0;\n";
-
-  *js << "while (";
-  if (!m_pCondition->ToJavaScript(js, ReturnType::kInfered))
-    return false;
-
-  *js << ")\n";
-  if (CXFA_IsTooBig(js))
-    return false;
-
-  if (!m_pExpression->ToJavaScript(js, type))
-    return false;
-
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMBreakExpression::CXFA_FMBreakExpression() : CXFA_FMExpression() {}
-
-CXFA_FMBreakExpression::~CXFA_FMBreakExpression() = default;
-
-bool CXFA_FMBreakExpression::ToJavaScript(CFX_WideTextBuf* js,
-                                          ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  *js << "pfm_ret = 0;\nbreak;\n";
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMContinueExpression::CXFA_FMContinueExpression() : CXFA_FMExpression() {}
-
-CXFA_FMContinueExpression::~CXFA_FMContinueExpression() = default;
-
-bool CXFA_FMContinueExpression::ToJavaScript(CFX_WideTextBuf* js,
-                                             ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  *js << "pfm_ret = 0;\ncontinue;\n";
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMForExpression::CXFA_FMForExpression(
-    WideStringView wsVariant,
-    std::unique_ptr<CXFA_FMSimpleExpression> pAssignment,
-    std::unique_ptr<CXFA_FMSimpleExpression> pAccessor,
-    int32_t iDirection,
-    std::unique_ptr<CXFA_FMSimpleExpression> pStep,
-    std::unique_ptr<CXFA_FMExpression> pList)
-    : CXFA_FMExpression(),
-      m_wsVariant(wsVariant),
-      m_pAssignment(std::move(pAssignment)),
-      m_pAccessor(std::move(pAccessor)),
-      m_bDirection(iDirection == 1),
-      m_pStep(std::move(pStep)),
-      m_pList(std::move(pList)) {}
-
-CXFA_FMForExpression::~CXFA_FMForExpression() = default;
-
-bool CXFA_FMForExpression::ToJavaScript(CFX_WideTextBuf* js, ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  if (type == ReturnType::kImplied)
-    *js << "pfm_ret = 0;\n";
-
-  *js << "{\n";
-
-  WideString tmpName = IdentifierToName(m_wsVariant);
-  *js << "var " << tmpName << " = null;\n";
-
-  *js << "for (" << tmpName << " = pfm_rt.get_val(";
-  if (!m_pAssignment->ToJavaScript(js, ReturnType::kInfered))
-    return false;
-  *js << "); ";
-
-  *js << tmpName << (m_bDirection ? kLessEqual : kGreaterEqual);
-  *js << "pfm_rt.get_val(";
-  if (!m_pAccessor->ToJavaScript(js, ReturnType::kInfered))
-    return false;
-  *js << "); ";
-
-  *js << tmpName << (m_bDirection ? kPlusEqual : kMinusEqual);
-  if (m_pStep) {
-    *js << "pfm_rt.get_val(";
-    if (!m_pStep->ToJavaScript(js, ReturnType::kInfered))
-      return false;
-    *js << ")";
-  } else {
-    *js << "1";
-  }
-  *js << ")\n";
-  if (CXFA_IsTooBig(js))
-    return false;
-
-  if (!m_pList->ToJavaScript(js, type))
-    return false;
-
-  *js << "}\n";
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMForeachExpression::CXFA_FMForeachExpression(
-    WideStringView wsIdentifier,
-    std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>&& pAccessors,
-    std::unique_ptr<CXFA_FMExpression> pList)
-    : CXFA_FMExpression(),
-      m_wsIdentifier(wsIdentifier),
-      m_pAccessors(std::move(pAccessors)),
-      m_pList(std::move(pList)) {}
-
-CXFA_FMForeachExpression::~CXFA_FMForeachExpression() = default;
-
-bool CXFA_FMForeachExpression::ToJavaScript(CFX_WideTextBuf* js,
-                                            ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  if (type == ReturnType::kImplied)
-    *js << "pfm_ret = 0;\n";
-
-  *js << "{\n";
-
-  WideString tmpName = IdentifierToName(m_wsIdentifier);
-  *js << "var " << tmpName << " = null;\n";
-  *js << "var pfm_ary = pfm_rt.concat_obj(";
-  for (const auto& expr : m_pAccessors) {
-    if (!expr->ToJavaScript(js, ReturnType::kInfered))
-      return false;
-    if (expr != m_pAccessors.back())
-      *js << ", ";
-  }
-  *js << ");\n";
-
-  *js << "var pfm_ary_idx = 0;\n";
-  *js << "while(pfm_ary_idx < pfm_ary.length)\n{\n";
-  *js << tmpName << " = pfm_ary[pfm_ary_idx++];\n";
-  if (!m_pList->ToJavaScript(js, type))
-    return false;
-  *js << "}\n";  // while
-
-  *js << "}\n";  // block
-  return !CXFA_IsTooBig(js);
-}
diff --git a/xfa/fxfa/fm2js/cxfa_fmexpression.h b/xfa/fxfa/fm2js/cxfa_fmexpression.h
deleted file mode 100644
index 2761ef5..0000000
--- a/xfa/fxfa/fm2js/cxfa_fmexpression.h
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_FM2JS_CXFA_FMEXPRESSION_H_
-#define XFA_FXFA_FM2JS_CXFA_FMEXPRESSION_H_
-
-#include <memory>
-#include <vector>
-
-#include "xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h"
-
-class CFX_WideTextBuf;
-
-class CXFA_FMExpression {
- public:
-  virtual ~CXFA_FMExpression() = default;
-  virtual bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) = 0;
-
- protected:
-  CXFA_FMExpression();
-};
-
-class CXFA_FMFunctionDefinition final : public CXFA_FMExpression {
- public:
-  CXFA_FMFunctionDefinition(
-      WideStringView wsName,
-      std::vector<WideStringView>&& arguments,
-      std::vector<std::unique_ptr<CXFA_FMExpression>>&& expressions);
-  ~CXFA_FMFunctionDefinition() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  WideStringView m_wsName;
-  std::vector<WideStringView> m_pArguments;
-  std::vector<std::unique_ptr<CXFA_FMExpression>> m_pExpressions;
-};
-
-class CXFA_FMAST {
- public:
-  explicit CXFA_FMAST(
-      std::vector<std::unique_ptr<CXFA_FMExpression>> expressions);
-  ~CXFA_FMAST();
-
-  bool ToJavaScript(CFX_WideTextBuf* js);
-
- private:
-  std::vector<std::unique_ptr<CXFA_FMExpression>> expressions_;
-};
-
-class CXFA_FMVarExpression final : public CXFA_FMExpression {
- public:
-  CXFA_FMVarExpression(WideStringView wsName,
-                       std::unique_ptr<CXFA_FMSimpleExpression> pInit);
-  ~CXFA_FMVarExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  WideStringView m_wsName;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pInit;
-};
-
-class CXFA_FMExpExpression final : public CXFA_FMExpression {
- public:
-  explicit CXFA_FMExpExpression(
-      std::unique_ptr<CXFA_FMSimpleExpression> pExpression);
-  ~CXFA_FMExpExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExpression;
-};
-
-class CXFA_FMBlockExpression final : public CXFA_FMExpression {
- public:
-  CXFA_FMBlockExpression(
-      std::vector<std::unique_ptr<CXFA_FMExpression>>&& pExpressionList);
-  ~CXFA_FMBlockExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  std::vector<std::unique_ptr<CXFA_FMExpression>> m_ExpressionList;
-};
-
-class CXFA_FMDoExpression final : public CXFA_FMExpression {
- public:
-  explicit CXFA_FMDoExpression(std::unique_ptr<CXFA_FMExpression> pList);
-  ~CXFA_FMDoExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  std::unique_ptr<CXFA_FMExpression> m_pList;
-};
-
-class CXFA_FMIfExpression final : public CXFA_FMExpression {
- public:
-  CXFA_FMIfExpression(
-      std::unique_ptr<CXFA_FMSimpleExpression> pExpression,
-      std::unique_ptr<CXFA_FMExpression> pIfExpression,
-      std::vector<std::unique_ptr<CXFA_FMIfExpression>> pElseIfExpressions,
-      std::unique_ptr<CXFA_FMExpression> pElseExpression);
-  ~CXFA_FMIfExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExpression;
-  std::unique_ptr<CXFA_FMExpression> m_pIfExpression;
-  std::vector<std::unique_ptr<CXFA_FMIfExpression>> m_pElseIfExpressions;
-  std::unique_ptr<CXFA_FMExpression> m_pElseExpression;
-};
-
-class CXFA_FMWhileExpression final : public CXFA_FMExpression {
- public:
-  CXFA_FMWhileExpression(std::unique_ptr<CXFA_FMSimpleExpression> pCodition,
-                         std::unique_ptr<CXFA_FMExpression> pExpression);
-  ~CXFA_FMWhileExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pCondition;
-  std::unique_ptr<CXFA_FMExpression> m_pExpression;
-};
-
-class CXFA_FMBreakExpression final : public CXFA_FMExpression {
- public:
-  CXFA_FMBreakExpression();
-  ~CXFA_FMBreakExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-};
-
-class CXFA_FMContinueExpression final : public CXFA_FMExpression {
- public:
-  CXFA_FMContinueExpression();
-  ~CXFA_FMContinueExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-};
-
-class CXFA_FMForExpression final : public CXFA_FMExpression {
- public:
-  CXFA_FMForExpression(WideStringView wsVariant,
-                       std::unique_ptr<CXFA_FMSimpleExpression> pAssignment,
-                       std::unique_ptr<CXFA_FMSimpleExpression> pAccessor,
-                       int32_t iDirection,
-                       std::unique_ptr<CXFA_FMSimpleExpression> pStep,
-                       std::unique_ptr<CXFA_FMExpression> pList);
-  ~CXFA_FMForExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  WideStringView m_wsVariant;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pAssignment;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pAccessor;
-  const bool m_bDirection;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pStep;
-  std::unique_ptr<CXFA_FMExpression> m_pList;
-};
-
-class CXFA_FMForeachExpression final : public CXFA_FMExpression {
- public:
-  // Takes ownership of |pAccessors|.
-  CXFA_FMForeachExpression(
-      WideStringView wsIdentifier,
-      std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>&& pAccessors,
-      std::unique_ptr<CXFA_FMExpression> pList);
-  ~CXFA_FMForeachExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  WideStringView m_wsIdentifier;
-  std::vector<std::unique_ptr<CXFA_FMSimpleExpression>> m_pAccessors;
-  std::unique_ptr<CXFA_FMExpression> m_pList;
-};
-
-#endif  // XFA_FXFA_FM2JS_CXFA_FMEXPRESSION_H_
diff --git a/xfa/fxfa/fm2js/cxfa_fmexpression_unittest.cpp b/xfa/fxfa/fm2js/cxfa_fmexpression_unittest.cpp
deleted file mode 100644
index 9d1a64c..0000000
--- a/xfa/fxfa/fm2js/cxfa_fmexpression_unittest.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "xfa/fxfa/fm2js/cxfa_fmexpression.h"
-
-#include <memory>
-#include <utility>
-
-#include "core/fxcrt/cfx_widetextbuf.h"
-#include "core/fxcrt/fx_string.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h"
-
-TEST(CXFA_FMExpressionTest, VarExpressionInitNull) {
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf accumulator;
-
-  CXFA_FMVarExpression(L"s", nullptr)
-      .ToJavaScript(&accumulator, ReturnType::kInfered);
-  EXPECT_STREQ(
-      LR"***(var s = "";
-)***",
-      accumulator.MakeString().c_str());
-}
-
-TEST(CXFA_FMExpressionTest, VarExpressionInitBlank) {
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf accumulator;
-
-  auto init = pdfium::MakeUnique<CXFA_FMStringExpression>(LR"("")");
-  CXFA_FMVarExpression(L"s", std::move(init))
-      .ToJavaScript(&accumulator, ReturnType::kInfered);
-  EXPECT_STREQ(
-      LR"***(var s = "";
-s = pfm_rt.var_filter(s);
-)***",
-      accumulator.MakeString().c_str());
-}
-
-TEST(CXFA_FMExpressionTest, VarExpressionInitString) {
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf accumulator;
-
-  auto init = pdfium::MakeUnique<CXFA_FMStringExpression>(LR"("foo")");
-  CXFA_FMVarExpression(L"s", std::move(init))
-      .ToJavaScript(&accumulator, ReturnType::kInfered);
-  EXPECT_STREQ(
-      LR"***(var s = "foo";
-s = pfm_rt.var_filter(s);
-)***",
-      accumulator.MakeString().c_str());
-}
-
-TEST(CXFA_FMExpressionTest, VarExpressionInitNumeric) {
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf accumulator;
-
-  auto init = pdfium::MakeUnique<CXFA_FMNumberExpression>(L"112");
-  CXFA_FMVarExpression(L"s", std::move(init))
-      .ToJavaScript(&accumulator, ReturnType::kInfered);
-  EXPECT_STREQ(
-      LR"***(var s = 112;
-s = pfm_rt.var_filter(s);
-)***",
-      accumulator.MakeString().c_str());
-}
diff --git a/xfa/fxfa/fm2js/cxfa_fmlexer.cpp b/xfa/fxfa/fm2js/cxfa_fmlexer.cpp
deleted file mode 100644
index efb59f2..0000000
--- a/xfa/fxfa/fm2js/cxfa_fmlexer.cpp
+++ /dev/null
@@ -1,396 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/fm2js/cxfa_fmlexer.h"
-
-#include <algorithm>
-
-#include "core/fxcrt/fx_extension.h"
-
-namespace {
-
-bool IsFormCalcCharacter(wchar_t c) {
-  return (c >= 0x09 && c <= 0x0D) || (c >= 0x20 && c <= 0xd7FF) ||
-         (c >= 0xE000 && c <= 0xFFFD);
-}
-
-bool IsIdentifierCharacter(wchar_t c) {
-  return FXSYS_iswalnum(c) || c == 0x005F ||  // '_'
-         c == 0x0024;                         // '$'
-}
-
-bool IsInitialIdentifierCharacter(wchar_t c) {
-  return FXSYS_iswalpha(c) || c == 0x005F ||  // '_'
-         c == 0x0024 ||                       // '$'
-         c == 0x0021;                         // '!'
-}
-
-bool IsWhitespaceCharacter(wchar_t c) {
-  return c == 0x0009 ||  // Horizontal tab
-         c == 0x000B ||  // Vertical tab
-         c == 0x000C ||  // Form feed
-         c == 0x0020;    // Space
-}
-
-const XFA_FMKeyword keyWords[] = {
-    {TOKdo, "do"},
-    {TOKkseq, "eq"},
-    {TOKksge, "ge"},
-    {TOKksgt, "gt"},
-    {TOKif, "if"},
-    {TOKin, "in"},
-    {TOKksle, "le"},
-    {TOKkslt, "lt"},
-    {TOKksne, "ne"},
-    {TOKksor, "or"},
-    {TOKnull, "null"},
-    {TOKbreak, "break"},
-    {TOKksand, "and"},
-    {TOKend, "end"},
-    {TOKeof, "eof"},
-    {TOKfor, "for"},
-    {TOKnan, "nan"},
-    {TOKksnot, "not"},
-    {TOKvar, "var"},
-    {TOKthen, "then"},
-    {TOKelse, "else"},
-    {TOKexit, "exit"},
-    {TOKdownto, "downto"},
-    {TOKreturn, "return"},
-    {TOKinfinity, "infinity"},
-    {TOKendwhile, "endwhile"},
-    {TOKforeach, "foreach"},
-    {TOKendfunc, "endfunc"},
-    {TOKelseif, "elseif"},
-    {TOKwhile, "while"},
-    {TOKendfor, "endfor"},
-    {TOKthrow, "throw"},
-    {TOKstep, "step"},
-    {TOKupto, "upto"},
-    {TOKcontinue, "continue"},
-    {TOKfunc, "func"},
-    {TOKendif, "endif"},
-};
-
-#ifndef NDEBUG
-const char* const tokenStrings[] = {
-    "TOKand",        "TOKlparen",     "TOKrparen",   "TOKmul",
-    "TOKplus",       "TOKcomma",      "TOKminus",    "TOKdot",
-    "TOKdiv",        "TOKlt",         "TOKassign",   "TOKgt",
-    "TOKlbracket",   "TOKrbracket",   "TOKor",       "TOKdotscream",
-    "TOKdotstar",    "TOKdotdot",     "TOKle",       "TOKne",
-    "TOKeq",         "TOKge",         "TOKdo",       "TOKkseq",
-    "TOKksge",       "TOKksgt",       "TOKif",       "TOKin",
-    "TOKksle",       "TOKkslt",       "TOKksne",     "TOKksor",
-    "TOKnull",       "TOKbreak",      "TOKksand",    "TOKend",
-    "TOKeof",        "TOKfor",        "TOKnan",      "TOKksnot",
-    "TOKvar",        "TOKthen",       "TOKelse",     "TOKexit",
-    "TOKdownto",     "TOKreturn",     "TOKinfinity", "TOKendwhile",
-    "TOKforeach",    "TOKendfunc",    "TOKelseif",   "TOKwhile",
-    "TOKendfor",     "TOKthrow",      "TOKstep",     "TOKupto",
-    "TOKcontinue",   "TOKfunc",       "TOKendif",    "TOKstar",
-    "TOKidentifier", "TOKunderscore", "TOKdollar",   "TOKexclamation",
-    "TOKcall",       "TOKstring",     "TOKnumber",   "TOKreserver",
-};
-#endif  // NDEBUG
-
-XFA_FM_TOKEN TokenizeIdentifier(WideStringView str) {
-  const XFA_FMKeyword* result =
-      std::find_if(std::begin(keyWords), std::end(keyWords),
-                   [str](const XFA_FMKeyword& iter) {
-                     return str.EqualsASCII(iter.m_keyword);
-                   });
-  if (result != std::end(keyWords) && str.EqualsASCII(result->m_keyword))
-    return result->m_type;
-  return TOKidentifier;
-}
-
-}  // namespace
-
-CXFA_FMToken::CXFA_FMToken(XFA_FM_TOKEN token) : m_type(token) {}
-
-CXFA_FMToken::CXFA_FMToken() : CXFA_FMToken(TOKreserver) {}
-
-CXFA_FMToken::CXFA_FMToken(const CXFA_FMToken&) = default;
-
-CXFA_FMToken::~CXFA_FMToken() = default;
-
-#ifndef NDEBUG
-WideString CXFA_FMToken::ToDebugString() const {
-  WideString str = WideString::FromASCII("type = ");
-  str += WideString::FromASCII(tokenStrings[m_type]);
-  str += WideString::FromASCII(", string = ");
-  str += m_string;
-  return str;
-}
-#endif  // NDEBUG
-
-CXFA_FMLexer::CXFA_FMLexer(WideStringView wsFormCalc)
-    : m_spInput(wsFormCalc.span()) {}
-
-CXFA_FMLexer::~CXFA_FMLexer() = default;
-
-CXFA_FMToken CXFA_FMLexer::NextToken() {
-  if (m_bLexerError)
-    return CXFA_FMToken();
-
-  while (!IsComplete() && m_spInput[m_nCursor]) {
-    if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
-      RaiseError();
-      return CXFA_FMToken();
-    }
-
-    switch (m_spInput[m_nCursor]) {
-      case '\n':
-        ++m_nCursor;
-        break;
-      case '\r':
-        ++m_nCursor;
-        break;
-      case ';':
-        AdvanceForComment();
-        break;
-      case '"':
-        return AdvanceForString();
-      case '0':
-      case '1':
-      case '2':
-      case '3':
-      case '4':
-      case '5':
-      case '6':
-      case '7':
-      case '8':
-      case '9':
-        return AdvanceForNumber();
-      case '=':
-        ++m_nCursor;
-        if (m_nCursor >= m_spInput.size())
-          return CXFA_FMToken(TOKassign);
-
-        if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
-          RaiseError();
-          return CXFA_FMToken();
-        }
-        if (m_spInput[m_nCursor] == '=') {
-          ++m_nCursor;
-          return CXFA_FMToken(TOKeq);
-        }
-        return CXFA_FMToken(TOKassign);
-      case '<':
-        ++m_nCursor;
-        if (m_nCursor >= m_spInput.size())
-          return CXFA_FMToken(TOKlt);
-
-        if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
-          RaiseError();
-          return CXFA_FMToken();
-        }
-        if (m_spInput[m_nCursor] == '=') {
-          ++m_nCursor;
-          return CXFA_FMToken(TOKle);
-        }
-        if (m_spInput[m_nCursor] == '>') {
-          ++m_nCursor;
-          return CXFA_FMToken(TOKne);
-        }
-        return CXFA_FMToken(TOKlt);
-      case '>':
-        ++m_nCursor;
-        if (m_nCursor >= m_spInput.size())
-          return CXFA_FMToken(TOKgt);
-
-        if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
-          RaiseError();
-          return CXFA_FMToken();
-        }
-        if (m_spInput[m_nCursor] == '=') {
-          ++m_nCursor;
-          return CXFA_FMToken(TOKge);
-        }
-        return CXFA_FMToken(TOKgt);
-      case ',':
-        ++m_nCursor;
-        return CXFA_FMToken(TOKcomma);
-      case '(':
-        ++m_nCursor;
-        return CXFA_FMToken(TOKlparen);
-      case ')':
-        ++m_nCursor;
-        return CXFA_FMToken(TOKrparen);
-      case '[':
-        ++m_nCursor;
-        return CXFA_FMToken(TOKlbracket);
-      case ']':
-        ++m_nCursor;
-        return CXFA_FMToken(TOKrbracket);
-      case '&':
-        ++m_nCursor;
-        return CXFA_FMToken(TOKand);
-      case '|':
-        ++m_nCursor;
-        return CXFA_FMToken(TOKor);
-      case '+':
-        ++m_nCursor;
-        return CXFA_FMToken(TOKplus);
-      case '-':
-        ++m_nCursor;
-        return CXFA_FMToken(TOKminus);
-      case '*':
-        ++m_nCursor;
-        return CXFA_FMToken(TOKmul);
-      case '/': {
-        ++m_nCursor;
-        if (m_nCursor >= m_spInput.size())
-          return CXFA_FMToken(TOKdiv);
-
-        if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
-          RaiseError();
-          return CXFA_FMToken();
-        }
-        if (m_spInput[m_nCursor] != '/')
-          return CXFA_FMToken(TOKdiv);
-
-        AdvanceForComment();
-        break;
-      }
-      case '.':
-        ++m_nCursor;
-        if (m_nCursor >= m_spInput.size())
-          return CXFA_FMToken(TOKdot);
-
-        if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
-          RaiseError();
-          return CXFA_FMToken();
-        }
-
-        if (m_spInput[m_nCursor] == '.') {
-          ++m_nCursor;
-          return CXFA_FMToken(TOKdotdot);
-        }
-        if (m_spInput[m_nCursor] == '*') {
-          ++m_nCursor;
-          return CXFA_FMToken(TOKdotstar);
-        }
-        if (m_spInput[m_nCursor] == '#') {
-          ++m_nCursor;
-          return CXFA_FMToken(TOKdotscream);
-        }
-        if (FXSYS_IsDecimalDigit(m_spInput[m_nCursor])) {
-          --m_nCursor;
-          return AdvanceForNumber();
-        }
-        return CXFA_FMToken(TOKdot);
-      default:
-        if (IsWhitespaceCharacter(m_spInput[m_nCursor])) {
-          ++m_nCursor;
-          break;
-        }
-        if (!IsInitialIdentifierCharacter(m_spInput[m_nCursor])) {
-          RaiseError();
-          return CXFA_FMToken();
-        }
-        return AdvanceForIdentifier();
-    }
-  }
-  return CXFA_FMToken(TOKeof);
-}
-
-CXFA_FMToken CXFA_FMLexer::AdvanceForNumber() {
-  // This will set end to the character after the end of the number.
-  int32_t used_length = 0;
-  if (m_nCursor < m_spInput.size()) {
-    FXSYS_wcstof(&m_spInput[m_nCursor], m_spInput.size() - m_nCursor,
-                 &used_length);
-  }
-  size_t end = m_nCursor + used_length;
-  if (used_length == 0 ||
-      (end < m_spInput.size() && FXSYS_iswalpha(m_spInput[end]))) {
-    RaiseError();
-    return CXFA_FMToken();
-  }
-  CXFA_FMToken token(TOKnumber);
-  token.m_string =
-      WideStringView(m_spInput.subspan(m_nCursor, end - m_nCursor));
-  m_nCursor = end;
-  return token;
-}
-
-CXFA_FMToken CXFA_FMLexer::AdvanceForString() {
-  CXFA_FMToken token(TOKstring);
-  size_t start = m_nCursor;
-  ++m_nCursor;
-  while (!IsComplete() && m_spInput[m_nCursor]) {
-    if (!IsFormCalcCharacter(m_spInput[m_nCursor]))
-      break;
-
-    if (m_spInput[m_nCursor] == '"') {
-      // Check for escaped "s, i.e. "".
-      ++m_nCursor;
-      // If the end of the input has been reached it was not escaped.
-      if (m_nCursor >= m_spInput.size()) {
-        token.m_string =
-            WideStringView(m_spInput.subspan(start, m_nCursor - start));
-        return token;
-      }
-      // If the next character is not a " then the end of the string has been
-      // found.
-      if (m_spInput[m_nCursor] != '"') {
-        if (!IsFormCalcCharacter(m_spInput[m_nCursor]))
-          break;
-
-        token.m_string =
-            WideStringView(m_spInput.subspan(start, m_nCursor - start));
-        return token;
-      }
-    }
-    ++m_nCursor;
-  }
-
-  // Didn't find the end of the string.
-  RaiseError();
-  return CXFA_FMToken();
-}
-
-CXFA_FMToken CXFA_FMLexer::AdvanceForIdentifier() {
-  size_t start = m_nCursor;
-  ++m_nCursor;
-  while (!IsComplete() && m_spInput[m_nCursor]) {
-    if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
-      RaiseError();
-      return CXFA_FMToken();
-    }
-    if (!IsIdentifierCharacter(m_spInput[m_nCursor]))
-      break;
-
-    ++m_nCursor;
-  }
-
-  WideStringView str =
-      WideStringView(m_spInput.subspan(start, m_nCursor - start));
-  CXFA_FMToken token(TokenizeIdentifier(str));
-  token.m_string = str;
-  return token;
-}
-
-void CXFA_FMLexer::AdvanceForComment() {
-  ++m_nCursor;
-  while (!IsComplete() && m_spInput[m_nCursor]) {
-    if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
-      RaiseError();
-      return;
-    }
-    if (m_spInput[m_nCursor] == L'\r') {
-      ++m_nCursor;
-      return;
-    }
-    if (m_spInput[m_nCursor] == L'\n') {
-      ++m_nCursor;
-      return;
-    }
-    ++m_nCursor;
-  }
-}
diff --git a/xfa/fxfa/fm2js/cxfa_fmlexer.h b/xfa/fxfa/fm2js/cxfa_fmlexer.h
deleted file mode 100644
index 2d9e5fc..0000000
--- a/xfa/fxfa/fm2js/cxfa_fmlexer.h
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_FM2JS_CXFA_FMLEXER_H_
-#define XFA_FXFA_FM2JS_CXFA_FMLEXER_H_
-
-#include "core/fxcrt/fx_string.h"
-
-enum XFA_FM_TOKEN {
-  TOKand,
-  TOKlparen,
-  TOKrparen,
-  TOKmul,
-  TOKplus,
-  TOKcomma,
-  TOKminus,
-  TOKdot,
-  TOKdiv,
-  TOKlt,
-  TOKassign,
-  TOKgt,
-  TOKlbracket,
-  TOKrbracket,
-  TOKor,
-  TOKdotscream,
-  TOKdotstar,
-  TOKdotdot,
-  TOKle,
-  TOKne,
-  TOKeq,
-  TOKge,
-  TOKdo,
-  TOKkseq,
-  TOKksge,
-  TOKksgt,
-  TOKif,
-  TOKin,
-  TOKksle,
-  TOKkslt,
-  TOKksne,
-  TOKksor,
-  TOKnull,
-  TOKbreak,
-  TOKksand,
-  TOKend,
-  TOKeof,
-  TOKfor,
-  TOKnan,
-  TOKksnot,
-  TOKvar,
-  TOKthen,
-  TOKelse,
-  TOKexit,
-  TOKdownto,
-  TOKreturn,
-  TOKinfinity,
-  TOKendwhile,
-  TOKforeach,
-  TOKendfunc,
-  TOKelseif,
-  TOKwhile,
-  TOKendfor,
-  TOKthrow,
-  TOKstep,
-  TOKupto,
-  TOKcontinue,
-  TOKfunc,
-  TOKendif,
-  TOKstar,
-  TOKidentifier,
-  TOKunderscore,
-  TOKdollar,
-  TOKexclamation,
-  TOKcall,
-  TOKstring,
-  TOKnumber,
-  TOKreserver
-};
-
-struct XFA_FMKeyword {
-  XFA_FM_TOKEN m_type;
-  const char* m_keyword;  // Raw, POD struct.
-};
-
-class CXFA_FMToken {
- public:
-  CXFA_FMToken();
-  explicit CXFA_FMToken(XFA_FM_TOKEN token);
-  CXFA_FMToken(const CXFA_FMToken&);
-  ~CXFA_FMToken();
-
-#ifndef NDEBUG
-  WideString ToDebugString() const;
-#endif  // NDEBUG
-
-  WideStringView m_string;
-  XFA_FM_TOKEN m_type;
-};
-
-class CXFA_FMLexer {
- public:
-  explicit CXFA_FMLexer(WideStringView wsFormcalc);
-  ~CXFA_FMLexer();
-
-  CXFA_FMToken NextToken();
-  bool IsComplete() const { return m_nCursor >= m_spInput.size(); }
-
- private:
-  CXFA_FMToken AdvanceForNumber();
-  CXFA_FMToken AdvanceForString();
-  CXFA_FMToken AdvanceForIdentifier();
-  void AdvanceForComment();
-
-  void RaiseError() { m_bLexerError = true; }
-
-  pdfium::span<const wchar_t> m_spInput;
-  size_t m_nCursor = 0;
-  bool m_bLexerError = false;
-};
-
-#endif  // XFA_FXFA_FM2JS_CXFA_FMLEXER_H_
diff --git a/xfa/fxfa/fm2js/cxfa_fmlexer_unittest.cpp b/xfa/fxfa/fm2js/cxfa_fmlexer_unittest.cpp
deleted file mode 100644
index 3627e15..0000000
--- a/xfa/fxfa/fm2js/cxfa_fmlexer_unittest.cpp
+++ /dev/null
@@ -1,292 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "xfa/fxfa/fm2js/cxfa_fmlexer.h"
-
-#include <vector>
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
-
-TEST(CXFA_FMLexerTest, NullString) {
-  WideStringView null_string;
-  CXFA_FMLexer lexer(null_string);
-  CXFA_FMToken token = lexer.NextToken();
-  EXPECT_EQ(TOKeof, token.m_type);
-  EXPECT_TRUE(lexer.IsComplete());
-}
-
-TEST(CXFA_FMLexerTest, EmptyString) {
-  CXFA_FMLexer lexer(L"");
-  CXFA_FMToken token = lexer.NextToken();
-  EXPECT_EQ(TOKeof, token.m_type);
-  EXPECT_TRUE(lexer.IsComplete());
-}
-
-TEST(CXFA_FMLexerTest, Numbers) {
-  auto lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"-12");
-  CXFA_FMToken token = lexer->NextToken();
-  // TODO(dsinclair): Should this return -12 instead of two tokens?
-  EXPECT_EQ(TOKminus, token.m_type);
-  token = lexer->NextToken();
-  EXPECT_EQ(L"12", token.m_string);
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token.m_type);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"1.5362");
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token.m_type);
-  EXPECT_EQ(L"1.5362", token.m_string);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"0.875");
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token.m_type);
-  EXPECT_EQ(L"0.875", token.m_string);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"5.56e-2");
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token.m_type);
-  EXPECT_EQ(L"5.56e-2", token.m_string);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"1.234E10");
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token.m_type);
-  EXPECT_EQ(L"1.234E10", token.m_string);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"123456789.012345678");
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token.m_type);
-  // TODO(dsinclair): This should round as per IEEE 64-bit values.
-  // EXPECT_EQ(L"123456789.01234567", token.m_string);
-  EXPECT_EQ(L"123456789.012345678", token.m_string);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"99999999999999999");
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token.m_type);
-  // TODO(dsinclair): This is spec'd as rounding when > 16 significant digits
-  // prior to the exponent.
-  // EXPECT_EQ(L"100000000000000000", token.m_string);
-  EXPECT_EQ(L"99999999999999999", token.m_string);
-  EXPECT_TRUE(lexer->IsComplete());
-}
-
-// The quotes are stripped in CXFA_FMStringExpression::ToJavaScript.
-TEST(CXFA_FMLexerTest, Strings) {
-  auto lexer =
-      pdfium::MakeUnique<CXFA_FMLexer>(L"\"The cat jumped over the fence.\"");
-  CXFA_FMToken token = lexer->NextToken();
-  EXPECT_EQ(TOKstring, token.m_type);
-  EXPECT_EQ(L"\"The cat jumped over the fence.\"", token.m_string);
-
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token.m_type);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"\"\"");
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKstring, token.m_type);
-  EXPECT_EQ(L"\"\"", token.m_string);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(
-      L"\"The message reads: \"\"Warning: Insufficient Memory\"\"\"");
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKstring, token.m_type);
-  EXPECT_EQ(L"\"The message reads: \"\"Warning: Insufficient Memory\"\"\"",
-            token.m_string);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(
-      L"\"\\u0047\\u006f\\u0066\\u0069\\u0073\\u0068\\u0021\\u000d\\u000a\"");
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKstring, token.m_type);
-  EXPECT_EQ(
-      L"\"\\u0047\\u006f\\u0066\\u0069\\u0073\\u0068\\u0021\\u000d\\u000a\"",
-      token.m_string);
-  EXPECT_TRUE(lexer->IsComplete());
-}
-
-// Note, 'this' is a keyword but is not matched by the lexer.
-TEST(CXFA_FMLexerTest, OperatorsAndKeywords) {
-  struct {
-    const wchar_t* op;
-    XFA_FM_TOKEN token;
-  } op[] = {{L"+", TOKplus},
-            {L"/", TOKdiv},
-            {L"-", TOKminus},
-            {L"&", TOKand},
-            {L"|", TOKor},
-            {L"*", TOKmul},
-            {L"<", TOKlt},
-            {L">", TOKgt},
-            {L"==", TOKeq},
-            {L"<>", TOKne},
-            {L"<=", TOKle},
-            {L">=", TOKge},
-            {L"and", TOKksand},
-            {L"break", TOKbreak},
-            {L"continue", TOKcontinue},
-            {L"do", TOKdo},
-            {L"downto", TOKdownto},
-            {L"else", TOKelse},
-            {L"elseif", TOKelseif},
-            {L"end", TOKend},
-            {L"endfor", TOKendfor},
-            {L"endfunc", TOKendfunc},
-            {L"endif", TOKendif},
-            {L"endwhile", TOKendwhile},
-            {L"eq", TOKkseq},
-            {L"exit", TOKexit},
-            {L"for", TOKfor},
-            {L"foreach", TOKforeach},
-            {L"func", TOKfunc},
-            {L"ge", TOKksge},
-            {L"gt", TOKksgt},
-            {L"if", TOKif},
-            {L"in", TOKin},
-            {L"infinity", TOKinfinity},
-            {L"le", TOKksle},
-            {L"lt", TOKkslt},
-            {L"nan", TOKnan},
-            {L"ne", TOKksne},
-            {L"not", TOKksnot},
-            {L"null", TOKnull},
-            {L"or", TOKksor},
-            {L"return", TOKreturn},
-            {L"step", TOKstep},
-            {L"then", TOKthen},
-            {L"throw", TOKthrow},
-            {L"upto", TOKupto},
-            {L"var", TOKvar},
-            {L"while", TOKwhile},
-
-            // The following are defined but aren't in the spec.
-            {L"(", TOKlparen},
-            {L")", TOKrparen},
-            {L",", TOKcomma},
-            {L".", TOKdot},
-            {L"[", TOKlbracket},
-            {L"]", TOKrbracket},
-            {L"..", TOKdotdot},
-            {L".#", TOKdotscream},
-            {L".*", TOKdotstar}};
-
-  for (size_t i = 0; i < FX_ArraySize(op); ++i) {
-    auto lexer = pdfium::MakeUnique<CXFA_FMLexer>(op[i].op);
-    CXFA_FMToken token = lexer->NextToken();
-    EXPECT_EQ(op[i].token, token.m_type);
-    EXPECT_TRUE(lexer->IsComplete());
-  }
-}
-
-TEST(CXFA_FMLexerTest, Comments) {
-  auto lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"// Empty.");
-  CXFA_FMToken token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token.m_type);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"//");
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token.m_type);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"123 // Empty.\n\"str\"");
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token.m_type);
-  EXPECT_EQ(L"123", token.m_string);
-
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKstring, token.m_type);
-  EXPECT_EQ(L"\"str\"", token.m_string);
-
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token.m_type);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(L";");
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token.m_type);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"; Empty.");
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token.m_type);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"123 ;Empty.\n\"str\"");
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token.m_type);
-  EXPECT_EQ(L"123", token.m_string);
-
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKstring, token.m_type);
-  EXPECT_EQ(L"\"str\"", token.m_string);
-
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token.m_type);
-  EXPECT_TRUE(lexer->IsComplete());
-}
-
-TEST(CXFA_FMLexerTest, ValidIdentifiers) {
-  std::vector<const wchar_t*> identifiers = {
-      L"a", L"an_identifier", L"_ident", L"$ident", L"!ident", L"GetAddr"};
-  for (const auto* ident : identifiers) {
-    auto lexer = pdfium::MakeUnique<CXFA_FMLexer>(ident);
-    CXFA_FMToken token = lexer->NextToken();
-    EXPECT_EQ(TOKidentifier, token.m_type);
-    EXPECT_EQ(ident, token.m_string);
-    EXPECT_TRUE(lexer->IsComplete());
-  }
-}
-
-TEST(CXFA_FMLexerTest, InvalidIdentifiers) {
-  auto lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"#a");
-  auto token = lexer->NextToken();
-  EXPECT_EQ(TOKreserver, token.m_type);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"1a");
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKreserver, token.m_type);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"an@identifier");
-  token = lexer->NextToken();
-  EXPECT_NE(TOKreserver, token.m_type);
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKreserver, token.m_type);
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKreserver, token.m_type);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"_ident@");
-  token = lexer->NextToken();
-  EXPECT_NE(TOKreserver, token.m_type);
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKreserver, token.m_type);
-  EXPECT_FALSE(lexer->IsComplete());
-}
-
-TEST(CXFA_FMLexerTest, Whitespace) {
-  auto lexer = pdfium::MakeUnique<CXFA_FMLexer>(L" \t\xc\x9\xb");
-  CXFA_FMToken token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token.m_type);
-
-  lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"123 \t\xc\x9\xb 456");
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token.m_type);
-  EXPECT_EQ(L"123", token.m_string);
-
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token.m_type);
-  EXPECT_EQ(L"456", token.m_string);
-
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token.m_type);
-  EXPECT_TRUE(lexer->IsComplete());
-}
-
-TEST(CXFA_FMLexerTest, NullData) {
-  auto lexer = pdfium::MakeUnique<CXFA_FMLexer>(
-      WideStringView(L"\x2d\x32\x00\x2d\x32", 5));
-  CXFA_FMToken token = lexer->NextToken();
-  EXPECT_EQ(TOKminus, token.m_type);
-
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token.m_type);
-  EXPECT_EQ(L"2", token.m_string);
-
-  token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token.m_type);
-  EXPECT_FALSE(lexer->IsComplete());
-}
diff --git a/xfa/fxfa/fm2js/cxfa_fmparser.cpp b/xfa/fxfa/fm2js/cxfa_fmparser.cpp
deleted file mode 100644
index 152b5a1..0000000
--- a/xfa/fxfa/fm2js/cxfa_fmparser.cpp
+++ /dev/null
@@ -1,1138 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/fm2js/cxfa_fmparser.h"
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "core/fxcrt/autorestorer.h"
-#include "third_party/base/ptr_util.h"
-
-namespace {
-
-constexpr unsigned int kMaxParseDepth = 1250;
-constexpr unsigned int kMaxPostExpressions = 256;
-constexpr unsigned int kMaxExpressionListSize = 10000;
-
-}  // namespace
-
-CXFA_FMParser::CXFA_FMParser(WideStringView wsFormcalc)
-    : m_lexer(pdfium::MakeUnique<CXFA_FMLexer>(wsFormcalc)),
-      m_error(false),
-      m_parse_depth(0),
-      m_max_parse_depth(kMaxParseDepth) {}
-
-CXFA_FMParser::~CXFA_FMParser() = default;
-
-std::unique_ptr<CXFA_FMAST> CXFA_FMParser::Parse() {
-  m_token = m_lexer->NextToken();
-  if (HasError())
-    return nullptr;
-
-  auto expressions = ParseExpressionList();
-  if (HasError())
-    return nullptr;
-
-  // We failed to parse all of the input so something has gone wrong.
-  if (!m_lexer->IsComplete())
-    return nullptr;
-
-  return pdfium::MakeUnique<CXFA_FMAST>(std::move(expressions));
-}
-
-bool CXFA_FMParser::NextToken() {
-  if (HasError())
-    return false;
-
-  m_token = m_lexer->NextToken();
-  while (!HasError() && m_token.m_type == TOKreserver)
-    m_token = m_lexer->NextToken();
-  return !HasError();
-}
-
-bool CXFA_FMParser::CheckThenNext(XFA_FM_TOKEN op) {
-  if (HasError())
-    return false;
-
-  if (m_token.m_type != op) {
-    m_error = true;
-    return false;
-  }
-  return NextToken();
-}
-
-bool CXFA_FMParser::IncrementParseDepthAndCheck() {
-  return ++m_parse_depth < m_max_parse_depth;
-}
-
-std::vector<std::unique_ptr<CXFA_FMExpression>>
-CXFA_FMParser::ParseExpressionList() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return std::vector<std::unique_ptr<CXFA_FMExpression>>();
-
-  std::vector<std::unique_ptr<CXFA_FMExpression>> expressions;
-  while (!HasError()) {
-    if (m_token.m_type == TOKeof || m_token.m_type == TOKendfunc ||
-        m_token.m_type == TOKendif || m_token.m_type == TOKelseif ||
-        m_token.m_type == TOKelse || m_token.m_type == TOKendwhile ||
-        m_token.m_type == TOKendfor || m_token.m_type == TOKend ||
-        m_token.m_type == TOKendfunc || m_token.m_type == TOKreserver) {
-      break;
-    }
-
-    std::unique_ptr<CXFA_FMExpression> expr =
-        m_token.m_type == TOKfunc ? ParseFunction() : ParseExpression();
-    if (!expr) {
-      m_error = true;
-      return std::vector<std::unique_ptr<CXFA_FMExpression>>();
-    }
-
-    if (expressions.size() >= kMaxExpressionListSize) {
-      m_error = true;
-      return std::vector<std::unique_ptr<CXFA_FMExpression>>();
-    }
-
-    expressions.push_back(std::move(expr));
-  }
-  return expressions;
-}
-
-// Func := 'func' Identifier '(' ParameterList ')' do ExpressionList 'endfunc'
-// ParamterList := (Not actually defined in the grammar) .....
-//                 (Identifier (',' Identifier)*)?
-std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseFunction() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-  if (!CheckThenNext(TOKfunc))
-    return nullptr;
-  if (m_token.m_type != TOKidentifier)
-    return nullptr;
-
-  WideStringView ident = m_token.m_string;
-  if (!NextToken())
-    return nullptr;
-  if (!CheckThenNext(TOKlparen))
-    return nullptr;
-
-  std::vector<WideStringView> arguments;
-  bool last_was_comma = false;
-  while (1) {
-    if (m_token.m_type == TOKrparen)
-      break;
-    if (m_token.m_type != TOKidentifier)
-      return nullptr;
-
-    last_was_comma = false;
-
-    arguments.push_back(m_token.m_string);
-    if (!NextToken())
-      return nullptr;
-    if (m_token.m_type != TOKcomma)
-      continue;
-
-    last_was_comma = true;
-    if (!NextToken())
-      return nullptr;
-  }
-  if (last_was_comma || !CheckThenNext(TOKrparen))
-    return nullptr;
-  if (!CheckThenNext(TOKdo))
-    return nullptr;
-
-  std::vector<std::unique_ptr<CXFA_FMExpression>> expressions;
-  if (m_token.m_type != TOKendfunc)
-    expressions = ParseExpressionList();
-
-  if (!CheckThenNext(TOKendfunc))
-    return nullptr;
-
-  return pdfium::MakeUnique<CXFA_FMFunctionDefinition>(
-      ident, std::move(arguments), std::move(expressions));
-}
-
-// Expression := IfExpression | WhileExpression | ForExpression |
-//               ForEachExpression | AssignmentExpression |
-//               DeclarationExpression | SimpleExpression
-std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-
-  std::unique_ptr<CXFA_FMExpression> expr;
-  switch (m_token.m_type) {
-    case TOKvar:
-      expr = ParseDeclarationExpression();
-      break;
-    case TOKnull:
-    case TOKnumber:
-    case TOKstring:
-    case TOKplus:
-    case TOKminus:
-    case TOKksnot:
-    case TOKidentifier:
-    case TOKlparen:
-      expr = ParseExpExpression();
-      break;
-    case TOKif:
-      expr = ParseIfExpression();
-      break;
-    case TOKwhile:
-      expr = ParseWhileExpression();
-      break;
-    case TOKfor:
-      expr = ParseForExpression();
-      break;
-    case TOKforeach:
-      expr = ParseForeachExpression();
-      break;
-    case TOKdo:
-      expr = ParseDoExpression();
-      break;
-    case TOKbreak:
-      expr = pdfium::MakeUnique<CXFA_FMBreakExpression>();
-      if (!NextToken())
-        return nullptr;
-      break;
-    case TOKcontinue:
-      expr = pdfium::MakeUnique<CXFA_FMContinueExpression>();
-      if (!NextToken())
-        return nullptr;
-      break;
-    default:
-      return nullptr;
-  }
-  return expr;
-}
-
-// Declaration := 'var' Variable | 'var' Variable '=' SimpleExpression |
-//           'Func' Identifier '(' ParameterList ')' do ExpressionList 'EndFunc'
-// TODO(dsinclair): We appear to be handling the 'func' case elsewhere.
-std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseDeclarationExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-
-  WideStringView ident;
-  if (!NextToken())
-    return nullptr;
-  if (m_token.m_type != TOKidentifier)
-    return nullptr;
-
-  ident = m_token.m_string;
-  if (!NextToken())
-    return nullptr;
-
-  std::unique_ptr<CXFA_FMSimpleExpression> expr;
-  if (m_token.m_type == TOKassign) {
-    if (!NextToken())
-      return nullptr;
-
-    expr = ParseSimpleExpression();
-    if (!expr)
-      return nullptr;
-  }
-
-  return pdfium::MakeUnique<CXFA_FMVarExpression>(ident, std::move(expr));
-}
-
-// SimpleExpression := LogicalOrExpression
-std::unique_ptr<CXFA_FMSimpleExpression>
-CXFA_FMParser::ParseSimpleExpression() {
-  if (HasError())
-    return nullptr;
-
-  return ParseLogicalOrExpression();
-}
-
-// Exp := SimpleExpression ( '=' SimpleExpression )?
-std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseExpExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-
-  std::unique_ptr<CXFA_FMSimpleExpression> pExp1 = ParseSimpleExpression();
-  if (!pExp1)
-    return nullptr;
-
-  if (m_token.m_type == TOKassign) {
-    if (!NextToken())
-      return nullptr;
-
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2 = ParseSimpleExpression();
-    if (!pExp2)
-      return nullptr;
-
-    pExp1 = pdfium::MakeUnique<CXFA_FMAssignExpression>(
-        TOKassign, std::move(pExp1), std::move(pExp2));
-  }
-  return pdfium::MakeUnique<CXFA_FMExpExpression>(std::move(pExp1));
-}
-
-// LogicalOr := LogicalAndExpression |
-//              LogicalOrExpression LogicalOrOperator LogicalAndExpression
-std::unique_ptr<CXFA_FMSimpleExpression>
-CXFA_FMParser::ParseLogicalOrExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-
-  std::unique_ptr<CXFA_FMSimpleExpression> e1 = ParseLogicalAndExpression();
-  if (!e1)
-    return nullptr;
-
-  // TODO(dsinclair): Is this for() needed?
-  for (;;) {
-    if (!IncrementParseDepthAndCheck())
-      return nullptr;
-
-    switch (m_token.m_type) {
-      case TOKor:
-      case TOKksor: {
-        if (!NextToken())
-          return nullptr;
-
-        std::unique_ptr<CXFA_FMSimpleExpression> e2(
-            ParseLogicalAndExpression());
-        if (!e2)
-          return nullptr;
-
-        e1 = pdfium::MakeUnique<CXFA_FMLogicalOrExpression>(
-            TOKor, std::move(e1), std::move(e2));
-        continue;
-      }
-      default:
-        break;
-    }
-    break;
-  }
-  return e1;
-}
-
-// LogicalAnd := EqualityExpression |
-//               LogicalAndExpression LogicalAndOperator EqualityExpression
-std::unique_ptr<CXFA_FMSimpleExpression>
-CXFA_FMParser::ParseLogicalAndExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-
-  std::unique_ptr<CXFA_FMSimpleExpression> e1 = ParseEqualityExpression();
-  if (!e1)
-    return nullptr;
-
-  // TODO(dsinclair): Is this for() needed?
-  for (;;) {
-    if (!IncrementParseDepthAndCheck())
-      return nullptr;
-
-    switch (m_token.m_type) {
-      case TOKand:
-      case TOKksand: {
-        if (!NextToken())
-          return nullptr;
-
-        std::unique_ptr<CXFA_FMSimpleExpression> e2 = ParseEqualityExpression();
-        if (!e2)
-          return nullptr;
-
-        e1 = pdfium::MakeUnique<CXFA_FMLogicalAndExpression>(
-            TOKand, std::move(e1), std::move(e2));
-        continue;
-      }
-      default:
-        break;
-    }
-    break;
-  }
-  return e1;
-}
-
-// Equality := RelationExpression |
-//             EqualityExpression EqulaityOperator RelationalExpression
-std::unique_ptr<CXFA_FMSimpleExpression>
-CXFA_FMParser::ParseEqualityExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-
-  std::unique_ptr<CXFA_FMSimpleExpression> e1 = ParseRelationalExpression();
-  if (!e1)
-    return nullptr;
-
-  // TODO(dsinclair): Is this for() needed?
-  for (;;) {
-    if (!IncrementParseDepthAndCheck())
-      return nullptr;
-
-    switch (m_token.m_type) {
-      case TOKeq:
-      case TOKkseq: {
-        if (!NextToken())
-          return nullptr;
-
-        std::unique_ptr<CXFA_FMSimpleExpression> e2 =
-            ParseRelationalExpression();
-        if (!e2)
-          return nullptr;
-
-        e1 = pdfium::MakeUnique<CXFA_FMEqualExpression>(TOKeq, std::move(e1),
-                                                        std::move(e2));
-        continue;
-      }
-      case TOKne:
-      case TOKksne: {
-        if (!NextToken())
-          return nullptr;
-
-        std::unique_ptr<CXFA_FMSimpleExpression> e2 =
-            ParseRelationalExpression();
-        if (!e2)
-          return nullptr;
-
-        e1 = pdfium::MakeUnique<CXFA_FMNotEqualExpression>(TOKne, std::move(e1),
-                                                           std::move(e2));
-        continue;
-      }
-      default:
-        break;
-    }
-    break;
-  }
-  return e1;
-}
-
-// Relational := AdditiveExpression |
-//               RelationalExpression RelationalOperator AdditiveExpression
-std::unique_ptr<CXFA_FMSimpleExpression>
-CXFA_FMParser::ParseRelationalExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-
-  std::unique_ptr<CXFA_FMSimpleExpression> e1 = ParseAdditiveExpression();
-  if (!e1)
-    return nullptr;
-
-  // TODO(dsinclair): Is this for() needed?
-  for (;;) {
-    if (!IncrementParseDepthAndCheck())
-      return nullptr;
-
-    std::unique_ptr<CXFA_FMSimpleExpression> e2;
-    switch (m_token.m_type) {
-      case TOKlt:
-      case TOKkslt:
-        if (!NextToken())
-          return nullptr;
-
-        e2 = ParseAdditiveExpression();
-        if (!e2)
-          return nullptr;
-
-        e1 = pdfium::MakeUnique<CXFA_FMLtExpression>(TOKlt, std::move(e1),
-                                                     std::move(e2));
-        continue;
-      case TOKgt:
-      case TOKksgt:
-        if (!NextToken())
-          return nullptr;
-
-        e2 = ParseAdditiveExpression();
-        if (!e2)
-          return nullptr;
-
-        e1 = pdfium::MakeUnique<CXFA_FMGtExpression>(TOKgt, std::move(e1),
-                                                     std::move(e2));
-        continue;
-      case TOKle:
-      case TOKksle:
-        if (!NextToken())
-          return nullptr;
-
-        e2 = ParseAdditiveExpression();
-        if (!e2)
-          return nullptr;
-
-        e1 = pdfium::MakeUnique<CXFA_FMLeExpression>(TOKle, std::move(e1),
-                                                     std::move(e2));
-        continue;
-      case TOKge:
-      case TOKksge:
-        if (!NextToken())
-          return nullptr;
-
-        e2 = ParseAdditiveExpression();
-        if (!e2)
-          return nullptr;
-
-        e1 = pdfium::MakeUnique<CXFA_FMGeExpression>(TOKge, std::move(e1),
-                                                     std::move(e2));
-        continue;
-      default:
-        break;
-    }
-    break;
-  }
-  return e1;
-}
-
-// Additive := MultiplicativeExpression |
-//             AdditiveExpression AdditiveOperator MultiplicativeExpression
-std::unique_ptr<CXFA_FMSimpleExpression>
-CXFA_FMParser::ParseAdditiveExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-
-  std::unique_ptr<CXFA_FMSimpleExpression> e1 = ParseMultiplicativeExpression();
-  if (!e1)
-    return nullptr;
-
-  // TODO(dsinclair): Is this for() needed?
-  for (;;) {
-    if (!IncrementParseDepthAndCheck())
-      return nullptr;
-
-    std::unique_ptr<CXFA_FMSimpleExpression> e2;
-    switch (m_token.m_type) {
-      case TOKplus:
-        if (!NextToken())
-          return nullptr;
-
-        e2 = ParseMultiplicativeExpression();
-        if (!e2)
-          return nullptr;
-
-        e1 = pdfium::MakeUnique<CXFA_FMPlusExpression>(TOKplus, std::move(e1),
-                                                       std::move(e2));
-        continue;
-      case TOKminus:
-        if (!NextToken())
-          return nullptr;
-
-        e2 = ParseMultiplicativeExpression();
-        if (!e2)
-          return nullptr;
-
-        e1 = pdfium::MakeUnique<CXFA_FMMinusExpression>(TOKminus, std::move(e1),
-                                                        std::move(e2));
-        continue;
-      default:
-        break;
-    }
-    break;
-  }
-  return e1;
-}
-
-// Multiplicative := UnaryExpression |
-//                 MultiplicateExpression MultiplicativeOperator UnaryExpression
-std::unique_ptr<CXFA_FMSimpleExpression>
-CXFA_FMParser::ParseMultiplicativeExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-
-  std::unique_ptr<CXFA_FMSimpleExpression> e1 = ParseUnaryExpression();
-  if (!e1)
-    return nullptr;
-
-  // TODO(dsinclair): Is this for() needed?
-  for (;;) {
-    if (!IncrementParseDepthAndCheck())
-      return nullptr;
-
-    std::unique_ptr<CXFA_FMSimpleExpression> e2;
-    switch (m_token.m_type) {
-      case TOKmul:
-        if (!NextToken())
-          return nullptr;
-
-        e2 = ParseUnaryExpression();
-        if (!e2)
-          return nullptr;
-
-        e1 = pdfium::MakeUnique<CXFA_FMMulExpression>(TOKmul, std::move(e1),
-                                                      std::move(e2));
-        continue;
-      case TOKdiv:
-        if (!NextToken())
-          return nullptr;
-
-        e2 = ParseUnaryExpression();
-        if (!e2)
-          return nullptr;
-
-        e1 = pdfium::MakeUnique<CXFA_FMDivExpression>(TOKdiv, std::move(e1),
-                                                      std::move(e2));
-        continue;
-      default:
-        break;
-    }
-    break;
-  }
-  return e1;
-}
-
-// Unary := PrimaryExpression | UnaryOperator UnaryExpression
-std::unique_ptr<CXFA_FMSimpleExpression> CXFA_FMParser::ParseUnaryExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-
-  std::unique_ptr<CXFA_FMSimpleExpression> expr;
-  switch (m_token.m_type) {
-    case TOKplus:
-      if (!NextToken())
-        return nullptr;
-
-      expr = ParseUnaryExpression();
-      if (!expr)
-        return nullptr;
-
-      expr = pdfium::MakeUnique<CXFA_FMPosExpression>(std::move(expr));
-      break;
-    case TOKminus:
-      if (!NextToken())
-        return nullptr;
-
-      expr = ParseUnaryExpression();
-      if (!expr)
-        return nullptr;
-
-      expr = pdfium::MakeUnique<CXFA_FMNegExpression>(std::move(expr));
-      break;
-    case TOKksnot:
-      if (!NextToken())
-        return nullptr;
-
-      expr = ParseUnaryExpression();
-      if (!expr)
-        return nullptr;
-
-      expr = pdfium::MakeUnique<CXFA_FMNotExpression>(std::move(expr));
-      break;
-    default:
-      return ParsePrimaryExpression();
-  }
-  return expr;
-}
-
-// Primary := Literal | FunctionCall | Accessor ('.*' )? |
-//           '(' SimpleExpression ')'
-std::unique_ptr<CXFA_FMSimpleExpression>
-CXFA_FMParser::ParsePrimaryExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-
-  std::unique_ptr<CXFA_FMSimpleExpression> expr = ParseLiteral();
-  if (expr)
-    return NextToken() ? std::move(expr) : nullptr;
-
-  switch (m_token.m_type) {
-    case TOKidentifier: {
-      WideStringView wsIdentifier(m_token.m_string);
-      if (!NextToken())
-        return nullptr;
-      if (m_token.m_type == TOKlbracket) {
-        std::unique_ptr<CXFA_FMSimpleExpression> s = ParseIndexExpression();
-        if (!s)
-          return nullptr;
-
-        expr = pdfium::MakeUnique<CXFA_FMDotAccessorExpression>(
-            nullptr, TOKdot, wsIdentifier, std::move(s));
-        if (!expr)
-          return nullptr;
-        if (!NextToken())
-          return nullptr;
-      } else {
-        expr = pdfium::MakeUnique<CXFA_FMIdentifierExpression>(wsIdentifier);
-      }
-      break;
-    }
-    case TOKlparen:
-      expr = ParseParenExpression();
-      if (!expr)
-        return nullptr;
-      break;
-    default:
-      return nullptr;
-  }
-  return ParsePostExpression(std::move(expr));
-}
-
-// Literal := String | Number | Null
-std::unique_ptr<CXFA_FMSimpleExpression> CXFA_FMParser::ParseLiteral() {
-  switch (m_token.m_type) {
-    case TOKnumber:
-      return pdfium::MakeUnique<CXFA_FMNumberExpression>(m_token.m_string);
-    case TOKstring:
-      return pdfium::MakeUnique<CXFA_FMStringExpression>(m_token.m_string);
-    case TOKnull:
-      return pdfium::MakeUnique<CXFA_FMNullExpression>();
-    default:
-      return nullptr;
-  }
-}
-
-// TODO(dsinclair): Make this match up to the grammar
-// I believe this is parsing the accessor ( '.' | '..' | '.#' )
-std::unique_ptr<CXFA_FMSimpleExpression> CXFA_FMParser::ParsePostExpression(
-    std::unique_ptr<CXFA_FMSimpleExpression> expr) {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-
-  size_t expr_count = 0;
-  while (1) {
-    ++expr_count;
-    // Limit the number of expressions allowed in the post expression statement.
-    // If we don't do this then its possible to generate a stack overflow
-    // by having a very large number of things like .. expressions.
-    if (expr_count > kMaxPostExpressions)
-      return nullptr;
-
-    switch (m_token.m_type) {
-      case TOKlparen: {
-        std::unique_ptr<std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>>
-            expressions = ParseArgumentList();
-        if (!expressions)
-          return nullptr;
-
-        expr = pdfium::MakeUnique<CXFA_FMCallExpression>(
-            std::move(expr), std::move(*expressions), false);
-        if (!NextToken())
-          return nullptr;
-        if (m_token.m_type != TOKlbracket)
-          continue;
-
-        std::unique_ptr<CXFA_FMSimpleExpression> s = ParseIndexExpression();
-        if (!s)
-          return nullptr;
-
-        expr = pdfium::MakeUnique<CXFA_FMDotAccessorExpression>(
-            std::move(expr), TOKcall, WideStringView(), std::move(s));
-        break;
-      }
-      case TOKdot: {
-        if (!NextToken())
-          return nullptr;
-        if (m_token.m_type != TOKidentifier)
-          return nullptr;
-
-        WideStringView tempStr = m_token.m_string;
-        if (!NextToken())
-          return nullptr;
-        if (m_token.m_type == TOKlparen) {
-          std::unique_ptr<std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>>
-              expressions = ParseArgumentList();
-          if (!expressions)
-            return nullptr;
-
-          auto pIdentifier =
-              pdfium::MakeUnique<CXFA_FMIdentifierExpression>(tempStr);
-          auto pExpCall = pdfium::MakeUnique<CXFA_FMCallExpression>(
-              std::move(pIdentifier), std::move(*expressions), true);
-          expr = pdfium::MakeUnique<CXFA_FMMethodCallExpression>(
-              std::move(expr), std::move(pExpCall));
-          if (!NextToken())
-            return nullptr;
-          if (m_token.m_type != TOKlbracket)
-            continue;
-
-          std::unique_ptr<CXFA_FMSimpleExpression> s = ParseIndexExpression();
-          if (!s)
-            return nullptr;
-
-          expr = pdfium::MakeUnique<CXFA_FMDotAccessorExpression>(
-              std::move(expr), TOKcall, WideStringView(), std::move(s));
-        } else if (m_token.m_type == TOKlbracket) {
-          std::unique_ptr<CXFA_FMSimpleExpression> s = ParseIndexExpression();
-          if (!s)
-            return nullptr;
-
-          expr = pdfium::MakeUnique<CXFA_FMDotAccessorExpression>(
-              std::move(expr), TOKdot, tempStr, std::move(s));
-        } else {
-          std::unique_ptr<CXFA_FMSimpleExpression> s =
-              pdfium::MakeUnique<CXFA_FMIndexExpression>(ACCESSOR_NO_INDEX,
-                                                         nullptr, false);
-          expr = pdfium::MakeUnique<CXFA_FMDotAccessorExpression>(
-              std::move(expr), TOKdot, tempStr, std::move(s));
-          continue;
-        }
-        break;
-      }
-      case TOKdotdot: {
-        if (!NextToken())
-          return nullptr;
-        if (m_token.m_type != TOKidentifier)
-          return nullptr;
-
-        WideStringView tempStr = m_token.m_string;
-        if (!NextToken())
-          return nullptr;
-        if (m_token.m_type == TOKlbracket) {
-          std::unique_ptr<CXFA_FMSimpleExpression> s = ParseIndexExpression();
-          if (!s)
-            return nullptr;
-
-          expr = pdfium::MakeUnique<CXFA_FMDotDotAccessorExpression>(
-              std::move(expr), TOKdotdot, tempStr, std::move(s));
-        } else {
-          std::unique_ptr<CXFA_FMSimpleExpression> s =
-              pdfium::MakeUnique<CXFA_FMIndexExpression>(ACCESSOR_NO_INDEX,
-                                                         nullptr, false);
-          expr = pdfium::MakeUnique<CXFA_FMDotDotAccessorExpression>(
-              std::move(expr), TOKdotdot, tempStr, std::move(s));
-          continue;
-        }
-        break;
-      }
-      case TOKdotscream: {
-        if (!NextToken())
-          return nullptr;
-        if (m_token.m_type != TOKidentifier)
-          return nullptr;
-
-        WideStringView tempStr = m_token.m_string;
-        if (!NextToken())
-          return nullptr;
-
-        if (m_token.m_type != TOKlbracket) {
-          std::unique_ptr<CXFA_FMSimpleExpression> s =
-              pdfium::MakeUnique<CXFA_FMIndexExpression>(ACCESSOR_NO_INDEX,
-                                                         nullptr, false);
-          expr = pdfium::MakeUnique<CXFA_FMDotAccessorExpression>(
-              std::move(expr), TOKdotscream, tempStr, std::move(s));
-          continue;
-        }
-
-        std::unique_ptr<CXFA_FMSimpleExpression> s = ParseIndexExpression();
-        if (!s)
-          return nullptr;
-
-        expr = pdfium::MakeUnique<CXFA_FMDotAccessorExpression>(
-            std::move(expr), TOKdotscream, tempStr, std::move(s));
-        break;
-      }
-      case TOKdotstar: {
-        std::unique_ptr<CXFA_FMSimpleExpression> s =
-            pdfium::MakeUnique<CXFA_FMIndexExpression>(ACCESSOR_NO_INDEX,
-                                                       nullptr, false);
-        expr = pdfium::MakeUnique<CXFA_FMDotAccessorExpression>(
-            std::move(expr), TOKdotstar, L"*", std::move(s));
-        break;
-      }
-      default:
-        return expr;
-    }
-    if (!NextToken())
-      return nullptr;
-  }
-  return expr;
-}
-
-// Argument lists are zero or more comma seperated simple expressions found
-// between '(' and ')'
-std::unique_ptr<std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>>
-CXFA_FMParser::ParseArgumentList() {
-  if (m_token.m_type != TOKlparen || !NextToken())
-    return nullptr;
-
-  auto expressions = pdfium::MakeUnique<
-      std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>>();
-  bool first_arg = true;
-  while (m_token.m_type != TOKrparen) {
-    if (first_arg) {
-      first_arg = false;
-    } else {
-      if (m_token.m_type != TOKcomma || !NextToken())
-        return nullptr;
-    }
-
-    std::unique_ptr<CXFA_FMSimpleExpression> exp = ParseSimpleExpression();
-    if (!exp)
-      return nullptr;
-
-    expressions->push_back(std::move(exp));
-    if (expressions->size() > kMaxPostExpressions)
-      return nullptr;
-  }
-
-  return expressions;
-}
-
-// Index := '[' ('*' | '+' SimpleExpression | '-' SimpleExpression) ']'
-std::unique_ptr<CXFA_FMSimpleExpression> CXFA_FMParser::ParseIndexExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-  if (!CheckThenNext(TOKlbracket))
-    return nullptr;
-
-  if (m_token.m_type == TOKmul) {
-    auto pExp = pdfium::MakeUnique<CXFA_FMIndexExpression>(
-        ACCESSOR_NO_RELATIVEINDEX, nullptr, true);
-    if (!pExp || !NextToken())
-      return nullptr;
-
-    // TODO(dsinclair): This should CheckThenNext(TOKrbracket) but need to clean
-    // up the callsites.
-    if (m_token.m_type != TOKrbracket)
-      return nullptr;
-    return pExp;
-  }
-
-  XFA_FM_AccessorIndex accessorIndex = ACCESSOR_NO_RELATIVEINDEX;
-  if (m_token.m_type == TOKplus) {
-    accessorIndex = ACCESSOR_POSITIVE_INDEX;
-    if (!NextToken())
-      return nullptr;
-  } else if (m_token.m_type == TOKminus) {
-    accessorIndex = ACCESSOR_NEGATIVE_INDEX;
-    if (!NextToken())
-      return nullptr;
-  }
-
-  std::unique_ptr<CXFA_FMSimpleExpression> s = ParseSimpleExpression();
-  if (!s)
-    return nullptr;
-  if (m_token.m_type != TOKrbracket)
-    return nullptr;
-
-  return pdfium::MakeUnique<CXFA_FMIndexExpression>(accessorIndex, std::move(s),
-                                                    false);
-}
-
-// Paren := '(' SimpleExpression ')'
-std::unique_ptr<CXFA_FMSimpleExpression> CXFA_FMParser::ParseParenExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-
-  if (!CheckThenNext(TOKlparen))
-    return nullptr;
-  if (m_token.m_type == TOKrparen)
-    return nullptr;
-
-  std::unique_ptr<CXFA_FMSimpleExpression> pExp1 = ParseSimpleExpression();
-  if (!pExp1)
-    return nullptr;
-
-  if (!CheckThenNext(TOKrparen))
-    return nullptr;
-  return pExp1;
-}
-
-// If := 'if' '(' SimpleExpression ')' 'then' ExpressionList
-//       ('elseif' '(' SimpleExpression ')' 'then' ExpressionList)*
-//       ('else' ExpressionList)?
-//       'endif'
-std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseIfExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-
-  if (!CheckThenNext(TOKif))
-    return nullptr;
-
-  std::unique_ptr<CXFA_FMSimpleExpression> pCondition = ParseParenExpression();
-  if (!pCondition)
-    return nullptr;
-  if (!CheckThenNext(TOKthen))
-    return nullptr;
-
-  auto pIfExpressions =
-      pdfium::MakeUnique<CXFA_FMBlockExpression>(ParseExpressionList());
-
-  std::vector<std::unique_ptr<CXFA_FMIfExpression>> pElseIfExpressions;
-  while (m_token.m_type == TOKelseif) {
-    if (!NextToken())
-      return nullptr;
-
-    auto elseIfCondition = ParseParenExpression();
-    if (!elseIfCondition)
-      return nullptr;
-    if (!CheckThenNext(TOKthen))
-      return nullptr;
-
-    auto elseIfExprs = ParseExpressionList();
-    pElseIfExpressions.push_back(pdfium::MakeUnique<CXFA_FMIfExpression>(
-        std::move(elseIfCondition),
-        pdfium::MakeUnique<CXFA_FMBlockExpression>(std::move(elseIfExprs)),
-        std::vector<std::unique_ptr<CXFA_FMIfExpression>>(), nullptr));
-  }
-
-  std::unique_ptr<CXFA_FMExpression> pElseExpression;
-  if (m_token.m_type == TOKelse) {
-    if (!NextToken())
-      return nullptr;
-
-    pElseExpression =
-        pdfium::MakeUnique<CXFA_FMBlockExpression>(ParseExpressionList());
-  }
-  if (!CheckThenNext(TOKendif))
-    return nullptr;
-
-  return pdfium::MakeUnique<CXFA_FMIfExpression>(
-      std::move(pCondition), std::move(pIfExpressions),
-      std::move(pElseIfExpressions), std::move(pElseExpression));
-}
-
-// While := 'while' '(' SimpleExpression ')' 'do' ExpressionList 'endwhile'
-std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseWhileExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-  if (!CheckThenNext(TOKwhile))
-    return nullptr;
-
-  std::unique_ptr<CXFA_FMSimpleExpression> pCondition = ParseParenExpression();
-  if (!pCondition || !CheckThenNext(TOKdo))
-    return nullptr;
-
-  auto exprs = ParseExpressionList();
-  if (!CheckThenNext(TOKendwhile))
-    return nullptr;
-
-  return pdfium::MakeUnique<CXFA_FMWhileExpression>(
-      std::move(pCondition),
-      pdfium::MakeUnique<CXFA_FMBlockExpression>(std::move(exprs)));
-}
-
-// For := 'for' Assignment 'upto' Accessor ('step' SimpleExpression)?
-//            'do' ExpressionList 'endfor' |
-//         'for' Assignment 'downto' Accessor ('step' SimpleExpression)?
-//            'do' ExpressionList 'endfor'
-std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseForExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-  if (!CheckThenNext(TOKfor))
-    return nullptr;
-  if (m_token.m_type != TOKidentifier)
-    return nullptr;
-
-  WideStringView wsVariant = m_token.m_string;
-  if (!NextToken())
-    return nullptr;
-  if (!CheckThenNext(TOKassign))
-    return nullptr;
-
-  std::unique_ptr<CXFA_FMSimpleExpression> pAssignment =
-      ParseSimpleExpression();
-  if (!pAssignment)
-    return nullptr;
-
-  int32_t iDirection = 0;
-  if (m_token.m_type == TOKupto)
-    iDirection = 1;
-  else if (m_token.m_type == TOKdownto)
-    iDirection = -1;
-  else
-    return nullptr;
-
-  if (!NextToken())
-    return nullptr;
-
-  std::unique_ptr<CXFA_FMSimpleExpression> pAccessor = ParseSimpleExpression();
-  if (!pAccessor)
-    return nullptr;
-
-  std::unique_ptr<CXFA_FMSimpleExpression> pStep;
-  if (m_token.m_type == TOKstep) {
-    if (!NextToken())
-      return nullptr;
-    pStep = ParseSimpleExpression();
-    if (!pStep)
-      return nullptr;
-  }
-  if (!CheckThenNext(TOKdo))
-    return nullptr;
-
-  auto exprs = ParseExpressionList();
-  if (!CheckThenNext(TOKendfor))
-    return nullptr;
-
-  return pdfium::MakeUnique<CXFA_FMForExpression>(
-      wsVariant, std::move(pAssignment), std::move(pAccessor), iDirection,
-      std::move(pStep),
-      pdfium::MakeUnique<CXFA_FMBlockExpression>(std::move(exprs)));
-}
-
-// Foreach := 'foreach' Identifier 'in' '(' ArgumentList ')'
-//            'do' ExpressionList 'endfor'
-std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseForeachExpression() {
-  if (m_token.m_type != TOKforeach)
-    return nullptr;
-
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-  if (!CheckThenNext(TOKforeach))
-    return nullptr;
-  if (m_token.m_type != TOKidentifier)
-    return nullptr;
-
-  WideStringView wsIdentifier = m_token.m_string;
-  if (!NextToken() || !CheckThenNext(TOKin) || !CheckThenNext(TOKlparen))
-    return nullptr;
-
-  std::vector<std::unique_ptr<CXFA_FMSimpleExpression>> pArgumentList;
-  while (m_token.m_type != TOKrparen) {
-    std::unique_ptr<CXFA_FMSimpleExpression> s = ParseSimpleExpression();
-    if (!s)
-      return nullptr;
-
-    pArgumentList.push_back(std::move(s));
-    if (m_token.m_type != TOKcomma)
-      break;
-    if (!NextToken())
-      return nullptr;
-  }
-  // We must have arguments.
-  if (pArgumentList.empty())
-    return nullptr;
-  if (!CheckThenNext(TOKrparen))
-    return nullptr;
-
-  auto exprs = ParseExpressionList();
-  if (!CheckThenNext(TOKendfor))
-    return nullptr;
-
-  return pdfium::MakeUnique<CXFA_FMForeachExpression>(
-      wsIdentifier, std::move(pArgumentList),
-      pdfium::MakeUnique<CXFA_FMBlockExpression>(std::move(exprs)));
-}
-
-// Block := 'do' ExpressionList 'end'
-std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseDoExpression() {
-  if (m_token.m_type != TOKdo)
-    return nullptr;
-
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-  if (!CheckThenNext(TOKdo))
-    return nullptr;
-
-  auto exprs = ParseExpressionList();
-  if (!CheckThenNext(TOKend))
-    return nullptr;
-
-  return pdfium::MakeUnique<CXFA_FMDoExpression>(
-      pdfium::MakeUnique<CXFA_FMBlockExpression>(std::move(exprs)));
-}
-
-bool CXFA_FMParser::HasError() const {
-  return m_error || m_token.m_type == TOKreserver;
-}
diff --git a/xfa/fxfa/fm2js/cxfa_fmparser.h b/xfa/fxfa/fm2js/cxfa_fmparser.h
deleted file mode 100644
index ad2b367..0000000
--- a/xfa/fxfa/fm2js/cxfa_fmparser.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_FM2JS_CXFA_FMPARSER_H_
-#define XFA_FXFA_FM2JS_CXFA_FMPARSER_H_
-
-#include <memory>
-#include <vector>
-
-#include "xfa/fxfa/fm2js/cxfa_fmexpression.h"
-#include "xfa/fxfa/fm2js/cxfa_fmlexer.h"
-
-class CXFA_FMParser {
- public:
-  explicit CXFA_FMParser(WideStringView wsFormcalc);
-  ~CXFA_FMParser();
-
-  std::unique_ptr<CXFA_FMAST> Parse();
-  bool HasError() const;
-
-  void SetMaxParseDepthForTest(unsigned long max_depth) {
-    m_max_parse_depth = max_depth;
-  }
-
- private:
-  bool NextToken();
-  bool CheckThenNext(XFA_FM_TOKEN op);
-  bool IncrementParseDepthAndCheck();
-
-  std::vector<std::unique_ptr<CXFA_FMExpression>> ParseExpressionList();
-  std::unique_ptr<CXFA_FMExpression> ParseFunction();
-  std::unique_ptr<CXFA_FMExpression> ParseExpression();
-  std::unique_ptr<CXFA_FMExpression> ParseDeclarationExpression();
-  std::unique_ptr<CXFA_FMExpression> ParseExpExpression();
-  std::unique_ptr<CXFA_FMExpression> ParseIfExpression();
-  std::unique_ptr<CXFA_FMExpression> ParseWhileExpression();
-  std::unique_ptr<CXFA_FMExpression> ParseForExpression();
-  std::unique_ptr<CXFA_FMExpression> ParseForeachExpression();
-  std::unique_ptr<CXFA_FMExpression> ParseDoExpression();
-  std::unique_ptr<CXFA_FMSimpleExpression> ParseParenExpression();
-  std::unique_ptr<CXFA_FMSimpleExpression> ParseSimpleExpression();
-  std::unique_ptr<CXFA_FMSimpleExpression> ParseLogicalOrExpression();
-  std::unique_ptr<CXFA_FMSimpleExpression> ParseLogicalAndExpression();
-  std::unique_ptr<CXFA_FMSimpleExpression> ParseEqualityExpression();
-  std::unique_ptr<CXFA_FMSimpleExpression> ParseRelationalExpression();
-  std::unique_ptr<CXFA_FMSimpleExpression> ParseAdditiveExpression();
-  std::unique_ptr<CXFA_FMSimpleExpression> ParseMultiplicativeExpression();
-  std::unique_ptr<CXFA_FMSimpleExpression> ParseUnaryExpression();
-  std::unique_ptr<CXFA_FMSimpleExpression> ParsePrimaryExpression();
-  std::unique_ptr<CXFA_FMSimpleExpression> ParsePostExpression(
-      std::unique_ptr<CXFA_FMSimpleExpression> e);
-  std::unique_ptr<std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>>
-  ParseArgumentList();
-  std::unique_ptr<CXFA_FMSimpleExpression> ParseIndexExpression();
-  std::unique_ptr<CXFA_FMSimpleExpression> ParseLiteral();
-
-  std::unique_ptr<CXFA_FMLexer> m_lexer;
-  CXFA_FMToken m_token;
-  bool m_error;
-  unsigned long m_parse_depth;
-  unsigned long m_max_parse_depth;
-};
-
-#endif  // XFA_FXFA_FM2JS_CXFA_FMPARSER_H_
diff --git a/xfa/fxfa/fm2js/cxfa_fmparser_unittest.cpp b/xfa/fxfa/fm2js/cxfa_fmparser_unittest.cpp
deleted file mode 100644
index 4c5aa46..0000000
--- a/xfa/fxfa/fm2js/cxfa_fmparser_unittest.cpp
+++ /dev/null
@@ -1,516 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "xfa/fxfa/fm2js/cxfa_fmparser.h"
-
-#include <vector>
-
-#include "core/fxcrt/cfx_widetextbuf.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h"
-
-TEST(CXFA_FMParserTest, Empty) {
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(L"");
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  ASSERT_TRUE(ast);
-  EXPECT_FALSE(parser->HasError());
-
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf buf;
-  EXPECT_TRUE(ast->ToJavaScript(&buf));
-  // TODO(dsinclair): This is a little weird .....
-  EXPECT_STREQ(L"// comments only", buf.MakeString().c_str());
-}
-
-TEST(CXFA_FMParserTest, CommentOnlyIsError) {
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(L"; Just comment");
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  ASSERT_TRUE(ast);
-  // TODO(dsinclair): This isn't allowed per the spec.
-  EXPECT_FALSE(parser->HasError());
-  // EXPECT_TRUE(parser->HasError());
-
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf buf;
-  EXPECT_TRUE(ast->ToJavaScript(&buf));
-  EXPECT_STREQ(L"// comments only", buf.MakeString().c_str());
-}
-
-TEST(CXFA_FMParserTest, CommentThenValue) {
-  const wchar_t ret[] =
-      LR"***((function() {
-let pfm_method_runner = function(obj, cb) {
-  if (pfm_rt.is_ary(obj)) {
-    let pfm_method_return = null;
-    for (var idx = obj.length -1; idx > 1; idx--) {
-      pfm_method_return = cb(obj[idx]);
-    }
-    return pfm_method_return;
-  }
-  return cb(obj);
-};
-var pfm_ret = null;
-pfm_ret = 12;
-return pfm_rt.get_val(pfm_ret);
-}).call(this);)***";
-
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(L"; Just comment\n12");
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  ASSERT_TRUE(ast);
-  EXPECT_FALSE(parser->HasError());
-
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf buf;
-  EXPECT_TRUE(ast->ToJavaScript(&buf));
-  EXPECT_STREQ(ret, buf.MakeString().c_str());
-}
-
-TEST(CXFA_FMParserTest, Parse) {
-  const wchar_t input[] =
-      LR"***($ = Avg (-3, 5, -6, 12, -13);
-$ = Avg (Table2..Row[*].Cell1);
-if ($ ne -1)then
-  border.fill.color.value = "255,64,64";
-elseif ($ ne -2) then
-  border.fill.color.value = "128,128,128";
-else
-  border.fill.color.value = "20,170,13";
-endif
-$)***";
-
-  const wchar_t ret[] =
-      LR"***((function() {
-let pfm_method_runner = function(obj, cb) {
-  if (pfm_rt.is_ary(obj)) {
-    let pfm_method_return = null;
-    for (var idx = obj.length -1; idx > 1; idx--) {
-      pfm_method_return = cb(obj[idx]);
-    }
-    return pfm_method_return;
-  }
-  return cb(obj);
-};
-var pfm_ret = null;
-if (pfm_rt.is_obj(this))
-{
-pfm_rt.asgn_val_op(this, pfm_rt.Avg(pfm_rt.neg_op(3), 5, pfm_rt.neg_op(6), 12, pfm_rt.neg_op(13)));
-}
-if (pfm_rt.is_obj(this))
-{
-pfm_rt.asgn_val_op(this, pfm_rt.Avg(pfm_rt.dot_acc(pfm_rt.dotdot_acc(Table2, "Table2", "Row", 1), "", "Cell1", 0, 0)));
-}
-if (pfm_rt.get_val(pfm_rt.neq_op(this, pfm_rt.neg_op(1))))
-{
-if (pfm_rt.is_obj(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0)))
-{
-pfm_rt.asgn_val_op(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0), "255,64,64");
-}
-}
-else if (pfm_rt.get_val(pfm_rt.neq_op(this, pfm_rt.neg_op(2))))
-{
-if (pfm_rt.is_obj(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0)))
-{
-pfm_rt.asgn_val_op(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0), "128,128,128");
-}
-}
-else {
-if (pfm_rt.is_obj(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0)))
-{
-pfm_rt.asgn_val_op(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0), "20,170,13");
-}
-}
-pfm_ret = this;
-return pfm_rt.get_val(pfm_ret);
-}).call(this);)***";
-
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  ASSERT_TRUE(ast);
-  EXPECT_FALSE(parser->HasError());
-
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf buf;
-  EXPECT_TRUE(ast->ToJavaScript(&buf));
-  EXPECT_EQ(ret, buf.AsStringView());
-}
-
-TEST(CXFA_FMParserTest, MaxParseDepth) {
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(L"foo(bar[baz(fizz[0])])");
-  parser->SetMaxParseDepthForTest(5);
-  EXPECT_EQ(nullptr, parser->Parse());
-  EXPECT_TRUE(parser->HasError());
-}
-
-TEST(CFXA_FMParserTest, chromium752201) {
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(
-      LR"***(fTep a
-.#
-fo@ =[=l)***");
-  EXPECT_EQ(nullptr, parser->Parse());
-  EXPECT_TRUE(parser->HasError());
-}
-
-TEST(CXFA_FMParserTest, MultipleAssignmentIsNotAllowed) {
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(L"(a=(b=t))=u");
-
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  ASSERT_TRUE(!ast);
-  EXPECT_TRUE(parser->HasError());
-}
-
-TEST(CXFA_FMParserTest, ParseFuncWithParams) {
-  const wchar_t input[] =
-      LR"***(func MyFunction(param1, param2) do
-  param1 * param2
-endfunc)***";
-
-  const wchar_t ret[] =
-      LR"***((function() {
-let pfm_method_runner = function(obj, cb) {
-  if (pfm_rt.is_ary(obj)) {
-    let pfm_method_return = null;
-    for (var idx = obj.length -1; idx > 1; idx--) {
-      pfm_method_return = cb(obj[idx]);
-    }
-    return pfm_method_return;
-  }
-  return cb(obj);
-};
-var pfm_ret = null;
-function MyFunction(param1, param2) {
-var pfm_ret = null;
-pfm_ret = pfm_rt.mul_op(param1, param2);
-return pfm_ret;
-}
-return pfm_rt.get_val(pfm_ret);
-}).call(this);)***";
-
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  ASSERT_TRUE(ast);
-  EXPECT_FALSE(parser->HasError());
-
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf buf;
-  EXPECT_TRUE(ast->ToJavaScript(&buf));
-  EXPECT_STREQ(ret, buf.MakeString().c_str());
-}
-
-TEST(CXFA_FMParserTest, ParseFuncWithoutParams) {
-  const wchar_t input[] =
-      LR"***(func MyFunction() do
-  42
-endfunc)***";
-
-  const wchar_t ret[] =
-      LR"***((function() {
-let pfm_method_runner = function(obj, cb) {
-  if (pfm_rt.is_ary(obj)) {
-    let pfm_method_return = null;
-    for (var idx = obj.length -1; idx > 1; idx--) {
-      pfm_method_return = cb(obj[idx]);
-    }
-    return pfm_method_return;
-  }
-  return cb(obj);
-};
-var pfm_ret = null;
-function MyFunction() {
-var pfm_ret = null;
-pfm_ret = 42;
-return pfm_ret;
-}
-return pfm_rt.get_val(pfm_ret);
-}).call(this);)***";
-
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  ASSERT_TRUE(ast);
-  EXPECT_FALSE(parser->HasError());
-
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf buf;
-  EXPECT_TRUE(ast->ToJavaScript(&buf));
-  EXPECT_STREQ(ret, buf.MakeString().c_str());
-}
-
-TEST(CXFA_FMParserTest, ParseFuncWithBadParamsList) {
-  const wchar_t input[] =
-      LR"***(func MyFunction(param1,) do
-  param1 * param2
-endfunc)***";
-
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  ASSERT_TRUE(ast == nullptr);
-  EXPECT_TRUE(parser->HasError());
-}
-
-TEST(CXFA_FMParserTest, ParseBadIfExpression) {
-  const wchar_t input[] = L"if ( then";
-
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  ASSERT_TRUE(ast == nullptr);
-  EXPECT_TRUE(parser->HasError());
-}
-
-TEST(CXFA_FMParserTest, ParseBadElseIfExpression) {
-  const wchar_t input[] =
-      LR"***(if ($ ne -1) then"
-elseif( then)***";
-
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  ASSERT_TRUE(ast == nullptr);
-  EXPECT_TRUE(parser->HasError());
-}
-
-TEST(CXFA_FMParserTest, ParseDepthWithWideTree) {
-  const wchar_t input[] = L"a <> b <> c <> d <> e <> f <> g <> h <> i <> j";
-
-  {
-    auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-    std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-    ASSERT_TRUE(ast);
-    EXPECT_TRUE(!parser->HasError());
-  }
-
-  {
-    auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-    parser->SetMaxParseDepthForTest(5);
-    std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-    ASSERT_TRUE(ast == nullptr);
-    EXPECT_TRUE(parser->HasError());
-  }
-}
-
-TEST(CXFA_FMParserTest, ParseCallSmall) {
-  const wchar_t input[] = L"i.f(O)";
-  const wchar_t ret[] =
-      LR"***((function() {
-let pfm_method_runner = function(obj, cb) {
-  if (pfm_rt.is_ary(obj)) {
-    let pfm_method_return = null;
-    for (var idx = obj.length -1; idx > 1; idx--) {
-      pfm_method_return = cb(obj[idx]);
-    }
-    return pfm_method_return;
-  }
-  return cb(obj);
-};
-var pfm_ret = null;
-pfm_ret = pfm_rt.get_val((function() {
-  return pfm_method_runner(i, function(obj) {
-    return obj.f(pfm_rt.get_val(O));
-  });
-}).call(this));
-return pfm_rt.get_val(pfm_ret);
-}).call(this);)***";
-
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  EXPECT_FALSE(parser->HasError());
-
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf buf;
-  EXPECT_TRUE(ast->ToJavaScript(&buf));
-  EXPECT_STREQ(ret, buf.MakeString().c_str());
-}
-
-TEST(CXFA_FMParserTest, ParseCallBig) {
-  const wchar_t input[] = L"i.f(O.e(O.e(O)))";
-  const wchar_t ret[] =
-      LR"***((function() {
-let pfm_method_runner = function(obj, cb) {
-  if (pfm_rt.is_ary(obj)) {
-    let pfm_method_return = null;
-    for (var idx = obj.length -1; idx > 1; idx--) {
-      pfm_method_return = cb(obj[idx]);
-    }
-    return pfm_method_return;
-  }
-  return cb(obj);
-};
-var pfm_ret = null;
-pfm_ret = pfm_rt.get_val((function() {
-  return pfm_method_runner(i, function(obj) {
-    return obj.f(pfm_rt.get_val((function() {
-  return pfm_method_runner(O, function(obj) {
-    return obj.e(pfm_rt.get_val((function() {
-  return pfm_method_runner(O, function(obj) {
-    return obj.e(pfm_rt.get_val(O));
-  });
-}).call(this)));
-  });
-}).call(this)));
-  });
-}).call(this));
-return pfm_rt.get_val(pfm_ret);
-}).call(this);)***";
-
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  EXPECT_FALSE(parser->HasError());
-
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf buf;
-  EXPECT_TRUE(ast->ToJavaScript(&buf));
-  EXPECT_STREQ(ret, buf.MakeString().c_str());
-}
-
-TEST(CXFA_FMParserTest, ParseVar) {
-  const wchar_t input[] = LR"(var s = "")";
-  const wchar_t ret[] =
-      LR"***((function() {
-let pfm_method_runner = function(obj, cb) {
-  if (pfm_rt.is_ary(obj)) {
-    let pfm_method_return = null;
-    for (var idx = obj.length -1; idx > 1; idx--) {
-      pfm_method_return = cb(obj[idx]);
-    }
-    return pfm_method_return;
-  }
-  return cb(obj);
-};
-var pfm_ret = null;
-var s = "";
-s = pfm_rt.var_filter(s);
-pfm_ret = s;
-return pfm_rt.get_val(pfm_ret);
-}).call(this);)***";
-
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  EXPECT_FALSE(parser->HasError());
-
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf buf;
-  EXPECT_TRUE(ast->ToJavaScript(&buf));
-  EXPECT_STREQ(ret, buf.MakeString().c_str());
-}
-
-TEST(CXFA_FMParserTest, ParseFunctionCallNoArguments) {
-  const wchar_t input[] = L"P.x()";
-  const wchar_t ret[] =
-      LR"***((function() {
-let pfm_method_runner = function(obj, cb) {
-  if (pfm_rt.is_ary(obj)) {
-    let pfm_method_return = null;
-    for (var idx = obj.length -1; idx > 1; idx--) {
-      pfm_method_return = cb(obj[idx]);
-    }
-    return pfm_method_return;
-  }
-  return cb(obj);
-};
-var pfm_ret = null;
-pfm_ret = pfm_rt.get_val((function() {
-  return pfm_method_runner(P, function(obj) {
-    return obj.x();
-  });
-}).call(this));
-return pfm_rt.get_val(pfm_ret);
-}).call(this);)***";
-
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  EXPECT_FALSE(parser->HasError());
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf buf;
-  EXPECT_TRUE(ast->ToJavaScript(&buf));
-  EXPECT_STREQ(ret, buf.MakeString().c_str());
-}
-
-TEST(CXFA_FMParserTest, ParseFunctionCallSingleArgument) {
-  const wchar_t input[] = L"P.x(foo)";
-  const wchar_t ret[] =
-      LR"***((function() {
-let pfm_method_runner = function(obj, cb) {
-  if (pfm_rt.is_ary(obj)) {
-    let pfm_method_return = null;
-    for (var idx = obj.length -1; idx > 1; idx--) {
-      pfm_method_return = cb(obj[idx]);
-    }
-    return pfm_method_return;
-  }
-  return cb(obj);
-};
-var pfm_ret = null;
-pfm_ret = pfm_rt.get_val((function() {
-  return pfm_method_runner(P, function(obj) {
-    return obj.x(pfm_rt.get_jsobj(foo));
-  });
-}).call(this));
-return pfm_rt.get_val(pfm_ret);
-}).call(this);)***";
-
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  EXPECT_FALSE(parser->HasError());
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf buf;
-  EXPECT_TRUE(ast->ToJavaScript(&buf));
-  EXPECT_STREQ(ret, buf.MakeString().c_str());
-}
-
-TEST(CXFA_FMParserTest, ParseFunctionCallMultipleArguments) {
-  const wchar_t input[] = L"P.x(foo, bar, baz)";
-  const wchar_t ret[] =
-      LR"***((function() {
-let pfm_method_runner = function(obj, cb) {
-  if (pfm_rt.is_ary(obj)) {
-    let pfm_method_return = null;
-    for (var idx = obj.length -1; idx > 1; idx--) {
-      pfm_method_return = cb(obj[idx]);
-    }
-    return pfm_method_return;
-  }
-  return cb(obj);
-};
-var pfm_ret = null;
-pfm_ret = pfm_rt.get_val((function() {
-  return pfm_method_runner(P, function(obj) {
-    return obj.x(pfm_rt.get_jsobj(foo), pfm_rt.get_val(bar), pfm_rt.get_val(baz));
-  });
-}).call(this));
-return pfm_rt.get_val(pfm_ret);
-}).call(this);)***";
-
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  EXPECT_FALSE(parser->HasError());
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf buf;
-  EXPECT_TRUE(ast->ToJavaScript(&buf));
-  EXPECT_STREQ(ret, buf.MakeString().c_str());
-}
-
-TEST(CXFA_FMParserTest, ParseFunctionCallMissingCommas) {
-  const wchar_t input[] = L"P.x(!foo!bar!baz)";
-
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  ASSERT_TRUE(ast == nullptr);
-  EXPECT_TRUE(parser->HasError());
-}
-
-TEST(CXFA_FMParserTest, ParseFunctionCallTrailingComma) {
-  const wchar_t input[] = L"P.x(foo,bar,baz,)";
-
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  ASSERT_TRUE(ast == nullptr);
-  EXPECT_TRUE(parser->HasError());
-}
-
-TEST(CXFA_FMParserTest, ParseFunctionCallExtraComma) {
-  const wchar_t input[] = L"P.x(foo,bar,,baz)";
-
-  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
-  ASSERT_TRUE(ast == nullptr);
-  EXPECT_TRUE(parser->HasError());
-}
diff --git a/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.cpp b/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.cpp
deleted file mode 100644
index e94f55a..0000000
--- a/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.cpp
+++ /dev/null
@@ -1,692 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "core/fxcrt/autorestorer.h"
-#include "core/fxcrt/cfx_widetextbuf.h"
-#include "core/fxcrt/fx_extension.h"
-#include "third_party/base/logging.h"
-#include "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h"
-
-namespace {
-
-const wchar_t* const g_BuiltInFuncs[] = {
-    L"Abs",          L"Apr",       L"At",       L"Avg",
-    L"Ceil",         L"Choose",    L"Concat",   L"Count",
-    L"Cterm",        L"Date",      L"Date2Num", L"DateFmt",
-    L"Decode",       L"Encode",    L"Eval",     L"Exists",
-    L"Floor",        L"Format",    L"FV",       L"Get",
-    L"HasValue",     L"If",        L"Ipmt",     L"IsoDate2Num",
-    L"IsoTime2Num",  L"Left",      L"Len",      L"LocalDateFmt",
-    L"LocalTimeFmt", L"Lower",     L"Ltrim",    L"Max",
-    L"Min",          L"Mod",       L"NPV",      L"Num2Date",
-    L"Num2GMTime",   L"Num2Time",  L"Oneof",    L"Parse",
-    L"Pmt",          L"Post",      L"PPmt",     L"Put",
-    L"PV",           L"Rate",      L"Ref",      L"Replace",
-    L"Right",        L"Round",     L"Rtrim",    L"Space",
-    L"Str",          L"Stuff",     L"Substr",   L"Sum",
-    L"Term",         L"Time",      L"Time2Num", L"TimeFmt",
-    L"UnitType",     L"UnitValue", L"Upper",    L"Uuid",
-    L"Within",       L"WordNum",
-};
-
-const size_t g_BuiltInFuncsMaxLen = 12;
-
-struct XFA_FMSOMMethod {
-  const wchar_t* m_wsSomMethodName;
-  uint32_t m_dParameters;
-};
-
-const XFA_FMSOMMethod gs_FMSomMethods[] = {
-    {L"absPage", 0x01},
-    {L"absPageInBatch", 0x01},
-    {L"absPageSpan", 0x01},
-    {L"append", 0x01},
-    {L"clear", 0x01},
-    {L"formNodes", 0x01},
-    {L"h", 0x01},
-    {L"insert", 0x03},
-    {L"isRecordGroup", 0x01},
-    {L"page", 0x01},
-    {L"pageSpan", 0x01},
-    {L"remove", 0x01},
-    {L"saveFilteredXML", 0x01},
-    {L"setElement", 0x01},
-    {L"sheet", 0x01},
-    {L"sheetInBatch", 0x01},
-    {L"sign", 0x61},
-    {L"verify", 0x0d},
-    {L"w", 0x01},
-    {L"x", 0x01},
-    {L"y", 0x01},
-};
-
-}  // namespace
-
-CXFA_FMSimpleExpression::CXFA_FMSimpleExpression(XFA_FM_TOKEN op) : m_op(op) {}
-
-XFA_FM_TOKEN CXFA_FMSimpleExpression::GetOperatorToken() const {
-  return m_op;
-}
-
-CXFA_FMNullExpression::CXFA_FMNullExpression()
-    : CXFA_FMSimpleExpression(TOKnull) {}
-
-bool CXFA_FMNullExpression::ToJavaScript(CFX_WideTextBuf* js, ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  *js << "null";
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMNumberExpression::CXFA_FMNumberExpression(WideStringView wsNumber)
-    : CXFA_FMSimpleExpression(TOKnumber), m_wsNumber(wsNumber) {}
-
-CXFA_FMNumberExpression::~CXFA_FMNumberExpression() = default;
-
-bool CXFA_FMNumberExpression::ToJavaScript(CFX_WideTextBuf* js,
-                                           ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  *js << m_wsNumber;
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMStringExpression::CXFA_FMStringExpression(WideStringView wsString)
-    : CXFA_FMSimpleExpression(TOKstring), m_wsString(wsString) {}
-
-CXFA_FMStringExpression::~CXFA_FMStringExpression() = default;
-
-bool CXFA_FMStringExpression::ToJavaScript(CFX_WideTextBuf* js,
-                                           ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  WideString tempStr(m_wsString);
-  if (tempStr.GetLength() <= 2) {
-    *js << tempStr;
-    return !CXFA_IsTooBig(js);
-  }
-
-  *js << "\"";
-  for (size_t i = 1; i < tempStr.GetLength() - 1; i++) {
-    wchar_t oneChar = tempStr[i];
-    switch (oneChar) {
-      case L'\"':
-        ++i;
-        *js << "\\\"";
-        break;
-      case 0x0d:
-        break;
-      case 0x0a:
-        *js << "\\n";
-        break;
-      default:
-        js->AppendChar(oneChar);
-        break;
-    }
-  }
-  *js << "\"";
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMIdentifierExpression::CXFA_FMIdentifierExpression(
-    WideStringView wsIdentifier)
-    : CXFA_FMSimpleExpression(TOKidentifier), m_wsIdentifier(wsIdentifier) {}
-
-CXFA_FMIdentifierExpression::~CXFA_FMIdentifierExpression() = default;
-
-bool CXFA_FMIdentifierExpression::ToJavaScript(CFX_WideTextBuf* js,
-                                               ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  if (m_wsIdentifier.EqualsASCII("$"))
-    *js << "this";
-  else if (m_wsIdentifier.EqualsASCII("!"))
-    *js << "xfa.datasets";
-  else if (m_wsIdentifier.EqualsASCII("$data"))
-    *js << "xfa.datasets.data";
-  else if (m_wsIdentifier.EqualsASCII("$event"))
-    *js << "xfa.event";
-  else if (m_wsIdentifier.EqualsASCII("$form"))
-    *js << "xfa.form";
-  else if (m_wsIdentifier.EqualsASCII("$host"))
-    *js << "xfa.host";
-  else if (m_wsIdentifier.EqualsASCII("$layout"))
-    *js << "xfa.layout";
-  else if (m_wsIdentifier.EqualsASCII("$template"))
-    *js << "xfa.template";
-  else if (m_wsIdentifier[0] == L'!')
-    *js << "pfm__excl__" << m_wsIdentifier.Last(m_wsIdentifier.GetLength() - 1);
-  else
-    *js << m_wsIdentifier;
-
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMAssignExpression::CXFA_FMAssignExpression(
-    XFA_FM_TOKEN op,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMSimpleExpression(op),
-      m_pExp1(std::move(pExp1)),
-      m_pExp2(std::move(pExp2)) {}
-
-CXFA_FMAssignExpression::~CXFA_FMAssignExpression() = default;
-
-bool CXFA_FMAssignExpression::ToJavaScript(CFX_WideTextBuf* js,
-                                           ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  CFX_WideTextBuf tempExp1;
-  if (!m_pExp1->ToJavaScript(&tempExp1, ReturnType::kInfered))
-    return false;
-
-  *js << "if (pfm_rt.is_obj(" << tempExp1 << "))\n{\n";
-  if (type == ReturnType::kImplied)
-    *js << "pfm_ret = ";
-
-  CFX_WideTextBuf tempExp2;
-  if (!m_pExp2->ToJavaScript(&tempExp2, ReturnType::kInfered))
-    return false;
-
-  *js << "pfm_rt.asgn_val_op(" << tempExp1 << ", " << tempExp2 << ");\n}\n";
-
-  if (m_pExp1->GetOperatorToken() == TOKidentifier &&
-      !tempExp1.AsStringView().EqualsASCII("this")) {
-    *js << "else\n{\n";
-    if (type == ReturnType::kImplied)
-      *js << "pfm_ret = ";
-
-    *js << tempExp1 << " = pfm_rt.asgn_val_op";
-    *js << "(" << tempExp1 << ", " << tempExp2 << ");\n";
-    *js << "}\n";
-  }
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMBinExpression::CXFA_FMBinExpression(
-    const WideString& opName,
-    XFA_FM_TOKEN op,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMSimpleExpression(op),
-      m_OpName(opName),
-      m_pExp1(std::move(pExp1)),
-      m_pExp2(std::move(pExp2)) {}
-
-CXFA_FMBinExpression::~CXFA_FMBinExpression() = default;
-
-bool CXFA_FMBinExpression::ToJavaScript(CFX_WideTextBuf* js, ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  *js << "pfm_rt." << m_OpName << "(";
-  if (!m_pExp1->ToJavaScript(js, ReturnType::kInfered))
-    return false;
-  *js << ", ";
-  if (!m_pExp2->ToJavaScript(js, ReturnType::kInfered))
-    return false;
-  *js << ")";
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMLogicalOrExpression::CXFA_FMLogicalOrExpression(
-    XFA_FM_TOKEN op,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(L"log_or_op",
-                           op,
-                           std::move(pExp1),
-                           std::move(pExp2)) {}
-
-CXFA_FMLogicalAndExpression::CXFA_FMLogicalAndExpression(
-    XFA_FM_TOKEN op,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(L"log_and_op",
-                           op,
-                           std::move(pExp1),
-                           std::move(pExp2)) {}
-
-CXFA_FMEqualExpression::CXFA_FMEqualExpression(
-    XFA_FM_TOKEN op,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(L"eq_op",
-                           op,
-                           std::move(pExp1),
-                           std::move(pExp2)) {}
-
-CXFA_FMNotEqualExpression::CXFA_FMNotEqualExpression(
-    XFA_FM_TOKEN op,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(L"neq_op",
-                           op,
-                           std::move(pExp1),
-                           std::move(pExp2)) {}
-
-CXFA_FMGtExpression::CXFA_FMGtExpression(
-    XFA_FM_TOKEN op,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(L"gt_op",
-                           op,
-                           std::move(pExp1),
-                           std::move(pExp2)) {}
-
-CXFA_FMGeExpression::CXFA_FMGeExpression(
-    XFA_FM_TOKEN op,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(L"ge_op",
-                           op,
-                           std::move(pExp1),
-                           std::move(pExp2)) {}
-
-CXFA_FMLtExpression::CXFA_FMLtExpression(
-    XFA_FM_TOKEN op,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(L"lt_op",
-                           op,
-                           std::move(pExp1),
-                           std::move(pExp2)) {}
-
-CXFA_FMLeExpression::CXFA_FMLeExpression(
-    XFA_FM_TOKEN op,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(L"le_op",
-                           op,
-                           std::move(pExp1),
-                           std::move(pExp2)) {}
-
-CXFA_FMPlusExpression::CXFA_FMPlusExpression(
-    XFA_FM_TOKEN op,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(L"plus_op",
-                           op,
-                           std::move(pExp1),
-                           std::move(pExp2)) {}
-
-CXFA_FMMinusExpression::CXFA_FMMinusExpression(
-    XFA_FM_TOKEN op,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(L"minus_op",
-                           op,
-                           std::move(pExp1),
-                           std::move(pExp2)) {}
-
-CXFA_FMMulExpression::CXFA_FMMulExpression(
-    XFA_FM_TOKEN op,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(L"mul_op",
-                           op,
-                           std::move(pExp1),
-                           std::move(pExp2)) {}
-
-CXFA_FMDivExpression::CXFA_FMDivExpression(
-    XFA_FM_TOKEN op,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(L"div_op",
-                           op,
-                           std::move(pExp1),
-                           std::move(pExp2)) {}
-
-CXFA_FMUnaryExpression::CXFA_FMUnaryExpression(
-    const WideString& opName,
-    XFA_FM_TOKEN op,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp)
-    : CXFA_FMSimpleExpression(op), m_OpName(opName), m_pExp(std::move(pExp)) {}
-
-CXFA_FMUnaryExpression::~CXFA_FMUnaryExpression() = default;
-
-bool CXFA_FMUnaryExpression::ToJavaScript(CFX_WideTextBuf* js,
-                                          ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  *js << "pfm_rt." << m_OpName.c_str() << "(";
-  if (!m_pExp->ToJavaScript(js, ReturnType::kInfered))
-    return false;
-  *js << ")";
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMPosExpression::CXFA_FMPosExpression(
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp)
-    : CXFA_FMUnaryExpression(L"pos_op", TOKplus, std::move(pExp)) {}
-
-CXFA_FMNegExpression::CXFA_FMNegExpression(
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp)
-    : CXFA_FMUnaryExpression(L"neg_op", TOKminus, std::move(pExp)) {}
-
-CXFA_FMNotExpression::CXFA_FMNotExpression(
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp)
-    : CXFA_FMUnaryExpression(L"log_not_op", TOKksnot, std::move(pExp)) {}
-
-CXFA_FMCallExpression::CXFA_FMCallExpression(
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp,
-    std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>&& pArguments,
-    bool bIsSomMethod)
-    : CXFA_FMSimpleExpression(TOKcall),
-      m_pExp(std::move(pExp)),
-      m_bIsSomMethod(bIsSomMethod),
-      m_Arguments(std::move(pArguments)) {}
-
-CXFA_FMCallExpression::~CXFA_FMCallExpression() {}
-
-bool CXFA_FMCallExpression::IsBuiltInFunc(CFX_WideTextBuf* funcName) {
-  if (funcName->GetLength() > g_BuiltInFuncsMaxLen)
-    return false;
-
-  WideString str = funcName->MakeString();
-  const wchar_t* const* pMatchResult = std::lower_bound(
-      std::begin(g_BuiltInFuncs), std::end(g_BuiltInFuncs), str,
-      [](const wchar_t* iter, const WideString& val) -> bool {
-        return val.CompareNoCase(iter) > 0;
-      });
-  if (pMatchResult != std::end(g_BuiltInFuncs) &&
-      !str.CompareNoCase(*pMatchResult)) {
-    funcName->Clear();
-    *funcName << *pMatchResult;
-    return true;
-  }
-  return false;
-}
-
-uint32_t CXFA_FMCallExpression::IsMethodWithObjParam(
-    const WideString& methodName) {
-  const XFA_FMSOMMethod* result = std::lower_bound(
-      std::begin(gs_FMSomMethods), std::end(gs_FMSomMethods), methodName,
-      [](const XFA_FMSOMMethod iter, const WideString& val) {
-        return val.Compare(iter.m_wsSomMethodName) > 0;
-      });
-  if (result != std::end(gs_FMSomMethods) &&
-      !methodName.Compare(result->m_wsSomMethodName)) {
-    return result->m_dParameters;
-  }
-  return 0;
-}
-
-bool CXFA_FMCallExpression::ToJavaScript(CFX_WideTextBuf* js, ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  CFX_WideTextBuf funcName;
-  if (!m_pExp->ToJavaScript(&funcName, ReturnType::kInfered))
-    return false;
-
-  if (m_bIsSomMethod) {
-    *js << funcName << "(";
-    uint32_t methodPara = IsMethodWithObjParam(funcName.MakeString());
-    if (methodPara > 0) {
-      for (size_t i = 0; i < m_Arguments.size(); ++i) {
-        // Currently none of our expressions use objects for a parameter over
-        // the 6th. Make sure we don't overflow the shift when doing this
-        // check. If we ever need more the 32 object params we can revisit.
-        *js << "pfm_rt.get_";
-        if (i < 32 && (methodPara & (0x01 << i)) > 0)
-          *js << "jsobj";
-        else
-          *js << "val";
-
-        *js << "(";
-        if (!m_Arguments[i]->ToJavaScript(js, ReturnType::kInfered))
-          return false;
-        *js << ")";
-        if (i + 1 < m_Arguments.size())
-          *js << ", ";
-      }
-    } else {
-      for (const auto& expr : m_Arguments) {
-        *js << "pfm_rt.get_val(";
-        if (!expr->ToJavaScript(js, ReturnType::kInfered))
-          return false;
-        *js << ")";
-        if (expr != m_Arguments.back())
-          *js << ", ";
-      }
-    }
-    *js << ")";
-    return !CXFA_IsTooBig(js);
-  }
-
-  bool isEvalFunc = false;
-  bool isExistsFunc = false;
-  if (!IsBuiltInFunc(&funcName)) {
-    // If a function is not a SomMethod or a built-in then the input was
-    // invalid, so failing. The scanner/lexer should catch this, but currently
-    // doesn't. This failure will bubble up to the top-level and cause the
-    // transpile to fail.
-    return false;
-  }
-
-  if (funcName.AsStringView().EqualsASCII("Eval")) {
-    isEvalFunc = true;
-    *js << "eval.call(this, pfm_rt.Translate";
-  } else {
-    if (funcName.AsStringView().EqualsASCII("Exists"))
-      isExistsFunc = true;
-
-    *js << "pfm_rt." << funcName;
-  }
-
-  *js << "(";
-  if (isExistsFunc) {
-    *js << "\n(\nfunction ()\n{\ntry\n{\n";
-    if (!m_Arguments.empty()) {
-      *js << "return ";
-      if (!m_Arguments[0]->ToJavaScript(js, ReturnType::kInfered))
-        return false;
-      *js << ";\n}\n";
-    } else {
-      *js << "return 0;\n}\n";
-    }
-    *js << "catch(accessExceptions)\n";
-    *js << "{\nreturn 0;\n}\n}\n).call(this)\n";
-  } else {
-    for (const auto& expr : m_Arguments) {
-      if (!expr->ToJavaScript(js, ReturnType::kInfered))
-        return false;
-      if (expr != m_Arguments.back())
-        *js << ", ";
-    }
-  }
-  *js << ")";
-  if (isEvalFunc)
-    *js << ")";
-
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMDotAccessorExpression::CXFA_FMDotAccessorExpression(
-    std::unique_ptr<CXFA_FMSimpleExpression> pAccessor,
-    XFA_FM_TOKEN op,
-    WideStringView wsIdentifier,
-    std::unique_ptr<CXFA_FMSimpleExpression> pIndexExp)
-    : CXFA_FMSimpleExpression(op),
-      m_wsIdentifier(wsIdentifier),
-      m_pExp1(std::move(pAccessor)),
-      m_pExp2(std::move(pIndexExp)) {}
-
-CXFA_FMDotAccessorExpression::~CXFA_FMDotAccessorExpression() = default;
-
-bool CXFA_FMDotAccessorExpression::ToJavaScript(CFX_WideTextBuf* js,
-                                                ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  *js << "pfm_rt.dot_acc(";
-
-  CFX_WideTextBuf tempExp1;
-  if (m_pExp1) {
-    if (!m_pExp1->ToJavaScript(&tempExp1, ReturnType::kInfered))
-      return false;
-
-    *js << tempExp1;
-  } else {
-    *js << "null";
-  }
-  *js << ", \"";
-
-  if (m_pExp1 && m_pExp1->GetOperatorToken() == TOKidentifier)
-    *js << tempExp1;
-
-  *js << "\", ";
-  if (m_op == TOKdotscream)
-    *js << "\"#" << m_wsIdentifier << "\", ";
-  else if (m_op == TOKdotstar)
-    *js << "\"*\", ";
-  else if (m_op == TOKcall)
-    *js << "\"\", ";
-  else
-    *js << "\"" << m_wsIdentifier << "\", ";
-
-  if (!m_pExp2->ToJavaScript(js, ReturnType::kInfered))
-    return false;
-
-  *js << ")";
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMIndexExpression::CXFA_FMIndexExpression(
-    XFA_FM_AccessorIndex accessorIndex,
-    std::unique_ptr<CXFA_FMSimpleExpression> pIndexExp,
-    bool bIsStarIndex)
-    : CXFA_FMSimpleExpression(TOKlbracket),
-      m_pExp(std::move(pIndexExp)),
-      m_accessorIndex(accessorIndex),
-      m_bIsStarIndex(bIsStarIndex) {}
-
-CXFA_FMIndexExpression::~CXFA_FMIndexExpression() = default;
-
-bool CXFA_FMIndexExpression::ToJavaScript(CFX_WideTextBuf* js,
-                                          ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  switch (m_accessorIndex) {
-    case ACCESSOR_NO_INDEX:
-      *js << "0";
-      break;
-    case ACCESSOR_NO_RELATIVEINDEX:
-      *js << "1";
-      break;
-    case ACCESSOR_POSITIVE_INDEX:
-      *js << "2";
-      break;
-    case ACCESSOR_NEGATIVE_INDEX:
-      *js << "3";
-      break;
-    default:
-      *js << "0";
-  }
-  if (m_bIsStarIndex)
-    return !CXFA_IsTooBig(js);
-
-  *js << ", ";
-  if (m_pExp) {
-    if (!m_pExp->ToJavaScript(js, ReturnType::kInfered))
-      return false;
-  } else {
-    *js << "0";
-  }
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMDotDotAccessorExpression::CXFA_FMDotDotAccessorExpression(
-    std::unique_ptr<CXFA_FMSimpleExpression> pAccessor,
-    XFA_FM_TOKEN op,
-    WideStringView wsIdentifier,
-    std::unique_ptr<CXFA_FMSimpleExpression> pIndexExp)
-    : CXFA_FMSimpleExpression(op),
-      m_wsIdentifier(wsIdentifier),
-      m_pExp1(std::move(pAccessor)),
-      m_pExp2(std::move(pIndexExp)) {}
-
-CXFA_FMDotDotAccessorExpression::~CXFA_FMDotDotAccessorExpression() = default;
-
-bool CXFA_FMDotDotAccessorExpression::ToJavaScript(CFX_WideTextBuf* js,
-                                                   ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  *js << "pfm_rt.dotdot_acc(";
-  if (!m_pExp1->ToJavaScript(js, ReturnType::kInfered))
-    return false;
-  *js << ", "
-      << "\"";
-  if (m_pExp1->GetOperatorToken() == TOKidentifier) {
-    if (!m_pExp1->ToJavaScript(js, ReturnType::kInfered))
-      return false;
-  }
-
-  *js << "\", \"" << m_wsIdentifier << "\", ";
-  if (!m_pExp2->ToJavaScript(js, ReturnType::kInfered))
-    return false;
-  *js << ")";
-  return !CXFA_IsTooBig(js);
-}
-
-CXFA_FMMethodCallExpression::CXFA_FMMethodCallExpression(
-    std::unique_ptr<CXFA_FMSimpleExpression> pAccessorExp1,
-    std::unique_ptr<CXFA_FMSimpleExpression> pCallExp)
-    : CXFA_FMSimpleExpression(TOKdot),
-      m_pExp1(std::move(pAccessorExp1)),
-      m_pExp2(std::move(pCallExp)) {}
-
-CXFA_FMMethodCallExpression::~CXFA_FMMethodCallExpression() = default;
-
-bool CXFA_FMMethodCallExpression::ToJavaScript(CFX_WideTextBuf* js,
-                                               ReturnType type) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  CFX_WideTextBuf buf;
-  if (!m_pExp1->ToJavaScript(&buf, ReturnType::kInfered))
-    return false;
-
-  *js << "(function() {\n";
-  *js << "  return pfm_method_runner(" << buf << ", function(obj) {\n";
-  *js << "    return obj.";
-  if (!m_pExp2->ToJavaScript(js, ReturnType::kInfered))
-    return false;
-  *js << ";\n";
-  *js << "  });\n";
-  *js << "}).call(this)";
-  return !CXFA_IsTooBig(js);
-}
-
-bool CXFA_IsTooBig(const CFX_WideTextBuf* js) {
-  return js->GetSize() >= 256 * 1024 * 1024;
-}
diff --git a/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h b/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h
deleted file mode 100644
index ae8f38e..0000000
--- a/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h
+++ /dev/null
@@ -1,325 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_FM2JS_CXFA_FMSIMPLEEXPRESSION_H_
-#define XFA_FXFA_FM2JS_CXFA_FMSIMPLEEXPRESSION_H_
-
-#include <memory>
-#include <vector>
-
-#include "xfa/fxfa/fm2js/cxfa_fmlexer.h"
-
-enum XFA_FM_AccessorIndex {
-  ACCESSOR_NO_INDEX,
-  ACCESSOR_NO_RELATIVEINDEX,
-  ACCESSOR_POSITIVE_INDEX,
-  ACCESSOR_NEGATIVE_INDEX
-};
-
-enum class ReturnType { kImplied, kInfered };
-
-class CFX_WideTextBuf;
-
-class CXFA_FMSimpleExpression {
- public:
-  virtual ~CXFA_FMSimpleExpression() = default;
-  virtual bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) = 0;
-
-  XFA_FM_TOKEN GetOperatorToken() const;
-
- protected:
-  explicit CXFA_FMSimpleExpression(XFA_FM_TOKEN op);
-
-  const XFA_FM_TOKEN m_op;
-};
-
-class CXFA_FMNullExpression final : public CXFA_FMSimpleExpression {
- public:
-  CXFA_FMNullExpression();
-  ~CXFA_FMNullExpression() override {}
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-};
-
-class CXFA_FMNumberExpression final : public CXFA_FMSimpleExpression {
- public:
-  explicit CXFA_FMNumberExpression(WideStringView wsNumber);
-  ~CXFA_FMNumberExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  WideStringView m_wsNumber;
-};
-
-class CXFA_FMStringExpression final : public CXFA_FMSimpleExpression {
- public:
-  explicit CXFA_FMStringExpression(WideStringView wsString);
-  ~CXFA_FMStringExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  WideStringView m_wsString;
-};
-
-class CXFA_FMIdentifierExpression final : public CXFA_FMSimpleExpression {
- public:
-  explicit CXFA_FMIdentifierExpression(WideStringView wsIdentifier);
-  ~CXFA_FMIdentifierExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  WideStringView m_wsIdentifier;
-};
-
-class CXFA_FMAssignExpression final : public CXFA_FMSimpleExpression {
- public:
-  CXFA_FMAssignExpression(XFA_FM_TOKEN op,
-                          std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                          std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMAssignExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp1;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp2;
-};
-
-class CXFA_FMBinExpression : public CXFA_FMSimpleExpression {
- public:
-  ~CXFA_FMBinExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- protected:
-  CXFA_FMBinExpression(const WideString& opName,
-                       XFA_FM_TOKEN op,
-                       std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                       std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-
- private:
-  WideString m_OpName;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp1;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp2;
-};
-
-class CXFA_FMLogicalOrExpression final : public CXFA_FMBinExpression {
- public:
-  CXFA_FMLogicalOrExpression(XFA_FM_TOKEN op,
-                             std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                             std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMLogicalOrExpression() override {}
-};
-
-class CXFA_FMLogicalAndExpression final : public CXFA_FMBinExpression {
- public:
-  CXFA_FMLogicalAndExpression(XFA_FM_TOKEN op,
-                              std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                              std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMLogicalAndExpression() override {}
-};
-
-class CXFA_FMEqualExpression final : public CXFA_FMBinExpression {
- public:
-  CXFA_FMEqualExpression(XFA_FM_TOKEN op,
-                         std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                         std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMEqualExpression() override {}
-};
-
-class CXFA_FMNotEqualExpression final : public CXFA_FMBinExpression {
- public:
-  CXFA_FMNotEqualExpression(XFA_FM_TOKEN op,
-                            std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                            std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMNotEqualExpression() override {}
-};
-
-class CXFA_FMGtExpression final : public CXFA_FMBinExpression {
- public:
-  CXFA_FMGtExpression(XFA_FM_TOKEN op,
-                      std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                      std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMGtExpression() override {}
-};
-
-class CXFA_FMGeExpression final : public CXFA_FMBinExpression {
- public:
-  CXFA_FMGeExpression(XFA_FM_TOKEN op,
-                      std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                      std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMGeExpression() override {}
-};
-
-class CXFA_FMLtExpression final : public CXFA_FMBinExpression {
- public:
-  CXFA_FMLtExpression(XFA_FM_TOKEN op,
-                      std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                      std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMLtExpression() override {}
-};
-
-class CXFA_FMLeExpression final : public CXFA_FMBinExpression {
- public:
-  CXFA_FMLeExpression(XFA_FM_TOKEN op,
-                      std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                      std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMLeExpression() override {}
-};
-
-class CXFA_FMPlusExpression final : public CXFA_FMBinExpression {
- public:
-  CXFA_FMPlusExpression(XFA_FM_TOKEN op,
-                        std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                        std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMPlusExpression() override {}
-};
-
-class CXFA_FMMinusExpression final : public CXFA_FMBinExpression {
- public:
-  CXFA_FMMinusExpression(XFA_FM_TOKEN op,
-                         std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                         std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMMinusExpression() override {}
-};
-
-class CXFA_FMMulExpression final : public CXFA_FMBinExpression {
- public:
-  CXFA_FMMulExpression(XFA_FM_TOKEN op,
-                       std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                       std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMMulExpression() override {}
-};
-
-class CXFA_FMDivExpression final : public CXFA_FMBinExpression {
- public:
-  CXFA_FMDivExpression(XFA_FM_TOKEN op,
-                       std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                       std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMDivExpression() override {}
-};
-
-class CXFA_FMUnaryExpression : public CXFA_FMSimpleExpression {
- public:
-  ~CXFA_FMUnaryExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- protected:
-  CXFA_FMUnaryExpression(const WideString& opName,
-                         XFA_FM_TOKEN op,
-                         std::unique_ptr<CXFA_FMSimpleExpression> pExp);
-
- private:
-  WideString m_OpName;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp;
-};
-
-class CXFA_FMPosExpression final : public CXFA_FMUnaryExpression {
- public:
-  explicit CXFA_FMPosExpression(std::unique_ptr<CXFA_FMSimpleExpression> pExp);
-  ~CXFA_FMPosExpression() override {}
-};
-
-class CXFA_FMNegExpression final : public CXFA_FMUnaryExpression {
- public:
-  explicit CXFA_FMNegExpression(std::unique_ptr<CXFA_FMSimpleExpression> pExp);
-  ~CXFA_FMNegExpression() override {}
-};
-
-class CXFA_FMNotExpression final : public CXFA_FMUnaryExpression {
- public:
-  explicit CXFA_FMNotExpression(std::unique_ptr<CXFA_FMSimpleExpression> pExp);
-  ~CXFA_FMNotExpression() override {}
-};
-
-class CXFA_FMCallExpression final : public CXFA_FMSimpleExpression {
- public:
-  CXFA_FMCallExpression(
-      std::unique_ptr<CXFA_FMSimpleExpression> pExp,
-      std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>&& pArguments,
-      bool bIsSomMethod);
-  ~CXFA_FMCallExpression() override;
-
-  bool IsBuiltInFunc(CFX_WideTextBuf* funcName);
-  uint32_t IsMethodWithObjParam(const WideString& methodName);
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp;
-  bool m_bIsSomMethod;
-  std::vector<std::unique_ptr<CXFA_FMSimpleExpression>> m_Arguments;
-};
-
-class CXFA_FMDotAccessorExpression final : public CXFA_FMSimpleExpression {
- public:
-  CXFA_FMDotAccessorExpression(
-      std::unique_ptr<CXFA_FMSimpleExpression> pAccessor,
-      XFA_FM_TOKEN op,
-      WideStringView wsIdentifier,
-      std::unique_ptr<CXFA_FMSimpleExpression> pIndexExp);
-  ~CXFA_FMDotAccessorExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  WideStringView m_wsIdentifier;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp1;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp2;
-};
-
-class CXFA_FMIndexExpression final : public CXFA_FMSimpleExpression {
- public:
-  CXFA_FMIndexExpression(XFA_FM_AccessorIndex accessorIndex,
-                         std::unique_ptr<CXFA_FMSimpleExpression> pIndexExp,
-                         bool bIsStarIndex);
-  ~CXFA_FMIndexExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp;
-  XFA_FM_AccessorIndex m_accessorIndex;
-  bool m_bIsStarIndex;
-};
-
-class CXFA_FMDotDotAccessorExpression final : public CXFA_FMSimpleExpression {
- public:
-  CXFA_FMDotDotAccessorExpression(
-      std::unique_ptr<CXFA_FMSimpleExpression> pAccessor,
-      XFA_FM_TOKEN op,
-      WideStringView wsIdentifier,
-      std::unique_ptr<CXFA_FMSimpleExpression> pIndexExp);
-  ~CXFA_FMDotDotAccessorExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  WideStringView m_wsIdentifier;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp1;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp2;
-};
-
-class CXFA_FMMethodCallExpression final : public CXFA_FMSimpleExpression {
- public:
-  CXFA_FMMethodCallExpression(
-      std::unique_ptr<CXFA_FMSimpleExpression> pAccessorExp1,
-      std::unique_ptr<CXFA_FMSimpleExpression> pCallExp);
-  ~CXFA_FMMethodCallExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp1;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp2;
-};
-
-bool CXFA_IsTooBig(const CFX_WideTextBuf* js);
-
-#endif  // XFA_FXFA_FM2JS_CXFA_FMSIMPLEEXPRESSION_H_
diff --git a/xfa/fxfa/fm2js/cxfa_fmsimpleexpression_unittest.cpp b/xfa/fxfa/fm2js/cxfa_fmsimpleexpression_unittest.cpp
deleted file mode 100644
index 198087c..0000000
--- a/xfa/fxfa/fm2js/cxfa_fmsimpleexpression_unittest.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h"
-
-#include <memory>
-#include <utility>
-
-#include "core/fxcrt/cfx_widetextbuf.h"
-#include "core/fxcrt/fx_string.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fxfa/fm2js/cxfa_fmlexer.h"
-#include "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h"
-
-TEST(FMCallExpressionTest, more_than_32_arguments) {
-  // Use sign as it has 3 object parameters at positions 0, 5, and 6.
-  auto exp = pdfium::MakeUnique<CXFA_FMIdentifierExpression>(L"sign");
-
-  std::vector<std::unique_ptr<CXFA_FMSimpleExpression>> args;
-  for (size_t i = 0; i < 50; i++)
-    args.push_back(pdfium::MakeUnique<CXFA_FMNullExpression>());
-
-  CXFA_FMToJavaScriptDepth::Reset();
-  CXFA_FMCallExpression callExp(std::move(exp), std::move(args), true);
-
-  CFX_WideTextBuf js;
-  callExp.ToJavaScript(&js, ReturnType::kInfered);
-
-  // Generate the result javascript string.
-  WideString result = L"sign(";
-  for (size_t i = 0; i < 50; i++) {
-    if (i > 0)
-      result += L", ";
-
-    result += L"pfm_rt.get_";
-    // Object positions for sign() method.
-    if (i == 0 || i == 5 || i == 6)
-      result += L"jsobj(null)";
-    else
-      result += L"val(null)";
-  }
-  result += L")";
-
-  EXPECT_EQ(result.AsStringView(), js.AsStringView());
-}
-
-TEST(FMStringExpressionTest, Empty) {
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf accumulator;
-  CXFA_FMStringExpression(L"").ToJavaScript(&accumulator, ReturnType::kInfered);
-  EXPECT_EQ(L"", accumulator.AsStringView());
-}
-
-TEST(FMStringExpressionTest, Short) {
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf accumulator;
-  CXFA_FMStringExpression(L"a").ToJavaScript(&accumulator,
-                                             ReturnType::kInfered);
-  EXPECT_EQ(L"a", accumulator.AsStringView());
-}
-
-TEST(FMStringExpressionTest, Medium) {
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf accumulator;
-  CXFA_FMStringExpression(L".abcd.").ToJavaScript(&accumulator,
-                                                  ReturnType::kInfered);
-  EXPECT_EQ(L"\"abcd\"", accumulator.AsStringView());
-}
-
-TEST(FMStringExpressionTest, Long) {
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf accumulator;
-  std::vector<WideStringView::UnsignedType> vec(140000, L'A');
-  CXFA_FMStringExpression(WideStringView(vec))
-      .ToJavaScript(&accumulator, ReturnType::kInfered);
-  EXPECT_EQ(140000u, accumulator.GetLength());
-}
-
-TEST(FMStringExpressionTest, Quoted) {
-  CXFA_FMToJavaScriptDepth::Reset();
-  CFX_WideTextBuf accumulator;
-  CXFA_FMStringExpression(L".Simon says \"\"run\"\".")
-      .ToJavaScript(&accumulator, ReturnType::kInfered);
-  EXPECT_EQ(L"\"Simon says \\\"run\\\"\"", accumulator.AsStringView());
-}
diff --git a/xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.cpp b/xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.cpp
deleted file mode 100644
index 023516b..0000000
--- a/xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h"
-
-unsigned long CXFA_FMToJavaScriptDepth::depth_ = 0;
-
-void CXFA_FMToJavaScriptDepth::Reset() {
-  depth_ = 0;
-}
diff --git a/xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h b/xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h
deleted file mode 100644
index f4cd1be..0000000
--- a/xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef XFA_FXFA_FM2JS_CXFA_FMTOJAVASCRIPTDEPTH_H_
-#define XFA_FXFA_FM2JS_CXFA_FMTOJAVASCRIPTDEPTH_H_
-
-class CXFA_FMToJavaScriptDepth {
- public:
-  CXFA_FMToJavaScriptDepth() { depth_++; }
-  ~CXFA_FMToJavaScriptDepth() { depth_--; }
-
-  bool IsWithinMaxDepth() const { return depth_ <= kMaxDepth; }
-
-  static void Reset();
-
- private:
-  // Arbitarily picked by looking at how deep a translation got before hitting
-  // the getting fuzzer memory limits. Should be larger then |kMaxParseDepth| in
-  // cxfa_fmparser.cpp.
-  const unsigned long kMaxDepth = 5000;
-
-  static unsigned long depth_;
-};
-
-#endif  // XFA_FXFA_FM2JS_CXFA_FMTOJAVASCRIPTDEPTH_H_
diff --git a/xfa/fxfa/formcalc/BUILD.gn b/xfa/fxfa/formcalc/BUILD.gn
new file mode 100644
index 0000000..114c730
--- /dev/null
+++ b/xfa/fxfa/formcalc/BUILD.gn
@@ -0,0 +1,44 @@
+# Copyright 2018 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../../pdfium.gni")
+import("../../../testing/test.gni")
+
+assert(pdf_enable_xfa)
+
+source_set("formcalc") {
+  sources = [
+    "cxfa_fmexpression.cpp",
+    "cxfa_fmexpression.h",
+    "cxfa_fmlexer.cpp",
+    "cxfa_fmlexer.h",
+    "cxfa_fmparser.cpp",
+    "cxfa_fmparser.h",
+    "cxfa_fmtojavascriptdepth.cpp",
+    "cxfa_fmtojavascriptdepth.h",
+  ]
+  configs += [
+    "../../../:pdfium_strict_config",
+    "../../../:pdfium_noshorten_config",
+    "../../:xfa_warnings",
+  ]
+  deps = [
+    "../../../core/fxcrt",
+    "../../../fxjs:gc",
+  ]
+  visibility = [ "../../../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [
+    "cxfa_fmexpression_unittest.cpp",
+    "cxfa_fmlexer_unittest.cpp",
+    "cxfa_fmparser_unittest.cpp",
+  ]
+  deps = [
+    ":formcalc",
+    "../../../fxjs:gc",
+  ]
+  pdfium_root_dir = "../../../"
+}
diff --git a/xfa/fxfa/formcalc/DEPS b/xfa/fxfa/formcalc/DEPS
new file mode 100644
index 0000000..c921aa3
--- /dev/null
+++ b/xfa/fxfa/formcalc/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  '-xfa/fwl',
+]
diff --git a/xfa/fxfa/formcalc/cxfa_fmexpression.cpp b/xfa/fxfa/formcalc/cxfa_fmexpression.cpp
new file mode 100644
index 0000000..ca4de2c
--- /dev/null
+++ b/xfa/fxfa/formcalc/cxfa_fmexpression.cpp
@@ -0,0 +1,1192 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fxfa/formcalc/cxfa_fmexpression.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "core/fxcrt/autorestorer.h"
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/widetext_buffer.h"
+#include "fxjs/gc/container_trace.h"
+#include "third_party/base/check.h"
+#include "v8/include/cppgc/visitor.h"
+#include "xfa/fxfa/formcalc/cxfa_fmtojavascriptdepth.h"
+
+namespace {
+
+const wchar_t kLessEqual[] = L" <= ";
+const wchar_t kGreaterEqual[] = L" >= ";
+const wchar_t kPlusEqual[] = L" += ";
+const wchar_t kMinusEqual[] = L" -= ";
+
+const wchar_t* const kBuiltInFuncs[] = {
+    L"Abs",          L"Apr",       L"At",       L"Avg",
+    L"Ceil",         L"Choose",    L"Concat",   L"Count",
+    L"Cterm",        L"Date",      L"Date2Num", L"DateFmt",
+    L"Decode",       L"Encode",    L"Eval",     L"Exists",
+    L"Floor",        L"Format",    L"FV",       L"Get",
+    L"HasValue",     L"If",        L"Ipmt",     L"IsoDate2Num",
+    L"IsoTime2Num",  L"Left",      L"Len",      L"LocalDateFmt",
+    L"LocalTimeFmt", L"Lower",     L"Ltrim",    L"Max",
+    L"Min",          L"Mod",       L"NPV",      L"Num2Date",
+    L"Num2GMTime",   L"Num2Time",  L"Oneof",    L"Parse",
+    L"Pmt",          L"Post",      L"PPmt",     L"Put",
+    L"PV",           L"Rate",      L"Ref",      L"Replace",
+    L"Right",        L"Round",     L"Rtrim",    L"Space",
+    L"Str",          L"Stuff",     L"Substr",   L"Sum",
+    L"Term",         L"Time",      L"Time2Num", L"TimeFmt",
+    L"UnitType",     L"UnitValue", L"Upper",    L"Uuid",
+    L"Within",       L"WordNum",
+};
+
+const size_t kBuiltInFuncsMaxLen = 12;
+
+struct XFA_FMSOMMethod {
+  const wchar_t* m_wsSomMethodName;  // Ok, POD struct.
+  uint32_t m_dParameters;
+};
+
+const XFA_FMSOMMethod kFMSomMethods[] = {
+    {L"absPage", 0x01},
+    {L"absPageInBatch", 0x01},
+    {L"absPageSpan", 0x01},
+    {L"append", 0x01},
+    {L"clear", 0x01},
+    {L"formNodes", 0x01},
+    {L"h", 0x01},
+    {L"insert", 0x03},
+    {L"isRecordGroup", 0x01},
+    {L"page", 0x01},
+    {L"pageSpan", 0x01},
+    {L"remove", 0x01},
+    {L"saveFilteredXML", 0x01},
+    {L"setElement", 0x01},
+    {L"sheet", 0x01},
+    {L"sheetInBatch", 0x01},
+    {L"sign", 0x61},
+    {L"verify", 0x0d},
+    {L"w", 0x01},
+    {L"x", 0x01},
+    {L"y", 0x01},
+};
+
+WideString IdentifierToName(const WideString& ident) {
+  if (ident.IsEmpty() || ident[0] != L'!')
+    return ident;
+  return L"pfm__excl__" + ident.Last(ident.GetLength() - 1);
+}
+
+}  // namespace
+
+CXFA_FMExpression::CXFA_FMExpression() = default;
+
+CXFA_FMExpression::~CXFA_FMExpression() = default;
+
+void CXFA_FMExpression::Trace(cppgc::Visitor* visitor) const {}
+
+CXFA_FMSimpleExpression::CXFA_FMSimpleExpression(XFA_FM_TOKEN op) : m_op(op) {}
+
+CXFA_FMSimpleExpression::~CXFA_FMSimpleExpression() = default;
+
+CXFA_FMChainableExpression::CXFA_FMChainableExpression(
+    XFA_FM_TOKEN op,
+    CXFA_FMSimpleExpression* pExp1,
+    CXFA_FMSimpleExpression* pExp2)
+    : CXFA_FMSimpleExpression(op), m_pExp1(pExp1), m_pExp2(pExp2) {}
+
+CXFA_FMChainableExpression::~CXFA_FMChainableExpression() = default;
+
+void CXFA_FMChainableExpression::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FMSimpleExpression::Trace(visitor);
+  visitor->Trace(m_pExp1);
+  visitor->Trace(m_pExp2);
+}
+
+CXFA_FMNullExpression::CXFA_FMNullExpression()
+    : CXFA_FMSimpleExpression(TOKnull) {}
+
+CXFA_FMNullExpression::~CXFA_FMNullExpression() = default;
+
+bool CXFA_FMNullExpression::ToJavaScript(WideTextBuffer* js,
+                                         ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  *js << "null";
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMNumberExpression::CXFA_FMNumberExpression(WideString wsNumber)
+    : CXFA_FMSimpleExpression(TOKnumber), m_wsNumber(std::move(wsNumber)) {}
+
+CXFA_FMNumberExpression::~CXFA_FMNumberExpression() = default;
+
+bool CXFA_FMNumberExpression::ToJavaScript(WideTextBuffer* js,
+                                           ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  *js << m_wsNumber;
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMStringExpression::CXFA_FMStringExpression(WideString wsString)
+    : CXFA_FMSimpleExpression(TOKstring), m_wsString(std::move(wsString)) {}
+
+CXFA_FMStringExpression::~CXFA_FMStringExpression() = default;
+
+bool CXFA_FMStringExpression::ToJavaScript(WideTextBuffer* js,
+                                           ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  WideString tempStr(m_wsString);
+  if (tempStr.GetLength() <= 2) {
+    *js << tempStr;
+    return !CXFA_IsTooBig(*js);
+  }
+
+  *js << "\"";
+  for (size_t i = 1; i < tempStr.GetLength() - 1; i++) {
+    wchar_t oneChar = tempStr[i];
+    switch (oneChar) {
+      case L'\"':
+        ++i;
+        *js << "\\\"";
+        break;
+      case 0x0d:
+        break;
+      case 0x0a:
+        *js << "\\n";
+        break;
+      default:
+        js->AppendChar(oneChar);
+        break;
+    }
+  }
+  *js << "\"";
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMIdentifierExpression::CXFA_FMIdentifierExpression(
+    WideString wsIdentifier)
+    : CXFA_FMSimpleExpression(TOKidentifier),
+      m_wsIdentifier(std::move(wsIdentifier)) {}
+
+CXFA_FMIdentifierExpression::~CXFA_FMIdentifierExpression() = default;
+
+bool CXFA_FMIdentifierExpression::ToJavaScript(WideTextBuffer* js,
+                                               ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  if (m_wsIdentifier.EqualsASCII("$"))
+    *js << "this";
+  else if (m_wsIdentifier.EqualsASCII("!"))
+    *js << "xfa.datasets";
+  else if (m_wsIdentifier.EqualsASCII("$data"))
+    *js << "xfa.datasets.data";
+  else if (m_wsIdentifier.EqualsASCII("$event"))
+    *js << "xfa.event";
+  else if (m_wsIdentifier.EqualsASCII("$form"))
+    *js << "xfa.form";
+  else if (m_wsIdentifier.EqualsASCII("$host"))
+    *js << "xfa.host";
+  else if (m_wsIdentifier.EqualsASCII("$layout"))
+    *js << "xfa.layout";
+  else if (m_wsIdentifier.EqualsASCII("$template"))
+    *js << "xfa.template";
+  else if (m_wsIdentifier[0] == L'!')
+    *js << "pfm__excl__" << m_wsIdentifier.Last(m_wsIdentifier.GetLength() - 1);
+  else
+    *js << m_wsIdentifier;
+
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMAssignExpression::CXFA_FMAssignExpression(XFA_FM_TOKEN op,
+                                                 CXFA_FMSimpleExpression* pExp1,
+                                                 CXFA_FMSimpleExpression* pExp2)
+    : CXFA_FMChainableExpression(op, pExp1, pExp2) {}
+
+CXFA_FMAssignExpression::~CXFA_FMAssignExpression() = default;
+
+bool CXFA_FMAssignExpression::ToJavaScript(WideTextBuffer* js,
+                                           ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  WideTextBuffer tempExp1;
+  const CXFA_FMSimpleExpression* exp1 = GetFirstExpression();
+  if (!exp1->ToJavaScript(&tempExp1, ReturnType::kInferred))
+    return false;
+
+  *js << "if (pfm_rt.is_obj(" << tempExp1 << "))\n{\n";
+  if (type == ReturnType::kImplied)
+    *js << "pfm_ret = ";
+
+  WideTextBuffer tempExp2;
+  const CXFA_FMSimpleExpression* exp2 = GetSecondExpression();
+  if (!exp2->ToJavaScript(&tempExp2, ReturnType::kInferred))
+    return false;
+
+  *js << "pfm_rt.asgn_val_op(" << tempExp1 << ", " << tempExp2 << ");\n}\n";
+
+  if (exp1->GetOperatorToken() == TOKidentifier &&
+      !tempExp1.AsStringView().EqualsASCII("this")) {
+    *js << "else\n{\n";
+    if (type == ReturnType::kImplied)
+      *js << "pfm_ret = ";
+
+    *js << tempExp1 << " = pfm_rt.asgn_val_op";
+    *js << "(" << tempExp1 << ", " << tempExp2 << ");\n";
+    *js << "}\n";
+  }
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMBinExpression::CXFA_FMBinExpression(const WideString& opName,
+                                           XFA_FM_TOKEN op,
+                                           CXFA_FMSimpleExpression* pExp1,
+                                           CXFA_FMSimpleExpression* pExp2)
+    : CXFA_FMChainableExpression(op, pExp1, pExp2), m_OpName(opName) {}
+
+CXFA_FMBinExpression::~CXFA_FMBinExpression() = default;
+
+bool CXFA_FMBinExpression::ToJavaScript(WideTextBuffer* js,
+                                        ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  *js << "pfm_rt." << m_OpName << "(";
+  if (!GetFirstExpression()->ToJavaScript(js, ReturnType::kInferred))
+    return false;
+  *js << ", ";
+  if (!GetSecondExpression()->ToJavaScript(js, ReturnType::kInferred))
+    return false;
+  *js << ")";
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMLogicalOrExpression::CXFA_FMLogicalOrExpression(
+    XFA_FM_TOKEN op,
+    CXFA_FMSimpleExpression* pExp1,
+    CXFA_FMSimpleExpression* pExp2)
+    : CXFA_FMBinExpression(L"log_or_op", op, pExp1, pExp2) {}
+
+CXFA_FMLogicalOrExpression::~CXFA_FMLogicalOrExpression() = default;
+
+CXFA_FMLogicalAndExpression::CXFA_FMLogicalAndExpression(
+    XFA_FM_TOKEN op,
+    CXFA_FMSimpleExpression* pExp1,
+    CXFA_FMSimpleExpression* pExp2)
+    : CXFA_FMBinExpression(L"log_and_op", op, pExp1, pExp2) {}
+
+CXFA_FMLogicalAndExpression::~CXFA_FMLogicalAndExpression() = default;
+
+CXFA_FMEqualExpression::CXFA_FMEqualExpression(XFA_FM_TOKEN op,
+                                               CXFA_FMSimpleExpression* pExp1,
+                                               CXFA_FMSimpleExpression* pExp2)
+    : CXFA_FMBinExpression(L"eq_op", op, pExp1, pExp2) {}
+
+CXFA_FMEqualExpression::~CXFA_FMEqualExpression() = default;
+
+CXFA_FMNotEqualExpression::CXFA_FMNotEqualExpression(
+    XFA_FM_TOKEN op,
+    CXFA_FMSimpleExpression* pExp1,
+    CXFA_FMSimpleExpression* pExp2)
+    : CXFA_FMBinExpression(L"neq_op", op, pExp1, pExp2) {}
+
+CXFA_FMNotEqualExpression::~CXFA_FMNotEqualExpression() = default;
+
+CXFA_FMGtExpression::CXFA_FMGtExpression(XFA_FM_TOKEN op,
+                                         CXFA_FMSimpleExpression* pExp1,
+                                         CXFA_FMSimpleExpression* pExp2)
+    : CXFA_FMBinExpression(L"gt_op", op, pExp1, pExp2) {}
+
+CXFA_FMGtExpression::~CXFA_FMGtExpression() = default;
+
+CXFA_FMGeExpression::CXFA_FMGeExpression(XFA_FM_TOKEN op,
+                                         CXFA_FMSimpleExpression* pExp1,
+                                         CXFA_FMSimpleExpression* pExp2)
+    : CXFA_FMBinExpression(L"ge_op", op, pExp1, pExp2) {}
+
+CXFA_FMGeExpression::~CXFA_FMGeExpression() = default;
+
+CXFA_FMLtExpression::CXFA_FMLtExpression(XFA_FM_TOKEN op,
+                                         CXFA_FMSimpleExpression* pExp1,
+                                         CXFA_FMSimpleExpression* pExp2)
+    : CXFA_FMBinExpression(L"lt_op", op, pExp1, pExp2) {}
+
+CXFA_FMLtExpression::~CXFA_FMLtExpression() = default;
+
+CXFA_FMLeExpression::CXFA_FMLeExpression(XFA_FM_TOKEN op,
+                                         CXFA_FMSimpleExpression* pExp1,
+                                         CXFA_FMSimpleExpression* pExp2)
+    : CXFA_FMBinExpression(L"le_op", op, pExp1, pExp2) {}
+
+CXFA_FMLeExpression::~CXFA_FMLeExpression() = default;
+
+CXFA_FMPlusExpression::CXFA_FMPlusExpression(XFA_FM_TOKEN op,
+                                             CXFA_FMSimpleExpression* pExp1,
+                                             CXFA_FMSimpleExpression* pExp2)
+    : CXFA_FMBinExpression(L"plus_op", op, pExp1, pExp2) {}
+
+CXFA_FMPlusExpression::~CXFA_FMPlusExpression() = default;
+
+CXFA_FMMinusExpression::CXFA_FMMinusExpression(XFA_FM_TOKEN op,
+                                               CXFA_FMSimpleExpression* pExp1,
+                                               CXFA_FMSimpleExpression* pExp2)
+    : CXFA_FMBinExpression(L"minus_op", op, pExp1, pExp2) {}
+
+CXFA_FMMinusExpression::~CXFA_FMMinusExpression() = default;
+
+CXFA_FMMulExpression::CXFA_FMMulExpression(XFA_FM_TOKEN op,
+                                           CXFA_FMSimpleExpression* pExp1,
+                                           CXFA_FMSimpleExpression* pExp2)
+    : CXFA_FMBinExpression(L"mul_op", op, pExp1, pExp2) {}
+
+CXFA_FMMulExpression::~CXFA_FMMulExpression() = default;
+
+CXFA_FMDivExpression::CXFA_FMDivExpression(XFA_FM_TOKEN op,
+                                           CXFA_FMSimpleExpression* pExp1,
+                                           CXFA_FMSimpleExpression* pExp2)
+    : CXFA_FMBinExpression(L"div_op", op, pExp1, pExp2) {}
+
+CXFA_FMDivExpression::~CXFA_FMDivExpression() = default;
+
+CXFA_FMUnaryExpression::CXFA_FMUnaryExpression(const WideString& opName,
+                                               XFA_FM_TOKEN op,
+                                               CXFA_FMSimpleExpression* pExp)
+    : CXFA_FMSimpleExpression(op), m_OpName(opName), m_pExp(pExp) {}
+
+CXFA_FMUnaryExpression::~CXFA_FMUnaryExpression() = default;
+
+void CXFA_FMUnaryExpression::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FMSimpleExpression::Trace(visitor);
+  visitor->Trace(m_pExp);
+}
+
+bool CXFA_FMUnaryExpression::ToJavaScript(WideTextBuffer* js,
+                                          ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  *js << "pfm_rt." << m_OpName << "(";
+  if (!m_pExp->ToJavaScript(js, ReturnType::kInferred))
+    return false;
+  *js << ")";
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMPosExpression::CXFA_FMPosExpression(CXFA_FMSimpleExpression* pExp)
+    : CXFA_FMUnaryExpression(L"pos_op", TOKplus, pExp) {}
+
+CXFA_FMPosExpression::~CXFA_FMPosExpression() = default;
+
+CXFA_FMNegExpression::CXFA_FMNegExpression(CXFA_FMSimpleExpression* pExp)
+    : CXFA_FMUnaryExpression(L"neg_op", TOKminus, pExp) {}
+
+CXFA_FMNegExpression::~CXFA_FMNegExpression() = default;
+
+CXFA_FMNotExpression::CXFA_FMNotExpression(CXFA_FMSimpleExpression* pExp)
+    : CXFA_FMUnaryExpression(L"log_not_op", TOKksnot, pExp) {}
+
+CXFA_FMNotExpression::~CXFA_FMNotExpression() = default;
+
+CXFA_FMCallExpression::CXFA_FMCallExpression(
+    CXFA_FMSimpleExpression* pExp,
+    std::vector<cppgc::Member<CXFA_FMSimpleExpression>>&& pArguments,
+    bool bIsSomMethod)
+    : CXFA_FMSimpleExpression(TOKcall),
+      m_pExp(pExp),
+      m_Arguments(std::move(pArguments)),
+      m_bIsSomMethod(bIsSomMethod) {}
+
+CXFA_FMCallExpression::~CXFA_FMCallExpression() = default;
+
+void CXFA_FMCallExpression::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FMSimpleExpression::Trace(visitor);
+  visitor->Trace(m_pExp);
+  ContainerTrace(visitor, m_Arguments);
+}
+
+bool CXFA_FMCallExpression::IsBuiltInFunc(WideTextBuffer* funcName) const {
+  if (funcName->GetLength() > kBuiltInFuncsMaxLen)
+    return false;
+
+  WideString str = funcName->MakeString();
+  const wchar_t* const* pMatchResult =
+      std::lower_bound(std::begin(kBuiltInFuncs), std::end(kBuiltInFuncs), str,
+                       [](const wchar_t* iter, const WideString& val) -> bool {
+                         return val.CompareNoCase(iter) > 0;
+                       });
+  if (pMatchResult != std::end(kBuiltInFuncs) &&
+      !str.CompareNoCase(*pMatchResult)) {
+    funcName->Clear();
+    *funcName << *pMatchResult;
+    return true;
+  }
+  return false;
+}
+
+uint32_t CXFA_FMCallExpression::IsMethodWithObjParam(
+    const WideString& methodName) const {
+  const XFA_FMSOMMethod* result = std::lower_bound(
+      std::begin(kFMSomMethods), std::end(kFMSomMethods), methodName,
+      [](const XFA_FMSOMMethod iter, const WideString& val) {
+        return val.Compare(iter.m_wsSomMethodName) > 0;
+      });
+  if (result != std::end(kFMSomMethods) &&
+      methodName == result->m_wsSomMethodName) {
+    return result->m_dParameters;
+  }
+  return 0;
+}
+
+bool CXFA_FMCallExpression::ToJavaScript(WideTextBuffer* js,
+                                         ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  WideTextBuffer funcName;
+  if (!m_pExp->ToJavaScript(&funcName, ReturnType::kInferred))
+    return false;
+
+  if (m_bIsSomMethod) {
+    *js << funcName << "(";
+    uint32_t methodPara = IsMethodWithObjParam(funcName.MakeString());
+    if (methodPara > 0) {
+      for (size_t i = 0; i < m_Arguments.size(); ++i) {
+        // Currently none of our expressions use objects for a parameter over
+        // the 6th. Make sure we don't overflow the shift when doing this
+        // check. If we ever need more the 32 object params we can revisit.
+        *js << "pfm_rt.get_";
+        if (i < 32 && (methodPara & (0x01 << i)) > 0)
+          *js << "jsobj";
+        else
+          *js << "val";
+
+        *js << "(";
+        if (!m_Arguments[i]->ToJavaScript(js, ReturnType::kInferred))
+          return false;
+        *js << ")";
+        if (i + 1 < m_Arguments.size())
+          *js << ", ";
+      }
+    } else {
+      for (const auto& expr : m_Arguments) {
+        *js << "pfm_rt.get_val(";
+        if (!expr->ToJavaScript(js, ReturnType::kInferred))
+          return false;
+        *js << ")";
+        if (expr != m_Arguments.back())
+          *js << ", ";
+      }
+    }
+    *js << ")";
+    return !CXFA_IsTooBig(*js);
+  }
+
+  bool isEvalFunc = false;
+  bool isExistsFunc = false;
+  if (!IsBuiltInFunc(&funcName)) {
+    // If a function is not a SomMethod or a built-in then the input was
+    // invalid, so failing. The scanner/lexer should catch this, but currently
+    // doesn't. This failure will bubble up to the top-level and cause the
+    // transpile to fail.
+    return false;
+  }
+
+  if (funcName.AsStringView().EqualsASCII("Eval")) {
+    isEvalFunc = true;
+    *js << "eval.call(this, pfm_rt.Translate";
+  } else {
+    if (funcName.AsStringView().EqualsASCII("Exists"))
+      isExistsFunc = true;
+
+    *js << "pfm_rt." << funcName;
+  }
+
+  *js << "(";
+  if (isExistsFunc) {
+    *js << "\n(\nfunction ()\n{\ntry\n{\n";
+    if (!m_Arguments.empty()) {
+      *js << "return ";
+      if (!m_Arguments[0]->ToJavaScript(js, ReturnType::kInferred))
+        return false;
+      *js << ";\n}\n";
+    } else {
+      *js << "return 0;\n}\n";
+    }
+    *js << "catch(accessExceptions)\n";
+    *js << "{\nreturn 0;\n}\n}\n).call(this)\n";
+  } else {
+    for (const auto& expr : m_Arguments) {
+      if (!expr->ToJavaScript(js, ReturnType::kInferred))
+        return false;
+      if (expr != m_Arguments.back())
+        *js << ", ";
+    }
+  }
+  *js << ")";
+  if (isEvalFunc)
+    *js << ")";
+
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMDotAccessorExpression::CXFA_FMDotAccessorExpression(
+    CXFA_FMSimpleExpression* pAccessor,
+    XFA_FM_TOKEN op,
+    WideString wsIdentifier,
+    CXFA_FMSimpleExpression* pIndexExp)
+    : CXFA_FMChainableExpression(op, pAccessor, pIndexExp),
+      m_wsIdentifier(std::move(wsIdentifier)) {}
+
+CXFA_FMDotAccessorExpression::~CXFA_FMDotAccessorExpression() = default;
+
+bool CXFA_FMDotAccessorExpression::ToJavaScript(WideTextBuffer* js,
+                                                ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  *js << "pfm_rt.dot_acc(";
+
+  CXFA_FMSimpleExpression* exp1 = GetFirstExpression();
+  if (exp1) {
+    // Write directly to the buffer with each recursion. Creating
+    // and copying temporaries here becomes expensive when there
+    // is deep recursion, even though we may need to re-create the
+    // same thing again below. See https://crbug.com/1274018.
+    if (!exp1->ToJavaScript(js, ReturnType::kInferred))
+      return false;
+  } else {
+    *js << "null";
+  }
+  *js << ", \"";
+  if (exp1 && exp1->GetOperatorToken() == TOKidentifier) {
+    if (!exp1->ToJavaScript(js, ReturnType::kInferred))
+      return false;
+  }
+  *js << "\", ";
+  if (GetOperatorToken() == TOKdotscream)
+    *js << "\"#" << m_wsIdentifier << "\", ";
+  else if (GetOperatorToken() == TOKdotstar)
+    *js << "\"*\", ";
+  else if (GetOperatorToken() == TOKcall)
+    *js << "\"\", ";
+  else
+    *js << "\"" << m_wsIdentifier << "\", ";
+
+  CXFA_FMSimpleExpression* exp2 = GetSecondExpression();
+  if (!exp2->ToJavaScript(js, ReturnType::kInferred))
+    return false;
+
+  *js << ")";
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMIndexExpression::CXFA_FMIndexExpression(
+    AccessorIndex accessorIndex,
+    CXFA_FMSimpleExpression* pIndexExp,
+    bool bIsStarIndex)
+    : CXFA_FMSimpleExpression(TOKlbracket),
+      m_pExp(pIndexExp),
+      m_accessorIndex(accessorIndex),
+      m_bIsStarIndex(bIsStarIndex) {}
+
+CXFA_FMIndexExpression::~CXFA_FMIndexExpression() = default;
+
+void CXFA_FMIndexExpression::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FMSimpleExpression::Trace(visitor);
+  visitor->Trace(m_pExp);
+}
+
+bool CXFA_FMIndexExpression::ToJavaScript(WideTextBuffer* js,
+                                          ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  switch (m_accessorIndex) {
+    case AccessorIndex::kNoIndex:
+      *js << "0";
+      break;
+    case AccessorIndex::kNoRelativeIndex:
+      *js << "1";
+      break;
+    case AccessorIndex::kPositiveIndex:
+      *js << "2";
+      break;
+    case AccessorIndex::kNegativeIndex:
+      *js << "3";
+      break;
+  }
+  if (m_bIsStarIndex)
+    return !CXFA_IsTooBig(*js);
+
+  *js << ", ";
+  if (m_pExp) {
+    if (!m_pExp->ToJavaScript(js, ReturnType::kInferred))
+      return false;
+  } else {
+    *js << "0";
+  }
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMDotDotAccessorExpression::CXFA_FMDotDotAccessorExpression(
+    CXFA_FMSimpleExpression* pAccessor,
+    XFA_FM_TOKEN op,
+    WideString wsIdentifier,
+    CXFA_FMSimpleExpression* pIndexExp)
+    : CXFA_FMChainableExpression(op, pAccessor, pIndexExp),
+      m_wsIdentifier(std::move(wsIdentifier)) {}
+
+CXFA_FMDotDotAccessorExpression::~CXFA_FMDotDotAccessorExpression() = default;
+
+bool CXFA_FMDotDotAccessorExpression::ToJavaScript(WideTextBuffer* js,
+                                                   ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  CXFA_FMSimpleExpression* exp1 = GetFirstExpression();
+  *js << "pfm_rt.dotdot_acc(";
+  if (!exp1->ToJavaScript(js, ReturnType::kInferred))
+    return false;
+  *js << ", "
+      << "\"";
+  if (exp1->GetOperatorToken() == TOKidentifier) {
+    if (!exp1->ToJavaScript(js, ReturnType::kInferred))
+      return false;
+  }
+
+  CXFA_FMSimpleExpression* exp2 = GetSecondExpression();
+  *js << "\", \"" << m_wsIdentifier << "\", ";
+  if (!exp2->ToJavaScript(js, ReturnType::kInferred))
+    return false;
+  *js << ")";
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMMethodCallExpression::CXFA_FMMethodCallExpression(
+    CXFA_FMSimpleExpression* pAccessorExp1,
+    CXFA_FMSimpleExpression* pCallExp)
+    : CXFA_FMChainableExpression(TOKdot, pAccessorExp1, pCallExp) {}
+
+CXFA_FMMethodCallExpression::~CXFA_FMMethodCallExpression() = default;
+
+bool CXFA_FMMethodCallExpression::ToJavaScript(WideTextBuffer* js,
+                                               ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  *js << "(function() {\n";
+  *js << "  return pfm_method_runner(";
+  if (!GetFirstExpression()->ToJavaScript(js, ReturnType::kInferred))
+    return false;
+
+  *js << ", function(obj) {\n";
+  *js << "    return obj.";
+  if (!GetSecondExpression()->ToJavaScript(js, ReturnType::kInferred))
+    return false;
+
+  *js << ";\n";
+  *js << "  });\n";
+  *js << "}).call(this)";
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMFunctionDefinition::CXFA_FMFunctionDefinition(
+    WideString wsName,
+    std::vector<WideString>&& arguments,
+    std::vector<cppgc::Member<CXFA_FMExpression>>&& expressions)
+    : m_wsName(std::move(wsName)),
+      m_pArguments(std::move(arguments)),
+      m_pExpressions(std::move(expressions)) {
+  DCHECK(!m_wsName.IsEmpty());
+}
+
+CXFA_FMFunctionDefinition::~CXFA_FMFunctionDefinition() = default;
+
+void CXFA_FMFunctionDefinition::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FMExpression::Trace(visitor);
+  ContainerTrace(visitor, m_pExpressions);
+}
+
+bool CXFA_FMFunctionDefinition::ToJavaScript(WideTextBuffer* js,
+                                             ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  if (m_wsName.IsEmpty())
+    return false;
+
+  *js << "function " << IdentifierToName(m_wsName) << "(";
+  for (const auto& identifier : m_pArguments) {
+    if (identifier != m_pArguments.front())
+      *js << ", ";
+
+    *js << IdentifierToName(identifier);
+  }
+  *js << ") {\n";
+
+  *js << "var pfm_ret = null;\n";
+  for (const auto& expr : m_pExpressions) {
+    ReturnType ret_type = expr == m_pExpressions.back() ? ReturnType::kImplied
+                                                        : ReturnType::kInferred;
+    if (!expr->ToJavaScript(js, ret_type))
+      return false;
+  }
+
+  *js << "return pfm_ret;\n";
+  *js << "}\n";
+
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMAST::CXFA_FMAST(
+    std::vector<cppgc::Member<CXFA_FMExpression>> expressions)
+    : expressions_(std::move(expressions)) {}
+
+CXFA_FMAST::~CXFA_FMAST() = default;
+
+void CXFA_FMAST::Trace(cppgc::Visitor* visitor) const {
+  ContainerTrace(visitor, expressions_);
+}
+
+absl::optional<WideTextBuffer> CXFA_FMAST::ToJavaScript() const {
+  WideTextBuffer js;
+  if (expressions_.empty()) {
+    js << "// comments only";
+    return js;
+  }
+
+  js << "(function() {\n";
+  js << "let pfm_method_runner = function(obj, cb) {\n";
+  js << "  if (pfm_rt.is_ary(obj)) {\n";
+  js << "    let pfm_method_return = null;\n";
+  js << "    for (var idx = obj.length -1; idx > 1; idx--) {\n";
+  js << "      pfm_method_return = cb(obj[idx]);\n";
+  js << "    }\n";
+  js << "    return pfm_method_return;\n";
+  js << "  }\n";
+  js << "  return cb(obj);\n";
+  js << "};\n";
+  js << "var pfm_ret = null;\n";
+  for (const auto& expr : expressions_) {
+    CXFA_FMAssignExpression::ReturnType ret_type =
+        expr == expressions_.back()
+            ? CXFA_FMAssignExpression::ReturnType::kImplied
+            : CXFA_FMAssignExpression::ReturnType::kInferred;
+    if (!expr->ToJavaScript(&js, ret_type))
+      return absl::nullopt;
+  }
+  js << "return pfm_rt.get_val(pfm_ret);\n";
+  js << "}).call(this);";
+
+  if (CXFA_IsTooBig(js))
+    return absl::nullopt;
+
+  return js;
+}
+
+CXFA_FMVarExpression::CXFA_FMVarExpression(WideString wsName,
+                                           CXFA_FMSimpleExpression* pInit)
+    : m_wsName(std::move(wsName)), m_pInit(pInit) {}
+
+CXFA_FMVarExpression::~CXFA_FMVarExpression() = default;
+
+void CXFA_FMVarExpression::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FMExpression::Trace(visitor);
+  visitor->Trace(m_pInit);
+}
+
+bool CXFA_FMVarExpression::ToJavaScript(WideTextBuffer* js,
+                                        ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  WideString tempName = IdentifierToName(m_wsName);
+  *js << "var " << tempName << " = ";
+  if (m_pInit) {
+    if (!m_pInit->ToJavaScript(js, ReturnType::kInferred))
+      return false;
+
+    *js << ";\n";
+    *js << tempName << " = pfm_rt.var_filter(" << tempName << ");\n";
+  } else {
+    *js << "\"\";\n";
+  }
+
+  if (type == ReturnType::kImplied)
+    *js << "pfm_ret = " << tempName << ";\n";
+
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMExpExpression::CXFA_FMExpExpression(CXFA_FMSimpleExpression* pExpression)
+    : m_pExpression(pExpression) {}
+
+CXFA_FMExpExpression::~CXFA_FMExpExpression() = default;
+
+void CXFA_FMExpExpression::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FMExpression::Trace(visitor);
+  visitor->Trace(m_pExpression);
+}
+
+bool CXFA_FMExpExpression::ToJavaScript(WideTextBuffer* js,
+                                        ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  if (type == ReturnType::kInferred) {
+    bool ret = m_pExpression->ToJavaScript(js, ReturnType::kInferred);
+    if (m_pExpression->GetOperatorToken() != TOKassign)
+      *js << ";\n";
+
+    return ret;
+  }
+
+  if (m_pExpression->GetOperatorToken() == TOKassign)
+    return m_pExpression->ToJavaScript(js, ReturnType::kImplied);
+
+  if (m_pExpression->GetOperatorToken() == TOKstar ||
+      m_pExpression->GetOperatorToken() == TOKdotstar ||
+      m_pExpression->GetOperatorToken() == TOKdotscream ||
+      m_pExpression->GetOperatorToken() == TOKdotdot ||
+      m_pExpression->GetOperatorToken() == TOKdot) {
+    *js << "pfm_ret = pfm_rt.get_val(";
+    if (!m_pExpression->ToJavaScript(js, ReturnType::kInferred))
+      return false;
+
+    *js << ");\n";
+    return !CXFA_IsTooBig(*js);
+  }
+
+  *js << "pfm_ret = ";
+  if (!m_pExpression->ToJavaScript(js, ReturnType::kInferred))
+    return false;
+
+  *js << ";\n";
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMBlockExpression::CXFA_FMBlockExpression(
+    std::vector<cppgc::Member<CXFA_FMExpression>>&& pExpressionList)
+    : m_ExpressionList(std::move(pExpressionList)) {}
+
+CXFA_FMBlockExpression::~CXFA_FMBlockExpression() = default;
+
+void CXFA_FMBlockExpression::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FMExpression::Trace(visitor);
+  ContainerTrace(visitor, m_ExpressionList);
+}
+
+bool CXFA_FMBlockExpression::ToJavaScript(WideTextBuffer* js,
+                                          ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  *js << "{\n";
+  for (const auto& expr : m_ExpressionList) {
+    if (type == ReturnType::kInferred) {
+      if (!expr->ToJavaScript(js, ReturnType::kInferred))
+        return false;
+    } else {
+      ReturnType ret_type = expr == m_ExpressionList.back()
+                                ? ReturnType::kImplied
+                                : ReturnType::kInferred;
+      if (!expr->ToJavaScript(js, ret_type))
+        return false;
+    }
+  }
+  *js << "}\n";
+
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMDoExpression::CXFA_FMDoExpression(CXFA_FMExpression* pList)
+    : m_pList(pList) {}
+
+CXFA_FMDoExpression::~CXFA_FMDoExpression() = default;
+
+void CXFA_FMDoExpression::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FMExpression::Trace(visitor);
+  visitor->Trace(m_pList);
+}
+
+bool CXFA_FMDoExpression::ToJavaScript(WideTextBuffer* js,
+                                       ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  return m_pList->ToJavaScript(js, type);
+}
+
+CXFA_FMIfExpression::CXFA_FMIfExpression(
+    CXFA_FMSimpleExpression* pExpression,
+    CXFA_FMExpression* pIfExpression,
+    std::vector<cppgc::Member<CXFA_FMIfExpression>>&& pElseIfExpressions,
+    CXFA_FMExpression* pElseExpression)
+    : m_pExpression(pExpression),
+      m_pIfExpression(pIfExpression),
+      m_pElseIfExpressions(std::move(pElseIfExpressions)),
+      m_pElseExpression(pElseExpression) {
+  DCHECK(m_pExpression);
+}
+
+CXFA_FMIfExpression::~CXFA_FMIfExpression() = default;
+
+void CXFA_FMIfExpression::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FMExpression::Trace(visitor);
+  visitor->Trace(m_pExpression);
+  visitor->Trace(m_pIfExpression);
+  ContainerTrace(visitor, m_pElseIfExpressions);
+  visitor->Trace(m_pElseExpression);
+}
+
+bool CXFA_FMIfExpression::ToJavaScript(WideTextBuffer* js,
+                                       ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  if (type == ReturnType::kImplied)
+    *js << "pfm_ret = 0;\n";
+
+  *js << "if (pfm_rt.get_val(";
+  if (!m_pExpression->ToJavaScript(js, ReturnType::kInferred))
+    return false;
+  *js << "))\n";
+
+  if (CXFA_IsTooBig(*js))
+    return false;
+
+  if (m_pIfExpression) {
+    if (!m_pIfExpression->ToJavaScript(js, type))
+      return false;
+    if (CXFA_IsTooBig(*js))
+      return false;
+  }
+
+  for (auto& expr : m_pElseIfExpressions) {
+    *js << "else ";
+    if (!expr->ToJavaScript(js, ReturnType::kInferred))
+      return false;
+  }
+
+  if (m_pElseExpression) {
+    *js << "else ";
+    if (!m_pElseExpression->ToJavaScript(js, type))
+      return false;
+  }
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMWhileExpression::CXFA_FMWhileExpression(
+    CXFA_FMSimpleExpression* pCondition,
+    CXFA_FMExpression* pExpression)
+    : m_pCondition(pCondition), m_pExpression(pExpression) {}
+
+CXFA_FMWhileExpression::~CXFA_FMWhileExpression() = default;
+
+void CXFA_FMWhileExpression::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FMExpression::Trace(visitor);
+  visitor->Trace(m_pCondition);
+  visitor->Trace(m_pExpression);
+}
+
+bool CXFA_FMWhileExpression::ToJavaScript(WideTextBuffer* js,
+                                          ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  if (type == ReturnType::kImplied)
+    *js << "pfm_ret = 0;\n";
+
+  *js << "while (";
+  if (!m_pCondition->ToJavaScript(js, ReturnType::kInferred))
+    return false;
+
+  *js << ")\n";
+  if (CXFA_IsTooBig(*js))
+    return false;
+
+  if (!m_pExpression->ToJavaScript(js, type))
+    return false;
+
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMBreakExpression::CXFA_FMBreakExpression() : CXFA_FMExpression() {}
+
+CXFA_FMBreakExpression::~CXFA_FMBreakExpression() = default;
+
+bool CXFA_FMBreakExpression::ToJavaScript(WideTextBuffer* js,
+                                          ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  *js << "pfm_ret = 0;\nbreak;\n";
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMContinueExpression::CXFA_FMContinueExpression() : CXFA_FMExpression() {}
+
+CXFA_FMContinueExpression::~CXFA_FMContinueExpression() = default;
+
+bool CXFA_FMContinueExpression::ToJavaScript(WideTextBuffer* js,
+                                             ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  *js << "pfm_ret = 0;\ncontinue;\n";
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMForExpression::CXFA_FMForExpression(WideString wsVariant,
+                                           CXFA_FMSimpleExpression* pAssignment,
+                                           CXFA_FMSimpleExpression* pAccessor,
+                                           int32_t iDirection,
+                                           CXFA_FMSimpleExpression* pStep,
+                                           CXFA_FMExpression* pList)
+    : m_wsVariant(std::move(wsVariant)),
+      m_bDirection(iDirection == 1),
+      m_pAssignment(pAssignment),
+      m_pAccessor(pAccessor),
+      m_pStep(pStep),
+      m_pList(pList) {}
+
+CXFA_FMForExpression::~CXFA_FMForExpression() = default;
+
+void CXFA_FMForExpression::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FMExpression::Trace(visitor);
+  visitor->Trace(m_pAssignment);
+  visitor->Trace(m_pAccessor);
+  visitor->Trace(m_pStep);
+  visitor->Trace(m_pList);
+}
+
+bool CXFA_FMForExpression::ToJavaScript(WideTextBuffer* js,
+                                        ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  if (type == ReturnType::kImplied)
+    *js << "pfm_ret = 0;\n";
+
+  *js << "{\n";
+
+  WideString tmpName = IdentifierToName(m_wsVariant);
+  *js << "var " << tmpName << " = null;\n";
+
+  *js << "for (" << tmpName << " = pfm_rt.get_val(";
+  if (!m_pAssignment->ToJavaScript(js, ReturnType::kInferred))
+    return false;
+  *js << "); ";
+
+  *js << tmpName << (m_bDirection ? kLessEqual : kGreaterEqual);
+  *js << "pfm_rt.get_val(";
+  if (!m_pAccessor->ToJavaScript(js, ReturnType::kInferred))
+    return false;
+  *js << "); ";
+
+  *js << tmpName << (m_bDirection ? kPlusEqual : kMinusEqual);
+  if (m_pStep) {
+    *js << "pfm_rt.get_val(";
+    if (!m_pStep->ToJavaScript(js, ReturnType::kInferred))
+      return false;
+    *js << ")";
+  } else {
+    *js << "1";
+  }
+  *js << ")\n";
+  if (CXFA_IsTooBig(*js))
+    return false;
+
+  if (!m_pList->ToJavaScript(js, type))
+    return false;
+
+  *js << "}\n";
+  return !CXFA_IsTooBig(*js);
+}
+
+CXFA_FMForeachExpression::CXFA_FMForeachExpression(
+    WideString wsIdentifier,
+    std::vector<cppgc::Member<CXFA_FMSimpleExpression>>&& pAccessors,
+    CXFA_FMExpression* pList)
+    : m_wsIdentifier(std::move(wsIdentifier)),
+      m_pAccessors(std::move(pAccessors)),
+      m_pList(pList) {}
+
+CXFA_FMForeachExpression::~CXFA_FMForeachExpression() = default;
+
+void CXFA_FMForeachExpression::Trace(cppgc::Visitor* visitor) const {
+  CXFA_FMExpression::Trace(visitor);
+  ContainerTrace(visitor, m_pAccessors);
+  visitor->Trace(m_pList);
+}
+
+bool CXFA_FMForeachExpression::ToJavaScript(WideTextBuffer* js,
+                                            ReturnType type) const {
+  CXFA_FMToJavaScriptDepth depthManager;
+  if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  if (type == ReturnType::kImplied)
+    *js << "pfm_ret = 0;\n";
+
+  *js << "{\n";
+
+  WideString tmpName = IdentifierToName(m_wsIdentifier);
+  *js << "var " << tmpName << " = null;\n";
+  *js << "var pfm_ary = pfm_rt.concat_obj(";
+  for (const auto& expr : m_pAccessors) {
+    if (!expr->ToJavaScript(js, ReturnType::kInferred))
+      return false;
+    if (expr != m_pAccessors.back())
+      *js << ", ";
+  }
+  *js << ");\n";
+
+  *js << "var pfm_ary_idx = 0;\n";
+  *js << "while(pfm_ary_idx < pfm_ary.length)\n{\n";
+  *js << tmpName << " = pfm_ary[pfm_ary_idx++];\n";
+  if (!m_pList->ToJavaScript(js, type))
+    return false;
+
+  *js << "}\n";  // while
+  *js << "}\n";  // block
+  return !CXFA_IsTooBig(*js);
+}
+
+bool CXFA_IsTooBig(const WideTextBuffer& js) {
+  return js.GetSize() >= 256 * 1024 * 1024;
+}
diff --git a/xfa/fxfa/formcalc/cxfa_fmexpression.h b/xfa/fxfa/formcalc/cxfa_fmexpression.h
new file mode 100644
index 0000000..e00921f
--- /dev/null
+++ b/xfa/fxfa/formcalc/cxfa_fmexpression.h
@@ -0,0 +1,608 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FXFA_FORMCALC_CXFA_FMEXPRESSION_H_
+#define XFA_FXFA_FORMCALC_CXFA_FMEXPRESSION_H_
+
+#include <vector>
+
+#include "core/fxcrt/widestring.h"
+#include "core/fxcrt/widetext_buffer.h"
+#include "fxjs/gc/heap.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "xfa/fxfa/formcalc/cxfa_fmlexer.h"
+
+class CXFA_FMExpression : public cppgc::GarbageCollected<CXFA_FMExpression> {
+ public:
+  enum class ReturnType { kImplied, kInferred };
+
+  virtual ~CXFA_FMExpression();
+  virtual void Trace(cppgc::Visitor* visitor) const;
+
+  virtual bool ToJavaScript(WideTextBuffer* js, ReturnType type) const = 0;
+
+ protected:
+  CXFA_FMExpression();
+};
+
+class CXFA_FMSimpleExpression : public CXFA_FMExpression {
+ public:
+  ~CXFA_FMSimpleExpression() override;
+
+  XFA_FM_TOKEN GetOperatorToken() const { return m_op; }
+
+ protected:
+  explicit CXFA_FMSimpleExpression(XFA_FM_TOKEN op);
+
+ private:
+  const XFA_FM_TOKEN m_op;
+};
+
+class CXFA_FMChainableExpression : public CXFA_FMSimpleExpression {
+ public:
+  ~CXFA_FMChainableExpression() override;
+  void Trace(cppgc::Visitor* visitor) const override;
+
+ protected:
+  CXFA_FMChainableExpression(XFA_FM_TOKEN op,
+                             CXFA_FMSimpleExpression* pExp1,
+                             CXFA_FMSimpleExpression* pExp2);
+
+  CXFA_FMSimpleExpression* GetFirstExpression() const { return m_pExp1; }
+  CXFA_FMSimpleExpression* GetSecondExpression() const { return m_pExp2; }
+
+ private:
+  cppgc::Member<CXFA_FMSimpleExpression> m_pExp1;
+  cppgc::Member<CXFA_FMSimpleExpression> m_pExp2;
+};
+
+class CXFA_FMNullExpression final : public CXFA_FMSimpleExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMNullExpression() override;
+
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  CXFA_FMNullExpression();
+};
+
+class CXFA_FMNumberExpression final : public CXFA_FMSimpleExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMNumberExpression() override;
+
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  explicit CXFA_FMNumberExpression(WideString wsNumber);
+
+  WideString m_wsNumber;
+};
+
+class CXFA_FMStringExpression final : public CXFA_FMSimpleExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMStringExpression() override;
+
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  explicit CXFA_FMStringExpression(WideString wsString);
+
+  WideString m_wsString;
+};
+
+class CXFA_FMIdentifierExpression final : public CXFA_FMSimpleExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMIdentifierExpression() override;
+
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  explicit CXFA_FMIdentifierExpression(WideString wsIdentifier);
+
+  WideString m_wsIdentifier;
+};
+
+class CXFA_FMAssignExpression final : public CXFA_FMChainableExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMAssignExpression() override;
+
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  CXFA_FMAssignExpression(XFA_FM_TOKEN op,
+                          CXFA_FMSimpleExpression* pExp1,
+                          CXFA_FMSimpleExpression* pExp2);
+};
+
+class CXFA_FMBinExpression : public CXFA_FMChainableExpression {
+ public:
+  ~CXFA_FMBinExpression() override;
+
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ protected:
+  CXFA_FMBinExpression(const WideString& opName,
+                       XFA_FM_TOKEN op,
+                       CXFA_FMSimpleExpression* pExp1,
+                       CXFA_FMSimpleExpression* pExp2);
+
+ private:
+  WideString m_OpName;
+};
+
+class CXFA_FMLogicalOrExpression final : public CXFA_FMBinExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMLogicalOrExpression() override;
+
+ private:
+  CXFA_FMLogicalOrExpression(XFA_FM_TOKEN op,
+                             CXFA_FMSimpleExpression* pExp1,
+                             CXFA_FMSimpleExpression* pExp2);
+};
+
+class CXFA_FMLogicalAndExpression final : public CXFA_FMBinExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMLogicalAndExpression() override;
+
+ private:
+  CXFA_FMLogicalAndExpression(XFA_FM_TOKEN op,
+                              CXFA_FMSimpleExpression* pExp1,
+                              CXFA_FMSimpleExpression* pExp2);
+};
+
+class CXFA_FMEqualExpression final : public CXFA_FMBinExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMEqualExpression() override;
+
+ private:
+  CXFA_FMEqualExpression(XFA_FM_TOKEN op,
+                         CXFA_FMSimpleExpression* pExp1,
+                         CXFA_FMSimpleExpression* pExp2);
+};
+
+class CXFA_FMNotEqualExpression final : public CXFA_FMBinExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMNotEqualExpression() override;
+
+ private:
+  CXFA_FMNotEqualExpression(XFA_FM_TOKEN op,
+                            CXFA_FMSimpleExpression* pExp1,
+                            CXFA_FMSimpleExpression* pExp2);
+};
+
+class CXFA_FMGtExpression final : public CXFA_FMBinExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMGtExpression() override;
+
+ private:
+  CXFA_FMGtExpression(XFA_FM_TOKEN op,
+                      CXFA_FMSimpleExpression* pExp1,
+                      CXFA_FMSimpleExpression* pExp2);
+};
+
+class CXFA_FMGeExpression final : public CXFA_FMBinExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMGeExpression() override;
+
+ private:
+  CXFA_FMGeExpression(XFA_FM_TOKEN op,
+                      CXFA_FMSimpleExpression* pExp1,
+                      CXFA_FMSimpleExpression* pExp2);
+};
+
+class CXFA_FMLtExpression final : public CXFA_FMBinExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMLtExpression() override;
+
+ private:
+  CXFA_FMLtExpression(XFA_FM_TOKEN op,
+                      CXFA_FMSimpleExpression* pExp1,
+                      CXFA_FMSimpleExpression* pExp2);
+};
+
+class CXFA_FMLeExpression final : public CXFA_FMBinExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMLeExpression() override;
+
+ private:
+  CXFA_FMLeExpression(XFA_FM_TOKEN op,
+                      CXFA_FMSimpleExpression* pExp1,
+                      CXFA_FMSimpleExpression* pExp2);
+};
+
+class CXFA_FMPlusExpression final : public CXFA_FMBinExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMPlusExpression() override;
+
+ private:
+  CXFA_FMPlusExpression(XFA_FM_TOKEN op,
+                        CXFA_FMSimpleExpression* pExp1,
+                        CXFA_FMSimpleExpression* pExp2);
+};
+
+class CXFA_FMMinusExpression final : public CXFA_FMBinExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMMinusExpression() override;
+
+ private:
+  CXFA_FMMinusExpression(XFA_FM_TOKEN op,
+                         CXFA_FMSimpleExpression* pExp1,
+                         CXFA_FMSimpleExpression* pExp2);
+};
+
+class CXFA_FMMulExpression final : public CXFA_FMBinExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMMulExpression() override;
+
+ private:
+  CXFA_FMMulExpression(XFA_FM_TOKEN op,
+                       CXFA_FMSimpleExpression* pExp1,
+                       CXFA_FMSimpleExpression* pExp2);
+};
+
+class CXFA_FMDivExpression final : public CXFA_FMBinExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMDivExpression() override;
+
+ private:
+  CXFA_FMDivExpression(XFA_FM_TOKEN op,
+                       CXFA_FMSimpleExpression* pExp1,
+                       CXFA_FMSimpleExpression* pExp2);
+};
+
+class CXFA_FMUnaryExpression : public CXFA_FMSimpleExpression {
+ public:
+  ~CXFA_FMUnaryExpression() override;
+
+  void Trace(cppgc::Visitor* visitor) const override;
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ protected:
+  CXFA_FMUnaryExpression(const WideString& opName,
+                         XFA_FM_TOKEN op,
+                         CXFA_FMSimpleExpression* pExp);
+
+ private:
+  WideString m_OpName;
+  cppgc::Member<CXFA_FMSimpleExpression> m_pExp;
+};
+
+class CXFA_FMPosExpression final : public CXFA_FMUnaryExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMPosExpression() override;
+
+ private:
+  explicit CXFA_FMPosExpression(CXFA_FMSimpleExpression* pExp);
+};
+
+class CXFA_FMNegExpression final : public CXFA_FMUnaryExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMNegExpression() override;
+
+ private:
+  explicit CXFA_FMNegExpression(CXFA_FMSimpleExpression* pExp);
+};
+
+class CXFA_FMNotExpression final : public CXFA_FMUnaryExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMNotExpression() override;
+
+ private:
+  explicit CXFA_FMNotExpression(CXFA_FMSimpleExpression* pExp);
+};
+
+class CXFA_FMCallExpression final : public CXFA_FMSimpleExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMCallExpression() override;
+
+  void Trace(cppgc::Visitor* visitor) const override;
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+  bool IsBuiltInFunc(WideTextBuffer* funcName) const;
+  uint32_t IsMethodWithObjParam(const WideString& methodName) const;
+
+ private:
+  CXFA_FMCallExpression(
+      CXFA_FMSimpleExpression* pExp,
+      std::vector<cppgc::Member<CXFA_FMSimpleExpression>>&& pArguments,
+      bool bIsSomMethod);
+
+  cppgc::Member<CXFA_FMSimpleExpression> m_pExp;
+  std::vector<cppgc::Member<CXFA_FMSimpleExpression>> m_Arguments;
+  bool m_bIsSomMethod;
+};
+
+class CXFA_FMDotAccessorExpression final : public CXFA_FMChainableExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMDotAccessorExpression() override;
+
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  CXFA_FMDotAccessorExpression(CXFA_FMSimpleExpression* pAccessor,
+                               XFA_FM_TOKEN op,
+                               WideString wsIdentifier,
+                               CXFA_FMSimpleExpression* pIndexExp);
+
+  WideString m_wsIdentifier;
+};
+
+class CXFA_FMIndexExpression final : public CXFA_FMSimpleExpression {
+ public:
+  enum class AccessorIndex : uint8_t {
+    kNoIndex,
+    kNoRelativeIndex,
+    kPositiveIndex,
+    kNegativeIndex
+  };
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMIndexExpression() override;
+
+  void Trace(cppgc::Visitor* visitor) const override;
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  CXFA_FMIndexExpression(AccessorIndex accessorIndex,
+                         CXFA_FMSimpleExpression* pIndexExp,
+                         bool bIsStarIndex);
+
+  cppgc::Member<CXFA_FMSimpleExpression> m_pExp;
+  AccessorIndex m_accessorIndex;
+  bool m_bIsStarIndex;
+};
+
+class CXFA_FMDotDotAccessorExpression final
+    : public CXFA_FMChainableExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMDotDotAccessorExpression() override;
+
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  CXFA_FMDotDotAccessorExpression(CXFA_FMSimpleExpression* pAccessor,
+                                  XFA_FM_TOKEN op,
+                                  WideString wsIdentifier,
+                                  CXFA_FMSimpleExpression* pIndexExp);
+
+  WideString m_wsIdentifier;
+};
+
+class CXFA_FMMethodCallExpression final : public CXFA_FMChainableExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMMethodCallExpression() override;
+
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  CXFA_FMMethodCallExpression(CXFA_FMSimpleExpression* pAccessorExp1,
+                              CXFA_FMSimpleExpression* pCallExp);
+};
+
+class CXFA_FMFunctionDefinition final : public CXFA_FMExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMFunctionDefinition() override;
+
+  void Trace(cppgc::Visitor* visitor) const override;
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  CXFA_FMFunctionDefinition(
+      WideString wsName,
+      std::vector<WideString>&& arguments,
+      std::vector<cppgc::Member<CXFA_FMExpression>>&& expressions);
+
+  const WideString m_wsName;
+  std::vector<WideString> const m_pArguments;
+  std::vector<cppgc::Member<CXFA_FMExpression>> const m_pExpressions;
+};
+
+class CXFA_FMVarExpression final : public CXFA_FMExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMVarExpression() override;
+
+  void Trace(cppgc::Visitor* visitor) const override;
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  CXFA_FMVarExpression(WideString wsName, CXFA_FMSimpleExpression* pInit);
+
+  WideString const m_wsName;
+  cppgc::Member<CXFA_FMSimpleExpression> const m_pInit;
+};
+
+class CXFA_FMExpExpression final : public CXFA_FMExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMExpExpression() override;
+
+  void Trace(cppgc::Visitor* visitor) const override;
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  explicit CXFA_FMExpExpression(CXFA_FMSimpleExpression* pExpression);
+
+  cppgc::Member<CXFA_FMSimpleExpression> const m_pExpression;
+};
+
+class CXFA_FMBlockExpression final : public CXFA_FMExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMBlockExpression() override;
+
+  void Trace(cppgc::Visitor* visitor) const override;
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  CXFA_FMBlockExpression(
+      std::vector<cppgc::Member<CXFA_FMExpression>>&& pExpressionList);
+
+  std::vector<cppgc::Member<CXFA_FMExpression>> const m_ExpressionList;
+};
+
+class CXFA_FMDoExpression final : public CXFA_FMExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMDoExpression() override;
+
+  void Trace(cppgc::Visitor* visitor) const override;
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  explicit CXFA_FMDoExpression(CXFA_FMExpression* pList);
+
+  cppgc::Member<CXFA_FMExpression> const m_pList;
+};
+
+class CXFA_FMIfExpression final : public CXFA_FMExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMIfExpression() override;
+
+  void Trace(cppgc::Visitor* visitor) const override;
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  CXFA_FMIfExpression(
+      CXFA_FMSimpleExpression* pExpression,
+      CXFA_FMExpression* pIfExpression,
+      std::vector<cppgc::Member<CXFA_FMIfExpression>>&& pElseIfExpressions,
+      CXFA_FMExpression* pElseExpression);
+
+  cppgc::Member<CXFA_FMSimpleExpression> const m_pExpression;
+  cppgc::Member<CXFA_FMExpression> const m_pIfExpression;
+  std::vector<cppgc::Member<CXFA_FMIfExpression>> const m_pElseIfExpressions;
+  cppgc::Member<CXFA_FMExpression> const m_pElseExpression;
+};
+
+class CXFA_FMWhileExpression final : public CXFA_FMExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMWhileExpression() override;
+
+  void Trace(cppgc::Visitor* visitor) const override;
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  CXFA_FMWhileExpression(CXFA_FMSimpleExpression* pCodition,
+                         CXFA_FMExpression* pExpression);
+
+  cppgc::Member<CXFA_FMSimpleExpression> const m_pCondition;
+  cppgc::Member<CXFA_FMExpression> const m_pExpression;
+};
+
+class CXFA_FMBreakExpression final : public CXFA_FMExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMBreakExpression() override;
+
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  CXFA_FMBreakExpression();
+};
+
+class CXFA_FMContinueExpression final : public CXFA_FMExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMContinueExpression() override;
+
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  CXFA_FMContinueExpression();
+};
+
+class CXFA_FMForExpression final : public CXFA_FMExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMForExpression() override;
+
+  void Trace(cppgc::Visitor* visitor) const override;
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  CXFA_FMForExpression(WideString wsVariant,
+                       CXFA_FMSimpleExpression* pAssignment,
+                       CXFA_FMSimpleExpression* pAccessor,
+                       int32_t iDirection,
+                       CXFA_FMSimpleExpression* pStep,
+                       CXFA_FMExpression* pList);
+
+  const WideString m_wsVariant;
+  const bool m_bDirection;
+  cppgc::Member<CXFA_FMSimpleExpression> const m_pAssignment;
+  cppgc::Member<CXFA_FMSimpleExpression> const m_pAccessor;
+  cppgc::Member<CXFA_FMSimpleExpression> const m_pStep;
+  cppgc::Member<CXFA_FMExpression> const m_pList;
+};
+
+class CXFA_FMForeachExpression final : public CXFA_FMExpression {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMForeachExpression() override;
+
+  void Trace(cppgc::Visitor* visitor) const override;
+  bool ToJavaScript(WideTextBuffer* js, ReturnType type) const override;
+
+ private:
+  // Takes ownership of |pAccessors|.
+  CXFA_FMForeachExpression(
+      WideString wsIdentifier,
+      std::vector<cppgc::Member<CXFA_FMSimpleExpression>>&& pAccessors,
+      CXFA_FMExpression* pList);
+
+  const WideString m_wsIdentifier;
+  std::vector<cppgc::Member<CXFA_FMSimpleExpression>> const m_pAccessors;
+  cppgc::Member<CXFA_FMExpression> const m_pList;
+};
+
+class CXFA_FMAST : public cppgc::GarbageCollected<CXFA_FMAST> {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_FMAST();
+
+  void Trace(cppgc::Visitor* visitor) const;
+  absl::optional<WideTextBuffer> ToJavaScript() const;
+
+ private:
+  explicit CXFA_FMAST(
+      std::vector<cppgc::Member<CXFA_FMExpression>> expressions);
+
+  std::vector<cppgc::Member<CXFA_FMExpression>> const expressions_;
+};
+
+bool CXFA_IsTooBig(const WideTextBuffer& js);
+
+#endif  // XFA_FXFA_FORMCALC_CXFA_FMEXPRESSION_H_
diff --git a/xfa/fxfa/formcalc/cxfa_fmexpression_unittest.cpp b/xfa/fxfa/formcalc/cxfa_fmexpression_unittest.cpp
new file mode 100644
index 0000000..12ff832
--- /dev/null
+++ b/xfa/fxfa/formcalc/cxfa_fmexpression_unittest.cpp
@@ -0,0 +1,170 @@
+// Copyright 2018 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fxfa/formcalc/cxfa_fmexpression.h"
+
+#include <utility>
+
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widetext_buffer.h"
+#include "testing/fxgc_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "v8/include/cppgc/heap.h"
+#include "xfa/fxfa/formcalc/cxfa_fmlexer.h"
+#include "xfa/fxfa/formcalc/cxfa_fmtojavascriptdepth.h"
+
+class FMExpressionTest : public FXGCUnitTest {};
+class FMCallExpressionTest : public FXGCUnitTest {};
+class FMStringExpressionTest : public FXGCUnitTest {};
+
+TEST_F(FMCallExpressionTest, more_than_32_arguments) {
+  // Use sign as it has 3 object parameters at positions 0, 5, and 6.
+  auto* exp = cppgc::MakeGarbageCollected<CXFA_FMIdentifierExpression>(
+      heap()->GetAllocationHandle(), L"sign");
+
+  std::vector<cppgc::Member<CXFA_FMSimpleExpression>> args;
+  for (size_t i = 0; i < 50; i++) {
+    args.push_back(cppgc::MakeGarbageCollected<CXFA_FMNullExpression>(
+        heap()->GetAllocationHandle()));
+  }
+  CXFA_FMToJavaScriptDepth::Reset();
+  auto* callExp = cppgc::MakeGarbageCollected<CXFA_FMCallExpression>(
+      heap()->GetAllocationHandle(), exp, std::move(args), true);
+
+  WideTextBuffer js;
+  callExp->ToJavaScript(&js, CXFA_FMAssignExpression::ReturnType::kInferred);
+
+  // Generate the result javascript string.
+  WideString result = L"sign(";
+  for (size_t i = 0; i < 50; i++) {
+    if (i > 0)
+      result += L", ";
+
+    result += L"pfm_rt.get_";
+    // Object positions for sign() method.
+    if (i == 0 || i == 5 || i == 6)
+      result += L"jsobj(null)";
+    else
+      result += L"val(null)";
+  }
+  result += L")";
+
+  EXPECT_EQ(result.AsStringView(), js.AsStringView());
+}
+
+TEST_F(FMStringExpressionTest, Empty) {
+  CXFA_FMToJavaScriptDepth::Reset();
+  WideTextBuffer accumulator;
+  auto* exp = cppgc::MakeGarbageCollected<CXFA_FMStringExpression>(
+      heap()->GetAllocationHandle(), L"");
+  exp->ToJavaScript(&accumulator,
+                    CXFA_FMAssignExpression::ReturnType::kInferred);
+  EXPECT_EQ(L"", accumulator.AsStringView());
+}
+
+TEST_F(FMStringExpressionTest, Short) {
+  CXFA_FMToJavaScriptDepth::Reset();
+  WideTextBuffer accumulator;
+  auto* exp = cppgc::MakeGarbageCollected<CXFA_FMStringExpression>(
+      heap()->GetAllocationHandle(), L"a");
+  exp->ToJavaScript(&accumulator,
+                    CXFA_FMAssignExpression::ReturnType::kInferred);
+  EXPECT_EQ(L"a", accumulator.AsStringView());
+}
+
+TEST_F(FMStringExpressionTest, Medium) {
+  CXFA_FMToJavaScriptDepth::Reset();
+  WideTextBuffer accumulator;
+  auto* exp = cppgc::MakeGarbageCollected<CXFA_FMStringExpression>(
+      heap()->GetAllocationHandle(), L".abcd.");
+  exp->ToJavaScript(&accumulator,
+                    CXFA_FMAssignExpression::ReturnType::kInferred);
+  EXPECT_EQ(L"\"abcd\"", accumulator.AsStringView());
+}
+
+TEST_F(FMStringExpressionTest, Long) {
+  CXFA_FMToJavaScriptDepth::Reset();
+  WideTextBuffer accumulator;
+  std::vector<WideStringView::UnsignedType> vec(140000, L'A');
+  auto* exp = cppgc::MakeGarbageCollected<CXFA_FMStringExpression>(
+      heap()->GetAllocationHandle(), WideString(WideStringView(vec)));
+  exp->ToJavaScript(&accumulator,
+                    CXFA_FMAssignExpression::ReturnType::kInferred);
+  EXPECT_EQ(140000u, accumulator.GetLength());
+}
+
+TEST_F(FMStringExpressionTest, Quoted) {
+  CXFA_FMToJavaScriptDepth::Reset();
+  WideTextBuffer accumulator;
+  auto* exp = cppgc::MakeGarbageCollected<CXFA_FMStringExpression>(
+      heap()->GetAllocationHandle(), L".Simon says \"\"run\"\".");
+  exp->ToJavaScript(&accumulator,
+                    CXFA_FMAssignExpression::ReturnType::kInferred);
+  EXPECT_EQ(L"\"Simon says \\\"run\\\"\"", accumulator.AsStringView());
+}
+
+TEST_F(FMExpressionTest, VarExpressionInitNull) {
+  CXFA_FMToJavaScriptDepth::Reset();
+  WideTextBuffer accumulator;
+
+  auto* expr = cppgc::MakeGarbageCollected<CXFA_FMVarExpression>(
+      heap()->GetAllocationHandle(), L"s", nullptr);
+  expr->ToJavaScript(&accumulator,
+                     CXFA_FMAssignExpression::ReturnType::kInferred);
+  EXPECT_STREQ(
+      LR"***(var s = "";
+)***",
+      accumulator.MakeString().c_str());
+}
+
+TEST_F(FMExpressionTest, VarExpressionInitBlank) {
+  CXFA_FMToJavaScriptDepth::Reset();
+  WideTextBuffer accumulator;
+
+  auto* init = cppgc::MakeGarbageCollected<CXFA_FMStringExpression>(
+      heap()->GetAllocationHandle(), LR"("")");
+  auto* expr = cppgc::MakeGarbageCollected<CXFA_FMVarExpression>(
+      heap()->GetAllocationHandle(), L"s", init);
+  expr->ToJavaScript(&accumulator,
+                     CXFA_FMAssignExpression::ReturnType::kInferred);
+  EXPECT_STREQ(
+      LR"***(var s = "";
+s = pfm_rt.var_filter(s);
+)***",
+      accumulator.MakeString().c_str());
+}
+
+TEST_F(FMExpressionTest, VarExpressionInitString) {
+  CXFA_FMToJavaScriptDepth::Reset();
+  WideTextBuffer accumulator;
+
+  auto* init = cppgc::MakeGarbageCollected<CXFA_FMStringExpression>(
+      heap()->GetAllocationHandle(), LR"("foo")");
+  auto* expr = cppgc::MakeGarbageCollected<CXFA_FMVarExpression>(
+      heap()->GetAllocationHandle(), L"s", init);
+  expr->ToJavaScript(&accumulator,
+                     CXFA_FMAssignExpression::ReturnType::kInferred);
+  EXPECT_STREQ(
+      LR"***(var s = "foo";
+s = pfm_rt.var_filter(s);
+)***",
+      accumulator.MakeString().c_str());
+}
+
+TEST_F(FMExpressionTest, VarExpressionInitNumeric) {
+  CXFA_FMToJavaScriptDepth::Reset();
+  WideTextBuffer accumulator;
+
+  auto* init = cppgc::MakeGarbageCollected<CXFA_FMNumberExpression>(
+      heap()->GetAllocationHandle(), L"112");
+  auto* expr = cppgc::MakeGarbageCollected<CXFA_FMVarExpression>(
+      heap()->GetAllocationHandle(), L"s", init);
+  expr->ToJavaScript(&accumulator,
+                     CXFA_FMAssignExpression::ReturnType::kInferred);
+  EXPECT_STREQ(
+      LR"***(var s = 112;
+s = pfm_rt.var_filter(s);
+)***",
+      accumulator.MakeString().c_str());
+}
diff --git a/xfa/fxfa/formcalc/cxfa_fmlexer.cpp b/xfa/fxfa/formcalc/cxfa_fmlexer.cpp
new file mode 100644
index 0000000..234b709
--- /dev/null
+++ b/xfa/fxfa/formcalc/cxfa_fmlexer.cpp
@@ -0,0 +1,396 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fxfa/formcalc/cxfa_fmlexer.h"
+
+#include <algorithm>
+
+#include "core/fxcrt/fx_extension.h"
+
+namespace {
+
+bool IsFormCalcCharacter(wchar_t c) {
+  return (c >= 0x09 && c <= 0x0D) || (c >= 0x20 && c <= 0xd7FF) ||
+         (c >= 0xE000 && c <= 0xFFFD);
+}
+
+bool IsIdentifierCharacter(wchar_t c) {
+  return FXSYS_iswalnum(c) || c == 0x005F ||  // '_'
+         c == 0x0024;                         // '$'
+}
+
+bool IsInitialIdentifierCharacter(wchar_t c) {
+  return FXSYS_iswalpha(c) || c == 0x005F ||  // '_'
+         c == 0x0024 ||                       // '$'
+         c == 0x0021;                         // '!'
+}
+
+bool IsWhitespaceCharacter(wchar_t c) {
+  return c == 0x0009 ||  // Horizontal tab
+         c == 0x000B ||  // Vertical tab
+         c == 0x000C ||  // Form feed
+         c == 0x0020;    // Space
+}
+
+struct XFA_FMKeyword {
+  XFA_FM_TOKEN m_type;
+  const char* m_keyword;  // Raw, POD struct.
+};
+
+const XFA_FMKeyword keyWords[] = {
+    {TOKdo, "do"},
+    {TOKkseq, "eq"},
+    {TOKksge, "ge"},
+    {TOKksgt, "gt"},
+    {TOKif, "if"},
+    {TOKin, "in"},
+    {TOKksle, "le"},
+    {TOKkslt, "lt"},
+    {TOKksne, "ne"},
+    {TOKksor, "or"},
+    {TOKnull, "null"},
+    {TOKbreak, "break"},
+    {TOKksand, "and"},
+    {TOKend, "end"},
+    {TOKeof, "eof"},
+    {TOKfor, "for"},
+    {TOKnan, "nan"},
+    {TOKksnot, "not"},
+    {TOKvar, "var"},
+    {TOKthen, "then"},
+    {TOKelse, "else"},
+    {TOKexit, "exit"},
+    {TOKdownto, "downto"},
+    {TOKreturn, "return"},
+    {TOKinfinity, "infinity"},
+    {TOKendwhile, "endwhile"},
+    {TOKforeach, "foreach"},
+    {TOKendfunc, "endfunc"},
+    {TOKelseif, "elseif"},
+    {TOKwhile, "while"},
+    {TOKendfor, "endfor"},
+    {TOKthrow, "throw"},
+    {TOKstep, "step"},
+    {TOKupto, "upto"},
+    {TOKcontinue, "continue"},
+    {TOKfunc, "func"},
+    {TOKendif, "endif"},
+};
+
+#ifndef NDEBUG
+const char* const tokenStrings[] = {
+    "TOKand",        "TOKlparen",     "TOKrparen",   "TOKmul",
+    "TOKplus",       "TOKcomma",      "TOKminus",    "TOKdot",
+    "TOKdiv",        "TOKlt",         "TOKassign",   "TOKgt",
+    "TOKlbracket",   "TOKrbracket",   "TOKor",       "TOKdotscream",
+    "TOKdotstar",    "TOKdotdot",     "TOKle",       "TOKne",
+    "TOKeq",         "TOKge",         "TOKdo",       "TOKkseq",
+    "TOKksge",       "TOKksgt",       "TOKif",       "TOKin",
+    "TOKksle",       "TOKkslt",       "TOKksne",     "TOKksor",
+    "TOKnull",       "TOKbreak",      "TOKksand",    "TOKend",
+    "TOKeof",        "TOKfor",        "TOKnan",      "TOKksnot",
+    "TOKvar",        "TOKthen",       "TOKelse",     "TOKexit",
+    "TOKdownto",     "TOKreturn",     "TOKinfinity", "TOKendwhile",
+    "TOKforeach",    "TOKendfunc",    "TOKelseif",   "TOKwhile",
+    "TOKendfor",     "TOKthrow",      "TOKstep",     "TOKupto",
+    "TOKcontinue",   "TOKfunc",       "TOKendif",    "TOKstar",
+    "TOKidentifier", "TOKunderscore", "TOKdollar",   "TOKexclamation",
+    "TOKcall",       "TOKstring",     "TOKnumber",   "TOKreserver",
+};
+#endif  // NDEBUG
+
+XFA_FM_TOKEN TokenizeIdentifier(WideStringView str) {
+  const XFA_FMKeyword* result =
+      std::find_if(std::begin(keyWords), std::end(keyWords),
+                   [str](const XFA_FMKeyword& iter) {
+                     return str.EqualsASCII(iter.m_keyword);
+                   });
+  if (result != std::end(keyWords) && str.EqualsASCII(result->m_keyword))
+    return result->m_type;
+  return TOKidentifier;
+}
+
+}  // namespace
+
+CXFA_FMLexer::Token::Token() = default;
+
+CXFA_FMLexer::Token::Token(XFA_FM_TOKEN token) : m_type(token) {}
+
+CXFA_FMLexer::Token::Token(XFA_FM_TOKEN token, WideStringView str)
+    : m_type(token), m_string(str) {}
+
+CXFA_FMLexer::Token::Token(const Token& that) = default;
+
+CXFA_FMLexer::Token::~Token() = default;
+
+#ifndef NDEBUG
+WideString CXFA_FMLexer::Token::ToDebugString() const {
+  WideString str = WideString::FromASCII("type = ");
+  str += WideString::FromASCII(tokenStrings[m_type]);
+  str += WideString::FromASCII(", string = ");
+  str += m_string;
+  return str;
+}
+#endif  // NDEBUG
+
+CXFA_FMLexer::CXFA_FMLexer(WideStringView wsFormCalc)
+    : m_spInput(wsFormCalc.span()) {}
+
+CXFA_FMLexer::~CXFA_FMLexer() = default;
+
+CXFA_FMLexer::Token CXFA_FMLexer::NextToken() {
+  if (m_bLexerError)
+    return Token();
+
+  while (!IsComplete() && m_spInput[m_nCursor]) {
+    if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
+      RaiseError();
+      return Token();
+    }
+
+    switch (m_spInput[m_nCursor]) {
+      case '\n':
+        ++m_nCursor;
+        break;
+      case '\r':
+        ++m_nCursor;
+        break;
+      case ';':
+        AdvanceForComment();
+        break;
+      case '"':
+        return AdvanceForString();
+      case '0':
+      case '1':
+      case '2':
+      case '3':
+      case '4':
+      case '5':
+      case '6':
+      case '7':
+      case '8':
+      case '9':
+        return AdvanceForNumber();
+      case '=':
+        ++m_nCursor;
+        if (m_nCursor >= m_spInput.size())
+          return Token(TOKassign);
+
+        if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
+          RaiseError();
+          return Token();
+        }
+        if (m_spInput[m_nCursor] == '=') {
+          ++m_nCursor;
+          return Token(TOKeq);
+        }
+        return Token(TOKassign);
+      case '<':
+        ++m_nCursor;
+        if (m_nCursor >= m_spInput.size())
+          return Token(TOKlt);
+
+        if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
+          RaiseError();
+          return Token();
+        }
+        if (m_spInput[m_nCursor] == '=') {
+          ++m_nCursor;
+          return Token(TOKle);
+        }
+        if (m_spInput[m_nCursor] == '>') {
+          ++m_nCursor;
+          return Token(TOKne);
+        }
+        return Token(TOKlt);
+      case '>':
+        ++m_nCursor;
+        if (m_nCursor >= m_spInput.size())
+          return Token(TOKgt);
+
+        if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
+          RaiseError();
+          return Token();
+        }
+        if (m_spInput[m_nCursor] == '=') {
+          ++m_nCursor;
+          return Token(TOKge);
+        }
+        return Token(TOKgt);
+      case ',':
+        ++m_nCursor;
+        return Token(TOKcomma);
+      case '(':
+        ++m_nCursor;
+        return Token(TOKlparen);
+      case ')':
+        ++m_nCursor;
+        return Token(TOKrparen);
+      case '[':
+        ++m_nCursor;
+        return Token(TOKlbracket);
+      case ']':
+        ++m_nCursor;
+        return Token(TOKrbracket);
+      case '&':
+        ++m_nCursor;
+        return Token(TOKand);
+      case '|':
+        ++m_nCursor;
+        return Token(TOKor);
+      case '+':
+        ++m_nCursor;
+        return Token(TOKplus);
+      case '-':
+        ++m_nCursor;
+        return Token(TOKminus);
+      case '*':
+        ++m_nCursor;
+        return Token(TOKmul);
+      case '/': {
+        ++m_nCursor;
+        if (m_nCursor >= m_spInput.size())
+          return Token(TOKdiv);
+
+        if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
+          RaiseError();
+          return Token();
+        }
+        if (m_spInput[m_nCursor] != '/')
+          return Token(TOKdiv);
+
+        AdvanceForComment();
+        break;
+      }
+      case '.':
+        ++m_nCursor;
+        if (m_nCursor >= m_spInput.size())
+          return Token(TOKdot);
+
+        if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
+          RaiseError();
+          return Token();
+        }
+
+        if (m_spInput[m_nCursor] == '.') {
+          ++m_nCursor;
+          return Token(TOKdotdot);
+        }
+        if (m_spInput[m_nCursor] == '*') {
+          ++m_nCursor;
+          return Token(TOKdotstar);
+        }
+        if (m_spInput[m_nCursor] == '#') {
+          ++m_nCursor;
+          return Token(TOKdotscream);
+        }
+        if (FXSYS_IsDecimalDigit(m_spInput[m_nCursor])) {
+          --m_nCursor;
+          return AdvanceForNumber();
+        }
+        return Token(TOKdot);
+      default:
+        if (IsWhitespaceCharacter(m_spInput[m_nCursor])) {
+          ++m_nCursor;
+          break;
+        }
+        if (!IsInitialIdentifierCharacter(m_spInput[m_nCursor])) {
+          RaiseError();
+          return Token();
+        }
+        return AdvanceForIdentifier();
+    }
+  }
+  return Token(TOKeof);
+}
+
+CXFA_FMLexer::Token CXFA_FMLexer::AdvanceForNumber() {
+  // This will set end to the character after the end of the number.
+  size_t used_length = 0;
+  if (m_nCursor < m_spInput.size()) {
+    FXSYS_wcstof(&m_spInput[m_nCursor], m_spInput.size() - m_nCursor,
+                 &used_length);
+  }
+  size_t end = m_nCursor + used_length;
+  if (used_length == 0 ||
+      (end < m_spInput.size() && FXSYS_iswalpha(m_spInput[end]))) {
+    RaiseError();
+    return Token();
+  }
+  WideStringView str(m_spInput.subspan(m_nCursor, end - m_nCursor));
+  m_nCursor = end;
+  return Token(TOKnumber, str);
+}
+
+CXFA_FMLexer::Token CXFA_FMLexer::AdvanceForString() {
+  size_t start = m_nCursor;
+  ++m_nCursor;
+  while (!IsComplete() && m_spInput[m_nCursor]) {
+    if (!IsFormCalcCharacter(m_spInput[m_nCursor]))
+      break;
+
+    if (m_spInput[m_nCursor] == '"') {
+      // Check for escaped "s, i.e. "".
+      ++m_nCursor;
+      // If the end of the input has been reached it was not escaped.
+      if (m_nCursor >= m_spInput.size()) {
+        return Token(TOKstring, WideStringView(m_spInput.subspan(
+                                    start, m_nCursor - start)));
+      }
+      // If the next character is not a " then the end of the string has been
+      // found.
+      if (m_spInput[m_nCursor] != '"') {
+        if (!IsFormCalcCharacter(m_spInput[m_nCursor]))
+          break;
+
+        return Token(TOKstring, WideStringView(m_spInput.subspan(
+                                    start, m_nCursor - start)));
+      }
+    }
+    ++m_nCursor;
+  }
+
+  // Didn't find the end of the string.
+  RaiseError();
+  return Token();
+}
+
+CXFA_FMLexer::Token CXFA_FMLexer::AdvanceForIdentifier() {
+  size_t start = m_nCursor;
+  ++m_nCursor;
+  while (!IsComplete() && m_spInput[m_nCursor]) {
+    if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
+      RaiseError();
+      return Token();
+    }
+    if (!IsIdentifierCharacter(m_spInput[m_nCursor]))
+      break;
+
+    ++m_nCursor;
+  }
+
+  WideStringView str(m_spInput.subspan(start, m_nCursor - start));
+  return Token(TokenizeIdentifier(str), str);
+}
+
+void CXFA_FMLexer::AdvanceForComment() {
+  ++m_nCursor;
+  while (!IsComplete() && m_spInput[m_nCursor]) {
+    if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
+      RaiseError();
+      return;
+    }
+    if (m_spInput[m_nCursor] == L'\r') {
+      ++m_nCursor;
+      return;
+    }
+    if (m_spInput[m_nCursor] == L'\n') {
+      ++m_nCursor;
+      return;
+    }
+    ++m_nCursor;
+  }
+}
diff --git a/xfa/fxfa/formcalc/cxfa_fmlexer.h b/xfa/fxfa/formcalc/cxfa_fmlexer.h
new file mode 100644
index 0000000..926d1ba
--- /dev/null
+++ b/xfa/fxfa/formcalc/cxfa_fmlexer.h
@@ -0,0 +1,126 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FXFA_FORMCALC_CXFA_FMLEXER_H_
+#define XFA_FXFA_FORMCALC_CXFA_FMLEXER_H_
+
+#include "core/fxcrt/widestring.h"
+#include "v8/include/cppgc/macros.h"
+
+enum XFA_FM_TOKEN {
+  TOKand,
+  TOKlparen,
+  TOKrparen,
+  TOKmul,
+  TOKplus,
+  TOKcomma,
+  TOKminus,
+  TOKdot,
+  TOKdiv,
+  TOKlt,
+  TOKassign,
+  TOKgt,
+  TOKlbracket,
+  TOKrbracket,
+  TOKor,
+  TOKdotscream,
+  TOKdotstar,
+  TOKdotdot,
+  TOKle,
+  TOKne,
+  TOKeq,
+  TOKge,
+  TOKdo,
+  TOKkseq,
+  TOKksge,
+  TOKksgt,
+  TOKif,
+  TOKin,
+  TOKksle,
+  TOKkslt,
+  TOKksne,
+  TOKksor,
+  TOKnull,
+  TOKbreak,
+  TOKksand,
+  TOKend,
+  TOKeof,
+  TOKfor,
+  TOKnan,
+  TOKksnot,
+  TOKvar,
+  TOKthen,
+  TOKelse,
+  TOKexit,
+  TOKdownto,
+  TOKreturn,
+  TOKinfinity,
+  TOKendwhile,
+  TOKforeach,
+  TOKendfunc,
+  TOKelseif,
+  TOKwhile,
+  TOKendfor,
+  TOKthrow,
+  TOKstep,
+  TOKupto,
+  TOKcontinue,
+  TOKfunc,
+  TOKendif,
+  TOKstar,
+  TOKidentifier,
+  TOKunderscore,
+  TOKdollar,
+  TOKexclamation,
+  TOKcall,
+  TOKstring,
+  TOKnumber,
+  TOKreserver
+};
+
+class CXFA_FMLexer {
+  CPPGC_STACK_ALLOCATED();  // Raw pointers allowed.
+
+ public:
+  class Token {
+   public:
+    Token();
+    explicit Token(XFA_FM_TOKEN token);
+    Token(XFA_FM_TOKEN token, WideStringView str);
+    Token(const Token& that);
+    ~Token();
+
+    XFA_FM_TOKEN GetType() const { return m_type; }
+    WideStringView GetString() const { return m_string; }
+#ifndef NDEBUG
+    WideString ToDebugString() const;
+#endif  // NDEBUG
+
+   private:
+    XFA_FM_TOKEN m_type = TOKreserver;
+    WideStringView m_string;
+  };
+
+  explicit CXFA_FMLexer(WideStringView wsFormcalc);
+  ~CXFA_FMLexer();
+
+  Token NextToken();
+  bool IsComplete() const { return m_nCursor >= m_spInput.size(); }
+
+ private:
+  Token AdvanceForNumber();
+  Token AdvanceForString();
+  Token AdvanceForIdentifier();
+  void AdvanceForComment();
+
+  void RaiseError() { m_bLexerError = true; }
+
+  pdfium::span<const wchar_t> m_spInput;
+  size_t m_nCursor = 0;
+  bool m_bLexerError = false;
+};
+
+#endif  // XFA_FXFA_FORMCALC_CXFA_FMLEXER_H_
diff --git a/xfa/fxfa/formcalc/cxfa_fmlexer_unittest.cpp b/xfa/fxfa/formcalc/cxfa_fmlexer_unittest.cpp
new file mode 100644
index 0000000..5991fb7
--- /dev/null
+++ b/xfa/fxfa/formcalc/cxfa_fmlexer_unittest.cpp
@@ -0,0 +1,317 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fxfa/formcalc/cxfa_fmlexer.h"
+
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(CXFA_FMLexerTest, NullString) {
+  WideStringView null_string;
+  CXFA_FMLexer lexer(null_string);
+  CXFA_FMLexer::Token token = lexer.NextToken();
+  EXPECT_EQ(TOKeof, token.GetType());
+  EXPECT_TRUE(lexer.IsComplete());
+}
+
+TEST(CXFA_FMLexerTest, EmptyString) {
+  CXFA_FMLexer lexer(L"");
+  CXFA_FMLexer::Token token = lexer.NextToken();
+  EXPECT_EQ(TOKeof, token.GetType());
+  EXPECT_TRUE(lexer.IsComplete());
+}
+
+TEST(CXFA_FMLexerTest, Numbers) {
+  {
+    CXFA_FMLexer lexer(L"-12");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    // TODO(dsinclair): Should this return -12 instead of two tokens?
+    EXPECT_EQ(TOKminus, token.GetType());
+    token = lexer.NextToken();
+    EXPECT_EQ(L"12", token.GetString());
+    token = lexer.NextToken();
+    EXPECT_EQ(TOKeof, token.GetType());
+  }
+  {
+    CXFA_FMLexer lexer(L"1.5362");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKnumber, token.GetType());
+    EXPECT_EQ(L"1.5362", token.GetString());
+  }
+  {
+    CXFA_FMLexer lexer(L"0.875");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKnumber, token.GetType());
+    EXPECT_EQ(L"0.875", token.GetString());
+  }
+  {
+    CXFA_FMLexer lexer(L"5.56e-2");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKnumber, token.GetType());
+    EXPECT_EQ(L"5.56e-2", token.GetString());
+  }
+  {
+    CXFA_FMLexer lexer(L"1.234E10");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKnumber, token.GetType());
+    EXPECT_EQ(L"1.234E10", token.GetString());
+  }
+  {
+    CXFA_FMLexer lexer(L"123456789.012345678");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKnumber, token.GetType());
+    // TODO(dsinclair): This should round as per IEEE 64-bit values.
+    // EXPECT_EQ(L"123456789.01234567", token.GetString());
+    EXPECT_EQ(L"123456789.012345678", token.GetString());
+  }
+  {
+    CXFA_FMLexer lexer(L"99999999999999999");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKnumber, token.GetType());
+    // TODO(dsinclair): This is spec'd as rounding when > 16 significant digits
+    // prior to the exponent.
+    // EXPECT_EQ(L"100000000000000000", token.GetString());
+    EXPECT_EQ(L"99999999999999999", token.GetString());
+    EXPECT_TRUE(lexer.IsComplete());
+  }
+}
+
+// The quotes are stripped in CXFA_FMStringExpression::ToJavaScript.
+TEST(CXFA_FMLexerTest, Strings) {
+  {
+    CXFA_FMLexer lexer(L"\"The cat jumped over the fence.\"");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKstring, token.GetType());
+    EXPECT_EQ(L"\"The cat jumped over the fence.\"", token.GetString());
+
+    token = lexer.NextToken();
+    EXPECT_EQ(TOKeof, token.GetType());
+  }
+  {
+    CXFA_FMLexer lexer(L"\"\"");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKstring, token.GetType());
+    EXPECT_EQ(L"\"\"", token.GetString());
+  }
+  {
+    CXFA_FMLexer lexer(
+        L"\"The message reads: \"\"Warning: Insufficient Memory\"\"\"");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKstring, token.GetType());
+    EXPECT_EQ(L"\"The message reads: \"\"Warning: Insufficient Memory\"\"\"",
+              token.GetString());
+  }
+  {
+    CXFA_FMLexer lexer(
+        L"\"\\u0047\\u006f\\u0066\\u0069\\u0073\\u0068\\u0021\\u000d\\u000a\"");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKstring, token.GetType());
+    EXPECT_EQ(
+        L"\"\\u0047\\u006f\\u0066\\u0069\\u0073\\u0068\\u0021\\u000d\\u000a\"",
+        token.GetString());
+    EXPECT_TRUE(lexer.IsComplete());
+  }
+}
+
+// Note, 'this' is a keyword but is not matched by the lexer.
+TEST(CXFA_FMLexerTest, OperatorsAndKeywords) {
+  struct {
+    const wchar_t* op;
+    XFA_FM_TOKEN token;
+  } op[] = {{L"+", TOKplus},
+            {L"/", TOKdiv},
+            {L"-", TOKminus},
+            {L"&", TOKand},
+            {L"|", TOKor},
+            {L"*", TOKmul},
+            {L"<", TOKlt},
+            {L">", TOKgt},
+            {L"==", TOKeq},
+            {L"<>", TOKne},
+            {L"<=", TOKle},
+            {L">=", TOKge},
+            {L"and", TOKksand},
+            {L"break", TOKbreak},
+            {L"continue", TOKcontinue},
+            {L"do", TOKdo},
+            {L"downto", TOKdownto},
+            {L"else", TOKelse},
+            {L"elseif", TOKelseif},
+            {L"end", TOKend},
+            {L"endfor", TOKendfor},
+            {L"endfunc", TOKendfunc},
+            {L"endif", TOKendif},
+            {L"endwhile", TOKendwhile},
+            {L"eq", TOKkseq},
+            {L"exit", TOKexit},
+            {L"for", TOKfor},
+            {L"foreach", TOKforeach},
+            {L"func", TOKfunc},
+            {L"ge", TOKksge},
+            {L"gt", TOKksgt},
+            {L"if", TOKif},
+            {L"in", TOKin},
+            {L"infinity", TOKinfinity},
+            {L"le", TOKksle},
+            {L"lt", TOKkslt},
+            {L"nan", TOKnan},
+            {L"ne", TOKksne},
+            {L"not", TOKksnot},
+            {L"null", TOKnull},
+            {L"or", TOKksor},
+            {L"return", TOKreturn},
+            {L"step", TOKstep},
+            {L"then", TOKthen},
+            {L"throw", TOKthrow},
+            {L"upto", TOKupto},
+            {L"var", TOKvar},
+            {L"while", TOKwhile},
+
+            // The following are defined but aren't in the spec.
+            {L"(", TOKlparen},
+            {L")", TOKrparen},
+            {L",", TOKcomma},
+            {L".", TOKdot},
+            {L"[", TOKlbracket},
+            {L"]", TOKrbracket},
+            {L"..", TOKdotdot},
+            {L".#", TOKdotscream},
+            {L".*", TOKdotstar}};
+
+  for (size_t i = 0; i < std::size(op); ++i) {
+    CXFA_FMLexer lexer(op[i].op);
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(op[i].token, token.GetType());
+    EXPECT_TRUE(lexer.IsComplete());
+  }
+}
+
+TEST(CXFA_FMLexerTest, Comments) {
+  {
+    CXFA_FMLexer lexer(L"// Empty.");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKeof, token.GetType());
+  }
+  {
+    CXFA_FMLexer lexer(L"//");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKeof, token.GetType());
+  }
+  {
+    CXFA_FMLexer lexer(L"123 // Empty.\n\"str\"");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKnumber, token.GetType());
+    EXPECT_EQ(L"123", token.GetString());
+
+    token = lexer.NextToken();
+    EXPECT_EQ(TOKstring, token.GetType());
+    EXPECT_EQ(L"\"str\"", token.GetString());
+
+    token = lexer.NextToken();
+    EXPECT_EQ(TOKeof, token.GetType());
+  }
+  {
+    CXFA_FMLexer lexer(L";");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKeof, token.GetType());
+  }
+  {
+    CXFA_FMLexer lexer(L"; Empty.");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKeof, token.GetType());
+  }
+  {
+    CXFA_FMLexer lexer(L"123 ;Empty.\n\"str\"");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKnumber, token.GetType());
+    EXPECT_EQ(L"123", token.GetString());
+
+    token = lexer.NextToken();
+    EXPECT_EQ(TOKstring, token.GetType());
+    EXPECT_EQ(L"\"str\"", token.GetString());
+
+    token = lexer.NextToken();
+    EXPECT_EQ(TOKeof, token.GetType());
+    EXPECT_TRUE(lexer.IsComplete());
+  }
+}
+
+TEST(CXFA_FMLexerTest, ValidIdentifiers) {
+  std::vector<const wchar_t*> identifiers = {
+      L"a", L"an_identifier", L"_ident", L"$ident", L"!ident", L"GetAddr"};
+  for (const auto* ident : identifiers) {
+    CXFA_FMLexer lexer(ident);
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKidentifier, token.GetType());
+    EXPECT_EQ(ident, token.GetString());
+    EXPECT_TRUE(lexer.IsComplete());
+  }
+}
+
+TEST(CXFA_FMLexerTest, InvalidIdentifiers) {
+  {
+    CXFA_FMLexer lexer(L"#a");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKreserver, token.GetType());
+  }
+  {
+    CXFA_FMLexer lexer(L"1a");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKreserver, token.GetType());
+  }
+  {
+    CXFA_FMLexer lexer(L"an@identifier");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_NE(TOKreserver, token.GetType());
+    token = lexer.NextToken();
+    EXPECT_EQ(TOKreserver, token.GetType());
+    token = lexer.NextToken();
+    EXPECT_EQ(TOKreserver, token.GetType());
+  }
+  {
+    CXFA_FMLexer lexer(L"_ident@");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_NE(TOKreserver, token.GetType());
+    token = lexer.NextToken();
+    EXPECT_EQ(TOKreserver, token.GetType());
+    EXPECT_FALSE(lexer.IsComplete());
+  }
+}
+
+TEST(CXFA_FMLexerTest, Whitespace) {
+  {
+    CXFA_FMLexer lexer(L" \t\xc\x9\xb");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKeof, token.GetType());
+  }
+  {
+    CXFA_FMLexer lexer(L"123 \t\xc\x9\xb 456");
+    CXFA_FMLexer::Token token = lexer.NextToken();
+    EXPECT_EQ(TOKnumber, token.GetType());
+    EXPECT_EQ(L"123", token.GetString());
+
+    token = lexer.NextToken();
+    EXPECT_EQ(TOKnumber, token.GetType());
+    EXPECT_EQ(L"456", token.GetString());
+
+    token = lexer.NextToken();
+    EXPECT_EQ(TOKeof, token.GetType());
+    EXPECT_TRUE(lexer.IsComplete());
+  }
+}
+
+TEST(CXFA_FMLexerTest, NullData) {
+  CXFA_FMLexer lexer(WideStringView(L"\x2d\x32\x00\x2d\x32", 5));
+  CXFA_FMLexer::Token token = lexer.NextToken();
+  EXPECT_EQ(TOKminus, token.GetType());
+
+  token = lexer.NextToken();
+  EXPECT_EQ(TOKnumber, token.GetType());
+  EXPECT_EQ(L"2", token.GetString());
+
+  token = lexer.NextToken();
+  EXPECT_EQ(TOKeof, token.GetType());
+  EXPECT_FALSE(lexer.IsComplete());
+}
diff --git a/xfa/fxfa/formcalc/cxfa_fmparser.cpp b/xfa/fxfa/formcalc/cxfa_fmparser.cpp
new file mode 100644
index 0000000..7c73c37
--- /dev/null
+++ b/xfa/fxfa/formcalc/cxfa_fmparser.cpp
@@ -0,0 +1,1103 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fxfa/formcalc/cxfa_fmparser.h"
+
+#include <utility>
+#include <vector>
+
+#include "core/fxcrt/autorestorer.h"
+#include "v8/include/cppgc/heap.h"
+
+namespace {
+
+constexpr unsigned int kMaxParseDepth = 1250;
+constexpr unsigned int kMaxPostExpressions = 256;
+constexpr unsigned int kMaxExpressionListSize = 10000;
+
+}  // namespace
+
+CXFA_FMParser::CXFA_FMParser(cppgc::Heap* pHeap, CXFA_FMLexer* pLexer)
+    : m_heap(pHeap), m_lexer(pLexer), m_max_parse_depth(kMaxParseDepth) {}
+
+CXFA_FMParser::~CXFA_FMParser() = default;
+
+CXFA_FMAST* CXFA_FMParser::Parse() {
+  m_token = m_lexer->NextToken();
+  if (HasError())
+    return nullptr;
+
+  auto expressions = ParseExpressionList();
+  if (HasError())
+    return nullptr;
+
+  // We failed to parse all of the input so something has gone wrong.
+  if (!m_lexer->IsComplete())
+    return nullptr;
+
+  return cppgc::MakeGarbageCollected<CXFA_FMAST>(m_heap->GetAllocationHandle(),
+                                                 std::move(expressions));
+}
+
+bool CXFA_FMParser::NextToken() {
+  if (HasError())
+    return false;
+
+  m_token = m_lexer->NextToken();
+  while (!HasError() && m_token.GetType() == TOKreserver)
+    m_token = m_lexer->NextToken();
+  return !HasError();
+}
+
+bool CXFA_FMParser::CheckThenNext(XFA_FM_TOKEN op) {
+  if (HasError())
+    return false;
+
+  if (m_token.GetType() != op) {
+    m_error = true;
+    return false;
+  }
+  return NextToken();
+}
+
+bool CXFA_FMParser::IncrementParseDepthAndCheck() {
+  return ++m_parse_depth < m_max_parse_depth;
+}
+
+std::vector<cppgc::Member<CXFA_FMExpression>>
+CXFA_FMParser::ParseExpressionList() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return std::vector<cppgc::Member<CXFA_FMExpression>>();
+
+  std::vector<cppgc::Member<CXFA_FMExpression>> expressions;
+  while (!HasError()) {
+    if (m_token.GetType() == TOKeof || m_token.GetType() == TOKendfunc ||
+        m_token.GetType() == TOKendif || m_token.GetType() == TOKelseif ||
+        m_token.GetType() == TOKelse || m_token.GetType() == TOKendwhile ||
+        m_token.GetType() == TOKendfor || m_token.GetType() == TOKend ||
+        m_token.GetType() == TOKendfunc || m_token.GetType() == TOKreserver) {
+      break;
+    }
+
+    CXFA_FMExpression* expr =
+        m_token.GetType() == TOKfunc ? ParseFunction() : ParseExpression();
+    if (!expr) {
+      m_error = true;
+      return std::vector<cppgc::Member<CXFA_FMExpression>>();
+    }
+    if (expressions.size() >= kMaxExpressionListSize) {
+      m_error = true;
+      return std::vector<cppgc::Member<CXFA_FMExpression>>();
+    }
+    expressions.push_back(expr);
+  }
+  return expressions;
+}
+
+// Func := 'func' Identifier '(' ParameterList ')' do ExpressionList 'endfunc'
+// ParamterList := (Not actually defined in the grammar) .....
+//                 (Identifier (',' Identifier)*)?
+CXFA_FMExpression* CXFA_FMParser::ParseFunction() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+  if (!CheckThenNext(TOKfunc))
+    return nullptr;
+  if (m_token.GetType() != TOKidentifier)
+    return nullptr;
+
+  WideString ident(m_token.GetString());
+  if (!NextToken())
+    return nullptr;
+  if (!CheckThenNext(TOKlparen))
+    return nullptr;
+
+  std::vector<WideString> arguments;
+  bool last_was_comma = false;
+  while (true) {
+    if (m_token.GetType() == TOKrparen)
+      break;
+    if (m_token.GetType() != TOKidentifier)
+      return nullptr;
+
+    last_was_comma = false;
+
+    arguments.emplace_back(m_token.GetString());
+    if (!NextToken())
+      return nullptr;
+    if (m_token.GetType() != TOKcomma)
+      continue;
+
+    last_was_comma = true;
+    if (!NextToken())
+      return nullptr;
+  }
+  if (last_was_comma || !CheckThenNext(TOKrparen))
+    return nullptr;
+  if (!CheckThenNext(TOKdo))
+    return nullptr;
+
+  std::vector<cppgc::Member<CXFA_FMExpression>> expressions;
+  if (m_token.GetType() != TOKendfunc)
+    expressions = ParseExpressionList();
+
+  if (!CheckThenNext(TOKendfunc))
+    return nullptr;
+
+  return cppgc::MakeGarbageCollected<CXFA_FMFunctionDefinition>(
+      m_heap->GetAllocationHandle(), std::move(ident), std::move(arguments),
+      std::move(expressions));
+}
+
+// Expression := IfExpression | WhileExpression | ForExpression |
+//               ForEachExpression | AssignmentExpression |
+//               DeclarationExpression | SimpleExpression
+CXFA_FMExpression* CXFA_FMParser::ParseExpression() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+
+  CXFA_FMExpression* expr = nullptr;
+  switch (m_token.GetType()) {
+    case TOKvar:
+      expr = ParseDeclarationExpression();
+      break;
+    case TOKnull:
+    case TOKnumber:
+    case TOKstring:
+    case TOKplus:
+    case TOKminus:
+    case TOKksnot:
+    case TOKidentifier:
+    case TOKlparen:
+      expr = ParseExpExpression();
+      break;
+    case TOKif:
+      expr = ParseIfExpression();
+      break;
+    case TOKwhile:
+      expr = ParseWhileExpression();
+      break;
+    case TOKfor:
+      expr = ParseForExpression();
+      break;
+    case TOKforeach:
+      expr = ParseForeachExpression();
+      break;
+    case TOKdo:
+      expr = ParseDoExpression();
+      break;
+    case TOKbreak:
+      expr = cppgc::MakeGarbageCollected<CXFA_FMBreakExpression>(
+          m_heap->GetAllocationHandle());
+      if (!NextToken())
+        return nullptr;
+      break;
+    case TOKcontinue:
+      expr = cppgc::MakeGarbageCollected<CXFA_FMContinueExpression>(
+          m_heap->GetAllocationHandle());
+      if (!NextToken())
+        return nullptr;
+      break;
+    default:
+      return nullptr;
+  }
+  return expr;
+}
+
+// Declaration := 'var' Variable | 'var' Variable '=' SimpleExpression |
+//           'Func' Identifier '(' ParameterList ')' do ExpressionList 'EndFunc'
+// TODO(dsinclair): We appear to be handling the 'func' case elsewhere.
+CXFA_FMExpression* CXFA_FMParser::ParseDeclarationExpression() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+
+  if (!NextToken() || m_token.GetType() != TOKidentifier)
+    return nullptr;
+
+  WideString ident(m_token.GetString());
+  if (!NextToken())
+    return nullptr;
+
+  CXFA_FMSimpleExpression* expr = nullptr;
+  if (m_token.GetType() == TOKassign) {
+    if (!NextToken())
+      return nullptr;
+
+    expr = ParseSimpleExpression();
+    if (!expr)
+      return nullptr;
+  }
+
+  return cppgc::MakeGarbageCollected<CXFA_FMVarExpression>(
+      m_heap->GetAllocationHandle(), std::move(ident), expr);
+}
+
+// SimpleExpression := LogicalOrExpression
+CXFA_FMSimpleExpression* CXFA_FMParser::ParseSimpleExpression() {
+  if (HasError())
+    return nullptr;
+
+  return ParseLogicalOrExpression();
+}
+
+// Exp := SimpleExpression ( '=' SimpleExpression )?
+CXFA_FMExpression* CXFA_FMParser::ParseExpExpression() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+
+  CXFA_FMSimpleExpression* pExp1 = ParseSimpleExpression();
+  if (!pExp1)
+    return nullptr;
+
+  if (m_token.GetType() == TOKassign) {
+    if (!NextToken())
+      return nullptr;
+
+    CXFA_FMSimpleExpression* pExp2 = ParseSimpleExpression();
+    if (!pExp2)
+      return nullptr;
+
+    pExp1 = cppgc::MakeGarbageCollected<CXFA_FMAssignExpression>(
+        m_heap->GetAllocationHandle(), TOKassign, pExp1, pExp2);
+  }
+  return cppgc::MakeGarbageCollected<CXFA_FMExpExpression>(
+      m_heap->GetAllocationHandle(), pExp1);
+}
+
+// LogicalOr := LogicalAndExpression |
+//              LogicalOrExpression LogicalOrOperator LogicalAndExpression
+CXFA_FMSimpleExpression* CXFA_FMParser::ParseLogicalOrExpression() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+
+  CXFA_FMSimpleExpression* e1 = ParseLogicalAndExpression();
+  if (!e1)
+    return nullptr;
+
+  while (true) {
+    if (!IncrementParseDepthAndCheck())
+      return nullptr;
+
+    switch (m_token.GetType()) {
+      case TOKor:
+      case TOKksor: {
+        if (!NextToken())
+          return nullptr;
+        CXFA_FMSimpleExpression* e2 = ParseLogicalAndExpression();
+        if (!e2)
+          return nullptr;
+        e1 = cppgc::MakeGarbageCollected<CXFA_FMLogicalOrExpression>(
+            m_heap->GetAllocationHandle(), TOKor, e1, e2);
+        break;
+      }
+      default:
+        return e1;
+    }
+  }
+}
+
+// LogicalAnd := EqualityExpression |
+//               LogicalAndExpression LogicalAndOperator EqualityExpression
+CXFA_FMSimpleExpression* CXFA_FMParser::ParseLogicalAndExpression() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+
+  CXFA_FMSimpleExpression* e1 = ParseEqualityExpression();
+  if (!e1)
+    return nullptr;
+
+  while (true) {
+    if (!IncrementParseDepthAndCheck())
+      return nullptr;
+
+    switch (m_token.GetType()) {
+      case TOKand:
+      case TOKksand: {
+        if (!NextToken())
+          return nullptr;
+        CXFA_FMSimpleExpression* e2 = ParseEqualityExpression();
+        if (!e2)
+          return nullptr;
+        e1 = cppgc::MakeGarbageCollected<CXFA_FMLogicalAndExpression>(
+            m_heap->GetAllocationHandle(), TOKand, e1, e2);
+        break;
+      }
+      default:
+        return e1;
+    }
+  }
+}
+
+// Equality := RelationExpression |
+//             EqualityExpression EqulaityOperator RelationalExpression
+CXFA_FMSimpleExpression* CXFA_FMParser::ParseEqualityExpression() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+
+  CXFA_FMSimpleExpression* e1 = ParseRelationalExpression();
+  if (!e1)
+    return nullptr;
+
+  while (true) {
+    if (!IncrementParseDepthAndCheck())
+      return nullptr;
+
+    switch (m_token.GetType()) {
+      case TOKeq:
+      case TOKkseq: {
+        if (!NextToken())
+          return nullptr;
+        CXFA_FMSimpleExpression* e2 = ParseRelationalExpression();
+        if (!e2)
+          return nullptr;
+        e1 = cppgc::MakeGarbageCollected<CXFA_FMEqualExpression>(
+            m_heap->GetAllocationHandle(), TOKeq, e1, e2);
+        break;
+      }
+      case TOKne:
+      case TOKksne: {
+        if (!NextToken())
+          return nullptr;
+        CXFA_FMSimpleExpression* e2 = ParseRelationalExpression();
+        if (!e2)
+          return nullptr;
+        e1 = cppgc::MakeGarbageCollected<CXFA_FMNotEqualExpression>(
+            m_heap->GetAllocationHandle(), TOKne, e1, e2);
+        break;
+      }
+      default:
+        return e1;
+    }
+  }
+}
+
+// Relational := AdditiveExpression |
+//               RelationalExpression RelationalOperator AdditiveExpression
+CXFA_FMSimpleExpression* CXFA_FMParser::ParseRelationalExpression() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+
+  CXFA_FMSimpleExpression* e1 = ParseAdditiveExpression();
+  if (!e1)
+    return nullptr;
+
+  while (true) {
+    if (!IncrementParseDepthAndCheck())
+      return nullptr;
+
+    switch (m_token.GetType()) {
+      case TOKlt:
+      case TOKkslt: {
+        if (!NextToken())
+          return nullptr;
+        CXFA_FMSimpleExpression* e2 = ParseAdditiveExpression();
+        if (!e2)
+          return nullptr;
+        e1 = cppgc::MakeGarbageCollected<CXFA_FMLtExpression>(
+            m_heap->GetAllocationHandle(), TOKlt, e1, e2);
+        break;
+      }
+      case TOKgt:
+      case TOKksgt: {
+        if (!NextToken())
+          return nullptr;
+        CXFA_FMSimpleExpression* e2 = ParseAdditiveExpression();
+        if (!e2)
+          return nullptr;
+        e1 = cppgc::MakeGarbageCollected<CXFA_FMGtExpression>(
+            m_heap->GetAllocationHandle(), TOKgt, e1, e2);
+        break;
+      }
+      case TOKle:
+      case TOKksle: {
+        if (!NextToken())
+          return nullptr;
+        CXFA_FMSimpleExpression* e2 = ParseAdditiveExpression();
+        if (!e2)
+          return nullptr;
+        e1 = cppgc::MakeGarbageCollected<CXFA_FMLeExpression>(
+            m_heap->GetAllocationHandle(), TOKle, e1, e2);
+        break;
+      }
+      case TOKge:
+      case TOKksge: {
+        if (!NextToken())
+          return nullptr;
+        CXFA_FMSimpleExpression* e2 = ParseAdditiveExpression();
+        if (!e2)
+          return nullptr;
+        e1 = cppgc::MakeGarbageCollected<CXFA_FMGeExpression>(
+            m_heap->GetAllocationHandle(), TOKge, e1, e2);
+        break;
+      }
+      default:
+        return e1;
+    }
+  }
+}
+
+// Additive := MultiplicativeExpression |
+//             AdditiveExpression AdditiveOperator MultiplicativeExpression
+CXFA_FMSimpleExpression* CXFA_FMParser::ParseAdditiveExpression() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+
+  CXFA_FMSimpleExpression* e1 = ParseMultiplicativeExpression();
+  if (!e1)
+    return nullptr;
+
+  while (true) {
+    if (!IncrementParseDepthAndCheck())
+      return nullptr;
+
+    switch (m_token.GetType()) {
+      case TOKplus: {
+        if (!NextToken())
+          return nullptr;
+        CXFA_FMSimpleExpression* e2 = ParseMultiplicativeExpression();
+        if (!e2)
+          return nullptr;
+        e1 = cppgc::MakeGarbageCollected<CXFA_FMPlusExpression>(
+            m_heap->GetAllocationHandle(), TOKplus, e1, e2);
+        break;
+      }
+      case TOKminus: {
+        if (!NextToken())
+          return nullptr;
+        CXFA_FMSimpleExpression* e2 = ParseMultiplicativeExpression();
+        if (!e2)
+          return nullptr;
+        e1 = cppgc::MakeGarbageCollected<CXFA_FMMinusExpression>(
+            m_heap->GetAllocationHandle(), TOKminus, e1, e2);
+        break;
+      }
+      default:
+        return e1;
+    }
+  }
+}
+
+// Multiplicative := UnaryExpression |
+//                 MultiplicateExpression MultiplicativeOperator UnaryExpression
+CXFA_FMSimpleExpression* CXFA_FMParser::ParseMultiplicativeExpression() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+
+  CXFA_FMSimpleExpression* e1 = ParseUnaryExpression();
+  if (!e1)
+    return nullptr;
+
+  while (true) {
+    if (!IncrementParseDepthAndCheck())
+      return nullptr;
+
+    switch (m_token.GetType()) {
+      case TOKmul: {
+        if (!NextToken())
+          return nullptr;
+        CXFA_FMSimpleExpression* e2 = ParseUnaryExpression();
+        if (!e2)
+          return nullptr;
+        e1 = cppgc::MakeGarbageCollected<CXFA_FMMulExpression>(
+            m_heap->GetAllocationHandle(), TOKmul, e1, e2);
+        break;
+      }
+      case TOKdiv: {
+        if (!NextToken())
+          return nullptr;
+        CXFA_FMSimpleExpression* e2 = ParseUnaryExpression();
+        if (!e2)
+          return nullptr;
+        e1 = cppgc::MakeGarbageCollected<CXFA_FMDivExpression>(
+            m_heap->GetAllocationHandle(), TOKdiv, e1, e2);
+        break;
+      }
+      default:
+        return e1;
+    }
+  }
+}
+
+// Unary := PrimaryExpression | UnaryOperator UnaryExpression
+CXFA_FMSimpleExpression* CXFA_FMParser::ParseUnaryExpression() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+
+  switch (m_token.GetType()) {
+    case TOKplus: {
+      if (!NextToken())
+        return nullptr;
+      CXFA_FMSimpleExpression* expr = ParseUnaryExpression();
+      if (!expr)
+        return nullptr;
+      return cppgc::MakeGarbageCollected<CXFA_FMPosExpression>(
+          m_heap->GetAllocationHandle(), expr);
+    }
+    case TOKminus: {
+      if (!NextToken())
+        return nullptr;
+      CXFA_FMSimpleExpression* expr = ParseUnaryExpression();
+      if (!expr)
+        return nullptr;
+      return cppgc::MakeGarbageCollected<CXFA_FMNegExpression>(
+          m_heap->GetAllocationHandle(), expr);
+    }
+    case TOKksnot: {
+      if (!NextToken())
+        return nullptr;
+      CXFA_FMSimpleExpression* expr = ParseUnaryExpression();
+      if (!expr)
+        return nullptr;
+      return cppgc::MakeGarbageCollected<CXFA_FMNotExpression>(
+          m_heap->GetAllocationHandle(), expr);
+    }
+    default:
+      return ParsePrimaryExpression();
+  }
+}
+
+// Primary := Literal | FunctionCall | Accessor ('.*' )? |
+//           '(' SimpleExpression ')'
+CXFA_FMSimpleExpression* CXFA_FMParser::ParsePrimaryExpression() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+
+  CXFA_FMSimpleExpression* expr = ParseLiteral();
+  if (expr)
+    return NextToken() ? expr : nullptr;
+
+  switch (m_token.GetType()) {
+    case TOKidentifier: {
+      WideString wsIdentifier(m_token.GetString());
+      if (!NextToken())
+        return nullptr;
+      if (m_token.GetType() == TOKlbracket) {
+        CXFA_FMSimpleExpression* s = ParseIndexExpression();
+        if (!s)
+          return nullptr;
+        expr = cppgc::MakeGarbageCollected<CXFA_FMDotAccessorExpression>(
+            m_heap->GetAllocationHandle(), nullptr, TOKdot,
+            std::move(wsIdentifier), s);
+        if (!expr)
+          return nullptr;
+        if (!NextToken())
+          return nullptr;
+      } else {
+        expr = cppgc::MakeGarbageCollected<CXFA_FMIdentifierExpression>(
+            m_heap->GetAllocationHandle(), wsIdentifier);
+      }
+      break;
+    }
+    case TOKlparen: {
+      expr = ParseParenExpression();
+      if (!expr)
+        return nullptr;
+      break;
+    }
+    default:
+      return nullptr;
+  }
+  return ParsePostExpression(expr);
+}
+
+// Literal := String | Number | Null
+CXFA_FMSimpleExpression* CXFA_FMParser::ParseLiteral() {
+  switch (m_token.GetType()) {
+    case TOKnumber:
+      return cppgc::MakeGarbageCollected<CXFA_FMNumberExpression>(
+          m_heap->GetAllocationHandle(), WideString(m_token.GetString()));
+    case TOKstring:
+      return cppgc::MakeGarbageCollected<CXFA_FMStringExpression>(
+          m_heap->GetAllocationHandle(), WideString(m_token.GetString()));
+    case TOKnull:
+      return cppgc::MakeGarbageCollected<CXFA_FMNullExpression>(
+          m_heap->GetAllocationHandle());
+    default:
+      return nullptr;
+  }
+}
+
+// TODO(dsinclair): Make this match up to the grammar
+// I believe this is parsing the accessor ( '.' | '..' | '.#' )
+CXFA_FMSimpleExpression* CXFA_FMParser::ParsePostExpression(
+    CXFA_FMSimpleExpression* expr) {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+
+  size_t expr_count = 0;
+  while (true) {
+    ++expr_count;
+    // Limit the number of expressions allowed in the post expression statement.
+    // If we don't do this then its possible to generate a stack overflow
+    // by having a very large number of things like .. expressions.
+    if (expr_count > kMaxPostExpressions)
+      return nullptr;
+
+    switch (m_token.GetType()) {
+      case TOKlparen: {
+        absl::optional<std::vector<cppgc::Member<CXFA_FMSimpleExpression>>>
+            expressions = ParseArgumentList();
+        if (!expressions.has_value())
+          return nullptr;
+
+        expr = cppgc::MakeGarbageCollected<CXFA_FMCallExpression>(
+            m_heap->GetAllocationHandle(), expr, std::move(expressions.value()),
+            false);
+        if (!NextToken())
+          return nullptr;
+        if (m_token.GetType() != TOKlbracket)
+          continue;
+
+        CXFA_FMSimpleExpression* s = ParseIndexExpression();
+        if (!s)
+          return nullptr;
+
+        expr = cppgc::MakeGarbageCollected<CXFA_FMDotAccessorExpression>(
+            m_heap->GetAllocationHandle(), expr, TOKcall, WideString(), s);
+        break;
+      }
+      case TOKdot: {
+        if (!NextToken())
+          return nullptr;
+        if (m_token.GetType() != TOKidentifier)
+          return nullptr;
+
+        WideString tempStr(m_token.GetString());
+        if (!NextToken())
+          return nullptr;
+        if (m_token.GetType() == TOKlparen) {
+          absl::optional<std::vector<cppgc::Member<CXFA_FMSimpleExpression>>>
+              expressions = ParseArgumentList();
+          if (!expressions.has_value())
+            return nullptr;
+
+          auto* pIdentifier =
+              cppgc::MakeGarbageCollected<CXFA_FMIdentifierExpression>(
+                  m_heap->GetAllocationHandle(), std::move(tempStr));
+          auto* pExpCall = cppgc::MakeGarbageCollected<CXFA_FMCallExpression>(
+              m_heap->GetAllocationHandle(), pIdentifier,
+              std::move(expressions.value()), true);
+          expr = cppgc::MakeGarbageCollected<CXFA_FMMethodCallExpression>(
+              m_heap->GetAllocationHandle(), expr, pExpCall);
+          if (!NextToken())
+            return nullptr;
+          if (m_token.GetType() != TOKlbracket)
+            continue;
+
+          CXFA_FMSimpleExpression* s = ParseIndexExpression();
+          if (!s)
+            return nullptr;
+
+          expr = cppgc::MakeGarbageCollected<CXFA_FMDotAccessorExpression>(
+              m_heap->GetAllocationHandle(), expr, TOKcall, WideString(), s);
+        } else if (m_token.GetType() == TOKlbracket) {
+          CXFA_FMSimpleExpression* s = ParseIndexExpression();
+          if (!s)
+            return nullptr;
+
+          expr = cppgc::MakeGarbageCollected<CXFA_FMDotAccessorExpression>(
+              m_heap->GetAllocationHandle(), expr, TOKdot, std::move(tempStr),
+              s);
+        } else {
+          auto* subexpr = cppgc::MakeGarbageCollected<CXFA_FMIndexExpression>(
+              m_heap->GetAllocationHandle(),
+              CXFA_FMIndexExpression::AccessorIndex::kNoIndex, nullptr, false);
+          expr = cppgc::MakeGarbageCollected<CXFA_FMDotAccessorExpression>(
+              m_heap->GetAllocationHandle(), expr, TOKdot, std::move(tempStr),
+              subexpr);
+          continue;
+        }
+        break;
+      }
+      case TOKdotdot: {
+        if (!NextToken())
+          return nullptr;
+        if (m_token.GetType() != TOKidentifier)
+          return nullptr;
+
+        WideString tempStr(m_token.GetString());
+        if (!NextToken())
+          return nullptr;
+        if (m_token.GetType() == TOKlbracket) {
+          CXFA_FMSimpleExpression* s = ParseIndexExpression();
+          if (!s)
+            return nullptr;
+
+          expr = cppgc::MakeGarbageCollected<CXFA_FMDotDotAccessorExpression>(
+              m_heap->GetAllocationHandle(), expr, TOKdotdot,
+              std::move(tempStr), s);
+        } else {
+          auto* subexpr = cppgc::MakeGarbageCollected<CXFA_FMIndexExpression>(
+              m_heap->GetAllocationHandle(),
+              CXFA_FMIndexExpression::AccessorIndex::kNoIndex, nullptr, false);
+          expr = cppgc::MakeGarbageCollected<CXFA_FMDotDotAccessorExpression>(
+              m_heap->GetAllocationHandle(), expr, TOKdotdot,
+              std::move(tempStr), subexpr);
+          continue;
+        }
+        break;
+      }
+      case TOKdotscream: {
+        if (!NextToken())
+          return nullptr;
+        if (m_token.GetType() != TOKidentifier)
+          return nullptr;
+
+        WideString tempStr(m_token.GetString());
+        if (!NextToken())
+          return nullptr;
+
+        if (m_token.GetType() != TOKlbracket) {
+          auto* subexpr = cppgc::MakeGarbageCollected<CXFA_FMIndexExpression>(
+              m_heap->GetAllocationHandle(),
+              CXFA_FMIndexExpression::AccessorIndex::kNoIndex, nullptr, false);
+          expr = cppgc::MakeGarbageCollected<CXFA_FMDotAccessorExpression>(
+              m_heap->GetAllocationHandle(), expr, TOKdotscream,
+              std::move(tempStr), subexpr);
+          continue;
+        }
+
+        CXFA_FMSimpleExpression* s = ParseIndexExpression();
+        if (!s)
+          return nullptr;
+
+        expr = cppgc::MakeGarbageCollected<CXFA_FMDotAccessorExpression>(
+            m_heap->GetAllocationHandle(), expr, TOKdotscream,
+            std::move(tempStr), s);
+        break;
+      }
+      case TOKdotstar: {
+        auto* subexpr = cppgc::MakeGarbageCollected<CXFA_FMIndexExpression>(
+            m_heap->GetAllocationHandle(),
+            CXFA_FMIndexExpression::AccessorIndex::kNoIndex, nullptr, false);
+        expr = cppgc::MakeGarbageCollected<CXFA_FMDotAccessorExpression>(
+            m_heap->GetAllocationHandle(), expr, TOKdotstar, L"*", subexpr);
+        break;
+      }
+      default:
+        return expr;
+    }
+    if (!NextToken())
+      return nullptr;
+  }
+}
+
+// Argument lists are zero or more comma seperated simple expressions found
+// between '(' and ')'
+absl::optional<std::vector<cppgc::Member<CXFA_FMSimpleExpression>>>
+CXFA_FMParser::ParseArgumentList() {
+  if (m_token.GetType() != TOKlparen || !NextToken())
+    return absl::nullopt;
+
+  std::vector<cppgc::Member<CXFA_FMSimpleExpression>> expressions;
+  bool first_arg = true;
+  while (m_token.GetType() != TOKrparen) {
+    if (first_arg) {
+      first_arg = false;
+    } else {
+      if (m_token.GetType() != TOKcomma || !NextToken())
+        return absl::nullopt;
+    }
+
+    CXFA_FMSimpleExpression* exp = ParseSimpleExpression();
+    if (!exp)
+      return absl::nullopt;
+
+    expressions.push_back(exp);
+    if (expressions.size() > kMaxPostExpressions)
+      return absl::nullopt;
+  }
+
+  return expressions;
+}
+
+// Index := '[' ('*' | '+' SimpleExpression | '-' SimpleExpression) ']'
+CXFA_FMSimpleExpression* CXFA_FMParser::ParseIndexExpression() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+  if (!CheckThenNext(TOKlbracket))
+    return nullptr;
+
+  if (m_token.GetType() == TOKmul) {
+    auto* pExp = cppgc::MakeGarbageCollected<CXFA_FMIndexExpression>(
+        m_heap->GetAllocationHandle(),
+        CXFA_FMIndexExpression::AccessorIndex::kNoRelativeIndex, nullptr, true);
+    if (!pExp || !NextToken())
+      return nullptr;
+
+    // TODO(dsinclair): This should CheckThenNext(TOKrbracket) but need to clean
+    // up the callsites.
+    if (m_token.GetType() != TOKrbracket)
+      return nullptr;
+    return pExp;
+  }
+
+  CXFA_FMIndexExpression::AccessorIndex accessorIndex =
+      CXFA_FMIndexExpression::AccessorIndex::kNoRelativeIndex;
+  if (m_token.GetType() == TOKplus) {
+    accessorIndex = CXFA_FMIndexExpression::AccessorIndex::kPositiveIndex;
+    if (!NextToken())
+      return nullptr;
+  } else if (m_token.GetType() == TOKminus) {
+    accessorIndex = CXFA_FMIndexExpression::AccessorIndex::kNegativeIndex;
+    if (!NextToken())
+      return nullptr;
+  }
+
+  CXFA_FMSimpleExpression* s = ParseSimpleExpression();
+  if (!s)
+    return nullptr;
+  if (m_token.GetType() != TOKrbracket)
+    return nullptr;
+
+  return cppgc::MakeGarbageCollected<CXFA_FMIndexExpression>(
+      m_heap->GetAllocationHandle(), accessorIndex, s, false);
+}
+
+// Paren := '(' SimpleExpression ')'
+CXFA_FMSimpleExpression* CXFA_FMParser::ParseParenExpression() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+
+  if (!CheckThenNext(TOKlparen))
+    return nullptr;
+  if (m_token.GetType() == TOKrparen)
+    return nullptr;
+
+  CXFA_FMSimpleExpression* pExp1 = ParseSimpleExpression();
+  if (!pExp1)
+    return nullptr;
+
+  if (!CheckThenNext(TOKrparen))
+    return nullptr;
+  return pExp1;
+}
+
+// If := 'if' '(' SimpleExpression ')' 'then' ExpressionList
+//       ('elseif' '(' SimpleExpression ')' 'then' ExpressionList)*
+//       ('else' ExpressionList)?
+//       'endif'
+CXFA_FMExpression* CXFA_FMParser::ParseIfExpression() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+
+  if (!CheckThenNext(TOKif))
+    return nullptr;
+
+  CXFA_FMSimpleExpression* pCondition = ParseParenExpression();
+  if (!pCondition)
+    return nullptr;
+  if (!CheckThenNext(TOKthen))
+    return nullptr;
+
+  auto* pIfExpressions = cppgc::MakeGarbageCollected<CXFA_FMBlockExpression>(
+      m_heap->GetAllocationHandle(), ParseExpressionList());
+
+  std::vector<cppgc::Member<CXFA_FMIfExpression>> pElseIfExpressions;
+  while (m_token.GetType() == TOKelseif) {
+    if (!NextToken())
+      return nullptr;
+
+    auto* elseIfCondition = ParseParenExpression();
+    if (!elseIfCondition)
+      return nullptr;
+    if (!CheckThenNext(TOKthen))
+      return nullptr;
+
+    auto elseIfExprs = ParseExpressionList();
+    pElseIfExpressions.push_back(
+        cppgc::MakeGarbageCollected<CXFA_FMIfExpression>(
+            m_heap->GetAllocationHandle(), elseIfCondition,
+            cppgc::MakeGarbageCollected<CXFA_FMBlockExpression>(
+                m_heap->GetAllocationHandle(), std::move(elseIfExprs)),
+            std::vector<cppgc::Member<CXFA_FMIfExpression>>(), nullptr));
+  }
+
+  CXFA_FMExpression* pElseExpression = nullptr;
+  if (m_token.GetType() == TOKelse) {
+    if (!NextToken())
+      return nullptr;
+
+    pElseExpression = cppgc::MakeGarbageCollected<CXFA_FMBlockExpression>(
+        m_heap->GetAllocationHandle(), ParseExpressionList());
+  }
+  if (!CheckThenNext(TOKendif))
+    return nullptr;
+
+  return cppgc::MakeGarbageCollected<CXFA_FMIfExpression>(
+      m_heap->GetAllocationHandle(), pCondition, pIfExpressions,
+      std::move(pElseIfExpressions), pElseExpression);
+}
+
+// While := 'while' '(' SimpleExpression ')' 'do' ExpressionList 'endwhile'
+CXFA_FMExpression* CXFA_FMParser::ParseWhileExpression() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+  if (!CheckThenNext(TOKwhile))
+    return nullptr;
+
+  CXFA_FMSimpleExpression* pCondition = ParseParenExpression();
+  if (!pCondition || !CheckThenNext(TOKdo))
+    return nullptr;
+
+  auto exprs = ParseExpressionList();
+  if (!CheckThenNext(TOKendwhile))
+    return nullptr;
+
+  return cppgc::MakeGarbageCollected<CXFA_FMWhileExpression>(
+      m_heap->GetAllocationHandle(), pCondition,
+      cppgc::MakeGarbageCollected<CXFA_FMBlockExpression>(
+          m_heap->GetAllocationHandle(), std::move(exprs)));
+}
+
+// For := 'for' Assignment 'upto' Accessor ('step' SimpleExpression)?
+//            'do' ExpressionList 'endfor' |
+//         'for' Assignment 'downto' Accessor ('step' SimpleExpression)?
+//            'do' ExpressionList 'endfor'
+CXFA_FMExpression* CXFA_FMParser::ParseForExpression() {
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+  if (!CheckThenNext(TOKfor))
+    return nullptr;
+  if (m_token.GetType() != TOKidentifier)
+    return nullptr;
+
+  WideString wsVariant(m_token.GetString());
+  if (!NextToken())
+    return nullptr;
+  if (!CheckThenNext(TOKassign))
+    return nullptr;
+
+  CXFA_FMSimpleExpression* pAssignment = ParseSimpleExpression();
+  if (!pAssignment)
+    return nullptr;
+
+  int32_t iDirection = 0;
+  if (m_token.GetType() == TOKupto)
+    iDirection = 1;
+  else if (m_token.GetType() == TOKdownto)
+    iDirection = -1;
+  else
+    return nullptr;
+
+  if (!NextToken())
+    return nullptr;
+
+  CXFA_FMSimpleExpression* pAccessor = ParseSimpleExpression();
+  if (!pAccessor)
+    return nullptr;
+
+  CXFA_FMSimpleExpression* pStep = nullptr;
+  if (m_token.GetType() == TOKstep) {
+    if (!NextToken())
+      return nullptr;
+    pStep = ParseSimpleExpression();
+    if (!pStep)
+      return nullptr;
+  }
+  if (!CheckThenNext(TOKdo))
+    return nullptr;
+
+  auto exprs = ParseExpressionList();
+  if (!CheckThenNext(TOKendfor))
+    return nullptr;
+
+  return cppgc::MakeGarbageCollected<CXFA_FMForExpression>(
+      m_heap->GetAllocationHandle(), wsVariant, pAssignment, pAccessor,
+      iDirection, pStep,
+      cppgc::MakeGarbageCollected<CXFA_FMBlockExpression>(
+          m_heap->GetAllocationHandle(), std::move(exprs)));
+}
+
+// Foreach := 'foreach' Identifier 'in' '(' ArgumentList ')'
+//            'do' ExpressionList 'endfor'
+CXFA_FMExpression* CXFA_FMParser::ParseForeachExpression() {
+  if (m_token.GetType() != TOKforeach)
+    return nullptr;
+
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+  if (!CheckThenNext(TOKforeach))
+    return nullptr;
+  if (m_token.GetType() != TOKidentifier)
+    return nullptr;
+
+  WideString wsIdentifier(m_token.GetString());
+  if (!NextToken() || !CheckThenNext(TOKin) || !CheckThenNext(TOKlparen))
+    return nullptr;
+
+  std::vector<cppgc::Member<CXFA_FMSimpleExpression>> pArgumentList;
+  while (m_token.GetType() != TOKrparen) {
+    CXFA_FMSimpleExpression* s = ParseSimpleExpression();
+    if (!s)
+      return nullptr;
+
+    pArgumentList.push_back(s);
+    if (m_token.GetType() != TOKcomma)
+      break;
+    if (!NextToken())
+      return nullptr;
+  }
+  // We must have arguments.
+  if (pArgumentList.empty())
+    return nullptr;
+  if (!CheckThenNext(TOKrparen))
+    return nullptr;
+
+  auto exprs = ParseExpressionList();
+  if (!CheckThenNext(TOKendfor))
+    return nullptr;
+
+  return cppgc::MakeGarbageCollected<CXFA_FMForeachExpression>(
+      m_heap->GetAllocationHandle(), std::move(wsIdentifier),
+      std::move(pArgumentList),
+      cppgc::MakeGarbageCollected<CXFA_FMBlockExpression>(
+          m_heap->GetAllocationHandle(), std::move(exprs)));
+}
+
+// Block := 'do' ExpressionList 'end'
+CXFA_FMExpression* CXFA_FMParser::ParseDoExpression() {
+  if (m_token.GetType() != TOKdo)
+    return nullptr;
+
+  AutoRestorer<unsigned long> restorer(&m_parse_depth);
+  if (HasError() || !IncrementParseDepthAndCheck())
+    return nullptr;
+  if (!CheckThenNext(TOKdo))
+    return nullptr;
+
+  auto exprs = ParseExpressionList();
+  if (!CheckThenNext(TOKend))
+    return nullptr;
+
+  return cppgc::MakeGarbageCollected<CXFA_FMDoExpression>(
+      m_heap->GetAllocationHandle(),
+      cppgc::MakeGarbageCollected<CXFA_FMBlockExpression>(
+          m_heap->GetAllocationHandle(), std::move(exprs)));
+}
+
+bool CXFA_FMParser::HasError() const {
+  return m_error || m_token.GetType() == TOKreserver;
+}
diff --git a/xfa/fxfa/formcalc/cxfa_fmparser.h b/xfa/fxfa/formcalc/cxfa_fmparser.h
new file mode 100644
index 0000000..e8b6c44
--- /dev/null
+++ b/xfa/fxfa/formcalc/cxfa_fmparser.h
@@ -0,0 +1,74 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FXFA_FORMCALC_CXFA_FMPARSER_H_
+#define XFA_FXFA_FORMCALC_CXFA_FMPARSER_H_
+
+#include <vector>
+
+#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"
+#include "v8/include/cppgc/member.h"
+#include "xfa/fxfa/formcalc/cxfa_fmexpression.h"
+#include "xfa/fxfa/formcalc/cxfa_fmlexer.h"
+
+class CXFA_FMParser {
+  CPPGC_STACK_ALLOCATED();  // Allow Raw/Unowned pointers.
+
+ public:
+  CXFA_FMParser(cppgc::Heap* heap, CXFA_FMLexer* pLexer);
+  ~CXFA_FMParser();
+
+  // Returned object is owned by cppgc heap.
+  CXFA_FMAST* Parse();
+  bool HasError() const;
+
+  void SetMaxParseDepthForTest(unsigned long max_depth) {
+    m_max_parse_depth = max_depth;
+  }
+
+ private:
+  bool NextToken();
+  bool CheckThenNext(XFA_FM_TOKEN op);
+  bool IncrementParseDepthAndCheck();
+
+  std::vector<cppgc::Member<CXFA_FMExpression>> ParseExpressionList();
+  CXFA_FMExpression* ParseFunction();
+  CXFA_FMExpression* ParseExpression();
+  CXFA_FMExpression* ParseDeclarationExpression();
+  CXFA_FMExpression* ParseExpExpression();
+  CXFA_FMExpression* ParseIfExpression();
+  CXFA_FMExpression* ParseWhileExpression();
+  CXFA_FMExpression* ParseForExpression();
+  CXFA_FMExpression* ParseForeachExpression();
+  CXFA_FMExpression* ParseDoExpression();
+  CXFA_FMSimpleExpression* ParseParenExpression();
+  CXFA_FMSimpleExpression* ParseSimpleExpression();
+  CXFA_FMSimpleExpression* ParseLogicalOrExpression();
+  CXFA_FMSimpleExpression* ParseLogicalAndExpression();
+  CXFA_FMSimpleExpression* ParseEqualityExpression();
+  CXFA_FMSimpleExpression* ParseRelationalExpression();
+  CXFA_FMSimpleExpression* ParseAdditiveExpression();
+  CXFA_FMSimpleExpression* ParseMultiplicativeExpression();
+  CXFA_FMSimpleExpression* ParseUnaryExpression();
+  CXFA_FMSimpleExpression* ParsePrimaryExpression();
+  CXFA_FMSimpleExpression* ParsePostExpression(CXFA_FMSimpleExpression* e);
+  CXFA_FMSimpleExpression* ParseIndexExpression();
+  CXFA_FMSimpleExpression* ParseLiteral();
+  absl::optional<std::vector<cppgc::Member<CXFA_FMSimpleExpression>>>
+  ParseArgumentList();
+
+  UnownedPtr<cppgc::Heap> const m_heap;
+  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;
+  unsigned long m_max_parse_depth;
+};
+
+#endif  // XFA_FXFA_FORMCALC_CXFA_FMPARSER_H_
diff --git a/xfa/fxfa/formcalc/cxfa_fmparser_unittest.cpp b/xfa/fxfa/formcalc/cxfa_fmparser_unittest.cpp
new file mode 100644
index 0000000..98c73f0
--- /dev/null
+++ b/xfa/fxfa/formcalc/cxfa_fmparser_unittest.cpp
@@ -0,0 +1,538 @@
+// Copyright 2017 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fxfa/formcalc/cxfa_fmparser.h"
+
+#include "core/fxcrt/widetext_buffer.h"
+#include "testing/fxgc_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "xfa/fxfa/formcalc/cxfa_fmtojavascriptdepth.h"
+
+class CXFA_FMParserTest : public FXGCUnitTest {};
+
+TEST_F(CXFA_FMParserTest, Empty) {
+  CXFA_FMLexer lexer(L"");
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  ASSERT_TRUE(ast);
+  EXPECT_FALSE(parser.HasError());
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
+  ASSERT_TRUE(buf.has_value());
+  // TODO(dsinclair): This is a little weird .....
+  EXPECT_STREQ(L"// comments only", buf.value().MakeString().c_str());
+}
+
+TEST_F(CXFA_FMParserTest, CommentOnlyIsError) {
+  CXFA_FMLexer lexer(L"; Just comment");
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  ASSERT_TRUE(ast);
+  // TODO(dsinclair): This isn't allowed per the spec.
+  EXPECT_FALSE(parser.HasError());
+  // EXPECT_TRUE(parser.HasError());
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
+  ASSERT_TRUE(buf.has_value());
+  EXPECT_STREQ(L"// comments only", buf.value().MakeString().c_str());
+}
+
+TEST_F(CXFA_FMParserTest, CommentThenValue) {
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+pfm_ret = 12;
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  CXFA_FMLexer lexer(L"; Just comment\n12");
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  ASSERT_TRUE(ast);
+  EXPECT_FALSE(parser.HasError());
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
+  ASSERT_TRUE(buf.has_value());
+  EXPECT_STREQ(ret, buf.value().MakeString().c_str());
+}
+
+TEST_F(CXFA_FMParserTest, Parse) {
+  const wchar_t input[] =
+      LR"***($ = Avg (-3, 5, -6, 12, -13);
+$ = Avg (Table2..Row[*].Cell1);
+if ($ ne -1)then
+  border.fill.color.value = "255,64,64";
+elseif ($ ne -2) then
+  border.fill.color.value = "128,128,128";
+else
+  border.fill.color.value = "20,170,13";
+endif
+$)***";
+
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+if (pfm_rt.is_obj(this))
+{
+pfm_rt.asgn_val_op(this, pfm_rt.Avg(pfm_rt.neg_op(3), 5, pfm_rt.neg_op(6), 12, pfm_rt.neg_op(13)));
+}
+if (pfm_rt.is_obj(this))
+{
+pfm_rt.asgn_val_op(this, pfm_rt.Avg(pfm_rt.dot_acc(pfm_rt.dotdot_acc(Table2, "Table2", "Row", 1), "", "Cell1", 0, 0)));
+}
+if (pfm_rt.get_val(pfm_rt.neq_op(this, pfm_rt.neg_op(1))))
+{
+if (pfm_rt.is_obj(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0)))
+{
+pfm_rt.asgn_val_op(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0), "255,64,64");
+}
+}
+else if (pfm_rt.get_val(pfm_rt.neq_op(this, pfm_rt.neg_op(2))))
+{
+if (pfm_rt.is_obj(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0)))
+{
+pfm_rt.asgn_val_op(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0), "128,128,128");
+}
+}
+else {
+if (pfm_rt.is_obj(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0)))
+{
+pfm_rt.asgn_val_op(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0), "20,170,13");
+}
+}
+pfm_ret = this;
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  CXFA_FMLexer lexer(input);
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  ASSERT_TRUE(ast);
+  EXPECT_FALSE(parser.HasError());
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
+  ASSERT_TRUE(buf.has_value());
+  EXPECT_EQ(ret, buf.value().AsStringView());
+}
+
+TEST_F(CXFA_FMParserTest, MaxParseDepth) {
+  CXFA_FMLexer lexer(L"foo(bar[baz(fizz[0])])");
+  CXFA_FMParser parser(heap(), &lexer);
+  parser.SetMaxParseDepthForTest(5);
+  EXPECT_FALSE(parser.Parse());
+  EXPECT_TRUE(parser.HasError());
+}
+
+TEST_F(CXFA_FMParserTest, chromium752201) {
+  CXFA_FMLexer lexer(
+      LR"***(fTep a
+.#
+fo@ =[=l)***");
+
+  CXFA_FMParser parser(heap(), &lexer);
+  EXPECT_FALSE(parser.Parse());
+  EXPECT_TRUE(parser.HasError());
+}
+
+TEST_F(CXFA_FMParserTest, MultipleAssignmentIsNotAllowed) {
+  CXFA_FMLexer lexer(L"(a=(b=t))=u");
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  ASSERT_TRUE(!ast);
+  EXPECT_TRUE(parser.HasError());
+}
+
+TEST_F(CXFA_FMParserTest, ParseFuncWithParams) {
+  const wchar_t input[] =
+      LR"***(func MyFunction(param1, param2) do
+  param1 * param2
+endfunc)***";
+
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+function MyFunction(param1, param2) {
+var pfm_ret = null;
+pfm_ret = pfm_rt.mul_op(param1, param2);
+return pfm_ret;
+}
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  CXFA_FMLexer lexer(input);
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  ASSERT_TRUE(ast);
+  EXPECT_FALSE(parser.HasError());
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
+  ASSERT_TRUE(buf.has_value());
+  EXPECT_STREQ(ret, buf.value().MakeString().c_str());
+}
+
+TEST_F(CXFA_FMParserTest, ParseFuncWithoutParams) {
+  const wchar_t input[] =
+      LR"***(func MyFunction() do
+  42
+endfunc)***";
+
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+function MyFunction() {
+var pfm_ret = null;
+pfm_ret = 42;
+return pfm_ret;
+}
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  CXFA_FMLexer lexer(input);
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  ASSERT_TRUE(ast);
+  EXPECT_FALSE(parser.HasError());
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
+  ASSERT_TRUE(buf.has_value());
+  EXPECT_STREQ(ret, buf.value().MakeString().c_str());
+}
+
+TEST_F(CXFA_FMParserTest, ParseFuncWithBadParamsList) {
+  const wchar_t input[] =
+      LR"***(func MyFunction(param1,) do
+  param1 * param2
+endfunc)***";
+
+  CXFA_FMLexer lexer(input);
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  ASSERT_TRUE(ast == nullptr);
+  EXPECT_TRUE(parser.HasError());
+}
+
+TEST_F(CXFA_FMParserTest, ParseBadIfExpression) {
+  const wchar_t input[] = L"if ( then";
+  CXFA_FMLexer lexer(input);
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  ASSERT_TRUE(ast == nullptr);
+  EXPECT_TRUE(parser.HasError());
+}
+
+TEST_F(CXFA_FMParserTest, ParseBadElseIfExpression) {
+  const wchar_t input[] =
+      LR"***(if ($ ne -1) then"
+elseif( then)***";
+
+  CXFA_FMLexer lexer(input);
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  ASSERT_TRUE(ast == nullptr);
+  EXPECT_TRUE(parser.HasError());
+}
+
+TEST_F(CXFA_FMParserTest, ParseDepthWithWideTree) {
+  const wchar_t input[] = L"a <> b <> c <> d <> e <> f <> g <> h <> i <> j";
+
+  {
+    CXFA_FMLexer lexer(input);
+    CXFA_FMParser parser(heap(), &lexer);
+    CXFA_FMAST* ast = parser.Parse();
+    ASSERT_TRUE(ast);
+    EXPECT_TRUE(!parser.HasError());
+  }
+
+  {
+    CXFA_FMLexer lexer(input);
+    CXFA_FMParser parser(heap(), &lexer);
+    parser.SetMaxParseDepthForTest(5);
+    CXFA_FMAST* ast = parser.Parse();
+    ASSERT_TRUE(ast == nullptr);
+    EXPECT_TRUE(parser.HasError());
+  }
+}
+
+TEST_F(CXFA_FMParserTest, ParseCallSmall) {
+  const wchar_t input[] = L"i.f(O)";
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+pfm_ret = pfm_rt.get_val((function() {
+  return pfm_method_runner(i, function(obj) {
+    return obj.f(pfm_rt.get_val(O));
+  });
+}).call(this));
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  CXFA_FMLexer lexer(input);
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  EXPECT_FALSE(parser.HasError());
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
+  ASSERT_TRUE(buf.has_value());
+  EXPECT_STREQ(ret, buf.value().MakeString().c_str());
+}
+
+TEST_F(CXFA_FMParserTest, ParseCallBig) {
+  const wchar_t input[] = L"i.f(O.e(O.e(O)))";
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+pfm_ret = pfm_rt.get_val((function() {
+  return pfm_method_runner(i, function(obj) {
+    return obj.f(pfm_rt.get_val((function() {
+  return pfm_method_runner(O, function(obj) {
+    return obj.e(pfm_rt.get_val((function() {
+  return pfm_method_runner(O, function(obj) {
+    return obj.e(pfm_rt.get_val(O));
+  });
+}).call(this)));
+  });
+}).call(this)));
+  });
+}).call(this));
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  CXFA_FMLexer lexer(input);
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  EXPECT_FALSE(parser.HasError());
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
+  ASSERT_TRUE(buf.has_value());
+  EXPECT_STREQ(ret, buf.value().MakeString().c_str());
+}
+
+TEST_F(CXFA_FMParserTest, ParseVar) {
+  const wchar_t input[] = LR"(var s = "")";
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+var s = "";
+s = pfm_rt.var_filter(s);
+pfm_ret = s;
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  CXFA_FMLexer lexer(input);
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  EXPECT_FALSE(parser.HasError());
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
+  ASSERT_TRUE(buf.has_value());
+  EXPECT_STREQ(ret, buf.value().MakeString().c_str());
+}
+
+TEST_F(CXFA_FMParserTest, ParseFunctionCallNoArguments) {
+  const wchar_t input[] = L"P.x()";
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+pfm_ret = pfm_rt.get_val((function() {
+  return pfm_method_runner(P, function(obj) {
+    return obj.x();
+  });
+}).call(this));
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  CXFA_FMLexer lexer(input);
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  EXPECT_FALSE(parser.HasError());
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
+  ASSERT_TRUE(buf.has_value());
+  EXPECT_STREQ(ret, buf.value().MakeString().c_str());
+}
+
+TEST_F(CXFA_FMParserTest, ParseFunctionCallSingleArgument) {
+  const wchar_t input[] = L"P.x(foo)";
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+pfm_ret = pfm_rt.get_val((function() {
+  return pfm_method_runner(P, function(obj) {
+    return obj.x(pfm_rt.get_jsobj(foo));
+  });
+}).call(this));
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  CXFA_FMLexer lexer(input);
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  EXPECT_FALSE(parser.HasError());
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
+  ASSERT_TRUE(buf.has_value());
+  EXPECT_STREQ(ret, buf.value().MakeString().c_str());
+}
+
+TEST_F(CXFA_FMParserTest, ParseFunctionCallMultipleArguments) {
+  const wchar_t input[] = L"P.x(foo, bar, baz)";
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+pfm_ret = pfm_rt.get_val((function() {
+  return pfm_method_runner(P, function(obj) {
+    return obj.x(pfm_rt.get_jsobj(foo), pfm_rt.get_val(bar), pfm_rt.get_val(baz));
+  });
+}).call(this));
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  CXFA_FMLexer lexer(input);
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  EXPECT_FALSE(parser.HasError());
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
+  ASSERT_TRUE(buf.has_value());
+  EXPECT_STREQ(ret, buf.value().MakeString().c_str());
+}
+
+TEST_F(CXFA_FMParserTest, ParseFunctionCallMissingCommas) {
+  const wchar_t input[] = L"P.x(!foo!bar!baz)";
+  CXFA_FMLexer lexer(input);
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  ASSERT_TRUE(ast == nullptr);
+  EXPECT_TRUE(parser.HasError());
+}
+
+TEST_F(CXFA_FMParserTest, ParseFunctionCallTrailingComma) {
+  const wchar_t input[] = L"P.x(foo,bar,baz,)";
+  CXFA_FMLexer lexer(input);
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  ASSERT_TRUE(ast == nullptr);
+  EXPECT_TRUE(parser.HasError());
+}
+
+TEST_F(CXFA_FMParserTest, ParseFunctionCallExtraComma) {
+  const wchar_t input[] = L"P.x(foo,bar,,baz)";
+  CXFA_FMLexer lexer(input);
+  CXFA_FMParser parser(heap(), &lexer);
+  CXFA_FMAST* ast = parser.Parse();
+  ASSERT_TRUE(ast == nullptr);
+  EXPECT_TRUE(parser.HasError());
+}
diff --git a/xfa/fxfa/formcalc/cxfa_fmtojavascriptdepth.cpp b/xfa/fxfa/formcalc/cxfa_fmtojavascriptdepth.cpp
new file mode 100644
index 0000000..3796de5
--- /dev/null
+++ b/xfa/fxfa/formcalc/cxfa_fmtojavascriptdepth.cpp
@@ -0,0 +1,11 @@
+// Copyright 2017 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fxfa/formcalc/cxfa_fmtojavascriptdepth.h"
+
+unsigned long CXFA_FMToJavaScriptDepth::depth_ = 0;
+
+void CXFA_FMToJavaScriptDepth::Reset() {
+  depth_ = 0;
+}
diff --git a/xfa/fxfa/formcalc/cxfa_fmtojavascriptdepth.h b/xfa/fxfa/formcalc/cxfa_fmtojavascriptdepth.h
new file mode 100644
index 0000000..e720469
--- /dev/null
+++ b/xfa/fxfa/formcalc/cxfa_fmtojavascriptdepth.h
@@ -0,0 +1,26 @@
+// Copyright 2017 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef XFA_FXFA_FORMCALC_CXFA_FMTOJAVASCRIPTDEPTH_H_
+#define XFA_FXFA_FORMCALC_CXFA_FMTOJAVASCRIPTDEPTH_H_
+
+class CXFA_FMToJavaScriptDepth {
+ public:
+  CXFA_FMToJavaScriptDepth() { depth_++; }
+  ~CXFA_FMToJavaScriptDepth() { depth_--; }
+
+  bool IsWithinMaxDepth() const { return depth_ <= kMaxDepth; }
+
+  static void Reset();
+
+ private:
+  // Arbitarily picked by looking at how deep a translation got before hitting
+  // the getting fuzzer memory limits. Should be larger then |kMaxParseDepth| in
+  // cxfa_fmparser.cpp.
+  const unsigned long kMaxDepth = 5000;
+
+  static unsigned long depth_;
+};
+
+#endif  // XFA_FXFA_FORMCALC_CXFA_FMTOJAVASCRIPTDEPTH_H_
diff --git a/xfa/fxfa/fxfa.h b/xfa/fxfa/fxfa.h
index 2028cd3..3df4438 100644
--- a/xfa/fxfa/fxfa.h
+++ b/xfa/fxfa/fxfa.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,21 +7,6 @@
 #ifndef XFA_FXFA_FXFA_H_
 #define XFA_FXFA_FXFA_H_
 
-#include <memory>
-
-#include "core/fxcrt/cfx_timer.h"
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/fx_dib.h"
-#include "xfa/fxfa/fxfa_basic.h"
-
-class CXFA_FFDoc;
-class CXFA_FFPageView;
-class CXFA_FFWidget;
-class CXFA_Submit;
-class IFX_SeekableReadStream;
-class IJS_Runtime;
-
 // Note, values must match fpdf_formfill.h JSPLATFORM_ALERT_BUTTON_* flags.
 enum class AlertButton {
   kDefault = 0,
@@ -57,16 +42,14 @@
   kXFAForeground = 3,
 };
 
-#define XFA_PRINTOPT_ShowDialog 0x00000001
-#define XFA_PRINTOPT_CanCancel 0x00000002
-#define XFA_PRINTOPT_ShrinkPage 0x00000004
-#define XFA_PRINTOPT_AsImage 0x00000008
-#define XFA_PRINTOPT_ReverseOrder 0x00000010
-#define XFA_PRINTOPT_PrintAnnot 0x00000020
-
-#define XFA_PAGEVIEWEVENT_PostAdded 1
-#define XFA_PAGEVIEWEVENT_PostRemoved 3
-#define XFA_PAGEVIEWEVENT_StopLayout 4
+enum class XFA_PrintOpt : uint8_t {
+  kShowDialog = 1 << 0,
+  kCanCancel = 1 << 1,
+  kShrinkPage = 1 << 2,
+  kAsImage = 1 << 3,
+  kReverseOrder = 1 << 4,
+  kPrintAnnot = 1 << 5,
+};
 
 enum class XFA_EventError {
   kError = -1,
@@ -75,188 +58,17 @@
   kDisabled = 2,
 };
 
-enum XFA_WidgetStatus {
-  XFA_WidgetStatus_None = 0,
-
-  XFA_WidgetStatus_Access = 1 << 0,
-  XFA_WidgetStatus_ButtonDown = 1 << 1,
-  XFA_WidgetStatus_Disabled = 1 << 2,
-  XFA_WidgetStatus_Focused = 1 << 3,
-  XFA_WidgetStatus_Printable = 1 << 4,
-  XFA_WidgetStatus_RectCached = 1 << 5,
-  XFA_WidgetStatus_TextEditValueChanged = 1 << 6,
-  XFA_WidgetStatus_Viewable = 1 << 7,
-  XFA_WidgetStatus_Visible = 1 << 8
-};
-
-// Probably should be called IXFA_AppDelegate.
-class IXFA_AppProvider {
- public:
-  virtual ~IXFA_AppProvider() = default;
-
-  /**
-   * Returns the language of the running host application. Such as zh_CN
-   */
-  virtual WideString GetLanguage() = 0;
-
-  /**
-   * Returns the platform of the machine running the script. Such as WIN
-   */
-  virtual WideString GetPlatform() = 0;
-
-  /**
-   * Get application name, such as Phantom.
-   */
-  virtual WideString GetAppName() = 0;
-
-  /**
-   * Get application message box title.
-   */
-  virtual WideString GetAppTitle() const = 0;
-
-  /**
-   * Causes the system to play a sound.
-   * @param[in] dwType The system code for the appropriate sound.0 (Error)1
-   * (Warning)2 (Question)3 (Status)4 (Default)
-   */
-  virtual void Beep(uint32_t dwType) = 0;
-
-  /**
-   * Displays a message box.
-   * @param[in] wsMessage    - Message string to display in box.
-   * @param[in] wsTitle      - Title string for box.
-   * @param[in] dwIconType   - Icon type, refer to XFA_MBICON.
-   * @param[in] dwButtonType - Button type, refer to XFA_MESSAGEBUTTON.
-   * @return A valid integer representing the value of the button pressed by the
-   * user, refer to XFA_ID.
-   */
-  virtual int32_t MsgBox(const WideString& wsMessage,
-                         const WideString& wsTitle,
-                         uint32_t dwIconType,
-                         uint32_t dwButtonType) = 0;
-
-  /**
-   * Get a response from the user.
-   * @param[in] wsQuestion      - Message string to display in box.
-   * @param[in] wsTitle         - Title string for box.
-   * @param[in] wsDefaultAnswer - Initial contents for answer.
-   * @param[in] bMask           - Mask the user input with asterisks when true,
-   * @return A string containing the user's response.
-   */
-  virtual WideString Response(const WideString& wsQuestion,
-                              const WideString& wsTitle,
-                              const WideString& wsDefaultAnswer,
-                              bool bMask) = 0;
-
-  /**
-   * Download something from somewhere.
-   * @param[in] wsURL - http, ftp, such as
-   * "http://www.w3.org/TR/REC-xml-names/".
-   */
-  virtual RetainPtr<IFX_SeekableReadStream> DownloadURL(
-      const WideString& wsURL) = 0;
-
-  /**
-   * POST data to the given url.
-   * @param[in] wsURL         the URL being uploaded.
-   * @param[in] wsData        the data being uploaded.
-   * @param[in] wsContentType the content type of data including text/html,
-   * text/xml, text/plain, multipart/form-data,
-   *                          application/x-www-form-urlencoded,
-   * application/octet-stream, any valid MIME type.
-   * @param[in] wsEncode      the encode of data including UTF-8, UTF-16,
-   * ISO8859-1, any recognized [IANA]character encoding
-   * @param[in] wsHeader      any additional HTTP headers to be included in the
-   * post.
-   * @param[out] wsResponse   decoded response from server.
-   * @return true Server permitted the post request, false otherwise.
-   */
-  virtual bool PostRequestURL(const WideString& wsURL,
-                              const WideString& wsData,
-                              const WideString& wsContentType,
-                              const WideString& wsEncode,
-                              const WideString& wsHeader,
-                              WideString& wsResponse) = 0;
-
-  /**
-   * PUT data to the given url.
-   * @param[in] wsURL         the URL being uploaded.
-   * @param[in] wsData            the data being uploaded.
-   * @param[in] wsEncode      the encode of data including UTF-8, UTF-16,
-   * ISO8859-1, any recognized [IANA]character encoding
-   * @return true Server permitted the post request, false otherwise.
-   */
-  virtual bool PutRequestURL(const WideString& wsURL,
-                             const WideString& wsData,
-                             const WideString& wsEncode) = 0;
-
-  virtual TimerHandlerIface* GetTimerHandler() const = 0;
-};
-
-class IXFA_DocEnvironment {
- public:
-  virtual ~IXFA_DocEnvironment() = default;
-
-  virtual void SetChangeMark(CXFA_FFDoc* hDoc) = 0;
-  virtual void InvalidateRect(CXFA_FFPageView* pPageView,
-                              const CFX_RectF& rt) = 0;
-  // Show or hide caret.
-  virtual void DisplayCaret(CXFA_FFWidget* hWidget,
-                            bool bVisible,
-                            const CFX_RectF* pRtAnchor) = 0;
-
-  virtual bool GetPopupPos(CXFA_FFWidget* hWidget,
-                           float fMinPopup,
-                           float fMaxPopup,
-                           const CFX_RectF& rtAnchor,
-                           CFX_RectF* pPopupRect) = 0;
-  virtual bool PopupMenu(CXFA_FFWidget* hWidget, const CFX_PointF& ptPopup) = 0;
-
-  // Specify dwFlags XFA_PAGEVIEWEVENT_Added, XFA_PAGEVIEWEVENT_Removing
-  virtual void PageViewEvent(CXFA_FFPageView* pPageView, uint32_t dwFlags) = 0;
-
-  virtual void WidgetPostAdd(CXFA_FFWidget* hWidget) = 0;
-  virtual void WidgetPreRemove(CXFA_FFWidget* hWidget) = 0;
-  virtual int32_t CountPages(CXFA_FFDoc* hDoc) = 0;
-  virtual int32_t GetCurrentPage(CXFA_FFDoc* hDoc) = 0;
-  virtual void SetCurrentPage(CXFA_FFDoc* hDoc, int32_t iCurPage) = 0;
-  virtual bool IsCalculationsEnabled(CXFA_FFDoc* hDoc) = 0;
-  virtual void SetCalculationsEnabled(CXFA_FFDoc* hDoc, bool bEnabled) = 0;
-  virtual void GetTitle(CXFA_FFDoc* hDoc, WideString& wsTitle) = 0;
-  virtual void SetTitle(CXFA_FFDoc* hDoc, const WideString& wsTitle) = 0;
-  virtual void ExportData(CXFA_FFDoc* hDoc,
-                          const WideString& wsFilePath,
-                          bool bXDP) = 0;
-  virtual void GotoURL(CXFA_FFDoc* hDoc, const WideString& bsURL) = 0;
-  virtual bool IsValidationsEnabled(CXFA_FFDoc* hDoc) = 0;
-  virtual void SetValidationsEnabled(CXFA_FFDoc* hDoc, bool bEnabled) = 0;
-  virtual void SetFocusWidget(CXFA_FFDoc* hDoc, CXFA_FFWidget* hWidget) = 0;
-  virtual void Print(CXFA_FFDoc* hDoc,
-                     int32_t nStartPage,
-                     int32_t nEndPage,
-                     uint32_t dwOptions) = 0;
-  virtual FX_ARGB GetHighlightColor(CXFA_FFDoc* hDoc) = 0;
-  virtual IJS_Runtime* GetIJSRuntime(CXFA_FFDoc* hDoc) const = 0;
-  virtual RetainPtr<IFX_SeekableReadStream> OpenLinkedFile(
-      CXFA_FFDoc* hDoc,
-      const WideString& wsLink) = 0;
-
-#ifdef PDF_XFA_ELEMENT_SUBMIT_ENABLED
-  virtual bool Submit(CXFA_FFDoc* hDoc, CXFA_Submit* submit) = 0;
-#endif  // PDF_XFA_ELEMENT_SUBMIT_ENABLED
-};
-
-class IXFA_WidgetIterator {
- public:
-  virtual ~IXFA_WidgetIterator() = default;
-
-  virtual void Reset() = 0;
-  virtual CXFA_FFWidget* MoveToFirst() = 0;
-  virtual CXFA_FFWidget* MoveToLast() = 0;
-  virtual CXFA_FFWidget* MoveToNext() = 0;
-  virtual CXFA_FFWidget* MoveToPrevious() = 0;
-  virtual CXFA_FFWidget* GetCurrentWidget() = 0;
-  virtual bool SetCurrentWidget(CXFA_FFWidget* hWidget) = 0;
+enum class XFA_WidgetStatus : uint16_t {
+  kNone = 0,
+  kAccess = 1 << 0,
+  kButtonDown = 1 << 1,
+  kDisabled = 1 << 2,
+  kFocused = 1 << 3,
+  kPrintable = 1 << 4,
+  kRectCached = 1 << 5,
+  kTextEditValueChanged = 1 << 6,
+  kViewable = 1 << 7,
+  kVisible = 1 << 8
 };
 
 #endif  // XFA_FXFA_FXFA_H_
diff --git a/xfa/fxfa/fxfa_basic.h b/xfa/fxfa/fxfa_basic.h
index 333a61d..efabdef 100644
--- a/xfa/fxfa/fxfa_basic.h
+++ b/xfa/fxfa/fxfa_basic.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,10 +9,6 @@
 
 #include <stdint.h>
 
-class CXFA_Measurement;
-enum class XFA_ObjectType;
-struct XFA_SCRIPTATTRIBUTEINFO;
-
 enum XFA_HashCode : uint32_t {
   XFA_HASHCODE_None = 0,
 
@@ -52,36 +48,13 @@
 #undef PCKT____
 };
 
-enum XFA_XDPPACKET {
-  XFA_XDPPACKET_UNKNOWN = 0,
-  XFA_XDPPACKET_Config = 1 << static_cast<uint8_t>(XFA_PacketType::Config),
-  XFA_XDPPACKET_Template = 1 << static_cast<uint8_t>(XFA_PacketType::Template),
-  XFA_XDPPACKET_Datasets = 1 << static_cast<uint8_t>(XFA_PacketType::Datasets),
-  XFA_XDPPACKET_Form = 1 << static_cast<uint8_t>(XFA_PacketType::Form),
-  XFA_XDPPACKET_LocaleSet = 1
-                            << static_cast<uint8_t>(XFA_PacketType::LocaleSet),
-  XFA_XDPPACKET_ConnectionSet =
-      1 << static_cast<uint8_t>(XFA_PacketType::ConnectionSet),
-  XFA_XDPPACKET_SourceSet = 1
-                            << static_cast<uint8_t>(XFA_PacketType::SourceSet),
-  XFA_XDPPACKET_Xdc = 1 << static_cast<uint8_t>(XFA_PacketType::Xdc),
-  XFA_XDPPACKET_Pdf = 1 << static_cast<uint8_t>(XFA_PacketType::Pdf),
-  XFA_XDPPACKET_Xfdf = 1 << static_cast<uint8_t>(XFA_PacketType::Xfdf),
-  XFA_XDPPACKET_Xmpmeta = 1 << static_cast<uint8_t>(XFA_PacketType::Xmpmeta),
-  XFA_XDPPACKET_Signature = 1
-                            << static_cast<uint8_t>(XFA_PacketType::Signature),
-  XFA_XDPPACKET_Stylesheet =
-      1 << static_cast<uint8_t>(XFA_PacketType::Stylesheet),
-  XFA_XDPPACKET_USER = 1 << static_cast<uint8_t>(XFA_PacketType::User),
-  XFA_XDPPACKET_XDP = 1 << static_cast<uint8_t>(XFA_PacketType::Xdp)
-};
-
-enum XFA_XDPPACKET_FLAGS {
-  XFA_XDPPACKET_FLAGS_COMPLETEMATCH = 1,
-  XFA_XDPPACKET_FLAGS_PREFIXMATCH = 2,
-  XFA_XDPPACKET_FLAGS_NOMATCH = 4,
-  XFA_XDPPACKET_FLAGS_SUPPORTONE = 8,
-  XFA_XDPPACKET_FLAGS_SUPPORTMANY = 16,
+enum class XFA_XDPPACKET {
+  kUNKNOWN = 0,
+#undef PCKT____
+#define PCKT____(a, b, c, d, e, f) \
+  k##c = 1 << static_cast<uint8_t>(XFA_PacketType::c),
+#include "xfa/fxfa/parser/packets.inc"
+#undef PCKT____
 };
 
 enum class XFA_AttributeValue : uint16_t {
@@ -115,9 +88,6 @@
   Measure,
 };
 
-#define XFA_PROPERTYFLAG_OneOf 0x01
-#define XFA_PROPERTYFLAG_DefaultOneOf 0x02
-
 enum class XFA_Unit : uint8_t {
   Percent = 0,
   Em,
diff --git a/xfa/fxfa/fxfa_basic_unittest.cpp b/xfa/fxfa/fxfa_basic_unittest.cpp
index 5f77708..ca69e4c 100644
--- a/xfa/fxfa/fxfa_basic_unittest.cpp
+++ b/xfa/fxfa/fxfa_basic_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,10 +11,10 @@
 
 void HashTestCase(uint32_t hash, const char* str, uint32_t* so_far) {
   if (hash != 0xffffffffu) {
-    EXPECT_EQ(hash, FX_HashCode_GetAsIfW(str, false)) << str;
+    EXPECT_EQ(hash, FX_HashCode_GetAsIfW(str)) << str;
     EXPECT_LT(*so_far, hash) << hash;
   } else {
-    EXPECT_NE(hash, FX_HashCode_GetAsIfW(str, false)) << str;
+    EXPECT_NE(hash, FX_HashCode_GetAsIfW(str)) << str;
   }
   *so_far = hash;
 }
diff --git a/xfa/fxfa/layout/BUILD.gn b/xfa/fxfa/layout/BUILD.gn
index 6c9295f..1d28f04 100644
--- a/xfa/fxfa/layout/BUILD.gn
+++ b/xfa/fxfa/layout/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2019 The PDFium Authors. All rights reserved.
+# Copyright 2019 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -23,20 +23,23 @@
     "cxfa_viewlayoutprocessor.cpp",
     "cxfa_viewlayoutprocessor.h",
   ]
+  allow_circular_includes_from = [ "../../../fxjs" ]
+  configs += [
+    "../../../:pdfium_strict_config",
+    "../../../:pdfium_noshorten_config",
+    "../../:xfa_warnings",
+  ]
   deps = [
     "../../../core/fxcrt",
     "../../../fxjs",
+    "../../../fxjs:gc",
     "../parser",
   ]
-  allow_circular_includes_from = [ "../../../fxjs" ]
-  configs += [
-    "../../../:pdfium_core_config",
-    "../../:xfa_warnings",
-  ]
   visibility = [ "../../../*" ]
 }
 
 pdfium_embeddertest_source_set("embeddertests") {
   sources = [ "cxfa_layoutitem_embeddertest.cpp" ]
+  deps = [ "../../../fxjs:gc" ]
   pdfium_root_dir = "../../../"
 }
diff --git a/xfa/fxfa/layout/DEPS b/xfa/fxfa/layout/DEPS
new file mode 100644
index 0000000..4931b25
--- /dev/null
+++ b/xfa/fxfa/layout/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  '-xfa/fwl',
+]
+
diff --git a/xfa/fxfa/layout/cxfa_contentlayoutitem.cpp b/xfa/fxfa/layout/cxfa_contentlayoutitem.cpp
index 250cd57..9eacf72 100644
--- a/xfa/fxfa/layout/cxfa_contentlayoutitem.cpp
+++ b/xfa/fxfa/layout/cxfa_contentlayoutitem.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,26 +6,26 @@
 
 #include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
 
-#include <utility>
-
 #include "fxjs/xfa/cjx_object.h"
+#include "third_party/base/check_op.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
 #include "xfa/fxfa/parser/cxfa_margin.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-CXFA_ContentLayoutItem::CXFA_ContentLayoutItem(
-    CXFA_Node* pNode,
-    std::unique_ptr<CXFA_FFWidget> pWidget)
-    : CXFA_LayoutItem(pNode, kContentItem), m_pFFWidget(std::move(pWidget)) {
+CXFA_ContentLayoutItem::CXFA_ContentLayoutItem(CXFA_Node* pNode,
+                                               CXFA_FFWidget* pWidget)
+    : CXFA_LayoutItem(pNode, kContentItem), m_pFFWidget(pWidget) {
   if (m_pFFWidget)
     m_pFFWidget->SetLayoutItem(this);
 }
 
-CXFA_ContentLayoutItem::~CXFA_ContentLayoutItem() {
-  if (m_pFFWidget)
-    m_pFFWidget->SetLayoutItem(nullptr);
+CXFA_ContentLayoutItem::~CXFA_ContentLayoutItem() = default;
 
-  RemoveSelf();
+void CXFA_ContentLayoutItem::Trace(cppgc::Visitor* visitor) const {
+  CXFA_LayoutItem::Trace(visitor);
+  visitor->Trace(m_pPrev);
+  visitor->Trace(m_pNext);
+  visitor->Trace(m_pFFWidget);
 }
 
 CXFA_ContentLayoutItem* CXFA_ContentLayoutItem::GetFirst() {
@@ -61,11 +61,13 @@
     m_pPrev->m_pNext = m_pNext;
 }
 
-CFX_RectF CXFA_ContentLayoutItem::GetRect(bool bRelative) const {
+CFX_RectF CXFA_ContentLayoutItem::GetRelativeRect() const {
+  return CFX_RectF(m_sPos, m_sSize);
+}
+
+CFX_RectF CXFA_ContentLayoutItem::GetAbsoluteRect() const {
   CFX_PointF sPos = m_sPos;
   CFX_SizeF sSize = m_sSize;
-  if (bRelative)
-    return CFX_RectF(sPos, sSize);
 
   for (CXFA_LayoutItem* pLayoutItem = GetParent(); pLayoutItem;
        pLayoutItem = pLayoutItem->GetParent()) {
diff --git a/xfa/fxfa/layout/cxfa_contentlayoutitem.h b/xfa/fxfa/layout/cxfa_contentlayoutitem.h
index 063f9a3..8131d0d 100644
--- a/xfa/fxfa/layout/cxfa_contentlayoutitem.h
+++ b/xfa/fxfa/layout/cxfa_contentlayoutitem.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,51 +7,52 @@
 #ifndef XFA_FXFA_LAYOUT_CXFA_CONTENTLAYOUTITEM_H_
 #define XFA_FXFA_LAYOUT_CXFA_CONTENTLAYOUTITEM_H_
 
-#include <memory>
-
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/mask.h"
+#include "v8/include/cppgc/persistent.h"
+#include "xfa/fxfa/fxfa.h"
 #include "xfa/fxfa/layout/cxfa_layoutitem.h"
 
 class CXFA_FFWidget;
 
-class CXFA_ContentLayoutItem : public CXFA_LayoutItem {
+class CXFA_ContentLayoutItem final : public CXFA_LayoutItem {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ContentLayoutItem() override;
 
-  CXFA_FFWidget* GetFFWidget() { return m_pFFWidget.get(); }
+  void Trace(cppgc::Visitor* visitor) const override;
+
+  CXFA_FFWidget* GetFFWidget() { return m_pFFWidget; }
 
   CXFA_ContentLayoutItem* GetFirst();
   CXFA_ContentLayoutItem* GetLast();
-  CXFA_ContentLayoutItem* GetPrev() const { return m_pPrev.Get(); }
-  CXFA_ContentLayoutItem* GetNext() const { return m_pNext.Get(); }
+  CXFA_ContentLayoutItem* GetPrev() const { return m_pPrev; }
+  CXFA_ContentLayoutItem* GetNext() const { return m_pNext; }
   void InsertAfter(CXFA_ContentLayoutItem* pNext);
 
-  CFX_RectF GetRect(bool bRelative) const;
+  CFX_RectF GetRelativeRect() const;
+  CFX_RectF GetAbsoluteRect() const;
   size_t GetIndex() const;
 
-  void SetStatusBits(uint32_t val) { m_dwStatus |= val; }
-  void ClearStatusBits(uint32_t val) { m_dwStatus &= ~val; }
+  void SetStatusBits(Mask<XFA_WidgetStatus> val) { m_dwStatus |= val; }
+  void ClearStatusBits(Mask<XFA_WidgetStatus> val) { m_dwStatus &= ~val; }
 
   // TRUE if all (not any) bits set in |val| are set in |m_dwStatus|.
-  bool TestStatusBits(uint32_t val) const { return (m_dwStatus & val) == val; }
+  bool TestStatusBits(Mask<XFA_WidgetStatus> val) const {
+    return m_dwStatus.TestAll(val);
+  }
 
   CFX_PointF m_sPos;
   CFX_SizeF m_sSize;
 
  private:
-  CXFA_ContentLayoutItem(CXFA_Node* pNode,
-                         std::unique_ptr<CXFA_FFWidget> pFFWidget);
-
+  CXFA_ContentLayoutItem(CXFA_Node* pNode, CXFA_FFWidget* pFFWidget);
   void RemoveSelf();
 
-  mutable uint32_t m_dwStatus = 0;
-  UnownedPtr<CXFA_ContentLayoutItem> m_pPrev;
-  UnownedPtr<CXFA_ContentLayoutItem> m_pNext;
-  std::unique_ptr<CXFA_FFWidget> const m_pFFWidget;
+  mutable Mask<XFA_WidgetStatus> m_dwStatus;
+  cppgc::Member<CXFA_ContentLayoutItem> m_pPrev;
+  cppgc::Member<CXFA_ContentLayoutItem> m_pNext;
+  cppgc::Member<CXFA_FFWidget> const m_pFFWidget;
 };
 
 inline CXFA_FFWidget* GetFFWidget(CXFA_ContentLayoutItem* item) {
diff --git a/xfa/fxfa/layout/cxfa_contentlayoutprocessor.cpp b/xfa/fxfa/layout/cxfa_contentlayoutprocessor.cpp
index 833ab99..96eb05f 100644
--- a/xfa/fxfa/layout/cxfa_contentlayoutprocessor.cpp
+++ b/xfa/fxfa/layout/cxfa_contentlayoutprocessor.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #include "xfa/fxfa/layout/cxfa_contentlayoutprocessor.h"
 
 #include <algorithm>
-#include <memory>
 #include <utility>
 #include <vector>
 
+#include "core/fxcrt/stl_util.h"
+#include "fxjs/gc/container_trace.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/adapters.h"
+#include "third_party/base/notreached.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
@@ -78,7 +78,7 @@
       break;
     }
     default:
-      NOTREACHED();
+      NOTREACHED_NORETURN();
   }
 }
 
@@ -92,32 +92,32 @@
 
   CFX_SizeF containerSize;
   if (eType == XFA_Element::Subform || eType == XFA_Element::ExclGroup) {
-    Optional<CXFA_Measurement> wValue =
+    absl::optional<CXFA_Measurement> wValue =
         pFormNode->JSObject()->TryMeasure(XFA_Attribute::W, false);
-    if (wValue && wValue->GetValue() > kXFALayoutPrecision) {
+    if (wValue.has_value() && wValue->GetValue() > kXFALayoutPrecision) {
       containerSize.width = wValue->ToUnit(XFA_Unit::Pt);
       *bContainerWidthAutoSize = false;
     }
 
-    Optional<CXFA_Measurement> hValue =
+    absl::optional<CXFA_Measurement> hValue =
         pFormNode->JSObject()->TryMeasure(XFA_Attribute::H, false);
-    if (hValue && hValue->GetValue() > kXFALayoutPrecision) {
+    if (hValue.has_value() && hValue->GetValue() > kXFALayoutPrecision) {
       containerSize.height = hValue->ToUnit(XFA_Unit::Pt);
       *bContainerHeightAutoSize = false;
     }
   }
 
   if (*bContainerWidthAutoSize && eType == XFA_Element::Subform) {
-    Optional<CXFA_Measurement> maxW =
+    absl::optional<CXFA_Measurement> maxW =
         pFormNode->JSObject()->TryMeasure(XFA_Attribute::MaxW, false);
-    if (maxW && maxW->GetValue() > kXFALayoutPrecision) {
+    if (maxW.has_value() && maxW->GetValue() > kXFALayoutPrecision) {
       containerSize.width = maxW->ToUnit(XFA_Unit::Pt);
       *bContainerWidthAutoSize = false;
     }
 
-    Optional<CXFA_Measurement> maxH =
+    absl::optional<CXFA_Measurement> maxH =
         pFormNode->JSObject()->TryMeasure(XFA_Attribute::MaxH, false);
-    if (maxH && maxH->GetValue() > kXFALayoutPrecision) {
+    if (maxH.has_value() && maxH->GetValue() > kXFALayoutPrecision) {
       containerSize.height = maxH->ToUnit(XFA_Unit::Pt);
       *bContainerHeightAutoSize = false;
     }
@@ -138,14 +138,14 @@
   if (bContainerWidthAutoSize) {
     componentSize.width = fContentCalculatedWidth;
     if (pMarginNode) {
-      Optional<CXFA_Measurement> leftInset =
+      absl::optional<CXFA_Measurement> leftInset =
           pMarginNode->JSObject()->TryMeasure(XFA_Attribute::LeftInset, false);
-      if (leftInset)
+      if (leftInset.has_value())
         componentSize.width += leftInset->ToUnit(XFA_Unit::Pt);
 
-      Optional<CXFA_Measurement> rightInset =
+      absl::optional<CXFA_Measurement> rightInset =
           pMarginNode->JSObject()->TryMeasure(XFA_Attribute::RightInset, false);
-      if (rightInset)
+      if (rightInset.has_value())
         componentSize.width += rightInset->ToUnit(XFA_Unit::Pt);
     }
   }
@@ -153,15 +153,15 @@
   if (bContainerHeightAutoSize) {
     componentSize.height = fContentCalculatedHeight;
     if (pMarginNode) {
-      Optional<CXFA_Measurement> topInset =
+      absl::optional<CXFA_Measurement> topInset =
           pMarginNode->JSObject()->TryMeasure(XFA_Attribute::TopInset, false);
-      if (topInset)
+      if (topInset.has_value())
         componentSize.height += topInset->ToUnit(XFA_Unit::Pt);
 
-      Optional<CXFA_Measurement> bottomInset =
+      absl::optional<CXFA_Measurement> bottomInset =
           pMarginNode->JSObject()->TryMeasure(XFA_Attribute::BottomInset,
                                               false);
-      if (bottomInset)
+      if (bottomInset.has_value())
         componentSize.height += bottomInset->ToUnit(XFA_Unit::Pt);
     }
   }
@@ -184,27 +184,30 @@
   return inset;
 }
 
-void RelocateTableRowCells(const RetainPtr<CXFA_ContentLayoutItem>& pLayoutRow,
+void RelocateTableRowCells(CXFA_ContentLayoutItem* pLayoutRow,
                            const std::vector<float>& rgSpecifiedColumnWidths,
                            XFA_AttributeValue eLayout) {
   bool bContainerWidthAutoSize = true;
   bool bContainerHeightAutoSize = true;
-  CFX_SizeF containerSize = CalculateContainerSpecifiedSize(
+  const CFX_SizeF containerSize = CalculateContainerSpecifiedSize(
       pLayoutRow->GetFormNode(), &bContainerWidthAutoSize,
       &bContainerHeightAutoSize);
+
   CXFA_Margin* pMargin =
       pLayoutRow->GetFormNode()->GetFirstChildByClass<CXFA_Margin>(
           XFA_Element::Margin);
-  CFX_FloatRect inset = GetMarginInset(pMargin);
-  float fContentWidthLimit =
+  const CFX_FloatRect inset = GetMarginInset(pMargin);
+
+  const float fContentWidthLimit =
       bContainerWidthAutoSize ? FLT_MAX
                               : containerSize.width - inset.left - inset.right;
-  float fContentCurrentHeight =
+  const float fContentCurrentHeight =
       pLayoutRow->m_sSize.height - inset.top - inset.bottom;
+
   float fContentCalculatedWidth = 0;
   float fContentCalculatedHeight = 0;
   float fCurrentColX = 0;
-  int32_t nCurrentColIdx = 0;
+  size_t nCurrentColIdx = 0;
   bool bMetWholeRowCell = false;
 
   for (CXFA_LayoutItem* pIter = pLayoutRow->GetFirstChild(); pIter;
@@ -213,24 +216,28 @@
     if (!pLayoutChild)
       continue;
 
-    int32_t nOriginalColSpan =
+    const int32_t nOriginalColSpan =
         pLayoutChild->GetFormNode()->JSObject()->GetInteger(
             XFA_Attribute::ColSpan);
-    if (nOriginalColSpan <= 0 && nOriginalColSpan != -1)
+
+    size_t nColSpan;
+    if (nOriginalColSpan > 0)
+      nColSpan = static_cast<size_t>(nOriginalColSpan);
+    else if (nOriginalColSpan == -1)
+      nColSpan = rgSpecifiedColumnWidths.size();
+    else
       continue;
 
-    int32_t nColSpan = nOriginalColSpan;
+    CHECK(nCurrentColIdx <= rgSpecifiedColumnWidths.size());
+    const size_t remaining = rgSpecifiedColumnWidths.size() - nCurrentColIdx;
+    nColSpan = std::min(nColSpan, remaining);
+
     float fColSpanWidth = 0;
-    if (nColSpan == -1 ||
-        nCurrentColIdx + nColSpan >
-            pdfium::CollectionSize<int32_t>(rgSpecifiedColumnWidths)) {
-      nColSpan = pdfium::CollectionSize<int32_t>(rgSpecifiedColumnWidths) -
-                 nCurrentColIdx;
-    }
-    for (int32_t i = 0; i < nColSpan; i++)
+    for (size_t i = 0; i < nColSpan; i++)
       fColSpanWidth += rgSpecifiedColumnWidths[nCurrentColIdx + i];
 
-    if (nColSpan != nOriginalColSpan) {
+    if (nOriginalColSpan == -1 ||
+        nColSpan != static_cast<size_t>(nOriginalColSpan)) {
       fColSpanWidth = bMetWholeRowCell ? 0
                                        : std::max(fColSpanWidth,
                                                   pLayoutChild->m_sSize.height);
@@ -268,32 +275,31 @@
       CXFA_Para* pParaNode =
           pLayoutChild->GetFormNode()->GetFirstChildByClass<CXFA_Para>(
               XFA_Element::Para);
-      if (pParaNode && pLayoutChild->GetFirstChild()) {
-        float fOffHeight = fContentCalculatedHeight - fOldChildHeight;
-        XFA_AttributeValue eVType =
-            pParaNode->JSObject()->GetEnum(XFA_Attribute::VAlign);
-        switch (eVType) {
-          case XFA_AttributeValue::Middle:
-            fOffHeight = fOffHeight / 2;
-            break;
-          case XFA_AttributeValue::Bottom:
-            break;
-          case XFA_AttributeValue::Top:
-          default:
-            fOffHeight = 0;
-            break;
-        }
-        if (fOffHeight > 0) {
-          for (CXFA_LayoutItem* pInnerIter = pLayoutChild->GetFirstChild();
-               pInnerIter; pInnerIter = pInnerIter->GetNextSibling()) {
-            CXFA_ContentLayoutItem* pInnerChild =
-                pInnerIter->AsContentLayoutItem();
-            if (!pInnerChild)
-              continue;
+      if (!pParaNode || !pLayoutChild->GetFirstChild())
+        continue;
 
-            pInnerChild->m_sPos.y += fOffHeight;
-          }
-        }
+      float fOffHeight = fContentCalculatedHeight - fOldChildHeight;
+      XFA_AttributeValue eVType =
+          pParaNode->JSObject()->GetEnum(XFA_Attribute::VAlign);
+      switch (eVType) {
+        case XFA_AttributeValue::Middle:
+          fOffHeight = fOffHeight / 2;
+          break;
+        case XFA_AttributeValue::Bottom:
+          break;
+        case XFA_AttributeValue::Top:
+        default:
+          fOffHeight = 0;
+          break;
+      }
+      if (fOffHeight <= 0)
+        continue;
+
+      for (CXFA_LayoutItem* pInnerIter = pLayoutChild->GetFirstChild();
+           pInnerIter; pInnerIter = pInnerIter->GetNextSibling()) {
+        CXFA_ContentLayoutItem* pInnerChild = pInnerIter->AsContentLayoutItem();
+        if (pInnerChild)
+          pInnerChild->m_sPos.y += fOffHeight;
       }
     }
   }
@@ -331,10 +337,10 @@
 
 XFA_AttributeValue GetLayout(CXFA_Node* pFormNode, bool* bRootForceTb) {
   *bRootForceTb = false;
-  Optional<XFA_AttributeValue> layoutMode =
+  absl::optional<XFA_AttributeValue> layoutMode =
       pFormNode->JSObject()->TryEnum(XFA_Attribute::Layout, false);
-  if (layoutMode)
-    return *layoutMode;
+  if (layoutMode.has_value())
+    return layoutMode.value();
 
   CXFA_Node* pParentNode = pFormNode->GetParent();
   if (pParentNode && pParentNode->GetElementType() == XFA_Element::Form) {
@@ -360,13 +366,11 @@
     if (!bPreFind)
       eKeepType = XFA_Attribute::Next;
 
-    Optional<XFA_AttributeValue> previous =
+    absl::optional<XFA_AttributeValue> previous =
         pKeep->JSObject()->TryEnum(eKeepType, false);
-    if (previous) {
-      if (*previous == XFA_AttributeValue::ContentArea ||
-          *previous == XFA_AttributeValue::PageArea) {
-        return true;
-      }
+    if (previous == XFA_AttributeValue::ContentArea ||
+        previous == XFA_AttributeValue::PageArea) {
+      return true;
     }
   }
 
@@ -378,55 +382,59 @@
   if (!bPreFind)
     eKeepType = XFA_Attribute::Previous;
 
-  Optional<XFA_AttributeValue> next =
+  absl::optional<XFA_AttributeValue> next =
       pKeep->JSObject()->TryEnum(eKeepType, false);
-  if (!next)
-    return false;
-  if (*next == XFA_AttributeValue::ContentArea ||
-      *next == XFA_AttributeValue::PageArea) {
+  if (next == XFA_AttributeValue::ContentArea ||
+      next == XFA_AttributeValue::PageArea) {
     return true;
   }
   return false;
 }
 
-Optional<CXFA_ContentLayoutProcessor::Stage> FindBreakNode(
+absl::optional<CXFA_ContentLayoutProcessor::Stage> FindBreakBeforeNode(
     CXFA_Node* pContainerNode,
-    bool bBreakBefore,
     CXFA_Node** pCurActionNode) {
   for (CXFA_Node* pBreakNode = pContainerNode; pBreakNode;
        pBreakNode = pBreakNode->GetNextSibling()) {
-    XFA_Attribute eAttributeType =
-        bBreakBefore ? XFA_Attribute::Before : XFA_Attribute::After;
-
     switch (pBreakNode->GetElementType()) {
-      case XFA_Element::BreakBefore: {
-        if (!bBreakBefore)
-          break;
-
+      case XFA_Element::BreakBefore:
         *pCurActionNode = pBreakNode;
         return CXFA_ContentLayoutProcessor::Stage::kBreakBefore;
-      }
-      case XFA_Element::BreakAfter: {
-        if (bBreakBefore)
-          break;
-
-        *pCurActionNode = pBreakNode;
-        return CXFA_ContentLayoutProcessor::Stage::kBreakAfter;
-      }
       case XFA_Element::Break:
-        if (pBreakNode->JSObject()->GetEnum(eAttributeType) ==
+        if (pBreakNode->JSObject()->GetEnum(XFA_Attribute::Before) ==
             XFA_AttributeValue::Auto) {
           break;
         }
-
         *pCurActionNode = pBreakNode;
-        return bBreakBefore ? CXFA_ContentLayoutProcessor::Stage::kBreakBefore
-                            : CXFA_ContentLayoutProcessor::Stage::kBreakAfter;
+        return CXFA_ContentLayoutProcessor::Stage::kBreakBefore;
       default:
         break;
     }
   }
-  return {};
+  return absl::nullopt;
+}
+
+absl::optional<CXFA_ContentLayoutProcessor::Stage> FindBreakAfterNode(
+    CXFA_Node* pContainerNode,
+    CXFA_Node** pCurActionNode) {
+  for (CXFA_Node* pBreakNode = pContainerNode; pBreakNode;
+       pBreakNode = pBreakNode->GetNextSibling()) {
+    switch (pBreakNode->GetElementType()) {
+      case XFA_Element::BreakAfter:
+        *pCurActionNode = pBreakNode;
+        return CXFA_ContentLayoutProcessor::Stage::kBreakAfter;
+      case XFA_Element::Break:
+        if (pBreakNode->JSObject()->GetEnum(XFA_Attribute::After) ==
+            XFA_AttributeValue::Auto) {
+          break;
+        }
+        *pCurActionNode = pBreakNode;
+        return CXFA_ContentLayoutProcessor::Stage::kBreakAfter;
+      default:
+        break;
+    }
+  }
+  return absl::nullopt;
 }
 
 void DeleteLayoutGeneratedNode(CXFA_Node* pGenerateNode) {
@@ -436,12 +444,12 @@
   CXFA_NodeIterator sIterator(pGenerateNode);
   for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
        pNode = sIterator.MoveToNext()) {
-    RetainPtr<CXFA_ContentLayoutItem> pCurLayoutItem(
-        ToContentLayoutItem(pNode->JSObject()->GetLayoutItem()));
+    CXFA_ContentLayoutItem* pCurLayoutItem =
+        ToContentLayoutItem(pNode->JSObject()->GetLayoutItem());
     while (pCurLayoutItem) {
       CXFA_ContentLayoutItem* pNextLayoutItem = pCurLayoutItem->GetNext();
-      pNotify->OnLayoutItemRemoving(pDocLayout, pCurLayoutItem.Get());
-      pCurLayoutItem.Reset(pNextLayoutItem);
+      pNotify->OnLayoutItemRemoving(pDocLayout, pCurLayoutItem);
+      pCurLayoutItem = pNextLayoutItem;
     }
   }
   pGenerateNode->GetParent()->RemoveChildAndNotify(pGenerateNode, true);
@@ -476,7 +484,8 @@
       bool bAnyChanged = false;
       CXFA_Document* pDocument = pFormNode->GetDocument();
       CXFA_FFNotify* pNotify = pDocument->GetNotify();
-      float fCurTopMargin = 0, fCurBottomMargin = 0;
+      float fCurTopMargin = 0;
+      float fCurBottomMargin = 0;
       CXFA_Margin* pMarginNode =
           pFormNode->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin);
       if (pMarginNode && bCalculateMargin) {
@@ -489,8 +498,8 @@
       while (bChanged) {
         bChanged = false;
         {
-          Optional<float> fRelSplitPos = pFormNode->FindSplitPos(
-              pNotify->GetHDOC()->GetDocView(), pLayoutItem->GetIndex(),
+          absl::optional<float> fRelSplitPos = pFormNode->FindSplitPos(
+              pNotify->GetFFDoc()->GetDocView(), pLayoutItem->GetIndex(),
               *fProposedSplitPos - fCurVerticalOffset);
           if (fRelSplitPos.has_value()) {
             bAnyChanged = true;
@@ -617,44 +626,62 @@
 }  // namespace
 
 CXFA_ContentLayoutProcessor::CXFA_ContentLayoutProcessor(
+    cppgc::Heap* pHeap,
     CXFA_Node* pNode,
     CXFA_ViewLayoutProcessor* pViewLayoutProcessor)
-    : m_pFormNode(pNode), m_pViewLayoutProcessor(pViewLayoutProcessor) {
-  ASSERT(GetFormNode());
-  ASSERT(GetFormNode()->IsContainerNode() ||
+    : m_pHeap(pHeap),
+      m_pFormNode(pNode),
+      m_pViewLayoutProcessor(pViewLayoutProcessor) {
+  DCHECK(GetFormNode());
+  DCHECK(GetFormNode()->IsContainerNode() ||
          GetFormNode()->GetElementType() == XFA_Element::Form);
-  m_pOldLayoutItem.Reset(
-      ToContentLayoutItem(GetFormNode()->JSObject()->GetLayoutItem()));
+  m_pOldLayoutItem =
+      ToContentLayoutItem(GetFormNode()->JSObject()->GetLayoutItem());
 }
 
-CXFA_ContentLayoutProcessor::~CXFA_ContentLayoutProcessor() {}
+CXFA_ContentLayoutProcessor::~CXFA_ContentLayoutProcessor() = default;
 
-RetainPtr<CXFA_ContentLayoutItem>
-CXFA_ContentLayoutProcessor::CreateContentLayoutItem(CXFA_Node* pFormNode) {
+void CXFA_ContentLayoutProcessor::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pFormNode);
+  visitor->Trace(m_pCurChildNode);
+  visitor->Trace(m_pKeepHeadNode);
+  visitor->Trace(m_pKeepTailNode);
+  visitor->Trace(m_pCurChildPreprocessor);
+  visitor->Trace(m_pLayoutItem);
+  visitor->Trace(m_pOldLayoutItem);
+  visitor->Trace(m_pViewLayoutProcessor);
+  ContainerTrace(visitor, m_ArrayKeepItems);
+  ContainerTrace(visitor, m_PendingNodes);
+  ContainerTrace(visitor, m_PendingNodesCount);
+}
+
+CXFA_ContentLayoutItem* CXFA_ContentLayoutProcessor::CreateContentLayoutItem(
+    CXFA_Node* pFormNode) {
   if (!pFormNode)
     return nullptr;
 
   if (m_pOldLayoutItem) {
-    RetainPtr<CXFA_ContentLayoutItem> pLayoutItem = m_pOldLayoutItem;
-    m_pOldLayoutItem.Reset(m_pOldLayoutItem->GetNext());
+    CXFA_ContentLayoutItem* pLayoutItem = m_pOldLayoutItem;
+    m_pOldLayoutItem = m_pOldLayoutItem->GetNext();
     return pLayoutItem;
   }
   CXFA_FFNotify* pNotify = pFormNode->GetDocument()->GetNotify();
-  auto pNewLayoutItem = pdfium::MakeRetain<CXFA_ContentLayoutItem>(
-      pFormNode, pNotify->OnCreateContentLayoutItem(pFormNode));
+  auto* pNewLayoutItem = cppgc::MakeGarbageCollected<CXFA_ContentLayoutItem>(
+      GetHeap()->GetAllocationHandle(), pFormNode,
+      pNotify->OnCreateContentLayoutItem(pFormNode));
 
   CXFA_ContentLayoutItem* pPrevLayoutItem =
       ToContentLayoutItem(pFormNode->JSObject()->GetLayoutItem());
   if (pPrevLayoutItem) {
-    pPrevLayoutItem->GetLast()->InsertAfter(pNewLayoutItem.Get());
+    pPrevLayoutItem->GetLast()->InsertAfter(pNewLayoutItem);
   } else {
-    pFormNode->JSObject()->SetLayoutItem(pNewLayoutItem.Get());
+    pFormNode->JSObject()->SetLayoutItem(pNewLayoutItem);
   }
   return pNewLayoutItem;
 }
 
 float CXFA_ContentLayoutProcessor::FindSplitPos(float fProposedSplitPos) {
-  ASSERT(m_pLayoutItem);
+  DCHECK(m_pLayoutItem);
   auto value = GetFormNode()->JSObject()->TryEnum(XFA_Attribute::Layout, true);
   XFA_AttributeValue eLayout = value.value_or(XFA_AttributeValue::Position);
   bool bCalculateMargin = eLayout != XFA_AttributeValue::Position;
@@ -690,7 +717,7 @@
         XFA_Attribute::BottomInset, XFA_Unit::Pt);
   }
 
-  RetainPtr<CXFA_ContentLayoutItem> pSecondLayoutItem;
+  CXFA_ContentLayoutItem* pSecondLayoutItem = nullptr;
   if (m_pCurChildPreprocessor &&
       m_pCurChildPreprocessor->GetFormNode() == pLayoutItem->GetFormNode()) {
     pSecondLayoutItem = m_pCurChildPreprocessor->CreateContentLayoutItem(
@@ -731,16 +758,16 @@
     bOrphanedItem = true;
   }
 
-  std::vector<RetainPtr<CXFA_ContentLayoutItem>> children;
+  std::vector<CXFA_ContentLayoutItem*> children;
   while (auto* pFirst = ToContentLayoutItem(pLayoutItem->GetFirstChild())) {
-    children.emplace_back(pFirst);
+    children.push_back(pFirst);
     pLayoutItem->RemoveChild(children.back());
   }
 
   float lHeightForKeep = 0;
   float fAddMarginHeight = 0;
-  std::vector<RetainPtr<CXFA_ContentLayoutItem>> keepLayoutItems;
-  for (auto& pChildItem : children) {
+  std::vector<CXFA_ContentLayoutItem*> keepLayoutItems;
+  for (CXFA_ContentLayoutItem* pChildItem : children) {
     if (fSplitPos <= fCurTopMargin + pChildItem->m_sPos.y + fCurBottomMargin +
                          kXFALayoutPrecision) {
       if (!ExistContainerKeep(pChildItem->GetFormNode(), true)) {
@@ -751,7 +778,7 @@
         continue;
       }
       if (lHeightForKeep < kXFALayoutPrecision) {
-        for (auto& pPreItem : keepLayoutItems) {
+        for (CXFA_ContentLayoutItem* pPreItem : keepLayoutItems) {
           pLayoutItem->RemoveChild(pPreItem);
           pPreItem->m_sPos.y -= fSplitPos;
           if (pPreItem->m_sPos.y < 0)
@@ -785,7 +812,7 @@
 
     float fOldHeight = pSecondLayoutItem->m_sSize.height;
     SplitLayoutItem(
-        pChildItem.Get(), pSecondLayoutItem.Get(),
+        pChildItem, pSecondLayoutItem,
         fSplitPos - fCurTopMargin - fCurBottomMargin - pChildItem->m_sPos.y);
     fAddMarginHeight = pSecondLayoutItem->m_sSize.height - fOldHeight;
     pLayoutItem->AppendLastChild(pChildItem);
@@ -795,15 +822,14 @@
 }
 
 void CXFA_ContentLayoutProcessor::SplitLayoutItem(float fSplitPos) {
-  ASSERT(m_pLayoutItem);
+  DCHECK(m_pLayoutItem);
   SplitLayoutItem(m_pLayoutItem.Get(), nullptr, fSplitPos);
 }
 
-RetainPtr<CXFA_ContentLayoutItem>
-CXFA_ContentLayoutProcessor::ExtractLayoutItem() {
-  RetainPtr<CXFA_ContentLayoutItem> pLayoutItem = m_pLayoutItem;
+CXFA_ContentLayoutItem* CXFA_ContentLayoutProcessor::ExtractLayoutItem() {
+  CXFA_ContentLayoutItem* pLayoutItem = m_pLayoutItem;
   if (pLayoutItem) {
-    m_pLayoutItem.Reset(ToContentLayoutItem(pLayoutItem->GetNextSibling()));
+    m_pLayoutItem = ToContentLayoutItem(pLayoutItem->GetNextSibling());
     pLayoutItem->RemoveSelfIfParented();
   }
   if (m_nCurChildNodeStage != Stage::kDone || !m_pOldLayoutItem)
@@ -815,105 +841,100 @@
       m_pOldLayoutItem->GetFormNode()->GetDocument());
 
   while (m_pOldLayoutItem) {
-    RetainPtr<CXFA_ContentLayoutItem> pToDeleteItem = m_pOldLayoutItem;
-    m_pOldLayoutItem.Reset(pToDeleteItem->GetNext());
+    CXFA_ContentLayoutItem* pToDeleteItem = m_pOldLayoutItem;
+    m_pOldLayoutItem = pToDeleteItem->GetNext();
     if (pToDeleteItem == pLayoutItem)
       break;
-    pNotify->OnLayoutItemRemoving(pDocLayout, pToDeleteItem.Get());
+    pNotify->OnLayoutItemRemoving(pDocLayout, pToDeleteItem);
     pToDeleteItem->RemoveSelfIfParented();
   }
   return pLayoutItem;
 }
 
-void CXFA_ContentLayoutProcessor::GotoNextContainerNodeSimple(
-    bool bUsePageBreak) {
-  m_nCurChildNodeStage = GotoNextContainerNode(
-      m_nCurChildNodeStage, bUsePageBreak, GetFormNode(), &m_pCurChildNode);
+void CXFA_ContentLayoutProcessor::GotoNextContainerNodeSimple() {
+  std::tie(m_nCurChildNodeStage, m_pCurChildNode) = GotoNextContainerNode(
+      m_nCurChildNodeStage, GetFormNode(), m_pCurChildNode);
 }
 
-CXFA_ContentLayoutProcessor::Stage
+std::pair<CXFA_ContentLayoutProcessor::Stage, CXFA_Node*>
 CXFA_ContentLayoutProcessor::GotoNextContainerNode(Stage nCurStage,
-                                                   bool bUsePageBreak,
                                                    CXFA_Node* pParentContainer,
-                                                   CXFA_Node** pCurActionNode) {
+                                                   CXFA_Node* pCurActionNode) {
   CXFA_Node* pChildContainer = nullptr;
   switch (nCurStage) {
     case Stage::kBreakBefore:
     case Stage::kBreakAfter: {
-      pChildContainer = (*pCurActionNode)->GetParent();
+      pChildContainer = pCurActionNode->GetParent();
       break;
     }
     case Stage::kKeep:
     case Stage::kContainer:
-      pChildContainer = *pCurActionNode;
+      pChildContainer = pCurActionNode;
       break;
     default:
       pChildContainer = nullptr;
       break;
   }
 
-  Optional<Stage> ret;
+  absl::optional<Stage> ret;
   switch (nCurStage) {
     case Stage::kKeep:
-      ret = HandleKeep(pChildContainer->GetFirstChild(), pCurActionNode);
+      ret = HandleKeep(pChildContainer->GetFirstChild(), &pCurActionNode);
       if (ret.has_value())
-        return ret.value();
+        return {ret.value(), pCurActionNode};
       break;
 
     case Stage::kNone:
-      *pCurActionNode = nullptr;
-      FALLTHROUGH;
+      pCurActionNode = nullptr;
+      [[fallthrough]];
 
     case Stage::kBookendLeader:
-      ret = HandleBookendLeader(pParentContainer, pCurActionNode);
+      ret = HandleBookendLeader(pParentContainer, &pCurActionNode);
       if (ret.has_value())
-        return ret.value();
-
-      *pCurActionNode = nullptr;
-      FALLTHROUGH;
+        return {ret.value(), pCurActionNode};
+      pCurActionNode = nullptr;
+      [[fallthrough]];
 
     case Stage::kBreakBefore:
-      ret = HandleBreakBefore(pChildContainer, pCurActionNode);
+      ret = HandleBreakBefore(pChildContainer, &pCurActionNode);
       if (ret.has_value())
-        return ret.value();
+        return {ret.value(), pCurActionNode};
       break;
 
     case Stage::kContainer:
-      *pCurActionNode = nullptr;
-      FALLTHROUGH;
+      pCurActionNode = nullptr;
+      [[fallthrough]];
 
     case Stage::kBreakAfter:
-      ret = HandleBreakAfter(pChildContainer, pCurActionNode);
+      ret = HandleBreakAfter(pChildContainer, &pCurActionNode);
       if (ret.has_value())
-        return ret.value();
+        return {ret.value(), pCurActionNode};
       break;
 
     case Stage::kBookendTrailer:
-      ret = HandleBookendTrailer(pParentContainer, pCurActionNode);
+      ret = HandleBookendTrailer(pParentContainer, &pCurActionNode);
       if (ret.has_value())
-        return ret.value();
-      FALLTHROUGH;
+        return {ret.value(), pCurActionNode};
+      [[fallthrough]];
 
     default:
-      *pCurActionNode = nullptr;
-      return Stage::kDone;
+      return {Stage::kDone, nullptr};
   }
 
   ret = HandleCheckNextChildContainer(pParentContainer, pChildContainer,
-                                      pCurActionNode);
+                                      &pCurActionNode);
   if (ret.has_value())
-    return ret.value();
+    return {ret.value(), pCurActionNode};
 
-  *pCurActionNode = nullptr;
-  ret = HandleBookendTrailer(pParentContainer, pCurActionNode);
+  pCurActionNode = nullptr;
+  ret = HandleBookendTrailer(pParentContainer, &pCurActionNode);
   if (ret.has_value())
-    return ret.value();
+    return {ret.value(), pCurActionNode};
 
-  *pCurActionNode = nullptr;
-  return Stage::kDone;
+  return {Stage::kDone, nullptr};
 }
 
-Optional<CXFA_ContentLayoutProcessor::Stage>
+absl::optional<CXFA_ContentLayoutProcessor::Stage>
 CXFA_ContentLayoutProcessor::ProcessKeepNodesForCheckNext(
     CXFA_Node** pCurActionNode,
     CXFA_Node** pNextContainer,
@@ -927,35 +948,35 @@
       m_pKeepHeadNode = *pNextContainer;
       m_bIsProcessKeep = true;
     }
-    return {};
+    return absl::nullopt;
   }
 
   if (!m_bIsProcessKeep || !m_pKeepHeadNode) {
     if (m_bKeepBreakFinish)
       *pLastKeepNode = true;
     m_bKeepBreakFinish = false;
-    return {};
+    return absl::nullopt;
   }
 
   m_pKeepTailNode = *pNextContainer;
   if (m_bKeepBreakFinish) {
     *pNextContainer = m_pKeepHeadNode;
     ProcessKeepNodesEnd();
-    return {};
+    return absl::nullopt;
   }
 
-  Optional<Stage> ret =
-      FindBreakNode((*pNextContainer)->GetFirstChild(), true, pCurActionNode);
+  absl::optional<Stage> ret =
+      FindBreakBeforeNode((*pNextContainer)->GetFirstChild(), pCurActionNode);
   if (!ret.has_value()) {
     *pNextContainer = m_pKeepHeadNode;
     ProcessKeepNodesEnd();
-    return {};
+    return absl::nullopt;
   }
 
   return ret;
 }
 
-Optional<CXFA_ContentLayoutProcessor::Stage>
+absl::optional<CXFA_ContentLayoutProcessor::Stage>
 CXFA_ContentLayoutProcessor::ProcessKeepNodesForBreakBefore(
     CXFA_Node** pCurActionNode,
     CXFA_Node* pContainerNode) {
@@ -966,7 +987,7 @@
   }
 
   CXFA_Node* pBreakAfterNode = pContainerNode->GetFirstChild();
-  return FindBreakNode(pBreakAfterNode, false, pCurActionNode);
+  return FindBreakAfterNode(pBreakAfterNode, pCurActionNode);
 }
 
 void CXFA_ContentLayoutProcessor::DoLayoutPageArea(
@@ -974,39 +995,41 @@
   CXFA_Node* pFormNode = pPageAreaLayoutItem->GetFormNode();
   CXFA_Node* pCurChildNode = nullptr;
   CXFA_LayoutItem* pBeforeItem = nullptr;
-  for (Stage nCurChildNodeStage = GotoNextContainerNode(
-           Stage::kNone, false, pFormNode, &pCurChildNode);
-       pCurChildNode;
-       nCurChildNodeStage = GotoNextContainerNode(nCurChildNodeStage, false,
-                                                  pFormNode, &pCurChildNode)) {
-    if (nCurChildNodeStage != Stage::kContainer)
-      continue;
-    if (pCurChildNode->GetElementType() == XFA_Element::Variables)
+  Stage nCurChildNodeStage = Stage::kNone;
+  while (true) {
+    std::tie(nCurChildNodeStage, pCurChildNode) =
+        GotoNextContainerNode(nCurChildNodeStage, pFormNode, pCurChildNode);
+    if (!pCurChildNode)
+      break;
+
+    if (nCurChildNodeStage != Stage::kContainer ||
+        pCurChildNode->GetElementType() == XFA_Element::Variables)
       continue;
 
-    auto pProcessor =
-        pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(pCurChildNode, nullptr);
+    auto* pProcessor = cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>(
+        GetHeap()->GetAllocationHandle(), GetHeap(), pCurChildNode, nullptr);
     pProcessor->DoLayout(false, FLT_MAX, FLT_MAX);
     if (!pProcessor->HasLayoutItem())
       continue;
 
     pProcessor->SetCurrentComponentPos(CalculatePositionedContainerPos(
         pCurChildNode, pProcessor->GetCurrentComponentSize()));
-    RetainPtr<CXFA_LayoutItem> pProcessItem = pProcessor->ExtractLayoutItem();
+
+    CXFA_LayoutItem* pProcessItem = pProcessor->ExtractLayoutItem();
     if (!pBeforeItem)
       pPageAreaLayoutItem->AppendFirstChild(pProcessItem);
     else
       pPageAreaLayoutItem->InsertAfter(pProcessItem, pBeforeItem);
 
-    pBeforeItem = pProcessItem.Get();
+    pBeforeItem = pProcessItem;
   }
 
   pBeforeItem = nullptr;
-  RetainPtr<CXFA_LayoutItem> pLayoutItem(pPageAreaLayoutItem->GetFirstChild());
+  CXFA_LayoutItem* pLayoutItem = pPageAreaLayoutItem->GetFirstChild();
   while (pLayoutItem) {
     if (!pLayoutItem->IsContentLayoutItem() ||
         pLayoutItem->GetFormNode()->GetElementType() != XFA_Element::Draw) {
-      pLayoutItem.Reset(pLayoutItem->GetNextSibling());
+      pLayoutItem = pLayoutItem->GetNextSibling();
       continue;
     }
     if (pLayoutItem->GetFormNode()->GetElementType() != XFA_Element::Draw)
@@ -1019,8 +1042,8 @@
     else
       pPageAreaLayoutItem->InsertAfter(pLayoutItem, pBeforeItem);
 
-    pBeforeItem = pLayoutItem.Get();
-    pLayoutItem.Reset(pNextLayoutItem);
+    pBeforeItem = pLayoutItem;
+    pLayoutItem = pNextLayoutItem;
   }
 }
 
@@ -1043,26 +1066,28 @@
   float fHiddenContentCalculatedWidth = 0;
   float fHiddenContentCalculatedHeight = 0;
   if (!m_pCurChildNode)
-    GotoNextContainerNodeSimple(false);
+    GotoNextContainerNodeSimple();
 
   int32_t iColIndex = 0;
-  for (; m_pCurChildNode; GotoNextContainerNodeSimple(false)) {
+  for (; m_pCurChildNode; GotoNextContainerNodeSimple()) {
     if (m_nCurChildNodeStage != Stage::kContainer)
       continue;
     if (m_pCurChildNode->GetElementType() == XFA_Element::Variables)
       continue;
 
-    auto pProcessor = pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(
-        m_pCurChildNode, m_pViewLayoutProcessor.Get());
+    auto* pProcessor = cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>(
+        GetHeap()->GetAllocationHandle(), GetHeap(), m_pCurChildNode,
+        m_pViewLayoutProcessor);
+
     if (pContext && pContext->m_prgSpecifiedColumnWidths) {
       int32_t iColSpan =
           m_pCurChildNode->JSObject()->GetInteger(XFA_Attribute::ColSpan);
-      if (iColSpan <= pdfium::CollectionSize<int32_t>(
+      if (iColSpan <= fxcrt::CollectionSize<int32_t>(
                           *pContext->m_prgSpecifiedColumnWidths) -
                           iColIndex) {
         pContext->m_fCurColumnWidth = 0.0f;
         if (iColSpan == -1) {
-          iColSpan = pdfium::CollectionSize<int32_t>(
+          iColSpan = fxcrt::CollectionSize<int32_t>(
               *pContext->m_prgSpecifiedColumnWidths);
         }
         for (int32_t i = 0; iColIndex + i < iColSpan; ++i) {
@@ -1137,7 +1162,7 @@
   if (!pLayoutNode)
     pLayoutNode = GetFormNode();
 
-  ASSERT(!m_pCurChildNode);
+  DCHECK(!m_pCurChildNode);
 
   m_pLayoutItem = CreateContentLayoutItem(GetFormNode());
   bool bContainerWidthAutoSize = true;
@@ -1174,21 +1199,23 @@
   }
 
   int32_t iSpecifiedColumnCount =
-      pdfium::CollectionSize<int32_t>(m_rgSpecifiedColumnWidths);
+      fxcrt::CollectionSize<int32_t>(m_rgSpecifiedColumnWidths);
   Context layoutContext;
   layoutContext.m_prgSpecifiedColumnWidths = &m_rgSpecifiedColumnWidths;
   Context* pLayoutContext =
       iSpecifiedColumnCount > 0 ? &layoutContext : nullptr;
   if (!m_pCurChildNode)
-    GotoNextContainerNodeSimple(false);
+    GotoNextContainerNodeSimple();
 
-  for (; m_pCurChildNode; GotoNextContainerNodeSimple(false)) {
+  for (; m_pCurChildNode; GotoNextContainerNodeSimple()) {
     layoutContext.m_fCurColumnWidth.reset();
     if (m_nCurChildNodeStage != Stage::kContainer)
       continue;
 
-    auto pProcessor = pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(
-        m_pCurChildNode, m_pViewLayoutProcessor.Get());
+    auto* pProcessor = cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>(
+        GetHeap()->GetAllocationHandle(), GetHeap(), m_pCurChildNode,
+        m_pViewLayoutProcessor);
+
     pProcessor->DoLayoutInternal(false, FLT_MAX, FLT_MAX, pLayoutContext);
     if (!pProcessor->HasLayoutItem())
       continue;
@@ -1231,7 +1258,7 @@
       }
     }
 
-    iRowCount = pdfium::CollectionSize<int32_t>(rgRowItems);
+    iRowCount = fxcrt::CollectionSize<int32_t>(rgRowItems);
     iColCount = 0;
     bool bMoreColumns = true;
     while (bMoreColumns) {
@@ -1263,9 +1290,8 @@
           continue;
 
         if (iColCount >= iSpecifiedColumnCount) {
-          int32_t c =
-              iColCount + 1 -
-              pdfium::CollectionSize<int32_t>(m_rgSpecifiedColumnWidths);
+          int32_t c = iColCount + 1 -
+                      fxcrt::CollectionSize<int32_t>(m_rgSpecifiedColumnWidths);
           for (int32_t j = 0; j < c; j++)
             m_rgSpecifiedColumnWidths.push_back(0);
         }
@@ -1281,7 +1307,7 @@
         continue;
 
       float fFinalColumnWidth = 0.0f;
-      if (pdfium::IndexInBounds(m_rgSpecifiedColumnWidths, iColCount))
+      if (fxcrt::IndexInBounds(m_rgSpecifiedColumnWidths, iColCount))
         fFinalColumnWidth = m_rgSpecifiedColumnWidths[iColCount];
 
       for (int32_t i = 0; i < iRowCount; ++i) {
@@ -1297,8 +1323,7 @@
   float fCurrentRowY = 0;
   for (CXFA_LayoutItem* pIter = m_pLayoutItem->GetFirstChild(); pIter;
        pIter = pIter->GetNextSibling()) {
-    RetainPtr<CXFA_ContentLayoutItem> pLayoutChild(
-        pIter->AsContentLayoutItem());
+    CXFA_ContentLayoutItem* pLayoutChild = pIter->AsContentLayoutItem();
     if (!pLayoutChild || !pLayoutChild->GetFormNode()->PresenceRequiresSpace())
       continue;
 
@@ -1377,10 +1402,9 @@
   }
 
   float fTotalHeight = 0;
-  for (auto iter = m_ArrayKeepItems.rbegin(); iter != m_ArrayKeepItems.rend();
-       iter++) {
-    AddLeaderAfterSplit(*iter);
-    fTotalHeight += (*iter)->m_sSize.height;
+  for (const auto& item : pdfium::base::Reversed(m_ArrayKeepItems)) {
+    AddLeaderAfterSplit(item);
+    fTotalHeight += item->m_sSize.height;
   }
   m_ArrayKeepItems.clear();
 
@@ -1390,7 +1414,7 @@
 bool CXFA_ContentLayoutProcessor::ProcessKeepForSplit(
     CXFA_ContentLayoutProcessor* pChildProcessor,
     Result eRetValue,
-    std::vector<RetainPtr<CXFA_ContentLayoutItem>>* rgCurLineLayoutItem,
+    std::vector<cppgc::Persistent<CXFA_ContentLayoutItem>>* rgCurLineLayoutItem,
     float* fContentCurRowAvailWidth,
     float* fContentCurRowHeight,
     float* fContentCurRowY,
@@ -1408,11 +1432,11 @@
     return false;
 
   CFX_SizeF childSize = pChildProcessor->GetCurrentComponentSize();
-  std::vector<RetainPtr<CXFA_ContentLayoutItem>> keepLayoutItems;
+  std::vector<CXFA_ContentLayoutItem*> keepLayoutItems;
   if (JudgePutNextPage(m_pLayoutItem.Get(), childSize.height,
                        &keepLayoutItems)) {
     m_ArrayKeepItems.clear();
-    for (auto& item : keepLayoutItems) {
+    for (CXFA_ContentLayoutItem* item : keepLayoutItems) {
       m_pLayoutItem->RemoveChild(item);
       *fContentCurRowY -= item->m_sSize.height;
       m_ArrayKeepItems.push_back(item);
@@ -1434,15 +1458,14 @@
 bool CXFA_ContentLayoutProcessor::JudgePutNextPage(
     CXFA_ContentLayoutItem* pParentLayoutItem,
     float fChildHeight,
-    std::vector<RetainPtr<CXFA_ContentLayoutItem>>* pKeepItems) {
+    std::vector<CXFA_ContentLayoutItem*>* pKeepItems) {
   if (!pParentLayoutItem)
     return false;
 
   float fItemsHeight = 0;
   for (CXFA_LayoutItem* pIter = pParentLayoutItem->GetFirstChild(); pIter;
        pIter = pIter->GetNextSibling()) {
-    RetainPtr<CXFA_ContentLayoutItem> pChildLayoutItem(
-        pIter->AsContentLayoutItem());
+    CXFA_ContentLayoutItem* pChildLayoutItem = pIter->AsContentLayoutItem();
     if (!pChildLayoutItem)
       continue;
 
@@ -1472,14 +1495,14 @@
         pNode->SetBindingNode(nullptr);
       }
     }
-    pNode->SetFlag(XFA_NodeFlag_UnusedNode);
+    pNode->SetFlag(XFA_NodeFlag::kUnusedNode);
   }
 }
 
 void CXFA_ContentLayoutProcessor::ProcessUnUseOverFlow(
     CXFA_Node* pLeaderNode,
     CXFA_Node* pTrailerNode,
-    const RetainPtr<CXFA_ContentLayoutItem>& pTrailerItem,
+    CXFA_ContentLayoutItem* pTrailerItem,
     CXFA_Node* pFormNode) {
   ProcessUnUseBinds(pLeaderNode);
   ProcessUnUseBinds(pTrailerNode);
@@ -1541,7 +1564,7 @@
 
   fContentCurRowY += InsertKeepLayoutItems();
   if (m_nCurChildNodeStage == Stage::kNone)
-    GotoNextContainerNodeSimple(true);
+    GotoNextContainerNodeSimple();
 
   fContentCurRowY += InsertPendingItems(GetFormNode());
   if (m_pCurChildPreprocessor && m_nCurChildNodeStage == Stage::kContainer) {
@@ -1559,7 +1582,8 @@
     float fContentCurRowHeight = 0;
     float fContentCurRowAvailWidth = fContentWidthLimit;
     m_fWidthLimit = fContentCurRowAvailWidth;
-    std::vector<RetainPtr<CXFA_ContentLayoutItem>> rgCurLineLayoutItems[3];
+    std::vector<cppgc::Persistent<CXFA_ContentLayoutItem>>
+        rgCurLineLayoutItems[3];
     uint8_t uCurHAlignState =
         (eFlowStrategy != XFA_AttributeValue::Rl_tb ? 0 : 2);
     if (pLastChild) {
@@ -1576,7 +1600,7 @@
             pLayoutNext->InsertAfter(
                 m_pCurChildPreprocessor->m_pLayoutItem.Get());
           }
-          m_pCurChildPreprocessor->m_pLayoutItem.Reset(pLayoutNext);
+          m_pCurChildPreprocessor->m_pLayoutItem = pLayoutNext;
           break;
         }
         uint8_t uHAlign =
@@ -1596,18 +1620,18 @@
         }
       }
 
-      RetainPtr<CXFA_ContentLayoutItem> pLayoutNextTemp(pLastChild);
+      CXFA_ContentLayoutItem* pLayoutNextTemp = pLastChild;
       while (pLayoutNextTemp) {
         CXFA_ContentLayoutItem* pSaveLayoutNext =
             ToContentLayoutItem(pLayoutNextTemp->GetNextSibling());
         pLayoutNextTemp->RemoveSelfIfParented();
-        pLayoutNextTemp.Reset(pSaveLayoutNext);
+        pLayoutNextTemp = pSaveLayoutNext;
       }
       pLastChild = nullptr;
     }
 
     while (m_pCurChildNode) {
-      std::unique_ptr<CXFA_ContentLayoutProcessor> pProcessor;
+      CXFA_ContentLayoutProcessor* pProcessor = nullptr;
       bool bAddedItemInRow = false;
       fContentCurRowY += InsertPendingItems(GetFormNode());
       switch (m_nCurChildNodeStage) {
@@ -1623,7 +1647,7 @@
           if (!bUseBreakControl || !m_pViewLayoutProcessor)
             break;
 
-          Optional<CXFA_ViewLayoutProcessor::BreakData> break_data =
+          absl::optional<CXFA_ViewLayoutProcessor::BreakData> break_data =
               m_pViewLayoutProcessor->ProcessBreakBefore(m_pCurChildNode);
           if (!break_data.has_value() || !break_data.value().bCreatePage ||
               GetFormNode()->GetElementType() == XFA_Element::Form) {
@@ -1641,11 +1665,13 @@
                 !m_pLayoutItem) {
               AddPendingNode(pTrailerNode, true);
             } else {
-              auto pTempProcessor =
-                  pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(pTrailerNode,
-                                                                  nullptr);
+              auto* pTempProcessor =
+                  cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>(
+                      GetHeap()->GetAllocationHandle(), GetHeap(), pTrailerNode,
+                      nullptr);
+
               InsertFlowedItem(
-                  pTempProcessor.get(), bContainerWidthAutoSize,
+                  pTempProcessor, bContainerWidthAutoSize,
                   bContainerHeightAutoSize, container_size.height,
                   eFlowStrategy, &uCurHAlignState, rgCurLineLayoutItems, false,
                   FLT_MAX, FLT_MAX, fContentWidthLimit, &fContentCurRowY,
@@ -1653,7 +1679,7 @@
                   &bAddedItemInRow, &bForceEndPage, pContext, false);
             }
           }
-          GotoNextContainerNodeSimple(true);
+          GotoNextContainerNodeSimple();
           bForceEndPage = true;
           bIsManualBreak = true;
           goto SuspendAndCreateNewRow;
@@ -1662,7 +1688,7 @@
           if (!bUseBreakControl || !m_pViewLayoutProcessor)
             break;
 
-          Optional<CXFA_ViewLayoutProcessor::BreakData> break_data =
+          absl::optional<CXFA_ViewLayoutProcessor::BreakData> break_data =
               m_pViewLayoutProcessor->ProcessBreakAfter(m_pCurChildNode);
           if (!break_data.has_value() ||
               GetFormNode()->GetElementType() == XFA_Element::Form) {
@@ -1673,10 +1699,12 @@
           CXFA_Node* pTrailerNode = break_data.value().pTrailer;
           bool bCreatePage = break_data.value().bCreatePage;
           if (JudgeLeaderOrTrailerForOccur(pTrailerNode)) {
-            auto pTempProcessor =
-                pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(pTrailerNode,
-                                                                nullptr);
-            InsertFlowedItem(pTempProcessor.get(), bContainerWidthAutoSize,
+            auto* pTempProcessor =
+                cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>(
+                    GetHeap()->GetAllocationHandle(), GetHeap(), pTrailerNode,
+                    nullptr);
+
+            InsertFlowedItem(pTempProcessor, bContainerWidthAutoSize,
                              bContainerHeightAutoSize, container_size.height,
                              eFlowStrategy, &uCurHAlignState,
                              rgCurLineLayoutItems, false, FLT_MAX, FLT_MAX,
@@ -1692,11 +1720,12 @@
                   &calculated_size.height, &fContentCurRowY,
                   fContentCurRowHeight, fContentWidthLimit, false);
               rgCurLineLayoutItems->clear();
-              auto pTempProcessor =
-                  pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(pLeaderNode,
-                                                                  nullptr);
+              auto* pTempProcessor =
+                  cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>(
+                      GetHeap()->GetAllocationHandle(), GetHeap(), pLeaderNode,
+                      nullptr);
               InsertFlowedItem(
-                  pTempProcessor.get(), bContainerWidthAutoSize,
+                  pTempProcessor, bContainerWidthAutoSize,
                   bContainerHeightAutoSize, container_size.height,
                   eFlowStrategy, &uCurHAlignState, rgCurLineLayoutItems, false,
                   FLT_MAX, FLT_MAX, fContentWidthLimit, &fContentCurRowY,
@@ -1708,7 +1737,7 @@
               AddPendingNode(pLeaderNode, true);
           }
 
-          GotoNextContainerNodeSimple(true);
+          GotoNextContainerNodeSimple();
           if (bCreatePage) {
             bForceEndPage = true;
             bIsManualBreak = true;
@@ -1719,19 +1748,22 @@
         }
         case Stage::kBookendLeader: {
           if (m_pCurChildPreprocessor) {
-            pProcessor = std::move(m_pCurChildPreprocessor);
+            pProcessor = m_pCurChildPreprocessor.Get();
+            m_pCurChildPreprocessor = nullptr;
           } else if (m_pViewLayoutProcessor) {
             CXFA_Node* pLeaderNode =
                 m_pViewLayoutProcessor->ProcessBookendLeader(m_pCurChildNode);
             if (pLeaderNode) {
-              pProcessor = pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(
-                  pLeaderNode, m_pViewLayoutProcessor.Get());
+              pProcessor =
+                  cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>(
+                      GetHeap()->GetAllocationHandle(), GetHeap(), pLeaderNode,
+                      m_pViewLayoutProcessor);
             }
           }
 
           if (pProcessor) {
             if (InsertFlowedItem(
-                    pProcessor.get(), bContainerWidthAutoSize,
+                    pProcessor, bContainerWidthAutoSize,
                     bContainerHeightAutoSize, container_size.height,
                     eFlowStrategy, &uCurHAlignState, rgCurLineLayoutItems,
                     bUseBreakControl, fAvailHeight, fRealHeight,
@@ -1741,24 +1773,27 @@
                     false) != Result::kDone) {
               goto SuspendAndCreateNewRow;
             }
-            pProcessor.reset();
+            pProcessor = nullptr;
           }
           break;
         }
         case Stage::kBookendTrailer: {
           if (m_pCurChildPreprocessor) {
-            pProcessor = std::move(m_pCurChildPreprocessor);
+            pProcessor = m_pCurChildPreprocessor;
+            m_pCurChildPreprocessor.Clear();
           } else if (m_pViewLayoutProcessor) {
             CXFA_Node* pTrailerNode =
                 m_pViewLayoutProcessor->ProcessBookendTrailer(m_pCurChildNode);
             if (pTrailerNode) {
-              pProcessor = pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(
-                  pTrailerNode, m_pViewLayoutProcessor.Get());
+              pProcessor =
+                  cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>(
+                      GetHeap()->GetAllocationHandle(), GetHeap(), pTrailerNode,
+                      m_pViewLayoutProcessor);
             }
           }
           if (pProcessor) {
             if (InsertFlowedItem(
-                    pProcessor.get(), bContainerWidthAutoSize,
+                    pProcessor, bContainerWidthAutoSize,
                     bContainerHeightAutoSize, container_size.height,
                     eFlowStrategy, &uCurHAlignState, rgCurLineLayoutItems,
                     bUseBreakControl, fAvailHeight, fRealHeight,
@@ -1768,12 +1803,12 @@
                     false) != Result::kDone) {
               goto SuspendAndCreateNewRow;
             }
-            pProcessor.reset();
+            pProcessor = nullptr;
           }
           break;
         }
         case Stage::kContainer: {
-          ASSERT(m_pCurChildNode->IsContainerNode());
+          DCHECK(m_pCurChildNode->IsContainerNode());
           if (m_pCurChildNode->GetElementType() == XFA_Element::Variables)
             break;
           if (fContentCurRowY >= fHeightLimit + kXFALayoutPrecision &&
@@ -1786,51 +1821,53 @@
 
           bool bNewRow = false;
           if (m_pCurChildPreprocessor) {
-            pProcessor = std::move(m_pCurChildPreprocessor);
+            pProcessor = m_pCurChildPreprocessor;
+            m_pCurChildPreprocessor.Clear();
             bNewRow = true;
           } else {
-            pProcessor = pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(
-                m_pCurChildNode, m_pViewLayoutProcessor.Get());
+            pProcessor =
+                cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>(
+                    GetHeap()->GetAllocationHandle(), GetHeap(),
+                    m_pCurChildNode, m_pViewLayoutProcessor);
           }
 
           pProcessor->InsertPendingItems(m_pCurChildNode);
           Result rs = InsertFlowedItem(
-              pProcessor.get(), bContainerWidthAutoSize,
-              bContainerHeightAutoSize, container_size.height, eFlowStrategy,
-              &uCurHAlignState, rgCurLineLayoutItems, bUseBreakControl,
-              fAvailHeight, fRealHeight, fContentWidthLimit, &fContentCurRowY,
-              &fContentCurRowAvailWidth, &fContentCurRowHeight,
-              &bAddedItemInRow, &bForceEndPage, pContext, bNewRow);
+              pProcessor, bContainerWidthAutoSize, bContainerHeightAutoSize,
+              container_size.height, eFlowStrategy, &uCurHAlignState,
+              rgCurLineLayoutItems, bUseBreakControl, fAvailHeight, fRealHeight,
+              fContentWidthLimit, &fContentCurRowY, &fContentCurRowAvailWidth,
+              &fContentCurRowHeight, &bAddedItemInRow, &bForceEndPage, pContext,
+              bNewRow);
           switch (rs) {
             case Result::kManualBreak:
               bIsManualBreak = true;
-              FALLTHROUGH;
+              [[fallthrough]];
             case Result::kPageFullBreak:
               bForceEndPage = true;
-              FALLTHROUGH;
+              [[fallthrough]];
             case Result::kRowFullBreak:
               goto SuspendAndCreateNewRow;
             case Result::kDone:
-            default:
               fContentCurRowY +=
                   pProcessor->InsertPendingItems(m_pCurChildNode);
-              pProcessor.reset();
+              pProcessor = nullptr;
               break;
           }
           break;
         }
         case Stage::kDone:
           break;
-        default:
-          break;
       }
-      GotoNextContainerNodeSimple(true);
+      GotoNextContainerNodeSimple();
       if (bAddedItemInRow && eFlowStrategy == XFA_AttributeValue::Tb)
         break;
       continue;
     SuspendAndCreateNewRow:
-      if (pProcessor)
-        m_pCurChildPreprocessor = std::move(pProcessor);
+      if (pProcessor) {
+        m_pCurChildPreprocessor = pProcessor;
+        pProcessor = nullptr;
+      }
       break;
     }
 
@@ -1872,7 +1909,8 @@
 }
 
 bool CXFA_ContentLayoutProcessor::CalculateRowChildPosition(
-    std::vector<RetainPtr<CXFA_ContentLayoutItem>> (&rgCurLineLayoutItems)[3],
+    std::vector<cppgc::Persistent<CXFA_ContentLayoutItem>> (
+        &rgCurLineLayoutItems)[3],
     XFA_AttributeValue eFlowStrategy,
     bool bContainerHeightAutoSize,
     bool bContainerWidthAutoSize,
@@ -1886,7 +1924,7 @@
   float fGroupWidths[3] = {0, 0, 0};
   int32_t nTotalLength = 0;
   for (int32_t i = 0; i < 3; i++) {
-    nGroupLengths[i] = pdfium::CollectionSize<int32_t>(rgCurLineLayoutItems[i]);
+    nGroupLengths[i] = fxcrt::CollectionSize<int32_t>(rgCurLineLayoutItems[i]);
     for (int32_t c = nGroupLengths[i], j = 0; j < c; j++) {
       nTotalLength++;
       if (rgCurLineLayoutItems[i][j]->GetFormNode()->PresenceRequiresSpace())
@@ -2022,7 +2060,7 @@
   if (m_pLayoutItem)
     return;
 
-  ASSERT(!m_pCurChildNode);
+  DCHECK(!m_pCurChildNode);
   m_pLayoutItem = CreateContentLayoutItem(GetFormNode());
   if (!m_pLayoutItem)
     return;
@@ -2133,7 +2171,7 @@
 }
 
 void CXFA_ContentLayoutProcessor::UpdatePendingItemLayout(
-    const RetainPtr<CXFA_ContentLayoutItem>& pLayoutItem) {
+    CXFA_ContentLayoutItem* pLayoutItem) {
   XFA_AttributeValue eLayout =
       pLayoutItem->GetFormNode()->JSObject()->GetEnum(XFA_Attribute::Layout);
   switch (eLayout) {
@@ -2148,7 +2186,7 @@
 
 void CXFA_ContentLayoutProcessor::AddTrailerBeforeSplit(
     float fSplitPos,
-    const RetainPtr<CXFA_ContentLayoutItem>& pTrailerLayoutItem,
+    CXFA_ContentLayoutItem* pTrailerLayoutItem,
     bool bUseInherited) {
   if (!pTrailerLayoutItem)
     return;
@@ -2167,7 +2205,7 @@
   CXFA_Margin* pMargin =
       GetFormNode()->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin);
   CFX_FloatRect inset = GetMarginInset(pMargin);
-  if (!IsAddNewRowForTrailer(pTrailerLayoutItem.Get())) {
+  if (!IsAddNewRowForTrailer(pTrailerLayoutItem)) {
     pTrailerLayoutItem->m_sPos.y = m_fLastRowY;
     pTrailerLayoutItem->m_sPos.x = m_fLastRowWidth;
     m_pLayoutItem->m_sSize.width += pTrailerLayoutItem->m_sSize.width;
@@ -2209,7 +2247,7 @@
 }
 
 void CXFA_ContentLayoutProcessor::AddLeaderAfterSplit(
-    const RetainPtr<CXFA_ContentLayoutItem>& pLeaderLayoutItem) {
+    CXFA_ContentLayoutItem* pLeaderLayoutItem) {
   UpdatePendingItemLayout(pLeaderLayoutItem);
 
   CXFA_Margin* pMarginNode =
@@ -2273,11 +2311,13 @@
   }
 
   while (!m_PendingNodes.empty()) {
-    auto pPendingProcessor = pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(
-        m_PendingNodes.front(), nullptr);
+    auto* pPendingProcessor =
+        cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>(
+            GetHeap()->GetAllocationHandle(), GetHeap(), m_PendingNodes.front(),
+            nullptr);
     m_PendingNodes.pop_front();
     pPendingProcessor->DoLayout(false, FLT_MAX, FLT_MAX);
-    RetainPtr<CXFA_ContentLayoutItem> pPendingLayoutItem;
+    CXFA_ContentLayoutItem* pPendingLayoutItem = nullptr;
     if (pPendingProcessor->HasLayoutItem())
       pPendingLayoutItem = pPendingProcessor->ExtractLayoutItem();
     if (pPendingLayoutItem) {
@@ -2297,7 +2337,8 @@
     float fContainerHeight,
     XFA_AttributeValue eFlowStrategy,
     uint8_t* uCurHAlignState,
-    std::vector<RetainPtr<CXFA_ContentLayoutItem>> (&rgCurLineLayoutItems)[3],
+    std::vector<cppgc::Persistent<CXFA_ContentLayoutItem>> (
+        &rgCurLineLayoutItems)[3],
     bool bUseBreakControl,
     float fAvailHeight,
     float fRealHeight,
@@ -2380,25 +2421,26 @@
   CXFA_Node* pOverflowLeaderNode = nullptr;
   CXFA_Node* pOverflowTrailerNode = nullptr;
   CXFA_Node* pFormNode = nullptr;
-  RetainPtr<CXFA_ContentLayoutItem> pTrailerLayoutItem;
+  CXFA_ContentLayoutItem* pTrailerLayoutItem = nullptr;
   bool bIsAddTrailerHeight = false;
   if (m_pViewLayoutProcessor &&
       pProcessor->GetFormNode()->GetIntact() == XFA_AttributeValue::None) {
     pFormNode =
         m_pViewLayoutProcessor->QueryOverflow(pProcessor->GetFormNode());
     if (!pFormNode && pLayoutContext && pLayoutContext->m_pOverflowProcessor) {
-      pFormNode = pLayoutContext->m_pOverflowNode.Get();
+      pFormNode = pLayoutContext->m_pOverflowNode;
       bUseInherited = true;
     }
-    Optional<CXFA_ViewLayoutProcessor::OverflowData> overflow_data =
+    absl::optional<CXFA_ViewLayoutProcessor::OverflowData> overflow_data =
         m_pViewLayoutProcessor->ProcessOverflow(pFormNode, false);
     if (overflow_data.has_value()) {
       pOverflowLeaderNode = overflow_data.value().pLeader;
       pOverflowTrailerNode = overflow_data.value().pTrailer;
       if (pProcessor->JudgeLeaderOrTrailerForOccur(pOverflowTrailerNode)) {
         if (pOverflowTrailerNode) {
-          auto pOverflowLeaderProcessor =
-              pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(
+          auto* pOverflowLeaderProcessor =
+              cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>(
+                  GetHeap()->GetAllocationHandle(), GetHeap(),
                   pOverflowTrailerNode, nullptr);
           pOverflowLeaderProcessor->DoLayout(false, FLT_MAX, FLT_MAX);
           pTrailerLayoutItem =
@@ -2409,8 +2451,8 @@
 
         bIsAddTrailerHeight =
             bUseInherited
-                ? IsAddNewRowForTrailer(pTrailerLayoutItem.Get())
-                : pProcessor->IsAddNewRowForTrailer(pTrailerLayoutItem.Get());
+                ? IsAddNewRowForTrailer(pTrailerLayoutItem)
+                : pProcessor->IsAddNewRowForTrailer(pTrailerLayoutItem);
         if (bIsAddTrailerHeight) {
           childSize.height += pTrailerLayoutItem->m_sSize.height;
           bIsAddTrailerHeight = true;
@@ -2442,7 +2484,7 @@
                                          pTrailerLayoutItem, pFormNode);
       }
 
-      RetainPtr<CXFA_ContentLayoutItem> pChildLayoutItem =
+      CXFA_ContentLayoutItem* pChildLayoutItem =
           pProcessor->ExtractLayoutItem();
       if (ExistContainerKeep(pProcessor->GetFormNode(), false) &&
           pProcessor->GetFormNode()->GetIntact() == XFA_AttributeValue::None) {
@@ -2591,7 +2633,7 @@
   if (pProcessor->GetFormNode()->GetIntact() == XFA_AttributeValue::None &&
       eLayout == XFA_AttributeValue::Tb) {
     if (m_pViewLayoutProcessor) {
-      Optional<CXFA_ViewLayoutProcessor::OverflowData> overflow_data =
+      absl::optional<CXFA_ViewLayoutProcessor::OverflowData> overflow_data =
           m_pViewLayoutProcessor->ProcessOverflow(pFormNode, true);
       if (overflow_data.has_value()) {
         pOverflowLeaderNode = overflow_data.value().pLeader;
@@ -2612,7 +2654,7 @@
   if (!pFormNode && pLayoutContext)
     pFormNode = pLayoutContext->m_pOverflowProcessor->GetFormNode();
   if (m_pViewLayoutProcessor) {
-    Optional<CXFA_ViewLayoutProcessor::OverflowData> overflow_data =
+    absl::optional<CXFA_ViewLayoutProcessor::OverflowData> overflow_data =
         m_pViewLayoutProcessor->ProcessOverflow(pFormNode, true);
     if (overflow_data.has_value()) {
       pOverflowLeaderNode = overflow_data.value().pLeader;
@@ -2627,15 +2669,15 @@
   return Result::kPageFullBreak;
 }
 
-Optional<CXFA_ContentLayoutProcessor::Stage>
+absl::optional<CXFA_ContentLayoutProcessor::Stage>
 CXFA_ContentLayoutProcessor::HandleKeep(CXFA_Node* pBreakAfterNode,
                                         CXFA_Node** pCurActionNode) {
   if (m_bKeepBreakFinish)
-    return {};
-  return FindBreakNode(pBreakAfterNode, false, pCurActionNode);
+    return absl::nullopt;
+  return FindBreakAfterNode(pBreakAfterNode, pCurActionNode);
 }
 
-Optional<CXFA_ContentLayoutProcessor::Stage>
+absl::optional<CXFA_ContentLayoutProcessor::Stage>
 CXFA_ContentLayoutProcessor::HandleBookendLeader(CXFA_Node* pParentContainer,
                                                  CXFA_Node** pCurActionNode) {
   for (CXFA_Node* pBookendNode = *pCurActionNode
@@ -2651,18 +2693,19 @@
         break;
     }
   }
-  return {};
+  return absl::nullopt;
 }
 
-Optional<CXFA_ContentLayoutProcessor::Stage>
+absl::optional<CXFA_ContentLayoutProcessor::Stage>
 CXFA_ContentLayoutProcessor::HandleBreakBefore(CXFA_Node* pChildContainer,
                                                CXFA_Node** pCurActionNode) {
   if (!*pCurActionNode)
-    return {};
+    return absl::nullopt;
 
   CXFA_Node* pBreakBeforeNode = (*pCurActionNode)->GetNextSibling();
   if (!m_bKeepBreakFinish) {
-    Optional<Stage> ret = FindBreakNode(pBreakBeforeNode, true, pCurActionNode);
+    absl::optional<Stage> ret =
+        FindBreakBeforeNode(pBreakBeforeNode, pCurActionNode);
     if (ret.has_value())
       return ret.value();
   }
@@ -2673,19 +2716,19 @@
   return Stage::kContainer;
 }
 
-Optional<CXFA_ContentLayoutProcessor::Stage>
+absl::optional<CXFA_ContentLayoutProcessor::Stage>
 CXFA_ContentLayoutProcessor::HandleBreakAfter(CXFA_Node* pChildContainer,
                                               CXFA_Node** pCurActionNode) {
   if (*pCurActionNode) {
     CXFA_Node* pBreakAfterNode = (*pCurActionNode)->GetNextSibling();
-    return FindBreakNode(pBreakAfterNode, false, pCurActionNode);
+    return FindBreakAfterNode(pBreakAfterNode, pCurActionNode);
   }
 
   CXFA_Node* pBreakAfterNode = pChildContainer->GetFirstChild();
   return HandleKeep(pBreakAfterNode, pCurActionNode);
 }
 
-Optional<CXFA_ContentLayoutProcessor::Stage>
+absl::optional<CXFA_ContentLayoutProcessor::Stage>
 CXFA_ContentLayoutProcessor::HandleCheckNextChildContainer(
     CXFA_Node* pParentContainer,
     CXFA_Node* pChildContainer,
@@ -2700,17 +2743,17 @@
       DeleteLayoutGeneratedNode(pSaveNode);
   }
   if (!pNextChildContainer)
-    return {};
+    return absl::nullopt;
 
   bool bLastKeep = false;
-  Optional<Stage> ret = ProcessKeepNodesForCheckNext(
+  absl::optional<Stage> ret = ProcessKeepNodesForCheckNext(
       pCurActionNode, &pNextChildContainer, &bLastKeep);
   if (ret.has_value())
     return ret.value();
 
   if (!m_bKeepBreakFinish && !bLastKeep) {
-    ret = FindBreakNode(pNextChildContainer->GetFirstChild(), true,
-                        pCurActionNode);
+    ret = FindBreakBeforeNode(pNextChildContainer->GetFirstChild(),
+                              pCurActionNode);
     if (ret.has_value())
       return ret.value();
   }
@@ -2718,7 +2761,7 @@
   return m_bIsProcessKeep ? Stage::kKeep : Stage::kContainer;
 }
 
-Optional<CXFA_ContentLayoutProcessor::Stage>
+absl::optional<CXFA_ContentLayoutProcessor::Stage>
 CXFA_ContentLayoutProcessor::HandleBookendTrailer(CXFA_Node* pParentContainer,
                                                   CXFA_Node** pCurActionNode) {
   for (CXFA_Node* pBookendNode = *pCurActionNode
@@ -2734,7 +2777,7 @@
         break;
     }
   }
-  return {};
+  return absl::nullopt;
 }
 
 void CXFA_ContentLayoutProcessor::ProcessKeepNodesEnd() {
diff --git a/xfa/fxfa/layout/cxfa_contentlayoutprocessor.h b/xfa/fxfa/layout/cxfa_contentlayoutprocessor.h
index c4cfbe7..fac5673 100644
--- a/xfa/fxfa/layout/cxfa_contentlayoutprocessor.h
+++ b/xfa/fxfa/layout/cxfa_contentlayoutprocessor.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,25 +11,28 @@
 
 #include <list>
 #include <map>
-#include <memory>
+#include <utility>
 #include <vector>
 
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/optional.h"
+#include "fxjs/gc/heap.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/macros.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/persistent.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
 constexpr float kXFALayoutPrecision = 0.0005f;
 
 class CXFA_ContentLayoutItem;
-class CXFA_ContentLayoutProcessor;
-class CXFA_LayoutProcessor;
 class CXFA_Node;
 class CXFA_ViewLayoutItem;
 class CXFA_ViewLayoutProcessor;
 
-class CXFA_ContentLayoutProcessor {
+class CXFA_ContentLayoutProcessor
+    : public cppgc::GarbageCollected<CXFA_ContentLayoutProcessor> {
  public:
   enum class Result : uint8_t {
     kDone,
@@ -49,28 +52,36 @@
     kDone,
   };
 
-  CXFA_ContentLayoutProcessor(CXFA_Node* pNode,
-                              CXFA_ViewLayoutProcessor* pViewLayoutProcessor);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ContentLayoutProcessor();
 
+  void Trace(cppgc::Visitor* visitor) const;
+  cppgc::Heap* GetHeap() const { return m_pHeap; }
+
   Result DoLayout(bool bUseBreakControl, float fHeightLimit, float fRealHeight);
   void DoLayoutPageArea(CXFA_ViewLayoutItem* pPageAreaLayoutItem);
 
   CXFA_Node* GetFormNode() { return m_pFormNode; }
-  RetainPtr<CXFA_ContentLayoutItem> ExtractLayoutItem();
+  CXFA_ContentLayoutItem* ExtractLayoutItem();
 
  private:
   class Context {
+    CPPGC_STACK_ALLOCATED();  // Allows Raw/Unowned pointers.
+
    public:
     Context();
     ~Context();
 
-    Optional<float> m_fCurColumnWidth;
+    absl::optional<float> m_fCurColumnWidth;
     UnownedPtr<std::vector<float>> m_prgSpecifiedColumnWidths;
-    UnownedPtr<CXFA_ContentLayoutProcessor> m_pOverflowProcessor;
-    UnownedPtr<CXFA_Node> m_pOverflowNode;
+    UnownedPtr<CXFA_ContentLayoutProcessor> m_pOverflowProcessor;  // OK, stack
+    UnownedPtr<CXFA_Node> m_pOverflowNode;                         // Ok, stack
   };
 
+  CXFA_ContentLayoutProcessor(cppgc::Heap* pHeap,
+                              CXFA_Node* pNode,
+                              CXFA_ViewLayoutProcessor* pViewLayoutProcessor);
+
   Result DoLayoutInternal(bool bUseBreakControl,
                           float fHeightLimit,
                           float fRealHeight,
@@ -83,23 +94,23 @@
   bool ProcessKeepForSplit(
       CXFA_ContentLayoutProcessor* pChildProcessor,
       Result eRetValue,
-      std::vector<RetainPtr<CXFA_ContentLayoutItem>>* rgCurLineLayoutItem,
+      std::vector<cppgc::Persistent<CXFA_ContentLayoutItem>>*
+          rgCurLineLayoutItem,
       float* fContentCurRowAvailWidth,
       float* fContentCurRowHeight,
       float* fContentCurRowY,
       bool* bAddedItemInRow,
       bool* bForceEndPage,
       Result* result);
-  void ProcessUnUseOverFlow(
-      CXFA_Node* pLeaderNode,
-      CXFA_Node* pTrailerNode,
-      const RetainPtr<CXFA_ContentLayoutItem>& pTrailerItem,
-      CXFA_Node* pFormNode);
+  void ProcessUnUseOverFlow(CXFA_Node* pLeaderNode,
+                            CXFA_Node* pTrailerNode,
+                            CXFA_ContentLayoutItem* pTrailerItem,
+                            CXFA_Node* pFormNode);
   bool IsAddNewRowForTrailer(CXFA_ContentLayoutItem* pTrailerItem);
   bool JudgeLeaderOrTrailerForOccur(CXFA_Node* pFormNode);
 
-  RetainPtr<CXFA_ContentLayoutItem> CreateContentLayoutItem(
-      CXFA_Node* pFormNode);
+  // Object comes from GCed heap.
+  CXFA_ContentLayoutItem* CreateContentLayoutItem(CXFA_Node* pFormNode);
 
   void SetCurrentComponentPos(const CFX_PointF& pos);
   void SetCurrentComponentSize(const CFX_SizeF& size);
@@ -109,7 +120,8 @@
                        float fSplitPos);
   float InsertKeepLayoutItems();
   bool CalculateRowChildPosition(
-      std::vector<RetainPtr<CXFA_ContentLayoutItem>> (&rgCurLineLayoutItems)[3],
+      std::vector<cppgc::Persistent<CXFA_ContentLayoutItem>> (
+          &rgCurLineLayoutItems)[3],
       XFA_AttributeValue eFlowStrategy,
       bool bContainerHeightAutoSize,
       bool bContainerWidthAutoSize,
@@ -120,10 +132,9 @@
       float fContentWidthLimit,
       bool bRootForceTb);
   void ProcessUnUseBinds(CXFA_Node* pFormNode);
-  bool JudgePutNextPage(
-      CXFA_ContentLayoutItem* pParentLayoutItem,
-      float fChildHeight,
-      std::vector<RetainPtr<CXFA_ContentLayoutItem>>* pKeepItems);
+  bool JudgePutNextPage(CXFA_ContentLayoutItem* pParentLayoutItem,
+                        float fChildHeight,
+                        std::vector<CXFA_ContentLayoutItem*>* pKeepItems);
 
   void DoLayoutPositionedContainer(Context* pContext);
   void DoLayoutTableContainer(CXFA_Node* pLayoutNode);
@@ -135,29 +146,29 @@
                                  bool bRootForceTb);
   void DoLayoutField();
 
-  void GotoNextContainerNodeSimple(bool bUsePageBreak);
-  Stage GotoNextContainerNode(Stage nCurStage,
-                              bool bUsePageBreak,
-                              CXFA_Node* pParentContainer,
-                              CXFA_Node** pCurActionNode);
+  void GotoNextContainerNodeSimple();
 
-  Optional<Stage> ProcessKeepNodesForCheckNext(CXFA_Node** pCurActionNode,
-                                               CXFA_Node** pNextContainer,
-                                               bool* pLastKeepNode);
+  // Return new stage and new action node.
+  std::pair<Stage, CXFA_Node*> GotoNextContainerNode(
+      Stage nCurStage,
+      CXFA_Node* pParentContainer,
+      CXFA_Node* pCurActionNode);
 
-  Optional<Stage> ProcessKeepNodesForBreakBefore(CXFA_Node** pCurActionNode,
-                                                 CXFA_Node* pContainerNode);
+  absl::optional<Stage> ProcessKeepNodesForCheckNext(CXFA_Node** pCurActionNode,
+                                                     CXFA_Node** pNextContainer,
+                                                     bool* pLastKeepNode);
+
+  absl::optional<Stage> ProcessKeepNodesForBreakBefore(
+      CXFA_Node** pCurActionNode,
+      CXFA_Node* pContainerNode);
 
   CXFA_Node* GetSubformSetParent(CXFA_Node* pSubformSet);
 
-  void UpdatePendingItemLayout(
-      const RetainPtr<CXFA_ContentLayoutItem>& pLayoutItem);
-  void AddTrailerBeforeSplit(
-      float fSplitPos,
-      const RetainPtr<CXFA_ContentLayoutItem>& pTrailerLayoutItem,
-      bool bUseInherited);
-  void AddLeaderAfterSplit(
-      const RetainPtr<CXFA_ContentLayoutItem>& pLeaderLayoutItem);
+  void UpdatePendingItemLayout(CXFA_ContentLayoutItem* pLayoutItem);
+  void AddTrailerBeforeSplit(float fSplitPos,
+                             CXFA_ContentLayoutItem* pTrailerLayoutItem,
+                             bool bUseInherited);
+  void AddLeaderAfterSplit(CXFA_ContentLayoutItem* pLeaderLayoutItem);
   void AddPendingNode(CXFA_Node* pPendingNode, bool bBreakPending);
   float InsertPendingItems(CXFA_Node* pCurChildNode);
   Result InsertFlowedItem(
@@ -167,7 +178,8 @@
       float fContainerHeight,
       XFA_AttributeValue eFlowStrategy,
       uint8_t* uCurHAlignState,
-      std::vector<RetainPtr<CXFA_ContentLayoutItem>> (&rgCurLineLayoutItems)[3],
+      std::vector<cppgc::Persistent<CXFA_ContentLayoutItem>> (
+          &rgCurLineLayoutItems)[3],
       bool bUseBreakControl,
       float fAvailHeight,
       float fRealHeight,
@@ -180,19 +192,20 @@
       Context* pLayoutContext,
       bool bNewRow);
 
-  Optional<Stage> HandleKeep(CXFA_Node* pBreakAfterNode,
-                             CXFA_Node** pCurActionNode);
-  Optional<Stage> HandleBookendLeader(CXFA_Node* pParentContainer,
-                                      CXFA_Node** pCurActionNode);
-  Optional<Stage> HandleBreakBefore(CXFA_Node* pChildContainer,
-                                    CXFA_Node** pCurActionNode);
-  Optional<Stage> HandleBreakAfter(CXFA_Node* pChildContainer,
+  absl::optional<Stage> HandleKeep(CXFA_Node* pBreakAfterNode,
                                    CXFA_Node** pCurActionNode);
-  Optional<Stage> HandleCheckNextChildContainer(CXFA_Node* pParentContainer,
-                                                CXFA_Node* pChildContainer,
-                                                CXFA_Node** pCurActionNode);
-  Optional<Stage> HandleBookendTrailer(CXFA_Node* pParentContainer,
-                                       CXFA_Node** pCurActionNode);
+  absl::optional<Stage> HandleBookendLeader(CXFA_Node* pParentContainer,
+                                            CXFA_Node** pCurActionNode);
+  absl::optional<Stage> HandleBreakBefore(CXFA_Node* pChildContainer,
+                                          CXFA_Node** pCurActionNode);
+  absl::optional<Stage> HandleBreakAfter(CXFA_Node* pChildContainer,
+                                         CXFA_Node** pCurActionNode);
+  absl::optional<Stage> HandleCheckNextChildContainer(
+      CXFA_Node* pParentContainer,
+      CXFA_Node* pChildContainer,
+      CXFA_Node** pCurActionNode);
+  absl::optional<Stage> HandleBookendTrailer(CXFA_Node* pParentContainer,
+                                             CXFA_Node** pCurActionNode);
   void ProcessKeepNodesEnd();
   void AdjustContainerSpecifiedSize(Context* pContext,
                                     CFX_SizeF* pSize,
@@ -213,18 +226,19 @@
   float m_fLastRowWidth = 0;
   float m_fLastRowY = 0;
   float m_fWidthLimit = 0;
-  CXFA_Node* const m_pFormNode;
-  CXFA_Node* m_pCurChildNode = nullptr;
-  CXFA_Node* m_pKeepHeadNode = nullptr;
-  CXFA_Node* m_pKeepTailNode = nullptr;
-  RetainPtr<CXFA_ContentLayoutItem> m_pLayoutItem;
-  RetainPtr<CXFA_ContentLayoutItem> m_pOldLayoutItem;
-  UnownedPtr<CXFA_ViewLayoutProcessor> m_pViewLayoutProcessor;
+  UnownedPtr<cppgc::Heap> m_pHeap;
+  cppgc::Member<CXFA_Node> const m_pFormNode;
+  cppgc::Member<CXFA_Node> m_pCurChildNode;
+  cppgc::Member<CXFA_Node> m_pKeepHeadNode;
+  cppgc::Member<CXFA_Node> m_pKeepTailNode;
+  cppgc::Member<CXFA_ContentLayoutItem> m_pLayoutItem;
+  cppgc::Member<CXFA_ContentLayoutItem> m_pOldLayoutItem;
+  cppgc::Member<CXFA_ViewLayoutProcessor> m_pViewLayoutProcessor;
   std::vector<float> m_rgSpecifiedColumnWidths;
-  std::vector<RetainPtr<CXFA_ContentLayoutItem>> m_ArrayKeepItems;
-  std::list<CXFA_Node*> m_PendingNodes;
-  std::map<CXFA_Node*, int32_t> m_PendingNodesCount;
-  std::unique_ptr<CXFA_ContentLayoutProcessor> m_pCurChildPreprocessor;
+  std::vector<cppgc::Member<CXFA_ContentLayoutItem>> m_ArrayKeepItems;
+  std::list<cppgc::Member<CXFA_Node>> m_PendingNodes;
+  std::map<cppgc::Member<CXFA_Node>, int32_t> m_PendingNodesCount;
+  cppgc::Member<CXFA_ContentLayoutProcessor> m_pCurChildPreprocessor;
 };
 
 #endif  // XFA_FXFA_LAYOUT_CXFA_CONTENTLAYOUTPROCESSOR_H_
diff --git a/xfa/fxfa/layout/cxfa_layoutitem.cpp b/xfa/fxfa/layout/cxfa_layoutitem.cpp
index b4523c9..9b7dcca 100644
--- a/xfa/fxfa/layout/cxfa_layoutitem.cpp
+++ b/xfa/fxfa/layout/cxfa_layoutitem.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,6 @@
 
 #include "xfa/fxfa/layout/cxfa_layoutitem.h"
 
-#include <utility>
-
 #include "fxjs/xfa/cjx_object.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
@@ -17,20 +15,20 @@
 #include "xfa/fxfa/parser/cxfa_measurement.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-void XFA_ReleaseLayoutItem(const RetainPtr<CXFA_LayoutItem>& pLayoutItem) {
-  RetainPtr<CXFA_LayoutItem> pNode(pLayoutItem->GetFirstChild());
+void XFA_ReleaseLayoutItem(CXFA_LayoutItem* pLayoutItem) {
+  CXFA_LayoutItem* pNode = pLayoutItem->GetFirstChild();
   while (pNode) {
-    RetainPtr<CXFA_LayoutItem> pNext(pNode->GetNextSibling());
+    CXFA_LayoutItem* pNext = pNode->GetNextSibling();
     XFA_ReleaseLayoutItem(pNode);
-    pNode = std::move(pNext);
+    pNode = pNext;
   }
   CXFA_Document* pDocument = pLayoutItem->GetFormNode()->GetDocument();
   CXFA_FFNotify* pNotify = pDocument->GetNotify();
   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(pDocument);
-  pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem.Get());
+  pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem);
   if (pLayoutItem->GetFormNode()->GetElementType() == XFA_Element::PageArea) {
-    pNotify->OnPageEvent(ToViewLayoutItem(pLayoutItem.Get()),
-                         XFA_PAGEVIEWEVENT_PostRemoved);
+    pNotify->OnPageViewEvent(ToViewLayoutItem(pLayoutItem),
+                             CXFA_FFDoc::PageViewEvent::kPostRemoved);
   }
   pLayoutItem->RemoveSelfIfParented();
 }
@@ -38,13 +36,20 @@
 CXFA_LayoutItem::CXFA_LayoutItem(CXFA_Node* pNode, ItemType type)
     : m_ItemType(type), m_pFormNode(pNode) {}
 
-CXFA_LayoutItem::~CXFA_LayoutItem() {
-  CHECK(!GetParent());
-  if (m_pFormNode) {
-    auto* pJSObj = m_pFormNode->JSObject();
-    if (pJSObj && pJSObj->GetLayoutItem() == this)
-      pJSObj->SetLayoutItem(nullptr);
-  }
+CXFA_LayoutItem::~CXFA_LayoutItem() = default;
+
+void CXFA_LayoutItem::PreFinalize() {
+  if (!m_pFormNode)
+    return;
+
+  auto* pJSObj = m_pFormNode->JSObject();
+  if (pJSObj && pJSObj->GetLayoutItem() == this)
+    pJSObj->SetLayoutItem(nullptr);
+}
+
+void CXFA_LayoutItem::Trace(cppgc::Visitor* visitor) const {
+  GCedTreeNode<CXFA_LayoutItem>::Trace(visitor);
+  visitor->Trace(m_pFormNode);
 }
 
 CXFA_ViewLayoutItem* CXFA_LayoutItem::AsViewLayoutItem() {
@@ -75,3 +80,8 @@
   }
   return nullptr;
 }
+
+void CXFA_LayoutItem::SetFormNode(CXFA_Node* pNode) {
+  // Not in header, assignment requires complete type, not just forward decl.
+  m_pFormNode = pNode;
+}
diff --git a/xfa/fxfa/layout/cxfa_layoutitem.h b/xfa/fxfa/layout/cxfa_layoutitem.h
index 034c145..e372196 100644
--- a/xfa/fxfa/layout/cxfa_layoutitem.h
+++ b/xfa/fxfa/layout/cxfa_layoutitem.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,28 @@
 #ifndef XFA_FXFA_LAYOUT_CXFA_LAYOUTITEM_H_
 #define XFA_FXFA_LAYOUT_CXFA_LAYOUTITEM_H_
 
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/retained_tree_node.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "xfa/fxfa/parser/cxfa_document.h"
+#include "fxjs/gc/gced_tree_node.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/prefinalizer.h"
+#include "v8/include/cppgc/visitor.h"
 
 class CXFA_ContentLayoutItem;
-class CXFA_LayoutProcessor;
+class CXFA_Node;
 class CXFA_ViewLayoutItem;
 
-class CXFA_LayoutItem : public RetainedTreeNode<CXFA_LayoutItem> {
+class CXFA_LayoutItem : public GCedTreeNode<CXFA_LayoutItem> {
+  CPPGC_USING_PRE_FINALIZER(CXFA_LayoutItem, PreFinalize);
+
  public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_LayoutItem() override;
 
+  void PreFinalize();
+
+  // GCedTreeNode:
+  void Trace(cppgc::Visitor* visitor) const override;
+
   bool IsViewLayoutItem() const { return m_ItemType == kViewItem; }
   bool IsContentLayoutItem() const { return m_ItemType == kContentItem; }
   CXFA_ViewLayoutItem* AsViewLayoutItem();
@@ -28,8 +37,8 @@
   const CXFA_ContentLayoutItem* AsContentLayoutItem() const;
 
   const CXFA_ViewLayoutItem* GetPage() const;
-  CXFA_Node* GetFormNode() const { return m_pFormNode.Get(); }
-  void SetFormNode(CXFA_Node* pNode) { m_pFormNode = pNode; }
+  CXFA_Node* GetFormNode() const { return m_pFormNode; }
+  void SetFormNode(CXFA_Node* pNode);
 
  protected:
   enum ItemType { kViewItem, kContentItem };
@@ -37,7 +46,7 @@
 
  private:
   const ItemType m_ItemType;
-  UnownedPtr<CXFA_Node> m_pFormNode;
+  cppgc::Member<CXFA_Node> m_pFormNode;
 };
 
 inline CXFA_ViewLayoutItem* ToViewLayoutItem(CXFA_LayoutItem* item) {
@@ -48,6 +57,6 @@
   return item ? item->AsContentLayoutItem() : nullptr;
 }
 
-void XFA_ReleaseLayoutItem(const RetainPtr<CXFA_LayoutItem>& pLayoutItem);
+void XFA_ReleaseLayoutItem(CXFA_LayoutItem* pLayoutItem);
 
 #endif  // XFA_FXFA_LAYOUT_CXFA_LAYOUTITEM_H_
diff --git a/xfa/fxfa/layout/cxfa_layoutitem_embeddertest.cpp b/xfa/fxfa/layout/cxfa_layoutitem_embeddertest.cpp
index 0ba54ef..6b880b3 100644
--- a/xfa/fxfa/layout/cxfa_layoutitem_embeddertest.cpp
+++ b/xfa/fxfa/layout/cxfa_layoutitem_embeddertest.cpp
@@ -1,50 +1,51 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "testing/embedder_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/xfa_js_embedder_test.h"
 
-class CXFALayoutItemEmbedderTest : public EmbedderTest {};
-
-#if defined(LEAK_SANITIZER)
-
-// Leaks. See https://crbug.com/pdfium/1301
-#define MAYBE_Bug_1301 DISABLED_Bug_1301
-
-#else
-#define MAYBE_Bug_1301 Bug_1301
-#endif
+class CXFALayoutItemEmbedderTest : public XFAJSEmbedderTest {};
 
 TEST_F(CXFALayoutItemEmbedderTest, Bug_1265) {
-  EXPECT_TRUE(OpenDocument("bug_1265.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_1265.pdf"));
   FPDF_PAGE page0 = LoadPage(0);
   FPDF_PAGE page1 = LoadPage(1);
-  EXPECT_NE(nullptr, page0);
-  EXPECT_EQ(nullptr, page1);
+  EXPECT_TRUE(page0);
+  EXPECT_FALSE(page1);
   UnloadPage(page0);
 }
 
-TEST_F(CXFALayoutItemEmbedderTest, MAYBE_Bug_1301) {
-  EXPECT_TRUE(OpenDocument("bug_1301.pdf"));
+TEST_F(CXFALayoutItemEmbedderTest, Bug_1301) {
+  ASSERT_TRUE(OpenDocument("bug_1301.pdf"));
   FPDF_PAGE page0 = LoadPage(0);
   FPDF_PAGE page1 = LoadPage(1);
   FPDF_PAGE page2 = LoadPage(2);
-  EXPECT_NE(nullptr, page0);
-  EXPECT_NE(nullptr, page1);
-  EXPECT_EQ(nullptr, page2);
+  EXPECT_TRUE(page0);
+  EXPECT_TRUE(page1);
+  EXPECT_FALSE(page2);
   UnloadPage(page0);
   UnloadPage(page1);
 }
 
 TEST_F(CXFALayoutItemEmbedderTest, Bug_306123) {
-  EXPECT_TRUE(OpenDocument("bug_306123.pdf"));
+  ASSERT_TRUE(OpenDocument("bug_306123.pdf"));
   FPDF_PAGE page0 = LoadPage(0);
   FPDF_PAGE page1 = LoadPage(1);
   FPDF_PAGE page2 = LoadPage(2);
-  EXPECT_NE(nullptr, page0);
-  EXPECT_NE(nullptr, page1);
-  EXPECT_EQ(nullptr, page2);
+  EXPECT_TRUE(page0);
+  EXPECT_TRUE(page1);
+  EXPECT_FALSE(page2);
   UnloadPage(page0);
   UnloadPage(page1);
 }
+
+TEST_F(CXFALayoutItemEmbedderTest, BreakBeforeAfter) {
+  static constexpr int kExpectedPageCount = 10;
+  ASSERT_TRUE(OpenDocument("xfa/xfa_break_before_after.pdf"));
+  for (int i = 0; i < kExpectedPageCount; ++i) {
+    FPDF_PAGE page = LoadPage(i);
+    EXPECT_TRUE(page);
+    UnloadPage(page);
+  }
+}
diff --git a/xfa/fxfa/layout/cxfa_layoutprocessor.cpp b/xfa/fxfa/layout/cxfa_layoutprocessor.cpp
index bb38fbe..5ad17b8 100644
--- a/xfa/fxfa/layout/cxfa_layoutprocessor.cpp
+++ b/xfa/fxfa/layout/cxfa_layoutprocessor.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,9 @@
 
 #include "xfa/fxfa/layout/cxfa_layoutprocessor.h"
 
+#include "fxjs/gc/container_trace.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "v8/include/cppgc/heap.h"
 #include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
 #include "xfa/fxfa/layout/cxfa_contentlayoutprocessor.h"
 #include "xfa/fxfa/layout/cxfa_viewlayoutprocessor.h"
@@ -26,19 +26,27 @@
   return static_cast<CXFA_LayoutProcessor*>(pXFADoc->GetLayoutProcessor());
 }
 
-CXFA_LayoutProcessor::CXFA_LayoutProcessor() = default;
+CXFA_LayoutProcessor::CXFA_LayoutProcessor(cppgc::Heap* pHeap)
+    : m_pHeap(pHeap) {}
 
 CXFA_LayoutProcessor::~CXFA_LayoutProcessor() = default;
 
-void CXFA_LayoutProcessor::SetForceRelayout(bool bForceRestart) {
-  m_bNeedLayout = bForceRestart;
+void CXFA_LayoutProcessor::Trace(cppgc::Visitor* visitor) const {
+  CXFA_Document::LayoutProcessorIface::Trace(visitor);
+  visitor->Trace(m_pViewLayoutProcessor);
+  visitor->Trace(m_pContentLayoutProcessor);
 }
 
-int32_t CXFA_LayoutProcessor::StartLayout(bool bForceRestart) {
-  if (!bForceRestart && !NeedLayout())
-    return 100;
+void CXFA_LayoutProcessor::SetForceRelayout() {
+  m_bNeedLayout = true;
+}
 
-  m_pContentLayoutProcessor.reset();
+int32_t CXFA_LayoutProcessor::StartLayout() {
+  return NeedLayout() ? RestartLayout() : 100;
+}
+
+int32_t CXFA_LayoutProcessor::RestartLayout() {
+  m_pContentLayoutProcessor = nullptr;
   m_nProgressCounter = 0;
   CXFA_Node* pFormPacketNode =
       ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form));
@@ -50,16 +58,21 @@
   if (!pFormRoot)
     return -1;
 
-  if (!m_pViewLayoutProcessor)
-    m_pViewLayoutProcessor = pdfium::MakeUnique<CXFA_ViewLayoutProcessor>(this);
+  if (!m_pViewLayoutProcessor) {
+    m_pViewLayoutProcessor =
+        cppgc::MakeGarbageCollected<CXFA_ViewLayoutProcessor>(
+            GetHeap()->GetAllocationHandle(), GetHeap(), this);
+  }
   if (!m_pViewLayoutProcessor->InitLayoutPage(pFormRoot))
     return -1;
 
   if (!m_pViewLayoutProcessor->PrepareFirstPage(pFormRoot))
     return -1;
 
-  m_pContentLayoutProcessor = pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(
-      pFormRoot, m_pViewLayoutProcessor.get());
+  m_pContentLayoutProcessor =
+      cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>(
+          GetHeap()->GetAllocationHandle(), GetHeap(), pFormRoot,
+          m_pViewLayoutProcessor);
   m_nProgressCounter = 1;
   return 0;
 }
@@ -81,7 +94,7 @@
     if (eStatus != CXFA_ContentLayoutProcessor::Result::kDone)
       m_nProgressCounter++;
 
-    RetainPtr<CXFA_ContentLayoutItem> pLayoutItem =
+    CXFA_ContentLayoutItem* pLayoutItem =
         m_pContentLayoutProcessor->ExtractLayoutItem();
     if (pLayoutItem)
       pLayoutItem->m_sPos = CFX_PointF(fPosX, fPosY);
@@ -92,8 +105,8 @@
   if (eStatus == CXFA_ContentLayoutProcessor::Result::kDone) {
     m_pViewLayoutProcessor->FinishPaginatedPageSets();
     m_pViewLayoutProcessor->SyncLayoutData();
+    m_bHasChangedContainers = false;
     m_bNeedLayout = false;
-    m_rgChangedContainers.clear();
   }
   return 100 *
          (eStatus == CXFA_ContentLayoutProcessor::Result::kDone
@@ -104,10 +117,10 @@
 
 bool CXFA_LayoutProcessor::IncrementLayout() {
   if (m_bNeedLayout) {
-    StartLayout(true);
+    RestartLayout();
     return DoLayout() == 100;
   }
-  return m_rgChangedContainers.empty();
+  return !m_bHasChangedContainers;
 }
 
 int32_t CXFA_LayoutProcessor::CountPages() const {
@@ -123,11 +136,10 @@
   return pFormItem->JSObject()->GetLayoutItem();
 }
 
-void CXFA_LayoutProcessor::AddChangedContainer(CXFA_Node* pContainer) {
-  if (!pdfium::ContainsValue(m_rgChangedContainers, pContainer))
-    m_rgChangedContainers.push_back(pContainer);
+void CXFA_LayoutProcessor::SetHasChangedContainer() {
+  m_bHasChangedContainers = true;
 }
 
 bool CXFA_LayoutProcessor::NeedLayout() const {
-  return m_bNeedLayout || !m_rgChangedContainers.empty();
+  return m_bNeedLayout || m_bHasChangedContainers;
 }
diff --git a/xfa/fxfa/layout/cxfa_layoutprocessor.h b/xfa/fxfa/layout/cxfa_layoutprocessor.h
index 18dc208..8029891 100644
--- a/xfa/fxfa/layout/cxfa_layoutprocessor.h
+++ b/xfa/fxfa/layout/cxfa_layoutprocessor.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,13 @@
 #ifndef XFA_FXFA_LAYOUT_CXFA_LAYOUTPROCESSOR_H_
 #define XFA_FXFA_LAYOUT_CXFA_LAYOUTPROCESSOR_H_
 
-#include <memory>
-#include <vector>
+#include <stdint.h>
 
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 
 class CXFA_ContentLayoutProcessor;
@@ -20,37 +22,48 @@
 class CXFA_ViewLayoutItem;
 class CXFA_ViewLayoutProcessor;
 
-class CXFA_LayoutProcessor : public CXFA_Document::LayoutProcessorIface {
+namespace cppgc {
+class Heap;
+}  // namespace cppgc
+
+class CXFA_LayoutProcessor final : public CXFA_Document::LayoutProcessorIface {
  public:
   static CXFA_LayoutProcessor* FromDocument(const CXFA_Document* pXFADoc);
 
-  CXFA_LayoutProcessor();
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_LayoutProcessor() override;
 
-  // CXFA_Document::LayoutProcessorIface:
-  void SetForceRelayout(bool bForceRestart) override;
-  void AddChangedContainer(CXFA_Node* pContainer) override;
+  void Trace(cppgc::Visitor* visitor) const override;
 
-  int32_t StartLayout(bool bForceRestart);
+  // CXFA_Document::LayoutProcessorIface:
+  void SetForceRelayout() override;
+  void SetHasChangedContainer() override;
+
+  int32_t StartLayout();
   int32_t DoLayout();
   bool IncrementLayout();
   int32_t CountPages() const;
   CXFA_ViewLayoutItem* GetPage(int32_t index) const;
   CXFA_LayoutItem* GetLayoutItem(CXFA_Node* pFormItem);
   CXFA_ContentLayoutProcessor* GetRootContentLayoutProcessor() const {
-    return m_pContentLayoutProcessor.get();
+    return m_pContentLayoutProcessor;
   }
   CXFA_ViewLayoutProcessor* GetLayoutPageMgr() const {
-    return m_pViewLayoutProcessor.get();
+    return m_pViewLayoutProcessor;
   }
 
  private:
-  bool NeedLayout() const;
+  explicit CXFA_LayoutProcessor(cppgc::Heap* pHeap);
 
-  std::unique_ptr<CXFA_ViewLayoutProcessor> m_pViewLayoutProcessor;
-  std::unique_ptr<CXFA_ContentLayoutProcessor> m_pContentLayoutProcessor;
-  std::vector<CXFA_Node*> m_rgChangedContainers;
+  cppgc::Heap* GetHeap() { return m_pHeap; }
+  bool NeedLayout() const;
+  int32_t RestartLayout();
+
+  UnownedPtr<cppgc::Heap> const m_pHeap;
+  cppgc::Member<CXFA_ViewLayoutProcessor> m_pViewLayoutProcessor;
+  cppgc::Member<CXFA_ContentLayoutProcessor> m_pContentLayoutProcessor;
   uint32_t m_nProgressCounter = 0;
+  bool m_bHasChangedContainers = false;
   bool m_bNeedLayout = true;
 };
 
diff --git a/xfa/fxfa/layout/cxfa_traversestrategy_layoutitem.h b/xfa/fxfa/layout/cxfa_traversestrategy_layoutitem.h
index f71d708..12aed11 100644
--- a/xfa/fxfa/layout/cxfa_traversestrategy_layoutitem.h
+++ b/xfa/fxfa/layout/cxfa_traversestrategy_layoutitem.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,6 @@
 #ifndef XFA_FXFA_LAYOUT_CXFA_TRAVERSESTRATEGY_LAYOUTITEM_H_
 #define XFA_FXFA_LAYOUT_CXFA_TRAVERSESTRATEGY_LAYOUTITEM_H_
 
-#include "core/fxcrt/retain_ptr.h"
 #include "xfa/fxfa/layout/cxfa_layoutitem.h"
 #include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h"
 
@@ -27,6 +26,6 @@
 using CXFA_LayoutItemIterator =
     CXFA_NodeIteratorTemplate<CXFA_LayoutItem,
                               CXFA_TraverseStrategy_LayoutItem,
-                              RetainPtr<CXFA_LayoutItem>>;
+                              CXFA_LayoutItem*>;
 
 #endif  // XFA_FXFA_LAYOUT_CXFA_TRAVERSESTRATEGY_LAYOUTITEM_H_
diff --git a/xfa/fxfa/layout/cxfa_viewlayoutitem.cpp b/xfa/fxfa/layout/cxfa_viewlayoutitem.cpp
index fcef2ab..2269bc5 100644
--- a/xfa/fxfa/layout/cxfa_viewlayoutitem.cpp
+++ b/xfa/fxfa/layout/cxfa_viewlayoutitem.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,6 @@
 
 #include "xfa/fxfa/layout/cxfa_viewlayoutitem.h"
 
-#include <utility>
-
 #include "fxjs/xfa/cjx_object.h"
 #include "xfa/fxfa/cxfa_ffpageview.h"
 #include "xfa/fxfa/layout/cxfa_layoutprocessor.h"
@@ -16,17 +14,19 @@
 #include "xfa/fxfa/parser/cxfa_medium.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-CXFA_ViewLayoutItem::CXFA_ViewLayoutItem(
-    CXFA_Node* pNode,
-    std::unique_ptr<CXFA_FFPageView> pPageView)
-    : CXFA_LayoutItem(pNode, kViewItem), m_pFFPageView(std::move(pPageView)) {
+CXFA_ViewLayoutItem::CXFA_ViewLayoutItem(CXFA_Node* pNode,
+                                         CXFA_FFPageView* pPageView)
+    : CXFA_LayoutItem(pNode, kViewItem), m_pFFPageView(pPageView) {
   if (m_pFFPageView)
     m_pFFPageView->SetLayoutItem(this);
 }
 
-CXFA_ViewLayoutItem::~CXFA_ViewLayoutItem() {
-  if (m_pFFPageView)
-    m_pFFPageView->SetLayoutItem(nullptr);
+CXFA_ViewLayoutItem::~CXFA_ViewLayoutItem() = default;
+
+void CXFA_ViewLayoutItem::Trace(cppgc::Visitor* visitor) const {
+  CXFA_LayoutItem::Trace(visitor);
+  visitor->Trace(m_pOldSubform);
+  visitor->Trace(m_pFFPageView);
 }
 
 CXFA_LayoutProcessor* CXFA_ViewLayoutItem::GetLayout() const {
@@ -59,3 +59,7 @@
 CXFA_Node* CXFA_ViewLayoutItem::GetMasterPage() const {
   return GetFormNode();
 }
+
+void CXFA_ViewLayoutItem::SetOldSubform(CXFA_Node* pSubform) {
+  m_pOldSubform = pSubform;
+}
diff --git a/xfa/fxfa/layout/cxfa_viewlayoutitem.h b/xfa/fxfa/layout/cxfa_viewlayoutitem.h
index 1c9f77c..17ff635 100644
--- a/xfa/fxfa/layout/cxfa_viewlayoutitem.h
+++ b/xfa/fxfa/layout/cxfa_viewlayoutitem.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,32 +7,34 @@
 #ifndef XFA_FXFA_LAYOUT_CXFA_VIEWLAYOUTITEM_H_
 #define XFA_FXFA_LAYOUT_CXFA_VIEWLAYOUTITEM_H_
 
-#include <memory>
-
+#include "core/fxcrt/fx_coordinates.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fxfa/layout/cxfa_layoutitem.h"
 
 class CXFA_FFPageView;
+class CXFA_LayoutProcessor;
 
-class CXFA_ViewLayoutItem : public CXFA_LayoutItem {
+class CXFA_ViewLayoutItem final : public CXFA_LayoutItem {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ViewLayoutItem() override;
 
-  CXFA_FFPageView* GetPageView() const { return m_pFFPageView.get(); }
+  void Trace(cppgc::Visitor* visitor) const override;
+
+  CXFA_FFPageView* GetPageView() const { return m_pFFPageView; }
   CXFA_LayoutProcessor* GetLayout() const;
   int32_t GetPageIndex() const;
   CFX_SizeF GetPageSize() const;
   CXFA_Node* GetMasterPage() const;
-
-  UnownedPtr<CXFA_Node> m_pOldSubform;
+  CXFA_Node* GetOldSubform() const { return m_pOldSubform; }
+  void SetOldSubform(CXFA_Node* pSubform);
 
  private:
-  CXFA_ViewLayoutItem(CXFA_Node* pNode,
-                      std::unique_ptr<CXFA_FFPageView> pPageView);
+  CXFA_ViewLayoutItem(CXFA_Node* pNode, CXFA_FFPageView* pPageView);
 
-  std::unique_ptr<CXFA_FFPageView> const m_pFFPageView;
+  cppgc::Member<CXFA_FFPageView> const m_pFFPageView;
+  cppgc::Member<CXFA_Node> m_pOldSubform;
 };
 
 #endif  // XFA_FXFA_LAYOUT_CXFA_VIEWLAYOUTITEM_H_
diff --git a/xfa/fxfa/layout/cxfa_viewlayoutprocessor.cpp b/xfa/fxfa/layout/cxfa_viewlayoutprocessor.cpp
index dcfdd47..b56c377 100644
--- a/xfa/fxfa/layout/cxfa_viewlayoutprocessor.cpp
+++ b/xfa/fxfa/layout/cxfa_viewlayoutprocessor.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,10 +8,11 @@
 
 #include <utility>
 
+#include "core/fxcrt/stl_util.h"
+#include "fxjs/gc/container_trace.h"
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/cxfa_ffpageview.h"
 #include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
@@ -33,7 +34,6 @@
 #include "xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h"
 #include "xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h"
 #include "xfa/fxfa/parser/xfa_document_datamerger_imp.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 
 namespace {
 
@@ -105,23 +105,25 @@
 using PageSetIterator =
     CXFA_NodeIteratorTemplate<CXFA_ViewLayoutItem, TraverseStrategy_PageSet>;
 
-uint32_t GetRelevant(CXFA_Node* pFormItem, uint32_t dwParentRelvant) {
-  uint32_t dwRelevant = XFA_WidgetStatus_Viewable | XFA_WidgetStatus_Printable;
+Mask<XFA_WidgetStatus> GetRelevant(CXFA_Node* pFormItem,
+                                   Mask<XFA_WidgetStatus> dwParentRelvant) {
+  Mask<XFA_WidgetStatus> dwRelevant = {XFA_WidgetStatus::kViewable,
+                                       XFA_WidgetStatus::kPrintable};
   WideString wsRelevant =
       pFormItem->JSObject()->GetCData(XFA_Attribute::Relevant);
   if (!wsRelevant.IsEmpty()) {
     if (wsRelevant.EqualsASCII("+print") || wsRelevant.EqualsASCII("print"))
-      dwRelevant &= ~XFA_WidgetStatus_Viewable;
+      dwRelevant.Clear(XFA_WidgetStatus::kViewable);
     else if (wsRelevant.EqualsASCII("-print"))
-      dwRelevant &= ~XFA_WidgetStatus_Printable;
+      dwRelevant.Clear(XFA_WidgetStatus::kPrintable);
   }
-  if (!(dwParentRelvant & XFA_WidgetStatus_Viewable) &&
-      (dwRelevant != XFA_WidgetStatus_Viewable)) {
-    dwRelevant &= ~XFA_WidgetStatus_Viewable;
+  if (!(dwParentRelvant & XFA_WidgetStatus::kViewable) &&
+      (dwRelevant != XFA_WidgetStatus::kViewable)) {
+    dwRelevant.Clear(XFA_WidgetStatus::kViewable);
   }
-  if (!(dwParentRelvant & XFA_WidgetStatus_Printable) &&
-      (dwRelevant != XFA_WidgetStatus_Printable)) {
-    dwRelevant &= ~XFA_WidgetStatus_Printable;
+  if (!(dwParentRelvant & XFA_WidgetStatus::kPrintable) &&
+      (dwRelevant != XFA_WidgetStatus::kPrintable)) {
+    dwRelevant.Clear(XFA_WidgetStatus::kPrintable);
   }
   return dwRelevant;
 }
@@ -129,12 +131,12 @@
 void SyncContainer(CXFA_FFNotify* pNotify,
                    CXFA_LayoutProcessor* pDocLayout,
                    CXFA_LayoutItem* pViewItem,
-                   uint32_t dwRelevant,
+                   Mask<XFA_WidgetStatus> dwRelevant,
                    bool bVisible,
                    int32_t nPageIndex) {
   bool bVisibleItem = false;
-  uint32_t dwStatus = 0;
-  uint32_t dwRelevantContainer = 0;
+  Mask<XFA_WidgetStatus> dwStatus;
+  Mask<XFA_WidgetStatus> dwRelevantContainer;
   if (bVisible) {
     XFA_AttributeValue eAttributeValue =
         pViewItem->GetFormNode()
@@ -145,8 +147,9 @@
       bVisibleItem = true;
 
     dwRelevantContainer = GetRelevant(pViewItem->GetFormNode(), dwRelevant);
-    dwStatus =
-        (bVisibleItem ? XFA_WidgetStatus_Visible : 0) | dwRelevantContainer;
+    dwStatus = dwRelevantContainer;
+    if (bVisibleItem)
+      dwStatus |= XFA_WidgetStatus::kVisible;
   }
   pNotify->OnLayoutItemAdded(pDocLayout, pViewItem, nPageIndex, dwStatus);
   for (CXFA_LayoutItem* pChild = pViewItem->GetFirstChild(); pChild;
@@ -158,15 +161,6 @@
   }
 }
 
-void ReorderLayoutItemToTail(const RetainPtr<CXFA_LayoutItem>& pLayoutItem) {
-  CXFA_LayoutItem* pParentLayoutItem = pLayoutItem->GetParent();
-  if (!pParentLayoutItem)
-    return;
-
-  pParentLayoutItem->RemoveChild(pLayoutItem);
-  pParentLayoutItem->AppendLastChild(pLayoutItem);
-}
-
 CXFA_Node* ResolveBreakTarget(CXFA_Node* pPageSetRoot,
                               bool bNewExprStyle,
                               WideString* pTargetAll) {
@@ -178,11 +172,11 @@
     return nullptr;
 
   pTargetAll->Trim();
-  int32_t iSplitIndex = 0;
+  size_t iSplitIndex = 0;
   bool bTargetAllFind = true;
-  while (iSplitIndex != -1) {
+  while (true) {
     WideString wsExpr;
-    Optional<size_t> iSplitNextIndex = 0;
+    absl::optional<size_t> iSplitNextIndex = 0;
     if (!bTargetAllFind) {
       iSplitNextIndex = pTargetAll->Find(' ', iSplitIndex);
       if (!iSplitNextIndex.has_value())
@@ -207,28 +201,29 @@
       if (wsExpr.First(4).EqualsASCII("som(") && wsExpr.Back() == L')')
         wsProcessedTarget = wsExpr.Substr(4, wsExpr.GetLength() - 5);
 
-      XFA_RESOLVENODE_RS rs;
-      bool bRet = pDocument->GetScriptContext()->ResolveObjects(
-          pPageSetRoot, wsProcessedTarget.AsStringView(), &rs,
-          XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
-              XFA_RESOLVENODE_Attributes | XFA_RESOLVENODE_Siblings |
-              XFA_RESOLVENODE_Parent,
-          nullptr);
-      if (bRet && rs.objects.front()->IsNode())
-        return rs.objects.front()->AsNode();
+      absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+          pDocument->GetScriptContext()->ResolveObjects(
+              pPageSetRoot, wsProcessedTarget.AsStringView(),
+              Mask<XFA_ResolveFlag>{
+                  XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kProperties,
+                  XFA_ResolveFlag::kAttributes, XFA_ResolveFlag::kSiblings,
+                  XFA_ResolveFlag::kParent});
+      if (maybeResult.has_value() &&
+          maybeResult.value().objects.front()->IsNode()) {
+        return maybeResult.value().objects.front()->AsNode();
+      }
     }
     iSplitIndex = iSplitNextIndex.value();
   }
-  return nullptr;
 }
 
 void SetLayoutGeneratedNodeFlag(CXFA_Node* pNode) {
-  pNode->SetFlag(XFA_NodeFlag_LayoutGeneratedNode);
-  pNode->ClearFlag(XFA_NodeFlag_UnusedNode);
+  pNode->SetFlag(XFA_NodeFlag::kLayoutGeneratedNode);
+  pNode->ClearFlag(XFA_NodeFlag::kUnusedNode);
 }
 
-// Note: Returning nullptr is not the same as returning pdfium::nullopt.
-Optional<CXFA_ViewLayoutItem*> CheckContentAreaNotUsed(
+// Note: Returning nullptr is not the same as returning absl::nullopt.
+absl::optional<CXFA_ViewLayoutItem*> CheckContentAreaNotUsed(
     CXFA_ViewLayoutItem* pPageAreaLayoutItem,
     CXFA_Node* pContentArea) {
   for (CXFA_LayoutItem* pChild = pPageAreaLayoutItem->GetFirstChild(); pChild;
@@ -237,7 +232,7 @@
     if (pLayoutItem && pLayoutItem->GetFormNode() == pContentArea) {
       if (!pLayoutItem->GetFirstChild())
         return pLayoutItem;
-      return pdfium::nullopt;
+      return absl::nullopt;
     }
   }
   return nullptr;
@@ -246,12 +241,11 @@
 void SyncRemoveLayoutItem(CXFA_LayoutItem* pLayoutItem,
                           CXFA_FFNotify* pNotify,
                           CXFA_LayoutProcessor* pDocLayout) {
-  RetainPtr<CXFA_LayoutItem> pCurLayoutItem(pLayoutItem->GetFirstChild());
+  CXFA_LayoutItem* pCurLayoutItem = pLayoutItem->GetFirstChild();
   while (pCurLayoutItem) {
-    RetainPtr<CXFA_LayoutItem> pNextLayoutItem(
-        pCurLayoutItem->GetNextSibling());
-    SyncRemoveLayoutItem(pCurLayoutItem.Get(), pNotify, pDocLayout);
-    pCurLayoutItem = std::move(pNextLayoutItem);
+    CXFA_LayoutItem* pNextLayoutItem = pCurLayoutItem->GetNextSibling();
+    SyncRemoveLayoutItem(pCurLayoutItem, pNotify, pDocLayout);
+    pCurLayoutItem = pNextLayoutItem;
   }
   pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem);
   pLayoutItem->RemoveSelfIfParented();
@@ -333,21 +327,47 @@
 
 CXFA_ViewLayoutProcessor::CXFA_ViewRecord::~CXFA_ViewRecord() = default;
 
+void CXFA_ViewLayoutProcessor::CXFA_ViewRecord::Trace(
+    cppgc::Visitor* visitor) const {
+  visitor->Trace(pCurPageSet);
+  visitor->Trace(pCurPageArea);
+  visitor->Trace(pCurContentArea);
+}
+
 CXFA_ViewLayoutProcessor::CXFA_ViewLayoutProcessor(
+    cppgc::Heap* pHeap,
     CXFA_LayoutProcessor* pLayoutProcessor)
-    : m_pLayoutProcessor(pLayoutProcessor),
+    : m_pHeap(pHeap),
+      m_pLayoutProcessor(pLayoutProcessor),
       m_CurrentViewRecordIter(m_ProposedViewRecords.end()) {}
 
-CXFA_ViewLayoutProcessor::~CXFA_ViewLayoutProcessor() {
+CXFA_ViewLayoutProcessor::~CXFA_ViewLayoutProcessor() = default;
+
+void CXFA_ViewLayoutProcessor::PreFinalize() {
   ClearData();
-  RetainPtr<CXFA_LayoutItem> pLayoutItem(GetRootLayoutItem());
+  CXFA_LayoutItem* pLayoutItem = GetRootLayoutItem();
   while (pLayoutItem) {
     CXFA_LayoutItem* pNextLayout = pLayoutItem->GetNextSibling();
     XFA_ReleaseLayoutItem(pLayoutItem);
-    pLayoutItem.Reset(pNextLayout);
+    pLayoutItem = pNextLayout;
   }
 }
 
+void CXFA_ViewLayoutProcessor::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pLayoutProcessor);
+  visitor->Trace(m_pPageSetNode);
+  visitor->Trace(m_pCurPageArea);
+  visitor->Trace(m_pPageSetRootLayoutItem);
+  visitor->Trace(m_pPageSetCurLayoutItem);
+  ContainerTrace(visitor, m_ProposedViewRecords);
+
+  if (m_CurrentViewRecordIter != m_ProposedViewRecords.end())
+    visitor->Trace(*m_CurrentViewRecordIter);
+
+  ContainerTrace(visitor, m_PageArray);
+  ContainerTrace(visitor, m_pPageSetMap);
+}
+
 bool CXFA_ViewLayoutProcessor::InitLayoutPage(CXFA_Node* pFormNode) {
   PrepareLayout();
   CXFA_Node* pTemplateNode = pFormNode->GetTemplateNodeIfExists();
@@ -356,13 +376,13 @@
 
   m_pPageSetNode = pTemplateNode->JSObject()->GetOrCreateProperty<CXFA_PageSet>(
       0, XFA_Element::PageSet);
-  ASSERT(m_pPageSetNode);
+  DCHECK(m_pPageSetNode);
 
   if (m_pPageSetRootLayoutItem) {
     m_pPageSetRootLayoutItem->RemoveSelfIfParented();
   } else {
-    m_pPageSetRootLayoutItem =
-        pdfium::MakeRetain<CXFA_ViewLayoutItem>(m_pPageSetNode, nullptr);
+    m_pPageSetRootLayoutItem = cppgc::MakeGarbageCollected<CXFA_ViewLayoutItem>(
+        GetHeap()->GetAllocationHandle(), m_pPageSetNode, nullptr);
   }
   m_pPageSetCurLayoutItem = m_pPageSetRootLayoutItem;
   m_pPageSetNode->JSObject()->SetLayoutItem(m_pPageSetRootLayoutItem.Get());
@@ -399,7 +419,7 @@
       return false;
 
     m_pPageSetNode->InsertChildAndNotify(pPageArea, nullptr);
-    pPageArea->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+    pPageArea->SetInitializedFlagAndNotify();
   }
   CXFA_ContentArea* pContentArea =
       pPageArea->GetChild<CXFA_ContentArea>(0, XFA_Element::ContentArea, false);
@@ -410,7 +430,7 @@
       return false;
 
     pPageArea->InsertChildAndNotify(pContentArea, nullptr);
-    pContentArea->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+    pContentArea->SetInitializedFlagAndNotify();
     pContentArea->JSObject()->SetMeasure(
         XFA_Attribute::X, CXFA_Measurement(0.25f, XFA_Unit::In), false);
     pContentArea->JSObject()->SetMeasure(
@@ -429,7 +449,7 @@
       return false;
 
     pPageArea->InsertChildAndNotify(pMedium, nullptr);
-    pMedium->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+    pMedium->SetInitializedFlagAndNotify();
     pMedium->JSObject()->SetMeasure(
         XFA_Attribute::Short, CXFA_Measurement(8.5f, XFA_Unit::In), false);
     pMedium->JSObject()->SetMeasure(
@@ -507,37 +527,17 @@
   }
 }
 
-void CXFA_ViewLayoutProcessor::ReorderPendingLayoutRecordToTail(
-    CXFA_ViewRecord* pNewRecord,
-    CXFA_ViewRecord* pPrevRecord) {
-  if (!pNewRecord || !pPrevRecord)
-    return;
-  if (pNewRecord->pCurPageSet != pPrevRecord->pCurPageSet) {
-    ReorderLayoutItemToTail(pNewRecord->pCurPageSet);
-    return;
-  }
-  if (pNewRecord->pCurPageArea != pPrevRecord->pCurPageArea) {
-    ReorderLayoutItemToTail(pNewRecord->pCurPageArea);
-    return;
-  }
-  if (pNewRecord->pCurContentArea != pPrevRecord->pCurContentArea) {
-    ReorderLayoutItemToTail(pNewRecord->pCurContentArea);
-    return;
-  }
-}
-
 void CXFA_ViewLayoutProcessor::SubmitContentItem(
-    const RetainPtr<CXFA_ContentLayoutItem>& pContentLayoutItem,
+    CXFA_ContentLayoutItem* pContentLayoutItem,
     CXFA_ContentLayoutProcessor::Result eStatus) {
   if (pContentLayoutItem) {
-    if (!HasCurrentViewRecord())
+    CXFA_ViewRecord* pViewRecord = GetCurrentViewRecord();
+    if (!pViewRecord)
       return;
 
-    GetCurrentViewRecord()->pCurContentArea->AppendLastChild(
-        pContentLayoutItem);
+    pViewRecord->pCurContentArea->AppendLastChild(pContentLayoutItem);
     m_bCreateOverFlowPage = false;
   }
-
   if (eStatus != CXFA_ContentLayoutProcessor::Result::kDone) {
     if (eStatus == CXFA_ContentLayoutProcessor::Result::kPageFullBreak &&
         m_CurrentViewRecordIter == GetTailPosition()) {
@@ -549,11 +549,11 @@
 }
 
 float CXFA_ViewLayoutProcessor::GetAvailHeight() {
-  if (!HasCurrentViewRecord())
+  CXFA_ViewRecord* pViewRecord = GetCurrentViewRecord();
+  if (!pViewRecord)
     return 0.0f;
 
-  RetainPtr<CXFA_ViewLayoutItem> pLayoutItem =
-      GetCurrentViewRecord()->pCurContentArea;
+  CXFA_ViewLayoutItem* pLayoutItem = pViewRecord->pCurContentArea;
   if (!pLayoutItem || !pLayoutItem->GetFormNode())
     return 0.0f;
 
@@ -566,35 +566,36 @@
   return FLT_MAX;
 }
 
-CXFA_ViewLayoutProcessor::CXFA_ViewRecord*
-CXFA_ViewLayoutProcessor::AppendNewRecord(
-    std::unique_ptr<CXFA_ViewRecord> pNewRecord) {
-  m_ProposedViewRecords.push_back(std::move(pNewRecord));
-  return m_ProposedViewRecords.back().get();
+void CXFA_ViewLayoutProcessor::AppendNewRecord(CXFA_ViewRecord* pNewRecord) {
+  m_ProposedViewRecords.emplace_back(pNewRecord);
 }
 
 CXFA_ViewLayoutProcessor::CXFA_ViewRecord*
 CXFA_ViewLayoutProcessor::CreateViewRecord(CXFA_Node* pPageNode,
                                            bool bCreateNew) {
-  ASSERT(pPageNode);
-  auto pNewRecord = pdfium::MakeUnique<CXFA_ViewRecord>();
+  DCHECK(pPageNode);
+  auto* pNewRecord = cppgc::MakeGarbageCollected<CXFA_ViewRecord>(
+      GetHeap()->GetAllocationHandle());
   if (!HasCurrentViewRecord()) {
     CXFA_Node* pPageSet = pPageNode->GetParent();
     if (pPageSet == m_pPageSetNode) {
       pNewRecord->pCurPageSet = m_pPageSetRootLayoutItem;
     } else {
-      auto pPageSetLayoutItem =
-          pdfium::MakeRetain<CXFA_ViewLayoutItem>(pPageSet, nullptr);
-      pPageSet->JSObject()->SetLayoutItem(pPageSetLayoutItem.Get());
+      auto* pPageSetLayoutItem =
+          cppgc::MakeGarbageCollected<CXFA_ViewLayoutItem>(
+              GetHeap()->GetAllocationHandle(), pPageSet, nullptr);
+      pPageSet->JSObject()->SetLayoutItem(pPageSetLayoutItem);
       m_pPageSetRootLayoutItem->AppendLastChild(pPageSetLayoutItem);
-      pNewRecord->pCurPageSet = std::move(pPageSetLayoutItem);
+      pNewRecord->pCurPageSet = pPageSetLayoutItem;
     }
-    return AppendNewRecord(std::move(pNewRecord));
+    AppendNewRecord(pNewRecord);
+    return pNewRecord;
   }
 
   if (!IsPageSetRootOrderedOccurrence()) {
     *pNewRecord = *GetCurrentViewRecord();
-    return AppendNewRecord(std::move(pNewRecord));
+    AppendNewRecord(pNewRecord);
+    return pNewRecord;
   }
 
   CXFA_Node* pPageSet = pPageNode->GetParent();
@@ -602,14 +603,14 @@
     if (pPageSet == m_pPageSetNode) {
       pNewRecord->pCurPageSet = m_pPageSetCurLayoutItem;
     } else {
-      RetainPtr<CXFA_ViewLayoutItem> pParentLayoutItem(
-          ToViewLayoutItem(pPageSet->JSObject()->GetLayoutItem()));
+      CXFA_ViewLayoutItem* pParentLayoutItem =
+          ToViewLayoutItem(pPageSet->JSObject()->GetLayoutItem());
       if (!pParentLayoutItem)
         pParentLayoutItem = m_pPageSetCurLayoutItem;
-
       pNewRecord->pCurPageSet = pParentLayoutItem;
     }
-    return AppendNewRecord(std::move(pNewRecord));
+    AppendNewRecord(pNewRecord);
+    return pNewRecord;
   }
 
   CXFA_ViewLayoutItem* pParentPageSetLayout = nullptr;
@@ -620,52 +621,57 @@
     pParentPageSetLayout =
         ToViewLayoutItem(pPageSet->GetParent()->JSObject()->GetLayoutItem());
   }
-  auto pPageSetLayoutItem =
-      pdfium::MakeRetain<CXFA_ViewLayoutItem>(pPageSet, nullptr);
-  pPageSet->JSObject()->SetLayoutItem(pPageSetLayoutItem.Get());
+  auto* pPageSetLayoutItem = cppgc::MakeGarbageCollected<CXFA_ViewLayoutItem>(
+      GetHeap()->GetAllocationHandle(), pPageSet, nullptr);
+  pPageSet->JSObject()->SetLayoutItem(pPageSetLayoutItem);
   if (!pParentPageSetLayout) {
-    RetainPtr<CXFA_ViewLayoutItem> pPrePageSet(m_pPageSetRootLayoutItem);
-    while (pPrePageSet->GetNextSibling()) {
-      pPrePageSet.Reset(pPrePageSet->GetNextSibling()->AsViewLayoutItem());
-    }
+    CXFA_ViewLayoutItem* pPrePageSet = m_pPageSetRootLayoutItem;
+    while (pPrePageSet->GetNextSibling())
+      pPrePageSet = pPrePageSet->GetNextSibling()->AsViewLayoutItem();
+
     if (pPrePageSet->GetParent()) {
-      pPrePageSet->GetParent()->InsertAfter(pPageSetLayoutItem,
-                                            pPrePageSet.Get());
+      pPrePageSet->GetParent()->InsertAfter(pPageSetLayoutItem, pPrePageSet);
     }
     m_pPageSetCurLayoutItem = pPageSetLayoutItem;
   } else {
     pParentPageSetLayout->AppendLastChild(pPageSetLayoutItem);
   }
   pNewRecord->pCurPageSet = pPageSetLayoutItem;
-  return AppendNewRecord(std::move(pNewRecord));
+  AppendNewRecord(pNewRecord);
+  return pNewRecord;
 }
 
 CXFA_ViewLayoutProcessor::CXFA_ViewRecord*
 CXFA_ViewLayoutProcessor::CreateViewRecordSimple() {
-  auto pNewRecord = pdfium::MakeUnique<CXFA_ViewRecord>();
-  if (HasCurrentViewRecord())
-    *pNewRecord = *GetCurrentViewRecord();
+  auto* pNewRecord = cppgc::MakeGarbageCollected<CXFA_ViewRecord>(
+      GetHeap()->GetAllocationHandle());
+  CXFA_ViewRecord* pCurrentRecord = GetCurrentViewRecord();
+  if (pCurrentRecord)
+    *pNewRecord = *pCurrentRecord;
   else
     pNewRecord->pCurPageSet = m_pPageSetRootLayoutItem;
-  return AppendNewRecord(std::move(pNewRecord));
+  AppendNewRecord(pNewRecord);
+  return pNewRecord;
 }
 
 void CXFA_ViewLayoutProcessor::AddPageAreaLayoutItem(
     CXFA_ViewRecord* pNewRecord,
     CXFA_Node* pNewPageArea) {
-  RetainPtr<CXFA_ViewLayoutItem> pNewPageAreaLayoutItem;
-  if (pdfium::IndexInBounds(m_PageArray, m_nAvailPages)) {
-    RetainPtr<CXFA_ViewLayoutItem> pViewItem = m_PageArray[m_nAvailPages];
+  CXFA_ViewLayoutItem* pNewPageAreaLayoutItem = nullptr;
+  if (fxcrt::IndexInBounds(m_PageArray, m_nAvailPages)) {
+    CXFA_ViewLayoutItem* pViewItem = m_PageArray[m_nAvailPages];
     pViewItem->SetFormNode(pNewPageArea);
     m_nAvailPages++;
-    pNewPageAreaLayoutItem = std::move(pViewItem);
+    pNewPageAreaLayoutItem = pViewItem;
   } else {
     CXFA_FFNotify* pNotify = pNewPageArea->GetDocument()->GetNotify();
-    auto pViewItem = pdfium::MakeRetain<CXFA_ViewLayoutItem>(
-        pNewPageArea, pNotify->OnCreateViewLayoutItem(pNewPageArea));
+    auto* pViewItem = cppgc::MakeGarbageCollected<CXFA_ViewLayoutItem>(
+        GetHeap()->GetAllocationHandle(), pNewPageArea,
+        pNotify->OnCreateViewLayoutItem(pNewPageArea));
     m_PageArray.push_back(pViewItem);
     m_nAvailPages++;
-    pNotify->OnPageEvent(pViewItem.Get(), XFA_PAGEVIEWEVENT_PostRemoved);
+    pNotify->OnPageViewEvent(pViewItem,
+                             CXFA_FFDoc::PageViewEvent::kPostRemoved);
     pNewPageAreaLayoutItem = pViewItem;
   }
   pNewRecord->pCurPageSet->AppendLastChild(pNewPageAreaLayoutItem);
@@ -680,10 +686,10 @@
     pNewRecord->pCurContentArea = nullptr;
     return;
   }
-  auto pNewViewLayoutItem =
-      pdfium::MakeRetain<CXFA_ViewLayoutItem>(pContentArea, nullptr);
+  auto* pNewViewLayoutItem = cppgc::MakeGarbageCollected<CXFA_ViewLayoutItem>(
+      GetHeap()->GetAllocationHandle(), pContentArea, nullptr);
   pNewRecord->pCurPageArea->AppendLastChild(pNewViewLayoutItem);
-  pNewRecord->pCurContentArea = std::move(pNewViewLayoutItem);
+  pNewRecord->pCurContentArea = pNewViewLayoutItem;
 }
 
 void CXFA_ViewLayoutProcessor::FinishPaginatedPageSets() {
@@ -713,11 +719,11 @@
 }
 
 int32_t CXFA_ViewLayoutProcessor::GetPageCount() const {
-  return pdfium::CollectionSize<int32_t>(m_PageArray);
+  return fxcrt::CollectionSize<int32_t>(m_PageArray);
 }
 
 CXFA_ViewLayoutItem* CXFA_ViewLayoutProcessor::GetPage(int32_t index) const {
-  if (!pdfium::IndexInBounds(m_PageArray, index))
+  if (!fxcrt::IndexInBounds(m_PageArray, index))
     return nullptr;
   return m_PageArray[index].Get();
 }
@@ -725,7 +731,9 @@
 int32_t CXFA_ViewLayoutProcessor::GetPageIndex(
     const CXFA_ViewLayoutItem* pPage) const {
   auto it = std::find(m_PageArray.begin(), m_PageArray.end(), pPage);
-  return it != m_PageArray.end() ? it - m_PageArray.begin() : -1;
+  return it != m_PageArray.end()
+             ? pdfium::base::checked_cast<int32_t>(it - m_PageArray.begin())
+             : -1;
 }
 
 bool CXFA_ViewLayoutProcessor::RunBreak(XFA_Element eBreakType,
@@ -845,22 +853,22 @@
   return ret;
 }
 
-Optional<CXFA_ViewLayoutProcessor::BreakData>
+absl::optional<CXFA_ViewLayoutProcessor::BreakData>
 CXFA_ViewLayoutProcessor::ProcessBreakBefore(const CXFA_Node* pBreakNode) {
   return ProcessBreakBeforeOrAfter(pBreakNode, /*before=*/true);
 }
 
-Optional<CXFA_ViewLayoutProcessor::BreakData>
+absl::optional<CXFA_ViewLayoutProcessor::BreakData>
 CXFA_ViewLayoutProcessor::ProcessBreakAfter(const CXFA_Node* pBreakNode) {
   return ProcessBreakBeforeOrAfter(pBreakNode, /*before=*/false);
 }
 
-Optional<CXFA_ViewLayoutProcessor::BreakData>
+absl::optional<CXFA_ViewLayoutProcessor::BreakData>
 CXFA_ViewLayoutProcessor::ProcessBreakBeforeOrAfter(const CXFA_Node* pBreakNode,
                                                     bool bBefore) {
   CXFA_Node* pFormNode = pBreakNode->GetContainerParent();
   if (!pFormNode->PresenceRequiresSpace())
-    return pdfium::nullopt;
+    return absl::nullopt;
 
   BreakData break_data = ExecuteBreakBeforeOrAfter(pBreakNode, bBefore);
   CXFA_Document* pDocument = pBreakNode->GetDocument();
@@ -868,20 +876,20 @@
   pFormNode = pFormNode->GetContainerParent();
   if (break_data.pLeader) {
     if (!break_data.pLeader->IsContainerNode())
-      return pdfium::nullopt;
+      return absl::nullopt;
 
     pDataScope = XFA_DataMerge_FindDataScope(pFormNode);
     break_data.pLeader = pDocument->DataMerge_CopyContainer(
         break_data.pLeader, pFormNode, pDataScope, true, true, true);
     if (!break_data.pLeader)
-      return pdfium::nullopt;
+      return absl::nullopt;
 
     pDocument->DataMerge_UpdateBindingRelations(break_data.pLeader);
     SetLayoutGeneratedNodeFlag(break_data.pLeader);
   }
   if (break_data.pTrailer) {
     if (!break_data.pTrailer->IsContainerNode())
-      return pdfium::nullopt;
+      return absl::nullopt;
 
     if (!pDataScope)
       pDataScope = XFA_DataMerge_FindDataScope(pFormNode);
@@ -889,7 +897,7 @@
     break_data.pTrailer = pDocument->DataMerge_CopyContainer(
         break_data.pTrailer, pFormNode, pDataScope, true, true, true);
     if (!break_data.pTrailer)
-      return pdfium::nullopt;
+      return absl::nullopt;
 
     pDocument->DataMerge_UpdateBindingRelations(break_data.pTrailer);
     SetLayoutGeneratedNodeFlag(break_data.pTrailer);
@@ -1009,11 +1017,11 @@
   return true;
 }
 
-Optional<CXFA_ViewLayoutProcessor::OverflowData>
+absl::optional<CXFA_ViewLayoutProcessor::OverflowData>
 CXFA_ViewLayoutProcessor::ProcessOverflow(CXFA_Node* pFormNode,
                                           bool bCreatePage) {
   if (!pFormNode)
-    return pdfium::nullopt;
+    return absl::nullopt;
 
   CXFA_Node* pLeaderTemplate = nullptr;
   CXFA_Node* pTrailerTemplate = nullptr;
@@ -1036,7 +1044,7 @@
         overflow_data.pLeader = pDocument->DataMerge_CopyContainer(
             pLeaderTemplate, pFormNode, pDataScope, true, true, true);
         if (!overflow_data.pLeader)
-          return pdfium::nullopt;
+          return absl::nullopt;
 
         pDocument->DataMerge_UpdateBindingRelations(overflow_data.pLeader);
         SetLayoutGeneratedNodeFlag(overflow_data.pLeader);
@@ -1048,7 +1056,7 @@
         overflow_data.pTrailer = pDocument->DataMerge_CopyContainer(
             pTrailerTemplate, pFormNode, pDataScope, true, true, true);
         if (!overflow_data.pTrailer)
-          return pdfium::nullopt;
+          return absl::nullopt;
 
         pDocument->DataMerge_UpdateBindingRelations(overflow_data.pTrailer);
         SetLayoutGeneratedNodeFlag(overflow_data.pTrailer);
@@ -1058,7 +1066,7 @@
     if (bIsOverflowNode)
       break;
   }
-  return pdfium::nullopt;
+  return absl::nullopt;
 }
 
 CXFA_Node* CXFA_ViewLayoutProcessor::ResolveBookendLeaderOrTrailer(
@@ -1121,10 +1129,10 @@
     CXFA_Node* pOccurNode =
         pPageSet->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
     if (pOccurNode) {
-      Optional<int32_t> ret =
+      absl::optional<int32_t> ret =
           pOccurNode->JSObject()->TryInteger(XFA_Attribute::Max, false);
-      if (ret)
-        iMax = *ret;
+      if (ret.has_value())
+        iMax = ret.value();
     }
     if (iMax >= 0 && iMax <= iPageSetCount)
       return false;
@@ -1290,13 +1298,13 @@
   if (m_ePageSetMode != XFA_AttributeValue::DuplexPaginated)
     return true;
 
-  Optional<XFA_AttributeValue> ret =
+  absl::optional<XFA_AttributeValue> ret =
       pPageArea->JSObject()->TryEnum(XFA_Attribute::OddOrEven, true);
-  if (!ret || *ret == XFA_AttributeValue::Any)
+  if (!ret.has_value() || ret == XFA_AttributeValue::Any)
     return true;
 
   int32_t iPageLast = GetPageCount() % 2;
-  return *ret == XFA_AttributeValue::Odd ? iPageLast == 0 : iPageLast == 1;
+  return ret == XFA_AttributeValue::Odd ? iPageLast == 0 : iPageLast == 1;
 }
 
 CXFA_Node* CXFA_ViewLayoutProcessor::GetNextAvailPageArea(
@@ -1319,10 +1327,10 @@
       CXFA_Node* pOccurNode =
           m_pCurPageArea->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
       if (pOccurNode) {
-        Optional<int32_t> ret =
+        absl::optional<int32_t> ret =
             pOccurNode->JSObject()->TryInteger(XFA_Attribute::Max, false);
-        if (ret)
-          iMax = *ret;
+        if (ret.has_value())
+          iMax = ret.value();
       }
       if ((iMax < 0 || m_nCurPageCount < iMax)) {
         if (!bQuery) {
@@ -1381,8 +1389,9 @@
     if (pContentArea->GetParent() != m_pCurPageArea)
       return false;
 
-    Optional<CXFA_ViewLayoutItem*> pContentAreaLayout = CheckContentAreaNotUsed(
-        GetCurrentViewRecord()->pCurPageArea.Get(), pContentArea);
+    absl::optional<CXFA_ViewLayoutItem*> pContentAreaLayout =
+        CheckContentAreaNotUsed(GetCurrentViewRecord()->pCurPageArea.Get(),
+                                pContentArea);
     if (!pContentAreaLayout.has_value())
       return false;
     if (pContentAreaLayout.value()) {
@@ -1390,7 +1399,7 @@
         return false;
 
       CXFA_ViewRecord* pNewRecord = CreateViewRecordSimple();
-      pNewRecord->pCurContentArea.Reset(pContentAreaLayout.value());
+      pNewRecord->pCurContentArea = pContentAreaLayout.value();
       return true;
     }
   }
@@ -1423,16 +1432,16 @@
     return 0;
 
   int32_t iMin = 0;
-  Optional<int32_t> ret;
+  absl::optional<int32_t> ret;
   CXFA_Node* pOccurNode =
       pPageArea->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
   if (pOccurNode) {
     ret = pOccurNode->JSObject()->TryInteger(XFA_Attribute::Min, false);
-    if (ret)
-      iMin = *ret;
+    if (ret.has_value())
+      iMin = ret.value();
   }
 
-  if (!ret && !bTargetPageArea)
+  if (!ret.has_value() && !bTargetPageArea)
     return iMin;
 
   CXFA_Node* pContentArea = pPageArea->GetFirstChildByClass<CXFA_ContentArea>(
@@ -1467,12 +1476,12 @@
   if (!pOccurNode)
     return;
 
-  Optional<int32_t> iMin =
+  absl::optional<int32_t> iMin =
       pOccurNode->JSObject()->TryInteger(XFA_Attribute::Min, false);
-  if (!iMin || iCurSetCount >= *iMin)
+  if (!iMin.has_value() || iCurSetCount >= iMin.value())
     return;
 
-  for (int32_t i = 0; i < *iMin - iCurSetCount; i++) {
+  for (int32_t i = 0; i < iMin.value() - iCurSetCount; i++) {
     for (CXFA_Node* node = pPageSet->GetFirstChild(); node;
          node = node->GetNextSibling()) {
       if (node->GetElementType() == XFA_Element::PageArea)
@@ -1481,7 +1490,7 @@
         CreateMinPageSetRecord(node, true);
     }
   }
-  m_pPageSetMap[pPageSet] = *iMin;
+  m_pPageSetMap[pPageSet] = iMin.value();
 }
 
 void CXFA_ViewLayoutProcessor::CreateNextMinRecord(CXFA_Node* pRecordNode) {
@@ -1515,8 +1524,11 @@
 }
 
 bool CXFA_ViewLayoutProcessor::GetNextAvailContentHeight(float fChildHeight) {
-  CXFA_Node* pCurContentNode =
-      GetCurrentViewRecord()->pCurContentArea->GetFormNode();
+  CXFA_ViewRecord* pViewRecord = GetCurrentViewRecord();
+  if (!pViewRecord)
+    return false;
+
+  CXFA_Node* pCurContentNode = pViewRecord->pCurContentArea->GetFormNode();
   if (!pCurContentNode)
     return false;
 
@@ -1532,13 +1544,13 @@
   CXFA_Node* pOccurNode =
       pPageNode->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
   int32_t iMax = 0;
-  Optional<int32_t> ret;
+  absl::optional<int32_t> ret;
   if (pOccurNode) {
     ret = pOccurNode->JSObject()->TryInteger(XFA_Attribute::Max, false);
-    if (ret)
-      iMax = *ret;
+    if (ret.has_value())
+      iMax = ret.value();
   }
-  if (ret) {
+  if (ret.has_value()) {
     if (m_nCurPageCount == iMax) {
       CXFA_Node* pSrcPage = m_pCurPageArea;
       int32_t nSrcPageCount = m_nCurPageCount;
@@ -1547,11 +1559,11 @@
           GetNextAvailPageArea(nullptr, nullptr, false, true);
       m_pCurPageArea = pSrcPage;
       m_nCurPageCount = nSrcPageCount;
-      CXFA_ViewRecord* pPrevRecord = psSrcIter->get();
+      CXFA_ViewRecord* pPrevRecord = psSrcIter->Get();
       ++psSrcIter;
       while (psSrcIter != m_ProposedViewRecords.end()) {
         auto psSaveIter = psSrcIter++;
-        RemoveLayoutRecord(psSaveIter->get(), pPrevRecord);
+        RemoveLayoutRecord(psSaveIter->Get(), pPrevRecord);
         m_ProposedViewRecords.erase(psSaveIter);
       }
       if (pNextPage) {
@@ -1597,21 +1609,20 @@
   CXFA_Document* pDocument = m_pPageSetNode->GetDocument();
   CXFA_FFNotify* pNotify = pDocument->GetNotify();
   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(pDocument);
-  RetainPtr<CXFA_LayoutItem> pCurLayoutItem(pParentLayoutItem->GetFirstChild());
+  CXFA_LayoutItem* pCurLayoutItem = pParentLayoutItem->GetFirstChild();
   while (pCurLayoutItem) {
-    RetainPtr<CXFA_LayoutItem> pNextLayoutItem(
-        pCurLayoutItem->GetNextSibling());
+    CXFA_LayoutItem* pNextLayoutItem = pCurLayoutItem->GetNextSibling();
     if (pCurLayoutItem->IsContentLayoutItem()) {
       if (pCurLayoutItem->GetFormNode()->HasRemovedChildren()) {
-        SyncRemoveLayoutItem(pCurLayoutItem.Get(), pNotify, pDocLayout);
-        pCurLayoutItem = std::move(pNextLayoutItem);
+        SyncRemoveLayoutItem(pCurLayoutItem, pNotify, pDocLayout);
+        pCurLayoutItem = pNextLayoutItem;
         continue;
       }
       if (pCurLayoutItem->GetFormNode()->IsLayoutGeneratedNode())
         pCurLayoutItem->GetFormNode()->SetNodeAndDescendantsUnused();
     }
-    SaveLayoutItemChildren(pCurLayoutItem.Get());
-    pCurLayoutItem = std::move(pNextLayoutItem);
+    SaveLayoutItemChildren(pCurLayoutItem);
+    pCurLayoutItem = pNextLayoutItem;
   }
 }
 
@@ -1646,18 +1657,17 @@
   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(pDocument);
   CXFA_ViewLayoutItem* pRootLayout = GetRootLayoutItem();
 
-  int32_t iIndex = 0;
+  size_t pending_index = 0;
   for (; pRootLayout;
        pRootLayout = ToViewLayoutItem(pRootLayout->GetNextSibling())) {
     CXFA_Node* pPendingPageSet = nullptr;
     ViewLayoutItemIterator iterator(pRootLayout);
     CXFA_ViewLayoutItem* pRootPageSetViewItem = iterator.GetCurrent();
-    ASSERT(pRootPageSetViewItem->GetFormNode()->GetElementType() ==
+    DCHECK(pRootPageSetViewItem->GetFormNode()->GetElementType() ==
            XFA_Element::PageSet);
-    if (iIndex <
-        pdfium::CollectionSize<int32_t>(pDocument->m_pPendingPageSet)) {
-      pPendingPageSet = pDocument->m_pPendingPageSet[iIndex];
-      iIndex++;
+    if (pending_index < pDocument->GetPendingNodesCount()) {
+      pPendingPageSet = pDocument->GetPendingNodeAtIndex(pending_index);
+      ++pending_index;
     }
     if (!pPendingPageSet) {
       if (pRootPageSetViewItem->GetFormNode()->GetPacketType() ==
@@ -1673,7 +1683,7 @@
       pRootPageSetViewItem->GetFormNode()->JSObject()->SetLayoutItem(nullptr);
     }
     pRootPageSetViewItem->SetFormNode(pPendingPageSet);
-    pPendingPageSet->ClearFlag(XFA_NodeFlag_UnusedNode);
+    pPendingPageSet->ClearFlag(XFA_NodeFlag::kUnusedNode);
     for (CXFA_ViewLayoutItem* pViewItem = iterator.MoveToNext(); pViewItem;
          pViewItem = iterator.MoveToNext()) {
       CXFA_Node* pNode = pViewItem->GetFormNode();
@@ -1711,8 +1721,8 @@
           }
           if (bIsExistForm) {
             CXFA_Node* pNewSubform = pFormLayout->GetFormNode();
-            if (pViewItem->m_pOldSubform &&
-                pViewItem->m_pOldSubform != pNewSubform) {
+            if (pViewItem->GetOldSubform() &&
+                pViewItem->GetOldSubform() != pNewSubform) {
               CXFA_Node* pExistingNode = XFA_DataMerge_FindFormDOMInstance(
                   pDocument, pViewItem->GetFormNode()->GetElementType(),
                   pViewItem->GetFormNode()->GetNameHash(), pParentNode);
@@ -1720,11 +1730,10 @@
               for (CXFA_Node* pIter = sIterator.GetCurrent(); pIter;
                    pIter = sIterator.MoveToNext()) {
                 if (pIter->GetElementType() != XFA_Element::ContentArea) {
-                  RetainPtr<CXFA_LayoutItem> pLayoutItem(
-                      pIter->JSObject()->GetLayoutItem());
+                  CXFA_LayoutItem* pLayoutItem =
+                      pIter->JSObject()->GetLayoutItem();
                   if (pLayoutItem) {
-                    pNotify->OnLayoutItemRemoving(pDocLayout,
-                                                  pLayoutItem.Get());
+                    pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem);
                     pLayoutItem->RemoveSelfIfParented();
                   }
                 }
@@ -1733,7 +1742,7 @@
                 pParentNode->RemoveChildAndNotify(pExistingNode, true);
               }
             }
-            pViewItem->m_pOldSubform = pNewSubform;
+            pViewItem->SetOldSubform(pNewSubform);
           }
           CXFA_Node* pOldNode = pViewItem->GetFormNode();
           CXFA_Node* pNewNode = pDocument->DataMerge_CopyContainer(
@@ -1773,7 +1782,7 @@
       }
     }
     pDocument->DataMerge_UpdateBindingRelations(pPendingPageSet);
-    pPendingPageSet->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+    pPendingPageSet->SetInitializedFlagAndNotify();
   }
 
   CXFA_Node* pPageSet = GetRootLayoutItem()->GetFormNode();
@@ -1791,18 +1800,17 @@
             CXFA_ContainerIterator iteChild(pNode);
             CXFA_Node* pChildNode = iteChild.MoveToNext();
             for (; pChildNode; pChildNode = iteChild.MoveToNext()) {
-              RetainPtr<CXFA_LayoutItem> pLayoutItem(
-                  pChildNode->JSObject()->GetLayoutItem());
+              CXFA_LayoutItem* pLayoutItem =
+                  pChildNode->JSObject()->GetLayoutItem();
               if (pLayoutItem) {
-                pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem.Get());
+                pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem);
                 pLayoutItem->RemoveSelfIfParented();
               }
             }
           } else if (eType != XFA_Element::ContentArea) {
-            RetainPtr<CXFA_LayoutItem> pLayoutItem(
-                pNode->JSObject()->GetLayoutItem());
+            CXFA_LayoutItem* pLayoutItem = pNode->JSObject()->GetLayoutItem();
             if (pLayoutItem) {
-              pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem.Get());
+              pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem);
               pLayoutItem->RemoveSelfIfParented();
             }
           }
@@ -1810,12 +1818,12 @@
           pNode->GetParent()->RemoveChildAndNotify(pNode, true);
           pNode = pNext;
         } else {
-          pNode->ClearFlag(XFA_NodeFlag_UnusedNode);
-          pNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+          pNode->ClearFlag(XFA_NodeFlag::kUnusedNode);
+          pNode->SetInitializedFlagAndNotify();
           pNode = sIterator.MoveToNext();
         }
       } else {
-        pNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+        pNode->SetInitializedFlagAndNotify();
         pNode = sIterator.MoveToNext();
       }
     }
@@ -1856,8 +1864,8 @@
         continue;
 
       nPageIdx++;
-      uint32_t dwRelevant =
-          XFA_WidgetStatus_Viewable | XFA_WidgetStatus_Printable;
+      Mask<XFA_WidgetStatus> dwRelevant = {XFA_WidgetStatus::kViewable,
+                                           XFA_WidgetStatus::kPrintable};
       CXFA_LayoutItemIterator iterator(pViewItem);
       CXFA_LayoutItem* pChildLayoutItem = iterator.GetCurrent();
       while (pChildLayoutItem) {
@@ -1874,7 +1882,7 @@
                 ->TryEnum(XFA_Attribute::Presence, true)
                 .value_or(XFA_AttributeValue::Visible);
         bool bVisible = presence == XFA_AttributeValue::Visible;
-        uint32_t dwRelevantChild =
+        Mask<XFA_WidgetStatus> dwRelevantChild =
             GetRelevant(pContentItem->GetFormNode(), dwRelevant);
         SyncContainer(pNotify, m_pLayoutProcessor, pContentItem,
                       dwRelevantChild, bVisible, nPageIdx);
@@ -1883,11 +1891,11 @@
     }
   }
 
-  int32_t nPage = pdfium::CollectionSize<int32_t>(m_PageArray);
+  int32_t nPage = fxcrt::CollectionSize<int32_t>(m_PageArray);
   for (int32_t i = nPage - 1; i >= m_nAvailPages; i--) {
-    RetainPtr<CXFA_ViewLayoutItem> pPage = m_PageArray[i];
+    CXFA_ViewLayoutItem* pPage = m_PageArray[i];
     m_PageArray.erase(m_PageArray.begin() + i);
-    pNotify->OnPageEvent(pPage.Get(), XFA_PAGEVIEWEVENT_PostRemoved);
+    pNotify->OnPageViewEvent(pPage, CXFA_FFDoc::PageViewEvent::kPostRemoved);
   }
   ClearData();
 }
@@ -1900,11 +1908,13 @@
   if (!m_pPageSetRootLayoutItem)
     return;
 
-  RetainPtr<CXFA_ViewLayoutItem> pRootLayoutItem = m_pPageSetRootLayoutItem;
+  CXFA_ViewLayoutItem* pRootLayoutItem = m_pPageSetRootLayoutItem;
   if (pRootLayoutItem &&
       pRootLayoutItem->GetFormNode()->GetPacketType() == XFA_PacketType::Form) {
+    CXFA_Document* const pRootDocument =
+        pRootLayoutItem->GetFormNode()->GetDocument();
     CXFA_Node* pPageSetFormNode = pRootLayoutItem->GetFormNode();
-    pRootLayoutItem->GetFormNode()->GetDocument()->m_pPendingPageSet.clear();
+    pRootDocument->ClearPendingNodes();
     if (pPageSetFormNode->HasRemovedChildren()) {
       XFA_ReleaseLayoutItem(pRootLayoutItem);
       m_pPageSetRootLayoutItem = nullptr;
@@ -1918,17 +1928,15 @@
               XFA_Element::PageSet);
       pPageSetFormNode->GetParent()->RemoveChildAndNotify(pPageSetFormNode,
                                                           false);
-      pRootLayoutItem->GetFormNode()
-          ->GetDocument()
-          ->m_pPendingPageSet.push_back(pPageSetFormNode);
+      pRootDocument->AppendPendingNode(pPageSetFormNode);
       pPageSetFormNode = pNextPageSet;
     }
   }
   pRootLayoutItem = m_pPageSetRootLayoutItem;
   CXFA_ViewLayoutItem* pNextLayout = nullptr;
-  for (; pRootLayoutItem; pRootLayoutItem.Reset(pNextLayout)) {
+  for (; pRootLayoutItem; pRootLayoutItem = pNextLayout) {
     pNextLayout = ToViewLayoutItem(pRootLayoutItem->GetNextSibling());
-    SaveLayoutItemChildren(pRootLayoutItem.Get());
+    SaveLayoutItemChildren(pRootLayoutItem);
     pRootLayoutItem->RemoveSelfIfParented();
   }
   m_pPageSetRootLayoutItem = nullptr;
diff --git a/xfa/fxfa/layout/cxfa_viewlayoutprocessor.h b/xfa/fxfa/layout/cxfa_viewlayoutprocessor.h
index e623285..859621d 100644
--- a/xfa/fxfa/layout/cxfa_viewlayoutprocessor.h
+++ b/xfa/fxfa/layout/cxfa_viewlayoutprocessor.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,89 +10,110 @@
 #include <iterator>
 #include <list>
 #include <map>
-#include <memory>
 #include <vector>
 
-#include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/optional.h"
+#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"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/prefinalizer.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fxfa/layout/cxfa_contentlayoutprocessor.h"
 
 class CXFA_LayoutItem;
+class CXFA_LayoutProcessor;
 class CXFA_Node;
 
-class CXFA_ViewLayoutProcessor {
+class CXFA_ViewLayoutProcessor
+    : public cppgc::GarbageCollected<CXFA_ViewLayoutProcessor> {
+  CPPGC_USING_PRE_FINALIZER(CXFA_ViewLayoutProcessor, PreFinalize);
+
  public:
   struct BreakData {
-    CXFA_Node* pLeader;
-    CXFA_Node* pTrailer;
+    CPPGC_STACK_ALLOCATED();  // Raw/Unowned pointers allowed.
+   public:
+    UNOWNED_PTR_EXCLUSION CXFA_Node* pLeader;   // POD struct.
+    UNOWNED_PTR_EXCLUSION CXFA_Node* pTrailer;  // POD struct.
     bool bCreatePage;
   };
 
   struct OverflowData {
-    CXFA_Node* pLeader;
-    CXFA_Node* pTrailer;
+    CPPGC_STACK_ALLOCATED();  // Raw/Unowned pointers allowed.
+   public:
+    UNOWNED_PTR_EXCLUSION CXFA_Node* pLeader;   // POD struct.
+    UNOWNED_PTR_EXCLUSION CXFA_Node* pTrailer;  // POD struct.
   };
 
-  explicit CXFA_ViewLayoutProcessor(CXFA_LayoutProcessor* pLayoutProcessor);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ViewLayoutProcessor();
 
+  void PreFinalize();
+  void Trace(cppgc::Visitor* visitor) const;
+  cppgc::Heap* GetHeap() const { return m_pHeap; }
+
   bool InitLayoutPage(CXFA_Node* pFormNode);
   bool PrepareFirstPage(CXFA_Node* pRootSubform);
   float GetAvailHeight();
   bool GetNextAvailContentHeight(float fChildHeight);
-  void SubmitContentItem(
-      const RetainPtr<CXFA_ContentLayoutItem>& pContentLayoutItem,
-      CXFA_ContentLayoutProcessor::Result eStatus);
+  void SubmitContentItem(CXFA_ContentLayoutItem* pContentLayoutItem,
+                         CXFA_ContentLayoutProcessor::Result eStatus);
   void FinishPaginatedPageSets();
   void SyncLayoutData();
   int32_t GetPageCount() const;
   CXFA_ViewLayoutItem* GetPage(int32_t index) const;
   int32_t GetPageIndex(const CXFA_ViewLayoutItem* pPage) const;
   CXFA_ViewLayoutItem* GetRootLayoutItem() const {
-    return m_pPageSetRootLayoutItem.Get();
+    return m_pPageSetRootLayoutItem;
   }
-  Optional<BreakData> ProcessBreakBefore(const CXFA_Node* pBreakNode);
-  Optional<BreakData> ProcessBreakAfter(const CXFA_Node* pBreakNode);
-  Optional<OverflowData> ProcessOverflow(CXFA_Node* pFormNode,
-                                         bool bCreatePage);
+  absl::optional<BreakData> ProcessBreakBefore(const CXFA_Node* pBreakNode);
+  absl::optional<BreakData> ProcessBreakAfter(const CXFA_Node* pBreakNode);
+  absl::optional<OverflowData> ProcessOverflow(CXFA_Node* pFormNode,
+                                               bool bCreatePage);
   CXFA_Node* QueryOverflow(CXFA_Node* pFormNode);
   CXFA_Node* ProcessBookendLeader(const CXFA_Node* pBookendNode);
   CXFA_Node* ProcessBookendTrailer(const CXFA_Node* pBookendNode);
 
  private:
-  struct CXFA_ViewRecord {
-    CXFA_ViewRecord();
+  class CXFA_ViewRecord : public cppgc::GarbageCollected<CXFA_ViewRecord> {
+   public:
+    CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
     ~CXFA_ViewRecord();
 
-    RetainPtr<CXFA_ViewLayoutItem> pCurPageSet;
-    RetainPtr<CXFA_ViewLayoutItem> pCurPageArea;
-    RetainPtr<CXFA_ViewLayoutItem> pCurContentArea;
+    void Trace(cppgc::Visitor* visitor) const;
+
+    cppgc::Member<CXFA_ViewLayoutItem> pCurPageSet;
+    cppgc::Member<CXFA_ViewLayoutItem> pCurPageArea;
+    cppgc::Member<CXFA_ViewLayoutItem> pCurContentArea;
+
+   private:
+    CXFA_ViewRecord();
   };
 
-  using RecordList = std::list<std::unique_ptr<CXFA_ViewRecord>>;
+  CXFA_ViewLayoutProcessor(cppgc::Heap* pHeap,
+                           CXFA_LayoutProcessor* pLayoutProcessor);
 
   bool AppendNewPage(bool bFirstTemPage);
-  void ReorderPendingLayoutRecordToTail(CXFA_ViewRecord* pNewRecord,
-                                        CXFA_ViewRecord* pPrevRecord);
   void RemoveLayoutRecord(CXFA_ViewRecord* pNewRecord,
                           CXFA_ViewRecord* pPrevRecord);
   bool HasCurrentViewRecord() const {
     return m_CurrentViewRecordIter != m_ProposedViewRecords.end();
   }
   CXFA_ViewRecord* GetCurrentViewRecord() {
-    return m_CurrentViewRecordIter->get();
+    return HasCurrentViewRecord() ? m_CurrentViewRecordIter->Get() : nullptr;
   }
   const CXFA_ViewRecord* GetCurrentViewRecord() const {
-    return m_CurrentViewRecordIter->get();
+    return HasCurrentViewRecord() ? m_CurrentViewRecordIter->Get() : nullptr;
   }
   void ResetToFirstViewRecord() {
     m_CurrentViewRecordIter = m_ProposedViewRecords.begin();
   }
-  RecordList::iterator GetTailPosition() {
+  std::list<cppgc::Member<CXFA_ViewRecord>>::iterator GetTailPosition() {
     auto iter = m_ProposedViewRecords.end();
     return !m_ProposedViewRecords.empty() ? std::prev(iter) : iter;
   }
-  CXFA_ViewRecord* AppendNewRecord(std::unique_ptr<CXFA_ViewRecord> pNewRecord);
+  void AppendNewRecord(CXFA_ViewRecord* pNewRecord);
   CXFA_ViewRecord* CreateViewRecord(CXFA_Node* pPageNode, bool bCreateNew);
   CXFA_ViewRecord* CreateViewRecordSimple();
   void AddPageAreaLayoutItem(CXFA_ViewRecord* pNewRecord,
@@ -112,8 +133,9 @@
                                            bool bLeader);
   CXFA_Node* ResolveBookendLeaderOrTrailer(const CXFA_Node* pBookendNode,
                                            bool bLeader);
-  Optional<BreakData> ProcessBreakBeforeOrAfter(const CXFA_Node* pBreakNode,
-                                                bool bBefore);
+  absl::optional<BreakData> ProcessBreakBeforeOrAfter(
+      const CXFA_Node* pBreakNode,
+      bool bBefore);
   BreakData ExecuteBreakBeforeOrAfter(const CXFA_Node* pCurNode, bool bBefore);
 
   int32_t CreateMinPageRecord(CXFA_Node* pPageArea,
@@ -160,19 +182,20 @@
   void ProcessSimplexOrDuplexPageSets(CXFA_ViewLayoutItem* pPageSetLayoutItem,
                                       bool bIsSimplex);
 
-  CXFA_LayoutProcessor* m_pLayoutProcessor = nullptr;
-  CXFA_Node* m_pPageSetNode = nullptr;
-  RetainPtr<CXFA_ViewLayoutItem> m_pPageSetRootLayoutItem;
-  RetainPtr<CXFA_ViewLayoutItem> m_pPageSetCurLayoutItem;
-  RecordList m_ProposedViewRecords;
-  RecordList::iterator m_CurrentViewRecordIter;
-  CXFA_Node* m_pCurPageArea = nullptr;
+  UnownedPtr<cppgc::Heap> m_pHeap;
+  cppgc::Member<CXFA_LayoutProcessor> m_pLayoutProcessor;
+  cppgc::Member<CXFA_Node> m_pPageSetNode;
+  cppgc::Member<CXFA_Node> m_pCurPageArea;
+  cppgc::Member<CXFA_ViewLayoutItem> m_pPageSetRootLayoutItem;
+  cppgc::Member<CXFA_ViewLayoutItem> m_pPageSetCurLayoutItem;
+  std::list<cppgc::Member<CXFA_ViewRecord>> m_ProposedViewRecords;
+  std::list<cppgc::Member<CXFA_ViewRecord>>::iterator m_CurrentViewRecordIter;
   int32_t m_nAvailPages = 0;
   int32_t m_nCurPageCount = 0;
   XFA_AttributeValue m_ePageSetMode = XFA_AttributeValue::OrderedOccurrence;
   bool m_bCreateOverFlowPage = false;
-  std::map<CXFA_Node*, int32_t> m_pPageSetMap;
-  std::vector<RetainPtr<CXFA_ViewLayoutItem>> m_PageArray;
+  std::map<cppgc::Member<CXFA_Node>, int32_t> m_pPageSetMap;
+  std::vector<cppgc::Member<CXFA_ViewLayoutItem>> m_PageArray;
 };
 
 #endif  // XFA_FXFA_LAYOUT_CXFA_VIEWLAYOUTPROCESSOR_H_
diff --git a/xfa/fxfa/parser/BUILD.gn b/xfa/fxfa/parser/BUILD.gn
index c6f6d55..f42bd06 100644
--- a/xfa/fxfa/parser/BUILD.gn
+++ b/xfa/fxfa/parser/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -187,8 +187,6 @@
     "cxfa_delete.h",
     "cxfa_delta.cpp",
     "cxfa_delta.h",
-    "cxfa_deltas.cpp",
-    "cxfa_deltas.h",
     "cxfa_desc.cpp",
     "cxfa_desc.h",
     "cxfa_destination.cpp",
@@ -199,8 +197,8 @@
     "cxfa_digestmethods.h",
     "cxfa_document.cpp",
     "cxfa_document.h",
-    "cxfa_document_parser.cpp",
-    "cxfa_document_parser.h",
+    "cxfa_document_builder.cpp",
+    "cxfa_document_builder.h",
     "cxfa_documentassembly.cpp",
     "cxfa_documentassembly.h",
     "cxfa_draw.cpp",
@@ -381,8 +379,6 @@
     "cxfa_neverembed.h",
     "cxfa_node.cpp",
     "cxfa_node.h",
-    "cxfa_nodehelper.cpp",
-    "cxfa_nodehelper.h",
     "cxfa_nodeiteratortemplate.h",
     "cxfa_nodelocale.cpp",
     "cxfa_nodelocale.h",
@@ -672,38 +668,43 @@
     "cxfa_xsl.h",
     "cxfa_zpl.cpp",
     "cxfa_zpl.h",
+    "gced_locale_iface.h",
     "xfa_basic_data.cpp",
     "xfa_basic_data.h",
     "xfa_document_datamerger_imp.cpp",
     "xfa_document_datamerger_imp.h",
-    "xfa_resolvenode_rs.h",
     "xfa_utils.cpp",
     "xfa_utils.h",
   ]
+  allow_circular_includes_from = [ "../../../fxjs" ]
+  configs += [
+    "../../../:pdfium_strict_config",
+    "../../../:pdfium_noshorten_config",
+    "../../:xfa_warnings",
+  ]
   deps = [
     "../../../core/fxcodec",
     "../../../core/fxcrt",
     "../../../core/fxge",
     "../../../fxjs",
+    "../../../fxjs:gc",
     "../../fde",
-    "../../fgas",
-    "../../fxgraphics",
-  ]
-  allow_circular_includes_from = [ "../../../fxjs" ]
-  configs += [
-    "../../../:pdfium_core_config",
-    "../../:xfa_warnings",
+    "../../fgas/crt",
+    "../../fgas/font",
+    "../../fgas/graphics",
   ]
   visibility = [ "../../../*" ]
 }
 
 pdfium_unittest_source_set("unittests") {
   sources = [
-    "cxfa_document_parser_unittest.cpp",
+    "cxfa_document_builder_unittest.cpp",
+    "cxfa_document_unittest.cpp",
     "cxfa_localevalue_unittest.cpp",
     "cxfa_measurement_unittest.cpp",
     "cxfa_node_unittest.cpp",
     "cxfa_nodeiteratortemplate_unittest.cpp",
+    "cxfa_timezoneprovider_unittest.cpp",
     "cxfa_xmllocale_unittest.cpp",
     "xfa_basic_data_unittest.cpp",
     "xfa_utils_unittest.cpp",
@@ -711,11 +712,12 @@
   deps = [
     ":parser",
     "../../../fxjs",
+    "../../../fxjs:gc",
   ]
   pdfium_root_dir = "../../../"
 }
 
 pdfium_embeddertest_source_set("embeddertests") {
-  sources = [ "cxfa_document_parser_embeddertest.cpp" ]
+  sources = [ "cxfa_document_builder_embeddertest.cpp" ]
   pdfium_root_dir = "../../../"
 }
diff --git a/xfa/fxfa/parser/DEPS b/xfa/fxfa/parser/DEPS
index 22337ef..4931b25 100644
--- a/xfa/fxfa/parser/DEPS
+++ b/xfa/fxfa/parser/DEPS
@@ -1,5 +1,4 @@
 include_rules = [
-  # xfa/fwl should be standalone. https://crbug.com/pdfium/507
   '-xfa/fwl',
 ]
 
diff --git a/xfa/fxfa/parser/attribute_values.inc b/xfa/fxfa/parser/attribute_values.inc
index 6cfdadf..744c6af 100644
--- a/xfa/fxfa/parser/attribute_values.inc
+++ b/xfa/fxfa/parser/attribute_values.inc
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fxfa/parser/attributes.inc b/xfa/fxfa/parser/attributes.inc
index 7cc28e4..58dfd6f 100644
--- a/xfa/fxfa/parser/attributes.inc
+++ b/xfa/fxfa/parser/attributes.inc
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fxfa/parser/cscript_datawindow.cpp b/xfa/fxfa/parser/cscript_datawindow.cpp
index 1328dba..16bba42 100644
--- a/xfa/fxfa/parser/cscript_datawindow.cpp
+++ b/xfa/fxfa/parser/cscript_datawindow.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,14 @@
 #include "xfa/fxfa/parser/cscript_datawindow.h"
 
 #include "fxjs/xfa/cjx_datawindow.h"
-#include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_localemgr.h"
-#include "xfa/fxfa/parser/xfa_utils.h"
 
-CScript_DataWindow::CScript_DataWindow(CXFA_Document* pDocument)
-    : CXFA_Object(pDocument,
+CScript_DataWindow::CScript_DataWindow(CXFA_Document* doc)
+    : CXFA_Object(doc,
                   XFA_ObjectType::Object,
                   XFA_Element::DataWindow,
-                  pdfium::MakeUnique<CJX_DataWindow>(this)) {}
+                  cppgc::MakeGarbageCollected<CJX_DataWindow>(
+                      doc->GetHeap()->GetAllocationHandle(),
+                      this)) {}
 
-CScript_DataWindow::~CScript_DataWindow() {}
+CScript_DataWindow::~CScript_DataWindow() = default;
diff --git a/xfa/fxfa/parser/cscript_datawindow.h b/xfa/fxfa/parser/cscript_datawindow.h
index ceed58e..16ebe1b 100644
--- a/xfa/fxfa/parser/cscript_datawindow.h
+++ b/xfa/fxfa/parser/cscript_datawindow.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fxfa/parser/cscript_eventpseudomodel.cpp b/xfa/fxfa/parser/cscript_eventpseudomodel.cpp
index b4953af..92fa033 100644
--- a/xfa/fxfa/parser/cscript_eventpseudomodel.cpp
+++ b/xfa/fxfa/parser/cscript_eventpseudomodel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,14 @@
 #include "xfa/fxfa/parser/cscript_eventpseudomodel.h"
 
 #include "fxjs/xfa/cjx_eventpseudomodel.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
-CScript_EventPseudoModel::CScript_EventPseudoModel(CXFA_Document* pDocument)
-    : CXFA_Object(pDocument,
+CScript_EventPseudoModel::CScript_EventPseudoModel(CXFA_Document* doc)
+    : CXFA_Object(doc,
                   XFA_ObjectType::Object,
                   XFA_Element::EventPseudoModel,
-                  pdfium::MakeUnique<CJX_EventPseudoModel>(this)) {}
+                  cppgc::MakeGarbageCollected<CJX_EventPseudoModel>(
+                      doc->GetHeap()->GetAllocationHandle(),
+                      this)) {}
 
 CScript_EventPseudoModel::~CScript_EventPseudoModel() = default;
diff --git a/xfa/fxfa/parser/cscript_eventpseudomodel.h b/xfa/fxfa/parser/cscript_eventpseudomodel.h
index 8104162..131d267 100644
--- a/xfa/fxfa/parser/cscript_eventpseudomodel.h
+++ b/xfa/fxfa/parser/cscript_eventpseudomodel.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fxfa/parser/cscript_hostpseudomodel.cpp b/xfa/fxfa/parser/cscript_hostpseudomodel.cpp
index 4501633..00b45f8 100644
--- a/xfa/fxfa/parser/cscript_hostpseudomodel.cpp
+++ b/xfa/fxfa/parser/cscript_hostpseudomodel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,14 @@
 #include "xfa/fxfa/parser/cscript_hostpseudomodel.h"
 
 #include "fxjs/xfa/cjx_hostpseudomodel.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
-CScript_HostPseudoModel::CScript_HostPseudoModel(CXFA_Document* pDocument)
-    : CXFA_Object(pDocument,
+CScript_HostPseudoModel::CScript_HostPseudoModel(CXFA_Document* doc)
+    : CXFA_Object(doc,
                   XFA_ObjectType::Object,
                   XFA_Element::HostPseudoModel,
-                  pdfium::MakeUnique<CJX_HostPseudoModel>(this)) {}
+                  cppgc::MakeGarbageCollected<CJX_HostPseudoModel>(
+                      doc->GetHeap()->GetAllocationHandle(),
+                      this)) {}
 
 CScript_HostPseudoModel::~CScript_HostPseudoModel() = default;
diff --git a/xfa/fxfa/parser/cscript_hostpseudomodel.h b/xfa/fxfa/parser/cscript_hostpseudomodel.h
index c8af249..65408f3 100644
--- a/xfa/fxfa/parser/cscript_hostpseudomodel.h
+++ b/xfa/fxfa/parser/cscript_hostpseudomodel.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fxfa/parser/cscript_layoutpseudomodel.cpp b/xfa/fxfa/parser/cscript_layoutpseudomodel.cpp
index 4dfad07..7aab539 100644
--- a/xfa/fxfa/parser/cscript_layoutpseudomodel.cpp
+++ b/xfa/fxfa/parser/cscript_layoutpseudomodel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,14 @@
 #include "xfa/fxfa/parser/cscript_layoutpseudomodel.h"
 
 #include "fxjs/xfa/cjx_layoutpseudomodel.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
-CScript_LayoutPseudoModel::CScript_LayoutPseudoModel(CXFA_Document* pDocument)
-    : CXFA_Object(pDocument,
+CScript_LayoutPseudoModel::CScript_LayoutPseudoModel(CXFA_Document* doc)
+    : CXFA_Object(doc,
                   XFA_ObjectType::Object,
                   XFA_Element::LayoutPseudoModel,
-                  pdfium::MakeUnique<CJX_LayoutPseudoModel>(this)) {}
+                  cppgc::MakeGarbageCollected<CJX_LayoutPseudoModel>(
+                      doc->GetHeap()->GetAllocationHandle(),
+                      this)) {}
 
 CScript_LayoutPseudoModel::~CScript_LayoutPseudoModel() = default;
diff --git a/xfa/fxfa/parser/cscript_layoutpseudomodel.h b/xfa/fxfa/parser/cscript_layoutpseudomodel.h
index e88a6aa..a4da27d 100644
--- a/xfa/fxfa/parser/cscript_layoutpseudomodel.h
+++ b/xfa/fxfa/parser/cscript_layoutpseudomodel.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fxfa/parser/cscript_logpseudomodel.cpp b/xfa/fxfa/parser/cscript_logpseudomodel.cpp
index 185ac80..8f5eece 100644
--- a/xfa/fxfa/parser/cscript_logpseudomodel.cpp
+++ b/xfa/fxfa/parser/cscript_logpseudomodel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,14 @@
 #include "xfa/fxfa/parser/cscript_logpseudomodel.h"
 
 #include "fxjs/xfa/cjx_logpseudomodel.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
-CScript_LogPseudoModel::CScript_LogPseudoModel(CXFA_Document* pDocument)
-    : CXFA_Object(pDocument,
+CScript_LogPseudoModel::CScript_LogPseudoModel(CXFA_Document* doc)
+    : CXFA_Object(doc,
                   XFA_ObjectType::Object,
                   XFA_Element::LogPseudoModel,
-                  pdfium::MakeUnique<CJX_LogPseudoModel>(this)) {}
+                  cppgc::MakeGarbageCollected<CJX_LogPseudoModel>(
+                      doc->GetHeap()->GetAllocationHandle(),
+                      this)) {}
 
-CScript_LogPseudoModel::~CScript_LogPseudoModel() {}
+CScript_LogPseudoModel::~CScript_LogPseudoModel() = default;
diff --git a/xfa/fxfa/parser/cscript_logpseudomodel.h b/xfa/fxfa/parser/cscript_logpseudomodel.h
index aa11bea..e3d6258 100644
--- a/xfa/fxfa/parser/cscript_logpseudomodel.h
+++ b/xfa/fxfa/parser/cscript_logpseudomodel.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fxfa/parser/cscript_signaturepseudomodel.cpp b/xfa/fxfa/parser/cscript_signaturepseudomodel.cpp
index 1f92369..f5d1ae9 100644
--- a/xfa/fxfa/parser/cscript_signaturepseudomodel.cpp
+++ b/xfa/fxfa/parser/cscript_signaturepseudomodel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,14 @@
 #include "xfa/fxfa/parser/cscript_signaturepseudomodel.h"
 
 #include "fxjs/xfa/cjx_signaturepseudomodel.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
-CScript_SignaturePseudoModel::CScript_SignaturePseudoModel(
-    CXFA_Document* pDocument)
-    : CXFA_Object(pDocument,
+CScript_SignaturePseudoModel::CScript_SignaturePseudoModel(CXFA_Document* doc)
+    : CXFA_Object(doc,
                   XFA_ObjectType::Object,
                   XFA_Element::SignaturePseudoModel,
-                  pdfium::MakeUnique<CJX_SignaturePseudoModel>(this)) {}
+                  cppgc::MakeGarbageCollected<CJX_SignaturePseudoModel>(
+                      doc->GetHeap()->GetAllocationHandle(),
+                      this)) {}
 
 CScript_SignaturePseudoModel::~CScript_SignaturePseudoModel() = default;
diff --git a/xfa/fxfa/parser/cscript_signaturepseudomodel.h b/xfa/fxfa/parser/cscript_signaturepseudomodel.h
index 532a767..1a09791 100644
--- a/xfa/fxfa/parser/cscript_signaturepseudomodel.h
+++ b/xfa/fxfa/parser/cscript_signaturepseudomodel.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fxfa/parser/cxfa_accessiblecontent.cpp b/xfa/fxfa/parser/cxfa_accessiblecontent.cpp
index 4840fbb..2e387ab 100644
--- a/xfa/fxfa/parser/cxfa_accessiblecontent.cpp
+++ b/xfa/fxfa/parser/cxfa_accessiblecontent.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_accessiblecontent.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                                XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::AccessibleContent,
                 {},
                 kAccessibleContentAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_AccessibleContent::~CXFA_AccessibleContent() = default;
diff --git a/xfa/fxfa/parser/cxfa_accessiblecontent.h b/xfa/fxfa/parser/cxfa_accessiblecontent.h
index 2aea151..4b4758f 100644
--- a/xfa/fxfa/parser/cxfa_accessiblecontent.h
+++ b/xfa/fxfa/parser/cxfa_accessiblecontent.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_AccessibleContent final : public CXFA_Node {
  public:
-  CXFA_AccessibleContent(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_AccessibleContent() override;
+
+ private:
+  CXFA_AccessibleContent(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ACCESSIBLECONTENT_H_
diff --git a/xfa/fxfa/parser/cxfa_acrobat.cpp b/xfa/fxfa/parser/cxfa_acrobat.cpp
index 301c056..1c10964 100644
--- a/xfa/fxfa/parser/cxfa_acrobat.cpp
+++ b/xfa/fxfa/parser/cxfa_acrobat.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,16 @@
 #include "xfa/fxfa/parser/cxfa_acrobat.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kAcrobatPropertyData[] = {
-    {XFA_Element::AutoSave, 1, 0},
-    {XFA_Element::Validate, 1, 0},
-    {XFA_Element::ValidateApprovalSignatures, 1, 0},
-    {XFA_Element::Acrobat7, 1, 0},
-    {XFA_Element::Common, 1, 0},
+    {XFA_Element::AutoSave, 1, {}},
+    {XFA_Element::Validate, 1, {}},
+    {XFA_Element::ValidateApprovalSignatures, 1, {}},
+    {XFA_Element::Acrobat7, 1, {}},
+    {XFA_Element::Common, 1, {}},
 };
 
 const CXFA_Node::AttributeData kAcrobatAttributeData[] = {
@@ -29,11 +29,13 @@
 CXFA_Acrobat::CXFA_Acrobat(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Acrobat,
                 kAcrobatPropertyData,
                 kAcrobatAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Acrobat::~CXFA_Acrobat() = default;
diff --git a/xfa/fxfa/parser/cxfa_acrobat.h b/xfa/fxfa/parser/cxfa_acrobat.h
index 5711178..7bb06c0 100644
--- a/xfa/fxfa/parser/cxfa_acrobat.h
+++ b/xfa/fxfa/parser/cxfa_acrobat.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Acrobat final : public CXFA_Node {
  public:
-  CXFA_Acrobat(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Acrobat() override;
+
+ private:
+  CXFA_Acrobat(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ACROBAT_H_
diff --git a/xfa/fxfa/parser/cxfa_acrobat7.cpp b/xfa/fxfa/parser/cxfa_acrobat7.cpp
index 8b8afa4..bd6fc7c 100644
--- a/xfa/fxfa/parser/cxfa_acrobat7.cpp
+++ b/xfa/fxfa/parser/cxfa_acrobat7.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_acrobat7.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kAcrobat7PropertyData[] = {
-    {XFA_Element::DynamicRender, 1, 0},
+    {XFA_Element::DynamicRender, 1, {}},
 };
 
 const CXFA_Node::AttributeData kAcrobat7AttributeData[] = {
@@ -25,11 +25,13 @@
 CXFA_Acrobat7::CXFA_Acrobat7(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Acrobat7,
                 kAcrobat7PropertyData,
                 kAcrobat7AttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Acrobat7::~CXFA_Acrobat7() = default;
diff --git a/xfa/fxfa/parser/cxfa_acrobat7.h b/xfa/fxfa/parser/cxfa_acrobat7.h
index 8f64044..3afc486 100644
--- a/xfa/fxfa/parser/cxfa_acrobat7.h
+++ b/xfa/fxfa/parser/cxfa_acrobat7.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Acrobat7 final : public CXFA_Node {
  public:
-  CXFA_Acrobat7(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Acrobat7() override;
+
+ private:
+  CXFA_Acrobat7(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ACROBAT7_H_
diff --git a/xfa/fxfa/parser/cxfa_adbe_jsconsole.cpp b/xfa/fxfa/parser/cxfa_adbe_jsconsole.cpp
index cab9d62..520594e 100644
--- a/xfa/fxfa/parser/cxfa_adbe_jsconsole.cpp
+++ b/xfa/fxfa/parser/cxfa_adbe_jsconsole.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_adbe_jsconsole.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                          XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::ADBE_JSConsole,
                 {},
                 kADBE_JSConsoleAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ADBE_JSConsole::~CXFA_ADBE_JSConsole() = default;
diff --git a/xfa/fxfa/parser/cxfa_adbe_jsconsole.h b/xfa/fxfa/parser/cxfa_adbe_jsconsole.h
index 2fef074..b1a3a0b 100644
--- a/xfa/fxfa/parser/cxfa_adbe_jsconsole.h
+++ b/xfa/fxfa/parser/cxfa_adbe_jsconsole.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_ADBE_JSConsole final : public CXFA_Node {
  public:
-  CXFA_ADBE_JSConsole(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ADBE_JSConsole() override;
+
+ private:
+  CXFA_ADBE_JSConsole(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ADBE_JSCONSOLE_H_
diff --git a/xfa/fxfa/parser/cxfa_adbe_jsdebugger.cpp b/xfa/fxfa/parser/cxfa_adbe_jsdebugger.cpp
index 01775e0..fcd08d9 100644
--- a/xfa/fxfa/parser/cxfa_adbe_jsdebugger.cpp
+++ b/xfa/fxfa/parser/cxfa_adbe_jsdebugger.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_adbe_jsdebugger.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                            XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::ADBE_JSDebugger,
                 {},
                 kADBE_JSDebuggerAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ADBE_JSDebugger::~CXFA_ADBE_JSDebugger() = default;
diff --git a/xfa/fxfa/parser/cxfa_adbe_jsdebugger.h b/xfa/fxfa/parser/cxfa_adbe_jsdebugger.h
index 34fcb0c..7de5e49 100644
--- a/xfa/fxfa/parser/cxfa_adbe_jsdebugger.h
+++ b/xfa/fxfa/parser/cxfa_adbe_jsdebugger.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_ADBE_JSDebugger final : public CXFA_Node {
  public:
-  CXFA_ADBE_JSDebugger(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ADBE_JSDebugger() override;
+
+ private:
+  CXFA_ADBE_JSDebugger(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ADBE_JSDEBUGGER_H_
diff --git a/xfa/fxfa/parser/cxfa_addsilentprint.cpp b/xfa/fxfa/parser/cxfa_addsilentprint.cpp
index 26b7315..0e44795 100644
--- a/xfa/fxfa/parser/cxfa_addsilentprint.cpp
+++ b/xfa/fxfa/parser/cxfa_addsilentprint.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_addsilentprint.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                          XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::AddSilentPrint,
                 {},
                 kAddSilentPrintAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_AddSilentPrint::~CXFA_AddSilentPrint() = default;
diff --git a/xfa/fxfa/parser/cxfa_addsilentprint.h b/xfa/fxfa/parser/cxfa_addsilentprint.h
index abfff47..e1f8fd6 100644
--- a/xfa/fxfa/parser/cxfa_addsilentprint.h
+++ b/xfa/fxfa/parser/cxfa_addsilentprint.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_AddSilentPrint final : public CXFA_Node {
  public:
-  CXFA_AddSilentPrint(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_AddSilentPrint() override;
+
+ private:
+  CXFA_AddSilentPrint(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ADDSILENTPRINT_H_
diff --git a/xfa/fxfa/parser/cxfa_addviewerpreferences.cpp b/xfa/fxfa/parser/cxfa_addviewerpreferences.cpp
index d07fda2..f8392e5 100644
--- a/xfa/fxfa/parser/cxfa_addviewerpreferences.cpp
+++ b/xfa/fxfa/parser/cxfa_addviewerpreferences.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_addviewerpreferences.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                                      XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::AddViewerPreferences,
                 {},
                 kAddViewerPreferencesAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_AddViewerPreferences::~CXFA_AddViewerPreferences() = default;
diff --git a/xfa/fxfa/parser/cxfa_addviewerpreferences.h b/xfa/fxfa/parser/cxfa_addviewerpreferences.h
index 62fd6e3..e26b3b9 100644
--- a/xfa/fxfa/parser/cxfa_addviewerpreferences.h
+++ b/xfa/fxfa/parser/cxfa_addviewerpreferences.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_AddViewerPreferences final : public CXFA_Node {
  public:
-  CXFA_AddViewerPreferences(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_AddViewerPreferences() override;
+
+ private:
+  CXFA_AddViewerPreferences(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ADDVIEWERPREFERENCES_H_
diff --git a/xfa/fxfa/parser/cxfa_adjustdata.cpp b/xfa/fxfa/parser/cxfa_adjustdata.cpp
index 6dbb3d4..aa65a7d 100644
--- a/xfa/fxfa/parser/cxfa_adjustdata.cpp
+++ b/xfa/fxfa/parser/cxfa_adjustdata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_adjustdata.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_AdjustData::CXFA_AdjustData(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::AdjustData,
                 {},
                 kAdjustDataAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_AdjustData::~CXFA_AdjustData() = default;
diff --git a/xfa/fxfa/parser/cxfa_adjustdata.h b/xfa/fxfa/parser/cxfa_adjustdata.h
index 463f2e6..48a5d79 100644
--- a/xfa/fxfa/parser/cxfa_adjustdata.h
+++ b/xfa/fxfa/parser/cxfa_adjustdata.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_AdjustData final : public CXFA_Node {
  public:
-  CXFA_AdjustData(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_AdjustData() override;
+
+ private:
+  CXFA_AdjustData(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ADJUSTDATA_H_
diff --git a/xfa/fxfa/parser/cxfa_adobeextensionlevel.cpp b/xfa/fxfa/parser/cxfa_adobeextensionlevel.cpp
index dce1399..65637b9 100644
--- a/xfa/fxfa/parser/cxfa_adobeextensionlevel.cpp
+++ b/xfa/fxfa/parser/cxfa_adobeextensionlevel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_adobeextensionlevel.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                                    XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::AdobeExtensionLevel,
                 {},
                 kAdobeExtensionLevelAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_AdobeExtensionLevel::~CXFA_AdobeExtensionLevel() = default;
diff --git a/xfa/fxfa/parser/cxfa_adobeextensionlevel.h b/xfa/fxfa/parser/cxfa_adobeextensionlevel.h
index 5e0a507..770520a 100644
--- a/xfa/fxfa/parser/cxfa_adobeextensionlevel.h
+++ b/xfa/fxfa/parser/cxfa_adobeextensionlevel.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_AdobeExtensionLevel final : public CXFA_Node {
  public:
-  CXFA_AdobeExtensionLevel(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_AdobeExtensionLevel() override;
+
+ private:
+  CXFA_AdobeExtensionLevel(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ADOBEEXTENSIONLEVEL_H_
diff --git a/xfa/fxfa/parser/cxfa_agent.cpp b/xfa/fxfa/parser/cxfa_agent.cpp
index f55b7bb..7025044 100644
--- a/xfa/fxfa/parser/cxfa_agent.cpp
+++ b/xfa/fxfa/parser/cxfa_agent.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_agent.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
 CXFA_Agent::CXFA_Agent(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Agent,
                 {},
                 kAgentAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Agent::~CXFA_Agent() = default;
diff --git a/xfa/fxfa/parser/cxfa_agent.h b/xfa/fxfa/parser/cxfa_agent.h
index a84d2ac..5124225 100644
--- a/xfa/fxfa/parser/cxfa_agent.h
+++ b/xfa/fxfa/parser/cxfa_agent.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Agent final : public CXFA_Node {
  public:
-  CXFA_Agent(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Agent() override;
+
+ private:
+  CXFA_Agent(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_AGENT_H_
diff --git a/xfa/fxfa/parser/cxfa_alwaysembed.cpp b/xfa/fxfa/parser/cxfa_alwaysembed.cpp
index afbb4a0..088535c 100644
--- a/xfa/fxfa/parser/cxfa_alwaysembed.cpp
+++ b/xfa/fxfa/parser/cxfa_alwaysembed.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_alwaysembed.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_AlwaysEmbed::CXFA_AlwaysEmbed(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::AlwaysEmbed,
                 {},
                 kAlwaysEmbedAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_AlwaysEmbed::~CXFA_AlwaysEmbed() = default;
diff --git a/xfa/fxfa/parser/cxfa_alwaysembed.h b/xfa/fxfa/parser/cxfa_alwaysembed.h
index 754fd89..0eb0c9b 100644
--- a/xfa/fxfa/parser/cxfa_alwaysembed.h
+++ b/xfa/fxfa/parser/cxfa_alwaysembed.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_AlwaysEmbed final : public CXFA_Node {
  public:
-  CXFA_AlwaysEmbed(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_AlwaysEmbed() override;
+
+ private:
+  CXFA_AlwaysEmbed(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ALWAYSEMBED_H_
diff --git a/xfa/fxfa/parser/cxfa_amd.cpp b/xfa/fxfa/parser/cxfa_amd.cpp
index 0f6c9ef..7ea2cb8 100644
--- a/xfa/fxfa/parser/cxfa_amd.cpp
+++ b/xfa/fxfa/parser/cxfa_amd.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_amd.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Amd::CXFA_Amd(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Amd,
                 {},
                 kAmdAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Amd::~CXFA_Amd() = default;
diff --git a/xfa/fxfa/parser/cxfa_amd.h b/xfa/fxfa/parser/cxfa_amd.h
index 6d6562c..dec500e 100644
--- a/xfa/fxfa/parser/cxfa_amd.h
+++ b/xfa/fxfa/parser/cxfa_amd.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Amd final : public CXFA_Node {
  public:
-  CXFA_Amd(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Amd() override;
+
+ private:
+  CXFA_Amd(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_AMD_H_
diff --git a/xfa/fxfa/parser/cxfa_appearancefilter.cpp b/xfa/fxfa/parser/cxfa_appearancefilter.cpp
index 6d076da..7b1b980 100644
--- a/xfa/fxfa/parser/cxfa_appearancefilter.cpp
+++ b/xfa/fxfa/parser/cxfa_appearancefilter.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_appearancefilter.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -25,11 +25,13 @@
                                              XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::NodeC,
                 XFA_Element::AppearanceFilter,
                 {},
                 kAppearanceFilterAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_AppearanceFilter::~CXFA_AppearanceFilter() = default;
diff --git a/xfa/fxfa/parser/cxfa_appearancefilter.h b/xfa/fxfa/parser/cxfa_appearancefilter.h
index 80b0ba4..042ce1e 100644
--- a/xfa/fxfa/parser/cxfa_appearancefilter.h
+++ b/xfa/fxfa/parser/cxfa_appearancefilter.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_AppearanceFilter final : public CXFA_Node {
  public:
-  CXFA_AppearanceFilter(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_AppearanceFilter() override;
+
+ private:
+  CXFA_AppearanceFilter(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_APPEARANCEFILTER_H_
diff --git a/xfa/fxfa/parser/cxfa_arc.cpp b/xfa/fxfa/parser/cxfa_arc.cpp
index f9e2720..873afa1 100644
--- a/xfa/fxfa/parser/cxfa_arc.cpp
+++ b/xfa/fxfa/parser/cxfa_arc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_arc.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kArcPropertyData[] = {
-    {XFA_Element::Edge, 1, 0},
-    {XFA_Element::Fill, 1, 0},
+    {XFA_Element::Edge, 1, {}},
+    {XFA_Element::Fill, 1, {}},
 };
 
 const CXFA_Node::AttributeData kArcAttributeData[] = {
@@ -29,14 +29,23 @@
 
 }  // namespace
 
+// static
+CXFA_Arc* CXFA_Arc::FromNode(CXFA_Node* pNode) {
+  return pNode && pNode->GetElementType() == XFA_Element::Arc
+             ? static_cast<CXFA_Arc*>(pNode)
+             : nullptr;
+}
+
 CXFA_Arc::CXFA_Arc(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Box(doc,
                packet,
-               (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+               {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                XFA_ObjectType::Node,
                XFA_Element::Arc,
                kArcPropertyData,
                kArcAttributeData,
-               pdfium::MakeUnique<CJX_Node>(this)) {}
+               cppgc::MakeGarbageCollected<CJX_Node>(
+                   doc->GetHeap()->GetAllocationHandle(),
+                   this)) {}
 
 CXFA_Arc::~CXFA_Arc() = default;
diff --git a/xfa/fxfa/parser/cxfa_arc.h b/xfa/fxfa/parser/cxfa_arc.h
index 70d1b00..57eb290 100644
--- a/xfa/fxfa/parser/cxfa_arc.h
+++ b/xfa/fxfa/parser/cxfa_arc.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,13 @@
 
 class CXFA_Arc final : public CXFA_Box {
  public:
-  CXFA_Arc(CXFA_Document* doc, XFA_PacketType packet);
+  static CXFA_Arc* FromNode(CXFA_Node* pNode);
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Arc() override;
+
+ private:
+  CXFA_Arc(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ARC_H_
diff --git a/xfa/fxfa/parser/cxfa_area.cpp b/xfa/fxfa/parser/cxfa_area.cpp
index 16fa014..ff24351 100644
--- a/xfa/fxfa/parser/cxfa_area.cpp
+++ b/xfa/fxfa/parser/cxfa_area.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_area.h"
 
 #include "fxjs/xfa/cjx_container.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kAreaPropertyData[] = {
-    {XFA_Element::Desc, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Desc, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kAreaAttributeData[] = {
@@ -32,14 +32,16 @@
 }  // namespace
 
 CXFA_Area::CXFA_Area(CXFA_Document* doc, XFA_PacketType packet)
-    : CXFA_Node(
-          doc,
-          packet,
-          (XFA_XDPPACKET_Config | XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
-          XFA_ObjectType::ContainerNode,
-          XFA_Element::Area,
-          kAreaPropertyData,
-          kAreaAttributeData,
-          pdfium::MakeUnique<CJX_Container>(this)) {}
+    : CXFA_Node(doc,
+                packet,
+                {XFA_XDPPACKET::kConfig, XFA_XDPPACKET::kTemplate,
+                 XFA_XDPPACKET::kForm},
+                XFA_ObjectType::ContainerNode,
+                XFA_Element::Area,
+                kAreaPropertyData,
+                kAreaAttributeData,
+                cppgc::MakeGarbageCollected<CJX_Container>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Area::~CXFA_Area() = default;
diff --git a/xfa/fxfa/parser/cxfa_area.h b/xfa/fxfa/parser/cxfa_area.h
index cf1ff87..6008707 100644
--- a/xfa/fxfa/parser/cxfa_area.h
+++ b/xfa/fxfa/parser/cxfa_area.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Area final : public CXFA_Node {
  public:
-  CXFA_Area(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Area() override;
+
+ private:
+  CXFA_Area(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_AREA_H_
diff --git a/xfa/fxfa/parser/cxfa_arraynodelist.cpp b/xfa/fxfa/parser/cxfa_arraynodelist.cpp
index db36dca..81f987a 100644
--- a/xfa/fxfa/parser/cxfa_arraynodelist.cpp
+++ b/xfa/fxfa/parser/cxfa_arraynodelist.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,22 +9,34 @@
 #include <utility>
 #include <vector>
 
+#include "fxjs/gc/container_trace.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+
 CXFA_ArrayNodeList::CXFA_ArrayNodeList(CXFA_Document* pDocument)
     : CXFA_TreeList(pDocument) {}
 
-CXFA_ArrayNodeList::~CXFA_ArrayNodeList() {}
+CXFA_ArrayNodeList::~CXFA_ArrayNodeList() = default;
 
-void CXFA_ArrayNodeList::SetArrayNodeList(std::vector<CXFA_Node*> srcArray) {
-  if (!srcArray.empty())
-    m_array = std::move(srcArray);
+void CXFA_ArrayNodeList::Trace(cppgc::Visitor* visitor) const {
+  CXFA_TreeList::Trace(visitor);
+  ContainerTrace(visitor, m_array);
+}
+
+void CXFA_ArrayNodeList::SetArrayNodeList(
+    const std::vector<CXFA_Node*>& srcArray) {
+  if (!srcArray.empty()) {
+    m_array =
+        std::vector<cppgc::Member<CXFA_Node>>(srcArray.begin(), srcArray.end());
+  }
 }
 
 size_t CXFA_ArrayNodeList::GetLength() {
   return m_array.size();
 }
 
-void CXFA_ArrayNodeList::Append(CXFA_Node* pNode) {
+bool CXFA_ArrayNodeList::Append(CXFA_Node* pNode) {
   m_array.push_back(pNode);
+  return true;
 }
 
 bool CXFA_ArrayNodeList::Insert(CXFA_Node* pNewNode, CXFA_Node* pBeforeNode) {
diff --git a/xfa/fxfa/parser/cxfa_arraynodelist.h b/xfa/fxfa/parser/cxfa_arraynodelist.h
index 238c52c..4c532d4 100644
--- a/xfa/fxfa/parser/cxfa_arraynodelist.h
+++ b/xfa/fxfa/parser/cxfa_arraynodelist.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,6 +9,8 @@
 
 #include <vector>
 
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fxfa/parser/cxfa_treelist.h"
 
 class CXFA_Document;
@@ -16,20 +18,24 @@
 
 class CXFA_ArrayNodeList final : public CXFA_TreeList {
  public:
-  explicit CXFA_ArrayNodeList(CXFA_Document* pDocument);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ArrayNodeList() override;
 
+  void Trace(cppgc::Visitor* visitor) const override;
+
   // CXFA_TreeList:
   size_t GetLength() override;
-  void Append(CXFA_Node* pNode) override;
+  bool Append(CXFA_Node* pNode) override;
   bool Insert(CXFA_Node* pNewNode, CXFA_Node* pBeforeNode) override;
   void Remove(CXFA_Node* pNode) override;
   CXFA_Node* Item(size_t iIndex) override;
 
-  void SetArrayNodeList(std::vector<CXFA_Node*> srcArray);
+  void SetArrayNodeList(const std::vector<CXFA_Node*>& srcArray);
 
  private:
-  std::vector<CXFA_Node*> m_array;
+  explicit CXFA_ArrayNodeList(CXFA_Document* pDocument);
+
+  std::vector<cppgc::Member<CXFA_Node>> m_array;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ARRAYNODELIST_H_
diff --git a/xfa/fxfa/parser/cxfa_assist.cpp b/xfa/fxfa/parser/cxfa_assist.cpp
index cac0bc6..ccd675f 100644
--- a/xfa/fxfa/parser/cxfa_assist.cpp
+++ b/xfa/fxfa/parser/cxfa_assist.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_assist.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kAssistPropertyData[] = {
-    {XFA_Element::ToolTip, 1, 0},
-    {XFA_Element::Speak, 1, 0},
+    {XFA_Element::ToolTip, 1, {}},
+    {XFA_Element::Speak, 1, {}},
 };
 
 const CXFA_Node::AttributeData kAssistAttributeData[] = {
@@ -28,11 +28,13 @@
 CXFA_Assist::CXFA_Assist(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Assist,
                 kAssistPropertyData,
                 kAssistAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Assist::~CXFA_Assist() = default;
diff --git a/xfa/fxfa/parser/cxfa_assist.h b/xfa/fxfa/parser/cxfa_assist.h
index 77643cf..f97f3e4 100644
--- a/xfa/fxfa/parser/cxfa_assist.h
+++ b/xfa/fxfa/parser/cxfa_assist.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Assist final : public CXFA_Node {
  public:
-  CXFA_Assist(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Assist() override;
+
+ private:
+  CXFA_Assist(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ASSIST_H_
diff --git a/xfa/fxfa/parser/cxfa_attachnodelist.cpp b/xfa/fxfa/parser/cxfa_attachnodelist.cpp
index 6d8e5c3..1a15320 100644
--- a/xfa/fxfa/parser/cxfa_attachnodelist.cpp
+++ b/xfa/fxfa/parser/cxfa_attachnodelist.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,21 +15,33 @@
 
 CXFA_AttachNodeList::~CXFA_AttachNodeList() = default;
 
+void CXFA_AttachNodeList::Trace(cppgc::Visitor* visitor) const {
+  CXFA_TreeList::Trace(visitor);
+  visitor->Trace(m_pAttachNode);
+}
+
 size_t CXFA_AttachNodeList::GetLength() {
   return m_pAttachNode->CountChildren(
       XFA_Element::Unknown,
       m_pAttachNode->GetElementType() == XFA_Element::Subform);
 }
 
-void CXFA_AttachNodeList::Append(CXFA_Node* pNode) {
+bool CXFA_AttachNodeList::Append(CXFA_Node* pNode) {
+  if (pNode->IsAncestorOf(m_pAttachNode))
+    return false;
+
   CXFA_Node* pParent = pNode->GetParent();
   if (pParent)
     pParent->RemoveChildAndNotify(pNode, true);
 
   m_pAttachNode->InsertChildAndNotify(pNode, nullptr);
+  return true;
 }
 
 bool CXFA_AttachNodeList::Insert(CXFA_Node* pNewNode, CXFA_Node* pBeforeNode) {
+  if (pNewNode->IsAncestorOf(m_pAttachNode))
+    return false;
+
   if (pBeforeNode && pBeforeNode->GetParent() != m_pAttachNode)
     return false;
 
diff --git a/xfa/fxfa/parser/cxfa_attachnodelist.h b/xfa/fxfa/parser/cxfa_attachnodelist.h
index 2b20d70..4afff96 100644
--- a/xfa/fxfa/parser/cxfa_attachnodelist.h
+++ b/xfa/fxfa/parser/cxfa_attachnodelist.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,8 @@
 #ifndef XFA_FXFA_PARSER_CXFA_ATTACHNODELIST_H_
 #define XFA_FXFA_PARSER_CXFA_ATTACHNODELIST_H_
 
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fxfa/parser/cxfa_treelist.h"
 
 class CXFA_Document;
@@ -14,18 +16,22 @@
 
 class CXFA_AttachNodeList final : public CXFA_TreeList {
  public:
-  CXFA_AttachNodeList(CXFA_Document* pDocument, CXFA_Node* pAttachNode);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_AttachNodeList() override;
 
+  void Trace(cppgc::Visitor* visitor) const override;
+
   // CXFA_TreeList:
   size_t GetLength() override;
-  void Append(CXFA_Node* pNode) override;
+  bool Append(CXFA_Node* pNode) override;
   bool Insert(CXFA_Node* pNewNode, CXFA_Node* pBeforeNode) override;
   void Remove(CXFA_Node* pNode) override;
   CXFA_Node* Item(size_t iIndex) override;
 
  private:
-  UnownedPtr<CXFA_Node> const m_pAttachNode;
+  CXFA_AttachNodeList(CXFA_Document* pDocument, CXFA_Node* pAttachNode);
+
+  cppgc::Member<CXFA_Node> const m_pAttachNode;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ATTACHNODELIST_H_
diff --git a/xfa/fxfa/parser/cxfa_attributes.cpp b/xfa/fxfa/parser/cxfa_attributes.cpp
index aeaf8cf..9f2254f 100644
--- a/xfa/fxfa/parser/cxfa_attributes.cpp
+++ b/xfa/fxfa/parser/cxfa_attributes.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_attributes.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Attributes::CXFA_Attributes(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Attributes,
                 {},
                 kAttributesAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Attributes::~CXFA_Attributes() = default;
diff --git a/xfa/fxfa/parser/cxfa_attributes.h b/xfa/fxfa/parser/cxfa_attributes.h
index 099ecea..477fa8f 100644
--- a/xfa/fxfa/parser/cxfa_attributes.h
+++ b/xfa/fxfa/parser/cxfa_attributes.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Attributes final : public CXFA_Node {
  public:
-  CXFA_Attributes(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Attributes() override;
+
+ private:
+  CXFA_Attributes(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ATTRIBUTES_H_
diff --git a/xfa/fxfa/parser/cxfa_autosave.cpp b/xfa/fxfa/parser/cxfa_autosave.cpp
index 978cd56..1af4346 100644
--- a/xfa/fxfa/parser/cxfa_autosave.cpp
+++ b/xfa/fxfa/parser/cxfa_autosave.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_autosave.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_AutoSave::CXFA_AutoSave(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::AutoSave,
                 {},
                 kAutoSaveAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_AutoSave::~CXFA_AutoSave() = default;
diff --git a/xfa/fxfa/parser/cxfa_autosave.h b/xfa/fxfa/parser/cxfa_autosave.h
index 5324351..c906cb3 100644
--- a/xfa/fxfa/parser/cxfa_autosave.h
+++ b/xfa/fxfa/parser/cxfa_autosave.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_AutoSave final : public CXFA_Node {
  public:
-  CXFA_AutoSave(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_AutoSave() override;
+
+ private:
+  CXFA_AutoSave(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_AUTOSAVE_H_
diff --git a/xfa/fxfa/parser/cxfa_barcode.cpp b/xfa/fxfa/parser/cxfa_barcode.cpp
index a7dbb71..2801dc1 100644
--- a/xfa/fxfa/parser/cxfa_barcode.cpp
+++ b/xfa/fxfa/parser/cxfa_barcode.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_barcode.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_measurement.h"
 
 namespace {
@@ -42,15 +42,24 @@
 
 }  // namespace
 
+// static
+CXFA_Barcode* CXFA_Barcode::FromNode(CXFA_Node* pNode) {
+  return pNode && pNode->GetElementType() == XFA_Element::Barcode
+             ? static_cast<CXFA_Barcode*>(pNode)
+             : nullptr;
+}
+
 CXFA_Barcode::CXFA_Barcode(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Barcode,
                 {},
                 kBarcodeAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Barcode::~CXFA_Barcode() = default;
 
@@ -62,17 +71,17 @@
   return WideString(JSObject()->GetCData(XFA_Attribute::Type));
 }
 
-Optional<WideString> CXFA_Barcode::GetCharEncoding() {
+absl::optional<WideString> CXFA_Barcode::GetCharEncoding() {
   return JSObject()->TryCData(XFA_Attribute::CharEncoding, true);
 }
 
-Optional<bool> CXFA_Barcode::GetChecksum() {
-  Optional<XFA_AttributeValue> checksum =
+absl::optional<bool> CXFA_Barcode::GetChecksum() {
+  absl::optional<XFA_AttributeValue> checksum =
       JSObject()->TryEnum(XFA_Attribute::Checksum, true);
-  if (!checksum)
-    return {};
+  if (!checksum.has_value())
+    return absl::nullopt;
 
-  switch (*checksum) {
+  switch (checksum.value()) {
     case XFA_AttributeValue::None:
       return {false};
     case XFA_AttributeValue::Auto:
@@ -83,91 +92,92 @@
     default:
       break;
   }
-  return {};
+  return absl::nullopt;
 }
 
-Optional<int32_t> CXFA_Barcode::GetDataLength() {
-  Optional<WideString> wsDataLength =
+absl::optional<int32_t> CXFA_Barcode::GetDataLength() {
+  absl::optional<WideString> wsDataLength =
       JSObject()->TryCData(XFA_Attribute::DataLength, true);
-  if (!wsDataLength)
-    return {};
+  if (!wsDataLength.has_value())
+    return absl::nullopt;
 
-  return {FXSYS_wtoi(wsDataLength->c_str())};
+  return FXSYS_wtoi(wsDataLength->c_str());
 }
 
-Optional<char> CXFA_Barcode::GetStartChar() {
-  Optional<WideString> wsStartEndChar =
+absl::optional<char> CXFA_Barcode::GetStartChar() {
+  absl::optional<WideString> wsStartEndChar =
       JSObject()->TryCData(XFA_Attribute::StartChar, true);
-  if (!wsStartEndChar || wsStartEndChar->IsEmpty())
-    return {};
+  if (!wsStartEndChar.has_value() || wsStartEndChar->IsEmpty())
+    return absl::nullopt;
 
-  return {static_cast<char>((*wsStartEndChar)[0])};
+  return static_cast<char>(wsStartEndChar.value()[0]);
 }
 
-Optional<char> CXFA_Barcode::GetEndChar() {
-  Optional<WideString> wsStartEndChar =
+absl::optional<char> CXFA_Barcode::GetEndChar() {
+  absl::optional<WideString> wsStartEndChar =
       JSObject()->TryCData(XFA_Attribute::EndChar, true);
-  if (!wsStartEndChar || wsStartEndChar->IsEmpty())
-    return {};
+  if (!wsStartEndChar.has_value() || wsStartEndChar->IsEmpty())
+    return absl::nullopt;
 
-  return {static_cast<char>((*wsStartEndChar)[0])};
+  return static_cast<char>(wsStartEndChar.value()[0]);
 }
 
-Optional<int32_t> CXFA_Barcode::GetECLevel() {
-  Optional<WideString> wsECLevel =
+absl::optional<int32_t> CXFA_Barcode::GetECLevel() {
+  absl::optional<WideString> wsECLevel =
       JSObject()->TryCData(XFA_Attribute::ErrorCorrectionLevel, true);
-  if (!wsECLevel)
-    return {};
-  return {FXSYS_wtoi(wsECLevel->c_str())};
+  if (!wsECLevel.has_value())
+    return absl::nullopt;
+  return FXSYS_wtoi(wsECLevel->c_str());
 }
 
-Optional<int32_t> CXFA_Barcode::GetModuleWidth() {
-  Optional<CXFA_Measurement> moduleWidthHeight =
+absl::optional<int32_t> CXFA_Barcode::GetModuleWidth() {
+  absl::optional<CXFA_Measurement> moduleWidthHeight =
       JSObject()->TryMeasure(XFA_Attribute::ModuleWidth, true);
-  if (!moduleWidthHeight)
-    return {};
+  if (!moduleWidthHeight.has_value())
+    return absl::nullopt;
 
-  return {static_cast<int32_t>(moduleWidthHeight->ToUnit(XFA_Unit::Pt))};
+  return static_cast<int32_t>(moduleWidthHeight->ToUnit(XFA_Unit::Pt));
 }
 
-Optional<int32_t> CXFA_Barcode::GetModuleHeight() {
-  Optional<CXFA_Measurement> moduleWidthHeight =
+absl::optional<int32_t> CXFA_Barcode::GetModuleHeight() {
+  absl::optional<CXFA_Measurement> moduleWidthHeight =
       JSObject()->TryMeasure(XFA_Attribute::ModuleHeight, true);
-  if (!moduleWidthHeight)
-    return {};
+  if (!moduleWidthHeight.has_value())
+    return absl::nullopt;
 
-  return {static_cast<int32_t>(moduleWidthHeight->ToUnit(XFA_Unit::Pt))};
+  return static_cast<int32_t>(moduleWidthHeight->ToUnit(XFA_Unit::Pt));
 }
 
-Optional<bool> CXFA_Barcode::GetPrintChecksum() {
+absl::optional<bool> CXFA_Barcode::GetPrintChecksum() {
   return JSObject()->TryBoolean(XFA_Attribute::PrintCheckDigit, true);
 }
 
-Optional<XFA_AttributeValue> CXFA_Barcode::GetTextLocation() {
+absl::optional<XFA_AttributeValue> CXFA_Barcode::GetTextLocation() {
   return JSObject()->TryEnum(XFA_Attribute::TextLocation, true);
 }
 
-Optional<bool> CXFA_Barcode::GetTruncate() {
+absl::optional<bool> CXFA_Barcode::GetTruncate() {
   return JSObject()->TryBoolean(XFA_Attribute::Truncate, true);
 }
 
-Optional<int8_t> CXFA_Barcode::GetWideNarrowRatio() {
-  Optional<WideString> wsWideNarrowRatio =
+absl::optional<int8_t> CXFA_Barcode::GetWideNarrowRatio() {
+  absl::optional<WideString> wsWideNarrowRatio =
       JSObject()->TryCData(XFA_Attribute::WideNarrowRatio, true);
-  if (!wsWideNarrowRatio)
-    return {};
+  if (!wsWideNarrowRatio.has_value())
+    return absl::nullopt;
 
-  Optional<size_t> ptPos = wsWideNarrowRatio->Find(':');
-  if (!ptPos)
-    return {static_cast<int8_t>(FXSYS_wtoi(wsWideNarrowRatio->c_str()))};
+  absl::optional<size_t> ptPos = wsWideNarrowRatio->Find(':');
+  if (!ptPos.has_value())
+    return static_cast<int8_t>(FXSYS_wtoi(wsWideNarrowRatio->c_str()));
 
   int32_t fB = FXSYS_wtoi(
-      wsWideNarrowRatio->Last(wsWideNarrowRatio->GetLength() - (*ptPos + 1))
+      wsWideNarrowRatio
+          ->Last(wsWideNarrowRatio->GetLength() - (ptPos.value() + 1))
           .c_str());
   if (!fB)
-    return {0};
+    return 0;
 
-  int32_t fA = FXSYS_wtoi(wsWideNarrowRatio->First(*ptPos).c_str());
+  int32_t fA = FXSYS_wtoi(wsWideNarrowRatio->First(ptPos.value()).c_str());
   float result = static_cast<float>(fA) / static_cast<float>(fB);
-  return {static_cast<int8_t>(result)};
+  return static_cast<int8_t>(result);
 }
diff --git a/xfa/fxfa/parser/cxfa_barcode.h b/xfa/fxfa/parser/cxfa_barcode.h
index 219e8b3..a297ec0 100644
--- a/xfa/fxfa/parser/cxfa_barcode.h
+++ b/xfa/fxfa/parser/cxfa_barcode.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,29 +7,34 @@
 #ifndef XFA_FXFA_PARSER_CXFA_BARCODE_H_
 #define XFA_FXFA_PARSER_CXFA_BARCODE_H_
 
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
 class CXFA_Barcode final : public CXFA_Node {
  public:
-  CXFA_Barcode(CXFA_Document* doc, XFA_PacketType packet);
+  static CXFA_Barcode* FromNode(CXFA_Node* pNode);
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Barcode() override;
 
   XFA_FFWidgetType GetDefaultFFWidgetType() const override;
 
   WideString GetBarcodeType();
-  Optional<WideString> GetCharEncoding();
-  Optional<bool> GetChecksum();
-  Optional<int32_t> GetDataLength();
-  Optional<char> GetStartChar();
-  Optional<char> GetEndChar();
-  Optional<int32_t> GetECLevel();
-  Optional<int32_t> GetModuleWidth();
-  Optional<int32_t> GetModuleHeight();
-  Optional<bool> GetPrintChecksum();
-  Optional<XFA_AttributeValue> GetTextLocation();
-  Optional<bool> GetTruncate();
-  Optional<int8_t> GetWideNarrowRatio();
+  absl::optional<WideString> GetCharEncoding();
+  absl::optional<bool> GetChecksum();
+  absl::optional<int32_t> GetDataLength();
+  absl::optional<char> GetStartChar();
+  absl::optional<char> GetEndChar();
+  absl::optional<int32_t> GetECLevel();
+  absl::optional<int32_t> GetModuleWidth();
+  absl::optional<int32_t> GetModuleHeight();
+  absl::optional<bool> GetPrintChecksum();
+  absl::optional<XFA_AttributeValue> GetTextLocation();
+  absl::optional<bool> GetTruncate();
+  absl::optional<int8_t> GetWideNarrowRatio();
+
+ private:
+  CXFA_Barcode(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_BARCODE_H_
diff --git a/xfa/fxfa/parser/cxfa_base.cpp b/xfa/fxfa/parser/cxfa_base.cpp
index c5d0f88..80cf7d9 100644
--- a/xfa/fxfa/parser/cxfa_base.cpp
+++ b/xfa/fxfa/parser/cxfa_base.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_base.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Base::CXFA_Base(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Base,
                 {},
                 kBaseAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Base::~CXFA_Base() = default;
diff --git a/xfa/fxfa/parser/cxfa_base.h b/xfa/fxfa/parser/cxfa_base.h
index 3f1c9ba..b27877f 100644
--- a/xfa/fxfa/parser/cxfa_base.h
+++ b/xfa/fxfa/parser/cxfa_base.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Base final : public CXFA_Node {
  public:
-  CXFA_Base(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Base() override;
+
+ private:
+  CXFA_Base(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_BASE_H_
diff --git a/xfa/fxfa/parser/cxfa_batchoutput.cpp b/xfa/fxfa/parser/cxfa_batchoutput.cpp
index daec03f..2b3dc6d 100644
--- a/xfa/fxfa/parser/cxfa_batchoutput.cpp
+++ b/xfa/fxfa/parser/cxfa_batchoutput.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_batchoutput.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_BatchOutput::CXFA_BatchOutput(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::BatchOutput,
                 {},
                 kBatchOutputAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_BatchOutput::~CXFA_BatchOutput() = default;
diff --git a/xfa/fxfa/parser/cxfa_batchoutput.h b/xfa/fxfa/parser/cxfa_batchoutput.h
index 8d1de09..cdcd4e3 100644
--- a/xfa/fxfa/parser/cxfa_batchoutput.h
+++ b/xfa/fxfa/parser/cxfa_batchoutput.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_BatchOutput final : public CXFA_Node {
  public:
-  CXFA_BatchOutput(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_BatchOutput() override;
+
+ private:
+  CXFA_BatchOutput(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_BATCHOUTPUT_H_
diff --git a/xfa/fxfa/parser/cxfa_behavioroverride.cpp b/xfa/fxfa/parser/cxfa_behavioroverride.cpp
index 86ea80b..1c0b247 100644
--- a/xfa/fxfa/parser/cxfa_behavioroverride.cpp
+++ b/xfa/fxfa/parser/cxfa_behavioroverride.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_behavioroverride.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                              XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::BehaviorOverride,
                 {},
                 kBehaviorOverrideAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_BehaviorOverride::~CXFA_BehaviorOverride() = default;
diff --git a/xfa/fxfa/parser/cxfa_behavioroverride.h b/xfa/fxfa/parser/cxfa_behavioroverride.h
index d7dce1b..0bab96a 100644
--- a/xfa/fxfa/parser/cxfa_behavioroverride.h
+++ b/xfa/fxfa/parser/cxfa_behavioroverride.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_BehaviorOverride final : public CXFA_Node {
  public:
-  CXFA_BehaviorOverride(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_BehaviorOverride() override;
+
+ private:
+  CXFA_BehaviorOverride(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_BEHAVIOROVERRIDE_H_
diff --git a/xfa/fxfa/parser/cxfa_bind.cpp b/xfa/fxfa/parser/cxfa_bind.cpp
index 87f01e8..5bc093d 100644
--- a/xfa/fxfa/parser/cxfa_bind.cpp
+++ b/xfa/fxfa/parser/cxfa_bind.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,13 +8,13 @@
 
 #include "fxjs/xfa/cjx_node.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_picture.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kBindPropertyData[] = {
-    {XFA_Element::Picture, 1, 0},
+    {XFA_Element::Picture, 1, {}},
 };
 
 const CXFA_Node::AttributeData kBindAttributeData[] = {
@@ -35,18 +35,19 @@
 CXFA_Bind::CXFA_Bind(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_SourceSet | XFA_XDPPACKET_Template |
-                 XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kSourceSet, XFA_XDPPACKET::kTemplate,
+                 XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Bind,
                 kBindPropertyData,
                 kBindAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Bind::~CXFA_Bind() = default;
 
-WideString CXFA_Bind::GetPicture() {
-  CXFA_Picture* pPicture =
-      GetChild<CXFA_Picture>(0, XFA_Element::Picture, false);
+WideString CXFA_Bind::GetPicture() const {
+  const auto* pPicture = GetChild<CXFA_Picture>(0, XFA_Element::Picture, false);
   return pPicture ? pPicture->JSObject()->GetContent(false) : WideString();
 }
diff --git a/xfa/fxfa/parser/cxfa_bind.h b/xfa/fxfa/parser/cxfa_bind.h
index f6331d6..33e09de 100644
--- a/xfa/fxfa/parser/cxfa_bind.h
+++ b/xfa/fxfa/parser/cxfa_bind.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,10 +11,13 @@
 
 class CXFA_Bind final : public CXFA_Node {
  public:
-  CXFA_Bind(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Bind() override;
 
-  WideString GetPicture();
+  WideString GetPicture() const;
+
+ private:
+  CXFA_Bind(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_BIND_H_
diff --git a/xfa/fxfa/parser/cxfa_binditems.cpp b/xfa/fxfa/parser/cxfa_binditems.cpp
index 44badb8..d890fe4 100644
--- a/xfa/fxfa/parser/cxfa_binditems.cpp
+++ b/xfa/fxfa/parser/cxfa_binditems.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_binditems.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,12 +23,14 @@
 CXFA_BindItems::CXFA_BindItems(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::BindItems,
                 {},
                 kBindItemsAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_BindItems::~CXFA_BindItems() = default;
 
diff --git a/xfa/fxfa/parser/cxfa_binditems.h b/xfa/fxfa/parser/cxfa_binditems.h
index 34a4b67..36cebdb 100644
--- a/xfa/fxfa/parser/cxfa_binditems.h
+++ b/xfa/fxfa/parser/cxfa_binditems.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,12 +11,15 @@
 
 class CXFA_BindItems final : public CXFA_Node {
  public:
-  CXFA_BindItems(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_BindItems() override;
 
   WideString GetLabelRef();
   WideString GetValueRef();
   WideString GetRef();
+
+ private:
+  CXFA_BindItems(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_BINDITEMS_H_
diff --git a/xfa/fxfa/parser/cxfa_bookend.cpp b/xfa/fxfa/parser/cxfa_bookend.cpp
index 143a484..4525166 100644
--- a/xfa/fxfa/parser/cxfa_bookend.cpp
+++ b/xfa/fxfa/parser/cxfa_bookend.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_bookend.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -24,11 +24,13 @@
 CXFA_Bookend::CXFA_Bookend(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Bookend,
                 {},
                 kBookendAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Bookend::~CXFA_Bookend() = default;
diff --git a/xfa/fxfa/parser/cxfa_bookend.h b/xfa/fxfa/parser/cxfa_bookend.h
index 1c68763..0bd96f9 100644
--- a/xfa/fxfa/parser/cxfa_bookend.h
+++ b/xfa/fxfa/parser/cxfa_bookend.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Bookend final : public CXFA_Node {
  public:
-  CXFA_Bookend(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Bookend() override;
+
+ private:
+  CXFA_Bookend(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_BOOKEND_H_
diff --git a/xfa/fxfa/parser/cxfa_boolean.cpp b/xfa/fxfa/parser/cxfa_boolean.cpp
index 919d679..f949d2b 100644
--- a/xfa/fxfa/parser/cxfa_boolean.cpp
+++ b/xfa/fxfa/parser/cxfa_boolean.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_boolean.h"
 
 #include "fxjs/xfa/cjx_boolean.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,12 +23,14 @@
 CXFA_Boolean::CXFA_Boolean(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_SourceSet | XFA_XDPPACKET_Template |
-                 XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kSourceSet, XFA_XDPPACKET::kTemplate,
+                 XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Boolean,
                 {},
                 kBooleanAttributeData,
-                pdfium::MakeUnique<CJX_Boolean>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Boolean>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
-CXFA_Boolean::~CXFA_Boolean() {}
+CXFA_Boolean::~CXFA_Boolean() = default;
diff --git a/xfa/fxfa/parser/cxfa_boolean.h b/xfa/fxfa/parser/cxfa_boolean.h
index 60b8e5e..96de8b5 100644
--- a/xfa/fxfa/parser/cxfa_boolean.h
+++ b/xfa/fxfa/parser/cxfa_boolean.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Boolean final : public CXFA_Node {
  public:
-  CXFA_Boolean(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Boolean() override;
+
+ private:
+  CXFA_Boolean(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_BOOLEAN_H_
diff --git a/xfa/fxfa/parser/cxfa_border.cpp b/xfa/fxfa/parser/cxfa_border.cpp
index 1d315ad..035bbad 100644
--- a/xfa/fxfa/parser/cxfa_border.cpp
+++ b/xfa/fxfa/parser/cxfa_border.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,14 @@
 #include "xfa/fxfa/parser/cxfa_border.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kBorderPropertyData[] = {
-    {XFA_Element::Margin, 1, 0}, {XFA_Element::Edge, 4, 0},
-    {XFA_Element::Corner, 4, 0}, {XFA_Element::Fill, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Margin, 1, {}}, {XFA_Element::Edge, 4, {}},
+    {XFA_Element::Corner, 4, {}}, {XFA_Element::Fill, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kBorderAttributeData[] = {
@@ -35,11 +35,13 @@
 CXFA_Border::CXFA_Border(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Rectangle(doc,
                      packet,
-                     (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                     {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                      XFA_ObjectType::Node,
                      XFA_Element::Border,
                      kBorderPropertyData,
                      kBorderAttributeData,
-                     pdfium::MakeUnique<CJX_Node>(this)) {}
+                     cppgc::MakeGarbageCollected<CJX_Node>(
+                         doc->GetHeap()->GetAllocationHandle(),
+                         this)) {}
 
 CXFA_Border::~CXFA_Border() = default;
diff --git a/xfa/fxfa/parser/cxfa_border.h b/xfa/fxfa/parser/cxfa_border.h
index 6071a77..ea23bb5 100644
--- a/xfa/fxfa/parser/cxfa_border.h
+++ b/xfa/fxfa/parser/cxfa_border.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Border final : public CXFA_Rectangle {
  public:
-  CXFA_Border(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Border() override;
+
+ private:
+  CXFA_Border(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_BORDER_H_
diff --git a/xfa/fxfa/parser/cxfa_box.cpp b/xfa/fxfa/parser/cxfa_box.cpp
index 5ba01d3..cd644aa 100644
--- a/xfa/fxfa/parser/cxfa_box.cpp
+++ b/xfa/fxfa/parser/cxfa_box.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_box.h"
 
+#include <math.h>
+
 #include <algorithm>
 #include <utility>
 
 #include "fxjs/xfa/cjx_object.h"
+#include "third_party/base/notreached.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
+#include "xfa/fgas/graphics/cfgas_gepattern.h"
+#include "xfa/fgas/graphics/cfgas_geshading.h"
 #include "xfa/fxfa/parser/cxfa_corner.h"
 #include "xfa/fxfa/parser/cxfa_edge.h"
 #include "xfa/fxfa/parser/cxfa_fill.h"
@@ -17,10 +25,6 @@
 #include "xfa/fxfa/parser/cxfa_measurement.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_rectangle.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
-#include "xfa/fxgraphics/cxfa_gepattern.h"
-#include "xfa/fxgraphics/cxfa_geshading.h"
-#include "xfa/fxgraphics/cxfa_graphics.h"
 
 namespace {
 
@@ -59,12 +63,12 @@
 
 CXFA_Box::CXFA_Box(CXFA_Document* pDoc,
                    XFA_PacketType ePacket,
-                   uint32_t validPackets,
+                   Mask<XFA_XDPPACKET> validPackets,
                    XFA_ObjectType oType,
                    XFA_Element eType,
                    pdfium::span<const PropertyData> properties,
                    pdfium::span<const AttributeData> attributes,
-                   std::unique_ptr<CJX_Object> js_node)
+                   CJX_Object* js_node)
     : CXFA_Node(pDoc,
                 ePacket,
                 validPackets,
@@ -72,7 +76,7 @@
                 eType,
                 properties,
                 attributes,
-                std::move(js_node)) {}
+                js_node) {}
 
 CXFA_Box::~CXFA_Box() = default;
 
@@ -86,15 +90,17 @@
       .value_or(XFA_AttributeValue::Visible);
 }
 
-int32_t CXFA_Box::CountEdges() {
+size_t CXFA_Box::CountEdges() {
   return CountChildren(XFA_Element::Edge, false);
 }
 
-CXFA_Edge* CXFA_Box::GetEdgeIfExists(int32_t nIndex) {
-  if (nIndex == 0)
-    return JSObject()->GetOrCreateProperty<CXFA_Edge>(nIndex,
-                                                      XFA_Element::Edge);
-  return JSObject()->GetProperty<CXFA_Edge>(nIndex, XFA_Element::Edge);
+CXFA_Edge* CXFA_Box::GetEdgeIfExists(size_t nIndex) {
+  if (nIndex == 0) {
+    return JSObject()->GetOrCreateProperty<CXFA_Edge>(
+        pdfium::base::checked_cast<int32_t>(nIndex), XFA_Element::Edge);
+  }
+  return JSObject()->GetProperty<CXFA_Edge>(
+      pdfium::base::checked_cast<int32_t>(nIndex), XFA_Element::Edge);
 }
 
 std::vector<CXFA_Stroke*> CXFA_Box::GetStrokes() {
@@ -105,11 +111,11 @@
   return JSObject()->GetBoolean(XFA_Attribute::Circular);
 }
 
-Optional<int32_t> CXFA_Box::GetStartAngle() {
+absl::optional<int32_t> CXFA_Box::GetStartAngle() {
   return JSObject()->TryInteger(XFA_Attribute::StartAngle, false);
 }
 
-Optional<int32_t> CXFA_Box::GetSweepAngle() {
+absl::optional<int32_t> CXFA_Box::GetSweepAngle() {
   return JSObject()->TryInteger(XFA_Attribute::SweepAngle, false);
 }
 
@@ -178,7 +184,7 @@
   return strokes;
 }
 
-void CXFA_Box::Draw(CXFA_Graphics* pGS,
+void CXFA_Box::Draw(CFGAS_GEGraphics* pGS,
                     const CFX_RectF& rtWidget,
                     const CFX_Matrix& matrix,
                     bool forceRound) {
@@ -201,12 +207,12 @@
   } else if (type == XFA_Element::Rectangle || type == XFA_Element::Border) {
     ToRectangle(this)->Draw(strokes, pGS, rtWidget, matrix);
   } else {
-    NOTREACHED();
+    NOTREACHED_NORETURN();
   }
 }
 
 void CXFA_Box::DrawFill(const std::vector<CXFA_Stroke*>& strokes,
-                        CXFA_Graphics* pGS,
+                        CFGAS_GEGraphics* pGS,
                         CFX_RectF rtWidget,
                         const CFX_Matrix& matrix,
                         bool forceRound) {
@@ -214,13 +220,12 @@
   if (!fill || !fill->IsVisible())
     return;
 
-  pGS->SaveGraphState();
-
-  CXFA_GEPath fillPath;
+  CFGAS_GEPath fillPath;
   XFA_Element type = GetElementType();
+  CFGAS_GEGraphics::StateRestorer restorer(pGS);
   if (type == XFA_Element::Arc || forceRound) {
     CXFA_Edge* edge = GetEdgeIfExists(0);
-    float fThickness = std::fmax(0.0, edge ? edge->GetThickness() : 0);
+    float fThickness = fmax(0.0, edge ? edge->GetThickness() : 0);
     float fHalf = fThickness / 2;
     XFA_AttributeValue iHand = GetHand();
     if (iHand == XFA_AttributeValue::Left)
@@ -232,20 +237,17 @@
   } 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);
-  pGS->RestoreGraphState();
+  fill->Draw(pGS, fillPath, rtWidget, matrix);
 }
 
 void CXFA_Box::GetPathArcOrRounded(CFX_RectF rtDraw,
                                    bool forceRound,
-                                   CXFA_GEPath* fillPath) {
-  float a, b;
-  a = rtDraw.width / 2.0f;
-  b = rtDraw.height / 2.0f;
+                                   CFGAS_GEPath* fillPath) {
+  float a = rtDraw.width / 2.0f;
+  float b = rtDraw.height / 2.0f;
   if (IsCircular() || forceRound)
     a = b = std::min(a, b);
 
@@ -254,19 +256,19 @@
   rtDraw.top = center.y - b;
   rtDraw.width = a + a;
   rtDraw.height = b + b;
-  Optional<int32_t> startAngle = GetStartAngle();
-  Optional<int32_t> sweepAngle = GetSweepAngle();
-  if (!startAngle && !sweepAngle) {
+  absl::optional<int32_t> startAngle = GetStartAngle();
+  absl::optional<int32_t> sweepAngle = GetSweepAngle();
+  if (!startAngle.has_value() && !sweepAngle.has_value()) {
     fillPath->AddEllipse(rtDraw);
     return;
   }
 
   fillPath->AddArc(rtDraw.TopLeft(), rtDraw.Size(),
-                   -startAngle.value_or(0) * FX_PI / 180.0f,
-                   -sweepAngle.value_or(360) * FX_PI / 180.0f);
+                   -startAngle.value_or(0) * FXSYS_PI / 180.0f,
+                   -sweepAngle.value_or(360) * FXSYS_PI / 180.0f);
 }
 
-void CXFA_Box::StrokeArcOrRounded(CXFA_Graphics* pGS,
+void CXFA_Box::StrokeArcOrRounded(CFGAS_GEGraphics* pGS,
                                   CFX_RectF rtWidget,
                                   const CFX_Matrix& matrix,
                                   bool forceRound) {
@@ -299,18 +301,17 @@
     if (fHalf < 0.001f)
       return;
 
-    CXFA_GEPath arcPath;
+    CFGAS_GEPath arcPath;
     GetPathArcOrRounded(rtWidget, forceRound, &arcPath);
     if (edge)
-      edge->Stroke(&arcPath, pGS, matrix);
+      edge->Stroke(pGS, arcPath, matrix);
     return;
   }
-  pGS->SaveGraphState();
+  CFGAS_GEGraphics::StateRestorer restorer(pGS);
   pGS->SetLineWidth(fHalf);
 
-  float a, b;
-  a = rtWidget.width / 2.0f;
-  b = rtWidget.height / 2.0f;
+  float a = rtWidget.width / 2.0f;
+  float b = rtWidget.height / 2.0f;
   if (forceRound) {
     a = std::min(a, b);
     b = a;
@@ -322,30 +323,29 @@
   rtWidget.width = a + a;
   rtWidget.height = b + b;
 
-  CXFA_GEPath arcPath;
-  arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), 3.0f * FX_PI / 4.0f,
-                 FX_PI);
+  CFGAS_GEPath arcPath;
+  arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), 3.0f * FXSYS_PI / 4.0f,
+                 FXSYS_PI);
 
-  pGS->SetStrokeColor(CXFA_GEColor(0xFF808080));
-  pGS->StrokePath(&arcPath, &matrix);
+  pGS->SetStrokeColor(CFGAS_GEColor(0xFF808080));
+  pGS->StrokePath(arcPath, matrix);
   arcPath.Clear();
-  arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), -1.0f * FX_PI / 4.0f,
-                 FX_PI);
+  arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), -1.0f * FXSYS_PI / 4.0f,
+                 FXSYS_PI);
 
-  pGS->SetStrokeColor(CXFA_GEColor(0xFFFFFFFF));
-  pGS->StrokePath(&arcPath, &matrix);
+  pGS->SetStrokeColor(CFGAS_GEColor(0xFFFFFFFF));
+  pGS->StrokePath(arcPath, matrix);
   rtWidget.Deflate(fHalf, fHalf);
   arcPath.Clear();
-  arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), 3.0f * FX_PI / 4.0f,
-                 FX_PI);
+  arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), 3.0f * FXSYS_PI / 4.0f,
+                 FXSYS_PI);
 
-  pGS->SetStrokeColor(CXFA_GEColor(0xFF404040));
-  pGS->StrokePath(&arcPath, &matrix);
+  pGS->SetStrokeColor(CFGAS_GEColor(0xFF404040));
+  pGS->StrokePath(arcPath, matrix);
   arcPath.Clear();
-  arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), -1.0f * FX_PI / 4.0f,
-                 FX_PI);
+  arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), -1.0f * FXSYS_PI / 4.0f,
+                 FXSYS_PI);
 
-  pGS->SetStrokeColor(CXFA_GEColor(0xFFC0C0C0));
-  pGS->StrokePath(&arcPath, &matrix);
-  pGS->RestoreGraphState();
+  pGS->SetStrokeColor(CFGAS_GEColor(0xFFC0C0C0));
+  pGS->StrokePath(arcPath, matrix);
 }
diff --git a/xfa/fxfa/parser/cxfa_box.h b/xfa/fxfa/parser/cxfa_box.h
index cbda771..5012094 100644
--- a/xfa/fxfa/parser/cxfa_box.h
+++ b/xfa/fxfa/parser/cxfa_box.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,34 +7,33 @@
 #ifndef XFA_FXFA_PARSER_CXFA_BOX_H_
 #define XFA_FXFA_PARSER_CXFA_BOX_H_
 
-#include <memory>
 #include <tuple>
 #include <vector>
 
 #include "core/fxcrt/fx_coordinates.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
 
+class CFGAS_GEGraphics;
 class CXFA_Edge;
 class CXFA_Fill;
-class CXFA_Graphics;
-class CXFA_Margin;
 class CXFA_Stroke;
 
 class CXFA_Box : public CXFA_Node {
  public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Box() override;
 
   XFA_AttributeValue GetPresence();
   std::tuple<XFA_AttributeValue, bool, float> Get3DStyle();
 
-  int32_t CountEdges();
-  CXFA_Edge* GetEdgeIfExists(int32_t nIndex);
+  size_t CountEdges();
+  CXFA_Edge* GetEdgeIfExists(size_t nIndex);
   CXFA_Fill* GetOrCreateFillIfPossible();
 
   std::vector<CXFA_Stroke*> GetStrokes();
 
-  void Draw(CXFA_Graphics* pGS,
+  void Draw(CFGAS_GEGraphics* pGS,
             const CFX_RectF& rtWidget,
             const CFX_Matrix& matrix,
             bool forceRound);
@@ -42,33 +41,33 @@
  protected:
   CXFA_Box(CXFA_Document* pDoc,
            XFA_PacketType ePacket,
-           uint32_t validPackets,
+           Mask<XFA_XDPPACKET> validPackets,
            XFA_ObjectType oType,
            XFA_Element eType,
            pdfium::span<const PropertyData> properties,
            pdfium::span<const AttributeData> attributes,
-           std::unique_ptr<CJX_Object> js_node);
+           CJX_Object* js_node);
 
   XFA_AttributeValue GetHand();
 
  private:
   bool IsCircular();
-  Optional<int32_t> GetStartAngle();
-  Optional<int32_t> GetSweepAngle();
+  absl::optional<int32_t> GetStartAngle();
+  absl::optional<int32_t> GetSweepAngle();
 
   std::vector<CXFA_Stroke*> GetStrokesInternal(bool bNull);
   void DrawFill(const std::vector<CXFA_Stroke*>& strokes,
-                CXFA_Graphics* pGS,
+                CFGAS_GEGraphics* pGS,
                 CFX_RectF rtWidget,
                 const CFX_Matrix& matrix,
                 bool forceRound);
-  void StrokeArcOrRounded(CXFA_Graphics* pGS,
+  void StrokeArcOrRounded(CFGAS_GEGraphics* pGS,
                           CFX_RectF rtWidget,
                           const CFX_Matrix& matrix,
                           bool forceRound);
   void GetPathArcOrRounded(CFX_RectF rtDraw,
                            bool forceRound,
-                           CXFA_GEPath* fillPath);
+                           CFGAS_GEPath* fillPath);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_BOX_H_
diff --git a/xfa/fxfa/parser/cxfa_break.cpp b/xfa/fxfa/parser/cxfa_break.cpp
index e02fa1e..0fa322c 100644
--- a/xfa/fxfa/parser/cxfa_break.cpp
+++ b/xfa/fxfa/parser/cxfa_break.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_break.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kBreakPropertyData[] = {
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kBreakAttributeData[] = {
@@ -38,11 +38,13 @@
 CXFA_Break::CXFA_Break(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Break,
                 kBreakPropertyData,
                 kBreakAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Break::~CXFA_Break() = default;
diff --git a/xfa/fxfa/parser/cxfa_break.h b/xfa/fxfa/parser/cxfa_break.h
index 4bf7dde..0df37ff 100644
--- a/xfa/fxfa/parser/cxfa_break.h
+++ b/xfa/fxfa/parser/cxfa_break.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Break final : public CXFA_Node {
  public:
-  CXFA_Break(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Break() override;
+
+ private:
+  CXFA_Break(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_BREAK_H_
diff --git a/xfa/fxfa/parser/cxfa_breakafter.cpp b/xfa/fxfa/parser/cxfa_breakafter.cpp
index 252a607..601c089 100644
--- a/xfa/fxfa/parser/cxfa_breakafter.cpp
+++ b/xfa/fxfa/parser/cxfa_breakafter.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_breakafter.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kBreakAfterPropertyData[] = {
-    {XFA_Element::Script, 1, 0},
+    {XFA_Element::Script, 1, {}},
 };
 
 const CXFA_Node::AttributeData kBreakAfterAttributeData[] = {
@@ -32,11 +32,13 @@
 CXFA_BreakAfter::CXFA_BreakAfter(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::BreakAfter,
                 kBreakAfterPropertyData,
                 kBreakAfterAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_BreakAfter::~CXFA_BreakAfter() = default;
diff --git a/xfa/fxfa/parser/cxfa_breakafter.h b/xfa/fxfa/parser/cxfa_breakafter.h
index c607ef5..a28761b 100644
--- a/xfa/fxfa/parser/cxfa_breakafter.h
+++ b/xfa/fxfa/parser/cxfa_breakafter.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_BreakAfter final : public CXFA_Node {
  public:
-  CXFA_BreakAfter(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_BreakAfter() override;
+
+ private:
+  CXFA_BreakAfter(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_BREAKAFTER_H_
diff --git a/xfa/fxfa/parser/cxfa_breakbefore.cpp b/xfa/fxfa/parser/cxfa_breakbefore.cpp
index 0518ae1..d1194a2 100644
--- a/xfa/fxfa/parser/cxfa_breakbefore.cpp
+++ b/xfa/fxfa/parser/cxfa_breakbefore.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_breakbefore.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kBreakBeforePropertyData[] = {
-    {XFA_Element::Script, 1, 0},
+    {XFA_Element::Script, 1, {}},
 };
 
 const CXFA_Node::AttributeData kBreakBeforeAttributeData[] = {
@@ -32,11 +32,13 @@
 CXFA_BreakBefore::CXFA_BreakBefore(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::BreakBefore,
                 kBreakBeforePropertyData,
                 kBreakBeforeAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_BreakBefore::~CXFA_BreakBefore() = default;
diff --git a/xfa/fxfa/parser/cxfa_breakbefore.h b/xfa/fxfa/parser/cxfa_breakbefore.h
index 1b6dc63..0f714c9 100644
--- a/xfa/fxfa/parser/cxfa_breakbefore.h
+++ b/xfa/fxfa/parser/cxfa_breakbefore.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_BreakBefore final : public CXFA_Node {
  public:
-  CXFA_BreakBefore(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_BreakBefore() override;
+
+ private:
+  CXFA_BreakBefore(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_BREAKBEFORE_H_
diff --git a/xfa/fxfa/parser/cxfa_button.cpp b/xfa/fxfa/parser/cxfa_button.cpp
index 4b35aad..29ea2c1 100644
--- a/xfa/fxfa/parser/cxfa_button.cpp
+++ b/xfa/fxfa/parser/cxfa_button.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_button.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kButtonPropertyData[] = {
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kButtonAttributeData[] = {
@@ -25,15 +25,24 @@
 
 }  // namespace
 
+// static
+CXFA_Button* CXFA_Button::FromNode(CXFA_Node* pNode) {
+  return pNode && pNode->GetElementType() == XFA_Element::Button
+             ? static_cast<CXFA_Button*>(pNode)
+             : nullptr;
+}
+
 CXFA_Button::CXFA_Button(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Button,
                 kButtonPropertyData,
                 kButtonAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Button::~CXFA_Button() = default;
 
diff --git a/xfa/fxfa/parser/cxfa_button.h b/xfa/fxfa/parser/cxfa_button.h
index 1eddd65..d011e9b 100644
--- a/xfa/fxfa/parser/cxfa_button.h
+++ b/xfa/fxfa/parser/cxfa_button.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,12 +11,17 @@
 
 class CXFA_Button final : public CXFA_Node {
  public:
-  CXFA_Button(CXFA_Document* doc, XFA_PacketType packet);
+  static CXFA_Button* FromNode(CXFA_Node* pNode);
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Button() override;
 
   XFA_FFWidgetType GetDefaultFFWidgetType() const override;
 
   XFA_AttributeValue GetHighlight();
+
+ private:
+  CXFA_Button(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_BUTTON_H_
diff --git a/xfa/fxfa/parser/cxfa_cache.cpp b/xfa/fxfa/parser/cxfa_cache.cpp
index 8b51c02..9287c66 100644
--- a/xfa/fxfa/parser/cxfa_cache.cpp
+++ b/xfa/fxfa/parser/cxfa_cache.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_cache.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kCachePropertyData[] = {
-    {XFA_Element::TemplateCache, 1, 0},
+    {XFA_Element::TemplateCache, 1, {}},
 };
 
 const CXFA_Node::AttributeData kCacheAttributeData[] = {
@@ -25,11 +25,13 @@
 CXFA_Cache::CXFA_Cache(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Cache,
                 kCachePropertyData,
                 kCacheAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
-CXFA_Cache::~CXFA_Cache() {}
+CXFA_Cache::~CXFA_Cache() = default;
diff --git a/xfa/fxfa/parser/cxfa_cache.h b/xfa/fxfa/parser/cxfa_cache.h
index 430163a..8b0a41a 100644
--- a/xfa/fxfa/parser/cxfa_cache.h
+++ b/xfa/fxfa/parser/cxfa_cache.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Cache final : public CXFA_Node {
  public:
-  CXFA_Cache(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Cache() override;
+
+ private:
+  CXFA_Cache(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CACHE_H_
diff --git a/xfa/fxfa/parser/cxfa_calculate.cpp b/xfa/fxfa/parser/cxfa_calculate.cpp
index ce79864..b0c2af4 100644
--- a/xfa/fxfa/parser/cxfa_calculate.cpp
+++ b/xfa/fxfa/parser/cxfa_calculate.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_calculate.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_message.h"
 #include "xfa/fxfa/parser/cxfa_script.h"
 #include "xfa/fxfa/parser/cxfa_text.h"
@@ -15,9 +15,9 @@
 namespace {
 
 const CXFA_Node::PropertyData kCalculatePropertyData[] = {
-    {XFA_Element::Message, 1, 0},
-    {XFA_Element::Script, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Message, 1, {}},
+    {XFA_Element::Script, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kCalculateAttributeData[] = {
@@ -33,12 +33,14 @@
 CXFA_Calculate::CXFA_Calculate(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Calculate,
                 kCalculatePropertyData,
                 kCalculateAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Calculate::~CXFA_Calculate() = default;
 
@@ -52,11 +54,11 @@
   return GetChild<CXFA_Script>(0, XFA_Element::Script, false);
 }
 
-WideString CXFA_Calculate::GetMessageText() {
-  CXFA_Message* pNode = GetChild<CXFA_Message>(0, XFA_Element::Message, false);
+WideString CXFA_Calculate::GetMessageText() const {
+  const auto* pNode = GetChild<CXFA_Message>(0, XFA_Element::Message, false);
   if (!pNode)
     return WideString();
 
-  CXFA_Text* text = pNode->GetChild<CXFA_Text>(0, XFA_Element::Text, false);
+  const auto* text = pNode->GetChild<CXFA_Text>(0, XFA_Element::Text, false);
   return text ? text->GetContent() : WideString();
 }
diff --git a/xfa/fxfa/parser/cxfa_calculate.h b/xfa/fxfa/parser/cxfa_calculate.h
index a3934ac..58332e3 100644
--- a/xfa/fxfa/parser/cxfa_calculate.h
+++ b/xfa/fxfa/parser/cxfa_calculate.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,12 +13,15 @@
 
 class CXFA_Calculate final : public CXFA_Node {
  public:
-  CXFA_Calculate(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Calculate() override;
 
   XFA_AttributeValue GetOverride();
   CXFA_Script* GetScriptIfExists();
-  WideString GetMessageText();
+  WideString GetMessageText() const;
+
+ private:
+  CXFA_Calculate(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CALCULATE_H_
diff --git a/xfa/fxfa/parser/cxfa_calendarsymbols.cpp b/xfa/fxfa/parser/cxfa_calendarsymbols.cpp
index 155a296..d6d0012 100644
--- a/xfa/fxfa/parser/cxfa_calendarsymbols.cpp
+++ b/xfa/fxfa/parser/cxfa_calendarsymbols.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #include "xfa/fxfa/parser/cxfa_calendarsymbols.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kCalendarSymbolsPropertyData[] = {
-    {XFA_Element::EraNames, 1, 0},
-    {XFA_Element::DayNames, 2, 0},
-    {XFA_Element::MeridiemNames, 1, 0},
-    {XFA_Element::MonthNames, 2, 0},
+    {XFA_Element::EraNames, 1, {}},
+    {XFA_Element::DayNames, 2, {}},
+    {XFA_Element::MeridiemNames, 1, {}},
+    {XFA_Element::MonthNames, 2, {}},
 };
 
 const CXFA_Node::AttributeData kCalendarSymbolsAttributeData[] = {
@@ -29,11 +29,13 @@
                                            XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::CalendarSymbols,
                 kCalendarSymbolsPropertyData,
                 kCalendarSymbolsAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_CalendarSymbols::~CXFA_CalendarSymbols() = default;
diff --git a/xfa/fxfa/parser/cxfa_calendarsymbols.h b/xfa/fxfa/parser/cxfa_calendarsymbols.h
index 82ecde4..5956752 100644
--- a/xfa/fxfa/parser/cxfa_calendarsymbols.h
+++ b/xfa/fxfa/parser/cxfa_calendarsymbols.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_CalendarSymbols final : public CXFA_Node {
  public:
-  CXFA_CalendarSymbols(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_CalendarSymbols() override;
+
+ private:
+  CXFA_CalendarSymbols(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CALENDARSYMBOLS_H_
diff --git a/xfa/fxfa/parser/cxfa_caption.cpp b/xfa/fxfa/parser/cxfa_caption.cpp
index 1fadb8d..3de3ff1 100644
--- a/xfa/fxfa/parser/cxfa_caption.cpp
+++ b/xfa/fxfa/parser/cxfa_caption.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_caption.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_font.h"
 #include "xfa/fxfa/parser/cxfa_margin.h"
 #include "xfa/fxfa/parser/cxfa_measurement.h"
@@ -16,9 +16,9 @@
 namespace {
 
 const CXFA_Node::PropertyData kCaptionPropertyData[] = {
-    {XFA_Element::Margin, 1, 0}, {XFA_Element::Para, 1, 0},
-    {XFA_Element::Font, 1, 0},   {XFA_Element::Value, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Margin, 1, {}}, {XFA_Element::Para, 1, {}},
+    {XFA_Element::Font, 1, {}},   {XFA_Element::Value, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kCaptionAttributeData[] = {
@@ -37,12 +37,14 @@
 CXFA_Caption::CXFA_Caption(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Caption,
                 kCaptionPropertyData,
                 kCaptionAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Caption::~CXFA_Caption() = default;
 
diff --git a/xfa/fxfa/parser/cxfa_caption.h b/xfa/fxfa/parser/cxfa_caption.h
index 9e7d38a..a00d6d4 100644
--- a/xfa/fxfa/parser/cxfa_caption.h
+++ b/xfa/fxfa/parser/cxfa_caption.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,7 +18,7 @@
   static constexpr XFA_AttributeValue kDefaultPlacementType =
       XFA_AttributeValue::Left;
 
-  CXFA_Caption(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Caption() override;
 
   bool IsVisible();
@@ -28,6 +28,9 @@
   CXFA_Margin* GetMarginIfExists();
   CXFA_Font* GetFontIfExists();
   CXFA_Value* GetValueIfExists();
+
+ private:
+  CXFA_Caption(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CAPTION_H_
diff --git a/xfa/fxfa/parser/cxfa_certificate.cpp b/xfa/fxfa/parser/cxfa_certificate.cpp
index 65b6941..0aae4a5 100644
--- a/xfa/fxfa/parser/cxfa_certificate.cpp
+++ b/xfa/fxfa/parser/cxfa_certificate.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_certificate.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_Certificate::CXFA_Certificate(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::TextNode,
                 XFA_Element::Certificate,
                 {},
                 kCertificateAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Certificate::~CXFA_Certificate() = default;
diff --git a/xfa/fxfa/parser/cxfa_certificate.h b/xfa/fxfa/parser/cxfa_certificate.h
index 4923d57..16ea9fe 100644
--- a/xfa/fxfa/parser/cxfa_certificate.h
+++ b/xfa/fxfa/parser/cxfa_certificate.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Certificate final : public CXFA_Node {
  public:
-  CXFA_Certificate(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Certificate() override;
+
+ private:
+  CXFA_Certificate(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CERTIFICATE_H_
diff --git a/xfa/fxfa/parser/cxfa_certificates.cpp b/xfa/fxfa/parser/cxfa_certificates.cpp
index 42b025e..162b420 100644
--- a/xfa/fxfa/parser/cxfa_certificates.cpp
+++ b/xfa/fxfa/parser/cxfa_certificates.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,14 @@
 #include "xfa/fxfa/parser/cxfa_certificates.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kCertificatesPropertyData[] = {
-    {XFA_Element::KeyUsage, 1, 0}, {XFA_Element::SubjectDNs, 1, 0},
-    {XFA_Element::Issuers, 1, 0},  {XFA_Element::Signing, 1, 0},
-    {XFA_Element::Oids, 1, 0},
+    {XFA_Element::KeyUsage, 1, {}}, {XFA_Element::SubjectDNs, 1, {}},
+    {XFA_Element::Issuers, 1, {}},  {XFA_Element::Signing, 1, {}},
+    {XFA_Element::Oids, 1, {}},
 };
 
 const CXFA_Node::AttributeData kCertificatesAttributeData[] = {
@@ -32,11 +32,13 @@
 CXFA_Certificates::CXFA_Certificates(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Certificates,
                 kCertificatesPropertyData,
                 kCertificatesAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Certificates::~CXFA_Certificates() = default;
diff --git a/xfa/fxfa/parser/cxfa_certificates.h b/xfa/fxfa/parser/cxfa_certificates.h
index 6817d03..054591c 100644
--- a/xfa/fxfa/parser/cxfa_certificates.h
+++ b/xfa/fxfa/parser/cxfa_certificates.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Certificates final : public CXFA_Node {
  public:
-  CXFA_Certificates(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Certificates() override;
+
+ private:
+  CXFA_Certificates(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CERTIFICATES_H_
diff --git a/xfa/fxfa/parser/cxfa_change.cpp b/xfa/fxfa/parser/cxfa_change.cpp
index ae8da63..641cd52 100644
--- a/xfa/fxfa/parser/cxfa_change.cpp
+++ b/xfa/fxfa/parser/cxfa_change.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_change.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Change::CXFA_Change(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Change,
                 {},
                 kChangeAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Change::~CXFA_Change() = default;
diff --git a/xfa/fxfa/parser/cxfa_change.h b/xfa/fxfa/parser/cxfa_change.h
index 2fa4ba9..1213756 100644
--- a/xfa/fxfa/parser/cxfa_change.h
+++ b/xfa/fxfa/parser/cxfa_change.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Change final : public CXFA_Node {
  public:
-  CXFA_Change(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Change() override;
+
+ private:
+  CXFA_Change(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CHANGE_H_
diff --git a/xfa/fxfa/parser/cxfa_checkbutton.cpp b/xfa/fxfa/parser/cxfa_checkbutton.cpp
index 903ce49..71fe990 100644
--- a/xfa/fxfa/parser/cxfa_checkbutton.cpp
+++ b/xfa/fxfa/parser/cxfa_checkbutton.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,14 @@
 #include "xfa/fxfa/parser/cxfa_checkbutton.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kCheckButtonPropertyData[] = {
-    {XFA_Element::Margin, 1, 0},
-    {XFA_Element::Border, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Margin, 1, {}},
+    {XFA_Element::Border, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kCheckButtonAttributeData[] = {
@@ -31,15 +31,24 @@
 
 }  // namespace
 
+// static
+CXFA_CheckButton* CXFA_CheckButton::FromNode(CXFA_Node* pNode) {
+  return pNode && pNode->GetElementType() == XFA_Element::CheckButton
+             ? static_cast<CXFA_CheckButton*>(pNode)
+             : nullptr;
+}
+
 CXFA_CheckButton::CXFA_CheckButton(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::CheckButton,
                 kCheckButtonPropertyData,
                 kCheckButtonAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_CheckButton::~CXFA_CheckButton() = default;
 
diff --git a/xfa/fxfa/parser/cxfa_checkbutton.h b/xfa/fxfa/parser/cxfa_checkbutton.h
index 3ca8b9e..6090e0b 100644
--- a/xfa/fxfa/parser/cxfa_checkbutton.h
+++ b/xfa/fxfa/parser/cxfa_checkbutton.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,9 @@
 
 class CXFA_CheckButton final : public CXFA_Node {
  public:
-  CXFA_CheckButton(CXFA_Document* doc, XFA_PacketType packet);
+  static CXFA_CheckButton* FromNode(CXFA_Node* pNode);
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_CheckButton() override;
 
   XFA_FFWidgetType GetDefaultFFWidgetType() const override;
@@ -19,6 +21,9 @@
   bool IsRound();
   bool IsAllowNeutral();
   XFA_AttributeValue GetMark();
+
+ private:
+  CXFA_CheckButton(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CHECKBUTTON_H_
diff --git a/xfa/fxfa/parser/cxfa_choicelist.cpp b/xfa/fxfa/parser/cxfa_choicelist.cpp
index 63e9882..61f70c6 100644
--- a/xfa/fxfa/parser/cxfa_choicelist.cpp
+++ b/xfa/fxfa/parser/cxfa_choicelist.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,14 @@
 #include "xfa/fxfa/parser/cxfa_choicelist.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kChoiceListPropertyData[] = {
-    {XFA_Element::Margin, 1, 0},
-    {XFA_Element::Border, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Margin, 1, {}},
+    {XFA_Element::Border, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kChoiceListAttributeData[] = {
@@ -33,12 +33,14 @@
 CXFA_ChoiceList::CXFA_ChoiceList(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::ChoiceList,
                 kChoiceListPropertyData,
                 kChoiceListAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ChoiceList::~CXFA_ChoiceList() = default;
 
diff --git a/xfa/fxfa/parser/cxfa_choicelist.h b/xfa/fxfa/parser/cxfa_choicelist.h
index cff1463..8cd08f5 100644
--- a/xfa/fxfa/parser/cxfa_choicelist.h
+++ b/xfa/fxfa/parser/cxfa_choicelist.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,11 +11,14 @@
 
 class CXFA_ChoiceList final : public CXFA_Node {
  public:
-  CXFA_ChoiceList(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ChoiceList() override;
 
   XFA_Element GetValueNodeType() const override;
   XFA_FFWidgetType GetDefaultFFWidgetType() const override;
+
+ private:
+  CXFA_ChoiceList(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CHOICELIST_H_
diff --git a/xfa/fxfa/parser/cxfa_color.cpp b/xfa/fxfa/parser/cxfa_color.cpp
index 477121b..044372d 100644
--- a/xfa/fxfa/parser/cxfa_color.cpp
+++ b/xfa/fxfa/parser/cxfa_color.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,13 +6,14 @@
 
 #include "xfa/fxfa/parser/cxfa_color.h"
 
+#include "core/fxcrt/fx_extension.h"
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kColorPropertyData[] = {
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kColorAttributeData[] = {
@@ -25,26 +26,84 @@
 
 }  // namespace
 
+// static
+FX_ARGB CXFA_Color::StringToFXARGB(WideStringView view) {
+  static constexpr FX_ARGB kDefaultValue = 0xff000000;
+  if (view.IsEmpty())
+    return kDefaultValue;
+
+  const wchar_t* str = view.unterminated_c_str();
+  size_t len = view.GetLength();
+  size_t cc = 0;
+  while (cc < len && FXSYS_iswspace(str[cc]))
+    cc++;
+
+  if (cc >= len)
+    return kDefaultValue;
+
+  uint8_t r = 0;
+  uint8_t g = 0;
+  uint8_t b = 0;
+  while (cc < len) {
+    if (str[cc] == ',' || !FXSYS_IsDecimalDigit(str[cc]))
+      break;
+
+    r = r * 10 + str[cc] - '0';
+    cc++;
+  }
+  if (cc < len && str[cc] == ',') {
+    cc++;
+    while (cc < len && FXSYS_iswspace(str[cc]))
+      cc++;
+
+    while (cc < len) {
+      if (str[cc] == ',' || !FXSYS_IsDecimalDigit(str[cc]))
+        break;
+
+      g = g * 10 + str[cc] - '0';
+      cc++;
+    }
+    if (cc < len && str[cc] == ',') {
+      cc++;
+      while (cc < len && FXSYS_iswspace(str[cc]))
+        cc++;
+
+      while (cc < len) {
+        if (str[cc] == ',' || !FXSYS_IsDecimalDigit(str[cc]))
+          break;
+
+        b = b * 10 + str[cc] - '0';
+        cc++;
+      }
+    }
+  }
+  return ArgbEncode(0xFF, r, g, b);
+}
+
 CXFA_Color::CXFA_Color(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Color,
                 kColorPropertyData,
                 kColorAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Color::~CXFA_Color() = default;
 
-FX_ARGB CXFA_Color::GetValue() {
-  Optional<WideString> val = JSObject()->TryCData(XFA_Attribute::Value, false);
-  return val ? StringToFXARGB(val->AsStringView()) : 0xFF000000;
+FX_ARGB CXFA_Color::GetValue() const {
+  absl::optional<WideString> val =
+      JSObject()->TryCData(XFA_Attribute::Value, false);
+  return val.has_value() ? StringToFXARGB(val->AsStringView()) : 0xFF000000;
 }
 
-FX_ARGB CXFA_Color::GetValueOrDefault(FX_ARGB defaultValue) {
-  Optional<WideString> val = JSObject()->TryCData(XFA_Attribute::Value, false);
-  return val ? StringToFXARGB(val->AsStringView()) : defaultValue;
+FX_ARGB CXFA_Color::GetValueOrDefault(FX_ARGB defaultValue) const {
+  absl::optional<WideString> val =
+      JSObject()->TryCData(XFA_Attribute::Value, false);
+  return val.has_value() ? StringToFXARGB(val->AsStringView()) : defaultValue;
 }
 
 void CXFA_Color::SetValue(FX_ARGB color) {
@@ -54,5 +113,5 @@
   int b;
   std::tie(a, r, g, b) = ArgbDecode(color);
   JSObject()->SetCData(XFA_Attribute::Value,
-                       WideString::Format(L"%d,%d,%d", r, g, b), false, false);
+                       WideString::Format(L"%d,%d,%d", r, g, b));
 }
diff --git a/xfa/fxfa/parser/cxfa_color.h b/xfa/fxfa/parser/cxfa_color.h
index d13a553..afdbf70 100644
--- a/xfa/fxfa/parser/cxfa_color.h
+++ b/xfa/fxfa/parser/cxfa_color.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,18 +7,23 @@
 #ifndef XFA_FXFA_PARSER_CXFA_COLOR_H_
 #define XFA_FXFA_PARSER_CXFA_COLOR_H_
 
+#include "core/fxcrt/widestring.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
 class CXFA_Color final : public CXFA_Node {
  public:
   static constexpr FX_ARGB kBlackColor = 0xFF000000;
+  static FX_ARGB StringToFXARGB(WideStringView view);
 
-  CXFA_Color(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Color() override;
 
-  FX_ARGB GetValue();
-  FX_ARGB GetValueOrDefault(FX_ARGB defaultValue);
+  FX_ARGB GetValue() const;
+  FX_ARGB GetValueOrDefault(FX_ARGB defaultValue) const;
   void SetValue(FX_ARGB color);
+
+ private:
+  CXFA_Color(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_COLOR_H_
diff --git a/xfa/fxfa/parser/cxfa_comb.cpp b/xfa/fxfa/parser/cxfa_comb.cpp
index 4bd8ac5..e94b8d3 100644
--- a/xfa/fxfa/parser/cxfa_comb.cpp
+++ b/xfa/fxfa/parser/cxfa_comb.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_comb.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_Comb::CXFA_Comb(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Comb,
                 {},
                 kCombAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
-CXFA_Comb::~CXFA_Comb() {}
+CXFA_Comb::~CXFA_Comb() = default;
diff --git a/xfa/fxfa/parser/cxfa_comb.h b/xfa/fxfa/parser/cxfa_comb.h
index 4193a9c..dca9cc0 100644
--- a/xfa/fxfa/parser/cxfa_comb.h
+++ b/xfa/fxfa/parser/cxfa_comb.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Comb final : public CXFA_Node {
  public:
-  CXFA_Comb(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Comb() override;
+
+ private:
+  CXFA_Comb(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_COMB_H_
diff --git a/xfa/fxfa/parser/cxfa_command.cpp b/xfa/fxfa/parser/cxfa_command.cpp
index 4d1e283..ad79f8e 100644
--- a/xfa/fxfa/parser/cxfa_command.cpp
+++ b/xfa/fxfa/parser/cxfa_command.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #include "xfa/fxfa/parser/cxfa_command.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kCommandPropertyData[] = {
-    {XFA_Element::Query, 1, 0},
-    {XFA_Element::Insert, 1, 0},
-    {XFA_Element::Update, 1, 0},
-    {XFA_Element::Delete, 1, 0},
+    {XFA_Element::Query, 1, {}},
+    {XFA_Element::Insert, 1, {}},
+    {XFA_Element::Update, 1, {}},
+    {XFA_Element::Delete, 1, {}},
 };
 
 const CXFA_Node::AttributeData kCommandAttributeData[] = {
@@ -31,11 +31,13 @@
 CXFA_Command::CXFA_Command(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_SourceSet,
+                XFA_XDPPACKET::kSourceSet,
                 XFA_ObjectType::Node,
                 XFA_Element::Command,
                 kCommandPropertyData,
                 kCommandAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Command::~CXFA_Command() = default;
diff --git a/xfa/fxfa/parser/cxfa_command.h b/xfa/fxfa/parser/cxfa_command.h
index 46f9854..0516890 100644
--- a/xfa/fxfa/parser/cxfa_command.h
+++ b/xfa/fxfa/parser/cxfa_command.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Command final : public CXFA_Node {
  public:
-  CXFA_Command(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Command() override;
+
+ private:
+  CXFA_Command(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_COMMAND_H_
diff --git a/xfa/fxfa/parser/cxfa_common.cpp b/xfa/fxfa/parser/cxfa_common.cpp
index 576234c..8a817f2 100644
--- a/xfa/fxfa/parser/cxfa_common.cpp
+++ b/xfa/fxfa/parser/cxfa_common.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,19 @@
 #include "xfa/fxfa/parser/cxfa_common.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kCommonPropertyData[] = {
-    {XFA_Element::SuppressBanner, 1, 0},
-    {XFA_Element::VersionControl, 1, 0},
-    {XFA_Element::LocaleSet, 1, 0},
-    {XFA_Element::Template, 1, 0},
-    {XFA_Element::ValidationMessaging, 1, 0},
-    {XFA_Element::Locale, 1, 0},
-    {XFA_Element::Data, 1, 0},
-    {XFA_Element::Messaging, 1, 0},
+    {XFA_Element::SuppressBanner, 1, {}},
+    {XFA_Element::VersionControl, 1, {}},
+    {XFA_Element::LocaleSet, 1, {}},
+    {XFA_Element::Template, 1, {}},
+    {XFA_Element::ValidationMessaging, 1, {}},
+    {XFA_Element::Locale, 1, {}},
+    {XFA_Element::Data, 1, {}},
+    {XFA_Element::Messaging, 1, {}},
 };
 
 const CXFA_Node::AttributeData kCommonAttributeData[] = {
@@ -32,11 +32,13 @@
 CXFA_Common::CXFA_Common(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Common,
                 kCommonPropertyData,
                 kCommonAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Common::~CXFA_Common() = default;
diff --git a/xfa/fxfa/parser/cxfa_common.h b/xfa/fxfa/parser/cxfa_common.h
index 7cab852..9648986 100644
--- a/xfa/fxfa/parser/cxfa_common.h
+++ b/xfa/fxfa/parser/cxfa_common.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Common final : public CXFA_Node {
  public:
-  CXFA_Common(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Common() override;
+
+ private:
+  CXFA_Common(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_COMMON_H_
diff --git a/xfa/fxfa/parser/cxfa_compress.cpp b/xfa/fxfa/parser/cxfa_compress.cpp
index 6b593e7..f548bbc 100644
--- a/xfa/fxfa/parser/cxfa_compress.cpp
+++ b/xfa/fxfa/parser/cxfa_compress.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_compress.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_Compress::CXFA_Compress(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Compress,
                 {},
                 kCompressAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Compress::~CXFA_Compress() = default;
diff --git a/xfa/fxfa/parser/cxfa_compress.h b/xfa/fxfa/parser/cxfa_compress.h
index 2b56390..f96fadf 100644
--- a/xfa/fxfa/parser/cxfa_compress.h
+++ b/xfa/fxfa/parser/cxfa_compress.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Compress final : public CXFA_Node {
  public:
-  CXFA_Compress(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Compress() override;
+
+ private:
+  CXFA_Compress(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_COMPRESS_H_
diff --git a/xfa/fxfa/parser/cxfa_compression.cpp b/xfa/fxfa/parser/cxfa_compression.cpp
index 344101d..c61a8ad 100644
--- a/xfa/fxfa/parser/cxfa_compression.cpp
+++ b/xfa/fxfa/parser/cxfa_compression.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #include "xfa/fxfa/parser/cxfa_compression.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kCompressionPropertyData[] = {
-    {XFA_Element::Level, 1, 0},
-    {XFA_Element::Type, 1, 0},
-    {XFA_Element::CompressObjectStream, 1, 0},
-    {XFA_Element::CompressLogicalStructure, 1, 0},
+    {XFA_Element::Level, 1, {}},
+    {XFA_Element::Type, 1, {}},
+    {XFA_Element::CompressObjectStream, 1, {}},
+    {XFA_Element::CompressLogicalStructure, 1, {}},
 };
 
 const CXFA_Node::AttributeData kCompressionAttributeData[] = {
@@ -28,11 +28,13 @@
 CXFA_Compression::CXFA_Compression(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Compression,
                 kCompressionPropertyData,
                 kCompressionAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Compression::~CXFA_Compression() = default;
diff --git a/xfa/fxfa/parser/cxfa_compression.h b/xfa/fxfa/parser/cxfa_compression.h
index 7a571ae..0d4e657 100644
--- a/xfa/fxfa/parser/cxfa_compression.h
+++ b/xfa/fxfa/parser/cxfa_compression.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Compression final : public CXFA_Node {
  public:
-  CXFA_Compression(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Compression() override;
+
+ private:
+  CXFA_Compression(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_COMPRESSION_H_
diff --git a/xfa/fxfa/parser/cxfa_compresslogicalstructure.cpp b/xfa/fxfa/parser/cxfa_compresslogicalstructure.cpp
index c9bcf6b..b65450d 100644
--- a/xfa/fxfa/parser/cxfa_compresslogicalstructure.cpp
+++ b/xfa/fxfa/parser/cxfa_compresslogicalstructure.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_compresslogicalstructure.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
     XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::CompressLogicalStructure,
                 {},
                 kCompressLogicalStructureAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_CompressLogicalStructure::~CXFA_CompressLogicalStructure() = default;
diff --git a/xfa/fxfa/parser/cxfa_compresslogicalstructure.h b/xfa/fxfa/parser/cxfa_compresslogicalstructure.h
index 843eb60..c2185a0 100644
--- a/xfa/fxfa/parser/cxfa_compresslogicalstructure.h
+++ b/xfa/fxfa/parser/cxfa_compresslogicalstructure.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_CompressLogicalStructure final : public CXFA_Node {
  public:
-  CXFA_CompressLogicalStructure(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_CompressLogicalStructure() override;
+
+ private:
+  CXFA_CompressLogicalStructure(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_COMPRESSLOGICALSTRUCTURE_H_
diff --git a/xfa/fxfa/parser/cxfa_compressobjectstream.cpp b/xfa/fxfa/parser/cxfa_compressobjectstream.cpp
index ed643d0..439276a 100644
--- a/xfa/fxfa/parser/cxfa_compressobjectstream.cpp
+++ b/xfa/fxfa/parser/cxfa_compressobjectstream.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_compressobjectstream.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                                      XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::CompressObjectStream,
                 {},
                 kCompressObjectStreamAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_CompressObjectStream::~CXFA_CompressObjectStream() = default;
diff --git a/xfa/fxfa/parser/cxfa_compressobjectstream.h b/xfa/fxfa/parser/cxfa_compressobjectstream.h
index bce7c65..ffd7f55 100644
--- a/xfa/fxfa/parser/cxfa_compressobjectstream.h
+++ b/xfa/fxfa/parser/cxfa_compressobjectstream.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_CompressObjectStream final : public CXFA_Node {
  public:
-  CXFA_CompressObjectStream(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_CompressObjectStream() override;
+
+ private:
+  CXFA_CompressObjectStream(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_COMPRESSOBJECTSTREAM_H_
diff --git a/xfa/fxfa/parser/cxfa_config.cpp b/xfa/fxfa/parser/cxfa_config.cpp
index 540e967..6a8f37a 100644
--- a/xfa/fxfa/parser/cxfa_config.cpp
+++ b/xfa/fxfa/parser/cxfa_config.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,14 @@
 #include "xfa/fxfa/parser/cxfa_config.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kConfigPropertyData[] = {
-    {XFA_Element::Present, 1, 0},
-    {XFA_Element::Acrobat, 1, 0},
-    {XFA_Element::Trace, 1, 0},
+    {XFA_Element::Present, 1, {}},
+    {XFA_Element::Acrobat, 1, {}},
+    {XFA_Element::Trace, 1, {}},
 };
 
 const CXFA_Node::AttributeData kConfigAttributeData[] = {
@@ -27,11 +27,13 @@
 CXFA_Config::CXFA_Config(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ModelNode,
                 XFA_Element::Config,
                 kConfigPropertyData,
                 kConfigAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Config::~CXFA_Config() = default;
diff --git a/xfa/fxfa/parser/cxfa_config.h b/xfa/fxfa/parser/cxfa_config.h
index 644c475..181e407 100644
--- a/xfa/fxfa/parser/cxfa_config.h
+++ b/xfa/fxfa/parser/cxfa_config.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Config final : public CXFA_Node {
  public:
-  CXFA_Config(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Config() override;
+
+ private:
+  CXFA_Config(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CONFIG_H_
diff --git a/xfa/fxfa/parser/cxfa_conformance.cpp b/xfa/fxfa/parser/cxfa_conformance.cpp
index b9c8e34..903e631 100644
--- a/xfa/fxfa/parser/cxfa_conformance.cpp
+++ b/xfa/fxfa/parser/cxfa_conformance.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_conformance.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Conformance::CXFA_Conformance(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Conformance,
                 {},
                 kConformanceAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Conformance::~CXFA_Conformance() = default;
diff --git a/xfa/fxfa/parser/cxfa_conformance.h b/xfa/fxfa/parser/cxfa_conformance.h
index 829bb1c..4f768d3 100644
--- a/xfa/fxfa/parser/cxfa_conformance.h
+++ b/xfa/fxfa/parser/cxfa_conformance.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Conformance final : public CXFA_Node {
  public:
-  CXFA_Conformance(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Conformance() override;
+
+ private:
+  CXFA_Conformance(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CONFORMANCE_H_
diff --git a/xfa/fxfa/parser/cxfa_connect.cpp b/xfa/fxfa/parser/cxfa_connect.cpp
index c46debc..8524f3f 100644
--- a/xfa/fxfa/parser/cxfa_connect.cpp
+++ b/xfa/fxfa/parser/cxfa_connect.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #include "xfa/fxfa/parser/cxfa_connect.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kConnectPropertyData[] = {
-    {XFA_Element::Picture, 1, 0},
-    {XFA_Element::ConnectString, 1, 0},
-    {XFA_Element::User, 1, 0},
-    {XFA_Element::Password, 1, 0},
+    {XFA_Element::Picture, 1, {}},
+    {XFA_Element::ConnectString, 1, {}},
+    {XFA_Element::User, 1, {}},
+    {XFA_Element::Password, 1, {}},
 };
 
 const CXFA_Node::AttributeData kConnectAttributeData[] = {
@@ -36,12 +36,14 @@
 CXFA_Connect::CXFA_Connect(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_SourceSet | XFA_XDPPACKET_Template |
-                 XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kSourceSet, XFA_XDPPACKET::kTemplate,
+                 XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Connect,
                 kConnectPropertyData,
                 kConnectAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Connect::~CXFA_Connect() = default;
diff --git a/xfa/fxfa/parser/cxfa_connect.h b/xfa/fxfa/parser/cxfa_connect.h
index 3d616ce..78e2cf3 100644
--- a/xfa/fxfa/parser/cxfa_connect.h
+++ b/xfa/fxfa/parser/cxfa_connect.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Connect final : public CXFA_Node {
  public:
-  CXFA_Connect(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Connect() override;
+
+ private:
+  CXFA_Connect(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CONNECT_H_
diff --git a/xfa/fxfa/parser/cxfa_connectionset.cpp b/xfa/fxfa/parser/cxfa_connectionset.cpp
index 74585e6..8c1f38f 100644
--- a/xfa/fxfa/parser/cxfa_connectionset.cpp
+++ b/xfa/fxfa/parser/cxfa_connectionset.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,17 +7,19 @@
 #include "xfa/fxfa/parser/cxfa_connectionset.h"
 
 #include "fxjs/xfa/cjx_model.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 CXFA_ConnectionSet::CXFA_ConnectionSet(CXFA_Document* doc,
                                        XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_ConnectionSet,
+                XFA_XDPPACKET::kConnectionSet,
                 XFA_ObjectType::ModelNode,
                 XFA_Element::ConnectionSet,
                 {},
                 {},
-                pdfium::MakeUnique<CJX_Model>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Model>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ConnectionSet::~CXFA_ConnectionSet() = default;
diff --git a/xfa/fxfa/parser/cxfa_connectionset.h b/xfa/fxfa/parser/cxfa_connectionset.h
index 74238e0..dbdc824 100644
--- a/xfa/fxfa/parser/cxfa_connectionset.h
+++ b/xfa/fxfa/parser/cxfa_connectionset.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_ConnectionSet final : public CXFA_Node {
  public:
-  CXFA_ConnectionSet(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ConnectionSet() override;
+
+ private:
+  CXFA_ConnectionSet(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CONNECTIONSET_H_
diff --git a/xfa/fxfa/parser/cxfa_connectstring.cpp b/xfa/fxfa/parser/cxfa_connectstring.cpp
index c5e37f4..8630af2 100644
--- a/xfa/fxfa/parser/cxfa_connectstring.cpp
+++ b/xfa/fxfa/parser/cxfa_connectstring.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_connectstring.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -24,11 +24,13 @@
                                        XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_SourceSet,
+                XFA_XDPPACKET::kSourceSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::ConnectString,
                 {},
                 kConnectStringAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ConnectString::~CXFA_ConnectString() = default;
diff --git a/xfa/fxfa/parser/cxfa_connectstring.h b/xfa/fxfa/parser/cxfa_connectstring.h
index 88dd7bc..72acd4c 100644
--- a/xfa/fxfa/parser/cxfa_connectstring.h
+++ b/xfa/fxfa/parser/cxfa_connectstring.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_ConnectString final : public CXFA_Node {
  public:
-  CXFA_ConnectString(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ConnectString() override;
+
+ private:
+  CXFA_ConnectString(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CONNECTSTRING_H_
diff --git a/xfa/fxfa/parser/cxfa_contentarea.cpp b/xfa/fxfa/parser/cxfa_contentarea.cpp
index 8c62c0a..6c08e85 100644
--- a/xfa/fxfa/parser/cxfa_contentarea.cpp
+++ b/xfa/fxfa/parser/cxfa_contentarea.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_contentarea.h"
 
 #include "fxjs/xfa/cjx_container.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kContentAreaPropertyData[] = {
-    {XFA_Element::Desc, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Desc, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kContentAreaAttributeData[] = {
@@ -33,11 +33,13 @@
 CXFA_ContentArea::CXFA_ContentArea(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::ContentArea,
                 kContentAreaPropertyData,
                 kContentAreaAttributeData,
-                pdfium::MakeUnique<CJX_Container>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Container>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ContentArea::~CXFA_ContentArea() = default;
diff --git a/xfa/fxfa/parser/cxfa_contentarea.h b/xfa/fxfa/parser/cxfa_contentarea.h
index 43f6868..f047121 100644
--- a/xfa/fxfa/parser/cxfa_contentarea.h
+++ b/xfa/fxfa/parser/cxfa_contentarea.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_ContentArea final : public CXFA_Node {
  public:
-  CXFA_ContentArea(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ContentArea() override;
+
+ private:
+  CXFA_ContentArea(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CONTENTAREA_H_
diff --git a/xfa/fxfa/parser/cxfa_contentcopy.cpp b/xfa/fxfa/parser/cxfa_contentcopy.cpp
index 38fd27e..4108194 100644
--- a/xfa/fxfa/parser/cxfa_contentcopy.cpp
+++ b/xfa/fxfa/parser/cxfa_contentcopy.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_contentcopy.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_ContentCopy::CXFA_ContentCopy(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::ContentCopy,
                 {},
                 kContentCopyAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ContentCopy::~CXFA_ContentCopy() = default;
diff --git a/xfa/fxfa/parser/cxfa_contentcopy.h b/xfa/fxfa/parser/cxfa_contentcopy.h
index 5b32beb..e8959f6 100644
--- a/xfa/fxfa/parser/cxfa_contentcopy.h
+++ b/xfa/fxfa/parser/cxfa_contentcopy.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_ContentCopy final : public CXFA_Node {
  public:
-  CXFA_ContentCopy(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ContentCopy() override;
+
+ private:
+  CXFA_ContentCopy(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CONTENTCOPY_H_
diff --git a/xfa/fxfa/parser/cxfa_copies.cpp b/xfa/fxfa/parser/cxfa_copies.cpp
index 7b8ef0e..c6b454d 100644
--- a/xfa/fxfa/parser/cxfa_copies.cpp
+++ b/xfa/fxfa/parser/cxfa_copies.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_copies.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Copies::CXFA_Copies(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Copies,
                 {},
                 kCopiesAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Copies::~CXFA_Copies() = default;
diff --git a/xfa/fxfa/parser/cxfa_copies.h b/xfa/fxfa/parser/cxfa_copies.h
index 46497f9..bd30a09 100644
--- a/xfa/fxfa/parser/cxfa_copies.h
+++ b/xfa/fxfa/parser/cxfa_copies.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Copies final : public CXFA_Node {
  public:
-  CXFA_Copies(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Copies() override;
+
+ private:
+  CXFA_Copies(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_COPIES_H_
diff --git a/xfa/fxfa/parser/cxfa_corner.cpp b/xfa/fxfa/parser/cxfa_corner.cpp
index 3ffbc71..df0dcd5 100644
--- a/xfa/fxfa/parser/cxfa_corner.cpp
+++ b/xfa/fxfa/parser/cxfa_corner.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_corner.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kCornerPropertyData[] = {
-    {XFA_Element::Color, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Color, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kCornerAttributeData[] = {
@@ -36,11 +36,13 @@
 CXFA_Corner::CXFA_Corner(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Stroke(doc,
                   packet,
-                  (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                  {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                   XFA_ObjectType::Node,
                   XFA_Element::Corner,
                   kCornerPropertyData,
                   kCornerAttributeData,
-                  pdfium::MakeUnique<CJX_Node>(this)) {}
+                  cppgc::MakeGarbageCollected<CJX_Node>(
+                      doc->GetHeap()->GetAllocationHandle(),
+                      this)) {}
 
 CXFA_Corner::~CXFA_Corner() = default;
diff --git a/xfa/fxfa/parser/cxfa_corner.h b/xfa/fxfa/parser/cxfa_corner.h
index 7dcaf32..a944ce1 100644
--- a/xfa/fxfa/parser/cxfa_corner.h
+++ b/xfa/fxfa/parser/cxfa_corner.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Corner final : public CXFA_Stroke {
  public:
-  CXFA_Corner(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Corner() override;
+
+ private:
+  CXFA_Corner(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CORNER_H_
diff --git a/xfa/fxfa/parser/cxfa_creator.cpp b/xfa/fxfa/parser/cxfa_creator.cpp
index e03b32a..aa9d907 100644
--- a/xfa/fxfa/parser/cxfa_creator.cpp
+++ b/xfa/fxfa/parser/cxfa_creator.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_creator.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Creator::CXFA_Creator(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Creator,
                 {},
                 kCreatorAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Creator::~CXFA_Creator() = default;
diff --git a/xfa/fxfa/parser/cxfa_creator.h b/xfa/fxfa/parser/cxfa_creator.h
index 437fab5..b8e51cb 100644
--- a/xfa/fxfa/parser/cxfa_creator.h
+++ b/xfa/fxfa/parser/cxfa_creator.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Creator final : public CXFA_Node {
  public:
-  CXFA_Creator(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Creator() override;
+
+ private:
+  CXFA_Creator(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CREATOR_H_
diff --git a/xfa/fxfa/parser/cxfa_currencysymbol.cpp b/xfa/fxfa/parser/cxfa_currencysymbol.cpp
index beca653..fa35fdb 100644
--- a/xfa/fxfa/parser/cxfa_currencysymbol.cpp
+++ b/xfa/fxfa/parser/cxfa_currencysymbol.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_currencysymbol.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                          XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::CurrencySymbol,
                 {},
                 kCurrencySymbolAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_CurrencySymbol::~CXFA_CurrencySymbol() = default;
diff --git a/xfa/fxfa/parser/cxfa_currencysymbol.h b/xfa/fxfa/parser/cxfa_currencysymbol.h
index 3d7b5bd..58c1a8a 100644
--- a/xfa/fxfa/parser/cxfa_currencysymbol.h
+++ b/xfa/fxfa/parser/cxfa_currencysymbol.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_CurrencySymbol final : public CXFA_Node {
  public:
-  CXFA_CurrencySymbol(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_CurrencySymbol() override;
+
+ private:
+  CXFA_CurrencySymbol(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CURRENCYSYMBOL_H_
diff --git a/xfa/fxfa/parser/cxfa_currencysymbols.cpp b/xfa/fxfa/parser/cxfa_currencysymbols.cpp
index 3b2e90c..9cf0be6 100644
--- a/xfa/fxfa/parser/cxfa_currencysymbols.cpp
+++ b/xfa/fxfa/parser/cxfa_currencysymbols.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_currencysymbols.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kCurrencySymbolsPropertyData[] = {
-    {XFA_Element::CurrencySymbol, 3, 0},
+    {XFA_Element::CurrencySymbol, 3, {}},
 };
 
 }  // namespace
@@ -21,11 +21,13 @@
                                            XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::CurrencySymbols,
                 kCurrencySymbolsPropertyData,
                 {},
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_CurrencySymbols::~CXFA_CurrencySymbols() = default;
diff --git a/xfa/fxfa/parser/cxfa_currencysymbols.h b/xfa/fxfa/parser/cxfa_currencysymbols.h
index 2ce27cf..cf4c0f0 100644
--- a/xfa/fxfa/parser/cxfa_currencysymbols.h
+++ b/xfa/fxfa/parser/cxfa_currencysymbols.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_CurrencySymbols final : public CXFA_Node {
  public:
-  CXFA_CurrencySymbols(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_CurrencySymbols() override;
+
+ private:
+  CXFA_CurrencySymbols(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CURRENCYSYMBOLS_H_
diff --git a/xfa/fxfa/parser/cxfa_currentpage.cpp b/xfa/fxfa/parser/cxfa_currentpage.cpp
index a98aa1c..0ed4907 100644
--- a/xfa/fxfa/parser/cxfa_currentpage.cpp
+++ b/xfa/fxfa/parser/cxfa_currentpage.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_currentpage.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_CurrentPage::CXFA_CurrentPage(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::CurrentPage,
                 {},
                 kCurrentPageAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_CurrentPage::~CXFA_CurrentPage() = default;
diff --git a/xfa/fxfa/parser/cxfa_currentpage.h b/xfa/fxfa/parser/cxfa_currentpage.h
index 5bba6a0..394c128 100644
--- a/xfa/fxfa/parser/cxfa_currentpage.h
+++ b/xfa/fxfa/parser/cxfa_currentpage.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_CurrentPage final : public CXFA_Node {
  public:
-  CXFA_CurrentPage(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_CurrentPage() override;
+
+ private:
+  CXFA_CurrentPage(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CURRENTPAGE_H_
diff --git a/xfa/fxfa/parser/cxfa_data.cpp b/xfa/fxfa/parser/cxfa_data.cpp
index 5173147..42307af 100644
--- a/xfa/fxfa/parser/cxfa_data.cpp
+++ b/xfa/fxfa/parser/cxfa_data.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,16 @@
 #include "xfa/fxfa/parser/cxfa_data.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kDataPropertyData[] = {
-    {XFA_Element::Uri, 1, 0},        {XFA_Element::Xsl, 1, 0},
-    {XFA_Element::StartNode, 1, 0},  {XFA_Element::OutputXSL, 1, 0},
-    {XFA_Element::AdjustData, 1, 0}, {XFA_Element::Attributes, 1, 0},
-    {XFA_Element::Window, 1, 0},     {XFA_Element::Record, 1, 0},
-    {XFA_Element::Range, 1, 0},      {XFA_Element::IncrementalLoad, 1, 0},
+    {XFA_Element::Uri, 1, {}},        {XFA_Element::Xsl, 1, {}},
+    {XFA_Element::StartNode, 1, {}},  {XFA_Element::OutputXSL, 1, {}},
+    {XFA_Element::AdjustData, 1, {}}, {XFA_Element::Attributes, 1, {}},
+    {XFA_Element::Window, 1, {}},     {XFA_Element::Record, 1, {}},
+    {XFA_Element::Range, 1, {}},      {XFA_Element::IncrementalLoad, 1, {}},
 };
 
 const CXFA_Node::AttributeData kDataAttributeData[] = {
@@ -29,11 +29,13 @@
 CXFA_Data::CXFA_Data(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Data,
                 kDataPropertyData,
                 kDataAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Data::~CXFA_Data() = default;
diff --git a/xfa/fxfa/parser/cxfa_data.h b/xfa/fxfa/parser/cxfa_data.h
index 44cf40d..60de2d7 100644
--- a/xfa/fxfa/parser/cxfa_data.h
+++ b/xfa/fxfa/parser/cxfa_data.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Data final : public CXFA_Node {
  public:
-  CXFA_Data(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Data() override;
+
+ private:
+  CXFA_Data(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DATA_H_
diff --git a/xfa/fxfa/parser/cxfa_dataexporter.cpp b/xfa/fxfa/parser/cxfa_dataexporter.cpp
index 50106ab..6ba6635 100644
--- a/xfa/fxfa/parser/cxfa_dataexporter.cpp
+++ b/xfa/fxfa/parser/cxfa_dataexporter.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,6 +9,7 @@
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
+#include "third_party/base/check.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
@@ -19,10 +20,7 @@
 
 bool CXFA_DataExporter::Export(const RetainPtr<IFX_SeekableStream>& pStream,
                                CXFA_Node* pNode) {
-  ASSERT(pStream);
-
-  if (!pStream)
-    return false;
+  DCHECK(pStream);
 
   if (pNode->IsModelNode()) {
     switch (pNode->GetPacketType()) {
@@ -33,7 +31,7 @@
              pChild = pChild->GetNextSibling()) {
           Export(pStream, pChild);
         }
-        pStream->WriteString("</xdp:xdp\n>");
+        pStream->WriteString("</xdp:xdp>\n");
         break;
       }
       case XFA_PacketType::Datasets: {
@@ -42,7 +40,7 @@
           return false;
 
         CXFA_Node* pDataNode = pNode->GetFirstChild();
-        ASSERT(pDataNode);
+        DCHECK(pDataNode);
         XFA_DataExporter_DealWithDataGroupNode(pDataNode);
         pElement->Save(pStream);
         break;
diff --git a/xfa/fxfa/parser/cxfa_dataexporter.h b/xfa/fxfa/parser/cxfa_dataexporter.h
index 6fe19d5..f394a20 100644
--- a/xfa/fxfa/parser/cxfa_dataexporter.h
+++ b/xfa/fxfa/parser/cxfa_dataexporter.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,8 @@
 #ifndef XFA_FXFA_PARSER_CXFA_DATAEXPORTER_H_
 #define XFA_FXFA_PARSER_CXFA_DATAEXPORTER_H_
 
-#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
 
-class CXFA_Document;
 class CXFA_Node;
 class IFX_SeekableStream;
 
diff --git a/xfa/fxfa/parser/cxfa_datagroup.cpp b/xfa/fxfa/parser/cxfa_datagroup.cpp
index bb5b5fb..d0af602 100644
--- a/xfa/fxfa/parser/cxfa_datagroup.cpp
+++ b/xfa/fxfa/parser/cxfa_datagroup.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_datagroup.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -20,11 +20,13 @@
 CXFA_DataGroup::CXFA_DataGroup(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Datasets,
+                XFA_XDPPACKET::kDatasets,
                 XFA_ObjectType::Node,
                 XFA_Element::DataGroup,
                 {},
                 kDataGroupAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_DataGroup::~CXFA_DataGroup() = default;
diff --git a/xfa/fxfa/parser/cxfa_datagroup.h b/xfa/fxfa/parser/cxfa_datagroup.h
index 5a61704..7f45b47 100644
--- a/xfa/fxfa/parser/cxfa_datagroup.h
+++ b/xfa/fxfa/parser/cxfa_datagroup.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_DataGroup final : public CXFA_Node {
  public:
-  CXFA_DataGroup(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_DataGroup() override;
+
+ private:
+  CXFA_DataGroup(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DATAGROUP_H_
diff --git a/xfa/fxfa/parser/cxfa_datamodel.cpp b/xfa/fxfa/parser/cxfa_datamodel.cpp
index 4b506c5..ab5cb6b 100644
--- a/xfa/fxfa/parser/cxfa_datamodel.cpp
+++ b/xfa/fxfa/parser/cxfa_datamodel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,18 @@
 #include "xfa/fxfa/parser/cxfa_datamodel.h"
 
 #include "fxjs/xfa/cjx_model.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 CXFA_DataModel::CXFA_DataModel(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Datasets,
+                XFA_XDPPACKET::kDatasets,
                 XFA_ObjectType::ModelNode,
                 XFA_Element::DataModel,
                 {},
                 {},
-                pdfium::MakeUnique<CJX_Model>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Model>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_DataModel::~CXFA_DataModel() = default;
diff --git a/xfa/fxfa/parser/cxfa_datamodel.h b/xfa/fxfa/parser/cxfa_datamodel.h
index 8a1bd42..c6295b7 100644
--- a/xfa/fxfa/parser/cxfa_datamodel.h
+++ b/xfa/fxfa/parser/cxfa_datamodel.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_DataModel final : public CXFA_Node {
  public:
-  CXFA_DataModel(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_DataModel() override;
+
+ private:
+  CXFA_DataModel(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DATAMODEL_H_
diff --git a/xfa/fxfa/parser/cxfa_datavalue.cpp b/xfa/fxfa/parser/cxfa_datavalue.cpp
index 6e541a4..bc43f86 100644
--- a/xfa/fxfa/parser/cxfa_datavalue.cpp
+++ b/xfa/fxfa/parser/cxfa_datavalue.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_datavalue.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -25,11 +25,13 @@
 CXFA_DataValue::CXFA_DataValue(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Datasets,
+                XFA_XDPPACKET::kDatasets,
                 XFA_ObjectType::Node,
                 XFA_Element::DataValue,
                 {},
                 kDataValueAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_DataValue::~CXFA_DataValue() = default;
diff --git a/xfa/fxfa/parser/cxfa_datavalue.h b/xfa/fxfa/parser/cxfa_datavalue.h
index ee2c621..41dd47c 100644
--- a/xfa/fxfa/parser/cxfa_datavalue.h
+++ b/xfa/fxfa/parser/cxfa_datavalue.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_DataValue final : public CXFA_Node {
  public:
-  CXFA_DataValue(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_DataValue() override;
+
+ private:
+  CXFA_DataValue(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DATAVALUE_H_
diff --git a/xfa/fxfa/parser/cxfa_date.cpp b/xfa/fxfa/parser/cxfa_date.cpp
index 9508e65..1e559af 100644
--- a/xfa/fxfa/parser/cxfa_date.cpp
+++ b/xfa/fxfa/parser/cxfa_date.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_date.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_Date::CXFA_Date(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Date,
                 {},
                 kDateAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Date::~CXFA_Date() = default;
diff --git a/xfa/fxfa/parser/cxfa_date.h b/xfa/fxfa/parser/cxfa_date.h
index 3fb7ba7..85c1b6b 100644
--- a/xfa/fxfa/parser/cxfa_date.h
+++ b/xfa/fxfa/parser/cxfa_date.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Date final : public CXFA_Node {
  public:
-  CXFA_Date(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Date() override;
+
+ private:
+  CXFA_Date(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DATE_H_
diff --git a/xfa/fxfa/parser/cxfa_datepattern.cpp b/xfa/fxfa/parser/cxfa_datepattern.cpp
index 9f4f635..cf1fb2d 100644
--- a/xfa/fxfa/parser/cxfa_datepattern.cpp
+++ b/xfa/fxfa/parser/cxfa_datepattern.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_datepattern.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_DatePattern::CXFA_DatePattern(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::DatePattern,
                 {},
                 kDatePatternAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_DatePattern::~CXFA_DatePattern() = default;
diff --git a/xfa/fxfa/parser/cxfa_datepattern.h b/xfa/fxfa/parser/cxfa_datepattern.h
index e67a61f..0a7aed3 100644
--- a/xfa/fxfa/parser/cxfa_datepattern.h
+++ b/xfa/fxfa/parser/cxfa_datepattern.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_DatePattern final : public CXFA_Node {
  public:
-  CXFA_DatePattern(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_DatePattern() override;
+
+ private:
+  CXFA_DatePattern(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DATEPATTERN_H_
diff --git a/xfa/fxfa/parser/cxfa_datepatterns.cpp b/xfa/fxfa/parser/cxfa_datepatterns.cpp
index 9ac1922..987746b 100644
--- a/xfa/fxfa/parser/cxfa_datepatterns.cpp
+++ b/xfa/fxfa/parser/cxfa_datepatterns.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_datepatterns.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kDatePatternsPropertyData[] = {
-    {XFA_Element::DatePattern, 4, 0},
+    {XFA_Element::DatePattern, 4, {}},
 };
 
 }  // namespace
@@ -20,11 +20,13 @@
 CXFA_DatePatterns::CXFA_DatePatterns(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::DatePatterns,
                 kDatePatternsPropertyData,
                 {},
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_DatePatterns::~CXFA_DatePatterns() = default;
diff --git a/xfa/fxfa/parser/cxfa_datepatterns.h b/xfa/fxfa/parser/cxfa_datepatterns.h
index 6c2dca4..b3d1193 100644
--- a/xfa/fxfa/parser/cxfa_datepatterns.h
+++ b/xfa/fxfa/parser/cxfa_datepatterns.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_DatePatterns final : public CXFA_Node {
  public:
-  CXFA_DatePatterns(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_DatePatterns() override;
+
+ private:
+  CXFA_DatePatterns(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DATEPATTERNS_H_
diff --git a/xfa/fxfa/parser/cxfa_datetime.cpp b/xfa/fxfa/parser/cxfa_datetime.cpp
index 313a469..9dd154d 100644
--- a/xfa/fxfa/parser/cxfa_datetime.cpp
+++ b/xfa/fxfa/parser/cxfa_datetime.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_datetime.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_DateTime::CXFA_DateTime(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContentNode,
                 XFA_Element::DateTime,
                 {},
                 kDateTimeAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_DateTime::~CXFA_DateTime() = default;
diff --git a/xfa/fxfa/parser/cxfa_datetime.h b/xfa/fxfa/parser/cxfa_datetime.h
index ca71daf..c2b7726 100644
--- a/xfa/fxfa/parser/cxfa_datetime.h
+++ b/xfa/fxfa/parser/cxfa_datetime.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_DateTime final : public CXFA_Node {
  public:
-  CXFA_DateTime(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_DateTime() override;
+
+ private:
+  CXFA_DateTime(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DATETIME_H_
diff --git a/xfa/fxfa/parser/cxfa_datetimeedit.cpp b/xfa/fxfa/parser/cxfa_datetimeedit.cpp
index e64abbd..f6dc7d1 100644
--- a/xfa/fxfa/parser/cxfa_datetimeedit.cpp
+++ b/xfa/fxfa/parser/cxfa_datetimeedit.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #include "xfa/fxfa/parser/cxfa_datetimeedit.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kDateTimeEditPropertyData[] = {
-    {XFA_Element::Margin, 1, 0},
-    {XFA_Element::Border, 1, 0},
-    {XFA_Element::Comb, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Margin, 1, {}},
+    {XFA_Element::Border, 1, {}},
+    {XFA_Element::Comb, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kDateTimeEditAttributeData[] = {
@@ -33,12 +33,14 @@
 CXFA_DateTimeEdit::CXFA_DateTimeEdit(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::DateTimeEdit,
                 kDateTimeEditPropertyData,
                 kDateTimeEditAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_DateTimeEdit::~CXFA_DateTimeEdit() = default;
 
diff --git a/xfa/fxfa/parser/cxfa_datetimeedit.h b/xfa/fxfa/parser/cxfa_datetimeedit.h
index de76e2a..d38e813 100644
--- a/xfa/fxfa/parser/cxfa_datetimeedit.h
+++ b/xfa/fxfa/parser/cxfa_datetimeedit.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,11 +11,14 @@
 
 class CXFA_DateTimeEdit final : public CXFA_Node {
  public:
-  CXFA_DateTimeEdit(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_DateTimeEdit() override;
 
   XFA_Element GetValueNodeType() const override;
   XFA_FFWidgetType GetDefaultFFWidgetType() const override;
+
+ private:
+  CXFA_DateTimeEdit(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DATETIMEEDIT_H_
diff --git a/xfa/fxfa/parser/cxfa_datetimesymbols.cpp b/xfa/fxfa/parser/cxfa_datetimesymbols.cpp
index 159d821..d921e8a 100644
--- a/xfa/fxfa/parser/cxfa_datetimesymbols.cpp
+++ b/xfa/fxfa/parser/cxfa_datetimesymbols.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,17 +7,19 @@
 #include "xfa/fxfa/parser/cxfa_datetimesymbols.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 CXFA_DateTimeSymbols::CXFA_DateTimeSymbols(CXFA_Document* doc,
                                            XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::DateTimeSymbols,
                 {},
                 {},
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_DateTimeSymbols::~CXFA_DateTimeSymbols() = default;
diff --git a/xfa/fxfa/parser/cxfa_datetimesymbols.h b/xfa/fxfa/parser/cxfa_datetimesymbols.h
index cfbe069..b0a2a6d 100644
--- a/xfa/fxfa/parser/cxfa_datetimesymbols.h
+++ b/xfa/fxfa/parser/cxfa_datetimesymbols.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_DateTimeSymbols final : public CXFA_Node {
  public:
-  CXFA_DateTimeSymbols(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_DateTimeSymbols() override;
+
+ private:
+  CXFA_DateTimeSymbols(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DATETIMESYMBOLS_H_
diff --git a/xfa/fxfa/parser/cxfa_day.cpp b/xfa/fxfa/parser/cxfa_day.cpp
index ffbeb2f..44a6bf4 100644
--- a/xfa/fxfa/parser/cxfa_day.cpp
+++ b/xfa/fxfa/parser/cxfa_day.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,18 @@
 #include "xfa/fxfa/parser/cxfa_day.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 CXFA_Day::CXFA_Day(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Day,
                 {},
                 {},
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Day::~CXFA_Day() = default;
diff --git a/xfa/fxfa/parser/cxfa_day.h b/xfa/fxfa/parser/cxfa_day.h
index 6fb2f45..d283ac9 100644
--- a/xfa/fxfa/parser/cxfa_day.h
+++ b/xfa/fxfa/parser/cxfa_day.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Day final : public CXFA_Node {
  public:
-  CXFA_Day(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Day() override;
+
+ private:
+  CXFA_Day(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DAY_H_
diff --git a/xfa/fxfa/parser/cxfa_daynames.cpp b/xfa/fxfa/parser/cxfa_daynames.cpp
index 8a3b447..0007a4d 100644
--- a/xfa/fxfa/parser/cxfa_daynames.cpp
+++ b/xfa/fxfa/parser/cxfa_daynames.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_daynames.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kDayNamesPropertyData[] = {
-    {XFA_Element::Day, 7, 0},
+    {XFA_Element::Day, 7, {}},
 };
 
 const CXFA_Node::AttributeData kDayNamesAttributeData[] = {
@@ -24,11 +24,13 @@
 CXFA_DayNames::CXFA_DayNames(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::DayNames,
                 kDayNamesPropertyData,
                 kDayNamesAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_DayNames::~CXFA_DayNames() = default;
diff --git a/xfa/fxfa/parser/cxfa_daynames.h b/xfa/fxfa/parser/cxfa_daynames.h
index 229d69c..687c805 100644
--- a/xfa/fxfa/parser/cxfa_daynames.h
+++ b/xfa/fxfa/parser/cxfa_daynames.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_DayNames final : public CXFA_Node {
  public:
-  CXFA_DayNames(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_DayNames() override;
+
+ private:
+  CXFA_DayNames(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DAYNAMES_H_
diff --git a/xfa/fxfa/parser/cxfa_debug.cpp b/xfa/fxfa/parser/cxfa_debug.cpp
index f4b6ac9..1258b48 100644
--- a/xfa/fxfa/parser/cxfa_debug.cpp
+++ b/xfa/fxfa/parser/cxfa_debug.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_debug.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kDebugPropertyData[] = {
-    {XFA_Element::Uri, 1, 0},
+    {XFA_Element::Uri, 1, {}},
 };
 
 const CXFA_Node::AttributeData kDebugAttributeData[] = {
@@ -25,11 +25,13 @@
 CXFA_Debug::CXFA_Debug(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Debug,
                 kDebugPropertyData,
                 kDebugAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Debug::~CXFA_Debug() = default;
diff --git a/xfa/fxfa/parser/cxfa_debug.h b/xfa/fxfa/parser/cxfa_debug.h
index 1a254f0..6492627 100644
--- a/xfa/fxfa/parser/cxfa_debug.h
+++ b/xfa/fxfa/parser/cxfa_debug.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Debug final : public CXFA_Node {
  public:
-  CXFA_Debug(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Debug() override;
+
+ private:
+  CXFA_Debug(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DEBUG_H_
diff --git a/xfa/fxfa/parser/cxfa_decimal.cpp b/xfa/fxfa/parser/cxfa_decimal.cpp
index 93e5e10..b418e00 100644
--- a/xfa/fxfa/parser/cxfa_decimal.cpp
+++ b/xfa/fxfa/parser/cxfa_decimal.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_decimal.h"
 
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -25,11 +25,13 @@
 CXFA_Decimal::CXFA_Decimal(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Decimal,
                 {},
                 kDecimalAttributeData,
-                pdfium::MakeUnique<CJX_Object>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Object>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Decimal::~CXFA_Decimal() = default;
diff --git a/xfa/fxfa/parser/cxfa_decimal.h b/xfa/fxfa/parser/cxfa_decimal.h
index 668f61a..8d76ba8 100644
--- a/xfa/fxfa/parser/cxfa_decimal.h
+++ b/xfa/fxfa/parser/cxfa_decimal.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Decimal final : public CXFA_Node {
  public:
-  CXFA_Decimal(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Decimal() override;
+
+ private:
+  CXFA_Decimal(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DECIMAL_H_
diff --git a/xfa/fxfa/parser/cxfa_defaulttypeface.cpp b/xfa/fxfa/parser/cxfa_defaulttypeface.cpp
index 1d38e32..f356532 100644
--- a/xfa/fxfa/parser/cxfa_defaulttypeface.cpp
+++ b/xfa/fxfa/parser/cxfa_defaulttypeface.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_defaulttypeface.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -24,11 +24,13 @@
                                            XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::DefaultTypeface,
                 {},
                 kDefaultTypefaceAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_DefaultTypeface::~CXFA_DefaultTypeface() = default;
diff --git a/xfa/fxfa/parser/cxfa_defaulttypeface.h b/xfa/fxfa/parser/cxfa_defaulttypeface.h
index a241bf8..689cdb3 100644
--- a/xfa/fxfa/parser/cxfa_defaulttypeface.h
+++ b/xfa/fxfa/parser/cxfa_defaulttypeface.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_DefaultTypeface final : public CXFA_Node {
  public:
-  CXFA_DefaultTypeface(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_DefaultTypeface() override;
+
+ private:
+  CXFA_DefaultTypeface(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DEFAULTTYPEFACE_H_
diff --git a/xfa/fxfa/parser/cxfa_defaultui.cpp b/xfa/fxfa/parser/cxfa_defaultui.cpp
index 26114b0..09986e2 100644
--- a/xfa/fxfa/parser/cxfa_defaultui.cpp
+++ b/xfa/fxfa/parser/cxfa_defaultui.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_defaultui.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kDefaultUiPropertyData[] = {
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kDefaultUiAttributeData[] = {
@@ -26,12 +26,14 @@
 CXFA_DefaultUi::CXFA_DefaultUi(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::DefaultUi,
                 kDefaultUiPropertyData,
                 kDefaultUiAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_DefaultUi::~CXFA_DefaultUi() = default;
 
diff --git a/xfa/fxfa/parser/cxfa_defaultui.h b/xfa/fxfa/parser/cxfa_defaultui.h
index d09da0b..b421f79 100644
--- a/xfa/fxfa/parser/cxfa_defaultui.h
+++ b/xfa/fxfa/parser/cxfa_defaultui.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,10 +11,13 @@
 
 class CXFA_DefaultUi final : public CXFA_Node {
  public:
-  CXFA_DefaultUi(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_DefaultUi() override;
 
   XFA_FFWidgetType GetDefaultFFWidgetType() const override;
+
+ private:
+  CXFA_DefaultUi(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DEFAULTUI_H_
diff --git a/xfa/fxfa/parser/cxfa_delete.cpp b/xfa/fxfa/parser/cxfa_delete.cpp
index bf76b00..0eeb44c 100644
--- a/xfa/fxfa/parser/cxfa_delete.cpp
+++ b/xfa/fxfa/parser/cxfa_delete.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_delete.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_Delete::CXFA_Delete(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_SourceSet,
+                XFA_XDPPACKET::kSourceSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::Delete,
                 {},
                 kDeleteAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Delete::~CXFA_Delete() = default;
diff --git a/xfa/fxfa/parser/cxfa_delete.h b/xfa/fxfa/parser/cxfa_delete.h
index 0ea6ab3..a04aa72 100644
--- a/xfa/fxfa/parser/cxfa_delete.h
+++ b/xfa/fxfa/parser/cxfa_delete.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Delete final : public CXFA_Node {
  public:
-  CXFA_Delete(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Delete() override;
+
+ private:
+  CXFA_Delete(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DELETE_H_
diff --git a/xfa/fxfa/parser/cxfa_delta.cpp b/xfa/fxfa/parser/cxfa_delta.cpp
index c48b2b3..287ccef 100644
--- a/xfa/fxfa/parser/cxfa_delta.cpp
+++ b/xfa/fxfa/parser/cxfa_delta.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,18 @@
 #include "xfa/fxfa/parser/cxfa_delta.h"
 
 #include "fxjs/xfa/cjx_delta.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 CXFA_Delta::CXFA_Delta(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Form,
+                XFA_XDPPACKET::kForm,
                 XFA_ObjectType::Object,
                 XFA_Element::Delta,
                 {},
                 {},
-                pdfium::MakeUnique<CJX_Delta>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Delta>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Delta::~CXFA_Delta() = default;
diff --git a/xfa/fxfa/parser/cxfa_delta.h b/xfa/fxfa/parser/cxfa_delta.h
index 40bfa56..b1cf1cd 100644
--- a/xfa/fxfa/parser/cxfa_delta.h
+++ b/xfa/fxfa/parser/cxfa_delta.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Delta final : public CXFA_Node {
  public:
-  CXFA_Delta(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Delta() override;
+
+ private:
+  CXFA_Delta(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DELTA_H_
diff --git a/xfa/fxfa/parser/cxfa_deltas.cpp b/xfa/fxfa/parser/cxfa_deltas.cpp
deleted file mode 100644
index c55595c..0000000
--- a/xfa/fxfa/parser/cxfa_deltas.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/parser/cxfa_deltas.h"
-
-#include "fxjs/xfa/cjx_list.h"
-#include "third_party/base/ptr_util.h"
-
-CXFA_Deltas::CXFA_Deltas(CXFA_Document* doc)
-    : CXFA_List(doc, pdfium::MakeUnique<CJX_List>(this)) {}
-
-CXFA_Deltas::~CXFA_Deltas() {}
diff --git a/xfa/fxfa/parser/cxfa_deltas.h b/xfa/fxfa/parser/cxfa_deltas.h
deleted file mode 100644
index 4c00e9e..0000000
--- a/xfa/fxfa/parser/cxfa_deltas.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_PARSER_CXFA_DELTAS_H_
-#define XFA_FXFA_PARSER_CXFA_DELTAS_H_
-
-#include "xfa/fxfa/parser/cxfa_list.h"
-
-class CXFA_Deltas : public CXFA_List {
- public:
-  explicit CXFA_Deltas(CXFA_Document* doc);
-  ~CXFA_Deltas() override;
-};
-
-#endif  // XFA_FXFA_PARSER_CXFA_DELTAS_H_
diff --git a/xfa/fxfa/parser/cxfa_desc.cpp b/xfa/fxfa/parser/cxfa_desc.cpp
index dc26ca4..d06a232 100644
--- a/xfa/fxfa/parser/cxfa_desc.cpp
+++ b/xfa/fxfa/parser/cxfa_desc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,16 @@
 #include "xfa/fxfa/parser/cxfa_desc.h"
 
 #include "fxjs/xfa/cjx_desc.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kDescPropertyData[] = {
-    {XFA_Element::Text, 1, 0},     {XFA_Element::Time, 1, 0},
-    {XFA_Element::DateTime, 1, 0}, {XFA_Element::Image, 1, 0},
-    {XFA_Element::Decimal, 1, 0},  {XFA_Element::Boolean, 1, 0},
-    {XFA_Element::Integer, 1, 0},  {XFA_Element::ExData, 1, 0},
-    {XFA_Element::Date, 1, 0},     {XFA_Element::Float, 1, 0},
+    {XFA_Element::Text, 1, {}},     {XFA_Element::Time, 1, {}},
+    {XFA_Element::DateTime, 1, {}}, {XFA_Element::Image, 1, {}},
+    {XFA_Element::Decimal, 1, {}},  {XFA_Element::Boolean, 1, {}},
+    {XFA_Element::Integer, 1, {}},  {XFA_Element::ExData, 1, {}},
+    {XFA_Element::Date, 1, {}},     {XFA_Element::Float, 1, {}},
 };
 
 const CXFA_Node::AttributeData kDescAttributeData[] = {
@@ -30,11 +30,13 @@
 CXFA_Desc::CXFA_Desc(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Desc,
                 kDescPropertyData,
                 kDescAttributeData,
-                pdfium::MakeUnique<CJX_Desc>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Desc>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Desc::~CXFA_Desc() = default;
diff --git a/xfa/fxfa/parser/cxfa_desc.h b/xfa/fxfa/parser/cxfa_desc.h
index 11baa90..a077bb1 100644
--- a/xfa/fxfa/parser/cxfa_desc.h
+++ b/xfa/fxfa/parser/cxfa_desc.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Desc final : public CXFA_Node {
  public:
-  CXFA_Desc(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Desc() override;
+
+ private:
+  CXFA_Desc(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DESC_H_
diff --git a/xfa/fxfa/parser/cxfa_destination.cpp b/xfa/fxfa/parser/cxfa_destination.cpp
index b80dceb..bfd9761 100644
--- a/xfa/fxfa/parser/cxfa_destination.cpp
+++ b/xfa/fxfa/parser/cxfa_destination.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_destination.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Destination::CXFA_Destination(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Destination,
                 {},
                 kDestinationAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Destination::~CXFA_Destination() = default;
diff --git a/xfa/fxfa/parser/cxfa_destination.h b/xfa/fxfa/parser/cxfa_destination.h
index d31434f..9f05435 100644
--- a/xfa/fxfa/parser/cxfa_destination.h
+++ b/xfa/fxfa/parser/cxfa_destination.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Destination final : public CXFA_Node {
  public:
-  CXFA_Destination(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Destination() override;
+
+ private:
+  CXFA_Destination(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DESTINATION_H_
diff --git a/xfa/fxfa/parser/cxfa_digestmethod.cpp b/xfa/fxfa/parser/cxfa_digestmethod.cpp
index 70c781d..4382532 100644
--- a/xfa/fxfa/parser/cxfa_digestmethod.cpp
+++ b/xfa/fxfa/parser/cxfa_digestmethod.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_digestmethod.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
 CXFA_DigestMethod::CXFA_DigestMethod(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::NodeC,
                 XFA_Element::DigestMethod,
                 {},
                 kDigestMethodAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_DigestMethod::~CXFA_DigestMethod() = default;
diff --git a/xfa/fxfa/parser/cxfa_digestmethod.h b/xfa/fxfa/parser/cxfa_digestmethod.h
index 777f46a..0b6dbbf 100644
--- a/xfa/fxfa/parser/cxfa_digestmethod.h
+++ b/xfa/fxfa/parser/cxfa_digestmethod.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_DigestMethod final : public CXFA_Node {
  public:
-  CXFA_DigestMethod(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_DigestMethod() override;
+
+ private:
+  CXFA_DigestMethod(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DIGESTMETHOD_H_
diff --git a/xfa/fxfa/parser/cxfa_digestmethods.cpp b/xfa/fxfa/parser/cxfa_digestmethods.cpp
index 2a1d1a2..a478671 100644
--- a/xfa/fxfa/parser/cxfa_digestmethods.cpp
+++ b/xfa/fxfa/parser/cxfa_digestmethods.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_digestmethods.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -25,11 +25,13 @@
                                        XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::DigestMethods,
                 {},
                 kDigestMethodsAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_DigestMethods::~CXFA_DigestMethods() = default;
diff --git a/xfa/fxfa/parser/cxfa_digestmethods.h b/xfa/fxfa/parser/cxfa_digestmethods.h
index 61dac97..516fbb7 100644
--- a/xfa/fxfa/parser/cxfa_digestmethods.h
+++ b/xfa/fxfa/parser/cxfa_digestmethods.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_DigestMethods final : public CXFA_Node {
  public:
-  CXFA_DigestMethods(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_DigestMethods() override;
+
+ private:
+  CXFA_DigestMethods(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DIGESTMETHODS_H_
diff --git a/xfa/fxfa/parser/cxfa_document.cpp b/xfa/fxfa/parser/cxfa_document.cpp
index 1a2c5b9..45c7849 100644
--- a/xfa/fxfa/parser/cxfa_document.cpp
+++ b/xfa/fxfa/parser/cxfa_document.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,12 +10,16 @@
 #include <utility>
 
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/stl_util.h"
 #include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "core/fxcrt/xml/cfx_xmlelement.h"
+#include "fxjs/gc/container_trace.h"
 #include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_resolveprocessor.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/notreached.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cscript_datawindow.h"
@@ -43,7 +47,6 @@
 #include "xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h"
 #include "xfa/fxfa/parser/cxfa_value.h"
 #include "xfa/fxfa/parser/xfa_document_datamerger_imp.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
 namespace {
@@ -51,8 +54,8 @@
 const wchar_t kTemplateNS[] = L"http://www.xfa.org/schema/xfa-template/";
 
 struct RecurseRecord {
-  CXFA_Node* pTemplateChild;
-  CXFA_Node* pDataChild;
+  cppgc::Persistent<CXFA_Node> pTemplateChild;
+  cppgc::Persistent<CXFA_Node> pDataChild;
 };
 
 class CXFA_TraverseStrategy_DDGroup {
@@ -69,7 +72,7 @@
 };
 
 void FormValueNode_MatchNoneCreateChild(CXFA_Node* pFormNode) {
-  ASSERT(pFormNode->IsWidgetReady());
+  DCHECK(pFormNode->IsWidgetReady());
   // GetUIChildNode has the side effect of creating the UI child.
   pFormNode->GetUIChildNode();
 }
@@ -92,7 +95,7 @@
   if (!pValueNode)
     return;
 
-  ASSERT(pValueNode->GetPacketType() == XFA_PacketType::Form);
+  DCHECK_EQ(pValueNode->GetPacketType(), XFA_PacketType::Form);
   CXFA_Node* pChildNode = FormValueNode_CreateChild(pValueNode, iType);
   if (!pChildNode)
     return;
@@ -103,7 +106,7 @@
       if (!pContentRawDataNode) {
         XFA_Element element = XFA_Element::Sharptext;
         if (pChildNode->GetElementType() == XFA_Element::ExData) {
-          Optional<WideString> contentType =
+          absl::optional<WideString> contentType =
               pChildNode->JSObject()->TryAttribute(XFA_Attribute::ContentType,
                                                    false);
           if (contentType.has_value()) {
@@ -116,15 +119,14 @@
         pContentRawDataNode = pChildNode->CreateSamePacketNode(element);
         pChildNode->InsertChildAndNotify(pContentRawDataNode, nullptr);
       }
-      pContentRawDataNode->JSObject()->SetCData(XFA_Attribute::Value, wsContent,
-                                                false, false);
+      pContentRawDataNode->JSObject()->SetCData(XFA_Attribute::Value,
+                                                wsContent);
       break;
     }
     case XFA_ObjectType::NodeC:
     case XFA_ObjectType::TextNode:
     case XFA_ObjectType::NodeV: {
-      pChildNode->JSObject()->SetCData(XFA_Attribute::Value, wsContent, false,
-                                       false);
+      pChildNode->JSObject()->SetCData(XFA_Attribute::Value, wsContent);
       break;
     }
     default:
@@ -139,7 +141,7 @@
     if (pFormChild->GetElementType() == pProtoNode->GetElementType() &&
         pFormChild->GetNameHash() == pProtoNode->GetNameHash() &&
         pFormChild->IsUnusedNode()) {
-      pFormChild->ClearFlag(XFA_NodeFlag_UnusedNode);
+      pFormChild->ClearFlag(XFA_NodeFlag::kUnusedNode);
       pExistingNode = pFormChild;
       break;
     }
@@ -163,7 +165,7 @@
     CXFA_NodeIterator sIterator(pDestNode);
     for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
          pNode = sIterator.MoveToNext()) {
-      pNode->SetFlag(XFA_NodeFlag_UnusedNode);
+      pNode->SetFlag(XFA_NodeFlag::kUnusedNode);
     }
   }
   pDestNode->SetTemplateNode(pProtoNode);
@@ -175,7 +177,7 @@
     CXFA_NodeIterator sIterator(pDestNode);
     for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
          pNode = sIterator.MoveToNext()) {
-      pNode->ClearFlag(XFA_NodeFlag_UnusedNode);
+      pNode->ClearFlag(XFA_NodeFlag::kUnusedNode);
     }
   }
 }
@@ -187,8 +189,7 @@
   WideString wsSubformName =
       pTemplateNode->JSObject()->GetCData(XFA_Attribute::Name);
   WideString wsInstMgrNodeName = L"_" + wsSubformName;
-  uint32_t dwInstNameHash =
-      FX_HashCode_GetW(wsInstMgrNodeName.AsStringView(), false);
+  uint32_t dwInstNameHash = FX_HashCode_GetW(wsInstMgrNodeName.AsStringView());
   CXFA_Node* pExistingNode = XFA_DataMerge_FindFormDOMInstance(
       pDocument, XFA_Element::InstanceManager, dwInstNameHash, pFormParent);
   if (pExistingNode) {
@@ -213,7 +214,7 @@
     }
     pFormParent->RemoveChildAndNotify(pExistingNode, true);
     pFormParent->InsertChildAndNotify(pExistingNode, nullptr);
-    pExistingNode->ClearFlag(XFA_NodeFlag_UnusedNode);
+    pExistingNode->ClearFlag(XFA_NodeFlag::kUnusedNode);
     pExistingNode->SetTemplateNode(pTemplateNode);
     return pExistingNode;
   }
@@ -222,8 +223,7 @@
       pDocument->CreateNode(XFA_PacketType::Form, XFA_Element::InstanceManager);
   wsInstMgrNodeName =
       L"_" + pTemplateNode->JSObject()->GetCData(XFA_Attribute::Name);
-  pNewNode->JSObject()->SetCData(XFA_Attribute::Name, wsInstMgrNodeName, false,
-                                 false);
+  pNewNode->JSObject()->SetCData(XFA_Attribute::Name, wsInstMgrNodeName);
   pFormParent->InsertChildAndNotify(pNewNode, nullptr);
   pNewNode->SetTemplateNode(pTemplateNode);
   return pNewNode;
@@ -301,7 +301,7 @@
   if (wsName.IsEmpty())
     return nullptr;
 
-  uint32_t dwNameHash = FX_HashCode_GetW(wsName.AsStringView(), false);
+  uint32_t dwNameHash = FX_HashCode_GetW(wsName.AsStringView());
   CXFA_Node* pBounded = pDocument->GetGlobalBinding(dwNameHash);
   if (!pBounded) {
     pBounded =
@@ -318,7 +318,7 @@
   if (wsName.IsEmpty())
     return nullptr;
 
-  uint32_t dwNameHash = FX_HashCode_GetW(wsName.AsStringView(), false);
+  uint32_t dwNameHash = FX_HashCode_GetW(wsName.AsStringView());
   CXFA_Node* pLastDataScope = nullptr;
   for (CXFA_Node* pCurDataScope = pDataScope;
        pCurDataScope &&
@@ -346,22 +346,31 @@
                                CXFA_Node* pTemplateNode,
                                bool bForceBind,
                                bool bUpLevel) {
-  uint32_t dFlags = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_BindNew;
-  if (bUpLevel || !wsRef.EqualsASCII("name"))
-    dFlags |= (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings);
+  Mask<XFA_ResolveFlag> dwFlags = {XFA_ResolveFlag::kChildren,
+                                   XFA_ResolveFlag::kBindNew};
+  if (bUpLevel || !wsRef.EqualsASCII("name")) {
+    dwFlags |= XFA_ResolveFlag::kParent;
+    dwFlags |= XFA_ResolveFlag::kSiblings;
+  }
+  absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+      pDocument->GetScriptContext()->ResolveObjectsWithBindNode(
+          pDataScope, wsRef.AsStringView(), dwFlags, pTemplateNode);
+  if (!maybeResult.has_value())
+    return nullptr;
 
-  XFA_RESOLVENODE_RS rs;
-  pDocument->GetScriptContext()->ResolveObjects(
-      pDataScope, wsRef.AsStringView(), &rs, dFlags, pTemplateNode);
-  if (rs.dwFlags == XFA_ResolveNode_RSType_CreateNodeAll ||
-      rs.dwFlags == XFA_ResolveNode_RSType_CreateNodeMidAll ||
-      rs.objects.size() > 1) {
-    return pDocument->GetNotBindNode(rs.objects);
+  if (maybeResult.value().type ==
+          CFXJSE_Engine::ResolveResult::Type::kCreateNodeAll ||
+      maybeResult.value().type ==
+          CFXJSE_Engine::ResolveResult::Type::kCreateNodeMidAll ||
+      maybeResult.value().objects.size() > 1) {
+    return pDocument->GetNotBindNode(maybeResult.value().objects);
   }
 
-  if (rs.dwFlags == XFA_ResolveNode_RSType_CreateNodeOne) {
-    CXFA_Object* pObject =
-        !rs.objects.empty() ? rs.objects.front().Get() : nullptr;
+  if (maybeResult.value().type ==
+      CFXJSE_Engine::ResolveResult::Type::kCreateNodeOne) {
+    CXFA_Object* pObject = !maybeResult.value().objects.empty()
+                               ? maybeResult.value().objects.front().Get()
+                               : nullptr;
     CXFA_Node* pNode = ToNode(pObject);
     return (bForceBind || !pNode || !pNode->HasBindItem()) ? pNode : nullptr;
   }
@@ -445,7 +454,7 @@
           pResult = pGlobalBindNode;
           break;
         }
-        FALLTHROUGH;
+        [[fallthrough]];
       case XFA_AttributeValue::Once: {
         bAccessedDataDOM = true;
         CXFA_Node* pOnceBindNode = FindOnceDataNode(
@@ -493,7 +502,7 @@
   if (eType != XFA_Element::Field && eType != XFA_Element::ExclGroup)
     return;
 
-  ASSERT(pFormNode->IsWidgetReady());
+  DCHECK(pFormNode->IsWidgetReady());
   auto* defValue = pFormNode->JSObject()->GetOrCreateProperty<CXFA_Value>(
       0, XFA_Element::Value);
   if (!bDataToForm) {
@@ -510,11 +519,11 @@
         }
         CFX_XMLElement* pXMLDataElement =
             ToXMLElement(pDataNode->GetXMLMappingNode());
-        ASSERT(pXMLDataElement);
+        DCHECK(pXMLDataElement);
         pDataNode->JSObject()->SetAttributeValue(
-            wsValue, pFormNode->GetFormatDataValue(wsValue), false, false);
+            wsValue, pFormNode->GetFormatDataValue(wsValue));
         pDataNode->JSObject()->SetCData(XFA_Attribute::ContentType,
-                                        wsContentType, false, false);
+                                        wsContentType);
         if (!wsHref.IsEmpty())
           pXMLDataElement->SetAttribute(L"href", wsHref);
 
@@ -529,12 +538,10 @@
             for (const auto& text : wsSelTextArray) {
               CXFA_Node* pValue =
                   pDataNode->CreateSamePacketNode(XFA_Element::DataValue);
-              pValue->JSObject()->SetCData(XFA_Attribute::Name, L"value", false,
-                                           false);
+              pValue->JSObject()->SetCData(XFA_Attribute::Name, L"value");
               pValue->CreateXMLMappingNode();
               pDataNode->InsertChildAndNotify(pValue, nullptr);
-              pValue->JSObject()->SetCData(XFA_Attribute::Value, text, false,
-                                           false);
+              pValue->JSObject()->SetCData(XFA_Attribute::Value, text);
             }
           } else {
             CFX_XMLElement* pElement =
@@ -543,7 +550,7 @@
           }
         } else if (!wsValue.IsEmpty()) {
           pDataNode->JSObject()->SetAttributeValue(
-              wsValue, pFormNode->GetFormatDataValue(wsValue), false, false);
+              wsValue, pFormNode->GetFormatDataValue(wsValue));
         }
         break;
       case XFA_FFWidgetType::kCheckButton:
@@ -552,7 +559,7 @@
           break;
 
         pDataNode->JSObject()->SetAttributeValue(
-            wsValue, pFormNode->GetFormatDataValue(wsValue), false, false);
+            wsValue, pFormNode->GetFormatDataValue(wsValue));
         break;
       case XFA_FFWidgetType::kExclGroup: {
         CXFA_Node* pChecked = nullptr;
@@ -582,10 +589,8 @@
           WideString wsContent = pText->JSObject()->GetContent(false);
           if (wsContent == wsValue) {
             pChecked = pChild;
-            pDataNode->JSObject()->SetAttributeValue(wsValue, wsValue, false,
-                                                     false);
-            pFormNode->JSObject()->SetCData(XFA_Attribute::Value, wsContent,
-                                            false, false);
+            pDataNode->JSObject()->SetAttributeValue(wsValue, wsValue);
+            pFormNode->JSObject()->SetCData(XFA_Attribute::Value, wsContent);
             break;
           }
         }
@@ -623,7 +628,7 @@
 
         wsValue = pFormNode->NormalizeNumStr(wsValue);
         pDataNode->JSObject()->SetAttributeValue(
-            wsValue, pFormNode->GetFormatDataValue(wsValue), false, false);
+            wsValue, pFormNode->GetFormatDataValue(wsValue));
         CXFA_Value* pValue =
             pFormNode->JSObject()->GetOrCreateProperty<CXFA_Value>(
                 0, XFA_Element::Value);
@@ -636,7 +641,7 @@
           break;
 
         pDataNode->JSObject()->SetAttributeValue(
-            wsValue, pFormNode->GetFormatDataValue(wsValue), false, false);
+            wsValue, pFormNode->GetFormatDataValue(wsValue));
         break;
     }
     return;
@@ -645,8 +650,7 @@
   WideString wsXMLValue = pDataNode->JSObject()->GetContent(false);
   WideString wsNormalizeValue = pFormNode->GetNormalizeDataValue(wsXMLValue);
 
-  pDataNode->JSObject()->SetAttributeValue(wsNormalizeValue, wsXMLValue, false,
-                                           false);
+  pDataNode->JSObject()->SetAttributeValue(wsNormalizeValue, wsXMLValue);
   switch (pFormNode->GetFFWidgetType()) {
     case XFA_FFWidgetType::kImageEdit: {
       FormValueNode_SetChildContent(defValue, wsNormalizeValue,
@@ -659,7 +663,7 @@
             pXMLDataElement->GetAttribute(L"xfa:contentType");
         if (!wsContentType.IsEmpty()) {
           pDataNode->JSObject()->SetCData(XFA_Attribute::ContentType,
-                                          wsContentType, false, false);
+                                          wsContentType);
           image->SetContentType(wsContentType);
         }
 
@@ -672,7 +676,7 @@
     case XFA_FFWidgetType::kChoiceList:
       if (pFormNode->IsChoiceListMultiSelect()) {
         std::vector<CXFA_Node*> items = pDataNode->GetNodeListWithFilter(
-            XFA_NODEFILTER_Children | XFA_NODEFILTER_Properties);
+            {XFA_NodeFilter::kChildren, XFA_NodeFilter::kProperties});
         if (!items.empty()) {
           bool single = items.size() == 1;
           wsNormalizeValue.clear();
@@ -707,7 +711,7 @@
       break;
     case XFA_FFWidgetType::kNumericEdit: {
       WideString wsPicture =
-          pFormNode->GetPictureContent(XFA_VALUEPICTURE_DataBind);
+          pFormNode->GetPictureContent(XFA_ValuePicture::kDataBind);
       if (wsPicture.IsEmpty())
         wsNormalizeValue = pFormNode->NormalizeNumStr(wsNormalizeValue);
 
@@ -733,10 +737,10 @@
   if (!pParentDDNode) {
     CXFA_Node* pDataNode =
         pDocument->CreateNode(XFA_PacketType::Datasets, eNodeType);
-    pDataNode->JSObject()->SetCData(XFA_Attribute::Name, wsName, false, false);
+    pDataNode->JSObject()->SetCData(XFA_Attribute::Name, wsName);
     pDataNode->CreateXMLMappingNode();
     pDataParent->InsertChildAndNotify(pDataNode, nullptr);
-    pDataNode->SetFlag(XFA_NodeFlag_Initialized);
+    pDataNode->SetFlag(XFA_NodeFlag::kInitialized);
     return pDataNode;
   }
 
@@ -748,7 +752,7 @@
       if (pDDGroupNode->GetElementType() != XFA_Element::DataGroup)
         continue;
 
-      Optional<WideString> ns = pDDGroupNode->JSObject()->TryNamespace();
+      absl::optional<WideString> ns = pDDGroupNode->JSObject()->TryNamespace();
       if (!ns.has_value() ||
           !ns.value().EqualsASCII("http://ns.adobe.com/data-description/")) {
         continue;
@@ -764,7 +768,7 @@
 
     CXFA_Node* pDataNode =
         pDocument->CreateNode(XFA_PacketType::Datasets, eNodeType);
-    pDataNode->JSObject()->SetCData(XFA_Attribute::Name, wsName, false, false);
+    pDataNode->JSObject()->SetCData(XFA_Attribute::Name, wsName);
     pDataNode->CreateXMLMappingNode();
     if (eNodeType == XFA_Element::DataValue &&
         pDDNode->JSObject()->GetEnum(XFA_Attribute::Contains) ==
@@ -774,7 +778,7 @@
     }
     pDataParent->InsertChildAndNotify(pDataNode, nullptr);
     pDataNode->SetDataDescriptionNode(pDDNode);
-    pDataNode->SetFlag(XFA_NodeFlag_Initialized);
+    pDataNode->SetFlag(XFA_NodeFlag::kInitialized);
     return pDataNode;
   }
   return nullptr;
@@ -788,7 +792,7 @@
                                bool bUpLevel) {
   CXFA_Node* pFieldNode = XFA_NodeMerge_CloneOrMergeContainer(
       pDocument, pFormNode, pTemplateNode, false, nullptr);
-  ASSERT(pFieldNode);
+  DCHECK(pFieldNode);
   for (CXFA_Node* pTemplateChildNode = pTemplateNode->GetFirstChild();
        pTemplateChildNode;
        pTemplateChildNode = pTemplateChildNode->GetNextSibling()) {
@@ -851,10 +855,10 @@
       pOccurNode =
           pInstMgrNode->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
       if (pOccurNode)
-        pOccurNode->ClearFlag(XFA_NodeFlag_UnusedNode);
+        pOccurNode->ClearFlag(XFA_NodeFlag::kUnusedNode);
     }
     if (pInstMgrNode) {
-      pInstMgrNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+      pInstMgrNode->SetInitializedFlagAndNotify();
       pSearchArray = &subformArray;
       if (pFormParentNode->GetElementType() == XFA_Element::PageArea) {
         bOneInstance = true;
@@ -905,7 +909,7 @@
           pFirstInstance = pSubformNode;
 
         CreateDataBinding(pSubformNode, pDataNode, true);
-        ASSERT(pSubformNode);
+        DCHECK(pSubformNode);
         subformMapArray[pSubformNode] = pDataNode;
         nodeArray.push_back(pSubformNode);
       }
@@ -948,7 +952,7 @@
           eRelation == XFA_AttributeValue::Unordered) {
         CXFA_Node* pSubformSetNode = XFA_NodeMerge_CloneOrMergeContainer(
             pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
-        ASSERT(pSubformSetNode);
+        DCHECK(pSubformSetNode);
         if (!pFirstInstance)
           pFirstInstance = pSubformSetNode;
 
@@ -988,7 +992,7 @@
 
         switch (eRelation) {
           case XFA_AttributeValue::Choice: {
-            ASSERT(!rgItemMatchList.empty());
+            DCHECK(!rgItemMatchList.empty());
             SortRecurseRecord(&rgItemMatchList, pDataScope, true);
             pDocument->DataMerge_CopyContainer(
                 rgItemMatchList.front().pTemplateChild, pSubformSetNode,
@@ -1016,7 +1020,7 @@
       } else {
         CXFA_Node* pSubformSetNode = XFA_NodeMerge_CloneOrMergeContainer(
             pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
-        ASSERT(pSubformSetNode);
+        DCHECK(pSubformSetNode);
         if (!pFirstInstance)
           pFirstInstance = pSubformSetNode;
 
@@ -1038,7 +1042,7 @@
     if (iCurRepeatIndex == 0 && !bAccessedDataDOM) {
       int32_t iLimit = iMax;
       if (pInstMgrNode && pTemplateNode->GetNameHash() == 0) {
-        iLimit = pdfium::CollectionSize<int32_t>(subformArray);
+        iLimit = fxcrt::CollectionSize<int32_t>(subformArray);
         if (iLimit < iMin)
           iLimit = iInit;
       }
@@ -1057,7 +1061,7 @@
         }
         CXFA_Node* pSubformNode = XFA_NodeMerge_CloneOrMergeContainer(
             pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
-        ASSERT(pSubformNode);
+        DCHECK(pSubformNode);
         if (!pFirstInstance)
           pFirstInstance = pSubformNode;
 
@@ -1081,7 +1085,7 @@
   for (; iCurRepeatIndex < iMinimalLimit; iCurRepeatIndex++) {
     CXFA_Node* pSubformSetNode = XFA_NodeMerge_CloneOrMergeContainer(
         pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
-    ASSERT(pSubformSetNode);
+    DCHECK(pSubformSetNode);
     if (!pFirstInstance)
       pFirstInstance = pSubformSetNode;
 
@@ -1151,7 +1155,7 @@
           } else {
             CXFA_Node* pDataParent = pDataNode->GetParent();
             if (pDataParent != pDataScope) {
-              ASSERT(pDataParent);
+              DCHECK(pDataParent);
               pDataParent->RemoveChildAndNotify(pDataNode, true);
               pDataScope->InsertChildAndNotify(pDataNode, nullptr);
             }
@@ -1195,17 +1199,21 @@
               pTemplateNodeBind
                   ? pTemplateNodeBind->JSObject()->GetCData(XFA_Attribute::Ref)
                   : WideString();
-          uint32_t dFlags =
-              XFA_RESOLVENODE_Children | XFA_RESOLVENODE_CreateNode;
-          XFA_RESOLVENODE_RS rs;
-          pDocument->GetScriptContext()->ResolveObjects(
-              pDataScope, wsRef.AsStringView(), &rs, dFlags, pTemplateNode);
+          const Mask<XFA_ResolveFlag> kFlags = {XFA_ResolveFlag::kChildren,
+                                                XFA_ResolveFlag::kCreateNode};
+          absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+              pDocument->GetScriptContext()->ResolveObjectsWithBindNode(
+                  pDataScope, wsRef.AsStringView(), kFlags, pTemplateNode);
           CXFA_Object* pObject =
-              !rs.objects.empty() ? rs.objects.front().Get() : nullptr;
+              maybeResult.has_value() && !maybeResult.value().objects.empty()
+                  ? maybeResult.value().objects.front().Get()
+                  : nullptr;
           pDataNode = ToNode(pObject);
           if (pDataNode) {
-            CreateDataBinding(pFormNode, pDataNode,
-                              rs.dwFlags == XFA_ResolveNode_RSType_ExistNodes);
+            CreateDataBinding(
+                pFormNode, pDataNode,
+                maybeResult.value().type ==
+                    CFXJSE_Engine::ResolveResult::Type::kExistNodes);
           } else {
             FormValueNode_MatchNoneCreateChild(pFormNode);
           }
@@ -1236,7 +1244,7 @@
 }
 
 void UpdateDataRelation(CXFA_Node* pDataNode, CXFA_Node* pDataDescriptionNode) {
-  ASSERT(pDataDescriptionNode);
+  DCHECK(pDataDescriptionNode);
   for (CXFA_Node* pDataChild = pDataNode->GetFirstChild(); pDataChild;
        pDataChild = pDataChild->GetNextSibling()) {
     uint32_t dwNameHash = pDataChild->GetNameHash();
@@ -1251,7 +1259,8 @@
         if (pDDGroupNode->GetElementType() != XFA_Element::DataGroup)
           continue;
 
-        Optional<WideString> ns = pDDGroupNode->JSObject()->TryNamespace();
+        absl::optional<WideString> ns =
+            pDDGroupNode->JSObject()->TryNamespace();
         if (!ns.has_value() ||
             !ns.value().EqualsASCII("http://ns.adobe.com/data-description/")) {
           continue;
@@ -1274,9 +1283,12 @@
 }  // namespace
 
 CXFA_Document::CXFA_Document(CXFA_FFNotify* notify,
-                             std::unique_ptr<LayoutProcessorIface> pLayout)
-    : CXFA_NodeOwner(),
+                             cppgc::Heap* heap,
+                             LayoutProcessorIface* pLayout)
+    : heap_(heap),
       notify_(notify),
+      node_owner_(cppgc::MakeGarbageCollected<CXFA_NodeOwner>(
+          heap->GetAllocationHandle())),
       m_pLayoutProcessor(std::move(pLayout)) {
   if (m_pLayoutProcessor)
     m_pLayoutProcessor->SetDocument(this);
@@ -1284,16 +1296,32 @@
 
 CXFA_Document::~CXFA_Document() = default;
 
+void CXFA_Document::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(notify_);
+  visitor->Trace(node_owner_);
+  visitor->Trace(m_pRootNode);
+  visitor->Trace(m_pLocaleMgr);
+  visitor->Trace(m_pLayoutProcessor);
+  visitor->Trace(m_pScriptDataWindow);
+  visitor->Trace(m_pScriptEvent);
+  visitor->Trace(m_pScriptHost);
+  visitor->Trace(m_pScriptLog);
+  visitor->Trace(m_pScriptLayout);
+  visitor->Trace(m_pScriptSignature);
+  ContainerTrace(visitor, m_rgGlobalBinding);
+  ContainerTrace(visitor, m_pPendingPageSet);
+}
+
 void CXFA_Document::ClearLayoutData() {
-  m_pLayoutProcessor.reset();
+  m_pLayoutProcessor = nullptr;
   m_pScriptContext.reset();
-  m_pLocaleMgr.reset();
-  m_pScriptDataWindow.reset();
-  m_pScriptEvent.reset();
-  m_pScriptHost.reset();
-  m_pScriptLog.reset();
-  m_pScriptLayout.reset();
-  m_pScriptSignature.reset();
+  m_pLocaleMgr.Clear();
+  m_pScriptDataWindow = nullptr;
+  m_pScriptEvent = nullptr;
+  m_pScriptHost = nullptr;
+  m_pScriptLog = nullptr;
+  m_pScriptLayout = nullptr;
+  m_pScriptSignature = nullptr;
 }
 
 CXFA_Object* CXFA_Document::GetXFAObject(XFA_HashCode dwNodeNameHash) {
@@ -1313,16 +1341,16 @@
         if (pDatasetsChild->GetNameHash() != XFA_HASHCODE_Data)
           continue;
 
-        Optional<WideString> namespaceURI =
+        absl::optional<WideString> namespaceURI =
             pDatasetsChild->JSObject()->TryNamespace();
-        if (!namespaceURI)
+        if (!namespaceURI.has_value())
           continue;
 
-        Optional<WideString> datasetsURI =
+        absl::optional<WideString> datasetsURI =
             pDatasetsNode->JSObject()->TryNamespace();
-        if (!datasetsURI)
+        if (!datasetsURI.has_value())
           continue;
-        if (*namespaceURI == *datasetsURI)
+        if (namespaceURI.value() == datasetsURI.value())
           return pDatasetsChild;
       }
       return nullptr;
@@ -1335,34 +1363,41 @@
     }
     case XFA_HASHCODE_DataWindow: {
       if (!m_pScriptDataWindow)
-        m_pScriptDataWindow = pdfium::MakeUnique<CScript_DataWindow>(this);
-      return m_pScriptDataWindow.get();
+        m_pScriptDataWindow = cppgc::MakeGarbageCollected<CScript_DataWindow>(
+            GetHeap()->GetAllocationHandle(), this);
+      return m_pScriptDataWindow;
     }
     case XFA_HASHCODE_Event: {
       if (!m_pScriptEvent)
-        m_pScriptEvent = pdfium::MakeUnique<CScript_EventPseudoModel>(this);
-      return m_pScriptEvent.get();
+        m_pScriptEvent = cppgc::MakeGarbageCollected<CScript_EventPseudoModel>(
+            GetHeap()->GetAllocationHandle(), this);
+      return m_pScriptEvent;
     }
     case XFA_HASHCODE_Host: {
       if (!m_pScriptHost)
-        m_pScriptHost = pdfium::MakeUnique<CScript_HostPseudoModel>(this);
-      return m_pScriptHost.get();
+        m_pScriptHost = cppgc::MakeGarbageCollected<CScript_HostPseudoModel>(
+            GetHeap()->GetAllocationHandle(), this);
+      return m_pScriptHost;
     }
     case XFA_HASHCODE_Log: {
       if (!m_pScriptLog)
-        m_pScriptLog = pdfium::MakeUnique<CScript_LogPseudoModel>(this);
-      return m_pScriptLog.get();
+        m_pScriptLog = cppgc::MakeGarbageCollected<CScript_LogPseudoModel>(
+            GetHeap()->GetAllocationHandle(), this);
+      return m_pScriptLog;
     }
     case XFA_HASHCODE_Signature: {
       if (!m_pScriptSignature)
         m_pScriptSignature =
-            pdfium::MakeUnique<CScript_SignaturePseudoModel>(this);
-      return m_pScriptSignature.get();
+            cppgc::MakeGarbageCollected<CScript_SignaturePseudoModel>(
+                GetHeap()->GetAllocationHandle(), this);
+      return m_pScriptSignature;
     }
     case XFA_HASHCODE_Layout: {
       if (!m_pScriptLayout)
-        m_pScriptLayout = pdfium::MakeUnique<CScript_LayoutPseudoModel>(this);
-      return m_pScriptLayout.get();
+        m_pScriptLayout =
+            cppgc::MakeGarbageCollected<CScript_LayoutPseudoModel>(
+                GetHeap()->GetAllocationHandle(), this);
+      return m_pScriptLayout;
     }
     default:
       return m_pRootNode->GetFirstChildByName(dwNodeNameHash);
@@ -1373,7 +1408,8 @@
                                      XFA_Element eElement) {
   if (eElement == XFA_Element::Unknown)
     return nullptr;
-  return AddOwnedNode(CXFA_Node::Create(this, eElement, packet));
+
+  return CXFA_Node::Create(this, eElement, packet);
 }
 
 bool CXFA_Document::IsInteractive() {
@@ -1406,32 +1442,46 @@
 
 CXFA_LocaleMgr* CXFA_Document::GetLocaleMgr() {
   if (!m_pLocaleMgr) {
-    m_pLocaleMgr = pdfium::MakeUnique<CXFA_LocaleMgr>(
+    m_pLocaleMgr = cppgc::MakeGarbageCollected<CXFA_LocaleMgr>(
+        heap_->GetAllocationHandle(), heap_,
         ToNode(GetXFAObject(XFA_HASHCODE_LocaleSet)),
         GetNotify()->GetAppProvider()->GetLanguage());
   }
-  return m_pLocaleMgr.get();
+  return m_pLocaleMgr;
+}
+
+cppgc::Heap* CXFA_Document::GetHeap() const {
+  return heap_;
 }
 
 CFXJSE_Engine* CXFA_Document::InitScriptContext(CJS_Runtime* fxjs_runtime) {
-  ASSERT(!m_pScriptContext);
-  m_pScriptContext = pdfium::MakeUnique<CFXJSE_Engine>(this, fxjs_runtime);
+  DCHECK(!m_pScriptContext);
+  m_pScriptContext = std::make_unique<CFXJSE_Engine>(this, fxjs_runtime);
   return m_pScriptContext.get();
 }
 
 CFXJSE_Engine* CXFA_Document::GetScriptContext() const {
-  ASSERT(m_pScriptContext);
+  DCHECK(m_pScriptContext);
   return m_pScriptContext.get();
 }
 
 XFA_VERSION CXFA_Document::RecognizeXFAVersionNumber(
     const WideString& wsTemplateNS) {
+  XFA_VERSION eVersion = ParseXFAVersion(wsTemplateNS);
+  if (eVersion != XFA_VERSION_UNKNOWN)
+    m_eCurVersionMode = eVersion;
+
+  return eVersion;
+}
+
+// static
+XFA_VERSION CXFA_Document::ParseXFAVersion(const WideString& wsTemplateNS) {
   WideStringView wsTemplateURIPrefix(kTemplateNS);
   if (wsTemplateNS.GetLength() <= wsTemplateURIPrefix.GetLength())
     return XFA_VERSION_UNKNOWN;
 
   size_t prefixLength = wsTemplateURIPrefix.GetLength();
-  if (WideStringView(wsTemplateNS.c_str(), prefixLength) != wsTemplateURIPrefix)
+  if (wsTemplateNS.AsStringView().First(prefixLength) != wsTemplateURIPrefix)
     return XFA_VERSION_UNKNOWN;
 
   auto nDotPos = wsTemplateNS.Find('.', prefixLength);
@@ -1441,21 +1491,17 @@
   int8_t iMajor = FXSYS_wtoi(
       wsTemplateNS.Substr(prefixLength, nDotPos.value() - prefixLength)
           .c_str());
-  int8_t iMinor =
-      FXSYS_wtoi(wsTemplateNS
-                     .Substr(nDotPos.value() + 1,
-                             wsTemplateNS.GetLength() - nDotPos.value() - 2)
-                     .c_str());
-  XFA_VERSION eVersion = (XFA_VERSION)((int32_t)iMajor * 100 + iMinor);
+  int8_t iMinor = FXSYS_wtoi(wsTemplateNS.Substr(nDotPos.value() + 1).c_str());
+  XFA_VERSION eVersion =
+      static_cast<XFA_VERSION>(static_cast<int32_t>(iMajor) * 100 + iMinor);
   if (eVersion < XFA_VERSION_MIN || eVersion > XFA_VERSION_MAX)
     return XFA_VERSION_UNKNOWN;
 
-  m_eCurVersionMode = eVersion;
   return eVersion;
 }
 
 FormType CXFA_Document::GetFormType() const {
-  return GetNotify()->GetHDOC()->GetFormType();
+  return GetNotify()->GetFFDoc()->GetFormType();
 }
 
 CXFA_Node* CXFA_Document::GetNodeByID(CXFA_Node* pRoot,
@@ -1485,7 +1531,7 @@
        pNode = sIterator.MoveToNext()) {
     WideString wsIDVal = pNode->JSObject()->GetCData(XFA_Attribute::Id);
     if (!wsIDVal.IsEmpty())
-      mIDMap[FX_HashCode_GetW(wsIDVal.AsStringView(), false)] = pNode;
+      mIDMap[FX_HashCode_GetW(wsIDVal.AsStringView())] = pNode;
 
     WideString wsUseVal = pNode->JSObject()->GetCData(XFA_Attribute::Use);
     if (!wsUseVal.IsEmpty()) {
@@ -1504,51 +1550,31 @@
     WideStringView wsURI;
     WideStringView wsID;
     WideStringView wsSOM;
-
     if (!wsUseVal.IsEmpty()) {
-      auto uSharpPos = wsUseVal.Find('#');
-      if (!uSharpPos.has_value()) {
-        wsURI = wsUseVal.AsStringView();
-      } else {
-        wsURI = WideStringView(wsUseVal.c_str(), uSharpPos.value());
-        size_t uLen = wsUseVal.GetLength();
-        if (uLen >= uSharpPos.value() + 5 &&
-            WideStringView(wsUseVal.c_str() + uSharpPos.value(), 5) ==
-                L"#som(" &&
-            wsUseVal[uLen - 1] == ')') {
-          wsSOM = WideStringView(wsUseVal.c_str() + uSharpPos.value() + 5,
-                                 uLen - 1 - uSharpPos.value() - 5);
-        } else {
-          wsID = WideStringView(wsUseVal.c_str() + uSharpPos.value() + 1,
-                                uLen - uSharpPos.value() - 1);
-        }
-      }
+      ParseUseHref(wsUseVal, wsURI, wsID, wsSOM);
+      if (!wsURI.IsEmpty() && !wsURI.EqualsASCII("."))
+        continue;
     } else {
       wsUseVal = pUseHrefNode->JSObject()->GetCData(XFA_Attribute::Use);
-      if (!wsUseVal.IsEmpty()) {
-        if (wsUseVal[0] == '#')
-          wsID = WideStringView(wsUseVal.c_str() + 1, wsUseVal.GetLength() - 1);
-        else
-          wsSOM = WideStringView(wsUseVal.c_str(), wsUseVal.GetLength());
-      }
+      ParseUse(wsUseVal, wsID, wsSOM);
     }
-    if (!wsURI.IsEmpty() && !wsURI.EqualsASCII("."))
-      continue;
 
     CXFA_Node* pProtoNode = nullptr;
     if (!wsSOM.IsEmpty()) {
-      uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
-                        XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent |
-                        XFA_RESOLVENODE_Siblings;
-      XFA_RESOLVENODE_RS resolveNodeRS;
-      if (m_pScriptContext->ResolveObjects(pUseHrefNode, wsSOM, &resolveNodeRS,
-                                           dwFlag, nullptr)) {
-        auto* pFirstObject = resolveNodeRS.objects.front().Get();
+      absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+          m_pScriptContext->ResolveObjects(
+              pUseHrefNode, wsSOM,
+              Mask<XFA_ResolveFlag>{
+                  XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes,
+                  XFA_ResolveFlag::kProperties, XFA_ResolveFlag::kParent,
+                  XFA_ResolveFlag::kSiblings});
+      if (maybeResult.has_value()) {
+        auto* pFirstObject = maybeResult.value().objects.front().Get();
         if (pFirstObject && pFirstObject->IsNode())
           pProtoNode = pFirstObject->AsNode();
       }
     } else if (!wsID.IsEmpty()) {
-      auto it = mIDMap.find(FX_HashCode_GetW(wsID, false));
+      auto it = mIDMap.find(FX_HashCode_GetW(wsID));
       if (it == mIDMap.end())
         continue;
       pProtoNode = it->second;
@@ -1560,13 +1586,51 @@
   }
 }
 
+// static
+void CXFA_Document::ParseUseHref(const WideString& wsUseVal,
+                                 WideStringView& wsURI,
+                                 WideStringView& wsID,
+                                 WideStringView& wsSOM) {
+  if (wsUseVal.IsEmpty())
+    return;
+
+  auto uSharpPos = wsUseVal.Find('#');
+  if (!uSharpPos.has_value()) {
+    wsURI = wsUseVal.AsStringView();
+    return;
+  }
+  wsURI = wsUseVal.AsStringView().First(uSharpPos.value());
+  if (wsUseVal.AsStringView().Substr(uSharpPos.value(), 5) == L"#som(" &&
+      wsUseVal.Back() == ')') {
+    wsSOM = wsUseVal.AsStringView().Substr(
+        uSharpPos.value() + 5,
+        wsUseVal.GetLength() - 1 - uSharpPos.value() - 5);
+    return;
+  }
+  wsID = wsUseVal.AsStringView().Substr(uSharpPos.value() + 1);
+}
+
+// static
+void CXFA_Document::ParseUse(const WideString& wsUseVal,
+                             WideStringView& wsID,
+                             WideStringView& wsSOM) {
+  if (wsUseVal.IsEmpty())
+    return;
+
+  if (wsUseVal[0] == '#') {
+    wsID = wsUseVal.AsStringView().Substr(1);
+    return;
+  }
+  wsSOM = wsUseVal.AsStringView();
+}
+
 CXFA_Node* CXFA_Document::DataMerge_CopyContainer(CXFA_Node* pTemplateNode,
                                                   CXFA_Node* pFormNode,
                                                   CXFA_Node* pDataScope,
                                                   bool bOneInstance,
                                                   bool bDataMerge,
                                                   bool bUpLevel) {
-  ASSERT(pTemplateNode->IsContainerNode());
+  DCHECK(pTemplateNode->IsContainerNode());
   switch (pTemplateNode->GetElementType()) {
     case XFA_Element::Area:
     case XFA_Element::PageArea:
@@ -1584,8 +1648,7 @@
     case XFA_Element::Variables:
       return nullptr;
     default:
-      NOTREACHED();
-      return nullptr;
+      NOTREACHED_NORETURN();
   }
 }
 
@@ -1601,7 +1664,7 @@
 }
 
 CXFA_Node* CXFA_Document::GetNotBindNode(
-    const std::vector<UnownedPtr<CXFA_Object>>& arrayObjects) const {
+    pdfium::span<cppgc::Member<CXFA_Object>> arrayObjects) const {
   for (auto& pObject : arrayObjects) {
     CXFA_Node* pNode = pObject->AsNode();
     if (pNode && !pNode->HasBindItem())
@@ -1615,14 +1678,13 @@
   if (!pDatasetsRoot) {
     // Ownership will be passed in the AppendChild below to the XML tree.
     auto* pDatasetsXMLNode =
-        notify_->GetHDOC()->GetXMLDocument()->CreateNode<CFX_XMLElement>(
+        notify_->GetFFDoc()->GetXMLDocument()->CreateNode<CFX_XMLElement>(
             L"xfa:datasets");
     pDatasetsXMLNode->SetAttribute(L"xmlns:xfa",
                                    L"http://www.xfa.org/schema/xfa-data/1.0/");
     pDatasetsRoot =
         CreateNode(XFA_PacketType::Datasets, XFA_Element::DataModel);
-    pDatasetsRoot->JSObject()->SetCData(XFA_Attribute::Name, L"datasets", false,
-                                        false);
+    pDatasetsRoot->JSObject()->SetCData(XFA_Attribute::Name, L"datasets");
 
     m_pRootNode->GetXMLMappingNode()->AppendLastChild(pDatasetsXMLNode);
     m_pRootNode->InsertChildAndNotify(pDatasetsRoot, nullptr);
@@ -1639,7 +1701,7 @@
       continue;
 
     if (!pDDRoot && pChildNode->GetNameHash() == XFA_HASHCODE_DataDescription) {
-      Optional<WideString> namespaceURI =
+      absl::optional<WideString> namespaceURI =
           pChildNode->JSObject()->TryNamespace();
       if (!namespaceURI.has_value())
         continue;
@@ -1648,11 +1710,11 @@
         pDDRoot = pChildNode;
       }
     } else if (!pDataRoot && pChildNode->GetNameHash() == XFA_HASHCODE_Data) {
-      Optional<WideString> namespaceURI =
+      absl::optional<WideString> namespaceURI =
           pChildNode->JSObject()->TryNamespace();
-      if (!namespaceURI)
+      if (!namespaceURI.has_value())
         continue;
-      if (*namespaceURI == wsDatasetsURI)
+      if (namespaceURI == wsDatasetsURI)
         pDataRoot = pChildNode;
     }
     if (pDataRoot && pDDRoot)
@@ -1661,10 +1723,10 @@
 
   if (!pDataRoot) {
     pDataRoot = CreateNode(XFA_PacketType::Datasets, XFA_Element::DataGroup);
-    pDataRoot->JSObject()->SetCData(XFA_Attribute::Name, L"data", false, false);
+    pDataRoot->JSObject()->SetCData(XFA_Attribute::Name, L"data");
 
     auto* elem =
-        notify_->GetHDOC()->GetXMLDocument()->CreateNode<CFX_XMLElement>(
+        notify_->GetFFDoc()->GetXMLDocument()->CreateNode<CFX_XMLElement>(
             L"xfa:data");
     pDataRoot->SetXMLMappingNode(elem);
     pDatasetsRoot->InsertChildAndNotify(pDataRoot, nullptr);
@@ -1696,21 +1758,21 @@
     bEmptyForm = true;
     pFormRoot = static_cast<CXFA_Form*>(
         CreateNode(XFA_PacketType::Form, XFA_Element::Form));
-    ASSERT(pFormRoot);
-    pFormRoot->JSObject()->SetCData(XFA_Attribute::Name, L"form", false, false);
+    DCHECK(pFormRoot);
+    pFormRoot->JSObject()->SetCData(XFA_Attribute::Name, L"form");
     m_pRootNode->InsertChildAndNotify(pFormRoot, nullptr);
   } else {
     CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFANode>
         sIterator(pFormRoot);
     for (CXFA_Node* pNode = sIterator.MoveToNext(); pNode;
          pNode = sIterator.MoveToNext()) {
-      pNode->SetFlag(XFA_NodeFlag_UnusedNode);
+      pNode->SetFlag(XFA_NodeFlag::kUnusedNode);
     }
   }
 
   CXFA_Node* pSubformSetNode = XFA_NodeMerge_CloneOrMergeContainer(
       this, pFormRoot, pTemplateChosen, false, nullptr);
-  ASSERT(pSubformSetNode);
+  DCHECK(pSubformSetNode);
   if (!pDataTopLevel) {
     WideString wsFormName =
         pSubformSetNode->JSObject()->GetCData(XFA_Attribute::Name);
@@ -1718,11 +1780,11 @@
 
     pDataTopLevel = static_cast<CXFA_DataGroup*>(
         CreateNode(XFA_PacketType::Datasets, XFA_Element::DataGroup));
-    pDataTopLevel->JSObject()->SetCData(XFA_Attribute::Name, wsDataTopLevelName,
-                                        false, false);
+    pDataTopLevel->JSObject()->SetCData(XFA_Attribute::Name,
+                                        wsDataTopLevelName);
 
     auto* elem =
-        notify_->GetHDOC()->GetXMLDocument()->CreateNode<CFX_XMLElement>(
+        notify_->GetFFDoc()->GetXMLDocument()->CreateNode<CFX_XMLElement>(
             wsDataTopLevelName);
     pDataTopLevel->SetXMLMappingNode(elem);
 
@@ -1730,7 +1792,7 @@
     pDataRoot->InsertChildAndNotify(pDataTopLevel, pBeforeNode);
   }
 
-  ASSERT(pDataTopLevel);
+  DCHECK(pDataTopLevel);
   CreateDataBinding(pSubformSetNode, pDataTopLevel, true);
   for (CXFA_Node* pTemplateChild = pTemplateChosen->GetFirstChild();
        pTemplateChild; pTemplateChild = pTemplateChild->GetNextSibling()) {
@@ -1771,18 +1833,18 @@
         pNode->GetParent()->RemoveChildAndNotify(pNode, true);
         pNode = pNext;
       } else {
-        pNode->ClearFlag(XFA_NodeFlag_UnusedNode);
-        pNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+        pNode->ClearFlag(XFA_NodeFlag::kUnusedNode);
+        pNode->SetInitializedFlagAndNotify();
         pNode = sIterator.MoveToNext();
       }
     } else {
-      pNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+      pNode->SetInitializedFlagAndNotify();
       pNode = sIterator.MoveToNext();
     }
   }
 }
 
-void CXFA_Document::DoDataRemerge(bool bDoDataMerge) {
+void CXFA_Document::DoDataRemerge() {
   CXFA_Node* pFormRoot = ToNode(GetXFAObject(XFA_HASHCODE_Form));
   if (pFormRoot) {
     while (CXFA_Node* pNode = pFormRoot->GetFirstChild())
@@ -1791,11 +1853,8 @@
     pFormRoot->SetBindingNode(nullptr);
   }
   m_rgGlobalBinding.clear();
-
-  if (bDoDataMerge)
-    DoDataMerge();
-
-  GetLayoutProcessor()->SetForceRelayout(true);
+  DoDataMerge();
+  GetLayoutProcessor()->SetForceRelayout();
 }
 
 CXFA_Node* CXFA_Document::GetGlobalBinding(uint32_t dwNameHash) {
@@ -1808,6 +1867,22 @@
   m_rgGlobalBinding[dwNameHash] = pDataNode;
 }
 
+size_t CXFA_Document::GetPendingNodesCount() const {
+  return m_pPendingPageSet.size();
+}
+
+CXFA_Node* CXFA_Document::GetPendingNodeAtIndex(size_t index) const {
+  return m_pPendingPageSet[index];
+}
+
+void CXFA_Document::AppendPendingNode(CXFA_Node* node) {
+  m_pPendingPageSet.push_back(node);
+}
+
+void CXFA_Document::ClearPendingNodes() {
+  m_pPendingPageSet.clear();
+}
+
 void CXFA_Document::SetPendingNodesUnusedAndUnbound() {
   for (CXFA_Node* pPageNode : m_pPendingPageSet) {
     CXFA_NodeIterator sIterator(pPageNode);
@@ -1820,7 +1895,7 @@
           pNode->SetBindingNode(nullptr);
         }
       }
-      pNode->SetFlag(XFA_NodeFlag_UnusedNode);
+      pNode->SetFlag(XFA_NodeFlag::kUnusedNode);
     }
   }
 }
@@ -1828,3 +1903,7 @@
 CXFA_Document::LayoutProcessorIface::LayoutProcessorIface() = default;
 
 CXFA_Document::LayoutProcessorIface::~LayoutProcessorIface() = default;
+
+void CXFA_Document::LayoutProcessorIface::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pDocument);
+}
diff --git a/xfa/fxfa/parser/cxfa_document.h b/xfa/fxfa/parser/cxfa_document.h
index ea80475..3b33483 100644
--- a/xfa/fxfa/parser/cxfa_document.h
+++ b/xfa/fxfa/parser/cxfa_document.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,43 @@
 #ifndef XFA_FXFA_PARSER_CXFA_DOCUMENT_H_
 #define XFA_FXFA_PARSER_CXFA_DOCUMENT_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <map>
 #include <memory>
 #include <vector>
 
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/optional.h"
+#include "core/fxcrt/widestring.h"
+#include "fxjs/gc/heap.h"
+#include "third_party/abseil-cpp/absl/types/optional.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"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fxfa/fxfa.h"
+#include "xfa/fxfa/fxfa_basic.h"
 #include "xfa/fxfa/parser/cxfa_localemgr.h"
 #include "xfa/fxfa/parser/cxfa_nodeowner.h"
 
+class CFXJSE_Engine;
+class CJS_Runtime;
+class CScript_DataWindow;
+class CScript_EventPseudoModel;
+class CScript_HostPseudoModel;
+class CScript_LayoutPseudoModel;
+class CScript_LogPseudoModel;
+class CScript_SignaturePseudoModel;
+class CXFA_FFNotify;
+class CXFA_Node;
+class CXFA_Object;
+
+namespace cppgc {
+class Heap;
+}  // namespace cppgc
+
 enum XFA_VERSION {
   XFA_VERSION_UNKNOWN = 0,
   XFA_VERSION_200 = 200,
@@ -35,37 +62,29 @@
   XFA_VERSION_MAX = 400,
 };
 
-class CFXJSE_Engine;
-class CJS_Runtime;
-class CScript_DataWindow;
-class CScript_EventPseudoModel;
-class CScript_HostPseudoModel;
-class CScript_LayoutPseudoModel;
-class CScript_LogPseudoModel;
-class CScript_SignaturePseudoModel;
-class CXFA_FFNotify;
-class CXFA_Node;
-class CXFA_Object;
-
-class CXFA_Document final : public CXFA_NodeOwner {
+class CXFA_Document final : public cppgc::GarbageCollected<CXFA_Document> {
  public:
-  class LayoutProcessorIface {
+  class LayoutProcessorIface
+      : public cppgc::GarbageCollected<LayoutProcessorIface> {
    public:
     LayoutProcessorIface();
     virtual ~LayoutProcessorIface();
-    virtual void SetForceRelayout(bool enable) = 0;
-    virtual void AddChangedContainer(CXFA_Node* pContainer) = 0;
+
+    virtual void Trace(cppgc::Visitor* visitor) const;
+    virtual void SetForceRelayout() = 0;
+    virtual void SetHasChangedContainer() = 0;
 
     void SetDocument(CXFA_Document* pDocument) { m_pDocument = pDocument; }
-    CXFA_Document* GetDocument() const { return m_pDocument.Get(); }
+    CXFA_Document* GetDocument() const { return m_pDocument; }
 
    private:
-    UnownedPtr<CXFA_Document> m_pDocument;
+    cppgc::Member<CXFA_Document> m_pDocument;
   };
 
-  CXFA_Document(CXFA_FFNotify* notify,
-                std::unique_ptr<LayoutProcessorIface> pLayout);
-  ~CXFA_Document() override;
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_Document();
+
+  void Trace(cppgc::Visitor* visitor) const;
 
   bool HasScriptContext() const { return !!m_pScriptContext; }
   CFXJSE_Engine* InitScriptContext(CJS_Runtime* fxjs_runtime);
@@ -76,17 +95,18 @@
   // won't have an isolate set into it.
   CFXJSE_Engine* GetScriptContext() const;
 
-  CXFA_FFNotify* GetNotify() const { return notify_.Get(); }
+  CXFA_FFNotify* GetNotify() const { return notify_; }
+  CXFA_NodeOwner* GetNodeOwner() { return node_owner_; }
+  cppgc::Heap* GetHeap() const;
   CXFA_LocaleMgr* GetLocaleMgr();
   CXFA_Object* GetXFAObject(XFA_HashCode wsNodeNameHash);
   CXFA_Node* GetNodeByID(CXFA_Node* pRoot, WideStringView wsID) const;
   CXFA_Node* GetNotBindNode(
-      const std::vector<UnownedPtr<CXFA_Object>>& arrayNodes) const;
+      pdfium::span<cppgc::Member<CXFA_Object>> arrayNodes) const;
 
   LayoutProcessorIface* GetLayoutProcessor() const {
-    return m_pLayoutProcessor.get();
+    return m_pLayoutProcessor;
   }
-
   CXFA_Node* GetRoot() const { return m_pRootNode; }
   void SetRoot(CXFA_Node* pNewRoot) { m_pRootNode = pNewRoot; }
 
@@ -97,7 +117,7 @@
   void set_is_scripting() { m_bScripting = true; }
 
   bool IsInteractive();
-  XFA_VERSION GetCurVersionMode() { return m_eCurVersionMode; }
+  XFA_VERSION GetCurVersionMode() const { return m_eCurVersionMode; }
   XFA_VERSION RecognizeXFAVersionNumber(const WideString& wsTemplateNS);
   FormType GetFormType() const;
 
@@ -105,7 +125,7 @@
 
   void DoProtoMerge();
   void DoDataMerge();
-  void DoDataRemerge(bool bDoDataMerge);
+  void DoDataRemerge();
   CXFA_Node* DataMerge_CopyContainer(CXFA_Node* pTemplateNode,
                                      CXFA_Node* pFormNode,
                                      CXFA_Node* pDataScope,
@@ -118,25 +138,48 @@
 
   CXFA_Node* GetGlobalBinding(uint32_t dwNameHash);
   void RegisterGlobalBinding(uint32_t dwNameHash, CXFA_Node* pDataNode);
+
+  size_t GetPendingNodesCount() const;
+  CXFA_Node* GetPendingNodeAtIndex(size_t index) const;
+  void AppendPendingNode(CXFA_Node* node);
+  void ClearPendingNodes();
   void SetPendingNodesUnusedAndUnbound();
 
-  std::vector<CXFA_Node*> m_pPendingPageSet;
-
  private:
-  UnownedPtr<CXFA_FFNotify> const notify_;
-  CXFA_Node* m_pRootNode = nullptr;
-  std::map<uint32_t, CXFA_Node*> m_rgGlobalBinding;
+  friend class CXFA_DocumentTest_ParseXFAVersion_Test;
+  friend class CXFA_DocumentTest_ParseUseHref_Test;
+  friend class CXFA_DocumentTest_ParseUse_Test;
+
+  static XFA_VERSION ParseXFAVersion(const WideString& wsTemplateNS);
+  static void ParseUseHref(const WideString& wsUseVal,
+                           WideStringView& wsURI,
+                           WideStringView& wsID,
+                           WideStringView& wsSOM);
+  static void ParseUse(const WideString& wsUseVal,
+                       WideStringView& wsID,
+                       WideStringView& wsSOM);
+
+  CXFA_Document(CXFA_FFNotify* notify,
+                cppgc::Heap* heap,
+                LayoutProcessorIface* pLayout);
+
+  UnownedPtr<cppgc::Heap> heap_;
+  cppgc::Member<CXFA_FFNotify> const notify_;
+  cppgc::Member<CXFA_NodeOwner> const node_owner_;
+  cppgc::Member<CXFA_Node> m_pRootNode;
   std::unique_ptr<CFXJSE_Engine> m_pScriptContext;
-  std::unique_ptr<LayoutProcessorIface> m_pLayoutProcessor;
-  std::unique_ptr<CXFA_LocaleMgr> m_pLocaleMgr;
-  std::unique_ptr<CScript_DataWindow> m_pScriptDataWindow;
-  std::unique_ptr<CScript_EventPseudoModel> m_pScriptEvent;
-  std::unique_ptr<CScript_HostPseudoModel> m_pScriptHost;
-  std::unique_ptr<CScript_LogPseudoModel> m_pScriptLog;
-  std::unique_ptr<CScript_LayoutPseudoModel> m_pScriptLayout;
-  std::unique_ptr<CScript_SignaturePseudoModel> m_pScriptSignature;
+  cppgc::Member<LayoutProcessorIface> m_pLayoutProcessor;
+  cppgc::Member<CXFA_LocaleMgr> m_pLocaleMgr;
+  cppgc::Member<CScript_DataWindow> m_pScriptDataWindow;
+  cppgc::Member<CScript_EventPseudoModel> m_pScriptEvent;
+  cppgc::Member<CScript_HostPseudoModel> m_pScriptHost;
+  cppgc::Member<CScript_LogPseudoModel> m_pScriptLog;
+  cppgc::Member<CScript_LayoutPseudoModel> m_pScriptLayout;
+  cppgc::Member<CScript_SignaturePseudoModel> m_pScriptSignature;
+  std::map<uint32_t, cppgc::Member<CXFA_Node>> m_rgGlobalBinding;
+  std::vector<cppgc::Member<CXFA_Node>> m_pPendingPageSet;
   XFA_VERSION m_eCurVersionMode = XFA_VERSION_DEFAULT;
-  Optional<bool> m_Interactive;
+  absl::optional<bool> m_Interactive;
   bool m_bStrictScoping = false;
   bool m_bScripting = false;
 };
diff --git a/xfa/fxfa/parser/cxfa_document_builder.cpp b/xfa/fxfa/parser/cxfa_document_builder.cpp
new file mode 100644
index 0000000..c103284
--- /dev/null
+++ b/xfa/fxfa/parser/cxfa_document_builder.cpp
@@ -0,0 +1,1007 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fxfa/parser/cxfa_document_builder.h"
+
+#include <utility>
+#include <vector>
+
+#include "core/fxcrt/autorestorer.h"
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/xml/cfx_xmlchardata.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "core/fxcrt/xml/cfx_xmlelement.h"
+#include "core/fxcrt/xml/cfx_xmlinstruction.h"
+#include "core/fxcrt/xml/cfx_xmlnode.h"
+#include "core/fxcrt/xml/cfx_xmltext.h"
+#include "fxjs/xfa/cjx_object.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/check.h"
+#include "third_party/base/notreached.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/cxfa_subform.h"
+#include "xfa/fxfa/parser/cxfa_template.h"
+#include "xfa/fxfa/parser/xfa_basic_data.h"
+#include "xfa/fxfa/parser/xfa_utils.h"
+
+namespace {
+
+CFX_XMLNode* GetDocumentNode(CFX_XMLNode* pRootNode) {
+  for (CFX_XMLNode* pXMLNode = pRootNode->GetFirstChild(); pXMLNode;
+       pXMLNode = pXMLNode->GetNextSibling()) {
+    if (pXMLNode->GetType() == CFX_XMLNode::Type::kElement)
+      return pXMLNode;
+  }
+  return nullptr;
+}
+
+bool MatchNodeName(CFX_XMLNode* pNode,
+                   ByteStringView bsLocalTagName,
+                   ByteStringView bsNamespaceURIPrefix,
+                   XFA_PacketMatch eMatch) {
+  CFX_XMLElement* pElement = ToXMLElement(pNode);
+  if (!pElement)
+    return false;
+
+  if (!pElement->GetLocalTagName().EqualsASCII(bsLocalTagName))
+    return false;
+
+  if (eMatch == XFA_PacketMatch::kNoMatch)
+    return true;
+
+  WideString wsNodeStr = pElement->GetNamespaceURI();
+  if (eMatch == XFA_PacketMatch::kPrefixMatch) {
+    return wsNodeStr.AsStringView()
+        .First(bsNamespaceURIPrefix.GetLength())
+        .EqualsASCII(bsNamespaceURIPrefix);
+  }
+  return wsNodeStr.EqualsASCII(bsNamespaceURIPrefix);
+}
+
+bool GetAttributeLocalName(WideStringView wsAttributeName,
+                           WideString& wsLocalAttrName) {
+  WideString wsAttrName(wsAttributeName);
+  auto pos = wsAttrName.Find(L':', 0);
+  if (!pos.has_value()) {
+    wsLocalAttrName = std::move(wsAttrName);
+    return false;
+  }
+  wsLocalAttrName = wsAttrName.Last(wsAttrName.GetLength() - pos.value() - 1);
+  return true;
+}
+
+bool ResolveAttribute(CFX_XMLElement* pElement,
+                      const WideString& wsAttrName,
+                      WideString& wsLocalAttrName,
+                      WideString& wsNamespaceURI) {
+  WideString wsNSPrefix;
+  if (GetAttributeLocalName(wsAttrName.AsStringView(), wsLocalAttrName)) {
+    wsNSPrefix = wsAttrName.First(wsAttrName.GetLength() -
+                                  wsLocalAttrName.GetLength() - 1);
+  }
+  if (wsLocalAttrName.EqualsASCII("xmlns") || wsNSPrefix.EqualsASCII("xmlns") ||
+      wsNSPrefix.EqualsASCII("xml")) {
+    return false;
+  }
+  if (!XFA_FDEExtension_ResolveNamespaceQualifier(pElement, wsNSPrefix,
+                                                  &wsNamespaceURI)) {
+    wsNamespaceURI.clear();
+    return false;
+  }
+  return true;
+}
+
+absl::optional<WideString> FindAttributeWithNS(
+    CFX_XMLElement* pElement,
+    WideStringView wsLocalAttributeName,
+    WideStringView wsNamespaceURIPrefix) {
+  WideString wsAttrNS;
+  for (auto it : pElement->GetAttributes()) {
+    auto pos = it.first.Find(L':', 0);
+    WideString wsNSPrefix;
+    if (!pos.has_value()) {
+      if (wsLocalAttributeName != it.first)
+        continue;
+    } else {
+      if (wsLocalAttributeName !=
+          it.first.Last(it.first.GetLength() - pos.value() - 1)) {
+        continue;
+      }
+      wsNSPrefix = it.first.First(pos.value());
+    }
+    if (!XFA_FDEExtension_ResolveNamespaceQualifier(pElement, wsNSPrefix,
+                                                    &wsAttrNS) ||
+        wsAttrNS != wsNamespaceURIPrefix) {
+      continue;
+    }
+    return it.second;
+  }
+  return absl::nullopt;
+}
+
+CFX_XMLNode* GetDataSetsFromXDP(CFX_XMLNode* pXMLDocumentNode) {
+  XFA_PACKETINFO datasets_packet =
+      XFA_GetPacketByIndex(XFA_PacketType::Datasets);
+  if (MatchNodeName(pXMLDocumentNode, datasets_packet.name, datasets_packet.uri,
+                    datasets_packet.match)) {
+    return pXMLDocumentNode;
+  }
+  XFA_PACKETINFO xdp_packet = XFA_GetPacketByIndex(XFA_PacketType::Xdp);
+  if (!MatchNodeName(pXMLDocumentNode, xdp_packet.name, xdp_packet.uri,
+                     xdp_packet.match)) {
+    return nullptr;
+  }
+  for (CFX_XMLNode* pDatasetsNode = pXMLDocumentNode->GetFirstChild();
+       pDatasetsNode; pDatasetsNode = pDatasetsNode->GetNextSibling()) {
+    if (MatchNodeName(pDatasetsNode, datasets_packet.name, datasets_packet.uri,
+                      datasets_packet.match)) {
+      return pDatasetsNode;
+    }
+  }
+  return nullptr;
+}
+
+bool IsStringAllWhitespace(WideString wsText) {
+  wsText.TrimRight(L"\x20\x9\xD\xA");
+  return wsText.IsEmpty();
+}
+
+void ConvertXMLToPlainText(CFX_XMLElement* pRootXMLNode, WideString& wsOutput) {
+  for (CFX_XMLNode* pXMLChild = pRootXMLNode->GetFirstChild(); pXMLChild;
+       pXMLChild = pXMLChild->GetNextSibling()) {
+    switch (pXMLChild->GetType()) {
+      case CFX_XMLNode::Type::kElement: {
+        WideString wsTextData = ToXMLElement(pXMLChild)->GetTextData();
+        wsTextData += L"\n";
+        wsOutput += wsTextData;
+        break;
+      }
+      case CFX_XMLNode::Type::kText:
+      case CFX_XMLNode::Type::kCharData: {
+        WideString wsText = ToXMLText(pXMLChild)->GetText();
+        if (IsStringAllWhitespace(wsText))
+          continue;
+        wsOutput = std::move(wsText);
+        break;
+      }
+      default:
+        NOTREACHED_NORETURN();
+    }
+  }
+}
+
+WideString GetPlainTextFromRichText(CFX_XMLNode* pXMLNode) {
+  if (!pXMLNode)
+    return WideString();
+
+  WideString wsPlainText;
+  switch (pXMLNode->GetType()) {
+    case CFX_XMLNode::Type::kElement: {
+      CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLNode);
+      WideString wsTag = pXMLElement->GetLocalTagName();
+      uint32_t uTag = FX_HashCode_GetLoweredW(wsTag.AsStringView());
+      if (uTag == 0x0001f714) {
+        wsPlainText += L"\n";
+      } else if (uTag == 0x00000070) {
+        if (!wsPlainText.IsEmpty()) {
+          wsPlainText += L"\n";
+        }
+      } else if (uTag == 0xa48ac63) {
+        if (!wsPlainText.IsEmpty() && wsPlainText.Back() != '\n') {
+          wsPlainText += L"\n";
+        }
+      }
+      break;
+    }
+    case CFX_XMLNode::Type::kText:
+    case CFX_XMLNode::Type::kCharData: {
+      WideString wsContent = ToXMLText(pXMLNode)->GetText();
+      wsPlainText += wsContent;
+      break;
+    }
+    default:
+      break;
+  }
+  for (CFX_XMLNode* pChildXML = pXMLNode->GetFirstChild(); pChildXML;
+       pChildXML = pChildXML->GetNextSibling()) {
+    wsPlainText += GetPlainTextFromRichText(pChildXML);
+  }
+
+  return wsPlainText;
+}
+
+}  // namespace
+
+bool XFA_RecognizeRichText(CFX_XMLElement* pRichTextXMLNode) {
+  return pRichTextXMLNode && pRichTextXMLNode->GetNamespaceURI().EqualsASCII(
+                                 "http://www.w3.org/1999/xhtml");
+}
+
+CXFA_DocumentBuilder::CXFA_DocumentBuilder(CXFA_Document* pNodeFactory)
+    : node_factory_(pNodeFactory) {}
+
+CXFA_DocumentBuilder::~CXFA_DocumentBuilder() = default;
+
+bool CXFA_DocumentBuilder::BuildDocument(CFX_XMLDocument* pXML,
+                                         XFA_PacketType ePacketID) {
+  DCHECK(pXML);
+
+  CFX_XMLNode* root = Build(pXML);
+  if (!root)
+    return false;
+
+  root_node_ = ParseAsXDPPacket(root, ePacketID);
+  return !!root_node_;
+}
+
+CFX_XMLNode* CXFA_DocumentBuilder::Build(CFX_XMLDocument* pXML) {
+  if (!pXML)
+    return nullptr;
+
+  xml_doc_ = pXML;
+  xml_doc_->GetRoot()->InsertChildNode(
+      xml_doc_->CreateNode<CFX_XMLInstruction>(L"xml"), 0);
+
+  return GetDocumentNode(xml_doc_->GetRoot());
+}
+
+void CXFA_DocumentBuilder::ConstructXFANode(CXFA_Node* pXFANode,
+                                            CFX_XMLNode* pXMLNode) {
+  XFA_PacketType ePacketID = pXFANode->GetPacketType();
+  if (ePacketID == XFA_PacketType::Datasets) {
+    if (pXFANode->GetElementType() == XFA_Element::DataValue) {
+      for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild;
+           pXMLChild = pXMLChild->GetNextSibling()) {
+        CFX_XMLNode::Type eNodeType = pXMLChild->GetType();
+        if (eNodeType == CFX_XMLNode::Type::kInstruction)
+          continue;
+
+        if (eNodeType == CFX_XMLNode::Type::kElement) {
+          CXFA_Node* pXFAChild = node_factory_->CreateNode(
+              XFA_PacketType::Datasets, XFA_Element::DataValue);
+          if (!pXFAChild)
+            return;
+
+          CFX_XMLElement* child = static_cast<CFX_XMLElement*>(pXMLChild);
+          WideString wsNodeStr = child->GetLocalTagName();
+          pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, wsNodeStr);
+          WideString wsChildValue = GetPlainTextFromRichText(child);
+          if (!wsChildValue.IsEmpty())
+            pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsChildValue);
+
+          pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
+          pXFAChild->SetXMLMappingNode(pXMLChild);
+          pXFAChild->SetFlag(XFA_NodeFlag::kInitialized);
+          break;
+        }
+      }
+      root_node_ = pXFANode;
+    } else {
+      root_node_ = DataLoader(pXFANode, pXMLNode);
+    }
+  } else if (pXFANode->IsContentNode()) {
+    ParseContentNode(pXFANode, pXMLNode, ePacketID);
+    root_node_ = pXFANode;
+  } else {
+    root_node_ = NormalLoader(pXFANode, pXMLNode, ePacketID, true);
+  }
+}
+
+CXFA_Node* CXFA_DocumentBuilder::GetRootNode() const {
+  return root_node_;
+}
+
+CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket(CFX_XMLNode* pXMLDocumentNode,
+                                                  XFA_PacketType ePacketID) {
+  switch (ePacketID) {
+    case XFA_PacketType::Xdp:
+      return ParseAsXDPPacket_XDP(pXMLDocumentNode);
+    case XFA_PacketType::Config:
+      return ParseAsXDPPacket_Config(pXMLDocumentNode);
+    case XFA_PacketType::Template:
+      return ParseAsXDPPacket_Template(pXMLDocumentNode);
+    case XFA_PacketType::Form:
+      return ParseAsXDPPacket_Form(pXMLDocumentNode);
+    case XFA_PacketType::Datasets:
+      return ParseAsXDPPacket_Data(pXMLDocumentNode);
+    case XFA_PacketType::Xdc:
+      return ParseAsXDPPacket_Xdc(pXMLDocumentNode);
+    case XFA_PacketType::LocaleSet:
+      return ParseAsXDPPacket_LocaleConnectionSourceSet(
+          pXMLDocumentNode, XFA_PacketType::LocaleSet, XFA_Element::LocaleSet);
+    case XFA_PacketType::ConnectionSet:
+      return ParseAsXDPPacket_LocaleConnectionSourceSet(
+          pXMLDocumentNode, XFA_PacketType::ConnectionSet,
+          XFA_Element::ConnectionSet);
+    case XFA_PacketType::SourceSet:
+      return ParseAsXDPPacket_LocaleConnectionSourceSet(
+          pXMLDocumentNode, XFA_PacketType::SourceSet, XFA_Element::SourceSet);
+    default:
+      return ParseAsXDPPacket_User(pXMLDocumentNode);
+  }
+}
+
+CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket_XDP(
+    CFX_XMLNode* pXMLDocumentNode) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Xdp);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.match))
+    return nullptr;
+
+  CXFA_Node* pXFARootNode =
+      node_factory_->CreateNode(XFA_PacketType::Xdp, XFA_Element::Xfa);
+  if (!pXFARootNode)
+    return nullptr;
+
+  root_node_ = pXFARootNode;
+  pXFARootNode->JSObject()->SetCData(XFA_Attribute::Name, L"xfa");
+
+  for (auto it : ToXMLElement(pXMLDocumentNode)->GetAttributes()) {
+    if (it.first.EqualsASCII("uuid"))
+      pXFARootNode->JSObject()->SetCData(XFA_Attribute::Uuid, it.second);
+    else if (it.first.EqualsASCII("timeStamp"))
+      pXFARootNode->JSObject()->SetCData(XFA_Attribute::TimeStamp, it.second);
+  }
+
+  CFX_XMLNode* pXMLConfigDOMRoot = nullptr;
+  CXFA_Node* pXFAConfigDOMRoot = nullptr;
+  XFA_PACKETINFO config_packet = XFA_GetPacketByIndex(XFA_PacketType::Config);
+  for (CFX_XMLNode* pChildItem = pXMLDocumentNode->GetFirstChild(); pChildItem;
+       pChildItem = pChildItem->GetNextSibling()) {
+    if (!MatchNodeName(pChildItem, config_packet.name, config_packet.uri,
+                       config_packet.match)) {
+      continue;
+    }
+    // TODO(tsepez): make GetFirstChildByName() take a name.
+    uint32_t hash = FX_HashCode_GetAsIfW(config_packet.name);
+    if (pXFARootNode->GetFirstChildByName(hash))
+      return nullptr;
+
+    pXMLConfigDOMRoot = pChildItem;
+    pXFAConfigDOMRoot = ParseAsXDPPacket_Config(pXMLConfigDOMRoot);
+    if (pXFAConfigDOMRoot)
+      pXFARootNode->InsertChildAndNotify(pXFAConfigDOMRoot, nullptr);
+  }
+
+  CFX_XMLNode* pXMLDatasetsDOMRoot = nullptr;
+  CFX_XMLNode* pXMLFormDOMRoot = nullptr;
+  CFX_XMLNode* pXMLTemplateDOMRoot = nullptr;
+  for (CFX_XMLNode* pChildItem = pXMLDocumentNode->GetFirstChild(); pChildItem;
+       pChildItem = pChildItem->GetNextSibling()) {
+    CFX_XMLElement* pElement = ToXMLElement(pChildItem);
+    if (!pElement || pElement == pXMLConfigDOMRoot)
+      continue;
+
+    WideString wsPacketName = pElement->GetLocalTagName();
+    absl::optional<XFA_PACKETINFO> packet_info =
+        XFA_GetPacketByName(wsPacketName.AsStringView());
+    if (packet_info.has_value() && packet_info.value().uri &&
+        !MatchNodeName(pElement, packet_info.value().name,
+                       packet_info.value().uri, packet_info.value().match)) {
+      packet_info = {};
+    }
+    XFA_PacketType ePacket = XFA_PacketType::User;
+    if (packet_info.has_value())
+      ePacket = packet_info.value().packet_type;
+    if (ePacket == XFA_PacketType::Xdp)
+      continue;
+    if (ePacket == XFA_PacketType::Datasets) {
+      if (pXMLDatasetsDOMRoot)
+        return nullptr;
+
+      pXMLDatasetsDOMRoot = pElement;
+    } else if (ePacket == XFA_PacketType::Form) {
+      if (pXMLFormDOMRoot)
+        return nullptr;
+
+      pXMLFormDOMRoot = pElement;
+    } else if (ePacket == XFA_PacketType::Template) {
+      // Found a duplicate template packet.
+      if (pXMLTemplateDOMRoot)
+        return nullptr;
+
+      CXFA_Node* pPacketNode = ParseAsXDPPacket_Template(pElement);
+      if (pPacketNode) {
+        pXMLTemplateDOMRoot = pElement;
+        pXFARootNode->InsertChildAndNotify(pPacketNode, nullptr);
+      }
+    } else {
+      CXFA_Node* pPacketNode = ParseAsXDPPacket(pElement, ePacket);
+      if (pPacketNode) {
+        if (packet_info.has_value() &&
+            (packet_info.value().support == XFA_PacketSupport::kSupportOne) &&
+            pXFARootNode->GetFirstChildByName(
+                FX_HashCode_GetAsIfW(packet_info.value().name))) {
+          return nullptr;
+        }
+        pXFARootNode->InsertChildAndNotify(pPacketNode, nullptr);
+      }
+    }
+  }
+
+  // No template is found.
+  if (!pXMLTemplateDOMRoot)
+    return nullptr;
+
+  if (pXMLDatasetsDOMRoot) {
+    CXFA_Node* pPacketNode =
+        ParseAsXDPPacket(pXMLDatasetsDOMRoot, XFA_PacketType::Datasets);
+    if (pPacketNode)
+      pXFARootNode->InsertChildAndNotify(pPacketNode, nullptr);
+  }
+  if (pXMLFormDOMRoot) {
+    CXFA_Node* pPacketNode =
+        ParseAsXDPPacket(pXMLFormDOMRoot, XFA_PacketType::Form);
+    if (pPacketNode)
+      pXFARootNode->InsertChildAndNotify(pPacketNode, nullptr);
+  }
+
+  pXFARootNode->SetXMLMappingNode(pXMLDocumentNode);
+  return pXFARootNode;
+}
+
+CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket_Config(
+    CFX_XMLNode* pXMLDocumentNode) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Config);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.match))
+    return nullptr;
+
+  CXFA_Node* pNode =
+      node_factory_->CreateNode(XFA_PacketType::Config, XFA_Element::Config);
+  if (!pNode)
+    return nullptr;
+
+  pNode->JSObject()->SetCData(XFA_Attribute::Name,
+                              WideString::FromASCII(packet.name));
+  if (!NormalLoader(pNode, pXMLDocumentNode, XFA_PacketType::Config, true))
+    return nullptr;
+
+  pNode->SetXMLMappingNode(pXMLDocumentNode);
+  return pNode;
+}
+
+CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket_Template(
+    CFX_XMLNode* pXMLDocumentNode) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Template);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.match))
+    return nullptr;
+
+  CXFA_Node* pNode = node_factory_->CreateNode(XFA_PacketType::Template,
+                                               XFA_Element::Template);
+  if (!pNode)
+    return nullptr;
+
+  pNode->JSObject()->SetCData(XFA_Attribute::Name,
+                              WideString::FromASCII(packet.name));
+
+  CFX_XMLElement* pXMLDocumentElement = ToXMLElement(pXMLDocumentNode);
+  WideString wsNamespaceURI = pXMLDocumentElement->GetNamespaceURI();
+  if (wsNamespaceURI.IsEmpty())
+    wsNamespaceURI = pXMLDocumentElement->GetAttribute(L"xmlns:xfa");
+
+  pNode->GetDocument()->RecognizeXFAVersionNumber(wsNamespaceURI);
+
+  if (!NormalLoader(pNode, pXMLDocumentNode, XFA_PacketType::Template, true))
+    return nullptr;
+
+  pNode->SetXMLMappingNode(pXMLDocumentNode);
+  return pNode;
+}
+
+CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket_Form(
+    CFX_XMLNode* pXMLDocumentNode) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Form);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.match))
+    return nullptr;
+
+  CXFA_Node* pNode =
+      node_factory_->CreateNode(XFA_PacketType::Form, XFA_Element::Form);
+  if (!pNode)
+    return nullptr;
+
+  pNode->JSObject()->SetCData(XFA_Attribute::Name,
+                              WideString::FromASCII(packet.name));
+  CXFA_Template* pTemplateRoot =
+      root_node_->GetFirstChildByClass<CXFA_Template>(XFA_Element::Template);
+  CXFA_Subform* pTemplateChosen =
+      pTemplateRoot ? pTemplateRoot->GetFirstChildByClass<CXFA_Subform>(
+                          XFA_Element::Subform)
+                    : nullptr;
+  bool bUseAttribute = true;
+  if (pTemplateChosen &&
+      pTemplateChosen->JSObject()->GetEnum(XFA_Attribute::RestoreState) !=
+          XFA_AttributeValue::Auto) {
+    bUseAttribute = false;
+  }
+  if (!NormalLoader(pNode, pXMLDocumentNode, XFA_PacketType::Form,
+                    bUseAttribute))
+    return nullptr;
+
+  pNode->SetXMLMappingNode(pXMLDocumentNode);
+  return pNode;
+}
+
+CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket_Data(
+    CFX_XMLNode* pXMLDocumentNode) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Datasets);
+  CFX_XMLNode* pDatasetsXMLNode = GetDataSetsFromXDP(pXMLDocumentNode);
+  if (pDatasetsXMLNode) {
+    CXFA_Node* pNode = node_factory_->CreateNode(XFA_PacketType::Datasets,
+                                                 XFA_Element::DataModel);
+    if (!pNode)
+      return nullptr;
+
+    pNode->JSObject()->SetCData(XFA_Attribute::Name,
+                                WideString::FromASCII(packet.name));
+    if (!DataLoader(pNode, pDatasetsXMLNode))
+      return nullptr;
+
+    pNode->SetXMLMappingNode(pDatasetsXMLNode);
+    return pNode;
+  }
+
+  CFX_XMLNode* pDataXMLNode = nullptr;
+  if (MatchNodeName(pXMLDocumentNode, "data", packet.uri, packet.match)) {
+    ToXMLElement(pXMLDocumentNode)->RemoveAttribute(L"xmlns:xfa");
+    pDataXMLNode = pXMLDocumentNode;
+  } else {
+    auto* pDataElement = xml_doc_->CreateNode<CFX_XMLElement>(L"xfa:data");
+    pXMLDocumentNode->RemoveSelfIfParented();
+
+    CFX_XMLElement* pElement = ToXMLElement(pXMLDocumentNode);
+    pElement->RemoveAttribute(L"xmlns:xfa");
+
+    // The node was either removed from the parent above, or already has no
+    // parent so we can take ownership.
+    pDataElement->AppendLastChild(pXMLDocumentNode);
+    pDataXMLNode = pDataElement;
+  }
+  if (!pDataXMLNode)
+    return nullptr;
+
+  CXFA_Node* pNode = node_factory_->CreateNode(XFA_PacketType::Datasets,
+                                               XFA_Element::DataGroup);
+  if (!pNode)
+    return nullptr;
+
+  WideString wsLocalName = ToXMLElement(pDataXMLNode)->GetLocalTagName();
+  pNode->JSObject()->SetCData(XFA_Attribute::Name, wsLocalName);
+  if (!DataLoader(pNode, pDataXMLNode))
+    return nullptr;
+
+  pNode->SetXMLMappingNode(pDataXMLNode);
+  return pNode;
+}
+
+CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket_LocaleConnectionSourceSet(
+    CFX_XMLNode* pXMLDocumentNode,
+    XFA_PacketType packet_type,
+    XFA_Element element) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(packet_type);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.match))
+    return nullptr;
+
+  CXFA_Node* pNode = node_factory_->CreateNode(packet_type, element);
+  if (!pNode)
+    return nullptr;
+
+  pNode->JSObject()->SetCData(XFA_Attribute::Name,
+                              WideString::FromASCII(packet.name));
+  if (!NormalLoader(pNode, pXMLDocumentNode, packet_type, true))
+    return nullptr;
+
+  pNode->SetXMLMappingNode(pXMLDocumentNode);
+  return pNode;
+}
+
+CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket_Xdc(
+    CFX_XMLNode* pXMLDocumentNode) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Xdc);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.match))
+    return nullptr;
+
+  CXFA_Node* pNode =
+      node_factory_->CreateNode(XFA_PacketType::Xdc, XFA_Element::Xdc);
+  if (!pNode)
+    return nullptr;
+
+  pNode->JSObject()->SetCData(XFA_Attribute::Name,
+                              WideString::FromASCII(packet.name));
+  pNode->SetXMLMappingNode(pXMLDocumentNode);
+  return pNode;
+}
+
+CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket_User(
+    CFX_XMLNode* pXMLDocumentNode) {
+  CXFA_Node* pNode =
+      node_factory_->CreateNode(XFA_PacketType::Xdp, XFA_Element::Packet);
+  if (!pNode)
+    return nullptr;
+
+  WideString wsName = ToXMLElement(pXMLDocumentNode)->GetLocalTagName();
+  pNode->JSObject()->SetCData(XFA_Attribute::Name, wsName);
+  pNode->SetXMLMappingNode(pXMLDocumentNode);
+  return pNode;
+}
+
+CXFA_Node* CXFA_DocumentBuilder::DataLoader(CXFA_Node* pXFANode,
+                                            CFX_XMLNode* pXMLDoc) {
+  ParseDataGroup(pXFANode, pXMLDoc, XFA_PacketType::Datasets);
+  return pXFANode;
+}
+
+CXFA_Node* CXFA_DocumentBuilder::NormalLoader(CXFA_Node* pXFANode,
+                                              CFX_XMLNode* pXMLDoc,
+                                              XFA_PacketType ePacketID,
+                                              bool bUseAttribute) {
+  constexpr size_t kMaxExecuteRecursion = 1000;
+  if (execute_recursion_depth_ > kMaxExecuteRecursion)
+    return nullptr;
+  AutoRestorer<size_t> restorer(&execute_recursion_depth_);
+  ++execute_recursion_depth_;
+
+  bool bOneOfPropertyFound = false;
+  for (CFX_XMLNode* pXMLChild = pXMLDoc->GetFirstChild(); pXMLChild;
+       pXMLChild = pXMLChild->GetNextSibling()) {
+    switch (pXMLChild->GetType()) {
+      case CFX_XMLNode::Type::kElement: {
+        CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLChild);
+        WideString wsTagName = pXMLElement->GetLocalTagName();
+        XFA_Element eType = XFA_GetElementByName(wsTagName.AsStringView());
+        if (eType == XFA_Element::Unknown)
+          continue;
+
+        if (pXFANode->HasPropertyFlag(eType, XFA_PropertyFlag::kOneOf) ||
+            pXFANode->HasPropertyFlag(eType, XFA_PropertyFlag::kDefaultOneOf)) {
+          if (bOneOfPropertyFound)
+            break;
+          bOneOfPropertyFound = true;
+        }
+
+        CXFA_Node* pXFAChild = node_factory_->CreateNode(ePacketID, eType);
+        if (!pXFAChild)
+          return nullptr;
+        if (ePacketID == XFA_PacketType::Config) {
+          pXFAChild->JSObject()->SetAttributeByEnum(XFA_Attribute::Name,
+                                                    wsTagName, false);
+        }
+
+        bool IsNeedValue = true;
+        for (auto it : pXMLElement->GetAttributes()) {
+          WideString wsAttrName;
+          GetAttributeLocalName(it.first.AsStringView(), wsAttrName);
+          if (wsAttrName.EqualsASCII("nil") && it.second.EqualsASCII("true"))
+            IsNeedValue = false;
+
+          absl::optional<XFA_ATTRIBUTEINFO> attr =
+              XFA_GetAttributeByName(wsAttrName.AsStringView());
+          if (!attr.has_value())
+            continue;
+
+          if (!bUseAttribute && attr.value().attribute != XFA_Attribute::Name &&
+              attr.value().attribute != XFA_Attribute::Save) {
+            continue;
+          }
+          pXFAChild->JSObject()->SetAttributeByEnum(attr.value().attribute,
+                                                    it.second, false);
+        }
+        pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
+        if (eType == XFA_Element::Validate || eType == XFA_Element::Locale) {
+          if (ePacketID == XFA_PacketType::Config)
+            ParseContentNode(pXFAChild, pXMLElement, ePacketID);
+          else
+            NormalLoader(pXFAChild, pXMLElement, ePacketID, bUseAttribute);
+
+          break;
+        }
+        switch (pXFAChild->GetObjectType()) {
+          case XFA_ObjectType::ContentNode:
+          case XFA_ObjectType::TextNode:
+          case XFA_ObjectType::NodeC:
+          case XFA_ObjectType::NodeV:
+            if (IsNeedValue)
+              ParseContentNode(pXFAChild, pXMLElement, ePacketID);
+            break;
+          default:
+            NormalLoader(pXFAChild, pXMLElement, ePacketID, bUseAttribute);
+            break;
+        }
+      } break;
+      case CFX_XMLNode::Type::kInstruction:
+        ParseInstruction(pXFANode, ToXMLInstruction(pXMLChild), ePacketID);
+        break;
+      default:
+        break;
+    }
+  }
+  return pXFANode;
+}
+
+void CXFA_DocumentBuilder::ParseContentNode(CXFA_Node* pXFANode,
+                                            CFX_XMLNode* pXMLNode,
+                                            XFA_PacketType ePacketID) {
+  XFA_Element element = XFA_Element::Sharptext;
+  if (pXFANode->GetElementType() == XFA_Element::ExData) {
+    WideString wsContentType =
+        pXFANode->JSObject()->GetCData(XFA_Attribute::ContentType);
+    if (wsContentType.EqualsASCII("text/html"))
+      element = XFA_Element::SharpxHTML;
+    else if (wsContentType.EqualsASCII("text/xml"))
+      element = XFA_Element::Sharpxml;
+  }
+  if (element == XFA_Element::SharpxHTML)
+    pXFANode->SetXMLMappingNode(pXMLNode);
+
+  WideString wsValue;
+  for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild;
+       pXMLChild = pXMLChild->GetNextSibling()) {
+    CFX_XMLNode::Type eNodeType = pXMLChild->GetType();
+    if (eNodeType == CFX_XMLNode::Type::kInstruction)
+      continue;
+
+    CFX_XMLElement* pElement = ToXMLElement(pXMLChild);
+    if (element == XFA_Element::SharpxHTML) {
+      if (!pElement)
+        break;
+      if (XFA_RecognizeRichText(pElement))
+        wsValue += GetPlainTextFromRichText(pElement);
+    } else if (element == XFA_Element::Sharpxml) {
+      if (!pElement)
+        break;
+      ConvertXMLToPlainText(pElement, wsValue);
+    } else {
+      if (pElement)
+        break;
+      CFX_XMLText* pText = ToXMLText(pXMLChild);
+      if (pText)
+        wsValue = pText->GetText();
+    }
+    break;
+  }
+  if (!wsValue.IsEmpty()) {
+    if (pXFANode->IsContentNode()) {
+      CXFA_Node* pContentRawDataNode =
+          node_factory_->CreateNode(ePacketID, element);
+      DCHECK(pContentRawDataNode);
+      pContentRawDataNode->JSObject()->SetCData(XFA_Attribute::Value, wsValue);
+      pXFANode->InsertChildAndNotify(pContentRawDataNode, nullptr);
+    } else {
+      pXFANode->JSObject()->SetCData(XFA_Attribute::Value, wsValue);
+    }
+  }
+}
+
+void CXFA_DocumentBuilder::ParseDataGroup(CXFA_Node* pXFANode,
+                                          CFX_XMLNode* pXMLNode,
+                                          XFA_PacketType ePacketID) {
+  for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild;
+       pXMLChild = pXMLChild->GetNextSibling()) {
+    switch (pXMLChild->GetType()) {
+      case CFX_XMLNode::Type::kElement: {
+        CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLChild);
+        WideString wsNamespaceURI = pXMLElement->GetNamespaceURI();
+        if (wsNamespaceURI.EqualsASCII(
+                "http://www.xfa.com/schema/xfa-package/") ||
+            wsNamespaceURI.EqualsASCII(
+                "http://www.xfa.org/schema/xfa-package/") ||
+            wsNamespaceURI.EqualsASCII(
+                "http://www.w3.org/2001/XMLSchema-instance")) {
+          continue;
+        }
+
+        XFA_Element eNodeType = XFA_Element::DataModel;
+        if (eNodeType == XFA_Element::DataModel) {
+          absl::optional<WideString> wsDataNodeAttr =
+              FindAttributeWithNS(pXMLElement, L"dataNode",
+                                  L"http://www.xfa.org/schema/xfa-data/1.0/");
+          if (wsDataNodeAttr.has_value()) {
+            if (wsDataNodeAttr.value().EqualsASCII("dataGroup"))
+              eNodeType = XFA_Element::DataGroup;
+            else if (wsDataNodeAttr.value().EqualsASCII("dataValue"))
+              eNodeType = XFA_Element::DataValue;
+          }
+        }
+        if (eNodeType == XFA_Element::DataModel) {
+          absl::optional<WideString> wsContentType =
+              FindAttributeWithNS(pXMLElement, L"contentType",
+                                  L"http://www.xfa.org/schema/xfa-data/1.0/");
+          if (wsContentType.has_value() && !wsContentType.value().IsEmpty())
+            eNodeType = XFA_Element::DataValue;
+        }
+        if (eNodeType == XFA_Element::DataModel) {
+          for (CFX_XMLNode* pXMLDataChild = pXMLElement->GetFirstChild();
+               pXMLDataChild; pXMLDataChild = pXMLDataChild->GetNextSibling()) {
+            CFX_XMLElement* pElement = ToXMLElement(pXMLDataChild);
+            if (pElement && !XFA_RecognizeRichText(pElement)) {
+              eNodeType = XFA_Element::DataGroup;
+              break;
+            }
+          }
+        }
+        if (eNodeType == XFA_Element::DataModel)
+          eNodeType = XFA_Element::DataValue;
+
+        CXFA_Node* pXFAChild =
+            node_factory_->CreateNode(XFA_PacketType::Datasets, eNodeType);
+        if (!pXFAChild)
+          return;
+
+        pXFAChild->JSObject()->SetCData(XFA_Attribute::Name,
+                                        pXMLElement->GetLocalTagName());
+        bool bNeedValue = true;
+
+        for (auto it : pXMLElement->GetAttributes()) {
+          WideString wsName;
+          WideString wsNS;
+          if (!ResolveAttribute(pXMLElement, it.first, wsName, wsNS)) {
+            continue;
+          }
+          if (wsName.EqualsASCII("nil") && it.second.EqualsASCII("true")) {
+            bNeedValue = false;
+            continue;
+          }
+          if (wsNS.EqualsASCII("http://www.xfa.com/schema/xfa-package/") ||
+              wsNS.EqualsASCII("http://www.xfa.org/schema/xfa-package/") ||
+              wsNS.EqualsASCII("http://www.w3.org/2001/XMLSchema-instance") ||
+              wsNS.EqualsASCII("http://www.xfa.org/schema/xfa-data/1.0/")) {
+            continue;
+          }
+          CXFA_Node* pXFAMetaData = node_factory_->CreateNode(
+              XFA_PacketType::Datasets, XFA_Element::DataValue);
+          if (!pXFAMetaData)
+            return;
+
+          pXFAMetaData->JSObject()->SetCData(XFA_Attribute::Name, wsName);
+          pXFAMetaData->JSObject()->SetCData(XFA_Attribute::QualifiedName,
+                                             it.first);
+          pXFAMetaData->JSObject()->SetCData(XFA_Attribute::Value, it.second);
+          pXFAMetaData->JSObject()->SetEnum(
+              XFA_Attribute::Contains, XFA_AttributeValue::MetaData, false);
+          pXFAChild->InsertChildAndNotify(pXFAMetaData, nullptr);
+          pXFAMetaData->SetXMLMappingNode(pXMLElement);
+          pXFAMetaData->SetFlag(XFA_NodeFlag::kInitialized);
+        }
+
+        if (!bNeedValue)
+          pXMLElement->RemoveAttribute(L"xsi:nil");
+
+        pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
+        if (eNodeType == XFA_Element::DataGroup)
+          ParseDataGroup(pXFAChild, pXMLElement, ePacketID);
+        else if (bNeedValue)
+          ParseDataValue(pXFAChild, pXMLChild, XFA_PacketType::Datasets);
+
+        pXFAChild->SetXMLMappingNode(pXMLElement);
+        pXFAChild->SetFlag(XFA_NodeFlag::kInitialized);
+        continue;
+      }
+      case CFX_XMLNode::Type::kCharData:
+      case CFX_XMLNode::Type::kText: {
+        CFX_XMLText* pXMLText = ToXMLText(pXMLChild);
+        WideString wsText = pXMLText->GetText();
+        if (IsStringAllWhitespace(wsText))
+          continue;
+
+        CXFA_Node* pXFAChild = node_factory_->CreateNode(
+            XFA_PacketType::Datasets, XFA_Element::DataValue);
+        if (!pXFAChild)
+          return;
+
+        pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsText);
+        pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
+        pXFAChild->SetXMLMappingNode(pXMLText);
+        pXFAChild->SetFlag(XFA_NodeFlag::kInitialized);
+        continue;
+      }
+      default:
+        continue;
+    }
+  }
+}
+
+void CXFA_DocumentBuilder::ParseDataValue(CXFA_Node* pXFANode,
+                                          CFX_XMLNode* pXMLNode,
+                                          XFA_PacketType ePacketID) {
+  WideString wsValue;
+  WideString wsCurValue;
+  bool bMarkAsCompound = false;
+  CFX_XMLNode* pXMLCurValueNode = nullptr;
+  for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild;
+       pXMLChild = pXMLChild->GetNextSibling()) {
+    CFX_XMLNode::Type eNodeType = pXMLChild->GetType();
+    if (eNodeType == CFX_XMLNode::Type::kInstruction)
+      continue;
+
+    CFX_XMLText* pText = ToXMLText(pXMLChild);
+    if (pText) {
+      WideString wsText = pText->GetText();
+      if (!pXMLCurValueNode)
+        pXMLCurValueNode = pXMLChild;
+      wsCurValue += wsText;
+      continue;
+    }
+    if (XFA_RecognizeRichText(ToXMLElement(pXMLChild))) {
+      WideString wsText = GetPlainTextFromRichText(ToXMLElement(pXMLChild));
+      if (!pXMLCurValueNode)
+        pXMLCurValueNode = pXMLChild;
+      wsCurValue += wsText;
+      continue;
+    }
+    bMarkAsCompound = true;
+    if (pXMLCurValueNode) {
+      if (!wsCurValue.IsEmpty()) {
+        CXFA_Node* pXFAChild =
+            node_factory_->CreateNode(ePacketID, XFA_Element::DataValue);
+        if (!pXFAChild)
+          return;
+
+        pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, WideString());
+        pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsCurValue);
+        pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
+        pXFAChild->SetXMLMappingNode(pXMLCurValueNode);
+        pXFAChild->SetFlag(XFA_NodeFlag::kInitialized);
+        wsValue += wsCurValue;
+        wsCurValue.clear();
+      }
+      pXMLCurValueNode = nullptr;
+    }
+    CXFA_Node* pXFAChild =
+        node_factory_->CreateNode(ePacketID, XFA_Element::DataValue);
+    if (!pXFAChild)
+      return;
+
+    WideString wsNodeStr = ToXMLElement(pXMLChild)->GetLocalTagName();
+    pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, wsNodeStr);
+    ParseDataValue(pXFAChild, pXMLChild, ePacketID);
+    pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
+    pXFAChild->SetXMLMappingNode(pXMLChild);
+    pXFAChild->SetFlag(XFA_NodeFlag::kInitialized);
+    wsValue += pXFAChild->JSObject()->GetCData(XFA_Attribute::Value);
+  }
+
+  if (pXMLCurValueNode) {
+    if (!wsCurValue.IsEmpty()) {
+      if (bMarkAsCompound) {
+        CXFA_Node* pXFAChild =
+            node_factory_->CreateNode(ePacketID, XFA_Element::DataValue);
+        if (!pXFAChild)
+          return;
+
+        pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, WideString());
+        pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsCurValue);
+        pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
+        pXFAChild->SetXMLMappingNode(pXMLCurValueNode);
+        pXFAChild->SetFlag(XFA_NodeFlag::kInitialized);
+      }
+      wsValue += wsCurValue;
+      wsCurValue.clear();
+    }
+    pXMLCurValueNode = nullptr;
+  }
+  pXFANode->JSObject()->SetCData(XFA_Attribute::Value, wsValue);
+}
+
+void CXFA_DocumentBuilder::ParseInstruction(CXFA_Node* pXFANode,
+                                            CFX_XMLInstruction* pXMLInstruction,
+                                            XFA_PacketType ePacketID) {
+  const std::vector<WideString>& target_data = pXMLInstruction->GetTargetData();
+  if (pXMLInstruction->IsOriginalXFAVersion()) {
+    if (target_data.size() > 1 &&
+        (pXFANode->GetDocument()->RecognizeXFAVersionNumber(target_data[0]) !=
+         XFA_VERSION_UNKNOWN) &&
+        target_data[1].EqualsASCII("v2.7-scripting:1")) {
+      pXFANode->GetDocument()->set_is_scripting();
+    }
+    return;
+  }
+  if (pXMLInstruction->IsAcrobat()) {
+    if (target_data.size() > 1 && target_data[0].EqualsASCII("JavaScript") &&
+        target_data[1].EqualsASCII("strictScoping")) {
+      pXFANode->GetDocument()->set_is_strict_scoping();
+    }
+  }
+}
diff --git a/xfa/fxfa/parser/cxfa_document_builder.h b/xfa/fxfa/parser/cxfa_document_builder.h
new file mode 100644
index 0000000..5453af6
--- /dev/null
+++ b/xfa/fxfa/parser/cxfa_document_builder.h
@@ -0,0 +1,70 @@
+// Copyright 2014 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FXFA_PARSER_CXFA_DOCUMENT_BUILDER_H_
+#define XFA_FXFA_PARSER_CXFA_DOCUMENT_BUILDER_H_
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "v8/include/cppgc/macros.h"
+#include "xfa/fxfa/fxfa_basic.h"
+
+class CFX_XMLDocument;
+class CFX_XMLNode;
+class CXFA_Document;
+class CXFA_Node;
+class CFX_XMLInstruction;
+
+class CXFA_DocumentBuilder {
+  CPPGC_STACK_ALLOCATED();  // Allow Raw/Unowned pointers.
+
+ public:
+  explicit CXFA_DocumentBuilder(CXFA_Document* pNodeFactory);
+  ~CXFA_DocumentBuilder();
+
+  CFX_XMLNode* Build(CFX_XMLDocument* pXML);
+  bool BuildDocument(CFX_XMLDocument* pXML, XFA_PacketType ePacketID);
+  void ConstructXFANode(CXFA_Node* pXFANode, CFX_XMLNode* pXMLNode);
+  CXFA_Node* GetRootNode() const;
+
+ private:
+  CXFA_Node* ParseAsXDPPacket(CFX_XMLNode* pXMLDocumentNode,
+                              XFA_PacketType ePacketID);
+  CXFA_Node* ParseAsXDPPacket_XDP(CFX_XMLNode* pXMLDocumentNode);
+  CXFA_Node* ParseAsXDPPacket_Config(CFX_XMLNode* pXMLDocumentNode);
+  CXFA_Node* ParseAsXDPPacket_Template(CFX_XMLNode* pXMLDocumentNode);
+  CXFA_Node* ParseAsXDPPacket_Form(CFX_XMLNode* pXMLDocumentNode);
+  CXFA_Node* ParseAsXDPPacket_Data(CFX_XMLNode* pXMLDocumentNode);
+  CXFA_Node* ParseAsXDPPacket_LocaleConnectionSourceSet(
+      CFX_XMLNode* pXMLDocumentNode,
+      XFA_PacketType packet_type,
+      XFA_Element element);
+  CXFA_Node* ParseAsXDPPacket_Xdc(CFX_XMLNode* pXMLDocumentNode);
+  CXFA_Node* ParseAsXDPPacket_User(CFX_XMLNode* pXMLDocumentNode);
+  CXFA_Node* DataLoader(CXFA_Node* pXFANode, CFX_XMLNode* pXMLDoc);
+  CXFA_Node* NormalLoader(CXFA_Node* pXFANode,
+                          CFX_XMLNode* pXMLDoc,
+                          XFA_PacketType ePacketID,
+                          bool bUseAttribute);
+  void ParseContentNode(CXFA_Node* pXFANode,
+                        CFX_XMLNode* pXMLNode,
+                        XFA_PacketType ePacketID);
+  void ParseDataValue(CXFA_Node* pXFANode,
+                      CFX_XMLNode* pXMLNode,
+                      XFA_PacketType ePacketID);
+  void ParseDataGroup(CXFA_Node* pXFANode,
+                      CFX_XMLNode* pXMLNode,
+                      XFA_PacketType ePacketID);
+  void ParseInstruction(CXFA_Node* pXFANode,
+                        CFX_XMLInstruction* pXMLInstruction,
+                        XFA_PacketType ePacketID);
+
+  UnownedPtr<CXFA_Document> node_factory_;  // OK, stack-only.
+  UnownedPtr<CXFA_Node> root_node_;         // OK, stack-only.
+  UnownedPtr<CFX_XMLDocument> xml_doc_;
+  size_t execute_recursion_depth_ = 0;
+};
+
+#endif  // XFA_FXFA_PARSER_CXFA_DOCUMENT_BUILDER_H_
diff --git a/xfa/fxfa/parser/cxfa_document_builder_embeddertest.cpp b/xfa/fxfa/parser/cxfa_document_builder_embeddertest.cpp
new file mode 100644
index 0000000..da023b2
--- /dev/null
+++ b/xfa/fxfa/parser/cxfa_document_builder_embeddertest.cpp
@@ -0,0 +1,22 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class CXFASimpleParserEmbedderTest : public EmbedderTest {};
+
+TEST_F(CXFASimpleParserEmbedderTest, Bug_216) {
+  ASSERT_TRUE(OpenDocument("bug_216.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+  UnloadPage(page);
+}
+
+TEST_F(CXFASimpleParserEmbedderTest, Bug_709793) {
+  ASSERT_TRUE(OpenDocument("bug_709793.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+  UnloadPage(page);
+}
diff --git a/xfa/fxfa/parser/cxfa_document_builder_unittest.cpp b/xfa/fxfa/parser/cxfa_document_builder_unittest.cpp
new file mode 100644
index 0000000..0ebdb5b
--- /dev/null
+++ b/xfa/fxfa/parser/cxfa_document_builder_unittest.cpp
@@ -0,0 +1,146 @@
+// Copyright 2018 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fxfa/parser/cxfa_document_builder.h"
+
+#include <memory>
+
+#include "core/fxcrt/cfx_read_only_span_stream.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "core/fxcrt/xml/cfx_xmlparser.h"
+#include "testing/fxgc_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "v8/include/cppgc/allocation.h"
+#include "v8/include/cppgc/persistent.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
+
+class CXFA_DocumentBuilderTest : public FXGCUnitTest {
+ public:
+  void SetUp() override {
+    FXGCUnitTest::SetUp();
+    doc_ = cppgc::MakeGarbageCollected<CXFA_Document>(
+        heap()->GetAllocationHandle(), nullptr, heap(), nullptr);
+  }
+
+  void TearDown() override {
+    doc_ = nullptr;
+    FXGCUnitTest::TearDown();
+  }
+
+  CXFA_Document* GetDoc() const { return doc_; }
+
+  CXFA_Node* ParseAndBuild(const RetainPtr<CFX_ReadOnlySpanStream>& stream) {
+    xml_ = CFX_XMLParser(stream).Parse();
+    if (!xml_)
+      return nullptr;
+
+    CXFA_DocumentBuilder builder(doc_);
+    if (!builder.BuildDocument(xml_.get(), XFA_PacketType::Config))
+      return nullptr;
+    return builder.GetRootNode();
+  }
+
+ private:
+  std::unique_ptr<CFX_XMLDocument> xml_;
+  cppgc::Persistent<CXFA_Document> doc_;
+};
+
+TEST_F(CXFA_DocumentBuilderTest, EmptyInput) {
+  static const char kInput[] = "";
+  auto stream = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
+      pdfium::as_bytes(pdfium::make_span(kInput)));
+  EXPECT_FALSE(ParseAndBuild(stream));
+}
+
+TEST_F(CXFA_DocumentBuilderTest, BadInput) {
+  static const char kInput[] = "<<<>bar?>>>>>>>";
+  auto stream = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
+      pdfium::as_bytes(pdfium::make_span(kInput)));
+  EXPECT_FALSE(ParseAndBuild(stream));
+}
+
+TEST_F(CXFA_DocumentBuilderTest, XMLInstructionsScriptOff) {
+  static const char kInput[] =
+      "<config>\n"
+      "<?originalXFAVersion http://www.xfa.org/schema/xfa-template/2.7 "
+      "v2.7-scripting:0 ?>\n"
+      "</config>";
+  EXPECT_FALSE(GetDoc()->is_scripting());
+
+  auto stream = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
+      pdfium::as_bytes(pdfium::make_span(kInput)));
+
+  CXFA_Node* root = ParseAndBuild(stream);
+  ASSERT_TRUE(root);
+  EXPECT_FALSE(GetDoc()->is_scripting());
+}
+
+TEST_F(CXFA_DocumentBuilderTest, XMLInstructionsScriptOn) {
+  static const char kInput[] =
+      "<config>\n"
+      "<?originalXFAVersion http://www.xfa.org/schema/xfa-template/2.7 "
+      "v2.7-scripting:1 ?>\n"
+      "</config>";
+
+  EXPECT_FALSE(GetDoc()->is_scripting());
+
+  auto stream = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
+      pdfium::as_bytes(pdfium::make_span(kInput)));
+
+  CXFA_Node* root = ParseAndBuild(stream);
+  ASSERT_TRUE(root);
+  EXPECT_TRUE(GetDoc()->is_scripting());
+}
+
+TEST_F(CXFA_DocumentBuilderTest, XMLInstructionsStrictScope) {
+  static const char kInput[] =
+      "<config>"
+      "<?acrobat JavaScript strictScoping ?>\n"
+      "</config>";
+
+  EXPECT_FALSE(GetDoc()->is_strict_scoping());
+
+  auto stream = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
+      pdfium::as_bytes(pdfium::make_span(kInput)));
+
+  CXFA_Node* root = ParseAndBuild(stream);
+  ASSERT_TRUE(root);
+  EXPECT_TRUE(GetDoc()->is_strict_scoping());
+}
+
+TEST_F(CXFA_DocumentBuilderTest, XMLInstructionsStrictScopeBad) {
+  static const char kInput[] =
+      "<config>"
+      "<?acrobat JavaScript otherScoping ?>\n"
+      "</config>";
+
+  EXPECT_FALSE(GetDoc()->is_strict_scoping());
+
+  auto stream = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
+      pdfium::as_bytes(pdfium::make_span(kInput)));
+
+  CXFA_Node* root = ParseAndBuild(stream);
+  ASSERT_TRUE(root);
+  EXPECT_FALSE(GetDoc()->is_strict_scoping());
+}
+
+TEST_F(CXFA_DocumentBuilderTest, MultipleXMLInstructions) {
+  static const char kInput[] =
+      "<config>"
+      "<?originalXFAVersion http://www.xfa.org/schema/xfa-template/2.7 "
+      "v2.7-scripting:1 ?>\n"
+      "<?acrobat JavaScript strictScoping ?>\n"
+      "</config>";
+
+  EXPECT_FALSE(GetDoc()->is_scripting());
+  EXPECT_FALSE(GetDoc()->is_strict_scoping());
+
+  auto stream = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
+      pdfium::as_bytes(pdfium::make_span(kInput)));
+
+  CXFA_Node* root = ParseAndBuild(stream);
+  ASSERT_TRUE(root);
+  EXPECT_TRUE(GetDoc()->is_scripting());
+  EXPECT_TRUE(GetDoc()->is_strict_scoping());
+}
diff --git a/xfa/fxfa/parser/cxfa_document_parser.cpp b/xfa/fxfa/parser/cxfa_document_parser.cpp
deleted file mode 100644
index e210b94..0000000
--- a/xfa/fxfa/parser/cxfa_document_parser.cpp
+++ /dev/null
@@ -1,1040 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/parser/cxfa_document_parser.h"
-
-#include <utility>
-#include <vector>
-
-#include "core/fxcrt/autorestorer.h"
-#include "core/fxcrt/cfx_readonlymemorystream.h"
-#include "core/fxcrt/cfx_widetextbuf.h"
-#include "core/fxcrt/fx_codepage.h"
-#include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/xml/cfx_xmlchardata.h"
-#include "core/fxcrt/xml/cfx_xmldocument.h"
-#include "core/fxcrt/xml/cfx_xmlelement.h"
-#include "core/fxcrt/xml/cfx_xmlinstruction.h"
-#include "core/fxcrt/xml/cfx_xmlnode.h"
-#include "core/fxcrt/xml/cfx_xmlparser.h"
-#include "core/fxcrt/xml/cfx_xmltext.h"
-#include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/optional.h"
-#include "xfa/fxfa/fxfa.h"
-#include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_subform.h"
-#include "xfa/fxfa/parser/cxfa_template.h"
-#include "xfa/fxfa/parser/xfa_basic_data.h"
-#include "xfa/fxfa/parser/xfa_utils.h"
-
-namespace {
-
-CFX_XMLNode* GetDocumentNode(CFX_XMLNode* pRootNode) {
-  for (CFX_XMLNode* pXMLNode = pRootNode->GetFirstChild(); pXMLNode;
-       pXMLNode = pXMLNode->GetNextSibling()) {
-    if (pXMLNode->GetType() == CFX_XMLNode::Type::kElement)
-      return pXMLNode;
-  }
-  return nullptr;
-}
-
-bool MatchNodeName(CFX_XMLNode* pNode,
-                   WideStringView wsLocalTagName,
-                   WideStringView wsNamespaceURIPrefix,
-                   uint32_t eMatchFlags = XFA_XDPPACKET_FLAGS_NOMATCH) {
-  CFX_XMLElement* pElement = ToXMLElement(pNode);
-  if (!pElement)
-    return false;
-
-  WideString wsNodeStr = pElement->GetLocalTagName();
-  if (wsNodeStr != wsLocalTagName)
-    return false;
-
-  wsNodeStr = pElement->GetNamespaceURI();
-  if (eMatchFlags & XFA_XDPPACKET_FLAGS_NOMATCH)
-    return true;
-  if (eMatchFlags & XFA_XDPPACKET_FLAGS_PREFIXMATCH) {
-    return wsNodeStr.First(wsNamespaceURIPrefix.GetLength()) ==
-           wsNamespaceURIPrefix;
-  }
-
-  return wsNodeStr == wsNamespaceURIPrefix;
-}
-
-bool GetAttributeLocalName(WideStringView wsAttributeName,
-                           WideString& wsLocalAttrName) {
-  WideString wsAttrName(wsAttributeName);
-  auto pos = wsAttrName.Find(L':', 0);
-  if (!pos.has_value()) {
-    wsLocalAttrName = std::move(wsAttrName);
-    return false;
-  }
-  wsLocalAttrName = wsAttrName.Last(wsAttrName.GetLength() - pos.value() - 1);
-  return true;
-}
-
-bool ResolveAttribute(CFX_XMLElement* pElement,
-                      const WideString& wsAttrName,
-                      WideString& wsLocalAttrName,
-                      WideString& wsNamespaceURI) {
-  WideString wsNSPrefix;
-  if (GetAttributeLocalName(wsAttrName.AsStringView(), wsLocalAttrName)) {
-    wsNSPrefix = wsAttrName.First(wsAttrName.GetLength() -
-                                  wsLocalAttrName.GetLength() - 1);
-  }
-  if (wsLocalAttrName.EqualsASCII("xmlns") || wsNSPrefix.EqualsASCII("xmlns") ||
-      wsNSPrefix.EqualsASCII("xml")) {
-    return false;
-  }
-  if (!XFA_FDEExtension_ResolveNamespaceQualifier(pElement, wsNSPrefix,
-                                                  &wsNamespaceURI)) {
-    wsNamespaceURI.clear();
-    return false;
-  }
-  return true;
-}
-
-Optional<WideString> FindAttributeWithNS(CFX_XMLElement* pElement,
-                                         WideStringView wsLocalAttributeName,
-                                         WideStringView wsNamespaceURIPrefix) {
-  WideString wsAttrNS;
-  for (auto it : pElement->GetAttributes()) {
-    auto pos = it.first.Find(L':', 0);
-    WideString wsNSPrefix;
-    if (!pos.has_value()) {
-      if (wsLocalAttributeName != it.first)
-        continue;
-    } else {
-      if (wsLocalAttributeName !=
-          it.first.Last(it.first.GetLength() - pos.value() - 1)) {
-        continue;
-      }
-      wsNSPrefix = it.first.First(pos.value());
-    }
-    if (!XFA_FDEExtension_ResolveNamespaceQualifier(pElement, wsNSPrefix,
-                                                    &wsAttrNS) ||
-        wsAttrNS != wsNamespaceURIPrefix) {
-      continue;
-    }
-    return it.second;
-  }
-  return {};
-}
-
-CFX_XMLNode* GetDataSetsFromXDP(CFX_XMLNode* pXMLDocumentNode) {
-  XFA_PACKETINFO datasets_packet =
-      XFA_GetPacketByIndex(XFA_PacketType::Datasets);
-  if (MatchNodeName(pXMLDocumentNode, datasets_packet.name, datasets_packet.uri,
-                    datasets_packet.flags)) {
-    return pXMLDocumentNode;
-  }
-  XFA_PACKETINFO xdp_packet = XFA_GetPacketByIndex(XFA_PacketType::Xdp);
-  if (!MatchNodeName(pXMLDocumentNode, xdp_packet.name, xdp_packet.uri,
-                     xdp_packet.flags)) {
-    return nullptr;
-  }
-  for (CFX_XMLNode* pDatasetsNode = pXMLDocumentNode->GetFirstChild();
-       pDatasetsNode; pDatasetsNode = pDatasetsNode->GetNextSibling()) {
-    if (MatchNodeName(pDatasetsNode, datasets_packet.name, datasets_packet.uri,
-                      datasets_packet.flags)) {
-      return pDatasetsNode;
-    }
-  }
-  return nullptr;
-}
-
-bool IsStringAllWhitespace(WideString wsText) {
-  wsText.TrimRight(L"\x20\x9\xD\xA");
-  return wsText.IsEmpty();
-}
-
-void ConvertXMLToPlainText(CFX_XMLElement* pRootXMLNode, WideString& wsOutput) {
-  for (CFX_XMLNode* pXMLChild = pRootXMLNode->GetFirstChild(); pXMLChild;
-       pXMLChild = pXMLChild->GetNextSibling()) {
-    switch (pXMLChild->GetType()) {
-      case CFX_XMLNode::Type::kElement: {
-        WideString wsTextData = ToXMLElement(pXMLChild)->GetTextData();
-        wsTextData += L"\n";
-        wsOutput += wsTextData;
-        break;
-      }
-      case CFX_XMLNode::Type::kText:
-      case CFX_XMLNode::Type::kCharData: {
-        WideString wsText = ToXMLText(pXMLChild)->GetText();
-        if (IsStringAllWhitespace(wsText))
-          continue;
-        wsOutput = std::move(wsText);
-        break;
-      }
-      default:
-        NOTREACHED();
-        break;
-    }
-  }
-}
-
-WideString GetPlainTextFromRichText(CFX_XMLNode* pXMLNode) {
-  if (!pXMLNode)
-    return WideString();
-
-  WideString wsPlainText;
-  switch (pXMLNode->GetType()) {
-    case CFX_XMLNode::Type::kElement: {
-      CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLNode);
-      WideString wsTag = pXMLElement->GetLocalTagName();
-      uint32_t uTag = FX_HashCode_GetW(wsTag.AsStringView(), true);
-      if (uTag == 0x0001f714) {
-        wsPlainText += L"\n";
-      } else if (uTag == 0x00000070) {
-        if (!wsPlainText.IsEmpty()) {
-          wsPlainText += L"\n";
-        }
-      } else if (uTag == 0xa48ac63) {
-        if (!wsPlainText.IsEmpty() && wsPlainText.Back() != '\n') {
-          wsPlainText += L"\n";
-        }
-      }
-      break;
-    }
-    case CFX_XMLNode::Type::kText:
-    case CFX_XMLNode::Type::kCharData: {
-      WideString wsContent = ToXMLText(pXMLNode)->GetText();
-      wsPlainText += wsContent;
-      break;
-    }
-    default:
-      break;
-  }
-  for (CFX_XMLNode* pChildXML = pXMLNode->GetFirstChild(); pChildXML;
-       pChildXML = pChildXML->GetNextSibling()) {
-    wsPlainText += GetPlainTextFromRichText(pChildXML);
-  }
-
-  return wsPlainText;
-}
-
-}  // namespace
-
-bool XFA_RecognizeRichText(CFX_XMLElement* pRichTextXMLNode) {
-  return pRichTextXMLNode && pRichTextXMLNode->GetNamespaceURI().EqualsASCII(
-                                 "http://www.w3.org/1999/xhtml");
-}
-
-CXFA_DocumentParser::CXFA_DocumentParser(CXFA_Document* pFactory)
-    : m_pFactory(pFactory) {}
-
-CXFA_DocumentParser::~CXFA_DocumentParser() = default;
-
-bool CXFA_DocumentParser::Parse(
-    const RetainPtr<IFX_SeekableReadStream>& pStream,
-    XFA_PacketType ePacketID) {
-  xml_doc_ = LoadXML(pStream);
-  if (!xml_doc_)
-    return false;
-
-  CFX_XMLNode* root = GetDocumentNode(xml_doc_->GetRoot());
-  if (!root)
-    return false;
-
-  m_pRootNode = ParseAsXDPPacket(root, ePacketID);
-  return !!m_pRootNode;
-}
-
-CFX_XMLNode* CXFA_DocumentParser::ParseXMLData(const ByteString& wsXML) {
-  auto pStream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(wsXML.raw_span());
-  xml_doc_ = LoadXML(pStream);
-  if (!xml_doc_)
-    return nullptr;
-  return GetDocumentNode(xml_doc_->GetRoot());
-}
-
-std::unique_ptr<CFX_XMLDocument> CXFA_DocumentParser::LoadXML(
-    const RetainPtr<IFX_SeekableReadStream>& pStream) {
-  ASSERT(pStream);
-
-  CFX_XMLParser parser(pStream);
-  std::unique_ptr<CFX_XMLDocument> doc = parser.Parse();
-  if (doc) {
-    doc->GetRoot()->InsertChildNode(doc->CreateNode<CFX_XMLInstruction>(L"xml"),
-                                    0);
-  }
-  return doc;
-}
-
-void CXFA_DocumentParser::ConstructXFANode(CXFA_Node* pXFANode,
-                                           CFX_XMLNode* pXMLNode) {
-  XFA_PacketType ePacketID = pXFANode->GetPacketType();
-  if (ePacketID == XFA_PacketType::Datasets) {
-    if (pXFANode->GetElementType() == XFA_Element::DataValue) {
-      for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild;
-           pXMLChild = pXMLChild->GetNextSibling()) {
-        CFX_XMLNode::Type eNodeType = pXMLChild->GetType();
-        if (eNodeType == CFX_XMLNode::Type::kInstruction)
-          continue;
-
-        if (eNodeType == CFX_XMLNode::Type::kElement) {
-          CXFA_Node* pXFAChild = m_pFactory->CreateNode(
-              XFA_PacketType::Datasets, XFA_Element::DataValue);
-          if (!pXFAChild)
-            return;
-
-          CFX_XMLElement* child = static_cast<CFX_XMLElement*>(pXMLChild);
-          WideString wsNodeStr = child->GetLocalTagName();
-          pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, wsNodeStr, false,
-                                          false);
-          WideString wsChildValue = GetPlainTextFromRichText(child);
-          if (!wsChildValue.IsEmpty())
-            pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsChildValue,
-                                            false, false);
-
-          pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
-          pXFAChild->SetXMLMappingNode(pXMLChild);
-          pXFAChild->SetFlag(XFA_NodeFlag_Initialized);
-          break;
-        }
-      }
-      m_pRootNode = pXFANode;
-    } else {
-      m_pRootNode = DataLoader(pXFANode, pXMLNode, true);
-    }
-  } else if (pXFANode->IsContentNode()) {
-    ParseContentNode(pXFANode, pXMLNode, ePacketID);
-    m_pRootNode = pXFANode;
-  } else {
-    m_pRootNode = NormalLoader(pXFANode, pXMLNode, ePacketID, true);
-  }
-}
-
-CXFA_Node* CXFA_DocumentParser::GetRootNode() const {
-  return m_pRootNode;
-}
-
-CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket(CFX_XMLNode* pXMLDocumentNode,
-                                                 XFA_PacketType ePacketID) {
-  switch (ePacketID) {
-    case XFA_PacketType::Xdp:
-      return ParseAsXDPPacket_XDP(pXMLDocumentNode);
-    case XFA_PacketType::Config:
-      return ParseAsXDPPacket_Config(pXMLDocumentNode);
-    case XFA_PacketType::Template:
-      return ParseAsXDPPacket_Template(pXMLDocumentNode);
-    case XFA_PacketType::Form:
-      return ParseAsXDPPacket_Form(pXMLDocumentNode);
-    case XFA_PacketType::Datasets:
-      return ParseAsXDPPacket_Data(pXMLDocumentNode);
-    case XFA_PacketType::Xdc:
-      return ParseAsXDPPacket_Xdc(pXMLDocumentNode);
-    case XFA_PacketType::LocaleSet:
-      return ParseAsXDPPacket_LocaleConnectionSourceSet(
-          pXMLDocumentNode, XFA_PacketType::LocaleSet, XFA_Element::LocaleSet);
-    case XFA_PacketType::ConnectionSet:
-      return ParseAsXDPPacket_LocaleConnectionSourceSet(
-          pXMLDocumentNode, XFA_PacketType::ConnectionSet,
-          XFA_Element::ConnectionSet);
-    case XFA_PacketType::SourceSet:
-      return ParseAsXDPPacket_LocaleConnectionSourceSet(
-          pXMLDocumentNode, XFA_PacketType::SourceSet, XFA_Element::SourceSet);
-    default:
-      return ParseAsXDPPacket_User(pXMLDocumentNode);
-  }
-}
-
-CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_XDP(
-    CFX_XMLNode* pXMLDocumentNode) {
-  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Xdp);
-  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
-    return nullptr;
-
-  CXFA_Node* pXFARootNode =
-      m_pFactory->CreateNode(XFA_PacketType::Xdp, XFA_Element::Xfa);
-  if (!pXFARootNode)
-    return nullptr;
-
-  m_pRootNode = pXFARootNode;
-  pXFARootNode->JSObject()->SetCData(XFA_Attribute::Name, L"xfa", false, false);
-
-  for (auto it : ToXMLElement(pXMLDocumentNode)->GetAttributes()) {
-    if (it.first.EqualsASCII("uuid")) {
-      pXFARootNode->JSObject()->SetCData(XFA_Attribute::Uuid, it.second, false,
-                                         false);
-    } else if (it.first.EqualsASCII("timeStamp")) {
-      pXFARootNode->JSObject()->SetCData(XFA_Attribute::TimeStamp, it.second,
-                                         false, false);
-    }
-  }
-
-  CFX_XMLNode* pXMLConfigDOMRoot = nullptr;
-  CXFA_Node* pXFAConfigDOMRoot = nullptr;
-  XFA_PACKETINFO config_packet = XFA_GetPacketByIndex(XFA_PacketType::Config);
-  for (CFX_XMLNode* pChildItem = pXMLDocumentNode->GetFirstChild(); pChildItem;
-       pChildItem = pChildItem->GetNextSibling()) {
-    if (!MatchNodeName(pChildItem, config_packet.name, config_packet.uri,
-                       config_packet.flags)) {
-      continue;
-    }
-    // TODO(tsepez): make GetFirstChildByName() take a name.
-    uint32_t hash = FX_HashCode_GetW(config_packet.name, false);
-    if (pXFARootNode->GetFirstChildByName(hash))
-      return nullptr;
-
-    pXMLConfigDOMRoot = pChildItem;
-    pXFAConfigDOMRoot = ParseAsXDPPacket_Config(pXMLConfigDOMRoot);
-    if (pXFAConfigDOMRoot)
-      pXFARootNode->InsertChildAndNotify(pXFAConfigDOMRoot, nullptr);
-  }
-
-  CFX_XMLNode* pXMLDatasetsDOMRoot = nullptr;
-  CFX_XMLNode* pXMLFormDOMRoot = nullptr;
-  CFX_XMLNode* pXMLTemplateDOMRoot = nullptr;
-  for (CFX_XMLNode* pChildItem = pXMLDocumentNode->GetFirstChild(); pChildItem;
-       pChildItem = pChildItem->GetNextSibling()) {
-    CFX_XMLElement* pElement = ToXMLElement(pChildItem);
-    if (!pElement || pElement == pXMLConfigDOMRoot)
-      continue;
-
-    WideString wsPacketName = pElement->GetLocalTagName();
-    Optional<XFA_PACKETINFO> packet_info =
-        XFA_GetPacketByName(wsPacketName.AsStringView());
-    if (packet_info.has_value() && packet_info.value().uri &&
-        !MatchNodeName(pElement, packet_info.value().name,
-                       packet_info.value().uri, packet_info.value().flags)) {
-      packet_info = {};
-    }
-    XFA_PacketType ePacket = XFA_PacketType::User;
-    if (packet_info.has_value())
-      ePacket = packet_info.value().packet_type;
-    if (ePacket == XFA_PacketType::Xdp)
-      continue;
-    if (ePacket == XFA_PacketType::Datasets) {
-      if (pXMLDatasetsDOMRoot)
-        return nullptr;
-
-      pXMLDatasetsDOMRoot = pElement;
-    } else if (ePacket == XFA_PacketType::Form) {
-      if (pXMLFormDOMRoot)
-        return nullptr;
-
-      pXMLFormDOMRoot = pElement;
-    } else if (ePacket == XFA_PacketType::Template) {
-      // Found a duplicate template packet.
-      if (pXMLTemplateDOMRoot)
-        return nullptr;
-
-      CXFA_Node* pPacketNode = ParseAsXDPPacket_Template(pElement);
-      if (pPacketNode) {
-        pXMLTemplateDOMRoot = pElement;
-        pXFARootNode->InsertChildAndNotify(pPacketNode, nullptr);
-      }
-    } else {
-      CXFA_Node* pPacketNode = ParseAsXDPPacket(pElement, ePacket);
-      if (pPacketNode) {
-        if (packet_info.has_value() &&
-            (packet_info.value().flags & XFA_XDPPACKET_FLAGS_SUPPORTONE) &&
-            pXFARootNode->GetFirstChildByName(
-                FX_HashCode_GetW(packet_info.value().name, false))) {
-          return nullptr;
-        }
-        pXFARootNode->InsertChildAndNotify(pPacketNode, nullptr);
-      }
-    }
-  }
-
-  // No template is found.
-  if (!pXMLTemplateDOMRoot)
-    return nullptr;
-
-  if (pXMLDatasetsDOMRoot) {
-    CXFA_Node* pPacketNode =
-        ParseAsXDPPacket(pXMLDatasetsDOMRoot, XFA_PacketType::Datasets);
-    if (pPacketNode)
-      pXFARootNode->InsertChildAndNotify(pPacketNode, nullptr);
-  }
-  if (pXMLFormDOMRoot) {
-    CXFA_Node* pPacketNode =
-        ParseAsXDPPacket(pXMLFormDOMRoot, XFA_PacketType::Form);
-    if (pPacketNode)
-      pXFARootNode->InsertChildAndNotify(pPacketNode, nullptr);
-  }
-
-  pXFARootNode->SetXMLMappingNode(pXMLDocumentNode);
-  return pXFARootNode;
-}
-
-CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_Config(
-    CFX_XMLNode* pXMLDocumentNode) {
-  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Config);
-  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
-    return nullptr;
-
-  CXFA_Node* pNode =
-      m_pFactory->CreateNode(XFA_PacketType::Config, XFA_Element::Config);
-  if (!pNode)
-    return nullptr;
-
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
-  if (!NormalLoader(pNode, pXMLDocumentNode, XFA_PacketType::Config, true))
-    return nullptr;
-
-  pNode->SetXMLMappingNode(pXMLDocumentNode);
-  return pNode;
-}
-
-CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_Template(
-    CFX_XMLNode* pXMLDocumentNode) {
-  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Template);
-  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
-    return nullptr;
-
-  CXFA_Node* pNode =
-      m_pFactory->CreateNode(XFA_PacketType::Template, XFA_Element::Template);
-  if (!pNode)
-    return nullptr;
-
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
-
-  CFX_XMLElement* pXMLDocumentElement = ToXMLElement(pXMLDocumentNode);
-  WideString wsNamespaceURI = pXMLDocumentElement->GetNamespaceURI();
-  if (wsNamespaceURI.IsEmpty())
-    wsNamespaceURI = pXMLDocumentElement->GetAttribute(L"xmlns:xfa");
-
-  pNode->GetDocument()->RecognizeXFAVersionNumber(wsNamespaceURI);
-
-  if (!NormalLoader(pNode, pXMLDocumentNode, XFA_PacketType::Template, true))
-    return nullptr;
-
-  pNode->SetXMLMappingNode(pXMLDocumentNode);
-  return pNode;
-}
-
-CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_Form(
-    CFX_XMLNode* pXMLDocumentNode) {
-  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Form);
-  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
-    return nullptr;
-
-  CXFA_Node* pNode =
-      m_pFactory->CreateNode(XFA_PacketType::Form, XFA_Element::Form);
-  if (!pNode)
-    return nullptr;
-
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
-  CXFA_Template* pTemplateRoot =
-      m_pRootNode->GetFirstChildByClass<CXFA_Template>(XFA_Element::Template);
-  CXFA_Subform* pTemplateChosen =
-      pTemplateRoot ? pTemplateRoot->GetFirstChildByClass<CXFA_Subform>(
-                          XFA_Element::Subform)
-                    : nullptr;
-  bool bUseAttribute = true;
-  if (pTemplateChosen &&
-      pTemplateChosen->JSObject()->GetEnum(XFA_Attribute::RestoreState) !=
-          XFA_AttributeValue::Auto) {
-    bUseAttribute = false;
-  }
-  if (!NormalLoader(pNode, pXMLDocumentNode, XFA_PacketType::Form,
-                    bUseAttribute))
-    return nullptr;
-
-  pNode->SetXMLMappingNode(pXMLDocumentNode);
-  return pNode;
-}
-
-CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_Data(
-    CFX_XMLNode* pXMLDocumentNode) {
-  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Datasets);
-  CFX_XMLNode* pDatasetsXMLNode = GetDataSetsFromXDP(pXMLDocumentNode);
-  if (pDatasetsXMLNode) {
-    CXFA_Node* pNode = m_pFactory->CreateNode(XFA_PacketType::Datasets,
-                                              XFA_Element::DataModel);
-    if (!pNode)
-      return nullptr;
-
-    pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
-    if (!DataLoader(pNode, pDatasetsXMLNode, false))
-      return nullptr;
-
-    pNode->SetXMLMappingNode(pDatasetsXMLNode);
-    return pNode;
-  }
-
-  CFX_XMLNode* pDataXMLNode = nullptr;
-  if (MatchNodeName(pXMLDocumentNode, L"data", packet.uri, packet.flags)) {
-    ToXMLElement(pXMLDocumentNode)->RemoveAttribute(L"xmlns:xfa");
-    pDataXMLNode = pXMLDocumentNode;
-  } else {
-    auto* pDataElement = xml_doc_->CreateNode<CFX_XMLElement>(L"xfa:data");
-    pXMLDocumentNode->RemoveSelfIfParented();
-
-    CFX_XMLElement* pElement = ToXMLElement(pXMLDocumentNode);
-    pElement->RemoveAttribute(L"xmlns:xfa");
-
-    // The node was either removed from the parent above, or already has no
-    // parent so we can take ownership.
-    pDataElement->AppendLastChild(pXMLDocumentNode);
-    pDataXMLNode = pDataElement;
-  }
-  if (!pDataXMLNode)
-    return nullptr;
-
-  CXFA_Node* pNode =
-      m_pFactory->CreateNode(XFA_PacketType::Datasets, XFA_Element::DataGroup);
-  if (!pNode)
-    return nullptr;
-
-  WideString wsLocalName = ToXMLElement(pDataXMLNode)->GetLocalTagName();
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, wsLocalName, false, false);
-  if (!DataLoader(pNode, pDataXMLNode, true))
-    return nullptr;
-
-  pNode->SetXMLMappingNode(pDataXMLNode);
-  return pNode;
-}
-
-CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_LocaleConnectionSourceSet(
-    CFX_XMLNode* pXMLDocumentNode,
-    XFA_PacketType packet_type,
-    XFA_Element element) {
-  XFA_PACKETINFO packet = XFA_GetPacketByIndex(packet_type);
-  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
-    return nullptr;
-
-  CXFA_Node* pNode = m_pFactory->CreateNode(packet_type, element);
-  if (!pNode)
-    return nullptr;
-
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
-  if (!NormalLoader(pNode, pXMLDocumentNode, packet_type, true))
-    return nullptr;
-
-  pNode->SetXMLMappingNode(pXMLDocumentNode);
-  return pNode;
-}
-
-CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_Xdc(
-    CFX_XMLNode* pXMLDocumentNode) {
-  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Xdc);
-  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
-    return nullptr;
-
-  CXFA_Node* pNode =
-      m_pFactory->CreateNode(XFA_PacketType::Xdc, XFA_Element::Xdc);
-  if (!pNode)
-    return nullptr;
-
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
-  pNode->SetXMLMappingNode(pXMLDocumentNode);
-  return pNode;
-}
-
-CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_User(
-    CFX_XMLNode* pXMLDocumentNode) {
-  CXFA_Node* pNode =
-      m_pFactory->CreateNode(XFA_PacketType::Xdp, XFA_Element::Packet);
-  if (!pNode)
-    return nullptr;
-
-  WideString wsName = ToXMLElement(pXMLDocumentNode)->GetLocalTagName();
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, wsName, false, false);
-  pNode->SetXMLMappingNode(pXMLDocumentNode);
-  return pNode;
-}
-
-CXFA_Node* CXFA_DocumentParser::DataLoader(CXFA_Node* pXFANode,
-                                           CFX_XMLNode* pXMLDoc,
-                                           bool bDoTransform) {
-  ParseDataGroup(pXFANode, pXMLDoc, XFA_PacketType::Datasets);
-  return pXFANode;
-}
-
-CXFA_Node* CXFA_DocumentParser::NormalLoader(CXFA_Node* pXFANode,
-                                             CFX_XMLNode* pXMLDoc,
-                                             XFA_PacketType ePacketID,
-                                             bool bUseAttribute) {
-  constexpr size_t kMaxExecuteRecursion = 1000;
-  if (m_ExecuteRecursionDepth > kMaxExecuteRecursion)
-    return nullptr;
-  AutoRestorer<size_t> restorer(&m_ExecuteRecursionDepth);
-  ++m_ExecuteRecursionDepth;
-
-  bool bOneOfPropertyFound = false;
-  for (CFX_XMLNode* pXMLChild = pXMLDoc->GetFirstChild(); pXMLChild;
-       pXMLChild = pXMLChild->GetNextSibling()) {
-    switch (pXMLChild->GetType()) {
-      case CFX_XMLNode::Type::kElement: {
-        CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLChild);
-        WideString wsTagName = pXMLElement->GetLocalTagName();
-        XFA_Element eType = XFA_GetElementByName(wsTagName.AsStringView());
-        if (eType == XFA_Element::Unknown)
-          continue;
-
-        if (pXFANode->HasPropertyFlags(
-                eType,
-                XFA_PROPERTYFLAG_OneOf | XFA_PROPERTYFLAG_DefaultOneOf)) {
-          if (bOneOfPropertyFound)
-            break;
-          bOneOfPropertyFound = true;
-        }
-
-        CXFA_Node* pXFAChild = m_pFactory->CreateNode(ePacketID, eType);
-        if (!pXFAChild)
-          return nullptr;
-        if (ePacketID == XFA_PacketType::Config) {
-          pXFAChild->JSObject()->SetAttribute(XFA_Attribute::Name,
-                                              wsTagName.AsStringView(), false);
-        }
-
-        bool IsNeedValue = true;
-        for (auto it : pXMLElement->GetAttributes()) {
-          WideString wsAttrName;
-          GetAttributeLocalName(it.first.AsStringView(), wsAttrName);
-          if (wsAttrName.EqualsASCII("nil") && it.second.EqualsASCII("true"))
-            IsNeedValue = false;
-
-          Optional<XFA_ATTRIBUTEINFO> attr =
-              XFA_GetAttributeByName(wsAttrName.AsStringView());
-          if (!attr.has_value())
-            continue;
-
-          if (!bUseAttribute && attr.value().attribute != XFA_Attribute::Name &&
-              attr.value().attribute != XFA_Attribute::Save) {
-            continue;
-          }
-          pXFAChild->JSObject()->SetAttribute(attr.value().attribute,
-                                              it.second.AsStringView(), false);
-        }
-        pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
-        if (eType == XFA_Element::Validate || eType == XFA_Element::Locale) {
-          if (ePacketID == XFA_PacketType::Config)
-            ParseContentNode(pXFAChild, pXMLElement, ePacketID);
-          else
-            NormalLoader(pXFAChild, pXMLElement, ePacketID, bUseAttribute);
-
-          break;
-        }
-        switch (pXFAChild->GetObjectType()) {
-          case XFA_ObjectType::ContentNode:
-          case XFA_ObjectType::TextNode:
-          case XFA_ObjectType::NodeC:
-          case XFA_ObjectType::NodeV:
-            if (IsNeedValue)
-              ParseContentNode(pXFAChild, pXMLElement, ePacketID);
-            break;
-          default:
-            NormalLoader(pXFAChild, pXMLElement, ePacketID, bUseAttribute);
-            break;
-        }
-      } break;
-      case CFX_XMLNode::Type::kInstruction:
-        ParseInstruction(pXFANode, ToXMLInstruction(pXMLChild), ePacketID);
-        break;
-      default:
-        break;
-    }
-  }
-  return pXFANode;
-}
-
-void CXFA_DocumentParser::ParseContentNode(CXFA_Node* pXFANode,
-                                           CFX_XMLNode* pXMLNode,
-                                           XFA_PacketType ePacketID) {
-  XFA_Element element = XFA_Element::Sharptext;
-  if (pXFANode->GetElementType() == XFA_Element::ExData) {
-    WideString wsContentType =
-        pXFANode->JSObject()->GetCData(XFA_Attribute::ContentType);
-    if (wsContentType.EqualsASCII("text/html"))
-      element = XFA_Element::SharpxHTML;
-    else if (wsContentType.EqualsASCII("text/xml"))
-      element = XFA_Element::Sharpxml;
-  }
-  if (element == XFA_Element::SharpxHTML)
-    pXFANode->SetXMLMappingNode(pXMLNode);
-
-  WideString wsValue;
-  for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild;
-       pXMLChild = pXMLChild->GetNextSibling()) {
-    CFX_XMLNode::Type eNodeType = pXMLChild->GetType();
-    if (eNodeType == CFX_XMLNode::Type::kInstruction)
-      continue;
-
-    CFX_XMLElement* pElement = ToXMLElement(pXMLChild);
-    if (element == XFA_Element::SharpxHTML) {
-      if (!pElement)
-        break;
-      if (XFA_RecognizeRichText(pElement))
-        wsValue += GetPlainTextFromRichText(pElement);
-    } else if (element == XFA_Element::Sharpxml) {
-      if (!pElement)
-        break;
-      ConvertXMLToPlainText(pElement, wsValue);
-    } else {
-      if (pElement)
-        break;
-      CFX_XMLText* pText = ToXMLText(pXMLChild);
-      if (pText)
-        wsValue = pText->GetText();
-    }
-    break;
-  }
-  if (!wsValue.IsEmpty()) {
-    if (pXFANode->IsContentNode()) {
-      CXFA_Node* pContentRawDataNode =
-          m_pFactory->CreateNode(ePacketID, element);
-      ASSERT(pContentRawDataNode);
-      pContentRawDataNode->JSObject()->SetCData(XFA_Attribute::Value, wsValue,
-                                                false, false);
-      pXFANode->InsertChildAndNotify(pContentRawDataNode, nullptr);
-    } else {
-      pXFANode->JSObject()->SetCData(XFA_Attribute::Value, wsValue, false,
-                                     false);
-    }
-  }
-}
-
-void CXFA_DocumentParser::ParseDataGroup(CXFA_Node* pXFANode,
-                                         CFX_XMLNode* pXMLNode,
-                                         XFA_PacketType ePacketID) {
-  for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild;
-       pXMLChild = pXMLChild->GetNextSibling()) {
-    switch (pXMLChild->GetType()) {
-      case CFX_XMLNode::Type::kElement: {
-        CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLChild);
-        WideString wsNamespaceURI = pXMLElement->GetNamespaceURI();
-        if (wsNamespaceURI.EqualsASCII(
-                "http://www.xfa.com/schema/xfa-package/") ||
-            wsNamespaceURI.EqualsASCII(
-                "http://www.xfa.org/schema/xfa-package/") ||
-            wsNamespaceURI.EqualsASCII(
-                "http://www.w3.org/2001/XMLSchema-instance")) {
-          continue;
-        }
-
-        XFA_Element eNodeType = XFA_Element::DataModel;
-        if (eNodeType == XFA_Element::DataModel) {
-          Optional<WideString> wsDataNodeAttr =
-              FindAttributeWithNS(pXMLElement, L"dataNode",
-                                  L"http://www.xfa.org/schema/xfa-data/1.0/");
-          if (wsDataNodeAttr.has_value()) {
-            if (wsDataNodeAttr.value().EqualsASCII("dataGroup"))
-              eNodeType = XFA_Element::DataGroup;
-            else if (wsDataNodeAttr.value().EqualsASCII("dataValue"))
-              eNodeType = XFA_Element::DataValue;
-          }
-        }
-        if (eNodeType == XFA_Element::DataModel) {
-          Optional<WideString> wsContentType =
-              FindAttributeWithNS(pXMLElement, L"contentType",
-                                  L"http://www.xfa.org/schema/xfa-data/1.0/");
-          if (wsContentType.has_value() && !wsContentType.value().IsEmpty())
-            eNodeType = XFA_Element::DataValue;
-        }
-        if (eNodeType == XFA_Element::DataModel) {
-          for (CFX_XMLNode* pXMLDataChild = pXMLElement->GetFirstChild();
-               pXMLDataChild; pXMLDataChild = pXMLDataChild->GetNextSibling()) {
-            CFX_XMLElement* pElement = ToXMLElement(pXMLDataChild);
-            if (pElement && !XFA_RecognizeRichText(pElement)) {
-              eNodeType = XFA_Element::DataGroup;
-              break;
-            }
-          }
-        }
-        if (eNodeType == XFA_Element::DataModel)
-          eNodeType = XFA_Element::DataValue;
-
-        CXFA_Node* pXFAChild =
-            m_pFactory->CreateNode(XFA_PacketType::Datasets, eNodeType);
-        if (!pXFAChild)
-          return;
-
-        pXFAChild->JSObject()->SetCData(
-            XFA_Attribute::Name, pXMLElement->GetLocalTagName(), false, false);
-        bool bNeedValue = true;
-
-        for (auto it : pXMLElement->GetAttributes()) {
-          WideString wsName;
-          WideString wsNS;
-          if (!ResolveAttribute(pXMLElement, it.first, wsName, wsNS)) {
-            continue;
-          }
-          if (wsName.EqualsASCII("nil") && it.second.EqualsASCII("true")) {
-            bNeedValue = false;
-            continue;
-          }
-          if (wsNS.EqualsASCII("http://www.xfa.com/schema/xfa-package/") ||
-              wsNS.EqualsASCII("http://www.xfa.org/schema/xfa-package/") ||
-              wsNS.EqualsASCII("http://www.w3.org/2001/XMLSchema-instance") ||
-              wsNS.EqualsASCII("http://www.xfa.org/schema/xfa-data/1.0/")) {
-            continue;
-          }
-          CXFA_Node* pXFAMetaData = m_pFactory->CreateNode(
-              XFA_PacketType::Datasets, XFA_Element::DataValue);
-          if (!pXFAMetaData)
-            return;
-
-          pXFAMetaData->JSObject()->SetCData(XFA_Attribute::Name, wsName, false,
-                                             false);
-          pXFAMetaData->JSObject()->SetCData(XFA_Attribute::QualifiedName,
-                                             it.first, false, false);
-          pXFAMetaData->JSObject()->SetCData(XFA_Attribute::Value, it.second,
-                                             false, false);
-          pXFAMetaData->JSObject()->SetEnum(
-              XFA_Attribute::Contains, XFA_AttributeValue::MetaData, false);
-          pXFAChild->InsertChildAndNotify(pXFAMetaData, nullptr);
-          pXFAMetaData->SetXMLMappingNode(pXMLElement);
-          pXFAMetaData->SetFlag(XFA_NodeFlag_Initialized);
-        }
-
-        if (!bNeedValue)
-          pXMLElement->RemoveAttribute(L"xsi:nil");
-
-        pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
-        if (eNodeType == XFA_Element::DataGroup)
-          ParseDataGroup(pXFAChild, pXMLElement, ePacketID);
-        else if (bNeedValue)
-          ParseDataValue(pXFAChild, pXMLChild, XFA_PacketType::Datasets);
-
-        pXFAChild->SetXMLMappingNode(pXMLElement);
-        pXFAChild->SetFlag(XFA_NodeFlag_Initialized);
-        continue;
-      }
-      case CFX_XMLNode::Type::kCharData:
-      case CFX_XMLNode::Type::kText: {
-        CFX_XMLText* pXMLText = ToXMLText(pXMLChild);
-        WideString wsText = pXMLText->GetText();
-        if (IsStringAllWhitespace(wsText))
-          continue;
-
-        CXFA_Node* pXFAChild = m_pFactory->CreateNode(XFA_PacketType::Datasets,
-                                                      XFA_Element::DataValue);
-        if (!pXFAChild)
-          return;
-
-        pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsText, false,
-                                        false);
-        pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
-        pXFAChild->SetXMLMappingNode(pXMLText);
-        pXFAChild->SetFlag(XFA_NodeFlag_Initialized);
-        continue;
-      }
-      default:
-        continue;
-    }
-  }
-}
-
-void CXFA_DocumentParser::ParseDataValue(CXFA_Node* pXFANode,
-                                         CFX_XMLNode* pXMLNode,
-                                         XFA_PacketType ePacketID) {
-  CFX_WideTextBuf wsValueTextBuf;
-  CFX_WideTextBuf wsCurValueTextBuf;
-  bool bMarkAsCompound = false;
-  CFX_XMLNode* pXMLCurValueNode = nullptr;
-  for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild;
-       pXMLChild = pXMLChild->GetNextSibling()) {
-    CFX_XMLNode::Type eNodeType = pXMLChild->GetType();
-    if (eNodeType == CFX_XMLNode::Type::kInstruction)
-      continue;
-
-    CFX_XMLText* pText = ToXMLText(pXMLChild);
-    if (pText) {
-      WideString wsText = pText->GetText();
-      if (!pXMLCurValueNode)
-        pXMLCurValueNode = pXMLChild;
-      wsCurValueTextBuf << wsText;
-      continue;
-    }
-    if (XFA_RecognizeRichText(ToXMLElement(pXMLChild))) {
-      WideString wsText = GetPlainTextFromRichText(ToXMLElement(pXMLChild));
-      if (!pXMLCurValueNode)
-        pXMLCurValueNode = pXMLChild;
-      wsCurValueTextBuf << wsText;
-      continue;
-    }
-    bMarkAsCompound = true;
-    if (pXMLCurValueNode) {
-      WideString wsCurValue = wsCurValueTextBuf.MakeString();
-      if (!wsCurValue.IsEmpty()) {
-        CXFA_Node* pXFAChild =
-            m_pFactory->CreateNode(ePacketID, XFA_Element::DataValue);
-        if (!pXFAChild)
-          return;
-
-        pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, WideString(),
-                                        false, false);
-        pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsCurValue, false,
-                                        false);
-        pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
-        pXFAChild->SetXMLMappingNode(pXMLCurValueNode);
-        pXFAChild->SetFlag(XFA_NodeFlag_Initialized);
-        wsValueTextBuf << wsCurValue;
-        wsCurValueTextBuf.Clear();
-      }
-      pXMLCurValueNode = nullptr;
-    }
-    CXFA_Node* pXFAChild =
-        m_pFactory->CreateNode(ePacketID, XFA_Element::DataValue);
-    if (!pXFAChild)
-      return;
-
-    WideString wsNodeStr = ToXMLElement(pXMLChild)->GetLocalTagName();
-    pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, wsNodeStr, false,
-                                    false);
-    ParseDataValue(pXFAChild, pXMLChild, ePacketID);
-    pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
-    pXFAChild->SetXMLMappingNode(pXMLChild);
-    pXFAChild->SetFlag(XFA_NodeFlag_Initialized);
-    WideString wsCurValue =
-        pXFAChild->JSObject()->GetCData(XFA_Attribute::Value);
-    wsValueTextBuf << wsCurValue;
-  }
-
-  if (pXMLCurValueNode) {
-    WideString wsCurValue = wsCurValueTextBuf.MakeString();
-    if (!wsCurValue.IsEmpty()) {
-      if (bMarkAsCompound) {
-        CXFA_Node* pXFAChild =
-            m_pFactory->CreateNode(ePacketID, XFA_Element::DataValue);
-        if (!pXFAChild)
-          return;
-
-        pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, WideString(),
-                                        false, false);
-        pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsCurValue, false,
-                                        false);
-        pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
-        pXFAChild->SetXMLMappingNode(pXMLCurValueNode);
-        pXFAChild->SetFlag(XFA_NodeFlag_Initialized);
-      }
-      wsValueTextBuf << wsCurValue;
-      wsCurValueTextBuf.Clear();
-    }
-    pXMLCurValueNode = nullptr;
-  }
-  WideString wsNodeValue = wsValueTextBuf.MakeString();
-  pXFANode->JSObject()->SetCData(XFA_Attribute::Value, wsNodeValue, false,
-                                 false);
-}
-
-void CXFA_DocumentParser::ParseInstruction(CXFA_Node* pXFANode,
-                                           CFX_XMLInstruction* pXMLInstruction,
-                                           XFA_PacketType ePacketID) {
-  const std::vector<WideString>& target_data = pXMLInstruction->GetTargetData();
-  if (pXMLInstruction->IsOriginalXFAVersion()) {
-    if (target_data.size() > 1 &&
-        (pXFANode->GetDocument()->RecognizeXFAVersionNumber(target_data[0]) !=
-         XFA_VERSION_UNKNOWN) &&
-        target_data[1].EqualsASCII("v2.7-scripting:1")) {
-      pXFANode->GetDocument()->set_is_scripting();
-    }
-    return;
-  }
-  if (pXMLInstruction->IsAcrobat()) {
-    if (target_data.size() > 1 && target_data[0].EqualsASCII("JavaScript") &&
-        target_data[1].EqualsASCII("strictScoping")) {
-      pXFANode->GetDocument()->set_is_strict_scoping();
-    }
-  }
-}
diff --git a/xfa/fxfa/parser/cxfa_document_parser.h b/xfa/fxfa/parser/cxfa_document_parser.h
deleted file mode 100644
index a494b4f..0000000
--- a/xfa/fxfa/parser/cxfa_document_parser.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_PARSER_CXFA_DOCUMENT_PARSER_H_
-#define XFA_FXFA_PARSER_CXFA_DOCUMENT_PARSER_H_
-
-#include <memory>
-#include <utility>
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "xfa/fxfa/fxfa_basic.h"
-
-class CFX_XMLDocument;
-class CFX_XMLNode;
-class CXFA_Document;
-class CXFA_Node;
-class CFX_XMLInstruction;
-class IFX_SeekableReadStream;
-
-class CXFA_DocumentParser {
- public:
-  explicit CXFA_DocumentParser(CXFA_Document* pFactory);
-  ~CXFA_DocumentParser();
-
-  bool Parse(const RetainPtr<IFX_SeekableReadStream>& pStream,
-             XFA_PacketType ePacketID);
-
-  CFX_XMLNode* ParseXMLData(const ByteString& wsXML);
-  void ConstructXFANode(CXFA_Node* pXFANode, CFX_XMLNode* pXMLNode);
-
-  std::unique_ptr<CFX_XMLDocument> GetXMLDoc() { return std::move(xml_doc_); }
-  CXFA_Node* GetRootNode() const;
-
- private:
-  std::unique_ptr<CFX_XMLDocument> LoadXML(
-      const RetainPtr<IFX_SeekableReadStream>& pStream);
-
-  CXFA_Node* ParseAsXDPPacket(CFX_XMLNode* pXMLDocumentNode,
-                              XFA_PacketType ePacketID);
-  CXFA_Node* ParseAsXDPPacket_XDP(CFX_XMLNode* pXMLDocumentNode);
-  CXFA_Node* ParseAsXDPPacket_Config(CFX_XMLNode* pXMLDocumentNode);
-  CXFA_Node* ParseAsXDPPacket_Template(CFX_XMLNode* pXMLDocumentNode);
-  CXFA_Node* ParseAsXDPPacket_Form(CFX_XMLNode* pXMLDocumentNode);
-  CXFA_Node* ParseAsXDPPacket_Data(CFX_XMLNode* pXMLDocumentNode);
-  CXFA_Node* ParseAsXDPPacket_LocaleConnectionSourceSet(
-      CFX_XMLNode* pXMLDocumentNode,
-      XFA_PacketType packet_type,
-      XFA_Element element);
-  CXFA_Node* ParseAsXDPPacket_Xdc(CFX_XMLNode* pXMLDocumentNode);
-  CXFA_Node* ParseAsXDPPacket_User(CFX_XMLNode* pXMLDocumentNode);
-  CXFA_Node* NormalLoader(CXFA_Node* pXFANode,
-                          CFX_XMLNode* pXMLDoc,
-                          XFA_PacketType ePacketID,
-                          bool bUseAttribute);
-  CXFA_Node* DataLoader(CXFA_Node* pXFANode,
-                        CFX_XMLNode* pXMLDoc,
-                        bool bDoTransform);
-  void ParseContentNode(CXFA_Node* pXFANode,
-                        CFX_XMLNode* pXMLNode,
-                        XFA_PacketType ePacketID);
-  void ParseDataValue(CXFA_Node* pXFANode,
-                      CFX_XMLNode* pXMLNode,
-                      XFA_PacketType ePacketID);
-  void ParseDataGroup(CXFA_Node* pXFANode,
-                      CFX_XMLNode* pXMLNode,
-                      XFA_PacketType ePacketID);
-  void ParseInstruction(CXFA_Node* pXFANode,
-                        CFX_XMLInstruction* pXMLInstruction,
-                        XFA_PacketType ePacketID);
-
-  UnownedPtr<CXFA_Document> m_pFactory;
-  std::unique_ptr<CFX_XMLDocument> xml_doc_;
-  // TODO(dsinclair): Figure out who owns this.
-  CXFA_Node* m_pRootNode = nullptr;
-  size_t m_ExecuteRecursionDepth = 0;
-};
-
-#endif  // XFA_FXFA_PARSER_CXFA_DOCUMENT_PARSER_H_
diff --git a/xfa/fxfa/parser/cxfa_document_parser_embeddertest.cpp b/xfa/fxfa/parser/cxfa_document_parser_embeddertest.cpp
deleted file mode 100644
index 129c8bc..0000000
--- a/xfa/fxfa/parser/cxfa_document_parser_embeddertest.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "testing/embedder_test.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-class CXFASimpleParserEmbedderTest : public EmbedderTest {};
-
-TEST_F(CXFASimpleParserEmbedderTest, Bug_216) {
-  EXPECT_TRUE(OpenDocument("bug_216.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
-  UnloadPage(page);
-}
-
-TEST_F(CXFASimpleParserEmbedderTest, Bug_709793) {
-  EXPECT_TRUE(OpenDocument("bug_709793.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
-  UnloadPage(page);
-}
diff --git a/xfa/fxfa/parser/cxfa_document_parser_unittest.cpp b/xfa/fxfa/parser/cxfa_document_parser_unittest.cpp
deleted file mode 100644
index 625473a..0000000
--- a/xfa/fxfa/parser/cxfa_document_parser_unittest.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "xfa/fxfa/parser/cxfa_document_parser.h"
-
-#include "core/fxcrt/cfx_readonlymemorystream.h"
-#include "core/fxcrt/xml/cfx_xmldocument.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fxfa/parser/cxfa_document.h"
-
-class CXFA_DocumentParserTest : public testing::Test {
- public:
-  void SetUp() override {
-    doc_ = pdfium::MakeUnique<CXFA_Document>(nullptr, nullptr);
-    parser_ = pdfium::MakeUnique<CXFA_DocumentParser>(doc_.get());
-  }
-
-  void TearDown() override {
-    // Hold the XML tree until we cleanup the document.
-    std::unique_ptr<CFX_XMLDocument> doc = parser_->GetXMLDoc();
-    parser_ = nullptr;
-    doc_ = nullptr;
-  }
-
-  CXFA_Document* GetDoc() const { return doc_.get(); }
-  CXFA_DocumentParser* GetParser() const { return parser_.get(); }
-
- private:
-  std::unique_ptr<CXFA_Document> doc_;
-  std::unique_ptr<CXFA_DocumentParser> parser_;
-};
-
-TEST_F(CXFA_DocumentParserTest, XMLInstructionsScriptOff) {
-  static const char kInput[] =
-      "<config>\n"
-      "<?originalXFAVersion http://www.xfa.org/schema/xfa-template/2.7 "
-      "v2.7-scripting:0 ?>\n"
-      "</config>";
-  EXPECT_FALSE(GetDoc()->is_scripting());
-
-  auto stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-      pdfium::as_bytes(pdfium::make_span(kInput)));
-  ASSERT_TRUE(GetParser()->Parse(stream, XFA_PacketType::Config));
-
-  CXFA_Node* root = GetParser()->GetRootNode();
-  ASSERT_TRUE(root);
-  EXPECT_FALSE(GetDoc()->is_scripting());
-}
-
-TEST_F(CXFA_DocumentParserTest, XMLInstructionsScriptOn) {
-  static const char kInput[] =
-      "<config>\n"
-      "<?originalXFAVersion http://www.xfa.org/schema/xfa-template/2.7 "
-      "v2.7-scripting:1 ?>\n"
-      "</config>";
-
-  EXPECT_FALSE(GetDoc()->is_scripting());
-
-  auto stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-      pdfium::as_bytes(pdfium::make_span(kInput)));
-  ASSERT_TRUE(GetParser()->Parse(stream, XFA_PacketType::Config));
-
-  CXFA_Node* root = GetParser()->GetRootNode();
-  ASSERT_TRUE(root);
-  EXPECT_TRUE(GetDoc()->is_scripting());
-}
-
-TEST_F(CXFA_DocumentParserTest, XMLInstructionsStrictScope) {
-  static const char kInput[] =
-      "<config>"
-      "<?acrobat JavaScript strictScoping ?>\n"
-      "</config>";
-
-  EXPECT_FALSE(GetDoc()->is_strict_scoping());
-
-  auto stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-      pdfium::as_bytes(pdfium::make_span(kInput)));
-  ASSERT_TRUE(GetParser()->Parse(stream, XFA_PacketType::Config));
-
-  CXFA_Node* root = GetParser()->GetRootNode();
-  ASSERT_TRUE(root);
-  EXPECT_TRUE(GetDoc()->is_strict_scoping());
-}
-
-TEST_F(CXFA_DocumentParserTest, XMLInstructionsStrictScopeBad) {
-  static const char kInput[] =
-      "<config>"
-      "<?acrobat JavaScript otherScoping ?>\n"
-      "</config>";
-
-  EXPECT_FALSE(GetDoc()->is_strict_scoping());
-
-  auto stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-      pdfium::as_bytes(pdfium::make_span(kInput)));
-  ASSERT_TRUE(GetParser()->Parse(stream, XFA_PacketType::Config));
-
-  CXFA_Node* root = GetParser()->GetRootNode();
-  ASSERT_TRUE(root);
-  EXPECT_FALSE(GetDoc()->is_strict_scoping());
-}
-
-TEST_F(CXFA_DocumentParserTest, MultipleXMLInstructions) {
-  static const char kInput[] =
-      "<config>"
-      "<?originalXFAVersion http://www.xfa.org/schema/xfa-template/2.7 "
-      "v2.7-scripting:1 ?>\n"
-      "<?acrobat JavaScript strictScoping ?>\n"
-      "</config>";
-
-  EXPECT_FALSE(GetDoc()->is_scripting());
-  EXPECT_FALSE(GetDoc()->is_strict_scoping());
-
-  auto stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
-      pdfium::as_bytes(pdfium::make_span(kInput)));
-  ASSERT_TRUE(GetParser()->Parse(stream, XFA_PacketType::Config));
-
-  CXFA_Node* root = GetParser()->GetRootNode();
-  ASSERT_TRUE(root);
-
-  EXPECT_TRUE(GetDoc()->is_scripting());
-  EXPECT_TRUE(GetDoc()->is_strict_scoping());
-}
diff --git a/xfa/fxfa/parser/cxfa_document_unittest.cpp b/xfa/fxfa/parser/cxfa_document_unittest.cpp
new file mode 100644
index 0000000..f1863b6
--- /dev/null
+++ b/xfa/fxfa/parser/cxfa_document_unittest.cpp
@@ -0,0 +1,174 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fxfa/parser/cxfa_document.h"
+
+#include "core/fxcrt/fx_string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(CXFA_DocumentTest, ParseXFAVersion) {
+  // Malformed
+  EXPECT_EQ(XFA_VERSION_UNKNOWN, CXFA_Document::ParseXFAVersion(L""));
+  EXPECT_EQ(XFA_VERSION_UNKNOWN,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-template"));
+  EXPECT_EQ(XFA_VERSION_UNKNOWN,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-templatX/"));
+  EXPECT_EQ(XFA_VERSION_UNKNOWN,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-template/"));
+  EXPECT_EQ(XFA_VERSION_UNKNOWN,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-template/2"));
+
+  // Out-of-range
+  EXPECT_EQ(XFA_VERSION_UNKNOWN,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-template/-1.0"));
+  EXPECT_EQ(XFA_VERSION_UNKNOWN,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-template/1.9"));
+  EXPECT_EQ(XFA_VERSION_UNKNOWN,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-template/4.1"));
+
+  // Missing digits
+  EXPECT_EQ(XFA_VERSION_UNKNOWN,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-template/."));
+  EXPECT_EQ(XFA_VERSION_UNKNOWN,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-template/.3"));
+  EXPECT_EQ(XFA_VERSION_300, CXFA_Document::ParseXFAVersion(
+                                 L"http://www.xfa.org/schema/xfa-template/3."));
+  EXPECT_EQ(XFA_VERSION_UNKNOWN,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-template/clams.6"));
+  EXPECT_EQ(XFA_VERSION_300,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-template/3.clams"));
+
+  // Min / max values
+  EXPECT_EQ(XFA_VERSION_200,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-template/2.0"));
+  EXPECT_EQ(400, CXFA_Document::ParseXFAVersion(
+                     L"http://www.xfa.org/schema/xfa-template/4.0"));
+
+  // Number and decimal point parsing.
+  EXPECT_EQ(XFA_VERSION_306,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-template/3.6"));
+
+  // TODO(tsepez): maybe fail on these dubious values?
+  EXPECT_EQ(XFA_VERSION_306,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-template/0003.00006"));
+  EXPECT_EQ(XFA_VERSION_306,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-template/0003.00006.0000"));
+  EXPECT_EQ(XFA_VERSION_206,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-template/2.6clams"));
+  EXPECT_EQ(XFA_VERSION_206,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-template/1.106"));
+  EXPECT_EQ(XFA_VERSION_306,
+            CXFA_Document::ParseXFAVersion(
+                L"http://www.xfa.org/schema/xfa-template/4.-94"));
+  EXPECT_EQ(317, CXFA_Document::ParseXFAVersion(
+                     L"http://www.xfa.org/schema/xfa-template/3.17"));
+}
+
+TEST(CXFA_DocumentTest, ParseUseHref) {
+  {
+    WideString wsEmpty;  // Must outlive views.
+    WideStringView wsURI;
+    WideStringView wsID;
+    WideStringView wsSOM;
+    CXFA_Document::ParseUseHref(wsEmpty, wsURI, wsID, wsSOM);
+    EXPECT_EQ(L"", wsURI);
+    EXPECT_EQ(L"", wsID);
+    EXPECT_EQ(L"", wsSOM);
+  }
+  {
+    WideString wsNoSharp(L"url-part-only");  // Must outlive views.
+    WideStringView wsURI;
+    WideStringView wsID;
+    WideStringView wsSOM;
+    CXFA_Document::ParseUseHref(wsNoSharp, wsURI, wsID, wsSOM);
+    EXPECT_EQ(L"url-part-only", wsURI);
+    EXPECT_EQ(L"", wsID);
+    EXPECT_EQ(L"", wsSOM);
+  }
+  {
+    WideString wsNoSom(L"url-part#frag");  // Must outlive views.
+    WideStringView wsURI;
+    WideStringView wsID;
+    WideStringView wsSOM;
+    CXFA_Document::ParseUseHref(wsNoSom, wsURI, wsID, wsSOM);
+    EXPECT_EQ(L"url-part", wsURI);
+    EXPECT_EQ(L"frag", wsID);
+    EXPECT_EQ(L"", wsSOM);
+  }
+  {
+    WideString wsIncompleteSom(L"url-part#som(");  // Must outlive views.
+    WideStringView wsURI;
+    WideStringView wsID;
+    WideStringView wsSOM;
+    CXFA_Document::ParseUseHref(wsIncompleteSom, wsURI, wsID, wsSOM);
+    EXPECT_EQ(L"url-part", wsURI);
+    EXPECT_EQ(L"som(", wsID);
+    EXPECT_EQ(L"", wsSOM);
+  }
+  {
+    WideString wsEmptySom(L"url-part#som()");  // Must outlive views.
+    WideStringView wsURI;
+    WideStringView wsID;
+    WideStringView wsSOM;
+    CXFA_Document::ParseUseHref(wsEmptySom, wsURI, wsID, wsSOM);
+    EXPECT_EQ(L"url-part", wsURI);
+    EXPECT_EQ(L"", wsID);
+    EXPECT_EQ(L"", wsSOM);
+  }
+  {
+    WideString wsHasSom(
+        L"url-part#som(nested(foo.bar))");  // Must outlive views.
+    WideStringView wsURI;
+    WideStringView wsID;
+    WideStringView wsSOM;
+    CXFA_Document::ParseUseHref(wsHasSom, wsURI, wsID, wsSOM);
+    EXPECT_EQ(L"url-part", wsURI);
+    EXPECT_EQ(L"", wsID);
+    EXPECT_EQ(L"nested(foo.bar)", wsSOM);
+  }
+}
+
+TEST(CXFA_DocumentTest, ParseUse) {
+  {
+    WideString wsUseVal(L"");  // Must outlive views.
+    WideStringView wsID;
+    WideStringView wsSOM;
+    CXFA_Document::ParseUse(wsUseVal, wsID, wsSOM);
+    EXPECT_EQ(L"", wsID);
+    EXPECT_EQ(L"", wsSOM);
+  }
+  {
+    WideString wsUseVal(L"clams");  // Must outlive views.
+    WideStringView wsID;
+    WideStringView wsSOM;
+    CXFA_Document::ParseUse(wsUseVal, wsID, wsSOM);
+    EXPECT_EQ(L"", wsID);
+    EXPECT_EQ(L"clams", wsSOM);
+  }
+  {
+    WideString wsUseVal(L"#clams");  // Must outlive views.
+    WideStringView wsID;
+    WideStringView wsSOM;
+    CXFA_Document::ParseUse(wsUseVal, wsID, wsSOM);
+    EXPECT_EQ(L"clams", wsID);
+    EXPECT_EQ(L"", wsSOM);
+  }
+}
diff --git a/xfa/fxfa/parser/cxfa_documentassembly.cpp b/xfa/fxfa/parser/cxfa_documentassembly.cpp
index c6582e8..e590643 100644
--- a/xfa/fxfa/parser/cxfa_documentassembly.cpp
+++ b/xfa/fxfa/parser/cxfa_documentassembly.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_documentassembly.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                              XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::DocumentAssembly,
                 {},
                 kDocumentAssemblyAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_DocumentAssembly::~CXFA_DocumentAssembly() = default;
diff --git a/xfa/fxfa/parser/cxfa_documentassembly.h b/xfa/fxfa/parser/cxfa_documentassembly.h
index 92cde65..a8f2015 100644
--- a/xfa/fxfa/parser/cxfa_documentassembly.h
+++ b/xfa/fxfa/parser/cxfa_documentassembly.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_DocumentAssembly final : public CXFA_Node {
  public:
-  CXFA_DocumentAssembly(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_DocumentAssembly() override;
+
+ private:
+  CXFA_DocumentAssembly(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DOCUMENTASSEMBLY_H_
diff --git a/xfa/fxfa/parser/cxfa_draw.cpp b/xfa/fxfa/parser/cxfa_draw.cpp
index 02387d7..abe8b4c 100644
--- a/xfa/fxfa/parser/cxfa_draw.cpp
+++ b/xfa/fxfa/parser/cxfa_draw.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,17 +7,17 @@
 #include "xfa/fxfa/parser/cxfa_draw.h"
 
 #include "fxjs/xfa/cjx_draw.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kDrawPropertyData[] = {
-    {XFA_Element::Ui, 1, 0},     {XFA_Element::Margin, 1, 0},
-    {XFA_Element::Para, 1, 0},   {XFA_Element::Border, 1, 0},
-    {XFA_Element::Assist, 1, 0}, {XFA_Element::Traversal, 1, 0},
-    {XFA_Element::Keep, 1, 0},   {XFA_Element::Caption, 1, 0},
-    {XFA_Element::Desc, 1, 0},   {XFA_Element::Font, 1, 0},
-    {XFA_Element::Value, 1, 0},  {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Ui, 1, {}},     {XFA_Element::Margin, 1, {}},
+    {XFA_Element::Para, 1, {}},   {XFA_Element::Border, 1, {}},
+    {XFA_Element::Assist, 1, {}}, {XFA_Element::Traversal, 1, {}},
+    {XFA_Element::Keep, 1, {}},   {XFA_Element::Caption, 1, {}},
+    {XFA_Element::Desc, 1, {}},   {XFA_Element::Font, 1, {}},
+    {XFA_Element::Value, 1, {}},  {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kDrawAttributeData[] = {
@@ -52,11 +52,13 @@
 CXFA_Draw::CXFA_Draw(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::Draw,
                 kDrawPropertyData,
                 kDrawAttributeData,
-                pdfium::MakeUnique<CJX_Draw>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Draw>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Draw::~CXFA_Draw() = default;
diff --git a/xfa/fxfa/parser/cxfa_draw.h b/xfa/fxfa/parser/cxfa_draw.h
index c1b9006..06a03c7 100644
--- a/xfa/fxfa/parser/cxfa_draw.h
+++ b/xfa/fxfa/parser/cxfa_draw.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Draw final : public CXFA_Node {
  public:
-  CXFA_Draw(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Draw() override;
+
+ private:
+  CXFA_Draw(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DRAW_H_
diff --git a/xfa/fxfa/parser/cxfa_driver.cpp b/xfa/fxfa/parser/cxfa_driver.cpp
index 215601c..1a6dadb 100644
--- a/xfa/fxfa/parser/cxfa_driver.cpp
+++ b/xfa/fxfa/parser/cxfa_driver.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_driver.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kDriverPropertyData[] = {
-    {XFA_Element::FontInfo, 1, 0},
-    {XFA_Element::Xdc, 1, 0},
+    {XFA_Element::FontInfo, 1, {}},
+    {XFA_Element::Xdc, 1, {}},
 };
 
 const CXFA_Node::AttributeData kDriverAttributeData[] = {
@@ -27,11 +27,13 @@
 CXFA_Driver::CXFA_Driver(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Driver,
                 kDriverPropertyData,
                 kDriverAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Driver::~CXFA_Driver() = default;
diff --git a/xfa/fxfa/parser/cxfa_driver.h b/xfa/fxfa/parser/cxfa_driver.h
index f58e153..c9859a0 100644
--- a/xfa/fxfa/parser/cxfa_driver.h
+++ b/xfa/fxfa/parser/cxfa_driver.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Driver final : public CXFA_Node {
  public:
-  CXFA_Driver(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Driver() override;
+
+ private:
+  CXFA_Driver(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DRIVER_H_
diff --git a/xfa/fxfa/parser/cxfa_dsigdata.cpp b/xfa/fxfa/parser/cxfa_dsigdata.cpp
index 19788db..01ccd4f 100644
--- a/xfa/fxfa/parser/cxfa_dsigdata.cpp
+++ b/xfa/fxfa/parser/cxfa_dsigdata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_dsigdata.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -20,11 +20,13 @@
 CXFA_DSigData::CXFA_DSigData(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::DSigData,
                 {},
                 kDSigDataAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_DSigData::~CXFA_DSigData() = default;
diff --git a/xfa/fxfa/parser/cxfa_dsigdata.h b/xfa/fxfa/parser/cxfa_dsigdata.h
index 06e691c..cea66a6 100644
--- a/xfa/fxfa/parser/cxfa_dsigdata.h
+++ b/xfa/fxfa/parser/cxfa_dsigdata.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_DSigData final : public CXFA_Node {
  public:
-  CXFA_DSigData(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_DSigData() override;
+
+ private:
+  CXFA_DSigData(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DSIGDATA_H_
diff --git a/xfa/fxfa/parser/cxfa_duplexoption.cpp b/xfa/fxfa/parser/cxfa_duplexoption.cpp
index 36feccd..794b0ac 100644
--- a/xfa/fxfa/parser/cxfa_duplexoption.cpp
+++ b/xfa/fxfa/parser/cxfa_duplexoption.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_duplexoption.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_DuplexOption::CXFA_DuplexOption(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::DuplexOption,
                 {},
                 kDuplexOptionAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_DuplexOption::~CXFA_DuplexOption() = default;
diff --git a/xfa/fxfa/parser/cxfa_duplexoption.h b/xfa/fxfa/parser/cxfa_duplexoption.h
index 5bf8e50..59bcaf8 100644
--- a/xfa/fxfa/parser/cxfa_duplexoption.h
+++ b/xfa/fxfa/parser/cxfa_duplexoption.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_DuplexOption final : public CXFA_Node {
  public:
-  CXFA_DuplexOption(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_DuplexOption() override;
+
+ private:
+  CXFA_DuplexOption(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DUPLEXOPTION_H_
diff --git a/xfa/fxfa/parser/cxfa_dynamicrender.cpp b/xfa/fxfa/parser/cxfa_dynamicrender.cpp
index 5326cec..bd034eb 100644
--- a/xfa/fxfa/parser/cxfa_dynamicrender.cpp
+++ b/xfa/fxfa/parser/cxfa_dynamicrender.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_dynamicrender.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                        XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::DynamicRender,
                 {},
                 kDynamicRenderAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_DynamicRender::~CXFA_DynamicRender() = default;
diff --git a/xfa/fxfa/parser/cxfa_dynamicrender.h b/xfa/fxfa/parser/cxfa_dynamicrender.h
index 78b75a7..7f75a6c 100644
--- a/xfa/fxfa/parser/cxfa_dynamicrender.h
+++ b/xfa/fxfa/parser/cxfa_dynamicrender.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_DynamicRender final : public CXFA_Node {
  public:
-  CXFA_DynamicRender(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_DynamicRender() override;
+
+ private:
+  CXFA_DynamicRender(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DYNAMICRENDER_H_
diff --git a/xfa/fxfa/parser/cxfa_edge.cpp b/xfa/fxfa/parser/cxfa_edge.cpp
index 36ca7b9..402087c 100644
--- a/xfa/fxfa/parser/cxfa_edge.cpp
+++ b/xfa/fxfa/parser/cxfa_edge.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_edge.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kEdgePropertyData[] = {
-    {XFA_Element::Color, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Color, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kEdgeAttributeData[] = {
@@ -34,11 +34,13 @@
 CXFA_Edge::CXFA_Edge(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Stroke(doc,
                   packet,
-                  (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                  {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                   XFA_ObjectType::Node,
                   XFA_Element::Edge,
                   kEdgePropertyData,
                   kEdgeAttributeData,
-                  pdfium::MakeUnique<CJX_Node>(this)) {}
+                  cppgc::MakeGarbageCollected<CJX_Node>(
+                      doc->GetHeap()->GetAllocationHandle(),
+                      this)) {}
 
 CXFA_Edge::~CXFA_Edge() = default;
diff --git a/xfa/fxfa/parser/cxfa_edge.h b/xfa/fxfa/parser/cxfa_edge.h
index 79c7f14..a9c2738 100644
--- a/xfa/fxfa/parser/cxfa_edge.h
+++ b/xfa/fxfa/parser/cxfa_edge.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,8 +13,11 @@
  public:
   static constexpr FX_ARGB kDefaultColor = 0xFF000000;
 
-  CXFA_Edge(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Edge() override;
+
+ private:
+  CXFA_Edge(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_EDGE_H_
diff --git a/xfa/fxfa/parser/cxfa_effectiveinputpolicy.cpp b/xfa/fxfa/parser/cxfa_effectiveinputpolicy.cpp
index f2f210d..6154b31 100644
--- a/xfa/fxfa/parser/cxfa_effectiveinputpolicy.cpp
+++ b/xfa/fxfa/parser/cxfa_effectiveinputpolicy.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_effectiveinputpolicy.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -24,11 +24,13 @@
                                                      XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_ConnectionSet,
+                XFA_XDPPACKET::kConnectionSet,
                 XFA_ObjectType::Node,
                 XFA_Element::EffectiveInputPolicy,
                 {},
                 kEffectiveInputPolicyAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_EffectiveInputPolicy::~CXFA_EffectiveInputPolicy() = default;
diff --git a/xfa/fxfa/parser/cxfa_effectiveinputpolicy.h b/xfa/fxfa/parser/cxfa_effectiveinputpolicy.h
index 4f4fb8e..2377330 100644
--- a/xfa/fxfa/parser/cxfa_effectiveinputpolicy.h
+++ b/xfa/fxfa/parser/cxfa_effectiveinputpolicy.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_EffectiveInputPolicy final : public CXFA_Node {
  public:
-  CXFA_EffectiveInputPolicy(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_EffectiveInputPolicy() override;
+
+ private:
+  CXFA_EffectiveInputPolicy(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_EFFECTIVEINPUTPOLICY_H_
diff --git a/xfa/fxfa/parser/cxfa_effectiveoutputpolicy.cpp b/xfa/fxfa/parser/cxfa_effectiveoutputpolicy.cpp
index 43e07ff..453546f 100644
--- a/xfa/fxfa/parser/cxfa_effectiveoutputpolicy.cpp
+++ b/xfa/fxfa/parser/cxfa_effectiveoutputpolicy.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_effectiveoutputpolicy.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -24,11 +24,13 @@
                                                        XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_ConnectionSet,
+                XFA_XDPPACKET::kConnectionSet,
                 XFA_ObjectType::Node,
                 XFA_Element::EffectiveOutputPolicy,
                 {},
                 kEffectiveOutputPolicyAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_EffectiveOutputPolicy::~CXFA_EffectiveOutputPolicy() = default;
diff --git a/xfa/fxfa/parser/cxfa_effectiveoutputpolicy.h b/xfa/fxfa/parser/cxfa_effectiveoutputpolicy.h
index 9b15a47..351a356 100644
--- a/xfa/fxfa/parser/cxfa_effectiveoutputpolicy.h
+++ b/xfa/fxfa/parser/cxfa_effectiveoutputpolicy.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_EffectiveOutputPolicy final : public CXFA_Node {
  public:
-  CXFA_EffectiveOutputPolicy(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_EffectiveOutputPolicy() override;
+
+ private:
+  CXFA_EffectiveOutputPolicy(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_EFFECTIVEOUTPUTPOLICY_H_
diff --git a/xfa/fxfa/parser/cxfa_embed.cpp b/xfa/fxfa/parser/cxfa_embed.cpp
index 0941b19..79fc3a6 100644
--- a/xfa/fxfa/parser/cxfa_embed.cpp
+++ b/xfa/fxfa/parser/cxfa_embed.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_embed.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Embed::CXFA_Embed(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Embed,
                 {},
                 kEmbedAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Embed::~CXFA_Embed() = default;
diff --git a/xfa/fxfa/parser/cxfa_embed.h b/xfa/fxfa/parser/cxfa_embed.h
index 8772b30..6421bd8 100644
--- a/xfa/fxfa/parser/cxfa_embed.h
+++ b/xfa/fxfa/parser/cxfa_embed.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Embed final : public CXFA_Node {
  public:
-  CXFA_Embed(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Embed() override;
+
+ private:
+  CXFA_Embed(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_EMBED_H_
diff --git a/xfa/fxfa/parser/cxfa_encoding.cpp b/xfa/fxfa/parser/cxfa_encoding.cpp
index a9330d6..5ab47ff 100644
--- a/xfa/fxfa/parser/cxfa_encoding.cpp
+++ b/xfa/fxfa/parser/cxfa_encoding.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_encoding.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
 CXFA_Encoding::CXFA_Encoding(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::NodeC,
                 XFA_Element::Encoding,
                 {},
                 kEncodingAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Encoding::~CXFA_Encoding() = default;
diff --git a/xfa/fxfa/parser/cxfa_encoding.h b/xfa/fxfa/parser/cxfa_encoding.h
index b98ea21..62a00fe 100644
--- a/xfa/fxfa/parser/cxfa_encoding.h
+++ b/xfa/fxfa/parser/cxfa_encoding.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Encoding final : public CXFA_Node {
  public:
-  CXFA_Encoding(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Encoding() override;
+
+ private:
+  CXFA_Encoding(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ENCODING_H_
diff --git a/xfa/fxfa/parser/cxfa_encodings.cpp b/xfa/fxfa/parser/cxfa_encodings.cpp
index 939cb53..735946a 100644
--- a/xfa/fxfa/parser/cxfa_encodings.cpp
+++ b/xfa/fxfa/parser/cxfa_encodings.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_encodings.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -24,11 +24,13 @@
 CXFA_Encodings::CXFA_Encodings(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Encodings,
                 {},
                 kEncodingsAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Encodings::~CXFA_Encodings() = default;
diff --git a/xfa/fxfa/parser/cxfa_encodings.h b/xfa/fxfa/parser/cxfa_encodings.h
index 3e8de09..5a3b528 100644
--- a/xfa/fxfa/parser/cxfa_encodings.h
+++ b/xfa/fxfa/parser/cxfa_encodings.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Encodings final : public CXFA_Node {
  public:
-  CXFA_Encodings(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Encodings() override;
+
+ private:
+  CXFA_Encodings(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ENCODINGS_H_
diff --git a/xfa/fxfa/parser/cxfa_encrypt.cpp b/xfa/fxfa/parser/cxfa_encrypt.cpp
index baef9ad..54035cd 100644
--- a/xfa/fxfa/parser/cxfa_encrypt.cpp
+++ b/xfa/fxfa/parser/cxfa_encrypt.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_encrypt.h"
 
 #include "fxjs/xfa/cjx_encrypt.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kEncryptPropertyData[] = {
-    {XFA_Element::Certificate, 1, 0},
+    {XFA_Element::Certificate, 1, {}},
 };
 
 const CXFA_Node::AttributeData kEncryptAttributeData[] = {
@@ -26,14 +26,16 @@
 }  // namespace
 
 CXFA_Encrypt::CXFA_Encrypt(CXFA_Document* doc, XFA_PacketType packet)
-    : CXFA_Node(
-          doc,
-          packet,
-          (XFA_XDPPACKET_Template | XFA_XDPPACKET_Config | XFA_XDPPACKET_Form),
-          XFA_ObjectType::ContentNode,
-          XFA_Element::Encrypt,
-          kEncryptPropertyData,
-          kEncryptAttributeData,
-          pdfium::MakeUnique<CJX_Encrypt>(this)) {}
+    : CXFA_Node(doc,
+                packet,
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kConfig,
+                 XFA_XDPPACKET::kForm},
+                XFA_ObjectType::ContentNode,
+                XFA_Element::Encrypt,
+                kEncryptPropertyData,
+                kEncryptAttributeData,
+                cppgc::MakeGarbageCollected<CJX_Encrypt>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Encrypt::~CXFA_Encrypt() = default;
diff --git a/xfa/fxfa/parser/cxfa_encrypt.h b/xfa/fxfa/parser/cxfa_encrypt.h
index afb74b4..4bfa905 100644
--- a/xfa/fxfa/parser/cxfa_encrypt.h
+++ b/xfa/fxfa/parser/cxfa_encrypt.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Encrypt final : public CXFA_Node {
  public:
-  CXFA_Encrypt(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Encrypt() override;
+
+ private:
+  CXFA_Encrypt(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ENCRYPT_H_
diff --git a/xfa/fxfa/parser/cxfa_encryption.cpp b/xfa/fxfa/parser/cxfa_encryption.cpp
index e730770..06d5fa3 100644
--- a/xfa/fxfa/parser/cxfa_encryption.cpp
+++ b/xfa/fxfa/parser/cxfa_encryption.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,14 @@
 #include "xfa/fxfa/parser/cxfa_encryption.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kEncryptionPropertyData[] = {
-    {XFA_Element::EncryptionLevel, 1, 0},
-    {XFA_Element::Encrypt, 1, 0},
-    {XFA_Element::Permissions, 1, 0},
+    {XFA_Element::EncryptionLevel, 1, {}},
+    {XFA_Element::Encrypt, 1, {}},
+    {XFA_Element::Permissions, 1, {}},
 };
 
 const CXFA_Node::AttributeData kEncryptionAttributeData[] = {
@@ -27,11 +27,13 @@
 CXFA_Encryption::CXFA_Encryption(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Encryption,
                 kEncryptionPropertyData,
                 kEncryptionAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Encryption::~CXFA_Encryption() = default;
diff --git a/xfa/fxfa/parser/cxfa_encryption.h b/xfa/fxfa/parser/cxfa_encryption.h
index 1bcf48f..73a2c7c 100644
--- a/xfa/fxfa/parser/cxfa_encryption.h
+++ b/xfa/fxfa/parser/cxfa_encryption.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Encryption final : public CXFA_Node {
  public:
-  CXFA_Encryption(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Encryption() override;
+
+ private:
+  CXFA_Encryption(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ENCRYPTION_H_
diff --git a/xfa/fxfa/parser/cxfa_encryptionlevel.cpp b/xfa/fxfa/parser/cxfa_encryptionlevel.cpp
index b9a182f..452637f 100644
--- a/xfa/fxfa/parser/cxfa_encryptionlevel.cpp
+++ b/xfa/fxfa/parser/cxfa_encryptionlevel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_encryptionlevel.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                            XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::EncryptionLevel,
                 {},
                 kEncryptionLevelAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_EncryptionLevel::~CXFA_EncryptionLevel() = default;
diff --git a/xfa/fxfa/parser/cxfa_encryptionlevel.h b/xfa/fxfa/parser/cxfa_encryptionlevel.h
index faf300b..d6d9ef1 100644
--- a/xfa/fxfa/parser/cxfa_encryptionlevel.h
+++ b/xfa/fxfa/parser/cxfa_encryptionlevel.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_EncryptionLevel final : public CXFA_Node {
  public:
-  CXFA_EncryptionLevel(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_EncryptionLevel() override;
+
+ private:
+  CXFA_EncryptionLevel(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ENCRYPTIONLEVEL_H_
diff --git a/xfa/fxfa/parser/cxfa_encryptionmethod.cpp b/xfa/fxfa/parser/cxfa_encryptionmethod.cpp
index 4cad94c..1a7eb0b 100644
--- a/xfa/fxfa/parser/cxfa_encryptionmethod.cpp
+++ b/xfa/fxfa/parser/cxfa_encryptionmethod.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_encryptionmethod.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
                                              XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::NodeC,
                 XFA_Element::EncryptionMethod,
                 {},
                 kEncryptionMethodAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_EncryptionMethod::~CXFA_EncryptionMethod() = default;
diff --git a/xfa/fxfa/parser/cxfa_encryptionmethod.h b/xfa/fxfa/parser/cxfa_encryptionmethod.h
index cae708f..7959033 100644
--- a/xfa/fxfa/parser/cxfa_encryptionmethod.h
+++ b/xfa/fxfa/parser/cxfa_encryptionmethod.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_EncryptionMethod final : public CXFA_Node {
  public:
-  CXFA_EncryptionMethod(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_EncryptionMethod() override;
+
+ private:
+  CXFA_EncryptionMethod(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ENCRYPTIONMETHOD_H_
diff --git a/xfa/fxfa/parser/cxfa_encryptionmethods.cpp b/xfa/fxfa/parser/cxfa_encryptionmethods.cpp
index 806cd75..795eafa 100644
--- a/xfa/fxfa/parser/cxfa_encryptionmethods.cpp
+++ b/xfa/fxfa/parser/cxfa_encryptionmethods.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_encryptionmethods.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -25,11 +25,13 @@
                                                XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::EncryptionMethods,
                 {},
                 kEncryptionMethodsAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_EncryptionMethods::~CXFA_EncryptionMethods() = default;
diff --git a/xfa/fxfa/parser/cxfa_encryptionmethods.h b/xfa/fxfa/parser/cxfa_encryptionmethods.h
index 825ee9f..1782c23 100644
--- a/xfa/fxfa/parser/cxfa_encryptionmethods.h
+++ b/xfa/fxfa/parser/cxfa_encryptionmethods.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_EncryptionMethods final : public CXFA_Node {
  public:
-  CXFA_EncryptionMethods(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_EncryptionMethods() override;
+
+ private:
+  CXFA_EncryptionMethods(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ENCRYPTIONMETHODS_H_
diff --git a/xfa/fxfa/parser/cxfa_enforce.cpp b/xfa/fxfa/parser/cxfa_enforce.cpp
index f8ce4a0..811cf7f 100644
--- a/xfa/fxfa/parser/cxfa_enforce.cpp
+++ b/xfa/fxfa/parser/cxfa_enforce.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_enforce.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Enforce::CXFA_Enforce(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Enforce,
                 {},
                 kEnforceAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Enforce::~CXFA_Enforce() = default;
diff --git a/xfa/fxfa/parser/cxfa_enforce.h b/xfa/fxfa/parser/cxfa_enforce.h
index ec7d745..d8da40a 100644
--- a/xfa/fxfa/parser/cxfa_enforce.h
+++ b/xfa/fxfa/parser/cxfa_enforce.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Enforce final : public CXFA_Node {
  public:
-  CXFA_Enforce(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Enforce() override;
+
+ private:
+  CXFA_Enforce(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ENFORCE_H_
diff --git a/xfa/fxfa/parser/cxfa_equate.cpp b/xfa/fxfa/parser/cxfa_equate.cpp
index 52a4108..894767e 100644
--- a/xfa/fxfa/parser/cxfa_equate.cpp
+++ b/xfa/fxfa/parser/cxfa_equate.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_equate.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -24,11 +24,13 @@
 CXFA_Equate::CXFA_Equate(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Equate,
                 {},
                 kEquateAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Equate::~CXFA_Equate() = default;
diff --git a/xfa/fxfa/parser/cxfa_equate.h b/xfa/fxfa/parser/cxfa_equate.h
index fc1f8d6..91a9a75 100644
--- a/xfa/fxfa/parser/cxfa_equate.h
+++ b/xfa/fxfa/parser/cxfa_equate.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Equate final : public CXFA_Node {
  public:
-  CXFA_Equate(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Equate() override;
+
+ private:
+  CXFA_Equate(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_EQUATE_H_
diff --git a/xfa/fxfa/parser/cxfa_equaterange.cpp b/xfa/fxfa/parser/cxfa_equaterange.cpp
index 4398c24..cc4bb42 100644
--- a/xfa/fxfa/parser/cxfa_equaterange.cpp
+++ b/xfa/fxfa/parser/cxfa_equaterange.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_equaterange.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -24,11 +24,13 @@
 CXFA_EquateRange::CXFA_EquateRange(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::EquateRange,
                 {},
                 kEquateRangeAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_EquateRange::~CXFA_EquateRange() = default;
diff --git a/xfa/fxfa/parser/cxfa_equaterange.h b/xfa/fxfa/parser/cxfa_equaterange.h
index 56f05e1..adc25a4 100644
--- a/xfa/fxfa/parser/cxfa_equaterange.h
+++ b/xfa/fxfa/parser/cxfa_equaterange.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_EquateRange final : public CXFA_Node {
  public:
-  CXFA_EquateRange(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_EquateRange() override;
+
+ private:
+  CXFA_EquateRange(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_EQUATERANGE_H_
diff --git a/xfa/fxfa/parser/cxfa_era.cpp b/xfa/fxfa/parser/cxfa_era.cpp
index 1cb8df2..92ae7a9 100644
--- a/xfa/fxfa/parser/cxfa_era.cpp
+++ b/xfa/fxfa/parser/cxfa_era.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,18 @@
 #include "xfa/fxfa/parser/cxfa_era.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 CXFA_Era::CXFA_Era(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Era,
                 {},
                 {},
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Era::~CXFA_Era() = default;
diff --git a/xfa/fxfa/parser/cxfa_era.h b/xfa/fxfa/parser/cxfa_era.h
index e0ecdf7..17572c4 100644
--- a/xfa/fxfa/parser/cxfa_era.h
+++ b/xfa/fxfa/parser/cxfa_era.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Era final : public CXFA_Node {
  public:
-  CXFA_Era(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Era() override;
+
+ private:
+  CXFA_Era(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ERA_H_
diff --git a/xfa/fxfa/parser/cxfa_eranames.cpp b/xfa/fxfa/parser/cxfa_eranames.cpp
index 391c5fa..e7fdb6e 100644
--- a/xfa/fxfa/parser/cxfa_eranames.cpp
+++ b/xfa/fxfa/parser/cxfa_eranames.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_eranames.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kEraNamesPropertyData[] = {
-    {XFA_Element::Era, 2, 0},
+    {XFA_Element::Era, 2, {}},
 };
 
 }  // namespace
@@ -20,11 +20,13 @@
 CXFA_EraNames::CXFA_EraNames(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::EraNames,
                 kEraNamesPropertyData,
                 {},
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_EraNames::~CXFA_EraNames() = default;
diff --git a/xfa/fxfa/parser/cxfa_eranames.h b/xfa/fxfa/parser/cxfa_eranames.h
index 468b658..27d9000 100644
--- a/xfa/fxfa/parser/cxfa_eranames.h
+++ b/xfa/fxfa/parser/cxfa_eranames.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_EraNames final : public CXFA_Node {
  public:
-  CXFA_EraNames(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_EraNames() override;
+
+ private:
+  CXFA_EraNames(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ERANAMES_H_
diff --git a/xfa/fxfa/parser/cxfa_event.cpp b/xfa/fxfa/parser/cxfa_event.cpp
index 0b109c1..bb53119 100644
--- a/xfa/fxfa/parser/cxfa_event.cpp
+++ b/xfa/fxfa/parser/cxfa_event.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,18 +7,18 @@
 #include "xfa/fxfa/parser/cxfa_event.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_script.h"
 #include "xfa/fxfa/parser/cxfa_submit.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kEventPropertyData[] = {
-    {XFA_Element::Execute, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Script, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::SignData, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Extras, 1, 0},
-    {XFA_Element::Submit, 1, XFA_PROPERTYFLAG_OneOf},
+    {XFA_Element::Execute, 1, XFA_PropertyFlag::kOneOf},
+    {XFA_Element::Script, 1, XFA_PropertyFlag::kOneOf},
+    {XFA_Element::SignData, 1, XFA_PropertyFlag::kOneOf},
+    {XFA_Element::Extras, 1, {}},
+    {XFA_Element::Submit, 1, XFA_PropertyFlag::kOneOf},
 };
 
 const CXFA_Node::AttributeData kEventAttributeData[] = {
@@ -38,12 +38,14 @@
 CXFA_Event::CXFA_Event(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Event,
                 kEventPropertyData,
                 kEventAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Event::~CXFA_Event() = default;
 
diff --git a/xfa/fxfa/parser/cxfa_event.h b/xfa/fxfa/parser/cxfa_event.h
index 73da7c2..7366061 100644
--- a/xfa/fxfa/parser/cxfa_event.h
+++ b/xfa/fxfa/parser/cxfa_event.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CXFA_Event final : public CXFA_Node {
  public:
-  CXFA_Event(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Event() override;
 
   XFA_AttributeValue GetActivity();
@@ -26,6 +26,9 @@
 #endif  // PDF_XFA_ELEMENT_SUBMIT_ENABLED
 
   WideString GetRef();
+
+ private:
+  CXFA_Event(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_EVENT_H_
diff --git a/xfa/fxfa/parser/cxfa_exclgroup.cpp b/xfa/fxfa/parser/cxfa_exclgroup.cpp
index f662224..0ea154c 100644
--- a/xfa/fxfa/parser/cxfa_exclgroup.cpp
+++ b/xfa/fxfa/parser/cxfa_exclgroup.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,17 +7,17 @@
 #include "xfa/fxfa/parser/cxfa_exclgroup.h"
 
 #include "fxjs/xfa/cjx_exclgroup.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kExclGroupPropertyData[] = {
-    {XFA_Element::Margin, 1, 0},    {XFA_Element::Para, 1, 0},
-    {XFA_Element::Border, 1, 0},    {XFA_Element::Assist, 1, 0},
-    {XFA_Element::Traversal, 1, 0}, {XFA_Element::Validate, 1, 0},
-    {XFA_Element::Caption, 1, 0},   {XFA_Element::Bind, 1, 0},
-    {XFA_Element::Desc, 1, 0},      {XFA_Element::Calculate, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Margin, 1, {}},    {XFA_Element::Para, 1, {}},
+    {XFA_Element::Border, 1, {}},    {XFA_Element::Assist, 1, {}},
+    {XFA_Element::Traversal, 1, {}}, {XFA_Element::Validate, 1, {}},
+    {XFA_Element::Caption, 1, {}},   {XFA_Element::Bind, 1, {}},
+    {XFA_Element::Desc, 1, {}},      {XFA_Element::Calculate, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kExclGroupAttributeData[] = {
@@ -55,11 +55,13 @@
 CXFA_ExclGroup::CXFA_ExclGroup(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::ExclGroup,
                 kExclGroupPropertyData,
                 kExclGroupAttributeData,
-                pdfium::MakeUnique<CJX_ExclGroup>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_ExclGroup>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ExclGroup::~CXFA_ExclGroup() = default;
diff --git a/xfa/fxfa/parser/cxfa_exclgroup.h b/xfa/fxfa/parser/cxfa_exclgroup.h
index 390bb4a..584d195 100644
--- a/xfa/fxfa/parser/cxfa_exclgroup.h
+++ b/xfa/fxfa/parser/cxfa_exclgroup.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_ExclGroup final : public CXFA_Node {
  public:
-  CXFA_ExclGroup(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ExclGroup() override;
+
+ private:
+  CXFA_ExclGroup(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_EXCLGROUP_H_
diff --git a/xfa/fxfa/parser/cxfa_exclude.cpp b/xfa/fxfa/parser/cxfa_exclude.cpp
index 006831e..ca279b2 100644
--- a/xfa/fxfa/parser/cxfa_exclude.cpp
+++ b/xfa/fxfa/parser/cxfa_exclude.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_exclude.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Exclude::CXFA_Exclude(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Exclude,
                 {},
                 kExcludeAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Exclude::~CXFA_Exclude() = default;
diff --git a/xfa/fxfa/parser/cxfa_exclude.h b/xfa/fxfa/parser/cxfa_exclude.h
index 71f08fd..a75eae2 100644
--- a/xfa/fxfa/parser/cxfa_exclude.h
+++ b/xfa/fxfa/parser/cxfa_exclude.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Exclude final : public CXFA_Node {
  public:
-  CXFA_Exclude(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Exclude() override;
+
+ private:
+  CXFA_Exclude(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_EXCLUDE_H_
diff --git a/xfa/fxfa/parser/cxfa_excludens.cpp b/xfa/fxfa/parser/cxfa_excludens.cpp
index 0d47359..0a100d3 100644
--- a/xfa/fxfa/parser/cxfa_excludens.cpp
+++ b/xfa/fxfa/parser/cxfa_excludens.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_excludens.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_ExcludeNS::CXFA_ExcludeNS(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::ExcludeNS,
                 {},
                 kExcludeNSAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ExcludeNS::~CXFA_ExcludeNS() = default;
diff --git a/xfa/fxfa/parser/cxfa_excludens.h b/xfa/fxfa/parser/cxfa_excludens.h
index 9ce2e69..d7ee240 100644
--- a/xfa/fxfa/parser/cxfa_excludens.h
+++ b/xfa/fxfa/parser/cxfa_excludens.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_ExcludeNS final : public CXFA_Node {
  public:
-  CXFA_ExcludeNS(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ExcludeNS() override;
+
+ private:
+  CXFA_ExcludeNS(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_EXCLUDENS_H_
diff --git a/xfa/fxfa/parser/cxfa_exdata.cpp b/xfa/fxfa/parser/cxfa_exdata.cpp
index 1c2e856..1a05c68 100644
--- a/xfa/fxfa/parser/cxfa_exdata.cpp
+++ b/xfa/fxfa/parser/cxfa_exdata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_exdata.h"
 
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -26,18 +26,27 @@
 
 }  // namespace
 
+// static
+CXFA_ExData* CXFA_ExData::FromNode(CXFA_Node* pNode) {
+  return pNode && pNode->GetElementType() == XFA_Element::ExData
+             ? static_cast<CXFA_ExData*>(pNode)
+             : nullptr;
+}
+
 CXFA_ExData::CXFA_ExData(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContentNode,
                 XFA_Element::ExData,
                 {},
                 kExDataAttributeData,
-                pdfium::MakeUnique<CJX_Object>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Object>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ExData::~CXFA_ExData() = default;
 
 void CXFA_ExData::SetContentType(const WideString& wsContentType) {
-  JSObject()->SetCData(XFA_Attribute::ContentType, wsContentType, false, false);
+  JSObject()->SetCData(XFA_Attribute::ContentType, wsContentType);
 }
diff --git a/xfa/fxfa/parser/cxfa_exdata.h b/xfa/fxfa/parser/cxfa_exdata.h
index 8adf028..22aad55 100644
--- a/xfa/fxfa/parser/cxfa_exdata.h
+++ b/xfa/fxfa/parser/cxfa_exdata.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,10 +11,15 @@
 
 class CXFA_ExData final : public CXFA_Node {
  public:
-  CXFA_ExData(CXFA_Document* doc, XFA_PacketType packet);
+  static CXFA_ExData* FromNode(CXFA_Node* pNode);
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ExData() override;
 
   void SetContentType(const WideString& wsContentType);
+
+ private:
+  CXFA_ExData(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_EXDATA_H_
diff --git a/xfa/fxfa/parser/cxfa_execute.cpp b/xfa/fxfa/parser/cxfa_execute.cpp
index c89bd70..e822ba8 100644
--- a/xfa/fxfa/parser/cxfa_execute.cpp
+++ b/xfa/fxfa/parser/cxfa_execute.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_execute.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -27,11 +27,13 @@
 CXFA_Execute::CXFA_Execute(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Execute,
                 {},
                 kExecuteAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Execute::~CXFA_Execute() = default;
diff --git a/xfa/fxfa/parser/cxfa_execute.h b/xfa/fxfa/parser/cxfa_execute.h
index 63fd18b..430043e 100644
--- a/xfa/fxfa/parser/cxfa_execute.h
+++ b/xfa/fxfa/parser/cxfa_execute.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Execute final : public CXFA_Node {
  public:
-  CXFA_Execute(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Execute() override;
+
+ private:
+  CXFA_Execute(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_EXECUTE_H_
diff --git a/xfa/fxfa/parser/cxfa_exobject.cpp b/xfa/fxfa/parser/cxfa_exobject.cpp
index 86a7409..d96b33d 100644
--- a/xfa/fxfa/parser/cxfa_exobject.cpp
+++ b/xfa/fxfa/parser/cxfa_exobject.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_exobject.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kExObjectPropertyData[] = {
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kExObjectAttributeData[] = {
@@ -31,11 +31,13 @@
 CXFA_ExObject::CXFA_ExObject(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::ExObject,
                 kExObjectPropertyData,
                 kExObjectAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ExObject::~CXFA_ExObject() = default;
diff --git a/xfa/fxfa/parser/cxfa_exobject.h b/xfa/fxfa/parser/cxfa_exobject.h
index 79ffdb9..a66f903 100644
--- a/xfa/fxfa/parser/cxfa_exobject.h
+++ b/xfa/fxfa/parser/cxfa_exobject.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_ExObject final : public CXFA_Node {
  public:
-  CXFA_ExObject(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ExObject() override;
+
+ private:
+  CXFA_ExObject(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_EXOBJECT_H_
diff --git a/xfa/fxfa/parser/cxfa_extras.cpp b/xfa/fxfa/parser/cxfa_extras.cpp
index b23a689..9dd4adb 100644
--- a/xfa/fxfa/parser/cxfa_extras.cpp
+++ b/xfa/fxfa/parser/cxfa_extras.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_extras.h"
 
 #include "fxjs/xfa/cjx_extras.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,12 +23,14 @@
 CXFA_Extras::CXFA_Extras(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_SourceSet | XFA_XDPPACKET_Template |
-                 XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kSourceSet, XFA_XDPPACKET::kTemplate,
+                 XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Extras,
                 {},
                 kExtrasAttributeData,
-                pdfium::MakeUnique<CJX_Extras>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Extras>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Extras::~CXFA_Extras() = default;
diff --git a/xfa/fxfa/parser/cxfa_extras.h b/xfa/fxfa/parser/cxfa_extras.h
index 82108d0..886c3d3 100644
--- a/xfa/fxfa/parser/cxfa_extras.h
+++ b/xfa/fxfa/parser/cxfa_extras.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Extras final : public CXFA_Node {
  public:
-  CXFA_Extras(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Extras() override;
+
+ private:
+  CXFA_Extras(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_EXTRAS_H_
diff --git a/xfa/fxfa/parser/cxfa_field.cpp b/xfa/fxfa/parser/cxfa_field.cpp
index e33dfa4..7f0ece8 100644
--- a/xfa/fxfa/parser/cxfa_field.cpp
+++ b/xfa/fxfa/parser/cxfa_field.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,20 +7,20 @@
 #include "xfa/fxfa/parser/cxfa_field.h"
 
 #include "fxjs/xfa/cjx_field.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kFieldPropertyData[] = {
-    {XFA_Element::Ui, 1, 0},        {XFA_Element::Margin, 1, 0},
-    {XFA_Element::Para, 1, 0},      {XFA_Element::Format, 1, 0},
-    {XFA_Element::Border, 1, 0},    {XFA_Element::Assist, 1, 0},
-    {XFA_Element::Traversal, 1, 0}, {XFA_Element::Keep, 1, 0},
-    {XFA_Element::Validate, 1, 0},  {XFA_Element::Caption, 1, 0},
-    {XFA_Element::Bind, 1, 0},      {XFA_Element::Desc, 1, 0},
-    {XFA_Element::Font, 1, 0},      {XFA_Element::Value, 1, 0},
-    {XFA_Element::Calculate, 1, 0}, {XFA_Element::Extras, 1, 0},
-    {XFA_Element::Items, 2, 0},
+    {XFA_Element::Ui, 1, {}},        {XFA_Element::Margin, 1, {}},
+    {XFA_Element::Para, 1, {}},      {XFA_Element::Format, 1, {}},
+    {XFA_Element::Border, 1, {}},    {XFA_Element::Assist, 1, {}},
+    {XFA_Element::Traversal, 1, {}}, {XFA_Element::Keep, 1, {}},
+    {XFA_Element::Validate, 1, {}},  {XFA_Element::Caption, 1, {}},
+    {XFA_Element::Bind, 1, {}},      {XFA_Element::Desc, 1, {}},
+    {XFA_Element::Font, 1, {}},      {XFA_Element::Value, 1, {}},
+    {XFA_Element::Calculate, 1, {}}, {XFA_Element::Extras, 1, {}},
+    {XFA_Element::Items, 2, {}},
 };
 
 const CXFA_Node::AttributeData kFieldAttributeData[] = {
@@ -58,11 +58,13 @@
 CXFA_Field::CXFA_Field(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::Field,
                 kFieldPropertyData,
                 kFieldAttributeData,
-                pdfium::MakeUnique<CJX_Field>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Field>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Field::~CXFA_Field() = default;
diff --git a/xfa/fxfa/parser/cxfa_field.h b/xfa/fxfa/parser/cxfa_field.h
index e29cdd7..2de96b6 100644
--- a/xfa/fxfa/parser/cxfa_field.h
+++ b/xfa/fxfa/parser/cxfa_field.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Field final : public CXFA_Node {
  public:
-  CXFA_Field(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Field() override;
+
+ private:
+  CXFA_Field(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_FIELD_H_
diff --git a/xfa/fxfa/parser/cxfa_fill.cpp b/xfa/fxfa/parser/cxfa_fill.cpp
index 5bc8b02..044ba75 100644
--- a/xfa/fxfa/parser/cxfa_fill.cpp
+++ b/xfa/fxfa/parser/cxfa_fill.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,10 @@
 
 #include "xfa/fxfa/parser/cxfa_fill.h"
 
-#include "core/fxge/render_defines.h"
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
 #include "xfa/fxfa/parser/cxfa_color.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_linear.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_pattern.h"
@@ -19,14 +19,15 @@
 namespace {
 
 const CXFA_Node::PropertyData kFillPropertyData[] = {
-    {XFA_Element::Pattern, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Solid, 1,
-     XFA_PROPERTYFLAG_OneOf | XFA_PROPERTYFLAG_DefaultOneOf},
-    {XFA_Element::Stipple, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Color, 1, 0},
-    {XFA_Element::Linear, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Extras, 1, 0},
-    {XFA_Element::Radial, 1, XFA_PROPERTYFLAG_OneOf},
+    {XFA_Element::Pattern, 1, {XFA_PropertyFlag::kOneOf}},
+    {XFA_Element::Solid,
+     1,
+     {XFA_PropertyFlag::kOneOf, XFA_PropertyFlag::kDefaultOneOf}},
+    {XFA_Element::Stipple, 1, {XFA_PropertyFlag::kOneOf}},
+    {XFA_Element::Color, 1, {}},
+    {XFA_Element::Linear, 1, {XFA_PropertyFlag::kOneOf}},
+    {XFA_Element::Extras, 1, {}},
+    {XFA_Element::Radial, 1, {XFA_PropertyFlag::kOneOf}},
 };
 
 const CXFA_Node::AttributeData kFillAttributeData[] = {
@@ -42,12 +43,14 @@
 CXFA_Fill::CXFA_Fill(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Fill,
                 kFillPropertyData,
                 kFillAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Fill::~CXFA_Fill() = default;
 
@@ -67,11 +70,14 @@
   pColor->SetValue(color);
 }
 
-FX_ARGB CXFA_Fill::GetColor(bool bText) {
-  CXFA_Color* pColor = GetChild<CXFA_Color>(0, XFA_Element::Color, false);
-  if (!pColor)
-    return bText ? 0xFF000000 : 0xFFFFFFFF;
-  return pColor->GetValueOrDefault(bText ? 0xFF000000 : 0xFFFFFFFF);
+FX_ARGB CXFA_Fill::GetFillColor() const {
+  const auto* pColor = GetChild<CXFA_Color>(0, XFA_Element::Color, false);
+  return pColor ? pColor->GetValueOrDefault(0xFFFFFFFF) : 0xFFFFFFFF;
+}
+
+FX_ARGB CXFA_Fill::GetTextColor() const {
+  const auto* pColor = GetChild<CXFA_Color>(0, XFA_Element::Color, false);
+  return pColor ? pColor->GetValueOrDefault(0xFF000000) : 0xFF000000;
 }
 
 XFA_Element CXFA_Fill::GetType() const {
@@ -86,12 +92,11 @@
   return XFA_Element::Solid;
 }
 
-void CXFA_Fill::Draw(CXFA_Graphics* pGS,
-                     CXFA_GEPath* fillPath,
+void CXFA_Fill::Draw(CFGAS_GEGraphics* pGS,
+                     const CFGAS_GEPath& fillPath,
                      const CFX_RectF& rtWidget,
                      const CFX_Matrix& matrix) {
-  pGS->SaveGraphState();
-
+  CFGAS_GEGraphics::StateRestorer restorer(pGS);
   switch (GetType()) {
     case XFA_Element::Radial:
       DrawRadial(pGS, fillPath, rtWidget, matrix);
@@ -106,16 +111,15 @@
       DrawStipple(pGS, fillPath, rtWidget, matrix);
       break;
     default:
-      pGS->SetFillColor(CXFA_GEColor(GetColor(false)));
-      pGS->FillPath(fillPath, FXFILL_WINDING, &matrix);
+      pGS->SetFillColor(CFGAS_GEColor(GetFillColor()));
+      pGS->FillPath(fillPath, CFX_FillRenderOptions::FillType::kWinding,
+                    matrix);
       break;
   }
-
-  pGS->RestoreGraphState();
 }
 
-void CXFA_Fill::DrawStipple(CXFA_Graphics* pGS,
-                            CXFA_GEPath* fillPath,
+void CXFA_Fill::DrawStipple(CFGAS_GEGraphics* pGS,
+                            const CFGAS_GEPath& fillPath,
                             const CFX_RectF& rtWidget,
                             const CFX_Matrix& matrix) {
   CXFA_Stipple* stipple =
@@ -124,32 +128,32 @@
     stipple->Draw(pGS, fillPath, rtWidget, matrix);
 }
 
-void CXFA_Fill::DrawRadial(CXFA_Graphics* pGS,
-                           CXFA_GEPath* fillPath,
+void CXFA_Fill::DrawRadial(CFGAS_GEGraphics* pGS,
+                           const CFGAS_GEPath& fillPath,
                            const CFX_RectF& rtWidget,
                            const CFX_Matrix& matrix) {
   CXFA_Radial* radial =
       JSObject()->GetOrCreateProperty<CXFA_Radial>(0, XFA_Element::Radial);
   if (radial)
-    radial->Draw(pGS, fillPath, GetColor(false), rtWidget, matrix);
+    radial->Draw(pGS, fillPath, GetFillColor(), rtWidget, matrix);
 }
 
-void CXFA_Fill::DrawLinear(CXFA_Graphics* pGS,
-                           CXFA_GEPath* fillPath,
+void CXFA_Fill::DrawLinear(CFGAS_GEGraphics* pGS,
+                           const CFGAS_GEPath& fillPath,
                            const CFX_RectF& rtWidget,
                            const CFX_Matrix& matrix) {
   CXFA_Linear* linear =
       JSObject()->GetOrCreateProperty<CXFA_Linear>(0, XFA_Element::Linear);
   if (linear)
-    linear->Draw(pGS, fillPath, GetColor(false), rtWidget, matrix);
+    linear->Draw(pGS, fillPath, GetFillColor(), rtWidget, matrix);
 }
 
-void CXFA_Fill::DrawPattern(CXFA_Graphics* pGS,
-                            CXFA_GEPath* fillPath,
+void CXFA_Fill::DrawPattern(CFGAS_GEGraphics* pGS,
+                            const CFGAS_GEPath& fillPath,
                             const CFX_RectF& rtWidget,
                             const CFX_Matrix& matrix) {
   CXFA_Pattern* pattern =
       JSObject()->GetOrCreateProperty<CXFA_Pattern>(0, XFA_Element::Pattern);
   if (pattern)
-    pattern->Draw(pGS, fillPath, GetColor(false), rtWidget, matrix);
+    pattern->Draw(pGS, fillPath, GetFillColor(), rtWidget, matrix);
 }
diff --git a/xfa/fxfa/parser/cxfa_fill.h b/xfa/fxfa/parser/cxfa_fill.h
index 05fede9..8f4bf62 100644
--- a/xfa/fxfa/parser/cxfa_fill.h
+++ b/xfa/fxfa/parser/cxfa_fill.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,48 +8,47 @@
 #define XFA_FXFA_PARSER_CXFA_FILL_H_
 
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
 
-class CXFA_Graphics;
-class CXFA_Linear;
-class CXFA_Pattern;
-class CXFA_Radial;
-class CXFA_Stipple;
+class CFGAS_GEGraphics;
 
 class CXFA_Fill final : public CXFA_Node {
  public:
-  CXFA_Fill(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Fill() override;
 
   bool IsVisible();
 
-  FX_ARGB GetColor(bool bText);
+  FX_ARGB GetFillColor() const;
+  FX_ARGB GetTextColor() const;
   void SetColor(FX_ARGB color);
 
-  void Draw(CXFA_Graphics* pGS,
-            CXFA_GEPath* fillPath,
+  void Draw(CFGAS_GEGraphics* pGS,
+            const CFGAS_GEPath& fillPath,
             const CFX_RectF& rtWidget,
             const CFX_Matrix& matrix);
 
  private:
+  CXFA_Fill(CXFA_Document* doc, XFA_PacketType packet);
+
   XFA_Element GetType() const;
 
-  void DrawStipple(CXFA_Graphics* pGS,
-                   CXFA_GEPath* fillPath,
+  void DrawStipple(CFGAS_GEGraphics* pGS,
+                   const CFGAS_GEPath& fillPath,
                    const CFX_RectF& rtWidget,
                    const CFX_Matrix& matrix);
-  void DrawRadial(CXFA_Graphics* pGS,
-                  CXFA_GEPath* fillPath,
+  void DrawRadial(CFGAS_GEGraphics* pGS,
+                  const CFGAS_GEPath& fillPath,
                   const CFX_RectF& rtWidget,
                   const CFX_Matrix& matrix);
-  void DrawLinear(CXFA_Graphics* pGS,
-                  CXFA_GEPath* fillPath,
+  void DrawLinear(CFGAS_GEGraphics* pGS,
+                  const CFGAS_GEPath& fillPath,
                   const CFX_RectF& rtWidget,
                   const CFX_Matrix& matrix);
-  void DrawPattern(CXFA_Graphics* pGS,
-                   CXFA_GEPath* fillPath,
+  void DrawPattern(CFGAS_GEGraphics* pGS,
+                   const CFGAS_GEPath& fillPath,
                    const CFX_RectF& rtWidget,
                    const CFX_Matrix& matrix);
 };
diff --git a/xfa/fxfa/parser/cxfa_filter.cpp b/xfa/fxfa/parser/cxfa_filter.cpp
index fdd2960..99c3511 100644
--- a/xfa/fxfa/parser/cxfa_filter.cpp
+++ b/xfa/fxfa/parser/cxfa_filter.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,16 @@
 #include "xfa/fxfa/parser/cxfa_filter.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kFilterPropertyData[] = {
-    {XFA_Element::Mdp, 1, 0},           {XFA_Element::Certificates, 1, 0},
-    {XFA_Element::TimeStamp, 1, 0},     {XFA_Element::Handler, 1, 0},
-    {XFA_Element::DigestMethods, 1, 0}, {XFA_Element::Encodings, 1, 0},
-    {XFA_Element::Reasons, 1, 0},       {XFA_Element::AppearanceFilter, 1, 0},
-    {XFA_Element::LockDocument, 1, 0},
+    {XFA_Element::Mdp, 1, {}},           {XFA_Element::Certificates, 1, {}},
+    {XFA_Element::TimeStamp, 1, {}},     {XFA_Element::Handler, 1, {}},
+    {XFA_Element::DigestMethods, 1, {}}, {XFA_Element::Encodings, 1, {}},
+    {XFA_Element::Reasons, 1, {}},       {XFA_Element::AppearanceFilter, 1, {}},
+    {XFA_Element::LockDocument, 1, {}},
 };
 
 const CXFA_Node::AttributeData kFilterAttributeData[] = {
@@ -33,11 +33,13 @@
 CXFA_Filter::CXFA_Filter(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Filter,
                 kFilterPropertyData,
                 kFilterAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Filter::~CXFA_Filter() = default;
diff --git a/xfa/fxfa/parser/cxfa_filter.h b/xfa/fxfa/parser/cxfa_filter.h
index 033d97c..be87a6c 100644
--- a/xfa/fxfa/parser/cxfa_filter.h
+++ b/xfa/fxfa/parser/cxfa_filter.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Filter final : public CXFA_Node {
  public:
-  CXFA_Filter(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Filter() override;
+
+ private:
+  CXFA_Filter(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_FILTER_H_
diff --git a/xfa/fxfa/parser/cxfa_fliplabel.cpp b/xfa/fxfa/parser/cxfa_fliplabel.cpp
index 8780e87..42124f1 100644
--- a/xfa/fxfa/parser/cxfa_fliplabel.cpp
+++ b/xfa/fxfa/parser/cxfa_fliplabel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_fliplabel.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_FlipLabel::CXFA_FlipLabel(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::FlipLabel,
                 {},
                 kFlipLabelAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_FlipLabel::~CXFA_FlipLabel() = default;
diff --git a/xfa/fxfa/parser/cxfa_fliplabel.h b/xfa/fxfa/parser/cxfa_fliplabel.h
index e3f8165..ef43f37 100644
--- a/xfa/fxfa/parser/cxfa_fliplabel.h
+++ b/xfa/fxfa/parser/cxfa_fliplabel.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_FlipLabel final : public CXFA_Node {
  public:
-  CXFA_FlipLabel(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FlipLabel() override;
+
+ private:
+  CXFA_FlipLabel(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_FLIPLABEL_H_
diff --git a/xfa/fxfa/parser/cxfa_float.cpp b/xfa/fxfa/parser/cxfa_float.cpp
index a17341f..02f4e8e 100644
--- a/xfa/fxfa/parser/cxfa_float.cpp
+++ b/xfa/fxfa/parser/cxfa_float.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_float.h"
 
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_Float::CXFA_Float(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Float,
                 {},
                 kFloatAttributeData,
-                pdfium::MakeUnique<CJX_Object>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Object>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Float::~CXFA_Float() = default;
diff --git a/xfa/fxfa/parser/cxfa_float.h b/xfa/fxfa/parser/cxfa_float.h
index a3fc1ae..3a78987 100644
--- a/xfa/fxfa/parser/cxfa_float.h
+++ b/xfa/fxfa/parser/cxfa_float.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Float final : public CXFA_Node {
  public:
-  CXFA_Float(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Float() override;
+
+ private:
+  CXFA_Float(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_FLOAT_H_
diff --git a/xfa/fxfa/parser/cxfa_font.cpp b/xfa/fxfa/parser/cxfa_font.cpp
index 70e8426..77335e8 100644
--- a/xfa/fxfa/parser/cxfa_font.cpp
+++ b/xfa/fxfa/parser/cxfa_font.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #include "xfa/fxfa/parser/cxfa_font.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_fill.h"
 #include "xfa/fxfa/parser/cxfa_measurement.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kFontPropertyData[] = {
-    {XFA_Element::Fill, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Fill, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kFontAttributeData[] = {
@@ -53,15 +53,17 @@
 }  // namespace
 
 CXFA_Font::CXFA_Font(CXFA_Document* doc, XFA_PacketType packet)
-    : CXFA_Node(
-          doc,
-          packet,
-          (XFA_XDPPACKET_Template | XFA_XDPPACKET_Config | XFA_XDPPACKET_Form),
-          XFA_ObjectType::Node,
-          XFA_Element::Font,
-          kFontPropertyData,
-          kFontAttributeData,
-          pdfium::MakeUnique<CJX_Node>(this)) {}
+    : CXFA_Node(doc,
+                packet,
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kConfig,
+                 XFA_XDPPACKET::kForm},
+                XFA_ObjectType::Node,
+                XFA_Element::Font,
+                kFontPropertyData,
+                kFontAttributeData,
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Font::~CXFA_Font() = default;
 
@@ -130,7 +132,7 @@
   node->SetColor(color);
 }
 
-FX_ARGB CXFA_Font::GetColor() {
-  CXFA_Fill* fill = GetChild<CXFA_Fill>(0, XFA_Element::Fill, false);
-  return fill ? fill->GetColor(true) : 0xFF000000;
+FX_ARGB CXFA_Font::GetColor() const {
+  const auto* fill = GetChild<CXFA_Fill>(0, XFA_Element::Fill, false);
+  return fill ? fill->GetTextColor() : 0xFF000000;
 }
diff --git a/xfa/fxfa/parser/cxfa_font.h b/xfa/fxfa/parser/cxfa_font.h
index e292ed5..bc4ed1d 100644
--- a/xfa/fxfa/parser/cxfa_font.h
+++ b/xfa/fxfa/parser/cxfa_font.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #ifndef XFA_FXFA_PARSER_CXFA_FONT_H_
 #define XFA_FXFA_PARSER_CXFA_FONT_H_
 
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
 class CXFA_Font final : public CXFA_Node {
  public:
-  CXFA_Font(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Font() override;
 
   float GetBaselineShift() const;
@@ -28,8 +28,11 @@
   bool IsBold();
   bool IsItalic();
 
-  FX_ARGB GetColor();
+  FX_ARGB GetColor() const;
   void SetColor(FX_ARGB color);
+
+ private:
+  CXFA_Font(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_FONT_H_
diff --git a/xfa/fxfa/parser/cxfa_fontinfo.cpp b/xfa/fxfa/parser/cxfa_fontinfo.cpp
index 8ec5e93..e41dfbe 100644
--- a/xfa/fxfa/parser/cxfa_fontinfo.cpp
+++ b/xfa/fxfa/parser/cxfa_fontinfo.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,14 @@
 #include "xfa/fxfa/parser/cxfa_fontinfo.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kFontInfoPropertyData[] = {
-    {XFA_Element::SubsetBelow, 1, 0},
-    {XFA_Element::Map, 1, 0},
-    {XFA_Element::Embed, 1, 0},
+    {XFA_Element::SubsetBelow, 1, {}},
+    {XFA_Element::Map, 1, {}},
+    {XFA_Element::Embed, 1, {}},
 };
 
 const CXFA_Node::AttributeData kFontInfoAttributeData[] = {
@@ -27,11 +27,13 @@
 CXFA_FontInfo::CXFA_FontInfo(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::FontInfo,
                 kFontInfoPropertyData,
                 kFontInfoAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_FontInfo::~CXFA_FontInfo() = default;
diff --git a/xfa/fxfa/parser/cxfa_fontinfo.h b/xfa/fxfa/parser/cxfa_fontinfo.h
index b798c55..9b26f5a 100644
--- a/xfa/fxfa/parser/cxfa_fontinfo.h
+++ b/xfa/fxfa/parser/cxfa_fontinfo.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_FontInfo final : public CXFA_Node {
  public:
-  CXFA_FontInfo(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FontInfo() override;
+
+ private:
+  CXFA_FontInfo(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_FONTINFO_H_
diff --git a/xfa/fxfa/parser/cxfa_form.cpp b/xfa/fxfa/parser/cxfa_form.cpp
index 4586a29..18a3ca1 100644
--- a/xfa/fxfa/parser/cxfa_form.cpp
+++ b/xfa/fxfa/parser/cxfa_form.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_form.h"
 
 #include "fxjs/xfa/cjx_form.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -20,11 +20,13 @@
 CXFA_Form::CXFA_Form(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Form,
+                XFA_XDPPACKET::kForm,
                 XFA_ObjectType::ModelNode,
                 XFA_Element::Form,
                 {},
                 kFormAttributeData,
-                pdfium::MakeUnique<CJX_Form>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Form>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Form::~CXFA_Form() = default;
diff --git a/xfa/fxfa/parser/cxfa_form.h b/xfa/fxfa/parser/cxfa_form.h
index a0e2f32..01238bb 100644
--- a/xfa/fxfa/parser/cxfa_form.h
+++ b/xfa/fxfa/parser/cxfa_form.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Form final : public CXFA_Node {
  public:
-  CXFA_Form(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Form() override;
+
+ private:
+  CXFA_Form(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_FORM_H_
diff --git a/xfa/fxfa/parser/cxfa_format.cpp b/xfa/fxfa/parser/cxfa_format.cpp
index 9e84a6f..f1b2b76 100644
--- a/xfa/fxfa/parser/cxfa_format.cpp
+++ b/xfa/fxfa/parser/cxfa_format.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_format.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kFormatPropertyData[] = {
-    {XFA_Element::Picture, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Picture, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kFormatAttributeData[] = {
@@ -27,11 +27,13 @@
 CXFA_Format::CXFA_Format(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Format,
                 kFormatPropertyData,
                 kFormatAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Format::~CXFA_Format() = default;
diff --git a/xfa/fxfa/parser/cxfa_format.h b/xfa/fxfa/parser/cxfa_format.h
index 7e74a1c..37b9b38 100644
--- a/xfa/fxfa/parser/cxfa_format.h
+++ b/xfa/fxfa/parser/cxfa_format.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Format final : public CXFA_Node {
  public:
-  CXFA_Format(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Format() override;
+
+ private:
+  CXFA_Format(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_FORMAT_H_
diff --git a/xfa/fxfa/parser/cxfa_formfieldfilling.cpp b/xfa/fxfa/parser/cxfa_formfieldfilling.cpp
index 6dc5af4..5644793 100644
--- a/xfa/fxfa/parser/cxfa_formfieldfilling.cpp
+++ b/xfa/fxfa/parser/cxfa_formfieldfilling.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_formfieldfilling.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                              XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::FormFieldFilling,
                 {},
                 kFormFieldFillingAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_FormFieldFilling::~CXFA_FormFieldFilling() = default;
diff --git a/xfa/fxfa/parser/cxfa_formfieldfilling.h b/xfa/fxfa/parser/cxfa_formfieldfilling.h
index f9e7447..c2f2c10 100644
--- a/xfa/fxfa/parser/cxfa_formfieldfilling.h
+++ b/xfa/fxfa/parser/cxfa_formfieldfilling.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_FormFieldFilling final : public CXFA_Node {
  public:
-  CXFA_FormFieldFilling(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FormFieldFilling() override;
+
+ private:
+  CXFA_FormFieldFilling(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_FORMFIELDFILLING_H_
diff --git a/xfa/fxfa/parser/cxfa_groupparent.cpp b/xfa/fxfa/parser/cxfa_groupparent.cpp
index 8dd780c..88bdea6 100644
--- a/xfa/fxfa/parser/cxfa_groupparent.cpp
+++ b/xfa/fxfa/parser/cxfa_groupparent.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_groupparent.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_GroupParent::CXFA_GroupParent(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::GroupParent,
                 {},
                 kGroupParentAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_GroupParent::~CXFA_GroupParent() = default;
diff --git a/xfa/fxfa/parser/cxfa_groupparent.h b/xfa/fxfa/parser/cxfa_groupparent.h
index eb63a6a..016c3d4 100644
--- a/xfa/fxfa/parser/cxfa_groupparent.h
+++ b/xfa/fxfa/parser/cxfa_groupparent.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_GroupParent final : public CXFA_Node {
  public:
-  CXFA_GroupParent(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_GroupParent() override;
+
+ private:
+  CXFA_GroupParent(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_GROUPPARENT_H_
diff --git a/xfa/fxfa/parser/cxfa_handler.cpp b/xfa/fxfa/parser/cxfa_handler.cpp
index 88c6ac8..c54f530 100644
--- a/xfa/fxfa/parser/cxfa_handler.cpp
+++ b/xfa/fxfa/parser/cxfa_handler.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_handler.h"
 
 #include "fxjs/xfa/cjx_handler.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -24,11 +24,13 @@
 CXFA_Handler::CXFA_Handler(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::TextNode,
                 XFA_Element::Handler,
                 {},
                 kHandlerAttributeData,
-                pdfium::MakeUnique<CJX_Handler>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Handler>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Handler::~CXFA_Handler() = default;
diff --git a/xfa/fxfa/parser/cxfa_handler.h b/xfa/fxfa/parser/cxfa_handler.h
index 1ab1c4d..70e5aa5 100644
--- a/xfa/fxfa/parser/cxfa_handler.h
+++ b/xfa/fxfa/parser/cxfa_handler.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Handler final : public CXFA_Node {
  public:
-  CXFA_Handler(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Handler() override;
+
+ private:
+  CXFA_Handler(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_HANDLER_H_
diff --git a/xfa/fxfa/parser/cxfa_hyphenation.cpp b/xfa/fxfa/parser/cxfa_hyphenation.cpp
index 41d77f4..fe116a9 100644
--- a/xfa/fxfa/parser/cxfa_hyphenation.cpp
+++ b/xfa/fxfa/parser/cxfa_hyphenation.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_hyphenation.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -28,11 +28,13 @@
 CXFA_Hyphenation::CXFA_Hyphenation(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Hyphenation,
                 {},
                 kHyphenationAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Hyphenation::~CXFA_Hyphenation() = default;
diff --git a/xfa/fxfa/parser/cxfa_hyphenation.h b/xfa/fxfa/parser/cxfa_hyphenation.h
index 77b49e9..f8d142d 100644
--- a/xfa/fxfa/parser/cxfa_hyphenation.h
+++ b/xfa/fxfa/parser/cxfa_hyphenation.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Hyphenation final : public CXFA_Node {
  public:
-  CXFA_Hyphenation(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Hyphenation() override;
+
+ private:
+  CXFA_Hyphenation(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_HYPHENATION_H_
diff --git a/xfa/fxfa/parser/cxfa_ifempty.cpp b/xfa/fxfa/parser/cxfa_ifempty.cpp
index dc50a19..0c39c45 100644
--- a/xfa/fxfa/parser/cxfa_ifempty.cpp
+++ b/xfa/fxfa/parser/cxfa_ifempty.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_ifempty.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_IfEmpty::CXFA_IfEmpty(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::IfEmpty,
                 {},
                 kIfEmptyAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_IfEmpty::~CXFA_IfEmpty() = default;
diff --git a/xfa/fxfa/parser/cxfa_ifempty.h b/xfa/fxfa/parser/cxfa_ifempty.h
index 401faa7..c52c2f5 100644
--- a/xfa/fxfa/parser/cxfa_ifempty.h
+++ b/xfa/fxfa/parser/cxfa_ifempty.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_IfEmpty final : public CXFA_Node {
  public:
-  CXFA_IfEmpty(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_IfEmpty() override;
+
+ private:
+  CXFA_IfEmpty(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_IFEMPTY_H_
diff --git a/xfa/fxfa/parser/cxfa_image.cpp b/xfa/fxfa/parser/cxfa_image.cpp
index fe84b11..dcbdfa5 100644
--- a/xfa/fxfa/parser/cxfa_image.cpp
+++ b/xfa/fxfa/parser/cxfa_image.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_image.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -26,15 +26,24 @@
 
 }  // namespace
 
+// static
+CXFA_Image* CXFA_Image::FromNode(CXFA_Node* pNode) {
+  return pNode && pNode->GetElementType() == XFA_Element::Image
+             ? static_cast<CXFA_Image*>(pNode)
+             : nullptr;
+}
+
 CXFA_Image::CXFA_Image(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Image,
                 {},
                 kImageAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Image::~CXFA_Image() = default;
 
@@ -60,11 +69,11 @@
 }
 
 void CXFA_Image::SetContentType(const WideString& wsContentType) {
-  JSObject()->SetCData(XFA_Attribute::ContentType, wsContentType, false, false);
+  JSObject()->SetCData(XFA_Attribute::ContentType, wsContentType);
 }
 
 void CXFA_Image::SetHref(const WideString& wsHref) {
-  JSObject()->SetCData(XFA_Attribute::Href, wsHref, false, false);
+  JSObject()->SetCData(XFA_Attribute::Href, wsHref);
 }
 
 void CXFA_Image::SetTransferEncoding(XFA_AttributeValue iTransferEncoding) {
diff --git a/xfa/fxfa/parser/cxfa_image.h b/xfa/fxfa/parser/cxfa_image.h
index 62bb93a..c89883f 100644
--- a/xfa/fxfa/parser/cxfa_image.h
+++ b/xfa/fxfa/parser/cxfa_image.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,9 @@
 
 class CXFA_Image final : public CXFA_Node {
  public:
-  CXFA_Image(CXFA_Document* doc, XFA_PacketType packet);
+  static CXFA_Image* FromNode(CXFA_Node* pNode);
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Image() override;
 
   XFA_AttributeValue GetAspect();
@@ -25,6 +27,9 @@
 
   WideString GetContentType();
   void SetContentType(const WideString& wsContentType);
+
+ private:
+  CXFA_Image(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_IMAGE_H_
diff --git a/xfa/fxfa/parser/cxfa_imageedit.cpp b/xfa/fxfa/parser/cxfa_imageedit.cpp
index 5a6447e..6158fb7 100644
--- a/xfa/fxfa/parser/cxfa_imageedit.cpp
+++ b/xfa/fxfa/parser/cxfa_imageedit.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,14 @@
 #include "xfa/fxfa/parser/cxfa_imageedit.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kImageEditPropertyData[] = {
-    {XFA_Element::Margin, 1, 0},
-    {XFA_Element::Border, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Margin, 1, {}},
+    {XFA_Element::Border, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kImageEditAttributeData[] = {
@@ -30,12 +30,14 @@
 CXFA_ImageEdit::CXFA_ImageEdit(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::ImageEdit,
                 kImageEditPropertyData,
                 kImageEditAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ImageEdit::~CXFA_ImageEdit() = default;
 
diff --git a/xfa/fxfa/parser/cxfa_imageedit.h b/xfa/fxfa/parser/cxfa_imageedit.h
index 884c219..e922168 100644
--- a/xfa/fxfa/parser/cxfa_imageedit.h
+++ b/xfa/fxfa/parser/cxfa_imageedit.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,11 +11,14 @@
 
 class CXFA_ImageEdit final : public CXFA_Node {
  public:
-  CXFA_ImageEdit(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ImageEdit() override;
 
   XFA_Element GetValueNodeType() const override;
   XFA_FFWidgetType GetDefaultFFWidgetType() const override;
+
+ private:
+  CXFA_ImageEdit(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_IMAGEEDIT_H_
diff --git a/xfa/fxfa/parser/cxfa_includexdpcontent.cpp b/xfa/fxfa/parser/cxfa_includexdpcontent.cpp
index fb9f358..6dc3bd4 100644
--- a/xfa/fxfa/parser/cxfa_includexdpcontent.cpp
+++ b/xfa/fxfa/parser/cxfa_includexdpcontent.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_includexdpcontent.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                                XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::IncludeXDPContent,
                 {},
                 kIncludeXDPContentAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_IncludeXDPContent::~CXFA_IncludeXDPContent() = default;
diff --git a/xfa/fxfa/parser/cxfa_includexdpcontent.h b/xfa/fxfa/parser/cxfa_includexdpcontent.h
index 15c5bbe..e44e7eb 100644
--- a/xfa/fxfa/parser/cxfa_includexdpcontent.h
+++ b/xfa/fxfa/parser/cxfa_includexdpcontent.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_IncludeXDPContent final : public CXFA_Node {
  public:
-  CXFA_IncludeXDPContent(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_IncludeXDPContent() override;
+
+ private:
+  CXFA_IncludeXDPContent(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_INCLUDEXDPCONTENT_H_
diff --git a/xfa/fxfa/parser/cxfa_incrementalload.cpp b/xfa/fxfa/parser/cxfa_incrementalload.cpp
index cedf559..37d5350 100644
--- a/xfa/fxfa/parser/cxfa_incrementalload.cpp
+++ b/xfa/fxfa/parser/cxfa_incrementalload.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_incrementalload.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                            XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::IncrementalLoad,
                 {},
                 kIncrementalLoadAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_IncrementalLoad::~CXFA_IncrementalLoad() = default;
diff --git a/xfa/fxfa/parser/cxfa_incrementalload.h b/xfa/fxfa/parser/cxfa_incrementalload.h
index 253a2ae..919423a 100644
--- a/xfa/fxfa/parser/cxfa_incrementalload.h
+++ b/xfa/fxfa/parser/cxfa_incrementalload.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_IncrementalLoad final : public CXFA_Node {
  public:
-  CXFA_IncrementalLoad(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_IncrementalLoad() override;
+
+ private:
+  CXFA_IncrementalLoad(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_INCREMENTALLOAD_H_
diff --git a/xfa/fxfa/parser/cxfa_incrementalmerge.cpp b/xfa/fxfa/parser/cxfa_incrementalmerge.cpp
index faf1148..299d0a7 100644
--- a/xfa/fxfa/parser/cxfa_incrementalmerge.cpp
+++ b/xfa/fxfa/parser/cxfa_incrementalmerge.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_incrementalmerge.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                              XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::IncrementalMerge,
                 {},
                 kIncrementalMergeAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_IncrementalMerge::~CXFA_IncrementalMerge() = default;
diff --git a/xfa/fxfa/parser/cxfa_incrementalmerge.h b/xfa/fxfa/parser/cxfa_incrementalmerge.h
index 4ec65af..d2db0f9 100644
--- a/xfa/fxfa/parser/cxfa_incrementalmerge.h
+++ b/xfa/fxfa/parser/cxfa_incrementalmerge.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_IncrementalMerge final : public CXFA_Node {
  public:
-  CXFA_IncrementalMerge(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_IncrementalMerge() override;
+
+ private:
+  CXFA_IncrementalMerge(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_INCREMENTALMERGE_H_
diff --git a/xfa/fxfa/parser/cxfa_insert.cpp b/xfa/fxfa/parser/cxfa_insert.cpp
index ff98c51..d12277d 100644
--- a/xfa/fxfa/parser/cxfa_insert.cpp
+++ b/xfa/fxfa/parser/cxfa_insert.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_insert.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_Insert::CXFA_Insert(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_SourceSet,
+                XFA_XDPPACKET::kSourceSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::Insert,
                 {},
                 kInsertAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Insert::~CXFA_Insert() = default;
diff --git a/xfa/fxfa/parser/cxfa_insert.h b/xfa/fxfa/parser/cxfa_insert.h
index e157a82..74dcb0a 100644
--- a/xfa/fxfa/parser/cxfa_insert.h
+++ b/xfa/fxfa/parser/cxfa_insert.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Insert final : public CXFA_Node {
  public:
-  CXFA_Insert(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Insert() override;
+
+ private:
+  CXFA_Insert(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_INSERT_H_
diff --git a/xfa/fxfa/parser/cxfa_instancemanager.cpp b/xfa/fxfa/parser/cxfa_instancemanager.cpp
index e0d2658..83903e9 100644
--- a/xfa/fxfa/parser/cxfa_instancemanager.cpp
+++ b/xfa/fxfa/parser/cxfa_instancemanager.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_instancemanager.h"
 
 #include "fxjs/xfa/cjx_instancemanager.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kInstanceManagerPropertyData[] = {
-    {XFA_Element::Occur, 1, 0},
+    {XFA_Element::Occur, 1, {}},
 };
 
 const CXFA_Node::AttributeData kInstanceManagerAttributeData[] = {
@@ -25,11 +25,13 @@
                                            XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Form,
+                XFA_XDPPACKET::kForm,
                 XFA_ObjectType::Node,
                 XFA_Element::InstanceManager,
                 kInstanceManagerPropertyData,
                 kInstanceManagerAttributeData,
-                pdfium::MakeUnique<CJX_InstanceManager>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_InstanceManager>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_InstanceManager::~CXFA_InstanceManager() = default;
diff --git a/xfa/fxfa/parser/cxfa_instancemanager.h b/xfa/fxfa/parser/cxfa_instancemanager.h
index 3ace0e9..67c7101 100644
--- a/xfa/fxfa/parser/cxfa_instancemanager.h
+++ b/xfa/fxfa/parser/cxfa_instancemanager.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_InstanceManager final : public CXFA_Node {
  public:
-  CXFA_InstanceManager(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_InstanceManager() override;
+
+ private:
+  CXFA_InstanceManager(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_INSTANCEMANAGER_H_
diff --git a/xfa/fxfa/parser/cxfa_integer.cpp b/xfa/fxfa/parser/cxfa_integer.cpp
index cf48a25..2308681 100644
--- a/xfa/fxfa/parser/cxfa_integer.cpp
+++ b/xfa/fxfa/parser/cxfa_integer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_integer.h"
 
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,12 +23,14 @@
 CXFA_Integer::CXFA_Integer(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_SourceSet | XFA_XDPPACKET_Template |
-                 XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kSourceSet, XFA_XDPPACKET::kTemplate,
+                 XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Integer,
                 {},
                 kIntegerAttributeData,
-                pdfium::MakeUnique<CJX_Object>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Object>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Integer::~CXFA_Integer() = default;
diff --git a/xfa/fxfa/parser/cxfa_integer.h b/xfa/fxfa/parser/cxfa_integer.h
index ac94133..72ce7b8 100644
--- a/xfa/fxfa/parser/cxfa_integer.h
+++ b/xfa/fxfa/parser/cxfa_integer.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Integer final : public CXFA_Node {
  public:
-  CXFA_Integer(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Integer() override;
+
+ private:
+  CXFA_Integer(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_INTEGER_H_
diff --git a/xfa/fxfa/parser/cxfa_interactive.cpp b/xfa/fxfa/parser/cxfa_interactive.cpp
index a489ab3..687811f 100644
--- a/xfa/fxfa/parser/cxfa_interactive.cpp
+++ b/xfa/fxfa/parser/cxfa_interactive.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_interactive.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Interactive::CXFA_Interactive(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Interactive,
                 {},
                 kInteractiveAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
-CXFA_Interactive::~CXFA_Interactive() {}
+CXFA_Interactive::~CXFA_Interactive() = default;
diff --git a/xfa/fxfa/parser/cxfa_interactive.h b/xfa/fxfa/parser/cxfa_interactive.h
index 12db0bf..242166b 100644
--- a/xfa/fxfa/parser/cxfa_interactive.h
+++ b/xfa/fxfa/parser/cxfa_interactive.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Interactive final : public CXFA_Node {
  public:
-  CXFA_Interactive(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Interactive() override;
+
+ private:
+  CXFA_Interactive(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_INTERACTIVE_H_
diff --git a/xfa/fxfa/parser/cxfa_issuers.cpp b/xfa/fxfa/parser/cxfa_issuers.cpp
index 0d9f5f2..ffe0bdd 100644
--- a/xfa/fxfa/parser/cxfa_issuers.cpp
+++ b/xfa/fxfa/parser/cxfa_issuers.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_issuers.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -24,11 +24,13 @@
 CXFA_Issuers::CXFA_Issuers(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Issuers,
                 {},
                 kIssuersAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Issuers::~CXFA_Issuers() = default;
diff --git a/xfa/fxfa/parser/cxfa_issuers.h b/xfa/fxfa/parser/cxfa_issuers.h
index 11ba8b4..8986fdc 100644
--- a/xfa/fxfa/parser/cxfa_issuers.h
+++ b/xfa/fxfa/parser/cxfa_issuers.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Issuers final : public CXFA_Node {
  public:
-  CXFA_Issuers(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Issuers() override;
+
+ private:
+  CXFA_Issuers(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ISSUERS_H_
diff --git a/xfa/fxfa/parser/cxfa_items.cpp b/xfa/fxfa/parser/cxfa_items.cpp
index 89fee2b..9064779 100644
--- a/xfa/fxfa/parser/cxfa_items.cpp
+++ b/xfa/fxfa/parser/cxfa_items.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_items.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -27,11 +27,13 @@
 CXFA_Items::CXFA_Items(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Items,
                 {},
                 kItemsAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Items::~CXFA_Items() = default;
diff --git a/xfa/fxfa/parser/cxfa_items.h b/xfa/fxfa/parser/cxfa_items.h
index 609b028..6b3c554 100644
--- a/xfa/fxfa/parser/cxfa_items.h
+++ b/xfa/fxfa/parser/cxfa_items.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Items final : public CXFA_Node {
  public:
-  CXFA_Items(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Items() override;
+
+ private:
+  CXFA_Items(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ITEMS_H_
diff --git a/xfa/fxfa/parser/cxfa_jog.cpp b/xfa/fxfa/parser/cxfa_jog.cpp
index f979259..8421393 100644
--- a/xfa/fxfa/parser/cxfa_jog.cpp
+++ b/xfa/fxfa/parser/cxfa_jog.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_jog.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Jog::CXFA_Jog(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Jog,
                 {},
                 kJogAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Jog::~CXFA_Jog() = default;
diff --git a/xfa/fxfa/parser/cxfa_jog.h b/xfa/fxfa/parser/cxfa_jog.h
index a40a488..4a595f6 100644
--- a/xfa/fxfa/parser/cxfa_jog.h
+++ b/xfa/fxfa/parser/cxfa_jog.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Jog final : public CXFA_Node {
  public:
-  CXFA_Jog(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Jog() override;
+
+ private:
+  CXFA_Jog(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_JOG_H_
diff --git a/xfa/fxfa/parser/cxfa_keep.cpp b/xfa/fxfa/parser/cxfa_keep.cpp
index e500ee6..380493a 100644
--- a/xfa/fxfa/parser/cxfa_keep.cpp
+++ b/xfa/fxfa/parser/cxfa_keep.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_keep.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kKeepPropertyData[] = {
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kKeepAttributeData[] = {
@@ -32,11 +32,13 @@
 CXFA_Keep::CXFA_Keep(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Keep,
                 kKeepPropertyData,
                 kKeepAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Keep::~CXFA_Keep() = default;
diff --git a/xfa/fxfa/parser/cxfa_keep.h b/xfa/fxfa/parser/cxfa_keep.h
index 0d4a2e0..8a19794 100644
--- a/xfa/fxfa/parser/cxfa_keep.h
+++ b/xfa/fxfa/parser/cxfa_keep.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Keep final : public CXFA_Node {
  public:
-  CXFA_Keep(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Keep() override;
+
+ private:
+  CXFA_Keep(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_KEEP_H_
diff --git a/xfa/fxfa/parser/cxfa_keyusage.cpp b/xfa/fxfa/parser/cxfa_keyusage.cpp
index dda5c10..c242edd 100644
--- a/xfa/fxfa/parser/cxfa_keyusage.cpp
+++ b/xfa/fxfa/parser/cxfa_keyusage.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_keyusage.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -33,11 +33,13 @@
 CXFA_KeyUsage::CXFA_KeyUsage(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::KeyUsage,
                 {},
                 kKeyUsageAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_KeyUsage::~CXFA_KeyUsage() = default;
diff --git a/xfa/fxfa/parser/cxfa_keyusage.h b/xfa/fxfa/parser/cxfa_keyusage.h
index 3409e3f..1b63036 100644
--- a/xfa/fxfa/parser/cxfa_keyusage.h
+++ b/xfa/fxfa/parser/cxfa_keyusage.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_KeyUsage final : public CXFA_Node {
  public:
-  CXFA_KeyUsage(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_KeyUsage() override;
+
+ private:
+  CXFA_KeyUsage(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_KEYUSAGE_H_
diff --git a/xfa/fxfa/parser/cxfa_labelprinter.cpp b/xfa/fxfa/parser/cxfa_labelprinter.cpp
index 40f3616..edbb6e4 100644
--- a/xfa/fxfa/parser/cxfa_labelprinter.cpp
+++ b/xfa/fxfa/parser/cxfa_labelprinter.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #include "xfa/fxfa/parser/cxfa_labelprinter.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kLabelPrinterPropertyData[] = {
-    {XFA_Element::FontInfo, 1, 0},
-    {XFA_Element::Xdc, 1, 0},
-    {XFA_Element::BatchOutput, 1, 0},
-    {XFA_Element::FlipLabel, 1, 0},
+    {XFA_Element::FontInfo, 1, {}},
+    {XFA_Element::Xdc, 1, {}},
+    {XFA_Element::BatchOutput, 1, {}},
+    {XFA_Element::FlipLabel, 1, {}},
 };
 
 const CXFA_Node::AttributeData kLabelPrinterAttributeData[] = {
@@ -30,11 +30,13 @@
 CXFA_LabelPrinter::CXFA_LabelPrinter(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::LabelPrinter,
                 kLabelPrinterPropertyData,
                 kLabelPrinterAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_LabelPrinter::~CXFA_LabelPrinter() = default;
diff --git a/xfa/fxfa/parser/cxfa_labelprinter.h b/xfa/fxfa/parser/cxfa_labelprinter.h
index 74a5e82..0ff2094 100644
--- a/xfa/fxfa/parser/cxfa_labelprinter.h
+++ b/xfa/fxfa/parser/cxfa_labelprinter.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_LabelPrinter final : public CXFA_Node {
  public:
-  CXFA_LabelPrinter(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_LabelPrinter() override;
+
+ private:
+  CXFA_LabelPrinter(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_LABELPRINTER_H_
diff --git a/xfa/fxfa/parser/cxfa_layout.cpp b/xfa/fxfa/parser/cxfa_layout.cpp
index 12d6f49..8852e73 100644
--- a/xfa/fxfa/parser/cxfa_layout.cpp
+++ b/xfa/fxfa/parser/cxfa_layout.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_layout.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Layout::CXFA_Layout(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Layout,
                 {},
                 kLayoutAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Layout::~CXFA_Layout() = default;
diff --git a/xfa/fxfa/parser/cxfa_layout.h b/xfa/fxfa/parser/cxfa_layout.h
index ad25efa..049826f 100644
--- a/xfa/fxfa/parser/cxfa_layout.h
+++ b/xfa/fxfa/parser/cxfa_layout.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Layout final : public CXFA_Node {
  public:
-  CXFA_Layout(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Layout() override;
+
+ private:
+  CXFA_Layout(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_LAYOUT_H_
diff --git a/xfa/fxfa/parser/cxfa_level.cpp b/xfa/fxfa/parser/cxfa_level.cpp
index b738081..03a28f3 100644
--- a/xfa/fxfa/parser/cxfa_level.cpp
+++ b/xfa/fxfa/parser/cxfa_level.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_level.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Level::CXFA_Level(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Level,
                 {},
                 kLevelAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Level::~CXFA_Level() = default;
diff --git a/xfa/fxfa/parser/cxfa_level.h b/xfa/fxfa/parser/cxfa_level.h
index 8cc03dc..a654e89 100644
--- a/xfa/fxfa/parser/cxfa_level.h
+++ b/xfa/fxfa/parser/cxfa_level.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Level final : public CXFA_Node {
  public:
-  CXFA_Level(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Level() override;
+
+ private:
+  CXFA_Level(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_LEVEL_H_
diff --git a/xfa/fxfa/parser/cxfa_line.cpp b/xfa/fxfa/parser/cxfa_line.cpp
index 2ac97b2..7ab08c3 100644
--- a/xfa/fxfa/parser/cxfa_line.cpp
+++ b/xfa/fxfa/parser/cxfa_line.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,14 @@
 #include "xfa/fxfa/parser/cxfa_line.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_edge.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kLinePropertyData[] = {
-    {XFA_Element::Edge, 1, 0},
+    {XFA_Element::Edge, 1, {}},
 };
 
 const CXFA_Node::AttributeData kLineAttributeData[] = {
@@ -29,15 +29,24 @@
 
 }  // namespace
 
+// static
+CXFA_Line* CXFA_Line::FromNode(CXFA_Node* pNode) {
+  return pNode && pNode->GetElementType() == XFA_Element::Line
+             ? static_cast<CXFA_Line*>(pNode)
+             : nullptr;
+}
+
 CXFA_Line::CXFA_Line(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Line,
                 kLinePropertyData,
                 kLineAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Line::~CXFA_Line() = default;
 
diff --git a/xfa/fxfa/parser/cxfa_line.h b/xfa/fxfa/parser/cxfa_line.h
index 53584fa..e13e282 100644
--- a/xfa/fxfa/parser/cxfa_line.h
+++ b/xfa/fxfa/parser/cxfa_line.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,12 +13,17 @@
 
 class CXFA_Line final : public CXFA_Node {
  public:
-  CXFA_Line(CXFA_Document* doc, XFA_PacketType packet);
+  static CXFA_Line* FromNode(CXFA_Node* pNode);
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Line() override;
 
   XFA_AttributeValue GetHand();
   bool GetSlope();
   CXFA_Edge* GetEdgeIfExists();
+
+ private:
+  CXFA_Line(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_LINE_H_
diff --git a/xfa/fxfa/parser/cxfa_linear.cpp b/xfa/fxfa/parser/cxfa_linear.cpp
index b482d27..959138c 100644
--- a/xfa/fxfa/parser/cxfa_linear.cpp
+++ b/xfa/fxfa/parser/cxfa_linear.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,17 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_linear.h"
 
-#include "core/fxge/render_defines.h"
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+#include "xfa/fgas/graphics/cfgas_geshading.h"
 #include "xfa/fxfa/parser/cxfa_color.h"
-#include "xfa/fxgraphics/cxfa_geshading.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kLinearPropertyData[] = {
-    {XFA_Element::Color, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Color, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kLinearAttributeData[] = {
@@ -32,12 +32,14 @@
 CXFA_Linear::CXFA_Linear(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Linear,
                 kLinearPropertyData,
                 kLinearAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Linear::~CXFA_Linear() = default;
 
@@ -51,8 +53,8 @@
   return GetChild<CXFA_Color>(0, XFA_Element::Color, false);
 }
 
-void CXFA_Linear::Draw(CXFA_Graphics* pGS,
-                       CXFA_GEPath* fillPath,
+void CXFA_Linear::Draw(CFGAS_GEGraphics* pGS,
+                       const CFGAS_GEPath& fillPath,
                        FX_ARGB crStart,
                        const CFX_RectF& rtFill,
                        const CFX_Matrix& matrix) {
@@ -82,10 +84,8 @@
       break;
   }
 
-  CXFA_GEShading shading(ptStart, ptEnd, false, false, crStart, crEnd);
-
-  pGS->SaveGraphState();
-  pGS->SetFillColor(CXFA_GEColor(&shading));
-  pGS->FillPath(fillPath, FXFILL_WINDING, &matrix);
-  pGS->RestoreGraphState();
+  CFGAS_GEShading shading(ptStart, ptEnd, false, false, crStart, crEnd);
+  CFGAS_GEGraphics::StateRestorer restorer(pGS);
+  pGS->SetFillColor(CFGAS_GEColor(&shading));
+  pGS->FillPath(fillPath, CFX_FillRenderOptions::FillType::kWinding, matrix);
 }
diff --git a/xfa/fxfa/parser/cxfa_linear.h b/xfa/fxfa/parser/cxfa_linear.h
index 0fa5e1a..f299805 100644
--- a/xfa/fxfa/parser/cxfa_linear.h
+++ b/xfa/fxfa/parser/cxfa_linear.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,27 +8,29 @@
 #define XFA_FXFA_PARSER_CXFA_LINEAR_H_
 
 #include "core/fxcrt/fx_coordinates.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
 
+class CFGAS_GEGraphics;
 class CXFA_Color;
-class CXFA_Graphics;
 
 class CXFA_Linear final : public CXFA_Node {
  public:
   static constexpr XFA_AttributeValue kDefaultType =
       XFA_AttributeValue::ToRight;
 
-  CXFA_Linear(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Linear() override;
 
-  void Draw(CXFA_Graphics* pGS,
-            CXFA_GEPath* fillPath,
+  void Draw(CFGAS_GEGraphics* pGS,
+            const CFGAS_GEPath& fillPath,
             FX_ARGB crStart,
             const CFX_RectF& rtFill,
             const CFX_Matrix& matrix);
 
  private:
+  CXFA_Linear(CXFA_Document* doc, XFA_PacketType packet);
+
   XFA_AttributeValue GetType();
   CXFA_Color* GetColorIfExists();
 };
diff --git a/xfa/fxfa/parser/cxfa_linearized.cpp b/xfa/fxfa/parser/cxfa_linearized.cpp
index 6c2ffe1..dea3245 100644
--- a/xfa/fxfa/parser/cxfa_linearized.cpp
+++ b/xfa/fxfa/parser/cxfa_linearized.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_linearized.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Linearized::CXFA_Linearized(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Linearized,
                 {},
                 kLinearizedAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Linearized::~CXFA_Linearized() = default;
diff --git a/xfa/fxfa/parser/cxfa_linearized.h b/xfa/fxfa/parser/cxfa_linearized.h
index d2fe2d7..abce4f3 100644
--- a/xfa/fxfa/parser/cxfa_linearized.h
+++ b/xfa/fxfa/parser/cxfa_linearized.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Linearized final : public CXFA_Node {
  public:
-  CXFA_Linearized(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Linearized() override;
+
+ private:
+  CXFA_Linearized(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_LINEARIZED_H_
diff --git a/xfa/fxfa/parser/cxfa_list.cpp b/xfa/fxfa/parser/cxfa_list.cpp
index 6329cec..3bb7488 100644
--- a/xfa/fxfa/parser/cxfa_list.cpp
+++ b/xfa/fxfa/parser/cxfa_list.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,27 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_list.h"
 
-#include <utility>
-
 #include "core/fxcrt/fx_extension.h"
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cjx_treelist.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-CXFA_List::CXFA_List(CXFA_Document* pDocument, std::unique_ptr<CJX_Object> obj)
-    : CXFA_List(pDocument,
-                XFA_ObjectType::List,
-                XFA_Element::List,
-                std::move(obj)) {}
+CXFA_List::CXFA_List(CXFA_Document* pDocument, CJX_Object* obj)
+    : CXFA_List(pDocument, XFA_ObjectType::List, XFA_Element::List, obj) {}
 
 CXFA_List::CXFA_List(CXFA_Document* pDocument,
                      XFA_ObjectType objectType,
                      XFA_Element eType,
-                     std::unique_ptr<CJX_Object> obj)
-    : CXFA_Object(pDocument, objectType, eType, std::move(obj)) {
-  m_pDocument->GetScriptContext()->AddToCacheList(
-      std::unique_ptr<CXFA_List>(this));
-}
+                     CJX_Object* obj)
+    : CXFA_Object(pDocument, objectType, eType, obj) {}
 
-CXFA_List::~CXFA_List() {}
+CXFA_List::~CXFA_List() = default;
diff --git a/xfa/fxfa/parser/cxfa_list.h b/xfa/fxfa/parser/cxfa_list.h
index c114801..8a4a5b5 100644
--- a/xfa/fxfa/parser/cxfa_list.h
+++ b/xfa/fxfa/parser/cxfa_list.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,28 +7,30 @@
 #ifndef XFA_FXFA_PARSER_CXFA_LIST_H_
 #define XFA_FXFA_PARSER_CXFA_LIST_H_
 
-#include <memory>
-
+#include "fxjs/gc/heap.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
 
+class CJX_Object;
 class CXFA_Document;
+class CXFA_Node;
 
 class CXFA_List : public CXFA_Object {
  public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_List() override;
 
   virtual size_t GetLength() = 0;
-  virtual void Append(CXFA_Node* pNode) = 0;
+  virtual bool Append(CXFA_Node* pNode) = 0;
   virtual bool Insert(CXFA_Node* pNewNode, CXFA_Node* pBeforeNode) = 0;
   virtual void Remove(CXFA_Node* pNode) = 0;
   virtual CXFA_Node* Item(size_t iIndex) = 0;
 
  protected:
-  CXFA_List(CXFA_Document* doc, std::unique_ptr<CJX_Object> js_obj);
+  CXFA_List(CXFA_Document* doc, CJX_Object* js_obj);
   CXFA_List(CXFA_Document* pDocument,
             XFA_ObjectType objectType,
             XFA_Element eType,
-            std::unique_ptr<CJX_Object> obj);
+            CJX_Object* obj);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_LIST_H_
diff --git a/xfa/fxfa/parser/cxfa_locale.cpp b/xfa/fxfa/parser/cxfa_locale.cpp
index 6c54fb8..1285d89 100644
--- a/xfa/fxfa/parser/cxfa_locale.cpp
+++ b/xfa/fxfa/parser/cxfa_locale.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,19 @@
 #include "xfa/fxfa/parser/cxfa_locale.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kLocalePropertyData[] = {
-    {XFA_Element::DatePatterns, 1, 0},    {XFA_Element::CalendarSymbols, 1, 0},
-    {XFA_Element::CurrencySymbols, 1, 0}, {XFA_Element::Typefaces, 1, 0},
-    {XFA_Element::DateTimeSymbols, 1, 0}, {XFA_Element::NumberPatterns, 1, 0},
-    {XFA_Element::NumberSymbols, 1, 0},   {XFA_Element::TimePatterns, 1, 0},
+    {XFA_Element::DatePatterns, 1, {}},
+    {XFA_Element::CalendarSymbols, 1, {}},
+    {XFA_Element::CurrencySymbols, 1, {}},
+    {XFA_Element::Typefaces, 1, {}},
+    {XFA_Element::DateTimeSymbols, 1, {}},
+    {XFA_Element::NumberPatterns, 1, {}},
+    {XFA_Element::NumberSymbols, 1, {}},
+    {XFA_Element::TimePatterns, 1, {}},
 };
 
 const CXFA_Node::AttributeData kLocaleAttributeData[] = {
@@ -29,11 +33,13 @@
 CXFA_Locale::CXFA_Locale(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Config | XFA_XDPPACKET_LocaleSet),
+                {XFA_XDPPACKET::kConfig, XFA_XDPPACKET::kLocaleSet},
                 XFA_ObjectType::Node,
                 XFA_Element::Locale,
                 kLocalePropertyData,
                 kLocaleAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Locale::~CXFA_Locale() = default;
diff --git a/xfa/fxfa/parser/cxfa_locale.h b/xfa/fxfa/parser/cxfa_locale.h
index 67bee33..3f07dad 100644
--- a/xfa/fxfa/parser/cxfa_locale.h
+++ b/xfa/fxfa/parser/cxfa_locale.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Locale final : public CXFA_Node {
  public:
-  CXFA_Locale(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Locale() override;
+
+ private:
+  CXFA_Locale(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_LOCALE_H_
diff --git a/xfa/fxfa/parser/cxfa_localemgr.cpp b/xfa/fxfa/parser/cxfa_localemgr.cpp
index b6c65a6..f49275e 100644
--- a/xfa/fxfa/parser/cxfa_localemgr.cpp
+++ b/xfa/fxfa/parser/cxfa_localemgr.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,8 +13,9 @@
 
 #include "core/fxcodec/flate/flatemodule.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
+#include "fxjs/gc/container_trace.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 #include "xfa/fxfa/parser/cxfa_acrobat.h"
 #include "xfa/fxfa/parser/cxfa_common.h"
 #include "xfa/fxfa/parser/cxfa_locale.h"
@@ -24,22 +25,6 @@
 #include "xfa/fxfa/parser/cxfa_xmllocale.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
-#define FX_LANG_zh_HK 0x0c04
-#define FX_LANG_zh_CN 0x0804
-#define FX_LANG_zh_TW 0x0404
-#define FX_LANG_nl_NL 0x0413
-#define FX_LANG_en_GB 0x0809
-#define FX_LANG_en_US 0x0409
-#define FX_LANG_fr_FR 0x040c
-#define FX_LANG_de_DE 0x0407
-#define FX_LANG_it_IT 0x0410
-#define FX_LANG_ja_JP 0x0411
-#define FX_LANG_ko_KR 0x0412
-#define FX_LANG_pt_BR 0x0416
-#define FX_LANG_ru_RU 0x0419
-#define FX_LANG_es_LA 0x080a
-#define FX_LANG_es_ES 0x0c0a
-
 namespace {
 
 // These arrays are the hex encoded XML strings which define the locale.
@@ -49,7 +34,7 @@
 //       <month>January</month>
 //       <month>February</month>
 //   ...
-const uint8_t g_enUS_Locale[] = {
+const uint8_t k_enUS_Locale[] = {
     0x78, 0x9C, 0x95, 0x56, 0xD1, 0x6E, 0x9B, 0x30, 0x14, 0x7D, 0x9F, 0xB4,
     0x7F, 0x40, 0xD6, 0x2A, 0xB5, 0x52, 0x56, 0x6F, 0x8F, 0xA9, 0x88, 0xA5,
     0x6C, 0x24, 0x9B, 0x3A, 0xD1, 0x55, 0x22, 0x55, 0xB5, 0xBE, 0x4C, 0x0E,
@@ -103,7 +88,8 @@
     0x20, 0xF2, 0x94, 0x44, 0xB3, 0x45, 0x63, 0xCB, 0x5D, 0x09, 0xB6, 0x7B,
     0xD5, 0x58, 0xF7, 0x55, 0xA4, 0x96, 0x7F, 0x01, 0x75, 0x37, 0x1B, 0x8B,
 };
-const uint8_t g_enGB_Locale[] = {
+
+const uint8_t k_enGB_Locale[] = {
     0x78, 0x9C, 0x95, 0x56, 0xD1, 0x6E, 0xD3, 0x30, 0x14, 0x7D, 0x47, 0xE2,
     0x1F, 0xA2, 0x88, 0x49, 0x20, 0x6D, 0x33, 0x3C, 0x6E, 0xEA, 0x2C, 0x75,
     0xEB, 0xD6, 0x32, 0xC8, 0xA8, 0x48, 0xC5, 0xC4, 0x5E, 0x90, 0x9B, 0x78,
@@ -168,7 +154,8 @@
     0x2F, 0x06, 0x90, 0xBC, 0x5C, 0xF1, 0xD5, 0x67, 0xB1, 0xE1, 0xE9, 0xE6,
     0xD0, 0xFD, 0x9E, 0x95, 0x51, 0xF5, 0x19, 0x0B, 0xCB, 0x7F, 0x8E, 0x69,
     0xAC, 0xD0};
-const uint8_t g_zhCN_Locale[] = {
+
+const uint8_t k_zhCN_Locale[] = {
     0x78, 0x9C, 0xED, 0x56, 0x41, 0x4F, 0xD4, 0x40, 0x14, 0xBE, 0x9B, 0xF8,
     0x1F, 0x9A, 0x46, 0x52, 0x4D, 0x58, 0x46, 0x8F, 0x90, 0xD2, 0x64, 0x03,
     0x08, 0x06, 0xBB, 0x21, 0x96, 0x68, 0xE0, 0x62, 0xBA, 0xED, 0xB0, 0x2D,
@@ -236,7 +223,8 @@
     0xD8, 0x68, 0x39, 0xA9, 0x1D, 0xFA, 0x8D, 0x55, 0xD2, 0xE3, 0xF1, 0x7D,
     0xD1, 0xCD, 0x33, 0x63, 0x51, 0xFC, 0x15, 0x0D, 0xCB, 0xBF, 0xC0, 0xE4,
     0x3D, 0x40};
-const uint8_t g_zhTW_Locale[] = {
+
+const uint8_t k_zhTW_Locale[] = {
     0x78, 0x9C, 0xED, 0x57, 0xCD, 0x4F, 0xD4, 0x40, 0x14, 0x3F, 0x63, 0xE2,
     0xFF, 0x30, 0x69, 0x20, 0xD5, 0x04, 0xA8, 0x1E, 0x21, 0xA5, 0xC9, 0xC6,
     0x45, 0x30, 0x58, 0x42, 0xEC, 0x46, 0x22, 0x17, 0x33, 0xDB, 0x0E, 0xDB,
@@ -305,7 +293,8 @@
     0xF0, 0x6A, 0x0D, 0x5F, 0x21, 0x77, 0xF4, 0x2A, 0x6D, 0xCF, 0x69, 0xAC,
     0xE0, 0x00, 0x19, 0xDE, 0x1E, 0x2C, 0xB6, 0xD4, 0x7B, 0xCA, 0x52, 0xF8,
     0x67, 0x41, 0xF9, 0x03, 0x11, 0xC2, 0x4A, 0xC9};
-const uint8_t g_zhHK_Locale[] = {
+
+const uint8_t k_zhHK_Locale[] = {
     0x78, 0x9C, 0xA5, 0x57, 0x4D, 0x4F, 0x13, 0x41, 0x18, 0x3E, 0x63, 0xE2,
     0x7F, 0x98, 0x6C, 0x20, 0x0B, 0x09, 0x50, 0x8A, 0x9F, 0x25, 0xCB, 0x26,
     0x44, 0x90, 0x9A, 0x5A, 0x43, 0x2C, 0xD1, 0xC8, 0xC5, 0x6C, 0x77, 0x87,
@@ -379,7 +368,8 @@
     0x52, 0xAB, 0x1A, 0xA5, 0x15, 0x64, 0x33, 0x9D, 0xDD, 0xED, 0x47, 0x2E,
     0x70, 0x9E, 0x42, 0xCA, 0xF9, 0x27, 0x42, 0xFC, 0x0B, 0xD9, 0x0E, 0x4B,
     0xED};
-const uint8_t g_jaJP_Locale[] = {
+
+const uint8_t k_jaJP_Locale[] = {
     0x78, 0x9C, 0xED, 0x56, 0xCB, 0x6E, 0xD3, 0x40, 0x14, 0x5D, 0x17, 0x89,
     0x7F, 0x18, 0x59, 0x20, 0x83, 0xD4, 0xD6, 0x0D, 0xEF, 0x56, 0xAE, 0xA5,
     0x8A, 0x96, 0x86, 0x16, 0xA3, 0x08, 0x57, 0xA0, 0x76, 0x83, 0x26, 0xF6,
@@ -446,7 +436,8 @@
     0x21, 0x6A, 0x01, 0xB7, 0xC6, 0xD2, 0x0C, 0x5A, 0x8A, 0xA6, 0x77, 0x5C,
     0xD2, 0xD8, 0xA2, 0x3E, 0x33, 0x18, 0x89, 0x7F, 0xAA, 0x25, 0xE6, 0xA9,
     0x2A, 0xC1, 0x87, 0xBD, 0xF6, 0x17, 0x6C, 0x42, 0x08, 0x21};
-const uint8_t g_koKR_Locale[] = {
+
+const uint8_t k_koKR_Locale[] = {
     0x78, 0x9C, 0xED, 0x56, 0x4D, 0x6B, 0xD4, 0x40, 0x18, 0x3E, 0x57, 0xF0,
     0x3F, 0x0C, 0xC1, 0x12, 0x85, 0x76, 0xB7, 0xEB, 0x77, 0x4B, 0x1A, 0x28,
     0x6E, 0x6D, 0x65, 0x8D, 0x94, 0xA6, 0x58, 0xEC, 0x45, 0x66, 0x93, 0xE9,
@@ -515,7 +506,8 @@
     0x3E, 0x9A, 0x72, 0x3B, 0x81, 0xDD, 0xF7, 0x47, 0xD2, 0x64, 0x9C, 0xA1,
     0x05, 0x53, 0x2D, 0x75, 0x9E, 0x42, 0x3D, 0xFC, 0xFC, 0x17, 0xFF, 0x01,
     0xDC, 0xF1, 0x18, 0xD3};
-const uint8_t g_esES_Locale[] = {
+
+const uint8_t k_esES_Locale[] = {
     0x78, 0x9C, 0x9D, 0x56, 0xCF, 0x6B, 0xD4, 0x40, 0x14, 0xBE, 0x0B, 0xFE,
     0x0F, 0x21, 0x58, 0x56, 0xA1, 0xDD, 0xD1, 0x63, 0xCB, 0x36, 0x50, 0xDA,
     0xDA, 0x95, 0x1A, 0x59, 0x9A, 0xAA, 0xD8, 0x8B, 0x4C, 0x92, 0xE9, 0xEE,
@@ -585,7 +577,8 @@
     0x61, 0xC3, 0x23, 0x3E, 0xE5, 0xA9, 0xE6, 0xD0, 0x6C, 0xCF, 0x64, 0x94,
     0xFD, 0xFC, 0xC2, 0xF2, 0x2F, 0x7C, 0xDC, 0xCC, 0x2F,
 };
-const uint8_t g_esLA_Locale[] = {
+
+const uint8_t k_esLA_Locale[] = {
     0x78, 0x9C, 0x9D, 0x56, 0xCD, 0x4E, 0xDC, 0x30, 0x10, 0x3E, 0x53, 0xA9,
     0xEF, 0x60, 0x45, 0x45, 0x80, 0x04, 0x9B, 0xF6, 0xB8, 0x28, 0x44, 0x5A,
     0x01, 0xA5, 0x15, 0x0D, 0x5A, 0x75, 0x51, 0x4B, 0xB9, 0x54, 0x4E, 0x62,
@@ -653,7 +646,8 @@
     0x0D, 0xDC, 0x69, 0xDA, 0x25, 0x7A, 0xD3, 0x4B, 0x9B, 0xF0, 0xA5, 0x68,
     0xD6, 0x3C, 0x66, 0xD3, 0x73, 0x9E, 0x31, 0xF3, 0x22, 0xB3, 0x11, 0x57,
     0x3B, 0x4F, 0x43, 0xCF, 0x3F, 0xEF, 0xCD, 0xBF, 0xC4, 0xB5, 0xD4, 0x04};
-const uint8_t g_deDE_Loacale[] = {
+
+const uint8_t k_deDE_Loacale[] = {
     0x78, 0x9C, 0x95, 0x56, 0x4D, 0x6F, 0xD4, 0x30, 0x10, 0xBD, 0x23, 0xF1,
     0x1F, 0xAC, 0x88, 0xAA, 0x20, 0xB5, 0x1B, 0x38, 0xB6, 0xDA, 0x46, 0x5A,
     0x75, 0xFB, 0x41, 0x4B, 0xCA, 0xAA, 0x69, 0x41, 0xF4, 0x82, 0xBC, 0x89,
@@ -720,7 +714,8 @@
     0xB3, 0x2B, 0xBE, 0xE4, 0xA9, 0xE6, 0xDC, 0xFB, 0x3D, 0x2B, 0xA3, 0xEA,
     0x73, 0x16, 0x96, 0xFF, 0x00, 0x29, 0x27, 0xAF, 0x17,
 };
-const uint8_t g_frFR_Locale[] = {
+
+const uint8_t k_frFR_Locale[] = {
     0x78, 0x9C, 0x95, 0x56, 0x4D, 0x6F, 0xD3, 0x40, 0x10, 0xBD, 0x23, 0xF1,
     0x1F, 0x56, 0x16, 0x55, 0x41, 0x6A, 0xBD, 0x70, 0x4C, 0x95, 0x5A, 0xAA,
     0xFA, 0x15, 0x28, 0x46, 0x51, 0x53, 0x40, 0xF4, 0x82, 0x36, 0xF6, 0x36,
@@ -787,7 +782,8 @@
     0xCC, 0x49, 0x3E, 0x19, 0x7A, 0xBE, 0x86, 0x34, 0x12, 0x1D, 0x48, 0xEE,
     0x22, 0xE5, 0x93, 0xA9, 0x58, 0xF2, 0x54, 0x73, 0xF8, 0x76, 0xCF, 0xF2,
     0xA8, 0xF8, 0xD2, 0x85, 0xE5, 0x3F, 0x5D, 0x49, 0xBE, 0x1B};
-const uint8_t g_itIT_Locale[] = {
+
+const uint8_t k_itIT_Locale[] = {
     0x78, 0x9C, 0x95, 0x56, 0xD1, 0x6A, 0xD4, 0x40, 0x14, 0x7D, 0x17, 0xFC,
     0x87, 0x21, 0x58, 0x50, 0x68, 0x3B, 0xFA, 0xD8, 0x92, 0x06, 0x4A, 0x5B,
     0xBB, 0xA5, 0x46, 0x96, 0xEE, 0xAA, 0xD8, 0x17, 0x99, 0x64, 0xA6, 0xD9,
@@ -853,7 +849,8 @@
     0x64, 0xE8, 0xC5, 0x1A, 0xD2, 0x4C, 0x8E, 0x20, 0xF9, 0x45, 0x2E, 0xA2,
     0xF7, 0x72, 0xC5, 0xD3, 0xCD, 0xE1, 0xDB, 0x3D, 0xAB, 0xA2, 0xFA, 0xD3,
     0x16, 0x96, 0xFF, 0x00, 0x20, 0x8B, 0xBE, 0xF7};
-const uint8_t g_ptBR_Locale[] = {
+
+const uint8_t k_ptBR_Locale[] = {
     0x78, 0x9C, 0x9D, 0x56, 0x3D, 0x6F, 0xD4, 0x40, 0x10, 0xAD, 0x83, 0xC4,
     0x7F, 0x58, 0x59, 0x89, 0x0E, 0xA4, 0x24, 0x86, 0x92, 0xC8, 0x67, 0xE9,
     0x92, 0x0B, 0x39, 0x94, 0x18, 0x9D, 0xCE, 0x11, 0x88, 0x34, 0x68, 0x6D,
@@ -922,7 +919,8 @@
     0xD6, 0x4E, 0xAA, 0x04, 0x67, 0xBD, 0x30, 0x9D, 0x0E, 0x82, 0x69, 0x51,
     0x48, 0xEC, 0x4B, 0x9A, 0x22, 0xB3, 0x24, 0xD3, 0xC1, 0x56, 0x39, 0x4F,
     0x45, 0xCE, 0xFE, 0xF7, 0xD5, 0x3F, 0x30, 0xBB, 0xD9, 0x9B};
-const uint8_t g_nlNL_Locale[] = {
+
+const uint8_t k_nlNL_Locale[] = {
     0x78, 0x9C, 0x95, 0x56, 0x4D, 0x4F, 0xDC, 0x30, 0x10, 0x3D, 0x53, 0xA9,
     0xFF, 0xC1, 0x8A, 0x8A, 0x68, 0x25, 0x96, 0x2D, 0x47, 0x50, 0x88, 0xB4,
     0x05, 0x0A, 0x15, 0x04, 0xAD, 0x58, 0xDA, 0xAA, 0x5C, 0x2A, 0x6F, 0x62,
@@ -988,7 +986,8 @@
     0x17, 0xAA, 0x4B, 0x74, 0xDE, 0x4B, 0x9B, 0xF1, 0x41, 0x34, 0x37, 0x91,
     0x6C, 0xB1, 0xE2, 0x19, 0x33, 0x2F, 0x32, 0x1B, 0x6D, 0xB5, 0xF3, 0xB4,
     0xC7, 0xF9, 0xA7, 0xBA, 0xF3, 0x0F, 0xD5, 0xFA, 0xC0, 0xFA};
-const uint8_t g_ruRU_Locale[] = {
+
+const uint8_t k_ruRU_Locale[] = {
     0x78, 0x9C, 0xAD, 0x57, 0x4D, 0x4F, 0x13, 0x41, 0x18, 0x3E, 0x63, 0xE2,
     0x7F, 0x98, 0x6C, 0x24, 0x68, 0x02, 0xAD, 0x1E, 0x25, 0x65, 0x13, 0x22,
     0x0A, 0x06, 0xD7, 0x90, 0x16, 0x34, 0x72, 0x31, 0xDB, 0xDD, 0xA1, 0x5D,
@@ -1065,8 +1064,8 @@
     0xB3, 0x85, 0xFA, 0x59, 0x2A, 0x7A, 0xFF, 0x3D, 0xC4, 0x3F, 0xDE, 0xCB,
     0x8B, 0xC4};
 
-std::unique_ptr<LocaleIface> GetLocaleFromBuffer(
-    pdfium::span<const uint8_t> src_span) {
+CXFA_XMLLocale* GetLocaleFromBuffer(cppgc::Heap* heap,
+                                    pdfium::span<const uint8_t> src_span) {
   if (src_span.empty())
     return nullptr;
 
@@ -1077,12 +1076,12 @@
   if (!output)
     return nullptr;
 
-  return CXFA_XMLLocale::Create(pdfium::make_span(output.get(), dwSize));
+  return CXFA_XMLLocale::Create(heap, pdfium::make_span(output.get(), dwSize));
 }
 
-uint16_t GetLanguage(WideString wsLanguage) {
+CXFA_LocaleMgr::LangID GetLanguageID(WideString wsLanguage) {
   if (wsLanguage.GetLength() < 2)
-    return FX_LANG_en_US;
+    return CXFA_LocaleMgr::LangID::k_en_US;
 
   wsLanguage.MakeLower();
   uint32_t dwIDFirst = wsLanguage[0] << 8 | wsLanguage[1];
@@ -1091,159 +1090,181 @@
   switch (dwIDFirst) {
     case FXBSTR_ID(0, 0, 'z', 'h'):
       if (dwIDSecond == FXBSTR_ID(0, 0, 'c', 'n'))
-        return FX_LANG_zh_CN;
+        return CXFA_LocaleMgr::LangID::k_zh_CN;
       if (dwIDSecond == FXBSTR_ID(0, 0, 't', 'w'))
-        return FX_LANG_zh_TW;
+        return CXFA_LocaleMgr::LangID::k_zh_TW;
       if (dwIDSecond == FXBSTR_ID(0, 0, 'h', 'k'))
-        return FX_LANG_zh_HK;
+        return CXFA_LocaleMgr::LangID::k_zh_HK;
       break;
     case FXBSTR_ID(0, 0, 'j', 'a'):
-      return FX_LANG_ja_JP;
+      return CXFA_LocaleMgr::LangID::k_ja_JP;
     case FXBSTR_ID(0, 0, 'k', 'o'):
-      return FX_LANG_ko_KR;
+      return CXFA_LocaleMgr::LangID::k_ko_KR;
     case FXBSTR_ID(0, 0, 'e', 'n'):
-      return dwIDSecond == FXBSTR_ID(0, 0, 'g', 'b') ? FX_LANG_en_GB
-                                                     : FX_LANG_en_US;
+      return dwIDSecond == FXBSTR_ID(0, 0, 'g', 'b')
+                 ? CXFA_LocaleMgr::LangID::k_en_GB
+                 : CXFA_LocaleMgr::LangID::k_en_US;
     case FXBSTR_ID(0, 0, 'd', 'e'):
-      return FX_LANG_de_DE;
+      return CXFA_LocaleMgr::LangID::k_de_DE;
     case FXBSTR_ID(0, 0, 'f', 'r'):
-      return FX_LANG_fr_FR;
+      return CXFA_LocaleMgr::LangID::k_fr_FR;
     case FXBSTR_ID(0, 0, 'e', 's'):
-      return dwIDSecond == FXBSTR_ID(0, 0, 'e', 's') ? FX_LANG_es_ES
-                                                     : FX_LANG_es_LA;
+      return dwIDSecond == FXBSTR_ID(0, 0, 'e', 's')
+                 ? CXFA_LocaleMgr::LangID::k_es_ES
+                 : CXFA_LocaleMgr::LangID::k_es_LA;
     case FXBSTR_ID(0, 0, 'i', 't'):
-      return FX_LANG_it_IT;
+      return CXFA_LocaleMgr::LangID::k_it_IT;
     case FXBSTR_ID(0, 0, 'p', 't'):
-      return FX_LANG_pt_BR;
+      return CXFA_LocaleMgr::LangID::k_pt_BR;
     case FXBSTR_ID(0, 0, 'n', 'l'):
-      return FX_LANG_nl_NL;
+      return CXFA_LocaleMgr::LangID::k_nl_NL;
     case FXBSTR_ID(0, 0, 'r', 'u'):
-      return FX_LANG_ru_RU;
+      return CXFA_LocaleMgr::LangID::k_ru_RU;
   }
-  return FX_LANG_en_US;
+  return CXFA_LocaleMgr::LangID::k_en_US;
 }
 
 }  // namespace
 
-CXFA_LocaleMgr::CXFA_LocaleMgr(CXFA_Node* pLocaleSet, WideString wsDeflcid)
-    : m_pDefLocale(GetLocaleByName(wsDeflcid)),
-      m_dwDeflcid(GetLanguage(wsDeflcid)) {
+CXFA_LocaleMgr::CXFA_LocaleMgr(cppgc::Heap* pHeap,
+                               CXFA_Node* pLocaleSet,
+                               WideString wsDeflcid)
+    : m_pHeap(pHeap),
+      m_pDefLocale(GetLocaleByName(wsDeflcid)),
+      m_eDeflcid(GetLanguageID(wsDeflcid)) {
   if (!pLocaleSet)
     return;
 
   for (CXFA_Node* pNodeLocale = pLocaleSet->GetFirstChild(); pNodeLocale;
        pNodeLocale = pNodeLocale->GetNextSibling()) {
-    m_LocaleArray.push_back(pdfium::MakeUnique<CXFA_NodeLocale>(pNodeLocale));
+    m_LocaleArray.push_back(cppgc::MakeGarbageCollected<CXFA_NodeLocale>(
+        pHeap->GetAllocationHandle(), pNodeLocale));
   }
 }
 
-CXFA_LocaleMgr::~CXFA_LocaleMgr() {}
+CXFA_LocaleMgr::~CXFA_LocaleMgr() = default;
 
-LocaleIface* CXFA_LocaleMgr::GetDefLocale() {
+void CXFA_LocaleMgr::Trace(cppgc::Visitor* visitor) const {
+  ContainerTrace(visitor, m_LocaleArray);
+  ContainerTrace(visitor, m_XMLLocaleArray);
+  visitor->Trace(m_pDefLocale);
+}
+
+GCedLocaleIface* CXFA_LocaleMgr::GetDefLocale() {
   if (m_pDefLocale)
-    return m_pDefLocale.Get();
+    return m_pDefLocale;
 
   if (!m_LocaleArray.empty())
-    return m_LocaleArray[0].get();
+    return m_LocaleArray[0];
 
   if (!m_XMLLocaleArray.empty())
-    return m_XMLLocaleArray[0].get();
+    return m_XMLLocaleArray[0];
 
-  std::unique_ptr<LocaleIface> locale(GetLocale(m_dwDeflcid));
-  m_pDefLocale = locale.get();
-  if (locale)
-    m_XMLLocaleArray.push_back(std::move(locale));
+  CXFA_XMLLocale* pLocale = GetLocale(m_eDeflcid);
+  if (pLocale)
+    m_XMLLocaleArray.push_back(pLocale);
 
-  return m_pDefLocale.Get();
+  m_pDefLocale = pLocale;
+  return m_pDefLocale;
 }
 
-std::unique_ptr<LocaleIface> CXFA_LocaleMgr::GetLocale(uint16_t lcid) {
+CXFA_XMLLocale* CXFA_LocaleMgr::GetLocale(LangID lcid) {
   switch (lcid) {
-    case FX_LANG_zh_CN:
-      return GetLocaleFromBuffer(g_zhCN_Locale);
-    case FX_LANG_zh_TW:
-      return GetLocaleFromBuffer(g_zhTW_Locale);
-    case FX_LANG_zh_HK:
-      return GetLocaleFromBuffer(g_zhHK_Locale);
-    case FX_LANG_ja_JP:
-      return GetLocaleFromBuffer(g_jaJP_Locale);
-    case FX_LANG_ko_KR:
-      return GetLocaleFromBuffer(g_koKR_Locale);
-    case FX_LANG_en_GB:
-      return GetLocaleFromBuffer(g_enGB_Locale);
-    case FX_LANG_es_LA:
-      return GetLocaleFromBuffer(g_esLA_Locale);
-    case FX_LANG_es_ES:
-      return GetLocaleFromBuffer(g_esES_Locale);
-    case FX_LANG_de_DE:
-      return GetLocaleFromBuffer(g_deDE_Loacale);
-    case FX_LANG_fr_FR:
-      return GetLocaleFromBuffer(g_frFR_Locale);
-    case FX_LANG_it_IT:
-      return GetLocaleFromBuffer(g_itIT_Locale);
-    case FX_LANG_pt_BR:
-      return GetLocaleFromBuffer(g_ptBR_Locale);
-    case FX_LANG_nl_NL:
-      return GetLocaleFromBuffer(g_nlNL_Locale);
-    case FX_LANG_ru_RU:
-      return GetLocaleFromBuffer(g_ruRU_Locale);
-    case FX_LANG_en_US:
-    default:
-      return GetLocaleFromBuffer(g_enUS_Locale);
+    case LangID::k_zh_CN:
+      return GetLocaleFromBuffer(m_pHeap, k_zhCN_Locale);
+    case LangID::k_zh_TW:
+      return GetLocaleFromBuffer(m_pHeap, k_zhTW_Locale);
+    case LangID::k_zh_HK:
+      return GetLocaleFromBuffer(m_pHeap, k_zhHK_Locale);
+    case LangID::k_ja_JP:
+      return GetLocaleFromBuffer(m_pHeap, k_jaJP_Locale);
+    case LangID::k_ko_KR:
+      return GetLocaleFromBuffer(m_pHeap, k_koKR_Locale);
+    case LangID::k_en_GB:
+      return GetLocaleFromBuffer(m_pHeap, k_enGB_Locale);
+    case LangID::k_es_LA:
+      return GetLocaleFromBuffer(m_pHeap, k_esLA_Locale);
+    case LangID::k_es_ES:
+      return GetLocaleFromBuffer(m_pHeap, k_esES_Locale);
+    case LangID::k_de_DE:
+      return GetLocaleFromBuffer(m_pHeap, k_deDE_Loacale);
+    case LangID::k_fr_FR:
+      return GetLocaleFromBuffer(m_pHeap, k_frFR_Locale);
+    case LangID::k_it_IT:
+      return GetLocaleFromBuffer(m_pHeap, k_itIT_Locale);
+    case LangID::k_pt_BR:
+      return GetLocaleFromBuffer(m_pHeap, k_ptBR_Locale);
+    case LangID::k_nl_NL:
+      return GetLocaleFromBuffer(m_pHeap, k_nlNL_Locale);
+    case LangID::k_ru_RU:
+      return GetLocaleFromBuffer(m_pHeap, k_ruRU_Locale);
+    case LangID::k_en_US:
+      return GetLocaleFromBuffer(m_pHeap, k_enUS_Locale);
   }
 }
 
-LocaleIface* CXFA_LocaleMgr::GetLocaleByName(const WideString& wsLocaleName) {
+GCedLocaleIface* CXFA_LocaleMgr::GetLocaleByName(
+    const WideString& wsLocaleName) {
   for (size_t i = 0; i < m_LocaleArray.size(); i++) {
-    LocaleIface* pLocale = m_LocaleArray[i].get();
+    GCedLocaleIface* pLocale = m_LocaleArray[i];
     if (pLocale->GetName() == wsLocaleName)
       return pLocale;
   }
   if (wsLocaleName.GetLength() < 2)
     return nullptr;
+
   for (size_t i = 0; i < m_XMLLocaleArray.size(); i++) {
-    LocaleIface* pLocale = m_XMLLocaleArray[i].get();
+    GCedLocaleIface* pLocale = m_XMLLocaleArray[i];
     if (pLocale->GetName() == wsLocaleName)
       return pLocale;
   }
 
-  std::unique_ptr<LocaleIface> pLocale(GetLocale(GetLanguage(wsLocaleName)));
-  LocaleIface* pRetLocale = pLocale.get();
-  if (pLocale)
-    m_XMLLocaleArray.push_back(std::move(pLocale));
-  return pRetLocale;
+  CXFA_XMLLocale* pLocale = GetLocale(GetLanguageID(wsLocaleName));
+  if (!pLocale)
+    return nullptr;
+
+  m_XMLLocaleArray.push_back(pLocale);
+  return pLocale;
 }
 
-void CXFA_LocaleMgr::SetDefLocale(LocaleIface* pLocale) {
+void CXFA_LocaleMgr::SetDefLocale(GCedLocaleIface* pLocale) {
   m_pDefLocale = pLocale;
 }
 
-WideString CXFA_LocaleMgr::GetConfigLocaleName(CXFA_Node* pConfig) {
-  if (m_hasSetLocaleName)
+absl::optional<WideString> CXFA_LocaleMgr::GetConfigLocaleName(
+    CXFA_Node* pConfig) const {
+  if (m_bConfigLocaleCached)
     return m_wsConfigLocale;
 
-  m_hasSetLocaleName = true;
-  m_wsConfigLocale.clear();
+  DCHECK(!m_wsConfigLocale.has_value());
+  m_bConfigLocaleCached = true;
   if (!pConfig)
     return m_wsConfigLocale;
 
-  CXFA_Node* pChildfConfig =
+  CXFA_Node* pChildConfig =
       pConfig->GetFirstChildByClass<CXFA_Acrobat>(XFA_Element::Acrobat);
-  if (!pChildfConfig) {
-    pChildfConfig =
+  if (!pChildConfig) {
+    pChildConfig =
         pConfig->GetFirstChildByClass<CXFA_Present>(XFA_Element::Present);
+    if (!pChildConfig)
+      return m_wsConfigLocale;
   }
-  CXFA_Common* pCommon = pChildfConfig
-                             ? pChildfConfig->GetFirstChildByClass<CXFA_Common>(
-                                   XFA_Element::Common)
-                             : nullptr;
+
+  CXFA_Common* pCommon =
+      pChildConfig->GetFirstChildByClass<CXFA_Common>(XFA_Element::Common);
+  if (!pCommon)
+    return m_wsConfigLocale;
+
   CXFA_Locale* pLocale =
-      pCommon ? pCommon->GetFirstChildByClass<CXFA_Locale>(XFA_Element::Locale)
-              : nullptr;
-  if (pLocale) {
-    m_wsConfigLocale = pLocale->JSObject()
-                           ->TryCData(XFA_Attribute::Value, false)
-                           .value_or(WideString());
-  }
+      pCommon->GetFirstChildByClass<CXFA_Locale>(XFA_Element::Locale);
+  if (!pLocale)
+    return m_wsConfigLocale;
+
+  absl::optional<WideString> wsMaybeLocale =
+      pLocale->JSObject()->TryCData(XFA_Attribute::Value, false);
+  if (!wsMaybeLocale.has_value() || wsMaybeLocale.value().IsEmpty())
+    return m_wsConfigLocale;
+
+  m_wsConfigLocale = wsMaybeLocale;
   return m_wsConfigLocale;
 }
diff --git a/xfa/fxfa/parser/cxfa_localemgr.h b/xfa/fxfa/parser/cxfa_localemgr.h
index 9370988..d06f211 100644
--- a/xfa/fxfa/parser/cxfa_localemgr.h
+++ b/xfa/fxfa/parser/cxfa_localemgr.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,39 +7,77 @@
 #ifndef XFA_FXFA_PARSER_CXFA_LOCALEMGR_H_
 #define XFA_FXFA_PARSER_CXFA_LOCALEMGR_H_
 
-#include <memory>
 #include <vector>
 
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxcrt/widestring.h"
+#include "fxjs/gc/heap.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
 #include "xfa/fgas/crt/locale_mgr_iface.h"
+#include "xfa/fxfa/parser/gced_locale_iface.h"
 
 class CXFA_Node;
-class LocaleIface;
+class CXFA_NodeLocale;
+class CXFA_XMLLocale;
 
-class CXFA_LocaleMgr : public LocaleMgrIface {
+class CXFA_LocaleMgr final : public cppgc::GarbageCollected<CXFA_LocaleMgr>,
+                             public LocaleMgrIface {
  public:
-  CXFA_LocaleMgr(CXFA_Node* pLocaleSet, WideString wsDeflcid);
+  enum class LangID : uint16_t {
+    k_zh_HK = 0x0c04,
+    k_zh_CN = 0x0804,
+    k_zh_TW = 0x0404,
+    k_nl_NL = 0x0413,
+    k_en_GB = 0x0809,
+    k_en_US = 0x0409,
+    k_fr_FR = 0x040c,
+    k_de_DE = 0x0407,
+    k_it_IT = 0x0410,
+    k_ja_JP = 0x0411,
+    k_ko_KR = 0x0412,
+    k_pt_BR = 0x0416,
+    k_ru_RU = 0x0419,
+    k_es_LA = 0x080a,
+    k_es_ES = 0x0c0a,
+  };
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_LocaleMgr() override;
 
-  LocaleIface* GetDefLocale() override;
-  LocaleIface* GetLocaleByName(const WideString& wsLocaleName) override;
+  void Trace(cppgc::Visitor* visitor) const;
 
-  void SetDefLocale(LocaleIface* pLocale);
-  WideString GetConfigLocaleName(CXFA_Node* pConfig);
+  GCedLocaleIface* GetDefLocale() override;
+  GCedLocaleIface* GetLocaleByName(const WideString& wsLocaleName) override;
+
+  void SetDefLocale(GCedLocaleIface* pLocale);
+  absl::optional<WideString> GetConfigLocaleName(CXFA_Node* pConfig) const;
 
  private:
-  std::unique_ptr<LocaleIface> GetLocale(uint16_t lcid);
+  CXFA_LocaleMgr(cppgc::Heap* pHeap,
+                 CXFA_Node* pLocaleSet,
+                 WideString wsDeflcid);
 
-  std::vector<std::unique_ptr<LocaleIface>> m_LocaleArray;
-  std::vector<std::unique_ptr<LocaleIface>> m_XMLLocaleArray;
+  // May allocate a new object on the cppgc heap.
+  CXFA_XMLLocale* GetLocale(LangID lcid);
 
-  // Owned by m_LocaleArray or m_XMLLocaleArray.
-  UnownedPtr<LocaleIface> m_pDefLocale;
+  UnownedPtr<cppgc::Heap> m_pHeap;
+  std::vector<cppgc::Member<CXFA_NodeLocale>> m_LocaleArray;
+  std::vector<cppgc::Member<CXFA_XMLLocale>> m_XMLLocaleArray;
+  cppgc::Member<GCedLocaleIface> m_pDefLocale;
 
-  WideString m_wsConfigLocale;
-  uint16_t m_dwDeflcid;
-  bool m_hasSetLocaleName = false;
+  // Note: three possiblities
+  // 1. we might never have tried to determine |m_wsConfigLocale|.
+  // 2. we might have tried but gotten nothing and want to continue
+  //    to return nothing without ever trying again.
+  // 3. we might have tried and gotten something.
+  // So |m_bConfigLocaleCached| indicates whether we've already tried,
+  // and |m_wsConfigLocale| is the possibly nothing we got if we tried.
+  mutable absl::optional<WideString> m_wsConfigLocale;
+  mutable bool m_bConfigLocaleCached = false;
+
+  LangID m_eDeflcid;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_LOCALEMGR_H_
diff --git a/xfa/fxfa/parser/cxfa_localeset.cpp b/xfa/fxfa/parser/cxfa_localeset.cpp
index 527006c..e95e951 100644
--- a/xfa/fxfa/parser/cxfa_localeset.cpp
+++ b/xfa/fxfa/parser/cxfa_localeset.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_localeset.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_LocaleSet::CXFA_LocaleSet(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Config | XFA_XDPPACKET_LocaleSet),
+                {XFA_XDPPACKET::kConfig, XFA_XDPPACKET::kLocaleSet},
                 XFA_ObjectType::ModelNode,
                 XFA_Element::LocaleSet,
                 {},
                 kLocaleSetAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_LocaleSet::~CXFA_LocaleSet() = default;
diff --git a/xfa/fxfa/parser/cxfa_localeset.h b/xfa/fxfa/parser/cxfa_localeset.h
index dc6521a..493b964 100644
--- a/xfa/fxfa/parser/cxfa_localeset.h
+++ b/xfa/fxfa/parser/cxfa_localeset.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_LocaleSet final : public CXFA_Node {
  public:
-  CXFA_LocaleSet(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_LocaleSet() override;
+
+ private:
+  CXFA_LocaleSet(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_LOCALESET_H_
diff --git a/xfa/fxfa/parser/cxfa_localevalue.cpp b/xfa/fxfa/parser/cxfa_localevalue.cpp
index 0663d38..b3b0970 100644
--- a/xfa/fxfa/parser/cxfa_localevalue.cpp
+++ b/xfa/fxfa/parser/cxfa_localevalue.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,14 +6,16 @@
 
 #include "xfa/fxfa/parser/cxfa_localevalue.h"
 
-#include <cwchar>
+#include <wchar.h>
+
+#include <memory>
 #include <utility>
 #include <vector>
 
+#include "core/fxcrt/cfx_datetime.h"
 #include "core/fxcrt/fx_extension.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/span.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.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"
@@ -21,27 +23,29 @@
 
 namespace {
 
-FX_LOCALECATEGORY ValueCategory(FX_LOCALECATEGORY eCategory,
-                                uint32_t dwValueType) {
-  if (eCategory != FX_LOCALECATEGORY_Unknown)
+CFGAS_StringFormatter::Category ValueCategory(
+    CFGAS_StringFormatter::Category eCategory,
+    CXFA_LocaleValue::ValueType eValueType) {
+  if (eCategory != CFGAS_StringFormatter::Category::kUnknown)
     return eCategory;
 
-  switch (dwValueType) {
-    case XFA_VT_BOOLEAN:
-    case XFA_VT_INTEGER:
-    case XFA_VT_DECIMAL:
-    case XFA_VT_FLOAT:
-      return FX_LOCALECATEGORY_Num;
-    case XFA_VT_TEXT:
-      return FX_LOCALECATEGORY_Text;
-    case XFA_VT_DATE:
-      return FX_LOCALECATEGORY_Date;
-    case XFA_VT_TIME:
-      return FX_LOCALECATEGORY_Time;
-    case XFA_VT_DATETIME:
-      return FX_LOCALECATEGORY_DateTime;
+  switch (eValueType) {
+    case CXFA_LocaleValue::ValueType::kBoolean:
+    case CXFA_LocaleValue::ValueType::kInteger:
+    case CXFA_LocaleValue::ValueType::kDecimal:
+    case CXFA_LocaleValue::ValueType::kFloat:
+      return CFGAS_StringFormatter::Category::kNum;
+    case CXFA_LocaleValue::ValueType::kText:
+      return CFGAS_StringFormatter::Category::kText;
+    case CXFA_LocaleValue::ValueType::kDate:
+      return CFGAS_StringFormatter::Category::kDate;
+    case CXFA_LocaleValue::ValueType::kTime:
+      return CFGAS_StringFormatter::Category::kTime;
+    case CXFA_LocaleValue::ValueType::kDateTime:
+      return CFGAS_StringFormatter::Category::kDateTime;
+    default:
+      return CFGAS_StringFormatter::Category::kUnknown;
   }
-  return FX_LOCALECATEGORY_Unknown;
 }
 
 bool ValueSplitDateTime(const WideString& wsDateTime,
@@ -64,8 +68,10 @@
 }
 
 class ScopedLocale {
+  CPPGC_STACK_ALLOCATED();  // Raw/Unowned pointers allowed.
+
  public:
-  ScopedLocale(CXFA_LocaleMgr* pLocaleMgr, LocaleIface* pNewLocale)
+  ScopedLocale(CXFA_LocaleMgr* pLocaleMgr, GCedLocaleIface* pNewLocale)
       : m_pLocaleMgr(pLocaleMgr),
         m_pNewLocale(pNewLocale),
         m_pOrigLocale(pNewLocale ? m_pLocaleMgr->GetDefLocale() : nullptr) {
@@ -82,35 +88,35 @@
   ScopedLocale& operator=(const ScopedLocale& that) = delete;
 
  private:
-  UnownedPtr<CXFA_LocaleMgr> const m_pLocaleMgr;
-  LocaleIface* const m_pNewLocale;
-  LocaleIface* const m_pOrigLocale;
+  UnownedPtr<CXFA_LocaleMgr> const m_pLocaleMgr;    // Ok, stack-only.
+  UnownedPtr<GCedLocaleIface> const m_pNewLocale;   // Ok, stack-only.
+  UnownedPtr<GCedLocaleIface> const m_pOrigLocale;  // Ok, stack-only.
 };
 
 }  // namespace
 
 CXFA_LocaleValue::CXFA_LocaleValue() = default;
 
-CXFA_LocaleValue::CXFA_LocaleValue(uint32_t dwType, CXFA_LocaleMgr* pLocaleMgr)
+CXFA_LocaleValue::CXFA_LocaleValue(ValueType eType, CXFA_LocaleMgr* pLocaleMgr)
     : m_pLocaleMgr(pLocaleMgr),
-      m_dwType(dwType),
-      m_bValid(m_dwType != XFA_VT_NULL) {}
+      m_eType(eType),
+      m_bValid(m_eType != ValueType::kNull) {}
 
-CXFA_LocaleValue::CXFA_LocaleValue(uint32_t dwType,
+CXFA_LocaleValue::CXFA_LocaleValue(ValueType eType,
                                    const WideString& wsValue,
                                    CXFA_LocaleMgr* pLocaleMgr)
     : m_pLocaleMgr(pLocaleMgr),
       m_wsValue(wsValue),
-      m_dwType(dwType),
-      m_bValid(ValidateCanonicalValue(wsValue, dwType)) {}
+      m_eType(eType),
+      m_bValid(ValidateCanonicalValue(wsValue, eType)) {}
 
-CXFA_LocaleValue::CXFA_LocaleValue(uint32_t dwType,
+CXFA_LocaleValue::CXFA_LocaleValue(ValueType eType,
                                    const WideString& wsValue,
                                    const WideString& wsFormat,
-                                   LocaleIface* pLocale,
+                                   GCedLocaleIface* pLocale,
                                    CXFA_LocaleMgr* pLocaleMgr)
     : m_pLocaleMgr(pLocaleMgr),
-      m_dwType(dwType),
+      m_eType(eType),
       m_bValid(ParsePatternValue(wsValue, wsFormat, pLocale)) {}
 
 CXFA_LocaleValue::CXFA_LocaleValue(const CXFA_LocaleValue& that) = default;
@@ -122,12 +128,12 @@
 
 bool CXFA_LocaleValue::ValidateValue(const WideString& wsValue,
                                      const WideString& wsPattern,
-                                     LocaleIface* pLocale,
+                                     GCedLocaleIface* pLocale,
                                      WideString* pMatchFormat) {
   if (!m_pLocaleMgr)
     return false;
 
-  ScopedLocale scoped_locale(m_pLocaleMgr.Get(), pLocale);
+  ScopedLocale scoped_locale(m_pLocaleMgr, pLocale);
   std::vector<WideString> wsPatterns =
       CFGAS_StringFormatter::SplitOnBars(wsPattern);
 
@@ -136,59 +142,67 @@
   size_t i = 0;
   for (; !bRet && i < wsPatterns.size(); i++) {
     const WideString& wsFormat = wsPatterns[i];
-    auto pFormat =
-        pdfium::MakeUnique<CFGAS_StringFormatter>(m_pLocaleMgr.Get(), wsFormat);
-    switch (ValueCategory(pFormat->GetCategory(), m_dwType)) {
-      case FX_LOCALECATEGORY_Null:
+    auto pFormat = std::make_unique<CFGAS_StringFormatter>(wsFormat);
+    switch (ValueCategory(pFormat->GetCategory(), m_eType)) {
+      case CFGAS_StringFormatter::Category::kNull:
         bRet = pFormat->ParseNull(wsValue);
         if (!bRet)
           bRet = wsValue.IsEmpty();
         break;
-      case FX_LOCALECATEGORY_Zero:
+      case CFGAS_StringFormatter::Category::kZero:
         bRet = pFormat->ParseZero(wsValue);
         if (!bRet)
           bRet = wsValue.EqualsASCII("0");
         break;
-      case FX_LOCALECATEGORY_Num: {
+      case CFGAS_StringFormatter::Category::kNum: {
         WideString fNum;
-        bRet = pFormat->ParseNum(wsValue, &fNum);
+        bRet = pFormat->ParseNum(m_pLocaleMgr, wsValue, &fNum);
         if (!bRet)
-          bRet = pFormat->FormatNum(wsValue, &wsOutput);
+          bRet = pFormat->FormatNum(m_pLocaleMgr, wsValue, &wsOutput);
         break;
       }
-      case FX_LOCALECATEGORY_Text:
+      case CFGAS_StringFormatter::Category::kText:
         bRet = pFormat->ParseText(wsValue, &wsOutput);
         wsOutput.clear();
         if (!bRet)
           bRet = pFormat->FormatText(wsValue, &wsOutput);
         break;
-      case FX_LOCALECATEGORY_Date: {
+      case CFGAS_StringFormatter::Category::kDate: {
         CFX_DateTime dt;
         bRet = ValidateCanonicalDate(wsValue, &dt);
         if (!bRet) {
-          bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_Date, &dt);
+          bRet = pFormat->ParseDateTime(
+              m_pLocaleMgr, wsValue, CFGAS_StringFormatter::DateTimeType::kDate,
+              &dt);
           if (!bRet) {
-            bRet = pFormat->FormatDateTime(wsValue, FX_DATETIMETYPE_Date,
-                                           &wsOutput);
+            bRet = pFormat->FormatDateTime(
+                m_pLocaleMgr, wsValue,
+                CFGAS_StringFormatter::DateTimeType::kDate, &wsOutput);
           }
         }
         break;
       }
-      case FX_LOCALECATEGORY_Time: {
+      case CFGAS_StringFormatter::Category::kTime: {
         CFX_DateTime dt;
-        bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_Time, &dt);
+        bRet = pFormat->ParseDateTime(
+            m_pLocaleMgr, wsValue, CFGAS_StringFormatter::DateTimeType::kTime,
+            &dt);
         if (!bRet) {
-          bRet =
-              pFormat->FormatDateTime(wsValue, FX_DATETIMETYPE_Time, &wsOutput);
+          bRet = pFormat->FormatDateTime(
+              m_pLocaleMgr, wsValue, CFGAS_StringFormatter::DateTimeType::kTime,
+              &wsOutput);
         }
         break;
       }
-      case FX_LOCALECATEGORY_DateTime: {
+      case CFGAS_StringFormatter::Category::kDateTime: {
         CFX_DateTime dt;
-        bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_DateTime, &dt);
+        bRet = pFormat->ParseDateTime(
+            m_pLocaleMgr, wsValue,
+            CFGAS_StringFormatter::DateTimeType::kDateTime, &dt);
         if (!bRet) {
-          bRet = pFormat->FormatDateTime(wsValue, FX_DATETIMETYPE_DateTime,
-                                         &wsOutput);
+          bRet = pFormat->FormatDateTime(
+              m_pLocaleMgr, wsValue,
+              CFGAS_StringFormatter::DateTimeType::kDateTime, &wsOutput);
         }
         break;
       }
@@ -203,8 +217,10 @@
 }
 
 double CXFA_LocaleValue::GetDoubleNum() const {
-  if (!m_bValid || (m_dwType != XFA_VT_BOOLEAN && m_dwType != XFA_VT_INTEGER &&
-                    m_dwType != XFA_VT_DECIMAL && m_dwType != XFA_VT_FLOAT)) {
+  if (!m_bValid || (m_eType != CXFA_LocaleValue::ValueType::kBoolean &&
+                    m_eType != CXFA_LocaleValue::ValueType::kInteger &&
+                    m_eType != CXFA_LocaleValue::ValueType::kDecimal &&
+                    m_eType != CXFA_LocaleValue::ValueType::kFloat)) {
     return 0;
   }
 
@@ -212,7 +228,7 @@
 }
 
 CFX_DateTime CXFA_LocaleValue::GetDate() const {
-  if (!m_bValid || m_dwType != XFA_VT_DATE)
+  if (!m_bValid || m_eType != CXFA_LocaleValue::ValueType::kDate)
     return CFX_DateTime();
 
   CFX_DateTime dt;
@@ -221,7 +237,7 @@
 }
 
 CFX_DateTime CXFA_LocaleValue::GetTime() const {
-  if (!m_bValid || m_dwType != XFA_VT_TIME)
+  if (!m_bValid || m_eType != CXFA_LocaleValue::ValueType::kTime)
     return CFX_DateTime();
 
   CFX_DateTime dt;
@@ -230,14 +246,14 @@
 }
 
 bool CXFA_LocaleValue::SetDate(const CFX_DateTime& d) {
-  m_dwType = XFA_VT_DATE;
+  m_eType = CXFA_LocaleValue::ValueType::kDate;
   m_wsValue = WideString::Format(L"%04d-%02d-%02d", d.GetYear(), d.GetMonth(),
                                  d.GetDay());
   return true;
 }
 
 bool CXFA_LocaleValue::SetTime(const CFX_DateTime& t) {
-  m_dwType = XFA_VT_TIME;
+  m_eType = CXFA_LocaleValue::ValueType::kTime;
   m_wsValue = WideString::Format(L"%02d:%02d:%02d", t.GetHour(), t.GetMinute(),
                                  t.GetSecond());
   if (t.GetMillisecond() > 0)
@@ -246,7 +262,7 @@
 }
 
 bool CXFA_LocaleValue::SetDateTime(const CFX_DateTime& dt) {
-  m_dwType = XFA_VT_DATETIME;
+  m_eType = CXFA_LocaleValue::ValueType::kDateTime;
   m_wsValue = WideString::Format(L"%04d-%02d-%02dT%02d:%02d:%02d", dt.GetYear(),
                                  dt.GetMonth(), dt.GetDay(), dt.GetHour(),
                                  dt.GetMinute(), dt.GetSecond());
@@ -257,8 +273,8 @@
 
 bool CXFA_LocaleValue::FormatPatterns(WideString& wsResult,
                                       const WideString& wsFormat,
-                                      LocaleIface* pLocale,
-                                      XFA_VALUEPICTURE eValueType) const {
+                                      GCedLocaleIface* pLocale,
+                                      XFA_ValuePicture eValueType) const {
   wsResult.clear();
   for (const auto& pattern : CFGAS_StringFormatter::SplitOnBars(wsFormat)) {
     if (FormatSinglePattern(wsResult, pattern, pLocale, eValueType))
@@ -269,51 +285,54 @@
 
 bool CXFA_LocaleValue::FormatSinglePattern(WideString& wsResult,
                                            const WideString& wsFormat,
-                                           LocaleIface* pLocale,
-                                           XFA_VALUEPICTURE eValueType) const {
+                                           GCedLocaleIface* pLocale,
+                                           XFA_ValuePicture eValueType) const {
   if (!m_pLocaleMgr)
     return false;
 
-  ScopedLocale scoped_locale(m_pLocaleMgr.Get(), pLocale);
-
+  ScopedLocale scoped_locale(m_pLocaleMgr, pLocale);
   wsResult.clear();
+
   bool bRet = false;
-  auto pFormat =
-      pdfium::MakeUnique<CFGAS_StringFormatter>(m_pLocaleMgr.Get(), wsFormat);
-  FX_LOCALECATEGORY eCategory = ValueCategory(pFormat->GetCategory(), m_dwType);
+  auto pFormat = std::make_unique<CFGAS_StringFormatter>(wsFormat);
+  CFGAS_StringFormatter::Category eCategory =
+      ValueCategory(pFormat->GetCategory(), m_eType);
   switch (eCategory) {
-    case FX_LOCALECATEGORY_Null:
+    case CFGAS_StringFormatter::Category::kNull:
       if (m_wsValue.IsEmpty())
         bRet = pFormat->FormatNull(&wsResult);
       break;
-    case FX_LOCALECATEGORY_Zero:
+    case CFGAS_StringFormatter::Category::kZero:
       if (m_wsValue.EqualsASCII("0"))
         bRet = pFormat->FormatZero(&wsResult);
       break;
-    case FX_LOCALECATEGORY_Num:
-      bRet = pFormat->FormatNum(m_wsValue, &wsResult);
+    case CFGAS_StringFormatter::Category::kNum:
+      bRet = pFormat->FormatNum(m_pLocaleMgr, m_wsValue, &wsResult);
       break;
-    case FX_LOCALECATEGORY_Text:
+    case CFGAS_StringFormatter::Category::kText:
       bRet = pFormat->FormatText(m_wsValue, &wsResult);
       break;
-    case FX_LOCALECATEGORY_Date:
-      bRet =
-          pFormat->FormatDateTime(m_wsValue, FX_DATETIMETYPE_Date, &wsResult);
-      break;
-    case FX_LOCALECATEGORY_Time:
-      bRet =
-          pFormat->FormatDateTime(m_wsValue, FX_DATETIMETYPE_Time, &wsResult);
-      break;
-    case FX_LOCALECATEGORY_DateTime:
-      bRet = pFormat->FormatDateTime(m_wsValue, FX_DATETIMETYPE_DateTime,
+    case CFGAS_StringFormatter::Category::kDate:
+      bRet = pFormat->FormatDateTime(m_pLocaleMgr, m_wsValue,
+                                     CFGAS_StringFormatter::DateTimeType::kDate,
                                      &wsResult);
       break;
+    case CFGAS_StringFormatter::Category::kTime:
+      bRet = pFormat->FormatDateTime(m_pLocaleMgr, m_wsValue,
+                                     CFGAS_StringFormatter::DateTimeType::kTime,
+                                     &wsResult);
+      break;
+    case CFGAS_StringFormatter::Category::kDateTime:
+      bRet = pFormat->FormatDateTime(
+          m_pLocaleMgr, m_wsValue,
+          CFGAS_StringFormatter::DateTimeType::kDateTime, &wsResult);
+      break;
     default:
       wsResult = m_wsValue;
       bRet = true;
   }
-  if (!bRet && (eCategory != FX_LOCALECATEGORY_Num ||
-                eValueType != XFA_VALUEPICTURE_Display)) {
+  if (!bRet && (eCategory != CFGAS_StringFormatter::Category::kNum ||
+                eValueType != XFA_ValuePicture::kDisplay)) {
     wsResult = m_wsValue;
   }
 
@@ -321,13 +340,13 @@
 }
 
 bool CXFA_LocaleValue::ValidateCanonicalValue(const WideString& wsValue,
-                                              uint32_t dwVType) {
+                                              ValueType eType) {
   if (wsValue.IsEmpty())
     return true;
 
   CFX_DateTime dt;
-  switch (dwVType) {
-    case XFA_VT_DATE: {
+  switch (eType) {
+    case ValueType::kDate: {
       if (ValidateCanonicalDate(wsValue, &dt))
         return true;
 
@@ -339,7 +358,7 @@
       }
       return false;
     }
-    case XFA_VT_TIME: {
+    case ValueType::kTime: {
       if (ValidateCanonicalTime(wsValue))
         return true;
 
@@ -351,13 +370,16 @@
       }
       return false;
     }
-    case XFA_VT_DATETIME: {
+    case ValueType::kDateTime: {
       WideString wsDate, wsTime;
       if (ValueSplitDateTime(wsValue, wsDate, wsTime) &&
           ValidateCanonicalDate(wsDate, &dt) && ValidateCanonicalTime(wsTime)) {
         return true;
       }
     } break;
+    default: {
+      break;
+    }
   }
   return true;
 }
@@ -542,60 +564,65 @@
 
 bool CXFA_LocaleValue::ParsePatternValue(const WideString& wsValue,
                                          const WideString& wsPattern,
-                                         LocaleIface* pLocale) {
+                                         GCedLocaleIface* pLocale) {
   if (!m_pLocaleMgr)
     return false;
 
   std::vector<WideString> wsPatterns =
       CFGAS_StringFormatter::SplitOnBars(wsPattern);
 
-  ScopedLocale scoped_locale(m_pLocaleMgr.Get(), pLocale);
+  ScopedLocale scoped_locale(m_pLocaleMgr, pLocale);
   bool bRet = false;
   for (size_t i = 0; !bRet && i < wsPatterns.size(); i++) {
     const WideString& wsFormat = wsPatterns[i];
-    auto pFormat =
-        pdfium::MakeUnique<CFGAS_StringFormatter>(m_pLocaleMgr.Get(), wsFormat);
-    switch (ValueCategory(pFormat->GetCategory(), m_dwType)) {
-      case FX_LOCALECATEGORY_Null:
+    auto pFormat = std::make_unique<CFGAS_StringFormatter>(wsFormat);
+    switch (ValueCategory(pFormat->GetCategory(), m_eType)) {
+      case CFGAS_StringFormatter::Category::kNull:
         bRet = pFormat->ParseNull(wsValue);
         if (bRet)
           m_wsValue.clear();
         break;
-      case FX_LOCALECATEGORY_Zero:
+      case CFGAS_StringFormatter::Category::kZero:
         bRet = pFormat->ParseZero(wsValue);
         if (bRet)
           m_wsValue = L"0";
         break;
-      case FX_LOCALECATEGORY_Num: {
+      case CFGAS_StringFormatter::Category::kNum: {
         WideString fNum;
-        bRet = pFormat->ParseNum(wsValue, &fNum);
+        bRet = pFormat->ParseNum(m_pLocaleMgr, wsValue, &fNum);
         if (bRet)
           m_wsValue = std::move(fNum);
         break;
       }
-      case FX_LOCALECATEGORY_Text:
+      case CFGAS_StringFormatter::Category::kText:
         bRet = pFormat->ParseText(wsValue, &m_wsValue);
         break;
-      case FX_LOCALECATEGORY_Date: {
+      case CFGAS_StringFormatter::Category::kDate: {
         CFX_DateTime dt;
         bRet = ValidateCanonicalDate(wsValue, &dt);
         if (!bRet) {
-          bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_Date, &dt);
+          bRet = pFormat->ParseDateTime(
+              m_pLocaleMgr, wsValue, CFGAS_StringFormatter::DateTimeType::kDate,
+              &dt);
         }
         if (bRet)
           SetDate(dt);
         break;
       }
-      case FX_LOCALECATEGORY_Time: {
+      case CFGAS_StringFormatter::Category::kTime: {
         CFX_DateTime dt;
-        bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_Time, &dt);
+        bRet = pFormat->ParseDateTime(
+            m_pLocaleMgr, wsValue, CFGAS_StringFormatter::DateTimeType::kTime,
+            &dt);
         if (bRet)
           SetTime(dt);
         break;
       }
-      case FX_LOCALECATEGORY_DateTime: {
+      case CFGAS_StringFormatter::Category::kDateTime: {
         CFX_DateTime dt;
-        bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_DateTime, &dt);
+        bRet = pFormat->ParseDateTime(
+            m_pLocaleMgr, wsValue,
+            CFGAS_StringFormatter::DateTimeType::kDateTime, &dt);
         if (bRet)
           SetDateTime(dt);
         break;
@@ -615,9 +642,9 @@
 void CXFA_LocaleValue::GetNumericFormat(WideString& wsFormat,
                                         int32_t nIntLen,
                                         int32_t nDecLen) {
-  ASSERT(wsFormat.IsEmpty());
-  ASSERT(nIntLen >= -1);
-  ASSERT(nDecLen >= -1);
+  DCHECK(wsFormat.IsEmpty());
+  DCHECK(nIntLen >= -1);
+  DCHECK(nDecLen >= -1);
 
   int32_t nTotalLen = (nIntLen >= 0 ? nIntLen : 2) + 1 +
                       (nDecLen >= 0 ? nDecLen : 2) + (nDecLen == 0 ? 0 : 1);
@@ -654,14 +681,14 @@
 
 bool CXFA_LocaleValue::ValidateNumericTemp(const WideString& wsNumeric,
                                            const WideString& wsFormat,
-                                           LocaleIface* pLocale) {
+                                           GCedLocaleIface* pLocale) {
   if (wsFormat.IsEmpty() || wsNumeric.IsEmpty())
     return true;
 
   pdfium::span<const wchar_t> spNum = wsNumeric.span();
   pdfium::span<const wchar_t> spFmt = wsFormat.span();
-  int32_t n = 0;
-  int32_t nf = 0;
+  size_t n = 0;
+  size_t nf = 0;
   wchar_t c = spNum[n];
   wchar_t cf = spFmt[nf];
   if (cf == L's') {
@@ -671,8 +698,8 @@
   }
 
   bool bLimit = true;
-  int32_t nCount = wsNumeric.GetLength();
-  int32_t nCountFmt = wsFormat.GetLength();
+  size_t nCount = wsNumeric.GetLength();
+  size_t nCountFmt = wsFormat.GetLength();
   while (n < nCount && (!bLimit || nf < nCountFmt) &&
          FXSYS_IsDecimalDigit(c = spNum[n])) {
     if (bLimit) {
@@ -691,7 +718,7 @@
     return false;
 
   while (nf < nCountFmt && (cf = spFmt[nf]) != L'.') {
-    ASSERT(cf == L'z' || cf == L'*');
+    DCHECK(cf == L'z' || cf == L'*');
     ++nf;
   }
 
diff --git a/xfa/fxfa/parser/cxfa_localevalue.h b/xfa/fxfa/parser/cxfa_localevalue.h
index 2005905..d7809c0 100644
--- a/xfa/fxfa/parser/cxfa_localevalue.h
+++ b/xfa/fxfa/parser/cxfa_localevalue.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,36 +7,42 @@
 #ifndef XFA_FXFA_PARSER_CXFA_LOCALEVALUE_H_
 #define XFA_FXFA_PARSER_CXFA_LOCALEVALUE_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include <stdint.h>
+
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "v8/include/cppgc/macros.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class LocaleIface;
 class CFX_DateTime;
 class CXFA_LocaleMgr;
-
-#define XFA_VT_NULL 0
-#define XFA_VT_BOOLEAN 1
-#define XFA_VT_INTEGER 2
-#define XFA_VT_DECIMAL 4
-#define XFA_VT_FLOAT 8
-#define XFA_VT_TEXT 16
-#define XFA_VT_DATE 32
-#define XFA_VT_TIME 64
-#define XFA_VT_DATETIME 128
+class GCedLocaleIface;
 
 class CXFA_LocaleValue {
+  CPPGC_STACK_ALLOCATED();  // Raw/Unowned pointers allowed.
+
  public:
+  enum class ValueType : uint8_t {
+    kNull = 0,
+    kBoolean,
+    kInteger,
+    kDecimal,
+    kFloat,
+    kText,
+    kDate,
+    kTime,
+    kDateTime,
+  };
+
   CXFA_LocaleValue();
-  CXFA_LocaleValue(uint32_t dwType, CXFA_LocaleMgr* pLocaleMgr);
-  CXFA_LocaleValue(uint32_t dwType,
+  CXFA_LocaleValue(ValueType eType, CXFA_LocaleMgr* pLocaleMgr);
+  CXFA_LocaleValue(ValueType eType,
                    const WideString& wsValue,
                    CXFA_LocaleMgr* pLocaleMgr);
-  CXFA_LocaleValue(uint32_t dwType,
+  CXFA_LocaleValue(ValueType dwType,
                    const WideString& wsValue,
                    const WideString& wsFormat,
-                   LocaleIface* pLocale,
+                   GCedLocaleIface* pLocale,
                    CXFA_LocaleMgr* pLocaleMgr);
   CXFA_LocaleValue(const CXFA_LocaleValue& that);
   ~CXFA_LocaleValue();
@@ -45,22 +51,22 @@
 
   bool ValidateValue(const WideString& wsValue,
                      const WideString& wsPattern,
-                     LocaleIface* pLocale,
+                     GCedLocaleIface* pLocale,
                      WideString* pMatchFormat);
 
   bool FormatPatterns(WideString& wsResult,
                       const WideString& wsFormat,
-                      LocaleIface* pLocale,
-                      XFA_VALUEPICTURE eValueType) const;
+                      GCedLocaleIface* pLocale,
+                      XFA_ValuePicture eValueType) const;
 
   void GetNumericFormat(WideString& wsFormat, int32_t nIntLen, int32_t nDecLen);
   bool ValidateNumericTemp(const WideString& wsNumeric,
                            const WideString& wsFormat,
-                           LocaleIface* pLocale);
+                           GCedLocaleIface* pLocale);
 
   bool IsValid() const { return m_bValid; }
   const WideString& GetValue() const { return m_wsValue; }
-  uint32_t GetType() const { return m_dwType; }
+  ValueType GetType() const { return m_eType; }
   double GetDoubleNum() const;
   bool SetDate(const CFX_DateTime& d);
   CFX_DateTime GetDate() const;
@@ -69,9 +75,9 @@
  private:
   bool FormatSinglePattern(WideString& wsResult,
                            const WideString& wsFormat,
-                           LocaleIface* pLocale,
-                           XFA_VALUEPICTURE eValueType) const;
-  bool ValidateCanonicalValue(const WideString& wsValue, uint32_t dwVType);
+                           GCedLocaleIface* pLocale,
+                           XFA_ValuePicture eValueType) const;
+  bool ValidateCanonicalValue(const WideString& wsValue, ValueType eType);
   bool ValidateCanonicalDate(const WideString& wsDate, CFX_DateTime* unDate);
   bool ValidateCanonicalTime(const WideString& wsTime);
 
@@ -80,11 +86,11 @@
 
   bool ParsePatternValue(const WideString& wsValue,
                          const WideString& wsPattern,
-                         LocaleIface* pLocale);
+                         GCedLocaleIface* pLocale);
 
-  UnownedPtr<CXFA_LocaleMgr> m_pLocaleMgr;
+  UnownedPtr<CXFA_LocaleMgr> m_pLocaleMgr;  // Ok, stack-only.
   WideString m_wsValue;
-  uint32_t m_dwType = XFA_VT_NULL;
+  ValueType m_eType = ValueType::kNull;
   bool m_bValid = true;
 };
 
diff --git a/xfa/fxfa/parser/cxfa_localevalue_unittest.cpp b/xfa/fxfa/parser/cxfa_localevalue_unittest.cpp
index e69b058..c66a4d6 100644
--- a/xfa/fxfa/parser/cxfa_localevalue_unittest.cpp
+++ b/xfa/fxfa/parser/cxfa_localevalue_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,8 @@
 // We don't expect more precision than a float's worth from this code.
 float MakeDoubleNumAsFloat(const wchar_t* str) {
   return static_cast<float>(
-      CXFA_LocaleValue(XFA_VT_FLOAT, str, nullptr).GetDoubleNum());
+      CXFA_LocaleValue(CXFA_LocaleValue::ValueType::kFloat, str, nullptr)
+          .GetDoubleNum());
 }
 
 }  // namespace
diff --git a/xfa/fxfa/parser/cxfa_lockdocument.cpp b/xfa/fxfa/parser/cxfa_lockdocument.cpp
index b6aa783..9137d4a 100644
--- a/xfa/fxfa/parser/cxfa_lockdocument.cpp
+++ b/xfa/fxfa/parser/cxfa_lockdocument.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_lockdocument.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -24,11 +24,13 @@
 CXFA_LockDocument::CXFA_LockDocument(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContentNode,
                 XFA_Element::LockDocument,
                 {},
                 kLockDocumentAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_LockDocument::~CXFA_LockDocument() = default;
diff --git a/xfa/fxfa/parser/cxfa_lockdocument.h b/xfa/fxfa/parser/cxfa_lockdocument.h
index 1328d34..941c26b 100644
--- a/xfa/fxfa/parser/cxfa_lockdocument.h
+++ b/xfa/fxfa/parser/cxfa_lockdocument.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_LockDocument final : public CXFA_Node {
  public:
-  CXFA_LockDocument(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_LockDocument() override;
+
+ private:
+  CXFA_LockDocument(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_LOCKDOCUMENT_H_
diff --git a/xfa/fxfa/parser/cxfa_log.cpp b/xfa/fxfa/parser/cxfa_log.cpp
index e3e6c91..784a682 100644
--- a/xfa/fxfa/parser/cxfa_log.cpp
+++ b/xfa/fxfa/parser/cxfa_log.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #include "xfa/fxfa/parser/cxfa_log.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kLogPropertyData[] = {
-    {XFA_Element::To, 1, 0},
-    {XFA_Element::Uri, 1, 0},
-    {XFA_Element::Mode, 1, 0},
-    {XFA_Element::Threshold, 1, 0},
+    {XFA_Element::To, 1, {}},
+    {XFA_Element::Uri, 1, {}},
+    {XFA_Element::Mode, 1, {}},
+    {XFA_Element::Threshold, 1, {}},
 };
 
 const CXFA_Node::AttributeData kLogAttributeData[] = {
@@ -28,11 +28,13 @@
 CXFA_Log::CXFA_Log(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Log,
                 kLogPropertyData,
                 kLogAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Log::~CXFA_Log() = default;
diff --git a/xfa/fxfa/parser/cxfa_log.h b/xfa/fxfa/parser/cxfa_log.h
index 27173cd..ccfd824 100644
--- a/xfa/fxfa/parser/cxfa_log.h
+++ b/xfa/fxfa/parser/cxfa_log.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Log final : public CXFA_Node {
  public:
-  CXFA_Log(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Log() override;
+
+ private:
+  CXFA_Log(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_LOG_H_
diff --git a/xfa/fxfa/parser/cxfa_manifest.cpp b/xfa/fxfa/parser/cxfa_manifest.cpp
index 071ba3f..b76851e 100644
--- a/xfa/fxfa/parser/cxfa_manifest.cpp
+++ b/xfa/fxfa/parser/cxfa_manifest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_manifest.h"
 
 #include "fxjs/xfa/cjx_manifest.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kManifestPropertyData[] = {
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kManifestAttributeData[] = {
@@ -29,11 +29,13 @@
 CXFA_Manifest::CXFA_Manifest(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Manifest,
                 kManifestPropertyData,
                 kManifestAttributeData,
-                pdfium::MakeUnique<CJX_Manifest>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Manifest>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Manifest::~CXFA_Manifest() = default;
diff --git a/xfa/fxfa/parser/cxfa_manifest.h b/xfa/fxfa/parser/cxfa_manifest.h
index d5ca05f..b8ee0e0 100644
--- a/xfa/fxfa/parser/cxfa_manifest.h
+++ b/xfa/fxfa/parser/cxfa_manifest.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Manifest final : public CXFA_Node {
  public:
-  CXFA_Manifest(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Manifest() override;
+
+ private:
+  CXFA_Manifest(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_MANIFEST_H_
diff --git a/xfa/fxfa/parser/cxfa_map.cpp b/xfa/fxfa/parser/cxfa_map.cpp
index 782cbc5..5e2ae42 100644
--- a/xfa/fxfa/parser/cxfa_map.cpp
+++ b/xfa/fxfa/parser/cxfa_map.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_map.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -27,11 +27,13 @@
 CXFA_Map::CXFA_Map(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Config | XFA_XDPPACKET_SourceSet),
+                {XFA_XDPPACKET::kConfig, XFA_XDPPACKET::kSourceSet},
                 XFA_ObjectType::Node,
                 XFA_Element::Map,
                 {},
                 kMapAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Map::~CXFA_Map() = default;
diff --git a/xfa/fxfa/parser/cxfa_map.h b/xfa/fxfa/parser/cxfa_map.h
index f8e0797..8815498 100644
--- a/xfa/fxfa/parser/cxfa_map.h
+++ b/xfa/fxfa/parser/cxfa_map.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Map final : public CXFA_Node {
  public:
-  CXFA_Map(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Map() override;
+
+ private:
+  CXFA_Map(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_MAP_H_
diff --git a/xfa/fxfa/parser/cxfa_margin.cpp b/xfa/fxfa/parser/cxfa_margin.cpp
index 5e58a88..7c0eee4 100644
--- a/xfa/fxfa/parser/cxfa_margin.cpp
+++ b/xfa/fxfa/parser/cxfa_margin.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_margin.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kMarginPropertyData[] = {
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kMarginAttributeData[] = {
@@ -30,12 +30,14 @@
 CXFA_Margin::CXFA_Margin(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Margin,
                 kMarginPropertyData,
                 kMarginAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Margin::~CXFA_Margin() = default;
 
@@ -55,18 +57,18 @@
   return TryBottomInset().value_or(0);
 }
 
-Optional<float> CXFA_Margin::TryLeftInset() const {
+absl::optional<float> CXFA_Margin::TryLeftInset() const {
   return JSObject()->TryMeasureAsFloat(XFA_Attribute::LeftInset);
 }
 
-Optional<float> CXFA_Margin::TryTopInset() const {
+absl::optional<float> CXFA_Margin::TryTopInset() const {
   return JSObject()->TryMeasureAsFloat(XFA_Attribute::TopInset);
 }
 
-Optional<float> CXFA_Margin::TryRightInset() const {
+absl::optional<float> CXFA_Margin::TryRightInset() const {
   return JSObject()->TryMeasureAsFloat(XFA_Attribute::RightInset);
 }
 
-Optional<float> CXFA_Margin::TryBottomInset() const {
+absl::optional<float> CXFA_Margin::TryBottomInset() const {
   return JSObject()->TryMeasureAsFloat(XFA_Attribute::BottomInset);
 }
diff --git a/xfa/fxfa/parser/cxfa_margin.h b/xfa/fxfa/parser/cxfa_margin.h
index 813bdfe..e54b03e 100644
--- a/xfa/fxfa/parser/cxfa_margin.h
+++ b/xfa/fxfa/parser/cxfa_margin.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,7 @@
 
 class CXFA_Margin final : public CXFA_Node {
  public:
-  CXFA_Margin(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Margin() override;
 
   float GetLeftInset() const;
@@ -19,10 +19,13 @@
   float GetRightInset() const;
   float GetBottomInset() const;
 
-  Optional<float> TryLeftInset() const;
-  Optional<float> TryTopInset() const;
-  Optional<float> TryRightInset() const;
-  Optional<float> TryBottomInset() const;
+  absl::optional<float> TryLeftInset() const;
+  absl::optional<float> TryTopInset() const;
+  absl::optional<float> TryRightInset() const;
+  absl::optional<float> TryBottomInset() const;
+
+ private:
+  CXFA_Margin(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_MARGIN_H_
diff --git a/xfa/fxfa/parser/cxfa_mdp.cpp b/xfa/fxfa/parser/cxfa_mdp.cpp
index 3917bb6..1110c2f 100644
--- a/xfa/fxfa/parser/cxfa_mdp.cpp
+++ b/xfa/fxfa/parser/cxfa_mdp.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_mdp.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -25,11 +25,13 @@
 CXFA_Mdp::CXFA_Mdp(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Mdp,
                 {},
                 kMdpAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Mdp::~CXFA_Mdp() = default;
diff --git a/xfa/fxfa/parser/cxfa_mdp.h b/xfa/fxfa/parser/cxfa_mdp.h
index 79b1480..7142c8a 100644
--- a/xfa/fxfa/parser/cxfa_mdp.h
+++ b/xfa/fxfa/parser/cxfa_mdp.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Mdp final : public CXFA_Node {
  public:
-  CXFA_Mdp(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Mdp() override;
+
+ private:
+  CXFA_Mdp(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_MDP_H_
diff --git a/xfa/fxfa/parser/cxfa_measurement.cpp b/xfa/fxfa/parser/cxfa_measurement.cpp
index 3ce13e6..94675c8 100644
--- a/xfa/fxfa/parser/cxfa_measurement.cpp
+++ b/xfa/fxfa/parser/cxfa_measurement.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,10 @@
 
 #include "xfa/fxfa/parser/cxfa_measurement.h"
 
-#include <cmath>
+#include <math.h>
 
 #include "core/fxcrt/fx_extension.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
@@ -33,22 +34,21 @@
 }
 
 void CXFA_Measurement::SetString(WideStringView wsMeasure) {
+  if (wsMeasure.Front() == L'=')
+    wsMeasure = wsMeasure.Substr(1);
+
   if (wsMeasure.IsEmpty()) {
     Set(0, XFA_Unit::Unknown);
     return;
   }
 
-  if (wsMeasure[0] == L'=')
-    wsMeasure = wsMeasure.Last(wsMeasure.GetLength() - 1);
-
-  int32_t iUsedLen = 0;
+  size_t nUsedLen = 0;
   float fValue = FXSYS_wcstof(wsMeasure.unterminated_c_str(),
-                              wsMeasure.GetLength(), &iUsedLen);
-  if (!std::isfinite(fValue))
+                              wsMeasure.GetLength(), &nUsedLen);
+  if (!isfinite(fValue))
     fValue = 0.0f;
 
-  wsMeasure = wsMeasure.Last(wsMeasure.GetLength() - iUsedLen);
-  Set(fValue, GetUnitFromString(wsMeasure));
+  Set(fValue, GetUnitFromString(wsMeasure.Substr(nUsedLen)));
 }
 
 WideString CXFA_Measurement::ToString() const {
@@ -127,8 +127,7 @@
       *fValue /= kPtToPc;
       return true;
     default:
-      NOTREACHED();
-      return false;
+      NOTREACHED_NORETURN();
   }
 }
 
diff --git a/xfa/fxfa/parser/cxfa_measurement.h b/xfa/fxfa/parser/cxfa_measurement.h
index b2cf2d5..1324a31 100644
--- a/xfa/fxfa/parser/cxfa_measurement.h
+++ b/xfa/fxfa/parser/cxfa_measurement.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,7 @@
 #ifndef XFA_FXFA_PARSER_CXFA_MEASUREMENT_H_
 #define XFA_FXFA_PARSER_CXFA_MEASUREMENT_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/widestring.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
 class CXFA_Measurement {
@@ -34,8 +33,8 @@
   void SetString(WideStringView wsMeasure);
   bool ToUnitInternal(XFA_Unit eUnit, float* fValue) const;
 
-  float m_fValue;
-  XFA_Unit m_eUnit;
+  float m_fValue = 0.0f;
+  XFA_Unit m_eUnit = XFA_Unit::Percent;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_MEASUREMENT_H_
diff --git a/xfa/fxfa/parser/cxfa_measurement_unittest.cpp b/xfa/fxfa/parser/cxfa_measurement_unittest.cpp
index 7a84eb1..68bea7c 100644
--- a/xfa/fxfa/parser/cxfa_measurement_unittest.cpp
+++ b/xfa/fxfa/parser/cxfa_measurement_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -44,3 +44,33 @@
   EXPECT_EQ(XFA_Unit::Unknown, CXFA_Measurement::GetUnitFromString(L"Cm"));
   EXPECT_EQ(XFA_Unit::Unknown, CXFA_Measurement::GetUnitFromString(L"cM"));
 }
+
+TEST(CXFAMeasurementTest, EqualsPrefix) {
+  CXFA_Measurement no_unit(L"=5");
+  EXPECT_EQ(XFA_Unit::Unknown, no_unit.GetUnit());
+  EXPECT_FLOAT_EQ(5.0f, no_unit.GetValue());
+
+  CXFA_Measurement mm_unit(L"=5mm");
+  EXPECT_EQ(XFA_Unit::Mm, mm_unit.GetUnit());
+  EXPECT_FLOAT_EQ(5.0f, mm_unit.GetValue());
+}
+
+TEST(CXFAMeasurementTest, NoPrefix) {
+  CXFA_Measurement no_unit(L"5");
+  EXPECT_EQ(XFA_Unit::Unknown, no_unit.GetUnit());
+  EXPECT_FLOAT_EQ(5.0f, no_unit.GetValue());
+
+  CXFA_Measurement mm_unit(L"5mm");
+  EXPECT_EQ(XFA_Unit::Mm, mm_unit.GetUnit());
+  EXPECT_FLOAT_EQ(5.0f, mm_unit.GetValue());
+}
+
+TEST(CXFAMeasurementTest, InvalidValues) {
+  CXFA_Measurement empty(L"");
+  EXPECT_EQ(XFA_Unit::Unknown, empty.GetUnit());
+  EXPECT_FLOAT_EQ(0.0f, empty.GetValue());
+
+  CXFA_Measurement equals(L"=");
+  EXPECT_EQ(XFA_Unit::Unknown, equals.GetUnit());
+  EXPECT_FLOAT_EQ(0.0f, equals.GetValue());
+}
diff --git a/xfa/fxfa/parser/cxfa_medium.cpp b/xfa/fxfa/parser/cxfa_medium.cpp
index 47dee1b..8d1fe40 100644
--- a/xfa/fxfa/parser/cxfa_medium.cpp
+++ b/xfa/fxfa/parser/cxfa_medium.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_medium.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -32,11 +32,13 @@
 CXFA_Medium::CXFA_Medium(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Medium,
                 {},
                 kMediumAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Medium::~CXFA_Medium() = default;
diff --git a/xfa/fxfa/parser/cxfa_medium.h b/xfa/fxfa/parser/cxfa_medium.h
index 49be83f..196893c 100644
--- a/xfa/fxfa/parser/cxfa_medium.h
+++ b/xfa/fxfa/parser/cxfa_medium.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Medium final : public CXFA_Node {
  public:
-  CXFA_Medium(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Medium() override;
+
+ private:
+  CXFA_Medium(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_MEDIUM_H_
diff --git a/xfa/fxfa/parser/cxfa_mediuminfo.cpp b/xfa/fxfa/parser/cxfa_mediuminfo.cpp
index cc17657..212bfda 100644
--- a/xfa/fxfa/parser/cxfa_mediuminfo.cpp
+++ b/xfa/fxfa/parser/cxfa_mediuminfo.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_mediuminfo.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kMediumInfoPropertyData[] = {
-    {XFA_Element::Map, 1, 0},
+    {XFA_Element::Map, 1, {}},
 };
 
 const CXFA_Node::AttributeData kMediumInfoAttributeData[] = {
@@ -25,11 +25,13 @@
 CXFA_MediumInfo::CXFA_MediumInfo(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::MediumInfo,
                 kMediumInfoPropertyData,
                 kMediumInfoAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_MediumInfo::~CXFA_MediumInfo() = default;
diff --git a/xfa/fxfa/parser/cxfa_mediuminfo.h b/xfa/fxfa/parser/cxfa_mediuminfo.h
index 65e58df..f1fcd1c 100644
--- a/xfa/fxfa/parser/cxfa_mediuminfo.h
+++ b/xfa/fxfa/parser/cxfa_mediuminfo.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_MediumInfo final : public CXFA_Node {
  public:
-  CXFA_MediumInfo(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_MediumInfo() override;
+
+ private:
+  CXFA_MediumInfo(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_MEDIUMINFO_H_
diff --git a/xfa/fxfa/parser/cxfa_meridiem.cpp b/xfa/fxfa/parser/cxfa_meridiem.cpp
index e250f2d..a9ebbbf 100644
--- a/xfa/fxfa/parser/cxfa_meridiem.cpp
+++ b/xfa/fxfa/parser/cxfa_meridiem.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,18 @@
 #include "xfa/fxfa/parser/cxfa_meridiem.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 CXFA_Meridiem::CXFA_Meridiem(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Meridiem,
                 {},
                 {},
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Meridiem::~CXFA_Meridiem() = default;
diff --git a/xfa/fxfa/parser/cxfa_meridiem.h b/xfa/fxfa/parser/cxfa_meridiem.h
index 40c956c..6985be1 100644
--- a/xfa/fxfa/parser/cxfa_meridiem.h
+++ b/xfa/fxfa/parser/cxfa_meridiem.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Meridiem final : public CXFA_Node {
  public:
-  CXFA_Meridiem(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Meridiem() override;
+
+ private:
+  CXFA_Meridiem(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_MERIDIEM_H_
diff --git a/xfa/fxfa/parser/cxfa_meridiemnames.cpp b/xfa/fxfa/parser/cxfa_meridiemnames.cpp
index 70ede73..e77e1d0 100644
--- a/xfa/fxfa/parser/cxfa_meridiemnames.cpp
+++ b/xfa/fxfa/parser/cxfa_meridiemnames.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_meridiemnames.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kMeridiemNamesPropertyData[] = {
-    {XFA_Element::Meridiem, 2, 0},
+    {XFA_Element::Meridiem, 2, {}},
 };
 
 }  // namespace
@@ -21,11 +21,13 @@
                                        XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::MeridiemNames,
                 kMeridiemNamesPropertyData,
                 {},
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_MeridiemNames::~CXFA_MeridiemNames() = default;
diff --git a/xfa/fxfa/parser/cxfa_meridiemnames.h b/xfa/fxfa/parser/cxfa_meridiemnames.h
index 00fbbb7..6fe9315 100644
--- a/xfa/fxfa/parser/cxfa_meridiemnames.h
+++ b/xfa/fxfa/parser/cxfa_meridiemnames.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_MeridiemNames final : public CXFA_Node {
  public:
-  CXFA_MeridiemNames(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_MeridiemNames() override;
+
+ private:
+  CXFA_MeridiemNames(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_MERIDIEMNAMES_H_
diff --git a/xfa/fxfa/parser/cxfa_message.cpp b/xfa/fxfa/parser/cxfa_message.cpp
index 809a1db..fa137f7 100644
--- a/xfa/fxfa/parser/cxfa_message.cpp
+++ b/xfa/fxfa/parser/cxfa_message.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_message.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kMessagePropertyData[] = {
-    {XFA_Element::MsgId, 1, 0},
-    {XFA_Element::Severity, 1, 0},
+    {XFA_Element::MsgId, 1, {}},
+    {XFA_Element::Severity, 1, {}},
 };
 
 const CXFA_Node::AttributeData kMessageAttributeData[] = {
@@ -27,14 +27,16 @@
 }  // namespace
 
 CXFA_Message::CXFA_Message(CXFA_Document* doc, XFA_PacketType packet)
-    : CXFA_Node(
-          doc,
-          packet,
-          (XFA_XDPPACKET_Config | XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
-          XFA_ObjectType::Node,
-          XFA_Element::Message,
-          kMessagePropertyData,
-          kMessageAttributeData,
-          pdfium::MakeUnique<CJX_Node>(this)) {}
+    : CXFA_Node(doc,
+                packet,
+                {XFA_XDPPACKET::kConfig, XFA_XDPPACKET::kTemplate,
+                 XFA_XDPPACKET::kForm},
+                XFA_ObjectType::Node,
+                XFA_Element::Message,
+                kMessagePropertyData,
+                kMessageAttributeData,
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Message::~CXFA_Message() = default;
diff --git a/xfa/fxfa/parser/cxfa_message.h b/xfa/fxfa/parser/cxfa_message.h
index 81cb751..e1b3b4d 100644
--- a/xfa/fxfa/parser/cxfa_message.h
+++ b/xfa/fxfa/parser/cxfa_message.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Message final : public CXFA_Node {
  public:
-  CXFA_Message(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Message() override;
+
+ private:
+  CXFA_Message(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_MESSAGE_H_
diff --git a/xfa/fxfa/parser/cxfa_messaging.cpp b/xfa/fxfa/parser/cxfa_messaging.cpp
index 4bb2b88..aa0c8ff 100644
--- a/xfa/fxfa/parser/cxfa_messaging.cpp
+++ b/xfa/fxfa/parser/cxfa_messaging.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_messaging.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Messaging::CXFA_Messaging(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Messaging,
                 {},
                 kMessagingAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Messaging::~CXFA_Messaging() = default;
diff --git a/xfa/fxfa/parser/cxfa_messaging.h b/xfa/fxfa/parser/cxfa_messaging.h
index 86e16f3..60d7b67 100644
--- a/xfa/fxfa/parser/cxfa_messaging.h
+++ b/xfa/fxfa/parser/cxfa_messaging.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Messaging final : public CXFA_Node {
  public:
-  CXFA_Messaging(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Messaging() override;
+
+ private:
+  CXFA_Messaging(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_MESSAGING_H_
diff --git a/xfa/fxfa/parser/cxfa_mode.cpp b/xfa/fxfa/parser/cxfa_mode.cpp
index 4e8e09e..66eecf3 100644
--- a/xfa/fxfa/parser/cxfa_mode.cpp
+++ b/xfa/fxfa/parser/cxfa_mode.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_mode.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Mode::CXFA_Mode(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Mode,
                 {},
                 kModeAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Mode::~CXFA_Mode() = default;
diff --git a/xfa/fxfa/parser/cxfa_mode.h b/xfa/fxfa/parser/cxfa_mode.h
index 46675db..d021725 100644
--- a/xfa/fxfa/parser/cxfa_mode.h
+++ b/xfa/fxfa/parser/cxfa_mode.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Mode final : public CXFA_Node {
  public:
-  CXFA_Mode(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Mode() override;
+
+ private:
+  CXFA_Mode(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_MODE_H_
diff --git a/xfa/fxfa/parser/cxfa_modifyannots.cpp b/xfa/fxfa/parser/cxfa_modifyannots.cpp
index 6ec1293..bf93c2c 100644
--- a/xfa/fxfa/parser/cxfa_modifyannots.cpp
+++ b/xfa/fxfa/parser/cxfa_modifyannots.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_modifyannots.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_ModifyAnnots::CXFA_ModifyAnnots(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::ModifyAnnots,
                 {},
                 kModifyAnnotsAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ModifyAnnots::~CXFA_ModifyAnnots() = default;
diff --git a/xfa/fxfa/parser/cxfa_modifyannots.h b/xfa/fxfa/parser/cxfa_modifyannots.h
index c92b05b..f9e2e53 100644
--- a/xfa/fxfa/parser/cxfa_modifyannots.h
+++ b/xfa/fxfa/parser/cxfa_modifyannots.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_ModifyAnnots final : public CXFA_Node {
  public:
-  CXFA_ModifyAnnots(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ModifyAnnots() override;
+
+ private:
+  CXFA_ModifyAnnots(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_MODIFYANNOTS_H_
diff --git a/xfa/fxfa/parser/cxfa_month.cpp b/xfa/fxfa/parser/cxfa_month.cpp
index 8cc4481..16df6e6 100644
--- a/xfa/fxfa/parser/cxfa_month.cpp
+++ b/xfa/fxfa/parser/cxfa_month.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,18 @@
 #include "xfa/fxfa/parser/cxfa_month.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 CXFA_Month::CXFA_Month(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Month,
                 {},
                 {},
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Month::~CXFA_Month() = default;
diff --git a/xfa/fxfa/parser/cxfa_month.h b/xfa/fxfa/parser/cxfa_month.h
index f63095d..aeb8d9e 100644
--- a/xfa/fxfa/parser/cxfa_month.h
+++ b/xfa/fxfa/parser/cxfa_month.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Month final : public CXFA_Node {
  public:
-  CXFA_Month(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Month() override;
+
+ private:
+  CXFA_Month(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_MONTH_H_
diff --git a/xfa/fxfa/parser/cxfa_monthnames.cpp b/xfa/fxfa/parser/cxfa_monthnames.cpp
index dfb68d5..0c88608 100644
--- a/xfa/fxfa/parser/cxfa_monthnames.cpp
+++ b/xfa/fxfa/parser/cxfa_monthnames.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_monthnames.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kMonthNamesPropertyData[] = {
-    {XFA_Element::Month, 12, 0},
+    {XFA_Element::Month, 12, {}},
 };
 
 const CXFA_Node::AttributeData kMonthNamesAttributeData[] = {
@@ -24,11 +24,13 @@
 CXFA_MonthNames::CXFA_MonthNames(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::MonthNames,
                 kMonthNamesPropertyData,
                 kMonthNamesAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_MonthNames::~CXFA_MonthNames() = default;
diff --git a/xfa/fxfa/parser/cxfa_monthnames.h b/xfa/fxfa/parser/cxfa_monthnames.h
index cf0c331..319dc27 100644
--- a/xfa/fxfa/parser/cxfa_monthnames.h
+++ b/xfa/fxfa/parser/cxfa_monthnames.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_MonthNames final : public CXFA_Node {
  public:
-  CXFA_MonthNames(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_MonthNames() override;
+
+ private:
+  CXFA_MonthNames(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_MONTHNAMES_H_
diff --git a/xfa/fxfa/parser/cxfa_msgid.cpp b/xfa/fxfa/parser/cxfa_msgid.cpp
index 107f635..cf4ef33 100644
--- a/xfa/fxfa/parser/cxfa_msgid.cpp
+++ b/xfa/fxfa/parser/cxfa_msgid.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_msgid.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_MsgId::CXFA_MsgId(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::MsgId,
                 {},
                 kMsgIdAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_MsgId::~CXFA_MsgId() = default;
diff --git a/xfa/fxfa/parser/cxfa_msgid.h b/xfa/fxfa/parser/cxfa_msgid.h
index 4c166d7..e311753 100644
--- a/xfa/fxfa/parser/cxfa_msgid.h
+++ b/xfa/fxfa/parser/cxfa_msgid.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_MsgId final : public CXFA_Node {
  public:
-  CXFA_MsgId(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_MsgId() override;
+
+ private:
+  CXFA_MsgId(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_MSGID_H_
diff --git a/xfa/fxfa/parser/cxfa_nameattr.cpp b/xfa/fxfa/parser/cxfa_nameattr.cpp
index d29353f..e06e637 100644
--- a/xfa/fxfa/parser/cxfa_nameattr.cpp
+++ b/xfa/fxfa/parser/cxfa_nameattr.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_nameattr.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_NameAttr::CXFA_NameAttr(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::NameAttr,
                 {},
                 kNameAttrAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_NameAttr::~CXFA_NameAttr() = default;
diff --git a/xfa/fxfa/parser/cxfa_nameattr.h b/xfa/fxfa/parser/cxfa_nameattr.h
index f346bf6..07a281d 100644
--- a/xfa/fxfa/parser/cxfa_nameattr.h
+++ b/xfa/fxfa/parser/cxfa_nameattr.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_NameAttr final : public CXFA_Node {
  public:
-  CXFA_NameAttr(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_NameAttr() override;
+
+ private:
+  CXFA_NameAttr(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_NAMEATTR_H_
diff --git a/xfa/fxfa/parser/cxfa_neverembed.cpp b/xfa/fxfa/parser/cxfa_neverembed.cpp
index 18af282..5482e75 100644
--- a/xfa/fxfa/parser/cxfa_neverembed.cpp
+++ b/xfa/fxfa/parser/cxfa_neverembed.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_neverembed.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_NeverEmbed::CXFA_NeverEmbed(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::NeverEmbed,
                 {},
                 kNeverEmbedAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_NeverEmbed::~CXFA_NeverEmbed() = default;
diff --git a/xfa/fxfa/parser/cxfa_neverembed.h b/xfa/fxfa/parser/cxfa_neverembed.h
index 70f90a3..d68b011 100644
--- a/xfa/fxfa/parser/cxfa_neverembed.h
+++ b/xfa/fxfa/parser/cxfa_neverembed.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_NeverEmbed final : public CXFA_Node {
  public:
-  CXFA_NeverEmbed(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_NeverEmbed() override;
+
+ private:
+  CXFA_NeverEmbed(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_NEVEREMBED_H_
diff --git a/xfa/fxfa/parser/cxfa_node.cpp b/xfa/fxfa/parser/cxfa_node.cpp
index 05c6061..2bf0f16 100644
--- a/xfa/fxfa/parser/cxfa_node.cpp
+++ b/xfa/fxfa/parser/cxfa_node.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,9 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
+#include <math.h>
+#include <stdint.h>
+
 #include <algorithm>
 #include <map>
 #include <memory>
@@ -14,23 +17,27 @@
 #include <vector>
 
 #include "core/fxcrt/autorestorer.h"
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/cfx_read_only_string_stream.h"
+#include "core/fxcrt/cfx_read_only_vector_stream.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/stl_util.h"
 #include "core/fxcrt/xml/cfx_xmldocument.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
 #include "core/fxcrt/xml/cfx_xmltext.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/fx_font.h"
+#include "fxjs/gc/container_trace.h"
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cfxjse_value.h"
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/span.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/containers/span.h"
+#include "third_party/base/notreached.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fgas/crt/cfgas_decimal.h"
 #include "xfa/fgas/crt/locale_iface.h"
@@ -123,13 +130,12 @@
 #include "xfa/fxfa/parser/cxfa_defaultui.h"
 #include "xfa/fxfa/parser/cxfa_delete.h"
 #include "xfa/fxfa/parser/cxfa_delta.h"
-#include "xfa/fxfa/parser/cxfa_deltas.h"
 #include "xfa/fxfa/parser/cxfa_desc.h"
 #include "xfa/fxfa/parser/cxfa_destination.h"
 #include "xfa/fxfa/parser/cxfa_digestmethod.h"
 #include "xfa/fxfa/parser/cxfa_digestmethods.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_document_parser.h"
+#include "xfa/fxfa/parser/cxfa_document_builder.h"
 #include "xfa/fxfa/parser/cxfa_documentassembly.h"
 #include "xfa/fxfa/parser/cxfa_draw.h"
 #include "xfa/fxfa/parser/cxfa_driver.h"
@@ -369,7 +375,7 @@
 
 constexpr uint8_t kMaxExecuteRecursion = 2;
 
-constexpr uint8_t g_inv_base64[128] = {
+constexpr uint8_t kInvBase64[128] = {
     255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
     255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
     255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62,  255,
@@ -382,12 +388,12 @@
 };
 
 inline uint8_t GetInvBase64(uint8_t x) {
-  return (x & 128) == 0 ? g_inv_base64[x] : 255;
+  return (x & 128) == 0 ? kInvBase64[x] : 255;
 }
 
-std::vector<uint8_t> XFA_RemoveBase64Whitespace(
+DataVector<uint8_t> XFA_RemoveBase64Whitespace(
     pdfium::span<const uint8_t> spStr) {
-  std::vector<uint8_t> result;
+  DataVector<uint8_t> result;
   result.reserve(spStr.size());
   for (uint8_t ch : spStr) {
     if (GetInvBase64(ch) != 255 || ch == '=')
@@ -396,12 +402,12 @@
   return result;
 }
 
-std::vector<uint8_t> XFA_Base64Decode(const ByteString& bsStr) {
-  std::vector<uint8_t> result;
+DataVector<uint8_t> XFA_Base64Decode(const ByteString& bsStr) {
+  DataVector<uint8_t> result;
   if (bsStr.IsEmpty())
     return result;
 
-  std::vector<uint8_t> buffer = XFA_RemoveBase64Whitespace(bsStr.raw_span());
+  DataVector<uint8_t> buffer = XFA_RemoveBase64Whitespace(bsStr.raw_span());
   result.reserve(3 * (buffer.size() / 4));
 
   uint32_t dwLimb = 0;
@@ -412,21 +418,21 @@
         break;
       }
       if (buffer[i + 2] == '=') {
-        dwLimb = ((uint32_t)g_inv_base64[buffer[i]] << 6) |
-                 ((uint32_t)g_inv_base64[buffer[i + 1]]);
+        dwLimb = ((uint32_t)kInvBase64[buffer[i]] << 6) |
+                 ((uint32_t)kInvBase64[buffer[i + 1]]);
         result.push_back((uint8_t)(dwLimb >> 4) & 0xFF);
       } else {
-        dwLimb = ((uint32_t)g_inv_base64[buffer[i]] << 12) |
-                 ((uint32_t)g_inv_base64[buffer[i + 1]] << 6) |
-                 ((uint32_t)g_inv_base64[buffer[i + 2]]);
+        dwLimb = ((uint32_t)kInvBase64[buffer[i]] << 12) |
+                 ((uint32_t)kInvBase64[buffer[i + 1]] << 6) |
+                 ((uint32_t)kInvBase64[buffer[i + 2]]);
         result.push_back((uint8_t)(dwLimb >> 10) & 0xFF);
         result.push_back((uint8_t)(dwLimb >> 2) & 0xFF);
       }
     } else {
-      dwLimb = ((uint32_t)g_inv_base64[buffer[i]] << 18) |
-               ((uint32_t)g_inv_base64[buffer[i + 1]] << 12) |
-               ((uint32_t)g_inv_base64[buffer[i + 2]] << 6) |
-               ((uint32_t)g_inv_base64[buffer[i + 3]]);
+      dwLimb = ((uint32_t)kInvBase64[buffer[i]] << 18) |
+               ((uint32_t)kInvBase64[buffer[i + 1]] << 12) |
+               ((uint32_t)kInvBase64[buffer[i + 2]] << 6) |
+               ((uint32_t)kInvBase64[buffer[i + 3]]);
       result.push_back((uint8_t)(dwLimb >> 16) & 0xff);
       result.push_back((uint8_t)(dwLimb >> 8) & 0xff);
       result.push_back((uint8_t)(dwLimb)&0xff);
@@ -474,20 +480,19 @@
     return nullptr;
 
   FXCODEC_IMAGE_TYPE type = XFA_GetImageType(pImage->GetContentType());
-  ByteString bsData;  // Must outlive |pImageFileRead|.
-  std::vector<uint8_t> buffer;  // Must outlive |pImageFileRead|.
+
   RetainPtr<IFX_SeekableReadStream> pImageFileRead;
   if (wsImage.GetLength() > 0) {
     XFA_AttributeValue iEncoding = pImage->GetTransferEncoding();
     if (iEncoding == XFA_AttributeValue::Base64) {
-      bsData = wsImage.ToUTF8();
-      buffer = XFA_Base64Decode(bsData);
-      if (!buffer.empty())
-        pImageFileRead = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(buffer);
+      DataVector<uint8_t> buffer = XFA_Base64Decode(wsImage.ToUTF8());
+      if (!buffer.empty()) {
+        pImageFileRead =
+            pdfium::MakeRetain<CFX_ReadOnlyVectorStream>(std::move(buffer));
+      }
     } else {
-      bsData = wsImage.ToDefANSI();
       pImageFileRead =
-          pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(bsData.raw_span());
+          pdfium::MakeRetain<CFX_ReadOnlyStringStream>(wsImage.ToDefANSI());
     }
   } else {
     WideString wsURL = wsHref;
@@ -500,15 +505,14 @@
         return pBitmap;
       }
     }
-    pImageFileRead = pDoc->GetDocEnvironment()->OpenLinkedFile(pDoc, wsURL);
+    pImageFileRead = pDoc->OpenLinkedFile(wsURL);
   }
   if (!pImageFileRead)
     return nullptr;
 
   bNameImage = false;
-  RetainPtr<CFX_DIBitmap> pBitmap =
-      XFA_LoadImageFromBuffer(pImageFileRead, type, iImageXDpi, iImageYDpi);
-  return pBitmap;
+  return XFA_LoadImageFromBuffer(std::move(pImageFileRead), type, iImageXDpi,
+                                 iImageYDpi);
 }
 
 bool SplitDateTime(const WideString& wsDateTime,
@@ -542,75 +546,62 @@
   return true;
 }
 
-std::vector<CXFA_Node*> NodesSortedByDocumentIdx(
-    const std::set<CXFA_Node*>& rgNodeSet) {
-  if (rgNodeSet.empty())
-    return std::vector<CXFA_Node*>();
+// Stack allocated. Using containers of members would be correct here
+// if advanced GC worked with STL.
+using NodeSet = std::set<cppgc::Member<CXFA_Node>>;
+using NodeSetPair = std::pair<NodeSet, NodeSet>;
+using NodeSetPairMap = std::map<uint32_t, NodeSetPair>;
+using NodeSetPairMapMap = std::map<CXFA_Node*, NodeSetPairMap>;
+using NodeVector = std::vector<cppgc::Member<CXFA_Node>>;
 
-  std::vector<CXFA_Node*> rgNodeArray;
+NodeVector NodesSortedByDocumentIdx(const NodeSet& rgNodeSet) {
+  if (rgNodeSet.empty())
+    return NodeVector();
+
+  NodeVector rgNodeArray;
   CXFA_Node* pCommonParent = (*rgNodeSet.begin())->GetParent();
   for (CXFA_Node* pNode = pCommonParent->GetFirstChild(); pNode;
        pNode = pNode->GetNextSibling()) {
-    if (pdfium::ContainsValue(rgNodeSet, pNode))
+    if (pdfium::Contains(rgNodeSet, pNode))
       rgNodeArray.push_back(pNode);
   }
   return rgNodeArray;
 }
 
-using CXFA_NodeSetPair = std::pair<std::set<CXFA_Node*>, std::set<CXFA_Node*>>;
-using CXFA_NodeSetPairMap =
-    std::map<uint32_t, std::unique_ptr<CXFA_NodeSetPair>>;
-using CXFA_NodeSetPairMapMap =
-    std::map<CXFA_Node*, std::unique_ptr<CXFA_NodeSetPairMap>>;
-
-CXFA_NodeSetPair* NodeSetPairForNode(CXFA_Node* pNode,
-                                     CXFA_NodeSetPairMapMap* pMap) {
+NodeSetPair* NodeSetPairForNode(CXFA_Node* pNode, NodeSetPairMapMap* pMap) {
   CXFA_Node* pParentNode = pNode->GetParent();
   uint32_t dwNameHash = pNode->GetNameHash();
   if (!pParentNode || !dwNameHash)
     return nullptr;
 
-  if (!(*pMap)[pParentNode])
-    (*pMap)[pParentNode] = pdfium::MakeUnique<CXFA_NodeSetPairMap>();
-
-  CXFA_NodeSetPairMap* pNodeSetPairMap = (*pMap)[pParentNode].get();
-  if (!(*pNodeSetPairMap)[dwNameHash])
-    (*pNodeSetPairMap)[dwNameHash] = pdfium::MakeUnique<CXFA_NodeSetPair>();
-
-  return (*pNodeSetPairMap)[dwNameHash].get();
+  return &((*pMap)[pParentNode][dwNameHash]);
 }
 
-void ReorderDataNodes(const std::set<CXFA_Node*>& sSet1,
-                      const std::set<CXFA_Node*>& sSet2,
+void ReorderDataNodes(const NodeSet& sSet1,
+                      const NodeSet& sSet2,
                       bool bInsertBefore) {
-  CXFA_NodeSetPairMapMap rgMap;
+  NodeSetPairMapMap rgMap;
   for (CXFA_Node* pNode : sSet1) {
-    CXFA_NodeSetPair* pNodeSetPair = NodeSetPairForNode(pNode, &rgMap);
+    NodeSetPair* pNodeSetPair = NodeSetPairForNode(pNode, &rgMap);
     if (pNodeSetPair)
       pNodeSetPair->first.insert(pNode);
   }
   for (CXFA_Node* pNode : sSet2) {
-    CXFA_NodeSetPair* pNodeSetPair = NodeSetPairForNode(pNode, &rgMap);
+    NodeSetPair* pNodeSetPair = NodeSetPairForNode(pNode, &rgMap);
     if (pNodeSetPair) {
-      if (pdfium::ContainsValue(pNodeSetPair->first, pNode))
+      if (pdfium::Contains(pNodeSetPair->first, pNode))
         pNodeSetPair->first.erase(pNode);
       else
         pNodeSetPair->second.insert(pNode);
     }
   }
-  for (const auto& iter1 : rgMap) {
-    CXFA_NodeSetPairMap* pNodeSetPairMap = iter1.second.get();
-    if (!pNodeSetPairMap)
-      continue;
-
-    for (const auto& iter2 : *pNodeSetPairMap) {
-      CXFA_NodeSetPair* pNodeSetPair = iter2.second.get();
-      if (!pNodeSetPair)
-        continue;
+  for (auto& iter1 : rgMap) {
+    NodeSetPairMap* pNodeSetPairMap = &iter1.second;
+    for (auto& iter2 : *pNodeSetPairMap) {
+      NodeSetPair* pNodeSetPair = &iter2.second;
       if (!pNodeSetPair->first.empty() && !pNodeSetPair->second.empty()) {
-        std::vector<CXFA_Node*> rgNodeArray1 =
-            NodesSortedByDocumentIdx(pNodeSetPair->first);
-        std::vector<CXFA_Node*> rgNodeArray2 =
+        NodeVector rgNodeArray1 = NodesSortedByDocumentIdx(pNodeSetPair->first);
+        NodeVector rgNodeArray2 =
             NodesSortedByDocumentIdx(pNodeSetPair->second);
         CXFA_Node* pParentNode = nullptr;
         CXFA_Node* pBeforeNode = nullptr;
@@ -622,7 +613,7 @@
           pParentNode = pLastNode->GetParent();
           pBeforeNode = pLastNode->GetNextSibling();
         }
-        for (auto* pCurNode : rgNodeArray1) {
+        for (auto& pCurNode : rgNodeArray1) {
           pParentNode->RemoveChildAndNotify(pCurNode, true);
           pParentNode->InsertChildAndNotify(pCurNode, pBeforeNode);
         }
@@ -658,23 +649,21 @@
     wsSrcNum.Delete(0, 1);
   }
 
-  auto dot_index = wsSrcNum.Find('.');
-  dot_index = !dot_index.has_value() ? wsSrcNum.GetLength() : dot_index;
-
-  if (dot_index.value() < 1)
+  size_t dot_index = wsSrcNum.Find('.').value_or(wsSrcNum.GetLength());
+  if (dot_index == 0)
     return WideString();
 
-  size_t nPos = dot_index.value() % 3;
+  size_t nPos = dot_index % 3;
   WideString wsOutput;
-  for (size_t i = 0; i < dot_index.value(); i++) {
+  for (size_t i = 0; i < dot_index; i++) {
     if (i % 3 == nPos && i != 0)
       wsOutput += wsGroupSymbol;
 
     wsOutput += wsSrcNum[i];
   }
-  if (dot_index.value() < wsSrcNum.GetLength()) {
+  if (dot_index < wsSrcNum.GetLength()) {
     wsOutput += pLocale->GetDecimalSymbol();
-    wsOutput += wsSrcNum.Last(wsSrcNum.GetLength() - dot_index.value() - 1);
+    wsOutput += wsSrcNum.Last(wsSrcNum.GetLength() - dot_index - 1);
   }
   if (bNeg)
     return pLocale->GetMinusSymbol() + wsOutput;
@@ -684,24 +673,24 @@
 
 CXFA_Node* FindFirstSiblingNamedInList(CXFA_Node* parent,
                                        uint32_t dwNameHash,
-                                       uint32_t dwFilter);
+                                       Mask<XFA_NodeFilter> dwFilter);
 CXFA_Node* FindFirstSiblingOfClassInList(CXFA_Node* parent,
                                          XFA_Element element,
-                                         uint32_t dwFilter);
+                                         Mask<XFA_NodeFilter> dwFilter);
 
 CXFA_Node* FindFirstSiblingNamed(CXFA_Node* parent, uint32_t dwNameHash) {
   CXFA_Node* result = FindFirstSiblingNamedInList(parent, dwNameHash,
-                                                  XFA_NODEFILTER_Properties);
+                                                  XFA_NodeFilter::kProperties);
   if (result)
     return result;
 
   return FindFirstSiblingNamedInList(parent, dwNameHash,
-                                     XFA_NODEFILTER_Children);
+                                     XFA_NodeFilter::kChildren);
 }
 
 CXFA_Node* FindFirstSiblingNamedInList(CXFA_Node* parent,
                                        uint32_t dwNameHash,
-                                       uint32_t dwFilter) {
+                                       Mask<XFA_NodeFilter> dwFilter) {
   for (CXFA_Node* child : parent->GetNodeListWithFilter(dwFilter)) {
     if (child->GetNameHash() == dwNameHash)
       return child;
@@ -714,18 +703,18 @@
 }
 
 CXFA_Node* FindFirstSiblingOfClass(CXFA_Node* parent, XFA_Element element) {
-  CXFA_Node* result =
-      FindFirstSiblingOfClassInList(parent, element, XFA_NODEFILTER_Properties);
+  CXFA_Node* result = FindFirstSiblingOfClassInList(
+      parent, element, XFA_NodeFilter::kProperties);
   if (result)
     return result;
 
   return FindFirstSiblingOfClassInList(parent, element,
-                                       XFA_NODEFILTER_Children);
+                                       XFA_NodeFilter::kChildren);
 }
 
 CXFA_Node* FindFirstSiblingOfClassInList(CXFA_Node* parent,
                                          XFA_Element element,
-                                         uint32_t dwFilter) {
+                                         Mask<XFA_NodeFilter> dwFilter) {
   for (CXFA_Node* child : parent->GetNodeListWithFilter(dwFilter)) {
     if (child->GetElementType() == element)
       return child;
@@ -760,36 +749,11 @@
 void TraverseSiblings(CXFA_Node* parent,
                       uint32_t dwNameHash,
                       std::vector<CXFA_Node*>* pSiblings,
-                      bool bIsClassName,
-                      bool bIsFindProperty) {
-  ASSERT(parent);
-  ASSERT(pSiblings);
-
-  if (bIsFindProperty) {
-    for (CXFA_Node* child :
-         parent->GetNodeListWithFilter(XFA_NODEFILTER_Properties)) {
-      if (bIsClassName) {
-        if (child->GetClassHashCode() == dwNameHash)
-          pSiblings->push_back(child);
-      } else {
-        if (child->GetNameHash() == dwNameHash) {
-          if (child->GetElementType() != XFA_Element::PageSet &&
-              child->GetElementType() != XFA_Element::Extras &&
-              child->GetElementType() != XFA_Element::Items) {
-            pSiblings->push_back(child);
-          }
-        }
-      }
-      if (child->IsUnnamed() &&
-          child->GetElementType() == XFA_Element::PageSet) {
-        TraverseSiblings(child, dwNameHash, pSiblings, bIsClassName, false);
-      }
-    }
-    if (!pSiblings->empty())
-      return;
-  }
+                      bool bIsClassName) {
+  DCHECK(parent);
+  DCHECK(pSiblings);
   for (CXFA_Node* child :
-       parent->GetNodeListWithFilter(XFA_NODEFILTER_Children)) {
+       parent->GetNodeListWithFilter(XFA_NodeFilter::kChildren)) {
     if (child->GetElementType() == XFA_Element::Variables)
       continue;
 
@@ -800,56 +764,102 @@
       if (child->GetNameHash() == dwNameHash)
         pSiblings->push_back(child);
     }
-
     if (child->IsTransparent() &&
         child->GetElementType() != XFA_Element::PageSet) {
-      TraverseSiblings(child, dwNameHash, pSiblings, bIsClassName, false);
+      TraverseSiblings(child, dwNameHash, pSiblings, bIsClassName);
     }
   }
 }
 
+void TraversePropertiesOrSiblings(CXFA_Node* parent,
+                                  uint32_t dwNameHash,
+                                  std::vector<CXFA_Node*>* pSiblings,
+                                  bool bIsClassName) {
+  DCHECK(parent);
+  DCHECK(pSiblings);
+  for (CXFA_Node* child :
+       parent->GetNodeListWithFilter(XFA_NodeFilter::kProperties)) {
+    if (bIsClassName) {
+      if (child->GetClassHashCode() == dwNameHash)
+        pSiblings->push_back(child);
+    } else {
+      if (child->GetNameHash() == dwNameHash) {
+        if (child->GetElementType() != XFA_Element::PageSet &&
+            child->GetElementType() != XFA_Element::Extras &&
+            child->GetElementType() != XFA_Element::Items) {
+          pSiblings->push_back(child);
+        }
+      }
+    }
+    if (child->IsUnnamed() && child->GetElementType() == XFA_Element::PageSet) {
+      TraverseSiblings(child, dwNameHash, pSiblings, bIsClassName);
+    }
+  }
+  if (pSiblings->empty())
+    TraverseSiblings(parent, dwNameHash, pSiblings, bIsClassName);
+}
+
 }  // namespace
 
-class CXFA_WidgetLayoutData {
+class CXFA_WidgetLayoutData
+    : public cppgc::GarbageCollected<CXFA_WidgetLayoutData> {
  public:
-  CXFA_WidgetLayoutData() = default;
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   virtual ~CXFA_WidgetLayoutData() = default;
 
+  virtual void Trace(cppgc::Visitor* visitor) const {}
+
   virtual CXFA_FieldLayoutData* AsFieldLayoutData() { return nullptr; }
   virtual CXFA_ImageLayoutData* AsImageLayoutData() { return nullptr; }
   virtual CXFA_TextLayoutData* AsTextLayoutData() { return nullptr; }
 
+  float GetWidgetHeight() const { return m_fWidgetHeight; }
+  void SetWidgetHeight(float height) { m_fWidgetHeight = height; }
+
+ protected:
+  CXFA_WidgetLayoutData() = default;
+
+ private:
   float m_fWidgetHeight = -1.0f;
 };
 
 class CXFA_TextLayoutData final : public CXFA_WidgetLayoutData {
  public:
-  CXFA_TextLayoutData() = default;
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_TextLayoutData() override = default;
 
+  void Trace(cppgc::Visitor* visitor) const override {
+    CXFA_WidgetLayoutData::Trace(visitor);
+    visitor->Trace(m_pTextLayout);
+    visitor->Trace(m_pTextProvider);
+  }
+
   CXFA_TextLayoutData* AsTextLayoutData() override { return this; }
 
-  CXFA_TextLayout* GetTextLayout() const { return m_pTextLayout.get(); }
-  CXFA_TextProvider* GetTextProvider() const { return m_pTextProvider.get(); }
+  CXFA_TextLayout* GetTextLayout() const { return m_pTextLayout; }
+  CXFA_TextProvider* GetTextProvider() const { return m_pTextProvider; }
 
   void LoadText(CXFA_FFDoc* doc, CXFA_Node* pNode) {
     if (m_pTextLayout)
       return;
 
-    m_pTextProvider =
-        pdfium::MakeUnique<CXFA_TextProvider>(pNode, XFA_TEXTPROVIDERTYPE_Text);
-    m_pTextLayout =
-        pdfium::MakeUnique<CXFA_TextLayout>(doc, m_pTextProvider.get());
+    m_pTextProvider = cppgc::MakeGarbageCollected<CXFA_TextProvider>(
+        doc->GetHeap()->GetAllocationHandle(), pNode,
+        CXFA_TextProvider::Type::kText);
+    m_pTextLayout = cppgc::MakeGarbageCollected<CXFA_TextLayout>(
+        doc->GetHeap()->GetAllocationHandle(), doc, m_pTextProvider);
   }
 
  private:
-  std::unique_ptr<CXFA_TextLayout> m_pTextLayout;
-  std::unique_ptr<CXFA_TextProvider> m_pTextProvider;
+  CXFA_TextLayoutData() = default;
+
+  cppgc::Member<CXFA_TextLayout> m_pTextLayout;
+  cppgc::Member<CXFA_TextProvider> m_pTextProvider;
 };
 
 class CXFA_ImageLayoutData final : public CXFA_WidgetLayoutData {
  public:
-  CXFA_ImageLayoutData() = default;
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ImageLayoutData() override = default;
 
   CXFA_ImageLayoutData* AsImageLayoutData() override { return this; }
@@ -866,11 +876,20 @@
     if (!image)
       return false;
 
-    pNode->SetImageImage(XFA_LoadImageData(doc, image, m_bNamedImage,
-                                           m_iImageXDpi, m_iImageYDpi));
+    pNode->SetLayoutImage(XFA_LoadImageData(doc, image, m_bNamedImage,
+                                            m_iImageXDpi, m_iImageYDpi));
     return !!m_pDIBitmap;
   }
 
+  CFX_Size GetDpi() const { return CFX_Size(m_iImageXDpi, m_iImageYDpi); }
+  RetainPtr<CFX_DIBitmap> GetBitmap() { return m_pDIBitmap; }
+  void SetBitmap(RetainPtr<CFX_DIBitmap> pBitmap) {
+    m_pDIBitmap = std::move(pBitmap);
+  }
+
+ private:
+  CXFA_ImageLayoutData() = default;
+
   bool m_bNamedImage = false;
   int32_t m_iImageXDpi = 0;
   int32_t m_iImageYDpi = 0;
@@ -879,9 +898,14 @@
 
 class CXFA_FieldLayoutData : public CXFA_WidgetLayoutData {
  public:
-  CXFA_FieldLayoutData() = default;
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_FieldLayoutData() override = default;
 
+  void Trace(cppgc::Visitor* visitor) const override {
+    CXFA_WidgetLayoutData::Trace(visitor);
+    visitor->Trace(m_pCapTextLayout);
+    visitor->Trace(m_pCapTextProvider);
+  }
   CXFA_FieldLayoutData* AsFieldLayoutData() override { return this; }
 
   virtual CXFA_ImageEditData* AsImageEditData() { return nullptr; }
@@ -894,30 +918,37 @@
     if (!caption || caption->IsHidden())
       return false;
 
-    m_pCapTextProvider = pdfium::MakeUnique<CXFA_TextProvider>(
-        pNode, XFA_TEXTPROVIDERTYPE_Caption);
-    m_pCapTextLayout =
-        pdfium::MakeUnique<CXFA_TextLayout>(doc, m_pCapTextProvider.get());
+    m_pCapTextProvider = cppgc::MakeGarbageCollected<CXFA_TextProvider>(
+        doc->GetHeap()->GetAllocationHandle(), pNode,
+        CXFA_TextProvider::Type::kCaption);
+    m_pCapTextLayout = cppgc::MakeGarbageCollected<CXFA_TextLayout>(
+        doc->GetHeap()->GetAllocationHandle(), doc, m_pCapTextProvider);
     return true;
   }
 
-  std::unique_ptr<CXFA_TextLayout> m_pCapTextLayout;
-  std::unique_ptr<CXFA_TextProvider> m_pCapTextProvider;
+  cppgc::Member<CXFA_TextLayout> m_pCapTextLayout;
+  cppgc::Member<CXFA_TextProvider> m_pCapTextProvider;
   std::unique_ptr<CFDE_TextOut> m_pTextOut;
   std::vector<float> m_FieldSplitArray;
+
+ protected:
+  CXFA_FieldLayoutData() = default;
 };
 
 class CXFA_TextEditData final : public CXFA_FieldLayoutData {
  public:
-  CXFA_TextEditData() = default;
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_TextEditData() override = default;
 
   CXFA_TextEditData* AsTextEditData() override { return this; }
+
+ protected:
+  CXFA_TextEditData() = default;
 };
 
 class CXFA_ImageEditData final : public CXFA_FieldLayoutData {
  public:
-  CXFA_ImageEditData() = default;
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ImageEditData() override = default;
 
   CXFA_ImageEditData* AsImageEditData() override { return this; }
@@ -934,11 +965,20 @@
     if (!image)
       return false;
 
-    pNode->SetImageEditImage(XFA_LoadImageData(doc, image, m_bNamedImage,
-                                               m_iImageXDpi, m_iImageYDpi));
+    pNode->SetEditImage(XFA_LoadImageData(doc, image, m_bNamedImage,
+                                          m_iImageXDpi, m_iImageYDpi));
     return !!m_pDIBitmap;
   }
 
+  CFX_Size GetDpi() const { return CFX_Size(m_iImageXDpi, m_iImageYDpi); }
+  RetainPtr<CFX_DIBitmap> GetBitmap() { return m_pDIBitmap; }
+  void SetBitmap(RetainPtr<CFX_DIBitmap> pBitmap) {
+    m_pDIBitmap = std::move(pBitmap);
+  }
+
+ private:
+  CXFA_ImageEditData() = default;
+
   bool m_bNamedImage = false;
   int32_t m_iImageXDpi = 0;
   int32_t m_iImageYDpi = 0;
@@ -947,22 +987,31 @@
 
 CXFA_Node::CXFA_Node(CXFA_Document* pDoc,
                      XFA_PacketType ePacket,
-                     uint32_t validPackets,
+                     Mask<XFA_XDPPACKET> validPackets,
                      XFA_ObjectType oType,
                      XFA_Element eType,
                      pdfium::span<const PropertyData> properties,
                      pdfium::span<const AttributeData> attributes,
-                     std::unique_ptr<CJX_Object> js_object)
-    : CXFA_Object(pDoc, oType, eType, std::move(js_object)),
+                     CJX_Object* js_object)
+    : CXFA_Object(pDoc, oType, eType, js_object),
       m_Properties(properties),
       m_Attributes(attributes),
       m_ValidPackets(validPackets),
       m_ePacket(ePacket) {
-  ASSERT(m_pDocument);
+  DCHECK(m_pDocument);
 }
 
 CXFA_Node::~CXFA_Node() = default;
 
+void CXFA_Node::Trace(cppgc::Visitor* visitor) const {
+  CXFA_Object::Trace(visitor);
+  GCedTreeNodeMixin<CXFA_Node>::Trace(visitor);
+  visitor->Trace(m_pAuxNode);
+  ContainerTrace(visitor, binding_nodes_);
+  visitor->Trace(m_pLayoutData);
+  visitor->Trace(ui_);
+}
+
 CXFA_Node* CXFA_Node::Clone(bool bRecursive) {
   CXFA_Node* pClone = m_pDocument->CreateNode(m_ePacket, m_elementType);
   if (!pClone)
@@ -999,7 +1048,7 @@
       pClone->InsertChildAndNotify(pChild->Clone(bRecursive), nullptr);
     }
   }
-  pClone->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+  pClone->SetInitializedFlagAndNotify();
   pClone->SetBindingNode(nullptr);
   return pClone;
 }
@@ -1037,12 +1086,13 @@
 }
 
 bool CXFA_Node::IsValidInPacket(XFA_PacketType packet) const {
-  return !!(m_ValidPackets & (1 << static_cast<uint8_t>(packet)));
+  uint32_t bitflag = 1 << static_cast<uint8_t>(packet);
+  return !!(m_ValidPackets & static_cast<XFA_XDPPACKET>(bitflag));
 }
 
 const CXFA_Node::PropertyData* CXFA_Node::GetPropertyData(
     XFA_Element property) const {
-  ASSERT(property != XFA_Element::Unknown);
+  DCHECK(property != XFA_Element::Unknown);
   for (const auto& prop : m_Properties) {
     if (prop.property == property)
       return &prop;
@@ -1054,20 +1104,21 @@
   return !!GetPropertyData(property);
 }
 
-bool CXFA_Node::HasPropertyFlags(XFA_Element property, uint8_t flags) const {
+bool CXFA_Node::HasPropertyFlag(XFA_Element property,
+                                XFA_PropertyFlag flag) const {
   const PropertyData* data = GetPropertyData(property);
-  return data && !!(data->flags & flags);
+  return data && !!(data->flags & flag);
 }
 
-uint8_t CXFA_Node::PropertyOccuranceCount(XFA_Element property) const {
+uint8_t CXFA_Node::PropertyOccurrenceCount(XFA_Element property) const {
   const PropertyData* data = GetPropertyData(property);
-  return data ? data->occurance_count : 0;
+  return data ? data->occurrence_count : 0;
 }
 
 std::pair<CXFA_Node*, int32_t> CXFA_Node::GetProperty(
     int32_t index,
     XFA_Element eProperty) const {
-  if (index < 0 || index >= PropertyOccuranceCount(eProperty))
+  if (index < 0 || index >= PropertyOccurrenceCount(eProperty))
     return {nullptr, 0};
 
   int32_t iCount = 0;
@@ -1084,7 +1135,7 @@
 
 CXFA_Node* CXFA_Node::GetOrCreateProperty(int32_t index,
                                           XFA_Element eProperty) {
-  if (index < 0 || index >= PropertyOccuranceCount(eProperty))
+  if (index < 0 || index >= PropertyOccurrenceCount(eProperty))
     return nullptr;
 
   int32_t iCount = 0;
@@ -1093,10 +1144,10 @@
   if (node)
     return node;
 
-  if (HasPropertyFlags(eProperty, XFA_PROPERTYFLAG_OneOf)) {
+  if (HasPropertyFlag(eProperty, XFA_PropertyFlag::kOneOf)) {
     for (CXFA_Node* pNode = GetFirstChild(); pNode;
          pNode = pNode->GetNextSibling()) {
-      if (HasPropertyFlags(pNode->GetElementType(), XFA_PROPERTYFLAG_OneOf)) {
+      if (HasPropertyFlag(pNode->GetElementType(), XFA_PropertyFlag::kOneOf)) {
         return nullptr;
       }
     }
@@ -1109,22 +1160,23 @@
       return nullptr;
 
     InsertChildAndNotify(pNewNode, nullptr);
-    pNewNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+    pNewNode->SetInitializedFlagAndNotify();
   }
   return pNewNode;
 }
 
-Optional<XFA_Element> CXFA_Node::GetFirstPropertyWithFlag(uint8_t flag) const {
+absl::optional<XFA_Element> CXFA_Node::GetFirstPropertyWithFlag(
+    XFA_PropertyFlag flag) const {
   for (const auto& prop : m_Properties) {
     if (prop.flags & flag)
       return prop.property;
   }
-  return {};
+  return absl::nullopt;
 }
 
 const CXFA_Node::AttributeData* CXFA_Node::GetAttributeData(
     XFA_Attribute attr) const {
-  ASSERT(attr != XFA_Attribute::Unknown);
+  DCHECK(attr != XFA_Attribute::Unknown);
   for (const auto& cur_attr : m_Attributes) {
     if (cur_attr.attribute == attr)
       return &cur_attr;
@@ -1157,29 +1209,32 @@
 }
 
 std::vector<CXFA_Node*> CXFA_Node::GetNodeListWithFilter(
-    uint32_t dwTypeFilter) {
+    Mask<XFA_NodeFilter> dwFilter) {
+  if (!dwFilter)
+    return std::vector<CXFA_Node*>();
+
+  const bool bFilterChildren = !!(dwFilter & XFA_NodeFilter::kChildren);
+  const bool bFilterProperties = !!(dwFilter & XFA_NodeFilter::kProperties);
+  const bool bFilterOneOfProperties =
+      !!(dwFilter & XFA_NodeFilter::kOneOfProperty);
+
   std::vector<CXFA_Node*> nodes;
-  if (dwTypeFilter == (XFA_NODEFILTER_Children | XFA_NODEFILTER_Properties)) {
+  if (bFilterChildren && bFilterProperties && !bFilterOneOfProperties) {
     for (CXFA_Node* pChild = GetFirstChild(); pChild;
-         pChild = pChild->GetNextSibling())
+         pChild = pChild->GetNextSibling()) {
       nodes.push_back(pChild);
+    }
     return nodes;
   }
 
-  if (dwTypeFilter == 0)
-    return nodes;
-
-  bool bFilterChildren = !!(dwTypeFilter & XFA_NODEFILTER_Children);
-  bool bFilterProperties = !!(dwTypeFilter & XFA_NODEFILTER_Properties);
-  bool bFilterOneOfProperties = !!(dwTypeFilter & XFA_NODEFILTER_OneOfProperty);
   for (CXFA_Node* pChild = GetFirstChild(); pChild;
        pChild = pChild->GetNextSibling()) {
     if (HasProperty(pChild->GetElementType())) {
       if (bFilterProperties) {
         nodes.push_back(pChild);
       } else if (bFilterOneOfProperties &&
-                 HasPropertyFlags(pChild->GetElementType(),
-                                  XFA_PROPERTYFLAG_OneOf)) {
+                 HasPropertyFlag(pChild->GetElementType(),
+                                 XFA_PropertyFlag::kOneOf)) {
         nodes.push_back(pChild);
       } else if (bFilterChildren &&
                  (pChild->GetElementType() == XFA_Element::Variables ||
@@ -1194,8 +1249,8 @@
   if (!bFilterOneOfProperties || !nodes.empty())
     return nodes;
 
-  Optional<XFA_Element> property =
-      GetFirstPropertyWithFlag(XFA_PROPERTYFLAG_DefaultOneOf);
+  absl::optional<XFA_Element> property =
+      GetFirstPropertyWithFlag(XFA_PropertyFlag::kDefaultOneOf);
   if (!property.has_value())
     return nodes;
 
@@ -1203,7 +1258,7 @@
       m_pDocument->CreateNode(GetPacketType(), property.value());
   if (pNewNode) {
     InsertChildAndNotify(pNewNode, nullptr);
-    pNewNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+    pNewNode->SetInitializedFlagAndNotify();
     nodes.push_back(pNewNode);
   }
   return nodes;
@@ -1214,12 +1269,12 @@
   if (!pNode)
     return nullptr;
 
-  pNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+  pNode->SetInitializedFlagAndNotify();
   return pNode;
 }
 
 CXFA_Node* CXFA_Node::CloneTemplateToForm(bool bRecursive) {
-  ASSERT(m_ePacket == XFA_PacketType::Template);
+  DCHECK_EQ(m_ePacket, XFA_PacketType::Template);
   CXFA_Node* pClone =
       m_pDocument->CreateNode(XFA_PacketType::Form, m_elementType);
   if (!pClone)
@@ -1235,7 +1290,7 @@
                                    nullptr);
     }
   }
-  pClone->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+  pClone->SetInitializedFlagAndNotify();
   return pClone;
 }
 
@@ -1248,44 +1303,38 @@
 }
 
 CXFA_Node* CXFA_Node::GetBindData() {
-  ASSERT(GetPacketType() == XFA_PacketType::Form);
+  DCHECK_EQ(GetPacketType(), XFA_PacketType::Form);
   return GetBindingNode();
 }
 
-int32_t CXFA_Node::AddBindItem(CXFA_Node* pFormNode) {
-  ASSERT(pFormNode);
+std::vector<CXFA_Node*> CXFA_Node::GetBindItemsCopy() const {
+  return std::vector<CXFA_Node*>(binding_nodes_.begin(), binding_nodes_.end());
+}
+
+void CXFA_Node::AddBindItem(CXFA_Node* pFormNode) {
+  DCHECK(pFormNode);
 
   if (BindsFormItems()) {
-    bool found = false;
-    for (auto* v : binding_nodes_) {
-      if (v == pFormNode) {
-        found = true;
-        break;
-      }
-    }
-    if (!found)
+    if (!pdfium::Contains(binding_nodes_, pFormNode))
       binding_nodes_.emplace_back(pFormNode);
-    return pdfium::CollectionSize<int32_t>(binding_nodes_);
+    return;
   }
 
   CXFA_Node* pOldFormItem = GetBindingNode();
   if (!pOldFormItem) {
     SetBindingNode(pFormNode);
-    return 1;
+    return;
   }
   if (pOldFormItem == pFormNode)
-    return 1;
+    return;
 
-  std::vector<CXFA_Node*> items;
-  items.push_back(pOldFormItem);
-  items.push_back(pFormNode);
-  binding_nodes_ = std::move(items);
-
-  m_uNodeFlags |= XFA_NodeFlag_BindFormItems;
-  return 2;
+  binding_nodes_.clear();
+  binding_nodes_.push_back(pOldFormItem);
+  binding_nodes_.push_back(pFormNode);
+  m_uNodeFlags |= XFA_NodeFlag::kBindFormItems;
 }
 
-int32_t CXFA_Node::RemoveBindItem(CXFA_Node* pFormNode) {
+bool CXFA_Node::RemoveBindItem(CXFA_Node* pFormNode) {
   if (BindsFormItems()) {
     auto it =
         std::find(binding_nodes_.begin(), binding_nodes_.end(), pFormNode);
@@ -1293,18 +1342,18 @@
       binding_nodes_.erase(it);
 
     if (binding_nodes_.size() == 1) {
-      m_uNodeFlags &= ~XFA_NodeFlag_BindFormItems;
-      return 1;
+      m_uNodeFlags.Clear(XFA_NodeFlag::kBindFormItems);
+      return true;
     }
-    return pdfium::CollectionSize<int32_t>(binding_nodes_);
+    return !binding_nodes_.empty();
   }
 
   CXFA_Node* pOldFormItem = GetBindingNode();
   if (pOldFormItem != pFormNode)
-    return pOldFormItem ? 1 : 0;
+    return !!pOldFormItem;
 
   SetBindingNode(nullptr);
-  return 0;
+  return false;
 }
 
 bool CXFA_Node::HasBindItem() const {
@@ -1325,7 +1374,7 @@
     if (IsChoiceListMultiSelect())
       return nullptr;
 
-    WideString wsPicture = GetPictureContent(XFA_VALUEPICTURE_DataBind);
+    WideString wsPicture = GetPictureContent(XFA_ValuePicture::kDataBind);
     if (!wsPicture.IsEmpty())
       return this;
 
@@ -1339,7 +1388,7 @@
         continue;
       pFieldNode = pFormNode->IsWidgetReady() ? pFormNode : nullptr;
       if (pFieldNode)
-        wsPicture = pFieldNode->GetPictureContent(XFA_VALUEPICTURE_DataBind);
+        wsPicture = pFieldNode->GetPictureContent(XFA_ValuePicture::kDataBind);
       if (!wsPicture.IsEmpty())
         break;
 
@@ -1364,8 +1413,8 @@
   return pParentOfValueNode ? pParentOfValueNode->GetContainerNode() : nullptr;
 }
 
-LocaleIface* CXFA_Node::GetLocale() {
-  Optional<WideString> localeName = GetLocaleName();
+GCedLocaleIface* CXFA_Node::GetLocale() {
+  absl::optional<WideString> localeName = GetLocaleName();
   if (!localeName.has_value())
     return nullptr;
   if (localeName.value().EqualsASCII("ambient"))
@@ -1373,42 +1422,42 @@
   return GetDocument()->GetLocaleMgr()->GetLocaleByName(localeName.value());
 }
 
-Optional<WideString> CXFA_Node::GetLocaleName() {
+absl::optional<WideString> CXFA_Node::GetLocaleName() {
   CXFA_Node* pForm = ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form));
   if (!pForm)
-    return {};
+    return absl::nullopt;
 
   CXFA_Subform* pTopSubform =
       pForm->GetFirstChildByClass<CXFA_Subform>(XFA_Element::Subform);
   if (!pTopSubform)
-    return {};
+    return absl::nullopt;
 
+  absl::optional<WideString> localeName;
   CXFA_Node* pLocaleNode = this;
   do {
-    Optional<WideString> localeName =
+    localeName =
         pLocaleNode->JSObject()->TryCData(XFA_Attribute::Locale, false);
-    if (localeName)
+    if (localeName.has_value())
       return localeName;
 
     pLocaleNode = pLocaleNode->GetParent();
   } while (pLocaleNode && pLocaleNode != pTopSubform);
 
   CXFA_Node* pConfig = ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Config));
-  WideString wsLocaleName =
-      GetDocument()->GetLocaleMgr()->GetConfigLocaleName(pConfig);
-  if (!wsLocaleName.IsEmpty())
-    return wsLocaleName;
+  localeName = GetDocument()->GetLocaleMgr()->GetConfigLocaleName(pConfig);
+  if (localeName.has_value())
+    return localeName;
 
   if (pTopSubform) {
-    Optional<WideString> localeName =
+    localeName =
         pTopSubform->JSObject()->TryCData(XFA_Attribute::Locale, false);
-    if (localeName)
+    if (localeName.has_value())
       return localeName;
   }
 
   LocaleIface* pLocale = GetDocument()->GetLocaleMgr()->GetDefLocale();
   if (!pLocale)
-    return {};
+    return absl::nullopt;
 
   return pLocale->GetName();
 }
@@ -1419,9 +1468,10 @@
   XFA_AttributeValue eLayoutType =
       layout.value_or(XFA_AttributeValue::Position);
   if (pKeep) {
-    Optional<XFA_AttributeValue> intact = GetIntactFromKeep(pKeep, eLayoutType);
-    if (intact)
-      return *intact;
+    absl::optional<XFA_AttributeValue> intact =
+        GetIntactFromKeep(pKeep, eLayoutType);
+    if (intact.has_value())
+      return intact.value();
   }
 
   switch (GetElementType()) {
@@ -1451,9 +1501,9 @@
 
       XFA_VERSION version = m_pDocument->GetCurVersionMode();
       if (eParLayout == XFA_AttributeValue::Tb && version < XFA_VERSION_208) {
-        Optional<CXFA_Measurement> measureH =
+        absl::optional<CXFA_Measurement> measureH =
             JSObject()->TryMeasure(XFA_Attribute::H, false);
-        if (measureH)
+        if (measureH.has_value())
           return XFA_AttributeValue::ContentArea;
       }
       return XFA_AttributeValue::None;
@@ -1485,7 +1535,7 @@
 }
 
 void CXFA_Node::SetDataDescriptionNode(CXFA_Node* pDataDescriptionNode) {
-  ASSERT(m_ePacket == XFA_PacketType::Datasets);
+  DCHECK_EQ(m_ePacket, XFA_PacketType::Datasets);
   m_pAuxNode = pDataDescriptionNode;
 }
 
@@ -1545,6 +1595,15 @@
   return nullptr;
 }
 
+bool CXFA_Node::IsAncestorOf(const CXFA_Node* that) const {
+  while (that) {
+    if (this == that)
+      return true;
+    that = that->GetParent();
+  }
+  return false;
+}
+
 void CXFA_Node::InsertChildAndNotify(int32_t index, CXFA_Node* pNode) {
   InsertChildAndNotify(pNode, GetNthChild(index));
 }
@@ -1552,7 +1611,7 @@
 void CXFA_Node::InsertChildAndNotify(CXFA_Node* pNode, CXFA_Node* pBeforeNode) {
   CHECK(!pNode->GetParent());
   CHECK(!pBeforeNode || pBeforeNode->GetParent() == this);
-  pNode->ClearFlag(XFA_NodeFlag_HasRemovedChildren);
+  pNode->ClearFlag(XFA_NodeFlag::kHasRemovedChildren);
   InsertBefore(pNode, pBeforeNode);
 
   CXFA_FFNotify* pNotify = m_pDocument->GetNotify();
@@ -1562,9 +1621,9 @@
   if (!IsNeedSavingXMLNode() || !pNode->xml_node_)
     return;
 
-  ASSERT(!pNode->xml_node_->GetParent());
-  xml_node_->InsertBefore(pNode->xml_node_.Get(),
-                          pBeforeNode ? pBeforeNode->xml_node_.Get() : nullptr);
+  DCHECK(!pNode->xml_node_->GetParent());
+  xml_node_->InsertBefore(pNode->xml_node_,
+                          pBeforeNode ? pBeforeNode->xml_node_ : nullptr);
 }
 
 void CXFA_Node::RemoveChildAndNotify(CXFA_Node* pNode, bool bNotify) {
@@ -1572,20 +1631,20 @@
   if (pNode->GetParent() != this)
     return;
 
-  pNode->SetFlag(XFA_NodeFlag_HasRemovedChildren);
-  TreeNode<CXFA_Node>::RemoveChild(pNode);
+  pNode->SetFlag(XFA_NodeFlag::kHasRemovedChildren);
+  GCedTreeNodeMixin<CXFA_Node>::RemoveChild(pNode);
   OnRemoved(bNotify);
 
   if (!IsNeedSavingXMLNode() || !pNode->xml_node_)
     return;
 
   if (!pNode->IsAttributeInXML()) {
-    xml_node_->RemoveChild(pNode->xml_node_.Get());
+    xml_node_->RemoveChild(pNode->xml_node_);
     return;
   }
 
-  ASSERT(pNode->xml_node_ == xml_node_);
-  CFX_XMLElement* pXMLElement = ToXMLElement(pNode->xml_node_.Get());
+  DCHECK_EQ(pNode->xml_node_, xml_node_);
+  CFX_XMLElement* pXMLElement = ToXMLElement(pNode->xml_node_);
   if (pXMLElement) {
     WideString wsAttributeName =
         pNode->JSObject()->GetCData(XFA_Attribute::QualifiedName);
@@ -1608,7 +1667,7 @@
 }
 
 CXFA_Node* CXFA_Node::GetFirstChildByName(WideStringView wsName) const {
-  return GetFirstChildByName(FX_HashCode_GetW(wsName, false));
+  return GetFirstChildByName(FX_HashCode_GetW(wsName));
 }
 
 CXFA_Node* CXFA_Node::GetFirstChildByName(uint32_t dwNameHash) const {
@@ -1640,7 +1699,7 @@
 
 CXFA_Node* CXFA_Node::GetNextSameNameSiblingInternal(
     WideStringView wsNodeName) const {
-  return GetNextSameNameSibling(FX_HashCode_GetW(wsNodeName, false));
+  return GetNextSameNameSibling(FX_HashCode_GetW(wsNodeName));
 }
 
 CXFA_Node* CXFA_Node::GetNextSameClassSiblingInternal(XFA_Element eType) const {
@@ -1653,7 +1712,7 @@
 }
 
 CXFA_Node* CXFA_Node::GetOneChildNamed(WideStringView wsName) {
-  return FindFirstSiblingNamed(this, FX_HashCode_GetW(wsName, false));
+  return FindFirstSiblingNamed(this, FX_HashCode_GetW(wsName));
 }
 
 CXFA_Node* CXFA_Node::GetOneChildOfClass(WideStringView wsClass) {
@@ -1676,7 +1735,7 @@
   }
 
   uint32_t dwNameHash = bIsClassName ? GetClassHashCode() : GetNameHash();
-  TraverseSiblings(parent, dwNameHash, &siblings, bIsClassName, true);
+  TraversePropertiesOrSiblings(parent, dwNameHash, &siblings, bIsClassName);
   return siblings;
 }
 
@@ -1692,7 +1751,7 @@
   }
   uint32_t dwHashName = bIsClassIndex ? GetClassHashCode() : GetNameHash();
   std::vector<CXFA_Node*> siblings;
-  TraverseSiblings(parent, dwHashName, &siblings, bIsClassIndex, true);
+  TraversePropertiesOrSiblings(parent, dwHashName, &siblings, bIsClassIndex);
   for (size_t i = 0; i < siblings.size(); ++i) {
     if (siblings[i] == this)
       return i;
@@ -1744,29 +1803,26 @@
 bool CXFA_Node::HasFlag(XFA_NodeFlag dwFlag) const {
   if (m_uNodeFlags & dwFlag)
     return true;
-  if (dwFlag == XFA_NodeFlag_HasRemovedChildren)
+  if (dwFlag == XFA_NodeFlag::kHasRemovedChildren)
     return GetParent() && GetParent()->HasFlag(dwFlag);
   return false;
 }
 
-void CXFA_Node::SetFlagAndNotify(uint32_t dwFlag) {
-  ASSERT(dwFlag == XFA_NodeFlag_Initialized);
-
+void CXFA_Node::SetInitializedFlagAndNotify() {
   if (!IsInitialized()) {
     CXFA_FFNotify* pNotify = m_pDocument->GetNotify();
-    if (pNotify) {
+    if (pNotify)
       pNotify->OnNodeReady(this);
-    }
   }
+  m_uNodeFlags |= XFA_NodeFlag::kInitialized;
+}
+
+void CXFA_Node::SetFlag(XFA_NodeFlag dwFlag) {
   m_uNodeFlags |= dwFlag;
 }
 
-void CXFA_Node::SetFlag(uint32_t dwFlag) {
-  m_uNodeFlags |= dwFlag;
-}
-
-void CXFA_Node::ClearFlag(uint32_t dwFlag) {
-  m_uNodeFlags &= ~dwFlag;
+void CXFA_Node::ClearFlag(XFA_NodeFlag dwFlag) {
+  m_uNodeFlags.Clear(dwFlag);
 }
 
 bool CXFA_Node::IsAttributeInXML() {
@@ -1785,7 +1841,7 @@
 
 void CXFA_Node::UpdateNameHash() {
   WideString wsName = JSObject()->GetCData(XFA_Attribute::Name);
-  m_dwNameHash = FX_HashCode_GetW(wsName.AsStringView(), false);
+  m_dwNameHash = FX_HashCode_GetW(wsName.AsStringView());
 }
 
 CFX_XMLNode* CXFA_Node::CreateXMLMappingNode() {
@@ -1793,7 +1849,7 @@
     xml_node_ = GetXMLDocument()->CreateNode<CFX_XMLElement>(
         JSObject()->GetCData(XFA_Attribute::Name));
   }
-  return xml_node_.Get();
+  return xml_node_;
 }
 
 bool CXFA_Node::IsNeedSavingXMLNode() const {
@@ -1878,29 +1934,25 @@
         iCount > 0 ? item->GetNextSibling() : GetNextSibling();
     GetParent()->InsertChildAndNotify(pNewInstance, pNextSibling);
     if (bMoveDataBindingNodes) {
-      std::set<CXFA_Node*> sNew;
-      std::set<CXFA_Node*> sAfter;
+      NodeSet sNew;
       CXFA_NodeIteratorTemplate<CXFA_Node,
                                 CXFA_TraverseStrategy_XFAContainerNode>
           sIteratorNew(pNewInstance);
       for (CXFA_Node* pNode = sIteratorNew.GetCurrent(); pNode;
            pNode = sIteratorNew.MoveToNext()) {
         CXFA_Node* pDataNode = pNode->GetBindData();
-        if (!pDataNode)
-          continue;
-
-        sNew.insert(pDataNode);
+        if (pDataNode)
+          sNew.insert(pDataNode);
       }
+      NodeSet sAfter;
       CXFA_NodeIteratorTemplate<CXFA_Node,
                                 CXFA_TraverseStrategy_XFAContainerNode>
           sIteratorAfter(pNextSibling);
       for (CXFA_Node* pNode = sIteratorAfter.GetCurrent(); pNode;
            pNode = sIteratorAfter.MoveToNext()) {
         CXFA_Node* pDataNode = pNode->GetBindData();
-        if (!pDataNode)
-          continue;
-
-        sAfter.insert(pDataNode);
+        if (pDataNode)
+          sAfter.insert(pDataNode);
       }
       ReorderDataNodes(sNew, sAfter, false);
     }
@@ -1913,29 +1965,25 @@
 
     GetParent()->InsertChildAndNotify(pNewInstance, pBeforeInstance);
     if (bMoveDataBindingNodes) {
-      std::set<CXFA_Node*> sNew;
-      std::set<CXFA_Node*> sBefore;
+      NodeSet sNew;
       CXFA_NodeIteratorTemplate<CXFA_Node,
                                 CXFA_TraverseStrategy_XFAContainerNode>
           sIteratorNew(pNewInstance);
       for (CXFA_Node* pNode = sIteratorNew.GetCurrent(); pNode;
            pNode = sIteratorNew.MoveToNext()) {
         CXFA_Node* pDataNode = pNode->GetBindData();
-        if (!pDataNode)
-          continue;
-
-        sNew.insert(pDataNode);
+        if (pDataNode)
+          sNew.insert(pDataNode);
       }
+      NodeSet sBefore;
       CXFA_NodeIteratorTemplate<CXFA_Node,
                                 CXFA_TraverseStrategy_XFAContainerNode>
           sIteratorBefore(pBeforeInstance);
       for (CXFA_Node* pNode = sIteratorBefore.GetCurrent(); pNode;
            pNode = sIteratorBefore.MoveToNext()) {
         CXFA_Node* pDataNode = pNode->GetBindData();
-        if (!pDataNode)
-          continue;
-
-        sBefore.insert(pDataNode);
+        if (pDataNode)
+          sBefore.insert(pDataNode);
       }
       ReorderDataNodes(sNew, sBefore, true);
     }
@@ -1956,7 +2004,7 @@
     if (!pDataNode)
       continue;
 
-    if (pDataNode->RemoveBindItem(pFormNode) == 0) {
+    if (!pDataNode->RemoveBindItem(pFormNode)) {
       if (CXFA_Node* pDataParent = pDataNode->GetParent()) {
         pDataParent->RemoveChildAndNotify(pDataNode, true);
       }
@@ -1982,7 +2030,7 @@
   }
   if (!pDataScope) {
     pDataScope = ToNode(pDocument->GetXFAObject(XFA_HASHCODE_Record));
-    ASSERT(pDataScope);
+    DCHECK(pDataScope);
   }
 
   CXFA_Node* pInstance = pDocument->DataMerge_CopyContainer(
@@ -1994,54 +2042,58 @@
   return pInstance;
 }
 
-Optional<bool> CXFA_Node::GetDefaultBoolean(XFA_Attribute attr) const {
-  Optional<void*> value = GetDefaultValue(attr, XFA_AttributeType::Boolean);
-  if (!value)
-    return {};
-  return {!!*value};
+absl::optional<bool> CXFA_Node::GetDefaultBoolean(XFA_Attribute attr) const {
+  absl::optional<void*> value =
+      GetDefaultValue(attr, XFA_AttributeType::Boolean);
+  if (!value.has_value())
+    return absl::nullopt;
+  return !!value.value();
 }
 
-Optional<int32_t> CXFA_Node::GetDefaultInteger(XFA_Attribute attr) const {
-  Optional<void*> value = GetDefaultValue(attr, XFA_AttributeType::Integer);
-  if (!value)
-    return {};
-  return {static_cast<int32_t>(reinterpret_cast<uintptr_t>(*value))};
+absl::optional<int32_t> CXFA_Node::GetDefaultInteger(XFA_Attribute attr) const {
+  absl::optional<void*> value =
+      GetDefaultValue(attr, XFA_AttributeType::Integer);
+  if (!value.has_value())
+    return absl::nullopt;
+  return static_cast<int32_t>(reinterpret_cast<uintptr_t>(value.value()));
 }
 
-Optional<CXFA_Measurement> CXFA_Node::GetDefaultMeasurement(
+absl::optional<CXFA_Measurement> CXFA_Node::GetDefaultMeasurement(
     XFA_Attribute attr) const {
-  Optional<void*> value = GetDefaultValue(attr, XFA_AttributeType::Measure);
-  if (!value)
-    return {};
+  absl::optional<void*> value =
+      GetDefaultValue(attr, XFA_AttributeType::Measure);
+  if (!value.has_value())
+    return absl::nullopt;
 
-  WideString str = WideString(static_cast<const wchar_t*>(*value));
-  return {CXFA_Measurement(str.AsStringView())};
+  WideString str = WideString(static_cast<const wchar_t*>(value.value()));
+  return CXFA_Measurement(str.AsStringView());
 }
 
-Optional<WideString> CXFA_Node::GetDefaultCData(XFA_Attribute attr) const {
-  Optional<void*> value = GetDefaultValue(attr, XFA_AttributeType::CData);
-  if (!value)
-    return {};
-
-  return {WideString(static_cast<const wchar_t*>(*value))};
-}
-
-Optional<XFA_AttributeValue> CXFA_Node::GetDefaultEnum(
+absl::optional<WideString> CXFA_Node::GetDefaultCData(
     XFA_Attribute attr) const {
-  Optional<void*> value = GetDefaultValue(attr, XFA_AttributeType::Enum);
-  if (!value)
-    return {};
-  return {static_cast<XFA_AttributeValue>(reinterpret_cast<uintptr_t>(*value))};
+  absl::optional<void*> value = GetDefaultValue(attr, XFA_AttributeType::CData);
+  if (!value.has_value())
+    return absl::nullopt;
+
+  return WideString(static_cast<const wchar_t*>(value.value()));
 }
 
-Optional<void*> CXFA_Node::GetDefaultValue(XFA_Attribute attr,
-                                           XFA_AttributeType eType) const {
+absl::optional<XFA_AttributeValue> CXFA_Node::GetDefaultEnum(
+    XFA_Attribute attr) const {
+  absl::optional<void*> value = GetDefaultValue(attr, XFA_AttributeType::Enum);
+  if (!value.has_value())
+    return absl::nullopt;
+  return static_cast<XFA_AttributeValue>(
+      reinterpret_cast<uintptr_t>(value.value()));
+}
+
+absl::optional<void*> CXFA_Node::GetDefaultValue(
+    XFA_Attribute attr,
+    XFA_AttributeType eType) const {
   const AttributeData* data = GetAttributeData(attr);
-  if (!data)
-    return {};
-  if (data->type == eType)
-    return {data->default_value};
-  return {};
+  if (!data || data->type != eType)
+    return absl::nullopt;
+  return data->default_value;
 }
 
 void CXFA_Node::SendAttributeChangeMessage(XFA_Attribute eAttribute,
@@ -2140,7 +2192,7 @@
     case XFA_Element::Field:
     case XFA_Element::Subform:
     case XFA_Element::SubformSet:
-      pNotify->OnContainerChanged(this);
+      pNotify->OnContainerChanged();
       pNotify->OnValueChanged(this, eAttribute, this, this);
       break;
     case XFA_Element::Sharptext:
@@ -2188,7 +2240,7 @@
     pParent = pParent->GetParent();
 
   if (pParent)
-    pNotify->OnContainerChanged(pParent);
+    pNotify->OnContainerChanged();
 }
 
 void CXFA_Node::SyncValue(const WideString& wsValue, bool bNotify) {
@@ -2200,14 +2252,14 @@
   JSObject()->SetContent(wsValue, wsFormatValue, bNotify, false, true);
 }
 
-WideString CXFA_Node::GetRawValue() {
+WideString CXFA_Node::GetRawValue() const {
   return JSObject()->GetContent(false);
 }
 
 int32_t CXFA_Node::GetRotate() const {
-  Optional<int32_t> degrees =
+  absl::optional<int32_t> degrees =
       JSObject()->TryInteger(XFA_Attribute::Rotate, false);
-  return degrees ? XFA_MapRotation(*degrees) / 90 * 90 : 0;
+  return degrees.has_value() ? XFA_MapRotation(degrees.value()) / 90 * 90 : 0;
 }
 
 CXFA_Border* CXFA_Node::GetBorderIfExists() const {
@@ -2297,13 +2349,13 @@
   return JSObject()->GetProperty<CXFA_Bind>(0, XFA_Element::Bind);
 }
 
-Optional<XFA_AttributeValue> CXFA_Node::GetIntactFromKeep(
+absl::optional<XFA_AttributeValue> CXFA_Node::GetIntactFromKeep(
     const CXFA_Keep* pKeep,
     XFA_AttributeValue eLayoutType) const {
-  Optional<XFA_AttributeValue> intact =
+  absl::optional<XFA_AttributeValue> intact =
       pKeep->JSObject()->TryEnum(XFA_Attribute::Intact, false);
   if (!intact.has_value())
-    return {};
+    return absl::nullopt;
 
   if (intact.value() != XFA_AttributeValue::None ||
       eLayoutType != XFA_AttributeValue::Row ||
@@ -2317,10 +2369,10 @@
     return intact;
   }
 
-  Optional<XFA_AttributeValue> value =
+  absl::optional<XFA_AttributeValue> value =
       pKeep->JSObject()->TryEnum(XFA_Attribute::Previous, false);
-  if (value && (*value == XFA_AttributeValue::ContentArea ||
-                *value == XFA_AttributeValue::PageArea)) {
+  if (value == XFA_AttributeValue::ContentArea ||
+      value == XFA_AttributeValue::PageArea) {
     return XFA_AttributeValue::ContentArea;
   }
 
@@ -2329,38 +2381,36 @@
   if (!pNode)
     return intact;
 
-  Optional<XFA_AttributeValue> ret =
+  absl::optional<XFA_AttributeValue> ret =
       pNode->JSObject()->TryEnum(XFA_Attribute::Next, false);
-  if (!ret)
-    return intact;
-
-  return (*ret == XFA_AttributeValue::ContentArea ||
-          *ret == XFA_AttributeValue::PageArea)
-             ? XFA_AttributeValue::ContentArea
-             : intact;
+  if (ret == XFA_AttributeValue::ContentArea ||
+      ret == XFA_AttributeValue::PageArea) {
+    return XFA_AttributeValue::ContentArea;
+  }
+  return intact;
 }
 
-Optional<float> CXFA_Node::TryWidth() {
+absl::optional<float> CXFA_Node::TryWidth() {
   return JSObject()->TryMeasureAsFloat(XFA_Attribute::W);
 }
 
-Optional<float> CXFA_Node::TryHeight() {
+absl::optional<float> CXFA_Node::TryHeight() {
   return JSObject()->TryMeasureAsFloat(XFA_Attribute::H);
 }
 
-Optional<float> CXFA_Node::TryMinWidth() {
+absl::optional<float> CXFA_Node::TryMinWidth() {
   return JSObject()->TryMeasureAsFloat(XFA_Attribute::MinW);
 }
 
-Optional<float> CXFA_Node::TryMinHeight() {
+absl::optional<float> CXFA_Node::TryMinHeight() {
   return JSObject()->TryMeasureAsFloat(XFA_Attribute::MinH);
 }
 
-Optional<float> CXFA_Node::TryMaxWidth() {
+absl::optional<float> CXFA_Node::TryMaxWidth() {
   return JSObject()->TryMeasureAsFloat(XFA_Attribute::MaxW);
 }
 
-Optional<float> CXFA_Node::TryMaxHeight() {
+absl::optional<float> CXFA_Node::TryMaxHeight() {
   return JSObject()->TryMeasureAsFloat(XFA_Attribute::MaxH);
 }
 
@@ -2440,13 +2490,14 @@
 
   CXFA_EventParam EventParam;
   EventParam.m_eType = XFA_EVENT_Calculate;
+  EventParam.m_bTargeted = false;
   XFA_EventError iRet =
       ExecuteScript(pDocView, calc->GetScriptIfExists(), &EventParam);
   if (iRet != XFA_EventError::kSuccess)
     return iRet;
 
   if (GetRawValue() != EventParam.m_wsResult) {
-    SetValue(XFA_VALUEPICTURE_Raw, EventParam.m_wsResult);
+    SetValue(XFA_ValuePicture::kRaw, EventParam.m_wsResult);
     pDocView->UpdateUIDisplay(this, nullptr);
   }
   return XFA_EventError::kSuccess;
@@ -2454,15 +2505,8 @@
 
 void CXFA_Node::ProcessScriptTestValidate(CXFA_FFDocView* pDocView,
                                           CXFA_Validate* validate,
-                                          XFA_EventError iRet,
-                                          bool bRetValue,
                                           bool bVersionFlag) {
-  if (iRet != XFA_EventError::kSuccess)
-    return;
-  if (bRetValue)
-    return;
-
-  IXFA_AppProvider* pAppProvider =
+  CXFA_FFApp::CallbackIface* pAppProvider =
       pDocView->GetDoc()->GetApp()->GetAppProvider();
   if (!pAppProvider)
     return;
@@ -2485,7 +2529,7 @@
                              static_cast<uint32_t>(AlertIcon::kWarning),
                              static_cast<uint32_t>(AlertButton::kYesNo)) ==
         static_cast<uint32_t>(AlertReturn::kYes)) {
-      SetFlag(XFA_NodeFlag_UserInteractive);
+      SetFlag(XFA_NodeFlag::kUserInteractive);
     }
     return;
   }
@@ -2508,7 +2552,7 @@
   if (wsRawValue.IsEmpty())
     return XFA_EventError::kError;
 
-  LocaleIface* pLocale = GetLocale();
+  GCedLocaleIface* pLocale = GetLocale();
   if (!pLocale)
     return XFA_EventError::kNotExist;
 
@@ -2516,7 +2560,7 @@
   if (lcValue.ValidateValue(lcValue.GetValue(), wsPicture, pLocale, nullptr))
     return XFA_EventError::kSuccess;
 
-  IXFA_AppProvider* pAppProvider =
+  CXFA_FFApp::CallbackIface* pAppProvider =
       pDocView->GetDoc()->GetApp()->GetAppProvider();
   if (!pAppProvider)
     return XFA_EventError::kNotExist;
@@ -2546,7 +2590,7 @@
                            static_cast<uint32_t>(AlertIcon::kWarning),
                            static_cast<uint32_t>(AlertButton::kYesNo)) ==
       static_cast<uint32_t>(AlertReturn::kYes)) {
-    SetFlag(XFA_NodeFlag_UserInteractive);
+    SetFlag(XFA_NodeFlag::kUserInteractive);
   }
 
   return XFA_EventError::kError;
@@ -2556,7 +2600,7 @@
                                                   CXFA_Validate* validate,
                                                   int32_t iFlags,
                                                   bool bVersionFlag) {
-  if (!GetValue(XFA_VALUEPICTURE_Raw).IsEmpty())
+  if (!GetValue(XFA_ValuePicture::kRaw).IsEmpty())
     return XFA_EventError::kSuccess;
   if (m_bIsNull && m_bPreNull)
     return XFA_EventError::kSuccess;
@@ -2564,24 +2608,18 @@
   XFA_AttributeValue eNullTest = validate->GetNullTest();
   WideString wsNullMsg = validate->GetNullMessageText();
   if (iFlags & 0x01) {
-    XFA_EventError iRet = XFA_EventError::kSuccess;
-    if (eNullTest != XFA_AttributeValue::Disabled)
-      iRet = XFA_EventError::kError;
+    if (eNullTest == XFA_AttributeValue::Disabled)
+      return XFA_EventError::kSuccess;
 
-    if (wsNullMsg.IsEmpty())
-      return iRet;
-
-    if (eNullTest != XFA_AttributeValue::Disabled) {
-      pDocView->m_arrNullTestMsg.push_back(wsNullMsg);
-      return XFA_EventError::kError;
-    }
-    return XFA_EventError::kSuccess;
+    if (!wsNullMsg.IsEmpty())
+      pDocView->AddNullTestMsg(wsNullMsg);
+    return XFA_EventError::kError;
   }
   if (wsNullMsg.IsEmpty() && bVersionFlag &&
       eNullTest != XFA_AttributeValue::Disabled) {
     return XFA_EventError::kError;
   }
-  IXFA_AppProvider* pAppProvider =
+  CXFA_FFApp::CallbackIface* pAppProvider =
       pDocView->GetDoc()->GetApp()->GetAppProvider();
   if (!pAppProvider)
     return XFA_EventError::kNotExist;
@@ -2613,7 +2651,7 @@
                                static_cast<uint32_t>(AlertIcon::kWarning),
                                static_cast<uint32_t>(AlertButton::kYesNo)) ==
           static_cast<uint32_t>(AlertReturn::kYes)) {
-        SetFlag(XFA_NodeFlag_UserInteractive);
+        SetFlag(XFA_NodeFlag::kUserInteractive);
       }
       return XFA_EventError::kError;
     }
@@ -2633,8 +2671,10 @@
   if (!validate)
     return XFA_EventError::kNotExist;
 
-  bool bInitDoc = validate->NeedsInitApp();
-  bool bStatus = pDocView->GetLayoutStatus() < XFA_DOCVIEW_LAYOUTSTATUS_End;
+  const bool bInitDoc = validate->NeedsInitApp();
+  const bool bStatus =
+      pDocView->GetLayoutStatus() != CXFA_FFDocView::LayoutStatus::kEnd;
+
   XFA_EventError iFormat = XFA_EventError::kNotExist;
   XFA_EventError iRet = XFA_EventError::kNotExist;
   CXFA_Script* script = validate->GetScriptIfExists();
@@ -2643,7 +2683,6 @@
   if (script) {
     CXFA_EventParam eParam;
     eParam.m_eType = XFA_EVENT_Validate;
-    eParam.m_pTarget = this;
     std::tie(iRet, bRet) = ExecuteBoolScript(pDocView, script, &eParam);
   }
 
@@ -2651,7 +2690,7 @@
   bool bVersionFlag = version < XFA_VERSION_208;
 
   if (bInitDoc) {
-    validate->ClearFlag(XFA_NodeFlag_NeedsInitApp);
+    validate->ClearFlag(XFA_NodeFlag::kNeedsInitApp);
   } else {
     iFormat = ProcessFormatTestValidate(pDocView, validate, bVersionFlag);
     if (!bVersionFlag)
@@ -2660,9 +2699,10 @@
         &iRet,
         ProcessNullTestValidate(pDocView, validate, iFlags, bVersionFlag));
   }
-  if (iFormat != XFA_EventError::kSuccess && hasBoolResult)
-    ProcessScriptTestValidate(pDocView, validate, iRet, bRet, bVersionFlag);
-
+  if (iRet == XFA_EventError::kSuccess && iFormat != XFA_EventError::kSuccess &&
+      hasBoolResult && !bRet) {
+    ProcessScriptTestValidate(pDocView, validate, bVersionFlag);
+  }
   XFA_EventErrorAccumulate(&iRet, iFormat);
   return iRet;
 }
@@ -2712,7 +2752,7 @@
   if (m_ExecuteRecursionDepth > kMaxExecuteRecursion)
     return {XFA_EventError::kSuccess, false};
 
-  ASSERT(pEventParam);
+  DCHECK(pEventParam);
   if (!script)
     return {XFA_EventError::kNotExist, false};
   if (script->GetRunAt() == XFA_AttributeValue::Server)
@@ -2728,16 +2768,17 @@
 
   CXFA_FFDoc* pDoc = pDocView->GetDoc();
   CFXJSE_Engine* pContext = pDoc->GetXFADoc()->GetScriptContext();
-  pContext->SetEventParam(pEventParam);
+  CFXJSE_Engine::EventParamScope paramScope(
+      pContext, pEventParam->m_bTargeted ? this : nullptr, pEventParam);
   pContext->SetRunAtType(script->GetRunAt());
 
-  std::vector<CXFA_Node*> refNodes;
+  std::vector<cppgc::Persistent<CXFA_Node>> refNodes;
   if (pEventParam->m_eType == XFA_EVENT_InitCalculate ||
       pEventParam->m_eType == XFA_EVENT_Calculate) {
     pContext->SetNodesOfRunScript(&refNodes);
   }
 
-  auto pTmpRetValue = pdfium::MakeUnique<CFXJSE_Value>(pContext->GetIsolate());
+  auto pTmpRetValue = std::make_unique<CFXJSE_Value>();
   bool bRet = false;
   {
     AutoRestorer<uint8_t> restorer(&m_ExecuteRecursionDepth);
@@ -2751,9 +2792,10 @@
     iRet = XFA_EventError::kSuccess;
     if (pEventParam->m_eType == XFA_EVENT_Calculate ||
         pEventParam->m_eType == XFA_EVENT_InitCalculate) {
-      if (!pTmpRetValue->IsUndefined()) {
-        if (!pTmpRetValue->IsNull())
-          pEventParam->m_wsResult = pTmpRetValue->ToWideString();
+      if (!pTmpRetValue->IsUndefined(pContext->GetIsolate())) {
+        if (!pTmpRetValue->IsNull(pContext->GetIsolate()))
+          pEventParam->m_wsResult =
+              pTmpRetValue->ToWideString(pContext->GetIsolate());
 
         iRet = XFA_EventError::kSuccess;
       } else {
@@ -2762,7 +2804,7 @@
       if (pEventParam->m_eType == XFA_EVENT_InitCalculate) {
         if ((iRet == XFA_EventError::kSuccess) &&
             (GetRawValue() != pEventParam->m_wsResult)) {
-          SetValue(XFA_VALUEPICTURE_Raw, pEventParam->m_wsResult);
+          SetValue(XFA_ValuePicture::kRaw, pEventParam->m_wsResult);
           pDocView->AddValidateNode(this);
         }
       }
@@ -2770,32 +2812,28 @@
         if (pRefNode == this)
           continue;
 
-        CXFA_CalcData* pGlobalData = pRefNode->JSObject()->GetCalcData();
-        if (!pGlobalData) {
-          pRefNode->JSObject()->SetCalcData(
-              pdfium::MakeUnique<CXFA_CalcData>());
-          pGlobalData = pRefNode->JSObject()->GetCalcData();
-        }
-        if (!pdfium::ContainsValue(pGlobalData->m_Globals, this))
+        CJX_Object::CalcData* pGlobalData =
+            pRefNode->JSObject()->GetOrCreateCalcData(pDoc->GetHeap());
+        if (!pdfium::Contains(pGlobalData->m_Globals, this))
           pGlobalData->m_Globals.push_back(this);
       }
     }
   }
   pContext->SetNodesOfRunScript(nullptr);
-  pContext->SetEventParam(nullptr);
 
-  return {iRet, pTmpRetValue->IsBoolean() && pTmpRetValue->ToBoolean()};
+  return {iRet, pTmpRetValue->IsBoolean(pContext->GetIsolate()) &&
+                    pTmpRetValue->ToBoolean(pContext->GetIsolate())};
 }
 
 std::pair<XFA_FFWidgetType, CXFA_Ui*>
 CXFA_Node::CreateChildUIAndValueNodesIfNeeded() {
   XFA_Element eType = GetElementType();
-  ASSERT(eType == XFA_Element::Field || eType == XFA_Element::Draw);
+  DCHECK(eType == XFA_Element::Field || eType == XFA_Element::Draw);
 
   // Both Field and Draw have a UI property. We should always be able to
   // retrieve or create the UI element. If we can't something is wrong.
   CXFA_Ui* pUI = JSObject()->GetOrCreateProperty<CXFA_Ui>(0, XFA_Element::Ui);
-  ASSERT(pUI);
+  DCHECK(pUI);
 
   CXFA_Node* pUIChild = nullptr;
   // Search through the children of the UI node to see if we have any of our
@@ -2816,7 +2854,7 @@
   // reason something has gone really wrong.
   CXFA_Value* value =
       JSObject()->GetOrCreateProperty<CXFA_Value>(0, XFA_Element::Value);
-  ASSERT(value);
+  DCHECK(value);
 
   // The Value nodes only have One-Of children. So, if we have a first child
   // that child must be the type we want to use.
@@ -2858,7 +2896,6 @@
         widget_type = XFA_FFWidgetType::kRectangle;
         break;
       default:
-        NOTREACHED();
         break;
     }
   }
@@ -2881,7 +2918,7 @@
       widget_type = XFA_FFWidgetType::kTextEdit;
     }
   } else {
-    NOTREACHED();
+    NOTREACHED_NORETURN();
   }
 
   if (!pUIChild) {
@@ -2896,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) {
@@ -2929,7 +2965,7 @@
 }
 
 CXFA_Node* CXFA_Node::GetUIChildNode() {
-  ASSERT(HasCreatedUIWidget());
+  DCHECK(HasCreatedUIWidget());
 
   if (ff_widget_type_ != XFA_FFWidgetType::kNone)
     return ui_ ? ui_->GetFirstChild() : nullptr;
@@ -2942,7 +2978,7 @@
   } else if (type == XFA_Element::ExclGroup) {
     ff_widget_type_ = XFA_FFWidgetType::kExclGroup;
   } else {
-    NOTREACHED();
+    NOTREACHED_NORETURN();
   }
   return ui_ ? ui_->GetFirstChild() : nullptr;
 }
@@ -2973,24 +3009,25 @@
   if (border && border->GetPresence() != XFA_AttributeValue::Visible)
     return CFX_RectF();
 
-  Optional<float> left = mgUI->TryLeftInset();
-  Optional<float> top = mgUI->TryTopInset();
-  Optional<float> right = mgUI->TryRightInset();
-  Optional<float> bottom = mgUI->TryBottomInset();
+  absl::optional<float> left = mgUI->TryLeftInset();
+  absl::optional<float> top = mgUI->TryTopInset();
+  absl::optional<float> right = mgUI->TryRightInset();
+  absl::optional<float> bottom = mgUI->TryBottomInset();
   if (border) {
     bool bVisible = false;
     float fThickness = 0;
     XFA_AttributeValue iType = XFA_AttributeValue::Unknown;
     std::tie(iType, bVisible, fThickness) = border->Get3DStyle();
-    if (!left || !top || !right || !bottom) {
+    if (!left.has_value() || !top.has_value() || !right.has_value() ||
+        !bottom.has_value()) {
       std::vector<CXFA_Stroke*> strokes = border->GetStrokes();
-      if (!top)
+      if (!top.has_value())
         top = GetEdgeThickness(strokes, bVisible, 0);
-      if (!right)
+      if (!right.has_value())
         right = GetEdgeThickness(strokes, bVisible, 1);
-      if (!bottom)
+      if (!bottom.has_value())
         bottom = GetEdgeThickness(strokes, bVisible, 2);
-      if (!left)
+      if (!left.has_value())
         left = GetEdgeThickness(strokes, bVisible, 3);
     }
   }
@@ -3052,8 +3089,8 @@
           CXFA_Value* defValue = pChild->GetDefaultValueIfExists();
           if (defValue) {
             wsValue = defValue->GetChildValueContent();
-            SetValue(XFA_VALUEPICTURE_Raw, wsValue);
-            pChild->SetValue(XFA_VALUEPICTURE_Raw, wsValue);
+            SetValue(XFA_ValuePicture::kRaw, wsValue);
+            pChild->SetValue(XFA_ValuePicture::kRaw, wsValue);
             done = true;
           }
         }
@@ -3070,7 +3107,7 @@
                     ->JSObject()
                     ->GetContent(false);
           }
-          pChild->SetValue(XFA_VALUEPICTURE_Raw, itemText);
+          pChild->SetValue(XFA_ValuePicture::kRaw, itemText);
         }
         pNextChild = pChild->GetNextContainerSibling();
       }
@@ -3078,13 +3115,13 @@
     }
     case XFA_FFWidgetType::kChoiceList:
       ClearAllSelections();
-      FALLTHROUGH;
+      [[fallthrough]];
     default: {
       CXFA_Value* defValue = GetDefaultValueIfExists();
       if (defValue)
         wsValue = defValue->GetChildValueContent();
 
-      SetValue(XFA_VALUEPICTURE_Raw, wsValue);
+      SetValue(XFA_ValuePicture::kRaw, wsValue);
       break;
     }
   }
@@ -3108,11 +3145,10 @@
       image->SetTransferEncoding(XFA_AttributeValue::Base64);
     return;
   }
-  pBind->JSObject()->SetCData(XFA_Attribute::ContentType, wsContentType, false,
-                              false);
+  pBind->JSObject()->SetCData(XFA_Attribute::ContentType, wsContentType);
   CXFA_Node* pHrefNode = pBind->GetFirstChild();
   if (pHrefNode) {
-    pHrefNode->JSObject()->SetCData(XFA_Attribute::Value, wsHref, false, false);
+    pHrefNode->JSObject()->SetCData(XFA_Attribute::Value, wsHref);
     return;
   }
   CFX_XMLElement* pElement = ToXMLElement(pBind->GetXMLMappingNode());
@@ -3132,7 +3168,7 @@
   const bool bVert = iCapPlacement == XFA_AttributeValue::Top ||
                      iCapPlacement == XFA_AttributeValue::Bottom;
   CXFA_TextLayout* pCapTextLayout =
-      m_pLayoutData->AsFieldLayoutData()->m_pCapTextLayout.get();
+      m_pLayoutData->AsFieldLayoutData()->m_pCapTextLayout;
   if (pCapTextLayout) {
     if (!bVert && GetFFWidgetType() != XFA_FFWidgetType::kButton)
       pszCap->width = fCapReserve;
@@ -3220,37 +3256,37 @@
   if (para)
     pSize->width += para->GetMarginLeft() + para->GetTextIndent();
 
-  Optional<float> width = TryWidth();
-  if (width) {
-    pSize->width = *width;
+  absl::optional<float> width = TryWidth();
+  if (width.has_value()) {
+    pSize->width = width.value();
   } else {
-    Optional<float> min = TryMinWidth();
-    if (min)
-      pSize->width = std::max(pSize->width, *min);
+    absl::optional<float> min = TryMinWidth();
+    if (min.has_value())
+      pSize->width = std::max(pSize->width, min.value());
 
-    Optional<float> max = TryMaxWidth();
-    if (max && *max > 0)
-      pSize->width = std::min(pSize->width, *max);
+    absl::optional<float> max = TryMaxWidth();
+    if (max.has_value() && max.value() > 0)
+      pSize->width = std::min(pSize->width, max.value());
   }
 
-  Optional<float> height = TryHeight();
-  if (height) {
-    pSize->height = *height;
+  absl::optional<float> height = TryHeight();
+  if (height.has_value()) {
+    pSize->height = height.value();
   } else {
-    Optional<float> min = TryMinHeight();
-    if (min)
-      pSize->height = std::max(pSize->height, *min);
+    absl::optional<float> min = TryMinHeight();
+    if (min.has_value())
+      pSize->height = std::max(pSize->height, min.value());
 
-    Optional<float> max = TryMaxHeight();
-    if (max && *max > 0)
-      pSize->height = std::min(pSize->height, *max);
+    absl::optional<float> max = TryMaxHeight();
+    if (max.has_value() && max.value() > 0)
+      pSize->height = std::min(pSize->height, max.value());
   }
   return true;
 }
 
 void CXFA_Node::CalculateTextContentSize(CXFA_FFDoc* doc, CFX_SizeF* pSize) {
   float fFontSize = GetFontSize();
-  WideString wsText = GetValue(XFA_VALUEPICTURE_Display);
+  WideString wsText = GetValue(XFA_ValuePicture::kDisplay);
   if (wsText.IsEmpty()) {
     pSize->height += fFontSize;
     return;
@@ -3261,9 +3297,9 @@
 
   CXFA_FieldLayoutData* layoutData = m_pLayoutData->AsFieldLayoutData();
   if (!layoutData->m_pTextOut) {
-    layoutData->m_pTextOut = pdfium::MakeUnique<CFDE_TextOut>();
+    layoutData->m_pTextOut = std::make_unique<CFDE_TextOut>();
     CFDE_TextOut* pTextOut = layoutData->m_pTextOut.get();
-    pTextOut->SetFont(GetFDEFont(doc));
+    pTextOut->SetFont(GetFGASFont(doc));
     pTextOut->SetFontSize(fFontSize);
     pTextOut->SetLineBreakTolerance(fFontSize * 0.2f);
     pTextOut->SetLineSpace(GetLineHeight());
@@ -3350,17 +3386,17 @@
                     XFA_UnitPx2Pt(img_height, dpi.height));
 
   CFX_RectF rtFit;
-  Optional<float> width = TryWidth();
-  if (width) {
-    rtFit.width = *width;
+  absl::optional<float> width = TryWidth();
+  if (width.has_value()) {
+    rtFit.width = width.value();
     GetWidthWithoutMargin(rtFit.width);
   } else {
     rtFit.width = rtImage.width;
   }
 
-  Optional<float> height = TryHeight();
-  if (height) {
-    rtFit.height = *height;
+  absl::optional<float> height = TryHeight();
+  if (height.has_value()) {
+    rtFit.height = height.value();
     GetHeightWithoutMargin(rtFit.height);
   } else {
     rtFit.height = rtImage.height;
@@ -3370,53 +3406,52 @@
 }
 
 bool CXFA_Node::CalculateImageAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize) {
-  if (!GetImageImage())
-    LoadImageImage(doc);
+  if (!GetLayoutImage())
+    LoadLayoutImage(doc);
 
   pSize->clear();
-  RetainPtr<CFX_DIBitmap> pBitmap = GetImageImage();
+  RetainPtr<CFX_DIBitmap> pBitmap = GetLayoutImage();
   if (!pBitmap)
     return CalculateWidgetAutoSize(pSize);
 
   *pSize = CalculateImageSize(pBitmap->GetWidth(), pBitmap->GetHeight(),
-                              GetImageDpi());
+                              GetLayoutImageDpi());
   return CalculateWidgetAutoSize(pSize);
 }
 
 bool CXFA_Node::CalculateImageEditAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize) {
-  if (!GetImageEditImage())
-    LoadImageEditImage(doc);
+  if (!GetEditImage())
+    LoadEditImage(doc);
 
   pSize->clear();
-  RetainPtr<CFX_DIBitmap> pBitmap = GetImageEditImage();
+  RetainPtr<CFX_DIBitmap> pBitmap = GetEditImage();
   if (!pBitmap)
     return CalculateFieldAutoSize(doc, pSize);
 
   *pSize = CalculateImageSize(pBitmap->GetWidth(), pBitmap->GetHeight(),
-                              GetImageEditDpi());
+                              GetEditImageDpi());
   return CalculateFieldAutoSize(doc, pSize);
 }
 
-bool CXFA_Node::LoadImageImage(CXFA_FFDoc* doc) {
-  InitLayoutData();
+bool CXFA_Node::LoadLayoutImage(CXFA_FFDoc* doc) {
+  InitLayoutData(doc);
   return m_pLayoutData->AsImageLayoutData()->LoadImageData(doc, this);
 }
 
-bool CXFA_Node::LoadImageEditImage(CXFA_FFDoc* doc) {
-  InitLayoutData();
+bool CXFA_Node::LoadEditImage(CXFA_FFDoc* doc) {
+  InitLayoutData(doc);
   return m_pLayoutData->AsFieldLayoutData()->AsImageEditData()->LoadImageData(
       doc, this);
 }
 
-CFX_Size CXFA_Node::GetImageDpi() const {
-  CXFA_ImageLayoutData* pData = m_pLayoutData->AsImageLayoutData();
-  return CFX_Size(pData->m_iImageXDpi, pData->m_iImageYDpi);
+CFX_Size CXFA_Node::GetLayoutImageDpi() const {
+  return m_pLayoutData->AsImageLayoutData()->GetDpi();
 }
 
-CFX_Size CXFA_Node::GetImageEditDpi() const {
+CFX_Size CXFA_Node::GetEditImageDpi() const {
   CXFA_ImageEditData* pData =
       m_pLayoutData->AsFieldLayoutData()->AsImageEditData();
-  return CFX_Size(pData->m_iImageXDpi, pData->m_iImageYDpi);
+  return pData->GetDpi();
 }
 
 float CXFA_Node::CalculateWidgetAutoWidth(float fWidthCalc) {
@@ -3424,13 +3459,13 @@
   if (margin)
     fWidthCalc += margin->GetLeftInset() + margin->GetRightInset();
 
-  Optional<float> min = TryMinWidth();
-  if (min)
-    fWidthCalc = std::max(fWidthCalc, *min);
+  absl::optional<float> min = TryMinWidth();
+  if (min.has_value())
+    fWidthCalc = std::max(fWidthCalc, min.value());
 
-  Optional<float> max = TryMaxWidth();
-  if (max && *max > 0)
-    fWidthCalc = std::min(fWidthCalc, *max);
+  absl::optional<float> max = TryMaxWidth();
+  if (max.has_value() && max.value() > 0)
+    fWidthCalc = std::min(fWidthCalc, max.value());
 
   return fWidthCalc;
 }
@@ -3447,13 +3482,13 @@
   if (margin)
     fHeightCalc += margin->GetTopInset() + margin->GetBottomInset();
 
-  Optional<float> min = TryMinHeight();
-  if (min)
-    fHeightCalc = std::max(fHeightCalc, *min);
+  absl::optional<float> min = TryMinHeight();
+  if (min.has_value())
+    fHeightCalc = std::max(fHeightCalc, min.value());
 
-  Optional<float> max = TryMaxHeight();
-  if (max && *max > 0)
-    fHeightCalc = std::min(fHeightCalc, *max);
+  absl::optional<float> max = TryMaxHeight();
+  if (max.has_value() && max.value() > 0)
+    fHeightCalc = std::min(fHeightCalc, max.value());
 
   return fHeightCalc;
 }
@@ -3468,42 +3503,40 @@
 void CXFA_Node::StartWidgetLayout(CXFA_FFDoc* doc,
                                   float* pCalcWidth,
                                   float* pCalcHeight) {
-  InitLayoutData();
+  InitLayoutData(doc);
 
   if (GetFFWidgetType() == XFA_FFWidgetType::kText) {
-    m_pLayoutData->m_fWidgetHeight = TryHeight().value_or(-1);
+    m_pLayoutData->SetWidgetHeight(TryHeight().value_or(-1));
     StartTextLayout(doc, pCalcWidth, pCalcHeight);
     return;
   }
   if (*pCalcWidth > 0 && *pCalcHeight > 0)
     return;
 
-  m_pLayoutData->m_fWidgetHeight = -1;
+  m_pLayoutData->SetWidgetHeight(-1.0f);
   float fWidth = 0;
   if (*pCalcWidth > 0 && *pCalcHeight < 0) {
-    Optional<float> height = TryHeight();
-    if (height) {
-      *pCalcHeight = *height;
+    absl::optional<float> height = TryHeight();
+    if (height.has_value()) {
+      *pCalcHeight = height.value();
     } else {
       CFX_SizeF size = CalculateAccWidthAndHeight(doc, *pCalcWidth);
       *pCalcWidth = size.width;
       *pCalcHeight = size.height;
     }
-
-    m_pLayoutData->m_fWidgetHeight = *pCalcHeight;
+    m_pLayoutData->SetWidgetHeight(*pCalcHeight);
     return;
   }
   if (*pCalcWidth < 0 && *pCalcHeight < 0) {
-    Optional<float> height;
-    Optional<float> width = TryWidth();
-    if (width) {
-      fWidth = *width;
-
+    absl::optional<float> height;
+    absl::optional<float> width = TryWidth();
+    if (width.has_value()) {
+      fWidth = width.value();
       height = TryHeight();
-      if (height)
-        *pCalcHeight = *height;
+      if (height.has_value())
+        *pCalcHeight = height.value();
     }
-    if (!width || !height) {
+    if (!width.has_value() || !height.has_value()) {
       CFX_SizeF size = CalculateAccWidthAndHeight(doc, fWidth);
       *pCalcWidth = size.width;
       *pCalcHeight = size.height;
@@ -3511,11 +3544,11 @@
       *pCalcWidth = fWidth;
     }
   }
-  m_pLayoutData->m_fWidgetHeight = *pCalcHeight;
+  m_pLayoutData->SetWidgetHeight(*pCalcHeight);
 }
 
 CFX_SizeF CXFA_Node::CalculateAccWidthAndHeight(CXFA_FFDoc* doc, float fWidth) {
-  CFX_SizeF sz(fWidth, m_pLayoutData->m_fWidgetHeight);
+  CFX_SizeF sz(fWidth, m_pLayoutData->GetWidgetHeight());
   switch (GetFFWidgetType()) {
     case XFA_FFWidgetType::kBarcode:
     case XFA_FFWidgetType::kChoiceList:
@@ -3551,16 +3584,18 @@
     case XFA_FFWidgetType::kNone:
       break;
   }
-
-  m_pLayoutData->m_fWidgetHeight = sz.height;
+  m_pLayoutData->SetWidgetHeight(sz.height);
   return sz;
 }
 
-Optional<float> CXFA_Node::FindSplitPos(CXFA_FFDocView* pDocView,
-                                        size_t szBlockIndex,
-                                        float fCalcHeight) {
+absl::optional<float> CXFA_Node::FindSplitPos(CXFA_FFDocView* pDocView,
+                                              size_t szBlockIndex,
+                                              float fCalcHeight) {
+  if (!HasCreatedUIWidget())
+    return absl::nullopt;
+
   if (GetFFWidgetType() == XFA_FFWidgetType::kSubform)
-    return pdfium::nullopt;
+    return absl::nullopt;
 
   switch (GetFFWidgetType()) {
     case XFA_FFWidgetType::kText:
@@ -3594,12 +3629,13 @@
     CXFA_TextLayout* pTextLayout =
         m_pLayoutData->AsTextLayoutData()->GetTextLayout();
     fCalcHeight = pTextLayout->DoSplitLayout(
-        szBlockIndex, fCalcHeight, m_pLayoutData->m_fWidgetHeight - fTopInset);
+        szBlockIndex, fCalcHeight,
+        m_pLayoutData->GetWidgetHeight() - fTopInset);
     if (fCalcHeight != 0) {
       if (szBlockIndex == 0)
         fCalcHeight += fTopInset;
       if (fabs(fHeight - fCalcHeight) < kXFAWidgetPrecision)
-        return pdfium::nullopt;
+        return absl::nullopt;
     }
     return fCalcHeight;
   }
@@ -3617,7 +3653,7 @@
       return 0.0f;
     }
     if (iCapPlacement == XFA_AttributeValue::Bottom &&
-        m_pLayoutData->m_fWidgetHeight - fCapReserve - fBottomInset) {
+        m_pLayoutData->GetWidgetHeight() - fCapReserve - fBottomInset) {
       return 0.0f;
     }
     if (iCapPlacement != XFA_AttributeValue::Top)
@@ -3625,8 +3661,8 @@
   }
   CXFA_FieldLayoutData* pFieldData = m_pLayoutData->AsFieldLayoutData();
   int32_t iLinesCount = 0;
-  float fHeight = m_pLayoutData->m_fWidgetHeight;
-  if (GetValue(XFA_VALUEPICTURE_Display).IsEmpty()) {
+  float fHeight = m_pLayoutData->GetWidgetHeight();
+  if (GetValue(XFA_ValuePicture::kDisplay).IsEmpty()) {
     iLinesCount = 1;
   } else {
     if (!pFieldData->m_pTextOut) {
@@ -3640,14 +3676,14 @@
   std::vector<float>* pFieldArray = &pFieldData->m_FieldSplitArray;
   size_t szFieldSplitCount = pFieldArray->size();
   if (szFieldSplitCount < szBlockIndex * 3)
-    return pdfium::nullopt;
+    return absl::nullopt;
 
   for (size_t i = 0; i < szBlockIndex * 3; i += 3) {
-    iLinesCount -= (int32_t)(*pFieldArray)[i + 1];
+    iLinesCount -= static_cast<int32_t>((*pFieldArray)[i + 1]);
     fHeight -= (*pFieldArray)[i + 2];
   }
   if (iLinesCount == 0)
-    return pdfium::nullopt;
+    return absl::nullopt;
 
   float fLineHeight = GetLineHeight();
   float fFontSize = GetFontSize();
@@ -3673,8 +3709,7 @@
           fStartOffset += (fHeight - fTextHeight + fSpaceAbove);
           break;
         default:
-          NOTREACHED();
-          break;
+          NOTREACHED_NORETURN();
       }
     }
     if (fStartOffset < 0.1f)
@@ -3724,7 +3759,7 @@
       pFieldArray->push_back(0);
       pFieldArray->push_back(fCalcHeight);
     }
-    return pdfium::nullopt;
+    return absl::nullopt;
   }
 
   if (fCalcHeight - fStartOffset < fLineHeight) {
@@ -3741,8 +3776,8 @@
 
   float fTextNum =
       fCalcHeight + kXFAWidgetPrecision - fCapReserve - fStartOffset;
-  int32_t iLineNum =
-      (int32_t)((fTextNum + (fLineHeight - fFontSize)) / fLineHeight);
+  int32_t iLineNum = static_cast<int32_t>(
+      (fTextNum + (fLineHeight - fFontSize)) / fLineHeight);
   if (iLineNum >= iLinesCount) {
     if (fCalcHeight - fStartOffset - fTextHeight >= fFontSize) {
       if (szFieldSplitCount / 3 == (szBlockIndex + 1)) {
@@ -3752,14 +3787,14 @@
         pFieldArray->push_back(iLinesCount);
         pFieldArray->push_back(fCalcHeight);
       }
-      return pdfium::nullopt;
+      return absl::nullopt;
     }
     if (fHeight - fStartOffset - fTextHeight < fFontSize) {
       iLineNum -= 1;
       if (iLineNum == 0)
         return 0.0f;
     } else {
-      iLineNum = (int32_t)(fTextNum / fLineHeight);
+      iLineNum = static_cast<int32_t>(fTextNum / fLineHeight);
     }
   }
   if (iLineNum <= 0)
@@ -3774,41 +3809,47 @@
     pFieldArray->push_back(fSplitHeight);
   }
   if (fabs(fSplitHeight - fCalcHeight) < kXFAWidgetPrecision)
-    return pdfium::nullopt;
+    return absl::nullopt;
   return fSplitHeight;
 }
 
-void CXFA_Node::InitLayoutData() {
+void CXFA_Node::InitLayoutData(CXFA_FFDoc* doc) {
   if (m_pLayoutData)
     return;
 
   switch (GetFFWidgetType()) {
     case XFA_FFWidgetType::kText:
-      m_pLayoutData = pdfium::MakeUnique<CXFA_TextLayoutData>();
+      m_pLayoutData = cppgc::MakeGarbageCollected<CXFA_TextLayoutData>(
+          doc->GetHeap()->GetAllocationHandle());
       return;
     case XFA_FFWidgetType::kTextEdit:
-      m_pLayoutData = pdfium::MakeUnique<CXFA_TextEditData>();
+      m_pLayoutData = cppgc::MakeGarbageCollected<CXFA_TextEditData>(
+          doc->GetHeap()->GetAllocationHandle());
       return;
     case XFA_FFWidgetType::kImage:
-      m_pLayoutData = pdfium::MakeUnique<CXFA_ImageLayoutData>();
+      m_pLayoutData = cppgc::MakeGarbageCollected<CXFA_ImageLayoutData>(
+          doc->GetHeap()->GetAllocationHandle());
       return;
     case XFA_FFWidgetType::kImageEdit:
-      m_pLayoutData = pdfium::MakeUnique<CXFA_ImageEditData>();
+      m_pLayoutData = cppgc::MakeGarbageCollected<CXFA_ImageEditData>(
+          doc->GetHeap()->GetAllocationHandle());
       return;
     default:
       break;
   }
   if (GetElementType() == XFA_Element::Field) {
-    m_pLayoutData = pdfium::MakeUnique<CXFA_FieldLayoutData>();
+    m_pLayoutData = cppgc::MakeGarbageCollected<CXFA_FieldLayoutData>(
+        doc->GetHeap()->GetAllocationHandle());
     return;
   }
-  m_pLayoutData = pdfium::MakeUnique<CXFA_WidgetLayoutData>();
+  m_pLayoutData = cppgc::MakeGarbageCollected<CXFA_WidgetLayoutData>(
+      doc->GetHeap()->GetAllocationHandle());
 }
 
 void CXFA_Node::StartTextLayout(CXFA_FFDoc* doc,
                                 float* pCalcWidth,
                                 float* pCalcHeight) {
-  InitLayoutData();
+  InitLayoutData(doc);
 
   CXFA_TextLayoutData* pTextLayoutData = m_pLayoutData->AsTextLayoutData();
   pTextLayoutData->LoadText(doc, this);
@@ -3828,36 +3869,34 @@
     pTextLayout->StartLayout(fWidth);
   }
   if (*pCalcWidth < 0 && *pCalcHeight < 0) {
-    Optional<float> width = TryWidth();
-    if (width) {
-      pTextLayout->StartLayout(GetWidthWithoutMargin(*width));
-      *pCalcWidth = *width;
+    absl::optional<float> width = TryWidth();
+    if (width.has_value()) {
+      pTextLayout->StartLayout(GetWidthWithoutMargin(width.value()));
+      *pCalcWidth = width.value();
     } else {
       float fMaxWidth = CalculateWidgetAutoWidth(pTextLayout->StartLayout(-1));
       pTextLayout->StartLayout(GetWidthWithoutMargin(fMaxWidth));
       *pCalcWidth = fMaxWidth;
     }
   }
-  if (m_pLayoutData->m_fWidgetHeight < 0) {
-    m_pLayoutData->m_fWidgetHeight = pTextLayout->GetLayoutHeight();
-    m_pLayoutData->m_fWidgetHeight =
-        CalculateWidgetAutoHeight(m_pLayoutData->m_fWidgetHeight);
+  if (m_pLayoutData->GetWidgetHeight() < 0) {
+    m_pLayoutData->SetWidgetHeight(
+        CalculateWidgetAutoHeight(pTextLayout->GetLayoutHeight()));
   }
-  fTextHeight = m_pLayoutData->m_fWidgetHeight;
+  fTextHeight = m_pLayoutData->GetWidgetHeight();
   fTextHeight = GetHeightWithoutMargin(fTextHeight);
   pTextLayout->DoLayout(fTextHeight);
-  *pCalcHeight = m_pLayoutData->m_fWidgetHeight;
+  *pCalcHeight = m_pLayoutData->GetWidgetHeight();
 }
 
 bool CXFA_Node::LoadCaption(CXFA_FFDoc* doc) {
-  InitLayoutData();
+  InitLayoutData(doc);
   return m_pLayoutData->AsFieldLayoutData()->LoadCaption(doc, this);
 }
 
 CXFA_TextLayout* CXFA_Node::GetCaptionTextLayout() {
-  return m_pLayoutData
-             ? m_pLayoutData->AsFieldLayoutData()->m_pCapTextLayout.get()
-             : nullptr;
+  return m_pLayoutData ? m_pLayoutData->AsFieldLayoutData()->m_pCapTextLayout
+                       : nullptr;
 }
 
 CXFA_TextLayout* CXFA_Node::GetTextLayout() {
@@ -3865,32 +3904,32 @@
                        : nullptr;
 }
 
-RetainPtr<CFX_DIBitmap> CXFA_Node::GetImageImage() {
-  return m_pLayoutData ? m_pLayoutData->AsImageLayoutData()->m_pDIBitmap
+RetainPtr<CFX_DIBitmap> CXFA_Node::GetLayoutImage() {
+  return m_pLayoutData ? m_pLayoutData->AsImageLayoutData()->GetBitmap()
                        : nullptr;
 }
 
-RetainPtr<CFX_DIBitmap> CXFA_Node::GetImageEditImage() {
+RetainPtr<CFX_DIBitmap> CXFA_Node::GetEditImage() {
   return m_pLayoutData ? m_pLayoutData->AsFieldLayoutData()
                              ->AsImageEditData()
-                             ->m_pDIBitmap
+                             ->GetBitmap()
                        : nullptr;
 }
 
-void CXFA_Node::SetImageImage(const RetainPtr<CFX_DIBitmap>& newImage) {
+void CXFA_Node::SetLayoutImage(RetainPtr<CFX_DIBitmap> newImage) {
   CXFA_ImageLayoutData* pData = m_pLayoutData->AsImageLayoutData();
-  if (pData->m_pDIBitmap != newImage)
-    pData->m_pDIBitmap = newImage;
+  if (pData->GetBitmap() != newImage)
+    pData->SetBitmap(std::move(newImage));
 }
 
-void CXFA_Node::SetImageEditImage(const RetainPtr<CFX_DIBitmap>& newImage) {
+void CXFA_Node::SetEditImage(RetainPtr<CFX_DIBitmap> newImage) {
   CXFA_ImageEditData* pData =
       m_pLayoutData->AsFieldLayoutData()->AsImageEditData();
-  if (pData->m_pDIBitmap != newImage)
-    pData->m_pDIBitmap = newImage;
+  if (pData->GetBitmap() != newImage)
+    pData->SetBitmap(std::move(newImage));
 }
 
-RetainPtr<CFGAS_GEFont> CXFA_Node::GetFDEFont(CXFA_FFDoc* doc) {
+RetainPtr<CFGAS_GEFont> CXFA_Node::GetFGASFont(CXFA_FFDoc* doc) {
   WideString wsFontName = L"Courier";
   uint32_t dwFontStyle = 0;
   CXFA_Font* font = GetFontIfExists();
@@ -3902,12 +3941,11 @@
 
     wsFontName = font->GetTypeface();
   }
-  return doc->GetApp()->GetXFAFontMgr()->GetFont(doc, wsFontName.AsStringView(),
-                                                 dwFontStyle);
+  return doc->GetApp()->GetXFAFontMgr()->GetFont(doc, wsFontName, dwFontStyle);
 }
 
-bool CXFA_Node::HasButtonRollover() {
-  CXFA_Items* pItems = GetChild<CXFA_Items>(0, XFA_Element::Items, false);
+bool CXFA_Node::HasButtonRollover() const {
+  const auto* pItems = GetChild<CXFA_Items>(0, XFA_Element::Items, false);
   if (!pItems)
     return false;
 
@@ -3922,8 +3960,8 @@
   return false;
 }
 
-bool CXFA_Node::HasButtonDown() {
-  CXFA_Items* pItems = GetChild<CXFA_Items>(0, XFA_Element::Items, false);
+bool CXFA_Node::HasButtonDown() const {
+  const auto* pItems = GetChild<CXFA_Items>(0, XFA_Element::Items, false);
   if (!pItems)
     return false;
 
@@ -3950,29 +3988,30 @@
   return CXFA_Measurement(10, XFA_Unit::Pt).ToUnit(XFA_Unit::Pt);
 }
 
-XFA_CHECKSTATE CXFA_Node::GetCheckState() {
+XFA_CheckState CXFA_Node::GetCheckState() {
   WideString wsValue = GetRawValue();
   if (wsValue.IsEmpty())
-    return XFA_CHECKSTATE_Off;
+    return XFA_CheckState::kOff;
 
   auto* pItems = GetChild<CXFA_Items>(0, XFA_Element::Items, false);
   if (!pItems)
-    return XFA_CHECKSTATE_Off;
+    return XFA_CheckState::kOff;
 
   CXFA_Node* pText = pItems->GetFirstChild();
   int32_t i = 0;
   while (pText) {
-    Optional<WideString> wsContent = pText->JSObject()->TryContent(false, true);
-    if (wsContent && *wsContent == wsValue)
-      return static_cast<XFA_CHECKSTATE>(i);
+    absl::optional<WideString> wsContent =
+        pText->JSObject()->TryContent(false, true);
+    if (wsContent == wsValue)
+      return static_cast<XFA_CheckState>(i);
 
     i++;
     pText = pText->GetNextSibling();
   }
-  return XFA_CHECKSTATE_Off;
+  return XFA_CheckState::kOff;
 }
 
-void CXFA_Node::SetCheckState(XFA_CHECKSTATE eCheckState, bool bNotify) {
+void CXFA_Node::SetCheckState(XFA_CheckState eCheckState) {
   CXFA_Node* node = GetExclGroupIfExists();
   if (!node) {
     CXFA_Items* pItems = GetChild<CXFA_Items>(0, XFA_Element::Items, false);
@@ -3984,19 +4023,19 @@
     WideString wsContent;
     while (pText) {
       i++;
-      if (i == eCheckState) {
+      if (i == static_cast<int32_t>(eCheckState)) {
         wsContent = pText->JSObject()->GetContent(false);
         break;
       }
       pText = pText->GetNextSibling();
     }
-    SyncValue(wsContent, bNotify);
+    SyncValue(wsContent, true);
 
     return;
   }
 
   WideString wsValue;
-  if (eCheckState != XFA_CHECKSTATE_Off) {
+  if (eCheckState != XFA_CheckState::kOff) {
     if (CXFA_Items* pItems =
             GetChild<CXFA_Items>(0, XFA_Element::Items, false)) {
       CXFA_Node* pText = pItems->GetFirstChild();
@@ -4027,9 +4066,9 @@
       else
         wsChildValue.clear();
     }
-    pChild->SyncValue(wsChildValue, bNotify);
+    pChild->SyncValue(wsChildValue, true);
   }
-  node->SyncValue(wsValue, bNotify);
+  node->SyncValue(wsValue, true);
 }
 
 CXFA_Node* CXFA_Node::GetSelectedMember() {
@@ -4040,7 +4079,7 @@
 
   for (CXFA_Node* pNode = ToNode(GetFirstChild()); pNode;
        pNode = pNode->GetNextSibling()) {
-    if (pNode->GetCheckState() == XFA_CHECKSTATE_On) {
+    if (pNode->GetCheckState() == XFA_CheckState::kOn) {
       pSelectedMember = pNode;
       break;
     }
@@ -4048,12 +4087,12 @@
   return pSelectedMember;
 }
 
-CXFA_Node* CXFA_Node::SetSelectedMember(WideStringView wsName, bool bNotify) {
-  uint32_t nameHash = FX_HashCode_GetW(wsName, false);
+CXFA_Node* CXFA_Node::SetSelectedMember(WideStringView wsName) {
+  uint32_t nameHash = FX_HashCode_GetW(wsName);
   for (CXFA_Node* pNode = ToNode(GetFirstChild()); pNode;
        pNode = pNode->GetNextSibling()) {
     if (pNode->GetNameHash() == nameHash) {
-      pNode->SetCheckState(XFA_CHECKSTATE_On, bNotify);
+      pNode->SetCheckState(XFA_CheckState::kOn);
       return pNode;
     }
   }
@@ -4154,7 +4193,7 @@
          attr == XFA_AttributeValue::MultiSelect;
 }
 
-int32_t CXFA_Node::CountChoiceListItems(bool bSaveValue) {
+size_t CXFA_Node::CountChoiceListItems(bool bSaveValue) {
   std::vector<CXFA_Node*> pItems;
   int32_t iCount = 0;
   for (CXFA_Node* pNode = GetFirstChild(); pNode;
@@ -4181,8 +4220,8 @@
   return pItem->CountChildren(XFA_Element::Unknown, false);
 }
 
-Optional<WideString> CXFA_Node::GetChoiceListItem(int32_t nIndex,
-                                                  bool bSaveValue) {
+absl::optional<WideString> CXFA_Node::GetChoiceListItem(int32_t nIndex,
+                                                        bool bSaveValue) {
   std::vector<CXFA_Node*> pItemsArray;
   int32_t iCount = 0;
   for (CXFA_Node* pNode = GetFirstChild(); pNode;
@@ -4196,7 +4235,7 @@
       break;
   }
   if (iCount == 0)
-    return {};
+    return absl::nullopt;
 
   CXFA_Node* pItems = pItemsArray[0];
   if (iCount > 1) {
@@ -4208,13 +4247,14 @@
       pItems = pItemsArray[1];
   }
   if (!pItems)
-    return {};
+    return absl::nullopt;
 
   CXFA_Node* pItem =
       pItems->GetChild<CXFA_Node>(nIndex, XFA_Element::Unknown, false);
-  if (pItem)
-    return {pItem->JSObject()->GetContent(false)};
-  return {};
+  if (!pItem)
+    return absl::nullopt;
+
+  return pItem->JSObject()->GetContent(false);
 }
 
 std::vector<WideString> CXFA_Node::GetChoiceListItems(bool bSaveValue) {
@@ -4248,12 +4288,12 @@
 int32_t CXFA_Node::CountSelectedItems() {
   std::vector<WideString> wsValueArray = GetSelectedItemsValue();
   if (IsListBox() || !IsChoiceListAllowTextEntry())
-    return pdfium::CollectionSize<int32_t>(wsValueArray);
+    return fxcrt::CollectionSize<int32_t>(wsValueArray);
 
   int32_t iSelected = 0;
   std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
   for (const auto& value : wsValueArray) {
-    if (pdfium::ContainsValue(wsSaveTextArray, value))
+    if (pdfium::Contains(wsSaveTextArray, value))
       iSelected++;
   }
   return iSelected;
@@ -4261,13 +4301,15 @@
 
 int32_t CXFA_Node::GetSelectedItem(int32_t nIndex) {
   std::vector<WideString> wsValueArray = GetSelectedItemsValue();
-  if (!pdfium::IndexInBounds(wsValueArray, nIndex))
+  if (!fxcrt::IndexInBounds(wsValueArray, nIndex))
     return -1;
 
   std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
   auto it = std::find(wsSaveTextArray.begin(), wsSaveTextArray.end(),
                       wsValueArray[nIndex]);
-  return it != wsSaveTextArray.end() ? it - wsSaveTextArray.begin() : -1;
+  return it != wsSaveTextArray.end()
+             ? pdfium::base::checked_cast<int32_t>(it - wsSaveTextArray.begin())
+             : -1;
 }
 
 std::vector<int32_t> CXFA_Node::GetSelectedItems() {
@@ -4276,8 +4318,10 @@
   std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
   for (const auto& value : wsValueArray) {
     auto it = std::find(wsSaveTextArray.begin(), wsSaveTextArray.end(), value);
-    if (it != wsSaveTextArray.end())
-      iSelArray.push_back(it - wsSaveTextArray.begin());
+    if (it != wsSaveTextArray.end()) {
+      iSelArray.push_back(
+          pdfium::base::checked_cast<int32_t>(it - wsSaveTextArray.begin()));
+    }
   }
   return iSelArray;
 }
@@ -4294,27 +4338,26 @@
 
 bool CXFA_Node::GetItemState(int32_t nIndex) {
   std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
-  return pdfium::IndexInBounds(wsSaveTextArray, nIndex) &&
-         pdfium::ContainsValue(GetSelectedItemsValue(),
-                               wsSaveTextArray[nIndex]);
+  return fxcrt::IndexInBounds(wsSaveTextArray, nIndex) &&
+         pdfium::Contains(GetSelectedItemsValue(), wsSaveTextArray[nIndex]);
 }
 
 void CXFA_Node::SetItemState(int32_t nIndex,
                              bool bSelected,
                              bool bNotify,
-                             bool bScriptModify,
-                             bool bSyncData) {
+                             bool bScriptModify) {
   std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
-  if (!pdfium::IndexInBounds(wsSaveTextArray, nIndex))
+  if (!fxcrt::IndexInBounds(wsSaveTextArray, nIndex))
     return;
 
   int32_t iSel = -1;
   std::vector<WideString> wsValueArray = GetSelectedItemsValue();
   auto value_iter = std::find(wsValueArray.begin(), wsValueArray.end(),
                               wsSaveTextArray[nIndex]);
-  if (value_iter != wsValueArray.end())
-    iSel = value_iter - wsValueArray.begin();
-
+  if (value_iter != wsValueArray.end()) {
+    iSel =
+        pdfium::base::checked_cast<int32_t>(value_iter - wsValueArray.begin());
+  }
   if (IsChoiceListMultiSelect()) {
     if (bSelected) {
       if (iSel < 0) {
@@ -4323,8 +4366,7 @@
           wsValue += L"\n";
         }
         wsValue += wsSaveTextArray[nIndex];
-        JSObject()->SetContent(wsValue, wsValue, bNotify, bScriptModify,
-                               bSyncData);
+        JSObject()->SetContent(wsValue, wsValue, bNotify, bScriptModify, true);
       }
     } else if (iSel >= 0) {
       std::vector<int32_t> iSelArray = GetSelectedItems();
@@ -4332,18 +4374,18 @@
           std::find(iSelArray.begin(), iSelArray.end(), nIndex);
       if (selected_iter != iSelArray.end())
         iSelArray.erase(selected_iter);
-      SetSelectedItems(iSelArray, bNotify, bScriptModify, bSyncData);
+      SetSelectedItems(iSelArray, bNotify, bScriptModify, true);
     }
   } else {
     if (bSelected) {
       if (iSel < 0) {
         WideString wsSaveText = wsSaveTextArray[nIndex];
         JSObject()->SetContent(wsSaveText, GetFormatDataValue(wsSaveText),
-                               bNotify, bScriptModify, bSyncData);
+                               bNotify, bScriptModify, true);
       }
     } else if (iSel >= 0) {
       JSObject()->SetContent(WideString(), WideString(), bNotify, bScriptModify,
-                             bSyncData);
+                             true);
     }
   }
 }
@@ -4353,7 +4395,7 @@
                                  bool bScriptModify,
                                  bool bSyncData) {
   WideString wsValue;
-  int32_t iSize = pdfium::CollectionSize<int32_t>(iSelArray);
+  int32_t iSize = fxcrt::CollectionSize<int32_t>(iSelArray);
   if (iSize >= 1) {
     std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
     WideString wsItemValue;
@@ -4533,7 +4575,7 @@
       }
     } else {
       if (!bSetValue && pItems->JSObject()->GetBoolean(XFA_Attribute::Save)) {
-        SetItemState(nIndex, false, true, bScriptModify, true);
+        SetItemState(nIndex, false, true, bScriptModify);
         bSetValue = true;
       }
       int32_t i = 0;
@@ -4571,14 +4613,16 @@
   return false;
 }
 
-Optional<int32_t> CXFA_Node::GetNumberOfCells() {
+absl::optional<int32_t> CXFA_Node::GetNumberOfCells() {
   CXFA_Node* pUIChild = GetUIChildNode();
   if (!pUIChild)
-    return {};
-  if (CXFA_Comb* pNode =
-          pUIChild->GetChild<CXFA_Comb>(0, XFA_Element::Comb, false))
-    return {pNode->JSObject()->GetInteger(XFA_Attribute::NumberOfCells)};
-  return {};
+    return absl::nullopt;
+
+  CXFA_Comb* pNode = pUIChild->GetChild<CXFA_Comb>(0, XFA_Element::Comb, false);
+  if (!pNode)
+    return absl::nullopt;
+
+  return pNode->JSObject()->GetInteger(XFA_Attribute::NumberOfCells);
 }
 
 bool CXFA_Node::IsMultiLine() {
@@ -4586,8 +4630,9 @@
   return pUIChild && pUIChild->JSObject()->GetBoolean(XFA_Attribute::MultiLine);
 }
 
-std::pair<XFA_Element, int32_t> CXFA_Node::GetMaxChars() {
-  if (CXFA_Value* pNode = GetChild<CXFA_Value>(0, XFA_Element::Value, false)) {
+std::pair<XFA_Element, int32_t> CXFA_Node::GetMaxChars() const {
+  const auto* pNode = GetChild<CXFA_Value>(0, XFA_Element::Value, false);
+  if (pNode) {
     if (CXFA_Node* pChild = pNode->GetFirstChild()) {
       switch (pChild->GetElementType()) {
         case XFA_Element::Text:
@@ -4606,12 +4651,12 @@
   return {XFA_Element::Unknown, 0};
 }
 
-int32_t CXFA_Node::GetFracDigits() {
-  CXFA_Value* pNode = GetChild<CXFA_Value>(0, XFA_Element::Value, false);
+int32_t CXFA_Node::GetFracDigits() const {
+  const auto* pNode = GetChild<CXFA_Value>(0, XFA_Element::Value, false);
   if (!pNode)
     return -1;
 
-  CXFA_Decimal* pChild =
+  const auto* pChild =
       pNode->GetChild<CXFA_Decimal>(0, XFA_Element::Decimal, false);
   if (!pChild)
     return -1;
@@ -4621,12 +4666,12 @@
       .value_or(-1);
 }
 
-int32_t CXFA_Node::GetLeadDigits() {
-  CXFA_Value* pNode = GetChild<CXFA_Value>(0, XFA_Element::Value, false);
+int32_t CXFA_Node::GetLeadDigits() const {
+  const auto* pNode = GetChild<CXFA_Value>(0, XFA_Element::Value, false);
   if (!pNode)
     return -1;
 
-  CXFA_Decimal* pChild =
+  const auto* pChild =
       pNode->GetChild<CXFA_Decimal>(0, XFA_Element::Decimal, false);
   if (!pChild)
     return -1;
@@ -4636,7 +4681,7 @@
       .value_or(-1);
 }
 
-bool CXFA_Node::SetValue(XFA_VALUEPICTURE eValueType,
+bool CXFA_Node::SetValue(XFA_ValuePicture eValueType,
                          const WideString& wsValue) {
   if (wsValue.IsEmpty()) {
     SyncValue(wsValue, true);
@@ -4657,7 +4702,7 @@
   XFA_Element eType = pNode->GetElementType();
   if (!wsPicture.IsEmpty()) {
     CXFA_LocaleMgr* pLocaleMgr = GetDocument()->GetLocaleMgr();
-    LocaleIface* pLocale = GetLocale();
+    GCedLocaleIface* pLocale = GetLocale();
     CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(this);
     bValidate =
         widgetValue.ValidateValue(wsValue, wsPicture, pLocale, &wsPicture);
@@ -4682,21 +4727,22 @@
   return bValidate;
 }
 
-WideString CXFA_Node::GetPictureContent(XFA_VALUEPICTURE ePicture) {
-  if (ePicture == XFA_VALUEPICTURE_Raw)
+WideString CXFA_Node::GetPictureContent(XFA_ValuePicture ePicture) {
+  if (ePicture == XFA_ValuePicture::kRaw)
     return WideString();
 
   CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(this);
   switch (ePicture) {
-    case XFA_VALUEPICTURE_Display: {
-      if (CXFA_Format* pFormat =
-              GetChild<CXFA_Format>(0, XFA_Element::Format, false)) {
-        if (CXFA_Picture* pPicture = pFormat->GetChild<CXFA_Picture>(
-                0, XFA_Element::Picture, false)) {
-          Optional<WideString> picture =
+    case XFA_ValuePicture::kDisplay: {
+      auto* pFormat = GetChild<CXFA_Format>(0, XFA_Element::Format, false);
+      if (pFormat) {
+        auto* pPicture =
+            pFormat->GetChild<CXFA_Picture>(0, XFA_Element::Picture, false);
+        if (pPicture) {
+          absl::optional<WideString> picture =
               pPicture->JSObject()->TryContent(false, true);
-          if (picture)
-            return *picture;
+          if (picture.has_value())
+            return picture.value();
         }
       }
 
@@ -4704,31 +4750,34 @@
       if (!pLocale)
         return WideString();
 
-      uint32_t dwType = widgetValue.GetType();
-      switch (dwType) {
-        case XFA_VT_DATE:
-          return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium);
-        case XFA_VT_TIME:
-          return pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium);
-        case XFA_VT_DATETIME:
-          return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium) +
+      switch (widgetValue.GetType()) {
+        case CXFA_LocaleValue::ValueType::kDate:
+          return pLocale->GetDatePattern(
+              LocaleIface::DateTimeSubcategory::kMedium);
+        case CXFA_LocaleValue::ValueType::kTime:
+          return pLocale->GetTimePattern(
+              LocaleIface::DateTimeSubcategory::kMedium);
+        case CXFA_LocaleValue::ValueType::kDateTime:
+          return pLocale->GetDatePattern(
+                     LocaleIface::DateTimeSubcategory::kMedium) +
                  L"T" +
-                 pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium);
-        case XFA_VT_DECIMAL:
-        case XFA_VT_FLOAT:
+                 pLocale->GetTimePattern(
+                     LocaleIface::DateTimeSubcategory::kMedium);
+        case CXFA_LocaleValue::ValueType::kDecimal:
+        case CXFA_LocaleValue::ValueType::kFloat:
         default:
           return WideString();
       }
     }
-    case XFA_VALUEPICTURE_Edit: {
+    case XFA_ValuePicture::kEdit: {
       CXFA_Ui* pUI = GetChild<CXFA_Ui>(0, XFA_Element::Ui, false);
       if (pUI) {
         if (CXFA_Picture* pPicture =
                 pUI->GetChild<CXFA_Picture>(0, XFA_Element::Picture, false)) {
-          Optional<WideString> picture =
+          absl::optional<WideString> picture =
               pPicture->JSObject()->TryContent(false, true);
-          if (picture)
-            return *picture;
+          if (picture.has_value())
+            return picture.value();
         }
       }
 
@@ -4736,21 +4785,24 @@
       if (!pLocale)
         return WideString();
 
-      uint32_t dwType = widgetValue.GetType();
-      switch (dwType) {
-        case XFA_VT_DATE:
-          return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Short);
-        case XFA_VT_TIME:
-          return pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Short);
-        case XFA_VT_DATETIME:
-          return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Short) +
+      switch (widgetValue.GetType()) {
+        case CXFA_LocaleValue::ValueType::kDate:
+          return pLocale->GetDatePattern(
+              LocaleIface::DateTimeSubcategory::kShort);
+        case CXFA_LocaleValue::ValueType::kTime:
+          return pLocale->GetTimePattern(
+              LocaleIface::DateTimeSubcategory::kShort);
+        case CXFA_LocaleValue::ValueType::kDateTime:
+          return pLocale->GetDatePattern(
+                     LocaleIface::DateTimeSubcategory::kShort) +
                  L"T" +
-                 pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Short);
+                 pLocale->GetTimePattern(
+                     LocaleIface::DateTimeSubcategory::kShort);
         default:
           return WideString();
       }
     }
-    case XFA_VALUEPICTURE_DataBind: {
+    case XFA_ValuePicture::kDataBind: {
       CXFA_Bind* bind = GetBindIfExists();
       if (bind)
         return bind->GetPicture();
@@ -4762,10 +4814,10 @@
   return WideString();
 }
 
-WideString CXFA_Node::GetValue(XFA_VALUEPICTURE eValueType) {
+WideString CXFA_Node::GetValue(XFA_ValuePicture eValueType) {
   WideString wsValue = JSObject()->GetContent(false);
 
-  if (eValueType == XFA_VALUEPICTURE_Display)
+  if (eValueType == XFA_ValuePicture::kDisplay)
     wsValue = GetItemLabel(wsValue.AsStringView());
 
   WideString wsPicture = GetPictureContent(eValueType);
@@ -4775,7 +4827,7 @@
 
   switch (pNode->GetElementType()) {
     case XFA_Element::ChoiceList: {
-      if (eValueType == XFA_VALUEPICTURE_Display) {
+      if (eValueType == XFA_ValuePicture::kDisplay) {
         int32_t iSelItemIndex = GetSelectedItem(0);
         if (iSelItemIndex >= 0) {
           wsValue =
@@ -4786,9 +4838,9 @@
       break;
     }
     case XFA_Element::NumericEdit:
-      if (eValueType != XFA_VALUEPICTURE_Raw && wsPicture.IsEmpty()) {
+      if (eValueType != XFA_ValuePicture::kRaw && wsPicture.IsEmpty()) {
         LocaleIface* pLocale = GetLocale();
-        if (eValueType == XFA_VALUEPICTURE_Display && pLocale)
+        if (eValueType == XFA_ValuePicture::kDisplay && pLocale)
           wsValue = FormatNumStr(NormalizeNumStr(wsValue), pLocale);
       }
       break;
@@ -4798,23 +4850,26 @@
   if (wsPicture.IsEmpty())
     return wsValue;
 
-  if (LocaleIface* pLocale = GetLocale()) {
+  GCedLocaleIface* pLocale = GetLocale();
+  if (pLocale) {
     CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(this);
     CXFA_LocaleMgr* pLocaleMgr = GetDocument()->GetLocaleMgr();
     switch (widgetValue.GetType()) {
-      case XFA_VT_DATE: {
+      case CXFA_LocaleValue::ValueType::kDate: {
         WideString wsDate, wsTime;
         if (SplitDateTime(wsValue, wsDate, wsTime)) {
-          CXFA_LocaleValue date(XFA_VT_DATE, wsDate, pLocaleMgr);
+          CXFA_LocaleValue date(CXFA_LocaleValue::ValueType::kDate, wsDate,
+                                pLocaleMgr);
           if (date.FormatPatterns(wsValue, wsPicture, pLocale, eValueType))
             return wsValue;
         }
         break;
       }
-      case XFA_VT_TIME: {
+      case CXFA_LocaleValue::ValueType::kTime: {
         WideString wsDate, wsTime;
         if (SplitDateTime(wsValue, wsDate, wsTime)) {
-          CXFA_LocaleValue time(XFA_VT_TIME, wsTime, pLocaleMgr);
+          CXFA_LocaleValue time(CXFA_LocaleValue::ValueType::kTime, wsTime,
+                                pLocaleMgr);
           if (time.FormatPatterns(wsValue, wsPicture, pLocale, eValueType))
             return wsValue;
         }
@@ -4832,12 +4887,12 @@
   if (wsValue.IsEmpty())
     return WideString();
 
-  WideString wsPicture = GetPictureContent(XFA_VALUEPICTURE_DataBind);
+  WideString wsPicture = GetPictureContent(XFA_ValuePicture::kDataBind);
   if (wsPicture.IsEmpty())
     return wsValue;
 
   CXFA_LocaleMgr* pLocaleMgr = GetDocument()->GetLocaleMgr();
-  LocaleIface* pLocale = GetLocale();
+  GCedLocaleIface* pLocale = GetLocale();
   CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(this);
   if (widgetValue.ValidateValue(wsValue, wsPicture, pLocale, &wsPicture)) {
     widgetValue = CXFA_LocaleValue(widgetValue.GetType(), wsValue, wsPicture,
@@ -4851,12 +4906,13 @@
   if (wsValue.IsEmpty())
     return WideString();
 
-  WideString wsPicture = GetPictureContent(XFA_VALUEPICTURE_DataBind);
+  WideString wsPicture = GetPictureContent(XFA_ValuePicture::kDataBind);
   if (wsPicture.IsEmpty())
     return wsValue;
 
   WideString wsFormattedValue = wsValue;
-  if (LocaleIface* pLocale = GetLocale()) {
+  GCedLocaleIface* pLocale = GetLocale();
+  if (pLocale) {
     CXFA_Value* pNodeValue = GetChild<CXFA_Value>(0, XFA_Element::Value, false);
     if (!pNodeValue)
       return wsValue;
@@ -4865,56 +4921,30 @@
     if (!pValueChild)
       return wsValue;
 
-    int32_t iVTType = XFA_VT_NULL;
-    switch (pValueChild->GetElementType()) {
-      case XFA_Element::Decimal:
-        iVTType = XFA_VT_DECIMAL;
-        break;
-      case XFA_Element::Float:
-        iVTType = XFA_VT_FLOAT;
-        break;
-      case XFA_Element::Date:
-        iVTType = XFA_VT_DATE;
-        break;
-      case XFA_Element::Time:
-        iVTType = XFA_VT_TIME;
-        break;
-      case XFA_Element::DateTime:
-        iVTType = XFA_VT_DATETIME;
-        break;
-      case XFA_Element::Boolean:
-        iVTType = XFA_VT_BOOLEAN;
-        break;
-      case XFA_Element::Integer:
-        iVTType = XFA_VT_INTEGER;
-        break;
-      case XFA_Element::Text:
-        iVTType = XFA_VT_TEXT;
-        break;
-      default:
-        iVTType = XFA_VT_NULL;
-        break;
-    }
+    CXFA_LocaleValue::ValueType iVTType =
+        XFA_GetLocaleValueType(pValueChild->GetElementType());
     CXFA_LocaleMgr* pLocaleMgr = GetDocument()->GetLocaleMgr();
     CXFA_LocaleValue widgetValue(iVTType, wsValue, pLocaleMgr);
     switch (widgetValue.GetType()) {
-      case XFA_VT_DATE: {
+      case CXFA_LocaleValue::ValueType::kDate: {
         WideString wsDate, wsTime;
         if (SplitDateTime(wsValue, wsDate, wsTime)) {
-          CXFA_LocaleValue date(XFA_VT_DATE, wsDate, pLocaleMgr);
+          CXFA_LocaleValue date(CXFA_LocaleValue::ValueType::kDate, wsDate,
+                                pLocaleMgr);
           if (date.FormatPatterns(wsFormattedValue, wsPicture, pLocale,
-                                  XFA_VALUEPICTURE_DataBind)) {
+                                  XFA_ValuePicture::kDataBind)) {
             return wsFormattedValue;
           }
         }
         break;
       }
-      case XFA_VT_TIME: {
+      case CXFA_LocaleValue::ValueType::kTime: {
         WideString wsDate, wsTime;
         if (SplitDateTime(wsValue, wsDate, wsTime)) {
-          CXFA_LocaleValue time(XFA_VT_TIME, wsTime, pLocaleMgr);
+          CXFA_LocaleValue time(CXFA_LocaleValue::ValueType::kTime, wsTime,
+                                pLocaleMgr);
           if (time.FormatPatterns(wsFormattedValue, wsPicture, pLocale,
-                                  XFA_VALUEPICTURE_DataBind)) {
+                                  XFA_ValuePicture::kDataBind)) {
             return wsFormattedValue;
           }
         }
@@ -4924,7 +4954,7 @@
         break;
     }
     widgetValue.FormatPatterns(wsFormattedValue, wsPicture, pLocale,
-                               XFA_VALUEPICTURE_DataBind);
+                               XFA_ValuePicture::kDataBind);
   }
   return wsFormattedValue;
 }
@@ -4957,31 +4987,32 @@
 WideString CXFA_Node::NumericLimit(const WideString& wsValue) {
   int32_t iLead = GetLeadDigits();
   int32_t iTread = GetFracDigits();
-
-  if ((iLead == -1) && (iTread == -1))
+  if (iLead == -1 && iTread == -1)
     return wsValue;
 
-  WideString wsRet;
-  int32_t iLead_ = 0, iTread_ = -1;
-  int32_t iCount = wsValue.GetLength();
+  int32_t iCount = pdfium::base::checked_cast<int32_t>(wsValue.GetLength());
   if (iCount == 0)
     return wsValue;
 
+  WideString wsRet;
   int32_t i = 0;
   if (wsValue[i] == L'-') {
     wsRet += L'-';
     i++;
   }
+
+  int32_t iLead2 = 0;
+  int32_t iTread2 = -1;
   for (; i < iCount; i++) {
     wchar_t wc = wsValue[i];
     if (FXSYS_IsDecimalDigit(wc)) {
       if (iLead >= 0) {
-        iLead_++;
-        if (iLead_ > iLead)
+        iLead2++;
+        if (iLead2 > iLead)
           return L"0";
-      } else if (iTread_ >= 0) {
-        iTread_++;
-        if (iTread_ > iTread) {
+      } else if (iTread2 >= 0) {
+        iTread2++;
+        if (iTread2 > iTread) {
           if (iTread != -1) {
             CFGAS_Decimal wsDeci = CFGAS_Decimal(wsValue.AsStringView());
             wsDeci.SetScale(iTread);
@@ -4991,7 +5022,7 @@
         }
       }
     } else if (wc == L'.') {
-      iTread_ = 0;
+      iTread2 = 0;
       iLead = -1;
     }
     wsRet += wc;
@@ -5027,7 +5058,7 @@
   CXFA_NodeIterator sIterator(this);
   for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
        pNode = sIterator.MoveToNext()) {
-    pNode->SetFlag(XFA_NodeFlag_UnusedNode);
+    pNode->SetFlag(XFA_NodeFlag::kUnusedNode);
   }
 }
 
@@ -5063,7 +5094,7 @@
       ToXMLText(GetXMLMappingNode())->SetText(value);
       break;
     default:
-      NOTREACHED();
+      NOTREACHED_NORETURN();
   }
 }
 
@@ -5081,926 +5112,1248 @@
 }
 
 CFX_XMLDocument* CXFA_Node::GetXMLDocument() const {
-  return GetDocument()->GetNotify()->GetHDOC()->GetXMLDocument();
+  return GetDocument()->GetNotify()->GetFFDoc()->GetXMLDocument();
 }
 
 // static
-std::unique_ptr<CXFA_Node> CXFA_Node::Create(CXFA_Document* doc,
-                                             XFA_Element element,
-                                             XFA_PacketType packet) {
-  std::unique_ptr<CXFA_Node> node;
+CXFA_Node* CXFA_Node::Create(CXFA_Document* doc,
+                             XFA_Element element,
+                             XFA_PacketType packet) {
+  CXFA_Node* node = nullptr;
   switch (element) {
     case XFA_Element::Ps:
-      node = pdfium::MakeUnique<CXFA_Ps>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Ps>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::To:
-      node = pdfium::MakeUnique<CXFA_To>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_To>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Ui:
-      node = pdfium::MakeUnique<CXFA_Ui>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Ui>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::RecordSet:
-      node = pdfium::MakeUnique<CXFA_RecordSet>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_RecordSet>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::SubsetBelow:
-      node = pdfium::MakeUnique<CXFA_SubsetBelow>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_SubsetBelow>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::SubformSet:
-      node = pdfium::MakeUnique<CXFA_SubformSet>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_SubformSet>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::AdobeExtensionLevel:
-      node = pdfium::MakeUnique<CXFA_AdobeExtensionLevel>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_AdobeExtensionLevel>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Typeface:
-      node = pdfium::MakeUnique<CXFA_Typeface>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Typeface>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Break:
-      node = pdfium::MakeUnique<CXFA_Break>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Break>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::FontInfo:
-      node = pdfium::MakeUnique<CXFA_FontInfo>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_FontInfo>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::NumberPattern:
-      node = pdfium::MakeUnique<CXFA_NumberPattern>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_NumberPattern>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::DynamicRender:
-      node = pdfium::MakeUnique<CXFA_DynamicRender>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_DynamicRender>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::PrintScaling:
-      node = pdfium::MakeUnique<CXFA_PrintScaling>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_PrintScaling>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::CheckButton:
-      node = pdfium::MakeUnique<CXFA_CheckButton>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_CheckButton>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::DatePatterns:
-      node = pdfium::MakeUnique<CXFA_DatePatterns>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_DatePatterns>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::SourceSet:
-      node = pdfium::MakeUnique<CXFA_SourceSet>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_SourceSet>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Amd:
-      node = pdfium::MakeUnique<CXFA_Amd>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Amd>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Arc:
-      node = pdfium::MakeUnique<CXFA_Arc>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Arc>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Day:
-      node = pdfium::MakeUnique<CXFA_Day>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Day>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Era:
-      node = pdfium::MakeUnique<CXFA_Era>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Era>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Jog:
-      node = pdfium::MakeUnique<CXFA_Jog>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Jog>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Log:
-      node = pdfium::MakeUnique<CXFA_Log>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Log>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Map:
-      node = pdfium::MakeUnique<CXFA_Map>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Map>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Mdp:
-      node = pdfium::MakeUnique<CXFA_Mdp>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Mdp>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::BreakBefore:
-      node = pdfium::MakeUnique<CXFA_BreakBefore>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_BreakBefore>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Oid:
-      node = pdfium::MakeUnique<CXFA_Oid>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Oid>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Pcl:
-      node = pdfium::MakeUnique<CXFA_Pcl>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Pcl>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Pdf:
-      node = pdfium::MakeUnique<CXFA_Pdf>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Pdf>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Ref:
-      node = pdfium::MakeUnique<CXFA_Ref>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Ref>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Uri:
-      node = pdfium::MakeUnique<CXFA_Uri>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Uri>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Xdc:
-      node = pdfium::MakeUnique<CXFA_Xdc>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Xdc>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Xdp:
-      node = pdfium::MakeUnique<CXFA_Xdp>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Xdp>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Xfa:
-      node = pdfium::MakeUnique<CXFA_Xfa>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Xfa>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Xsl:
-      node = pdfium::MakeUnique<CXFA_Xsl>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Xsl>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Zpl:
-      node = pdfium::MakeUnique<CXFA_Zpl>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Zpl>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Cache:
-      node = pdfium::MakeUnique<CXFA_Cache>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Cache>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Margin:
-      node = pdfium::MakeUnique<CXFA_Margin>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Margin>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::KeyUsage:
-      node = pdfium::MakeUnique<CXFA_KeyUsage>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_KeyUsage>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Exclude:
-      node = pdfium::MakeUnique<CXFA_Exclude>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Exclude>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ChoiceList:
-      node = pdfium::MakeUnique<CXFA_ChoiceList>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ChoiceList>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Level:
-      node = pdfium::MakeUnique<CXFA_Level>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Level>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::LabelPrinter:
-      node = pdfium::MakeUnique<CXFA_LabelPrinter>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_LabelPrinter>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::CalendarSymbols:
-      node = pdfium::MakeUnique<CXFA_CalendarSymbols>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_CalendarSymbols>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Para:
-      node = pdfium::MakeUnique<CXFA_Para>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Para>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Part:
-      node = pdfium::MakeUnique<CXFA_Part>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Part>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Pdfa:
-      node = pdfium::MakeUnique<CXFA_Pdfa>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Pdfa>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Filter:
-      node = pdfium::MakeUnique<CXFA_Filter>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Filter>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Present:
-      node = pdfium::MakeUnique<CXFA_Present>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Present>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Pagination:
-      node = pdfium::MakeUnique<CXFA_Pagination>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Pagination>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Encoding:
-      node = pdfium::MakeUnique<CXFA_Encoding>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Encoding>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Event:
-      node = pdfium::MakeUnique<CXFA_Event>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Event>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Whitespace:
-      node = pdfium::MakeUnique<CXFA_Whitespace>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Whitespace>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::DefaultUi:
-      node = pdfium::MakeUnique<CXFA_DefaultUi>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_DefaultUi>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::DataModel:
-      node = pdfium::MakeUnique<CXFA_DataModel>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_DataModel>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Barcode:
-      node = pdfium::MakeUnique<CXFA_Barcode>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Barcode>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::TimePattern:
-      node = pdfium::MakeUnique<CXFA_TimePattern>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_TimePattern>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::BatchOutput:
-      node = pdfium::MakeUnique<CXFA_BatchOutput>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_BatchOutput>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Enforce:
-      node = pdfium::MakeUnique<CXFA_Enforce>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Enforce>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::CurrencySymbols:
-      node = pdfium::MakeUnique<CXFA_CurrencySymbols>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_CurrencySymbols>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::AddSilentPrint:
-      node = pdfium::MakeUnique<CXFA_AddSilentPrint>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_AddSilentPrint>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Rename:
-      node = pdfium::MakeUnique<CXFA_Rename>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Rename>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Operation:
-      node = pdfium::MakeUnique<CXFA_Operation>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Operation>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Typefaces:
-      node = pdfium::MakeUnique<CXFA_Typefaces>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Typefaces>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::SubjectDNs:
-      node = pdfium::MakeUnique<CXFA_SubjectDNs>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_SubjectDNs>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Issuers:
-      node = pdfium::MakeUnique<CXFA_Issuers>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Issuers>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::WsdlConnection:
-      node = pdfium::MakeUnique<CXFA_WsdlConnection>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_WsdlConnection>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Debug:
-      node = pdfium::MakeUnique<CXFA_Debug>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Debug>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Delta:
-      node = pdfium::MakeUnique<CXFA_Delta>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Delta>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::EraNames:
-      node = pdfium::MakeUnique<CXFA_EraNames>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_EraNames>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ModifyAnnots:
-      node = pdfium::MakeUnique<CXFA_ModifyAnnots>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ModifyAnnots>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::StartNode:
-      node = pdfium::MakeUnique<CXFA_StartNode>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_StartNode>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Button:
-      node = pdfium::MakeUnique<CXFA_Button>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Button>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Format:
-      node = pdfium::MakeUnique<CXFA_Format>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Format>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Border:
-      node = pdfium::MakeUnique<CXFA_Border>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Border>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Area:
-      node = pdfium::MakeUnique<CXFA_Area>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Area>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Hyphenation:
-      node = pdfium::MakeUnique<CXFA_Hyphenation>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Hyphenation>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Text:
-      node = pdfium::MakeUnique<CXFA_Text>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Text>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Time:
-      node = pdfium::MakeUnique<CXFA_Time>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Time>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Type:
-      node = pdfium::MakeUnique<CXFA_Type>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Type>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Overprint:
-      node = pdfium::MakeUnique<CXFA_Overprint>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Overprint>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Certificates:
-      node = pdfium::MakeUnique<CXFA_Certificates>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Certificates>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::EncryptionMethods:
-      node = pdfium::MakeUnique<CXFA_EncryptionMethods>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_EncryptionMethods>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::SetProperty:
-      node = pdfium::MakeUnique<CXFA_SetProperty>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_SetProperty>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::PrinterName:
-      node = pdfium::MakeUnique<CXFA_PrinterName>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_PrinterName>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::StartPage:
-      node = pdfium::MakeUnique<CXFA_StartPage>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_StartPage>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::PageOffset:
-      node = pdfium::MakeUnique<CXFA_PageOffset>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_PageOffset>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::DateTime:
-      node = pdfium::MakeUnique<CXFA_DateTime>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_DateTime>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Comb:
-      node = pdfium::MakeUnique<CXFA_Comb>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Comb>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Pattern:
-      node = pdfium::MakeUnique<CXFA_Pattern>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Pattern>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::IfEmpty:
-      node = pdfium::MakeUnique<CXFA_IfEmpty>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_IfEmpty>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::SuppressBanner:
-      node = pdfium::MakeUnique<CXFA_SuppressBanner>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_SuppressBanner>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::OutputBin:
-      node = pdfium::MakeUnique<CXFA_OutputBin>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_OutputBin>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Field:
-      node = pdfium::MakeUnique<CXFA_Field>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Field>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Agent:
-      node = pdfium::MakeUnique<CXFA_Agent>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Agent>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::OutputXSL:
-      node = pdfium::MakeUnique<CXFA_OutputXSL>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_OutputXSL>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::AdjustData:
-      node = pdfium::MakeUnique<CXFA_AdjustData>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_AdjustData>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::AutoSave:
-      node = pdfium::MakeUnique<CXFA_AutoSave>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_AutoSave>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ContentArea:
-      node = pdfium::MakeUnique<CXFA_ContentArea>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ContentArea>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::WsdlAddress:
-      node = pdfium::MakeUnique<CXFA_WsdlAddress>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_WsdlAddress>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Solid:
-      node = pdfium::MakeUnique<CXFA_Solid>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Solid>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::DateTimeSymbols:
-      node = pdfium::MakeUnique<CXFA_DateTimeSymbols>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_DateTimeSymbols>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::EncryptionLevel:
-      node = pdfium::MakeUnique<CXFA_EncryptionLevel>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_EncryptionLevel>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Edge:
-      node = pdfium::MakeUnique<CXFA_Edge>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Edge>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Stipple:
-      node = pdfium::MakeUnique<CXFA_Stipple>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Stipple>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Attributes:
-      node = pdfium::MakeUnique<CXFA_Attributes>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Attributes>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::VersionControl:
-      node = pdfium::MakeUnique<CXFA_VersionControl>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_VersionControl>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Meridiem:
-      node = pdfium::MakeUnique<CXFA_Meridiem>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Meridiem>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ExclGroup:
-      node = pdfium::MakeUnique<CXFA_ExclGroup>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ExclGroup>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ToolTip:
-      node = pdfium::MakeUnique<CXFA_ToolTip>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ToolTip>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Compress:
-      node = pdfium::MakeUnique<CXFA_Compress>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Compress>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Reason:
-      node = pdfium::MakeUnique<CXFA_Reason>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Reason>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Execute:
-      node = pdfium::MakeUnique<CXFA_Execute>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Execute>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ContentCopy:
-      node = pdfium::MakeUnique<CXFA_ContentCopy>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ContentCopy>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::DateTimeEdit:
-      node = pdfium::MakeUnique<CXFA_DateTimeEdit>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_DateTimeEdit>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Config:
-      node = pdfium::MakeUnique<CXFA_Config>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Config>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Image:
-      node = pdfium::MakeUnique<CXFA_Image>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Image>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::SharpxHTML:
-      node = pdfium::MakeUnique<CXFA_SharpxHTML>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_SharpxHTML>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::NumberOfCopies:
-      node = pdfium::MakeUnique<CXFA_NumberOfCopies>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_NumberOfCopies>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::BehaviorOverride:
-      node = pdfium::MakeUnique<CXFA_BehaviorOverride>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_BehaviorOverride>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::TimeStamp:
-      node = pdfium::MakeUnique<CXFA_TimeStamp>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_TimeStamp>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Month:
-      node = pdfium::MakeUnique<CXFA_Month>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Month>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ViewerPreferences:
-      node = pdfium::MakeUnique<CXFA_ViewerPreferences>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ViewerPreferences>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ScriptModel:
-      node = pdfium::MakeUnique<CXFA_ScriptModel>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ScriptModel>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Decimal:
-      node = pdfium::MakeUnique<CXFA_Decimal>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Decimal>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Subform:
-      node = pdfium::MakeUnique<CXFA_Subform>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Subform>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Select:
-      node = pdfium::MakeUnique<CXFA_Select>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Select>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Window:
-      node = pdfium::MakeUnique<CXFA_Window>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Window>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::LocaleSet:
-      node = pdfium::MakeUnique<CXFA_LocaleSet>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_LocaleSet>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Handler:
-      node = pdfium::MakeUnique<CXFA_Handler>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Handler>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Presence:
-      node = pdfium::MakeUnique<CXFA_Presence>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Presence>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Record:
-      node = pdfium::MakeUnique<CXFA_Record>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Record>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Embed:
-      node = pdfium::MakeUnique<CXFA_Embed>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Embed>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Version:
-      node = pdfium::MakeUnique<CXFA_Version>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Version>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Command:
-      node = pdfium::MakeUnique<CXFA_Command>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Command>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Copies:
-      node = pdfium::MakeUnique<CXFA_Copies>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Copies>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Staple:
-      node = pdfium::MakeUnique<CXFA_Staple>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Staple>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::SubmitFormat:
-      node = pdfium::MakeUnique<CXFA_SubmitFormat>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_SubmitFormat>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Boolean:
-      node = pdfium::MakeUnique<CXFA_Boolean>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Boolean>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Message:
-      node = pdfium::MakeUnique<CXFA_Message>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Message>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Output:
-      node = pdfium::MakeUnique<CXFA_Output>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Output>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::PsMap:
-      node = pdfium::MakeUnique<CXFA_PsMap>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_PsMap>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ExcludeNS:
-      node = pdfium::MakeUnique<CXFA_ExcludeNS>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ExcludeNS>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Assist:
-      node = pdfium::MakeUnique<CXFA_Assist>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Assist>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Picture:
-      node = pdfium::MakeUnique<CXFA_Picture>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Picture>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Traversal:
-      node = pdfium::MakeUnique<CXFA_Traversal>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Traversal>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::SilentPrint:
-      node = pdfium::MakeUnique<CXFA_SilentPrint>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_SilentPrint>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::WebClient:
-      node = pdfium::MakeUnique<CXFA_WebClient>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_WebClient>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Producer:
-      node = pdfium::MakeUnique<CXFA_Producer>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Producer>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Corner:
-      node = pdfium::MakeUnique<CXFA_Corner>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Corner>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::MsgId:
-      node = pdfium::MakeUnique<CXFA_MsgId>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_MsgId>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Color:
-      node = pdfium::MakeUnique<CXFA_Color>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Color>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Keep:
-      node = pdfium::MakeUnique<CXFA_Keep>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Keep>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Query:
-      node = pdfium::MakeUnique<CXFA_Query>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Query>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Insert:
-      node = pdfium::MakeUnique<CXFA_Insert>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Insert>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ImageEdit:
-      node = pdfium::MakeUnique<CXFA_ImageEdit>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ImageEdit>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Validate:
-      node = pdfium::MakeUnique<CXFA_Validate>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Validate>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::DigestMethods:
-      node = pdfium::MakeUnique<CXFA_DigestMethods>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_DigestMethods>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::NumberPatterns:
-      node = pdfium::MakeUnique<CXFA_NumberPatterns>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_NumberPatterns>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::PageSet:
-      node = pdfium::MakeUnique<CXFA_PageSet>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_PageSet>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Integer:
-      node = pdfium::MakeUnique<CXFA_Integer>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Integer>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::SoapAddress:
-      node = pdfium::MakeUnique<CXFA_SoapAddress>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_SoapAddress>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Equate:
-      node = pdfium::MakeUnique<CXFA_Equate>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Equate>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::FormFieldFilling:
-      node = pdfium::MakeUnique<CXFA_FormFieldFilling>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_FormFieldFilling>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::PageRange:
-      node = pdfium::MakeUnique<CXFA_PageRange>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_PageRange>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Update:
-      node = pdfium::MakeUnique<CXFA_Update>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Update>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ConnectString:
-      node = pdfium::MakeUnique<CXFA_ConnectString>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ConnectString>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Mode:
-      node = pdfium::MakeUnique<CXFA_Mode>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Mode>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Layout:
-      node = pdfium::MakeUnique<CXFA_Layout>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Layout>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Sharpxml:
-      node = pdfium::MakeUnique<CXFA_Sharpxml>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Sharpxml>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::XsdConnection:
-      node = pdfium::MakeUnique<CXFA_XsdConnection>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_XsdConnection>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Traverse:
-      node = pdfium::MakeUnique<CXFA_Traverse>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Traverse>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Encodings:
-      node = pdfium::MakeUnique<CXFA_Encodings>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Encodings>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Template:
-      node = pdfium::MakeUnique<CXFA_Template>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Template>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Acrobat:
-      node = pdfium::MakeUnique<CXFA_Acrobat>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Acrobat>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ValidationMessaging:
-      node = pdfium::MakeUnique<CXFA_ValidationMessaging>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ValidationMessaging>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Signing:
-      node = pdfium::MakeUnique<CXFA_Signing>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Signing>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Script:
-      node = pdfium::MakeUnique<CXFA_Script>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Script>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::AddViewerPreferences:
-      node = pdfium::MakeUnique<CXFA_AddViewerPreferences>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_AddViewerPreferences>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::AlwaysEmbed:
-      node = pdfium::MakeUnique<CXFA_AlwaysEmbed>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_AlwaysEmbed>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::PasswordEdit:
-      node = pdfium::MakeUnique<CXFA_PasswordEdit>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_PasswordEdit>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::NumericEdit:
-      node = pdfium::MakeUnique<CXFA_NumericEdit>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_NumericEdit>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::EncryptionMethod:
-      node = pdfium::MakeUnique<CXFA_EncryptionMethod>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_EncryptionMethod>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Change:
-      node = pdfium::MakeUnique<CXFA_Change>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Change>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::PageArea:
-      node = pdfium::MakeUnique<CXFA_PageArea>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_PageArea>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::SubmitUrl:
-      node = pdfium::MakeUnique<CXFA_SubmitUrl>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_SubmitUrl>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Oids:
-      node = pdfium::MakeUnique<CXFA_Oids>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Oids>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Signature:
-      node = pdfium::MakeUnique<CXFA_Signature>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Signature>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ADBE_JSConsole:
-      node = pdfium::MakeUnique<CXFA_ADBE_JSConsole>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ADBE_JSConsole>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Caption:
-      node = pdfium::MakeUnique<CXFA_Caption>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Caption>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Relevant:
-      node = pdfium::MakeUnique<CXFA_Relevant>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Relevant>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::FlipLabel:
-      node = pdfium::MakeUnique<CXFA_FlipLabel>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_FlipLabel>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ExData:
-      node = pdfium::MakeUnique<CXFA_ExData>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ExData>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::DayNames:
-      node = pdfium::MakeUnique<CXFA_DayNames>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_DayNames>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::SoapAction:
-      node = pdfium::MakeUnique<CXFA_SoapAction>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_SoapAction>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::DefaultTypeface:
-      node = pdfium::MakeUnique<CXFA_DefaultTypeface>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_DefaultTypeface>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Manifest:
-      node = pdfium::MakeUnique<CXFA_Manifest>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Manifest>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Overflow:
-      node = pdfium::MakeUnique<CXFA_Overflow>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Overflow>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Linear:
-      node = pdfium::MakeUnique<CXFA_Linear>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Linear>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::CurrencySymbol:
-      node = pdfium::MakeUnique<CXFA_CurrencySymbol>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_CurrencySymbol>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Delete:
-      node = pdfium::MakeUnique<CXFA_Delete>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Delete>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::DigestMethod:
-      node = pdfium::MakeUnique<CXFA_DigestMethod>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_DigestMethod>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::InstanceManager:
-      node = pdfium::MakeUnique<CXFA_InstanceManager>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_InstanceManager>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::EquateRange:
-      node = pdfium::MakeUnique<CXFA_EquateRange>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_EquateRange>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Medium:
-      node = pdfium::MakeUnique<CXFA_Medium>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Medium>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::TextEdit:
-      node = pdfium::MakeUnique<CXFA_TextEdit>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_TextEdit>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::TemplateCache:
-      node = pdfium::MakeUnique<CXFA_TemplateCache>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_TemplateCache>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::CompressObjectStream:
-      node = pdfium::MakeUnique<CXFA_CompressObjectStream>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_CompressObjectStream>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::DataValue:
-      node = pdfium::MakeUnique<CXFA_DataValue>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_DataValue>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::AccessibleContent:
-      node = pdfium::MakeUnique<CXFA_AccessibleContent>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_AccessibleContent>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::IncludeXDPContent:
-      node = pdfium::MakeUnique<CXFA_IncludeXDPContent>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_IncludeXDPContent>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::XmlConnection:
-      node = pdfium::MakeUnique<CXFA_XmlConnection>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_XmlConnection>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ValidateApprovalSignatures:
-      node = pdfium::MakeUnique<CXFA_ValidateApprovalSignatures>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ValidateApprovalSignatures>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::SignData:
-      node = pdfium::MakeUnique<CXFA_SignData>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_SignData>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Packets:
-      node = pdfium::MakeUnique<CXFA_Packets>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Packets>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::DatePattern:
-      node = pdfium::MakeUnique<CXFA_DatePattern>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_DatePattern>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::DuplexOption:
-      node = pdfium::MakeUnique<CXFA_DuplexOption>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_DuplexOption>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Base:
-      node = pdfium::MakeUnique<CXFA_Base>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Base>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Bind:
-      node = pdfium::MakeUnique<CXFA_Bind>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Bind>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Compression:
-      node = pdfium::MakeUnique<CXFA_Compression>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Compression>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::User:
-      node = pdfium::MakeUnique<CXFA_User>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_User>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Rectangle:
-      node = pdfium::MakeUnique<CXFA_Rectangle>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Rectangle>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::EffectiveOutputPolicy:
-      node = pdfium::MakeUnique<CXFA_EffectiveOutputPolicy>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_EffectiveOutputPolicy>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ADBE_JSDebugger:
-      node = pdfium::MakeUnique<CXFA_ADBE_JSDebugger>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ADBE_JSDebugger>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Acrobat7:
-      node = pdfium::MakeUnique<CXFA_Acrobat7>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Acrobat7>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Interactive:
-      node = pdfium::MakeUnique<CXFA_Interactive>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Interactive>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Locale:
-      node = pdfium::MakeUnique<CXFA_Locale>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Locale>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::CurrentPage:
-      node = pdfium::MakeUnique<CXFA_CurrentPage>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_CurrentPage>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Data:
-      node = pdfium::MakeUnique<CXFA_Data>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Data>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Date:
-      node = pdfium::MakeUnique<CXFA_Date>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Date>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Desc:
-      node = pdfium::MakeUnique<CXFA_Desc>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Desc>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Encrypt:
-      node = pdfium::MakeUnique<CXFA_Encrypt>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Encrypt>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Draw:
-      node = pdfium::MakeUnique<CXFA_Draw>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Draw>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Encryption:
-      node = pdfium::MakeUnique<CXFA_Encryption>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Encryption>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::MeridiemNames:
-      node = pdfium::MakeUnique<CXFA_MeridiemNames>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_MeridiemNames>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Messaging:
-      node = pdfium::MakeUnique<CXFA_Messaging>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Messaging>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Speak:
-      node = pdfium::MakeUnique<CXFA_Speak>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Speak>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::DataGroup:
-      node = pdfium::MakeUnique<CXFA_DataGroup>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_DataGroup>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Common:
-      node = pdfium::MakeUnique<CXFA_Common>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Common>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Sharptext:
-      node = pdfium::MakeUnique<CXFA_Sharptext>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Sharptext>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::PaginationOverride:
-      node = pdfium::MakeUnique<CXFA_PaginationOverride>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_PaginationOverride>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Reasons:
-      node = pdfium::MakeUnique<CXFA_Reasons>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Reasons>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::SignatureProperties:
-      node = pdfium::MakeUnique<CXFA_SignatureProperties>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_SignatureProperties>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Threshold:
-      node = pdfium::MakeUnique<CXFA_Threshold>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Threshold>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::AppearanceFilter:
-      node = pdfium::MakeUnique<CXFA_AppearanceFilter>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_AppearanceFilter>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Fill:
-      node = pdfium::MakeUnique<CXFA_Fill>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Fill>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Font:
-      node = pdfium::MakeUnique<CXFA_Font>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Font>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Form:
-      node = pdfium::MakeUnique<CXFA_Form>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Form>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::MediumInfo:
-      node = pdfium::MakeUnique<CXFA_MediumInfo>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_MediumInfo>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Certificate:
-      node = pdfium::MakeUnique<CXFA_Certificate>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Certificate>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Password:
-      node = pdfium::MakeUnique<CXFA_Password>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Password>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::RunScripts:
-      node = pdfium::MakeUnique<CXFA_RunScripts>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_RunScripts>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Trace:
-      node = pdfium::MakeUnique<CXFA_Trace>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Trace>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Float:
-      node = pdfium::MakeUnique<CXFA_Float>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Float>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::RenderPolicy:
-      node = pdfium::MakeUnique<CXFA_RenderPolicy>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_RenderPolicy>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Destination:
-      node = pdfium::MakeUnique<CXFA_Destination>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Destination>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Value:
-      node = pdfium::MakeUnique<CXFA_Value>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Value>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Bookend:
-      node = pdfium::MakeUnique<CXFA_Bookend>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Bookend>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ExObject:
-      node = pdfium::MakeUnique<CXFA_ExObject>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ExObject>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::OpenAction:
-      node = pdfium::MakeUnique<CXFA_OpenAction>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_OpenAction>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::NeverEmbed:
-      node = pdfium::MakeUnique<CXFA_NeverEmbed>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_NeverEmbed>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::BindItems:
-      node = pdfium::MakeUnique<CXFA_BindItems>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_BindItems>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Calculate:
-      node = pdfium::MakeUnique<CXFA_Calculate>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Calculate>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Print:
-      node = pdfium::MakeUnique<CXFA_Print>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Print>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Extras:
-      node = pdfium::MakeUnique<CXFA_Extras>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Extras>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Proto:
-      node = pdfium::MakeUnique<CXFA_Proto>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Proto>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::DSigData:
-      node = pdfium::MakeUnique<CXFA_DSigData>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_DSigData>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Creator:
-      node = pdfium::MakeUnique<CXFA_Creator>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Creator>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Connect:
-      node = pdfium::MakeUnique<CXFA_Connect>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Connect>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Permissions:
-      node = pdfium::MakeUnique<CXFA_Permissions>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Permissions>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::ConnectionSet:
-      node = pdfium::MakeUnique<CXFA_ConnectionSet>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_ConnectionSet>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Submit:
-      node = pdfium::MakeUnique<CXFA_Submit>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Submit>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Range:
-      node = pdfium::MakeUnique<CXFA_Range>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Range>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Linearized:
-      node = pdfium::MakeUnique<CXFA_Linearized>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Linearized>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Packet:
-      node = pdfium::MakeUnique<CXFA_Packet>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Packet>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::RootElement:
-      node = pdfium::MakeUnique<CXFA_RootElement>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_RootElement>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::PlaintextMetadata:
-      node = pdfium::MakeUnique<CXFA_PlaintextMetadata>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_PlaintextMetadata>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::NumberSymbols:
-      node = pdfium::MakeUnique<CXFA_NumberSymbols>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_NumberSymbols>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::PrintHighQuality:
-      node = pdfium::MakeUnique<CXFA_PrintHighQuality>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_PrintHighQuality>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Driver:
-      node = pdfium::MakeUnique<CXFA_Driver>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Driver>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::IncrementalLoad:
-      node = pdfium::MakeUnique<CXFA_IncrementalLoad>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_IncrementalLoad>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::SubjectDN:
-      node = pdfium::MakeUnique<CXFA_SubjectDN>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_SubjectDN>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::CompressLogicalStructure:
-      node = pdfium::MakeUnique<CXFA_CompressLogicalStructure>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_CompressLogicalStructure>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::IncrementalMerge:
-      node = pdfium::MakeUnique<CXFA_IncrementalMerge>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_IncrementalMerge>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Radial:
-      node = pdfium::MakeUnique<CXFA_Radial>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Radial>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Variables:
-      node = pdfium::MakeUnique<CXFA_Variables>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Variables>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::TimePatterns:
-      node = pdfium::MakeUnique<CXFA_TimePatterns>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_TimePatterns>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::EffectiveInputPolicy:
-      node = pdfium::MakeUnique<CXFA_EffectiveInputPolicy>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_EffectiveInputPolicy>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::NameAttr:
-      node = pdfium::MakeUnique<CXFA_NameAttr>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_NameAttr>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Conformance:
-      node = pdfium::MakeUnique<CXFA_Conformance>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Conformance>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Transform:
-      node = pdfium::MakeUnique<CXFA_Transform>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Transform>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::LockDocument:
-      node = pdfium::MakeUnique<CXFA_LockDocument>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_LockDocument>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::BreakAfter:
-      node = pdfium::MakeUnique<CXFA_BreakAfter>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_BreakAfter>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Line:
-      node = pdfium::MakeUnique<CXFA_Line>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Line>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Source:
-      node = pdfium::MakeUnique<CXFA_Source>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Source>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Occur:
-      node = pdfium::MakeUnique<CXFA_Occur>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Occur>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::PickTrayByPDFSize:
-      node = pdfium::MakeUnique<CXFA_PickTrayByPDFSize>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_PickTrayByPDFSize>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::MonthNames:
-      node = pdfium::MakeUnique<CXFA_MonthNames>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_MonthNames>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Severity:
-      node = pdfium::MakeUnique<CXFA_Severity>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Severity>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::GroupParent:
-      node = pdfium::MakeUnique<CXFA_GroupParent>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_GroupParent>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::DocumentAssembly:
-      node = pdfium::MakeUnique<CXFA_DocumentAssembly>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_DocumentAssembly>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::NumberSymbol:
-      node = pdfium::MakeUnique<CXFA_NumberSymbol>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_NumberSymbol>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Tagged:
-      node = pdfium::MakeUnique<CXFA_Tagged>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Tagged>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
     case XFA_Element::Items:
-      node = pdfium::MakeUnique<CXFA_Items>(doc, packet);
+      node = cppgc::MakeGarbageCollected<CXFA_Items>(
+          doc->GetHeap()->GetAllocationHandle(), doc, packet);
       break;
-    default:
-      NOTREACHED();
+    case XFA_Element::DataWindow:
+    case XFA_Element::Deltas:
+    case XFA_Element::EventPseudoModel:
+    case XFA_Element::HostPseudoModel:
+    case XFA_Element::LayoutPseudoModel:
+    case XFA_Element::List:
+    case XFA_Element::ListDuplicate:
+    case XFA_Element::LogPseudoModel:
+    case XFA_Element::Model:
+    case XFA_Element::Node:
+    case XFA_Element::NodeWithDesc:
+    case XFA_Element::NodeWithUse:
+    case XFA_Element::NodeWithValue:
+    case XFA_Element::Object:
+    case XFA_Element::SignaturePseudoModel:
+    case XFA_Element::Tree:
+    case XFA_Element::TreeList:
+    case XFA_Element::Unknown:
+      // These defined elements can not be made from an XML parse. Some are
+      // not CXFA_Node sub-classes, some are only used as intermediate classes,
+      // and so forth.
       return nullptr;
   }
   if (!node || !node->IsValidInPacket(packet))
diff --git a/xfa/fxfa/parser/cxfa_node.h b/xfa/fxfa/parser/cxfa_node.h
index 097a6a1..08e19f6 100644
--- a/xfa/fxfa/parser/cxfa_node.h
+++ b/xfa/fxfa/parser/cxfa_node.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,24 @@
 #ifndef XFA_FXFA_PARSER_CXFA_NODE_H_
 #define XFA_FXFA_PARSER_CXFA_NODE_H_
 
-#include <memory>
+#include <stddef.h>
+#include <stdint.h>
+
 #include <utility>
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/tree_node.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/optional.h"
-#include "third_party/base/span.h"
+#include "core/fxcrt/fx_coordinates.h"
+#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/containers/span.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fxfa/cxfa_ffwidget_type.h"
 #include "xfa/fxfa/fxfa.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
@@ -35,6 +44,7 @@
 class CXFA_Font;
 class CXFA_Keep;
 class CXFA_Margin;
+class CXFA_Measurement;
 class CXFA_Occur;
 class CXFA_Para;
 class CXFA_Script;
@@ -43,59 +53,78 @@
 class CXFA_Validate;
 class CXFA_Value;
 class CXFA_WidgetLayoutData;
-class LocaleIface;
+class GCedLocaleIface;
 
-#define XFA_NODEFILTER_Children 0x01
-#define XFA_NODEFILTER_Properties 0x02
-#define XFA_NODEFILTER_OneOfProperty 0x04
-
-enum XFA_CHECKSTATE {
-  XFA_CHECKSTATE_On = 0,
-  XFA_CHECKSTATE_Off = 1,
-  XFA_CHECKSTATE_Neutral = 2,
+enum class XFA_NodeFilter : uint8_t {
+  kChildren = 1 << 0,
+  kProperties = 1 << 1,
+  kOneOfProperty = 1 << 2,
 };
 
-enum XFA_VALUEPICTURE {
-  XFA_VALUEPICTURE_Raw = 0,
-  XFA_VALUEPICTURE_Display,
-  XFA_VALUEPICTURE_Edit,
-  XFA_VALUEPICTURE_DataBind,
+enum class XFA_CheckState : uint8_t {
+  kOn = 0,
+  kOff = 1,
+  kNeutral = 2,
 };
 
-enum XFA_NodeFlag {
-  XFA_NodeFlag_None = 0,
-  XFA_NodeFlag_Initialized = 1 << 0,
-  XFA_NodeFlag_HasRemovedChildren = 1 << 1,
-  XFA_NodeFlag_NeedsInitApp = 1 << 2,
-  XFA_NodeFlag_BindFormItems = 1 << 3,
-  XFA_NodeFlag_UserInteractive = 1 << 4,
-  XFA_NodeFlag_UnusedNode = 1 << 5,
-  XFA_NodeFlag_LayoutGeneratedNode = 1 << 6
+enum class XFA_ValuePicture : uint8_t {
+  kRaw = 0,
+  kDisplay,
+  kEdit,
+  kDataBind,
 };
 
-class CXFA_Node : public CXFA_Object, public TreeNode<CXFA_Node> {
+enum class XFA_NodeFlag : uint8_t {
+  kNone = 0,
+  kInitialized = 1 << 0,
+  kHasRemovedChildren = 1 << 1,
+  kNeedsInitApp = 1 << 2,
+  kBindFormItems = 1 << 3,
+  kUserInteractive = 1 << 4,
+  kUnusedNode = 1 << 5,
+  kLayoutGeneratedNode = 1 << 6
+};
+
+enum class XFA_PropertyFlag : uint8_t {
+  kOneOf = 1 << 0,
+  kDefaultOneOf = 1 << 1,
+};
+
+class CXFA_Node : public CXFA_Object, public GCedTreeNodeMixin<CXFA_Node> {
  public:
   struct PropertyData {
+    PropertyData() = delete;
+    constexpr PropertyData(XFA_Element property,
+                           uint8_t occurrence_count,
+                           Mask<XFA_PropertyFlag> flags)
+        : property(property),
+          occurrence_count(occurrence_count),
+          flags(flags) {}
+
     XFA_Element property;
-    uint8_t occurance_count;
-    uint8_t flags;
+    uint8_t occurrence_count;
+    Mask<XFA_PropertyFlag> flags;
   };
 
   struct AttributeData {
     XFA_Attribute attribute;
     XFA_AttributeType type;
-    void* default_value;
+    UNOWNED_PTR_EXCLUSION void* default_value;  // POD type.
   };
 
-  static std::unique_ptr<CXFA_Node> Create(CXFA_Document* doc,
-                                           XFA_Element element,
-                                           XFA_PacketType packet);
+  // Node is created from cppgc heap.
+  static CXFA_Node* Create(CXFA_Document* doc,
+                           XFA_Element element,
+                           XFA_PacketType packet);
 
   ~CXFA_Node() override;
 
+  // CXFA_Object:
+  void Trace(cppgc::Visitor* visitor) const override;
+
   bool HasProperty(XFA_Element property) const;
-  bool HasPropertyFlags(XFA_Element property, uint8_t flags) const;
-  uint8_t PropertyOccuranceCount(XFA_Element property) const;
+  bool HasPropertyFlag(XFA_Element property, XFA_PropertyFlag flag) const;
+  uint8_t PropertyOccurrenceCount(XFA_Element property) const;
 
   std::pair<CXFA_Node*, int32_t> GetProperty(int32_t index,
                                              XFA_Element eProperty) const;
@@ -111,9 +140,9 @@
 
   XFA_PacketType GetPacketType() const { return m_ePacket; }
 
-  void SetFlag(uint32_t dwFlag);
-  void SetFlagAndNotify(uint32_t dwFlag);
-  void ClearFlag(uint32_t dwFlag);
+  void SetInitializedFlagAndNotify();
+  void SetFlag(XFA_NodeFlag dwFlag);
+  void ClearFlag(XFA_NodeFlag dwFlag);
 
   CXFA_Node* CreateInstanceIfPossible(bool bDataMerge);
   int32_t GetCount();
@@ -124,13 +153,13 @@
                   int32_t iCount,
                   bool bMoveDataBindingNodes);
 
-  bool IsInitialized() const { return HasFlag(XFA_NodeFlag_Initialized); }
+  bool IsInitialized() const { return HasFlag(XFA_NodeFlag::kInitialized); }
   bool IsUserInteractive() const {
-    return HasFlag(XFA_NodeFlag_UserInteractive);
+    return HasFlag(XFA_NodeFlag::kUserInteractive);
   }
-  bool IsUnusedNode() const { return HasFlag(XFA_NodeFlag_UnusedNode); }
+  bool IsUnusedNode() const { return HasFlag(XFA_NodeFlag::kUnusedNode); }
   bool IsLayoutGeneratedNode() const {
-    return HasFlag(XFA_NodeFlag_LayoutGeneratedNode);
+    return HasFlag(XFA_NodeFlag::kLayoutGeneratedNode);
   }
 
   bool PresenceRequiresSpace() const;
@@ -138,7 +167,7 @@
   void SetNodeAndDescendantsUnused();
 
   bool HasRemovedChildren() const {
-    return HasFlag(XFA_NodeFlag_HasRemovedChildren);
+    return HasFlag(XFA_NodeFlag::kHasRemovedChildren);
   }
 
   bool IsAttributeInXML();
@@ -147,7 +176,7 @@
   }
 
   void SetXMLMappingNode(CFX_XMLNode* node) { xml_node_ = node; }
-  CFX_XMLNode* GetXMLMappingNode() const { return xml_node_.Get(); }
+  CFX_XMLNode* GetXMLMappingNode() const { return xml_node_; }
   CFX_XMLNode* CreateXMLMappingNode();
   bool IsNeedSavingXMLNode() const;
 
@@ -165,6 +194,13 @@
     return static_cast<T*>(GetChildInternal(index, eType, bOnlyChild));
   }
 
+  template <typename T>
+  const T* GetChild(size_t index, XFA_Element eType, bool bOnlyChild) const {
+    return static_cast<const T*>(GetChildInternal(index, eType, bOnlyChild));
+  }
+
+  bool IsAncestorOf(const CXFA_Node* that) const;
+
   void InsertChildAndNotify(int32_t index, CXFA_Node* pNode);
   void InsertChildAndNotify(CXFA_Node* pNode, CXFA_Node* pBeforeNode);
   void RemoveChildAndNotify(CXFA_Node* pNode, bool bNotify);
@@ -177,7 +213,7 @@
   CXFA_Node* GetContainerParent() const;
 
   std::vector<CXFA_Node*> GetNodeListForType(XFA_Element eTypeFilter);
-  std::vector<CXFA_Node*> GetNodeListWithFilter(uint32_t dwTypeFilter);
+  std::vector<CXFA_Node*> GetNodeListWithFilter(Mask<XFA_NodeFilter> dwFilter);
   CXFA_Node* CreateSamePacketNode(XFA_Element eType);
   CXFA_Node* CloneTemplateToForm(bool bRecursive);
   CXFA_Node* GetTemplateNodeIfExists() const;
@@ -186,13 +222,14 @@
   void SetDataDescriptionNode(CXFA_Node* pDataDescriptionNode);
   CXFA_Node* GetBindData();
   bool HasBindItems() const { return !binding_nodes_.empty(); }
-  std::vector<CXFA_Node*> GetBindItemsCopy() { return binding_nodes_; }
-  int32_t AddBindItem(CXFA_Node* pFormNode);
-  int32_t RemoveBindItem(CXFA_Node* pFormNode);
+  std::vector<CXFA_Node*> GetBindItemsCopy() const;
+  void AddBindItem(CXFA_Node* pFormNode);
+  // Returns true if there are still more items.
+  bool RemoveBindItem(CXFA_Node* pFormNode);
   bool HasBindItem() const;
   CXFA_Node* GetContainerNode();
-  LocaleIface* GetLocale();
-  Optional<WideString> GetLocaleName();
+  GCedLocaleIface* GetLocale();
+  absl::optional<WideString> GetLocaleName();
   XFA_AttributeValue GetIntact();
   WideString GetNameExpression();
 
@@ -222,11 +259,12 @@
 
   CXFA_Node* GetInstanceMgrOfSubform();
 
-  Optional<bool> GetDefaultBoolean(XFA_Attribute attr) const;
-  Optional<int32_t> GetDefaultInteger(XFA_Attribute attr) const;
-  Optional<CXFA_Measurement> GetDefaultMeasurement(XFA_Attribute attr) const;
-  Optional<WideString> GetDefaultCData(XFA_Attribute attr) const;
-  Optional<XFA_AttributeValue> GetDefaultEnum(XFA_Attribute attr) const;
+  absl::optional<bool> GetDefaultBoolean(XFA_Attribute attr) const;
+  absl::optional<int32_t> GetDefaultInteger(XFA_Attribute attr) const;
+  absl::optional<CXFA_Measurement> GetDefaultMeasurement(
+      XFA_Attribute attr) const;
+  absl::optional<WideString> GetDefaultCData(XFA_Attribute attr) const;
+  absl::optional<XFA_AttributeValue> GetDefaultEnum(XFA_Attribute attr) const;
 
   bool IsOpenAccess() const;
 
@@ -248,10 +286,10 @@
   CXFA_Validate* GetOrCreateValidateIfPossible();
 
   CXFA_Value* GetFormValueIfExists() const;
-  WideString GetRawValue();
+  WideString GetRawValue() const;
 
   int32_t GetRotate() const;
-  Optional<float> TryWidth();
+  absl::optional<float> TryWidth();
 
   CXFA_Node* GetExclGroupIfExists();
 
@@ -290,40 +328,39 @@
   void StartWidgetLayout(CXFA_FFDoc* doc,
                          float* pCalcWidth,
                          float* pCalcHeight);
-  Optional<float> FindSplitPos(CXFA_FFDocView* pDocView,
-                               size_t szBlockIndex,
-                               float fCalcHeight);
+  absl::optional<float> FindSplitPos(CXFA_FFDocView* pDocView,
+                                     size_t szBlockIndex,
+                                     float fCalcHeight);
 
   bool LoadCaption(CXFA_FFDoc* doc);
   CXFA_TextLayout* GetCaptionTextLayout();
   CXFA_TextLayout* GetTextLayout();
 
-  bool LoadImageImage(CXFA_FFDoc* doc);
-  bool LoadImageEditImage(CXFA_FFDoc* doc);
-  CFX_Size GetImageDpi() const;
-  CFX_Size GetImageEditDpi() const;
+  bool LoadLayoutImage(CXFA_FFDoc* doc);
+  bool LoadEditImage(CXFA_FFDoc* doc);
+  CFX_Size GetLayoutImageDpi() const;
+  CFX_Size GetEditImageDpi() const;
+  RetainPtr<CFX_DIBitmap> GetLayoutImage();
+  RetainPtr<CFX_DIBitmap> GetEditImage();
+  void SetLayoutImage(RetainPtr<CFX_DIBitmap> newImage);
+  void SetEditImage(RetainPtr<CFX_DIBitmap> newImage);
 
-  RetainPtr<CFX_DIBitmap> GetImageImage();
-  RetainPtr<CFX_DIBitmap> GetImageEditImage();
-  void SetImageImage(const RetainPtr<CFX_DIBitmap>& newImage);
-  void SetImageEditImage(const RetainPtr<CFX_DIBitmap>& newImage);
-
-  RetainPtr<CFGAS_GEFont> GetFDEFont(CXFA_FFDoc* doc);
+  RetainPtr<CFGAS_GEFont> GetFGASFont(CXFA_FFDoc* doc);
 
   bool IsListBox();
   bool IsRadioButton();
   bool IsMultiLine();
 
-  bool HasButtonRollover();
-  bool HasButtonDown();
+  bool HasButtonRollover() const;
+  bool HasButtonDown() const;
 
   float GetCheckButtonSize();
 
-  XFA_CHECKSTATE GetCheckState();
-  void SetCheckState(XFA_CHECKSTATE eCheckState, bool bNotify);
+  XFA_CheckState GetCheckState();
+  void SetCheckState(XFA_CheckState eCheckState);
 
   CXFA_Node* GetSelectedMember();
-  CXFA_Node* SetSelectedMember(WideStringView wsName, bool bNotify);
+  CXFA_Node* SetSelectedMember(WideStringView wsName);
   void SetSelectedMemberByValue(WideStringView wsValue,
                                 bool bNotify,
                                 bool bScriptModify,
@@ -333,8 +370,8 @@
   CXFA_Node* GetExclGroupNextMember(CXFA_Node* pNode);
 
   bool IsChoiceListAllowTextEntry();
-  int32_t CountChoiceListItems(bool bSaveValue);
-  Optional<WideString> GetChoiceListItem(int32_t nIndex, bool bSaveValue);
+  size_t CountChoiceListItems(bool bSaveValue);
+  absl::optional<WideString> GetChoiceListItem(int32_t nIndex, bool bSaveValue);
   bool IsChoiceListMultiSelect();
   bool IsChoiceListCommitOnSelect();
   std::vector<WideString> GetChoiceListItems(bool bSaveValue);
@@ -357,26 +394,24 @@
   void SetItemState(int32_t nIndex,
                     bool bSelected,
                     bool bNotify,
-                    bool bScriptModify,
-                    bool bSyncData);
+                    bool bScriptModify);
 
   WideString GetItemValue(WideStringView wsLabel);
 
   bool IsHorizontalScrollPolicyOff();
   bool IsVerticalScrollPolicyOff();
-  Optional<int32_t> GetNumberOfCells();
+  absl::optional<int32_t> GetNumberOfCells();
 
-  bool SetValue(XFA_VALUEPICTURE eValueType, const WideString& wsValue);
-  WideString GetValue(XFA_VALUEPICTURE eValueType);
-
-  WideString GetPictureContent(XFA_VALUEPICTURE ePicture);
+  bool SetValue(XFA_ValuePicture eValueType, const WideString& wsValue);
+  WideString GetValue(XFA_ValuePicture eValueType);
+  WideString GetPictureContent(XFA_ValuePicture ePicture);
   WideString GetNormalizeDataValue(const WideString& wsValue);
   WideString GetFormatDataValue(const WideString& wsValue);
   WideString NormalizeNumStr(const WideString& wsValue);
 
-  std::pair<XFA_Element, int32_t> GetMaxChars();
-  int32_t GetFracDigits();
-  int32_t GetLeadDigits();
+  std::pair<XFA_Element, int32_t> GetMaxChars() const;
+  int32_t GetFracDigits() const;
+  int32_t GetLeadDigits() const;
 
   WideString NumericLimit(const WideString& wsValue);
 
@@ -386,12 +421,12 @@
  protected:
   CXFA_Node(CXFA_Document* pDoc,
             XFA_PacketType ePacket,
-            uint32_t validPackets,
+            Mask<XFA_XDPPACKET> validPackets,
             XFA_ObjectType oType,
             XFA_Element eType,
             pdfium::span<const PropertyData> properties,
             pdfium::span<const AttributeData> attributes,
-            std::unique_ptr<CJX_Object> js_object);
+            CJX_Object* js_object);
 
   virtual XFA_Element GetValueNodeType() const;
   virtual XFA_FFWidgetType GetDefaultFFWidgetType() const;
@@ -399,8 +434,6 @@
  private:
   void ProcessScriptTestValidate(CXFA_FFDocView* pDocView,
                                  CXFA_Validate* validate,
-                                 XFA_EventError iRet,
-                                 bool pRetValue,
                                  bool bVersionFlag);
   XFA_EventError ProcessFormatTestValidate(CXFA_FFDocView* pDocView,
                                            CXFA_Validate* validate,
@@ -415,10 +448,11 @@
   bool HasFlag(XFA_NodeFlag dwFlag) const;
   const PropertyData* GetPropertyData(XFA_Element property) const;
   const AttributeData* GetAttributeData(XFA_Attribute attr) const;
-  Optional<XFA_Element> GetFirstPropertyWithFlag(uint8_t flag) const;
+  absl::optional<XFA_Element> GetFirstPropertyWithFlag(
+      XFA_PropertyFlag flag) const;
   void OnRemoved(bool bNotify) const;
-  Optional<void*> GetDefaultValue(XFA_Attribute attr,
-                                  XFA_AttributeType eType) const;
+  absl::optional<void*> GetDefaultValue(XFA_Attribute attr,
+                                        XFA_AttributeType eType) const;
   CXFA_Node* GetChildInternal(size_t index,
                               XFA_Element eType,
                               bool bOnlyChild) const;
@@ -442,7 +476,7 @@
   float GetHeightWithoutMargin(float fHeightCalc) const;
   void CalculateTextContentSize(CXFA_FFDoc* doc, CFX_SizeF* pSize);
   CFX_SizeF CalculateAccWidthAndHeight(CXFA_FFDoc* doc, float fWidth);
-  void InitLayoutData();
+  void InitLayoutData(CXFA_FFDoc* doc);
   void StartTextLayout(CXFA_FFDoc* doc, float* pCalcWidth, float* pCalcHeight);
 
   void InsertListTextItem(CXFA_Node* pItems,
@@ -462,21 +496,21 @@
       return nullptr;
     return binding_nodes_[0];
   }
-  bool BindsFormItems() const { return HasFlag(XFA_NodeFlag_BindFormItems); }
-  bool NeedsInitApp() const { return HasFlag(XFA_NodeFlag_NeedsInitApp); }
+  bool BindsFormItems() const { return HasFlag(XFA_NodeFlag::kBindFormItems); }
+  bool NeedsInitApp() const { return HasFlag(XFA_NodeFlag::kNeedsInitApp); }
   void SyncValue(const WideString& wsValue, bool bNotify);
   CXFA_Value* GetDefaultValueIfExists();
   CXFA_Bind* GetBindIfExists() const;
-  Optional<XFA_AttributeValue> GetIntactFromKeep(
+  absl::optional<XFA_AttributeValue> GetIntactFromKeep(
       const CXFA_Keep* pKeep,
       XFA_AttributeValue eLayoutType) const;
   CXFA_Node* GetTransparentParent();
 
-  Optional<float> TryHeight();
-  Optional<float> TryMinWidth();
-  Optional<float> TryMinHeight();
-  Optional<float> TryMaxWidth();
-  Optional<float> TryMaxHeight();
+  absl::optional<float> TryHeight();
+  absl::optional<float> TryMinWidth();
+  absl::optional<float> TryMinHeight();
+  absl::optional<float> TryMaxWidth();
+  absl::optional<float> TryMaxHeight();
   XFA_EventError ProcessEventInternal(CXFA_FFDocView* pDocView,
                                       XFA_AttributeValue iActivity,
                                       CXFA_Event* event,
@@ -484,22 +518,22 @@
 
   CFX_XMLDocument* GetXMLDocument() const;
 
-  const pdfium::span<const PropertyData> m_Properties;
-  const pdfium::span<const AttributeData> m_Attributes;
-  const uint32_t m_ValidPackets;
-  UnownedPtr<CFX_XMLNode> xml_node_;
-  const XFA_PacketType m_ePacket;
-  uint8_t m_ExecuteRecursionDepth = 0;
-  uint16_t m_uNodeFlags = XFA_NodeFlag_None;
-  uint32_t m_dwNameHash = 0;
-  CXFA_Node* m_pAuxNode = nullptr;         // Raw, node tree cleanup order.
-  std::vector<CXFA_Node*> binding_nodes_;  // Raw, node tree cleanup order.
+  XFA_FFWidgetType ff_widget_type_ = XFA_FFWidgetType::kNone;
   bool m_bIsNull = true;
   bool m_bPreNull = true;
   bool is_widget_ready_ = false;
-  std::unique_ptr<CXFA_WidgetLayoutData> m_pLayoutData;
-  CXFA_Ui* ui_ = nullptr;
-  XFA_FFWidgetType ff_widget_type_ = XFA_FFWidgetType::kNone;
+  const pdfium::span<const PropertyData> m_Properties;
+  const pdfium::span<const AttributeData> m_Attributes;
+  const Mask<XFA_XDPPACKET> m_ValidPackets;
+  UnownedPtr<CFX_XMLNode> xml_node_;
+  const XFA_PacketType m_ePacket;
+  uint8_t m_ExecuteRecursionDepth = 0;
+  Mask<XFA_NodeFlag> m_uNodeFlags = XFA_NodeFlag::kNone;
+  uint32_t m_dwNameHash = 0;
+  cppgc::Member<CXFA_Node> m_pAuxNode;
+  std::vector<cppgc::Member<CXFA_Node>> binding_nodes_;
+  cppgc::Member<CXFA_WidgetLayoutData> m_pLayoutData;
+  cppgc::Member<CXFA_Ui> ui_;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_NODE_H_
diff --git a/xfa/fxfa/parser/cxfa_node_unittest.cpp b/xfa/fxfa/parser/cxfa_node_unittest.cpp
index 620704f..e00772c 100644
--- a/xfa/fxfa/parser/cxfa_node_unittest.cpp
+++ b/xfa/fxfa/parser/cxfa_node_unittest.cpp
@@ -1,56 +1,67 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
+#include "fxjs/gc/heap.h"
 #include "fxjs/xfa/cjx_node.h"
+#include "testing/fxgc_unittest.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
+#include "v8/include/cppgc/allocation.h"
+#include "v8/include/cppgc/persistent.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 class TestNode final : public CXFA_Node {
  public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~TestNode() override = default;
+
+ private:
   explicit TestNode(CXFA_Document* doc)
       : CXFA_Node(doc,
                   XFA_PacketType::Form,
-                  (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                  {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                   XFA_ObjectType::Node,
                   XFA_Element::Node,
                   {},
                   {},
-                  pdfium::MakeUnique<CJX_Node>(this)) {}
-
-  ~TestNode() override = default;
+                  cppgc::MakeGarbageCollected<CJX_Node>(
+                      doc->GetHeap()->GetAllocationHandle(),
+                      this)) {}
 };
 
 }  // namespace
 
-class CXFANodeTest : public testing::Test {
+class CXFANodeTest : public FXGCUnitTest {
  public:
   void SetUp() override {
-    doc_ = pdfium::MakeUnique<CXFA_Document>(nullptr, nullptr);
-    node_ = pdfium::MakeUnique<TestNode>(doc_.get());
+    FXGCUnitTest::SetUp();
+    doc_ = cppgc::MakeGarbageCollected<CXFA_Document>(
+        heap()->GetAllocationHandle(), nullptr, heap(), nullptr);
+    node_ = cppgc::MakeGarbageCollected<TestNode>(heap()->GetAllocationHandle(),
+                                                  doc_);
   }
 
   void TearDown() override {
-    node_ = nullptr;
-    doc_ = nullptr;
+    node_.Clear();
+    doc_.Clear();
+    FXGCUnitTest::TearDown();
   }
 
-  CXFA_Document* GetDoc() const { return doc_.get(); }
-  CXFA_Node* GetNode() const { return node_.get(); }
+  CXFA_Document* GetDoc() const { return doc_; }
+  CXFA_Node* GetNode() const { return node_; }
 
  private:
-  std::unique_ptr<CXFA_Document> doc_;
-  std::unique_ptr<TestNode> node_;
+  cppgc::Persistent<CXFA_Document> doc_;
+  cppgc::Persistent<TestNode> node_;
 };
 
 TEST_F(CXFANodeTest, InsertFirstChild) {
-  EXPECT_EQ(nullptr, GetNode()->GetFirstChild());
-  EXPECT_EQ(nullptr, GetNode()->GetLastChild());
+  EXPECT_FALSE(GetNode()->GetFirstChild());
+  EXPECT_FALSE(GetNode()->GetLastChild());
 
   CXFA_Node* child =
       GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
@@ -59,8 +70,8 @@
   EXPECT_EQ(GetNode(), child->GetParent());
   EXPECT_EQ(child, GetNode()->GetFirstChild());
   EXPECT_EQ(child, GetNode()->GetLastChild());
-  EXPECT_EQ(nullptr, child->GetPrevSibling());
-  EXPECT_EQ(nullptr, child->GetNextSibling());
+  EXPECT_FALSE(child->GetPrevSibling());
+  EXPECT_FALSE(child->GetNextSibling());
 }
 
 TEST_F(CXFANodeTest, InsertChildByNegativeIndex) {
@@ -73,10 +84,10 @@
   GetNode()->InsertChildAndNotify(-1, child);
 
   EXPECT_EQ(GetNode(), child->GetParent());
-  EXPECT_EQ(nullptr, child->GetNextSibling());
+  EXPECT_FALSE(child->GetNextSibling());
   EXPECT_EQ(child0, child->GetPrevSibling());
   EXPECT_EQ(child, child0->GetNextSibling());
-  EXPECT_EQ(nullptr, child0->GetPrevSibling());
+  EXPECT_FALSE(child0->GetPrevSibling());
 
   EXPECT_EQ(child0, GetNode()->GetFirstChild());
   EXPECT_EQ(child, GetNode()->GetLastChild());
@@ -110,14 +121,14 @@
   EXPECT_EQ(child, child1->GetNextSibling());
   EXPECT_EQ(child2, child->GetNextSibling());
   EXPECT_EQ(child3, child2->GetNextSibling());
-  EXPECT_EQ(nullptr, child3->GetNextSibling());
+  EXPECT_FALSE(child3->GetNextSibling());
 
   EXPECT_EQ(child3, GetNode()->GetLastChild());
   EXPECT_EQ(child2, child3->GetPrevSibling());
   EXPECT_EQ(child, child2->GetPrevSibling());
   EXPECT_EQ(child1, child->GetPrevSibling());
   EXPECT_EQ(child0, child1->GetPrevSibling());
-  EXPECT_EQ(nullptr, child0->GetPrevSibling());
+  EXPECT_FALSE(child0->GetPrevSibling());
 }
 
 TEST_F(CXFANodeTest, InsertChildIndexPastEnd) {
@@ -134,7 +145,7 @@
   GetNode()->InsertChildAndNotify(20, child);
 
   EXPECT_EQ(GetNode(), child->GetParent());
-  EXPECT_EQ(nullptr, child->GetNextSibling());
+  EXPECT_FALSE(child->GetNextSibling());
   EXPECT_EQ(child1, child->GetPrevSibling());
   EXPECT_EQ(child, child1->GetNextSibling());
 
@@ -143,8 +154,8 @@
 }
 
 TEST_F(CXFANodeTest, InsertFirstChildBeforeNullptr) {
-  EXPECT_EQ(nullptr, GetNode()->GetFirstChild());
-  EXPECT_EQ(nullptr, GetNode()->GetLastChild());
+  EXPECT_FALSE(GetNode()->GetFirstChild());
+  EXPECT_FALSE(GetNode()->GetLastChild());
 
   CXFA_Node* child =
       GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
@@ -167,7 +178,7 @@
   GetNode()->InsertChildAndNotify(child, nullptr);
 
   EXPECT_EQ(GetNode(), child->GetParent());
-  EXPECT_EQ(nullptr, child->GetNextSibling());
+  EXPECT_FALSE(child->GetNextSibling());
   EXPECT_EQ(child1, child->GetPrevSibling());
   EXPECT_EQ(child, child1->GetNextSibling());
 
@@ -190,7 +201,7 @@
 
   EXPECT_EQ(GetNode(), child->GetParent());
   EXPECT_EQ(child0, child->GetNextSibling());
-  EXPECT_EQ(nullptr, child->GetPrevSibling());
+  EXPECT_FALSE(child->GetPrevSibling());
   EXPECT_EQ(child, child0->GetPrevSibling());
 
   EXPECT_EQ(child, GetNode()->GetFirstChild());
@@ -236,11 +247,11 @@
   EXPECT_EQ(child0, GetNode()->GetLastChild());
 
   GetNode()->RemoveChildAndNotify(child0, false);
-  EXPECT_EQ(nullptr, GetNode()->GetFirstChild());
-  EXPECT_EQ(nullptr, GetNode()->GetLastChild());
-  EXPECT_EQ(nullptr, child0->GetParent());
-  EXPECT_EQ(nullptr, child0->GetNextSibling());
-  EXPECT_EQ(nullptr, child0->GetPrevSibling());
+  EXPECT_FALSE(GetNode()->GetFirstChild());
+  EXPECT_FALSE(GetNode()->GetLastChild());
+  EXPECT_FALSE(child0->GetParent());
+  EXPECT_FALSE(child0->GetNextSibling());
+  EXPECT_FALSE(child0->GetPrevSibling());
 }
 
 TEST_F(CXFANodeTest, RemoveFirstChild) {
@@ -261,10 +272,10 @@
   GetNode()->RemoveChildAndNotify(child0, false);
   EXPECT_EQ(child1, GetNode()->GetFirstChild());
   EXPECT_EQ(child2, GetNode()->GetLastChild());
-  EXPECT_EQ(nullptr, child1->GetPrevSibling());
-  EXPECT_EQ(nullptr, child0->GetParent());
-  EXPECT_EQ(nullptr, child0->GetNextSibling());
-  EXPECT_EQ(nullptr, child0->GetPrevSibling());
+  EXPECT_FALSE(child1->GetPrevSibling());
+  EXPECT_FALSE(child0->GetParent());
+  EXPECT_FALSE(child0->GetNextSibling());
+  EXPECT_FALSE(child0->GetPrevSibling());
 }
 
 TEST_F(CXFANodeTest, RemoveLastChild) {
@@ -285,10 +296,10 @@
   GetNode()->RemoveChildAndNotify(child2, false);
   EXPECT_EQ(child0, GetNode()->GetFirstChild());
   EXPECT_EQ(child1, GetNode()->GetLastChild());
-  EXPECT_EQ(nullptr, child1->GetNextSibling());
-  EXPECT_EQ(nullptr, child2->GetParent());
-  EXPECT_EQ(nullptr, child2->GetNextSibling());
-  EXPECT_EQ(nullptr, child2->GetPrevSibling());
+  EXPECT_FALSE(child1->GetNextSibling());
+  EXPECT_FALSE(child2->GetParent());
+  EXPECT_FALSE(child2->GetNextSibling());
+  EXPECT_FALSE(child2->GetPrevSibling());
 }
 
 TEST_F(CXFANodeTest, RemoveChild) {
@@ -311,9 +322,9 @@
   EXPECT_EQ(child2, GetNode()->GetLastChild());
   EXPECT_EQ(child2, child0->GetNextSibling());
   EXPECT_EQ(child0, child2->GetPrevSibling());
-  EXPECT_EQ(nullptr, child1->GetParent());
-  EXPECT_EQ(nullptr, child1->GetNextSibling());
-  EXPECT_EQ(nullptr, child1->GetPrevSibling());
+  EXPECT_FALSE(child1->GetParent());
+  EXPECT_FALSE(child1->GetNextSibling());
+  EXPECT_FALSE(child1->GetPrevSibling());
 }
 
 TEST_F(CXFANodeTest, InsertChildWithParent) {
@@ -379,3 +390,35 @@
   GetNode()->RemoveChildAndNotify(child1, false);
   EXPECT_EQ(child0, child1->GetParent());
 }
+
+TEST_F(CXFANodeTest, AncestorOf) {
+  CXFA_Node* child0 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child0);
+
+  CXFA_Node* child1 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child1);
+
+  CXFA_Node* grandchild =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  child0->InsertChildAndNotify(-1, grandchild);
+
+  EXPECT_TRUE(GetNode()->IsAncestorOf(child0));
+  EXPECT_TRUE(GetNode()->IsAncestorOf(child1));
+  EXPECT_FALSE(child1->IsAncestorOf(child0));
+  EXPECT_TRUE(child0->IsAncestorOf(grandchild));
+  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_nodehelper.cpp b/xfa/fxfa/parser/cxfa_nodehelper.cpp
deleted file mode 100644
index dde90bc..0000000
--- a/xfa/fxfa/parser/cxfa_nodehelper.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/parser/cxfa_nodehelper.h"
-
-#include <utility>
-
-#include "core/fxcrt/fx_extension.h"
-#include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cjx_object.h"
-#include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_localemgr.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/xfa_basic_data.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
-#include "xfa/fxfa/parser/xfa_utils.h"
-
-CXFA_NodeHelper::CXFA_NodeHelper() = default;
-
-CXFA_NodeHelper::~CXFA_NodeHelper() = default;
-
-bool CXFA_NodeHelper::CreateNodeForCondition(const WideString& wsCondition) {
-  size_t szLen = wsCondition.GetLength();
-  WideString wsIndex(L"0");
-  bool bAll = false;
-  if (szLen == 0) {
-    m_iCreateFlag = XFA_ResolveNode_RSType_CreateNodeOne;
-    return false;
-  }
-  if (wsCondition[0] != '[')
-    return false;
-
-  size_t i = 1;
-  for (; i < szLen; ++i) {
-    wchar_t ch = wsCondition[i];
-    if (ch == ' ')
-      continue;
-
-    if (ch == '*')
-      bAll = true;
-    break;
-  }
-  if (bAll) {
-    wsIndex = L"1";
-    m_iCreateFlag = XFA_ResolveNode_RSType_CreateNodeAll;
-  } else {
-    m_iCreateFlag = XFA_ResolveNode_RSType_CreateNodeOne;
-    wsIndex = wsCondition.Substr(i, szLen - 1 - i);
-  }
-  int32_t iCount = wsIndex.GetInteger();
-  if (iCount < 0)
-    return false;
-
-  m_iCreateCount = iCount;
-  return true;
-}
-
-bool CXFA_NodeHelper::CreateNode(const WideString& wsName,
-                                 const WideString& wsCondition,
-                                 bool bLastNode,
-                                 CFXJSE_Engine* pScriptContext) {
-  if (!m_pCreateParent)
-    return false;
-
-  WideStringView wsNameView = wsName.AsStringView();
-  bool bIsClassName = false;
-  bool bResult = false;
-  if (!wsNameView.IsEmpty() && wsNameView[0] == '!') {
-    wsNameView = wsNameView.Last(wsNameView.GetLength() - 1);
-    m_pCreateParent = ToNode(
-        pScriptContext->GetDocument()->GetXFAObject(XFA_HASHCODE_Datasets));
-  }
-  if (!wsNameView.IsEmpty() && wsNameView[0] == '#') {
-    bIsClassName = true;
-    wsNameView = wsNameView.Last(wsNameView.GetLength() - 1);
-  }
-  if (wsNameView.IsEmpty())
-    return false;
-
-  if (m_iCreateCount == 0)
-    CreateNodeForCondition(wsCondition);
-
-  if (bIsClassName) {
-    XFA_Element eType = XFA_GetElementByName(wsNameView);
-    if (eType == XFA_Element::Unknown)
-      return false;
-
-    for (size_t i = 0; i < m_iCreateCount; ++i) {
-      CXFA_Node* pNewNode = m_pCreateParent->CreateSamePacketNode(eType);
-      if (pNewNode) {
-        m_pCreateParent->InsertChildAndNotify(pNewNode, nullptr);
-        if (i == m_iCreateCount - 1) {
-          m_pCreateParent = pNewNode;
-        }
-        bResult = true;
-      }
-    }
-  } else {
-    XFA_Element eClassType = XFA_Element::DataGroup;
-    if (bLastNode) {
-      eClassType = m_eLastCreateType;
-    }
-    for (size_t i = 0; i < m_iCreateCount; ++i) {
-      CXFA_Node* pNewNode = m_pCreateParent->CreateSamePacketNode(eClassType);
-      if (pNewNode) {
-        pNewNode->JSObject()->SetAttribute(XFA_Attribute::Name, wsNameView,
-                                           false);
-        pNewNode->CreateXMLMappingNode();
-        m_pCreateParent->InsertChildAndNotify(pNewNode, nullptr);
-        if (i == m_iCreateCount - 1) {
-          m_pCreateParent = pNewNode;
-        }
-        bResult = true;
-      }
-    }
-  }
-  if (!bResult)
-    m_pCreateParent = nullptr;
-
-  return bResult;
-}
-
-void CXFA_NodeHelper::SetCreateNodeType(CXFA_Node* refNode) {
-  if (!refNode)
-    return;
-
-  if (refNode->GetElementType() == XFA_Element::Subform) {
-    m_eLastCreateType = XFA_Element::DataGroup;
-  } else if (refNode->GetElementType() == XFA_Element::Field) {
-    m_eLastCreateType = XFA_FieldIsMultiListBox(refNode)
-                            ? XFA_Element::DataGroup
-                            : XFA_Element::DataValue;
-  } else if (refNode->GetElementType() == XFA_Element::ExclGroup) {
-    m_eLastCreateType = XFA_Element::DataValue;
-  }
-}
diff --git a/xfa/fxfa/parser/cxfa_nodehelper.h b/xfa/fxfa/parser/cxfa_nodehelper.h
deleted file mode 100644
index 66e7e66..0000000
--- a/xfa/fxfa/parser/cxfa_nodehelper.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_PARSER_CXFA_NODEHELPER_H_
-#define XFA_FXFA_PARSER_CXFA_NODEHELPER_H_
-
-#include <vector>
-
-#include "core/fxcrt/fx_string.h"
-#include "xfa/fxfa/fxfa_basic.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
-
-class CFXJSE_Engine;
-class CXFA_Node;
-
-class CXFA_NodeHelper {
- public:
-  CXFA_NodeHelper();
-  ~CXFA_NodeHelper();
-
-  bool CreateNode(const WideString& wsName,
-                  const WideString& wsCondition,
-                  bool bLastNode,
-                  CFXJSE_Engine* pScriptContext);
-  bool CreateNodeForCondition(const WideString& wsCondition);
-  void SetCreateNodeType(CXFA_Node* refNode);
-
-  XFA_Element m_eLastCreateType = XFA_Element::DataValue;
-  XFA_ResolveNode_RSType m_iCreateFlag = XFA_ResolveNode_RSType_CreateNodeOne;
-  size_t m_iCreateCount = 0;
-  int32_t m_iCurAllStart = -1;
-  UnownedPtr<CXFA_Node> m_pCreateParent;
-  UnownedPtr<CXFA_Node> m_pAllStartParent;
-};
-
-#endif  // XFA_FXFA_PARSER_CXFA_NODEHELPER_H_
diff --git a/xfa/fxfa/parser/cxfa_nodeiteratortemplate.h b/xfa/fxfa/parser/cxfa_nodeiteratortemplate.h
index 8803efa..de8512a 100644
--- a/xfa/fxfa/parser/cxfa_nodeiteratortemplate.h
+++ b/xfa/fxfa/parser/cxfa_nodeiteratortemplate.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,17 +8,20 @@
 #define XFA_FXFA_PARSER_CXFA_NODEITERATORTEMPLATE_H_
 
 #include "core/fxcrt/unowned_ptr.h"
+#include "v8/include/cppgc/macros.h"
 
 template <class NodeType,
           class TraverseStrategy,
           typename HolderType = UnownedPtr<NodeType>>
 class CXFA_NodeIteratorTemplate {
+  CPPGC_STACK_ALLOCATED();  // Allows Raw/Unowned |HolderType|.
+
  public:
   explicit CXFA_NodeIteratorTemplate(NodeType* pRoot)
       : m_pRoot(pRoot), m_pCurrent(pRoot) {}
 
-  NodeType* GetRoot() const { return m_pRoot.Get(); }
-  NodeType* GetCurrent() const { return m_pCurrent.Get(); }
+  NodeType* GetRoot() const { return static_cast<NodeType*>(m_pRoot); }
+  NodeType* GetCurrent() const { return static_cast<NodeType*>(m_pCurrent); }
 
   void Reset() { m_pCurrent = m_pRoot; }
   bool SetCurrent(NodeType* pNode) {
@@ -26,7 +29,7 @@
       m_pCurrent = nullptr;
       return false;
     }
-    m_pCurrent.Reset(pNode);
+    m_pCurrent = pNode;
     return true;
   }
 
@@ -34,26 +37,28 @@
     if (!m_pRoot)
       return nullptr;
     if (!m_pCurrent) {
-      m_pCurrent.Reset(LastDescendant(m_pRoot.Get()));
-      return m_pCurrent.Get();
+      m_pCurrent = LastDescendant(static_cast<NodeType*>(m_pRoot));
+      return static_cast<NodeType*>(m_pCurrent);
     }
-    NodeType* pSibling = PreviousSiblingWithinSubtree(m_pCurrent.Get());
+    NodeType* pSibling =
+        PreviousSiblingWithinSubtree(static_cast<NodeType*>(m_pCurrent));
     if (pSibling) {
-      m_pCurrent.Reset(LastDescendant(pSibling));
-      return m_pCurrent.Get();
+      m_pCurrent = LastDescendant(pSibling);
+      return static_cast<NodeType*>(m_pCurrent);
     }
-    NodeType* pParent = ParentWithinSubtree(m_pCurrent.Get());
+    NodeType* pParent = ParentWithinSubtree(static_cast<NodeType*>(m_pCurrent));
     if (pParent)
-      m_pCurrent.Reset(pParent);
+      m_pCurrent = pParent;
     return pParent;
   }
 
   NodeType* MoveToNext() {
     if (!m_pRoot || !m_pCurrent)
       return nullptr;
-    NodeType* pChild = TraverseStrategy::GetFirstChild(m_pCurrent.Get());
+    NodeType* pChild =
+        TraverseStrategy::GetFirstChild(static_cast<NodeType*>(m_pCurrent));
     if (pChild) {
-      m_pCurrent.Reset(pChild);
+      m_pCurrent = pChild;
       return pChild;
     }
     return SkipChildrenAndMoveToNext();
@@ -62,11 +67,11 @@
   NodeType* SkipChildrenAndMoveToNext() {
     if (!m_pRoot)
       return nullptr;
-    NodeType* pNode = m_pCurrent.Get();
+    NodeType* pNode = static_cast<NodeType*>(m_pCurrent);
     while (pNode) {
       NodeType* pSibling = NextSiblingWithinSubtree(pNode);
       if (pSibling) {
-        m_pCurrent.Reset(pSibling);
+        m_pCurrent = pSibling;
         return pSibling;
       }
       pNode = ParentWithinSubtree(pNode);
diff --git a/xfa/fxfa/parser/cxfa_nodeiteratortemplate_unittest.cpp b/xfa/fxfa/parser/cxfa_nodeiteratortemplate_unittest.cpp
index aa4f72d..4692ade 100644
--- a/xfa/fxfa/parser/cxfa_nodeiteratortemplate_unittest.cpp
+++ b/xfa/fxfa/parser/cxfa_nodeiteratortemplate_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,6 @@
 #include <vector>
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
 class CXFA_NodeIteratorTemplateTest : public testing::Test {
  public:
@@ -28,7 +27,7 @@
       }
     };
 
-    explicit Node(Node* parent) : parent_(parent), next_sibling_(nullptr) {
+    explicit Node(Node* parent) : parent_(parent) {
       if (parent) {
         if (!parent->children_.empty())
           parent->children_.back()->next_sibling_ = this;
@@ -37,8 +36,8 @@
     }
 
    private:
-    Node* parent_;
-    Node* next_sibling_;
+    Node* const parent_;
+    Node* next_sibling_ = nullptr;
     std::vector<Node*> children_;
   };
 
@@ -57,16 +56,16 @@
   //            child5
   //
   void SetUp() override {
-    root_ = pdfium::MakeUnique<Node>(nullptr);
-    child1_ = pdfium::MakeUnique<Node>(root_.get());
-    child2_ = pdfium::MakeUnique<Node>(root_.get());
-    child3_ = pdfium::MakeUnique<Node>(child2_.get());
-    child4_ = pdfium::MakeUnique<Node>(child3_.get());
-    child5_ = pdfium::MakeUnique<Node>(child4_.get());
-    child6_ = pdfium::MakeUnique<Node>(child3_.get());
-    child7_ = pdfium::MakeUnique<Node>(child2_.get());
-    child8_ = pdfium::MakeUnique<Node>(child7_.get());
-    child9_ = pdfium::MakeUnique<Node>(child2_.get());
+    root_ = std::make_unique<Node>(nullptr);
+    child1_ = std::make_unique<Node>(root_.get());
+    child2_ = std::make_unique<Node>(root_.get());
+    child3_ = std::make_unique<Node>(child2_.get());
+    child4_ = std::make_unique<Node>(child3_.get());
+    child5_ = std::make_unique<Node>(child4_.get());
+    child6_ = std::make_unique<Node>(child3_.get());
+    child7_ = std::make_unique<Node>(child2_.get());
+    child8_ = std::make_unique<Node>(child7_.get());
+    child9_ = std::make_unique<Node>(child2_.get());
   }
 
   Node* root() const { return root_.get(); }
@@ -95,11 +94,11 @@
 
 TEST_F(CXFA_NodeIteratorTemplateTest, Empty) {
   Iterator iter(nullptr);
-  EXPECT_EQ(nullptr, iter.GetRoot());
-  EXPECT_EQ(nullptr, iter.GetCurrent());
-  EXPECT_EQ(nullptr, iter.MoveToNext());
-  EXPECT_EQ(nullptr, iter.MoveToPrev());
-  EXPECT_EQ(nullptr, iter.SkipChildrenAndMoveToNext());
+  EXPECT_FALSE(iter.GetRoot());
+  EXPECT_FALSE(iter.GetCurrent());
+  EXPECT_FALSE(iter.MoveToNext());
+  EXPECT_FALSE(iter.MoveToPrev());
+  EXPECT_FALSE(iter.SkipChildrenAndMoveToNext());
 }
 
 TEST_F(CXFA_NodeIteratorTemplateTest, Root) {
@@ -119,7 +118,7 @@
   Iterator iter(child1());
   iter.SetCurrent(root());
   EXPECT_EQ(child1(), iter.GetRoot());
-  EXPECT_EQ(nullptr, iter.GetCurrent());
+  EXPECT_FALSE(iter.GetCurrent());
 }
 
 TEST_F(CXFA_NodeIteratorTemplateTest, CurrentNull) {
@@ -127,10 +126,10 @@
   EXPECT_EQ(child1(), iter.MoveToNext());
 
   iter.SetCurrent(nullptr);
-  EXPECT_EQ(nullptr, iter.GetCurrent());
+  EXPECT_FALSE(iter.GetCurrent());
 
-  EXPECT_EQ(nullptr, iter.MoveToNext());
-  EXPECT_EQ(nullptr, iter.GetCurrent());
+  EXPECT_FALSE(iter.MoveToNext());
+  EXPECT_FALSE(iter.GetCurrent());
 }
 
 TEST_F(CXFA_NodeIteratorTemplateTest, MoveToPrev) {
@@ -164,10 +163,10 @@
   EXPECT_EQ(root(), iter.MoveToPrev());
   EXPECT_EQ(root(), iter.GetCurrent());
 
-  EXPECT_EQ(nullptr, iter.MoveToPrev());
+  EXPECT_FALSE(iter.MoveToPrev());
   EXPECT_EQ(root(), iter.GetCurrent());
 
-  EXPECT_EQ(nullptr, iter.MoveToPrev());
+  EXPECT_FALSE(iter.MoveToPrev());
   EXPECT_EQ(root(), iter.GetCurrent());
 }
 
@@ -196,11 +195,11 @@
   EXPECT_EQ(child9(), iter.MoveToNext());
   EXPECT_EQ(child9(), iter.GetCurrent());
 
-  EXPECT_EQ(nullptr, iter.MoveToNext());
-  EXPECT_EQ(nullptr, iter.GetCurrent());
+  EXPECT_FALSE(iter.MoveToNext());
+  EXPECT_FALSE(iter.GetCurrent());
 
-  EXPECT_EQ(nullptr, iter.MoveToNext());
-  EXPECT_EQ(nullptr, iter.GetCurrent());
+  EXPECT_FALSE(iter.MoveToNext());
+  EXPECT_FALSE(iter.GetCurrent());
 }
 
 TEST_F(CXFA_NodeIteratorTemplateTest, SkipChildrenAndMoveToNext) {
@@ -208,7 +207,7 @@
   iter.SetCurrent(child3());
   EXPECT_EQ(child7(), iter.SkipChildrenAndMoveToNext());
   EXPECT_EQ(child9(), iter.SkipChildrenAndMoveToNext());
-  EXPECT_EQ(nullptr, iter.SkipChildrenAndMoveToNext());
+  EXPECT_FALSE(iter.SkipChildrenAndMoveToNext());
 }
 
 TEST_F(CXFA_NodeIteratorTemplateTest, BackAndForth) {
@@ -226,7 +225,7 @@
 
 TEST_F(CXFA_NodeIteratorTemplateTest, NextFromBeforeTheBeginning) {
   Iterator iter(root());
-  EXPECT_EQ(nullptr, iter.MoveToPrev());
+  EXPECT_FALSE(iter.MoveToPrev());
   EXPECT_EQ(root(), iter.GetCurrent());
   EXPECT_EQ(child1(), iter.MoveToNext());
 }
@@ -234,17 +233,17 @@
 TEST_F(CXFA_NodeIteratorTemplateTest, PrevFromAfterTheEnd) {
   Iterator iter(root());
   iter.SetCurrent(child9());
-  EXPECT_EQ(nullptr, iter.MoveToNext());
+  EXPECT_FALSE(iter.MoveToNext());
   EXPECT_EQ(child9(), iter.MoveToPrev());
 }
 
 TEST_F(CXFA_NodeIteratorTemplateTest, ChildAsRootPrev) {
   Iterator iter(child3());
-  EXPECT_EQ(nullptr, iter.MoveToPrev());
+  EXPECT_FALSE(iter.MoveToPrev());
 
   iter.SetCurrent(child4());
   EXPECT_EQ(child3(), iter.MoveToPrev());
-  EXPECT_EQ(nullptr, iter.MoveToPrev());
+  EXPECT_FALSE(iter.MoveToPrev());
 }
 
 TEST_F(CXFA_NodeIteratorTemplateTest, ChildAsRootNext) {
@@ -252,5 +251,5 @@
   iter.SetCurrent(child4());
   EXPECT_EQ(child5(), iter.MoveToNext());
   EXPECT_EQ(child6(), iter.MoveToNext());
-  EXPECT_EQ(nullptr, iter.MoveToNext());
+  EXPECT_FALSE(iter.MoveToNext());
 }
diff --git a/xfa/fxfa/parser/cxfa_nodelocale.cpp b/xfa/fxfa/parser/cxfa_nodelocale.cpp
index 01d2dc5..60a44de 100644
--- a/xfa/fxfa/parser/cxfa_nodelocale.cpp
+++ b/xfa/fxfa/parser/cxfa_nodelocale.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,6 @@
 
 #include "xfa/fxfa/parser/cxfa_nodelocale.h"
 
-#include <utility>
-
 #include "fxjs/xfa/cjx_object.h"
 #include "xfa/fxfa/parser/cxfa_calendarsymbols.h"
 #include "xfa/fxfa/parser/cxfa_datetimesymbols.h"
@@ -19,35 +17,39 @@
 
 namespace {
 
-const wchar_t g_FX_Percent[] = L"z,zzz,zzz,zzz,zzz,zzz%";
-const wchar_t g_FX_Currency[] = L"$z,zzz,zzz,zzz,zzz,zz9.99";
-const wchar_t g_FX_Decimal[] = L"z,zzz,zzz,zzz,zzz,zz9.zzz";
-const wchar_t g_FX_Integer[] = L"z,zzz,zzz,zzz,zzz,zzz";
+const wchar_t kFxPercent[] = L"z,zzz,zzz,zzz,zzz,zzz%";
+const wchar_t kFxCurrency[] = L"$z,zzz,zzz,zzz,zzz,zz9.99";
+const wchar_t kFxDecimal[] = L"z,zzz,zzz,zzz,zzz,zz9.zzz";
+const wchar_t kFxInteger[] = L"z,zzz,zzz,zzz,zzz,zzz";
 
 }  // namespace
 
-WideString XFA_PatternToString(FX_LOCALENUMSUBCATEGORY category) {
+WideString XFA_PatternToString(LocaleIface::NumSubcategory category) {
   switch (category) {
-    case FX_LOCALENUMPATTERN_Percent:
-      return g_FX_Percent;
-    case FX_LOCALENUMPATTERN_Currency:
-      return g_FX_Currency;
-    case FX_LOCALENUMPATTERN_Decimal:
-      return g_FX_Decimal;
-    case FX_LOCALENUMPATTERN_Integer:
-      return g_FX_Integer;
+    case LocaleIface::NumSubcategory::kPercent:
+      return kFxPercent;
+    case LocaleIface::NumSubcategory::kCurrency:
+      return kFxCurrency;
+    case LocaleIface::NumSubcategory::kDecimal:
+      return kFxDecimal;
+    case LocaleIface::NumSubcategory::kInteger:
+      return kFxInteger;
   }
   return WideString();
 }
 
-CXFA_NodeLocale::CXFA_NodeLocale(CXFA_Node* pLocale) : m_pLocale(pLocale) {}
+CXFA_NodeLocale::CXFA_NodeLocale(CXFA_Node* pNode) : m_pNode(pNode) {}
 
-CXFA_NodeLocale::~CXFA_NodeLocale() {}
+CXFA_NodeLocale::~CXFA_NodeLocale() = default;
+
+void CXFA_NodeLocale::Trace(cppgc::Visitor* visitor) const {
+  GCedLocaleIface::Trace(visitor);
+  visitor->Trace(m_pNode);
+}
 
 WideString CXFA_NodeLocale::GetName() const {
-  return WideString(m_pLocale
-                        ? m_pLocale->JSObject()->GetCData(XFA_Attribute::Name)
-                        : nullptr);
+  return WideString(m_pNode ? m_pNode->JSObject()->GetCData(XFA_Attribute::Name)
+                            : nullptr);
 }
 
 WideString CXFA_NodeLocale::GetDecimalSymbol() const {
@@ -72,9 +74,9 @@
 
 WideString CXFA_NodeLocale::GetDateTimeSymbols() const {
   CXFA_DateTimeSymbols* pSymbols =
-      m_pLocale ? m_pLocale->GetChild<CXFA_DateTimeSymbols>(
-                      0, XFA_Element::DateTimeSymbols, false)
-                : nullptr;
+      m_pNode ? m_pNode->GetChild<CXFA_DateTimeSymbols>(
+                    0, XFA_Element::DateTimeSymbols, false)
+              : nullptr;
   return pSymbols ? pSymbols->JSObject()->GetContent(false) : WideString();
 }
 
@@ -90,47 +92,45 @@
   return GetCalendarSymbol(XFA_Element::MeridiemNames, bAM ? 0 : 1, false);
 }
 
-FX_TIMEZONE CXFA_NodeLocale::GetTimeZone() const {
-  return CXFA_TimeZoneProvider().GetTimeZone();
+int CXFA_NodeLocale::GetTimeZoneInMinutes() const {
+  return CXFA_TimeZoneProvider().GetTimeZoneInMinutes();
 }
 
 WideString CXFA_NodeLocale::GetEraName(bool bAD) const {
   return GetCalendarSymbol(XFA_Element::EraNames, bAD ? 1 : 0, false);
 }
 
-WideString CXFA_NodeLocale::GetDatePattern(
-    FX_LOCALEDATETIMESUBCATEGORY eType) const {
+WideString CXFA_NodeLocale::GetDatePattern(DateTimeSubcategory eType) const {
   switch (eType) {
-    case FX_LOCALEDATETIMESUBCATEGORY_Short:
+    case DateTimeSubcategory::kShort:
       return GetSymbol(XFA_Element::DatePatterns, L"short");
-    case FX_LOCALEDATETIMESUBCATEGORY_Medium:
-    case FX_LOCALEDATETIMESUBCATEGORY_Default:
+    case DateTimeSubcategory::kMedium:
+    case DateTimeSubcategory::kDefault:
       return GetSymbol(XFA_Element::DatePatterns, L"med");
-    case FX_LOCALEDATETIMESUBCATEGORY_Full:
+    case DateTimeSubcategory::kFull:
       return GetSymbol(XFA_Element::DatePatterns, L"full");
-    case FX_LOCALEDATETIMESUBCATEGORY_Long:
+    case DateTimeSubcategory::kLong:
       return GetSymbol(XFA_Element::DatePatterns, L"long");
   }
   return WideString();
 }
 
-WideString CXFA_NodeLocale::GetTimePattern(
-    FX_LOCALEDATETIMESUBCATEGORY eType) const {
+WideString CXFA_NodeLocale::GetTimePattern(DateTimeSubcategory eType) const {
   switch (eType) {
-    case FX_LOCALEDATETIMESUBCATEGORY_Short:
+    case DateTimeSubcategory::kShort:
       return GetSymbol(XFA_Element::TimePatterns, L"short");
-    case FX_LOCALEDATETIMESUBCATEGORY_Medium:
-    case FX_LOCALEDATETIMESUBCATEGORY_Default:
+    case DateTimeSubcategory::kMedium:
+    case DateTimeSubcategory::kDefault:
       return GetSymbol(XFA_Element::TimePatterns, L"med");
-    case FX_LOCALEDATETIMESUBCATEGORY_Full:
+    case DateTimeSubcategory::kFull:
       return GetSymbol(XFA_Element::TimePatterns, L"full");
-    case FX_LOCALEDATETIMESUBCATEGORY_Long:
+    case DateTimeSubcategory::kLong:
       return GetSymbol(XFA_Element::TimePatterns, L"long");
   }
   return WideString();
 }
 
-WideString CXFA_NodeLocale::GetNumPattern(FX_LOCALENUMSUBCATEGORY eType) const {
+WideString CXFA_NodeLocale::GetNumPattern(NumSubcategory eType) const {
   return XFA_PatternToString(eType);
 }
 
@@ -138,7 +138,7 @@
                                           WideStringView wsName) const {
   CXFA_Node* pChild = pParent ? pParent->GetFirstChild() : nullptr;
   while (pChild) {
-    if (pChild->JSObject()->GetAttribute(XFA_Attribute::Name) == wsName)
+    if (pChild->JSObject()->GetAttributeByEnum(XFA_Attribute::Name) == wsName)
       return pChild;
 
     pChild = pChild->GetNextSibling();
@@ -149,7 +149,7 @@
 WideString CXFA_NodeLocale::GetSymbol(XFA_Element eElement,
                                       WideStringView symbol_type) const {
   CXFA_Node* pSymbols =
-      m_pLocale ? m_pLocale->GetChild<CXFA_Node>(0, eElement, false) : nullptr;
+      m_pNode ? m_pNode->GetChild<CXFA_Node>(0, eElement, false) : nullptr;
   CXFA_Node* pSymbol = GetNodeByName(pSymbols, symbol_type);
   return pSymbol ? pSymbol->JSObject()->GetContent(false) : WideString();
 }
@@ -158,9 +158,9 @@
                                               int index,
                                               bool bAbbr) const {
   CXFA_CalendarSymbols* pCalendar =
-      m_pLocale ? m_pLocale->GetChild<CXFA_CalendarSymbols>(
-                      0, XFA_Element::CalendarSymbols, false)
-                : nullptr;
+      m_pNode ? m_pNode->GetChild<CXFA_CalendarSymbols>(
+                    0, XFA_Element::CalendarSymbols, false)
+              : nullptr;
   if (!pCalendar)
     return WideString();
 
diff --git a/xfa/fxfa/parser/cxfa_nodelocale.h b/xfa/fxfa/parser/cxfa_nodelocale.h
index 0ada1a4..d7e26f3 100644
--- a/xfa/fxfa/parser/cxfa_nodelocale.h
+++ b/xfa/fxfa/parser/cxfa_nodelocale.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,23 @@
 #ifndef XFA_FXFA_PARSER_CXFA_NODELOCALE_H_
 #define XFA_FXFA_PARSER_CXFA_NODELOCALE_H_
 
-#include "xfa/fgas/crt/locale_iface.h"
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
 #include "xfa/fxfa/fxfa_basic.h"
+#include "xfa/fxfa/parser/gced_locale_iface.h"
 
 class CXFA_Node;
 
-WideString XFA_PatternToString(FX_LOCALENUMSUBCATEGORY category);
+WideString XFA_PatternToString(LocaleIface::NumSubcategory category);
 
-class CXFA_NodeLocale final : public LocaleIface {
+class CXFA_NodeLocale final : public GCedLocaleIface {
  public:
-  explicit CXFA_NodeLocale(CXFA_Node* pLocale);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_NodeLocale() override;
 
-  // LocaleIface
+  // GCedLocaleIface:
+  void Trace(cppgc::Visitor* visitor) const override;
   WideString GetName() const override;
   WideString GetDecimalSymbol() const override;
   WideString GetGroupingSymbol() const override;
@@ -30,21 +34,23 @@
   WideString GetMonthName(int32_t nMonth, bool bAbbr) const override;
   WideString GetDayName(int32_t nWeek, bool bAbbr) const override;
   WideString GetMeridiemName(bool bAM) const override;
-  FX_TIMEZONE GetTimeZone() const override;
+  int GetTimeZoneInMinutes() const override;
   WideString GetEraName(bool bAD) const override;
 
-  WideString GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY eType) const override;
-  WideString GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY eType) const override;
-  WideString GetNumPattern(FX_LOCALENUMSUBCATEGORY eType) const override;
+  WideString GetDatePattern(DateTimeSubcategory eType) const override;
+  WideString GetTimePattern(DateTimeSubcategory eType) const override;
+  WideString GetNumPattern(NumSubcategory eType) const override;
 
  private:
+  explicit CXFA_NodeLocale(CXFA_Node* pNode);
+
   CXFA_Node* GetNodeByName(CXFA_Node* pParent, WideStringView wsName) const;
   WideString GetSymbol(XFA_Element eElement, WideStringView symbol_type) const;
   WideString GetCalendarSymbol(XFA_Element eElement,
                                int index,
                                bool bAbbr) const;
 
-  UnownedPtr<CXFA_Node> const m_pLocale;
+  cppgc::Member<CXFA_Node> const m_pNode;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_NODELOCALE_H_
diff --git a/xfa/fxfa/parser/cxfa_nodeowner.cpp b/xfa/fxfa/parser/cxfa_nodeowner.cpp
index 9baf16c..b749f45 100644
--- a/xfa/fxfa/parser/cxfa_nodeowner.cpp
+++ b/xfa/fxfa/parser/cxfa_nodeowner.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,21 +6,20 @@
 
 #include "xfa/fxfa/parser/cxfa_nodeowner.h"
 
-#include <utility>
-
+#include "fxjs/gc/container_trace.h"
+#include "third_party/base/check.h"
+#include "xfa/fxfa/parser/cxfa_list.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
 CXFA_NodeOwner::CXFA_NodeOwner() = default;
 
-CXFA_NodeOwner::~CXFA_NodeOwner() {
-  is_being_destroyed_ = true;
+CXFA_NodeOwner::~CXFA_NodeOwner() = default;
+
+void CXFA_NodeOwner::Trace(cppgc::Visitor* visitor) const {
+  ContainerTrace(visitor, lists_);
 }
 
-CXFA_Node* CXFA_NodeOwner::AddOwnedNode(std::unique_ptr<CXFA_Node> node) {
-  if (!node)
-    return nullptr;
-
-  CXFA_Node* ret = node.get();
-  nodes_.push_back(std::move(node));
-  return ret;
+void CXFA_NodeOwner::PersistList(CXFA_List* list) {
+  DCHECK(list);
+  lists_.emplace_back(list);
 }
diff --git a/xfa/fxfa/parser/cxfa_nodeowner.h b/xfa/fxfa/parser/cxfa_nodeowner.h
index 8f6abca..553316b 100644
--- a/xfa/fxfa/parser/cxfa_nodeowner.h
+++ b/xfa/fxfa/parser/cxfa_nodeowner.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,23 +7,27 @@
 #ifndef XFA_FXFA_PARSER_CXFA_NODEOWNER_H_
 #define XFA_FXFA_PARSER_CXFA_NODEOWNER_H_
 
-#include <memory>
 #include <vector>
 
-class CXFA_Node;
+#include "fxjs/gc/heap.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
 
-class CXFA_NodeOwner {
+class CXFA_List;
+
+class CXFA_NodeOwner : public cppgc::GarbageCollected<CXFA_NodeOwner> {
  public:
-  virtual ~CXFA_NodeOwner();
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CXFA_NodeOwner();
 
-  CXFA_Node* AddOwnedNode(std::unique_ptr<CXFA_Node> node);
-  bool IsBeingDestroyed() const { return is_being_destroyed_; }
+  void Trace(cppgc::Visitor* visitor) const;
+  void PersistList(CXFA_List* list);
 
- protected:
+ private:
   CXFA_NodeOwner();
 
-  bool is_being_destroyed_ = false;
-  std::vector<std::unique_ptr<CXFA_Node>> nodes_;
+  std::vector<cppgc::Member<CXFA_List>> lists_;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_NODEOWNER_H_
diff --git a/xfa/fxfa/parser/cxfa_numberofcopies.cpp b/xfa/fxfa/parser/cxfa_numberofcopies.cpp
index d66fd98..94ed0c3 100644
--- a/xfa/fxfa/parser/cxfa_numberofcopies.cpp
+++ b/xfa/fxfa/parser/cxfa_numberofcopies.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_numberofcopies.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                          XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::NumberOfCopies,
                 {},
                 kNumberOfCopiesAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_NumberOfCopies::~CXFA_NumberOfCopies() = default;
diff --git a/xfa/fxfa/parser/cxfa_numberofcopies.h b/xfa/fxfa/parser/cxfa_numberofcopies.h
index 3215bb3..9060dd4 100644
--- a/xfa/fxfa/parser/cxfa_numberofcopies.h
+++ b/xfa/fxfa/parser/cxfa_numberofcopies.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_NumberOfCopies final : public CXFA_Node {
  public:
-  CXFA_NumberOfCopies(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_NumberOfCopies() override;
+
+ private:
+  CXFA_NumberOfCopies(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_NUMBEROFCOPIES_H_
diff --git a/xfa/fxfa/parser/cxfa_numberpattern.cpp b/xfa/fxfa/parser/cxfa_numberpattern.cpp
index 964855c..c9c9c83 100644
--- a/xfa/fxfa/parser/cxfa_numberpattern.cpp
+++ b/xfa/fxfa/parser/cxfa_numberpattern.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_numberpattern.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                        XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::NumberPattern,
                 {},
                 kNumberPatternAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_NumberPattern::~CXFA_NumberPattern() = default;
diff --git a/xfa/fxfa/parser/cxfa_numberpattern.h b/xfa/fxfa/parser/cxfa_numberpattern.h
index 5c42d46..5b7b59c 100644
--- a/xfa/fxfa/parser/cxfa_numberpattern.h
+++ b/xfa/fxfa/parser/cxfa_numberpattern.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_NumberPattern final : public CXFA_Node {
  public:
-  CXFA_NumberPattern(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_NumberPattern() override;
+
+ private:
+  CXFA_NumberPattern(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_NUMBERPATTERN_H_
diff --git a/xfa/fxfa/parser/cxfa_numberpatterns.cpp b/xfa/fxfa/parser/cxfa_numberpatterns.cpp
index 1597a29..d53ccf5 100644
--- a/xfa/fxfa/parser/cxfa_numberpatterns.cpp
+++ b/xfa/fxfa/parser/cxfa_numberpatterns.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_numberpatterns.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kNumberPatternsPropertyData[] = {
-    {XFA_Element::NumberPattern, 4, 0},
+    {XFA_Element::NumberPattern, 4, {}},
 };
 
 }  // namespace
@@ -21,11 +21,13 @@
                                          XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::NumberPatterns,
                 kNumberPatternsPropertyData,
                 {},
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_NumberPatterns::~CXFA_NumberPatterns() = default;
diff --git a/xfa/fxfa/parser/cxfa_numberpatterns.h b/xfa/fxfa/parser/cxfa_numberpatterns.h
index 58c8cb6..d7c4bf0 100644
--- a/xfa/fxfa/parser/cxfa_numberpatterns.h
+++ b/xfa/fxfa/parser/cxfa_numberpatterns.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_NumberPatterns final : public CXFA_Node {
  public:
-  CXFA_NumberPatterns(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_NumberPatterns() override;
+
+ private:
+  CXFA_NumberPatterns(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_NUMBERPATTERNS_H_
diff --git a/xfa/fxfa/parser/cxfa_numbersymbol.cpp b/xfa/fxfa/parser/cxfa_numbersymbol.cpp
index 398ab17..0080209 100644
--- a/xfa/fxfa/parser/cxfa_numbersymbol.cpp
+++ b/xfa/fxfa/parser/cxfa_numbersymbol.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_numbersymbol.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_NumberSymbol::CXFA_NumberSymbol(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::NumberSymbol,
                 {},
                 kNumberSymbolAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_NumberSymbol::~CXFA_NumberSymbol() = default;
diff --git a/xfa/fxfa/parser/cxfa_numbersymbol.h b/xfa/fxfa/parser/cxfa_numbersymbol.h
index 404f366..4e02a82 100644
--- a/xfa/fxfa/parser/cxfa_numbersymbol.h
+++ b/xfa/fxfa/parser/cxfa_numbersymbol.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_NumberSymbol final : public CXFA_Node {
  public:
-  CXFA_NumberSymbol(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_NumberSymbol() override;
+
+ private:
+  CXFA_NumberSymbol(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_NUMBERSYMBOL_H_
diff --git a/xfa/fxfa/parser/cxfa_numbersymbols.cpp b/xfa/fxfa/parser/cxfa_numbersymbols.cpp
index adc7234..9ce3564 100644
--- a/xfa/fxfa/parser/cxfa_numbersymbols.cpp
+++ b/xfa/fxfa/parser/cxfa_numbersymbols.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_numbersymbols.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kNumberSymbolsPropertyData[] = {
-    {XFA_Element::NumberSymbol, 5, 0},
+    {XFA_Element::NumberSymbol, 5, {}},
 };
 
 }  // namespace
@@ -21,11 +21,13 @@
                                        XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::NumberSymbols,
                 kNumberSymbolsPropertyData,
                 {},
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_NumberSymbols::~CXFA_NumberSymbols() = default;
diff --git a/xfa/fxfa/parser/cxfa_numbersymbols.h b/xfa/fxfa/parser/cxfa_numbersymbols.h
index 37bb6fc..092b8f2 100644
--- a/xfa/fxfa/parser/cxfa_numbersymbols.h
+++ b/xfa/fxfa/parser/cxfa_numbersymbols.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_NumberSymbols final : public CXFA_Node {
  public:
-  CXFA_NumberSymbols(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_NumberSymbols() override;
+
+ private:
+  CXFA_NumberSymbols(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_NUMBERSYMBOLS_H_
diff --git a/xfa/fxfa/parser/cxfa_numericedit.cpp b/xfa/fxfa/parser/cxfa_numericedit.cpp
index 641fe2a..d2e6271 100644
--- a/xfa/fxfa/parser/cxfa_numericedit.cpp
+++ b/xfa/fxfa/parser/cxfa_numericedit.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #include "xfa/fxfa/parser/cxfa_numericedit.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kNumericEditPropertyData[] = {
-    {XFA_Element::Margin, 1, 0},
-    {XFA_Element::Border, 1, 0},
-    {XFA_Element::Comb, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Margin, 1, {}},
+    {XFA_Element::Border, 1, {}},
+    {XFA_Element::Comb, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kNumericEditAttributeData[] = {
@@ -31,12 +31,14 @@
 CXFA_NumericEdit::CXFA_NumericEdit(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::NumericEdit,
                 kNumericEditPropertyData,
                 kNumericEditAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_NumericEdit::~CXFA_NumericEdit() = default;
 
diff --git a/xfa/fxfa/parser/cxfa_numericedit.h b/xfa/fxfa/parser/cxfa_numericedit.h
index 145bf08..3095041 100644
--- a/xfa/fxfa/parser/cxfa_numericedit.h
+++ b/xfa/fxfa/parser/cxfa_numericedit.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,11 +11,14 @@
 
 class CXFA_NumericEdit final : public CXFA_Node {
  public:
-  CXFA_NumericEdit(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_NumericEdit() override;
 
   XFA_Element GetValueNodeType() const override;
   XFA_FFWidgetType GetDefaultFFWidgetType() const override;
+
+ private:
+  CXFA_NumericEdit(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_NUMERICEDIT_H_
diff --git a/xfa/fxfa/parser/cxfa_object.cpp b/xfa/fxfa/parser/cxfa_object.cpp
index 6d730bf..308b0df 100644
--- a/xfa/fxfa/parser/cxfa_object.cpp
+++ b/xfa/fxfa/parser/cxfa_object.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,6 @@
 
 #include "xfa/fxfa/parser/cxfa_object.h"
 
-#include <utility>
-
 #include "core/fxcrt/fx_extension.h"
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cfxjse_value.h"
@@ -22,21 +20,19 @@
 CXFA_Object::CXFA_Object(CXFA_Document* pDocument,
                          XFA_ObjectType objectType,
                          XFA_Element elementType,
-                         std::unique_ptr<CJX_Object> jsObject)
-    : m_pDocument(pDocument),
-      m_objectType(objectType),
+                         CJX_Object* jsObject)
+    : m_objectType(objectType),
       m_elementType(elementType),
       m_elementName(XFA_ElementToName(elementType)),
-      m_elementNameHash(FX_HashCode_GetAsIfW(m_elementName, false)),
-      m_pJSObject(std::move(jsObject)) {}
+      m_elementNameHash(FX_HashCode_GetAsIfW(m_elementName)),
+      m_pDocument(pDocument),
+      m_pJSObject(jsObject) {}
 
-CXFA_Object::~CXFA_Object() {
-  if (!GetDocument()->IsBeingDestroyed() && GetDocument()->HasScriptContext())
-    GetDocument()->GetScriptContext()->RemoveJSBindingFromMap(this);
-}
+CXFA_Object::~CXFA_Object() = default;
 
-CXFA_Object* CXFA_Object::AsCXFAObject() {
-  return this;
+void CXFA_Object::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(m_pDocument);
+  visitor->Trace(m_pJSObject);
 }
 
 WideString CXFA_Object::GetSOMExpression() {
diff --git a/xfa/fxfa/parser/cxfa_object.h b/xfa/fxfa/parser/cxfa_object.h
index cfbcb13..c482d3b 100644
--- a/xfa/fxfa/parser/cxfa_object.h
+++ b/xfa/fxfa/parser/cxfa_object.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,17 @@
 #ifndef XFA_FXFA_PARSER_CXFA_OBJECT_H_
 #define XFA_FXFA_PARSER_CXFA_OBJECT_H_
 
-#include <memory>
-
 #include "core/fxcrt/fx_string.h"
+#include "fxjs/gc/heap.h"
 #include "fxjs/xfa/fxjse.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
+namespace cppgc {
+class Visitor;
+}  // namespace cppgc
+
 enum class XFA_ObjectType {
   Object,
   List,
@@ -34,12 +39,11 @@
 class CXFA_ThisProxy;
 class CXFA_TreeList;
 
-class CXFA_Object : public CFXJSE_HostObject {
+class CXFA_Object : public cppgc::GarbageCollected<CXFA_Object> {
  public:
-  ~CXFA_Object() override;
+  virtual ~CXFA_Object();
 
-  // CFXJSE_HostObject:
-  CXFA_Object* AsCXFAObject() override;
+  virtual void Trace(cppgc::Visitor* visitor) const;
 
   CXFA_Document* GetDocument() const { return m_pDocument.Get(); }
   XFA_ObjectType GetObjectType() const { return m_objectType; }
@@ -55,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 {
@@ -73,8 +78,8 @@
   CXFA_TreeList* AsTreeList();
   CXFA_ThisProxy* AsThisProxy();
 
-  CJX_Object* JSObject() { return m_pJSObject.get(); }
-  const CJX_Object* JSObject() const { return m_pJSObject.get(); }
+  CJX_Object* JSObject() { return m_pJSObject; }
+  const CJX_Object* JSObject() const { return m_pJSObject; }
 
   bool HasCreatedUIWidget() const {
     return m_elementType == XFA_Element::Field ||
@@ -93,14 +98,14 @@
   CXFA_Object(CXFA_Document* pDocument,
               XFA_ObjectType objectType,
               XFA_Element eType,
-              std::unique_ptr<CJX_Object> jsObject);
+              CJX_Object* jsObject);
 
-  UnownedPtr<CXFA_Document> const m_pDocument;
   const XFA_ObjectType m_objectType;
   const XFA_Element m_elementType;
   const ByteStringView m_elementName;
   const uint32_t m_elementNameHash;
-  std::unique_ptr<CJX_Object> m_pJSObject;
+  cppgc::WeakMember<CXFA_Document> m_pDocument;
+  cppgc::Member<CJX_Object> m_pJSObject;
 };
 
 // Helper functions that permit nullptr arguments.
diff --git a/xfa/fxfa/parser/cxfa_occur.cpp b/xfa/fxfa/parser/cxfa_occur.cpp
index ee68981..515ffdd 100644
--- a/xfa/fxfa/parser/cxfa_occur.cpp
+++ b/xfa/fxfa/parser/cxfa_occur.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_occur.h"
 
 #include "fxjs/xfa/cjx_occur.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kOccurPropertyData[] = {
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kOccurAttributeData[] = {
@@ -29,32 +29,37 @@
 CXFA_Occur::CXFA_Occur(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Occur,
                 kOccurPropertyData,
                 kOccurAttributeData,
-                pdfium::MakeUnique<CJX_Occur>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Occur>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Occur::~CXFA_Occur() = default;
 
 int32_t CXFA_Occur::GetMax() {
-  Optional<int32_t> max = JSObject()->TryInteger(XFA_Attribute::Max, true);
-  return max ? *max : GetMin();
+  absl::optional<int32_t> max =
+      JSObject()->TryInteger(XFA_Attribute::Max, true);
+  return max.has_value() ? max.value() : GetMin();
 }
 
 int32_t CXFA_Occur::GetMin() {
-  Optional<int32_t> min = JSObject()->TryInteger(XFA_Attribute::Min, true);
-  return min && *min >= 0 ? *min : 1;
+  absl::optional<int32_t> min =
+      JSObject()->TryInteger(XFA_Attribute::Min, true);
+  return min.has_value() && min.value() >= 0 ? min.value() : 1;
 }
 
 std::tuple<int32_t, int32_t, int32_t> CXFA_Occur::GetOccurInfo() {
   int32_t iMin = GetMin();
   int32_t iMax = GetMax();
 
-  Optional<int32_t> init =
+  absl::optional<int32_t> init =
       JSObject()->TryInteger(XFA_Attribute::Initial, false);
-  return {iMin, iMax, init && *init >= iMin ? *init : iMin};
+  return {iMin, iMax,
+          init.has_value() && init.value() >= iMin ? init.value() : iMin};
 }
 
 void CXFA_Occur::SetMax(int32_t iMax) {
diff --git a/xfa/fxfa/parser/cxfa_occur.h b/xfa/fxfa/parser/cxfa_occur.h
index 58029a3..0b0e6e3 100644
--- a/xfa/fxfa/parser/cxfa_occur.h
+++ b/xfa/fxfa/parser/cxfa_occur.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   static constexpr int32_t kDefaultMax = 1;
   static constexpr int32_t kDefaultMin = 1;
 
-  CXFA_Occur(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Occur() override;
 
   int32_t GetMax();
@@ -26,6 +26,9 @@
   void SetMin(int32_t iMin);
 
   std::tuple<int32_t, int32_t, int32_t> GetOccurInfo();
+
+ private:
+  CXFA_Occur(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_OCCUR_H_
diff --git a/xfa/fxfa/parser/cxfa_oid.cpp b/xfa/fxfa/parser/cxfa_oid.cpp
index 9032733..e65e5c0 100644
--- a/xfa/fxfa/parser/cxfa_oid.cpp
+++ b/xfa/fxfa/parser/cxfa_oid.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_oid.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_Oid::CXFA_Oid(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::TextNode,
                 XFA_Element::Oid,
                 {},
                 kOidAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Oid::~CXFA_Oid() = default;
diff --git a/xfa/fxfa/parser/cxfa_oid.h b/xfa/fxfa/parser/cxfa_oid.h
index c9ccb71..5f8e02a 100644
--- a/xfa/fxfa/parser/cxfa_oid.h
+++ b/xfa/fxfa/parser/cxfa_oid.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Oid final : public CXFA_Node {
  public:
-  CXFA_Oid(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Oid() override;
+
+ private:
+  CXFA_Oid(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_OID_H_
diff --git a/xfa/fxfa/parser/cxfa_oids.cpp b/xfa/fxfa/parser/cxfa_oids.cpp
index 64408ca..a9b9c10 100644
--- a/xfa/fxfa/parser/cxfa_oids.cpp
+++ b/xfa/fxfa/parser/cxfa_oids.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_oids.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -24,11 +24,13 @@
 CXFA_Oids::CXFA_Oids(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Oids,
                 {},
                 kOidsAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Oids::~CXFA_Oids() = default;
diff --git a/xfa/fxfa/parser/cxfa_oids.h b/xfa/fxfa/parser/cxfa_oids.h
index f537601..42b461a 100644
--- a/xfa/fxfa/parser/cxfa_oids.h
+++ b/xfa/fxfa/parser/cxfa_oids.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Oids final : public CXFA_Node {
  public:
-  CXFA_Oids(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Oids() override;
+
+ private:
+  CXFA_Oids(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_OIDS_H_
diff --git a/xfa/fxfa/parser/cxfa_openaction.cpp b/xfa/fxfa/parser/cxfa_openaction.cpp
index b6264c2..7490fe0 100644
--- a/xfa/fxfa/parser/cxfa_openaction.cpp
+++ b/xfa/fxfa/parser/cxfa_openaction.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_openaction.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kOpenActionPropertyData[] = {
-    {XFA_Element::Destination, 1, 0},
+    {XFA_Element::Destination, 1, {}},
 };
 
 const CXFA_Node::AttributeData kOpenActionAttributeData[] = {
@@ -25,11 +25,13 @@
 CXFA_OpenAction::CXFA_OpenAction(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::OpenAction,
                 kOpenActionPropertyData,
                 kOpenActionAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_OpenAction::~CXFA_OpenAction() = default;
diff --git a/xfa/fxfa/parser/cxfa_openaction.h b/xfa/fxfa/parser/cxfa_openaction.h
index 2eff67c..ef72da5 100644
--- a/xfa/fxfa/parser/cxfa_openaction.h
+++ b/xfa/fxfa/parser/cxfa_openaction.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_OpenAction final : public CXFA_Node {
  public:
-  CXFA_OpenAction(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_OpenAction() override;
+
+ private:
+  CXFA_OpenAction(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_OPENACTION_H_
diff --git a/xfa/fxfa/parser/cxfa_operation.cpp b/xfa/fxfa/parser/cxfa_operation.cpp
index dbd59ba..14a8b52 100644
--- a/xfa/fxfa/parser/cxfa_operation.cpp
+++ b/xfa/fxfa/parser/cxfa_operation.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_operation.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -25,11 +25,13 @@
 CXFA_Operation::CXFA_Operation(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_ConnectionSet,
+                XFA_XDPPACKET::kConnectionSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::Operation,
                 {},
                 kOperationAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Operation::~CXFA_Operation() = default;
diff --git a/xfa/fxfa/parser/cxfa_operation.h b/xfa/fxfa/parser/cxfa_operation.h
index 4df63aa..b83343c 100644
--- a/xfa/fxfa/parser/cxfa_operation.h
+++ b/xfa/fxfa/parser/cxfa_operation.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Operation final : public CXFA_Node {
  public:
-  CXFA_Operation(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Operation() override;
+
+ private:
+  CXFA_Operation(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_OPERATION_H_
diff --git a/xfa/fxfa/parser/cxfa_output.cpp b/xfa/fxfa/parser/cxfa_output.cpp
index c9e77de..58cf71e 100644
--- a/xfa/fxfa/parser/cxfa_output.cpp
+++ b/xfa/fxfa/parser/cxfa_output.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,14 @@
 #include "xfa/fxfa/parser/cxfa_output.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kOutputPropertyData[] = {
-    {XFA_Element::To, 1, 0},
-    {XFA_Element::Uri, 1, 0},
-    {XFA_Element::Type, 1, 0},
+    {XFA_Element::To, 1, {}},
+    {XFA_Element::Uri, 1, {}},
+    {XFA_Element::Type, 1, {}},
 };
 
 const CXFA_Node::AttributeData kOutputAttributeData[] = {
@@ -27,11 +27,13 @@
 CXFA_Output::CXFA_Output(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Output,
                 kOutputPropertyData,
                 kOutputAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Output::~CXFA_Output() = default;
diff --git a/xfa/fxfa/parser/cxfa_output.h b/xfa/fxfa/parser/cxfa_output.h
index 45555be..3577bb4 100644
--- a/xfa/fxfa/parser/cxfa_output.h
+++ b/xfa/fxfa/parser/cxfa_output.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Output final : public CXFA_Node {
  public:
-  CXFA_Output(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Output() override;
+
+ private:
+  CXFA_Output(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_OUTPUT_H_
diff --git a/xfa/fxfa/parser/cxfa_outputbin.cpp b/xfa/fxfa/parser/cxfa_outputbin.cpp
index 3b9174f..f7d6795 100644
--- a/xfa/fxfa/parser/cxfa_outputbin.cpp
+++ b/xfa/fxfa/parser/cxfa_outputbin.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_outputbin.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_OutputBin::CXFA_OutputBin(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::OutputBin,
                 {},
                 kOutputBinAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_OutputBin::~CXFA_OutputBin() = default;
diff --git a/xfa/fxfa/parser/cxfa_outputbin.h b/xfa/fxfa/parser/cxfa_outputbin.h
index 7ab4540..a053163 100644
--- a/xfa/fxfa/parser/cxfa_outputbin.h
+++ b/xfa/fxfa/parser/cxfa_outputbin.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_OutputBin final : public CXFA_Node {
  public:
-  CXFA_OutputBin(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_OutputBin() override;
+
+ private:
+  CXFA_OutputBin(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_OUTPUTBIN_H_
diff --git a/xfa/fxfa/parser/cxfa_outputxsl.cpp b/xfa/fxfa/parser/cxfa_outputxsl.cpp
index ab733bc..743029d 100644
--- a/xfa/fxfa/parser/cxfa_outputxsl.cpp
+++ b/xfa/fxfa/parser/cxfa_outputxsl.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_outputxsl.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kOutputXSLPropertyData[] = {
-    {XFA_Element::Uri, 1, 0},
+    {XFA_Element::Uri, 1, {}},
 };
 
 const CXFA_Node::AttributeData kOutputXSLAttributeData[] = {
@@ -25,11 +25,13 @@
 CXFA_OutputXSL::CXFA_OutputXSL(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::OutputXSL,
                 kOutputXSLPropertyData,
                 kOutputXSLAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_OutputXSL::~CXFA_OutputXSL() = default;
diff --git a/xfa/fxfa/parser/cxfa_outputxsl.h b/xfa/fxfa/parser/cxfa_outputxsl.h
index 361ea42..3344469 100644
--- a/xfa/fxfa/parser/cxfa_outputxsl.h
+++ b/xfa/fxfa/parser/cxfa_outputxsl.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_OutputXSL final : public CXFA_Node {
  public:
-  CXFA_OutputXSL(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_OutputXSL() override;
+
+ private:
+  CXFA_OutputXSL(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_OUTPUTXSL_H_
diff --git a/xfa/fxfa/parser/cxfa_overflow.cpp b/xfa/fxfa/parser/cxfa_overflow.cpp
index 9ae517b..10d41f7 100644
--- a/xfa/fxfa/parser/cxfa_overflow.cpp
+++ b/xfa/fxfa/parser/cxfa_overflow.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_overflow.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -25,11 +25,13 @@
 CXFA_Overflow::CXFA_Overflow(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Overflow,
                 {},
                 kOverflowAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Overflow::~CXFA_Overflow() = default;
diff --git a/xfa/fxfa/parser/cxfa_overflow.h b/xfa/fxfa/parser/cxfa_overflow.h
index da2717a..ee8ee41 100644
--- a/xfa/fxfa/parser/cxfa_overflow.h
+++ b/xfa/fxfa/parser/cxfa_overflow.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Overflow final : public CXFA_Node {
  public:
-  CXFA_Overflow(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Overflow() override;
+
+ private:
+  CXFA_Overflow(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_OVERFLOW_H_
diff --git a/xfa/fxfa/parser/cxfa_overprint.cpp b/xfa/fxfa/parser/cxfa_overprint.cpp
index ee70ce5..27695c0 100644
--- a/xfa/fxfa/parser/cxfa_overprint.cpp
+++ b/xfa/fxfa/parser/cxfa_overprint.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_overprint.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Overprint::CXFA_Overprint(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Overprint,
                 {},
                 kOverprintAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Overprint::~CXFA_Overprint() = default;
diff --git a/xfa/fxfa/parser/cxfa_overprint.h b/xfa/fxfa/parser/cxfa_overprint.h
index e5e360d..30446a2 100644
--- a/xfa/fxfa/parser/cxfa_overprint.h
+++ b/xfa/fxfa/parser/cxfa_overprint.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Overprint final : public CXFA_Node {
  public:
-  CXFA_Overprint(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Overprint() override;
+
+ private:
+  CXFA_Overprint(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_OVERPRINT_H_
diff --git a/xfa/fxfa/parser/cxfa_packet.cpp b/xfa/fxfa/parser/cxfa_packet.cpp
index 96938ac..c0d0a59 100644
--- a/xfa/fxfa/parser/cxfa_packet.cpp
+++ b/xfa/fxfa/parser/cxfa_packet.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,18 @@
 #include "xfa/fxfa/parser/cxfa_packet.h"
 
 #include "fxjs/xfa/cjx_packet.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 CXFA_Packet::CXFA_Packet(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_XDP,
+                XFA_XDPPACKET::kXdp,
                 XFA_ObjectType::NodeC,
                 XFA_Element::Packet,
                 {},
                 {},
-                pdfium::MakeUnique<CJX_Packet>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Packet>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Packet::~CXFA_Packet() = default;
diff --git a/xfa/fxfa/parser/cxfa_packet.h b/xfa/fxfa/parser/cxfa_packet.h
index c94c74c..42db7a4 100644
--- a/xfa/fxfa/parser/cxfa_packet.h
+++ b/xfa/fxfa/parser/cxfa_packet.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Packet final : public CXFA_Node {
  public:
-  CXFA_Packet(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Packet() override;
+
+ private:
+  CXFA_Packet(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PACKET_H_
diff --git a/xfa/fxfa/parser/cxfa_packets.cpp b/xfa/fxfa/parser/cxfa_packets.cpp
index 7981063..488d97c 100644
--- a/xfa/fxfa/parser/cxfa_packets.cpp
+++ b/xfa/fxfa/parser/cxfa_packets.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_packets.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Packets::CXFA_Packets(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Packets,
                 {},
                 kPacketsAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Packets::~CXFA_Packets() = default;
diff --git a/xfa/fxfa/parser/cxfa_packets.h b/xfa/fxfa/parser/cxfa_packets.h
index 5e0f408..5f5e239 100644
--- a/xfa/fxfa/parser/cxfa_packets.h
+++ b/xfa/fxfa/parser/cxfa_packets.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Packets final : public CXFA_Node {
  public:
-  CXFA_Packets(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Packets() override;
+
+ private:
+  CXFA_Packets(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PACKETS_H_
diff --git a/xfa/fxfa/parser/cxfa_pagearea.cpp b/xfa/fxfa/parser/cxfa_pagearea.cpp
index cc48e89..8593923 100644
--- a/xfa/fxfa/parser/cxfa_pagearea.cpp
+++ b/xfa/fxfa/parser/cxfa_pagearea.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #include "xfa/fxfa/parser/cxfa_pagearea.h"
 
 #include "fxjs/xfa/cjx_container.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kPageAreaPropertyData[] = {
-    {XFA_Element::Medium, 1, 0},
-    {XFA_Element::Desc, 1, 0},
-    {XFA_Element::Extras, 1, 0},
-    {XFA_Element::Occur, 1, 0},
+    {XFA_Element::Medium, 1, {}},
+    {XFA_Element::Desc, 1, {}},
+    {XFA_Element::Extras, 1, {}},
+    {XFA_Element::Occur, 1, {}},
 };
 
 const CXFA_Node::AttributeData kPageAreaAttributeData[] = {
@@ -39,11 +39,13 @@
 CXFA_PageArea::CXFA_PageArea(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::PageArea,
                 kPageAreaPropertyData,
                 kPageAreaAttributeData,
-                pdfium::MakeUnique<CJX_Container>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Container>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_PageArea::~CXFA_PageArea() = default;
diff --git a/xfa/fxfa/parser/cxfa_pagearea.h b/xfa/fxfa/parser/cxfa_pagearea.h
index 8a8afaa..e5dcc1c 100644
--- a/xfa/fxfa/parser/cxfa_pagearea.h
+++ b/xfa/fxfa/parser/cxfa_pagearea.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_PageArea final : public CXFA_Node {
  public:
-  CXFA_PageArea(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_PageArea() override;
+
+ private:
+  CXFA_PageArea(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PAGEAREA_H_
diff --git a/xfa/fxfa/parser/cxfa_pageoffset.cpp b/xfa/fxfa/parser/cxfa_pageoffset.cpp
index 8054b4b..95431b1 100644
--- a/xfa/fxfa/parser/cxfa_pageoffset.cpp
+++ b/xfa/fxfa/parser/cxfa_pageoffset.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_pageoffset.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_PageOffset::CXFA_PageOffset(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::PageOffset,
                 {},
                 kPageOffsetAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_PageOffset::~CXFA_PageOffset() = default;
diff --git a/xfa/fxfa/parser/cxfa_pageoffset.h b/xfa/fxfa/parser/cxfa_pageoffset.h
index d090b11..435a9c0 100644
--- a/xfa/fxfa/parser/cxfa_pageoffset.h
+++ b/xfa/fxfa/parser/cxfa_pageoffset.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_PageOffset final : public CXFA_Node {
  public:
-  CXFA_PageOffset(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_PageOffset() override;
+
+ private:
+  CXFA_PageOffset(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PAGEOFFSET_H_
diff --git a/xfa/fxfa/parser/cxfa_pagerange.cpp b/xfa/fxfa/parser/cxfa_pagerange.cpp
index 307b8dd..9a82b06 100644
--- a/xfa/fxfa/parser/cxfa_pagerange.cpp
+++ b/xfa/fxfa/parser/cxfa_pagerange.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_pagerange.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_PageRange::CXFA_PageRange(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::PageRange,
                 {},
                 kPageRangeAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_PageRange::~CXFA_PageRange() = default;
diff --git a/xfa/fxfa/parser/cxfa_pagerange.h b/xfa/fxfa/parser/cxfa_pagerange.h
index a0317b5..bd1e1c1 100644
--- a/xfa/fxfa/parser/cxfa_pagerange.h
+++ b/xfa/fxfa/parser/cxfa_pagerange.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_PageRange final : public CXFA_Node {
  public:
-  CXFA_PageRange(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_PageRange() override;
+
+ private:
+  CXFA_PageRange(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PAGERANGE_H_
diff --git a/xfa/fxfa/parser/cxfa_pageset.cpp b/xfa/fxfa/parser/cxfa_pageset.cpp
index 1f08645..35daeca 100644
--- a/xfa/fxfa/parser/cxfa_pageset.cpp
+++ b/xfa/fxfa/parser/cxfa_pageset.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_pageset.h"
 
 #include "fxjs/xfa/cjx_container.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kPageSetPropertyData[] = {
-    {XFA_Element::Extras, 1, 0},
-    {XFA_Element::Occur, 1, 0},
+    {XFA_Element::Extras, 1, {}},
+    {XFA_Element::Occur, 1, {}},
 };
 
 const CXFA_Node::AttributeData kPageSetAttributeData[] = {
@@ -33,11 +33,13 @@
 CXFA_PageSet::CXFA_PageSet(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::PageSet,
                 kPageSetPropertyData,
                 kPageSetAttributeData,
-                pdfium::MakeUnique<CJX_Container>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Container>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_PageSet::~CXFA_PageSet() = default;
diff --git a/xfa/fxfa/parser/cxfa_pageset.h b/xfa/fxfa/parser/cxfa_pageset.h
index da71581..e675c05 100644
--- a/xfa/fxfa/parser/cxfa_pageset.h
+++ b/xfa/fxfa/parser/cxfa_pageset.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_PageSet final : public CXFA_Node {
  public:
-  CXFA_PageSet(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_PageSet() override;
+
+ private:
+  CXFA_PageSet(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PAGESET_H_
diff --git a/xfa/fxfa/parser/cxfa_pagination.cpp b/xfa/fxfa/parser/cxfa_pagination.cpp
index 1c393a1..8680561 100644
--- a/xfa/fxfa/parser/cxfa_pagination.cpp
+++ b/xfa/fxfa/parser/cxfa_pagination.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_pagination.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Pagination::CXFA_Pagination(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Pagination,
                 {},
                 kPaginationAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Pagination::~CXFA_Pagination() = default;
diff --git a/xfa/fxfa/parser/cxfa_pagination.h b/xfa/fxfa/parser/cxfa_pagination.h
index 9bbd820..c651118 100644
--- a/xfa/fxfa/parser/cxfa_pagination.h
+++ b/xfa/fxfa/parser/cxfa_pagination.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Pagination final : public CXFA_Node {
  public:
-  CXFA_Pagination(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Pagination() override;
+
+ private:
+  CXFA_Pagination(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PAGINATION_H_
diff --git a/xfa/fxfa/parser/cxfa_paginationoverride.cpp b/xfa/fxfa/parser/cxfa_paginationoverride.cpp
index 0dfe7f2..ef19a39 100644
--- a/xfa/fxfa/parser/cxfa_paginationoverride.cpp
+++ b/xfa/fxfa/parser/cxfa_paginationoverride.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_paginationoverride.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                                  XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::PaginationOverride,
                 {},
                 kPaginationOverrideAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_PaginationOverride::~CXFA_PaginationOverride() = default;
diff --git a/xfa/fxfa/parser/cxfa_paginationoverride.h b/xfa/fxfa/parser/cxfa_paginationoverride.h
index 4f0efba..f42fc9e 100644
--- a/xfa/fxfa/parser/cxfa_paginationoverride.h
+++ b/xfa/fxfa/parser/cxfa_paginationoverride.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_PaginationOverride final : public CXFA_Node {
  public:
-  CXFA_PaginationOverride(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_PaginationOverride() override;
+
+ private:
+  CXFA_PaginationOverride(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PAGINATIONOVERRIDE_H_
diff --git a/xfa/fxfa/parser/cxfa_para.cpp b/xfa/fxfa/parser/cxfa_para.cpp
index 6907baf..0565e99 100644
--- a/xfa/fxfa/parser/cxfa_para.cpp
+++ b/xfa/fxfa/parser/cxfa_para.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_para.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_measurement.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kParaPropertyData[] = {
-    {XFA_Element::Hyphenation, 1, 0},
+    {XFA_Element::Hyphenation, 1, {}},
 };
 
 const CXFA_Node::AttributeData kParaAttributeData[] = {
@@ -43,12 +43,14 @@
 CXFA_Para::CXFA_Para(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Para,
                 kParaPropertyData,
                 kParaAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Para::~CXFA_Para() = default;
 
diff --git a/xfa/fxfa/parser/cxfa_para.h b/xfa/fxfa/parser/cxfa_para.h
index 735a199..98effb0 100644
--- a/xfa/fxfa/parser/cxfa_para.h
+++ b/xfa/fxfa/parser/cxfa_para.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,7 @@
 
 class CXFA_Para final : public CXFA_Node {
  public:
-  CXFA_Para(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Para() override;
 
   XFA_AttributeValue GetHorizontalAlign();
@@ -22,6 +22,9 @@
   float GetSpaceAbove();
   float GetSpaceBelow();
   float GetTextIndent();
+
+ private:
+  CXFA_Para(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PARA_H_
diff --git a/xfa/fxfa/parser/cxfa_part.cpp b/xfa/fxfa/parser/cxfa_part.cpp
index d2acf53..220ad62 100644
--- a/xfa/fxfa/parser/cxfa_part.cpp
+++ b/xfa/fxfa/parser/cxfa_part.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_part.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Part::CXFA_Part(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Part,
                 {},
                 kPartAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Part::~CXFA_Part() = default;
diff --git a/xfa/fxfa/parser/cxfa_part.h b/xfa/fxfa/parser/cxfa_part.h
index 99c2c59..1985f6a 100644
--- a/xfa/fxfa/parser/cxfa_part.h
+++ b/xfa/fxfa/parser/cxfa_part.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Part final : public CXFA_Node {
  public:
-  CXFA_Part(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Part() override;
+
+ private:
+  CXFA_Part(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PART_H_
diff --git a/xfa/fxfa/parser/cxfa_password.cpp b/xfa/fxfa/parser/cxfa_password.cpp
index 057b818..cb389e3 100644
--- a/xfa/fxfa/parser/cxfa_password.cpp
+++ b/xfa/fxfa/parser/cxfa_password.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_password.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_Password::CXFA_Password(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_SourceSet,
+                XFA_XDPPACKET::kSourceSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::Password,
                 {},
                 kPasswordAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Password::~CXFA_Password() = default;
diff --git a/xfa/fxfa/parser/cxfa_password.h b/xfa/fxfa/parser/cxfa_password.h
index a2cbca7..c06af1e 100644
--- a/xfa/fxfa/parser/cxfa_password.h
+++ b/xfa/fxfa/parser/cxfa_password.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Password final : public CXFA_Node {
  public:
-  CXFA_Password(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Password() override;
+
+ private:
+  CXFA_Password(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PASSWORD_H_
diff --git a/xfa/fxfa/parser/cxfa_passwordedit.cpp b/xfa/fxfa/parser/cxfa_passwordedit.cpp
index f8f3c5e..11d5621 100644
--- a/xfa/fxfa/parser/cxfa_passwordedit.cpp
+++ b/xfa/fxfa/parser/cxfa_passwordedit.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,14 @@
 #include "xfa/fxfa/parser/cxfa_passwordedit.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kPasswordEditPropertyData[] = {
-    {XFA_Element::Margin, 1, 0},
-    {XFA_Element::Border, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Margin, 1, {}},
+    {XFA_Element::Border, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kPasswordEditAttributeData[] = {
@@ -28,15 +28,24 @@
 
 }  // namespace
 
+// static
+CXFA_PasswordEdit* CXFA_PasswordEdit::FromNode(CXFA_Node* pNode) {
+  return pNode && pNode->GetElementType() == XFA_Element::PasswordEdit
+             ? static_cast<CXFA_PasswordEdit*>(pNode)
+             : nullptr;
+}
+
 CXFA_PasswordEdit::CXFA_PasswordEdit(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::PasswordEdit,
                 kPasswordEditPropertyData,
                 kPasswordEditAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_PasswordEdit::~CXFA_PasswordEdit() = default;
 
diff --git a/xfa/fxfa/parser/cxfa_passwordedit.h b/xfa/fxfa/parser/cxfa_passwordedit.h
index fae812f..7f4b920 100644
--- a/xfa/fxfa/parser/cxfa_passwordedit.h
+++ b/xfa/fxfa/parser/cxfa_passwordedit.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,11 +11,16 @@
 
 class CXFA_PasswordEdit final : public CXFA_Node {
  public:
-  CXFA_PasswordEdit(CXFA_Document* doc, XFA_PacketType packet);
+  static CXFA_PasswordEdit* FromNode(CXFA_Node* pNode);
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_PasswordEdit() override;
 
   XFA_FFWidgetType GetDefaultFFWidgetType() const override;
   WideString GetPasswordChar();
+
+ private:
+  CXFA_PasswordEdit(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PASSWORDEDIT_H_
diff --git a/xfa/fxfa/parser/cxfa_pattern.cpp b/xfa/fxfa/parser/cxfa_pattern.cpp
index e6f38aa..5e5c15c 100644
--- a/xfa/fxfa/parser/cxfa_pattern.cpp
+++ b/xfa/fxfa/parser/cxfa_pattern.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,17 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_pattern.h"
 
-#include "core/fxge/render_defines.h"
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+#include "xfa/fgas/graphics/cfgas_gepattern.h"
 #include "xfa/fxfa/parser/cxfa_color.h"
-#include "xfa/fxgraphics/cxfa_gepattern.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kPatternPropertyData[] = {
-    {XFA_Element::Color, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Color, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kPatternAttributeData[] = {
@@ -32,12 +32,14 @@
 CXFA_Pattern::CXFA_Pattern(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Pattern,
                 kPatternPropertyData,
                 kPatternAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Pattern::~CXFA_Pattern() = default;
 
@@ -49,39 +51,36 @@
   return JSObject()->GetEnum(XFA_Attribute::Type);
 }
 
-void CXFA_Pattern::Draw(CXFA_Graphics* pGS,
-                        CXFA_GEPath* fillPath,
+void CXFA_Pattern::Draw(CFGAS_GEGraphics* pGS,
+                        const CFGAS_GEPath& fillPath,
                         FX_ARGB crStart,
                         const CFX_RectF& rtFill,
                         const CFX_Matrix& matrix) {
   CXFA_Color* pColor = GetColorIfExists();
   FX_ARGB crEnd = pColor ? pColor->GetValue() : CXFA_Color::kBlackColor;
-
-  FX_HatchStyle iHatch = FX_HatchStyle::Cross;
+  CFGAS_GEPattern::HatchStyle iHatch = CFGAS_GEPattern::HatchStyle::Cross;
   switch (GetType()) {
     case XFA_AttributeValue::CrossDiagonal:
-      iHatch = FX_HatchStyle::DiagonalCross;
+      iHatch = CFGAS_GEPattern::HatchStyle::DiagonalCross;
       break;
     case XFA_AttributeValue::DiagonalLeft:
-      iHatch = FX_HatchStyle::ForwardDiagonal;
+      iHatch = CFGAS_GEPattern::HatchStyle::ForwardDiagonal;
       break;
     case XFA_AttributeValue::DiagonalRight:
-      iHatch = FX_HatchStyle::BackwardDiagonal;
+      iHatch = CFGAS_GEPattern::HatchStyle::BackwardDiagonal;
       break;
     case XFA_AttributeValue::Horizontal:
-      iHatch = FX_HatchStyle::Horizontal;
+      iHatch = CFGAS_GEPattern::HatchStyle::Horizontal;
       break;
     case XFA_AttributeValue::Vertical:
-      iHatch = FX_HatchStyle::Vertical;
+      iHatch = CFGAS_GEPattern::HatchStyle::Vertical;
       break;
     default:
       break;
   }
 
-  CXFA_GEPattern pattern(iHatch, crEnd, crStart);
-
-  pGS->SaveGraphState();
-  pGS->SetFillColor(CXFA_GEColor(&pattern, 0x0));
-  pGS->FillPath(fillPath, FXFILL_WINDING, &matrix);
-  pGS->RestoreGraphState();
+  CFGAS_GEPattern pattern(iHatch, crEnd, crStart);
+  CFGAS_GEGraphics::StateRestorer restorer(pGS);
+  pGS->SetFillColor(CFGAS_GEColor(&pattern, 0x0));
+  pGS->FillPath(fillPath, CFX_FillRenderOptions::FillType::kWinding, matrix);
 }
diff --git a/xfa/fxfa/parser/cxfa_pattern.h b/xfa/fxfa/parser/cxfa_pattern.h
index bf5c526..010ab87 100644
--- a/xfa/fxfa/parser/cxfa_pattern.h
+++ b/xfa/fxfa/parser/cxfa_pattern.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,27 +8,29 @@
 #define XFA_FXFA_PARSER_CXFA_PATTERN_H_
 
 #include "core/fxcrt/fx_coordinates.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
 
+class CFGAS_GEGraphics;
 class CXFA_Color;
-class CXFA_Graphics;
 
 class CXFA_Pattern final : public CXFA_Node {
  public:
   static constexpr XFA_AttributeValue kDefaultType =
       XFA_AttributeValue::Unknown;
 
-  CXFA_Pattern(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Pattern() override;
 
-  void Draw(CXFA_Graphics* pGS,
-            CXFA_GEPath* fillPath,
+  void Draw(CFGAS_GEGraphics* pGS,
+            const CFGAS_GEPath& fillPath,
             FX_ARGB crStart,
             const CFX_RectF& rtFill,
             const CFX_Matrix& matrix);
 
  private:
+  CXFA_Pattern(CXFA_Document* doc, XFA_PacketType packet);
+
   XFA_AttributeValue GetType();
   CXFA_Color* GetColorIfExists();
 };
diff --git a/xfa/fxfa/parser/cxfa_pcl.cpp b/xfa/fxfa/parser/cxfa_pcl.cpp
index e55c80d..57b5ade 100644
--- a/xfa/fxfa/parser/cxfa_pcl.cpp
+++ b/xfa/fxfa/parser/cxfa_pcl.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #include "xfa/fxfa/parser/cxfa_pcl.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kPclPropertyData[] = {
-    {XFA_Element::FontInfo, 1, 0},   {XFA_Element::Jog, 1, 0},
-    {XFA_Element::Xdc, 1, 0},        {XFA_Element::BatchOutput, 1, 0},
-    {XFA_Element::PageOffset, 1, 0}, {XFA_Element::OutputBin, 1, 0},
-    {XFA_Element::Staple, 1, 0},     {XFA_Element::MediumInfo, 1, 0},
+    {XFA_Element::FontInfo, 1, {}},   {XFA_Element::Jog, 1, {}},
+    {XFA_Element::Xdc, 1, {}},        {XFA_Element::BatchOutput, 1, {}},
+    {XFA_Element::PageOffset, 1, {}}, {XFA_Element::OutputBin, 1, {}},
+    {XFA_Element::Staple, 1, {}},     {XFA_Element::MediumInfo, 1, {}},
 };
 
 const CXFA_Node::AttributeData kPclAttributeData[] = {
@@ -29,11 +29,13 @@
 CXFA_Pcl::CXFA_Pcl(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Pcl,
                 kPclPropertyData,
                 kPclAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Pcl::~CXFA_Pcl() = default;
diff --git a/xfa/fxfa/parser/cxfa_pcl.h b/xfa/fxfa/parser/cxfa_pcl.h
index c9d94d6..c0f3809 100644
--- a/xfa/fxfa/parser/cxfa_pcl.h
+++ b/xfa/fxfa/parser/cxfa_pcl.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Pcl final : public CXFA_Node {
  public:
-  CXFA_Pcl(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Pcl() override;
+
+ private:
+  CXFA_Pcl(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PCL_H_
diff --git a/xfa/fxfa/parser/cxfa_pdf.cpp b/xfa/fxfa/parser/cxfa_pdf.cpp
index 499893c..9a7e006 100644
--- a/xfa/fxfa/parser/cxfa_pdf.cpp
+++ b/xfa/fxfa/parser/cxfa_pdf.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,30 +7,30 @@
 #include "xfa/fxfa/parser/cxfa_pdf.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kPdfPropertyData[] = {
-    {XFA_Element::AdobeExtensionLevel, 1, 0},
-    {XFA_Element::FontInfo, 1, 0},
-    {XFA_Element::Xdc, 1, 0},
-    {XFA_Element::Pdfa, 1, 0},
-    {XFA_Element::BatchOutput, 1, 0},
-    {XFA_Element::ViewerPreferences, 1, 0},
-    {XFA_Element::ScriptModel, 1, 0},
-    {XFA_Element::Version, 1, 0},
-    {XFA_Element::SubmitFormat, 1, 0},
-    {XFA_Element::SilentPrint, 1, 0},
-    {XFA_Element::Producer, 1, 0},
-    {XFA_Element::Compression, 1, 0},
-    {XFA_Element::Interactive, 1, 0},
-    {XFA_Element::Encryption, 1, 0},
-    {XFA_Element::RenderPolicy, 1, 0},
-    {XFA_Element::OpenAction, 1, 0},
-    {XFA_Element::Creator, 1, 0},
-    {XFA_Element::Linearized, 1, 0},
-    {XFA_Element::Tagged, 1, 0},
+    {XFA_Element::AdobeExtensionLevel, 1, {}},
+    {XFA_Element::FontInfo, 1, {}},
+    {XFA_Element::Xdc, 1, {}},
+    {XFA_Element::Pdfa, 1, {}},
+    {XFA_Element::BatchOutput, 1, {}},
+    {XFA_Element::ViewerPreferences, 1, {}},
+    {XFA_Element::ScriptModel, 1, {}},
+    {XFA_Element::Version, 1, {}},
+    {XFA_Element::SubmitFormat, 1, {}},
+    {XFA_Element::SilentPrint, 1, {}},
+    {XFA_Element::Producer, 1, {}},
+    {XFA_Element::Compression, 1, {}},
+    {XFA_Element::Interactive, 1, {}},
+    {XFA_Element::Encryption, 1, {}},
+    {XFA_Element::RenderPolicy, 1, {}},
+    {XFA_Element::OpenAction, 1, {}},
+    {XFA_Element::Creator, 1, {}},
+    {XFA_Element::Linearized, 1, {}},
+    {XFA_Element::Tagged, 1, {}},
 };
 
 const CXFA_Node::AttributeData kPdfAttributeData[] = {
@@ -44,11 +44,13 @@
 CXFA_Pdf::CXFA_Pdf(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Pdf,
                 kPdfPropertyData,
                 kPdfAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Pdf::~CXFA_Pdf() = default;
diff --git a/xfa/fxfa/parser/cxfa_pdf.h b/xfa/fxfa/parser/cxfa_pdf.h
index 00e4d38..267847e 100644
--- a/xfa/fxfa/parser/cxfa_pdf.h
+++ b/xfa/fxfa/parser/cxfa_pdf.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Pdf final : public CXFA_Node {
  public:
-  CXFA_Pdf(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Pdf() override;
+
+ private:
+  CXFA_Pdf(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PDF_H_
diff --git a/xfa/fxfa/parser/cxfa_pdfa.cpp b/xfa/fxfa/parser/cxfa_pdfa.cpp
index 77d723f..0cc3ca1 100644
--- a/xfa/fxfa/parser/cxfa_pdfa.cpp
+++ b/xfa/fxfa/parser/cxfa_pdfa.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #include "xfa/fxfa/parser/cxfa_pdfa.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kPdfaPropertyData[] = {
-    {XFA_Element::Amd, 1, 0},
-    {XFA_Element::Part, 1, 0},
-    {XFA_Element::IncludeXDPContent, 1, 0},
-    {XFA_Element::Conformance, 1, 0},
+    {XFA_Element::Amd, 1, {}},
+    {XFA_Element::Part, 1, {}},
+    {XFA_Element::IncludeXDPContent, 1, {}},
+    {XFA_Element::Conformance, 1, {}},
 };
 
 const CXFA_Node::AttributeData kPdfaAttributeData[] = {
@@ -28,11 +28,13 @@
 CXFA_Pdfa::CXFA_Pdfa(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Pdfa,
                 kPdfaPropertyData,
                 kPdfaAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Pdfa::~CXFA_Pdfa() = default;
diff --git a/xfa/fxfa/parser/cxfa_pdfa.h b/xfa/fxfa/parser/cxfa_pdfa.h
index e43f025..22ce68c 100644
--- a/xfa/fxfa/parser/cxfa_pdfa.h
+++ b/xfa/fxfa/parser/cxfa_pdfa.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Pdfa final : public CXFA_Node {
  public:
-  CXFA_Pdfa(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Pdfa() override;
+
+ private:
+  CXFA_Pdfa(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PDFA_H_
diff --git a/xfa/fxfa/parser/cxfa_permissions.cpp b/xfa/fxfa/parser/cxfa_permissions.cpp
index 06e3a08..f09cc14 100644
--- a/xfa/fxfa/parser/cxfa_permissions.cpp
+++ b/xfa/fxfa/parser/cxfa_permissions.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,20 +7,20 @@
 #include "xfa/fxfa/parser/cxfa_permissions.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kPermissionsPropertyData[] = {
-    {XFA_Element::ModifyAnnots, 1, 0},
-    {XFA_Element::ContentCopy, 1, 0},
-    {XFA_Element::FormFieldFilling, 1, 0},
-    {XFA_Element::Change, 1, 0},
-    {XFA_Element::AccessibleContent, 1, 0},
-    {XFA_Element::Print, 1, 0},
-    {XFA_Element::PlaintextMetadata, 1, 0},
-    {XFA_Element::PrintHighQuality, 1, 0},
-    {XFA_Element::DocumentAssembly, 1, 0},
+    {XFA_Element::ModifyAnnots, 1, {}},
+    {XFA_Element::ContentCopy, 1, {}},
+    {XFA_Element::FormFieldFilling, 1, {}},
+    {XFA_Element::Change, 1, {}},
+    {XFA_Element::AccessibleContent, 1, {}},
+    {XFA_Element::Print, 1, {}},
+    {XFA_Element::PlaintextMetadata, 1, {}},
+    {XFA_Element::PrintHighQuality, 1, {}},
+    {XFA_Element::DocumentAssembly, 1, {}},
 };
 
 const CXFA_Node::AttributeData kPermissionsAttributeData[] = {
@@ -33,11 +33,13 @@
 CXFA_Permissions::CXFA_Permissions(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Permissions,
                 kPermissionsPropertyData,
                 kPermissionsAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Permissions::~CXFA_Permissions() = default;
diff --git a/xfa/fxfa/parser/cxfa_permissions.h b/xfa/fxfa/parser/cxfa_permissions.h
index 79b3bf1..bed60f4 100644
--- a/xfa/fxfa/parser/cxfa_permissions.h
+++ b/xfa/fxfa/parser/cxfa_permissions.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Permissions final : public CXFA_Node {
  public:
-  CXFA_Permissions(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Permissions() override;
+
+ private:
+  CXFA_Permissions(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PERMISSIONS_H_
diff --git a/xfa/fxfa/parser/cxfa_picktraybypdfsize.cpp b/xfa/fxfa/parser/cxfa_picktraybypdfsize.cpp
index 3b2b7a3..2b48575 100644
--- a/xfa/fxfa/parser/cxfa_picktraybypdfsize.cpp
+++ b/xfa/fxfa/parser/cxfa_picktraybypdfsize.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_picktraybypdfsize.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                                XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::PickTrayByPDFSize,
                 {},
                 kPickTrayByPDFSizeAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_PickTrayByPDFSize::~CXFA_PickTrayByPDFSize() = default;
diff --git a/xfa/fxfa/parser/cxfa_picktraybypdfsize.h b/xfa/fxfa/parser/cxfa_picktraybypdfsize.h
index 3871552..e0f4e3c 100644
--- a/xfa/fxfa/parser/cxfa_picktraybypdfsize.h
+++ b/xfa/fxfa/parser/cxfa_picktraybypdfsize.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_PickTrayByPDFSize final : public CXFA_Node {
  public:
-  CXFA_PickTrayByPDFSize(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_PickTrayByPDFSize() override;
+
+ private:
+  CXFA_PickTrayByPDFSize(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PICKTRAYBYPDFSIZE_H_
diff --git a/xfa/fxfa/parser/cxfa_picture.cpp b/xfa/fxfa/parser/cxfa_picture.cpp
index a1005ec..e003d1f 100644
--- a/xfa/fxfa/parser/cxfa_picture.cpp
+++ b/xfa/fxfa/parser/cxfa_picture.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_picture.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,14 +22,16 @@
 }  // namespace
 
 CXFA_Picture::CXFA_Picture(CXFA_Document* doc, XFA_PacketType packet)
-    : CXFA_Node(
-          doc,
-          packet,
-          (XFA_XDPPACKET_Config | XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
-          XFA_ObjectType::ContentNode,
-          XFA_Element::Picture,
-          {},
-          kPictureAttributeData,
-          pdfium::MakeUnique<CJX_Node>(this)) {}
+    : CXFA_Node(doc,
+                packet,
+                {XFA_XDPPACKET::kConfig, XFA_XDPPACKET::kTemplate,
+                 XFA_XDPPACKET::kForm},
+                XFA_ObjectType::ContentNode,
+                XFA_Element::Picture,
+                {},
+                kPictureAttributeData,
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Picture::~CXFA_Picture() = default;
diff --git a/xfa/fxfa/parser/cxfa_picture.h b/xfa/fxfa/parser/cxfa_picture.h
index 304c7b9..52bbe9e 100644
--- a/xfa/fxfa/parser/cxfa_picture.h
+++ b/xfa/fxfa/parser/cxfa_picture.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Picture final : public CXFA_Node {
  public:
-  CXFA_Picture(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Picture() override;
+
+ private:
+  CXFA_Picture(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PICTURE_H_
diff --git a/xfa/fxfa/parser/cxfa_plaintextmetadata.cpp b/xfa/fxfa/parser/cxfa_plaintextmetadata.cpp
index 75abb78..90265e5 100644
--- a/xfa/fxfa/parser/cxfa_plaintextmetadata.cpp
+++ b/xfa/fxfa/parser/cxfa_plaintextmetadata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_plaintextmetadata.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                                XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::PlaintextMetadata,
                 {},
                 kPlaintextMetadataAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_PlaintextMetadata::~CXFA_PlaintextMetadata() = default;
diff --git a/xfa/fxfa/parser/cxfa_plaintextmetadata.h b/xfa/fxfa/parser/cxfa_plaintextmetadata.h
index 65e0ded..531de0f 100644
--- a/xfa/fxfa/parser/cxfa_plaintextmetadata.h
+++ b/xfa/fxfa/parser/cxfa_plaintextmetadata.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_PlaintextMetadata final : public CXFA_Node {
  public:
-  CXFA_PlaintextMetadata(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_PlaintextMetadata() override;
+
+ private:
+  CXFA_PlaintextMetadata(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PLAINTEXTMETADATA_H_
diff --git a/xfa/fxfa/parser/cxfa_presence.cpp b/xfa/fxfa/parser/cxfa_presence.cpp
index 99353a6..25dbcd4 100644
--- a/xfa/fxfa/parser/cxfa_presence.cpp
+++ b/xfa/fxfa/parser/cxfa_presence.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_presence.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Presence::CXFA_Presence(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Presence,
                 {},
                 kPresenceAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Presence::~CXFA_Presence() = default;
diff --git a/xfa/fxfa/parser/cxfa_presence.h b/xfa/fxfa/parser/cxfa_presence.h
index 1bd6e45..da683e6 100644
--- a/xfa/fxfa/parser/cxfa_presence.h
+++ b/xfa/fxfa/parser/cxfa_presence.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Presence final : public CXFA_Node {
  public:
-  CXFA_Presence(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Presence() override;
+
+ private:
+  CXFA_Presence(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PRESENCE_H_
diff --git a/xfa/fxfa/parser/cxfa_present.cpp b/xfa/fxfa/parser/cxfa_present.cpp
index e645f55..6b54740 100644
--- a/xfa/fxfa/parser/cxfa_present.cpp
+++ b/xfa/fxfa/parser/cxfa_present.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,25 +7,25 @@
 #include "xfa/fxfa/parser/cxfa_present.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kPresentPropertyData[] = {
-    {XFA_Element::Xdp, 1, 0},
-    {XFA_Element::Cache, 1, 0},
-    {XFA_Element::Pagination, 1, 0},
-    {XFA_Element::Overprint, 1, 0},
-    {XFA_Element::BehaviorOverride, 1, 0},
-    {XFA_Element::Copies, 1, 0},
-    {XFA_Element::Output, 1, 0},
-    {XFA_Element::Validate, 1, 0},
-    {XFA_Element::Layout, 1, 0},
-    {XFA_Element::Script, 1, 0},
-    {XFA_Element::Common, 1, 0},
-    {XFA_Element::PaginationOverride, 1, 0},
-    {XFA_Element::Destination, 1, 0},
-    {XFA_Element::IncrementalMerge, 1, 0},
+    {XFA_Element::Xdp, 1, {}},
+    {XFA_Element::Cache, 1, {}},
+    {XFA_Element::Pagination, 1, {}},
+    {XFA_Element::Overprint, 1, {}},
+    {XFA_Element::BehaviorOverride, 1, {}},
+    {XFA_Element::Copies, 1, {}},
+    {XFA_Element::Output, 1, {}},
+    {XFA_Element::Validate, 1, {}},
+    {XFA_Element::Layout, 1, {}},
+    {XFA_Element::Script, 1, {}},
+    {XFA_Element::Common, 1, {}},
+    {XFA_Element::PaginationOverride, 1, {}},
+    {XFA_Element::Destination, 1, {}},
+    {XFA_Element::IncrementalMerge, 1, {}},
 };
 
 const CXFA_Node::AttributeData kPresentAttributeData[] = {
@@ -38,11 +38,13 @@
 CXFA_Present::CXFA_Present(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Present,
                 kPresentPropertyData,
                 kPresentAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Present::~CXFA_Present() = default;
diff --git a/xfa/fxfa/parser/cxfa_present.h b/xfa/fxfa/parser/cxfa_present.h
index 36db503..6fbddfe 100644
--- a/xfa/fxfa/parser/cxfa_present.h
+++ b/xfa/fxfa/parser/cxfa_present.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Present final : public CXFA_Node {
  public:
-  CXFA_Present(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Present() override;
+
+ private:
+  CXFA_Present(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PRESENT_H_
diff --git a/xfa/fxfa/parser/cxfa_print.cpp b/xfa/fxfa/parser/cxfa_print.cpp
index 227af1e..381e1bf 100644
--- a/xfa/fxfa/parser/cxfa_print.cpp
+++ b/xfa/fxfa/parser/cxfa_print.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_print.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Print::CXFA_Print(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Print,
                 {},
                 kPrintAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Print::~CXFA_Print() = default;
diff --git a/xfa/fxfa/parser/cxfa_print.h b/xfa/fxfa/parser/cxfa_print.h
index 7f6fff9..cc785eb 100644
--- a/xfa/fxfa/parser/cxfa_print.h
+++ b/xfa/fxfa/parser/cxfa_print.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Print final : public CXFA_Node {
  public:
-  CXFA_Print(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Print() override;
+
+ private:
+  CXFA_Print(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PRINT_H_
diff --git a/xfa/fxfa/parser/cxfa_printername.cpp b/xfa/fxfa/parser/cxfa_printername.cpp
index b434d26..d4bbe0a 100644
--- a/xfa/fxfa/parser/cxfa_printername.cpp
+++ b/xfa/fxfa/parser/cxfa_printername.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_printername.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_PrinterName::CXFA_PrinterName(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::PrinterName,
                 {},
                 kPrinterNameAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_PrinterName::~CXFA_PrinterName() = default;
diff --git a/xfa/fxfa/parser/cxfa_printername.h b/xfa/fxfa/parser/cxfa_printername.h
index a85d61e..ba4fb4f 100644
--- a/xfa/fxfa/parser/cxfa_printername.h
+++ b/xfa/fxfa/parser/cxfa_printername.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_PrinterName final : public CXFA_Node {
  public:
-  CXFA_PrinterName(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_PrinterName() override;
+
+ private:
+  CXFA_PrinterName(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PRINTERNAME_H_
diff --git a/xfa/fxfa/parser/cxfa_printhighquality.cpp b/xfa/fxfa/parser/cxfa_printhighquality.cpp
index 38d16f5..0669901 100644
--- a/xfa/fxfa/parser/cxfa_printhighquality.cpp
+++ b/xfa/fxfa/parser/cxfa_printhighquality.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_printhighquality.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                              XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::PrintHighQuality,
                 {},
                 kPrintHighQualityAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_PrintHighQuality::~CXFA_PrintHighQuality() = default;
diff --git a/xfa/fxfa/parser/cxfa_printhighquality.h b/xfa/fxfa/parser/cxfa_printhighquality.h
index 6268489..b7040a1 100644
--- a/xfa/fxfa/parser/cxfa_printhighquality.h
+++ b/xfa/fxfa/parser/cxfa_printhighquality.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_PrintHighQuality final : public CXFA_Node {
  public:
-  CXFA_PrintHighQuality(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_PrintHighQuality() override;
+
+ private:
+  CXFA_PrintHighQuality(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PRINTHIGHQUALITY_H_
diff --git a/xfa/fxfa/parser/cxfa_printscaling.cpp b/xfa/fxfa/parser/cxfa_printscaling.cpp
index a6641a3..9ecbb8d 100644
--- a/xfa/fxfa/parser/cxfa_printscaling.cpp
+++ b/xfa/fxfa/parser/cxfa_printscaling.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_printscaling.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_PrintScaling::CXFA_PrintScaling(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::PrintScaling,
                 {},
                 kPrintScalingAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_PrintScaling::~CXFA_PrintScaling() = default;
diff --git a/xfa/fxfa/parser/cxfa_printscaling.h b/xfa/fxfa/parser/cxfa_printscaling.h
index 76bf112..7eddb99 100644
--- a/xfa/fxfa/parser/cxfa_printscaling.h
+++ b/xfa/fxfa/parser/cxfa_printscaling.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_PrintScaling final : public CXFA_Node {
  public:
-  CXFA_PrintScaling(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_PrintScaling() override;
+
+ private:
+  CXFA_PrintScaling(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PRINTSCALING_H_
diff --git a/xfa/fxfa/parser/cxfa_producer.cpp b/xfa/fxfa/parser/cxfa_producer.cpp
index 07ed6e5..5ce2515 100644
--- a/xfa/fxfa/parser/cxfa_producer.cpp
+++ b/xfa/fxfa/parser/cxfa_producer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_producer.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Producer::CXFA_Producer(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Producer,
                 {},
                 kProducerAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Producer::~CXFA_Producer() = default;
diff --git a/xfa/fxfa/parser/cxfa_producer.h b/xfa/fxfa/parser/cxfa_producer.h
index c40a334..ecc3b97 100644
--- a/xfa/fxfa/parser/cxfa_producer.h
+++ b/xfa/fxfa/parser/cxfa_producer.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Producer final : public CXFA_Node {
  public:
-  CXFA_Producer(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Producer() override;
+
+ private:
+  CXFA_Producer(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PRODUCER_H_
diff --git a/xfa/fxfa/parser/cxfa_proto.cpp b/xfa/fxfa/parser/cxfa_proto.cpp
index c59ae13..5749243 100644
--- a/xfa/fxfa/parser/cxfa_proto.cpp
+++ b/xfa/fxfa/parser/cxfa_proto.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,18 @@
 #include "xfa/fxfa/parser/cxfa_proto.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 CXFA_Proto::CXFA_Proto(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Proto,
                 {},
                 {},
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Proto::~CXFA_Proto() = default;
diff --git a/xfa/fxfa/parser/cxfa_proto.h b/xfa/fxfa/parser/cxfa_proto.h
index 5af7ba2..4407f23 100644
--- a/xfa/fxfa/parser/cxfa_proto.h
+++ b/xfa/fxfa/parser/cxfa_proto.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Proto final : public CXFA_Node {
  public:
-  CXFA_Proto(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Proto() override;
+
+ private:
+  CXFA_Proto(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PROTO_H_
diff --git a/xfa/fxfa/parser/cxfa_ps.cpp b/xfa/fxfa/parser/cxfa_ps.cpp
index 35e5da3..68c4607 100644
--- a/xfa/fxfa/parser/cxfa_ps.cpp
+++ b/xfa/fxfa/parser/cxfa_ps.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #include "xfa/fxfa/parser/cxfa_ps.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kPsPropertyData[] = {
-    {XFA_Element::FontInfo, 1, 0},  {XFA_Element::Jog, 1, 0},
-    {XFA_Element::Xdc, 1, 0},       {XFA_Element::BatchOutput, 1, 0},
-    {XFA_Element::OutputBin, 1, 0}, {XFA_Element::Compress, 1, 0},
-    {XFA_Element::Staple, 1, 0},    {XFA_Element::MediumInfo, 1, 0},
+    {XFA_Element::FontInfo, 1, {}},  {XFA_Element::Jog, 1, {}},
+    {XFA_Element::Xdc, 1, {}},       {XFA_Element::BatchOutput, 1, {}},
+    {XFA_Element::OutputBin, 1, {}}, {XFA_Element::Compress, 1, {}},
+    {XFA_Element::Staple, 1, {}},    {XFA_Element::MediumInfo, 1, {}},
 };
 
 const CXFA_Node::AttributeData kPsAttributeData[] = {
@@ -29,11 +29,13 @@
 CXFA_Ps::CXFA_Ps(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Ps,
                 kPsPropertyData,
                 kPsAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Ps::~CXFA_Ps() = default;
diff --git a/xfa/fxfa/parser/cxfa_ps.h b/xfa/fxfa/parser/cxfa_ps.h
index f51c510..afb8923 100644
--- a/xfa/fxfa/parser/cxfa_ps.h
+++ b/xfa/fxfa/parser/cxfa_ps.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Ps final : public CXFA_Node {
  public:
-  CXFA_Ps(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Ps() override;
+
+ private:
+  CXFA_Ps(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PS_H_
diff --git a/xfa/fxfa/parser/cxfa_psmap.cpp b/xfa/fxfa/parser/cxfa_psmap.cpp
index 12ca00b..496f03d 100644
--- a/xfa/fxfa/parser/cxfa_psmap.cpp
+++ b/xfa/fxfa/parser/cxfa_psmap.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,18 @@
 #include "xfa/fxfa/parser/cxfa_psmap.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 CXFA_PsMap::CXFA_PsMap(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::PsMap,
                 {},
                 {},
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_PsMap::~CXFA_PsMap() = default;
diff --git a/xfa/fxfa/parser/cxfa_psmap.h b/xfa/fxfa/parser/cxfa_psmap.h
index 802e123..4e6f935 100644
--- a/xfa/fxfa/parser/cxfa_psmap.h
+++ b/xfa/fxfa/parser/cxfa_psmap.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_PsMap final : public CXFA_Node {
  public:
-  CXFA_PsMap(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_PsMap() override;
+
+ private:
+  CXFA_PsMap(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PSMAP_H_
diff --git a/xfa/fxfa/parser/cxfa_query.cpp b/xfa/fxfa/parser/cxfa_query.cpp
index 7f2db0b..7149af7 100644
--- a/xfa/fxfa/parser/cxfa_query.cpp
+++ b/xfa/fxfa/parser/cxfa_query.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_query.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kQueryPropertyData[] = {
-    {XFA_Element::RecordSet, 1, 0},
-    {XFA_Element::Select, 1, 0},
+    {XFA_Element::RecordSet, 1, {}},
+    {XFA_Element::Select, 1, {}},
 };
 
 const CXFA_Node::AttributeData kQueryAttributeData[] = {
@@ -30,11 +30,13 @@
 CXFA_Query::CXFA_Query(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_SourceSet,
+                XFA_XDPPACKET::kSourceSet,
                 XFA_ObjectType::Node,
                 XFA_Element::Query,
                 kQueryPropertyData,
                 kQueryAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Query::~CXFA_Query() = default;
diff --git a/xfa/fxfa/parser/cxfa_query.h b/xfa/fxfa/parser/cxfa_query.h
index 7d62c68..80b4daa 100644
--- a/xfa/fxfa/parser/cxfa_query.h
+++ b/xfa/fxfa/parser/cxfa_query.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Query final : public CXFA_Node {
  public:
-  CXFA_Query(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Query() override;
+
+ private:
+  CXFA_Query(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_QUERY_H_
diff --git a/xfa/fxfa/parser/cxfa_radial.cpp b/xfa/fxfa/parser/cxfa_radial.cpp
index 4eb944f..4f4f557 100644
--- a/xfa/fxfa/parser/cxfa_radial.cpp
+++ b/xfa/fxfa/parser/cxfa_radial.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,19 +6,21 @@
 
 #include "xfa/fxfa/parser/cxfa_radial.h"
 
+#include <math.h>
+
 #include <utility>
 
-#include "core/fxge/render_defines.h"
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
+#include "xfa/fgas/graphics/cfgas_geshading.h"
 #include "xfa/fxfa/parser/cxfa_color.h"
-#include "xfa/fxgraphics/cxfa_geshading.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kRadialPropertyData[] = {
-    {XFA_Element::Color, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Color, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kRadialAttributeData[] = {
@@ -34,12 +36,14 @@
 CXFA_Radial::CXFA_Radial(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Radial,
                 kRadialPropertyData,
                 kRadialAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Radial::~CXFA_Radial() = default;
 
@@ -52,8 +56,8 @@
   return GetChild<CXFA_Color>(0, XFA_Element::Color, false);
 }
 
-void CXFA_Radial::Draw(CXFA_Graphics* pGS,
-                       CXFA_GEPath* fillPath,
+void CXFA_Radial::Draw(CFGAS_GEGraphics* pGS,
+                       const CFGAS_GEPath& fillPath,
                        FX_ARGB crStart,
                        const CFX_RectF& rtFill,
                        const CFX_Matrix& matrix) {
@@ -62,14 +66,11 @@
   if (!IsToEdge())
     std::swap(crStart, crEnd);
 
-  float endRadius = sqrt(rtFill.Width() * rtFill.Width() +
-                         rtFill.Height() * rtFill.Height()) /
-                    2;
-  CXFA_GEShading shading(rtFill.Center(), rtFill.Center(), 0, endRadius, true,
-                         true, crStart, crEnd);
+  float end_radius = FXSYS_sqrt2(rtFill.Width(), rtFill.Height()) / 2;
+  CFGAS_GEShading shading(rtFill.Center(), rtFill.Center(), 0, end_radius, true,
+                          true, crStart, crEnd);
 
-  pGS->SaveGraphState();
-  pGS->SetFillColor(CXFA_GEColor(&shading));
-  pGS->FillPath(fillPath, FXFILL_WINDING, &matrix);
-  pGS->RestoreGraphState();
+  CFGAS_GEGraphics::StateRestorer restorer(pGS);
+  pGS->SetFillColor(CFGAS_GEColor(&shading));
+  pGS->FillPath(fillPath, CFX_FillRenderOptions::FillType::kWinding, matrix);
 }
diff --git a/xfa/fxfa/parser/cxfa_radial.h b/xfa/fxfa/parser/cxfa_radial.h
index 8fb30b5..cb0e296 100644
--- a/xfa/fxfa/parser/cxfa_radial.h
+++ b/xfa/fxfa/parser/cxfa_radial.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,24 +8,26 @@
 #define XFA_FXFA_PARSER_CXFA_RADIAL_H_
 
 #include "core/fxcrt/fx_coordinates.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
 
+class CFGAS_GEGraphics;
 class CXFA_Color;
-class CXFA_Graphics;
 
 class CXFA_Radial final : public CXFA_Node {
  public:
-  CXFA_Radial(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Radial() override;
 
-  void Draw(CXFA_Graphics* pGS,
-            CXFA_GEPath* fillPath,
+  void Draw(CFGAS_GEGraphics* pGS,
+            const CFGAS_GEPath& fillPath,
             FX_ARGB crStart,
             const CFX_RectF& rtFill,
             const CFX_Matrix& matrix);
 
  private:
+  CXFA_Radial(CXFA_Document* doc, XFA_PacketType packet);
+
   bool IsToEdge();
   CXFA_Color* GetColorIfExists();
 };
diff --git a/xfa/fxfa/parser/cxfa_range.cpp b/xfa/fxfa/parser/cxfa_range.cpp
index 9f62b1d..cf5a9c3 100644
--- a/xfa/fxfa/parser/cxfa_range.cpp
+++ b/xfa/fxfa/parser/cxfa_range.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_range.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Range::CXFA_Range(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Range,
                 {},
                 kRangeAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Range::~CXFA_Range() = default;
diff --git a/xfa/fxfa/parser/cxfa_range.h b/xfa/fxfa/parser/cxfa_range.h
index 5d8920d..d561d8d 100644
--- a/xfa/fxfa/parser/cxfa_range.h
+++ b/xfa/fxfa/parser/cxfa_range.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Range final : public CXFA_Node {
  public:
-  CXFA_Range(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Range() override;
+
+ private:
+  CXFA_Range(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_RANGE_H_
diff --git a/xfa/fxfa/parser/cxfa_reason.cpp b/xfa/fxfa/parser/cxfa_reason.cpp
index 8af61ba..9574c9c 100644
--- a/xfa/fxfa/parser/cxfa_reason.cpp
+++ b/xfa/fxfa/parser/cxfa_reason.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_reason.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_Reason::CXFA_Reason(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::TextNode,
                 XFA_Element::Reason,
                 {},
                 kReasonAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Reason::~CXFA_Reason() = default;
diff --git a/xfa/fxfa/parser/cxfa_reason.h b/xfa/fxfa/parser/cxfa_reason.h
index 1475637..98b739f 100644
--- a/xfa/fxfa/parser/cxfa_reason.h
+++ b/xfa/fxfa/parser/cxfa_reason.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Reason final : public CXFA_Node {
  public:
-  CXFA_Reason(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Reason() override;
+
+ private:
+  CXFA_Reason(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_REASON_H_
diff --git a/xfa/fxfa/parser/cxfa_reasons.cpp b/xfa/fxfa/parser/cxfa_reasons.cpp
index 704f411..ac7e4f4 100644
--- a/xfa/fxfa/parser/cxfa_reasons.cpp
+++ b/xfa/fxfa/parser/cxfa_reasons.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_reasons.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -24,11 +24,13 @@
 CXFA_Reasons::CXFA_Reasons(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Reasons,
                 {},
                 kReasonsAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Reasons::~CXFA_Reasons() = default;
diff --git a/xfa/fxfa/parser/cxfa_reasons.h b/xfa/fxfa/parser/cxfa_reasons.h
index 673a9b2..1df46d6 100644
--- a/xfa/fxfa/parser/cxfa_reasons.h
+++ b/xfa/fxfa/parser/cxfa_reasons.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Reasons final : public CXFA_Node {
  public:
-  CXFA_Reasons(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Reasons() override;
+
+ private:
+  CXFA_Reasons(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_REASONS_H_
diff --git a/xfa/fxfa/parser/cxfa_record.cpp b/xfa/fxfa/parser/cxfa_record.cpp
index 5c10f05..5121e63 100644
--- a/xfa/fxfa/parser/cxfa_record.cpp
+++ b/xfa/fxfa/parser/cxfa_record.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_record.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Record::CXFA_Record(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Record,
                 {},
                 kRecordAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Record::~CXFA_Record() = default;
diff --git a/xfa/fxfa/parser/cxfa_record.h b/xfa/fxfa/parser/cxfa_record.h
index de0d0c4..be477c5 100644
--- a/xfa/fxfa/parser/cxfa_record.h
+++ b/xfa/fxfa/parser/cxfa_record.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Record final : public CXFA_Node {
  public:
-  CXFA_Record(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Record() override;
+
+ private:
+  CXFA_Record(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_RECORD_H_
diff --git a/xfa/fxfa/parser/cxfa_recordset.cpp b/xfa/fxfa/parser/cxfa_recordset.cpp
index ad77695..0660251 100644
--- a/xfa/fxfa/parser/cxfa_recordset.cpp
+++ b/xfa/fxfa/parser/cxfa_recordset.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_recordset.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -34,11 +34,13 @@
 CXFA_RecordSet::CXFA_RecordSet(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_SourceSet,
+                XFA_XDPPACKET::kSourceSet,
                 XFA_ObjectType::Node,
                 XFA_Element::RecordSet,
                 {},
                 kRecordSetAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_RecordSet::~CXFA_RecordSet() = default;
diff --git a/xfa/fxfa/parser/cxfa_recordset.h b/xfa/fxfa/parser/cxfa_recordset.h
index a9895bd..f36516b 100644
--- a/xfa/fxfa/parser/cxfa_recordset.h
+++ b/xfa/fxfa/parser/cxfa_recordset.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_RecordSet final : public CXFA_Node {
  public:
-  CXFA_RecordSet(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_RecordSet() override;
+
+ private:
+  CXFA_RecordSet(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_RECORDSET_H_
diff --git a/xfa/fxfa/parser/cxfa_rectangle.cpp b/xfa/fxfa/parser/cxfa_rectangle.cpp
index 632141d..c6be5ac 100644
--- a/xfa/fxfa/parser/cxfa_rectangle.cpp
+++ b/xfa/fxfa/parser/cxfa_rectangle.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,20 +6,24 @@
 
 #include "xfa/fxfa/parser/cxfa_rectangle.h"
 
+#include <math.h>
+
 #include <utility>
 
-#include "core/fxge/render_defines.h"
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/notreached.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
 #include "xfa/fxfa/parser/cxfa_corner.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_stroke.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kRectanglePropertyData[] = {
-    {XFA_Element::Edge, 4, 0},
-    {XFA_Element::Corner, 4, 0},
-    {XFA_Element::Fill, 1, 0},
+    {XFA_Element::Edge, 4, {}},
+    {XFA_Element::Corner, 4, {}},
+    {XFA_Element::Fill, 1, {}},
 };
 
 const CXFA_Node::AttributeData kRectangleAttributeData[] = {
@@ -32,24 +36,33 @@
 
 }  // namespace
 
+// static
+CXFA_Rectangle* CXFA_Rectangle::FromNode(CXFA_Node* pNode) {
+  return pNode && pNode->GetElementType() == XFA_Element::Rectangle
+             ? static_cast<CXFA_Rectangle*>(pNode)
+             : nullptr;
+}
+
 CXFA_Rectangle::CXFA_Rectangle(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Box(doc,
                packet,
-               (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+               {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                XFA_ObjectType::Node,
                XFA_Element::Rectangle,
                kRectanglePropertyData,
                kRectangleAttributeData,
-               pdfium::MakeUnique<CJX_Node>(this)) {}
+               cppgc::MakeGarbageCollected<CJX_Node>(
+                   doc->GetHeap()->GetAllocationHandle(),
+                   this)) {}
 
 CXFA_Rectangle::CXFA_Rectangle(CXFA_Document* pDoc,
                                XFA_PacketType ePacket,
-                               uint32_t validPackets,
+                               Mask<XFA_XDPPACKET> validPackets,
                                XFA_ObjectType oType,
                                XFA_Element eType,
                                pdfium::span<const PropertyData> properties,
                                pdfium::span<const AttributeData> attributes,
-                               std::unique_ptr<CJX_Object> js_node)
+                               CJX_Object* js_node)
     : CXFA_Box(pDoc,
                ePacket,
                validPackets,
@@ -57,18 +70,18 @@
                eType,
                properties,
                attributes,
-               std::move(js_node)) {}
+               js_node) {}
 
-CXFA_Rectangle::~CXFA_Rectangle() {}
+CXFA_Rectangle::~CXFA_Rectangle() = default;
 
 void CXFA_Rectangle::GetFillPath(const std::vector<CXFA_Stroke*>& strokes,
                                  const CFX_RectF& rtWidget,
-                                 CXFA_GEPath* fillPath) {
+                                 CFGAS_GEPath* fillPath) {
   bool bSameStyles = true;
   CXFA_Stroke* stroke1 = strokes[0];
   for (int32_t i = 1; i < 8; i++) {
     CXFA_Stroke* stroke2 = strokes[i];
-    if (!stroke1->SameStyles(stroke2, 0)) {
+    if (!stroke1->SameStyles(stroke2, {})) {
       bSameStyles = false;
       break;
     }
@@ -79,8 +92,9 @@
     stroke1 = strokes[0];
     for (int32_t i = 2; i < 8; i += 2) {
       CXFA_Stroke* stroke2 = strokes[i];
-      if (!stroke1->SameStyles(stroke2, XFA_STROKE_SAMESTYLE_NoPresence |
-                                            XFA_STROKE_SAMESTYLE_Corner)) {
+      if (!stroke1->SameStyles(stroke2,
+                               {CXFA_Stroke::SameStyleOption::kNoPresence,
+                                CXFA_Stroke::SameStyleOption::kCorner})) {
         bSameStyles = false;
         break;
       }
@@ -107,7 +121,8 @@
     float vy = 1.0f;
     float nx = 1.0f;
     float ny = 1.0f;
-    CFX_PointF cp1, cp2;
+    CFX_PointF cp1;
+    CFX_PointF cp2;
     CXFA_Stroke* corner1 = strokes[i];
     CXFA_Stroke* corner2 = strokes[(i + 2) % 8];
     float fRadius1 = corner1->GetRadius();
@@ -115,49 +130,60 @@
     bool bInverted = corner1->IsInverted();
     bool bRound = corner1->GetJoinType() == XFA_AttributeValue::Round;
     if (bRound) {
-      sy = FX_PI / 2;
+      sy = FXSYS_PI / 2;
     }
     switch (i) {
       case 0:
         cp1 = rtWidget.TopLeft();
         cp2 = rtWidget.TopRight();
-        vx = 1, vy = 1;
-        nx = -1, ny = 0;
+        vx = 1;
+        vy = 1;
+        nx = -1;
+        ny = 0;
         if (bRound) {
-          sx = bInverted ? FX_PI / 2 : FX_PI;
+          sx = bInverted ? FXSYS_PI / 2 : FXSYS_PI;
         } else {
-          sx = 1, sy = 0;
+          sx = 1;
+          sy = 0;
         }
         break;
       case 2:
         cp1 = rtWidget.TopRight();
         cp2 = rtWidget.BottomRight();
-        vx = -1, vy = 1;
-        nx = 0, ny = -1;
+        vx = -1;
+        vy = 1;
+        nx = 0;
+        ny = -1;
         if (bRound) {
-          sx = bInverted ? FX_PI : FX_PI * 3 / 2;
+          sx = bInverted ? FXSYS_PI : FXSYS_PI * 3 / 2;
         } else {
-          sx = 0, sy = 1;
+          sx = 0;
+          sy = 1;
         }
         break;
       case 4:
         cp1 = rtWidget.BottomRight();
         cp2 = rtWidget.BottomLeft();
-        vx = -1, vy = -1;
-        nx = 1, ny = 0;
+        vx = -1;
+        vy = -1;
+        nx = 1;
+        ny = 0;
         if (bRound) {
-          sx = bInverted ? FX_PI * 3 / 2 : 0;
+          sx = bInverted ? FXSYS_PI * 3 / 2 : 0;
         } else {
-          sx = -1, sy = 0;
+          sx = -1;
+          sy = 0;
         }
         break;
       case 6:
         cp1 = rtWidget.BottomLeft();
         cp2 = rtWidget.TopLeft();
-        vx = 1, vy = -1;
-        nx = 0, ny = 1;
+        vx = 1;
+        vy = -1;
+        nx = 0;
+        ny = 1;
         if (bRound) {
-          sx = bInverted ? 0 : FX_PI / 2;
+          sx = bInverted ? 0 : FXSYS_PI / 2;
         } else {
           sx = 0;
           sy = -1;
@@ -169,7 +195,7 @@
 
     if (bRound) {
       if (fRadius1 < 0)
-        sx -= FX_PI;
+        sx -= FXSYS_PI;
       if (bInverted)
         sy *= -1;
 
@@ -196,7 +222,7 @@
 }
 
 void CXFA_Rectangle::Draw(const std::vector<CXFA_Stroke*>& strokes,
-                          CXFA_Graphics* pGS,
+                          CFGAS_GEGraphics* pGS,
                           CFX_RectF rtWidget,
                           const CFX_Matrix& matrix) {
   bool bVisible = false;
@@ -210,7 +236,7 @@
     return;
 
   for (int32_t i = 1; i < 8; i += 2) {
-    float fThickness = std::fmax(0.0, strokes[i]->GetThickness());
+    float fThickness = fmax(0.0, strokes[i]->GetThickness());
     float fHalf = fThickness / 2;
     XFA_AttributeValue iHand = GetHand();
     switch (i) {
@@ -252,7 +278,7 @@
 }
 
 void CXFA_Rectangle::Stroke(const std::vector<CXFA_Stroke*>& strokes,
-                            CXFA_Graphics* pGS,
+                            CFGAS_GEGraphics* pGS,
                             CFX_RectF rtWidget,
                             const CFX_Matrix& matrix) {
   bool bVisible;
@@ -277,8 +303,7 @@
         StrokeEmbossed(pGS, rtWidget, fThickness, matrix);
         break;
       default:
-        NOTREACHED();
-        break;
+        NOTREACHED_NORETURN();
     }
     return;
   }
@@ -288,7 +313,7 @@
   CXFA_Stroke* stroke1 = strokes[0];
   for (int32_t i = 1; i < 8; i++) {
     CXFA_Stroke* stroke2 = strokes[i];
-    if (!stroke1->SameStyles(stroke2, 0)) {
+    if (!stroke1->SameStyles(stroke2, {})) {
       bSameStyles = false;
       break;
     }
@@ -299,8 +324,9 @@
     bClose = true;
     for (int32_t i = 2; i < 8; i += 2) {
       CXFA_Stroke* stroke2 = strokes[i];
-      if (!stroke1->SameStyles(stroke2, XFA_STROKE_SAMESTYLE_NoPresence |
-                                            XFA_STROKE_SAMESTYLE_Corner)) {
+      if (!stroke1->SameStyles(stroke2,
+                               {CXFA_Stroke::SameStyleOption::kNoPresence,
+                                CXFA_Stroke::SameStyleOption::kCorner})) {
         bSameStyles = false;
         break;
       }
@@ -316,14 +342,14 @@
   }
 
   bool bStart = true;
-  CXFA_GEPath path;
+  CFGAS_GEPath path;
   for (int32_t i = 0; i < 8; i++) {
     CXFA_Stroke* stroke = strokes[i];
     if ((i % 1) == 0 && stroke->GetRadius() < 0) {
       bool bEmpty = path.IsEmpty();
       if (!bEmpty) {
         if (stroke)
-          stroke->Stroke(&path, pGS, matrix);
+          stroke->Stroke(pGS, path, matrix);
         path.Clear();
       }
       bStart = true;
@@ -331,10 +357,10 @@
     }
     GetPath(strokes, rtWidget, path, i, bStart, !bSameStyles);
 
-    bStart = !stroke->SameStyles(strokes[(i + 1) % 8], 0);
+    bStart = !stroke->SameStyles(strokes[(i + 1) % 8], {});
     if (bStart) {
       if (stroke)
-        stroke->Stroke(&path, pGS, matrix);
+        stroke->Stroke(pGS, path, matrix);
       path.Clear();
     }
   }
@@ -344,11 +370,11 @@
       path.Close();
     }
     if (strokes[7])
-      strokes[7]->Stroke(&path, pGS, matrix);
+      strokes[7]->Stroke(pGS, path, matrix);
   }
 }
 
-void CXFA_Rectangle::StrokeRect(CXFA_Graphics* pGraphic,
+void CXFA_Rectangle::StrokeRect(CFGAS_GEGraphics* pGraphic,
                                 const CFX_RectF& rt,
                                 float fLineWidth,
                                 const CFX_Matrix& matrix,
@@ -356,7 +382,7 @@
                                 FX_ARGB argbBottomRight) {
   float fBottom = rt.bottom();
   float fRight = rt.right();
-  CXFA_GEPath pathLT;
+  CFGAS_GEPath pathLT;
   pathLT.MoveTo(CFX_PointF(rt.left, fBottom));
   pathLT.LineTo(CFX_PointF(rt.left, rt.top));
   pathLT.LineTo(CFX_PointF(fRight, rt.top));
@@ -364,10 +390,10 @@
   pathLT.LineTo(CFX_PointF(rt.left + fLineWidth, rt.top + fLineWidth));
   pathLT.LineTo(CFX_PointF(rt.left + fLineWidth, fBottom - fLineWidth));
   pathLT.LineTo(CFX_PointF(rt.left, fBottom));
-  pGraphic->SetFillColor(CXFA_GEColor(argbTopLeft));
-  pGraphic->FillPath(&pathLT, FXFILL_WINDING, &matrix);
+  pGraphic->SetFillColor(CFGAS_GEColor(argbTopLeft));
+  pGraphic->FillPath(pathLT, CFX_FillRenderOptions::FillType::kWinding, matrix);
 
-  CXFA_GEPath pathRB;
+  CFGAS_GEPath pathRB;
   pathRB.MoveTo(CFX_PointF(fRight, rt.top));
   pathRB.LineTo(CFX_PointF(fRight, fBottom));
   pathRB.LineTo(CFX_PointF(rt.left, fBottom));
@@ -375,11 +401,11 @@
   pathRB.LineTo(CFX_PointF(fRight - fLineWidth, fBottom - fLineWidth));
   pathRB.LineTo(CFX_PointF(fRight - fLineWidth, rt.top + fLineWidth));
   pathRB.LineTo(CFX_PointF(fRight, rt.top));
-  pGraphic->SetFillColor(CXFA_GEColor(argbBottomRight));
-  pGraphic->FillPath(&pathRB, FXFILL_WINDING, &matrix);
+  pGraphic->SetFillColor(CFGAS_GEColor(argbBottomRight));
+  pGraphic->FillPath(pathRB, CFX_FillRenderOptions::FillType::kWinding, matrix);
 }
 
-void CXFA_Rectangle::StrokeLowered(CXFA_Graphics* pGS,
+void CXFA_Rectangle::StrokeLowered(CFGAS_GEGraphics* pGS,
                                    CFX_RectF rt,
                                    float fThickness,
                                    const CFX_Matrix& matrix) {
@@ -387,16 +413,15 @@
   CFX_RectF rtInner(rt);
   rtInner.Deflate(fHalfWidth, fHalfWidth);
 
-  CXFA_GEPath path;
+  CFGAS_GEPath path;
   path.AddRectangle(rt.left, rt.top, rt.width, rt.height);
   path.AddRectangle(rtInner.left, rtInner.top, rtInner.width, rtInner.height);
-  pGS->SetFillColor(CXFA_GEColor(0xFF000000));
-  pGS->FillPath(&path, FXFILL_ALTERNATE, &matrix);
-
+  pGS->SetFillColor(CFGAS_GEColor(0xFF000000));
+  pGS->FillPath(path, CFX_FillRenderOptions::FillType::kEvenOdd, matrix);
   StrokeRect(pGS, rtInner, fHalfWidth, matrix, 0xFF808080, 0xFFC0C0C0);
 }
 
-void CXFA_Rectangle::StrokeRaised(CXFA_Graphics* pGS,
+void CXFA_Rectangle::StrokeRaised(CFGAS_GEGraphics* pGS,
                                   CFX_RectF rt,
                                   float fThickness,
                                   const CFX_Matrix& matrix) {
@@ -404,16 +429,15 @@
   CFX_RectF rtInner(rt);
   rtInner.Deflate(fHalfWidth, fHalfWidth);
 
-  CXFA_GEPath path;
+  CFGAS_GEPath path;
   path.AddRectangle(rt.left, rt.top, rt.width, rt.height);
   path.AddRectangle(rtInner.left, rtInner.top, rtInner.width, rtInner.height);
-  pGS->SetFillColor(CXFA_GEColor(0xFF000000));
-  pGS->FillPath(&path, FXFILL_ALTERNATE, &matrix);
-
+  pGS->SetFillColor(CFGAS_GEColor(0xFF000000));
+  pGS->FillPath(path, CFX_FillRenderOptions::FillType::kEvenOdd, matrix);
   StrokeRect(pGS, rtInner, fHalfWidth, matrix, 0xFFFFFFFF, 0xFF808080);
 }
 
-void CXFA_Rectangle::StrokeEtched(CXFA_Graphics* pGS,
+void CXFA_Rectangle::StrokeEtched(CFGAS_GEGraphics* pGS,
                                   CFX_RectF rt,
                                   float fThickness,
                                   const CFX_Matrix& matrix) {
@@ -425,7 +449,7 @@
   StrokeRect(pGS, rtInner, fHalfWidth, matrix, 0xFFFFFFFF, 0xFF808080);
 }
 
-void CXFA_Rectangle::StrokeEmbossed(CXFA_Graphics* pGS,
+void CXFA_Rectangle::StrokeEmbossed(CFGAS_GEGraphics* pGS,
                                     CFX_RectF rt,
                                     float fThickness,
                                     const CFX_Matrix& matrix) {
@@ -439,12 +463,12 @@
 
 void CXFA_Rectangle::GetPath(const std::vector<CXFA_Stroke*>& strokes,
                              CFX_RectF rtWidget,
-                             CXFA_GEPath& path,
+                             CFGAS_GEPath& path,
                              int32_t nIndex,
                              bool bStart,
                              bool bCorner) {
-  ASSERT(nIndex >= 0);
-  ASSERT(nIndex < 8);
+  DCHECK(nIndex >= 0);
+  DCHECK(nIndex < 8);
 
   int32_t n = (nIndex & 1) ? nIndex - 1 : nIndex;
   CXFA_Stroke* corner1 = strokes[n];
@@ -463,9 +487,9 @@
     CXFA_Stroke* strokeBefore = strokes[(nIndex + 1 * 8 - 1) % 8];
     CXFA_Stroke* strokeAfter = strokes[nIndex + 1];
     if (stroke->IsInverted()) {
-      if (!stroke->SameStyles(strokeBefore, 0))
+      if (!stroke->SameStyles(strokeBefore, {}))
         halfBefore = strokeBefore->GetThickness() / 2;
-      if (!stroke->SameStyles(strokeAfter, 0))
+      if (!stroke->SameStyles(strokeAfter, {}))
         halfAfter = strokeAfter->GetThickness() / 2;
     }
   } else {
@@ -489,7 +513,7 @@
   CFX_PointF cp1;
   CFX_PointF cp2;
   if (bRound)
-    sy = FX_PI / 2;
+    sy = FXSYS_PI / 2;
 
   switch (nIndex) {
     case 0:
@@ -498,17 +522,22 @@
       cp2 = rtWidget.TopRight();
       if (nIndex == 0) {
         cpStart.x = cp1.x - halfBefore;
-        cpStart.y = cp1.y + fRadius1, offsetY = -halfAfter;
+        cpStart.y = cp1.y + fRadius1;
+        offsetY = -halfAfter;
       } else {
-        cpStart.x = cp1.x + fRadius1 - halfBefore, cpStart.y = cp1.y,
+        cpStart.x = cp1.x + fRadius1 - halfBefore;
+        cpStart.y = cp1.y;
         offsetEX = halfAfter;
       }
-      vx = 1, vy = 1;
-      nx = -1, ny = 0;
+      vx = 1;
+      vy = 1;
+      nx = -1;
+      ny = 0;
       if (bRound) {
-        sx = bInverted ? FX_PI / 2 : FX_PI;
+        sx = bInverted ? FXSYS_PI / 2 : FXSYS_PI;
       } else {
-        sx = 1, sy = 0;
+        sx = 1;
+        sy = 0;
       }
       break;
     case 2:
@@ -516,18 +545,23 @@
       cp1 = rtWidget.TopRight();
       cp2 = rtWidget.BottomRight();
       if (nIndex == 2) {
-        cpStart.x = cp1.x - fRadius1, cpStart.y = cp1.y - halfBefore,
+        cpStart.x = cp1.x - fRadius1;
+        cpStart.y = cp1.y - halfBefore;
         offsetX = halfAfter;
       } else {
-        cpStart.x = cp1.x, cpStart.y = cp1.y + fRadius1 - halfBefore,
+        cpStart.x = cp1.x;
+        cpStart.y = cp1.y + fRadius1 - halfBefore;
         offsetEY = halfAfter;
       }
-      vx = -1, vy = 1;
-      nx = 0, ny = -1;
+      vx = -1;
+      vy = 1;
+      nx = 0;
+      ny = -1;
       if (bRound) {
-        sx = bInverted ? FX_PI : FX_PI * 3 / 2;
+        sx = bInverted ? FXSYS_PI : FXSYS_PI * 3 / 2;
       } else {
-        sx = 0, sy = 1;
+        sx = 0;
+        sy = 1;
       }
       break;
     case 4:
@@ -535,18 +569,23 @@
       cp1 = rtWidget.BottomRight();
       cp2 = rtWidget.BottomLeft();
       if (nIndex == 4) {
-        cpStart.x = cp1.x + halfBefore, cpStart.y = cp1.y - fRadius1,
+        cpStart.x = cp1.x + halfBefore;
+        cpStart.y = cp1.y - fRadius1;
         offsetY = halfAfter;
       } else {
-        cpStart.x = cp1.x - fRadius1 + halfBefore, cpStart.y = cp1.y,
+        cpStart.x = cp1.x - fRadius1 + halfBefore;
+        cpStart.y = cp1.y;
         offsetEX = -halfAfter;
       }
-      vx = -1, vy = -1;
-      nx = 1, ny = 0;
+      vx = -1;
+      vy = -1;
+      nx = 1;
+      ny = 0;
       if (bRound) {
-        sx = bInverted ? FX_PI * 3 / 2 : 0;
+        sx = bInverted ? FXSYS_PI * 3 / 2 : 0;
       } else {
-        sx = -1, sy = 0;
+        sx = -1;
+        sy = 0;
       }
       break;
     case 6:
@@ -554,10 +593,12 @@
       cp1 = rtWidget.BottomLeft();
       cp2 = rtWidget.TopLeft();
       if (nIndex == 6) {
-        cpStart.x = cp1.x + fRadius1, cpStart.y = cp1.y + halfBefore,
+        cpStart.x = cp1.x + fRadius1;
+        cpStart.y = cp1.y + halfBefore;
         offsetX = -halfAfter;
       } else {
-        cpStart.x = cp1.x, cpStart.y = cp1.y - fRadius1 + halfBefore,
+        cpStart.x = cp1.x;
+        cpStart.y = cp1.y - fRadius1 + halfBefore;
         offsetEY = -halfAfter;
       }
       vx = 1;
@@ -565,7 +606,7 @@
       nx = 0;
       ny = 1;
       if (bRound) {
-        sx = bInverted ? 0 : FX_PI / 2;
+        sx = bInverted ? 0 : FXSYS_PI / 2;
       } else {
         sx = 0;
         sy = -1;
@@ -582,7 +623,7 @@
   }
   if (bRound) {
     if (fRadius1 < 0)
-      sx -= FX_PI;
+      sx -= FXSYS_PI;
     if (bInverted)
       sy *= -1;
 
diff --git a/xfa/fxfa/parser/cxfa_rectangle.h b/xfa/fxfa/parser/cxfa_rectangle.h
index 6d17471..ac69a77 100644
--- a/xfa/fxfa/parser/cxfa_rectangle.h
+++ b/xfa/fxfa/parser/cxfa_rectangle.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,57 +7,58 @@
 #ifndef XFA_FXFA_PARSER_CXFA_RECTANGLE_H_
 #define XFA_FXFA_PARSER_CXFA_RECTANGLE_H_
 
-#include <memory>
 #include <vector>
 
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fxfa/parser/cxfa_box.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
 
 class CXFA_Rectangle : public CXFA_Box {
  public:
-  CXFA_Rectangle(CXFA_Document* doc, XFA_PacketType packet);
+  static CXFA_Rectangle* FromNode(CXFA_Node* pNode);
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Rectangle() override;
 
   void GetFillPath(const std::vector<CXFA_Stroke*>& strokes,
                    const CFX_RectF& rtWidget,
-                   CXFA_GEPath* fillPath);
+                   CFGAS_GEPath* fillPath);
   void Draw(const std::vector<CXFA_Stroke*>& strokes,
-            CXFA_Graphics* pGS,
+            CFGAS_GEGraphics* pGS,
             CFX_RectF rtWidget,
             const CFX_Matrix& matrix);
 
  protected:
+  CXFA_Rectangle(CXFA_Document* doc, XFA_PacketType packet);
   CXFA_Rectangle(CXFA_Document* pDoc,
                  XFA_PacketType ePacket,
-                 uint32_t validPackets,
+                 Mask<XFA_XDPPACKET> validPackets,
                  XFA_ObjectType oType,
                  XFA_Element eType,
                  pdfium::span<const PropertyData> properties,
                  pdfium::span<const AttributeData> attributes,
-                 std::unique_ptr<CJX_Object> js_node);
+                 CJX_Object* js_node);
 
- private:
   void Stroke(const std::vector<CXFA_Stroke*>& strokes,
-              CXFA_Graphics* pGS,
+              CFGAS_GEGraphics* pGS,
               CFX_RectF rtWidget,
               const CFX_Matrix& matrix);
-  void StrokeEmbossed(CXFA_Graphics* pGS,
+  void StrokeEmbossed(CFGAS_GEGraphics* pGS,
                       CFX_RectF rt,
                       float fThickness,
                       const CFX_Matrix& matrix);
-  void StrokeLowered(CXFA_Graphics* pGS,
+  void StrokeLowered(CFGAS_GEGraphics* pGS,
                      CFX_RectF rt,
                      float fThickness,
                      const CFX_Matrix& matrix);
-  void StrokeRaised(CXFA_Graphics* pGS,
+  void StrokeRaised(CFGAS_GEGraphics* pGS,
                     CFX_RectF rt,
                     float fThickness,
                     const CFX_Matrix& matrix);
-  void StrokeEtched(CXFA_Graphics* pGS,
+  void StrokeEtched(CFGAS_GEGraphics* pGS,
                     CFX_RectF rt,
                     float fThickness,
                     const CFX_Matrix& matrix);
-  void StrokeRect(CXFA_Graphics* pGraphic,
+  void StrokeRect(CFGAS_GEGraphics* pGraphic,
                   const CFX_RectF& rt,
                   float fLineWidth,
                   const CFX_Matrix& matrix,
@@ -65,7 +66,7 @@
                   FX_ARGB argbBottomRight);
   void GetPath(const std::vector<CXFA_Stroke*>& strokes,
                CFX_RectF rtWidget,
-               CXFA_GEPath& path,
+               CFGAS_GEPath& path,
                int32_t nIndex,
                bool bStart,
                bool bCorner);
diff --git a/xfa/fxfa/parser/cxfa_ref.cpp b/xfa/fxfa/parser/cxfa_ref.cpp
index fc4f59a..0d072d9 100644
--- a/xfa/fxfa/parser/cxfa_ref.cpp
+++ b/xfa/fxfa/parser/cxfa_ref.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_ref.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
 CXFA_Ref::CXFA_Ref(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::TextNode,
                 XFA_Element::Ref,
                 {},
                 kRefAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Ref::~CXFA_Ref() = default;
diff --git a/xfa/fxfa/parser/cxfa_ref.h b/xfa/fxfa/parser/cxfa_ref.h
index 5ab8e00..dfe124e 100644
--- a/xfa/fxfa/parser/cxfa_ref.h
+++ b/xfa/fxfa/parser/cxfa_ref.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Ref final : public CXFA_Node {
  public:
-  CXFA_Ref(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Ref() override;
+
+ private:
+  CXFA_Ref(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_REF_H_
diff --git a/xfa/fxfa/parser/cxfa_relevant.cpp b/xfa/fxfa/parser/cxfa_relevant.cpp
index f6b8a52..8988683 100644
--- a/xfa/fxfa/parser/cxfa_relevant.cpp
+++ b/xfa/fxfa/parser/cxfa_relevant.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_relevant.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Relevant::CXFA_Relevant(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Relevant,
                 {},
                 kRelevantAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Relevant::~CXFA_Relevant() = default;
diff --git a/xfa/fxfa/parser/cxfa_relevant.h b/xfa/fxfa/parser/cxfa_relevant.h
index 03283c3..d57461c 100644
--- a/xfa/fxfa/parser/cxfa_relevant.h
+++ b/xfa/fxfa/parser/cxfa_relevant.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Relevant final : public CXFA_Node {
  public:
-  CXFA_Relevant(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Relevant() override;
+
+ private:
+  CXFA_Relevant(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_RELEVANT_H_
diff --git a/xfa/fxfa/parser/cxfa_rename.cpp b/xfa/fxfa/parser/cxfa_rename.cpp
index f444554..8634b5a 100644
--- a/xfa/fxfa/parser/cxfa_rename.cpp
+++ b/xfa/fxfa/parser/cxfa_rename.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_rename.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Rename::CXFA_Rename(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Rename,
                 {},
                 kRenameAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Rename::~CXFA_Rename() = default;
diff --git a/xfa/fxfa/parser/cxfa_rename.h b/xfa/fxfa/parser/cxfa_rename.h
index f50d852..5c0e66b 100644
--- a/xfa/fxfa/parser/cxfa_rename.h
+++ b/xfa/fxfa/parser/cxfa_rename.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Rename final : public CXFA_Node {
  public:
-  CXFA_Rename(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Rename() override;
+
+ private:
+  CXFA_Rename(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_RENAME_H_
diff --git a/xfa/fxfa/parser/cxfa_renderpolicy.cpp b/xfa/fxfa/parser/cxfa_renderpolicy.cpp
index 6497eec..170bf81 100644
--- a/xfa/fxfa/parser/cxfa_renderpolicy.cpp
+++ b/xfa/fxfa/parser/cxfa_renderpolicy.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_renderpolicy.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_RenderPolicy::CXFA_RenderPolicy(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::RenderPolicy,
                 {},
                 kRenderPolicyAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_RenderPolicy::~CXFA_RenderPolicy() = default;
diff --git a/xfa/fxfa/parser/cxfa_renderpolicy.h b/xfa/fxfa/parser/cxfa_renderpolicy.h
index 4947289..6a1b0bb 100644
--- a/xfa/fxfa/parser/cxfa_renderpolicy.h
+++ b/xfa/fxfa/parser/cxfa_renderpolicy.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_RenderPolicy final : public CXFA_Node {
  public:
-  CXFA_RenderPolicy(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_RenderPolicy() override;
+
+ private:
+  CXFA_RenderPolicy(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_RENDERPOLICY_H_
diff --git a/xfa/fxfa/parser/cxfa_rootelement.cpp b/xfa/fxfa/parser/cxfa_rootelement.cpp
index 46d5aea..9123a9c 100644
--- a/xfa/fxfa/parser/cxfa_rootelement.cpp
+++ b/xfa/fxfa/parser/cxfa_rootelement.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_rootelement.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_RootElement::CXFA_RootElement(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_ConnectionSet,
+                XFA_XDPPACKET::kConnectionSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::RootElement,
                 {},
                 kRootElementAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_RootElement::~CXFA_RootElement() = default;
diff --git a/xfa/fxfa/parser/cxfa_rootelement.h b/xfa/fxfa/parser/cxfa_rootelement.h
index 4a9218e..10b20fb 100644
--- a/xfa/fxfa/parser/cxfa_rootelement.h
+++ b/xfa/fxfa/parser/cxfa_rootelement.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_RootElement final : public CXFA_Node {
  public:
-  CXFA_RootElement(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_RootElement() override;
+
+ private:
+  CXFA_RootElement(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ROOTELEMENT_H_
diff --git a/xfa/fxfa/parser/cxfa_runscripts.cpp b/xfa/fxfa/parser/cxfa_runscripts.cpp
index dbc6acc..18ca1bf 100644
--- a/xfa/fxfa/parser/cxfa_runscripts.cpp
+++ b/xfa/fxfa/parser/cxfa_runscripts.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_runscripts.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_RunScripts::CXFA_RunScripts(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::RunScripts,
                 {},
                 kRunScriptsAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_RunScripts::~CXFA_RunScripts() = default;
diff --git a/xfa/fxfa/parser/cxfa_runscripts.h b/xfa/fxfa/parser/cxfa_runscripts.h
index 2f80b0d..00f1fc4 100644
--- a/xfa/fxfa/parser/cxfa_runscripts.h
+++ b/xfa/fxfa/parser/cxfa_runscripts.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_RunScripts final : public CXFA_Node {
  public:
-  CXFA_RunScripts(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_RunScripts() override;
+
+ private:
+  CXFA_RunScripts(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_RUNSCRIPTS_H_
diff --git a/xfa/fxfa/parser/cxfa_script.cpp b/xfa/fxfa/parser/cxfa_script.cpp
index 884f97d..4b4e8ce 100644
--- a/xfa/fxfa/parser/cxfa_script.cpp
+++ b/xfa/fxfa/parser/cxfa_script.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,14 @@
 #include "xfa/fxfa/parser/cxfa_script.h"
 
 #include "fxjs/xfa/cjx_script.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kScriptPropertyData[] = {
-    {XFA_Element::Exclude, 1, 0},
-    {XFA_Element::CurrentPage, 1, 0},
-    {XFA_Element::RunScripts, 1, 0},
+    {XFA_Element::Exclude, 1, {}},
+    {XFA_Element::CurrentPage, 1, {}},
+    {XFA_Element::RunScripts, 1, {}},
 };
 
 const CXFA_Node::AttributeData kScriptAttributeData[] = {
@@ -32,21 +32,30 @@
 
 }  // namespace
 
+// static
+CXFA_Script* CXFA_Script::FromNode(CXFA_Node* pNode) {
+  return pNode && pNode->GetElementType() == XFA_Element::Script
+             ? static_cast<CXFA_Script*>(pNode)
+             : nullptr;
+}
+
 CXFA_Script::CXFA_Script(CXFA_Document* doc, XFA_PacketType packet)
-    : CXFA_Node(
-          doc,
-          packet,
-          (XFA_XDPPACKET_Config | XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
-          XFA_ObjectType::ContentNode,
-          XFA_Element::Script,
-          kScriptPropertyData,
-          kScriptAttributeData,
-          pdfium::MakeUnique<CJX_Script>(this)) {}
+    : CXFA_Node(doc,
+                packet,
+                {XFA_XDPPACKET::kConfig, XFA_XDPPACKET::kTemplate,
+                 XFA_XDPPACKET::kForm},
+                XFA_ObjectType::ContentNode,
+                XFA_Element::Script,
+                kScriptPropertyData,
+                kScriptAttributeData,
+                cppgc::MakeGarbageCollected<CJX_Script>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Script::~CXFA_Script() = default;
 
 CXFA_Script::Type CXFA_Script::GetContentType() {
-  Optional<WideString> cData =
+  absl::optional<WideString> cData =
       JSObject()->TryCData(XFA_Attribute::ContentType, false);
   if (!cData.has_value())
     return Type::Formcalc;
diff --git a/xfa/fxfa/parser/cxfa_script.h b/xfa/fxfa/parser/cxfa_script.h
index f559284..fe6abde 100644
--- a/xfa/fxfa/parser/cxfa_script.h
+++ b/xfa/fxfa/parser/cxfa_script.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,12 +18,17 @@
     Unknown,
   };
 
-  CXFA_Script(CXFA_Document* doc, XFA_PacketType packet);
+  static CXFA_Script* FromNode(CXFA_Node* pNode);
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Script() override;
 
   Type GetContentType();
   XFA_AttributeValue GetRunAt();
   WideString GetExpression();
+
+ private:
+  CXFA_Script(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SCRIPT_H_
diff --git a/xfa/fxfa/parser/cxfa_scriptmodel.cpp b/xfa/fxfa/parser/cxfa_scriptmodel.cpp
index 8de4901..0ff649e 100644
--- a/xfa/fxfa/parser/cxfa_scriptmodel.cpp
+++ b/xfa/fxfa/parser/cxfa_scriptmodel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_scriptmodel.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_ScriptModel::CXFA_ScriptModel(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::ScriptModel,
                 {},
                 kScriptModelAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ScriptModel::~CXFA_ScriptModel() = default;
diff --git a/xfa/fxfa/parser/cxfa_scriptmodel.h b/xfa/fxfa/parser/cxfa_scriptmodel.h
index a71073e..5c5bf99 100644
--- a/xfa/fxfa/parser/cxfa_scriptmodel.h
+++ b/xfa/fxfa/parser/cxfa_scriptmodel.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_ScriptModel final : public CXFA_Node {
  public:
-  CXFA_ScriptModel(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ScriptModel() override;
+
+ private:
+  CXFA_ScriptModel(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SCRIPTMODEL_H_
diff --git a/xfa/fxfa/parser/cxfa_select.cpp b/xfa/fxfa/parser/cxfa_select.cpp
index 04e51dc..9968dc7 100644
--- a/xfa/fxfa/parser/cxfa_select.cpp
+++ b/xfa/fxfa/parser/cxfa_select.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_select.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_Select::CXFA_Select(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_SourceSet,
+                XFA_XDPPACKET::kSourceSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::Select,
                 {},
                 kSelectAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Select::~CXFA_Select() = default;
diff --git a/xfa/fxfa/parser/cxfa_select.h b/xfa/fxfa/parser/cxfa_select.h
index 82fc0d2..41894e1 100644
--- a/xfa/fxfa/parser/cxfa_select.h
+++ b/xfa/fxfa/parser/cxfa_select.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Select final : public CXFA_Node {
  public:
-  CXFA_Select(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Select() override;
+
+ private:
+  CXFA_Select(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SELECT_H_
diff --git a/xfa/fxfa/parser/cxfa_setproperty.cpp b/xfa/fxfa/parser/cxfa_setproperty.cpp
index a748d58..6e80f75 100644
--- a/xfa/fxfa/parser/cxfa_setproperty.cpp
+++ b/xfa/fxfa/parser/cxfa_setproperty.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_setproperty.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
 CXFA_SetProperty::CXFA_SetProperty(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::SetProperty,
                 {},
                 kSetPropertyAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_SetProperty::~CXFA_SetProperty() = default;
diff --git a/xfa/fxfa/parser/cxfa_setproperty.h b/xfa/fxfa/parser/cxfa_setproperty.h
index aab588b..9797ad4 100644
--- a/xfa/fxfa/parser/cxfa_setproperty.h
+++ b/xfa/fxfa/parser/cxfa_setproperty.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_SetProperty final : public CXFA_Node {
  public:
-  CXFA_SetProperty(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_SetProperty() override;
+
+ private:
+  CXFA_SetProperty(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SETPROPERTY_H_
diff --git a/xfa/fxfa/parser/cxfa_severity.cpp b/xfa/fxfa/parser/cxfa_severity.cpp
index 78a0872..16d2a7f 100644
--- a/xfa/fxfa/parser/cxfa_severity.cpp
+++ b/xfa/fxfa/parser/cxfa_severity.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_severity.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Severity::CXFA_Severity(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Severity,
                 {},
                 kSeverityAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Severity::~CXFA_Severity() = default;
diff --git a/xfa/fxfa/parser/cxfa_severity.h b/xfa/fxfa/parser/cxfa_severity.h
index b2283c9..0b564af 100644
--- a/xfa/fxfa/parser/cxfa_severity.h
+++ b/xfa/fxfa/parser/cxfa_severity.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Severity final : public CXFA_Node {
  public:
-  CXFA_Severity(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Severity() override;
+
+ private:
+  CXFA_Severity(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SEVERITY_H_
diff --git a/xfa/fxfa/parser/cxfa_sharptext.cpp b/xfa/fxfa/parser/cxfa_sharptext.cpp
index 515e881..7112af8 100644
--- a/xfa/fxfa/parser/cxfa_sharptext.cpp
+++ b/xfa/fxfa/parser/cxfa_sharptext.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_sharptext.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -20,13 +20,15 @@
 CXFA_Sharptext::CXFA_Sharptext(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Config |
-                 XFA_XDPPACKET_LocaleSet | XFA_XDPPACKET_ConnectionSet |
-                 XFA_XDPPACKET_SourceSet | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kConfig,
+                 XFA_XDPPACKET::kLocaleSet, XFA_XDPPACKET::kConnectionSet,
+                 XFA_XDPPACKET::kSourceSet, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::NodeV,
                 XFA_Element::Sharptext,
                 {},
                 kSharptextAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Sharptext::~CXFA_Sharptext() = default;
diff --git a/xfa/fxfa/parser/cxfa_sharptext.h b/xfa/fxfa/parser/cxfa_sharptext.h
index f2a41e8..7e6c43a 100644
--- a/xfa/fxfa/parser/cxfa_sharptext.h
+++ b/xfa/fxfa/parser/cxfa_sharptext.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Sharptext final : public CXFA_Node {
  public:
-  CXFA_Sharptext(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Sharptext() override;
+
+ private:
+  CXFA_Sharptext(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SHARPTEXT_H_
diff --git a/xfa/fxfa/parser/cxfa_sharpxhtml.cpp b/xfa/fxfa/parser/cxfa_sharpxhtml.cpp
index 6758b48..b7c6a25 100644
--- a/xfa/fxfa/parser/cxfa_sharpxhtml.cpp
+++ b/xfa/fxfa/parser/cxfa_sharpxhtml.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_sharpxhtml.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -20,13 +20,15 @@
 CXFA_SharpxHTML::CXFA_SharpxHTML(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Config |
-                 XFA_XDPPACKET_LocaleSet | XFA_XDPPACKET_ConnectionSet |
-                 XFA_XDPPACKET_SourceSet | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kConfig,
+                 XFA_XDPPACKET::kLocaleSet, XFA_XDPPACKET::kConnectionSet,
+                 XFA_XDPPACKET::kSourceSet, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::NodeV,
                 XFA_Element::SharpxHTML,
                 {},
                 kSharpxHTMLAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_SharpxHTML::~CXFA_SharpxHTML() = default;
diff --git a/xfa/fxfa/parser/cxfa_sharpxhtml.h b/xfa/fxfa/parser/cxfa_sharpxhtml.h
index b73789d..5de3e27 100644
--- a/xfa/fxfa/parser/cxfa_sharpxhtml.h
+++ b/xfa/fxfa/parser/cxfa_sharpxhtml.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_SharpxHTML final : public CXFA_Node {
  public:
-  CXFA_SharpxHTML(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_SharpxHTML() override;
+
+ private:
+  CXFA_SharpxHTML(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SHARPXHTML_H_
diff --git a/xfa/fxfa/parser/cxfa_sharpxml.cpp b/xfa/fxfa/parser/cxfa_sharpxml.cpp
index 0e066b1..f5a4be5 100644
--- a/xfa/fxfa/parser/cxfa_sharpxml.cpp
+++ b/xfa/fxfa/parser/cxfa_sharpxml.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_sharpxml.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -20,11 +20,13 @@
 CXFA_Sharpxml::CXFA_Sharpxml(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::NodeV,
                 XFA_Element::Sharpxml,
                 {},
                 kSharpxmlAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Sharpxml::~CXFA_Sharpxml() = default;
diff --git a/xfa/fxfa/parser/cxfa_sharpxml.h b/xfa/fxfa/parser/cxfa_sharpxml.h
index b2e467a..a8519c7 100644
--- a/xfa/fxfa/parser/cxfa_sharpxml.h
+++ b/xfa/fxfa/parser/cxfa_sharpxml.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Sharpxml final : public CXFA_Node {
  public:
-  CXFA_Sharpxml(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Sharpxml() override;
+
+ private:
+  CXFA_Sharpxml(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SHARPXML_H_
diff --git a/xfa/fxfa/parser/cxfa_signature.cpp b/xfa/fxfa/parser/cxfa_signature.cpp
index d059808..0f38363 100644
--- a/xfa/fxfa/parser/cxfa_signature.cpp
+++ b/xfa/fxfa/parser/cxfa_signature.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,14 @@
 #include "xfa/fxfa/parser/cxfa_signature.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kSignaturePropertyData[] = {
-    {XFA_Element::Margin, 1, 0}, {XFA_Element::Filter, 1, 0},
-    {XFA_Element::Border, 1, 0}, {XFA_Element::Manifest, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Margin, 1, {}}, {XFA_Element::Filter, 1, {}},
+    {XFA_Element::Border, 1, {}}, {XFA_Element::Manifest, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kSignatureAttributeData[] = {
@@ -30,12 +30,14 @@
 CXFA_Signature::CXFA_Signature(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Signature,
                 kSignaturePropertyData,
                 kSignatureAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Signature::~CXFA_Signature() = default;
 
diff --git a/xfa/fxfa/parser/cxfa_signature.h b/xfa/fxfa/parser/cxfa_signature.h
index 678e892..c129863 100644
--- a/xfa/fxfa/parser/cxfa_signature.h
+++ b/xfa/fxfa/parser/cxfa_signature.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,10 +11,13 @@
 
 class CXFA_Signature final : public CXFA_Node {
  public:
-  CXFA_Signature(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Signature() override;
 
   XFA_FFWidgetType GetDefaultFFWidgetType() const override;
+
+ private:
+  CXFA_Signature(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SIGNATURE_H_
diff --git a/xfa/fxfa/parser/cxfa_signatureproperties.cpp b/xfa/fxfa/parser/cxfa_signatureproperties.cpp
index 63b2635..3fffdff 100644
--- a/xfa/fxfa/parser/cxfa_signatureproperties.cpp
+++ b/xfa/fxfa/parser/cxfa_signatureproperties.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_signatureproperties.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                                    XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::SignatureProperties,
                 {},
                 kSignaturePropertiesAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_SignatureProperties::~CXFA_SignatureProperties() = default;
diff --git a/xfa/fxfa/parser/cxfa_signatureproperties.h b/xfa/fxfa/parser/cxfa_signatureproperties.h
index a66346e..679612b 100644
--- a/xfa/fxfa/parser/cxfa_signatureproperties.h
+++ b/xfa/fxfa/parser/cxfa_signatureproperties.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_SignatureProperties final : public CXFA_Node {
  public:
-  CXFA_SignatureProperties(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_SignatureProperties() override;
+
+ private:
+  CXFA_SignatureProperties(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SIGNATUREPROPERTIES_H_
diff --git a/xfa/fxfa/parser/cxfa_signdata.cpp b/xfa/fxfa/parser/cxfa_signdata.cpp
index 3710325..8904b10 100644
--- a/xfa/fxfa/parser/cxfa_signdata.cpp
+++ b/xfa/fxfa/parser/cxfa_signdata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_signdata.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kSignDataPropertyData[] = {
-    {XFA_Element::Filter, 1, 0},
-    {XFA_Element::Manifest, 1, 0},
+    {XFA_Element::Filter, 1, {}},
+    {XFA_Element::Manifest, 1, {}},
 };
 
 const CXFA_Node::AttributeData kSignDataAttributeData[] = {
@@ -31,11 +31,13 @@
 CXFA_SignData::CXFA_SignData(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::SignData,
                 kSignDataPropertyData,
                 kSignDataAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_SignData::~CXFA_SignData() = default;
diff --git a/xfa/fxfa/parser/cxfa_signdata.h b/xfa/fxfa/parser/cxfa_signdata.h
index e28460d..84050cd 100644
--- a/xfa/fxfa/parser/cxfa_signdata.h
+++ b/xfa/fxfa/parser/cxfa_signdata.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_SignData final : public CXFA_Node {
  public:
-  CXFA_SignData(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_SignData() override;
+
+ private:
+  CXFA_SignData(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SIGNDATA_H_
diff --git a/xfa/fxfa/parser/cxfa_signing.cpp b/xfa/fxfa/parser/cxfa_signing.cpp
index 939d4f5..a9fedd9 100644
--- a/xfa/fxfa/parser/cxfa_signing.cpp
+++ b/xfa/fxfa/parser/cxfa_signing.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_signing.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -24,11 +24,13 @@
 CXFA_Signing::CXFA_Signing(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Signing,
                 {},
                 kSigningAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Signing::~CXFA_Signing() = default;
diff --git a/xfa/fxfa/parser/cxfa_signing.h b/xfa/fxfa/parser/cxfa_signing.h
index 7c37403..b52a91d 100644
--- a/xfa/fxfa/parser/cxfa_signing.h
+++ b/xfa/fxfa/parser/cxfa_signing.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Signing final : public CXFA_Node {
  public:
-  CXFA_Signing(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Signing() override;
+
+ private:
+  CXFA_Signing(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SIGNING_H_
diff --git a/xfa/fxfa/parser/cxfa_silentprint.cpp b/xfa/fxfa/parser/cxfa_silentprint.cpp
index 1a417aa..74c4922 100644
--- a/xfa/fxfa/parser/cxfa_silentprint.cpp
+++ b/xfa/fxfa/parser/cxfa_silentprint.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_silentprint.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kSilentPrintPropertyData[] = {
-    {XFA_Element::AddSilentPrint, 1, 0},
-    {XFA_Element::PrinterName, 1, 0},
+    {XFA_Element::AddSilentPrint, 1, {}},
+    {XFA_Element::PrinterName, 1, {}},
 };
 
 const CXFA_Node::AttributeData kSilentPrintAttributeData[] = {
@@ -26,11 +26,13 @@
 CXFA_SilentPrint::CXFA_SilentPrint(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::SilentPrint,
                 kSilentPrintPropertyData,
                 kSilentPrintAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_SilentPrint::~CXFA_SilentPrint() = default;
diff --git a/xfa/fxfa/parser/cxfa_silentprint.h b/xfa/fxfa/parser/cxfa_silentprint.h
index 378084d..549bad5 100644
--- a/xfa/fxfa/parser/cxfa_silentprint.h
+++ b/xfa/fxfa/parser/cxfa_silentprint.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_SilentPrint final : public CXFA_Node {
  public:
-  CXFA_SilentPrint(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_SilentPrint() override;
+
+ private:
+  CXFA_SilentPrint(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SILENTPRINT_H_
diff --git a/xfa/fxfa/parser/cxfa_soapaction.cpp b/xfa/fxfa/parser/cxfa_soapaction.cpp
index 514c667..3958676 100644
--- a/xfa/fxfa/parser/cxfa_soapaction.cpp
+++ b/xfa/fxfa/parser/cxfa_soapaction.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_soapaction.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_SoapAction::CXFA_SoapAction(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_ConnectionSet,
+                XFA_XDPPACKET::kConnectionSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::SoapAction,
                 {},
                 kSoapActionAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_SoapAction::~CXFA_SoapAction() = default;
diff --git a/xfa/fxfa/parser/cxfa_soapaction.h b/xfa/fxfa/parser/cxfa_soapaction.h
index b7f7526..409196b 100644
--- a/xfa/fxfa/parser/cxfa_soapaction.h
+++ b/xfa/fxfa/parser/cxfa_soapaction.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_SoapAction final : public CXFA_Node {
  public:
-  CXFA_SoapAction(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_SoapAction() override;
+
+ private:
+  CXFA_SoapAction(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SOAPACTION_H_
diff --git a/xfa/fxfa/parser/cxfa_soapaddress.cpp b/xfa/fxfa/parser/cxfa_soapaddress.cpp
index 08fd142..6dae6a1 100644
--- a/xfa/fxfa/parser/cxfa_soapaddress.cpp
+++ b/xfa/fxfa/parser/cxfa_soapaddress.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_soapaddress.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_SoapAddress::CXFA_SoapAddress(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_ConnectionSet,
+                XFA_XDPPACKET::kConnectionSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::SoapAddress,
                 {},
                 kSoapAddressAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_SoapAddress::~CXFA_SoapAddress() = default;
diff --git a/xfa/fxfa/parser/cxfa_soapaddress.h b/xfa/fxfa/parser/cxfa_soapaddress.h
index 130ddf3..254d9ee 100644
--- a/xfa/fxfa/parser/cxfa_soapaddress.h
+++ b/xfa/fxfa/parser/cxfa_soapaddress.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_SoapAddress final : public CXFA_Node {
  public:
-  CXFA_SoapAddress(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_SoapAddress() override;
+
+ private:
+  CXFA_SoapAddress(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SOAPADDRESS_H_
diff --git a/xfa/fxfa/parser/cxfa_solid.cpp b/xfa/fxfa/parser/cxfa_solid.cpp
index 3ab0e1d..da8de3f 100644
--- a/xfa/fxfa/parser/cxfa_solid.cpp
+++ b/xfa/fxfa/parser/cxfa_solid.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_solid.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kSolidPropertyData[] = {
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kSolidAttributeData[] = {
@@ -26,11 +26,13 @@
 CXFA_Solid::CXFA_Solid(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Solid,
                 kSolidPropertyData,
                 kSolidAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Solid::~CXFA_Solid() = default;
diff --git a/xfa/fxfa/parser/cxfa_solid.h b/xfa/fxfa/parser/cxfa_solid.h
index 28666ef..ce9939b 100644
--- a/xfa/fxfa/parser/cxfa_solid.h
+++ b/xfa/fxfa/parser/cxfa_solid.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Solid final : public CXFA_Node {
  public:
-  CXFA_Solid(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Solid() override;
+
+ private:
+  CXFA_Solid(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SOLID_H_
diff --git a/xfa/fxfa/parser/cxfa_source.cpp b/xfa/fxfa/parser/cxfa_source.cpp
index eb7773c..cdc864f 100644
--- a/xfa/fxfa/parser/cxfa_source.cpp
+++ b/xfa/fxfa/parser/cxfa_source.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_source.h"
 
 #include "fxjs/xfa/cjx_source.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kSourcePropertyData[] = {
-    {XFA_Element::Connect, 1, 0},
+    {XFA_Element::Connect, 1, {}},
 };
 
 const CXFA_Node::AttributeData kSourceAttributeData[] = {
@@ -27,11 +27,13 @@
 CXFA_Source::CXFA_Source(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_SourceSet,
+                XFA_XDPPACKET::kSourceSet,
                 XFA_ObjectType::Node,
                 XFA_Element::Source,
                 kSourcePropertyData,
                 kSourceAttributeData,
-                pdfium::MakeUnique<CJX_Source>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Source>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Source::~CXFA_Source() = default;
diff --git a/xfa/fxfa/parser/cxfa_source.h b/xfa/fxfa/parser/cxfa_source.h
index 96af083..526e3bb 100644
--- a/xfa/fxfa/parser/cxfa_source.h
+++ b/xfa/fxfa/parser/cxfa_source.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Source final : public CXFA_Node {
  public:
-  CXFA_Source(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Source() override;
+
+ private:
+  CXFA_Source(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SOURCE_H_
diff --git a/xfa/fxfa/parser/cxfa_sourceset.cpp b/xfa/fxfa/parser/cxfa_sourceset.cpp
index ba66cdf..f4281dc 100644
--- a/xfa/fxfa/parser/cxfa_sourceset.cpp
+++ b/xfa/fxfa/parser/cxfa_sourceset.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_sourceset.h"
 
 #include "fxjs/xfa/cjx_model.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_SourceSet::CXFA_SourceSet(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_SourceSet,
+                XFA_XDPPACKET::kSourceSet,
                 XFA_ObjectType::ModelNode,
                 XFA_Element::SourceSet,
                 {},
                 kSourceSetAttributeData,
-                pdfium::MakeUnique<CJX_Model>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Model>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_SourceSet::~CXFA_SourceSet() = default;
diff --git a/xfa/fxfa/parser/cxfa_sourceset.h b/xfa/fxfa/parser/cxfa_sourceset.h
index c31b94c..390c729 100644
--- a/xfa/fxfa/parser/cxfa_sourceset.h
+++ b/xfa/fxfa/parser/cxfa_sourceset.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_SourceSet final : public CXFA_Node {
  public:
-  CXFA_SourceSet(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_SourceSet() override;
+
+ private:
+  CXFA_SourceSet(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SOURCESET_H_
diff --git a/xfa/fxfa/parser/cxfa_speak.cpp b/xfa/fxfa/parser/cxfa_speak.cpp
index 605b929..ee66bad 100644
--- a/xfa/fxfa/parser/cxfa_speak.cpp
+++ b/xfa/fxfa/parser/cxfa_speak.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_speak.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -26,11 +26,13 @@
 CXFA_Speak::CXFA_Speak(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::TextNode,
                 XFA_Element::Speak,
                 {},
                 kSpeakAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Speak::~CXFA_Speak() = default;
diff --git a/xfa/fxfa/parser/cxfa_speak.h b/xfa/fxfa/parser/cxfa_speak.h
index 5497927..cefa58a 100644
--- a/xfa/fxfa/parser/cxfa_speak.h
+++ b/xfa/fxfa/parser/cxfa_speak.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Speak final : public CXFA_Node {
  public:
-  CXFA_Speak(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Speak() override;
+
+ private:
+  CXFA_Speak(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SPEAK_H_
diff --git a/xfa/fxfa/parser/cxfa_staple.cpp b/xfa/fxfa/parser/cxfa_staple.cpp
index 436518c..dd0fc6e 100644
--- a/xfa/fxfa/parser/cxfa_staple.cpp
+++ b/xfa/fxfa/parser/cxfa_staple.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_staple.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_Staple::CXFA_Staple(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Staple,
                 {},
                 kStapleAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Staple::~CXFA_Staple() = default;
diff --git a/xfa/fxfa/parser/cxfa_staple.h b/xfa/fxfa/parser/cxfa_staple.h
index 44ef654..0b93840 100644
--- a/xfa/fxfa/parser/cxfa_staple.h
+++ b/xfa/fxfa/parser/cxfa_staple.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Staple final : public CXFA_Node {
  public:
-  CXFA_Staple(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Staple() override;
+
+ private:
+  CXFA_Staple(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_STAPLE_H_
diff --git a/xfa/fxfa/parser/cxfa_startnode.cpp b/xfa/fxfa/parser/cxfa_startnode.cpp
index f415b29..3d2510a 100644
--- a/xfa/fxfa/parser/cxfa_startnode.cpp
+++ b/xfa/fxfa/parser/cxfa_startnode.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_startnode.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_StartNode::CXFA_StartNode(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::StartNode,
                 {},
                 kStartNodeAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_StartNode::~CXFA_StartNode() = default;
diff --git a/xfa/fxfa/parser/cxfa_startnode.h b/xfa/fxfa/parser/cxfa_startnode.h
index b8bab06..a75d4ab 100644
--- a/xfa/fxfa/parser/cxfa_startnode.h
+++ b/xfa/fxfa/parser/cxfa_startnode.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_StartNode final : public CXFA_Node {
  public:
-  CXFA_StartNode(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_StartNode() override;
+
+ private:
+  CXFA_StartNode(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_STARTNODE_H_
diff --git a/xfa/fxfa/parser/cxfa_startpage.cpp b/xfa/fxfa/parser/cxfa_startpage.cpp
index b8e31cd..fd49ef3 100644
--- a/xfa/fxfa/parser/cxfa_startpage.cpp
+++ b/xfa/fxfa/parser/cxfa_startpage.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_startpage.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_StartPage::CXFA_StartPage(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::StartPage,
                 {},
                 kStartPageAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_StartPage::~CXFA_StartPage() = default;
diff --git a/xfa/fxfa/parser/cxfa_startpage.h b/xfa/fxfa/parser/cxfa_startpage.h
index ae0ecee..83749a5 100644
--- a/xfa/fxfa/parser/cxfa_startpage.h
+++ b/xfa/fxfa/parser/cxfa_startpage.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_StartPage final : public CXFA_Node {
  public:
-  CXFA_StartPage(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_StartPage() override;
+
+ private:
+  CXFA_StartPage(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_STARTPAGE_H_
diff --git a/xfa/fxfa/parser/cxfa_stipple.cpp b/xfa/fxfa/parser/cxfa_stipple.cpp
index 024d111..6724605 100644
--- a/xfa/fxfa/parser/cxfa_stipple.cpp
+++ b/xfa/fxfa/parser/cxfa_stipple.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,16 +6,16 @@
 
 #include "xfa/fxfa/parser/cxfa_stipple.h"
 
-#include "core/fxge/render_defines.h"
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
 #include "xfa/fxfa/parser/cxfa_color.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kStipplePropertyData[] = {
-    {XFA_Element::Color, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Color, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kStippleAttributeData[] = {
@@ -30,12 +30,14 @@
 CXFA_Stipple::CXFA_Stipple(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Stipple,
                 kStipplePropertyData,
                 kStippleAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Stipple::~CXFA_Stipple() = default;
 
@@ -49,8 +51,8 @@
       .value_or(GetDefaultRate());
 }
 
-void CXFA_Stipple::Draw(CXFA_Graphics* pGS,
-                        CXFA_GEPath* fillPath,
+void CXFA_Stipple::Draw(CFGAS_GEGraphics* pGS,
+                        const CFGAS_GEPath& fillPath,
                         const CFX_RectF& rtFill,
                         const CFX_Matrix& matrix) {
   int32_t iRate = GetRate();
@@ -65,8 +67,7 @@
   std::tie(alpha, colorref) = ArgbToAlphaAndColorRef(crColor);
   FX_ARGB cr = AlphaAndColorRefToArgb(iRate * alpha / 100, colorref);
 
-  pGS->SaveGraphState();
-  pGS->SetFillColor(CXFA_GEColor(cr));
-  pGS->FillPath(fillPath, FXFILL_WINDING, &matrix);
-  pGS->RestoreGraphState();
+  CFGAS_GEGraphics::StateRestorer restorer(pGS);
+  pGS->SetFillColor(CFGAS_GEColor(cr));
+  pGS->FillPath(fillPath, CFX_FillRenderOptions::FillType::kWinding, matrix);
 }
diff --git a/xfa/fxfa/parser/cxfa_stipple.h b/xfa/fxfa/parser/cxfa_stipple.h
index af5581f..87f870a 100644
--- a/xfa/fxfa/parser/cxfa_stipple.h
+++ b/xfa/fxfa/parser/cxfa_stipple.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,25 +8,27 @@
 #define XFA_FXFA_PARSER_CXFA_STIPPLE_H_
 
 #include "core/fxcrt/fx_coordinates.h"
+#include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
 
+class CFGAS_GEGraphics;
 class CXFA_Color;
-class CXFA_Graphics;
 
 class CXFA_Stipple final : public CXFA_Node {
  public:
   static int32_t GetDefaultRate() { return 50; }
 
-  CXFA_Stipple(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Stipple() override;
 
-  void Draw(CXFA_Graphics* pGS,
-            CXFA_GEPath* fillPath,
+  void Draw(CFGAS_GEGraphics* pGS,
+            const CFGAS_GEPath& fillPath,
             const CFX_RectF& rtFill,
             const CFX_Matrix& matrix);
 
  private:
+  CXFA_Stipple(CXFA_Document* doc, XFA_PacketType packet);
+
   CXFA_Color* GetColorIfExists();
   int32_t GetRate();
 };
diff --git a/xfa/fxfa/parser/cxfa_stroke.cpp b/xfa/fxfa/parser/cxfa_stroke.cpp
index 65301c0..d1af2fe 100644
--- a/xfa/fxfa/parser/cxfa_stroke.cpp
+++ b/xfa/fxfa/parser/cxfa_stroke.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,17 +6,20 @@
 
 #include "xfa/fxfa/parser/cxfa_stroke.h"
 
+#include <math.h>
+
 #include <utility>
 
 #include "fxjs/xfa/cjx_object.h"
+#include "xfa/fgas/graphics/cfgas_gegraphics.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
 #include "xfa/fxfa/parser/cxfa_color.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_measurement.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
-#include "xfa/fxgraphics/cxfa_graphics.h"
 
-void XFA_StrokeTypeSetLineDash(CXFA_Graphics* pGraphics,
+void XFA_StrokeTypeSetLineDash(CFGAS_GEGraphics* pGraphics,
                                XFA_AttributeValue iStrokeType,
                                XFA_AttributeValue iCapType) {
   switch (iStrokeType) {
@@ -26,7 +29,7 @@
         dashArray[1] = 2;
         dashArray[3] = 2;
       }
-      pGraphics->SetLineDash(0, dashArray, FX_ArraySize(dashArray));
+      pGraphics->SetLineDash(0, dashArray);
       break;
     }
     case XFA_AttributeValue::DashDotDot: {
@@ -36,7 +39,7 @@
         dashArray[3] = 2;
         dashArray[5] = 2;
       }
-      pGraphics->SetLineDash(0, dashArray, FX_ArraySize(dashArray));
+      pGraphics->SetLineDash(0, dashArray);
       break;
     }
     case XFA_AttributeValue::Dashed: {
@@ -44,7 +47,7 @@
       if (iCapType != XFA_AttributeValue::Butt)
         dashArray[1] = 2;
 
-      pGraphics->SetLineDash(0, dashArray, FX_ArraySize(dashArray));
+      pGraphics->SetLineDash(0, dashArray);
       break;
     }
     case XFA_AttributeValue::Dotted: {
@@ -52,7 +55,7 @@
       if (iCapType != XFA_AttributeValue::Butt)
         dashArray[1] = 2;
 
-      pGraphics->SetLineDash(0, dashArray, FX_ArraySize(dashArray));
+      pGraphics->SetLineDash(0, dashArray);
       break;
     }
     default:
@@ -63,12 +66,12 @@
 
 CXFA_Stroke::CXFA_Stroke(CXFA_Document* pDoc,
                          XFA_PacketType ePacket,
-                         uint32_t validPackets,
+                         Mask<XFA_XDPPACKET> validPackets,
                          XFA_ObjectType oType,
                          XFA_Element eType,
                          pdfium::span<const PropertyData> properties,
                          pdfium::span<const AttributeData> attributes,
-                         std::unique_ptr<CJX_Object> js_node)
+                         CJX_Object* js_node)
     : CXFA_Node(pDoc,
                 ePacket,
                 validPackets,
@@ -76,7 +79,7 @@
                 eType,
                 properties,
                 attributes,
-                std::move(js_node)) {}
+                js_node) {}
 
 CXFA_Stroke::~CXFA_Stroke() = default;
 
@@ -107,12 +110,12 @@
   JSObject()->SetMeasure(XFA_Attribute::Thickness, msThinkness, false);
 }
 
-FX_ARGB CXFA_Stroke::GetColor() {
-  CXFA_Color* pNode = GetChild<CXFA_Color>(0, XFA_Element::Color, false);
+FX_ARGB CXFA_Stroke::GetColor() const {
+  const auto* pNode = GetChild<CXFA_Color>(0, XFA_Element::Color, false);
   if (!pNode)
     return 0xFF000000;
 
-  return StringToFXARGB(
+  return CXFA_Color::StringToFXARGB(
       pNode->JSObject()->GetCData(XFA_Attribute::Value).AsStringView());
 }
 
@@ -128,8 +131,7 @@
   int b;
   std::tie(a, r, g, b) = ArgbDecode(argb);
   pNode->JSObject()->SetCData(XFA_Attribute::Value,
-                              WideString::Format(L"%d,%d,%d", r, g, b), false,
-                              false);
+                              WideString::Format(L"%d,%d,%d", r, g, b));
 }
 
 XFA_AttributeValue CXFA_Stroke::GetJoinType() {
@@ -147,12 +149,13 @@
       .ToUnit(XFA_Unit::Pt);
 }
 
-bool CXFA_Stroke::SameStyles(CXFA_Stroke* stroke, uint32_t dwFlags) {
+bool CXFA_Stroke::SameStyles(CXFA_Stroke* stroke,
+                             Mask<SameStyleOption> dwFlags) {
   if (this == stroke)
     return true;
   if (fabs(GetThickness() - stroke->GetThickness()) >= 0.01f)
     return false;
-  if ((dwFlags & XFA_STROKE_SAMESTYLE_NoPresence) == 0 &&
+  if (!(dwFlags & SameStyleOption::kNoPresence) &&
       IsVisible() != stroke->IsVisible()) {
     return false;
   }
@@ -160,15 +163,15 @@
     return false;
   if (GetColor() != stroke->GetColor())
     return false;
-  if ((dwFlags & XFA_STROKE_SAMESTYLE_Corner) != 0 &&
+  if ((dwFlags & CXFA_Stroke::SameStyleOption::kCorner) &&
       fabs(GetRadius() - stroke->GetRadius()) >= 0.01f) {
     return false;
   }
   return true;
 }
 
-void CXFA_Stroke::Stroke(CXFA_GEPath* pPath,
-                         CXFA_Graphics* pGS,
+void CXFA_Stroke::Stroke(CFGAS_GEGraphics* pGS,
+                         const CFGAS_GEPath& pPath,
                          const CFX_Matrix& matrix) {
   if (!IsVisible())
     return;
@@ -177,15 +180,14 @@
   if (fThickness < 0.001f)
     return;
 
-  pGS->SaveGraphState();
+  CFGAS_GEGraphics::StateRestorer restorer(pGS);
   if (IsCorner() && fThickness > 2 * GetRadius())
     fThickness = 2 * GetRadius();
 
   pGS->SetLineWidth(fThickness);
   pGS->EnableActOnDash();
-  pGS->SetLineCap(CFX_GraphStateData::LineCapButt);
+  pGS->SetLineCap(CFX_GraphStateData::LineCap::kButt);
   XFA_StrokeTypeSetLineDash(pGS, GetStrokeType(), XFA_AttributeValue::Butt);
-  pGS->SetStrokeColor(CXFA_GEColor(GetColor()));
-  pGS->StrokePath(pPath, &matrix);
-  pGS->RestoreGraphState();
+  pGS->SetStrokeColor(CFGAS_GEColor(GetColor()));
+  pGS->StrokePath(pPath, matrix);
 }
diff --git a/xfa/fxfa/parser/cxfa_stroke.h b/xfa/fxfa/parser/cxfa_stroke.h
index c87bc50..1f98117 100644
--- a/xfa/fxfa/parser/cxfa_stroke.h
+++ b/xfa/fxfa/parser/cxfa_stroke.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,27 +7,26 @@
 #ifndef XFA_FXFA_PARSER_CXFA_STROKE_H_
 #define XFA_FXFA_PARSER_CXFA_STROKE_H_
 
-#include <memory>
-
-#include "core/fxge/fx_dib.h"
+#include "core/fxcrt/mask.h"
+#include "core/fxge/dib/fx_dib.h"
 #include "xfa/fxfa/fxfa_basic.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-enum StrokeSameStyle {
-  XFA_STROKE_SAMESTYLE_NoPresence = 1,
-  XFA_STROKE_SAMESTYLE_Corner = 2
-};
-
-class CXFA_GEPath;
-class CXFA_Graphics;
+class CFGAS_GEGraphics;
+class CFGAS_GEPath;
 class CXFA_Node;
 
-void XFA_StrokeTypeSetLineDash(CXFA_Graphics* pGraphics,
+void XFA_StrokeTypeSetLineDash(CFGAS_GEGraphics* pGraphics,
                                XFA_AttributeValue iStrokeType,
                                XFA_AttributeValue iCapType);
 
 class CXFA_Stroke : public CXFA_Node {
  public:
+  enum class SameStyleOption {
+    kNoPresence = 1 << 0,
+    kCorner = 1 << 1,
+  };
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Stroke() override;
 
   bool IsCorner() const { return GetElementType() == XFA_Element::Corner; }
@@ -43,22 +42,24 @@
   CXFA_Measurement GetMSThickness() const;
   void SetMSThickness(CXFA_Measurement msThinkness);
 
-  FX_ARGB GetColor();
+  FX_ARGB GetColor() const;
   void SetColor(FX_ARGB argb);
 
-  bool SameStyles(CXFA_Stroke* stroke, uint32_t dwFlags);
+  bool SameStyles(CXFA_Stroke* stroke, Mask<SameStyleOption> dwFlags);
 
-  void Stroke(CXFA_GEPath* pPath, CXFA_Graphics* pGS, const CFX_Matrix& matrix);
+  void Stroke(CFGAS_GEGraphics* pGS,
+              const CFGAS_GEPath& pPath,
+              const CFX_Matrix& matrix);
 
  protected:
   CXFA_Stroke(CXFA_Document* pDoc,
               XFA_PacketType ePacket,
-              uint32_t validPackets,
+              Mask<XFA_XDPPACKET> validPackets,
               XFA_ObjectType oType,
               XFA_Element eType,
               pdfium::span<const PropertyData> properties,
               pdfium::span<const AttributeData> attributes,
-              std::unique_ptr<CJX_Object> js_node);
+              CJX_Object* js_node);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_STROKE_H_
diff --git a/xfa/fxfa/parser/cxfa_subform.cpp b/xfa/fxfa/parser/cxfa_subform.cpp
index 37d99ec..d87d694 100644
--- a/xfa/fxfa/parser/cxfa_subform.cpp
+++ b/xfa/fxfa/parser/cxfa_subform.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,20 +7,20 @@
 #include "xfa/fxfa/parser/cxfa_subform.h"
 
 #include "fxjs/xfa/cjx_subform.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kSubformPropertyData[] = {
-    {XFA_Element::Break, 1, 0},   {XFA_Element::Margin, 1, 0},
-    {XFA_Element::Para, 1, 0},    {XFA_Element::Border, 1, 0},
-    {XFA_Element::Assist, 1, 0},  {XFA_Element::Traversal, 1, 0},
-    {XFA_Element::Keep, 1, 0},    {XFA_Element::Validate, 1, 0},
-    {XFA_Element::PageSet, 1, 0}, {XFA_Element::Overflow, 1, 0},
-    {XFA_Element::Bind, 1, 0},    {XFA_Element::Desc, 1, 0},
-    {XFA_Element::Bookend, 1, 0}, {XFA_Element::Calculate, 1, 0},
-    {XFA_Element::Extras, 1, 0},  {XFA_Element::Variables, 1, 0},
-    {XFA_Element::Occur, 1, 0},
+    {XFA_Element::Break, 1, {}},   {XFA_Element::Margin, 1, {}},
+    {XFA_Element::Para, 1, {}},    {XFA_Element::Border, 1, {}},
+    {XFA_Element::Assist, 1, {}},  {XFA_Element::Traversal, 1, {}},
+    {XFA_Element::Keep, 1, {}},    {XFA_Element::Validate, 1, {}},
+    {XFA_Element::PageSet, 1, {}}, {XFA_Element::Overflow, 1, {}},
+    {XFA_Element::Bind, 1, {}},    {XFA_Element::Desc, 1, {}},
+    {XFA_Element::Bookend, 1, {}}, {XFA_Element::Calculate, 1, {}},
+    {XFA_Element::Extras, 1, {}},  {XFA_Element::Variables, 1, {}},
+    {XFA_Element::Occur, 1, {}},
 };
 
 const CXFA_Node::AttributeData kSubformAttributeData[] = {
@@ -63,14 +63,23 @@
 
 }  // namespace
 
+// static
+CXFA_Subform* CXFA_Subform::FromNode(CXFA_Node* pNode) {
+  return pNode && pNode->GetElementType() == XFA_Element::Subform
+             ? static_cast<CXFA_Subform*>(pNode)
+             : nullptr;
+}
+
 CXFA_Subform::CXFA_Subform(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::Subform,
                 kSubformPropertyData,
                 kSubformAttributeData,
-                pdfium::MakeUnique<CJX_Subform>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Subform>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Subform::~CXFA_Subform() = default;
diff --git a/xfa/fxfa/parser/cxfa_subform.h b/xfa/fxfa/parser/cxfa_subform.h
index 3d16d10..b11310a 100644
--- a/xfa/fxfa/parser/cxfa_subform.h
+++ b/xfa/fxfa/parser/cxfa_subform.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,13 @@
 
 class CXFA_Subform final : public CXFA_Node {
  public:
-  CXFA_Subform(CXFA_Document* doc, XFA_PacketType packet);
+  static CXFA_Subform* FromNode(CXFA_Node* pNode);
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Subform() override;
+
+ private:
+  CXFA_Subform(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SUBFORM_H_
diff --git a/xfa/fxfa/parser/cxfa_subformset.cpp b/xfa/fxfa/parser/cxfa_subformset.cpp
index d1f6ba5..5acb9af 100644
--- a/xfa/fxfa/parser/cxfa_subformset.cpp
+++ b/xfa/fxfa/parser/cxfa_subformset.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,14 @@
 #include "xfa/fxfa/parser/cxfa_subformset.h"
 
 #include "fxjs/xfa/cjx_container.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kSubformSetPropertyData[] = {
-    {XFA_Element::Break, 1, 0},  {XFA_Element::Overflow, 1, 0},
-    {XFA_Element::Desc, 1, 0},   {XFA_Element::Bookend, 1, 0},
-    {XFA_Element::Extras, 1, 0}, {XFA_Element::Occur, 1, 0},
+    {XFA_Element::Break, 1, {}},  {XFA_Element::Overflow, 1, {}},
+    {XFA_Element::Desc, 1, {}},   {XFA_Element::Bookend, 1, {}},
+    {XFA_Element::Extras, 1, {}}, {XFA_Element::Occur, 1, {}},
 };
 
 const CXFA_Node::AttributeData kSubformSetAttributeData[] = {
@@ -32,11 +32,13 @@
 CXFA_SubformSet::CXFA_SubformSet(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::SubformSet,
                 kSubformSetPropertyData,
                 kSubformSetAttributeData,
-                pdfium::MakeUnique<CJX_Container>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Container>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_SubformSet::~CXFA_SubformSet() = default;
diff --git a/xfa/fxfa/parser/cxfa_subformset.h b/xfa/fxfa/parser/cxfa_subformset.h
index 12b9e9f..68a16e3 100644
--- a/xfa/fxfa/parser/cxfa_subformset.h
+++ b/xfa/fxfa/parser/cxfa_subformset.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_SubformSet final : public CXFA_Node {
  public:
-  CXFA_SubformSet(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_SubformSet() override;
+
+ private:
+  CXFA_SubformSet(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SUBFORMSET_H_
diff --git a/xfa/fxfa/parser/cxfa_subjectdn.cpp b/xfa/fxfa/parser/cxfa_subjectdn.cpp
index 1130739..8dcc453 100644
--- a/xfa/fxfa/parser/cxfa_subjectdn.cpp
+++ b/xfa/fxfa/parser/cxfa_subjectdn.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_subjectdn.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -24,11 +24,13 @@
 CXFA_SubjectDN::CXFA_SubjectDN(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::NodeC,
                 XFA_Element::SubjectDN,
                 {},
                 kSubjectDNAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_SubjectDN::~CXFA_SubjectDN() = default;
diff --git a/xfa/fxfa/parser/cxfa_subjectdn.h b/xfa/fxfa/parser/cxfa_subjectdn.h
index a4d94d7..5d86a8a 100644
--- a/xfa/fxfa/parser/cxfa_subjectdn.h
+++ b/xfa/fxfa/parser/cxfa_subjectdn.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_SubjectDN final : public CXFA_Node {
  public:
-  CXFA_SubjectDN(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_SubjectDN() override;
+
+ private:
+  CXFA_SubjectDN(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SUBJECTDN_H_
diff --git a/xfa/fxfa/parser/cxfa_subjectdns.cpp b/xfa/fxfa/parser/cxfa_subjectdns.cpp
index 524bc96..d47bb1e 100644
--- a/xfa/fxfa/parser/cxfa_subjectdns.cpp
+++ b/xfa/fxfa/parser/cxfa_subjectdns.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_subjectdns.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -24,11 +24,13 @@
 CXFA_SubjectDNs::CXFA_SubjectDNs(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::SubjectDNs,
                 {},
                 kSubjectDNsAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_SubjectDNs::~CXFA_SubjectDNs() = default;
diff --git a/xfa/fxfa/parser/cxfa_subjectdns.h b/xfa/fxfa/parser/cxfa_subjectdns.h
index 03d770b..b7cb77d 100644
--- a/xfa/fxfa/parser/cxfa_subjectdns.h
+++ b/xfa/fxfa/parser/cxfa_subjectdns.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_SubjectDNs final : public CXFA_Node {
  public:
-  CXFA_SubjectDNs(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_SubjectDNs() override;
+
+ private:
+  CXFA_SubjectDNs(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SUBJECTDNS_H_
diff --git a/xfa/fxfa/parser/cxfa_submit.cpp b/xfa/fxfa/parser/cxfa_submit.cpp
index 82eed83..ad398d9 100644
--- a/xfa/fxfa/parser/cxfa_submit.cpp
+++ b/xfa/fxfa/parser/cxfa_submit.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_submit.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kSubmitPropertyData[] = {
-    {XFA_Element::Encrypt, 1, 0},
+    {XFA_Element::Encrypt, 1, {}},
 };
 
 const CXFA_Node::AttributeData kSubmitAttributeData[] = {
@@ -32,12 +32,14 @@
 CXFA_Submit::CXFA_Submit(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Submit,
                 kSubmitPropertyData,
                 kSubmitAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Submit::~CXFA_Submit() = default;
 
diff --git a/xfa/fxfa/parser/cxfa_submit.h b/xfa/fxfa/parser/cxfa_submit.h
index 13cad86..1b6f7c3 100644
--- a/xfa/fxfa/parser/cxfa_submit.h
+++ b/xfa/fxfa/parser/cxfa_submit.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,13 +12,16 @@
 
 class CXFA_Submit final : public CXFA_Node {
  public:
-  CXFA_Submit(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Submit() override;
 
   bool IsSubmitEmbedPDF();
   XFA_AttributeValue GetSubmitFormat();
   WideString GetSubmitTarget();
   WideString GetSubmitXDPContent();
+
+ private:
+  CXFA_Submit(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SUBMIT_H_
diff --git a/xfa/fxfa/parser/cxfa_submitformat.cpp b/xfa/fxfa/parser/cxfa_submitformat.cpp
index 551b92b..00fe7a7 100644
--- a/xfa/fxfa/parser/cxfa_submitformat.cpp
+++ b/xfa/fxfa/parser/cxfa_submitformat.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_submitformat.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_SubmitFormat::CXFA_SubmitFormat(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::SubmitFormat,
                 {},
                 kSubmitFormatAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_SubmitFormat::~CXFA_SubmitFormat() = default;
diff --git a/xfa/fxfa/parser/cxfa_submitformat.h b/xfa/fxfa/parser/cxfa_submitformat.h
index bd6df26..9a3b697 100644
--- a/xfa/fxfa/parser/cxfa_submitformat.h
+++ b/xfa/fxfa/parser/cxfa_submitformat.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_SubmitFormat final : public CXFA_Node {
  public:
-  CXFA_SubmitFormat(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_SubmitFormat() override;
+
+ private:
+  CXFA_SubmitFormat(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SUBMITFORMAT_H_
diff --git a/xfa/fxfa/parser/cxfa_submiturl.cpp b/xfa/fxfa/parser/cxfa_submiturl.cpp
index 3212acb..1c36dfd 100644
--- a/xfa/fxfa/parser/cxfa_submiturl.cpp
+++ b/xfa/fxfa/parser/cxfa_submiturl.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_submiturl.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_SubmitUrl::CXFA_SubmitUrl(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::SubmitUrl,
                 {},
                 kSubmitUrlAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_SubmitUrl::~CXFA_SubmitUrl() = default;
diff --git a/xfa/fxfa/parser/cxfa_submiturl.h b/xfa/fxfa/parser/cxfa_submiturl.h
index f1914db..8bd64fe 100644
--- a/xfa/fxfa/parser/cxfa_submiturl.h
+++ b/xfa/fxfa/parser/cxfa_submiturl.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_SubmitUrl final : public CXFA_Node {
  public:
-  CXFA_SubmitUrl(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_SubmitUrl() override;
+
+ private:
+  CXFA_SubmitUrl(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SUBMITURL_H_
diff --git a/xfa/fxfa/parser/cxfa_subsetbelow.cpp b/xfa/fxfa/parser/cxfa_subsetbelow.cpp
index ed179c9..c65099f 100644
--- a/xfa/fxfa/parser/cxfa_subsetbelow.cpp
+++ b/xfa/fxfa/parser/cxfa_subsetbelow.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_subsetbelow.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_SubsetBelow::CXFA_SubsetBelow(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::SubsetBelow,
                 {},
                 kSubsetBelowAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_SubsetBelow::~CXFA_SubsetBelow() = default;
diff --git a/xfa/fxfa/parser/cxfa_subsetbelow.h b/xfa/fxfa/parser/cxfa_subsetbelow.h
index 230a563..c83ab02 100644
--- a/xfa/fxfa/parser/cxfa_subsetbelow.h
+++ b/xfa/fxfa/parser/cxfa_subsetbelow.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_SubsetBelow final : public CXFA_Node {
  public:
-  CXFA_SubsetBelow(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_SubsetBelow() override;
+
+ private:
+  CXFA_SubsetBelow(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SUBSETBELOW_H_
diff --git a/xfa/fxfa/parser/cxfa_suppressbanner.cpp b/xfa/fxfa/parser/cxfa_suppressbanner.cpp
index 3371170..6174dfd 100644
--- a/xfa/fxfa/parser/cxfa_suppressbanner.cpp
+++ b/xfa/fxfa/parser/cxfa_suppressbanner.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_suppressbanner.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                          XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::SuppressBanner,
                 {},
                 kSuppressBannerAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_SuppressBanner::~CXFA_SuppressBanner() = default;
diff --git a/xfa/fxfa/parser/cxfa_suppressbanner.h b/xfa/fxfa/parser/cxfa_suppressbanner.h
index 40057d0..47b81b1 100644
--- a/xfa/fxfa/parser/cxfa_suppressbanner.h
+++ b/xfa/fxfa/parser/cxfa_suppressbanner.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_SuppressBanner final : public CXFA_Node {
  public:
-  CXFA_SuppressBanner(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_SuppressBanner() override;
+
+ private:
+  CXFA_SuppressBanner(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SUPPRESSBANNER_H_
diff --git a/xfa/fxfa/parser/cxfa_tagged.cpp b/xfa/fxfa/parser/cxfa_tagged.cpp
index 940b12c..0a3335c 100644
--- a/xfa/fxfa/parser/cxfa_tagged.cpp
+++ b/xfa/fxfa/parser/cxfa_tagged.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_tagged.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Tagged::CXFA_Tagged(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Tagged,
                 {},
                 kTaggedAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Tagged::~CXFA_Tagged() = default;
diff --git a/xfa/fxfa/parser/cxfa_tagged.h b/xfa/fxfa/parser/cxfa_tagged.h
index ff54423..b551174 100644
--- a/xfa/fxfa/parser/cxfa_tagged.h
+++ b/xfa/fxfa/parser/cxfa_tagged.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Tagged final : public CXFA_Node {
  public:
-  CXFA_Tagged(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Tagged() override;
+
+ private:
+  CXFA_Tagged(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TAGGED_H_
diff --git a/xfa/fxfa/parser/cxfa_template.cpp b/xfa/fxfa/parser/cxfa_template.cpp
index bc5c444..2ac723c 100644
--- a/xfa/fxfa/parser/cxfa_template.cpp
+++ b/xfa/fxfa/parser/cxfa_template.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,14 @@
 #include "xfa/fxfa/parser/cxfa_template.h"
 
 #include "fxjs/xfa/cjx_template.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kTemplatePropertyData[] = {
-    {XFA_Element::Uri, 1, 0},       {XFA_Element::Xsl, 1, 0},
-    {XFA_Element::StartPage, 1, 0}, {XFA_Element::Relevant, 1, 0},
-    {XFA_Element::Base, 1, 0},      {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Uri, 1, {}},       {XFA_Element::Xsl, 1, {}},
+    {XFA_Element::StartPage, 1, {}}, {XFA_Element::Relevant, 1, {}},
+    {XFA_Element::Base, 1, {}},      {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kTemplateAttributeData[] = {
@@ -27,14 +27,16 @@
 }  // namespace
 
 CXFA_Template::CXFA_Template(CXFA_Document* doc, XFA_PacketType packet)
-    : CXFA_Node(
-          doc,
-          packet,
-          (XFA_XDPPACKET_Config | XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
-          XFA_ObjectType::ModelNode,
-          XFA_Element::Template,
-          kTemplatePropertyData,
-          kTemplateAttributeData,
-          pdfium::MakeUnique<CJX_Template>(this)) {}
+    : CXFA_Node(doc,
+                packet,
+                {XFA_XDPPACKET::kConfig, XFA_XDPPACKET::kTemplate,
+                 XFA_XDPPACKET::kForm},
+                XFA_ObjectType::ModelNode,
+                XFA_Element::Template,
+                kTemplatePropertyData,
+                kTemplateAttributeData,
+                cppgc::MakeGarbageCollected<CJX_Template>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Template::~CXFA_Template() = default;
diff --git a/xfa/fxfa/parser/cxfa_template.h b/xfa/fxfa/parser/cxfa_template.h
index fa9b999..487d6fd 100644
--- a/xfa/fxfa/parser/cxfa_template.h
+++ b/xfa/fxfa/parser/cxfa_template.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Template final : public CXFA_Node {
  public:
-  CXFA_Template(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Template() override;
+
+ private:
+  CXFA_Template(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TEMPLATE_H_
diff --git a/xfa/fxfa/parser/cxfa_templatecache.cpp b/xfa/fxfa/parser/cxfa_templatecache.cpp
index bfc19b3..0a704bc 100644
--- a/xfa/fxfa/parser/cxfa_templatecache.cpp
+++ b/xfa/fxfa/parser/cxfa_templatecache.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_templatecache.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
                                        XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::TemplateCache,
                 {},
                 kTemplateCacheAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_TemplateCache::~CXFA_TemplateCache() = default;
diff --git a/xfa/fxfa/parser/cxfa_templatecache.h b/xfa/fxfa/parser/cxfa_templatecache.h
index dd0136d..6daf6a4 100644
--- a/xfa/fxfa/parser/cxfa_templatecache.h
+++ b/xfa/fxfa/parser/cxfa_templatecache.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_TemplateCache final : public CXFA_Node {
  public:
-  CXFA_TemplateCache(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_TemplateCache() override;
+
+ private:
+  CXFA_TemplateCache(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TEMPLATECACHE_H_
diff --git a/xfa/fxfa/parser/cxfa_text.cpp b/xfa/fxfa/parser/cxfa_text.cpp
index bb921a0..43c2c94 100644
--- a/xfa/fxfa/parser/cxfa_text.cpp
+++ b/xfa/fxfa/parser/cxfa_text.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_text.h"
 
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,19 +22,28 @@
 
 }  // namespace
 
+// static
+CXFA_Text* CXFA_Text::FromNode(CXFA_Node* pNode) {
+  return pNode && pNode->GetElementType() == XFA_Element::Text
+             ? static_cast<CXFA_Text*>(pNode)
+             : nullptr;
+}
+
 CXFA_Text::CXFA_Text(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_SourceSet | XFA_XDPPACKET_Template |
-                 XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kSourceSet, XFA_XDPPACKET::kTemplate,
+                 XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Text,
                 {},
                 kTextAttributeData,
-                pdfium::MakeUnique<CJX_Object>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Object>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Text::~CXFA_Text() = default;
 
-WideString CXFA_Text::GetContent() {
+WideString CXFA_Text::GetContent() const {
   return JSObject()->GetContent(false);
 }
diff --git a/xfa/fxfa/parser/cxfa_text.h b/xfa/fxfa/parser/cxfa_text.h
index 811ce1a..2e82bf8 100644
--- a/xfa/fxfa/parser/cxfa_text.h
+++ b/xfa/fxfa/parser/cxfa_text.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,20 @@
 #ifndef XFA_FXFA_PARSER_CXFA_TEXT_H_
 #define XFA_FXFA_PARSER_CXFA_TEXT_H_
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
 class CXFA_Text final : public CXFA_Node {
  public:
-  CXFA_Text(CXFA_Document* doc, XFA_PacketType packet);
+  static CXFA_Text* FromNode(CXFA_Node* pNode);
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Text() override;
 
-  WideString GetContent();
+  WideString GetContent() const;
+
+ private:
+  CXFA_Text(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TEXT_H_
diff --git a/xfa/fxfa/parser/cxfa_textedit.cpp b/xfa/fxfa/parser/cxfa_textedit.cpp
index 7ff47c7..92885f1 100644
--- a/xfa/fxfa/parser/cxfa_textedit.cpp
+++ b/xfa/fxfa/parser/cxfa_textedit.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #include "xfa/fxfa/parser/cxfa_textedit.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kTextEditPropertyData[] = {
-    {XFA_Element::Margin, 1, 0},
-    {XFA_Element::Border, 1, 0},
-    {XFA_Element::Comb, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Margin, 1, {}},
+    {XFA_Element::Border, 1, {}},
+    {XFA_Element::Comb, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kTextEditAttributeData[] = {
@@ -35,12 +35,14 @@
 CXFA_TextEdit::CXFA_TextEdit(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::TextEdit,
                 kTextEditPropertyData,
                 kTextEditAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_TextEdit::~CXFA_TextEdit() = default;
 
diff --git a/xfa/fxfa/parser/cxfa_textedit.h b/xfa/fxfa/parser/cxfa_textedit.h
index d4cafa4..44f116a 100644
--- a/xfa/fxfa/parser/cxfa_textedit.h
+++ b/xfa/fxfa/parser/cxfa_textedit.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,10 +11,13 @@
 
 class CXFA_TextEdit final : public CXFA_Node {
  public:
-  CXFA_TextEdit(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_TextEdit() override;
 
   XFA_FFWidgetType GetDefaultFFWidgetType() const override;
+
+ private:
+  CXFA_TextEdit(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TEXTEDIT_H_
diff --git a/xfa/fxfa/parser/cxfa_thisproxy.cpp b/xfa/fxfa/parser/cxfa_thisproxy.cpp
index 3654409..c7eb1c3 100644
--- a/xfa/fxfa/parser/cxfa_thisproxy.cpp
+++ b/xfa/fxfa/parser/cxfa_thisproxy.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,25 @@
 #include "xfa/fxfa/parser/cxfa_thisproxy.h"
 
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/cxfa_script.h"
 
-CXFA_ThisProxy::CXFA_ThisProxy(CXFA_Node* pThisNode, CXFA_Node* pScriptNode)
-    : CXFA_Object(pThisNode->GetDocument(),
-                  XFA_ObjectType::ThisProxy,
-                  XFA_Element::Object,
-                  pdfium::MakeUnique<CJX_Object>(this)),
+CXFA_ThisProxy::CXFA_ThisProxy(CXFA_Node* pThisNode, CXFA_Script* pScriptNode)
+    : CXFA_Object(
+          pThisNode->GetDocument(),
+          XFA_ObjectType::ThisProxy,
+          XFA_Element::Object,
+          cppgc::MakeGarbageCollected<CJX_Object>(
+              pThisNode->GetDocument()->GetHeap()->GetAllocationHandle(),
+              this)),
       m_pThisNode(pThisNode),
       m_pScriptNode(pScriptNode) {}
 
 CXFA_ThisProxy::~CXFA_ThisProxy() = default;
+
+void CXFA_ThisProxy::Trace(cppgc::Visitor* visitor) const {
+  CXFA_Object::Trace(visitor);
+  visitor->Trace(m_pThisNode);
+  visitor->Trace(m_pScriptNode);
+}
diff --git a/xfa/fxfa/parser/cxfa_thisproxy.h b/xfa/fxfa/parser/cxfa_thisproxy.h
index 4ec6e4a..37624e8 100644
--- a/xfa/fxfa/parser/cxfa_thisproxy.h
+++ b/xfa/fxfa/parser/cxfa_thisproxy.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,22 +7,28 @@
 #ifndef XFA_FXFA_PARSER_CXFA_THISPROXY_H_
 #define XFA_FXFA_PARSER_CXFA_THISPROXY_H_
 
-#include "core/fxcrt/unowned_ptr.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
 
 class CXFA_Node;
+class CXFA_Script;
 
 class CXFA_ThisProxy final : public CXFA_Object {
  public:
-  CXFA_ThisProxy(CXFA_Node* pThisNode, CXFA_Node* pScriptNode);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ThisProxy() override;
 
-  CXFA_Node* GetThisNode() const { return m_pThisNode.Get(); }
-  CXFA_Node* GetScriptNode() const { return m_pScriptNode.Get(); }
+  void Trace(cppgc::Visitor* visitor) const override;
+
+  CXFA_Node* GetThisNode() const { return m_pThisNode; }
+  CXFA_Script* GetScriptNode() const { return m_pScriptNode; }
 
  private:
-  UnownedPtr<CXFA_Node> m_pThisNode;
-  UnownedPtr<CXFA_Node> m_pScriptNode;
+  CXFA_ThisProxy(CXFA_Node* pThisNode, CXFA_Script* pScriptNode);
+
+  cppgc::Member<CXFA_Node> m_pThisNode;
+  cppgc::Member<CXFA_Script> m_pScriptNode;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_THISPROXY_H_
diff --git a/xfa/fxfa/parser/cxfa_threshold.cpp b/xfa/fxfa/parser/cxfa_threshold.cpp
index 3d1863a..7b289af 100644
--- a/xfa/fxfa/parser/cxfa_threshold.cpp
+++ b/xfa/fxfa/parser/cxfa_threshold.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_threshold.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Threshold::CXFA_Threshold(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Threshold,
                 {},
                 kThresholdAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Threshold::~CXFA_Threshold() = default;
diff --git a/xfa/fxfa/parser/cxfa_threshold.h b/xfa/fxfa/parser/cxfa_threshold.h
index 597b134..25e7555 100644
--- a/xfa/fxfa/parser/cxfa_threshold.h
+++ b/xfa/fxfa/parser/cxfa_threshold.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Threshold final : public CXFA_Node {
  public:
-  CXFA_Threshold(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Threshold() override;
+
+ private:
+  CXFA_Threshold(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_THRESHOLD_H_
diff --git a/xfa/fxfa/parser/cxfa_time.cpp b/xfa/fxfa/parser/cxfa_time.cpp
index ea33713..9f54528 100644
--- a/xfa/fxfa/parser/cxfa_time.cpp
+++ b/xfa/fxfa/parser/cxfa_time.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_time.h"
 
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_Time::CXFA_Time(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Time,
                 {},
                 kTimeAttributeData,
-                pdfium::MakeUnique<CJX_Object>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Object>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Time::~CXFA_Time() = default;
diff --git a/xfa/fxfa/parser/cxfa_time.h b/xfa/fxfa/parser/cxfa_time.h
index c189bc5..2ab4efd 100644
--- a/xfa/fxfa/parser/cxfa_time.h
+++ b/xfa/fxfa/parser/cxfa_time.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Time final : public CXFA_Node {
  public:
-  CXFA_Time(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Time() override;
+
+ private:
+  CXFA_Time(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TIME_H_
diff --git a/xfa/fxfa/parser/cxfa_timepattern.cpp b/xfa/fxfa/parser/cxfa_timepattern.cpp
index 9264da2..e38b8b9 100644
--- a/xfa/fxfa/parser/cxfa_timepattern.cpp
+++ b/xfa/fxfa/parser/cxfa_timepattern.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_timepattern.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_TimePattern::CXFA_TimePattern(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::TimePattern,
                 {},
                 kTimePatternAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_TimePattern::~CXFA_TimePattern() = default;
diff --git a/xfa/fxfa/parser/cxfa_timepattern.h b/xfa/fxfa/parser/cxfa_timepattern.h
index 7c150a9..91df26b 100644
--- a/xfa/fxfa/parser/cxfa_timepattern.h
+++ b/xfa/fxfa/parser/cxfa_timepattern.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_TimePattern final : public CXFA_Node {
  public:
-  CXFA_TimePattern(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_TimePattern() override;
+
+ private:
+  CXFA_TimePattern(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TIMEPATTERN_H_
diff --git a/xfa/fxfa/parser/cxfa_timepatterns.cpp b/xfa/fxfa/parser/cxfa_timepatterns.cpp
index 1c92303..c8bc468 100644
--- a/xfa/fxfa/parser/cxfa_timepatterns.cpp
+++ b/xfa/fxfa/parser/cxfa_timepatterns.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_timepatterns.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kTimePatternsPropertyData[] = {
-    {XFA_Element::TimePattern, 4, 0},
+    {XFA_Element::TimePattern, 4, {}},
 };
 
 }  // namespace
@@ -20,11 +20,13 @@
 CXFA_TimePatterns::CXFA_TimePatterns(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::TimePatterns,
                 kTimePatternsPropertyData,
                 {},
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_TimePatterns::~CXFA_TimePatterns() = default;
diff --git a/xfa/fxfa/parser/cxfa_timepatterns.h b/xfa/fxfa/parser/cxfa_timepatterns.h
index 13c05c8..558d850 100644
--- a/xfa/fxfa/parser/cxfa_timepatterns.h
+++ b/xfa/fxfa/parser/cxfa_timepatterns.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_TimePatterns final : public CXFA_Node {
  public:
-  CXFA_TimePatterns(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_TimePatterns() override;
+
+ private:
+  CXFA_TimePatterns(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TIMEPATTERNS_H_
diff --git a/xfa/fxfa/parser/cxfa_timestamp.cpp b/xfa/fxfa/parser/cxfa_timestamp.cpp
index 68f1c50..c81c73c 100644
--- a/xfa/fxfa/parser/cxfa_timestamp.cpp
+++ b/xfa/fxfa/parser/cxfa_timestamp.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_timestamp.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -25,11 +25,13 @@
 CXFA_TimeStamp::CXFA_TimeStamp(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::TimeStamp,
                 {},
                 kTimeStampAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_TimeStamp::~CXFA_TimeStamp() = default;
diff --git a/xfa/fxfa/parser/cxfa_timestamp.h b/xfa/fxfa/parser/cxfa_timestamp.h
index 6a6c1ac..fec0f01 100644
--- a/xfa/fxfa/parser/cxfa_timestamp.h
+++ b/xfa/fxfa/parser/cxfa_timestamp.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_TimeStamp final : public CXFA_Node {
  public:
-  CXFA_TimeStamp(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_TimeStamp() override;
+
+ private:
+  CXFA_TimeStamp(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TIMESTAMP_H_
diff --git a/xfa/fxfa/parser/cxfa_timezoneprovider.cpp b/xfa/fxfa/parser/cxfa_timezoneprovider.cpp
index 0469563..44ed2ab 100644
--- a/xfa/fxfa/parser/cxfa_timezoneprovider.cpp
+++ b/xfa/fxfa/parser/cxfa_timezoneprovider.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,29 +6,27 @@
 
 #include "xfa/fxfa/parser/cxfa_timezoneprovider.h"
 
+#include <stdlib.h>
 #include <time.h>
 
 #include "build/build_config.h"
 
 static bool g_bProviderTimeZoneSet = false;
 
-CXFA_TimeZoneProvider::CXFA_TimeZoneProvider() {
-#if defined(OS_WIN)
-  if (!g_bProviderTimeZoneSet) {
-    g_bProviderTimeZoneSet = true;
-    _tzset();
-  }
-  m_tz.tzHour = static_cast<int8_t>(_timezone / 3600 * -1);
-  m_tz.tzMinute = static_cast<int8_t>((abs(_timezone) % 3600) / 60);
+#if BUILDFLAG(IS_WIN)
+#define TIMEZONE _timezone
+#define TZSET _tzset
 #else
+#define TZSET tzset
+#define TIMEZONE timezone
+#endif
+
+CXFA_TimeZoneProvider::CXFA_TimeZoneProvider() {
   if (!g_bProviderTimeZoneSet) {
     g_bProviderTimeZoneSet = true;
-    tzset();
+    TZSET();
   }
-  m_tz.tzHour = static_cast<int8_t>(timezone / 3600 * -1);
-  m_tz.tzMinute =
-      static_cast<int8_t>((abs(static_cast<int>(timezone)) % 3600) / 60);
-#endif
+  tz_minutes_ = TIMEZONE / -60;
 }
 
-CXFA_TimeZoneProvider::~CXFA_TimeZoneProvider() {}
+CXFA_TimeZoneProvider::~CXFA_TimeZoneProvider() = default;
diff --git a/xfa/fxfa/parser/cxfa_timezoneprovider.h b/xfa/fxfa/parser/cxfa_timezoneprovider.h
index 753fbb7..faa12fe 100644
--- a/xfa/fxfa/parser/cxfa_timezoneprovider.h
+++ b/xfa/fxfa/parser/cxfa_timezoneprovider.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,17 +7,15 @@
 #ifndef XFA_FXFA_PARSER_CXFA_TIMEZONEPROVIDER_H_
 #define XFA_FXFA_PARSER_CXFA_TIMEZONEPROVIDER_H_
 
-#include "core/fxcrt/cfx_datetime.h"
-
 class CXFA_TimeZoneProvider {
  public:
   CXFA_TimeZoneProvider();
   ~CXFA_TimeZoneProvider();
 
-  FX_TIMEZONE GetTimeZone() const { return m_tz; }
+  int GetTimeZoneInMinutes() const { return tz_minutes_; }
 
  private:
-  FX_TIMEZONE m_tz;
+  int tz_minutes_;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TIMEZONEPROVIDER_H_
diff --git a/xfa/fxfa/parser/cxfa_timezoneprovider_unittest.cpp b/xfa/fxfa/parser/cxfa_timezoneprovider_unittest.cpp
new file mode 100644
index 0000000..74fcc2a
--- /dev/null
+++ b/xfa/fxfa/parser/cxfa_timezoneprovider_unittest.cpp
@@ -0,0 +1,58 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fxfa/parser/cxfa_timezoneprovider.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/scoped_set_tz.h"
+
+TEST(CXFA_TimeZoneProviderTest, HourOffsets) {
+  {
+    ScopedSetTZ scoped_set_tz("UTC");
+    EXPECT_EQ(0, CXFA_TimeZoneProvider().GetTimeZoneInMinutes());
+  }
+  {
+    ScopedSetTZ scoped_set_tz("UTC+1");
+    EXPECT_EQ(-60, CXFA_TimeZoneProvider().GetTimeZoneInMinutes());
+  }
+  {
+    ScopedSetTZ scoped_set_tz("UTC-1");
+    EXPECT_EQ(60, CXFA_TimeZoneProvider().GetTimeZoneInMinutes());
+  }
+  {
+    ScopedSetTZ scoped_set_tz("UTC+14");
+    EXPECT_EQ(-840, CXFA_TimeZoneProvider().GetTimeZoneInMinutes());
+  }
+  {
+    ScopedSetTZ scoped_set_tz("UTC-14");
+    EXPECT_EQ(840, CXFA_TimeZoneProvider().GetTimeZoneInMinutes());
+  }
+}
+
+TEST(CXFA_TimeZoneProviderTest, HalfHourOffsets) {
+  {
+    ScopedSetTZ scoped_set_tz("UTC+0:30");
+    EXPECT_EQ(-30, CXFA_TimeZoneProvider().GetTimeZoneInMinutes());
+  }
+  {
+    ScopedSetTZ scoped_set_tz("UTC-0:30");
+    EXPECT_EQ(30, CXFA_TimeZoneProvider().GetTimeZoneInMinutes());
+  }
+  {
+    ScopedSetTZ scoped_set_tz("UTC+1:30");
+    EXPECT_EQ(-90, CXFA_TimeZoneProvider().GetTimeZoneInMinutes());
+  }
+  {
+    ScopedSetTZ scoped_set_tz("UTC-1:30");
+    EXPECT_EQ(90, CXFA_TimeZoneProvider().GetTimeZoneInMinutes());
+  }
+  {
+    ScopedSetTZ scoped_set_tz("UTC+9:30");
+    EXPECT_EQ(-570, CXFA_TimeZoneProvider().GetTimeZoneInMinutes());
+  }
+  {
+    ScopedSetTZ scoped_set_tz("UTC-9:30");
+    EXPECT_EQ(570, CXFA_TimeZoneProvider().GetTimeZoneInMinutes());
+  }
+}
diff --git a/xfa/fxfa/parser/cxfa_to.cpp b/xfa/fxfa/parser/cxfa_to.cpp
index cb400c0..06b7979 100644
--- a/xfa/fxfa/parser/cxfa_to.cpp
+++ b/xfa/fxfa/parser/cxfa_to.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_to.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_To::CXFA_To(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::To,
                 {},
                 kToAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_To::~CXFA_To() = default;
diff --git a/xfa/fxfa/parser/cxfa_to.h b/xfa/fxfa/parser/cxfa_to.h
index 8510bc2..f0cbf62 100644
--- a/xfa/fxfa/parser/cxfa_to.h
+++ b/xfa/fxfa/parser/cxfa_to.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_To final : public CXFA_Node {
  public:
-  CXFA_To(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_To() override;
+
+ private:
+  CXFA_To(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TO_H_
diff --git a/xfa/fxfa/parser/cxfa_tooltip.cpp b/xfa/fxfa/parser/cxfa_tooltip.cpp
index 2fecedd..e143488 100644
--- a/xfa/fxfa/parser/cxfa_tooltip.cpp
+++ b/xfa/fxfa/parser/cxfa_tooltip.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_tooltip.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_ToolTip::CXFA_ToolTip(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::TextNode,
                 XFA_Element::ToolTip,
                 {},
                 kToolTipAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ToolTip::~CXFA_ToolTip() = default;
diff --git a/xfa/fxfa/parser/cxfa_tooltip.h b/xfa/fxfa/parser/cxfa_tooltip.h
index f46ca62..f78abbe 100644
--- a/xfa/fxfa/parser/cxfa_tooltip.h
+++ b/xfa/fxfa/parser/cxfa_tooltip.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_ToolTip final : public CXFA_Node {
  public:
-  CXFA_ToolTip(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ToolTip() override;
+
+ private:
+  CXFA_ToolTip(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TOOLTIP_H_
diff --git a/xfa/fxfa/parser/cxfa_trace.cpp b/xfa/fxfa/parser/cxfa_trace.cpp
index 741805d..08c9f1b 100644
--- a/xfa/fxfa/parser/cxfa_trace.cpp
+++ b/xfa/fxfa/parser/cxfa_trace.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_trace.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Trace::CXFA_Trace(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Trace,
                 {},
                 kTraceAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Trace::~CXFA_Trace() = default;
diff --git a/xfa/fxfa/parser/cxfa_trace.h b/xfa/fxfa/parser/cxfa_trace.h
index 1d76f73..f36b8e9 100644
--- a/xfa/fxfa/parser/cxfa_trace.h
+++ b/xfa/fxfa/parser/cxfa_trace.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Trace final : public CXFA_Node {
  public:
-  CXFA_Trace(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Trace() override;
+
+ private:
+  CXFA_Trace(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TRACE_H_
diff --git a/xfa/fxfa/parser/cxfa_transform.cpp b/xfa/fxfa/parser/cxfa_transform.cpp
index e00ddc3..15e15d0 100644
--- a/xfa/fxfa/parser/cxfa_transform.cpp
+++ b/xfa/fxfa/parser/cxfa_transform.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #include "xfa/fxfa/parser/cxfa_transform.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kTransformPropertyData[] = {
-    {XFA_Element::Whitespace, 1, 0},  {XFA_Element::Rename, 1, 0},
-    {XFA_Element::IfEmpty, 1, 0},     {XFA_Element::Presence, 1, 0},
-    {XFA_Element::Picture, 1, 0},     {XFA_Element::NameAttr, 1, 0},
-    {XFA_Element::GroupParent, 1, 0},
+    {XFA_Element::Whitespace, 1, {}},  {XFA_Element::Rename, 1, {}},
+    {XFA_Element::IfEmpty, 1, {}},     {XFA_Element::Presence, 1, {}},
+    {XFA_Element::Picture, 1, {}},     {XFA_Element::NameAttr, 1, {}},
+    {XFA_Element::GroupParent, 1, {}},
 };
 
 const CXFA_Node::AttributeData kTransformAttributeData[] = {
@@ -29,11 +29,13 @@
 CXFA_Transform::CXFA_Transform(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Transform,
                 kTransformPropertyData,
                 kTransformAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Transform::~CXFA_Transform() = default;
diff --git a/xfa/fxfa/parser/cxfa_transform.h b/xfa/fxfa/parser/cxfa_transform.h
index 6409c6c..37bab1f 100644
--- a/xfa/fxfa/parser/cxfa_transform.h
+++ b/xfa/fxfa/parser/cxfa_transform.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Transform final : public CXFA_Node {
  public:
-  CXFA_Transform(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Transform() override;
+
+ private:
+  CXFA_Transform(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TRANSFORM_H_
diff --git a/xfa/fxfa/parser/cxfa_traversal.cpp b/xfa/fxfa/parser/cxfa_traversal.cpp
index ad367e9..4bc7094 100644
--- a/xfa/fxfa/parser/cxfa_traversal.cpp
+++ b/xfa/fxfa/parser/cxfa_traversal.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_traversal.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kTraversalPropertyData[] = {
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kTraversalAttributeData[] = {
@@ -26,11 +26,13 @@
 CXFA_Traversal::CXFA_Traversal(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Traversal,
                 kTraversalPropertyData,
                 kTraversalAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Traversal::~CXFA_Traversal() = default;
diff --git a/xfa/fxfa/parser/cxfa_traversal.h b/xfa/fxfa/parser/cxfa_traversal.h
index 154211b..46d647f 100644
--- a/xfa/fxfa/parser/cxfa_traversal.h
+++ b/xfa/fxfa/parser/cxfa_traversal.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Traversal final : public CXFA_Node {
  public:
-  CXFA_Traversal(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Traversal() override;
+
+ private:
+  CXFA_Traversal(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TRAVERSAL_H_
diff --git a/xfa/fxfa/parser/cxfa_traverse.cpp b/xfa/fxfa/parser/cxfa_traverse.cpp
index 7afc59f..d5962b2 100644
--- a/xfa/fxfa/parser/cxfa_traverse.cpp
+++ b/xfa/fxfa/parser/cxfa_traverse.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_traverse.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kTraversePropertyData[] = {
-    {XFA_Element::Script, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Script, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kTraverseAttributeData[] = {
@@ -30,11 +30,13 @@
 CXFA_Traverse::CXFA_Traverse(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Traverse,
                 kTraversePropertyData,
                 kTraverseAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
-CXFA_Traverse::~CXFA_Traverse() {}
+CXFA_Traverse::~CXFA_Traverse() = default;
diff --git a/xfa/fxfa/parser/cxfa_traverse.h b/xfa/fxfa/parser/cxfa_traverse.h
index dec5b89..df4934e 100644
--- a/xfa/fxfa/parser/cxfa_traverse.h
+++ b/xfa/fxfa/parser/cxfa_traverse.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Traverse final : public CXFA_Node {
  public:
-  CXFA_Traverse(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Traverse() override;
+
+ private:
+  CXFA_Traverse(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TRAVERSE_H_
diff --git a/xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h b/xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h
index d867f9f..1208d0f 100644
--- a/xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h
+++ b/xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h b/xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h
index 90f39a8..a6e7e08 100644
--- a/xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h
+++ b/xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fxfa/parser/cxfa_treelist.cpp b/xfa/fxfa/parser/cxfa_treelist.cpp
index 2dbec60..86cc523 100644
--- a/xfa/fxfa/parser/cxfa_treelist.cpp
+++ b/xfa/fxfa/parser/cxfa_treelist.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,23 +6,23 @@
 
 #include "xfa/fxfa/parser/cxfa_treelist.h"
 
-#include <memory>
-
 #include "core/fxcrt/fx_extension.h"
 #include "fxjs/xfa/cjx_treelist.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-CXFA_TreeList::CXFA_TreeList(CXFA_Document* pDocument)
-    : CXFA_List(pDocument,
+CXFA_TreeList::CXFA_TreeList(CXFA_Document* doc)
+    : CXFA_List(doc,
                 XFA_ObjectType::TreeList,
                 XFA_Element::TreeList,
-                pdfium::MakeUnique<CJX_TreeList>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TreeList>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_TreeList::~CXFA_TreeList() = default;
 
 CXFA_Node* CXFA_TreeList::NamedItem(WideStringView wsName) {
-  uint32_t dwHashCode = FX_HashCode_GetW(wsName, false);
+  uint32_t dwHashCode = FX_HashCode_GetW(wsName);
   size_t count = GetLength();
   for (size_t i = 0; i < count; i++) {
     CXFA_Node* ret = Item(i);
diff --git a/xfa/fxfa/parser/cxfa_treelist.h b/xfa/fxfa/parser/cxfa_treelist.h
index a194e32..297a929 100644
--- a/xfa/fxfa/parser/cxfa_treelist.h
+++ b/xfa/fxfa/parser/cxfa_treelist.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,17 +7,20 @@
 #ifndef XFA_FXFA_PARSER_CXFA_TREELIST_H_
 #define XFA_FXFA_PARSER_CXFA_TREELIST_H_
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 #include "xfa/fxfa/parser/cxfa_list.h"
 
 class CXFA_Node;
 
 class CXFA_TreeList : public CXFA_List {
  public:
-  explicit CXFA_TreeList(CXFA_Document* pDocument);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_TreeList() override;
 
   CXFA_Node* NamedItem(WideStringView wsName);
+
+ protected:
+  explicit CXFA_TreeList(CXFA_Document* pDocument);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TREELIST_H_
diff --git a/xfa/fxfa/parser/cxfa_type.cpp b/xfa/fxfa/parser/cxfa_type.cpp
index ba36158..690946b 100644
--- a/xfa/fxfa/parser/cxfa_type.cpp
+++ b/xfa/fxfa/parser/cxfa_type.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_type.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Type::CXFA_Type(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Type,
                 {},
                 kTypeAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Type::~CXFA_Type() = default;
diff --git a/xfa/fxfa/parser/cxfa_type.h b/xfa/fxfa/parser/cxfa_type.h
index ef696fd..c7d26ff 100644
--- a/xfa/fxfa/parser/cxfa_type.h
+++ b/xfa/fxfa/parser/cxfa_type.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Type final : public CXFA_Node {
  public:
-  CXFA_Type(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Type() override;
+
+ private:
+  CXFA_Type(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TYPE_H_
diff --git a/xfa/fxfa/parser/cxfa_typeface.cpp b/xfa/fxfa/parser/cxfa_typeface.cpp
index 0d2c2dc..04bed2f 100644
--- a/xfa/fxfa/parser/cxfa_typeface.cpp
+++ b/xfa/fxfa/parser/cxfa_typeface.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_typeface.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -20,11 +20,13 @@
 CXFA_Typeface::CXFA_Typeface(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::Typeface,
                 {},
                 kTypefaceAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Typeface::~CXFA_Typeface() = default;
diff --git a/xfa/fxfa/parser/cxfa_typeface.h b/xfa/fxfa/parser/cxfa_typeface.h
index 7b07cae..47522bc 100644
--- a/xfa/fxfa/parser/cxfa_typeface.h
+++ b/xfa/fxfa/parser/cxfa_typeface.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Typeface final : public CXFA_Node {
  public:
-  CXFA_Typeface(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Typeface() override;
+
+ private:
+  CXFA_Typeface(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TYPEFACE_H_
diff --git a/xfa/fxfa/parser/cxfa_typefaces.cpp b/xfa/fxfa/parser/cxfa_typefaces.cpp
index dfc35e0..40edbe4 100644
--- a/xfa/fxfa/parser/cxfa_typefaces.cpp
+++ b/xfa/fxfa/parser/cxfa_typefaces.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,18 @@
 #include "xfa/fxfa/parser/cxfa_typefaces.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 CXFA_Typefaces::CXFA_Typefaces(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_LocaleSet,
+                XFA_XDPPACKET::kLocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::Typefaces,
                 {},
                 {},
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Typefaces::~CXFA_Typefaces() = default;
diff --git a/xfa/fxfa/parser/cxfa_typefaces.h b/xfa/fxfa/parser/cxfa_typefaces.h
index b65c57d..95a01fa 100644
--- a/xfa/fxfa/parser/cxfa_typefaces.h
+++ b/xfa/fxfa/parser/cxfa_typefaces.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Typefaces final : public CXFA_Node {
  public:
-  CXFA_Typefaces(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Typefaces() override;
+
+ private:
+  CXFA_Typefaces(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TYPEFACES_H_
diff --git a/xfa/fxfa/parser/cxfa_ui.cpp b/xfa/fxfa/parser/cxfa_ui.cpp
index 1630a3d..847e278 100644
--- a/xfa/fxfa/parser/cxfa_ui.cpp
+++ b/xfa/fxfa/parser/cxfa_ui.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,24 +7,24 @@
 #include "xfa/fxfa/parser/cxfa_ui.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kUiPropertyData[] = {
-    {XFA_Element::CheckButton, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::ChoiceList, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::DefaultUi, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Barcode, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Button, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::DateTimeEdit, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Picture, 1, 0},
-    {XFA_Element::ImageEdit, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::PasswordEdit, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::NumericEdit, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Signature, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::TextEdit, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Extras, 1, 0},
+constexpr CXFA_Node::PropertyData kUiPropertyData[] = {
+    {XFA_Element::CheckButton, 1, {XFA_PropertyFlag::kOneOf}},
+    {XFA_Element::ChoiceList, 1, {XFA_PropertyFlag::kOneOf}},
+    {XFA_Element::DefaultUi, 1, {XFA_PropertyFlag::kOneOf}},
+    {XFA_Element::Barcode, 1, {XFA_PropertyFlag::kOneOf}},
+    {XFA_Element::Button, 1, {XFA_PropertyFlag::kOneOf}},
+    {XFA_Element::DateTimeEdit, 1, {XFA_PropertyFlag::kOneOf}},
+    {XFA_Element::Picture, 1, {}},
+    {XFA_Element::ImageEdit, 1, {XFA_PropertyFlag::kOneOf}},
+    {XFA_Element::PasswordEdit, 1, {XFA_PropertyFlag::kOneOf}},
+    {XFA_Element::NumericEdit, 1, {XFA_PropertyFlag::kOneOf}},
+    {XFA_Element::Signature, 1, {XFA_PropertyFlag::kOneOf}},
+    {XFA_Element::TextEdit, 1, {XFA_PropertyFlag::kOneOf}},
+    {XFA_Element::Extras, 1, {}},
 };
 
 const CXFA_Node::AttributeData kUiAttributeData[] = {
@@ -38,12 +38,14 @@
 CXFA_Ui::CXFA_Ui(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Ui,
                 kUiPropertyData,
                 kUiAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Ui::~CXFA_Ui() = default;
 
@@ -51,7 +53,7 @@
   for (auto& prop : kUiPropertyData) {
     if (prop.property != child->GetElementType())
       continue;
-    if (!!(prop.flags & XFA_PROPERTYFLAG_OneOf))
+    if (!!(prop.flags & XFA_PropertyFlag::kOneOf))
       return true;
   }
   return false;
diff --git a/xfa/fxfa/parser/cxfa_ui.h b/xfa/fxfa/parser/cxfa_ui.h
index d479e95..945e879 100644
--- a/xfa/fxfa/parser/cxfa_ui.h
+++ b/xfa/fxfa/parser/cxfa_ui.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,10 +11,13 @@
 
 class CXFA_Ui final : public CXFA_Node {
  public:
-  CXFA_Ui(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Ui() override;
 
   bool IsAOneOfChild(CXFA_Node* child) const;
+
+ private:
+  CXFA_Ui(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_UI_H_
diff --git a/xfa/fxfa/parser/cxfa_update.cpp b/xfa/fxfa/parser/cxfa_update.cpp
index 6150ff5..5d180aa 100644
--- a/xfa/fxfa/parser/cxfa_update.cpp
+++ b/xfa/fxfa/parser/cxfa_update.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_update.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_Update::CXFA_Update(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_SourceSet,
+                XFA_XDPPACKET::kSourceSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::Update,
                 {},
                 kUpdateAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Update::~CXFA_Update() = default;
diff --git a/xfa/fxfa/parser/cxfa_update.h b/xfa/fxfa/parser/cxfa_update.h
index 3e6bb6c..3e3fd77 100644
--- a/xfa/fxfa/parser/cxfa_update.h
+++ b/xfa/fxfa/parser/cxfa_update.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Update final : public CXFA_Node {
  public:
-  CXFA_Update(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Update() override;
+
+ private:
+  CXFA_Update(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_UPDATE_H_
diff --git a/xfa/fxfa/parser/cxfa_uri.cpp b/xfa/fxfa/parser/cxfa_uri.cpp
index 79d1ea6..26dd953 100644
--- a/xfa/fxfa/parser/cxfa_uri.cpp
+++ b/xfa/fxfa/parser/cxfa_uri.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_uri.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -25,11 +25,13 @@
 CXFA_Uri::CXFA_Uri(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Config | XFA_XDPPACKET_ConnectionSet),
+                {XFA_XDPPACKET::kConfig, XFA_XDPPACKET::kConnectionSet},
                 XFA_ObjectType::TextNode,
                 XFA_Element::Uri,
                 {},
                 kUriAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Uri::~CXFA_Uri() = default;
diff --git a/xfa/fxfa/parser/cxfa_uri.h b/xfa/fxfa/parser/cxfa_uri.h
index 96252fc..bfe6b73 100644
--- a/xfa/fxfa/parser/cxfa_uri.h
+++ b/xfa/fxfa/parser/cxfa_uri.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Uri final : public CXFA_Node {
  public:
-  CXFA_Uri(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Uri() override;
+
+ private:
+  CXFA_Uri(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_URI_H_
diff --git a/xfa/fxfa/parser/cxfa_user.cpp b/xfa/fxfa/parser/cxfa_user.cpp
index fb36d34..1e8176c 100644
--- a/xfa/fxfa/parser/cxfa_user.cpp
+++ b/xfa/fxfa/parser/cxfa_user.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_user.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_User::CXFA_User(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_SourceSet,
+                XFA_XDPPACKET::kSourceSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::User,
                 {},
                 kUserAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_User::~CXFA_User() = default;
diff --git a/xfa/fxfa/parser/cxfa_user.h b/xfa/fxfa/parser/cxfa_user.h
index eca1a37..08641fc 100644
--- a/xfa/fxfa/parser/cxfa_user.h
+++ b/xfa/fxfa/parser/cxfa_user.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_User final : public CXFA_Node {
  public:
-  CXFA_User(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_User() override;
+
+ private:
+  CXFA_User(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_USER_H_
diff --git a/xfa/fxfa/parser/cxfa_validate.cpp b/xfa/fxfa/parser/cxfa_validate.cpp
index d65b170..2ca3811 100644
--- a/xfa/fxfa/parser/cxfa_validate.cpp
+++ b/xfa/fxfa/parser/cxfa_validate.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,7 @@
 
 #include "fxjs/xfa/cjx_node.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_message.h"
 #include "xfa/fxfa/parser/cxfa_picture.h"
 #include "xfa/fxfa/parser/cxfa_script.h"
@@ -17,10 +17,10 @@
 namespace {
 
 const CXFA_Node::PropertyData kValidatePropertyData[] = {
-    {XFA_Element::Message, 1, 0},
-    {XFA_Element::Picture, 1, 0},
-    {XFA_Element::Script, 1, 0},
-    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Message, 1, {}},
+    {XFA_Element::Picture, 1, {}},
+    {XFA_Element::Script, 1, {}},
+    {XFA_Element::Extras, 1, {}},
 };
 const CXFA_Node::AttributeData kValidateAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
@@ -43,15 +43,17 @@
 }  // namespace
 
 CXFA_Validate::CXFA_Validate(CXFA_Document* doc, XFA_PacketType packet)
-    : CXFA_Node(
-          doc,
-          packet,
-          (XFA_XDPPACKET_Config | XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
-          XFA_ObjectType::ContentNode,
-          XFA_Element::Validate,
-          kValidatePropertyData,
-          kValidateAttributeData,
-          pdfium::MakeUnique<CJX_Node>(this)) {}
+    : CXFA_Node(doc,
+                packet,
+                {XFA_XDPPACKET::kConfig, XFA_XDPPACKET::kTemplate,
+                 XFA_XDPPACKET::kForm},
+                XFA_ObjectType::ContentNode,
+                XFA_Element::Validate,
+                kValidatePropertyData,
+                kValidateAttributeData,
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Validate::~CXFA_Validate() = default;
 
@@ -60,10 +62,11 @@
 }
 
 void CXFA_Validate::SetNullTest(const WideString& wsValue) {
-  Optional<XFA_AttributeValue> item =
+  absl::optional<XFA_AttributeValue> item =
       XFA_GetAttributeValueByName(wsValue.AsStringView());
-  JSObject()->SetEnum(XFA_Attribute::NullTest,
-                      item ? *item : XFA_AttributeValue::Disabled, false);
+  JSObject()->SetEnum(
+      XFA_Attribute::NullTest,
+      item.has_value() ? item.value() : XFA_AttributeValue::Disabled, false);
 }
 
 XFA_AttributeValue CXFA_Validate::GetNullTest() {
@@ -138,13 +141,12 @@
 
   CXFA_Node* pTextNode = pNode->CreateSamePacketNode(XFA_Element::Text);
   pNode->InsertChildAndNotify(pTextNode, nullptr);
-  pTextNode->JSObject()->SetCData(XFA_Attribute::Name, wsMessageType, false,
-                                  false);
+  pTextNode->JSObject()->SetCData(XFA_Attribute::Name, wsMessageType);
   pTextNode->JSObject()->SetContent(wsMessage, wsMessage, false, false, true);
 }
 
-WideString CXFA_Validate::GetPicture() {
-  CXFA_Picture* pNode = GetChild<CXFA_Picture>(0, XFA_Element::Picture, false);
+WideString CXFA_Validate::GetPicture() const {
+  const auto* pNode = GetChild<CXFA_Picture>(0, XFA_Element::Picture, false);
   return pNode ? pNode->JSObject()->GetContent(false) : WideString();
 }
 
diff --git a/xfa/fxfa/parser/cxfa_validate.h b/xfa/fxfa/parser/cxfa_validate.h
index 0018cf9..e64a7ca 100644
--- a/xfa/fxfa/parser/cxfa_validate.h
+++ b/xfa/fxfa/parser/cxfa_validate.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,7 +13,7 @@
 
 class CXFA_Validate final : public CXFA_Node {
  public:
-  CXFA_Validate(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Validate() override;
 
   XFA_AttributeValue GetFormatTest();
@@ -30,10 +30,12 @@
   WideString GetScriptMessageText();
   void SetScriptMessageText(const WideString& wsMessage);
 
-  WideString GetPicture();
+  WideString GetPicture() const;
   CXFA_Script* GetScriptIfExists();
 
  private:
+  CXFA_Validate(CXFA_Document* doc, XFA_PacketType packet);
+
   WideString GetMessageText(const WideString& wsMessageType);
   void SetMessageText(const WideString& wsMessageType,
                       const WideString& wsMessage);
diff --git a/xfa/fxfa/parser/cxfa_validateapprovalsignatures.cpp b/xfa/fxfa/parser/cxfa_validateapprovalsignatures.cpp
index d0aa5fd..6b1bfb0 100644
--- a/xfa/fxfa/parser/cxfa_validateapprovalsignatures.cpp
+++ b/xfa/fxfa/parser/cxfa_validateapprovalsignatures.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_validateapprovalsignatures.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
     XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::ValidateApprovalSignatures,
                 {},
                 kValidateApprovalSignaturesAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ValidateApprovalSignatures::~CXFA_ValidateApprovalSignatures() = default;
diff --git a/xfa/fxfa/parser/cxfa_validateapprovalsignatures.h b/xfa/fxfa/parser/cxfa_validateapprovalsignatures.h
index 0504168..038a1f2 100644
--- a/xfa/fxfa/parser/cxfa_validateapprovalsignatures.h
+++ b/xfa/fxfa/parser/cxfa_validateapprovalsignatures.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_ValidateApprovalSignatures final : public CXFA_Node {
  public:
-  CXFA_ValidateApprovalSignatures(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ValidateApprovalSignatures() override;
+
+ private:
+  CXFA_ValidateApprovalSignatures(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_VALIDATEAPPROVALSIGNATURES_H_
diff --git a/xfa/fxfa/parser/cxfa_validationmessaging.cpp b/xfa/fxfa/parser/cxfa_validationmessaging.cpp
index 687f37c..2ba30aa 100644
--- a/xfa/fxfa/parser/cxfa_validationmessaging.cpp
+++ b/xfa/fxfa/parser/cxfa_validationmessaging.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_validationmessaging.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -22,11 +22,13 @@
                                                    XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::ValidationMessaging,
                 {},
                 kValidationMessagingAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ValidationMessaging::~CXFA_ValidationMessaging() = default;
diff --git a/xfa/fxfa/parser/cxfa_validationmessaging.h b/xfa/fxfa/parser/cxfa_validationmessaging.h
index 1131788..c0f475d 100644
--- a/xfa/fxfa/parser/cxfa_validationmessaging.h
+++ b/xfa/fxfa/parser/cxfa_validationmessaging.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_ValidationMessaging final : public CXFA_Node {
  public:
-  CXFA_ValidationMessaging(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ValidationMessaging() override;
+
+ private:
+  CXFA_ValidationMessaging(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_VALIDATIONMESSAGING_H_
diff --git a/xfa/fxfa/parser/cxfa_value.cpp b/xfa/fxfa/parser/cxfa_value.cpp
index 979eabc..48f3b98 100644
--- a/xfa/fxfa/parser/cxfa_value.cpp
+++ b/xfa/fxfa/parser/cxfa_value.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,8 +8,8 @@
 
 #include "fxjs/xfa/cjx_node.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_arc.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_exdata.h"
 #include "xfa/fxfa/parser/cxfa_image.h"
 #include "xfa/fxfa/parser/cxfa_line.h"
@@ -17,23 +17,23 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kValuePropertyData[] = {
-    {XFA_Element::Arc, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Text, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Time, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::DateTime, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Image, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Decimal, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Boolean, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Integer, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::ExData, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Rectangle, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Date, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Float, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Line, 1, XFA_PROPERTYFLAG_OneOf},
+constexpr CXFA_Node::PropertyData kValuePropertyData[] = {
+    {XFA_Element::Arc, 1, XFA_PropertyFlag::kOneOf},
+    {XFA_Element::Text, 1, XFA_PropertyFlag::kOneOf},
+    {XFA_Element::Time, 1, XFA_PropertyFlag::kOneOf},
+    {XFA_Element::DateTime, 1, XFA_PropertyFlag::kOneOf},
+    {XFA_Element::Image, 1, XFA_PropertyFlag::kOneOf},
+    {XFA_Element::Decimal, 1, XFA_PropertyFlag::kOneOf},
+    {XFA_Element::Boolean, 1, XFA_PropertyFlag::kOneOf},
+    {XFA_Element::Integer, 1, XFA_PropertyFlag::kOneOf},
+    {XFA_Element::ExData, 1, XFA_PropertyFlag::kOneOf},
+    {XFA_Element::Rectangle, 1, XFA_PropertyFlag::kOneOf},
+    {XFA_Element::Date, 1, XFA_PropertyFlag::kOneOf},
+    {XFA_Element::Float, 1, XFA_PropertyFlag::kOneOf},
+    {XFA_Element::Line, 1, XFA_PropertyFlag::kOneOf},
 };
 
-const CXFA_Node::AttributeData kValueAttributeData[] = {
+constexpr CXFA_Node::AttributeData kValueAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Relevant, XFA_AttributeType::CData, nullptr},
@@ -46,12 +46,14 @@
 CXFA_Value::CXFA_Value(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::Node,
                 XFA_Element::Value,
                 kValuePropertyData,
                 kValueAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Value::~CXFA_Value() = default;
 
@@ -68,43 +70,25 @@
 }
 
 CXFA_Arc* CXFA_Value::GetArcIfExists() const {
-  CXFA_Node* node = GetFirstChild();
-  if (!node || node->GetElementType() != XFA_Element::Arc)
-    return nullptr;
-  return static_cast<CXFA_Arc*>(node);
+  return CXFA_Arc::FromNode(GetFirstChild());
 }
 
 CXFA_Line* CXFA_Value::GetLineIfExists() const {
-  CXFA_Node* node = GetFirstChild();
-  if (!node || node->GetElementType() != XFA_Element::Line)
-    return nullptr;
-  return static_cast<CXFA_Line*>(node);
+  return CXFA_Line::FromNode(GetFirstChild());
 }
 
 CXFA_Rectangle* CXFA_Value::GetRectangleIfExists() const {
-  CXFA_Node* node = GetFirstChild();
-  if (!node || node->GetElementType() != XFA_Element::Rectangle)
-    return nullptr;
-  return static_cast<CXFA_Rectangle*>(node);
+  return CXFA_Rectangle::FromNode(GetFirstChild());
 }
 
 CXFA_Text* CXFA_Value::GetTextIfExists() const {
-  CXFA_Node* node = GetFirstChild();
-  if (!node || node->GetElementType() != XFA_Element::Text)
-    return nullptr;
-  return static_cast<CXFA_Text*>(node);
+  return CXFA_Text::FromNode(GetFirstChild());
 }
 
 CXFA_ExData* CXFA_Value::GetExDataIfExists() const {
-  CXFA_Node* node = GetFirstChild();
-  if (!node || node->GetElementType() != XFA_Element::ExData)
-    return nullptr;
-  return static_cast<CXFA_ExData*>(node);
+  return CXFA_ExData::FromNode(GetFirstChild());
 }
 
 CXFA_Image* CXFA_Value::GetImageIfExists() const {
-  CXFA_Node* node = GetFirstChild();
-  if (!node || node->GetElementType() != XFA_Element::Image)
-    return nullptr;
-  return static_cast<CXFA_Image*>(node);
+  return CXFA_Image::FromNode(GetFirstChild());
 }
diff --git a/xfa/fxfa/parser/cxfa_value.h b/xfa/fxfa/parser/cxfa_value.h
index ccd4c2c..5fb3fab 100644
--- a/xfa/fxfa/parser/cxfa_value.h
+++ b/xfa/fxfa/parser/cxfa_value.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #ifndef XFA_FXFA_PARSER_CXFA_VALUE_H_
 #define XFA_FXFA_PARSER_CXFA_VALUE_H_
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_text.h"
 
@@ -19,7 +19,7 @@
 
 class CXFA_Value final : public CXFA_Node {
  public:
-  CXFA_Value(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Value() override;
 
   XFA_Element GetChildValueClassID() const;
@@ -30,6 +30,9 @@
   CXFA_Text* GetTextIfExists() const;
   CXFA_ExData* GetExDataIfExists() const;
   CXFA_Image* GetImageIfExists() const;
+
+ private:
+  CXFA_Value(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_VALUE_H_
diff --git a/xfa/fxfa/parser/cxfa_variables.cpp b/xfa/fxfa/parser/cxfa_variables.cpp
index 5974b9e..87ee907 100644
--- a/xfa/fxfa/parser/cxfa_variables.cpp
+++ b/xfa/fxfa/parser/cxfa_variables.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_variables.h"
 
 #include "fxjs/xfa/cjx_container.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -19,14 +19,23 @@
 
 }  // namespace
 
+// static
+CXFA_Variables* CXFA_Variables::FromNode(CXFA_Node* pNode) {
+  return pNode && pNode->GetElementType() == XFA_Element::Variables
+             ? static_cast<CXFA_Variables*>(pNode)
+             : nullptr;
+}
+
 CXFA_Variables::CXFA_Variables(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm},
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::Variables,
                 {},
                 kVariablesAttributeData,
-                pdfium::MakeUnique<CJX_Container>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Container>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Variables::~CXFA_Variables() = default;
diff --git a/xfa/fxfa/parser/cxfa_variables.h b/xfa/fxfa/parser/cxfa_variables.h
index b9f6d72..a032431 100644
--- a/xfa/fxfa/parser/cxfa_variables.h
+++ b/xfa/fxfa/parser/cxfa_variables.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,13 @@
 
 class CXFA_Variables final : public CXFA_Node {
  public:
-  CXFA_Variables(CXFA_Document* doc, XFA_PacketType packet);
+  static CXFA_Variables* FromNode(CXFA_Node* pNode);
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Variables() override;
+
+ private:
+  CXFA_Variables(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_VARIABLES_H_
diff --git a/xfa/fxfa/parser/cxfa_version.cpp b/xfa/fxfa/parser/cxfa_version.cpp
index 651d498..0527820 100644
--- a/xfa/fxfa/parser/cxfa_version.cpp
+++ b/xfa/fxfa/parser/cxfa_version.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_version.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Version::CXFA_Version(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Version,
                 {},
                 kVersionAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Version::~CXFA_Version() = default;
diff --git a/xfa/fxfa/parser/cxfa_version.h b/xfa/fxfa/parser/cxfa_version.h
index 4b4f425..fe7a5b8 100644
--- a/xfa/fxfa/parser/cxfa_version.h
+++ b/xfa/fxfa/parser/cxfa_version.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Version final : public CXFA_Node {
  public:
-  CXFA_Version(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Version() override;
+
+ private:
+  CXFA_Version(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_VERSION_H_
diff --git a/xfa/fxfa/parser/cxfa_versioncontrol.cpp b/xfa/fxfa/parser/cxfa_versioncontrol.cpp
index c72b877..e20f851 100644
--- a/xfa/fxfa/parser/cxfa_versioncontrol.cpp
+++ b/xfa/fxfa/parser/cxfa_versioncontrol.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_versioncontrol.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -27,11 +27,13 @@
                                          XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::VersionControl,
                 {},
                 kVersionControlAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_VersionControl::~CXFA_VersionControl() = default;
diff --git a/xfa/fxfa/parser/cxfa_versioncontrol.h b/xfa/fxfa/parser/cxfa_versioncontrol.h
index 733b817..cecd29b 100644
--- a/xfa/fxfa/parser/cxfa_versioncontrol.h
+++ b/xfa/fxfa/parser/cxfa_versioncontrol.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_VersionControl final : public CXFA_Node {
  public:
-  CXFA_VersionControl(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_VersionControl() override;
+
+ private:
+  CXFA_VersionControl(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_VERSIONCONTROL_H_
diff --git a/xfa/fxfa/parser/cxfa_viewerpreferences.cpp b/xfa/fxfa/parser/cxfa_viewerpreferences.cpp
index 4952c65..cce1491 100644
--- a/xfa/fxfa/parser/cxfa_viewerpreferences.cpp
+++ b/xfa/fxfa/parser/cxfa_viewerpreferences.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,20 +7,20 @@
 #include "xfa/fxfa/parser/cxfa_viewerpreferences.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kViewerPreferencesPropertyData[] = {
-    {XFA_Element::PrintScaling, 1, 0},
-    {XFA_Element::Enforce, 1, 0},
-    {XFA_Element::NumberOfCopies, 1, 0},
-    {XFA_Element::PageRange, 1, 0},
-    {XFA_Element::AddViewerPreferences, 1, 0},
-    {XFA_Element::ADBE_JSConsole, 1, 0},
-    {XFA_Element::DuplexOption, 1, 0},
-    {XFA_Element::ADBE_JSDebugger, 1, 0},
-    {XFA_Element::PickTrayByPDFSize, 1, 0},
+    {XFA_Element::PrintScaling, 1, {}},
+    {XFA_Element::Enforce, 1, {}},
+    {XFA_Element::NumberOfCopies, 1, {}},
+    {XFA_Element::PageRange, 1, {}},
+    {XFA_Element::AddViewerPreferences, 1, {}},
+    {XFA_Element::ADBE_JSConsole, 1, {}},
+    {XFA_Element::DuplexOption, 1, {}},
+    {XFA_Element::ADBE_JSDebugger, 1, {}},
+    {XFA_Element::PickTrayByPDFSize, 1, {}},
 };
 
 const CXFA_Node::AttributeData kViewerPreferencesAttributeData[] = {
@@ -34,11 +34,13 @@
                                                XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::ViewerPreferences,
                 kViewerPreferencesPropertyData,
                 kViewerPreferencesAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_ViewerPreferences::~CXFA_ViewerPreferences() = default;
diff --git a/xfa/fxfa/parser/cxfa_viewerpreferences.h b/xfa/fxfa/parser/cxfa_viewerpreferences.h
index 229061f..f4fdd79 100644
--- a/xfa/fxfa/parser/cxfa_viewerpreferences.h
+++ b/xfa/fxfa/parser/cxfa_viewerpreferences.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_ViewerPreferences final : public CXFA_Node {
  public:
-  CXFA_ViewerPreferences(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_ViewerPreferences() override;
+
+ private:
+  CXFA_ViewerPreferences(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_VIEWERPREFERENCES_H_
diff --git a/xfa/fxfa/parser/cxfa_webclient.cpp b/xfa/fxfa/parser/cxfa_webclient.cpp
index 66844fc..5bd24b7 100644
--- a/xfa/fxfa/parser/cxfa_webclient.cpp
+++ b/xfa/fxfa/parser/cxfa_webclient.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_webclient.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kWebClientPropertyData[] = {
-    {XFA_Element::FontInfo, 1, 0},
-    {XFA_Element::Xdc, 1, 0},
+    {XFA_Element::FontInfo, 1, {}},
+    {XFA_Element::Xdc, 1, {}},
 };
 
 const CXFA_Node::AttributeData kWebClientAttributeData[] = {
@@ -27,11 +27,13 @@
 CXFA_WebClient::CXFA_WebClient(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::WebClient,
                 kWebClientPropertyData,
                 kWebClientAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_WebClient::~CXFA_WebClient() = default;
diff --git a/xfa/fxfa/parser/cxfa_webclient.h b/xfa/fxfa/parser/cxfa_webclient.h
index 41da046..4ca6440 100644
--- a/xfa/fxfa/parser/cxfa_webclient.h
+++ b/xfa/fxfa/parser/cxfa_webclient.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_WebClient final : public CXFA_Node {
  public:
-  CXFA_WebClient(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_WebClient() override;
+
+ private:
+  CXFA_WebClient(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_WEBCLIENT_H_
diff --git a/xfa/fxfa/parser/cxfa_whitespace.cpp b/xfa/fxfa/parser/cxfa_whitespace.cpp
index d8bdc62..75168be 100644
--- a/xfa/fxfa/parser/cxfa_whitespace.cpp
+++ b/xfa/fxfa/parser/cxfa_whitespace.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_whitespace.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Whitespace::CXFA_Whitespace(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Whitespace,
                 {},
                 kWhitespaceAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Whitespace::~CXFA_Whitespace() = default;
diff --git a/xfa/fxfa/parser/cxfa_whitespace.h b/xfa/fxfa/parser/cxfa_whitespace.h
index 096a2da..385137e 100644
--- a/xfa/fxfa/parser/cxfa_whitespace.h
+++ b/xfa/fxfa/parser/cxfa_whitespace.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Whitespace final : public CXFA_Node {
  public:
-  CXFA_Whitespace(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Whitespace() override;
+
+ private:
+  CXFA_Whitespace(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_WHITESPACE_H_
diff --git a/xfa/fxfa/parser/cxfa_window.cpp b/xfa/fxfa/parser/cxfa_window.cpp
index c1dd78d..ced61d7 100644
--- a/xfa/fxfa/parser/cxfa_window.cpp
+++ b/xfa/fxfa/parser/cxfa_window.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_window.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Window::CXFA_Window(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Window,
                 {},
                 kWindowAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Window::~CXFA_Window() = default;
diff --git a/xfa/fxfa/parser/cxfa_window.h b/xfa/fxfa/parser/cxfa_window.h
index fc2fcb7..2edc71b 100644
--- a/xfa/fxfa/parser/cxfa_window.h
+++ b/xfa/fxfa/parser/cxfa_window.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Window final : public CXFA_Node {
  public:
-  CXFA_Window(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Window() override;
+
+ private:
+  CXFA_Window(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_WINDOW_H_
diff --git a/xfa/fxfa/parser/cxfa_wsdladdress.cpp b/xfa/fxfa/parser/cxfa_wsdladdress.cpp
index 9c35cac..4bbafd1 100644
--- a/xfa/fxfa/parser/cxfa_wsdladdress.cpp
+++ b/xfa/fxfa/parser/cxfa_wsdladdress.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_wsdladdress.h"
 
 #include "fxjs/xfa/cjx_textnode.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -23,11 +23,13 @@
 CXFA_WsdlAddress::CXFA_WsdlAddress(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_ConnectionSet,
+                XFA_XDPPACKET::kConnectionSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::WsdlAddress,
                 {},
                 kWsdlAddressAttributeData,
-                pdfium::MakeUnique<CJX_TextNode>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_TextNode>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_WsdlAddress::~CXFA_WsdlAddress() = default;
diff --git a/xfa/fxfa/parser/cxfa_wsdladdress.h b/xfa/fxfa/parser/cxfa_wsdladdress.h
index e65bd6f..6caac9b 100644
--- a/xfa/fxfa/parser/cxfa_wsdladdress.h
+++ b/xfa/fxfa/parser/cxfa_wsdladdress.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_WsdlAddress final : public CXFA_Node {
  public:
-  CXFA_WsdlAddress(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_WsdlAddress() override;
+
+ private:
+  CXFA_WsdlAddress(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_WSDLADDRESS_H_
diff --git a/xfa/fxfa/parser/cxfa_wsdlconnection.cpp b/xfa/fxfa/parser/cxfa_wsdlconnection.cpp
index 188255f..77ff7eb 100644
--- a/xfa/fxfa/parser/cxfa_wsdlconnection.cpp
+++ b/xfa/fxfa/parser/cxfa_wsdlconnection.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,17 +7,17 @@
 #include "xfa/fxfa/parser/cxfa_wsdlconnection.h"
 
 #include "fxjs/xfa/cjx_wsdlconnection.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kWsdlConnectionPropertyData[] = {
-    {XFA_Element::Operation, 1, 0},
-    {XFA_Element::WsdlAddress, 1, 0},
-    {XFA_Element::SoapAddress, 1, 0},
-    {XFA_Element::SoapAction, 1, 0},
-    {XFA_Element::EffectiveOutputPolicy, 1, 0},
-    {XFA_Element::EffectiveInputPolicy, 1, 0},
+    {XFA_Element::Operation, 1, {}},
+    {XFA_Element::WsdlAddress, 1, {}},
+    {XFA_Element::SoapAddress, 1, {}},
+    {XFA_Element::SoapAction, 1, {}},
+    {XFA_Element::EffectiveOutputPolicy, 1, {}},
+    {XFA_Element::EffectiveInputPolicy, 1, {}},
 };
 
 const CXFA_Node::AttributeData kWsdlConnectionAttributeData[] = {
@@ -31,11 +31,13 @@
                                          XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_ConnectionSet,
+                XFA_XDPPACKET::kConnectionSet,
                 XFA_ObjectType::Node,
                 XFA_Element::WsdlConnection,
                 kWsdlConnectionPropertyData,
                 kWsdlConnectionAttributeData,
-                pdfium::MakeUnique<CJX_WsdlConnection>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_WsdlConnection>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_WsdlConnection::~CXFA_WsdlConnection() = default;
diff --git a/xfa/fxfa/parser/cxfa_wsdlconnection.h b/xfa/fxfa/parser/cxfa_wsdlconnection.h
index b2238fd..65da703 100644
--- a/xfa/fxfa/parser/cxfa_wsdlconnection.h
+++ b/xfa/fxfa/parser/cxfa_wsdlconnection.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_WsdlConnection final : public CXFA_Node {
  public:
-  CXFA_WsdlConnection(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_WsdlConnection() override;
+
+ private:
+  CXFA_WsdlConnection(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_WSDLCONNECTION_H_
diff --git a/xfa/fxfa/parser/cxfa_xdc.cpp b/xfa/fxfa/parser/cxfa_xdc.cpp
index 84198da..07b1f8a 100644
--- a/xfa/fxfa/parser/cxfa_xdc.cpp
+++ b/xfa/fxfa/parser/cxfa_xdc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_xdc.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kXdcPropertyData[] = {
-    {XFA_Element::Uri, 1, 0},
-    {XFA_Element::Xsl, 1, 0},
+    {XFA_Element::Uri, 1, {}},
+    {XFA_Element::Xsl, 1, {}},
 };
 
 const CXFA_Node::AttributeData kXdcAttributeData[] = {
@@ -26,11 +26,13 @@
 CXFA_Xdc::CXFA_Xdc(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                (XFA_XDPPACKET_Config | XFA_XDPPACKET_Xdc),
+                {XFA_XDPPACKET::kConfig, XFA_XDPPACKET::kXdc},
                 XFA_ObjectType::ModelNode,
                 XFA_Element::Xdc,
                 kXdcPropertyData,
                 kXdcAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Xdc::~CXFA_Xdc() = default;
diff --git a/xfa/fxfa/parser/cxfa_xdc.h b/xfa/fxfa/parser/cxfa_xdc.h
index 81cbb57..2acb59c 100644
--- a/xfa/fxfa/parser/cxfa_xdc.h
+++ b/xfa/fxfa/parser/cxfa_xdc.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Xdc final : public CXFA_Node {
  public:
-  CXFA_Xdc(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Xdc() override;
+
+ private:
+  CXFA_Xdc(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_XDC_H_
diff --git a/xfa/fxfa/parser/cxfa_xdp.cpp b/xfa/fxfa/parser/cxfa_xdp.cpp
index f3dc76c..bf09169 100644
--- a/xfa/fxfa/parser/cxfa_xdp.cpp
+++ b/xfa/fxfa/parser/cxfa_xdp.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_xdp.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kXdpPropertyData[] = {
-    {XFA_Element::Packets, 1, 0},
+    {XFA_Element::Packets, 1, {}},
 };
 
 const CXFA_Node::AttributeData kXdpAttributeData[] = {
@@ -25,11 +25,13 @@
 CXFA_Xdp::CXFA_Xdp(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Xdp,
                 kXdpPropertyData,
                 kXdpAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Xdp::~CXFA_Xdp() = default;
diff --git a/xfa/fxfa/parser/cxfa_xdp.h b/xfa/fxfa/parser/cxfa_xdp.h
index 16ad5d8..fd01be8 100644
--- a/xfa/fxfa/parser/cxfa_xdp.h
+++ b/xfa/fxfa/parser/cxfa_xdp.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Xdp final : public CXFA_Node {
  public:
-  CXFA_Xdp(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Xdp() override;
+
+ private:
+  CXFA_Xdp(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_XDP_H_
diff --git a/xfa/fxfa/parser/cxfa_xfa.cpp b/xfa/fxfa/parser/cxfa_xfa.cpp
index 22fdd35..f51f5bc 100644
--- a/xfa/fxfa/parser/cxfa_xfa.cpp
+++ b/xfa/fxfa/parser/cxfa_xfa.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include "xfa/fxfa/parser/cxfa_xfa.h"
 
 #include "fxjs/xfa/cjx_xfa.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
@@ -21,11 +21,13 @@
 CXFA_Xfa::CXFA_Xfa(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_XDP,
+                XFA_XDPPACKET::kXdp,
                 XFA_ObjectType::ModelNode,
                 XFA_Element::Xfa,
                 {},
                 kXfaAttributeData,
-                pdfium::MakeUnique<CJX_Xfa>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Xfa>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Xfa::~CXFA_Xfa() = default;
diff --git a/xfa/fxfa/parser/cxfa_xfa.h b/xfa/fxfa/parser/cxfa_xfa.h
index d7d33df..3b5769e 100644
--- a/xfa/fxfa/parser/cxfa_xfa.h
+++ b/xfa/fxfa/parser/cxfa_xfa.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Xfa final : public CXFA_Node {
  public:
-  CXFA_Xfa(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Xfa() override;
+
+ private:
+  CXFA_Xfa(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_XFA_H_
diff --git a/xfa/fxfa/parser/cxfa_xmlconnection.cpp b/xfa/fxfa/parser/cxfa_xmlconnection.cpp
index 17be7bc..c0c6667 100644
--- a/xfa/fxfa/parser/cxfa_xmlconnection.cpp
+++ b/xfa/fxfa/parser/cxfa_xmlconnection.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #include "xfa/fxfa/parser/cxfa_xmlconnection.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kXmlConnectionPropertyData[] = {
-    {XFA_Element::Uri, 1, 0},
+    {XFA_Element::Uri, 1, {}},
 };
 
 const CXFA_Node::AttributeData kXmlConnectionAttributeData[] = {
@@ -26,11 +26,13 @@
                                        XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_ConnectionSet,
+                XFA_XDPPACKET::kConnectionSet,
                 XFA_ObjectType::Node,
                 XFA_Element::XmlConnection,
                 kXmlConnectionPropertyData,
                 kXmlConnectionAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_XmlConnection::~CXFA_XmlConnection() = default;
diff --git a/xfa/fxfa/parser/cxfa_xmlconnection.h b/xfa/fxfa/parser/cxfa_xmlconnection.h
index 33cc038..197a2c3 100644
--- a/xfa/fxfa/parser/cxfa_xmlconnection.h
+++ b/xfa/fxfa/parser/cxfa_xmlconnection.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_XmlConnection final : public CXFA_Node {
  public:
-  CXFA_XmlConnection(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_XmlConnection() override;
+
+ private:
+  CXFA_XmlConnection(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_XMLCONNECTION_H_
diff --git a/xfa/fxfa/parser/cxfa_xmllocale.cpp b/xfa/fxfa/parser/cxfa_xmllocale.cpp
index c7a6f47..8395680 100644
--- a/xfa/fxfa/parser/cxfa_xmllocale.cpp
+++ b/xfa/fxfa/parser/cxfa_xmllocale.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,12 +8,12 @@
 
 #include <utility>
 
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/xml/cfx_xmldocument.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlparser.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_localemgr.h"
 #include "xfa/fxfa/parser/cxfa_nodelocale.h"
@@ -30,37 +30,37 @@
 }  // namespace
 
 // static
-std::unique_ptr<CXFA_XMLLocale> CXFA_XMLLocale::Create(
-    pdfium::span<uint8_t> data) {
-  auto stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(data);
+CXFA_XMLLocale* CXFA_XMLLocale::Create(cppgc::Heap* heap,
+                                       pdfium::span<uint8_t> data) {
+  auto stream = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(data);
   CFX_XMLParser parser(stream);
   auto doc = parser.Parse();
   if (!doc)
     return nullptr;
 
-  CFX_XMLElement* locale = nullptr;
   for (auto* child = doc->GetRoot()->GetFirstChild(); child;
        child = child->GetNextSibling()) {
     CFX_XMLElement* elem = ToXMLElement(child);
     if (elem && elem->GetName().EqualsASCII("locale")) {
-      locale = elem;
-      break;
+      return cppgc::MakeGarbageCollected<CXFA_XMLLocale>(
+          heap->GetAllocationHandle(), std::move(doc), elem);
     }
   }
-  if (!locale)
-    return nullptr;
-
-  return pdfium::MakeUnique<CXFA_XMLLocale>(std::move(doc), locale);
+  return nullptr;
 }
 
 CXFA_XMLLocale::CXFA_XMLLocale(std::unique_ptr<CFX_XMLDocument> doc,
-                               CFX_XMLElement* locale)
+                               const CFX_XMLElement* locale)
     : xml_doc_(std::move(doc)), locale_(locale) {
-  ASSERT(xml_doc_);
-  ASSERT(locale_);
+  DCHECK(xml_doc_);
+  DCHECK(locale_);
 }
 
-CXFA_XMLLocale::~CXFA_XMLLocale() {}
+CXFA_XMLLocale::~CXFA_XMLLocale() = default;
+
+void CXFA_XMLLocale::Trace(cppgc::Visitor* visitor) const {
+  GCedLocaleIface::Trace(visitor);
+}
 
 WideString CXFA_XMLLocale::GetName() const {
   return locale_->GetAttribute(L"name");
@@ -113,8 +113,8 @@
   return GetCalendarSymbol(L"meridiem", bAM ? 0 : 1, false);
 }
 
-FX_TIMEZONE CXFA_XMLLocale::GetTimeZone() const {
-  return CXFA_TimeZoneProvider().GetTimeZone();
+int CXFA_XMLLocale::GetTimeZoneInMinutes() const {
+  return CXFA_TimeZoneProvider().GetTimeZoneInMinutes();
 }
 
 WideString CXFA_XMLLocale::GetEraName(bool bAD) const {
@@ -153,57 +153,55 @@
   return sym_element ? sym_element->GetTextData() : WideString();
 }
 
-WideString CXFA_XMLLocale::GetDatePattern(
-    FX_LOCALEDATETIMESUBCATEGORY eType) const {
+WideString CXFA_XMLLocale::GetDatePattern(DateTimeSubcategory eType) const {
   CFX_XMLElement* patterns = locale_->GetFirstChildNamed(L"datePatterns");
   if (!patterns)
     return WideString();
 
   WideString wsName;
   switch (eType) {
-    case FX_LOCALEDATETIMESUBCATEGORY_Short:
+    case DateTimeSubcategory::kShort:
       wsName = L"short";
       break;
-    case FX_LOCALEDATETIMESUBCATEGORY_Default:
-    case FX_LOCALEDATETIMESUBCATEGORY_Medium:
+    case DateTimeSubcategory::kDefault:
+    case DateTimeSubcategory::kMedium:
       wsName = L"med";
       break;
-    case FX_LOCALEDATETIMESUBCATEGORY_Full:
+    case DateTimeSubcategory::kFull:
       wsName = L"full";
       break;
-    case FX_LOCALEDATETIMESUBCATEGORY_Long:
+    case DateTimeSubcategory::kLong:
       wsName = L"long";
       break;
   }
   return GetPattern(patterns, L"datePattern", wsName.AsStringView());
 }
 
-WideString CXFA_XMLLocale::GetTimePattern(
-    FX_LOCALEDATETIMESUBCATEGORY eType) const {
+WideString CXFA_XMLLocale::GetTimePattern(DateTimeSubcategory eType) const {
   CFX_XMLElement* patterns = locale_->GetFirstChildNamed(L"timePatterns");
   if (!patterns)
     return WideString();
 
   WideString wsName;
   switch (eType) {
-    case FX_LOCALEDATETIMESUBCATEGORY_Short:
+    case DateTimeSubcategory::kShort:
       wsName = L"short";
       break;
-    case FX_LOCALEDATETIMESUBCATEGORY_Default:
-    case FX_LOCALEDATETIMESUBCATEGORY_Medium:
+    case DateTimeSubcategory::kDefault:
+    case DateTimeSubcategory::kMedium:
       wsName = L"med";
       break;
-    case FX_LOCALEDATETIMESUBCATEGORY_Full:
+    case DateTimeSubcategory::kFull:
       wsName = L"full";
       break;
-    case FX_LOCALEDATETIMESUBCATEGORY_Long:
+    case DateTimeSubcategory::kLong:
       wsName = L"long";
       break;
   }
   return GetPattern(patterns, L"timePattern", wsName.AsStringView());
 }
 
-WideString CXFA_XMLLocale::GetNumPattern(FX_LOCALENUMSUBCATEGORY eType) const {
+WideString CXFA_XMLLocale::GetNumPattern(NumSubcategory eType) const {
   CFX_XMLElement* patterns = locale_->GetFirstChildNamed(L"numberPatterns");
   return patterns ? XFA_PatternToString(eType) : WideString();
 }
diff --git a/xfa/fxfa/parser/cxfa_xmllocale.h b/xfa/fxfa/parser/cxfa_xmllocale.h
index 472f774..0c045ff 100644
--- a/xfa/fxfa/parser/cxfa_xmllocale.h
+++ b/xfa/fxfa/parser/cxfa_xmllocale.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,21 +9,25 @@
 
 #include <memory>
 
-#include "third_party/base/span.h"
-#include "xfa/fgas/crt/locale_iface.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "fxjs/gc/heap.h"
+#include "third_party/base/containers/span.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "xfa/fxfa/parser/gced_locale_iface.h"
 
 class CFX_XMLDocument;
 class CFX_XMLElement;
 
-class CXFA_XMLLocale final : public LocaleIface {
+class CXFA_XMLLocale final : public GCedLocaleIface {
  public:
-  static std::unique_ptr<CXFA_XMLLocale> Create(pdfium::span<uint8_t> data);
+  // Object is created on cppgc heap.
+  static CXFA_XMLLocale* Create(cppgc::Heap* heap, pdfium::span<uint8_t> data);
 
-  explicit CXFA_XMLLocale(std::unique_ptr<CFX_XMLDocument> root,
-                          CFX_XMLElement* locale);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_XMLLocale() override;
 
-  // LocaleIface
+  // GCedLocaleIface:
+  void Trace(cppgc::Visitor* visitor) const override;
   WideString GetName() const override;
   WideString GetDecimalSymbol() const override;
   WideString GetGroupingSymbol() const override;
@@ -34,14 +38,17 @@
   WideString GetMonthName(int32_t nMonth, bool bAbbr) const override;
   WideString GetDayName(int32_t nWeek, bool bAbbr) const override;
   WideString GetMeridiemName(bool bAM) const override;
-  FX_TIMEZONE GetTimeZone() const override;
+  int GetTimeZoneInMinutes() const override;
   WideString GetEraName(bool bAD) const override;
 
-  WideString GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY eType) const override;
-  WideString GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY eType) const override;
-  WideString GetNumPattern(FX_LOCALENUMSUBCATEGORY eType) const override;
+  WideString GetDatePattern(DateTimeSubcategory eType) const override;
+  WideString GetTimePattern(DateTimeSubcategory eType) const override;
+  WideString GetNumPattern(NumSubcategory eType) const override;
 
  private:
+  CXFA_XMLLocale(std::unique_ptr<CFX_XMLDocument> root,
+                 const CFX_XMLElement* locale);
+
   WideString GetPattern(CFX_XMLElement* pElement,
                         WideStringView bsTag,
                         WideStringView wsName) const;
@@ -50,7 +57,7 @@
                                bool bAbbr) const;
 
   std::unique_ptr<CFX_XMLDocument> xml_doc_;
-  UnownedPtr<CFX_XMLElement> locale_;
+  UnownedPtr<const CFX_XMLElement> locale_;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_XMLLOCALE_H_
diff --git a/xfa/fxfa/parser/cxfa_xmllocale_unittest.cpp b/xfa/fxfa/parser/cxfa_xmllocale_unittest.cpp
index 395a360..ab4febf 100644
--- a/xfa/fxfa/parser/cxfa_xmllocale_unittest.cpp
+++ b/xfa/fxfa/parser/cxfa_xmllocale_unittest.cpp
@@ -1,11 +1,10 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "xfa/fxfa/parser/cxfa_xmllocale.h"
 
-#include <memory>
-
+#include "testing/fxgc_unittest.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -88,32 +87,35 @@
     "</currencySymbols>"
     "</locale>";
 
-std::unique_ptr<CXFA_XMLLocale> CreateLocaleHelper() {
-  return CXFA_XMLLocale::Create(pdfium::as_writable_bytes(
-      pdfium::make_span(const_cast<char*>(kXMLData), strlen(kXMLData))));
+CXFA_XMLLocale* CreateLocaleHelper(cppgc::Heap* heap) {
+  return CXFA_XMLLocale::Create(
+      heap, pdfium::as_writable_bytes(pdfium::make_span(
+                const_cast<char*>(kXMLData), strlen(kXMLData))));
 }
 
 }  // namespace
 
-TEST(CXFA_XMLLocaleTest, Create) {
-  auto locale = CreateLocaleHelper();
+class CXFA_XMLLocaleTest : public FXGCUnitTest {};
+
+TEST_F(CXFA_XMLLocaleTest, Create) {
+  auto* locale = CreateLocaleHelper(heap());
   EXPECT_TRUE(locale != nullptr);
 }
 
-TEST(CXFA_XMLLocaleTest, CreateBadXML) {
-  auto locale = CXFA_XMLLocale::Create(pdfium::span<uint8_t>());
+TEST_F(CXFA_XMLLocaleTest, CreateBadXML) {
+  auto* locale = CXFA_XMLLocale::Create(heap(), pdfium::span<uint8_t>());
   EXPECT_TRUE(locale == nullptr);
 }
 
-TEST(CXFA_XMLLocaleTest, GetName) {
-  auto locale = CreateLocaleHelper();
+TEST_F(CXFA_XMLLocaleTest, GetName) {
+  auto* locale = CreateLocaleHelper(heap());
   ASSERT_TRUE(locale != nullptr);
 
   EXPECT_EQ(L"en_US", locale->GetName());
 }
 
-TEST(CXFA_XMLLocaleTest, GetNumericSymbols) {
-  auto locale = CreateLocaleHelper();
+TEST_F(CXFA_XMLLocaleTest, GetNumericSymbols) {
+  auto* locale = CreateLocaleHelper(heap());
   ASSERT_TRUE(locale != nullptr);
 
   EXPECT_EQ(L".", locale->GetDecimalSymbol());
@@ -123,15 +125,15 @@
   EXPECT_EQ(L"$", locale->GetCurrencySymbol());
 }
 
-TEST(CXFA_XMLLocaleTest, GetDateTimeSymbols) {
-  auto locale = CreateLocaleHelper();
+TEST_F(CXFA_XMLLocaleTest, GetDateTimeSymbols) {
+  auto* locale = CreateLocaleHelper(heap());
   ASSERT_TRUE(locale != nullptr);
 
   EXPECT_EQ(L"GyMdkHmsSEDFwWahKzZ", locale->GetDateTimeSymbols());
 }
 
-TEST(CXFA_XMLLocaleTest, GetMonthName) {
-  auto locale = CreateLocaleHelper();
+TEST_F(CXFA_XMLLocaleTest, GetMonthName) {
+  auto* locale = CreateLocaleHelper(heap());
   ASSERT_TRUE(locale != nullptr);
 
   EXPECT_EQ(L"", locale->GetMonthName(24, false));
@@ -140,8 +142,8 @@
   EXPECT_EQ(L"February", locale->GetMonthName(1, false));
 }
 
-TEST(CXFA_XMLLocaleTest, GetDayName) {
-  auto locale = CreateLocaleHelper();
+TEST_F(CXFA_XMLLocaleTest, GetDayName) {
+  auto* locale = CreateLocaleHelper(heap());
   ASSERT_TRUE(locale != nullptr);
 
   EXPECT_EQ(L"", locale->GetDayName(24, false));
@@ -150,64 +152,64 @@
   EXPECT_EQ(L"Monday", locale->GetDayName(1, false));
 }
 
-TEST(CXFA_XMLLocaleTest, GetMeridiemName) {
-  auto locale = CreateLocaleHelper();
+TEST_F(CXFA_XMLLocaleTest, GetMeridiemName) {
+  auto* locale = CreateLocaleHelper(heap());
   ASSERT_TRUE(locale != nullptr);
 
   EXPECT_EQ(L"AM", locale->GetMeridiemName(true));
   EXPECT_EQ(L"PM", locale->GetMeridiemName(false));
 }
 
-TEST(CXFA_XMLLocaleTest, GetEraName) {
-  auto locale = CreateLocaleHelper();
+TEST_F(CXFA_XMLLocaleTest, GetEraName) {
+  auto* locale = CreateLocaleHelper(heap());
   ASSERT_TRUE(locale != nullptr);
 
   EXPECT_EQ(L"AD", locale->GetEraName(true));
   EXPECT_EQ(L"BC", locale->GetEraName(false));
 }
 
-TEST(CXFA_XMLLocaleTest, GetDatePattern) {
-  auto locale = CreateLocaleHelper();
+TEST_F(CXFA_XMLLocaleTest, GetDatePattern) {
+  auto* locale = CreateLocaleHelper(heap());
   ASSERT_TRUE(locale != nullptr);
 
   EXPECT_EQ(L"M/D/YY",
-            locale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Short));
+            locale->GetDatePattern(LocaleIface::DateTimeSubcategory::kShort));
   EXPECT_EQ(L"MMM D, YYYY",
-            locale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Default));
+            locale->GetDatePattern(LocaleIface::DateTimeSubcategory::kDefault));
   EXPECT_EQ(L"MMM D, YYYY",
-            locale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium));
+            locale->GetDatePattern(LocaleIface::DateTimeSubcategory::kMedium));
   EXPECT_EQ(L"EEEE, MMMM D, YYYY",
-            locale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Full));
+            locale->GetDatePattern(LocaleIface::DateTimeSubcategory::kFull));
   EXPECT_EQ(L"MMMM D, YYYY",
-            locale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Long));
+            locale->GetDatePattern(LocaleIface::DateTimeSubcategory::kLong));
 }
 
-TEST(CXFA_XMLLocaleTest, GetTimePattern) {
-  auto locale = CreateLocaleHelper();
+TEST_F(CXFA_XMLLocaleTest, GetTimePattern) {
+  auto* locale = CreateLocaleHelper(heap());
   ASSERT_TRUE(locale != nullptr);
 
   EXPECT_EQ(L"h:MM A",
-            locale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Short));
+            locale->GetTimePattern(LocaleIface::DateTimeSubcategory::kShort));
   EXPECT_EQ(L"h:MM:SS A",
-            locale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Default));
+            locale->GetTimePattern(LocaleIface::DateTimeSubcategory::kDefault));
   EXPECT_EQ(L"h:MM:SS A",
-            locale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium));
+            locale->GetTimePattern(LocaleIface::DateTimeSubcategory::kMedium));
   EXPECT_EQ(L"h:MM:SS A Z",
-            locale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Full));
+            locale->GetTimePattern(LocaleIface::DateTimeSubcategory::kFull));
   EXPECT_EQ(L"h:MM:SS A Z",
-            locale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Long));
+            locale->GetTimePattern(LocaleIface::DateTimeSubcategory::kLong));
 }
 
-TEST(CXFA_XMLLocaleTest, GetNumPattern) {
-  auto locale = CreateLocaleHelper();
+TEST_F(CXFA_XMLLocaleTest, GetNumPattern) {
+  auto* locale = CreateLocaleHelper(heap());
   ASSERT_TRUE(locale != nullptr);
 
   EXPECT_EQ(L"z,zzz,zzz,zzz,zzz,zzz%",
-            locale->GetNumPattern(FX_LOCALENUMPATTERN_Percent));
+            locale->GetNumPattern(LocaleIface::NumSubcategory::kPercent));
   EXPECT_EQ(L"$z,zzz,zzz,zzz,zzz,zz9.99",
-            locale->GetNumPattern(FX_LOCALENUMPATTERN_Currency));
+            locale->GetNumPattern(LocaleIface::NumSubcategory::kCurrency));
   EXPECT_EQ(L"z,zzz,zzz,zzz,zzz,zz9.zzz",
-            locale->GetNumPattern(FX_LOCALENUMPATTERN_Decimal));
+            locale->GetNumPattern(LocaleIface::NumSubcategory::kDecimal));
   EXPECT_EQ(L"z,zzz,zzz,zzz,zzz,zzz",
-            locale->GetNumPattern(FX_LOCALENUMPATTERN_Integer));
+            locale->GetNumPattern(LocaleIface::NumSubcategory::kInteger));
 }
diff --git a/xfa/fxfa/parser/cxfa_xsdconnection.cpp b/xfa/fxfa/parser/cxfa_xsdconnection.cpp
index 1973870..68fd9d0 100644
--- a/xfa/fxfa/parser/cxfa_xsdconnection.cpp
+++ b/xfa/fxfa/parser/cxfa_xsdconnection.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_xsdconnection.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kXsdConnectionPropertyData[] = {
-    {XFA_Element::Uri, 1, 0},
-    {XFA_Element::RootElement, 1, 0},
+    {XFA_Element::Uri, 1, {}},
+    {XFA_Element::RootElement, 1, {}},
 };
 
 const CXFA_Node::AttributeData kXsdConnectionAttributeData[] = {
@@ -27,11 +27,13 @@
                                        XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_ConnectionSet,
+                XFA_XDPPACKET::kConnectionSet,
                 XFA_ObjectType::Node,
                 XFA_Element::XsdConnection,
                 kXsdConnectionPropertyData,
                 kXsdConnectionAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_XsdConnection::~CXFA_XsdConnection() = default;
diff --git a/xfa/fxfa/parser/cxfa_xsdconnection.h b/xfa/fxfa/parser/cxfa_xsdconnection.h
index 8cdef80..312b86e 100644
--- a/xfa/fxfa/parser/cxfa_xsdconnection.h
+++ b/xfa/fxfa/parser/cxfa_xsdconnection.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_XsdConnection final : public CXFA_Node {
  public:
-  CXFA_XsdConnection(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_XsdConnection() override;
+
+ private:
+  CXFA_XsdConnection(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_XSDCONNECTION_H_
diff --git a/xfa/fxfa/parser/cxfa_xsl.cpp b/xfa/fxfa/parser/cxfa_xsl.cpp
index 2db4bdc..575f3d7 100644
--- a/xfa/fxfa/parser/cxfa_xsl.cpp
+++ b/xfa/fxfa/parser/cxfa_xsl.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #include "xfa/fxfa/parser/cxfa_xsl.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kXslPropertyData[] = {
-    {XFA_Element::Uri, 1, 0},
-    {XFA_Element::Debug, 1, 0},
+    {XFA_Element::Uri, 1, {}},
+    {XFA_Element::Debug, 1, {}},
 };
 
 const CXFA_Node::AttributeData kXslAttributeData[] = {
@@ -26,11 +26,13 @@
 CXFA_Xsl::CXFA_Xsl(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Xsl,
                 kXslPropertyData,
                 kXslAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Xsl::~CXFA_Xsl() = default;
diff --git a/xfa/fxfa/parser/cxfa_xsl.h b/xfa/fxfa/parser/cxfa_xsl.h
index 1be8d4b..68de3ba 100644
--- a/xfa/fxfa/parser/cxfa_xsl.h
+++ b/xfa/fxfa/parser/cxfa_xsl.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Xsl final : public CXFA_Node {
  public:
-  CXFA_Xsl(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Xsl() override;
+
+ private:
+  CXFA_Xsl(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_XSL_H_
diff --git a/xfa/fxfa/parser/cxfa_zpl.cpp b/xfa/fxfa/parser/cxfa_zpl.cpp
index aa9db61..104ccd8 100644
--- a/xfa/fxfa/parser/cxfa_zpl.cpp
+++ b/xfa/fxfa/parser/cxfa_zpl.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,15 @@
 #include "xfa/fxfa/parser/cxfa_zpl.h"
 
 #include "fxjs/xfa/cjx_node.h"
-#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
 namespace {
 
 const CXFA_Node::PropertyData kZplPropertyData[] = {
-    {XFA_Element::FontInfo, 1, 0},
-    {XFA_Element::Xdc, 1, 0},
-    {XFA_Element::BatchOutput, 1, 0},
-    {XFA_Element::FlipLabel, 1, 0},
+    {XFA_Element::FontInfo, 1, {}},
+    {XFA_Element::Xdc, 1, {}},
+    {XFA_Element::BatchOutput, 1, {}},
+    {XFA_Element::FlipLabel, 1, {}},
 };
 
 const CXFA_Node::AttributeData kZplAttributeData[] = {
@@ -29,11 +29,13 @@
 CXFA_Zpl::CXFA_Zpl(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
-                XFA_XDPPACKET_Config,
+                XFA_XDPPACKET::kConfig,
                 XFA_ObjectType::Node,
                 XFA_Element::Zpl,
                 kZplPropertyData,
                 kZplAttributeData,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
+                cppgc::MakeGarbageCollected<CJX_Node>(
+                    doc->GetHeap()->GetAllocationHandle(),
+                    this)) {}
 
 CXFA_Zpl::~CXFA_Zpl() = default;
diff --git a/xfa/fxfa/parser/cxfa_zpl.h b/xfa/fxfa/parser/cxfa_zpl.h
index 6433015..78f540e 100644
--- a/xfa/fxfa/parser/cxfa_zpl.h
+++ b/xfa/fxfa/parser/cxfa_zpl.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,8 +11,11 @@
 
 class CXFA_Zpl final : public CXFA_Node {
  public:
-  CXFA_Zpl(CXFA_Document* doc, XFA_PacketType packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CXFA_Zpl() override;
+
+ private:
+  CXFA_Zpl(CXFA_Document* doc, XFA_PacketType packet);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ZPL_H_
diff --git a/xfa/fxfa/parser/element_attributes.inc b/xfa/fxfa/parser/element_attributes.inc
index c8bfc60..f0eb68e 100644
--- a/xfa/fxfa/parser/element_attributes.inc
+++ b/xfa/fxfa/parser/element_attributes.inc
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fxfa/parser/elements.inc b/xfa/fxfa/parser/elements.inc
index b0246ec..456b8e6 100644
--- a/xfa/fxfa/parser/elements.inc
+++ b/xfa/fxfa/parser/elements.inc
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -316,7 +316,7 @@
 ELEM____(0xffffffffu, "hostPseudoModel", HostPseudoModel, Object)
 ELEM____(0xffffffffu, "layoutPseudoModel", LayoutPseudoModel, Object)
 ELEM____(0xffffffffu, "dataWindow", DataWindow, Object)
-ELEM____(0xffffffffu, "treeList,", TreeList, ListDuplicate)
+ELEM____(0xffffffffu, "treeList", TreeList, ListDuplicate)
 ELEM____(0xffffffffu, "logPseudoModel", LogPseudoModel, Object)
 ELEM____(0xffffffffu, "list", List, ListDuplicate)
 ELEM____(0xffffffffu, "object", Object, Unknown)
diff --git a/xfa/fxfa/parser/gced_locale_iface.h b/xfa/fxfa/parser/gced_locale_iface.h
new file mode 100644
index 0000000..7d4ca63
--- /dev/null
+++ b/xfa/fxfa/parser/gced_locale_iface.h
@@ -0,0 +1,19 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FXFA_PARSER_GCED_LOCALE_IFACE_H_
+#define XFA_FXFA_PARSER_GCED_LOCALE_IFACE_H_
+
+#include "v8/include/cppgc/garbage-collected.h"
+#include "xfa/fgas/crt/locale_iface.h"
+
+class GCedLocaleIface : public cppgc::GarbageCollected<GCedLocaleIface>,
+                        public LocaleIface {
+ public:
+  virtual void Trace(cppgc::Visitor* visitor) const {}
+};
+
+#endif  // XFA_FXFA_PARSER_GCED_LOCALE_IFACE_H_
diff --git a/xfa/fxfa/parser/packets.inc b/xfa/fxfa/parser/packets.inc
index 58e49a4..bc6bd8a 100644
--- a/xfa/fxfa/parser/packets.inc
+++ b/xfa/fxfa/parser/packets.inc
@@ -1,21 +1,22 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
-PCKT____(0x0811929du, "sourceSet", SourceSet, L"http://www.xfa.org/schema/xfa-source-set/", NOMATCH, SUPPORTONE)
-PCKT____(0x0b843dbau, "pdf", Pdf, L"http://ns.adobe.com/xdp/pdf/", COMPLETEMATCH, SUPPORTONE)
-PCKT____(0x0c56afbfu, "xdc", Xdc, L"http://www.xfa.org/schema/xdc/", NOMATCH, SUPPORTONE)
-PCKT____(0x0c56afccu, "xdp", Xdp, L"http://ns.adobe.com/xdp/", COMPLETEMATCH, SUPPORTONE)
-PCKT____(0x132a8fbcu, "xmpmeta", Xmpmeta, L"http://ns.adobe.com/xmpmeta/", NOMATCH, SUPPORTMANY)
-PCKT____(0x48d004a8u, "xfdf", Xfdf, L"http://ns.adobe.com/xfdf/", NOMATCH, SUPPORTONE)
-PCKT____(0x4e1e39b6u, "config", Config, L"http://www.xfa.org/schema/xci/", NOMATCH, SUPPORTONE)
-PCKT____(0x5473b6dcu, "localeSet", LocaleSet, L"http://www.xfa.org/schema/xfa-locale-set/", NOMATCH, SUPPORTONE)
-PCKT____(0x6038580au, "stylesheet", Stylesheet, L"http://www.w3.org/1999/XSL/Transform", NOMATCH, SUPPORTMANY)
-PCKT____(0x803550fcu, "template", Template,  L"http://www.xfa.org/schema/xfa-template/", NOMATCH, SUPPORTONE)
-PCKT____(0x8b036f32u, "signature", Signature, L"http://www.w3.org/2000/09/xmldsig#", NOMATCH, SUPPORTONE)
-PCKT____(0x99b95079u, "datasets", Datasets,  L"http://www.xfa.org/schema/xfa-data/", PREFIXMATCH, SUPPORTONE)
-PCKT____(0xcd309ff4u, "form", Form, L"http://www.xfa.org/schema/xfa-form/", NOMATCH, SUPPORTONE)
-PCKT____(0xe14c801cu, "connectionSet", ConnectionSet, L"http://www.xfa.org/schema/xfa-connection-set/", NOMATCH, SUPPORTONE)
-PCKT____(0xffffffffu, "user", User, nullptr, NOMATCH, SUPPORTMANY)
+// NOTE: Ordered by hashcode for binary searching.
+PCKT____(0x0811929du, "sourceSet", SourceSet, "http://www.xfa.org/schema/xfa-source-set/", kNoMatch, kSupportOne)
+PCKT____(0x0b843dbau, "pdf", Pdf, "http://ns.adobe.com/xdp/pdf/", kCompleteMatch, kSupportOne)
+PCKT____(0x0c56afbfu, "xdc", Xdc, "http://www.xfa.org/schema/xdc/", kNoMatch, kSupportOne)
+PCKT____(0x0c56afccu, "xdp", Xdp, "http://ns.adobe.com/xdp/", kCompleteMatch, kSupportOne)
+PCKT____(0x132a8fbcu, "xmpmeta", Xmpmeta, "http://ns.adobe.com/xmpmeta/", kNoMatch, kSupportMany)
+PCKT____(0x48d004a8u, "xfdf", Xfdf, "http://ns.adobe.com/xfdf/", kNoMatch, kSupportOne)
+PCKT____(0x4e1e39b6u, "config", Config, "http://www.xfa.org/schema/xci/", kNoMatch, kSupportOne)
+PCKT____(0x5473b6dcu, "localeSet", LocaleSet, "http://www.xfa.org/schema/xfa-locale-set/", kNoMatch, kSupportOne)
+PCKT____(0x6038580au, "stylesheet", Stylesheet, "http://www.w3.org/1999/XSL/Transform", kNoMatch, kSupportMany)
+PCKT____(0x803550fcu, "template", Template,  "http://www.xfa.org/schema/xfa-template/", kNoMatch, kSupportOne)
+PCKT____(0x8b036f32u, "signature", Signature, "http://www.w3.org/2000/09/xmldsig#", kNoMatch, kSupportOne)
+PCKT____(0x99b95079u, "datasets", Datasets,  "http://www.xfa.org/schema/xfa-data/", kPrefixMatch, kSupportOne)
+PCKT____(0xcd309ff4u, "form", Form, "http://www.xfa.org/schema/xfa-form/", kNoMatch, kSupportOne)
+PCKT____(0xe14c801cu, "connectionSet", ConnectionSet, "http://www.xfa.org/schema/xfa-connection-set/", kNoMatch, kSupportOne)
+PCKT____(0xffffffffu, "user", User, nullptr, kNoMatch, kSupportMany)
diff --git a/xfa/fxfa/parser/xfa_basic_data.cpp b/xfa/fxfa/parser/xfa_basic_data.cpp
index e7b01c9..4ae0dc0 100644
--- a/xfa/fxfa/parser/xfa_basic_data.cpp
+++ b/xfa/fxfa/parser/xfa_basic_data.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,7 @@
 
 #include "xfa/fxfa/parser/xfa_basic_data.h"
 
+#include <iterator>
 #include <utility>
 
 #include "fxjs/xfa/cjx_boolean.h"
@@ -43,19 +44,15 @@
 
 namespace {
 
-struct PacketRecord {
-  XFA_PacketType packet_type;
+struct PacketTableRecord {
   uint32_t hash;
-  uint32_t flags;
-  const wchar_t* name;
-  const wchar_t* uri;
+  XFA_PACKETINFO info;
 };
 
-const PacketRecord g_PacketTable[] = {
+const PacketTableRecord kPacketTable[] = {
 #undef PCKT____
-#define PCKT____(a, b, c, d, e, f)                                          \
-  {XFA_PacketType::c, a, XFA_XDPPACKET_FLAGS_##e | XFA_XDPPACKET_FLAGS_##f, \
-   L##b, d},
+#define PCKT____(a, b, c, d, e, f) \
+  {a, {XFA_PacketType::c, XFA_PacketMatch::e, XFA_PacketSupport::f, b, d}},
 #include "xfa/fxfa/parser/packets.inc"
 #undef PCKT____
 };
@@ -64,146 +61,207 @@
   uint32_t hash;  // Hashed as wide string.
   XFA_Element element;
   XFA_Element parent;
-  const char* name;
 };
 
-const ElementRecord g_ElementTable[] = {
+// Contains read-only data that do not require relocation.
+// Parts that require relocation are in `kElementNames` below.
+constexpr ElementRecord kElementRecords[] = {
 #undef ELEM____
-#define ELEM____(a, b, c, d) {a, XFA_Element::c, XFA_Element::d, b},
+#define ELEM____(a, b, c, d) {a, XFA_Element::c, XFA_Element::d},
 #include "xfa/fxfa/parser/elements.inc"
 #undef ELEM____
 };
 
+constexpr const char* kElementNames[] = {
+#undef ELEM____
+#define ELEM____(a, b, c, d) b,
+#include "xfa/fxfa/parser/elements.inc"
+#undef ELEM____
+};
+
+static_assert(std::size(kElementRecords) == std::size(kElementNames),
+              "Size mismatch");
+
 struct AttributeRecord {
   uint32_t hash;  // Hashed as wide string.
   XFA_Attribute attribute;
   XFA_ScriptType script_type;
-  const char* name;
 };
 
-const AttributeRecord g_AttributeTable[] = {
+// Contains read-only data that do not require relocation.
+// Parts that require relocation are in `kAttributeNames` below.
+constexpr AttributeRecord kAttributeRecords[] = {
 #undef ATTR____
-#define ATTR____(a, b, c, d) {a, XFA_Attribute::c, XFA_ScriptType::d, b},
+#define ATTR____(a, b, c, d) {a, XFA_Attribute::c, XFA_ScriptType::d},
 #include "xfa/fxfa/parser/attributes.inc"
 #undef ATTR____
 };
 
-struct AttributeValueRecord {
-  uint32_t uHash;  // |pName| hashed as WideString.
-  XFA_AttributeValue eName;
-  const char* pName;
+constexpr const char* kAttributeNames[] = {
+#undef ATTR____
+#define ATTR____(a, b, c, d) b,
+#include "xfa/fxfa/parser/attributes.inc"
+#undef ATTR____
 };
 
-const AttributeValueRecord g_AttributeValueTable[] = {
+static_assert(std::size(kAttributeRecords) == std::size(kAttributeNames),
+              "Size mismatch");
+
+struct AttributeValueRecord {
+  // Associated entry in `kAttributeValueNames` hashed as WideString.
+  uint32_t uHash;
+  XFA_AttributeValue eName;
+};
+
+// Contains read-only data that do not require relocation.
+// Parts that require relocation are in `kAttributeValueNames` below.
+constexpr AttributeValueRecord kAttributeValueRecords[] = {
 #undef VALUE____
-#define VALUE____(a, b, c) {a, XFA_AttributeValue::c, b},
+#define VALUE____(a, b, c) {a, XFA_AttributeValue::c},
 #include "xfa/fxfa/parser/attribute_values.inc"
 #undef VALUE____
 };
 
+constexpr const char* kAttributeValueNames[] = {
+#undef VALUE____
+#define VALUE____(a, b, c) b,
+#include "xfa/fxfa/parser/attribute_values.inc"
+#undef VALUE____
+};
+
+static_assert(std::size(kAttributeValueRecords) ==
+                  std::size(kAttributeValueNames),
+              "Size mismatch");
+
 struct ElementAttributeRecord {
   XFA_Element element;
   XFA_Attribute attribute;
-  XFA_ATTRIBUTE_CALLBACK callback;
 };
 
-const ElementAttributeRecord g_ElementAttributeTable[] = {
+// Contains read-only data that do not require relocation.
+// Parts that require relocation are in `kElementAttributeCallbacks` below.
+constexpr ElementAttributeRecord kElementAttributeRecords[] = {
 #undef ELEM_ATTR____
-#define ELEM_ATTR____(a, b, c) {XFA_Element::a, XFA_Attribute::b, c##_static},
+#define ELEM_ATTR____(a, b, c) {XFA_Element::a, XFA_Attribute::b},
 #include "xfa/fxfa/parser/element_attributes.inc"
 #undef ELEM_ATTR____
 };
 
+constexpr XFA_ATTRIBUTE_CALLBACK kElementAttributeCallbacks[] = {
+#undef ELEM_ATTR____
+#define ELEM_ATTR____(a, b, c) c##_static,
+#include "xfa/fxfa/parser/element_attributes.inc"
+#undef ELEM_ATTR____
+};
+
+static_assert(std::size(kElementAttributeRecords) ==
+                  std::size(kElementAttributeCallbacks),
+              "Size mismatch");
+
 }  // namespace
 
 XFA_PACKETINFO XFA_GetPacketByIndex(XFA_PacketType ePacket) {
-  const PacketRecord* pRecord = &g_PacketTable[static_cast<uint8_t>(ePacket)];
-  return {pRecord->name, pRecord->packet_type, pRecord->uri, pRecord->flags};
+  return kPacketTable[static_cast<uint8_t>(ePacket)].info;
 }
 
-Optional<XFA_PACKETINFO> XFA_GetPacketByName(WideStringView wsName) {
-  uint32_t hash = FX_HashCode_GetW(wsName, false);
+absl::optional<XFA_PACKETINFO> XFA_GetPacketByName(WideStringView wsName) {
+  uint32_t hash = FX_HashCode_GetW(wsName);
   auto* elem = std::lower_bound(
-      std::begin(g_PacketTable), std::end(g_PacketTable), hash,
-      [](const PacketRecord& a, uint32_t hash) { return a.hash < hash; });
-  if (elem != std::end(g_PacketTable) && elem->name == wsName)
-    return XFA_GetPacketByIndex(elem->packet_type);
-  return {};
+      std::begin(kPacketTable), std::end(kPacketTable), hash,
+      [](const PacketTableRecord& a, uint32_t hash) { return a.hash < hash; });
+  if (elem != std::end(kPacketTable) && wsName.EqualsASCII(elem->info.name))
+    return elem->info;
+  return absl::nullopt;
 }
 
 ByteStringView XFA_ElementToName(XFA_Element elem) {
-  return g_ElementTable[static_cast<size_t>(elem)].name;
+  return kElementNames[static_cast<size_t>(elem)];
 }
 
 XFA_Element XFA_GetElementByName(WideStringView name) {
-  uint32_t hash = FX_HashCode_GetW(name, false);
+  uint32_t hash = FX_HashCode_GetW(name);
   auto* elem = std::lower_bound(
-      std::begin(g_ElementTable), std::end(g_ElementTable), hash,
+      std::begin(kElementRecords), std::end(kElementRecords), hash,
       [](const ElementRecord& a, uint32_t hash) { return a.hash < hash; });
-  if (elem != std::end(g_ElementTable) && name.EqualsASCII(elem->name))
-    return elem->element;
-  return XFA_Element::Unknown;
+  if (elem == std::end(kElementRecords))
+    return XFA_Element::Unknown;
+
+  size_t index = std::distance(std::begin(kElementRecords), elem);
+  return name.EqualsASCII(kElementNames[index]) ? elem->element
+                                                : XFA_Element::Unknown;
 }
 
 ByteStringView XFA_AttributeToName(XFA_Attribute attr) {
-  return g_AttributeTable[static_cast<size_t>(attr)].name;
+  return kAttributeNames[static_cast<size_t>(attr)];
 }
 
-Optional<XFA_ATTRIBUTEINFO> XFA_GetAttributeByName(WideStringView name) {
-  uint32_t hash = FX_HashCode_GetW(name, false);
+absl::optional<XFA_ATTRIBUTEINFO> XFA_GetAttributeByName(WideStringView name) {
+  uint32_t hash = FX_HashCode_GetW(name);
   auto* elem = std::lower_bound(
-      std::begin(g_AttributeTable), std::end(g_AttributeTable), hash,
+      std::begin(kAttributeRecords), std::end(kAttributeRecords), hash,
       [](const AttributeRecord& a, uint32_t hash) { return a.hash < hash; });
-  if (elem != std::end(g_AttributeTable) && name.EqualsASCII(elem->name)) {
-    XFA_ATTRIBUTEINFO result;
-    result.attribute = elem->attribute;
-    result.eValueType = elem->script_type;
-    return result;
-  }
-  return {};
+  if (elem == std::end(kAttributeRecords))
+    return absl::nullopt;
+
+  size_t index = std::distance(std::begin(kAttributeRecords), elem);
+  if (!name.EqualsASCII(kAttributeNames[index]))
+    return absl::nullopt;
+
+  XFA_ATTRIBUTEINFO result;
+  result.attribute = elem->attribute;
+  result.eValueType = elem->script_type;
+  return result;
 }
 
 ByteStringView XFA_AttributeValueToName(XFA_AttributeValue item) {
-  return g_AttributeValueTable[static_cast<int32_t>(item)].pName;
+  return kAttributeValueNames[static_cast<int32_t>(item)];
 }
 
-Optional<XFA_AttributeValue> XFA_GetAttributeValueByName(WideStringView name) {
-  auto* it = std::lower_bound(std::begin(g_AttributeValueTable),
-                              std::end(g_AttributeValueTable),
-                              FX_HashCode_GetW(name, false),
-                              [](const AttributeValueRecord& arg,
-                                 uint32_t hash) { return arg.uHash < hash; });
-  if (it != std::end(g_AttributeValueTable) && name.EqualsASCII(it->pName))
-    return it->eName;
+absl::optional<XFA_AttributeValue> XFA_GetAttributeValueByName(
+    WideStringView name) {
+  auto* it =
+      std::lower_bound(std::begin(kAttributeValueRecords),
+                       std::end(kAttributeValueRecords), FX_HashCode_GetW(name),
+                       [](const AttributeValueRecord& arg, uint32_t hash) {
+                         return arg.uHash < hash;
+                       });
+  if (it == std::end(kAttributeValueRecords))
+    return absl::nullopt;
 
-  return {};
+  size_t index = std::distance(std::begin(kAttributeValueRecords), it);
+  if (!name.EqualsASCII(kAttributeValueNames[index]))
+    return absl::nullopt;
+
+  return it->eName;
 }
 
-Optional<XFA_SCRIPTATTRIBUTEINFO> XFA_GetScriptAttributeByName(
+absl::optional<XFA_SCRIPTATTRIBUTEINFO> XFA_GetScriptAttributeByName(
     XFA_Element element,
     WideStringView attribute_name) {
-  Optional<XFA_ATTRIBUTEINFO> attr = XFA_GetAttributeByName(attribute_name);
+  absl::optional<XFA_ATTRIBUTEINFO> attr =
+      XFA_GetAttributeByName(attribute_name);
   if (!attr.has_value())
-    return {};
+    return absl::nullopt;
 
   while (element != XFA_Element::Unknown) {
     auto compound_key = std::make_pair(element, attr.value().attribute);
     auto* it = std::lower_bound(
-        std::begin(g_ElementAttributeTable), std::end(g_ElementAttributeTable),
-        compound_key,
+        std::begin(kElementAttributeRecords),
+        std::end(kElementAttributeRecords), compound_key,
         [](const ElementAttributeRecord& arg,
            const std::pair<XFA_Element, XFA_Attribute>& key) {
           return std::make_pair(arg.element, arg.attribute) < key;
         });
-    if (it != std::end(g_ElementAttributeTable) &&
+    if (it != std::end(kElementAttributeRecords) &&
         compound_key == std::make_pair(it->element, it->attribute)) {
       XFA_SCRIPTATTRIBUTEINFO result;
       result.attribute = attr.value().attribute;
       result.eValueType = attr.value().eValueType;
-      result.callback = it->callback;
+      size_t index = std::distance(std::begin(kElementAttributeRecords), it);
+      result.callback = kElementAttributeCallbacks[index];
       return result;
     }
-    element = g_ElementTable[static_cast<size_t>(element)].parent;
+    element = kElementRecords[static_cast<size_t>(element)].parent;
   }
-  return {};
+  return absl::nullopt;
 }
diff --git a/xfa/fxfa/parser/xfa_basic_data.h b/xfa/fxfa/parser/xfa_basic_data.h
index 4c3e0c7..b338bec 100644
--- a/xfa/fxfa/parser/xfa_basic_data.h
+++ b/xfa/fxfa/parser/xfa_basic_data.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,14 +11,32 @@
 
 #include "core/fxcrt/widestring.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
+using XFA_ATTRIBUTE_CALLBACK = void (*)(v8::Isolate* pIsolate,
+                                        CJX_Object* pNode,
+                                        v8::Local<v8::Value>* pValue,
+                                        bool bSetting,
+                                        XFA_Attribute eAttribute);
+
+enum class XFA_PacketMatch : uint8_t {
+  kCompleteMatch = 1,
+  kPrefixMatch,
+  kNoMatch,
+};
+
+enum class XFA_PacketSupport : uint8_t {
+  kSupportOne = 1,
+  kSupportMany,
+};
+
 struct XFA_PACKETINFO {
-  const wchar_t* name;
   XFA_PacketType packet_type;
-  const wchar_t* uri;
-  uint32_t flags;
+  XFA_PacketMatch match;
+  XFA_PacketSupport support;
+  const char* name;
+  const char* uri;
 };
 
 struct XFA_ATTRIBUTEINFO {
@@ -33,18 +51,19 @@
 };
 
 XFA_PACKETINFO XFA_GetPacketByIndex(XFA_PacketType ePacket);
-Optional<XFA_PACKETINFO> XFA_GetPacketByName(WideStringView wsName);
+absl::optional<XFA_PACKETINFO> XFA_GetPacketByName(WideStringView wsName);
 
 ByteStringView XFA_ElementToName(XFA_Element elem);
 XFA_Element XFA_GetElementByName(WideStringView name);
 
 ByteStringView XFA_AttributeToName(XFA_Attribute attr);
-Optional<XFA_ATTRIBUTEINFO> XFA_GetAttributeByName(WideStringView name);
+absl::optional<XFA_ATTRIBUTEINFO> XFA_GetAttributeByName(WideStringView name);
 
 ByteStringView XFA_AttributeValueToName(XFA_AttributeValue item);
-Optional<XFA_AttributeValue> XFA_GetAttributeValueByName(WideStringView name);
+absl::optional<XFA_AttributeValue> XFA_GetAttributeValueByName(
+    WideStringView name);
 
-Optional<XFA_SCRIPTATTRIBUTEINFO> XFA_GetScriptAttributeByName(
+absl::optional<XFA_SCRIPTATTRIBUTEINFO> XFA_GetScriptAttributeByName(
     XFA_Element eElement,
     WideStringView wsAttributeName);
 
diff --git a/xfa/fxfa/parser/xfa_basic_data_unittest.cpp b/xfa/fxfa/parser/xfa_basic_data_unittest.cpp
index b9307d5..f30d655 100644
--- a/xfa/fxfa/parser/xfa_basic_data_unittest.cpp
+++ b/xfa/fxfa/parser/xfa_basic_data_unittest.cpp
@@ -1,15 +1,13 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "xfa/fxfa/parser/xfa_basic_data.h"
 
-#include <utility>
-
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(XFABasicDataTest, GetPacketByName) {
-  Optional<XFA_PACKETINFO> result = XFA_GetPacketByName(L"");
+  absl::optional<XFA_PACKETINFO> result = XFA_GetPacketByName(L"");
   EXPECT_FALSE(result.has_value());
 
   result = XFA_GetPacketByName(L"nonesuch");
@@ -26,10 +24,10 @@
 
 TEST(XFABasicDataTest, PacketToName) {
   XFA_PACKETINFO result = XFA_GetPacketByIndex(XFA_PacketType::Datasets);
-  EXPECT_STREQ(L"datasets", result.name);
+  EXPECT_STREQ("datasets", result.name);
 
   result = XFA_GetPacketByIndex(XFA_PacketType::ConnectionSet);
-  EXPECT_STREQ(L"connectionSet", result.name);
+  EXPECT_STREQ("connectionSet", result.name);
 }
 
 TEST(XFABasicDataTest, GetElementByName) {
@@ -51,7 +49,7 @@
 }
 
 TEST(XFABasicDataTest, GetAttributeByName) {
-  Optional<XFA_ATTRIBUTEINFO> result = XFA_GetAttributeByName(L"");
+  absl::optional<XFA_ATTRIBUTEINFO> result = XFA_GetAttributeByName(L"");
   EXPECT_FALSE(result.has_value());
 
   result = XFA_GetAttributeByName(L"nonesuch");
@@ -76,7 +74,7 @@
 }
 
 TEST(XFABasicDataTest, GetAttributeValueByName) {
-  Optional<XFA_AttributeValue> result = XFA_GetAttributeValueByName(L"");
+  absl::optional<XFA_AttributeValue> result = XFA_GetAttributeValueByName(L"");
   EXPECT_FALSE(result.has_value());
 
   result = XFA_GetAttributeValueByName(L"nonesuch");
diff --git a/xfa/fxfa/parser/xfa_document_datamerger_imp.cpp b/xfa/fxfa/parser/xfa_document_datamerger_imp.cpp
index dfd8cf0..01cabb7 100644
--- a/xfa/fxfa/parser/xfa_document_datamerger_imp.cpp
+++ b/xfa/fxfa/parser/xfa_document_datamerger_imp.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,7 +23,6 @@
   return true;
 }
 
-
 CXFA_Node* XFA_DataMerge_FindFormDOMInstance(CXFA_Document* pDocument,
                                              XFA_Element eType,
                                              uint32_t dwNameHash,
@@ -60,7 +59,7 @@
       pFormParent->RemoveChildAndNotify(pExistingNode, true);
       pFormParent->InsertChildAndNotify(pExistingNode, nullptr);
     }
-    pExistingNode->ClearFlag(XFA_NodeFlag_UnusedNode);
+    pExistingNode->ClearFlag(XFA_NodeFlag::kUnusedNode);
     pExistingNode->SetTemplateNode(pTemplateNode);
     if (bRecursive && pExistingNode->GetElementType() != XFA_Element::Items) {
       for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
@@ -71,7 +70,7 @@
         }
       }
     }
-    pExistingNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+    pExistingNode->SetInitializedFlagAndNotify();
     return pExistingNode;
   }
 
@@ -103,4 +102,3 @@
   return ToNode(
       pParentFormNode->GetDocument()->GetXFAObject(XFA_HASHCODE_Data));
 }
-
diff --git a/xfa/fxfa/parser/xfa_document_datamerger_imp.h b/xfa/fxfa/parser/xfa_document_datamerger_imp.h
index d2c84d3..aa56402 100644
--- a/xfa/fxfa/parser/xfa_document_datamerger_imp.h
+++ b/xfa/fxfa/parser/xfa_document_datamerger_imp.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/xfa/fxfa/parser/xfa_resolvenode_rs.h b/xfa/fxfa/parser/xfa_resolvenode_rs.h
deleted file mode 100644
index dd9b79a..0000000
--- a/xfa/fxfa/parser/xfa_resolvenode_rs.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_PARSER_XFA_RESOLVENODE_RS_H_
-#define XFA_FXFA_PARSER_XFA_RESOLVENODE_RS_H_
-
-#include <vector>
-
-#include "core/fxcrt/unowned_ptr.h"
-#include "xfa/fxfa/parser/xfa_basic_data.h"
-
-class CXFA_Object;
-
-enum XFA_ResolveNode_RSType {
-  XFA_ResolveNode_RSType_Nodes,
-  XFA_ResolveNode_RSType_Attribute,
-  XFA_ResolveNode_RSType_CreateNodeOne,
-  XFA_ResolveNode_RSType_CreateNodeAll,
-  XFA_ResolveNode_RSType_CreateNodeMidAll,
-  XFA_ResolveNode_RSType_ExistNodes,
-};
-
-struct XFA_RESOLVENODE_RS {
-  XFA_RESOLVENODE_RS();
-  ~XFA_RESOLVENODE_RS();
-
-  XFA_ResolveNode_RSType dwFlags = XFA_ResolveNode_RSType_Nodes;
-  XFA_SCRIPTATTRIBUTEINFO script_attribute;
-  std::vector<UnownedPtr<CXFA_Object>> objects;
-};
-
-inline XFA_RESOLVENODE_RS::XFA_RESOLVENODE_RS() = default;
-
-inline XFA_RESOLVENODE_RS::~XFA_RESOLVENODE_RS() = default;
-
-#endif  // XFA_FXFA_PARSER_XFA_RESOLVENODE_RS_H_
diff --git a/xfa/fxfa/parser/xfa_utils.cpp b/xfa/fxfa/parser/xfa_utils.cpp
index e9a01ba..a594ffd 100644
--- a/xfa/fxfa/parser/xfa_utils.cpp
+++ b/xfa/fxfa/parser/xfa_utils.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,18 +10,17 @@
 #include <vector>
 
 #include "core/fxcrt/cfx_memorystream.h"
-#include "core/fxcrt/cfx_widetextbuf.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/widetext_buffer.h"
 #include "core/fxcrt/xml/cfx_xmlchardata.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
 #include "core/fxcrt/xml/cfx_xmltext.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_localemgr.h"
-#include "xfa/fxfa/parser/cxfa_localevalue.h"
 #include "xfa/fxfa/parser/cxfa_measurement.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_ui.h"
@@ -33,30 +32,30 @@
 const char kFormNS[] = "http://www.xfa.org/schema/xfa-form/";
 
 WideString ExportEncodeAttribute(const WideString& str) {
-  CFX_WideTextBuf textBuf;
-  int32_t iLen = str.GetLength();
-  for (int32_t i = 0; i < iLen; i++) {
+  WideString textBuf;
+  textBuf.Reserve(str.GetLength());  // Result always at least as big as input.
+  for (size_t i = 0; i < str.GetLength(); i++) {
     switch (str[i]) {
       case '&':
-        textBuf << "&amp;";
+        textBuf += L"&amp;";
         break;
       case '<':
-        textBuf << "&lt;";
+        textBuf += L"&lt;";
         break;
       case '>':
-        textBuf << "&gt;";
+        textBuf += L"&gt;";
         break;
       case '\'':
-        textBuf << "&apos;";
+        textBuf += L"&apos;";
         break;
       case '\"':
-        textBuf << "&quot;";
+        textBuf += L"&quot;";
         break;
       default:
-        textBuf.AppendChar(str[i]);
+        textBuf += str[i];
     }
   }
-  return textBuf.MakeString();
+  return textBuf;
 }
 
 bool IsXMLValidChar(wchar_t ch) {
@@ -65,9 +64,9 @@
 }
 
 WideString ExportEncodeContent(const WideString& str) {
-  CFX_WideTextBuf textBuf;
-  int32_t iLen = str.GetLength();
-  for (int32_t i = 0; i < iLen; i++) {
+  WideTextBuffer textBuf;
+  size_t iLen = str.GetLength();
+  for (size_t i = 0; i < iLen; i++) {
     wchar_t ch = str[i];
     if (!IsXMLValidChar(ch))
       continue;
@@ -113,12 +112,12 @@
 }
 
 bool ContentNodeNeedtoExport(CXFA_Node* pContentNode) {
-  Optional<WideString> wsContent =
+  absl::optional<WideString> wsContent =
       pContentNode->JSObject()->TryContent(false, false);
-  if (!wsContent)
+  if (!wsContent.has_value())
     return false;
 
-  ASSERT(pContentNode->IsContentNode());
+  DCHECK(pContentNode->IsContentNode());
   CXFA_Node* pParentNode = pContentNode->GetParent();
   if (!pParentNode || pParentNode->GetElementType() != XFA_Element::Value)
     return true;
@@ -133,27 +132,24 @@
   return true;
 }
 
-void SaveAttribute(CXFA_Node* pNode,
-                   XFA_Attribute eName,
-                   const WideString& wsName,
-                   bool bProto,
-                   WideString& wsOutput) {
+WideString SaveAttribute(CXFA_Node* pNode,
+                         XFA_Attribute eName,
+                         WideStringView wsName,
+                         bool bProto) {
   if (!bProto && !pNode->JSObject()->HasAttribute(eName))
-    return;
+    return WideString();
 
-  Optional<WideString> value = pNode->JSObject()->TryAttribute(eName, false);
-  if (!value)
-    return;
+  absl::optional<WideString> value =
+      pNode->JSObject()->TryAttribute(eName, false);
+  if (!value.has_value())
+    return WideString();
 
-  wsOutput += L" ";
-  wsOutput += wsName;
-  wsOutput += L"=\"";
-  wsOutput += ExportEncodeAttribute(*value);
-  wsOutput += L"\"";
+  WideString wsEncoded = ExportEncodeAttribute(value.value());
+  return WideString{L" ", wsName, L"=\"", wsEncoded.AsStringView(), L"\""};
 }
 
 void RegenerateFormFile_Changed(CXFA_Node* pNode,
-                                CFX_WideTextBuf& buf,
+                                WideTextBuffer& buf,
                                 bool bSaveXML) {
   WideString wsAttrs;
   for (size_t i = 0;; ++i) {
@@ -165,10 +161,8 @@
         (AttributeSaveInDataModel(pNode, attr) && !bSaveXML)) {
       continue;
     }
-    WideString wsAttr;
-    SaveAttribute(pNode, attr, WideString::FromASCII(XFA_AttributeToName(attr)),
-                  bSaveXML, wsAttr);
-    wsAttrs += wsAttr;
+    WideString wsAttr = WideString::FromASCII(XFA_AttributeToName(attr));
+    wsAttrs += SaveAttribute(pNode, attr, wsAttr.AsStringView(), bSaveXML);
   }
 
   WideString wsChildren;
@@ -187,7 +181,7 @@
       if (!pRawValueNode)
         break;
 
-      Optional<WideString> contentType =
+      absl::optional<WideString> contentType =
           pNode->JSObject()->TryAttribute(XFA_Attribute::ContentType, false);
       if (pRawValueNode->GetElementType() == XFA_Element::SharpxHTML &&
           contentType.has_value() &&
@@ -202,14 +196,15 @@
 
         auto pMemStream = pdfium::MakeRetain<CFX_MemoryStream>();
         pRichTextXML->Save(pMemStream);
-        wsChildren += WideString::FromUTF8(
-            ByteStringView(pMemStream->GetBuffer(), pMemStream->GetSize()));
+        wsChildren +=
+            WideString::FromUTF8(ByteStringView(pMemStream->GetSpan()));
       } else if (pRawValueNode->GetElementType() == XFA_Element::Sharpxml &&
                  contentType.has_value() &&
                  contentType.value().EqualsASCII("text/xml")) {
-        Optional<WideString> rawValue = pRawValueNode->JSObject()->TryAttribute(
-            XFA_Attribute::Value, false);
-        if (!rawValue || rawValue->IsEmpty())
+        absl::optional<WideString> rawValue =
+            pRawValueNode->JSObject()->TryAttribute(XFA_Attribute::Value,
+                                                    false);
+        if (!rawValue.has_value() || rawValue->IsEmpty())
           break;
 
         std::vector<WideString> wsSelTextArray =
@@ -222,18 +217,10 @@
         if (bodyTagName.IsEmpty())
           bodyTagName = L"ListBox1";
 
-        buf << "<";
-        buf << bodyTagName;
-        buf << " xmlns=\"\"\n>";
-        for (int32_t i = 0; i < pdfium::CollectionSize<int32_t>(wsSelTextArray);
-             i++) {
-          buf << "<value\n>";
-          buf << ExportEncodeContent(wsSelTextArray[i]);
-          buf << "</value\n>";
-        }
-        buf << "</";
-        buf << bodyTagName;
-        buf << "\n>";
+        buf << "<" << bodyTagName << " xmlns=\"\">\n";
+        for (const WideString& text : wsSelTextArray)
+          buf << "<value>" << ExportEncodeContent(text) << "</value>\n";
+        buf << "</" << bodyTagName << ">\n";
         wsChildren += buf.AsStringView();
         buf.Clear();
       } else {
@@ -259,7 +246,7 @@
           bSaveXML = true;
         }
       }
-      CFX_WideTextBuf newBuf;
+      WideTextBuffer newBuf;
       CXFA_Node* pChildNode = pNode->GetFirstChild();
       while (pChildNode) {
         RegenerateFormFile_Changed(pChildNode, newBuf, bSaveXML);
@@ -285,20 +272,14 @@
   if (!wsChildren.IsEmpty() || !wsAttrs.IsEmpty() ||
       pNode->JSObject()->HasAttribute(XFA_Attribute::Name)) {
     WideString wsElement = WideString::FromASCII(pNode->GetClassName());
-    WideString wsName;
-    SaveAttribute(pNode, XFA_Attribute::Name, L"name", true, wsName);
     buf << "<";
     buf << wsElement;
-    buf << wsName;
+    buf << SaveAttribute(pNode, XFA_Attribute::Name, L"name", true);
     buf << wsAttrs;
     if (wsChildren.IsEmpty()) {
-      buf << "\n/>";
+      buf << "/>\n";
     } else {
-      buf << "\n>";
-      buf << wsChildren;
-      buf << "</";
-      buf << wsElement;
-      buf << "\n>";
+      buf << ">\n" << wsChildren << "</" << wsElement << ">\n";
     }
   }
 }
@@ -309,7 +290,7 @@
   XFA_Element eType = pNode->GetElementType();
   if (eType == XFA_Element::Field || eType == XFA_Element::Draw ||
       !pNode->IsContainerNode()) {
-    CFX_WideTextBuf buf;
+    WideTextBuffer buf;
     RegenerateFormFile_Changed(pNode, buf, bSaveXML);
     size_t nLen = buf.GetLength();
     if (nLen > 0)
@@ -321,10 +302,8 @@
   pStream->WriteString("<");
   pStream->WriteString(wsElement.ToUTF8().AsStringView());
 
-  WideString wsOutput;
-  SaveAttribute(pNode, XFA_Attribute::Name, L"name", true, wsOutput);
-
-  WideString wsAttrs;
+  WideString wsOutput =
+      SaveAttribute(pNode, XFA_Attribute::Name, L"name", true);
   for (size_t i = 0;; ++i) {
     XFA_Attribute attr = pNode->GetAttribute(i);
     if (attr == XFA_Attribute::Unknown)
@@ -332,10 +311,8 @@
     if (attr == XFA_Attribute::Name)
       continue;
 
-    WideString wsAttr;
-    SaveAttribute(pNode, attr, WideString::FromASCII(XFA_AttributeToName(attr)),
-                  false, wsAttr);
-    wsOutput += wsAttr;
+    WideString wsAttr = WideString::FromASCII(XFA_AttributeToName(attr));
+    wsOutput += SaveAttribute(pNode, attr, wsAttr.AsStringView(), false);
   }
 
   if (!wsOutput.IsEmpty())
@@ -361,12 +338,14 @@
   if (!pTemplateRoot)
     return WideString();
 
-  Optional<WideString> templateNS = pTemplateRoot->JSObject()->TryNamespace();
-  if (!templateNS)
+  absl::optional<WideString> templateNS =
+      pTemplateRoot->JSObject()->TryNamespace();
+  if (!templateNS.has_value())
     return WideString();
 
   XFA_VERSION eVersion =
-      pTemplateRoot->GetDocument()->RecognizeXFAVersionNumber(*templateNS);
+      pTemplateRoot->GetDocument()->RecognizeXFAVersionNumber(
+          templateNS.value());
   if (eVersion == XFA_VERSION_UNKNOWN)
     eVersion = XFA_VERSION_DEFAULT;
 
@@ -375,8 +354,8 @@
 
 }  // namespace
 
-CXFA_LocaleValue XFA_GetLocaleValue(CXFA_Node* pNode) {
-  CXFA_Value* pNodeValue =
+CXFA_LocaleValue XFA_GetLocaleValue(const CXFA_Node* pNode) {
+  const auto* pNodeValue =
       pNode->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
   if (!pNodeValue)
     return CXFA_LocaleValue();
@@ -385,40 +364,34 @@
   if (!pValueChild)
     return CXFA_LocaleValue();
 
-  int32_t iVTType = XFA_VT_NULL;
-  switch (pValueChild->GetElementType()) {
-    case XFA_Element::Decimal:
-      iVTType = XFA_VT_DECIMAL;
-      break;
-    case XFA_Element::Float:
-      iVTType = XFA_VT_FLOAT;
-      break;
-    case XFA_Element::Date:
-      iVTType = XFA_VT_DATE;
-      break;
-    case XFA_Element::Time:
-      iVTType = XFA_VT_TIME;
-      break;
-    case XFA_Element::DateTime:
-      iVTType = XFA_VT_DATETIME;
-      break;
-    case XFA_Element::Boolean:
-      iVTType = XFA_VT_BOOLEAN;
-      break;
-    case XFA_Element::Integer:
-      iVTType = XFA_VT_INTEGER;
-      break;
-    case XFA_Element::Text:
-      iVTType = XFA_VT_TEXT;
-      break;
-    default:
-      iVTType = XFA_VT_NULL;
-      break;
-  }
-  return CXFA_LocaleValue(iVTType, pNode->GetRawValue(),
+  return CXFA_LocaleValue(XFA_GetLocaleValueType(pValueChild->GetElementType()),
+                          pNode->GetRawValue(),
                           pNode->GetDocument()->GetLocaleMgr());
 }
 
+CXFA_LocaleValue::ValueType XFA_GetLocaleValueType(XFA_Element element) {
+  switch (element) {
+    case XFA_Element::Decimal:
+      return CXFA_LocaleValue::ValueType::kDecimal;
+    case XFA_Element::Float:
+      return CXFA_LocaleValue::ValueType::kFloat;
+    case XFA_Element::Date:
+      return CXFA_LocaleValue::ValueType::kDate;
+    case XFA_Element::Time:
+      return CXFA_LocaleValue::ValueType::kTime;
+    case XFA_Element::DateTime:
+      return CXFA_LocaleValue::ValueType::kDateTime;
+    case XFA_Element::Boolean:
+      return CXFA_LocaleValue::ValueType::kBoolean;
+    case XFA_Element::Integer:
+      return CXFA_LocaleValue::ValueType::kInteger;
+    case XFA_Element::Text:
+      return CXFA_LocaleValue::ValueType::kText;
+    default:
+      return CXFA_LocaleValue::ValueType::kNull;
+  }
+}
+
 bool XFA_FDEExtension_ResolveNamespaceQualifier(CFX_XMLElement* pNode,
                                                 const WideString& wsQualifier,
                                                 WideString* wsNamespaceURI) {
@@ -462,8 +435,7 @@
 
   CFX_XMLElement* pElement = ToXMLElement(pDataNode->GetXMLMappingNode());
   if (iChildNum > 0) {
-    if (pElement->HasAttribute(L"xfa:dataNode"))
-      pElement->RemoveAttribute(L"xfa:dataNode");
+    pElement->RemoveAttribute(L"xfa:dataNode");
     return;
   }
   pElement->SetAttribute(L"xfa:dataNode", L"dataGroup");
@@ -482,7 +454,7 @@
     if (wsVersionNumber.IsEmpty())
       wsVersionNumber = L"2.8";
 
-    wsVersionNumber += L"/\"\n>";
+    wsVersionNumber += L"/\">\n";
     pStream->WriteString(wsVersionNumber.ToUTF8().AsStringView());
 
     CXFA_Node* pChildNode = pNode->GetFirstChild();
@@ -490,17 +462,18 @@
       RegenerateFormFile_Container(pChildNode, pStream, false);
       pChildNode = pChildNode->GetNextSibling();
     }
-    pStream->WriteString("</form\n>");
+    pStream->WriteString("</form>\n");
   } else {
     RegenerateFormFile_Container(pNode, pStream, bSaveXML);
   }
 }
 
-bool XFA_FieldIsMultiListBox(CXFA_Node* pFieldNode) {
+bool XFA_FieldIsMultiListBox(const CXFA_Node* pFieldNode) {
   if (!pFieldNode)
     return false;
 
-  CXFA_Ui* pUIChild = pFieldNode->GetChild<CXFA_Ui>(0, XFA_Element::Ui, false);
+  const auto* pUIChild =
+      pFieldNode->GetChild<CXFA_Ui>(0, XFA_Element::Ui, false);
   if (!pUIChild)
     return false;
 
diff --git a/xfa/fxfa/parser/xfa_utils.h b/xfa/fxfa/parser/xfa_utils.h
index 89c30c3..2e6565a 100644
--- a/xfa/fxfa/parser/xfa_utils.h
+++ b/xfa/fxfa/parser/xfa_utils.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,23 +8,25 @@
 #define XFA_FXFA_PARSER_XFA_UTILS_H_
 
 #include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/widestring.h"
 #include "xfa/fxfa/fxfa.h"
 #include "xfa/fxfa/fxfa_basic.h"
+#include "xfa/fxfa/parser/cxfa_localevalue.h"
 
 class CFX_XMLElement;
-class CFX_XMLNode;
-class CXFA_LocaleValue;
 class CXFA_Node;
 
 bool XFA_FDEExtension_ResolveNamespaceQualifier(CFX_XMLElement* pNode,
                                                 const WideString& wsQualifier,
                                                 WideString* wsNamespaceURI);
 
-CXFA_LocaleValue XFA_GetLocaleValue(CXFA_Node* pNode);
+CXFA_LocaleValue XFA_GetLocaleValue(const CXFA_Node* pNode);
+CXFA_LocaleValue::ValueType XFA_GetLocaleValueType(XFA_Element element);
 int32_t XFA_MapRotation(int32_t nRotation);
 
 bool XFA_RecognizeRichText(CFX_XMLElement* pRichTextXMLNode);
-bool XFA_FieldIsMultiListBox(CXFA_Node* pFieldNode);
+bool XFA_FieldIsMultiListBox(const CXFA_Node* pFieldNode);
 
 void XFA_DataExporter_DealWithDataGroupNode(CXFA_Node* pDataNode);
 void XFA_DataExporter_RegenerateFormFile(
diff --git a/xfa/fxfa/parser/xfa_utils_unittest.cpp b/xfa/fxfa/parser/xfa_utils_unittest.cpp
index e4d6cb8..c20f7b0 100644
--- a/xfa/fxfa/parser/xfa_utils_unittest.cpp
+++ b/xfa/fxfa/parser/xfa_utils_unittest.cpp
@@ -1,9 +1,11 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "xfa/fxfa/parser/xfa_utils.h"
 
+#include <iterator>
+
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(XfaUtilsImpTest, XFA_MapRotation) {
@@ -16,7 +18,7 @@
                    {91, 91},       {359, 359},  {360, 0},   {361, 1},
                    {100000, 280}};
 
-  for (size_t i = 0; i < FX_ArraySize(TestCases); ++i) {
+  for (size_t i = 0; i < std::size(TestCases); ++i) {
     EXPECT_EQ(TestCases[i].expected_output,
               XFA_MapRotation(TestCases[i].input));
   }
diff --git a/xfa/fxgraphics/BUILD.gn b/xfa/fxgraphics/BUILD.gn
deleted file mode 100644
index b1b0204..0000000
--- a/xfa/fxgraphics/BUILD.gn
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("../../pdfium.gni")
-
-assert(pdf_enable_xfa)
-
-source_set("fxgraphics") {
-  sources = [
-    "cxfa_gecolor.cpp",
-    "cxfa_gecolor.h",
-    "cxfa_gepath.cpp",
-    "cxfa_gepath.h",
-    "cxfa_gepattern.cpp",
-    "cxfa_gepattern.h",
-    "cxfa_geshading.cpp",
-    "cxfa_geshading.h",
-    "cxfa_graphics.cpp",
-    "cxfa_graphics.h",
-  ]
-  configs += [
-    "../../:pdfium_core_config",
-    "../:xfa_warnings",
-  ]
-  deps = [
-    "../../core/fxcrt",
-    "../../core/fxge",
-  ]
-  visibility = [ "../../*" ]
-}
diff --git a/xfa/fxgraphics/cxfa_gecolor.cpp b/xfa/fxgraphics/cxfa_gecolor.cpp
deleted file mode 100644
index 32dec96..0000000
--- a/xfa/fxgraphics/cxfa_gecolor.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxgraphics/cxfa_gecolor.h"
-
-CXFA_GEColor::CXFA_GEColor(const FX_ARGB argb) : m_type(Solid), m_argb(argb) {}
-
-CXFA_GEColor::CXFA_GEColor(CXFA_GEPattern* pattern, const FX_ARGB argb)
-    : m_type(Pattern), m_argb(argb), m_pPattern(pattern) {}
-
-CXFA_GEColor::CXFA_GEColor(CXFA_GEShading* shading)
-    : m_type(Shading), m_pShading(shading) {}
-
-CXFA_GEColor::CXFA_GEColor(const CXFA_GEColor& that) = default;
-
-CXFA_GEColor::~CXFA_GEColor() = default;
-
-CXFA_GEColor& CXFA_GEColor::operator=(const CXFA_GEColor& that) = default;
diff --git a/xfa/fxgraphics/cxfa_gecolor.h b/xfa/fxgraphics/cxfa_gecolor.h
deleted file mode 100644
index 9cf40aa..0000000
--- a/xfa/fxgraphics/cxfa_gecolor.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXGRAPHICS_CXFA_GECOLOR_H_
-#define XFA_FXGRAPHICS_CXFA_GECOLOR_H_
-
-#include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_dib.h"
-
-class CXFA_GEPattern;
-class CXFA_GEShading;
-
-class CXFA_GEColor {
- public:
-  enum Type { Invalid, Solid, Pattern, Shading };
-
-  explicit CXFA_GEColor(const FX_ARGB argb);
-  explicit CXFA_GEColor(CXFA_GEShading* shading);
-  CXFA_GEColor(CXFA_GEPattern* pattern, const FX_ARGB argb);
-  CXFA_GEColor(const CXFA_GEColor& that);
-  ~CXFA_GEColor();
-
-  Type GetType() const { return m_type; }
-  FX_ARGB GetArgb() const {
-    ASSERT(m_type == Solid || m_type == Pattern);
-    return m_argb;
-  }
-  CXFA_GEPattern* GetPattern() const {
-    ASSERT(m_type == Pattern);
-    return m_pPattern.Get();
-  }
-  CXFA_GEShading* GetShading() const {
-    ASSERT(m_type == Shading);
-    return m_pShading.Get();
-  }
-
-  CXFA_GEColor& operator=(const CXFA_GEColor& that);
-
- private:
-  Type m_type = Invalid;
-  FX_ARGB m_argb = 0;
-  UnownedPtr<CXFA_GEPattern> m_pPattern;
-  UnownedPtr<CXFA_GEShading> m_pShading;
-};
-
-#endif  // XFA_FXGRAPHICS_CXFA_GECOLOR_H_
diff --git a/xfa/fxgraphics/cxfa_gepath.cpp b/xfa/fxgraphics/cxfa_gepath.cpp
deleted file mode 100644
index 6597c80..0000000
--- a/xfa/fxgraphics/cxfa_gepath.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxgraphics/cxfa_gepath.h"
-
-#include "core/fxge/cfx_pathdata.h"
-
-CXFA_GEPath::CXFA_GEPath() {}
-
-CXFA_GEPath::~CXFA_GEPath() {}
-
-void CXFA_GEPath::Clear() {
-  data_.Clear();
-}
-
-void CXFA_GEPath::Close() {
-  data_.ClosePath();
-}
-
-void CXFA_GEPath::MoveTo(const CFX_PointF& point) {
-  data_.AppendPoint(point, FXPT_TYPE::MoveTo, false);
-}
-
-void CXFA_GEPath::LineTo(const CFX_PointF& point) {
-  data_.AppendPoint(point, FXPT_TYPE::LineTo, false);
-}
-
-void CXFA_GEPath::BezierTo(const CFX_PointF& c1,
-                           const CFX_PointF& c2,
-                           const CFX_PointF& to) {
-  data_.AppendPoint(c1, FXPT_TYPE::BezierTo, false);
-  data_.AppendPoint(c2, FXPT_TYPE::BezierTo, false);
-  data_.AppendPoint(to, FXPT_TYPE::BezierTo, false);
-}
-
-void CXFA_GEPath::ArcTo(const CFX_PointF& pos,
-                        const CFX_SizeF& size,
-                        float start_angle,
-                        float sweep_angle) {
-  CFX_SizeF new_size = size / 2.0f;
-  ArcToInternal(CFX_PointF(pos.x + new_size.width, pos.y + new_size.height),
-                new_size, start_angle, sweep_angle);
-}
-
-void CXFA_GEPath::ArcToInternal(const CFX_PointF& pos,
-                                const CFX_SizeF& size,
-                                float start_angle,
-                                float sweep_angle) {
-  float x0 = cos(sweep_angle / 2);
-  float y0 = sin(sweep_angle / 2);
-  float tx = ((1.0f - x0) * 4) / (3 * 1.0f);
-  float ty = y0 - ((tx * x0) / y0);
-
-  CFX_PointF points[] = {CFX_PointF(x0 + tx, -ty), CFX_PointF(x0 + tx, ty)};
-  float sn = sin(start_angle + sweep_angle / 2);
-  float cs = cos(start_angle + sweep_angle / 2);
-
-  CFX_PointF bezier;
-  bezier.x = pos.x + (size.width * ((points[0].x * cs) - (points[0].y * sn)));
-  bezier.y = pos.y + (size.height * ((points[0].x * sn) + (points[0].y * cs)));
-  data_.AppendPoint(bezier, FXPT_TYPE::BezierTo, false);
-
-  bezier.x = pos.x + (size.width * ((points[1].x * cs) - (points[1].y * sn)));
-  bezier.y = pos.y + (size.height * ((points[1].x * sn) + (points[1].y * cs)));
-  data_.AppendPoint(bezier, FXPT_TYPE::BezierTo, false);
-
-  bezier.x = pos.x + (size.width * cos(start_angle + sweep_angle));
-  bezier.y = pos.y + (size.height * sin(start_angle + sweep_angle));
-  data_.AppendPoint(bezier, FXPT_TYPE::BezierTo, false);
-}
-
-void CXFA_GEPath::AddLine(const CFX_PointF& p1, const CFX_PointF& p2) {
-  data_.AppendPoint(p1, FXPT_TYPE::MoveTo, false);
-  data_.AppendPoint(p2, FXPT_TYPE::LineTo, false);
-}
-
-void CXFA_GEPath::AddRectangle(float left,
-                               float top,
-                               float width,
-                               float height) {
-  data_.AppendRect(left, top, left + width, top + height);
-}
-
-void CXFA_GEPath::AddEllipse(const CFX_RectF& rect) {
-  AddArc(rect.TopLeft(), rect.Size(), 0, FX_PI * 2);
-}
-
-void CXFA_GEPath::AddArc(const CFX_PointF& original_pos,
-                         const CFX_SizeF& original_size,
-                         float start_angle,
-                         float sweep_angle) {
-  if (sweep_angle == 0)
-    return;
-
-  const float bezier_arc_angle_epsilon = 0.01f;
-  while (start_angle > FX_PI * 2)
-    start_angle -= FX_PI * 2;
-  while (start_angle < 0)
-    start_angle += FX_PI * 2;
-  if (sweep_angle >= FX_PI * 2)
-    sweep_angle = FX_PI * 2;
-  if (sweep_angle <= -FX_PI * 2)
-    sweep_angle = -FX_PI * 2;
-
-  CFX_SizeF size = original_size / 2;
-  CFX_PointF pos(original_pos.x + size.width, original_pos.y + size.height);
-  data_.AppendPoint(pos + CFX_PointF(size.width * cos(start_angle),
-                                     size.height * sin(start_angle)),
-                    FXPT_TYPE::MoveTo, false);
-
-  float total_sweep = 0;
-  float local_sweep = 0;
-  float prev_sweep = 0;
-  bool done = false;
-  do {
-    if (sweep_angle < 0) {
-      prev_sweep = total_sweep;
-      local_sweep = -FX_PI / 2;
-      total_sweep -= FX_PI / 2;
-      if (total_sweep <= sweep_angle + bezier_arc_angle_epsilon) {
-        local_sweep = sweep_angle - prev_sweep;
-        done = true;
-      }
-    } else {
-      prev_sweep = total_sweep;
-      local_sweep = FX_PI / 2;
-      total_sweep += FX_PI / 2;
-      if (total_sweep >= sweep_angle - bezier_arc_angle_epsilon) {
-        local_sweep = sweep_angle - prev_sweep;
-        done = true;
-      }
-    }
-
-    ArcToInternal(pos, size, start_angle, local_sweep);
-    start_angle += local_sweep;
-  } while (!done);
-}
-
-void CXFA_GEPath::AddSubpath(CXFA_GEPath* path) {
-  if (!path)
-    return;
-  data_.Append(&path->data_, nullptr);
-}
-
-void CXFA_GEPath::TransformBy(const CFX_Matrix& mt) {
-  data_.Transform(mt);
-}
diff --git a/xfa/fxgraphics/cxfa_gepath.h b/xfa/fxgraphics/cxfa_gepath.h
deleted file mode 100644
index 0c3afc4..0000000
--- a/xfa/fxgraphics/cxfa_gepath.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXGRAPHICS_CXFA_GEPATH_H_
-#define XFA_FXGRAPHICS_CXFA_GEPATH_H_
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxge/cfx_pathdata.h"
-#include "xfa/fxgraphics/cxfa_graphics.h"
-
-class CXFA_GEPath final {
- public:
-  CXFA_GEPath();
-  ~CXFA_GEPath();
-
-  const CFX_PathData* GetPathData() const { return &data_; }
-
-  void Clear();
-  bool IsEmpty() const { return data_.GetPoints().empty(); }
-  void TransformBy(const CFX_Matrix& mt);
-
-  void Close();
-  void MoveTo(const CFX_PointF& point);
-  void LineTo(const CFX_PointF& point);
-  void BezierTo(const CFX_PointF& c1,
-                const CFX_PointF& c2,
-                const CFX_PointF& to);
-  void ArcTo(const CFX_PointF& pos,
-             const CFX_SizeF& size,
-             float startAngle,
-             float sweepAngle);
-
-  void AddLine(const CFX_PointF& p1, const CFX_PointF& p2);
-  void AddRectangle(float left, float top, float width, float height);
-  void AddEllipse(const CFX_RectF& rect);
-  void AddArc(const CFX_PointF& pos,
-              const CFX_SizeF& size,
-              float startAngle,
-              float sweepAngle);
-
-  void AddSubpath(CXFA_GEPath* path);
-
- private:
-  void ArcToInternal(const CFX_PointF& pos,
-                     const CFX_SizeF& size,
-                     float start_angle,
-                     float sweep_angle);
-
-  CFX_PathData data_;
-};
-
-#endif  // XFA_FXGRAPHICS_CXFA_GEPATH_H_
diff --git a/xfa/fxgraphics/cxfa_gepattern.cpp b/xfa/fxgraphics/cxfa_gepattern.cpp
deleted file mode 100644
index 5a98a5f..0000000
--- a/xfa/fxgraphics/cxfa_gepattern.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxgraphics/cxfa_gepattern.h"
-
-CXFA_GEPattern::CXFA_GEPattern(FX_HatchStyle hatchStyle,
-                               const FX_ARGB foreArgb,
-                               const FX_ARGB backArgb)
-    : m_hatchStyle(hatchStyle), m_foreArgb(foreArgb), m_backArgb(backArgb) {}
-
-CXFA_GEPattern::~CXFA_GEPattern() {}
diff --git a/xfa/fxgraphics/cxfa_gepattern.h b/xfa/fxgraphics/cxfa_gepattern.h
deleted file mode 100644
index f5a3413..0000000
--- a/xfa/fxgraphics/cxfa_gepattern.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXGRAPHICS_CXFA_GEPATTERN_H_
-#define XFA_FXGRAPHICS_CXFA_GEPATTERN_H_
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
-#include "xfa/fxgraphics/cxfa_graphics.h"
-
-class CFX_DIBitmap;
-class CFX_Matrix;
-
-class CXFA_GEPattern final {
- public:
-  CXFA_GEPattern(FX_HatchStyle hatchStyle,
-                 const FX_ARGB foreArgb,
-                 const FX_ARGB backArgb);
-
-  ~CXFA_GEPattern();
-
- private:
-  friend class CXFA_Graphics;
-
-  const FX_HatchStyle m_hatchStyle;
-  const FX_ARGB m_foreArgb;
-  const FX_ARGB m_backArgb;
-};
-
-#endif  // XFA_FXGRAPHICS_CXFA_GEPATTERN_H_
diff --git a/xfa/fxgraphics/cxfa_geshading.cpp b/xfa/fxgraphics/cxfa_geshading.cpp
deleted file mode 100644
index d1bec70..0000000
--- a/xfa/fxgraphics/cxfa_geshading.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxgraphics/cxfa_geshading.h"
-
-CXFA_GEShading::CXFA_GEShading(const CFX_PointF& beginPoint,
-                               const CFX_PointF& endPoint,
-                               bool isExtendedBegin,
-                               bool isExtendedEnd,
-                               const FX_ARGB beginArgb,
-                               const FX_ARGB endArgb)
-    : m_type(FX_SHADING_Axial),
-      m_beginPoint(beginPoint),
-      m_endPoint(endPoint),
-      m_beginRadius(0),
-      m_endRadius(0),
-      m_isExtendedBegin(isExtendedBegin),
-      m_isExtendedEnd(isExtendedEnd),
-      m_beginArgb(beginArgb),
-      m_endArgb(endArgb) {
-  InitArgbArray();
-}
-
-CXFA_GEShading::CXFA_GEShading(const CFX_PointF& beginPoint,
-                               const CFX_PointF& endPoint,
-                               const float beginRadius,
-                               const float endRadius,
-                               bool isExtendedBegin,
-                               bool isExtendedEnd,
-                               const FX_ARGB beginArgb,
-                               const FX_ARGB endArgb)
-    : m_type(FX_SHADING_Radial),
-      m_beginPoint(beginPoint),
-      m_endPoint(endPoint),
-      m_beginRadius(beginRadius),
-      m_endRadius(endRadius),
-      m_isExtendedBegin(isExtendedBegin),
-      m_isExtendedEnd(isExtendedEnd),
-      m_beginArgb(beginArgb),
-      m_endArgb(endArgb) {
-  InitArgbArray();
-}
-
-CXFA_GEShading::~CXFA_GEShading() {}
-
-void CXFA_GEShading::InitArgbArray() {
-  int32_t a1;
-  int32_t r1;
-  int32_t g1;
-  int32_t b1;
-  std::tie(a1, r1, g1, b1) = ArgbDecode(m_beginArgb);
-
-  int32_t a2;
-  int32_t r2;
-  int32_t g2;
-  int32_t b2;
-  std::tie(a2, r2, g2, b2) = ArgbDecode(m_endArgb);
-
-  float f = static_cast<float>(FX_SHADING_Steps - 1);
-  float aScale = 1.0 * (a2 - a1) / f;
-  float rScale = 1.0 * (r2 - r1) / f;
-  float gScale = 1.0 * (g2 - g1) / f;
-  float bScale = 1.0 * (b2 - b1) / f;
-
-  for (int32_t i = 0; i < FX_SHADING_Steps; i++) {
-    int32_t a3 = static_cast<int32_t>(i * aScale);
-    int32_t r3 = static_cast<int32_t>(i * rScale);
-    int32_t g3 = static_cast<int32_t>(i * gScale);
-    int32_t b3 = static_cast<int32_t>(i * bScale);
-
-    // TODO(dsinclair): Add overloads for FX_ARGB. pdfium:437
-    m_argbArray[i] =
-        FXARGB_TODIB(ArgbEncode(a1 + a3, r1 + r3, g1 + g3, b1 + b3));
-  }
-}
diff --git a/xfa/fxgraphics/cxfa_geshading.h b/xfa/fxgraphics/cxfa_geshading.h
deleted file mode 100644
index 8905a12..0000000
--- a/xfa/fxgraphics/cxfa_geshading.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXGRAPHICS_CXFA_GESHADING_H_
-#define XFA_FXGRAPHICS_CXFA_GESHADING_H_
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxge/fx_dib.h"
-
-#define FX_SHADING_Steps 256
-
-enum CXFA_GEShading_Type { FX_SHADING_Axial = 1, FX_SHADING_Radial };
-
-class CXFA_GEShading final {
- public:
-  // Axial shading.
-  CXFA_GEShading(const CFX_PointF& beginPoint,
-                 const CFX_PointF& endPoint,
-                 bool isExtendedBegin,
-                 bool isExtendedEnd,
-                 const FX_ARGB beginArgb,
-                 const FX_ARGB endArgb);
-
-  // Radial shading.
-  CXFA_GEShading(const CFX_PointF& beginPoint,
-                 const CFX_PointF& endPoint,
-                 const float beginRadius,
-                 const float endRadius,
-                 bool isExtendedBegin,
-                 bool isExtendedEnd,
-                 const FX_ARGB beginArgb,
-                 const FX_ARGB endArgb);
-
-  ~CXFA_GEShading();
-
- private:
-  friend class CXFA_Graphics;
-
-  void InitArgbArray();
-
-  const CXFA_GEShading_Type m_type;
-  const CFX_PointF m_beginPoint;
-  const CFX_PointF m_endPoint;
-  const float m_beginRadius;
-  const float m_endRadius;
-  const bool m_isExtendedBegin;
-  const bool m_isExtendedEnd;
-  const FX_ARGB m_beginArgb;
-  const FX_ARGB m_endArgb;
-  FX_ARGB m_argbArray[FX_SHADING_Steps];
-};
-
-#endif  // XFA_FXGRAPHICS_CXFA_GESHADING_H_
diff --git a/xfa/fxgraphics/cxfa_graphics.cpp b/xfa/fxgraphics/cxfa_graphics.cpp
deleted file mode 100644
index ff6bac1..0000000
--- a/xfa/fxgraphics/cxfa_graphics.cpp
+++ /dev/null
@@ -1,427 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxgraphics/cxfa_graphics.h"
-
-#include <cmath>
-#include <memory>
-
-#include "core/fxge/cfx_defaultrenderdevice.h"
-#include "core/fxge/cfx_renderdevice.h"
-#include "core/fxge/cfx_unicodeencoding.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fxgraphics/cxfa_gecolor.h"
-#include "xfa/fxgraphics/cxfa_gepath.h"
-#include "xfa/fxgraphics/cxfa_gepattern.h"
-#include "xfa/fxgraphics/cxfa_geshading.h"
-
-namespace {
-
-struct FX_HATCHDATA {
-  int32_t width;
-  int32_t height;
-  uint8_t maskBits[64];
-};
-
-const FX_HATCHDATA kHatchBitmapData[] = {
-    {16,  // Horizontal
-     16,
-     {
-         0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
-         0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-     }},
-    {16,  // Vertical
-     16,
-     {
-         0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
-         0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80,
-         0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
-         0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
-         0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
-         0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
-     }},
-    {16,  // ForwardDiagonal
-     16,
-     {
-         0x80, 0x80, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x20, 0x00,
-         0x00, 0x10, 0x10, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04, 0x04,
-         0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x80,
-         0x80, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00,
-         0x10, 0x10, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04, 0x04, 0x00,
-         0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
-     }},
-    {16,  // BackwardDiagonal
-     16,
-     {
-         0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x04, 0x04, 0x00,
-         0x00, 0x08, 0x08, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x20, 0x20,
-         0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x01,
-         0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00,
-         0x08, 0x08, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x20, 0x20, 0x00,
-         0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
-     }},
-    {16,  // Cross
-     16,
-     {
-         0xff, 0xff, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
-         0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80,
-         0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0xff,
-         0xff, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
-         0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
-         0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
-     }},
-    {16,  // DiagonalCross
-     16,
-     {
-         0x81, 0x81, 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x24, 0x24, 0x00,
-         0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x24, 0x24,
-         0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00, 0x81,
-         0x81, 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x24, 0x24, 0x00, 0x00,
-         0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x24, 0x24, 0x00,
-         0x00, 0x42, 0x42, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00,
-     }},
-};
-
-const FX_HATCHDATA kHatchPlaceHolder = {
-    0,
-    0,
-    {
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    }};
-
-const FX_HATCHDATA& GetHatchBitmapData(size_t index) {
-  return index < FX_ArraySize(kHatchBitmapData) ? kHatchBitmapData[index]
-                                                : kHatchPlaceHolder;
-}
-
-}  // namespace
-
-CXFA_Graphics::CXFA_Graphics(CFX_RenderDevice* renderDevice)
-    : m_renderDevice(renderDevice) {
-  ASSERT(m_renderDevice);
-}
-
-CXFA_Graphics::~CXFA_Graphics() = default;
-
-void CXFA_Graphics::SaveGraphState() {
-  m_renderDevice->SaveState();
-  m_infoStack.push_back(pdfium::MakeUnique<TInfo>(m_info));
-}
-
-void CXFA_Graphics::RestoreGraphState() {
-  m_renderDevice->RestoreState(false);
-  if (m_infoStack.empty())
-    return;
-
-  m_info = *m_infoStack.back();
-  m_infoStack.pop_back();
-  return;
-}
-
-void CXFA_Graphics::SetLineCap(CFX_GraphStateData::LineCap lineCap) {
-  m_info.graphState.m_LineCap = lineCap;
-}
-
-void CXFA_Graphics::SetLineDash(float dashPhase,
-                                const float* dashArray,
-                                size_t dashCount) {
-  ASSERT(dashArray);
-  ASSERT(dashCount);
-
-  float scale = m_info.isActOnDash ? m_info.graphState.m_LineWidth : 1.0;
-  m_info.graphState.m_DashPhase = dashPhase;
-  m_info.graphState.m_DashArray.resize(dashCount);
-  for (size_t i = 0; i < dashCount; i++)
-    m_info.graphState.m_DashArray[i] = dashArray[i] * scale;
-}
-
-void CXFA_Graphics::SetSolidLineDash() {
-  m_info.graphState.m_DashArray.clear();
-}
-
-void CXFA_Graphics::SetLineWidth(float lineWidth) {
-  m_info.graphState.m_LineWidth = lineWidth;
-}
-
-void CXFA_Graphics::EnableActOnDash() {
-  m_info.isActOnDash = true;
-}
-
-void CXFA_Graphics::SetStrokeColor(const CXFA_GEColor& color) {
-  m_info.strokeColor = color;
-}
-
-void CXFA_Graphics::SetFillColor(const CXFA_GEColor& color) {
-    m_info.fillColor = color;
-}
-
-void CXFA_Graphics::StrokePath(CXFA_GEPath* path, const CFX_Matrix* matrix) {
-  if (path)
-    RenderDeviceStrokePath(path, matrix);
-}
-
-void CXFA_Graphics::FillPath(CXFA_GEPath* path,
-                             FX_FillMode fillMode,
-                             const CFX_Matrix* matrix) {
-  if (path)
-    RenderDeviceFillPath(path, fillMode, matrix);
-}
-
-void CXFA_Graphics::ConcatMatrix(const CFX_Matrix* matrix) {
-  if (matrix)
-    m_info.CTM.Concat(*matrix);
-}
-
-const CFX_Matrix* CXFA_Graphics::GetMatrix() const {
-  return &m_info.CTM;
-}
-
-CFX_RectF CXFA_Graphics::GetClipRect() const {
-  FX_RECT r = m_renderDevice->GetClipBox();
-  return CFX_RectF(r.left, r.top, r.Width(), r.Height());
-}
-
-void CXFA_Graphics::SetClipRect(const CFX_RectF& rect) {
-  m_renderDevice->SetClip_Rect(
-      FX_RECT(FXSYS_roundf(rect.left), FXSYS_roundf(rect.top),
-              FXSYS_roundf(rect.right()), FXSYS_roundf(rect.bottom())));
-}
-
-CFX_RenderDevice* CXFA_Graphics::GetRenderDevice() {
-  return m_renderDevice;
-}
-
-void CXFA_Graphics::RenderDeviceStrokePath(const CXFA_GEPath* path,
-                                           const CFX_Matrix* matrix) {
-  if (m_info.strokeColor.GetType() != CXFA_GEColor::Solid)
-    return;
-
-  CFX_Matrix m = m_info.CTM;
-  if (matrix)
-    m.Concat(*matrix);
-
-  m_renderDevice->DrawPath(path->GetPathData(), &m, &m_info.graphState, 0x0,
-                           m_info.strokeColor.GetArgb(), 0);
-}
-
-void CXFA_Graphics::RenderDeviceFillPath(const CXFA_GEPath* path,
-                                         FX_FillMode fillMode,
-                                         const CFX_Matrix* matrix) {
-  CFX_Matrix m = m_info.CTM;
-  if (matrix)
-    m.Concat(*matrix);
-
-  switch (m_info.fillColor.GetType()) {
-    case CXFA_GEColor::Solid:
-      m_renderDevice->DrawPath(path->GetPathData(), &m, &m_info.graphState,
-                               m_info.fillColor.GetArgb(), 0x0, fillMode);
-      return;
-    case CXFA_GEColor::Pattern:
-      FillPathWithPattern(path, fillMode, m);
-      return;
-    case CXFA_GEColor::Shading:
-      FillPathWithShading(path, fillMode, m);
-      return;
-    default:
-      return;
-  }
-}
-
-void CXFA_Graphics::FillPathWithPattern(const CXFA_GEPath* path,
-                                        FX_FillMode fillMode,
-                                        const CFX_Matrix& matrix) {
-  RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap();
-  int32_t width = bitmap->GetWidth();
-  int32_t height = bitmap->GetHeight();
-  auto bmp = pdfium::MakeRetain<CFX_DIBitmap>();
-  bmp->Create(width, height, FXDIB_Argb);
-  m_renderDevice->GetDIBits(bmp, 0, 0);
-
-  FX_HatchStyle hatchStyle = m_info.fillColor.GetPattern()->m_hatchStyle;
-  const FX_HATCHDATA& data =
-      GetHatchBitmapData(static_cast<size_t>(hatchStyle));
-
-  auto mask = pdfium::MakeRetain<CFX_DIBitmap>();
-  mask->Create(data.width, data.height, FXDIB_1bppMask);
-  memcpy(mask->GetBuffer(), data.maskBits, mask->GetPitch() * data.height);
-  const CFX_FloatRect rectf =
-      matrix.TransformRect(path->GetPathData()->GetBoundingBox());
-  const FX_RECT rect = rectf.ToRoundedFxRect();
-
-  CFX_DefaultRenderDevice device;
-  device.Attach(bmp, false, nullptr, false);
-  device.FillRect(rect, m_info.fillColor.GetPattern()->m_backArgb);
-  for (int32_t j = rect.bottom; j < rect.top; j += mask->GetHeight()) {
-    for (int32_t i = rect.left; i < rect.right; i += mask->GetWidth())
-      device.SetBitMask(mask, i, j, m_info.fillColor.GetPattern()->m_foreArgb);
-  }
-  CFX_RenderDevice::StateRestorer restorer(m_renderDevice);
-  m_renderDevice->SetClip_PathFill(path->GetPathData(), &matrix, fillMode);
-  SetDIBitsWithMatrix(bmp, CFX_Matrix());
-}
-
-void CXFA_Graphics::FillPathWithShading(const CXFA_GEPath* path,
-                                        FX_FillMode fillMode,
-                                        const CFX_Matrix& matrix) {
-  RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap();
-  int32_t width = bitmap->GetWidth();
-  int32_t height = bitmap->GetHeight();
-  float start_x = m_info.fillColor.GetShading()->m_beginPoint.x;
-  float start_y = m_info.fillColor.GetShading()->m_beginPoint.y;
-  float end_x = m_info.fillColor.GetShading()->m_endPoint.x;
-  float end_y = m_info.fillColor.GetShading()->m_endPoint.y;
-  auto bmp = pdfium::MakeRetain<CFX_DIBitmap>();
-  bmp->Create(width, height, FXDIB_Argb);
-  m_renderDevice->GetDIBits(bmp, 0, 0);
-  int32_t pitch = bmp->GetPitch();
-  bool result = false;
-  switch (m_info.fillColor.GetShading()->m_type) {
-    case FX_SHADING_Axial: {
-      float x_span = end_x - start_x;
-      float y_span = end_y - start_y;
-      float axis_len_square = (x_span * x_span) + (y_span * y_span);
-      for (int32_t row = 0; row < height; row++) {
-        uint32_t* dib_buf =
-            reinterpret_cast<uint32_t*>(bmp->GetBuffer() + row * pitch);
-        for (int32_t column = 0; column < width; column++) {
-          float scale = 0.0f;
-          if (axis_len_square) {
-            float y = static_cast<float>(row);
-            float x = static_cast<float>(column);
-            scale = (((x - start_x) * x_span) + ((y - start_y) * y_span)) /
-                    axis_len_square;
-            if (std::isnan(scale) || scale < 0.0f) {
-              if (!m_info.fillColor.GetShading()->m_isExtendedBegin)
-                continue;
-              scale = 0.0f;
-            } else if (scale > 1.0f) {
-              if (!m_info.fillColor.GetShading()->m_isExtendedEnd)
-                continue;
-              scale = 1.0f;
-            }
-          }
-          int32_t index = static_cast<int32_t>(scale * (FX_SHADING_Steps - 1));
-          dib_buf[column] = m_info.fillColor.GetShading()->m_argbArray[index];
-        }
-      }
-      result = true;
-      break;
-    }
-    case FX_SHADING_Radial: {
-      float start_r = m_info.fillColor.GetShading()->m_beginRadius;
-      float end_r = m_info.fillColor.GetShading()->m_endRadius;
-      float a = ((start_x - end_x) * (start_x - end_x)) +
-                ((start_y - end_y) * (start_y - end_y)) -
-                ((start_r - end_r) * (start_r - end_r));
-      for (int32_t row = 0; row < height; row++) {
-        uint32_t* dib_buf = (uint32_t*)(bmp->GetBuffer() + row * pitch);
-        for (int32_t column = 0; column < width; column++) {
-          float x = (float)(column);
-          float y = (float)(row);
-          float b = -2 * (((x - start_x) * (end_x - start_x)) +
-                          ((y - start_y) * (end_y - start_y)) +
-                          (start_r * (end_r - start_r)));
-          float c = ((x - start_x) * (x - start_x)) +
-                    ((y - start_y) * (y - start_y)) - (start_r * start_r);
-          float s;
-          if (a == 0) {
-            s = -c / b;
-          } else {
-            float b2_4ac = (b * b) - 4 * (a * c);
-            if (b2_4ac < 0) {
-              continue;
-            }
-            float root = (sqrt(b2_4ac));
-            float s1, s2;
-            if (a > 0) {
-              s1 = (-b - root) / (2 * a);
-              s2 = (-b + root) / (2 * a);
-            } else {
-              s2 = (-b - root) / (2 * a);
-              s1 = (-b + root) / (2 * a);
-            }
-            if (s2 <= 1.0f || m_info.fillColor.GetShading()->m_isExtendedEnd) {
-              s = (s2);
-            } else {
-              s = (s1);
-            }
-            if ((start_r) + s * (end_r - start_r) < 0) {
-              continue;
-            }
-          }
-          if (std::isnan(s) || s < 0.0f) {
-            if (!m_info.fillColor.GetShading()->m_isExtendedBegin)
-              continue;
-            s = 0.0f;
-          }
-          if (s > 1.0f) {
-            if (!m_info.fillColor.GetShading()->m_isExtendedEnd)
-              continue;
-            s = 1.0f;
-          }
-          int index = (int32_t)(s * (FX_SHADING_Steps - 1));
-          dib_buf[column] = m_info.fillColor.GetShading()->m_argbArray[index];
-        }
-      }
-      result = true;
-      break;
-    }
-    default: {
-      result = false;
-      break;
-    }
-  }
-  if (result) {
-    CFX_RenderDevice::StateRestorer restorer(m_renderDevice);
-    m_renderDevice->SetClip_PathFill(path->GetPathData(), &matrix, fillMode);
-    SetDIBitsWithMatrix(bmp, matrix);
-  }
-}
-
-void CXFA_Graphics::SetDIBitsWithMatrix(const RetainPtr<CFX_DIBBase>& source,
-                                        const CFX_Matrix& matrix) {
-  if (matrix.IsIdentity()) {
-    m_renderDevice->SetDIBits(source, 0, 0);
-  } else {
-    CFX_Matrix m((float)source->GetWidth(), 0, 0, (float)source->GetHeight(), 0,
-                 0);
-    m.Concat(matrix);
-    int32_t left;
-    int32_t top;
-    RetainPtr<CFX_DIBitmap> bmp1 = source->FlipImage(false, true);
-    RetainPtr<CFX_DIBitmap> bmp2 = bmp1->TransformTo(m, &left, &top);
-    m_renderDevice->SetDIBits(bmp2, left, top);
-  }
-}
-
-CXFA_Graphics::TInfo::TInfo()
-    : isActOnDash(false), strokeColor(nullptr), fillColor(nullptr) {}
-
-CXFA_Graphics::TInfo::TInfo(const TInfo& info)
-    : graphState(info.graphState),
-      CTM(info.CTM),
-      isActOnDash(info.isActOnDash),
-      strokeColor(info.strokeColor),
-      fillColor(info.fillColor) {}
-
-CXFA_Graphics::TInfo& CXFA_Graphics::TInfo::operator=(const TInfo& other) {
-  graphState = other.graphState;
-  CTM = other.CTM;
-  isActOnDash = other.isActOnDash;
-  strokeColor = other.strokeColor;
-  fillColor = other.fillColor;
-  return *this;
-}
diff --git a/xfa/fxgraphics/cxfa_graphics.h b/xfa/fxgraphics/cxfa_graphics.h
deleted file mode 100644
index d6ab7de..0000000
--- a/xfa/fxgraphics/cxfa_graphics.h
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXGRAPHICS_CXFA_GRAPHICS_H_
-#define XFA_FXGRAPHICS_CXFA_GRAPHICS_H_
-
-#include <memory>
-#include <vector>
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxge/cfx_graphstatedata.h"
-#include "xfa/fxgraphics/cxfa_gecolor.h"
-
-using FX_FillMode = int32_t;
-
-enum class FX_HatchStyle {
-  Horizontal = 0,
-  Vertical = 1,
-  ForwardDiagonal = 2,
-  BackwardDiagonal = 3,
-  Cross = 4,
-  DiagonalCross = 5
-};
-
-class CFX_DIBBase;
-class CFX_RenderDevice;
-class CXFA_GEPath;
-
-class CXFA_Graphics {
- public:
-  explicit CXFA_Graphics(CFX_RenderDevice* renderDevice);
-  ~CXFA_Graphics();
-
-  void SaveGraphState();
-  void RestoreGraphState();
-
-  CFX_RectF GetClipRect() const;
-  const CFX_Matrix* GetMatrix() const;
-  CFX_RenderDevice* GetRenderDevice();
-
-  void SetLineCap(CFX_GraphStateData::LineCap lineCap);
-  void SetLineDash(float dashPhase, const float* dashArray, size_t dashCount);
-  void SetSolidLineDash();
-  void SetLineWidth(float lineWidth);
-  void EnableActOnDash();
-  void SetStrokeColor(const CXFA_GEColor& color);
-  void SetFillColor(const CXFA_GEColor& color);
-  void SetClipRect(const CFX_RectF& rect);
-  void StrokePath(CXFA_GEPath* path, const CFX_Matrix* matrix);
-  void FillPath(CXFA_GEPath* path,
-                FX_FillMode fillMode,
-                const CFX_Matrix* matrix);
-  void ConcatMatrix(const CFX_Matrix* matrix);
-
- private:
-  struct TInfo {
-    TInfo();
-    explicit TInfo(const TInfo& info);
-    TInfo& operator=(const TInfo& other);
-
-    CFX_GraphStateData graphState;
-    CFX_Matrix CTM;
-    bool isActOnDash;
-    CXFA_GEColor strokeColor;
-    CXFA_GEColor fillColor;
-  };
-
-  void RenderDeviceStrokePath(const CXFA_GEPath* path,
-                              const CFX_Matrix* matrix);
-  void RenderDeviceFillPath(const CXFA_GEPath* path,
-                            FX_FillMode fillMode,
-                            const CFX_Matrix* matrix);
-  void FillPathWithPattern(const CXFA_GEPath* path,
-                           FX_FillMode fillMode,
-                           const CFX_Matrix& matrix);
-  void FillPathWithShading(const CXFA_GEPath* path,
-                           FX_FillMode fillMode,
-                           const CFX_Matrix& matrix);
-  void SetDIBitsWithMatrix(const RetainPtr<CFX_DIBBase>& source,
-                           const CFX_Matrix& matrix);
-
-  CFX_RenderDevice* const m_renderDevice;  // Not owned.
-  TInfo m_info;
-  std::vector<std::unique_ptr<TInfo>> m_infoStack;
-};
-
-#endif  // XFA_FXGRAPHICS_CXFA_GRAPHICS_H_